aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cmake.conf3
-rw-r--r--.gitignore5
-rw-r--r--CMakeLists.txt3
-rw-r--r--LICENSES/Apache-2.0.txt61
-rw-r--r--LICENSES/CC-BY-3.0.txt319
-rw-r--r--LICENSES/MIT.txt11
-rw-r--r--LICENSES/OFL-1.1.txt97
-rw-r--r--cmake/QtDeclarativeSetup.cmake4
-rw-r--r--coin/axivion/ci_config_linux.json47
-rw-r--r--coin/instructions/coin_dom_standalone_old_qt_mac.yaml69
-rw-r--r--coin/instructions/coin_dom_standalone_old_qt_win.yaml75
-rw-r--r--coin/module_config.yaml2
-rw-r--r--conanfile.py87
-rw-r--r--dependencies.yaml10
-rw-r--r--examples/CMakeLists.txt10
-rw-r--r--examples/README4
-rw-r--r--examples/core/permissions/CMakeLists.txt57
-rw-r--r--examples/core/permissions/Info.plist59
-rw-r--r--examples/core/permissions/Main.qml99
-rw-r--r--examples/core/permissions/android/AndroidManifest.xml46
-rw-r--r--examples/core/permissions/main.cpp21
-rw-r--r--examples/examples.pro1
-rw-r--r--examples/platforms/CMakeLists.txt6
-rw-r--r--examples/platforms/android/.gitignore10
-rw-r--r--examples/platforms/android/CMakeLists.txt6
-rw-r--r--examples/platforms/android/doc/images/portrait_java.pngbin0 -> 43786 bytes
-rw-r--r--examples/platforms/android/doc/src/qml_in_android_studio_projects.qdoc186
-rw-r--r--examples/platforms/android/qml_in_android_view/CMakeLists.txt32
-rw-r--r--examples/platforms/android/qml_in_android_view/main.cpp11
-rw-r--r--examples/platforms/android/qml_in_android_view/main.qml87
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/CMakeLists.txt18
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/build.gradle56
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/AndroidManifest.xml27
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/Colors.java35
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java178
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml74
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml31
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/layout/activity_main.xml165
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml6
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml6
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webpbin0 -> 4118 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webpbin0 -> 8980 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webpbin0 -> 6056 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webpbin0 -> 2650 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webpbin0 -> 5556 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webpbin0 -> 3680 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webpbin0 -> 5924 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webpbin0 -> 13218 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webpbin0 -> 8836 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webpbin0 -> 10144 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webpbin0 -> 25688 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webpbin0 -> 14828 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webpbin0 -> 14820 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webpbin0 -> 41052 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webpbin0 -> 21224 bytes
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/colors.xml11
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/strings.xml13
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/styles.xml6
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/themes.xml16
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/backup_rules.xml10
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/data_extraction_rules.xml10
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/build.gradle4
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/gradle.properties22
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--examples/platforms/android/qml_in_java_based_android_project/settings.gradle17
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/CMakeLists.txt18
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/build.gradle64
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/AndroidManifest.xml26
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/Colors.kt31
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt156
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml74
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml30
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/layout/activity_main.xml164
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml6
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml6
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webpbin0 -> 4118 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webpbin0 -> 8980 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webpbin0 -> 6056 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webpbin0 -> 2650 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webpbin0 -> 5556 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webpbin0 -> 3680 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webpbin0 -> 5924 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webpbin0 -> 13218 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webpbin0 -> 8836 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webpbin0 -> 10144 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webpbin0 -> 25688 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webpbin0 -> 14828 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webpbin0 -> 14820 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webpbin0 -> 41052 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webpbin0 -> 21224 bytes
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/colors.xml10
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/strings.xml12
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/styles.xml6
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/themes.xml16
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/backup_rules.xml13
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/data_extraction_rules.xml19
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/build.gradle5
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/gradle.properties23
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--examples/platforms/android/qml_in_kotlin_based_android_project/settings.gradle17
-rw-r--r--examples/qml/CMakeLists.txt15
-rw-r--r--examples/qml/doc/src/qml-extending.qdoc339
-rw-r--r--examples/qml/dynamicscene/CMakeLists.txt52
-rw-r--r--examples/qml/dynamicscene/GenericSceneItem.qml12
-rw-r--r--examples/qml/dynamicscene/PaletteItem.qml22
-rw-r--r--examples/qml/dynamicscene/PerspectiveItem.qml27
-rw-r--r--examples/qml/dynamicscene/Sun.qml36
-rw-r--r--examples/qml/dynamicscene/doc/images/qml-dynamicscene-example.pngbin32286 -> 0 bytes
-rw-r--r--examples/qml/dynamicscene/doc/src/dynamicscene.qdoc10
-rw-r--r--examples/qml/dynamicscene/dynamicscene.pro9
-rw-r--r--examples/qml/dynamicscene/dynamicscene.qml237
-rw-r--r--examples/qml/dynamicscene/dynamicscene.qmlproject16
-rw-r--r--examples/qml/dynamicscene/dynamicscene.qrc18
-rw-r--r--examples/qml/dynamicscene/images/NOTE1
-rw-r--r--examples/qml/dynamicscene/images/face-smile.pngbin15408 -> 0 bytes
-rw-r--r--examples/qml/dynamicscene/images/moon.pngbin1757 -> 0 bytes
-rw-r--r--examples/qml/dynamicscene/images/rabbit_brown.pngbin1245 -> 0 bytes
-rw-r--r--examples/qml/dynamicscene/images/rabbit_bw.pngbin1759 -> 0 bytes
-rw-r--r--examples/qml/dynamicscene/images/star.pngbin259 -> 0 bytes
-rw-r--r--examples/qml/dynamicscene/images/sun.pngbin8110 -> 0 bytes
-rw-r--r--examples/qml/dynamicscene/images/tree_s.pngbin3406 -> 0 bytes
-rw-r--r--examples/qml/dynamicscene/itemCreation.js62
-rw-r--r--examples/qml/dynamicscene/main.cpp4
-rw-r--r--examples/qml/locale/locale.qml101
-rw-r--r--examples/qml/networkaccessmanagerfactory/CMakeLists.txt44
-rw-r--r--examples/qml/networkaccessmanagerfactory/main.cpp97
-rw-r--r--examples/qml/networkaccessmanagerfactory/view.qml10
-rw-r--r--examples/qml/qml-i18n/CMakeLists.txt51
-rw-r--r--examples/qml/qml-i18n/Main.qml (renamed from examples/qml/qml-i18n/qml-i18n.qml)0
-rw-r--r--examples/qml/qml-i18n/doc/src/i18n.qdoc5
-rw-r--r--examples/qml/qml-i18n/i18n/base.ts4
-rw-r--r--examples/qml/qml-i18n/i18n/qml_en_AU.ts4
-rw-r--r--examples/qml/qml-i18n/i18n/qml_fr.ts4
-rw-r--r--examples/qml/qml-i18n/main.cpp14
-rw-r--r--examples/qml/qml-i18n/qml-i18n.pro1
-rw-r--r--examples/qml/qml-i18n/qml-i18n.qrc5
-rw-r--r--examples/qml/qml-i18n/qmldir5
-rw-r--r--examples/qml/qml.pro17
-rw-r--r--examples/qml/qmldom/CMakeLists.txt37
-rw-r--r--examples/qml/qmldom/qmldomloadeditwrite.cpp500
-rw-r--r--examples/qml/qmlextensionplugins/CMakeLists.txt61
-rw-r--r--examples/qml/qmlextensionplugins/imports/TimeExample/Clock.qml51
-rw-r--r--examples/qml/qmlextensionplugins/plugin.cpp15
-rw-r--r--examples/qml/qmlextensionplugins/plugins.qml16
-rw-r--r--examples/qml/qmlextensionplugins/timemodel.cpp51
-rw-r--r--examples/qml/qmlextensionplugins/timemodel.h64
-rw-r--r--examples/qml/referenceexamples/CMakeLists.txt18
-rw-r--r--examples/qml/referenceexamples/adding/CMakeLists.txt42
-rw-r--r--examples/qml/referenceexamples/adding/adding.pro13
-rw-r--r--examples/qml/referenceexamples/adding/adding.qrc5
-rw-r--r--examples/qml/referenceexamples/adding/example.qml11
-rw-r--r--examples/qml/referenceexamples/adding/main.cpp26
-rw-r--r--examples/qml/referenceexamples/adding/person.cpp27
-rw-r--r--examples/qml/referenceexamples/adding/person.h32
-rw-r--r--examples/qml/referenceexamples/attached/CMakeLists.txt44
-rw-r--r--examples/qml/referenceexamples/attached/attached.pro15
-rw-r--r--examples/qml/referenceexamples/attached/attached.qrc5
-rw-r--r--examples/qml/referenceexamples/attached/birthdayparty.cpp45
-rw-r--r--examples/qml/referenceexamples/attached/birthdayparty.h57
-rw-r--r--examples/qml/referenceexamples/attached/example.qml32
-rw-r--r--examples/qml/referenceexamples/attached/main.cpp48
-rw-r--r--examples/qml/referenceexamples/attached/person.cpp59
-rw-r--r--examples/qml/referenceexamples/attached/person.h75
-rw-r--r--examples/qml/referenceexamples/binding/CMakeLists.txt45
-rw-r--r--examples/qml/referenceexamples/binding/binding.pro17
-rw-r--r--examples/qml/referenceexamples/binding/binding.qrc5
-rw-r--r--examples/qml/referenceexamples/binding/birthdayparty.cpp66
-rw-r--r--examples/qml/referenceexamples/binding/birthdayparty.h67
-rw-r--r--examples/qml/referenceexamples/binding/example.qml41
-rw-r--r--examples/qml/referenceexamples/binding/happybirthdaysong.cpp47
-rw-r--r--examples/qml/referenceexamples/binding/happybirthdaysong.h40
-rw-r--r--examples/qml/referenceexamples/binding/main.cpp48
-rw-r--r--examples/qml/referenceexamples/binding/person.cpp79
-rw-r--r--examples/qml/referenceexamples/binding/person.h84
-rw-r--r--examples/qml/referenceexamples/coercion/CMakeLists.txt43
-rw-r--r--examples/qml/referenceexamples/coercion/birthdayparty.cpp29
-rw-r--r--examples/qml/referenceexamples/coercion/birthdayparty.h34
-rw-r--r--examples/qml/referenceexamples/coercion/coercion.pro15
-rw-r--r--examples/qml/referenceexamples/coercion/coercion.qrc5
-rw-r--r--examples/qml/referenceexamples/coercion/example.qml18
-rw-r--r--examples/qml/referenceexamples/coercion/main.cpp35
-rw-r--r--examples/qml/referenceexamples/coercion/person.cpp24
-rw-r--r--examples/qml/referenceexamples/coercion/person.h54
-rw-r--r--examples/qml/referenceexamples/default/CMakeLists.txt43
-rw-r--r--examples/qml/referenceexamples/default/birthdayparty.cpp30
-rw-r--r--examples/qml/referenceexamples/default/birthdayparty.h35
-rw-r--r--examples/qml/referenceexamples/default/default.pro15
-rw-r--r--examples/qml/referenceexamples/default/default.qrc5
-rw-r--r--examples/qml/referenceexamples/default/example.qml17
-rw-r--r--examples/qml/referenceexamples/default/main.cpp34
-rw-r--r--examples/qml/referenceexamples/default/person.cpp24
-rw-r--r--examples/qml/referenceexamples/default/person.h45
-rw-r--r--examples/qml/referenceexamples/extended/CMakeLists.txt44
-rw-r--r--examples/qml/referenceexamples/extended/example.qml10
-rw-r--r--examples/qml/referenceexamples/extended/extended.pro13
-rw-r--r--examples/qml/referenceexamples/extended/extended.qrc5
-rw-r--r--examples/qml/referenceexamples/extended/lineedit.cpp71
-rw-r--r--examples/qml/referenceexamples/extended/lineedit.h47
-rw-r--r--examples/qml/referenceexamples/extended/main.cpp27
-rw-r--r--examples/qml/referenceexamples/grouped/CMakeLists.txt44
-rw-r--r--examples/qml/referenceexamples/grouped/birthdayparty.cpp29
-rw-r--r--examples/qml/referenceexamples/grouped/birthdayparty.h34
-rw-r--r--examples/qml/referenceexamples/grouped/example.qml41
-rw-r--r--examples/qml/referenceexamples/grouped/grouped.pro15
-rw-r--r--examples/qml/referenceexamples/grouped/grouped.qrc5
-rw-r--r--examples/qml/referenceexamples/grouped/main.cpp42
-rw-r--r--examples/qml/referenceexamples/grouped/person.cpp59
-rw-r--r--examples/qml/referenceexamples/grouped/person.h77
-rw-r--r--examples/qml/referenceexamples/methods/CMakeLists.txt43
-rw-r--r--examples/qml/referenceexamples/methods/birthdayparty.cpp39
-rw-r--r--examples/qml/referenceexamples/methods/birthdayparty.h36
-rw-r--r--examples/qml/referenceexamples/methods/example.qml21
-rw-r--r--examples/qml/referenceexamples/methods/main.cpp29
-rw-r--r--examples/qml/referenceexamples/methods/methods.pro15
-rw-r--r--examples/qml/referenceexamples/methods/methods.qrc5
-rw-r--r--examples/qml/referenceexamples/methods/person.cpp24
-rw-r--r--examples/qml/referenceexamples/methods/person.h30
-rw-r--r--examples/qml/referenceexamples/properties/CMakeLists.txt43
-rw-r--r--examples/qml/referenceexamples/properties/birthdayparty.cpp87
-rw-r--r--examples/qml/referenceexamples/properties/birthdayparty.h52
-rw-r--r--examples/qml/referenceexamples/properties/example.qml18
-rw-r--r--examples/qml/referenceexamples/properties/main.cpp28
-rw-r--r--examples/qml/referenceexamples/properties/person.cpp24
-rw-r--r--examples/qml/referenceexamples/properties/person.h30
-rw-r--r--examples/qml/referenceexamples/properties/properties.pro15
-rw-r--r--examples/qml/referenceexamples/properties/properties.qrc5
-rw-r--r--examples/qml/referenceexamples/referenceexamples.pro17
-rw-r--r--examples/qml/referenceexamples/referenceexamples.qmlproject14
-rw-r--r--examples/qml/referenceexamples/signal/CMakeLists.txt44
-rw-r--r--examples/qml/referenceexamples/signal/birthdayparty.cpp51
-rw-r--r--examples/qml/referenceexamples/signal/birthdayparty.h57
-rw-r--r--examples/qml/referenceexamples/signal/example.qml36
-rw-r--r--examples/qml/referenceexamples/signal/main.cpp47
-rw-r--r--examples/qml/referenceexamples/signal/person.cpp59
-rw-r--r--examples/qml/referenceexamples/signal/person.h75
-rw-r--r--examples/qml/referenceexamples/signal/signal.pro15
-rw-r--r--examples/qml/referenceexamples/signal/signal.qrc5
-rw-r--r--examples/qml/referenceexamples/valuesource/CMakeLists.txt45
-rw-r--r--examples/qml/referenceexamples/valuesource/birthdayparty.cpp60
-rw-r--r--examples/qml/referenceexamples/valuesource/birthdayparty.h64
-rw-r--r--examples/qml/referenceexamples/valuesource/example.qml40
-rw-r--r--examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp43
-rw-r--r--examples/qml/referenceexamples/valuesource/happybirthdaysong.h44
-rw-r--r--examples/qml/referenceexamples/valuesource/main.cpp48
-rw-r--r--examples/qml/referenceexamples/valuesource/person.cpp59
-rw-r--r--examples/qml/referenceexamples/valuesource/person.h77
-rw-r--r--examples/qml/referenceexamples/valuesource/valuesource.pro17
-rw-r--r--examples/qml/referenceexamples/valuesource/valuesource.qrc5
-rw-r--r--examples/qml/shell/CMakeLists.txt47
-rw-r--r--examples/qml/shell/main.cpp116
-rw-r--r--examples/qml/shell/shell.pro9
-rw-r--r--examples/qml/tutorials/CMakeLists.txt4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/CMakeLists.txt12
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/CMakeLists.txt50
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml16
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.pro13
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.qrc6
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.cpp99
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h48
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.cpp32
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.cpp30
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.h34
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/qmldir.in4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/CMakeLists.txt50
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml16
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.cpp99
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.h48
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.pro12
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.qrc6
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.cpp37
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.cpp30
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/qmldir.in4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/CMakeLists.txt50
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml15
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.cpp99
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.h49
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.pro12
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.qrc6
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.cpp37
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.cpp30
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.h51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/qmldir.in4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/CMakeLists.txt49
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml33
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.cpp99
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.h49
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.pro12
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.qrc6
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.cpp45
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.cpp96
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h98
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/qmldir.in5
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/CMakeLists.txt49
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml23
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.pro12
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.qrc6
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.cpp117
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h72
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp48
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.cpp96
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.h98
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/qmldir.in5
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/CMakeLists.txt50
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml38
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.cpp137
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.h82
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.cpp45
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h39
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.cpp49
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.cpp96
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.h98
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/qmldir.in5
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.pro14
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.qrc6
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/CMakeLists.txt63
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml43
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/application.pro26
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp150
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h90
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.pro7
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.qrc6
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h20
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.cpp45
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.h39
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/CMakeLists.txt17
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.cpp45
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h36
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/library.pro8
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/main.cpp50
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.cpp96
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.h98
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/qmldir.in5
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extending-qml-advanced.pro10
-rw-r--r--examples/qml/tutorials/extending-qml/CMakeLists.txt2
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/App.qml (renamed from examples/qml/tutorials/extending-qml/chapter1-basics/app.qml)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt38
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc5
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp2
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/qmldir6
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/App.qml (renamed from examples/qml/tutorials/extending-qml/chapter2-methods/app.qml)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt38
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc5
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp2
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/qmldir6
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/App.qml (renamed from examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt38
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc5
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp2
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/qmldir6
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/App.qml (renamed from examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt37
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc5
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp2
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/qmldir6
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/App.qml (renamed from examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt38
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc5
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp2
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/qmldir6
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/App.qml (renamed from examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt37
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt42
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir5
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc5
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp4
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/qmldir3
-rw-r--r--examples/qml/xmlhttprequest/CMakeLists.txt44
-rw-r--r--examples/qml/xmlhttprequest/data.xml5
-rw-r--r--examples/qml/xmlhttprequest/doc/images/qml-xmlhttprequest-example.pngbin20934 -> 0 bytes
-rw-r--r--examples/qml/xmlhttprequest/doc/src/xmlhttprequest.qdoc20
-rw-r--r--examples/qml/xmlhttprequest/main.cpp26
-rw-r--r--examples/qml/xmlhttprequest/methods.js34
-rw-r--r--examples/qml/xmlhttprequest/xmlhttprequest.pro15
-rw-r--r--examples/qml/xmlhttprequest/xmlhttprequest.qml28
-rw-r--r--examples/qml/xmlhttprequest/xmlhttprequest.qmlproject16
-rw-r--r--examples/qml/xmlhttprequest/xmlhttprequest.qrc7
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter1/CMakeLists.txt22
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter1/helloplugin.cpp19
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter1/helloplugin.h21
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter1/plugin.json13
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter1/test.qml24
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter2/CMakeLists.txt22
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter2/helloplugin.cpp56
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter2/helloplugin.h21
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter2/plugin.json13
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter2/test.qml24
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter3/CMakeLists.txt22
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter3/helloplugin.cpp64
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter3/helloplugin.h21
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter3/plugin.json13
-rw-r--r--examples/qmlcompiler/tutorials/helloworld/chapter3/test.qml26
-rw-r--r--examples/qmltest/CMakeLists.txt1
-rw-r--r--examples/qmltest/qmltest.pro2
-rw-r--r--examples/qmltest/qmltest/CMakeLists.txt53
-rw-r--r--examples/qmltest/qmltest/tst_basic.qml41
-rw-r--r--examples/qmltest/qmltest/tst_item.qml20
-rw-r--r--examples/qmltest/qmltest/tst_qmltest.cpp5
-rw-r--r--examples/quick/CMakeLists.txt21
-rw-r--r--examples/quick/animation/CMakeLists.txt34
-rw-r--r--examples/quick/animation/behaviors/tvtennis.qml77
-rw-r--r--examples/quick/animation/behaviors/wigglytext.qml16
-rw-r--r--examples/quick/animation/doc/src/animation.qdoc2
-rw-r--r--examples/quick/animation/easing/easing.qml4
-rw-r--r--examples/quick/animation/pathanimation/pathanimation.qml4
-rw-r--r--examples/quick/animation/pathinterpolator/pathinterpolator.qml4
-rw-r--r--examples/quick/animation/states/states.qml1
-rw-r--r--examples/quick/canvas/CMakeLists.txt34
-rw-r--r--examples/quick/canvas/LabeledSlider.qml23
-rw-r--r--examples/quick/canvas/bezierCurve/bezierCurve.qml143
-rw-r--r--examples/quick/canvas/canvas.qml25
-rw-r--r--examples/quick/canvas/clip/clip.qml175
-rw-r--r--examples/quick/canvas/doc/src/canvas.qdoc1
-rw-r--r--examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml153
-rw-r--r--examples/quick/canvas/roundedrect/roundedrect.qml130
-rw-r--r--examples/quick/canvas/smile/smile.qml143
-rw-r--r--examples/quick/canvas/squircle/squircle.qml166
-rw-r--r--examples/quick/canvas/tiger/tiger.qml131
-rw-r--r--examples/quick/customitems/CMakeLists.txt3
-rw-r--r--examples/quick/customitems/customitems.pro3
-rw-r--r--examples/quick/customitems/dialcontrol/CMakeLists.txt43
-rw-r--r--examples/quick/customitems/dialcontrol/Dial.qml2
-rw-r--r--examples/quick/customitems/dialcontrol/QuitButton.qml1
-rw-r--r--examples/quick/customitems/dialcontrol/dialcontrol.qml2
-rw-r--r--examples/quick/customitems/dialcontrol/doc/src/dialcontrol.qdoc1
-rw-r--r--examples/quick/customitems/flipable/CMakeLists.txt36
-rw-r--r--examples/quick/customitems/flipable/doc/src/flipable.qdoc1
-rw-r--r--examples/quick/customitems/maskedmousearea/CMakeLists.txt50
-rw-r--r--examples/quick/customitems/maskedmousearea/main.cpp17
-rw-r--r--examples/quick/customitems/maskedmousearea/maskedmousearea.cpp104
-rw-r--r--examples/quick/customitems/maskedmousearea/maskedmousearea.h62
-rw-r--r--examples/quick/customitems/maskedmousearea/maskedmousearea.qml95
-rw-r--r--examples/quick/customitems/painteditem/CMakeLists.txt40
-rw-r--r--examples/quick/customitems/painteditem/TextBalloon/CMakeLists.txt23
-rw-r--r--examples/quick/customitems/painteditem/doc/src/textballoons.qdoc1
-rw-r--r--examples/quick/customitems/painteditem/textballoons.qml8
-rw-r--r--examples/quick/delegatechooser/CMakeLists.txt43
-rw-r--r--examples/quick/delegatechooser/delegatechooser.qml99
-rw-r--r--examples/quick/delegatechooser/main.cpp4
-rw-r--r--examples/quick/draganddrop/CMakeLists.txt35
-rw-r--r--examples/quick/draganddrop/doc/src/draganddrop.qdoc1
-rw-r--r--examples/quick/draganddrop/draganddrop.qml5
-rw-r--r--examples/quick/draganddrop/draganddrop.qrc1
-rw-r--r--examples/quick/draganddrop/external/externaldraganddrop.qml84
-rw-r--r--examples/quick/draganddrop/tiles/DragTile.qml25
-rw-r--r--examples/quick/draganddrop/tiles/DropTile.qml15
-rw-r--r--examples/quick/draganddrop/tiles/tiles.qml32
-rw-r--r--examples/quick/draganddrop/views/Icon.qml6
-rw-r--r--examples/quick/draganddrop/views/gridview.qml17
-rw-r--r--examples/quick/embeddedinwidgets/CMakeLists.txt36
-rw-r--r--examples/quick/embeddedinwidgets/Main.qml (renamed from examples/quick/embeddedinwidgets/main.qml)0
-rw-r--r--examples/quick/embeddedinwidgets/doc/images/qml-embeddedinwidgets-example.jpgbin0 -> 27138 bytes
-rw-r--r--examples/quick/embeddedinwidgets/doc/src/embeddedinwidgets.qdoc46
-rw-r--r--examples/quick/embeddedinwidgets/embeddedinwidgets.pro11
-rw-r--r--examples/quick/embeddedinwidgets/embeddedinwidgets.qrc6
-rw-r--r--examples/quick/embeddedinwidgets/main.cpp4
-rw-r--r--examples/quick/embeddedinwidgets/qmldir4
-rw-r--r--examples/quick/embeddedinwidgets/reflect.frag3
-rw-r--r--examples/quick/externaldraganddrop/CMakeLists.txt42
-rw-r--r--examples/quick/externaldraganddrop/DragAndDropTextItem.qml67
-rw-r--r--examples/quick/externaldraganddrop/doc/images/qml-dnd2-example.pngbin48666 -> 0 bytes
-rw-r--r--examples/quick/externaldraganddrop/doc/src/externaldraganddrop.qdoc20
-rw-r--r--examples/quick/externaldraganddrop/externaldraganddrop.pro12
-rw-r--r--examples/quick/externaldraganddrop/externaldraganddrop.qml43
-rw-r--r--examples/quick/externaldraganddrop/externaldraganddrop.qmlproject16
-rw-r--r--examples/quick/externaldraganddrop/externaldraganddrop.qrc6
-rw-r--r--examples/quick/externaldraganddrop/main.cpp4
-rw-r--r--examples/quick/imageelements/BorderImageSelector.qml10
-rw-r--r--examples/quick/imageelements/CMakeLists.txt34
-rw-r--r--examples/quick/imageelements/ImageCell.qml8
-rw-r--r--examples/quick/imageelements/MyBorderImage.qml31
-rw-r--r--examples/quick/imageelements/ShadowRectangle.qml20
-rw-r--r--examples/quick/imageelements/animatedimage.qml7
-rw-r--r--examples/quick/imageelements/animatedsprite.qml13
-rw-r--r--examples/quick/imageelements/borderimage.qml88
-rw-r--r--examples/quick/imageelements/doc/src/imageelements.qdoc1
-rw-r--r--examples/quick/imageelements/framestepping.qml6
-rw-r--r--examples/quick/imageelements/image.qml7
-rw-r--r--examples/quick/imageelements/imageelements.qml17
-rw-r--r--examples/quick/imageelements/multiframeborderimage.qml8
-rw-r--r--examples/quick/imageelements/shadows.qml15
-rw-r--r--examples/quick/imageelements/spritesequence.qml26
-rw-r--r--examples/quick/imageprovider/CMakeLists.txt37
-rw-r--r--examples/quick/imageprovider/doc/src/imageprovider.qdoc1
-rw-r--r--examples/quick/imageresponseprovider/CMakeLists.txt26
-rw-r--r--examples/quick/imageresponseprovider/doc/src/imageresponseprovider.qdoc1
-rw-r--r--examples/quick/itemvariablerefreshrate/CMakeLists.txt32
-rw-r--r--examples/quick/itemvariablerefreshrate/doc/src/itemvariablerefreshrate.qdoc1
-rw-r--r--examples/quick/keyinteraction/CMakeLists.txt34
-rw-r--r--examples/quick/keyinteraction/ContextMenu.qml9
-rw-r--r--examples/quick/keyinteraction/GridMenu.qml52
-rw-r--r--examples/quick/keyinteraction/ListMenu.qml82
-rw-r--r--examples/quick/keyinteraction/ListViewDelegate.qml30
-rw-r--r--examples/quick/keyinteraction/TabMenu.qml45
-rw-r--r--examples/quick/keyinteraction/doc/src/keyinteraction.qdoc1
-rw-r--r--examples/quick/keyinteraction/focus.qml40
-rw-r--r--examples/quick/layouts/CMakeLists.txt34
-rw-r--r--examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc1
-rw-r--r--examples/quick/layouts/layouts.qml35
-rw-r--r--examples/quick/layouts/layouts.qmlproject16
-rw-r--r--examples/quick/localstorage/CMakeLists.txt34
-rw-r--r--examples/quick/localstorage/Database.js20
-rw-r--r--examples/quick/localstorage/Header.qml36
-rw-r--r--examples/quick/localstorage/MyDelegate.qml14
-rw-r--r--examples/quick/localstorage/doc/src/localstorage.qdoc1
-rw-r--r--examples/quick/localstorage/localstorage.qml49
-rw-r--r--examples/quick/models/CMakeLists.txt2
-rw-r--r--examples/quick/models/abstractitemmodel/CMakeLists.txt34
-rw-r--r--examples/quick/models/abstractitemmodel/doc/src/abstractitemmodel-example.qdoc1
-rw-r--r--examples/quick/models/abstractitemmodel/model.h6
-rw-r--r--examples/quick/models/objectlistmodel/CMakeLists.txt34
-rw-r--r--examples/quick/models/objectlistmodel/doc/src/objectlistmodel-example.qdoc1
-rw-r--r--examples/quick/models/stringlistmodel/CMakeLists.txt34
-rw-r--r--examples/quick/models/stringlistmodel/doc/src/stringlistmodel-example.qdoc1
-rw-r--r--examples/quick/mousearea/CMakeLists.txt34
-rw-r--r--examples/quick/mousearea/doc/src/mousearea.qdoc1
-rw-r--r--examples/quick/mousearea/mousearea-wheel-example.qml14
-rw-r--r--examples/quick/mousearea/mousearea.qml127
-rw-r--r--examples/quick/multieffect/CMakeLists.txt2
-rw-r--r--examples/quick/multieffect/itemswitcher/CMakeLists.txt36
-rw-r--r--examples/quick/multieffect/itemswitcher/doc/images/qml-multieffectitemswitcher-example.jpgbin0 -> 69659 bytes
-rw-r--r--examples/quick/multieffect/itemswitcher/doc/src/itemswitcher.qdoc16
-rw-r--r--examples/quick/multieffect/testbed/CMakeLists.txt42
-rw-r--r--examples/quick/multieffect/testbed/doc/images/qml-multieffecttestbed-example.jpgbin0 -> 59436 bytes
-rw-r--r--examples/quick/multieffect/testbed/doc/src/testbed.qdoc16
-rw-r--r--examples/quick/multieffect/testbed/qml.qrc6
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml66
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep383
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml97
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsbbin0 -> 1563 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsbbin0 -> 1934 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsbbin0 -> 6495 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsbbin0 -> 5369 bytes
-rw-r--r--examples/quick/multieffect/testbed/qml/Settings.qml1
-rw-r--r--examples/quick/multieffect/testbed/qml/SettingsView.qml7
-rw-r--r--examples/quick/multieffect/testbed/qml/WarningsView.qml7
-rw-r--r--examples/quick/multieffect/testbed/qml/main.qml37
-rw-r--r--examples/quick/particles/CMakeLists.txt3
-rw-r--r--examples/quick/particles/affectors/CMakeLists.txt34
-rw-r--r--examples/quick/particles/affectors/GreyButton.qml2
-rw-r--r--examples/quick/particles/affectors/attractor.qml1
-rw-r--r--examples/quick/particles/affectors/doc/src/affectors.qdoc1
-rw-r--r--examples/quick/particles/affectors/spritegoal.qml3
-rw-r--r--examples/quick/particles/affectors/turbulence.qml1
-rw-r--r--examples/quick/particles/affectors/wander.qml6
-rw-r--r--examples/quick/particles/emitters/CMakeLists.txt34
-rw-r--r--examples/quick/particles/emitters/customemitter.qml6
-rw-r--r--examples/quick/particles/emitters/doc/src/emitters.qdoc1
-rw-r--r--examples/quick/particles/emitters/velocityfrommotion.qml12
-rw-r--r--examples/quick/particles/imageparticle/CMakeLists.txt34
-rw-r--r--examples/quick/particles/imageparticle/doc/src/imageparticle.qdoc1
-rw-r--r--examples/quick/particles/itemparticle/CMakeLists.txt50
-rw-r--r--examples/quick/particles/itemparticle/delegates.qml58
-rw-r--r--examples/quick/particles/itemparticle/itemparticle.pro10
-rw-r--r--examples/quick/particles/itemparticle/itemparticle.qml262
-rw-r--r--examples/quick/particles/itemparticle/main.cpp4
-rw-r--r--examples/quick/particles/particles.pro1
-rw-r--r--examples/quick/particles/system/CMakeLists.txt34
-rw-r--r--examples/quick/particles/system/doc/src/system.qdoc1
-rw-r--r--examples/quick/particles/system/dynamiccomparison.qml25
-rw-r--r--examples/quick/particles/system/dynamicemitters.qml40
-rw-r--r--examples/quick/particles/system/multiplepainters.qml15
-rw-r--r--examples/quick/particles/system/startstop.qml11
-rw-r--r--examples/quick/pointerhandlers/CMakeLists.txt43
-rw-r--r--examples/quick/pointerhandlers/components/CorkPanel.qml118
-rw-r--r--examples/quick/pointerhandlers/components/FlashAnimation.qml8
-rw-r--r--examples/quick/pointerhandlers/components/MouseFeedbackSprite.qml13
-rw-r--r--examples/quick/pointerhandlers/components/QuadPieMenu.qml7
-rw-r--r--examples/quick/pointerhandlers/components/ScrollBar.qml4
-rw-r--r--examples/quick/pointerhandlers/components/images/cork.jpg (renamed from examples/quick/touchinteraction/flickable/cork.jpg)bin149337 -> 149337 bytes
-rw-r--r--examples/quick/pointerhandlers/components/images/note-yellow.png (renamed from examples/quick/touchinteraction/flickable/note-yellow.png)bin54283 -> 54283 bytes
-rw-r--r--examples/quick/pointerhandlers/components/images/tack.png (renamed from examples/quick/touchinteraction/flickable/tack.png)bin7282 -> 7282 bytes
-rw-r--r--examples/quick/pointerhandlers/corkboards.qml51
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fakeflickable.jpgbin0 -> 104984 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fling.webpbin0 -> 48514 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-joystick.jpgbin0 -> 81973 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-map.webpbin0 -> 424394 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-mixer.webpbin0 -> 185554 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-multibutton.webpbin0 -> 620084 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-piemenu.webpbin0 -> 40846 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pinchhandler.webpbin0 -> 999018 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pointhandler.webpbin0 -> 286790 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/images/pointerhandlers-example-taphandler.webpbin0 -> 155468 bytes
-rw-r--r--examples/quick/pointerhandlers/doc/src/pointerhandlers.qdoc171
-rw-r--r--examples/quick/pointerhandlers/images/blur-circle.png (renamed from examples/quick/touchinteraction/multipointtouch/blur-circle.png)bin4279 -> 4279 bytes
-rw-r--r--examples/quick/pointerhandlers/multiflame.qml69
-rw-r--r--examples/quick/pointerhandlers/pointerhandlers.qml6
-rw-r--r--examples/quick/pointerhandlers/qml.qrc9
-rw-r--r--examples/quick/pointerhandlers/tapHandler.qml34
-rw-r--r--examples/quick/positioners/CMakeLists.txt34
-rw-r--r--examples/quick/positioners/doc/src/positioners.qdoc1
-rw-r--r--examples/quick/positioners/positioners-attachedproperties.qml32
-rw-r--r--examples/quick/positioners/positioners-transitions.qml222
-rw-r--r--examples/quick/positioners/positioners.qml4
-rw-r--r--examples/quick/quick-accessibility/CMakeLists.txt34
-rw-r--r--examples/quick/quick-accessibility/doc/src/accessibility.qdoc1
-rw-r--r--examples/quick/quick.pro11
-rw-r--r--examples/quick/quickshapes/CMakeLists.txt18
-rw-r--r--examples/quick/quickshapes/quickshapes.pro2
-rw-r--r--examples/quick/quickshapes/shapes/CMakeLists.txt70
-rw-r--r--examples/quick/quickshapes/shapes/arcDirection.qml59
-rw-r--r--examples/quick/quickshapes/shapes/arcRotation.qml82
-rw-r--r--examples/quick/quickshapes/shapes/capStyles.qml62
-rw-r--r--examples/quick/quickshapes/shapes/clippedtigers.qml110
-rw-r--r--examples/quick/quickshapes/shapes/cubicCurve.qml164
-rw-r--r--examples/quick/quickshapes/shapes/dashPattern.qml43
-rw-r--r--examples/quick/quickshapes/shapes/doc/images/qml-shapes-example.png (renamed from examples/quick/shapes/doc/images/qml-shapes-example.png)bin49261 -> 49261 bytes
-rw-r--r--examples/quick/quickshapes/shapes/doc/src/shapes.qdoc16
-rw-r--r--examples/quick/quickshapes/shapes/ellipticalArcs.qml93
-rw-r--r--examples/quick/quickshapes/shapes/fillRules.qml62
-rw-r--r--examples/quick/quickshapes/shapes/gradientSpreadModes.qml102
-rw-r--r--examples/quick/quickshapes/shapes/interactive.qml247
-rw-r--r--examples/quick/quickshapes/shapes/joinStyles.qml56
-rw-r--r--examples/quick/quickshapes/shapes/largeOrSmallArc.qml52
-rw-r--r--examples/quick/quickshapes/shapes/linearGradient.qml90
-rw-r--r--examples/quick/quickshapes/shapes/main.cpp4
-rw-r--r--examples/quick/quickshapes/shapes/main.qml20
-rw-r--r--examples/quick/quickshapes/shapes/quadraticCurve.qml57
-rw-r--r--examples/quick/quickshapes/shapes/radialGradient.qml113
-rw-r--r--examples/quick/quickshapes/shapes/sampling.qml257
-rw-r--r--examples/quick/quickshapes/shapes/shapegallery.qml184
-rw-r--r--examples/quick/quickshapes/shapes/shapes.pro10
-rw-r--r--examples/quick/quickshapes/shapes/shapes.qrc28
-rw-r--r--examples/quick/quickshapes/shapes/strokeOrFill.qml120
-rw-r--r--examples/quick/quickshapes/shapes/tapableTriangle.qml76
-rw-r--r--examples/quick/quickshapes/shapes/text.qml32
-rw-r--r--examples/quick/quickshapes/shapes/tiger.qml3670
-rw-r--r--examples/quick/quickshapes/shapes/tigerLoader.qml32
-rw-r--r--examples/quick/quickshapes/shapes/zoomtiger.qml152
-rw-r--r--examples/quick/quickshapes/weatherforecast/BouncyShape.qml33
-rw-r--r--examples/quick/quickshapes/weatherforecast/CMakeLists.txt84
-rw-r--r--examples/quick/quickshapes/weatherforecast/Cloud.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/CloudWithLightning.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/CloudWithLightningAndRain_generated.qml140
-rw-r--r--examples/quick/quickshapes/weatherforecast/CloudWithLightning_generated.qml142
-rw-r--r--examples/quick/quickshapes/weatherforecast/CloudWithRain.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/CloudWithRain_generated.qml114
-rw-r--r--examples/quick/quickshapes/weatherforecast/CloudWithSnow.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/CloudWithSnow_generated.qml74
-rw-r--r--examples/quick/quickshapes/weatherforecast/Cloud_generated.qml59
-rw-r--r--examples/quick/quickshapes/weatherforecast/DemoShape.qml12
-rw-r--r--examples/quick/quickshapes/weatherforecast/Europe.qml176
-rw-r--r--examples/quick/quickshapes/weatherforecast/Europe_generated.qml647
-rw-r--r--examples/quick/quickshapes/weatherforecast/Gear.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/Gear_generated.qml36
-rw-r--r--examples/quick/quickshapes/weatherforecast/MapLabel.qml65
-rw-r--r--examples/quick/quickshapes/weatherforecast/SettingsDrawer.qml117
-rw-r--r--examples/quick/quickshapes/weatherforecast/Sun.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/SunBehindCloud.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/SunBehindCloud_generated.qml90
-rw-r--r--examples/quick/quickshapes/weatherforecast/SunBehindLargeCloud_generated.qml90
-rw-r--r--examples/quick/quickshapes/weatherforecast/SunBehindRainCloud.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/SunBehindRainCloud_generated.qml138
-rw-r--r--examples/quick/quickshapes/weatherforecast/SunBehindSmallCloud.qml5
-rw-r--r--examples/quick/quickshapes/weatherforecast/SunBehindSmallCloud_generated.qml106
-rw-r--r--examples/quick/quickshapes/weatherforecast/Sun_generated.qml119
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/Europe.svg395
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/OFL.txt93
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/WorkSans-Regular.ttfbin0 -> 191916 bytes
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/apache.txt201
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/attribution.txt10
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/cloud-svgrepo-com.svg54
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/cloud-with-lightning-and-rain-svgrepo-com.svg2
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/cloud-with-lightning-svgrepo-com.svg108
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/cloud-with-rain-svgrepo-com.svg2
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/cloud-with-snow-svgrepo-com.svg2
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/gear-alt-stroke.svg40
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/sun-behind-cloud-svgrepo-com.svg2
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/sun-behind-large-cloud-svgrepo-com.svg2
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/sun-behind-rain-cloud-svgrepo-com.svg2
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/sun-behind-small-cloud-svgrepo-com.svg2
-rw-r--r--examples/quick/quickshapes/weatherforecast/assets/sun-svgrepo-com.svg89
-rw-r--r--examples/quick/quickshapes/weatherforecast/doc/images/qml-weatherforecast-example.pngbin0 -> 219725 bytes
-rw-r--r--examples/quick/quickshapes/weatherforecast/doc/images/settings.pngbin0 -> 232654 bytes
-rw-r--r--examples/quick/quickshapes/weatherforecast/doc/images/zoomed.pngbin0 -> 173748 bytes
-rw-r--r--examples/quick/quickshapes/weatherforecast/doc/src/weatherforecast.qdoc125
-rw-r--r--examples/quick/quickshapes/weatherforecast/main.cpp25
-rw-r--r--examples/quick/quickshapes/weatherforecast/main.qml358
-rw-r--r--examples/quick/quickshapes/weatherforecast/qt_attribution.json55
-rw-r--r--examples/quick/quickshapes/weatherforecast/qtquickcontrols2.conf (renamed from examples/quickcontrols/sidepanel/qtquickcontrols2.conf)0
-rw-r--r--examples/quick/quickshapes/weatherforecast/weatherforecast.pro10
-rw-r--r--examples/quick/quickshapes/weatherforecast/weatherforecast.qrc33
-rw-r--r--examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/CMakeLists.txt35
-rw-r--r--examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/doc/src/qquickwidgetversuswindow_opengl.qdoc1
-rw-r--r--examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/main.cpp2
-rw-r--r--examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/wobble.frag3
-rw-r--r--examples/quick/quickwidgets/quickwidget/CMakeLists.txt35
-rw-r--r--examples/quick/quickwidgets/quickwidget/doc/src/quickwidget.qdoc2
-rw-r--r--examples/quick/quickwidgets/quickwidget/main.cpp25
-rw-r--r--examples/quick/quickwidgets/quickwidget/rotatingsquare.qml2
-rw-r--r--examples/quick/rendercontrol/CMakeLists.txt5
-rw-r--r--examples/quick/rendercontrol/rendercontrol_d3d11/CMakeLists.txt34
-rw-r--r--examples/quick/rendercontrol/rendercontrol_d3d11/demo.qml2
-rw-r--r--examples/quick/rendercontrol/rendercontrol_d3d11/doc/src/rendercontrol_d3d11.qdoc2
-rw-r--r--examples/quick/rendercontrol/rendercontrol_d3d11/quad.frag3
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt36
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp2
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/demo.qml2
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol_opengl.qdoc2
-rw-r--r--examples/quick/rendercontrol/rendercontrol_rhi/CMakeLists.txt44
-rw-r--r--examples/quick/rendercontrol/rendercontrol_rhi/demo.qml156
-rw-r--r--examples/quick/rendercontrol/rendercontrol_rhi/doc/images/rendercontrol-rhi-example.jpgbin0 -> 117091 bytes
-rw-r--r--examples/quick/rendercontrol/rendercontrol_rhi/doc/src/rendercontrol_rhi.qdoc140
-rw-r--r--examples/quick/rendercontrol/rendercontrol_rhi/main.cpp640
-rw-r--r--examples/quick/rendercontrol/rendercontrol_rhi/rendercontrol_rhi.pro10
-rw-r--r--examples/quick/rendercontrol/rendercontrol_rhi/rendercontrol_rhi.qrc5
-rw-r--r--examples/quick/responsivelayouts/CMakeLists.txt47
-rw-r--r--examples/quick/responsivelayouts/doc/images/qtquicklayouts-example-responsivelayouts.pngbin0 -> 34381 bytes
-rw-r--r--examples/quick/responsivelayouts/doc/src/qtquickresponsiveLayouts-examples.qdoc67
-rw-r--r--examples/quick/responsivelayouts/icons/grid.svg4
-rw-r--r--examples/quick/responsivelayouts/icons/settings.svg4
-rw-r--r--examples/quick/responsivelayouts/icons/text.svg4
-rw-r--r--examples/quick/responsivelayouts/main.cpp15
-rw-r--r--examples/quick/responsivelayouts/responsivelayouts.pro14
-rw-r--r--examples/quick/responsivelayouts/responsivelayouts.qml160
-rw-r--r--examples/quick/responsivelayouts/responsivelayouts.qrc8
-rw-r--r--examples/quick/righttoleft/CMakeLists.txt48
-rw-r--r--examples/quick/righttoleft/doc/images/qml-righttoleft-example.pngbin35323 -> 0 bytes
-rw-r--r--examples/quick/righttoleft/doc/src/righttoleft.qdoc24
-rw-r--r--examples/quick/righttoleft/layoutdirection/layoutdirection.qml218
-rw-r--r--examples/quick/righttoleft/layoutdirection/layoutdirection.qmlproject16
-rw-r--r--examples/quick/righttoleft/layoutmirroring/layoutmirroring.qml166
-rw-r--r--examples/quick/righttoleft/layoutmirroring/layoutmirroring.qmlproject16
-rw-r--r--examples/quick/righttoleft/main.cpp4
-rw-r--r--examples/quick/righttoleft/righttoleft.pro10
-rw-r--r--examples/quick/righttoleft/righttoleft.qml19
-rw-r--r--examples/quick/righttoleft/righttoleft.qmlproject16
-rw-r--r--examples/quick/righttoleft/righttoleft.qrc8
-rw-r--r--examples/quick/righttoleft/textalignment/textalignment.qml421
-rw-r--r--examples/quick/righttoleft/textalignment/textalignment.qmlproject16
-rw-r--r--examples/quick/scenegraph/CMakeLists.txt5
-rw-r--r--examples/quick/scenegraph/customgeometry/CMakeLists.txt29
-rw-r--r--examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc34
-rw-r--r--examples/quick/scenegraph/custommaterial/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc1
-rw-r--r--examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag22
-rw-r--r--examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsbbin2538 -> 2603 bytes
-rw-r--r--examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsbbin1547 -> 1521 bytes
-rw-r--r--examples/quick/scenegraph/customrendernode/CMakeLists.txt33
-rw-r--r--examples/quick/scenegraph/customrendernode/customrender.cpp189
-rw-r--r--examples/quick/scenegraph/customrendernode/customrender.h7
-rw-r--r--examples/quick/scenegraph/customrendernode/doc/images/customrendernode-example.gifbin44088 -> 0 bytes
-rw-r--r--examples/quick/scenegraph/customrendernode/doc/images/customrendernode-example.jpgbin0 -> 38552 bytes
-rw-r--r--examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc128
-rw-r--r--examples/quick/scenegraph/customrendernode/main.cpp28
-rw-r--r--examples/quick/scenegraph/customrendernode/main.qml16
-rw-r--r--examples/quick/scenegraph/customrendernode/shaders/customrender.frag3
-rw-r--r--examples/quick/scenegraph/d3d11underqml/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc1
-rw-r--r--examples/quick/scenegraph/d3d11underqml/squircle.frag3
-rw-r--r--examples/quick/scenegraph/fboitem/CMakeLists.txt46
-rw-r--r--examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpgbin25863 -> 0 bytes
-rw-r--r--examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc12
-rw-r--r--examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp38
-rw-r--r--examples/quick/scenegraph/fboitem/fboinsgrenderer.h19
-rw-r--r--examples/quick/scenegraph/fboitem/fboitem.pro20
-rw-r--r--examples/quick/scenegraph/fboitem/fboitem.qrc6
-rw-r--r--examples/quick/scenegraph/fboitem/main.cpp22
-rw-r--r--examples/quick/scenegraph/fboitem/main.qml82
-rw-r--r--examples/quick/scenegraph/fboitem/shaders/checker.frag22
-rw-r--r--examples/quick/scenegraph/fboitem/shaders/checker.frag.qsbbin953 -> 0 bytes
-rw-r--r--examples/quick/scenegraph/graph/CMakeLists.txt29
-rw-r--r--examples/quick/scenegraph/graph/doc/src/graph.qdoc1
-rw-r--r--examples/quick/scenegraph/graph/main.cpp2
-rw-r--r--examples/quick/scenegraph/graph/main.qml12
-rw-r--r--examples/quick/scenegraph/graph/shaders/line.frag3
-rw-r--r--examples/quick/scenegraph/graph/shaders/noisy.frag3
-rw-r--r--examples/quick/scenegraph/metaltextureimport/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc6
-rw-r--r--examples/quick/scenegraph/metaltextureimport/squircle.frag3
-rw-r--r--examples/quick/scenegraph/metalunderqml/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc1
-rw-r--r--examples/quick/scenegraph/metalunderqml/squircle.frag3
-rw-r--r--examples/quick/scenegraph/openglunderqml/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc2
-rw-r--r--examples/quick/scenegraph/rhitextureitem/CMakeLists.txt77
-rw-r--r--examples/quick/scenegraph/rhitextureitem/SettingsDrawer.qml117
-rw-r--r--examples/quick/scenegraph/rhitextureitem/doc/images/rhitextureitem-example.jpgbin0 -> 96428 bytes
-rw-r--r--examples/quick/scenegraph/rhitextureitem/doc/src/rhitextureitem.qdoc106
-rw-r--r--examples/quick/scenegraph/rhitextureitem/icon_settings.pngbin0 -> 3175 bytes
-rw-r--r--examples/quick/scenegraph/rhitextureitem/icon_settings@2x.pngbin0 -> 6616 bytes
-rw-r--r--examples/quick/scenegraph/rhitextureitem/icon_settings@3x.pngbin0 -> 10083 bytes
-rw-r--r--examples/quick/scenegraph/rhitextureitem/icon_settings@4x.pngbin0 -> 13502 bytes
-rw-r--r--examples/quick/scenegraph/rhitextureitem/main.cpp18
-rw-r--r--examples/quick/scenegraph/rhitextureitem/main.qml259
-rw-r--r--examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/checker.frag.qsbbin0 -> 1611 bytes
-rw-r--r--examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/color.frag.qsbbin0 -> 853 bytes
-rw-r--r--examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/color.vert.qsbbin0 -> 1256 bytes
-rw-r--r--examples/quick/scenegraph/rhitextureitem/rhitextureitem.cpp141
-rw-r--r--examples/quick/scenegraph/rhitextureitem/rhitextureitem.h57
-rw-r--r--examples/quick/scenegraph/rhitextureitem/rhitextureitem.pro16
-rw-r--r--examples/quick/scenegraph/rhitextureitem/rhitextureitem.qrc8
-rw-r--r--examples/quick/scenegraph/rhitextureitem/shaders/checker.frag26
-rw-r--r--examples/quick/scenegraph/rhitextureitem/shaders/color.frag13
-rw-r--r--examples/quick/scenegraph/rhitextureitem/shaders/color.vert16
-rw-r--r--examples/quick/scenegraph/rhiunderqml/CMakeLists.txt61
-rw-r--r--examples/quick/scenegraph/rhiunderqml/doc/images/rhiunderqml-example.jpgbin0 -> 35984 bytes
-rw-r--r--examples/quick/scenegraph/rhiunderqml/doc/src/rhiunderqml.qdoc236
-rw-r--r--examples/quick/scenegraph/rhiunderqml/main.cpp17
-rw-r--r--examples/quick/scenegraph/rhiunderqml/main.qml39
-rw-r--r--examples/quick/scenegraph/rhiunderqml/prebuilts_for_qmake/squircle_rhi.frag.qsbbin0 -> 1537 bytes
-rw-r--r--examples/quick/scenegraph/rhiunderqml/prebuilts_for_qmake/squircle_rhi.vert.qsbbin0 -> 1214 bytes
-rw-r--r--examples/quick/scenegraph/rhiunderqml/rhisquircle.cpp224
-rw-r--r--examples/quick/scenegraph/rhiunderqml/rhisquircle.h43
-rw-r--r--examples/quick/scenegraph/rhiunderqml/rhiunderqml.pro11
-rw-r--r--examples/quick/scenegraph/rhiunderqml/rhiunderqml.qrc7
-rw-r--r--examples/quick/scenegraph/scenegraph.pro7
-rw-r--r--examples/quick/scenegraph/shared/logorenderer.cpp219
-rw-r--r--examples/quick/scenegraph/shared/logorenderer.h41
-rw-r--r--examples/quick/scenegraph/shared/squircle_rhi.frag14
-rw-r--r--examples/quick/scenegraph/shared/squircle_rhi.vert7
-rw-r--r--examples/quick/scenegraph/threadedanimation/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/threadedanimation/doc/images/threadedanimation-example.jpgbin0 -> 37409 bytes
-rw-r--r--examples/quick/scenegraph/threadedanimation/doc/src/threadedanimation.qdoc33
-rw-r--r--examples/quick/scenegraph/twotextureproviders/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/twotextureproviders/doc/src/twotextureproviders.qdoc1
-rw-r--r--examples/quick/scenegraph/twotextureproviders/shaders/checker.frag3
-rw-r--r--examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag3
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/doc/src/vulkantextureimport.qdoc3
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/CMakeLists.txt31
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc88
-rw-r--r--examples/quick/shadereffects/CMakeLists.txt34
-rw-r--r--examples/quick/shadereffects/content/shaders/blur.frag3
-rw-r--r--examples/quick/shadereffects/content/shaders/colorize.frag3
-rw-r--r--examples/quick/shadereffects/content/shaders/outline.frag3
-rw-r--r--examples/quick/shadereffects/content/shaders/shadow.frag3
-rw-r--r--examples/quick/shadereffects/content/shaders/wobble.frag3
-rw-r--r--examples/quick/shadereffects/doc/src/shadereffects.qdoc1
-rw-r--r--examples/quick/shadereffects/shadereffects.qml8
-rw-r--r--examples/quick/shapes/CMakeLists.txt69
-rw-r--r--examples/quick/shapes/arcDirection.qml52
-rw-r--r--examples/quick/shapes/arcRotation.qml68
-rw-r--r--examples/quick/shapes/capStyles.qml47
-rw-r--r--examples/quick/shapes/clippedtigers.qml90
-rw-r--r--examples/quick/shapes/cubicCurve.qml129
-rw-r--r--examples/quick/shapes/dashPattern.qml53
-rw-r--r--examples/quick/shapes/doc/src/shapes.qdoc15
-rw-r--r--examples/quick/shapes/ellipticalArcs.qml70
-rw-r--r--examples/quick/shapes/fillRules.qml44
-rw-r--r--examples/quick/shapes/gradientSpreadModes.qml68
-rw-r--r--examples/quick/shapes/interactive.qml214
-rw-r--r--examples/quick/shapes/joinStyles.qml50
-rw-r--r--examples/quick/shapes/largeOrSmallArc.qml46
-rw-r--r--examples/quick/shapes/linearGradient.qml41
-rw-r--r--examples/quick/shapes/main.cpp4
-rw-r--r--examples/quick/shapes/main.qml19
-rw-r--r--examples/quick/shapes/quadraticCurve.qml55
-rw-r--r--examples/quick/shapes/radialGradient.qml44
-rw-r--r--examples/quick/shapes/sampling.qml139
-rw-r--r--examples/quick/shapes/shapegallery.qml153
-rw-r--r--examples/quick/shapes/shapes.pro10
-rw-r--r--examples/quick/shapes/shapes.qrc27
-rw-r--r--examples/quick/shapes/strokeOrFill.qml107
-rw-r--r--examples/quick/shapes/tapableTriangle.qml54
-rw-r--r--examples/quick/shapes/text.qml26
-rw-r--r--examples/quick/shapes/tiger.qml3667
-rw-r--r--examples/quick/shapes/tigerLoader.qml32
-rw-r--r--examples/quick/shared/CMakeLists.txt24
-rw-r--r--examples/quick/shared/FlickrRssModel.qml45
-rw-r--r--examples/quick/shared/LauncherList.qml16
-rw-r--r--examples/quick/shared/QtBundleQmlModuleForMacOS.cmake32
-rw-r--r--examples/quick/shared/SimpleLauncherDelegate.qml73
-rw-r--r--examples/quick/shared/qmldir1
-rw-r--r--examples/quick/shared/shared.qrc1
-rw-r--r--examples/quick/tableview/gameoflife/CMakeLists.txt42
-rw-r--r--examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc1
-rw-r--r--examples/quick/tableview/gameoflife/main.qml3
-rw-r--r--examples/quick/tableview/pixelator/CMakeLists.txt42
-rw-r--r--examples/quick/tableview/pixelator/doc/src/pixelator.qdoc1
-rw-r--r--examples/quick/text/CMakeLists.txt39
-rw-r--r--examples/quick/text/doc/src/text.qdoc1
-rw-r--r--examples/quick/text/fonts/availableFonts.qml7
-rw-r--r--examples/quick/text/fonts/banner.qml31
-rw-r--r--examples/quick/text/fonts/fonts.qml51
-rw-r--r--examples/quick/text/fonts/hello.qml29
-rw-r--r--examples/quick/text/styledtext-layout.qml12
-rw-r--r--examples/quick/text/text.qml13
-rw-r--r--examples/quick/text/text.qrc5
-rw-r--r--examples/quick/text/textselection/textselection.qml178
-rw-r--r--examples/quick/threading/CMakeLists.txt52
-rw-r--r--examples/quick/threading/doc/images/qml-threading-example.pngbin22778 -> 0 bytes
-rw-r--r--examples/quick/threading/doc/src/threading.qdoc44
-rw-r--r--examples/quick/threading/main.cpp4
-rw-r--r--examples/quick/threading/threadedlistmodel/dataloader.mjs12
-rw-r--r--examples/quick/threading/threadedlistmodel/doc/src/threadedlistmodel.qdoc13
-rw-r--r--examples/quick/threading/threadedlistmodel/threadedlistmodel.qmlproject19
-rw-r--r--examples/quick/threading/threadedlistmodel/timedisplay.qml42
-rw-r--r--examples/quick/threading/threading.pro10
-rw-r--r--examples/quick/threading/threading.qml18
-rw-r--r--examples/quick/threading/threading.qmlproject17
-rw-r--r--examples/quick/threading/threading.qrc10
-rw-r--r--examples/quick/threading/workerscript/Spinner.qml51
-rw-r--r--examples/quick/threading/workerscript/workerscript.mjs27
-rw-r--r--examples/quick/threading/workerscript/workerscript.qml62
-rw-r--r--examples/quick/threading/workerscript/workerscript.qmlproject19
-rw-r--r--examples/quick/touchinteraction/CMakeLists.txt70
-rw-r--r--examples/quick/touchinteraction/doc/images/qml-touchinteraction-example.pngbin35860 -> 0 bytes
-rw-r--r--examples/quick/touchinteraction/doc/src/touchinteraction.qdoc55
-rw-r--r--examples/quick/touchinteraction/flickable/Panel.qml113
-rw-r--r--examples/quick/touchinteraction/flickable/basic-flickable.qml33
-rw-r--r--examples/quick/touchinteraction/flickable/corkboards.qml51
-rw-r--r--examples/quick/touchinteraction/main.cpp4
-rw-r--r--examples/quick/touchinteraction/multipointtouch/AugmentedTouchPoint.qml35
-rw-r--r--examples/quick/touchinteraction/multipointtouch/BearWhackParticleSystem.qml146
-rw-r--r--examples/quick/touchinteraction/multipointtouch/ParticleFlame.qml33
-rw-r--r--examples/quick/touchinteraction/multipointtouch/bearwhack.qml97
-rw-r--r--examples/quick/touchinteraction/multipointtouch/multiflame.qml57
-rw-r--r--examples/quick/touchinteraction/pincharea/flickresize.qml62
-rw-r--r--examples/quick/touchinteraction/touchinteraction.pro10
-rw-r--r--examples/quick/touchinteraction/touchinteraction.qml21
-rw-r--r--examples/quick/touchinteraction/touchinteraction.qmlproject17
-rw-r--r--examples/quick/touchinteraction/touchinteraction.qrc27
-rw-r--r--examples/quick/tutorials/dynamicview/CMakeLists.txt2
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview1/CMakeLists.txt34
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview1/PetsModel.qml54
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml21
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview2/CMakeLists.txt34
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview2/PetsModel.qml54
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview2/dynamicview.qml46
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview3/CMakeLists.txt34
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview3/PetsModel.qml54
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml53
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview4/CMakeLists.txt34
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview4/ListSelector.qml40
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview4/PetsModel.qml54
-rw-r--r--examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml76
-rw-r--r--examples/quick/tutorials/samegame/CMakeLists.txt2
-rw-r--r--examples/quick/tutorials/samegame/samegame1/CMakeLists.txt34
-rw-r--r--examples/quick/tutorials/samegame/samegame2/CMakeLists.txt34
-rw-r--r--examples/quick/tutorials/samegame/samegame3/CMakeLists.txt34
-rw-r--r--examples/quick/tutorials/samegame/samegame4/CMakeLists.txt34
-rw-r--r--examples/quick/tutorials/samegame/samegame4/samegame.js11
-rw-r--r--examples/quick/views/CMakeLists.txt42
-rw-r--r--examples/quick/views/delegatemodel/dragselection.qml116
-rw-r--r--examples/quick/views/delegatemodel/flipable.pro9
-rw-r--r--examples/quick/views/delegatemodel/flipable.qrc9
-rw-r--r--examples/quick/views/delegatemodel/main.cpp4
-rw-r--r--examples/quick/views/delegatemodel/slideshow.qml121
-rw-r--r--examples/quick/views/doc/src/views.qdoc8
-rw-r--r--examples/quick/views/gridview/gridview-example.qml23
-rw-r--r--examples/quick/views/listview/content/PressAndHoldButton.qml2
-rw-r--r--examples/quick/views/listview/content/SmallText.qml2
-rw-r--r--examples/quick/views/listview/displaymargin.qml6
-rw-r--r--examples/quick/views/listview/dynamiclist.qml100
-rw-r--r--examples/quick/views/listview/expandingdelegates.qml77
-rw-r--r--examples/quick/views/listview/highlight.qml44
-rw-r--r--examples/quick/views/listview/highlightranges.qml63
-rw-r--r--examples/quick/views/listview/sections.qml101
-rw-r--r--examples/quick/views/objectmodel/objectmodel.qml62
-rw-r--r--examples/quick/views/package/Delegate.qml43
-rw-r--r--examples/quick/views/package/view.qml20
-rw-r--r--examples/quick/views/pathview/pathview-example.qml84
-rw-r--r--examples/quick/views/views.qml3
-rw-r--r--examples/quick/views/views.qrc2
-rw-r--r--examples/quick/window/CMakeLists.txt34
-rw-r--r--examples/quick/window/Splash.qml19
-rw-r--r--examples/quick/window/doc/src/window.qdoc1
-rw-r--r--examples/quick/window/window.qml46
-rw-r--r--examples/quick/window/window.qrc2
-rw-r--r--examples/quickcontrols/CMakeLists.txt14
-rw-r--r--examples/quickcontrols/attachedstyleproperties/CMakeLists.txt38
-rw-r--r--examples/quickcontrols/attachedstyleproperties/MyStyle/CMakeLists.txt22
-rw-r--r--examples/quickcontrols/attachedstyleproperties/MyStyle/Label.qml3
-rw-r--r--examples/quickcontrols/attachedstyleproperties/MyStyle/Popup.qml3
-rw-r--r--examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp2
-rw-r--r--examples/quickcontrols/attachedstyleproperties/attachedstyleproperties.qml9
-rw-r--r--examples/quickcontrols/attachedstyleproperties/doc/src/qtquickcontrols-attachedstyleproperties.qdoc1
-rw-r--r--examples/quickcontrols/chattutorial/CMakeLists.txt12
-rw-r--r--examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt37
-rw-r--r--examples/quickcontrols/chattutorial/chapter1/Main.qml23
-rw-r--r--examples/quickcontrols/chattutorial/chapter1/chapter1.pro7
-rw-r--r--examples/quickcontrols/chattutorial/chapter1/main.cpp4
-rw-r--r--examples/quickcontrols/chattutorial/chapter1/main.qml23
-rw-r--r--examples/quickcontrols/chattutorial/chapter1/qmldir2
-rw-r--r--examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt37
-rw-r--r--examples/quickcontrols/chattutorial/chapter2/Main.qml50
-rw-r--r--examples/quickcontrols/chattutorial/chapter2/chapter2.pro6
-rw-r--r--examples/quickcontrols/chattutorial/chapter2/main.cpp4
-rw-r--r--examples/quickcontrols/chattutorial/chapter2/main.qml45
-rw-r--r--examples/quickcontrols/chattutorial/chapter2/qmldir2
-rw-r--r--examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt37
-rw-r--r--examples/quickcontrols/chattutorial/chapter3/ContactPage.qml10
-rw-r--r--examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml22
-rw-r--r--examples/quickcontrols/chattutorial/chapter3/Main.qml18
-rw-r--r--examples/quickcontrols/chattutorial/chapter3/chapter3.pro6
-rw-r--r--examples/quickcontrols/chattutorial/chapter3/main.cpp4
-rw-r--r--examples/quickcontrols/chattutorial/chapter3/main.qml19
-rw-r--r--examples/quickcontrols/chattutorial/chapter3/qmldir4
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt39
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/ContactPage.qml14
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml40
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/Main.qml18
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/chapter4.pro11
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/main.cpp10
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/main.qml19
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/qmldir5
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp4
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h6
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp2
-rw-r--r--examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h4
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml2
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt39
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml2
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/ContactPage.qml14
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml38
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/Main.qml18
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/chapter5.pro11
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/main.cpp10
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/main.qml19
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/qmldir6
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp4
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h6
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp2
-rw-r--r--examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h4
-rw-r--r--examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc111
-rw-r--r--examples/quickcontrols/contactlist/CMakeLists.txt37
-rw-r--r--examples/quickcontrols/contactlist/ContactDelegate.ui.qml17
-rw-r--r--examples/quickcontrols/contactlist/ContactDialog.qml8
-rw-r--r--examples/quickcontrols/contactlist/ContactForm.ui.qml2
-rw-r--r--examples/quickcontrols/contactlist/ContactList.qml70
-rw-r--r--examples/quickcontrols/contactlist/ContactView.ui.qml9
-rw-r--r--examples/quickcontrols/contactlist/SectionDelegate.ui.qml6
-rw-r--r--examples/quickcontrols/contactlist/contactlist.pro15
-rw-r--r--examples/quickcontrols/contactlist/contactlist.qml70
-rw-r--r--examples/quickcontrols/contactlist/contactmodel.cpp2
-rw-r--r--examples/quickcontrols/contactlist/contactmodel.h4
-rw-r--r--examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml2
-rw-r--r--examples/quickcontrols/contactlist/doc/src/qtquickcontrols-contactlist.qdoc2
-rw-r--r--examples/quickcontrols/contactlist/main.cpp8
-rw-r--r--examples/quickcontrols/contactlist/qmldir8
-rw-r--r--examples/quickcontrols/eventcalendar/CMakeLists.txt31
-rw-r--r--examples/quickcontrols/eventcalendar/doc/src/qtquickcontrols-eventcalendar.qdoc1
-rw-r--r--examples/quickcontrols/eventcalendar/eventmodel.cpp2
-rw-r--r--examples/quickcontrols/eventcalendar/eventmodel.h2
-rw-r--r--examples/quickcontrols/eventcalendar/sqleventdatabase.cpp2
-rw-r--r--examples/quickcontrols/eventcalendar/sqleventdatabase.h2
-rw-r--r--examples/quickcontrols/filesystemexplorer/CMakeLists.txt84
-rw-r--r--examples/quickcontrols/filesystemexplorer/Main.qml184
-rw-r--r--examples/quickcontrols/filesystemexplorer/doc/images/qtquickcontrols-filesystemexplorer.webpbin0 -> 47416 bytes
-rw-r--r--examples/quickcontrols/filesystemexplorer/doc/src/qtquickcontrols-filesystemexplorer.qdoc123
-rw-r--r--examples/quickcontrols/filesystemexplorer/filesystemexplorer.pro60
-rw-r--r--examples/quickcontrols/filesystemexplorer/filesystemmodel.cpp89
-rw-r--r--examples/quickcontrols/filesystemexplorer/filesystemmodel.h40
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/app_icon.svg2
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/folder_closed.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/folder_open.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/generic_file.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/globe.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/info_sign.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/light_bulb.svg43
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/qt_logo.svg26
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/read.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/icons/resize.svg6
-rw-r--r--examples/quickcontrols/filesystemexplorer/linenumbermodel.cpp58
-rw-r--r--examples/quickcontrols/filesystemexplorer/linenumbermodel.h32
-rw-r--r--examples/quickcontrols/filesystemexplorer/main.cpp43
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/About.qml93
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/Colors.qml23
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/Editor.qml161
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/FileSystemView.qml156
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/MyMenu.qml45
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/MyMenuBar.qml177
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/ResizeButton.qml23
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/Sidebar.qml140
-rw-r--r--examples/quickcontrols/filesystemexplorer/qml/WindowDragHandler.qml16
-rw-r--r--examples/quickcontrols/filesystemexplorer/qmldir12
-rw-r--r--examples/quickcontrols/flatstyle/CMakeLists.txt33
-rw-r--r--examples/quickcontrols/flatstyle/MainForm.ui.qml1
-rw-r--r--examples/quickcontrols/flatstyle/doc/src/qtquickcontrols-flatstyle.qdoc1
-rw-r--r--examples/quickcontrols/gallery/CMakeLists.txt33
-rw-r--r--examples/quickcontrols/gallery/doc/src/qtquickcontrols-gallery.qdoc17
-rw-r--r--examples/quickcontrols/gallery/gallery.qml128
-rw-r--r--examples/quickcontrols/gallery/images/qt-logo.pngbin1301 -> 2849 bytes
-rw-r--r--examples/quickcontrols/gallery/images/qt-logo@2x.pngbin2611 -> 4936 bytes
-rw-r--r--examples/quickcontrols/gallery/images/qt-logo@3x.pngbin4155 -> 7254 bytes
-rw-r--r--examples/quickcontrols/gallery/images/qt-logo@4x.pngbin5916 -> 9505 bytes
-rw-r--r--examples/quickcontrols/gallery/pages/BusyIndicatorPage.qml6
-rw-r--r--examples/quickcontrols/gallery/pages/ButtonPage.qml10
-rw-r--r--examples/quickcontrols/gallery/pages/CheckBoxPage.qml10
-rw-r--r--examples/quickcontrols/gallery/pages/ComboBoxPage.qml21
-rw-r--r--examples/quickcontrols/gallery/pages/DelayButtonPage.qml6
-rw-r--r--examples/quickcontrols/gallery/pages/DelegatePage.qml262
-rw-r--r--examples/quickcontrols/gallery/pages/DialPage.qml4
-rw-r--r--examples/quickcontrols/gallery/pages/DialogPage.qml44
-rw-r--r--examples/quickcontrols/gallery/pages/FramePage.qml8
-rw-r--r--examples/quickcontrols/gallery/pages/GroupBoxPage.qml10
-rw-r--r--examples/quickcontrols/gallery/pages/PageIndicatorPage.qml2
-rw-r--r--examples/quickcontrols/gallery/pages/ProgressBarPage.qml4
-rw-r--r--examples/quickcontrols/gallery/pages/RadioButtonPage.qml10
-rw-r--r--examples/quickcontrols/gallery/pages/RangeSliderPage.qml2
-rw-r--r--examples/quickcontrols/gallery/pages/ScrollBarPage.qml4
-rw-r--r--examples/quickcontrols/gallery/pages/ScrollIndicatorPage.qml4
-rw-r--r--examples/quickcontrols/gallery/pages/SliderPage.qml2
-rw-r--r--examples/quickcontrols/gallery/pages/SpinBoxPage.qml4
-rw-r--r--examples/quickcontrols/gallery/pages/StackViewPage.qml17
-rw-r--r--examples/quickcontrols/gallery/pages/SwipeViewPage.qml8
-rw-r--r--examples/quickcontrols/gallery/pages/SwitchPage.qml10
-rw-r--r--examples/quickcontrols/gallery/pages/TabBarPage.qml14
-rw-r--r--examples/quickcontrols/gallery/pages/TextAreaPage.qml6
-rw-r--r--examples/quickcontrols/gallery/pages/TextFieldPage.qml4
-rw-r--r--examples/quickcontrols/gallery/pages/ToolTipPage.qml6
-rw-r--r--examples/quickcontrols/gallery/pages/TumblerPage.qml2
-rw-r--r--examples/quickcontrols/imagine/automotive/CMakeLists.txt31
-rw-r--r--examples/quickcontrols/imagine/automotive/doc/src/qtquickcontrols-automotive.qdoc1
-rw-r--r--examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered.pngbin2532 -> 2144 bytes
-rw-r--r--examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered@2x.pngbin4629 -> 3784 bytes
-rw-r--r--examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed.pngbin1271 -> 1178 bytes
-rw-r--r--examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed@2x.pngbin3230 -> 2475 bytes
-rw-r--r--examples/quickcontrols/imagine/automotive/imagine-assets/dial-background.pngbin1037 -> 1014 bytes
-rw-r--r--examples/quickcontrols/imagine/automotive/imagine-assets/dial-background@2x.pngbin2817 -> 2182 bytes
-rw-r--r--examples/quickcontrols/imagine/automotive/qml/automotive.qml51
-rw-r--r--examples/quickcontrols/imagine/automotive/qtquickcontrols2.conf8
-rw-r--r--examples/quickcontrols/imagine/musicplayer/CMakeLists.txt187
-rw-r--r--examples/quickcontrols/imagine/musicplayer/doc/images/qtquickcontrols-musicplayer.pngbin1457884 -> 0 bytes
-rw-r--r--examples/quickcontrols/imagine/musicplayer/doc/src/qtquickcontrols-musicplayer.qdoc35
-rw-r--r--examples/quickcontrols/imagine/musicplayer/musicplayer.cpp24
-rw-r--r--examples/quickcontrols/imagine/musicplayer/musicplayer.qml420
-rw-r--r--examples/quickcontrols/imagine/musicplayer/qtquickcontrols2.conf10
-rw-r--r--examples/quickcontrols/ios/todolist/AppSettings.qml2
-rw-r--r--examples/quickcontrols/ios/todolist/CMakeLists.txt21
-rw-r--r--examples/quickcontrols/ios/todolist/Database.qml2
-rw-r--r--examples/quickcontrols/ios/todolist/FontSizePage.qml2
-rw-r--r--examples/quickcontrols/ios/todolist/HomePage.qml5
-rw-r--r--examples/quickcontrols/ios/todolist/MaxTasksPage.qml3
-rw-r--r--examples/quickcontrols/ios/todolist/NavBar.qml2
-rw-r--r--examples/quickcontrols/ios/todolist/ProjectPage.qml5
-rw-r--r--examples/quickcontrols/ios/todolist/SettingsPage.qml4
-rw-r--r--examples/quickcontrols/ios/todolist/ToggleCompletedTasksPage.qml3
-rw-r--r--examples/quickcontrols/ios/todolist/main.qml2
-rw-r--r--examples/quickcontrols/ios/todolist/src/main.cpp2
-rw-r--r--examples/quickcontrols/quickcontrols.pro9
-rw-r--r--examples/quickcontrols/sidepanel/sidepanel.cpp17
-rw-r--r--examples/quickcontrols/sidepanel/sidepanel.qml132
-rw-r--r--examples/quickcontrols/swipetoremove/swipetoremove.cpp20
-rw-r--r--examples/quickcontrols/swipetoremove/swipetoremove.qml130
-rw-r--r--examples/quickcontrols/texteditor/CMakeLists.txt40
-rw-r--r--examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc181
-rw-r--r--examples/quickcontrols/texteditor/documenthandler.cpp348
-rw-r--r--examples/quickcontrols/texteditor/documenthandler.h121
-rw-r--r--examples/quickcontrols/texteditor/example.md10
-rw-r--r--examples/quickcontrols/texteditor/fonts/fontello.ttfbin10208 -> 19044 bytes
-rw-r--r--examples/quickcontrols/texteditor/images/qt-logo.pngbin4039 -> 3255 bytes
-rw-r--r--examples/quickcontrols/texteditor/qml/+touch/texteditor.qml130
-rw-r--r--examples/quickcontrols/texteditor/qml/texteditor.qml349
-rw-r--r--examples/quickcontrols/texteditor/texteditor.cpp12
-rw-r--r--examples/quickcontrols/texteditor/texteditor.html27
-rw-r--r--examples/quickcontrols/texteditor/texteditor.pro7
-rw-r--r--examples/quickcontrols/wearable/CMakeLists.txt220
-rw-r--r--examples/quickcontrols/wearable/Wearable/AlarmsPage.qml172
-rw-r--r--examples/quickcontrols/wearable/Wearable/AppSettings.qml14
-rw-r--r--examples/quickcontrols/wearable/Wearable/CMakeLists.txt152
-rw-r--r--examples/quickcontrols/wearable/Wearable/Clock.qml248
-rw-r--r--examples/quickcontrols/wearable/Wearable/DemoMode.qml174
-rw-r--r--examples/quickcontrols/wearable/Wearable/DemoModeIndicator.qml57
-rw-r--r--examples/quickcontrols/wearable/Wearable/FitnessPage.qml283
-rw-r--r--examples/quickcontrols/wearable/Wearable/LauncherPage.qml185
-rw-r--r--examples/quickcontrols/wearable/Wearable/ListHeaderItem.qml83
-rw-r--r--examples/quickcontrols/wearable/Wearable/ListItem.qml26
-rw-r--r--examples/quickcontrols/wearable/Wearable/Main.qml125
-rw-r--r--examples/quickcontrols/wearable/Wearable/MenuHeader.qml92
-rw-r--r--examples/quickcontrols/wearable/Wearable/NavigationFallbackPage.qml100
-rw-r--r--examples/quickcontrols/wearable/Wearable/NavigationPage.qml228
-rw-r--r--examples/quickcontrols/wearable/Wearable/NotificationsPage.qml77
-rw-r--r--examples/quickcontrols/wearable/Wearable/RouteElement.qml70
-rw-r--r--examples/quickcontrols/wearable/Wearable/SettingsPage.qml136
-rw-r--r--examples/quickcontrols/wearable/Wearable/SwipeViewPage.qml (renamed from examples/quickcontrols/wearable/qml/SwipeViewPage.qml)0
-rw-r--r--examples/quickcontrols/wearable/Wearable/WeatherPage.qml330
-rw-r--r--examples/quickcontrols/wearable/Wearable/WorldClockPage.qml59
-rw-r--r--examples/quickcontrols/wearable/Wearable/fallbackroute.json78
-rw-r--r--examples/quickcontrols/wearable/Wearable/fitness.js34
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/back-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/back-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/bearleft-dark.svg7
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/bearleft-light.svg7
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/bearright-dark.svg7
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/bearright-light.svg7
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/bell-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/bell-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/bluetooth-dark.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/bluetooth-light.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/clock-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/clock-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/darkmode-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/darkmode-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/demomode-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/demomode-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/drop-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/drop-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/forward-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/forward-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/hearth-dark.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/hearth-light.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/left-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/left-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/lightleft-dark.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/lightleft-light.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/lightright-dark.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/lightright-light.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/maps-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/maps-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/notification-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/notification-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/plus-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/plus-light.svg5
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/pressure-dark.svg9
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/pressure-light.svg9
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/right-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/right-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/settings-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/settings-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/sun-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/sun-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/sunrise-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/sunrise-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/sunset-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/sunset-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/thermometer-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/thermometer-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/uturnleft-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/uturnleft-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/uturnright-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/uturnright-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-dark.svg10
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-few-clouds-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-few-clouds-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-fog-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-fog-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-haze-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-haze-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-icy-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-icy-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-light.svg10
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-overcast-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-overcast-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-showers-dark.svg6
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-showers-light.svg6
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-showers-scattered-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-showers-scattered-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-sleet-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-sleet-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-snow-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-snow-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-storm-dark.svg6
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-storm-light.svg6
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-sunny-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-sunny-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-sunny-very-few-clouds-dark.svg10
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-sunny-very-few-clouds-light.svg10
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-thundershower-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/weather-thundershower-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/wifi-dark.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/wifi-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/wind-dark.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/wind-light.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/icons/yellowsun.svg4
-rw-r--r--examples/quickcontrols/wearable/Wearable/images/fallbackmap.pngbin0 -> 143664 bytes
-rw-r--r--examples/quickcontrols/wearable/Wearable/images/fitness-man-running-dark.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/images/fitness-man-running-light.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/images/fitness-man-walking-dark.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/images/fitness-man-walking-light.svg3
-rw-r--r--examples/quickcontrols/wearable/Wearable/navigation.js119
-rw-r--r--examples/quickcontrols/wearable/Wearable/notifications.js28
-rw-r--r--examples/quickcontrols/wearable/Wearable/qmldir19
-rw-r--r--examples/quickcontrols/wearable/Wearable/weather.js51
-rw-r--r--examples/quickcontrols/wearable/Wearable/weather.json (renamed from examples/quickcontrols/wearable/qml/Weather/weather.json)0
-rw-r--r--examples/quickcontrols/wearable/WearableSettings/CMakeLists.txt45
-rw-r--r--examples/quickcontrols/wearable/WearableSettings/WearableSettings.qml14
-rw-r--r--examples/quickcontrols/wearable/WearableSettings/qmldir5
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/CMakeLists.txt51
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/PageIndicator.qml42
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/Slider.qml59
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/Switch.qml54
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/UIStyle.qml114
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/fonts/OFL.txt93
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-Bold.ttfbin0 -> 53896 bytes
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-Regular.ttfbin0 -> 57392 bytes
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-SemiBold.ttfbin0 -> 56752 bytes
-rw-r--r--examples/quickcontrols/wearable/WearableStyle/qmldir8
-rw-r--r--examples/quickcontrols/wearable/doc/images/qtquickcontrols-wearable.pngbin108926 -> 28479 bytes
-rw-r--r--examples/quickcontrols/wearable/doc/src/qtquickcontrols-wearable.qdoc136
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36/alarms.pngbin510 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36/fitness.pngbin443 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36/navigation.pngbin474 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36/notifications.pngbin559 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36/settings.pngbin544 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36/weather.pngbin427 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36/worldclock.pngbin470 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36@2/alarms.pngbin871 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36@2/fitness.pngbin743 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36@2/navigation.pngbin803 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36@2/notifications.pngbin1016 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36@2/settings.pngbin802 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36@2/weather.pngbin747 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/36x36@2/worldclock.pngbin817 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/icons/wearable/index.theme14
-rw-r--r--examples/quickcontrols/wearable/images/back.pngbin834 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/back@2x.pngbin528 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/back@3x.pngbin788 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/back@4x.pngbin1002 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/background-dark.pngbin28157 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/background-light.pngbin343879 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/home.pngbin856 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/home@2x.pngbin653 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/home@3x.pngbin909 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/images/home@4x.pngbin1167 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Alarms/AlarmsPage.qml67
-rw-r--r--examples/quickcontrols/wearable/qml/DemoMode.qml211
-rw-r--r--examples/quickcontrols/wearable/qml/DemoModeIndicator.qml47
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/FitnessPage.qml84
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/fitness.js18
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/images/man-running-dark.pngbin1400 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/images/man-running-dark@2x.pngbin2260 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/images/man-running-light.pngbin1216 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/images/man-running-light@2x.pngbin2473 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/images/man-walking-dark.pngbin1408 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/images/man-walking-dark@2x.pngbin2215 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/images/man-walking-light.pngbin1231 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Fitness/images/man-walking-light@2x.pngbin2308 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/LauncherPage.qml142
-rw-r--r--examples/quickcontrols/wearable/qml/NaviButton.qml36
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/NavigationPage.qml73
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/RouteElement.qml48
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/end.pngbin1515 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/end@2x.pngbin1383 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/leftturn-dark.pngbin1670 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/leftturn-dark@2x.pngbin1191 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/leftturn-light.pngbin1280 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/leftturn-light@2x.pngbin1191 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/marker.pngbin1515 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/navigation-dark.pngbin474 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/navigation-dark@2x.pngbin802 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/navigation-light.pngbin474 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/navigation-light@2x.pngbin803 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/rightturn-dark.pngbin1670 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/rightturn-dark@2x.pngbin1176 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/rightturn-light.pngbin1246 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/rightturn-light@2x.pngbin1176 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/start.pngbin1515 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/start@2x.pngbin1364 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/straight-dark.pngbin1112 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/straight-dark@2x.pngbin654 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/straight-light.pngbin896 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/straight-light@2x.pngbin654 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/uturn.pngbin1833 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/images/uturn@2x.pngbin4780 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/navigation.js84
-rw-r--r--examples/quickcontrols/wearable/qml/Navigation/walk_route.json1
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/NotificationsPage.qml65
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/avatarf-dark.pngbin1848 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/avatarf-dark@2x.pngbin1540 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/avatarf-light.pngbin1565 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/avatarf-light@2x.pngbin1540 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/avatarm-dark.pngbin1622 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/avatarm-dark@2x.pngbin1367 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/avatarm-light.pngbin1367 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/avatarm-light@2x.pngbin1367 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/missedcall-dark.pngbin1994 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/missedcall-dark@2x.pngbin1374 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/missedcall-light.pngbin1976 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/images/missedcall-light@2x.pngbin1374 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Notifications/notifications.js25
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/SettingsPage.qml129
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/bluetooth-dark.pngbin793 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/bluetooth-dark@2x.pngbin2668 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/bluetooth-light.pngbin1678 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/bluetooth-light@2x.pngbin2718 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/brightness-dark.pngbin1276 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/brightness-dark@2x.pngbin1127 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/brightness-light.pngbin1277 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/brightness-light@2x.pngbin1127 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/demo-mode-dark.pngbin797 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/demo-mode-dark@2x.pngbin1154 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/demo-mode-light.pngbin797 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/demo-mode-light@2x.pngbin1150 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/demo-mode-white.pngbin576 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/demo-mode-white@2x.pngbin1069 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/demo-mode.svg93
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/theme-dark.pngbin1129 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/theme-dark@2x.pngbin2002 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/theme-light.pngbin1138 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/theme-light@2x.pngbin2004 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/theme.svg84
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/wifi-dark.pngbin1476 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/wifi-dark@2x.pngbin1072 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/wifi-light.pngbin1477 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Settings/images/wifi-light@2x.pngbin1072 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Style/PageIndicator.qml42
-rw-r--r--examples/quickcontrols/wearable/qml/Style/Slider.qml39
-rw-r--r--examples/quickcontrols/wearable/qml/Style/Switch.qml39
-rw-r--r--examples/quickcontrols/wearable/qml/Style/UIStyle.qml60
-rw-r--r--examples/quickcontrols/wearable/qml/Style/qmldir1
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/WeatherPage.qml250
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/humidity-dark.pngbin1673 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/humidity-dark@2x.pngbin1440 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/humidity-light.pngbin1619 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/humidity-light@2x.pngbin1440 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/pressure-dark.pngbin1506 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/pressure-dark@2x.pngbin1542 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/pressure-light.pngbin1508 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/pressure-light@2x.pngbin1543 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/sunrise-dark.pngbin1813 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/sunrise-dark@2x.pngbin1235 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/sunrise-light.pngbin1703 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/sunrise-light@2x.pngbin1235 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/sunset-dark.pngbin1809 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/sunset-dark@2x.pngbin1267 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/sunset-light.pngbin1755 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/sunset-light@2x.pngbin1267 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/temperature-dark.pngbin1232 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/temperature-dark@2x.pngbin939 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/temperature-light.pngbin1147 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/temperature-light@2x.pngbin939 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/wind-dark.pngbin1715 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/wind-dark@2x.pngbin1106 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/wind-light.pngbin1609 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/images/wind-light@2x.pngbin1106 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/Weather/weather.js30
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/Clock.qml141
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/WorldClockPage.qml60
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/center.pngbin691 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/center@2x.pngbin1796 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/clock-night.pngbin24236 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/clock-night@2x.pngbin64764 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/clock.pngbin11173 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/second.pngbin158 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/second@2x.pngbin115 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissdaydial.pngbin5078 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissdaydial@2x.pngbin10146 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissdayhour.pngbin181 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissdayhour@2x.pngbin269 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissdayminute.pngbin187 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissdayminute@2x.pngbin371 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissnightdial.pngbin4970 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissnightdial@2x.pngbin13594 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissnighthour.pngbin260 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissnighthour@2x.pngbin492 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissnightminute.pngbin187 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissnightminute@2x.pngbin365 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/qml/WorldClock/images/swissseconds.pngbin1120 -> 0 bytes
-rw-r--r--examples/quickcontrols/wearable/wearable.cpp16
-rw-r--r--examples/quickcontrols/wearable/wearable.pro7
-rw-r--r--examples/quickcontrols/wearable/wearable.qml93
-rw-r--r--examples/quickcontrols/wearable/wearable.qrc284
-rw-r--r--src/3rdparty/masm/WeakRandom.h40
-rw-r--r--src/3rdparty/masm/config.h40
-rw-r--r--src/3rdparty/masm/qt_attribution.json26
-rw-r--r--src/3rdparty/masm/stubs/ExecutableAllocator.h40
-rw-r--r--src/3rdparty/masm/stubs/JSGlobalData.h40
-rw-r--r--src/3rdparty/masm/stubs/Options.cpp40
-rw-r--r--src/3rdparty/masm/stubs/Options.h40
-rw-r--r--src/3rdparty/masm/stubs/SuperSampler.h40
-rw-r--r--src/3rdparty/masm/stubs/WTFStubs.cpp40
-rw-r--r--src/3rdparty/masm/stubs/WTFStubs.h40
-rw-r--r--src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h40
-rw-r--r--src/3rdparty/masm/stubs/runtime/VM.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/FastAllocBase.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/FastMalloc.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/HashMap.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/HashSet.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/Noncopyable.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/Optional.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/OwnPtr.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/PassOwnPtr.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/PassRefPtr.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/RefCounted.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/RefPtr.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/TypeTraits.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/UnusedParam.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/Vector.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/text/CString.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/text/StringBuilder.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/text/WTFString.h40
-rw-r--r--src/3rdparty/masm/stubs/wtf/unicode/Unicode.h40
-rw-r--r--src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp40
-rw-r--r--src/3rdparty/masm/wtf/OSAllocatorIntegrity.cpp42
-rw-r--r--src/3rdparty/masm/wtf/OSAllocatorPosix.cpp11
-rw-r--r--src/3rdparty/masm/wtf/Platform.h10
-rw-r--r--src/CMakeLists.txt38
-rw-r--r--src/core/CMakeLists.txt14
-rw-r--r--src/core/doc/qtqmlcore.qdocconf2
-rw-r--r--src/core/doc/src/includes/qmlpermissions.qdocinc34
-rw-r--r--src/core/doc/src/qmlpermissions.qdoc193
-rw-r--r--src/core/doc/src/qtqmlcore-qmltypes.qdoc2
-rw-r--r--src/core/doc/src/qtqmlcore.qdoc10
-rw-r--r--src/core/qqmlcoreglobal_p.h2
-rw-r--r--src/core/qqmlpermissions_p.h117
-rw-r--r--src/core/qqmlsettings.cpp27
-rw-r--r--src/core/qqmlsettings_p.h2
-rw-r--r--src/core/qqmlstandardpaths.cpp8
-rw-r--r--src/core/qqmlstandardpaths_p.h2
-rw-r--r--src/core/qqmlsysteminformation_p.h2
-rw-r--r--src/effects/CMakeLists.txt356
-rw-r--r--src/effects/data/shaders/bluritems.frag3
-rw-r--r--src/effects/data/shaders/bluritems.frag.qsbbin1738 -> 0 bytes
-rw-r--r--src/effects/data/shaders/bluritems.vert.qsbbin2395 -> 0 bytes
-rw-r--r--src/effects/data/shaders/compile.bat46
-rw-r--r--src/effects/data/shaders/multieffect.frag3
-rw-r--r--src/effects/data/shaders/multieffect_c.vert.qsbbin2439 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_c0.frag.qsbbin2862 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cb1.frag.qsbbin3345 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cb2.frag.qsbbin3492 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cb3.frag.qsbbin3612 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cbs1.frag.qsbbin4004 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cbs2.frag.qsbbin4206 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cbs3.frag.qsbbin4399 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cm0.frag.qsbbin3365 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cmb1.frag.qsbbin3838 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cmb2.frag.qsbbin3994 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cmb3.frag.qsbbin4138 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cmbs1.frag.qsbbin4511 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cmbs2.frag.qsbbin4727 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cmbs3.frag.qsbbin4884 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cms0.frag.qsbbin3752 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cms1.frag.qsbbin4235 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cms2.frag.qsbbin4432 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cms3.frag.qsbbin4560 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cs.vert.qsbbin2680 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cs0.frag.qsbbin3230 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cs1.frag.qsbbin3771 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cs2.frag.qsbbin3927 -> 0 bytes
-rw-r--r--src/effects/data/shaders/multieffect_cs3.frag.qsbbin4031 -> 0 bytes
-rw-r--r--src/effects/qgfxsourceproxy_p.h12
-rw-r--r--src/effects/qquickmultieffect.cpp114
-rw-r--r--src/effects/qquickmultieffect_p.h2
-rw-r--r--src/effects/qquickmultieffect_p_p.h2
-rw-r--r--src/effects/qtquickeffectsglobal_p.h2
-rw-r--r--src/imports/CMakeLists.txt1
-rw-r--r--src/imports/builtins/CMakeLists.txt16
-rw-r--r--src/imports/builtins/builtins.qmltypes273
-rw-r--r--src/imports/builtins/jsroot.qmltypes3395
-rw-r--r--src/imports/tooling/Component.qml15
-rw-r--r--src/imports/tooling/Enum.qml2
-rw-r--r--src/imports/tooling/Method.qml1
-rw-r--r--src/imports/tooling/Module.qml2
-rw-r--r--src/labs/animation/CMakeLists.txt3
-rw-r--r--src/labs/animation/qqmlanimationglobal_p.h2
-rw-r--r--src/labs/animation/qquickboundaryrule.cpp28
-rw-r--r--src/labs/animation/qquickboundaryrule_p.h26
-rw-r--r--src/labs/folderlistmodel/CMakeLists.txt3
-rw-r--r--src/labs/folderlistmodel/fileproperty_p.h8
-rw-r--r--src/labs/folderlistmodel/qquickfolderlistmodel.cpp73
-rw-r--r--src/labs/folderlistmodel/qquickfolderlistmodel_p.h34
-rw-r--r--src/labs/folderlistmodel/qquickfolderlistmodelglobal_p.h2
-rw-r--r--src/labs/models/CMakeLists.txt3
-rw-r--r--src/labs/models/doc/src/qmllabsmodels.qdoc4
-rw-r--r--src/labs/models/qqmldelegatecomponent.cpp26
-rw-r--r--src/labs/models/qqmldelegatecomponent_p.h23
-rw-r--r--src/labs/models/qqmlmodelsglobal_p.h2
-rw-r--r--src/labs/models/qqmltablemodel_p.h11
-rw-r--r--src/labs/models/qqmltablemodelcolumn.cpp82
-rw-r--r--src/labs/models/qqmltablemodelcolumn_p.h6
-rw-r--r--src/labs/platform/CMakeLists.txt106
-rw-r--r--src/labs/platform/doc/qtlabsplatform.qdocconf2
-rw-r--r--src/labs/platform/doc/snippets/qtlabsplatform-menu-instantiator.qml2
-rw-r--r--src/labs/platform/qquicklabsplatformcolordialog_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformdialog_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformfiledialog_p.h4
-rw-r--r--src/labs/platform/qquicklabsplatformfolderdialog_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformfontdialog_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformicon_p.h6
-rw-r--r--src/labs/platform/qquicklabsplatformiconloader_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformmenu.cpp4
-rw-r--r--src/labs/platform/qquicklabsplatformmenu_p.h9
-rw-r--r--src/labs/platform/qquicklabsplatformmenubar_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformmenuitem.cpp6
-rw-r--r--src/labs/platform/qquicklabsplatformmenuitem_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformmenuitemgroup_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformmenuseparator_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformmessagedialog_p.h2
-rw-r--r--src/labs/platform/qquicklabsplatformstandardpaths_p.h4
-rw-r--r--src/labs/platform/qquicklabsplatformsystemtrayicon.cpp9
-rw-r--r--src/labs/platform/qquicklabsplatformsystemtrayicon_p.h4
-rw-r--r--src/labs/platform/widgets/qwidgetplatformfiledialog.cpp6
-rw-r--r--src/labs/platform/widgets/qwidgetplatformmessagedialog.cpp2
-rw-r--r--src/labs/platform/widgets/qwidgetplatformsystemtrayicon.cpp2
-rw-r--r--src/labs/settings/CMakeLists.txt3
-rw-r--r--src/labs/settings/qqmlsettings.cpp6
-rw-r--r--src/labs/settings/qqmlsettings_p.h4
-rw-r--r--src/labs/settings/qqmlsettingsglobal_p.h2
-rw-r--r--src/labs/sharedimage/CMakeLists.txt3
-rw-r--r--src/labs/sharedimage/qsharedimageloader.cpp2
-rw-r--r--src/labs/sharedimage/qsharedimageloader_p.h2
-rw-r--r--src/labs/sharedimage/qsharedimageprovider.cpp2
-rw-r--r--src/labs/sharedimage/qsharedimageprovider_p.h4
-rw-r--r--src/labs/sharedimage/qtlabssharedimageglobal_p.h4
-rw-r--r--src/labs/wavefrontmesh/CMakeLists.txt3
-rw-r--r--src/labs/wavefrontmesh/qqmlwavefrontmeshglobal_p.h2
-rw-r--r--src/labs/wavefrontmesh/qwavefrontmesh_p.h10
-rw-r--r--src/particles/CMakeLists.txt152
-rw-r--r--src/particles/qquickage_p.h2
-rw-r--r--src/particles/qquickangledirection_p.h4
-rw-r--r--src/particles/qquickcumulativedirection.cpp2
-rw-r--r--src/particles/qquickcumulativedirection_p.h4
-rw-r--r--src/particles/qquickcustomaffector.cpp40
-rw-r--r--src/particles/qquickcustomaffector_p.h4
-rw-r--r--src/particles/qquickdirection_p.h4
-rw-r--r--src/particles/qquickellipseextruder_p.h2
-rw-r--r--src/particles/qquickfriction_p.h2
-rw-r--r--src/particles/qquickgravity_p.h2
-rw-r--r--src/particles/qquickgroupgoal_p.h2
-rw-r--r--src/particles/qquickimageparticle.cpp161
-rw-r--r--src/particles/qquickimageparticle_p.h6
-rw-r--r--src/particles/qquickitemparticle.cpp61
-rw-r--r--src/particles/qquickitemparticle_p.h12
-rw-r--r--src/particles/qquicklineextruder.cpp2
-rw-r--r--src/particles/qquicklineextruder_p.h2
-rw-r--r--src/particles/qquickmaskextruder.cpp2
-rw-r--r--src/particles/qquickmaskextruder_p.h4
-rw-r--r--src/particles/qquickparticleaffector.cpp2
-rw-r--r--src/particles/qquickparticleaffector_p.h2
-rw-r--r--src/particles/qquickparticleemitter.cpp54
-rw-r--r--src/particles/qquickparticleemitter_p.h4
-rw-r--r--src/particles/qquickparticleextruder_p.h4
-rw-r--r--src/particles/qquickparticlegroup.cpp2
-rw-r--r--src/particles/qquickparticlegroup_p.h2
-rw-r--r--src/particles/qquickparticlepainter.cpp14
-rw-r--r--src/particles/qquickparticlepainter_p.h2
-rw-r--r--src/particles/qquickparticlesystem.cpp180
-rw-r--r--src/particles/qquickparticlesystem_p.h110
-rw-r--r--src/particles/qquickpointattractor.cpp64
-rw-r--r--src/particles/qquickpointattractor_p.h2
-rw-r--r--src/particles/qquickpointdirection_p.h2
-rw-r--r--src/particles/qquickrectangleextruder_p.h2
-rw-r--r--src/particles/qquickspritegoal_p.h2
-rw-r--r--src/particles/qquicktargetdirection_p.h2
-rw-r--r--src/particles/qquicktrailemitter.cpp44
-rw-r--r--src/particles/qquicktrailemitter_p.h6
-rw-r--r--src/particles/qquickturbulence.cpp2
-rw-r--r--src/particles/qquickturbulence_p.h2
-rw-r--r--src/particles/qquickv4particledata.cpp265
-rw-r--r--src/particles/qquickv4particledata_p.h107
-rw-r--r--src/particles/qquickwander_p.h2
-rw-r--r--src/particles/qtquickparticlesglobal_p.h2
-rwxr-xr-xsrc/particles/shaders_ng/compile.bat28
-rw-r--r--src/particles/shaders_ng/imageparticle.frag7
-rw-r--r--src/particles/shaders_ng/imageparticle.vert22
-rw-r--r--src/particles/shaders_ng/imageparticle_colored.frag.qsbbin1676 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_colored.vert.qsbbin4026 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_coloredpoint.frag.qsbbin1663 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_coloredpoint.vert.qsbbin3526 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_deformed.frag.qsbbin1689 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_deformed.vert.qsbbin5389 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_simplepoint.frag.qsbbin1655 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_simplepoint.vert.qsbbin3474 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_sprite.frag.qsbbin1988 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_sprite.vert.qsbbin6028 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_tabled.frag.qsbbin1875 -> 0 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_tabled.vert.qsbbin5694 -> 0 bytes
-rw-r--r--src/plugins/CMakeLists.txt6
-rw-r--r--src/plugins/qmllint/quick/plugin.json16
-rw-r--r--src/plugins/qmllint/quick/quicklintplugin.cpp390
-rw-r--r--src/plugins/qmllint/quick/quicklintplugin.h53
-rw-r--r--src/plugins/qmlls/CMakeLists.txt6
-rw-r--r--src/plugins/qmlls/quick/CMakeLists.txt14
-rw-r--r--src/plugins/qmlls/quick/plugin.json8
-rw-r--r--src/plugins/qmlls/quick/qqmllsquickplugin.cpp187
-rw-r--r--src/plugins/qmlls/quick/qqmllsquickplugin_p.h47
-rw-r--r--src/plugins/qmltooling/packetprotocol/CMakeLists.txt1
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp23
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp9
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp3
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp12
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp7
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp50
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h15
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp5
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp3
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp4
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h4
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp1
-rw-r--r--src/qml/CMakeLists.txt219
-rw-r--r--src/qml/Qt6AndroidQmlMacros.cmake92
-rw-r--r--src/qml/Qt6QmlBuildInternals.cmake32
-rw-r--r--src/qml/Qt6QmlConfigExtras.cmake.in14
-rw-r--r--src/qml/Qt6QmlDeploySupport.cmake81
-rw-r--r--src/qml/Qt6QmlMacros.cmake1257
-rw-r--r--src/qml/Qt6QmlPluginTemplate.cpp.in3
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp8
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h10
-rw-r--r--src/qml/animations/qanimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qanimationjobutil_p.h12
-rw-r--r--src/qml/animations/qcontinuinganimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qparallelanimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qpauseanimationjob_p.h2
-rw-r--r--src/qml/animations/qsequentialanimationgroupjob_p.h2
-rw-r--r--src/qml/common/qjsnumbercoercion.cpp30
-rw-r--r--src/qml/common/qjsnumbercoercion.h40
-rw-r--r--src/qml/common/qqmljsmemorypool_p.h10
-rw-r--r--src/qml/common/qqmlsignalnames.cpp257
-rw-r--r--src/qml/common/qqmlsignalnames_p.h59
-rw-r--r--src/qml/common/qqmltranslation_p.h6
-rw-r--r--src/qml/common/qv4compileddata.cpp431
-rw-r--r--src/qml/common/qv4compileddata_p.h466
-rw-r--r--src/qml/common/qv4staticvalue_p.h9
-rw-r--r--src/qml/common/qv4stringtoarrayindex_p.h2
-rw-r--r--src/qml/compat/removed_api.cpp16
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp262
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h57
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h2
-rw-r--r--src/qml/compiler/qv4bytecodehandler_p.h2
-rw-r--r--src/qml/compiler/qv4codegen.cpp486
-rw-r--r--src/qml/compiler/qv4codegen_p.h50
-rw-r--r--src/qml/compiler/qv4compiler.cpp56
-rw-r--r--src/qml/compiler/qv4compiler_p.h7
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h11
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp234
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h30
-rw-r--r--src/qml/configure.cmake62
-rw-r--r--src/qml/debugger/qqmlabstractprofileradapter_p.h4
-rw-r--r--src/qml/debugger/qqmldebug.cpp2
-rw-r--r--src/qml/debugger/qqmldebugconnector_p.h6
-rw-r--r--src/qml/debugger/qqmldebugserver_p.h2
-rw-r--r--src/qml/debugger/qqmldebugserverconnection_p.h4
-rw-r--r--src/qml/debugger/qqmldebugservice.cpp8
-rw-r--r--src/qml/debugger/qqmldebugservice_p.h2
-rw-r--r--src/qml/debugger/qqmldebugservicefactory_p.h2
-rw-r--r--src/qml/debugger/qqmldebugserviceinterfaces.cpp17
-rw-r--r--src/qml/debugger/qqmldebugserviceinterfaces_p.h16
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h2
-rw-r--r--src/qml/doc/images/extending-qml-advanced-word-cloud.pngbin0 -> 73771 bytes
-rw-r--r--src/qml/doc/qtqml.qdocconf6
-rw-r--r--src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt2
-rw-r--r--src/qml/doc/snippets/code/backend/main.qml4
-rw-r--r--src/qml/doc/snippets/code/doc_src_qtqml.cmake2
-rw-r--r--src/qml/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp24
-rw-r--r--src/qml/doc/snippets/qml/Button.qml2
-rw-r--r--src/qml/doc/snippets/qml/CMakeLists.txt2
-rw-r--r--src/qml/doc/snippets/qml/DynamicText.qml2
-rw-r--r--src/qml/doc/snippets/qml/SelfDestroyingRect.qml2
-rw-r--r--src/qml/doc/snippets/qml/Sprite.qml2
-rw-r--r--src/qml/doc/snippets/qml/XHRForm.qml83
-rw-r--r--src/qml/doc/snippets/qml/application.qml2
-rw-r--r--src/qml/doc/snippets/qml/comments.qml2
-rw-r--r--src/qml/doc/snippets/qml/component.qml2
-rw-r--r--src/qml/doc/snippets/qml/component/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/component/main.qml2
-rw-r--r--src/qml/doc/snippets/qml/createComponent-simple.qml2
-rw-r--r--src/qml/doc/snippets/qml/createComponent.qml2
-rw-r--r--src/qml/doc/snippets/qml/createQmlObject.qml4
-rw-r--r--src/qml/doc/snippets/qml/dynamicObjects-destroy.qml2
-rw-r--r--src/qml/doc/snippets/qml/events.qml2
-rw-r--r--src/qml/doc/snippets/qml/exposing-state/RequiredProperties.qml23
-rw-r--r--src/qml/doc/snippets/qml/exposing-state/createWithInitialProperties.cpp23
-rw-r--r--src/qml/doc/snippets/qml/exposing-state/singleton.h49
-rw-r--r--src/qml/doc/snippets/qml/exposing-state/useSingleton.qml9
-rw-r--r--src/qml/doc/snippets/qml/imports/installed-module.qml2
-rw-r--r--src/qml/doc/snippets/qml/imports/named-imports.qml24
-rw-r--r--src/qml/doc/snippets/qml/imports/qtquick-1.0.qml2
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/connectjs.qml2
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml2
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFive.qml4
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFour.qml4
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleOne.qml4
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleThree.qml4
-rw-r--r--src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleTwo.qml4
-rw-r--r--src/qml/doc/snippets/qml/properties.qml17
-rw-r--r--src/qml/doc/snippets/qml/qml-documents/A.qml2
-rw-r--r--src/qml/doc/snippets/qml/qml-documents/B.qml2
-rw-r--r--src/qml/doc/snippets/qml/qml-documents/Images.qml2
-rw-r--r--src/qml/doc/snippets/qml/qml-documents/LabeledImageBox.qml2
-rw-r--r--src/qml/doc/snippets/qml/qml-documents/inline-component.qml2
-rw-r--r--src/qml/doc/snippets/qml/qml-documents/inline-text-component.qml2
-rw-r--r--src/qml/doc/snippets/qml/qml-documents/non-trivial.qml2
-rw-r--r--src/qml/doc/snippets/qml/qml-documents/qmldocuments.qml2
-rw-r--r--src/qml/doc/snippets/qml/qsTr.qml2
-rw-r--r--src/qml/doc/snippets/qml/qsTrId.1.qml2
-rw-r--r--src/qml/doc/snippets/qml/qsTrId.qml2
-rw-r--r--src/qml/doc/snippets/qml/qsTranslate.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtBinding.1.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtBinding.2.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtBinding.3.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtBinding.4.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtLater.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtTrIdNoOp.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtTrNoOp.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtTranslateNoOp.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/context-advanced/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/context-advanced/connections.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/context/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/loading/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/properties-qml/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/variantlistmap/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtobject.qml2
-rw-r--r--src/qml/doc/snippets/qml/reusablecomponents/Button.qml2
-rw-r--r--src/qml/doc/snippets/qml/reusablecomponents/application.qml2
-rw-r--r--src/qml/doc/snippets/qml/reusablecomponents/component.qml2
-rw-r--r--src/qml/doc/snippets/qml/reusablecomponents/focusbutton.qml2
-rw-r--r--src/qml/doc/snippets/qml/xmlhttprequest.js25
-rw-r--r--src/qml/doc/snippets/qmllint/config.ini (renamed from src/quick/doc/snippets/qmllint/config.ini)0
-rw-r--r--src/qml/doc/snippets/qmltc/CMakeLists.txt5
-rw-r--r--src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp2
-rw-r--r--src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp5
-rw-r--r--src/qml/doc/src/cmake/cmake-properties.qdoc4
-rw-r--r--src/qml/doc/src/cmake/cmake-variables.qdoc44
-rw-r--r--src/qml/doc/src/cmake/policy/qtp0001.qdoc21
-rw-r--r--src/qml/doc/src/cmake/policy/qtp0004.qdoc34
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc123
-rw-r--r--src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc23
-rw-r--r--src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc44
-rw-r--r--src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc9
-rw-r--r--src/qml/doc/src/cmake/qt_target_qml_sources.qdoc2
-rw-r--r--src/qml/doc/src/cppclasses/topic.qdoc2
-rw-r--r--src/qml/doc/src/cppintegration/contextproperties.qdoc17
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc90
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc143
-rw-r--r--src/qml/doc/src/cppintegration/exposecppattributes.qdoc46
-rw-r--r--src/qml/doc/src/cppintegration/exposecppstate.qdoc66
-rw-r--r--src/qml/doc/src/cppintegration/extending-tutorial-advanced.qdoc380
-rw-r--r--src/qml/doc/src/cppintegration/extending-tutorial.qdoc73
-rw-r--r--src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc3
-rw-r--r--src/qml/doc/src/cppintegration/topic.qdoc18
-rw-r--r--src/qml/doc/src/examples.qdoc47
-rw-r--r--src/qml/doc/src/external-resources.qdoc21
-rw-r--r--src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.qdocinc2
-rw-r--r--src/qml/doc/src/includes/qqmlcomponent.qdoc2
-rw-r--r--src/qml/doc/src/includes/qualified-class-name.qdocinc7
-rw-r--r--src/qml/doc/src/javascript/dynamicobjectcreation.qdoc10
-rw-r--r--src/qml/doc/src/javascript/finetuning.qdoc20
-rw-r--r--src/qml/doc/src/javascript/functionlist.qdoc4
-rw-r--r--src/qml/doc/src/javascript/hostenvironment.qdoc40
-rw-r--r--src/qml/doc/src/javascript/memory.qdoc2
-rw-r--r--src/qml/doc/src/javascript/number.qdoc25
-rw-r--r--src/qml/doc/src/javascript/qmlglobalobject.qdoc109
-rw-r--r--src/qml/doc/src/javascript/string.qdoc26
-rw-r--r--src/qml/doc/src/javascript/topic.qdoc19
-rw-r--r--src/qml/doc/src/javascript/xmlhttprequest.qdoc301
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc268
-rw-r--r--src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc163
-rw-r--r--src/qml/doc/src/qmllanguageref/documents/scope.qdoc1
-rw-r--r--src/qml/doc/src/qmllanguageref/documents/structure.qdoc291
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc6
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc82
-rw-r--r--src/qml/doc/src/qmllanguageref/qmlreference.qdoc5
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/basics.qdoc9
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc22
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/imports.qdoc21
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc122
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/signals.qdoc87
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/namespaces.qdoc16
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/references.qdoc53
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/sequencetypes.qdoc76
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/topic.qdoc46
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc76
-rw-r--r--src/qml/doc/src/qmllint/access-singleton-via-object.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/alias-cycle.qdoc60
-rw-r--r--src/qml/doc/src/qmllint/attached-property-reuse.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/compiler.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/deferred-property-id.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/deprecated.qdoc23
-rw-r--r--src/qml/doc/src/qmllint/duplicate-property-binding.qdoc122
-rw-r--r--src/qml/doc/src/qmllint/duplicated-name.qdoc70
-rw-r--r--src/qml/doc/src/qmllint/import.qdoc174
-rw-r--r--src/qml/doc/src/qmllint/incompatible-type.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/inheritance-cycle.qdoc46
-rw-r--r--src/qml/doc/src/qmllint/invalid-lint-directive.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/missing-enum-entry.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/missing-property.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/missing-type.qdoc240
-rw-r--r--src/qml/doc/src/qmllint/multiline-strings.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/non-list-property.qdoc85
-rw-r--r--src/qml/doc/src/qmllint/plugin.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/prefixed-import-type.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/qtqml-qmllint-warnings-and-errors.qdoc9
-rw-r--r--src/qml/doc/src/qmllint/read-only-property.qdoc38
-rw-r--r--src/qml/doc/src/qmllint/recursion-depth-errors.qdoc53
-rw-r--r--src/qml/doc/src/qmllint/required.qdoc65
-rw-r--r--src/qml/doc/src/qmllint/restricted-type.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/signal-handler-parameters.qdoc261
-rw-r--r--src/qml/doc/src/qmllint/syntax.duplicate-ids.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/syntax.id-quotation.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/syntax.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/top-level-component.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/uncreatable-type.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/unqualified.qdoc52
-rw-r--r--src/qml/doc/src/qmllint/unresolved-alias.qdoc50
-rw-r--r--src/qml/doc/src/qmllint/unresolved-type.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/unused-imports.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/use-proper-function.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/var-used-before-declaration.qdoc26
-rw-r--r--src/qml/doc/src/qmllint/with.qdoc50
-rw-r--r--src/qml/doc/src/qmlsingletons.qdoc339
-rw-r--r--src/qml/doc/src/qmltypereference.qdoc49
-rw-r--r--src/qml/doc/src/qt6-changes.qdoc2
-rw-r--r--src/qml/doc/src/qtqml-cpp.qdoc2
-rw-r--r--src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc24
-rw-r--r--src/qml/doc/src/qtqml-tool-qmlcachegen.qdoc12
-rw-r--r--src/qml/doc/src/qtqml-tool-qmlsc.qdoc30
-rw-r--r--src/qml/doc/src/qtqml-tool-qmltc.qdoc275
-rw-r--r--src/qml/doc/src/qtqml-tooling.qdoc48
-rw-r--r--src/qml/doc/src/qtqml-writing-a-module.qdoc209
-rw-r--r--src/qml/doc/src/qtqml.qdoc154
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qml.qdoc107
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc146
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qmlimportscanner.qdoc15
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qmllint.qdoc141
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qmlls.qdoc168
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qmlpreview.qdoc74
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qmlprofiler.qdoc10
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-qmltyperegistrar.qdoc10
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc92
-rw-r--r--src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc120
-rw-r--r--src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc283
-rw-r--r--src/qml/doc/src/tools/qtquickcompiler/qtqml-tool-qmlcachegen.qdoc13
-rw-r--r--src/qml/inlinecomponentutils_p.h26
-rw-r--r--src/qml/jit/qv4assemblercommon.cpp18
-rw-r--r--src/qml/jit/qv4assemblercommon_p.h13
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp5
-rw-r--r--src/qml/jit/qv4baselinejit.cpp18
-rw-r--r--src/qml/jit/qv4baselinejit_p.h6
-rw-r--r--src/qml/jsapi/qjsengine.cpp54
-rw-r--r--src/qml/jsapi/qjsengine.h25
-rw-r--r--src/qml/jsapi/qjsengine_p.h2
-rw-r--r--src/qml/jsapi/qjslist.cpp18
-rw-r--r--src/qml/jsapi/qjslist.h368
-rw-r--r--src/qml/jsapi/qjsmanagedvalue.cpp8
-rw-r--r--src/qml/jsapi/qjsprimitivevalue.cpp45
-rw-r--r--src/qml/jsapi/qjsprimitivevalue.h151
-rw-r--r--src/qml/jsapi/qjsvalue.cpp35
-rw-r--r--src/qml/jsapi/qjsvalue.h3
-rw-r--r--src/qml/jsapi/qjsvalue_p.h12
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp16
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h8
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp32
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp11
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper.cpp29
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_p.h3
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_unix.cpp23
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_win.cpp29
-rw-r--r--src/qml/jsruntime/qv4context.cpp18
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp57
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h8
-rw-r--r--src/qml/jsruntime/qv4engine.cpp664
-rw-r--r--src/qml/jsruntime/qv4engine_p.h66
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h3
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4estable.cpp23
-rw-r--r--src/qml/jsruntime/qv4estable_p.h11
-rw-r--r--src/qml/jsruntime/qv4executableallocator_p.h2
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp466
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h314
-rw-r--r--src/qml/jsruntime/qv4function.cpp145
-rw-r--r--src/qml/jsruntime/qv4function_p.h48
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp24
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h26
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4global_p.h3
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp31
-rw-r--r--src/qml/jsruntime/qv4identifierhash.cpp4
-rw-r--r--src/qml/jsruntime/qv4identifierhash_p.h2
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp27
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h6
-rw-r--r--src/qml/jsruntime/qv4include.cpp20
-rw-r--r--src/qml/jsruntime/qv4include_p.h1
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp43
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h27
-rw-r--r--src/qml/jsruntime/qv4jscall.cpp23
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h480
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp27
-rw-r--r--src/qml/jsruntime/qv4jsonobject_p.h7
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp89
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h62
-rw-r--r--src/qml/jsruntime/qv4managed_p.h2
-rw-r--r--src/qml/jsruntime/qv4mapobject.cpp3
-rw-r--r--src/qml/jsruntime/qv4math_p.h6
-rw-r--r--src/qml/jsruntime/qv4memberdata.cpp2
-rw-r--r--src/qml/jsruntime/qv4module.cpp24
-rw-r--r--src/qml/jsruntime/qv4object.cpp43
-rw-r--r--src/qml/jsruntime/qv4object_p.h2
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h2
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp126
-rw-r--r--src/qml/jsruntime/qv4persistent_p.h24
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp4
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h4
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp24
-rw-r--r--src/qml/jsruntime/qv4promiseobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4property_p.h2
-rw-r--r--src/qml/jsruntime/qv4propertykey.cpp7
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h18
-rw-r--r--src/qml/jsruntime/qv4qmetaobjectwrapper.cpp126
-rw-r--r--src/qml/jsruntime/qv4qmetaobjectwrapper_p.h66
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp54
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp991
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h111
-rw-r--r--src/qml/jsruntime/qv4referenceobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4regexp.cpp93
-rw-r--r--src/qml/jsruntime/qv4regexp_p.h2
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h38
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference.cpp26
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference_p.h12
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp133
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h2
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h198
-rw-r--r--src/qml/jsruntime/qv4script.cpp20
-rw-r--r--src/qml/jsruntime/qv4script_p.h8
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp204
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h26
-rw-r--r--src/qml/jsruntime/qv4setobject.cpp9
-rw-r--r--src/qml/jsruntime/qv4sparsearray_p.h44
-rw-r--r--src/qml/jsruntime/qv4stackframe.cpp11
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h41
-rw-r--r--src/qml/jsruntime/qv4string.cpp31
-rw-r--r--src/qml/jsruntime/qv4string_p.h30
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp5
-rw-r--r--src/qml/jsruntime/qv4symbol.cpp2
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp13
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h2
-rw-r--r--src/qml/jsruntime/qv4urlobject.cpp52
-rw-r--r--src/qml/jsruntime/qv4urlobject_p.h13
-rw-r--r--src/qml/jsruntime/qv4value_p.h15
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp3
-rw-r--r--src/qml/jsruntime/qv4variantobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp147
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h2
-rw-r--r--src/qml/memory/design.md125
-rw-r--r--src/qml/memory/qv4mm.cpp570
-rw-r--r--src/qml/memory/qv4mm_p.h194
-rw-r--r--src/qml/memory/qv4mmdefs_p.h19
-rw-r--r--src/qml/memory/qv4stacklimits.cpp20
-rw-r--r--src/qml/memory/qv4writebarrier.cpp36
-rw-r--r--src/qml/memory/qv4writebarrier_p.h142
-rw-r--r--src/qml/parser/qqmljs.g138
-rw-r--r--src/qml/parser/qqmljsast.cpp40
-rw-r--r--src/qml/parser/qqmljsast_p.h129
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h2
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h809
-rw-r--r--src/qml/parser/qqmljsengine_p.h4
-rw-r--r--src/qml/parser/qqmljslexer.cpp67
-rw-r--r--src/qml/parser/qqmljslexer_p.h16
-rw-r--r--src/qml/qml/ftw/qbipointer_p.h12
-rw-r--r--src/qml/qml/ftw/qhashedstring_p.h4
-rw-r--r--src/qml/qml/ftw/qintrusivelist_p.h285
-rw-r--r--src/qml/qml/ftw/qqmlnullablevalue_p.h71
-rw-r--r--src/qml/qml/ftw/qqmlrefcount_p.h68
-rw-r--r--src/qml/qml/ftw/qqmlthread.cpp32
-rw-r--r--src/qml/qml/ftw/qqmlthread_p.h1
-rw-r--r--src/qml/qml/ftw/qrecyclepool_p.h34
-rw-r--r--src/qml/qml/qqml.cpp1156
-rw-r--r--src/qml/qml/qqml.h71
-rw-r--r--src/qml/qml/qqmlabstractbinding_p.h2
-rw-r--r--src/qml/qml/qqmlanybinding_p.h7
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp43
-rw-r--r--src/qml/qml/qqmlapplicationengine_p.h2
-rw-r--r--src/qml/qml/qqmlbinding.cpp22
-rw-r--r--src/qml/qml/qqmlbinding_p.h2
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp8
-rw-r--r--src/qml/qml/qqmlboundsignal_p.h7
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions.cpp107
-rw-r--r--src/qml/qml/qqmlbuiltinfunctions_p.h16
-rw-r--r--src/qml/qml/qqmlcomponent.cpp257
-rw-r--r--src/qml/qml/qqmlcomponent.h11
-rw-r--r--src/qml/qml/qqmlcomponent_p.h21
-rw-r--r--src/qml/qml/qqmlcomponentandaliasresolver_p.h480
-rw-r--r--src/qml/qml/qqmlcomponentattached_p.h13
-rw-r--r--src/qml/qml/qqmlcontext.cpp129
-rw-r--r--src/qml/qml/qqmlcontext.h1
-rw-r--r--src/qml/qml/qqmlcontextdata_p.h30
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp21
-rw-r--r--src/qml/qml/qqmlcustomparser_p.h3
-rw-r--r--src/qml/qml/qqmldata_p.h90
-rw-r--r--src/qml/qml/qqmldatablob.cpp26
-rw-r--r--src/qml/qml/qqmldatablob_p.h5
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp4
-rw-r--r--src/qml/qml/qqmldelayedcallqueue_p.h6
-rw-r--r--src/qml/qml/qqmlengine.cpp351
-rw-r--r--src/qml/qml/qqmlengine.h2
-rw-r--r--src/qml/qml/qqmlengine_p.h73
-rw-r--r--src/qml/qml/qqmlexpression.cpp8
-rw-r--r--src/qml/qml/qqmlexpression.h1
-rw-r--r--src/qml/qml/qqmlextensionplugin.cpp20
-rw-r--r--src/qml/qml/qqmlfile.cpp185
-rw-r--r--src/qml/qml/qqmlfile.h6
-rw-r--r--src/qml/qml/qqmlfileselector_p.h6
-rw-r--r--src/qml/qml/qqmlfinalizer_p.h2
-rw-r--r--src/qml/qml/qqmlglobal.cpp772
-rw-r--r--src/qml/qml/qqmlglobal_p.h58
-rw-r--r--src/qml/qml/qqmlguard_p.h14
-rw-r--r--src/qml/qml/qqmlimport.cpp310
-rw-r--r--src/qml/qml/qqmlimport_p.h51
-rw-r--r--src/qml/qml/qqmlincubator.cpp58
-rw-r--r--src/qml/qml/qqmlincubator_p.h4
-rw-r--r--src/qml/qml/qqmlirloader.cpp13
-rw-r--r--src/qml/qml/qqmlirloader_p.h2
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp46
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h6
-rw-r--r--src/qml/qml/qqmllist.cpp2
-rw-r--r--src/qml/qml/qqmllist.h39
-rw-r--r--src/qml/qml/qqmllist_p.h2
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp217
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h31
-rw-r--r--src/qml/qml/qqmllocale.cpp602
-rw-r--r--src/qml/qml/qqmllocale_p.h221
-rw-r--r--src/qml/qml/qqmlloggingcategory.cpp28
-rw-r--r--src/qml/qml/qqmlmetamoduleregistration.cpp2
-rw-r--r--src/qml/qml/qqmlmetaobject.cpp62
-rw-r--r--src/qml/qml/qqmlmetaobject_p.h107
-rw-r--r--src/qml/qml/qqmlmetatype.cpp621
-rw-r--r--src/qml/qml/qqmlmetatype_p.h179
-rw-r--r--src/qml/qml/qqmlmetatypedata.cpp17
-rw-r--r--src/qml/qml/qqmlmetatypedata_p.h10
-rw-r--r--src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp15
-rw-r--r--src/qml/qml/qqmlnotifier_p.h2
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp225
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h41
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp2
-rw-r--r--src/qml/qml/qqmlopenmetaobject_p.h7
-rw-r--r--src/qml/qml/qqmlplatform_p.h4
-rw-r--r--src/qml/qml/qqmlpluginimporter.cpp13
-rw-r--r--src/qml/qml/qqmlprivate.h214
-rw-r--r--src/qml/qml/qqmlproperty.cpp198
-rw-r--r--src/qml/qml/qqmlproperty_p.h4
-rw-r--r--src/qml/qml/qqmlpropertybinding.cpp15
-rw-r--r--src/qml/qml/qqmlpropertybinding_p.h42
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp110
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h15
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp110
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h529
-rw-r--r--src/qml/qml/qqmlpropertycachevector_p.h36
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h99
-rw-r--r--src/qml/qml/qqmlpropertyresolver.cpp7
-rw-r--r--src/qml/qml/qqmlpropertytopropertybinding_p.h2
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp82
-rw-r--r--src/qml/qml/qqmlpropertyvalidator_p.h8
-rw-r--r--src/qml/qml/qqmlpropertyvalueinterceptor_p.h2
-rw-r--r--src/qml/qml/qqmlproxymetaobject.cpp12
-rw-r--r--src/qml/qml/qqmlproxymetaobject_p.h7
-rw-r--r--src/qml/qml/qqmlregistration.h13
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp62
-rw-r--r--src/qml/qml/qqmlscriptblob_p.h3
-rw-r--r--src/qml/qml/qqmlscriptdata.cpp11
-rw-r--r--src/qml/qml/qqmlscriptdata_p.h15
-rw-r--r--src/qml/qml/qqmlscriptstring.cpp20
-rw-r--r--src/qml/qml/qqmlscriptstring.h1
-rw-r--r--src/qml/qml/qqmlstringconverters.cpp68
-rw-r--r--src/qml/qml/qqmlstringconverters_p.h91
-rw-r--r--src/qml/qml/qqmltype.cpp545
-rw-r--r--src/qml/qml/qqmltype_p.h53
-rw-r--r--src/qml/qml/qqmltype_p_p.h234
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp495
-rw-r--r--src/qml/qml/qqmltypecompiler_p.h75
-rw-r--r--src/qml/qml/qqmltypedata.cpp462
-rw-r--r--src/qml/qml/qqmltypedata_p.h35
-rw-r--r--src/qml/qml/qqmltypeloader.cpp154
-rw-r--r--src/qml/qml/qqmltypeloader_p.h25
-rw-r--r--src/qml/qml/qqmltypeloaderqmldircontent.cpp4
-rw-r--r--src/qml/qml/qqmltypeloaderqmldircontent_p.h9
-rw-r--r--src/qml/qml/qqmltypeloaderthread.cpp10
-rw-r--r--src/qml/qml/qqmltypemodule_p.h2
-rw-r--r--src/qml/qml/qqmltypenamecache_p.h32
-rw-r--r--src/qml/qml/qqmltypenotavailable.cpp15
-rw-r--r--src/qml/qml/qqmltypenotavailable_p.h34
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp172
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h5
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp19
-rw-r--r--src/qml/qml/qqmlvaluetype_p.h53
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp76
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h47
-rw-r--r--src/qml/qml/qqmlvme_p.h2
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp329
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h90
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp126
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp149
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h3
-rw-r--r--src/qml/qmldirparser/qqmlimportresolver_p.h2
-rw-r--r--src/qml/qmltc/qqmltcobjectcreationhelper.cpp38
-rw-r--r--src/qml/qmltc/qqmltcobjectcreationhelper_p.h2
-rw-r--r--src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h2
-rw-r--r--src/qml/qmltc/supportlibrary/qqmlcpponassignment_p.h2
-rw-r--r--src/qml/qqmlbuiltins_p.h407
-rw-r--r--src/qml/qtqml.tracepoints21
-rw-r--r--src/qml/qtqmlcompilerglobal_p.h2
-rw-r--r--src/qml/qtqmlglobal_p.h2
-rw-r--r--src/qml/types/qqmlbind.cpp162
-rw-r--r--src/qml/types/qqmlbind_p.h4
-rw-r--r--src/qml/types/qqmlconnections.cpp208
-rw-r--r--src/qml/types/qqmlconnections_p.h14
-rw-r--r--src/qml/types/qqmltimer_p.h4
-rw-r--r--src/qmlcompiler/CMakeLists.txt36
-rw-r--r--src/qmlcompiler/doc/qtqmlcompiler-index.qdoc81
-rw-r--r--src/qmlcompiler/doc/qtqmlcompiler.qdocconf49
-rw-r--r--src/qmlcompiler/doc/src/qtqmlcompiler-module.qdoc13
-rw-r--r--src/qmlcompiler/doc/src/tutorial.qdoc229
-rw-r--r--src/qmlcompiler/qcoloroutput.cpp50
-rw-r--r--src/qmlcompiler/qcoloroutput_p.h4
-rw-r--r--src/qmlcompiler/qdeferredpointer_p.h38
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp610
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks_p.h59
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp2561
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h148
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h150
-rw-r--r--src/qmlcompiler/qqmljscompiler.cpp107
-rw-r--r--src/qmlcompiler/qqmljscompiler_p.h32
-rw-r--r--src/qmlcompiler/qqmljscontextualtypes_p.h113
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer.cpp98
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer_p.h2
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp174
-rw-r--r--src/qmlcompiler/qqmljsimporter_p.h94
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp655
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h37
-rw-r--r--src/qmlcompiler/qqmljslinter.cpp157
-rw-r--r--src/qmlcompiler/qqmljslinter_p.h17
-rw-r--r--src/qmlcompiler/qqmljslintercodegen.cpp22
-rw-r--r--src/qmlcompiler/qqmljslintercodegen_p.h4
-rw-r--r--src/qmlcompiler/qqmljsliteralbindingcheck.cpp143
-rw-r--r--src/qmlcompiler/qqmljsliteralbindingcheck_p.h35
-rw-r--r--src/qmlcompiler/qqmljsloadergenerator.cpp2
-rw-r--r--src/qmlcompiler/qqmljsloadergenerator_p.h6
-rw-r--r--src/qmlcompiler/qqmljslogger.cpp419
-rw-r--r--src/qmlcompiler/qqmljslogger_p.h182
-rw-r--r--src/qmlcompiler/qqmljsloggingutils.cpp136
-rw-r--r--src/qmlcompiler/qqmljsloggingutils.h82
-rw-r--r--src/qmlcompiler/qqmljsloggingutils_p.h108
-rw-r--r--src/qmlcompiler/qqmljsmetatypes.cpp46
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h191
-rw-r--r--src/qmlcompiler/qqmljsoptimizations.cpp531
-rw-r--r--src/qmlcompiler/qqmljsoptimizations_p.h65
-rw-r--r--src/qmlcompiler/qqmljsregistercontent.cpp49
-rw-r--r--src/qmlcompiler/qqmljsregistercontent_p.h94
-rw-r--r--src/qmlcompiler/qqmljsresourcefilemapper_p.h4
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp492
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h711
-rw-r--r--src/qmlcompiler/qqmljsscopesbyid_p.h30
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck.cpp167
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck_p.h33
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer.cpp19
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer_p.h10
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp73
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader_p.h6
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp1262
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h36
-rw-r--r--src/qmlcompiler/qqmljstypereader.cpp16
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp868
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h131
-rw-r--r--src/qmlcompiler/qqmljsutils.cpp63
-rw-r--r--src/qmlcompiler/qqmljsutils_p.h73
-rw-r--r--src/qmlcompiler/qqmljsvaluetypefromstringcheck.cpp63
-rw-r--r--src/qmlcompiler/qqmljsvaluetypefromstringcheck_p.h48
-rw-r--r--src/qmlcompiler/qqmlsa.cpp1617
-rw-r--r--src/qmlcompiler/qqmlsa.h435
-rw-r--r--src/qmlcompiler/qqmlsa_p.h251
-rw-r--r--src/qmlcompiler/qqmlsaconstants.h50
-rw-r--r--src/qmlcompiler/qqmlsasourcelocation.cpp125
-rw-r--r--src/qmlcompiler/qqmlsasourcelocation.h83
-rw-r--r--src/qmlcompiler/qqmlsasourcelocation_p.h53
-rw-r--r--src/qmlcompiler/qresourcerelocater.cpp1
-rw-r--r--src/qmlcompiler/qresourcerelocater_p.h4
-rw-r--r--src/qmldebug/CMakeLists.txt1
-rw-r--r--src/qmldebug/qqmldebugclient_p_p.h2
-rw-r--r--src/qmldebug/qqmlenginedebugclient.cpp2
-rw-r--r--src/qmldebug/qqmlenginedebugclient_p.h2
-rw-r--r--src/qmldebug/qv4debugclient.cpp2
-rw-r--r--src/qmldom/CMakeLists.txt4
-rw-r--r--src/qmldom/qqmldom_fwd_p.h20
-rw-r--r--src/qmldom/qqmldom_utils.cpp63
-rw-r--r--src/qmldom/qqmldom_utils_p.h53
-rw-r--r--src/qmldom/qqmldomastcreator.cpp3688
-rw-r--r--src/qmldom/qqmldomastcreator_p.h683
-rw-r--r--src/qmldom/qqmldomastdumper.cpp23
-rw-r--r--src/qmldom/qqmldomastdumper_p.h2
-rw-r--r--src/qmldom/qqmldomattachedinfo.cpp210
-rw-r--r--src/qmldom/qqmldomattachedinfo_p.h175
-rw-r--r--src/qmldom/qqmldomcodeformatter.cpp3
-rw-r--r--src/qmldom/qqmldomcomments.cpp583
-rw-r--r--src/qmldom/qqmldomcomments_p.h127
-rw-r--r--src/qmldom/qqmldomcompare.cpp19
-rw-r--r--src/qmldom/qqmldomcompare_p.h18
-rw-r--r--src/qmldom/qqmldomconstants_p.h192
-rw-r--r--src/qmldom/qqmldomelements.cpp743
-rw-r--r--src/qmldom/qqmldomelements_p.h465
-rw-r--r--src/qmldom/qqmldomerrormessage.cpp94
-rw-r--r--src/qmldom/qqmldomerrormessage_p.h62
-rw-r--r--src/qmldom/qqmldomexternalitems.cpp280
-rw-r--r--src/qmldom/qqmldomexternalitems_p.h334
-rw-r--r--src/qmldom/qqmldomfieldfilter.cpp13
-rw-r--r--src/qmldom/qqmldomfieldfilter_p.h15
-rw-r--r--src/qmldom/qqmldomfilewriter.cpp2
-rw-r--r--src/qmldom/qqmldomfilewriter_p.h7
-rw-r--r--src/qmldom/qqmldomfunctionref_p.h30
-rw-r--r--src/qmldom/qqmldomindentinglinewriter.cpp2
-rw-r--r--src/qmldom/qqmldomindentinglinewriter_p.h4
-rw-r--r--src/qmldom/qqmldomitem.cpp1903
-rw-r--r--src/qmldom/qqmldomitem_p.h1288
-rw-r--r--src/qmldom/qqmldomlinewriter.cpp9
-rw-r--r--src/qmldom/qqmldomlinewriter_p.h19
-rw-r--r--src/qmldom/qqmldommock.cpp14
-rw-r--r--src/qmldom/qqmldommock_p.h16
-rw-r--r--src/qmldom/qqmldommoduleindex.cpp80
-rw-r--r--src/qmldom/qqmldommoduleindex_p.h33
-rw-r--r--src/qmldom/qqmldomoutwriter.cpp363
-rw-r--r--src/qmldom/qqmldomoutwriter_p.h42
-rw-r--r--src/qmldom/qqmldompath.cpp85
-rw-r--r--src/qmldom/qqmldompath_p.h402
-rw-r--r--src/qmldom/qqmldomreformatter.cpp1980
-rw-r--r--src/qmldom/qqmldomreformatter_p.h189
-rw-r--r--src/qmldom/qqmldomscanner.cpp2
-rw-r--r--src/qmldom/qqmldomscanner_p.h4
-rw-r--r--src/qmldom/qqmldomscriptelements.cpp355
-rw-r--r--src/qmldom/qqmldomscriptelements_p.h405
-rw-r--r--src/qmldom/qqmldomstringdumper.cpp16
-rw-r--r--src/qmldom/qqmldomstringdumper_p.h28
-rw-r--r--src/qmldom/qqmldomtop.cpp1595
-rw-r--r--src/qmldom/qqmldomtop_p.h721
-rw-r--r--src/qmldom/qqmldomtypesreader.cpp53
-rw-r--r--src/qmldom/qqmldomtypesreader_p.h23
-rw-r--r--src/qmldom/standalone/CMakeLists.txt80
-rw-r--r--src/qmldom/standalone/private/qtqmlcompilerexports_p.h20
-rw-r--r--src/qmlintegration/CMakeLists.txt1
-rw-r--r--src/qmlintegration/qqmlintegration.h53
-rw-r--r--src/qmllocalstorage/CMakeLists.txt3
-rw-r--r--src/qmllocalstorage/qqmllocalstorage.cpp6
-rw-r--r--src/qmllocalstorage/qqmllocalstorage_p.h4
-rw-r--r--src/qmllocalstorage/qqmllocalstorageglobal_p.h2
-rw-r--r--src/qmlls/CMakeLists.txt45
-rw-r--r--src/qmlls/qdochtmlparser.cpp214
-rw-r--r--src/qmlls/qdochtmlparser_p.h51
-rw-r--r--src/qmlls/qlanguageserver.cpp378
-rw-r--r--src/qmlls/qlanguageserver_p.h92
-rw-r--r--src/qmlls/qlanguageserver_p_p.h53
-rw-r--r--src/qmlls/qlspcustomtypes_p.h56
-rw-r--r--src/qmlls/qqmlbasemodule_p.h267
-rw-r--r--src/qmlls/qqmlcodemodel.cpp906
-rw-r--r--src/qmlls/qqmlcodemodel_p.h173
-rw-r--r--src/qmlls/qqmlcompletioncontextstrings.cpp50
-rw-r--r--src/qmlls/qqmlcompletioncontextstrings_p.h62
-rw-r--r--src/qmlls/qqmlcompletionsupport.cpp195
-rw-r--r--src/qmlls/qqmlcompletionsupport_p.h60
-rw-r--r--src/qmlls/qqmlfindusagessupport.cpp79
-rw-r--r--src/qmlls/qqmlfindusagessupport_p.h47
-rw-r--r--src/qmlls/qqmlformatting.cpp95
-rw-r--r--src/qmlls/qqmlformatting_p.h44
-rw-r--r--src/qmlls/qqmlgotodefinitionsupport.cpp70
-rw-r--r--src/qmlls/qqmlgotodefinitionsupport_p.h49
-rw-r--r--src/qmlls/qqmlgototypedefinitionsupport.cpp74
-rw-r--r--src/qmlls/qqmlgototypedefinitionsupport_p.h50
-rw-r--r--src/qmlls/qqmlhighlightsupport.cpp212
-rw-r--r--src/qmlls/qqmlhighlightsupport_p.h96
-rw-r--r--src/qmlls/qqmlhover.cpp81
-rw-r--r--src/qmlls/qqmlhover_p.h44
-rw-r--r--src/qmlls/qqmllanguageserver.cpp185
-rw-r--r--src/qmlls/qqmllanguageserver_p.h83
-rw-r--r--src/qmlls/qqmllintsuggestions.cpp378
-rw-r--r--src/qmlls/qqmllintsuggestions_p.h72
-rw-r--r--src/qmlls/qqmllscompletion.cpp1888
-rw-r--r--src/qmlls/qqmllscompletion_p.h226
-rw-r--r--src/qmlls/qqmllscompletionplugin.cpp4
-rw-r--r--src/qmlls/qqmllscompletionplugin_p.h42
-rw-r--r--src/qmlls/qqmllsplugin_p.h42
-rw-r--r--src/qmlls/qqmllsutils.cpp2183
-rw-r--r--src/qmlls/qqmllsutils_p.h178
-rw-r--r--src/qmlls/qqmlrangeformatting.cpp144
-rw-r--r--src/qmlls/qqmlrangeformatting_p.h44
-rw-r--r--src/qmlls/qqmlrenamesymbolsupport.cpp108
-rw-r--r--src/qmlls/qqmlrenamesymbolsupport_p.h44
-rw-r--r--src/qmlls/qqmlsemantictokens.cpp773
-rw-r--r--src/qmlls/qqmlsemantictokens_p.h132
-rw-r--r--src/qmlls/qtextblock.cpp101
-rw-r--r--src/qmlls/qtextblock_p.h73
-rw-r--r--src/qmlls/qtextcursor.cpp122
-rw-r--r--src/qmlls/qtextcursor_p.h72
-rw-r--r--src/qmlls/qtextdocument.cpp124
-rw-r--r--src/qmlls/qtextdocument_p.h78
-rw-r--r--src/qmlls/qtextsynchronization.cpp99
-rw-r--r--src/qmlls/qtextsynchronization_p.h43
-rw-r--r--src/qmlls/qworkspace.cpp172
-rw-r--r--src/qmlls/qworkspace_p.h43
-rw-r--r--src/qmlmodels/CMakeLists.txt7
-rw-r--r--src/qmlmodels/doc/qtqmlmodels.qdocconf2
-rw-r--r--src/qmlmodels/doc/snippets/delegatemodel/delegatemodel.qml4
-rw-r--r--src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml4
-rw-r--r--src/qmlmodels/doc/snippets/delegatemodel/delegatemodelgroup.qml4
-rw-r--r--src/qmlmodels/doc/snippets/package/Delegate.qml2
-rw-r--r--src/qmlmodels/doc/snippets/package/view.qml4
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/WorkerScript.qml43
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/dataloader.mjs13
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listelements.qml2
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listmodel-modify.qml2
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listmodel-nested.qml2
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listmodel-simple.qml2
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listmodel.qml2
-rw-r--r--src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml6
-rw-r--r--src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml6
-rw-r--r--src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml6
-rw-r--r--src/qmlmodels/doc/src/qtqmlmodel.qdoc2
-rw-r--r--src/qmlmodels/qqmlabstractdelegatecomponent_p.h3
-rw-r--r--src/qmlmodels/qqmladaptormodel.cpp963
-rw-r--r--src/qmlmodels/qqmladaptormodel_p.h8
-rw-r--r--src/qmlmodels/qqmladaptormodelenginedata.cpp24
-rw-r--r--src/qmlmodels/qqmladaptormodelenginedata_p.h68
-rw-r--r--src/qmlmodels/qqmlchangeset.cpp4
-rw-r--r--src/qmlmodels/qqmlchangeset_p.h9
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp267
-rw-r--r--src/qmlmodels/qqmldelegatemodel_p.h41
-rw-r--r--src/qmlmodels/qqmldelegatemodel_p_p.h19
-rw-r--r--src/qmlmodels/qqmldmabstractitemmodeldata.cpp253
-rw-r--r--src/qmlmodels/qqmldmabstractitemmodeldata_p.h334
-rw-r--r--src/qmlmodels/qqmldmlistaccessordata.cpp157
-rw-r--r--src/qmlmodels/qqmldmlistaccessordata_p.h293
-rw-r--r--src/qmlmodels/qqmldmobjectdata.cpp18
-rw-r--r--src/qmlmodels/qqmldmobjectdata_p.h247
-rw-r--r--src/qmlmodels/qqmlinstantiator.cpp38
-rw-r--r--src/qmlmodels/qqmlinstantiator_p.h2
-rw-r--r--src/qmlmodels/qqmlinstantiator_p_p.h5
-rw-r--r--src/qmlmodels/qqmlitemmodels.qdoc7
-rw-r--r--src/qmlmodels/qqmllistaccessor.cpp146
-rw-r--r--src/qmlmodels/qqmllistaccessor_p.h1
-rw-r--r--src/qmlmodels/qqmllistmodel.cpp62
-rw-r--r--src/qmlmodels/qqmllistmodel_p.h11
-rw-r--r--src/qmlmodels/qqmllistmodel_p_p.h17
-rw-r--r--src/qmlmodels/qqmllistmodelworkeragent.cpp6
-rw-r--r--src/qmlmodels/qqmllistmodelworkeragent_p.h12
-rw-r--r--src/qmlmodels/qqmlmodelindexvaluetype_p.h12
-rw-r--r--src/qmlmodels/qqmlobjectmodel_p.h9
-rw-r--r--src/qmlmodels/qqmltableinstancemodel.cpp23
-rw-r--r--src/qmlmodels/qqmltableinstancemodel_p.h5
-rw-r--r--src/qmlmodels/qqmltreemodeltotablemodel.cpp115
-rw-r--r--src/qmlmodels/qqmltreemodeltotablemodel_p_p.h12
-rw-r--r--src/qmlmodels/qquickpackage.cpp4
-rw-r--r--src/qmlmodels/qquickpackage_p.h6
-rw-r--r--src/qmlmodels/qtqmlmodelsglobal_p.h2
-rw-r--r--src/qmlnetwork/CMakeLists.txt29
-rw-r--r--src/qmlnetwork/doc/qtqmlnetwork.qdocconf37
-rw-r--r--src/qmlnetwork/doc/src/qtqmlnetwork-qmltypes.qdoc32
-rw-r--r--src/qmlnetwork/doc/src/qtqmlnetwork.qdoc23
-rw-r--r--src/qmlnetwork/qqmlnetworkinformation.cpp24
-rw-r--r--src/qmlnetwork/qqmlnetworkinformation.qdoc70
-rw-r--r--src/qmlnetwork/qqmlnetworkinformation_p.h41
-rw-r--r--src/qmlnetwork/ssl/qqmlssl.qdoc200
-rw-r--r--src/qmlnetwork/ssl/qqmlsslconfiguration.cpp175
-rw-r--r--src/qmlnetwork/ssl/qqmlsslconfiguration_p.h109
-rw-r--r--src/qmlnetwork/ssl/qqmlsslkey.cpp68
-rw-r--r--src/qmlnetwork/ssl/qqmlsslkey_p.h77
-rw-r--r--src/qmlnetwork/ssl/qqmlsslnamespace_p.h36
-rw-r--r--src/qmlnetwork/ssl/qqmlsslsocketnamespace_p.h36
-rw-r--r--src/qmltest/CMakeLists.txt1
-rw-r--r--src/qmltest/SignalSpy.qml45
-rw-r--r--src/qmltest/TestCase.qml339
-rw-r--r--src/qmltest/doc/qtqmltest.qdocconf2
-rw-r--r--src/qmltest/doc/snippets/modules_MyModule_CMakeLists.txt42
-rw-r--r--src/qmltest/doc/snippets/overview.cmake2
-rw-r--r--src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp12
-rw-r--r--src/qmltest/doc/snippets/testApp/CMakeLists.txt46
-rw-r--r--src/qmltest/doc/snippets/testApp/MyModule/CMakeLists.txt32
-rw-r--r--src/qmltest/doc/snippets/testApp/MyModule/MyButton.qml12
-rw-r--r--src/qmltest/doc/snippets/testApp/tests/CMakeLists.txt29
-rw-r--r--src/qmltest/doc/snippets/testApp/tests/main.cpp9
-rw-r--r--src/qmltest/doc/snippets/testApp/tests/setup.cpp21
-rw-r--r--src/qmltest/doc/snippets/testApp/tests/setup.h24
-rw-r--r--src/qmltest/doc/snippets/testApp/tests/tst_testqml.qml31
-rw-r--r--src/qmltest/doc/src/qtquicktest-index.qdoc76
-rw-r--r--src/qmltest/doc/src/qtquicktest.qdoc6
-rw-r--r--src/qmltest/qtestoptions_p.h5
-rw-r--r--src/qmltest/quicktest.cpp135
-rw-r--r--src/qmltest/quicktest.h14
-rw-r--r--src/qmltest/quicktest_p.h6
-rw-r--r--src/qmltest/quicktestevent.cpp4
-rw-r--r--src/qmltest/quicktestevent_p.h6
-rw-r--r--src/qmltest/quicktestglobal.h15
-rw-r--r--src/qmltest/quicktestglobal_p.h2
-rw-r--r--src/qmltest/quicktestresult.cpp20
-rw-r--r--src/qmltest/quicktestresult_p.h4
-rw-r--r--src/qmltest/quicktestutil.cpp2
-rw-r--r--src/qmltest/quicktestutil_p.h10
-rw-r--r--src/qmltoolingsettings/CMakeLists.txt16
-rw-r--r--src/qmltoolingsettings/qqmltoolingsettings.cpp140
-rw-r--r--src/qmltoolingsettings/qqmltoolingsettings_p.h48
-rw-r--r--src/qmltoolingsettings/qqmltoolingutils.cpp57
-rw-r--r--src/qmltoolingsettings/qqmltoolingutils_p.h37
-rw-r--r--src/qmltyperegistrar/CMakeLists.txt17
-rw-r--r--src/qmltyperegistrar/jsroot_metatypes.json11097
-rw-r--r--src/qmltyperegistrar/qanystringviewutils_p.h187
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor.cpp888
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor_p.h241
-rw-r--r--src/qmltyperegistrar/qqmljsstreamwriter.cpp135
-rw-r--r--src/qmltyperegistrar/qqmljsstreamwriter_p.h29
-rw-r--r--src/qmltyperegistrar/qqmltyperegistrar.cpp337
-rw-r--r--src/qmltyperegistrar/qqmltyperegistrar_p.h24
-rw-r--r--src/qmltyperegistrar/qqmltyperegistrarconstants_p.h174
-rw-r--r--src/qmltyperegistrar/qqmltyperegistrarutils.cpp59
-rw-r--r--src/qmltyperegistrar/qqmltyperegistrarutils_p.h34
-rw-r--r--src/qmltyperegistrar/qqmltypesclassdescription.cpp575
-rw-r--r--src/qmltyperegistrar/qqmltypesclassdescription_p.h102
-rw-r--r--src/qmltyperegistrar/qqmltypescreator.cpp587
-rw-r--r--src/qmltyperegistrar/qqmltypescreator_p.h31
-rw-r--r--src/qmlworkerscript/CMakeLists.txt8
-rw-r--r--src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf2
-rw-r--r--src/qmlworkerscript/doc/snippets/qml/workerscript/workerscript.qml2
-rw-r--r--src/qmlworkerscript/qquickworkerscript.cpp15
-rw-r--r--src/qmlworkerscript/qquickworkerscript_p.h7
-rw-r--r--src/qmlworkerscript/qtqmlworkerscriptglobal_p.h2
-rw-r--r--src/qmlworkerscript/qv4serialize.cpp7
-rw-r--r--src/qmlxmllistmodel/CMakeLists.txt3
-rw-r--r--src/qmlxmllistmodel/doc/qtqmlxmllistmodel.qdocconf2
-rw-r--r--src/qmlxmllistmodel/qqmlxmllistmodel.cpp32
-rw-r--r--src/qmlxmllistmodel/qqmlxmllistmodel_p.h7
-rw-r--r--src/qmlxmllistmodel/qtqmlxmllistmodelglobal_p.h2
-rw-r--r--src/quick/CMakeLists.txt190
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp4
-rw-r--r--src/quick/accessible/qaccessiblequickitem_p.h2
-rw-r--r--src/quick/accessible/qaccessiblequickview.cpp10
-rw-r--r--src/quick/configure.cmake7
-rw-r--r--src/quick/designer/qqmldesignermetaobject_p.h2
-rw-r--r--src/quick/designer/qquickdesignersupport.cpp2
-rw-r--r--src/quick/doc/images/declarative-scalegrid.pngbin4228 -> 7712 bytes
-rw-r--r--src/quick/doc/images/how-to-time-picker-dark.pngbin0 -> 26831 bytes
-rw-r--r--src/quick/doc/images/how-to-time-picker-light.pngbin0 -> 25659 bytes
-rw-r--r--src/quick/doc/images/pinchAndDragHandlers-drag-one-rect.pngbin0 -> 6222 bytes
-rw-r--r--src/quick/doc/images/pinchAndDragHandlers-drag-two-rects.pngbin0 -> 7828 bytes
-rw-r--r--src/quick/doc/images/pinchAndDragHandlers-pinch.pngbin0 -> 8122 bytes
-rw-r--r--src/quick/doc/images/pointerHandlers/dragReleaseMenu.webpbin0 -> 86252 bytes
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webpbin0 -> 53154 bytes
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webpbin0 -> 27426 bytes
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webpbin0 -> 51008 bytes
-rw-r--r--src/quick/doc/images/qml-borderimage-rounded.pngbin0 -> 5728 bytes
-rw-r--r--src/quick/doc/images/qml-borderimage-scaled.pngbin1552 -> 5661 bytes
-rw-r--r--src/quick/doc/images/qml-borderimage-tiled.pngbin1602 -> 4936 bytes
-rw-r--r--src/quick/doc/images/qml-item-canvas-startAngle.pngbin5099 -> 5826 bytes
-rw-r--r--src/quick/doc/images/simpleProxy.pngbin0 -> 4601 bytes
-rw-r--r--src/quick/doc/qtquick.qdocconf37
-rw-r--r--src/quick/doc/snippets/code/doc_src_qtquick.cmake2
-rw-r--r--src/quick/doc/snippets/imgprovider/imageprovider-example.qml2
-rw-r--r--src/quick/doc/snippets/layouts/responsiveDeclarative.qml33
-rw-r--r--src/quick/doc/snippets/layouts/responsiveStates.qml44
-rw-r--r--src/quick/doc/snippets/layouts/simpleProxy.qml62
-rw-r--r--src/quick/doc/snippets/pointerHandlers/dragHandler.qml2
-rw-r--r--src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml2
-rw-r--r--src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml2
-rw-r--r--src/quick/doc/snippets/pointerHandlers/dragReleaseMenu.qml72
-rw-r--r--src/quick/doc/snippets/pointerHandlers/handlerFlick.qml4
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml2
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml2
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pinchAndDragHandlers.qml54
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pinchHandler.qml2
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml2
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandler.qml5
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml21
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml36
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml54
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml36
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml51
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml51
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerGrabChanged.qml46
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml51
-rw-r--r--src/quick/doc/snippets/pointerHandlers/wheelHandler.qml2
-rw-r--r--src/quick/doc/snippets/qml/anchoranimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/anchorchanges.qml2
-rw-r--r--src/quick/doc/snippets/qml/animatedimage.qml2
-rw-r--r--src/quick/doc/snippets/qml/animation.qml2
-rw-r--r--src/quick/doc/snippets/qml/animators.qml2
-rw-r--r--src/quick/doc/snippets/qml/behavior.qml2
-rw-r--r--src/quick/doc/snippets/qml/borderimage/borderimage-rounded.qml124
-rw-r--r--src/quick/doc/snippets/qml/borderimage/borderimage-scaled.qml92
-rw-r--r--src/quick/doc/snippets/qml/borderimage/borderimage-tiled.qml92
-rw-r--r--src/quick/doc/snippets/qml/borderimage/normal-image.qml48
-rw-r--r--src/quick/doc/snippets/qml/borderimage/pics/borderframe.pngbin0 -> 3411 bytes
-rw-r--r--src/quick/doc/snippets/qml/borderimage/pics/borderframe.svg91
-rw-r--r--src/quick/doc/snippets/qml/boundaryRule.qml4
-rw-r--r--src/quick/doc/snippets/qml/coloranimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/colors.qml2
-rw-r--r--src/quick/doc/snippets/qml/column/column-transitions.qml2
-rw-r--r--src/quick/doc/snippets/qml/column/column.qml2
-rw-r--r--src/quick/doc/snippets/qml/column/vertical-positioner.qml2
-rw-r--r--src/quick/doc/snippets/qml/drag.qml2
-rw-r--r--src/quick/doc/snippets/qml/example.md146
-rw-r--r--src/quick/doc/snippets/qml/externalDragScaledImage.qml28
-rw-r--r--src/quick/doc/snippets/qml/externaldrag.qml8
-rw-r--r--src/quick/doc/snippets/qml/flickable.qml2
-rw-r--r--src/quick/doc/snippets/qml/flickableScrollbar.qml2
-rw-r--r--src/quick/doc/snippets/qml/flipable/flipable.qml2
-rw-r--r--src/quick/doc/snippets/qml/flow.qml2
-rw-r--r--src/quick/doc/snippets/qml/focus/MyClickableWidget.qml2
-rw-r--r--src/quick/doc/snippets/qml/focus/MyWidget.qml2
-rw-r--r--src/quick/doc/snippets/qml/focus/advancedFocus.qml2
-rw-r--r--src/quick/doc/snippets/qml/focus/basicwidget.qml2
-rw-r--r--src/quick/doc/snippets/qml/focus/clickablewidget.qml2
-rw-r--r--src/quick/doc/snippets/qml/focus/myfocusscopewidget.qml2
-rw-r--r--src/quick/doc/snippets/qml/focus/rectangle.qml2
-rw-r--r--src/quick/doc/snippets/qml/focus/widget.qml2
-rw-r--r--src/quick/doc/snippets/qml/gradient.qml2
-rw-r--r--src/quick/doc/snippets/qml/grid-spacing.qml2
-rw-r--r--src/quick/doc/snippets/qml/grid/grid.qml2
-rw-r--r--src/quick/doc/snippets/qml/gridview/ContactModel.qml2
-rw-r--r--src/quick/doc/snippets/qml/gridview/gridview.qml2
-rw-r--r--src/quick/doc/snippets/qml/image.qml2
-rw-r--r--src/quick/doc/snippets/qml/images/qt-logo.pngbin0 -> 6208 bytes
-rw-r--r--src/quick/doc/snippets/qml/images/qt_logo.svg26
-rw-r--r--src/quick/doc/snippets/qml/images/red.pngbin0 -> 130 bytes
-rw-r--r--src/quick/doc/snippets/qml/item/childrenRect.qml2
-rw-r--r--src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml4
-rw-r--r--src/quick/doc/snippets/qml/item/containmentMask-shape.qml4
-rw-r--r--src/quick/doc/snippets/qml/item/itemGrab.qml7
-rw-r--r--src/quick/doc/snippets/qml/keynavigation.qml2
-rw-r--r--src/quick/doc/snippets/qml/keys/keys-handler.qml2
-rw-r--r--src/quick/doc/snippets/qml/keys/keys-pressed.qml2
-rw-r--r--src/quick/doc/snippets/qml/layerblending.qml2
-rw-r--r--src/quick/doc/snippets/qml/layout-simple.qml6
-rw-r--r--src/quick/doc/snippets/qml/layoutmirroring.qml2
-rw-r--r--src/quick/doc/snippets/qml/listview-decorations.qml5
-rw-r--r--src/quick/doc/snippets/qml/listview-sections.qml2
-rw-r--r--src/quick/doc/snippets/qml/listview.qml2
-rw-r--r--src/quick/doc/snippets/qml/listview/ContactModel.qml2
-rw-r--r--src/quick/doc/snippets/qml/listview/ReusableDelegate.qml2
-rw-r--r--src/quick/doc/snippets/qml/listview/listview.qml2
-rw-r--r--src/quick/doc/snippets/qml/loader/KeyReader.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/MyComponent.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/MyItem.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/connections.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/creationContext1.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/creationContext2.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/creationContext3.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/creationContext4.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/focus.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/simple.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/sizeitem.qml4
-rw-r--r--src/quick/doc/snippets/qml/loader/sizeloader.qml4
-rw-r--r--src/quick/doc/snippets/qml/localstorage/hello.qml2
-rw-r--r--src/quick/doc/snippets/qml/models/views-models-delegates.qml2
-rw-r--r--src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml2
-rw-r--r--src/quick/doc/snippets/qml/mousearea/mousearea.qml2
-rw-r--r--src/quick/doc/snippets/qml/mousearea/mouseareadragfilter.qml2
-rw-r--r--src/quick/doc/snippets/qml/multipointtoucharea/multipointtoucharea.qml2
-rw-r--r--src/quick/doc/snippets/qml/nestedWindowTransientParent.qml23
-rw-r--r--src/quick/doc/snippets/qml/numberanimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/parallelanimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/parentanimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/parentchange.qml2
-rw-r--r--src/quick/doc/snippets/qml/path/arcdirection.qml2
-rw-r--r--src/quick/doc/snippets/qml/path/arcradius.qml2
-rw-r--r--src/quick/doc/snippets/qml/path/arcrotation.qml2
-rw-r--r--src/quick/doc/snippets/qml/path/basicarc.qml2
-rw-r--r--src/quick/doc/snippets/qml/path/basiccurve.qml2
-rw-r--r--src/quick/doc/snippets/qml/path/largearc.qml2
-rw-r--r--src/quick/doc/snippets/qml/pathinterpolator.qml2
-rw-r--r--src/quick/doc/snippets/qml/pathview/ContactModel.qml2
-rw-r--r--src/quick/doc/snippets/qml/pathview/pathattributes.qml2
-rw-r--r--src/quick/doc/snippets/qml/pathview/pathview.qml11
-rw-r--r--src/quick/doc/snippets/qml/propertyaction-sequential.qml2
-rw-r--r--src/quick/doc/snippets/qml/propertyaction.qml2
-rw-r--r--src/quick/doc/snippets/qml/propertyanimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/propertychanges.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-data-models/dynamic-listmodel.qml10
-rw-r--r--src/quick/doc/snippets/qml/qml-data-models/listelements.qml10
-rw-r--r--src/quick/doc/snippets/qml/qml-data-models/listmodel-listview-required.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-data-models/listmodel-listview.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/components/Button.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/components/application.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/methods/app.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/properties/ImageViewer.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/properties/alias-override.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/properties/alias.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/properties/alias/ImageViewer.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/properties/alias/application.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/properties/application.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/properties/property-signals.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/signals/basic.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/signals/connectdynamic.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/signals/connectslots.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/signals/no-parameters.qml2
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/signals/parameters.qml2
-rw-r--r--src/quick/doc/snippets/qml/rectangle/rectangle-colors.qml2
-rw-r--r--src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml2
-rw-r--r--src/quick/doc/snippets/qml/rectangle/rectangle.qml2
-rw-r--r--src/quick/doc/snippets/qml/regularexpression.qml2
-rw-r--r--src/quick/doc/snippets/qml/repeaters/repeater-grid-index.qml2
-rw-r--r--src/quick/doc/snippets/qml/repeaters/repeater.qml12
-rw-r--r--src/quick/doc/snippets/qml/righttoleft.qml2
-rw-r--r--src/quick/doc/snippets/qml/righttoleft/Child.qml2
-rw-r--r--src/quick/doc/snippets/qml/rotation.qml2
-rw-r--r--src/quick/doc/snippets/qml/rotationanimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/row.qml2
-rw-r--r--src/quick/doc/snippets/qml/row/row.qml2
-rw-r--r--src/quick/doc/snippets/qml/sequentialanimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/smoothedanimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/splashWindow.qml57
-rw-r--r--src/quick/doc/snippets/qml/springanimation.qml2
-rw-r--r--src/quick/doc/snippets/qml/state-when.qml2
-rw-r--r--src/quick/doc/snippets/qml/state.qml2
-rw-r--r--src/quick/doc/snippets/qml/states.qml2
-rw-r--r--src/quick/doc/snippets/qml/states/statechangescript.qml2
-rw-r--r--src/quick/doc/snippets/qml/systempalette.qml2
-rw-r--r--src/quick/doc/snippets/qml/tableview/cpp-tablemodel.h2
-rw-r--r--src/quick/doc/snippets/qml/tableview/cpp-tablemodel.qml2
-rw-r--r--src/quick/doc/snippets/qml/tableview/editdelegate.qml4
-rw-r--r--src/quick/doc/snippets/qml/tableview/keyboard-navigation.qml2
-rw-r--r--src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml4
-rw-r--r--src/quick/doc/snippets/qml/tableview/reusabledelegate.qml2
-rw-r--r--src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml2
-rw-r--r--src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml2
-rw-r--r--src/quick/doc/snippets/qml/text/onLinkActivated.qml2
-rw-r--r--src/quick/doc/snippets/qml/text/textEditFormats.qml2
-rw-r--r--src/quick/doc/snippets/qml/text/textFormats.qml2
-rw-r--r--src/quick/doc/snippets/qml/textEditStatusSwitch.qml31
-rw-r--r--src/quick/doc/snippets/qml/texteditor.qml2
-rw-r--r--src/quick/doc/snippets/qml/texthandling.qml2
-rw-r--r--src/quick/doc/snippets/qml/transition-animation.qml13
-rw-r--r--src/quick/doc/snippets/qml/transition-from-to-modified.qml2
-rw-r--r--src/quick/doc/snippets/qml/transition-from-to.qml2
-rw-r--r--src/quick/doc/snippets/qml/transition-reversible.qml2
-rw-r--r--src/quick/doc/snippets/qml/transition.qml2
-rw-r--r--src/quick/doc/snippets/qml/transitions-list.qml2
-rw-r--r--src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml71
-rw-r--r--src/quick/doc/snippets/qml/viewtransitions/viewtransitions-basic.qml2
-rw-r--r--src/quick/doc/snippets/qml/viewtransitions/viewtransitions-delayedbyindex.qml2
-rw-r--r--src/quick/doc/snippets/qml/viewtransitions/viewtransitions-intermediatemove.qml2
-rw-r--r--src/quick/doc/snippets/qml/viewtransitions/viewtransitions-interruptedgood.qml2
-rw-r--r--src/quick/doc/snippets/qml/viewtransitions/viewtransitions-pathanim.qml2
-rw-r--r--src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactionbad.qml2
-rw-r--r--src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactiongood.qml2
-rw-r--r--src/quick/doc/snippets/qml/visualparent.qml2
-rw-r--r--src/quick/doc/snippets/qml/visualparent2.qml2
-rw-r--r--src/quick/doc/snippets/qml/windowActiveAttached.qml10
-rw-r--r--src/quick/doc/snippets/qml/windowPalette.qml38
-rw-r--r--src/quick/doc/snippets/qml/windowVisibility.qml29
-rw-r--r--src/quick/doc/snippets/qml/windowconstraints.qml8
-rw-r--r--src/quick/doc/snippets/qquickrhiitem/qquickrhiitem_intro.cpp147
-rw-r--r--src/quick/doc/src/advtutorial.qdoc2
-rw-r--r--src/quick/doc/src/concepts/effects/particles.qdoc1
-rw-r--r--src/quick/doc/src/concepts/effects/topic.qdoc3
-rw-r--r--src/quick/doc/src/concepts/input/focus.qdoc4
-rw-r--r--src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc5
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc2
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc10
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-responsive.qdoc101
-rw-r--r--src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc40
-rw-r--r--src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc162
-rw-r--r--src/quick/doc/src/concepts/positioning/righttoleft.qdoc10
-rw-r--r--src/quick/doc/src/concepts/positioning/topic.qdoc1
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc22
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc164
-rw-r--r--src/quick/doc/src/dynamicview-tutorial.qdoc1
-rw-r--r--src/quick/doc/src/examples.qdoc68
-rw-r--r--src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc35
-rw-r--r--src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc140
-rw-r--r--src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc22
-rw-r--r--src/quick/doc/src/includes/item.qdocinc4
-rw-r--r--src/quick/doc/src/includes/layout.qdocinc2
-rw-r--r--src/quick/doc/src/internal/deliverMatchingPointsToItem.puml14
-rw-r--r--src/quick/doc/src/internal/ideal-pointer-event-delivery-single-drag.dox242
-rw-r--r--src/quick/doc/src/internal/ideal-pointer-event-delivery.dox22
-rw-r--r--src/quick/doc/src/internal/ideal-pointer-event-delivery.puml55
-rw-r--r--src/quick/doc/src/internal/pinchAndDragHandlers-singleMoveDelivery.svg851
-rw-r--r--src/quick/doc/src/internal/pinchAndDragHandlers-singlePressDelivery.svg714
-rw-r--r--src/quick/doc/src/internal/pinchAndDragHandlers-singlePressPrep.svg648
-rw-r--r--src/quick/doc/src/internal/pinchAndDragHandlers-singleReleaseDelivery.svg825
-rw-r--r--src/quick/doc/src/internal/pinchAndDragHandlers.dot30
-rw-r--r--src/quick/doc/src/internal/pinchAndDragHandlers.svg137
-rw-r--r--src/quick/doc/src/internal/textEdit.dot52
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc91
-rw-r--r--src/quick/doc/src/qtquick-android.qdoc14
-rw-r--r--src/quick/doc/src/qtquick-how-tos.qdoc109
-rw-r--r--src/quick/doc/src/qtquick.qdoc22
-rw-r--r--src/quick/handlers/qquickdragaxis_p.h2
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp23
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h4
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp55
-rw-r--r--src/quick/handlers/qquickhandlerpoint_p.h30
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp17
-rw-r--r--src/quick/handlers/qquickhoverhandler_p.h2
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp2
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p.h2
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p_p.h2
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp62
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h11
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp11
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p.h2
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p_p.h2
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp233
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h4
-rw-r--r--src/quick/handlers/qquickpointerhandler_p_p.h3
-rw-r--r--src/quick/handlers/qquickpointhandler.cpp128
-rw-r--r--src/quick/handlers/qquickpointhandler_p.h2
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp35
-rw-r--r--src/quick/handlers/qquicksinglepointhandler_p.h2
-rw-r--r--src/quick/handlers/qquicksinglepointhandler_p_p.h2
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp267
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h18
-rw-r--r--src/quick/handlers/qquickwheelhandler.cpp6
-rw-r--r--src/quick/handlers/qquickwheelhandler_p.h2
-rw-r--r--src/quick/handlers/qquickwheelhandler_p_p.h2
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp39
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h15
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp306
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h4
-rw-r--r--src/quick/items/qquickaccessibleattached_p.h4
-rw-r--r--src/quick/items/qquickanchors.cpp12
-rw-r--r--src/quick/items/qquickanchors_p.h4
-rw-r--r--src/quick/items/qquickanchors_p_p.h2
-rw-r--r--src/quick/items/qquickanimatedimage.cpp83
-rw-r--r--src/quick/items/qquickanimatedimage_p.h4
-rw-r--r--src/quick/items/qquickanimatedimage_p_p.h24
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h2
-rw-r--r--src/quick/items/qquickborderimage.cpp102
-rw-r--r--src/quick/items/qquickborderimage_p.h3
-rw-r--r--src/quick/items/qquickborderimage_p_p.h17
-rw-r--r--src/quick/items/qquickclipnode_p.h2
-rw-r--r--src/quick/items/qquickcolorgroup.cpp29
-rw-r--r--src/quick/items/qquickcolorgroup_p.h12
-rw-r--r--src/quick/items/qquickdrag.cpp86
-rw-r--r--src/quick/items/qquickdrag_p.h57
-rw-r--r--src/quick/items/qquickdrag_p_p.h4
-rw-r--r--src/quick/items/qquickdroparea.cpp13
-rw-r--r--src/quick/items/qquickdroparea_p.h44
-rw-r--r--src/quick/items/qquickevents.cpp19
-rw-r--r--src/quick/items/qquickevents_p_p.h93
-rw-r--r--src/quick/items/qquickflickable.cpp329
-rw-r--r--src/quick/items/qquickflickable_p.h4
-rw-r--r--src/quick/items/qquickflickable_p_p.h32
-rw-r--r--src/quick/items/qquickflickablebehavior_p.h7
-rw-r--r--src/quick/items/qquickflipable.cpp90
-rw-r--r--src/quick/items/qquickflipable_p.h4
-rw-r--r--src/quick/items/qquickfocusscope_p.h4
-rw-r--r--src/quick/items/qquickframebufferobject.cpp4
-rw-r--r--src/quick/items/qquickgraphicsconfiguration.cpp104
-rw-r--r--src/quick/items/qquickgraphicsconfiguration.h3
-rw-r--r--src/quick/items/qquickgraphicsconfiguration_p.h7
-rw-r--r--src/quick/items/qquickgraphicsdevice.cpp21
-rw-r--r--src/quick/items/qquickgraphicsdevice.h4
-rw-r--r--src/quick/items/qquickgraphicsdevice_p.h4
-rw-r--r--src/quick/items/qquickgraphicsinfo.cpp62
-rw-r--r--src/quick/items/qquickgraphicsinfo_p.h1
-rw-r--r--src/quick/items/qquickgridview.cpp175
-rw-r--r--src/quick/items/qquickgridview_p.h4
-rw-r--r--src/quick/items/qquickimage.cpp74
-rw-r--r--src/quick/items/qquickimage_p.h5
-rw-r--r--src/quick/items/qquickimage_p_p.h4
-rw-r--r--src/quick/items/qquickimagebase.cpp79
-rw-r--r--src/quick/items/qquickimagebase_p.h3
-rw-r--r--src/quick/items/qquickimagebase_p_p.h28
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p.h2
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p_p.h2
-rw-r--r--src/quick/items/qquickitem.cpp703
-rw-r--r--src/quick/items/qquickitem.h26
-rw-r--r--src/quick/items/qquickitem_p.h124
-rw-r--r--src/quick/items/qquickitemanimation.cpp13
-rw-r--r--src/quick/items/qquickitemanimation_p.h12
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h6
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp16
-rw-r--r--src/quick/items/qquickitemsmodule.cpp156
-rw-r--r--src/quick/items/qquickitemview.cpp73
-rw-r--r--src/quick/items/qquickitemview_p.h22
-rw-r--r--src/quick/items/qquickitemview_p_p.h26
-rw-r--r--src/quick/items/qquickitemviewfxitem.cpp35
-rw-r--r--src/quick/items/qquickitemviewfxitem_p_p.h10
-rw-r--r--src/quick/items/qquickitemviewtransition_p.h20
-rw-r--r--src/quick/items/qquicklistview.cpp294
-rw-r--r--src/quick/items/qquicklistview_p.h11
-rw-r--r--src/quick/items/qquickloader.cpp48
-rw-r--r--src/quick/items/qquickloader_p.h5
-rw-r--r--src/quick/items/qquickloader_p_p.h1
-rw-r--r--src/quick/items/qquickmousearea.cpp28
-rw-r--r--src/quick/items/qquickmousearea_p.h4
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp57
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h28
-rw-r--r--src/quick/items/qquickpainteditem.cpp21
-rw-r--r--src/quick/items/qquickpainteditem_p.h3
-rw-r--r--src/quick/items/qquickpalette.cpp55
-rw-r--r--src/quick/items/qquickpalette_p.h17
-rw-r--r--src/quick/items/qquickpalettecolorprovider.cpp52
-rw-r--r--src/quick/items/qquickpalettecolorprovider_p.h3
-rw-r--r--src/quick/items/qquickpaletteproviderprivatebase_p.h32
-rw-r--r--src/quick/items/qquickpathview.cpp109
-rw-r--r--src/quick/items/qquickpathview_p.h10
-rw-r--r--src/quick/items/qquickpathview_p_p.h5
-rw-r--r--src/quick/items/qquickpincharea.cpp2
-rw-r--r--src/quick/items/qquickpincharea_p.h39
-rw-r--r--src/quick/items/qquickpositioners.cpp66
-rw-r--r--src/quick/items/qquickpositioners_p.h34
-rw-r--r--src/quick/items/qquickpositioners_p_p.h7
-rw-r--r--src/quick/items/qquickrectangle.cpp280
-rw-r--r--src/quick/items/qquickrectangle_p.h40
-rw-r--r--src/quick/items/qquickrectangle_p_p.h20
-rw-r--r--src/quick/items/qquickrendercontrol.cpp118
-rw-r--r--src/quick/items/qquickrendercontrol.h5
-rw-r--r--src/quick/items/qquickrendercontrol_p.h4
-rw-r--r--src/quick/items/qquickrendertarget.cpp1164
-rw-r--r--src/quick/items/qquickrendertarget.h31
-rw-r--r--src/quick/items/qquickrendertarget_p.h25
-rw-r--r--src/quick/items/qquickrepeater_p.h4
-rw-r--r--src/quick/items/qquickrhiitem.cpp1144
-rw-r--r--src/quick/items/qquickrhiitem.h124
-rw-r--r--src/quick/items/qquickrhiitem_p.h101
-rw-r--r--src/quick/items/qquickscalegrid_p_p.h14
-rw-r--r--src/quick/items/qquickscreen.cpp2
-rw-r--r--src/quick/items/qquickscreen_p.h42
-rw-r--r--src/quick/items/qquickselectable_p.h11
-rw-r--r--src/quick/items/qquickshadereffect.cpp48
-rw-r--r--src/quick/items/qquickshadereffect_p.h2
-rw-r--r--src/quick/items/qquickshadereffect_p_p.h1
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h8
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp32
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h2
-rw-r--r--src/quick/items/qquicksprite_p.h2
-rw-r--r--src/quick/items/qquickspriteengine_p.h14
-rw-r--r--src/quick/items/qquickspritesequence_p.h2
-rw-r--r--src/quick/items/qquickstateoperations.cpp54
-rw-r--r--src/quick/items/qquickstateoperations_p.h24
-rw-r--r--src/quick/items/qquicktableview.cpp704
-rw-r--r--src/quick/items/qquicktableview_p.h37
-rw-r--r--src/quick/items/qquicktableview_p_p.h31
-rw-r--r--src/quick/items/qquicktext.cpp615
-rw-r--r--src/quick/items/qquicktext_p.h26
-rw-r--r--src/quick/items/qquicktext_p_p.h15
-rw-r--r--src/quick/items/qquicktextcontrol.cpp12
-rw-r--r--src/quick/items/qquicktextcontrol_p.h1
-rw-r--r--src/quick/items/qquicktextcontrol_p_p.h1
-rw-r--r--src/quick/items/qquicktextdocument.cpp640
-rw-r--r--src/quick/items/qquicktextdocument.h43
-rw-r--r--src/quick/items/qquicktextdocument_p.h94
-rw-r--r--src/quick/items/qquicktextedit.cpp822
-rw-r--r--src/quick/items/qquicktextedit_p.h30
-rw-r--r--src/quick/items/qquicktextedit_p_p.h130
-rw-r--r--src/quick/items/qquicktextinput.cpp322
-rw-r--r--src/quick/items/qquicktextinput_p.h10
-rw-r--r--src/quick/items/qquicktextinput_p_p.h6
-rw-r--r--src/quick/items/qquicktextinterface_p.h2
-rw-r--r--src/quick/items/qquicktextnode.cpp286
-rw-r--r--src/quick/items/qquicktextnode_p.h95
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp41
-rw-r--r--src/quick/items/qquicktextnodeengine_p.h7
-rw-r--r--src/quick/items/qquicktextutil_p.h2
-rw-r--r--src/quick/items/qquicktranslate_p.h10
-rw-r--r--src/quick/items/qquicktreeview.cpp115
-rw-r--r--src/quick/items/qquicktreeview_p.h16
-rw-r--r--src/quick/items/qquicktreeview_p_p.h2
-rw-r--r--src/quick/items/qquickview.cpp71
-rw-r--r--src/quick/items/qquickview.h2
-rw-r--r--src/quick/items/qquickview_p.h5
-rw-r--r--src/quick/items/qquickwindow.cpp740
-rw-r--r--src/quick/items/qquickwindow.h14
-rw-r--r--src/quick/items/qquickwindow_p.h63
-rw-r--r--src/quick/items/qquickwindowattached_p.h16
-rw-r--r--src/quick/items/qquickwindowcontainer.cpp606
-rw-r--r--src/quick/items/qquickwindowcontainer_p.h79
-rw-r--r--src/quick/items/qquickwindowmodule.cpp522
-rw-r--r--src/quick/items/qquickwindowmodule_p.h30
-rw-r--r--src/quick/items/qquickwindowmodule_p_p.h15
-rw-r--r--src/quick/items/qsginternaltextnode.cpp265
-rw-r--r--src/quick/items/qsginternaltextnode_p.h199
-rw-r--r--src/quick/jar/CMakeLists.txt13
-rw-r--r--src/quick/jar/org/qtproject/qt/android/QtQuickView.java264
-rw-r--r--src/quick/jar/org/qtproject/qt/android/QtQuickView.qdoc239
-rw-r--r--src/quick/platform/android/qandroidquickviewembedding.cpp439
-rw-r--r--src/quick/platform/android/qandroidquickviewembedding_p.h82
-rw-r--r--src/quick/qtquick.tracepoints48
-rw-r--r--src/quick/qtquickglobal_p.h6
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp8
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp17
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp4
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp148
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h13
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp3
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp4
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp57
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h4
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp2
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp148
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h28
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer_p_p.h7
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp226
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h32
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp25
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h4
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp199
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.cpp62
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader_p.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp11
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.h4
-rw-r--r--src/quick/scenegraph/coreapi/qsgnodeupdater_p.h2
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp11
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h22
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.cpp89
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.h5
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp214
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.h5
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode_p.h5
-rw-r--r--src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp8
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.cpp102
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture_mac.mm2
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture_p.h28
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture_platform.h27
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp17
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h40
-rw-r--r--src/quick/scenegraph/qsgbasicglyphnode_p.h2
-rw-r--r--src/quick/scenegraph/qsgbasicinternalimagenode.cpp18
-rw-r--r--src/quick/scenegraph/qsgbasicinternalimagenode_p.h2
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp580
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h14
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp173
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h54
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp2
-rw-r--r--src/quick/scenegraph/qsgcontextplugin_p.h4
-rw-r--r--src/quick/scenegraph/qsgcurveabstractnode_p.h32
-rw-r--r--src/quick/scenegraph/qsgcurvefillnode.cpp60
-rw-r--r--src/quick/scenegraph/qsgcurvefillnode_p.cpp309
-rw-r--r--src/quick/scenegraph/qsgcurvefillnode_p.h198
-rw-r--r--src/quick/scenegraph/qsgcurvefillnode_p_p.h44
-rw-r--r--src/quick/scenegraph/qsgcurveglyphatlas.cpp142
-rw-r--r--src/quick/scenegraph/qsgcurveglyphatlas_p.h69
-rw-r--r--src/quick/scenegraph/qsgcurveglyphnode.cpp164
-rw-r--r--src/quick/scenegraph/qsgcurveglyphnode_p.h68
-rw-r--r--src/quick/scenegraph/qsgcurveprocessor.cpp1887
-rw-r--r--src/quick/scenegraph/qsgcurveprocessor_p.h53
-rw-r--r--src/quick/scenegraph/qsgcurvestrokenode.cpp112
-rw-r--r--src/quick/scenegraph/qsgcurvestrokenode_p.cpp90
-rw-r--r--src/quick/scenegraph/qsgcurvestrokenode_p.h116
-rw-r--r--src/quick/scenegraph/qsgcurvestrokenode_p_p.h75
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext.cpp19
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext_p.h5
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode.cpp7
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp187
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p_p.h1
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode.cpp17
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode_p.h4
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp30
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h4
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp98
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h13
-rw-r--r--src/quick/scenegraph/qsgdefaultspritenode.cpp26
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp10
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp189
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h12
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp71
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h30
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp23
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h4
-rw-r--r--src/quick/scenegraph/qsgrhiinternaltextnode.cpp41
-rw-r--r--src/quick/scenegraph/qsgrhiinternaltextnode_p.h31
-rw-r--r--src/quick/scenegraph/qsgrhilayer.cpp9
-rw-r--r--src/quick/scenegraph/qsgrhilayer_p.h4
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode.cpp139
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode_p.h1
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp209
-rw-r--r--src/quick/scenegraph/qsgrhisupport_p.h37
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp72
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache_p.h3
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp162
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop_p.h2
-rw-r--r--src/quick/scenegraph/shaders_ng/24bittextmask.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/32bitcolortext.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask_a.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag15
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert16
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag15
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag15
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert18
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag15
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.vert16
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag11
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag11
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.frag11
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.vert14
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag15
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert20
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag15
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert20
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.vert14
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.vert28
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext_a.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.frag7
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.vert10
-rw-r--r--src/quick/scenegraph/shaders_ng/shapecurve.frag142
-rw-r--r--src/quick/scenegraph/shaders_ng/shapecurve.vert79
-rw-r--r--src/quick/scenegraph/shaders_ng/shapestroke.frag134
-rw-r--r--src/quick/scenegraph/shaders_ng/shapestroke.vert82
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.vert28
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.vert28
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.frag11
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.vert20
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.vert22
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext_a.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.vert20
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.frag11
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.vert14
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.vert16
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.frag3
-rw-r--r--src/quick/scenegraph/util/qquadpath.cpp950
-rw-r--r--src/quick/scenegraph/util/qquadpath_p.h341
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator.cpp13
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator_p.h2
-rw-r--r--src/quick/scenegraph/util/qsgdefaultimagenode_p.h2
-rw-r--r--src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h2
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode.cpp5
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgflatcolormaterial.cpp26
-rw-r--r--src/quick/scenegraph/util/qsggradientcache.cpp121
-rw-r--r--src/quick/scenegraph/util/qsggradientcache_p.h72
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture.cpp7
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgrhiatlastexture_p.h2
-rw-r--r--src/quick/scenegraph/util/qsgsimpletexturenode.cpp2
-rw-r--r--src/quick/scenegraph/util/qsgtextnode.cpp320
-rw-r--r--src/quick/scenegraph/util/qsgtextnode.h101
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp35
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial_p.h6
-rw-r--r--src/quick/scenegraph/util/qsgvertexcolormaterial.cpp32
-rw-r--r--src/quick/util/qminimalflatset_p.h12
-rw-r--r--src/quick/util/qquickanimation.cpp138
-rw-r--r--src/quick/util/qquickanimation_p.h36
-rw-r--r--src/quick/util/qquickanimation_p_p.h15
-rw-r--r--src/quick/util/qquickanimationcontroller_p.h4
-rw-r--r--src/quick/util/qquickanimator.cpp21
-rw-r--r--src/quick/util/qquickanimator_p.h23
-rw-r--r--src/quick/util/qquickanimator_p_p.h2
-rw-r--r--src/quick/util/qquickanimatorjob.cpp4
-rw-r--r--src/quick/util/qquickanimatorjob_p.h19
-rw-r--r--src/quick/util/qquickapplication.cpp1
-rw-r--r--src/quick/util/qquickapplication_p.h20
-rw-r--r--src/quick/util/qquickbehavior.cpp7
-rw-r--r--src/quick/util/qquickbehavior_p.h4
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp412
-rw-r--r--src/quick/util/qquickdeliveryagent_p_p.h14
-rw-r--r--src/quick/util/qquickfontloader.cpp30
-rw-r--r--src/quick/util/qquickfontloader_p.h4
-rw-r--r--src/quick/util/qquickfontmetrics_p.h4
-rw-r--r--src/quick/util/qquickforeignutils.cpp31
-rw-r--r--src/quick/util/qquickforeignutils_p.h16
-rw-r--r--src/quick/util/qquickframeanimation_p.h4
-rw-r--r--src/quick/util/qquickglobal.cpp1
-rw-r--r--src/quick/util/qquickimageprovider.cpp16
-rw-r--r--src/quick/util/qquickinputmethod_p.h18
-rw-r--r--src/quick/util/qquickpath.cpp121
-rw-r--r--src/quick/util/qquickpath_p.h53
-rw-r--r--src/quick/util/qquickpath_p_p.h3
-rw-r--r--src/quick/util/qquickpathinterpolator_p.h4
-rw-r--r--src/quick/util/qquickpixmap_p.h205
-rw-r--r--src/quick/util/qquickpixmapcache.cpp741
-rw-r--r--src/quick/util/qquickpixmapcache_p.h188
-rw-r--r--src/quick/util/qquickprofiler_p.h14
-rw-r--r--src/quick/util/qquickpropertychanges.cpp6
-rw-r--r--src/quick/util/qquickpropertychanges_p.h4
-rw-r--r--src/quick/util/qquickshortcut.cpp24
-rw-r--r--src/quick/util/qquickshortcut_p.h2
-rw-r--r--src/quick/util/qquicksmoothedanimation.cpp19
-rw-r--r--src/quick/util/qquicksmoothedanimation_p.h4
-rw-r--r--src/quick/util/qquickspringanimation.cpp6
-rw-r--r--src/quick/util/qquickspringanimation_p.h4
-rw-r--r--src/quick/util/qquickstate.cpp42
-rw-r--r--src/quick/util/qquickstate_p.h9
-rw-r--r--src/quick/util/qquickstatechangescript_p.h4
-rw-r--r--src/quick/util/qquickstategroup.cpp142
-rw-r--r--src/quick/util/qquickstategroup_p.h4
-rw-r--r--src/quick/util/qquickstyledtext.cpp22
-rw-r--r--src/quick/util/qquickstyledtext_p.h10
-rw-r--r--src/quick/util/qquicksvgparser_p.h2
-rw-r--r--src/quick/util/qquicksystempalette.cpp26
-rw-r--r--src/quick/util/qquicksystempalette_p.h6
-rw-r--r--src/quick/util/qquicktextmetrics.cpp19
-rw-r--r--src/quick/util/qquicktextmetrics_p.h4
-rw-r--r--src/quick/util/qquicktextselection.cpp187
-rw-r--r--src/quick/util/qquicktextselection_p.h80
-rw-r--r--src/quick/util/qquicktimeline_p_p.h22
-rw-r--r--src/quick/util/qquicktransition.cpp9
-rw-r--r--src/quick/util/qquicktransition_p.h4
-rw-r--r--src/quick/util/qquicktransitionmanager.cpp4
-rw-r--r--src/quick/util/qquicktransitionmanager_p_p.h2
-rw-r--r--src/quick/util/qquickvalidator.cpp8
-rw-r--r--src/quick/util/qquickvalidator_p.h13
-rw-r--r--src/quick/util/qquickvaluetypes.cpp140
-rw-r--r--src/quick/util/qquickvaluetypes_p.h58
-rw-r--r--src/quickcontrols/CMakeLists.txt27
-rw-r--r--src/quickcontrols/basic/CMakeLists.txt28
-rw-r--r--src/quickcontrols/basic/ComboBox.qml12
-rw-r--r--src/quickcontrols/basic/Dial.qml9
-rw-r--r--src/quickcontrols/basic/DialogButtonBox.qml4
-rw-r--r--src/quickcontrols/basic/HorizontalHeaderView.qml14
-rw-r--r--src/quickcontrols/basic/Menu.qml4
-rw-r--r--src/quickcontrols/basic/TreeViewDelegate.qml8
-rw-r--r--src/quickcontrols/basic/VerticalHeaderView.qml14
-rw-r--r--src/quickcontrols/basic/impl/CMakeLists.txt5
-rw-r--r--src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp6
-rw-r--r--src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h2
-rw-r--r--src/quickcontrols/basic/impl/qquickbasicdial.cpp32
-rw-r--r--src/quickcontrols/basic/impl/qquickbasicdial_p.h10
-rw-r--r--src/quickcontrols/basic/impl/qquickbasicprogressbar.cpp16
-rw-r--r--src/quickcontrols/basic/impl/qquickbasicprogressbar_p.h2
-rw-r--r--src/quickcontrols/basic/qquickbasicstyle_p.h4
-rw-r--r--src/quickcontrols/basic/qquickbasictheme_p.h4
-rw-r--r--src/quickcontrols/basic/qtquickcontrols2basicforeign.cpp10
-rw-r--r--src/quickcontrols/basic/qtquickcontrols2basicforeign_p.h51
-rw-r--r--src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp1
-rw-r--r--src/quickcontrols/configure.cmake6
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-basic-popup-property-propagation.pngbin0 -> 15746 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-basic-thumbnail.pngbin4352 -> 0 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-basic.pngbin10801 -> 14891 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-fusion-dark.pngbin0 -> 17346 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-fusion-light.pngbin0 -> 18238 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-fusion-thumbnail.pngbin6275 -> 0 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-imagine-thumbnail.pngbin4043 -> 0 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-imagine.pngbin7918 -> 15361 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-ios-dark.pngbin17749 -> 18682 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-ios-light.pngbin18954 -> 22634 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-macos-dark.pngbin23979 -> 18045 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-macos-light.pngbin22318 -> 17409 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-macos-thumbnail.pngbin22504 -> 0 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-accent.pngbin1291 -> 2122 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-attributes.pngbin7618 -> 8775 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-background.pngbin1444 -> 2266 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-dark.pngbin10934 -> 23019 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-elevation.pngbin2566 -> 2798 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-foreground.pngbin1377 -> 2127 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-light.pngbin11182 -> 21180 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-theme.pngbin1451 -> 2346 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-thumbnail.pngbin10480 -> 0 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-variant-dense.pngbin21059 -> 24189 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-material-variant-normal.pngbin22897 -> 26245 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-universal-dark.pngbin10373 -> 17940 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-universal-light.pngbin10846 -> 17956 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-universal-thumbnail.pngbin8606 -> 0 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-windows-thumbnail.pngbin12394 -> 0 bytes
-rw-r--r--src/quickcontrols/doc/images/qtquickcontrols-windows.pngbin17132 -> 17132 bytes
-rw-r--r--src/quickcontrols/doc/qtquickcontrols.qdocconf44
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-action.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-busyindicator-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-button-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-button-icononly.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-button-textbesideicon.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-button-textonly.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-calendarmodel.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-group.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-checkdelegate-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-combobox-accepted.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml13
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-combobox-find.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-combobox-popup.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-combobox-textat.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-combobox-valuerole.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-container-oncurrentindexchanged.qml13
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-custom-palette-buttons.qml43
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow-layout.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-delaybutton-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-dial-custom.qml4
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modal.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modeless.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-dialog.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox-attached.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-frame-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-frame.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-checkable.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-groupbox.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-headerview.qml79
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-label-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-label.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-material-accent.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-material-attributes.qml120
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-material-background.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-material-elevation.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-material-foreground.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-material-theme.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-material-variant.qml12
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-menu-createObject.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-menu-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-menu-instantiator.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-menubar-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-menubar.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-layout.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-localization.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modal.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modeless.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-overview.cmake2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-interactive.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-pane-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-pane.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-popup-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-popup-property-propagation.qml59
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-popup.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml31
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-radiobutton-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-radiodelegate-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-rangeslider-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-roundbutton.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-active.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-non-attached.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy-alwayson-when-needed.qml21
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy-alwayson.qml14
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy.qml14
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-active.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-non-attached.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-interactive.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-listview.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-policy.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-scrollview.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-slider-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-double.qml28
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-prefix.qml30
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-textual.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-spinbox.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-splitview-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-splitview-handle-containmentmask.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-stackview-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-stackview-visible.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-transition.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate.qml5
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-indicator.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-switch-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-switchdelegate-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-explicit.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-flickable.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tabbar.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tabbutton.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-textarea-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-textarea-scrollable.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-textfield-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-textfield-disabled.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-textfield-focused.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-textfield-normal.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-toolbar-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-toolbar.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-toolbutton-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-hover.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-pressandhold.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-slider.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tooltip.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-custom.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-listView.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-pathView.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-timePicker.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-universal-accent.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-universal-background.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-universal-foreground.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-universal-theme.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn-layout.qml2
-rw-r--r--src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn.qml2
-rw-r--r--src/quickcontrols/doc/src/includes/container-currentindex.qdocinc6
-rw-r--r--src/quickcontrols/doc/src/includes/qquickcontrol-focusreason.qdocinc15
-rw-r--r--src/quickcontrols/doc/src/includes/qquickheaderview.qdocinc86
-rw-r--r--src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc5
-rw-r--r--src/quickcontrols/doc/src/includes/qquickstackview.qdocinc26
-rw-r--r--src/quickcontrols/doc/src/includes/style-screenshots.qdocinc9
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc51
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc122
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-examples.qdoc1
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc15
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-focus.qdoc2
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-fusion.qdoc2
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-imagine.qdoc9
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-index.qdoc26
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-ios.qdoc19
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-macos.qdoc8
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-material.qdoc124
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-styles.qdoc41
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc18
-rw-r--r--src/quickcontrols/doc/src/qtquickcontrols-windows.qdoc6
-rw-r--r--src/quickcontrols/fusion/BusyIndicator.qml2
-rw-r--r--src/quickcontrols/fusion/CMakeLists.txt21
-rw-r--r--src/quickcontrols/fusion/ComboBox.qml14
-rw-r--r--src/quickcontrols/fusion/Dial.qml7
-rw-r--r--src/quickcontrols/fusion/HorizontalHeaderView.qml14
-rw-r--r--src/quickcontrols/fusion/Menu.qml4
-rw-r--r--src/quickcontrols/fusion/TabButton.qml1
-rw-r--r--src/quickcontrols/fusion/TreeViewDelegate.qml8
-rw-r--r--src/quickcontrols/fusion/VerticalHeaderView.qml14
-rw-r--r--src/quickcontrols/fusion/impl/CMakeLists.txt3
-rw-r--r--src/quickcontrols/fusion/impl/SwitchIndicator.qml7
-rw-r--r--src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp13
-rw-r--r--src/quickcontrols/fusion/qquickfusionstyle_p.h14
-rw-r--r--src/quickcontrols/fusion/qquickfusiontheme_p.h4
-rw-r--r--src/quickcontrols/fusion/qtquickcontrols2fusionstyleplugin.cpp1
-rw-r--r--src/quickcontrols/imagine/CMakeLists.txt25
-rw-r--r--src/quickcontrols/imagine/ComboBox.qml7
-rw-r--r--src/quickcontrols/imagine/Dial.qml7
-rw-r--r--src/quickcontrols/imagine/HorizontalHeaderView.qml14
-rw-r--r--src/quickcontrols/imagine/Menu.qml4
-rw-r--r--src/quickcontrols/imagine/RangeSlider.qml15
-rw-r--r--src/quickcontrols/imagine/Slider.qml9
-rw-r--r--src/quickcontrols/imagine/VerticalHeaderView.qml14
-rw-r--r--src/quickcontrols/imagine/impl/CMakeLists.txt20
-rw-r--r--src/quickcontrols/imagine/impl/OpacityMask.qml2
-rw-r--r--src/quickcontrols/imagine/impl/shaders/+glslcore/OpacityMask.frag13
-rw-r--r--src/quickcontrols/imagine/impl/shaders/+qsb/OpacityMask.fragbin1409 -> 0 bytes
-rw-r--r--src/quickcontrols/imagine/impl/shaders/OpacityMask.frag25
-rw-r--r--src/quickcontrols/imagine/impl/shaders/OpacityMask_rhi.frag17
-rw-r--r--src/quickcontrols/imagine/impl/shaders/compile.bat4
-rw-r--r--src/quickcontrols/imagine/qquickimaginestyle.cpp2
-rw-r--r--src/quickcontrols/imagine/qquickimaginestyle_p.h3
-rw-r--r--src/quickcontrols/imagine/qquickimaginetheme.cpp10
-rw-r--r--src/quickcontrols/imagine/qquickimaginetheme_p.h4
-rw-r--r--src/quickcontrols/imagine/qtquickcontrols2imaginestyleplugin.cpp1
-rw-r--r--src/quickcontrols/ios/BusyIndicator.qml4
-rw-r--r--src/quickcontrols/ios/Button.qml6
-rw-r--r--src/quickcontrols/ios/CMakeLists.txt9
-rw-r--r--src/quickcontrols/ios/CheckBox.qml6
-rw-r--r--src/quickcontrols/ios/CheckDelegate.qml14
-rw-r--r--src/quickcontrols/ios/ComboBox.qml25
-rw-r--r--src/quickcontrols/ios/DelayButton.qml56
-rw-r--r--src/quickcontrols/ios/Dial.qml8
-rw-r--r--src/quickcontrols/ios/Dialog.qml74
-rw-r--r--src/quickcontrols/ios/DialogButtonBox.qml76
-rw-r--r--src/quickcontrols/ios/Drawer.qml4
-rw-r--r--src/quickcontrols/ios/Frame.qml2
-rw-r--r--src/quickcontrols/ios/GroupBox.qml2
-rw-r--r--src/quickcontrols/ios/HorizontalHeaderView.qml14
-rw-r--r--src/quickcontrols/ios/ItemDelegate.qml10
-rw-r--r--src/quickcontrols/ios/Menu.qml8
-rw-r--r--src/quickcontrols/ios/MenuBar.qml2
-rw-r--r--src/quickcontrols/ios/MenuBarItem.qml6
-rw-r--r--src/quickcontrols/ios/MenuItem.qml12
-rw-r--r--src/quickcontrols/ios/MenuSeparator.qml4
-rw-r--r--src/quickcontrols/ios/PageIndicator.qml4
-rw-r--r--src/quickcontrols/ios/Popup.qml4
-rw-r--r--src/quickcontrols/ios/ProgressBar.qml31
-rw-r--r--src/quickcontrols/ios/RadioButton.qml6
-rw-r--r--src/quickcontrols/ios/RadioDelegate.qml14
-rw-r--r--src/quickcontrols/ios/RangeSlider.qml22
-rw-r--r--src/quickcontrols/ios/ScrollBar.qml4
-rw-r--r--src/quickcontrols/ios/ScrollIndicator.qml4
-rw-r--r--src/quickcontrols/ios/SelectionRectangle.qml30
-rw-r--r--src/quickcontrols/ios/Slider.qml16
-rw-r--r--src/quickcontrols/ios/SpinBox.qml12
-rw-r--r--src/quickcontrols/ios/SwipeDelegate.qml10
-rw-r--r--src/quickcontrols/ios/Switch.qml10
-rw-r--r--src/quickcontrols/ios/SwitchDelegate.qml18
-rw-r--r--src/quickcontrols/ios/TabBar.qml2
-rw-r--r--src/quickcontrols/ios/ToolBar.qml2
-rw-r--r--src/quickcontrols/ios/ToolButton.qml6
-rw-r--r--src/quickcontrols/ios/ToolSeparator.qml24
-rw-r--r--src/quickcontrols/ios/ToolTip.qml53
-rw-r--r--src/quickcontrols/ios/TreeViewDelegate.qml16
-rw-r--r--src/quickcontrols/ios/VerticalHeaderView.qml14
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark.9.pngbin0 -> 241 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark@2x.9.pngbin0 -> 318 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark@3x.9.pngbin0 -> 404 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light.9.pngbin0 -> 241 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light@2x.9.pngbin0 -> 326 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light@3x.9.pngbin0 -> 420 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark.9.pngbin0 -> 245 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark@2x.9.pngbin0 -> 321 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark@3x.9.pngbin0 -> 420 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light.9.pngbin0 -> 245 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light@2x.9.pngbin0 -> 321 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light@3x.9.pngbin0 -> 420 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark.9.pngbin0 -> 108 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark@2x.9.pngbin0 -> 111 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark@3x.9.pngbin0 -> 118 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark.9.pngbin0 -> 288 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark@2x.9.pngbin0 -> 406 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark@3x.9.pngbin0 -> 553 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light.9.pngbin0 -> 299 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light@2x.9.pngbin0 -> 427 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light@3x.9.pngbin0 -> 573 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark.9.pngbin0 -> 291 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark@2x.9.pngbin0 -> 418 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark@3x.9.pngbin0 -> 558 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light.9.pngbin0 -> 292 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light@2x.9.pngbin0 -> 423 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light@3x.9.pngbin0 -> 554 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light.9.pngbin0 -> 112 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light@2x.9.pngbin0 -> 115 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light@3x.9.pngbin0 -> 122 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark.9.pngbin0 -> 100 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark@2x.9.pngbin0 -> 102 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark@3x.9.pngbin0 -> 109 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light.9.pngbin0 -> 100 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light@2x.9.pngbin0 -> 102 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light@3x.9.pngbin0 -> 112 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-separator-dark.9.pngbin0 -> 78 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-separator-dark@2x.9.pngbin0 -> 80 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-separator-dark@3x.9.pngbin0 -> 83 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-separator-light.9.pngbin0 -> 78 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-separator-light@2x.9.pngbin0 -> 81 bytes
-rw-r--r--src/quickcontrols/ios/images/dialogbuttonbox-separator-light@3x.9.pngbin0 -> 84 bytes
-rw-r--r--src/quickcontrols/ios/images/selectionrectangle-handle-dark.pngbin0 -> 448 bytes
-rw-r--r--src/quickcontrols/ios/images/selectionrectangle-handle-dark@2x.pngbin0 -> 1208 bytes
-rw-r--r--src/quickcontrols/ios/images/selectionrectangle-handle-dark@3x.pngbin0 -> 1513 bytes
-rw-r--r--src/quickcontrols/ios/images/selectionrectangle-handle-light.pngbin0 -> 445 bytes
-rw-r--r--src/quickcontrols/ios/images/selectionrectangle-handle-light@2x.pngbin0 -> 1201 bytes
-rw-r--r--src/quickcontrols/ios/images/selectionrectangle-handle-light@3x.pngbin0 -> 1456 bytes
-rw-r--r--src/quickcontrols/ios/images/tooltip-background-dark.9.pngbin0 -> 1968 bytes
-rw-r--r--src/quickcontrols/ios/images/tooltip-background-dark@2x.9.pngbin0 -> 5161 bytes
-rw-r--r--src/quickcontrols/ios/images/tooltip-background-dark@3x.9.pngbin0 -> 9861 bytes
-rw-r--r--src/quickcontrols/ios/images/tooltip-background-light.9.pngbin0 -> 1984 bytes
-rw-r--r--src/quickcontrols/ios/images/tooltip-background-light@2x.9.pngbin0 -> 5256 bytes
-rw-r--r--src/quickcontrols/ios/images/tooltip-background-light@3x.9.pngbin0 -> 9877 bytes
-rw-r--r--src/quickcontrols/ios/impl/CMakeLists.txt4
-rw-r--r--src/quickcontrols/ios/impl/DialogButtonBoxDelegate.qml68
-rw-r--r--src/quickcontrols/ios/impl/qquickiosstyle_p.h2
-rw-r--r--src/quickcontrols/ios/qquickiostheme.mm40
-rw-r--r--src/quickcontrols/macos/BusyIndicator.qml41
-rw-r--r--src/quickcontrols/macos/CMakeLists.txt40
-rw-r--r--src/quickcontrols/macos/CheckDelegate.qml50
-rw-r--r--src/quickcontrols/macos/DelayButton.qml69
-rw-r--r--src/quickcontrols/macos/Dialog.qml8
-rw-r--r--src/quickcontrols/macos/DialogButtonBox.qml6
-rw-r--r--src/quickcontrols/macos/ItemDelegate.qml6
-rw-r--r--src/quickcontrols/macos/RadioDelegate.qml6
-rw-r--r--src/quickcontrols/macos/RangeSlider.qml100
-rw-r--r--src/quickcontrols/macos/ScrollView.qml4
-rw-r--r--src/quickcontrols/macos/SpinBox.qml6
-rw-r--r--src/quickcontrols/macos/Switch.qml37
-rw-r--r--src/quickcontrols/macos/SwitchDelegate.qml50
-rw-r--r--src/quickcontrols/macos/images/busyindicator-dark.webpbin0 -> 4500 bytes
-rw-r--r--src/quickcontrols/macos/images/busyindicator-dark@2x.webpbin0 -> 8676 bytes
-rw-r--r--src/quickcontrols/macos/images/busyindicator-dark@3x.webpbin0 -> 11966 bytes
-rw-r--r--src/quickcontrols/macos/images/busyindicator-light.webpbin0 -> 5160 bytes
-rw-r--r--src/quickcontrols/macos/images/busyindicator-light@2x.webpbin0 -> 9142 bytes
-rw-r--r--src/quickcontrols/macos/images/busyindicator-light@3x.webpbin0 -> 11920 bytes
-rw-r--r--src/quickcontrols/macos/impl/CMakeLists.txt22
-rw-r--r--src/quickcontrols/macos/impl/SwitchHandle.qml27
-rw-r--r--src/quickcontrols/macos/impl/SwitchIndicator.qml93
-rw-r--r--src/quickcontrols/material/Button.qml7
-rw-r--r--src/quickcontrols/material/CMakeLists.txt34
-rw-r--r--src/quickcontrols/material/ComboBox.qml64
-rw-r--r--src/quickcontrols/material/Dial.qml7
-rw-r--r--src/quickcontrols/material/Dialog.qml5
-rw-r--r--src/quickcontrols/material/Drawer.qml40
-rw-r--r--src/quickcontrols/material/Frame.qml7
-rw-r--r--src/quickcontrols/material/GroupBox.qml9
-rw-r--r--src/quickcontrols/material/HorizontalHeaderView.qml14
-rw-r--r--src/quickcontrols/material/Menu.qml16
-rw-r--r--src/quickcontrols/material/Page.qml6
-rw-r--r--src/quickcontrols/material/Pane.qml6
-rw-r--r--src/quickcontrols/material/Popup.qml11
-rw-r--r--src/quickcontrols/material/RangeSlider.qml57
-rw-r--r--src/quickcontrols/material/Slider.qml57
-rw-r--r--src/quickcontrols/material/SpinBox.qml22
-rw-r--r--src/quickcontrols/material/StackView.qml41
-rw-r--r--src/quickcontrols/material/Switch.qml3
-rw-r--r--src/quickcontrols/material/TextArea.qml63
-rw-r--r--src/quickcontrols/material/TextField.qml59
-rw-r--r--src/quickcontrols/material/TreeViewDelegate.qml8
-rw-r--r--src/quickcontrols/material/VerticalHeaderView.qml14
-rw-r--r--src/quickcontrols/material/impl/CMakeLists.txt8
-rw-r--r--src/quickcontrols/material/impl/RadioIndicator.qml31
-rw-r--r--src/quickcontrols/material/impl/RectangularGlow.qml2
-rw-r--r--src/quickcontrols/material/impl/SliderHandle.qml15
-rw-r--r--src/quickcontrols/material/impl/SwitchIndicator.qml10
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h2
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp321
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h105
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialprogressbar.cpp4
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialprogressbar_p.h2
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialripple.cpp12
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialripple_p.h2
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp422
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h122
-rw-r--r--src/quickcontrols/material/qquickmaterialstyle.cpp99
-rw-r--r--src/quickcontrols/material/qquickmaterialstyle_p.h29
-rw-r--r--src/quickcontrols/material/qquickmaterialtheme.cpp19
-rw-r--r--src/quickcontrols/material/qquickmaterialtheme_p.h4
-rw-r--r--src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp2
-rw-r--r--src/quickcontrols/material/shaders/+glslcore/RectangularGlow.frag25
-rw-r--r--src/quickcontrols/material/shaders/+hlsl/RectangularGlow.frag21
-rw-r--r--src/quickcontrols/material/shaders/+qsb/RectangularGlow.fragbin2007 -> 0 bytes
-rw-r--r--src/quickcontrols/material/shaders/RectangularGlow.frag38
-rw-r--r--src/quickcontrols/material/shaders/RectangularGlow_rhi.frag28
-rw-r--r--src/quickcontrols/material/shaders/compile.bat4
-rw-r--r--src/quickcontrols/qquickattachedpropertypropagator.cpp150
-rw-r--r--src/quickcontrols/qquickattachedpropertypropagator.h4
-rw-r--r--src/quickcontrols/qquickstyle.cpp5
-rw-r--r--src/quickcontrols/qquickstyleplugin.cpp27
-rw-r--r--src/quickcontrols/qquickstyleplugin_p.h1
-rw-r--r--src/quickcontrols/universal/CMakeLists.txt21
-rw-r--r--src/quickcontrols/universal/ComboBox.qml7
-rw-r--r--src/quickcontrols/universal/Dial.qml7
-rw-r--r--src/quickcontrols/universal/DialogButtonBox.qml4
-rw-r--r--src/quickcontrols/universal/HorizontalHeaderView.qml14
-rw-r--r--src/quickcontrols/universal/Menu.qml4
-rw-r--r--src/quickcontrols/universal/VerticalHeaderView.qml14
-rw-r--r--src/quickcontrols/universal/impl/CMakeLists.txt3
-rw-r--r--src/quickcontrols/universal/impl/qquickuniversalbusyindicator_p.h2
-rw-r--r--src/quickcontrols/universal/impl/qquickuniversalprogressbar.cpp22
-rw-r--r--src/quickcontrols/universal/impl/qquickuniversalprogressbar_p.h2
-rw-r--r--src/quickcontrols/universal/qquickuniversalstyle.cpp2
-rw-r--r--src/quickcontrols/universal/qquickuniversalstyle_p.h3
-rw-r--r--src/quickcontrols/universal/qquickuniversaltheme.cpp7
-rw-r--r--src/quickcontrols/universal/qquickuniversaltheme_p.h4
-rw-r--r--src/quickcontrols/universal/qtquickcontrols2universalstyleplugin.cpp1
-rw-r--r--src/quickcontrols/windows/ApplicationWindow.qml9
-rw-r--r--src/quickcontrols/windows/CMakeLists.txt11
-rw-r--r--src/quickcontrols/windows/CheckDelegate.qml75
-rw-r--r--src/quickcontrols/windows/ComboBox.qml7
-rw-r--r--src/quickcontrols/windows/DelayButton.qml82
-rw-r--r--src/quickcontrols/windows/ItemDelegate.qml10
-rw-r--r--src/quickcontrols/windows/RadioDelegate.qml12
-rw-r--r--src/quickcontrols/windows/RangeSlider.qml105
-rw-r--r--src/quickcontrols/windows/ScrollBar.qml15
-rw-r--r--src/quickcontrols/windows/ScrollView.qml4
-rw-r--r--src/quickcontrols/windows/SpinBox.qml4
-rw-r--r--src/quickcontrols/windows/Switch.qml37
-rw-r--r--src/quickcontrols/windows/SwitchDelegate.qml45
-rw-r--r--src/quickcontrols/windows/impl/CMakeLists.txt21
-rw-r--r--src/quickcontrols/windows/impl/SwitchIndicator.qml59
-rw-r--r--src/quickcontrolsimpl/CMakeLists.txt4
-rw-r--r--src/quickcontrolsimpl/qquickanimatednode_p.h2
-rw-r--r--src/quickcontrolsimpl/qquickchecklabel_p.h4
-rw-r--r--src/quickcontrolsimpl/qquickclippedtext_p.h4
-rw-r--r--src/quickcontrolsimpl/qquickcolor_p.h2
-rw-r--r--src/quickcontrolsimpl/qquickcolorimage_p.h2
-rw-r--r--src/quickcontrolsimpl/qquickiconimage.cpp15
-rw-r--r--src/quickcontrolsimpl/qquickiconimage_p.h4
-rw-r--r--src/quickcontrolsimpl/qquickiconimage_p_p.h2
-rw-r--r--src/quickcontrolsimpl/qquickiconlabel_p.h4
-rw-r--r--src/quickcontrolsimpl/qquickimageselector_p.h3
-rw-r--r--src/quickcontrolsimpl/qquickitemgroup_p.h4
-rw-r--r--src/quickcontrolsimpl/qquickmnemoniclabel_p.h4
-rw-r--r--src/quickcontrolsimpl/qquickninepatchimage.cpp8
-rw-r--r--src/quickcontrolsimpl/qquickninepatchimage_p.h2
-rw-r--r--src/quickcontrolsimpl/qquickpaddedrectangle_p.h4
-rw-r--r--src/quickcontrolsimpl/qquickplaceholdertext.cpp17
-rw-r--r--src/quickcontrolsimpl/qquickplaceholdertext_p.h6
-rw-r--r--src/quickcontrolsimpl/qquickplatformtheme_p.h2
-rw-r--r--src/quickcontrolsimpl/qquicktumblerview_p.h2
-rw-r--r--src/quickcontrolsimpl/qtquickcontrols2foreign.cpp10
-rw-r--r--src/quickcontrolsimpl/qtquickcontrols2foreign_p.h47
-rw-r--r--src/quickcontrolsimpl/qtquickcontrols2implglobal_p.h2
-rw-r--r--src/quickcontrolstestutils/CMakeLists.txt12
-rw-r--r--src/quickcontrolstestutils/controlstestutils.cpp7
-rw-r--r--src/quickcontrolstestutils/controlstestutils_p.h13
-rw-r--r--src/quickcontrolstestutils/dialogstestutils.cpp9
-rw-r--r--src/quickcontrolstestutils/dialogstestutils_p.h2
-rw-r--r--src/quickcontrolstestutils/qtest_quickcontrols_p.h2
-rw-r--r--src/quickdialogs/quickdialogs/CMakeLists.txt4
-rw-r--r--src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf2
-rw-r--r--src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml2
-rw-r--r--src/quickdialogs/quickdialogs/qquickabstractdialog.cpp78
-rw-r--r--src/quickdialogs/quickdialogs/qquickabstractdialog_p.h31
-rw-r--r--src/quickdialogs/quickdialogs/qquickcolordialog.cpp1
-rw-r--r--src/quickdialogs/quickdialogs/qquickcolordialog_p.h4
-rw-r--r--src/quickdialogs/quickdialogs/qquickfiledialog_p.h4
-rw-r--r--src/quickdialogs/quickdialogs/qquickfolderdialog_p.h4
-rw-r--r--src/quickdialogs/quickdialogs/qquickfontdialog_p.h4
-rw-r--r--src/quickdialogs/quickdialogs/qquickmessagedialog.cpp16
-rw-r--r--src/quickdialogs/quickdialogs/qquickmessagedialog_p.h6
-rw-r--r--src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt70
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml3
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml27
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml5
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml26
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml5
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml24
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml5
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml27
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml3
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml23
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml4
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml7
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp5
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h4
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp24
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h6
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h5
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp5
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp11
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h4
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp111
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h14
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h11
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h4
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h6
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h6
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp18
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h6
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h4
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h2
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag3
-rw-r--r--src/quickdialogs/quickdialogsutils/CMakeLists.txt3
-rw-r--r--src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h2
-rw-r--r--src/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h2
-rw-r--r--src/quicklayouts/CMakeLists.txt4
-rw-r--r--src/quicklayouts/qquickgridlayoutengine_p.h2
-rw-r--r--src/quicklayouts/qquicklayout.cpp57
-rw-r--r--src/quicklayouts/qquicklayout_p.h86
-rw-r--r--src/quicklayouts/qquicklayoutglobal_p.h2
-rw-r--r--src/quicklayouts/qquicklayoutitemproxy.cpp532
-rw-r--r--src/quicklayouts/qquicklayoutitemproxy_p.h147
-rw-r--r--src/quicklayouts/qquicklayoutstyleinfo.cpp2
-rw-r--r--src/quicklayouts/qquicklinearlayout.cpp159
-rw-r--r--src/quicklayouts/qquicklinearlayout_p.h29
-rw-r--r--src/quicklayouts/qquickstacklayout.cpp48
-rw-r--r--src/quicklayouts/qquickstacklayout_p.h20
-rw-r--r--src/quicknativestyle/CMakeLists.txt34
-rw-r--r--src/quicknativestyle/controls/DefaultComboBox.qml7
-rw-r--r--src/quicknativestyle/controls/DefaultItemDelegate.qml32
-rw-r--r--src/quicknativestyle/controls/DefaultItemDelegateIconLabel.qml21
-rw-r--r--src/quicknativestyle/controls/DefaultProgressBar.qml5
-rw-r--r--src/quicknativestyle/controls/DefaultRadioDelegate.qml54
-rw-r--r--src/quicknativestyle/controls/DefaultScrollBar.qml2
-rw-r--r--src/quicknativestyle/controls/DefaultTextArea.qml4
-rw-r--r--src/quicknativestyle/controls/DefaultTreeViewDelegate.qml8
-rw-r--r--src/quicknativestyle/items/qquickstyleitem.h4
-rw-r--r--src/quicknativestyle/items/qquickstyleitembutton.h2
-rw-r--r--src/quicknativestyle/items/qquickstyleitemcheckbox.h2
-rw-r--r--src/quicknativestyle/items/qquickstyleitemcheckdelegate.cpp32
-rw-r--r--src/quicknativestyle/items/qquickstyleitemcheckdelegate.h25
-rw-r--r--src/quicknativestyle/items/qquickstyleitemdelaybutton.cpp28
-rw-r--r--src/quicknativestyle/items/qquickstyleitemdelaybutton.h25
-rw-r--r--src/quicknativestyle/items/qquickstyleitemdial.cpp4
-rw-r--r--src/quicknativestyle/items/qquickstyleitemradiobutton.h2
-rw-r--r--src/quicknativestyle/items/qquickstyleitemradiodelegate.cpp29
-rw-r--r--src/quicknativestyle/items/qquickstyleitemradiodelegate.h25
-rw-r--r--src/quicknativestyle/items/qquickstyleitemscrollbar.cpp8
-rw-r--r--src/quicknativestyle/items/qquickstyleitemscrollbar.h2
-rw-r--r--src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm88
-rw-r--r--src/quicknativestyle/qstyle/qquickcommonstyle.cpp36
-rw-r--r--src/quicknativestyle/qstyle/qquickcommonstyle_p.h4
-rw-r--r--src/quicknativestyle/qstyle/qquickstylehelper.cpp9
-rw-r--r--src/quicknativestyle/qstyle/qquickstyleoption.cpp2
-rw-r--r--src/quicknativestyle/qstyle/qquickstyleoption.h11
-rw-r--r--src/quicknativestyle/qstyle/windows/qquickwindowsstyle.cpp2
-rw-r--r--src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp18
-rw-r--r--src/quicknativestyle/qtquickcontrols2nativestyleplugin.cpp13
-rw-r--r--src/quicknativestyle/util/FocusFrame.qml82
-rw-r--r--src/quicknativestyle/util/MacFocusFrame.qml82
-rw-r--r--src/quicknativestyle/util/WindowsFocusFrame.qml58
-rw-r--r--src/quicknativestyle/util/qquickfocusframe.cpp118
-rw-r--r--src/quicknativestyle/util/qquickfocusframe.h39
-rw-r--r--src/quicknativestyle/util/qquickmacfocusframe.h26
-rw-r--r--src/quicknativestyle/util/qquickmacfocusframe.mm126
-rw-r--r--src/quicknativestyle/util/qquickwindowsfocusframe.cpp17
-rw-r--r--src/quicknativestyle/util/qquickwindowsfocusframe.h24
-rw-r--r--src/quickshapes/CMakeLists.txt30
-rw-r--r--src/quickshapes/qquickshape.cpp510
-rw-r--r--src/quickshapes/qquickshape_p.h83
-rw-r--r--src/quickshapes/qquickshape_p_p.h56
-rw-r--r--src/quickshapes/qquickshapecurverenderer.cpp748
-rw-r--r--src/quickshapes/qquickshapecurverenderer_p.h154
-rw-r--r--src/quickshapes/qquickshapecurverenderer_p_p.h28
-rw-r--r--src/quickshapes/qquickshapegenericrenderer.cpp106
-rw-r--r--src/quickshapes/qquickshapegenericrenderer_p.h15
-rw-r--r--src/quickshapes/qquickshapesglobal_p.h4
-rwxr-xr-xsrc/quickshapes/shaders_ng/compile.bat15
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.frag7
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.frag.qsbbin2284 -> 0 bytes
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.vert10
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.vert.qsbbin1856 -> 0 bytes
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.frag7
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.frag.qsbbin1500 -> 0 bytes
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.vert10
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.vert.qsbbin2003 -> 0 bytes
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.frag7
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.frag.qsbbin2555 -> 0 bytes
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.vert10
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.vert.qsbbin1956 -> 0 bytes
-rw-r--r--src/quickshapes/shaders_ng/wireframe.frag25
-rw-r--r--src/quickshapes/shaders_ng/wireframe.vert23
-rw-r--r--src/quicktemplates/CMakeLists.txt62
-rw-r--r--src/quicktemplates/configure.cmake7
-rw-r--r--src/quicktemplates/qquickabstractbutton.cpp100
-rw-r--r--src/quicktemplates/qquickabstractbutton_p.h4
-rw-r--r--src/quicktemplates/qquickabstractbutton_p_p.h6
-rw-r--r--src/quicktemplates/qquickaction.cpp2
-rw-r--r--src/quicktemplates/qquickaction_p.h4
-rw-r--r--src/quicktemplates/qquickactiongroup.cpp2
-rw-r--r--src/quicktemplates/qquickactiongroup_p.h6
-rw-r--r--src/quicktemplates/qquickapplicationwindow.cpp59
-rw-r--r--src/quicktemplates/qquickapplicationwindow_p.h30
-rw-r--r--src/quicktemplates/qquickbusyindicator_p.h4
-rw-r--r--src/quicktemplates/qquickbutton_p.h4
-rw-r--r--src/quicktemplates/qquickbuttongroup.cpp17
-rw-r--r--src/quicktemplates/qquickbuttongroup_p.h6
-rw-r--r--src/quicktemplates/qquickcalendar_p.h2
-rw-r--r--src/quicktemplates/qquickcalendarmodel.cpp32
-rw-r--r--src/quicktemplates/qquickcalendarmodel_p.h8
-rw-r--r--src/quicktemplates/qquickcheckbox.cpp22
-rw-r--r--src/quicktemplates/qquickcheckbox_p.h9
-rw-r--r--src/quicktemplates/qquickcheckdelegate_p.h4
-rw-r--r--src/quicktemplates/qquickcombobox.cpp38
-rw-r--r--src/quicktemplates/qquickcombobox_p.h7
-rw-r--r--src/quicktemplates/qquickcontainer.cpp13
-rw-r--r--src/quicktemplates/qquickcontainer_p.h6
-rw-r--r--src/quicktemplates/qquickcontainer_p_p.h2
-rw-r--r--src/quicktemplates/qquickcontentitem_p.h2
-rw-r--r--src/quicktemplates/qquickcontrol.cpp138
-rw-r--r--src/quicktemplates/qquickcontrol_p.h36
-rw-r--r--src/quicktemplates/qquickcontrol_p_p.h12
-rw-r--r--src/quicktemplates/qquickdayofweekmodel_p.h2
-rw-r--r--src/quicktemplates/qquickdayofweekrow_p.h2
-rw-r--r--src/quicktemplates/qquickdeferredexecute.cpp3
-rw-r--r--src/quicktemplates/qquickdeferredexecute_p_p.h6
-rw-r--r--src/quicktemplates/qquickdeferredpointer_p_p.h4
-rw-r--r--src/quicktemplates/qquickdelaybutton_p.h4
-rw-r--r--src/quicktemplates/qquickdial.cpp243
-rw-r--r--src/quicktemplates/qquickdial_p.h21
-rw-r--r--src/quicktemplates/qquickdialog.cpp4
-rw-r--r--src/quicktemplates/qquickdialog_p.h6
-rw-r--r--src/quicktemplates/qquickdialog_p_p.h2
-rw-r--r--src/quicktemplates/qquickdialogbuttonbox.cpp14
-rw-r--r--src/quicktemplates/qquickdialogbuttonbox_p.h14
-rw-r--r--src/quicktemplates/qquickdialogbuttonbox_p_p.h2
-rw-r--r--src/quicktemplates/qquickdrawer.cpp100
-rw-r--r--src/quicktemplates/qquickdrawer_p.h4
-rw-r--r--src/quicktemplates/qquickdrawer_p_p.h8
-rw-r--r--src/quicktemplates/qquickframe_p.h4
-rw-r--r--src/quicktemplates/qquickframe_p_p.h2
-rw-r--r--src/quicktemplates/qquickgroupbox.cpp11
-rw-r--r--src/quicktemplates/qquickgroupbox_p.h4
-rw-r--r--src/quicktemplates/qquickheaderview.cpp161
-rw-r--r--src/quicktemplates/qquickheaderview_p.h17
-rw-r--r--src/quicktemplates/qquickheaderview_p_p.h6
-rw-r--r--src/quicktemplates/qquickicon_p.h2
-rw-r--r--src/quicktemplates/qquickindicatorbutton_p.cpp10
-rw-r--r--src/quicktemplates/qquickindicatorbutton_p.h3
-rw-r--r--src/quicktemplates/qquickitemdelegate.cpp5
-rw-r--r--src/quicktemplates/qquickitemdelegate_p.h4
-rw-r--r--src/quicktemplates/qquickitemdelegate_p_p.h2
-rw-r--r--src/quicktemplates/qquicklabel.cpp4
-rw-r--r--src/quicktemplates/qquicklabel_p.h12
-rw-r--r--src/quicktemplates/qquickmenu.cpp43
-rw-r--r--src/quicktemplates/qquickmenu_p.h11
-rw-r--r--src/quicktemplates/qquickmenu_p_p.h2
-rw-r--r--src/quicktemplates/qquickmenubar.cpp3
-rw-r--r--src/quicktemplates/qquickmenubar_p.h6
-rw-r--r--src/quicktemplates/qquickmenubar_p_p.h4
-rw-r--r--src/quicktemplates/qquickmenubaritem.cpp82
-rw-r--r--src/quicktemplates/qquickmenubaritem_p.h10
-rw-r--r--src/quicktemplates/qquickmenubaritem_p_p.h3
-rw-r--r--src/quicktemplates/qquickmenuitem_p.h7
-rw-r--r--src/quicktemplates/qquickmenuseparator.cpp2
-rw-r--r--src/quicktemplates/qquickmenuseparator_p.h4
-rw-r--r--src/quicktemplates/qquickmonthgrid.cpp21
-rw-r--r--src/quicktemplates/qquickmonthgrid_p.h10
-rw-r--r--src/quicktemplates/qquickmonthmodel.cpp2
-rw-r--r--src/quicktemplates/qquickmonthmodel_p.h4
-rw-r--r--src/quicktemplates/qquickoverlay.cpp70
-rw-r--r--src/quicktemplates/qquickoverlay_p.h6
-rw-r--r--src/quicktemplates/qquickoverlay_p_p.h4
-rw-r--r--src/quicktemplates/qquickpage.cpp18
-rw-r--r--src/quicktemplates/qquickpage_p.h4
-rw-r--r--src/quicktemplates/qquickpageindicator.cpp7
-rw-r--r--src/quicktemplates/qquickpageindicator_p.h5
-rw-r--r--src/quicktemplates/qquickpane.cpp30
-rw-r--r--src/quicktemplates/qquickpane_p.h4
-rw-r--r--src/quicktemplates/qquickpane_p_p.h4
-rw-r--r--src/quicktemplates/qquickpopup.cpp258
-rw-r--r--src/quicktemplates/qquickpopup_p.h7
-rw-r--r--src/quicktemplates/qquickpopup_p_p.h16
-rw-r--r--src/quicktemplates/qquickpopupanchors_p.h6
-rw-r--r--src/quicktemplates/qquickpopupitem.cpp9
-rw-r--r--src/quicktemplates/qquickpopupitem_p_p.h6
-rw-r--r--src/quicktemplates/qquickpopuppositioner.cpp23
-rw-r--r--src/quicktemplates/qquickpopuppositioner_p_p.h2
-rw-r--r--src/quicktemplates/qquickpresshandler.cpp7
-rw-r--r--src/quicktemplates/qquickpresshandler_p_p.h4
-rw-r--r--src/quicktemplates/qquickprogressbar.cpp2
-rw-r--r--src/quicktemplates/qquickprogressbar_p.h4
-rw-r--r--src/quicktemplates/qquickradiobutton.cpp2
-rw-r--r--src/quicktemplates/qquickradiobutton_p.h4
-rw-r--r--src/quicktemplates/qquickradiodelegate.cpp2
-rw-r--r--src/quicktemplates/qquickradiodelegate_p.h4
-rw-r--r--src/quicktemplates/qquickrangeslider.cpp62
-rw-r--r--src/quicktemplates/qquickrangeslider_p.h6
-rw-r--r--src/quicktemplates/qquickroundbutton_p.h4
-rw-r--r--src/quicktemplates/qquickscrollbar.cpp84
-rw-r--r--src/quicktemplates/qquickscrollbar_p.h6
-rw-r--r--src/quicktemplates/qquickscrollbar_p_p.h1
-rw-r--r--src/quicktemplates/qquickscrollindicator.cpp25
-rw-r--r--src/quicktemplates/qquickscrollindicator_p.h6
-rw-r--r--src/quicktemplates/qquickscrollview.cpp192
-rw-r--r--src/quicktemplates/qquickscrollview_p.h13
-rw-r--r--src/quicktemplates/qquickselectionrectangle.cpp153
-rw-r--r--src/quicktemplates/qquickselectionrectangle_p.h8
-rw-r--r--src/quicktemplates/qquickselectionrectangle_p_p.h2
-rw-r--r--src/quicktemplates/qquickshortcutcontext.cpp12
-rw-r--r--src/quicktemplates/qquickshortcutcontext_p_p.h2
-rw-r--r--src/quicktemplates/qquickslider.cpp20
-rw-r--r--src/quicktemplates/qquickslider_p.h4
-rw-r--r--src/quicktemplates/qquickspinbox.cpp182
-rw-r--r--src/quicktemplates/qquickspinbox_p.h10
-rw-r--r--src/quicktemplates/qquicksplitview.cpp294
-rw-r--r--src/quicktemplates/qquicksplitview_p.h12
-rw-r--r--src/quicktemplates/qquicksplitview_p_p.h11
-rw-r--r--src/quicktemplates/qquickstackelement.cpp58
-rw-r--r--src/quicktemplates/qquickstackelement_p_p.h16
-rw-r--r--src/quicktemplates/qquickstacktransition_p_p.h4
-rw-r--r--src/quicktemplates/qquickstackview.cpp520
-rw-r--r--src/quicktemplates/qquickstackview_p.cpp123
-rw-r--r--src/quicktemplates/qquickstackview_p.h81
-rw-r--r--src/quicktemplates/qquickstackview_p_p.h27
-rw-r--r--src/quicktemplates/qquickswipe_p.h2
-rw-r--r--src/quicktemplates/qquickswipedelegate.cpp56
-rw-r--r--src/quicktemplates/qquickswipedelegate_p.h5
-rw-r--r--src/quicktemplates/qquickswipedelegate_p_p.h1
-rw-r--r--src/quicktemplates/qquickswipeview.cpp41
-rw-r--r--src/quicktemplates/qquickswipeview_p.h9
-rw-r--r--src/quicktemplates/qquickswitch_p.h4
-rw-r--r--src/quicktemplates/qquickswitchdelegate_p.h4
-rw-r--r--src/quicktemplates/qquicktabbar_p.h8
-rw-r--r--src/quicktemplates/qquicktabbutton.cpp2
-rw-r--r--src/quicktemplates/qquicktabbutton_p.h4
-rw-r--r--src/quicktemplates/qquicktextarea.cpp78
-rw-r--r--src/quicktemplates/qquicktextarea_p.h14
-rw-r--r--src/quicktemplates/qquicktextarea_p_p.h9
-rw-r--r--src/quicktemplates/qquicktextfield.cpp37
-rw-r--r--src/quicktemplates/qquicktextfield_p.h12
-rw-r--r--src/quicktemplates/qquicktextfield_p_p.h1
-rw-r--r--src/quicktemplates/qquicktheme_p.h2
-rw-r--r--src/quicktemplates/qquicktheme_p_p.h2
-rw-r--r--src/quicktemplates/qquicktoolbar_p.h4
-rw-r--r--src/quicktemplates/qquicktoolbutton.cpp2
-rw-r--r--src/quicktemplates/qquicktoolbutton_p.h4
-rw-r--r--src/quicktemplates/qquicktoolseparator_p.h4
-rw-r--r--src/quicktemplates/qquicktooltip_p.h6
-rw-r--r--src/quicktemplates/qquicktreeviewdelegate.cpp8
-rw-r--r--src/quicktemplates/qquicktreeviewdelegate_p.h2
-rw-r--r--src/quicktemplates/qquicktumbler.cpp3
-rw-r--r--src/quicktemplates/qquicktumbler_p.h6
-rw-r--r--src/quicktemplates/qquicktumbler_p_p.h4
-rw-r--r--src/quicktemplates/qquickweeknumbercolumn_p.h2
-rw-r--r--src/quicktemplates/qquickweeknumbermodel_p.h2
-rw-r--r--src/quicktemplates/qtquicktemplates2global_p.h10
-rw-r--r--src/quicktestutils/CMakeLists.txt1
-rw-r--r--src/quicktestutils/qml/platforminputcontext_p.h2
-rw-r--r--src/quicktestutils/qml/platformquirks_p.h20
-rw-r--r--src/quicktestutils/qml/qmlutils.cpp31
-rw-r--r--src/quicktestutils/qml/qmlutils_p.h18
-rw-r--r--src/quicktestutils/qml/testhttpserver.cpp8
-rw-r--r--src/quicktestutils/qml/testhttpserver_p.h2
-rw-r--r--src/quicktestutils/quick/geometrytestutils.cpp2
-rw-r--r--src/quicktestutils/quick/geometrytestutils_p.h2
-rw-r--r--src/quicktestutils/quick/viewtestutils.cpp7
-rw-r--r--src/quicktestutils/quick/viewtestutils_p.h5
-rw-r--r--src/quicktestutils/quick/visualtestutils.cpp80
-rw-r--r--src/quicktestutils/quick/visualtestutils_p.h38
-rw-r--r--src/quickvectorimage/CMakeLists.txt40
-rw-r--r--src/quickvectorimage/generator/qquickgenerator.cpp80
-rw-r--r--src/quickvectorimage/generator/qquickgenerator_p.h75
-rw-r--r--src/quickvectorimage/generator/qquickitemgenerator.cpp393
-rw-r--r--src/quickvectorimage/generator/qquickitemgenerator_p.h56
-rw-r--r--src/quickvectorimage/generator/qquicknodeinfo_p.h105
-rw-r--r--src/quickvectorimage/generator/qquickqmlgenerator.cpp578
-rw-r--r--src/quickvectorimage/generator/qquickqmlgenerator_p.h101
-rw-r--r--src/quickvectorimage/generator/qsvgvisitorimpl.cpp776
-rw-r--r--src/quickvectorimage/generator/qsvgvisitorimpl_p.h69
-rw-r--r--src/quickvectorimage/generator/utils_p.h264
-rw-r--r--src/quickvectorimage/qquickvectorimage.cpp135
-rw-r--r--src/quickvectorimage/qquickvectorimage_p.h49
-rw-r--r--src/quickvectorimage/qquickvectorimage_p_p.h47
-rw-r--r--src/quickvectorimage/qquickvectorimageglobal_p.h43
-rw-r--r--src/quickwidgets/qaccessiblequickwidget.cpp30
-rw-r--r--src/quickwidgets/qaccessiblequickwidget_p.h6
-rw-r--r--src/quickwidgets/qquickwidget.cpp109
-rw-r--r--src/quickwidgets/qquickwidget_p.h4
-rw-r--r--sync.profile37
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/auto/CMakeLists.txt1
-rw-r--r--tests/auto/cmake/CMakeLists.txt22
-rw-r--r--tests/auto/cmake/empty_qmldir/enemy.cpp2
-rw-r--r--tests/auto/cmake/empty_qmldir/enemy.h2
-rw-r--r--tests/auto/cmake/empty_qmldir/main.cpp2
-rw-r--r--tests/auto/cmake/qmlimportscanner/main.cpp2
-rw-r--r--tests/auto/cmake/qmlquery/CMakeLists.txt1
-rw-r--r--tests/auto/cmake/qmlquery/My/OtherThings/CMakeLists.txt28
-rw-r--r--tests/auto/cmake/qmlquery/My/OtherThings/test.cpp3
-rw-r--r--tests/auto/cmake/qmlquery/My/OtherThings/test.h18
-rw-r--r--tests/auto/cmake/qtquickcompiler/main.cpp2
-rw-r--r--tests/auto/cmake/test_common_import_path/CMakeLists.txt5
-rw-r--r--tests/auto/cmake/test_common_import_path/main.cpp2
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt43
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/CMakeLists.txt4
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/CMakeLists.txt9
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/CMakeLists.txt4
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/CMakeLists.txt7
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/Main.qml4
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Main.qml4
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/Main.qml5
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/SomeSubfolder/CMakeLists.txt13
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/SomeSubfolder/Main.qml5
-rw-r--r--tests/auto/cmake/test_generate_qmlls_ini/main.cpp96
-rw-r--r--tests/auto/cmake/test_import_static_shapes_plugin_resources/app.qml2
-rw-r--r--tests/auto/cmake/test_import_static_shapes_plugin_resources/tst_main.cpp2
-rw-r--r--tests/auto/cmake/test_internal_singleton/CMakeLists.txt23
-rw-r--r--tests/auto/cmake/test_internal_singleton/Test.qml4
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/Base/Base.cpp10
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/Base/Base.h16
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/Base/CMakeLists.txt13
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/Base/qml/Red.qml10
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/CMakeLists.txt59
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/Derived/CMakeLists.txt42
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/Derived/qml/Blue.qml10
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/Derived/qml/main.qml23
-rw-r--r--tests/auto/cmake/test_link_qml_module_without_target/main.cpp43
-rw-r--r--tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.cpp2
-rw-r--r--tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.h2
-rw-r--r--tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/FunkyItemQml.qml2
-rw-r--r--tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.cpp2
-rw-r--r--tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.h2
-rw-r--r--tests/auto/cmake/test_qml_app_deployment/main.cpp2
-rw-r--r--tests/auto/cmake/test_qml_app_deployment/main.qml2
-rw-r--r--tests/auto/cmake/test_static_qml_module/CMakeLists.txt38
-rw-r--r--tests/auto/cmake/test_static_qml_module/MyElement.cpp4
-rw-r--r--tests/auto/cmake/test_static_qml_module/MyElement.h20
-rw-r--r--tests/auto/cmake/test_static_qml_module/main.cpp35
-rw-r--r--tests/auto/cmake/test_static_qml_module/main.qml7
-rw-r--r--tests/auto/cmake/tooling_imports/First/Neighbor.cpp2
-rw-r--r--tests/auto/cmake/tooling_imports/First/Neighbor.h9
-rw-r--r--tests/auto/cmake/tooling_imports/main.cpp2
-rw-r--r--tests/auto/core/CMakeLists.txt4
-rw-r--r--tests/auto/core/qqmlsettings/CMakeLists.txt9
-rw-r--r--tests/auto/core/qqmlsettings/data/aliases.qml2
-rw-r--r--tests/auto/core/qqmlsettings/data/basic.qml2
-rw-r--r--tests/auto/core/qqmlsettings/data/categories.qml2
-rw-r--r--tests/auto/core/qqmlsettings/data/cpp-aliases.qml2
-rw-r--r--tests/auto/core/qqmlsettings/data/resources.qml10
-rw-r--r--tests/auto/core/qqmlsettings/data/siblings.qml2
-rw-r--r--tests/auto/core/qqmlsettings/data/types.qml2
-rw-r--r--tests/auto/core/qqmlsettings/test_settings.ini3
-rw-r--r--tests/auto/core/qqmlsettings/tst_qqmlsettings.cpp14
-rw-r--r--tests/auto/core/qqmlstandardpaths/CMakeLists.txt2
-rw-r--r--tests/auto/core/qqmlstandardpaths/data/tst_standardpaths.qml2
-rw-r--r--tests/auto/core/qqmlstandardpaths/tst_qqmlstandardpaths.cpp6
-rw-r--r--tests/auto/core/qqmlsysteminformation/CMakeLists.txt2
-rw-r--r--tests/auto/core/qqmlsysteminformation/data/tst_systeminformation.qml2
-rw-r--r--tests/auto/core/qqmlsysteminformation/tst_qqmlsysteminformation.cpp2
-rw-r--r--tests/auto/guiapplauncher/examples.txt15
-rw-r--r--tests/auto/particles/qquickage/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickage/data/jump.qml2
-rw-r--r--tests/auto/particles/qquickage/data/kill.qml2
-rw-r--r--tests/auto/particles/qquickage/data/onceoff.qml2
-rw-r--r--tests/auto/particles/qquickage/data/sustained.qml2
-rw-r--r--tests/auto/particles/qquickage/tst_qquickage.cpp2
-rw-r--r--tests/auto/particles/qquickangleddirection/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickangleddirection/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp2
-rw-r--r--tests/auto/particles/qquickcumulativedirection/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickcumulativedirection/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp2
-rw-r--r--tests/auto/particles/qquickcustomaffector/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickcustomaffector/data/affectedSignal.qml2
-rw-r--r--tests/auto/particles/qquickcustomaffector/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickcustomaffector/data/move.qml2
-rw-r--r--tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp2
-rw-r--r--tests/auto/particles/qquickellipseextruder/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickellipseextruder/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp2
-rw-r--r--tests/auto/particles/qquickfriction/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickfriction/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickfriction/data/threshold.qml2
-rw-r--r--tests/auto/particles/qquickfriction/tst_qquickfriction.cpp2
-rw-r--r--tests/auto/particles/qquickgravity/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickgravity/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickgravity/tst_qquickgravity.cpp2
-rw-r--r--tests/auto/particles/qquickgroupgoal/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickgroupgoal/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp2
-rw-r--r--tests/auto/particles/qquickimageparticle/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickimageparticle/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickimageparticle/data/colorVariance.qml2
-rw-r--r--tests/auto/particles/qquickimageparticle/data/colored.qml2
-rw-r--r--tests/auto/particles/qquickimageparticle/data/deformed.qml2
-rw-r--r--tests/auto/particles/qquickimageparticle/data/sprite.qml2
-rw-r--r--tests/auto/particles/qquickimageparticle/data/tabled.qml2
-rw-r--r--tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp2
-rw-r--r--tests/auto/particles/qquickitemparticle/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickitemparticle/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickitemparticle/data/loader.qml2
-rw-r--r--tests/auto/particles/qquickitemparticle/data/managed.qml2
-rw-r--r--tests/auto/particles/qquickitemparticle/data/takeGive.qml2
-rw-r--r--tests/auto/particles/qquickitemparticle/data/unmanaged.qml2
-rw-r--r--tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp2
-rw-r--r--tests/auto/particles/qquicklineextruder/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquicklineextruder/data/basic.qml2
-rw-r--r--tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp2
-rw-r--r--tests/auto/particles/qquickmaskextruder/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickmaskextruder/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp2
-rw-r--r--tests/auto/particles/qquickparticlegroup/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickparticlegroup/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp2
-rw-r--r--tests/auto/particles/qquickparticlesystem/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickparticlesystem/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickparticlesystem/data/crashaffectors.qml2
-rw-r--r--tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp2
-rw-r--r--tests/auto/particles/qquickpointattractor/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickpointattractor/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp2
-rw-r--r--tests/auto/particles/qquickpointdirection/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickpointdirection/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp2
-rw-r--r--tests/auto/particles/qquickrectangleextruder/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickrectangleextruder/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp2
-rw-r--r--tests/auto/particles/qquickspritegoal/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickspritegoal/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp2
-rw-r--r--tests/auto/particles/qquicktargetdirection/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquicktargetdirection/data/basic.qml2
-rw-r--r--tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp2
-rw-r--r--tests/auto/particles/qquicktrailemitter/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquicktrailemitter/data/basic.qml2
-rw-r--r--tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp2
-rw-r--r--tests/auto/particles/qquickturbulence/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickturbulence/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp2
-rw-r--r--tests/auto/particles/qquickwander/CMakeLists.txt6
-rw-r--r--tests/auto/particles/qquickwander/data/basic.qml2
-rw-r--r--tests/auto/particles/qquickwander/tst_qquickwander.cpp2
-rw-r--r--tests/auto/particles/shared/particlestestsshared.h2
-rw-r--r--tests/auto/qml/CMakeLists.txt21
-rw-r--r--tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt6
-rw-r--r--tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp5
-rw-r--r--tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt6
-rw-r--r--tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp5
-rw-r--r--tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt6
-rw-r--r--tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp44
-rw-r--r--tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt6
-rw-r--r--tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp2
-rw-r--r--tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt6
-rw-r--r--tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp10
-rw-r--r--tests/auto/qml/bindingdependencyapi/CMakeLists.txt6
-rw-r--r--tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp34
-rw-r--r--tests/auto/qml/common/CMakeLists.txt11
-rw-r--r--tests/auto/qml/common/tst_qml_common.cpp168
-rw-r--r--tests/auto/qml/common/tst_qml_common.h35
-rw-r--r--tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qdebugmessageservice/data/test.qml2
-rw-r--r--tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp2
-rw-r--r--tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp36
-rw-r--r--tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp13
-rw-r--r--tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp16
-rw-r--r--tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/condition.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/exception.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/quit.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/test.js2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/test.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/timer.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp74
-rw-r--r--tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmldebugservice/data/test.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp56
-rw-r--r--tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp4
-rw-r--r--tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml2
-rw-r--r--tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp26
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml4
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp59
-rw-r--r--tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp2
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/data/broken.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/data/window.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/data/window1.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/data/window2.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/data/zoom.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp4
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt6
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml2
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp14
-rw-r--r--tests/auto/qml/debugger/qv4debugger/CMakeLists.txt9
-rw-r--r--tests/auto/qml/debugger/qv4debugger/commontypes.h4
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml4
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/module1.js5
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/module2.mjs7
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/module3.mjs (renamed from src/plugins/scenegraph/openvg/openvg.tracepoints)0
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/module4.mjs0
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp81
-rw-r--r--tests/auto/qml/debugger/shared/debugutil.cpp2
-rw-r--r--tests/auto/qml/debugger/shared/debugutil_p.h2
-rw-r--r--tests/auto/qml/debugger/shared/qqmldebugprocess.cpp2
-rw-r--r--tests/auto/qml/debugger/shared/qqmldebugprocess_p.h2
-rw-r--r--tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp2
-rw-r--r--tests/auto/qml/debugger/shared/qqmldebugtestservice.h2
-rw-r--r--tests/auto/qml/ecmascripttests/CMakeLists.txt13
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations7
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/CMakeLists.txt28
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/main.cpp90
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp849
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/test262runner.h129
-rwxr-xr-xtests/auto/qml/ecmascripttests/test262.py611
-rw-r--r--tests/auto/qml/ecmascripttests/test262runner.cpp1010
-rw-r--r--tests/auto/qml/ecmascripttests/test262runner.h167
-rw-r--r--tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp94
-rw-r--r--tests/auto/qml/linebylinelex/CMakeLists.txt6
-rw-r--r--tests/auto/qml/linebylinelex/tst_linebylinelex.cpp2
-rw-r--r--tests/auto/qml/parserstress/CMakeLists.txt6
-rw-r--r--tests/auto/qml/parserstress/tst_parserstress.cpp6
-rw-r--r--tests/auto/qml/qjsengine/CMakeLists.txt7
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp464
-rw-r--r--tests/auto/qml/qjsmanagedvalue/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp23
-rw-r--r--tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h2
-rw-r--r--tests/auto/qml/qjsonbinding/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp11
-rw-r--r--tests/auto/qml/qjsprimitivevalue/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp34
-rw-r--r--tests/auto/qml/qjsvalue/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp42
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.h9
-rw-r--r--tests/auto/qml/qjsvalueiterator/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp6
-rw-r--r--tests/auto/qml/qml/CMakeLists.txt8
-rw-r--r--tests/auto/qml/qml/data/resizeItem.qml28
-rw-r--r--tests/auto/qml/qml/data/sizedItem.qml19
-rw-r--r--tests/auto/qml/qml/data/unsizedItem.qml18
-rw-r--r--tests/auto/qml/qml/tst_qml.cpp127
-rw-r--r--tests/auto/qml/qmlbasicapp/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp2
-rw-r--r--tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h2
-rw-r--r--tests/auto/qml/qmlbasicapp/main.qml2
-rw-r--r--tests/auto/qml/qmlbasicapp/manual_imports.cpp2
-rw-r--r--tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp2
-rw-r--r--tests/auto/qml/qmlcachegen/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qmlcachegen/data/truncateTest.qml764
-rw-r--r--tests/auto/qml/qmlcachegen/scriptstringprops.h2
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp26
-rw-r--r--tests/auto/qml/qmlcppcodegen/CMakeLists.txt14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Action.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/B.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt252
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml12
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Dummy2.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml19
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js3
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Panel.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Planner.qml50
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml12
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/Variable.qml22
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ambiguous.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/asCast.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml33
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml40
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp19
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/birthdayparty.h8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml45
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/childobject.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml39
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml54
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml32
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/conversions2.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dateConversions.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h29
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h41
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumConversion.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumProblems.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumProperty.h71
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enumproblems.h68
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/extra/extra.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/failures.qml60
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml23
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/fileDialog.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/flagEnum.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h38
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h42
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/idAccess.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml39
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/intToEnum.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/interactive.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/internalConversion.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/invisible.h36
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/iteration.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml28
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml26
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listConversion.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listToString.qml25
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listprovider.h24
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/math.qml1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml59
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/methods.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/multiforeign.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml33
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/nullComparison.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml37
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml42
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h53
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml77
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/person.cpp20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/person.h44
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml13
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml19
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/resettable.h39
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/resettable.qml23
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml19
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h56
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml31
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml35
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/state.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/theme.cpp2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/theme.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/thisObject.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/timelinetheme.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml14
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/trigraphs.qml5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp47
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/urlString.qml7
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml43
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml34
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantMap.qml27
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h17
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantReturn.qml15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/variantreturn.h63
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/voidConversion.qml10
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h61
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/withlength.h28
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/writeback.qml43
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp5738
-rw-r--r--tests/auto/qml/qmldiskcache/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp404
-rw-r--r--tests/auto/qml/qmlformat/CMakeLists.txt9
-rw-r--r--tests/auto/qml/qmlformat/data/Annotations.formatted.qml17
-rw-r--r--tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml10
-rw-r--r--tests/auto/qml/qmlformat/data/Example1.formatted.qml10
-rw-r--r--tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml10
-rw-r--r--tests/auto/qml/qmlformat/data/Example1.formatted2.qml10
-rw-r--r--tests/auto/qml/qmlformat/data/FrontInline.formatted.qml3
-rw-r--r--tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml4
-rw-r--r--tests/auto/qml/qmlformat/data/arrayEndComma.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml10
-rw-r--r--tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml12
-rw-r--r--tests/auto/qml/qmlformat/data/blanklinesAfterComment.qml15
-rw-r--r--tests/auto/qml/qmlformat/data/class.formatted.js5
-rw-r--r--tests/auto/qml/qmlformat/data/class.js1
-rw-r--r--tests/auto/qml/qmlformat/data/commentInEnum.formatted.qml11
-rw-r--r--tests/auto/qml/qmlformat/data/commentInEnum.qml12
-rw-r--r--tests/auto/qml/qmlformat/data/commentInQmlObject.formatted.qml4
-rw-r--r--tests/auto/qml/qmlformat/data/commentInQmlObject.qml5
-rw-r--r--tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml24
-rw-r--r--tests/auto/qml/qmlformat/data/destructuringFunctionParameter.qml9
-rw-r--r--tests/auto/qml/qmlformat/data/directives.formatted.js8
-rw-r--r--tests/auto/qml/qmlformat/data/directives.js9
-rw-r--r--tests/auto/qml/qmlformat/data/directivesWithComments.formatted.js6
-rw-r--r--tests/auto/qml/qmlformat/data/directivesWithComments.js8
-rw-r--r--tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml12
-rw-r--r--tests/auto/qml/qmlformat/data/dontRemoveComments.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml2
-rw-r--r--tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.formatted.qml13
-rw-r--r--tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.qml11
-rw-r--r--tests/auto/qml/qmlformat/data/escapeChars.formatted.qml16
-rw-r--r--tests/auto/qml/qmlformat/data/escapeChars.qml16
-rw-r--r--tests/auto/qml/qmlformat/data/esm.formatted.mjs44
-rw-r--r--tests/auto/qml/qmlformat/data/esm.mjs33
-rw-r--r--tests/auto/qml/qmlformat/data/filesOption/valid1.formatted.qml10
-rw-r--r--tests/auto/qml/qmlformat/data/filesOption/valid1.qml9
-rw-r--r--tests/auto/qml/qmlformat/data/filesOption/valid2.formatted.qml10
-rw-r--r--tests/auto/qml/qmlformat/data/filesOption/valid2.qml9
-rw-r--r--tests/auto/qml/qmlformat/data/forOf.formatted.qml1
-rw-r--r--tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml3
-rw-r--r--tests/auto/qml/qmlformat/data/importStatements.formatted.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/importStatements.qml12
-rw-r--r--tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml6
-rw-r--r--tests/auto/qml/qmlformat/data/javascriptBlock.qml5
-rw-r--r--tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.formatted.js6
-rw-r--r--tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.js1
-rw-r--r--tests/auto/qml/qmlformat/data/lambdaWithIfElse.formatted.js7
-rw-r--r--tests/auto/qml/qmlformat/data/lambdaWithIfElse.js1
-rw-r--r--tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.formatted.js9
-rw-r--r--tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.js1
-rw-r--r--tests/auto/qml/qmlformat/data/messyIfStatement.formatted.js4
-rw-r--r--tests/auto/qml/qmlformat/data/messyIfStatement.js2
-rw-r--r--tests/auto/qml/qmlformat/data/mini_esm.formattedTabs.mjs20
-rw-r--r--tests/auto/qml/qmlformat/data/mini_esm.mjs3
-rw-r--r--tests/auto/qml/qmlformat/data/multilineComment.formatted.qml6
-rw-r--r--tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml2
-rw-r--r--tests/auto/qml/qmlformat/data/nestedIf.formatted.qml1
-rw-r--r--tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml137
-rw-r--r--tests/auto/qml/qmlformat/data/objectDestructuring.qml77
-rw-r--r--tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml3
-rw-r--r--tests/auto/qml/qmlformat/data/objectsSpacing.qml4
-rw-r--r--tests/auto/qml/qmlformat/data/pragma.formatted.js4
-rw-r--r--tests/auto/qml/qmlformat/data/pragma.formatted.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/pragma.js1
-rw-r--r--tests/auto/qml/qmlformat/data/pragma.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/propertyNames.formatted.qml6
-rw-r--r--tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml2
-rw-r--r--tests/auto/qml/qmlformat/data/simpleJSStatement.formatted.js1
-rw-r--r--tests/auto/qml/qmlformat/data/simpleJSStatement.js1
-rw-r--r--tests/auto/qml/qmlformat/data/simpleLoop.formatted.js4
-rw-r--r--tests/auto/qml/qmlformat/data/simpleLoop.js1
-rw-r--r--tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.formatted.js4
-rw-r--r--tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.js1
-rw-r--r--tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml9
-rw-r--r--tests/auto/qml/qmlformat/data/threeFunctions.formattedFuncSpacing.js11
-rw-r--r--tests/auto/qml/qmlformat/data/threeFunctions.formattedTabs.js9
-rw-r--r--tests/auto/qml/qmlformat/data/threeFunctions.formattedW2.js9
-rw-r--r--tests/auto/qml/qmlformat/data/threeFunctionsOneLine.js1
-rw-r--r--tests/auto/qml/qmlformat/data/twoFunctions.formatted.js12
-rw-r--r--tests/auto/qml/qmlformat/data/twoFunctions.js3
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp309
-rw-r--r--tests/auto/qml/qmlimportscanner/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Drawer.qml.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Imports.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Simple.qml.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Singleton.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Things.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/localImport.qml.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/rootPath.json4
-rw-r--r--tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp4
-rw-r--r--tests/auto/qml/qmllint/CMakeLists.txt12
-rw-r--r--tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml5
-rw-r--r--tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir2
-rw-r--r--tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml14
-rw-r--r--tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes31
-rw-r--r--tests/auto/qml/qmllint/data/LocaleTest/qmldir3
-rw-r--r--tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml5
-rw-r--r--tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc10
-rw-r--r--tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml5
-rw-r--r--tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml5
-rw-r--r--tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir1
-rw-r--r--tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir1
-rw-r--r--tests/auto/qml/qmllint/data/MultiDirectory/qmldir5
-rw-r--r--tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes112
-rw-r--r--tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml12
-rw-r--r--tests/auto/qml/qmllint/data/MyStyle/qmldir4
-rw-r--r--tests/auto/qml/qmllint/data/NeedImportPath.qml5
-rw-r--r--tests/auto/qml/qmllint/data/Qtbug111015/qmldir3
-rw-r--r--tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes12
-rw-r--r--tests/auto/qml/qmllint/data/SharedFunctions.js5
-rw-r--r--tests/auto/qml/qmllint/data/StringToDateTime/qmldir3
-rw-r--r--tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes14
-rw-r--r--tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes20
-rw-r--r--tests/auto/qml/qmllint/data/Things/plugins.qmltypes20
-rw-r--r--tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml12
-rw-r--r--tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml12
-rw-r--r--tests/auto/qml/qmllint/data/addressableValue.qml8
-rw-r--r--tests/auto/qml/qmllint/data/attachedImportUse.qml7
-rw-r--r--tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml8
-rw-r--r--tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes513
-rw-r--r--tests/auto/qml/qmllint/data/coercetovoid.qml8
-rw-r--r--tests/auto/qml/qmllint/data/customParser.qml4
-rw-r--r--tests/auto/qml/qmllint/data/dontCheckJSTypes.qml11
-rw-r--r--tests/auto/qml/qmllint/data/findMemberPrint.qml13
-rw-r--r--tests/auto/qml/qmllint/data/generalizedGroupHint.qml23
-rw-r--r--tests/auto/qml/qmllint/data/groupedAttachedLayout.qml20
-rw-r--r--tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml11
-rw-r--r--tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir3
-rw-r--r--tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc6
-rw-r--r--tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc6
-rw-r--r--tests/auto/qml/qmllint/data/importNonexistentFile.qml3
-rw-r--r--tests/auto/qml/qmllint/data/importNullDevice.qml3
-rw-r--r--tests/auto/qml/qmllint/data/initReadonly.qml1
-rw-r--r--tests/auto/qml/qmllint/data/inlineComponent.qml1
-rw-r--r--tests/auto/qml/qmllint/data/invalidIdLookup.qml10
-rw-r--r--tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml8
-rw-r--r--tests/auto/qml/qmllint/data/locale.qml6
-rw-r--r--tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml9
-rw-r--r--tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml9
-rw-r--r--tests/auto/qml/qmllint/data/multifix.fixed.qml14
-rw-r--r--tests/auto/qml/qmllint/data/multifix.qml13
-rw-r--r--tests/auto/qml/qmllint/data/notQmlRootMethods.qml8
-rw-r--r--tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml1
-rw-r--r--tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml8
-rw-r--r--tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml19
-rw-r--r--tests/auto/qml/qmllint/data/qEventPoint.qml9
-rw-r--r--tests/auto/qml/qmllint/data/qmlRootMethods.qml12
-rw-r--r--tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml2
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml9
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml5
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml6
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml7
-rw-r--r--tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml5
-rw-r--r--tests/auto/qml/qmllint/data/scriptInTemplate.qml6
-rw-r--r--tests/auto/qml/qmllint/data/scripts/Foo.js6
-rw-r--r--tests/auto/qml/qmllint/data/scripts/qmldir4
-rw-r--r--tests/auto/qml/qmllint/data/scripts/scripts.qmltypes22
-rw-r--r--tests/auto/qml/qmllint/data/scriptstring.qml73
-rw-r--r--tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini2
-rw-r--r--tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml7
-rw-r--r--tests/auto/qml/qmllint/data/storeNameMethod.qml12
-rw-r--r--tests/auto/qml/qmllint/data/stringToDateTime.qml7
-rw-r--r--tests/auto/qml/qmllint/data/untitled/components/Foo.qml5
-rw-r--r--tests/auto/qml/qmllint/data/untitled/main.qml9
-rw-r--r--tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc6
-rw-r--r--tests/auto/qml/qmllint/data/useConstInvokable.qml5
-rw-r--r--tests/auto/qml/qmllint/data/validLiterals.qml6
-rw-r--r--tests/auto/qml/qmllint/data/valueTypesFromString.qml7
-rw-r--r--tests/auto/qml/qmllint/data/variantMapLookup.qml13
-rw-r--r--tests/auto/qml/qmllint/data/writeListProperty.qml7
-rw-r--r--tests/auto/qml/qmllint/lintplugin.cpp48
-rw-r--r--tests/auto/qml/qmllint/lintplugin.h4
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp481
-rw-r--r--tests/auto/qml/qmlplugindump/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h2
-rw-r--r--tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp6
-rw-r--r--tests/auto/qml/qmlsplitlib/CMakeLists.txt24
-rw-r--r--tests/auto/qml/qmlsplitlib/lib.h2
-rw-r--r--tests/auto/qml/qmlsplitlib/lib2.cpp5
-rw-r--r--tests/auto/qml/qmlsplitlib/lib2.h30
-rw-r--r--tests/auto/qml/qmlsplitlib/main.qml2
-rw-r--r--tests/auto/qml/qmlsplitlib/main2.qml8
-rw-r--r--tests/auto/qml/qmlsplitlib/manual_imports.cpp2
-rw-r--r--tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp15
-rw-r--r--tests/auto/qml/qmltc/BLACKLIST5
-rw-r--r--tests/auto/qml/qmltc/CMakeLists.txt15
-rw-r--r--tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt3
-rw-r--r--tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/CMakeLists.txt28
-rw-r--r--tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/HelloExportedWorldNoFileName.qml5
-rw-r--r--tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt31
-rw-r--r--tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml5
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt21
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/Connections.qml12
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/QmlTableModel.qml62
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/aliases.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/custominitialization.h64
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithrequiredproperties.h75
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/myCheckBox.qml5
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/mySignals.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/properties.qml14
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/qtbug120700_main.qml25
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/qtbug123476.qml9
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/requiredProperties.qml61
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/signalConnections.qml46
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/singletons.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/stringToUrl.qml12
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/subfolder/code.js2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/translations.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/translationsById.qml2
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml2
-rw-r--r--tests/auto/qml/qmltc/nameconflict.cpp2
-rw-r--r--tests/auto/qml/qmltc/nameconflict.h2
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.cpp198
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.h14
-rw-r--r--tests/auto/qml/qmltc_manual/CMakeLists.txt9
-rw-r--r--tests/auto/qml/qmltc_manual/testclasses.h2
-rw-r--r--tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp2
-rw-r--r--tests/auto/qml/qmltc_qprocess/CMakeLists.txt17
-rw-r--r--tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h2
-rw-r--r--tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h27
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/QmlBaseFromAnotherModule.qml13
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml18
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml18
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/constructFromString.qml7
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml10
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml2
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml20
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml10
-rw-r--r--tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp126
-rw-r--r--tests/auto/qml/qmltyperegistrar/CMakeLists.txt28
-rw-r--r--tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt4
-rw-r--r--tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h51
-rw-r--r--tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h2
-rw-r--r--tests/auto/qml/qmltyperegistrar/duplicatedExports.h2
-rw-r--r--tests/auto/qml/qmltyperegistrar/enum.cpp5
-rw-r--r--tests/auto/qml/qmltyperegistrar/enum.h40
-rw-r--r--tests/auto/qml/qmltyperegistrar/foo.cpp2
-rw-r--r--tests/auto/qml/qmltyperegistrar/foo.h2
-rw-r--r--tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt3
-rw-r--r--tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp2
-rw-r--r--tests/auto/qml/qmltyperegistrar/foreign/foreign.h2
-rw-r--r--tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h19
-rw-r--r--tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h18
-rw-r--r--tests/auto/qml/qmltyperegistrar/hppheader.hpp2
-rw-r--r--tests/auto/qml/qmltyperegistrar/missingTypes.json167
-rw-r--r--tests/auto/qml/qmltyperegistrar/noextheader2
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp579
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h343
-rw-r--r--tests/auto/qml/qqmlanybinding/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp4
-rw-r--r--tests/auto/qml/qqmlanybinding/withbindable.h2
-rw-r--r--tests/auto/qml/qqmlapplicationengine/CMakeLists.txt13
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp2
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/CMakeLists.txt30
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/Main.qml5
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.qmbin0 -> 92 bytes
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.ts12
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/main.cpp26
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/CMakeLists.txt30
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/Main.qml13
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.qmbin0 -> 91 bytes
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.ts12
-rw-r--r--tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/main.cpp23
-rw-r--r--tests/auto/qml/qqmlapplicationengine/testapp/main.cpp2
-rw-r--r--tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp59
-rw-r--r--tests/auto/qml/qqmlbinding/CMakeLists.txt40
-rw-r--r--tests/auto/qml/qqmlbinding/WithBindableProperties.h2
-rw-r--r--tests/auto/qml/qqmlbinding/data/bindingOverwriting2.qml21
-rw-r--r--tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml18
-rw-r--r--tests/auto/qml/qqmlbinding/data/restoreBinding2.qml2
-rw-r--r--tests/auto/qml/qqmlbinding/data/restoreBinding3.qml2
-rw-r--r--tests/auto/qml/qqmlbinding/data/restoreBinding4.qml2
-rw-r--r--tests/auto/qml/qqmlbinding/data/toggleEnableProperlyRemembersValues.qml13
-rw-r--r--tests/auto/qml/qqmlbinding/data/whenEvaluatedEarlyEnough.qml23
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp69
-rw-r--r--tests/auto/qml/qqmlchangeset/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp39
-rw-r--r--tests/auto/qml/qqmlcomponent/CMakeLists.txt11
-rw-r--r--tests/auto/qml/qqmlcomponent/data/complexObjectArgument.qml28
-rw-r--r--tests/auto/qml/qqmlcomponent/data/createObject.qml3
-rw-r--r--tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml14
-rw-r--r--tests/auto/qml/qqmlcomponent/data/createQmlObject.qml21
-rw-r--r--tests/auto/qml/qqmlcomponent/data/dynamic.qml6
-rw-r--r--tests/auto/qml/qqmlcomponent/data/removeBinding.qml32
-rw-r--r--tests/auto/qml/qqmlcomponent/lifecyclewatcher.h24
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp186
-rw-r--r--tests/auto/qml/qqmlconnections/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlconnections/data/badSignalHandlerName.qml15
-rw-r--r--tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp83
-rw-r--r--tests/auto/qml/qqmlconsole/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlconsole/data/assert.qml2
-rw-r--r--tests/auto/qml/qqmlconsole/data/categorized_logging.qml2
-rw-r--r--tests/auto/qml/qqmlconsole/data/exception.qml2
-rw-r--r--tests/auto/qml/qqmlconsole/data/logging.qml2
-rw-r--r--tests/auto/qml/qqmlconsole/data/profiling.qml2
-rw-r--r--tests/auto/qml/qqmlconsole/data/tracing.qml2
-rw-r--r--tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp35
-rw-r--r--tests/auto/qml/qqmlcontext/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlcontext/data/A.qml6
-rw-r--r--tests/auto/qml/qqmlcontext/data/B.qml6
-rw-r--r--tests/auto/qml/qqmlcontext/data/C.qml6
-rw-r--r--tests/auto/qml/qqmlcontext/data/destroyContextObject.qml5
-rw-r--r--tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml5
-rw-r--r--tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp229
-rw-r--r--tests/auto/qml/qqmlcpputils/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp21
-rw-r--r--tests/auto/qml/qqmldelegatemodel/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml2
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/clearCacheDuringInsertion.qml138
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml50
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/integerModel.qml2
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/listModel.qml2
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/modifyObjectUnderConstruction.qml18
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/overriddenModelData.qml55
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/persistedItemsCache.qml62
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/requiredModelData.qml50
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/resetModelData.qml16
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml64
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/universalModelData.qml72
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/viewUpdatedOnDelegateChoiceAffectingRoleChange.qml93
-rw-r--r--tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp411
-rw-r--r--tests/auto/qml/qqmldirparser/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmldirparser/data/versioned-internal/qmldir3
-rw-r--r--tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp24
-rw-r--r--tests/auto/qml/qqmlecmascript/CMakeLists.txt7
-rw-r--r--tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml20
-rw-r--r--tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml13
-rw-r--r--tests/auto/qml/qqmlecmascript/data/date.qml6
-rw-r--r--tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml30
-rw-r--r--tests/auto/qml/qqmlecmascript/data/getThis.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml6
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml8
-rw-r--r--tests/auto/qml/qqmlecmascript/data/resetGadget.qml9
-rw-r--r--tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml20
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml21
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml30
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml36
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml21
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml19
-rw-r--r--tests/auto/qml/qqmlecmascript/data/singletonTest.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/singletonTest2.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.cpp59
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h217
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp395
-rw-r--r--tests/auto/qml/qqmlengine/CMakeLists.txt7
-rw-r--r--tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml19
-rw-r--r--tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/Main.qml5
-rw-r--r--tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonA.qml11
-rw-r--r--tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonB.qml11
-rw-r--r--tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/TestItem.qml5
-rw-r--r--tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/qmldir3
-rw-r--r--tests/auto/qml/qqmlengine/data/markCurrentFunctionAsTranslationBinding.qml7
-rw-r--r--tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs2
-rw-r--r--tests/auto/qml/qqmlengine/data/nativeModuleImport.qml2
-rw-r--r--tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml18
-rw-r--r--tests/auto/qml/qqmlengine/declarativelyregistered.cpp2
-rw-r--r--tests/auto/qml/qqmlengine/declarativelyregistered.h2
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp146
-rw-r--r--tests/auto/qml/qqmlengine/variantlistQJsonConversion.h53
-rw-r--r--tests/auto/qml/qqmlenginecleanup/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml2
-rw-r--r--tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp2
-rw-r--r--tests/auto/qml/qqmlenginecleanup/data/TestType.qml2
-rw-r--r--tests/auto/qml/qqmlenginecleanup/data/testFile1.qml2
-rw-r--r--tests/auto/qml/qqmlenginecleanup/data/testFile2.qml2
-rw-r--r--tests/auto/qml/qqmlenginecleanup/data/testFile3.qml2
-rw-r--r--tests/auto/qml/qqmlenginecleanup/data/types.qml2
-rw-r--r--tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp29
-rw-r--r--tests/auto/qml/qqmlerror/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlerror/tst_qqmlerror.cpp2
-rw-r--r--tests/auto/qml/qqmlexpression/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp38
-rw-r--r--tests/auto/qml/qqmlextensionplugin/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlextensionplugin/data/dummy.qml2
-rw-r--r--tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp4
-rw-r--r--tests/auto/qml/qqmlfile/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlfile/tst_qqmlfile.cpp2
-rw-r--r--tests/auto/qml/qqmlfileselector/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml6
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/Name.js1
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/Name.js1
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/Name.js1
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir3
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/MyButton.qml7
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/Name.js1
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/MyButton.qml7
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/Name.js1
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest2/MyButton.qml7
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest2/Name.js1
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest2/main.qml9
-rw-r--r--tests/auto/qml/qqmlfileselector/data/qmldirtest2/qmldir5
-rw-r--r--tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp45
-rw-r--r--tests/auto/qml/qqmlglobal/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp2
-rw-r--r--tests/auto/qml/qqmlimport/CMakeLists.txt14
-rw-r--r--tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml2
-rw-r--r--tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml2
-rw-r--r--tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/Preferred.qml5
-rw-r--r--tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/qmldir3
-rw-r--r--tests/auto/qml/qqmlimport/data/MyModuleName/Font.js4
-rw-r--r--tests/auto/qml/qqmlimport/data/MyModuleName/qmldir2
-rw-r--r--tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml6
-rw-r--r--tests/auto/qml/qqmlimport/data/noimport/Main.qml5
-rw-r--r--tests/auto/qml/qqmlimport/data/noimport/qmldir3
-rw-r--r--tests/auto/qml/qqmlimport/data/prefer2.qml3
-rw-r--r--tests/auto/qml/qqmlimport/data/qualifiedScriptImport.qml8
-rw-r--r--tests/auto/qml/qqmlimport/data/testfile_supported.qml2
-rw-r--r--tests/auto/qml/qqmlimport/data/testfile_unsupported.qml2
-rw-r--r--tests/auto/qml/qqmlimport/qmldir2
-rw-r--r--tests/auto/qml/qqmlimport/tst_qqmlimport.cpp138
-rw-r--r--tests/auto/qml/qqmlincubator/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlincubator/data/garbageCollection2.qml12
-rw-r--r--tests/auto/qml/qqmlincubator/testtypes.cpp2
-rw-r--r--tests/auto/qml/qqmlincubator/testtypes.h2
-rw-r--r--tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp12
-rw-r--r--tests/auto/qml/qqmlinfo/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlinfo/attached.cpp2
-rw-r--r--tests/auto/qml/qqmlinfo/attached.h2
-rw-r--r--tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp4
-rw-r--r--tests/auto/qml/qqmlinstantiator/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlinstantiator/data/listDataDestruction.qml20
-rw-r--r--tests/auto/qml/qqmlinstantiator/data/removeDuringModelChange.qml10
-rw-r--r--tests/auto/qml/qqmlinstantiator/stringmodel.h2
-rw-r--r--tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp103
-rw-r--r--tests/auto/qml/qqmlitemmodels/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlitemmodels/data/modelindex.qml2
-rw-r--r--tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml2
-rw-r--r--tests/auto/qml/qqmlitemmodels/qtestmodel.h7
-rw-r--r--tests/auto/qml/qqmlitemmodels/testtypes.h2
-rw-r--r--tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp4
-rw-r--r--tests/auto/qml/qqmljsscope/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt3
-rw-r--r--tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h23
-rw-r--r--tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp2
-rw-r--r--tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h2
-rw-r--r--tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h2
-rw-r--r--tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h2
-rw-r--r--tests/auto/qml/qqmljsscope/data/attachedTypeResolution.qml6
-rw-r--r--tests/auto/qml/qqmljsscope/data/methodAndSignalSourceLocation.qml16
-rw-r--r--tests/auto/qml/qqmljsscope/data/ownModuleName.qml10
-rw-r--r--tests/auto/qml/qqmljsscope/data/requiredAlias.qml20
-rw-r--r--tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp329
-rw-r--r--tests/auto/qml/qqmllanguage/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmllanguage/data/AliasHolder.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnumSelfReference.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/data/Comps/IconPropertiesGroup.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/Comps/OverlayDrawer.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/Comps/qmldir4
-rw-r--r--tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml27
-rw-r--r--tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle1.qml15
-rw-r--r--tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle2.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/UIToolBar.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.16.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/aliasWriter.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/ambiguousComponents.qml16
-rw-r--r--tests/auto/qml/qqmllanguage/data/asCastToInlineComponent.qml16
-rw-r--r--tests/auto/qml/qqmllanguage/data/asValueType.qml13
-rw-r--r--tests/auto/qml/qqmllanguage/data/badICAnnotation.qml25
-rw-r--r--tests/auto/qml/qqmllanguage/data/badSingleton/SingletonTest.qml (renamed from tests/auto/qml/qqmllanguage/data/SingletonTest.qml)0
-rw-r--r--tests/auto/qml/qqmllanguage/data/badSingleton/qmldir (renamed from tests/auto/qml/qqmllanguage/data/qmldir)0
-rw-r--r--tests/auto/qml/qqmllanguage/data/badSingleton/qtbug_85932.qml (renamed from tests/auto/qml/qqmllanguage/data/qtbug_85932.qml)0
-rw-r--r--tests/auto/qml/qqmllanguage/data/corpseInQmlList.qml13
-rw-r--r--tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml9
-rw-r--r--tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/derivedFromUnexposedBase.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/dynamicGroupPropertyRejected.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/enumPropsManyUnderlyingTypes.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/enumScopes.qml16
-rw-r--r--tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml27
-rw-r--r--tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/isNullOrUndefined_interpreter.qml22
-rw-r--r--tests/auto/qml/qqmllanguage/data/isNullOrUndefined_jit.qml22
-rw-r--r--tests/auto/qml/qqmllanguage/data/jitExceptions.qml16
-rw-r--r--tests/auto/qml/qqmllanguage/data/longConversion.qml28
-rw-r--r--tests/auto/qml/qqmllanguage/data/manuallyCallSignalHandler.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/objectInList.qml17
-rw-r--r--tests/auto/qml/qqmllanguage/data/objectMethodClone.qml23
-rw-r--r--tests/auto/qml/qqmllanguage/data/optionalChainCallOnNullProperty.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/retainThis.qml49
-rw-r--r--tests/auto/qml/qqmllanguage/data/signatureEnforced.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/signatureIgnored.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js2
-rw-r--r--tests/auto/qml/qqmllanguage/data/singletonTest17.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/typedObjectList.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/unregisteredValueTypeConversion.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/variantObjectList.qml17
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp46
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h539
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp900
-rw-r--r--tests/auto/qml/qqmllistcompositor/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp21
-rw-r--r--tests/auto/qml/qqmllistmodel/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmllistmodel/data/nestedLists.qml37
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp255
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp160
-rw-r--r--tests/auto/qml/qqmllistreference/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmllistreference/data/consoleLogSyntheticList.qml8
-rw-r--r--tests/auto/qml/qqmllistreference/data/listIgnoresNull.qml21
-rw-r--r--tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp62
-rw-r--r--tests/auto/qml/qqmllocale/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmllocale/tst_qqmllocale.cpp31
-rw-r--r--tests/auto/qml/qqmlmetaobject/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp2
-rw-r--r--tests/auto/qml/qqmlmetatype/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlmetatype/data/Components/App.qml2
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp114
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp89
-rw-r--r--tests/auto/qml/qqmlnotifier/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp2
-rw-r--r--tests/auto/qml/qqmlobjectmodel/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp2
-rw-r--r--tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp2
-rw-r--r--tests/auto/qml/qqmlparser/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlparser/tst_qqmlparser.cpp88
-rw-r--r--tests/auto/qml/qqmlpromise/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-get-length.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml2
-rw-r--r--tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp2
-rw-r--r--tests/auto/qml/qqmlproperty/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml50
-rw-r--r--tests/auto/qml/qqmlproperty/data/listAssignmentSignals.qml13
-rw-r--r--tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml9
-rw-r--r--tests/auto/qml/qqmlproperty/interfaces.h2
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp91
-rw-r--r--tests/auto/qml/qqmlpropertycache/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlpropertycache/data/duplicateIdsAndGeneralizedGroupProperties.qml64
-rw-r--r--tests/auto/qml/qqmlpropertycache/data/overriddenSignal.qml36
-rw-r--r--tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal.qml8
-rw-r--r--tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal2.qml6
-rw-r--r--tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp84
-rw-r--r--tests/auto/qml/qqmlpropertymap/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp29
-rw-r--r--tests/auto/qml/qqmlqt/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlqt/tst_qqmlqt.cpp20
-rw-r--r--tests/auto/qml/qqmlsettings/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlsettings/data/aliases.qml2
-rw-r--r--tests/auto/qml/qqmlsettings/data/basic.qml2
-rw-r--r--tests/auto/qml/qqmlsettings/data/categories.qml2
-rw-r--r--tests/auto/qml/qqmlsettings/data/cpp-aliases.qml2
-rw-r--r--tests/auto/qml/qqmlsettings/data/siblings.qml2
-rw-r--r--tests/auto/qml/qqmlsettings/data/types.qml2
-rw-r--r--tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp2
-rw-r--r--tests/auto/qml/qqmlsqldatabase/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml2
-rw-r--r--tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp7
-rw-r--r--tests/auto/qml/qqmltablemodel/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestModel.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestUtils.js2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/common.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/complex.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/empty.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/intAndDouble.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml2
-rw-r--r--tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp2
-rw-r--r--tests/auto/qml/qqmltimer/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmltimer/tst_qqmltimer.cpp77
-rw-r--r--tests/auto/qml/qqmltranslation/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmltranslation/data/jstranslation.qml1
-rw-r--r--tests/auto/qml/qqmltranslation/data/pragmacontext.qml8
-rw-r--r--tests/auto/qml/qqmltranslation/data/pragmacontextstringliteral.qml8
-rw-r--r--tests/auto/qml/qqmltranslation/data/translation.js6
-rw-r--r--tests/auto/qml/qqmltranslation/data/translation.qml4
-rw-r--r--tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp65
-rw-r--r--tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp30
-rw-r--r--tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h3
-rw-r--r--tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp2
-rw-r--r--tests/auto/qml/qqmltypeloader/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp2
-rw-r--r--tests/auto/qml/qqmltypeloader/SlowImport/plugin.h2
-rw-r--r--tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp2
-rw-r--r--tests/auto/qml/qqmltypeloader/SlowImport/slow.h2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/GenericView.qml2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Intercept.qml2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/NiceView.qml2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/doesExist.qml2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/a.qml3
-rw-r--r--tests/auto/qml/qqmltypeloader/data/load_synchronous.qml2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/test_intercept.qml2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/test_load_complete.qml2
-rw-r--r--tests/auto/qml/qqmltypeloader/declarativetesttype.h2
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp68
-rw-r--r--tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml2
-rw-r--r--tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml5
-rw-r--r--tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml17
-rw-r--r--tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp7
-rw-r--r--tests/auto/qml/qqmlvaluetypeproviders/testtypes.h216
-rw-r--r--tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp191
-rw-r--r--tests/auto/qml/qqmlvaluetypes/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlvaluetypes/testtypes.cpp2
-rw-r--r--tests/auto/qml/qqmlvaluetypes/testtypes.h2
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp520
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect18
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect16
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml2
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml2
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml2
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/abort.expect16
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/abort.qml15
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml8
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml9
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml12
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect12
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml19
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js4
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/open.qml9
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect12
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml1
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml9
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml2
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.qml20
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.reply3
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml3
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect14
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect14
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml5
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml21
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml4
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect16
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml5
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect16
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expectbin258 -> 263 bytes
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml5
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml6
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml6
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect16
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml6
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml6
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect16
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml5
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml6
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect14
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect14
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml11
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect12
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect12
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect12
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect19
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml3
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect16
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml5
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml8
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml10
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml3
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/status.expect12
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/status.qml4
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml4
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/text.expect6
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp107
-rw-r--r--tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp18
-rw-r--r--tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp2
-rw-r--r--tests/auto/qml/qquickworkerscript/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp103
-rw-r--r--tests/auto/qml/qrcqml/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qrcqml/tst_qrcqml.cpp12
-rw-r--r--tests/auto/qml/qtqmlmodules/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp20
-rw-r--r--tests/auto/qml/qv4assembler/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qv4assembler/data/crash.qml2
-rw-r--r--tests/auto/qml/qv4assembler/tst_qv4assembler.cpp2
-rw-r--r--tests/auto/qml/qv4estable/CMakeLists.txt24
-rw-r--r--tests/auto/qml/qv4estable/tst_qv4estable.cpp40
-rw-r--r--tests/auto/qml/qv4identifiertable/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp2
-rw-r--r--tests/auto/qml/qv4mm/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qv4mm/data/createdestroy.qml2
-rw-r--r--tests/auto/qml/qv4mm/data/createobjects.qml2
-rw-r--r--tests/auto/qml/qv4mm/data/simpleObject.qml3
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp316
-rw-r--r--tests/auto/qml/qv4regexp/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qv4regexp/tst_qv4regexp.cpp2
-rw-r--r--tests/auto/qml/qv4urlobject/CMakeLists.txt26
-rw-r--r--tests/auto/qml/qv4urlobject/tst_qv4urlobject.cpp153
-rw-r--r--tests/auto/qml/qwidgetsinqml/CMakeLists.txt8
-rw-r--r--tests/auto/qml/qwidgetsinqml/dummy_imports.qml7
-rw-r--r--tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp19
-rw-r--r--tests/auto/qml/registrationmacros/CMakeLists.txt6
-rw-r--r--tests/auto/qml/registrationmacros/tst_registrationmacros.cpp2
-rw-r--r--tests/auto/qml/registrationmacros/types.cpp2
-rw-r--r--tests/auto/qml/registrationmacros/types.h2
-rw-r--r--tests/auto/qml/v4misc/CMakeLists.txt6
-rw-r--r--tests/auto/qml/v4misc/tst_v4misc.cpp2
-rw-r--r--tests/auto/qmldom/CMakeLists.txt1
-rw-r--r--tests/auto/qmldom/combined/CMakeLists.txt8
-rw-r--r--tests/auto/qmldom/combined/tst_dom_all.cpp7
-rw-r--r--tests/auto/qmldom/domdata/domitem/Base.qml5
-rw-r--r--tests/auto/qmldom/domdata/domitem/Derived.qml5
-rw-r--r--tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml6
-rw-r--r--tests/auto/qmldom/domdata/domitem/WithImplicitImport.qml5
-rw-r--r--tests/auto/qmldom/domdata/domitem/Yyy.qml35
-rw-r--r--tests/auto/qmldom/domdata/domitem/aliasProperties.qml2
-rw-r--r--tests/auto/qmldom/domdata/domitem/astComments.qml12
-rw-r--r--tests/auto/qmldom/domdata/domitem/attachedOrGroupedProperties.qml34
-rw-r--r--tests/auto/qmldom/domdata/domitem/breakStatement.qml9
-rw-r--r--tests/auto/qmldom/domdata/domitem/callExpressions.qml61
-rw-r--r--tests/auto/qmldom/domdata/domitem/checkScopes.qml11
-rw-r--r--tests/auto/qmldom/domdata/domitem/commaExpression.qml8
-rw-r--r--tests/auto/qmldom/domdata/domitem/conditionalExpression.qml8
-rw-r--r--tests/auto/qmldom/domdata/domitem/continueStatement.qml9
-rw-r--r--tests/auto/qmldom/domdata/domitem/crashes/lambda.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/crashes/templateStrings.qml10
-rw-r--r--tests/auto/qmldom/domdata/domitem/ecmaScriptClass.qml44
-rw-r--r--tests/auto/qmldom/domdata/domitem/emptyMethodBody.qml6
-rw-r--r--tests/auto/qmldom/domdata/domitem/enumDeclarations.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/fieldMemberExpression.qml32
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegion.qml8
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/comments.qml12
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/enums.qml12
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/functions.qml15
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/imports.qml11
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/pragmas.qml11
-rw-r--r--tests/auto/qmldom/domdata/domitem/finalizeScriptExpressions.qml29
-rw-r--r--tests/auto/qmldom/domdata/domitem/forStatements.qml15
-rw-r--r--tests/auto/qmldom/domdata/domitem/ifStatements.qml27
-rw-r--r--tests/auto/qmldom/domdata/domitem/import.js2
-rw-r--r--tests/auto/qmldom/domdata/domitem/inactiveVisitorMarkerCrash.qml13
-rw-r--r--tests/auto/qmldom/domdata/domitem/inlineComponents.qml17
-rw-r--r--tests/auto/qmldom/domdata/domitem/inlineObject.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml2
-rw-r--r--tests/auto/qmldom/domdata/domitem/iterationStatements.qml64
-rw-r--r--tests/auto/qmldom/domdata/domitem/module.mjs6
-rw-r--r--tests/auto/qmldom/domdata/domitem/nullStatements.qml14
-rw-r--r--tests/auto/qmldom/domdata/domitem/objectBindings.qml13
-rw-r--r--tests/auto/qmldom/domdata/domitem/propertyBindings.qml13
-rw-r--r--tests/auto/qmldom/domdata/domitem/returnStatements.qml14
-rw-r--r--tests/auto/qmldom/domdata/domitem/simplestJSStatement.js1
-rw-r--r--tests/auto/qmldom/domdata/domitem/simplestJSmodule.mjs1
-rw-r--r--tests/auto/qmldom/domdata/domitem/switchStatement.qml28
-rw-r--r--tests/auto/qmldom/domdata/domitem/tryStatements.qml10
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/decrement.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/delete.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/increment.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/not.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/postDecrement.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/postIncrement.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/tilde.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/typeof.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryMinus.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryPlus.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/void.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/variableDeclarations.qml23
-rw-r--r--tests/auto/qmldom/domdata/domitem/visitTreeFilter.qml4
-rw-r--r--tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml5
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml4
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Unindented.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml4
-rw-r--r--tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml2
-rw-r--r--tests/auto/qmldom/domitem/CMakeLists.txt7
-rw-r--r--tests/auto/qmldom/domitem/tst_qmldomitem.cpp2
-rw-r--r--tests/auto/qmldom/domitem/tst_qmldomitem.h2783
-rw-r--r--tests/auto/qmldom/errormessage/CMakeLists.txt6
-rw-r--r--tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp2
-rw-r--r--tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h2
-rw-r--r--tests/auto/qmldom/merging/CMakeLists.txt7
-rw-r--r--tests/auto/qmldom/merging/tst_dommerging.cpp2
-rw-r--r--tests/auto/qmldom/merging/tst_dommerging.h15
-rw-r--r--tests/auto/qmldom/path/CMakeLists.txt6
-rw-r--r--tests/auto/qmldom/path/tst_qmldompath.cpp2
-rw-r--r--tests/auto/qmldom/path/tst_qmldompath.h18
-rw-r--r--tests/auto/qmldom/reformatter/CMakeLists.txt7
-rw-r--r--tests/auto/qmldom/reformatter/tst_reformatter.cpp2
-rw-r--r--tests/auto/qmldom/reformatter/tst_reformatter.h410
-rw-r--r--tests/auto/qmldom/standalone/CMakeLists.txt39
-rw-r--r--tests/auto/qmldom/standalone/tst_standalone.cpp5
-rw-r--r--tests/auto/qmldom/standalone/tst_standalone.h634
-rw-r--r--tests/auto/qmldom/stringdumper/CMakeLists.txt6
-rw-r--r--tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp2
-rw-r--r--tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h2
-rw-r--r--tests/auto/qmlls/CMakeLists.txt10
-rw-r--r--tests/auto/qmlls/cli/CMakeLists.txt34
-rw-r--r--tests/auto/qmlls/cli/data/ImportPath1/SomeModule/A.qml5
-rw-r--r--tests/auto/qmlls/cli/data/ImportPath1/SomeModule/qmldir2
-rw-r--r--tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/B.qml5
-rw-r--r--tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/qmldir2
-rw-r--r--tests/auto/qmlls/cli/data/sourceFolder/ImportFromBothPaths.qml10
-rw-r--r--tests/auto/qmlls/cli/data/sourceFolder/ImportFromImportPath1.qml5
-rw-r--r--tests/auto/qmlls/cli/tst_qmlls_cli.cpp309
-rw-r--r--tests/auto/qmlls/cli/tst_qmlls_cli.h38
-rw-r--r--tests/auto/qmlls/completions/CMakeLists.txt27
-rw-r--r--tests/auto/qmlls/completions/data/buildDir/BuildDir/qmldir2
-rw-r--r--tests/auto/qmlls/completions/data/completions/Yyy.qml29
-rw-r--r--tests/auto/qmlls/completions/data/completions/fromBuildDir.qml9
-rw-r--r--tests/auto/qmlls/completions/tst_qmllscompletions.cpp500
-rw-r--r--tests/auto/qmlls/lifecycle/CMakeLists.txt10
-rw-r--r--tests/auto/qmlls/lifecycle/qiopipe.cpp2
-rw-r--r--tests/auto/qmlls/lifecycle/qiopipe.h2
-rw-r--r--tests/auto/qmlls/lifecycle/tst_lifecycle.cpp4
-rw-r--r--tests/auto/qmlls/modules/CMakeLists.txt45
-rw-r--r--tests/auto/qmlls/modules/data/buildDir/BuildDir/BuildDirType.qml (renamed from tests/auto/qmlls/completions/data/buildDir/BuildDir/BuildDirType.qml)0
-rw-r--r--tests/auto/qmlls/modules/data/buildDir/BuildDir/qmldir3
-rw-r--r--tests/auto/qmlls/modules/data/completions/SomeBase.qml5
-rw-r--r--tests/auto/qmlls/modules/data/completions/Yyy.qml32
-rw-r--r--tests/auto/qmlls/modules/data/completions/Zzz.qml (renamed from tests/auto/qmlls/completions/data/completions/Zzz.qml)0
-rw-r--r--tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml14
-rw-r--r--tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml14
-rw-r--r--tests/auto/qmlls/modules/data/completions/fromBuildDir.qml10
-rw-r--r--tests/auto/qmlls/modules/data/findDefinition/jsDefinitions.qml17
-rw-r--r--tests/auto/qmlls/modules/data/findUsages/jsIdentifierUsages.qml13
-rw-r--r--tests/auto/qmlls/modules/data/formatting/blanklines.formatted.qml4
-rw-r--r--tests/auto/qmlls/modules/data/formatting/blanklines.qml15
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted1.qml23
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted2.qml20
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted3.qml23
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted4.qml20
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted5.qml20
-rw-r--r--tests/auto/qmlls/modules/data/formatting/rangeFormatting.qml23
-rw-r--r--tests/auto/qmlls/modules/data/highlighting/basic.qml11
-rw-r--r--tests/auto/qmlls/modules/data/highlighting/bigFile.qml351
-rw-r--r--tests/auto/qmlls/modules/data/hover/test.qml8
-rw-r--r--tests/auto/qmlls/modules/data/linting/SimpleItem.qml6
-rw-r--r--tests/auto/qmlls/modules/data/quickfixes/INeedAQuickFix.qml19
-rw-r--r--tests/auto/qmlls/modules/data/sourceDir/A.qml5
-rw-r--r--tests/auto/qmlls/modules/data/sourceDir/Main.qml6
-rw-r--r--tests/auto/qmlls/modules/data/sourceDir/qmldir3
-rw-r--r--tests/auto/qmlls/modules/tst_qmlls_modules.cpp1696
-rw-r--r--tests/auto/qmlls/modules/tst_qmlls_modules.h85
-rw-r--r--tests/auto/qmlls/qmlls/CMakeLists.txt12
-rw-r--r--tests/auto/qmlls/qmlls/tst_qmlls.cpp13
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/CMakeLists.txt33
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/Main.qml5
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/mycppmodule.qmltypes36
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/qmldir5
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/Main.qml13
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.cpp8
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.h29
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.cpp10
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.h21
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.qml8
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.cpp10
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.hpp18
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.cpp9
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.h14
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.cpp10
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.h18
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.cpp10
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.h21
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.txt0
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.cpp131
-rw-r--r--tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.h35
-rw-r--r--tests/auto/qmlls/utils/CMakeLists.txt48
-rw-r--r--tests/auto/qmlls/utils/data/BaseType.qml28
-rw-r--r--tests/auto/qmlls/utils/data/JSDefinitions.qml76
-rw-r--r--tests/auto/qmlls/utils/data/JSUsages.qml196
-rw-r--r--tests/auto/qmlls/utils/data/JSUsagesFromAnotherFile.qml16
-rw-r--r--tests/auto/qmlls/utils/data/Type.qml30
-rw-r--r--tests/auto/qmlls/utils/data/Yyy.qml143
-rw-r--r--tests/auto/qmlls/utils/data/Zzz.qml12
-rw-r--r--tests/auto/qmlls/utils/data/completions/afterDots.qml20
-rw-r--r--tests/auto/qmlls/utils/data/completions/attachedAndGroupedProperty.qml12
-rw-r--r--tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml8
-rw-r--r--tests/auto/qmlls/utils/data/completions/boundComponents.qml21
-rw-r--r--tests/auto/qmlls/utils/data/completions/commaExpression.qml7
-rw-r--r--tests/auto/qmlls/utils/data/completions/conditionalExpression.qml7
-rw-r--r--tests/auto/qmlls/utils/data/completions/continueAndBreakStatement.qml31
-rw-r--r--tests/auto/qmlls/utils/data/completions/functionBody.qml13
-rw-r--r--tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml8
-rw-r--r--tests/auto/qmlls/utils/data/completions/labelledStatement.qml20
-rw-r--r--tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml9
-rw-r--r--tests/auto/qmlls/utils/data/completions/missingRHS.qml19
-rw-r--r--tests/auto/qmlls/utils/data/completions/parenthesizedExpression.qml10
-rw-r--r--tests/auto/qmlls/utils/data/completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml11
-rw-r--r--tests/auto/qmlls/utils/data/completions/returnStatement.qml10
-rw-r--r--tests/auto/qmlls/utils/data/completions/suggestContinueAndBreak.qml54
-rw-r--r--tests/auto/qmlls/utils/data/completions/switchStatements.qml37
-rw-r--r--tests/auto/qmlls/utils/data/completions/throwStatement.qml11
-rw-r--r--tests/auto/qmlls/utils/data/completions/tryStatements.qml7
-rw-r--r--tests/auto/qmlls/utils/data/completions/unaryExpression.qml17
-rw-r--r--tests/auto/qmlls/utils/data/completions/variableDeclaration.qml36
-rw-r--r--tests/auto/qmlls/utils/data/emptyFile.qml0
-rw-r--r--tests/auto/qmlls/utils/data/file1.qml55
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/attachedPropertyUsage/attachedPropertyUsage.qml14
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/binding/binding.qml11
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/bindings/bindings.qml39
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/connections/connections.qml37
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/enums/Enums.qml27
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/enums/EnumsFromAnotherFile.qml5
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/fontFamilyUsage.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/groupPropertyUsage.qml35
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/idUsages/idUsages.qml19
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/inlineComponents/InlineComponentProvider.qml23
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents.qml26
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents2.qml9
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/jsIdentifier/jsIdentifier.qml16
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml16
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/property/PropertyFromAnotherFile.qml5
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/property/property.qml48
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyChanges/propertyChanges.qml35
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile2.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile3.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile4.qml6
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/propertyInNested/propertyInNested.qml56
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/recursive/RecursiveInOtherFile.qml10
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/recursive/recursive.qml28
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalAndHandlers2.qml43
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalsAndHandlers.qml40
-rw-r--r--tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml6
-rw-r--r--tests/auto/qmlls/utils/data/highlights/Identifiers.qml37
-rw-r--r--tests/auto/qmlls/utils/data/highlights/bindings.qml12
-rw-r--r--tests/auto/qmlls/utils/data/highlights/comments.qml19
-rw-r--r--tests/auto/qmlls/utils/data/highlights/enums.qml11
-rw-r--r--tests/auto/qmlls/utils/data/highlights/identifiers.qml37
-rw-r--r--tests/auto/qmlls/utils/data/highlights/imports.qml9
-rw-r--r--tests/auto/qmlls/utils/data/highlights/literals.qml14
-rw-r--r--tests/auto/qmlls/utils/data/highlights/methodAndSignal.qml11
-rw-r--r--tests/auto/qmlls/utils/data/highlights/objectAndComponent.qml11
-rw-r--r--tests/auto/qmlls/utils/data/highlights/pragmas.qml10
-rw-r--r--tests/auto/qmlls/utils/data/highlights/properties.qml13
-rw-r--r--tests/auto/qmlls/utils/data/highlights/scriptExpressions.qml116
-rw-r--r--tests/auto/qmlls/utils/data/pragmas.qml8
-rw-r--r--tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject-qt-5.html134
-rw-r--r--tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject.html129
-rw-r--r--tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-item.html1486
-rw-r--r--tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-mousearea.html676
-rw-r--r--tests/auto/qmlls/utils/data/qualifiedModule.qml7
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/BaseType.qml12
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/BindingsOnDeferred.qml19
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/Derived1.qml6
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/Derived2.qml6
-rw-r--r--tests/auto/qmlls/utils/data/resolveExpressionType/DerivedType.qml32
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp650
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_highlighting.h37
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_utils.cpp4026
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_utils.h98
-rw-r--r--tests/auto/qmlnetwork/CMakeLists.txt8
-rw-r--r--tests/auto/qmlnetwork/qqmlnetworkinformation/CMakeLists.txt39
-rw-r--r--tests/auto/qmlnetwork/qqmlnetworkinformation/data/tst_networkinformation.qml14
-rw-r--r--tests/auto/qmlnetwork/qqmlnetworkinformation/tst_qqmlnetworkinformation.cpp51
-rw-r--r--tests/auto/qmlnetwork/qqmlsslconfiguration/CMakeLists.txt43
-rw-r--r--tests/auto/qmlnetwork/qqmlsslconfiguration/data/cert.pem34
-rw-r--r--tests/auto/qmlnetwork/qqmlsslconfiguration/data/key.pem52
-rw-r--r--tests/auto/qmlnetwork/qqmlsslconfiguration/qml/tst_sslconfiguration.qml158
-rw-r--r--tests/auto/qmlnetwork/qqmlsslconfiguration/tst_sslconfiguration_qml.cpp6
-rw-r--r--tests/auto/qmlnetwork/qqmlsslkey/CMakeLists.txt29
-rw-r--r--tests/auto/qmlnetwork/qqmlsslkey/qml/tst_sslkey.qml60
-rw-r--r--tests/auto/qmlnetwork/qqmlsslkey/tst_sslkey_qml.cpp6
-rw-r--r--tests/auto/qmltest-blacklist/animators/Box.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_behavior.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_mixed.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_nested.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_on.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_opacity.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_parallel.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_restart.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_rotation.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_scale.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_sequential.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_stopped.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_transition.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_x.qml2
-rw-r--r--tests/auto/qmltest-blacklist/animators/tst_y.qml2
-rw-r--r--tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml2
-rw-r--r--tests/auto/qmltest-blacklist/shortcut/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest-blacklist/shortcut/tst_shortcut.qml2
-rw-r--r--tests/auto/qmltest/animatedimage/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/animatedimage/tst_animatedimage.qml2
-rw-r--r--tests/auto/qmltest/animations/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/animations/tst_abstractanimationjobcrash.qml2
-rw-r--r--tests/auto/qmltest/borderimage/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/borderimage/InvalidSciFile.qml2
-rw-r--r--tests/auto/qmltest/borderimage/tst_borderimage.qml2
-rw-r--r--tests/auto/qmltest/buttonclick/Button.qml2
-rw-r--r--tests/auto/qmltest/buttonclick/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/buttonclick/tst_buttonclick.qml2
-rw-r--r--tests/auto/qmltest/createbenchmark/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/createbenchmark/item.qml2
-rw-r--r--tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml2
-rw-r--r--tests/auto/qmltest/events/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/events/tst_drag.qml2
-rw-r--r--tests/auto/qmltest/events/tst_events.qml2
-rw-r--r--tests/auto/qmltest/events/tst_touch.qml2
-rw-r--r--tests/auto/qmltest/events/tst_wheel.qml2
-rw-r--r--tests/auto/qmltest/fontloader/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/fontloader/tst_fontloader.qml2
-rw-r--r--tests/auto/qmltest/fuzzycompare/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/fuzzycompare/tst_FuzzyCompare.qml2
-rw-r--r--tests/auto/qmltest/gradient/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/gradient/tst_gradient.qml2
-rw-r--r--tests/auto/qmltest/image/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/image/tst_image.qml2
-rw-r--r--tests/auto/qmltest/itemgrabber/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml2
-rw-r--r--tests/auto/qmltest/layout/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/layout/Container.qml2
-rw-r--r--tests/auto/qmltest/layout/ContainerUser.qml2
-rw-r--r--tests/auto/qmltest/layout/tst_layout.qml2
-rw-r--r--tests/auto/qmltest/listmodel/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/listmodel/tst_listmodel.qml2
-rw-r--r--tests/auto/qmltest/listview/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/listview/data/MultiDelegate.qml2
-rw-r--r--tests/auto/qmltest/listview/data/MultiDelegate2.qml2
-rw-r--r--tests/auto/qmltest/listview/data/MultiDelegate3.qml2
-rw-r--r--tests/auto/qmltest/listview/data/asynclistviewloader.qml2
-rw-r--r--tests/auto/qmltest/listview/data/asyncloadercurrentindex.qml2
-rw-r--r--tests/auto/qmltest/listview/tst_listview.qml2
-rw-r--r--tests/auto/qmltest/objectmodel/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/objectmodel/tst_objectmodel.qml2
-rw-r--r--tests/auto/qmltest/pathview/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/positioners/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/positioners/tst_positioners.qml2
-rw-r--r--tests/auto/qmltest/qqmlbinding/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/qqmlbinding/tst_binding.qml2
-rw-r--r--tests/auto/qmltest/qqmlbinding/tst_binding2.qml2
-rw-r--r--tests/auto/qmltest/qtbug46798/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/qtbug46798/tst_qtbug46798.qml2
-rw-r--r--tests/auto/qmltest/rectangle/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/rectangle/tst_rectangle.qml2
-rw-r--r--tests/auto/qmltest/selftests/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/selftests/tst_compare.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_compare_quickobjects.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_createTemporaryObject.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_datadriven.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_destroy.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_findChild.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_grabImage.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_multiTestCase.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_selftests.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_signalspy.qml53
-rw-r--r--tests/auto/qmltest/selftests/tst_stringify.qml2
-rw-r--r--tests/auto/qmltest/selftests/tst_tryVerify.qml2
-rw-r--r--tests/auto/qmltest/shadersource/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/shadersource/tst_DynamicallyCreated.qml2
-rw-r--r--tests/auto/qmltest/shadersource/tst_DynamicallyCreatedSource.qml2
-rw-r--r--tests/auto/qmltest/shadersource/tst_SourceInOtherWindow.qml2
-rw-r--r--tests/auto/qmltest/shadersource/tst_SourceItem.qml2
-rw-r--r--tests/auto/qmltest/shadersource/tst_SourcedFromOtherWindow.qml2
-rw-r--r--tests/auto/qmltest/stability/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/stability/tst_unloadrepeater.qml2
-rw-r--r--tests/auto/qmltest/text/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/text/tst_text.qml2
-rw-r--r--tests/auto/qmltest/textedit/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/textedit/tst_textedit.qml2
-rw-r--r--tests/auto/qmltest/textedit/tst_textedit_editingfinished.qml2
-rw-r--r--tests/auto/qmltest/textinput/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/textinput/tst_textinput.qml5
-rw-r--r--tests/auto/qmltest/url/tst_url.qml2
-rw-r--r--tests/auto/qmltest/window/CMakeLists.txt6
-rw-r--r--tests/auto/qmltest/window/tst_clickwindow.qml2
-rw-r--r--tests/auto/quick/CMakeLists.txt10
-rw-r--r--tests/auto/quick/doc/CMakeLists.txt7
-rw-r--r--tests/auto/quick/doc/how-tos/CMakeLists.txt6
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-button/CMakeLists.txt31
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-button/Main.qml19
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-button/backend.cpp13
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-button/backend.h17
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-button/tst_how-to-cpp-button.cpp53
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/CMakeLists.txt25
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/backend.cpp36
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/backend.h29
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/script.mjs13
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/tst_how-to-cpp-enum-js.cpp47
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/CMakeLists.txt33
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/active-focus-debugging/ActiveFocusDebuggingMain.qml25
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimeComponentLabel.qml29
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePicker.qml325
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerDialog.qml88
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerLabel.qml126
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerMain.qml59
-rw-r--r--tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp368
-rw-r--r--tests/auto/quick/drawingmodes/CMakeLists.txt6
-rw-r--r--tests/auto/quick/drawingmodes/tst_drawingmodes.cpp4
-rw-r--r--tests/auto/quick/examples/CMakeLists.txt8
-rw-r--r--tests/auto/quick/examples/tst_examples.cpp36
-rw-r--r--tests/auto/quick/geometry/CMakeLists.txt6
-rw-r--r--tests/auto/quick/geometry/tst_geometry.cpp2
-rw-r--r--tests/auto/quick/nodes/CMakeLists.txt6
-rw-r--r--tests/auto/quick/nodes/tst_nodestest.cpp11
-rw-r--r--tests/auto/quick/nokeywords/CMakeLists.txt6
-rw-r--r--tests/auto/quick/nokeywords/tst_nokeywords.cpp2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST3
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml49
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml24
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp247
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp4
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST5
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml6
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp6
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp4
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST3
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml37
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml80
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml17
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp85
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml11
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp165
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml36
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml78
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp112
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp4
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST8
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml18
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml14
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml39
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp380
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt6
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml18
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp2
-rw-r--r--tests/auto/quick/propertyrequirements/CMakeLists.txt6
-rw-r--r--tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp13
-rw-r--r--tests/auto/quick/qquickaccessible/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickaccessible/data/hittest.qml2
-rw-r--r--tests/auto/quick/qquickaccessible/data/ignored.qml2
-rw-r--r--tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp58
-rw-r--r--tests/auto/quick/qquickanchors/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickanchors/data/centerin.qml7
-rw-r--r--tests/auto/quick/qquickanchors/tst_qquickanchors.cpp25
-rw-r--r--tests/auto/quick/qquickanimatedimage/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickanimatedimage/data/currentframe.qml1
-rw-r--r--tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp33
-rw-r--r--tests/auto/quick/qquickanimatedsprite/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickanimatedsprite/data/basic.qml2
-rw-r--r--tests/auto/quick/qquickanimatedsprite/data/frameChange.qml2
-rw-r--r--tests/auto/quick/qquickanimatedsprite/data/infiniteLoops.qml2
-rw-r--r--tests/auto/quick/qquickanimatedsprite/data/largeAnimation.qml2
-rw-r--r--tests/auto/quick/qquickanimatedsprite/data/runningChange.qml2
-rw-r--r--tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml2
-rw-r--r--tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp2
-rw-r--r--tests/auto/quick/qquickanimationcontroller/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickanimationcontroller/tst_qquickanimationcontroller.cpp2
-rw-r--r--tests/auto/quick/qquickanimations/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickanimations/data/animationJobSelfDestructionBug.qml2
-rw-r--r--tests/auto/quick/qquickanimations/data/animatorInvalidTargetCrash.qml2
-rw-r--r--tests/auto/quick/qquickanimations/data/changePropertiesDuringAnimation.qml2
-rw-r--r--tests/auto/quick/qquickanimations/data/fastFlickingBug.qml2
-rw-r--r--tests/auto/quick/qquickanimations/data/finished.qml2
-rw-r--r--tests/auto/quick/qquickanimations/data/frameAnimation.qml2
-rw-r--r--tests/auto/quick/qquickanimations/data/infiniteAnimationWithoutFrom.qml2
-rw-r--r--tests/auto/quick/qquickanimations/data/restartAnimationGroupWhenDirty.qml92
-rw-r--r--tests/auto/quick/qquickanimations/data/restartNestedAnimationGroupWhenDirty.qml96
-rw-r--r--tests/auto/quick/qquickanimations/data/scriptActionCrash.qml2
-rw-r--r--tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml29
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp85
-rw-r--r--tests/auto/quick/qquickanimators/BLACKLIST2
-rw-r--r--tests/auto/quick/qquickanimators/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickanimators/data/animatorImplicitFrom.qml2
-rw-r--r--tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml2
-rw-r--r--tests/auto/quick/qquickanimators/data/windowWithAnimator.qml2
-rw-r--r--tests/auto/quick/qquickanimators/tst_qquickanimators.cpp2
-rw-r--r--tests/auto/quick/qquickapplication/BLACKLIST3
-rw-r--r--tests/auto/quick/qquickapplication/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickapplication/tst_qquickapplication.cpp14
-rw-r--r--tests/auto/quick/qquickbehaviors/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickbehaviors/bindable.h2
-rw-r--r--tests/auto/quick/qquickbehaviors/data/duplicated.qml31
-rw-r--r--tests/auto/quick/qquickbehaviors/data/qtbug21549-2.qml2
-rw-r--r--tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp17
-rw-r--r--tests/auto/quick/qquickborderimage/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp18
-rw-r--r--tests/auto/quick/qquickboundaryrule/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp2
-rw-r--r--tests/auto/quick/qquickcanvasitem/BLACKLIST1
-rw-r--r--tests/auto/quick/qquickcanvasitem/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_arc.qml22
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml9
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_composite.qml7
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml8
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml1
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml6
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_image.qml25
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml1
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_invalidContext.qml8
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_line.qml9
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_path.qml16
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml6
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml5
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml11
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_state.qml16
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml5
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml1
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_text.qml6
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_transform.qml6
-rw-r--r--tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp2
-rw-r--r--tests/auto/quick/qquickcolorgroup/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickcolorgroup/tst_qquickcolorgroup.cpp2
-rw-r--r--tests/auto/quick/qquickdeliveryagent/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickdeliveryagent/data/clearItemsOnHoverLeave.qml32
-rw-r--r--tests/auto/quick/qquickdeliveryagent/data/compoundControl.qml28
-rw-r--r--tests/auto/quick/qquickdeliveryagent/data/flickableTextEdit.qml2
-rw-r--r--tests/auto/quick/qquickdeliveryagent/data/listViewDelegate.qml2
-rw-r--r--tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp163
-rw-r--r--tests/auto/quick/qquickdesignersupport/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickdesignersupport/data/RegTestComponent.qml20
-rw-r--r--tests/auto/quick/qquickdesignersupport/data/regTestProperties.qml13
-rw-r--r--tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp42
-rw-r--r--tests/auto/quick/qquickdrag/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickdrag/tst_qquickdrag.cpp2
-rw-r--r--tests/auto/quick/qquickdragattached/CMakeLists.txt27
-rw-r--r--tests/auto/quick/qquickdragattached/data/qt_logo.svg26
-rw-r--r--tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp66
-rw-r--r--tests/auto/quick/qquickdroparea/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickdroparea/data/nested1.qml2
-rw-r--r--tests/auto/quick/qquickdroparea/data/nested2.qml2
-rw-r--r--tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp2
-rw-r--r--tests/auto/quick/qquickdynamicpropertyanimation/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickdynamicpropertyanimation/tst_qquickdynamicpropertyanimation.cpp2
-rw-r--r--tests/auto/quick/qquickflickable/BLACKLIST9
-rw-r--r--tests/auto/quick/qquickflickable/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickflickable/data/endpoints.qml14
-rw-r--r--tests/auto/quick/qquickflickable/data/flickableWithTapHandler.qml24
-rw-r--r--tests/auto/quick/qquickflickable/data/nested.qml2
-rw-r--r--tests/auto/quick/qquickflickable/data/overshoot.qml2
-rw-r--r--tests/auto/quick/qquickflickable/data/overshoot_reentrant.qml2
-rw-r--r--tests/auto/quick/qquickflickable/data/parallel.qml2
-rw-r--r--tests/auto/quick/qquickflickable/data/pressDelay.qml12
-rw-r--r--tests/auto/quick/qquickflickable/data/ratios.qml2
-rw-r--r--tests/auto/quick/qquickflickable/data/visibleAreaBinding.qml2
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp391
-rw-r--r--tests/auto/quick/qquickflipable/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml32
-rw-r--r--tests/auto/quick/qquickflipable/tst_qquickflipable.cpp30
-rw-r--r--tests/auto/quick/qquickfocusscope/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp2
-rw-r--r--tests/auto/quick/qquickfontloader/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp2
-rw-r--r--tests/auto/quick/qquickfontloader_static/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp2
-rw-r--r--tests/auto/quick/qquickfontmetrics/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp6
-rw-r--r--tests/auto/quick/qquickframebufferobject/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickframebufferobject/data/testStuff.qml2
-rw-r--r--tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp2
-rw-r--r--tests/auto/quick/qquickgraphicsinfo/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp2
-rw-r--r--tests/auto/quick/qquickgridview/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickgridview/data/attachedProperties.qml2
-rw-r--r--tests/auto/quick/qquickgridview/data/displayMargin.qml2
-rw-r--r--tests/auto/quick/qquickgridview/data/margins2.qml2
-rw-r--r--tests/auto/quick/qquickgridview/data/negativeDisplayMargin.qml2
-rw-r--r--tests/auto/quick/qquickgridview/data/qtbug49218.qml2
-rw-r--r--tests/auto/quick/qquickgridview/data/qtbug91461.qml2
-rw-r--r--tests/auto/quick/qquickgridview/data/removeAccessibleChildrenEvenIfReusingItems.qml48
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp166
-rw-r--r--tests/auto/quick/qquickimage/BLACKLIST3
-rw-r--r--tests/auto/quick/qquickimage/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickimage/data/hugeImages.qml2
-rw-r--r--tests/auto/quick/qquickimage/data/qtbug_32513.qml2
-rw-r--r--tests/auto/quick/qquickimage/data/statusChanged.qml25
-rw-r--r--tests/auto/quick/qquickimage/tst_qquickimage.cpp47
-rw-r--r--tests/auto/quick/qquickimageprovider/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp19
-rw-r--r--tests/auto/quick/qquickitem/CMakeLists.txt8
-rw-r--r--tests/auto/quick/qquickitem/data/childAtRectangle.qml2
-rw-r--r--tests/auto/quick/qquickitem/data/mainWindowQtBug60123.qml2
-rw-r--r--tests/auto/quick/qquickitem/data/shortcutOverride.qml2
-rw-r--r--tests/auto/quick/qquickitem/tst_qquickitem.cpp237
-rw-r--r--tests/auto/quick/qquickitem2/CMakeLists.txt11
-rw-r--r--tests/auto/quick/qquickitem2/data/embedded.qml30
-rw-r--r--tests/auto/quick/qquickitem2/data/focusInScopeChanges.qml31
-rw-r--r--tests/auto/quick/qquickitem2/data/focusReason.qml85
-rw-r--r--tests/auto/quick/qquickitem2/data/grabToImage.qml2
-rw-r--r--tests/auto/quick/qquickitem2/data/keysforward.qml2
-rw-r--r--tests/auto/quick/qquickitem2/data/mapCoordinates.qml2
-rw-r--r--tests/auto/quick/qquickitem2/data/mapCoordinatesRect.qml2
-rw-r--r--tests/auto/quick/qquickitem2/data/mapCoordinatesWithWindows.qml51
-rw-r--r--tests/auto/quick/qquickitem2/data/standardkeys.qml2
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp567
-rw-r--r--tests/auto/quick/qquickitemlayer/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp2
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt46
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/data/test.qml38
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp352
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/rhiitem.h75
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h60
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp162
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/testrhiitem.h61
-rw-r--r--tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp77
-rw-r--r--tests/auto/quick/qquicklayouts/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicklayouts/data/LayoutHelperLibrary.js42
-rw-r--r--tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml2
-rw-r--r--tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml2
-rw-r--r--tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml2
-rw-r--r--tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml2
-rw-r--r--tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml2
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml126
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_layoutproxy.qml687
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml439
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml142
-rw-r--r--tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp11
-rw-r--r--tests/auto/quick/qquicklistview/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicklistview/data/addoncompleted.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/attachedProperties.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/displayMargin.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/flickBothDirections.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/negativeDisplayMargin.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/objectModelCulling.qml1
-rw-r--r--tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/proxytest.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/qtbug34576.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/qtbug61537_modelChangesAsync.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/repositionListViewOnPopulateTransition.qml51
-rw-r--r--tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/reusedelegateitems.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/roundingErrors.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/setpositiononlayout.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/sizeTransitions.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/snapOneItemResize.qml1
-rw-r--r--tests/auto/quick/qquicklistview/data/stickyPositioning-both.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/stickyPositioning-footer.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/stickyPositioning-header.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/usechooserwithoutdefault.qml2
-rw-r--r--tests/auto/quick/qquicklistview/incrementalmodel.cpp2
-rw-r--r--tests/auto/quick/qquicklistview/incrementalmodel.h2
-rw-r--r--tests/auto/quick/qquicklistview/proxytestinnermodel.cpp2
-rw-r--r--tests/auto/quick/qquicklistview/proxytestinnermodel.h2
-rw-r--r--tests/auto/quick/qquicklistview/randomsortmodel.cpp2
-rw-r--r--tests/auto/quick/qquicklistview/randomsortmodel.h2
-rw-r--r--tests/auto/quick/qquicklistview/reusemodel.h2
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp170
-rw-r--r--tests/auto/quick/qquicklistview2/CMakeLists.txt9
-rw-r--r--tests/auto/quick/qquicklistview2/data/areaZeroView.qml22
-rw-r--r--tests/auto/quick/qquicklistview2/data/bindOnHeaderAndFooterXPosition.qml29
-rw-r--r--tests/auto/quick/qquicklistview2/data/bindOnHeaderAndFooterYPosition.qml29
-rw-r--r--tests/auto/quick/qquicklistview2/data/boundDelegateComponent.qml6
-rw-r--r--tests/auto/quick/qquicklistview2/data/changingOrientationWithListModel.qml47
-rw-r--r--tests/auto/quick/qquicklistview2/data/changingOrientationWithObjectModel.qml54
-rw-r--r--tests/auto/quick/qquicklistview2/data/delegateChooserEnumRole.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/delegateContextHandling.qml75
-rw-r--r--tests/auto/quick/qquicklistview2/data/delegateModelRefresh.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/fetchMore.qml21
-rw-r--r--tests/auto/quick/qquicklistview2/data/footerUpdate.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/innerRequired.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/maxXExtent.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/maxYExtent.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/metaSequenceAsModel.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/qtbug86744.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/qtbug98315.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/qtbug_92809.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/sectionBoundComponent.qml14
-rw-r--r--tests/auto/quick/qquicklistview2/data/sectionGeometryChange.qml58
-rw-r--r--tests/auto/quick/qquicklistview2/data/sectionsNoOverlap.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/urlListModel.qml2
-rw-r--r--tests/auto/quick/qquicklistview2/data/viewportAvoidUndesiredMovementOnSetCurrentIndex.qml47
-rw-r--r--tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp378
-rw-r--r--tests/auto/quick/qquicklistview2/typerolemodel.cpp2
-rw-r--r--tests/auto/quick/qquicklistview2/typerolemodel.h2
-rw-r--r--tests/auto/quick/qquickloader/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickloader/data/Rect120x60.qml8
-rw-r--r--tests/auto/quick/qquickloader/data/bindings.qml2
-rw-r--r--tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml2
-rw-r--r--tests/auto/quick/qquickloader/data/itemLoaderWindow.qml2
-rw-r--r--tests/auto/quick/qquickloader/data/noEngine.qml2
-rw-r--r--tests/auto/quick/qquickloader/data/parentErrors.qml2
-rw-r--r--tests/auto/quick/qquickloader/data/rootContext.qml2
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp84
-rw-r--r--tests/auto/quick/qquickmousearea/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickmousearea/data/clickThrough2.qml6
-rw-r--r--tests/auto/quick/qquickmousearea/data/containsMouseAndHoverDisabled.qml15
-rw-r--r--tests/auto/quick/qquickmousearea/data/cursorUpdating.qml71
-rw-r--r--tests/auto/quick/qquickmousearea/data/hoverAfterPress.qml2
-rw-r--r--tests/auto/quick/qquickmousearea/data/ignoreBySource.qml2
-rw-r--r--tests/auto/quick/qquickmousearea/data/mask.qml2
-rw-r--r--tests/auto/quick/qquickmousearea/data/moveAndReleaseWithoutPress.qml2
-rw-r--r--tests/auto/quick/qquickmousearea/data/qtbug34368.qml4
-rw-r--r--tests/auto/quick/qquickmousearea/data/qtbug49100.qml2
-rw-r--r--tests/auto/quick/qquickmousearea/data/rejectEvent.qml2
-rw-r--r--tests/auto/quick/qquickmousearea/data/updateMousePosOnResize.qml6
-rw-r--r--tests/auto/quick/qquickmousearea/data/wheel.qml2
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp125
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/BLACKLIST4
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml12
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml6
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/mouse.qml8
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/nested.qml2
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/nestedTouchPosCheck.qml52
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml4
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/signalTest.qml14
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/touchOverMouseArea.qml21
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/data/transformedMultiPointTouchArea.qml4
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp157
-rw-r--r--tests/auto/quick/qquickpainteditem/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp22
-rw-r--r--tests/auto/quick/qquickpalette/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickpalette/data/palette-item-custom.qml2
-rw-r--r--tests/auto/quick/qquickpalette/data/palette-item-default.qml2
-rw-r--r--tests/auto/quick/qquickpalette/data/palette-window-custom.qml2
-rw-r--r--tests/auto/quick/qquickpalette/data/palette-window-default.qml2
-rw-r--r--tests/auto/quick/qquickpalette/tst_qquickpalette.cpp2
-rw-r--r--tests/auto/quick/qquickpath/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickpath/tst_qquickpath.cpp2
-rw-r--r--tests/auto/quick/qquickpathview/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickpathview/data/flickableDelegate.qml2
-rw-r--r--tests/auto/quick/qquickpathview/data/mousePressAfterFlick.qml61
-rw-r--r--tests/auto/quick/qquickpathview/data/nestedInFlickable.qml2
-rw-r--r--tests/auto/quick/qquickpathview/data/nestedmousearea2.qml2
-rw-r--r--tests/auto/quick/qquickpathview/data/objectModelMove.qml2
-rw-r--r--tests/auto/quick/qquickpathview/data/overcached.qml27
-rw-r--r--tests/auto/quick/qquickpathview/data/qtbug37815.qml2
-rw-r--r--tests/auto/quick/qquickpathview/data/qtbug46487.qml28
-rw-r--r--tests/auto/quick/qquickpathview/data/qtbug53464.qml2
-rw-r--r--tests/auto/quick/qquickpathview/data/qtbug90479.qml25
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp159
-rw-r--r--tests/auto/quick/qquickpincharea/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp32
-rw-r--r--tests/auto/quick/qquickpixmapcache/BLACKLIST3
-rw-r--r--tests/auto/quick/qquickpixmapcache/CMakeLists.txt18
-rw-r--r--tests/auto/quick/qquickpixmapcache/data/tableViewWithDeviceLoadingImages.qml45
-rw-r--r--tests/auto/quick/qquickpixmapcache/deviceloadingimage.cpp43
-rw-r--r--tests/auto/quick/qquickpixmapcache/deviceloadingimage.h19
-rw-r--r--tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp82
-rw-r--r--tests/auto/quick/qquickpositioners/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp19
-rw-r--r--tests/auto/quick/qquickrectangle/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickrectangle/data/multi-radius.qml27
-rw-r--r--tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp79
-rw-r--r--tests/auto/quick/qquickrendercontrol/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickrendercontrol/data/rect.qml2
-rw-r--r--tests/auto/quick/qquickrendercontrol/data/rect_depth.qml28
-rw-r--r--tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp198
-rw-r--r--tests/auto/quick/qquickrepeater/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp118
-rw-r--r--tests/auto/quick/qquickrhiitem/BLACKLIST (renamed from tests/auto/quick/qquickitemrhiintegration/BLACKLIST)0
-rw-r--r--tests/auto/quick/qquickrhiitem/CMakeLists.txt52
-rw-r--r--tests/auto/quick/qquickrhiitem/data/test.qml41
-rw-r--r--tests/auto/quick/qquickrhiitem/testrhiitem.cpp157
-rw-r--r--tests/auto/quick/qquickrhiitem/testrhiitem.h61
-rw-r--r--tests/auto/quick/qquickrhiitem/texture.frag (renamed from tests/auto/quick/qquickitemrhiintegration/texture.frag)0
-rw-r--r--tests/auto/quick/qquickrhiitem/texture.vert (renamed from tests/auto/quick/qquickitemrhiintegration/texture.vert)0
-rw-r--r--tests/auto/quick/qquickrhiitem/tst_qquickrhiitem.cpp150
-rw-r--r--tests/auto/quick/qquickscreen/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickscreen/tst_qquickscreen.cpp2
-rw-r--r--tests/auto/quick/qquickshadereffect/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickshadereffect/data/MyIcon.qml2
-rw-r--r--tests/auto/quick/qquickshadereffect/data/deleteShaderEffectSource.qml2
-rw-r--r--tests/auto/quick/qquickshadereffect/data/deleteSourceItem.qml2
-rw-r--r--tests/auto/quick/qquickshadereffect/data/hideParent.qml2
-rw-r--r--tests/auto/quick/qquickshadereffect/data/testProperties.qml2
-rw-r--r--tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml2
-rw-r--r--tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp2
-rw-r--r--tests/auto/quick/qquickshape/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickshape/data/multiline.qml2
-rw-r--r--tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml2
-rw-r--r--tests/auto/quick/qquickshape/data/pathitem7.qml2
-rw-r--r--tests/auto/quick/qquickshape/data/pathitem8.qml2
-rw-r--r--tests/auto/quick/qquickshape/data/polyline.qml2
-rw-r--r--tests/auto/quick/qquickshape/tst_qquickshape.cpp2
-rw-r--r--tests/auto/quick/qquickshortcut/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickshortcut/data/embedded.qml20
-rw-r--r--tests/auto/quick/qquickshortcut/data/multiple.qml2
-rw-r--r--tests/auto/quick/qquickshortcut/data/shortcuts.qml2
-rw-r--r--tests/auto/quick/qquickshortcut/data/shortcutsRect.qml2
-rw-r--r--tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp89
-rw-r--r--tests/auto/quick/qquicksmoothedanimation/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicksmoothedanimation/tst_qquicksmoothedanimation.cpp2
-rw-r--r--tests/auto/quick/qquickspringanimation/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickspringanimation/tst_qquickspringanimation.cpp2
-rw-r--r--tests/auto/quick/qquickspritesequence/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickspritesequence/data/advance.qml2
-rw-r--r--tests/auto/quick/qquickspritesequence/data/basic.qml2
-rw-r--r--tests/auto/quick/qquickspritesequence/data/crashonstart.qml2
-rw-r--r--tests/auto/quick/qquickspritesequence/data/huge.qml2
-rw-r--r--tests/auto/quick/qquickspritesequence/data/spriteaftergoal.qml2
-rw-r--r--tests/auto/quick/qquickspritesequence/data/spritebeforegoal.qml2
-rw-r--r--tests/auto/quick/qquickspritesequence/tst_qquickspritesequence.cpp2
-rw-r--r--tests/auto/quick/qquickstates/CMakeLists.txt31
-rw-r--r--tests/auto/quick/qquickstates/data/anchorRewindSize.qml30
-rw-r--r--tests/auto/quick/qquickstates/data/broken.qml5
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp63
-rw-r--r--tests/auto/quick/qquickstyledtext/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickstyledtext/tst_qquickstyledtext.cpp3
-rw-r--r--tests/auto/quick/qquicksystempalette/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicksystempalette/tst_qquicksystempalette.cpp8
-rw-r--r--tests/auto/quick/qquicktableview/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicktableview/data/alternatingrowheightcolumnwidth.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/asyncloader.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/asyncplain.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/changemodelfromdelegate.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/changemodelordelegateduringupdate.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/checkalwaysemit.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/checkmodelpropertyrevision.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/checkrowandcolumnnotchanged.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/columnwidthboundtoviewwidth.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/contentwidthheight.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/countingtableview.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/delegateWithRequired.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/delegatewithRequiredUnset.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/delegatewithanchors.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/editdelegate.qml6
-rw-r--r--tests/auto/quick/qquicktableview/data/editdelegate_combobox.qml6
-rw-r--r--tests/auto/quick/qquicktableview/data/forcelayout.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/hiderowsandcolumns.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/iscolumnloaded.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/plaintableview.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/positionlast.qml52
-rw-r--r--tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/resetJsModelData.qml19
-rw-r--r--tests/auto/quick/qquicktableview/data/resetModelData.qml25
-rw-r--r--tests/auto/quick/qquicktableview/data/setcontentpos.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/sizefromdelegate.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/syncviewsimple.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/tableviewdefaultspacing.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/tableviewfocus.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/tableviewimplicitsize.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/tableviewinteractive.qml32
-rw-r--r--tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/tweakimplicitsize.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/unloadheader.qml43
-rw-r--r--tests/auto/quick/qquicktableview/data/usechooserwithoutdefault.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml2
-rw-r--r--tests/auto/quick/qquicktableview/data/zerosizedtableview.qml10
-rw-r--r--tests/auto/quick/qquicktableview/data/zerosizedviewport.qml31
-rw-r--r--tests/auto/quick/qquicktableview/testmodel.h41
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp416
-rw-r--r--tests/auto/quick/qquicktext/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml27
-rw-r--r--tests/auto/quick/qquicktext/data/lineLayout.qml2
-rw-r--r--tests/auto/quick/qquicktext/data/lineLayoutFontUpdate.qml25
-rw-r--r--tests/auto/quick/qquicktext/data/lineLayoutImplicitWidth.qml4
-rw-r--r--tests/auto/quick/qquicktext/data/lineLayoutRelayout.qml2
-rw-r--r--tests/auto/quick/qquicktext/data/loaderActiveOnVisible.qml24
-rw-r--r--tests/auto/quick/qquicktext/data/long.qml7
-rw-r--r--tests/auto/quick/qquicktext/data/qtbug_106205.qml28
-rw-r--r--tests/auto/quick/qquicktext/data/tarzeau_ocr_a.ttfbin0 -> 24544 bytes
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp508
-rw-r--r--tests/auto/quick/qquicktextdocument/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello-8857-7.html9
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello-utf16be.htmlbin0 -> 48 bytes
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello.html1
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello.md1
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello.txt1
-rw-r--r--tests/auto/quick/qquicktextdocument/data/sideBySideIndependent.qml21
-rw-r--r--tests/auto/quick/qquicktextdocument/data/sideBySideIndependentReverse.qml21
-rw-r--r--tests/auto/quick/qquicktextdocument/data/text.qml14
-rw-r--r--tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp525
-rw-r--r--tests/auto/quick/qquicktextedit/BLACKLIST15
-rw-r--r--tests/auto/quick/qquicktextedit/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicktextedit/data/embeddedImagesLocalError.qml2
-rw-r--r--tests/auto/quick/qquicktextedit/data/embeddedImagesRemote.qml2
-rw-r--r--tests/auto/quick/qquicktextedit/data/embeddedImagesRemoteError.qml2
-rw-r--r--tests/auto/quick/qquicktextedit/data/focusByDefault.qml7
-rw-r--r--tests/auto/quick/qquicktextedit/data/httpfail/warning.pngbin0 -> 10285 bytes
-rw-r--r--tests/auto/quick/qquicktextedit/data/httpslow/turtle.svg15
-rw-r--r--tests/auto/quick/qquicktextedit/data/inFlickable.qml7
-rw-r--r--tests/auto/quick/qquicktextedit/data/multipleRemoteImages.md13
-rw-r--r--tests/auto/quick/qquicktextedit/data/qtConfigureHelp.qml186
-rw-r--r--tests/auto/quick/qquicktextedit/data/qtbug-112858.qml12
-rw-r--r--tests/auto/quick/qquicktextedit/data/resizeTextEditPolish.qml42
-rw-r--r--tests/auto/quick/qquicktextedit/data/textEdit.qml6
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp1013
-rw-r--r--tests/auto/quick/qquicktextinput/BLACKLIST24
-rw-r--r--tests/auto/quick/qquicktextinput/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp41
-rw-r--r--tests/auto/quick/qquicktextmetrics/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicktextmetrics/tst_qquicktextmetrics.cpp2
-rw-r--r--tests/auto/quick/qquicktimeline/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicktimeline/tst_qquicktimeline.cpp2
-rw-r--r--tests/auto/quick/qquicktreeview/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquicktreeview/data/CustomDelegate.qml2
-rw-r--r--tests/auto/quick/qquicktreeview/data/normaltreeview.qml2
-rw-r--r--tests/auto/quick/qquicktreeview/testmodel.cpp32
-rw-r--r--tests/auto/quick/qquicktreeview/testmodel.h3
-rw-r--r--tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp223
-rw-r--r--tests/auto/quick/qquickview/CMakeLists.txt16
-rw-r--r--tests/auto/quick/qquickview/data/TestQml.qml3
-rw-r--r--tests/auto/quick/qquickview/data/overlay.qml17
-rw-r--r--tests/auto/quick/qquickview/tst_qquickview.cpp85
-rw-r--r--tests/auto/quick/qquickview_extra/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickview_extra/tst_qquickview_extra.cpp4
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/data/externalManagedModel.qml2
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp58
-rw-r--r--tests/auto/quick/qquickwindow/CMakeLists.txt6
-rw-r--r--tests/auto/quick/qquickwindow/data/conflictingVisibleFalse.qml6
-rw-r--r--tests/auto/quick/qquickwindow/data/conflictingVisibleTrue.qml6
-rw-r--r--tests/auto/quick/qquickwindow/data/eventTypes.qml20
-rw-r--r--tests/auto/quick/qquickwindow/data/maximized.qml5
-rw-r--r--tests/auto/quick/qquickwindow/data/shortcut.qml2
-rw-r--r--tests/auto/quick/qquickwindow/data/shortcutOverride.qml2
-rw-r--r--tests/auto/quick/qquickwindow/data/visibilityDoesntClobberWindowState.qml5
-rw-r--r--tests/auto/quick/qquickwindow/data/windowattached.qml4
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp208
-rw-r--r--tests/auto/quick/qquickwindowcontainer/CMakeLists.txt37
-rw-r--r--tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_container.qml15
-rw-r--r--tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_item.qml15
-rw-r--r--tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_window.qml10
-rw-r--r--tests/auto/quick/qquickwindowcontainer/data/deferredVisibilityWithoutWindow.qml11
-rw-r--r--tests/auto/quick/qquickwindowcontainer/data/windowComponent.qml28
-rw-r--r--tests/auto/quick/qquickwindowcontainer/tst_qquickwindowcontainer.cpp192
-rw-r--r--tests/auto/quick/rendernode/CMakeLists.txt6
-rw-r--r--tests/auto/quick/rendernode/data/glsimple.qml2
-rw-r--r--tests/auto/quick/rendernode/data/simple.qml2
-rw-r--r--tests/auto/quick/rendernode/tst_rendernode.cpp4
-rw-r--r--tests/auto/quick/scenegraph/BLACKLIST3
-rw-r--r--tests/auto/quick/scenegraph/CMakeLists.txt16
-rw-r--r--tests/auto/quick/scenegraph/data/RenderTestBase.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/manyWindows_dftext.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/manyWindows_image.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/manyWindows_ntext.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/manyWindows_rects.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/renderControl_rect.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_BreakOpacityBatch.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_DrawSets.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_ImageFiltering.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_Mipmap.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_MovingOverlap.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_OpacityThroughBatchRoot.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_OutOfFloatRange.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_Overlap.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_StackingOrder.qml2
-rw-r--r--tests/auto/quick/scenegraph/data/render_bug37422.frag.qsbbin686 -> 0 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/render_bug37422.qml4
-rw-r--r--tests/auto/quick/scenegraph/data/simple.qml2
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp72
-rw-r--r--tests/auto/quick/sharedimage/CMakeLists.txt6
-rw-r--r--tests/auto/quick/sharedimage/tst_sharedimage.cpp2
-rw-r--r--tests/auto/quick/softwarerenderer/CMakeLists.txt6
-rw-r--r--tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp2
-rw-r--r--tests/auto/quick/touchmouse/BLACKLIST5
-rw-r--r--tests/auto/quick/touchmouse/CMakeLists.txt6
-rw-r--r--tests/auto/quick/touchmouse/tst_touchmouse.cpp23
-rw-r--r--tests/auto/quickcontrols/CMakeLists.txt5
-rw-r--r--tests/auto/quickcontrols/accessibility/CMakeLists.txt12
-rw-r--r--tests/auto/quickcontrols/accessibility/tst_accessibility.cpp2
-rw-r--r--tests/auto/quickcontrols/cmake/CMakeLists.txt16
-rw-r--r--tests/auto/quickcontrols/controls/basic/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/basic/tst_basic.cpp2
-rw-r--r--tests/auto/quickcontrols/controls/data/SignalSequenceSpy.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/TestItem.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/TumblerDatePicker.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/TumblerListView.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/TumblerPathView.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/splitview/fillItemInMiddle.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/splitview/fillItemOnLeft.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/splitview/fillItemOnTop.qml2
-rw-r--r--tests/auto/quickcontrols/controls/data/stackview/Rect.qml8
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml174
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_action.qml30
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_actiongroup.qml192
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_busyindicator.qml35
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_button.qml66
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_buttongroup.qml126
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml37
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_checkbox.qml48
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml30
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_combobox.qml54
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_container.qml32
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_control.qml184
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml10
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_delaybutton.qml30
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_dial.qml325
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_dialog.qml61
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_dialogbuttonbox.qml80
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_frame.qml14
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_groupbox.qml14
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml20
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_label.qml40
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_menuitem.qml26
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_monthgrid.qml48
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_page.qml37
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_pageindicator.qml41
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_pane.qml141
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_popup.qml218
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_progressbar.qml18
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_radiobutton.qml58
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_radiodelegate.qml26
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_rangeslider.qml318
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_roundbutton.qml20
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_scrollbar.qml171
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_scrollindicator.qml44
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_scrollview.qml193
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml371
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_slider.qml159
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_spinbox.qml585
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_splitview.qml478
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_stackview.qml588
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml244
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_swipeview.qml164
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_switch.qml52
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml62
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_tabbar.qml72
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_tabbutton.qml24
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_textarea.qml155
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_textfield.qml146
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_toolbar.qml14
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_toolbutton.qml32
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_toolseparator.qml16
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_tooltip.qml65
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_tumbler.qml49
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_weeknumbercolumn.qml16
-rw-r--r--tests/auto/quickcontrols/controls/fusion/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/fusion/tst_fusion.cpp2
-rw-r--r--tests/auto/quickcontrols/controls/imagine/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/imagine/tst_imagine.cpp2
-rw-r--r--tests/auto/quickcontrols/controls/ios/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/ios/tst_ios.cpp2
-rw-r--r--tests/auto/quickcontrols/controls/macos/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/macos/tst_macos.cpp2
-rw-r--r--tests/auto/quickcontrols/controls/material/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/material/tst_material.cpp2
-rw-r--r--tests/auto/quickcontrols/controls/universal/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/universal/tst_universal.cpp2
-rw-r--r--tests/auto/quickcontrols/controls/windows/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/controls/windows/tst_windows.cpp2
-rw-r--r--tests/auto/quickcontrols/cursor/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/cursor/data/buttons.qml2
-rw-r--r--tests/auto/quickcontrols/cursor/data/containers.qml2
-rw-r--r--tests/auto/quickcontrols/cursor/data/editable.qml2
-rw-r--r--tests/auto/quickcontrols/cursor/data/pageindicator.qml2
-rw-r--r--tests/auto/quickcontrols/cursor/data/scrollbar.qml2
-rw-r--r--tests/auto/quickcontrols/cursor/data/sliders.qml2
-rw-r--r--tests/auto/quickcontrols/cursor/tst_cursor.cpp2
-rw-r--r--tests/auto/quickcontrols/customization/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/AbstractButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ApplicationWindow.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/BusyIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Button.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/CheckBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/CheckDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ComboBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Container.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Control.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/DelayButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Dial.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Dialog.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/DialogButtonBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Drawer.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Frame.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/GroupBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ItemDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Label.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Menu.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/MenuBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/MenuBarItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/MenuItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/MenuSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Page.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/PageIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Pane.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Popup.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ProgressBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/RadioButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/RadioDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/RangeSlider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/RoundButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ScrollBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ScrollIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ScrollView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Slider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/SpinBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/StackView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/SwipeDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/SwipeView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Switch.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/SwitchDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/TabBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/TabButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/TextArea.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/TextField.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ToolBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ToolButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ToolSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/ToolTip.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/empty/Tumbler.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/AbstractButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ApplicationWindow.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/BusyIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Button.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/CheckBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/CheckDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ComboBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Container.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Control.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/DelayButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Dial.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Dialog.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/DialogButtonBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Drawer.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Frame.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/GroupBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ItemDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Label.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Menu.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/MenuBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/MenuBarItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/MenuItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/MenuSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Page.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/PageIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Pane.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Popup.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ProgressBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/RadioButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/RadioDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/RangeSlider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/RoundButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ScrollBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ScrollIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ScrollView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Slider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/SpinBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/StackView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/SwipeDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/SwipeView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Switch.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/SwitchDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/TabBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/TabButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/TextArea.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/TextField.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ToolBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ToolButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ToolSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/ToolTip.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/identified/Tumbler.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/AbstractButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ApplicationWindow.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/BusyIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Button.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/CheckBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/CheckDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ComboBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Container.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Control.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/DelayButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Dial.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Dialog.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/DialogButtonBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Drawer.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Frame.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/GroupBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ItemDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Label.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Menu.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBarItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/MenuItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/MenuSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Page.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/PageIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Pane.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Popup.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ProgressBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/RadioButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/RadioDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/RangeSlider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/RoundButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Slider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/SpinBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/StackView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Switch.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/SwitchDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/TabBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/TabButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/TextArea.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/TextField.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ToolBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ToolButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ToolSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/ToolTip.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/incomplete/Tumbler.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/AbstractButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ApplicationWindow.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/BusyIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Button.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/CheckBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/CheckDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ComboBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Container.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Control.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/DelayButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Dial.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Dialog.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/DialogButtonBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Drawer.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Frame.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/GroupBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ItemDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Label.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Menu.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/MenuBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/MenuBarItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/MenuItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/MenuSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Page.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/PageIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Pane.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Popup.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ProgressBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/RadioButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/RadioDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/RangeSlider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/RoundButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ScrollBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ScrollIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ScrollView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Slider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/SpinBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/StackView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/SwipeDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/SwipeView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Switch.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/SwitchDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/TabBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/TabButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/TextArea.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/TextField.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ToolBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ToolButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ToolSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/ToolTip.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/override/Tumbler.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/AbstractButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ApplicationWindow.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/BusyIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Button.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/CheckBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/CheckDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ComboBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Container.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Control.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/DelayButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Dial.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Dialog.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/DialogButtonBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Drawer.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Frame.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/GroupBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ItemDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Label.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Menu.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/MenuBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/MenuBarItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/MenuItem.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/MenuSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Page.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/PageIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Pane.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Popup.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ProgressBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/RadioButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/RadioDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/RangeSlider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/RoundButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ScrollBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ScrollIndicator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ScrollView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Slider.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/SpinBox.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/StackView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/SwipeDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/SwipeView.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Switch.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/SwitchDelegate.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/TabBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/TabButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/TextArea.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/TextField.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ToolBar.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ToolButton.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ToolSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/ToolTip.qml2
-rw-r--r--tests/auto/quickcontrols/customization/data/styles/simple/Tumbler.qml2
-rw-r--r--tests/auto/quickcontrols/customization/tst_customization.cpp14
-rw-r--r--tests/auto/quickcontrols/deferred/CMakeLists.txt6
-rw-r--r--tests/auto/quickcontrols/deferred/data/abortedIncubation.qml171
-rw-r--r--tests/auto/quickcontrols/deferred/tst_qquickdeferred.cpp23
-rw-r--r--tests/auto/quickcontrols/designer/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/designer/tst_designer.cpp2
-rw-r--r--tests/auto/quickcontrols/focus/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/focus/data/activeFocusOnTab.qml2
-rw-r--r--tests/auto/quickcontrols/focus/data/keyNavigation.qml2
-rw-r--r--tests/auto/quickcontrols/focus/data/visualFocus.qml2
-rw-r--r--tests/auto/quickcontrols/focus/tst_focus.cpp8
-rw-r--r--tests/auto/quickcontrols/font/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/font/data/font-appwindow-custom.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/font-appwindow-default.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/font-control-custom.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/font-control-default.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/font-popup-custom.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/font-popup-default.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/inheritance-childcontrol.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/inheritance-childpopup.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/inheritance-control.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/inheritance-dynamicchildcontrol.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/inheritance-dynamicchildpopup.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/inheritance-dynamiccontrol.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/inheritance-dynamicpopup.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/inheritance-popup.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/listview.qml2
-rw-r--r--tests/auto/quickcontrols/font/data/resolve.qml2
-rw-r--r--tests/auto/quickcontrols/font/tst_font.cpp38
-rw-r--r--tests/auto/quickcontrols/palette/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/palette/data/bindings.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/childPopupInheritance.qml40
-rw-r--r--tests/auto/quickcontrols/palette/data/comboBoxPopupWithApplicationWindow.qml32
-rw-r--r--tests/auto/quickcontrols/palette/data/comboBoxPopupWithWindow.qml33
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritance-childcontrol.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritance-childpopup.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritance-control.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritance-dynamicchildcontrol.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritance-dynamicchildpopup.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritance-dynamiccontrol.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritance-dynamicpopup.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/inheritance-popup.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/listview.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/palette-appwindow-bindingpalette.qml15
-rw-r--r--tests/auto/quickcontrols/palette/data/palette-appwindow-custom.qml3
-rw-r--r--tests/auto/quickcontrols/palette/data/palette-appwindow-default.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/palette-control-custom.qml3
-rw-r--r--tests/auto/quickcontrols/palette/data/palette-control-default.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/palette-popup-custom.qml3
-rw-r--r--tests/auto/quickcontrols/palette/data/palette-popup-default.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/reset-color.qml52
-rw-r--r--tests/auto/quickcontrols/palette/data/resolve.qml2
-rw-r--r--tests/auto/quickcontrols/palette/data/set-palette.qml2
-rw-r--r--tests/auto/quickcontrols/palette/qtquickcontrols2.conf4
-rw-r--r--tests/auto/quickcontrols/palette/tst_palette.cpp203
-rw-r--r--tests/auto/quickcontrols/platform/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_colordialog.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_filedialog.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_folderdialog.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_fontdialog.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_menu.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_menubar.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_menuitem.qml27
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_menuitemgroup.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_menuseparator.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_messagedialog.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_standardpaths.qml2
-rw-r--r--tests/auto/quickcontrols/platform/data/tst_systemtrayicon.qml2
-rw-r--r--tests/auto/quickcontrols/platform/tst_platform.cpp15
-rw-r--r--tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp4
-rw-r--r--tests/auto/quickcontrols/pressandhold/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/pressandhold/tst_pressandhold.cpp40
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt12
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/activeFocusControl.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/activefocusontab.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/attachedProperties.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml15
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/basicapplicationwindow.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/clearfocusondestruction.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/defaultFocus.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/fill.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/focusAfterPopupClosed.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/font.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/layout.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/layoutLayout.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/locale.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/opacity.qml2
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp54
-rw-r--r--tests/auto/quickcontrols/qquickcolor/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickcolor/data/tst_color.qml2
-rw-r--r--tests/auto/quickcontrols/qquickcolor/tst_qquickcolor.cpp2
-rw-r--r--tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickcontrol/data/flickable.qml2
-rw-r--r--tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-button.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-hover.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-wheel.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/applicationwindow.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/dragOverModalShadow.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/flickable.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/grabber.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/header.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/multiTouch.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/multiple.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/reposition.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/slider.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/topEdgeScreenEdge.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/window-button.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/window-hover.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/window-wheel.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/data/window.qml2
-rw-r--r--tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp43
-rw-r--r--tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickheaderview/data/DefaultRoles.qml34
-rw-r--r--tests/auto/quickcontrols/qquickheaderview/data/ListModel.qml2
-rw-r--r--tests/auto/quickcontrols/qquickheaderview/data/Window.qml2
-rw-r--r--tests/auto/quickcontrols/qquickheaderview/data/headerData.qml37
-rw-r--r--tests/auto/quickcontrols/qquickheaderview/data/resizableHandlerBlockingEvents.qml29
-rw-r--r--tests/auto/quickcontrols/qquickheaderview/tst_qquickheaderview.cpp89
-rw-r--r--tests/auto/quickcontrols/qquickiconimage/BLACKLIST2
-rw-r--r--tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickiconimage/data/translucentColors.qml2
-rw-r--r--tests/auto/quickcontrols/qquickiconimage/tst_qquickiconimage.cpp53
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/data/colorChanges.qml2
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/data/iconlabel.qml2
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyIcon.qml2
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyText.qml2
-rw-r--r--tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp5
-rw-r--r--tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickimaginestyle/data/tst_imagine.qml2
-rw-r--r--tests/auto/quickcontrols/qquickimaginestyle/tst_qquickimaginestyle.cpp2
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt11
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml557
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp2
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyleconf/data/applicationwindow.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyleconf/qtquickcontrols2.conf4
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/actionShortcuts.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/actions.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/addItem.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/applicationWindowScrollable.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/applicationwindow.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/delegateFromSeparateComponent.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/disableWhenTriggered.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/disabledMenuItemKeyNavigation.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/giveMenuItemFocusOnButtonPress.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/invalidUrlInImgTag.qml14
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/menuItemWidths.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/menuSeparator.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/order.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/popup.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/pressAndHold.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/removeTakeItem.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/repeater.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml27
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/scrollableWithPadding.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/subMenuDisabled.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/subMenus.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/data/windowScrollable.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp25
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/data/checkHighlightWhenDismissed.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/data/delegateFromSeparateComponent.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/data/empty.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/data/hoverAfterClosingWithEscape.qml46
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/data/menubar.qml2
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/data/touch.qml20
-rw-r--r--tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp132
-rw-r--r--tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt8
-rw-r--r--tests/auto/quickcontrols/qquickninepatchimage/data/logo.pkmbin0 -> 32784 bytes
-rw-r--r--tests/auto/quickcontrols/qquickninepatchimage/data/o1_bc1.ktxbin0 -> 2116 bytes
-rw-r--r--tests/auto/quickcontrols/qquickninepatchimage/data/qt4.astcbin0 -> 12816 bytes
-rw-r--r--tests/auto/quickcontrols/qquickninepatchimage/tst_qquickninepatchimage.cpp72
-rw-r--r--tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt43
-rw-r--r--tests/auto/quickcontrols/qquickoverlay/tst_qquickoverlay.cpp223
-rw-r--r--tests/auto/quickcontrols/qquickpopup/BLACKLIST11
-rw-r--r--tests/auto/quickcontrols/qquickpopup/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterExit.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml27
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose1.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose2.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose3.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClosingSeveralPopups.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusOnDelayedEnter.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml18
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/applicationwindow-wheel.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/applicationwindow.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/centerInOverlayWithinStackViewItem.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/closeOnEscapeWithNestedPopups.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/countChanged.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/cursor.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/destroyDuringExitTransition.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/dialog.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/dimmerContainmentMask.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/disabledPalette.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/doubleClickInMouseArea.qml23
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/fadeDimmer.qml39
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/grabber.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/mirroredCombobox.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/modelessOnModalOnModeless.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/multiplepopup.qml82
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/nested-wheel-overlay-parent.qml39
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/nested.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/noDimmer.qml22
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/orientation.qml24
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/releaseAfterExitTransition.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/rotatedCombobox.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowHeight.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowWidth.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/tabFence.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/toolTipCrashOnClose.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/window-hover.qml17
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/window-wheel.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/window.qml2
-rw-r--r--tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp340
-rw-r--r--tests/auto/quickcontrols/qquickstyle/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickstyle/tst_qquickstyle.cpp2
-rw-r--r--tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt4
-rw-r--r--tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp10
-rw-r--r--tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt4
-rw-r--r--tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp8
-rw-r--r--tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquicktreeviewdelegate/data/unmodified.qml2
-rw-r--r--tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.cpp2
-rw-r--r--tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.h2
-rw-r--r--tests/auto/quickcontrols/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp24
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyle/data/tst_universal.qml2
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyle/tst_qquickuniversalstyle.cpp2
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyleconf/data/applicationwindow.qml2
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyleconf/qtquickcontrols2.conf4
-rw-r--r--tests/auto/quickcontrols/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp2
-rw-r--r--tests/auto/quickcontrols/revisions/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/revisions/tst_revisions.cpp2
-rw-r--r--tests/auto/quickcontrols/sanity/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/sanity/data/attachedTypeResolution.qml6
-rw-r--r--tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp36
-rw-r--r--tests/auto/quickcontrols/sanity/quickcontrolssanity.h2
-rw-r--r--tests/auto/quickcontrols/sanity/tst_sanity.cpp16
-rw-r--r--tests/auto/quickcontrols/snippets/CMakeLists.txt4
-rw-r--r--tests/auto/quickcontrols/snippets/tst_snippets.cpp2
-rw-r--r--tests/auto/quickcontrols/styleimports/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/styleimports/data/applicationWindowWithButton.qml5
-rw-r--r--tests/auto/quickcontrols/styleimports/data/customStyleSelector.qml2
-rw-r--r--tests/auto/quickcontrols/styleimports/data/importStyleWithQmlDirFallback.qml17
-rw-r--r--tests/auto/quickcontrols/styleimports/data/styles/FileSystemStyle/Button.qml5
-rw-r--r--tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsMaterial/Button.qml34
-rw-r--r--tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsMaterial/qmldir3
-rw-r--r--tests/auto/quickcontrols/styleimports/tst_styleimports.cpp96
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimematerial/data/importMaterialStyleWithoutControls.qml2
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimematerial/tst_styleimportscompiletimematerial.cpp2
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/QmlOnly/Button.qml2
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/importQmlOnlyStyleWithoutControls.qml2
-rw-r--r--tests/auto/quickcontrols/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp2
-rw-r--r--tests/auto/quickcontrols/translation/CMakeLists.txt2
-rw-r--r--tests/auto/quickcontrols/translation/data/comboBox.qml2
-rw-r--r--tests/auto/quickcontrols/translation/data/dialogButtonBox.qml2
-rw-r--r--tests/auto/quickcontrols/translation/data/dialogButtonBoxWithCustomButtons.qml2
-rw-r--r--tests/auto/quickcontrols/translation/data/stackView.qml2
-rw-r--r--tests/auto/quickcontrols/translation/tst_translation.cpp2
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/CMakeLists.txt6
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialog.qml2
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindow.qml27
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindowVisibleTrue.qml28
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/data/windowSwapping.qml58
-rw-r--r--tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp96
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt6
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTitle.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/fileDialog.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp309
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/CMakeLists.txt6
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/data/acceptRejectLabel.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindCurrentFolder.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindTitle.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/data/folderDialog.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp20
-rw-r--r--tests/auto/quickdialogs/qquickfontdialogimpl/CMakeLists.txt6
-rw-r--r--tests/auto/quickdialogs/qquickfontdialogimpl/data/fontDialog.qml2
-rw-r--r--tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp2
-rw-r--r--tests/auto/quickdialogs/qquickmessagedialogimpl/CMakeLists.txt6
-rw-r--r--tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialog.qml2
-rw-r--r--tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialogWithButtons.qml19
-rw-r--r--tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp99
-rw-r--r--tests/auto/quicktest/polish-qml/CMakeLists.txt6
-rw-r--r--tests/auto/quicktest/polish-qml/data/tst_polish.qml2
-rw-r--r--tests/auto/quicktest/polish-qml/tst_polish-qml.cpp2
-rw-r--r--tests/auto/quicktest/polish/CMakeLists.txt6
-rw-r--r--tests/auto/quicktest/polish/data/childPolish.qml2
-rw-r--r--tests/auto/quicktest/polish/data/polish.qml2
-rw-r--r--tests/auto/quicktest/polish/tst_polish.cpp2
-rw-r--r--tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt6
-rw-r--r--tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml2
-rw-r--r--tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp2
-rw-r--r--tests/auto/quicktest/signalspy/CMakeLists.txt6
-rw-r--r--tests/auto/quicktest/signalspy/data/signalspy.qml2
-rw-r--r--tests/auto/quicktest/signalspy/data/signalspy2.qml2
-rw-r--r--tests/auto/quicktest/signalspy/mypropertymap.cpp2
-rw-r--r--tests/auto/quicktest/signalspy/mypropertymap.h2
-rw-r--r--tests/auto/quicktest/signalspy/tst_signalspy.cpp2
-rw-r--r--tests/auto/quicktest/testfiltering/quicktestmain/CMakeLists.txt6
-rw-r--r--tests/auto/quicktest/testfiltering/quicktestmain/quicktestmain.cpp2
-rw-r--r--tests/auto/quicktest/testfiltering/quicktestmain/tst_first.qml2
-rw-r--r--tests/auto/quicktest/testfiltering/quicktestmain/tst_second.qml2
-rw-r--r--tests/auto/quicktest/testfiltering/quicktestmain/tst_third.qml2
-rw-r--r--tests/auto/quicktest/testfiltering/test/CMakeLists.txt6
-rw-r--r--tests/auto/quicktest/testfiltering/tst_testfiltering.cpp2
-rw-r--r--tests/auto/quicktest/testswithcomponents/CMakeLists.txt6
-rw-r--r--tests/auto/quicktest/testswithcomponents/data/tst_setup.qml2
-rw-r--r--tests/auto/quicktest/testswithcomponents/tst_quicktestswithcomponents.cpp2
-rw-r--r--tests/auto/quickwidgets/qquickwidget/CMakeLists.txt6
-rw-r--r--tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml2
-rw-r--r--tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml2
-rw-r--r--tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml11
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp156
-rw-r--r--tests/auto/toolsupport/CMakeLists.txt8
-rw-r--r--tests/auto/toolsupport/tst_toolsupport.cpp11
-rw-r--r--tests/baseline/CMakeLists.txt6
-rw-r--r--tests/baseline/controls/BLACKLIST15
-rw-r--r--tests/baseline/controls/data/dialog/dialog.qml4
-rw-r--r--tests/baseline/controls/data/dialog/modal.qml4
-rw-r--r--tests/baseline/controls/data/groupBox/groupbox.qml2
-rw-r--r--tests/baseline/controls/data/popup/modal.qml2
-rw-r--r--tests/baseline/controls/data/popup/popup.qml2
-rw-r--r--tests/baseline/controls/data/textarea/textarea.qml3
-rw-r--r--tests/baseline/controls/data/textfield/textfield.qml5
-rw-r--r--tests/baseline/controls/tst_baseline_controls.cpp26
-rw-r--r--tests/baseline/scenegraph/data/Ignore1
-rw-r--r--tests/baseline/scenegraph/data/rectangles/rectangles_allCornerCombinations.qml43
-rw-r--r--tests/baseline/scenegraph/data/rectangles/rectangles_irregularcorners.qml1579
-rw-r--r--tests/baseline/scenegraph/data/shape/OrderedPaths.qml141
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_alignment.qml90
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_arc.qml187
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_arc_fill.qml187
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_boundingrect.qml46
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_boundingrect_square_cap.qml45
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_cubic.qml47
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_dashcurves.qml547
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_dashlines.qml505
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_implicit_size.qml91
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_intersecting1.qml68
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_intersecting2.qml68
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_intersecting3.qml68
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_intersecting4.qml72
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_intersecting5.qml67
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_intersecting6.qml67
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_intersecting7.qml67
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_linear_gradient.qml56
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_lines.qml174
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_multiline.qml93
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_order.qml12
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_order2.qml91
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_order_async.qml12
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_quad.qml45
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_spread.qml53
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_text.qml137
-rw-r--r--tests/baseline/scenegraph/data/text/text_baseline_alignment.qml37
-rw-r--r--tests/baseline/scenegraph/data/text/text_context_font_merging.qml18
-rw-r--r--tests/baseline/scenegraph/data/text/text_emoji.qml5
-rw-r--r--tests/baseline/scenegraph/data/text/text_emoji_hebrew.qml4
-rw-r--r--tests/baseline/scenegraph/data/text/textedit_baseline_alignment.qml29
-rw-r--r--tests/baseline/scenegraph/scenegrabber/CMakeLists.txt2
-rw-r--r--tests/baseline/scenegraph/scenegrabber/main.cpp129
-rw-r--r--tests/baseline/scenegraph/scenegraph/tst_baseline_scenegraph.cpp13
-rw-r--r--tests/benchmarks/particles/affectors/data/basic.qml2
-rw-r--r--tests/benchmarks/particles/affectors/data/filtered.qml2
-rw-r--r--tests/benchmarks/particles/affectors/tst_affectors.cpp2
-rw-r--r--tests/benchmarks/particles/emission/data/basic.qml2
-rw-r--r--tests/benchmarks/particles/emission/tst_emission.cpp2
-rw-r--r--tests/benchmarks/qml/CMakeLists.txt4
-rw-r--r--tests/benchmarks/qml/animation/data/animation.qml2
-rw-r--r--tests/benchmarks/qml/animation/tst_animation.cpp2
-rw-r--r--tests/benchmarks/qml/binding/testtypes.cpp2
-rw-r--r--tests/benchmarks/qml/binding/testtypes.h2
-rw-r--r--tests/benchmarks/qml/binding/tst_binding.cpp2
-rw-r--r--tests/benchmarks/qml/compilation/data/BoomBlock.qml2
-rw-r--r--tests/benchmarks/qml/compilation/tst_compilation.cpp2
-rw-r--r--tests/benchmarks/qml/creation/data/CustomItem.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/emptyCustomItem.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/emptyItem.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/item.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemUsingOnComponentCompleted.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemWithAnchoredChild.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemWithChildBindedToSize.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemWithProperties.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest1.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest2.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest3.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest4.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest5.qml2
-rw-r--r--tests/benchmarks/qml/creation/data/qobject.qml2
-rw-r--r--tests/benchmarks/qml/creation/tst_creation.cpp2
-rw-r--r--tests/benchmarks/qml/deltablue/BaseConstraint.qml11
-rw-r--r--tests/benchmarks/qml/deltablue/CMakeLists.txt37
-rw-r--r--tests/benchmarks/qml/deltablue/ChainTest.qml67
-rw-r--r--tests/benchmarks/qml/deltablue/Constraint.qml147
-rw-r--r--tests/benchmarks/qml/deltablue/Main.qml132
-rw-r--r--tests/benchmarks/qml/deltablue/Main2.qml16
-rw-r--r--tests/benchmarks/qml/deltablue/Plan.qml27
-rw-r--r--tests/benchmarks/qml/deltablue/Planner.qml194
-rw-r--r--tests/benchmarks/qml/deltablue/ProjectionTest.qml112
-rw-r--r--tests/benchmarks/qml/deltablue/Satisfaction.qml13
-rw-r--r--tests/benchmarks/qml/deltablue/Strength.qml45
-rw-r--r--tests/benchmarks/qml/deltablue/Variable.qml48
-rw-r--r--tests/benchmarks/qml/deltablue/deltablue.js858
-rw-r--r--tests/benchmarks/qml/deltablue/main.cpp68
-rw-r--r--tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicFour.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicOne.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicThree.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicTwo.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/Mlbsi.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/Mldsi.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/Mlsi.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/ModuleBm.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/Msbsi.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/Msdsi.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/Mssi.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/PragmaBm.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/PragmaModuleBm.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/Slsi.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/Sssi.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi1.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi10.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi11.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi12.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi13.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi14.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi15.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi2.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi3.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi4.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi5.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi6.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi7.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi8.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlbsi9.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi1.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi10.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi11.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi12.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi13.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi14.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi15.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi2.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi3.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi4.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi5.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi6.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi7.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi8.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mldsi9.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mlsi.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/moduleBm.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi1.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi10.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi11.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi12.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi13.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi14.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi15.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi2.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi3.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi4.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi5.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi6.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi7.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi8.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msbsi9.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi1.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi10.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi11.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi12.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi13.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi14.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi15.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi2.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi3.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi4.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi5.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi6.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi7.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi8.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/msdsi9.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/mssi.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/pragmaBmOne.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/pragmaBmTwo.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/pragmaLib.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/pragmaModuleBm.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/slsi.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsImports/sssi.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsTargets/JsOne.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/jsTargets/JsTwo.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/largeTargets/gridview-example.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/largeTargets/layoutdirection.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/largeTargets/mousearea-example.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/resolutionTargets/ResolveOne.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/CppToJs.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/CppToQml.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEight.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEleven.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFive.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFour.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppNine.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppOne.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSeven.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSix.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTen.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppThree.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTwo.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceOne.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceTwo.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/scopeSwitching/cppToJs.js2
-rw-r--r--tests/benchmarks/qml/holistic/data/smallTargets/SmallFour.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/smallTargets/SmallOne.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/smallTargets/SmallThree.qml2
-rw-r--r--tests/benchmarks/qml/holistic/data/smallTargets/SmallTwo.qml2
-rw-r--r--tests/benchmarks/qml/holistic/testtypes.cpp2
-rw-r--r--tests/benchmarks/qml/holistic/testtypes.h2
-rw-r--r--tests/benchmarks/qml/holistic/tst_holistic.cpp2
-rw-r--r--tests/benchmarks/qml/javascript/testtypes.cpp2
-rw-r--r--tests/benchmarks/qml/javascript/testtypes.h2
-rw-r--r--tests/benchmarks/qml/javascript/tst_javascript.cpp2
-rw-r--r--tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp2
-rw-r--r--tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp6
-rw-r--r--tests/benchmarks/qml/js/qjsvalueiterator/tst_qjsvalueiterator.cpp2
-rw-r--r--tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp2
-rw-r--r--tests/benchmarks/qml/painting/paintbenchmark.cpp2
-rw-r--r--tests/benchmarks/qml/qmldom/CMakeLists.txt16
-rw-r--r--tests/benchmarks/qml/qmldom/data/deeplyNested.qml2995
-rw-r--r--tests/benchmarks/qml/qmldom/data/longQmlFile.qml3667
-rw-r--r--tests/benchmarks/qml/qmldom/tst_qmldomconstruction.cpp71
-rw-r--r--tests/benchmarks/qml/qproperty/main.cpp2
-rw-r--r--tests/benchmarks/qml/qproperty/propertytester.h2
-rw-r--r--tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/data/myqmlobject.qml2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/data/myqmlobject_binding.qml2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/data/object.qml2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/data/object_id.qml2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/data/samegame/BoomBlock.qml2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.2.qml2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.qml2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/testtypes.cpp2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/testtypes.h2
-rw-r--r--tests/benchmarks/qml/qqmlcomponent/tst_qqmlcomponent.cpp2
-rw-r--r--tests/benchmarks/qml/qqmlmetaproperty/data/object.qml2
-rw-r--r--tests/benchmarks/qml/qqmlmetaproperty/data/synthesized_object.qml2
-rw-r--r--tests/benchmarks/qml/qqmlmetaproperty/tst_qqmlmetaproperty.cpp2
-rw-r--r--tests/benchmarks/qml/qquickwindow/tst_qquickwindow.cpp2
-rw-r--r--tests/benchmarks/qml/script/data/CustomObject.qml2
-rw-r--r--tests/benchmarks/qml/script/data/block.qml2
-rw-r--r--tests/benchmarks/qml/script/data/enums.qml2
-rw-r--r--tests/benchmarks/qml/script/data/global.js2
-rw-r--r--tests/benchmarks/qml/script/data/global_prop.qml2
-rw-r--r--tests/benchmarks/qml/script/data/namespacedEnums.qml2
-rw-r--r--tests/benchmarks/qml/script/data/scriptCall.qml2
-rw-r--r--tests/benchmarks/qml/script/data/signal_args.qml2
-rw-r--r--tests/benchmarks/qml/script/data/signal_heavyArgsAccess.qml2
-rw-r--r--tests/benchmarks/qml/script/data/signal_heavyIdAccess.qml2
-rw-r--r--tests/benchmarks/qml/script/data/signal_qml.qml2
-rw-r--r--tests/benchmarks/qml/script/data/signal_unconnected.qml2
-rw-r--r--tests/benchmarks/qml/script/data/signal_unusedArgs.qml2
-rw-r--r--tests/benchmarks/qml/script/data/slot_complex.qml2
-rw-r--r--tests/benchmarks/qml/script/data/slot_complex_js.qml2
-rw-r--r--tests/benchmarks/qml/script/data/slot_simple.qml2
-rw-r--r--tests/benchmarks/qml/script/data/slot_simple_js.qml2
-rw-r--r--tests/benchmarks/qml/script/tst_script.cpp2
-rw-r--r--tests/benchmarks/qml/typeimports/data/QmlTestType1.qml2
-rw-r--r--tests/benchmarks/qml/typeimports/data/QmlTestType2.qml2
-rw-r--r--tests/benchmarks/qml/typeimports/data/QmlTestType3.qml2
-rw-r--r--tests/benchmarks/qml/typeimports/data/QmlTestType4.qml2
-rw-r--r--tests/benchmarks/qml/typeimports/data/cpp.qml2
-rw-r--r--tests/benchmarks/qml/typeimports/data/qml.qml2
-rw-r--r--tests/benchmarks/qml/typeimports/tst_typeimports.cpp2
-rw-r--r--tests/benchmarks/quick/CMakeLists.txt1
-rw-r--r--tests/benchmarks/quick/colorresolving/CMakeLists.txt25
-rw-r--r--tests/benchmarks/quick/colorresolving/data/tst_colorresolving.qml2
-rw-r--r--tests/benchmarks/quick/colorresolving/tst_colorresolving.cpp2
-rw-r--r--tests/benchmarks/quick/curverenderer/CMakeLists.txt11
-rw-r--r--tests/benchmarks/quick/curverenderer/tst_bench_curverenderer.cpp78
-rw-r--r--tests/benchmarks/quick/events/data/mouseevent.qml2
-rw-r--r--tests/benchmarks/quick/events/data/touchevent.qml2
-rw-r--r--tests/benchmarks/quick/events/tst_events.cpp2
-rw-r--r--tests/benchmarks/quickcontrols/creationtime/tst_creationtime.cpp2
-rw-r--r--tests/benchmarks/quickcontrols/objectcount/tst_objectcount.cpp2
-rw-r--r--tests/libfuzzer/qml/jsapi/evaluate/main.cpp2
-rw-r--r--tests/libfuzzer/qml/qml/qqmlcomponent/create/main.cpp2
-rw-r--r--tests/manual/CMakeLists.txt19
-rw-r--r--tests/manual/accessibility/animation.qml2
-rw-r--r--tests/manual/accessibility/behavior.qml2
-rw-r--r--tests/manual/accessibility/flickable.qml2
-rw-r--r--tests/manual/accessibility/hittest.qml2
-rw-r--r--tests/manual/accessibility/numberanimation.qml2
-rw-r--r--tests/manual/accessibility/textandbuttons.qml2
-rw-r--r--tests/manual/accessibility/transition.qml2
-rw-r--r--tests/manual/cursorChange/main.qml2
-rw-r--r--tests/manual/delegatechooser/CMakeLists.txt42
-rw-r--r--tests/manual/delegatechooser/delegatechooser.pro (renamed from examples/quick/delegatechooser/delegatechooser.pro)0
-rw-r--r--tests/manual/delegatechooser/delegatechooser.qml99
-rw-r--r--tests/manual/delegatechooser/main.cpp4
-rw-r--r--tests/manual/delegatechooser/qml.qrc (renamed from examples/quick/delegatechooser/qml.qrc)0
-rw-r--r--tests/manual/delegatemodel/delegatemodel.qmlproject (renamed from examples/quick/views/delegatemodel/delegatemodel.qmlproject)0
-rw-r--r--tests/manual/delegatemodel/slideshow.qml158
-rw-r--r--tests/manual/e2e/CMakeLists.txt4
-rw-r--r--tests/manual/e2e/qml/CMakeLists.txt6
-rw-r--r--tests/manual/e2e/qml/qmlformat/CMakeLists.txt27
-rw-r--r--tests/manual/e2e/qml/qmlformat/e2e_qmlformat.cpp309
-rw-r--r--tests/manual/fontfeatures/CMakeLists.txt30
-rw-r--r--tests/manual/fontfeatures/main.cpp17
-rw-r--r--tests/manual/fontfeatures/main.qml247
-rw-r--r--tests/manual/frameanimation/main.cpp2
-rw-r--r--tests/manual/frameanimation/main.qml2
-rw-r--r--tests/manual/helloswift/CMakeLists.txt97
-rw-r--r--tests/manual/helloswift/greeter.h43
-rw-r--r--tests/manual/helloswift/hello.swift21
-rw-r--r--tests/manual/helloswift/main.cpp34
-rw-r--r--tests/manual/helloswift/main.qml33
-rw-r--r--tests/manual/helloswift/module.modulemap3
-rw-r--r--tests/manual/highdpi/CMakeLists.txt21
-rw-r--r--tests/manual/highdpi/HighDPIImageProvider/qmldir1
-rw-r--r--tests/manual/highdpi/ImageProvider/qmldir1
-rw-r--r--tests/manual/highdpi/borderimage.qml2
-rw-r--r--tests/manual/highdpi/fillmodes.qml2
-rw-r--r--tests/manual/highdpi/image.qml2
-rw-r--r--tests/manual/highdpi/image2.qml2
-rw-r--r--tests/manual/highdpi/imageprovider.cpp2
-rw-r--r--tests/manual/highdpi/imageprovider.qml4
-rw-r--r--tests/manual/highdpi/imagesize.qml2
-rw-r--r--tests/manual/highdpi/mirror.qml2
-rw-r--r--tests/manual/highdpi/sourcesize.qml2
-rw-r--r--tests/manual/highdpi/svg.qml2
-rw-r--r--tests/manual/httpserver/main.cpp2
-rw-r--r--tests/manual/imagehandler/main.qml2
-rw-r--r--tests/manual/itemparticle/CMakeLists.txt49
-rw-r--r--tests/manual/itemparticle/delegates.qml58
-rw-r--r--tests/manual/itemparticle/images/rocket.png (renamed from examples/quick/particles/itemparticle/images/rocket.png)bin7315 -> 7315 bytes
-rw-r--r--tests/manual/itemparticle/itemparticle.pro10
-rw-r--r--tests/manual/itemparticle/itemparticle.qml262
-rw-r--r--tests/manual/itemparticle/itemparticle.qrc (renamed from examples/quick/particles/itemparticle/itemparticle.qrc)0
-rw-r--r--tests/manual/itemparticle/main.cpp4
-rw-r--r--tests/manual/itemparticle/script.js (renamed from examples/quick/particles/itemparticle/script.js)0
-rw-r--r--tests/manual/listview/main.cpp2
-rw-r--r--tests/manual/listview/main.qml2
-rw-r--r--tests/manual/maskedmousearea/CMakeLists.txt47
-rw-r--r--tests/manual/maskedmousearea/images/cloud_1.png (renamed from examples/quick/customitems/maskedmousearea/images/cloud_1.png)bin49395 -> 49395 bytes
-rw-r--r--tests/manual/maskedmousearea/images/cloud_2.png (renamed from examples/quick/customitems/maskedmousearea/images/cloud_2.png)bin32288 -> 32288 bytes
-rw-r--r--tests/manual/maskedmousearea/images/moon.png (renamed from examples/quick/customitems/maskedmousearea/images/moon.png)bin13263 -> 13263 bytes
-rw-r--r--tests/manual/maskedmousearea/main.cpp15
-rw-r--r--tests/manual/maskedmousearea/maskedmousearea.cpp104
-rw-r--r--tests/manual/maskedmousearea/maskedmousearea.h62
-rw-r--r--tests/manual/maskedmousearea/maskedmousearea.pro (renamed from examples/quick/customitems/maskedmousearea/maskedmousearea.pro)0
-rw-r--r--tests/manual/maskedmousearea/maskedmousearea.qml95
-rw-r--r--tests/manual/maskedmousearea/maskedmousearea.qmlproject (renamed from examples/quick/customitems/maskedmousearea/maskedmousearea.qmlproject)0
-rw-r--r--tests/manual/maskedmousearea/maskedmousearea.qrc (renamed from examples/quick/customitems/maskedmousearea/maskedmousearea.qrc)0
-rw-r--r--tests/manual/mousearea/main.cpp2
-rw-r--r--tests/manual/mousearea/main.qml2
-rw-r--r--tests/manual/mousearea/plainMouseArea.qml2
-rw-r--r--tests/manual/networkaccessmanagerfactory/CMakeLists.txt45
-rw-r--r--tests/manual/networkaccessmanagerfactory/doc/src/networkaccessmanagerfactory.qdoc (renamed from examples/qml/networkaccessmanagerfactory/doc/src/networkaccessmanagerfactory.qdoc)0
-rw-r--r--tests/manual/networkaccessmanagerfactory/main.cpp97
-rw-r--r--tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.pro (renamed from examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.pro)0
-rw-r--r--tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.qmlproject (renamed from examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.qmlproject)0
-rw-r--r--tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.qrc (renamed from examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.qrc)0
-rw-r--r--tests/manual/networkaccessmanagerfactory/view.qml10
-rw-r--r--tests/manual/nodetypes_ng/AtlasedImages.qml2
-rw-r--r--tests/manual/nodetypes_ng/CompressedImages.qml2
-rw-r--r--tests/manual/nodetypes_ng/DistanceFieldText.qml2
-rw-r--r--tests/manual/nodetypes_ng/Images.qml2
-rw-r--r--tests/manual/nodetypes_ng/Layers.qml2
-rw-r--r--tests/manual/nodetypes_ng/LotsOfNodes.qml2
-rw-r--r--tests/manual/nodetypes_ng/LotsOfRects.qml2
-rw-r--r--tests/manual/nodetypes_ng/MoreWindows.qml2
-rw-r--r--tests/manual/nodetypes_ng/MultiClipRects.qml2
-rw-r--r--tests/manual/nodetypes_ng/Painter.qml2
-rw-r--r--tests/manual/nodetypes_ng/Rects.qml2
-rw-r--r--tests/manual/nodetypes_ng/ShaderEffect.qml2
-rw-r--r--tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml2
-rw-r--r--tests/manual/nodetypes_ng/ShaderEffectSource.qml2
-rw-r--r--tests/manual/nodetypes_ng/SimpleRect.qml2
-rw-r--r--tests/manual/nodetypes_ng/Text.qml2
-rw-r--r--tests/manual/nodetypes_ng/main.qml2
-rw-r--r--tests/manual/nodetypes_ng/nodetypes_ng.cpp2
-rw-r--r--tests/manual/painterpathquickshape/1535737773.svg25
-rw-r--r--tests/manual/painterpathquickshape/CMakeLists.txt87
-rw-r--r--tests/manual/painterpathquickshape/ControlPanel.qml416
-rw-r--r--tests/manual/painterpathquickshape/ControlPoint.qml48
-rw-r--r--tests/manual/painterpathquickshape/ControlledShape.qml135
-rw-r--r--tests/manual/painterpathquickshape/CubicShape.qml72
-rw-r--r--tests/manual/painterpathquickshape/DashedStroke.qml24
-rw-r--r--tests/manual/painterpathquickshape/FONTLOG.txt22
-rw-r--r--tests/manual/painterpathquickshape/Graziano.ttfbin0 -> 73364 bytes
-rw-r--r--tests/manual/painterpathquickshape/Intersect.qml141
-rw-r--r--tests/manual/painterpathquickshape/Intersect2.qml136
-rw-r--r--tests/manual/painterpathquickshape/Mussel.qml42
-rw-r--r--tests/manual/painterpathquickshape/OFL-FAQ.txt534
-rw-r--r--tests/manual/painterpathquickshape/OFL.txt93
-rw-r--r--tests/manual/painterpathquickshape/README.md7
-rw-r--r--tests/manual/painterpathquickshape/SimpleShape.qml115
-rw-r--r--tests/manual/painterpathquickshape/SmallPolygon.qml47
-rw-r--r--tests/manual/painterpathquickshape/Squircle.qml60
-rw-r--r--tests/manual/painterpathquickshape/SvgShape.qml116
-rw-r--r--tests/manual/painterpathquickshape/TextShape.qml31
-rw-r--r--tests/manual/painterpathquickshape/arcDirection.qml37
-rw-r--r--tests/manual/painterpathquickshape/arcRotation.qml72
-rw-r--r--tests/manual/painterpathquickshape/background.pngbin0 -> 5397 bytes
-rw-r--r--tests/manual/painterpathquickshape/capStyles.qml46
-rw-r--r--tests/manual/painterpathquickshape/cubicCurve.qml103
-rw-r--r--tests/manual/painterpathquickshape/dashPattern.qml36
-rw-r--r--tests/manual/painterpathquickshape/debugpaintitem.cpp170
-rw-r--r--tests/manual/painterpathquickshape/debugpaintitem.h79
-rw-r--r--tests/manual/painterpathquickshape/debugvisualizationcontroller.cpp46
-rw-r--r--tests/manual/painterpathquickshape/debugvisualizationcontroller.h35
-rw-r--r--tests/manual/painterpathquickshape/ellipticalArcs.qml77
-rw-r--r--tests/manual/painterpathquickshape/fillRules.qml44
-rw-r--r--tests/manual/painterpathquickshape/gradientSpreadModes.qml72
-rw-r--r--tests/manual/painterpathquickshape/hand-print.svg104
-rw-r--r--tests/manual/painterpathquickshape/joinStyles.qml40
-rw-r--r--tests/manual/painterpathquickshape/largeOrSmallArc.qml34
-rw-r--r--tests/manual/painterpathquickshape/linearGradient.qml84
-rw-r--r--tests/manual/painterpathquickshape/main.cpp35
-rw-r--r--tests/manual/painterpathquickshape/main.qml292
-rw-r--r--tests/manual/painterpathquickshape/peace_victory.svg96
-rw-r--r--tests/manual/painterpathquickshape/quadraticCurve.qml47
-rw-r--r--tests/manual/painterpathquickshape/radialGradient.qml106
-rw-r--r--tests/manual/painterpathquickshape/strokeOrFill.qml115
-rw-r--r--tests/manual/painterpathquickshape/svgpathloader.cpp152
-rw-r--r--tests/manual/painterpathquickshape/svgpathloader.h78
-rw-r--r--tests/manual/painterpathquickshape/text.qml21
-rw-r--r--tests/manual/painterpathquickshape/tiger.qml3810
-rw-r--r--tests/manual/painterpathquickshape/zoombox.frag22
-rw-r--r--tests/manual/pointer/content/CheckBox.qml2
-rw-r--r--tests/manual/pointer/content/FakeFlickable.qml102
-rw-r--r--tests/manual/pointer/content/FlashAnimation.qml2
-rw-r--r--tests/manual/pointer/content/MomentumAnimation.qml2
-rw-r--r--tests/manual/pointer/content/MouseAreaButton.qml2
-rw-r--r--tests/manual/pointer/content/MouseAreaSlider.qml2
-rw-r--r--tests/manual/pointer/content/MptaButton.qml2
-rw-r--r--tests/manual/pointer/content/Slider.qml2
-rw-r--r--tests/manual/pointer/content/TapHandlerButton.qml2
-rw-r--r--tests/manual/pointer/content/TextBox.qml2
-rw-r--r--tests/manual/pointer/content/TouchpointFeedbackSprite.qml2
-rw-r--r--tests/manual/pointer/flickablesWithHandlers.qml2
-rw-r--r--tests/manual/pointer/hoverpropagation.qml2
-rw-r--r--tests/manual/pointer/map2.qml2
-rw-r--r--tests/manual/pointer/photosurface.qml88
-rw-r--r--tests/manual/pointer/pinchAndWheel.qml6
-rw-r--r--tests/manual/pointer/pinchDragFlingMPTA.qml2
-rw-r--r--tests/manual/pointer/pinchFlickable.qml2
-rw-r--r--tests/manual/pointer/pinchNullTarget.qml2
-rw-r--r--tests/manual/pointer/pointHandlerOnFlickable.qml2
-rw-r--r--tests/manual/pointer/pointerDrag.qml2
-rw-r--r--tests/manual/pointer/rubberbandOnTable.qml2
-rw-r--r--tests/manual/pointer/tapWithModifiers.qml2
-rw-r--r--tests/manual/qmldom/CMakeLists.txt35
-rw-r--r--tests/manual/qmldom/qmldomloadeditwrite.cpp502
-rw-r--r--tests/manual/qmlextensionplugins/CMakeLists.txt62
-rw-r--r--tests/manual/qmlextensionplugins/doc/images/qml-plugins-example.png (renamed from examples/qml/qmlextensionplugins/doc/images/qml-plugins-example.png)bin15773 -> 15773 bytes
-rw-r--r--tests/manual/qmlextensionplugins/doc/src/qmlpluginex.qdoc (renamed from examples/qml/qmlextensionplugins/doc/src/qmlpluginex.qdoc)0
-rw-r--r--tests/manual/qmlextensionplugins/imports/TimeExample/Clock.qml51
-rw-r--r--tests/manual/qmlextensionplugins/imports/TimeExample/center.png (renamed from examples/qml/qmlextensionplugins/imports/TimeExample/center.png)bin765 -> 765 bytes
-rw-r--r--tests/manual/qmlextensionplugins/imports/TimeExample/clock.png (renamed from examples/qml/qmlextensionplugins/imports/TimeExample/clock.png)bin20653 -> 20653 bytes
-rw-r--r--tests/manual/qmlextensionplugins/imports/TimeExample/hour.png (renamed from examples/qml/qmlextensionplugins/imports/TimeExample/hour.png)bin518 -> 518 bytes
-rw-r--r--tests/manual/qmlextensionplugins/imports/TimeExample/minute.png (renamed from examples/qml/qmlextensionplugins/imports/TimeExample/minute.png)bin528 -> 528 bytes
-rw-r--r--tests/manual/qmlextensionplugins/imports/TimeExample/qmldir (renamed from examples/qml/qmlextensionplugins/imports/TimeExample/qmldir)0
-rw-r--r--tests/manual/qmlextensionplugins/plugin.cpp15
-rw-r--r--tests/manual/qmlextensionplugins/plugins.qml16
-rw-r--r--tests/manual/qmlextensionplugins/plugins.qmlproject (renamed from examples/qml/qmlextensionplugins/plugins.qmlproject)0
-rw-r--r--tests/manual/qmlextensionplugins/qmlextensionplugins.pro (renamed from examples/qml/qmlextensionplugins/qmlextensionplugins.pro)0
-rw-r--r--tests/manual/qmlextensionplugins/timemodel.cpp51
-rw-r--r--tests/manual/qmlextensionplugins/timemodel.h64
-rw-r--r--tests/manual/qmlls-cmake-builds/CMakeLists.txt40
-rw-r--r--tests/manual/qmlls-cmake-builds/Main.qml14
-rw-r--r--tests/manual/qmlls-cmake-builds/README.md16
-rw-r--r--tests/manual/qmlls-cmake-builds/helloworld.cpp8
-rw-r--r--tests/manual/qmlls-cmake-builds/helloworld.h29
-rw-r--r--tests/manual/qmlls-cmake-builds/main.cpp22
-rw-r--r--tests/manual/qmllsformatter/test.qml45
-rw-r--r--tests/manual/qmltest/CMakeLists.txt47
-rw-r--r--tests/manual/qmltest/qmltest.pro (renamed from examples/qmltest/qmltest/qmltest.pro)0
-rw-r--r--tests/manual/qmltest/tst_basic.qml41
-rw-r--r--tests/manual/qmltest/tst_item.qml20
-rw-r--r--tests/manual/qmltest/tst_qmltest.cpp5
-rw-r--r--tests/manual/qmltypememory/TestPlugin/plugin.cpp2
-rw-r--r--tests/manual/qmltypememory/TestType.qml2
-rw-r--r--tests/manual/qmltypememory/main.cpp2
-rw-r--r--tests/manual/qmltypememory/main.qml2
-rw-r--r--tests/manual/qmltypememory/testdriver.cpp2
-rw-r--r--tests/manual/qmltypememory/testdriver.h2
-rw-r--r--tests/manual/quickcontrols/CMakeLists.txt7
-rw-r--r--tests/manual/quickcontrols/buttons/ButtonLoader.qml2
-rw-r--r--tests/manual/quickcontrols/buttons/buttons.cpp2
-rw-r--r--tests/manual/quickcontrols/buttons/buttons.qml2
-rw-r--r--tests/manual/quickcontrols/fonts/main.cpp2
-rw-r--r--tests/manual/quickcontrols/fonts/main.qml44
-rw-r--r--tests/manual/quickcontrols/gifs/CMakeLists.txt15
-rw-r--r--tests/manual/quickcontrols/gifs/capturedevent.cpp2
-rw-r--r--tests/manual/quickcontrols/gifs/capturedevent.h2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-busyindicator.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-flat.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-highlighted.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-button.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox-tristate.qml5
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox.qml5
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate-tristate.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-combobox.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-delaybutton.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-no-wrap.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-wrap.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-drawer.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-itemdelegate.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-menu.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar-indeterminate.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiobutton.qml5
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiodelegate.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-rangeslider.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar-snap.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollindicator.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider-snap.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-pop.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-push.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-replace.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-unwind.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-behind.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-leading-trailing.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipeview.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-switch.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-switchdelegate.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-tabbar.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/data/qtquickcontrols-tumbler-wrap.qml2
-rw-r--r--tests/manual/quickcontrols/gifs/eventcapturer.cpp2
-rw-r--r--tests/manual/quickcontrols/gifs/eventcapturer.h2
-rw-r--r--tests/manual/quickcontrols/gifs/gifrecorder.cpp6
-rw-r--r--tests/manual/quickcontrols/gifs/gifrecorder.h2
-rw-r--r--tests/manual/quickcontrols/gifs/tst_gifs.cpp2
-rw-r--r--tests/manual/quickcontrols/headerview/main.cpp3
-rw-r--r--tests/manual/quickcontrols/headerview/main.qml4
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/CMakeLists.txt187
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/icons.qrc (renamed from examples/quickcontrols/imagine/musicplayer/icons/icons.qrc)0
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/bluetooth.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/bluetooth.png)bin556 -> 556 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cart.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cart.png)bin425 -> 425 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cloud.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cloud.png)bin525 -> 525 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/favorite.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/favorite.png)bin379 -> 379 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/filter.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/filter.png)bin441 -> 441 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/folder.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/folder.png)bin279 -> 279 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/message.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/message.png)bin466 -> 466 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/music.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/music.png)bin454 -> 454 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/next.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/next.png)bin201 -> 201 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/pause.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/pause.png)bin141 -> 141 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/power.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/power.png)bin710 -> 710 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/previous.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/previous.png)bin203 -> 203 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/repeat.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/repeat.png)bin328 -> 328 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/save.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/save.png)bin419 -> 419 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/settings.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/settings.png)bin499 -> 499 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/shuffle.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/shuffle.png)bin366 -> 366 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/stop.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/stop.png)bin157 -> 157 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/bluetooth.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/bluetooth.png)bin1202 -> 1202 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cart.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cart.png)bin782 -> 782 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cloud.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cloud.png)bin1003 -> 1003 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/favorite.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/favorite.png)bin744 -> 744 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/filter.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/filter.png)bin872 -> 872 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/folder.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/folder.png)bin437 -> 437 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/grid.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/grid.png)bin177 -> 177 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/message.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/message.png)bin850 -> 850 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/music.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/music.png)bin842 -> 842 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/next.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/next.png)bin354 -> 354 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/pause.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/pause.png)bin163 -> 163 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/power.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/power.png)bin1329 -> 1329 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/previous.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/previous.png)bin436 -> 436 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/repeat.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/repeat.png)bin634 -> 634 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/save.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/save.png)bin496 -> 496 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/settings.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/settings.png)bin960 -> 960 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/shuffle.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/shuffle.png)bin724 -> 724 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/stop.png (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/stop.png)bin242 -> 242 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/icons.svg (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/icons.svg)0
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/index.theme (renamed from examples/quickcontrols/imagine/musicplayer/icons/musicplayer/index.theme)0
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/images/album-cover.jpg (renamed from examples/quickcontrols/imagine/musicplayer/images/album-cover.jpg)bin917863 -> 917863 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/applicationwindow-background.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/applicationwindow-background.png)bin82 -> 82 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered.9.png)bin1446 -> 1446 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered@2x.9.png)bin4150 -> 4150 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked.9.png)bin1353 -> 1353 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked@2x.9.png)bin4049 -> 4049 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled.9.png)bin543 -> 543 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled@2x.9.png)bin1509 -> 1509 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered.9.png)bin1444 -> 1444 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered@2x.9.png)bin3855 -> 3855 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed.9.png)bin1437 -> 1437 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed@2x.9.png)bin4048 -> 4048 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background.9.png)bin1368 -> 1368 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background@2x.9.png)bin3813 -> 3813 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered.9.png)bin4220 -> 4220 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered@2x.9.png)bin15135 -> 15135 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open.9.png)bin4197 -> 4197 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open@2x.9.png)bin14766 -> 14766 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed.9.png)bin4254 -> 4254 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed@2x.9.png)bin14995 -> 14995 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background.9.png)bin4283 -> 4283 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background@2x.9.png)bin14357 -> 14357 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered.png)bin390 -> 390 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered@2x.png)bin961 -> 961 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open.png)bin415 -> 415 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open@2x.png)bin929 -> 929 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed.png)bin373 -> 373 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed@2x.png)bin955 -> 955 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator.png)bin381 -> 381 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator@2x.png)bin970 -> 970 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup.9.png)bin2437 -> 2437 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup@2x.9.png)bin5349 -> 5349 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered.png)bin11658 -> 11658 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered@2x.png)bin36836 -> 36836 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed.png)bin12061 -> 12061 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed@2x.png)bin39021 -> 39021 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background.png)bin10640 -> 10640 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background@2x.png)bin34791 -> 34791 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed.png)bin408 -> 408 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed@2x.png)bin963 -> 963 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle.png)bin360 -> 360 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle@2x.png)bin736 -> 736 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/frame-background.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/frame-background.9.png)bin236 -> 236 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/frame-background@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/frame-background@2x.9.png)bin419 -> 419 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/imagine-assets.qrc (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/imagine-assets.qrc)0
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked.9.png)bin1121 -> 1121 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked@2x.9.png)bin3119 -> 3119 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled.9.png)bin127 -> 127 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled@2x.9.png)bin144 -> 144 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered.9.png)bin1120 -> 1120 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered@2x.9.png)bin2579 -> 2579 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed.9.png)bin1116 -> 1116 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed@2x.9.png)bin3140 -> 3140 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background.9.png)bin127 -> 127 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background@2x.9.png)bin144 -> 144 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered.png)bin1643 -> 1643 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered@2x.png)bin4863 -> 4863 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked.png)bin1506 -> 1506 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked@2x.png)bin4389 -> 4389 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled.png)bin955 -> 955 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled@2x.png)bin2197 -> 2197 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered.png)bin1470 -> 1470 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered@2x.png)bin4373 -> 4373 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed.png)bin1502 -> 1502 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed@2x.png)bin4450 -> 4450 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background.png)bin1415 -> 1415 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background@2x.png)bin4224 -> 4224 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered.png)bin82 -> 82 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered@2x.png)bin83 -> 83 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed.png)bin82 -> 82 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed@2x.png)bin83 -> 83 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive.png)bin82 -> 82 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive@2x.png)bin83 -> 83 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled.9.png)bin222 -> 222 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled@2x.9.png)bin470 -> 470 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal.9.png)bin235 -> 235 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal@2x.9.png)bin489 -> 489 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled.9.png)bin239 -> 239 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled@2x.9.png)bin447 -> 447 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical.9.png)bin226 -> 226 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical@2x.9.png)bin475 -> 475 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled.png)bin523 -> 523 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled@2x.png)bin947 -> 947 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered.png)bin1066 -> 1066 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered@2x.png)bin2866 -> 2866 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle.png)bin1048 -> 1048 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle@2x.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle@2x.png)bin2852 -> 2852 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal.9.png)bin235 -> 235 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal@2x.9.png)bin489 -> 489 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled.9.png)bin226 -> 226 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled@2x.9.png)bin475 -> 475 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical.9.png)bin226 -> 226 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical@2x.9.png)bin475 -> 475 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled.9.png)bin252 -> 252 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled@2x.9.png)bin445 -> 445 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background.9.png)bin256 -> 256 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background@2x.9.png)bin481 -> 481 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background.9.png)bin240 -> 240 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background@2x.9.png)bin483 -> 483 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered.9.png)bin1174 -> 1174 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered@2x.9.png)bin3156 -> 3156 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked.9.png)bin1106 -> 1106 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked@2x.9.png)bin3141 -> 3141 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered.9.png)bin1077 -> 1077 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered@2x.9.png)bin3026 -> 3026 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed.9.png)bin1137 -> 1137 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed@2x.9.png)bin3158 -> 3158 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background.9.png)bin1079 -> 1079 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background@2x.9.png)bin2737 -> 2737 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background.9.png)bin2413 -> 2413 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background@2x.9.png (renamed from examples/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background@2x.9.png)bin4677 -> 4677 bytes
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/musicplayer.cpp24
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/musicplayer.pro (renamed from examples/quickcontrols/imagine/musicplayer/musicplayer.pro)0
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/musicplayer.qml417
-rw-r--r--tests/manual/quickcontrols/imagine/musicplayer/qtquickcontrols2.conf8
-rw-r--r--tests/manual/quickcontrols/manual.pro1
-rw-r--r--tests/manual/quickcontrols/material/CMakeLists.txt2
-rw-r--r--tests/manual/quickcontrols/material/Constants.qml2
-rw-r--r--tests/manual/quickcontrols/material/material.cpp2
-rw-r--r--tests/manual/quickcontrols/material/material.qml7
-rw-r--r--tests/manual/quickcontrols/material/pages/ButtonPage.qml13
-rw-r--r--tests/manual/quickcontrols/material/pages/DelayButtonPage.qml3
-rw-r--r--tests/manual/quickcontrols/material/pages/RoundButtonPage.qml6
-rw-r--r--tests/manual/quickcontrols/material/pages/SwitchPage.qml10
-rw-r--r--tests/manual/quickcontrols/material/pages/TextAreaPage.qml194
-rw-r--r--tests/manual/quickcontrols/material/pages/TextFieldPage.qml118
-rw-r--r--tests/manual/quickcontrols/nativestyle/Buttons.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CheckBoxes.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/ComboBoxes.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/ControlContainer.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomButtons.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomCheckBoxes.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomComboBoxes.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomDials.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomFrames.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomProgressBars.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomRadioButtons.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomScrollBars.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomSliders.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomSpinBoxes.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomTextAreas.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/CustomTextFields.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/Dials.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/Frames.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/ProgressBars.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/RadioButtons.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/ScrollBars.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/Sliders.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/SlidersMini.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/SlidersSmall.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/SpinBoxes.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/TextAreas.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/TextFields.qml2
-rw-r--r--tests/manual/quickcontrols/nativestyle/main.cpp2
-rw-r--r--tests/manual/quickcontrols/nativestyle/main.qml2
-rw-r--r--tests/manual/quickcontrols/qquickdialog/CustomDialog.qml2
-rw-r--r--tests/manual/quickcontrols/qquickdialog/DialogLabel.qml2
-rw-r--r--tests/manual/quickcontrols/qquickdialog/Marker.qml2
-rw-r--r--tests/manual/quickcontrols/qquickdialog/qquickdialog.cpp2
-rw-r--r--tests/manual/quickcontrols/qquickdialog/qquickdialog.qml2
-rw-r--r--tests/manual/quickcontrols/screenshots/screenshots.cpp2
-rw-r--r--tests/manual/quickcontrols/screenshots/screenshots.qml2
-rw-r--r--tests/manual/quickcontrols/shared/FontAwesome.qml2
-rw-r--r--tests/manual/quickcontrols/shared/FontAwesomeIcon.qml2
-rw-r--r--tests/manual/quickcontrols/sidepanel/CMakeLists.txt (renamed from examples/quickcontrols/sidepanel/CMakeLists.txt)0
-rw-r--r--tests/manual/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-landscape.png (renamed from examples/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-landscape.png)bin55216 -> 55216 bytes
-rw-r--r--tests/manual/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-portrait.png (renamed from examples/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-portrait.png)bin32060 -> 32060 bytes
-rw-r--r--tests/manual/quickcontrols/sidepanel/doc/src/qtquickcontrols-sidepanel.qdoc (renamed from examples/quickcontrols/sidepanel/doc/src/qtquickcontrols-sidepanel.qdoc)0
-rw-r--r--tests/manual/quickcontrols/sidepanel/images/qt-logo.png (renamed from examples/quickcontrols/sidepanel/images/qt-logo.png)bin1301 -> 1301 bytes
-rw-r--r--tests/manual/quickcontrols/sidepanel/images/qt-logo@2x.png (renamed from examples/quickcontrols/sidepanel/images/qt-logo@2x.png)bin2611 -> 2611 bytes
-rw-r--r--tests/manual/quickcontrols/sidepanel/images/qt-logo@3x.png (renamed from examples/quickcontrols/sidepanel/images/qt-logo@3x.png)bin4155 -> 4155 bytes
-rw-r--r--tests/manual/quickcontrols/sidepanel/images/qt-logo@4x.png (renamed from examples/quickcontrols/sidepanel/images/qt-logo@4x.png)bin5916 -> 5916 bytes
-rw-r--r--tests/manual/quickcontrols/sidepanel/qtquickcontrols2.conf2
-rw-r--r--tests/manual/quickcontrols/sidepanel/sidepanel.cpp17
-rw-r--r--tests/manual/quickcontrols/sidepanel/sidepanel.pro (renamed from examples/quickcontrols/sidepanel/sidepanel.pro)0
-rw-r--r--tests/manual/quickcontrols/sidepanel/sidepanel.qml132
-rw-r--r--tests/manual/quickcontrols/styles-cover-flow/CoverFlowDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/styles-cover-flow/CoverFlowPath.qml2
-rw-r--r--tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.cpp2
-rw-r--r--tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.qml2
-rw-r--r--tests/manual/quickcontrols/styles/styles.cpp2
-rw-r--r--tests/manual/quickcontrols/styles/styles.qml151
-rw-r--r--tests/manual/quickcontrols/swipedelegate/CloseOnCompletedWorks.qml2
-rw-r--r--tests/manual/quickcontrols/swipetoremove/CMakeLists.txt (renamed from examples/quickcontrols/swipetoremove/CMakeLists.txt)0
-rw-r--r--tests/manual/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.gif (renamed from examples/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.gif)bin371365 -> 371365 bytes
-rw-r--r--tests/manual/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.png (renamed from examples/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.png)bin22511 -> 22511 bytes
-rw-r--r--tests/manual/quickcontrols/swipetoremove/doc/src/qtquickcontrols-swipetoremove.qdoc (renamed from examples/quickcontrols/swipetoremove/doc/src/qtquickcontrols-swipetoremove.qdoc)0
-rw-r--r--tests/manual/quickcontrols/swipetoremove/fonts/LICENSE.txt (renamed from examples/quickcontrols/swipetoremove/fonts/LICENSE.txt)0
-rw-r--r--tests/manual/quickcontrols/swipetoremove/fonts/fontello.ttf (renamed from examples/quickcontrols/swipetoremove/fonts/fontello.ttf)bin5164 -> 5164 bytes
-rw-r--r--tests/manual/quickcontrols/swipetoremove/swipetoremove.cpp20
-rw-r--r--tests/manual/quickcontrols/swipetoremove/swipetoremove.pro (renamed from examples/quickcontrols/swipetoremove/swipetoremove.pro)0
-rw-r--r--tests/manual/quickcontrols/swipetoremove/swipetoremove.qml130
-rw-r--r--tests/manual/quickcontrols/systemtrayicon/systemtrayicon.cpp2
-rw-r--r--tests/manual/quickcontrols/systemtrayicon/systemtrayicon.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/+Imagine/ApplicationWindow.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/+Imagine/ContentPane.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/+Imagine/Dialog.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/+Imagine/Menu.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/+Imagine/ToolBar.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/ApplicationWindow.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/ColorEditor.qml3
-rw-r--r--tests/manual/quickcontrols/testbench/ContentPane.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/ControlContainer.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/Dialog.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/ExampleContainer.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/Menu.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/SettingsDialog.qml9
-rw-r--r--tests/manual/quickcontrols/testbench/ToolBar.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/assetfixer.cpp2
-rw-r--r--tests/manual/quickcontrols/testbench/assetfixer.h2
-rw-r--r--tests/manual/quickcontrols/testbench/clipboard.cpp3
-rw-r--r--tests/manual/quickcontrols/testbench/clipboard.h2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/BusyIndicator.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Button.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/CheckBox.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml8
-rw-r--r--tests/manual/quickcontrols/testbench/controls/ComboBox.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/DelayButton.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Dial.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Dialog.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Frame.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/GroupBox.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Label.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Menu.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/MenuBar.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Page.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/PageIndicator.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Pane.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/ProgressBar.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/RadioButton.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml10
-rw-r--r--tests/manual/quickcontrols/testbench/controls/RangeSlider.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/RoundButton.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/ScrollBar.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/ScrollIndicator.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Slider.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/SpinBox.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/SplitView.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Switch.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/TabBar.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/TextArea.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/TextField.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/ToolBar.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/ToolTip.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/controls/Tumbler.qml2
-rw-r--r--tests/manual/quickcontrols/testbench/directoryvalidator.cpp2
-rw-r--r--tests/manual/quickcontrols/testbench/directoryvalidator.h2
-rw-r--r--tests/manual/quickcontrols/testbench/fonts.qrc2
-rw-r--r--tests/manual/quickcontrols/testbench/main.cpp2
-rw-r--r--tests/manual/quickcontrols/testbench/testbench.qml2
-rw-r--r--tests/manual/quickcontrols/viewinqwidget/main.cpp2
-rw-r--r--tests/manual/quickcontrols/viewinqwidget/main.qml2
-rw-r--r--tests/manual/quickdialogs/dialogs/ColorDialogPage.qml21
-rw-r--r--tests/manual/quickdialogs/dialogs/FileDialogPage.qml18
-rw-r--r--tests/manual/quickdialogs/dialogs/FolderDialogPage.qml13
-rw-r--r--tests/manual/quickdialogs/dialogs/FontDialogPage.qml14
-rw-r--r--tests/manual/quickdialogs/dialogs/MessageDialogPage.qml30
-rw-r--r--tests/manual/quickdialogs/dialogs/StringListView.qml2
-rw-r--r--tests/manual/quickdialogs/dialogs/Theme.qml2
-rw-r--r--tests/manual/quickdialogs/dialogs/dialogs.cpp2
-rw-r--r--tests/manual/quickdialogs/dialogs/dialogs.qml4
-rw-r--r--tests/manual/quicklayouts/gridlayout/uniformColumnTest.qml111
-rw-r--r--tests/manual/quicklayouts/layoutItemProxy/LayoutChooser.qml52
-rw-r--r--tests/manual/quicklayouts/layoutItemProxy/ms-rearchitect.qml235
-rw-r--r--tests/manual/quicklayouts/layoutItemProxy/ms-replace.qml137
-rw-r--r--tests/manual/quicklayouts/layoutItemProxy/ms-showhide.qml155
-rw-r--r--tests/manual/quicklayouts/layoutItemProxy/scaffold.qml202
-rw-r--r--tests/manual/quickvectorimage/CMakeLists.txt1
-rw-r--r--tests/manual/quickvectorimage/vectorimage/CMakeLists.txt30
-rw-r--r--tests/manual/quickvectorimage/vectorimage/Main.qml37
-rw-r--r--tests/manual/quickvectorimage/vectorimage/res/spheres.svg72
-rw-r--r--tests/manual/quickvectorimage/vectorimage/vectorimage.cpp22
-rw-r--r--tests/manual/quickwidgetviewer/main.cpp2
-rw-r--r--tests/manual/rectangle/corners.qml221
-rw-r--r--tests/manual/righttoleft/CMakeLists.txt3
-rw-r--r--tests/manual/righttoleft/layoutdirection/CMakeLists.txt31
-rw-r--r--tests/manual/righttoleft/layoutdirection/layoutdirection.cpp18
-rw-r--r--tests/manual/righttoleft/layoutdirection/layoutdirection.pro5
-rw-r--r--tests/manual/righttoleft/layoutdirection/layoutdirection.qml220
-rw-r--r--tests/manual/righttoleft/layoutdirection/layoutdirection.qrc6
-rw-r--r--tests/manual/righttoleft/layoutmirroring/CMakeLists.txt31
-rw-r--r--tests/manual/righttoleft/layoutmirroring/layoutmirroring.cpp18
-rw-r--r--tests/manual/righttoleft/layoutmirroring/layoutmirroring.pro5
-rw-r--r--tests/manual/righttoleft/layoutmirroring/layoutmirroring.qml197
-rw-r--r--tests/manual/righttoleft/layoutmirroring/layoutmirroring.qrc6
-rw-r--r--tests/manual/righttoleft/textalignment/CMakeLists.txt30
-rw-r--r--tests/manual/righttoleft/textalignment/textalignment.cpp18
-rw-r--r--tests/manual/righttoleft/textalignment/textalignment.pro5
-rw-r--r--tests/manual/righttoleft/textalignment/textalignment.qml472
-rw-r--r--tests/manual/righttoleft/textalignment/textalignment.qrc5
-rw-r--r--tests/manual/scalablepath/ShapeTestScale.qml2
-rw-r--r--tests/manual/scalablepath/main.cpp2
-rw-r--r--tests/manual/scalablepath/main.qml2
-rw-r--r--tests/manual/shapestest/main.cpp2
-rw-r--r--tests/manual/shapestest/shapestest.qml2
-rw-r--r--tests/manual/shared/Button.qml50
-rw-r--r--tests/manual/shared/CMakeLists.txt62
-rw-r--r--tests/manual/shared/CheckBox.qml59
-rw-r--r--tests/manual/shared/FlickrRssModel.qml45
-rw-r--r--tests/manual/shared/Images.qml14
-rw-r--r--tests/manual/shared/Label.qml9
-rw-r--r--tests/manual/shared/QtBundleQmlModuleForMacOS.cmake35
-rw-r--r--tests/manual/shared/Slider.qml82
-rw-r--r--tests/manual/shared/TabSet.qml69
-rw-r--r--tests/manual/shared/TextField.qml43
-rw-r--r--tests/manual/shared/images/back.pngbin0 -> 1590 bytes
-rw-r--r--tests/manual/shared/images/checkmark.pngbin0 -> 809 bytes
-rw-r--r--tests/manual/shared/images/next.pngbin0 -> 1371 bytes
-rw-r--r--tests/manual/shared/images/qt-logo.pngbin0 -> 9186 bytes
-rw-r--r--tests/manual/shared/images/slider_handle.pngbin0 -> 887 bytes
-rw-r--r--tests/manual/shared/images/tab.pngbin0 -> 309 bytes
-rw-r--r--tests/manual/shared/qmldir11
-rw-r--r--tests/manual/shared/quick_shared.qrc15
-rw-r--r--tests/manual/shared/shared.h43
-rw-r--r--tests/manual/shared/shared.qrc21
-rw-r--r--tests/manual/shortcuts/shortcuts.qml3
-rw-r--r--tests/manual/svg/CMakeLists.txt74
-rw-r--r--tests/manual/svg/QmlGenerator.qml26
-rw-r--r--tests/manual/svg/SvgImage.qml17
-rw-r--r--tests/manual/svg/data/image/1.svg3
-rw-r--r--tests/manual/svg/data/image/2.svg3
-rw-r--r--tests/manual/svg/data/image/3.svg6
-rw-r--r--tests/manual/svg/data/image/data.pngbin0 -> 6901 bytes
-rw-r--r--tests/manual/svg/data/image/qtlogo.pngbin0 -> 6901 bytes
-rw-r--r--tests/manual/svg/data/painting/fill_color.svg7
-rw-r--r--tests/manual/svg/data/painting/fill_gradient.svg15
-rw-r--r--tests/manual/svg/data/painting/fill_stroke.svg3
-rw-r--r--tests/manual/svg/data/painting/stroke_color.svg7
-rw-r--r--tests/manual/svg/data/styling/stroking_capStyle_shapes_1.svg11
-rw-r--r--tests/manual/svg/data/styling/stroking_capStyle_shapes_2.svg12
-rw-r--r--tests/manual/svg/data/styling/stroking_dash.svg10
-rw-r--r--tests/manual/svg/data/styling/stroking_joinStyle_shapes_1.svg12
-rw-r--r--tests/manual/svg/data/text/1.svg44
-rw-r--r--tests/manual/svg/data/text/2.svg45
-rw-r--r--tests/manual/svg/data/text/3.svg20
-rw-r--r--tests/manual/svg/data/text/4.svg20
-rw-r--r--tests/manual/svg/data/text/5.svg44
-rw-r--r--tests/manual/svg/main.cpp14
-rw-r--r--tests/manual/svg/mainwindow.cpp142
-rw-r--r--tests/manual/svg/mainwindow.h47
-rw-r--r--tests/manual/svg/mainwindow.ui292
-rw-r--r--tests/manual/svg/svgmanager.cpp96
-rw-r--r--tests/manual/svg/svgmanager.h82
-rw-r--r--tests/manual/svg/svgpainter.cpp81
-rw-r--r--tests/manual/svg/svgpainter.h49
-rw-r--r--tests/manual/syntaxhighlighter/app.qml9
-rw-r--r--tests/manual/syntaxhighlighter/documenthighlighter.cpp2
-rw-r--r--tests/manual/syntaxhighlighter/documenthighlighter.h2
-rw-r--r--tests/manual/syntaxhighlighter/main.cpp2
-rw-r--r--tests/manual/tableview/abstracttablemodel/Button.qml2
-rw-r--r--tests/manual/tableview/abstracttablemodel/main.cpp2
-rw-r--r--tests/manual/tableview/abstracttablemodel/main.qml79
-rw-r--r--tests/manual/tableview/imagetiling/imageTiling.qml2
-rw-r--r--tests/manual/tableview/listmodel/main.cpp2
-rw-r--r--tests/manual/tableview/listmodel/main.qml2
-rw-r--r--tests/manual/tableview/storagemodel/main.cpp2
-rw-r--r--tests/manual/tableview/storagemodel/main.qml2
-rw-r--r--tests/manual/tableview/storagemodel/storagemodel.cpp2
-rw-r--r--tests/manual/tableview/storagemodel/storagemodel.h2
-rw-r--r--tests/manual/tableview/tablemodel/form/RowForm.qml2
-rw-r--r--tests/manual/tableview/tablemodel/form/main.cpp2
-rw-r--r--tests/manual/tableview/tablemodel/form/main.qml2
-rw-r--r--tests/manual/tableview/tablemodel/json/main.cpp2
-rw-r--r--tests/manual/tableview/tablemodel/json/main.qml2
-rw-r--r--tests/manual/text/SignalIndicator.qml2
-rw-r--r--tests/manual/text/main.cpp2
-rw-r--r--tests/manual/text/textInputPropertiesAndSignals.qml11
-rw-r--r--tests/manual/textrendering/CMakeLists.txt40
-rw-r--r--tests/manual/textrendering/main.cpp26
-rw-r--r--tests/manual/textrendering/main.qml108
-rw-r--r--tests/manual/textselection/pics/endHandle.png (renamed from examples/quick/text/textselection/pics/endHandle.png)bin90 -> 90 bytes
-rw-r--r--tests/manual/textselection/pics/endHandle.sci (renamed from examples/quick/text/textselection/pics/endHandle.sci)0
-rw-r--r--tests/manual/textselection/pics/startHandle.png (renamed from examples/quick/text/textselection/pics/startHandle.png)bin88 -> 88 bytes
-rw-r--r--tests/manual/textselection/pics/startHandle.sci (renamed from examples/quick/text/textselection/pics/startHandle.sci)0
-rw-r--r--tests/manual/textselection/textselection.qml201
-rw-r--r--tests/manual/threading/CMakeLists.txt2
-rw-r--r--tests/manual/threading/threadedlistmodel/CMakeLists.txt33
-rw-r--r--tests/manual/threading/threadedlistmodel/dataloader.mjs12
-rw-r--r--tests/manual/threading/threadedlistmodel/threadedlistmodel.cpp19
-rw-r--r--tests/manual/threading/threadedlistmodel/threadedlistmodel.pro6
-rw-r--r--tests/manual/threading/threadedlistmodel/threadedlistmodel.qrc5
-rw-r--r--tests/manual/threading/threadedlistmodel/timedisplay.qml42
-rw-r--r--tests/manual/threading/workerscript/CMakeLists.txt34
-rw-r--r--tests/manual/threading/workerscript/Spinner.qml51
-rw-r--r--tests/manual/threading/workerscript/workerscript.cpp19
-rw-r--r--tests/manual/threading/workerscript/workerscript.mjs27
-rw-r--r--tests/manual/threading/workerscript/workerscript.pro6
-rw-r--r--tests/manual/threading/workerscript/workerscript.qml60
-rw-r--r--tests/manual/threading/workerscript/workerscript.qrc7
-rw-r--r--tests/manual/touch/CMakeLists.txt19
-rw-r--r--tests/manual/touch/basic-flickable.qml33
-rw-r--r--tests/manual/touch/bearwhack/AugmentedTouchPoint.qml35
-rw-r--r--tests/manual/touch/bearwhack/Bear0.png (renamed from examples/quick/touchinteraction/multipointtouch/Bear0.png)bin14359 -> 14359 bytes
-rw-r--r--tests/manual/touch/bearwhack/Bear1.png (renamed from examples/quick/touchinteraction/multipointtouch/Bear1.png)bin99378 -> 99378 bytes
-rw-r--r--tests/manual/touch/bearwhack/Bear2.png (renamed from examples/quick/touchinteraction/multipointtouch/Bear2.png)bin80759 -> 80759 bytes
-rw-r--r--tests/manual/touch/bearwhack/Bear3.png (renamed from examples/quick/touchinteraction/multipointtouch/Bear3.png)bin21122 -> 21122 bytes
-rw-r--r--tests/manual/touch/bearwhack/BearB.png (renamed from examples/quick/touchinteraction/multipointtouch/BearB.png)bin16505 -> 16505 bytes
-rw-r--r--tests/manual/touch/bearwhack/BearWhackParticleSystem.qml146
-rw-r--r--tests/manual/touch/bearwhack/bearwhack.qml97
-rw-r--r--tests/manual/touch/bearwhack/blur-circle.pngbin0 -> 4279 bytes
-rw-r--r--tests/manual/touch/bearwhack/blur-circle3.png (renamed from examples/quick/touchinteraction/multipointtouch/blur-circle3.png)bin5148 -> 5148 bytes
-rw-r--r--tests/manual/touch/bearwhack/heart-blur.png (renamed from examples/quick/touchinteraction/multipointtouch/heart-blur.png)bin5406 -> 5406 bytes
-rw-r--r--tests/manual/touch/bearwhack/title.png (renamed from examples/quick/touchinteraction/multipointtouch/title.png)bin76389 -> 76389 bytes
-rw-r--r--tests/manual/touch/flickresize.qml62
-rw-r--r--tests/manual/touch/flicktext.qml2
-rw-r--r--tests/manual/touch/main.cpp2
-rw-r--r--tests/manual/touch/main.qml4
-rw-r--r--tests/manual/touch/mpta-crosshairs.qml2
-rw-r--r--tests/manual/touch/qt-logo.jpg (renamed from examples/quick/touchinteraction/pincharea/qt-logo.jpg)bin40886 -> 40886 bytes
-rw-r--r--tests/manual/treeview/sidebyside/CMakeLists.txt2
-rw-r--r--tests/manual/treeview/sidebyside/data/treeview.qml50
-rw-r--r--tests/manual/treeview/sidebyside/main.cpp3
-rw-r--r--tests/manual/treeview/sidebyside/testmodel.cpp2
-rw-r--r--tests/manual/treeview/sidebyside/testmodel.h2
-rw-r--r--tests/manual/v4/auto/executableallocator/tst_executableallocator.cpp2
-rw-r--r--tests/manual/wasm/CMakeLists.txt1
-rw-r--r--tests/manual/wasm/a11y/CMakeLists.txt1
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/AboutDialog.qml50
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/CMakeLists.txt51
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/MeetingInviteesModel.qml7
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/MeetingInviteesPage.qml139
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/MeetingSchedulerPage.qml163
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/MeetingSummary.qml117
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/MeetingTabs.qml95
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/WasmListView.qml95
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/WasmMenu.qml54
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/WasmToolBar.qml37
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/basic_items.html24
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/main.cpp15
-rw-r--r--tests/manual/wasm/a11y/qml_basic_item/main.qml97
-rw-r--r--tests/manual/window/CMakeLists.txt4
-rw-r--r--tests/manual/window/rotation/CMakeLists.txt28
-rw-r--r--tests/manual/window/rotation/Main.qml124
-rw-r--r--tests/manual/window/rotation/main.cpp18
-rw-r--r--tests/manual/windowembedding/CMakeLists.txt86
-rw-r--r--tests/manual/windowembedding/QtLogo.qml29
-rw-r--r--tests/manual/windowembedding/examples/clipping.qml33
-rw-r--r--tests/manual/windowembedding/examples/hdr.qml41
-rw-r--r--tests/manual/windowembedding/examples/hdrtest.mp4bin0 -> 76694 bytes
-rw-r--r--tests/manual/windowembedding/examples/map.qml26
-rw-r--r--tests/manual/windowembedding/examples/stacking.qml50
-rw-r--r--tests/manual/windowembedding/examples/transform.qml42
-rw-r--r--tests/manual/windowembedding/examples/video.qml27
-rw-r--r--tests/manual/windowembedding/examples/webview.qml18
-rw-r--r--tests/manual/windowembedding/examples/widget.qml27
-rw-r--r--tests/manual/windowembedding/main.cpp111
-rw-r--r--tests/manual/windowembedding/qtlogo-green.pngbin0 -> 6901 bytes
-rw-r--r--tests/manual/windowembedding/qtlogo-white.pngbin0 -> 4441 bytes
-rw-r--r--tests/manual/windowembedding/windowembedding.qml24
-rw-r--r--tests/manual/windowembedding/windowembedding.qmlproject17
-rw-r--r--tests/system/sys_animatedsprite.qtt2
-rw-r--r--tests/system/sys_elements.qtt2
-rw-r--r--tests/system/sys_listview.qtt2
-rw-r--r--tests/system/sys_text.qtt2
-rw-r--r--tests/system/sys_textedit.qtt2
-rw-r--r--tests/system/sys_textinput.qtt2
-rw-r--r--tests/testapplications/animatedsprite/animatedsprite.qml2
-rw-r--r--tests/testapplications/animatedsprite/animatedspriteadvance.qml2
-rw-r--r--tests/testapplications/elements/content/AffectorElement.qml2
-rw-r--r--tests/testapplications/elements/content/AnimatedImageElement.qml2
-rw-r--r--tests/testapplications/elements/content/AppContainer.qml2
-rw-r--r--tests/testapplications/elements/content/BorderImageElement.qml2
-rw-r--r--tests/testapplications/elements/content/BugPanel.qml2
-rw-r--r--tests/testapplications/elements/content/ColumnElement.qml2
-rw-r--r--tests/testapplications/elements/content/DirectionElement.qml2
-rw-r--r--tests/testapplications/elements/content/DoubleValidatorElement.qml2
-rw-r--r--tests/testapplications/elements/content/EmitterElement.qml2
-rw-r--r--tests/testapplications/elements/content/FlickableElement.qml2
-rw-r--r--tests/testapplications/elements/content/FlipableElement.qml2
-rw-r--r--tests/testapplications/elements/content/FlowElement.qml2
-rw-r--r--tests/testapplications/elements/content/FocusScopeElement.qml2
-rw-r--r--tests/testapplications/elements/content/FontLoaderElement.qml2
-rw-r--r--tests/testapplications/elements/content/GradientElement.qml2
-rw-r--r--tests/testapplications/elements/content/GridElement.qml2
-rw-r--r--tests/testapplications/elements/content/GridViewElement.qml2
-rw-r--r--tests/testapplications/elements/content/Help.qml2
-rw-r--r--tests/testapplications/elements/content/HelpDesk.qml2
-rw-r--r--tests/testapplications/elements/content/ImageElement.qml2
-rw-r--r--tests/testapplications/elements/content/ImageParticleElement.qml2
-rw-r--r--tests/testapplications/elements/content/IntValidatorElement.qml2
-rw-r--r--tests/testapplications/elements/content/KeysElement.qml2
-rw-r--r--tests/testapplications/elements/content/ListViewElement.qml2
-rw-r--r--tests/testapplications/elements/content/MouseAreaElement.qml2
-rw-r--r--tests/testapplications/elements/content/ParallelAnimationElement.qml2
-rw-r--r--tests/testapplications/elements/content/ParticleSystemElement.qml2
-rw-r--r--tests/testapplications/elements/content/RectangleElement.qml2
-rw-r--r--tests/testapplications/elements/content/RegExpValidatorElement.qml2
-rw-r--r--tests/testapplications/elements/content/RepeaterElement.qml2
-rw-r--r--tests/testapplications/elements/content/RowElement.qml2
-rw-r--r--tests/testapplications/elements/content/ScaleElement.qml2
-rw-r--r--tests/testapplications/elements/content/SequentialAnimationElement.qml2
-rw-r--r--tests/testapplications/elements/content/ShapeElement.qml2
-rw-r--r--tests/testapplications/elements/content/SpriteSequenceElement.qml2
-rw-r--r--tests/testapplications/elements/content/SystemPaletteElement.qml2
-rw-r--r--tests/testapplications/elements/content/SystemTestHelp.qml2
-rw-r--r--tests/testapplications/elements/content/TextEditElement.qml2
-rw-r--r--tests/testapplications/elements/content/TextElement.qml2
-rw-r--r--tests/testapplications/elements/content/TextInputElement.qml2
-rw-r--r--tests/testapplications/elements/content/TrailEmitterElement.qml2
-rw-r--r--tests/testapplications/elements/content/XmlListModelElement.qml2
-rw-r--r--tests/testapplications/elements/content/elements.js2
-rw-r--r--tests/testapplications/elements/elements.qml2
-rw-r--r--tests/testapplications/listview/alteredViews.qml2
-rw-r--r--tests/testapplications/listview/onRemove.qml2
-rw-r--r--tests/testapplications/listview/sections.qml2
-rw-r--r--tests/testapplications/listview/sticky.qml152
-rw-r--r--tests/testapplications/listview/viewTransitions.qml2
-rw-r--r--tests/testapplications/qsgimage/ImageNG.qml2
-rw-r--r--tests/testapplications/qsgimage/img-align.qml2
-rw-r--r--tests/testapplications/text/Button.qml2
-rw-r--r--tests/testapplications/text/ControlView.qml2
-rw-r--r--tests/testapplications/text/text.qml2
-rw-r--r--tests/testapplications/text/textedit.qml2
-rw-r--r--tests/testapplications/text/textinput.qml2
-rw-r--r--tests/testapplications/textlayout/styledtext-layout.qml2
-rw-r--r--tools/CMakeLists.txt125
-rw-r--r--tools/qml/CMakeLists.txt41
-rw-r--r--tools/qml/ResizeItemToWindow.qml20
-rw-r--r--tools/qml/ResizeWindowToItem.qml18
-rw-r--r--tools/qml/conf/content/resizeItemToWindow.qml23
-rw-r--r--tools/qml/conf/content/resizeWindowToItem.qml18
-rw-r--r--tools/qml/conf/default.qml10
-rw-r--r--tools/qml/conf/resizeToItem.qml10
-rw-r--r--tools/qml/default.qml10
-rw-r--r--tools/qml/main.cpp99
-rw-r--r--tools/qml/resizeToItem.qml10
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp99
-rw-r--r--tools/qmldom/qmldomtool.cpp16
-rw-r--r--tools/qmlformat/CMakeLists.txt3
-rw-r--r--tools/qmlformat/qmlformat.cpp145
-rw-r--r--tools/qmljs/CMakeLists.txt5
-rw-r--r--tools/qmljs/qmljs.cpp81
-rw-r--r--tools/qmljsrootgen/main.cpp27
-rw-r--r--tools/qmllint/CMakeLists.txt3
-rw-r--r--tools/qmllint/main.cpp225
-rw-r--r--tools/qmlls/CMakeLists.txt26
-rw-r--r--tools/qmlls/lspcustomtypes.h44
-rw-r--r--tools/qmlls/qlanguageserver.cpp376
-rw-r--r--tools/qmlls/qlanguageserver.h91
-rw-r--r--tools/qmlls/qlanguageserver_p.h52
-rw-r--r--tools/qmlls/qmlcompletionsupport.cpp665
-rw-r--r--tools/qmlls/qmlcompletionsupport.h47
-rw-r--r--tools/qmlls/qmllanguageservertool.cpp96
-rw-r--r--tools/qmlls/qmllintsuggestions.cpp288
-rw-r--r--tools/qmlls/qmllintsuggestions.h40
-rw-r--r--tools/qmlls/qqmlcodemodel.cpp700
-rw-r--r--tools/qmlls/qqmlcodemodel.h128
-rw-r--r--tools/qmlls/qqmllanguageserver.cpp146
-rw-r--r--tools/qmlls/qqmllanguageserver.h66
-rw-r--r--tools/qmlls/textblock.cpp101
-rw-r--r--tools/qmlls/textblock.h61
-rw-r--r--tools/qmlls/textcursor.cpp121
-rw-r--r--tools/qmlls/textcursor.h60
-rw-r--r--tools/qmlls/textdocument.cpp116
-rw-r--r--tools/qmlls/textdocument.h66
-rw-r--r--tools/qmlls/textsynchronization.cpp99
-rw-r--r--tools/qmlls/textsynchronization.h32
-rw-r--r--tools/qmlls/workspace.cpp164
-rw-r--r--tools/qmlls/workspace.h32
-rw-r--r--tools/qmlplugindump/main.cpp220
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp210
-rw-r--r--tools/qmltc/CMakeLists.txt2
-rw-r--r--tools/qmltc/main.cpp52
-rw-r--r--tools/qmltc/qmltccodewriter.cpp99
-rw-r--r--tools/qmltc/qmltccodewriter.h4
-rw-r--r--tools/qmltc/qmltccompiler.cpp397
-rw-r--r--tools/qmltc/qmltccompiler.h8
-rw-r--r--tools/qmltc/qmltccompilerpieces.cpp17
-rw-r--r--tools/qmltc/qmltccompilerpieces.h27
-rw-r--r--tools/qmltc/qmltcoutputir.h46
-rw-r--r--tools/qmltc/qmltcpropertyutils.h11
-rw-r--r--tools/qmltc/qmltctyperesolver.cpp3
-rw-r--r--tools/qmltc/qmltcvisitor.cpp78
-rw-r--r--tools/qmltc/qmltcvisitor.h8
-rw-r--r--tools/qmltime/qmltime.h1
-rw-r--r--tools/qmltyperegistrar/qmltyperegistrar.cpp80
-rw-r--r--tools/shared/qqmltoolingsettings.cpp125
-rw-r--r--tools/shared/qqmltoolingsettings.h33
-rw-r--r--tools/svgtoqml/CMakeLists.txt32
-rw-r--r--tools/svgtoqml/main.cpp141
-rw-r--r--tools/svgtoqml/main.qml21
8075 files changed, 255927 insertions, 82953 deletions
diff --git a/.cmake.conf b/.cmake.conf
index 2d1d35cf7a..b768639e74 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,5 +1,6 @@
-set(QT_REPO_MODULE_VERSION "6.6.0")
+set(QT_REPO_MODULE_VERSION "6.8.0")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1")
list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1")
+list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_FOREACH=1")
diff --git a/.gitignore b/.gitignore
index e6ff521c67..def0f040cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,7 +94,6 @@ examples/quickcontrols/contactlist/contactlist
examples/quickcontrols/flatstyle/flatstyle
examples/quickcontrols/gallery/gallery
examples/quickcontrols/imagine/automotive/automotive
-examples/quickcontrols/imagine/musicplayer/musicplayer
examples/quickcontrols/sidepanel/sidepanel
examples/quickcontrols/swipetoremove/swipetoremove
examples/quickcontrols/texteditor/texteditor
@@ -365,7 +364,11 @@ src/qml/qml_compile_hash_p.h
/build*
CMakeFiles
CTestTestfile.cmake
+CMakeUserPresets.json
cmake_install.cmake
*_autogen
tst_*.xml
CMakeLists.txt.user
+
+# QML Language Server ini-files
+.qmlls.ini
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d9e4c15d80..e152cde949 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,7 +14,8 @@ project(QtDeclarative # special case
)
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core) # special case
-find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Gui Network Widgets OpenGL OpenGLWidgets Sql Concurrent Test LanguageServerPrivate LinguistTools)
+find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Gui Network Widgets OpenGL OpenGLWidgets Sql Concurrent Test LanguageServerPrivate LinguistTools Svg)
+qt_internal_project_setup()
# Set up QT_HOST_PATH as an extra root path to look for the ShaderToolsTools package
# when cross-compiling.
diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt
new file mode 100644
index 0000000000..136d900456
--- /dev/null
+++ b/LICENSES/Apache-2.0.txt
@@ -0,0 +1,61 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/LICENSES/CC-BY-3.0.txt b/LICENSES/CC-BY-3.0.txt
new file mode 100644
index 0000000000..1a16e05564
--- /dev/null
+++ b/LICENSES/CC-BY-3.0.txt
@@ -0,0 +1,319 @@
+Creative Commons Legal Code
+
+Attribution 3.0 Unported
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+ DAMAGES RESULTING FROM ITS USE.
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
+
+1. Definitions
+
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+ other pre-existing works, such as a translation, adaptation,
+ derivative work, arrangement of music or other alterations of a
+ literary or artistic work, or phonogram or performance and includes
+ cinematographic adaptations or any other form in which the Work may be
+ recast, transformed, or adapted including in any form recognizably
+ derived from the original, except that a work that constitutes a
+ Collection will not be considered an Adaptation for the purpose of
+ this License. For the avoidance of doubt, where the Work is a musical
+ work, performance or phonogram, the synchronization of the Work in
+ timed-relation with a moving image ("synching") will be considered an
+ Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+ encyclopedias and anthologies, or performances, phonograms or
+ broadcasts, or other works or subject matter other than works listed
+ in Section 1(f) below, which, by reason of the selection and
+ arrangement of their contents, constitute intellectual creations, in
+ which the Work is included in its entirety in unmodified form along
+ with one or more other contributions, each constituting separate and
+ independent works in themselves, which together are assembled into a
+ collective whole. A work that constitutes a Collection will not be
+ considered an Adaptation (as defined above) for the purposes of this
+ License.
+ c. "Distribute" means to make available to the public the original and
+ copies of the Work or Adaptation, as appropriate, through sale or
+ other transfer of ownership.
+ d. "Licensor" means the individual, individuals, entity or entities that
+ offer(s) the Work under the terms of this License.
+ e. "Original Author" means, in the case of a literary or artistic work,
+ the individual, individuals, entity or entities who created the Work
+ or if no individual or entity can be identified, the publisher; and in
+ addition (i) in the case of a performance the actors, singers,
+ musicians, dancers, and other persons who act, sing, deliver, declaim,
+ play in, interpret or otherwise perform literary or artistic works or
+ expressions of folklore; (ii) in the case of a phonogram the producer
+ being the person or legal entity who first fixes the sounds of a
+ performance or other sounds; and, (iii) in the case of broadcasts, the
+ organization that transmits the broadcast.
+ f. "Work" means the literary and/or artistic work offered under the terms
+ of this License including without limitation any production in the
+ literary, scientific and artistic domain, whatever may be the mode or
+ form of its expression including digital form, such as a book,
+ pamphlet and other writing; a lecture, address, sermon or other work
+ of the same nature; a dramatic or dramatico-musical work; a
+ choreographic work or entertainment in dumb show; a musical
+ composition with or without words; a cinematographic work to which are
+ assimilated works expressed by a process analogous to cinematography;
+ a work of drawing, painting, architecture, sculpture, engraving or
+ lithography; a photographic work to which are assimilated works
+ expressed by a process analogous to photography; a work of applied
+ art; an illustration, map, plan, sketch or three-dimensional work
+ relative to geography, topography, architecture or science; a
+ performance; a broadcast; a phonogram; a compilation of data to the
+ extent it is protected as a copyrightable work; or a work performed by
+ a variety or circus performer to the extent it is not otherwise
+ considered a literary or artistic work.
+ g. "You" means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this License with
+ respect to the Work, or who has received express permission from the
+ Licensor to exercise rights under this License despite a previous
+ violation.
+ h. "Publicly Perform" means to perform public recitations of the Work and
+ to communicate to the public those public recitations, by any means or
+ process, including by wire or wireless means or public digital
+ performances; to make available to the public Works in such a way that
+ members of the public may access these Works from a place and at a
+ place individually chosen by them; to perform the Work to the public
+ by any means or process and the communication to the public of the
+ performances of the Work, including by public digital performance; to
+ broadcast and rebroadcast the Work by any means including signs,
+ sounds or images.
+ i. "Reproduce" means to make copies of the Work by any means including
+ without limitation by sound or visual recordings and the right of
+ fixation and reproducing fixations of the Work, including storage of a
+ protected performance or phonogram in digital form or other electronic
+ medium.
+
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
+
+ a. to Reproduce the Work, to incorporate the Work into one or more
+ Collections, and to Reproduce the Work as incorporated in the
+ Collections;
+ b. to create and Reproduce Adaptations provided that any such Adaptation,
+ including any translation in any medium, takes reasonable steps to
+ clearly label, demarcate or otherwise identify that changes were made
+ to the original Work. For example, a translation could be marked "The
+ original work was translated from English to Spanish," or a
+ modification could indicate "The original work has been modified.";
+ c. to Distribute and Publicly Perform the Work including as incorporated
+ in Collections; and,
+ d. to Distribute and Publicly Perform Adaptations.
+ e. For the avoidance of doubt:
+
+ i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+ which the right to collect royalties through any statutory or
+ compulsory licensing scheme cannot be waived, the Licensor
+ reserves the exclusive right to collect such royalties for any
+ exercise by You of the rights granted under this License;
+ ii. Waivable Compulsory License Schemes. In those jurisdictions in
+ which the right to collect royalties through any statutory or
+ compulsory licensing scheme can be waived, the Licensor waives the
+ exclusive right to collect such royalties for any exercise by You
+ of the rights granted under this License; and,
+ iii. Voluntary License Schemes. The Licensor waives the right to
+ collect royalties, whether individually or, in the event that the
+ Licensor is a member of a collecting society that administers
+ voluntary licensing schemes, via that society, from any exercise
+ by You of the rights granted under this License.
+
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats. Subject to Section 8(f), all rights not expressly
+granted by Licensor are hereby reserved.
+
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
+
+ a. You may Distribute or Publicly Perform the Work only under the terms
+ of this License. You must include a copy of, or the Uniform Resource
+ Identifier (URI) for, this License with every copy of the Work You
+ Distribute or Publicly Perform. You may not offer or impose any terms
+ on the Work that restrict the terms of this License or the ability of
+ the recipient of the Work to exercise the rights granted to that
+ recipient under the terms of the License. You may not sublicense the
+ Work. You must keep intact all notices that refer to this License and
+ to the disclaimer of warranties with every copy of the Work You
+ Distribute or Publicly Perform. When You Distribute or Publicly
+ Perform the Work, You may not impose any effective technological
+ measures on the Work that restrict the ability of a recipient of the
+ Work from You to exercise the rights granted to that recipient under
+ the terms of the License. This Section 4(a) applies to the Work as
+ incorporated in a Collection, but this does not require the Collection
+ apart from the Work itself to be made subject to the terms of this
+ License. If You create a Collection, upon notice from any Licensor You
+ must, to the extent practicable, remove from the Collection any credit
+ as required by Section 4(b), as requested. If You create an
+ Adaptation, upon notice from any Licensor You must, to the extent
+ practicable, remove from the Adaptation any credit as required by
+ Section 4(b), as requested.
+ b. If You Distribute, or Publicly Perform the Work or any Adaptations or
+ Collections, You must, unless a request has been made pursuant to
+ Section 4(a), keep intact all copyright notices for the Work and
+ provide, reasonable to the medium or means You are utilizing: (i) the
+ name of the Original Author (or pseudonym, if applicable) if supplied,
+ and/or if the Original Author and/or Licensor designate another party
+ or parties (e.g., a sponsor institute, publishing entity, journal) for
+ attribution ("Attribution Parties") in Licensor's copyright notice,
+ terms of service or by other reasonable means, the name of such party
+ or parties; (ii) the title of the Work if supplied; (iii) to the
+ extent reasonably practicable, the URI, if any, that Licensor
+ specifies to be associated with the Work, unless such URI does not
+ refer to the copyright notice or licensing information for the Work;
+ and (iv) , consistent with Section 3(b), in the case of an Adaptation,
+ a credit identifying the use of the Work in the Adaptation (e.g.,
+ "French translation of the Work by Original Author," or "Screenplay
+ based on original Work by Original Author"). The credit required by
+ this Section 4 (b) may be implemented in any reasonable manner;
+ provided, however, that in the case of a Adaptation or Collection, at
+ a minimum such credit will appear, if a credit for all contributing
+ authors of the Adaptation or Collection appears, then as part of these
+ credits and in a manner at least as prominent as the credits for the
+ other contributing authors. For the avoidance of doubt, You may only
+ use the credit required by this Section for the purpose of attribution
+ in the manner set out above and, by exercising Your rights under this
+ License, You may not implicitly or explicitly assert or imply any
+ connection with, sponsorship or endorsement by the Original Author,
+ Licensor and/or Attribution Parties, as appropriate, of You or Your
+ use of the Work, without the separate, express prior written
+ permission of the Original Author, Licensor and/or Attribution
+ Parties.
+ c. Except as otherwise agreed in writing by the Licensor or as may be
+ otherwise permitted by applicable law, if You Reproduce, Distribute or
+ Publicly Perform the Work either by itself or as part of any
+ Adaptations or Collections, You must not distort, mutilate, modify or
+ take other derogatory action in relation to the Work which would be
+ prejudicial to the Original Author's honor or reputation. Licensor
+ agrees that in those jurisdictions (e.g. Japan), in which any exercise
+ of the right granted in Section 3(b) of this License (the right to
+ make Adaptations) would be deemed to be a distortion, mutilation,
+ modification or other derogatory action prejudicial to the Original
+ Author's honor and reputation, the Licensor will waive or not assert,
+ as appropriate, this Section, to the fullest extent permitted by the
+ applicable national law, to enable You to reasonably exercise Your
+ right under Section 3(b) of this License (right to make Adaptations)
+ but not otherwise.
+
+5. Representations, Warranties and Disclaimer
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+ a. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this License.
+ Individuals or entities who have received Adaptations or Collections
+ from You under this License, however, will not have their licenses
+ terminated provided such individuals or entities remain in full
+ compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
+ survive any termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+ perpetual (for the duration of the applicable copyright in the Work).
+ Notwithstanding the above, Licensor reserves the right to release the
+ Work under different license terms or to stop distributing the Work at
+ any time; provided, however that any such election will not serve to
+ withdraw this License (or any other license that has been, or is
+ required to be, granted under the terms of this License), and this
+ License will continue in full force and effect unless terminated as
+ stated above.
+
+8. Miscellaneous
+
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+ the Licensor offers to the recipient a license to the Work on the same
+ terms and conditions as the license granted to You under this License.
+ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
+ offers to the recipient a license to the original Work on the same
+ terms and conditions as the license granted to You under this License.
+ c. If any provision of this License is invalid or unenforceable under
+ applicable law, it shall not affect the validity or enforceability of
+ the remainder of the terms of this License, and without further action
+ by the parties to this agreement, such provision shall be reformed to
+ the minimum extent necessary to make such provision valid and
+ enforceable.
+ d. No term or provision of this License shall be deemed waived and no
+ breach consented to unless such waiver or consent shall be in writing
+ and signed by the party to be charged with such waiver or consent.
+ e. This License constitutes the entire agreement between the parties with
+ respect to the Work licensed here. There are no understandings,
+ agreements or representations with respect to the Work not specified
+ here. Licensor shall not be bound by any additional provisions that
+ may appear in any communication from You. This License may not be
+ modified without the mutual written agreement of the Licensor and You.
+ f. The rights granted under, and the subject matter referenced, in this
+ License were drafted utilizing the terminology of the Berne Convention
+ for the Protection of Literary and Artistic Works (as amended on
+ September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+ Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+ and the Universal Copyright Convention (as revised on July 24, 1971).
+ These rights and subject matter take effect in the relevant
+ jurisdiction in which the License terms are sought to be enforced
+ according to the corresponding provisions of the implementation of
+ those treaty provisions in the applicable national law. If the
+ standard suite of rights granted under applicable copyright law
+ includes additional rights not granted under this License, such
+ additional rights are deemed to be included in the License; this
+ License is not intended to restrict the license of any rights under
+ applicable law.
+
+
+Creative Commons Notice
+
+ Creative Commons is not a party to this License, and makes no warranty
+ whatsoever in connection with the Work. Creative Commons will not be
+ liable to You or any party on any legal theory for any damages
+ whatsoever, including without limitation any general, special,
+ incidental or consequential damages arising in connection to this
+ license. Notwithstanding the foregoing two (2) sentences, if Creative
+ Commons has expressly identified itself as the Licensor hereunder, it
+ shall have all rights and obligations of Licensor.
+
+ Except for the limited purpose of indicating to the public that the
+ Work is licensed under the CCPL, Creative Commons does not authorize
+ the use by either party of the trademark "Creative Commons" or any
+ related trademark or logo of Creative Commons without the prior
+ written consent of Creative Commons. Any permitted use will be in
+ compliance with Creative Commons' then-current trademark usage
+ guidelines, as may be published on its website or otherwise made
+ available upon request from time to time. For the avoidance of doubt,
+ this trademark restriction does not form part of this License.
+
+ Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt
new file mode 100644
index 0000000000..bc65af3cbd
--- /dev/null
+++ b/LICENSES/MIT.txt
@@ -0,0 +1,11 @@
+
+
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/LICENSES/OFL-1.1.txt b/LICENSES/OFL-1.1.txt
new file mode 100644
index 0000000000..40bd8a65d5
--- /dev/null
+++ b/LICENSES/OFL-1.1.txt
@@ -0,0 +1,97 @@
+Copyright (c) <dates>, <Copyright Holder> (<URL|email>),
+with Reserved Font Name <Reserved Font Name>.
+Copyright (c) <dates>, <additional Copyright Holder> (<URL|email>),
+with Reserved Font Name <additional Reserved Font Name>.
+Copyright (c) <dates>, <additional Copyright Holder> (<URL|email>).
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/cmake/QtDeclarativeSetup.cmake b/cmake/QtDeclarativeSetup.cmake
index 6f39befa26..165a71e09a 100644
--- a/cmake/QtDeclarativeSetup.cmake
+++ b/cmake/QtDeclarativeSetup.cmake
@@ -74,5 +74,7 @@ function(qt_declarative_generate_reg_exp_jit_tables consuming_target)
)
target_sources(${consuming_target} PRIVATE ${output_file})
target_include_directories(${consuming_target} PRIVATE $<BUILD_INTERFACE:${generate_dir}>)
- set_source_files_properties(${output_file} PROPERTIES GENERATED TRUE)
+ set_source_files_properties(${output_file} PROPERTIES
+ GENERATED TRUE
+ _qt_non_module_header TRUE)
endfunction()
diff --git a/coin/axivion/ci_config_linux.json b/coin/axivion/ci_config_linux.json
new file mode 100644
index 0000000000..2c65f29783
--- /dev/null
+++ b/coin/axivion/ci_config_linux.json
@@ -0,0 +1,47 @@
+{
+ "Project": {
+ "BuildSystemIntegration": {
+ "child_order": [
+ "GCCSetup",
+ "CMake",
+ "LinkLibraries"
+ ]
+ },
+ "CMake": {
+ "_active": true,
+ "_copy_from": "CMakeIntegration",
+ "build_environment": {},
+ "build_options": "-j4",
+ "generate_options": "--fresh",
+ "generator": "Ninja"
+ },
+ "GCCSetup": {
+ "_active": true,
+ "_copy_from": "Command",
+ "build_command": "gccsetup --cc gcc --cxx g++ --config ../../../axivion/"
+ },
+ "LinkLibraries": {
+ "_active": true,
+ "_copy_from": "AxivionLinker",
+ "input_files": [
+ "build/lib/lib*.so*.ir",
+ "build/qml/*/lib*.so*.ir",
+ "build/qml/*/*/lib*.so*.ir",
+ "build/qml/*/*/*/lib*.so*.ir",
+ "build/qml/*/*/*/*/lib*.so*.ir"
+ ],
+ "ir": "build/$(env:TESTED_MODULE_COIN).ir",
+ "plugin_files": [
+ "build/plugins/*/lib*.so*.ir"
+ ]
+ }
+ },
+ "_Format": "1.0",
+ "_Version": "7.6.2",
+ "_VersionNum": [
+ 7,
+ 6,
+ 2,
+ 12725
+ ]
+}
diff --git a/coin/instructions/coin_dom_standalone_old_qt_mac.yaml b/coin/instructions/coin_dom_standalone_old_qt_mac.yaml
deleted file mode 100644
index d0a265a763..0000000000
--- a/coin/instructions/coin_dom_standalone_old_qt_mac.yaml
+++ /dev/null
@@ -1,69 +0,0 @@
-type: Group
-instructions:
- - type: SetBuildDirectory
- directory: "{{.SourceDir}}/examples/qml/qmldom"
- - type: MakeDirectory
- directory: "{{.BuildDir}}/qt63"
- - type: ChangeDirectory
- directory: "{{.BuildDir}}/qt63"
- - type: EnvironmentVariable
- variableName: CONFIGURE_ENV_PREFIX
- variableValue: "{{.Env.ENV_PREFIX}}"
- - type: ExecuteCommand
- command: "curl -s https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-beta2-released/Qt/qtbase/qtbase-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z -o qtbase.7z"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- download of https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-beta2-released/Qt/qtbase/qtbase-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z failed
- - type: ExecuteCommand
- command: "curl -s https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-beta2-released/Qt/qtdeclarative/qtdeclarative-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z -o qtdeclarative.7z"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- Download of https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-beta2-released/Qt/qtdeclarative/qtdeclarative-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z failed
- - type: ExecuteCommand
- command: "7za x qtbase.7z"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- Uncompress of qtbase failed.
- - type: ExecuteCommand
- command: "7za x qtdeclarative.7z"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- Uncompress of qtdeclarative failed.
- - type: ExecuteCommand
- command: "{{.Env.ENV_PREFIX}} cmake -S {{.SourceDir}}/examples/qml/qmldom -B standalone63 -DCMAKE_PREFIX_PATH={{.BuildDir}}/qt63"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- configuration of qmldom standalone on Qt 6.5 failed
- - type: ExecuteCommand
- command: "{{.Env.ENV_PREFIX}} cmake --build standalone63"
- ignoreExitCode: false
- maxTimeInSeconds: 3600
- maxTimeBetweenOutput: 1800
- userMessageOnFailure: >
- Compilation of qmldom standalone on Qt 6.5 FAILED, see the log for details.
- - type: ExecuteCommand
- command: "./standalone63/qmldomloadeditwrite"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- Run of qmldom standalone on Qt 6.5 FAILED
-enable_if:
- condition: and
- conditions:
- - condition: property
- property: target.arch
- contains_value: X86_64
- - condition: property
- property: target.os
- contains_value: MacOS
diff --git a/coin/instructions/coin_dom_standalone_old_qt_win.yaml b/coin/instructions/coin_dom_standalone_old_qt_win.yaml
deleted file mode 100644
index 47320c278f..0000000000
--- a/coin/instructions/coin_dom_standalone_old_qt_win.yaml
+++ /dev/null
@@ -1,75 +0,0 @@
-type: Group
-instructions:
- - type: SetBuildDirectory
- directory: "{{.SourceDir}}/examples/qml/qmldom"
- - type: MakeDirectory
- directory: "{{.BuildDir}}/qt63"
- - type: ChangeDirectory
- directory: "{{.BuildDir}}/qt63"
- - type: EnvironmentVariable
- variableName: CONFIGURE_ENV_PREFIX
- variableValue: "{{.Env.ENV_PREFIX}}"
- - type: ExecuteCommand
- command: "curl -s https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-beta2-released/Qt/qtbase/qtbase-Windows-Windows_10_22H2-MSVC2019-Windows-Windows_10_22H2-X86_64.7z -o qtbase.7z"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- download of https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-beta2-released/Qt/qtbase/qtbase-Windows-Windows_10_22H2-MSVC2019-Windows-Windows_10_22H2-X86_64.7z failed
- - type: ExecuteCommand
- command: "curl -s https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-beta2-released/Qt/qtdeclarative/qtdeclarative-Windows-Windows_10_22H2-MSVC2019-Windows-Windows_10_22H2-X86_64.7z -o qtdeclarative.7z"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- Download of https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-beta2-released/Qt/qtdeclarative/qtdeclarative-Windows-Windows_10_22H2-MSVC2019-Windows-Windows_10_22H2-X86_64.7z failed
- - type: ExecuteCommand
- command: "7z.exe x qtbase.7z"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- Uncompress of qtbase failed.
- - type: ExecuteCommand
- command: "7z.exe x qtdeclarative.7z"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- Uncompress of qtdeclarative failed.
- - type: ExecuteCommand
- command: "{{.Env.ENV_PREFIX}} cmake -S {{.SourceDir}}/examples/qml/qmldom -B standalone63 -DCMAKE_PREFIX_PATH={{.BuildDir}}/qt63"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- configuration of qmldom standalone on Qt 6.5 failed
- - type: ExecuteCommand
- command: "{{.Env.ENV_PREFIX}} cmake --build standalone63"
- ignoreExitCode: false
- maxTimeInSeconds: 3600
- maxTimeBetweenOutput: 1800
- userMessageOnFailure: >
- Compilation of qmldom standalone on Qt 6.5 FAILED, see the log for details.
- - type: PrependToEnvironmentVariable
- variableName: PATH
- variableValue: "{{.BuildDir}}/qt63/bin;"
- - type: ExecuteCommand
- command: "./standalone63/qmldomloadeditwrite"
- ignoreExitCode: false
- maxTimeInSeconds: 1800
- maxTimeBetweenOutput: 900
- userMessageOnFailure: >
- Run of qmldom standalone on Qt 6.5 FAILED
-enable_if:
- condition: and
- conditions:
- - condition: property
- property: target.arch
- contains_value: X86_64
- - condition: property
- property: target.os
- contains_value: Windows
- - condition: property
- property: target.compiler
- contains_value: MSVC
diff --git a/coin/module_config.yaml b/coin/module_config.yaml
index 23f6189286..9acb62722c 100644
--- a/coin/module_config.yaml
+++ b/coin/module_config.yaml
@@ -17,5 +17,3 @@ instructions:
Test:
- !include "{{qt/qtbase}}/coin_module_test_template_v3.yaml"
- !include "{{qt/qtbase}}/coin_module_test_docs.yaml"
- - !include "{{qt/qtdeclarative}}/coin_dom_standalone_old_qt_mac.yaml"
- - !include "{{qt/qtdeclarative}}/coin_dom_standalone_old_qt_win.yaml"
diff --git a/conanfile.py b/conanfile.py
deleted file mode 100644
index 0cdf5195cb..0000000000
--- a/conanfile.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-from conans import ConanFile
-import re
-from pathlib import Path
-from typing import Dict, Any
-
-_qtdeclarative_features = [
- "qml-animation",
- "qml-debug",
- "qml-delegate-model",
- "qml-devtools",
- "qml-itemmodel",
- "qml-jit",
- "qml-list-model",
- "qml-locale",
- "qml-network",
- "qml-object-model",
- "qml-preview",
- "qml-profiler",
- "qml-table-model",
- "qml-worker-script",
- "qml-xml-http-request",
- "qml-xmllistmodel",
- "quick-animatedimage",
- "quick-canvas",
- "quick-designer",
- "quick-draganddrop",
- "quick-flipable",
- "quick-gridview",
- "quick-listview",
- "quick-particles",
- "quick-path",
- "quick-pathview",
- "quick-positioners",
- "quick-repeater",
- "quick-shadereffect",
- "quick-sprite",
- "quick-tableview",
- "quick-treeview",
- "quickcontrols2-fusion",
- "quickcontrols2-imagine",
- "quickcontrols2-macos",
- "quickcontrols2-material",
- "quickcontrols2-universal",
- "quickcontrols2-windows",
- "quicktemplates2-calendar",
- "quicktemplates2-hover",
- "quicktemplates2-multitouch",
-]
-
-
-def _parse_qt_version_by_key(key: str) -> str:
- with open(Path(__file__).parent.resolve() / ".cmake.conf") as f:
- m = re.search(fr'{key} .*"(.*)"', f.read())
- return m.group(1) if m else ""
-
-
-def _get_qt_minor_version() -> str:
- return ".".join(_parse_qt_version_by_key("QT_REPO_MODULE_VERSION").split(".")[:2])
-
-
-class QtDeclarative(ConanFile):
- name = "qtdeclarative"
- license = "LGPL-3.0, GPL-2.0+, Commercial Qt License Agreement"
- author = "The Qt Company <https://www.qt.io/contact-us>"
- url = "https://code.qt.io/cgit/qt/qtdeclarative.git"
- description = (
- "The Qt Declarative module provides a declarative framework for building highly dynamic, "
- "custom user interfaces"
- )
- topics = "qt", "qt6", "qtdeclarative"
- settings = "os", "compiler", "arch", "build_type"
- # for referencing the version number and prerelease tag and dependencies info
- exports = ".cmake.conf", "dependencies.yaml"
- exports_sources = "*", "!conan*.*"
- python_requires = f"qt-conan-common/{_get_qt_minor_version()}@qt/everywhere"
- python_requires_extend = "qt-conan-common.QtLeafModule"
-
- def get_qt_leaf_module_options(self) -> Dict[str, Any]:
- """Implements abstractmethod from qt-conan-common.QtLeafModule"""
- return self._shared.convert_qt_features_to_conan_options(_qtdeclarative_features)
-
- def get_qt_leaf_module_default_options(self) -> Dict[str, Any]:
- """Implements abstractmethod from qt-conan-common.QtLeafModule"""
- return self._shared.convert_qt_features_to_default_conan_options(_qtdeclarative_features)
diff --git a/dependencies.yaml b/dependencies.yaml
index 64d54c8e87..b3b18a686d 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,16 +1,16 @@
dependencies:
../qtbase:
- ref: d7e8d5bb1b5a9c4b21a3d824780c672eaf4e56b1
+ ref: 4641945e45206508b44678011bb83da7722bad62
required: true
../qtimageformats:
- ref: d7f26f37179a245f33c8f39dfbc15bccf65913f5
+ ref: 0f495cc4db125282ae5bab6e21c504c2a0520606
required: false
../qtlanguageserver:
- ref: f05856da0f01d8fe3de8bef6700570387a337ac7
+ ref: 19f182c85e4156917e28e1f5c14b8da2079aefd6
required: false
../qtshadertools:
- ref: 78808bcf3b642ec1ec4a9e60ff677ab72eb676db
+ ref: 2eced24fc6550f2845f5d6de33100e7c27207008
required: false
../qtsvg:
- ref: b3572c6416a5a383b63b9f2adce83ce3dc383db3
+ ref: c6fe4261bdaa4f47f779e3ef0e4de89ca80d2be7
required: false
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 57d9fa87d2..6e3573b1be 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,16 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_examples_build_begin(EXTERNAL_BUILD)
add_subdirectory(qml)
-if(TARGET Qt::QuickTest)
- add_subdirectory(qmltest)
-endif()
-if(TARGET Qt::Quick)
+if(TARGET Qt6::Quick)
add_subdirectory(quick)
+ add_subdirectory(platforms)
endif()
-if(TARGET Qt::QuickTemplates2)
+if(TARGET Qt6::QuickTemplates2)
add_subdirectory(quickcontrols)
endif()
diff --git a/examples/README b/examples/README
index f4567ab1d2..a8ec653023 100644
--- a/examples/README
+++ b/examples/README
@@ -6,8 +6,8 @@ designers to actually implement their UI vision. QML UIs can integrate
with C++ code in many ways, including being loaded as a part of a C++ UI
and loading data models from C++ and interacting with them.
-Mostof these examples can be viewed directly with the
+Most of these examples can be viewed directly with the
QML viewer utility, without requiring compilation.
-Documentation for these examples can be found via the Examples
+Documentation for these examples can be found via the Examples and Tutorials
link in the main Qt documentation.
diff --git a/examples/core/permissions/CMakeLists.txt b/examples/core/permissions/CMakeLists.txt
new file mode 100644
index 0000000000..d09acc335a
--- /dev/null
+++ b/examples/core/permissions/CMakeLists.txt
@@ -0,0 +1,57 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(permissions LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
+qt_standard_project_setup(REQUIRES 6.6)
+
+qt_add_executable(permissions
+ main.cpp
+)
+
+qt_add_qml_module(permissions
+ URI PermissionsExample
+ QML_FILES
+ "Main.qml"
+)
+
+target_link_libraries(permissions PUBLIC
+ Qt::Core
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::Quick
+ Qt::QuickControls2
+)
+
+if(UNIX AND NOT APPLE AND CMAKE_CROSSCOMPILING)
+ find_package(Qt6 REQUIRED COMPONENTS QuickTemplates2)
+
+ # Work around QTBUG-86533
+ target_link_libraries(permissions PRIVATE Qt::QuickTemplates2)
+endif()
+
+set_target_properties(permissions PROPERTIES
+ MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist"
+ MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.examples.qmlpermissions"
+ QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
+)
+
+install(TARGETS permissions
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET permissions
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/core/permissions/Info.plist b/examples/core/permissions/Info.plist
new file mode 100644
index 0000000000..a02adecbb0
--- /dev/null
+++ b/examples/core/permissions/Info.plist
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+
+ <key>CFBundleName</key>
+ <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
+ <key>CFBundleExecutable</key>
+ <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
+
+ <key>CFBundleVersion</key>
+ <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
+ <key>CFBundleShortVersionString</key>
+ <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
+
+ <key>LSMinimumSystemVersion</key>
+ <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
+
+ <key>NSHumanReadableCopyright</key>
+ <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
+
+ <key>CFBundleIconFile</key>
+ <string>${MACOSX_BUNDLE_ICON_FILE}</string>
+
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+
+ <key>NSBluetoothAlwaysUsageDescription</key>
+ <string>Testing BluetoothAlways</string>
+ <key>NSCalendarsUsageDescription</key>
+ <string>Testing Calendars</string>
+ <key>NSCameraUsageDescription</key>
+ <string>Testing Camera</string>
+ <key>NSContactsUsageDescription</key>
+ <string>Testing Contacts</string>
+ <key>NSHealthShareUsageDescription</key>
+ <string>Testing HealthShare</string>
+ <key>NSHealthUpdateUsageDescription</key>
+ <string>Testing HealthUpdate</string>
+ <key>NSLocationUsageDescription</key>
+ <string>Testing Location on macOS</string>
+ <key>NSLocationWhenInUseUsageDescription</key>
+ <string>Testing Location when in use on iOS</string>
+ <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
+ <string>Testing Location always and when in use on iOS</string>
+ <key>NSMicrophoneUsageDescription</key>
+ <string>Testing Microphone</string>
+
+</dict>
+</plist> \ No newline at end of file
diff --git a/examples/core/permissions/Main.qml b/examples/core/permissions/Main.qml
new file mode 100644
index 0000000000..5d2aeaa16f
--- /dev/null
+++ b/examples/core/permissions/Main.qml
@@ -0,0 +1,99 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtCore
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+Window {
+ id: window
+ visible: true
+ title: qsTr("Qt QML Permissions")
+
+ readonly property int margin: 11
+
+ width: layout.implicitWidth + 2 * margin
+ height: layout.implicitHeight + 2 * margin
+
+ Rectangle {
+ anchors.fill: parent
+ color: locationPermission.status === Qt.Granted ? "green" :
+ locationPermission.status === Qt.Denied ? "red" : "blue"
+
+ LocationPermission {
+ id: locationPermission
+
+ // If any of the properties of the permission are changed,
+ // checkPermision is used to check, and the status will
+ // update accordingly. For example, upgrading from Approximate
+ // to Precise would either result in Undetermined, if we
+ // can still request Precise, or Denied, if we already did,
+ // or it's not possible to upgrade the permission.
+ accuracy: accuracyCombo.currentValue === qsTr("Precise") ? LocationPermission.Precise
+ : LocationPermission.Approximate
+ availability: availabilityCombo.currentValue === qsTr("Always") ? LocationPermission.Always
+ : LocationPermission.WhenInUse
+ // However, the permission will not be affected by other
+ // permissions of the same type. In other words, a more
+ // extensive permission being granted does not reflect
+ // in this permission. Nor does losing an already granted
+ // permission, for example due to the user updating their
+ // system settings.
+ onStatusChanged: console.log("Status: " + status)
+ onAccuracyChanged: console.log("Accuracy: " + accuracy)
+ onAvailabilityChanged: console.log("Availability: " + availability)
+ }
+
+ ColumnLayout {
+ id: layout
+
+ anchors.fill: parent
+ anchors.margins: window.margin
+
+ Text {
+ readonly property string statusAsString: locationPermission.status === Qt.Granted ? qsTr("Granted") :
+ locationPermission.status === Qt.Undetermined ? qsTr("Undetermined")
+ : qsTr("Denied")
+ text: qsTr(`Location services, status: ${statusAsString}`)
+
+ font.bold: true
+ font.pointSize: 16
+ color: locationPermission.status === Qt.Undetermined ? "white" : "black"
+
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ RowLayout {
+ ComboBox {
+ id: accuracyCombo
+
+ model: ["Precise", "Approximate"]
+ implicitContentWidthPolicy: ComboBox.WidestText
+ Layout.fillWidth: true
+ }
+ ComboBox {
+ id: availabilityCombo
+
+ model: ["Always", "When in use"]
+
+ implicitContentWidthPolicy: ComboBox.WidestText
+ Layout.fillWidth: true
+ }
+ }
+
+ Button {
+ text: qsTr("Request location permissions")
+ enabled: locationPermission.status !== Qt.Denied
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: false
+
+ onClicked: locationPermission.request()
+ }
+ }
+
+ Component.onCompleted: {
+ console.log(locationPermission.status)
+ }
+ }
+}
diff --git a/examples/core/permissions/android/AndroidManifest.xml b/examples/core/permissions/android/AndroidManifest.xml
new file mode 100644
index 0000000000..ac84e78bee
--- /dev/null
+++ b/examples/core/permissions/android/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.example"
+ android:installLocation="auto"
+ android:versionCode="-- %%INSERT_VERSION_CODE%% --"
+ android:versionName="-- %%INSERT_VERSION_NAME%% --">
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <!-- %%INSERT_PERMISSIONS -->
+ <!-- %%INSERT_FEATURES -->
+ <supports-screens
+ android:anyDensity="true"
+ android:largeScreens="true"
+ android:normalScreens="true"
+ android:smallScreens="true" />
+ <application
+ android:name="org.qtproject.qt.android.bindings.QtApplication"
+ android:hardwareAccelerated="true"
+ android:label="-- %%INSERT_APP_NAME%% --"
+ android:requestLegacyExternalStorage="true"
+ android:allowNativeHeapPointerTagging="false"
+ android:allowBackup="true"
+ android:fullBackupOnly="false">
+ <activity
+ android:name="org.qtproject.qt.android.bindings.QtActivity"
+ android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
+ android:label="-- %%INSERT_APP_NAME%% --"
+ android:launchMode="singleTop"
+ android:screenOrientation="unspecified"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.app.lib_name"
+ android:value="-- %%INSERT_APP_LIB_NAME%% --" />
+
+ <meta-data
+ android:name="android.app.extract_android_style"
+ android:value="minimal" />
+ </activity>
+ </application>
+</manifest>
diff --git a/examples/core/permissions/main.cpp b/examples/core/permissions/main.cpp
new file mode 100644
index 0000000000..6635362871
--- /dev/null
+++ b/examples/core/permissions/main.cpp
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char **argv)
+{
+ QT_USE_NAMESPACE
+ QGuiApplication::setApplicationName("Permissions");
+ QGuiApplication::setOrganizationName("QtProject");
+
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.loadFromModule("PermissionsExample", "Main");
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
diff --git a/examples/examples.pro b/examples/examples.pro
index a106166f20..b73064e0c7 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -1,5 +1,4 @@
TEMPLATE = subdirs
-qtHaveModule(qmltest): SUBDIRS += qmltest
SUBDIRS += qml
qtHaveModule(quick): SUBDIRS += quick quickcontrols
diff --git a/examples/platforms/CMakeLists.txt b/examples/platforms/CMakeLists.txt
new file mode 100644
index 0000000000..78fb09f4f6
--- /dev/null
+++ b/examples/platforms/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if(ANDROID)
+ add_subdirectory(android)
+endif()
diff --git a/examples/platforms/android/.gitignore b/examples/platforms/android/.gitignore
new file mode 100644
index 0000000000..e1bb0d95b4
--- /dev/null
+++ b/examples/platforms/android/.gitignore
@@ -0,0 +1,10 @@
+*.user
+build/
+**/.idea/
+**/.gradle/
+**/local.properties
+**/app/libs/
+**/app/src/main/res/xml/qtprovider_paths.xml
+**/app/src/main/res/values/libs.xml
+**/app/assets/android_rcc_bundle.rcc
+
diff --git a/examples/platforms/android/CMakeLists.txt b/examples/platforms/android/CMakeLists.txt
new file mode 100644
index 0000000000..7ed54f821d
--- /dev/null
+++ b/examples/platforms/android/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+qt_internal_add_example(qml_in_android_view)
+qt_internal_add_example(qml_in_java_based_android_project)
+qt_internal_add_example(qml_in_kotlin_based_android_project)
diff --git a/examples/platforms/android/doc/images/portrait_java.png b/examples/platforms/android/doc/images/portrait_java.png
new file mode 100644
index 0000000000..266f0a2709
--- /dev/null
+++ b/examples/platforms/android/doc/images/portrait_java.png
Binary files differ
diff --git a/examples/platforms/android/doc/src/qml_in_android_studio_projects.qdoc b/examples/platforms/android/doc/src/qml_in_android_studio_projects.qdoc
new file mode 100644
index 0000000000..be1fa8b7a2
--- /dev/null
+++ b/examples/platforms/android/doc/src/qml_in_android_studio_projects.qdoc
@@ -0,0 +1,186 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qml-in-android-studio-projects-example.html
+ \title QML in Android Studio Projects
+ \brief Uses a \l {Qt Quick View Android Class}{QtQuickView}
+ to embed a QML component into Android projects.
+ \ingroup qtquickexamples
+
+ \section1 Overview
+
+ This example contains a QML project that you can import into Android Studio
+ with the \l {Qt Tools for Android Studio} plugin
+ and Java and Kotlin projects that utilize the
+ \l {Qt Quick View Android Class}{QtQuickView} API.
+
+ For more information on how QML works, see the \l {Qt Qml}. This
+ documentation will focus on how a QML component is embedded into
+ Java- and Kotlin-based Android applications.
+
+ \image portrait_java.png
+
+ First, we look at the \c MainActivity's onCreate() method of the Java
+ and Kotlin projects.
+
+ For a Java-based project:
+
+ \snippet android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java onCreate
+
+ For a Kotlin-based project:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt onCreate
+
+ \note in the Kotlin project we use \l {Android: View binding}{View binding}
+ to access the UI components of the application:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt binding
+
+ Inside the \c onCreate() method, an instance of
+ \l {Qt Quick View Android Class}{QtQuickView} named
+ \c m_qmlView is created by giving it the Java/Kotlin application Context,
+ URI of the QML project's \c main.qml file and the name of the QML project's
+ main library as parameters.
+
+ For a Java-based project:
+
+ \snippet android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java m_qmlView
+
+ For a Kotlin-based project:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt m_qmlView
+
+ \c m_qmlView is then added to Android FrameLayout ViewGroup with
+ appropriate layout parameters.
+
+ For a Java-based project:
+
+ \snippet android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java layoutParams
+
+ For a Kotlin-based project:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt layoutParams
+
+ \section1 Interacting with the QML component
+
+ To interact with the embedded QML component we first need to implement
+ the \l {Qt Quick View Android Class}{QtQuickView} public interface
+ \l [Qt Quick View Android Class]{public interface StatusChangeListener}{StatusChangeListener}.
+
+ For a Java-based project:
+
+ \code
+ public class MainActivity extends AppCompatActivity implements
+ QtQuickView.StatusChangeListener{
+ ...
+ }
+ \endcode
+
+ IFor a Kotlin-based project:
+
+ \code
+ class MainActivity : AppCompatActivity(), QtQuickView.StatusChangeListener{
+ ...
+ }
+ \endcode
+
+ Then, define an override for the
+ \l [Qt Quick View Android Class]{public interface StatusChangeListener}{StatusChangeListener}
+ callback function \c onStatusChanged().
+
+ For a Java-based project:
+
+ \snippet android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java onStatusChanged
+
+ For a Kotlin-based project:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt onStatusChanged
+
+ Then, set that listener to listen for status changes of \c m_qmlView
+ with the \l [Qt Quick View Android Class]{public void setStatusChangeListener(StatusChangeListener listener)}{setStatusChangeListener()}.
+
+ For a Java-based project:
+
+ \snippet android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java setStatusChangeListener
+
+ For a Kotlin-based project:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt setStatusChangeListener
+
+ The overridden callback function \c onStatusChanged() receives
+ \c StatusChanged() signal containing the current
+ \l [Qt Quick View Android Class]{Status values}{Status value} of the
+ \c m_qmlView. If this
+ \l [Qt Quick View Android Class]{Status values}{Status value}
+ is confirmed to be
+ \l [Qt Quick View Android Class]{Status values}{STATUS_READY},
+ we can start interacting with the QML view.
+
+ \section1 Getting and setting QML view property values
+
+ Getting and setting QML view property values happens through the
+ \l [Qt Quick View Android Class]{public <T extends Object> T getProperty(String propertyName)}{QtQuickView.getProperty()}
+ and \l [Qt Quick View Android Class]{public void setProperty(String propertyName, Object value)}{QtQuickView.setProperty()}
+ methods.
+
+ The root object of the QML component's background color is set when a click
+ event of an Android button occurs.
+
+ For a Java-based project:
+
+ \snippet android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java onClickListener
+
+ For a Kotlin-based project:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt onClickListener
+
+ With the \l [Qt Quick View Android Class]{public void setProperty(StringpropertyName, Object value)}{QtQuickView.setProperty()}
+ method we set the "colorStringFormat" property value to a random color
+ value that is fetched from the project's \c Colors.java class.
+
+ The \l [Qt Quick View Android Class]{public <T extends Object> T getProperty (String propertyName)}{QtQuickView.getProperty()}{QtQuickView.getProperty()}
+ method is used here to fetch the current background color of the root
+ object of the QML component and then show it to the user on the Android
+ side of the application.
+
+ \section1 Signal listeners
+
+ \l {Qt Quick View Android Class}{QtQuickView} class offers a
+ connectSignalListener() and disconnectSignalListener() methods which are
+ used to connect and disconnect a signal listener to a signal that is
+ declared in the QML component root object.
+
+ Here we connect a signal listener to the \c onClicked() signal of the
+ QML component.
+
+ For a Java-based project:
+
+ \snippet android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java qml signal listener
+
+ For a Kotlin-based project:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt qml signal listener
+
+ The \c onClicked() signal is emitted every time the button on the QML UI is
+ clicked. That signal is then received by this listener and the background
+ color of the layout holding the Android side of the application is set to
+ a random color value fetched from the project's \c Colors.java class.
+
+ The \l [Qt Quick View Android Class]{public <T> int addSignalListener(String signalName, Class<T> argType, SignalListener<T> listener)}{QtQuickView.connectSignalListener()}
+ returns a unique signal listener id which we store and use later to
+ identify and disconnect the listener.
+
+ For a Java-based project:
+
+ \snippet android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java disconnect qml signal listener
+
+ For a Kotlin-based project:
+
+ \snippet android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt disconnect qml signal listener
+
+ Here, the previously connected signal listener is disconnected using the
+ \l [Qt Quick View Android Class]{public boolean removeSignalListener(int signalListenerId)}{QtQuickView.disconnectSignalListener()}
+ method by giving it the unique signal listener id.
+
+*/
diff --git a/examples/platforms/android/qml_in_android_view/CMakeLists.txt b/examples/platforms/android/qml_in_android_view/CMakeLists.txt
new file mode 100644
index 0000000000..9aed5c0a4e
--- /dev/null
+++ b/examples/platforms/android/qml_in_android_view/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+cmake_minimum_required(VERSION 3.16)
+
+project(qml_in_android_view VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 6.7 REQUIRED COMPONENTS Quick)
+
+qt_standard_project_setup(REQUIRES 6.6)
+
+qt_add_executable(qml_in_android_view
+ main.cpp
+)
+
+qt_add_qml_module(qml_in_android_view
+ URI qml_in_android_view
+ VERSION 1.0
+ QML_FILES main.qml
+)
+
+target_link_libraries(qml_in_android_view
+ PRIVATE Qt6::Quick
+)
+
+install(TARGETS qml_in_android_view
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
diff --git a/examples/platforms/android/qml_in_android_view/main.cpp b/examples/platforms/android/qml_in_android_view/main.cpp
new file mode 100644
index 0000000000..c195699a0f
--- /dev/null
+++ b/examples/platforms/android/qml_in_android_view/main.cpp
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include <QGuiApplication>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ return app.exec();
+}
+
diff --git a/examples/platforms/android/qml_in_android_view/main.qml b/examples/platforms/android/qml_in_android_view/main.qml
new file mode 100644
index 0000000000..3ed2b6f58b
--- /dev/null
+++ b/examples/platforms/android/qml_in_android_view/main.qml
@@ -0,0 +1,87 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+ id: mainRectangle
+
+ property string colorStringFormat: "#1CB669"
+
+ signal onClicked()
+
+ color: colorStringFormat
+
+ Text {
+ id: helloText
+
+ text: "QML"
+ color: "white"
+ font.pixelSize: 72
+ fontSizeMode: Text.VerticalFit
+ // Height is calculated based on display orientation
+ // from Screen height, dividing numbers are based on what what seem
+ // to look good on most displays
+ height: Screen.width > Screen.height ? Screen.height / 8 : (Screen.height / 2) / 8
+ font.bold: true
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+
+ Text {
+ id: changeColorText
+
+ text: "Tap button to change Java view background color"
+ wrapMode: Text.Wrap
+ color: "white"
+ font.pixelSize: 58
+ fontSizeMode: Text.Fit
+ // Height and width are calculated based on display orientation
+ // from Screen height and width, dividing numbers are based on what seem to
+ // look good on most displays
+ height: Screen.width > Screen.height ? Screen.height / 8 : (Screen.height / 2) / 8
+ width: Screen.width > Screen.height ? (Screen.width / 2) / 2 : Screen.width / 2
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: helloText.bottom
+ anchors.topMargin: Screen.height / 10
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Button {
+ id: button
+ // Width is calculated from changeColorText which is calculated from Screen size
+ // dividing numbers are base on what seems to look good on most displays
+ width: changeColorText.width / 1.6
+ height: changeColorText.height * 1.2
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: changeColorText.bottom
+ anchors.topMargin: height / 5
+
+ onClicked: mainRectangle.onClicked()
+
+ background: Rectangle {
+ id: buttonBackground
+
+ radius: 14
+ color: "#6200EE"
+ opacity: button.down ? 0.6 : 1
+ scale: button.down ? 0.9 : 1
+ }
+ contentItem: Text {
+ id: buttonText
+
+ text: "CHANGE COLOR"
+ color: "white"
+ font.pixelSize: 58
+ minimumPixelSize: 10
+ fontSizeMode: Text.Fit
+ font.bold: true
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+}
diff --git a/examples/platforms/android/qml_in_java_based_android_project/CMakeLists.txt b/examples/platforms/android/qml_in_java_based_android_project/CMakeLists.txt
new file mode 100644
index 0000000000..ed5212d564
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+cmake_minimum_required(VERSION 3.16)
+
+project(qml_in_java_based_android_project VERSION 0.1 LANGUAGES CXX)
+
+install(DIRECTORY
+ gradle
+ app
+ DESTINATION .
+)
+install(FILES
+ settings.gradle
+ gradle.properties
+ build.gradle
+ CMakeLists.txt
+ DESTINATION .
+)
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/build.gradle b/examples/platforms/android/qml_in_java_based_android_project/app/build.gradle
new file mode 100644
index 0000000000..8e9ab1deb9
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/build.gradle
@@ -0,0 +1,56 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ namespace 'com.example.qml_in_java_based_android_project'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.example.qml_in_java_based_android_project"
+ minSdk 26
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ packagingOptions {
+ jniLibs {
+ useLegacyPackaging true
+ }
+ }
+ sourceSets {
+ main {
+ assets {
+ srcDirs 'assets'
+ }
+ jniLibs {
+ srcDirs 'libs'
+ }
+ }
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.9.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+}
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/AndroidManifest.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..f058a307fb
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <application
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.MyApplication"
+ tools:targetApi="34">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:configChanges="orientation|screenLayout|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/Colors.java b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/Colors.java
new file mode 100644
index 0000000000..ba2a9265f9
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/Colors.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+package com.example.qml_in_java_based_android_project;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Stack;
+
+class Colors {
+ private final Stack<Integer> recycle;
+ private final Stack<Integer> colors;
+
+ public Colors() {
+ colors = new Stack<>();
+ recycle = new Stack<>();
+ recycle.addAll(Arrays.asList(
+ 0xff1CB669, 0xff00414A, 0xff27138B,
+ 0xffB5C10E, 0xff373F26, 0xffAF93DF,
+ 0xff817505
+ )
+ );
+ }
+
+ public String getColor() {
+ if (colors.size()==0) {
+ while (!recycle.isEmpty())
+ colors.push(recycle.pop());
+ Collections.shuffle(colors);
+ }
+ int color = colors.pop();
+ recycle.push(color);
+ return String.format("#%06X", (0xFFFFFF & color));
+ }
+}
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java
new file mode 100644
index 0000000000..e7aee43c55
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java
@@ -0,0 +1,178 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+package com.example.qml_in_java_based_android_project;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.SwitchCompat;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.qtproject.qt.android.QtQuickView;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+// Implement QtQuickView StatusChangeListener interface to get status updates
+// from the underlying QQuickView
+public class MainActivity extends AppCompatActivity implements QtQuickView.StatusChangeListener {
+
+ private static final String TAG = "myTag";
+ private final Colors m_colors = new Colors();
+ private final Map<Integer, String> m_statusNames = new HashMap<Integer, String>() {{
+ put(QtQuickView.STATUS_READY, " READY");
+ put(QtQuickView.STATUS_LOADING, " LOADING");
+ put(QtQuickView.STATUS_ERROR, " ERROR");
+ put(QtQuickView.STATUS_NULL, " NULL");
+ }};
+ private int m_qmlButtonSignalListenerId;
+ private LinearLayout m_mainLinear;
+ private FrameLayout m_qmlFrameLayout;
+ private QtQuickView m_qmlView;
+ private LinearLayout m_androidControlsLayout;
+ private TextView m_getPropertyValueText;
+ private TextView m_qmlStatus;
+ private SwitchCompat m_switch;
+ private View m_box;
+
+ //! [onCreate]
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ m_mainLinear = findViewById(R.id.mainLinear);
+ m_getPropertyValueText = findViewById(R.id.getPropertyValueText);
+ m_qmlStatus = findViewById(R.id.qmlStatus);
+ m_androidControlsLayout = findViewById(R.id.javaLinear);
+ m_box = findViewById(R.id.box);
+
+ m_switch = findViewById(R.id.switch1);
+ m_switch.setOnClickListener(view -> switchListener());
+ //! [m_qmlView]
+ m_qmlView = new QtQuickView(this, "qrc:/qt/qml/qml_in_android_view/main.qml",
+ "qml_in_android_view");
+ //! [m_qmlView]
+
+ // Set status change listener for m_qmlView
+ // listener implemented below in OnStatusChanged
+ //! [setStatusChangeListener]
+ m_qmlView.setStatusChangeListener(this);
+ //! [setStatusChangeListener]
+ //! [layoutParams]
+ ViewGroup.LayoutParams params = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ m_qmlFrameLayout = findViewById(R.id.qmlFrame);
+ m_qmlFrameLayout.addView(m_qmlView, params);
+ //! [layoutParams]
+ Button button = findViewById(R.id.button);
+ button.setOnClickListener(view -> onClickListener());
+
+ // Check target device orientation on launch
+ handleOrientationChanges();
+ }
+ //! [onCreate]
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ handleOrientationChanges();
+ }
+
+ private void handleOrientationChanges() {
+ // When specific target device display configurations (listed in AndroidManifest.xml
+ // android:configChanges) change, get display metrics and make needed changes to UI
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ ViewGroup.LayoutParams qmlFrameLayoutParams = m_qmlFrameLayout.getLayoutParams();
+ ViewGroup.LayoutParams linearLayoutParams = m_androidControlsLayout.getLayoutParams();
+
+ if (displayMetrics.heightPixels > displayMetrics.widthPixels) {
+ m_mainLinear.setOrientation(LinearLayout.VERTICAL);
+ qmlFrameLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ qmlFrameLayoutParams.height = 0;
+ linearLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ linearLayoutParams.height = 0;
+ } else {
+ m_mainLinear.setOrientation(LinearLayout.HORIZONTAL);
+ qmlFrameLayoutParams.width = 0;
+ qmlFrameLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ linearLayoutParams.width = 0;
+ linearLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+ m_qmlFrameLayout.setLayoutParams(qmlFrameLayoutParams);
+ m_androidControlsLayout.setLayoutParams(linearLayoutParams);
+ }
+
+ //! [onStatusChanged]
+ @Override
+ public void onStatusChanged(int status) {
+ Log.i(TAG, "Status of QtQuickView: " + status);
+
+ final String qmlStatus = getResources().getString(R.string.qml_view_status)
+ + m_statusNames.get(status);
+
+ // Show current QML View status in a textview
+ m_qmlStatus.setText(qmlStatus);
+
+ // Connect signal listener to "onClicked" signal from main.qml
+ // addSignalListener returns int which can be used later to identify the listener
+ //! [qml signal listener]
+ if (status == QtQuickView.STATUS_READY && !m_switch.isChecked()) {
+ m_qmlButtonSignalListenerId = m_qmlView.connectSignalListener("onClicked", Object.class,
+ (String signal, Object o) -> {
+ Log.i(TAG, "QML button clicked");
+ m_androidControlsLayout.setBackgroundColor(Color.parseColor(m_colors.getColor()));
+ });
+
+ }
+ //! [qml signal listener]
+ }
+ //! [onStatusChanged]
+ //! [onClickListener]
+ public void onClickListener() {
+ // Set the QML view root object property "colorStringFormat" value to
+ // color from Colors.getColor()
+ m_qmlView.setProperty("colorStringFormat", m_colors.getColor());
+
+ String qmlBackgroundColor = m_qmlView.getProperty("colorStringFormat");
+
+ // Display the QML View background color code
+ m_getPropertyValueText.setText(qmlBackgroundColor);
+
+ // Display the QML View background color in a view
+ m_box.setBackgroundColor(Color.parseColor(qmlBackgroundColor));
+ }
+ //! [onClickListener]
+
+ public void switchListener() {
+ TextView text = findViewById(R.id.switchText);
+ // Disconnect QML button signal listener if switch is On using the saved signal listener Id
+ // and connect it again if switch is turned off
+ if (m_switch.isChecked()) {
+ Log.i(TAG, "QML button onClicked signal listener disconnected");
+ text.setText(R.string.connect_qml_button_signal_listener);
+ //! [disconnect qml signal listener]
+ m_qmlView.disconnectSignalListener(m_qmlButtonSignalListenerId);
+ //! [disconnect qml signal listener]
+ } else {
+ Log.i(TAG, "QML button onClicked signal listener connected");
+ text.setText(R.string.disconnect_qml_button_signal_listener);
+ m_qmlButtonSignalListenerId = m_qmlView.connectSignalListener("onClicked",
+ Object.class, (String t, Object value) -> {
+ Log.i(TAG, "QML button clicked");
+ m_androidControlsLayout.setBackgroundColor(Color.parseColor(m_colors.getColor()));
+ });
+ }
+ }
+}
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..ca3826a46c
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector
+ android:height="108dp"
+ android:width="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z"/>
+ <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+</vector>
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..9a5435b534
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,31 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/layout/activity_main.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000000..0d55ae643b
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/mainLinear"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity"
+ android:orientation="vertical"
+ android:baselineAligned="false">
+
+ <FrameLayout
+ android:id="@+id/qmlFrame"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/javaLinear"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="@color/lilac"
+ android:orientation="vertical">
+
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:gravity="center_horizontal"
+ android:includeFontPadding="false"
+ android:text="@string/java"
+ android:textColor="@color/white"
+ android:textSize="24sp"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/qmlStatus"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="16dp"
+ android:gravity="center_horizontal"
+ android:text="@string/qml_view_status"
+ android:textColor="@color/white"/>
+
+ <LinearLayout
+ android:id="@+id/buttonAndSwitchLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:layout_marginTop="16dp">
+
+ <LinearLayout
+ android:id="@+id/buttonLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/changeColorText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="3"
+ android:text="@string/change_qml_background"
+ android:textColor="@color/white" />
+
+ <Button
+ android:id="@+id/button"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:text="@string/button"
+ android:textSize="14sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/switchLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/switchText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="3"
+ android:text="@string/disconnect_qml_button_signal_listener"
+ android:textColor="@color/white" />
+
+ <androidx.appcompat.widget.SwitchCompat
+ android:id="@+id/switch1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ app:showText="true"
+ app:switchTextAppearance="@style/switchStyle"
+ android:switchTextAppearance="@android:style/TextAppearance.Small"
+ android:textOff="@string/off"
+ android:textOn="@string/on"
+ tools:ignore="UseSwitchCompatOrMaterialXml" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/qmlColorLinear"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:padding="10dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/qmlViewBackgroundText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="2"
+ android:text="@string/qml_view_background_color"
+ android:textColor="@color/white" />
+
+ <TextView
+ android:id="@+id/getPropertyValueText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:textColor="@color/white" />
+ </LinearLayout>
+
+ <View
+ android:id="@+id/box"
+ android:layout_width="100dp"
+ android:layout_height="50dp"
+ android:layout_gravity="center_horizontal"
+ android:background="@android:color/transparent"
+ android:layout_weight="0"/>
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..3ff874648d
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..3ff874648d
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000000..f043b66984
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..53e483046c
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..5646cd4957
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000000..8121c6f7c2
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..da81ab2c05
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..d68eb57118
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..818fa2b2dc
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..57d6591183
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..cf05b107f0
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..30855a994a
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..385517a7eb
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..10ec83e5f4
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..5a9dab1d44
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..62d203c390
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..52d2c11162
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/colors.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..30d00eda0f
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/colors.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+ <color name="lilac">#AF93DF</color>
+</resources>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/strings.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..67d301c48c
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+<resources>
+ <string name="app_name">qml_in_java_based_android_project</string>
+ <string name="button">Change color</string>
+ <string name="java">Java</string>
+ <string name="change_qml_background">Tap button to change QML view background color</string>
+ <string name="disconnect_qml_button_signal_listener">Tap switch to disconnect QML button signal listener</string>
+ <string name="connect_qml_button_signal_listener">Tap switch to connect QML button signal listener</string>
+ <string name="on">On</string>
+ <string name="off">Off</string>
+ <string name="qml_view_status">QML view status: </string>
+ <string name="qml_view_background_color">QML view background color:</string>
+</resources>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/styles.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000000..bce864063a
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/styles.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="switchStyle">
+ <item name="android:textSize">12sp</item>
+ </style>
+</resources>
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/themes.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000000..515cb95981
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_500</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/white</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_700</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/backup_rules.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000000..04dd1acfe3
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+</full-backup-content>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/data_extraction_rules.xml b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000000..9840b57766
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample data extraction rules file; uncomment and customize as necessary.
+ See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+ for details.
+-->
+<data-extraction-rules>
+ <cloud-backup>
+ </cloud-backup>
+</data-extraction-rules>
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/build.gradle b/examples/platforms/android/qml_in_java_based_android_project/build.gradle
new file mode 100644
index 0000000000..b92d690313
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/build.gradle
@@ -0,0 +1,4 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+id 'com.android.application' version '7.4.1' apply false
+}
diff --git a/examples/platforms/android/qml_in_java_based_android_project/gradle.properties b/examples/platforms/android/qml_in_java_based_android_project/gradle.properties
new file mode 100644
index 0000000000..dacb776f4a
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/gradle.properties
@@ -0,0 +1,22 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+
diff --git a/examples/platforms/android/qml_in_java_based_android_project/gradle/wrapper/gradle-wrapper.properties b/examples/platforms/android/qml_in_java_based_android_project/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..e94b8a6800
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Feb 08 15:14:57 EET 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/examples/platforms/android/qml_in_java_based_android_project/settings.gradle b/examples/platforms/android/qml_in_java_based_android_project/settings.gradle
new file mode 100644
index 0000000000..80a48f7b1a
--- /dev/null
+++ b/examples/platforms/android/qml_in_java_based_android_project/settings.gradle
@@ -0,0 +1,17 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "qml_in_java_based_android_project"
+include ':app'
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/CMakeLists.txt b/examples/platforms/android/qml_in_kotlin_based_android_project/CMakeLists.txt
new file mode 100644
index 0000000000..efa2575b8f
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+cmake_minimum_required(VERSION 3.16)
+
+project(qml_in_kotlin_based_android_project VERSION 0.1 LANGUAGES CXX)
+
+install(DIRECTORY
+ gradle
+ app
+ DESTINATION .
+)
+install(FILES
+ settings.gradle
+ gradle.properties
+ build.gradle
+ CMakeLists.txt
+ DESTINATION .
+)
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/build.gradle b/examples/platforms/android/qml_in_kotlin_based_android_project/app/build.gradle
new file mode 100644
index 0000000000..be631746b6
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/build.gradle
@@ -0,0 +1,64 @@
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'com.example.qml_in_kotlin_based_android_project'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.example.qml_in_kotlin_based_android_project"
+ minSdk 26
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildFeatures {
+ viewBinding = true
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ packagingOptions {
+ jniLibs {
+ useLegacyPackaging true
+ }
+ }
+ sourceSets {
+ main {
+ assets {
+ srcDirs 'assets'
+ }
+ jniLibs {
+ srcDirs 'libs'
+ }
+ }
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.10.1'
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.11.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+}
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/AndroidManifest.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..fb183e2d79
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <application
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.Qml_in_kotlin_based_android_project"
+ tools:targetApi="34">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:configChanges="screenSize|screenLayout|orientation">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/Colors.kt b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/Colors.kt
new file mode 100644
index 0000000000..7770497796
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/Colors.kt
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+package com.example.qml_in_kotlin_based_android_project
+
+import java.util.Collections
+import java.util.Stack
+
+internal class Colors {
+ private val recycle: Stack<Int> = Stack()
+ private val colors: Stack<Int> = Stack()
+
+ init {
+ recycle.addAll(
+ mutableListOf(
+ -0xe34997, -0xffbeb6, -0xd8ec75,
+ -0x4a3ef2, -0xc8c0da, -0x506c21,
+ -0x7e8afb
+ )
+ )
+ }
+
+ fun getColor(): String {
+ if (colors.size == 0) {
+ while (!recycle.isEmpty()) colors.push(recycle.pop())
+ Collections.shuffle(colors)
+ }
+ val color = colors.pop()
+ recycle.push(color)
+ return String.format("#%06X", 0xFFFFFF and color)
+ }
+}
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt
new file mode 100644
index 0000000000..4ec1591709
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/java/com/example/qml_in_kotlin_based_android_project/MainActivity.kt
@@ -0,0 +1,156 @@
+package com.example.qml_in_kotlin_based_android_project
+
+import android.content.res.Configuration
+import android.graphics.Color
+import android.os.Bundle
+import android.util.DisplayMetrics
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.appcompat.app.AppCompatActivity
+import com.example.qml_in_kotlin_based_android_project.databinding.ActivityMainBinding
+import org.qtproject.qt.android.QtQuickView
+
+class MainActivity : AppCompatActivity(), QtQuickView.StatusChangeListener {
+
+ private val TAG = "myTag"
+ private val m_colors: Colors = Colors()
+ private lateinit var m_binding: ActivityMainBinding
+ private var m_qmlButtonSignalListenerId = 0
+ private var m_qmlView: QtQuickView? = null
+ private val m_statusNames = hashMapOf(
+ QtQuickView.STATUS_READY to "READY",
+ QtQuickView.STATUS_LOADING to "LOADING",
+ QtQuickView.STATUS_ERROR to "ERROR",
+ QtQuickView.STATUS_NULL to "NULL"
+ )
+ //! [onCreate]
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ //! [binding]
+ m_binding = ActivityMainBinding.inflate(layoutInflater)
+ val view = m_binding.root
+ setContentView(view)
+ //! [binding]
+
+ m_binding.signalSwitch.setOnClickListener { switchListener() }
+
+ //! [m_qmlView]
+ m_qmlView = QtQuickView(
+ this, "qrc:/qt/qml/qml_in_android_view/main.qml",
+ "qml_in_android_view"
+ )
+ //! [m_qmlView]
+
+ // Set status change listener for m_qmlView
+ // listener implemented below in OnStatusChanged
+ //! [setStatusChangeListener]
+ m_qmlView!!.setStatusChangeListener(this)
+ //! [setStatusChangeListener]
+
+ //! [layoutParams]
+ val params: ViewGroup.LayoutParams = FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ m_binding.qmlFrame.addView(m_qmlView, params)
+ //! [layoutParams]
+
+ m_binding.changeColorButton.setOnClickListener { onClickListener() }
+
+ // Check target device orientation on launch
+ handleOrientationChanges()
+ }
+ //! [onCreate]
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ handleOrientationChanges()
+ }
+
+ private fun handleOrientationChanges() {
+ // When specific target device display configurations (listed in AndroidManifest.xml
+ // android:configChanges) change, get display metrics and make needed changes to UI
+ val displayMetrics = DisplayMetrics()
+ windowManager.defaultDisplay.getMetrics(displayMetrics)
+ val qmlFrameLayoutParams = m_binding.qmlFrame.layoutParams
+ val linearLayoutParams = m_binding.kotlinLinear.layoutParams
+
+ if (displayMetrics.heightPixels > displayMetrics.widthPixels) {
+ m_binding.mainLinear.orientation = LinearLayout.VERTICAL
+ qmlFrameLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
+ qmlFrameLayoutParams.height = 0
+ linearLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
+ linearLayoutParams.height = 0
+ } else {
+ m_binding.mainLinear.orientation = LinearLayout.HORIZONTAL
+ qmlFrameLayoutParams.width = 0
+ qmlFrameLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+ linearLayoutParams.width = 0
+ linearLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+ }
+ m_binding.qmlFrame.layoutParams = qmlFrameLayoutParams
+ m_binding.kotlinLinear.layoutParams = linearLayoutParams
+ }
+ //! [onClickListener]
+ private fun onClickListener() {
+ // Set the QML view root object property "colorStringFormat" value to
+ // color from Colors.getColor()
+ m_qmlView!!.setProperty("colorStringFormat", m_colors.getColor())
+
+ val qmlBackgroundColor = m_qmlView!!.getProperty<String>("colorStringFormat")
+
+ // Display the QML View background color code
+ m_binding.getPropertyValueText.text = qmlBackgroundColor
+
+ // Display the QML View background color in a view
+ m_binding.colorBox.setBackgroundColor(Color.parseColor(qmlBackgroundColor))
+ }
+ //! [onClickListener]
+
+ private fun switchListener() {
+ // Disconnect QML button signal listener if switch is On using the saved signal listener Id
+ // and connect it again if switch is turned off
+ if (m_binding.signalSwitch.isChecked) {
+ Log.v(TAG, "QML button onClicked signal listener disconnected")
+ m_binding.switchText.setText(R.string.connect_qml_button_signal_listener)
+ //! [disconnect qml signal listener]
+ m_qmlView!!.disconnectSignalListener(m_qmlButtonSignalListenerId)
+ //! [disconnect qml signal listener]
+ } else {
+ Log.v(TAG, "QML button onClicked signal listener connected")
+ m_binding.switchText.setText(R.string.disconnect_qml_button_signal_listener)
+ m_qmlButtonSignalListenerId = m_qmlView!!.connectSignalListener<Any>(
+ "onClicked",
+ Any::class.java
+ ) { t: String?, value: Any? ->
+ Log.i(TAG, "QML button clicked")
+ m_binding.kotlinLinear.setBackgroundColor(Color.parseColor(m_colors.getColor()))
+ }
+ }
+ }
+
+ //! [onStatusChanged]
+ override fun onStatusChanged(status: Int) {
+ Log.v(TAG, "Status of QtQuickView: $status")
+
+ val qmlStatus = (resources.getString(R.string.qml_view_status)
+ + m_statusNames[status])
+
+ // Show current QML View status in a textview
+ m_binding.qmlStatus.text = qmlStatus
+
+ // Connect signal listener to "onClicked" signal from main.qml
+ // addSignalListener returns int which can be used later to identify the listener
+ //! [qml signal listener]
+ if (status == QtQuickView.STATUS_READY && !m_binding.signalSwitch.isChecked) {
+ m_qmlButtonSignalListenerId = m_qmlView!!.connectSignalListener(
+ "onClicked", Any::class.java
+ ) { _: String?, _: Any? ->
+ Log.v(TAG, "QML button clicked")
+ m_binding.kotlinLinear.setBackgroundColor(Color.parseColor(m_colors.getColor()))
+ }
+ }
+ //! [qml signal listener]
+ }
+ //! [onStatusChanged]
+}
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..ca3826a46c
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector
+ android:height="108dp"
+ android:width="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z"/>
+ <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+</vector>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..7706ab9e6d
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/layout/activity_main.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000000..03f01fdd25
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/mainLinear"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity"
+ android:orientation="vertical"
+ android:baselineAligned="false">
+
+ <FrameLayout
+ android:id="@+id/qmlFrame"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/kotlinLinear"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="@color/lilac"
+ android:orientation="vertical">
+
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:gravity="center_horizontal"
+ android:includeFontPadding="false"
+ android:text="@string/kotlin"
+ android:textColor="@color/white"
+ android:textSize="24sp"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/qmlStatus"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="16dp"
+ android:gravity="center_horizontal"
+ android:text="@string/qml_view_status"
+ android:textColor="@color/white"/>
+
+ <LinearLayout
+ android:id="@+id/buttonAndSwitchLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:layout_marginTop="16dp">
+
+ <LinearLayout
+ android:id="@+id/buttonLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/changeColorText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="3"
+ android:text="@string/change_qml_background"
+ android:textColor="@color/white" />
+
+ <Button
+ android:id="@+id/changeColorButton"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:text="@string/button"
+ android:textSize="14sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/switchLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/switchText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="3"
+ android:text="@string/disconnect_qml_button_signal_listener"
+ android:textColor="@color/white" />
+
+ <androidx.appcompat.widget.SwitchCompat
+ android:id="@+id/signalSwitch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:switchTextAppearance="@android:style/TextAppearance.Small"
+ android:textOff="@string/off"
+ android:textOn="@string/on"
+ app:switchTextAppearance="@style/switchStyle"
+ app:showText="true"
+ tools:ignore="UseSwitchCompatOrMaterialXml" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/qmlColorLinear"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:padding="10dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/qmlViewBackgroundText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:maxLines="2"
+ android:text="@string/qml_view_background_color"
+ android:textColor="@color/white" />
+
+ <TextView
+ android:id="@+id/getPropertyValueText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:textColor="@color/white" />
+ </LinearLayout>
+
+ <View
+ android:id="@+id/colorBox"
+ android:layout_width="100dp"
+ android:layout_height="50dp"
+ android:layout_gravity="center_horizontal"
+ android:background="@android:color/transparent"
+ android:layout_weight="0"/>
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..3ff874648d
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
+
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..3ff874648d
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
+
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000000..f043b66984
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..53e483046c
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..5646cd4957
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000000..8121c6f7c2
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..da81ab2c05
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..d68eb57118
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..818fa2b2dc
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..57d6591183
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..cf05b107f0
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..30855a994a
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..385517a7eb
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..10ec83e5f4
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000000..5a9dab1d44
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000..62d203c390
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000..52d2c11162
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/colors.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..d38e4ce6b3
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+ <color name="lilac">#AF93DF</color>
+</resources>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/strings.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..12661a6334
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+<resources>
+ <string name="app_name">qml_in_kotlin_based_android_project</string>
+ <string name="button">Change color</string>
+ <string name="kotlin">Kotlin</string>
+ <string name="change_qml_background">Tap button to change QML view background color</string>
+ <string name="disconnect_qml_button_signal_listener">Tap switch to disconnect QML button signal listener</string>
+ <string name="connect_qml_button_signal_listener">Tap switch to connect QML button signal listener</string>
+ <string name="on">On</string>
+ <string name="off">Off</string>
+ <string name="qml_view_status">QML view status: </string>
+ <string name="qml_view_background_color">QML view background color:</string>
+</resources>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/styles.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000000..bce864063a
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/styles.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="switchStyle">
+ <item name="android:textSize">12sp</item>
+ </style>
+</resources>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/themes.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000000..b501d7b323
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.Qml_in_kotlin_based_android_project" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_500</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/white</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_700</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/backup_rules.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000000..148c18b659
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+ <!--
+ <include domain="sharedpref" path="."/>
+ <exclude domain="sharedpref" path="device.xml"/>
+-->
+</full-backup-content>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/data_extraction_rules.xml b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000000..0c4f95cab9
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample data extraction rules file; uncomment and customize as necessary.
+ See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+ for details.
+-->
+<data-extraction-rules>
+ <cloud-backup>
+ <!-- TODO: Use <include> and <exclude> to control what is backed up.
+ <include .../>
+ <exclude .../>
+ -->
+ </cloud-backup>
+ <!--
+ <device-transfer>
+ <include .../>
+ <exclude .../>
+ </device-transfer>
+ -->
+</data-extraction-rules>
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/build.gradle b/examples/platforms/android/qml_in_kotlin_based_android_project/build.gradle
new file mode 100644
index 0000000000..97e3f23ff2
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/build.gradle
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+id 'com.android.application' version '8.2.2' apply false
+ id 'org.jetbrains.kotlin.android' version '1.9.22' apply false
+}
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/gradle.properties b/examples/platforms/android/qml_in_kotlin_based_android_project/gradle.properties
new file mode 100644
index 0000000000..2cbd6d19d3
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/gradle/wrapper/gradle-wrapper.properties b/examples/platforms/android/qml_in_kotlin_based_android_project/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..a3353773cb
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Mar 21 11:14:46 EET 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/examples/platforms/android/qml_in_kotlin_based_android_project/settings.gradle b/examples/platforms/android/qml_in_kotlin_based_android_project/settings.gradle
new file mode 100644
index 0000000000..a30bfe06d0
--- /dev/null
+++ b/examples/platforms/android/qml_in_kotlin_based_android_project/settings.gradle
@@ -0,0 +1,17 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "qml_in_kotlin_based_android_project"
+include ':app'
diff --git a/examples/qml/CMakeLists.txt b/examples/qml/CMakeLists.txt
index 7bc4cb2625..e5dc00d0fd 100644
--- a/examples/qml/CMakeLists.txt
+++ b/examples/qml/CMakeLists.txt
@@ -1,17 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-add_subdirectory(referenceexamples)
add_subdirectory(tutorials)
-qt_internal_add_example(shell)
-if(TARGET Qt::Quick)
- qt_internal_add_example(qmlextensionplugins)
- qt_internal_add_example(xmlhttprequest)
- if (TARGET Qt::lupdate)
+if(TARGET Qt6::Quick)
+ if (TARGET Qt6::lupdate)
qt_internal_add_example(qml-i18n)
endif()
- qt_internal_add_example(dynamicscene)
-endif()
-if(QT_FEATURE_qml_network AND TARGET Qt::Quick)
- qt_internal_add_example(networkaccessmanagerfactory)
endif()
+
diff --git a/examples/qml/doc/src/qml-extending.qdoc b/examples/qml/doc/src/qml-extending.qdoc
deleted file mode 100644
index 5d92f0a51c..0000000000
--- a/examples/qml/doc/src/qml-extending.qdoc
+++ /dev/null
@@ -1,339 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
-\example referenceexamples/adding
-\title Extending QML - Adding Types Example
-\brief Exporting C++ Classes.
-\ingroup qmlextendingexamples
-
-The Adding Types Example shows how to add a new object type, \c Person, to QML.
-The \c Person type can be used from QML like this:
-
-\snippet referenceexamples/adding/example.qml 0
-
-\section1 Declare the Person Class
-
-All QML types map to C++ types. Here we declare a basic C++ Person class
-with the two properties we want accessible on the QML type - name and shoeSize.
-Although in this example we use the same name for the C++ class as the QML
-type, the C++ class can be named differently, or appear in a namespace.
-
-\snippet referenceexamples/adding/person.h 0
-
-\section1 Define the Person Class
-
-\snippet referenceexamples/adding/person.cpp 0
-
-The Person class implementation is quite basic. The property accessors simply
-return members of the object instance.
-
-\section1 Running the Example
-
-The main.cpp file in the example includes a simple shell application that
-loads and runs the QML snippet shown at the beginning of this page.
-*/
-
-/*!
-\example referenceexamples/extended
-\title Extending QML - Extension Objects Example
-\brief Extension Objects.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-Shows how to use \l {QML_EXTENDED} to provide an
-\l {Registering Extension Objects}{extension object} to a \l QLineEdit without modifying or
-subclassing it.
-
-Firstly, the LineEditExtension class is registered with the QML system as an
-extension of QLineEdit. We declare a foreign type to do this as we cannot modify
-Qt's internal QLineEdit class.
-
-\snippet referenceexamples/extended/lineedit.h 0
-
-Note the usage of \l QML_NAMED_ELEMENT() instead of \l QML_ELEMENT.
-QML_ELEMENT uses the name of the containing type by default, "LineEditExtension" in this case.
-As the class being an extension class is an implementation detail, we choose the more natural name "LineEdit" instead
-
-The QML engine then instantiates a \l QLineEdit:
-
-\snippet referenceexamples/extended/main.cpp 1
-
-In QML, a property is set on the line edit that only exists in the LineEditExtension class:
-
-\snippet referenceexamples/extended/example.qml 0
-
-The extension type performs calls on the \l QLineEdit that otherwise will
-not be accessible to the QML engine.
-*/
-
-/*!
-\example referenceexamples/properties
-\title Extending QML - Object and List Property Types Example
-\brief Exporting C++ Properties.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-The Object and List Property Types example shows how to add object and list
-properties in QML. This example adds a BirthdayParty type that specifies
-a birthday party, consisting of a celebrant and a list of guests. People are
-specified using the People QML type built in the previous example.
-
-\snippet referenceexamples/properties/example.qml 0
-
-\section1 Declare the BirthdayParty
-
-The BirthdayParty class is declared like this:
-
-\snippet referenceexamples/properties/birthdayparty.h 0
-\snippet referenceexamples/properties/birthdayparty.h 1
-\snippet referenceexamples/properties/birthdayparty.h 2
-\snippet referenceexamples/properties/birthdayparty.h 3
-
-The class contains a member to store the celebrant object, and also a
-QList<Person *> member.
-
-In QML, the type of a list properties - and the guests property is a list of
-people - are all of type QQmlListProperty<T>. QQmlListProperty is simple value
-type that contains a set of function pointers. QML calls these function
-pointers whenever it needs to read from, write to or otherwise interact with
-the list. In addition to concrete lists like the people list used in this
-example, the use of QQmlListProperty allows for "virtual lists" and other advanced
-scenarios.
-
-\section2 Define the BirthdayParty
-
-The implementation of BirthdayParty property accessors is straight forward.
-
-\snippet referenceexamples/properties/birthdayparty.cpp 0
-
-\section1 Running the Example
-
-The main.cpp file in the example includes a simple shell application that
-loads and runs the QML snippet shown at the beginning of this page.
-*/
-
-/*!
-\example referenceexamples/coercion
-\title Extending QML - Inheritance and Coercion Example
-\brief C++ Inheritance and Coercion.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Object and List Property Types Example}
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-The Inheritance and Coercion Example shows how to use base classes to assign
-types of more than one type to a property. It specializes the Person type
-developed in the previous examples into two types - a \c Boy and a \c Girl.
-
-\snippet referenceexamples/coercion/example.qml 0
-
-\section1 Declare Boy and Girl
-
-\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
-registered with the QML engine.
-
-As an example, the inheritance used here is a little contrived, but in real
-applications it is likely that the two extensions would add additional
-properties or modify the Person classes behavior.
-
-\section2 Define People as a Base Class
-
-The implementation of the People class itself has not changed since the
-previous example. However, as we have repurposed the People class as a common
-base for Boy and Girl, we want to prevent it from being instantiated from QML
-directly - an explicit Boy or Girl should be instantiated instead.
-
-\snippet referenceexamples/coercion/person.h 0
-
-While we want to disallow instantiating Person from within QML, it still needs
-to be registered with the QML engine, so that it can be used as a property type
-and other types can be coerced to it. This is what the QML_UNCREATABLE macro
-does.
-
-\section1 Running the Example
-
-The BirthdayParty type has not changed since the previous example. The
-celebrant and guests property still use the People type.
-
-\snippet referenceexamples/coercion/birthdayparty.h 0
-
-However, as all three types, Person, Boy and Girl, have been registered with the
-QML system, on assignment QML automatically (and type-safely) converts the Boy
-and Girl objects into a Person.
-
-The main.cpp file in the example includes a simple shell application that
-loads and runs the QML snippet shown at the beginning of this page.
-*/
-
-/*!
-\example referenceexamples/default
-\title Extending QML - Default Property Example
-\brief Default Property.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Inheritance and Coercion Example}
-\li \l {Extending QML - Object and List Property Types Example}
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-The Default Property Example is a minor modification of the
-\l {Extending QML - Inheritance and Coercion Example} that simplifies the
-specification of a BirthdayParty through the use of a default property.
-
-\snippet referenceexamples/default/example.qml 0
-
-\section1 Declaring the BirthdayParty Class
-
-The only difference between this example and the last, is the addition of the
-\c DefaultProperty class info annotation.
-
-\snippet referenceexamples/default/birthdayparty.h 0
-
-The default property specifies the property to assign to whenever an explicit
-property is not specified, in the case of the BirthdayParty type the guest
-property. It is purely a syntactic simplification, the behavior is identical
-to specifying the property by name, but it can add a more natural feel in many
-situations. The default property must be either an object or list property.
-
-\section1 Running the Example
-
-The main.cpp file in the example includes a simple shell application that
-loads and runs the QML snippet shown at the beginning of this page.
-*/
-
-/*!
-\example referenceexamples/grouped
-\title Extending QML - Grouped Properties Example
-\brief Grouped Properties.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Default Property Example}
-\li \l {Extending QML - Inheritance and Coercion Example}
-\li \l {Extending QML - Object and List Property Types Example}
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-*/
-
-/*!
-\example referenceexamples/attached
-\title Extending QML - Attached Properties Example
-\brief Attached Properties.
-\ingroup qmlextendingexamples
-
-This example demonstrates how to create custom
-\l {Attached Properties and Attached Signal Handlers} {attached properties}.
-For a more in-depth description on how one can create attached properties,
-see \l {Providing Attached Properties}.
-
-
-This example builds on:
-\list
-\li \l {Extending QML - Grouped Properties Example}
-\li \l {Extending QML - Default Property Example}
-\li \l {Extending QML - Inheritance and Coercion Example}
-\li \l {Extending QML - Object and List Property Types Example}
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-*/
-
-/*!
-\example referenceexamples/signal
-\title Extending QML - Signal Support Example
-\brief Signal Support.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Attached Properties Example}
-\li \l {Extending QML - Grouped Properties Example}
-\li \l {Extending QML - Default Property Example}
-\li \l {Extending QML - Inheritance and Coercion Example}
-\li \l {Extending QML - Object and List Property Types Example}
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-*/
-
-/*!
-\example referenceexamples/methods
-\title Extending QML - Methods Example
-\brief Methods Support.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Inheritance and Coercion Example}
-\li \l {Extending QML - Object and List Property Types Example}
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-The Methods Example has an additional method in the \c BirthdayParty class: \c invite().
-\c invite() is declared with \l Q_INVOKABLE so that it can be
-called from QML.
-
-\snippet referenceexamples/methods/birthdayparty.h 0
-
-In \c example.qml, the \c invite() method is called in the \l [QML]{QtQml::Component::completed()}{Component.onCompleted} signal handler:
-
-\snippet referenceexamples/methods/example.qml 0
-*/
-
-/*!
-\example referenceexamples/valuesource
-\title Extending QML - Property Value Source Example
-\brief Property Value Source.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Signal Support Example}
-\li \l {Extending QML - Attached Properties Example}
-\li \l {Extending QML - Grouped Properties Example}
-\li \l {Extending QML - Default Property Example}
-\li \l {Extending QML - Inheritance and Coercion Example}
-\li \l {Extending QML - Object and List Property Types Example}
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-*/
-
-/*!
-\example referenceexamples/binding
-\title Extending QML - Binding Example
-\brief Binding.
-\ingroup qmlextendingexamples
-
-This example builds on:
-\list
-\li \l {Extending QML - Property Value Source Example}
-\li \l {Extending QML - Signal Support Example}
-\li \l {Extending QML - Attached Properties Example}
-\li \l {Extending QML - Grouped Properties Example}
-\li \l {Extending QML - Default Property Example}
-\li \l {Extending QML - Inheritance and Coercion Example}
-\li \l {Extending QML - Object and List Property Types Example}
-\li \l {Extending QML - Adding Types Example}
-\endlist
-
-*/
diff --git a/examples/qml/dynamicscene/CMakeLists.txt b/examples/qml/dynamicscene/CMakeLists.txt
deleted file mode 100644
index b91ed5aca7..0000000000
--- a/examples/qml/dynamicscene/CMakeLists.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(dynamicscene LANGUAGES CXX)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/dynamicscene")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_add_executable(dynamicsceneexample WIN32 MACOSX_BUNDLE main.cpp)
-
-target_link_libraries(dynamicsceneexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-qt_add_qml_module(dynamicsceneexample
- URI dynamicscene
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "GenericSceneItem.qml"
- "PaletteItem.qml"
- "PerspectiveItem.qml"
- "Sun.qml"
- "dynamicscene.qml"
- "itemCreation.js"
- RESOURCES
- "images/NOTE"
- "images/face-smile.png"
- "images/moon.png"
- "images/rabbit_brown.png"
- "images/rabbit_bw.png"
- "images/star.png"
- "images/sun.png"
- "images/tree_s.png"
-)
-
-install(TARGETS dynamicsceneexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/dynamicscene/GenericSceneItem.qml b/examples/qml/dynamicscene/GenericSceneItem.qml
deleted file mode 100644
index 5ff3760bb7..0000000000
--- a/examples/qml/dynamicscene/GenericSceneItem.qml
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Image {
- property bool created: false
- property string image
-
- source: image
-
-}
diff --git a/examples/qml/dynamicscene/PaletteItem.qml b/examples/qml/dynamicscene/PaletteItem.qml
deleted file mode 100644
index ef03b8ce38..0000000000
--- a/examples/qml/dynamicscene/PaletteItem.qml
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import "itemCreation.js" as Code
-
-Image {
- id: paletteItem
-
- property string componentFile
- property string image
-
- source: image
-
- MouseArea {
- anchors.fill: parent
-
- onPressed: (mouse)=> Code.startDrag(mouse);
- onPositionChanged: (mouse)=> Code.continueDrag(mouse);
- onReleased: (mouse)=> Code.endDrag(mouse);
- }
-}
diff --git a/examples/qml/dynamicscene/PerspectiveItem.qml b/examples/qml/dynamicscene/PerspectiveItem.qml
deleted file mode 100644
index 8857b92376..0000000000
--- a/examples/qml/dynamicscene/PerspectiveItem.qml
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Image {
- id: rootItem
-
- property bool created: false
- property string image
-
- property double scaledBottom: y + (height + height*scale) / 2
- property bool onLand: scaledBottom > (window.height / 2 + window.centerOffset)
-
- source: image
- opacity: onLand ? 1 : 0.25
- scale: Math.max((y + height - 250) * 0.01, 0.3)
-
- onCreatedChanged: {
- if (created && !onLand)
- rootItem.destroy();
- else
- z = scaledBottom;
- }
-
- onYChanged: z = scaledBottom;
-}
diff --git a/examples/qml/dynamicscene/Sun.qml b/examples/qml/dynamicscene/Sun.qml
deleted file mode 100644
index e755563eba..0000000000
--- a/examples/qml/dynamicscene/Sun.qml
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Image {
- id: sun
-
- property bool created: false
- property string image: "images/sun.png"
-
- source: image
- onCreatedChanged: {
- if (created) {
- sun.z = 1; // above the sky but below the ground layer
- window.activeSuns++;
- // once item is created, start moving offscreen
- dropYAnim.duration = (window.height + window.centerOffset - sun.y) * 16;
- dropAnim.running = true;
- } else {
- window.activeSuns--;
- }
- }
-
- SequentialAnimation on y{
- id: dropAnim
- running: false
- NumberAnimation {
- id: dropYAnim
- to: (window.height / 2) + window.centerOffset
- }
- ScriptAction {
- script: { sun.created = false; sun.destroy() }
- }
- }
-}
diff --git a/examples/qml/dynamicscene/doc/images/qml-dynamicscene-example.png b/examples/qml/dynamicscene/doc/images/qml-dynamicscene-example.png
deleted file mode 100644
index 38260a7d3f..0000000000
--- a/examples/qml/dynamicscene/doc/images/qml-dynamicscene-example.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/dynamicscene/doc/src/dynamicscene.qdoc b/examples/qml/dynamicscene/doc/src/dynamicscene.qdoc
deleted file mode 100644
index a7a3351fdc..0000000000
--- a/examples/qml/dynamicscene/doc/src/dynamicscene.qdoc
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-/*!
- \title QML Example - Dynamic Scene
- \example dynamicscene
- \brief This example demonstrates creating components dynamically.
- \image qml-dynamicscene-example.png
-*/
-
-
diff --git a/examples/qml/dynamicscene/dynamicscene.pro b/examples/qml/dynamicscene/dynamicscene.pro
deleted file mode 100644
index 29a3fa97e2..0000000000
--- a/examples/qml/dynamicscene/dynamicscene.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-
-RESOURCES += dynamicscene.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/dynamicscene
-INSTALLS += target
diff --git a/examples/qml/dynamicscene/dynamicscene.qml b/examples/qml/dynamicscene/dynamicscene.qml
deleted file mode 100644
index 62795703c5..0000000000
--- a/examples/qml/dynamicscene/dynamicscene.qml
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Particles
-import QtQuick.Controls
-
-Item {
- id: window
-
- property int activeSuns: 0
- property int centerOffset: 72
-
- height: 480; width: 320
-
-
- MouseArea {
- anchors.fill: parent
- onClicked: window.focus = false;
- }
-
- //This is the message box that pops up when there's an error
- Rectangle {
- id: dialog
-
- opacity: 0
- anchors.centerIn: parent
- width: dialogText.width + 6; height: dialogText.height + 6
- border.color: 'black'
- color: 'lightsteelblue'
- z: 65535 //Arbitrary number chosen to be above all the items, including the scaled perspective ones.
-
- function show(str){
- dialogText.text = str;
- dialogAnim.start();
- }
-
- Label {
- id: dialogText
- x: 3; y: 3
- font.pixelSize: 14
- }
-
- SequentialAnimation {
- id: dialogAnim
- NumberAnimation { target: dialog; property:"opacity"; to: 1; duration: 1000 }
- PauseAnimation { duration: 5000 }
- NumberAnimation { target: dialog; property:"opacity"; to: 0; duration: 1000 }
- }
- }
-
- Item {
- id: scene
- anchors { top: sky.top; bottom: ground.bottom; left: parent.left; right: parent.right}
- z: 10
- }
-
- // sky
- Rectangle {
- id: sky
- anchors { left: parent.left; top: toolbox.bottom; right: parent.right; bottomMargin: -window.centerOffset; bottom: parent.verticalCenter }
- gradient: Gradient {
- GradientStop { id: gradientStopA; position: 0.0; color: "#0E1533" }
- GradientStop { id: gradientStopB; position: 1.0; color: "#437284" }
- }
- }
-
- // stars (when there's no sun)
- ParticleSystem {
- id: particlesystem
- anchors.fill: sky
-
- ImageParticle {
- id: stars
- source: "images/star.png"
- groups: ["stars"]
- opacity: .5
- }
-
- Emitter {
- id: starsemitter
- anchors.fill: parent
- emitRate: parent.width / 50
- lifeSpan: 5000
- group: "stars"
- }
- }
-
- // ground
- Rectangle {
- id: ground
- z: 2 // just above the sun so that the sun can set behind it
- anchors { left: parent.left; top: parent.verticalCenter; topMargin: window.centerOffset; right: parent.right; bottom: parent.bottom }
- gradient: Gradient {
- GradientStop { position: 0.0; color: "ForestGreen" }
- GradientStop { position: 1.0; color: "DarkGreen" }
- }
- }
-
- // top panel
- Rectangle {
- id: toolbox
-
- height: window.centerOffset * 2
- color: "white"
- anchors { right: parent.right; top: parent.top; left: parent.left}
-
- Column {
- anchors.centerIn: parent
- spacing: 8
-
- Label { text: "Drag an item into the scene." }
-
- Rectangle {
- width: palette.width + 10; height: palette.height + 10
- border.color: "black"
-
- Row {
- id: palette
- anchors.centerIn: parent
- spacing: 8
-
- PaletteItem {
- anchors.verticalCenter: parent.verticalCenter
- componentFile: "Sun.qml"
- source: "images/sun.png"
- image: "images/sun.png"
- }
- PaletteItem {
- anchors.verticalCenter: parent.verticalCenter
- componentFile: "GenericSceneItem.qml"
- source: "images/moon.png"
- image: "images/moon.png"
- }
- PaletteItem {
- anchors.verticalCenter: parent.verticalCenter
- componentFile: "PerspectiveItem.qml"
- source: "images/tree_s.png"
- image: "images/tree_s.png"
- }
- PaletteItem {
- anchors.verticalCenter: parent.verticalCenter
- componentFile: "PerspectiveItem.qml"
- source: "images/rabbit_brown.png"
- image: "images/rabbit_brown.png"
- }
- PaletteItem {
- anchors.verticalCenter: parent.verticalCenter
- componentFile: "PerspectiveItem.qml"
- source: "images/rabbit_bw.png"
- image: "images/rabbit_bw.png"
- }
- }
- }
-
- Label { text: "Active Suns: " + window.activeSuns }
- }
- }
-
- //Popup toolbox down the bottom
- Rectangle {
- id: popupToolbox
- z: 1000
- width: parent.width
- height: popupColumn.height + 16
- color: "white"
-
- property bool poppedUp: false
- property int downY: window.height - (createButton.height + 16)
- property int upY: window.height - (popupColumn.height + 16)
- y: poppedUp ? upY : downY
- Behavior on y { NumberAnimation {}}
-
- Column {
- id: popupColumn
- y: 8
- spacing: 8
-
- Row {
- height: createButton.height
- spacing: 8
- Label { text: "Custom QML:"; anchors.verticalCenter: parent.verticalCenter }
- Button {
- id: popupButton
- text: popupToolbox.poppedUp ? "Hide" : "Show"
- onClicked: popupToolbox.poppedUp = !popupToolbox.poppedUp
- }
- Button {
- id: createButton
- text: "Create"
- onClicked: {
- try {
- Qt.createQmlObject(qmlText.text, scene, 'CustomObject');
- } catch(err) {
- dialog.show('Error on line ' + err.qmlErrors[0].lineNumber + '\n' + err.qmlErrors[0].message);
- }
- }
- }
-
- }
-
- Rectangle {
- width: 360; height: 240
-
- TextArea {
- id: qmlText
- anchors.fill: parent; anchors.margins: 5
- readOnly: false
- font.pixelSize: 14
- selectByMouse: true
- wrapMode: TextEdit.WordWrap
-
- text: "import QtQuick\nImage {\n id: smile\n x: 360 * Math.random()\n y: 40 * Math.random() \n source: 'images/face-smile.png'\n NumberAnimation on opacity { \n to: 0; duration: 1500\n }\n Component.onCompleted: smile.destroy(1500);\n}"
- }
- }
- }
- }
-
- //Day state, for when a sun is added to the scene
- states: State {
- name: "Day"
- when: window.activeSuns > 0
-
- PropertyChanges {
- gradientStopA.color: "DeepSkyBlue"
- gradientStopB.color: "SkyBlue"
- stars.opacity: 0
- }
- }
-
- //! [top-level transitions]
- transitions: Transition {
- PropertyAnimation { duration: 3000 }
- ColorAnimation { duration: 3000 }
- }
- //! [top-level transitions]
-}
diff --git a/examples/qml/dynamicscene/dynamicscene.qmlproject b/examples/qml/dynamicscene/dynamicscene.qmlproject
deleted file mode 100644
index 52ac474383..0000000000
--- a/examples/qml/dynamicscene/dynamicscene.qmlproject
+++ /dev/null
@@ -1,16 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "dynamicscene.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/qml/dynamicscene/dynamicscene.qrc b/examples/qml/dynamicscene/dynamicscene.qrc
deleted file mode 100644
index f354fc469a..0000000000
--- a/examples/qml/dynamicscene/dynamicscene.qrc
+++ /dev/null
@@ -1,18 +0,0 @@
-<RCC>
- <qresource prefix="/qt/qml/dynamicscene">
- <file>dynamicscene.qml</file>
- <file>images/face-smile.png</file>
- <file>images/moon.png</file>
- <file>images/NOTE</file>
- <file>images/rabbit_brown.png</file>
- <file>images/rabbit_bw.png</file>
- <file>images/star.png</file>
- <file>images/sun.png</file>
- <file>images/tree_s.png</file>
- <file>GenericSceneItem.qml</file>
- <file>itemCreation.js</file>
- <file>PaletteItem.qml</file>
- <file>PerspectiveItem.qml</file>
- <file>Sun.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/qml/dynamicscene/images/NOTE b/examples/qml/dynamicscene/images/NOTE
deleted file mode 100644
index fcd87f9132..0000000000
--- a/examples/qml/dynamicscene/images/NOTE
+++ /dev/null
@@ -1 +0,0 @@
-Images (except star.png) are from the KDE project.
diff --git a/examples/qml/dynamicscene/images/face-smile.png b/examples/qml/dynamicscene/images/face-smile.png
deleted file mode 100644
index 3d66d72578..0000000000
--- a/examples/qml/dynamicscene/images/face-smile.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/dynamicscene/images/moon.png b/examples/qml/dynamicscene/images/moon.png
deleted file mode 100644
index 1c0d6066a8..0000000000
--- a/examples/qml/dynamicscene/images/moon.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/dynamicscene/images/rabbit_brown.png b/examples/qml/dynamicscene/images/rabbit_brown.png
deleted file mode 100644
index ebfdeed332..0000000000
--- a/examples/qml/dynamicscene/images/rabbit_brown.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/dynamicscene/images/rabbit_bw.png b/examples/qml/dynamicscene/images/rabbit_bw.png
deleted file mode 100644
index 7bff9b92ca..0000000000
--- a/examples/qml/dynamicscene/images/rabbit_bw.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/dynamicscene/images/star.png b/examples/qml/dynamicscene/images/star.png
deleted file mode 100644
index bdcd36909d..0000000000
--- a/examples/qml/dynamicscene/images/star.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/dynamicscene/images/sun.png b/examples/qml/dynamicscene/images/sun.png
deleted file mode 100644
index c5fd36ed39..0000000000
--- a/examples/qml/dynamicscene/images/sun.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/dynamicscene/images/tree_s.png b/examples/qml/dynamicscene/images/tree_s.png
deleted file mode 100644
index 6eac35a729..0000000000
--- a/examples/qml/dynamicscene/images/tree_s.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/dynamicscene/itemCreation.js b/examples/qml/dynamicscene/itemCreation.js
deleted file mode 100644
index 2829a83410..0000000000
--- a/examples/qml/dynamicscene/itemCreation.js
+++ /dev/null
@@ -1,62 +0,0 @@
-var itemComponent = null;
-var draggedItem = null;
-var startingMouse;
-var posnInWindow;
-
-function startDrag(mouse)
-{
- posnInWindow = paletteItem.mapToItem(window, 0, 0);
- startingMouse = { x: mouse.x, y: mouse.y }
- loadComponent();
-}
-
-//Creation is split into two functions due to an asynchronous wait while
-//possible external files are loaded.
-
-function loadComponent() {
- if (itemComponent != null) { // component has been previously loaded
- createItem();
- return;
- }
-
- itemComponent = Qt.createComponent(paletteItem.componentFile);
- if (itemComponent.status == Component.Loading) //Depending on the content, it can be ready or error immediately
- component.statusChanged.connect(createItem);
- else
- createItem();
-}
-
-function createItem() {
- if (itemComponent.status == Component.Ready && draggedItem == null) {
- draggedItem = itemComponent.createObject(window, {"image": paletteItem.image, "x": posnInWindow.x, "y": posnInWindow.y, "z": 3});
- // make sure created item is above the ground layer
- } else if (itemComponent.status == Component.Error) {
- draggedItem = null;
- console.log("error creating component");
- console.log(itemComponent.errorString());
- }
-}
-
-function continueDrag(mouse)
-{
- if (draggedItem == null)
- return;
-
- draggedItem.x = mouse.x + posnInWindow.x - startingMouse.x;
- draggedItem.y = mouse.y + posnInWindow.y - startingMouse.y;
-}
-
-function endDrag(mouse)
-{
- if (draggedItem == null)
- return;
-
- if (draggedItem.y < toolbox.height) { //Don't drop it in the toolbox
- draggedItem.destroy();
- draggedItem = null;
- } else {
- draggedItem.created = true;
- draggedItem = null;
- }
-}
-
diff --git a/examples/qml/dynamicscene/main.cpp b/examples/qml/dynamicscene/main.cpp
deleted file mode 100644
index e46981848a..0000000000
--- a/examples/qml/dynamicscene/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../../quick/shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(dynamicscene/dynamicscene)
diff --git a/examples/qml/locale/locale.qml b/examples/qml/locale/locale.qml
deleted file mode 100644
index 4308dd0c82..0000000000
--- a/examples/qml/locale/locale.qml
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- id: root
- width: 320
- height: 480
- color: "lightgray"
-
- component LocaleDelegate: Text {
- required property var modelData
- required property int index
-
- property string locale: modelData
- height: 30
- width: view.width
- text: `${Qt.locale(modelData).name} (${Qt.locale(modelData).nativeCountryName}/${Qt.locale(modelData).nativeLanguageName})`
- MouseArea {
- anchors.fill: parent
- onClicked: view.currentIndex = parent.index
- }
- }
-
- property string locale: (view.currentItem as LocaleDelegate).locale
-
- Text {
- id: title
- text: "Select locale:"
- }
-
- Rectangle {
- id: chooser
- anchors.top: title.bottom
- anchors.topMargin: 5
- width: parent.width-10
- x: 5
- height: parent.height/2 - 10
- color: "#40300030"
- ListView {
- id: view
- clip: true
- focus: true
- anchors.fill: parent
- model: [
- "en_US",
- "en_GB",
- "fi_FI",
- "de_DE",
- "ar_SA",
- "hi_IN",
- "zh_CN",
- "th_TH",
- "fr_FR",
- "nb_NO",
- "sv_SE"
- ]
-
- delegate: LocaleDelegate {}
- highlight: Rectangle {
- height: 30
- color: "#60300030"
- }
- }
- }
-
- Rectangle {
- color: "white"
- anchors.top: chooser.bottom
- anchors.topMargin: 5
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 5
- x: 5; width: parent.width - 10
-
- Column {
- anchors.fill: parent
- spacing: 5
- Text {
- property var date: new Date()
- text: "Date: " + date.toLocaleDateString(Qt.locale(root.locale))
- }
- Text {
- property var date: new Date()
- text: "Time: " + date.toLocaleTimeString(Qt.locale(root.locale))
- }
- Text {
- property var dow: Qt.locale(root.locale).firstDayOfWeek
- text: "First day of week: " + Qt.locale(root.locale).standaloneDayName(dow)
- }
- Text {
- property var num: 10023823
- text: "Number: " + num.toLocaleString(Qt.locale(root.locale))
- }
- Text {
- property var num: 10023823
- text: "Currency: " + num.toLocaleCurrencyString(Qt.locale(root.locale))
- }
- }
- }
-}
diff --git a/examples/qml/networkaccessmanagerfactory/CMakeLists.txt b/examples/qml/networkaccessmanagerfactory/CMakeLists.txt
deleted file mode 100644
index e50a97ab8c..0000000000
--- a/examples/qml/networkaccessmanagerfactory/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(networkaccessmanagerfactory LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/networkaccessmanagerfactory")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Qml Quick)
-
-qt_add_executable(networkaccessmanagerfactory
- main.cpp
-)
-
-set_target_properties(networkaccessmanagerfactory PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(networkaccessmanagerfactory PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Network
- Qt::Qml
- Qt::Quick
-)
-
-qt_add_qml_module(networkaccessmanagerfactory
- URI Example
- QML_FILES view.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS networkaccessmanagerfactory
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/networkaccessmanagerfactory/main.cpp b/examples/qml/networkaccessmanagerfactory/main.cpp
deleted file mode 100644
index dc00d88abe..0000000000
--- a/examples/qml/networkaccessmanagerfactory/main.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QCommandLineParser>
-#include <QGuiApplication>
-#include <QNetworkAccessManager>
-#include <QNetworkProxy>
-
-#include <QQmlEngine>
-#include <QQmlNetworkAccessManagerFactory>
-#include <QtQuick/QQuickView>
-
-
-/*
- This example illustrates using a QQmlNetworkAccessManagerFactory to
- create a QNetworkAccessManager with a proxy.
-
- Usage:
- networkaccessmanagerfactory [-host <proxy> -port <port>] [file]
-*/
-
-#if QT_CONFIG(networkproxy)
-static QString proxyHost;
-static int proxyPort = 0;
-#endif // networkproxy
-
-class MyNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
-{
-public:
- QNetworkAccessManager *create(QObject *parent) override;
-};
-
-QNetworkAccessManager *MyNetworkAccessManagerFactory::create(QObject *parent)
-{
- QNetworkAccessManager *nam = new QNetworkAccessManager(parent);
-#if QT_CONFIG(networkproxy)
- if (!proxyHost.isEmpty()) {
- qDebug() << "Created QNetworkAccessManager using proxy" << (proxyHost + ":" + QString::number(proxyPort));
- QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, proxyHost, proxyPort);
- nam->setProxy(proxy);
- }
-#endif // networkproxy
-
- return nam;
-}
-
-int main(int argc, char ** argv)
-{
- QUrl source("qrc:view.qml");
-
- QGuiApplication app(argc, argv);
-
- QCommandLineParser parser;
-#if QT_CONFIG(networkproxy)
- QCommandLineOption proxyHostOption("host", "The proxy host to use.", "host");
- parser.addOption(proxyHostOption);
- QCommandLineOption proxyPortOption("port", "The proxy port to use.", "port", "0");
- parser.addOption(proxyPortOption);
-#endif // networkproxy
- parser.addPositionalArgument("file", "The file to use.");
- QCommandLineOption helpOption = parser.addHelpOption();
- parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
- QStringList arguments = QCoreApplication::arguments();
- if (!parser.parse(arguments)) {
- qWarning() << parser.helpText() << '\n' << parser.errorText();
- exit(1);
- }
- if (parser.isSet(helpOption)) {
- qWarning() << parser.helpText();
- exit(0);
- }
-#if QT_CONFIG(networkproxy)
- if (parser.isSet(proxyHostOption))
- proxyHost = parser.value(proxyHostOption);
- if (parser.isSet(proxyPortOption)) {
- bool ok = true;
- proxyPort = parser.value(proxyPortOption).toInt(&ok);
- if (!ok || proxyPort < 1 || proxyPort > 65535) {
- qWarning() << parser.helpText() << "\nNo valid port given. It should\
- be a number between 1 and 65535";
- exit(1);
- }
- }
-#endif // networkproxy
- if (parser.positionalArguments().count() == 1)
- source = QUrl::fromLocalFile(parser.positionalArguments().first());
-
- QQuickView view;
- MyNetworkAccessManagerFactory networkManagerFactory;
- view.engine()->setNetworkAccessManagerFactory(&networkManagerFactory);
-
- view.setSource(source);
- view.show();
-
- return app.exec();
-}
-
diff --git a/examples/qml/networkaccessmanagerfactory/view.qml b/examples/qml/networkaccessmanagerfactory/view.qml
deleted file mode 100644
index f3c845fabd..0000000000
--- a/examples/qml/networkaccessmanagerfactory/view.qml
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Image {
- width: 200
- height: 100
- source: "http://doc.qt.io/qt-6/images/logo.png"
-}
diff --git a/examples/qml/qml-i18n/CMakeLists.txt b/examples/qml/qml-i18n/CMakeLists.txt
index 83bec4d11b..fd242d3678 100644
--- a/examples/qml/qml-i18n/CMakeLists.txt
+++ b/examples/qml/qml-i18n/CMakeLists.txt
@@ -1,46 +1,45 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(qml-i18n LANGUAGES CXX)
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/qmli18n")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick Qml LinguistTools)
+qt_standard_project_setup(REQUIRES 6.5 I18N_TRANSLATED_LANGUAGES en en_AU fr)
+
qt_add_executable(qmli18n WIN32 MACOSX_BUNDLE main.cpp)
target_link_libraries(qmli18n PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(qmli18n
- URI i18n
+ URI Translated
QML_FILES
- qml-i18n.qml
- NO_RESOURCE_TARGET_PATH
+ Main.qml
)
qt_add_translations(qmli18n
- TS_FILES
- i18n/base.ts
- i18n/qml_en.ts
- i18n/qml_en_AU.ts
- i18n/qml_fr.ts
+ RESOURCE_PREFIX /qt/qml/Translated/i18n
+ TS_FILE_BASE qml
+ TS_FILE_DIR i18n
)
install(TARGETS qmli18n
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET qmli18n
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/qml-i18n/qml-i18n.qml b/examples/qml/qml-i18n/Main.qml
index 8d414348dd..8d414348dd 100644
--- a/examples/qml/qml-i18n/qml-i18n.qml
+++ b/examples/qml/qml-i18n/Main.qml
diff --git a/examples/qml/qml-i18n/doc/src/i18n.qdoc b/examples/qml/qml-i18n/doc/src/i18n.qdoc
index 3ea56f6518..6096aa3383 100644
--- a/examples/qml/qml-i18n/doc/src/i18n.qdoc
+++ b/examples/qml/qml-i18n/doc/src/i18n.qdoc
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\title Qt Quick I18N
+ \examplecategory {User Interface Components}
\example qml-i18n
\image qml-i18n-example.png
\ingroup examples-linguist
@@ -13,10 +14,10 @@
\section1 Marking Text as Translatable
- In the \c qml-i18n.qml file, use the \c qsTr command to mark UI text as
+ In the \c Main.qml file, use the \c qsTr command to mark UI text as
translatable:
- \quotefromfile qml-i18n/qml-i18n.qml
+ \quotefromfile qml-i18n/Main.qml
\skipto }
\skipto Text
\printuntil )
diff --git a/examples/qml/qml-i18n/i18n/base.ts b/examples/qml/qml-i18n/i18n/base.ts
index 99ceeeb016..17564620e3 100644
--- a/examples/qml/qml-i18n/i18n/base.ts
+++ b/examples/qml/qml-i18n/i18n/base.ts
@@ -2,9 +2,9 @@
<!DOCTYPE TS>
<TS version="2.1" sourcelanguage="en">
<context>
- <name>qml-i18n</name>
+ <name>Main</name>
<message>
- <location filename="../qml-i18n.qml" line="68"/>
+ <location filename="../Main.qml" line="21"/>
<source>Hello</source>
<translation type="unfinished"></translation>
</message>
diff --git a/examples/qml/qml-i18n/i18n/qml_en_AU.ts b/examples/qml/qml-i18n/i18n/qml_en_AU.ts
index feb4f9e9c0..98e1d8649f 100644
--- a/examples/qml/qml-i18n/i18n/qml_en_AU.ts
+++ b/examples/qml/qml-i18n/i18n/qml_en_AU.ts
@@ -2,9 +2,9 @@
<!DOCTYPE TS>
<TS version="2.1" language="en_AU" sourcelanguage="en">
<context>
- <name>qml-i18n</name>
+ <name>Main</name>
<message>
- <location filename="../qml-i18n.qml" line="68"/>
+ <location filename="../Main.qml" line="21"/>
<source>Hello</source>
<translation>G&apos;day</translation>
</message>
diff --git a/examples/qml/qml-i18n/i18n/qml_fr.ts b/examples/qml/qml-i18n/i18n/qml_fr.ts
index c4872e63d0..73c525e535 100644
--- a/examples/qml/qml-i18n/i18n/qml_fr.ts
+++ b/examples/qml/qml-i18n/i18n/qml_fr.ts
@@ -2,9 +2,9 @@
<!DOCTYPE TS>
<TS version="2.1" language="fr" sourcelanguage="en">
<context>
- <name>qml-i18n</name>
+ <name>Main</name>
<message>
- <location filename="../qml-i18n.qml" line="68"/>
+ <location filename="../Main.qml" line="21"/>
<source>Hello</source>
<translation>Bonjour</translation>
</message>
diff --git a/examples/qml/qml-i18n/main.cpp b/examples/qml/qml-i18n/main.cpp
index d03cb05aed..fe20802d3d 100644
--- a/examples/qml/qml-i18n/main.cpp
+++ b/examples/qml/qml-i18n/main.cpp
@@ -1,5 +1,6 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#include <QGuiApplication>
#include <QQmlApplicationEngine>
@@ -7,14 +8,11 @@ int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
- const QUrl url(QStringLiteral("qrc:/qml-i18n.qml"));
- QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
- &app, [url](QObject *obj, const QUrl &objUrl) {
- if (!obj && url == objUrl)
- QCoreApplication::exit(-1);
- }, Qt::QueuedConnection);
- engine.load(url);
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
+ &app, []() { QCoreApplication::exit(-1); },
+ Qt::QueuedConnection);
+ engine.loadFromModule("Translated", "Main");
return app.exec();
}
diff --git a/examples/qml/qml-i18n/qml-i18n.pro b/examples/qml/qml-i18n/qml-i18n.pro
index 847c741b00..0ecfbc01e5 100644
--- a/examples/qml/qml-i18n/qml-i18n.pro
+++ b/examples/qml/qml-i18n/qml-i18n.pro
@@ -8,6 +8,7 @@ RESOURCES += qml-i18n.qrc
target.path = $$[QT_INSTALL_EXAMPLES]/qml/qml-i18n
INSTALLS += target
+QM_FILES_RESOURCE_PREFIX = /qt/qml/Translated/i18n/
CONFIG += lrelease embed_translations
TRANSLATIONS += \
diff --git a/examples/qml/qml-i18n/qml-i18n.qrc b/examples/qml/qml-i18n/qml-i18n.qrc
index 3a505b1665..79ec649894 100644
--- a/examples/qml/qml-i18n/qml-i18n.qrc
+++ b/examples/qml/qml-i18n/qml-i18n.qrc
@@ -1,5 +1,6 @@
<RCC>
- <qresource prefix="/">
- <file>qml-i18n.qml</file>
+ <qresource prefix="/qt/qml/Translated">
+ <file>Main.qml</file>
+ <file>qmldir</file>
</qresource>
</RCC>
diff --git a/examples/qml/qml-i18n/qmldir b/examples/qml/qml-i18n/qmldir
new file mode 100644
index 0000000000..b4773af30e
--- /dev/null
+++ b/examples/qml/qml-i18n/qmldir
@@ -0,0 +1,5 @@
+module Translated
+typeinfo qml-i18n.qmltypes
+depends QtQuick
+prefer :/qt/qml/Translated/
+Main 254.0 Main.qml
diff --git a/examples/qml/qml.pro b/examples/qml/qml.pro
index df73fe0200..f1652820a6 100644
--- a/examples/qml/qml.pro
+++ b/examples/qml/qml.pro
@@ -1,21 +1,8 @@
TEMPLATE = subdirs
QT_FOR_CONFIG += qml
-qtHaveModule(quick) {
- SUBDIRS += \
- qmlextensionplugins \
- xmlhttprequest
-
- qtConfig(qml-network): \
- SUBDIRS += networkaccessmanagerfactory
-}
-
SUBDIRS += \
- referenceexamples \
- tutorials \
- shell
+ tutorials
EXAMPLE_FILES = \
- dynamicscene \
- qml-i18n \
- locale
+ qml-i18n
diff --git a/examples/qml/qmldom/CMakeLists.txt b/examples/qml/qmldom/CMakeLists.txt
deleted file mode 100644
index 1bd1ad0807..0000000000
--- a/examples/qml/qmldom/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.18)
-project(qmldomloadeditwrite LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/qmldomloadeditwrite")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Qml)
-
-add_compile_definitions(
- QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../../../tests/auto/qmldom/domdata"
-)
-
-add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../src/qmldom/standalone qmldom)
-
-qt_add_executable(qmldomloadeditwrite
- qmldomloadeditwrite.cpp
-)
-
-target_link_libraries(qmldomloadeditwrite PUBLIC
- Qt::Core
- Qt::Qml
- qmldomlib
-)
-
-install(TARGETS qmldomloadeditwrite
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/qmldom/qmldomloadeditwrite.cpp b/examples/qml/qmldom/qmldomloadeditwrite.cpp
deleted file mode 100644
index 7c7609651b..0000000000
--- a/examples/qml/qmldom/qmldomloadeditwrite.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-// common declarations
-#include "qmldom/qqmldomitem_p.h"
-// comparisons of two DomItems
-#include "qmldom/qqmldomcompare_p.h"
-// field filters to compare only selected fields (ignore for example location changes)
-#include "qmldom/qqmldomfieldfilter_p.h"
-// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
-#include "qmldom/qqmldomelements_p.h"
-// cast of the top level items (DomEnvironments,...)
-#include "qmldom/qqmldomtop_p.h"
-
-#include <QtTest/QtTest>
-#include <QCborValue>
-#include <QDebug>
-#include <QLatin1String>
-#include <QLatin1Char>
-#include <QLibraryInfo>
-#include <QDir>
-
-#include <memory>
-
-// everything is in the QQmlJS::Dom namespace
-using namespace QQmlJS::Dom;
-
-int main()
-{
- QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
- QStringList qmltypeDirs =
- QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
-
- qDebug() << "Creating an environment loading qml from the directories" << qmltypeDirs;
- qDebug() << "single threaded, no dependencies";
- DomItem env =
- DomEnvironment::create(qmltypeDirs,
- QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
- | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
-
- QString testFilePath = baseDir + QLatin1String("/file1.qml");
- DomItem tFile; // place where to store the loaded file
- // env.loadBuiltins();
-
- qDebug() << "loading the file" << testFilePath;
- env.loadFile(
- testFilePath, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) {
- tFile = newIt; // callback called when everything is loaded that receives the loaded
- // external file pair (path, oldValue, newValue)
- },
- LoadOption::DefaultLoad);
-
- // trigger the load
- env.loadPendingDependencies();
-
- // # Read only API: DomItem is a generic pointer for read only access to Dom Itmes :)
- {
- // ## declarative json like API
- DomItem qmlFile = tFile.field(Fields::currentItem);
- DomItem imports = qmlFile.field(Fields::imports);
- DomItem qmlObj = qmlFile.field(Fields::components)
- .key(QString())
- .index(0)
- .field(Fields::objects)
- .index(0);
-
- // ### Dump
- // any DomItem can be dumped
- qDebug() << "writing to QDebug dumps that element:" << imports;
- // often the dump is too verbose, and one might want it to a separate file
- QString dumpFilePath =
- QDir(QDir::tempPath())
- .filePath(QFileInfo(testFilePath).baseName() + QLatin1String(".dump.json"));
- qmlFile.dump(dumpFilePath, FieldFilter::defaultFilter());
- qDebug() << "dumped file to" << dumpFilePath;
-
- // ### Paths
- qDebug() << "To identify a DomItem a canonical path can be used:"
- << imports.canonicalPath();
- // a path can be converted to/from strings
- QString pString = imports.canonicalPath().toString();
- Path importsPath = Path::fromString(pString);
- // and loaded again using the .path(somePath) method
- DomItem imports2 = env.path(importsPath);
- Q_ASSERT(imports == imports2);
- // the canonical path is absolute, but you can have relative paths
- Path first = Path::Index(0);
- DomItem firstImport = imports.path(first);
- // an existing path can also be extended
- Path firstImportPath = importsPath.index(0);
- Q_ASSERT(firstImportPath == firstImport.canonicalPath());
- // the normal elements of a path are index, key, field
- // Uppercase static method creates one, lowercase appends to an existing path.
- Path mainComponentPath = Path::Field(Fields::components).key("").index(0);
- DomItem mainComponent = qmlFile.path(mainComponentPath);
- // DomItems have the same methods to access their elements
- DomItem mainComponent2 = qmlFile.field(Fields::components).key("").index(0);
- // two other special ements are root (root element for absolute paths)
- Path topPath = Path::Root(PathRoot::Top);
- Q_ASSERT(topPath == importsPath[0]);
- // the current element performs an operation (tipically a lookup or iteration) at the
- // current path location (not handled here)
- Path lookupPath = Path::Current(PathCurrent::Lookup);
-
- // there are various visit methods to iterate/visit DomItems in particular visitTree
- // which is quite flexible.
- // They normally use callbacks that can return false to stop the iteration.
- // Still often the DomKind specific for loop presentated later are clearer and more
- // convenient
- {
- QDebug dbg = qDebug().noquote().nospace();
- imports.visitTree(
- Path(),
- [&dbg](Path p, const DomItem &el, bool adopted) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "*" << p.last() << " "
- << domKindToString(el.domKind()) << "(" << el.internalKindStr()
- << ")\n";
- // returning false here stops the whole iteration
- return true;
- },
- VisitOption::Default, // we want a recursive visit visiting also the top and
- // adopted
- [&dbg](Path p, const DomItem &, bool canonicalChild) {
- // returning false here skips that branch
- if (!canonicalChild) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "+" << p.last()
- << " (adopted, will not recurse)\n";
- } else if (p && p.headIndex(0) % 2 == 1) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "-" << p.last()
- << " *recursive visit skipped*\n";
- return false; // we skip odd entries in lists;
- } else {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "+" << p.last()
- << "\n";
- }
- return true;
- },
- [&dbg](Path p, const DomItem &, bool) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "=" << p.last() << "\n";
- return true;
- });
- }
-
- // ### DomKind
- // any DomItem belongs to one of 5 fundamental types
-
- // 1. Object (a C++ object)
- Q_ASSERT(qmlFile.domKind() == DomKind::Object);
- // The underlying type of the c++ object can be found with .internalKind()
- Q_ASSERT(qmlFile.internalKind() == DomType::QmlFile);
- // .initernalKindStr() is a convenience string version of it
- Q_ASSERT(qmlFile.internalKindStr() == u"QmlFile");
- // the object attributes (fields) can be reached using .field(u"filedName")
- // normally one should not use a string, but the Fields:: constant
- DomItem qmlFile2 = tFile.field(Fields::currentItem);
- // all the available fields can be listed via fields()
- qDebug() << "The" << qmlObj.internalKindStr() << "at" << qmlObj.canonicalPath()
- << "has the following fields:" << qmlObj.fields();
- // we can access the underlying C++ object with as<>
- if (const QmlFile *qmlFilePtr = qmlFile.as<QmlFile>())
- qDebug() << "The QmlFile lives at the address" << qmlFilePtr;
- // We can get the shared pointer of the owner type (which for the file is the QmlFile itself
- if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
- qDebug() << "QmlFile uses shared pointers as ownership method, the underlying address "
- "is the same"
- << qmlFilePtr.get();
-
- // 2. a (Cbor-) Value, i.e a string, number,...
- DomItem fPath = qmlFile.field(Fields::canonicalFilePath);
- Q_ASSERT(fPath.domKind() == DomKind::Value);
- // the Cbor representation of a value can be extracted with .value(), and in this case we
- // can then call toString
- qDebug() << "The filePath DomItem is " << fPath << " and it still 'knows' its path "
- << fPath.canonicalPath() << " but can have it also as value:" << fPath.value()
- << "or even better as string." << fPath.value().toString(QLatin1String("*none*"));
- // a DomItem might have a valid value() even if it is not of type DomKind::Value, indeed
- // CBor maps and lists are mapped to DomKind::Map and DomKind::List, and can be traversed
- // thought that but also have a valid value().
-
- // 3. a list
- Q_ASSERT(imports.domKind() == DomKind::List);
- // the number of elements can be sound with .indexes() and with .index(n) we access each
- // element
- qDebug() << "We have " << imports.indexes() << " imports, and the first is "
- << imports.index(0);
- // If we want to just loop on the elements .values() is the most convenient way
- // technically values *always* works even for objects and maps, iterating on the values
- for (DomItem import : imports.values()) {
- if (const Import *importPtr = import.as<Import>()) {
- if (importPtr->implicit)
- qDebug() << importPtr->uri.toString() << importPtr->version.stringValue();
- }
- }
-
- // 4. a map
- DomItem bindings = qmlObj.field(Fields::bindings);
- Q_ASSERT(bindings.domKind() == DomKind::Map);
- // The keys of the map can be reached either with .keys() or .sortedKeys(), each element
- // with .key(k)
- qDebug() << "bindings";
- for (QString k : bindings.sortedKeys()) {
- for (DomItem b : bindings.key(k).values()) {
- qDebug() << k << ":" << b;
- }
- }
-
- // 5 The empty element
- DomItem empty;
- Q_ASSERT(empty.domKind() == DomKind::Empty);
- // The empty element is the only DomItem that casted to bool returns false, so checking for
- // it can be just an implicit cast to bool
- Q_ASSERT(bindings && !empty);
- // the empty element supports all the previus operations so that one can traverse a non
- // existing path without checking at every element, but only check the result
- DomItem nonExisting = qmlFile.field(u"no-existing").key(u"a").index(0);
- Q_ASSERT(!nonExisting);
-
- // the index operator [] can be used instead of .index/.key/.field, it might be slightly
- // less efficient but works
-
- // find type
- // access type
-
- // ### write out
- // it is possible to write out a qmlFile (actually also parts of it), which will
- // automatically reformat it
- QString reformattedFilePath =
- QDir(QDir::tempPath())
- .filePath(QFileInfo(testFilePath).baseName() + QLatin1String(".qml"));
- DomItem newFile = qmlFile.writeOut(reformattedFilePath);
- qDebug() << "reformatted written at " << reformattedFilePath;
-
- // ## Jumping around
- // ### Generic Methods
- // from a DomItem you do no have just deeper in the tree, you can also go up the hierarch
- // toward the root .container() just goes up one step in the canonicalPath of the object
- Q_ASSERT(imports == firstImport.container());
- // .containingObject() goes up to the containing DomKind::Object, skipping over all Maps and
- // Lists
- Q_ASSERT(qmlFile == firstImport.containingObject());
- // .owner() returns the shared pointer based "owner" object, qmlFile and ScriptExpression
- // are owningItems
- Q_ASSERT(qmlFile == bindings.owner());
- // .top() goes to the top of the tree, i.e the environment (or the universe)
- Q_ASSERT(env == bindings.top());
- // environment is normally the same as top, but making sure it is a actually a
- // DomEnvironment
- Q_ASSERT(env = bindings.environment());
- // the universe is a cache of loaded files which for each file keeps two versions: the
- // latest and the latest valid it can be reached with .universe(), from the universe you
- // cannot get back to the environment.
- Q_ASSERT(env.universe().internalKind() == DomType::DomUniverse);
-
- // ## QML Oriented Methods
- // The Dom model is not for generic json-like structures, so there are methods tailored for
- // Qml and its structure
- // The methods can succeed if there is a clearly defined unique result.
- // sometime there is an obivious, but not necessarily unique choice (tipically going up the
- // hierarchy), for example given a qml file the obvious choice for a component is the root
- // component, but the file might contain other inline components, and for an object with
- // different version exposed (C++ property versioning) the latest version is the natural
- // choice, but other might be available. In these case passing GoTo::MostLikely as argument
- // makes the method to this obivious choice (or possibly even only choice if no other
- // versions/components are actually defined), instead of refusing any potentially ambiguous
- // situation and returning the empty element.
-
- // .fileObject() goes to the object representing the whole file
- // (from either the external object returned by load or from inside the file)
- DomItem fileObject = tFile.fileObject();
- DomItem fileObject2 = imports.fileObject();
- Q_ASSERT(fileObject == fileObject2 && fileObject.internalKind() == DomType::QmlFile);
- // .component() goes to the component object.
- Q_ASSERT(qmlObj.component() == qmlFile.component(GoTo::MostLikely));
- // .pragmas gives access to the pragmas of the current component
- Q_ASSERT(qmlFile.pragmas() == qmlFile.field(Fields::pragmas));
-
- // QmlObject
- // QmlObject if the main to represent the type information (methods, bindings,
- // properties,...) of qml. Please note that QmlObject -> component operation is potentially
- // lossy, when multiple version are exposed, so we represent a type through its root object,
- // not through a component.
-
- // .qmlObject() goes to the current QmlObject
- Q_ASSERT(qmlObj == bindings.qmlObject());
-
- // Given the centrality of QmlObject several of its attributes have convenience methods
- // to access them:
-
- // .children() makes subObjects contained inside a QmlObject accessible
- // note that it is possible to add objects also by directly binding the children or data
- // attribute, those children are not listed here, this accesses only those listed inside
- // the QmlObject
- Q_ASSERT(qmlObj.children() == qmlObj.field(Fields::children));
- DomItem subObj0 = qmlObj.children().index(0);
- // .child(<i>) is a shortcut for .children.index(<i>)
- Q_ASSERT(subObj0 == qmlObj.child(0));
- // rootQmlObject goes to the root qmlObject (unless one reaches an empty element)
- Q_ASSERT(!subObj0 || subObj0.rootQmlObject() == qmlObj);
- // .bindings() returns the bindings defined in the current object
- Q_ASSERT(bindings == qmlObj.bindings());
- DomItem mCompObj = qmlObj.child(0)
- .child(0)
- .bindings()
- .key(u"delegate")
- .index(0)
- .field(Fields::value)
- .child(1);
- // .methods() gives methods definitions and signals
- DomItem methods = mCompObj.methods();
- qDebug() << "mCompObj methods:";
- for (QString methodName : methods.sortedKeys()) {
- for (DomItem method : methods.key(methodName).values()) {
- if (const MethodInfo *methodPtr = method.as<MethodInfo>()) {
- Q_ASSERT(methodName == methodPtr->name);
- qDebug() << " " << methodPtr->name << methodPtr->methodType;
- }
- }
- }
- qDebug() << "mCompObj propertyDefs:";
- // .propertyDefs() returns the properties defined in the current object
- DomItem pDefs = mCompObj.propertyDefs();
- for (QString pDefName : pDefs.sortedKeys()) {
- for (DomItem pDef : pDefs.key(pDefName).values()) {
- if (const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>()) {
- Q_ASSERT(pDefName == pDefPtr->name);
- qDebug() << " " << pDefPtr->name << pDefPtr->typeName;
- }
- }
- }
- // binding and property definitions are about the ones defined in the current object
- // often one is interested also to the inherited properties.
- // Here PropertyInfo helps, it list all the definitions and bindings for a given property
- // in the inheritance order (local definitions, parent definitions, parent parent
- // definitions,...)
- // .propertyInfos() gives access in the usual way (through a DomItem)
- DomItem propertyInfos = mCompObj.propertyInfos();
- // .propertyInfoWithName(<name>) directly accesses one
- PropertyInfo pInfo = mCompObj.propertyInfoWithName(QStringLiteral(u"a"));
- qDebug() << "bindings" << pInfo.bindings;
- // .propertyInfoNames() gives the names of the properties
- Q_ASSERT(propertyInfos.keys() == mCompObj.propertyInfoNames());
-
- // .globalScope() goes to the globa scope object
- Q_ASSERT(qmlObj.globalScope().internalKind() == DomType::GlobalScope);
- // and scope to the containing scope
- Q_ASSERT(bindings.scope() == qmlObj);
- }
- // mutate & edit
- {
- // DomItem handles read-only access, but if one wants to change something it cannot be used.
- // MutableDomItem can be initialized with a DomItem, and provides also the methods to modify
- // the item. It keeps the OwningItem and the path to the current item.
- // Mutability can invalidate pointers to non owning items (and thus DomItem).
- // For this reason one should not modify something that other code can have a DomItem
- // pointer to, the best practice is to make shared object immutable and never change them.
- // One should modify only a copy that is used only by a single thread, and
- // do not shared untils all modifications are done.
- // A MutableItem stays valid (or becomes Empty), but stays safe to use
- //
- // Assuming one guarantees that editing is ok, doing it in practice is just about using
- // MutableDomItem instead of DomItem
- // It is possible to simply initialize a mutable item with a DomItem
- DomItem origFile = tFile.fileObject();
- MutableDomItem myFile0(origFile);
- // Normally it is better to have a separate environment. Is possible to avoid re-reading
- // the files already read by sharing the Universe between two environments.
- // But normally it is better and just as safe to work on a copy, so that one can be sure
- // that no DomItem is kept by other code gets invalidated. The .makeCopy creates a deep
- // copy, and by default (DomItem::CopyOption::EnvConnected) creates an environment which to
- // takes all non local elements from the current environment (its parent environment) but
- // replaces the file object with the copy. When finished one can replace the file object of
- // the parent with the new one using .commitToBase().
- MutableDomItem myFile = origFile.makeCopy();
- Q_ASSERT(myFile.ownerAs<QmlFile>()
- && myFile.ownerAs<QmlFile>() != myFile0.ownerAs<QmlFile>());
- Q_ASSERT(myFile.environment().ownerAs<DomEnvironment>()
- && myFile.environment().ownerAs<DomEnvironment>()
- != myFile0.environment().ownerAs<DomEnvironment>());
- // we can check that the two files are really identical (.item() give back the DomItem of
- // a MutableDomItem
- Q_ASSERT(domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
- // MutableDomItem has the same methods as DomItem
- MutableDomItem qmlObj = myFile.qmlObject(GoTo::MostLikely);
- MutableDomItem qmlObj2 = myFile.field(Fields::components)
- .key(QString())
- .index(0)
- .field(Fields::objects)
- .index(0);
- Q_ASSERT(qmlObj && qmlObj == qmlObj2);
- qDebug() << "mutable qmlObj has canonicalPath " << qmlObj.canonicalPath();
- // but it adds methods to add
- // * new PropertyDefinitions
- PropertyDefinition b;
- b.name = QLatin1String("xx");
- b.typeName = QLatin1String("int");
- // if we make t true we also have to give a value...
- MutableDomItem addedPDef = qmlObj.addPropertyDef(b);
- qDebug() << "added property definition at:" << addedPDef.pathFromOwner();
- // * new bindings
- MutableDomItem addedBinding0 = qmlObj.addBinding(
- Binding("height",
- std::shared_ptr<ScriptExpression>(new ScriptExpression(
- QStringLiteral(u"243"),
- ScriptExpression::ExpressionType::BindingExpression))));
- // by default addBinding, addPropertyDef and addMethod have the AddOption::Override
- // to make it more difficult to create invalid documents, so that only the
- // following binding remains (where we use the convenience constructor that constucts
- // the ScriptExpression internally
- MutableDomItem addedBinding = qmlObj.addBinding(Binding("height", QStringLiteral(u"242")));
- qDebug() << "added binding at:" << addedBinding.pathFromOwner();
- // * new methods
- MethodInfo mInfo;
- mInfo.name = QLatin1String("foo2");
- MethodParameter param;
- param.name = QLatin1String("x");
- mInfo.parameters.append(param);
- mInfo.setCode(QLatin1String("return 4*10+2 - x"));
- // we can change the added binding
- addedBinding.setCode(QLatin1String("245"));
- MutableDomItem addedMethod = qmlObj.addMethod(mInfo);
- qDebug() << "added method at:" << addedMethod.pathFromOwner();
- // * new QmlObjects
- QmlObject subObj;
- subObj.setName(QLatin1String("Item"));
- MutableDomItem addedSubObj = qmlObj.addChild(subObj);
- qDebug() << "added subObject at:" << addedMethod.pathFromOwner();
- // It is possible to modify the content of objects, using the mutableAs method
- if (PropertyDefinition *addedPDefPtr = addedPDef.mutableAs<PropertyDefinition>()) {
- addedPDefPtr->isRequired = true;
- }
- MutableDomItem firstChild = qmlObj.child(0);
- qDebug() << "firstChild:" << firstChild;
- // It is possible remove objects
- if (QmlObject *qmlObjPtr = qmlObj.mutableAs<QmlObject>()) {
- QList<QmlObject> children = qmlObjPtr->children();
- children.removeAt(0);
- qmlObjPtr->setChildren(children);
- }
- // But as MutableDomItem does not keep the identity, just the same position, the addedSubObj
- // becomes invalid (and firstChild changes)
- qDebug() << "after removal firstChild:" << firstChild;
- qDebug() << "addedSubObj becomes invalid:" << addedSubObj;
- qDebug() << "But the last object is the added one:"
- << qmlObj.child(qmlObj.children().indexes() - 1);
-
- // now origFile are different
- Q_ASSERT(!domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
- // and we can look at the places where they differ
- qDebug().noquote().nospace()
- << "Edits introduced the following diffs (ignoring file locations"
- << " and thus whitespace/reformatting changes):\n"
- << domCompareStrList(origFile, myFile, FieldFilter::noLocationFilter(),
- DomCompareStrList::AllDiffs)
- .join(QString());
-
- QString reformattedFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".qml"));
- MutableDomItem reformattedEditedFile = myFile.writeOut(reformattedFilePath);
- // the reformatted edited file might be different from the edited file
- // but the differences are just in file location/formatting
- Q_ASSERT(domCompareStrList(myFile, reformattedEditedFile, FieldFilter::noLocationFilter())
- .isEmpty());
-
- qDebug() << "The edited file was written at " << reformattedFilePath;
- QString dumpFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited0") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- myFile.dump(dumpFilePath);
- qDebug() << "The non reformatted edited file was dumped at " << dumpFilePath;
- QString reformattedDumpFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- reformattedEditedFile.dump(reformattedDumpFilePath);
- qDebug() << "The edited file was dumped at " << reformattedDumpFilePath;
- // The top environment still contains the original loaded file
- Q_ASSERT(origFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>() == origFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().ownerAs<QmlFile>() == origFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
- != reformattedEditedFile.ownerAs<QmlFile>());
- // we can commit the reformatted file
- if (!reformattedEditedFile.commitToBase()) {
- qWarning() << "No reformatted file to commit";
- }
- // myFile might not be the same (If and updated check is requested, not the case here)
- if (myFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>()
- && !myFile.commitToBase()) {
- qWarning() << "Could not commit edited file";
- }
- // but refreshing it (looking up its canonical path) we always find the updated file
- Q_ASSERT(myFile.refreshed().ownerAs<QmlFile>() == reformattedEditedFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
- == reformattedEditedFile.ownerAs<QmlFile>());
- }
-}
diff --git a/examples/qml/qmlextensionplugins/CMakeLists.txt b/examples/qml/qmlextensionplugins/CMakeLists.txt
deleted file mode 100644
index 753f05b173..0000000000
--- a/examples/qml/qmlextensionplugins/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(qmlqtimeexample LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/qmlextensionplugins/imports/TimeExample")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml)
-
-#![0]
-set(qml_files
- imports/TimeExample/Clock.qml
-)
-
-set(images
- imports/TimeExample/center.png
- imports/TimeExample/clock.png
- imports/TimeExample/hour.png
- imports/TimeExample/minute.png
-)
-#![0]
-
-foreach(file IN LISTS qml_files images)
- get_filename_component(filename ${file} NAME)
- set_source_files_properties(${file} PROPERTIES QT_RESOURCE_ALIAS ${filename})
-endforeach()
-
-#![1]
-qt_add_qml_module(qmlqtimeexample
- OUTPUT_DIRECTORY imports/TimeExample
- URI "TimeExample"
- SOURCES timemodel.cpp timemodel.h
- AUTO_RESOURCE_PREFIX
- QML_FILES ${qml_files}
- RESOURCES ${images}
-)
-#![1]
-
-set_target_properties(qmlqtimeexample PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(qmlqtimeexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
-)
-
-install(TARGETS qmlqtimeexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/qmlextensionplugins/imports/TimeExample/Clock.qml b/examples/qml/qmlextensionplugins/imports/TimeExample/Clock.qml
deleted file mode 100644
index b071e2e031..0000000000
--- a/examples/qml/qmlextensionplugins/imports/TimeExample/Clock.qml
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- id: clock
- width: 200; height: 200; color: "gray"
-
- property alias city: cityLabel.text
- property variant hours
- property variant minutes
- property variant shift : 0
-
- Image { id: background; source: "clock.png" }
-
- Image {
- x: 92.5; y: 27
- source: "hour.png"
- transform: Rotation {
- id: hourRotation
- origin.x: 7.5; origin.y: 73;
- angle: (clock.hours * 30) + (clock.minutes * 0.5)
- Behavior on angle {
- SpringAnimation{ spring: 2; damping: 0.2; modulus: 360 }
- }
- }
- }
-
- Image {
- x: 93.5; y: 17
- source: "minute.png"
- transform: Rotation {
- id: minuteRotation
- origin.x: 6.5; origin.y: 83;
- angle: clock.minutes * 6
- Behavior on angle {
- SpringAnimation{ spring: 2; damping: 0.2; modulus: 360 }
- }
- }
- }
-
- Image {
- anchors.centerIn: background; source: "center.png"
- }
-
- Text {
- id: cityLabel; font.bold: true; font.pixelSize: 14; y:200; color: "white"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-}
diff --git a/examples/qml/qmlextensionplugins/plugin.cpp b/examples/qml/qmlextensionplugins/plugin.cpp
deleted file mode 100644
index 066d284a5e..0000000000
--- a/examples/qml/qmlextensionplugins/plugin.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QtQml/QQmlEngineExtensionPlugin>
-#include <qdebug.h>
-
-//![plugin]
-class QExampleQmlPlugin : public QQmlEngineExtensionPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
-};
-//![plugin]
-
-#include "plugin.moc"
diff --git a/examples/qml/qmlextensionplugins/plugins.qml b/examples/qml/qmlextensionplugins/plugins.qml
deleted file mode 100644
index 55e8903400..0000000000
--- a/examples/qml/qmlextensionplugins/plugins.qml
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-//![0]
-import TimeExample // import types from the plugin
-
-Clock { // this class is defined in QML (imports/TimeExample/Clock.qml)
-
- Time { // this class is defined in C++ (plugin.cpp)
- id: time
- }
-
- hours: time.hour
- minutes: time.minute
-
-}
-//![0]
diff --git a/examples/qml/qmlextensionplugins/timemodel.cpp b/examples/qml/qmlextensionplugins/timemodel.cpp
deleted file mode 100644
index a94a7e773d..0000000000
--- a/examples/qml/qmlextensionplugins/timemodel.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "timemodel.h"
-
-int TimeModel::instances=0;
-MinuteTimer *TimeModel::timer=nullptr;
-
-void MinuteTimer::start()
-{
- if (!timer.isActive()) {
- time = QTime::currentTime();
- timer.start(60000-time.second()*1000, this);
- }
-}
-
-void MinuteTimer::stop()
-{
- timer.stop();
-}
-
-void MinuteTimer::timerEvent(QTimerEvent *)
-{
- QTime now = QTime::currentTime();
- if (now.second() == 59 && now.minute() == time.minute() && now.hour() == time.hour()) {
- // just missed time tick over, force it, wait extra 0.5 seconds
- time = time.addSecs(60);
- timer.start(60500, this);
- } else {
- time = now;
- timer.start(60000-time.second()*1000, this);
- }
- emit timeChanged();
-}
-
-TimeModel::TimeModel(QObject *parent) : QObject(parent)
-{
- if (++instances == 1) {
- if (!timer)
- timer = new MinuteTimer(QCoreApplication::instance());
- connect(timer, &MinuteTimer::timeChanged, this, &TimeModel::timeChanged);
- timer->start();
- }
-}
-
-TimeModel::~TimeModel()
-{
- if (--instances == 0) {
- timer->stop();
- }
-}
diff --git a/examples/qml/qmlextensionplugins/timemodel.h b/examples/qml/qmlextensionplugins/timemodel.h
deleted file mode 100644
index 2c27a6dada..0000000000
--- a/examples/qml/qmlextensionplugins/timemodel.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef TIMEMODEL_H
-#define TIMEMODEL_H
-
-#include <QtQml/qqml.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qbasictimer.h>
-#include <QtCore/qcoreapplication.h>
-
-// Implements a "TimeModel" class with hour and minute properties
-// that change on-the-minute yet efficiently sleep the rest
-// of the time.
-
-class MinuteTimer : public QObject
-{
- Q_OBJECT
-public:
- MinuteTimer(QObject *parent) : QObject(parent) {}
-
- void start();
- void stop();
-
- int hour() const { return time.hour(); }
- int minute() const { return time.minute(); }
-
-signals:
- void timeChanged();
-
-protected:
- void timerEvent(QTimerEvent *) override;
-
-private:
- QTime time;
- QBasicTimer timer;
-};
-
-//![0]
-class TimeModel : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int hour READ hour NOTIFY timeChanged)
- Q_PROPERTY(int minute READ minute NOTIFY timeChanged)
- QML_NAMED_ELEMENT(Time)
-//![0]
-
-public:
- TimeModel(QObject *parent=nullptr);
- ~TimeModel() override;
-
- int minute() const { return timer->minute(); }
- int hour() const { return timer->hour(); }
-
-signals:
- void timeChanged();
-
-private:
- QTime t;
- static MinuteTimer *timer;
- static int instances;
-};
-
-#endif // TIMEMODEL_H
diff --git a/examples/qml/referenceexamples/CMakeLists.txt b/examples/qml/referenceexamples/CMakeLists.txt
deleted file mode 100644
index 88908ec9a0..0000000000
--- a/examples/qml/referenceexamples/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-qt_internal_add_example(adding)
-qt_internal_add_example(coercion)
-qt_internal_add_example(default)
-qt_internal_add_example(properties)
-qt_internal_add_example(methods)
-if(TARGET Qt::Widgets)
- qt_internal_add_example(extended)
-endif()
-if(TARGET Qt::Quick)
- qt_internal_add_example(attached)
- qt_internal_add_example(binding)
- qt_internal_add_example(grouped)
- qt_internal_add_example(signal)
- qt_internal_add_example(valuesource)
-endif()
diff --git a/examples/qml/referenceexamples/adding/CMakeLists.txt b/examples/qml/referenceexamples/adding/CMakeLists.txt
deleted file mode 100644
index f1d3fc6eac..0000000000
--- a/examples/qml/referenceexamples/adding/CMakeLists.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(adding LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/adding")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
-
-qt_add_executable(adding
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(adding PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(adding PUBLIC
- Qt::Core
- Qt::Qml
-)
-
-qt_add_qml_module(adding
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS adding
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/adding/adding.pro b/examples/qml/referenceexamples/adding/adding.pro
deleted file mode 100644
index a4a677c3c4..0000000000
--- a/examples/qml/referenceexamples/adding/adding.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-QT = core qml
-CONFIG += qmltypes
-
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp
-HEADERS += person.h
-RESOURCES += adding.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/adding
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/adding/adding.qrc b/examples/qml/referenceexamples/adding/adding.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/adding/adding.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/adding/example.qml b/examples/qml/referenceexamples/adding/example.qml
deleted file mode 100644
index 068352bc35..0000000000
--- a/examples/qml/referenceexamples/adding/example.qml
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-// ![0]
-import People
-
-Person {
- name: "Bob Jones"
- shoeSize: 12
-}
-// ![0]
diff --git a/examples/qml/referenceexamples/adding/main.cpp b/examples/qml/referenceexamples/adding/main.cpp
deleted file mode 100644
index 319217da56..0000000000
--- a/examples/qml/referenceexamples/adding/main.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *person = qobject_cast<Person *>(component.create());
- if (!person) {
- qWarning() << component.errors();
- return EXIT_FAILURE;
- }
-
- qInfo() << "The person's name is" << person->name()
- << "\nThey wear a" << person->shoeSize() << "sized shoe";
-
- return EXIT_SUCCESS;
-}
diff --git a/examples/qml/referenceexamples/adding/person.cpp b/examples/qml/referenceexamples/adding/person.cpp
deleted file mode 100644
index 5b6ce4b9d6..0000000000
--- a/examples/qml/referenceexamples/adding/person.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-// ![0]
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-int Person::shoeSize() const
-{
- return m_shoeSize;
-}
-
-void Person::setShoeSize(int s)
-{
- m_shoeSize = s;
-}
-
-// ![0]
diff --git a/examples/qml/referenceexamples/adding/person.h b/examples/qml/referenceexamples/adding/person.h
deleted file mode 100644
index 867927ee2b..0000000000
--- a/examples/qml/referenceexamples/adding/person.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QtQml/qqml.h>
-
-//![0]
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
- QML_ELEMENT
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- int shoeSize() const;
- void setShoeSize(int);
-
-private:
- QString m_name;
- int m_shoeSize = 0;
-};
-//![0]
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/attached/CMakeLists.txt b/examples/qml/referenceexamples/attached/CMakeLists.txt
deleted file mode 100644
index ce4ff3e80f..0000000000
--- a/examples/qml/referenceexamples/attached/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(attached LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/attached")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml)
-
-qt_add_executable(attached
- birthdayparty.cpp birthdayparty.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(attached PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(attached PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
-)
-
-qt_add_qml_module(attached
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS attached
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/attached/attached.pro b/examples/qml/referenceexamples/attached/attached.pro
deleted file mode 100644
index 8c66b189f7..0000000000
--- a/examples/qml/referenceexamples/attached/attached.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT += qml
-CONFIG += qmltypes
-
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp
-HEADERS += person.h \
- birthdayparty.h
-RESOURCES += attached.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/attached
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/attached/attached.qrc b/examples/qml/referenceexamples/attached/attached.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/attached/attached.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/attached/birthdayparty.cpp b/examples/qml/referenceexamples/attached/birthdayparty.cpp
deleted file mode 100644
index a76b0f55cb..0000000000
--- a/examples/qml/referenceexamples/attached/birthdayparty.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-QDate BirthdayPartyAttached::rsvp() const
-{
- return m_rsvp;
-}
-
-void BirthdayPartyAttached::setRsvp(QDate d)
-{
- m_rsvp = d;
-}
-
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- m_host = c;
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return {this, &m_guests};
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
-
-BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
-{
- return new BirthdayPartyAttached(object);
-}
-
diff --git a/examples/qml/referenceexamples/attached/birthdayparty.h b/examples/qml/referenceexamples/attached/birthdayparty.h
deleted file mode 100644
index 7eb6508869..0000000000
--- a/examples/qml/referenceexamples/attached/birthdayparty.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QDate>
-#include <qqml.h>
-#include "person.h"
-
-class BirthdayPartyAttached : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QDate rsvp() const;
- void setRsvp(QDate);
-
-private:
- QDate m_rsvp;
-};
-
-class BirthdayParty : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(Person *host READ host WRITE setHost)
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
- Q_CLASSINFO("DefaultProperty", "guests")
- QML_ELEMENT
-
-//! [declare attached]
- QML_ATTACHED(BirthdayPartyAttached)
-//! [declare attached]
-
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
-
- //! [static attached]
- static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
- //! [static attached]
-private:
- Person *m_host = nullptr;
- QList<Person *> m_guests;
-};
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/attached/example.qml b/examples/qml/referenceexamples/attached/example.qml
deleted file mode 100644
index a4e675fe51..0000000000
--- a/examples/qml/referenceexamples/attached/example.qml
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-import QtQuick // For QColor
-
-//! [begin]
-BirthdayParty {
-//! [begin]
-
-//! [rsvp]
- Boy {
- name: "Robert Campbell"
- BirthdayParty.rsvp: "2009-07-01"
- }
-//! [rsvp]
- // ![1]
- Boy {
- name: "Leo Hodges"
- shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
-
- BirthdayParty.rsvp: "2009-07-06"
- }
- // ![1]
- host: Boy {
- name: "Jack Smith"
- shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
- }
-//! [end]
-}
-//! [end]
-
diff --git a/examples/qml/referenceexamples/attached/main.cpp b/examples/qml/referenceexamples/attached/main.cpp
deleted file mode 100644
index cfe0a1b9b2..0000000000
--- a/examples/qml/referenceexamples/attached/main.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!";
-
- if (qobject_cast<Boy *>(party->host()))
- qInfo() << "He is inviting:";
- else
- qInfo() << "She is inviting:";
-
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
- Person *guest = party->guest(ii);
-
- //! [query rsvp]
- QDate rsvpDate;
- QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
-
- if (attached)
- rsvpDate = attached->property("rsvp").toDate();
- //! [query rsvp]
- if (rsvpDate.isNull())
- qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd";
- else
- qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString();
- }
-
- return EXIT_SUCCESS;
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/attached/person.cpp b/examples/qml/referenceexamples/attached/person.cpp
deleted file mode 100644
index 358fbf0ed8..0000000000
--- a/examples/qml/referenceexamples/attached/person.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-int ShoeDescription::size() const
-{
- return m_size;
-}
-
-void ShoeDescription::setSize(int s)
-{
- m_size = s;
-}
-
-QColor ShoeDescription::color() const
-{
- return m_color;
-}
-
-void ShoeDescription::setColor(const QColor &c)
-{
- m_color = c;
-}
-
-QString ShoeDescription::brand() const
-{
- return m_brand;
-}
-
-void ShoeDescription::setBrand(const QString &b)
-{
- m_brand = b;
-}
-
-qreal ShoeDescription::price() const
-{
- return m_price;
-}
-
-void ShoeDescription::setPrice(qreal p)
-{
- m_price = p;
-}
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-ShoeDescription *Person::shoe()
-{
- return &m_shoe;
-}
diff --git a/examples/qml/referenceexamples/attached/person.h b/examples/qml/referenceexamples/attached/person.h
deleted file mode 100644
index 9c617ee9ab..0000000000
--- a/examples/qml/referenceexamples/attached/person.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QColor>
-#include <QtQml/qqml.h>
-
-class ShoeDescription : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int size READ size WRITE setSize)
- Q_PROPERTY(QColor color READ color WRITE setColor)
- Q_PROPERTY(QString brand READ brand WRITE setBrand)
- Q_PROPERTY(qreal price READ price WRITE setPrice)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- int size() const;
- void setSize(int);
-
- QColor color() const;
- void setColor(const QColor &);
-
- QString brand() const;
- void setBrand(const QString &);
-
- qreal price() const;
- void setPrice(qreal);
-
-private:
- int m_size = 0;
- QColor m_color;
- QString m_brand;
- qreal m_price = 0;
-};
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(ShoeDescription *shoe READ shoe)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- ShoeDescription *shoe();
-private:
- QString m_name;
- ShoeDescription m_shoe;
-};
-
-class Boy : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-class Girl : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/binding/CMakeLists.txt b/examples/qml/referenceexamples/binding/CMakeLists.txt
deleted file mode 100644
index 83ad8001e8..0000000000
--- a/examples/qml/referenceexamples/binding/CMakeLists.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(binding LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/binding")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml)
-
-qt_add_executable(binding
- birthdayparty.cpp birthdayparty.h
- happybirthdaysong.cpp happybirthdaysong.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(binding PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(binding PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
-)
-
-qt_add_qml_module(binding
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS binding
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/binding/binding.pro b/examples/qml/referenceexamples/binding/binding.pro
deleted file mode 100644
index dce780d0a2..0000000000
--- a/examples/qml/referenceexamples/binding/binding.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-QT += qml
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp \
- happybirthdaysong.cpp
-HEADERS += person.h \
- birthdayparty.h \
- happybirthdaysong.h
-RESOURCES += binding.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/binding
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/binding/binding.qrc b/examples/qml/referenceexamples/binding/binding.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/binding/binding.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/binding/birthdayparty.cpp b/examples/qml/referenceexamples/binding/birthdayparty.cpp
deleted file mode 100644
index a98b5fbdfb..0000000000
--- a/examples/qml/referenceexamples/binding/birthdayparty.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-QDate BirthdayPartyAttached::rsvp() const
-{
- return m_rsvp;
-}
-
-void BirthdayPartyAttached::setRsvp(QDate d)
-{
- if (d != m_rsvp) {
- m_rsvp = d;
- emit rsvpChanged();
- }
-}
-
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- if (c == m_host) return;
- m_host = c;
- emit hostChanged();
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return QQmlListProperty<Person>(this, &m_guests);
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
-
-void BirthdayParty::startParty()
-{
- QTime time = QTime::currentTime();
- emit partyStarted(time);
-}
-
-QString BirthdayParty::announcement() const
-{
- return QString();
-}
-
-void BirthdayParty::setAnnouncement(const QString &speak)
-{
- qWarning().noquote() << speak;
-}
-
-BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
-{
- return new BirthdayPartyAttached(object);
-}
-
diff --git a/examples/qml/referenceexamples/binding/birthdayparty.h b/examples/qml/referenceexamples/binding/birthdayparty.h
deleted file mode 100644
index 3b4c92cae3..0000000000
--- a/examples/qml/referenceexamples/binding/birthdayparty.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QDate>
-#include <QDebug>
-#include <qqml.h>
-#include "person.h"
-
-class BirthdayPartyAttached : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QDate rsvp() const;
- void setRsvp(QDate);
-
-signals:
- void rsvpChanged();
-
-private:
- QDate m_rsvp;
-};
-
-class BirthdayParty : public QObject
-{
- Q_OBJECT
-// ![0]
- Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged)
-// ![0]
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
- Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement)
- Q_CLASSINFO("DefaultProperty", "guests")
- QML_ELEMENT
- QML_ATTACHED(BirthdayPartyAttached)
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
-
- QString announcement() const;
- void setAnnouncement(const QString &);
-
- static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
-
- void startParty();
-signals:
- void partyStarted(QTime time);
- void hostChanged();
-
-private:
- Person *m_host = nullptr;
- QList<Person *> m_guests;
-};
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/binding/example.qml b/examples/qml/referenceexamples/binding/example.qml
deleted file mode 100644
index 262d667903..0000000000
--- a/examples/qml/referenceexamples/binding/example.qml
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-import QtQuick // For QColor
-
-// ![0]
-BirthdayParty {
- id: theParty
-
- HappyBirthdaySong on announcement { name: theParty.host.name }
-
- host: Boy {
- name: "Bob Jones"
- shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 }
- }
-// ![0]
- onPartyStarted: (time) => { console.log("This party started rockin' at " + time); }
-
-
- Boy {
- name: "Leo Hodges"
- BirthdayParty.rsvp: "2009-07-06"
- shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
- }
- Boy {
- name: "Jack Smith"
- shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
- }
- Girl {
- name: "Anne Brown"
- BirthdayParty.rsvp: "2009-07-01"
- shoe.size: 7
- shoe.color: "red"
- shoe.brand: "Marc Jacobs"
- shoe.price: 699.99
- }
-
-// ![1]
-}
-// ![1]
diff --git a/examples/qml/referenceexamples/binding/happybirthdaysong.cpp b/examples/qml/referenceexamples/binding/happybirthdaysong.cpp
deleted file mode 100644
index cf71a7f213..0000000000
--- a/examples/qml/referenceexamples/binding/happybirthdaysong.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "happybirthdaysong.h"
-#include <QTimer>
-
-HappyBirthdaySong::HappyBirthdaySong(QObject *parent) :
- QObject(parent)
-{
- auto *timer = new QTimer(this);
- QObject::connect(timer, &QTimer::timeout, this, &HappyBirthdaySong::advance);
- timer->start(1000);
-}
-
-void HappyBirthdaySong::setTarget(const QQmlProperty &p)
-{
- m_target = p;
-}
-
-QString HappyBirthdaySong::name() const
-{
- return m_name;
-}
-
-void HappyBirthdaySong::setName(const QString &name)
-{
- if (m_name == name)
- return;
-
- m_name = name;
-
- m_lyrics.clear();
- m_lyrics << "Happy birthday to you,";
- m_lyrics << "Happy birthday to you,";
- m_lyrics << "Happy birthday dear " + m_name + ",";
- m_lyrics << "Happy birthday to you!";
- m_lyrics << "";
-
- emit nameChanged();
-}
-
-void HappyBirthdaySong::advance()
-{
- m_line = (m_line + 1) % m_lyrics.count();
-
- m_target.write(m_lyrics.at(m_line));
-}
-
diff --git a/examples/qml/referenceexamples/binding/happybirthdaysong.h b/examples/qml/referenceexamples/binding/happybirthdaysong.h
deleted file mode 100644
index 8705abfa83..0000000000
--- a/examples/qml/referenceexamples/binding/happybirthdaysong.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#ifndef HAPPYBIRTHDAYSONG_H
-#define HAPPYBIRTHDAYSONG_H
-
-#include <QQmlPropertyValueSource>
-#include <QQmlProperty>
-#include <qqml.h>
-
-#include <QStringList>
-#include <qqml.h>
-
-class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
- Q_INTERFACES(QQmlPropertyValueSource)
- QML_ELEMENT
-public:
- explicit HappyBirthdaySong(QObject *parent = nullptr);
-
- void setTarget(const QQmlProperty &) override;
-
- QString name() const;
- void setName(const QString &);
-
-private slots:
- void advance();
-
-signals:
- void nameChanged();
-private:
- qsizetype m_line = -1;
- QStringList m_lyrics;
- QQmlProperty m_target;
- QString m_name;
-};
-
-#endif // HAPPYBIRTHDAYSONG_H
-
diff --git a/examples/qml/referenceexamples/binding/main.cpp b/examples/qml/referenceexamples/binding/main.cpp
deleted file mode 100644
index 716deeccda..0000000000
--- a/examples/qml/referenceexamples/binding/main.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "happybirthdaysong.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!";
-
- if (qobject_cast<Boy *>(party->host()))
- qInfo() << "He is inviting:";
- else
- qInfo() << "She is inviting:";
-
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
- Person *guest = party->guest(ii);
-
- QDate rsvpDate;
- QObject *attached =
- qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
- if (attached)
- rsvpDate = attached->property("rsvp").toDate();
-
- if (rsvpDate.isNull())
- qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd";
- else
- qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString();
- }
-
- party->startParty();
- return QCoreApplication::exec();
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/binding/person.cpp b/examples/qml/referenceexamples/binding/person.cpp
deleted file mode 100644
index a4c26679d6..0000000000
--- a/examples/qml/referenceexamples/binding/person.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-int ShoeDescription::size() const
-{
- return m_size;
-}
-
-void ShoeDescription::setSize(int s)
-{
- if (m_size == s)
- return;
-
- m_size = s;
- emit shoeChanged();
-}
-
-QColor ShoeDescription::color() const
-{
- return m_color;
-}
-
-void ShoeDescription::setColor(const QColor &c)
-{
- if (m_color == c)
- return;
-
- m_color = c;
- emit shoeChanged();
-}
-
-QString ShoeDescription::brand() const
-{
- return m_brand;
-}
-
-void ShoeDescription::setBrand(const QString &b)
-{
- if (m_brand == b)
- return;
-
- m_brand = b;
- emit shoeChanged();
-}
-
-qreal ShoeDescription::price() const
-{
- return m_price;
-}
-
-void ShoeDescription::setPrice(qreal p)
-{
- if (m_price == p)
- return;
-
- m_price = p;
- emit shoeChanged();
-}
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- if (m_name == n)
- return;
-
- m_name = n;
- emit nameChanged();
-}
-
-ShoeDescription *Person::shoe()
-{
- return &m_shoe;
-}
diff --git a/examples/qml/referenceexamples/binding/person.h b/examples/qml/referenceexamples/binding/person.h
deleted file mode 100644
index 8314348bdf..0000000000
--- a/examples/qml/referenceexamples/binding/person.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QColor>
-#include <QtQml/qqml.h>
-
-class ShoeDescription : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged)
- Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged)
- Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged)
- Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- int size() const;
- void setSize(int);
-
- QColor color() const;
- void setColor(const QColor &);
-
- QString brand() const;
- void setBrand(const QString &);
-
- qreal price() const;
- void setPrice(qreal);
-
-signals:
- void shoeChanged();
-
-private:
- int m_size = 0;
- QColor m_color;
- QString m_brand;
- qreal m_price = 0;
-};
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
-// ![0]
- Q_PROPERTY(ShoeDescription *shoe READ shoe CONSTANT)
-// ![0]
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- ShoeDescription *shoe();
-
-signals:
- void nameChanged();
-
-private:
- QString m_name;
- ShoeDescription m_shoe;
-};
-
-class Boy : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-class Girl : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/coercion/CMakeLists.txt b/examples/qml/referenceexamples/coercion/CMakeLists.txt
deleted file mode 100644
index 495b266f2f..0000000000
--- a/examples/qml/referenceexamples/coercion/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(coercion LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/coercion")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
-
-qt_add_executable(coercion
- birthdayparty.cpp birthdayparty.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(coercion PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(coercion PUBLIC
- Qt::Core
- Qt::Qml
-)
-
-qt_add_qml_module(coercion
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS coercion
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/coercion/birthdayparty.cpp b/examples/qml/referenceexamples/coercion/birthdayparty.cpp
deleted file mode 100644
index 6cf8a608e7..0000000000
--- a/examples/qml/referenceexamples/coercion/birthdayparty.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- m_host = c;
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return {this, &m_guests};
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
diff --git a/examples/qml/referenceexamples/coercion/birthdayparty.h b/examples/qml/referenceexamples/coercion/birthdayparty.h
deleted file mode 100644
index 79508ff9c1..0000000000
--- a/examples/qml/referenceexamples/coercion/birthdayparty.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QQmlListProperty>
-#include "person.h"
-
-class BirthdayParty : public QObject
-{
- Q_OBJECT
-// ![0]
- Q_PROPERTY(Person *host READ host WRITE setHost)
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
- QML_ELEMENT
-// ![0]
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
-
-private:
- Person *m_host = nullptr;
- QList<Person *> m_guests;
-};
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/coercion/coercion.pro b/examples/qml/referenceexamples/coercion/coercion.pro
deleted file mode 100644
index 225fd13e08..0000000000
--- a/examples/qml/referenceexamples/coercion/coercion.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT = core qml
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp
-HEADERS += person.h \
- birthdayparty.h
-RESOURCES += coercion.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/coercion
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/coercion/coercion.qrc b/examples/qml/referenceexamples/coercion/coercion.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/coercion/coercion.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/coercion/example.qml b/examples/qml/referenceexamples/coercion/example.qml
deleted file mode 100644
index 99469bec57..0000000000
--- a/examples/qml/referenceexamples/coercion/example.qml
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-
-// ![0]
-BirthdayParty {
- host: Boy {
- name: "Bob Jones"
- shoeSize: 12
- }
- guests: [
- Boy { name: "Leo Hodges" },
- Boy { name: "Jack Smith" },
- Girl { name: "Anne Brown" }
- ]
-}
-// ![0]
diff --git a/examples/qml/referenceexamples/coercion/main.cpp b/examples/qml/referenceexamples/coercion/main.cpp
deleted file mode 100644
index 5bcd9681e3..0000000000
--- a/examples/qml/referenceexamples/coercion/main.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!";
-
- if (qobject_cast<Boy *>(party->host()))
- qInfo() << "He is inviting:";
- else
- qInfo() << "She is inviting:";
-
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii)
- qInfo() << " " << party->guest(ii)->name();
-
- return EXIT_SUCCESS;
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/coercion/person.cpp b/examples/qml/referenceexamples/coercion/person.cpp
deleted file mode 100644
index ab7aefcbee..0000000000
--- a/examples/qml/referenceexamples/coercion/person.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-int Person::shoeSize() const
-{
- return m_shoeSize;
-}
-
-void Person::setShoeSize(int s)
-{
- m_shoeSize = s;
-}
diff --git a/examples/qml/referenceexamples/coercion/person.h b/examples/qml/referenceexamples/coercion/person.h
deleted file mode 100644
index ea9ff970b4..0000000000
--- a/examples/qml/referenceexamples/coercion/person.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QtQml/qqml.h>
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
- //![0]
- QML_ELEMENT
- QML_UNCREATABLE("Person is an abstract base class.")
- //![0]
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- int shoeSize() const;
- void setShoeSize(int);
-
-private:
- QString m_name;
- int m_shoeSize = 0;
-};
-
-// ![1]
-class Boy : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-//! [girl class]
-class Girl : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-//! [girl class]
-
-// ![1]
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/default/CMakeLists.txt b/examples/qml/referenceexamples/default/CMakeLists.txt
deleted file mode 100644
index 681085f386..0000000000
--- a/examples/qml/referenceexamples/default/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(default LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/default")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
-
-qt_add_executable(default
- birthdayparty.cpp birthdayparty.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(default PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(default PUBLIC
- Qt::Core
- Qt::Qml
-)
-
-qt_add_qml_module(default
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS default
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/default/birthdayparty.cpp b/examples/qml/referenceexamples/default/birthdayparty.cpp
deleted file mode 100644
index 5a9f38d9f3..0000000000
--- a/examples/qml/referenceexamples/default/birthdayparty.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- m_host = c;
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return {this, &m_guests};
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
-
diff --git a/examples/qml/referenceexamples/default/birthdayparty.h b/examples/qml/referenceexamples/default/birthdayparty.h
deleted file mode 100644
index ce5106ef99..0000000000
--- a/examples/qml/referenceexamples/default/birthdayparty.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QQmlListProperty>
-#include "person.h"
-
-// ![0]
-class BirthdayParty : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(Person *host READ host WRITE setHost)
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
- Q_CLASSINFO("DefaultProperty", "guests")
- QML_ELEMENT
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
-
-private:
- Person *m_host = nullptr;
- QList<Person *> m_guests;
-};
-// ![0]
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/default/default.pro b/examples/qml/referenceexamples/default/default.pro
deleted file mode 100644
index f52f749ddd..0000000000
--- a/examples/qml/referenceexamples/default/default.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT = core qml
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp
-HEADERS += person.h \
- birthdayparty.h
-RESOURCES += default.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/default
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/default/default.qrc b/examples/qml/referenceexamples/default/default.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/default/default.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/default/example.qml b/examples/qml/referenceexamples/default/example.qml
deleted file mode 100644
index addc74dfb5..0000000000
--- a/examples/qml/referenceexamples/default/example.qml
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-
-// ![0]
-BirthdayParty {
- host: Boy {
- name: "Bob Jones"
- shoeSize: 12
- }
-
- Boy { name: "Leo Hodges" }
- Boy { name: "Jack Smith" }
- Girl { name: "Anne Brown" }
-}
-// ![0]
diff --git a/examples/qml/referenceexamples/default/main.cpp b/examples/qml/referenceexamples/default/main.cpp
deleted file mode 100644
index 5e7e711ada..0000000000
--- a/examples/qml/referenceexamples/default/main.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!";
-
- if (qobject_cast<Boy *>(party->host()))
- qInfo() << "He is inviting:";
- else
- qInfo() << "She is inviting:";
-
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii)
- qInfo() << " " << party->guest(ii)->name();
-
- return EXIT_SUCCESS;
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/default/person.cpp b/examples/qml/referenceexamples/default/person.cpp
deleted file mode 100644
index ab7aefcbee..0000000000
--- a/examples/qml/referenceexamples/default/person.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-int Person::shoeSize() const
-{
- return m_shoeSize;
-}
-
-void Person::setShoeSize(int s)
-{
- m_shoeSize = s;
-}
diff --git a/examples/qml/referenceexamples/default/person.h b/examples/qml/referenceexamples/default/person.h
deleted file mode 100644
index 04ab53696d..0000000000
--- a/examples/qml/referenceexamples/default/person.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QtQml/qqml.h>
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- int shoeSize() const;
- void setShoeSize(int);
-private:
- QString m_name;
- int m_shoeSize = 0;
-};
-
-class Boy : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-class Girl : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/extended/CMakeLists.txt b/examples/qml/referenceexamples/extended/CMakeLists.txt
deleted file mode 100644
index 6ecff60d0c..0000000000
--- a/examples/qml/referenceexamples/extended/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(extended LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/extended")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Widgets)
-
-qt_add_executable(extended
- lineedit.cpp lineedit.h
- main.cpp
-)
-
-set_target_properties(extended PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(extended PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Widgets
-)
-
-qt_add_qml_module(extended
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS extended
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/extended/example.qml b/examples/qml/referenceexamples/extended/example.qml
deleted file mode 100644
index 8a0b3193d8..0000000000
--- a/examples/qml/referenceexamples/extended/example.qml
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-
-// ![0]
-QLineEdit {
- leftMargin: 20
-}
-// ![0]
diff --git a/examples/qml/referenceexamples/extended/extended.pro b/examples/qml/referenceexamples/extended/extended.pro
deleted file mode 100644
index 094e5201ca..0000000000
--- a/examples/qml/referenceexamples/extended/extended.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-QT += qml widgets
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- lineedit.cpp
-HEADERS += lineedit.h
-RESOURCES += extended.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/extended
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/extended/extended.qrc b/examples/qml/referenceexamples/extended/extended.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/extended/extended.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/extended/lineedit.cpp b/examples/qml/referenceexamples/extended/lineedit.cpp
deleted file mode 100644
index 802f47ef03..0000000000
--- a/examples/qml/referenceexamples/extended/lineedit.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "lineedit.h"
-#include <qqml.h>
-
-LineEditExtension::LineEditExtension(QObject *object)
-: QObject(object), m_lineedit(qobject_cast<QLineEdit *>(object))
-{
-}
-
-int LineEditExtension::leftMargin() const
-{
- return m_lineedit->textMargins().left();
-}
-
-void LineEditExtension::setLeftMargin(int l)
-{
- QMargins m = m_lineedit->textMargins();
- if (m.left() != l) {
- m.setLeft(l);
- m_lineedit->setTextMargins(m);
- emit marginsChanged();
- }
-}
-
-int LineEditExtension::rightMargin() const
-{
- return m_lineedit->textMargins().right();
-}
-
-void LineEditExtension::setRightMargin(int r)
-{
- QMargins m = m_lineedit->textMargins();
- if (m.right() != r) {
- m.setRight(r);
- m_lineedit->setTextMargins(m);
- emit marginsChanged();
- }
-}
-
-int LineEditExtension::topMargin() const
-{
- return m_lineedit->textMargins().top();
-}
-
-void LineEditExtension::setTopMargin(int t)
-{
- QMargins m = m_lineedit->textMargins();
- if (m.top() != t) {
- m.setTop(t);
- m_lineedit->setTextMargins(m);
- emit marginsChanged();
- }
-}
-
-int LineEditExtension::bottomMargin() const
-{
- return m_lineedit->textMargins().bottom();
-}
-
-void LineEditExtension::setBottomMargin(int b)
-{
- QMargins m = m_lineedit->textMargins();
- if (m.bottom() != b) {
- m.setBottom(b);
- m_lineedit->setTextMargins(m);
- emit marginsChanged();
- }
-}
-
-
diff --git a/examples/qml/referenceexamples/extended/lineedit.h b/examples/qml/referenceexamples/extended/lineedit.h
deleted file mode 100644
index 44bb9fe72d..0000000000
--- a/examples/qml/referenceexamples/extended/lineedit.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#ifndef LINEEDIT_H
-#define LINEEDIT_H
-
-#include <QLineEdit>
-#include <qqml.h>
-
-class LineEditExtension : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int leftMargin READ leftMargin WRITE setLeftMargin NOTIFY marginsChanged)
- Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin NOTIFY marginsChanged)
- Q_PROPERTY(int topMargin READ topMargin WRITE setTopMargin NOTIFY marginsChanged)
- Q_PROPERTY(int bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY marginsChanged)
-public:
- LineEditExtension(QObject *);
-
- int leftMargin() const;
- void setLeftMargin(int);
-
- int rightMargin() const;
- void setRightMargin(int);
-
- int topMargin() const;
- void setTopMargin(int);
-
- int bottomMargin() const;
- void setBottomMargin(int);
-signals:
- void marginsChanged();
-
-private:
- QLineEdit *m_lineedit;
-};
-
-// ![0]
-struct QLineEditForeign
-{
- Q_GADGET
- QML_FOREIGN(QLineEdit)
- QML_NAMED_ELEMENT(QLineEdit)
- QML_EXTENDED(LineEditExtension)
-};
-// ![0]
-
-#endif // LINEEDIT_H
diff --git a/examples/qml/referenceexamples/extended/main.cpp b/examples/qml/referenceexamples/extended/main.cpp
deleted file mode 100644
index 7861acf707..0000000000
--- a/examples/qml/referenceexamples/extended/main.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include <QLineEdit>
-#include "lineedit.h"
-
-int main(int argc, char ** argv)
-{
- QApplication app(argc, argv);
-
-// ![1]
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *edit = qobject_cast<QLineEdit *>(component.create());
-// ![1]
-
- if (edit) {
- edit->show();
- return QApplication::exec();
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/grouped/CMakeLists.txt b/examples/qml/referenceexamples/grouped/CMakeLists.txt
deleted file mode 100644
index b1c7587b58..0000000000
--- a/examples/qml/referenceexamples/grouped/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(grouped LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/grouped")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml)
-
-qt_add_executable(grouped
- birthdayparty.cpp birthdayparty.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(grouped PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(grouped PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
-)
-
-qt_add_qml_module(grouped
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS grouped
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/grouped/birthdayparty.cpp b/examples/qml/referenceexamples/grouped/birthdayparty.cpp
deleted file mode 100644
index 6cf8a608e7..0000000000
--- a/examples/qml/referenceexamples/grouped/birthdayparty.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- m_host = c;
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return {this, &m_guests};
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
diff --git a/examples/qml/referenceexamples/grouped/birthdayparty.h b/examples/qml/referenceexamples/grouped/birthdayparty.h
deleted file mode 100644
index 7f011ca95f..0000000000
--- a/examples/qml/referenceexamples/grouped/birthdayparty.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QQmlListProperty>
-#include "person.h"
-
-class BirthdayParty : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(Person *host READ host WRITE setHost)
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
- Q_CLASSINFO("DefaultProperty", "guests")
- QML_ELEMENT
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
-
-private:
- Person *m_host;
- QList<Person *> m_guests;
-};
-
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/grouped/example.qml b/examples/qml/referenceexamples/grouped/example.qml
deleted file mode 100644
index 31d122e647..0000000000
--- a/examples/qml/referenceexamples/grouped/example.qml
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-import QtQuick // For QColor
-
-// ![0]
-BirthdayParty {
- host: Boy {
- name: "Bob Jones"
- shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 }
- }
-
- Boy {
- name: "Leo Hodges"
-//![grouped]
- shoe { size: 10; color: "black"; brand: "Thebok"; price: 59.95 }
-//![grouped]
- }
- // ![1]
- Boy {
- name: "Jack Smith"
- shoe {
- size: 8
- color: "blue"
- brand: "Luma"
- price: 19.95
- }
- }
- // ![1]
- Girl {
- name: "Anne Brown"
-//![ungrouped]
- shoe.size: 7
- shoe.color: "red"
- shoe.brand: "Job Macobs"
- shoe.price: 699.99
-//![ungrouped]
- }
-}
-// ![0]
diff --git a/examples/qml/referenceexamples/grouped/grouped.pro b/examples/qml/referenceexamples/grouped/grouped.pro
deleted file mode 100644
index 1513ac552d..0000000000
--- a/examples/qml/referenceexamples/grouped/grouped.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT += qml
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp
-HEADERS += person.h \
- birthdayparty.h
-RESOURCES += grouped.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/grouped
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/grouped/grouped.qrc b/examples/qml/referenceexamples/grouped/grouped.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/grouped/grouped.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/grouped/main.cpp b/examples/qml/referenceexamples/grouped/main.cpp
deleted file mode 100644
index 16f17262a2..0000000000
--- a/examples/qml/referenceexamples/grouped/main.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!";
-
- if (qobject_cast<Boy *>(party->host()))
- qInfo() << "He is inviting:";
- else
- qInfo() << "She is inviting:";
-
- Person *bestShoe = nullptr;
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
- Person *guest = party->guest(ii);
- qInfo() << " " << guest->name();
-
- if (!bestShoe || bestShoe->shoe()->price() < guest->shoe()->price())
- bestShoe = guest;
- }
- if (bestShoe)
- qInfo() << bestShoe->name() << "is wearing the best shoes!";
-
- return EXIT_SUCCESS;
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/grouped/person.cpp b/examples/qml/referenceexamples/grouped/person.cpp
deleted file mode 100644
index 358fbf0ed8..0000000000
--- a/examples/qml/referenceexamples/grouped/person.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-int ShoeDescription::size() const
-{
- return m_size;
-}
-
-void ShoeDescription::setSize(int s)
-{
- m_size = s;
-}
-
-QColor ShoeDescription::color() const
-{
- return m_color;
-}
-
-void ShoeDescription::setColor(const QColor &c)
-{
- m_color = c;
-}
-
-QString ShoeDescription::brand() const
-{
- return m_brand;
-}
-
-void ShoeDescription::setBrand(const QString &b)
-{
- m_brand = b;
-}
-
-qreal ShoeDescription::price() const
-{
- return m_price;
-}
-
-void ShoeDescription::setPrice(qreal p)
-{
- m_price = p;
-}
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-ShoeDescription *Person::shoe()
-{
- return &m_shoe;
-}
diff --git a/examples/qml/referenceexamples/grouped/person.h b/examples/qml/referenceexamples/grouped/person.h
deleted file mode 100644
index b5fa469025..0000000000
--- a/examples/qml/referenceexamples/grouped/person.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QColor>
-#include <QtQml/qqml.h>
-
-class ShoeDescription : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int size READ size WRITE setSize)
- Q_PROPERTY(QColor color READ color WRITE setColor)
- Q_PROPERTY(QString brand READ brand WRITE setBrand)
- Q_PROPERTY(qreal price READ price WRITE setPrice)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- int size() const;
- void setSize(int);
-
- QColor color() const;
- void setColor(const QColor &);
-
- QString brand() const;
- void setBrand(const QString &);
-
- qreal price() const;
- void setPrice(qreal);
-
-private:
- int m_size = 0;
- QColor m_color;
- QString m_brand;
- qreal m_price = 0;
-};
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
-// ![1]
- Q_PROPERTY(ShoeDescription *shoe READ shoe)
-// ![1]
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- ShoeDescription *shoe();
-private:
- QString m_name;
- ShoeDescription m_shoe;
-};
-
-class Boy : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-class Girl : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/methods/CMakeLists.txt b/examples/qml/referenceexamples/methods/CMakeLists.txt
deleted file mode 100644
index 57c0733723..0000000000
--- a/examples/qml/referenceexamples/methods/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(methods LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/methods")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
-
-qt_add_executable(methods
- birthdayparty.cpp birthdayparty.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(methods PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(methods PUBLIC
- Qt::Core
- Qt::Qml
-)
-
-qt_add_qml_module(methods
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS methods
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/methods/birthdayparty.cpp b/examples/qml/referenceexamples/methods/birthdayparty.cpp
deleted file mode 100644
index 35ae42f779..0000000000
--- a/examples/qml/referenceexamples/methods/birthdayparty.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-// ![0]
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- m_host = c;
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return {this, &m_guests};
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
-
-void BirthdayParty::invite(const QString &name)
-{
- auto *person = new Person(this);
- person->setName(name);
- m_guests.append(person);
-}
-// ![0]
-
diff --git a/examples/qml/referenceexamples/methods/birthdayparty.h b/examples/qml/referenceexamples/methods/birthdayparty.h
deleted file mode 100644
index 796464c333..0000000000
--- a/examples/qml/referenceexamples/methods/birthdayparty.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QQmlListProperty>
-#include "person.h"
-
-class BirthdayParty : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(Person *host READ host WRITE setHost)
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
- QML_ELEMENT
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
-
-// ![0]
- Q_INVOKABLE void invite(const QString &name);
-// ![0]
-
-private:
- Person *m_host = nullptr;
- QList<Person *> m_guests;
-};
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/methods/example.qml b/examples/qml/referenceexamples/methods/example.qml
deleted file mode 100644
index 41d053edd2..0000000000
--- a/examples/qml/referenceexamples/methods/example.qml
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-// ![0]
-import QtQuick
-import People
-
-BirthdayParty {
- host: Person {
- name: "Bob Jones"
- shoeSize: 12
- }
- guests: [
- Person { name: "Leo Hodges" },
- Person { name: "Jack Smith" },
- Person { name: "Anne Brown" }
- ]
-
- Component.onCompleted: invite("William Green")
-}
-// ![0]
diff --git a/examples/qml/referenceexamples/methods/main.cpp b/examples/qml/referenceexamples/methods/main.cpp
deleted file mode 100644
index dd9b03566b..0000000000
--- a/examples/qml/referenceexamples/methods/main.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!"
- << "\nThey are inviting:";
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii)
- qInfo() << " " << party->guest(ii)->name();
- return EXIT_SUCCESS;
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/methods/methods.pro b/examples/qml/referenceexamples/methods/methods.pro
deleted file mode 100644
index 2a5f3cff41..0000000000
--- a/examples/qml/referenceexamples/methods/methods.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT = core qml
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp
-HEADERS += person.h \
- birthdayparty.h
-RESOURCES += methods.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/methods
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/methods/methods.qrc b/examples/qml/referenceexamples/methods/methods.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/methods/methods.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/methods/person.cpp b/examples/qml/referenceexamples/methods/person.cpp
deleted file mode 100644
index ab7aefcbee..0000000000
--- a/examples/qml/referenceexamples/methods/person.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-int Person::shoeSize() const
-{
- return m_shoeSize;
-}
-
-void Person::setShoeSize(int s)
-{
- m_shoeSize = s;
-}
diff --git a/examples/qml/referenceexamples/methods/person.h b/examples/qml/referenceexamples/methods/person.h
deleted file mode 100644
index d8d4941183..0000000000
--- a/examples/qml/referenceexamples/methods/person.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QtQml/qqml.h>
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
- QML_ELEMENT
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- int shoeSize() const;
- void setShoeSize(int);
-
-private:
- QString m_name;
- int m_shoeSize = 0;
-};
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/properties/CMakeLists.txt b/examples/qml/referenceexamples/properties/CMakeLists.txt
deleted file mode 100644
index dbe521d17d..0000000000
--- a/examples/qml/referenceexamples/properties/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(properties LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/properties")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
-
-qt_add_executable(properties
- birthdayparty.cpp birthdayparty.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(properties PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(properties PUBLIC
- Qt::Core
- Qt::Qml
-)
-
-qt_add_qml_module(properties
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS properties
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/properties/birthdayparty.cpp b/examples/qml/referenceexamples/properties/birthdayparty.cpp
deleted file mode 100644
index 2f97634abc..0000000000
--- a/examples/qml/referenceexamples/properties/birthdayparty.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-// ![0]
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- m_host = c;
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return {this, this,
- &BirthdayParty::appendGuest,
- &BirthdayParty::guestCount,
- &BirthdayParty::guest,
- &BirthdayParty::clearGuests,
- &BirthdayParty::replaceGuest,
- &BirthdayParty::removeLastGuest};
-}
-
-void BirthdayParty::appendGuest(Person *p)
-{
- m_guests.append(p);
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
-
-void BirthdayParty::clearGuests() {
- m_guests.clear();
-}
-
-void BirthdayParty::replaceGuest(qsizetype index, Person *p)
-{
- m_guests[index] = p;
-}
-
-void BirthdayParty::removeLastGuest()
-{
- m_guests.removeLast();
-}
-
-// ![0]
-
-void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *p)
-{
- reinterpret_cast< BirthdayParty *>(list->data)->appendGuest(p);
-}
-
-void BirthdayParty::clearGuests(QQmlListProperty<Person>* list)
-{
- reinterpret_cast< BirthdayParty *>(list->data)->clearGuests();
-}
-
-void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype i, Person *p)
-{
- reinterpret_cast< BirthdayParty* >(list->data)->replaceGuest(i, p);
-}
-
-void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list)
-{
- reinterpret_cast< BirthdayParty* >(list->data)->removeLastGuest();
-}
-
-Person* BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype i)
-{
- return reinterpret_cast< BirthdayParty* >(list->data)->guest(i);
-}
-
-qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list)
-{
- return reinterpret_cast< BirthdayParty* >(list->data)->guestCount();
-}
diff --git a/examples/qml/referenceexamples/properties/birthdayparty.h b/examples/qml/referenceexamples/properties/birthdayparty.h
deleted file mode 100644
index 239626da17..0000000000
--- a/examples/qml/referenceexamples/properties/birthdayparty.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QList>
-#include <QQmlListProperty>
-#include "person.h"
-
-// ![0]
-class BirthdayParty : public QObject
-{
- Q_OBJECT
-// ![0]
-// ![1]
- Q_PROPERTY(Person *host READ host WRITE setHost)
-// ![1]
-// ![2]
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
-// ![2]
-// ![3]
- QML_ELEMENT
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- void appendGuest(Person *);
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
- void clearGuests();
- void replaceGuest(qsizetype, Person *);
- void removeLastGuest();
-
-private:
- static void appendGuest(QQmlListProperty<Person> *, Person *);
- static qsizetype guestCount(QQmlListProperty<Person> *);
- static Person* guest(QQmlListProperty<Person> *, qsizetype);
- static void clearGuests(QQmlListProperty<Person> *);
- static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *);
- static void removeLastGuest(QQmlListProperty<Person> *);
-
- Person *m_host = nullptr;
- QList<Person *> m_guests;
-};
-// ![3]
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/properties/example.qml b/examples/qml/referenceexamples/properties/example.qml
deleted file mode 100644
index 52fb3344f0..0000000000
--- a/examples/qml/referenceexamples/properties/example.qml
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-
-// ![0]
-BirthdayParty {
- host: Person {
- name: "Bob Jones"
- shoeSize: 12
- }
- guests: [
- Person { name: "Leo Hodges" },
- Person { name: "Jack Smith" },
- Person { name: "Anne Brown" }
- ]
-}
-// ![0]
diff --git a/examples/qml/referenceexamples/properties/main.cpp b/examples/qml/referenceexamples/properties/main.cpp
deleted file mode 100644
index f381686dbc..0000000000
--- a/examples/qml/referenceexamples/properties/main.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!\n"
- "They are inviting:";
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii)
- qInfo() << " " << party->guest(ii)->name();
- return EXIT_SUCCESS;
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/properties/person.cpp b/examples/qml/referenceexamples/properties/person.cpp
deleted file mode 100644
index ab7aefcbee..0000000000
--- a/examples/qml/referenceexamples/properties/person.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-int Person::shoeSize() const
-{
- return m_shoeSize;
-}
-
-void Person::setShoeSize(int s)
-{
- m_shoeSize = s;
-}
diff --git a/examples/qml/referenceexamples/properties/person.h b/examples/qml/referenceexamples/properties/person.h
deleted file mode 100644
index d8d4941183..0000000000
--- a/examples/qml/referenceexamples/properties/person.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QtQml/qqml.h>
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
- QML_ELEMENT
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- int shoeSize() const;
- void setShoeSize(int);
-
-private:
- QString m_name;
- int m_shoeSize = 0;
-};
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/properties/properties.pro b/examples/qml/referenceexamples/properties/properties.pro
deleted file mode 100644
index 6697afa2c5..0000000000
--- a/examples/qml/referenceexamples/properties/properties.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT = core qml
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp
-HEADERS += person.h \
- birthdayparty.h
-RESOURCES += properties.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/properties
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/properties/properties.qrc b/examples/qml/referenceexamples/properties/properties.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/properties/properties.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/referenceexamples.pro b/examples/qml/referenceexamples/referenceexamples.pro
deleted file mode 100644
index 3f4bbcf75d..0000000000
--- a/examples/qml/referenceexamples/referenceexamples.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS += \
- adding \
- coercion \
- default \
- properties \
- methods
-
-qtHaveModule(widgets): SUBDIRS += extended
-
-qtHaveModule(quick): SUBDIRS += \
- attached \
- binding \
- grouped \
- signal \
- valuesource
diff --git a/examples/qml/referenceexamples/referenceexamples.qmlproject b/examples/qml/referenceexamples/referenceexamples.qmlproject
deleted file mode 100644
index 2bb4016996..0000000000
--- a/examples/qml/referenceexamples/referenceexamples.qmlproject
+++ /dev/null
@@ -1,14 +0,0 @@
-import QmlProject 1.0
-
-Project {
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/qml/referenceexamples/signal/CMakeLists.txt b/examples/qml/referenceexamples/signal/CMakeLists.txt
deleted file mode 100644
index ce196f3a38..0000000000
--- a/examples/qml/referenceexamples/signal/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(signal LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/signal")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml)
-
-qt_add_executable(signal
- birthdayparty.cpp birthdayparty.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(signal PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(signal PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
-)
-
-qt_add_qml_module(signal
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS signal
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/signal/birthdayparty.cpp b/examples/qml/referenceexamples/signal/birthdayparty.cpp
deleted file mode 100644
index b2cc131597..0000000000
--- a/examples/qml/referenceexamples/signal/birthdayparty.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-QDate BirthdayPartyAttached::rsvp() const
-{
- return m_rsvp;
-}
-
-void BirthdayPartyAttached::setRsvp(QDate d)
-{
- m_rsvp = d;
-}
-
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- m_host = c;
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return {this, &m_guests};
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
-
-void BirthdayParty::startParty()
-{
- QTime time = QTime::currentTime();
- emit partyStarted(time);
-}
-
-BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
-{
- return new BirthdayPartyAttached(object);
-}
-
diff --git a/examples/qml/referenceexamples/signal/birthdayparty.h b/examples/qml/referenceexamples/signal/birthdayparty.h
deleted file mode 100644
index 22f3bdf9da..0000000000
--- a/examples/qml/referenceexamples/signal/birthdayparty.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QDate>
-#include <qqml.h>
-#include "person.h"
-
-class BirthdayPartyAttached : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QDate rsvp() const;
- void setRsvp(QDate);
-
-private:
- QDate m_rsvp;
-};
-
-class BirthdayParty : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(Person *host READ host WRITE setHost)
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
- Q_CLASSINFO("DefaultProperty", "guests")
- QML_ELEMENT
- QML_ATTACHED(BirthdayPartyAttached)
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
-
- static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
-
- void startParty();
-// ![0]
-signals:
- void partyStarted(QTime time);
-// ![0]
-
-private:
- Person *m_host = nullptr;
- QList<Person *> m_guests;
-};
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/signal/example.qml b/examples/qml/referenceexamples/signal/example.qml
deleted file mode 100644
index c35db2fe46..0000000000
--- a/examples/qml/referenceexamples/signal/example.qml
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-import QtQuick // For QColor
-
-BirthdayParty {
-// ![0]
- onPartyStarted: (time) => { console.log("This party started rockin' at " + time); }
-// ![0]
-
- host: Boy {
- name: "Bob Jones"
- shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 }
- }
-
- Boy {
- name: "Leo Hodges"
- BirthdayParty.rsvp: "2009-07-06"
- shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
- }
- Boy {
- name: "Jack Smith"
- shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
- }
- Girl {
- name: "Anne Brown"
- BirthdayParty.rsvp: "2009-07-01"
- shoe.size: 7
- shoe.color: "red"
- shoe.brand: "Marc Jacobs"
- shoe.price: 699.99
- }
-// ![1]
-}
-// ![1]
diff --git a/examples/qml/referenceexamples/signal/main.cpp b/examples/qml/referenceexamples/signal/main.cpp
deleted file mode 100644
index f99ddeb6c7..0000000000
--- a/examples/qml/referenceexamples/signal/main.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!";
-
- if (qobject_cast<Boy *>(party->host()))
- qInfo() << "He is inviting:";
- else
- qInfo() << "She is inviting:";
-
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
- Person *guest = party->guest(ii);
-
- QDate rsvpDate;
- QObject *attached =
- qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
- if (attached)
- rsvpDate = attached->property("rsvp").toDate();
-
- if (rsvpDate.isNull())
- qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd";
- else
- qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString();
- }
-
- party->startParty();
- return EXIT_SUCCESS;
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/signal/person.cpp b/examples/qml/referenceexamples/signal/person.cpp
deleted file mode 100644
index 358fbf0ed8..0000000000
--- a/examples/qml/referenceexamples/signal/person.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-int ShoeDescription::size() const
-{
- return m_size;
-}
-
-void ShoeDescription::setSize(int s)
-{
- m_size = s;
-}
-
-QColor ShoeDescription::color() const
-{
- return m_color;
-}
-
-void ShoeDescription::setColor(const QColor &c)
-{
- m_color = c;
-}
-
-QString ShoeDescription::brand() const
-{
- return m_brand;
-}
-
-void ShoeDescription::setBrand(const QString &b)
-{
- m_brand = b;
-}
-
-qreal ShoeDescription::price() const
-{
- return m_price;
-}
-
-void ShoeDescription::setPrice(qreal p)
-{
- m_price = p;
-}
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-ShoeDescription *Person::shoe()
-{
- return &m_shoe;
-}
diff --git a/examples/qml/referenceexamples/signal/person.h b/examples/qml/referenceexamples/signal/person.h
deleted file mode 100644
index 9c617ee9ab..0000000000
--- a/examples/qml/referenceexamples/signal/person.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QColor>
-#include <QtQml/qqml.h>
-
-class ShoeDescription : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int size READ size WRITE setSize)
- Q_PROPERTY(QColor color READ color WRITE setColor)
- Q_PROPERTY(QString brand READ brand WRITE setBrand)
- Q_PROPERTY(qreal price READ price WRITE setPrice)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- int size() const;
- void setSize(int);
-
- QColor color() const;
- void setColor(const QColor &);
-
- QString brand() const;
- void setBrand(const QString &);
-
- qreal price() const;
- void setPrice(qreal);
-
-private:
- int m_size = 0;
- QColor m_color;
- QString m_brand;
- qreal m_price = 0;
-};
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(ShoeDescription *shoe READ shoe)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- ShoeDescription *shoe();
-private:
- QString m_name;
- ShoeDescription m_shoe;
-};
-
-class Boy : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-class Girl : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/signal/signal.pro b/examples/qml/referenceexamples/signal/signal.pro
deleted file mode 100644
index 3c31234b3c..0000000000
--- a/examples/qml/referenceexamples/signal/signal.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT += qml
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp
-HEADERS += person.h \
- birthdayparty.h
-RESOURCES += signal.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/signal
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/signal/signal.qrc b/examples/qml/referenceexamples/signal/signal.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/signal/signal.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/referenceexamples/valuesource/CMakeLists.txt b/examples/qml/referenceexamples/valuesource/CMakeLists.txt
deleted file mode 100644
index 266bb2b754..0000000000
--- a/examples/qml/referenceexamples/valuesource/CMakeLists.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(valuesource LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/referenceexamples/valuesource")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml)
-
-qt_add_executable(valuesource
- birthdayparty.cpp birthdayparty.h
- happybirthdaysong.cpp happybirthdaysong.h
- main.cpp
- person.cpp person.h
-)
-
-set_target_properties(valuesource PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(valuesource PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
-)
-
-qt_add_qml_module(valuesource
- URI People
- QML_FILES example.qml
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS valuesource
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/referenceexamples/valuesource/birthdayparty.cpp b/examples/qml/referenceexamples/valuesource/birthdayparty.cpp
deleted file mode 100644
index ba8b8829e7..0000000000
--- a/examples/qml/referenceexamples/valuesource/birthdayparty.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "birthdayparty.h"
-
-QDate BirthdayPartyAttached::rsvp() const
-{
- return m_rsvp;
-}
-
-void BirthdayPartyAttached::setRsvp(QDate d)
-{
- m_rsvp = d;
-}
-
-Person *BirthdayParty::host() const
-{
- return m_host;
-}
-
-void BirthdayParty::setHost(Person *c)
-{
- m_host = c;
-}
-
-QQmlListProperty<Person> BirthdayParty::guests()
-{
- return {this, &m_guests};
-}
-
-qsizetype BirthdayParty::guestCount() const
-{
- return m_guests.count();
-}
-
-Person *BirthdayParty::guest(qsizetype index) const
-{
- return m_guests.at(index);
-}
-
-void BirthdayParty::startParty()
-{
- QTime time = QTime::currentTime();
- emit partyStarted(time);
-}
-
-QString BirthdayParty::announcement() const
-{
- return QString();
-}
-
-void BirthdayParty::setAnnouncement(const QString &speak)
-{
- qInfo().noquote() << speak;
-}
-
-BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
-{
- return new BirthdayPartyAttached(object);
-}
diff --git a/examples/qml/referenceexamples/valuesource/birthdayparty.h b/examples/qml/referenceexamples/valuesource/birthdayparty.h
deleted file mode 100644
index 922dac838b..0000000000
--- a/examples/qml/referenceexamples/valuesource/birthdayparty.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef BIRTHDAYPARTY_H
-#define BIRTHDAYPARTY_H
-
-#include <QObject>
-#include <QDate>
-#include <QDebug>
-#include <qqml.h>
-#include "person.h"
-
-class BirthdayPartyAttached : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QDate rsvp() const;
- void setRsvp(QDate);
-
-private:
- QDate m_rsvp;
-};
-
-class BirthdayParty : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(Person *host READ host WRITE setHost)
- Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
-// ![0]
- Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement)
-// ![0]
- Q_CLASSINFO("DefaultProperty", "guests")
- QML_ELEMENT
- QML_ATTACHED(BirthdayPartyAttached)
-public:
- using QObject::QObject;
-
- Person *host() const;
- void setHost(Person *);
-
- QQmlListProperty<Person> guests();
- qsizetype guestCount() const;
- Person *guest(qsizetype) const;
-
- QString announcement() const;
- void setAnnouncement(const QString &);
-
- static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
-
- void startParty();
-
-signals:
- void partyStarted(QTime time);
-
-private:
- Person *m_host = nullptr;
- QList<Person *> m_guests;
-};
-
-#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/referenceexamples/valuesource/example.qml b/examples/qml/referenceexamples/valuesource/example.qml
deleted file mode 100644
index b6efe211c1..0000000000
--- a/examples/qml/referenceexamples/valuesource/example.qml
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import People
-import QtQuick // For QColor
-
-// ![0]
-BirthdayParty {
- HappyBirthdaySong on announcement { name: "Bob Jones" }
-// ![0]
-
- onPartyStarted: (time) => { console.log("This party started rockin' at " + time); }
-
-
- host: Boy {
- name: "Bob Jones"
- shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 }
- }
-
- Boy {
- name: "Leo Hodges"
- BirthdayParty.rsvp: "2009-07-06"
- shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
- }
- Boy {
- name: "Jack Smith"
- shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
- }
- Girl {
- name: "Anne Brown"
- BirthdayParty.rsvp: "2009-07-01"
- shoe.size: 7
- shoe.color: "red"
- shoe.brand: "Marc Jacobs"
- shoe.price: 699.99
- }
-
-// ![1]
-}
-// ![1]
diff --git a/examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp b/examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp
deleted file mode 100644
index e9a8c13ae9..0000000000
--- a/examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "happybirthdaysong.h"
-#include <QTimer>
-
-HappyBirthdaySong::HappyBirthdaySong(QObject *parent) :
- QObject(parent)
-{
- auto *timer = new QTimer(this);
- QObject::connect(timer, &QTimer::timeout, this, &HappyBirthdaySong::advance);
- timer->start(1000);
-}
-
-void HappyBirthdaySong::setTarget(const QQmlProperty &p)
-{
- m_target = p;
-}
-
-QString HappyBirthdaySong::name() const
-{
- return m_name;
-}
-
-void HappyBirthdaySong::setName(const QString &name)
-{
- m_name = name;
-
- m_lyrics.clear();
- m_lyrics << "Happy birthday to you,";
- m_lyrics << "Happy birthday to you,";
- m_lyrics << "Happy birthday dear " + m_name + ",";
- m_lyrics << "Happy birthday to you!";
- m_lyrics << "";
-}
-
-void HappyBirthdaySong::advance()
-{
- m_line = (m_line + 1) % m_lyrics.count();
-
- m_target.write(m_lyrics.at(m_line));
-}
-
diff --git a/examples/qml/referenceexamples/valuesource/happybirthdaysong.h b/examples/qml/referenceexamples/valuesource/happybirthdaysong.h
deleted file mode 100644
index 1aad47232a..0000000000
--- a/examples/qml/referenceexamples/valuesource/happybirthdaysong.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef HAPPYBIRTHDAYSONG_H
-#define HAPPYBIRTHDAYSONG_H
-
-#include <QQmlPropertyValueSource>
-#include <QQmlProperty>
-#include <qqml.h>
-
-#include <QStringList>
-
-// ![0]
-class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
-{
- Q_OBJECT
- Q_INTERFACES(QQmlPropertyValueSource)
-// ![0]
- Q_PROPERTY(QString name READ name WRITE setName)
-// ![1]
- QML_ELEMENT
-public:
- explicit HappyBirthdaySong(QObject *parent = nullptr);
-
- void setTarget(const QQmlProperty &) override;
-// ![1]
-
- QString name() const;
- void setName(const QString &);
-
-private slots:
- void advance();
-
-private:
- qsizetype m_line = -1;
- QStringList m_lyrics;
- QQmlProperty m_target;
- QString m_name;
-// ![2]
-};
-// ![2]
-
-#endif // HAPPYBIRTHDAYSONG_H
-
diff --git a/examples/qml/referenceexamples/valuesource/main.cpp b/examples/qml/referenceexamples/valuesource/main.cpp
deleted file mode 100644
index 716deeccda..0000000000
--- a/examples/qml/referenceexamples/valuesource/main.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QCoreApplication>
-#include <QQmlEngine>
-#include <QQmlComponent>
-#include <QDebug>
-#include "birthdayparty.h"
-#include "happybirthdaysong.h"
-#include "person.h"
-
-int main(int argc, char ** argv)
-{
- QCoreApplication app(argc, argv);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- auto *party = qobject_cast<BirthdayParty *>(component.create());
-
- if (party && party->host()) {
- qInfo() << party->host()->name() << "is having a birthday!";
-
- if (qobject_cast<Boy *>(party->host()))
- qInfo() << "He is inviting:";
- else
- qInfo() << "She is inviting:";
-
- for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
- Person *guest = party->guest(ii);
-
- QDate rsvpDate;
- QObject *attached =
- qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
- if (attached)
- rsvpDate = attached->property("rsvp").toDate();
-
- if (rsvpDate.isNull())
- qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd";
- else
- qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString();
- }
-
- party->startParty();
- return QCoreApplication::exec();
- }
-
- qWarning() << component.errors();
- return EXIT_FAILURE;
-}
diff --git a/examples/qml/referenceexamples/valuesource/person.cpp b/examples/qml/referenceexamples/valuesource/person.cpp
deleted file mode 100644
index 358fbf0ed8..0000000000
--- a/examples/qml/referenceexamples/valuesource/person.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "person.h"
-
-int ShoeDescription::size() const
-{
- return m_size;
-}
-
-void ShoeDescription::setSize(int s)
-{
- m_size = s;
-}
-
-QColor ShoeDescription::color() const
-{
- return m_color;
-}
-
-void ShoeDescription::setColor(const QColor &c)
-{
- m_color = c;
-}
-
-QString ShoeDescription::brand() const
-{
- return m_brand;
-}
-
-void ShoeDescription::setBrand(const QString &b)
-{
- m_brand = b;
-}
-
-qreal ShoeDescription::price() const
-{
- return m_price;
-}
-
-void ShoeDescription::setPrice(qreal p)
-{
- m_price = p;
-}
-
-QString Person::name() const
-{
- return m_name;
-}
-
-void Person::setName(const QString &n)
-{
- m_name = n;
-}
-
-ShoeDescription *Person::shoe()
-{
- return &m_shoe;
-}
diff --git a/examples/qml/referenceexamples/valuesource/person.h b/examples/qml/referenceexamples/valuesource/person.h
deleted file mode 100644
index b5fa469025..0000000000
--- a/examples/qml/referenceexamples/valuesource/person.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef PERSON_H
-#define PERSON_H
-
-#include <QObject>
-#include <QColor>
-#include <QtQml/qqml.h>
-
-class ShoeDescription : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int size READ size WRITE setSize)
- Q_PROPERTY(QColor color READ color WRITE setColor)
- Q_PROPERTY(QString brand READ brand WRITE setBrand)
- Q_PROPERTY(qreal price READ price WRITE setPrice)
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- int size() const;
- void setSize(int);
-
- QColor color() const;
- void setColor(const QColor &);
-
- QString brand() const;
- void setBrand(const QString &);
-
- qreal price() const;
- void setPrice(qreal);
-
-private:
- int m_size = 0;
- QColor m_color;
- QString m_brand;
- qreal m_price = 0;
-};
-
-class Person : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QString name READ name WRITE setName)
-// ![1]
- Q_PROPERTY(ShoeDescription *shoe READ shoe)
-// ![1]
- QML_ANONYMOUS
-public:
- using QObject::QObject;
-
- QString name() const;
- void setName(const QString &);
-
- ShoeDescription *shoe();
-private:
- QString m_name;
- ShoeDescription m_shoe;
-};
-
-class Boy : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-class Girl : public Person
-{
- Q_OBJECT
- QML_ELEMENT
-public:
- using Person::Person;
-};
-
-#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/valuesource/valuesource.pro b/examples/qml/referenceexamples/valuesource/valuesource.pro
deleted file mode 100644
index 6d29cf1b70..0000000000
--- a/examples/qml/referenceexamples/valuesource/valuesource.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-QT += qml
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = People
-QML_IMPORT_MAJOR_VERSION = 1
-
-SOURCES += main.cpp \
- person.cpp \
- birthdayparty.cpp \
- happybirthdaysong.cpp
-HEADERS += person.h \
- birthdayparty.h \
- happybirthdaysong.h
-RESOURCES += valuesource.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/referenceexamples/valuesource
-INSTALLS += target
diff --git a/examples/qml/referenceexamples/valuesource/valuesource.qrc b/examples/qml/referenceexamples/valuesource/valuesource.qrc
deleted file mode 100644
index e2fa01d5e7..0000000000
--- a/examples/qml/referenceexamples/valuesource/valuesource.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>example.qml</file>
-</qresource>
-</RCC>
diff --git a/examples/qml/shell/CMakeLists.txt b/examples/qml/shell/CMakeLists.txt
deleted file mode 100644
index b5bda8a4db..0000000000
--- a/examples/qml/shell/CMakeLists.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(shell LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/shell")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Qml)
-
-qt_add_executable(shell
- main.cpp
-)
-
-set_target_properties(shell PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(shell PUBLIC
- Qt::Core
- Qt::Qml
-)
-
-if(WIN32)
- set_target_properties(shell PROPERTIES
- WIN32_EXECUTABLE FALSE
- )
-endif()
-
-if(APPLE)
- set_target_properties(shell PROPERTIES
- MACOSX_BUNDLE FALSE
- )
-endif()
-
-install(TARGETS shell
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/shell/main.cpp b/examples/qml/shell/main.cpp
deleted file mode 100644
index 6ec72c3d05..0000000000
--- a/examples/qml/shell/main.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-
-#include <QtCore/qfile.h>
-#include <QtCore/qtextstream.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qscopedpointer.h>
-
-#include <QtCore/QCoreApplication>
-
-#include <QtQml/qjsengine.h>
-
-#include <stdlib.h>
-
-
-class CommandInterface : public QObject
-{
- Q_OBJECT
-public:
- Q_INVOKABLE void quit() { m_wantsToQuit = true; }
- static bool wantsToQuit() { return m_wantsToQuit; }
-private:
- static bool m_wantsToQuit;
-};
-
-bool CommandInterface::m_wantsToQuit = false;
-
-
-static void interactive(QJSEngine *eng)
-{
- QTextStream qin(stdin, QFile::ReadOnly);
- const char *prompt = "qs> ";
-
- forever {
- QString line;
-
- printf("%s", prompt);
- fflush(stdout);
-
- line = qin.readLine();
- if (line.isNull())
- break;
-
- if (line.trimmed().isEmpty())
- continue;
-
- line += QLatin1Char('\n');
-
- QJSValue result = eng->evaluate(line, QLatin1String("typein"));
-
- fprintf(stderr, "%s\n", qPrintable(result.toString()));
-
- if (CommandInterface::wantsToQuit())
- break;
- }
-}
-
-int main(int argc, char *argv[])
-{
- QCoreApplication app(argc, argv);
- QScopedPointer<QJSEngine> eng(new QJSEngine());
- {
- QJSValue globalObject = eng->globalObject();
- QJSValue interface = eng->newQObject(new CommandInterface);
- globalObject.setProperty("qt", interface);
- }
-
- if (! *++argv) {
- interactive(eng.data());
- return EXIT_SUCCESS;
- }
-
- while (const char *arg = *argv++) {
- QString fileName = QString::fromLocal8Bit(arg);
-
- if (fileName == QLatin1String("-i")) {
- interactive(eng.data());
- break;
- }
-
- QString contents;
- int lineNumber = 1;
-
- if (fileName == QLatin1String("-")) {
- QTextStream stream(stdin, QFile::ReadOnly);
- contents = stream.readAll();
- } else {
- QFile file(fileName);
- if (file.open(QFile::ReadOnly)) {
- QTextStream stream(&file);
- contents = stream.readAll();
- file.close();
-
- // strip off #!/usr/bin/env qjs line
- if (contents.startsWith("#!")) {
- contents.remove(0, contents.indexOf("\n"));
- ++lineNumber;
- }
- }
- }
-
- if (contents.isEmpty())
- continue;
-
- QJSValue result = eng->evaluate(contents, fileName, lineNumber);
- if (result.isError()) {
- fprintf (stderr, " %s\n\n", qPrintable(result.toString()));
- return EXIT_FAILURE;
- }
- }
-
- return EXIT_SUCCESS;
-}
-
-#include <main.moc>
diff --git a/examples/qml/shell/shell.pro b/examples/qml/shell/shell.pro
deleted file mode 100644
index 9215108e6d..0000000000
--- a/examples/qml/shell/shell.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-QT = core qml
-
-win32: CONFIG += console
-mac:CONFIG -= app_bundle
-
-SOURCES += main.cpp
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/shell
-INSTALLS += target
diff --git a/examples/qml/tutorials/CMakeLists.txt b/examples/qml/tutorials/CMakeLists.txt
index 1a82ed75af..ecfec86a25 100644
--- a/examples/qml/tutorials/CMakeLists.txt
+++ b/examples/qml/tutorials/CMakeLists.txt
@@ -1,6 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-if(TARGET Qt::Quick)
+if(TARGET Qt6::Quick)
add_subdirectory(extending-qml)
endif()
diff --git a/examples/qml/tutorials/extending-qml-advanced/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/CMakeLists.txt
new file mode 100644
index 0000000000..4b47f135a3
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if(NOT ANDROID)
+ qt_internal_add_example(advanced1-Base-project)
+ qt_internal_add_example(advanced2-Inheritance-and-coercion)
+ qt_internal_add_example(advanced3-Default-properties)
+endif(NOT ANDROID)
+qt_internal_add_example(advanced4-Grouped-properties)
+qt_internal_add_example(advanced5-Attached-properties)
+qt_internal_add_example(advanced6-Property-value-source)
+qt_internal_add_example(advanced7-Extension-objects)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/CMakeLists.txt
new file mode 100644
index 0000000000..45dfbc0b12
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(baseproject LANGUAGES CXX)
+
+if (ANDROID)
+ message(FATAL_ERROR "This project cannot be built on Android.")
+endif()
+
+find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+qt_standard_project_setup()
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_executable(baseproject
+ birthdayparty.cpp birthdayparty.h
+ main.cpp
+ person.cpp person.h
+)
+
+set_target_properties(baseproject PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(baseproject PUBLIC
+ Qt6::Core
+ Qt6::Qml
+)
+
+qt_add_qml_module(baseproject
+ URI People
+ QML_FILES Main.qml
+)
+
+install(TARGETS baseproject
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET baseproject
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml
new file mode 100644
index 0000000000..c917d5b1f1
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ host: Person {
+ name: "Bob Jones"
+ shoeSize: 12
+ }
+ guests: [
+ Person { name: "Leo Hodges" },
+ Person { name: "Jack Smith" },
+ Person { name: "Anne Brown" }
+ ]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.pro b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.pro
new file mode 100644
index 0000000000..26c865fbba
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.pro
@@ -0,0 +1,13 @@
+QT = core qml
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = People
+QML_IMPORT_MAJOR_VERSION = 1
+
+SOURCES += main.cpp \
+ person.cpp \
+ birthdayparty.cpp
+HEADERS += person.h \
+ birthdayparty.h
+
+RESOURCES += baseproject.qrc
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.qrc
new file mode 100644
index 0000000000..b1eeb489e2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/qt/qml/People/">
+ <file>Main.qml</file>
+ <file alias="qmldir">qmldir.in</file>
+</qresource>
+</RCC>
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.cpp
new file mode 100644
index 0000000000..ad38f284e7
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+
+Person *BirthdayParty::host() const
+{
+ return m_host;
+}
+
+void BirthdayParty::setHost(Person *host)
+{
+ if (m_host != host) {
+ m_host = host;
+ emit hostChanged();
+ }
+}
+
+QQmlListProperty<Person> BirthdayParty::guests()
+{
+ return { this,
+ this,
+ &BirthdayParty::appendGuest,
+ &BirthdayParty::guestCount,
+ &BirthdayParty::guest,
+ &BirthdayParty::clearGuests,
+ &BirthdayParty::replaceGuest,
+ &BirthdayParty::removeLastGuest };
+}
+
+void BirthdayParty::appendGuest(Person *guest)
+{
+ m_guests.append(guest);
+ emit guestsChanged();
+}
+
+qsizetype BirthdayParty::guestCount() const
+{
+ return m_guests.count();
+}
+
+Person *BirthdayParty::guest(qsizetype index) const
+{
+ return m_guests.at(index);
+}
+
+void BirthdayParty::clearGuests()
+{
+ if (!m_guests.empty()) {
+ m_guests.clear();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::replaceGuest(qsizetype index, Person *guest)
+{
+ if (m_guests.size() > index) {
+ m_guests[index] = guest;
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::removeLastGuest()
+{
+ if (!m_guests.empty()) {
+ m_guests.removeLast();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->appendGuest(guest);
+}
+
+void BirthdayParty::clearGuests(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->clearGuests();
+}
+
+void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype index, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->replaceGuest(index, guest);
+}
+
+void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->removeLastGuest();
+}
+
+Person *BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype index)
+{
+ return static_cast<BirthdayParty *>(list->data)->guest(index);
+}
+
+qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list)
+{
+ return static_cast<BirthdayParty *>(list->data)->guestCount();
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h
new file mode 100644
index 0000000000..1a28ef2632
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BIRTHDAYPARTY_H
+#define BIRTHDAYPARTY_H
+
+#include "person.h"
+
+#include <QObject>
+#include <QQmlListProperty>
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
+ QML_ELEMENT
+public:
+ using QObject::QObject;
+
+ Person *host() const;
+ void setHost(Person *);
+
+ QQmlListProperty<Person> guests();
+ void appendGuest(Person *);
+ qsizetype guestCount() const;
+ Person *guest(qsizetype) const;
+ void clearGuests();
+ void replaceGuest(qsizetype, Person *);
+ void removeLastGuest();
+
+signals:
+ void hostChanged();
+ void guestsChanged();
+
+private:
+ static void appendGuest(QQmlListProperty<Person> *, Person *);
+ static qsizetype guestCount(QQmlListProperty<Person> *);
+ static Person *guest(QQmlListProperty<Person> *, qsizetype);
+ static void clearGuests(QQmlListProperty<Person> *);
+ static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *);
+ static void removeLastGuest(QQmlListProperty<Person> *);
+
+ Person *m_host = nullptr;
+ QList<Person *> m_guests;
+};
+
+#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.cpp
new file mode 100644
index 0000000000..eac94016d2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+#include "person.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QQmlComponent>
+#include <QQmlEngine>
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadFromModule("People", "Main");
+ std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };
+
+ if (party && party->host()) {
+ qInfo() << party->host()->name()
+ << "is having a birthday!\n"
+ "They are inviting:";
+ for (qsizetype ii = 0; ii < party->guestCount(); ++ii)
+ qInfo() << " " << party->guest(ii)->name();
+ return EXIT_SUCCESS;
+ }
+
+ qWarning() << component.errors();
+ return EXIT_FAILURE;
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.cpp
new file mode 100644
index 0000000000..f8f4b1d2f4
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "person.h"
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+}
+
+int Person::shoeSize() const
+{
+ return m_shoeSize;
+}
+
+void Person::setShoeSize(int shoeSize)
+{
+ if (m_shoeSize != shoeSize) {
+ m_shoeSize = shoeSize;
+ emit shoeSizeChanged();
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.h
new file mode 100644
index 0000000000..ded272626a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PERSON_H
+#define PERSON_H
+
+#include <QtQml/qqml.h>
+#include <QObject>
+
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL)
+ QML_ELEMENT
+public:
+ using QObject::QObject;
+
+ QString name() const;
+ void setName(const QString &);
+
+ int shoeSize() const;
+ void setShoeSize(int);
+
+signals:
+ void nameChanged();
+ void shoeSizeChanged();
+
+private:
+ QString m_name;
+ int m_shoeSize = 0;
+};
+
+#endif // PERSON_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/qmldir.in
new file mode 100644
index 0000000000..70cde3c958
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/qmldir.in
@@ -0,0 +1,4 @@
+module People
+typeinfo birthdayparty.qmltypes
+prefer :/qt/qml/People/
+Main 254.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/CMakeLists.txt
new file mode 100644
index 0000000000..a2fa4812eb
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(coercion LANGUAGES CXX)
+
+if (ANDROID)
+ message(FATAL_ERROR "This project cannot be built on Android.")
+endif()
+
+find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+qt_standard_project_setup()
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_executable(coercion
+ birthdayparty.cpp birthdayparty.h
+ main.cpp
+ person.cpp person.h
+)
+
+set_target_properties(coercion PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(coercion PUBLIC
+ Qt6::Core
+ Qt6::Qml
+)
+
+qt_add_qml_module(coercion
+ URI People
+ QML_FILES Main.qml
+)
+
+install(TARGETS coercion
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET coercion
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml
new file mode 100644
index 0000000000..1c3fe141ca
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ host: Boy {
+ name: "Bob Jones"
+ shoeSize: 12
+ }
+ guests: [
+ Boy { name: "Leo Hodges" },
+ Boy { name: "Jack Smith" },
+ Girl { name: "Anne Brown" }
+ ]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.cpp
new file mode 100644
index 0000000000..ad38f284e7
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+
+Person *BirthdayParty::host() const
+{
+ return m_host;
+}
+
+void BirthdayParty::setHost(Person *host)
+{
+ if (m_host != host) {
+ m_host = host;
+ emit hostChanged();
+ }
+}
+
+QQmlListProperty<Person> BirthdayParty::guests()
+{
+ return { this,
+ this,
+ &BirthdayParty::appendGuest,
+ &BirthdayParty::guestCount,
+ &BirthdayParty::guest,
+ &BirthdayParty::clearGuests,
+ &BirthdayParty::replaceGuest,
+ &BirthdayParty::removeLastGuest };
+}
+
+void BirthdayParty::appendGuest(Person *guest)
+{
+ m_guests.append(guest);
+ emit guestsChanged();
+}
+
+qsizetype BirthdayParty::guestCount() const
+{
+ return m_guests.count();
+}
+
+Person *BirthdayParty::guest(qsizetype index) const
+{
+ return m_guests.at(index);
+}
+
+void BirthdayParty::clearGuests()
+{
+ if (!m_guests.empty()) {
+ m_guests.clear();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::replaceGuest(qsizetype index, Person *guest)
+{
+ if (m_guests.size() > index) {
+ m_guests[index] = guest;
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::removeLastGuest()
+{
+ if (!m_guests.empty()) {
+ m_guests.removeLast();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->appendGuest(guest);
+}
+
+void BirthdayParty::clearGuests(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->clearGuests();
+}
+
+void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype index, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->replaceGuest(index, guest);
+}
+
+void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->removeLastGuest();
+}
+
+Person *BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype index)
+{
+ return static_cast<BirthdayParty *>(list->data)->guest(index);
+}
+
+qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list)
+{
+ return static_cast<BirthdayParty *>(list->data)->guestCount();
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.h
new file mode 100644
index 0000000000..f9a5c126e3
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BIRTHDAYPARTY_H
+#define BIRTHDAYPARTY_H
+
+#include "person.h"
+
+#include <QObject>
+#include <QQmlListProperty>
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
+ QML_ELEMENT
+public:
+ using QObject::QObject;
+
+ Person *host() const;
+ void setHost(Person *);
+
+ QQmlListProperty<Person> guests();
+ void appendGuest(Person *);
+ qsizetype guestCount() const;
+ Person *guest(qsizetype) const;
+ void clearGuests();
+ void replaceGuest(qsizetype, Person *);
+ void removeLastGuest();
+
+signals:
+ void hostChanged();
+ void guestsChanged();
+
+private:
+ static void appendGuest(QQmlListProperty<Person> *list, Person *);
+ static qsizetype guestCount(QQmlListProperty<Person> *);
+ static Person *guest(QQmlListProperty<Person> *, qsizetype);
+ static void clearGuests(QQmlListProperty<Person> *);
+ static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *);
+ static void removeLastGuest(QQmlListProperty<Person> *);
+
+ Person *m_host = nullptr;
+ QList<Person *> m_guests;
+};
+
+#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.pro b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.pro
new file mode 100644
index 0000000000..1ba8194965
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.pro
@@ -0,0 +1,12 @@
+QT = core qml
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = People
+QML_IMPORT_MAJOR_VERSION = 1
+
+SOURCES += main.cpp \
+ person.cpp \
+ birthdayparty.cpp
+HEADERS += person.h \
+ birthdayparty.h
+RESOURCES += coercion.qrc
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.qrc
new file mode 100644
index 0000000000..b1eeb489e2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/qt/qml/People/">
+ <file>Main.qml</file>
+ <file alias="qmldir">qmldir.in</file>
+</qresource>
+</RCC>
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.cpp
new file mode 100644
index 0000000000..fa26448f44
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+#include "person.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QQmlComponent>
+#include <QQmlEngine>
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadFromModule("People", "Main");
+ std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };
+
+ if (party && party->host()) {
+ qInfo() << party->host()->name() << "is having a birthday!";
+
+ if (qobject_cast<Boy *>(party->host()))
+ qInfo() << "He is inviting:";
+ else
+ qInfo() << "She is inviting:";
+
+ for (qsizetype ii = 0; ii < party->guestCount(); ++ii)
+ qInfo() << " " << party->guest(ii)->name();
+
+ return EXIT_SUCCESS;
+ }
+
+ qWarning() << component.errors();
+ return EXIT_FAILURE;
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.cpp
new file mode 100644
index 0000000000..f8f4b1d2f4
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "person.h"
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+}
+
+int Person::shoeSize() const
+{
+ return m_shoeSize;
+}
+
+void Person::setShoeSize(int shoeSize)
+{
+ if (m_shoeSize != shoeSize) {
+ m_shoeSize = shoeSize;
+ emit shoeSizeChanged();
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
new file mode 100644
index 0000000000..99fc9209ab
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PERSON_H
+#define PERSON_H
+
+#include <QtQml/qqml.h>
+#include <QObject>
+
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL)
+ QML_ELEMENT
+ QML_UNCREATABLE("Person is an abstract base class.")
+public:
+ using QObject::QObject;
+
+ QString name() const;
+ void setName(const QString &);
+
+ int shoeSize() const;
+ void setShoeSize(int);
+
+signals:
+ void nameChanged();
+ void shoeSizeChanged();
+
+private:
+ QString m_name;
+ int m_shoeSize = 0;
+};
+
+class Boy : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+class Girl : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+#endif // PERSON_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/qmldir.in
new file mode 100644
index 0000000000..3ccd68f7cc
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/qmldir.in
@@ -0,0 +1,4 @@
+module People
+typeinfo coercion.qmltypes
+prefer :/qt/qml/People/
+Main 254.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/CMakeLists.txt
new file mode 100644
index 0000000000..0682e487c8
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(default LANGUAGES CXX)
+
+if (ANDROID)
+ message(FATAL_ERROR "This project cannot be built on Android.")
+endif()
+
+find_package(Qt6 REQUIRED COMPONENTS Core Qml)
+qt_standard_project_setup()
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_executable(default
+ birthdayparty.cpp birthdayparty.h
+ main.cpp
+ person.cpp person.h
+)
+
+set_target_properties(default PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(default PUBLIC
+ Qt6::Core
+ Qt6::Qml
+)
+
+qt_add_qml_module(default
+ URI People
+ QML_FILES Main.qml
+)
+
+install(TARGETS default
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET default
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml
new file mode 100644
index 0000000000..1070427cb0
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ host: Boy {
+ name: "Bob Jones"
+ shoeSize: 12
+ }
+
+ Boy { name: "Leo Hodges" }
+ Boy { name: "Jack Smith" }
+ Girl { name: "Anne Brown" }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.cpp
new file mode 100644
index 0000000000..ad38f284e7
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+
+Person *BirthdayParty::host() const
+{
+ return m_host;
+}
+
+void BirthdayParty::setHost(Person *host)
+{
+ if (m_host != host) {
+ m_host = host;
+ emit hostChanged();
+ }
+}
+
+QQmlListProperty<Person> BirthdayParty::guests()
+{
+ return { this,
+ this,
+ &BirthdayParty::appendGuest,
+ &BirthdayParty::guestCount,
+ &BirthdayParty::guest,
+ &BirthdayParty::clearGuests,
+ &BirthdayParty::replaceGuest,
+ &BirthdayParty::removeLastGuest };
+}
+
+void BirthdayParty::appendGuest(Person *guest)
+{
+ m_guests.append(guest);
+ emit guestsChanged();
+}
+
+qsizetype BirthdayParty::guestCount() const
+{
+ return m_guests.count();
+}
+
+Person *BirthdayParty::guest(qsizetype index) const
+{
+ return m_guests.at(index);
+}
+
+void BirthdayParty::clearGuests()
+{
+ if (!m_guests.empty()) {
+ m_guests.clear();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::replaceGuest(qsizetype index, Person *guest)
+{
+ if (m_guests.size() > index) {
+ m_guests[index] = guest;
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::removeLastGuest()
+{
+ if (!m_guests.empty()) {
+ m_guests.removeLast();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->appendGuest(guest);
+}
+
+void BirthdayParty::clearGuests(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->clearGuests();
+}
+
+void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype index, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->replaceGuest(index, guest);
+}
+
+void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->removeLastGuest();
+}
+
+Person *BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype index)
+{
+ return static_cast<BirthdayParty *>(list->data)->guest(index);
+}
+
+qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list)
+{
+ return static_cast<BirthdayParty *>(list->data)->guestCount();
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.h
new file mode 100644
index 0000000000..4d7e61a487
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BIRTHDAYPARTY_H
+#define BIRTHDAYPARTY_H
+
+#include "person.h"
+
+#include <QObject>
+#include <QQmlListProperty>
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
+ Q_CLASSINFO("DefaultProperty", "guests")
+ QML_ELEMENT
+public:
+ using QObject::QObject;
+
+ Person *host() const;
+ void setHost(Person *);
+
+ QQmlListProperty<Person> guests();
+ void appendGuest(Person *);
+ qsizetype guestCount() const;
+ Person *guest(qsizetype) const;
+ void clearGuests();
+ void replaceGuest(qsizetype, Person *);
+ void removeLastGuest();
+
+signals:
+ void hostChanged();
+ void guestsChanged();
+
+private:
+ static void appendGuest(QQmlListProperty<Person> *list, Person *);
+ static qsizetype guestCount(QQmlListProperty<Person> *);
+ static Person *guest(QQmlListProperty<Person> *, qsizetype);
+ static void clearGuests(QQmlListProperty<Person> *);
+ static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *);
+ static void removeLastGuest(QQmlListProperty<Person> *);
+
+ Person *m_host = nullptr;
+ QList<Person *> m_guests;
+};
+
+#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.pro b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.pro
new file mode 100644
index 0000000000..65dd27213b
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.pro
@@ -0,0 +1,12 @@
+QT = core qml
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = People
+QML_IMPORT_MAJOR_VERSION = 1
+
+SOURCES += main.cpp \
+ person.cpp \
+ birthdayparty.cpp
+HEADERS += person.h \
+ birthdayparty.h
+RESOURCES += default.qrc
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.qrc
new file mode 100644
index 0000000000..b1eeb489e2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/qt/qml/People/">
+ <file>Main.qml</file>
+ <file alias="qmldir">qmldir.in</file>
+</qresource>
+</RCC>
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.cpp
new file mode 100644
index 0000000000..fa26448f44
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+#include "person.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QQmlComponent>
+#include <QQmlEngine>
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadFromModule("People", "Main");
+ std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };
+
+ if (party && party->host()) {
+ qInfo() << party->host()->name() << "is having a birthday!";
+
+ if (qobject_cast<Boy *>(party->host()))
+ qInfo() << "He is inviting:";
+ else
+ qInfo() << "She is inviting:";
+
+ for (qsizetype ii = 0; ii < party->guestCount(); ++ii)
+ qInfo() << " " << party->guest(ii)->name();
+
+ return EXIT_SUCCESS;
+ }
+
+ qWarning() << component.errors();
+ return EXIT_FAILURE;
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.cpp
new file mode 100644
index 0000000000..f8f4b1d2f4
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "person.h"
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+}
+
+int Person::shoeSize() const
+{
+ return m_shoeSize;
+}
+
+void Person::setShoeSize(int shoeSize)
+{
+ if (m_shoeSize != shoeSize) {
+ m_shoeSize = shoeSize;
+ emit shoeSizeChanged();
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.h
new file mode 100644
index 0000000000..99fc9209ab
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PERSON_H
+#define PERSON_H
+
+#include <QtQml/qqml.h>
+#include <QObject>
+
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL)
+ QML_ELEMENT
+ QML_UNCREATABLE("Person is an abstract base class.")
+public:
+ using QObject::QObject;
+
+ QString name() const;
+ void setName(const QString &);
+
+ int shoeSize() const;
+ void setShoeSize(int);
+
+signals:
+ void nameChanged();
+ void shoeSizeChanged();
+
+private:
+ QString m_name;
+ int m_shoeSize = 0;
+};
+
+class Boy : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+class Girl : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+#endif // PERSON_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/qmldir.in
new file mode 100644
index 0000000000..da1b995d64
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/qmldir.in
@@ -0,0 +1,4 @@
+module People
+typeinfo default.qmltypes
+prefer :/qt/qml/People/
+Main 254.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/CMakeLists.txt
new file mode 100644
index 0000000000..8f66d1ad07
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(grouped LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Qml Gui)
+qt_standard_project_setup()
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_executable(grouped
+ birthdayparty.cpp birthdayparty.h
+ main.cpp
+ person.cpp person.h
+)
+
+set_target_properties(grouped PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(grouped PUBLIC
+ Qt6::Core
+ Qt6::Qml
+ Qt6::Gui
+)
+
+qt_add_qml_module(grouped
+ URI People
+ QML_FILES Main.qml
+ DEPENDENCIES
+ QtQuick
+)
+
+install(TARGETS grouped
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET grouped
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml
new file mode 100644
index 0000000000..27951b5ea8
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+import QtQuick // For QColor
+
+BirthdayParty {
+ host: Boy {
+ name: "Bob Jones"
+ shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 }
+ }
+
+ Boy {
+ name: "Leo Hodges"
+ shoe { size: 10; color: "black"; brand: "Thebok"; price: 59.95 }
+ }
+ Boy {
+ name: "Jack Smith"
+ shoe {
+ size: 8
+ color: "blue"
+ brand: "Luma"
+ price: 19.95
+ }
+ }
+ Girl {
+ name: "Anne Brown"
+ shoe.size: 7
+ shoe.color: "red"
+ shoe.brand: "Job Macobs"
+ shoe.price: 99.99
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.cpp
new file mode 100644
index 0000000000..ad38f284e7
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+
+Person *BirthdayParty::host() const
+{
+ return m_host;
+}
+
+void BirthdayParty::setHost(Person *host)
+{
+ if (m_host != host) {
+ m_host = host;
+ emit hostChanged();
+ }
+}
+
+QQmlListProperty<Person> BirthdayParty::guests()
+{
+ return { this,
+ this,
+ &BirthdayParty::appendGuest,
+ &BirthdayParty::guestCount,
+ &BirthdayParty::guest,
+ &BirthdayParty::clearGuests,
+ &BirthdayParty::replaceGuest,
+ &BirthdayParty::removeLastGuest };
+}
+
+void BirthdayParty::appendGuest(Person *guest)
+{
+ m_guests.append(guest);
+ emit guestsChanged();
+}
+
+qsizetype BirthdayParty::guestCount() const
+{
+ return m_guests.count();
+}
+
+Person *BirthdayParty::guest(qsizetype index) const
+{
+ return m_guests.at(index);
+}
+
+void BirthdayParty::clearGuests()
+{
+ if (!m_guests.empty()) {
+ m_guests.clear();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::replaceGuest(qsizetype index, Person *guest)
+{
+ if (m_guests.size() > index) {
+ m_guests[index] = guest;
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::removeLastGuest()
+{
+ if (!m_guests.empty()) {
+ m_guests.removeLast();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->appendGuest(guest);
+}
+
+void BirthdayParty::clearGuests(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->clearGuests();
+}
+
+void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype index, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->replaceGuest(index, guest);
+}
+
+void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->removeLastGuest();
+}
+
+Person *BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype index)
+{
+ return static_cast<BirthdayParty *>(list->data)->guest(index);
+}
+
+qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list)
+{
+ return static_cast<BirthdayParty *>(list->data)->guestCount();
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.h
new file mode 100644
index 0000000000..4d7e61a487
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BIRTHDAYPARTY_H
+#define BIRTHDAYPARTY_H
+
+#include "person.h"
+
+#include <QObject>
+#include <QQmlListProperty>
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
+ Q_CLASSINFO("DefaultProperty", "guests")
+ QML_ELEMENT
+public:
+ using QObject::QObject;
+
+ Person *host() const;
+ void setHost(Person *);
+
+ QQmlListProperty<Person> guests();
+ void appendGuest(Person *);
+ qsizetype guestCount() const;
+ Person *guest(qsizetype) const;
+ void clearGuests();
+ void replaceGuest(qsizetype, Person *);
+ void removeLastGuest();
+
+signals:
+ void hostChanged();
+ void guestsChanged();
+
+private:
+ static void appendGuest(QQmlListProperty<Person> *list, Person *);
+ static qsizetype guestCount(QQmlListProperty<Person> *);
+ static Person *guest(QQmlListProperty<Person> *, qsizetype);
+ static void clearGuests(QQmlListProperty<Person> *);
+ static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *);
+ static void removeLastGuest(QQmlListProperty<Person> *);
+
+ Person *m_host = nullptr;
+ QList<Person *> m_guests;
+};
+
+#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.pro b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.pro
new file mode 100644
index 0000000000..52e2937edf
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.pro
@@ -0,0 +1,12 @@
+QT += qml
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = People
+QML_IMPORT_MAJOR_VERSION = 1
+
+SOURCES += main.cpp \
+ person.cpp \
+ birthdayparty.cpp
+HEADERS += person.h \
+ birthdayparty.h
+RESOURCES += grouped.qrc
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.qrc
new file mode 100644
index 0000000000..b1eeb489e2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/qt/qml/People/">
+ <file>Main.qml</file>
+ <file alias="qmldir">qmldir.in</file>
+</qresource>
+</RCC>
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.cpp
new file mode 100644
index 0000000000..0721d496f0
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.cpp
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+#include "person.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QQmlComponent>
+#include <QQmlEngine>
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadFromModule("People", "Main");
+ std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };
+
+ if (party && party->host()) {
+ qInfo() << party->host()->name() << "is having a birthday!";
+
+ if (qobject_cast<Boy *>(party->host()))
+ qInfo() << "He is inviting:";
+ else
+ qInfo() << "She is inviting:";
+
+ Person *bestShoe = nullptr;
+ for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
+ Person *guest = party->guest(ii);
+ qInfo() << " " << guest->name();
+
+ if (!bestShoe || bestShoe->shoe()->price() < guest->shoe()->price())
+ bestShoe = guest;
+ }
+ if (bestShoe)
+ qInfo() << bestShoe->name() << "is wearing the best shoes!";
+
+ return EXIT_SUCCESS;
+ }
+
+ qWarning() << component.errors();
+ return EXIT_FAILURE;
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.cpp
new file mode 100644
index 0000000000..53cec6b192
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.cpp
@@ -0,0 +1,96 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "person.h"
+
+Person::Person(QObject *parent) : QObject(parent)
+{
+ m_shoe = new ShoeDescription(this);
+}
+
+int ShoeDescription::size() const
+{
+ return m_size;
+}
+
+void ShoeDescription::setSize(int size)
+{
+ if (m_size != size) {
+ m_size = size;
+ emit shoeChanged();
+ }
+}
+
+QColor ShoeDescription::color() const
+{
+ return m_color;
+}
+
+void ShoeDescription::setColor(const QColor &color)
+{
+ if (m_color != color) {
+ m_color = color;
+ emit shoeChanged();
+ }
+}
+
+QString ShoeDescription::brand() const
+{
+ return m_brand;
+}
+
+void ShoeDescription::setBrand(const QString &brand)
+{
+ if (m_brand != brand) {
+ m_brand = brand;
+ emit shoeChanged();
+ }
+}
+
+qreal ShoeDescription::price() const
+{
+ return m_price;
+}
+
+void ShoeDescription::setPrice(qreal price)
+{
+ if (m_price != price) {
+ m_price = price;
+ emit shoeChanged();
+ }
+}
+
+bool ShoeDescription::operatorEqualsImpl(const ShoeDescription &lhs, const ShoeDescription &rhs)
+{
+ return lhs.m_size == rhs.m_size && lhs.m_color == rhs.m_color && lhs.m_brand == rhs.m_brand
+ && lhs.m_price == rhs.m_price;
+}
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+}
+
+ShoeDescription *Person::shoe() const
+{
+ return m_shoe;
+}
+
+void Person::setShoe(ShoeDescription *shoe)
+{
+ if (!shoe)
+ return;
+
+ if (*m_shoe != *shoe) {
+ m_shoe = shoe;
+ emit shoeChanged();
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h
new file mode 100644
index 0000000000..4f040e491b
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PERSON_H
+#define PERSON_H
+
+#include <QtQml/qqml.h>
+#include <QColor>
+#include <QObject>
+
+class ShoeDescription : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged FINAL)
+ QML_ANONYMOUS
+public:
+ using QObject::QObject;
+
+ int size() const;
+ void setSize(int);
+
+ QColor color() const;
+ void setColor(const QColor &);
+
+ QString brand() const;
+ void setBrand(const QString &);
+
+ qreal price() const;
+ void setPrice(qreal);
+
+ friend bool operator==(const ShoeDescription &lhs, const ShoeDescription &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+ friend bool operator!=(const ShoeDescription &lhs, const ShoeDescription &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+signals:
+ void shoeChanged();
+
+private:
+ static bool operatorEqualsImpl(const ShoeDescription &, const ShoeDescription &);
+
+ int m_size = 0;
+ QColor m_color;
+ QString m_brand;
+ qreal m_price = 0;
+};
+
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe NOTIFY shoeChanged FINAL)
+ QML_ELEMENT
+ QML_UNCREATABLE("Person is an abstract base class.")
+public:
+ using QObject::QObject;
+
+ Person(QObject *parent = nullptr);
+
+ QString name() const;
+ void setName(const QString &);
+
+ ShoeDescription *shoe() const;
+ void setShoe(ShoeDescription *shoe);
+
+signals:
+ void nameChanged();
+ void shoeChanged();
+
+private:
+ QString m_name;
+ ShoeDescription *m_shoe = nullptr;
+};
+
+class Boy : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+class Girl : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+#endif // PERSON_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/qmldir.in
new file mode 100644
index 0000000000..2e634e41af
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/qmldir.in
@@ -0,0 +1,5 @@
+module People
+typeinfo grouped.qmltypes
+prefer :/qt/qml/People/
+Main 254.0 Main.qml
+depends QtQuick
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/CMakeLists.txt
new file mode 100644
index 0000000000..c7b30376b6
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(attached LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick)
+qt_standard_project_setup()
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_executable(attached
+ birthdayparty.cpp birthdayparty.h
+ main.cpp
+ person.cpp person.h
+)
+
+set_target_properties(attached PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(attached PUBLIC
+ Qt6::Core
+ Qt6::Qml
+ Qt6::Quick
+)
+
+qt_add_qml_module(attached
+ URI People
+ QML_FILES Main.qml
+ DEPENDENCIES
+ QtQuick
+)
+
+install(TARGETS attached
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET attached
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml
new file mode 100644
index 0000000000..8175eae209
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+import QtQuick // For QColor
+
+BirthdayParty {
+ Boy {
+ name: "Robert Campbell"
+ BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd")
+ }
+
+ Boy {
+ name: "Leo Hodges"
+ shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
+ BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd")
+ }
+
+ host: Boy {
+ name: "Jack Smith"
+ shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.pro b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.pro
new file mode 100644
index 0000000000..ab154eb48b
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.pro
@@ -0,0 +1,12 @@
+QT += qml quick
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = People
+QML_IMPORT_MAJOR_VERSION = 1
+
+SOURCES += main.cpp \
+ person.cpp \
+ birthdayparty.cpp
+HEADERS += person.h \
+ birthdayparty.h
+RESOURCES += attached.qrc
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.qrc
new file mode 100644
index 0000000000..b1eeb489e2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/qt/qml/People/">
+ <file>Main.qml</file>
+ <file alias="qmldir">qmldir.in</file>
+</qresource>
+</RCC>
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.cpp
new file mode 100644
index 0000000000..0379a7accf
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.cpp
@@ -0,0 +1,117 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+
+QDate BirthdayPartyAttached::rsvp() const
+{
+ return m_rsvp;
+}
+
+void BirthdayPartyAttached::setRsvp(QDate rsvpDate)
+{
+ if (m_rsvp != rsvpDate) {
+ m_rsvp = rsvpDate;
+ emit rsvpChanged();
+ }
+}
+
+Person *BirthdayParty::host() const
+{
+ return m_host;
+}
+
+void BirthdayParty::setHost(Person *host)
+{
+ if (m_host != host) {
+ m_host = host;
+ emit hostChanged();
+ }
+}
+
+QQmlListProperty<Person> BirthdayParty::guests()
+{
+ return { this,
+ this,
+ &BirthdayParty::appendGuest,
+ &BirthdayParty::guestCount,
+ &BirthdayParty::guest,
+ &BirthdayParty::clearGuests,
+ &BirthdayParty::replaceGuest,
+ &BirthdayParty::removeLastGuest };
+}
+
+void BirthdayParty::appendGuest(Person *guest)
+{
+ m_guests.append(guest);
+ emit guestsChanged();
+}
+
+qsizetype BirthdayParty::guestCount() const
+{
+ return m_guests.count();
+}
+
+Person *BirthdayParty::guest(qsizetype index) const
+{
+ return m_guests.at(index);
+}
+
+void BirthdayParty::clearGuests()
+{
+ if (!m_guests.empty()) {
+ m_guests.clear();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::replaceGuest(qsizetype index, Person *guest)
+{
+ if (m_guests.size() > index) {
+ m_guests[index] = guest;
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::removeLastGuest()
+{
+ if (!m_guests.empty()) {
+ m_guests.removeLast();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->appendGuest(guest);
+}
+
+void BirthdayParty::clearGuests(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->clearGuests();
+}
+
+void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype index, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->replaceGuest(index, guest);
+}
+
+void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->removeLastGuest();
+}
+
+Person *BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype index)
+{
+ return static_cast<BirthdayParty *>(list->data)->guest(index);
+}
+
+qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list)
+{
+ return static_cast<BirthdayParty *>(list->data)->guestCount();
+}
+
+BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
+{
+ return new BirthdayPartyAttached(object);
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h
new file mode 100644
index 0000000000..1b2503895a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BIRTHDAYPARTY_H
+#define BIRTHDAYPARTY_H
+
+#include "person.h"
+
+#include <QDate>
+#include <QObject>
+#include <qqml.h>
+
+class BirthdayPartyAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL)
+ QML_ANONYMOUS
+public:
+ using QObject::QObject;
+
+ QDate rsvp() const;
+ void setRsvp(QDate);
+
+signals:
+ void rsvpChanged();
+
+private:
+ QDate m_rsvp;
+};
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
+ Q_CLASSINFO("DefaultProperty", "guests")
+ QML_ELEMENT
+ QML_ATTACHED(BirthdayPartyAttached)
+
+public:
+ using QObject::QObject;
+
+ Person *host() const;
+ void setHost(Person *);
+
+ QQmlListProperty<Person> guests();
+ void appendGuest(Person *);
+ qsizetype guestCount() const;
+ Person *guest(qsizetype) const;
+ void clearGuests();
+ void replaceGuest(qsizetype, Person *);
+ void removeLastGuest();
+
+ static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
+
+signals:
+ void hostChanged();
+ void guestsChanged();
+
+private:
+ static void appendGuest(QQmlListProperty<Person> *list, Person *);
+ static qsizetype guestCount(QQmlListProperty<Person> *);
+ static Person *guest(QQmlListProperty<Person> *, qsizetype);
+ static void clearGuests(QQmlListProperty<Person> *);
+ static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *);
+ static void removeLastGuest(QQmlListProperty<Person> *);
+
+ Person *m_host = nullptr;
+ QList<Person *> m_guests;
+};
+
+#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp
new file mode 100644
index 0000000000..09691f3b6a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+#include "person.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QQmlComponent>
+#include <QQmlEngine>
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadFromModule("People", "Main");
+ std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };
+
+ if (party && party->host()) {
+ qInfo() << party->host()->name() << "is having a birthday!";
+
+ if (qobject_cast<Boy *>(party->host()))
+ qInfo() << "He is inviting:";
+ else
+ qInfo() << "She is inviting:";
+
+ for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
+ Person *guest = party->guest(ii);
+
+ QDate rsvpDate;
+ QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
+
+ if (attached)
+ rsvpDate = attached->property("rsvp").toDate();
+ if (rsvpDate.isNull())
+ qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd";
+ else
+ qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString();
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+ qWarning() << component.errors();
+ return EXIT_FAILURE;
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.cpp
new file mode 100644
index 0000000000..53cec6b192
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.cpp
@@ -0,0 +1,96 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "person.h"
+
+Person::Person(QObject *parent) : QObject(parent)
+{
+ m_shoe = new ShoeDescription(this);
+}
+
+int ShoeDescription::size() const
+{
+ return m_size;
+}
+
+void ShoeDescription::setSize(int size)
+{
+ if (m_size != size) {
+ m_size = size;
+ emit shoeChanged();
+ }
+}
+
+QColor ShoeDescription::color() const
+{
+ return m_color;
+}
+
+void ShoeDescription::setColor(const QColor &color)
+{
+ if (m_color != color) {
+ m_color = color;
+ emit shoeChanged();
+ }
+}
+
+QString ShoeDescription::brand() const
+{
+ return m_brand;
+}
+
+void ShoeDescription::setBrand(const QString &brand)
+{
+ if (m_brand != brand) {
+ m_brand = brand;
+ emit shoeChanged();
+ }
+}
+
+qreal ShoeDescription::price() const
+{
+ return m_price;
+}
+
+void ShoeDescription::setPrice(qreal price)
+{
+ if (m_price != price) {
+ m_price = price;
+ emit shoeChanged();
+ }
+}
+
+bool ShoeDescription::operatorEqualsImpl(const ShoeDescription &lhs, const ShoeDescription &rhs)
+{
+ return lhs.m_size == rhs.m_size && lhs.m_color == rhs.m_color && lhs.m_brand == rhs.m_brand
+ && lhs.m_price == rhs.m_price;
+}
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+}
+
+ShoeDescription *Person::shoe() const
+{
+ return m_shoe;
+}
+
+void Person::setShoe(ShoeDescription *shoe)
+{
+ if (!shoe)
+ return;
+
+ if (*m_shoe != *shoe) {
+ m_shoe = shoe;
+ emit shoeChanged();
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.h
new file mode 100644
index 0000000000..4f040e491b
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PERSON_H
+#define PERSON_H
+
+#include <QtQml/qqml.h>
+#include <QColor>
+#include <QObject>
+
+class ShoeDescription : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged FINAL)
+ QML_ANONYMOUS
+public:
+ using QObject::QObject;
+
+ int size() const;
+ void setSize(int);
+
+ QColor color() const;
+ void setColor(const QColor &);
+
+ QString brand() const;
+ void setBrand(const QString &);
+
+ qreal price() const;
+ void setPrice(qreal);
+
+ friend bool operator==(const ShoeDescription &lhs, const ShoeDescription &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+ friend bool operator!=(const ShoeDescription &lhs, const ShoeDescription &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+signals:
+ void shoeChanged();
+
+private:
+ static bool operatorEqualsImpl(const ShoeDescription &, const ShoeDescription &);
+
+ int m_size = 0;
+ QColor m_color;
+ QString m_brand;
+ qreal m_price = 0;
+};
+
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe NOTIFY shoeChanged FINAL)
+ QML_ELEMENT
+ QML_UNCREATABLE("Person is an abstract base class.")
+public:
+ using QObject::QObject;
+
+ Person(QObject *parent = nullptr);
+
+ QString name() const;
+ void setName(const QString &);
+
+ ShoeDescription *shoe() const;
+ void setShoe(ShoeDescription *shoe);
+
+signals:
+ void nameChanged();
+ void shoeChanged();
+
+private:
+ QString m_name;
+ ShoeDescription *m_shoe = nullptr;
+};
+
+class Boy : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+class Girl : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+#endif // PERSON_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/qmldir.in
new file mode 100644
index 0000000000..1038298c01
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/qmldir.in
@@ -0,0 +1,5 @@
+module People
+typeinfo attached.qmltypes
+prefer :/qt/qml/People/
+Main 254.0 Main.qml
+depends QtQuick
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/CMakeLists.txt
new file mode 100644
index 0000000000..e6aa49e5cb
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(valuesource LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick)
+qt_standard_project_setup()
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_executable(valuesource
+ birthdayparty.cpp birthdayparty.h
+ happybirthdaysong.cpp happybirthdaysong.h
+ main.cpp
+ person.cpp person.h
+)
+
+set_target_properties(valuesource PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(valuesource PUBLIC
+ Qt6::Core
+ Qt6::Qml
+ Qt6::Quick
+)
+
+qt_add_qml_module(valuesource
+ URI People
+ QML_FILES Main.qml
+ DEPENDENCIES
+ QtQuick
+)
+
+install(TARGETS valuesource
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET valuesource
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml
new file mode 100644
index 0000000000..7f79d0a0c4
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+import QtQuick // For QColor
+
+BirthdayParty {
+ id: party
+ HappyBirthdaySong on announcement {
+ name: party.host.name
+ }
+
+ onPartyStarted: (time) => { console.log("This party started rockin' at " + time); }
+
+
+ host: Boy {
+ name: "Bob Jones"
+ shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 }
+ }
+
+ Boy {
+ name: "Leo Hodges"
+ BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd")
+ shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
+ }
+ Boy {
+ name: "Jack Smith"
+ shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
+ }
+ Girl {
+ name: "Anne Brown"
+ BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd")
+ shoe.size: 7
+ shoe.color: "red"
+ shoe.brand: "Marc Jacobs"
+ shoe.price: 99.99
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.cpp
new file mode 100644
index 0000000000..b14f7ef315
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.cpp
@@ -0,0 +1,137 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+
+QDate BirthdayPartyAttached::rsvp() const
+{
+ return m_rsvp;
+}
+
+void BirthdayPartyAttached::setRsvp(QDate rsvpDate)
+{
+ if (m_rsvp != rsvpDate) {
+ m_rsvp = rsvpDate;
+ emit rsvpChanged();
+ }
+}
+
+Person *BirthdayParty::host() const
+{
+ return m_host;
+}
+
+void BirthdayParty::setHost(Person *host)
+{
+ if (m_host != host) {
+ m_host = host;
+ emit hostChanged();
+ }
+}
+
+QQmlListProperty<Person> BirthdayParty::guests()
+{
+ return { this,
+ this,
+ &BirthdayParty::appendGuest,
+ &BirthdayParty::guestCount,
+ &BirthdayParty::guest,
+ &BirthdayParty::clearGuests,
+ &BirthdayParty::replaceGuest,
+ &BirthdayParty::removeLastGuest };
+}
+
+void BirthdayParty::appendGuest(Person *guest)
+{
+ m_guests.append(guest);
+ emit guestsChanged();
+}
+
+qsizetype BirthdayParty::guestCount() const
+{
+ return m_guests.count();
+}
+
+Person *BirthdayParty::guest(qsizetype index) const
+{
+ return m_guests.at(index);
+}
+
+void BirthdayParty::clearGuests()
+{
+ if (!m_guests.empty()) {
+ m_guests.clear();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::replaceGuest(qsizetype index, Person *guest)
+{
+ if (m_guests.size() > index) {
+ m_guests[index] = guest;
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::removeLastGuest()
+{
+ if (!m_guests.empty()) {
+ m_guests.removeLast();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->appendGuest(guest);
+}
+
+void BirthdayParty::clearGuests(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->clearGuests();
+}
+
+void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype index, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->replaceGuest(index, guest);
+}
+
+void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->removeLastGuest();
+}
+
+Person *BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype index)
+{
+ return static_cast<BirthdayParty *>(list->data)->guest(index);
+}
+
+qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list)
+{
+ return static_cast<BirthdayParty *>(list->data)->guestCount();
+}
+
+void BirthdayParty::startParty()
+{
+ QDateTime time = QDateTime::currentDateTime();
+ emit partyStarted(time);
+}
+
+QString BirthdayParty::announcement() const
+{
+ return m_announcement;
+}
+
+void BirthdayParty::setAnnouncement(const QString &announcement)
+{
+ if (m_announcement != announcement) {
+ m_announcement = announcement;
+ emit announcementChanged();
+ }
+ qInfo().noquote() << announcement;
+}
+
+BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
+{
+ return new BirthdayPartyAttached(object);
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.h
new file mode 100644
index 0000000000..799a3fa969
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BIRTHDAYPARTY_H
+#define BIRTHDAYPARTY_H
+
+#include "person.h"
+
+#include <QDate>
+#include <QDebug>
+#include <QObject>
+#include <QQmlListProperty>
+#include <qqml.h>
+
+class BirthdayPartyAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL)
+ QML_ANONYMOUS
+public:
+ using QObject::QObject;
+
+ QDate rsvp() const;
+ void setRsvp(QDate);
+
+signals:
+ void rsvpChanged();
+
+private:
+ QDate m_rsvp;
+};
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
+ Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement NOTIFY announcementChanged FINAL)
+ Q_CLASSINFO("DefaultProperty", "guests")
+ QML_ELEMENT
+ QML_ATTACHED(BirthdayPartyAttached)
+public:
+ using QObject::QObject;
+
+ Person *host() const;
+ void setHost(Person *);
+
+ QString announcement() const;
+ void setAnnouncement(const QString &);
+
+ QQmlListProperty<Person> guests();
+ void appendGuest(Person *);
+ qsizetype guestCount() const;
+ Person *guest(qsizetype) const;
+ void clearGuests();
+ void replaceGuest(qsizetype, Person *);
+ void removeLastGuest();
+
+ static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
+
+ void startParty();
+
+signals:
+ void hostChanged();
+ void guestsChanged();
+ void partyStarted(QDateTime time);
+ void announcementChanged();
+
+private:
+ static void appendGuest(QQmlListProperty<Person> *, Person *);
+ static qsizetype guestCount(QQmlListProperty<Person> *);
+ static Person *guest(QQmlListProperty<Person> *, qsizetype);
+ static void clearGuests(QQmlListProperty<Person> *);
+ static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *);
+ static void removeLastGuest(QQmlListProperty<Person> *);
+
+ Person *m_host = nullptr;
+ QList<Person *> m_guests;
+ QString m_announcement;
+};
+
+#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.cpp
new file mode 100644
index 0000000000..7a756a4a71
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.cpp
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "happybirthdaysong.h"
+
+#include <QTimer>
+
+HappyBirthdaySong::HappyBirthdaySong(QObject *parent) : QObject(parent)
+{
+ auto *timer = new QTimer(this);
+ QObject::connect(timer, &QTimer::timeout, this, &HappyBirthdaySong::advance);
+ timer->start(1000);
+}
+
+void HappyBirthdaySong::setTarget(const QQmlProperty &target)
+{
+ m_target = target;
+}
+
+QString HappyBirthdaySong::name() const
+{
+ return m_name;
+}
+
+void HappyBirthdaySong::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+
+ m_lyrics.clear();
+ m_lyrics << "Happy birthday to you,";
+ m_lyrics << "Happy birthday to you,";
+ m_lyrics << "Happy birthday dear " + m_name + ",";
+ m_lyrics << "Happy birthday to you!";
+ m_lyrics << "";
+}
+
+void HappyBirthdaySong::advance()
+{
+ m_line = (m_line + 1) % m_lyrics.count();
+
+ m_target.write(m_lyrics.at(m_line));
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h
new file mode 100644
index 0000000000..13907d5485
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef HAPPYBIRTHDAYSONG_H
+#define HAPPYBIRTHDAYSONG_H
+
+#include <QQmlProperty>
+#include <QQmlPropertyValueSource>
+#include <qqml.h>
+#include <QStringList>
+
+class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlPropertyValueSource)
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ QML_ELEMENT
+public:
+ explicit HappyBirthdaySong(QObject *parent = nullptr);
+
+ void setTarget(const QQmlProperty &) override;
+
+ QString name() const;
+ void setName(const QString &);
+
+signals:
+ void nameChanged();
+
+private slots:
+ void advance();
+
+private:
+ qsizetype m_line = -1;
+ QStringList m_lyrics;
+ QQmlProperty m_target;
+ QString m_name;
+};
+
+#endif // HAPPYBIRTHDAYSONG_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.cpp
new file mode 100644
index 0000000000..6d67b6179e
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.cpp
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+#include "person.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QQmlComponent>
+#include <QQmlEngine>
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadFromModule("People", "Main");
+ std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };
+
+ if (party && party->host()) {
+ qInfo() << party->host()->name() << "is having a birthday!";
+
+ if (qobject_cast<Boy *>(party->host()))
+ qInfo() << "He is inviting:";
+ else
+ qInfo() << "She is inviting:";
+
+ for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
+ Person *guest = party->guest(ii);
+
+ QDate rsvpDate;
+ QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
+ if (attached)
+ rsvpDate = attached->property("rsvp").toDate();
+
+ if (rsvpDate.isNull())
+ qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd";
+ else
+ qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString();
+ }
+
+ party->startParty();
+ return QCoreApplication::exec();
+ }
+
+ qWarning() << component.errors();
+ return EXIT_FAILURE;
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.cpp
new file mode 100644
index 0000000000..53cec6b192
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.cpp
@@ -0,0 +1,96 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "person.h"
+
+Person::Person(QObject *parent) : QObject(parent)
+{
+ m_shoe = new ShoeDescription(this);
+}
+
+int ShoeDescription::size() const
+{
+ return m_size;
+}
+
+void ShoeDescription::setSize(int size)
+{
+ if (m_size != size) {
+ m_size = size;
+ emit shoeChanged();
+ }
+}
+
+QColor ShoeDescription::color() const
+{
+ return m_color;
+}
+
+void ShoeDescription::setColor(const QColor &color)
+{
+ if (m_color != color) {
+ m_color = color;
+ emit shoeChanged();
+ }
+}
+
+QString ShoeDescription::brand() const
+{
+ return m_brand;
+}
+
+void ShoeDescription::setBrand(const QString &brand)
+{
+ if (m_brand != brand) {
+ m_brand = brand;
+ emit shoeChanged();
+ }
+}
+
+qreal ShoeDescription::price() const
+{
+ return m_price;
+}
+
+void ShoeDescription::setPrice(qreal price)
+{
+ if (m_price != price) {
+ m_price = price;
+ emit shoeChanged();
+ }
+}
+
+bool ShoeDescription::operatorEqualsImpl(const ShoeDescription &lhs, const ShoeDescription &rhs)
+{
+ return lhs.m_size == rhs.m_size && lhs.m_color == rhs.m_color && lhs.m_brand == rhs.m_brand
+ && lhs.m_price == rhs.m_price;
+}
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+}
+
+ShoeDescription *Person::shoe() const
+{
+ return m_shoe;
+}
+
+void Person::setShoe(ShoeDescription *shoe)
+{
+ if (!shoe)
+ return;
+
+ if (*m_shoe != *shoe) {
+ m_shoe = shoe;
+ emit shoeChanged();
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.h
new file mode 100644
index 0000000000..4f040e491b
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PERSON_H
+#define PERSON_H
+
+#include <QtQml/qqml.h>
+#include <QColor>
+#include <QObject>
+
+class ShoeDescription : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged FINAL)
+ QML_ANONYMOUS
+public:
+ using QObject::QObject;
+
+ int size() const;
+ void setSize(int);
+
+ QColor color() const;
+ void setColor(const QColor &);
+
+ QString brand() const;
+ void setBrand(const QString &);
+
+ qreal price() const;
+ void setPrice(qreal);
+
+ friend bool operator==(const ShoeDescription &lhs, const ShoeDescription &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+ friend bool operator!=(const ShoeDescription &lhs, const ShoeDescription &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+signals:
+ void shoeChanged();
+
+private:
+ static bool operatorEqualsImpl(const ShoeDescription &, const ShoeDescription &);
+
+ int m_size = 0;
+ QColor m_color;
+ QString m_brand;
+ qreal m_price = 0;
+};
+
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe NOTIFY shoeChanged FINAL)
+ QML_ELEMENT
+ QML_UNCREATABLE("Person is an abstract base class.")
+public:
+ using QObject::QObject;
+
+ Person(QObject *parent = nullptr);
+
+ QString name() const;
+ void setName(const QString &);
+
+ ShoeDescription *shoe() const;
+ void setShoe(ShoeDescription *shoe);
+
+signals:
+ void nameChanged();
+ void shoeChanged();
+
+private:
+ QString m_name;
+ ShoeDescription *m_shoe = nullptr;
+};
+
+class Boy : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+class Girl : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+#endif // PERSON_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/qmldir.in
new file mode 100644
index 0000000000..4c63c729d6
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/qmldir.in
@@ -0,0 +1,5 @@
+module People
+typeinfo valuesource.qmltypes
+prefer :/qt/qml/People/
+Main 254.0 Main.qml
+depends QtQuick
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.pro b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.pro
new file mode 100644
index 0000000000..c55299cecf
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.pro
@@ -0,0 +1,14 @@
+QT += qml quick
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = People
+QML_IMPORT_MAJOR_VERSION = 1
+
+SOURCES += main.cpp \
+ person.cpp \
+ birthdayparty.cpp \
+ happybirthdaysong.cpp
+HEADERS += person.h \
+ birthdayparty.h \
+ happybirthdaysong.h
+RESOURCES += valuesource.qrc
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.qrc
new file mode 100644
index 0000000000..b1eeb489e2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/qt/qml/People/">
+ <file>Main.qml</file>
+ <file alias="qmldir">qmldir.in</file>
+</qresource>
+</RCC>
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/CMakeLists.txt
new file mode 100644
index 0000000000..ffdb5b0949
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/CMakeLists.txt
@@ -0,0 +1,63 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(foreign LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick Gui)
+qt_standard_project_setup()
+
+qt_policy(SET QTP0001 NEW)
+
+add_subdirectory(library/)
+
+qt_add_executable(foreign
+ birthdayparty.cpp
+ birthdayparty.h
+ foreigndisplay.h
+ happybirthdaysong.cpp
+ happybirthdaysong.h
+ person.cpp
+ person.h
+ main.cpp
+)
+
+target_link_libraries(foreign PUBLIC
+ Qt6::Core
+ Qt6::Qml
+ Qt6::Gui
+ library
+)
+
+set_target_properties(foreign PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_include_directories(foreign PUBLIC
+ "${PROJECT_BINARY_DIR}"
+ "${PROJECT_SOURCE_DIR}/library"
+)
+
+qt_add_qml_module(foreign
+ URI People
+ QML_FILES Main.qml
+ SOURCES foreigndisplay.h
+ DEPENDENCIES
+ QtQuick
+)
+
+install(TARGETS foreign
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET foreign
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml
new file mode 100644
index 0000000000..988bea49f8
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+import QtQuick // For QColor
+
+BirthdayParty {
+ id: party
+ HappyBirthdaySong on announcement {
+ name: party.host.name
+ }
+
+ display: ThirdPartyDisplay {
+ foregroundColor: "black"
+ backgroundColor: "white"
+ }
+
+ onPartyStarted: (time) => { console.log("This party started rockin' at " + time); }
+
+
+ host: Boy {
+ name: "Bob Jones"
+ shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 }
+ }
+
+ Boy {
+ name: "Leo Hodges"
+ BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd")
+ shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
+ }
+ Boy {
+ name: "Jack Smith"
+ shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
+ }
+ Girl {
+ name: "Anne Brown"
+ BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd")
+ shoe.size: 7
+ shoe.color: "red"
+ shoe.brand: "Marc Jacobs"
+ shoe.price: 99.99
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/application.pro b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/application.pro
new file mode 100644
index 0000000000..b53397d49c
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/application.pro
@@ -0,0 +1,26 @@
+TEMPLATE = app
+
+CONFIG += console
+
+QT += core qml
+
+DEPENDPATH += library
+INCLUDEPATH += library
+LIBS += -Llibrary/ -llibrary
+
+SOURCES += \
+ birthdayparty.cpp \
+ happybirthdaysong.cpp \
+ main.cpp \
+ person.cpp
+HEADERS += \
+ birthdayparty.h \
+ foreigndisplay.h \
+ happybirthdaysong.h \
+ person.h
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = People
+QML_IMPORT_MAJOR_VERSION = 1
+
+RESOURCES += foreign.qrc
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp
new file mode 100644
index 0000000000..7a9debe195
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp
@@ -0,0 +1,150 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+
+QDate BirthdayPartyAttached::rsvp() const
+{
+ return m_rsvp;
+}
+
+void BirthdayPartyAttached::setRsvp(QDate rsvpDate)
+{
+ if (m_rsvp != rsvpDate) {
+ m_rsvp = rsvpDate;
+ emit rsvpChanged();
+ }
+}
+
+Person *BirthdayParty::host() const
+{
+ return m_host;
+}
+
+void BirthdayParty::setHost(Person *host)
+{
+ if (m_host != host) {
+ m_host = host;
+ emit hostChanged();
+ }
+}
+
+QQmlListProperty<Person> BirthdayParty::guests()
+{
+ return { this,
+ this,
+ &BirthdayParty::appendGuest,
+ &BirthdayParty::guestCount,
+ &BirthdayParty::guest,
+ &BirthdayParty::clearGuests,
+ &BirthdayParty::replaceGuest,
+ &BirthdayParty::removeLastGuest };
+}
+
+void BirthdayParty::appendGuest(Person *guest)
+{
+ m_guests.append(guest);
+ emit guestsChanged();
+}
+
+qsizetype BirthdayParty::guestCount() const
+{
+ return m_guests.count();
+}
+
+Person *BirthdayParty::guest(qsizetype index) const
+{
+ return m_guests.at(index);
+}
+
+void BirthdayParty::clearGuests()
+{
+ if (!m_guests.empty()) {
+ m_guests.clear();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::replaceGuest(qsizetype index, Person *guest)
+{
+ if (m_guests.size() > index) {
+ m_guests[index] = guest;
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::removeLastGuest()
+{
+ if (!m_guests.empty()) {
+ m_guests.removeLast();
+ emit guestsChanged();
+ }
+}
+
+void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->appendGuest(guest);
+}
+
+void BirthdayParty::clearGuests(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->clearGuests();
+}
+
+void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype index, Person *guest)
+{
+ static_cast<BirthdayParty *>(list->data)->replaceGuest(index, guest);
+}
+
+void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list)
+{
+ static_cast<BirthdayParty *>(list->data)->removeLastGuest();
+}
+
+Person *BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype index)
+{
+ return static_cast<BirthdayParty *>(list->data)->guest(index);
+}
+
+qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list)
+{
+ return static_cast<BirthdayParty *>(list->data)->guestCount();
+}
+
+void BirthdayParty::startParty()
+{
+ QDateTime time = QDateTime::currentDateTime();
+ emit partyStarted(time);
+}
+
+QString BirthdayParty::announcement() const
+{
+ return m_announcement;
+}
+
+void BirthdayParty::setAnnouncement(const QString &announcement)
+{
+ if (m_announcement != announcement) {
+ m_announcement = announcement;
+ emit announcementChanged();
+ }
+ m_display->setContent(announcement);
+}
+
+ThirdPartyDisplay *BirthdayParty::display() const
+{
+ return m_display;
+}
+
+void BirthdayParty::setDisplay(ThirdPartyDisplay *display)
+{
+ if (m_display != display) {
+ m_display = display;
+ emit displayChanged();
+ }
+}
+
+BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
+{
+ return new BirthdayPartyAttached(object);
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h
new file mode 100644
index 0000000000..59c53f2484
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BIRTHDAYPARTY_H
+#define BIRTHDAYPARTY_H
+
+#include "person.h"
+#include "ThirdPartyDisplay.h"
+
+#include <QDate>
+#include <QDebug>
+#include <QObject>
+#include <qqml.h>
+
+#include <memory>
+
+class BirthdayPartyAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL)
+ QML_ANONYMOUS
+public:
+ using QObject::QObject;
+
+ QDate rsvp() const;
+ void setRsvp(QDate);
+
+signals:
+ void rsvpChanged();
+
+private:
+ QDate m_rsvp;
+};
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
+ Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement NOTIFY announcementChanged FINAL)
+ Q_PROPERTY(ThirdPartyDisplay *display READ display WRITE setDisplay NOTIFY displayChanged FINAL)
+ Q_CLASSINFO("DefaultProperty", "guests")
+ QML_ELEMENT
+ QML_ATTACHED(BirthdayPartyAttached)
+public:
+ using QObject::QObject;
+
+ Person *host() const;
+ void setHost(Person *);
+
+ QQmlListProperty<Person> guests();
+ void appendGuest(Person *);
+ qsizetype guestCount() const;
+ Person *guest(qsizetype) const;
+ void clearGuests();
+ void replaceGuest(qsizetype, Person *);
+ void removeLastGuest();
+
+ QString announcement() const;
+ void setAnnouncement(const QString &);
+
+ ThirdPartyDisplay *display() const;
+ void setDisplay(ThirdPartyDisplay *);
+
+ static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
+
+ void startParty();
+
+signals:
+ void hostChanged();
+ void guestsChanged();
+ void partyStarted(QDateTime time);
+ void announcementChanged();
+ void displayChanged();
+
+private:
+ static void appendGuest(QQmlListProperty<Person> *, Person *);
+ static qsizetype guestCount(QQmlListProperty<Person> *);
+ static Person *guest(QQmlListProperty<Person> *, qsizetype);
+ static void clearGuests(QQmlListProperty<Person> *);
+ static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *);
+ static void removeLastGuest(QQmlListProperty<Person> *);
+
+ Person *m_host = nullptr;
+ QList<Person *> m_guests;
+ QString m_announcement;
+ ThirdPartyDisplay *m_display = nullptr;
+};
+
+#endif // BIRTHDAYPARTY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.pro b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.pro
new file mode 100644
index 0000000000..b637cb0840
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.pro
@@ -0,0 +1,7 @@
+TEMPLATE = subdirs
+
+SUBDIRS = \
+ application.pro \
+ library
+
+application.depends = library
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.qrc
new file mode 100644
index 0000000000..b1eeb489e2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/qt/qml/People/">
+ <file>Main.qml</file>
+ <file alias="qmldir">qmldir.in</file>
+</qresource>
+</RCC>
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h
new file mode 100644
index 0000000000..ee42ca965c
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FOREIGNDISPLAY_H
+#define FOREIGNDISPLAY_H
+
+#include "ThirdPartyDisplay.h"
+
+#include <QColor>
+#include <QObject>
+#include <qqml.h>
+
+class ForeignDisplay : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(ThirdPartyDisplay)
+ QML_FOREIGN(ThirdPartyDisplay)
+};
+
+#endif // FOREIGNDISPLAY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.cpp
new file mode 100644
index 0000000000..7a756a4a71
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.cpp
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "happybirthdaysong.h"
+
+#include <QTimer>
+
+HappyBirthdaySong::HappyBirthdaySong(QObject *parent) : QObject(parent)
+{
+ auto *timer = new QTimer(this);
+ QObject::connect(timer, &QTimer::timeout, this, &HappyBirthdaySong::advance);
+ timer->start(1000);
+}
+
+void HappyBirthdaySong::setTarget(const QQmlProperty &target)
+{
+ m_target = target;
+}
+
+QString HappyBirthdaySong::name() const
+{
+ return m_name;
+}
+
+void HappyBirthdaySong::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+
+ m_lyrics.clear();
+ m_lyrics << "Happy birthday to you,";
+ m_lyrics << "Happy birthday to you,";
+ m_lyrics << "Happy birthday dear " + m_name + ",";
+ m_lyrics << "Happy birthday to you!";
+ m_lyrics << "";
+}
+
+void HappyBirthdaySong::advance()
+{
+ m_line = (m_line + 1) % m_lyrics.count();
+
+ m_target.write(m_lyrics.at(m_line));
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.h
new file mode 100644
index 0000000000..f87521a760
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef HAPPYBIRTHDAYSONG_H
+#define HAPPYBIRTHDAYSONG_H
+
+#include <QQmlPropertyValueSource>
+#include <QQmlProperty>
+#include <qqml.h>
+#include <QStringList>
+
+class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlPropertyValueSource)
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ QML_ELEMENT
+public:
+ explicit HappyBirthdaySong(QObject *parent = nullptr);
+
+ void setTarget(const QQmlProperty &) override;
+
+ QString name() const;
+ void setName(const QString &);
+
+signals:
+ void nameChanged();
+
+private slots:
+ void advance();
+
+private:
+ qsizetype m_line = -1;
+ QStringList m_lyrics;
+ QQmlProperty m_target;
+ QString m_name;
+};
+
+#endif // HAPPYBIRTHDAYSONG_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/CMakeLists.txt
new file mode 100644
index 0000000000..7fa6d6a81a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+project(library)
+
+add_library(library ThirdPartyDisplay.cpp ThirdPartyDisplay.h)
+
+qt_extract_metatypes(library)
+
+target_link_libraries(library PUBLIC
+ Qt6::Core
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::Gui
+)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.cpp
new file mode 100644
index 0000000000..5fc4eb2e8f
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.cpp
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "ThirdPartyDisplay.h"
+#include <QDebug>
+
+const QString &ThirdPartyDisplay::content() const
+{
+ return m_content;
+}
+
+void ThirdPartyDisplay::setContent(const QString &content)
+{
+ if (m_content != content) {
+ m_content = content;
+ emit contentChanged();
+ }
+ qInfo() << QStringLiteral("[Fancy ThirdPartyDisplay] ") + content;
+}
+
+QColor ThirdPartyDisplay::foregroundColor() const
+{
+ return m_foregroundColor;
+}
+
+void ThirdPartyDisplay::setForegroundColor(QColor color)
+{
+ if (m_foregroundColor != color) {
+ m_foregroundColor = color;
+ emit colorsChanged();
+ }
+}
+
+QColor ThirdPartyDisplay::backgroundColor() const
+{
+ return m_backgroundColor;
+}
+
+void ThirdPartyDisplay::setBackgroundColor(QColor color)
+{
+ if (m_backgroundColor != color) {
+ m_backgroundColor = color;
+ emit colorsChanged();
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h
new file mode 100644
index 0000000000..525c9f72cf
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef THIRDPARTYDISPLAY_H
+#define THIRDPARTYDISPLAY_H
+
+#include <QColor>
+#include <QObject>
+
+class Q_DECL_EXPORT ThirdPartyDisplay : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString content READ content WRITE setContent NOTIFY contentChanged FINAL)
+ Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor NOTIFY colorsChanged FINAL)
+ Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY colorsChanged FINAL)
+public:
+ const QString &content() const;
+ void setContent(const QString &content);
+
+ QColor foregroundColor() const;
+ void setForegroundColor(QColor);
+
+ QColor backgroundColor() const;
+ void setBackgroundColor(QColor);
+
+signals:
+ void contentChanged();
+ void colorsChanged();
+
+private:
+ QString m_content;
+ QColor m_foregroundColor;
+ QColor m_backgroundColor;
+};
+
+#endif // THIRDPARTYDISPLAY_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/library.pro b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/library.pro
new file mode 100644
index 0000000000..f7009c46c9
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/library.pro
@@ -0,0 +1,8 @@
+TEMPLATE = lib
+
+CONFIG += static
+
+SOURCES += ThirdPartyDisplay.cpp
+HEADERS += ThirdPartyDisplay.h
+
+QT += core qml gui
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/main.cpp
new file mode 100644
index 0000000000..9c6f6bcc2f
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/main.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "birthdayparty.h"
+#include "person.h"
+
+#include <QCoreApplication>
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <QDebug>
+#include <QFile>
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadFromModule("People", "Main");
+ std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };
+
+ if (party && party->host()) {
+ qInfo() << party->host()->name() << "is having a birthday!";
+
+ if (qobject_cast<Boy *>(party->host()))
+ qInfo() << "He is inviting:";
+ else
+ qInfo() << "She is inviting:";
+
+ for (qsizetype ii = 0; ii < party->guestCount(); ++ii) {
+ Person *guest = party->guest(ii);
+
+ QDate rsvpDate;
+ QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
+ if (attached)
+ rsvpDate = attached->property("rsvp").toDate();
+
+ if (rsvpDate.isNull())
+ qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd";
+ else
+ qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString();
+ }
+
+ party->startParty();
+ return QCoreApplication::exec();
+ }
+
+ qWarning() << component.errors();
+ return EXIT_FAILURE;
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.cpp
new file mode 100644
index 0000000000..53cec6b192
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.cpp
@@ -0,0 +1,96 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "person.h"
+
+Person::Person(QObject *parent) : QObject(parent)
+{
+ m_shoe = new ShoeDescription(this);
+}
+
+int ShoeDescription::size() const
+{
+ return m_size;
+}
+
+void ShoeDescription::setSize(int size)
+{
+ if (m_size != size) {
+ m_size = size;
+ emit shoeChanged();
+ }
+}
+
+QColor ShoeDescription::color() const
+{
+ return m_color;
+}
+
+void ShoeDescription::setColor(const QColor &color)
+{
+ if (m_color != color) {
+ m_color = color;
+ emit shoeChanged();
+ }
+}
+
+QString ShoeDescription::brand() const
+{
+ return m_brand;
+}
+
+void ShoeDescription::setBrand(const QString &brand)
+{
+ if (m_brand != brand) {
+ m_brand = brand;
+ emit shoeChanged();
+ }
+}
+
+qreal ShoeDescription::price() const
+{
+ return m_price;
+}
+
+void ShoeDescription::setPrice(qreal price)
+{
+ if (m_price != price) {
+ m_price = price;
+ emit shoeChanged();
+ }
+}
+
+bool ShoeDescription::operatorEqualsImpl(const ShoeDescription &lhs, const ShoeDescription &rhs)
+{
+ return lhs.m_size == rhs.m_size && lhs.m_color == rhs.m_color && lhs.m_brand == rhs.m_brand
+ && lhs.m_price == rhs.m_price;
+}
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &name)
+{
+ if (m_name != name) {
+ m_name = name;
+ emit nameChanged();
+ }
+}
+
+ShoeDescription *Person::shoe() const
+{
+ return m_shoe;
+}
+
+void Person::setShoe(ShoeDescription *shoe)
+{
+ if (!shoe)
+ return;
+
+ if (*m_shoe != *shoe) {
+ m_shoe = shoe;
+ emit shoeChanged();
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.h
new file mode 100644
index 0000000000..03c2dab953
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PERSON_H
+#define PERSON_H
+
+#include <QtQml/qqml.h>
+#include <QObject>
+#include <QColor>
+
+class ShoeDescription : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged FINAL)
+ Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged FINAL)
+ QML_ANONYMOUS
+public:
+ using QObject::QObject;
+
+ int size() const;
+ void setSize(int);
+
+ QColor color() const;
+ void setColor(const QColor &);
+
+ QString brand() const;
+ void setBrand(const QString &);
+
+ qreal price() const;
+ void setPrice(qreal);
+
+ friend bool operator==(const ShoeDescription &lhs, const ShoeDescription &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+ friend bool operator!=(const ShoeDescription &lhs, const ShoeDescription &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+signals:
+ void shoeChanged();
+
+private:
+ static bool operatorEqualsImpl(const ShoeDescription &, const ShoeDescription &);
+
+ int m_size = 0;
+ QColor m_color;
+ QString m_brand;
+ qreal m_price = 0;
+};
+
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe NOTIFY shoeChanged FINAL)
+ QML_ELEMENT
+ QML_UNCREATABLE("Person is an abstract base class.")
+public:
+ using QObject::QObject;
+
+ Person(QObject *parent = nullptr);
+
+ QString name() const;
+ void setName(const QString &);
+
+ ShoeDescription *shoe() const;
+ void setShoe(ShoeDescription *shoe);
+
+signals:
+ void nameChanged();
+ void shoeChanged();
+
+private:
+ QString m_name;
+ ShoeDescription *m_shoe = nullptr;
+};
+
+class Boy : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+class Girl : public Person
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ using Person::Person;
+};
+
+#endif // PERSON_H
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/qmldir.in
new file mode 100644
index 0000000000..5289a31938
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/qmldir.in
@@ -0,0 +1,5 @@
+module People
+typeinfo foreign.qmltypes
+prefer :/qt/qml/People/
+Main 254.0 Main.qml
+depends QtQuick
diff --git a/examples/qml/tutorials/extending-qml-advanced/extending-qml-advanced.pro b/examples/qml/tutorials/extending-qml-advanced/extending-qml-advanced.pro
new file mode 100644
index 0000000000..387d880a7d
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/extending-qml-advanced.pro
@@ -0,0 +1,10 @@
+TEMPLATE = subdirs
+
+SUBDIRS += \
+ advanced1-Base-project \
+ advanced2-Inheritance-and-coercion \
+ advanced3-Default-properties \
+ advanced4-Grouped-properties
+ advanced5-Attached-properties \
+ advanced6-Property-value-source \
+ advanced7-Extension-objects \
diff --git a/examples/qml/tutorials/extending-qml/CMakeLists.txt b/examples/qml/tutorials/extending-qml/CMakeLists.txt
index f5fc6e5cfe..87e3eaf54d 100644
--- a/examples/qml/tutorials/extending-qml/CMakeLists.txt
+++ b/examples/qml/tutorials/extending-qml/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(chapter1-basics)
qt_internal_add_example(chapter2-methods)
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml b/examples/qml/tutorials/extending-qml/chapter1-basics/App.qml
index ed89ce97c4..ed89ce97c4 100644
--- a/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/App.qml
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt
index 02ed972ef5..0080197766 100644
--- a/examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt
@@ -1,19 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter1-basics LANGUAGES CXX)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter1-basics")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(chapter1-basics
main.cpp
piechart.cpp piechart.h
@@ -25,21 +19,29 @@ set_target_properties(chapter1-basics PROPERTIES
)
target_link_libraries(chapter1-basics PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
#![0]
qt_add_qml_module(chapter1-basics
URI Charts
- QML_FILES app.qml
- NO_RESOURCE_TARGET_PATH
+ QML_FILES App.qml
DEPENDENCIES QtQuick
)
#![0]
install(TARGETS chapter1-basics
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chapter1-basics
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc b/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc
index f1168aef3b..c72f203e67 100644
--- a/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc
@@ -1,5 +1,6 @@
<RCC>
- <qresource prefix="/">
- <file>app.qml</file>
+ <qresource prefix="/qt/qml/Charts">
+ <file>App.qml</file>
+ <file>qmldir</file>
</qresource>
</RCC>
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp b/examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp
index f2eaab03bc..bc4bbba809 100644
--- a/examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp
@@ -11,7 +11,7 @@ int main(int argc, char *argv[])
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.setSource(QUrl("qrc:///app.qml"));
+ view.loadFromModule("Charts", "App");
view.show();
return QGuiApplication::exec();
}
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/qmldir b/examples/qml/tutorials/extending-qml/chapter1-basics/qmldir
new file mode 100644
index 0000000000..f69878a753
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/qmldir
@@ -0,0 +1,6 @@
+module Charts
+typeinfo chapter1-basics.qmltypes
+depends QtQuick
+prefer :/qt/qml/Charts/
+App 254.0 App.qml
+
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml b/examples/qml/tutorials/extending-qml/chapter2-methods/App.qml
index 56597c3f42..56597c3f42 100644
--- a/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/App.qml
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt
index f0906cf380..3fc7d3ffbf 100644
--- a/examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt
@@ -1,19 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter2-methods LANGUAGES CXX)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter2-methods")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(chapter2-methods
main.cpp
piechart.cpp piechart.h
@@ -25,21 +19,29 @@ set_target_properties(chapter2-methods PROPERTIES
)
target_link_libraries(chapter2-methods PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(chapter2-methods
URI Charts
- QML_FILES app.qml
- NO_RESOURCE_TARGET_PATH
+ QML_FILES App.qml
DEPENDENCIES QtQuick
)
install(TARGETS chapter2-methods
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chapter2-methods
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc b/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc
index f1168aef3b..c72f203e67 100644
--- a/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc
@@ -1,5 +1,6 @@
<RCC>
- <qresource prefix="/">
- <file>app.qml</file>
+ <qresource prefix="/qt/qml/Charts">
+ <file>App.qml</file>
+ <file>qmldir</file>
</qresource>
</RCC>
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp b/examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp
index f2eaab03bc..bc4bbba809 100644
--- a/examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp
@@ -11,7 +11,7 @@ int main(int argc, char *argv[])
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.setSource(QUrl("qrc:///app.qml"));
+ view.loadFromModule("Charts", "App");
view.show();
return QGuiApplication::exec();
}
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/qmldir b/examples/qml/tutorials/extending-qml/chapter2-methods/qmldir
new file mode 100644
index 0000000000..e8b0c98311
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/qmldir
@@ -0,0 +1,6 @@
+module Charts
+typeinfo chapter2-methods.qmltypes
+depends QtQuick
+prefer :/qt/qml/Charts/
+App 254.0 App.qml
+
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml b/examples/qml/tutorials/extending-qml/chapter3-bindings/App.qml
index d29eaf4b9f..d29eaf4b9f 100644
--- a/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/App.qml
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt
index 05c567fd30..a842eb48ca 100644
--- a/examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt
@@ -1,19 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter3-bindings LANGUAGES CXX)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter3-bindings")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(chapter3-bindings
main.cpp
piechart.cpp piechart.h
@@ -25,21 +19,29 @@ set_target_properties(chapter3-bindings PROPERTIES
)
target_link_libraries(chapter3-bindings PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(chapter3-bindings
URI Charts
- QML_FILES app.qml
- NO_RESOURCE_TARGET_PATH
+ QML_FILES App.qml
DEPENDENCIES QtQuick
)
install(TARGETS chapter3-bindings
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chapter3-bindings
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc b/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc
index f1168aef3b..c72f203e67 100644
--- a/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc
@@ -1,5 +1,6 @@
<RCC>
- <qresource prefix="/">
- <file>app.qml</file>
+ <qresource prefix="/qt/qml/Charts">
+ <file>App.qml</file>
+ <file>qmldir</file>
</qresource>
</RCC>
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp b/examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp
index f2eaab03bc..bc4bbba809 100644
--- a/examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp
@@ -11,7 +11,7 @@ int main(int argc, char *argv[])
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.setSource(QUrl("qrc:///app.qml"));
+ view.loadFromModule("Charts", "App");
view.show();
return QGuiApplication::exec();
}
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/qmldir b/examples/qml/tutorials/extending-qml/chapter3-bindings/qmldir
new file mode 100644
index 0000000000..f0911d3c89
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/qmldir
@@ -0,0 +1,6 @@
+module Charts
+typeinfo chapter3-bindings.qmltypes
+depends QtQuick
+prefer :/qt/qml/Charts/
+App 254.0 App.qml
+
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/App.qml
index 2edcda568e..2edcda568e 100644
--- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/App.qml
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt
index 3914819640..fd6428093b 100644
--- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt
@@ -1,18 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter4-customPropertyTypes LANGUAGES CXX)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter4-customPropertyTypes")
+qt_standard_project_setup(REQUIRES 6.5)
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
#![0]
qt_add_executable(chapter4-customPropertyTypes
main.cpp
@@ -26,21 +21,29 @@ set_target_properties(chapter4-customPropertyTypes PROPERTIES
)
target_link_libraries(chapter4-customPropertyTypes PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
#![1]
qt_add_qml_module(chapter4-customPropertyTypes
URI Charts
- QML_FILES app.qml
- NO_RESOURCE_TARGET_PATH
+ QML_FILES App.qml
DEPENDENCIES QtQuick
)
#![1]
install(TARGETS chapter4-customPropertyTypes
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chapter4-customPropertyTypes
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc
index f1168aef3b..c72f203e67 100644
--- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc
@@ -1,5 +1,6 @@
<RCC>
- <qresource prefix="/">
- <file>app.qml</file>
+ <qresource prefix="/qt/qml/Charts">
+ <file>App.qml</file>
+ <file>qmldir</file>
</qresource>
</RCC>
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp
index 8833750ebf..09a025ae85 100644
--- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp
@@ -14,7 +14,7 @@ int main(int argc, char *argv[])
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.setSource(QUrl("qrc:///app.qml"));
+ view.loadFromModule("Charts", "App");
view.show();
return QGuiApplication::exec();
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/qmldir b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/qmldir
new file mode 100644
index 0000000000..14c64f5269
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/qmldir
@@ -0,0 +1,6 @@
+module Charts
+typeinfo chapter4-customPropertyTypes.qmltypes
+depends QtQuick
+prefer :/qt/qml/Charts/
+App 254.0 App.qml
+
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml b/examples/qml/tutorials/extending-qml/chapter5-listproperties/App.qml
index 9e200a9b60..9e200a9b60 100644
--- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/App.qml
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt
index d3b70f2017..73984849c0 100644
--- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt
@@ -1,19 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter5-listproperties LANGUAGES CXX)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter5-listproperties")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(chapter5-listproperties
main.cpp
piechart.cpp piechart.h
@@ -26,21 +20,29 @@ set_target_properties(chapter5-listproperties PROPERTIES
)
target_link_libraries(chapter5-listproperties PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(chapter5-listproperties
URI Charts
- QML_FILES app.qml
- NO_RESOURCE_TARGET_PATH
+ QML_FILES App.qml
DEPENDENCIES QtQuick
)
install(TARGETS chapter5-listproperties
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chapter5-listproperties
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc b/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc
index f1168aef3b..c72f203e67 100644
--- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc
@@ -1,5 +1,6 @@
<RCC>
- <qresource prefix="/">
- <file>app.qml</file>
+ <qresource prefix="/qt/qml/Charts">
+ <file>App.qml</file>
+ <file>qmldir</file>
</qresource>
</RCC>
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp b/examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp
index 3d11a689a6..12d4d49438 100644
--- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp
@@ -12,7 +12,7 @@ int main(int argc, char *argv[])
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.setSource(QUrl("qrc:///app.qml"));
+ view.loadFromModule("Charts", "App");
view.show();
return QGuiApplication::exec();
}
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/qmldir b/examples/qml/tutorials/extending-qml/chapter5-listproperties/qmldir
new file mode 100644
index 0000000000..cebd0320ab
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/qmldir
@@ -0,0 +1,6 @@
+module Charts
+typeinfo chapter5-listproperties.qmltypes
+depends QtQuick
+prefer :/qt/qml/Charts/
+App 254.0 App.qml
+
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml b/examples/qml/tutorials/extending-qml/chapter6-plugins/App.qml
index 1d1cb74176..1d1cb74176 100644
--- a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/App.qml
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt
index f1a7ef1b51..bd88766e0c 100644
--- a/examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt
@@ -1,19 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter6-plugins LANGUAGES CXX)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter6-plugins")
-
find_package(Qt6 REQUIRED COMPONENTS Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(chapter6-plugins
main.cpp
)
@@ -24,20 +18,29 @@ set_target_properties(chapter6-plugins PROPERTIES
)
target_link_libraries(chapter6-plugins PRIVATE
- Qt::Qml
- Qt::Quick
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(chapter6-plugins
URI ChartsApp
- QML_FILES app.qml
- NO_RESOURCE_TARGET_PATH
+ QML_FILES App.qml
)
+add_subdirectory(Charts)
+
install(TARGETS chapter6-plugins
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-add_subdirectory(Charts)
+qt_generate_deploy_qml_app_script(
+ TARGET chapter6-plugins
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
+
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt
index 4a8f2e0b7b..a0fed166e0 100644
--- a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt6_policy(SET QTP0001 NEW)
qt6_add_qml_module(chartsplugin
@@ -14,40 +14,16 @@ target_sources(chartsplugin PRIVATE
)
target_link_libraries(chartsplugin PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
-if(QT6_IS_SHARED_LIBS_BUILD AND APPLE)
- get_target_property(is_bundle chapter6-plugins MACOSX_BUNDLE)
- if(is_bundle)
- # The application's main.cpp adds an explicit QML import path to look for qml modules under
- # a PlugIns subdirectory in a macOS bundle.
- # Copy the qmldir and shared library qml plugin.
-
- set(charts_dir "$<TARGET_FILE_DIR:chartsplugin>")
- set(chars_qmldir_file "${charts_dir}/qmldir")
- set(app_dir "$<TARGET_FILE_DIR:chapter6-plugins>")
- set(bundle_charts_dir "${app_dir}/../PlugIns/Charts")
-
- add_custom_command(TARGET chartsplugin POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E make_directory ${bundle_charts_dir}
- COMMAND ${CMAKE_COMMAND} -E copy_if_different
- $<TARGET_FILE:chartsplugin> ${bundle_charts_dir}
- COMMAND ${CMAKE_COMMAND} -E copy_if_different
- ${chars_qmldir_file} ${bundle_charts_dir}
- VERBATIM
- )
- endif()
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLEDIR}/Charts")
install(TARGETS chartsplugin
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts"
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
- DESTINATION "${INSTALL_EXAMPLEDIR}")
+ DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts"
+)
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir
index d9e8471b3c..4536f60e91 100644
--- a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir
@@ -1,2 +1,5 @@
module Charts
-plugin chartsplugin
+optional plugin chartsplugin
+typeinfo plugins.qmltypes
+depends QtQuick
+prefer :/qt/qml/Charts/
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc b/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc
index f1168aef3b..5c4567d0d9 100644
--- a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc
@@ -1,5 +1,6 @@
<RCC>
- <qresource prefix="/">
- <file>app.qml</file>
+ <qresource prefix="/qt/qml/ChartsApp">
+ <file>App.qml</file>
+ <file>qmldir</file>
</qresource>
</RCC>
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp b/examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp
index 3475401ebd..7dfb1cf5a7 100644
--- a/examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp
@@ -9,12 +9,12 @@ int main(int argc, char *argv[])
QGuiApplication app(argc, argv);
//![0]
QQuickView view;
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
view.engine()->addImportPath(app.applicationDirPath() + "/../PlugIns");
#endif
//![0]
view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.setSource(QUrl("qrc:///app.qml"));
+ view.loadFromModule("ChartsApp", "App");
view.show();
return app.exec();
}
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/qmldir b/examples/qml/tutorials/extending-qml/chapter6-plugins/qmldir
new file mode 100644
index 0000000000..d7fa4b260b
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/qmldir
@@ -0,0 +1,3 @@
+module ChartsApp
+prefer :/qt/qml/ChartsApp/
+App 254.0 App.qml
diff --git a/examples/qml/xmlhttprequest/CMakeLists.txt b/examples/qml/xmlhttprequest/CMakeLists.txt
deleted file mode 100644
index 63d15c37dd..0000000000
--- a/examples/qml/xmlhttprequest/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(xmlhttprequest LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/xmlhttprequest")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_add_executable(xmlhttprequestexample
- WIN32
- MACOSX_BUNDLE
- main.cpp
-)
-
-qt_add_qml_module(xmlhttprequestexample
- URI xmlhttprequest
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "methods.js"
- "xmlhttprequest.qml"
- RESOURCES
- data.xml
-)
-
-target_link_libraries(xmlhttprequestexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-install(TARGETS xmlhttprequestexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qml/xmlhttprequest/data.xml b/examples/qml/xmlhttprequest/data.xml
deleted file mode 100644
index 8b7f1e116d..0000000000
--- a/examples/qml/xmlhttprequest/data.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<data>
- <element1 />
- <element2 />
-</data>
-
diff --git a/examples/qml/xmlhttprequest/doc/images/qml-xmlhttprequest-example.png b/examples/qml/xmlhttprequest/doc/images/qml-xmlhttprequest-example.png
deleted file mode 100644
index f585613427..0000000000
--- a/examples/qml/xmlhttprequest/doc/images/qml-xmlhttprequest-example.png
+++ /dev/null
Binary files differ
diff --git a/examples/qml/xmlhttprequest/doc/src/xmlhttprequest.qdoc b/examples/qml/xmlhttprequest/doc/src/xmlhttprequest.qdoc
deleted file mode 100644
index fc6ca600cd..0000000000
--- a/examples/qml/xmlhttprequest/doc/src/xmlhttprequest.qdoc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-/*!
- \title Qt Quick Examples - XMLHttpRequest
- \example xmlhttprequest
- \brief This is a collection of XMLHttpRequest examples.
- \image qml-xmlhttprequest-example.png
-
- \e XMLHttpRequest contains a small QML example demonstrating \l{Qt QML}'s
- XMLHttpRequest functionality. For more information, visit the section about
- \l{QML Global Object#XMLHttpRequest}{XMLHttpRequest}.
-
- \include examples-run.qdocinc
-
- \section1 Get Data
-
- \e{Get data} uses the XMLHttpRequest API to fetch an XML document from a
- server. It displays the header of the HTTP response and the body of the XML
- document.
-*/
diff --git a/examples/qml/xmlhttprequest/main.cpp b/examples/qml/xmlhttprequest/main.cpp
deleted file mode 100644
index f76ca49de5..0000000000
--- a/examples/qml/xmlhttprequest/main.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QDir>
-#include <QGuiApplication>
-#include <QQmlFileSelector>
-#include <QQuickView>
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
- app.setOrganizationName("QtProject");
- app.setOrganizationDomain("qt-project.org");
- app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName());
- QQuickView view;
-
- qputenv("QML_XHR_ALLOW_FILE_READ", QByteArray("1"));
-
- view.connect(view.engine(), &QQmlEngine::quit, &app, &QCoreApplication::quit);
- view.setSource(QUrl("qrc:/qt/qml/xmlhttprequest/xmlhttprequest.qml"));
- if (view.status() == QQuickView::Error)
- return -1;
- view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.show();
- return app.exec();
-}
diff --git a/examples/qml/xmlhttprequest/methods.js b/examples/qml/xmlhttprequest/methods.js
deleted file mode 100644
index 12bde365b2..0000000000
--- a/examples/qml/xmlhttprequest/methods.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-function showRequestInfo(text) {
- msg.text = msg.text + "\n" + text
-}
-
-function makeRequest()
-{
-
- var doc = new XMLHttpRequest();
- msg.text = "";
- doc.onreadystatechange = function() {
- if (doc.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
- showRequestInfo("Headers -->");
- showRequestInfo(doc.getAllResponseHeaders ());
- showRequestInfo("Last modified -->");
- showRequestInfo(doc.getResponseHeader ("Last-Modified"));
-
- } else if (doc.readyState == XMLHttpRequest.DONE) {
- var a = doc.responseXML.documentElement;
- for (var ii = 0; ii < a.childNodes.length; ++ii) {
- showRequestInfo(a.childNodes[ii].nodeName);
- }
- showRequestInfo("Headers -->");
- showRequestInfo(doc.getAllResponseHeaders ());
- showRequestInfo("Last modified -->");
- showRequestInfo(doc.getResponseHeader ("Last-Modified"));
- }
- }
-
- doc.open("GET", "data.xml");
- doc.send();
-}
diff --git a/examples/qml/xmlhttprequest/xmlhttprequest.pro b/examples/qml/xmlhttprequest/xmlhttprequest.pro
deleted file mode 100644
index 6d572fff59..0000000000
--- a/examples/qml/xmlhttprequest/xmlhttprequest.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-RESOURCES += \
- xmlhttprequest.qrc
-
-EXAMPLE_FILES = \
- data.xml
-
-target.path = $$[QT_INSTALL_EXAMPLES]/qml/xmlhttprequest
-INSTALLS += target
-
-DISTFILES += \
- methods.js
diff --git a/examples/qml/xmlhttprequest/xmlhttprequest.qml b/examples/qml/xmlhttprequest/xmlhttprequest.qml
deleted file mode 100644
index 434648c16f..0000000000
--- a/examples/qml/xmlhttprequest/xmlhttprequest.qml
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import "methods.js" as Utils
-
-
-Item {
- height: 480
- width: 320
-
- property alias msg: ttext
-
- Label { id: ttext; anchors.fill: parent; anchors.margins: 10 }
-
- Button {
- id: button
- anchors.horizontalCenter: parent.horizontalCenter;
- anchors.bottom: parent.bottom
- anchors.margins: 10
- antialiasing: true
-
- text: qsTr("Request data.xml")
-
- onClicked: Utils.makeRequest()
- }
-}
diff --git a/examples/qml/xmlhttprequest/xmlhttprequest.qmlproject b/examples/qml/xmlhttprequest/xmlhttprequest.qmlproject
deleted file mode 100644
index 088042a100..0000000000
--- a/examples/qml/xmlhttprequest/xmlhttprequest.qmlproject
+++ /dev/null
@@ -1,16 +0,0 @@
-import QmlProject 1.0
-
-Project {
- mainFile: "xmlhttprequest.qml"
- /* Include .qml, .js, and image files from current directory and subdirectories */
-
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-} \ No newline at end of file
diff --git a/examples/qml/xmlhttprequest/xmlhttprequest.qrc b/examples/qml/xmlhttprequest/xmlhttprequest.qrc
deleted file mode 100644
index 39d2a62393..0000000000
--- a/examples/qml/xmlhttprequest/xmlhttprequest.qrc
+++ /dev/null
@@ -1,7 +0,0 @@
-<RCC>
- <qresource prefix="/qt/qml/xmlhttprequest">
- <file>xmlhttprequest.qml</file>
- <file>methods.js</file>
- <file>data.xml</file>
- </qresource>
-</RCC>
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter1/CMakeLists.txt b/examples/qmlcompiler/tutorials/helloworld/chapter1/CMakeLists.txt
new file mode 100644
index 0000000000..b9466b61b3
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter1/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.21)
+
+project(hello_world_plugin VERSION 1.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 REQUIRED COMPONENTS QmlCompiler)
+
+qt_standard_project_setup(
+ REQUIRES 6.6
+)
+
+qt_add_plugin(HelloWorldPlugin)
+
+target_sources(HelloWorldPlugin
+ PRIVATE
+ helloplugin.h
+ helloplugin.cpp
+)
+
+target_link_libraries(HelloWorldPlugin PRIVATE Qt::QmlCompiler)
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter1/helloplugin.cpp b/examples/qmlcompiler/tutorials/helloworld/chapter1/helloplugin.cpp
new file mode 100644
index 0000000000..ad36e64fae
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter1/helloplugin.cpp
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include "helloplugin.h"
+#include <QDebug>
+
+using namespace Qt::StringLiterals;
+
+static constexpr QQmlSA::LoggerWarningId helloWorld { "Plugin.HelloWorld.hello-world" };
+
+void HelloWorldPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement)
+{
+ const bool pluginIsEnabled = manager->isCategoryEnabled(helloWorld);
+ qDebug() << "Hello World plugin is" << (pluginIsEnabled ? "enabled" : "disabled");
+ if (!pluginIsEnabled)
+ return; // skip registration if the plugin is disabled anyway
+ // here we will later register our passes
+}
+
+#include "moc_helloplugin.cpp"
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter1/helloplugin.h b/examples/qmlcompiler/tutorials/helloworld/chapter1/helloplugin.h
new file mode 100644
index 0000000000..6666c78be0
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter1/helloplugin.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef HELLO_PLUGIN_H
+#define HELLO_PLUGIN_H
+
+#include <QtCore/qobject.h>
+#include <QtQmlCompiler/qqmlsa.h>
+
+class HelloWorldPlugin : public QObject, public QQmlSA::LintPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QmlLintPluginInterface_iid FILE "plugin.json")
+ Q_INTERFACES(QQmlSA::LintPlugin)
+
+public:
+ void registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) override;
+};
+
+#endif
+
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter1/plugin.json b/examples/qmlcompiler/tutorials/helloworld/chapter1/plugin.json
new file mode 100644
index 0000000000..3d8c1ff206
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter1/plugin.json
@@ -0,0 +1,13 @@
+{
+ "name": "HelloWorld",
+ "author": "Qt Example",
+ "description": "Demonstrates how to write a qmllint plugin",
+ "version": "1.0",
+ "loggingCategories": [
+ {
+ "name": "hello-world",
+ "settingsName": "HelloWorld",
+ "description": "Used to create test messages"
+ }
+ ]
+}
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter1/test.qml b/examples/qmlcompiler/tutorials/helloworld/chapter1/test.qml
new file mode 100644
index 0000000000..99af71ad7d
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter1/test.qml
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: root
+
+ property string greeting: "Hello"
+
+ component MyText : Text {}
+
+ component NotText : Item {
+ property string text
+ }
+
+ Text { text: "Hello world!" }
+ Text { text: root.greeting }
+ Text { text: "Goodbye world!" }
+ NotText {
+ text: "Does not trigger"
+ MyText { text: "Goodbye world!" }
+ }
+}
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter2/CMakeLists.txt b/examples/qmlcompiler/tutorials/helloworld/chapter2/CMakeLists.txt
new file mode 100644
index 0000000000..b9466b61b3
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter2/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.21)
+
+project(hello_world_plugin VERSION 1.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 REQUIRED COMPONENTS QmlCompiler)
+
+qt_standard_project_setup(
+ REQUIRES 6.6
+)
+
+qt_add_plugin(HelloWorldPlugin)
+
+target_sources(HelloWorldPlugin
+ PRIVATE
+ helloplugin.h
+ helloplugin.cpp
+)
+
+target_link_libraries(HelloWorldPlugin PRIVATE Qt::QmlCompiler)
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter2/helloplugin.cpp b/examples/qmlcompiler/tutorials/helloworld/chapter2/helloplugin.cpp
new file mode 100644
index 0000000000..431ad97f16
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter2/helloplugin.cpp
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "helloplugin.h"
+#include <QDebug>
+
+using namespace Qt::StringLiterals;
+
+static constexpr QQmlSA::LoggerWarningId helloWorld { "Plugin.HelloWorld.hello-world" };
+
+class HelloWorldElementPass : public QQmlSA::ElementPass
+{
+public:
+ HelloWorldElementPass(QQmlSA::PassManager *manager);
+ bool shouldRun(const QQmlSA::Element &element) override;
+ void run(const QQmlSA::Element &element) override;
+private:
+ QQmlSA::Element m_textType;
+};
+
+HelloWorldElementPass::HelloWorldElementPass(QQmlSA::PassManager *manager)
+ : QQmlSA::ElementPass(manager)
+{
+ m_textType = resolveType("QtQuick", "Text");
+}
+
+bool HelloWorldElementPass::shouldRun(const QQmlSA::Element &element)
+{
+ if (!element.inherits(m_textType))
+ return false;
+ if (!element.hasOwnPropertyBindings(u"text"_s))
+ return false;
+ return true;
+}
+
+void HelloWorldElementPass::run(const QQmlSA::Element &element)
+{
+ auto textBindings = element.ownPropertyBindings(u"text"_s);
+ for (const auto &textBinding: textBindings) {
+ if (textBinding.bindingType() != QQmlSA::BindingType::StringLiteral)
+ continue;
+ if (textBinding.stringValue() != u"Hello world!"_s)
+ emitWarning("Incorrect greeting", helloWorld, textBinding.sourceLocation());
+ }
+}
+
+void HelloWorldPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement)
+{
+ const bool pluginIsEnabled = manager->isCategoryEnabled(helloWorld);
+ qDebug() << "Hello World plugin is" << (pluginIsEnabled ? "enabled" : "disabled");
+ if (!pluginIsEnabled)
+ return; // skip registration if the plugin is disabled anyway
+ manager->registerElementPass(std::make_unique<HelloWorldElementPass>(manager));
+}
+
+#include "moc_helloplugin.cpp"
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter2/helloplugin.h b/examples/qmlcompiler/tutorials/helloworld/chapter2/helloplugin.h
new file mode 100644
index 0000000000..6666c78be0
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter2/helloplugin.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef HELLO_PLUGIN_H
+#define HELLO_PLUGIN_H
+
+#include <QtCore/qobject.h>
+#include <QtQmlCompiler/qqmlsa.h>
+
+class HelloWorldPlugin : public QObject, public QQmlSA::LintPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QmlLintPluginInterface_iid FILE "plugin.json")
+ Q_INTERFACES(QQmlSA::LintPlugin)
+
+public:
+ void registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) override;
+};
+
+#endif
+
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter2/plugin.json b/examples/qmlcompiler/tutorials/helloworld/chapter2/plugin.json
new file mode 100644
index 0000000000..3d8c1ff206
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter2/plugin.json
@@ -0,0 +1,13 @@
+{
+ "name": "HelloWorld",
+ "author": "Qt Example",
+ "description": "Demonstrates how to write a qmllint plugin",
+ "version": "1.0",
+ "loggingCategories": [
+ {
+ "name": "hello-world",
+ "settingsName": "HelloWorld",
+ "description": "Used to create test messages"
+ }
+ ]
+}
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter2/test.qml b/examples/qmlcompiler/tutorials/helloworld/chapter2/test.qml
new file mode 100644
index 0000000000..99af71ad7d
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter2/test.qml
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: root
+
+ property string greeting: "Hello"
+
+ component MyText : Text {}
+
+ component NotText : Item {
+ property string text
+ }
+
+ Text { text: "Hello world!" }
+ Text { text: root.greeting }
+ Text { text: "Goodbye world!" }
+ NotText {
+ text: "Does not trigger"
+ MyText { text: "Goodbye world!" }
+ }
+}
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter3/CMakeLists.txt b/examples/qmlcompiler/tutorials/helloworld/chapter3/CMakeLists.txt
new file mode 100644
index 0000000000..b9466b61b3
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter3/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.21)
+
+project(hello_world_plugin VERSION 1.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 REQUIRED COMPONENTS QmlCompiler)
+
+qt_standard_project_setup(
+ REQUIRES 6.6
+)
+
+qt_add_plugin(HelloWorldPlugin)
+
+target_sources(HelloWorldPlugin
+ PRIVATE
+ helloplugin.h
+ helloplugin.cpp
+)
+
+target_link_libraries(HelloWorldPlugin PRIVATE Qt::QmlCompiler)
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter3/helloplugin.cpp b/examples/qmlcompiler/tutorials/helloworld/chapter3/helloplugin.cpp
new file mode 100644
index 0000000000..93ee6d80c4
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter3/helloplugin.cpp
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "helloplugin.h"
+#include <QDebug>
+
+using namespace Qt::StringLiterals;
+
+static constexpr QQmlSA::LoggerWarningId helloWorld { "Plugin.HelloWorld.hello-world" };
+
+class HelloWorldElementPass : public QQmlSA::ElementPass
+{
+public:
+ HelloWorldElementPass(QQmlSA::PassManager *manager);
+ bool shouldRun(const QQmlSA::Element &element) override;
+ void run(const QQmlSA::Element &element) override;
+private:
+ QQmlSA::Element m_textType;
+};
+
+HelloWorldElementPass::HelloWorldElementPass(QQmlSA::PassManager *manager)
+ : QQmlSA::ElementPass(manager)
+{
+ m_textType = resolveType("QtQuick", "Text");
+}
+
+bool HelloWorldElementPass::shouldRun(const QQmlSA::Element &element)
+{
+ if (!element.inherits(m_textType))
+ return false;
+ if (!element.hasOwnPropertyBindings(u"text"_s))
+ return false;
+ return true;
+}
+
+void HelloWorldElementPass::run(const QQmlSA::Element &element)
+{
+ auto textBindings = element.ownPropertyBindings(u"text"_s);
+ for (const auto &textBinding: textBindings) {
+ if (textBinding.bindingType() != QQmlSA::BindingType::StringLiteral)
+ continue;
+ QString currentBinding = textBinding.stringValue();
+ if (currentBinding == u"Hello world!"_s)
+ continue;
+ if (currentBinding == u"Goodbye world!"_s) {
+ QQmlSA::FixSuggestion suggestion(u"Replace 'Goodbye' with 'Hello'"_s,
+ textBinding.sourceLocation(), u"\"Hello world!\""_s);
+ suggestion.setAutoApplicable(true);
+ emitWarning("Incorrect greeting", helloWorld, textBinding.sourceLocation(), suggestion);
+ }
+ }
+}
+
+
+void HelloWorldPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement)
+{
+ const bool pluginIsEnabled = manager->isCategoryEnabled(helloWorld);
+ qDebug() << "Hello World plugin is" << (pluginIsEnabled ? "enabled" : "disabled");
+ if (!pluginIsEnabled)
+ return; // skip registration if the plugin is disabled anyway
+ manager->registerElementPass(std::make_unique<HelloWorldElementPass>(manager));
+}
+
+#include "moc_helloplugin.cpp"
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter3/helloplugin.h b/examples/qmlcompiler/tutorials/helloworld/chapter3/helloplugin.h
new file mode 100644
index 0000000000..6666c78be0
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter3/helloplugin.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef HELLO_PLUGIN_H
+#define HELLO_PLUGIN_H
+
+#include <QtCore/qobject.h>
+#include <QtQmlCompiler/qqmlsa.h>
+
+class HelloWorldPlugin : public QObject, public QQmlSA::LintPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QmlLintPluginInterface_iid FILE "plugin.json")
+ Q_INTERFACES(QQmlSA::LintPlugin)
+
+public:
+ void registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) override;
+};
+
+#endif
+
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter3/plugin.json b/examples/qmlcompiler/tutorials/helloworld/chapter3/plugin.json
new file mode 100644
index 0000000000..3d8c1ff206
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter3/plugin.json
@@ -0,0 +1,13 @@
+{
+ "name": "HelloWorld",
+ "author": "Qt Example",
+ "description": "Demonstrates how to write a qmllint plugin",
+ "version": "1.0",
+ "loggingCategories": [
+ {
+ "name": "hello-world",
+ "settingsName": "HelloWorld",
+ "description": "Used to create test messages"
+ }
+ ]
+}
diff --git a/examples/qmlcompiler/tutorials/helloworld/chapter3/test.qml b/examples/qmlcompiler/tutorials/helloworld/chapter3/test.qml
new file mode 100644
index 0000000000..42fbaa108f
--- /dev/null
+++ b/examples/qmlcompiler/tutorials/helloworld/chapter3/test.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+// [0]
+import QtQuick
+
+Item {
+ id: root
+
+ property string greeting: "Hello"
+
+ component MyText : Text {}
+
+ component NotText : Item {
+ property string text
+ }
+
+ Text { text: "Hello world!" }
+ Text { text: root.greeting }
+ Text { text: "Goodbye world!" }
+ NotText {
+ text: "Does not trigger"
+ MyText { text: "Goodbye world!" }
+ }
+}
+// [0]
diff --git a/examples/qmltest/CMakeLists.txt b/examples/qmltest/CMakeLists.txt
deleted file mode 100644
index be5db5fbd1..0000000000
--- a/examples/qmltest/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-qt_internal_add_example(qmltest)
diff --git a/examples/qmltest/qmltest.pro b/examples/qmltest/qmltest.pro
deleted file mode 100644
index b98cc03c65..0000000000
--- a/examples/qmltest/qmltest.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS += qmltest
diff --git a/examples/qmltest/qmltest/CMakeLists.txt b/examples/qmltest/qmltest/CMakeLists.txt
deleted file mode 100644
index 2fa3c0898c..0000000000
--- a/examples/qmltest/qmltest/CMakeLists.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(tst_qmltestexample LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}")
-set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples)
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui QuickTest)
-
-qt_add_executable(tst_qmltestexample
- tst_qmltest.cpp
-)
-
-set_target_properties(tst_qmltestexample PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(tst_qmltestexample PUBLIC
- Qt::Core
- Qt::Gui
-
- Qt::QuickTest
-
-)
-
-# This line get added, but does not make sense
-#if(QT_BUILDING_QT)
-# target_link_libraries(tst_qmltestexample PUBLIC
-# Qt::Qml
-# Qt::QuickTest
-# )
-#endif()
-
-if(MACOS AND QT_BUILDING_QT)
- set_target_properties(tst_qmltestexample PROPERTIES
- MACOSX_BUNDLE FALSE
- )
-endif()
-
-install(TARGETS tst_qmltestexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/qmltest/qmltest/tst_basic.qml b/examples/qmltest/qmltest/tst_basic.qml
deleted file mode 100644
index e3ce172beb..0000000000
--- a/examples/qmltest/qmltest/tst_basic.qml
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtTest
-
-TestCase {
- name: "BasicTests"
-
- function test_pass() {
- compare(2 + 2, 4, "2 + 2")
- }
-
- function test_fail() {
- expectFail("", "this is the fail we wanted")
- compare(2 + 2, 5, "2 + 2")
- }
-
- function test_skip() {
- skip("skipping")
- }
-
- function test_expecting() {
- expectFail("", "this is the fail we wanted")
- verify(false)
- }
-
- function test_table_data() {
- return [
- {tag: "2 + 2 = 4", a: 2, b: 2, answer: 4 },
- {tag: "2 + 6 = 8", a: 2, b: 6, answer: 8 },
- {tag: "2 + 2 = 5", a: 2, b: 2, answer: 5 }, // fail
- ]
- }
-
- function test_table(data) {
- if (data.answer === 5)
- expectFail("", "this is the fail we wanted")
- compare(data.a + data.b, data.answer)
- }
-}
diff --git a/examples/qmltest/qmltest/tst_item.qml b/examples/qmltest/qmltest/tst_item.qml
deleted file mode 100644
index 047bd254dd..0000000000
--- a/examples/qmltest/qmltest/tst_item.qml
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtTest
-
-Rectangle {
- id: foo
- width: 640; height: 480
- color: "cyan"
-
- TestCase {
- name: "ItemTests"
- id: test1
-
- function test_color() {
- compare(foo.color, "#00ffff")
- }
- }
-}
diff --git a/examples/qmltest/qmltest/tst_qmltest.cpp b/examples/qmltest/qmltest/tst_qmltest.cpp
deleted file mode 100644
index d9b367c5a8..0000000000
--- a/examples/qmltest/qmltest/tst_qmltest.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QtQuickTest/quicktest.h>
-QUICK_TEST_MAIN(qmltestexample)
diff --git a/examples/quick/CMakeLists.txt b/examples/quick/CMakeLists.txt
index f063585486..ae3580eb56 100644
--- a/examples/quick/CMakeLists.txt
+++ b/examples/quick/CMakeLists.txt
@@ -1,45 +1,41 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(quick-accessibility)
qt_internal_add_example(animation)
qt_internal_add_example(draganddrop)
-qt_internal_add_example(externaldraganddrop)
qt_internal_add_example(canvas)
qt_internal_add_example(imageelements)
qt_internal_add_example(keyinteraction)
qt_internal_add_example(layouts)
+qt_internal_add_example(responsivelayouts)
qt_internal_add_example(localstorage)
add_subdirectory(models)
qt_internal_add_example(views)
add_subdirectory(tableview)
qt_internal_add_example(mousearea)
qt_internal_add_example(positioners)
-qt_internal_add_example(righttoleft)
add_subdirectory(scenegraph)
qt_internal_add_example(shadereffects)
qt_internal_add_example(text)
-qt_internal_add_example(threading)
-qt_internal_add_example(touchinteraction)
add_subdirectory(tutorials)
add_subdirectory(customitems)
qt_internal_add_example(imageprovider)
qt_internal_add_example(imageresponseprovider)
qt_internal_add_example(window)
add_subdirectory(particles)
-qt_internal_add_example(delegatechooser)
-qt_internal_add_example(shapes)
qt_internal_add_example(itemvariablerefreshrate)
add_subdirectory(multieffect)
if(QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3)
add_subdirectory(rendercontrol)
endif()
-if(TARGET Qt::Widgets)
+if(TARGET Qt6::Widgets)
qt_internal_add_example(embeddedinwidgets)
endif()
-if(TARGET Qt::QuickWidgets AND TARGET Qt::Widgets AND (QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3))
+if(TARGET Qt6::QuickWidgets AND TARGET Qt6::Widgets AND (QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3))
add_subdirectory(quickwidgets)
endif()
+add_subdirectory(quickshapes)
# qt_examples_build_end() misses at least some of these due to some
# source subdirectories being added multiple times. See QTBUG-96159.
@@ -48,14 +44,9 @@ set(reused_dir_targets
canvas_shared
views_shared
positioners_shared
- righttoleft_shared
text_shared
- threading_shared
- touchinteraction_shared
window_shared
- shapes_shared
imageelements_shared
- delegatechooser_shared
pointerhandlers_shared
affectors_shared
emitters_shared
@@ -70,7 +61,7 @@ set(reused_dir_targets
foreach(target IN LISTS reused_dir_targets)
if(TARGET ${target})
qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS moc rcc)
- if(TARGET Qt::Widgets)
+ if(TARGET Qt6::Widgets)
qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS uic)
endif()
endif()
diff --git a/examples/quick/animation/CMakeLists.txt b/examples/quick/animation/CMakeLists.txt
index 1c9d954c42..5dc6d73547 100644
--- a/examples/quick/animation/CMakeLists.txt
+++ b/examples/quick/animation/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(animation LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/animation")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../shared" "shared")
@@ -23,17 +17,16 @@ qt_add_executable(animationexample
)
target_link_libraries(animationexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(animationexample animation_shared)
qt_add_qml_module(animationexample
URI animation
- AUTO_RESOURCE_PREFIX
QML_FILES
"animation.qml"
"basics/animators.qml"
@@ -59,9 +52,16 @@ qt_add_qml_module(animationexample
)
install(TARGETS animationexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(animationexample)
+qt_generate_deploy_qml_app_script(
+ TARGET animationexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/animation/behaviors/tvtennis.qml b/examples/quick/animation/behaviors/tvtennis.qml
index 0b8ba15ad7..4a55448ad7 100644
--- a/examples/quick/animation/behaviors/tvtennis.qml
+++ b/examples/quick/animation/behaviors/tvtennis.qml
@@ -8,30 +8,67 @@ Rectangle {
width: 320; height: 480;
color: "#1e1b18"
+ state: "right"
+ states: [
+ State {
+ name: "left"
+ PropertyChanges {
+ leftBat {
+ y: (ball.y + ball.height / 2) - leftBat.height / 2
+ }
+ rightBat {
+ y: page.height / 2 - rightBat.height / 2
+ }
+ }
+ },
+ State {
+ name: "right"
+ PropertyChanges {
+ rightBat {
+ y: (ball.y + ball.height / 2) - rightBat.height / 2
+ }
+ leftBat {
+ y: page.height / 2 - leftBat.height / 2
+ }
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "left"; to: "right"
+ NumberAnimation { property: "y"; easing.type: Easing.InOutQuad; duration: 200}
+ },
+ Transition {
+ from: "right"; to: "left"
+ NumberAnimation { property: "y"; easing.type: Easing.InOutQuad; duration: 200}
+ }
+ ]
+
// Make a ball to bounce
Rectangle {
id: ball
- // Add a property for the target y coordinate
- property variant direction : "right"
-
- x: 20; width: 20; height: 20; z: 1
+ width: 20
+ height: 20
+ z: 1
color: "#80c342"
// Move the ball to the right and back to the left repeatedly
SequentialAnimation on x {
loops: Animation.Infinite
NumberAnimation { to: page.width - 40; duration: 2000 }
- PropertyAction { target: ball; property: "direction"; value: "left" }
+ PropertyAction { target: page; property: "state"; value: "left" }
NumberAnimation { to: 20; duration: 2000 }
- PropertyAction { target: ball; property: "direction"; value: "right" }
+ PropertyAction { target: page; property: "state"; value: "right" }
}
// Make y move with a velocity of 200
- Behavior on y { SpringAnimation{ velocity: 200; }
+ Behavior on y {
+ SpringAnimation { velocity: 200; }
}
- Component.onCompleted: y = page.height-10; // start the ball motion
+ Component.onCompleted: y = page.height - 10; // start the ball motion
// Detect the ball hitting the top or bottom of the view and bounce it
onYChanged: {
@@ -48,31 +85,27 @@ Rectangle {
Rectangle {
id: leftBat
color: "#328930"
- x: 2; width: 20; height: 90
- // ![0]
- y: ball.direction == 'left' ? ball.y - 45 : page.height/2 -45;
- Behavior on y { SpringAnimation{ velocity: 300 } }
- // ![0]
+ width: 20; height: 90
+ x: 2;
}
Rectangle {
id: rightBat
color: "#328930"
- x: page.width - 22; width: 20; height: 90
- y: ball.direction == 'right' ? ball.y - 45 : page.height/2 -45;
- Behavior on y { SpringAnimation{ velocity: 300 } }
+ width: 20; height: 90
+ x: page.width - width - 2
}
// The rest, to make it look realistic, if neither ever scores...
- Rectangle { color: "#328930"; x: page.width/2-80; y: 0; width: 40; height: 60 }
- Rectangle { color: "#1e1b18"; x: page.width/2-70; y: 10; width: 20; height: 40 }
- Rectangle { color: "#328930"; x: page.width/2+40; y: 0; width: 40; height: 60 }
- Rectangle { color: "#1e1b18"; x: page.width/2+50; y: 10; width: 20; height: 40 }
+ Rectangle { color: "#328930"; x: page.width / 2 - 80; y: 0; width: 40; height: 60 }
+ Rectangle { color: "#1e1b18"; x: page.width / 2 - 70; y: 10; width: 20; height: 40 }
+ Rectangle { color: "#328930"; x: page.width / 2 + 40; y: 0; width: 40; height: 60 }
+ Rectangle { color: "#1e1b18"; x: page.width / 2 + 50; y: 10; width: 20; height: 40 }
Repeater {
model: page.height / 20
- Rectangle {
+ delegate: Rectangle {
required property int index
color: "#328930"
- x: page.width / 2 - 5
+ x: parent.width / 2 - 5
y: index * 20
width: 10
height: 10
diff --git a/examples/quick/animation/behaviors/wigglytext.qml b/examples/quick/animation/behaviors/wigglytext.qml
index 712078866d..1b987f6391 100644
--- a/examples/quick/animation/behaviors/wigglytext.qml
+++ b/examples/quick/animation/behaviors/wigglytext.qml
@@ -4,26 +4,28 @@
import QtQml
import QtQuick
+pragma ComponentBehavior: Bound
+
Rectangle {
id: container
- property string text: "Drag me!"
+ property string text: qsTr("Drag me!")
property bool animated: true
width: 320; height: 480; color: "#474747"; focus: true
Keys.onPressed: (event) => {
- if (event.key == Qt.Key_Delete || event.key == Qt.Key_Backspace)
+ if (event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace)
container.remove()
- else if (event.text != "") {
+ else if (event.text !== "") {
container.append(event.text)
}
}
function append(text) {
container.animated = false
- var lastLetter = container.children[container.children.length - 1]
- var newLetter = letterComponent.createObject(container)
+ const lastLetter = container.children[container.children.length - 1]
+ let newLetter = letterComponent.createObject(container)
newLetter.text = text
newLetter.follow = lastLetter
container.animated = true
@@ -36,8 +38,8 @@ Rectangle {
function doLayout() {
var follow = null
- for (var i = 0; i < container.text.length; ++i) {
- var newLetter = letterComponent.createObject(container)
+ for (let i = 0; i < container.text.length; ++i) {
+ let newLetter = letterComponent.createObject(container)
newLetter.text = container.text[i]
newLetter.follow = follow
follow = newLetter
diff --git a/examples/quick/animation/doc/src/animation.qdoc b/examples/quick/animation/doc/src/animation.qdoc
index c2ae8c3dae..26e056b813 100644
--- a/examples/quick/animation/doc/src/animation.qdoc
+++ b/examples/quick/animation/doc/src/animation.qdoc
@@ -7,6 +7,7 @@
\brief This is a collection of QML Animation examples.
\image qml-animations-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e Animation is a collection of small QML examples relating to animation.
Each example is a small QML file emphasizing a particular type or feature.
@@ -53,7 +54,6 @@
\e{Tv Tennis} uses complex behaviors to make the paddles follow a ball to
simulate an infinite tennis game. Again, a binding which depends on other
values is applied to the position and a behavior provided the animation.
- \snippet animation/behaviors/tvtennis.qml 0
\section1 Easing Curves
diff --git a/examples/quick/animation/easing/easing.qml b/examples/quick/animation/easing/easing.qml
index 26272f3c0f..8b58bd5538 100644
--- a/examples/quick/animation/easing/easing.qml
+++ b/examples/quick/animation/easing/easing.qml
@@ -5,6 +5,8 @@ import QtQml
import QtQml.Models
import QtQuick
+pragma ComponentBehavior: Bound
+
Rectangle {
id: window
width: 320; height: 480; color: "#232323"
@@ -90,7 +92,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
MouseArea {
- onClicked: if (rect.state == '') rect.state = "right"; else rect.state = ''
+ onClicked: if (rect.state === '') rect.state = "right"; else rect.state = ''
anchors.fill: parent
anchors.margins: -5 // Make MouseArea bigger than the rectangle, itself
}
diff --git a/examples/quick/animation/pathanimation/pathanimation.qml b/examples/quick/animation/pathanimation/pathanimation.qml
index 1f3577a47c..b366f8178d 100644
--- a/examples/quick/animation/pathanimation/pathanimation.qml
+++ b/examples/quick/animation/pathanimation/pathanimation.qml
@@ -15,7 +15,7 @@ Rectangle {
antialiasing: true
onPaint: {
- var context = canvas.getContext("2d")
+ let context = canvas.getContext("2d")
context.clearRect(0, 0, width, height)
context.strokeStyle = "black"
context.path = pathAnim.path
@@ -65,7 +65,7 @@ Rectangle {
Text {
anchors.centerIn: parent
- text: "Box"
+ text: qsTr("Box")
}
}
}
diff --git a/examples/quick/animation/pathinterpolator/pathinterpolator.qml b/examples/quick/animation/pathinterpolator/pathinterpolator.qml
index a035e9ba0c..102aee9cee 100644
--- a/examples/quick/animation/pathinterpolator/pathinterpolator.qml
+++ b/examples/quick/animation/pathinterpolator/pathinterpolator.qml
@@ -15,7 +15,7 @@ Rectangle {
antialiasing: true
onPaint: {
- var context = canvas.getContext("2d")
+ let context = canvas.getContext("2d")
context.clearRect(0, 0, width, height)
context.strokeStyle = "black"
context.path = motionPath.path
@@ -71,7 +71,7 @@ Rectangle {
Text {
anchors.centerIn: parent
- text: "Box"
+ text: qsTr("Box")
}
}
}
diff --git a/examples/quick/animation/states/states.qml b/examples/quick/animation/states/states.qml
index 78a8c0668b..47d10287b1 100644
--- a/examples/quick/animation/states/states.qml
+++ b/examples/quick/animation/states/states.qml
@@ -1,7 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQml
import QtQuick
Rectangle {
diff --git a/examples/quick/canvas/CMakeLists.txt b/examples/quick/canvas/CMakeLists.txt
index 8a33e76fb9..b48770f0a6 100644
--- a/examples/quick/canvas/CMakeLists.txt
+++ b/examples/quick/canvas/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(canvas LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/canvas")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../shared" "shared")
@@ -23,15 +17,14 @@ qt_add_executable(canvasexample
)
target_link_libraries(canvasexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(canvasexample
URI canvas
- AUTO_RESOURCE_PREFIX
QML_FILES
"LabeledSlider.qml"
"bezierCurve/bezierCurve.qml"
@@ -49,9 +42,16 @@ qt_add_qml_module(canvasexample
)
install(TARGETS canvasexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(canvasexample)
+qt_generate_deploy_qml_app_script(
+ TARGET canvasexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/canvas/LabeledSlider.qml b/examples/quick/canvas/LabeledSlider.qml
index 5cab697b18..fa0ab98f78 100644
--- a/examples/quick/canvas/LabeledSlider.qml
+++ b/examples/quick/canvas/LabeledSlider.qml
@@ -7,32 +7,31 @@ import QtQuick.Controls
Item {
id: labeledSlider
property alias name: label.text
- implicitHeight: Math.max(label.implicitHeight, quickControlsSlider.implicitHeight)
property real min: 0.0
property real max: 1.0
- property real init: 0.0
- readonly property alias value: quickControlsSlider.value
+ property alias value: quickControlsSlider.value
+
+ implicitHeight: Math.max(label.implicitHeight, quickControlsSlider.implicitHeight)
Label {
id: label
anchors.left: parent.left
anchors.leftMargin: 10
- anchors.verticalCenter: parent.verticalCenter
- color: "#333"
+ anchors.verticalCenter: labeledSlider.verticalCenter
font: Qt.font({pointSize: 13})
}
Slider {
id: quickControlsSlider
- anchors.verticalCenter: parent.verticalCenter
- anchors.right: parent.right
- anchors.rightMargin: 10
- anchors.left: label.right
- anchors.leftMargin: 20
+ anchors {
+ verticalCenter: labeledSlider.verticalCenter
+ right: labeledSlider.right
+ rightMargin: 10
+ left: label.right
+ leftMargin: 10
+ }
from: labeledSlider.min
to: labeledSlider.max
width: labeledSlider.width - label.implicitWidth - (label.anchors.leftMargin + anchors.rightMargin + anchors.leftMargin)
-
- Component.onCompleted: ()=> value = labeledSlider.init;
}
}
diff --git a/examples/quick/canvas/bezierCurve/bezierCurve.qml b/examples/quick/canvas/bezierCurve/bezierCurve.qml
index 31ec4e60ef..0965a1e3db 100644
--- a/examples/quick/canvas/bezierCurve/bezierCurve.qml
+++ b/examples/quick/canvas/bezierCurve/bezierCurve.qml
@@ -6,94 +6,129 @@ import QtQuick.Controls
import "../"
Item {
- id:container
+ id: root
width: 320
height: 480
Column {
- spacing:5
- anchors.fill: parent
- anchors.topMargin: 12
+ spacing: 5
+ anchors {
+ fill: parent
+ topMargin: 12
+ }
Label {
- font.pointSize: 24
- font.bold: true
- text: "Bezier Curve"
anchors.horizontalCenter: parent.horizontalCenter
- color: "#777"
+ text: qsTr("Bezier Curve")
+ color: Qt.lighter(palette.text)
+ font {
+ pointSize: 24
+ bold: true
+ }
}
Canvas {
- id:canvas
- width:320
- height:280
- property color strokeStyle: Qt.darker(fillStyle, 1.4)
- property color fillStyle: "#b40000" // red
- property int lineWidth: lineWidthCtrl.value
- property bool fill: true
- property bool stroke: true
- property real alpha: 1.0
- property real scale : scaleCtrl.value
- property real rotate : rotateCtrl.value
+ id: canvas
+
+ readonly property color strokeStyle: Qt.darker(fillStyle, 1.4)
+ readonly property color fillStyle: "#b40000" // red
+ readonly property alias lineWidth: lineWidthCtrl.value
+ readonly property alias fill: toggleFillCheckBox.checked
+ readonly property alias stroke: toggleStrokeCheckBox.checked
+ readonly property real alpha: 1.0
+ readonly property alias scale: scaleCtrl.value
+ readonly property alias rotate: rotateCtrl.value
+
+ width: root.width
+ height: parent.height - controls.height
antialiasing: true
- onLineWidthChanged:requestPaint();
- onFillChanged:requestPaint();
- onStrokeChanged:requestPaint();
- onScaleChanged:requestPaint();
- onRotateChanged:requestPaint();
+ onLineWidthChanged: requestPaint()
+ onFillChanged: requestPaint()
+ onStrokeChanged: requestPaint()
+ onScaleChanged: requestPaint()
+ onRotateChanged: requestPaint()
- onPaint: {
- var ctx = canvas.getContext('2d');
- var originX = 85
- var originY = 75
- ctx.save();
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.translate(originX, originX);
- ctx.globalAlpha = canvas.alpha;
- ctx.strokeStyle = canvas.strokeStyle;
- ctx.fillStyle = canvas.fillStyle;
- ctx.lineWidth = canvas.lineWidth;
+ onPaint: function() {
+ let ctx = canvas.getContext('2d')
+ const originX = 85
+ const originY = 75
+ ctx.save()
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
+ ctx.translate(originX, originX)
+ ctx.globalAlpha = canvas.alpha
+ ctx.strokeStyle = canvas.strokeStyle
+ ctx.fillStyle = canvas.fillStyle
+ ctx.lineWidth = canvas.lineWidth
ctx.translate(originX, originY)
- ctx.scale(canvas.scale, canvas.scale);
- ctx.rotate(canvas.rotate);
+ ctx.scale(canvas.scale, canvas.scale)
+ ctx.rotate(canvas.rotate)
ctx.translate(-originX, -originY)
//! [0]
- ctx.beginPath();
- ctx.moveTo(75,40);
- ctx.bezierCurveTo(75,37,70,25,50,25);
- ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
- ctx.bezierCurveTo(20,80,40,102,75,120);
- ctx.bezierCurveTo(110,102,130,80,130,62.5);
- ctx.bezierCurveTo(130,62.5,130,25,100,25);
- ctx.bezierCurveTo(85,25,75,37,75,40);
- ctx.closePath();
+ ctx.beginPath()
+ ctx.moveTo(75,40)
+ ctx.bezierCurveTo(75,37,70,25,50,25)
+ ctx.bezierCurveTo(20,25,20,62.5,20,62.5)
+ ctx.bezierCurveTo(20,80,40,102,75,120)
+ ctx.bezierCurveTo(110,102,130,80,130,62.5)
+ ctx.bezierCurveTo(130,62.5,130,25,100,25)
+ ctx.bezierCurveTo(85,25,75,37,75,40)
+ ctx.closePath()
//! [0]
if (canvas.fill)
- ctx.fill();
+ ctx.fill()
if (canvas.stroke)
- ctx.stroke();
- ctx.restore();
+ ctx.stroke()
+ ctx.restore()
}
}
}
Column {
id: controls
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 12
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 12
+ }
LabeledSlider {
- id: lineWidthCtrl; name: "Outline"; min: 1; max: 10; init: 2; width: container.width
+ id: lineWidthCtrl
+ name: qsTr("Outline")
+ min: 1
+ max: 10
+ value: 2
+ width: root.width
}
LabeledSlider {
- id: scaleCtrl; name: "Scale"; min: 0.1; max: 10; init: 1; width: container.width
+ id: scaleCtrl
+ name: qsTr("Scale")
+ min: 0.1
+ max: 10
+ value: 1
+ width: root.width
}
LabeledSlider {
- id: rotateCtrl; name: "Rotate"; min: 0; max: Math.PI*2; init: 0; width: container.width
+ id: rotateCtrl
+ name: qsTr("Rotate")
+ min: 0
+ max: Math.PI * 2
+ value: 0
+ width: root.width
+ }
+ Row {
+ CheckBox {
+ id: toggleFillCheckBox
+ checked: true
+ text: qsTr("Toggle fill")
+ }
+ CheckBox {
+ id: toggleStrokeCheckBox
+ checked: true
+ text: qsTr("Toggle stroke")
+ }
}
}
}
diff --git a/examples/quick/canvas/canvas.qml b/examples/quick/canvas/canvas.qml
index 114941bbdc..6ac856eb6d 100644
--- a/examples/quick/canvas/canvas.qml
+++ b/examples/quick/canvas/canvas.qml
@@ -5,19 +5,26 @@ import QtQuick
import shared as Examples
Item {
+ id: root
height: 480
width: 320
Examples.LauncherList {
- id: ll
- anchors.fill: parent
+ anchors.fill: root
Component.onCompleted: {
- addExample("Red heart", "Draws a red heart with bezier curves", Qt.resolvedUrl("bezierCurve/bezierCurve.qml"));
- addExample("Talk bubble", "Draws a talk bubble with quadratic curves", Qt.resolvedUrl("quadraticCurveTo/quadraticCurveTo.qml"));
- addExample("Squircle", "Draws a smooth squircle with simple lines", Qt.resolvedUrl("squircle/squircle.qml"));
- addExample("Rounded rectangle", "Draws a rounded rectangle with lines and arcs", Qt.resolvedUrl("roundedrect/roundedrect.qml"));
- addExample("Smile face", "Draws a smile face with complex paths", Qt.resolvedUrl("smile/smile.qml"));
- addExample("Clip", "Shows the canvas clip feature", Qt.resolvedUrl("clip/clip.qml"));
- addExample("Tiger", "Draw a tiger with a collection of SVG paths", Qt.resolvedUrl("tiger/tiger.qml"));
+ addExample(qsTr("Red heart"), qsTr("Draws a red heart with bezier curves"),
+ Qt.resolvedUrl("bezierCurve/bezierCurve.qml"))
+ addExample(qsTr("Talk bubble"), qsTr("Draws a talk bubble with quadratic curves"),
+ Qt.resolvedUrl("quadraticCurveTo/quadraticCurveTo.qml"))
+ addExample(qsTr("Squircle"), qsTr("Draws a smooth squircle with simple lines"),
+ Qt.resolvedUrl("squircle/squircle.qml"))
+ addExample(qsTr("Rounded rectangle"), qsTr("Draws a rounded rectangle with lines and arcs"),
+ Qt.resolvedUrl("roundedrect/roundedrect.qml"))
+ addExample(qsTr("Smile face"), qsTr("Draws a smile face with complex paths"),
+ Qt.resolvedUrl("smile/smile.qml"))
+ addExample(qsTr("Clip"), qsTr("Shows the canvas clip feature"),
+ Qt.resolvedUrl("clip/clip.qml"))
+ addExample(qsTr("Tiger"), qsTr("Draw a tiger with a collection of SVG paths"),
+ Qt.resolvedUrl("tiger/tiger.qml"))
}
}
}
diff --git a/examples/quick/canvas/clip/clip.qml b/examples/quick/canvas/clip/clip.qml
index c39da7d108..3d0591f97a 100644
--- a/examples/quick/canvas/clip/clip.qml
+++ b/examples/quick/canvas/clip/clip.qml
@@ -6,108 +6,137 @@ import QtQuick.Controls
import "../"
Item {
- id:container
+ id: root
width: 320
height: 480
Column {
- spacing:5
- anchors.fill: parent
- anchors.topMargin: 12
+ spacing: 5
+ anchors {
+ fill: root
+ topMargin: 12
+ }
Label {
- font.pointSize: 24
- font.bold: true
- text: "Squircle with Clip"
anchors.horizontalCenter: parent.horizontalCenter
- color: "#777"
+ text: qsTr("Squircle with Clip")
+ color: Qt.lighter(palette.text)
+ font {
+ pointSize: 24
+ bold: true
+ }
}
Canvas {
id: canvas
- width: 320
- height: 280
- property color strokeStyle: Qt.darker(fillStyle, 1.2)
- property color fillStyle:"#14aaff" // green
- property int lineWidth:2
- property int nSize:nCtrl.value
- property real radius:rCtrl.value
- property bool fill:true
- property bool stroke:false
- property real px: width/2
- property real py: height/2 + 20
- property real alpha: 1.0
- property string imagefile:"../contents/qt-logo.png"
+
+ readonly property color strokeStyle: Qt.darker(fillStyle, 1.2)
+ readonly property color fillStyle: "#14aaff" // green
+ readonly property int lineWidth: 2
+ readonly property alias nSize: nCtrl.value
+ readonly property alias radius: rCtrl.value
+ readonly property alias fill: toggleFillCheckBox.checked
+ readonly property alias stroke: toggleStrokeCheckBox.checked
+ readonly property real px: width / 2
+ readonly property real py: height / 2 + 20
+ readonly property real alpha: 1.0
+ readonly property string imagefile: "../contents/qt-logo.png"
+
+ width: root.width
+ height: parent.height - controls.height
antialiasing: true
- Component.onCompleted:loadImage(canvas.imagefile);
-
- onRadiusChanged:requestPaint();
- onLineWidthChanged:requestPaint();
- onNSizeChanged:requestPaint();
- onFillChanged:requestPaint();
- onStrokeChanged:requestPaint();
- onImageLoaded : requestPaint();
- onPaint: squcirle();
-
- function squcirle() {
- var ctx = canvas.getContext("2d");
- var N = canvas.nSize;
- var R = canvas.radius;
-
- N=Math.abs(N);
- var M=N;
- if (N>100) M=100;
- if (N<0.00000000001) M=0.00000000001;
-
- ctx.save();
- ctx.globalAlpha = canvas.alpha;
- ctx.fillStyle = "white";
- ctx.fillRect(0, 0, canvas.width, canvas.height);
-
- ctx.strokeStyle = canvas.strokeStyle;
- ctx.fillStyle = canvas.fillStyle;
- ctx.lineWidth = canvas.lineWidth;
-
- ctx.beginPath();
- var i = 0, x, y;
- for (i=0; i<(2*R+1); i++){
- x = Math.round(i-R) + canvas.px;
- y = Math.round(Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(i-R),M)),1/M)) + canvas.py;
-
- if (i == 0)
- ctx.moveTo(x, y);
+
+ Component.onCompleted: loadImage(canvas.imagefile)
+ onRadiusChanged: requestPaint()
+ onLineWidthChanged: requestPaint()
+ onNSizeChanged: requestPaint()
+ onFillChanged: requestPaint()
+ onStrokeChanged: requestPaint()
+ onImageLoaded: requestPaint()
+
+ onPaint: function() {
+ let ctx = canvas.getContext("2d")
+ const N = Math.abs(canvas.nSize)
+ const R = canvas.radius
+
+ const M = Math.max(0.00000000001, Math.min(N, 100))
+
+ ctx.save()
+ ctx.globalAlpha = canvas.alpha
+ ctx.fillStyle = "white"
+ ctx.fillRect(0, 0, canvas.width, canvas.height)
+
+ ctx.strokeStyle = canvas.strokeStyle
+ ctx.fillStyle = canvas.fillStyle
+ ctx.lineWidth = canvas.lineWidth
+
+ ctx.beginPath()
+ let i, x, y
+ for (i = 0; i < (2 * R + 1); i++) {
+ x = Math.round(i - R) + canvas.px
+ y = Math.round(Math.pow(Math.abs(Math.pow(R, M) - Math.pow(Math.abs(i - R), M)), 1 / M)) + canvas.py
+
+ if (i === 0)
+ ctx.moveTo(x, y)
else
- ctx.lineTo(x, y);
+ ctx.lineTo(x, y)
}
- for (i=(2*R); i<(4*R+1); i++){
- x =Math.round(3*R-i)+canvas.px;
- y = Math.round(-Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(3*R-i),M)),1/M)) + canvas.py;
- ctx.lineTo(x, y);
+ for (i = (2 * R); i < (4 * R + 1); i++) {
+ x = Math.round(3 * R - i) + canvas.px
+ y = Math.round(-Math.pow(Math.abs(Math.pow(R, M) - Math.pow(Math.abs(3 * R - i), M)), 1 / M)) + canvas.py
+ ctx.lineTo(x, y)
}
- ctx.closePath();
+ ctx.closePath()
if (canvas.stroke) {
- ctx.stroke();
+ ctx.stroke()
}
if (canvas.fill) {
- ctx.fill();
+ ctx.fill()
}
//! [0]
- ctx.clip();
- ctx.drawImage(canvas.imagefile, 0, 0);
+ ctx.clip()
+ ctx.drawImage(canvas.imagefile, 0, 0)
//! [0]
- ctx.restore();
+ ctx.restore()
}
}
}
Column {
id: controls
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 12
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 12
+ }
- LabeledSlider {id: nCtrl; min: 1; max: 10; init: 4; name: "N"; width: container.width}
- LabeledSlider {id: rCtrl; min: 30; max: 180; init: 80; name: "Radius"; width: container.width}
+ LabeledSlider {
+ id: nCtrl
+ name: qsTr("N")
+ width: root.width
+ min: 1
+ max: 10
+ value: 4
+ }
+ LabeledSlider {
+ id: rCtrl
+ name: qsTr("Radius")
+ width: root.width
+ min: 30
+ max: 180
+ value: 80
+ }
+ Row {
+ CheckBox {
+ id: toggleFillCheckBox
+ checked: true
+ text: qsTr("Toggle fill")
+ }
+ CheckBox {
+ id: toggleStrokeCheckBox
+ text: qsTr("Toggle stroke")
+ }
+ }
}
}
diff --git a/examples/quick/canvas/doc/src/canvas.qdoc b/examples/quick/canvas/doc/src/canvas.qdoc
index 862925fc08..87a7c7b5c4 100644
--- a/examples/quick/canvas/doc/src/canvas.qdoc
+++ b/examples/quick/canvas/doc/src/canvas.qdoc
@@ -7,6 +7,7 @@
\brief This is a collection of QML Canvas examples.
\image qml-canvas-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e Canvas is a collection of small QML examples relating to the \l Canvas
type. Each example is a small QML file emphasizing a particular type or
diff --git a/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml b/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml
index 032319036b..0f788891ab 100644
--- a/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml
+++ b/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml
@@ -6,94 +6,133 @@ import QtQuick.Controls
import "../"
Item {
- id:container
- width:320
- height:480
+ id: root
+ width: 320
+ height: 480
Column {
- spacing:5
- anchors.fill: parent
- anchors.topMargin: 12
+ spacing: 5
+ anchors {
+ fill: parent
+ topMargin: 12
+ }
Label {
- font.pointSize: 24
- font.bold: true
- text: "Quadratic Curve"
anchors.horizontalCenter: parent.horizontalCenter
- color: "#777"
+ text: qsTr("Quadratic Curve")
+ color: Qt.lighter(palette.text)
+ font {
+ pointSize: 24
+ bold: true
+ }
}
Canvas {
id: canvas
- width: 320
- height: 280
- property color strokeStyle: Qt.darker(fillStyle, 1.3)
- property color fillStyle: "#14aaff" // blue
- property int lineWidth: lineWidthCtrl.value
- property bool fill: true
- property bool stroke: true
- property real alpha: 1.0
- property real scale : scaleCtrl.value
- property real rotate : rotateCtrl.value
+ readonly property color strokeStyle: Qt.darker(fillStyle, 1.3)
+ readonly property color fillStyle: "#14aaff" // blue
+ readonly property alias lineWidth: lineWidthCtrl.value
+ readonly property alias fill: toggleFillCheckBox.checked
+ readonly property alias stroke: toggleStrokeCheckBox.checked
+ readonly property alias scale: scaleCtrl.value
+ readonly property alias rotate: rotateCtrl.value
+ width: root.width
+ height: parent.height - controls.height
antialiasing: true
- onLineWidthChanged:requestPaint();
- onFillChanged:requestPaint();
- onStrokeChanged:requestPaint();
- onScaleChanged:requestPaint();
- onRotateChanged:requestPaint();
+ onLineWidthChanged: requestPaint()
+ onFillChanged: requestPaint()
+ onStrokeChanged: requestPaint()
+ onScaleChanged: requestPaint()
+ onRotateChanged: requestPaint()
- onPaint: {
- var ctx = canvas.getContext('2d');
- var originX = 75
- var originY = 75
+ onPaint: function() {
+ let ctx = canvas.getContext('2d')
+ const originX = 75
+ const originY = 75
- ctx.save();
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.translate(originX, originX);
- ctx.strokeStyle = canvas.strokeStyle;
- ctx.fillStyle = canvas.fillStyle;
- ctx.lineWidth = canvas.lineWidth;
+ ctx.save()
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
+ ctx.translate(originX, originX)
+ ctx.strokeStyle = canvas.strokeStyle
+ ctx.fillStyle = canvas.fillStyle
+ ctx.lineWidth = canvas.lineWidth
ctx.translate(originX, originY)
- ctx.rotate(canvas.rotate);
- ctx.scale(canvas.scale, canvas.scale);
+ ctx.rotate(canvas.rotate)
+ ctx.scale(canvas.scale, canvas.scale)
ctx.translate(-originX, -originY)
// ![0]
- ctx.beginPath();
- ctx.moveTo(75,25);
- ctx.quadraticCurveTo(25,25,25,62.5);
- ctx.quadraticCurveTo(25,100,50,100);
- ctx.quadraticCurveTo(50,120,30,125);
- ctx.quadraticCurveTo(60,120,65,100);
- ctx.quadraticCurveTo(125,100,125,62.5);
- ctx.quadraticCurveTo(125,25,75,25);
- ctx.closePath();
+ ctx.beginPath()
+ ctx.moveTo(75, 25)
+ ctx.quadraticCurveTo(25, 25, 25, 62.5)
+ ctx.quadraticCurveTo(25, 100, 50, 100)
+ ctx.quadraticCurveTo(50, 120, 30, 125)
+ ctx.quadraticCurveTo(60, 120, 65, 100)
+ ctx.quadraticCurveTo(125, 100, 125, 62.5)
+ ctx.quadraticCurveTo(125, 25, 75, 25)
+ ctx.closePath()
// ![0]
if (canvas.fill)
- ctx.fill();
+ ctx.fill()
if (canvas.stroke)
- ctx.stroke();
+ ctx.stroke()
// ![1]
- ctx.fillStyle = "white";
- ctx.font = "bold 17px sans-serif";
- ctx.fillText("Qt Quick", 40, 70);
+ ctx.fillStyle = "white"
+ ctx.font = "bold 17px sans-serif"
+ ctx.fillText("Qt Quick", 40, 70)
// ![1]
- ctx.restore();
+ ctx.restore()
}
}
}
Column {
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 12
+ id: controls
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 12
+ }
- LabeledSlider {id:lineWidthCtrl; min:1; max:10; init:2; name: "Outline"; width: container.width}
- LabeledSlider {id:scaleCtrl; min:0.1; max:10; init:1; name: "Scale"; width: container.width}
- LabeledSlider {id:rotateCtrl; min:0; max:Math.PI*2; init:0; name: "Rotate"; width: container.width}
+ LabeledSlider {
+ id: lineWidthCtrl
+ name: qsTr("Outline")
+ width: root.width
+ min: 1
+ max: 10
+ value: 2
+ }
+ LabeledSlider {
+ id: scaleCtrl
+ name: qsTr("Scale")
+ width: root.width
+ min: 0.1
+ max: 10
+ value: 1
+ }
+ LabeledSlider {
+ id: rotateCtrl
+ name: qsTr("Rotate")
+ width: root.width
+ min: 0
+ max: Math.PI * 2
+ value: 0
+ }
+ Row {
+ CheckBox {
+ id: toggleFillCheckBox
+ checked: true
+ text: qsTr("Toggle fill")
+ }
+ CheckBox {
+ id: toggleStrokeCheckBox
+ checked: true
+ text: qsTr("Toggle stroke")
+ }
+ }
}
}
diff --git a/examples/quick/canvas/roundedrect/roundedrect.qml b/examples/quick/canvas/roundedrect/roundedrect.qml
index 6966528402..26861b921c 100644
--- a/examples/quick/canvas/roundedrect/roundedrect.qml
+++ b/examples/quick/canvas/roundedrect/roundedrect.qml
@@ -6,81 +6,115 @@ import QtQuick.Controls
import "../"
Item {
- id:container
+ id: root
width: 320
height: 480
Column {
spacing: 6
- anchors.fill: parent
- anchors.topMargin: 12
+ anchors {
+ fill: parent
+ topMargin: 12
+ }
Label {
- font.pointSize: 24
- font.bold: true
- text: "Rounded rectangle"
anchors.horizontalCenter: parent.horizontalCenter
- color: "#777"
+ text: qsTr("Rounded rectangle")
+ color: Qt.lighter(palette.text)
+ font {
+ pointSize: 24
+ bold: true
+ }
}
Canvas {
id: canvas
- width: 320
- height: 280
- antialiasing: true
- property int radius: rCtrl.value
- property int rectx: 60
- property int recty: 60
- property int rectWidth: width - 2*rectx
- property int rectHeight: height - 2*recty
- property color strokeStyle: Qt.darker(fillStyle, 1.4)
- property color fillStyle: "#ae32a0" // purple
- property int lineWidth: lineWidthCtrl.value
- property bool fill: true
- property bool stroke: true
- property real alpha: 1.0
+ readonly property alias radius: rCtrl.value
+ readonly property int rectx: 60
+ readonly property int recty: 60
+ readonly property int rectWidth: width - 2 * rectx
+ readonly property int rectHeight: height - 2 * recty
+ readonly property color strokeStyle: Qt.darker(fillStyle, 1.4)
+ readonly property color fillStyle: "#ae32a0" // purple
+ readonly property alias lineWidth: lineWidthCtrl.value
+ readonly property alias fill: toggleFillCheckBox.checked
+ readonly property alias stroke: toggleStrokeCheckBox.checked
+ readonly property real alpha: 1.0
- onLineWidthChanged:requestPaint();
- onFillChanged:requestPaint();
- onStrokeChanged:requestPaint();
- onRadiusChanged:requestPaint();
+ width: root.width
+ height: parent.height - controls.height
+ antialiasing: true
+
+ onLineWidthChanged: requestPaint()
+ onFillChanged: requestPaint()
+ onStrokeChanged: requestPaint()
+ onRadiusChanged: requestPaint()
- onPaint: {
- var ctx = getContext("2d");
- ctx.save();
- ctx.clearRect(0,0,canvas.width, canvas.height);
- ctx.strokeStyle = canvas.strokeStyle;
+ onPaint: function() {
+ var ctx = getContext("2d")
+ ctx.save()
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
+ ctx.strokeStyle = canvas.strokeStyle
ctx.lineWidth = canvas.lineWidth
ctx.fillStyle = canvas.fillStyle
ctx.globalAlpha = canvas.alpha
- ctx.beginPath();
- ctx.moveTo(rectx+radius,recty); // top side
- ctx.lineTo(rectx+rectWidth-radius,recty);
+ ctx.beginPath()
+ ctx.moveTo(rectx + radius, recty) // top side
+ ctx.lineTo(rectx + rectWidth - radius, recty)
// draw top right corner
- ctx.arcTo(rectx+rectWidth,recty,rectx+rectWidth,recty+radius,radius);
- ctx.lineTo(rectx+rectWidth,recty+rectHeight-radius); // right side
+ ctx.arcTo(rectx + rectWidth, recty, rectx + rectWidth, recty + radius, radius)
+ ctx.lineTo(rectx + rectWidth, recty + rectHeight - radius) // right side
// draw bottom right corner
- ctx.arcTo(rectx+rectWidth,recty+rectHeight,rectx+rectWidth-radius,recty+rectHeight,radius);
- ctx.lineTo(rectx+radius,recty+rectHeight); // bottom side
+ ctx.arcTo(rectx + rectWidth, recty + rectHeight, rectx + rectWidth - radius, recty + rectHeight, radius)
+ ctx.lineTo(rectx + radius, recty + rectHeight) // bottom side
// draw bottom left corner
- ctx.arcTo(rectx,recty+rectHeight,rectx,recty+rectHeight-radius,radius);
- ctx.lineTo(rectx,recty+radius); // left side
+ ctx.arcTo(rectx, recty + rectHeight, rectx, recty + rectHeight - radius, radius)
+ ctx.lineTo(rectx, recty + radius) // left side
// draw top left corner
- ctx.arcTo(rectx,recty,rectx+radius,recty,radius);
- ctx.closePath();
+ ctx.arcTo(rectx, recty, rectx + radius, recty, radius)
+ ctx.closePath()
if (canvas.fill)
- ctx.fill();
+ ctx.fill()
if (canvas.stroke)
- ctx.stroke();
- ctx.restore();
+ ctx.stroke()
+ ctx.restore()
}
}
}
Column {
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 12
+ id: controls
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 12
+ }
- LabeledSlider {id: lineWidthCtrl ; min: 1 ; max: 10; init: 2 ; name: "Outline"; width: container.width}
- LabeledSlider {id: rCtrl ; min: 10 ; max: 80 ; init: 40 ; name: "Radius"; width: container.width}
+ LabeledSlider {
+ id: lineWidthCtrl
+ name: qsTr("Outline")
+ width: root.width
+ min: 1
+ max: 10
+ value: 2
+ }
+ LabeledSlider {
+ id: rCtrl
+ name: qsTr("Radius")
+ width: root.width
+ min: 10
+ max: 80
+ value: 40
+ }
+ Row {
+ CheckBox {
+ id: toggleFillCheckBox
+ checked: true
+ text: qsTr("Toggle fill")
+ }
+ CheckBox {
+ id: toggleStrokeCheckBox
+ checked: true
+ text: qsTr("Toggle stroke")
+ }
+ }
}
}
diff --git a/examples/quick/canvas/smile/smile.qml b/examples/quick/canvas/smile/smile.qml
index 0d2034d9ca..bf534a3da2 100644
--- a/examples/quick/canvas/smile/smile.qml
+++ b/examples/quick/canvas/smile/smile.qml
@@ -6,87 +6,124 @@ import QtQuick.Controls
import "../"
Item {
- id: container
+ id: root
width: 320
height: 480
Column {
spacing: 6
- anchors.fill: parent
- anchors.topMargin: 12
+ anchors {
+ fill: parent
+ topMargin: 12
+ }
Label {
- font.pointSize: 24
- font.bold: true
- text: "Smile with arcs"
anchors.horizontalCenter: parent.horizontalCenter
- color: "#777"
+ text: qsTr("Smile with arcs")
+ font {
+ pointSize: 24
+ bold: true
+ }
+ color: Qt.lighter(palette.text)
}
Canvas {
id: canvas
- width: 320
+
+ readonly property color strokeStyle: Qt.darker(fillStyle, 1.6)
+ readonly property color fillStyle: "#e0c31e" // yellow
+ readonly property alias lineWidth: lineWidthCtrl.value
+ readonly property alias fill: toggleFillCheckBox.checked
+ readonly property alias stroke: toggleStrokeCheckBox.checked
+ readonly property alias scale: scaleCtrl.value
+ readonly property alias rotate: rotateCtrl.value
+
+ width: root.width
height: parent.height - controls.height
antialiasing: true
- property color strokeStyle: Qt.darker(fillStyle, 1.6)
- property color fillStyle: "#e0c31e" // yellow
- property int lineWidth: lineWidthCtrl.value
- property bool fill: true
- property bool stroke: true
- property real scale : scaleCtrl.value
- property real rotate : rotateCtrl.value
-
- onLineWidthChanged:requestPaint();
- onFillChanged:requestPaint();
- onStrokeChanged:requestPaint();
- onScaleChanged:requestPaint();
- onRotateChanged:requestPaint();
+ onLineWidthChanged: requestPaint()
+ onFillChanged: requestPaint()
+ onStrokeChanged: requestPaint()
+ onScaleChanged: requestPaint()
+ onRotateChanged: requestPaint()
onPaint: {
- var ctx = canvas.getContext('2d');
- var originX = 85
- var originY = 75
- ctx.save();
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.translate(originX, originX);
- ctx.strokeStyle = canvas.strokeStyle;
- ctx.fillStyle = canvas.fillStyle;
- ctx.lineWidth = canvas.lineWidth;
+ let ctx = canvas.getContext('2d')
+ const originX = 85
+ const originY = 75
+ ctx.save()
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
+ ctx.translate(originX, originX)
+ ctx.strokeStyle = canvas.strokeStyle
+ ctx.fillStyle = canvas.fillStyle
+ ctx.lineWidth = canvas.lineWidth
ctx.translate(originX, originY)
- ctx.scale(canvas.scale, canvas.scale);
- ctx.rotate(canvas.rotate);
+ ctx.scale(canvas.scale, canvas.scale)
+ ctx.rotate(canvas.rotate)
ctx.translate(-originX, -originY)
- ctx.beginPath();
- ctx.moveTo(75 + 50 * Math.cos(0),
- 75 - 50 * Math.sin(Math.PI*2));
- ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
- ctx.moveTo(60,60);
- ctx.arc(60,60,5,0,Math.PI*2,true); // Left eye
- ctx.moveTo(90 + 5 * Math.cos(0),
- 65 - 5 * Math.sin(Math.PI*2));
- ctx.moveTo(90,60);
- ctx.arc(90,60,5,-Math.PI,Math.PI*3,false); // Right eye
- ctx.moveTo(75,70);
- ctx.arc(75,70,35,0,Math.PI,false); // Mouth (clockwise)
- ctx.closePath();
+ ctx.beginPath()
+ ctx.moveTo(75 + 50 * Math.cos(0), 75 - 50 * Math.sin(Math.PI * 2))
+ ctx.arc(75, 75, 50, 0, Math.PI * 2, true) // Outer circle
+ ctx.moveTo(60, 60)
+ ctx.arc(60, 60, 5, 0, Math.PI * 2, true) // Left eye
+ ctx.moveTo(90 + 5 * Math.cos(0), 65 - 5 * Math.sin(Math.PI * 2))
+ ctx.moveTo(90, 60)
+ ctx.arc(90, 60, 5, -Math.PI, Math.PI * 3, false) // Right eye
+ ctx.moveTo(75, 70)
+ ctx.arc(75, 70, 35, 0, Math.PI, false) // Mouth (clockwise)
+ ctx.closePath()
if (canvas.fill)
- ctx.fill();
+ ctx.fill()
if (canvas.stroke)
- ctx.stroke();
- ctx.restore();
+ ctx.stroke()
+ ctx.restore()
}
}
}
Column {
id: controls
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 12
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 12
+ }
- LabeledSlider {id: lineWidthCtrl ; min: 1 ; max: 10 ; init: 2 ; name: "Outline"; width: container.width}
- LabeledSlider {id: scaleCtrl ; min: 0.1 ; max: 10 ; init: 1 ; name: "Scale"; width: container.width}
- LabeledSlider {id: rotateCtrl ; min: 0 ; max: Math.PI*2 ; init: 0 ; name: "Rotate"; width: container.width}
+ LabeledSlider {
+ id: lineWidthCtrl
+ name: qsTr("Outline")
+ width: root.width
+ min: 1
+ max: 10
+ value: 2
+ }
+ LabeledSlider {
+ id: scaleCtrl
+ name: qsTr("Scale")
+ width: root.width
+ min: 0.1
+ max: 10
+ value: 1
+ }
+ LabeledSlider {
+ id: rotateCtrl
+ name: qsTr("Rotate")
+ width: root.width
+ min: 0
+ max: Math.PI * 2
+ value: 0
+ }
+ Row {
+ CheckBox {
+ id: toggleFillCheckBox
+ text: qsTr("Toggle fill")
+ }
+ CheckBox {
+ id: toggleStrokeCheckBox
+ checked: true
+ text: qsTr("Toggle stroke")
+ }
+ }
}
}
diff --git a/examples/quick/canvas/squircle/squircle.qml b/examples/quick/canvas/squircle/squircle.qml
index dcbc6fe517..3e4755e02c 100644
--- a/examples/quick/canvas/squircle/squircle.qml
+++ b/examples/quick/canvas/squircle/squircle.qml
@@ -6,21 +6,25 @@ import QtQuick.Controls
import "../"
Item {
- id: container
+ id: root
width: 320
height: 480
Column {
spacing: 6
- anchors.fill: parent
- anchors.topMargin: 12
+ anchors {
+ fill: parent
+ topMargin: 12
+ }
Label {
- font.pointSize: 24
- font.bold: true
- text: "Squircles"
anchors.horizontalCenter: parent.horizontalCenter
- color: "#777"
+ text: qsTr("Squircles")
+ color: Qt.lighter(palette.text)
+ font {
+ pointSize: 24
+ bold: true
+ }
}
Image {
@@ -32,84 +36,106 @@ Item {
Canvas {
id: canvas
- width: 320
- height: 250
+
+ readonly property color strokeStyle: Qt.darker(fillStyle, 1.2)
+ readonly property color fillStyle: "#6400aa"
+ readonly property int lineWidth: 2
+ readonly property alias nSize: nCtrl.value
+ readonly property alias radius: rCtrl.value
+ readonly property alias fill: toggleFillCheckBox.checked
+ readonly property alias stroke: toggleStrokeCheckBox.checked
+ readonly property real px: width / 2
+ readonly property real py: height / 2 + 10
+ readonly property real alpha: 1.0
+
+ width: root.width
+ height: parent.height - controls.height
antialiasing: true
- property color strokeStyle: Qt.darker(fillStyle, 1.2)
- property color fillStyle: "#6400aa"
-
- property int lineWidth: 2
- property int nSize: nCtrl.value
- property real radius: rCtrl.value
- property bool fill: true
- property bool stroke: false
- property real px: width/2
- property real py: height/2 + 10
- property real alpha: 1.0
-
- onRadiusChanged: requestPaint();
- onLineWidthChanged: requestPaint();
- onNSizeChanged: requestPaint();
- onFillChanged: requestPaint();
- onStrokeChanged: requestPaint();
-
- onPaint: squircle();
-
- function squircle() {
- var ctx = canvas.getContext("2d");
- var N = canvas.nSize;
- var R = canvas.radius;
-
- N=Math.abs(N);
- var M=N;
- if (N>100) M=100;
- if (N<0.00000000001) M=0.00000000001;
-
- ctx.save();
- ctx.globalAlpha =canvas.alpha;
- ctx.fillStyle = "white";
- ctx.fillRect(0, 0, canvas.width, canvas.height);
-
- ctx.strokeStyle = canvas.strokeStyle;
- ctx.fillStyle = canvas.fillStyle;
- ctx.lineWidth = canvas.lineWidth;
-
- ctx.beginPath();
- var i = 0, x, y;
- for (i=0; i<(2*R+1); i++){
- x = Math.round(i-R) + canvas.px;
- y = Math.round(Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(i-R),M)),1/M)) + canvas.py;
-
- if (i == 0)
- ctx.moveTo(x, y);
+ onRadiusChanged: requestPaint()
+ onLineWidthChanged: requestPaint()
+ onNSizeChanged: requestPaint()
+ onFillChanged: requestPaint()
+ onStrokeChanged: requestPaint()
+
+ onPaint: function () {
+ let ctx = canvas.getContext("2d")
+ const N = Math.abs(canvas.nSize)
+ const R = canvas.radius
+
+ const M = Math.max(0.00000000001, Math.min(N, 100))
+
+ ctx.save()
+ ctx.globalAlpha = canvas.alpha
+ ctx.fillStyle = "white"
+ ctx.fillRect(0, 0, canvas.width, canvas.height)
+
+ ctx.strokeStyle = canvas.strokeStyle
+ ctx.fillStyle = canvas.fillStyle
+ ctx.lineWidth = canvas.lineWidth
+
+ ctx.beginPath()
+ let i, x, y;
+ for (i = 0; i < (2 * R + 1); i++) {
+ x = Math.round(i - R) + canvas.px
+ y = Math.round(Math.pow(Math.abs(Math.pow(R, M) - Math.pow(Math.abs(i - R), M)), 1 / M)) + canvas.py
+
+ if (i === 0)
+ ctx.moveTo(x, y)
else
- ctx.lineTo(x, y);
+ ctx.lineTo(x, y)
}
- for (i=(2*R); i<(4*R+1); i++){
- x =Math.round(3*R-i)+canvas.px;
- y = Math.round(-Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(3*R-i),M)),1/M)) + canvas.py;
- ctx.lineTo(x, y);
+ for (i = (2 * R); i < (4 * R + 1); i++) {
+ x = Math.round(3 * R - i) + canvas.px
+ y = Math.round(-Math.pow(Math.abs(Math.pow(R, M) - Math.pow(Math.abs(3 * R - i), M)), 1 / M)) + canvas.py
+ ctx.lineTo(x, y)
}
- ctx.closePath();
+ ctx.closePath()
if (canvas.stroke) {
- ctx.stroke();
+ ctx.stroke()
}
-
if (canvas.fill) {
- ctx.fill();
+ ctx.fill()
}
- ctx.restore();
+ ctx.restore()
}
}
}
Column {
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 12
+ id: controls
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 12
+ }
- LabeledSlider {id: nCtrl ; min: 1 ; max: 10 ; init: 2 ; name: "N"; width: container.width}
- LabeledSlider {id: rCtrl ; min: 30 ; max: 180 ; init: 60 ; name: "Radius"; width: container.width}
+ LabeledSlider {
+ id: nCtrl
+ name: qsTr("N")
+ width: root.width
+ min: 1
+ max: 10
+ value: 2
+ }
+ LabeledSlider {
+ id: rCtrl
+ name: qsTr("Radius")
+ width: root.width
+ min: 30
+ max: 180
+ value: 60
+ }
+ Row {
+ CheckBox {
+ id: toggleFillCheckBox
+ text: qsTr("Toggle fill")
+ }
+ CheckBox {
+ id: toggleStrokeCheckBox
+ checked: true
+ text: qsTr("Toggle stroke")
+ }
+ }
}
}
diff --git a/examples/quick/canvas/tiger/tiger.qml b/examples/quick/canvas/tiger/tiger.qml
index 7110f97f56..e936619411 100644
--- a/examples/quick/canvas/tiger/tiger.qml
+++ b/examples/quick/canvas/tiger/tiger.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
@@ -7,92 +7,113 @@ import "../"
import "tiger.js" as Tiger
Item {
- id: container
+ id: root
width: 320
height: 480
Column {
spacing: 6
- anchors.fill: parent
- anchors.topMargin: 12
+ anchors {
+ fill: parent
+ topMargin: 12
+ }
Label {
- font.pointSize: 24
- font.bold: true
- text: "Tiger with SVG path"
anchors.horizontalCenter: parent.horizontalCenter
- color: "#777"
+ text: qsTr("Tiger with SVG path")
+ color: Qt.lighter(palette.text)
+ font {
+ pointSize: 24
+ bold: true
+ }
}
Canvas {
id: canvas
- width: 320
- height: 280
+
+ readonly property real alpha: alphaCtrl.value
+ readonly property real scale: scaleCtrl.value
+ readonly property real rotate: rotateCtrl.value
+
+ width: root.width
+ height: parent.height - controls.height
antialiasing: true
- property string strokeStyle: "steelblue"
- property string fillStyle: "yellow"
- property bool fill: true
- property bool stroke: true
- property real alpha: alphaCtrl.value
- property real scale: scaleCtrl.value
- property real rotate: rotateCtrl.value
- property int frame: 0
-
- onFillChanged: requestPaint();
- onStrokeChanged: requestPaint();
- onAlphaChanged: requestPaint();
- onScaleChanged: requestPaint();
- onRotateChanged: requestPaint();
-
- onPaint: {
- var ctx = canvas.getContext('2d');
- var originX = canvas.width/2 + 30
- var originY = canvas.height/2 + 60
-
- ctx.save();
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.globalAlpha = canvas.alpha;
- ctx.globalCompositeOperation = "source-over";
+ onAlphaChanged: requestPaint()
+ onScaleChanged: requestPaint()
+ onRotateChanged: requestPaint()
+
+ onPaint: function() {
+ let ctx = canvas.getContext('2d')
+ const originX = canvas.width / 2 + 30
+ const originY = canvas.height / 2 + 60
+
+ ctx.save()
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
+ ctx.globalAlpha = canvas.alpha
+ ctx.globalCompositeOperation = "source-over"
ctx.translate(originX, originY)
- ctx.scale(canvas.scale, canvas.scale);
- ctx.rotate(canvas.rotate);
+ ctx.scale(canvas.scale, canvas.scale)
+ ctx.rotate(canvas.rotate)
ctx.translate(-originX, -originY)
- ctx.strokeStyle = Qt.rgba(.3, .3, .3,1);
- ctx.lineWidth = 1;
+ ctx.strokeStyle = Qt.rgba(.3, .3, .3,1)
+ ctx.lineWidth = 1
//! [0]
- for (var i = 0; i < Tiger.tiger.length; i++) {
- if (Tiger.tiger[i].width != undefined)
- ctx.lineWidth = Tiger.tiger[i].width;
+ for (let i = 0; i < Tiger.tiger.length; i++) {
+ if (Tiger.tiger[i].width !== undefined)
+ ctx.lineWidth = Tiger.tiger[i].width
- if (Tiger.tiger[i].path != undefined)
- ctx.path = Tiger.tiger[i].path;
+ if (Tiger.tiger[i].path !== undefined)
+ ctx.path = Tiger.tiger[i].path
- if (Tiger.tiger[i].fill != undefined) {
- ctx.fillStyle = Tiger.tiger[i].fill;
- ctx.fill();
+ if (Tiger.tiger[i].fill !== undefined) {
+ ctx.fillStyle = Tiger.tiger[i].fill
+ ctx.fill()
}
- if (Tiger.tiger[i].stroke != undefined) {
- ctx.strokeStyle = Tiger.tiger[i].stroke;
- ctx.stroke();
+ if (Tiger.tiger[i].stroke !== undefined) {
+ ctx.strokeStyle = Tiger.tiger[i].stroke
+ ctx.stroke()
}
}
//! [0]
- ctx.restore();
+ ctx.restore()
}
}
}
Column {
id: controls
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 12
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 12
+ }
- LabeledSlider {id: scaleCtrl ; min: 0.1 ; max: 1 ; init: 0.3 ; name: "Scale"; width: container.width}
- LabeledSlider {id: rotateCtrl ; min: 0 ; max: Math.PI*2 ; init: 0 ; name: "Rotate"; width: container.width}
- LabeledSlider {id: alphaCtrl ; min: 0 ; max: 1 ; init: 1 ; name: "Alpha"; width: container.width}
+ LabeledSlider {
+ id: scaleCtrl
+ name: qsTr("Scale")
+ width: root.width
+ min: 0.1
+ max: 1
+ value: 0.3
+ }
+ LabeledSlider {
+ id: rotateCtrl
+ name: qsTr("Rotate")
+ width: root.width
+ min: 0
+ max: Math.PI * 2
+ value: 0
+ }
+ LabeledSlider {
+ id: alphaCtrl
+ name: qsTr("Alpha")
+ width: root.width
+ min: 0
+ max: 1
+ value: 1
+ }
}
}
diff --git a/examples/quick/customitems/CMakeLists.txt b/examples/quick/customitems/CMakeLists.txt
index 8fad896269..92c5c8fe85 100644
--- a/examples/quick/customitems/CMakeLists.txt
+++ b/examples/quick/customitems/CMakeLists.txt
@@ -1,7 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(dialcontrol)
qt_internal_add_example(flipable)
qt_internal_add_example(painteditem)
-qt_internal_add_example(maskedmousearea)
diff --git a/examples/quick/customitems/customitems.pro b/examples/quick/customitems/customitems.pro
index 399c1dd126..3199bfc9ad 100644
--- a/examples/quick/customitems/customitems.pro
+++ b/examples/quick/customitems/customitems.pro
@@ -1,7 +1,6 @@
TEMPLATE = subdirs
SUBDIRS = \
- painteditem \
- maskedmousearea
+ painteditem
EXAMPLE_FILES = \
dialcontrol \
diff --git a/examples/quick/customitems/dialcontrol/CMakeLists.txt b/examples/quick/customitems/dialcontrol/CMakeLists.txt
index e714908c52..ff26205142 100644
--- a/examples/quick/customitems/dialcontrol/CMakeLists.txt
+++ b/examples/quick/customitems/dialcontrol/CMakeLists.txt
@@ -1,38 +1,30 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(dialcontrol LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/customitems/dialcontrol")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(dialcontrolexample
+ WIN32
+ MACOSX_BUNDLE
main.cpp
)
-set_target_properties(dialcontrolexample PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(dialcontrolexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+target_link_libraries(dialcontrolexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(dialcontrolexample
URI dialcontrol
- AUTO_RESOURCE_PREFIX
QML_FILES
"Dial.qml"
"QuitButton.qml"
@@ -46,7 +38,16 @@ qt_add_qml_module(dialcontrolexample
)
install(TARGETS dialcontrolexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET dialcontrolexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/customitems/dialcontrol/Dial.qml b/examples/quick/customitems/dialcontrol/Dial.qml
index 7e3b8cbd87..3b9692cc91 100644
--- a/examples/quick/customitems/dialcontrol/Dial.qml
+++ b/examples/quick/customitems/dialcontrol/Dial.qml
@@ -32,7 +32,7 @@ Item {
id: needleRotation
origin.x: 5; origin.y: 65
//! [needle angle]
- angle: Math.min(Math.max(-130, root.value*2.6 - 130), 133)
+ angle: Math.min(Math.max(-130, root.value * 2.6 - 130), 133)
Behavior on angle {
SpringAnimation {
spring: 1.4
diff --git a/examples/quick/customitems/dialcontrol/QuitButton.qml b/examples/quick/customitems/dialcontrol/QuitButton.qml
index af362b6ddf..841ff34ee8 100644
--- a/examples/quick/customitems/dialcontrol/QuitButton.qml
+++ b/examples/quick/customitems/dialcontrol/QuitButton.qml
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
+
Image {
source: "quit.png"
scale: quitMouse.pressed ? 0.8 : 1.0
diff --git a/examples/quick/customitems/dialcontrol/dialcontrol.qml b/examples/quick/customitems/dialcontrol/dialcontrol.qml
index d30f66fa2c..695ec9fb46 100644
--- a/examples/quick/customitems/dialcontrol/dialcontrol.qml
+++ b/examples/quick/customitems/dialcontrol/dialcontrol.qml
@@ -43,7 +43,7 @@ Rectangle {
return
}
- var desiredPercent = slider.x * 100 / (oldWidth - 32)
+ let desiredPercent = slider.x * 100 / (oldWidth - 32)
slider.x = desiredPercent * (width - 32) / 100
oldWidth = width
}
diff --git a/examples/quick/customitems/dialcontrol/doc/src/dialcontrol.qdoc b/examples/quick/customitems/dialcontrol/doc/src/dialcontrol.qdoc
index b6cdecf5df..7b56478c37 100644
--- a/examples/quick/customitems/dialcontrol/doc/src/dialcontrol.qdoc
+++ b/examples/quick/customitems/dialcontrol/doc/src/dialcontrol.qdoc
@@ -6,6 +6,7 @@
\title UI Components: Dial Control Example
\example customitems/dialcontrol
\brief The Dial Control Example shows how to create a speedometer-type dial.
+ \examplecategory {User Interface Components}
This example shows how to create a dial-type control. It combines
\l Image elements with \l Rotation transforms and \l SpringAnimation behaviors
diff --git a/examples/quick/customitems/flipable/CMakeLists.txt b/examples/quick/customitems/flipable/CMakeLists.txt
index 1e9bec27c5..8d1c7ab7cf 100644
--- a/examples/quick/customitems/flipable/CMakeLists.txt
+++ b/examples/quick/customitems/flipable/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
@@ -7,30 +7,25 @@ project(flipable LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/customitems/flipable")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(flipableexample
WIN32
MACOSX_BUNDLE
main.cpp
)
-target_link_libraries(flipableexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+target_link_libraries(flipableexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(flipableexample
URI flipable
- AUTO_RESOURCE_PREFIX
QML_FILES
"flipable.qml"
"Card.qml"
@@ -41,7 +36,16 @@ qt_add_qml_module(flipableexample
)
install(TARGETS flipableexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET flipableexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/customitems/flipable/doc/src/flipable.qdoc b/examples/quick/customitems/flipable/doc/src/flipable.qdoc
index 1870b2743b..8b2eea6611 100644
--- a/examples/quick/customitems/flipable/doc/src/flipable.qdoc
+++ b/examples/quick/customitems/flipable/doc/src/flipable.qdoc
@@ -5,6 +5,7 @@
\title UI Components: Flipable Example
\example customitems/flipable
\brief The Flipable Example shows an item that flips whenever clicked, rotating around the y-axis.
+ \examplecategory {User Interface Components}
This example shows how to use the \l Flipable element.
diff --git a/examples/quick/customitems/maskedmousearea/CMakeLists.txt b/examples/quick/customitems/maskedmousearea/CMakeLists.txt
deleted file mode 100644
index 8c768bbc56..0000000000
--- a/examples/quick/customitems/maskedmousearea/CMakeLists.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(maskedmousearea LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/customitems/maskedmousearea")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_add_executable(maskedmousearea
- main.cpp
- maskedmousearea.cpp maskedmousearea.h
-)
-
-set_target_properties(maskedmousearea PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(maskedmousearea PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-qt_add_qml_module(maskedmousearea
- URI Example
- QML_FILES
- maskedmousearea.qml
- RESOURCES
- images/cloud_1.png
- images/cloud_2.png
- images/moon.png
- RESOURCE_PREFIX /customitems/maskedmousearea
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS maskedmousearea
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/quick/customitems/maskedmousearea/main.cpp b/examples/quick/customitems/maskedmousearea/main.cpp
deleted file mode 100644
index 64cc1c62f2..0000000000
--- a/examples/quick/customitems/maskedmousearea/main.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QGuiApplication>
-#include <QQuickView>
-
-#include "maskedmousearea.h"
-
-int main(int argc, char* argv[])
-{
- QGuiApplication app(argc,argv);
- QQuickView view;
-
- view.setSource(QUrl("qrc:///customitems/maskedmousearea/maskedmousearea.qml"));
- view.show();
- return QGuiApplication::exec();
-}
diff --git a/examples/quick/customitems/maskedmousearea/maskedmousearea.cpp b/examples/quick/customitems/maskedmousearea/maskedmousearea.cpp
deleted file mode 100644
index 21dbec8cdd..0000000000
--- a/examples/quick/customitems/maskedmousearea/maskedmousearea.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "maskedmousearea.h"
-
-#include <QStyleHints>
-#include <QGuiApplication>
-#include <qqmlfile.h>
-
-MaskedMouseArea::MaskedMouseArea(QQuickItem *parent)
- : QQuickItem(parent),
- m_pressed(false),
- m_alphaThreshold(0.0),
- m_containsMouse(false)
-{
- setAcceptHoverEvents(true);
- setAcceptedMouseButtons(Qt::LeftButton);
-}
-
-void MaskedMouseArea::setPressed(bool pressed)
-{
- if (m_pressed != pressed) {
- m_pressed = pressed;
- emit pressedChanged();
- }
-}
-
-void MaskedMouseArea::setContainsMouse(bool containsMouse)
-{
- if (m_containsMouse != containsMouse) {
- m_containsMouse = containsMouse;
- emit containsMouseChanged();
- }
-}
-
-void MaskedMouseArea::setMaskSource(const QUrl &source)
-{
- if (m_maskSource != source) {
- m_maskSource = source;
- m_maskImage = QImage(QQmlFile::urlToLocalFileOrQrc(source));
- emit maskSourceChanged();
- }
-}
-
-void MaskedMouseArea::setAlphaThreshold(qreal threshold)
-{
- if (m_alphaThreshold != threshold) {
- m_alphaThreshold = threshold;
- emit alphaThresholdChanged();
- }
-}
-
-bool MaskedMouseArea::contains(const QPointF &point) const
-{
- if (!QQuickItem::contains(point) || m_maskImage.isNull())
- return false;
-
- QPoint p = point.toPoint();
-
- if (p.x() < 0 || p.x() >= m_maskImage.width() ||
- p.y() < 0 || p.y() >= m_maskImage.height())
- return false;
-
- qreal r = qBound<int>(0, m_alphaThreshold * 255, 255);
- return qAlpha(m_maskImage.pixel(p)) > r;
-}
-
-void MaskedMouseArea::mousePressEvent(QMouseEvent *event)
-{
- setPressed(true);
- m_pressPoint = event->position().toPoint();
- emit pressed();
-}
-
-void MaskedMouseArea::mouseReleaseEvent(QMouseEvent *event)
-{
- setPressed(false);
- emit released();
-
- const int threshold = qApp->styleHints()->startDragDistance();
- const bool isClick = (threshold >= qAbs(event->position().toPoint().x() - m_pressPoint.x()) &&
- threshold >= qAbs(event->position().toPoint().y() - m_pressPoint.y()));
-
- if (isClick)
- emit clicked();
-}
-
-void MaskedMouseArea::mouseUngrabEvent()
-{
- setPressed(false);
- emit canceled();
-}
-
-void MaskedMouseArea::hoverEnterEvent(QHoverEvent *event)
-{
- Q_UNUSED(event);
- setContainsMouse(true);
-}
-
-void MaskedMouseArea::hoverLeaveEvent(QHoverEvent *event)
-{
- Q_UNUSED(event);
- setContainsMouse(false);
-}
diff --git a/examples/quick/customitems/maskedmousearea/maskedmousearea.h b/examples/quick/customitems/maskedmousearea/maskedmousearea.h
deleted file mode 100644
index b235df2a98..0000000000
--- a/examples/quick/customitems/maskedmousearea/maskedmousearea.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef MASKEDMOUSEAREA_H
-#define MASKEDMOUSEAREA_H
-
-#include <QImage>
-#include <QQuickItem>
-
-
-class MaskedMouseArea : public QQuickItem
-{
- Q_OBJECT
- Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
- Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged)
- Q_PROPERTY(QUrl maskSource READ maskSource WRITE setMaskSource NOTIFY maskSourceChanged)
- Q_PROPERTY(qreal alphaThreshold READ alphaThreshold WRITE setAlphaThreshold NOTIFY alphaThresholdChanged)
- QML_ELEMENT
-
-public:
- MaskedMouseArea(QQuickItem *parent = nullptr);
-
- bool contains(const QPointF &point) const override;
-
- bool isPressed() const { return m_pressed; }
- bool containsMouse() const { return m_containsMouse; }
-
- QUrl maskSource() const { return m_maskSource; }
- void setMaskSource(const QUrl &source);
-
- qreal alphaThreshold() const { return m_alphaThreshold; }
- void setAlphaThreshold(qreal threshold);
-
-signals:
- void pressed();
- void released();
- void clicked();
- void canceled();
- void pressedChanged();
- void maskSourceChanged();
- void containsMouseChanged();
- void alphaThresholdChanged();
-
-protected:
- void setPressed(bool pressed);
- void setContainsMouse(bool containsMouse);
- void mousePressEvent(QMouseEvent *event) override;
- void mouseReleaseEvent(QMouseEvent *event) override;
- void hoverEnterEvent(QHoverEvent *event) override;
- void hoverLeaveEvent(QHoverEvent *event) override;
- void mouseUngrabEvent() override;
-
-private:
- bool m_pressed;
- QUrl m_maskSource;
- QImage m_maskImage;
- QPointF m_pressPoint;
- qreal m_alphaThreshold;
- bool m_containsMouse;
-};
-
-#endif
diff --git a/examples/quick/customitems/maskedmousearea/maskedmousearea.qml b/examples/quick/customitems/maskedmousearea/maskedmousearea.qml
deleted file mode 100644
index df8de3dcb8..0000000000
--- a/examples/quick/customitems/maskedmousearea/maskedmousearea.qml
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import Example
-
-Rectangle {
- height: 480
- width: 320
- color: "black"
-
- Text {
- text: qsTr("CLICK AND HOVER")
- opacity: 0.6
- color: "white"
- font.pixelSize: 20
- anchors.top: parent.top
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 50
- }
-
- Image {
- id: moon
- anchors.centerIn: parent
- scale: moonArea.pressed ? 1.1 : 1.0
- opacity: moonArea.containsMouse ? 1.0 : 0.7
- source: Qt.resolvedUrl("images/moon.png")
-
- MaskedMouseArea {
- id: moonArea
- anchors.fill: parent
- alphaThreshold: 0.4
- maskSource: moon.source
- }
-
- Behavior on opacity {
- NumberAnimation { duration: 200 }
- }
- Behavior on scale {
- NumberAnimation { duration: 100 }
- }
- }
-
- Image {
- id: rightCloud
- anchors {
- centerIn: moon
- verticalCenterOffset: 30
- horizontalCenterOffset: 80
- }
- scale: rightCloudArea.pressed ? 1.1 : 1.0
- opacity: rightCloudArea.containsMouse ? 1.0 : 0.7
- source: Qt.resolvedUrl("images/cloud_2.png")
-
- MaskedMouseArea {
- id: rightCloudArea
- anchors.fill: parent
- alphaThreshold: 0.4
- maskSource: rightCloud.source
- }
-
- Behavior on opacity {
- NumberAnimation { duration: 200 }
- }
- Behavior on scale {
- NumberAnimation { duration: 100 }
- }
- }
-
- Image {
- id: leftCloud
- anchors {
- centerIn: moon
- verticalCenterOffset: 40
- horizontalCenterOffset: -80
- }
- scale: leftCloudArea.pressed ? 1.1 : 1.0
- opacity: leftCloudArea.containsMouse ? 1.0 : 0.7
- source: Qt.resolvedUrl("images/cloud_1.png")
-
- MaskedMouseArea {
- id: leftCloudArea
- anchors.fill: parent
- alphaThreshold: 0.4
- maskSource: leftCloud.source
- }
-
- Behavior on opacity {
- NumberAnimation { duration: 200 }
- }
- Behavior on scale {
- NumberAnimation { duration: 100 }
- }
- }
-}
diff --git a/examples/quick/customitems/painteditem/CMakeLists.txt b/examples/quick/customitems/painteditem/CMakeLists.txt
index 9f2d54bf8d..ca23161b1c 100644
--- a/examples/quick/customitems/painteditem/CMakeLists.txt
+++ b/examples/quick/customitems/painteditem/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(painteditem LANGUAGES CXX)
@@ -7,44 +7,40 @@ project(painteditem LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/customitems/painteditem")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
add_subdirectory(TextBalloon)
qt_add_executable(painteditemexample WIN32 MACOSX_BUNDLE main.cpp)
qt_add_qml_module(painteditemexample
URI painteditem
- AUTO_RESOURCE_PREFIX
QML_FILES
"textballoons.qml"
)
target_link_libraries(painteditemexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(painteditemexample qmltextballoon)
install(TARGETS painteditemexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-include(../../shared/QtBundleQmlModuleForMacOS.cmake)
-
-set(app_target "painteditemexample")
-set(qml_plugin_target "qmltextballoon")
-set(qml_module_uri "TextBalloon")
-add_qml_module_to_macos_app_bundle(
- "${app_target}" "${qml_plugin_target}" "${qml_module_uri}")
+qt_generate_deploy_qml_app_script(
+ TARGET painteditemexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/customitems/painteditem/TextBalloon/CMakeLists.txt b/examples/quick/customitems/painteditem/TextBalloon/CMakeLists.txt
index 950e8ee616..b94b297aeb 100644
--- a/examples/quick/customitems/painteditem/TextBalloon/CMakeLists.txt
+++ b/examples/quick/customitems/painteditem/TextBalloon/CMakeLists.txt
@@ -1,33 +1,28 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
message(FATAL_ERROR "TextBalloon should be built as part of the 'painteditem' project, and not in isolation.")
endif()
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/customitems/painteditem/TextBalloon")
-
qt_add_qml_module(qmltextballoon
URI "TextBalloon"
PLUGIN_TARGET qmltextballoon
- AUTO_RESOURCE_PREFIX
SOURCES
textballoon.cpp textballoon.h
)
-target_link_libraries(qmltextballoon PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+target_link_libraries(qmltextballoon PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS qmltextballoon
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/TextBalloon"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/TextBalloon"
)
-
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
- DESTINATION "${INSTALL_EXAMPLEDIR}"
+ DESTINATION "${CMAKE_INSTALL_BINDIR}/TextBalloon"
)
diff --git a/examples/quick/customitems/painteditem/doc/src/textballoons.qdoc b/examples/quick/customitems/painteditem/doc/src/textballoons.qdoc
index 4d41f6c2a5..a0194759bd 100644
--- a/examples/quick/customitems/painteditem/doc/src/textballoons.qdoc
+++ b/examples/quick/customitems/painteditem/doc/src/textballoons.qdoc
@@ -5,6 +5,7 @@
\brief Shows how to implement QPainter-based custom scenegraph items.
\example customitems/painteditem
\ingroup qtquickexamples
+ \examplecategory {Graphics}
The Painted Item example shows how to use the QML Scene Graph framework to
implement custom scenegraph items using QPainter.
diff --git a/examples/quick/customitems/painteditem/textballoons.qml b/examples/quick/customitems/painteditem/textballoons.qml
index d363093d95..1ef491f061 100644
--- a/examples/quick/customitems/painteditem/textballoons.qml
+++ b/examples/quick/customitems/painteditem/textballoons.qml
@@ -20,14 +20,14 @@ Item {
}
ListView {
+ id: balloonView
anchors.bottom: controls.top
anchors.bottomMargin: 2
anchors.top: parent.top
- id: balloonView
delegate: TextBalloon {
- anchors.right: index % 2 == 0 ? undefined : parent.right
+ anchors.right: index % 2 != 0 ? parent?.right : undefined
height: 60
- rightAligned: index % 2 == 0 ? false : true
+ rightAligned: index % 2 != 0
width: balloonWidth
}
model: balloonModel
@@ -50,7 +50,7 @@ Item {
Text {
anchors.centerIn: parent
- text: "Add another balloon"
+ text: qsTr("Add another balloon")
}
MouseArea {
diff --git a/examples/quick/delegatechooser/CMakeLists.txt b/examples/quick/delegatechooser/CMakeLists.txt
deleted file mode 100644
index cd8edd2524..0000000000
--- a/examples/quick/delegatechooser/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(delegatechooser LANGUAGES CXX)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/delegatechooser")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_standard_project_setup()
-
-add_subdirectory("../shared" "shared")
-
-qt_add_executable(delegatechooserexample WIN32 MACOSX_BUNDLE main.cpp)
-
-target_link_libraries(delegatechooserexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-add_dependencies(delegatechooserexample delegatechooser_shared)
-
-qt_add_qml_module(delegatechooserexample
- URI delegatechooser
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "delegatechooser.qml"
-)
-
-install(TARGETS delegatechooserexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
-
-bundle_shared(delegatechooserexample)
diff --git a/examples/quick/delegatechooser/delegatechooser.qml b/examples/quick/delegatechooser/delegatechooser.qml
deleted file mode 100644
index 9e7eff9fdc..0000000000
--- a/examples/quick/delegatechooser/delegatechooser.qml
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQml.Models
-import QtQuick.Layouts
-import Qt.labs.qmlmodels
-import shared
-
-Rectangle {
- visible: true
- width: 640
- height: 640
-
- ListModel {
- id: listModel
- ListElement { dataType: "rect"; color: "green" }
- ListElement { dataType: "image" }
- ListElement { dataType: "rect"; color: "green" }
- ListElement { dataType: "image" }
- ListElement { dataType: "rect"; color: "blue" }
- ListElement { dataType: "rect"; color: "blue" }
- ListElement { dataType: "rect"; color: "blue" }
- ListElement { dataType: "rect"; color: "blue" }
- ListElement { dataType: "rect"; color: "blue" }
- ListElement { dataType: "rect"; color: "blue" }
- }
-
- ListModel {
- id: listModel2
- ListElement { dataType: "rect"; color: "blue" }
- ListElement { dataType: "rect"; color: "blue" }
- ListElement { dataType: "rect"; color: "green" }
- ListElement { dataType: "image" }
- ListElement { dataType: "rect"; color: "green" }
- ListElement { dataType: "image" }
- ListElement { dataType: "rect"; color: "blue" }
- ListElement { dataType: "rect"; color: "lightsteelblue" }
- ListElement { dataType: "rect"; color: "fuchsia" }
- ListElement { dataType: "rect"; color: "lime" }
- }
-
- DelegateChooser {
- id: fancyDelegate
- role: "dataType"
- DelegateChoice {
- roleValue: "rect"
- delegate: DelegateChooser {
- DelegateChoice {
- row: 0
- Rectangle {
- width: parent.width
- height: 50
- color: "red"
- border.color: "black"
- border.width: 1
- }
- }
- DelegateChoice {
- Rectangle {
- width: parent.width
- height: 50
- color: model.color
- border.color: "black"
- border.width: 1
- }
- }
- }
- }
- DelegateChoice {
- roleValue: "image"
- delegate: Image {
- width: parent.width
- height: 100
- source: Images.qtLogo
- fillMode: Image.PreserveAspectFit
- }
- }
- }
-
- Item {
- anchors.fill: parent
- id: ite
- RowLayout {
- ListView {
- Layout.preferredHeight: ite.height
- Layout.preferredWidth: ite.width * 0.5
- model: listModel
- delegate: fancyDelegate
- }
- ListView {
- Layout.preferredHeight: ite.height
- Layout.preferredWidth: ite.width * 0.5
- model: listModel2
- delegate: fancyDelegate
- }
- }
- }
-}
diff --git a/examples/quick/delegatechooser/main.cpp b/examples/quick/delegatechooser/main.cpp
deleted file mode 100644
index 7cccc7340b..0000000000
--- a/examples/quick/delegatechooser/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(delegatechooser/delegatechooser)
diff --git a/examples/quick/draganddrop/CMakeLists.txt b/examples/quick/draganddrop/CMakeLists.txt
index 78fbe5da6b..0493f8feea 100644
--- a/examples/quick/draganddrop/CMakeLists.txt
+++ b/examples/quick/draganddrop/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(draganddrop LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/draganddrop")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../shared" "shared")
@@ -24,7 +18,6 @@ qt_add_executable(draganddropexample
qt_add_qml_module(draganddropexample
URI draganddrop
- AUTO_RESOURCE_PREFIX
QML_FILES
"draganddrop.qml"
"tiles/DragTile.qml"
@@ -32,21 +25,29 @@ qt_add_qml_module(draganddropexample
"tiles/tiles.qml"
"views/Icon.qml"
"views/gridview.qml"
+ "external/externaldraganddrop.qml"
)
target_link_libraries(draganddropexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(draganddropexample draganddrop_shared)
install(TARGETS draganddropexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(draganddropexample)
+qt_generate_deploy_qml_app_script(
+ TARGET draganddropexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/draganddrop/doc/src/draganddrop.qdoc b/examples/quick/draganddrop/doc/src/draganddrop.qdoc
index 23a135b2fa..444bbafca4 100644
--- a/examples/quick/draganddrop/doc/src/draganddrop.qdoc
+++ b/examples/quick/draganddrop/doc/src/draganddrop.qdoc
@@ -6,6 +6,7 @@
\brief This is a collection of QML drag and drop examples.
\image qml-draganddrop-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e{Drag and Drop} is a collection of small QML examples relating to the drag
and drop functionality. For more information, visit the \l{Drag and Drop}
diff --git a/examples/quick/draganddrop/draganddrop.qml b/examples/quick/draganddrop/draganddrop.qml
index 87874527af..9e1629e5d4 100644
--- a/examples/quick/draganddrop/draganddrop.qml
+++ b/examples/quick/draganddrop/draganddrop.qml
@@ -11,8 +11,9 @@ Item {
id: ll
anchors.fill: parent
Component.onCompleted: {
- addExample("Tiles", "Press and drag tiles to move them into the matching colored boxes", Qt.resolvedUrl("tiles/tiles.qml"));
- addExample("GridView", "Press and drag to re-order items in the grid", Qt.resolvedUrl("views/gridview.qml"));
+ addExample(qsTr("Tiles"), qsTr("Press and drag tiles to move them into the matching colored boxes"), Qt.resolvedUrl("tiles/tiles.qml"))
+ addExample(qsTr("GridView"), qsTr("Press and drag to re-order items in the grid"), Qt.resolvedUrl("views/gridview.qml"))
+ addExample(qsTr("External"), qsTr("Drag and drop between this and other applications."), Qt.resolvedUrl("external/externaldraganddrop.qml"))
}
}
}
diff --git a/examples/quick/draganddrop/draganddrop.qrc b/examples/quick/draganddrop/draganddrop.qrc
index a710562b79..1e6ae00d12 100644
--- a/examples/quick/draganddrop/draganddrop.qrc
+++ b/examples/quick/draganddrop/draganddrop.qrc
@@ -6,5 +6,6 @@
<file>tiles/tiles.qml</file>
<file>views/gridview.qml</file>
<file>views/Icon.qml</file>
+ <file>external/externaldraganddrop.qml</file>
</qresource>
</RCC>
diff --git a/examples/quick/draganddrop/external/externaldraganddrop.qml b/examples/quick/draganddrop/external/externaldraganddrop.qml
new file mode 100644
index 0000000000..8156a13f55
--- /dev/null
+++ b/examples/quick/draganddrop/external/externaldraganddrop.qml
@@ -0,0 +1,84 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Control {
+ padding: 8
+ contentItem: ColumnLayout {
+ component DragAndDropArea: Rectangle {
+ id: item
+ property string display: qsTr("Drag items to this area, or drag this item to a different drop area.")
+ property alias dropEnabled: acceptDropCB.checked
+ color: dropArea.containsDrag ? Qt.darker(palette.base) : palette.base
+
+ ColorAnimation on color {
+ id: rejectAnimation
+ from: "#FCC"
+ to: palette.base
+ duration: 1000
+ }
+ Label {
+ anchors.fill: parent
+ anchors.margins: 10
+ text: item.display
+ wrapMode: Text.WordWrap
+ }
+ DropArea {
+ id: dropArea
+ anchors.fill: parent
+ keys: ["text/plain"]
+ onEntered: (drag) => {
+ if (!acceptDropCB.checked) {
+ drag.accepted = false
+ rejectAnimation.start()
+ }
+ }
+ onDropped: (drop) => {
+ if (drop.hasText && acceptDropCB.checked) {
+ if (drop.proposedAction === Qt.MoveAction || drop.proposedAction === Qt.CopyAction) {
+ item.display = drop.text
+ drop.acceptProposedAction()
+ }
+ }
+ }
+ }
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ drag.target: draggable
+ }
+ Item {
+ id: draggable
+ anchors.fill: parent
+ Drag.active: mouseArea.drag.active
+ Drag.hotSpot.x: 0
+ Drag.hotSpot.y: 0
+ Drag.mimeData: { "text/plain": item.display }
+ Drag.dragType: Drag.Automatic
+ Drag.onDragFinished: (dropAction) => {
+ if (dropAction === Qt.MoveAction)
+ item.display = ""
+ }
+ }
+ CheckBox {
+ id: acceptDropCB
+ anchors.bottom: parent.bottom
+ checked: true
+ text: qsTr("accept drop")
+ }
+ }
+
+ DragAndDropArea {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+
+ DragAndDropArea {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+}
diff --git a/examples/quick/draganddrop/tiles/DragTile.qml b/examples/quick/draganddrop/tiles/DragTile.qml
index c4d81a7662..f35695f865 100644
--- a/examples/quick/draganddrop/tiles/DragTile.qml
+++ b/examples/quick/draganddrop/tiles/DragTile.qml
@@ -10,12 +10,14 @@ Item {
required property string colorKey
required property int modelData
- width: 64; height: 64
+ width: 64
+ height: 64
MouseArea {
id: mouseArea
- width: 64; height: 64
+ width: 64
+ height: 64
anchors.centerIn: parent
drag.target: tile
@@ -25,9 +27,12 @@ Item {
Rectangle {
id: tile
- width: 64; height: 64
- anchors.verticalCenter: parent.verticalCenter
- anchors.horizontalCenter: parent.horizontalCenter
+ width: 64
+ height: 64
+ anchors {
+ verticalCenter: parent.verticalCenter
+ horizontalCenter: parent.horizontalCenter
+ }
color: root.colorKey
@@ -41,13 +46,19 @@ Item {
color: "white"
font.pixelSize: 48
text: root.modelData + 1
- horizontalAlignment:Text.AlignHCenter
+ horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
//! [1]
states: State {
when: mouseArea.drag.active
- AnchorChanges { target: tile; anchors.verticalCenter: undefined; anchors.horizontalCenter: undefined }
+ AnchorChanges {
+ target: tile
+ anchors {
+ verticalCenter: undefined
+ horizontalCenter: undefined
+ }
+ }
}
}
}
diff --git a/examples/quick/draganddrop/tiles/DropTile.qml b/examples/quick/draganddrop/tiles/DropTile.qml
index 1d429a5a59..409c3801e4 100644
--- a/examples/quick/draganddrop/tiles/DropTile.qml
+++ b/examples/quick/draganddrop/tiles/DropTile.qml
@@ -8,25 +8,16 @@ DropArea {
id: dragTarget
property string colorKey
- property alias dropProxy: dragTarget
- width: 64; height: 64
+ width: 64
+ height: 64
keys: [ colorKey ]
Rectangle {
id: dropRectangle
anchors.fill: parent
- color: dragTarget.colorKey
-
- states: [
- State {
- when: dragTarget.containsDrag
- PropertyChanges {
- dropRectangle.color: "grey"
- }
- }
- ]
+ color: dragTarget.containsDrag ? "grey" : dragTarget.colorKey
}
}
//! [0]
diff --git a/examples/quick/draganddrop/tiles/tiles.qml b/examples/quick/draganddrop/tiles/tiles.qml
index 62fb802972..c9667022d7 100644
--- a/examples/quick/draganddrop/tiles/tiles.qml
+++ b/examples/quick/draganddrop/tiles/tiles.qml
@@ -14,22 +14,28 @@ Rectangle {
Grid {
id: redDestination
- anchors.left: redSource.right; anchors.top: parent.top;
- anchors.margins: 5
+ anchors {
+ left: redSource.right
+ top: parent.top
+ margins: 5
+ }
width: 64*3
height: 64*3
opacity: 0.5
columns: 3
Repeater {
- model: 9;
+ model: 9
delegate: DropTile { colorKey: "red" }
}
}
Grid {
- anchors.right: blueSource.left; anchors.bottom: parent.bottom;
- anchors.margins: 5
+ anchors {
+ right: blueSource.left
+ bottom: parent.bottom
+ margins: 5
+ }
width: 64*3
height: 64*3
@@ -46,8 +52,12 @@ Rectangle {
Column {
id: redSource
- anchors.left: parent.left; anchors.top: parent.top; anchors.bottom: parent.bottom
- anchors.margins: 5
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ margins: 5
+ }
width: 64
spacing: -16
@@ -59,8 +69,12 @@ Rectangle {
Column {
id: blueSource
- anchors.right: parent.right; anchors.top: parent.top; anchors.bottom: parent.bottom
- anchors.margins: 5
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ margins: 5
+ }
width: 64
spacing: -16
diff --git a/examples/quick/draganddrop/views/Icon.qml b/examples/quick/draganddrop/views/Icon.qml
index ed5c6e51e5..80bd0c1e7a 100644
--- a/examples/quick/draganddrop/views/Icon.qml
+++ b/examples/quick/draganddrop/views/Icon.qml
@@ -41,8 +41,10 @@ Rectangle {
AnchorChanges {
target: icon
- anchors.horizontalCenter: undefined
- anchors.verticalCenter: undefined
+ anchors {
+ horizontalCenter: undefined
+ verticalCenter: undefined
+ }
}
}
]
diff --git a/examples/quick/draganddrop/views/gridview.qml b/examples/quick/draganddrop/views/gridview.qml
index 1a45f77d45..244d73341d 100644
--- a/examples/quick/draganddrop/views/gridview.qml
+++ b/examples/quick/draganddrop/views/gridview.qml
@@ -1,17 +1,23 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQml
import QtQuick
import QtQml.Models
GridView {
id: root
- width: 320; height: 480
- cellWidth: 80; cellHeight: 80
+ width: 320
+ height: 480
+ cellWidth: 80
+ cellHeight: 80
displaced: Transition {
- NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
+ NumberAnimation {
+ properties: "x,y"
+ easing.type: Easing.OutQuad
+ }
}
//! [0]
@@ -48,9 +54,10 @@ GridView {
//! [1]
delegate: DropArea {
id: delegateRoot
- required property color color;
+ required property color color
- width: 80; height: 80
+ width: 80
+ height: 80
onEntered: function(drag) {
visualModel.items.move((drag.source as Icon).visualIndex, icon.visualIndex)
diff --git a/examples/quick/embeddedinwidgets/CMakeLists.txt b/examples/quick/embeddedinwidgets/CMakeLists.txt
index 0a52518444..d977def966 100644
--- a/examples/quick/embeddedinwidgets/CMakeLists.txt
+++ b/examples/quick/embeddedinwidgets/CMakeLists.txt
@@ -1,42 +1,44 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(embeddedinwidgets LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/embeddedinwidgets")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick Widgets)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(embeddedinwidgetsexample WIN32 MACOSX_BUNDLE
main.cpp
)
target_link_libraries(embeddedinwidgetsexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::Widgets
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::Widgets
)
# Resources:
qt_add_qml_module(embeddedinwidgetsexample
URI embeddedinwidgets
- AUTO_RESOURCE_PREFIX
QML_FILES
- "main.qml"
+ "Main.qml"
RESOURCES
"reflect.frag.qsb"
)
install(TARGETS embeddedinwidgetsexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET embeddedinwidgetsexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/embeddedinwidgets/main.qml b/examples/quick/embeddedinwidgets/Main.qml
index 1408e8f261..1408e8f261 100644
--- a/examples/quick/embeddedinwidgets/main.qml
+++ b/examples/quick/embeddedinwidgets/Main.qml
diff --git a/examples/quick/embeddedinwidgets/doc/images/qml-embeddedinwidgets-example.jpg b/examples/quick/embeddedinwidgets/doc/images/qml-embeddedinwidgets-example.jpg
new file mode 100644
index 0000000000..b2d188e422
--- /dev/null
+++ b/examples/quick/embeddedinwidgets/doc/images/qml-embeddedinwidgets-example.jpg
Binary files differ
diff --git a/examples/quick/embeddedinwidgets/doc/src/embeddedinwidgets.qdoc b/examples/quick/embeddedinwidgets/doc/src/embeddedinwidgets.qdoc
new file mode 100644
index 0000000000..5e76b5c472
--- /dev/null
+++ b/examples/quick/embeddedinwidgets/doc/src/embeddedinwidgets.qdoc
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \title Qt Quick Examples - Embedded in Widgets
+ \example embeddedinwidgets
+ \image qml-embeddedinwidgets-example.jpg
+ \brief Demonstrates embedding a QQuickWindow into a QWidget UI via QWidget::createWindowContainer().
+ \ingroup qtquickexamples
+ \examplecategory {Graphics}
+
+ This example demonstrates one of the approaches to adding Qt Quick content
+ into a QWidget-based application. QQuickView, and its parent class,
+ QQuickWindow derive from QWindow. This means that they can be used with
+ QWidget::createWindowContainer(), like any other QWindow.
+
+ \include examples-run.qdocinc
+
+ Embedding the window internally leads to creating a native child widget
+ inside the widget hierarchy, and the window (the QQuickView in the example)
+ is reparented. The container widget takes care of repositioning the child
+ window so that it appears to be part of the user interface, even though in
+ reality it is separate native window that paints independently from its
+ parent, the QWidget-based top-level window.
+
+ \snippet embeddedinwidgets/main.cpp ctor
+
+ The key step during initialization is the creation of \c container, a
+ QWidget that wraps and hosts the QQuickView. This widget can then added to a
+ layout like any other QWidget.
+
+ \note Using native window embedding is just one possible approach to
+ combining QWidget and Qt Quick user interfaces. The other, more commonly
+ used approach for this is QQuickWidget. See the \l{Qt Quick Widgets Example}
+ for an example of using QQuickWidget. There are significant differences
+ internally when it comes to rendering and event handling. QQuickWidget does
+ not use native windows at all and redirects the Qt Quick rendering into a
+ texture that is then composited with the rest of the QWidget content via a
+ 3D graphics API such as OpenGL or Vulkan. This brings more flexibility, at
+ the expense of performance. It also works on platforms where there is no
+ actual windowing system and no concept of native windows. Whereas the window
+ embedding approach demonstrated by this example can be more performant, but
+ it is best suited for user interfaces where the Qt Quick content occupies a
+ fixed rectangular area and is not resized, stacked, or clipped afterwards.
+
+ \sa QWidget::createWindowContainer()
+*/
diff --git a/examples/quick/embeddedinwidgets/embeddedinwidgets.pro b/examples/quick/embeddedinwidgets/embeddedinwidgets.pro
index 936da9dfeb..493f462370 100644
--- a/examples/quick/embeddedinwidgets/embeddedinwidgets.pro
+++ b/examples/quick/embeddedinwidgets/embeddedinwidgets.pro
@@ -3,10 +3,15 @@ QT += widgets quick
SOURCES += main.cpp
-OTHER_FILES += main.qml
+OTHER_FILES += Main.qml
-RESOURCES += \
- embeddedinwidgets.qrc
+qml_resources.files = \
+ qmldir \
+ Main.qml
+
+qml_resources.prefix = /qt/qml/embeddedinwidgets
+
+RESOURCES += qml_resources
target.path = $$[QT_INSTALL_EXAMPLES]/quick/embeddedinwidgets
INSTALLS += target
diff --git a/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc b/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc
deleted file mode 100644
index 40a959932a..0000000000
--- a/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/qt/qml/embeddedinwidgets">
- <file>main.qml</file>
- <file>reflect.frag.qsb</file>
- </qresource>
-</RCC>
diff --git a/examples/quick/embeddedinwidgets/main.cpp b/examples/quick/embeddedinwidgets/main.cpp
index 3633294d13..2cc70e195e 100644
--- a/examples/quick/embeddedinwidgets/main.cpp
+++ b/examples/quick/embeddedinwidgets/main.cpp
@@ -26,6 +26,7 @@ private:
QQuickView *m_quickView;
};
+//! [ctor]
MainWindow::MainWindow()
: m_quickView(new QQuickView)
{
@@ -37,7 +38,7 @@ MainWindow::MainWindow()
this, &MainWindow::quickViewStatusChanged);
connect(m_quickView, &QQuickWindow::sceneGraphError,
this, &MainWindow::sceneGraphError);
- m_quickView->setSource(QUrl(QStringLiteral("qrc:///embeddedinwidgets/main.qml")));
+ m_quickView->loadFromModule("embeddedinwidgets", "Main");
QWidget *container = QWidget::createWindowContainer(m_quickView);
container->setMinimumSize(m_quickView->size());
@@ -51,6 +52,7 @@ MainWindow::MainWindow()
QMenu *fileMenu = menuBar()->addMenu(tr("File"));
fileMenu->addAction(tr("Quit"), qApp, &QCoreApplication::quit);
}
+//! [ctor]
void MainWindow::quickViewStatusChanged(QQuickView::Status status)
{
diff --git a/examples/quick/embeddedinwidgets/qmldir b/examples/quick/embeddedinwidgets/qmldir
new file mode 100644
index 0000000000..06d6e62c19
--- /dev/null
+++ b/examples/quick/embeddedinwidgets/qmldir
@@ -0,0 +1,4 @@
+module embeddedinwidgets
+prefer :/qt/qml/embeddedinwidgets/
+Main 254.0 Main.qml
+
diff --git a/examples/quick/embeddedinwidgets/reflect.frag b/examples/quick/embeddedinwidgets/reflect.frag
index 04a81f86e5..e1d5dbf450 100644
--- a/examples/quick/embeddedinwidgets/reflect.frag
+++ b/examples/quick/embeddedinwidgets/reflect.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
diff --git a/examples/quick/externaldraganddrop/CMakeLists.txt b/examples/quick/externaldraganddrop/CMakeLists.txt
deleted file mode 100644
index 3c31d55fa4..0000000000
--- a/examples/quick/externaldraganddrop/CMakeLists.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(externaldraganddrop LANGUAGES CXX)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/externaldraganddrop")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_standard_project_setup()
-
-qt_add_executable(externaldraganddropexample
- WIN32
- MACOSX_BUNDLE
- main.cpp
-)
-
-qt_add_qml_module(externaldraganddropexample
- URI externaldraganddrop
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "DragAndDropTextItem.qml"
- "externaldraganddrop.qml"
-)
-
-target_link_libraries(externaldraganddropexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-install(TARGETS externaldraganddropexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/quick/externaldraganddrop/DragAndDropTextItem.qml b/examples/quick/externaldraganddrop/DragAndDropTextItem.qml
deleted file mode 100644
index c241f6ed82..0000000000
--- a/examples/quick/externaldraganddrop/DragAndDropTextItem.qml
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-Rectangle {
- id: item
- property string display
- property alias dropEnabled: acceptDropCB.checked
- color: dropArea.containsDrag ? "#CFC" : "#EEE"
- ColorAnimation on color {
- id: rejectAnimation
- from: "#FCC"
- to: "#EEE"
- duration: 1000
- }
- Label {
- anchors.fill: parent
- anchors.margins: 10
- text: item.display
- wrapMode: Text.WordWrap
- }
- DropArea {
- id: dropArea
- anchors.fill: parent
- keys: ["text/plain"]
- onEntered: (drag) => {
- if (!acceptDropCB.checked) {
- drag.accepted = false
- rejectAnimation.start()
- }
- }
- onDropped: (drop) => {
- if (drop.hasText && acceptDropCB.checked) {
- if (drop.proposedAction == Qt.MoveAction || drop.proposedAction == Qt.CopyAction) {
- item.display = drop.text
- drop.acceptProposedAction()
- }
- }
- }
- }
- MouseArea {
- id: mouseArea
- anchors.fill: parent
- drag.target: draggable
- }
- Item {
- id: draggable
- anchors.fill: parent
- Drag.active: mouseArea.drag.active
- Drag.hotSpot.x: 0
- Drag.hotSpot.y: 0
- Drag.mimeData: { "text/plain": item.display }
- Drag.dragType: Drag.Automatic
- Drag.onDragFinished: (dropAction) => {
- if (dropAction == Qt.MoveAction)
- item.display = ""
- }
- }
- CheckBox {
- id: acceptDropCB
- anchors.bottom: parent.bottom
- checked: true
- text: "accept drop"
- }
-}
diff --git a/examples/quick/externaldraganddrop/doc/images/qml-dnd2-example.png b/examples/quick/externaldraganddrop/doc/images/qml-dnd2-example.png
deleted file mode 100644
index e657d81795..0000000000
--- a/examples/quick/externaldraganddrop/doc/images/qml-dnd2-example.png
+++ /dev/null
Binary files differ
diff --git a/examples/quick/externaldraganddrop/doc/src/externaldraganddrop.qdoc b/examples/quick/externaldraganddrop/doc/src/externaldraganddrop.qdoc
deleted file mode 100644
index 974c91a3cc..0000000000
--- a/examples/quick/externaldraganddrop/doc/src/externaldraganddrop.qdoc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-/*!
- \title Qt Quick Examples - externaldraganddrop
- \example externaldraganddrop
- \brief This is an example of drag-and-drop among QML applications.
- \image qml-dnd2-example.png
- \ingroup qtquickexamples
-
- \e externaldraganddrop demonstrates how to perform drag and
- drop with \l MouseArea and \l DropArea.
-
- The example allows you to drag the text to other boxes, out of boxes into
- other applications, and from other applications into the boxes. Use the
- option or CTRL keys to copy rather than move text when dragging between
- boxes.
-
- \include examples-run.qdocinc
-
-*/
diff --git a/examples/quick/externaldraganddrop/externaldraganddrop.pro b/examples/quick/externaldraganddrop/externaldraganddrop.pro
deleted file mode 100644
index cd456f9b27..0000000000
--- a/examples/quick/externaldraganddrop/externaldraganddrop.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-RESOURCES += externaldraganddrop.qrc
-
-EXAMPLE_FILES = \
- externaldraganddrop.qml \
- DragAndDropTextItem.qml
-
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/externaldraganddrop
-INSTALLS += target
diff --git a/examples/quick/externaldraganddrop/externaldraganddrop.qml b/examples/quick/externaldraganddrop/externaldraganddrop.qml
deleted file mode 100644
index b0cd20e69e..0000000000
--- a/examples/quick/externaldraganddrop/externaldraganddrop.qml
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-
-Item {
- id: root
- width: 320
- height: 480
-
- ColumnLayout {
-
- anchors.fill: parent
- anchors.margins: 8
-
- Label {
- Layout.fillWidth: true
- text: "Drag text into, out of, and between the boxes below."
- wrapMode: Text.WordWrap
- }
-
- DragAndDropTextItem {
- Layout.fillWidth: true
- Layout.fillHeight: true
- display: "Sample Text"
- }
-
- DragAndDropTextItem {
- Layout.fillWidth: true
- Layout.fillHeight: true
- display: "Option/ctrl drag to copy instead of move text."
- }
-
- DragAndDropTextItem {
- Layout.fillWidth: true
- Layout.fillHeight: true
- dropEnabled: false
- display: "Drag out into other applications."
- }
- }
-}
diff --git a/examples/quick/externaldraganddrop/externaldraganddrop.qmlproject b/examples/quick/externaldraganddrop/externaldraganddrop.qmlproject
deleted file mode 100644
index 359efae597..0000000000
--- a/examples/quick/externaldraganddrop/externaldraganddrop.qmlproject
+++ /dev/null
@@ -1,16 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "externaldraganddrop.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/externaldraganddrop/externaldraganddrop.qrc b/examples/quick/externaldraganddrop/externaldraganddrop.qrc
deleted file mode 100644
index 94f7e47e24..0000000000
--- a/examples/quick/externaldraganddrop/externaldraganddrop.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/qt/qml/externaldraganddrop">
- <file>externaldraganddrop.qml</file>
- <file>DragAndDropTextItem.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/quick/externaldraganddrop/main.cpp b/examples/quick/externaldraganddrop/main.cpp
deleted file mode 100644
index cd4831018c..0000000000
--- a/examples/quick/externaldraganddrop/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(externaldraganddrop/externaldraganddrop)
diff --git a/examples/quick/imageelements/BorderImageSelector.qml b/examples/quick/imageelements/BorderImageSelector.qml
index 0ea1b0b551..ae99b35231 100644
--- a/examples/quick/imageelements/BorderImageSelector.qml
+++ b/examples/quick/imageelements/BorderImageSelector.qml
@@ -1,10 +1,12 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
Item {
id: selector
+
property int curIdx: 0
property int maxIdx: 3
property int gridWidth: 240
@@ -12,11 +14,11 @@ Item {
width: parent.width
height: 64
function advance(steps) {
- var nextIdx = curIdx + steps
+ const nextIdx = curIdx + steps
if (nextIdx < 0 || nextIdx > maxIdx)
- return;
- flickable.contentX += gridWidth * steps;
- curIdx += steps;
+ return
+ flickable.contentX += gridWidth * steps
+ curIdx += steps
}
Image {
source: "pics/arrow.png"
diff --git a/examples/quick/imageelements/CMakeLists.txt b/examples/quick/imageelements/CMakeLists.txt
index 905a70d55d..e13f57af9b 100644
--- a/examples/quick/imageelements/CMakeLists.txt
+++ b/examples/quick/imageelements/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(imageelements LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/imageelements")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../shared" "shared")
@@ -23,15 +17,14 @@ qt_add_executable(imageelementsexample
)
target_link_libraries(imageelementsexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(imageelementsexample
URI imageelements
- AUTO_RESOURCE_PREFIX
QML_FILES
"animatedimage.qml"
"animatedsprite.qml"
@@ -61,9 +54,16 @@ qt_add_qml_module(imageelementsexample
)
install(TARGETS imageelementsexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(imageelementsexample)
+qt_generate_deploy_qml_app_script(
+ TARGET imageelementsexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/imageelements/ImageCell.qml b/examples/quick/imageelements/ImageCell.qml
index a49a6f7e52..537ed2a939 100644
--- a/examples/quick/imageelements/ImageCell.qml
+++ b/examples/quick/imageelements/ImageCell.qml
@@ -9,13 +9,17 @@ Item {
Image {
id: image
- width: parent.width; height: parent.height - captionItem.height
+
+ width: parent.width
+ height: parent.height - captionItem.height
source: "pics/qt-logo.png"
clip: true // only makes a difference if mode is PreserveAspectCrop
}
Label {
id: captionItem
- anchors.horizontalCenter: parent.horizontalCenter; anchors.bottom: parent.bottom
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
}
}
diff --git a/examples/quick/imageelements/MyBorderImage.qml b/examples/quick/imageelements/MyBorderImage.qml
index 9b516c242f..ae2d4171bd 100644
--- a/examples/quick/imageelements/MyBorderImage.qml
+++ b/examples/quick/imageelements/MyBorderImage.qml
@@ -17,32 +17,43 @@ Item {
property int maxHeight
property int margin
- width: 240; height: 200
+ width: 240
+ height: 200
BorderImage {
- id: image; anchors.centerIn: parent
+ id: image
+
+ anchors.centerIn: parent
SequentialAnimation on width {
loops: Animation.Infinite
NumberAnimation {
- from: container.minWidth; to: container.maxWidth
- duration: 2000; easing.type: Easing.InOutQuad
+ from: container.minWidth
+ to: container.maxWidth
+ duration: 2000
+ easing.type: Easing.InOutQuad
}
NumberAnimation {
- from: container.maxWidth; to: container.minWidth
- duration: 2000; easing.type: Easing.InOutQuad
+ from: container.maxWidth
+ to: container.minWidth
+ duration: 2000
+ easing.type: Easing.InOutQuad
}
}
SequentialAnimation on height {
loops: Animation.Infinite
NumberAnimation {
- from: container.minHeight; to: container.maxHeight
- duration: 2000; easing.type: Easing.InOutQuad
+ from: container.minHeight
+ to: container.maxHeight
+ duration: 2000
+ easing.type: Easing.InOutQuad
}
NumberAnimation {
- from: container.maxHeight; to: container.minHeight
- duration: 2000; easing.type: Easing.InOutQuad
+ from: container.maxHeight
+ to: container.minHeight
+ duration: 2000
+ easing.type: Easing.InOutQuad
}
}
diff --git a/examples/quick/imageelements/ShadowRectangle.qml b/examples/quick/imageelements/ShadowRectangle.qml
index b81da838fe..2be2fb81f2 100644
--- a/examples/quick/imageelements/ShadowRectangle.qml
+++ b/examples/quick/imageelements/ShadowRectangle.qml
@@ -9,11 +9,25 @@ Item {
//! [shadow]
BorderImage {
anchors.fill: rectangle
- anchors { leftMargin: -6; topMargin: -6; rightMargin: -8; bottomMargin: -8 }
- border { left: 10; top: 10; right: 10; bottom: 10 }
+ anchors {
+ leftMargin: -6
+ topMargin: -6
+ rightMargin: -8
+ bottomMargin: -8
+ }
+ border {
+ left: 10
+ top: 10
+ right: 10
+ bottom: 10
+ }
source: "pics/shadow.png"
}
//! [shadow]
- Rectangle { id: rectangle; anchors.fill: parent }
+ Rectangle {
+ id: rectangle
+
+ anchors.fill: parent
+ }
}
diff --git a/examples/quick/imageelements/animatedimage.qml b/examples/quick/imageelements/animatedimage.qml
index 6a6f12873b..114b5c9075 100644
--- a/examples/quick/imageelements/animatedimage.qml
+++ b/examples/quick/imageelements/animatedimage.qml
@@ -12,6 +12,7 @@ Column {
//! [image]
AnimatedImage {
id: animation
+
source: "pics/Uniflow_steam_engine.gif"
anchors.horizontalCenter: parent.horizontalCenter
speed: speedSlider.value
@@ -23,6 +24,7 @@ Column {
Rectangle {
id: timeline
+
color: "steelblue"
width: animation.width
height: 1
@@ -32,7 +34,8 @@ Column {
Rectangle {
property int frames: animation.frameCount
- width: 4; height: 8
+ width: 4
+ height: 8
x: (animation.width - width) * animation.currentFrame / frames
y: -4
color: "red"
@@ -51,6 +54,7 @@ Column {
Slider {
id: speedSlider
+
from: 0
to: 5
value: 1
@@ -65,6 +69,7 @@ Column {
TextMetrics {
id: fontMetrics
+
text: "100%"
font.pointSize: 12
}
diff --git a/examples/quick/imageelements/animatedsprite.qml b/examples/quick/imageelements/animatedsprite.qml
index ec89a02991..50da7cf0e9 100644
--- a/examples/quick/imageelements/animatedsprite.qml
+++ b/examples/quick/imageelements/animatedsprite.qml
@@ -14,6 +14,7 @@ Item {
//! [sprite]
AnimatedSprite {
id: sprite
+
anchors.centerIn: parent
source: "pics/speaker.png"
frameCount: 60
@@ -34,21 +35,21 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: (mouse) => {
if (!sprite.running) {
- sprite.start();
+ sprite.start()
} else if (!sprite.paused) {
- sprite.pause();
+ sprite.pause()
} else {
if (mouse.button === Qt.LeftButton)
- sprite.resume();
+ sprite.resume()
else if (mouse.button === Qt.MiddleButton)
- sprite.advance(-1);
+ sprite.advance(-1)
else if (mouse.button === Qt.RightButton)
- sprite.advance(1);
+ sprite.advance(1)
}
}
}
- Component.onCompleted: console.log("Press Space to toggle visibility. Click with mouse to pause/resume.")
+ Component.onCompleted: console.log(qsTr("Press Space to toggle visibility. Click with mouse to pause/resume."))
focus: true
Keys.onSpacePressed: sprite.visible = !sprite.visible
}
diff --git a/examples/quick/imageelements/borderimage.qml b/examples/quick/imageelements/borderimage.qml
index cd358de743..7c522a64be 100644
--- a/examples/quick/imageelements/borderimage.qml
+++ b/examples/quick/imageelements/borderimage.qml
@@ -5,11 +5,13 @@ import QtQuick
Rectangle {
id: page
+
width: 320
height: 480
BorderImageSelector {
id: selector
+
curIdx: 0
maxIdx: 3
gridWidth: 240
@@ -20,6 +22,7 @@ Rectangle {
Flickable {
id: mainFlickable
+
width: parent.width
anchors.bottom: parent.bottom
anchors.top: selector.bottom
@@ -29,52 +32,91 @@ Rectangle {
contentWidth: 1030
contentHeight: 420
Grid {
- anchors.centerIn: parent; spacing: 20
+ anchors.centerIn: parent
+ spacing: 20
MyBorderImage {
- minWidth: 120; maxWidth: 240; minHeight: 120; maxHeight: 200
- source: Qt.resolvedUrl("pics/colors.png"); margin: 30
+ minWidth: 120
+ maxWidth: 240
+ minHeight: 120
+ maxHeight: 200
+ source: Qt.resolvedUrl("pics/colors.png")
+ margin: 30
}
MyBorderImage {
- minWidth: 120; maxWidth: 240; minHeight: 120; maxHeight: 200
- source: Qt.resolvedUrl("pics/colors.png"); margin: 30
- horizontalMode: BorderImage.Repeat; verticalMode: BorderImage.Repeat
+ minWidth: 120
+ maxWidth: 240
+ minHeight: 120
+ maxHeight: 200
+ source: Qt.resolvedUrl("pics/colors.png")
+ margin: 30
+ horizontalMode: BorderImage.Repeat
+ verticalMode: BorderImage.Repeat
}
MyBorderImage {
- minWidth: 120; maxWidth: 240; minHeight: 120; maxHeight: 200
- source: Qt.resolvedUrl("pics/colors.png"); margin: 30
- horizontalMode: BorderImage.Stretch; verticalMode: BorderImage.Repeat
+ minWidth: 120
+ maxWidth: 240
+ minHeight: 120
+ maxHeight: 200
+ source: Qt.resolvedUrl("pics/colors.png")
+ margin: 30
+ horizontalMode: BorderImage.Stretch
+ verticalMode: BorderImage.Repeat
}
MyBorderImage {
- minWidth: 120; maxWidth: 240; minHeight: 120; maxHeight: 200
- source: Qt.resolvedUrl("pics/colors.png"); margin: 30
- horizontalMode: BorderImage.Round; verticalMode: BorderImage.Round
+ minWidth: 120
+ maxWidth: 240
+ minHeight: 120
+ maxHeight: 200
+ source: Qt.resolvedUrl("pics/colors.png")
+ margin: 30
+ horizontalMode: BorderImage.Round
+ verticalMode: BorderImage.Round
}
MyBorderImage {
- minWidth: 60; maxWidth: 200; minHeight: 40; maxHeight: 200
- source: Qt.resolvedUrl("pics/bw.png"); margin: 10
+ minWidth: 60
+ maxWidth: 200
+ minHeight: 40
+ maxHeight: 200
+ source: Qt.resolvedUrl("pics/bw.png")
+ margin: 10
}
MyBorderImage {
- minWidth: 60; maxWidth: 200; minHeight: 40; maxHeight: 200
- source: Qt.resolvedUrl("pics/bw.png"); margin: 10
- horizontalMode: BorderImage.Repeat; verticalMode: BorderImage.Repeat
+ minWidth: 60
+ maxWidth: 200
+ minHeight: 40
+ maxHeight: 200
+ source: Qt.resolvedUrl("pics/bw.png")
+ margin: 10
+ horizontalMode: BorderImage.Repeat
+ verticalMode: BorderImage.Repeat
}
MyBorderImage {
- minWidth: 60; maxWidth: 200; minHeight: 40; maxHeight: 200
- source: Qt.resolvedUrl("pics/bw.png"); margin: 10
- horizontalMode: BorderImage.Stretch; verticalMode: BorderImage.Repeat
+ minWidth: 60
+ maxWidth: 200
+ minHeight: 40
+ maxHeight: 200
+ source: Qt.resolvedUrl("pics/bw.png")
+ margin: 10
+ horizontalMode: BorderImage.Stretch
+ verticalMode: BorderImage.Repeat
}
MyBorderImage {
- minWidth: 60; maxWidth: 200; minHeight: 40; maxHeight: 200
- source: Qt.resolvedUrl("pics/bw.png"); margin: 10
- horizontalMode: BorderImage.Round; verticalMode: BorderImage.Round
+ minWidth: 60
+ maxWidth: 200
+ minHeight: 40
+ maxHeight: 200
+ source: Qt.resolvedUrl("pics/bw.png")
+ margin: 10
+ horizontalMode: BorderImage.Round
+ verticalMode: BorderImage.Round
}
}
}
diff --git a/examples/quick/imageelements/doc/src/imageelements.qdoc b/examples/quick/imageelements/doc/src/imageelements.qdoc
index 3caf49201b..06fa25ab19 100644
--- a/examples/quick/imageelements/doc/src/imageelements.qdoc
+++ b/examples/quick/imageelements/doc/src/imageelements.qdoc
@@ -6,6 +6,7 @@
\brief This is a collection of QML examples relating to image types.
\image qml-imageelements-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e{Image Elements} is a collection of small QML examples relating to image
types. For more information, visit \l{Use Case - Visual Elements In QML}.
diff --git a/examples/quick/imageelements/framestepping.qml b/examples/quick/imageelements/framestepping.qml
index 2f104248d8..a06aaae780 100644
--- a/examples/quick/imageelements/framestepping.qml
+++ b/examples/quick/imageelements/framestepping.qml
@@ -9,6 +9,7 @@ Rectangle {
height: 320
Image {
id: img
+
anchors.centerIn: parent
cache: true
source: "pics/multi.ico"
@@ -30,7 +31,8 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.margins: 6
horizontalAlignment: Text.AlignHCenter
- text: qsTr("frame " + (img.currentFrame + 1) + " of " + img.frameCount +
- "\nPress PgUp/PgDn to switch frames")
+ text: qsTr("frame %1 of %2 \nPress PgUp/PgDn to switch frames")
+ .arg(img.currentFrame + 1)
+ .arg(img.frameCount)
}
}
diff --git a/examples/quick/imageelements/image.qml b/examples/quick/imageelements/image.qml
index 1deaa9827d..11acb51054 100644
--- a/examples/quick/imageelements/image.qml
+++ b/examples/quick/imageelements/image.qml
@@ -1,12 +1,15 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
Rectangle {
width: 320
height: 480
Grid {
+ id: grid
+
property int cellWidth: (width - (spacing * (columns - 1))) / columns
property int cellHeight: (height - (spacing * (rows - 1))) / rows
@@ -18,8 +21,8 @@ Rectangle {
spacing: 30
component SizedImageCell: ImageCell {
- width: parent.cellWidth
- height: parent.cellHeight
+ width: grid.cellWidth
+ height: grid.cellHeight
}
SizedImageCell {
diff --git a/examples/quick/imageelements/imageelements.qml b/examples/quick/imageelements/imageelements.qml
index e50362be5f..3470a30879 100644
--- a/examples/quick/imageelements/imageelements.qml
+++ b/examples/quick/imageelements/imageelements.qml
@@ -9,16 +9,17 @@ Item {
width: 320
Shared.LauncherList {
id: ll
+
anchors.fill: parent
Component.onCompleted: {
- addExample("BorderImage", "An image with scaled borders", Qt.resolvedUrl("borderimage.qml"));
- addExample("Image", "A showcase of the options available to Image", Qt.resolvedUrl("image.qml"));
- addExample("Shadows", "Rectangles with a drop-shadow effect", Qt.resolvedUrl("shadows.qml"));
- addExample("AnimatedImage", "An image which plays animated formats", Qt.resolvedUrl("animatedimage.qml"));
- addExample("AnimatedSprite", "A simple sprite-based animation", Qt.resolvedUrl("animatedsprite.qml"));
- addExample("SpriteSequence", "A sprite-based animation with complex transitions", Qt.resolvedUrl("spritesequence.qml"));
- addExample("FrameStepping", "A multi-frame non-animated image", Qt.resolvedUrl("framestepping.qml"));
- addExample("MultiBorderImage", "A multi-frame image with scaled borders", Qt.resolvedUrl("multiframeborderimage.qml"));
+ addExample(qsTr("BorderImage"), qsTr("An image with scaled borders"), Qt.resolvedUrl("borderimage.qml"))
+ addExample(qsTr("Image"), qsTr("A showcase of the options available to Image"), Qt.resolvedUrl("image.qml"))
+ addExample(qsTr("Shadows"), qsTr("Rectangles with a drop-shadow effect"), Qt.resolvedUrl("shadows.qml"))
+ addExample(qsTr("AnimatedImage"), qsTr("An image which plays animated formats"), Qt.resolvedUrl("animatedimage.qml"))
+ addExample(qsTr("AnimatedSprite"), qsTr("A simple sprite-based animation"), Qt.resolvedUrl("animatedsprite.qml"))
+ addExample(qsTr("SpriteSequence"), qsTr("A sprite-based animation with complex transitions"), Qt.resolvedUrl("spritesequence.qml"))
+ addExample(qsTr("FrameStepping"), qsTr("A multi-frame non-animated image"), Qt.resolvedUrl("framestepping.qml"))
+ addExample(qsTr("MultiBorderImage"), qsTr("A multi-frame image with scaled borders"), Qt.resolvedUrl("multiframeborderimage.qml"))
}
}
}
diff --git a/examples/quick/imageelements/multiframeborderimage.qml b/examples/quick/imageelements/multiframeborderimage.qml
index fd4e2ba190..b5e76ae976 100644
--- a/examples/quick/imageelements/multiframeborderimage.qml
+++ b/examples/quick/imageelements/multiframeborderimage.qml
@@ -9,11 +9,17 @@ Rectangle {
height: 320
BorderImage {
id: img
+
anchors.fill: parent
anchors.margins: 6
cache: true
source: "pics/multi.ico"
- border { left: 19; top: 19; right: 19; bottom: 19 }
+ border {
+ left: 19
+ top: 19
+ right: 19
+ bottom: 19
+ }
horizontalTileMode: BorderImage.Stretch
Shortcut {
diff --git a/examples/quick/imageelements/shadows.qml b/examples/quick/imageelements/shadows.qml
index db78f04e98..3f345a3937 100644
--- a/examples/quick/imageelements/shadows.qml
+++ b/examples/quick/imageelements/shadows.qml
@@ -6,21 +6,28 @@ import QtQuick
Rectangle {
id: window
- width: 480; height: 320
+ width: 480
+ height: 320
color: "gray"
ShadowRectangle {
- anchors.centerIn: parent; width: 250; height: 250
+ anchors.centerIn: parent
+ width: 250
+ height: 250
color: "lightsteelblue"
}
ShadowRectangle {
- anchors.centerIn: parent; width: 200; height: 200
+ anchors.centerIn: parent
+ width: 200
+ height: 200
color: "steelblue"
}
ShadowRectangle {
- anchors.centerIn: parent; width: 150; height: 150
+ anchors.centerIn: parent
+ width: 150
+ height: 150
color: "thistle"
}
}
diff --git a/examples/quick/imageelements/spritesequence.qml b/examples/quick/imageelements/spritesequence.qml
index 6cd97dee74..d578700195 100644
--- a/examples/quick/imageelements/spritesequence.qml
+++ b/examples/quick/imageelements/spritesequence.qml
@@ -6,20 +6,36 @@ Item {
width: 320
height: 480
MouseArea {
- onClicked: anim.start();
+ onClicked: anim.start()
anchors.fill: parent
}
//! [animation]
SequentialAnimation {
id: anim
- ScriptAction { script: image.goalSprite = "falling"; }
- NumberAnimation { target: image; property: "y"; to: 480; duration: 12000; }
- ScriptAction { script: {image.goalSprite = ""; image.jumpTo("still");} }
- PropertyAction { target: image; property: "y"; value: 0 }
+
+ ScriptAction { script: image.goalSprite = "falling" }
+ NumberAnimation {
+ target: image
+ property: "y"
+ to: 480
+ duration: 12000
+ }
+ ScriptAction {
+ script: {
+ image.goalSprite = ""
+ image.jumpTo("still")
+ }
+ }
+ PropertyAction {
+ target: image
+ property: "y"
+ value: 0
+ }
}
//! [animation]
SpriteSequence {
id: image
+
width: 256
height: 256
anchors.horizontalCenter: parent.horizontalCenter
diff --git a/examples/quick/imageprovider/CMakeLists.txt b/examples/quick/imageprovider/CMakeLists.txt
index 0b880da74b..a2fa3e5087 100644
--- a/examples/quick/imageprovider/CMakeLists.txt
+++ b/examples/quick/imageprovider/CMakeLists.txt
@@ -1,47 +1,36 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(qmlimageproviderplugin LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/imageprovider/ImageProviderCore")
-set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples/quick/imageprovider)
+set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/examples/quick/imageprovider)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt6_add_qml_module(qmlimageproviderplugin
URI "ImageProviderCore"
- AUTO_RESOURCE_PREFIX
PLUGIN_TARGET qmlimageproviderplugin
NO_PLUGIN_OPTIONAL
NO_GENERATE_PLUGIN_SOURCE
CLASS_NAME ImageProviderExtensionPlugin
-)
-
-target_sources(qmlimageproviderplugin PRIVATE
- imageprovider.cpp
-)
-
-set_target_properties(qmlimageproviderplugin PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
+ SOURCES
+ imageprovider.cpp
)
target_link_libraries(qmlimageproviderplugin PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS qmlimageproviderplugin
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
diff --git a/examples/quick/imageprovider/doc/src/imageprovider.qdoc b/examples/quick/imageprovider/doc/src/imageprovider.qdoc
index 7a4f64285c..d4cc2c923d 100644
--- a/examples/quick/imageprovider/doc/src/imageprovider.qdoc
+++ b/examples/quick/imageprovider/doc/src/imageprovider.qdoc
@@ -5,6 +5,7 @@
\title C++ Extensions: Image Provider Example
\brief How to load images in QML with QQuickImageProvider.
\example imageprovider
+ \examplecategory {Graphics}
This examples shows how to use QQuickImageProvider to serve images
to QML image elements.
diff --git a/examples/quick/imageresponseprovider/CMakeLists.txt b/examples/quick/imageresponseprovider/CMakeLists.txt
index 0e64832d1a..52486af046 100644
--- a/examples/quick/imageresponseprovider/CMakeLists.txt
+++ b/examples/quick/imageresponseprovider/CMakeLists.txt
@@ -1,23 +1,19 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(qmlimageresponseproviderplugin LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/imageresponseprovider/ImageResponseProviderCore")
-set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples/quick/imageresponseprovider)
+set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/examples/quick/imageresponseprovider)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt6_add_qml_module(qmlimageresponseproviderplugin
URI "ImageResponseProviderCore"
- AUTO_RESOURCE_PREFIX
PLUGIN_TARGET qmlimageresponseproviderplugin
NO_PLUGIN_OPTIONAL
NO_GENERATE_PLUGIN_SOURCE
@@ -34,14 +30,14 @@ set_target_properties(qmlimageresponseproviderplugin PROPERTIES
)
target_link_libraries(qmlimageresponseproviderplugin PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS qmlimageresponseproviderplugin
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
diff --git a/examples/quick/imageresponseprovider/doc/src/imageresponseprovider.qdoc b/examples/quick/imageresponseprovider/doc/src/imageresponseprovider.qdoc
index f05118abbb..7d45057116 100644
--- a/examples/quick/imageresponseprovider/doc/src/imageresponseprovider.qdoc
+++ b/examples/quick/imageresponseprovider/doc/src/imageresponseprovider.qdoc
@@ -5,6 +5,7 @@
\title C++ Extensions: Image Response Provider Example
\example imageresponseprovider
\brief How to load images asynchronously in QML.
+ \examplecategory {Graphics}
This examples shows how to use QQuickImageProvider to serve images
diff --git a/examples/quick/itemvariablerefreshrate/CMakeLists.txt b/examples/quick/itemvariablerefreshrate/CMakeLists.txt
index a08fc01aca..ca65a005e1 100644
--- a/examples/quick/itemvariablerefreshrate/CMakeLists.txt
+++ b/examples/quick/itemvariablerefreshrate/CMakeLists.txt
@@ -4,15 +4,9 @@
cmake_minimum_required(VERSION 3.16)
project(itemvariablerefreshrate LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/itemvariablerefreshrate")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(itemvariablerefreshrateexample
WIN32
@@ -21,15 +15,14 @@ qt_add_executable(itemvariablerefreshrateexample
)
target_link_libraries(itemvariablerefreshrateexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(itemvariablerefreshrateexample
URI itemvariablerefreshrate
- AUTO_RESOURCE_PREFIX
QML_FILES
"itemvariablerefreshrate.qml"
RESOURCES
@@ -37,7 +30,16 @@ qt_add_qml_module(itemvariablerefreshrateexample
)
install(TARGETS itemvariablerefreshrateexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET itemvariablerefreshrateexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/itemvariablerefreshrate/doc/src/itemvariablerefreshrate.qdoc b/examples/quick/itemvariablerefreshrate/doc/src/itemvariablerefreshrate.qdoc
index 5be8cd36eb..51fbc2c523 100644
--- a/examples/quick/itemvariablerefreshrate/doc/src/itemvariablerefreshrate.qdoc
+++ b/examples/quick/itemvariablerefreshrate/doc/src/itemvariablerefreshrate.qdoc
@@ -7,6 +7,7 @@
\brief A Qt Quick example shows how to setup variable refresh rate for
specific parts of the UI.
\ingroup qtquickexamples
+ \examplecategory {Graphics}
This example demonstrates using the live property of a \l{Item Layers} to get a
variable and independent refresh rate for Quick items. This can provide a significant
diff --git a/examples/quick/keyinteraction/CMakeLists.txt b/examples/quick/keyinteraction/CMakeLists.txt
index 6524b87fcf..926e5e58a3 100644
--- a/examples/quick/keyinteraction/CMakeLists.txt
+++ b/examples/quick/keyinteraction/CMakeLists.txt
@@ -1,34 +1,27 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(keyinteraction LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/keyinteraction")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(keyinteractionexample WIN32 MACOSX_BUNDLE
main.cpp
)
target_link_libraries(keyinteractionexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
# Resources:
qt_add_qml_module(keyinteractionexample
URI keyinteraction
- AUTO_RESOURCE_PREFIX
QML_FILES
"ContextMenu.qml"
"GridMenu.qml"
@@ -43,7 +36,16 @@ qt_add_qml_module(keyinteractionexample
)
install(TARGETS keyinteractionexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET keyinteractionexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/keyinteraction/ContextMenu.qml b/examples/quick/keyinteraction/ContextMenu.qml
index 6364118602..22d149fd9d 100644
--- a/examples/quick/keyinteraction/ContextMenu.qml
+++ b/examples/quick/keyinteraction/ContextMenu.qml
@@ -5,6 +5,7 @@ import QtQuick
FocusScope {
id: container
+
required property Item keyRightTarget
property bool open: false
@@ -19,10 +20,14 @@ FocusScope {
Keys.onRightPressed: container.keyRightTarget.focus = true
Text {
- anchors { top: parent.top; horizontalCenter: parent.horizontalCenter; margins: 30 }
+ anchors {
+ top: parent.top
+ horizontalCenter: parent.horizontalCenter
+ margins: 30
+ }
color: "black"
font.pixelSize: 14
- text: "Context Menu"
+ text: qsTr("Context Menu")
}
}
}
diff --git a/examples/quick/keyinteraction/GridMenu.qml b/examples/quick/keyinteraction/GridMenu.qml
index d21f846089..b17a72bdc8 100644
--- a/examples/quick/keyinteraction/GridMenu.qml
+++ b/examples/quick/keyinteraction/GridMenu.qml
@@ -1,10 +1,12 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
FocusScope {
id: menu
+
property alias interactive: gridView.interactive
required property Item keyUpTarget
required property Item keyDownTarget
@@ -14,14 +16,24 @@ FocusScope {
anchors.fill: parent
clip: true
gradient: Gradient {
- GradientStop { position: 0.0; color: "#193441" }
- GradientStop { position: 1.0; color: Qt.darker("#193441") }
+ GradientStop {
+ position: 0.0
+ color: "#193441"
+ }
+ GradientStop {
+ position: 1.0
+ color: Qt.darker("#193441")
+ }
}
GridView {
id: gridView
- anchors.fill: parent; anchors.leftMargin: 20; anchors.rightMargin: 20
- cellWidth: 152; cellHeight: 152
+
+ anchors.fill: parent
+ anchors.leftMargin: 20
+ anchors.rightMargin: 20
+ cellWidth: 152
+ cellHeight: 152
focus: true
model: 12
@@ -31,22 +43,36 @@ FocusScope {
delegate: Item {
id: container
- width: GridView.view.cellWidth
- height: GridView.view.cellHeight
+
+ width: gridView.cellWidth
+ height: gridView.cellHeight
required property int index
Rectangle {
id: content
+
color: "transparent"
antialiasing: true
- anchors.fill: parent; anchors.margins: 20; radius: 10
+ anchors.fill: parent
+ anchors.margins: 20
+ radius: 10
- Rectangle { color: "#91AA9D"; anchors.fill: parent; anchors.margins: 3; radius: 8; antialiasing: true }
- Image { source: "images/qt-logo.png"; anchors.centerIn: parent }
+ Rectangle {
+ color: "#91AA9D"
+ anchors.fill: parent
+ anchors.margins: 3
+ radius: 8
+ antialiasing: true
+ }
+ Image {
+ source: "images/qt-logo.png"
+ anchors.centerIn: parent
+ }
}
MouseArea {
id: mouseArea
+
anchors.fill: parent
hoverEnabled: true
@@ -57,7 +83,8 @@ FocusScope {
}
states: State {
- name: "active"; when: container.activeFocus
+ name: "active"
+ when: container.activeFocus
PropertyChanges {
content {
color: "#FCFFF5"
@@ -67,7 +94,10 @@ FocusScope {
}
transitions: Transition {
- NumberAnimation { properties: "scale"; duration: 100 }
+ NumberAnimation {
+ properties: "scale"
+ duration: 100
+ }
}
}
}
diff --git a/examples/quick/keyinteraction/ListMenu.qml b/examples/quick/keyinteraction/ListMenu.qml
index 47201099ff..0f4763e1b2 100644
--- a/examples/quick/keyinteraction/ListMenu.qml
+++ b/examples/quick/keyinteraction/ListMenu.qml
@@ -6,67 +6,117 @@ import QtQuick
FocusScope {
id: menu
+
clip: true
required property Item keyUpTarget
required property Item keyLeftTarget
ListView {
id: list1
- y: activeFocus ? 10 : 40; width: parent.width / 3; height: parent.height - 20
+
+ y: activeFocus ? 10 : 40
+ width: parent.width / 3
+ height: parent.height - 20
focus: true
+
KeyNavigation.up: menu.keyUpTarget
KeyNavigation.left: menu.keyLeftTarget
KeyNavigation.right: list2
- model: 10; cacheBuffer: 200
+
+ model: 10
+ cacheBuffer: 200
delegate: ListViewDelegate {}
Behavior on y {
- NumberAnimation { duration: 600; easing.type: Easing.OutQuint }
+ NumberAnimation {
+ duration: 600
+ easing.type: Easing.OutQuint
+ }
}
}
ListView {
id: list2
- y: activeFocus ? 10 : 40; x: parseInt(parent.width / 3); width: parent.width / 3; height: parent.height - 20
+
+ y: activeFocus ? 10 : 40
+ x: parseInt(parent.width / 3)
+ width: parent.width / 3
+ height: parent.height - 20
+
KeyNavigation.up: menu.keyUpTarget
KeyNavigation.left: list1
KeyNavigation.right: list3
- model: 10; cacheBuffer: 200
+
+ model: 10
+ cacheBuffer: 200
delegate: ListViewDelegate {}
Behavior on y {
- NumberAnimation { duration: 600; easing.type: Easing.OutQuint }
+ NumberAnimation {
+ duration: 600
+ easing.type: Easing.OutQuint
+ }
}
}
ListView {
id: list3
- y: activeFocus ? 10 : 40; x: parseInt(2 * parent.width / 3); width: parent.width / 3; height: parent.height - 20
+
+ y: activeFocus ? 10 : 40
+ x: parseInt(2 * parent.width / 3)
+ width: parent.width / 3
+ height: parent.height - 20
+
KeyNavigation.up: menu.keyUpTarget
KeyNavigation.left: list2
- model: 10; cacheBuffer: 200
+
+ model: 10
+ cacheBuffer: 200
delegate: ListViewDelegate {}
Behavior on y {
- NumberAnimation { duration: 600; easing.type: Easing.OutQuint }
+ NumberAnimation {
+ duration: 600
+ easing.type: Easing.OutQuint
+ }
}
}
- Rectangle { width: parent.width; height: 1; color: "#D1DBBD" }
+ Rectangle {
+ width: parent.width
+ height: 1
+ color: "#D1DBBD"
+ }
Rectangle {
- y: 1; width: parent.width; height: 10
+ y: 1
+ width: parent.width
+ height: 10
gradient: Gradient {
- GradientStop { position: 0.0; color: "#3E606F" }
- GradientStop { position: 1.0; color: "transparent" }
+ GradientStop {
+ position: 0.0
+ color: "#3E606F"
+ }
+ GradientStop {
+ position: 1.0
+ color: "transparent"
+ }
}
}
Rectangle {
- y: parent.height - 10; width: parent.width; height: 10
+ y: parent.height - 10
+ width: parent.width
+ height: 10
gradient: Gradient {
- GradientStop { position: 1.0; color: "#3E606F" }
- GradientStop { position: 0.0; color: "transparent" }
+ GradientStop {
+ position: 1.0
+ color: "#3E606F"
+ }
+ GradientStop {
+ position: 0.0
+ color: "transparent"
+ }
}
}
}
diff --git a/examples/quick/keyinteraction/ListViewDelegate.qml b/examples/quick/keyinteraction/ListViewDelegate.qml
index 4b2424f393..8ae43c8ab5 100644
--- a/examples/quick/keyinteraction/ListViewDelegate.qml
+++ b/examples/quick/keyinteraction/ListViewDelegate.qml
@@ -7,28 +7,42 @@ Item {
id: container
required property int index
- width: ListView.view.width; height: 60; anchors.leftMargin: 10; anchors.rightMargin: 10
+ width: ListView.view.width
+ height: 60
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
Rectangle {
id: content
- anchors.centerIn: parent; width: container.width - 40; height: container.height - 10
+
+ anchors.centerIn: parent
+ width: container.width - 40
+ height: container.height - 10
color: "transparent"
antialiasing: true
radius: 10
- Rectangle { anchors.fill: parent; anchors.margins: 3; color: "#91AA9D"; antialiasing: true; radius: 8 }
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 3
+ color: "#91AA9D"
+ antialiasing: true
+ radius: 8
+ }
}
Text {
id: label
+
anchors.centerIn: content
- text: "List element " + (container.index + 1)
+ text: qsTr("List element ") + (container.index + 1)
color: "#193441"
font.pixelSize: 14
}
MouseArea {
id: mouseArea
+
anchors.fill: parent
hoverEnabled: true
@@ -39,7 +53,8 @@ Item {
}
states: State {
- name: "active"; when: container.activeFocus
+ name: "active"
+ when: container.activeFocus
PropertyChanges {
content {
color: "#FCFFF5"
@@ -50,6 +65,9 @@ Item {
}
transitions: Transition {
- NumberAnimation { properties: "scale"; duration: 100 }
+ NumberAnimation {
+ properties: "scale"
+ duration: 100
+ }
}
}
diff --git a/examples/quick/keyinteraction/TabMenu.qml b/examples/quick/keyinteraction/TabMenu.qml
index e3c622aca1..04451b3206 100644
--- a/examples/quick/keyinteraction/TabMenu.qml
+++ b/examples/quick/keyinteraction/TabMenu.qml
@@ -1,6 +1,7 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
FocusScope {
@@ -12,19 +13,30 @@ FocusScope {
anchors.fill: parent
clip: true
gradient: Gradient {
- GradientStop { position: 0.0; color: "#193441" }
- GradientStop { position: 1.0; color: Qt.darker("#193441") }
+ GradientStop {
+ position: 0.0
+ color: "#193441"
+ }
+ GradientStop {
+ position: 1.0
+ color: Qt.darker("#193441")
+ }
}
Row {
id: tabView
- anchors.fill: parent; anchors.leftMargin: 20; anchors.rightMargin: 20
+
+ anchors.fill: parent
+ anchors.leftMargin: 20
+ anchors.rightMargin: 20
Repeater {
activeFocusOnTab: false
model: 5
Item {
id: container
- width: 152; height: 152
+
+ width: 152
+ height: 152
activeFocusOnTab: true
focus: true
@@ -35,10 +47,21 @@ FocusScope {
id: content
color: "transparent"
antialiasing: true
- anchors.fill: parent; anchors.margins: 20; radius: 10
+ anchors.fill: parent
+ anchors.margins: 20
+ radius: 10
- Rectangle { color: "#91AA9D"; anchors.fill: parent; anchors.margins: 3; radius: 8; antialiasing: true }
- Image { source: "images/qt-logo.png"; anchors.centerIn: parent }
+ Rectangle {
+ color: "#91AA9D"
+ anchors.fill: parent
+ anchors.margins: 3
+ radius: 8
+ antialiasing: true
+ }
+ Image {
+ source: "images/qt-logo.png"
+ anchors.centerIn: parent
+ }
}
MouseArea {
@@ -52,7 +75,8 @@ FocusScope {
}
states: State {
- name: "active"; when: container.activeFocus
+ name: "active"
+ when: container.activeFocus
PropertyChanges {
content {
color: "#FCFFF5"
@@ -62,7 +86,10 @@ FocusScope {
}
transitions: Transition {
- NumberAnimation { properties: "scale"; duration: 100 }
+ NumberAnimation {
+ properties: "scale"
+ duration: 100
+ }
}
}
}
diff --git a/examples/quick/keyinteraction/doc/src/keyinteraction.qdoc b/examples/quick/keyinteraction/doc/src/keyinteraction.qdoc
index 1b1cc47138..378169452c 100644
--- a/examples/quick/keyinteraction/doc/src/keyinteraction.qdoc
+++ b/examples/quick/keyinteraction/doc/src/keyinteraction.qdoc
@@ -6,6 +6,7 @@
\brief This is a collection of QML keyboard interaction examples.
\image qml-keyinteraction-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e{Keyboard Focus in Qt Quick} combines various methods of
handling keyboard focus. For more information, visit
diff --git a/examples/quick/keyinteraction/focus.qml b/examples/quick/keyinteraction/focus.qml
index 385f35d05b..42edcb5f01 100644
--- a/examples/quick/keyinteraction/focus.qml
+++ b/examples/quick/keyinteraction/focus.qml
@@ -7,18 +7,23 @@ import QtQuick
Rectangle {
id: window
- width: 800; height: 640
+ width: 800
+ height: 640
color: "#3E606F"
FocusScope {
id: mainView
- width: parent.width; height: parent.height
+ width: parent.width
+ height: parent.height
focus: true
TabMenu {
id: tabMenu
- y: 160; width: parent.width; height: 160
+
+ y: 160
+ width: parent.width
+ height: 160
keyUpTarget: listMenu
keyDownTarget: gridMenu
@@ -34,7 +39,10 @@ Rectangle {
GridMenu {
id: gridMenu
- y: 320; width: parent.width; height: 320
+
+ y: 320
+ width: parent.width
+ height: 320
activeFocusOnTab: true
keyUpTarget: tabMenu
@@ -49,7 +57,10 @@ Rectangle {
ListMenu {
id: listMenu
- y: 640; width: parent.width; height: 320
+
+ y: 640
+ width: parent.width
+ height: 320
activeFocusOnTab: true
keyUpTarget: gridMenu
@@ -72,7 +83,7 @@ Rectangle {
State {
name: "showTabViews"
PropertyChanges {
- tabMenu.y: 160
+ tabMenu.y: 160
gridMenu.y: 320
listMenu.y: 640
}
@@ -81,7 +92,7 @@ Rectangle {
State {
name: "showGridViews"
PropertyChanges {
- tabMenu.y: 0
+ tabMenu.y: 0
gridMenu.y: 160
listMenu.y: 480
}
@@ -98,7 +109,11 @@ Rectangle {
]
transitions: Transition {
- NumberAnimation { properties: "y"; duration: 600; easing.type: Easing.OutQuint }
+ NumberAnimation {
+ properties: "y"
+ duration: 600
+ easing.type: Easing.OutQuint
+ }
}
}
@@ -108,7 +123,8 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
MouseArea {
- anchors.fill: parent; anchors.margins: -10
+ anchors.fill: parent
+ anchors.margins: -10
onClicked: contextMenu.focus = true
}
}
@@ -135,6 +151,10 @@ Rectangle {
}
transitions: Transition {
- NumberAnimation { properties: "x,opacity"; duration: 600; easing.type: Easing.OutQuint }
+ NumberAnimation {
+ properties: "x,opacity"
+ duration: 600
+ easing.type: Easing.OutQuint
+ }
}
}
diff --git a/examples/quick/layouts/CMakeLists.txt b/examples/quick/layouts/CMakeLists.txt
index 74235b1553..2979b8173d 100644
--- a/examples/quick/layouts/CMakeLists.txt
+++ b/examples/quick/layouts/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(layouts LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/layouts")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(layoutsexample
WIN32
@@ -22,20 +16,28 @@ qt_add_executable(layoutsexample
qt_add_qml_module(layoutsexample
URI layouts
- AUTO_RESOURCE_PREFIX
QML_FILES
"layouts.qml"
)
target_link_libraries(layoutsexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS layoutsexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET layoutsexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc b/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc
index dcbbaeeed7..a7b021f1f9 100644
--- a/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc
+++ b/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc
@@ -6,6 +6,7 @@
\brief Demonstrates how to use layout types to arrange a UI.
\image qtquicklayouts-example-layouts.png
\ingroup qtquickexamples
+ \examplecategory {User Interface Components}
This example shows how to easily arrange UI components into
\l{Qt Quick Layouts}{layouts} with \l{GridLayout}, \l{RowLayout}, and
diff --git a/examples/quick/layouts/layouts.qml b/examples/quick/layouts/layouts.qml
index eb6554cb80..92fe305559 100644
--- a/examples/quick/layouts/layouts.qml
+++ b/examples/quick/layouts/layouts.qml
@@ -5,12 +5,14 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
+pragma ComponentBehavior: Bound
+
ApplicationWindow {
id: appWindow
visible: true
- title: "Basic layouts"
- property int margin: 11
+ title: qsTr("Basic layouts")
+ readonly property int margin: 11
Component.onCompleted: {
width = mainLayout.implicitWidth + 2 * margin
@@ -26,27 +28,30 @@ ApplicationWindow {
anchors.margins: appWindow.margin
GroupBox {
id: rowBox
- title: "Row layout"
+ title: qsTr("Row layout")
Layout.fillWidth: true
+ Layout.fillHeight: false
Layout.minimumWidth: rowLayout.Layout.minimumWidth + 30
RowLayout {
id: rowLayout
anchors.fill: parent
TextField {
- placeholderText: "This wants to grow horizontally"
+ placeholderText: qsTr("This wants to grow horizontally")
Layout.fillWidth: true
}
Button {
- text: "Button"
+ text: qsTr("Button")
+ Layout.fillWidth: false
}
}
}
GroupBox {
id: gridBox
- title: "Grid layout"
+ title: qsTr("Grid layout")
Layout.fillWidth: true
+ Layout.fillHeight: false
Layout.minimumWidth: gridLayout.Layout.minimumWidth + 30
GridLayout {
@@ -55,17 +60,17 @@ ApplicationWindow {
flow: GridLayout.TopToBottom
anchors.fill: parent
- Label { text: "Line 1" }
- Label { text: "Line 2" }
- Label { text: "Line 3" }
+ Label { text: qsTr("Line 1") }
+ Label { text: qsTr("Line 2") }
+ Label { text: qsTr("Line 3") }
TextField { }
TextField { }
TextField { }
TextArea {
- text: "This widget spans over three rows in the GridLayout.\n"
- + "All items in the GridLayout are implicitly positioned from top to bottom."
+ text: qsTr("This widget spans over three rows in the GridLayout.\n")
+ + qsTr("All items in the GridLayout are implicitly positioned from top to bottom.")
wrapMode: TextArea.WordWrap
Layout.rowSpan: 3
Layout.fillHeight: true
@@ -77,14 +82,14 @@ ApplicationWindow {
}
TextArea {
id: t3
- text: "This fills the whole cell"
+ text: qsTr("This fills the whole cell")
Layout.minimumHeight: 30
Layout.fillHeight: true
Layout.fillWidth: true
}
GroupBox {
id: stackBox
- title: "Stack layout"
+ title: qsTr("Stack layout")
implicitWidth: 200
implicitHeight: 60
Layout.minimumHeight: 60
@@ -104,8 +109,8 @@ ApplicationWindow {
color: Qt.hsla((0.5 + index) / stackRepeater.count, 0.3, 0.7, 1)
Button {
anchors.centerIn: parent
- text: "Page " + (parent.index + 1)
- onClicked: { stackLayout.advance() }
+ text: qsTr("Page ") + (parent.index + 1)
+ onClicked: stackLayout.advance()
}
}
}
diff --git a/examples/quick/layouts/layouts.qmlproject b/examples/quick/layouts/layouts.qmlproject
deleted file mode 100644
index e5a8bf02ca..0000000000
--- a/examples/quick/layouts/layouts.qmlproject
+++ /dev/null
@@ -1,16 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "main.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/localstorage/CMakeLists.txt b/examples/quick/localstorage/CMakeLists.txt
index cd19eca89b..cf39d98c20 100644
--- a/examples/quick/localstorage/CMakeLists.txt
+++ b/examples/quick/localstorage/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(localstorage LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/localstorage")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(localstorageexample
WIN32
@@ -22,7 +16,6 @@ qt_add_executable(localstorageexample
qt_add_qml_module(localstorageexample
URI localstorage
- AUTO_RESOURCE_PREFIX
QML_FILES
"Database.js"
"Header.qml"
@@ -32,14 +25,23 @@ qt_add_qml_module(localstorageexample
)
target_link_libraries(localstorageexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS localstorageexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET localstorageexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/localstorage/Database.js b/examples/quick/localstorage/Database.js
index 18bf4a25cd..dd66ce8246 100644
--- a/examples/quick/localstorage/Database.js
+++ b/examples/quick/localstorage/Database.js
@@ -3,7 +3,7 @@
function dbInit()
{
- var db = LocalStorage.openDatabaseSync("Activity_Tracker_DB", "", "Track exercise", 1000000)
+ let db = LocalStorage.openDatabaseSync("Activity_Tracker_DB", "", "Track exercise", 1000000)
try {
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS trip_log (date text,trip_desc text,distance numeric)')
@@ -26,12 +26,12 @@ function dbGetHandle()
function dbInsert(Pdate, Pdesc, Pdistance)
{
- var db = dbGetHandle()
- var rowid = 0;
+ let db = dbGetHandle()
+ let rowid = 0;
db.transaction(function (tx) {
tx.executeSql('INSERT INTO trip_log VALUES(?, ?, ?)',
[Pdate, Pdesc, Pdistance])
- var result = tx.executeSql('SELECT last_insert_rowid()')
+ let result = tx.executeSql('SELECT last_insert_rowid()')
rowid = result.insertId
})
return rowid;
@@ -39,11 +39,11 @@ function dbInsert(Pdate, Pdesc, Pdistance)
function dbReadAll()
{
- var db = dbGetHandle()
+ let db = dbGetHandle()
db.transaction(function (tx) {
- var results = tx.executeSql(
- 'SELECT rowid,date,trip_desc,distance FROM trip_log order by rowid desc')
- for (var i = 0; i < results.rows.length; i++) {
+ let results = tx.executeSql(
+ 'SELECT rowid,date,trip_desc,distance FROM trip_log order by rowid desc')
+ for (let i = 0; i < results.rows.length; i++) {
listModel.append({
id: results.rows.item(i).rowid,
checked: " ",
@@ -57,7 +57,7 @@ function dbReadAll()
function dbUpdate(Pdate, Pdesc, Pdistance, Prowid)
{
- var db = dbGetHandle()
+ let db = dbGetHandle()
db.transaction(function (tx) {
tx.executeSql(
'update trip_log set date=?, trip_desc=?, distance=? where rowid = ?', [Pdate, Pdesc, Pdistance, Prowid])
@@ -66,7 +66,7 @@ function dbUpdate(Pdate, Pdesc, Pdistance, Prowid)
function dbDeleteRow(Prowid)
{
- var db = dbGetHandle()
+ let db = dbGetHandle()
db.transaction(function (tx) {
tx.executeSql('delete from trip_log where rowid = ?', [Prowid])
})
diff --git a/examples/quick/localstorage/Header.qml b/examples/quick/localstorage/Header.qml
index 2274747564..a539452844 100644
--- a/examples/quick/localstorage/Header.qml
+++ b/examples/quick/localstorage/Header.qml
@@ -8,15 +8,15 @@ import "Database.js" as JS
Item {
id: root
- width: Screen.width / 2
- height: Screen.height / 7
-
required property ListView listView
signal statusMessage(string msg)
+
+ width: Screen.width / 2
+ height: Screen.height / 7
enabled: false
function insertrec() {
- var rowid = parseInt(JS.dbInsert(dateInput.text, descInput.text, distInput.text), 10)
+ const rowid = parseInt(JS.dbInsert(dateInput.text, descInput.text, distInput.text), 10)
if (rowid) {
listView.model.setProperty(listView.currentIndex, "id", rowid)
listView.forceLayout()
@@ -99,13 +99,13 @@ Item {
font.pixelSize: 22
activeFocusOnPress: true
activeFocusOnTab: true
+ Layout.fillWidth: false
ToolTip {
- parent: dateInput
x: parent.width + 3
y: (parent.height - height) / 2
text: qsTr("Date format = 'YYYY-MM-DD'")
- visible: parent.enabled && parent.hovered
+ visible: dateInput.enabled && dateInput.hovered
delay: 1000
}
@@ -113,13 +113,13 @@ Item {
regularExpression: /\d{4}[,.:/-]\d\d?[,.:/-]\d\d?/
}
- onFocusChanged: ()=> {
+ onFocusChanged: function() {
if (!dateInput.focus && !acceptableInput && root.enabled)
root.statusMessage(qsTr("Please fill in the date"));
}
- onEditingFinished: ()=> {
- let regex = /(\d+)[,.:/-](\d+)[,.:/-](\d+)/
+ onEditingFinished: function() {
+ const regex = /(\d+)[,.:/-](\d+)[,.:/-](\d+)/
if (dateInput.text.match(regex))
dateInput.text = dateInput.text.replace(regex, '$1-$2-$3')
}
@@ -127,29 +127,31 @@ Item {
TextField {
id: descInput
+ property string oldString
font.pixelSize: 22
activeFocusOnPress: true
activeFocusOnTab: true
- property string oldString
- onFocusChanged: ()=> { if (focus) oldString = descInput.text; }
- onEditingFinished: ()=> {
- if (descInput.text.length < 8 && descInput.text != descInput.oldString && root.enabled)
+ Layout.fillWidth: false
+ onFocusChanged: if (focus) oldString = descInput.text
+ onEditingFinished: function() {
+ if (descInput.text.length < 8 && descInput.text !== descInput.oldString && root.enabled)
root.statusMessage(qsTr("Enter a description of minimum 8 characters"))
}
}
TextField {
id: distInput
+ property string oldString
font.pixelSize: 22
activeFocusOnPress: true
activeFocusOnTab: true
+ Layout.fillWidth: false
validator: RegularExpressionValidator {
regularExpression: /\d{1,3}/
}
- property string oldString
- onFocusChanged: ()=> { if (focus) oldString = distInput.text; }
- onEditingFinished: ()=> {
- if (distInput.text == "" && distInput.text != distInput.oldString && root.enabled)
+ onFocusChanged: if (focus) oldString = distInput.text
+ onEditingFinished: function() {
+ if (distInput.text === "" && distInput.text !== distInput.oldString && root.enabled)
root.statusMessage(qsTr("Please fill in the distance"))
}
}
diff --git a/examples/quick/localstorage/MyDelegate.qml b/examples/quick/localstorage/MyDelegate.qml
index 30ccd77749..94f78b5ed6 100644
--- a/examples/quick/localstorage/MyDelegate.qml
+++ b/examples/quick/localstorage/MyDelegate.qml
@@ -4,15 +4,10 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import QtQuick.LocalStorage
-import "Database.js" as JS
Item {
id: delegate
- width: ListView.view.width
- implicitHeight: rDate.implicitHeight * 1.5
-
required property int index
required property int distance
required property string trip_desc
@@ -20,13 +15,18 @@ Item {
signal clicked()
+ width: ListView.view.width
+ implicitHeight: rDate.implicitHeight * 1.5
+
Rectangle {
id: baseRec
anchors.fill: parent
opacity: 0.8
color: delegate.index % 2 ? "lightgrey" : "grey"
- border.width: 2
- border.color: Qt.lighter(color)
+ border {
+ width: 2
+ color: Qt.lighter(color)
+ }
radius: 5
MouseArea {
diff --git a/examples/quick/localstorage/doc/src/localstorage.qdoc b/examples/quick/localstorage/doc/src/localstorage.qdoc
index 653d5dc62e..e9596987dc 100644
--- a/examples/quick/localstorage/doc/src/localstorage.qdoc
+++ b/examples/quick/localstorage/doc/src/localstorage.qdoc
@@ -3,6 +3,7 @@
/*!
\title Qt Quick Examples - Local Storage
\example localstorage
+ \examplecategory {Data Processing & I/O}
\brief A collection of QML local storage examples.
\borderedimage qml-localstorage-example.png
diff --git a/examples/quick/localstorage/localstorage.qml b/examples/quick/localstorage/localstorage.qml
index 1f8ef79db7..3bed3e99df 100644
--- a/examples/quick/localstorage/localstorage.qml
+++ b/examples/quick/localstorage/localstorage.qml
@@ -7,16 +7,19 @@ import QtQuick.Layouts
import QtQuick.LocalStorage
import "Database.js" as JS
+pragma ComponentBehavior: Bound
+
Window {
id: window
+
+ property bool creatingNewEntry: false
+ property bool editingEntry: false
+
visible: true
width: Screen.width / 2
height: Screen.height / 1.8
color: "#161616"
- property bool creatingNewEntry: false
- property bool editingEntry: false
-
Rectangle {
anchors.fill: parent
@@ -34,6 +37,7 @@ Window {
RowLayout {
Button {
text: qsTr("New")
+ Layout.fillWidth: false
onClicked: {
input.initrec_new()
window.creatingNewEntry = true
@@ -42,10 +46,11 @@ Window {
}
Button {
id: saveButton
- enabled: (window.creatingNewEntry || window.editingEntry) && listView.currentIndex != -1
+ enabled: (window.creatingNewEntry || window.editingEntry) && listView.currentIndex !== -1
text: qsTr("Save")
+ Layout.fillWidth: false
onClicked: {
- var insertedRow = false;
+ let insertedRow = false;
if (listView.model.get(listView.currentIndex).id < 1) {
//insert mode
if (input.insertrec()) {
@@ -76,7 +81,8 @@ Window {
Button {
id: editButton
text: qsTr("Edit")
- enabled: !window.creatingNewEntry && !window.editingEntry && listView.currentIndex != -1
+ enabled: !window.creatingNewEntry && !window.editingEntry && listView.currentIndex !== -1
+ Layout.fillWidth: false
onClicked: {
input.editrec(listView.model.get(listView.currentIndex).date,
listView.model.get(listView.currentIndex).trip_desc,
@@ -89,11 +95,12 @@ Window {
Button {
id: deleteButton
text: qsTr("Delete")
- enabled: !window.creatingNewEntry && listView.currentIndex != -1
+ enabled: !window.creatingNewEntry && listView.currentIndex !== -1
+ Layout.fillWidth: false
onClicked: {
JS.dbDeleteRow(listView.model.get(listView.currentIndex).id)
listView.model.remove(listView.currentIndex, 1)
- if (listView.count == 0) {
+ if (listView.count === 0) {
// ListView doesn't automatically set its currentIndex to -1
// when the count becomes 0.
listView.currentIndex = -1
@@ -103,7 +110,8 @@ Window {
Button {
id: cancelButton
text: qsTr("Cancel")
- enabled: (window.creatingNewEntry || window.editingEntry) && listView.currentIndex != -1
+ enabled: (window.creatingNewEntry || window.editingEntry) && listView.currentIndex !== -1
+ Layout.fillWidth: false
onClicked: {
if (listView.model.get(listView.currentIndex).id === 0) {
// This entry had an id of 0, which means it was being created and hadn't
@@ -118,12 +126,13 @@ Window {
}
Button {
text: qsTr("Exit")
+ Layout.fillWidth: false
onClicked: Qt.quit()
}
}
Item {
Layout.fillWidth: true
- height: 5
+ Layout.preferredHeight: 5
}
Label {
Layout.alignment: Qt.AlignCenter
@@ -133,8 +142,8 @@ Window {
Component {
id: highlightBar
Rectangle {
- width: listView.currentItem !== null ? listView.currentItem.width : implicitWidth
- height: listView.currentItem !== null ? listView.currentItem.height : implicitHeight
+ width: listView.currentItem?.width ?? implicitWidth
+ height: listView.currentItem?.height ?? implicitHeight
color: "lightgreen"
}
}
@@ -157,19 +166,23 @@ Window {
header: Component {
RowLayout {
- property var headerTitles: [qsTr("Date"), qsTr("Description"), qsTr("Distance")]
width: ListView.view.width
Repeater {
- model: headerTitles
+ model: [qsTr("Date"), qsTr("Description"), qsTr("Distance")]
delegate: Label {
id: headerTitleDelegate
+
+ required property string modelData
+
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: 1
text: modelData
- font.pointSize: 15
- font.bold: true
- font.underline: true
+ font {
+ pointSize: 15
+ bold: true
+ underline: true
+ }
padding: 12
horizontalAlignment: Label.AlignHCenter
}
@@ -183,7 +196,7 @@ Window {
font.bold: true
font.pointSize: 20
opacity: 0.0
- visible: opacity !== 0 // properly cull item if effectively invisible
+ visible: opacity > 0 // properly cull item if effectively invisible
Layout.alignment: Layout.Center
function displayWarning(text) {
diff --git a/examples/quick/models/CMakeLists.txt b/examples/quick/models/CMakeLists.txt
index 0fe51771df..e8756b65ec 100644
--- a/examples/quick/models/CMakeLists.txt
+++ b/examples/quick/models/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(abstractitemmodel)
qt_internal_add_example(objectlistmodel)
diff --git a/examples/quick/models/abstractitemmodel/CMakeLists.txt b/examples/quick/models/abstractitemmodel/CMakeLists.txt
index 27837d3b31..3b3ab40025 100644
--- a/examples/quick/models/abstractitemmodel/CMakeLists.txt
+++ b/examples/quick/models/abstractitemmodel/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(abstractitemmodel LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/models/abstractitemmodel")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(abstractitemmodelexample WIN32 MACOSX_BUNDLE
main.cpp
@@ -20,22 +14,30 @@ qt_add_executable(abstractitemmodelexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(abstractitemmodelexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
# Resources:
qt_add_qml_module(abstractitemmodelexample
URI abstractitemmodel
- AUTO_RESOURCE_PREFIX
QML_FILES
"view.qml"
)
install(TARGETS abstractitemmodelexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET abstractitemmodelexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/models/abstractitemmodel/doc/src/abstractitemmodel-example.qdoc b/examples/quick/models/abstractitemmodel/doc/src/abstractitemmodel-example.qdoc
index 61d63ca417..6f627d050c 100644
--- a/examples/quick/models/abstractitemmodel/doc/src/abstractitemmodel-example.qdoc
+++ b/examples/quick/models/abstractitemmodel/doc/src/abstractitemmodel-example.qdoc
@@ -5,6 +5,7 @@
\example models/abstractitemmodel
\title Models and Views: AbstractItemModel Example
\brief Shows how to use a QAbstractItemModel subclass as a model in QML.
+ \examplecategory {User Interface Components}
\image qml-abstractitemmodel-example.png
*/
diff --git a/examples/quick/models/abstractitemmodel/model.h b/examples/quick/models/abstractitemmodel/model.h
index 85942127fc..d4a5e738a7 100644
--- a/examples/quick/models/abstractitemmodel/model.h
+++ b/examples/quick/models/abstractitemmodel/model.h
@@ -1,5 +1,9 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MODEL_H
+#define MODEL_H
+
#include <QAbstractListModel>
#include <QStringList>
@@ -45,4 +49,4 @@ private:
};
//![2]
-
+#endif // MODEL_H
diff --git a/examples/quick/models/objectlistmodel/CMakeLists.txt b/examples/quick/models/objectlistmodel/CMakeLists.txt
index e4d5ed35e4..8e74bb1883 100644
--- a/examples/quick/models/objectlistmodel/CMakeLists.txt
+++ b/examples/quick/models/objectlistmodel/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(objectlistmodel LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/models/objectlistmodel")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(objectlistmodelexample WIN32 MACOSX_BUNDLE
dataobject.cpp dataobject.h
@@ -20,22 +14,30 @@ qt_add_executable(objectlistmodelexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(objectlistmodelexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
# Resources:
qt_add_qml_module(objectlistmodelexample
URI objectlistmodel
- AUTO_RESOURCE_PREFIX
QML_FILES
"view.qml"
)
install(TARGETS objectlistmodelexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET objectlistmodelexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/models/objectlistmodel/doc/src/objectlistmodel-example.qdoc b/examples/quick/models/objectlistmodel/doc/src/objectlistmodel-example.qdoc
index b011b01fdd..2ceec9376d 100644
--- a/examples/quick/models/objectlistmodel/doc/src/objectlistmodel-example.qdoc
+++ b/examples/quick/models/objectlistmodel/doc/src/objectlistmodel-example.qdoc
@@ -5,6 +5,7 @@
\example models/objectlistmodel
\title Models and Views: Object ListModel Example
\brief Shows how to use a QList<QObject*> as a model in QML.
+ \examplecategory {User Interface Components}
\image qml-objectlistmodel-example.png
*/
diff --git a/examples/quick/models/stringlistmodel/CMakeLists.txt b/examples/quick/models/stringlistmodel/CMakeLists.txt
index 6846331831..bdbacace99 100644
--- a/examples/quick/models/stringlistmodel/CMakeLists.txt
+++ b/examples/quick/models/stringlistmodel/CMakeLists.txt
@@ -1,40 +1,42 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(stringlistmodel LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/models/stringlistmodel")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(stringlistmodelexample WIN32 MACOSX_BUNDLE
main.cpp
)
target_link_libraries(stringlistmodelexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
# Resources:
qt_add_qml_module(stringlistmodelexample
URI stringlistmodel
- AUTO_RESOURCE_PREFIX
QML_FILES
"view.qml"
)
install(TARGETS stringlistmodelexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET stringlistmodelexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/models/stringlistmodel/doc/src/stringlistmodel-example.qdoc b/examples/quick/models/stringlistmodel/doc/src/stringlistmodel-example.qdoc
index 57b24692ae..3b00a0e211 100644
--- a/examples/quick/models/stringlistmodel/doc/src/stringlistmodel-example.qdoc
+++ b/examples/quick/models/stringlistmodel/doc/src/stringlistmodel-example.qdoc
@@ -5,6 +5,7 @@
\example models/stringlistmodel
\title Models and Views: String ListModel Example
\brief Shows how to use a QStringList as a model in QML.
+ \examplecategory {User Interface Components}
\image qml-stringlistmodel-example.png
*/
diff --git a/examples/quick/mousearea/CMakeLists.txt b/examples/quick/mousearea/CMakeLists.txt
index 89a16690a3..b78d6c9157 100644
--- a/examples/quick/mousearea/CMakeLists.txt
+++ b/examples/quick/mousearea/CMakeLists.txt
@@ -1,41 +1,43 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(mousearea LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/mousearea")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(mouseareaexample WIN32 MACOSX_BUNDLE
main.cpp
)
target_link_libraries(mouseareaexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
# Resources:
qt_add_qml_module(mouseareaexample
URI mousearea
- AUTO_RESOURCE_PREFIX
QML_FILES
"mousearea-wheel-example.qml"
"mousearea.qml"
)
install(TARGETS mouseareaexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET mouseareaexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/mousearea/doc/src/mousearea.qdoc b/examples/quick/mousearea/doc/src/mousearea.qdoc
index 520644907b..3ff2422747 100644
--- a/examples/quick/mousearea/doc/src/mousearea.qdoc
+++ b/examples/quick/mousearea/doc/src/mousearea.qdoc
@@ -6,6 +6,7 @@
\brief This is an example of the MouseArea type in QML.
\image qml-mousearea-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e MouseArea example shows how to respond to clicks and drags with a
\l MouseArea. For more information, visit
diff --git a/examples/quick/mousearea/mousearea-wheel-example.qml b/examples/quick/mousearea/mousearea-wheel-example.qml
index 966cb046dd..145c69fcef 100644
--- a/examples/quick/mousearea/mousearea-wheel-example.qml
+++ b/examples/quick/mousearea/mousearea-wheel-example.qml
@@ -27,11 +27,11 @@ Rectangle {
MouseArea {
anchors.fill: parent
- onWheel: {
+ onWheel: (wheel) => {
if (wheel.modifiers & Qt.ControlModifier) {
- parent.scaleFactor += 0.2 * wheel.angleDelta.y / 120;
+ parent.scaleFactor += 0.2 * wheel.angleDelta.y / 120
if (parent.scaleFactor < 0)
- parent.scaleFactor = 0;
+ parent.scaleFactor = 0
}
}
}
@@ -40,9 +40,11 @@ Rectangle {
}
Text {
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
+ anchors {
+ bottom: parent.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
color: "#FFD700"
- text: "Rotate the mouse wheel pressing <Control> to resize the squares."
+ text: qsTr("Rotate the mouse wheel pressing <Control> to resize the squares.")
}
}
diff --git a/examples/quick/mousearea/mousearea.qml b/examples/quick/mousearea/mousearea.qml
index 2bfab0d6b8..b220b47a9e 100644
--- a/examples/quick/mousearea/mousearea.qml
+++ b/examples/quick/mousearea/mousearea.qml
@@ -5,16 +5,26 @@ import QtQuick
Rectangle {
id: box
- width: 320; height: 480
+ width: 320
+ height: 480
Rectangle {
id: redSquare
- width: 120; height: 120
- anchors.top: parent.top; anchors.left: parent.left; anchors.margins: 10
+ width: 120
+ height: 120
+ anchors {
+ top: parent.top
+ left: parent.left
+ margins: 10
+ }
color: "red"
opacity: redSquareMouseArea.containsPress ? 0.6 : 1.0
- Text { text: "Click"; font.pixelSize: 16; anchors.centerIn: parent }
+ Text {
+ text: qsTr("Click")
+ font.pixelSize: 16
+ anchors.centerIn: parent
+ }
MouseArea {
id: redSquareMouseArea
@@ -26,92 +36,99 @@ Rectangle {
// Value 'All.Buttons' is eqivalent to:
// 'Qt::LeftButton | Qt::RightButton | Qt::MiddleButton .... | Qt::ExtraButton24'
- onEntered: info.text = 'Entered'
- onExited: info.text = 'Exited (pressed=' + pressed + ')'
+ onEntered: info.text = qsTr('Entered')
+ onExited: info.text = qsTr('Exited (pressed=') + pressed + ')'
onPressed: (mouse) => {
if (mouse.button == Qt.LeftButton)
- buttonID = 'LeftButton'
+ buttonID = qsTr('LeftButton')
else if (mouse.button == Qt.RightButton)
- buttonID = 'RightButton'
+ buttonID = qsTr('RightButton')
else if (mouse.button == Qt.MiddleButton)
- buttonID = 'MiddleButton'
+ buttonID = qsTr('MiddleButton')
else if (mouse.button == Qt.BackButton)
- buttonID = 'BackButton'
+ buttonID = qsTr('BackButton')
else if (mouse.button == Qt.ForwardButton)
- buttonID = 'ForwardButton'
+ buttonID = qsTr('ForwardButton')
else if (mouse.button == Qt.TaskButton)
- buttonID = 'TaskButton'
+ buttonID = qsTr('TaskButton')
else if (mouse.button == Qt.ExtraButton4)
- buttonID = 'ExtraButton4'
+ buttonID = qsTr('ExtraButton4')
else if (mouse.button == Qt.ExtraButton5)
- buttonID = 'ExtraButton5'
+ buttonID = qsTr('ExtraButton5')
else if (mouse.button == Qt.ExtraButton6)
- buttonID = 'ExtraButton6'
+ buttonID = qsTr('ExtraButton6')
else if (mouse.button == Qt.ExtraButton7)
- buttonID = 'ExtraButton7'
+ buttonID = qsTr('ExtraButton7')
else if (mouse.button == Qt.ExtraButton8)
- buttonID = 'ExtraButton8'
+ buttonID = qsTr('ExtraButton8')
else if (mouse.button == Qt.ExtraButton9)
- buttonID = 'ExtraButton9'
+ buttonID = qsTr('ExtraButton9')
else if (mouse.button == Qt.ExtraButton10)
- buttonID = 'ExtraButton10'
+ buttonID = qsTr('ExtraButton10')
else if (mouse.button == Qt.ExtraButton11)
- buttonID = 'ExtraButton11'
+ buttonID = qsTr('ExtraButton11')
else if (mouse.button == Qt.ExtraButton12)
- buttonID = 'ExtraButton12'
+ buttonID = qsTr('ExtraButton12')
else if (mouse.button == Qt.ExtraButton13)
- buttonID = 'ExtraButton13'
+ buttonID = qsTr('ExtraButton13')
else if (mouse.button == Qt.ExtraButton14)
- buttonID = 'ExtraButton14'
+ buttonID = qsTr('ExtraButton14')
else if (mouse.button == Qt.ExtraButton15)
- buttonID = 'ExtraButton15'
+ buttonID = qsTr('ExtraButton15')
else if (mouse.button == Qt.ExtraButton16)
- buttonID = 'ExtraButton16'
+ buttonID = qsTr('ExtraButton16')
else if (mouse.button == Qt.ExtraButton17)
- buttonID = 'ExtraButton17'
+ buttonID = qsTr('ExtraButton17')
else if (mouse.button == Qt.ExtraButton18)
- buttonID = 'ExtraButton18'
+ buttonID = qsTr('ExtraButton18')
else if (mouse.button == Qt.ExtraButton19)
- buttonID = 'ExtraButton19'
+ buttonID = qsTr('ExtraButton19')
else if (mouse.button == Qt.ExtraButton20)
- buttonID = 'ExtraButton20'
+ buttonID = qsTr('ExtraButton20')
else if (mouse.button == Qt.ExtraButton21)
- buttonID = 'ExtraButton21'
+ buttonID = qsTr('ExtraButton21')
else if (mouse.button == Qt.ExtraButton22)
- buttonID = 'ExtraButton22'
+ buttonID = qsTr('ExtraButton22')
else if (mouse.button == Qt.ExtraButton23)
- buttonID = 'ExtraButton23'
+ buttonID = qsTr('ExtraButton23')
else if (mouse.button == Qt.ExtraButton24)
- buttonID = 'ExtraButton24'
+ buttonID = qsTr('ExtraButton24')
- info.text = 'Pressed (' + buttonID + ' shift='
- + (mouse.modifiers & Qt.ShiftModifier ? 'true' : 'false') + ')'
- var posInBox = redSquare.mapToItem(box, mouse.x, mouse.y)
- posInfo.text = + mouse.x + ',' + mouse.y + ' in square'
- + ' (' + posInBox.x + ',' + posInBox.y + ' in window)'
+ info.text = qsTr('Pressed (') + buttonID + qsTr(' shift=')
+ + (mouse.modifiers & Qt.ShiftModifier ? qsTr('true') : qsTr('false')) + ')'
+ const posInBox = redSquare.mapToItem(box, mouse.x, mouse.y)
+ posInfo.text = + mouse.x + ',' + mouse.y + qsTr(' in square')
+ + ' (' + posInBox.x + ',' + posInBox.y + qsTr(' in window)')
}
onReleased: (mouse) => {
- btn.text = 'Released (isClick=' + mouse.isClick + ' wasHeld=' + mouse.wasHeld + ')'
+ btn.text = qsTr('Released (isClick=') + mouse.isClick + qsTr(' wasHeld=') + mouse.wasHeld + ')'
posInfo.text = ''
}
//! [clicks]
- onPressAndHold: btn.text = 'Press and hold'
- onClicked: (mouse) => { btn.text = 'Clicked (wasHeld=' + mouse.wasHeld + ')' }
- onDoubleClicked: btn.text = 'Double clicked'
+ onPressAndHold: btn.text = qsTr('Press and hold')
+ onClicked: (mouse) => { btn.text = qsTr('Clicked (wasHeld=') + mouse.wasHeld + ')' }
+ onDoubleClicked: btn.text = qsTr('Double clicked')
//! [clicks]
}
}
Rectangle {
id: blueSquare
- width: 120; height: 120
- x: box.width - width - 10; y: 10 // making this item draggable, so don't use anchors
+ width: 120
+ height: 120
+ x: box.width - width - 10
+ y: 10 // making this item draggable, so don't use anchors
color: "blue"
- Text { text: "Drag"; font.pixelSize: 16; color: "white"; anchors.centerIn: parent }
+ Text {
+ text: qsTr("Drag")
+ font.pixelSize: 16
+ color: "white"
+ anchors.centerIn: parent
+ }
MouseArea {
anchors.fill: parent
@@ -128,18 +145,30 @@ Rectangle {
Text {
id: info
- anchors.bottom: btn.top; anchors.horizontalCenter: parent.horizontalCenter; anchors.margins: 20
+ anchors{
+ bottom: btn.top
+ horizontalCenter: parent.horizontalCenter
+ margins: 20
+ }
- onTextChanged: function(text) { console.log(text) }
+ onTextChanged: console.log(text)
}
Text {
id: btn
- anchors.bottom: posInfo.top; anchors.horizontalCenter: parent.horizontalCenter; anchors.margins: 20
+ anchors {
+ bottom: posInfo.top
+ horizontalCenter: parent.horizontalCenter
+ margins: 20
+ }
}
Text {
id: posInfo
- anchors.bottom: parent.bottom; anchors.horizontalCenter: parent.horizontalCenter; anchors.margins: 20
+ anchors {
+ bottom: parent.bottom
+ horizontalCenter: parent.horizontalCenter
+ margins: 20
+ }
}
}
diff --git a/examples/quick/multieffect/CMakeLists.txt b/examples/quick/multieffect/CMakeLists.txt
index 7462337905..802cb16eea 100644
--- a/examples/quick/multieffect/CMakeLists.txt
+++ b/examples/quick/multieffect/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(itemswitcher)
qt_internal_add_example(testbed)
diff --git a/examples/quick/multieffect/itemswitcher/CMakeLists.txt b/examples/quick/multieffect/itemswitcher/CMakeLists.txt
index 922b6b8023..cb2043d023 100644
--- a/examples/quick/multieffect/itemswitcher/CMakeLists.txt
+++ b/examples/quick/multieffect/itemswitcher/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(itemswitcher LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/multieffect/itemswitcher")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick QuickControls2)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../../shared" "shared")
@@ -21,11 +15,11 @@ qt_add_executable(itemswitcherexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(itemswitcherexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
- Qt::QuickControls2
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::QuickControls2
)
add_dependencies(itemswitcherexample itemswitcher_shared)
@@ -33,7 +27,6 @@ add_dependencies(itemswitcherexample itemswitcher_shared)
# Resources:
qt_add_qml_module(itemswitcherexample
URI itemswitcher
- AUTO_RESOURCE_PREFIX
QML_FILES
"qml/main.qml"
"qml/PagesView.qml"
@@ -62,9 +55,16 @@ qt_add_qml_module(itemswitcherexample
)
install(TARGETS itemswitcherexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(itemswitcherexample)
+qt_generate_deploy_qml_app_script(
+ TARGET itemswitcherexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/multieffect/itemswitcher/doc/images/qml-multieffectitemswitcher-example.jpg b/examples/quick/multieffect/itemswitcher/doc/images/qml-multieffectitemswitcher-example.jpg
new file mode 100644
index 0000000000..d1d3393e3f
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/doc/images/qml-multieffectitemswitcher-example.jpg
Binary files differ
diff --git a/examples/quick/multieffect/itemswitcher/doc/src/itemswitcher.qdoc b/examples/quick/multieffect/itemswitcher/doc/src/itemswitcher.qdoc
new file mode 100644
index 0000000000..f5588cb71e
--- /dev/null
+++ b/examples/quick/multieffect/itemswitcher/doc/src/itemswitcher.qdoc
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \title Qt Quick Examples - MultiEffect Item Switcher
+ \example multieffect/itemswitcher
+ \image qml-multieffectitemswitcher-example.jpg
+ \brief Demonstrates MultiEffect usage.
+ \ingroup qtquickexamples
+ \examplecategory {Graphics}
+
+ This example demonstrates advanced usage of the MultiEffect type, which
+ offers a simple, efficient solution for applying effects such as blur, drop
+ shadow, or colorize to an Item and its children.
+
+ \include examples-run.qdocinc
+*/
diff --git a/examples/quick/multieffect/testbed/CMakeLists.txt b/examples/quick/multieffect/testbed/CMakeLists.txt
index 71c448e1c6..fb8c566378 100644
--- a/examples/quick/multieffect/testbed/CMakeLists.txt
+++ b/examples/quick/multieffect/testbed/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(testbed LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/multieffect/testbed")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick QuickControls2)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../../shared" "shared")
@@ -21,11 +15,11 @@ qt_add_executable(testbedexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(testbedexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
- Qt::QuickControls2
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::QuickControls2
)
add_dependencies(testbedexample testbed_shared)
@@ -33,7 +27,6 @@ add_dependencies(testbedexample testbed_shared)
# Resources:
qt_add_qml_module(testbedexample
URI testbed
- AUTO_RESOURCE_PREFIX
QML_FILES
"qml/FpsItem.qml"
"qml/main.qml"
@@ -49,6 +42,8 @@ qt_add_qml_module(testbedexample
"qml/SettingsComponentSlider.qml"
"qml/SettingsComponentCheckBox.qml"
"qml/SettingsComponentColorSelector.qml"
+ "qml/CustomMultiEffect/BlurHelper.qml"
+ "qml/CustomMultiEffect/CustomMultiEffect.qml"
RESOURCES
"qml/images/pause.png"
"qml/images/play.png"
@@ -57,12 +52,23 @@ qt_add_qml_module(testbedexample
"qml/images/arrow.png"
"qml/images/Built_with_Qt.png"
"qml/images/Built_with_Qt_RGB_logo.png"
+ "qml/CustomMultiEffect/bluritems.frag.qsb"
+ "qml/CustomMultiEffect/bluritems.vert.qsb"
+ "qml/CustomMultiEffect/custommultieffect.frag.qsb"
+ "qml/CustomMultiEffect/custommultieffect.vert.qsb"
)
install(TARGETS testbedexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(testbedexample)
+qt_generate_deploy_qml_app_script(
+ TARGET testbedexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/multieffect/testbed/doc/images/qml-multieffecttestbed-example.jpg b/examples/quick/multieffect/testbed/doc/images/qml-multieffecttestbed-example.jpg
new file mode 100644
index 0000000000..de6b05da56
--- /dev/null
+++ b/examples/quick/multieffect/testbed/doc/images/qml-multieffecttestbed-example.jpg
Binary files differ
diff --git a/examples/quick/multieffect/testbed/doc/src/testbed.qdoc b/examples/quick/multieffect/testbed/doc/src/testbed.qdoc
new file mode 100644
index 0000000000..64a815b3a8
--- /dev/null
+++ b/examples/quick/multieffect/testbed/doc/src/testbed.qdoc
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \title Qt Quick Examples - MultiEffect Test Bed
+ \example multieffect/testbed
+ \image qml-multieffecttestbed-example.jpg
+ \brief Demonstrates MultiEffect usage.
+ \ingroup qtquickexamples
+ \examplecategory {Graphics}
+
+ This example demonstrates the MultiEffect type, which offers a simple,
+ efficient solution for applying effects such as blur, drop shadow, or
+ colorize to an Item and its children.
+
+ \include examples-run.qdocinc
+*/
diff --git a/examples/quick/multieffect/testbed/qml.qrc b/examples/quick/multieffect/testbed/qml.qrc
index ab21cc8b0f..4c23cff873 100644
--- a/examples/quick/multieffect/testbed/qml.qrc
+++ b/examples/quick/multieffect/testbed/qml.qrc
@@ -21,5 +21,11 @@
<file>qml/images/arrow.png</file>
<file>qml/images/Built_with_Qt.png</file>
<file>qml/images/Built_with_Qt_RGB_logo.png</file>
+ <file>qml/CustomMultiEffect/BlurHelper.qml</file>
+ <file>qml/CustomMultiEffect/bluritems.frag.qsb</file>
+ <file>qml/CustomMultiEffect/bluritems.vert.qsb</file>
+ <file>qml/CustomMultiEffect/custommultieffect.frag.qsb</file>
+ <file>qml/CustomMultiEffect/CustomMultiEffect.qml</file>
+ <file>qml/CustomMultiEffect/custommultieffect.vert.qsb</file>
</qresource>
</RCC>
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml
new file mode 100644
index 0000000000..7a9390791b
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml
@@ -0,0 +1,66 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: rootItem
+ property alias blurSrc1: blurredItemSource1
+ property alias blurSrc2: blurredItemSource2
+ property alias blurSrc3: blurredItemSource3
+ property alias blurSrc4: blurredItemSource4
+ property alias blurSrc5: blurredItemSource5
+
+ component BlurItem : ShaderEffect {
+ property vector2d offset: Qt.vector2d((1.0 + rootItem.blurMultiplier) / width,
+ (1.0 + rootItem.blurMultiplier) / height)
+ visible: false
+ layer.enabled: true
+ layer.smooth: true
+ vertexShader: "bluritems.vert.qsb"
+ fragmentShader: "bluritems.frag.qsb"
+ }
+
+ QtObject {
+ id: priv
+ property bool useBlurItem1: true
+ property bool useBlurItem2: rootItem.blurMax > 2
+ property bool useBlurItem3: rootItem.blurMax > 8
+ property bool useBlurItem4: rootItem.blurMax > 16
+ property bool useBlurItem5: rootItem.blurMax > 32
+ }
+
+ BlurItem {
+ id: blurredItemSource1
+ property Item src: priv.useBlurItem1 ? source : null
+ // Size of the first blurred item is by default half of the source.
+ // Increase for quality and decrease for performance & more blur.
+ readonly property int blurItemSize: 8
+ width: src ? Math.ceil(src.width / 16) * blurItemSize : 0
+ height: src ? Math.ceil(src.height / 16) * blurItemSize : 0
+ }
+ BlurItem {
+ id: blurredItemSource2
+ property Item src: priv.useBlurItem2 ? blurredItemSource1 : null
+ width: blurredItemSource1.width * 0.5
+ height: blurredItemSource1.height * 0.5
+ }
+ BlurItem {
+ id: blurredItemSource3
+ property Item src: priv.useBlurItem3 ? blurredItemSource2 : null
+ width: blurredItemSource2.width * 0.5
+ height: blurredItemSource2.height * 0.5
+ }
+ BlurItem {
+ id: blurredItemSource4
+ property Item src: priv.useBlurItem4 ? blurredItemSource3 : null
+ width: blurredItemSource3.width * 0.5
+ height: blurredItemSource3.height * 0.5
+ }
+ BlurItem {
+ id: blurredItemSource5
+ property Item src: priv.useBlurItem5 ? blurredItemSource4 : null
+ width: blurredItemSource4.width * 0.5
+ height: blurredItemSource4.height * 0.5
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep
new file mode 100644
index 0000000000..2600bc505f
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep
@@ -0,0 +1,383 @@
+{
+ "QEP": {
+ "QQEM": "0.43",
+ "connections": [
+ {
+ "fromId": 2,
+ "toId": 1
+ },
+ {
+ "fromId": 0,
+ "toId": 3
+ },
+ {
+ "fromId": 3,
+ "toId": 2
+ }
+ ],
+ "exportDirectory": ".",
+ "exportFlags": 11,
+ "exportName": "CustomMultiEffect",
+ "nodes": [
+ {
+ "fragmentCode": [
+ "void main() {",
+ " fragColor = texture(iSource, texCoord);",
+ " @nodes",
+ " fragColor = fragColor * qt_Opacity;",
+ "}"
+ ],
+ "name": "Main",
+ "nodeId": 0,
+ "type": 0,
+ "vertexCode": [
+ "void main() {",
+ " texCoord = qt_MultiTexCoord0;",
+ " fragCoord = qt_Vertex.xy;",
+ " vec2 vertCoord = qt_Vertex.xy;",
+ " @nodes",
+ " gl_Position = qt_Matrix * vec4(vertCoord, 0.0, 1.0);",
+ "}"
+ ],
+ "x": 140,
+ "y": 35.25
+ },
+ {
+ "name": "Output",
+ "nodeId": 1,
+ "type": 1,
+ "x": 140,
+ "y": 589.75
+ },
+ {
+ "description": "This node matches to features and API of the Qt Quick MultiEffect element. This makes it easy to build customized MultiEffects.",
+ "fragmentCode": [
+ "{",
+ " vec4 color = fragColor;",
+ "#if (BLUR_ENABLED)",
+ " // blur",
+ " vec4 blurredColor = texture(iSource, texCoord) * blurWeight1[0];",
+ " blurredColor += texture(iSourceBlur1, texCoord) * blurWeight1[1];",
+ "#if (BLUR_HELPER_MAX_LEVEL > 2)",
+ " blurredColor += texture(iSourceBlur2, texCoord) * blurWeight1[2];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 8)",
+ " blurredColor += texture(iSourceBlur3, texCoord) * blurWeight1[3];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 16)",
+ " blurredColor += texture(iSourceBlur4, texCoord) * blurWeight2[0];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 32)",
+ " blurredColor += texture(iSourceBlur5, texCoord) * blurWeight2[1];",
+ "#endif",
+ " color = blurredColor;",
+ "#endif",
+ " // contrast, brightness, saturation and colorization",
+ " color.rgb = (color.rgb - 0.5 * color.a) * (1.0 + contrast) + 0.5 * color.a;",
+ " color.rgb += brightness * color.a;",
+ " float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));",
+ " float colorizationAlpha = colorization * colorizationColor.a;",
+ " color.rgb = mix(color.rgb, gray * colorizationColor.rgb, colorizationAlpha);",
+ " color.rgb = mix(vec3(gray), color.rgb, 1.0 + saturation);",
+ "#if (SHADOW_ENABLED)",
+ " // shadow",
+ " float shadow = texture(iSource, shadowTexCoord).a * shadowBlurWeight1[0];",
+ " shadow += texture(iSourceBlur1, shadowTexCoord).a * shadowBlurWeight1[1];",
+ "#if (BLUR_HELPER_MAX_LEVEL > 2)",
+ " shadow += texture(iSourceBlur2, shadowTexCoord).a * shadowBlurWeight1[2];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 8)",
+ " shadow += texture(iSourceBlur3, shadowTexCoord).a * shadowBlurWeight1[3];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 16)",
+ " shadow += texture(iSourceBlur4, shadowTexCoord).a * shadowBlurWeight2[0];",
+ "#endif",
+ "#if (BLUR_HELPER_MAX_LEVEL > 32)",
+ " shadow += texture(iSourceBlur5, shadowTexCoord).a * shadowBlurWeight2[1];",
+ "#endif",
+ " shadow *= (shadowColor.a * shadowOpacity);",
+ " float saa = (1.0 - color.a) * (1.0 - shadow);",
+ " color.rgb = mix(shadowColor.rgb * shadow, color.rgb, color.a + saa);",
+ " color.a = 1.0 - saa;",
+ "#endif",
+ "#if (MASK_ENABLED)",
+ " // mask",
+ " vec4 maskTexture = texture(maskSource, texCoord);",
+ " float alphaMask = maskTexture.a;",
+ " const float mSLow = 1.0 + maskSpreadAtMin;",
+ " const float mSUp = 1.0 + maskSpreadAtMax;",
+ " float m1 = smoothstep(maskThresholdMin * mSLow - (mSLow - 0.999), maskThresholdMin * mSLow, alphaMask);",
+ " float m2 = smoothstep((1.0 - maskThresholdMax) * mSUp - (mSUp - 0.999), (1.0 - maskThresholdMax) * mSUp, (1.0 - alphaMask));",
+ " float mm = m1 * m2;",
+ " color *= (1.0 - float(maskInverted)) * mm + float(maskInverted) * (1.0 - mm);",
+ "#endif",
+ " fragColor = color;",
+ "}"
+ ],
+ "name": "MultiEffect",
+ "nodeId": 2,
+ "properties": [
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "-1",
+ "name": "contrast",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "-1",
+ "name": "brightness",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "-1",
+ "name": "saturation",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "colorization",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "1, 0, 0, 1",
+ "name": "colorizationColor",
+ "type": "color",
+ "value": "1, 0, 0, 1"
+ },
+ {
+ "defaultValue": "1",
+ "description": "Enables the blur effect.",
+ "name": "BLUR_ENABLED",
+ "type": "define",
+ "value": "1"
+ },
+ {
+ "defaultValue": "32",
+ "maxValue": "64",
+ "minValue": "0",
+ "name": "blurMax",
+ "type": "int",
+ "value": "32"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "blur",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "description": "Enables the shadow effect.",
+ "name": "SHADOW_ENABLED",
+ "type": "define",
+ "value": "1"
+ },
+ {
+ "defaultValue": "1",
+ "description": "This property defines how much blur (radius) is applied to the shadow.\n\nThe value ranges from 0.0 (no blur) to 1.0 (full blur). By default, the property is set to \\c 0.0 (no change). The amount of full blur is affected by blurHelperBlurMultiplier.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "shadowBlur",
+ "type": "float",
+ "value": "1"
+ },
+ {
+ "defaultValue": "1",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "shadowOpacity",
+ "type": "float",
+ "value": "1"
+ },
+ {
+ "defaultValue": "0, 0, 0, 1",
+ "name": "shadowColor",
+ "type": "color",
+ "value": "0, 0, 0, 1"
+ },
+ {
+ "defaultValue": "1",
+ "maxValue": "1.2",
+ "minValue": "0.8",
+ "name": "shadowScale",
+ "type": "float",
+ "value": "1"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "30",
+ "minValue": "-30",
+ "name": "shadowHorizontalOffset",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "maxValue": "30",
+ "minValue": "-30",
+ "name": "shadowVerticalOffset",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "1",
+ "description": "Enables the mask effect.",
+ "name": "MASK_ENABLED",
+ "type": "define",
+ "value": "1"
+ },
+ {
+ "defaultValue": "",
+ "description": "Source item for the mask effect. By default the alpha channel of the source item is used for masking but this can be easily adjusted in the shader.",
+ "name": "maskSource",
+ "type": "image",
+ "value": ""
+ },
+ {
+ "defaultValue": "0",
+ "description": "This property defines a lower threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.\n\nThe value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 0.0.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "maskThresholdMin",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "0",
+ "description": "This property defines the smoothness of the mask edges near the maskThresholdMin. Setting higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.\n\nThe value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "maskSpreadAtMin",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "1",
+ "description": "This property defines an upper threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.\n\nThe value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 1.0.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "maskThresholdMax",
+ "type": "float",
+ "value": "1"
+ },
+ {
+ "defaultValue": "0",
+ "description": "This property defines the smoothness of the mask edges near the maskThresholdMax. Using higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.\n\nThe value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.",
+ "maxValue": "1",
+ "minValue": "0",
+ "name": "maskSpreadAtMax",
+ "type": "float",
+ "value": "0"
+ },
+ {
+ "defaultValue": "false",
+ "description": "This property switches the mask to the opposite side; instead of masking away the content outside maskThresholdMin and maskThresholdMax, content between them will get masked away.\n\nBy default, the property is set to false.",
+ "name": "maskInverted",
+ "type": "bool",
+ "value": "false"
+ }
+ ],
+ "type": 2,
+ "vertexCode": [
+ "out vec4 blurWeight1;",
+ "out vec2 blurWeight2;",
+ "out vec2 shadowTexCoord;",
+ "out vec4 shadowBlurWeight1;",
+ "out vec2 shadowBlurWeight2;",
+ "",
+ "float blurWeight(float v) {",
+ " return max(0.0, min(1.0, 1.0 - v * 2.0));",
+ "}",
+ "",
+ "@main",
+ "{",
+ "#if (BLUR_ENABLED)",
+ " float blurLod = sqrt(blur * (blurMax / 64.0)) * 1.2 - 0.2;",
+ " float bw1 = blurWeight(abs(blurLod - 0.1));",
+ " float bw2 = blurWeight(abs(blurLod - 0.3));",
+ " float bw3 = blurWeight(abs(blurLod - 0.5));",
+ " float bw4 = blurWeight(abs(blurLod - 0.7));",
+ " float bw5 = blurWeight(abs(blurLod - 0.9));",
+ " float bw6 = blurWeight(abs(blurLod - 1.1));",
+ "",
+ " float bsum = bw1 + bw2 + bw3 + bw4 + bw5 + bw6;",
+ " blurWeight1 = vec4(bw1 / bsum, bw2 / bsum, bw3 / bsum, bw4 / bsum);",
+ " blurWeight2 = vec2(bw5 / bsum, bw6 / bsum);",
+ "#endif",
+ "#if (SHADOW_ENABLED)",
+ " float shadowBlurLod = sqrt(shadowBlur * (blurMax / 64.0)) * 1.2 - 0.2;",
+ " float sbw1 = blurWeight(abs(shadowBlurLod - 0.1));",
+ " float sbw2 = blurWeight(abs(shadowBlurLod - 0.3));",
+ " float sbw3 = blurWeight(abs(shadowBlurLod - 0.5));",
+ " float sbw4 = blurWeight(abs(shadowBlurLod - 0.7));",
+ " float sbw5 = blurWeight(abs(shadowBlurLod - 0.9));",
+ " float sbw6 = blurWeight(abs(shadowBlurLod - 1.1));",
+ "",
+ " float sbsum = sbw1 + sbw2 + sbw3 + sbw4 + sbw5 + sbw6;",
+ " shadowBlurWeight1 = vec4(sbw1 / sbsum, sbw2 / sbsum, sbw3 / sbsum, sbw4 / sbsum);",
+ " shadowBlurWeight2 = vec2(sbw5 / sbsum, sbw6 / sbsum);",
+ "",
+ " vec2 shadowOffset = vec2(shadowHorizontalOffset / iResolution.x, shadowVerticalOffset / iResolution.y);",
+ " float invertedScale = 1.0 / shadowScale;",
+ " float s = (1.0 - invertedScale) * 0.5;",
+ " vec2 shadowCenterOffset = vec2(s);",
+ " shadowTexCoord = qt_MultiTexCoord0 - shadowOffset;",
+ " shadowTexCoord = (shadowTexCoord * invertedScale) + shadowCenterOffset;",
+ "#endif",
+ "}"
+ ],
+ "x": 105,
+ "y": 327.5
+ },
+ {
+ "description": "This node is required e.g. for FastBlur and DropShadow. It generates blurred iSourceBlur[1..6] samplers to be available for shaders.",
+ "fragmentCode": [
+ "@blursources"
+ ],
+ "name": "BlurHelper",
+ "nodeId": 3,
+ "properties": [
+ {
+ "defaultValue": "64",
+ "description": "This property defines the maximum pixel radius that blur with value 1.0 will reach.\n\nMeaningful range of this value is from 2 (subtle blur) to 64 (high blur). By default, the property is set to 32. For the most optimal performance, select as small value as you need.\n\nNote: This affects to both blur and shadow effects.",
+ "name": "BLUR_HELPER_MAX_LEVEL",
+ "type": "define",
+ "value": "64"
+ },
+ {
+ "defaultValue": "0",
+ "description": "This property defines a multiplier for extending the blur radius.\n\nThe value ranges from 0.0 (not multiplied) to inf. By default, the property is set to 0.0. Incresing the multiplier extends the blur radius, but decreases the blur quality. This is more performant option for a bigger blur radius than BLUR_HELPER_MAX_LEVEL as it doesn't increase the amount of texture lookups.\n\nNote: This affects to both blur and shadow effects.",
+ "maxValue": "2",
+ "minValue": "0",
+ "name": "blurMultiplier",
+ "type": "float",
+ "value": "0"
+ }
+ ],
+ "type": 2,
+ "x": 105,
+ "y": 188.875
+ }
+ ],
+ "settings": {
+ "headings": [
+ "// Copyright (C) 2023 The Qt Company Ltd.",
+ "// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause"
+ ]
+ },
+ "version": 1
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml
new file mode 100644
index 0000000000..d0fdf9c1b9
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml
@@ -0,0 +1,97 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// Created with Qt Quick Effect Maker (version 0.43), Thu Feb 16 13:48:55 2023
+
+import QtQuick
+
+Item {
+ id: rootItem
+
+ // This is the main source for the effect
+ property Item source: null
+
+ property real contrast: 0
+ property real brightness: 0
+ property real saturation: 0
+ property real colorization: 0
+ property color colorizationColor: Qt.rgba(1, 0, 0, 1)
+ property int blurMax: 32
+ property real blur: 0
+ // This property defines how much blur (radius) is applied to the shadow.
+ //
+ // The value ranges from 0.0 (no blur) to 1.0 (full blur). By default, the property is set to \c 0.0 (no change). The amount of full blur is affected by blurHelperBlurMultiplier.
+ property real shadowBlur: 1
+ property real shadowOpacity: 1
+ property color shadowColor: Qt.rgba(0, 0, 0, 1)
+ property real shadowScale: 1
+ property real shadowHorizontalOffset: 0
+ property real shadowVerticalOffset: 0
+ // Source item for the mask effect. By default the alpha channel of the source item is used for masking but this can be easily adjusted in the shader.
+ property var maskSource: null
+ // This property defines a lower threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.
+ //
+ // The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 0.0.
+ property real maskThresholdMin: 0
+ // This property defines the smoothness of the mask edges near the maskThresholdMin. Setting higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.
+ //
+ // The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.
+ property real maskSpreadAtMin: 0
+ // This property defines an upper threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.
+ //
+ // The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 1.0.
+ property real maskThresholdMax: 1
+ // This property defines the smoothness of the mask edges near the maskThresholdMax. Using higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.
+ //
+ // The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.
+ property real maskSpreadAtMax: 0
+ // This property switches the mask to the opposite side; instead of masking away the content outside maskThresholdMin and maskThresholdMax, content between them will get masked away.
+ //
+ // By default, the property is set to false.
+ property bool maskInverted: false
+ // This property defines a multiplier for extending the blur radius.
+ //
+ // The value ranges from 0.0 (not multiplied) to inf. By default, the property is set to 0.0. Incresing the multiplier extends the blur radius, but decreases the blur quality. This is more performant option for a bigger blur radius than BLUR_HELPER_MAX_LEVEL as it doesn't increase the amount of texture lookups.
+ //
+ // Note: This affects to both blur and shadow effects.
+ property real blurMultiplier: 0
+
+ BlurHelper {
+ id: blurHelper
+ anchors.fill: parent
+ property int blurMax: 64
+ property real blurMultiplier: rootItem.blurMultiplier
+ }
+ ShaderEffect {
+ readonly property alias iSource: rootItem.source
+ readonly property vector3d iResolution: Qt.vector3d(width, height, 1.0)
+ readonly property alias iSourceBlur1: blurHelper.blurSrc1
+ readonly property alias iSourceBlur2: blurHelper.blurSrc2
+ readonly property alias iSourceBlur3: blurHelper.blurSrc3
+ readonly property alias iSourceBlur4: blurHelper.blurSrc4
+ readonly property alias iSourceBlur5: blurHelper.blurSrc5
+ readonly property alias contrast: rootItem.contrast
+ readonly property alias brightness: rootItem.brightness
+ readonly property alias saturation: rootItem.saturation
+ readonly property alias colorization: rootItem.colorization
+ readonly property alias colorizationColor: rootItem.colorizationColor
+ readonly property alias blurMax: rootItem.blurMax
+ readonly property alias blur: rootItem.blur
+ readonly property alias shadowBlur: rootItem.shadowBlur
+ readonly property alias shadowOpacity: rootItem.shadowOpacity
+ readonly property alias shadowColor: rootItem.shadowColor
+ readonly property alias shadowScale: rootItem.shadowScale
+ readonly property alias shadowHorizontalOffset: rootItem.shadowHorizontalOffset
+ readonly property alias shadowVerticalOffset: rootItem.shadowVerticalOffset
+ readonly property alias maskSource: rootItem.maskSource
+ readonly property alias maskThresholdMin: rootItem.maskThresholdMin
+ readonly property alias maskSpreadAtMin: rootItem.maskSpreadAtMin
+ readonly property alias maskThresholdMax: rootItem.maskThresholdMax
+ readonly property alias maskSpreadAtMax: rootItem.maskSpreadAtMax
+ readonly property alias maskInverted: rootItem.maskInverted
+ readonly property alias blurMultiplier: rootItem.blurMultiplier
+
+ vertexShader: 'custommultieffect.vert.qsb'
+ fragmentShader: 'custommultieffect.frag.qsb'
+ anchors.fill: parent
+ }
+}
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb
new file mode 100644
index 0000000000..054745d277
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb
new file mode 100644
index 0000000000..6aa0eb1d4e
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb
new file mode 100644
index 0000000000..370cf501dc
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb
new file mode 100644
index 0000000000..c522c25aee
--- /dev/null
+++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb
Binary files differ
diff --git a/examples/quick/multieffect/testbed/qml/Settings.qml b/examples/quick/multieffect/testbed/qml/Settings.qml
index 6139bdc6f4..05947c2ab4 100644
--- a/examples/quick/multieffect/testbed/qml/Settings.qml
+++ b/examples/quick/multieffect/testbed/qml/Settings.qml
@@ -18,6 +18,7 @@ QtObject {
property bool animateMovement: true
property bool showShader: false
property bool showItemSize: false
+ property bool showCustomMultiEffect: false
property bool autoPaddingEnabled: true
property rect paddingRect: Qt.rect(0, 0, 0, 0)
diff --git a/examples/quick/multieffect/testbed/qml/SettingsView.qml b/examples/quick/multieffect/testbed/qml/SettingsView.qml
index 9b753298dd..789f7685c2 100644
--- a/examples/quick/multieffect/testbed/qml/SettingsView.qml
+++ b/examples/quick/multieffect/testbed/qml/SettingsView.qml
@@ -125,6 +125,13 @@ Item {
settings.autoPaddingEnabled = checked;
}
}
+ SettingsComponentCheckBox {
+ text: "Show Custom MultiEffect"
+ checked: settings.showCustomMultiEffect
+ onToggled: {
+ settings.showCustomMultiEffect = checked;
+ }
+ }
Item {
width: 1
height: 20 * dp
diff --git a/examples/quick/multieffect/testbed/qml/WarningsView.qml b/examples/quick/multieffect/testbed/qml/WarningsView.qml
index 880a75b885..c25ad5fccd 100644
--- a/examples/quick/multieffect/testbed/qml/WarningsView.qml
+++ b/examples/quick/multieffect/testbed/qml/WarningsView.qml
@@ -28,4 +28,11 @@ Item {
anchors.leftMargin: 16
text: qsTr("Item resized!")
}
+ WarningsItem {
+ id: customEffectWarning
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ text: qsTr("<b>Note:</b> Custom MultiEffect doesn't support all the MultiEffect features, like paddings and disabling effects.")
+ opacity: settings.showCustomMultiEffect
+ }
}
diff --git a/examples/quick/multieffect/testbed/qml/main.qml b/examples/quick/multieffect/testbed/qml/main.qml
index 6d34a47bfa..8fc761169e 100644
--- a/examples/quick/multieffect/testbed/qml/main.qml
+++ b/examples/quick/multieffect/testbed/qml/main.qml
@@ -4,6 +4,7 @@
import QtQuick
import QtQuick.Window
import QtQuick.Effects
+import "CustomMultiEffect"
Rectangle {
id: mainWindow
@@ -77,9 +78,9 @@ Rectangle {
MultiEffect {
id: quickMultiEffect
anchors.fill: testSourceItem
+ visible: !settings.showCustomMultiEffect
source: testSourceItem
maskSource: testMaskItem
-
autoPaddingEnabled: settings.autoPaddingEnabled
paddingRect: settings.paddingRect
brightness: settings.brightnessEnabled ? settings.brightness : 0
@@ -106,12 +107,42 @@ Rectangle {
maskSpreadAtMax: settings.maskSpreadAtMax
onItemSizeChanged: {
- warningsView.showSizeWarning();
+ if (visible)
+ warningsView.showSizeWarning();
}
onShaderChanged: {
- warningsView.showShaderWarning();
+ if (visible)
+ warningsView.showShaderWarning();
}
}
+
+ // For comparison, custom effect created with QQEM and the MultiEffect node.
+ CustomMultiEffect {
+ id: customMultiEffect
+ anchors.fill: testSourceItem
+ visible: settings.showCustomMultiEffect
+ source: testSourceItem
+ maskSource: testMaskItem
+ brightness: settings.brightnessEnabled ? settings.brightness : 0
+ contrast: settings.contrastEnabled ? settings.contrast : 0
+ saturation: settings.saturationEnabled ? settings.saturation : 0
+ colorizationColor: settings.colorizationColor
+ colorization: settings.colorizationEnabled ? settings.colorization : 0
+ blur: settings.blur
+ blurMax: settings.blurMax
+ blurMultiplier: settings.blurMultiplier
+ shadowOpacity: settings.shadowOpacity
+ shadowBlur: settings.shadowBlur
+ shadowHorizontalOffset: settings.shadowHorizontalOffset
+ shadowVerticalOffset: settings.shadowVerticalOffset
+ shadowColor: settings.shadowColor
+ shadowScale: settings.shadowScale
+ maskInverted: settings.maskInverted
+ maskThresholdMin: settings.maskThresholdMin
+ maskSpreadAtMin: settings.maskSpreadAtMin
+ maskThresholdMax: settings.maskThresholdMax
+ maskSpreadAtMax: settings.maskSpreadAtMax
+ }
}
SettingsView {
diff --git a/examples/quick/particles/CMakeLists.txt b/examples/quick/particles/CMakeLists.txt
index d34300510f..ce9658b74a 100644
--- a/examples/quick/particles/CMakeLists.txt
+++ b/examples/quick/particles/CMakeLists.txt
@@ -1,8 +1,7 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(affectors)
qt_internal_add_example(emitters)
qt_internal_add_example(imageparticle)
-qt_internal_add_example(itemparticle)
qt_internal_add_example(system)
diff --git a/examples/quick/particles/affectors/CMakeLists.txt b/examples/quick/particles/affectors/CMakeLists.txt
index 5f710fbdd8..dc29e1787b 100644
--- a/examples/quick/particles/affectors/CMakeLists.txt
+++ b/examples/quick/particles/affectors/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(affectors LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/particles/affectors")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../../shared" "shared")
@@ -21,10 +15,10 @@ qt_add_executable(affectorsexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(affectorsexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(affectorsexample affectors_shared)
@@ -32,7 +26,6 @@ add_dependencies(affectorsexample affectors_shared)
# Resources:
qt_add_qml_module(affectorsexample
URI affectors
- AUTO_RESOURCE_PREFIX
QML_FILES
"affectors.qml"
"GreyButton.qml"
@@ -65,9 +58,16 @@ qt_add_qml_module(affectorsexample
)
install(TARGETS affectorsexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(affectorsexample)
+qt_generate_deploy_qml_app_script(
+ TARGET affectorsexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/particles/affectors/GreyButton.qml b/examples/quick/particles/affectors/GreyButton.qml
index 0d29246c2b..412a6dfa60 100644
--- a/examples/quick/particles/affectors/GreyButton.qml
+++ b/examples/quick/particles/affectors/GreyButton.qml
@@ -6,7 +6,7 @@ import QtQuick
Item {
id: container
- property string text: "Button"
+ required property string text
property string subText: ""
signal clicked
diff --git a/examples/quick/particles/affectors/attractor.qml b/examples/quick/particles/affectors/attractor.qml
index a0793b5fed..c5b95ee2a0 100644
--- a/examples/quick/particles/affectors/attractor.qml
+++ b/examples/quick/particles/affectors/attractor.qml
@@ -69,7 +69,6 @@ Rectangle {
groups: ["engine"]
source: "qrc:///particleresources/fuzzydot.png"
- color: "orange"
SequentialAnimation on color {
loops: Animation.Infinite
ColorAnimation {
diff --git a/examples/quick/particles/affectors/doc/src/affectors.qdoc b/examples/quick/particles/affectors/doc/src/affectors.qdoc
index 5d98029b44..73f99aabed 100644
--- a/examples/quick/particles/affectors/doc/src/affectors.qdoc
+++ b/examples/quick/particles/affectors/doc/src/affectors.qdoc
@@ -4,6 +4,7 @@
/*!
\title Qt Quick Particles Examples - Affectors
\example particles/affectors
+ \examplecategory {Graphics}
\brief This is a collection of examples using Affectors in the QML particle system.
\image qml-affectors-example.png
diff --git a/examples/quick/particles/affectors/spritegoal.qml b/examples/quick/particles/affectors/spritegoal.qml
index 8d157be7d9..98d793d304 100644
--- a/examples/quick/particles/affectors/spritegoal.qml
+++ b/examples/quick/particles/affectors/spritegoal.qml
@@ -114,7 +114,7 @@ Item {
id: circle
x: root.width / 1.2
y: root.height / 1.7
- property real percent: 0
+ property real percent
SequentialAnimation on percent {
id: circleAnim1
@@ -135,7 +135,6 @@ Item {
groups: ["exhaust"]
source: "qrc:///particleresources/fuzzydot.png"
- color: "orange"
SequentialAnimation on color {
loops: Animation.Infinite
ColorAnimation {
diff --git a/examples/quick/particles/affectors/turbulence.qml b/examples/quick/particles/affectors/turbulence.qml
index dac3a5c46d..049e1a584d 100644
--- a/examples/quick/particles/affectors/turbulence.qml
+++ b/examples/quick/particles/affectors/turbulence.qml
@@ -31,7 +31,6 @@ Rectangle {
width: parent.width
x: parent. width / 4
anchors.fill: parent
- strength: 32
NumberAnimation on strength{from: 16; to: 64; easing.type: Easing.InOutBounce; duration: 1800; loops: -1}
}
//! [0]
diff --git a/examples/quick/particles/affectors/wander.qml b/examples/quick/particles/affectors/wander.qml
index e93bfe200c..c4c2914d49 100644
--- a/examples/quick/particles/affectors/wander.qml
+++ b/examples/quick/particles/affectors/wander.qml
@@ -45,15 +45,15 @@ Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
spacing: 4
GreyButton {
- text:"dx/dt"
+ text: qsTr("dx/dt")
onClicked: wanderer.affectedParameter = Wander.Position;
}
GreyButton {
- text:"dv/dt"
+ text: qsTr("dv/dt")
onClicked: wanderer.affectedParameter = Wander.Velocity;
}
GreyButton {
- text:"da/dt"
+ text: qsTr("da/dt")
onClicked: wanderer.affectedParameter = Wander.Acceleration;
}
}
diff --git a/examples/quick/particles/emitters/CMakeLists.txt b/examples/quick/particles/emitters/CMakeLists.txt
index 8bb31beba6..772f45fa0a 100644
--- a/examples/quick/particles/emitters/CMakeLists.txt
+++ b/examples/quick/particles/emitters/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(emitters LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/particles/emitters")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../../shared" "shared")
@@ -21,10 +15,10 @@ qt_add_executable(emittersexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(emittersexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(emittersexample emitters_shared)
@@ -32,7 +26,6 @@ add_dependencies(emittersexample emitters_shared)
# Resources:
qt_add_qml_module(emittersexample
URI emitters
- AUTO_RESOURCE_PREFIX
QML_FILES
"burstandpulse.qml"
"customemitter.qml"
@@ -48,9 +41,16 @@ qt_add_qml_module(emittersexample
)
install(TARGETS emittersexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(emittersexample)
+qt_generate_deploy_qml_app_script(
+ TARGET emittersexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/particles/emitters/customemitter.qml b/examples/quick/particles/emitters/customemitter.qml
index 5aba7d1b9f..ed23d68720 100644
--- a/examples/quick/particles/emitters/customemitter.qml
+++ b/examples/quick/particles/emitters/customemitter.qml
@@ -16,7 +16,7 @@ ParticleSystem {
}
property real petalLength: 180
- property real petalRotation: 0
+ property real petalRotation
NumberAnimation on petalRotation {
from: 0;
to: 360;
@@ -34,9 +34,9 @@ ParticleSystem {
//! [0]
onEmitParticles: (particles) => {
for (var i=0; i<particles.length; i++) {
- var particle = particles[i];
+ let particle = particles[i];
particle.startSize = Math.max(02,Math.min(492,Math.tan(particle.t/2)*24));
- var theta = Math.floor(Math.random() * 6.0);
+ let theta = Math.floor(Math.random() * 6.0);
particle.red = theta == 0 || theta == 1 || theta == 2 ? 0.2 : 1;
particle.green = theta == 2 || theta == 3 || theta == 4 ? 0.2 : 1;
particle.blue = theta == 4 || theta == 5 || theta == 0 ? 0.2 : 1;
diff --git a/examples/quick/particles/emitters/doc/src/emitters.qdoc b/examples/quick/particles/emitters/doc/src/emitters.qdoc
index db06577422..51a1ac308a 100644
--- a/examples/quick/particles/emitters/doc/src/emitters.qdoc
+++ b/examples/quick/particles/emitters/doc/src/emitters.qdoc
@@ -3,6 +3,7 @@
/*!
\title Qt Quick Particles Examples - Emitters
\example particles/emitters
+ \examplecategory {Graphics}
\brief This is a collection of examples using Emitters in the QML particle system.
\image qml-emitters-example.png
diff --git a/examples/quick/particles/emitters/velocityfrommotion.qml b/examples/quick/particles/emitters/velocityfrommotion.qml
index df8070e768..aa65ac7d9e 100644
--- a/examples/quick/particles/emitters/velocityfrommotion.qml
+++ b/examples/quick/particles/emitters/velocityfrommotion.qml
@@ -25,7 +25,6 @@ Rectangle {
ImageParticle {
system: sys1
source: "qrc:///particleresources/glowdot.png"
- color: "cyan"
alpha: 0
SequentialAnimation on color {
loops: Animation.Infinite
@@ -73,7 +72,6 @@ Rectangle {
//! [0]
ParticleSystem { id: sys2 }
ImageParticle {
- color: "cyan"
system: sys2
alpha: 0
SequentialAnimation on color {
@@ -114,7 +112,6 @@ Rectangle {
ImageParticle {
source: "qrc:///particleresources/glowdot.png"
system: sys3
- color: "orange"
alpha: 0
SequentialAnimation on color {
loops: Animation.Infinite
@@ -155,7 +152,6 @@ Rectangle {
ImageParticle {
system: sys4
source: "qrc:///particleresources/star.png"
- color: "green"
alpha: 0
SequentialAnimation on color {
loops: Animation.Infinite
@@ -199,12 +195,12 @@ Rectangle {
Item {
id: circle
//anchors.fill: parent
- property real radius: 0
+ property real radius
property real dx: root.width / 2
property real dy: root.height / 2
property real cx: radius * Math.sin(percent*6.283185307179) + dx
property real cy: radius * Math.cos(percent*6.283185307179) + dy
- property real percent: 0
+ property real percent
SequentialAnimation on percent {
loops: Animation.Infinite
@@ -247,7 +243,7 @@ Rectangle {
property real dy: root.height / 2
property real cx: radius * Math.sin(percent*6.283185307179) + dx
property real cy: radius * Math.cos(percent*6.283185307179) + dy
- property real percent: 0
+ property real percent
SequentialAnimation on percent {
loops: Animation.Infinite
@@ -263,7 +259,7 @@ Rectangle {
property real dy: circle3.cy
property real cx: radius * Math.sin(percent*6.283185307179) + dx
property real cy: radius * Math.cos(percent*6.283185307179) + dy
- property real percent: 0
+ property real percent
SequentialAnimation on percent {
loops: Animation.Infinite
diff --git a/examples/quick/particles/imageparticle/CMakeLists.txt b/examples/quick/particles/imageparticle/CMakeLists.txt
index 1bc4acd678..c587d9bcc3 100644
--- a/examples/quick/particles/imageparticle/CMakeLists.txt
+++ b/examples/quick/particles/imageparticle/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(imageparticle LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/particles/imageparticle")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../../shared" "shared")
@@ -21,10 +15,10 @@ qt_add_executable(imageparticleexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(imageparticleexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(imageparticleexample imageparticle_shared)
@@ -32,7 +26,6 @@ add_dependencies(imageparticleexample imageparticle_shared)
# Resources:
qt_add_qml_module(imageparticleexample
URI imageparticle
- AUTO_RESOURCE_PREFIX
QML_FILES
"allatonce.qml"
"colored.qml"
@@ -54,9 +47,16 @@ qt_add_qml_module(imageparticleexample
)
install(TARGETS imageparticleexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(imageparticleexample)
+qt_generate_deploy_qml_app_script(
+ TARGET imageparticleexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/particles/imageparticle/doc/src/imageparticle.qdoc b/examples/quick/particles/imageparticle/doc/src/imageparticle.qdoc
index d4c8c5d66b..4bbbc6a430 100644
--- a/examples/quick/particles/imageparticle/doc/src/imageparticle.qdoc
+++ b/examples/quick/particles/imageparticle/doc/src/imageparticle.qdoc
@@ -4,6 +4,7 @@
/*!
\title Qt Quick Particles Examples - Image Particles
\example particles/imageparticle
+ \examplecategory {Graphics}
\brief This is a collection of examples using Affectors in the QML particle system.
\image qml-imageparticle-example.png
diff --git a/examples/quick/particles/itemparticle/CMakeLists.txt b/examples/quick/particles/itemparticle/CMakeLists.txt
deleted file mode 100644
index 77fe5a4961..0000000000
--- a/examples/quick/particles/itemparticle/CMakeLists.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(itemparticle LANGUAGES CXX)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/particles/itemparticle")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_standard_project_setup()
-
-add_subdirectory("../../shared" "shared")
-
-qt_add_executable(itemparticleexample WIN32 MACOSX_BUNDLE
- main.cpp
-)
-
-target_link_libraries(itemparticleexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-add_dependencies(itemparticleexample itemparticle_shared)
-
-# Resources:
-qt_add_qml_module(itemparticleexample
- URI itemparticle
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "itemparticle.qml"
- "delegates.qml"
- "script.js"
- RESOURCES
- "images/rocket.png"
-)
-
-install(TARGETS itemparticleexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
-
-bundle_shared(itemparticleexample)
diff --git a/examples/quick/particles/itemparticle/delegates.qml b/examples/quick/particles/itemparticle/delegates.qml
deleted file mode 100644
index 35bbbc7132..0000000000
--- a/examples/quick/particles/itemparticle/delegates.qml
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Particles
-
-Rectangle {
- id: root;
- width: 360
- height: 600
- color: "black"
-
- function newPithySaying() {
- switch (Math.floor(Math.random()*16)) {
- case 0: return "Hello World";
- case 1: return "G'day Mate";
- case 2: return "Code Less";
- case 3: return "Create More";
- case 4: return "Deploy Everywhere";
- case 5: return "Qt Meta-object Language";
- case 6: return "Qt Magic Language";
- case 7: return "Fluid UIs";
- case 8: return "Touchable";
- case 9: return "How's it going?";
- case 10: return "Do you like text?";
- case 11: return "Enjoy!";
- case 12: return "ERROR: Out of pith";
- case 13: return "Punctuation Failure";
- case 14: return "I can go faster";
- case 15: return "I can go slower";
- default: return "OMGWTFBBQ";
- }
- }
-
- ParticleSystem {
- anchors.fill: parent
- id: syssy
- MouseArea {
- anchors.fill: parent
- onClicked: syssy.running = !syssy.running
- }
- Emitter {
- anchors.centerIn: parent
- emitRate: 1
- lifeSpan: 4800
- lifeSpanVariation: 1600
- velocity: AngleDirection {angleVariation: 360; magnitude: 40; magnitudeVariation: 20}
- }
- ItemParticle {
- delegate: Text {
- text: root.newPithySaying();
- color: "white"
- font.pixelSize: 18
- font.bold: true
- }
- }
- }
-}
diff --git a/examples/quick/particles/itemparticle/itemparticle.pro b/examples/quick/particles/itemparticle/itemparticle.pro
deleted file mode 100644
index c0426cd032..0000000000
--- a/examples/quick/particles/itemparticle/itemparticle.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-RESOURCES += \
- itemparticle.qrc \
- ../../shared/shared.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/particles/itemparticle
-INSTALLS += target
diff --git a/examples/quick/particles/itemparticle/itemparticle.qml b/examples/quick/particles/itemparticle/itemparticle.qml
deleted file mode 100644
index 6655dde939..0000000000
--- a/examples/quick/particles/itemparticle/itemparticle.qml
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Particles
-import shared
-import "script.js" as Script
-
-Item {
- id: root
- width: 640
- height: 480
- Rectangle {
- anchors.fill: parent
- color: "black"
- z: -1
- }
- Item {
- id: loading
- Behavior on opacity {NumberAnimation {}}
- anchors.fill: parent
- Text {
- anchors.centerIn: parent
- text: "Loading"
- color: "white"
- }
- }
- ParticleSystem {
- id: sys;
- running: true
- }
- Emitter {
- id: emitter
- system: sys
- height: parent.height - 132/2
- x: -132/2
- y: 132/2
- velocity: PointDirection { x: 32; xVariation: 8 }
- emitRate: 0.5
- lifeSpan: Emitter.InfiniteLife
- group: "photos"
- }
- Age {
- system: sys
- x: parent.width + 132/2
- height: parent.height
- width: 1000
- }
- ImageParticle {
- system: sys
- groups: ["fireworks"]
- source: "qrc:///particleresources/star.png"
- color: "lightsteelblue"
- alpha: 0
- colorVariation: 0
- z: 1000
- }
- ItemParticle {
- id: mp
- z: 0
- system: sys
- fade: false
- groups: ["photos"]
- }
- Component {
- id: alertDelegate
- Rectangle {
- color: "DarkSlateGray"
- border.width: 1
- border.color: "LightSteelBlue"
- width: 144
- height: 132
- antialiasing: true
- radius: 3
- NumberAnimation on scale {
- running: true
- loops: 1
- from: 0.2
- to: 1
- }
- Image {
- source: "images/rocket.png"
- anchors.centerIn: parent
- }
- Text {
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- text: "A new ship has arrived!"
- color: "LightSteelBlue"
- }
- }
- }
- property Item alertItem;
- function alert() {
- force.enabled = true;
- alertItem = alertDelegate.createObject(root);
- alertItem.x = root.width/2 - alertItem.width/2
- alertItem.y = root.height/2 - alertItem.height/2
- spawnFireworks.pulse(200);
- stopAlert.start();
- }
- focus: true
- Keys.onSpacePressed: alert();
- Timer {
- id: stopAlert
- running: false
- repeat: false
- interval: 800
- onTriggered: {
- force.enabled = false;
- mp.take(root.alertItem, true);
- centerEmitter.burst(1);
- }
- }
- Attractor {
- id: force
- system: sys
- pointX: root.width/2
- pointY: root.height/2
- strength: -10000
- enabled: false
- anchors.centerIn: parent
- width: parent.width/2
- height: parent.height/2
- groups:["photos"]
- affectedParameter: Attractor.Position
- }
- Emitter {
- id: centerEmitter
- velocity: PointDirection { x: 32; xVariation: 8;}
- emitRate: 0.5
- lifeSpan: 12000 //TODO: A -1 or something which does 'infinite'? (but need disable fade first)
- maximumEmitted: 20
- group: "photos"
- system: sys
- anchors.centerIn: parent
- enabled: false
-
- //TODO: Zoom in effect
- }
- Emitter {
- id: spawnFireworks
- group: "fireworks"
- system: sys
- maximumEmitted: 400
- emitRate: 400
- lifeSpan: 2800
- x: parent.width/2
- y: parent.height/2 - 64
- width: 8
- height: 8
- enabled: false
- size: 32
- endSize: 8
- velocity: AngleDirection { magnitude: 160; magnitudeVariation: 120; angleVariation: 90; angle: 270 }
- acceleration: PointDirection { y: 160 }
- }
- Item { x: -1000; y: -1000 //offscreen
- Repeater {//Load them here, add to system on completed
- model: flickrModel
- delegate: theDelegate
- }
- }
- FlickrRssModel {
- id: flickrModel
- tags: "particle,particles"
- }
- Component {
- id: theDelegate
- Image {
- required property int index
- required property string title
- required property string media
- required property string thumbnail
-
- id: image
- antialiasing: true;
- source: thumbnail
- cache: true
- property real depth: Math.random()
- property real darken: 0.75
- z: Math.floor(depth * 100)
- scale: (depth + 1) / 2
- sourceSize {
- width: root.width
- height: root.height
- }
- width: 132
- height: 132
- fillMode: Image.PreserveAspectFit
- Rectangle {
- // Darken based on depth
- anchors.centerIn: parent
- width: parent.paintedWidth + 1
- height: parent.paintedHeight + 1
- color: "black"
- opacity: image.darken * (1 - image.depth)
- antialiasing: true;
- }
- Text {
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottomMargin: Math.max(parent.paintedWidth, parent.paintedHeight) - Math.min(parent.width, parent.height)
- width: parent.paintedWidth - 4
- horizontalAlignment: Text.AlignHCenter
- elide: Text.ElideRight
- text: image.title
- color: "black"
- }
- ItemParticle.onDetached: mp.take(image); // respawns
- onStatusChanged: if (image.status == Image.Ready) {
- loading.opacity = 0;
- mp.take(image);
- }
- function manage()
- {
- if (state == "selected") {
- console.log("Taking " + index);
- mp.freeze(image);
- } else {
- console.log("Returning " +index);
- mp.unfreeze(image);
- }
- }
- TapHandler {
- gesturePolicy: TapHandler.ReleaseWithinBounds
- onTapped: image.state = (image.state == "" ? "selected" : "")
- }
- states: State {
- name: "selected"
- ParentChange {
- target: image
- parent: root
- }
- PropertyChanges {
- image {
- source: image.media
- x: 0
- y: 0
- width: root.width
- height: root.height
- z: 101
- opacity: 1
- rotation: 0
- darken: 0
- }
- }
- }
- transitions: Transition {
- to: "selected"
- reversible: true
- SequentialAnimation {
- ScriptAction { script: image.manage() }
- ParallelAnimation {
- ParentAnimation {NumberAnimation { properties: "x,y" }}
- PropertyAnimation { properties: "width, height, z, rotation, darken"; easing.type: Easing.InOutQuad }
- }
- }
- }
- }
- }
-}
diff --git a/examples/quick/particles/itemparticle/main.cpp b/examples/quick/particles/itemparticle/main.cpp
deleted file mode 100644
index 8baca3753c..0000000000
--- a/examples/quick/particles/itemparticle/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../../shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(itemparticle/itemparticle)
diff --git a/examples/quick/particles/particles.pro b/examples/quick/particles/particles.pro
index 7ef86618d5..62f7f35807 100644
--- a/examples/quick/particles/particles.pro
+++ b/examples/quick/particles/particles.pro
@@ -2,7 +2,6 @@ TEMPLATE = subdirs
SUBDIRS += affectors \
emitters \
imageparticle \
- itemparticle \
system
#Install shared images too
diff --git a/examples/quick/particles/system/CMakeLists.txt b/examples/quick/particles/system/CMakeLists.txt
index d224d607ce..0c8f8852f9 100644
--- a/examples/quick/particles/system/CMakeLists.txt
+++ b/examples/quick/particles/system/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(system LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/particles/system")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../../shared" "shared")
@@ -21,10 +15,10 @@ qt_add_executable(systemexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(systemexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(systemexample system_shared)
@@ -32,7 +26,6 @@ add_dependencies(systemexample system_shared)
# Resources:
qt_add_qml_module(systemexample
URI system
- AUTO_RESOURCE_PREFIX
QML_FILES
"dynamiccomparison.qml"
"dynamicemitters.qml"
@@ -47,9 +40,16 @@ qt_add_qml_module(systemexample
)
install(TARGETS systemexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(systemexample)
+qt_generate_deploy_qml_app_script(
+ TARGET systemexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/particles/system/doc/src/system.qdoc b/examples/quick/particles/system/doc/src/system.qdoc
index da10dfdac0..b936bd8976 100644
--- a/examples/quick/particles/system/doc/src/system.qdoc
+++ b/examples/quick/particles/system/doc/src/system.qdoc
@@ -4,6 +4,7 @@
/*!
\title Qt Quick Particles Examples - System
\example particles/system
+ \examplecategory {Graphics}
\brief This is a collection of examples using Affectors in the QML particle system.
\image qml-system-example.png
diff --git a/examples/quick/particles/system/dynamiccomparison.qml b/examples/quick/particles/system/dynamiccomparison.qml
index 0d8b4cc399..d29b72f998 100644
--- a/examples/quick/particles/system/dynamiccomparison.qml
+++ b/examples/quick/particles/system/dynamiccomparison.qml
@@ -4,6 +4,8 @@
import QtQuick
import QtQuick.Particles
+pragma ComponentBehavior: Bound
+
Rectangle {
id: root
color: "black"
@@ -38,11 +40,11 @@ Rectangle {
id: fakeEmitter
function burst(number) {
while (number > 0) {
- var item = fakeParticle.createObject(root);
- item.lifeSpan = Math.random() * 5000 + 5000;
- item.x = Math.random() * (root.width/2) + (root.width/2);
- item.y = 0;
- number--;
+ let item = fakeParticle.createObject(root)
+ item.lifeSpan = Math.random() * 5000 + 5000
+ item.x = Math.random() * (root.width / 2) + (root.width / 2)
+ item.y = 0
+ number--
}
}
@@ -54,13 +56,12 @@ Rectangle {
width: 32
height: 32
source: "qrc:///particleresources/glowdot.png"
- y: 0
- PropertyAnimation on y {from: -16; to: root.height-16; duration: container.lifeSpan; running: true}
+ PropertyAnimation on y { from: -16; to: root.height - 16; duration: container.lifeSpan; running: true }
SequentialAnimation on opacity {
running: true
- NumberAnimation { from:0; to: 1; duration: 500}
- PauseAnimation { duration: container.lifeSpan - 1000}
- NumberAnimation { from:1; to: 0; duration: 500}
+ NumberAnimation { from: 0; to: 1; duration: 500 }
+ PauseAnimation { duration: container.lifeSpan - 1000 }
+ NumberAnimation { from: 1; to: 0; duration: 500 }
ScriptAction { script: container.destroy(); }
}
}
@@ -82,7 +83,7 @@ Rectangle {
Text {
anchors.left: parent.left
anchors.bottom: parent.bottom
- text: "1000 particles"
+ text: qsTr("1000 particles")
color: "white"
MouseArea {
anchors.fill: parent
@@ -92,7 +93,7 @@ Rectangle {
Text {
anchors.right: parent.right
anchors.bottom: parent.bottom
- text: "1000 items"
+ text: qsTr("1000 items")
color: "white"
MouseArea {
anchors.fill: parent
diff --git a/examples/quick/particles/system/dynamicemitters.qml b/examples/quick/particles/system/dynamicemitters.qml
index 29f9235ac8..31c70b2a6c 100644
--- a/examples/quick/particles/system/dynamicemitters.qml
+++ b/examples/quick/particles/system/dynamicemitters.qml
@@ -4,6 +4,8 @@
import QtQuick
import QtQuick.Particles
+pragma ComponentBehavior: Bound
+
Rectangle {
id: root
color: "black"
@@ -31,15 +33,18 @@ Rectangle {
lifeSpan: 600
size: 16
endSize: 8
- velocity: AngleDirection {angleVariation:360; magnitude: 60}
+ velocity: AngleDirection {
+ angleVariation: 360
+ magnitude: 60
+ }
}
property int life: 2600
property real targetX: 0
property real targetY: 0
function go() {
- xAnim.start();
- yAnim.start();
+ xAnim.start()
+ yAnim.start()
container.enabled = true
}
system: sys
@@ -49,35 +54,35 @@ Rectangle {
endSize: 8
NumberAnimation on x {
id: xAnim;
- to: targetX
- duration: life
+ to: container.targetX
+ duration: container.life
running: false
}
NumberAnimation on y {
id: yAnim;
- to: targetY
- duration: life
+ to: container.targetY
+ duration: container.life
running: false
}
Timer {
- interval: life
+ interval: container.life
running: true
- onTriggered: container.destroy();
+ onTriggered: container.destroy()
}
}
}
- function customEmit(x,y) {
+ function customEmit(x, y) {
//! [0]
- for (var i=0; i<8; i++) {
- var obj = emitterComp.createObject(root);
+ for (var i = 0; i < 8; i++) {
+ let obj = emitterComp.createObject(root)
obj.x = x
obj.y = y
obj.targetX = Math.random() * 240 - 120 + obj.x
obj.targetY = Math.random() * 240 - 120 + obj.y
obj.life = Math.round(Math.random() * 2400) + 200
obj.emitRate = Math.round(Math.random() * 32) + 32
- obj.go();
+ obj.go()
}
//! [0]
}
@@ -87,16 +92,15 @@ Rectangle {
triggeredOnStart: true
running: true
repeat: true
- onTriggered: customEmit(Math.random() * 320, Math.random() * 480)
+ onTriggered: root.customEmit(Math.random() * 320, Math.random() * 480)
}
- MouseArea {
- anchors.fill: parent
- onClicked: (mouse)=> customEmit(mouse.x, mouse.y);
+ TapHandler {
+ onTapped: function(event) { root.customEmit(event.pressPosition.x, event.pressPosition.y) }
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
- text: "Click Somewhere"
+ text: qsTr("Click Somewhere")
color: "white"
font.pixelSize: 24
}
diff --git a/examples/quick/particles/system/multiplepainters.qml b/examples/quick/particles/system/multiplepainters.qml
index dbbe3abed5..5356f5fe8a 100644
--- a/examples/quick/particles/system/multiplepainters.qml
+++ b/examples/quick/particles/system/multiplepainters.qml
@@ -13,13 +13,12 @@ Rectangle {
ParticleSystem {
id: sys
}
- MouseArea {
- anchors.fill: parent
- onClicked: cloneMode = !cloneMode;
+ TapHandler {
+ onTapped: root.cloneMode = !root.cloneMode
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
- text: "Click to Toggle"
+ text: qsTr("Click to Toggle")
color: "white"
font.pixelSize: 24
}
@@ -30,18 +29,18 @@ Rectangle {
emitRate: 200
lifeSpan: 4000
startTime: 4000
- velocity: PointDirection { y: -120; }
+ velocity: PointDirection { y: -120 }
}
ImageParticle {
system: sys
- visible: !cloneMode
+ visible: !root.cloneMode
source: "images/particle2.png"
}
ImageParticle {
system: sys
- visible: cloneMode
+ visible: root.cloneMode
z: 0
source: "images/particle3.png"
}
@@ -49,7 +48,7 @@ Rectangle {
ImageParticle {
system: sys
clip: true
- visible: cloneMode
+ visible: root.cloneMode
y: 120
height: 240
width: root.width
diff --git a/examples/quick/particles/system/startstop.qml b/examples/quick/particles/system/startstop.qml
index e275a4410b..e89c1f40f0 100644
--- a/examples/quick/particles/system/startstop.qml
+++ b/examples/quick/particles/system/startstop.qml
@@ -9,15 +9,16 @@ Rectangle {
height: 540
color: "black"
Text {
- text: "Left click to start/stop\nRight click to pause/unpause"
+ text: qsTr("Left click to start/stop\nRight click to pause/unpause")
color: "white"
font.pixelSize: 24
}
- MouseArea {
- anchors.fill: parent
+
+ TapHandler {
acceptedButtons: Qt.LeftButton | Qt.RightButton
- onClicked: (mouse) => {
- if (mouse.button == Qt.LeftButton)
+ onTapped: function (event, mouseButton)
+ {
+ if (mouseButton === Qt.LeftButton)
particles.running = !particles.running
else
particles.paused = !particles.paused;
diff --git a/examples/quick/pointerhandlers/CMakeLists.txt b/examples/quick/pointerhandlers/CMakeLists.txt
index d0fdd89eae..a8b3cbe3d0 100644
--- a/examples/quick/pointerhandlers/CMakeLists.txt
+++ b/examples/quick/pointerhandlers/CMakeLists.txt
@@ -1,39 +1,33 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(pointerhandlers LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/pointerhandlers")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick Svg)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../shared" "shared")
qt_add_executable(pointerhandlersexample WIN32 MACOSX_BUNDLE main.cpp)
target_link_libraries(pointerhandlersexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
- Qt::Svg
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::Svg
)
add_dependencies(pointerhandlersexample pointerhandlers_shared)
qt_add_qml_module(pointerhandlersexample
URI pointerhandlers
- AUTO_RESOURCE_PREFIX
QML_FILES
"components/Button.qml"
"components/CheckBox.qml"
+ "components/CorkPanel.qml"
"components/FakeFlickable.qml"
"components/FlashAnimation.qml"
"components/LeftDrawer.qml"
@@ -43,12 +37,14 @@ qt_add_qml_module(pointerhandlersexample
"components/ScrollBar.qml"
"components/Slider.qml"
"components/TouchpointFeedbackSprite.qml"
+ "corkboards.qml"
"fakeFlickable.qml"
"flingAnimation.qml"
"joystick.qml"
"map.qml"
"mixer.qml"
"multibuttons.qml"
+ "multiflame.qml"
"pieMenu.qml"
"pinchHandler.qml"
"pointerhandlers.qml"
@@ -58,6 +54,7 @@ qt_add_qml_module(pointerhandlersexample
"tapHandler.qml"
RESOURCES
"components/images/checkmark.png"
+ "components/images/cork.jpg"
"components/images/fingersprite.png"
"components/images/mixer-knob.png"
"components/images/mouse.png"
@@ -65,8 +62,11 @@ qt_add_qml_module(pointerhandlersexample
"components/images/mouse_middle.png"
"components/images/mouse_right.png"
"components/images/mouse_wheel_ridges.png"
+ "components/images/note-yellow.png"
+ "components/images/tack.png"
"images/arrowhead.png"
"images/balloon.png"
+ "images/blur-circle.png"
"images/cursor-airbrush.png"
"images/cursor-eraser.png"
"images/cursor-felt-marker.png"
@@ -80,9 +80,16 @@ qt_add_qml_module(pointerhandlersexample
)
install(TARGETS pointerhandlersexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(pointerhandlersexample)
+qt_generate_deploy_qml_app_script(
+ TARGET pointerhandlersexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/pointerhandlers/components/CorkPanel.qml b/examples/quick/pointerhandlers/components/CorkPanel.qml
new file mode 100644
index 0000000000..4aec2b5c7c
--- /dev/null
+++ b/examples/quick/pointerhandlers/components/CorkPanel.qml
@@ -0,0 +1,118 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Image {
+ id: corkPanel
+ source: Qt.resolvedUrl("images/cork.jpg")
+ width: ListView.view.width
+ height: ListView.view.height
+ fillMode: Image.PreserveAspectCrop
+
+ required property string name
+ required property var notes
+
+ TapHandler {
+ objectName: corkPanel.name
+ onTapped: corkPanel.Window.activeFocusItem.focus = false
+ }
+
+ Text {
+ text: corkPanel.name
+ x: 15
+ y: 8
+ height: 40
+ width: 370
+ font.pixelSize: 18
+ font.bold: true
+ color: "white"
+ style: Text.Outline
+ styleColor: "black"
+ wrapMode: Text.Wrap
+ }
+
+ Repeater {
+ model: corkPanel.notes
+ Item {
+ id: fulcrum
+
+ x: 100 + Math.random() * (corkPanel.width - 0.5 * paper.width)
+ y: 50 + Math.random() * (corkPanel.height - 0.5 * paper.height)
+
+ Item {
+ id: note
+ scale: 0.7
+
+ Image {
+ id: paper
+ x: 8 + -width * 0.6 / 2
+ y: -20
+ source: "images/note-yellow.png"
+ scale: 0.6
+ transformOrigin: Item.TopLeft
+ antialiasing: true
+
+ DragHandler {
+ target: fulcrum
+ xAxis.minimum: 100
+ xAxis.maximum: corkPanel.width - 80
+ yAxis.minimum: 0
+ yAxis.maximum: corkPanel.height - 80
+ }
+ }
+
+ TextEdit {
+ id: text
+ x: -104
+ y: 36
+ width: 215
+ height: 24
+ font.pixelSize: 24
+ readOnly: false
+ selectByMouse: activeFocus
+ rotation: -8
+ text: noteText
+ wrapMode: Text.Wrap
+ }
+
+ rotation: -flickable.horizontalVelocity / 100
+ Behavior on rotation {
+ SpringAnimation {
+ spring: 2.0
+ damping: 0.15
+ }
+ }
+ }
+
+ Image {
+ x: -width / 2
+ y: -height * 0.5 / 2
+ source: "images/tack.png"
+ scale: 0.7
+ transformOrigin: Item.TopLeft
+ }
+
+ states: State {
+ name: "pressed"
+ when: text.activeFocus
+ PropertyChanges {
+ target: note
+ rotation: 8
+ scale: 1
+ }
+ PropertyChanges {
+ target: fulcrum
+ z: 8
+ }
+ }
+
+ transitions: Transition {
+ NumberAnimation {
+ properties: "rotation,scale"
+ duration: 200
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/pointerhandlers/components/FlashAnimation.qml b/examples/quick/pointerhandlers/components/FlashAnimation.qml
index e369282470..bdbcaecdfa 100644
--- a/examples/quick/pointerhandlers/components/FlashAnimation.qml
+++ b/examples/quick/pointerhandlers/components/FlashAnimation.qml
@@ -6,15 +6,9 @@ import QtQuick
SequentialAnimation {
id: tapFlash
running: false
+ loops: 3
PropertyAction { value: false }
PauseAnimation { duration: 100 }
PropertyAction { value: true }
PauseAnimation { duration: 100 }
- PropertyAction { value: false }
- PauseAnimation { duration: 100 }
- PropertyAction { value: true }
- PauseAnimation { duration: 100 }
- PropertyAction { value: false }
- PauseAnimation { duration: 100 }
- PropertyAction { value: true }
}
diff --git a/examples/quick/pointerhandlers/components/MouseFeedbackSprite.qml b/examples/quick/pointerhandlers/components/MouseFeedbackSprite.qml
index 1de4de8e48..57c58d826f 100644
--- a/examples/quick/pointerhandlers/components/MouseFeedbackSprite.qml
+++ b/examples/quick/pointerhandlers/components/MouseFeedbackSprite.qml
@@ -10,22 +10,27 @@ HoverHandler {
target: Image {
objectName: "mouse sprite"
source: "images/mouse.png"
- opacity: (handler.point.pressedButtons || wheelAnimationTimer.running) ? 1 : 0
+ opacity: (phandler.point.pressedButtons || wheelAnimationTimer.running) ? 1 : 0
x: handler.point.position.x - width / 2
y: handler.point.position.y - height / 2
parent: handler.parent
Image {
source: "images/mouse_left.png"
- visible: handler.point.pressedButtons & Qt.LeftButton
+ visible: phandler.point.pressedButtons & Qt.LeftButton
}
Image {
source: "images/mouse_middle.png"
- visible: handler.point.pressedButtons & Qt.MiddleButton
+ visible: phandler.point.pressedButtons & Qt.MiddleButton
}
Image {
source: "images/mouse_right.png"
- visible: handler.point.pressedButtons & Qt.RightButton
+ visible: phandler.point.pressedButtons & Qt.RightButton
}
+ PointHandler {
+ id: phandler
+ acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
+ }
+
WheelHandler {
blocking: false
onWheel: (event)=> {
diff --git a/examples/quick/pointerhandlers/components/QuadPieMenu.qml b/examples/quick/pointerhandlers/components/QuadPieMenu.qml
index 9c2364fea2..eae268eccb 100644
--- a/examples/quick/pointerhandlers/components/QuadPieMenu.qml
+++ b/examples/quick/pointerhandlers/components/QuadPieMenu.qml
@@ -9,6 +9,7 @@ TapHandler {
signal triggered(string text)
id: menuTap
+ acceptedButtons: Qt.RightButton
gesturePolicy: TapHandler.DragWithinBounds
onPressedChanged: if (pressed) {
impl.x = point.position.x - impl.width / 2
@@ -22,7 +23,10 @@ TapHandler {
parent: menuTap.parent
width: 100
height: 100
- scale: Math.min(1, Math.max(0, menuTap.timeHeld * 4))
+ // with touchscreen or stylus, long-press slowly expands the menu to size
+ // with mouse or touchpad right-click, it opens instantly
+ scale: menuTap.point.device.pointerType === PointerDevice.Generic ?
+ 1 : Math.min(1, Math.max(0, menuTap.timeHeld * 4))
opacity: scale * 2
visible: menuTap.pressed
property Shape highlightedShape: null
@@ -35,6 +39,7 @@ TapHandler {
width: 100
height: 100
containsMode: Shape.FillContains
+ preferredRendererType: Shape.CurveRenderer
property bool highlighted: menuTap.pressed &&
shape.contains(shape.mapFromItem(menuTap.parent, menuTap.point.position))
diff --git a/examples/quick/pointerhandlers/components/ScrollBar.qml b/examples/quick/pointerhandlers/components/ScrollBar.qml
index d78ee56140..6b13c03305 100644
--- a/examples/quick/pointerhandlers/components/ScrollBar.qml
+++ b/examples/quick/pointerhandlers/components/ScrollBar.qml
@@ -70,7 +70,7 @@ Rectangle {
PropertyChanges {
knob {
max: root.width - knob.width
- scrolledX: Math.min(max, Math.max(0, max * flick.contentX / (flick.width - flick.contentWidth)))
+ scrolledX: Math.min(knob.max, Math.max(0, knob.max * flick.contentX / (flick.width - flick.contentWidth)))
scrolledY: 1
scrollDistance: flick.width - flick.contentWidth
width: flick.width * (flick.width / flick.contentWidth) - (height - anchors.margins) * 2
@@ -91,7 +91,7 @@ Rectangle {
knob {
max: root.height - knob.height
scrolledX: 1
- scrolledY: Math.min(max, Math.max(0, max * flick.contentY / (flick.height - flick.contentHeight)))
+ scrolledY: Math.min(knob.max, Math.max(0, knob.max * flick.contentY / (flick.height - flick.contentHeight)))
scrollDistance: flick.height - flick.contentHeight
width: root.width - 2
height: root.width - 2
diff --git a/examples/quick/touchinteraction/flickable/cork.jpg b/examples/quick/pointerhandlers/components/images/cork.jpg
index 160bc002bf..160bc002bf 100644
--- a/examples/quick/touchinteraction/flickable/cork.jpg
+++ b/examples/quick/pointerhandlers/components/images/cork.jpg
Binary files differ
diff --git a/examples/quick/touchinteraction/flickable/note-yellow.png b/examples/quick/pointerhandlers/components/images/note-yellow.png
index 3195952ad2..3195952ad2 100644
--- a/examples/quick/touchinteraction/flickable/note-yellow.png
+++ b/examples/quick/pointerhandlers/components/images/note-yellow.png
Binary files differ
diff --git a/examples/quick/touchinteraction/flickable/tack.png b/examples/quick/pointerhandlers/components/images/tack.png
index cef2d1cd23..cef2d1cd23 100644
--- a/examples/quick/touchinteraction/flickable/tack.png
+++ b/examples/quick/pointerhandlers/components/images/tack.png
Binary files differ
diff --git a/examples/quick/pointerhandlers/corkboards.qml b/examples/quick/pointerhandlers/corkboards.qml
new file mode 100644
index 0000000000..76928f1b8c
--- /dev/null
+++ b/examples/quick/pointerhandlers/corkboards.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import "components"
+
+Rectangle {
+ width: 320; height: 480
+ color: "#464646"
+
+ ListModel {
+ id: list
+
+ ListElement {
+ name: "Panel One"
+ notes: [
+ ListElement { noteText: "Tap to edit" },
+ ListElement { noteText: "Drag to move" },
+ ListElement { noteText: "Flick to scroll" }
+ ]
+ }
+
+ ListElement {
+ name: "Panel Two"
+ notes: [
+ ListElement { noteText: "Note One" },
+ ListElement { noteText: "Note Two" }
+ ]
+ }
+
+ ListElement {
+ name: "Panel Three"
+ notes: [
+ ListElement { noteText: "Note Three" }
+ ]
+ }
+ }
+
+ ListView {
+ id: flickable
+
+ anchors.fill: parent
+ focus: true
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ orientation: ListView.Horizontal
+ snapMode: ListView.SnapOneItem
+ model: list
+ delegate: CorkPanel { objectName: name }
+ }
+}
+
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fakeflickable.jpg b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fakeflickable.jpg
new file mode 100644
index 0000000000..5149665a07
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fakeflickable.jpg
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fling.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fling.webp
new file mode 100644
index 0000000000..4b8794971e
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-fling.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-joystick.jpg b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-joystick.jpg
new file mode 100644
index 0000000000..c9d167b1e7
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-joystick.jpg
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-map.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-map.webp
new file mode 100644
index 0000000000..b96b0cf90a
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-map.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-mixer.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-mixer.webp
new file mode 100644
index 0000000000..f263458d12
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-mixer.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-multibutton.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-multibutton.webp
new file mode 100644
index 0000000000..1d13805c64
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-multibutton.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-piemenu.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-piemenu.webp
new file mode 100644
index 0000000000..cc5ca5d394
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-piemenu.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pinchhandler.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pinchhandler.webp
new file mode 100644
index 0000000000..89f61251ab
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pinchhandler.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pointhandler.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pointhandler.webp
new file mode 100644
index 0000000000..1a0ac33d0c
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-pointhandler.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-taphandler.webp b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-taphandler.webp
new file mode 100644
index 0000000000..162e97ea15
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/images/pointerhandlers-example-taphandler.webp
Binary files differ
diff --git a/examples/quick/pointerhandlers/doc/src/pointerhandlers.qdoc b/examples/quick/pointerhandlers/doc/src/pointerhandlers.qdoc
new file mode 100644
index 0000000000..d9f2e5f1cc
--- /dev/null
+++ b/examples/quick/pointerhandlers/doc/src/pointerhandlers.qdoc
@@ -0,0 +1,171 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example pointerhandlers
+ \title Qt Quick Examples - Pointer Handlers
+ \ingroup qtquickexamples
+ \examplecategory {User Interface Components}
+ \meta tags {quick, input handlers}
+ \brief Shows how to use \l {Qt Quick Input Handlers} in interactive components.
+
+ \l {Qt Quick Controls} contains pre-built components that are sufficient
+ for many kinds of user interfaces; but you may sometimes find a need to
+ write such components "from scratch". In that case, you will need to make
+ substantial use of Pointer Handlers. This is a collection of small examples
+ that show how to implement some common and less-common interaction patterns.
+
+ If you build the example as an executable, you can choose pages from a
+ top-level ListView. The \c TouchpointFeedbackSprite and \c MouseFeedbackSprite
+ components show how to use PointHandler to provide feedback about what the
+ user is doing with the pointing devices: these components are always
+ visible throughout the example, and do not interfere with interaction
+ anywhere else. These components are handy for reuse if you ever need to do
+ a video or live demonstration of a user interface.
+
+ Alternatively, some of the individual QML files can be run on the command
+ line with the \l {QML Runtime Tool}{qml tool}, as a quick reminder to see
+ how a particular kind of component can be built.
+
+ \section1 TapHandler
+
+ \image pointerhandlers-example-taphandler.webp
+
+ The "tap" page shows how to detect touchscreen taps, long-presses, and
+ mouse clicks with different buttons. You can also try out the different
+ \l {TapHandler::}{gesturePolicy} values.
+
+ \section2 Multi-tap Buttons
+
+ On this page are three custom \c Button components, with different
+ \l {TapHandler::}{gesturePolicy} values: one button requires you
+ to keep your finger or mouse within the button during the whole time
+ from press to release; one will execute even if you drag outside
+ the button and then drag back in before releasing; and one will
+ exit its pressed state if you drag a little past the
+ \l {QStyleHints::startDragDistance}{system-wide drag threshold}.
+ If you try this example on a touchscreen, you should be able to interact
+ with all three buttons at the same time with different fingers.
+
+ \image pointerhandlers-example-multibutton.webp
+
+ \section2 Pie Menu
+
+ This page shows how to write a touchscreen-friendly pie menu that animates
+ into view when activated by a long-press. You can select a menu item by
+ dragging from the center position onto one of the sectors and releasing. If
+ you drag outside the menu, or release in the middle of the menu, nothing is
+ selected. If you drag outside the grey viewport, the menu is cancelled.
+
+ \image pointerhandlers-example-piemenu.webp
+
+ \section1 PointHandler
+
+ On the "single point handler" page, PointHandler provides feedback from
+ some of the properties that can be found in \l SinglePointHandler::point
+ and \l handlerPoint::device. If you have built the example as
+ an executable, you will see this feedback at the same time as the feedback
+ from the PointHandlers in the main pointerhandlers.qml file.
+
+ \image pointerhandlers-example-pointhandler.webp
+
+ The "tablet canvas" page uses PointHandler to uniquely detect different
+ \l {QPointingDevice::PointerType}{types of pointing devices}, so that if
+ you have a drawing tablet connected to your computer, you can draw on the
+ Canvas item with different types of stylus: pen, airbrush or marker. You
+ can "erase" strokes with the stylus eraser (done here by drawing on top
+ with the background color). Several \l {HoverHandler}{HoverHandlers}
+ provide feedback about which type of stylus or eraser is detected.
+
+ \section1 HoverHandler
+
+ The "hover sidebar" page shows how to detect when the mouse is hovering a
+ button component and its container at the same time. The propagation can be
+ disabled by setting the \l HoverHandler::blocking property. You can try out
+ all combinations of HoverHandler and MouseArea here, to compare how they
+ handle hover detection. And one more HoverHandler is used to show the
+ current mouse position in scene coordinates.
+
+ You can also verify that the HoverHandler on the animated "platform"
+ is hovered whenever it slides under the mouse cursor.
+
+ \section1 DragHandler
+
+ The "joystick" page simply has a \l DragHandler dragging an \l Image.
+ A \l State with \l AnchorChanges unlocks the \l anchors so that dragging is
+ possible; and an \l AnchorAnimation animates the knob's return to the
+ center position when released.
+
+ \image pointerhandlers-example-joystick.jpg
+
+ The "fling animation" page demonstrates one use of the
+ DragHandler::centroid::velocity property, which simply makes the value from
+ QEventPoint::velocity() available in QML. The \c MomentumAnimation component
+ that is used in this example shows one way to simulate phyics (momentum and
+ friction) without resorting to heavier solutions.
+
+ \image pointerhandlers-example-fling.webp
+
+ \section1 PinchHandler
+
+ The "pinch" page demonstrates multiple PinchHandlers: some require two
+ fingers and some require three, to perform the usual scaling, rotation and
+ translation, with constraints managed via PinchHandler's axis min and max
+ properties. One of the PinchHandler instances is used to manipulate
+ different \l Rectangle properties rather than position, scale and rotation.
+ Various PinchHandler properties are bound in various ways. You should be
+ able to manipulate each instance uniquely. Some items also have
+ DragHandlers and TapHandlers.
+
+ \image pointerhandlers-example-pinchhandler.webp
+
+ \section1 Interoperability
+
+ \section2 Sliders
+
+ The "mixer" page demonstrates the use of multiple handlers inside a
+ ListView delegate. You can interact with multiple \c Slider components
+ simultaneously, and you can flick the ListView sideways.
+
+ \list
+ \li A DragHandler allows you to start dragging anywhere along the "track"
+ where the slider knob can slide. Because the default \l{DragHandler::}{snapMode}
+ is \c DragHandler.SnapAuto, the knob will \e snap into a position centered
+ under the mouse or touchpoint after you have dragged past the
+ \l {QStyleHints::startDragDistance}{system-wide drag threshold}.
+
+ \li A WheelHandler directly adjusts the \c y property of the knob \l Image.
+
+ \li A BoundaryRule prevents either the DragHandler or the WheelHandler from
+ dragging the knob too far.
+
+ \li A TapHandler provides one more gesture than a typical \c Slider component
+ would have: you can tap on the knob.
+ \endlist
+
+ \image pointerhandlers-example-mixer.webp
+
+ \section2 Map
+
+ The "map" page demonstrates dragging, transformation and re-scaling an
+ \l {Qt SVG}{SVG} \l Image. You should be able to zoom into a particular
+ location on the map; and if the zoom level is changed substantially,
+ \l Image::sourceSize is changed to request the SVG to be re-rendered at a
+ different resolution. Dragging vertically with two fingers activates a
+ DragHandler that manipulates a \l Rotation transform to tilt the map.
+
+ \image pointerhandlers-example-map.webp
+
+ \section2 Fake Flickable
+
+ The "fake Flickable" page contains an attempt to reproduce much of the
+ functionality of \l Flickable using discrete pointer handlers and
+ animations. Flickable is a complex component, but here you can see one way
+ of separating the individual behaviors that it provides, in case you would
+ like to have only part of the functionality without the complexity.
+
+ There's also a slide-out "drawer" component holding a few more
+ pointer-handler-powered controls.
+
+ \image pointerhandlers-example-fakeflickable.jpg
+*/
diff --git a/examples/quick/touchinteraction/multipointtouch/blur-circle.png b/examples/quick/pointerhandlers/images/blur-circle.png
index 1b03cd0b2c..1b03cd0b2c 100644
--- a/examples/quick/touchinteraction/multipointtouch/blur-circle.png
+++ b/examples/quick/pointerhandlers/images/blur-circle.png
Binary files differ
diff --git a/examples/quick/pointerhandlers/multiflame.qml b/examples/quick/pointerhandlers/multiflame.qml
new file mode 100644
index 0000000000..7793e52ad6
--- /dev/null
+++ b/examples/quick/pointerhandlers/multiflame.qml
@@ -0,0 +1,69 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Particles
+
+Rectangle {
+ id: root
+ width: 360
+ height: 480
+ color: "black"
+
+ component ColoredEmitter: Emitter {
+ id: emitter
+ property string color
+ group: color
+ velocityFromMovement: 10
+ emitRate: 80
+ lifeSpan: 1500
+ velocity: PointDirection {
+ y: -90
+ yVariation: 50
+ }
+ acceleration: PointDirection {
+ xVariation: 100
+ yVariation: 90
+ }
+ size: 51
+ sizeVariation: 53
+ endSize: 64
+ enabled: handler.active
+ x: handler.point.position.x
+ y: handler.point.position.y
+
+ PointHandler {
+ id: handler
+ parent: root
+ }
+
+ ImageParticle {
+ id: img
+ groups: [emitter.color]
+ source: "images/blur-circle.png"
+ colorVariation: 0.1
+ color: emitter.color
+ alpha: 0
+ system: sys
+ }
+ }
+
+ ParticleSystem {
+ id: sys
+ ColoredEmitter {
+ color: "indianred"
+ }
+ ColoredEmitter {
+ color: "greenyellow"
+ }
+ ColoredEmitter {
+ color: "yellow"
+ }
+ ColoredEmitter {
+ color: "darkorange"
+ }
+ ColoredEmitter {
+ color: "violet"
+ }
+ }
+}
diff --git a/examples/quick/pointerhandlers/pointerhandlers.qml b/examples/quick/pointerhandlers/pointerhandlers.qml
index f063853793..049182fa8a 100644
--- a/examples/quick/pointerhandlers/pointerhandlers.qml
+++ b/examples/quick/pointerhandlers/pointerhandlers.qml
@@ -20,12 +20,14 @@ Rectangle {
addExample("multibuttons", "TapHandler: gesturePolicy (99 red balloons)", Qt.resolvedUrl("multibuttons.qml"))
addExample("pieMenu", "TapHandler: pie menu", Qt.resolvedUrl("pieMenu.qml"))
addExample("single point handler", "PointHandler: properties such as seat, device, modifiers, velocity, pressure", Qt.resolvedUrl("singlePointHandlerProperties.qml"))
+ addExample("multiflame", "PointHandler: particle flames around touchpoints", Qt.resolvedUrl("multiflame.qml"))
addExample("hover sidebar", "HoverHandler: a hierarchy of items sharing the hover state", Qt.resolvedUrl("sidebar.qml"))
addExample("joystick", "DragHandler: move one item inside another with any pointing device", Qt.resolvedUrl("joystick.qml"))
addExample("mixer", "DragHandler: drag multiple sliders with multiple fingers", Qt.resolvedUrl("mixer.qml"))
addExample("fling animation", "DragHandler: after dragging, use an animation to simulate momentum", Qt.resolvedUrl("flingAnimation.qml"))
addExample("pinch", "PinchHandler: scale, rotate and drag", Qt.resolvedUrl("pinchHandler.qml"))
addExample("map", "scale, pan, re-render at different resolutions", Qt.resolvedUrl("map.qml"))
+ addExample("corkboards", "editable, movable sticky notes in a ListView", Qt.resolvedUrl("corkboards.qml"))
addExample("fake Flickable", "implementation of a simplified Flickable using only Items, DragHandler and MomentumAnimation", Qt.resolvedUrl("fakeFlickable.qml"))
addExample("tablet canvas", "PointHandler and HoverHandler with a tablet: detect the stylus, and draw", Qt.resolvedUrl("tabletCanvasDrawing.qml"))
}
@@ -40,7 +42,7 @@ Rectangle {
model: 10
delegate: TouchpointFeedbackSprite { parent: glassPane }
}
-
- MouseFeedbackSprite { }
}
+
+ MouseFeedbackSprite { }
}
diff --git a/examples/quick/pointerhandlers/qml.qrc b/examples/quick/pointerhandlers/qml.qrc
index e9a39bc50c..237eb8eae0 100644
--- a/examples/quick/pointerhandlers/qml.qrc
+++ b/examples/quick/pointerhandlers/qml.qrc
@@ -1,11 +1,13 @@
<RCC>
- <qresource prefix="/pointerhandlers">
+ <qresource prefix="/qt/qml/pointerhandlers">
+ <file>corkboards.qml</file>
<file>flingAnimation.qml</file>
<file>fakeFlickable.qml</file>
<file>joystick.qml</file>
<file>map.qml</file>
<file>mixer.qml</file>
<file>multibuttons.qml</file>
+ <file>multiflame.qml</file>
<file>pieMenu.qml</file>
<file>pinchHandler.qml</file>
<file>pointerhandlers.qml</file>
@@ -15,6 +17,7 @@
<file>tapHandler.qml</file>
<file>components/Button.qml</file>
<file>components/CheckBox.qml</file>
+ <file>components/CorkPanel.qml</file>
<file>components/FakeFlickable.qml</file>
<file>components/FlashAnimation.qml</file>
<file>components/LeftDrawer.qml</file>
@@ -27,6 +30,10 @@
<file>images/arrowhead.png</file>
<file>images/balloon.png</file>
<file>components/images/checkmark.png</file>
+ <file>components/images/cork.jpg</file>
+ <file>components/images/note-yellow.png</file>
+ <file>components/images/tack.png</file>
+ <file>images/blur-circle.png</file>
<file>images/cursor-airbrush.png</file>
<file>images/cursor-eraser.png</file>
<file>images/cursor-felt-marker.png</file>
diff --git a/examples/quick/pointerhandlers/tapHandler.qml b/examples/quick/pointerhandlers/tapHandler.qml
index 178c067ce0..5891491695 100644
--- a/examples/quick/pointerhandlers/tapHandler.qml
+++ b/examples/quick/pointerhandlers/tapHandler.qml
@@ -6,20 +6,22 @@ import QtQuick.Layouts
import "components"
Item {
- width: 800
+ width: 1024
height: 480
Rectangle {
id: rect
anchors.fill: parent; anchors.margins: 40; anchors.topMargin: 60
- border.width: 3; border.color: "transparent"
- color: handler.pressed ? "lightsteelblue" : "darkgrey"
+ border.width: 4; border.color: "transparent"
+ color: handler.pressed ? "lightsteelblue" : "aliceblue"
TapHandler {
id: handler
acceptedButtons: (leftAllowedCB.checked ? Qt.LeftButton : Qt.NoButton) |
(middleAllowedCB.checked ? Qt.MiddleButton : Qt.NoButton) |
(rightAllowedCB.checked ? Qt.RightButton : Qt.NoButton)
+ exclusiveSignals: (singleExclusiveCB.checked ? TapHandler.SingleTap : TapHandler.NotExclusive) |
+ (doubleExclusiveCB.checked ? TapHandler.DoubleTap : TapHandler.NotExclusive)
gesturePolicy: (policyDragThresholdCB.checked ? TapHandler.DragThreshold :
policyWithinBoundsCB.checked ? TapHandler.WithinBounds :
policyDragWithinBoundsCB.checked ? TapHandler.DragWithinBounds :
@@ -30,6 +32,16 @@ Item {
borderBlink.blinkColor = "red"
borderBlink.start()
}
+ onSingleTapped: function(point, button) {
+ console.log("single-tapped button " + button + " @ " + point.scenePosition)
+ rect.border.width = 4
+ borderBlink.tapFeedback(button)
+ }
+ onDoubleTapped: function(point, button) {
+ console.log("double-tapped button " + button + " @ " + point.scenePosition)
+ rect.border.width = 12
+ borderBlink.tapFeedback(button)
+ }
onTapped: function(point, button) {
console.log("tapped button " + button + " @ " + point.scenePosition +
" on device '" + point.device.name + "' with modifiers " + handler.point.modifiers +
@@ -37,8 +49,6 @@ Item {
if (tapCount > 1) {
tapCountLabel.text = tapCount
flashAnimation.start()
- } else {
- borderBlink.tapFeedback(button)
}
}
onLongPressed: longPressFeedback.createObject(rect,
@@ -97,10 +107,10 @@ Item {
id: borderBlink
property color blinkColor: "red"
function tapFeedback(button) {
+ stop();
blinkColor = buttonToBlinkColor(button);
start();
}
- loops: 3
ScriptAction { script: rect.border.color = borderBlink.blinkColor }
PauseAnimation { duration: 100 }
ScriptAction { script: rect.border.color = "transparent" }
@@ -145,6 +155,18 @@ Item {
}
Text {
+ text: "exclusive signals:"
+ }
+ CheckBox {
+ id: singleExclusiveCB
+ text: "single"
+ }
+ CheckBox {
+ id: doubleExclusiveCB
+ text: "double"
+ }
+
+ Text {
text: "gesture policy:"
horizontalAlignment: Text.AlignRight
Layout.row: 1
diff --git a/examples/quick/positioners/CMakeLists.txt b/examples/quick/positioners/CMakeLists.txt
index 213173557e..64f5cae495 100644
--- a/examples/quick/positioners/CMakeLists.txt
+++ b/examples/quick/positioners/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(positioners LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/positioners")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../shared" "shared")
@@ -24,7 +18,6 @@ qt_add_executable(positionersexample
qt_add_qml_module(positionersexample
URI positioners
- AUTO_RESOURCE_PREFIX
QML_FILES
"positioners-attachedproperties.qml"
"positioners-transitions.qml"
@@ -32,16 +25,23 @@ qt_add_qml_module(positionersexample
)
target_link_libraries(positionersexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS positionersexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(positionersexample)
+qt_generate_deploy_qml_app_script(
+ TARGET positionersexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/positioners/doc/src/positioners.qdoc b/examples/quick/positioners/doc/src/positioners.qdoc
index 3fe8e4ef7e..2b1af4d496 100644
--- a/examples/quick/positioners/doc/src/positioners.qdoc
+++ b/examples/quick/positioners/doc/src/positioners.qdoc
@@ -6,6 +6,7 @@
\brief This is a collection of QML Positioner examples.
\image qml-positioners-example.png
\ingroup qtquickexamples
+ \examplecategory {User Interface Components}
\e Positioners is a collection of small QML examples relating to
positioners. Each example is a small QML file emphasizing a particular type
diff --git a/examples/quick/positioners/positioners-attachedproperties.qml b/examples/quick/positioners/positioners-attachedproperties.qml
index 60aabb6b52..18cf7d866d 100644
--- a/examples/quick/positioners/positioners-attachedproperties.qml
+++ b/examples/quick/positioners/positioners-attachedproperties.qml
@@ -17,7 +17,7 @@ Rectangle {
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: 10
- text: hidingRect.visible ? "Hide" : "Show"
+ text: hidingRect.visible ? qsTr("Hide") : qsTr("Show")
onClicked: hidingRect.visible = !hidingRect.visible
}
@@ -42,9 +42,9 @@ Rectangle {
anchors.left: parent.right
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
- text: "Index: " + parent.Positioner.index
- + (parent.Positioner.isFirstItem ? " (First)" : "")
- + (parent.Positioner.isLastItem ? " (Last)" : "")
+ text: qsTr("Index: %1%2%3").arg(parent.Positioner.index)
+ .arg(parent.Positioner.isFirstItem ? qsTr(" (First)") : "")
+ .arg(parent.Positioner.isLastItem ? qsTr(" (Last)") : "")
}
// When mouse is clicked, display the values of the positioner
@@ -65,9 +65,9 @@ Rectangle {
anchors.left: parent.right
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
- text: "Index: " + parent.Positioner.index
- + (parent.Positioner.isFirstItem ? " (First)" : "")
- + (parent.Positioner.isLastItem ? " (Last)" : "")
+ text: qsTr("Index: %1%2%3").arg(parent.Positioner.index)
+ .arg(parent.Positioner.isFirstItem ? qsTr(" (First)") : "")
+ .arg(parent.Positioner.isLastItem ? qsTr(" (Last)") : "")
}
// When mouse is clicked, display the values of the positioner
@@ -87,9 +87,9 @@ Rectangle {
anchors.left: parent.right
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
- text: "Index: " + parent.Positioner.index
- + (parent.Positioner.isFirstItem ? " (First)" : "")
- + (parent.Positioner.isLastItem ? " (Last)" : "")
+ text: qsTr("Index: %1%2%3").arg(parent.Positioner.index)
+ .arg(parent.Positioner.isFirstItem ? qsTr(" (First)") : "")
+ .arg(parent.Positioner.isLastItem ? qsTr(" (Last)") : "")
}
// When mouse is clicked, display the values of the positioner
@@ -111,18 +111,18 @@ Rectangle {
anchors.left: parent.right
anchors.leftMargin: 20
anchors.verticalCenter: parent.verticalCenter
- text: "Index: " + parent.Positioner.index
- + (parent.Positioner.isFirstItem ? " (First)" : "")
- + (parent.Positioner.isLastItem ? " (Last)" : "")
+ text: qsTr("Index: %1%2%3").arg(parent.Positioner.index)
+ .arg(parent.Positioner.isFirstItem ? qsTr(" (First)") : "")
+ .arg(parent.Positioner.isLastItem ? qsTr(" (Last)") : "")
}
}
// Print the index of the child item in the positioner and convenience
// properties showing if it's the first or last item.
function showInfo(positioner) {
- console.log("Item Index = " + positioner.index)
- console.log(" isFirstItem = " + positioner.isFirstItem)
- console.log(" isLastItem = " + positioner.isLastItem)
+ console.log(qsTr("Item Index = ") + positioner.index)
+ console.log(qsTr(" isFirstItem = ") + positioner.isFirstItem)
+ console.log(qsTr(" isLastItem = ") + positioner.isLastItem)
}
}
}
diff --git a/examples/quick/positioners/positioners-transitions.qml b/examples/quick/positioners/positioners-transitions.qml
index 4507080182..44eb2ea625 100644
--- a/examples/quick/positioners/positioners-transitions.qml
+++ b/examples/quick/positioners/positioners-transitions.qml
@@ -19,7 +19,7 @@ Item {
interval: 2000
running: true
repeat: true
- onTriggered: page.effectiveOpacity = (page.effectiveOpacity == 1.0 ? 0.0 : 1.0);
+ onTriggered: page.effectiveOpacity = (page.effectiveOpacity == 1.0 ? 0.0 : 1.0)
}
Column {
@@ -32,41 +32,73 @@ Item {
spacing: page.elementSpacing
populate: Transition {
- NumberAnimation { properties: "x,y"; from: 200; duration: 100; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x,y"
+ from: 200
+ duration: 100
+ easing.type: Easing.OutBounce
+ }
}
add: Transition {
- NumberAnimation { properties: "y"; easing.type: Easing.OutQuad }
+ NumberAnimation {
+ properties: "y"
+ easing.type: Easing.OutQuad
+ }
}
move: Transition {
- NumberAnimation { properties: "y"; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "y"
+ easing.type: Easing.OutBounce
+ }
}
- Rectangle { color: "#80c342"; width: page.bigSize; height: page.smallSize }
+ Rectangle {
+ color: "#80c342"
+ width: page.bigSize
+ height: page.smallSize
+ }
Rectangle {
id: greenV1
+
visible: opacity != 0
- width: page.bigSize; height: page.smallSize
+ width: page.bigSize
+ height: page.smallSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#14aaff"; width: page.bigSize; height: page.smallSize }
+ Rectangle {
+ color: "#14aaff"
+ width: page.bigSize
+ height: page.smallSize
+ }
Rectangle {
id: greenV2
+
visible: opacity != 0
- width: page.bigSize; height: page.smallSize
+ width: page.bigSize
+ height: page.smallSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#6400aa"; width: page.bigSize; height: page.smallSize }
- Rectangle { color: "#80c342"; width: page.bigSize; height: page.smallSize }
+ Rectangle {
+ color: "#6400aa"
+ width: page.bigSize
+ height: page.smallSize
+ }
+
+ Rectangle {
+ color: "#80c342"
+ width: page.bigSize
+ height: page.smallSize
+ }
}
Row {
@@ -79,41 +111,73 @@ Item {
spacing: page.elementSpacing
populate: Transition {
- NumberAnimation { properties: "x,y"; from: 200; duration: 100; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x,y"
+ from: 200
+ duration: 100
+ easing.type: Easing.OutBounce
+ }
}
add: Transition {
- NumberAnimation { properties: "x"; easing.type: Easing.OutQuad }
+ NumberAnimation {
+ properties: "x"
+ easing.type: Easing.OutQuad
+ }
}
move: Transition {
- NumberAnimation { properties: "x"; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x"
+ easing.type: Easing.OutBounce
+ }
}
- Rectangle { color: "#80c342"; width: page.smallSize; height: page.bigSize }
+ Rectangle {
+ color: "#80c342"
+ width: page.smallSize
+ height: page.bigSize
+ }
Rectangle {
id: blueH1
+
visible: opacity != 0
- width: page.smallSize; height: page.bigSize
+ width: page.smallSize
+ height: page.bigSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#14aaff"; width: page.smallSize; height: page.bigSize }
+ Rectangle {
+ color: "#14aaff"
+ width: page.smallSize
+ height: page.bigSize
+ }
Rectangle {
id: greenH2
+
visible: opacity != 0
- width: page.smallSize; height: page.bigSize
+ width: page.smallSize
+ height: page.bigSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#6400aa"; width: page.smallSize; height: page.bigSize }
- Rectangle { color: "#80c342"; width: page.smallSize; height: page.bigSize }
+ Rectangle {
+ color: "#6400aa"
+ width: page.smallSize
+ height: page.bigSize
+ }
+
+ Rectangle {
+ color: "#80c342"
+ width: page.smallSize
+ height: page.bigSize
+ }
}
Grid {
@@ -124,54 +188,97 @@ Item {
spacing: page.elementSpacing
populate: Transition {
- NumberAnimation { properties: "x,y"; from: 200; duration: 100; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x,y"
+ from: 200
+ duration: 100
+ easing.type: Easing.OutBounce
+ }
}
add: Transition {
- NumberAnimation { properties: "x,y"; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x,y"
+ easing.type: Easing.OutBounce
+ }
}
move: Transition {
- NumberAnimation { properties: "x,y"; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x,y"
+ easing.type: Easing.OutBounce
+ }
}
- Rectangle { color: "#80c342"; width: page.smallSize; height: page.smallSize }
+ Rectangle {
+ color: "#80c342"
+ width: page.smallSize
+ height: page.smallSize
+ }
Rectangle {
id: greenG1
+
visible: opacity != 0
- width: page.smallSize; height: page.smallSize
+ width: page.smallSize
+ height: page.smallSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#14aaff"; width: page.smallSize; height: page.smallSize }
+ Rectangle {
+ color: "#14aaff"
+ width: page.smallSize
+ height: page.smallSize
+ }
Rectangle {
id: greenG2
+
visible: opacity != 0
- width: page.smallSize; height:page. smallSize
+ width: page.smallSize
+ height:page. smallSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#6400aa"; width: page.smallSize; height: page.smallSize }
+ Rectangle {
+ color: "#6400aa"
+ width: page.smallSize
+ height: page.smallSize
+ }
Rectangle {
id: greenG3
+
visible: opacity != 0
- width: page.smallSize; height: page.smallSize
+ width: page.smallSize
+ height: page.smallSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#80c342"; width: page.smallSize; height: page.smallSize }
- Rectangle { color: "#14aaff"; width: page.smallSize; height: page.smallSize }
- Rectangle { color: "#6400aa"; width: page.smallSize; height: page.smallSize }
+ Rectangle {
+ color: "#80c342"
+ width: page.smallSize
+ height: page.smallSize
+ }
+
+ Rectangle {
+ color: "#14aaff"
+ width: page.smallSize
+ height: page.smallSize
+ }
+
+ Rectangle {
+ color: "#6400aa"
+ width: page.smallSize
+ height: page.smallSize
+ }
}
Flow {
@@ -185,58 +292,91 @@ Item {
//! [move]
move: Transition {
- NumberAnimation { properties: "x,y"; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x,y"
+ easing.type: Easing.OutBounce
+ }
}
//! [move]
//! [add]
add: Transition {
- NumberAnimation { properties: "x,y"; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x,y"
+ easing.type: Easing.OutBounce
+ }
}
//! [add]
//! [populate]
populate: Transition {
- NumberAnimation { properties: "x,y"; from: 200; duration: 100; easing.type: Easing.OutBounce }
+ NumberAnimation {
+ properties: "x,y"
+ from: 200
+ duration: 100
+ easing.type: Easing.OutBounce
+ }
}
//! [populate]
- Rectangle { color: "#80c342"; width: page.smallSize; height: page.smallSize }
+ Rectangle {
+ color: "#80c342"
+ width: page.smallSize
+ height: page.smallSize
+ }
Rectangle {
id: greenF1
+
visible: opacity != 0
- width: 0.6 * page.bigSize; height: page.smallSize
+ width: 0.6 * page.bigSize
+ height: page.smallSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#14aaff"; width: 0.3 * page.bigSize; height: page.smallSize }
+ Rectangle {
+ color: "#14aaff"
+ width: 0.3 * page.bigSize
+ height: page.smallSize
+ }
Rectangle {
id: greenF2
+
visible: opacity != 0
- width: 0.6 * page.bigSize; height: page.smallSize
+ width: 0.6 * page.bigSize
+ height: page.smallSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#6400aa"; width: page.smallSize; height: page.smallSize }
+ Rectangle {
+ color: "#6400aa"
+ width: page.smallSize
+ height: page.smallSize
+ }
Rectangle {
id: greenF3
+
visible: opacity != 0
- width: 0.4 * page.bigSize; height: page.smallSize
+ width: 0.4 * page.bigSize
+ height: page.smallSize
color: "#006325"
border.color: "transparent"
Behavior on opacity { NumberAnimation {} }
opacity: page.effectiveOpacity
}
- Rectangle { color: "#80c342"; width: 0.8 * page.bigSize; height: page.smallSize }
+ Rectangle {
+ color: "#80c342"
+ width: 0.8 * page.bigSize
+ height: page.smallSize
+ }
}
}
diff --git a/examples/quick/positioners/positioners.qml b/examples/quick/positioners/positioners.qml
index 645979b598..d166135441 100644
--- a/examples/quick/positioners/positioners.qml
+++ b/examples/quick/positioners/positioners.qml
@@ -11,8 +11,8 @@ Item {
id: ll
anchors.fill: parent
Component.onCompleted: {
- addExample("Transitions", "Fluidly shows and hides elements", Qt.resolvedUrl("positioners-transitions.qml"));
- addExample("Attached Properties", "Knows where it is in the positioner", Qt.resolvedUrl("positioners-attachedproperties.qml"));
+ addExample(qsTr("Transitions"), qsTr("Fluidly shows and hides elements"), Qt.resolvedUrl("positioners-transitions.qml"))
+ addExample(qsTr("Attached Properties"), qsTr("Knows where it is in the positioner"), Qt.resolvedUrl("positioners-attachedproperties.qml"))
}
}
}
diff --git a/examples/quick/quick-accessibility/CMakeLists.txt b/examples/quick/quick-accessibility/CMakeLists.txt
index 1ddb70eef9..1501537b7b 100644
--- a/examples/quick/quick-accessibility/CMakeLists.txt
+++ b/examples/quick/quick-accessibility/CMakeLists.txt
@@ -1,20 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(quick-accessibility LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/quick-accessibility")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(quick-accessibility
WIN32
@@ -23,15 +17,14 @@ qt_add_executable(quick-accessibility
)
target_link_libraries(quick-accessibility PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(quick-accessibility
URI accessibility
- AUTO_RESOURCE_PREFIX
QML_FILES
"accessibility.qml"
"Button.qml"
@@ -40,7 +33,16 @@ qt_add_qml_module(quick-accessibility
)
install(TARGETS quick-accessibility
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET quick-accessibility
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/quick-accessibility/doc/src/accessibility.qdoc b/examples/quick/quick-accessibility/doc/src/accessibility.qdoc
index f4b435dc47..c6951cedd1 100644
--- a/examples/quick/quick-accessibility/doc/src/accessibility.qdoc
+++ b/examples/quick/quick-accessibility/doc/src/accessibility.qdoc
@@ -5,6 +5,7 @@
\example quick-accessibility
\brief This example demonstrates the implementation of accessible buttons.
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e Accessibility demonstrates QML types that are augmented with meta-data
for accessibility systems. For more information, visit the
diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro
index 159b0082a3..b9fb3b48d1 100644
--- a/examples/quick/quick.pro
+++ b/examples/quick/quick.pro
@@ -2,33 +2,30 @@ TEMPLATE = subdirs
SUBDIRS = quick-accessibility \
animation \
draganddrop \
- externaldraganddrop \
canvas \
imageelements \
keyinteraction \
layouts \
+ responsivelayouts \
localstorage \
models \
views \
tableview \
mousearea \
+ pointerhandlers \
positioners \
- righttoleft \
scenegraph \
shadereffects \
text \
- threading \
- touchinteraction \
tutorials \
customitems \
imageprovider \
imageresponseprovider \
window \
particles \
- delegatechooser \
- shapes \
itemvariablerefreshrate \
- multieffect
+ multieffect \
+ quickshapes
#OpenGL Support Required
qtConfig(opengl(es1|es2)?) {
diff --git a/examples/quick/quickshapes/CMakeLists.txt b/examples/quick/quickshapes/CMakeLists.txt
new file mode 100644
index 0000000000..e739c3dc89
--- /dev/null
+++ b/examples/quick/quickshapes/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+qt_internal_add_example(shapes)
+qt_internal_add_example(weatherforecast)
+
+# qt_examples_build_end() misses at least some of these due to some
+# source subdirectories being added multiple times. See QTBUG-96159.
+set(reused_dir_targets shapes_shared weatherforecast_shared)
+
+foreach(target IN LISTS reused_dir_targets)
+ if(TARGET ${target})
+ qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS moc rcc)
+ if(TARGET Qt6::Widgets)
+ qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS uic)
+ endif()
+ endif()
+endforeach()
diff --git a/examples/quick/quickshapes/quickshapes.pro b/examples/quick/quickshapes/quickshapes.pro
new file mode 100644
index 0000000000..80465eb81d
--- /dev/null
+++ b/examples/quick/quickshapes/quickshapes.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS = shapes weatherforecast
diff --git a/examples/quick/quickshapes/shapes/CMakeLists.txt b/examples/quick/quickshapes/shapes/CMakeLists.txt
new file mode 100644
index 0000000000..5036f1a37e
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/CMakeLists.txt
@@ -0,0 +1,70 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(shapes LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+add_subdirectory("../../shared" "shared")
+
+qt_add_executable(shapesexample
+ WIN32
+ MACOSX_BUNDLE
+ main.cpp
+)
+
+target_link_libraries(shapesexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+add_dependencies(shapesexample shapes_shared)
+
+qt_add_qml_module(shapesexample
+ URI shapes
+ QML_FILES
+ "clippedtigers.qml"
+ "interactive.qml"
+ "cubicCurve.qml"
+ "ellipticalArcs.qml"
+ "gradientSpreadModes.qml"
+ "arcDirection.qml"
+ "largeOrSmallArc.qml"
+ "arcRotation.qml"
+ "tigerLoader.qml"
+ "text.qml"
+ "strokeOrFill.qml"
+ "dashPattern.qml"
+ "linearGradient.qml"
+ "radialGradient.qml"
+ "fillRules.qml"
+ "joinStyles.qml"
+ "capStyles.qml"
+ "quadraticCurve.qml"
+ "main.qml"
+ "sampling.qml"
+ "shapegallery.qml"
+ "tapableTriangle.qml"
+ "tiger.qml"
+ "zoomtiger.qml"
+)
+
+install(TARGETS shapesexample
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET shapesexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/quickshapes/shapes/arcDirection.qml b/examples/quick/quickshapes/shapes/arcDirection.qml
new file mode 100644
index 0000000000..60ec181380
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/arcDirection.qml
@@ -0,0 +1,59 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ border.color: "gray"
+
+ Repeater {
+ model: 2
+ delegate: Shape {
+ id: delegate
+
+ required property int index
+
+ anchors.fill: parent
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: delegate.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 4
+ startY: 4
+ PathArc {
+ id: arc
+ x: 96
+ y: 96
+ radiusX: 100
+ radiusY: 100
+ direction: delegate.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise
+ }
+ }
+ }
+ }
+ }
+
+ Column {
+ anchors.right: parent.right
+ Text {
+ text: qsTr("Clockwise (sweep 1)")
+ color: "red"
+ }
+ Text {
+ text: qsTr("Counter clockwise (sweep 0)")
+ color: "blue"
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/arcRotation.qml b/examples/quick/quickshapes/shapes/arcRotation.qml
new file mode 100644
index 0000000000..2433280698
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/arcRotation.qml
@@ -0,0 +1,82 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Repeater {
+ model: 2
+ delegate: Shape {
+ id: delegate1
+
+ required property int index
+
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: delegate1.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 50
+ startY: 100
+ PathArc {
+ x: 150
+ y: 100
+ radiusX: 50
+ radiusY: 20
+ xAxisRotation: delegate1.index === 0 ? 0 : 45
+ }
+ }
+ }
+ }
+
+ Repeater {
+ model: 2
+ delegate: Shape {
+ id: delegate2
+
+ required property int index
+
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: delegate2.index === 0 ? "red" : "blue"
+
+ startX: 50
+ startY: 100
+ PathArc {
+ x: 150
+ y: 100
+ radiusX: 50
+ radiusY: 20
+ xAxisRotation: delegate2.index === 0 ? 0 : 45
+ direction: PathArc.Counterclockwise
+ }
+ }
+ }
+ }
+
+ Column {
+ anchors.right: parent.right
+ Text {
+ text: qsTr("0 degrees")
+ color: "red"
+ }
+ Text {
+ text: qsTr("45 degrees")
+ color: "blue"
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/capStyles.qml b/examples/quick/quickshapes/shapes/capStyles.qml
new file mode 100644
index 0000000000..7fc7a4e2b9
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/capStyles.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Shape {
+ anchors.centerIn: parent
+ width: 200
+ height: 100
+
+ ShapePath {
+ id: capTest
+ strokeColor: "green"
+ strokeWidth: 20
+ fillColor: "transparent"
+
+ property int capStyleIdx: 0
+ readonly property variant styles: [ ShapePath.FlatCap, ShapePath.SquareCap, ShapePath.RoundCap ]
+ readonly property variant styleTexts: [ qsTr("FlatCap"), qsTr("SquareCap"), qsTr("RoundCap") ]
+
+ capStyle: styles[capStyleIdx]
+
+ startX: 40
+ startY: 30
+ PathQuad {
+ x: 50
+ y: 80
+ controlX: 0
+ controlY: 80
+ }
+ PathLine {
+ x: 150
+ y: 80
+ }
+ PathQuad {
+ x: 160
+ y: 30
+ controlX: 200
+ controlY: 80
+ }
+ }
+ }
+
+ Timer {
+ interval: 1000
+ repeat: true
+ running: true
+ onTriggered: capTest.capStyleIdx = (capTest.capStyleIdx + 1) % capTest.styles.length
+ }
+
+ Text {
+ id: txt
+ anchors.right: parent.right
+ text: capTest.styleTexts[capTest.capStyleIdx]
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/clippedtigers.qml b/examples/quick/quickshapes/shapes/clippedtigers.qml
new file mode 100644
index 0000000000..8885f60dd7
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/clippedtigers.qml
@@ -0,0 +1,110 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ id: root
+ width: 1024
+ height: 768
+
+ readonly property color col: "lightsteelblue"
+ gradient: Gradient {
+ GradientStop {
+ position: 0.0
+ color: Qt.tint(root.col, "#20FFFFFF")
+ }
+ GradientStop {
+ position: 0.1
+ color: Qt.tint(root.col, "#20AAAAAA")
+ }
+ GradientStop {
+ position: 0.9
+ color: Qt.tint(root.col, "#20666666")
+ }
+ GradientStop {
+ position: 1.0
+ color: Qt.tint(root.col, "#20000000")
+ }
+ }
+
+ Rectangle {
+ id: scissorRect
+ width: 200
+ height: 200
+ x: 150
+ readonly property real centerY: parent.height / 2 - height / 2
+ property real dy: 0
+ y: centerY + dy
+ clip: true
+
+ Loader {
+ id: loader1
+ width: parent.width
+ height: parent.height
+ y: 25 - scissorRect.dy
+ source: "tiger.qml"
+ asynchronous: true
+ visible: status === Loader.Ready
+ }
+
+ SequentialAnimation {
+ loops: Animation.Infinite
+ running: loader1.status === Loader.Ready && (loader1.item as Shape)?.status === Shape.Ready
+ NumberAnimation {
+ target: scissorRect
+ property: "dy"
+ from: 0
+ to: -scissorRect.centerY
+ duration: 2000
+ }
+ NumberAnimation {
+ target: scissorRect
+ property: "dy"
+ from: -scissorRect.centerY
+ to: scissorRect.centerY
+ duration: 4000
+ }
+ NumberAnimation {
+ target: scissorRect
+ property: "dy"
+ from: scissorRect.centerY
+ to: 0
+ duration: 2000
+ }
+ }
+ }
+
+ // With a more complex transformation (like rotation), stenciling is used
+ // instead of scissoring, this is more expensive. It may also trigger a
+ // slower code path for Shapes, depending on the path rendering backend
+ // in use, and may affect rendering quality as well.
+ Rectangle {
+ id: stencilRect
+ width: 300
+ height: 200
+ anchors {
+ right: parent.right
+ rightMargin: 100
+ verticalCenter: parent.verticalCenter
+ }
+ clip: true // NB! still clips to bounding rect (not shape)
+
+ Loader {
+ id: loader2
+ width: parent.width
+ height: parent.height
+ source: "tiger.qml"
+ asynchronous: true
+ visible: status === Loader.Ready
+ }
+
+ NumberAnimation on rotation {
+ from: 0
+ to: 360
+ duration: 5000
+ loops: Animation.Infinite
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/cubicCurve.qml b/examples/quick/quickshapes/shapes/cubicCurve.qml
new file mode 100644
index 0000000000..3fa4ed6f4b
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/cubicCurve.qml
@@ -0,0 +1,164 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Item {
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+
+ Shape {
+ id: shape
+ anchors.fill: parent
+
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "black"
+ fillGradient: ConicalGradient {
+ id: conGrad
+ centerX: 100
+ centerY: 75
+ NumberAnimation on angle {
+ from: 0
+ to: 360
+ duration: 10000
+ loops: Animation.Infinite
+ }
+ GradientStop {
+ position: 0
+ color: "#00000000"
+ }
+ GradientStop {
+ position: 0.10
+ color: "#ffe0cc73"
+ }
+ GradientStop {
+ position: 0.17
+ color: "#ffc6a006"
+ }
+ GradientStop {
+ position: 0.46
+ color: "#ff600659"
+ }
+ GradientStop {
+ position: 0.72
+ color: "#ff0680ac"
+ }
+ GradientStop {
+ position: 0.92
+ color: "#ffb9d9e6"
+ }
+ GradientStop {
+ position: 1.00
+ color: "#00000000"
+ }
+ }
+
+ startX: 50
+ startY: 100
+ PathCubic {
+ x: 150
+ y: 100
+ control1X: cp1.x
+ control1Y: cp1.y
+ control2X: cp2.x
+ control2Y: cp2.y
+ }
+ }
+ }
+
+ Rectangle {
+ id: cp1
+ color: "red"
+ width: 10
+ height: 10
+ SequentialAnimation {
+ loops: Animation.Infinite
+ running: true
+ NumberAnimation {
+ target: cp1
+ property: "x"
+ from: 0
+ to: shape.width - cp1.width
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp1
+ property: "x"
+ from: shape.width - cp1.width
+ to: 0
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp1
+ property: "y"
+ from: 0
+ to: shape.height - cp1.height
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp1
+ property: "y"
+ from: shape.height - cp1.height
+ to: 0
+ duration: 5000
+ }
+ }
+ }
+
+ Rectangle {
+ id: cp2
+ color: "blue"
+ width: 10
+ height: 10
+ x: shape.width - width
+ SequentialAnimation {
+ loops: Animation.Infinite
+ running: true
+ NumberAnimation {
+ target: cp2
+ property: "y"
+ from: 0
+ to: shape.height - cp2.height
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp2
+ property: "y"
+ from: shape.height - cp2.height
+ to: 0
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp2
+ property: "x"
+ from: shape.width - cp2.width
+ to: 0
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp2
+ property: "x"
+ from: 0
+ to: shape.width - cp2.width
+ duration: 5000
+ }
+ }
+ }
+ }
+
+ Text {
+ anchors {
+ right: parent.right
+ top: parent.top
+ }
+ text: qsTr("Conical gradient angle: ") + Math.round(conGrad.angle)
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/dashPattern.qml b/examples/quick/quickshapes/shapes/dashPattern.qml
new file mode 100644
index 0000000000..742f8622e9
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/dashPattern.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+ Shape {
+ id: shape
+ anchors.fill: parent
+
+ ShapePath {
+ id: p
+ strokeWidth: 5
+ strokeColor: "blue"
+ strokeStyle: ShapePath.DashLine
+ dashPattern: [ 1, 4, 4, 4 ]
+ fillColor: "lightBlue"
+
+ property real xr: 70
+ property real yr: 30
+ startX: shape.width / 2 - xr
+ startY: shape.height / 2 - yr
+ PathArc {
+ x: shape.width / 2 + p.xr
+ y: shape.height / 2 + p.yr
+ radiusX: p.xr
+ radiusY: p.yr
+ useLargeArc: true
+ }
+ PathArc {
+ x: shape.width / 2 - p.xr
+ y: shape.height / 2 - p.yr
+ radiusX: p.xr
+ radiusY: p.yr
+ useLargeArc: true
+ }
+ }
+ }
+}
diff --git a/examples/quick/shapes/doc/images/qml-shapes-example.png b/examples/quick/quickshapes/shapes/doc/images/qml-shapes-example.png
index 055fc244ea..055fc244ea 100644
--- a/examples/quick/shapes/doc/images/qml-shapes-example.png
+++ b/examples/quick/quickshapes/shapes/doc/images/qml-shapes-example.png
Binary files differ
diff --git a/examples/quick/quickshapes/shapes/doc/src/shapes.qdoc b/examples/quick/quickshapes/shapes/doc/src/shapes.qdoc
new file mode 100644
index 0000000000..eb9f5da52d
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/doc/src/shapes.qdoc
@@ -0,0 +1,16 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \title Qt Quick Examples - Shapes
+ \example quickshapes/shapes
+ \image qml-shapes-example.png
+ \brief A Qt Quick example demonstrating the use of shape items.
+ \ingroup qtquickexamples
+ \examplecategory {Graphics}
+
+ This example demonstrates the usage of the \l Shape type in Qt Quick. Shapes
+ allow efficiently rendering stroked and filled lines, curves, and arcs in Qt
+ Quick scenes.
+
+ \include examples-run.qdocinc
+*/
diff --git a/examples/quick/quickshapes/shapes/ellipticalArcs.qml b/examples/quick/quickshapes/shapes/ellipticalArcs.qml
new file mode 100644
index 0000000000..d2b65cb4c1
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/ellipticalArcs.qml
@@ -0,0 +1,93 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+ Shape {
+ id: shape
+ width: 220
+ height: 200
+ anchors.centerIn: parent
+
+ ShapePath {
+ fillGradient: LinearGradient {
+ y2: shape.height
+ GradientStop {
+ position: 0
+ color: "yellow"
+ }
+ GradientStop {
+ position: 1
+ color: "green"
+ }
+ }
+
+ startX: 10
+ startY: 100
+ PathArc {
+ relativeX: 50
+ y: 100
+ radiusX: 25
+ radiusY: 25
+ }
+ PathArc {
+ relativeX: 50
+ y: 100
+ radiusX: 25
+ radiusY: 35
+ }
+ PathArc {
+ relativeX: 50
+ y: 100
+ radiusX: 25
+ radiusY: 60
+ }
+ PathArc {
+ relativeX: 50
+ y: 100
+ radiusX: 50
+ radiusY: 120
+ }
+ }
+ }
+
+ Shape {
+ width: 120
+ height: 130
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+
+ scale: 0.5
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "darkBlue"
+ strokeWidth: 20
+ capStyle: ShapePath.RoundCap
+
+ PathAngleArc {
+ centerX: 65
+ centerY: 95
+ radiusX: 45
+ radiusY: 45
+ startAngle: -180
+ SequentialAnimation on sweepAngle {
+ loops: Animation.Infinite
+ NumberAnimation {
+ to: 360
+ duration: 2000
+ }
+ NumberAnimation {
+ to: 0
+ duration: 2000
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/fillRules.qml b/examples/quick/quickshapes/shapes/fillRules.qml
new file mode 100644
index 0000000000..1e96af94e7
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/fillRules.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+ Shape {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ ShapePath {
+ id: star
+ strokeColor: "blue"
+ fillColor: "magenta"
+ strokeWidth: 2
+ PathMove {
+ x: 90
+ y: 50
+ }
+ PathLine {
+ x: 50 + 40 * Math.cos(0.8 * 1 * Math.PI)
+ y: 50 + 40 * Math.sin(0.8 * 1 * Math.PI)
+ }
+ PathLine {
+ x: 50 + 40 * Math.cos(0.8 * 2 * Math.PI)
+ y: 50 + 40 * Math.sin(0.8 * 2 * Math.PI)
+ }
+ PathLine {
+ x: 50 + 40 * Math.cos(0.8 * 3 * Math.PI)
+ y: 50 + 40 * Math.sin(0.8 * 3 * Math.PI)
+ }
+ PathLine {
+ x: 50 + 40 * Math.cos(0.8 * 4 * Math.PI)
+ y: 50 + 40 * Math.sin(0.8 * 4 * Math.PI)
+ }
+ PathLine {
+ x: 90
+ y: 50
+ }
+ }
+ NumberAnimation on rotation {
+ from: 0
+ to: 360
+ duration: 5000
+ loops: Animation.Infinite
+ }
+ }
+ Timer {
+ interval: 2000
+ onTriggered: star.fillRule = (star.fillRule === ShapePath.OddEvenFill ? ShapePath.WindingFill : ShapePath.OddEvenFill)
+ repeat: true
+ running: true
+ }
+ Text {
+ anchors.right: parent.right
+ text: star.fillRule === ShapePath.OddEvenFill ? qsTr("OddEvenFill") : qsTr("WindingFill")
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/gradientSpreadModes.qml b/examples/quick/quickshapes/shapes/gradientSpreadModes.qml
new file mode 100644
index 0000000000..ba2dc2b734
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/gradientSpreadModes.qml
@@ -0,0 +1,102 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+ Rectangle {
+ anchors.centerIn: parent
+ width: 200
+ height: 200
+ border.color: "black"
+
+ Shape {
+ anchors.fill: parent
+
+ ShapePath {
+ strokeColor: "transparent"
+
+ fillGradient: LinearGradient {
+ id: grad
+ y1: 50
+ y2: 150
+ GradientStop {
+ position: 0
+ color: "black"
+ }
+ GradientStop {
+ position: 1
+ color: "red"
+ }
+ }
+
+ startX: 10
+ startY: 10
+ PathLine {
+ relativeX: 180
+ relativeY: 0
+ }
+ PathLine {
+ relativeX: 0
+ relativeY: 180
+ }
+ PathLine {
+ relativeX: -180
+ relativeY: 0
+ }
+ PathLine {
+ relativeX: 0
+ relativeY: -180
+ }
+ }
+ }
+
+ Timer {
+ id: spreadTimer
+ interval: 3000
+ running: true
+ repeat: true
+ readonly property variant spreads: [ ShapeGradient.PadSpread, ShapeGradient.RepeatSpread, ShapeGradient.ReflectSpread ]
+ readonly property variant spreadTexts: [ qsTr("PadSpread"), qsTr("RepeatSpread"), qsTr("ReflectSpread") ]
+ property int spreadIdx: 0
+ onTriggered: function() {
+ spreadIdx = (spreadIdx + 1) % spreads.length
+ grad.spread = spreads[spreadIdx]
+ }
+ }
+
+ Shape {
+ anchors.fill: parent
+ ShapePath {
+ strokeColor: "gray"
+ strokeWidth: 2
+ fillColor: "transparent"
+ PathMove {
+ x: 0
+ y: 50
+ }
+ PathLine {
+ relativeX: 200
+ relativeY: 0
+ }
+ PathMove {
+ x: 0
+ y: 150
+ }
+ PathLine {
+ relativeX: 200
+ relativeY: 0
+ }
+ }
+ }
+ }
+
+ Text {
+ anchors.right: parent.right
+ text: spreadTimer.spreadTexts[spreadTimer.spreadIdx]
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/interactive.qml b/examples/quick/quickshapes/shapes/interactive.qml
new file mode 100644
index 0000000000..0525f4eed3
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/interactive.qml
@@ -0,0 +1,247 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Shapes
+
+pragma ComponentBehavior: Bound
+
+Rectangle {
+ id: root
+ width: 1024
+ height: 768
+ color: palette.window
+
+ RowLayout {
+ id: topRow
+ x: 20
+ y: 10
+ spacing: 20
+
+ ButtonGroup {
+ id: toolButtons
+ buttons: drawingTools.children
+ }
+
+ RowLayout {
+ id: drawingTools
+ ToolButton {
+ text: qsTr("Line")
+ checkable: true
+ checked: true
+
+ property Component shapeType: Component {
+ ShapePath {
+ id: lineShapePath
+ strokeColor: root.palette.windowText
+ strokeWidth: widthSlider.value
+ fillColor: "transparent"
+ PathLine {
+ id: pathSegment
+ x: lineShapePath.startX + 1
+ y: lineShapePath.startY + 1
+ }
+ function finishCreation() {
+ createStartEndHandles(this, pathSegment);
+ }
+ }
+ }
+ }
+ ToolButton {
+ text: qsTr("Quadratic")
+ checkable: true
+
+ property Component shapeType: Component {
+ ShapePath {
+ id: quadShapePath
+ strokeColor: strokeSwitch.checked ? root.palette.windowText : "transparent"
+ strokeWidth: widthSlider.value
+ fillColor: fillSwitch.checked ? "green" : "transparent"
+ PathQuad {
+ id: pathSegment
+ x: quadShapePath.startX + 1
+ y: quadShapePath.startY + 1
+ controlX: quadShapePath.startX + 50
+ controlY: quadShapePath.startY + 50
+ }
+ function finishCreation() {
+ createStartEndHandles(this, pathSegment);
+ pointDragHandle.createObject(canvas, {
+ idleColor: "blue",
+ target: pathSegment, xprop: "controlX", yprop: "controlY"
+ });
+ }
+ }
+ }
+ }
+ ToolButton {
+ text: qsTr("Cubic")
+ checkable: true
+
+ property Component shapeType: Component {
+ ShapePath {
+ id: cubicShapePath
+ strokeColor: strokeSwitch.checked ? root.palette.windowText : "transparent"
+ strokeWidth: widthSlider.value
+ fillColor: fillSwitch.checked ? "green" : "transparent"
+ PathCubic {
+ id: pathSegment
+ x: cubicShapePath.startX + 1
+ y: cubicShapePath.startY + 1
+ control1X: cubicShapePath.startX + 50;
+ control1Y: cubicShapePath.startY + 50;
+ control2X: cubicShapePath.startX + 150;
+ control2Y: cubicShapePath.startY + 50;
+ }
+ function finishCreation() {
+ createStartEndHandles(this, pathSegment);
+ pointDragHandle.createObject(canvas, {
+ idleColor: "blue",
+ target: pathSegment, xprop: "control1X", yprop: "control1Y"
+ });
+ pointDragHandle.createObject(canvas, {
+ idleColor: "lightBlue",
+ target: pathSegment, xprop: "control2X", yprop: "control2Y"
+ });
+ }
+ }
+ }
+ }
+ ToolButton {
+ id: modifyButton
+ text: qsTr("Modify")
+ checkable: true
+ onCheckedChanged: {
+ if (checked)
+ showHandlesSwitch.checked = true;
+ }
+ }
+ }
+
+ Label {
+ text: qsTr("Width")
+ }
+ Slider {
+ id: widthSlider
+ from: 1
+ to: 60
+ value: 4
+ }
+
+ Switch {
+ id: showHandlesSwitch
+ text: qsTr("Handles")
+ }
+
+ Switch {
+ id: fillSwitch
+ text: qsTr("Fill")
+ }
+
+ Switch {
+ id: strokeSwitch
+ text: qsTr("Stroke")
+ checked: true
+ }
+ }
+
+ Component {
+ id: pointDragHandle
+
+ Rectangle {
+ id: rect
+ property variant target
+ property string xprop
+ property string yprop
+ property color idleColor: "red"
+
+ width: 20
+ height: width
+ radius: halfWidth
+ visible: showHandlesSwitch.checked
+ color: hh.hovered ? "yellow" : idleColor
+ border.color: "grey"
+ opacity: 0.75
+
+ property real halfWidth: width / 2
+ property bool complete: false
+ Binding {
+ target: rect.target
+ property: rect.xprop
+ value: rect.x + rect.halfWidth
+ when: rect.complete
+ }
+ Binding {
+ target: rect.target
+ property: rect.yprop
+ value: rect.y + rect.halfWidth
+ when: rect.complete
+ }
+
+ DragHandler { }
+
+ HoverHandler {
+ id: hh
+ }
+
+ Component.onCompleted: {
+ x = target[xprop] - halfWidth;
+ y = target[yprop] - halfWidth;
+ complete = true;
+ }
+ }
+ }
+
+ function createStartEndHandles(shapePath, pathSegment) {
+ pointDragHandle.createObject(canvas, {
+ idleColor: "red",
+ target: shapePath, xprop: "startX", yprop: "startY"
+ });
+ pointDragHandle.createObject(canvas, {
+ idleColor: "red",
+ target: pathSegment, xprop: "x", yprop: "y"
+ });
+ }
+
+ Rectangle {
+ id: canvas
+ color: palette.base
+ width: root.width - 40
+ height: root.height - y - 20
+ x: 20
+ anchors.top: topRow.bottom
+ anchors.topMargin: 20
+
+ DragHandler {
+ target: null
+ grabPermissions: DragHandler.TakeOverForbidden
+ property ShapePath activePath: null
+ onActiveChanged: {
+ const tool = toolButtons.checkedButton;
+ if (tool != modifyButton) {
+ if (active) {
+ activePath = tool.shapeType.createObject(root, {
+ startX: centroid.position.x, startY: centroid.position.y
+ });
+ shape.data.push(activePath);
+ } else {
+ activePath.finishCreation();
+ activePath = null;
+ }
+ }
+ }
+ onCentroidChanged: if (activePath) {
+ var pathObj = activePath.pathElements[0];
+ pathObj.x = centroid.position.x;
+ pathObj.y = centroid.position.y;
+ }
+ }
+
+ Shape {
+ id: shape
+ anchors.fill: parent
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/joinStyles.qml b/examples/quick/quickshapes/shapes/joinStyles.qml
new file mode 100644
index 0000000000..127cb50fbe
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/joinStyles.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Shape {
+ width: 120
+ height: 120
+ anchors.centerIn: parent
+
+ ShapePath {
+ id: joinTest
+
+ strokeColor: "black"
+ strokeWidth: 16
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+
+ property int joinStyleIdx: 0
+ readonly property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ]
+ readonly property variant styleTexts: [ qsTr("BevelJoin"), qsTr("MiterJoin"), qsTr("RoundJoin") ]
+
+ joinStyle: styles[joinStyleIdx]
+
+ startX: 30
+ startY: 30
+ PathLine {
+ x: 100
+ y: 100
+ }
+ PathLine {
+ x: 30
+ y: 100
+ }
+ }
+ }
+
+ Timer {
+ interval: 1000
+ repeat: true
+ running: true
+ onTriggered: joinTest.joinStyleIdx = (joinTest.joinStyleIdx + 1) % joinTest.styles.length
+ }
+
+ Text {
+ id: txt
+ anchors.right: parent.right
+ text: joinTest.styleTexts[joinTest.joinStyleIdx]
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/largeOrSmallArc.qml b/examples/quick/quickshapes/shapes/largeOrSmallArc.qml
new file mode 100644
index 0000000000..0d887d162d
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/largeOrSmallArc.qml
@@ -0,0 +1,52 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Repeater {
+ model: 2
+ delegate: Shape {
+ id: delegate
+ required property int index
+
+ anchors.centerIn: parent
+ width: 200
+ height: 200
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: delegate.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 50
+ startY: 100
+ PathArc {
+ x: 100
+ y: 150
+ radiusX: 50
+ radiusY: 50
+ useLargeArc: delegate.index === 1
+ }
+ }
+ }
+ }
+
+ Column {
+ anchors.right: parent.right
+ Text {
+ text: qsTr("Small")
+ color: "red"
+ }
+ Text {
+ text: qsTr("Large")
+ color: "blue"
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/linearGradient.qml b/examples/quick/quickshapes/shapes/linearGradient.qml
new file mode 100644
index 0000000000..cb89be7e6b
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/linearGradient.qml
@@ -0,0 +1,90 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+ Shape {
+ width: 200
+ height: 150
+ anchors.centerIn: parent
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "red"
+ fillGradient: LinearGradient {
+ x1: 20
+ y1: 20
+ x2: 180
+ y2: 130
+ GradientStop {
+ position: 0
+ color: "blue"
+ }
+ GradientStop {
+ position: 0.2
+ color: "green"
+ }
+ GradientStop {
+ position: 0.4
+ color: "red"
+ }
+ GradientStop {
+ position: 0.6
+ color: "yellow"
+ }
+ GradientStop {
+ position: 1
+ color: "cyan"
+ }
+ }
+ fillColor: "blue" // ignored with the gradient set
+ strokeStyle: ShapePath.DashLine
+ dashPattern: [ 1, 4 ]
+ startX: 20
+ startY: 20
+ PathLine {
+ x: 180
+ y: 130
+ }
+ PathLine {
+ x: 20
+ y: 130
+ }
+ PathLine {
+ x: 20
+ y: 20
+ }
+ }
+ transform: Rotation {
+ origin.x: 100
+ origin.y: 50
+ axis {
+ x: 0
+ y: 1
+ z: 0
+ }
+ SequentialAnimation on angle {
+ NumberAnimation {
+ from: 0
+ to: 75
+ duration: 2000
+ }
+ NumberAnimation {
+ from: 75
+ to: -75
+ duration: 4000
+ }
+ NumberAnimation {
+ from: -75
+ to: 0
+ duration: 2000
+ }
+ loops: Animation.Infinite
+ }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/main.cpp b/examples/quick/quickshapes/shapes/main.cpp
new file mode 100644
index 0000000000..f8a895da83
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/main.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include "../../shared/shared.h"
+DECLARATIVE_EXAMPLE_MAIN(shapes/main)
diff --git a/examples/quick/quickshapes/shapes/main.qml b/examples/quick/quickshapes/shapes/main.qml
new file mode 100644
index 0000000000..ddd6e4edf0
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/main.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import shared
+
+Item {
+ width: 1280
+ height: 720
+ LauncherList {
+ anchors.fill: parent
+ Component.onCompleted: {
+ addExample(qsTr("Shape Gallery"), qsTr("Simple path rendering examples"), Qt.resolvedUrl("shapegallery.qml"))
+ addExample(qsTr("Interactive Shape"), qsTr("Dynamic, interactive path rendering examples"), Qt.resolvedUrl("interactive.qml"))
+ addExample(qsTr("Anti-aliasing"), qsTr("Improving quality"), Qt.resolvedUrl("sampling.qml"))
+ addExample(qsTr("Magnify My Tiger!"), qsTr("Path zooming example"), Qt.resolvedUrl("zoomtiger.qml"))
+ addExample(qsTr("Clip My Tiger!"), qsTr("Clip examples, a.k.a. What Not To Do"), Qt.resolvedUrl("clippedtigers.qml"))
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/quadraticCurve.qml b/examples/quick/quickshapes/shapes/quadraticCurve.qml
new file mode 100644
index 0000000000..d626d2693a
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/quadraticCurve.qml
@@ -0,0 +1,57 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Item {
+ width: 200
+ height: 100
+ anchors.centerIn: parent
+
+ Shape {
+ id: shape
+ anchors.fill: parent
+
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "black"
+ fillColor: "transparent"
+
+ startX: 50
+ startY: 50
+ PathQuad {
+ x: 150
+ y: 50
+ controlX: cp.x
+ controlY: cp.y
+ }
+ }
+ }
+
+ Rectangle {
+ id: cp
+ color: "red"
+ width: 10
+ height: 10
+ SequentialAnimation on x {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 0
+ to: shape.width - cp.width
+ duration: 5000
+ }
+ NumberAnimation {
+ from: shape.width - cp.width
+ to: 0
+ duration: 5000
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/radialGradient.qml b/examples/quick/quickshapes/shapes/radialGradient.qml
new file mode 100644
index 0000000000..87db319c9a
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/radialGradient.qml
@@ -0,0 +1,113 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+ Shape {
+ width: 200
+ height: 150
+ anchors.centerIn: parent
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "red"
+ fillGradient: RadialGradient {
+ centerX: 100
+ centerY: 100
+ centerRadius: 100
+ SequentialAnimation on focalRadius {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1
+ to: 20
+ duration: 2000
+ }
+ NumberAnimation {
+ from: 20
+ to: 1
+ duration: 2000
+ }
+ }
+ SequentialAnimation on focalX {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 50
+ to: 150
+ duration: 3000
+ }
+ NumberAnimation {
+ from: 150
+ to: 50
+ duration: 3000
+ }
+ }
+ SequentialAnimation on focalY {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 50
+ to: 150
+ duration: 1000
+ }
+ NumberAnimation {
+ from: 150
+ to: 50
+ duration: 1000
+ }
+ }
+ GradientStop {
+ position: 0
+ color: "#ffffff"
+ }
+ GradientStop {
+ position: 0.11
+ color: "#f9ffa0"
+ }
+ GradientStop {
+ position: 0.13
+ color: "#f9ff99"
+ }
+ GradientStop {
+ position: 0.14
+ color: "#f3ff86"
+ }
+ GradientStop {
+ position: 0.49
+ color: "#93b353"
+ }
+ GradientStop {
+ position: 0.87
+ color: "#264619"
+ }
+ GradientStop {
+ position: 0.96
+ color: "#0c1306"
+ }
+ GradientStop {
+ position: 1
+ color: "#000000"
+ }
+ }
+ fillColor: "blue" // ignored with the gradient set
+ strokeStyle: ShapePath.DashLine
+ dashPattern: [ 1, 4 ]
+ startX: 20
+ startY: 20
+ PathLine {
+ x: 180
+ y: 130
+ }
+ PathLine {
+ x: 20
+ y: 130
+ }
+ PathLine {
+ x: 20
+ y: 20
+ }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/sampling.qml b/examples/quick/quickshapes/shapes/sampling.qml
new file mode 100644
index 0000000000..27515b6722
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/sampling.qml
@@ -0,0 +1,257 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ id: root
+ width: 1024
+ height: 768
+
+ readonly property color col: "lightsteelblue"
+ gradient: Gradient {
+ GradientStop {
+ position: 0.0
+ color: Qt.tint(root.col, "#20FFFFFF")
+ }
+ GradientStop {
+ position: 0.1
+ color: Qt.tint(root.col, "#20AAAAAA")
+ }
+ GradientStop {
+ position: 0.9
+ color: Qt.tint(root.col, "#20666666")
+ }
+ GradientStop {
+ position: 1.0
+ color: Qt.tint(root.col, "#20000000")
+ }
+ }
+
+ Row {
+ anchors {
+ fill: parent
+ margins: 20
+ }
+ spacing: 40
+
+ Column {
+ spacing: 40
+
+ Text {
+ text: qsTr("Original")
+ }
+
+ // A simple Shape without anything special.
+ Rectangle {
+ color: "lightGray"
+ width: 400
+ height: 200
+
+ Shape {
+ x: 30
+ y: 20
+ width: 50
+ height: 50
+ scale: 2
+
+ ShapePath {
+ strokeColor: "green"
+ NumberAnimation on strokeWidth {
+ from: 1
+ to: 20
+ duration: 5000
+ }
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+
+ startX: 40
+ startY: 30
+ PathQuad {
+ x: 50
+ y: 80
+ controlX: 0
+ controlY: 80
+ }
+ PathLine {
+ x: 150
+ y: 80
+ }
+ PathQuad {
+ x: 160
+ y: 30
+ controlX: 200
+ controlY: 80
+ }
+ }
+ }
+ }
+
+ Text {
+ text: qsTr("Supersampling (2x)")
+ }
+
+ // Now let's use 2x supersampling via layers. This way the entire subtree
+ // is rendered into an FBO twice the size and then drawn with linear
+ // filtering. This allows having some level of AA even when there is no
+ // support for multisample framebuffers.
+ Rectangle {
+ id: supersampledItem
+ color: "lightGray"
+ width: 400
+ height: 200
+
+ layer.enabled: true
+ layer.smooth: true
+ layer.textureSize: Qt.size(supersampledItem.width * 2, supersampledItem.height * 2)
+
+ Shape {
+ x: 30
+ y: 20
+ width: 50
+ height: 50
+ scale: 2
+
+ ShapePath {
+ strokeColor: "green"
+ NumberAnimation on strokeWidth {
+ from: 1
+ to: 20
+ duration: 5000
+ }
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+
+ startX: 40
+ startY: 30
+ PathQuad {
+ x: 50
+ y: 80
+ controlX: 0
+ controlY: 80
+ }
+ PathLine {
+ x: 150
+ y: 80
+ }
+ PathQuad {
+ x: 160
+ y: 30
+ controlX: 200
+ controlY: 80
+ }
+ }
+ }
+ }
+ }
+
+ Column {
+ spacing: 40
+
+ Text {
+ text: qsTr("Multisampling (4x)")
+ }
+
+ // Now let's use 4x MSAA, again via layers. This needs support for
+ // multisample renderbuffers and framebuffer blits.
+ Rectangle {
+ color: "lightGray"
+ width: 400
+ height: 200
+
+ layer.enabled: true
+ layer.smooth: true
+ layer.samples: 4
+
+ Shape {
+ x: 30
+ y: 20
+ width: 50
+ height: 50
+ scale: 2
+
+ ShapePath {
+ strokeColor: "green"
+ NumberAnimation on strokeWidth {
+ from: 1
+ to: 20
+ duration: 5000
+ }
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+
+ startX: 40
+ startY: 30
+ PathQuad {
+ x: 50
+ y: 80
+ controlX: 0
+ controlY: 80
+ }
+ PathLine {
+ x: 150
+ y: 80
+ }
+ PathQuad {
+ x: 160
+ y: 30
+ controlX: 200
+ controlY: 80
+ }
+ }
+ }
+ }
+ Text {
+ text: qsTr("CurveRenderer")
+ }
+
+ // Now let's use CurveRenderer with built-in antialiasing support.
+ Rectangle {
+ color: "lightGray"
+ width: 400
+ height: 200
+
+ Shape {
+ x: 30
+ y: 20
+ width: 50
+ height: 50
+ scale: 2
+ preferredRendererType: Shape.CurveRenderer
+ antialiasing: true
+
+ ShapePath {
+ strokeColor: "green"
+ NumberAnimation on strokeWidth {
+ from: 1
+ to: 20
+ duration: 5000
+ }
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+
+ startX: 40
+ startY: 30
+ PathQuad {
+ x: 50
+ y: 80
+ controlX: 0
+ controlY: 80
+ }
+ PathLine {
+ x: 150
+ y: 80
+ }
+ PathQuad {
+ x: 160
+ y: 30
+ controlX: 200
+ controlY: 80
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/shapegallery.qml b/examples/quick/quickshapes/shapes/shapegallery.qml
new file mode 100644
index 0000000000..74059b8208
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/shapegallery.qml
@@ -0,0 +1,184 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+pragma ComponentBehavior: Bound
+
+Rectangle {
+ id: root
+ width: 1024
+ height: 768
+
+ readonly property color col: "lightsteelblue"
+ gradient: Gradient {
+ GradientStop {
+ position: 0.0
+ color: Qt.tint(root.col, "#20FFFFFF")
+ }
+ GradientStop {
+ position: 0.1
+ color: Qt.tint(root.col, "#20AAAAAA")
+ }
+ GradientStop {
+ position: 0.9
+ color: Qt.tint(root.col, "#20666666")
+ }
+ GradientStop {
+ position: 1.0
+ color: Qt.tint(root.col, "#20000000")
+ }
+ }
+
+ readonly property int gridSpacing: 10
+
+ Rectangle {
+ anchors {
+ fill: parent
+ margins: 10
+ }
+ color: "lightBlue"
+ clip: true
+
+ GridView {
+ id: grid
+ anchors {
+ fill: parent
+ margins: root.gridSpacing
+ }
+ cellWidth: 300
+ cellHeight: 300
+ delegate: Rectangle {
+ id: gridDelegate
+
+ required property string name
+ required property string shapeUrl
+
+ border.color: "purple"
+ width: grid.cellWidth - root.gridSpacing
+ height: grid.cellHeight - root.gridSpacing
+ Column {
+ anchors.fill: parent
+ anchors.margins: 4
+ Item {
+ width: parent.width
+ height: parent.height - delegText.height
+ Loader {
+ source: Qt.resolvedUrl(gridDelegate.shapeUrl)
+ anchors.fill: parent
+ }
+ }
+ Text {
+ id: delegText
+ text: gridDelegate.name
+ font.pointSize: 16
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ }
+ model: ListModel {
+ ListElement {
+ name: qsTr("Stroke and fill")
+ shapeUrl: "tapableTriangle.qml"
+ }
+ ListElement {
+ name: qsTr("Stroke or fill only")
+ shapeUrl: "strokeOrFill.qml"
+ }
+ ListElement {
+ name: qsTr("Dash pattern")
+ shapeUrl: "dashPattern.qml"
+ }
+ ListElement {
+ name: qsTr("Linear gradient")
+ shapeUrl: "linearGradient.qml"
+ }
+ ListElement {
+ name: qsTr("Radial gradient")
+ shapeUrl: "radialGradient.qml"
+ }
+ ListElement {
+ name: qsTr("Fill rules")
+ shapeUrl: "fillRules.qml"
+ }
+ ListElement {
+ name: qsTr("Join styles")
+ shapeUrl: "joinStyles.qml"
+ }
+ ListElement {
+ name: qsTr("Cap styles")
+ shapeUrl: "capStyles.qml"
+ }
+ ListElement {
+ name: qsTr("Quadratic curve")
+ shapeUrl: "quadraticCurve.qml"
+ }
+ ListElement {
+ name: qsTr("Cubic curve")
+ shapeUrl: "cubicCurve.qml"
+ }
+ ListElement {
+ name: qsTr("Elliptical arc")
+ shapeUrl: "ellipticalArcs.qml"
+ }
+ ListElement {
+ name: qsTr("Gradient spread modes")
+ shapeUrl: "gradientSpreadModes.qml"
+ }
+ ListElement {
+ name: qsTr("Arc direction")
+ shapeUrl: "arcDirection.qml"
+ }
+ ListElement {
+ name: qsTr("Large/small arc")
+ shapeUrl: "largeOrSmallArc.qml"
+ }
+ ListElement {
+ name: qsTr("Arc rotation")
+ shapeUrl: "arcRotation.qml"
+ }
+ ListElement {
+ name: qsTr("Tiger")
+ shapeUrl: "tigerLoader.qml"
+ }
+ ListElement {
+ name: qsTr("Text")
+ shapeUrl: "text.qml"
+ }
+ }
+ }
+ }
+
+ Text {
+ anchors.right: parent.right
+ // used only to get the renderer type
+ Shape {
+ id: dummyShape
+ ShapePath { }
+ }
+ color: "darkBlue"
+ font.pointSize: 12
+ readonly property variant rendererStrings: [ qsTr("Unknown"), qsTr("Generic (QtGui triangulator)"), qsTr("GL_NV_path_rendering"), qsTr("Software (QPainter)"), qsTr("Curve Renderer") ]
+ text: "Active Shape backend: " + rendererStrings[dummyShape.rendererType]
+ SequentialAnimation on opacity {
+ NumberAnimation {
+ from: 1
+ to: 0
+ duration: 5000
+ }
+ PauseAnimation {
+ duration: 5000
+ }
+ NumberAnimation {
+ from: 0
+ to: 1
+ duration: 1000
+ }
+ PauseAnimation {
+ duration: 5000
+ }
+ loops: Animation.Infinite
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/shapes.pro b/examples/quick/quickshapes/shapes/shapes.pro
new file mode 100644
index 0000000000..f14a02a23c
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/shapes.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+
+QT += quick qml
+SOURCES += main.cpp
+RESOURCES += \
+ shapes.qrc \
+ ../../shared/shared.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/quickshapes/shapes
+INSTALLS += target
diff --git a/examples/quick/quickshapes/shapes/shapes.qrc b/examples/quick/quickshapes/shapes/shapes.qrc
new file mode 100644
index 0000000000..413816dba2
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/shapes.qrc
@@ -0,0 +1,28 @@
+<RCC>
+ <qresource prefix="/qt/qml/shapes">
+ <file>main.qml</file>
+ <file>shapegallery.qml</file>
+ <file>interactive.qml</file>
+ <file>sampling.qml</file>
+ <file>clippedtigers.qml</file>
+ <file>tiger.qml</file>
+ <file>tapableTriangle.qml</file>
+ <file>strokeOrFill.qml</file>
+ <file>dashPattern.qml</file>
+ <file>linearGradient.qml</file>
+ <file>radialGradient.qml</file>
+ <file>fillRules.qml</file>
+ <file>joinStyles.qml</file>
+ <file>capStyles.qml</file>
+ <file>quadraticCurve.qml</file>
+ <file>cubicCurve.qml</file>
+ <file>ellipticalArcs.qml</file>
+ <file>gradientSpreadModes.qml</file>
+ <file>arcDirection.qml</file>
+ <file>largeOrSmallArc.qml</file>
+ <file>arcRotation.qml</file>
+ <file>tigerLoader.qml</file>
+ <file>text.qml</file>
+ <file>zoomtiger.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/quickshapes/shapes/strokeOrFill.qml b/examples/quick/quickshapes/shapes/strokeOrFill.qml
new file mode 100644
index 0000000000..c939ed067d
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/strokeOrFill.qml
@@ -0,0 +1,120 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Shape {
+ id: circ1
+ anchors.fill: parent
+
+ ShapePath {
+ id: p1
+ fillColor: "transparent" // stroke only
+ strokeWidth: 4
+
+ SequentialAnimation on strokeColor {
+ loops: Animation.Infinite
+ ColorAnimation {
+ from: "black"
+ to: "yellow"
+ duration: 5000
+ }
+ ColorAnimation {
+ from: "yellow"
+ to: "green"
+ duration: 5000
+ }
+ ColorAnimation {
+ from: "green"
+ to: "black"
+ duration: 5000
+ }
+ }
+
+ readonly property real r: 60
+ startX: circ1.width / 2 - r
+ startY: circ1.height / 2 - r
+ PathArc {
+ x: circ1.width / 2 + p1.r
+ y: circ1.height / 2 + p1.r
+ radiusX: p1.r
+ radiusY: p1.r
+ useLargeArc: true
+ }
+ PathArc {
+ x: circ1.width / 2 - p1.r
+ y: circ1.height / 2 - p1.r
+ radiusX: p1.r
+ radiusY: p1.r
+ useLargeArc: true
+ }
+ }
+ }
+
+ Shape {
+ id: circ2
+ anchors.fill: parent
+
+ SequentialAnimation on opacity {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1.0
+ to: 0.0
+ duration: 5000
+ }
+ NumberAnimation {
+ from: 0.0
+ to: 1.0
+ duration: 5000
+ }
+ }
+
+ ShapePath {
+ id: p2
+ strokeWidth: -1 // or strokeColor: "transparent"
+
+ SequentialAnimation on fillColor {
+ loops: Animation.Infinite
+ ColorAnimation {
+ from: "gray"
+ to: "purple"
+ duration: 3000
+ }
+ ColorAnimation {
+ from: "purple"
+ to: "red"
+ duration: 3000
+ }
+ ColorAnimation {
+ from: "red"
+ to: "gray"
+ duration: 3000
+ }
+ }
+
+ readonly property real r: 40
+ startX: circ2.width / 2 - r
+ startY: circ2.height / 2 - r
+ PathArc {
+ x: circ2.width / 2 + p2.r
+ y: circ2.height / 2 + p2.r
+ radiusX: p2.r
+ radiusY: p2.r
+ useLargeArc: true
+ }
+ PathArc {
+ x: circ2.width / 2 - p2.r
+ y: circ2.height / 2 - p2.r
+ radiusX: p2.r
+ radiusY: p2.r
+ useLargeArc: true
+ }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/tapableTriangle.qml b/examples/quick/quickshapes/shapes/tapableTriangle.qml
new file mode 100644
index 0000000000..c76821e898
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/tapableTriangle.qml
@@ -0,0 +1,76 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ width: 256
+ height: 256
+ color: th.pressed ? "steelBlue" : "lightGray"
+ containmentMask: ctr
+
+ TapHandler {
+ id: th
+ }
+
+ Shape {
+ id: ctr
+ anchors.fill: parent
+ containsMode: Shape.FillContains
+
+ ShapePath {
+ strokeColor: "red"
+ fillColor: "blue"
+
+ SequentialAnimation on strokeWidth {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1
+ to: 30
+ duration: 5000
+ }
+ NumberAnimation {
+ from: 30
+ to: 1
+ duration: 5000
+ }
+ PauseAnimation {
+ duration: 2000
+ }
+ }
+
+ startX: 30
+ startY: 30
+ PathLine {
+ x: ctr.width - 30
+ y: ctr.height - 30
+ }
+ PathLine {
+ x: 30
+ y: ctr.height - 30
+ }
+ PathLine {
+ x: 30
+ y: 30
+ }
+ }
+
+ // Besides ShapePath, Shape supports visual and non-visual objects too, allowing
+ // free mixing without going through extra hoops:
+ Rectangle {
+ id: testRect
+ color: "green"
+ opacity: 0.3
+ width: 20
+ height: 20
+ anchors.right: parent.right
+ }
+ Timer {
+ interval: 100
+ repeat: true
+ onTriggered: testRect.width = testRect.width > 1 ? testRect.width - 1 : 20
+ running: true
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/text.qml b/examples/quick/quickshapes/shapes/text.qml
new file mode 100644
index 0000000000..8a105fb254
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/text.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Shape {
+ anchors.centerIn: parent
+ width: 200
+ height: 100
+
+ ShapePath {
+ id: capTest
+ strokeColor: "black"
+ strokeWidth: 1
+ fillColor: "black"
+
+ PathText {
+ x: 0
+ y: 0
+ text: qsTr("Qt!")
+ font.family: "Arial"
+ font.pixelSize: 150
+ }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/tiger.qml b/examples/quick/quickshapes/shapes/tiger.qml
new file mode 100644
index 0000000000..40fcaf7fc2
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/tiger.qml
@@ -0,0 +1,3670 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Shape {
+ asynchronous: true
+ width: 494; height: 510
+ property bool highlightOnTap: true
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -122.304; y: 84.285 }
+ PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 }
+ PathCubic { control1X: -123.851; control1Y: 86.141; control2X: -140.305; control2Y: 38.066; x: -160.833; y: 40.309 }
+ PathCubic { control1X: -160.833; control1Y: 40.309; control2X: -143.05; control2Y: 32.956; x: -122.304; y: 84.285 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -118.774; y: 81.262 }
+ PathCubic { control1X: -118.774; control1Y: 81.262; control2X: -119.323; control2Y: 83.078; x: -120.092; y: 82.779 }
+ PathCubic { control1X: -120.86; control1Y: 82.481; control2X: -119.977; control2Y: 31.675; x: -140.043; y: 26.801 }
+ PathCubic { control1X: -140.043; control1Y: 26.801; control2X: -120.82; control2Y: 25.937; x: -118.774; y: 81.262 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -91.284; y: 123.59 }
+ PathCubic { control1X: -91.284; control1Y: 123.59; control2X: -89.648; control2Y: 124.55; x: -90.118; y: 125.227 }
+ PathCubic { control1X: -90.589; control1Y: 125.904; control2X: -139.763; control2Y: 113.102; x: -149.218; y: 131.459 }
+ PathCubic { control1X: -149.218; control1Y: 131.459; control2X: -145.539; control2Y: 112.572; x: -91.284; y: 123.59 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -94.093; y: 133.801 }
+ PathCubic { control1X: -94.093; control1Y: 133.801; control2X: -92.237; control2Y: 134.197; x: -92.471; y: 134.988 }
+ PathCubic { control1X: -92.704; control1Y: 135.779; control2X: -143.407; control2Y: 139.121; x: -146.597; y: 159.522 }
+ PathCubic { control1X: -146.597; control1Y: 159.522; control2X: -149.055; control2Y: 140.437; x: -94.093; y: 133.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -98.304; y: 128.276 }
+ PathCubic { control1X: -98.304; control1Y: 128.276; control2X: -96.526; control2Y: 128.939; x: -96.872; y: 129.687 }
+ PathCubic { control1X: -97.218; control1Y: 130.435; control2X: -147.866; control2Y: 126.346; x: -153.998; y: 146.064 }
+ PathCubic { control1X: -153.998; control1Y: 146.064; control2X: -153.646; control2Y: 126.825; x: -98.304; y: 128.276 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -109.009; y: 110.072 }
+ PathCubic { control1X: -109.009; control1Y: 110.072; control2X: -107.701; control2Y: 111.446; x: -108.34; y: 111.967 }
+ PathCubic { control1X: -108.979; control1Y: 112.488; control2X: -152.722; control2Y: 86.634; x: -166.869; y: 101.676 }
+ PathCubic { control1X: -166.869; control1Y: 101.676; control2X: -158.128; control2Y: 84.533; x: -109.009; y: 110.072 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -116.554; y: 114.263 }
+ PathCubic { control1X: -116.554; control1Y: 114.263; control2X: -115.098; control2Y: 115.48; x: -115.674; y: 116.071 }
+ PathCubic { control1X: -116.25; control1Y: 116.661; control2X: -162.638; control2Y: 95.922; x: -174.992; y: 112.469 }
+ PathCubic { control1X: -174.992; control1Y: 112.469; control2X: -168.247; control2Y: 94.447; x: -116.554; y: 114.263 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -119.154; y: 118.335 }
+ PathCubic { control1X: -119.154; control1Y: 118.335; control2X: -117.546; control2Y: 119.343; x: -118.036; y: 120.006 }
+ PathCubic { control1X: -118.526; control1Y: 120.669; control2X: -167.308; control2Y: 106.446; x: -177.291; y: 124.522 }
+ PathCubic { control1X: -177.291; control1Y: 124.522; control2X: -173.066; control2Y: 105.749; x: -119.154; y: 118.335 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -108.42; y: 118.949 }
+ PathCubic { control1X: -108.42; control1Y: 118.949; control2X: -107.298; control2Y: 120.48; x: -107.999; y: 120.915 }
+ PathCubic { control1X: -108.7; control1Y: 121.35; control2X: -148.769; control2Y: 90.102; x: -164.727; y: 103.207 }
+ PathCubic { control1X: -164.727; control1Y: 103.207; control2X: -153.862; control2Y: 87.326; x: -108.42; y: 118.949 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -128.2; y: 90 }
+ PathCubic { control1X: -128.2; control1Y: 90; control2X: -127.6; control2Y: 91.8; x: -128.4; y: 92 }
+ PathCubic { control1X: -129.2; control1Y: 92.2; control2X: -157.8; control2Y: 50.2; x: -177.001; y: 57.8 }
+ PathCubic { control1X: -177.001; control1Y: 57.8; control2X: -161.8; control2Y: 46; x: -128.2; y: 90 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -127.505; y: 96.979 }
+ PathCubic { control1X: -127.505; control1Y: 96.979; control2X: -126.53; control2Y: 98.608; x: -127.269; y: 98.975 }
+ PathCubic { control1X: -128.007; control1Y: 99.343; control2X: -164.992; control2Y: 64.499; x: -182.101; y: 76.061 }
+ PathCubic { control1X: -182.101; control1Y: 76.061; control2X: -169.804; control2Y: 61.261; x: -127.505; y: 96.979 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -127.62; y: 101.349 }
+ PathCubic { control1X: -127.62; control1Y: 101.349; control2X: -126.498; control2Y: 102.88; x: -127.199; y: 103.315 }
+ PathCubic { control1X: -127.9; control1Y: 103.749; control2X: -167.969; control2Y: 72.502; x: -183.927; y: 85.607 }
+ PathCubic { control1X: -183.927; control1Y: 85.607; control2X: -173.062; control2Y: 69.726; x: -127.62; y: 101.349 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -129.83; y: 103.065 }
+ PathCubic { control1X: -129.327; control1Y: 109.113; control2X: -128.339; control2Y: 115.682; x: -126.6; y: 118.801 }
+ PathCubic { control1X: -126.6; control1Y: 118.801; control2X: -130.2; control2Y: 131.201; x: -121.4; y: 144.401 }
+ PathCubic { control1X: -121.4; control1Y: 144.401; control2X: -121.8; control2Y: 151.601; x: -120.2; y: 154.801 }
+ PathCubic { control1X: -120.2; control1Y: 154.801; control2X: -116.2; control2Y: 163.201; x: -111.4; y: 164.001 }
+ PathCubic { control1X: -107.516; control1Y: 164.648; control2X: -98.793; control2Y: 167.717; x: -88.932; y: 169.121 }
+ PathCubic { control1X: -88.932; control1Y: 169.121; control2X: -71.8; control2Y: 183.201; x: -75; y: 196.001 }
+ PathCubic { control1X: -75; control1Y: 196.001; control2X: -75.4; control2Y: 212.401; x: -79; y: 214.001 }
+ PathCubic { control1X: -79; control1Y: 214.001; control2X: -67.4; control2Y: 202.801; x: -77; y: 219.601 }
+ PathLine { x: -81.4; y: 238.401 }
+ PathCubic { control1X: -81.4; control1Y: 238.401; control2X: -55.8; control2Y: 216.801; x: -71.4; y: 235.201 }
+ PathLine { x: -81.4; y: 261.201 }
+ PathCubic { control1X: -81.4; control1Y: 261.201; control2X: -61.8; control2Y: 242.801; x: -69; y: 251.201 }
+ PathLine { x: -72.2; y: 260.001 }
+ PathCubic { control1X: -72.2; control1Y: 260.001; control2X: -29; control2Y: 232.801; x: -59.8; y: 262.401 }
+ PathCubic { control1X: -59.8; control1Y: 262.401; control2X: -51.8; control2Y: 258.801; x: -47.4; y: 261.601 }
+ PathCubic { control1X: -47.4; control1Y: 261.601; control2X: -40.6; control2Y: 260.401; x: -41.4; y: 262.001 }
+ PathCubic { control1X: -41.4; control1Y: 262.001; control2X: -62.2; control2Y: 272.401; x: -65.8; y: 290.801 }
+ PathCubic { control1X: -65.8; control1Y: 290.801; control2X: -57.4; control2Y: 280.801; x: -60.6; y: 291.601 }
+ PathLine { x: -60.2; y: 303.201 }
+ PathCubic { control1X: -60.2; control1Y: 303.201; control2X: -56.2; control2Y: 281.601; x: -56.6; y: 319.201 }
+ PathCubic { control1X: -56.6; control1Y: 319.201; control2X: -37.4; control2Y: 301.201; x: -49; y: 322.001 }
+ PathLine { x: -49; y: 338.801 }
+ PathCubic { control1X: -49; control1Y: 338.801; control2X: -33.8; control2Y: 322.401; x: -40.2; y: 335.201 }
+ PathCubic { control1X: -40.2; control1Y: 335.201; control2X: -30.2; control2Y: 326.401; x: -34.2; y: 341.601 }
+ PathCubic { control1X: -34.2; control1Y: 341.601; control2X: -35; control2Y: 352.001; x: -30.6; y: 340.801 }
+ PathCubic { control1X: -30.6; control1Y: 340.801; control2X: -14.6; control2Y: 310.201; x: -20.6; y: 336.401 }
+ PathCubic { control1X: -20.6; control1Y: 336.401; control2X: -21.4; control2Y: 355.601; x: -16.6; y: 340.801 }
+ PathCubic { control1X: -16.6; control1Y: 340.801; control2X: -16.2; control2Y: 351.201; x: -7; y: 358.401 }
+ PathCubic { control1X: -7; control1Y: 358.401; control2X: -8.2; control2Y: 307.601; x: 4.6; y: 343.601 }
+ PathLine { x: 8.6; y: 360.001 }
+ PathCubic { control1X: 8.6; control1Y: 360.001; control2X: 11.4; control2Y: 350.801; x: 11; y: 345.601 }
+ PathCubic { control1X: 11; control1Y: 345.601; control2X: 25.8; control2Y: 329.201; x: 19; y: 353.601 }
+ PathCubic { control1X: 19; control1Y: 353.601; control2X: 34.2; control2Y: 330.801; x: 31; y: 344.001 }
+ PathCubic { control1X: 31; control1Y: 344.001; control2X: 23.4; control2Y: 360.001; x: 25; y: 364.801 }
+ PathCubic { control1X: 25; control1Y: 364.801; control2X: 41.8; control2Y: 330.001; x: 43; y: 328.401 }
+ PathCubic { control1X: 43; control1Y: 328.401; control2X: 41; control2Y: 370.802; x: 51.8; y: 334.801 }
+ PathCubic { control1X: 51.8; control1Y: 334.801; control2X: 57.4; control2Y: 346.801; x: 54.6; y: 351.201 }
+ PathCubic { control1X: 54.6; control1Y: 351.201; control2X: 62.6; control2Y: 343.201; x: 61.8; y: 340.001 }
+ PathCubic { control1X: 61.8; control1Y: 340.001; control2X: 66.4; control2Y: 331.801; x: 69.2; y: 345.401 }
+ PathCubic { control1X: 69.2; control1Y: 345.401; control2X: 71; control2Y: 354.801; x: 72.6; y: 351.601 }
+ PathCubic { control1X: 72.6; control1Y: 351.601; control2X: 76.6; control2Y: 375.602; x: 77.8; y: 352.801 }
+ PathCubic { control1X: 77.8; control1Y: 352.801; control2X: 79.4; control2Y: 339.201; x: 72.2; y: 327.601 }
+ PathCubic { control1X: 72.2; control1Y: 327.601; control2X: 73; control2Y: 324.401; x: 70.2; y: 320.401 }
+ PathCubic { control1X: 70.2; control1Y: 320.401; control2X: 83.8; control2Y: 342.001; x: 76.6; y: 313.201 }
+ PathCubic { control1X: 76.6; control1Y: 313.201; control2X: 87.801; control2Y: 321.201; x: 89.001; y: 321.201 }
+ PathCubic { control1X: 89.001; control1Y: 321.201; control2X: 75.4; control2Y: 298.001; x: 84.2; y: 302.801 }
+ PathCubic { control1X: 84.2; control1Y: 302.801; control2X: 79; control2Y: 292.401; x: 97.001; y: 304.401 }
+ PathCubic { control1X: 97.001; control1Y: 304.401; control2X: 81; control2Y: 288.401; x: 98.601; y: 298.001 }
+ PathCubic { control1X: 98.601; control1Y: 298.001; control2X: 106.601; control2Y: 304.401; x: 99.001; y: 294.401 }
+ PathCubic { control1X: 99.001; control1Y: 294.401; control2X: 84.6; control2Y: 278.401; x: 106.601; y: 296.401 }
+ PathCubic { control1X: 106.601; control1Y: 296.401; control2X: 118.201; control2Y: 312.801; x: 119.001; y: 315.601 }
+ PathCubic { control1X: 119.001; control1Y: 315.601; control2X: 109.001; control2Y: 286.401; x: 104.601; y: 283.601 }
+ PathCubic { control1X: 104.601; control1Y: 283.601; control2X: 113.001; control2Y: 247.201; x: 154.201; y: 262.801 }
+ PathCubic { control1X: 154.201; control1Y: 262.801; control2X: 161.001; control2Y: 280.001; x: 165.401; y: 261.601 }
+ PathCubic { control1X: 165.401; control1Y: 261.601; control2X: 178.201; control2Y: 255.201; x: 189.401; y: 282.801 }
+ PathCubic { control1X: 189.401; control1Y: 282.801; control2X: 193.401; control2Y: 269.201; x: 192.601; y: 266.401 }
+ PathCubic { control1X: 192.601; control1Y: 266.401; control2X: 199.401; control2Y: 267.601; x: 198.601; y: 266.401 }
+ PathCubic { control1X: 198.601; control1Y: 266.401; control2X: 211.801; control2Y: 270.801; x: 213.001; y: 270.001 }
+ PathCubic { control1X: 213.001; control1Y: 270.001; control2X: 219.801; control2Y: 276.801; x: 220.201; y: 273.201 }
+ PathCubic { control1X: 220.201; control1Y: 273.201; control2X: 229.401; control2Y: 276.001; x: 227.401; y: 272.401 }
+ PathCubic { control1X: 227.401; control1Y: 272.401; control2X: 236.201; control2Y: 288.001; x: 236.601; y: 291.601 }
+ PathLine { x: 239.001; y: 277.601 }
+ PathLine { x: 241.001; y: 280.401 }
+ PathCubic { control1X: 241.001; control1Y: 280.401; control2X: 242.601; control2Y: 272.801; x: 241.801; y: 271.601 }
+ PathCubic { control1X: 241.001; control1Y: 270.401; control2X: 261.801; control2Y: 278.401; x: 266.601; y: 299.201 }
+ PathLine { x: 268.601; y: 307.601 }
+ PathCubic { control1X: 268.601; control1Y: 307.601; control2X: 274.601; control2Y: 292.801; x: 273.001; y: 288.801 }
+ PathCubic { control1X: 273.001; control1Y: 288.801; control2X: 278.201; control2Y: 289.601; x: 278.601; y: 294.001 }
+ PathCubic { control1X: 278.601; control1Y: 294.001; control2X: 282.601; control2Y: 270.801; x: 277.801; y: 264.801 }
+ PathCubic { control1X: 277.801; control1Y: 264.801; control2X: 282.201; control2Y: 264.001; x: 283.401; y: 267.601 }
+ PathLine { x: 283.401; y: 260.401 }
+ PathCubic { control1X: 283.401; control1Y: 260.401; control2X: 290.601; control2Y: 261.201; x: 290.601; y: 258.801 }
+ PathCubic { control1X: 290.601; control1Y: 258.801; control2X: 295.001; control2Y: 254.801; x: 297.001; y: 259.601 }
+ PathCubic { control1X: 297.001; control1Y: 259.601; control2X: 284.601; control2Y: 224.401; x: 303.001; y: 243.601 }
+ PathCubic { control1X: 303.001; control1Y: 243.601; control2X: 310.201; control2Y: 254.401; x: 306.601; y: 235.601 }
+ PathCubic { control1X: 303.001; control1Y: 216.801; control2X: 299.001; control2Y: 215.201; x: 303.801; y: 214.801 }
+ PathCubic { control1X: 303.801; control1Y: 214.801; control2X: 304.601; control2Y: 211.201; x: 302.601; y: 209.601 }
+ PathCubic { control1X: 300.601; control1Y: 208.001; control2X: 303.801; control2Y: 209.601; x: 303.801; y: 209.601 }
+ PathCubic { control1X: 303.801; control1Y: 209.601; control2X: 308.601; control2Y: 213.601; x: 303.401; y: 191.601 }
+ PathCubic { control1X: 303.401; control1Y: 191.601; control2X: 309.801; control2Y: 193.201; x: 297.801; y: 164.001 }
+ PathCubic { control1X: 297.801; control1Y: 164.001; control2X: 300.601; control2Y: 161.601; x: 296.601; y: 153.201 }
+ PathCubic { control1X: 296.601; control1Y: 153.201; control2X: 304.601; control2Y: 157.601; x: 307.401; y: 156.001 }
+ PathCubic { control1X: 307.401; control1Y: 156.001; control2X: 307.001; control2Y: 154.401; x: 303.801; y: 150.401 }
+ PathCubic { control1X: 303.801; control1Y: 150.401; control2X: 282.201; control2Y: 95.6; x: 302.601; y: 117.601 }
+ PathCubic { control1X: 302.601; control1Y: 117.601; control2X: 314.451; control2Y: 131.151; x: 308.051; y: 108.351 }
+ PathCubic { control1X: 308.051; control1Y: 108.351; control2X: 298.94; control2Y: 84.341; x: 299.717; y: 80.045 }
+ PathLine { x: -129.83; y: 103.065 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: 299.717; y: 80.245 }
+ PathCubic { control1X: 300.345; control1Y: 80.426; control2X: 302.551; control2Y: 81.55; x: 303.801; y: 83.2 }
+ PathCubic { control1X: 303.801; control1Y: 83.2; control2X: 310.601; control2Y: 94; x: 305.401; y: 75.6 }
+ PathCubic { control1X: 305.401; control1Y: 75.6; control2X: 296.201; control2Y: 46.8; x: 305.001; y: 58 }
+ PathCubic { control1X: 305.001; control1Y: 58; control2X: 311.001; control2Y: 65.2; x: 307.801; y: 51.6 }
+ PathCubic { control1X: 303.936; control1Y: 35.173; control2X: 301.401; control2Y: 28.8; x: 301.401; y: 28.8 }
+ PathCubic { control1X: 301.401; control1Y: 28.8; control2X: 313.001; control2Y: 33.6; x: 286.201; y: -6 }
+ PathLine { x: 295.001; y: -2.4 }
+ PathCubic { control1X: 295.001; control1Y: -2.4; control2X: 275.401; control2Y: -42; x: 253.801; y: -47.2 }
+ PathLine { x: 245.801; y: -53.2 }
+ PathCubic { control1X: 245.801; control1Y: -53.2; control2X: 284.201; control2Y: -91.2; x: 271.401; y: -128 }
+ PathCubic { control1X: 271.401; control1Y: -128; control2X: 264.601; control2Y: -133.2; x: 255.001; y: -124 }
+ PathCubic { control1X: 255.001; control1Y: -124; control2X: 248.601; control2Y: -119.2; x: 242.601; y: -120.8 }
+ PathCubic { control1X: 242.601; control1Y: -120.8; control2X: 211.801; control2Y: -119.6; x: 209.801; y: -119.6 }
+ PathCubic { control1X: 207.801; control1Y: -119.6; control2X: 173.001; control2Y: -156.8; x: 107.401; y: -139.2 }
+ PathCubic { control1X: 107.401; control1Y: -139.2; control2X: 102.201; control2Y: -137.2; x: 97.801; y: -138.4 }
+ PathCubic { control1X: 97.801; control1Y: -138.4; control2X: 79.4; control2Y: -154.4; x: 30.6; y: -131.6 }
+ PathCubic { control1X: 30.6; control1Y: -131.6; control2X: 20.6; control2Y: -129.6; x: 19; y: -129.6 }
+ PathCubic { control1X: 17.4; control1Y: -129.6; control2X: 14.6; control2Y: -129.6; x: 6.6; y: -123.2 }
+ PathCubic { control1X: -1.4; control1Y: -116.8; control2X: -1.8; control2Y: -116; x: -3.8; y: -114.4 }
+ PathCubic { control1X: -3.8; control1Y: -114.4; control2X: -20.2; control2Y: -103.2; x: -25; y: -102.4 }
+ PathCubic { control1X: -25; control1Y: -102.4; control2X: -36.6; control2Y: -96; x: -41; y: -86 }
+ PathLine { x: -44.6; y: -84.8 }
+ PathCubic { control1X: -44.6; control1Y: -84.8; control2X: -46.2; control2Y: -77.6; x: -46.6; y: -76.4 }
+ PathCubic { control1X: -46.6; control1Y: -76.4; control2X: -51.4; control2Y: -72.8; x: -52.2; y: -67.2 }
+ PathCubic { control1X: -52.2; control1Y: -67.2; control2X: -61; control2Y: -61.2; x: -60.6; y: -56.8 }
+ PathCubic { control1X: -60.6; control1Y: -56.8; control2X: -62.2; control2Y: -51.6; x: -63; y: -46.8 }
+ PathCubic { control1X: -63; control1Y: -46.8; control2X: -70.2; control2Y: -42; x: -69.4; y: -39.2 }
+ PathCubic { control1X: -69.4; control1Y: -39.2; control2X: -77; control2Y: -25.2; x: -75.8; y: -18.4 }
+ PathCubic { control1X: -75.8; control1Y: -18.4; control2X: -82.2; control2Y: -18.8; x: -85; y: -16.4 }
+ PathCubic { control1X: -85; control1Y: -16.4; control2X: -85.8; control2Y: -11.6; x: -87.4; y: -11.2 }
+ PathCubic { control1X: -87.4; control1Y: -11.2; control2X: -90.2; control2Y: -10; x: -87.8; y: -6 }
+ PathCubic { control1X: -87.8; control1Y: -6; control2X: -89.4; control2Y: -3.2; x: -89.8; y: -1.6 }
+ PathCubic { control1X: -89.8; control1Y: -1.6; control2X: -89; control2Y: 1.2; x: -93.4; y: 6.8 }
+ PathCubic { control1X: -93.4; control1Y: 6.8; control2X: -99.8; control2Y: 25.6; x: -97.8; y: 30.8 }
+ PathCubic { control1X: -97.8; control1Y: 30.8; control2X: -97.4; control2Y: 35.6; x: -100.2; y: 37.2 }
+ PathCubic { control1X: -100.2; control1Y: 37.2; control2X: -103.8; control2Y: 36.8; x: -95.4; y: 48.8 }
+ PathCubic { control1X: -95.4; control1Y: 48.8; control2X: -94.6; control2Y: 50; x: -97.8; y: 52.4 }
+ PathCubic { control1X: -97.8; control1Y: 52.4; control2X: -115; control2Y: 56; x: -117.4; y: 72.4 }
+ PathCubic { control1X: -117.4; control1Y: 72.4; control2X: -131; control2Y: 87.2; x: -131; y: 92.4 }
+ PathCubic { control1X: -131; control1Y: 94.705; control2X: -130.729; control2Y: 97.852; x: -130.03; y: 102.465 }
+ PathCubic { control1X: -130.03; control1Y: 102.465; control2X: -130.6; control2Y: 110.801; x: -103; y: 111.601 }
+ PathCubic { control1X: -75.4; control1Y: 112.401; control2X: 299.717; control2Y: 80.245; x: 299.717; y: 80.245 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: -115.6; y: 102.6 }
+ PathCubic { control1X: -140.6; control1Y: 63.2; control2X: -126.2; control2Y: 119.601; x: -126.2; y: 119.601 }
+ PathCubic { control1X: -117.4; control1Y: 154.001; control2X: 12.2; control2Y: 116.401; x: 12.2; y: 116.401 }
+ PathCubic { control1X: 12.2; control1Y: 116.401; control2X: 181.001; control2Y: 86; x: 192.201; y: 82 }
+ PathCubic { control1X: 203.401; control1Y: 78; control2X: 298.601; control2Y: 84.4; x: 298.601; y: 84.4 }
+ PathLine { x: 293.001; y: 67.6 }
+ PathCubic { control1X: 228.201; control1Y: 21.2; control2X: 209.001; control2Y: 44.4; x: 195.401; y: 40.4 }
+ PathCubic { control1X: 181.801; control1Y: 36.4; control2X: 184.201; control2Y: 46; x: 181.001; y: 46.8 }
+ PathCubic { control1X: 177.801; control1Y: 47.6; control2X: 138.601; control2Y: 22.8; x: 132.201; y: 23.6 }
+ PathCubic { control1X: 125.801; control1Y: 24.4; control2X: 100.459; control2Y: 0.649; x: 115.401; y: 32.4 }
+ PathCubic { control1X: 131.401; control1Y: 66.4; control2X: 57; control2Y: 71.6; x: 40.2; y: 60.4 }
+ PathCubic { control1X: 23.4; control1Y: 49.2; control2X: 47.4; control2Y: 78.8; x: 47.4; y: 78.8 }
+ PathCubic { control1X: 65.8; control1Y: 98.8; control2X: 31.4; control2Y: 82; x: 31.4; y: 82 }
+ PathCubic { control1X: -3; control1Y: 69.2; control2X: -27; control2Y: 94.8; x: -30.2; y: 95.6 }
+ PathCubic { control1X: -33.4; control1Y: 96.4; control2X: -38.2; control2Y: 99.6; x: -39; y: 93.2 }
+ PathCubic { control1X: -39.8; control1Y: 86.8; control2X: -47.31; control2Y: 70.099; x: -79; y: 96.4 }
+ PathCubic { control1X: -99; control1Y: 113.001; control2X: -112.8; control2Y: 91; x: -112.8; y: 91 }
+ PathLine { x: -115.6; y: 102.6 }
+ }
+
+ ShapePath {
+ fillColor: "#e87f3a"
+ strokeWidth: -1
+ PathMove { x: 133.51; y: 25.346 }
+ PathCubic { control1X: 127.11; control1Y: 26.146; control2X: 101.743; control2Y: 2.407; x: 116.71; y: 34.146 }
+ PathCubic { control1X: 133.31; control1Y: 69.346; control2X: 58.31; control2Y: 73.346; x: 41.51; y: 62.146 }
+ PathCubic { control1X: 24.709; control1Y: 50.946; control2X: 48.71; control2Y: 80.546; x: 48.71; y: 80.546 }
+ PathCubic { control1X: 67.11; control1Y: 100.546; control2X: 32.709; control2Y: 83.746; x: 32.709; y: 83.746 }
+ PathCubic { control1X: -1.691; control1Y: 70.946; control2X: -25.691; control2Y: 96.546; x: -28.891; y: 97.346 }
+ PathCubic { control1X: -32.091; control1Y: 98.146; control2X: -36.891; control2Y: 101.346; x: -37.691; y: 94.946 }
+ PathCubic { control1X: -38.491; control1Y: 88.546; control2X: -45.87; control2Y: 72.012; x: -77.691; y: 98.146 }
+ PathCubic { control1X: -98.927; control1Y: 115.492; control2X: -112.418; control2Y: 94.037; x: -112.418; y: 94.037 }
+ PathLine { x: -115.618; y: 104.146 }
+ PathCubic { control1X: -140.618; control1Y: 64.346; control2X: -125.546; control2Y: 122.655; x: -125.546; y: 122.655 }
+ PathCubic { control1X: -116.745; control1Y: 157.056; control2X: 13.509; control2Y: 118.146; x: 13.509; y: 118.146 }
+ PathCubic { control1X: 13.509; control1Y: 118.146; control2X: 182.31; control2Y: 87.746; x: 193.51; y: 83.746 }
+ PathCubic { control1X: 204.71; control1Y: 79.746; control2X: 299.038; control2Y: 86.073; x: 299.038; y: 86.073 }
+ PathLine { x: 293.51; y: 68.764 }
+ PathCubic { control1X: 228.71; control1Y: 22.364; control2X: 210.31; control2Y: 46.146; x: 196.71; y: 42.146 }
+ PathCubic { control1X: 183.11; control1Y: 38.146; control2X: 185.51; control2Y: 47.746; x: 182.31; y: 48.546 }
+ PathCubic { control1X: 179.11; control1Y: 49.346; control2X: 139.91; control2Y: 24.546; x: 133.51; y: 25.346 }
+ }
+
+ ShapePath {
+ fillColor: "#ea8c4d"
+ strokeWidth: -1
+ PathMove { x: 134.819; y: 27.091 }
+ PathCubic { control1X: 128.419; control1Y: 27.891; control2X: 103.685; control2Y: 3.862; x: 118.019; y: 35.891 }
+ PathCubic { control1X: 134.219; control1Y: 72.092; control2X: 59.619; control2Y: 75.092; x: 42.819; y: 63.892 }
+ PathCubic { control1X: 26.019; control1Y: 52.692; control2X: 50.019; control2Y: 82.292; x: 50.019; y: 82.292 }
+ PathCubic { control1X: 68.419; control1Y: 102.292; control2X: 34.019; control2Y: 85.492; x: 34.019; y: 85.492 }
+ PathCubic { control1X: -0.381; control1Y: 72.692; control2X: -24.382; control2Y: 98.292; x: -27.582; y: 99.092 }
+ PathCubic { control1X: -30.782; control1Y: 99.892; control2X: -35.582; control2Y: 103.092; x: -36.382; y: 96.692 }
+ PathCubic { control1X: -37.182; control1Y: 90.292; control2X: -44.43; control2Y: 73.925; x: -76.382; y: 99.892 }
+ PathCubic { control1X: -98.855; control1Y: 117.983; control2X: -112.036; control2Y: 97.074; x: -112.036; y: 97.074 }
+ PathLine { x: -115.636; y: 105.692 }
+ PathCubic { control1X: -139.436; control1Y: 66.692; control2X: -124.891; control2Y: 125.71; x: -124.891; y: 125.71 }
+ PathCubic { control1X: -116.091; control1Y: 160.11; control2X: 14.819; control2Y: 119.892; x: 14.819; y: 119.892 }
+ PathCubic { control1X: 14.819; control1Y: 119.892; control2X: 183.619; control2Y: 89.492; x: 194.819; y: 85.492 }
+ PathCubic { control1X: 206.019; control1Y: 81.492; control2X: 299.474; control2Y: 87.746; x: 299.474; y: 87.746 }
+ PathLine { x: 294.02; y: 69.928 }
+ PathCubic { control1X: 229.219; control1Y: 23.528; control2X: 211.619; control2Y: 47.891; x: 198.019; y: 43.891 }
+ PathCubic { control1X: 184.419; control1Y: 39.891; control2X: 186.819; control2Y: 49.491; x: 183.619; y: 50.292 }
+ PathCubic { control1X: 180.419; control1Y: 51.092; control2X: 141.219; control2Y: 26.291; x: 134.819; y: 27.091 }
+ }
+
+ ShapePath {
+ fillColor: "#ec9961"
+ strokeWidth: -1
+ PathMove { x: 136.128; y: 28.837 }
+ PathCubic { control1X: 129.728; control1Y: 29.637; control2X: 104.999; control2Y: 5.605; x: 119.328; y: 37.637 }
+ PathCubic { control1X: 136.128; control1Y: 75.193; control2X: 60.394; control2Y: 76.482; x: 44.128; y: 65.637 }
+ PathCubic { control1X: 27.328; control1Y: 54.437; control2X: 51.328; control2Y: 84.037; x: 51.328; y: 84.037 }
+ PathCubic { control1X: 69.728; control1Y: 104.037; control2X: 35.328; control2Y: 87.237; x: 35.328; y: 87.237 }
+ PathCubic { control1X: 0.928; control1Y: 74.437; control2X: -23.072; control2Y: 100.037; x: -26.272; y: 100.837 }
+ PathCubic { control1X: -29.472; control1Y: 101.637; control2X: -34.272; control2Y: 104.837; x: -35.072; y: 98.437 }
+ PathCubic { control1X: -35.872; control1Y: 92.037; control2X: -42.989; control2Y: 75.839; x: -75.073; y: 101.637 }
+ PathCubic { control1X: -98.782; control1Y: 120.474; control2X: -111.655; control2Y: 100.11; x: -111.655; y: 100.11 }
+ PathLine { x: -115.655; y: 107.237 }
+ PathCubic { control1X: -137.455; control1Y: 70.437; control2X: -124.236; control2Y: 128.765; x: -124.236; y: 128.765 }
+ PathCubic { control1X: -115.436; control1Y: 163.165; control2X: 16.128; control2Y: 121.637; x: 16.128; y: 121.637 }
+ PathCubic { control1X: 16.128; control1Y: 121.637; control2X: 184.928; control2Y: 91.237; x: 196.129; y: 87.237 }
+ PathCubic { control1X: 207.329; control1Y: 83.237; control2X: 299.911; control2Y: 89.419; x: 299.911; y: 89.419 }
+ PathLine { x: 294.529; y: 71.092 }
+ PathCubic { control1X: 229.729; control1Y: 24.691; control2X: 212.929; control2Y: 49.637; x: 199.329; y: 45.637 }
+ PathCubic { control1X: 185.728; control1Y: 41.637; control2X: 188.128; control2Y: 51.237; x: 184.928; y: 52.037 }
+ PathCubic { control1X: 181.728; control1Y: 52.837; control2X: 142.528; control2Y: 28.037; x: 136.128; y: 28.837 }
+ }
+
+ ShapePath {
+ fillColor: "#eea575"
+ strokeWidth: -1
+ PathMove { x: 137.438; y: 30.583 }
+ PathCubic { control1X: 131.037; control1Y: 31.383; control2X: 106.814; control2Y: 7.129; x: 120.637; y: 39.383 }
+ PathCubic { control1X: 137.438; control1Y: 78.583; control2X: 62.237; control2Y: 78.583; x: 45.437; y: 67.383 }
+ PathCubic { control1X: 28.637; control1Y: 56.183; control2X: 52.637; control2Y: 85.783; x: 52.637; y: 85.783 }
+ PathCubic { control1X: 71.037; control1Y: 105.783; control2X: 36.637; control2Y: 88.983; x: 36.637; y: 88.983 }
+ PathCubic { control1X: 2.237; control1Y: 76.183; control2X: -21.763; control2Y: 101.783; x: -24.963; y: 102.583 }
+ PathCubic { control1X: -28.163; control1Y: 103.383; control2X: -32.963; control2Y: 106.583; x: -33.763; y: 100.183 }
+ PathCubic { control1X: -34.563; control1Y: 93.783; control2X: -41.548; control2Y: 77.752; x: -73.763; y: 103.383 }
+ PathCubic { control1X: -98.709; control1Y: 122.965; control2X: -111.273; control2Y: 103.146; x: -111.273; y: 103.146 }
+ PathLine { x: -115.673; y: 108.783 }
+ PathCubic { control1X: -135.473; control1Y: 73.982; control2X: -123.582; control2Y: 131.819; x: -123.582; y: 131.819 }
+ PathCubic { control1X: -114.782; control1Y: 166.22; control2X: 17.437; control2Y: 123.383; x: 17.437; y: 123.383 }
+ PathCubic { control1X: 17.437; control1Y: 123.383; control2X: 186.238; control2Y: 92.983; x: 197.438; y: 88.983 }
+ PathCubic { control1X: 208.638; control1Y: 84.983; control2X: 300.347; control2Y: 91.092; x: 300.347; y: 91.092 }
+ PathLine { x: 295.038; y: 72.255 }
+ PathCubic { control1X: 230.238; control1Y: 25.855; control2X: 214.238; control2Y: 51.383; x: 200.638; y: 47.383 }
+ PathCubic { control1X: 187.038; control1Y: 43.383; control2X: 189.438; control2Y: 52.983; x: 186.238; y: 53.783 }
+ PathCubic { control1X: 183.038; control1Y: 54.583; control2X: 143.838; control2Y: 29.783; x: 137.438; y: 30.583 }
+ }
+
+ ShapePath {
+ fillColor: "#f1b288"
+ strokeWidth: -1
+ PathMove { x: 138.747; y: 32.328 }
+ PathCubic { control1X: 132.347; control1Y: 33.128; control2X: 106.383; control2Y: 9.677; x: 121.947; y: 41.128 }
+ PathCubic { control1X: 141.147; control1Y: 79.928; control2X: 63.546; control2Y: 80.328; x: 46.746; y: 69.128 }
+ PathCubic { control1X: 29.946; control1Y: 57.928; control2X: 53.946; control2Y: 87.528; x: 53.946; y: 87.528 }
+ PathCubic { control1X: 72.346; control1Y: 107.528; control2X: 37.946; control2Y: 90.728; x: 37.946; y: 90.728 }
+ PathCubic { control1X: 3.546; control1Y: 77.928; control2X: -20.454; control2Y: 103.528; x: -23.654; y: 104.328 }
+ PathCubic { control1X: -26.854; control1Y: 105.128; control2X: -31.654; control2Y: 108.328; x: -32.454; y: 101.928 }
+ PathCubic { control1X: -33.254; control1Y: 95.528; control2X: -40.108; control2Y: 79.665; x: -72.454; y: 105.128 }
+ PathCubic { control1X: -98.636; control1Y: 125.456; control2X: -110.891; control2Y: 106.183; x: -110.891; y: 106.183 }
+ PathLine { x: -115.691; y: 110.328 }
+ PathCubic { control1X: -133.691; control1Y: 77.128; control2X: -122.927; control2Y: 134.874; x: -122.927; y: 134.874 }
+ PathCubic { control1X: -114.127; control1Y: 169.274; control2X: 18.746; control2Y: 125.128; x: 18.746; y: 125.128 }
+ PathCubic { control1X: 18.746; control1Y: 125.128; control2X: 187.547; control2Y: 94.728; x: 198.747; y: 90.728 }
+ PathCubic { control1X: 209.947; control1Y: 86.728; control2X: 300.783; control2Y: 92.764; x: 300.783; y: 92.764 }
+ PathLine { x: 295.547; y: 73.419 }
+ PathCubic { control1X: 230.747; control1Y: 27.019; control2X: 215.547; control2Y: 53.128; x: 201.947; y: 49.128 }
+ PathCubic { control1X: 188.347; control1Y: 45.128; control2X: 190.747; control2Y: 54.728; x: 187.547; y: 55.528 }
+ PathCubic { control1X: 184.347; control1Y: 56.328; control2X: 145.147; control2Y: 31.528; x: 138.747; y: 32.328 }
+ }
+
+ ShapePath {
+ fillColor: "#f3bf9c"
+ strokeWidth: -1
+ PathMove { x: 140.056; y: 34.073 }
+ PathCubic { control1X: 133.655; control1Y: 34.873; control2X: 107.313; control2Y: 11.613; x: 123.255; y: 42.873 }
+ PathCubic { control1X: 143.656; control1Y: 82.874; control2X: 64.855; control2Y: 82.074; x: 48.055; y: 70.874 }
+ PathCubic { control1X: 31.255; control1Y: 59.674; control2X: 55.255; control2Y: 89.274; x: 55.255; y: 89.274 }
+ PathCubic { control1X: 73.655; control1Y: 109.274; control2X: 39.255; control2Y: 92.474; x: 39.255; y: 92.474 }
+ PathCubic { control1X: 4.855; control1Y: 79.674; control2X: -19.145; control2Y: 105.274; x: -22.345; y: 106.074 }
+ PathCubic { control1X: -25.545; control1Y: 106.874; control2X: -30.345; control2Y: 110.074; x: -31.145; y: 103.674 }
+ PathCubic { control1X: -31.945; control1Y: 97.274; control2X: -38.668; control2Y: 81.578; x: -71.145; y: 106.874 }
+ PathCubic { control1X: -98.564; control1Y: 127.947; control2X: -110.509; control2Y: 109.219; x: -110.509; y: 109.219 }
+ PathLine { x: -115.709; y: 111.874 }
+ PathCubic { control1X: -131.709; control1Y: 81.674; control2X: -122.273; control2Y: 137.929; x: -122.273; y: 137.929 }
+ PathCubic { control1X: -113.473; control1Y: 172.329; control2X: 20.055; control2Y: 126.874; x: 20.055; y: 126.874 }
+ PathCubic { control1X: 20.055; control1Y: 126.874; control2X: 188.856; control2Y: 96.474; x: 200.056; y: 92.474 }
+ PathCubic { control1X: 211.256; control1Y: 88.474; control2X: 301.22; control2Y: 94.437; x: 301.22; y: 94.437 }
+ PathLine { x: 296.056; y: 74.583 }
+ PathCubic { control1X: 231.256; control1Y: 28.183; control2X: 216.856; control2Y: 54.874; x: 203.256; y: 50.874 }
+ PathCubic { control1X: 189.656; control1Y: 46.873; control2X: 192.056; control2Y: 56.474; x: 188.856; y: 57.274 }
+ PathCubic { control1X: 185.656; control1Y: 58.074; control2X: 146.456; control2Y: 33.273; x: 140.056; y: 34.073 }
+ }
+
+ ShapePath {
+ fillColor: "#f5ccb0"
+ strokeWidth: -1
+ PathMove { x: 141.365; y: 35.819 }
+ PathCubic { control1X: 134.965; control1Y: 36.619; control2X: 107.523; control2Y: 13.944; x: 124.565; y: 44.619 }
+ PathCubic { control1X: 146.565; control1Y: 84.219; control2X: 66.164; control2Y: 83.819; x: 49.364; y: 72.619 }
+ PathCubic { control1X: 32.564; control1Y: 61.419; control2X: 56.564; control2Y: 91.019; x: 56.564; y: 91.019 }
+ PathCubic { control1X: 74.964; control1Y: 111.019; control2X: 40.564; control2Y: 94.219; x: 40.564; y: 94.219 }
+ PathCubic { control1X: 6.164; control1Y: 81.419; control2X: -17.836; control2Y: 107.019; x: -21.036; y: 107.819 }
+ PathCubic { control1X: -24.236; control1Y: 108.619; control2X: -29.036; control2Y: 111.819; x: -29.836; y: 105.419 }
+ PathCubic { control1X: -30.636; control1Y: 99.019; control2X: -37.227; control2Y: 83.492; x: -69.836; y: 108.619 }
+ PathCubic { control1X: -98.491; control1Y: 130.438; control2X: -110.127; control2Y: 112.256; x: -110.127; y: 112.256 }
+ PathLine { x: -115.727; y: 113.419 }
+ PathCubic { control1X: -130.128; control1Y: 85.019; control2X: -121.618; control2Y: 140.983; x: -121.618; y: 140.983 }
+ PathCubic { control1X: -112.818; control1Y: 175.384; control2X: 21.364; control2Y: 128.619; x: 21.364; y: 128.619 }
+ PathCubic { control1X: 21.364; control1Y: 128.619; control2X: 190.165; control2Y: 98.219; x: 201.365; y: 94.219 }
+ PathCubic { control1X: 212.565; control1Y: 90.219; control2X: 301.656; control2Y: 96.11; x: 301.656; y: 96.11 }
+ PathLine { x: 296.565; y: 75.746 }
+ PathCubic { control1X: 231.765; control1Y: 29.346; control2X: 218.165; control2Y: 56.619; x: 204.565; y: 52.619 }
+ PathCubic { control1X: 190.965; control1Y: 48.619; control2X: 193.365; control2Y: 58.219; x: 190.165; y: 59.019 }
+ PathCubic { control1X: 186.965; control1Y: 59.819; control2X: 147.765; control2Y: 35.019; x: 141.365; y: 35.819 }
+ }
+
+ ShapePath {
+ fillColor: "#f8d8c4"
+ strokeWidth: -1
+ PathMove { x: 142.674; y: 37.565 }
+ PathCubic { control1X: 136.274; control1Y: 38.365; control2X: 108.832; control2Y: 15.689; x: 125.874; y: 46.365 }
+ PathCubic { control1X: 147.874; control1Y: 85.965; control2X: 67.474; control2Y: 85.565; x: 50.674; y: 74.365 }
+ PathCubic { control1X: 33.874; control1Y: 63.165; control2X: 57.874; control2Y: 92.765; x: 57.874; y: 92.765 }
+ PathCubic { control1X: 76.274; control1Y: 112.765; control2X: 41.874; control2Y: 95.965; x: 41.874; y: 95.965 }
+ PathCubic { control1X: 7.473; control1Y: 83.165; control2X: -16.527; control2Y: 108.765; x: -19.727; y: 109.565 }
+ PathCubic { control1X: -22.927; control1Y: 110.365; control2X: -27.727; control2Y: 113.565; x: -28.527; y: 107.165 }
+ PathCubic { control1X: -29.327; control1Y: 100.765; control2X: -35.786; control2Y: 85.405; x: -68.527; y: 110.365 }
+ PathCubic { control1X: -98.418; control1Y: 132.929; control2X: -109.745; control2Y: 115.293; x: -109.745; y: 115.293 }
+ PathLine { x: -115.745; y: 114.965 }
+ PathCubic { control1X: -129.346; control1Y: 88.564; control2X: -120.963; control2Y: 144.038; x: -120.963; y: 144.038 }
+ PathCubic { control1X: -112.163; control1Y: 178.438; control2X: 22.673; control2Y: 130.365; x: 22.673; y: 130.365 }
+ PathCubic { control1X: 22.673; control1Y: 130.365; control2X: 191.474; control2Y: 99.965; x: 202.674; y: 95.965 }
+ PathCubic { control1X: 213.874; control1Y: 91.965; control2X: 302.093; control2Y: 97.783; x: 302.093; y: 97.783 }
+ PathLine { x: 297.075; y: 76.91 }
+ PathCubic { control1X: 232.274; control1Y: 30.51; control2X: 219.474; control2Y: 58.365; x: 205.874; y: 54.365 }
+ PathCubic { control1X: 192.274; control1Y: 50.365; control2X: 194.674; control2Y: 59.965; x: 191.474; y: 60.765 }
+ PathCubic { control1X: 188.274; control1Y: 61.565; control2X: 149.074; control2Y: 36.765; x: 142.674; y: 37.565 }
+ }
+
+ ShapePath {
+ fillColor: "#fae5d7"
+ strokeWidth: -1
+ PathMove { x: 143.983; y: 39.31 }
+ PathCubic { control1X: 137.583; control1Y: 40.11; control2X: 110.529; control2Y: 17.223; x: 127.183; y: 48.11 }
+ PathCubic { control1X: 149.183; control1Y: 88.91; control2X: 68.783; control2Y: 87.31; x: 51.983; y: 76.11 }
+ PathCubic { control1X: 35.183; control1Y: 64.91; control2X: 59.183; control2Y: 94.51; x: 59.183; y: 94.51 }
+ PathCubic { control1X: 77.583; control1Y: 114.51; control2X: 43.183; control2Y: 97.71; x: 43.183; y: 97.71 }
+ PathCubic { control1X: 8.783; control1Y: 84.91; control2X: -15.217; control2Y: 110.51; x: -18.417; y: 111.31 }
+ PathCubic { control1X: -21.618; control1Y: 112.11; control2X: -26.418; control2Y: 115.31; x: -27.218; y: 108.91 }
+ PathCubic { control1X: -28.018; control1Y: 102.51; control2X: -34.346; control2Y: 87.318; x: -67.218; y: 112.11 }
+ PathCubic { control1X: -98.345; control1Y: 135.42; control2X: -109.363; control2Y: 118.329; x: -109.363; y: 118.329 }
+ PathLine { x: -115.764; y: 116.51 }
+ PathCubic { control1X: -128.764; control1Y: 92.51; control2X: -120.309; control2Y: 147.093; x: -120.309; y: 147.093 }
+ PathCubic { control1X: -111.509; control1Y: 181.493; control2X: 23.983; control2Y: 132.11; x: 23.983; y: 132.11 }
+ PathCubic { control1X: 23.983; control1Y: 132.11; control2X: 192.783; control2Y: 101.71; x: 203.983; y: 97.71 }
+ PathCubic { control1X: 215.183; control1Y: 93.71; control2X: 302.529; control2Y: 99.456; x: 302.529; y: 99.456 }
+ PathLine { x: 297.583; y: 78.074 }
+ PathCubic { control1X: 232.783; control1Y: 31.673; control2X: 220.783; control2Y: 60.11; x: 207.183; y: 56.11 }
+ PathCubic { control1X: 193.583; control1Y: 52.11; control2X: 195.983; control2Y: 61.71; x: 192.783; y: 62.51 }
+ PathCubic { control1X: 189.583; control1Y: 63.31; control2X: 150.383; control2Y: 38.51; x: 143.983; y: 39.31 }
+ }
+
+ ShapePath {
+ fillColor: "#fcf2eb"
+ strokeWidth: -1
+ PathMove { x: 145.292; y: 41.055 }
+ PathCubic { control1X: 138.892; control1Y: 41.855; control2X: 112.917; control2Y: 18.411; x: 128.492; y: 49.855 }
+ PathCubic { control1X: 149.692; control1Y: 92.656; control2X: 70.092; control2Y: 89.056; x: 53.292; y: 77.856 }
+ PathCubic { control1X: 36.492; control1Y: 66.656; control2X: 60.492; control2Y: 96.256; x: 60.492; y: 96.256 }
+ PathCubic { control1X: 78.892; control1Y: 116.256; control2X: 44.492; control2Y: 99.456; x: 44.492; y: 99.456 }
+ PathCubic { control1X: 10.092; control1Y: 86.656; control2X: -13.908; control2Y: 112.256; x: -17.108; y: 113.056 }
+ PathCubic { control1X: -20.308; control1Y: 113.856; control2X: -25.108; control2Y: 117.056; x: -25.908; y: 110.656 }
+ PathCubic { control1X: -26.708; control1Y: 104.256; control2X: -32.905; control2Y: 89.232; x: -65.908; y: 113.856 }
+ PathCubic { control1X: -98.273; control1Y: 137.911; control2X: -108.982; control2Y: 121.365; x: -108.982; y: 121.365 }
+ PathLine { x: -115.782; y: 118.056 }
+ PathCubic { control1X: -128.582; control1Y: 94.856; control2X: -119.654; control2Y: 150.147; x: -119.654; y: 150.147 }
+ PathCubic { control1X: -110.854; control1Y: 184.547; control2X: 25.292; control2Y: 133.856; x: 25.292; y: 133.856 }
+ PathCubic { control1X: 25.292; control1Y: 133.856; control2X: 194.093; control2Y: 103.456; x: 205.293; y: 99.456 }
+ PathCubic { control1X: 216.493; control1Y: 95.456; control2X: 302.965; control2Y: 101.128; x: 302.965; y: 101.128 }
+ PathLine { x: 298.093; y: 79.237 }
+ PathCubic { control1X: 233.292; control1Y: 32.837; control2X: 222.093; control2Y: 61.856; x: 208.493; y: 57.856 }
+ PathCubic { control1X: 194.893; control1Y: 53.855; control2X: 197.293; control2Y: 63.456; x: 194.093; y: 64.256 }
+ PathCubic { control1X: 190.892; control1Y: 65.056; control2X: 151.692; control2Y: 40.255; x: 145.292; y: 41.055 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: -115.8; y: 119.601 }
+ PathCubic { control1X: -128.6; control1Y: 97.6; control2X: -119; control2Y: 153.201; x: -119; y: 153.201 }
+ PathCubic { control1X: -110.2; control1Y: 187.601; control2X: 26.6; control2Y: 135.601; x: 26.6; y: 135.601 }
+ PathCubic { control1X: 26.6; control1Y: 135.601; control2X: 195.401; control2Y: 105.2; x: 206.601; y: 101.2 }
+ PathCubic { control1X: 217.801; control1Y: 97.2; control2X: 303.401; control2Y: 102.8; x: 303.401; y: 102.8 }
+ PathLine { x: 298.601; y: 80.4 }
+ PathCubic { control1X: 233.801; control1Y: 34; control2X: 223.401; control2Y: 63.6; x: 209.801; y: 59.6 }
+ PathCubic { control1X: 196.201; control1Y: 55.6; control2X: 198.601; control2Y: 65.2; x: 195.401; y: 66 }
+ PathCubic { control1X: 192.201; control1Y: 66.8; control2X: 153.001; control2Y: 42; x: 146.601; y: 42.8 }
+ PathCubic { control1X: 140.201; control1Y: 43.6; control2X: 114.981; control2Y: 19.793; x: 129.801; y: 51.6 }
+ PathCubic { control1X: 152.028; control1Y: 99.307; control2X: 69.041; control2Y: 89.227; x: 54.6; y: 79.6 }
+ PathCubic { control1X: 37.8; control1Y: 68.4; control2X: 61.8; control2Y: 98; x: 61.8; y: 98 }
+ PathCubic { control1X: 80.2; control1Y: 118.001; control2X: 45.8; control2Y: 101.2; x: 45.8; y: 101.2 }
+ PathCubic { control1X: 11.4; control1Y: 88.4; control2X: -12.6; control2Y: 114.001; x: -15.8; y: 114.801 }
+ PathCubic { control1X: -19; control1Y: 115.601; control2X: -23.8; control2Y: 118.801; x: -24.6; y: 112.401 }
+ PathCubic { control1X: -25.4; control1Y: 106; control2X: -31.465; control2Y: 91.144; x: -64.6; y: 115.601 }
+ PathCubic { control1X: -98.2; control1Y: 140.401; control2X: -108.6; control2Y: 124.401; x: -108.6; y: 124.401 }
+ PathLine { x: -115.8; y: 119.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -74.2; y: 149.601 }
+ PathCubic { control1X: -74.2; control1Y: 149.601; control2X: -81.4; control2Y: 161.201; x: -60.6; y: 174.401 }
+ PathCubic { control1X: -60.6; control1Y: 174.401; control2X: -59.2; control2Y: 175.801; x: -77.2; y: 171.601 }
+ PathCubic { control1X: -77.2; control1Y: 171.601; control2X: -83.4; control2Y: 169.601; x: -85; y: 159.201 }
+ PathCubic { control1X: -85; control1Y: 159.201; control2X: -89.8; control2Y: 154.801; x: -94.6; y: 149.201 }
+ PathCubic { control1X: -99.4; control1Y: 143.601; control2X: -74.2; control2Y: 149.601; x: -74.2; y: 149.601 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 65.8; y: 102 }
+ PathCubic { control1X: 65.8; control1Y: 102; control2X: 83.498; control2Y: 128.821; x: 82.9; y: 133.601 }
+ PathCubic { control1X: 81.6; control1Y: 144.001; control2X: 81.4; control2Y: 153.601; x: 84.6; y: 157.601 }
+ PathCubic { control1X: 87.801; control1Y: 161.601; control2X: 96.601; control2Y: 194.801; x: 96.601; y: 194.801 }
+ PathCubic { control1X: 96.601; control1Y: 194.801; control2X: 96.201; control2Y: 196.001; x: 108.601; y: 158.001 }
+ PathCubic { control1X: 108.601; control1Y: 158.001; control2X: 120.201; control2Y: 142.001; x: 100.201; y: 123.601 }
+ PathCubic { control1X: 100.201; control1Y: 123.601; control2X: 65; control2Y: 94.8; x: 65.8; y: 102 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -54.2; y: 176.401 }
+ PathCubic { control1X: -54.2; control1Y: 176.401; control2X: -43; control2Y: 183.601; x: -57.4; y: 214.801 }
+ PathLine { x: -51; y: 212.401 }
+ PathCubic { control1X: -51; control1Y: 212.401; control2X: -51.8; control2Y: 223.601; x: -55; y: 226.001 }
+ PathLine { x: -47.8; y: 222.801 }
+ PathCubic { control1X: -47.8; control1Y: 222.801; control2X: -43; control2Y: 230.801; x: -47; y: 235.601 }
+ PathCubic { control1X: -47; control1Y: 235.601; control2X: -30.2; control2Y: 243.601; x: -31; y: 250.001 }
+ PathCubic { control1X: -31; control1Y: 250.001; control2X: -24.6; control2Y: 242.001; x: -28.6; y: 235.601 }
+ PathCubic { control1X: -32.6; control1Y: 229.201; control2X: -39.8; control2Y: 233.201; x: -39; y: 214.801 }
+ PathLine { x: -47.8; y: 218.001 }
+ PathCubic { control1X: -47.8; control1Y: 218.001; control2X: -42.2; control2Y: 209.201; x: -42.2; y: 202.801 }
+ PathLine { x: -50.2; y: 205.201 }
+ PathCubic { control1X: -50.2; control1Y: 205.201; control2X: -34.731; control2Y: 178.623; x: -45.4; y: 177.201 }
+ PathCubic { control1X: -51.4; control1Y: 176.401; control2X: -54.2; control2Y: 176.401; x: -54.2; y: 176.401 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -21.8; y: 193.201 }
+ PathCubic { control1X: -21.8; control1Y: 193.201; control2X: -19; control2Y: 188.801; x: -21.8; y: 189.601 }
+ PathCubic { control1X: -24.6; control1Y: 190.401; control2X: -55.8; control2Y: 205.201; x: -61.8; y: 214.801 }
+ PathCubic { control1X: -61.8; control1Y: 214.801; control2X: -27.4; control2Y: 190.401; x: -21.8; y: 193.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -11.4; y: 201.201 }
+ PathCubic { control1X: -11.4; control1Y: 201.201; control2X: -8.6; control2Y: 196.801; x: -11.4; y: 197.601 }
+ PathCubic { control1X: -14.2; control1Y: 198.401; control2X: -45.4; control2Y: 213.201; x: -51.4; y: 222.801 }
+ PathCubic { control1X: -51.4; control1Y: 222.801; control2X: -17; control2Y: 198.401; x: -11.4; y: 201.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 1.8; y: 186.001 }
+ PathCubic { control1X: 1.8; control1Y: 186.001; control2X: 4.6; control2Y: 181.601; x: 1.8; y: 182.401 }
+ PathCubic { control1X: -1; control1Y: 183.201; control2X: -32.2; control2Y: 198.001; x: -38.2; y: 207.601 }
+ PathCubic { control1X: -38.2; control1Y: 207.601; control2X: -3.8; control2Y: 183.201; x: 1.8; y: 186.001 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -21.4; y: 229.601 }
+ PathCubic { control1X: -21.4; control1Y: 229.601; control2X: -21.4; control2Y: 223.601; x: -24.2; y: 224.401 }
+ PathCubic { control1X: -27; control1Y: 225.201; control2X: -63; control2Y: 242.801; x: -69; y: 252.401 }
+ PathCubic { control1X: -69; control1Y: 252.401; control2X: -27; control2Y: 226.801; x: -21.4; y: 229.601 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -20.2; y: 218.801 }
+ PathCubic { control1X: -20.2; control1Y: 218.801; control2X: -19; control2Y: 214.001; x: -21.8; y: 214.801 }
+ PathCubic { control1X: -23.8; control1Y: 214.801; control2X: -50.2; control2Y: 226.401; x: -56.2; y: 236.001 }
+ PathCubic { control1X: -56.2; control1Y: 236.001; control2X: -26.6; control2Y: 214.401; x: -20.2; y: 218.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -34.6; y: 266.401 }
+ PathLine { x: -44.6; y: 274.001 }
+ PathCubic { control1X: -44.6; control1Y: 274.001; control2X: -34.2; control2Y: 266.401; x: -30.6; y: 267.601 }
+ PathCubic { control1X: -30.6; control1Y: 267.601; control2X: -37.4; control2Y: 278.801; x: -38.2; y: 284.001 }
+ PathCubic { control1X: -38.2; control1Y: 284.001; control2X: -27.8; control2Y: 271.201; x: -22.2; y: 271.601 }
+ PathCubic { control1X: -22.2; control1Y: 271.601; control2X: -14.6; control2Y: 272.001; x: -14.6; y: 282.801 }
+ PathCubic { control1X: -14.6; control1Y: 282.801; control2X: -9; control2Y: 272.401; x: -5.8; y: 272.801 }
+ PathCubic { control1X: -5.8; control1Y: 272.801; control2X: -4.6; control2Y: 279.201; x: -5.8; y: 286.001 }
+ PathCubic { control1X: -5.8; control1Y: 286.001; control2X: -1.8; control2Y: 278.401; x: 2.2; y: 280.001 }
+ PathCubic { control1X: 2.2; control1Y: 280.001; control2X: 8.6; control2Y: 278.001; x: 7.8; y: 289.601 }
+ PathCubic { control1X: 7.8; control1Y: 289.601; control2X: 7.8; control2Y: 300.001; x: 7; y: 302.801 }
+ PathCubic { control1X: 7; control1Y: 302.801; control2X: 12.6; control2Y: 276.401; x: 15; y: 276.001 }
+ PathCubic { control1X: 15; control1Y: 276.001; control2X: 23; control2Y: 274.801; x: 27.8; y: 283.601 }
+ PathCubic { control1X: 27.8; control1Y: 283.601; control2X: 23.8; control2Y: 276.001; x: 28.6; y: 278.001 }
+ PathCubic { control1X: 28.6; control1Y: 278.001; control2X: 39.4; control2Y: 279.601; x: 42.6; y: 286.401 }
+ PathCubic { control1X: 42.6; control1Y: 286.401; control2X: 35.8; control2Y: 274.401; x: 41.4; y: 277.601 }
+ PathCubic { control1X: 41.4; control1Y: 277.601; control2X: 48.2; control2Y: 277.601; x: 49.4; y: 284.001 }
+ PathCubic { control1X: 49.4; control1Y: 284.001; control2X: 57.8; control2Y: 305.201; x: 59.8; y: 306.801 }
+ PathCubic { control1X: 59.8; control1Y: 306.801; control2X: 52.2; control2Y: 285.201; x: 53.8; y: 285.201 }
+ PathCubic { control1X: 53.8; control1Y: 285.201; control2X: 51.8; control2Y: 273.201; x: 57; y: 288.001 }
+ PathCubic { control1X: 57; control1Y: 288.001; control2X: 53.8; control2Y: 274.001; x: 59.4; y: 274.801 }
+ PathCubic { control1X: 65; control1Y: 275.601; control2X: 69.4; control2Y: 285.601; x: 77.8; y: 283.201 }
+ PathCubic { control1X: 77.8; control1Y: 283.201; control2X: 87.401; control2Y: 288.801; x: 89.401; y: 219.601 }
+ PathLine { x: -34.6; y: 266.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -29.8; y: 173.601 }
+ PathCubic { control1X: -29.8; control1Y: 173.601; control2X: -15; control2Y: 167.601; x: 25; y: 173.601 }
+ PathCubic { control1X: 25; control1Y: 173.601; control2X: 32.2; control2Y: 174.001; x: 39; y: 165.201 }
+ PathCubic { control1X: 45.8; control1Y: 156.401; control2X: 72.6; control2Y: 149.201; x: 79; y: 151.201 }
+ PathLine { x: 88.601; y: 157.601 }
+ PathLine { x: 89.401; y: 158.801 }
+ PathCubic { control1X: 89.401; control1Y: 158.801; control2X: 101.801; control2Y: 169.201; x: 102.201; y: 176.801 }
+ PathCubic { control1X: 102.601; control1Y: 184.401; control2X: 87.801; control2Y: 232.401; x: 78.2; y: 248.401 }
+ PathCubic { control1X: 68.6; control1Y: 264.401; control2X: 59; control2Y: 276.801; x: 39.8; y: 274.401 }
+ PathCubic { control1X: 39.8; control1Y: 274.401; control2X: 19; control2Y: 270.401; x: -6.6; y: 274.401 }
+ PathCubic { control1X: -6.6; control1Y: 274.401; control2X: -35.8; control2Y: 272.801; x: -38.6; y: 264.801 }
+ PathCubic { control1X: -41.4; control1Y: 256.801; control2X: -27.4; control2Y: 241.601; x: -27.4; y: 241.601 }
+ PathCubic { control1X: -27.4; control1Y: 241.601; control2X: -23; control2Y: 233.201; x: -24.2; y: 218.801 }
+ PathCubic { control1X: -25.4; control1Y: 204.401; control2X: -25; control2Y: 176.401; x: -29.8; y: 173.601 }
+ }
+
+ ShapePath {
+ fillColor: "#e5668c"
+ strokeWidth: -1
+ PathMove { x: -7.8; y: 175.601 }
+ PathCubic { control1X: 0.6; control1Y: 194.001; control2X: -29; control2Y: 259.201; x: -29; y: 259.201 }
+ PathCubic { control1X: -31; control1Y: 260.801; control2X: -16.34; control2Y: 266.846; x: -6.2; y: 264.401 }
+ PathCubic { control1X: 4.746; control1Y: 261.763; control2X: 45; control2Y: 266.001; x: 45; y: 266.001 }
+ PathCubic { control1X: 68.6; control1Y: 250.401; control2X: 81.4; control2Y: 206.001; x: 81.4; y: 206.001 }
+ PathCubic { control1X: 81.4; control1Y: 206.001; control2X: 91.801; control2Y: 182.001; x: 74.2; y: 178.801 }
+ PathCubic { control1X: 56.6; control1Y: 175.601; control2X: -7.8; control2Y: 175.601; x: -7.8; y: 175.601 }
+ }
+
+ ShapePath {
+ fillColor: "#b23259"
+ strokeWidth: -1
+ PathMove { x: -9.831; y: 206.497 }
+ PathCubic { control1X: -6.505; control1Y: 193.707; control2X: -4.921; control2Y: 181.906; x: -7.8; y: 175.601 }
+ PathCubic { control1X: -7.8; control1Y: 175.601; control2X: 54.6; control2Y: 182.001; x: 65.8; y: 161.201 }
+ PathCubic { control1X: 70.041; control1Y: 153.326; control2X: 84.801; control2Y: 184.001; x: 84.4; y: 193.601 }
+ PathCubic { control1X: 84.4; control1Y: 193.601; control2X: 21.4; control2Y: 208.001; x: 6.6; y: 196.801 }
+ PathLine { x: -9.831; y: 206.497 }
+ }
+
+ ShapePath {
+ fillColor: "#a5264c"
+ strokeWidth: -1
+ PathMove { x: -5.4; y: 222.801 }
+ PathCubic { control1X: -5.4; control1Y: 222.801; control2X: -3.4; control2Y: 230.001; x: -5.8; y: 234.001 }
+ PathCubic { control1X: -5.8; control1Y: 234.001; control2X: -7.4; control2Y: 234.801; x: -8.6; y: 235.201 }
+ PathCubic { control1X: -8.6; control1Y: 235.201; control2X: -7.4; control2Y: 238.801; x: -1.4; y: 240.401 }
+ PathCubic { control1X: -1.4; control1Y: 240.401; control2X: 0.6; control2Y: 244.801; x: 3; y: 245.201 }
+ PathCubic { control1X: 5.4; control1Y: 245.601; control2X: 10.2; control2Y: 251.201; x: 14.2; y: 250.001 }
+ PathCubic { control1X: 18.2; control1Y: 248.801; control2X: 29.4; control2Y: 244.801; x: 29.4; y: 244.801 }
+ PathCubic { control1X: 29.4; control1Y: 244.801; control2X: 35; control2Y: 241.601; x: 43.8; y: 245.201 }
+ PathCubic { control1X: 43.8; control1Y: 245.201; control2X: 46.175; control2Y: 244.399; x: 46.6; y: 240.401 }
+ PathCubic { control1X: 47.1; control1Y: 235.701; control2X: 50.2; control2Y: 232.001; x: 52.2; y: 230.001 }
+ PathCubic { control1X: 54.2; control1Y: 228.001; control2X: 63.8; control2Y: 215.201; x: 62.6; y: 214.801 }
+ PathCubic { control1X: 61.4; control1Y: 214.401; control2X: -5.4; control2Y: 222.801; x: -5.4; y: 222.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ff727f"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -9.8; y: 174.401 }
+ PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -12.6; control2Y: 196.801; x: -9.4; y: 205.201 }
+ PathCubic { control1X: -6.2; control1Y: 213.601; control2X: -7; control2Y: 215.601; x: -7.8; y: 219.601 }
+ PathCubic { control1X: -8.6; control1Y: 223.601; control2X: -4.2; control2Y: 233.601; x: 1.4; y: 239.601 }
+ PathLine { x: 13.4; y: 241.201 }
+ PathCubic { control1X: 13.4; control1Y: 241.201; control2X: 28.6; control2Y: 237.601; x: 37.8; y: 240.401 }
+ PathCubic { control1X: 37.8; control1Y: 240.401; control2X: 46.794; control2Y: 241.744; x: 50.2; y: 226.801 }
+ PathCubic { control1X: 50.2; control1Y: 226.801; control2X: 55; control2Y: 220.401; x: 62.2; y: 217.601 }
+ PathCubic { control1X: 69.4; control1Y: 214.801; control2X: 76.6; control2Y: 173.201; x: 72.6; y: 165.201 }
+ PathCubic { control1X: 68.6; control1Y: 157.201; control2X: 54.2; control2Y: 152.801; x: 38.2; y: 168.401 }
+ PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -8.2; y: 249.201 }
+ PathCubic { control1X: -8.2; control1Y: 249.201; control2X: -9; control2Y: 247.201; x: -13.4; y: 246.801 }
+ PathCubic { control1X: -13.4; control1Y: 246.801; control2X: -35.8; control2Y: 243.201; x: -44.2; y: 230.801 }
+ PathCubic { control1X: -44.2; control1Y: 230.801; control2X: -51; control2Y: 225.201; x: -46.6; y: 236.801 }
+ PathCubic { control1X: -46.6; control1Y: 236.801; control2X: -36.2; control2Y: 257.201; x: -29.4; y: 260.001 }
+ PathCubic { control1X: -29.4; control1Y: 260.001; control2X: -13; control2Y: 264.001; x: -8.2; y: 249.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cc3f4c"
+ strokeWidth: -1
+ PathMove { x: 71.742; y: 185.229 }
+ PathCubic { control1X: 72.401; control1Y: 177.323; control2X: 74.354; control2Y: 168.709; x: 72.6; y: 165.201 }
+ PathCubic { control1X: 66.154; control1Y: 152.307; control2X: 49.181; control2Y: 157.695; x: 38.2; y: 168.401 }
+ PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 }
+ PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -11.545; control2Y: 188.364; x: -10.705; y: 198.376 }
+ PathCubic { control1X: -10.705; control1Y: 198.376; control2X: 26.6; control2Y: 186.801; x: 27.4; y: 192.401 }
+ PathCubic { control1X: 27.4; control1Y: 192.401; control2X: 29; control2Y: 189.201; x: 38.2; y: 189.201 }
+ PathCubic { control1X: 47.4; control1Y: 189.201; control2X: 70.142; control2Y: 188.029; x: 71.742; y: 185.229 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a51926"
+ strokeWidth: 2
+ PathMove { x: 28.6; y: 175.201 }
+ PathCubic { control1X: 28.6; control1Y: 175.201; control2X: 33.4; control2Y: 180.001; x: 29.8; y: 189.601 }
+ PathCubic { control1X: 29.8; control1Y: 189.601; control2X: 15.4; control2Y: 205.601; x: 17.4; y: 219.601 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -19.4; y: 260.001 }
+ PathCubic { control1X: -19.4; control1Y: 260.001; control2X: -23.8; control2Y: 247.201; x: -15; y: 254.001 }
+ PathCubic { control1X: -15; control1Y: 254.001; control2X: -10.2; control2Y: 256.001; x: -11.4; y: 257.601 }
+ PathCubic { control1X: -12.6; control1Y: 259.201; control2X: -18.2; control2Y: 263.201; x: -19.4; y: 260.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -14.36; y: 261.201 }
+ PathCubic { control1X: -14.36; control1Y: 261.201; control2X: -17.88; control2Y: 250.961; x: -10.84; y: 256.401 }
+ PathCubic { control1X: -10.84; control1Y: 256.401; control2X: -6.419; control2Y: 258.849; x: -7.96; y: 259.281 }
+ PathCubic { control1X: -12.52; control1Y: 260.561; control2X: -7.96; control2Y: 263.121; x: -14.36; y: 261.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -9.56; y: 261.201 }
+ PathCubic { control1X: -9.56; control1Y: 261.201; control2X: -13.08; control2Y: 250.961; x: -6.04; y: 256.401 }
+ PathCubic { control1X: -6.04; control1Y: 256.401; control2X: -1.665; control2Y: 258.711; x: -3.16; y: 259.281 }
+ PathCubic { control1X: -6.52; control1Y: 260.561; control2X: -3.16; control2Y: 263.121; x: -9.56; y: 261.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -2.96; y: 261.401 }
+ PathCubic { control1X: -2.96; control1Y: 261.401; control2X: -6.48; control2Y: 251.161; x: 0.56; y: 256.601 }
+ PathCubic { control1X: 0.56; control1Y: 256.601; control2X: 4.943; control2Y: 258.933; x: 3.441; y: 259.481 }
+ PathCubic { control1X: 0.48; control1Y: 260.561; control2X: 3.441; control2Y: 263.321; x: -2.96; y: 261.401 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: 3.52; y: 261.321 }
+ PathCubic { control1X: 3.52; control1Y: 261.321; control2X: 0; control2Y: 251.081; x: 7.041; y: 256.521 }
+ PathCubic { control1X: 7.041; control1Y: 256.521; control2X: 10.881; control2Y: 258.121; x: 9.921; y: 259.401 }
+ PathCubic { control1X: 8.961; control1Y: 260.681; control2X: 9.921; control2Y: 263.241; x: 3.52; y: 261.321 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: 10.2; y: 262.001 }
+ PathCubic { control1X: 10.2; control1Y: 262.001; control2X: 5.4; control2Y: 249.601; x: 14.6; y: 256.001 }
+ PathCubic { control1X: 14.6; control1Y: 256.001; control2X: 19.4; control2Y: 258.001; x: 18.2; y: 259.601 }
+ PathCubic { control1X: 17; control1Y: 261.201; control2X: 18.2; control2Y: 264.401; x: 10.2; y: 262.001 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ PathMove { x: -18.2; y: 244.801 }
+ PathCubic { control1X: -18.2; control1Y: 244.801; control2X: -5; control2Y: 242.001; x: 1; y: 245.201 }
+ PathCubic { control1X: 1; control1Y: 245.201; control2X: 7; control2Y: 246.401; x: 8.2; y: 246.001 }
+ PathCubic { control1X: 9.4; control1Y: 245.601; control2X: 12.6; control2Y: 245.201; x: 12.6; y: 245.201 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ PathMove { x: 15.8; y: 253.601 }
+ PathCubic { control1X: 15.8; control1Y: 253.601; control2X: 27.8; control2Y: 240.001; x: 39.8; y: 244.401 }
+ PathCubic { control1X: 46.816; control1Y: 246.974; control2X: 45.8; control2Y: 243.601; x: 46.6; y: 240.801 }
+ PathCubic { control1X: 47.4; control1Y: 238.001; control2X: 47.6; control2Y: 233.801; x: 52.6; y: 230.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: 33; y: 237.601 }
+ PathCubic { control1X: 33; control1Y: 237.601; control2X: 29; control2Y: 226.801; x: 26.2; y: 239.601 }
+ PathCubic { control1X: 23.4; control1Y: 252.401; control2X: 20.2; control2Y: 256.001; x: 18.6; y: 258.801 }
+ PathCubic { control1X: 18.6; control1Y: 258.801; control2X: 18.6; control2Y: 264.001; x: 27; y: 263.601 }
+ PathCubic { control1X: 27; control1Y: 263.601; control2X: 37.8; control2Y: 263.201; x: 38.2; y: 260.401 }
+ PathCubic { control1X: 38.6; control1Y: 257.601; control2X: 37; control2Y: 246.001; x: 33; y: 237.601 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ PathMove { x: 47; y: 244.801 }
+ PathCubic { control1X: 47; control1Y: 244.801; control2X: 50.6; control2Y: 242.401; x: 53; y: 243.601 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ PathMove { x: 53.5; y: 228.401 }
+ PathCubic { control1X: 53.5; control1Y: 228.401; control2X: 56.4; control2Y: 223.501; x: 61.2; y: 222.701 }
+ }
+
+ ShapePath {
+ fillColor: "#b2b2b2"
+ strokeWidth: -1
+ PathMove { x: -25.8; y: 265.201 }
+ PathCubic { control1X: -25.8; control1Y: 265.201; control2X: -7.8; control2Y: 268.401; x: -3.4; y: 266.801 }
+ PathCubic { control1X: -3.4; control1Y: 266.801; control2X: 5.4; control2Y: 266.801; x: -3; y: 268.801 }
+ PathCubic { control1X: -3; control1Y: 268.801; control2X: -15.8; control2Y: 268.801; x: -23.8; y: 267.601 }
+ PathCubic { control1X: -23.8; control1Y: 267.601; control2X: -35.4; control2Y: 262.001; x: -25.8; y: 265.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -11.8; y: 172.001 }
+ PathCubic { control1X: -11.8; control1Y: 172.001; control2X: 5.8; control2Y: 172.001; x: 7.8; y: 172.801 }
+ PathCubic { control1X: 7.8; control1Y: 172.801; control2X: 15; control2Y: 203.601; x: 11.4; y: 211.201 }
+ PathCubic { control1X: 11.4; control1Y: 211.201; control2X: 10.2; control2Y: 214.001; x: 7.4; y: 208.401 }
+ PathCubic { control1X: 7.4; control1Y: 208.401; control2X: -11; control2Y: 175.601; x: -14.2; y: 173.601 }
+ PathCubic { control1X: -17.4; control1Y: 171.601; control2X: -13; control2Y: 172.001; x: -11.8; y: 172.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -88.9; y: 169.301 }
+ PathCubic { control1X: -88.9; control1Y: 169.301; control2X: -80; control2Y: 171.001; x: -67.4; y: 173.601 }
+ PathCubic { control1X: -67.4; control1Y: 173.601; control2X: -62.6; control2Y: 196.001; x: -59.4; y: 200.801 }
+ PathCubic { control1X: -56.2; control1Y: 205.601; control2X: -59.8; control2Y: 205.601; x: -63.4; y: 202.801 }
+ PathCubic { control1X: -67; control1Y: 200.001; control2X: -81.8; control2Y: 186.001; x: -83.8; y: 181.601 }
+ PathCubic { control1X: -85.8; control1Y: 177.201; control2X: -88.9; control2Y: 169.301; x: -88.9; y: 169.301 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -67.039; y: 173.818 }
+ PathCubic { control1X: -67.039; control1Y: 173.818; control2X: -61.239; control2Y: 175.366; x: -60.23; y: 177.581 }
+ PathCubic { control1X: -59.222; control1Y: 179.795; control2X: -61.432; control2Y: 183.092; x: -61.432; y: 183.092 }
+ PathCubic { control1X: -61.432; control1Y: 183.092; control2X: -62.432; control2Y: 186.397; x: -63.634; y: 184.235 }
+ PathCubic { control1X: -64.836; control1Y: 182.072; control2X: -67.708; control2Y: 174.412; x: -67.039; y: 173.818 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -67; y: 173.601 }
+ PathCubic { control1X: -67; control1Y: 173.601; control2X: -63.4; control2Y: 178.801; x: -59.8; y: 178.801 }
+ PathCubic { control1X: -56.2; control1Y: 178.801; control2X: -55.818; control2Y: 178.388; x: -53; y: 179.001 }
+ PathCubic { control1X: -48.4; control1Y: 180.001; control2X: -48.8; control2Y: 178.001; x: -42.2; y: 179.201 }
+ PathCubic { control1X: -39.56; control1Y: 179.681; control2X: -37; control2Y: 178.801; x: -34.2; y: 180.001 }
+ PathCubic { control1X: -31.4; control1Y: 181.201; control2X: -28.2; control2Y: 180.401; x: -27; y: 178.401 }
+ PathCubic { control1X: -25.8; control1Y: 176.401; control2X: -21; control2Y: 172.201; x: -21; y: 172.201 }
+ PathCubic { control1X: -21; control1Y: 172.201; control2X: -33.8; control2Y: 174.001; x: -36.6; y: 174.801 }
+ PathCubic { control1X: -36.6; control1Y: 174.801; control2X: -59; control2Y: 176.001; x: -67; y: 173.601 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -22.4; y: 173.801 }
+ PathCubic { control1X: -22.4; control1Y: 173.801; control2X: -28.85; control2Y: 177.301; x: -29.25; y: 179.701 }
+ PathCubic { control1X: -29.65; control1Y: 182.101; control2X: -24; control2Y: 185.801; x: -24; y: 185.801 }
+ PathCubic { control1X: -24; control1Y: 185.801; control2X: -21.25; control2Y: 190.401; x: -20.65; y: 188.001 }
+ PathCubic { control1X: -20.05; control1Y: 185.601; control2X: -21.6; control2Y: 174.201; x: -22.4; y: 173.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -59.885; y: 179.265 }
+ PathCubic { control1X: -59.885; control1Y: 179.265; control2X: -52.878; control2Y: 190.453; x: -52.661; y: 179.242 }
+ PathCubic { control1X: -52.661; control1Y: 179.242; control2X: -52.104; control2Y: 177.984; x: -53.864; y: 177.962 }
+ PathCubic { control1X: -59.939; control1Y: 177.886; control2X: -58.418; control2Y: 173.784; x: -59.885; y: 179.265 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -52.707; y: 179.514 }
+ PathCubic { control1X: -52.707; control1Y: 179.514; control2X: -44.786; control2Y: 190.701; x: -45.422; y: 179.421 }
+ PathCubic { control1X: -45.422; control1Y: 179.421; control2X: -45.415; control2Y: 179.089; x: -47.168; y: 178.936 }
+ PathCubic { control1X: -51.915; control1Y: 178.522; control2X: -51.57; control2Y: 174.004; x: -52.707; y: 179.514 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -45.494; y: 179.522 }
+ PathCubic { control1X: -45.494; control1Y: 179.522; control2X: -37.534; control2Y: 190.15; x: -38.203; y: 180.484 }
+ PathCubic { control1X: -38.203; control1Y: 180.484; control2X: -38.084; control2Y: 179.251; x: -39.738; y: 178.95 }
+ PathCubic { control1X: -43.63; control1Y: 178.244; control2X: -43.841; control2Y: 174.995; x: -45.494; y: 179.522 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -38.618; y: 179.602 }
+ PathCubic { control1X: -38.618; control1Y: 179.602; control2X: -30.718; control2Y: 191.163; x: -30.37; y: 181.382 }
+ PathCubic { control1X: -30.37; control1Y: 181.382; control2X: -28.726; control2Y: 180.004; x: -30.472; y: 179.782 }
+ PathCubic { control1X: -36.29; control1Y: 179.042; control2X: -35.492; control2Y: 174.588; x: -38.618; y: 179.602 }
+ }
+
+ ShapePath {
+ fillColor: "#e5e5b2"
+ strokeWidth: -1
+ PathMove { x: -74.792; y: 183.132 }
+ PathLine { x: -82.45; y: 181.601 }
+ PathCubic { control1X: -85.05; control1Y: 176.601; control2X: -87.15; control2Y: 170.451; x: -87.15; y: 170.451 }
+ PathCubic { control1X: -87.15; control1Y: 170.451; control2X: -80.8; control2Y: 171.451; x: -68.3; y: 174.251 }
+ PathCubic { control1X: -68.3; control1Y: 174.251; control2X: -67.424; control2Y: 177.569; x: -65.952; y: 183.364 }
+ PathLine { x: -74.792; y: 183.132 }
+ }
+
+ ShapePath {
+ fillColor: "#e5e5b2"
+ strokeWidth: -1
+ PathMove { x: -9.724; y: 178.47 }
+ PathCubic { control1X: -11.39; control1Y: 175.964; control2X: -12.707; control2Y: 174.206; x: -13.357; y: 173.8 }
+ PathCubic { control1X: -16.37; control1Y: 171.917; control2X: -12.227; control2Y: 172.294; x: -11.098; y: 172.294 }
+ PathCubic { control1X: -11.098; control1Y: 172.294; control2X: 5.473; control2Y: 172.294; x: 7.356; y: 173.047 }
+ PathCubic { control1X: 7.356; control1Y: 173.047; control2X: 7.88; control2Y: 175.289; x: 8.564; y: 178.68 }
+ PathCubic { control1X: 8.564; control1Y: 178.68; control2X: -1.524; control2Y: 176.67; x: -9.724; y: 178.47 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 43.88; y: 40.321 }
+ PathCubic { control1X: 71.601; control1Y: 44.281; control2X: 97.121; control2Y: 8.641; x: 98.881; y: -1.04 }
+ PathCubic { control1X: 100.641; control1Y: -10.72; control2X: 90.521; control2Y: -22.6; x: 90.521; y: -22.6 }
+ PathCubic { control1X: 91.841; control1Y: -25.68; control2X: 87.001; control2Y: -39.76; x: 81.721; y: -49 }
+ PathCubic { control1X: 76.441; control1Y: -58.24; control2X: 60.54; control2Y: -57.266; x: 43; y: -58.24 }
+ PathCubic { control1X: 27.16; control1Y: -59.12; control2X: 8.68; control2Y: -35.8; x: 7.36; y: -34.04 }
+ PathCubic { control1X: 6.04; control1Y: -32.28; control2X: 12.2; control2Y: 6.001; x: 13.52; y: 11.721 }
+ PathCubic { control1X: 14.84; control1Y: 17.441; control2X: 12.2; control2Y: 43.841; x: 12.2; y: 43.841 }
+ PathCubic { control1X: 46.44; control1Y: 34.741; control2X: 16.16; control2Y: 36.361; x: 43.88; y: 40.321 }
+ }
+
+ ShapePath {
+ fillColor: "#ea8e51"
+ strokeWidth: -1
+ PathMove { x: 8.088; y: -33.392 }
+ PathCubic { control1X: 6.792; control1Y: -31.664; control2X: 12.84; control2Y: 5.921; x: 14.136; y: 11.537 }
+ PathCubic { control1X: 15.432; control1Y: 17.153; control2X: 12.84; control2Y: 43.073; x: 12.84; y: 43.073 }
+ PathCubic { control1X: 45.512; control1Y: 34.193; control2X: 16.728; control2Y: 35.729; x: 43.944; y: 39.617 }
+ PathCubic { control1X: 71.161; control1Y: 43.505; control2X: 96.217; control2Y: 8.513; x: 97.945; y: -0.992 }
+ PathCubic { control1X: 99.673; control1Y: -10.496; control2X: 89.737; control2Y: -22.16; x: 89.737; y: -22.16 }
+ PathCubic { control1X: 91.033; control1Y: -25.184; control2X: 86.281; control2Y: -39.008; x: 81.097; y: -48.08 }
+ PathCubic { control1X: 75.913; control1Y: -57.152; control2X: 60.302; control2Y: -56.195; x: 43.08; y: -57.152 }
+ PathCubic { control1X: 27.528; control1Y: -58.016; control2X: 9.384; control2Y: -35.12; x: 8.088; y: -33.392 }
+ }
+
+ ShapePath {
+ fillColor: "#efaa7c"
+ strokeWidth: -1
+ PathMove { x: 8.816; y: -32.744 }
+ PathCubic { control1X: 7.544; control1Y: -31.048; control2X: 13.48; control2Y: 5.841; x: 14.752; y: 11.353 }
+ PathCubic { control1X: 16.024; control1Y: 16.865; control2X: 13.48; control2Y: 42.305; x: 13.48; y: 42.305 }
+ PathCubic { control1X: 44.884; control1Y: 33.145; control2X: 17.296; control2Y: 35.097; x: 44.008; y: 38.913 }
+ PathCubic { control1X: 70.721; control1Y: 42.729; control2X: 95.313; control2Y: 8.385; x: 97.009; y: -0.944 }
+ PathCubic { control1X: 98.705; control1Y: -10.272; control2X: 88.953; control2Y: -21.72; x: 88.953; y: -21.72 }
+ PathCubic { control1X: 90.225; control1Y: -24.688; control2X: 85.561; control2Y: -38.256; x: 80.473; y: -47.16 }
+ PathCubic { control1X: 75.385; control1Y: -56.064; control2X: 60.063; control2Y: -55.125; x: 43.16; y: -56.064 }
+ PathCubic { control1X: 27.896; control1Y: -56.912; control2X: 10.088; control2Y: -34.44; x: 8.816; y: -32.744 }
+ }
+
+ ShapePath {
+ fillColor: "#f4c6a8"
+ strokeWidth: -1
+ PathMove { x: 9.544; y: -32.096 }
+ PathCubic { control1X: 8.296; control1Y: -30.432; control2X: 14.12; control2Y: 5.761; x: 15.368; y: 11.169 }
+ PathCubic { control1X: 16.616; control1Y: 16.577; control2X: 14.12; control2Y: 41.537; x: 14.12; y: 41.537 }
+ PathCubic { control1X: 43.556; control1Y: 32.497; control2X: 17.864; control2Y: 34.465; x: 44.072; y: 38.209 }
+ PathCubic { control1X: 70.281; control1Y: 41.953; control2X: 94.409; control2Y: 8.257; x: 96.073; y: -0.895 }
+ PathCubic { control1X: 97.737; control1Y: -10.048; control2X: 88.169; control2Y: -21.28; x: 88.169; y: -21.28 }
+ PathCubic { control1X: 89.417; control1Y: -24.192; control2X: 84.841; control2Y: -37.504; x: 79.849; y: -46.24 }
+ PathCubic { control1X: 74.857; control1Y: -54.976; control2X: 59.824; control2Y: -54.055; x: 43.24; y: -54.976 }
+ PathCubic { control1X: 28.264; control1Y: -55.808; control2X: 10.792; control2Y: -33.76; x: 9.544; y: -32.096 }
+ }
+
+ ShapePath {
+ fillColor: "#f9e2d3"
+ strokeWidth: -1
+ PathMove { x: 10.272; y: -31.448 }
+ PathCubic { control1X: 9.048; control1Y: -29.816; control2X: 14.76; control2Y: 5.681; x: 15.984; y: 10.985 }
+ PathCubic { control1X: 17.208; control1Y: 16.289; control2X: 14.76; control2Y: 40.769; x: 14.76; y: 40.769 }
+ PathCubic { control1X: 42.628; control1Y: 31.849; control2X: 18.432; control2Y: 33.833; x: 44.136; y: 37.505 }
+ PathCubic { control1X: 69.841; control1Y: 41.177; control2X: 93.505; control2Y: 8.129; x: 95.137; y: -0.848 }
+ PathCubic { control1X: 96.769; control1Y: -9.824; control2X: 87.385; control2Y: -20.84; x: 87.385; y: -20.84 }
+ PathCubic { control1X: 88.609; control1Y: -23.696; control2X: 84.121; control2Y: -36.752; x: 79.225; y: -45.32 }
+ PathCubic { control1X: 74.329; control1Y: -53.888; control2X: 59.585; control2Y: -52.985; x: 43.32; y: -53.888 }
+ PathCubic { control1X: 28.632; control1Y: -54.704; control2X: 11.496; control2Y: -33.08; x: 10.272; y: -31.448 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 44.2; y: 36.8 }
+ PathCubic { control1X: 69.4; control1Y: 40.4; control2X: 92.601; control2Y: 8; x: 94.201; y: -0.8 }
+ PathCubic { control1X: 95.801; control1Y: -9.6; control2X: 86.601; control2Y: -20.4; x: 86.601; y: -20.4 }
+ PathCubic { control1X: 87.801; control1Y: -23.2; control2X: 83.4; control2Y: -36; x: 78.6; y: -44.4 }
+ PathCubic { control1X: 73.8; control1Y: -52.8; control2X: 59.346; control2Y: -51.914; x: 43.4; y: -52.8 }
+ PathCubic { control1X: 29; control1Y: -53.6; control2X: 12.2; control2Y: -32.4; x: 11; y: -30.8 }
+ PathCubic { control1X: 9.8; control1Y: -29.2; control2X: 15.4; control2Y: 5.6; x: 16.6; y: 10.8 }
+ PathCubic { control1X: 17.8; control1Y: 16; control2X: 15.4; control2Y: 40; x: 15.4; y: 40 }
+ PathCubic { control1X: 40.9; control1Y: 31.4; control2X: 19; control2Y: 33.2; x: 44.2; y: 36.8 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 90.601; y: 2.8 }
+ PathCubic { control1X: 90.601; control1Y: 2.8; control2X: 62.8; control2Y: 10.4; x: 51.2; y: 8.8 }
+ PathCubic { control1X: 51.2; control1Y: 8.8; control2X: 35.4; control2Y: 2.2; x: 26.6; y: 24 }
+ PathCubic { control1X: 26.6; control1Y: 24; control2X: 23; control2Y: 31.2; x: 21; y: 33.2 }
+ PathCubic { control1X: 19; control1Y: 35.2; control2X: 90.601; control2Y: 2.8; x: 90.601; y: 2.8 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 94.401; y: 0.6 }
+ PathCubic { control1X: 94.401; control1Y: 0.6; control2X: 65.4; control2Y: 12.8; x: 55.4; y: 12.4 }
+ PathCubic { control1X: 55.4; control1Y: 12.4; control2X: 39; control2Y: 7.8; x: 30.6; y: 22.4 }
+ PathCubic { control1X: 30.6; control1Y: 22.4; control2X: 22.2; control2Y: 31.6; x: 19; y: 33.2 }
+ PathCubic { control1X: 19; control1Y: 33.2; control2X: 18.6; control2Y: 34.8; x: 25; y: 30.8 }
+ PathLine { x: 35.4; y: 36 }
+ PathCubic { control1X: 35.4; control1Y: 36; control2X: 50.2; control2Y: 45.6; x: 59.8; y: 29.6 }
+ PathCubic { control1X: 59.8; control1Y: 29.6; control2X: 63.8; control2Y: 18.4; x: 63.8; y: 16.4 }
+ PathCubic { control1X: 63.8; control1Y: 14.4; control2X: 85; control2Y: 8.8; x: 86.601; y: 8.4 }
+ PathCubic { control1X: 88.201; control1Y: 8; control2X: 94.801; control2Y: 3.8; x: 94.401; y: 0.6 }
+ }
+
+ ShapePath {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ PathMove { x: 47; y: 36.514 }
+ PathCubic { control1X: 40.128; control1Y: 36.514; control2X: 31.755; control2Y: 32.649; x: 31.755; y: 26.4 }
+ PathCubic { control1X: 31.755; control1Y: 20.152; control2X: 40.128; control2Y: 13.887; x: 47; y: 13.887 }
+ PathCubic { control1X: 53.874; control1Y: 13.887; control2X: 59.446; control2Y: 18.952; x: 59.446; y: 25.2 }
+ PathCubic { control1X: 59.446; control1Y: 31.449; control2X: 53.874; control2Y: 36.514; x: 47; y: 36.514 }
+ }
+
+ ShapePath {
+ fillColor: "#659900"
+ strokeWidth: -1
+ PathMove { x: 43.377; y: 19.83 }
+ PathCubic { control1X: 38.531; control1Y: 20.552; control2X: 33.442; control2Y: 22.055; x: 33.514; y: 21.839 }
+ PathCubic { control1X: 35.054; control1Y: 17.22; control2X: 41.415; control2Y: 13.887; x: 47; y: 13.887 }
+ PathCubic { control1X: 51.296; control1Y: 13.887; control2X: 55.084; control2Y: 15.865; x: 57.32; y: 18.875 }
+ PathCubic { control1X: 57.32; control1Y: 18.875; control2X: 52.004; control2Y: 18.545; x: 43.377; y: 19.83 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 55.4; y: 19.6 }
+ PathCubic { control1X: 55.4; control1Y: 19.6; control2X: 51; control2Y: 16.4; x: 51; y: 18.6 }
+ PathCubic { control1X: 51; control1Y: 18.6; control2X: 54.6; control2Y: 23; x: 55.4; y: 19.6 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 45.4; y: 27.726 }
+ PathCubic { control1X: 42.901; control1Y: 27.726; control2X: 40.875; control2Y: 25.7; x: 40.875; y: 23.2 }
+ PathCubic { control1X: 40.875; control1Y: 20.701; control2X: 42.901; control2Y: 18.675; x: 45.4; y: 18.675 }
+ PathCubic { control1X: 47.9; control1Y: 18.675; control2X: 49.926; control2Y: 20.701; x: 49.926; y: 23.2 }
+ PathCubic { control1X: 49.926; control1Y: 25.7; control2X: 47.9; control2Y: 27.726; x: 45.4; y: 27.726 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: -58.6; y: 14.4 }
+ PathCubic { control1X: -58.6; control1Y: 14.4; control2X: -61.8; control2Y: -6.8; x: -59.4; y: -11.2 }
+ PathCubic { control1X: -59.4; control1Y: -11.2; control2X: -48.6; control2Y: -21.2; x: -49; y: -24.8 }
+ PathCubic { control1X: -49; control1Y: -24.8; control2X: -49.4; control2Y: -42.8; x: -50.6; y: -43.6 }
+ PathCubic { control1X: -51.8; control1Y: -44.4; control2X: -59.4; control2Y: -50.4; x: -65.4; y: -44 }
+ PathCubic { control1X: -65.4; control1Y: -44; control2X: -75.8; control2Y: -26; x: -75; y: -19.6 }
+ PathLine { x: -75; y: -17.6 }
+ PathCubic { control1X: -75; control1Y: -17.6; control2X: -82.6; control2Y: -18; x: -84.2; y: -16 }
+ PathCubic { control1X: -84.2; control1Y: -16; control2X: -85.4; control2Y: -10.8; x: -86.6; y: -10.4 }
+ PathCubic { control1X: -86.6; control1Y: -10.4; control2X: -89.4; control2Y: -8; x: -87.4; y: -5.2 }
+ PathCubic { control1X: -87.4; control1Y: -5.2; control2X: -89.4; control2Y: -2.8; x: -89; y: 1.2 }
+ PathLine { x: -81.4; y: 5.2 }
+ PathCubic { control1X: -81.4; control1Y: 5.2; control2X: -79.4; control2Y: 19.6; x: -68.6; y: 24.8 }
+ PathCubic { control1X: -63.764; control1Y: 27.129; control2X: -60.6; control2Y: 20.4; x: -58.6; y: 14.4 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: -59.6; y: 12.56 }
+ PathCubic { control1X: -59.6; control1Y: 12.56; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 }
+ PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 }
+ PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 }
+ PathCubic { control1X: -53.48; control1Y: -40.36; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 }
+ PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 }
+ PathLine { x: -74.36; y: -16.24 }
+ PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 }
+ PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 }
+ PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 }
+ PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 }
+ PathLine { x: -80.12; y: 4.28 }
+ PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 }
+ PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.96; x: -59.6; y: 12.56 }
+ }
+
+ ShapePath {
+ fillColor: "#eb955c"
+ strokeWidth: -1
+ PathMove { x: -51.05; y: -42.61 }
+ PathCubic { control1X: -52.14; control1Y: -43.47; control2X: -59.63; control2Y: -49.24; x: -65.48; y: -43 }
+ PathCubic { control1X: -65.48; control1Y: -43; control2X: -75.62; control2Y: -25.45; x: -74.84; y: -19.21 }
+ PathLine { x: -74.84; y: -17.26 }
+ PathCubic { control1X: -74.84; control1Y: -17.26; control2X: -82.25; control2Y: -17.65; x: -83.81; y: -15.7 }
+ PathCubic { control1X: -83.81; control1Y: -15.7; control2X: -84.98; control2Y: -10.63; x: -86.15; y: -10.24 }
+ PathCubic { control1X: -86.15; control1Y: -10.24; control2X: -88.88; control2Y: -7.9; x: -86.93; y: -5.17 }
+ PathCubic { control1X: -86.93; control1Y: -5.17; control2X: -88.88; control2Y: -2.83; x: -88.49; y: 1.07 }
+ PathLine { x: -81.08; y: 4.97 }
+ PathCubic { control1X: -81.08; control1Y: 4.97; control2X: -79.13; control2Y: 19.01; x: -68.6; y: 24.08 }
+ PathCubic { control1X: -63.886; control1Y: 26.35; control2X: -60.8; control2Y: 19.79; x: -58.85; y: 13.94 }
+ PathCubic { control1X: -58.85; control1Y: 13.94; control2X: -61.97; control2Y: -6.73; x: -59.63; y: -11.02 }
+ PathCubic { control1X: -59.63; control1Y: -11.02; control2X: -49.1; control2Y: -20.77; x: -49.49; y: -24.28 }
+ PathCubic { control1X: -49.49; control1Y: -24.28; control2X: -49.88; control2Y: -41.83; x: -51.05; y: -42.61 }
+ }
+
+ ShapePath {
+ fillColor: "#f2b892"
+ strokeWidth: -1
+ PathMove { x: -51.5; y: -41.62 }
+ PathCubic { control1X: -52.48; control1Y: -42.54; control2X: -59.86; control2Y: -48.08; x: -65.56; y: -42 }
+ PathCubic { control1X: -65.56; control1Y: -42; control2X: -75.44; control2Y: -24.9; x: -74.68; y: -18.82 }
+ PathLine { x: -74.68; y: -16.92 }
+ PathCubic { control1X: -74.68; control1Y: -16.92; control2X: -81.9; control2Y: -17.3; x: -83.42; y: -15.4 }
+ PathCubic { control1X: -83.42; control1Y: -15.4; control2X: -84.56; control2Y: -10.46; x: -85.7; y: -10.08 }
+ PathCubic { control1X: -85.7; control1Y: -10.08; control2X: -88.36; control2Y: -7.8; x: -86.46; y: -5.14 }
+ PathCubic { control1X: -86.46; control1Y: -5.14; control2X: -88.36; control2Y: -2.86; x: -87.98; y: 0.94 }
+ PathLine { x: -80.76; y: 4.74 }
+ PathCubic { control1X: -80.76; control1Y: 4.74; control2X: -78.86; control2Y: 18.42; x: -68.6; y: 23.36 }
+ PathCubic { control1X: -64.006; control1Y: 25.572; control2X: -61; control2Y: 19.18; x: -59.1; y: 13.48 }
+ PathCubic { control1X: -59.1; control1Y: 13.48; control2X: -62.14; control2Y: -6.66; x: -59.86; y: -10.84 }
+ PathCubic { control1X: -59.86; control1Y: -10.84; control2X: -49.6; control2Y: -20.34; x: -49.98; y: -23.76 }
+ PathCubic { control1X: -49.98; control1Y: -23.76; control2X: -50.36; control2Y: -40.86; x: -51.5; y: -41.62 }
+ }
+
+ ShapePath {
+ fillColor: "#f8dcc8"
+ strokeWidth: -1
+ PathMove { x: -51.95; y: -40.63 }
+ PathCubic { control1X: -52.82; control1Y: -41.61; control2X: -60.09; control2Y: -46.92; x: -65.64; y: -41 }
+ PathCubic { control1X: -65.64; control1Y: -41; control2X: -75.26; control2Y: -24.35; x: -74.52; y: -18.43 }
+ PathLine { x: -74.52; y: -16.58 }
+ PathCubic { control1X: -74.52; control1Y: -16.58; control2X: -81.55; control2Y: -16.95; x: -83.03; y: -15.1 }
+ PathCubic { control1X: -83.03; control1Y: -15.1; control2X: -84.14; control2Y: -10.29; x: -85.25; y: -9.92 }
+ PathCubic { control1X: -85.25; control1Y: -9.92; control2X: -87.84; control2Y: -7.7; x: -85.99; y: -5.11 }
+ PathCubic { control1X: -85.99; control1Y: -5.11; control2X: -87.84; control2Y: -2.89; x: -87.47; y: 0.81 }
+ PathLine { x: -80.44; y: 4.51 }
+ PathCubic { control1X: -80.44; control1Y: 4.51; control2X: -78.59; control2Y: 17.83; x: -68.6; y: 22.64 }
+ PathCubic { control1X: -64.127; control1Y: 24.794; control2X: -61.2; control2Y: 18.57; x: -59.35; y: 13.02 }
+ PathCubic { control1X: -59.35; control1Y: 13.02; control2X: -62.31; control2Y: -6.59; x: -60.09; y: -10.66 }
+ PathCubic { control1X: -60.09; control1Y: -10.66; control2X: -50.1; control2Y: -19.91; x: -50.47; y: -23.24 }
+ PathCubic { control1X: -50.47; control1Y: -23.24; control2X: -50.84; control2Y: -39.89; x: -51.95; y: -40.63 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: -59.6; y: 12.46 }
+ PathCubic { control1X: -59.6; control1Y: 12.46; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 }
+ PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 }
+ PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 }
+ PathCubic { control1X: -53.16; control1Y: -40.68; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 }
+ PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 }
+ PathLine { x: -74.36; y: -16.24 }
+ PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 }
+ PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 }
+ PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 }
+ PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 }
+ PathLine { x: -80.12; y: 4.28 }
+ PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 }
+ PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.86; x: -59.6; y: 12.46 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -62.7; y: 6.2 }
+ PathCubic { control1X: -62.7; control1Y: 6.2; control2X: -84.3; control2Y: -4; x: -85.2; y: -4.8 }
+ PathCubic { control1X: -85.2; control1Y: -4.8; control2X: -76.1; control2Y: 3.4; x: -75.3; y: 3.4 }
+ PathCubic { control1X: -74.5; control1Y: 3.4; control2X: -62.7; control2Y: 6.2; x: -62.7; y: 6.2 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -79.8; y: 0 }
+ PathCubic { control1X: -79.8; control1Y: 0; control2X: -61.4; control2Y: 3.6; x: -61.4; y: 8 }
+ PathCubic { control1X: -61.4; control1Y: 10.912; control2X: -61.643; control2Y: 24.331; x: -67; y: 22.8 }
+ PathCubic { control1X: -75.4; control1Y: 20.4; control2X: -71.8; control2Y: 6; x: -79.8; y: 0 }
+ }
+
+ ShapePath {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ PathMove { x: -71.4; y: 3.8 }
+ PathCubic { control1X: -71.4; control1Y: 3.8; control2X: -62.422; control2Y: 5.274; x: -61.4; y: 8 }
+ PathCubic { control1X: -60.8; control1Y: 9.6; control2X: -60.137; control2Y: 17.908; x: -65.6; y: 19 }
+ PathCubic { control1X: -70.152; control1Y: 19.911; control2X: -72.382; control2Y: 9.69; x: -71.4; y: 3.8 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 14.595; y: 46.349 }
+ PathCubic { control1X: 14.098; control1Y: 44.607; control2X: 15.409; control2Y: 44.738; x: 17.2; y: 44.2 }
+ PathCubic { control1X: 19.2; control1Y: 43.6; control2X: 31.4; control2Y: 39.8; x: 32.2; y: 37.2 }
+ PathCubic { control1X: 33; control1Y: 34.6; control2X: 46.2; control2Y: 39; x: 46.2; y: 39 }
+ PathCubic { control1X: 48; control1Y: 39.8; control2X: 52.4; control2Y: 42.4; x: 52.4; y: 42.4 }
+ PathCubic { control1X: 57.2; control1Y: 43.6; control2X: 63.8; control2Y: 44; x: 63.8; y: 44 }
+ PathCubic { control1X: 66.2; control1Y: 45; control2X: 69.6; control2Y: 47.8; x: 69.6; y: 47.8 }
+ PathCubic { control1X: 84.2; control1Y: 58; control2X: 96.601; control2Y: 50.8; x: 96.601; y: 50.8 }
+ PathCubic { control1X: 116.601; control1Y: 44.2; control2X: 110.601; control2Y: 27; x: 110.601; y: 27 }
+ PathCubic { control1X: 107.601; control1Y: 18; control2X: 110.801; control2Y: 14.6; x: 110.801; y: 14.6 }
+ PathCubic { control1X: 111.001; control1Y: 10.8; control2X: 118.201; control2Y: 17.2; x: 118.201; y: 17.2 }
+ PathCubic { control1X: 120.801; control1Y: 21.4; control2X: 121.601; control2Y: 26.4; x: 121.601; y: 26.4 }
+ PathCubic { control1X: 129.601; control1Y: 37.6; control2X: 126.201; control2Y: 19.8; x: 126.201; y: 19.8 }
+ PathCubic { control1X: 126.401; control1Y: 18.8; control2X: 123.601; control2Y: 15.2; x: 123.601; y: 14 }
+ PathCubic { control1X: 123.601; control1Y: 12.8; control2X: 121.801; control2Y: 9.4; x: 121.801; y: 9.4 }
+ PathCubic { control1X: 118.801; control1Y: 6; control2X: 121.201; control2Y: -1; x: 121.201; y: -1 }
+ PathCubic { control1X: 123.001; control1Y: -14.8; control2X: 120.801; control2Y: -13; x: 120.801; y: -13 }
+ PathCubic { control1X: 119.601; control1Y: -14.8; control2X: 110.401; control2Y: -4.8; x: 110.401; y: -4.8 }
+ PathCubic { control1X: 108.201; control1Y: -1.4; control2X: 102.201; control2Y: 0.2; x: 102.201; y: 0.2 }
+ PathCubic { control1X: 99.401; control1Y: 2; control2X: 96.001; control2Y: 0.6; x: 96.001; y: 0.6 }
+ PathCubic { control1X: 93.401; control1Y: 0.2; control2X: 87.801; control2Y: 7.2; x: 87.801; y: 7.2 }
+ PathCubic { control1X: 90.601; control1Y: 7; control2X: 93.001; control2Y: 11.4; x: 95.401; y: 11.6 }
+ PathCubic { control1X: 97.801; control1Y: 11.8; control2X: 99.601; control2Y: 9.2; x: 101.201; y: 8.6 }
+ PathCubic { control1X: 102.801; control1Y: 8; control2X: 105.601; control2Y: 13.8; x: 105.601; y: 13.8 }
+ PathCubic { control1X: 106.001; control1Y: 16.4; control2X: 100.401; control2Y: 21.2; x: 100.401; y: 21.2 }
+ PathCubic { control1X: 100.001; control1Y: 25.8; control2X: 98.401; control2Y: 24.2; x: 98.401; y: 24.2 }
+ PathCubic { control1X: 95.401; control1Y: 23.6; control2X: 94.201; control2Y: 27.4; x: 93.201; y: 32 }
+ PathCubic { control1X: 92.201; control1Y: 36.6; control2X: 88.001; control2Y: 37; x: 88.001; y: 37 }
+ PathCubic { control1X: 86.401; control1Y: 44.4; control2X: 85.2; control2Y: 41.4; x: 85.2; y: 41.4 }
+ PathCubic { control1X: 85; control1Y: 35.8; control2X: 79; control2Y: 41.6; x: 79; y: 41.6 }
+ PathCubic { control1X: 77.8; control1Y: 43.6; control2X: 73.2; control2Y: 41.4; x: 73.2; y: 41.4 }
+ PathCubic { control1X: 66.4; control1Y: 39.4; control2X: 68.8; control2Y: 37.4; x: 68.8; y: 37.4 }
+ PathCubic { control1X: 70.6; control1Y: 35.2; control2X: 81.8; control2Y: 37.4; x: 81.8; y: 37.4 }
+ PathCubic { control1X: 84; control1Y: 35.8; control2X: 76; control2Y: 31.8; x: 76; y: 31.8 }
+ PathCubic { control1X: 75.4; control1Y: 30; control2X: 76.4; control2Y: 25.6; x: 76.4; y: 25.6 }
+ PathCubic { control1X: 77.6; control1Y: 22.4; control2X: 84.4; control2Y: 16.8; x: 84.4; y: 16.8 }
+ PathCubic { control1X: 93.801; control1Y: 15.6; control2X: 91.001; control2Y: 14; x: 91.001; y: 14 }
+ PathCubic { control1X: 84.801; control1Y: 8.8; control2X: 79; control2Y: 16.4; x: 79; y: 16.4 }
+ PathCubic { control1X: 76.8; control1Y: 22.6; control2X: 59.4; control2Y: 37.6; x: 59.4; y: 37.6 }
+ PathCubic { control1X: 54.6; control1Y: 41; control2X: 57.2; control2Y: 34.2; x: 53.2; y: 37.6 }
+ PathCubic { control1X: 49.2; control1Y: 41; control2X: 28.6; control2Y: 32; x: 28.6; y: 32 }
+ PathCubic { control1X: 17.038; control1Y: 30.807; control2X: 14.306; control2Y: 46.549; x: 10.777; y: 43.429 }
+ PathCubic { control1X: 10.777; control1Y: 43.429; control2X: 16.195; control2Y: 51.949; x: 14.595; y: 46.349 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 209.401; y: -120 }
+ PathCubic { control1X: 209.401; control1Y: -120; control2X: 183.801; control2Y: -112; x: 181.001; y: -93.2 }
+ PathCubic { control1X: 181.001; control1Y: -93.2; control2X: 178.601; control2Y: -70.4; x: 199.001; y: -52.8 }
+ PathCubic { control1X: 199.001; control1Y: -52.8; control2X: 199.401; control2Y: -46.4; x: 201.401; y: -43.2 }
+ PathCubic { control1X: 201.401; control1Y: -43.2; control2X: 199.801; control2Y: -38.4; x: 218.601; y: -46 }
+ PathLine { x: 245.801; y: -54.4 }
+ PathCubic { control1X: 245.801; control1Y: -54.4; control2X: 252.201; control2Y: -56.8; x: 257.401; y: -65.6 }
+ PathCubic { control1X: 262.601; control1Y: -74.4; control2X: 277.801; control2Y: -93.2; x: 274.201; y: -118.4 }
+ PathCubic { control1X: 274.201; control1Y: -118.4; control2X: 275.401; control2Y: -129.6; x: 269.401; y: -130 }
+ PathCubic { control1X: 269.401; control1Y: -130; control2X: 261.001; control2Y: -131.6; x: 253.801; y: -124 }
+ PathCubic { control1X: 253.801; control1Y: -124; control2X: 247.001; control2Y: -120.8; x: 244.601; y: -121.2 }
+ PathLine { x: 209.401; y: -120 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 264.022; y: -120.99 }
+ PathCubic { control1X: 264.022; control1Y: -120.99; control2X: 266.122; control2Y: -129.92; x: 261.282; y: -125.08 }
+ PathCubic { control1X: 261.282; control1Y: -125.08; control2X: 254.242; control2Y: -119.36; x: 246.761; y: -119.36 }
+ PathCubic { control1X: 246.761; control1Y: -119.36; control2X: 232.241; control2Y: -117.16; x: 227.841; y: -103.96 }
+ PathCubic { control1X: 227.841; control1Y: -103.96; control2X: 223.881; control2Y: -77.12; x: 231.801; y: -71.4 }
+ PathCubic { control1X: 231.801; control1Y: -71.4; control2X: 236.641; control2Y: -63.92; x: 243.681; y: -70.52 }
+ PathCubic { control1X: 250.722; control1Y: -77.12; control2X: 266.222; control2Y: -107.35; x: 264.022; y: -120.99 }
+ }
+
+ ShapePath {
+ fillColor: "#323232"
+ strokeWidth: -1
+ PathMove { x: 263.648; y: -120.632 }
+ PathCubic { control1X: 263.648; control1Y: -120.632; control2X: 265.738; control2Y: -129.376; x: 260.986; y: -124.624 }
+ PathCubic { control1X: 260.986; control1Y: -124.624; control2X: 254.074; control2Y: -119.008; x: 246.729; y: -119.008 }
+ PathCubic { control1X: 246.729; control1Y: -119.008; control2X: 232.473; control2Y: -116.848; x: 228.153; y: -103.888 }
+ PathCubic { control1X: 228.153; control1Y: -103.888; control2X: 224.265; control2Y: -77.536; x: 232.041; y: -71.92 }
+ PathCubic { control1X: 232.041; control1Y: -71.92; control2X: 236.793; control2Y: -64.576; x: 243.705; y: -71.056 }
+ PathCubic { control1X: 250.618; control1Y: -77.536; control2X: 265.808; control2Y: -107.24; x: 263.648; y: -120.632 }
+ }
+
+ ShapePath {
+ fillColor: "#666666"
+ strokeWidth: -1
+ PathMove { x: 263.274; y: -120.274 }
+ PathCubic { control1X: 263.274; control1Y: -120.274; control2X: 265.354; control2Y: -128.832; x: 260.69; y: -124.168 }
+ PathCubic { control1X: 260.69; control1Y: -124.168; control2X: 253.906; control2Y: -118.656; x: 246.697; y: -118.656 }
+ PathCubic { control1X: 246.697; control1Y: -118.656; control2X: 232.705; control2Y: -116.536; x: 228.465; y: -103.816 }
+ PathCubic { control1X: 228.465; control1Y: -103.816; control2X: 224.649; control2Y: -77.952; x: 232.281; y: -72.44 }
+ PathCubic { control1X: 232.281; control1Y: -72.44; control2X: 236.945; control2Y: -65.232; x: 243.729; y: -71.592 }
+ PathCubic { control1X: 250.514; control1Y: -77.952; control2X: 265.394; control2Y: -107.13; x: 263.274; y: -120.274 }
+ }
+
+ ShapePath {
+ fillColor: "#999999"
+ strokeWidth: -1
+ PathMove { x: 262.9; y: -119.916 }
+ PathCubic { control1X: 262.9; control1Y: -119.916; control2X: 264.97; control2Y: -128.288; x: 260.394; y: -123.712 }
+ PathCubic { control1X: 260.394; control1Y: -123.712; control2X: 253.738; control2Y: -118.304; x: 246.665; y: -118.304 }
+ PathCubic { control1X: 246.665; control1Y: -118.304; control2X: 232.937; control2Y: -116.224; x: 228.777; y: -103.744 }
+ PathCubic { control1X: 228.777; control1Y: -103.744; control2X: 225.033; control2Y: -78.368; x: 232.521; y: -72.96 }
+ PathCubic { control1X: 232.521; control1Y: -72.96; control2X: 237.097; control2Y: -65.888; x: 243.753; y: -72.128 }
+ PathCubic { control1X: 250.41; control1Y: -78.368; control2X: 264.98; control2Y: -107.02; x: 262.9; y: -119.916 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 262.526; y: -119.558 }
+ PathCubic { control1X: 262.526; control1Y: -119.558; control2X: 264.586; control2Y: -127.744; x: 260.098; y: -123.256 }
+ PathCubic { control1X: 260.098; control1Y: -123.256; control2X: 253.569; control2Y: -117.952; x: 246.633; y: -117.952 }
+ PathCubic { control1X: 246.633; control1Y: -117.952; control2X: 233.169; control2Y: -115.912; x: 229.089; y: -103.672 }
+ PathCubic { control1X: 229.089; control1Y: -103.672; control2X: 225.417; control2Y: -78.784; x: 232.761; y: -73.48 }
+ PathCubic { control1X: 232.761; control1Y: -73.48; control2X: 237.249; control2Y: -66.544; x: 243.777; y: -72.664 }
+ PathCubic { control1X: 250.305; control1Y: -78.784; control2X: 264.566; control2Y: -106.91; x: 262.526; y: -119.558 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 262.151; y: -119.2 }
+ PathCubic { control1X: 262.151; control1Y: -119.2; control2X: 264.201; control2Y: -127.2; x: 259.801; y: -122.8 }
+ PathCubic { control1X: 259.801; control1Y: -122.8; control2X: 253.401; control2Y: -117.6; x: 246.601; y: -117.6 }
+ PathCubic { control1X: 246.601; control1Y: -117.6; control2X: 233.401; control2Y: -115.6; x: 229.401; y: -103.6 }
+ PathCubic { control1X: 229.401; control1Y: -103.6; control2X: 225.801; control2Y: -79.2; x: 233.001; y: -74 }
+ PathCubic { control1X: 233.001; control1Y: -74; control2X: 237.401; control2Y: -67.2; x: 243.801; y: -73.2 }
+ PathCubic { control1X: 250.201; control1Y: -79.2; control2X: 264.151; control2Y: -106.8; x: 262.151; y: -119.2 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: 50.6; y: 84 }
+ PathCubic { control1X: 50.6; control1Y: 84; control2X: 30.2; control2Y: 64.8; x: 22.2; y: 64 }
+ PathCubic { control1X: 22.2; control1Y: 64; control2X: -12.2; control2Y: 60; x: -27; y: 78 }
+ PathCubic { control1X: -27; control1Y: 78; control2X: -9.4; control2Y: 57.6; x: 18.2; y: 63.2 }
+ PathCubic { control1X: 18.2; control1Y: 63.2; control2X: -3.4; control2Y: 58.8; x: -15.8; y: 62 }
+ PathCubic { control1X: -15.8; control1Y: 62; control2X: -32.6; control2Y: 62; x: -42.2; y: 76 }
+ PathLine { x: -45; y: 80.8 }
+ PathCubic { control1X: -45; control1Y: 80.8; control2X: -41; control2Y: 66; x: -22.6; y: 60 }
+ PathCubic { control1X: -22.6; control1Y: 60; control2X: 0.2; control2Y: 55.2; x: 11; y: 60 }
+ PathCubic { control1X: 11; control1Y: 60; control2X: -10.6; control2Y: 53.2; x: -20.6; y: 55.2 }
+ PathCubic { control1X: -20.6; control1Y: 55.2; control2X: -51; control2Y: 52.8; x: -63.8; y: 79.2 }
+ PathCubic { control1X: -63.8; control1Y: 79.2; control2X: -59.8; control2Y: 64.8; x: -45; y: 57.6 }
+ PathCubic { control1X: -45; control1Y: 57.6; control2X: -31.4; control2Y: 48.8; x: -11; y: 51.6 }
+ PathCubic { control1X: -11; control1Y: 51.6; control2X: 3.4; control2Y: 54.8; x: 8.6; y: 57.2 }
+ PathCubic { control1X: 13.8; control1Y: 59.6; control2X: 12.6; control2Y: 56.8; x: 4.2; y: 52 }
+ PathCubic { control1X: 4.2; control1Y: 52; control2X: -1.4; control2Y: 42; x: -15.4; y: 42.4 }
+ PathCubic { control1X: -15.4; control1Y: 42.4; control2X: -58.2; control2Y: 46; x: -68.6; y: 58 }
+ PathCubic { control1X: -68.6; control1Y: 58; control2X: -55; control2Y: 46.8; x: -44.6; y: 44 }
+ PathCubic { control1X: -44.6; control1Y: 44; control2X: -22.2; control2Y: 36; x: -13.8; y: 36.8 }
+ PathCubic { control1X: -13.8; control1Y: 36.8; control2X: 11; control2Y: 37.8; x: 18.6; y: 33.8 }
+ PathCubic { control1X: 18.6; control1Y: 33.8; control2X: 7.4; control2Y: 38.8; x: 10.6; y: 42 }
+ PathCubic { control1X: 13.8; control1Y: 45.2; control2X: 20.6; control2Y: 52.8; x: 20.6; y: 54 }
+ PathCubic { control1X: 20.6; control1Y: 55.2; control2X: 44.8; control2Y: 77.3; x: 48.4; y: 81.7 }
+ PathLine { x: 50.6; y: 84 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 189; y: 278 }
+ PathCubic { control1X: 189; control1Y: 278; control2X: 173.5; control2Y: 241.5; x: 161; y: 232 }
+ PathCubic { control1X: 161; control1Y: 232; control2X: 187; control2Y: 248; x: 190.5; y: 266 }
+ PathCubic { control1X: 190.5; control1Y: 266; control2X: 190.5; control2Y: 276; x: 189; y: 278 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 236; y: 285.5 }
+ PathCubic { control1X: 236; control1Y: 285.5; control2X: 209.5; control2Y: 230.5; x: 191; y: 206.5 }
+ PathCubic { control1X: 191; control1Y: 206.5; control2X: 234.5; control2Y: 244; x: 239.5; y: 270.5 }
+ PathLine { x: 240; y: 276 }
+ PathLine { x: 237; y: 273.5 }
+ PathCubic { control1X: 237; control1Y: 273.5; control2X: 236.5; control2Y: 282.5; x: 236; y: 285.5 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 292.5; y: 237 }
+ PathCubic { control1X: 292.5; control1Y: 237; control2X: 230; control2Y: 177.5; x: 228.5; y: 175 }
+ PathCubic { control1X: 228.5; control1Y: 175; control2X: 289; control2Y: 241; x: 292; y: 248.5 }
+ PathCubic { control1X: 292; control1Y: 248.5; control2X: 290; control2Y: 239.5; x: 292.5; y: 237 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 104; y: 280.5 }
+ PathCubic { control1X: 104; control1Y: 280.5; control2X: 123.5; control2Y: 228.5; x: 142.5; y: 251 }
+ PathCubic { control1X: 142.5; control1Y: 251; control2X: 157.5; control2Y: 261; x: 157; y: 264 }
+ PathCubic { control1X: 157; control1Y: 264; control2X: 153; control2Y: 257.5; x: 135; y: 258 }
+ PathCubic { control1X: 135; control1Y: 258; control2X: 116; control2Y: 255; x: 104; y: 280.5 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 294.5; y: 153 }
+ PathCubic { control1X: 294.5; control1Y: 153; control2X: 249.5; control2Y: 124.5; x: 242; y: 123 }
+ PathCubic { control1X: 230.193; control1Y: 120.639; control2X: 291.5; control2Y: 152; x: 296.5; y: 162.5 }
+ PathCubic { control1X: 296.5; control1Y: 162.5; control2X: 298.5; control2Y: 160; x: 294.5; y: 153 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 143.801; y: 259.601 }
+ PathCubic { control1X: 143.801; control1Y: 259.601; control2X: 164.201; control2Y: 257.601; x: 171.001; y: 250.801 }
+ PathLine { x: 175.401; y: 254.401 }
+ PathLine { x: 193.001; y: 216.001 }
+ PathLine { x: 196.601; y: 221.201 }
+ PathCubic { control1X: 196.601; control1Y: 221.201; control2X: 211.001; control2Y: 206.401; x: 210.201; y: 198.401 }
+ PathCubic { control1X: 209.401; control1Y: 190.401; control2X: 223.001; control2Y: 204.401; x: 223.001; y: 204.401 }
+ PathCubic { control1X: 223.001; control1Y: 204.401; control2X: 222.201; control2Y: 192.801; x: 229.401; y: 199.601 }
+ PathCubic { control1X: 229.401; control1Y: 199.601; control2X: 227.001; control2Y: 184.001; x: 235.401; y: 192.001 }
+ PathCubic { control1X: 235.401; control1Y: 192.001; control2X: 224.864; control2Y: 161.844; x: 247.401; y: 187.601 }
+ PathCubic { control1X: 253.001; control1Y: 194.001; control2X: 248.601; control2Y: 187.201; x: 248.601; y: 187.201 }
+ PathCubic { control1X: 248.601; control1Y: 187.201; control2X: 222.601; control2Y: 139.201; x: 244.201; y: 153.601 }
+ PathCubic { control1X: 244.201; control1Y: 153.601; control2X: 246.201; control2Y: 130.801; x: 245.001; y: 126.401 }
+ PathCubic { control1X: 243.801; control1Y: 122.001; control2X: 241.801; control2Y: 99.6; x: 237.001; y: 94.4 }
+ PathCubic { control1X: 232.201; control1Y: 89.2; control2X: 237.401; control2Y: 87.6; x: 243.001; y: 92.8 }
+ PathCubic { control1X: 243.001; control1Y: 92.8; control2X: 231.801; control2Y: 68.8; x: 245.001; y: 80.8 }
+ PathCubic { control1X: 245.001; control1Y: 80.8; control2X: 241.401; control2Y: 65.6; x: 237.001; y: 62.8 }
+ PathCubic { control1X: 237.001; control1Y: 62.8; control2X: 231.401; control2Y: 45.6; x: 246.601; y: 56.4 }
+ PathCubic { control1X: 246.601; control1Y: 56.4; control2X: 242.201; control2Y: 44; x: 239.001; y: 40.8 }
+ PathCubic { control1X: 239.001; control1Y: 40.8; control2X: 227.401; control2Y: 13.2; x: 234.601; y: 18 }
+ PathLine { x: 239.001; y: 21.6 }
+ PathCubic { control1X: 239.001; control1Y: 21.6; control2X: 232.201; control2Y: 7.6; x: 238.601; y: 12 }
+ PathCubic { control1X: 245.001; control1Y: 16.4; control2X: 245.001; control2Y: 16; x: 245.001; y: 16 }
+ PathCubic { control1X: 245.001; control1Y: 16; control2X: 223.801; control2Y: -17.2; x: 244.201; y: 0.4 }
+ PathCubic { control1X: 244.201; control1Y: 0.4; control2X: 236.042; control2Y: -13.518; x: 232.601; y: -20.4 }
+ PathCubic { control1X: 232.601; control1Y: -20.4; control2X: 213.801; control2Y: -40.8; x: 228.201; y: -34.4 }
+ PathLine { x: 233.001; y: -32.8 }
+ PathCubic { control1X: 233.001; control1Y: -32.8; control2X: 224.201; control2Y: -42.8; x: 216.201; y: -44.4 }
+ PathCubic { control1X: 208.201; control1Y: -46; control2X: 218.601; control2Y: -52.4; x: 225.001; y: -50.4 }
+ PathCubic { control1X: 231.401; control1Y: -48.4; control2X: 247.001; control2Y: -40.8; x: 247.001; y: -40.8 }
+ PathCubic { control1X: 247.001; control1Y: -40.8; control2X: 259.801; control2Y: -22; x: 263.801; y: -21.6 }
+ PathCubic { control1X: 263.801; control1Y: -21.6; control2X: 243.801; control2Y: -29.2; x: 249.801; y: -21.2 }
+ PathCubic { control1X: 249.801; control1Y: -21.2; control2X: 264.201; control2Y: -7.2; x: 257.001; y: -7.6 }
+ PathCubic { control1X: 257.001; control1Y: -7.6; control2X: 251.001; control2Y: -0.4; x: 255.801; y: 8.4 }
+ PathCubic { control1X: 255.801; control1Y: 8.4; control2X: 237.342; control2Y: -9.991; x: 252.201; y: 15.6 }
+ PathLine { x: 259.001; y: 32 }
+ PathCubic { control1X: 259.001; control1Y: 32; control2X: 234.601; control2Y: 7.2; x: 245.801; y: 29.2 }
+ PathCubic { control1X: 245.801; control1Y: 29.2; control2X: 263.001; control2Y: 52.8; x: 265.001; y: 53.2 }
+ PathCubic { control1X: 267.001; control1Y: 53.6; control2X: 271.401; control2Y: 62.4; x: 271.401; y: 62.4 }
+ PathLine { x: 267.001; y: 60.4 }
+ PathLine { x: 272.201; y: 69.2 }
+ PathCubic { control1X: 272.201; control1Y: 69.2; control2X: 261.001; control2Y: 57.2; x: 267.001; y: 70.4 }
+ PathLine { x: 272.601; y: 84.8 }
+ PathCubic { control1X: 272.601; control1Y: 84.8; control2X: 252.201; control2Y: 62.8; x: 265.801; y: 92.4 }
+ PathCubic { control1X: 265.801; control1Y: 92.4; control2X: 249.401; control2Y: 87.2; x: 258.201; y: 104.4 }
+ PathCubic { control1X: 258.201; control1Y: 104.4; control2X: 256.601; control2Y: 120.401; x: 257.001; y: 125.601 }
+ PathCubic { control1X: 257.401; control1Y: 130.801; control2X: 258.601; control2Y: 159.201; x: 254.201; y: 167.201 }
+ PathCubic { control1X: 249.801; control1Y: 175.201; control2X: 260.201; control2Y: 194.401; x: 262.201; y: 198.401 }
+ PathCubic { control1X: 264.201; control1Y: 202.401; control2X: 267.801; control2Y: 213.201; x: 259.001; y: 204.001 }
+ PathCubic { control1X: 250.201; control1Y: 194.801; control2X: 254.601; control2Y: 200.401; x: 256.601; y: 209.201 }
+ PathCubic { control1X: 258.601; control1Y: 218.001; control2X: 264.601; control2Y: 233.601; x: 263.801; y: 239.201 }
+ PathCubic { control1X: 263.801; control1Y: 239.201; control2X: 262.601; control2Y: 240.401; x: 259.401; y: 236.801 }
+ PathCubic { control1X: 259.401; control1Y: 236.801; control2X: 244.601; control2Y: 214.001; x: 246.201; y: 228.401 }
+ PathCubic { control1X: 246.201; control1Y: 228.401; control2X: 245.001; control2Y: 236.401; x: 241.801; y: 245.201 }
+ PathCubic { control1X: 241.801; control1Y: 245.201; control2X: 238.601; control2Y: 256.001; x: 238.601; y: 247.201 }
+ PathCubic { control1X: 238.601; control1Y: 247.201; control2X: 235.401; control2Y: 230.401; x: 232.601; y: 238.001 }
+ PathCubic { control1X: 229.801; control1Y: 245.601; control2X: 226.201; control2Y: 251.601; x: 223.401; y: 254.001 }
+ PathCubic { control1X: 220.601; control1Y: 256.401; control2X: 215.401; control2Y: 233.601; x: 214.201; y: 244.001 }
+ PathCubic { control1X: 214.201; control1Y: 244.001; control2X: 202.201; control2Y: 231.601; x: 197.401; y: 248.001 }
+ PathLine { x: 185.801; y: 264.401 }
+ PathCubic { control1X: 185.801; control1Y: 264.401; control2X: 185.401; control2Y: 252.001; x: 184.201; y: 258.001 }
+ PathCubic { control1X: 184.201; control1Y: 258.001; control2X: 154.201; control2Y: 264.001; x: 143.801; y: 259.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 109.401; y: -97.2 }
+ PathCubic { control1X: 109.401; control1Y: -97.2; control2X: 97.801; control2Y: -105.2; x: 93.801; y: -104.8 }
+ PathCubic { control1X: 89.801; control1Y: -104.4; control2X: 121.401; control2Y: -113.6; x: 162.601; y: -86 }
+ PathCubic { control1X: 162.601; control1Y: -86; control2X: 167.401; control2Y: -83.2; x: 171.001; y: -83.6 }
+ PathCubic { control1X: 171.001; control1Y: -83.6; control2X: 174.201; control2Y: -81.2; x: 171.401; y: -77.6 }
+ PathCubic { control1X: 171.401; control1Y: -77.6; control2X: 162.601; control2Y: -68; x: 173.801; y: -56.8 }
+ PathCubic { control1X: 173.801; control1Y: -56.8; control2X: 192.201; control2Y: -50; x: 186.601; y: -58.8 }
+ PathCubic { control1X: 186.601; control1Y: -58.8; control2X: 197.401; control2Y: -54.8; x: 199.801; y: -50.8 }
+ PathCubic { control1X: 202.201; control1Y: -46.8; control2X: 201.001; control2Y: -50.8; x: 201.001; y: -50.8 }
+ PathCubic { control1X: 201.001; control1Y: -50.8; control2X: 194.601; control2Y: -58; x: 188.601; y: -63.2 }
+ PathCubic { control1X: 188.601; control1Y: -63.2; control2X: 183.401; control2Y: -65.2; x: 180.601; y: -73.6 }
+ PathCubic { control1X: 177.801; control1Y: -82; control2X: 175.401; control2Y: -92; x: 179.801; y: -95.2 }
+ PathCubic { control1X: 179.801; control1Y: -95.2; control2X: 175.801; control2Y: -90.8; x: 176.601; y: -94.8 }
+ PathCubic { control1X: 177.401; control1Y: -98.8; control2X: 181.001; control2Y: -102.4; x: 182.601; y: -102.8 }
+ PathCubic { control1X: 184.201; control1Y: -103.2; control2X: 200.601; control2Y: -119; x: 207.401; y: -119.4 }
+ PathCubic { control1X: 207.401; control1Y: -119.4; control2X: 198.201; control2Y: -118; x: 195.201; y: -119 }
+ PathCubic { control1X: 192.201; control1Y: -120; control2X: 165.601; control2Y: -131.4; x: 159.601; y: -132.6 }
+ PathCubic { control1X: 159.601; control1Y: -132.6; control2X: 142.801; control2Y: -139.2; x: 154.801; y: -137.2 }
+ PathCubic { control1X: 154.801; control1Y: -137.2; control2X: 190.601; control2Y: -133.4; x: 208.801; y: -120.2 }
+ PathCubic { control1X: 208.801; control1Y: -120.2; control2X: 201.601; control2Y: -128.6; x: 183.201; y: -135.6 }
+ PathCubic { control1X: 183.201; control1Y: -135.6; control2X: 161.001; control2Y: -148.2; x: 125.801; y: -143.2 }
+ PathCubic { control1X: 125.801; control1Y: -143.2; control2X: 108.001; control2Y: -140; x: 100.201; y: -138.2 }
+ PathCubic { control1X: 100.201; control1Y: -138.2; control2X: 97.601; control2Y: -138.8; x: 97.001; y: -139.2 }
+ PathCubic { control1X: 96.401; control1Y: -139.6; control2X: 84.6; control2Y: -148.6; x: 57; y: -141.6 }
+ PathCubic { control1X: 57; control1Y: -141.6; control2X: 40; control2Y: -137; x: 31.4; y: -132.2 }
+ PathCubic { control1X: 31.4; control1Y: -132.2; control2X: 16.2; control2Y: -131; x: 12.6; y: -127.8 }
+ PathCubic { control1X: 12.6; control1Y: -127.8; control2X: -6; control2Y: -113.2; x: -8; y: -112.4 }
+ PathCubic { control1X: -10; control1Y: -111.6; control2X: -21.4; control2Y: -104; x: -22.2; y: -103.6 }
+ PathCubic { control1X: -22.2; control1Y: -103.6; control2X: 2.4; control2Y: -110.2; x: 4.8; y: -112.6 }
+ PathCubic { control1X: 7.2; control1Y: -115; control2X: 24.6; control2Y: -117.6; x: 27; y: -116.2 }
+ PathCubic { control1X: 29.4; control1Y: -114.8; control2X: 37.8; control2Y: -115.4; x: 28.2; y: -114.8 }
+ PathCubic { control1X: 28.2; control1Y: -114.8; control2X: 103.801; control2Y: -100; x: 104.601; y: -98 }
+ PathCubic { control1X: 105.401; control1Y: -96; control2X: 109.401; control2Y: -97.2; x: 109.401; y: -97.2 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 180.801; y: -106.4 }
+ PathCubic { control1X: 180.801; control1Y: -106.4; control2X: 170.601; control2Y: -113.8; x: 168.601; y: -113.8 }
+ PathCubic { control1X: 166.601; control1Y: -113.8; control2X: 154.201; control2Y: -124; x: 150.001; y: -123.6 }
+ PathCubic { control1X: 145.801; control1Y: -123.2; control2X: 133.601; control2Y: -133.2; x: 106.201; y: -125 }
+ PathCubic { control1X: 106.201; control1Y: -125; control2X: 105.601; control2Y: -127; x: 109.201; y: -127.8 }
+ PathCubic { control1X: 109.201; control1Y: -127.8; control2X: 115.601; control2Y: -130; x: 116.001; y: -130.6 }
+ PathCubic { control1X: 116.001; control1Y: -130.6; control2X: 136.201; control2Y: -134.8; x: 143.401; y: -131.2 }
+ PathCubic { control1X: 143.401; control1Y: -131.2; control2X: 152.601; control2Y: -128.6; x: 158.801; y: -122.4 }
+ PathCubic { control1X: 158.801; control1Y: -122.4; control2X: 170.001; control2Y: -119.2; x: 173.201; y: -120.2 }
+ PathCubic { control1X: 173.201; control1Y: -120.2; control2X: 182.001; control2Y: -118; x: 182.401; y: -116.2 }
+ PathCubic { control1X: 182.401; control1Y: -116.2; control2X: 188.201; control2Y: -113.2; x: 186.401; y: -110.6 }
+ PathCubic { control1X: 186.401; control1Y: -110.6; control2X: 186.801; control2Y: -109; x: 180.801; y: -106.4 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 168.33; y: -108.509 }
+ PathCubic { control1X: 169.137; control1Y: -107.877; control2X: 170.156; control2Y: -107.779; x: 170.761; y: -106.97 }
+ PathCubic { control1X: 170.995; control1Y: -106.656; control2X: 170.706; control2Y: -106.33; x: 170.391; y: -106.233 }
+ PathCubic { control1X: 169.348; control1Y: -105.916; control2X: 168.292; control2Y: -106.486; x: 167.15; y: -105.898 }
+ PathCubic { control1X: 166.748; control1Y: -105.691; control2X: 166.106; control2Y: -105.873; x: 165.553; y: -106.022 }
+ PathCubic { control1X: 163.921; control1Y: -106.463; control2X: 162.092; control2Y: -106.488; x: 160.401; y: -105.8 }
+ PathCubic { control1X: 158.416; control1Y: -106.929; control2X: 156.056; control2Y: -106.345; x: 153.975; y: -107.346 }
+ PathCubic { control1X: 153.917; control1Y: -107.373; control2X: 153.695; control2Y: -107.027; x: 153.621; y: -107.054 }
+ PathCubic { control1X: 150.575; control1Y: -108.199; control2X: 146.832; control2Y: -107.916; x: 144.401; y: -110.2 }
+ PathCubic { control1X: 141.973; control1Y: -110.612; control2X: 139.616; control2Y: -111.074; x: 137.188; y: -111.754 }
+ PathCubic { control1X: 135.37; control1Y: -112.263; control2X: 133.961; control2Y: -113.252; x: 132.341; y: -114.084 }
+ PathCubic { control1X: 130.964; control1Y: -114.792; control2X: 129.507; control2Y: -115.314; x: 127.973; y: -115.686 }
+ PathCubic { control1X: 126.11; control1Y: -116.138; control2X: 124.279; control2Y: -116.026; x: 122.386; y: -116.546 }
+ PathCubic { control1X: 122.293; control1Y: -116.571; control2X: 122.101; control2Y: -116.227; x: 122.019; y: -116.254 }
+ PathCubic { control1X: 121.695; control1Y: -116.362; control2X: 121.405; control2Y: -116.945; x: 121.234; y: -116.892 }
+ PathCubic { control1X: 119.553; control1Y: -116.37; control2X: 118.065; control2Y: -117.342; x: 116.401; y: -117 }
+ PathCubic { control1X: 115.223; control1Y: -118.224; control2X: 113.495; control2Y: -117.979; x: 111.949; y: -118.421 }
+ PathCubic { control1X: 108.985; control1Y: -119.269; control2X: 105.831; control2Y: -117.999; x: 102.801; y: -119 }
+ PathCubic { control1X: 106.914; control1Y: -120.842; control2X: 111.601; control2Y: -119.61; x: 115.663; y: -121.679 }
+ PathCubic { control1X: 117.991; control1Y: -122.865; control2X: 120.653; control2Y: -121.763; x: 123.223; y: -122.523 }
+ PathCubic { control1X: 123.71; control1Y: -122.667; control2X: 124.401; control2Y: -122.869; x: 124.801; y: -122.2 }
+ PathCubic { control1X: 124.935; control1Y: -122.335; control2X: 125.117; control2Y: -122.574; x: 125.175; y: -122.546 }
+ PathCubic { control1X: 127.625; control1Y: -121.389; control2X: 129.94; control2Y: -120.115; x: 132.422; y: -119.049 }
+ PathCubic { control1X: 132.763; control1Y: -118.903; control2X: 133.295; control2Y: -119.135; x: 133.547; y: -118.933 }
+ PathCubic { control1X: 135.067; control1Y: -117.717; control2X: 137.01; control2Y: -117.82; x: 138.401; y: -116.6 }
+ PathCubic { control1X: 140.099; control1Y: -117.102; control2X: 141.892; control2Y: -116.722; x: 143.621; y: -117.346 }
+ PathCubic { control1X: 143.698; control1Y: -117.373; control2X: 143.932; control2Y: -117.032; x: 143.965; y: -117.054 }
+ PathCubic { control1X: 145.095; control1Y: -117.802; control2X: 146.25; control2Y: -117.531; x: 147.142; y: -117.227 }
+ PathCubic { control1X: 147.48; control1Y: -117.112; control2X: 148.143; control2Y: -116.865; x: 148.448; y: -116.791 }
+ PathCubic { control1X: 149.574; control1Y: -116.515; control2X: 150.43; control2Y: -116.035; x: 151.609; y: -115.852 }
+ PathCubic { control1X: 151.723; control1Y: -115.834; control2X: 151.908; control2Y: -116.174; x: 151.98; y: -116.146 }
+ PathCubic { control1X: 153.103; control1Y: -115.708; control2X: 154.145; control2Y: -115.764; x: 154.801; y: -114.6 }
+ PathCubic { control1X: 154.936; control1Y: -114.735; control2X: 155.101; control2Y: -114.973; x: 155.183; y: -114.946 }
+ PathCubic { control1X: 156.21; control1Y: -114.608; control2X: 156.859; control2Y: -113.853; x: 157.96; y: -113.612 }
+ PathCubic { control1X: 158.445; control1Y: -113.506; control2X: 159.057; control2Y: -112.88; x: 159.633; y: -112.704 }
+ PathCubic { control1X: 162.025; control1Y: -111.973; control2X: 163.868; control2Y: -110.444; x: 166.062; y: -109.549 }
+ PathCubic { control1X: 166.821; control1Y: -109.239; control2X: 167.697; control2Y: -109.005; x: 168.33; y: -108.509 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 91.696; y: -122.739 }
+ PathCubic { control1X: 89.178; control1Y: -124.464; control2X: 86.81; control2Y: -125.57; x: 84.368; y: -127.356 }
+ PathCubic { control1X: 84.187; control1Y: -127.489; control2X: 83.827; control2Y: -127.319; x: 83.625; y: -127.441 }
+ PathCubic { control1X: 82.618; control1Y: -128.05; control2X: 81.73; control2Y: -128.631; x: 80.748; y: -129.327 }
+ PathCubic { control1X: 80.209; control1Y: -129.709; control2X: 79.388; control2Y: -129.698; x: 78.88; y: -129.956 }
+ PathCubic { control1X: 76.336; control1Y: -131.248; control2X: 73.707; control2Y: -131.806; x: 71.2; y: -133 }
+ PathCubic { control1X: 71.882; control1Y: -133.638; control2X: 73.004; control2Y: -133.394; x: 73.6; y: -134.2 }
+ PathCubic { control1X: 73.795; control1Y: -133.92; control2X: 74.033; control2Y: -133.636; x: 74.386; y: -133.827 }
+ PathCubic { control1X: 76.064; control1Y: -134.731; control2X: 77.914; control2Y: -134.884; x: 79.59; y: -134.794 }
+ PathCubic { control1X: 81.294; control1Y: -134.702; control2X: 83.014; control2Y: -134.397; x: 84.789; y: -134.125 }
+ PathCubic { control1X: 85.096; control1Y: -134.078; control2X: 85.295; control2Y: -133.555; x: 85.618; y: -133.458 }
+ PathCubic { control1X: 87.846; control1Y: -132.795; control2X: 90.235; control2Y: -133.32; x: 92.354; y: -132.482 }
+ PathCubic { control1X: 93.945; control1Y: -131.853; control2X: 95.515; control2Y: -131.03; x: 96.754; y: -129.755 }
+ PathCubic { control1X: 97.006; control1Y: -129.495; control2X: 96.681; control2Y: -129.194; x: 96.401; y: -129 }
+ PathCubic { control1X: 96.789; control1Y: -129.109; control2X: 97.062; control2Y: -128.903; x: 97.173; y: -128.59 }
+ PathCubic { control1X: 97.257; control1Y: -128.351; control2X: 97.257; control2Y: -128.049; x: 97.173; y: -127.81 }
+ PathCubic { control1X: 97.061; control1Y: -127.498; control2X: 96.782; control2Y: -127.397; x: 96.408; y: -127.346 }
+ PathCubic { control1X: 95.001; control1Y: -127.156; control2X: 96.773; control2Y: -128.536; x: 96.073; y: -128.088 }
+ PathCubic { control1X: 94.8; control1Y: -127.274; control2X: 95.546; control2Y: -125.868; x: 94.801; y: -124.6 }
+ PathCubic { control1X: 94.521; control1Y: -124.794; control2X: 94.291; control2Y: -125.012; x: 94.401; y: -125.4 }
+ PathCubic { control1X: 94.635; control1Y: -124.878; control2X: 94.033; control2Y: -124.588; x: 93.865; y: -124.272 }
+ PathCubic { control1X: 93.48; control1Y: -123.547; control2X: 92.581; control2Y: -122.132; x: 91.696; y: -122.739 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 59.198; y: -115.391 }
+ PathCubic { control1X: 56.044; control1Y: -116.185; control2X: 52.994; control2Y: -116.07; x: 49.978; y: -117.346 }
+ PathCubic { control1X: 49.911; control1Y: -117.374; control2X: 49.688; control2Y: -117.027; x: 49.624; y: -117.054 }
+ PathCubic { control1X: 48.258; control1Y: -117.648; control2X: 47.34; control2Y: -118.614; x: 46.264; y: -119.66 }
+ PathCubic { control1X: 45.351; control1Y: -120.548; control2X: 43.693; control2Y: -120.161; x: 42.419; y: -120.648 }
+ PathCubic { control1X: 42.095; control1Y: -120.772; control2X: 41.892; control2Y: -121.284; x: 41.591; y: -121.323 }
+ PathCubic { control1X: 40.372; control1Y: -121.48; control2X: 39.445; control2Y: -122.429; x: 38.4; y: -123 }
+ PathCubic { control1X: 40.736; control1Y: -123.795; control2X: 43.147; control2Y: -123.764; x: 45.609; y: -124.148 }
+ PathCubic { control1X: 45.722; control1Y: -124.166; control2X: 45.867; control2Y: -123.845; x: 46; y: -123.845 }
+ PathCubic { control1X: 46.136; control1Y: -123.845; control2X: 46.266; control2Y: -124.066; x: 46.4; y: -124.2 }
+ PathCubic { control1X: 46.595; control1Y: -123.92; control2X: 46.897; control2Y: -123.594; x: 47.154; y: -123.848 }
+ PathCubic { control1X: 47.702; control1Y: -124.388; control2X: 48.258; control2Y: -124.198; x: 48.798; y: -124.158 }
+ PathCubic { control1X: 48.942; control1Y: -124.148; control2X: 49.067; control2Y: -123.845; x: 49.2; y: -123.845 }
+ PathCubic { control1X: 49.336; control1Y: -123.845; control2X: 49.467; control2Y: -124.156; x: 49.6; y: -124.156 }
+ PathCubic { control1X: 49.736; control1Y: -124.155; control2X: 49.867; control2Y: -123.845; x: 50; y: -123.845 }
+ PathCubic { control1X: 50.136; control1Y: -123.845; control2X: 50.266; control2Y: -124.066; x: 50.4; y: -124.2 }
+ PathCubic { control1X: 51.092; control1Y: -123.418; control2X: 51.977; control2Y: -123.972; x: 52.799; y: -123.793 }
+ PathCubic { control1X: 53.837; control1Y: -123.566; control2X: 54.104; control2Y: -122.418; x: 55.178; y: -122.12 }
+ PathCubic { control1X: 59.893; control1Y: -120.816; control2X: 64.03; control2Y: -118.671; x: 68.393; y: -116.584 }
+ PathCubic { control1X: 68.7; control1Y: -116.437; control2X: 68.91; control2Y: -116.189; x: 68.8; y: -115.8 }
+ PathCubic { control1X: 69.067; control1Y: -115.8; control2X: 69.38; control2Y: -115.888; x: 69.57; y: -115.756 }
+ PathCubic { control1X: 70.628; control1Y: -115.024; control2X: 71.669; control2Y: -114.476; x: 72.366; y: -113.378 }
+ PathCubic { control1X: 72.582; control1Y: -113.039; control2X: 72.253; control2Y: -112.632; x: 72.02; y: -112.684 }
+ PathCubic { control1X: 67.591; control1Y: -113.679; control2X: 63.585; control2Y: -114.287; x: 59.198; y: -115.391 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 45.338; y: -71.179 }
+ PathCubic { control1X: 43.746; control1Y: -72.398; control2X: 43.162; control2Y: -74.429; x: 42.034; y: -76.221 }
+ PathCubic { control1X: 41.82; control1Y: -76.561; control2X: 42.094; control2Y: -76.875; x: 42.411; y: -76.964 }
+ PathCubic { control1X: 42.971; control1Y: -77.123; control2X: 43.514; control2Y: -76.645; x: 43.923; y: -76.443 }
+ PathCubic { control1X: 45.668; control1Y: -75.581; control2X: 47.203; control2Y: -74.339; x: 49.2; y: -74.2 }
+ PathCubic { control1X: 51.19; control1Y: -71.966; control2X: 55.45; control2Y: -71.581; x: 55.457; y: -68.2 }
+ PathCubic { control1X: 55.458; control1Y: -67.341; control2X: 54.03; control2Y: -68.259; x: 53.6; y: -67.4 }
+ PathCubic { control1X: 51.149; control1Y: -68.403; control2X: 48.76; control2Y: -68.3; x: 46.38; y: -69.767 }
+ PathCubic { control1X: 45.763; control1Y: -70.148; control2X: 46.093; control2Y: -70.601; x: 45.338; y: -71.179 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 17.8; y: -123.756 }
+ PathCubic { control1X: 17.935; control1Y: -123.755; control2X: 24.966; control2Y: -123.522; x: 24.949; y: -123.408 }
+ PathCubic { control1X: 24.904; control1Y: -123.099; control2X: 17.174; control2Y: -122.05; x: 16.81; y: -122.22 }
+ PathCubic { control1X: 16.646; control1Y: -122.296; control2X: 9.134; control2Y: -119.866; x: 9; y: -120 }
+ PathCubic { control1X: 9.268; control1Y: -120.135; control2X: 17.534; control2Y: -123.756; x: 17.8; y: -123.756 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 33.2; y: -114 }
+ PathCubic { control1X: 33.2; control1Y: -114; control2X: 18.4; control2Y: -112.2; x: 14; y: -111 }
+ PathCubic { control1X: 9.6; control1Y: -109.8; control2X: -9; control2Y: -102.2; x: -12; y: -100.2 }
+ PathCubic { control1X: -12; control1Y: -100.2; control2X: -25.4; control2Y: -94.8; x: -42.4; y: -74.8 }
+ PathCubic { control1X: -42.4; control1Y: -74.8; control2X: -34.8; control2Y: -78.2; x: -32.6; y: -81 }
+ PathCubic { control1X: -32.6; control1Y: -81; control2X: -19; control2Y: -93.6; x: -19.2; y: -91 }
+ PathCubic { control1X: -19.2; control1Y: -91; control2X: -7; control2Y: -99.6; x: -7.6; y: -97.4 }
+ PathCubic { control1X: -7.6; control1Y: -97.4; control2X: 16.8; control2Y: -108.6; x: 14.8; y: -105.4 }
+ PathCubic { control1X: 14.8; control1Y: -105.4; control2X: 36.4; control2Y: -110; x: 35.4; y: -108 }
+ PathCubic { control1X: 35.4; control1Y: -108; control2X: 54.2; control2Y: -103.6; x: 51.4; y: -103.4 }
+ PathCubic { control1X: 51.4; control1Y: -103.4; control2X: 45.6; control2Y: -102.2; x: 52; y: -98.6 }
+ PathCubic { control1X: 52; control1Y: -98.6; control2X: 48.6; control2Y: -94.2; x: 43.2; y: -98.2 }
+ PathCubic { control1X: 37.8; control1Y: -102.2; control2X: 40.8; control2Y: -100; x: 35.8; y: -99 }
+ PathCubic { control1X: 35.8; control1Y: -99; control2X: 33.2; control2Y: -98.2; x: 28.6; y: -102.2 }
+ PathCubic { control1X: 28.6; control1Y: -102.2; control2X: 23; control2Y: -106.8; x: 14.2; y: -103.2 }
+ PathCubic { control1X: 14.2; control1Y: -103.2; control2X: -16.4; control2Y: -90.6; x: -18.4; y: -90 }
+ PathCubic { control1X: -18.4; control1Y: -90; control2X: -22; control2Y: -87.2; x: -24.4; y: -83.6 }
+ PathCubic { control1X: -24.4; control1Y: -83.6; control2X: -30.2; control2Y: -79.2; x: -33.2; y: -77.8 }
+ PathCubic { control1X: -33.2; control1Y: -77.8; control2X: -46; control2Y: -66.2; x: -47.2; y: -64.8 }
+ PathCubic { control1X: -47.2; control1Y: -64.8; control2X: -50.6; control2Y: -59.6; x: -51.4; y: -59.2 }
+ PathCubic { control1X: -51.4; control1Y: -59.2; control2X: -45; control2Y: -63; x: -43; y: -65 }
+ PathCubic { control1X: -43; control1Y: -65; control2X: -29; control2Y: -75; x: -23.6; y: -75.8 }
+ PathCubic { control1X: -23.6; control1Y: -75.8; control2X: -19.2; control2Y: -78.8; x: -18.4; y: -80.2 }
+ PathCubic { control1X: -18.4; control1Y: -80.2; control2X: -4; control2Y: -89.4; x: 0.2; y: -89.4 }
+ PathCubic { control1X: 0.2; control1Y: -89.4; control2X: 9.4; control2Y: -84.2; x: 11.8; y: -91.2 }
+ PathCubic { control1X: 11.8; control1Y: -91.2; control2X: 17.6; control2Y: -93; x: 23.2; y: -91.8 }
+ PathCubic { control1X: 23.2; control1Y: -91.8; control2X: 26.4; control2Y: -94.4; x: 25.6; y: -96.6 }
+ PathCubic { control1X: 25.6; control1Y: -96.6; control2X: 27.2; control2Y: -98.4; x: 28.2; y: -94.6 }
+ PathCubic { control1X: 28.2; control1Y: -94.6; control2X: 31.6; control2Y: -91; x: 36.4; y: -93 }
+ PathCubic { control1X: 36.4; control1Y: -93; control2X: 40.4; control2Y: -93.2; x: 38.4; y: -90.8 }
+ PathCubic { control1X: 38.4; control1Y: -90.8; control2X: 34; control2Y: -87; x: 22.2; y: -86.8 }
+ PathCubic { control1X: 22.2; control1Y: -86.8; control2X: 9.8; control2Y: -86.2; x: -6.6; y: -78.6 }
+ PathCubic { control1X: -6.6; control1Y: -78.6; control2X: -36.4; control2Y: -68.2; x: -45.6; y: -57.8 }
+ PathCubic { control1X: -45.6; control1Y: -57.8; control2X: -52; control2Y: -49; x: -57.4; y: -47.8 }
+ PathCubic { control1X: -57.4; control1Y: -47.8; control2X: -63.2; control2Y: -47; x: -69.2; y: -39.6 }
+ PathCubic { control1X: -69.2; control1Y: -39.6; control2X: -59.4; control2Y: -45.4; x: -50.4; y: -45.4 }
+ PathCubic { control1X: -50.4; control1Y: -45.4; control2X: -46.4; control2Y: -47.8; x: -50.2; y: -44.2 }
+ PathCubic { control1X: -50.2; control1Y: -44.2; control2X: -53.8; control2Y: -36.6; x: -52.2; y: -31.2 }
+ PathCubic { control1X: -52.2; control1Y: -31.2; control2X: -52.8; control2Y: -26; x: -53.6; y: -24.4 }
+ PathCubic { control1X: -53.6; control1Y: -24.4; control2X: -61.4; control2Y: -11.6; x: -61.4; y: -9.2 }
+ PathCubic { control1X: -61.4; control1Y: -6.8; control2X: -60.2; control2Y: 3; x: -59.8; y: 3.6 }
+ PathCubic { control1X: -59.4; control1Y: 4.2; control2X: -60.8; control2Y: 2; x: -57; y: 4.4 }
+ PathCubic { control1X: -53.2; control1Y: 6.8; control2X: -50.4; control2Y: 8.4; x: -49.6; y: 11.2 }
+ PathCubic { control1X: -48.8; control1Y: 14; control2X: -51.6; control2Y: 5.8; x: -51.8; y: 4 }
+ PathCubic { control1X: -52; control1Y: 2.2; control2X: -56.2; control2Y: -5; x: -55.4; y: -7.4 }
+ PathCubic { control1X: -55.4; control1Y: -7.4; control2X: -54.4; control2Y: -6.4; x: -53.6; y: -5 }
+ PathCubic { control1X: -53.6; control1Y: -5; control2X: -54.2; control2Y: -5.6; x: -53.6; y: -9.2 }
+ PathCubic { control1X: -53.6; control1Y: -9.2; control2X: -52.8; control2Y: -14.4; x: -51.4; y: -17.6 }
+ PathCubic { control1X: -50; control1Y: -20.8; control2X: -48; control2Y: -24.6; x: -47.6; y: -25.4 }
+ PathCubic { control1X: -47.2; control1Y: -26.2; control2X: -47.2; control2Y: -32; x: -45.8; y: -29.4 }
+ PathLine { x: -42.4; y: -26.8 }
+ PathCubic { control1X: -42.4; control1Y: -26.8; control2X: -45.2; control2Y: -29.4; x: -43; y: -31.6 }
+ PathCubic { control1X: -43; control1Y: -31.6; control2X: -44; control2Y: -37.2; x: -42.2; y: -39.8 }
+ PathCubic { control1X: -42.2; control1Y: -39.8; control2X: -35.2; control2Y: -48.2; x: -33.6; y: -49.2 }
+ PathCubic { control1X: -32; control1Y: -50.2; control2X: -33.4; control2Y: -49.8; x: -33.4; y: -49.8 }
+ PathCubic { control1X: -33.4; control1Y: -49.8; control2X: -27.4; control2Y: -54; x: -33.2; y: -52.4 }
+ PathCubic { control1X: -33.2; control1Y: -52.4; control2X: -37.2; control2Y: -50.8; x: -40.2; y: -50.8 }
+ PathCubic { control1X: -40.2; control1Y: -50.8; control2X: -47.8; control2Y: -48.8; x: -43.8; y: -53 }
+ PathCubic { control1X: -39.8; control1Y: -57.2; control2X: -29.8; control2Y: -62.6; x: -26; y: -62.4 }
+ PathLine { x: -25.2; y: -60.8 }
+ PathLine { x: -14; y: -63.2 }
+ PathLine { x: -15.2; y: -62.4 }
+ PathCubic { control1X: -15.2; control1Y: -62.4; control2X: -15.4; control2Y: -62.6; x: -11.2; y: -63 }
+ PathCubic { control1X: -7; control1Y: -63.4; control2X: -1.2; control2Y: -62; x: 0.2; y: -63.8 }
+ PathCubic { control1X: 1.6; control1Y: -65.6; control2X: 5; control2Y: -66.6; x: 4.6; y: -65.2 }
+ PathCubic { control1X: 4.2; control1Y: -63.8; control2X: 4; control2Y: -61.8; x: 4; y: -61.8 }
+ PathCubic { control1X: 4; control1Y: -61.8; control2X: 9; control2Y: -67.6; x: 8.4; y: -65.4 }
+ PathCubic { control1X: 7.8; control1Y: -63.2; control2X: -0.4; control2Y: -58; x: -1.8; y: -51.8 }
+ PathLine { x: 8.6; y: -60 }
+ PathLine { x: 12.2; y: -63 }
+ PathCubic { control1X: 12.2; control1Y: -63; control2X: 15.8; control2Y: -60.8; x: 16; y: -62.4 }
+ PathCubic { control1X: 16.2; control1Y: -64; control2X: 20.8; control2Y: -69.8; x: 22; y: -69.6 }
+ PathCubic { control1X: 23.2; control1Y: -69.4; control2X: 25.2; control2Y: -72.2; x: 25; y: -69.6 }
+ PathCubic { control1X: 24.8; control1Y: -67; control2X: 32.4; control2Y: -61.6; x: 32.4; y: -61.6 }
+ PathCubic { control1X: 32.4; control1Y: -61.6; control2X: 35.6; control2Y: -63.4; x: 37; y: -62 }
+ PathCubic { control1X: 38.4; control1Y: -60.6; control2X: 42.6; control2Y: -81.8; x: 42.6; y: -81.8 }
+ PathLine { x: 67.6; y: -92.4 }
+ PathLine { x: 111.201; y: -95.8 }
+ PathLine { x: 94.201; y: -102.6 }
+ PathLine { x: 33.2; y: -114 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ PathMove { x: 51.4; y: 85 }
+ PathCubic { control1X: 51.4; control1Y: 85; control2X: 36.4; control2Y: 68.2; x: 28; y: 65.6 }
+ PathCubic { control1X: 28; control1Y: 65.6; control2X: 14.6; control2Y: 58.8; x: -10; y: 66.6 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ PathMove { x: 24.8; y: 64.2 }
+ PathCubic { control1X: 24.8; control1Y: 64.2; control2X: -0.4; control2Y: 56.2; x: -15.8; y: 60.4 }
+ PathCubic { control1X: -15.8; control1Y: 60.4; control2X: -34.2; control2Y: 62.4; x: -42.6; y: 76.2 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ PathMove { x: 21.2; y: 63 }
+ PathCubic { control1X: 21.2; control1Y: 63; control2X: 4.2; control2Y: 55.8; x: -10.6; y: 53.6 }
+ PathCubic { control1X: -10.6; control1Y: 53.6; control2X: -27.2; control2Y: 51; x: -43.8; y: 58.2 }
+ PathCubic { control1X: -43.8; control1Y: 58.2; control2X: -56; control2Y: 64.2; x: -61.4; y: 74.4 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ PathMove { x: 22.2; y: 63.4 }
+ PathCubic { control1X: 22.2; control1Y: 63.4; control2X: 6.8; control2Y: 52.4; x: 5.8; y: 51 }
+ PathCubic { control1X: 5.8; control1Y: 51; control2X: -1.2; control2Y: 40; x: -14.2; y: 39.6 }
+ PathCubic { control1X: -14.2; control1Y: 39.6; control2X: -35.6; control2Y: 40.4; x: -52.8; y: 48.4 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 20.895; y: 54.407 }
+ PathCubic { control1X: 22.437; control1Y: 55.87; control2X: 49.4; control2Y: 84.8; x: 49.4; y: 84.8 }
+ PathCubic { control1X: 84.6; control1Y: 121.401; control2X: 56.6; control2Y: 87.2; x: 56.6; y: 87.2 }
+ PathCubic { control1X: 49; control1Y: 82.4; control2X: 39.8; control2Y: 63.6; x: 39.8; y: 63.6 }
+ PathCubic { control1X: 38.6; control1Y: 60.8; control2X: 53.8; control2Y: 70.8; x: 53.8; y: 70.8 }
+ PathCubic { control1X: 57.8; control1Y: 71.6; control2X: 71.4; control2Y: 90.8; x: 71.4; y: 90.8 }
+ PathCubic { control1X: 64.6; control1Y: 88.4; control2X: 69.4; control2Y: 95.6; x: 69.4; y: 95.6 }
+ PathCubic { control1X: 72.2; control1Y: 97.6; control2X: 92.601; control2Y: 113.201; x: 92.601; y: 113.201 }
+ PathCubic { control1X: 96.201; control1Y: 117.201; control2X: 100.201; control2Y: 118.801; x: 100.201; y: 118.801 }
+ PathCubic { control1X: 114.201; control1Y: 113.601; control2X: 107.801; control2Y: 126.801; x: 107.801; y: 126.801 }
+ PathCubic { control1X: 110.201; control1Y: 133.601; control2X: 115.801; control2Y: 122.001; x: 115.801; y: 122.001 }
+ PathCubic { control1X: 127.001; control1Y: 105.2; control2X: 110.601; control2Y: 107.601; x: 110.601; y: 107.601 }
+ PathCubic { control1X: 80.6; control1Y: 110.401; control2X: 73.8; control2Y: 94.4; x: 73.8; y: 94.4 }
+ PathCubic { control1X: 71.4; control1Y: 92; control2X: 80.2; control2Y: 94.4; x: 80.2; y: 94.4 }
+ PathCubic { control1X: 88.601; control1Y: 96.4; control2X: 73; control2Y: 82; x: 73; y: 82 }
+ PathCubic { control1X: 75.4; control1Y: 82; control2X: 84.6; control2Y: 88.8; x: 84.6; y: 88.8 }
+ PathCubic { control1X: 95.001; control1Y: 98; control2X: 97.001; control2Y: 96; x: 97.001; y: 96 }
+ PathCubic { control1X: 115.001; control1Y: 87.2; control2X: 125.401; control2Y: 94.8; x: 125.401; y: 94.8 }
+ PathCubic { control1X: 127.401; control1Y: 96.4; control2X: 121.801; control2Y: 103.2; x: 123.401; y: 108.401 }
+ PathCubic { control1X: 125.001; control1Y: 113.601; control2X: 129.801; control2Y: 126.001; x: 129.801; y: 126.001 }
+ PathCubic { control1X: 127.401; control1Y: 127.601; control2X: 127.801; control2Y: 138.401; x: 127.801; y: 138.401 }
+ PathCubic { control1X: 144.601; control1Y: 161.601; control2X: 135.001; control2Y: 159.601; x: 135.001; y: 159.601 }
+ PathCubic { control1X: 119.401; control1Y: 159.201; control2X: 134.201; control2Y: 166.801; x: 134.201; y: 166.801 }
+ PathCubic { control1X: 137.401; control1Y: 168.801; control2X: 146.201; control2Y: 176.001; x: 146.201; y: 176.001 }
+ PathCubic { control1X: 143.401; control1Y: 174.801; control2X: 141.801; control2Y: 180.001; x: 141.801; y: 180.001 }
+ PathCubic { control1X: 146.601; control1Y: 184.001; control2X: 143.801; control2Y: 188.801; x: 143.801; y: 188.801 }
+ PathCubic { control1X: 137.801; control1Y: 190.001; control2X: 136.601; control2Y: 194.001; x: 136.601; y: 194.001 }
+ PathCubic { control1X: 143.401; control1Y: 202.001; control2X: 133.401; control2Y: 202.401; x: 133.401; y: 202.401 }
+ PathCubic { control1X: 137.001; control1Y: 206.801; control2X: 132.201; control2Y: 218.801; x: 132.201; y: 218.801 }
+ PathCubic { control1X: 127.401; control1Y: 218.801; control2X: 121.001; control2Y: 224.401; x: 121.001; y: 224.401 }
+ PathCubic { control1X: 123.401; control1Y: 229.201; control2X: 113.001; control2Y: 234.801; x: 113.001; y: 234.801 }
+ PathCubic { control1X: 104.601; control1Y: 236.401; control2X: 107.401; control2Y: 243.201; x: 107.401; y: 243.201 }
+ PathCubic { control1X: 99.401; control1Y: 249.201; control2X: 97.001; control2Y: 265.201; x: 97.001; y: 265.201 }
+ PathCubic { control1X: 96.201; control1Y: 275.601; control2X: 93.801; control2Y: 278.801; x: 99.001; y: 276.801 }
+ PathCubic { control1X: 104.201; control1Y: 274.801; control2X: 103.401; control2Y: 262.401; x: 103.401; y: 262.401 }
+ PathCubic { control1X: 98.601; control1Y: 246.801; control2X: 141.401; control2Y: 230.801; x: 141.401; y: 230.801 }
+ PathCubic { control1X: 145.401; control1Y: 229.201; control2X: 146.201; control2Y: 224.001; x: 146.201; y: 224.001 }
+ PathCubic { control1X: 148.201; control1Y: 224.401; control2X: 157.001; control2Y: 232.001; x: 157.001; y: 232.001 }
+ PathCubic { control1X: 164.601; control1Y: 243.201; control2X: 165.001; control2Y: 234.001; x: 165.001; y: 234.001 }
+ PathCubic { control1X: 166.201; control1Y: 230.401; control2X: 164.601; control2Y: 224.401; x: 164.601; y: 224.401 }
+ PathCubic { control1X: 170.601; control1Y: 202.801; control2X: 156.601; control2Y: 196.401; x: 156.601; y: 196.401 }
+ PathCubic { control1X: 146.601; control1Y: 162.801; control2X: 160.601; control2Y: 171.201; x: 160.601; y: 171.201 }
+ PathCubic { control1X: 163.401; control1Y: 176.801; control2X: 174.201; control2Y: 182.001; x: 174.201; y: 182.001 }
+ PathLine { x: 177.801; y: 179.601 }
+ PathCubic { control1X: 176.201; control1Y: 174.801; control2X: 184.601; control2Y: 168.801; x: 184.601; y: 168.801 }
+ PathCubic { control1X: 187.401; control1Y: 175.201; control2X: 193.401; control2Y: 167.201; x: 193.401; y: 167.201 }
+ PathCubic { control1X: 197.001; control1Y: 142.801; control2X: 209.401; control2Y: 157.201; x: 209.401; y: 157.201 }
+ PathCubic { control1X: 213.401; control1Y: 158.401; control2X: 214.601; control2Y: 151.601; x: 214.601; y: 151.601 }
+ PathCubic { control1X: 218.201; control1Y: 141.201; control2X: 214.601; control2Y: 127.601; x: 214.601; y: 127.601 }
+ PathCubic { control1X: 218.201; control1Y: 127.201; control2X: 227.801; control2Y: 133.201; x: 227.801; y: 133.201 }
+ PathCubic { control1X: 230.601; control1Y: 129.601; control2X: 221.401; control2Y: 112.801; x: 225.401; y: 115.201 }
+ PathCubic { control1X: 229.401; control1Y: 117.601; control2X: 233.801; control2Y: 119.201; x: 233.801; y: 119.201 }
+ PathCubic { control1X: 234.601; control1Y: 117.201; control2X: 224.601; control2Y: 104.801; x: 224.601; y: 104.801 }
+ PathCubic { control1X: 220.201; control1Y: 102; control2X: 215.001; control2Y: 81.6; x: 215.001; y: 81.6 }
+ PathCubic { control1X: 222.201; control1Y: 85.2; control2X: 212.201; control2Y: 70; x: 212.201; y: 70 }
+ PathCubic { control1X: 212.201; control1Y: 66.8; control2X: 218.201; control2Y: 55.6; x: 218.201; y: 55.6 }
+ PathCubic { control1X: 217.401; control1Y: 48.8; control2X: 218.201; control2Y: 49.2; x: 218.201; y: 49.2 }
+ PathCubic { control1X: 221.001; control1Y: 50.4; control2X: 229.001; control2Y: 52; x: 222.201; y: 45.6 }
+ PathCubic { control1X: 215.401; control1Y: 39.2; control2X: 223.001; control2Y: 34.4; x: 223.001; y: 34.4 }
+ PathCubic { control1X: 227.401; control1Y: 31.6; control2X: 213.801; control2Y: 32; x: 213.801; y: 32 }
+ PathCubic { control1X: 208.601; control1Y: 27.6; control2X: 209.001; control2Y: 23.6; x: 209.001; y: 23.6 }
+ PathCubic { control1X: 217.001; control1Y: 25.6; control2X: 202.601; control2Y: 11.2; x: 200.201; y: 7.6 }
+ PathCubic { control1X: 197.801; control1Y: 4; control2X: 207.401; control2Y: -1.2; x: 207.401; y: -1.2 }
+ PathCubic { control1X: 220.601; control1Y: -4.8; control2X: 209.001; control2Y: -8; x: 209.001; y: -8 }
+ PathCubic { control1X: 189.401; control1Y: -7.6; control2X: 200.201; control2Y: -18.4; x: 200.201; y: -18.4 }
+ PathCubic { control1X: 206.201; control1Y: -18; control2X: 204.601; control2Y: -20.4; x: 204.601; y: -20.4 }
+ PathCubic { control1X: 199.401; control1Y: -21.6; control2X: 189.801; control2Y: -28; x: 189.801; y: -28 }
+ PathCubic { control1X: 185.801; control1Y: -31.6; control2X: 189.401; control2Y: -30.8; x: 189.401; y: -30.8 }
+ PathCubic { control1X: 206.201; control1Y: -29.6; control2X: 177.401; control2Y: -40.8; x: 177.401; y: -40.8 }
+ PathCubic { control1X: 185.401; control1Y: -40.8; control2X: 167.401; control2Y: -51.2; x: 167.401; y: -51.2 }
+ PathCubic { control1X: 165.401; control1Y: -52.8; control2X: 162.201; control2Y: -60.4; x: 162.201; y: -60.4 }
+ PathCubic { control1X: 156.201; control1Y: -65.6; control2X: 151.401; control2Y: -72.4; x: 151.401; y: -72.4 }
+ PathCubic { control1X: 151.001; control1Y: -76.8; control2X: 146.201; control2Y: -81.6; x: 146.201; y: -81.6 }
+ PathCubic { control1X: 134.601; control1Y: -95.2; control2X: 129.001; control2Y: -94.8; x: 129.001; y: -94.8 }
+ PathCubic { control1X: 114.201; control1Y: -98.4; control2X: 109.001; control2Y: -97.6; x: 109.001; y: -97.6 }
+ PathLine { x: 56.2; y: -93.2 }
+ PathCubic { control1X: 29.8; control1Y: -80.4; control2X: 37.6; control2Y: -59.4; x: 37.6; y: -59.4 }
+ PathCubic { control1X: 44; control1Y: -51; control2X: 53.2; control2Y: -54.8; x: 53.2; y: -54.8 }
+ PathCubic { control1X: 57.8; control1Y: -61; control2X: 69.4; control2Y: -58.8; x: 69.4; y: -58.8 }
+ PathCubic { control1X: 89.801; control1Y: -55.6; control2X: 87.201; control2Y: -59.2; x: 87.201; y: -59.2 }
+ PathCubic { control1X: 84.801; control1Y: -63.8; control2X: 68.6; control2Y: -70; x: 68.4; y: -70.6 }
+ PathCubic { control1X: 68.2; control1Y: -71.2; control2X: 59.4; control2Y: -74.6; x: 59.4; y: -74.6 }
+ PathCubic { control1X: 56.4; control1Y: -75.8; control2X: 52; control2Y: -85; x: 52; y: -85 }
+ PathCubic { control1X: 48.8; control1Y: -88.4; control2X: 64.6; control2Y: -82.6; x: 64.6; y: -82.6 }
+ PathCubic { control1X: 63.4; control1Y: -81.6; control2X: 70.8; control2Y: -77.6; x: 70.8; y: -77.6 }
+ PathCubic { control1X: 88.201; control1Y: -78.6; control2X: 98.801; control2Y: -67.8; x: 98.801; y: -67.8 }
+ PathCubic { control1X: 109.601; control1Y: -51.2; control2X: 109.801; control2Y: -59.4; x: 109.801; y: -59.4 }
+ PathCubic { control1X: 112.601; control1Y: -68.8; control2X: 100.801; control2Y: -90; x: 100.801; y: -90 }
+ PathCubic { control1X: 101.201; control1Y: -92; control2X: 109.401; control2Y: -85.4; x: 109.401; y: -85.4 }
+ PathCubic { control1X: 110.801; control1Y: -87.4; control2X: 111.601; control2Y: -81.6; x: 111.601; y: -81.6 }
+ PathCubic { control1X: 111.801; control1Y: -79.2; control2X: 115.601; control2Y: -71.2; x: 115.601; y: -71.2 }
+ PathCubic { control1X: 118.401; control1Y: -58.2; control2X: 122.001; control2Y: -65.6; x: 122.001; y: -65.6 }
+ PathLine { x: 126.601; y: -56.2 }
+ PathCubic { control1X: 128.001; control1Y: -53.6; control2X: 122.001; control2Y: -46; x: 122.001; y: -46 }
+ PathCubic { control1X: 121.801; control1Y: -43.2; control2X: 122.601; control2Y: -43.4; x: 117.001; y: -35.8 }
+ PathCubic { control1X: 111.401; control1Y: -28.2; control2X: 114.801; control2Y: -23.8; x: 114.801; y: -23.8 }
+ PathCubic { control1X: 113.401; control1Y: -17.2; control2X: 122.201; control2Y: -17.6; x: 122.201; y: -17.6 }
+ PathCubic { control1X: 124.801; control1Y: -15.4; control2X: 128.201; control2Y: -15.4; x: 128.201; y: -15.4 }
+ PathCubic { control1X: 130.001; control1Y: -13.4; control2X: 132.401; control2Y: -14; x: 132.401; y: -14 }
+ PathCubic { control1X: 134.001; control1Y: -17.8; control2X: 140.201; control2Y: -15.8; x: 140.201; y: -15.8 }
+ PathCubic { control1X: 141.601; control1Y: -18.2; control2X: 149.801; control2Y: -18.6; x: 149.801; y: -18.6 }
+ PathCubic { control1X: 150.801; control1Y: -21.2; control2X: 151.201; control2Y: -22.8; x: 154.601; y: -23.4 }
+ PathCubic { control1X: 158.001; control1Y: -24; control2X: 133.401; control2Y: -67; x: 133.401; y: -67 }
+ PathCubic { control1X: 139.801; control1Y: -67.8; control2X: 131.601; control2Y: -80.2; x: 131.601; y: -80.2 }
+ PathCubic { control1X: 129.401; control1Y: -86.8; control2X: 140.801; control2Y: -72.2; x: 143.001; y: -70.8 }
+ PathCubic { control1X: 145.201; control1Y: -69.4; control2X: 146.201; control2Y: -67.2; x: 144.601; y: -67.4 }
+ PathCubic { control1X: 143.001; control1Y: -67.6; control2X: 141.201; control2Y: -65.4; x: 142.601; y: -65.2 }
+ PathCubic { control1X: 144.001; control1Y: -65; control2X: 157.001; control2Y: -50; x: 160.401; y: -39.8 }
+ PathCubic { control1X: 163.801; control1Y: -29.6; control2X: 169.801; control2Y: -25.6; x: 176.001; y: -19.6 }
+ PathCubic { control1X: 182.201; control1Y: -13.6; control2X: 181.401; control2Y: 10.6; x: 181.401; y: 10.6 }
+ PathCubic { control1X: 181.001; control1Y: 19.4; control2X: 187.001; control2Y: 30; x: 187.001; y: 30 }
+ PathCubic { control1X: 189.001; control1Y: 33.8; control2X: 184.801; control2Y: 52; x: 184.801; y: 52 }
+ PathCubic { control1X: 182.801; control1Y: 54.2; control2X: 184.201; control2Y: 55; x: 184.201; y: 55 }
+ PathCubic { control1X: 185.201; control1Y: 56.2; control2X: 192.001; control2Y: 69.4; x: 192.001; y: 69.4 }
+ PathCubic { control1X: 190.201; control1Y: 69.2; control2X: 193.801; control2Y: 72.8; x: 193.801; y: 72.8 }
+ PathCubic { control1X: 199.001; control1Y: 78.8; control2X: 192.601; control2Y: 75.8; x: 192.601; y: 75.8 }
+ PathCubic { control1X: 186.601; control1Y: 74.2; control2X: 193.601; control2Y: 84; x: 193.601; y: 84 }
+ PathCubic { control1X: 194.801; control1Y: 85.8; control2X: 185.801; control2Y: 81.2; x: 185.801; y: 81.2 }
+ PathCubic { control1X: 176.601; control1Y: 80.6; control2X: 188.201; control2Y: 87.8; x: 188.201; y: 87.8 }
+ PathCubic { control1X: 196.801; control1Y: 95; control2X: 185.401; control2Y: 90.6; x: 185.401; y: 90.6 }
+ PathCubic { control1X: 180.801; control1Y: 88.8; control2X: 184.001; control2Y: 95.6; x: 184.001; y: 95.6 }
+ PathCubic { control1X: 187.201; control1Y: 97.2; control2X: 204.401; control2Y: 104.2; x: 204.401; y: 104.2 }
+ PathCubic { control1X: 204.801; control1Y: 108.001; control2X: 201.801; control2Y: 113.001; x: 201.801; y: 113.001 }
+ PathCubic { control1X: 202.201; control1Y: 117.001; control2X: 200.001; control2Y: 120.401; x: 200.001; y: 120.401 }
+ PathCubic { control1X: 198.801; control1Y: 128.601; control2X: 198.201; control2Y: 129.401; x: 198.201; y: 129.401 }
+ PathCubic { control1X: 194.001; control1Y: 129.601; control2X: 186.601; control2Y: 143.401; x: 186.601; y: 143.401 }
+ PathCubic { control1X: 184.801; control1Y: 146.001; control2X: 174.601; control2Y: 158.001; x: 174.601; y: 158.001 }
+ PathCubic { control1X: 172.601; control1Y: 165.001; control2X: 154.601; control2Y: 157.801; x: 154.601; y: 157.801 }
+ PathCubic { control1X: 148.001; control1Y: 161.201; control2X: 150.001; control2Y: 157.801; x: 150.001; y: 157.801 }
+ PathCubic { control1X: 149.601; control1Y: 155.601; control2X: 154.401; control2Y: 149.601; x: 154.401; y: 149.601 }
+ PathCubic { control1X: 161.401; control1Y: 147.001; control2X: 158.801; control2Y: 136.201; x: 158.801; y: 136.201 }
+ PathCubic { control1X: 162.801; control1Y: 134.801; control2X: 151.601; control2Y: 132.001; x: 151.801; y: 130.801 }
+ PathCubic { control1X: 152.001; control1Y: 129.601; control2X: 157.801; control2Y: 128.201; x: 157.801; y: 128.201 }
+ PathCubic { control1X: 165.801; control1Y: 126.201; control2X: 161.401; control2Y: 123.801; x: 161.401; y: 123.801 }
+ PathCubic { control1X: 160.801; control1Y: 119.801; control2X: 163.801; control2Y: 114.201; x: 163.801; y: 114.201 }
+ PathCubic { control1X: 175.401; control1Y: 113.401; control2X: 163.801; control2Y: 97.2; x: 163.801; y: 97.2 }
+ PathCubic { control1X: 153.001; control1Y: 89.6; control2X: 152.001; control2Y: 83.8; x: 152.001; y: 83.8 }
+ PathCubic { control1X: 164.601; control1Y: 75.6; control2X: 156.401; control2Y: 63.2; x: 156.601; y: 59.6 }
+ PathCubic { control1X: 156.801; control1Y: 56; control2X: 158.001; control2Y: 34.4; x: 158.001; y: 34.4 }
+ PathCubic { control1X: 156.001; control1Y: 28.2; control2X: 153.001; control2Y: 14.6; x: 153.001; y: 14.6 }
+ PathCubic { control1X: 155.201; control1Y: 9.4; control2X: 162.601; control2Y: -3.2; x: 162.601; y: -3.2 }
+ PathCubic { control1X: 165.401; control1Y: -7.4; control2X: 174.201; control2Y: -12.2; x: 172.001; y: -15.2 }
+ PathCubic { control1X: 169.801; control1Y: -18.2; control2X: 162.001; control2Y: -16.4; x: 162.001; y: -16.4 }
+ PathCubic { control1X: 154.201; control1Y: -17.8; control2X: 154.801; control2Y: -12.6; x: 154.801; y: -12.6 }
+ PathCubic { control1X: 153.201; control1Y: -11.6; control2X: 152.401; control2Y: -6.6; x: 152.401; y: -6.6 }
+ PathCubic { control1X: 151.68; control1Y: 1.333; control2X: 142.801; control2Y: 7.6; x: 142.801; y: 7.6 }
+ PathCubic { control1X: 131.601; control1Y: 13.8; control2X: 140.801; control2Y: 17.8; x: 140.801; y: 17.8 }
+ PathCubic { control1X: 146.801; control1Y: 24.4; control2X: 137.001; control2Y: 24.6; x: 137.001; y: 24.6 }
+ PathCubic { control1X: 126.001; control1Y: 22.8; control2X: 134.201; control2Y: 33; x: 134.201; y: 33 }
+ PathCubic { control1X: 145.001; control1Y: 45.8; control2X: 142.001; control2Y: 48.6; x: 142.001; y: 48.6 }
+ PathCubic { control1X: 131.801; control1Y: 49.6; control2X: 144.401; control2Y: 58.8; x: 144.401; y: 58.8 }
+ PathCubic { control1X: 144.401; control1Y: 58.8; control2X: 143.601; control2Y: 56.8; x: 143.801; y: 58.6 }
+ PathCubic { control1X: 144.001; control1Y: 60.4; control2X: 147.001; control2Y: 64.6; x: 147.801; y: 66.6 }
+ PathCubic { control1X: 148.601; control1Y: 68.6; control2X: 144.601; control2Y: 68.8; x: 144.601; y: 68.8 }
+ PathCubic { control1X: 145.201; control1Y: 78.4; control2X: 129.801; control2Y: 74.2; x: 129.801; y: 74.2 }
+ PathCubic { control1X: 129.801; control1Y: 74.2; control2X: 129.801; control2Y: 74.2; x: 128.201; y: 74.4 }
+ PathCubic { control1X: 126.601; control1Y: 74.6; control2X: 115.401; control2Y: 73.8; x: 109.601; y: 71.6 }
+ PathCubic { control1X: 103.801; control1Y: 69.4; control2X: 97.001; control2Y: 69.4; x: 97.001; y: 69.4 }
+ PathCubic { control1X: 97.001; control1Y: 69.4; control2X: 93.001; control2Y: 71.2; x: 85.4; y: 71 }
+ PathCubic { control1X: 77.8; control1Y: 70.8; control2X: 69.8; control2Y: 73.6; x: 69.8; y: 73.6 }
+ PathCubic { control1X: 65.4; control1Y: 73.2; control2X: 74; control2Y: 68.8; x: 74.2; y: 69 }
+ PathCubic { control1X: 74.4; control1Y: 69.2; control2X: 80; control2Y: 63.6; x: 72; y: 64.2 }
+ PathCubic { control1X: 50.203; control1Y: 65.835; control2X: 39.4; control2Y: 55.6; x: 39.4; y: 55.6 }
+ PathCubic { control1X: 37.4; control1Y: 54.2; control2X: 34.8; control2Y: 51.4; x: 34.8; y: 51.4 }
+ PathCubic { control1X: 24.8; control1Y: 49.4; control2X: 36.2; control2Y: 63.8; x: 36.2; y: 63.8 }
+ PathCubic { control1X: 37.4; control1Y: 65.2; control2X: 36; control2Y: 66.2; x: 36; y: 66.2 }
+ PathCubic { control1X: 35.2; control1Y: 64.6; control2X: 27.4; control2Y: 59.2; x: 27.4; y: 59.2 }
+ PathCubic { control1X: 24.589; control1Y: 58.227; control2X: 23.226; control2Y: 56.893; x: 20.895; y: 54.407 }
+ }
+
+ ShapePath {
+ fillColor: "#4c0000"
+ strokeWidth: -1
+ PathMove { x: -3; y: 42.8 }
+ PathCubic { control1X: -3; control1Y: 42.8; control2X: 8.6; control2Y: 48.4; x: 11.2; y: 51.2 }
+ PathCubic { control1X: 13.8; control1Y: 54; control2X: 27.8; control2Y: 65.4; x: 27.8; y: 65.4 }
+ PathCubic { control1X: 27.8; control1Y: 65.4; control2X: 22.4; control2Y: 63.4; x: 19.8; y: 61.6 }
+ PathCubic { control1X: 17.2; control1Y: 59.8; control2X: 6.4; control2Y: 51.6; x: 6.4; y: 51.6 }
+ PathCubic { control1X: 6.4; control1Y: 51.6; control2X: 2.6; control2Y: 45.6; x: -3; y: 42.8 }
+ }
+
+ ShapePath {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ PathMove { x: -61.009; y: 11.603 }
+ PathCubic { control1X: -60.672; control1Y: 11.455; control2X: -61.196; control2Y: 8.743; x: -61.4; y: 8.2 }
+ PathCubic { control1X: -62.422; control1Y: 5.474; control2X: -71.4; control2Y: 4; x: -71.4; y: 4 }
+ PathCubic { control1X: -71.627; control1Y: 5.365; control2X: -71.682; control2Y: 6.961; x: -71.576; y: 8.599 }
+ PathCubic { control1X: -71.576; control1Y: 8.599; control2X: -66.708; control2Y: 14.118; x: -61.009; y: 11.603 }
+ }
+
+ ShapePath {
+ fillColor: "#659900"
+ strokeWidth: -1
+ PathMove { x: -61.009; y: 11.403 }
+ PathCubic { control1X: -61.458; control1Y: 11.561; control2X: -61.024; control2Y: 8.669; x: -61.2; y: 8.2 }
+ PathCubic { control1X: -62.222; control1Y: 5.474; control2X: -71.4; control2Y: 3.9; x: -71.4; y: 3.9 }
+ PathCubic { control1X: -71.627; control1Y: 5.265; control2X: -71.682; control2Y: 6.861; x: -71.576; y: 8.499 }
+ PathCubic { control1X: -71.576; control1Y: 8.499; control2X: -67.308; control2Y: 13.618; x: -61.009; y: 11.403 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -65.4; y: 11.546 }
+ PathCubic { control1X: -66.025; control1Y: 11.546; control2X: -66.531; control2Y: 10.406; x: -66.531; y: 9 }
+ PathCubic { control1X: -66.531; control1Y: 7.595; control2X: -66.025; control2Y: 6.455; x: -65.4; y: 6.455 }
+ PathCubic { control1X: -64.775; control1Y: 6.455; control2X: -64.268; control2Y: 7.595; x: -64.268; y: 9 }
+ PathCubic { control1X: -64.268; control1Y: 10.406; control2X: -64.775; control2Y: 11.546; x: -65.4; y: 11.546 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -65.4; y: 9 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -111; y: 109.601 }
+ PathCubic { control1X: -111; control1Y: 109.601; control2X: -116.6; control2Y: 119.601; x: -91.8; y: 113.601 }
+ PathCubic { control1X: -91.8; control1Y: 113.601; control2X: -77.8; control2Y: 112.401; x: -75.4; y: 110.001 }
+ PathCubic { control1X: -74.2; control1Y: 110.801; control2X: -65.834; control2Y: 113.734; x: -63; y: 114.401 }
+ PathCubic { control1X: -56.2; control1Y: 116.001; control2X: -47.8; control2Y: 106; x: -47.8; y: 106 }
+ PathCubic { control1X: -47.8; control1Y: 106; control2X: -43.2; control2Y: 95.5; x: -40.4; y: 95.5 }
+ PathCubic { control1X: -37.6; control1Y: 95.5; control2X: -40.8; control2Y: 97.1; x: -40.8; y: 97.1 }
+ PathCubic { control1X: -40.8; control1Y: 97.1; control2X: -47.4; control2Y: 107.201; x: -47; y: 108.801 }
+ PathCubic { control1X: -47; control1Y: 108.801; control2X: -52.2; control2Y: 128.801; x: -68.2; y: 129.601 }
+ PathCubic { control1X: -68.2; control1Y: 129.601; control2X: -84.35; control2Y: 130.551; x: -83; y: 136.401 }
+ PathCubic { control1X: -83; control1Y: 136.401; control2X: -74.2; control2Y: 134.001; x: -71.8; y: 136.401 }
+ PathCubic { control1X: -71.8; control1Y: 136.401; control2X: -61; control2Y: 136.001; x: -69; y: 142.401 }
+ PathLine { x: -75.8; y: 154.001 }
+ PathCubic { control1X: -75.8; control1Y: 154.001; control2X: -75.66; control2Y: 157.919; x: -85.8; y: 154.401 }
+ PathCubic { control1X: -95.6; control1Y: 151.001; control2X: -105.9; control2Y: 138.101; x: -105.9; y: 138.101 }
+ PathCubic { control1X: -105.9; control1Y: 138.101; control2X: -121.85; control2Y: 123.551; x: -111; y: 109.601 }
+ }
+
+ ShapePath {
+ fillColor: "#e59999"
+ strokeWidth: -1
+ PathMove { x: -112.2; y: 113.601 }
+ PathCubic { control1X: -112.2; control1Y: 113.601; control2X: -114.2; control2Y: 123.201; x: -77.4; y: 112.801 }
+ PathCubic { control1X: -77.4; control1Y: 112.801; control2X: -73; control2Y: 112.801; x: -70.6; y: 113.601 }
+ PathCubic { control1X: -68.2; control1Y: 114.401; control2X: -56.2; control2Y: 117.201; x: -54.2; y: 116.001 }
+ PathCubic { control1X: -54.2; control1Y: 116.001; control2X: -61.4; control2Y: 129.601; x: -73; y: 128.001 }
+ PathCubic { control1X: -73; control1Y: 128.001; control2X: -86.2; control2Y: 129.601; x: -85.8; y: 134.401 }
+ PathCubic { control1X: -85.8; control1Y: 134.401; control2X: -81.8; control2Y: 141.601; x: -77; y: 144.001 }
+ PathCubic { control1X: -77; control1Y: 144.001; control2X: -74.2; control2Y: 146.401; x: -74.6; y: 149.601 }
+ PathCubic { control1X: -75; control1Y: 152.801; control2X: -77.8; control2Y: 154.401; x: -79.8; y: 155.201 }
+ PathCubic { control1X: -81.8; control1Y: 156.001; control2X: -85; control2Y: 152.801; x: -86.6; y: 152.801 }
+ PathCubic { control1X: -88.2; control1Y: 152.801; control2X: -96.6; control2Y: 146.401; x: -101; y: 141.601 }
+ PathCubic { control1X: -105.4; control1Y: 136.801; control2X: -113.8; control2Y: 124.801; x: -113.4; y: 122.001 }
+ PathCubic { control1X: -113; control1Y: 119.201; control2X: -112.2; control2Y: 113.601; x: -112.2; y: 113.601 }
+ }
+
+ ShapePath {
+ fillColor: "#b26565"
+ strokeWidth: -1
+ PathMove { x: -109; y: 131.051 }
+ PathCubic { control1X: -106.4; control1Y: 135.001; control2X: -103.2; control2Y: 139.201; x: -101; y: 141.601 }
+ PathCubic { control1X: -96.6; control1Y: 146.401; control2X: -88.2; control2Y: 152.801; x: -86.6; y: 152.801 }
+ PathCubic { control1X: -85; control1Y: 152.801; control2X: -81.8; control2Y: 156.001; x: -79.8; y: 155.201 }
+ PathCubic { control1X: -77.8; control1Y: 154.401; control2X: -75; control2Y: 152.801; x: -74.6; y: 149.601 }
+ PathCubic { control1X: -74.2; control1Y: 146.401; control2X: -77; control2Y: 144.001; x: -77; y: 144.001 }
+ PathCubic { control1X: -80.066; control1Y: 142.468; control2X: -82.806; control2Y: 138.976; x: -84.385; y: 136.653 }
+ PathCubic { control1X: -84.385; control1Y: 136.653; control2X: -84.2; control2Y: 139.201; x: -89.4; y: 138.401 }
+ PathCubic { control1X: -94.6; control1Y: 137.601; control2X: -99.8; control2Y: 134.801; x: -101.4; y: 131.601 }
+ PathCubic { control1X: -103; control1Y: 128.401; control2X: -105.4; control2Y: 126.001; x: -103.8; y: 129.601 }
+ PathCubic { control1X: -102.2; control1Y: 133.201; control2X: -99.8; control2Y: 136.801; x: -98.2; y: 137.201 }
+ PathCubic { control1X: -96.6; control1Y: 137.601; control2X: -97; control2Y: 138.801; x: -99.4; y: 138.401 }
+ PathCubic { control1X: -101.8; control1Y: 138.001; control2X: -104.6; control2Y: 137.601; x: -109; y: 132.401 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: -111.6; y: 110.001 }
+ PathCubic { control1X: -111.6; control1Y: 110.001; control2X: -109.8; control2Y: 96.4; x: -108.6; y: 92.4 }
+ PathCubic { control1X: -108.6; control1Y: 92.4; control2X: -109.4; control2Y: 85.6; x: -107; y: 81.4 }
+ PathCubic { control1X: -104.6; control1Y: 77.2; control2X: -102.6; control2Y: 71; x: -99.6; y: 65.6 }
+ PathCubic { control1X: -96.6; control1Y: 60.2; control2X: -96.4; control2Y: 56.2; x: -92.4; y: 54.6 }
+ PathCubic { control1X: -88.4; control1Y: 53; control2X: -82.4; control2Y: 44.4; x: -79.6; y: 43.4 }
+ PathCubic { control1X: -76.8; control1Y: 42.4; control2X: -77; control2Y: 43.2; x: -77; y: 43.2 }
+ PathCubic { control1X: -77; control1Y: 43.2; control2X: -70.2; control2Y: 28.4; x: -56.6; y: 32.4 }
+ PathCubic { control1X: -56.6; control1Y: 32.4; control2X: -72.8; control2Y: 29.6; x: -57; y: 20.2 }
+ PathCubic { control1X: -57; control1Y: 20.2; control2X: -61.8; control2Y: 21.3; x: -58.5; y: 14.3 }
+ PathCubic { control1X: -56.299; control1Y: 9.632; control2X: -56.8; control2Y: 16.4; x: -67.8; y: 28.2 }
+ PathCubic { control1X: -67.8; control1Y: 28.2; control2X: -72.8; control2Y: 36.8; x: -78; y: 39.8 }
+ PathCubic { control1X: -83.2; control1Y: 42.8; control2X: -95.2; control2Y: 49.8; x: -96.4; y: 53.6 }
+ PathCubic { control1X: -97.6; control1Y: 57.4; control2X: -100.8; control2Y: 63.2; x: -102.8; y: 64.8 }
+ PathCubic { control1X: -104.8; control1Y: 66.4; control2X: -107.6; control2Y: 70.6; x: -108; y: 74 }
+ PathCubic { control1X: -108; control1Y: 74; control2X: -109.2; control2Y: 78; x: -110.6; y: 79.2 }
+ PathCubic { control1X: -112; control1Y: 80.4; control2X: -112.2; control2Y: 83.6; x: -112.2; y: 85.6 }
+ PathCubic { control1X: -112.2; control1Y: 87.6; control2X: -114.2; control2Y: 90.4; x: -114; y: 92.8 }
+ PathCubic { control1X: -114; control1Y: 92.8; control2X: -113.2; control2Y: 111.801; x: -113.6; y: 113.801 }
+ PathLine { x: -111.6; y: 110.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: -120.2; y: 114.601 }
+ PathCubic { control1X: -120.2; control1Y: 114.601; control2X: -122.2; control2Y: 113.201; x: -126.6; y: 119.201 }
+ PathCubic { control1X: -126.6; control1Y: 119.201; control2X: -119.3; control2Y: 152.201; x: -119.3; y: 153.601 }
+ PathCubic { control1X: -119.3; control1Y: 153.601; control2X: -118.2; control2Y: 151.501; x: -119.5; y: 144.301 }
+ PathCubic { control1X: -120.8; control1Y: 137.101; control2X: -121.7; control2Y: 124.401; x: -121.7; y: 124.401 }
+ PathLine { x: -120.2; y: 114.601 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: -98.6; y: 54 }
+ PathCubic { control1X: -98.6; control1Y: 54; control2X: -116.2; control2Y: 57.2; x: -115.8; y: 86.4 }
+ PathLine { x: -116.6; y: 111.201 }
+ PathCubic { control1X: -116.6; control1Y: 111.201; control2X: -117.8; control2Y: 85.6; x: -119; y: 84 }
+ PathCubic { control1X: -120.2; control1Y: 82.4; control2X: -116.2; control2Y: 71.2; x: -119.4; y: 77.2 }
+ PathCubic { control1X: -119.4; control1Y: 77.2; control2X: -133.4; control2Y: 91.2; x: -125.4; y: 112.401 }
+ PathCubic { control1X: -125.4; control1Y: 112.401; control2X: -123.9; control2Y: 115.701; x: -126.9; y: 111.101 }
+ PathCubic { control1X: -126.9; control1Y: 111.101; control2X: -131.5; control2Y: 98.5; x: -130.4; y: 92.1 }
+ PathCubic { control1X: -130.4; control1Y: 92.1; control2X: -130.2; control2Y: 89.9; x: -128.3; y: 87.1 }
+ PathCubic { control1X: -128.3; control1Y: 87.1; control2X: -119.7; control2Y: 75.4; x: -117; y: 73.1 }
+ PathCubic { control1X: -117; control1Y: 73.1; control2X: -115.2; control2Y: 58.7; x: -99.8; y: 53.5 }
+ PathCubic { control1X: -99.8; control1Y: 53.5; control2X: -94.1; control2Y: 51.2; x: -98.6; y: 54 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 40.8; y: -12.2 }
+ PathCubic { control1X: 41.46; control1Y: -12.554; control2X: 41.451; control2Y: -13.524; x: 42.031; y: -13.697 }
+ PathCubic { control1X: 43.18; control1Y: -14.041; control2X: 43.344; control2Y: -15.108; x: 43.862; y: -15.892 }
+ PathCubic { control1X: 44.735; control1Y: -17.211; control2X: 44.928; control2Y: -18.744; x: 45.51; y: -20.235 }
+ PathCubic { control1X: 45.782; control1Y: -20.935; control2X: 45.809; control2Y: -21.89; x: 45.496; y: -22.55 }
+ PathCubic { control1X: 44.322; control1Y: -25.031; control2X: 43.62; control2Y: -27.48; x: 42.178; y: -29.906 }
+ PathCubic { control1X: 41.91; control1Y: -30.356; control2X: 41.648; control2Y: -31.15; x: 41.447; y: -31.748 }
+ PathCubic { control1X: 40.984; control1Y: -33.132; control2X: 39.727; control2Y: -34.123; x: 38.867; y: -35.443 }
+ PathCubic { control1X: 38.579; control1Y: -35.884; control2X: 39.104; control2Y: -36.809; x: 38.388; y: -36.893 }
+ PathCubic { control1X: 37.491; control1Y: -36.998; control2X: 36.042; control2Y: -37.578; x: 35.809; y: -36.552 }
+ PathCubic { control1X: 35.221; control1Y: -33.965; control2X: 36.232; control2Y: -31.442; x: 37.2; y: -29 }
+ PathCubic { control1X: 36.418; control1Y: -28.308; control2X: 36.752; control2Y: -27.387; x: 36.904; y: -26.62 }
+ PathCubic { control1X: 37.614; control1Y: -23.014; control2X: 36.416; control2Y: -19.662; x: 35.655; y: -16.188 }
+ PathCubic { control1X: 35.632; control1Y: -16.084; control2X: 35.974; control2Y: -15.886; x: 35.946; y: -15.824 }
+ PathCubic { control1X: 34.724; control1Y: -13.138; control2X: 33.272; control2Y: -10.693; x: 31.453; y: -8.312 }
+ PathCubic { control1X: 30.695; control1Y: -7.32; control2X: 29.823; control2Y: -6.404; x: 29.326; y: -5.341 }
+ PathCubic { control1X: 28.958; control1Y: -4.554; control2X: 28.55; control2Y: -3.588; x: 28.8; y: -2.6 }
+ PathCubic { control1X: 25.365; control1Y: 0.18; control2X: 23.115; control2Y: 4.025; x: 20.504; y: 7.871 }
+ PathCubic { control1X: 20.042; control1Y: 8.551; control2X: 20.333; control2Y: 9.76; x: 20.884; y: 10.029 }
+ PathCubic { control1X: 21.697; control1Y: 10.427; control2X: 22.653; control2Y: 9.403; x: 23.123; y: 8.557 }
+ PathCubic { control1X: 23.512; control1Y: 7.859; control2X: 23.865; control2Y: 7.209; x: 24.356; y: 6.566 }
+ PathCubic { control1X: 24.489; control1Y: 6.391; control2X: 24.31; control2Y: 5.972; x: 24.445; y: 5.851 }
+ PathCubic { control1X: 27.078; control1Y: 3.504; control2X: 28.747; control2Y: 0.568; x: 31.2; y: -1.8 }
+ PathCubic { control1X: 33.15; control1Y: -2.129; control2X: 34.687; control2Y: -3.127; x: 36.435; y: -4.14 }
+ PathCubic { control1X: 36.743; control1Y: -4.319; control2X: 37.267; control2Y: -4.07; x: 37.557; y: -4.265 }
+ PathCubic { control1X: 39.31; control1Y: -5.442; control2X: 39.308; control2Y: -7.478; x: 39.414; y: -9.388 }
+ PathCubic { control1X: 39.464; control1Y: -10.272; control2X: 39.66; control2Y: -11.589; x: 40.8; y: -12.2 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 31.959; y: -16.666 }
+ PathCubic { control1X: 32.083; control1Y: -16.743; control2X: 31.928; control2Y: -17.166; x: 32.037; y: -17.382 }
+ PathCubic { control1X: 32.199; control1Y: -17.706; control2X: 32.602; control2Y: -17.894; x: 32.764; y: -18.218 }
+ PathCubic { control1X: 32.873; control1Y: -18.434; control2X: 32.71; control2Y: -18.814; x: 32.846; y: -18.956 }
+ PathCubic { control1X: 35.179; control1Y: -21.403; control2X: 35.436; control2Y: -24.427; x: 34.4; y: -27.4 }
+ PathCubic { control1X: 35.424; control1Y: -28.02; control2X: 35.485; control2Y: -29.282; x: 35.06; y: -30.129 }
+ PathCubic { control1X: 34.207; control1Y: -31.829; control2X: 34.014; control2Y: -33.755; x: 33.039; y: -35.298 }
+ PathCubic { control1X: 32.237; control1Y: -36.567; control2X: 30.659; control2Y: -37.811; x: 29.288; y: -36.508 }
+ PathCubic { control1X: 28.867; control1Y: -36.108; control2X: 28.546; control2Y: -35.321; x: 28.824; y: -34.609 }
+ PathCubic { control1X: 28.888; control1Y: -34.446; control2X: 29.173; control2Y: -34.3; x: 29.146; y: -34.218 }
+ PathCubic { control1X: 29.039; control1Y: -33.894; control2X: 28.493; control2Y: -33.67; x: 28.487; y: -33.398 }
+ PathCubic { control1X: 28.457; control1Y: -31.902; control2X: 27.503; control2Y: -30.391; x: 28.133; y: -29.062 }
+ PathCubic { control1X: 28.905; control1Y: -27.433; control2X: 29.724; control2Y: -25.576; x: 30.4; y: -23.8 }
+ PathCubic { control1X: 29.166; control1Y: -21.684; control2X: 30.199; control2Y: -19.235; x: 28.446; y: -17.358 }
+ PathCubic { control1X: 28.31; control1Y: -17.212; control2X: 28.319; control2Y: -16.826; x: 28.441; y: -16.624 }
+ PathCubic { control1X: 28.733; control1Y: -16.138; control2X: 29.139; control2Y: -15.732; x: 29.625; y: -15.44 }
+ PathCubic { control1X: 29.827; control1Y: -15.319; control2X: 30.175; control2Y: -15.317; x: 30.375; y: -15.441 }
+ PathCubic { control1X: 30.953; control1Y: -15.803; control2X: 31.351; control2Y: -16.29; x: 31.959; y: -16.666 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 94.771; y: -26.977 }
+ PathCubic { control1X: 96.16; control1Y: -25.185; control2X: 96.45; control2Y: -22.39; x: 94.401; y: -21 }
+ PathCubic { control1X: 94.951; control1Y: -17.691; control2X: 98.302; control2Y: -19.67; x: 100.401; y: -20.2 }
+ PathCubic { control1X: 100.292; control1Y: -20.588; control2X: 100.519; control2Y: -20.932; x: 100.802; y: -20.937 }
+ PathCubic { control1X: 101.859; control1Y: -20.952; control2X: 102.539; control2Y: -21.984; x: 103.601; y: -21.8 }
+ PathCubic { control1X: 104.035; control1Y: -23.357; control2X: 105.673; control2Y: -24.059; x: 106.317; y: -25.439 }
+ PathCubic { control1X: 108.043; control1Y: -29.134; control2X: 107.452; control2Y: -33.407; x: 104.868; y: -36.653 }
+ PathCubic { control1X: 104.666; control1Y: -36.907; control2X: 104.883; control2Y: -37.424; x: 104.759; y: -37.786 }
+ PathCubic { control1X: 104.003; control1Y: -39.997; control2X: 101.935; control2Y: -40.312; x: 100.001; y: -41 }
+ PathCubic { control1X: 98.824; control1Y: -44.875; control2X: 98.163; control2Y: -48.906; x: 96.401; y: -52.6 }
+ PathCubic { control1X: 94.787; control1Y: -52.85; control2X: 94.089; control2Y: -54.589; x: 92.752; y: -55.309 }
+ PathCubic { control1X: 91.419; control1Y: -56.028; control2X: 90.851; control2Y: -54.449; x: 90.892; y: -53.403 }
+ PathCubic { control1X: 90.899; control1Y: -53.198; control2X: 91.351; control2Y: -52.974; x: 91.181; y: -52.609 }
+ PathCubic { control1X: 91.105; control1Y: -52.445; control2X: 90.845; control2Y: -52.334; x: 90.845; y: -52.2 }
+ PathCubic { control1X: 90.846; control1Y: -52.065; control2X: 91.067; control2Y: -51.934; x: 91.201; y: -51.8 }
+ PathCubic { control1X: 90.283; control1Y: -50.98; control2X: 88.86; control2Y: -50.503; x: 88.565; y: -49.358 }
+ PathCubic { control1X: 87.611; control1Y: -45.648; control2X: 90.184; control2Y: -42.523; x: 91.852; y: -39.322 }
+ PathCubic { control1X: 92.443; control1Y: -38.187; control2X: 91.707; control2Y: -36.916; x: 90.947; y: -35.708 }
+ PathCubic { control1X: 90.509; control1Y: -35.013; control2X: 90.617; control2Y: -33.886; x: 90.893; y: -33.03 }
+ PathCubic { control1X: 91.645; control1Y: -30.699; control2X: 93.236; control2Y: -28.96; x: 94.771; y: -26.977 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 57.611; y: -8.591 }
+ PathCubic { control1X: 56.124; control1Y: -6.74; control2X: 52.712; control2Y: -4.171; x: 55.629; y: -2.243 }
+ PathCubic { control1X: 55.823; control1Y: -2.114; control2X: 56.193; control2Y: -2.11; x: 56.366; y: -2.244 }
+ PathCubic { control1X: 58.387; control1Y: -3.809; control2X: 60.39; control2Y: -4.712; x: 62.826; y: -5.294 }
+ PathCubic { control1X: 62.95; control1Y: -5.323; control2X: 63.224; control2Y: -4.856; x: 63.593; y: -5.017 }
+ PathCubic { control1X: 65.206; control1Y: -5.72; control2X: 67.216; control2Y: -5.662; x: 68.4; y: -7 }
+ PathCubic { control1X: 72.167; control1Y: -6.776; control2X: 75.732; control2Y: -7.892; x: 79.123; y: -9.2 }
+ PathCubic { control1X: 80.284; control1Y: -9.648; control2X: 81.554; control2Y: -10.207; x: 82.755; y: -10.709 }
+ PathCubic { control1X: 84.131; control1Y: -11.285; control2X: 85.335; control2Y: -12.213; x: 86.447; y: -13.354 }
+ PathCubic { control1X: 86.58; control1Y: -13.49; control2X: 86.934; control2Y: -13.4; x: 87.201; y: -13.4 }
+ PathCubic { control1X: 87.161; control1Y: -14.263; control2X: 88.123; control2Y: -14.39; x: 88.37; y: -15.012 }
+ PathCubic { control1X: 88.462; control1Y: -15.244; control2X: 88.312; control2Y: -15.64; x: 88.445; y: -15.742 }
+ PathCubic { control1X: 90.583; control1Y: -17.372; control2X: 91.503; control2Y: -19.39; x: 90.334; y: -21.767 }
+ PathCubic { control1X: 90.049; control1Y: -22.345; control2X: 89.8; control2Y: -22.963; x: 89.234; y: -23.439 }
+ PathCubic { control1X: 88.149; control1Y: -24.35; control2X: 87.047; control2Y: -23.496; x: 86; y: -23.8 }
+ PathCubic { control1X: 85.841; control1Y: -23.172; control2X: 85.112; control2Y: -23.344; x: 84.726; y: -23.146 }
+ PathCubic { control1X: 83.867; control1Y: -22.707; control2X: 82.534; control2Y: -23.292; x: 81.675; y: -22.854 }
+ PathCubic { control1X: 80.313; control1Y: -22.159; control2X: 79.072; control2Y: -21.99; x: 77.65; y: -21.613 }
+ PathCubic { control1X: 77.338; control1Y: -21.531; control2X: 76.56; control2Y: -21.627; x: 76.4; y: -21 }
+ PathCubic { control1X: 76.266; control1Y: -21.134; control2X: 76.118; control2Y: -21.368; x: 76.012; y: -21.346 }
+ PathCubic { control1X: 74.104; control1Y: -20.95; control2X: 72.844; control2Y: -20.736; x: 71.543; y: -19.044 }
+ PathCubic { control1X: 71.44; control1Y: -18.911; control2X: 70.998; control2Y: -19.09; x: 70.839; y: -18.955 }
+ PathCubic { control1X: 69.882; control1Y: -18.147; control2X: 69.477; control2Y: -16.913; x: 68.376; y: -16.241 }
+ PathCubic { control1X: 68.175; control1Y: -16.118; control2X: 67.823; control2Y: -16.286; x: 67.629; y: -16.157 }
+ PathCubic { control1X: 66.983; control1Y: -15.726; control2X: 66.616; control2Y: -15.085; x: 65.974; y: -14.638 }
+ PathCubic { control1X: 65.645; control1Y: -14.409; control2X: 65.245; control2Y: -14.734; x: 65.277; y: -14.99 }
+ PathCubic { control1X: 65.522; control1Y: -16.937; control2X: 66.175; control2Y: -18.724; x: 65.6; y: -20.6 }
+ PathCubic { control1X: 67.677; control1Y: -23.12; control2X: 70.194; control2Y: -25.069; x: 72; y: -27.8 }
+ PathCubic { control1X: 72.015; control1Y: -29.966; control2X: 72.707; control2Y: -32.112; x: 72.594; y: -34.189 }
+ PathCubic { control1X: 72.584; control1Y: -34.382; control2X: 72.296; control2Y: -35.115; x: 72.17; y: -35.462 }
+ PathCubic { control1X: 71.858; control1Y: -36.316; control2X: 72.764; control2Y: -37.382; x: 71.92; y: -38.106 }
+ PathCubic { control1X: 70.516; control1Y: -39.309; control2X: 69.224; control2Y: -38.433; x: 68.4; y: -37 }
+ PathCubic { control1X: 66.562; control1Y: -36.61; control2X: 64.496; control2Y: -35.917; x: 62.918; y: -37.151 }
+ PathCubic { control1X: 61.911; control1Y: -37.938; control2X: 61.333; control2Y: -38.844; x: 60.534; y: -39.9 }
+ PathCubic { control1X: 59.549; control1Y: -41.202; control2X: 59.884; control2Y: -42.638; x: 59.954; y: -44.202 }
+ PathCubic { control1X: 59.96; control1Y: -44.33; control2X: 59.645; control2Y: -44.466; x: 59.645; y: -44.6 }
+ PathCubic { control1X: 59.646; control1Y: -44.735; control2X: 59.866; control2Y: -44.866; x: 60; y: -45 }
+ PathCubic { control1X: 59.294; control1Y: -45.626; control2X: 59.019; control2Y: -46.684; x: 58; y: -47 }
+ PathCubic { control1X: 58.305; control1Y: -48.092; control2X: 57.629; control2Y: -48.976; x: 56.758; y: -49.278 }
+ PathCubic { control1X: 54.763; control1Y: -49.969; control2X: 53.086; control2Y: -48.057; x: 51.194; y: -47.984 }
+ PathCubic { control1X: 50.68; control1Y: -47.965; control2X: 50.213; control2Y: -49.003; x: 49.564; y: -49.328 }
+ PathCubic { control1X: 49.132; control1Y: -49.544; control2X: 48.428; control2Y: -49.577; x: 48.066; y: -49.311 }
+ PathCubic { control1X: 47.378; control1Y: -48.807; control2X: 46.789; control2Y: -48.693; x: 46.031; y: -48.488 }
+ PathCubic { control1X: 44.414; control1Y: -48.052; control2X: 43.136; control2Y: -46.958; x: 41.656; y: -46.103 }
+ PathCubic { control1X: 40.171; control1Y: -45.246; control2X: 39.216; control2Y: -43.809; x: 38.136; y: -42.489 }
+ PathCubic { control1X: 37.195; control1Y: -41.337; control2X: 37.059; control2Y: -38.923; x: 38.479; y: -38.423 }
+ PathCubic { control1X: 40.322; control1Y: -37.773; control2X: 41.626; control2Y: -40.476; x: 43.592; y: -40.15 }
+ PathCubic { control1X: 43.904; control1Y: -40.099; control2X: 44.11; control2Y: -39.788; x: 44; y: -39.4 }
+ PathCubic { control1X: 44.389; control1Y: -39.291; control2X: 44.607; control2Y: -39.52; x: 44.8; y: -39.8 }
+ PathCubic { control1X: 45.658; control1Y: -38.781; control2X: 46.822; control2Y: -38.444; x: 47.76; y: -37.571 }
+ PathCubic { control1X: 48.73; control1Y: -36.667; control2X: 50.476; control2Y: -37.085; x: 51.491; y: -36.088 }
+ PathCubic { control1X: 53.02; control1Y: -34.586; control2X: 52.461; control2Y: -31.905; x: 54.4; y: -30.6 }
+ PathCubic { control1X: 53.814; control1Y: -29.287; control2X: 53.207; control2Y: -28.01; x: 52.872; y: -26.583 }
+ PathCubic { control1X: 52.59; control1Y: -25.377; control2X: 53.584; control2Y: -24.18; x: 54.795; y: -24.271 }
+ PathCubic { control1X: 56.053; control1Y: -24.365; control2X: 56.315; control2Y: -25.124; x: 56.8; y: -26.2 }
+ PathCubic { control1X: 57.067; control1Y: -25.933; control2X: 57.536; control2Y: -25.636; x: 57.495; y: -25.42 }
+ PathCubic { control1X: 57.038; control1Y: -23.033; control2X: 56.011; control2Y: -21.04; x: 55.553; y: -18.609 }
+ PathCubic { control1X: 55.494; control1Y: -18.292; control2X: 55.189; control2Y: -18.09; x: 54.8; y: -18.2 }
+ PathCubic { control1X: 54.332; control1Y: -14.051; control2X: 50.28; control2Y: -11.657; x: 47.735; y: -8.492 }
+ PathCubic { control1X: 47.332; control1Y: -7.99; control2X: 47.328; control2Y: -6.741; x: 47.737; y: -6.338 }
+ PathCubic { control1X: 49.14; control1Y: -4.951; control2X: 51.1; control2Y: -6.497; x: 52.8; y: -7 }
+ PathCubic { control1X: 53.013; control1Y: -8.206; control2X: 53.872; control2Y: -9.148; x: 55.204; y: -9.092 }
+ PathCubic { control1X: 55.46; control1Y: -9.082; control2X: 55.695; control2Y: -9.624; x: 56.019; y: -9.754 }
+ PathCubic { control1X: 56.367; control1Y: -9.892; control2X: 56.869; control2Y: -9.668; x: 57.155; y: -9.866 }
+ PathCubic { control1X: 58.884; control1Y: -11.061; control2X: 60.292; control2Y: -12.167; x: 62.03; y: -13.356 }
+ PathCubic { control1X: 62.222; control1Y: -13.487; control2X: 62.566; control2Y: -13.328; x: 62.782; y: -13.436 }
+ PathCubic { control1X: 63.107; control1Y: -13.598; control2X: 63.294; control2Y: -13.985; x: 63.617; y: -14.17 }
+ PathCubic { control1X: 63.965; control1Y: -14.37; control2X: 64.207; control2Y: -14.08; x: 64.4; y: -13.8 }
+ PathCubic { control1X: 63.754; control1Y: -13.451; control2X: 63.75; control2Y: -12.494; x: 63.168; y: -12.292 }
+ PathCubic { control1X: 62.393; control1Y: -12.024; control2X: 61.832; control2Y: -11.511; x: 61.158; y: -11.064 }
+ PathCubic { control1X: 60.866; control1Y: -10.871; control2X: 60.207; control2Y: -11.119; x: 60.103; y: -10.94 }
+ PathCubic { control1X: 59.505; control1Y: -9.912; control2X: 58.321; control2Y: -9.474; x: 57.611; y: -8.591 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 2.2; y: -58 }
+ PathCubic { control1X: 2.2; control1Y: -58; control2X: -7.038; control2Y: -60.872; x: -18.2; y: -35.2 }
+ PathCubic { control1X: -18.2; control1Y: -35.2; control2X: -20.6; control2Y: -30; x: -23; y: -28 }
+ PathCubic { control1X: -25.4; control1Y: -26; control2X: -36.6; control2Y: -22.4; x: -38.6; y: -18.4 }
+ PathLine { x: -49; y: -2.4 }
+ PathCubic { control1X: -49; control1Y: -2.4; control2X: -34.2; control2Y: -18.4; x: -31; y: -20.8 }
+ PathCubic { control1X: -31; control1Y: -20.8; control2X: -23; control2Y: -29.2; x: -26.2; y: -22.4 }
+ PathCubic { control1X: -26.2; control1Y: -22.4; control2X: -40.2; control2Y: -11.6; x: -39; y: -2.4 }
+ PathCubic { control1X: -39; control1Y: -2.4; control2X: -44.6; control2Y: 12; x: -45.4; y: 14 }
+ PathCubic { control1X: -45.4; control1Y: 14; control2X: -29.4; control2Y: -18; x: -27; y: -19.2 }
+ PathCubic { control1X: -24.6; control1Y: -20.4; control2X: -23.4; control2Y: -20.4; x: -24.6; y: -16.8 }
+ PathCubic { control1X: -25.8; control1Y: -13.2; control2X: -26.2; control2Y: 3.2; x: -29; y: 5.2 }
+ PathCubic { control1X: -29; control1Y: 5.2; control2X: -21; control2Y: -15.2; x: -21.8; y: -18.4 }
+ PathCubic { control1X: -21.8; control1Y: -18.4; control2X: -18.6; control2Y: -22; x: -16.2; y: -16.8 }
+ PathLine { x: -17.4; y: -0.8 }
+ PathLine { x: -13; y: 11.2 }
+ PathCubic { control1X: -13; control1Y: 11.2; control2X: -15.4; control2Y: 0; x: -13.8; y: -15.6 }
+ PathCubic { control1X: -13.8; control1Y: -15.6; control2X: -15.8; control2Y: -26; x: -11.8; y: -20.4 }
+ PathCubic { control1X: -7.8; control1Y: -14.8; control2X: 1.8; control2Y: -8.8; x: 1.8; y: -4 }
+ PathCubic { control1X: 1.8; control1Y: -4; control2X: -3.4; control2Y: -21.6; x: -12.6; y: -26.4 }
+ PathLine { x: -16.6; y: -20.4 }
+ PathLine { x: -17.8; y: -22.4 }
+ PathCubic { control1X: -17.8; control1Y: -22.4; control2X: -21.4; control2Y: -23.2; x: -17; y: -30 }
+ PathCubic { control1X: -12.6; control1Y: -36.8; control2X: -13; control2Y: -37.6; x: -13; y: -37.6 }
+ PathCubic { control1X: -13; control1Y: -37.6; control2X: -6.6; control2Y: -30.4; x: -5; y: -30.4 }
+ PathCubic { control1X: -5; control1Y: -30.4; control2X: 8.2; control2Y: -38; x: 9.4; y: -13.6 }
+ PathCubic { control1X: 9.4; control1Y: -13.6; control2X: 16.2; control2Y: -28; x: 7; y: -34.8 }
+ PathCubic { control1X: 7; control1Y: -34.8; control2X: -7.8; control2Y: -36.8; x: -6.6; y: -42 }
+ PathLine { x: 0.6; y: -54.4 }
+ PathCubic { control1X: 4.2; control1Y: -59.6; control2X: 2.6; control2Y: -56.8; x: 2.6; y: -56.8 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -17.8; y: -41.6 }
+ PathCubic { control1X: -17.8; control1Y: -41.6; control2X: -30.6; control2Y: -41.6; x: -33.8; y: -36.4 }
+ PathLine { x: -41; y: -26.8 }
+ PathCubic { control1X: -41; control1Y: -26.8; control2X: -23.8; control2Y: -36.8; x: -19.8; y: -38 }
+ PathCubic { control1X: -15.8; control1Y: -39.2; control2X: -17.8; control2Y: -41.6; x: -17.8; y: -41.6 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -57.8; y: -35.2 }
+ PathCubic { control1X: -57.8; control1Y: -35.2; control2X: -59.8; control2Y: -34; x: -60.2; y: -31.2 }
+ PathCubic { control1X: -60.6; control1Y: -28.4; control2X: -63; control2Y: -28; x: -62.2; y: -25.2 }
+ PathCubic { control1X: -61.4; control1Y: -22.4; control2X: -59.4; control2Y: -20; x: -59.4; y: -24 }
+ PathCubic { control1X: -59.4; control1Y: -28; control2X: -57.8; control2Y: -30; x: -57; y: -31.2 }
+ PathCubic { control1X: -56.2; control1Y: -32.4; control2X: -54.6; control2Y: -36.8; x: -57.8; y: -35.2 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -66.6; y: 26 }
+ PathCubic { control1X: -66.6; control1Y: 26; control2X: -75; control2Y: 22; x: -78.2; y: 18.4 }
+ PathCubic { control1X: -81.4; control1Y: 14.8; control2X: -80.948; control2Y: 19.966; x: -85.8; y: 19.6 }
+ PathCubic { control1X: -91.647; control1Y: 19.159; control2X: -90.6; control2Y: 3.2; x: -90.6; y: 3.2 }
+ PathLine { x: -94.6; y: 10.8 }
+ PathCubic { control1X: -94.6; control1Y: 10.8; control2X: -95.8; control2Y: 25.2; x: -87.8; y: 22.8 }
+ PathCubic { control1X: -83.893; control1Y: 21.628; control2X: -82.6; control2Y: 23.2; x: -84.2; y: 24 }
+ PathCubic { control1X: -85.8; control1Y: 24.8; control2X: -78.6; control2Y: 25.2; x: -81.4; y: 26.8 }
+ PathCubic { control1X: -84.2; control1Y: 28.4; control2X: -69.8; control2Y: 23.2; x: -72.2; y: 33.6 }
+ PathLine { x: -66.6; y: 26 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -79.2; y: 40.4 }
+ PathCubic { control1X: -79.2; control1Y: 40.4; control2X: -94.6; control2Y: 44.8; x: -98.2; y: 35.2 }
+ PathCubic { control1X: -98.2; control1Y: 35.2; control2X: -103; control2Y: 37.6; x: -100.8; y: 40.6 }
+ PathCubic { control1X: -98.6; control1Y: 43.6; control2X: -97.4; control2Y: 44; x: -97.4; y: 44 }
+ PathCubic { control1X: -97.4; control1Y: 44; control2X: -92; control2Y: 45.2; x: -92.6; y: 46 }
+ PathCubic { control1X: -93.2; control1Y: 46.8; control2X: -95.6; control2Y: 50.2; x: -95.6; y: 50.2 }
+ PathCubic { control1X: -95.6; control1Y: 50.2; control2X: -85.4; control2Y: 44.2; x: -79.2; y: 40.4 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 149.201; y: 118.601 }
+ PathCubic { control1X: 148.774; control1Y: 120.735; control2X: 147.103; control2Y: 121.536; x: 145.201; y: 122.201 }
+ PathCubic { control1X: 143.284; control1Y: 121.243; control2X: 140.686; control2Y: 118.137; x: 138.801; y: 120.201 }
+ PathCubic { control1X: 138.327; control1Y: 119.721; control2X: 137.548; control2Y: 119.661; x: 137.204; y: 118.999 }
+ PathCubic { control1X: 136.739; control1Y: 118.101; control2X: 137.011; control2Y: 117.055; x: 136.669; y: 116.257 }
+ PathCubic { control1X: 136.124; control1Y: 114.985; control2X: 135.415; control2Y: 113.619; x: 135.601; y: 112.201 }
+ PathCubic { control1X: 137.407; control1Y: 111.489; control2X: 138.002; control2Y: 109.583; x: 137.528; y: 107.82 }
+ PathCubic { control1X: 137.459; control1Y: 107.563; control2X: 137.03; control2Y: 107.366; x: 137.23; y: 107.017 }
+ PathCubic { control1X: 137.416; control1Y: 106.694; control2X: 137.734; control2Y: 106.467; x: 138.001; y: 106.2 }
+ PathCubic { control1X: 137.866; control1Y: 106.335; control2X: 137.721; control2Y: 106.568; x: 137.61; y: 106.548 }
+ PathCubic { control1X: 137; control1Y: 106.442; control2X: 137.124; control2Y: 105.805; x: 137.254; y: 105.418 }
+ PathCubic { control1X: 137.839; control1Y: 103.672; control2X: 139.853; control2Y: 103.408; x: 141.201; y: 104.6 }
+ PathCubic { control1X: 141.457; control1Y: 104.035; control2X: 141.966; control2Y: 104.229; x: 142.401; y: 104.2 }
+ PathCubic { control1X: 142.351; control1Y: 103.621; control2X: 142.759; control2Y: 103.094; x: 142.957; y: 102.674 }
+ PathCubic { control1X: 143.475; control1Y: 101.576; control2X: 145.104; control2Y: 102.682; x: 145.901; y: 102.07 }
+ PathCubic { control1X: 146.977; control1Y: 101.245; control2X: 148.04; control2Y: 100.546; x: 149.118; y: 101.149 }
+ PathCubic { control1X: 150.927; control1Y: 102.162; control2X: 152.636; control2Y: 103.374; x: 153.835; y: 105.115 }
+ PathCubic { control1X: 154.41; control1Y: 105.949; control2X: 154.65; control2Y: 107.23; x: 154.592; y: 108.188 }
+ PathCubic { control1X: 154.554; control1Y: 108.835; control2X: 153.173; control2Y: 108.483; x: 152.83; y: 109.412 }
+ PathCubic { control1X: 152.185; control1Y: 111.16; control2X: 154.016; control2Y: 111.679; x: 154.772; y: 113.017 }
+ PathCubic { control1X: 154.97; control1Y: 113.366; control2X: 154.706; control2Y: 113.67; x: 154.391; y: 113.768 }
+ PathCubic { control1X: 153.98; control1Y: 113.896; control2X: 153.196; control2Y: 113.707; x: 153.334; y: 114.16 }
+ PathCubic { control1X: 154.306; control1Y: 117.353; control2X: 151.55; control2Y: 118.031; x: 149.201; y: 118.601 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 139.6; y: 138.201 }
+ PathCubic { control1X: 139.593; control1Y: 136.463; control2X: 137.992; control2Y: 134.707; x: 139.201; y: 133.001 }
+ PathCubic { control1X: 139.336; control1Y: 133.135; control2X: 139.467; control2Y: 133.356; x: 139.601; y: 133.356 }
+ PathCubic { control1X: 139.736; control1Y: 133.356; control2X: 139.867; control2Y: 133.135; x: 140.001; y: 133.001 }
+ PathCubic { control1X: 141.496; control1Y: 135.217; control2X: 145.148; control2Y: 136.145; x: 145.006; y: 138.991 }
+ PathCubic { control1X: 144.984; control1Y: 139.438; control2X: 143.897; control2Y: 140.356; x: 144.801; y: 141.001 }
+ PathCubic { control1X: 142.988; control1Y: 142.349; control2X: 142.933; control2Y: 144.719; x: 142.001; y: 146.601 }
+ PathCubic { control1X: 140.763; control1Y: 146.315; control2X: 139.551; control2Y: 145.952; x: 138.401; y: 145.401 }
+ PathCubic { control1X: 138.753; control1Y: 143.915; control2X: 138.636; control2Y: 142.231; x: 139.456; y: 140.911 }
+ PathCubic { control1X: 139.89; control1Y: 140.213; control2X: 139.603; control2Y: 139.134; x: 139.6; y: 138.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -26.6; y: 129.201 }
+ PathCubic { control1X: -26.6; control1Y: 129.201; control2X: -43.458; control2Y: 139.337; x: -29.4; y: 124.001 }
+ PathCubic { control1X: -20.6; control1Y: 114.401; control2X: -10.6; control2Y: 108.801; x: -10.6; y: 108.801 }
+ PathCubic { control1X: -10.6; control1Y: 108.801; control2X: -0.2; control2Y: 104.4; x: 3.4; y: 103.2 }
+ PathCubic { control1X: 7; control1Y: 102; control2X: 22.2; control2Y: 96.8; x: 25.4; y: 96.4 }
+ PathCubic { control1X: 28.6; control1Y: 96; control2X: 38.2; control2Y: 92; x: 45; y: 96 }
+ PathCubic { control1X: 51.8; control1Y: 100; control2X: 59.8; control2Y: 104.4; x: 59.8; y: 104.4 }
+ PathCubic { control1X: 59.8; control1Y: 104.4; control2X: 43.4; control2Y: 96; x: 39.8; y: 98.4 }
+ PathCubic { control1X: 36.2; control1Y: 100.8; control2X: 29; control2Y: 100.4; x: 23; y: 103.6 }
+ PathCubic { control1X: 23; control1Y: 103.6; control2X: 8.2; control2Y: 108.001; x: 5; y: 110.001 }
+ PathCubic { control1X: 1.8; control1Y: 112.001; control2X: -8.6; control2Y: 123.601; x: -10.2; y: 122.801 }
+ PathCubic { control1X: -11.8; control1Y: 122.001; control2X: -9.8; control2Y: 121.601; x: -8.6; y: 118.801 }
+ PathCubic { control1X: -7.4; control1Y: 116.001; control2X: -9.4; control2Y: 114.401; x: -17.4; y: 120.801 }
+ PathCubic { control1X: -25.4; control1Y: 127.201; control2X: -26.6; control2Y: 129.201; x: -26.6; y: 129.201 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -19.195; y: 123.234 }
+ PathCubic { control1X: -19.195; control1Y: 123.234; control2X: -17.785; control2Y: 110.194; x: -9.307; y: 111.859 }
+ PathCubic { control1X: -9.307; control1Y: 111.859; control2X: -1.081; control2Y: 107.689; x: 1.641; y: 105.721 }
+ PathCubic { control1X: 1.641; control1Y: 105.721; control2X: 9.78; control2Y: 104.019; x: 11.09; y: 103.402 }
+ PathCubic { control1X: 29.569; control1Y: 94.702; control2X: 44.288; control2Y: 99.221; x: 44.835; y: 98.101 }
+ PathCubic { control1X: 45.381; control1Y: 96.982; control2X: 65.006; control2Y: 104.099; x: 68.615; y: 108.185 }
+ PathCubic { control1X: 69.006; control1Y: 108.628; control2X: 58.384; control2Y: 102.588; x: 48.686; y: 100.697 }
+ PathCubic { control1X: 40.413; control1Y: 99.083; control2X: 18.811; control2Y: 100.944; x: 7.905; y: 106.48 }
+ PathCubic { control1X: 4.932; control1Y: 107.989; control2X: -4.013; control2Y: 113.773; x: -6.544; y: 113.662 }
+ PathCubic { control1X: -9.075; control1Y: 113.55; control2X: -19.195; control2Y: 123.234; x: -19.195; y: 123.234 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -23; y: 148.801 }
+ PathCubic { control1X: -23; control1Y: 148.801; control2X: -38.2; control2Y: 146.401; x: -21.4; y: 144.801 }
+ PathCubic { control1X: -21.4; control1Y: 144.801; control2X: -3.4; control2Y: 142.801; x: 0.6; y: 137.601 }
+ PathCubic { control1X: 0.6; control1Y: 137.601; control2X: 14.2; control2Y: 128.401; x: 17; y: 128.001 }
+ PathCubic { control1X: 19.8; control1Y: 127.601; control2X: 49.8; control2Y: 120.401; x: 50.2; y: 118.001 }
+ PathCubic { control1X: 50.6; control1Y: 115.601; control2X: 56.2; control2Y: 115.601; x: 57.8; y: 116.401 }
+ PathCubic { control1X: 59.4; control1Y: 117.201; control2X: 58.6; control2Y: 118.401; x: 55.8; y: 119.201 }
+ PathCubic { control1X: 53; control1Y: 120.001; control2X: 21.8; control2Y: 136.401; x: 15.4; y: 137.601 }
+ PathCubic { control1X: 9; control1Y: 138.801; control2X: -2.6; control2Y: 146.401; x: -7.4; y: 147.601 }
+ PathCubic { control1X: -12.2; control1Y: 148.801; control2X: -23; control2Y: 148.801; x: -23; y: 148.801 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -3.48; y: 141.403 }
+ PathCubic { control1X: -3.48; control1Y: 141.403; control2X: -12.062; control2Y: 140.574; x: -3.461; y: 139.755 }
+ PathCubic { control1X: -3.461; control1Y: 139.755; control2X: 5.355; control2Y: 136.331; x: 7.403; y: 133.668 }
+ PathCubic { control1X: 7.403; control1Y: 133.668; control2X: 14.367; control2Y: 128.957; x: 15.8; y: 128.753 }
+ PathCubic { control1X: 17.234; control1Y: 128.548; control2X: 31.194; control2Y: 124.861; x: 31.399; y: 123.633 }
+ PathCubic { control1X: 31.604; control1Y: 122.404; control2X: 65.67; control2Y: 109.823; x: 70.09; y: 113.013 }
+ PathCubic { control1X: 73.001; control1Y: 115.114; control2X: 63.1; control2Y: 113.437; x: 53.466; y: 117.847 }
+ PathCubic { control1X: 52.111; control1Y: 118.467; control2X: 18.258; control2Y: 133.054; x: 14.981; y: 133.668 }
+ PathCubic { control1X: 11.704; control1Y: 134.283; control2X: 5.765; control2Y: 138.174; x: 3.307; y: 138.788 }
+ PathCubic { control1X: 0.85; control1Y: 139.403; control2X: -3.48; control2Y: 141.403; x: -3.48; y: 141.403 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -11.4; y: 143.601 }
+ PathCubic { control1X: -11.4; control1Y: 143.601; control2X: -6.2; control2Y: 143.201; x: -7.4; y: 144.801 }
+ PathCubic { control1X: -8.6; control1Y: 146.401; control2X: -11; control2Y: 145.601; x: -11; y: 145.601 }
+ PathLine { x: -11.4; y: 143.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -18.6; y: 145.201 }
+ PathCubic { control1X: -18.6; control1Y: 145.201; control2X: -13.4; control2Y: 144.801; x: -14.6; y: 146.401 }
+ PathCubic { control1X: -15.8; control1Y: 148.001; control2X: -18.2; control2Y: 147.201; x: -18.2; y: 147.201 }
+ PathLine { x: -18.6; y: 145.201 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -29; y: 146.801 }
+ PathCubic { control1X: -29; control1Y: 146.801; control2X: -23.8; control2Y: 146.401; x: -25; y: 148.001 }
+ PathCubic { control1X: -26.2; control1Y: 149.601; control2X: -28.6; control2Y: 148.801; x: -28.6; y: 148.801 }
+ PathLine { x: -29; y: 146.801 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -36.6; y: 147.601 }
+ PathCubic { control1X: -36.6; control1Y: 147.601; control2X: -31.4; control2Y: 147.201; x: -32.6; y: 148.801 }
+ PathCubic { control1X: -33.8; control1Y: 150.401; control2X: -36.2; control2Y: 149.601; x: -36.2; y: 149.601 }
+ PathLine { x: -36.6; y: 147.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 1.8; y: 108.001 }
+ PathCubic { control1X: 1.8; control1Y: 108.001; control2X: 6.2; control2Y: 108.001; x: 5; y: 109.601 }
+ PathCubic { control1X: 3.8; control1Y: 111.201; control2X: 0.6; control2Y: 110.801; x: 0.6; y: 110.801 }
+ PathLine { x: 1.8; y: 108.001 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -8.2; y: 113.601 }
+ PathCubic { control1X: -8.2; control1Y: 113.601; control2X: -1.694; control2Y: 111.46; x: -4.2; y: 114.801 }
+ PathCubic { control1X: -5.4; control1Y: 116.401; control2X: -7.8; control2Y: 115.601; x: -7.8; y: 115.601 }
+ PathLine { x: -8.2; y: 113.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -19.4; y: 118.401 }
+ PathCubic { control1X: -19.4; control1Y: 118.401; control2X: -14.2; control2Y: 118.001; x: -15.4; y: 119.601 }
+ PathCubic { control1X: -16.6; control1Y: 121.201; control2X: -19; control2Y: 120.401; x: -19; y: 120.401 }
+ PathLine { x: -19.4; y: 118.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -27; y: 124.401 }
+ PathCubic { control1X: -27; control1Y: 124.401; control2X: -21.8; control2Y: 124.001; x: -23; y: 125.601 }
+ PathCubic { control1X: -24.2; control1Y: 127.201; control2X: -26.6; control2Y: 126.401; x: -26.6; y: 126.401 }
+ PathLine { x: -27; y: 124.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -33.8; y: 129.201 }
+ PathCubic { control1X: -33.8; control1Y: 129.201; control2X: -28.6; control2Y: 128.801; x: -29.8; y: 130.401 }
+ PathCubic { control1X: -31; control1Y: 132.001; control2X: -33.4; control2Y: 131.201; x: -33.4; y: 131.201 }
+ PathLine { x: -33.8; y: 129.201 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 5.282; y: 135.598 }
+ PathCubic { control1X: 5.282; control1Y: 135.598; control2X: 12.203; control2Y: 135.066; x: 10.606; y: 137.195 }
+ PathCubic { control1X: 9.009; control1Y: 139.325; control2X: 5.814; control2Y: 138.26; x: 5.814; y: 138.26 }
+ PathLine { x: 5.282; y: 135.598 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 15.682; y: 130.798 }
+ PathCubic { control1X: 15.682; control1Y: 130.798; control2X: 22.603; control2Y: 130.266; x: 21.006; y: 132.395 }
+ PathCubic { control1X: 19.409; control1Y: 134.525; control2X: 16.214; control2Y: 133.46; x: 16.214; y: 133.46 }
+ PathLine { x: 15.682; y: 130.798 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 26.482; y: 126.398 }
+ PathCubic { control1X: 26.482; control1Y: 126.398; control2X: 33.403; control2Y: 125.866; x: 31.806; y: 127.995 }
+ PathCubic { control1X: 30.209; control1Y: 130.125; control2X: 27.014; control2Y: 129.06; x: 27.014; y: 129.06 }
+ PathLine { x: 26.482; y: 126.398 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 36.882; y: 121.598 }
+ PathCubic { control1X: 36.882; control1Y: 121.598; control2X: 43.803; control2Y: 121.066; x: 42.206; y: 123.195 }
+ PathCubic { control1X: 40.609; control1Y: 125.325; control2X: 37.414; control2Y: 124.26; x: 37.414; y: 124.26 }
+ PathLine { x: 36.882; y: 121.598 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 9.282; y: 103.598 }
+ PathCubic { control1X: 9.282; control1Y: 103.598; control2X: 16.203; control2Y: 103.066; x: 14.606; y: 105.195 }
+ PathCubic { control1X: 13.009; control1Y: 107.325; control2X: 9.014; control2Y: 107.06; x: 9.014; y: 107.06 }
+ PathLine { x: 9.282; y: 103.598 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 19.282; y: 100.398 }
+ PathCubic { control1X: 19.282; control1Y: 100.398; control2X: 26.203; control2Y: 99.866; x: 24.606; y: 101.995 }
+ PathCubic { control1X: 23.009; control1Y: 104.125; control2X: 18.614; control2Y: 103.86; x: 18.614; y: 103.86 }
+ PathLine { x: 19.282; y: 100.398 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -3.4; y: 140.401 }
+ PathCubic { control1X: -3.4; control1Y: 140.401; control2X: 1.8; control2Y: 140.001; x: 0.6; y: 141.601 }
+ PathCubic { control1X: -0.6; control1Y: 143.201; control2X: -3; control2Y: 142.401; x: -3; y: 142.401 }
+ PathLine { x: -3.4; y: 140.401 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: -76.6; y: 41.2 }
+ PathCubic { control1X: -76.6; control1Y: 41.2; control2X: -81; control2Y: 50; x: -81.4; y: 53.2 }
+ PathCubic { control1X: -81.4; control1Y: 53.2; control2X: -80.6; control2Y: 44.4; x: -79.4; y: 42.4 }
+ PathCubic { control1X: -78.2; control1Y: 40.4; control2X: -76.6; control2Y: 41.2; x: -76.6; y: 41.2 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: -95; y: 55.2 }
+ PathCubic { control1X: -95; control1Y: 55.2; control2X: -98.2; control2Y: 69.6; x: -97.8; y: 72.4 }
+ PathCubic { control1X: -97.8; control1Y: 72.4; control2X: -99; control2Y: 60.8; x: -98.6; y: 59.6 }
+ PathCubic { control1X: -98.2; control1Y: 58.4; control2X: -95; control2Y: 55.2; x: -95; y: 55.2 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -74.2; y: -19.4 }
+ PathLine { x: -74.4; y: -16.2 }
+ PathLine { x: -76.6; y: -16 }
+ PathCubic { control1X: -76.6; control1Y: -16; control2X: -62.4; control2Y: -3.4; x: -61.8; y: 4.2 }
+ PathCubic { control1X: -61.8; control1Y: 4.2; control2X: -61; control2Y: -4; x: -74.2; y: -19.4 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -70.216; y: -18.135 }
+ PathCubic { control1X: -70.647; control1Y: -18.551; control2X: -70.428; control2Y: -19.296; x: -70.836; y: -19.556 }
+ PathCubic { control1X: -71.645; control1Y: -20.072; control2X: -69.538; control2Y: -20.129; x: -69.766; y: -20.845 }
+ PathCubic { control1X: -70.149; control1Y: -22.051; control2X: -69.962; control2Y: -22.072; x: -70.084; y: -23.348 }
+ PathCubic { control1X: -70.141; control1Y: -23.946; control2X: -69.553; control2Y: -25.486; x: -69.168; y: -25.926 }
+ PathCubic { control1X: -67.722; control1Y: -27.578; control2X: -69.046; control2Y: -30.51; x: -67.406; y: -32.061 }
+ PathCubic { control1X: -67.102; control1Y: -32.35; control2X: -66.726; control2Y: -32.902; x: -66.441; y: -33.32 }
+ PathCubic { control1X: -65.782; control1Y: -34.283; control2X: -64.598; control2Y: -34.771; x: -63.648; y: -35.599 }
+ PathCubic { control1X: -63.33; control1Y: -35.875; control2X: -63.531; control2Y: -36.702; x: -62.962; y: -36.61 }
+ PathCubic { control1X: -62.248; control1Y: -36.495; control2X: -61.007; control2Y: -36.625; x: -61.052; y: -35.784 }
+ PathCubic { control1X: -61.165; control1Y: -33.664; control2X: -62.494; control2Y: -31.944; x: -63.774; y: -30.276 }
+ PathCubic { control1X: -63.323; control1Y: -29.572; control2X: -63.781; control2Y: -28.937; x: -64.065; y: -28.38 }
+ PathCubic { control1X: -65.4; control1Y: -25.76; control2X: -65.211; control2Y: -22.919; x: -65.385; y: -20.079 }
+ PathCubic { control1X: -65.39; control1Y: -19.994; control2X: -65.697; control2Y: -19.916; x: -65.689; y: -19.863 }
+ PathCubic { control1X: -65.336; control1Y: -17.528; control2X: -64.752; control2Y: -15.329; x: -63.873; y: -13.1 }
+ PathCubic { control1X: -63.507; control1Y: -12.17; control2X: -63.036; control2Y: -11.275; x: -62.886; y: -10.348 }
+ PathCubic { control1X: -62.775; control1Y: -9.662; control2X: -62.672; control2Y: -8.829; x: -63.08; y: -8.124 }
+ PathCubic { control1X: -61.045; control1Y: -5.234; control2X: -62.354; control2Y: -2.583; x: -61.185; y: 0.948 }
+ PathCubic { control1X: -60.978; control1Y: 1.573; control2X: -59.286; control2Y: 3.487; x: -59.749; y: 3.326 }
+ PathCubic { control1X: -62.262; control1Y: 2.455; control2X: -62.374; control2Y: 2.057; x: -62.551; y: 1.304 }
+ PathCubic { control1X: -62.697; control1Y: 0.681; control2X: -63.027; control2Y: -0.696; x: -63.264; y: -1.298 }
+ PathCubic { control1X: -63.328; control1Y: -1.462; control2X: -63.499; control2Y: -3.346; x: -63.577; y: -3.468 }
+ PathCubic { control1X: -65.09; control1Y: -5.85; control2X: -63.732; control2Y: -5.674; x: -65.102; y: -8.032 }
+ PathCubic { control1X: -66.53; control1Y: -8.712; control2X: -67.496; control2Y: -9.816; x: -68.619; y: -10.978 }
+ PathCubic { control1X: -68.817; control1Y: -11.182; control2X: -67.674; control2Y: -11.906; x: -67.855; y: -12.119 }
+ PathCubic { control1X: -68.947; control1Y: -13.408; control2X: -70.1; control2Y: -14.175; x: -69.764; y: -15.668 }
+ PathCubic { control1X: -69.609; control1Y: -16.358; control2X: -69.472; control2Y: -17.415; x: -70.216; y: -18.135 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -73.8; y: -16.4 }
+ PathCubic { control1X: -73.8; control1Y: -16.4; control2X: -73.4; control2Y: -9.6; x: -71; y: -8 }
+ PathCubic { control1X: -68.6; control1Y: -6.4; control2X: -69.8; control2Y: -7.2; x: -73; y: -8.4 }
+ PathCubic { control1X: -76.2; control1Y: -9.6; control2X: -75; control2Y: -10.4; x: -75; y: -10.4 }
+ PathCubic { control1X: -75; control1Y: -10.4; control2X: -77.8; control2Y: -10; x: -75.4; y: -8 }
+ PathCubic { control1X: -73; control1Y: -6; control2X: -69.4; control2Y: -3.6; x: -71; y: -3.6 }
+ PathCubic { control1X: -72.6; control1Y: -3.6; control2X: -80.2; control2Y: -7.6; x: -80.2; y: -10.4 }
+ PathCubic { control1X: -80.2; control1Y: -13.2; control2X: -81.2; control2Y: -17.3; x: -81.2; y: -17.3 }
+ PathCubic { control1X: -81.2; control1Y: -17.3; control2X: -80.1; control2Y: -18.1; x: -75.3; y: -18 }
+ PathCubic { control1X: -75.3; control1Y: -18; control2X: -73.9; control2Y: -17.3; x: -73.8; y: -16.4 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -74.6; y: 2.2 }
+ PathCubic { control1X: -74.6; control1Y: 2.2; control2X: -83.12; control2Y: -0.591; x: -101.6; y: 2.8 }
+ PathCubic { control1X: -101.6; control1Y: 2.8; control2X: -92.569; control2Y: 0.722; x: -73.8; y: 3 }
+ PathCubic { control1X: -63.5; control1Y: 4.25; control2X: -74.6; control2Y: 2.2; x: -74.6; y: 2.2 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -72.502; y: 2.129 }
+ PathCubic { control1X: -72.502; control1Y: 2.129; control2X: -80.748; control2Y: -1.389; x: -99.453; y: 0.392 }
+ PathCubic { control1X: -99.453; control1Y: 0.392; control2X: -90.275; control2Y: -0.897; x: -71.774; y: 2.995 }
+ PathCubic { control1X: -61.62; control1Y: 5.131; control2X: -72.502; control2Y: 2.129; x: -72.502; y: 2.129 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -70.714; y: 2.222 }
+ PathCubic { control1X: -70.714; control1Y: 2.222; control2X: -78.676; control2Y: -1.899; x: -97.461; y: -1.514 }
+ PathCubic { control1X: -97.461; control1Y: -1.514; control2X: -88.213; control2Y: -2.118; x: -70.052; y: 3.14 }
+ PathCubic { control1X: -60.086; control1Y: 6.025; control2X: -70.714; control2Y: 2.222; x: -70.714; y: 2.222 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -69.444; y: 2.445 }
+ PathCubic { control1X: -69.444; control1Y: 2.445; control2X: -76.268; control2Y: -1.862; x: -93.142; y: -2.96 }
+ PathCubic { control1X: -93.142; control1Y: -2.96; control2X: -84.803; control2Y: -2.79; x: -68.922; y: 3.319 }
+ PathCubic { control1X: -60.206; control1Y: 6.672; control2X: -69.444; control2Y: 2.445; x: -69.444; y: 2.445 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 45.84; y: 12.961 }
+ PathCubic { control1X: 45.84; control1Y: 12.961; control2X: 44.91; control2Y: 13.605; x: 45.124; y: 12.424 }
+ PathCubic { control1X: 45.339; control1Y: 11.243; control2X: 73.547; control2Y: -1.927; x: 77.161; y: -1.677 }
+ PathCubic { control1X: 77.161; control1Y: -1.677; control2X: 46.913; control2Y: 11.529; x: 45.84; y: 12.961 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 42.446; y: 13.6 }
+ PathCubic { control1X: 42.446; control1Y: 13.6; control2X: 41.57; control2Y: 14.315; x: 41.691; y: 13.121 }
+ PathCubic { control1X: 41.812; control1Y: 11.927; control2X: 68.899; control2Y: -3.418; x: 72.521; y: -3.452 }
+ PathCubic { control1X: 72.521; control1Y: -3.452; control2X: 43.404; control2Y: 12.089; x: 42.446; y: 13.6 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 39.16; y: 14.975 }
+ PathCubic { control1X: 39.16; control1Y: 14.975; control2X: 38.332; control2Y: 15.747; x: 38.374; y: 14.547 }
+ PathCubic { control1X: 38.416; control1Y: 13.348; control2X: 58.233; control2Y: -2.149; x: 68.045; y: -4.023 }
+ PathCubic { control1X: 68.045; control1Y: -4.023; control2X: 50.015; control2Y: 4.104; x: 39.16; y: 14.975 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 36.284; y: 16.838 }
+ PathCubic { control1X: 36.284; control1Y: 16.838; control2X: 35.539; control2Y: 17.532; x: 35.577; y: 16.453 }
+ PathCubic { control1X: 35.615; control1Y: 15.373; control2X: 53.449; control2Y: 1.426; x: 62.28; y: -0.26 }
+ PathCubic { control1X: 62.28; control1Y: -0.26; control2X: 46.054; control2Y: 7.054; x: 36.284; y: 16.838 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 4.6; y: 164.801 }
+ PathCubic { control1X: 4.6; control1Y: 164.801; control2X: -10.6; control2Y: 162.401; x: 6.2; y: 160.801 }
+ PathCubic { control1X: 6.2; control1Y: 160.801; control2X: 24.2; control2Y: 158.801; x: 28.2; y: 153.601 }
+ PathCubic { control1X: 28.2; control1Y: 153.601; control2X: 41.8; control2Y: 144.401; x: 44.6; y: 144.001 }
+ PathCubic { control1X: 47.4; control1Y: 143.601; control2X: 63.8; control2Y: 140.001; x: 64.2; y: 137.601 }
+ PathCubic { control1X: 64.6; control1Y: 135.201; control2X: 70.6; control2Y: 132.801; x: 72.2; y: 133.601 }
+ PathCubic { control1X: 73.8; control1Y: 134.401; control2X: 73.8; control2Y: 143.601; x: 71; y: 144.401 }
+ PathCubic { control1X: 68.2; control1Y: 145.201; control2X: 49.4; control2Y: 152.401; x: 43; y: 153.601 }
+ PathCubic { control1X: 36.6; control1Y: 154.801; control2X: 25; control2Y: 162.401; x: 20.2; y: 163.601 }
+ PathCubic { control1X: 15.4; control1Y: 164.801; control2X: 4.6; control2Y: 164.801; x: 4.6; y: 164.801 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 77.6; y: 127.401 }
+ PathCubic { control1X: 77.6; control1Y: 127.401; control2X: 74.6; control2Y: 129.001; x: 73.4; y: 131.601 }
+ PathCubic { control1X: 73.4; control1Y: 131.601; control2X: 67; control2Y: 142.201; x: 52.8; y: 145.401 }
+ PathCubic { control1X: 52.8; control1Y: 145.401; control2X: 29.8; control2Y: 154.401; x: 22; y: 156.401 }
+ PathCubic { control1X: 22; control1Y: 156.401; control2X: 8.6; control2Y: 161.401; x: 1.2; y: 160.601 }
+ PathCubic { control1X: 1.2; control1Y: 160.601; control2X: -5.8; control2Y: 160.801; x: 0.4; y: 162.401 }
+ PathCubic { control1X: 0.4; control1Y: 162.401; control2X: 20.6; control2Y: 160.401; x: 24; y: 158.601 }
+ PathCubic { control1X: 24; control1Y: 158.601; control2X: 39.6; control2Y: 153.401; x: 42.6; y: 150.801 }
+ PathCubic { control1X: 45.6; control1Y: 148.201; control2X: 63.8; control2Y: 143.201; x: 66; y: 141.201 }
+ PathCubic { control1X: 68.2; control1Y: 139.201; control2X: 78; control2Y: 130.801; x: 77.6; y: 127.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 18.882; y: 158.911 }
+ PathCubic { control1X: 18.882; control1Y: 158.911; control2X: 24.111; control2Y: 158.685; x: 22.958; y: 160.234 }
+ PathCubic { control1X: 21.805; control1Y: 161.784; control2X: 19.357; control2Y: 160.91; x: 19.357; y: 160.91 }
+ PathLine { x: 18.882; y: 158.911 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 11.68; y: 160.263 }
+ PathCubic { control1X: 11.68; control1Y: 160.263; control2X: 16.908; control2Y: 160.037; x: 15.756; y: 161.586 }
+ PathCubic { control1X: 14.603; control1Y: 163.136; control2X: 12.155; control2Y: 162.263; x: 12.155; y: 162.263 }
+ PathLine { x: 11.68; y: 160.263 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 1.251; y: 161.511 }
+ PathCubic { control1X: 1.251; control1Y: 161.511; control2X: 6.48; control2Y: 161.284; x: 5.327; y: 162.834 }
+ PathCubic { control1X: 4.174; control1Y: 164.383; control2X: 1.726; control2Y: 163.51; x: 1.726; y: 163.51 }
+ PathLine { x: 1.251; y: 161.511 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -6.383; y: 162.055 }
+ PathCubic { control1X: -6.383; control1Y: 162.055; control2X: -1.154; control2Y: 161.829; x: -2.307; y: 163.378 }
+ PathCubic { control1X: -3.46; control1Y: 164.928; control2X: -5.908; control2Y: 164.054; x: -5.908; y: 164.054 }
+ PathLine { x: -6.383; y: 162.055 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 35.415; y: 151.513 }
+ PathCubic { control1X: 35.415; control1Y: 151.513; control2X: 42.375; control2Y: 151.212; x: 40.84; y: 153.274 }
+ PathCubic { control1X: 39.306; control1Y: 155.336; control2X: 36.047; control2Y: 154.174; x: 36.047; y: 154.174 }
+ PathLine { x: 35.415; y: 151.513 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 45.73; y: 147.088 }
+ PathCubic { control1X: 45.73; control1Y: 147.088; control2X: 51.689; control2Y: 143.787; x: 51.155; y: 148.849 }
+ PathCubic { control1X: 50.885; control1Y: 151.405; control2X: 46.362; control2Y: 149.749; x: 46.362; y: 149.749 }
+ PathLine { x: 45.73; y: 147.088 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 54.862; y: 144.274 }
+ PathCubic { control1X: 54.862; control1Y: 144.274; control2X: 62.021; control2Y: 140.573; x: 60.287; y: 146.035 }
+ PathCubic { control1X: 59.509; control1Y: 148.485; control2X: 55.493; control2Y: 146.935; x: 55.493; y: 146.935 }
+ PathLine { x: 54.862; y: 144.274 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 64.376; y: 139.449 }
+ PathCubic { control1X: 64.376; control1Y: 139.449; control2X: 68.735; control2Y: 134.548; x: 69.801; y: 141.21 }
+ PathCubic { control1X: 70.207; control1Y: 143.748; control2X: 65.008; control2Y: 142.11; x: 65.008; y: 142.11 }
+ PathLine { x: 64.376; y: 139.449 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 26.834; y: 155.997 }
+ PathCubic { control1X: 26.834; control1Y: 155.997; control2X: 32.062; control2Y: 155.77; x: 30.91; y: 157.32 }
+ PathCubic { control1X: 29.757; control1Y: 158.869; control2X: 27.308; control2Y: 157.996; x: 27.308; y: 157.996 }
+ PathLine { x: 26.834; y: 155.997 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 62.434; y: 34.603 }
+ PathCubic { control1X: 62.434; control1Y: 34.603; control2X: 61.708; control2Y: 35.268; x: 61.707; y: 34.197 }
+ PathCubic { control1X: 61.707; control1Y: 33.127; control2X: 79.191; control2Y: 19.863; x: 88.034; y: 18.479 }
+ PathCubic { control1X: 88.034; control1Y: 18.479; control2X: 71.935; control2Y: 25.208; x: 62.434; y: 34.603 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 65.4; y: 98.4 }
+ PathCubic { control1X: 65.4; control1Y: 98.4; control2X: 87.401; control2Y: 120.801; x: 96.601; y: 124.401 }
+ PathCubic { control1X: 96.601; control1Y: 124.401; control2X: 105.801; control2Y: 135.601; x: 101.801; y: 161.601 }
+ PathCubic { control1X: 101.801; control1Y: 161.601; control2X: 98.601; control2Y: 169.201; x: 95.401; y: 148.401 }
+ PathCubic { control1X: 95.401; control1Y: 148.401; control2X: 98.601; control2Y: 123.201; x: 87.401; y: 139.201 }
+ PathCubic { control1X: 87.401; control1Y: 139.201; control2X: 79; control2Y: 129.301; x: 85.4; y: 129.601 }
+ PathCubic { control1X: 85.4; control1Y: 129.601; control2X: 88.601; control2Y: 131.601; x: 89.001; y: 130.001 }
+ PathCubic { control1X: 89.401; control1Y: 128.401; control2X: 81.4; control2Y: 114.801; x: 64.2; y: 100.4 }
+ PathCubic { control1X: 47; control1Y: 86; control2X: 65.4; control2Y: 98.4; x: 65.4; y: 98.4 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 7; y: 137.201 }
+ PathCubic { control1X: 7; control1Y: 137.201; control2X: 6.8; control2Y: 135.401; x: 8.6; y: 136.201 }
+ PathCubic { control1X: 10.4; control1Y: 137.001; control2X: 104.601; control2Y: 143.201; x: 136.201; y: 167.201 }
+ PathCubic { control1X: 136.201; control1Y: 167.201; control2X: 91.001; control2Y: 144.001; x: 7; y: 137.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 17.4; y: 132.801 }
+ PathCubic { control1X: 17.4; control1Y: 132.801; control2X: 17.2; control2Y: 131.001; x: 19; y: 131.801 }
+ PathCubic { control1X: 20.8; control1Y: 132.601; control2X: 157.401; control2Y: 131.601; x: 181.001; y: 164.001 }
+ PathCubic { control1X: 181.001; control1Y: 164.001; control2X: 159.001; control2Y: 138.801; x: 17.4; y: 132.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 29; y: 128.801 }
+ PathCubic { control1X: 29; control1Y: 128.801; control2X: 28.8; control2Y: 127.001; x: 30.6; y: 127.801 }
+ PathCubic { control1X: 32.4; control1Y: 128.601; control2X: 205.801; control2Y: 115.601; x: 229.401; y: 148.001 }
+ PathCubic { control1X: 229.401; control1Y: 148.001; control2X: 219.801; control2Y: 122.401; x: 29; y: 128.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 39; y: 124.001 }
+ PathCubic { control1X: 39; control1Y: 124.001; control2X: 38.8; control2Y: 122.201; x: 40.6; y: 123.001 }
+ PathCubic { control1X: 42.4; control1Y: 123.801; control2X: 164.601; control2Y: 85.2; x: 188.201; y: 117.601 }
+ PathCubic { control1X: 188.201; control1Y: 117.601; control2X: 174.801; control2Y: 93; x: 39; y: 124.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -19; y: 146.801 }
+ PathCubic { control1X: -19; control1Y: 146.801; control2X: -19.2; control2Y: 145.001; x: -17.4; y: 145.801 }
+ PathCubic { control1X: -15.6; control1Y: 146.601; control2X: 2.2; control2Y: 148.801; x: 4.2; y: 187.601 }
+ PathCubic { control1X: 4.2; control1Y: 187.601; control2X: -3; control2Y: 145.601; x: -19; y: 146.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -27.8; y: 148.401 }
+ PathCubic { control1X: -27.8; control1Y: 148.401; control2X: -28; control2Y: 146.601; x: -26.2; y: 147.401 }
+ PathCubic { control1X: -24.4; control1Y: 148.201; control2X: -10.2; control2Y: 143.601; x: -13; y: 182.401 }
+ PathCubic { control1X: -13; control1Y: 182.401; control2X: -11.8; control2Y: 147.201; x: -27.8; y: 148.401 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -35.8; y: 148.801 }
+ PathCubic { control1X: -35.8; control1Y: 148.801; control2X: -36; control2Y: 147.001; x: -34.2; y: 147.801 }
+ PathCubic { control1X: -32.4; control1Y: 148.601; control2X: -17; control2Y: 149.201; x: -29.4; y: 171.601 }
+ PathCubic { control1X: -29.4; control1Y: 171.601; control2X: -19.8; control2Y: 147.601; x: -35.8; y: 148.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 11.526; y: 104.465 }
+ PathCubic { control1X: 11.526; control1Y: 104.465; control2X: 11.082; control2Y: 106.464; x: 12.631; y: 105.247 }
+ PathCubic { control1X: 28.699; control1Y: 92.622; control2X: 61.141; control2Y: 33.72; x: 116.826; y: 28.086 }
+ PathCubic { control1X: 116.826; control1Y: 28.086; control2X: 78.518; control2Y: 15.976; x: 11.526; y: 104.465 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 22.726; y: 102.665 }
+ PathCubic { control1X: 22.726; control1Y: 102.665; control2X: 21.363; control2Y: 101.472; x: 23.231; y: 100.847 }
+ PathCubic { control1X: 25.099; control1Y: 100.222; control2X: 137.541; control2Y: 27.72; x: 176.826; y: 35.686 }
+ PathCubic { control1X: 176.826; control1Y: 35.686; control2X: 149.719; control2Y: 28.176; x: 22.726; y: 102.665 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 1.885; y: 108.767 }
+ PathCubic { control1X: 1.885; control1Y: 108.767; control2X: 1.376; control2Y: 110.366; x: 3.087; y: 109.39 }
+ PathCubic { control1X: 12.062; control1Y: 104.27; control2X: 15.677; control2Y: 47.059; x: 59.254; y: 45.804 }
+ PathCubic { control1X: 59.254; control1Y: 45.804; control2X: 26.843; control2Y: 31.09; x: 1.885; y: 108.767 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -18.038; y: 119.793 }
+ PathCubic { control1X: -18.038; control1Y: 119.793; control2X: -19.115; control2Y: 121.079; x: -17.162; y: 120.825 }
+ PathCubic { control1X: -6.916; control1Y: 119.493; control2X: 14.489; control2Y: 78.222; x: 58.928; y: 83.301 }
+ PathCubic { control1X: 58.928; control1Y: 83.301; control2X: 26.962; control2Y: 68.955; x: -18.038; y: 119.793 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -6.8; y: 113.667 }
+ PathCubic { control1X: -6.8; control1Y: 113.667; control2X: -7.611; control2Y: 115.136; x: -5.742; y: 114.511 }
+ PathCubic { control1X: 4.057; control1Y: 111.237; control2X: 17.141; control2Y: 66.625; x: 61.729; y: 63.078 }
+ PathCubic { control1X: 61.729; control1Y: 63.078; control2X: 27.603; control2Y: 55.135; x: -6.8; y: 113.667 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -25.078; y: 124.912 }
+ PathCubic { control1X: -25.078; control1Y: 124.912; control2X: -25.951; control2Y: 125.954; x: -24.369; y: 125.748 }
+ PathCubic { control1X: -16.07; control1Y: 124.669; control2X: 1.268; control2Y: 91.24; x: 37.264; y: 95.354 }
+ PathCubic { control1X: 37.264; control1Y: 95.354; control2X: 11.371; control2Y: 83.734; x: -25.078; y: 124.912 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -32.677; y: 130.821 }
+ PathCubic { control1X: -32.677; control1Y: 130.821; control2X: -33.682; control2Y: 131.866; x: -32.091; y: 131.748 }
+ PathCubic { control1X: -27.923; control1Y: 131.439; control2X: 2.715; control2Y: 98.36; x: 21.183; y: 113.862 }
+ PathCubic { control1X: 21.183; control1Y: 113.862; control2X: 9.168; control2Y: 95.139; x: -32.677; y: 130.821 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 36.855; y: 98.898 }
+ PathCubic { control1X: 36.855; control1Y: 98.898; control2X: 35.654; control2Y: 97.543; x: 37.586; y: 97.158 }
+ PathCubic { control1X: 39.518; control1Y: 96.774; control2X: 160.221; control2Y: 39.061; x: 198.184; y: 51.927 }
+ PathCubic { control1X: 198.184; control1Y: 51.927; control2X: 172.243; control2Y: 41.053; x: 36.855; y: 98.898 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 3.4; y: 163.201 }
+ PathCubic { control1X: 3.4; control1Y: 163.201; control2X: 3.2; control2Y: 161.401; x: 5; y: 162.201 }
+ PathCubic { control1X: 6.8; control1Y: 163.001; control2X: 22.2; control2Y: 163.601; x: 9.8; y: 186.001 }
+ PathCubic { control1X: 9.8; control1Y: 186.001; control2X: 19.4; control2Y: 162.001; x: 3.4; y: 163.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 13.8; y: 161.601 }
+ PathCubic { control1X: 13.8; control1Y: 161.601; control2X: 13.6; control2Y: 159.801; x: 15.4; y: 160.601 }
+ PathCubic { control1X: 17.2; control1Y: 161.401; control2X: 35; control2Y: 163.601; x: 37; y: 202.401 }
+ PathCubic { control1X: 37; control1Y: 202.401; control2X: 29.8; control2Y: 160.401; x: 13.8; y: 161.601 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 20.6; y: 160.001 }
+ PathCubic { control1X: 20.6; control1Y: 160.001; control2X: 20.4; control2Y: 158.201; x: 22.2; y: 159.001 }
+ PathCubic { control1X: 24; control1Y: 159.801; control2X: 48.6; control2Y: 163.201; x: 72.2; y: 195.601 }
+ PathCubic { control1X: 72.2; control1Y: 195.601; control2X: 36.6; control2Y: 158.801; x: 20.6; y: 160.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 28.225; y: 157.972 }
+ PathCubic { control1X: 28.225; control1Y: 157.972; control2X: 27.788; control2Y: 156.214; x: 29.678; y: 156.768 }
+ PathCubic { control1X: 31.568; control1Y: 157.322; control2X: 52.002; control2Y: 155.423; x: 90.099; y: 189.599 }
+ PathCubic { control1X: 90.099; control1Y: 189.599; control2X: 43.924; control2Y: 154.656; x: 28.225; y: 157.972 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 38.625; y: 153.572 }
+ PathCubic { control1X: 38.625; control1Y: 153.572; control2X: 38.188; control2Y: 151.814; x: 40.078; y: 152.368 }
+ PathCubic { control1X: 41.968; control1Y: 152.922; control2X: 76.802; control2Y: 157.423; x: 128.499; y: 192.399 }
+ PathCubic { control1X: 128.499; control1Y: 192.399; control2X: 54.324; control2Y: 150.256; x: 38.625; y: 153.572 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -1.8; y: 142.001 }
+ PathCubic { control1X: -1.8; control1Y: 142.001; control2X: -2; control2Y: 140.201; x: -0.2; y: 141.001 }
+ PathCubic { control1X: 1.6; control1Y: 141.801; control2X: 55; control2Y: 144.401; x: 85.4; y: 171.201 }
+ PathCubic { control1X: 85.4; control1Y: 171.201; control2X: 50.499; control2Y: 146.426; x: -1.8; y: 142.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -11.8; y: 146.001 }
+ PathCubic { control1X: -11.8; control1Y: 146.001; control2X: -12; control2Y: 144.201; x: -10.2; y: 145.001 }
+ PathCubic { control1X: -8.4; control1Y: 145.801; control2X: 16.2; control2Y: 149.201; x: 39.8; y: 181.601 }
+ PathCubic { control1X: 39.8; control1Y: 181.601; control2X: 4.2; control2Y: 144.801; x: -11.8; y: 146.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 49.503; y: 148.962 }
+ PathCubic { control1X: 49.503; control1Y: 148.962; control2X: 48.938; control2Y: 147.241; x: 50.864; y: 147.655 }
+ PathCubic { control1X: 52.79; control1Y: 148.068; control2X: 87.86; control2Y: 150.004; x: 141.981; y: 181.098 }
+ PathCubic { control1X: 141.981; control1Y: 181.098; control2X: 64.317; control2Y: 146.704; x: 49.503; y: 148.962 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 57.903; y: 146.562 }
+ PathCubic { control1X: 57.903; control1Y: 146.562; control2X: 57.338; control2Y: 144.841; x: 59.264; y: 145.255 }
+ PathCubic { control1X: 61.19; control1Y: 145.668; control2X: 96.26; control2Y: 147.604; x: 150.381; y: 178.698 }
+ PathCubic { control1X: 150.381; control1Y: 178.698; control2X: 73.317; control2Y: 143.904; x: 57.903; y: 146.562 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 67.503; y: 141.562 }
+ PathCubic { control1X: 67.503; control1Y: 141.562; control2X: 66.938; control2Y: 139.841; x: 68.864; y: 140.255 }
+ PathCubic { control1X: 70.79; control1Y: 140.668; control2X: 113.86; control2Y: 145.004; x: 203.582; y: 179.298 }
+ PathCubic { control1X: 203.582; control1Y: 179.298; control2X: 82.917; control2Y: 138.904; x: 67.503; y: 141.562 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -43.8; y: 148.401 }
+ PathCubic { control1X: -43.8; control1Y: 148.401; control2X: -38.6; control2Y: 148.001; x: -39.8; y: 149.601 }
+ PathCubic { control1X: -41; control1Y: 151.201; control2X: -43.4; control2Y: 150.401; x: -43.4; y: 150.401 }
+ PathLine { x: -43.8; y: 148.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -13; y: 162.401 }
+ PathCubic { control1X: -13; control1Y: 162.401; control2X: -7.8; control2Y: 162.001; x: -9; y: 163.601 }
+ PathCubic { control1X: -10.2; control1Y: 165.201; control2X: -12.6; control2Y: 164.401; x: -12.6; y: 164.401 }
+ PathLine { x: -13; y: 162.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -21.8; y: 162.001 }
+ PathCubic { control1X: -21.8; control1Y: 162.001; control2X: -16.6; control2Y: 161.601; x: -17.8; y: 163.201 }
+ PathCubic { control1X: -19; control1Y: 164.801; control2X: -21.4; control2Y: 164.001; x: -21.4; y: 164.001 }
+ PathLine { x: -21.8; y: 162.001 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -117.169; y: 150.182 }
+ PathCubic { control1X: -117.169; control1Y: 150.182; control2X: -112.124; control2Y: 151.505; x: -113.782; y: 152.624 }
+ PathCubic { control1X: -115.439; control1Y: 153.744; control2X: -117.446; control2Y: 152.202; x: -117.446; y: 152.202 }
+ PathLine { x: -117.169; y: 150.182 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -115.169; y: 140.582 }
+ PathCubic { control1X: -115.169; control1Y: 140.582; control2X: -110.124; control2Y: 141.905; x: -111.782; y: 143.024 }
+ PathCubic { control1X: -113.439; control1Y: 144.144; control2X: -115.446; control2Y: 142.602; x: -115.446; y: 142.602 }
+ PathLine { x: -115.169; y: 140.582 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -122.369; y: 136.182 }
+ PathCubic { control1X: -122.369; control1Y: 136.182; control2X: -117.324; control2Y: 137.505; x: -118.982; y: 138.624 }
+ PathCubic { control1X: -120.639; control1Y: 139.744; control2X: -122.646; control2Y: 138.202; x: -122.646; y: 138.202 }
+ PathLine { x: -122.369; y: 136.182 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -42.6; y: 211.201 }
+ PathCubic { control1X: -42.6; control1Y: 211.201; control2X: -44.2; control2Y: 211.201; x: -48.2; y: 213.201 }
+ PathCubic { control1X: -50.2; control1Y: 213.201; control2X: -61.4; control2Y: 216.801; x: -67; y: 226.801 }
+ PathCubic { control1X: -67; control1Y: 226.801; control2X: -54.6; control2Y: 217.201; x: -42.6; y: 211.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 45.116; y: 303.847 }
+ PathCubic { control1X: 45.257; control1Y: 304.105; control2X: 45.312; control2Y: 304.525; x: 45.604; y: 304.542 }
+ PathCubic { control1X: 46.262; control1Y: 304.582; control2X: 47.495; control2Y: 304.883; x: 47.37; y: 304.247 }
+ PathCubic { control1X: 46.522; control1Y: 299.941; control2X: 45.648; control2Y: 295.004; x: 41.515; y: 293.197 }
+ PathCubic { control1X: 40.876; control1Y: 292.918; control2X: 39.434; control2Y: 293.331; x: 39.36; y: 294.215 }
+ PathCubic { control1X: 39.233; control1Y: 295.739; control2X: 39.116; control2Y: 297.088; x: 39.425; y: 298.554 }
+ PathCubic { control1X: 39.725; control1Y: 299.975; control2X: 41.883; control2Y: 299.985; x: 42.8; y: 298.601 }
+ PathCubic { control1X: 43.736; control1Y: 300.273; control2X: 44.168; control2Y: 302.116; x: 45.116; y: 303.847 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 34.038; y: 308.581 }
+ PathCubic { control1X: 34.786; control1Y: 309.994; control2X: 34.659; control2Y: 311.853; x: 36.074; y: 312.416 }
+ PathCubic { control1X: 36.814; control1Y: 312.71; control2X: 38.664; control2Y: 311.735; x: 38.246; y: 310.661 }
+ PathCubic { control1X: 37.444; control1Y: 308.6; control2X: 37.056; control2Y: 306.361; x: 35.667; y: 304.55 }
+ PathCubic { control1X: 35.467; control1Y: 304.288; control2X: 35.707; control2Y: 303.755; x: 35.547; y: 303.427 }
+ PathCubic { control1X: 34.953; control1Y: 302.207; control2X: 33.808; control2Y: 301.472; x: 32.4; y: 301.801 }
+ PathCubic { control1X: 31.285; control1Y: 304.004; control2X: 32.433; control2Y: 306.133; x: 33.955; y: 307.842 }
+ PathCubic { control1X: 34.091; control1Y: 307.994; control2X: 33.925; control2Y: 308.37; x: 34.038; y: 308.581 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -5.564; y: 303.391 }
+ PathCubic { control1X: -5.672; control1Y: 303.014; control2X: -5.71; control2Y: 302.551; x: -5.545; y: 302.23 }
+ PathCubic { control1X: -5.014; control1Y: 301.197; control2X: -4.221; control2Y: 300.075; x: -4.558; y: 299.053 }
+ PathCubic { control1X: -4.906; control1Y: 297.997; control2X: -6.022; control2Y: 298.179; x: -6.672; y: 298.748 }
+ PathCubic { control1X: -7.807; control1Y: 299.742; control2X: -7.856; control2Y: 301.568; x: -8.547; y: 302.927 }
+ PathCubic { control1X: -8.743; control1Y: 303.313; control2X: -8.692; control2Y: 303.886; x: -9.133; y: 304.277 }
+ PathCubic { control1X: -9.607; control1Y: 304.698; control2X: -10.047; control2Y: 306.222; x: -9.951; y: 306.793 }
+ PathCubic { control1X: -9.898; control1Y: 307.106; control2X: -10.081; control2Y: 317.014; x: -9.859; y: 316.751 }
+ PathCubic { control1X: -9.24; control1Y: 316.018; control2X: -6.19; control2Y: 306.284; x: -6.121; y: 305.392 }
+ PathCubic { control1X: -6.064; control1Y: 304.661; control2X: -5.332; control2Y: 304.196; x: -5.564; y: 303.391 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -31.202; y: 296.599 }
+ PathCubic { control1X: -28.568; control1Y: 294.1; control2X: -25.778; control2Y: 291.139; x: -26.22; y: 287.427 }
+ PathCubic { control1X: -26.336; control1Y: 286.451; control2X: -28.111; control2Y: 286.978; x: -28.298; y: 287.824 }
+ PathCubic { control1X: -29.1; control1Y: 291.449; control2X: -31.139; control2Y: 294.11; x: -33.707; y: 296.502 }
+ PathCubic { control1X: -35.903; control1Y: 298.549; control2X: -37.765; control2Y: 304.893; x: -38; y: 305.401 }
+ PathCubic { control1X: -34.303; control1Y: 300.145; control2X: -32.046; control2Y: 297.399; x: -31.202; y: 296.599 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -44.776; y: 290.635 }
+ PathCubic { control1X: -44.253; control1Y: 290.265; control2X: -44.555; control2Y: 289.774; x: -44.338; y: 289.442 }
+ PathCubic { control1X: -43.385; control1Y: 287.984; control2X: -42.084; control2Y: 286.738; x: -42.066; y: 285 }
+ PathCubic { control1X: -42.063; control1Y: 284.723; control2X: -42.441; control2Y: 284.414; x: -42.776; y: 284.638 }
+ PathCubic { control1X: -43.053; control1Y: 284.822; control2X: -43.395; control2Y: 284.952; x: -43.503; y: 285.082 }
+ PathCubic { control1X: -45.533; control1Y: 287.531; control2X: -46.933; control2Y: 290.202; x: -48.376; y: 293.014 }
+ PathCubic { control1X: -48.559; control1Y: 293.371; control2X: -49.703; control2Y: 297.862; x: -49.39; y: 297.973 }
+ PathCubic { control1X: -49.151; control1Y: 298.058; control2X: -47.431; control2Y: 293.877; x: -47.221; y: 293.763 }
+ PathCubic { control1X: -45.958; control1Y: 293.077; control2X: -45.946; control2Y: 291.462; x: -44.776; y: 290.635 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -28.043; y: 310.179 }
+ PathCubic { control1X: -27.599; control1Y: 309.31; control2X: -26.023; control2Y: 308.108; x: -26.136; y: 307.219 }
+ PathCubic { control1X: -26.254; control1Y: 306.291; control2X: -25.786; control2Y: 304.848; x: -26.698; y: 305.536 }
+ PathCubic { control1X: -27.955; control1Y: 306.484; control2X: -31.404; control2Y: 307.833; x: -31.674; y: 313.641 }
+ PathCubic { control1X: -31.7; control1Y: 314.212; control2X: -28.726; control2Y: 311.519; x: -28.043; y: 310.179 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -13.6; y: 293.001 }
+ PathCubic { control1X: -13.2; control1Y: 292.333; control2X: -12.492; control2Y: 292.806; x: -12.033; y: 292.543 }
+ PathCubic { control1X: -11.385; control1Y: 292.171; control2X: -10.774; control2Y: 291.613; x: -10.482; y: 290.964 }
+ PathCubic { control1X: -9.512; control1Y: 288.815; control2X: -7.743; control2Y: 286.995; x: -7.6; y: 284.601 }
+ PathCubic { control1X: -9.091; control1Y: 283.196; control2X: -9.77; control2Y: 285.236; x: -10.4; y: 286.201 }
+ PathCubic { control1X: -11.723; control1Y: 284.554; control2X: -12.722; control2Y: 286.428; x: -14.022; y: 286.947 }
+ PathCubic { control1X: -14.092; control1Y: 286.975; control2X: -14.305; control2Y: 286.628; x: -14.38; y: 286.655 }
+ PathCubic { control1X: -15.557; control1Y: 287.095; control2X: -16.237; control2Y: 288.176; x: -17.235; y: 288.957 }
+ PathCubic { control1X: -17.406; control1Y: 289.091; control2X: -17.811; control2Y: 288.911; x: -17.958; y: 289.047 }
+ PathCubic { control1X: -18.61; control1Y: 289.65; control2X: -19.583; control2Y: 289.975; x: -19.863; y: 290.657 }
+ PathCubic { control1X: -20.973; control1Y: 293.364; control2X: -24.113; control2Y: 295.459; x: -26; y: 303.001 }
+ PathCubic { control1X: -25.619; control1Y: 303.91; control2X: -21.488; control2Y: 296.359; x: -21.001; y: 295.661 }
+ PathCubic { control1X: -20.165; control1Y: 294.465; control2X: -20.047; control2Y: 297.322; x: -18.771; y: 296.656 }
+ PathCubic { control1X: -18.72; control1Y: 296.629; control2X: -18.534; control2Y: 296.867; x: -18.4; y: 297.001 }
+ PathCubic { control1X: -18.206; control1Y: 296.721; control2X: -17.988; control2Y: 296.492; x: -17.6; y: 296.601 }
+ PathCubic { control1X: -17.6; control1Y: 296.201; control2X: -17.734; control2Y: 295.645; x: -17.533; y: 295.486 }
+ PathCubic { control1X: -16.296; control1Y: 294.509; control2X: -16.38; control2Y: 293.441; x: -15.6; y: 292.201 }
+ PathCubic { control1X: -15.142; control1Y: 292.99; control2X: -14.081; control2Y: 292.271; x: -13.6; y: 293.001 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 46.2; y: 347.401 }
+ PathCubic { control1X: 46.2; control1Y: 347.401; control2X: 53.6; control2Y: 327.001; x: 49.2; y: 315.801 }
+ PathCubic { control1X: 49.2; control1Y: 315.801; control2X: 60.6; control2Y: 337.401; x: 56; y: 348.601 }
+ PathCubic { control1X: 56; control1Y: 348.601; control2X: 55.6; control2Y: 338.201; x: 51.6; y: 333.201 }
+ PathCubic { control1X: 51.6; control1Y: 333.201; control2X: 47.6; control2Y: 346.001; x: 46.2; y: 347.401 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 31.4; y: 344.801 }
+ PathCubic { control1X: 31.4; control1Y: 344.801; control2X: 36.8; control2Y: 336.001; x: 28.8; y: 317.601 }
+ PathCubic { control1X: 28.8; control1Y: 317.601; control2X: 28; control2Y: 338.001; x: 21.2; y: 349.001 }
+ PathCubic { control1X: 21.2; control1Y: 349.001; control2X: 35.4; control2Y: 328.801; x: 31.4; y: 344.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 21.4; y: 342.801 }
+ PathCubic { control1X: 21.4; control1Y: 342.801; control2X: 21.2; control2Y: 322.801; x: 21.6; y: 319.801 }
+ PathCubic { control1X: 21.6; control1Y: 319.801; control2X: 17.8; control2Y: 336.401; x: 7.6; y: 346.001 }
+ PathCubic { control1X: 7.6; control1Y: 346.001; control2X: 22; control2Y: 334.001; x: 21.4; y: 342.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 11.8; y: 310.801 }
+ PathCubic { control1X: 11.8; control1Y: 310.801; control2X: 17.8; control2Y: 324.401; x: 7.8; y: 342.801 }
+ PathCubic { control1X: 7.8; control1Y: 342.801; control2X: 14.2; control2Y: 330.601; x: 9.4; y: 323.601 }
+ PathCubic { control1X: 9.4; control1Y: 323.601; control2X: 12; control2Y: 320.201; x: 11.8; y: 310.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -7.4; y: 342.401 }
+ PathCubic { control1X: -7.4; control1Y: 342.401; control2X: -8.4; control2Y: 326.801; x: -6.6; y: 324.601 }
+ PathCubic { control1X: -6.6; control1Y: 324.601; control2X: -6.4; control2Y: 318.201; x: -6.8; y: 317.201 }
+ PathCubic { control1X: -6.8; control1Y: 317.201; control2X: -2.8; control2Y: 311.001; x: -2.6; y: 318.401 }
+ PathCubic { control1X: -2.6; control1Y: 318.401; control2X: -1.2; control2Y: 326.201; x: 1.6; y: 330.801 }
+ PathCubic { control1X: 1.6; control1Y: 330.801; control2X: 5.2; control2Y: 336.201; x: 5; y: 342.601 }
+ PathCubic { control1X: 5; control1Y: 342.601; control2X: -5; control2Y: 312.401; x: -7.4; y: 342.401 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -11; y: 314.801 }
+ PathCubic { control1X: -11; control1Y: 314.801; control2X: -17.6; control2Y: 325.601; x: -19.4; y: 344.601 }
+ PathCubic { control1X: -19.4; control1Y: 344.601; control2X: -20.8; control2Y: 338.401; x: -17; y: 324.001 }
+ PathCubic { control1X: -17; control1Y: 324.001; control2X: -12.8; control2Y: 308.601; x: -11; y: 314.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -32.8; y: 334.601 }
+ PathCubic { control1X: -32.8; control1Y: 334.601; control2X: -27.8; control2Y: 329.201; x: -26.4; y: 324.201 }
+ PathCubic { control1X: -26.4; control1Y: 324.201; control2X: -22.8; control2Y: 308.401; x: -29.2; y: 317.001 }
+ PathCubic { control1X: -29.2; control1Y: 317.001; control2X: -29; control2Y: 325.001; x: -37.2; y: 332.401 }
+ PathCubic { control1X: -37.2; control1Y: 332.401; control2X: -32.4; control2Y: 330.001; x: -32.8; y: 334.601 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -38.6; y: 329.601 }
+ PathCubic { control1X: -38.6; control1Y: 329.601; control2X: -35.2; control2Y: 312.201; x: -34.4; y: 311.401 }
+ PathCubic { control1X: -34.4; control1Y: 311.401; control2X: -32.6; control2Y: 308.001; x: -35.4; y: 311.201 }
+ PathCubic { control1X: -35.4; control1Y: 311.201; control2X: -44.2; control2Y: 330.401; x: -48.2; y: 337.001 }
+ PathCubic { control1X: -48.2; control1Y: 337.001; control2X: -40.2; control2Y: 327.801; x: -38.6; y: 329.601 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -44.4; y: 313.001 }
+ PathCubic { control1X: -44.4; control1Y: 313.001; control2X: -32.8; control2Y: 290.601; x: -54.6; y: 316.401 }
+ PathCubic { control1X: -54.6; control1Y: 316.401; control2X: -43.6; control2Y: 306.601; x: -44.4; y: 313.001 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -59.8; y: 298.401 }
+ PathCubic { control1X: -59.8; control1Y: 298.401; control2X: -55; control2Y: 279.601; x: -52.4; y: 279.801 }
+ PathCubic { control1X: -52.4; control1Y: 279.801; control2X: -44.2; control2Y: 270.801; x: -50.8; y: 281.401 }
+ PathCubic { control1X: -50.8; control1Y: 281.401; control2X: -56.8; control2Y: 291.001; x: -56.2; y: 300.801 }
+ PathCubic { control1X: -56.2; control1Y: 300.801; control2X: -56.8; control2Y: 291.201; x: -59.8; y: 298.401 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 270.5; y: 287 }
+ PathCubic { control1X: 270.5; control1Y: 287; control2X: 258.5; control2Y: 277; x: 256; y: 273.5 }
+ PathCubic { control1X: 256; control1Y: 273.5; control2X: 269.5; control2Y: 292; x: 269.5; y: 299 }
+ PathCubic { control1X: 269.5; control1Y: 299; control2X: 272; control2Y: 291.5; x: 270.5; y: 287 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 276; y: 265 }
+ PathCubic { control1X: 276; control1Y: 265; control2X: 255; control2Y: 250; x: 251.5; y: 242.5 }
+ PathCubic { control1X: 251.5; control1Y: 242.5; control2X: 278; control2Y: 272; x: 278; y: 276.5 }
+ PathCubic { control1X: 278; control1Y: 276.5; control2X: 278.5; control2Y: 267.5; x: 276; y: 265 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 293; y: 111 }
+ PathCubic { control1X: 293; control1Y: 111; control2X: 281; control2Y: 103; x: 279.5; y: 105 }
+ PathCubic { control1X: 279.5; control1Y: 105; control2X: 290; control2Y: 111.5; x: 292.5; y: 120 }
+ PathCubic { control1X: 292.5; control1Y: 120; control2X: 291; control2Y: 111; x: 293; y: 111 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 301.5; y: 191.5 }
+ PathLine { x: 284; y: 179.5 }
+ PathCubic { control1X: 284; control1Y: 179.5; control2X: 303; control2Y: 196.5; x: 303.5; y: 200.5 }
+ PathLine { x: 301.5; y: 191.5 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -89.25; y: 169 }
+ PathLine { x: -67.25; y: 173.75 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -39; y: 331 }
+ PathCubic { control1X: -39; control1Y: 331; control2X: -39.5; control2Y: 327.5; x: -48.5; y: 338 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -33.5; y: 336 }
+ PathCubic { control1X: -33.5; control1Y: 336; control2X: -31.5; control2Y: 329.5; x: -38; y: 334 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: 20.5; y: 344.5 }
+ PathCubic { control1X: 20.5; control1Y: 344.5; control2X: 22; control2Y: 333.5; x: 10.5; y: 346.5 }
+ }
+
+ Shape {
+ x: -184; y: -144; width: parent.width; height: parent.height
+ opacity: tapHandler.pressed ? 1 : 0
+ containsMode: Shape.FillContains
+
+ TapHandler {
+ id: tapHandler
+ enabled: highlightOnTap
+ }
+
+ ShapePath {
+ strokeColor: "red"
+ strokeWidth: 4
+ fillColor: "transparent"
+ PathSvg { path: `m 325.03711,0.5
+ c -26.61408,6.4494547 -49.95197,2.1018066 -76.21132,1.0771669
+ -22.26577,7.6817151 -47.96405,9.3627181 -65.67832,25.8497861
+ -15.74718,12.80008 -41.1564,19.605644 -45.74903,40.600391
+ -12.46933,17.76181 -25.36105,35.720146 -29.20117,57.999996
+ -18.709864,3.10961 -16.347355,30.83801 -22.385143,46.675
+ -6.848711,11.2677 11.07278,24.69174 -8.514666,27.97383
+ -10.266901,5.61543 -12.859313,28.96588 -13.732346,5.78143
+ 0.940083,-11.53398 -13.486195,-38.30626 -16.81701,-34.20231
+ 14.608079,7.8234 21.299281,50.52979 11.380052,48.14418
+ -3.406456,-15.12428 -26.181106,-38.29457 -31.849471,-35.62945
+ 16.851912,6.41472 35.569884,31.75215 28.172486,47.93115
+ -7.906485,-15.42757 -37.758959,-35.53783 -44.275447,-31.28685
+ 18.975831,1.7428 37.986009,20.68109 42.87115,37.14427 C
+ 42.279655,225.774 9.879724,213.57795 4.7080253,219.04989
+ 20.780803,212.57418 55.055919,239.88547 49.602579,241.25683
+ 38.186641,230.40078 6.6930104,222.77983 2.5752529,228.41774 c
+ 13.6045481,-8.33065 49.4437901,14.89041 43.5525671,14.2358
+ -9.759981,-7.96123 -43.5842921,7.36937 -17.554974,-1.20248
+ 9.464499,-3.73452 40.555672,12.80659 16.398749,5.14121
+ -9.1987,-7.28225 -39.0013156,3.37352 -14.121965,-2.12828
+ 13.244874,-0.0206 35.758428,14.62706 10.562447,6.42228
+ -10.780465,-8.4873 -47.8282254,11.10651 -21.027329,-0.003
+ 11.640859,-4.82877 52.615601,10.74471 24.234828,8.2659
+ -10.695834,-7.03902 -42.9384162,8.93905 -34.227854,5.58373
+ 9.077539,-8.56443 49.068801,-5.28097 43.06838,0.45546
+ -10.900893,-0.7118 -27.449619,17.27258 -10.00187,3.46526
+ 15.705191,-9.18198 18.344231,9.31645 1.10807,8.73907
+ -9.908444,1.77856 -21.108189,20.66671 -7.974821,4.92019
+ 15.750746,-14.10374 34.01348,2.07267 9.796961,8.69337
+ -8.17128,5.49929 -12.642664,19.13654 -3.994573,4.19708
+ 9.044753,-8.7077 23.850399,-13.64552 21.404959,4.02329
+ 12.509737,17.12562 51.158782,11.0442 45.106112,43.34009
+ -0.65006,10.05318 -3.79228,13.95389 1.62128,14.30064
+ -4.30913,8.82737 -14.652714,37.9591 2.92144,17.46024
+ 7.37972,-3.68333 -7.62399,16.24161 -7.98007,23.83761
+ -9.336865,18.77418 19.74873,-18.55943 6.62229,5.46195
+ 5.46464,-3.7389 36.23886,-19.41901 14.78167,0.58987
+ -8.59505,4.55644 29.29441,-2.99423 8.95489,6.47134 -9.22562,5.54437
+ -24.09765,26.79976 -11.73274,22.20385 -0.81685,5.4936
+ -1.58629,21.47626 2.34158,9.14886 1.61237,14.67029
+ -2.38384,25.22225 12.26908,15.1741 -4.40761,8.01039
+ -8.23679,36.91214 5.12235,17.92578 1.53454,2.99551 9.37569,3.1726
+ 7.15304,14.93579 3.51234,-11.31873 18.4607,-29.83809
+ 12.36869,-6.48005 -0.22629,16.26174 5.44303,-7.24791
+ 6.56926,10.49819 12.45412,28.9931 3.40908,-41.89883
+ 17.52051,-9.19238 3.23093,11.1924 6.53006,29.46941 7.55984,5.1249
+ 15.37236,-19.52583 4.09776,20.07416 12.64063,1.48215
+ 18.11247,-24.55068 -8.92586,38.39355 6.73828,6.62225
+ 4.55353,-6.91007 15.35028,-38.88977 12.55806,-13.78666
+ 1.05309,27.02664 11.54743,-24.40259 12.40657,6.86306
+ -1.72561,13.28253 11.85393,-24.15909 13.85568,-1.38002
+ 3.12455,8.33539 8.76536,26.46432 8.73882,5.09231 3.57025,-10.37352
+ -16.025,-37.75672 0.20707,-22.5788 -1.2458,-14.17213
+ -2.38918,-16.90145 10.85489,-6.71468 -16.57629,-17.22152
+ 0.19706,-26.08949 5.7751,-19.14889 -14.91681,-16.1674
+ 19.74174,7.19334 2.31875,-9.86869 -4.32508,-15.23278
+ 27.25228,29.12341 20.27514,18.81172 -11.97527,-18.92603
+ -17.96305,-45.80333 11.70099,-51.52566 17.19069,-9.57351
+ 31.17452,21.93154 38.50541,1.56304 16.26048,-4.6633
+ 22.3749,38.26516 24.86349,9.11316 5.94153,-9.9731 30.14313,6.97379
+ 36.34294,4.75012 7.07435,18.27732 8.06778,14.78971 11.04264,3.86016
+ 2.73754,-15.85945 28.7269,10.06391 28.09146,25.96561 3.00672,2.4754
+ 6.55025,-22.10264 11.23552,-14.43872 2.84155,-11.4823
+ -3.28976,-27.88574 4.24895,-25.5189 -0.61494,-11.53957
+ 22.83611,0.11011 10.64648,-15.28756 -6.5587,-21.38598
+ 9.32959,-3.0159 13.5107,-4.69375 -1.38592,-16.74533
+ -8.66673,-31.83316 -1.90087,-41.0875 2.39623,-15.14303
+ -12.50533,-44.45478 -4.70573,-48.49375 15.08472,3.42779
+ -20.39159,-42.17451 -1.69776,-40.85728 24.07272,21.63552
+ -3.65989,-30.10299 2.27233,-33.17152 16.90643,17.53071
+ -12.7383,-38.42821 6.79531,-21.57013 -4.50946,-21.08135
+ -2.53357,-37.43561 -15.5535,-55.59527 -11.0035,-12.40086
+ -1.87775,-7.12745 1.34831,-8.11755 C 468.27562,118.9774
+ 451.40746,102.656 430.98897,92.119168 439.06192,78.203836
+ 455.88012,60.123881 457.38638,40.337815 463.2373,23.183067
+ 450.82861,4.7342783 435.04883,22.626367 409.5188,28.206712
+ 386.3569,24.131269 365.63904,8.0954152 352.788,2.8857182
+ 338.88892,0.40735091 325.03711,0.5 Z m -219.0625,357.04297
+ -0.97656,0.88476 z`
+ }
+ }
+ }
+}
+
diff --git a/examples/quick/quickshapes/shapes/tigerLoader.qml b/examples/quick/quickshapes/shapes/tigerLoader.qml
new file mode 100644
index 0000000000..e49d33a828
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/tigerLoader.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Item {
+ anchors.fill: parent
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("Loading")
+ // Phase #1: Loader loads tiger.qml. After this we have our item.
+ // Phase #2: With some backends (generic) the item will start async processing. Wait for this too.
+ visible: shapeLoader.status != Loader.Ready || (shapeLoader.item as Shape)?.status === Shape.Processing
+ }
+
+ Loader {
+ id: shapeLoader
+ anchors.fill: parent
+ source: "tiger.qml"
+ asynchronous: true
+ visible: status == Loader.Ready
+ scale: 0.4
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/zoomtiger.qml b/examples/quick/quickshapes/shapes/zoomtiger.qml
new file mode 100644
index 0000000000..f23df0ca62
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/zoomtiger.qml
@@ -0,0 +1,152 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Controls
+
+Rectangle {
+ id: root
+ width: 1024
+ height: 768
+
+ readonly property color col: "lightsteelblue"
+ gradient: Gradient {
+ GradientStop {
+ position: 0.0
+ color: Qt.tint(root.col, "#20FFFFFF")
+ }
+ GradientStop {
+ position: 0.1
+ color: Qt.tint(root.col, "#20AAAAAA")
+ }
+ GradientStop {
+ position: 0.9
+ color: Qt.tint(root.col, "#20666666")
+ }
+ GradientStop {
+ position: 1.0
+ color: Qt.tint(root.col, "#20000000")
+ }
+ }
+
+ layer.enabled: msaaCheckBox.checked
+ layer.samples: 4
+
+ Item {
+ id: zoomView
+ anchors.fill: parent
+ anchors.margins: 10
+ anchors.leftMargin: 100
+ property bool zoomedIn: false
+ property rect zoomTarget: "0, 0, 100x100"
+ property rect zoomRect: zoomTarget
+ Behavior on zoomRect {
+ id: zoomBehavior
+ PropertyAnimation { id: zoomAnimation; duration: 800 }
+ }
+ property real zoomScale: Math.min(width / zoomRect.width, height / zoomRect.height)
+
+ Item {
+ width: loader1.width
+ height: loader1.height
+ scale: zoomView.zoomScale
+ transformOrigin: Item.TopLeft
+ Loader {
+ id: loader1
+ x: -zoomView.zoomRect.x
+ y: -zoomView.zoomRect.y
+ property rect br: item ? item.boundingRect : "0,0,100x100"
+ source: "tiger.qml"
+ asynchronous: true
+ visible: status === Loader.Ready
+ onLoaded: {
+ item.highlightOnTap = false
+ zoomView.zoomTarget = br
+ width = br
+ height = br
+ }
+ }
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+
+ property vector2d clickPoint
+ property vector2d zoomPoint
+ property bool dragged: false
+ property real targetSize: 30
+ enabled: !zoomAnimation.running
+ onPressed: (event) => {
+ clickPoint = Qt.vector2d(event.x, event.y)
+ zoomPoint = Qt.vector2d(zoomView.zoomRect.x, zoomView.zoomRect.y)
+ dragged = false
+ zoomBehavior.enabled = false
+ }
+ onReleased: (event) => {
+ if (!dragged)
+ doTap(Qt.point(event.x, event.y))
+ }
+ onPositionChanged: (event) => {
+ if (!dragged) {
+ let delta = Qt.vector2d(event.x, event.y).minus(clickPoint)
+ if (delta.length() < drag.threshold)
+ return
+ }
+ dragged = true
+ if (zoomView.zoomedIn) {
+ doDrag(Qt.point(event.x, event.y))
+ }
+ }
+ function doDrag(pos) {
+ let r = zoomView.zoomTarget
+ let xx = zoomPoint.x - (pos.x - clickPoint.x) / zoomView.zoomScale
+ let yy = zoomPoint.y - (pos.y - clickPoint.y) / zoomView.zoomScale
+ zoomView.zoomTarget = Qt.rect(xx, yy, r.width, r.height)
+ }
+ function doTap(pos) {
+ if (zoomView.zoomedIn) {
+ zoomBehavior.enabled = true
+ zoomView.zoomTarget = loader1.item.boundingRect
+ zoomView.zoomedIn = false
+ } else {
+ let localPos = loader1.mapFromItem(root, pos)
+ let xx = localPos.x - loader1.br.left
+ let yy = localPos.y - loader1.br.top
+ if (loader1.item.contains(Qt.point(xx,yy))) {
+ zoomBehavior.enabled = true
+ zoomView.zoomTarget = Qt.rect(xx - 184 - targetSize/2, yy - 144 - targetSize/2, targetSize, targetSize)
+ zoomView.zoomedIn = true
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ opacity: 0.5
+ color: "white"
+ x: settings.childrenRect.x
+ y: settings.childrenRect.y
+ width: settings.childrenRect.width
+ height: settings.childrenRect.height
+ }
+ Column {
+ id: settings
+ RadioButton {
+ id: curveRendererCheckBox
+ text: "Curve Renderer"
+ onCheckedChanged: {
+ loader1.item.preferredRendererType = checked ? Shape.CurveRenderer : Shape.GeometryRenderer
+ }
+ }
+ RadioButton {
+ id: geometryRendererCheckBox
+ text: "Geometry Renderer"
+ checked: true
+ }
+ RadioButton {
+ id: msaaCheckBox
+ text: "Geometry Renderer 4x MSAA"
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/weatherforecast/BouncyShape.qml b/examples/quick/quickshapes/weatherforecast/BouncyShape.qml
new file mode 100644
index 0000000000..6b2de82d1b
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/BouncyShape.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+Item {
+ id: root
+ default property Item child
+ property real amount: 2
+ children: [child, hoverArea]
+ width: child.width
+ height: child.height
+ property real bounceScale: hoverArea.containsMouse ? amount : 1
+ property alias hoverEnabled: hoverArea.enabled
+ Binding {
+ target: child
+ property: "scale"
+ value: root.bounceScale
+ }
+
+ Behavior on bounceScale {
+ NumberAnimation {
+ duration: 300
+ easing.type: Easing.InOutQuad
+ }
+ }
+ z: hoverArea.containsMouse ? 1 : 0
+
+ MouseArea {
+ id: hoverArea
+ anchors.fill: parent
+ hoverEnabled: true
+ }
+}
diff --git a/examples/quick/quickshapes/weatherforecast/CMakeLists.txt b/examples/quick/quickshapes/weatherforecast/CMakeLists.txt
new file mode 100644
index 0000000000..f890b22a0c
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/CMakeLists.txt
@@ -0,0 +1,84 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(weatherforecast LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+add_subdirectory("../../shared" "shared")
+
+qt_add_executable(weatherforecastexample
+ WIN32
+ MACOSX_BUNDLE
+ main.cpp
+)
+
+target_link_libraries(weatherforecastexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+add_dependencies(weatherforecastexample weatherforecast_shared)
+
+qt_add_qml_module(weatherforecastexample
+ URI weatherforecast
+ QML_FILES
+ "main.qml"
+ "Europe.qml"
+ "Europe_generated.qml"
+ "BouncyShape.qml"
+ "Cloud.qml"
+ "Cloud_generated.qml"
+ "CloudWithLightning.qml"
+ "CloudWithLightning_generated.qml"
+ "CloudWithRain.qml"
+ "CloudWithRain_generated.qml"
+ "CloudWithSnow.qml"
+ "CloudWithSnow_generated.qml"
+ "Gear.qml"
+ "Gear_generated.qml"
+ "MapLabel.qml"
+ "SettingsDrawer.qml"
+ "Sun.qml"
+ "Sun_generated.qml"
+ "SunBehindCloud.qml"
+ "SunBehindCloud_generated.qml"
+ "SunBehindRainCloud.qml"
+ "SunBehindRainCloud_generated.qml"
+ "SunBehindSmallCloud.qml"
+ "SunBehindSmallCloud_generated.qml"
+ "DemoShape.qml"
+ RESOURCES
+ "assets/WorkSans-Regular.ttf"
+)
+
+set(resource_files
+ "qtquickcontrols2.conf"
+)
+
+qt_add_resources(weatherforecastexample "res"
+ PREFIX
+ "/"
+ FILES
+ ${resource_files}
+)
+
+install(TARGETS weatherforecastexample
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET weatherforecastexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/quickshapes/weatherforecast/Cloud.qml b/examples/quick/quickshapes/weatherforecast/Cloud.qml
new file mode 100644
index 0000000000..64a06d4c72
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/Cloud.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+Cloud_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/CloudWithLightning.qml b/examples/quick/quickshapes/weatherforecast/CloudWithLightning.qml
new file mode 100644
index 0000000000..da91eb2838
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/CloudWithLightning.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+CloudWithLightning_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/CloudWithLightningAndRain_generated.qml b/examples/quick/quickshapes/weatherforecast/CloudWithLightningAndRain_generated.qml
new file mode 100644
index 0000000000..6b54874636
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/CloudWithLightningAndRain_generated.qml
@@ -0,0 +1,140 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/cloud-with-lightning-and-rain-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START type: DOC 0
+ DemoShape { //combined path container
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ // PATH visit count: 27
+ ShapePath {
+ // boundingRect: 46.8504, 67.82 37.9392x46.8238
+ strokeColor: "transparent"
+ fillColor: "#fffeca18"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 78 67.82 L 55.17 71.79 C 55.17 71.79 47.57 91.15 47.1 92.46 C 46.63 93.77 46.65 95.84 48.72 95.84 C 51.35 95.84 64.7 95.77 64.7 95.77 C 64.7 95.77 61.46 111.4 61.27 112.8 C 61.08 114.2 62.58 115.43 63.9 114.02 C 65.22 112.61 83.41 89.23 84.2 88.09 C 85.61 86.05 84.25 84.17 82.65 84.08 C 81.05 83.99 70.33 84.22 70.33 84.22 L 78 67.82 " }
+ }
+ // PATH visit count: 11
+ ShapePath {
+ // boundingRect: 66.5578, 69.49 9.44218x14.9005
+ strokeColor: "transparent"
+ fillColor: "#fffdb900"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 71.92 70.54 C 71.92 70.54 66.83 81.8 66.64 82.44 C 66.08 84.34 68.54 85.11 69.24 83.57 C 69.94 82.02 76 69.49 76 69.49 L 71.92 70.54 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 48.3858, 92.0244 17.1635x2.46762
+ strokeColor: "transparent"
+ fillColor: "#fffdb900"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 49.99 92.17 C 51.33 92.13 63.09 92.1 64.07 92.03 C 65.9 91.89 66.18 94.42 64.07 94.42 C 61.88 94.42 50.34 94.46 49.64 94.49 C 48.02 94.57 47.8 92.24 49.99 92.17 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 52.8347, 68.3555 12.6443x20.6092
+ strokeColor: "transparent"
+ fillColor: "#ffffe36a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 59.42 69.01 C 59.42 69.01 53.42 84.07 53.08 85.2 C 51.67 89.92 56.74 89.78 58.08 87.1 C 58.74 85.77 65.19 72.11 65.47 69.64 C 65.75 67.18 59.42 69.01 59.42 69.01 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 57.598, 86.3211 4.2165x4.82538
+ strokeColor: "transparent"
+ fillColor: "#ffffe36a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 61.01 86.57 C 60.15 85.98 59.11 86.5 58.48 87.49 C 57.85 88.48 56.93 90.09 58.34 90.94 C 59.61 91.7 60.73 90.17 61.23 89.46 C 61.72 88.75 62.35 87.48 61.01 86.57 " }
+ }
+ // PATH visit count: 16
+ ShapePath {
+ // boundingRect: 65.0829, 89.7223 7.8611x17.1865
+ strokeColor: "transparent"
+ fillColor: "#ffffe36a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 71.34 89.8 C 69.05 89.31 68.31 91.21 67.82 93.46 C 67.33 95.71 65.49 102.99 65.15 104.79 C 64.66 107.32 66.98 107.68 67.97 105.35 C 68.71 103.61 71.92 95.76 72.33 94.51 C 72.96 92.62 73.66 90.3 71.34 89.8 " }
+ }
+ // PATH visit count: 26
+ ShapePath {
+ // boundingRect: 4.48158, 6.41669 120.262x63.9578
+ strokeColor: "transparent"
+ fillColor: "#ffe4eaee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 107.92 46.26 C 107.92 46.26 110.52 11.28 80.61 6.84 C 53.72 2.85 48.44 28.38 48.44 28.38 C 48.44 28.38 38.28 26.01 30.98 32.53 C 23.73 39.01 24.29 46.68 24.29 46.68 C 24.29 46.68 6.41 43.86 4.58 59.21 C 2.87 73.55 24.01 69.98 24.01 69.98 L 124.39 62.59 C 124.39 62.59 126.77 55.05 119.18 49.36 C 114.67 45.97 107.92 46.26 107.92 46.26 " }
+ }
+ // PATH visit count: 49
+ ShapePath {
+ // boundingRect: 4.42853, 47.15 120.397x26.7991
+ strokeColor: "transparent"
+ fillGradient: RadialGradient {
+ centerX: 65.811
+ centerY: 75.277
+ centerRadius: 29.434
+ focalX: centerX; focalY: centerY
+ GradientStop { position: 0.193; color: "#ffffe366" }
+ GradientStop { position: 0.331; color: "#ffeddd82" }
+ GradientStop { position: 0.571; color: "#ffd0d4ad" }
+ GradientStop { position: 0.759; color: "#ffbfcfc7" }
+ GradientStop { position: 0.87; color: "#ffb8cdd1" }
+ }
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 53.81 54.19 C 54.65 53.91 59.44 58 73.24 58.13 C 92.2 58.32 106.56 47.15 106.56 47.15 C 106.56 47.15 107.04 50.55 105.53 52.97 C 103.65 55.97 100.74 56.54 100.74 56.54 C 100.74 56.54 106 60.2 113.22 59.64 C 119.73 59.13 124.44 56.92 124.44 56.92 C 124.44 56.92 125.07 60.13 124.72 63.26 C 124.02 69.5 120.36 72.04 116.32 72.5 C 113.35 72.84 87.98 72.96 77.56 73.06 C 68.17 73.15 20.32 74.35 15.43 73.81 C 10.36 73.25 6.51 70.2 5.11 65.83 C 4.04 62.52 4.55 60.39 4.55 60.39 C 4.55 60.39 13.87 62.86 19.1 62.27 C 25.67 61.52 30.08 57.39 30.08 57.39 C 30.08 57.39 34.02 59.64 42.75 58.89 C 50.26 58.23 53.81 54.19 53.81 54.19 " }
+ }
+ // PATH visit count: 16
+ ShapePath {
+ // boundingRect: 91.3033, 74.1363 13.4678x20.2873
+ strokeColor: "transparent"
+ fillColor: "#ff52c0ee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 103.9 74.2 C 102.88 73.63 99.25 77.02 97.21 79.06 C 95.96 80.31 91.98 83.58 91.44 86.73 C 90.1 94.47 98.97 96.23 102.49 92.64 C 105.15 89.93 104.67 84.64 104.67 82.86 C 104.68 80.39 105.17 74.9 103.9 74.2 " }
+ }
+ // PATH visit count: 16
+ ShapePath {
+ // boundingRect: 18.9213, 78.2966 12.9004x19.7846
+ strokeColor: "transparent"
+ fillColor: "#ff52c0ee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 31.02 78.36 C 30.05 77.8 26.57 81.08 24.61 83.07 C 23.41 84.28 19.6 87.45 19.06 90.53 C 17.74 98.08 26.19 99.85 29.57 96.37 C 32.12 93.74 31.7 88.57 31.71 86.83 C 31.72 84.42 32.22 79.06 31.02 78.36 " }
+ }
+ // PATH visit count: 16
+ ShapePath {
+ // boundingRect: 34.7194, 100.706 13.1416x20.3505
+ strokeColor: "transparent"
+ fillColor: "#ff52c0ee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 47.15 100.74 C 46.06 100.33 42.39 103.67 40.35 105.71 C 39.1 106.96 35.4 110.16 34.86 113.31 C 33.52 121.05 42.11 122.88 45.63 119.29 C 48.29 116.58 47.85 111.29 47.81 109.51 C 47.71 104.87 47.9 101.02 47.15 100.74 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 93.1117, 83.8677 5.96569x6.96478
+ strokeColor: "transparent"
+ fillColor: "#ffb2e6fe"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 97.78 84.22 C 96.38 83.49 94.68 83.8 93.6 86.05 C 92.52 88.3 93.41 90.09 94.54 90.6 C 95.84 91.2 97.45 90.65 98.53 88.82 C 99.61 86.99 98.95 84.83 97.78 84.22 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 36.0415, 110.757 5.61809x6.84249
+ strokeColor: "transparent"
+ fillColor: "#ffb2e6fe"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 40.01 110.93 C 38.57 110.43 36.99 110.96 36.27 113.27 C 35.55 115.58 36.66 117.16 37.81 117.5 C 39.14 117.89 40.6 117.14 41.38 115.23 C 42.15 113.32 41.21 111.35 40.01 110.93 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 20.2228, 87.8513 5.75099x7.08989
+ strokeColor: "transparent"
+ fillColor: "#ffb2e6fe"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 24.07 87.97 C 22.55 87.55 20.95 88.2 20.37 90.63 C 19.79 93.06 21.03 94.62 22.24 94.88 C 23.64 95.19 25.09 94.32 25.77 92.3 C 26.44 90.3 25.35 88.32 24.07 87.97 " }
+ }
+ } // END type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/CloudWithLightning_generated.qml b/examples/quick/quickshapes/weatherforecast/CloudWithLightning_generated.qml
new file mode 100644
index 0000000000..5be18dc851
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/CloudWithLightning_generated.qml
@@ -0,0 +1,142 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/cloud-with-lightning-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START svg1314 type: DOC 0
+ Item { // structure node
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ objectName: "svg1314"
+ // skipping DEFS "defs1318"
+ // PATH visit path1287 count: 27
+ DemoShape {
+ objectName: "path1287"
+ ShapePath {
+ objectName: "svg_path:path1287"
+ // boundingRect: 48.6804, 71.24 37.9392x46.8238
+ strokeColor: "#b0ab9d"
+ strokeWidth: 1
+ fillColor: "#fffeca18"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 79.83 71.24 L 57 75.21 C 57 75.21 49.4 94.57 48.93 95.88 C 48.46 97.19 48.48 99.26 50.55 99.26 C 53.18 99.26 66.53 99.19 66.53 99.19 C 66.53 99.19 63.29 114.82 63.1 116.22 C 62.91 117.62 64.41 118.85 65.73 117.44 C 67.04 116.03 85.24 92.65 86.03 91.51 C 87.44 89.47 86.08 87.59 84.48 87.5 C 82.88 87.41 72.16 87.64 72.16 87.64 L 79.83 71.24 " }
+ }
+ }
+ // PATH visit path1289 count: 11
+ DemoShape {
+ objectName: "path1289"
+ ShapePath {
+ objectName: "svg_path:path1289"
+ // boundingRect: 68.3478, 73.99 9.44218x14.9005
+ strokeColor: "transparent"
+ fillColor: "#fffdb900"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 73.71 75.04 C 73.71 75.04 68.62 86.3 68.43 86.94 C 67.87 88.84 70.33 89.61 71.03 88.07 C 71.73 86.52 77.79 73.99 77.79 73.99 L 73.71 75.04 " }
+ }
+ }
+ // PATH visit path1291 count: 13
+ DemoShape {
+ objectName: "path1291"
+ ShapePath {
+ objectName: "svg_path:path1291"
+ // boundingRect: 49.2295, 96.1544 17.1598x2.46762
+ strokeColor: "transparent"
+ fillColor: "#fffdb900"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 50.83 96.3 C 52.17 96.26 63.93 96.23 64.91 96.16 C 66.74 96.02 67.02 98.55 64.91 98.55 C 62.72 98.55 51.18 98.59 50.48 98.62 C 48.86 98.7 48.65 96.37 50.83 96.3 " }
+ }
+ }
+ // PATH visit path1293 count: 13
+ DemoShape {
+ objectName: "path1293"
+ ShapePath {
+ objectName: "svg_path:path1293"
+ // boundingRect: 53.3947, 75.3055 12.645x20.6092
+ strokeColor: "transparent"
+ fillColor: "#ffffe36a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 59.98 75.96 C 59.98 75.96 53.98 91.02 53.64 92.15 C 52.23 96.87 57.3 96.73 58.64 94.05 C 59.3 92.72 65.75 79.06 66.03 76.59 C 66.32 74.13 59.98 75.96 59.98 75.96 " }
+ }
+ }
+ // PATH visit path1295 count: 13
+ DemoShape {
+ objectName: "path1295"
+ ShapePath {
+ objectName: "svg_path:path1295"
+ // boundingRect: 59.108, 90.9111 4.20909x4.82538
+ strokeColor: "transparent"
+ fillColor: "#ffffe36a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 62.52 91.16 C 61.66 90.57 60.62 91.09 59.99 92.08 C 59.36 93.07 58.44 94.68 59.85 95.53 C 61.12 96.29 62.24 94.76 62.74 94.05 C 63.22 93.35 63.85 92.08 62.52 91.16 " }
+ }
+ }
+ // PATH visit path1297 count: 16
+ DemoShape {
+ objectName: "path1297"
+ ShapePath {
+ objectName: "svg_path:path1297"
+ // boundingRect: 65.8329, 94.3223 7.8611x17.1865
+ strokeColor: "transparent"
+ fillColor: "#ffffe36a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 72.09 94.4 C 69.8 93.91 69.06 95.81 68.57 98.06 C 68.08 100.31 66.24 107.59 65.9 109.39 C 65.41 111.92 67.73 112.28 68.72 109.95 C 69.46 108.21 72.67 100.36 73.08 99.11 C 73.71 97.22 74.41 94.89 72.09 94.4 " }
+ }
+ }
+ // PATH visit path1299 count: 26
+ DemoShape {
+ objectName: "path1299"
+ ShapePath {
+ objectName: "svg_path:path1299"
+ // boundingRect: 4.45246, 10.7839 119.361x62.1608
+ strokeColor: "transparent"
+ fillColor: "#ffe4eaee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 107.35 50.2 C 107.35 50.2 108.64 15.42 79.9 11.2 C 53.01 7.26 48.23 32.6 48.23 32.6 C 48.23 32.6 36.55 29.93 30.21 37.39 C 23.87 44.85 23.73 50.62 23.73 50.62 C 23.73 50.62 6.13 48.93 4.58 63.29 C 3.03 77.65 16.12 71.74 16.12 71.74 L 123.25 67.02 C 123.25 67.02 126.2 58.99 118.6 53.29 C 114.11 49.92 107.35 50.2 107.35 50.2 " }
+ }
+ }
+ // PATH visit path1312 count: 49
+ DemoShape {
+ objectName: "path1312"
+ ShapePath {
+ objectName: "svg_path:path1312"
+ // boundingRect: 4.32853, 52.17 119.834x25.6097
+ strokeColor: "transparent"
+ fillGradient: RadialGradient {
+ centerX: 65.427
+ centerY: 79.054
+ centerRadius: 29.294
+ focalX: centerX; focalY: centerY
+ GradientStop { position: 0.193; color: "#ffffe366" }
+ GradientStop { position: 0.331; color: "#ffeddd82" }
+ GradientStop { position: 0.571; color: "#ffd0d4ad" }
+ GradientStop { position: 0.759; color: "#ffbfcfc7" }
+ GradientStop { position: 0.87; color: "#ffb8cdd1" }
+ }
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 58.78 55.69 C 59.62 55.41 64.83 59.91 78.63 59.49 C 92.43 59.07 105.24 52.17 105.24 52.17 C 105.24 52.17 104.54 57.1 102.99 59.49 C 101.44 61.88 98.77 64.7 98.77 64.7 C 98.77 64.7 105.95 66.39 112.43 65.54 C 118.91 64.7 123.69 61.6 123.69 61.6 C 123.69 61.6 124.53 64.84 123.97 67.94 C 123.25 71.91 119.75 74.84 116.79 75.26 C 113.83 75.68 87.23 77.02 76.81 77.37 C 68.36 77.66 20.18 78.05 15.29 77.51 C 10.22 76.95 6.42 74.69 5.01 70.33 C 3.94 67.02 4.45 64.98 4.45 64.98 C 4.45 64.98 10.81 67.71 15.99 66.81 C 22.47 65.68 25.99 60.62 25.99 60.62 C 25.99 60.62 32.33 63.86 41.48 62.73 C 50.62 61.6 58.78 55.69 58.78 55.69 " }
+ }
+ }
+ // PATH visit path1377 count: 49
+ DemoShape {
+ objectName: "path1377"
+ // Scale 0.16, 0.16 Translate 0, 0
+ transform: Scale { xScale: 0.16; yScale: 0.16 }
+ ShapePath {
+ objectName: "svg_path:path1377"
+ // boundingRect: 27.0533, 67.399 748.971x418.724
+ strokeColor: "#b0ab9d"
+ strokeWidth: 6.25
+ fillColor: "transparent"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 462.465 67.416 C 326.828 69.1539 301.438 203.75 301.438 203.75 C 301.438 203.75 228.438 187.063 188.813 233.688 C 149.188 280.313 148.313 316.375 148.313 316.375 C 148.313 316.375 38.3125 305.813 28.625 395.563 C 28.2196 399.318 27.9883 402.832 27.8906 406.156 C 27.8882 406.155 27.8125 406.125 27.8125 406.125 C 27.8125 406.125 24.625 418.875 31.3125 439.563 C 40.125 466.813 63.875 480.938 95.5625 484.438 C 126.125 487.813 427.25 485.375 480.063 483.563 C 545.188 481.375 711.438 473 729.938 470.375 C 748.438 467.75 770.313 449.438 774.813 424.625 C 778.042 406.75 773.87 388.338 773.197 385.563 C 771.117 369.235 763.281 349.585 741.25 333.063 C 713.188 312 670.938 313.75 670.938 313.75 C 670.938 313.75 679 96.375 499.375 70 C 486.245 68.0762 473.96 67.2687 462.465 67.416 " }
+ }
+ }
+ } // END svg1314 type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/CloudWithRain.qml b/examples/quick/quickshapes/weatherforecast/CloudWithRain.qml
new file mode 100644
index 0000000000..e332d90a67
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/CloudWithRain.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+CloudWithRain_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/CloudWithRain_generated.qml b/examples/quick/quickshapes/weatherforecast/CloudWithRain_generated.qml
new file mode 100644
index 0000000000..5ee4aaef1d
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/CloudWithRain_generated.qml
@@ -0,0 +1,114 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/cloud-with-rain-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START type: DOC 0
+ DemoShape { //combined path container
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ // PATH visit count: 32
+ ShapePath {
+ // boundingRect: 4.51974, 6.33677 119.18x62.539
+ strokeColor: "transparent"
+ fillColor: "#ffe2ebee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 107.62 46.29 C 107.62 46.29 109.34 13.97 83.73 7.57 C 54.12 0.17 48.29 28.49 48.29 28.49 C 48.29 28.49 36.74 26.3 29.55 34.11 C 23.52 40.66 24.09 47.07 24.09 47.07 C 24.09 47.07 16.13 45.35 9.88 50.82 C 3.63 56.29 4.57 61.43 4.57 61.43 L 23.15 68.61 C 23.15 68.61 116.98 69.08 117.45 68.77 C 117.92 68.46 123.7 59.4 123.7 59.4 C 123.7 59.4 122.76 51.59 117.3 48.47 C 111.83 45.35 107.62 46.29 107.62 46.29 " }
+ }
+ // PATH visit count: 49
+ ShapePath {
+ // boundingRect: 4.46833, 47.5237 119.634x26.5724
+ strokeColor: "transparent"
+ fillColor: "#ffb9ced2"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 14.25 73.45 C 18.93 73.61 49.54 74.23 68.11 74.07 C 86.68 73.91 114.92 74.21 118.84 72.36 C 121.91 70.91 123.74 68.51 124.03 65.15 C 124.31 61.94 123.69 59.4 123.69 59.4 C 123.69 59.4 119.32 62.56 111.04 62.68 C 105.89 62.75 95.43 61.9 97.46 60.03 C 98.03 59.5 106.12 53.37 105.73 47.7 C 105.64 46.36 98.39 53.01 91.52 55.82 C 84.65 58.63 78.13 60.8 69.05 60.22 C 61.4 59.72 58.08 56.61 58.08 56.61 C 58.08 56.61 54.39 60.94 43.3 61.87 C 32.21 62.81 29.82 58.64 29.82 58.64 C 29.82 58.64 24.45 62.7 15.43 62.58 C 6.43 62.44 4.66 59.2 4.66 59.2 C 4.66 59.2 3.81 64.97 5.69 68.34 C 7.61 71.77 11.1 73.35 14.25 73.45 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 58.0616, 81.5193 13.614x19.5023
+ strokeColor: "transparent"
+ fillColor: "#ff4fc4f8"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 69.99 81.57 C 68.28 80.97 62.18 85.8 59.43 90.57 C 56.67 95.36 58.47 98.86 61.42 100.3 C 64.74 101.92 69.52 100.92 70.88 96.2 C 72.22 91.53 71.83 82.22 69.99 81.57 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 25.6777, 79.1454 13.4492x19.7249
+ strokeColor: "transparent"
+ fillColor: "#ff4fc4f8"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 37.66 79.19 C 36.11 78.64 31.08 83.31 28.82 85.83 C 25.79 89.22 23.45 95.54 29.2 98.1 C 34.99 100.67 38.75 96.46 39.04 90.64 C 39.32 85.22 38.94 79.65 37.66 79.19 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 37.6925, 101.563 13.5018x19.9666
+ strokeColor: "transparent"
+ fillColor: "#ff4fc4f8"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 49.87 101.65 C 48.22 100.76 41.29 106.91 39.08 110.58 C 36.88 114.25 37.02 118.96 41.52 120.89 C 46.02 122.82 49.99 120.35 50.82 113.92 C 51.4 109.36 51.46 102.51 49.87 101.65 " }
+ }
+ // PATH visit count: 10
+ ShapePath {
+ // boundingRect: 71.433, 102.131 13.3742x19.7696
+ strokeColor: "transparent"
+ fillColor: "#ff4fc4f8"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 83.38 102.18 C 82.19 101.51 73.95 107.87 72.1 112.5 C 68.43 121.69 80.97 125.79 83.82 117.15 C 85.48 112.11 84.85 103.01 83.38 102.18 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 91.7795, 82.2534 13.8268x20.1662
+ strokeColor: "transparent"
+ fillColor: "#ff4fc4f8"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 103.87 82.37 C 102.24 81.37 95.39 87.01 93.11 91.25 C 90.23 96.61 92.47 100.65 95.61 101.93 C 98.87 103.25 103.06 101.98 104.48 97.15 C 106.53 90.19 105.45 83.35 103.87 82.37 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 93.752, 91.0753 4.98803x7.77652
+ strokeColor: "transparent"
+ fillColor: "#ffaee6fd"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 96.4 91.09 C 95.32 91.22 93.98 92.76 93.77 95.39 C 93.56 98.02 95.22 99.35 96.9 98.68 C 98.48 98.05 98.74 95.8 98.74 94.38 C 98.74 92.96 98.07 90.88 96.4 91.09 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 59.3141, 90.2226 5.39921x7.99531
+ strokeColor: "transparent"
+ fillColor: "#ffaee6fd"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 63 90.26 C 61.86 89.94 59.74 91.7 59.37 94.55 C 59.08 96.75 59.93 98.08 61.64 98.21 C 64.02 98.39 64.77 95.46 64.71 93.67 C 64.67 92.65 64.49 90.68 63 90.26 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 39.61, 109.787 5.08875x8.13799
+ strokeColor: "transparent"
+ fillColor: "#ffaee6fd"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 42.45 109.79 C 41.39 109.71 39.61 111.49 39.61 114.26 C 39.61 117.03 41.05 118.23 42.75 117.86 C 44.45 117.48 44.79 114.23 44.68 112.94 C 44.57 111.65 44.02 109.91 42.45 109.79 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 27.3958, 88.3815 5.24188x7.26814
+ strokeColor: "transparent"
+ fillColor: "#ffaee6fd"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 30.91 88.44 C 29.48 88.03 27.65 89.84 27.43 91.88 C 27.2 93.92 28.15 95.4 29.66 95.63 C 31.17 95.86 32.46 94.04 32.61 91.92 C 32.77 89.8 32.24 88.81 30.91 88.44 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 73.239, 110.966 5.19528x8.30781
+ strokeColor: "transparent"
+ fillColor: "#ffaee6fd"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 76.4 110.97 C 75.08 110.86 73.19 112.81 73.24 115.76 C 73.29 118.71 74.82 119.21 75.83 119.27 C 77.28 119.36 78.29 117.65 78.42 114.97 C 78.53 112.72 78.02 111.1 76.4 110.97 " }
+ }
+ } // END type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/CloudWithSnow.qml b/examples/quick/quickshapes/weatherforecast/CloudWithSnow.qml
new file mode 100644
index 0000000000..d74648898f
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/CloudWithSnow.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+CloudWithSnow_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/CloudWithSnow_generated.qml b/examples/quick/quickshapes/weatherforecast/CloudWithSnow_generated.qml
new file mode 100644
index 0000000000..314defa445
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/CloudWithSnow_generated.qml
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/cloud-with-snow-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START type: DOC 0
+ DemoShape { //combined path container
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ // PATH visit count: 26
+ ShapePath {
+ // boundingRect: 4.90965, 6.40871 119.834x64.0813
+ strokeColor: "transparent"
+ fillColor: "#ffe4eaee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 108.55 45.69 C 108.55 45.69 110.52 11.27 80.61 6.83 C 53.72 2.85 48.44 28.38 48.44 28.38 C 48.44 28.38 38.92 25.94 31.62 32.46 C 24.36 38.93 25 46.11 25 46.11 C 25 46.11 6.84 44.28 5.01 59.63 C 3.3 73.97 24.01 69.98 24.01 69.98 L 124.39 62.59 C 124.39 62.59 126.77 55.05 119.18 49.36 C 114.67 45.97 108.55 45.69 108.55 45.69 " }
+ }
+ // PATH visit count: 49
+ ShapePath {
+ // boundingRect: 4.80443, 46.68 120.011x26.4483
+ strokeColor: "transparent"
+ fillColor: "#ffb9ced2"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 56.95 55.83 C 57.79 55.55 60.32 58.79 74.13 58.79 C 88.49 58.79 106.23 46.68 106.23 46.68 C 106.23 46.68 106.16 51.42 104.68 53.86 C 102.71 57.1 98.2 58.93 98.2 58.93 C 98.2 58.93 104.68 61.6 113.26 60.9 C 119.77 60.37 124.43 56.91 124.43 56.91 C 124.43 56.91 125.06 60.12 124.71 63.25 C 124.01 69.49 120.39 71.27 116.36 71.74 C 113.39 72.08 87.97 72.95 77.55 73.05 C 68.16 73.14 20.59 73.26 15.7 72.72 C 10.63 72.16 6.83 69.48 5.42 65.12 C 4.35 61.81 5 58.64 5 58.64 C 5 58.64 10.77 60.89 17.67 60.89 C 24.28 60.89 29.21 57.79 29.21 57.79 C 29.21 57.79 34.42 61.36 43.15 60.61 C 50.69 59.97 56.95 55.83 56.95 55.83 " }
+ }
+ // PATH visit count: 71
+ ShapePath {
+ // boundingRect: 92.5268, 76.076 22.6332x20.233
+ strokeColor: "transparent"
+ fillColor: "#ff92c9f4"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 104.07 81.59 C 104.07 81.59 106.32 77.93 106.92 77.19 C 107.52 76.45 108.54 75.64 109.81 76.35 C 111.08 77.05 110.65 78.5 110.3 79.13 C 109.95 79.76 107.84 83.85 107.84 83.85 C 107.84 83.85 111.82 83.89 112.7 83.89 C 113.58 83.89 115.16 84.59 115.16 86.14 C 115.16 87.48 114.53 88.32 113.01 88.39 C 111.49 88.46 107.7 88.46 107.7 88.46 C 107.7 88.46 109.74 91.8 110.34 92.82 C 110.94 93.84 111.01 95.07 110.13 95.81 C 109 96.76 107.53 96.23 106.65 94.86 C 105.99 93.82 103.94 90.85 103.94 90.85 C 103.94 90.85 101.51 94.37 100.88 95.14 C 100.25 95.91 99.09 96.83 97.92 95.95 C 96.48 94.86 97.31 93.63 97.92 92.68 L 100.52 88.6 C 100.52 88.6 96.09 88.49 94.82 88.49 C 93.55 88.49 92.46 87.79 92.53 86.34 C 92.6 84.86 93.52 84.26 95.03 84.23 C 96.54 84.19 100.56 84.19 100.56 84.19 C 100.56 84.19 98.1 80.67 97.46 79.65 C 96.83 78.63 96.76 77.33 97.74 76.59 C 98.73 75.85 99.89 76.27 100.63 77.22 C 101.37 78.17 104.07 81.59 104.07 81.59 " }
+ }
+ // PATH visit count: 71
+ ShapePath {
+ // boundingRect: 53.6668, 79.706 22.6332x20.233
+ strokeColor: "transparent"
+ fillColor: "#ff92c9f4"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 65.21 85.22 C 65.21 85.22 67.46 81.56 68.06 80.82 C 68.66 80.08 69.68 79.27 70.95 79.98 C 72.22 80.68 71.79 82.13 71.44 82.76 C 71.09 83.39 68.98 87.48 68.98 87.48 C 68.98 87.48 72.96 87.52 73.84 87.52 C 74.72 87.52 76.3 88.22 76.3 89.77 C 76.3 91.11 75.67 91.95 74.15 92.02 C 72.64 92.09 68.84 92.09 68.84 92.09 C 68.84 92.09 70.88 95.43 71.48 96.45 C 72.08 97.47 72.15 98.7 71.27 99.44 C 70.14 100.39 68.67 99.86 67.79 98.49 C 67.13 97.45 65.08 94.48 65.08 94.48 C 65.08 94.48 62.65 98 62.02 98.77 C 61.39 99.54 60.23 100.46 59.06 99.58 C 57.62 98.49 58.45 97.26 59.06 96.31 L 61.66 92.23 C 61.66 92.23 57.23 92.12 55.96 92.12 C 54.69 92.12 53.6 91.42 53.67 89.97 C 53.74 88.49 54.66 87.89 56.17 87.86 C 57.68 87.82 61.7 87.82 61.7 87.82 C 61.7 87.82 59.24 84.3 58.6 83.28 C 57.97 82.26 57.9 80.96 58.88 80.22 C 59.87 79.48 61.03 79.9 61.77 80.85 C 62.51 81.8 65.21 85.22 65.21 85.22 " }
+ }
+ // PATH visit count: 71
+ ShapePath {
+ // boundingRect: 32.6468, 99.916 22.6332x20.233
+ strokeColor: "transparent"
+ fillColor: "#ff92c9f4"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 44.19 105.43 C 44.19 105.43 46.44 101.77 47.04 101.03 C 47.64 100.29 48.66 99.48 49.93 100.19 C 51.2 100.89 50.77 102.34 50.42 102.97 C 50.07 103.6 47.96 107.69 47.96 107.69 C 47.96 107.69 51.94 107.73 52.82 107.73 C 53.7 107.73 55.28 108.43 55.28 109.98 C 55.28 111.32 54.65 112.16 53.13 112.23 C 51.62 112.3 47.82 112.3 47.82 112.3 C 47.82 112.3 49.86 115.64 50.46 116.66 C 51.06 117.68 51.13 118.91 50.25 119.65 C 49.12 120.6 47.65 120.07 46.77 118.7 C 46.11 117.66 44.06 114.69 44.06 114.69 C 44.06 114.69 41.63 118.21 41 118.98 C 40.37 119.75 39.21 120.67 38.04 119.79 C 36.6 118.7 37.43 117.47 38.04 116.52 L 40.64 112.44 C 40.64 112.44 36.21 112.33 34.94 112.33 C 33.67 112.33 32.58 111.63 32.65 110.18 C 32.72 108.7 33.64 108.1 35.15 108.07 C 36.66 108.03 40.68 108.03 40.68 108.03 C 40.68 108.03 38.22 104.51 37.58 103.49 C 36.95 102.47 36.88 101.17 37.86 100.43 C 38.85 99.69 40.01 100.11 40.75 101.06 C 41.49 102.01 44.19 105.43 44.19 105.43 " }
+ }
+ // PATH visit count: 71
+ ShapePath {
+ // boundingRect: 15.2836, 76.7159 22.6366x20.215
+ strokeColor: "transparent"
+ fillColor: "#ff92c9f4"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 26.9 82.16 C 26.9 82.16 29.21 78.53 29.82 77.8 C 30.43 77.07 31.47 76.28 32.72 77 C 33.98 77.72 33.53 79.16 33.17 79.79 C 32.81 80.42 30.63 84.47 30.63 84.47 C 30.63 84.47 34.61 84.57 35.49 84.58 C 36.37 84.59 37.94 85.32 37.92 86.87 C 37.9 88.21 37.25 89.04 35.74 89.09 C 34.23 89.14 30.43 89.08 30.43 89.08 C 30.43 89.08 32.42 92.45 33 93.48 C 33.58 94.51 33.64 95.74 32.74 96.47 C 31.6 97.4 30.13 96.85 29.27 95.47 C 28.62 94.42 26.62 91.42 26.62 91.42 C 26.62 91.42 24.14 94.9 23.49 95.67 C 22.84 96.43 21.68 97.34 20.52 96.43 C 19.09 95.32 19.95 94.1 20.57 93.16 L 23.24 89.12 C 23.24 89.12 18.81 88.95 17.54 88.93 C 16.27 88.91 15.19 88.19 15.29 86.75 C 15.38 85.27 16.31 84.69 17.82 84.68 C 19.33 84.67 23.35 84.73 23.35 84.73 C 23.35 84.73 20.94 81.17 20.32 80.14 C 19.7 79.11 19.65 77.81 20.65 77.08 C 21.65 76.36 22.8 76.8 23.53 77.76 C 24.24 78.7 26.9 82.16 26.9 82.16 " }
+ }
+ // PATH visit count: 71
+ ShapePath {
+ // boundingRect: 75.5368, 99.636 22.6332x20.233
+ strokeColor: "transparent"
+ fillColor: "#ff92c9f4"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 87.08 105.15 C 87.08 105.15 89.33 101.49 89.93 100.75 C 90.53 100.01 91.55 99.2 92.82 99.91 C 94.09 100.61 93.66 102.06 93.31 102.69 C 92.96 103.32 90.85 107.41 90.85 107.41 C 90.85 107.41 94.83 107.45 95.71 107.45 C 96.59 107.45 98.17 108.15 98.17 109.7 C 98.17 111.04 97.54 111.88 96.02 111.95 C 94.5 112.02 90.71 112.02 90.71 112.02 C 90.71 112.02 92.75 115.36 93.35 116.38 C 93.95 117.4 94.02 118.63 93.14 119.37 C 92.01 120.32 90.54 119.79 89.66 118.42 C 89 117.38 86.95 114.41 86.95 114.41 C 86.95 114.41 84.52 117.93 83.89 118.7 C 83.26 119.47 82.1 120.39 80.93 119.51 C 79.49 118.42 80.32 117.19 80.93 116.24 L 83.53 112.16 C 83.53 112.16 79.1 112.05 77.83 112.05 C 76.56 112.05 75.47 111.35 75.54 109.9 C 75.61 108.42 76.53 107.82 78.04 107.79 C 79.55 107.76 83.57 107.75 83.57 107.75 C 83.57 107.75 81.11 104.23 80.47 103.21 C 79.84 102.19 79.77 100.89 80.75 100.15 C 81.74 99.41 82.9 99.83 83.64 100.78 C 84.38 101.73 87.08 105.15 87.08 105.15 " }
+ }
+ } // END type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/Cloud_generated.qml b/examples/quick/quickshapes/weatherforecast/Cloud_generated.qml
new file mode 100644
index 0000000000..6c097d5b76
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/Cloud_generated.qml
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/cloud-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START svg1047 type: DOC 0
+ Item { // structure node
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ objectName: "svg1047"
+ // skipping DEFS "defs1051"
+ // PATH visit path1043 count: 33
+ DemoShape {
+ objectName: "path1043"
+ ShapePath {
+ objectName: "svg_path:path1043"
+ // boundingRect: 4.51543, 20.1148 119.331x70.3352
+ strokeColor: "transparent"
+ fillColor: "#ffe4eaee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 23.45 62.3 C 24.17 61.58 22.18 53.01 31.05 46.39 C 39.92 39.77 45.97 43.72 46.82 43.43 C 47.66 43.15 51.61 25.83 68.22 21.33 C 84.83 16.83 102.15 25.27 106.23 39.35 C 109.96 52.22 107.07 60.89 107.5 61.45 C 107.92 62.01 115.95 61.73 120.59 69.19 C 125.23 76.65 123.55 81.3 123.55 81.3 L 93.99 90.45 L 46.69 90.45 C 46.69 90.45 5.02 79.47 4.6 77.5 C 4.18 75.53 5.13 69.13 11.92 65.25 C 17.82 61.88 22.18 63.57 23.45 62.3 " }
+ }
+ }
+ // PATH visit path1045 count: 52
+ DemoShape {
+ objectName: "path1045"
+ ShapePath {
+ objectName: "svg_path:path1045"
+ // boundingRect: 4.21403, 74.83 119.62x29.9249
+ strokeColor: "transparent"
+ fillColor: "#ffbacdd2"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 35.16 92.84 C 35.16 92.84 19.38 96.14 8.71 87.88 C 2.29 82.9 4.63 74.83 4.63 74.83 C 4.63 74.83 9.23 79.48 18.52 80.74 C 27.81 82.01 38.23 81.58 38.23 81.58 C 38.23 81.58 40.83 86.02 50.62 88.06 C 62.89 90.61 69.36 84.33 69.36 84.33 C 69.36 84.33 72.72 88.35 84.55 88.63 C 96.38 88.91 103.01 80.65 104.12 80.46 C 104.68 80.37 107.94 83.33 114.4 82.29 C 120.55 81.3 123.79 78.63 123.79 78.63 C 123.79 78.63 124.68 85.25 118.49 89.33 C 113.66 92.51 105.26 92.85 105.26 92.85 C 105.26 92.85 103.98 97.76 98.21 101.33 C 92.85 104.66 83.61 105.77 76.77 103.73 C 68.18 101.17 66.05 97.26 66.05 97.26 C 66.05 97.26 59.65 101.01 49.65 99.74 C 40.2 98.56 35.16 92.84 35.16 92.84 " }
+ }
+ }
+ // PATH visit path1183 count: 61
+ DemoShape {
+ objectName: "path1183"
+ // Scale 0.16, 0.16 Translate 0, 0
+ transform: Scale { xScale: 0.16; yScale: 0.16 }
+ ShapePath {
+ objectName: "svg_path:path1183"
+ // boundingRect: 26.3377, 125.717 747.693x529.001
+ strokeColor: "#000000"
+ strokeWidth: 6.25
+ fillColor: "transparent"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 484.662 125.721 C 465.46 125.608 445.84 128.039 426.375 133.313 C 322.563 161.438 297.875 269.688 292.625 271.438 C 287.313 273.25 249.5 248.563 194.063 289.938 C 138.625 331.313 151.063 384.875 146.563 389.375 C 138.625 397.313 111.375 386.75 74.5 407.813 C 44.2186 425.116 32.5285 450.433 29.2656 467.994 C 29.2126 467.942 28.9375 467.688 28.9375 467.688 C 28.9375 467.688 14.3125 518.125 54.4375 549.25 C 121.125 600.875 219.75 580.25 219.75 580.25 C 219.75 580.25 251.25 616 310.313 623.375 C 372.813 631.313 412.813 607.875 412.813 607.875 C 412.813 607.875 426.125 632.313 479.813 648.313 C 522.563 661.063 580.313 654.125 613.813 633.313 C 649.875 611 657.875 580.313 657.875 580.313 C 657.875 580.313 710.375 578.188 740.563 558.313 C 770.74 538.421 773.978 508.955 773.949 496.918 C 774.565 484.395 772.045 461.952 753.688 432.438 C 724.688 385.813 674.5 387.563 671.875 384.063 C 669.188 380.563 687.25 326.375 663.938 245.938 C 643.219 174.438 567.871 126.207 484.662 125.721 " }
+ }
+ }
+ } // END svg1047 type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/DemoShape.qml b/examples/quick/quickshapes/weatherforecast/DemoShape.qml
new file mode 100644
index 0000000000..e0719d39f9
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/DemoShape.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Shape {
+ id: item
+ preferredRendererType: mainWindow.preferredRendererType
+ property string svg_id
+ containsMode: Shape.FillContains
+}
diff --git a/examples/quick/quickshapes/weatherforecast/Europe.qml b/examples/quick/quickshapes/weatherforecast/Europe.qml
new file mode 100644
index 0000000000..20a804c28b
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/Europe.qml
@@ -0,0 +1,176 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+Europe_generated {
+ id: map
+ property int containsMode: Shape.FillContains
+
+ property bool zoomedIn: false
+ signal zoomTo(Item child, string name, var childRect, var textRect)
+
+
+property var lookupTable: {
+ "fi" : "Finland"
+ ,"se" : "Sweden"
+ ,"dk" : "Denmark"
+ ,"gb-nir" : "Northern Ireland"
+ ,"gb-main" : "Great Britain"
+ ,"ie" : "Ireland"
+ ,"nl" : "The Netherlands"
+ ,"be" : "Belgium"
+ ,"lu" : "Luxembourg"
+ ,"de" : "Germany"
+ ,"fr" : "France"
+ ,"es" : "Spain"
+ ,"pt" : "Portugal"
+ ,"at" : "Austria"
+ ,"it" : "Italy"
+ ,"gr" : "Greece"
+ ,"ee" : "Estonia"
+ ,"lv" : "Latvia"
+ ,"lt" : "Lithuania"
+ ,"pl" : "Poland"
+ ,"cz" : "Czechia"
+ ,"sk" : "Slovakia"
+ ,"si" : "Slovenia"
+ ,"hu" : "Hungary"
+ ,"ro" : "Romania"
+ ,"bg" : "Bulgaria"
+ ,"cy" : "Cyprus"
+ ,"mt" : "Malta"
+ ,"hr" : "Croatia"
+ ,"ba" : "Bosnia and Herzegovina"
+ ,"me" : "Montenegro"
+ ,"rs" : "Serbia"
+ ,"rs-km" : "Kosovo"
+ ,"mk" : "North Makedonia"
+ ,"al" : "Albania"
+ ,"is" : "Iceland"
+ ,"by" : "Belarus"
+ ,"no" : "Norway"
+ ,"ua" : "Ukraine"
+ ,"ch" : "Switzerland"
+ ,"md" : "Moldova"
+ ,"ad" : "Andorra"
+ ,"mc" : "Monaco"
+ ,"li" : "Liechtenstein"
+ ,"ru-kgd" : "Kaliningrad"
+ ,"im" : "Isle of Man"
+ ,"fo" : "Faroe Islands"
+ }
+
+ property var overrideRects: {
+ "fi": {x: 6616, y: 1996, width: 1920, height: 800, rotation: 65},
+ "no": {x: 4794, y: 1967, width: 3360, height: 680, rotation: -65},
+ "se": {x: 5739, y: 2560, width: 1920, height: 800, rotation: -80},
+ "it": {x: 5734, y: 6483, width: 1680, height: 440, rotation: 45},
+ "pt": {x: 3105, y: 6783, width: 960, height: 320, rotation: -70},
+ "md": {x: 8294, y: 5472, width: 480, height: 200, rotation: 45},
+ "rs": {x: 7233, y: 6168, width: 840, height: 320, rotation: 45},
+ "gb-main": {x: 3943, y: 3998, width: 1800, height: 800, rotation: 75},
+ "ba": {x: 6570, y: 6289, width: 1440, height: 200, rotation: 0},
+ "al": {x: 7312, y: 6830, width: 600, height: 200, rotation: 75},
+ "be": {x: 5237, y: 5126, width: 600, height: 200, rotation: 40},
+ "hr": {x: 6705, y: 6053, width: 840, height: 200, rotation: 0},
+ "at": {x: 6191, y: 5556, width: 1080, height: 440, rotation: -25},
+ "cy": {x: 9509, y: 7497, width: 480, height: 200, rotation: -35},
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+
+ property Item currentChild: null
+ property color prevColor
+ property color selectedColor: "#dbd6c8"
+
+ acceptedButtons: Qt.LeftButton
+
+ function traverseChildren(item, x, y) {
+ let p = item.mapFromItem(mouseArea, x, y)
+ if (item.objectName) {
+ if (item.contains(p))
+ return item;
+ }
+ for (var i = 0; i < item.children.length; i++) {
+ let r = traverseChildren(item.children[i], x, y)
+ if (r)
+ return r;
+ }
+ return null;
+ }
+
+ function findChild(x, y) {
+ return traverseChildren(map, x, y)
+ }
+
+ function findMatchingPath(node) {
+ if (!node)
+ return null
+ let pathName = "svg_path:" + node.objectName
+ for (var i = 0; i < node.data.length; i++) {
+ let child = node.data[i]
+ if (child.objectName === pathName)
+ return child
+ }
+ return null
+ }
+
+ onPressed: (mouse)=> {
+ if (!map.zoomedIn) {
+ currentChild = findChild(mouse.x, mouse.y)
+ let path = findMatchingPath(currentChild)
+ if (path) {
+ prevColor = path.fillColor
+ path.fillColor = selectedColor
+ currentChild.z = 1
+ }
+ }
+ }
+
+ onPositionChanged: (mouse)=> {
+ if (currentChild) {
+ let localPos = currentChild.mapFromItem(mouseArea, mouse.x, mouse.y)
+ let path = findMatchingPath(currentChild)
+ if (path) {
+ if (currentChild.contains(localPos)) {
+ path.fillColor = selectedColor
+ } else {
+ path.fillColor = prevColor
+ }
+ }
+ }
+ }
+
+ onReleased: (mouse) => {
+ if (map.zoomedIn) {
+ zoomTo(null, null, null, null)
+ zoomedIn = false
+ if (currentChild) {
+ let path = findMatchingPath(currentChild)
+ if (path)
+ path.fillColor = prevColor
+ currentChild.z = 0
+ currentChild = null
+ }
+ } else if (currentChild) {
+ let localPos = currentChild.mapFromItem(mouseArea, mouse.x, mouse.y)
+ if (currentChild.contains(localPos)) {
+ let br = currentChild.boundingRect
+ let rot = 0
+ let or = overrideRects[currentChild.objectName]
+ if (or) {
+ rot = or.rotation
+ let r = currentChild.mapToItem(map, or.x, or.y, or.width, or.height)
+ or = {x: r.x, y: r.y, width: r.width, height: r.height, rotation: rot}
+ }
+ let rect = currentChild.mapToItem(map, br)
+ zoomTo(currentChild, lookupTable[currentChild.objectName], rect, or )
+ zoomedIn = true
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/weatherforecast/Europe_generated.qml b/examples/quick/quickshapes/weatherforecast/Europe_generated.qml
new file mode 100644
index 0000000000..4f03b87e37
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/Europe_generated.qml
@@ -0,0 +1,647 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// Copyright (C) 2010 Kolja21
+// SPDX-License-Identifier: CC-BY-3.0
+// Generated from: assets/Europe.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 1754, 161 6480.48x6957.59
+ // size 443x455
+ implicitWidth: 443
+ implicitHeight: 455
+ // START Europakarte type: DOC 0
+ Item { // structure node
+ transform: [
+ Translate { x: -1754; y: -161 },
+ Scale { xScale: width / 6480.48; yScale: height / 6957.59 }
+ ]
+ objectName: "Europakarte"
+ // skipping DEFS "defs"
+ // START countries type: G 1
+ Item { // structure node
+ objectName: "countries"
+ // Translate -1630.41, -991.638
+ x: -1630.41
+ y: -991.638
+ // PATH visit fi count: 485
+ DemoShape {
+ objectName: "fi"
+ ShapePath {
+ objectName: "svg_path:fi"
+ // boundingRect: 6920, 1421.37 1086.48x1971.11
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7814.3 3152.63 C 7822.9 3119.67 7850.13 3085.28 7867.33 3056.61 C 7911.75 2977.79 7938.98 2887.5 7969.08 2802.94 C 7992.01 2741.32 7999.18 2725.55 8002.04 2659.63 C 8003.48 2649.59 8009.21 2638.13 8004.91 2628.1 C 7993.44 2595.13 7865.89 2547.84 7832.93 2524.91 C 7828.63 2522.04 7880.22 2443.22 7844.39 2415.99 C 7831.5 2405.96 7804.27 2415.99 7795.67 2401.66 C 7758.4 2337.16 7804.27 2367.26 7792.8 2348.63 C 7775.6 2322.83 7756.97 2342.9 7735.47 2317.1 C 7716.84 2295.6 7729.74 2294.17 7739.77 2274.1 C 7745.51 2261.21 7711.11 2261.21 7711.11 2255.47 C 7712.54 2245.44 7712.54 2193.85 7712.54 2192.41 C 7682.45 2166.62 7718.28 2182.38 7726.88 2172.35 C 7732.61 2163.75 7696.78 2112.16 7692.48 2106.42 C 7668.12 2073.46 7640.89 2034.77 7612.22 2006.1 C 7607.92 2001.8 7584.99 1984.6 7580.69 1976.01 C 7559.19 1935.88 7627.99 1834.12 7600.76 1809.76 C 7593.59 1801.16 7580.69 1801.16 7572.09 1795.43 C 7556.33 1783.96 7549.16 1763.9 7533.4 1753.86 C 7531.96 1753.86 7483.24 1746.7 7481.8 1746.7 C 7473.2 1739.53 7470.34 1728.07 7464.61 1718.04 C 7433.08 1666.44 7470.34 1670.74 7454.57 1633.48 C 7447.41 1616.28 7420.18 1632.05 7425.91 1622.01 C 7433.08 1609.12 7443.11 1600.52 7451.71 1589.05 C 7405.85 1567.55 7466.04 1533.16 7447.41 1501.63 C 7418.74 1451.47 7374.32 1470.1 7337.05 1429.97 L 7335.62 1422.8 C 7334.19 1422.8 7332.75 1422.8 7331.32 1424.24 C 7331.32 1422.8 7329.89 1422.8 7329.89 1421.37 L 7329.89 1424.24 C 7301.22 1429.97 7306.96 1462.93 7282.59 1470.1 C 7261.1 1475.83 7269.7 1445.73 7245.33 1472.96 C 7242.47 1477.26 7245.33 1484.43 7243.9 1490.16 C 7242.47 1491.6 7226.7 1521.69 7226.7 1523.13 C 7222.4 1534.59 7231 1571.85 7232.43 1581.89 C 7232.43 1586.18 7229.57 1589.05 7229.57 1593.35 C 7229.57 1601.95 7231 1609.12 7232.43 1617.71 C 7236.73 1629.18 7246.76 1637.78 7248.2 1649.24 C 7255.36 1699.4 7242.47 1665.01 7225.27 1680.77 C 7205.2 1700.84 7223.83 1706.57 7218.1 1730.93 C 7216.67 1736.67 7210.94 1739.53 7209.5 1745.27 C 7209.5 1746.7 7190.87 1729.5 7188 1728.07 C 7183.71 1725.2 7130.68 1706.57 7126.38 1712.3 C 7120.65 1718.04 7124.95 1729.5 7120.65 1735.23 C 7103.45 1765.33 7080.52 1746.7 7056.15 1751 C 7048.99 1751 7041.82 1755.3 7036.09 1753.86 L 7030.36 1749.57 C 7027.49 1743.83 7027.49 1735.23 7024.62 1729.5 C 7014.59 1712.3 6981.63 1667.87 6964.43 1657.84 C 6927.17 1636.35 6925.74 1666.44 6928.6 1689.37 L 6920 1719.47 C 6920 1719.47 6921.44 1719.47 6921.44 1719.47 L 6920 1720.9 L 6925.74 1728.07 C 6924.3 1730.93 6918.57 1732.37 6925.74 1736.67 C 6950.1 1755.3 6991.66 1789.69 7017.46 1801.16 C 7047.55 1814.06 7071.92 1802.59 7099.15 1829.82 C 7104.88 1834.12 7100.58 1847.02 7106.31 1849.89 C 7110.61 1851.32 7113.48 1844.15 7116.35 1845.59 C 7127.81 1849.89 7132.11 1864.22 7140.71 1872.82 C 7143.58 1875.68 7152.18 1875.68 7150.74 1879.98 C 7134.98 1910.08 7152.18 1911.51 7156.48 1945.91 C 7157.91 1951.64 7152.18 1957.37 7152.18 1963.11 C 7150.74 1964.54 7150.74 1964.54 7150.74 1964.54 C 7150.74 1978.87 7172.24 1967.41 7177.97 1973.14 C 7196.6 1987.47 7166.51 2007.54 7175.11 2021.87 C 7183.71 2041.93 7218.1 2060.56 7220.97 2083.49 C 7222.4 2100.69 7209.5 2155.15 7210.94 2162.32 C 7223.83 2208.18 7255.36 2223.94 7272.56 2261.21 C 7278.29 2251.17 7285.46 2242.57 7305.52 2254.04 C 7315.56 2259.77 7309.82 2279.84 7319.86 2285.57 C 7328.45 2291.3 7339.92 2279.84 7349.95 2282.7 C 7354.25 2282.7 7354.25 2291.3 7357.12 2294.17 C 7365.72 2298.47 7365.72 2281.27 7374.32 2295.6 C 7390.08 2328.56 7378.62 2357.23 7384.35 2371.56 C 7388.65 2384.46 7427.34 2355.79 7410.15 2397.36 C 7405.85 2407.39 7391.51 2404.52 7382.92 2410.25 C 7374.32 2415.99 7364.28 2420.29 7358.55 2428.89 C 7355.69 2431.75 7359.98 2438.92 7357.12 2441.78 C 7354.25 2444.65 7347.09 2437.49 7345.65 2440.35 C 7341.35 2450.38 7339.92 2463.28 7339.92 2474.75 C 7339.92 2481.91 7344.22 2489.08 7341.35 2496.24 C 7339.92 2509.14 7322.72 2514.88 7319.86 2526.34 C 7316.99 2534.94 7324.16 2544.97 7321.29 2552.14 C 7314.12 2567.9 7298.36 2565.04 7296.93 2586.53 C 7295.49 2590.83 7304.09 2595.13 7304.09 2600.87 C 7299.79 2628.1 7261.1 2623.8 7259.66 2648.16 C 7258.23 2658.19 7251.06 2683.99 7249.63 2685.42 C 7243.9 2685.42 7236.73 2671.09 7231 2675.39 C 7218.1 2686.86 7238.17 2715.52 7225.27 2724.12 C 7220.97 2726.98 7212.37 2708.35 7213.8 2714.09 C 7213.8 2721.25 7223.83 2724.12 7226.7 2731.28 C 7239.6 2778.58 7222.4 2747.05 7202.34 2769.98 C 7198.04 2775.71 7206.64 2790.04 7200.9 2790.04 C 7192.3 2788.61 7196.6 2771.41 7189.44 2769.98 C 7177.97 2767.11 7163.64 2771.41 7157.91 2781.44 C 7134.98 2820.14 7183.71 2792.91 7180.84 2815.84 C 7177.97 2830.17 7140.71 2854.54 7139.28 2870.3 C 7132.11 2964.89 7140.71 2867.43 7155.04 2903.26 C 7156.48 2904.7 7142.14 2930.49 7153.61 2937.66 C 7155.04 2939.09 7157.91 2931.93 7159.34 2933.36 C 7162.21 2937.66 7157.91 2947.69 7163.64 2949.13 C 7166.51 2950.56 7160.77 2936.23 7165.07 2936.23 C 7169.37 2934.79 7170.81 2940.53 7172.24 2944.83 C 7180.84 2967.76 7170.81 3005.02 7175.11 3013.62 C 7177.97 3019.35 7186.57 3016.48 7190.87 3020.78 C 7196.6 3027.95 7199.47 3036.55 7203.77 3043.71 C 7205.2 3046.58 7202.34 3050.88 7203.77 3053.75 C 7206.64 3058.05 7212.37 3058.05 7215.23 3060.91 C 7216.67 3062.35 7209.5 3060.91 7209.5 3062.35 C 7208.07 3065.21 7210.94 3068.08 7212.37 3070.94 C 7219.53 3079.54 7235.3 3083.84 7233.87 3093.87 C 7232.43 3102.47 7209.5 3095.31 7208.07 3103.91 C 7205.2 3115.37 7222.4 3122.54 7225.27 3132.57 C 7226.7 3136.87 7219.53 3141.17 7220.97 3146.9 C 7222.4 3149.77 7231 3149.77 7231 3154.07 C 7229.57 3172.7 7215.23 3152.63 7220.97 3168.4 C 7222.4 3174.13 7229.57 3178.43 7228.13 3182.73 C 7226.7 3188.46 7212.37 3181.3 7210.94 3185.6 C 7209.5 3222.86 7225.27 3227.16 7236.73 3242.92 C 7239.6 3245.79 7225.27 3248.66 7228.13 3252.96 C 7229.57 3260.12 7232.43 3265.85 7236.73 3271.59 C 7268.26 3313.15 7243.9 3270.15 7273.99 3264.42 C 7279.73 3264.42 7268.26 3280.19 7273.99 3284.49 C 7282.59 3288.79 7292.63 3281.62 7302.66 3281.62 C 7311.26 3283.05 7315.56 3297.38 7322.72 3297.38 C 7329.89 3297.38 7332.75 3288.79 7338.49 3288.79 C 7341.35 3287.35 7338.49 3293.08 7339.92 3294.52 C 7344.22 3295.95 7347.09 3294.52 7351.39 3293.08 C 7352.82 3291.65 7352.82 3283.05 7354.25 3285.92 C 7368.58 3317.45 7345.65 3323.18 7351.39 3324.61 C 7354.25 3324.61 7387.21 3300.25 7391.51 3297.38 C 7392.95 3295.95 7395.81 3288.79 7397.25 3287.35 C 7390.08 3308.85 7371.45 3333.21 7405.85 3346.11 C 7411.58 3348.98 7423.04 3336.08 7425.91 3341.81 C 7428.78 3350.41 7405.85 3363.31 7414.44 3366.18 C 7423.04 3370.48 7421.61 3346.11 7430.21 3344.68 C 7437.38 3343.25 7431.64 3359.01 7428.78 3364.74 C 7424.48 3371.91 7415.88 3374.78 7408.71 3380.51 C 7405.85 3384.81 7394.38 3394.84 7398.68 3391.97 C 7443.11 3374.78 7425.91 3346.11 7448.84 3331.78 C 7456.01 3327.48 7437.38 3350.41 7444.54 3354.71 C 7454.57 3360.44 7467.47 3353.28 7477.5 3347.55 C 7484.67 3344.68 7487.54 3333.21 7493.27 3328.91 C 7501.87 3321.75 7540.56 3317.45 7544.86 3311.72 C 7549.16 3307.42 7534.83 3300.25 7539.13 3295.95 C 7546.3 3290.22 7557.76 3294.52 7567.79 3293.08 C 7570.66 3293.08 7576.39 3294.52 7577.83 3291.65 C 7580.69 3287.35 7574.96 3281.62 7576.39 3278.75 C 7577.83 3275.89 7577.83 3285.92 7580.69 3287.35 C 7582.13 3288.79 7619.39 3265.85 7622.25 3261.56 C 7623.69 3260.12 7616.52 3231.46 7630.85 3234.33 C 7633.72 3234.33 7686.75 3283.05 7673.85 3230.03 C 7670.98 3219.99 7645.18 3218.56 7649.48 3207.1 C 7652.35 3197.06 7670.98 3202.8 7681.01 3205.66 C 7686.75 3207.1 7686.75 3222.86 7692.48 3219.99 C 7706.81 3209.96 7685.31 3161.23 7702.51 3171.27 C 7716.84 3181.3 7678.15 3217.13 7722.58 3202.8 C 7791.37 3179.86 7732.61 3194.2 7746.94 3172.7 C 7748.37 3171.27 7749.81 3175.57 7752.67 3175.57 C 7755.54 3175.57 7759.84 3175.57 7761.27 3172.7 C 7762.7 3169.83 7756.97 3159.8 7761.27 3159.8 C 7762.7 3159.8 7785.63 3179.86 7792.8 3177 C 7802.83 3174.13 7810 3165.53 7815.73 3158.37 C 7817.16 3158.37 7817.16 3156.93 7817.16 3155.5 L 7814.3 3152.63 " }
+ }
+ }
+ // PATH visit se count: 926
+ DemoShape {
+ objectName: "se"
+ ShapePath {
+ objectName: "svg_path:se"
+ // boundingRect: 6292.28, 1712.3 980.285x2639.89
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6447.06 4352.19 C 6449.92 4343.6 6444.19 4339.3 6435.59 4335 L 6429.86 4320.66 C 6441.33 4327.83 6459.96 4332.13 6454.22 4345.03 C 6454.22 4347.89 6451.36 4350.76 6447.06 4352.19 M 6452.79 4286.27 C 6472.86 4263.34 6449.92 4264.77 6448.49 4236.11 C 6448.49 4227.51 6457.09 4221.78 6458.52 4213.18 C 6458.52 4210.31 6452.79 4207.44 6451.36 4204.58 C 6432.73 4201.71 6408.36 4195.98 6392.6 4207.44 C 6408.36 4187.38 6435.59 4193.11 6457.09 4197.41 C 6445.63 4178.78 6438.46 4164.45 6441.33 4161.58 C 6447.06 4148.68 6467.12 4177.35 6480.02 4171.62 C 6491.49 4167.32 6451.36 4150.12 6457.09 4138.65 C 6461.39 4128.62 6481.45 4140.09 6485.75 4131.49 C 6492.92 4121.45 6488.62 4107.12 6482.89 4097.09 C 6480.02 4091.36 6471.42 4095.66 6465.69 4094.22 C 6454.22 4089.93 6457.09 4069.86 6448.49 4059.83 C 6442.76 4052.66 6431.29 4052.66 6425.56 4045.5 C 6422.69 4039.76 6426.99 4032.6 6424.13 4026.87 C 6421.26 4019.7 6404.06 3996.77 6404.06 3995.34 C 6404.06 3992.47 6409.8 3991.04 6408.36 3989.6 C 6406.93 3986.74 6401.2 3988.17 6398.33 3986.74 C 6391.16 3981 6385.43 3972.41 6381.13 3965.24 C 6373.97 3952.34 6382.57 3917.95 6372.53 3902.18 C 6369.67 3897.88 6359.63 3903.61 6355.34 3899.31 C 6351.04 3893.58 6352.47 3883.55 6352.47 3874.95 C 6349.6 3853.45 6389.73 3803.29 6371.1 3780.36 C 6345.3 3747.4 6359.63 3836.25 6305.17 3781.79 C 6296.58 3773.2 6310.91 3747.4 6310.91 3737.37 C 6309.47 3720.17 6292.28 3655.68 6292.28 3655.68 C 6298.01 3648.51 6312.34 3661.41 6320.94 3659.98 C 6325.24 3674.31 6330.97 3688.64 6343.87 3670.01 C 6375.4 3625.58 6335.27 3568.25 6343.87 3542.46 C 6346.74 3532.42 6361.07 3530.99 6365.37 3522.39 C 6368.23 3516.66 6365.37 3509.49 6362.5 3503.76 C 6362.5 3499.46 6355.34 3498.03 6356.77 3495.16 C 6359.63 3485.13 6405.5 3482.26 6418.39 3449.3 L 6416.96 3407.74 C 6432.73 3326.05 6395.46 3341.81 6388.3 3280.19 C 6386.87 3265.85 6415.53 3273.02 6426.99 3262.99 L 6442.76 3205.66 C 6437.03 3169.83 6382.57 3172.7 6373.97 3139.74 C 6366.8 3109.64 6389.73 3069.51 6386.87 3037.98 C 6384 3016.48 6361.07 2997.85 6359.63 2976.36 C 6358.2 2967.76 6365.37 2957.72 6365.37 2949.13 C 6363.93 2939.09 6358.2 2930.49 6359.63 2920.46 C 6359.63 2911.86 6369.67 2906.13 6369.67 2897.53 C 6369.67 2724.12 6348.17 3025.08 6348.17 2851.67 C 6348.17 2835.91 6368.23 2825.87 6369.67 2810.11 C 6371.1 2804.38 6362.5 2798.64 6363.93 2791.48 C 6368.23 2768.55 6399.76 2719.82 6418.39 2708.35 C 6477.15 2671.09 6515.85 2738.45 6525.88 2671.09 C 6535.91 2600.87 6475.72 2615.2 6484.32 2589.4 C 6497.22 2549.27 6517.28 2509.14 6530.18 2470.45 C 6531.61 2464.72 6538.78 2463.28 6540.21 2457.55 L 6535.91 2404.52 C 6535.91 2403.09 6543.08 2360.09 6543.08 2360.09 L 6528.75 2304.2 C 6534.48 2291.3 6557.41 2304.2 6568.88 2298.47 L 6600.41 2271.24 C 6604.71 2258.34 6586.08 2246.87 6588.94 2233.98 C 6597.54 2208.18 6644.83 2152.29 6653.43 2127.92 C 6669.2 2089.23 6631.94 2086.36 6626.2 2054.83 C 6621.9 2029.03 6637.67 2044.8 6646.27 2034.77 C 6657.73 2020.43 6657.73 2000.37 6660.6 1984.6 C 6662.03 1973.14 6689.26 1922.98 6697.86 1922.98 L 6743.72 1943.04 C 6772.39 1931.58 6736.56 1849.89 6745.16 1838.42 C 6765.22 1812.62 6864.11 1861.35 6877.01 1852.75 C 6924.3 1822.66 6874.14 1825.52 6872.71 1812.62 C 6868.41 1795.43 6892.77 1782.53 6892.77 1765.33 C 6892.77 1755.3 6889.91 1745.27 6885.61 1736.67 C 6881.31 1729.5 6862.68 1725.2 6866.98 1718.04 C 6872.71 1708 6888.47 1715.17 6899.94 1712.3 L 6902.8 1712.3 C 6907.1 1715.17 6914.27 1715.17 6920 1719.47 C 6937.2 1730.93 6914.27 1729.5 6925.74 1736.67 C 6950.1 1755.3 6991.66 1789.69 7017.46 1801.16 C 7047.55 1814.06 7071.92 1802.59 7099.15 1829.82 C 7104.88 1834.12 7100.58 1847.02 7106.31 1849.89 C 7110.61 1851.32 7113.48 1844.15 7116.35 1845.59 C 7127.81 1849.89 7132.11 1864.22 7140.71 1872.82 C 7143.58 1875.68 7152.18 1875.68 7150.74 1879.98 C 7134.98 1910.08 7152.18 1911.51 7156.48 1945.91 C 7157.91 1951.64 7152.18 1957.37 7152.18 1963.11 C 7149.31 1980.31 7170.81 1967.41 7177.97 1973.14 C 7196.6 1987.47 7166.51 2007.54 7175.11 2021.87 C 7183.71 2041.93 7218.1 2060.56 7220.97 2083.49 C 7222.4 2100.69 7209.5 2155.15 7210.94 2162.32 C 7223.83 2208.18 7255.36 2223.94 7272.56 2261.21 C 7268.26 2266.94 7263.96 2274.1 7258.23 2276.97 C 7252.5 2279.84 7248.2 2265.51 7241.03 2266.94 C 7238.17 2268.37 7236.73 2275.54 7233.87 2275.54 C 7222.4 2276.97 7209.5 2266.94 7199.47 2272.67 C 7192.3 2276.97 7203.77 2294.17 7196.6 2298.47 C 7166.51 2317.1 7156.48 2282.7 7134.98 2278.4 C 7132.11 2278.4 7137.84 2287 7137.84 2291.3 C 7137.84 2294.17 7139.28 2297.03 7137.84 2299.9 C 7136.41 2309.93 7127.81 2318.53 7130.68 2327.13 C 7130.68 2328.56 7131.71 2330.54 7130.1 2330.49 C 7123.87 2330.33 7113.48 2327.13 7104.88 2322.83 C 7100.58 2321.4 7096.28 2309.93 7094.85 2314.23 C 7091.98 2330 7130.68 2324.26 7133.54 2340.03 C 7136.41 2348.63 7116.35 2342.9 7107.75 2347.2 C 7106.31 2348.63 7112.05 2348.63 7114.91 2348.63 C 7116.35 2350.06 7132.11 2360.09 7123.51 2367.26 C 7122.08 2368.69 7120.65 2360.09 7117.78 2361.53 C 7116.35 2362.96 7122.08 2368.69 7119.21 2370.13 C 7107.75 2377.29 7074.78 2370.13 7069.05 2391.62 C 7069.05 2395.92 7077.65 2391.62 7080.52 2393.06 C 7090.55 2400.22 7099.15 2410.25 7103.45 2421.72 C 7112.05 2447.52 7069.05 2458.98 7067.62 2487.65 C 7066.19 2522.04 7113.48 2524.91 7124.95 2550.71 C 7126.38 2552.14 7096.28 2589.4 7090.55 2602.3 C 7081.95 2619.5 7079.08 2689.72 7061.89 2692.59 C 7059.02 2692.59 7059.02 2682.56 7056.15 2683.99 C 7046.12 2685.42 7060.45 2708.35 7050.42 2712.65 C 7046.12 2715.52 7047.55 2695.45 7043.25 2698.32 C 7041.82 2698.32 7041.82 2699.75 7041.82 2701.19 L 7040.39 2701.19 C 7037.52 2705.49 7043.25 2714.09 7038.96 2716.95 C 7034.66 2722.69 7017.46 2719.82 7013.16 2728.42 C 7008.86 2734.15 7010.29 2745.62 7003.13 2748.48 C 6994.53 2751.35 6981.63 2735.58 6975.9 2742.75 C 6970.16 2751.35 6991.66 2762.81 6987.36 2772.85 C 6983.06 2780.01 6973.03 2758.51 6967.3 2762.81 C 6960.13 2768.55 6968.73 2780.01 6967.3 2788.61 C 6965.86 2798.64 6964.43 2812.97 6954.4 2818.71 C 6945.8 2823.01 6935.77 2808.67 6925.74 2810.11 C 6920 2810.11 6938.63 2814.41 6940.07 2820.14 C 6941.5 2823.01 6934.33 2824.44 6931.47 2824.44 C 6928.6 2824.44 6935.77 2817.27 6932.9 2814.41 C 6930.03 2812.97 6924.3 2812.97 6922.87 2815.84 C 6921.44 2820.14 6931.47 2824.44 6928.6 2828.74 C 6925.74 2831.61 6917.14 2823.01 6912.84 2827.31 C 6909.97 2830.17 6920 2831.61 6921.44 2835.91 C 6921.44 2837.34 6915.7 2834.47 6914.27 2835.91 C 6911.4 2841.64 6914.27 2851.67 6909.97 2855.97 C 6904.24 2861.7 6882.74 2857.4 6888.47 2861.7 C 6897.07 2868.87 6922.87 2857.4 6920 2867.43 C 6915.7 2878.9 6902.8 2886.07 6895.64 2896.1 C 6892.77 2898.96 6889.91 2904.7 6885.61 2906.13 C 6881.31 2907.56 6872.71 2906.13 6872.71 2901.83 C 6874.14 2897.53 6884.17 2900.4 6885.61 2896.1 C 6889.91 2887.5 6874.14 2898.96 6868.41 2900.4 C 6869.84 2904.7 6872.71 2909 6875.57 2913.3 C 6879.87 2923.33 6887.04 2950.56 6875.57 2947.69 C 6872.71 2947.69 6874.14 2933.36 6871.28 2936.23 C 6866.98 2943.39 6872.71 2954.86 6866.98 2962.02 C 6865.54 2966.32 6858.38 2960.59 6854.08 2960.59 C 6844.04 2962.02 6831.15 2960.59 6822.55 2966.32 C 6799.62 2983.52 6838.31 2990.69 6855.51 3010.75 C 6856.94 3012.18 6849.78 3013.62 6848.34 3012.18 C 6848.34 3010.75 6844.04 3010.75 6844.04 3012.18 C 6842.61 3023.65 6845.48 3035.12 6844.04 3046.58 C 6842.61 3053.75 6836.88 3059.48 6835.45 3066.64 C 6834.01 3075.24 6834.01 3083.84 6836.88 3092.44 C 6836.88 3093.87 6841.18 3089.58 6842.61 3091.01 C 6848.34 3101.04 6864.11 3115.37 6855.51 3123.97 C 6846.91 3132.57 6834.01 3108.21 6822.55 3112.51 C 6789.58 3123.97 6845.48 3154.07 6841.18 3185.6 C 6841.18 3191.33 6826.85 3182.73 6826.85 3187.03 C 6825.41 3194.2 6836.88 3195.63 6838.31 3201.36 C 6839.75 3204.23 6832.58 3202.8 6832.58 3205.66 C 6832.58 3211.39 6832.58 3218.56 6835.45 3224.29 C 6836.88 3230.03 6835.45 3237.19 6838.31 3242.92 C 6839.75 3245.79 6845.48 3240.06 6845.48 3242.92 C 6846.91 3244.36 6839.75 3244.36 6838.31 3245.79 C 6836.88 3248.66 6836.88 3252.96 6838.31 3255.82 C 6841.18 3262.99 6851.21 3267.29 6852.64 3275.89 C 6854.08 3284.49 6845.48 3293.08 6846.91 3301.68 C 6846.91 3304.55 6851.21 3300.25 6854.08 3301.68 C 6858.38 3303.12 6855.51 3313.15 6859.81 3313.15 C 6875.57 3313.15 6905.67 3304.55 6920 3313.15 C 6925.74 3316.02 6920 3326.05 6922.87 3331.78 C 6927.17 3340.38 6935.77 3346.11 6944.37 3351.84 C 6945.8 3353.28 6951.53 3347.55 6952.97 3350.41 C 6955.83 3353.28 6948.67 3359.01 6951.53 3360.44 C 6952.97 3360.44 7026.06 3331.78 6987.36 3374.78 C 6981.63 3381.94 6968.73 3361.88 6960.13 3364.74 C 6952.97 3367.61 6970.16 3373.34 6974.46 3379.07 C 6980.2 3387.67 6981.63 3400.57 6990.23 3406.3 C 6994.53 3407.74 6997.39 3391.97 7003.13 3394.84 C 7010.29 3397.71 7005.99 3410.6 7011.73 3414.9 C 7033.22 3439.27 7048.99 3423.5 7044.69 3462.2 C 7043.25 3476.53 7030.36 3460.77 7028.92 3460.77 C 7021.76 3462.2 7020.32 3470.8 7014.59 3475.1 C 7001.69 3489.43 7014.59 3502.33 6974.46 3520.96 C 6973.03 3522.39 6981.63 3535.29 6981.63 3536.72 C 6978.76 3541.02 6971.6 3533.86 6965.86 3535.29 C 6955.83 3536.72 6945.8 3545.32 6934.33 3545.32 C 6922.87 3545.32 6932.9 3523.82 6932.9 3513.79 C 6934.33 3503.76 6925.74 3505.19 6921.44 3499.46 C 6921.44 3498.03 6922.87 3495.16 6921.44 3492.29 C 6915.7 3483.7 6914.27 3470.8 6904.24 3466.5 C 6897.07 3463.63 6887.04 3470.8 6882.74 3477.96 C 6881.31 3482.26 6894.21 3476.53 6895.64 3479.4 C 6901.37 3489.43 6897.07 3502.33 6902.8 3510.93 C 6907.1 3518.09 6928.6 3516.66 6924.3 3523.82 C 6907.1 3556.79 6865.54 3506.63 6856.94 3509.49 C 6845.48 3512.36 6839.75 3528.12 6826.85 3528.12 C 6818.25 3528.12 6816.81 3509.49 6808.22 3510.93 C 6801.05 3512.36 6808.22 3526.69 6803.92 3533.86 C 6802.48 3536.72 6798.18 3530.99 6793.88 3530.99 C 6782.42 3532.42 6769.52 3532.42 6759.49 3538.16 C 6756.62 3539.59 6763.79 3543.89 6766.65 3546.76 C 6778.12 3553.92 6792.45 3542.46 6801.05 3539.59 C 6815.38 3538.16 6829.71 3556.79 6842.61 3552.49 C 6848.34 3549.62 6831.15 3532.42 6838.31 3533.86 C 6846.91 3533.86 6849.78 3549.62 6858.38 3553.92 C 6866.98 3556.79 6879.87 3546.76 6887.04 3553.92 C 6891.34 3558.22 6875.57 3566.82 6878.44 3573.99 C 6881.31 3578.28 6892.77 3575.42 6897.07 3571.12 C 6899.94 3568.25 6888.47 3561.09 6891.34 3559.65 C 6898.51 3556.79 6904.24 3565.39 6909.97 3566.82 C 6921.44 3569.69 6931.47 3576.85 6941.5 3572.55 C 6948.67 3569.69 6942.93 3553.92 6948.67 3549.62 C 6955.83 3545.32 6965.86 3553.92 6973.03 3549.62 C 6983.06 3545.32 6984.5 3526.69 6995.96 3525.26 C 7004.56 3523.82 7018.89 3538.16 7014.59 3545.32 C 7007.43 3555.35 6990.23 3545.32 6978.76 3549.62 C 6977.33 3549.62 6981.63 3553.92 6983.06 3555.35 C 6987.36 3563.95 6995.96 3572.55 6995.96 3582.58 C 6997.39 3585.45 6988.79 3581.15 6985.93 3584.02 C 6975.9 3589.75 6964.43 3595.48 6958.7 3605.52 C 6952.97 3615.55 6957.26 3628.45 6952.97 3638.48 C 6950.1 3644.21 6945.8 3616.98 6938.63 3619.85 C 6934.33 3622.71 6945.8 3638.48 6941.5 3635.61 C 6927.17 3629.88 6947.23 3581.15 6924.3 3584.02 C 6924.3 3584.02 6928.6 3647.08 6902.8 3665.71 C 6894.21 3671.44 6881.31 3667.14 6871.28 3668.57 C 6866.98 3670.01 6882.74 3672.87 6882.74 3677.17 C 6888.47 3694.37 6866.98 3682.91 6864.11 3685.77 C 6861.24 3687.21 6872.71 3687.21 6872.71 3691.51 C 6874.14 3692.94 6868.41 3690.07 6865.54 3691.51 C 6864.11 3692.94 6862.68 3695.8 6861.24 3695.8 C 6858.38 3695.8 6785.29 3685.77 6791.02 3698.67 C 6792.45 3701.54 6796.75 3701.54 6801.05 3701.54 C 6802.48 3701.54 6803.92 3697.24 6805.35 3697.24 C 6808.22 3698.67 6808.22 3707.27 6812.52 3705.84 C 6815.38 3705.84 6811.08 3697.24 6815.38 3697.24 C 6838.31 3691.51 6856.94 3697.24 6866.98 3720.17 C 6866.98 3723.03 6859.81 3718.74 6858.38 3720.17 C 6852.64 3725.9 6865.54 3734.5 6849.78 3734.5 C 6838.31 3734.5 6823.98 3718.74 6816.81 3727.33 C 6809.65 3735.93 6838.31 3731.63 6846.91 3737.37 C 6852.64 3741.67 6864.11 3767.46 6859.81 3774.63 C 6856.94 3780.36 6842.61 3770.33 6841.18 3776.06 C 6839.75 3783.23 6871.28 3821.92 6861.24 3834.82 C 6854.08 3843.42 6836.88 3830.52 6826.85 3834.82 C 6822.55 3834.82 6828.28 3840.55 6831.15 3843.42 C 6836.88 3850.59 6859.81 3857.75 6859.81 3869.22 C 6859.81 3879.25 6841.18 3884.98 6842.61 3896.45 C 6842.61 3905.05 6856.94 3905.05 6862.68 3912.21 C 6864.11 3915.08 6856.94 3915.08 6855.51 3916.51 C 6849.78 3926.54 6844.04 3938.01 6842.61 3949.47 C 6839.75 3958.07 6859.81 3989.6 6858.38 3992.47 C 6855.51 3995.34 6848.34 3989.6 6844.04 3993.9 C 6832.58 4005.37 6856.94 4028.3 6852.64 4046.93 C 6852.64 4051.23 6841.18 4046.93 6839.75 4051.23 C 6836.88 4055.53 6846.91 4061.26 6844.04 4065.56 C 6828.28 4112.86 6835.45 4031.17 6828.28 4079.89 C 6823.98 4109.99 6822.55 4157.28 6798.18 4180.21 C 6796.75 4181.65 6792.45 4180.21 6791.02 4177.35 C 6791.02 4174.48 6795.32 4171.62 6793.88 4168.75 C 6793.88 4164.45 6769.52 4167.32 6769.52 4167.32 C 6768.09 4165.88 6772.39 4160.15 6770.95 4160.15 C 6756.62 4161.58 6755.19 4168.75 6748.02 4175.91 C 6735.12 4185.95 6743.72 4164.45 6739.42 4165.88 C 6729.39 4171.62 6716.49 4174.48 6713.63 4174.48 C 6706.46 4175.91 6700.73 4173.05 6693.56 4173.05 C 6687.83 4173.05 6679.23 4168.75 6677.8 4174.48 C 6674.93 4183.08 6690.7 4191.68 6686.4 4198.85 C 6679.23 4206.01 6666.33 4197.41 6656.3 4200.28 C 6647.7 4203.15 6646.27 4213.18 6641.97 4220.34 C 6604.71 4274.8 6652 4257.61 6650.57 4294.87 C 6649.13 4306.33 6641.97 4316.37 6633.37 4324.96 C 6626.2 4332.13 6603.27 4317.8 6598.97 4317.8 C 6564.58 4314.93 6574.61 4339.3 6538.78 4339.3 C 6527.32 4339.3 6517.28 4332.13 6507.25 4330.7 C 6501.52 4330.7 6492.92 4335 6487.19 4332.13 C 6477.15 4326.4 6502.95 4322.1 6502.95 4322.1 C 6502.95 4313.5 6494.35 4306.33 6495.79 4297.73 C 6495.79 4289.14 6507.25 4284.84 6510.12 4277.67 C 6510.12 4277.67 6484.32 4241.84 6464.26 4210.31 C 6462.82 4216.04 6455.66 4221.78 6455.66 4228.94 C 6457.09 4259.04 6485.75 4256.17 6454.22 4284.84 C 6454.22 4286.27 6454.22 4286.27 6452.79 4286.27 M 6901.37 3930.84 C 6904.24 3926.54 6909.97 3927.98 6914.27 3926.54 C 6904.24 3949.47 6884.17 4048.36 6879.87 4078.46 C 6878.44 4085.63 6884.17 4141.52 6865.54 4142.95 C 6862.68 4142.95 6856.94 4078.46 6856.94 4074.16 C 6859.81 4032.6 6888.47 4011.1 6895.64 3972.41 C 6898.51 3955.21 6888.47 3948.04 6901.37 3930.84 M 7094.85 3892.15 C 7094.85 3900.75 7083.38 3902.18 7077.65 3907.91 C 7059.02 3929.41 7091.98 3913.65 7064.75 3935.14 C 7043.25 3950.91 7056.15 3950.91 7044.69 3972.41 C 7040.39 3979.57 7030.36 4001.07 7026.06 3992.47 C 7021.76 3983.87 7033.22 3972.41 7037.52 3962.37 C 7036.09 3963.81 7034.66 3966.67 7033.22 3966.67 C 7031.79 3966.67 7031.79 3962.37 7031.79 3960.94 C 7030.36 3950.91 7016.02 3933.71 7016.02 3930.84 C 7016.02 3922.24 7021.76 3913.65 7021.76 3905.05 C 7020.32 3895.01 7011.73 3887.85 7011.73 3877.82 C 7010.29 3870.65 7053.29 3801.86 7059.02 3798.99 C 7069.05 3794.69 7080.52 3798.99 7090.55 3797.56 C 7106.31 3820.49 7087.68 3823.36 7074.78 3853.45 C 7059.02 3890.72 7094.85 3879.25 7094.85 3892.15 M 7044.69 2716.95 C 7044.69 2719.82 7044.69 2715.52 7043.25 2711.22 C 7044.69 2712.65 7044.69 2715.52 7044.69 2716.95 M 6404.06 4257.61 L 6402.63 4253.31 C 6402.63 4244.71 6399.76 4238.97 6395.46 4236.11 L 6394.03 4230.38 C 6396.9 4230.38 6399.76 4231.81 6401.2 4234.67 C 6402.63 4240.41 6404.06 4249.01 6404.06 4257.61 " }
+ }
+ }
+ // PATH visit dk count: 398
+ DemoShape {
+ objectName: "dk"
+ ShapePath {
+ objectName: "svg_path:dk"
+ // boundingRect: 5979.04, 3895.01 481.94x581.976
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6123.16 4443.92 C 6104.53 4441.05 6085.9 4431.02 6067.27 4428.15 C 6062.97 4428.15 6045.77 4429.59 6035.74 4428.15 C 6035.74 4420.99 6035.74 4413.82 6037.17 4405.22 C 6037.17 4398.06 6048.64 4389.46 6042.91 4383.72 C 6048.64 4376.56 6040.04 4365.09 6040.04 4355.06 C 6038.61 4346.46 6042.91 4333.56 6037.17 4327.83 C 6024.27 4314.93 5987.01 4329.26 5979.85 4303.47 C 5975.55 4287.7 5989.88 4270.5 5989.88 4251.87 L 5992.74 4254.74 C 6037.17 4276.24 6008.51 4218.91 6005.64 4213.18 C 6004.21 4210.31 6002.78 4201.71 5998.48 4204.58 C 5991.31 4207.44 5992.74 4220.34 5985.58 4221.78 C 5981.28 4223.21 5984.15 4211.74 5984.15 4207.44 C 5985.58 4197.41 5988.45 4187.38 5988.45 4178.78 C 5989.88 4170.18 5981.28 4158.72 5988.45 4152.98 C 5989.88 4151.55 5992.74 4150.12 5994.18 4150.12 C 5992.74 4147.25 5987.01 4085.63 6001.34 4091.36 C 6009.94 4094.22 6004.21 4108.56 6008.51 4115.72 C 6021.41 4145.82 6083.03 4071.29 6085.9 4071.29 C 6116 4062.69 6081.6 4109.99 6087.33 4117.16 C 6093.07 4124.32 6104.53 4118.59 6114.56 4115.72 C 6111.7 4115.72 6110.26 4115.72 6110.26 4114.29 C 6107.4 4107.12 6116 4098.52 6111.7 4092.79 C 6108.83 4088.49 6100.23 4099.96 6098.8 4095.66 C 6095.93 4089.93 6107.4 4085.63 6107.4 4079.89 C 6108.83 4046.93 6087.33 4056.96 6114.56 4036.9 C 6120.3 4031.17 6137.49 4032.6 6141.79 4036.9 C 6144.66 4039.76 6137.49 4052.66 6140.36 4049.8 C 6146.09 4044.06 6146.09 4031.17 6153.26 4026.87 C 6158.99 4024 6166.16 4026.87 6171.89 4026.87 C 6179.06 4031.17 6213.45 4039.76 6213.45 4039.76 C 6216.32 4048.36 6209.15 4058.4 6210.59 4068.43 C 6210.59 4075.59 6222.05 4082.76 6219.18 4089.93 C 6216.32 4095.66 6206.29 4088.49 6200.55 4089.93 C 6191.95 4091.36 6222.05 4097.09 6220.62 4105.69 C 6220.62 4108.56 6219.18 4109.99 6219.18 4111.42 C 6224.92 4112.86 6226.35 4125.75 6233.52 4127.19 C 6250.71 4131.49 6276.51 4107.12 6283.68 4140.09 C 6286.54 4151.55 6252.15 4208.88 6240.68 4203.15 C 6230.65 4197.41 6230.65 4178.78 6219.18 4174.48 C 6206.29 4170.18 6207.72 4244.71 6203.42 4253.31 C 6196.25 4271.94 6184.79 4284.84 6164.72 4284.84 C 6154.69 4284.84 6133.19 4269.07 6136.06 4279.1 C 6140.36 4289.13 6163.29 4283.4 6169.02 4294.87 L 6169.02 4299.17 L 6171.89 4299.17 C 6177.62 4299.17 6179.06 4309.2 6183.36 4309.2 C 6199.12 4304.9 6210.59 4280.54 6226.35 4286.27 C 6234.95 4290.57 6239.25 4303.47 6249.28 4304.9 C 6255.01 4306.33 6256.45 4296.3 6260.75 4293.43 C 6286.54 4346.46 6255.01 4300.6 6265.05 4320.66 C 6273.64 4336.43 6300.88 4402.36 6247.85 4396.62 C 6236.38 4395.19 6226.35 4386.59 6214.89 4383.72 C 6210.59 4383.72 6203.42 4390.89 6199.12 4386.59 C 6187.65 4377.99 6227.78 4380.86 6204.85 4367.96 C 6169.02 4349.33 6204.85 4380.86 6193.39 4380.86 C 6189.09 4380.86 6179.06 4337.86 6179.06 4336.43 C 6174.76 4326.4 6160.42 4320.66 6157.56 4309.2 C 6157.56 4307.77 6158.99 4307.77 6158.99 4307.77 C 6147.53 4312.07 6131.76 4314.93 6131.76 4316.37 C 6128.9 4322.1 6146.09 4316.37 6148.96 4322.1 C 6150.39 4326.4 6140.36 4327.83 6140.36 4332.13 C 6138.93 4340.73 6140.36 4349.33 6143.23 4356.49 C 6144.66 4360.79 6151.83 4362.23 6151.83 4366.53 C 6151.83 4382.29 6126.03 4388.02 6123.16 4403.79 C 6123.16 4409.52 6134.63 4400.92 6138.93 4403.79 C 6148.96 4410.95 6157.56 4420.99 6158.99 4432.45 C 6160.42 4438.18 6148.96 4420.99 6143.23 4422.42 C 6136.06 4425.29 6127.46 4438.18 6123.16 4443.92 M 6394.03 4206.01 L 6394.03 4207.44 C 6409.8 4195.98 6434.16 4201.71 6451.36 4204.58 C 6452.79 4207.44 6458.52 4210.31 6458.52 4213.18 C 6457.09 4221.78 6448.49 4227.51 6448.49 4236.11 C 6449.92 4264.77 6472.86 4263.34 6452.79 4286.27 C 6445.63 4293.43 6434.16 4294.87 6426.99 4302.03 C 6414.1 4316.37 6429.86 4320.66 6442.76 4327.83 L 6451.36 4349.33 C 6439.89 4359.36 6408.36 4355.06 6408.36 4367.96 C 6409.8 4383.72 6444.19 4405.22 6408.36 4406.65 C 6395.46 4406.65 6385.43 4398.06 6373.97 4392.32 C 6379.7 4395.19 6386.87 4398.06 6388.3 4393.76 C 6391.16 4383.72 6382.57 4372.26 6375.4 4363.66 C 6372.53 4360.79 6371.1 4369.39 6368.23 4369.39 C 6359.64 4369.39 6353.9 4362.23 6346.74 4360.79 C 6339.57 4359.36 6332.4 4365.09 6326.67 4363.66 C 6323.81 4363.66 6323.81 4359.36 6323.81 4357.93 C 6332.4 4335 6306.61 4340.73 6308.04 4336.43 C 6309.47 4330.7 6319.51 4332.13 6320.94 4326.4 C 6326.67 4284.84 6313.77 4296.3 6290.84 4276.24 C 6287.98 4273.37 6279.38 4266.2 6283.68 4264.77 C 6296.58 4257.61 6365.37 4273.37 6352.47 4228.94 C 6349.6 4218.91 6333.84 4221.78 6325.24 4216.04 C 6320.94 4213.18 6335.27 4218.91 6339.57 4218.91 C 6346.74 4221.78 6353.9 4224.64 6361.07 4224.64 C 6382.57 4224.64 6416.96 4214.61 6409.8 4259.04 C 6408.36 4266.2 6389.73 4273.37 6395.46 4277.67 C 6421.26 4299.17 6412.66 4247.57 6408.36 4227.51 C 6406.93 4218.91 6384 4226.08 6386.87 4218.91 C 6388.3 4213.18 6391.16 4210.31 6394.03 4206.01 M 6422.69 4435.32 L 6415.53 4435.32 L 6415.53 4438.18 C 6416.96 4441.05 6415.53 4442.48 6415.53 4443.92 C 6411.23 4446.78 6405.5 4451.08 6404.06 4453.95 C 6399.76 4456.82 6396.9 4459.68 6395.46 4463.98 L 6394.03 4465.41 L 6394.03 4466.85 L 6394.03 4463.98 C 6385.43 4463.98 6375.4 4462.55 6371.1 4463.98 C 6363.93 4465.41 6358.2 4478.31 6349.6 4476.88 C 6338.14 4475.45 6332.4 4461.12 6322.37 4455.38 C 6315.21 4451.08 6305.17 4448.22 6299.44 4442.48 C 6295.14 4439.62 6310.91 4436.75 6325.24 4410.95 C 6345.3 4428.15 6375.4 4432.45 6392.6 4446.78 L 6394.03 4448.22 C 6385.43 4435.32 6366.8 4425.29 6373.97 4413.82 C 6376.83 4408.09 6385.43 4410.95 6389.73 4409.52 C 6399.76 4419.55 6421.26 4418.12 6424.13 4431.02 C 6424.13 4432.45 6424.13 4433.88 6422.69 4435.32 M 5989.88 4226.08 L 5989.88 4237.54 C 5988.45 4228.94 5988.45 4220.34 5989.88 4226.08 M 6240.68 3952.34 C 6244.98 3968.11 6230.65 4045.5 6204.85 4036.9 C 6196.25 4034.03 6189.09 4024 6179.06 4024 C 6189.09 4011.1 6146.09 4021.13 6134.63 4024 C 6124.6 4026.87 6116 4035.46 6105.96 4036.9 C 6101.67 4036.9 6098.8 4029.73 6094.5 4029.73 C 6083.03 4028.3 6071.57 4029.73 6061.54 4032.6 C 6057.24 4034.03 6060.1 4042.63 6055.8 4045.5 C 6035.74 4054.1 6057.24 3996.77 6028.57 4072.73 C 6027.14 4074.16 6035.74 4114.29 6034.31 4114.29 C 5987.01 4112.86 6012.81 4024 6040.04 4013.97 C 6064.4 4002.5 6098.8 4015.4 6123.16 4005.37 C 6156.13 3989.6 6148.96 3943.74 6179.06 3926.54 C 6189.09 3922.24 6201.99 3925.11 6212.02 3920.81 C 6222.05 3915.08 6227.78 3895.01 6239.25 3895.01 C 6260.75 3895.01 6227.78 3910.78 6226.35 3923.68 C 6223.48 3943.74 6233.52 3930.84 6240.68 3952.34 M 6372.53 4439.62 L 6359.64 4441.05 C 6369.67 4443.92 6378.27 4448.22 6385.43 4455.38 L 6386.87 4455.38 C 6382.57 4449.65 6376.83 4445.35 6372.53 4439.62 " }
+ }
+ }
+ // START gb type: G 1
+ Item { // structure node
+ objectName: "gb"
+ // PATH visit gb-nir count: 74
+ DemoShape {
+ objectName: "gb-nir"
+ // Translate -2.36538e-05, 0
+ x: -2.36538e-05
+ y: 0
+ ShapePath {
+ objectName: "svg_path:gb-nir"
+ // boundingRect: 4213.87, 4152.48 293.249x235.614
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 4338.97 4169.72 C 4343.27 4175.45 4349 4179.75 4354.73 4176.88 C 4363.33 4172.58 4366.2 4158.25 4374.8 4153.95 C 4383.4 4148.22 4393.43 4161.12 4402.03 4159.69 C 4412.06 4159.69 4419.23 4152.52 4427.82 4153.95 C 4437.86 4155.39 4443.59 4165.42 4452.19 4168.29 C 4456.49 4171.15 4465.09 4165.42 4466.52 4169.72 C 4472.25 4179.75 4475.12 4194.08 4472.25 4205.55 C 4472.25 4209.85 4466.52 4211.28 4466.52 4214.15 C 4482.28 4275.77 4475.12 4195.52 4492.32 4249.98 C 4498.05 4265.74 4460.79 4287.24 4460.79 4287.24 C 4485.15 4281.51 4510.95 4262.87 4506.65 4320.2 C 4500.92 4376.09 4460.79 4346 4449.32 4358.9 C 4439.29 4368.93 4446.46 4391.86 4420.66 4387.56 C 4414.93 4387.56 4410.63 4381.83 4406.33 4377.53 L 4400.59 4370.36 C 4379.1 4357.46 4409.19 4390.43 4360.47 4373.23 C 4353.3 4371.8 4363.33 4358.9 4366.2 4351.73 C 4367.63 4341.7 4353.3 4344.56 4350.43 4338.83 C 4343.27 4324.5 4350.43 4290.1 4334.67 4292.97 C 4273.04 4301.57 4351.87 4330.23 4285.94 4335.97 C 4264.44 4337.4 4208.55 4262.87 4214.28 4255.71 C 4215.72 4255.71 4271.61 4241.38 4271.61 4241.38 C 4274.48 4232.78 4255.84 4228.48 4254.41 4219.88 C 4251.54 4211.28 4288.81 4222.75 4293.11 4219.88 C 4316.04 4205.55 4307.44 4169.72 4338.97 4169.72 " }
+ }
+ }
+ // PATH visit gb-main count: 901
+ DemoShape {
+ objectName: "gb-main"
+ ShapePath {
+ objectName: "svg_path:gb-main"
+ // boundingRect: 4309.6, 3292.84 941.871x1863.96
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 4586.2 4531.94 C 4611.99 4552.01 4633.49 4563.47 4606.26 4576.37 M 4590.5 4583.54 C 4586.2 4586.4 4581.9 4587.84 4577.6 4587.84 C 4573.3 4587.84 4581.9 4582.1 4580.46 4579.24 C 4571.87 4549.14 4550.37 4543.41 4586.2 4531.94 M 4733.81 3558.82 C 4742.41 3563.12 4761.04 3571.72 4762.48 3581.75 C 4762.48 3588.92 4742.41 3601.82 4748.14 3597.52 C 4756.74 3591.79 4759.61 3574.59 4769.64 3576.02 C 4778.24 3578.89 4768.21 3597.52 4775.38 3603.25 C 4781.11 3607.55 4783.97 3591.79 4789.71 3590.35 C 4811.2 3584.62 4859.93 3593.22 4888.6 3597.52 C 4900.06 3598.95 4915.83 3588.92 4922.99 3597.52 C 4930.16 3604.69 4911.53 3611.85 4907.23 3620.45 C 4904.36 3629.05 4907.23 3637.65 4904.36 3646.25 C 4901.49 3653.41 4811.2 3707.87 4794.01 3720.77 C 4765.34 3742.27 4806.9 3717.91 4821.24 3729.37 C 4826.97 3732.24 4814.07 3737.97 4808.34 3742.27 C 4804.04 3746.57 4801.17 3753.73 4794.01 3755.17 C 4791.14 3755.17 4794.01 3745.14 4791.14 3745.14 C 4785.41 3743.7 4781.11 3749.44 4776.81 3750.87 C 4766.78 3755.17 4765.34 3743.7 4761.04 3753.73 C 4749.58 3779.53 4776.81 3788.13 4783.97 3779.53 C 4785.41 3776.67 4778.24 3772.37 4781.11 3772.37 C 4788.27 3770.93 4795.44 3776.67 4804.04 3776.67 C 4819.8 3775.23 4839.87 3758.03 4859.93 3762.33 C 4869.96 3763.77 4878.56 3773.8 4888.6 3776.67 C 4912.96 3783.83 4993.22 3782.4 5006.11 3811.06 C 5008.98 3819.66 5010.41 3828.26 5007.55 3836.86 L 4974.58 3869.82 C 4970.29 3875.55 4967.42 3882.72 4965.99 3889.89 C 4964.55 3892.75 4967.42 3895.62 4965.99 3897.05 C 4954.52 3918.55 4894.33 4003.11 4875.7 4004.54 C 4847.03 4007.41 4837 3993.07 4812.64 4011.7 C 4808.34 4014.57 4821.24 4016 4825.54 4014.57 C 4837 4013.14 4847.03 4005.97 4857.07 4004.54 C 4877.13 4003.11 4861.37 4017.44 4862.8 4021.74 C 4867.1 4031.77 4885.73 4034.64 4884.3 4044.67 C 4880 4076.2 4849.9 4048.97 4841.3 4050.4 C 4796.87 4057.57 4877.13 4076.2 4783.97 4069.03 C 4782.54 4069.03 4782.54 4084.8 4783.97 4084.8 C 4794.01 4089.1 4782.54 4080.5 4792.57 4081.93 C 4804.04 4084.8 4828.4 4103.43 4841.3 4094.83 C 4847.03 4090.53 4848.47 4080.5 4854.2 4077.63 C 4857.07 4076.2 4911.53 4116.33 4917.26 4122.06 C 4927.29 4129.22 4931.59 4165.05 4941.62 4176.52 C 4947.35 4182.25 4955.95 4183.68 4960.25 4189.42 C 4961.69 4192.28 4958.82 4195.15 4958.82 4199.45 C 4957.39 4208.05 4957.39 4218.08 4957.39 4226.68 C 4958.82 4252.48 4968.85 4358.53 4977.45 4380.03 C 4978.88 4382.89 4981.75 4374.3 4984.62 4375.73 C 4990.35 4377.16 4994.65 4384.33 5000.38 4387.19 C 5026.18 4402.96 5020.45 4390.06 5044.81 4420.16 C 5053.41 4430.19 5049.11 4447.39 5056.28 4458.85 C 5063.44 4474.62 5083.51 4460.29 5083.51 4488.95 C 5083.51 4497.55 5067.74 4497.55 5066.31 4506.15 C 5060.57 4529.08 5087.81 4562.04 5093.54 4582.1 C 5100.7 4609.33 5077.77 4569.21 5076.34 4599.3 C 5076.34 4606.47 5127.93 4660.93 5102.14 4688.16 C 5082.07 4708.22 5073.47 4685.29 5059.14 4721.12 C 5057.71 4725.42 5069.17 4719.69 5072.04 4722.55 C 5077.77 4726.85 5079.21 4735.45 5083.51 4739.75 C 5087.81 4744.05 5094.97 4751.22 5099.27 4748.35 C 5127.93 4729.72 5106.44 4711.09 5136.53 4715.39 C 5185.26 4722.55 5264.08 4744.05 5249.75 4821.44 C 5245.45 4845.81 5212.49 4910.3 5188.13 4918.9 C 5179.53 4921.76 5166.63 4904.57 5163.76 4911.73 C 5150.86 4937.53 5218.22 4930.36 5160.9 4946.13 C 5156.6 4947.56 5153.73 4944.7 5149.43 4941.83 C 5148 4941.83 5148 4938.96 5146.56 4938.96 C 5135.1 4940.4 5117.9 4937.53 5112.17 4946.13 C 5107.87 4954.73 5133.67 4943.26 5139.4 4950.43 C 5145.13 4956.16 5140.83 4967.63 5136.53 4974.79 C 5125.07 4993.42 5093.54 4980.52 5077.77 4986.26 C 5073.47 4987.69 5062.01 4991.99 5066.31 4994.86 C 5067.74 4994.86 5109.3 4981.96 5107.87 5000.59 C 5106.44 5007.75 5090.67 4999.16 5084.94 5003.46 C 5080.64 5007.75 5096.4 5014.92 5102.14 5012.05 C 5105 5009.19 5105 5000.59 5109.3 5000.59 C 5115.04 4999.16 5120.77 5004.89 5126.5 5009.19 C 5146.56 5027.82 5084.94 5017.79 5126.5 5024.95 C 5146.56 5027.82 5168.06 5012.05 5189.56 5020.65 C 5195.29 5022.09 5180.96 5029.25 5179.53 5034.98 C 5176.66 5045.02 5178.09 5055.05 5176.66 5066.51 C 5172.36 5080.85 5146.56 5075.11 5132.23 5082.28 C 5126.5 5086.58 5130.8 5100.91 5123.63 5103.78 C 5097.84 5112.38 5110.74 5089.45 5094.97 5099.48 C 5079.21 5109.51 5064.87 5105.21 5049.11 5112.38 C 5041.94 5115.24 5039.08 5126.71 5030.48 5126.71 C 5017.58 5126.71 4994.65 5102.34 4981.75 5099.48 C 4971.72 5098.04 4900.06 5115.24 4895.76 5099.48 C 4894.33 5092.31 4914.39 5098.04 4900.06 5088.01 C 4891.46 5082.28 4895.76 5095.18 4892.89 5099.48 C 4890.03 5100.91 4885.73 5098.04 4884.3 5095.18 C 4884.3 5090.88 4894.33 5086.58 4890.03 5086.58 C 4885.73 5085.15 4881.43 5095.18 4877.13 5093.74 C 4872.83 5092.31 4881.43 5085.15 4878.56 5080.85 C 4868.53 5067.95 4874.26 5093.74 4869.96 5092.31 C 4852.77 5086.58 4839.87 5057.92 4835.57 5062.21 C 4829.84 5069.38 4847.03 5079.41 4845.6 5088.01 C 4844.17 5102.34 4763.91 5083.71 4761.04 5088.01 C 4756.74 5095.18 4771.08 5105.21 4769.64 5113.81 C 4768.21 5118.11 4761.04 5112.38 4756.74 5112.38 L 4755.31 5112.38 C 4748.14 5108.08 4725.21 5092.31 4718.05 5095.18 C 4709.45 5099.48 4712.32 5112.38 4708.02 5119.54 C 4705.15 5122.41 4710.88 5110.94 4709.45 5106.64 C 4706.58 5099.48 4699.42 5093.74 4692.25 5088.01 C 4673.62 5069.38 4619.16 5056.48 4593.36 5066.51 C 4593.36 5066.51 4577.6 5135.31 4556.1 5145.34 C 4538.9 5152.5 4508.81 5110.94 4497.34 5105.21 C 4490.17 5100.91 4511.67 5118.11 4493.04 5113.81 C 4475.84 5109.51 4451.48 5092.31 4431.42 5098.04 C 4424.25 5099.48 4427.12 5112.38 4421.38 5115.24 C 4412.78 5118.11 4402.75 5112.38 4392.72 5112.38 C 4391.29 5112.38 4389.85 5112.38 4388.42 5113.81 C 4385.55 5120.97 4368.36 5156.8 4362.62 5156.8 C 4354.02 5155.37 4358.32 5136.74 4352.59 5129.57 C 4343.99 5115.24 4309.6 5125.27 4309.6 5109.51 C 4309.6 5099.48 4366.92 5096.61 4368.36 5096.61 C 4374.09 5095.18 4379.82 5089.45 4384.12 5083.71 C 4386.99 5082.28 4384.12 5076.55 4386.99 5075.11 C 4389.85 5072.25 4395.59 5076.55 4398.45 5075.11 C 4408.48 5066.51 4411.35 5053.62 4421.38 5046.45 C 4425.68 5043.58 4432.85 5049.32 4437.15 5047.88 C 4441.45 5045.02 4440.01 5039.28 4444.31 5036.42 C 4451.48 5029.25 4464.38 5027.82 4471.54 5019.22 C 4475.84 5013.49 4474.41 4980.52 4484.44 4976.22 C 4490.17 4974.79 4495.91 4986.26 4501.64 4987.69 C 4534.6 4990.56 4495.91 4940.4 4576.16 4954.73 C 4603.4 4959.03 4639.22 4996.29 4666.45 4974.79 C 4675.05 4967.63 4672.19 4953.29 4676.49 4944.7 C 4677.92 4941.83 4746.71 4898.83 4755.31 4888.8 C 4763.91 4878.77 4718.05 4908.87 4712.32 4913.17 C 4705.15 4916.03 4697.98 4917.47 4690.82 4918.9 C 4686.52 4918.9 4683.65 4911.73 4679.35 4913.17 C 4643.52 4923.2 4652.12 4953.29 4609.13 4928.93 C 4594.8 4918.9 4597.66 4894.53 4584.76 4881.64 C 4573.3 4870.17 4551.8 4895.97 4537.47 4888.8 C 4534.6 4887.37 4531.74 4842.94 4530.3 4838.64 C 4528.87 4835.77 4527.44 4844.37 4524.57 4844.37 C 4514.54 4845.81 4527.44 4834.34 4493.04 4844.37 C 4488.74 4844.37 4490.17 4852.97 4487.31 4855.84 C 4478.71 4858.71 4467.24 4861.57 4458.65 4858.71 C 4451.48 4857.27 4438.58 4848.67 4441.45 4842.94 C 4445.75 4834.34 4460.08 4840.07 4468.68 4838.64 C 4472.98 4837.21 4432.85 4835.77 4428.55 4830.04 C 4422.82 4821.44 4425.68 4809.98 4425.68 4799.95 C 4425.68 4771.28 4478.71 4787.05 4485.88 4784.18 C 4494.47 4781.31 4495.91 4766.98 4505.94 4765.55 C 4513.11 4765.55 4520.27 4772.72 4527.44 4771.28 C 4550.37 4765.55 4596.23 4726.85 4604.83 4706.79 C 4607.69 4701.06 4601.96 4645.16 4593.36 4636.56 C 4569 4606.47 4541.77 4692.46 4531.74 4633.7 C 4528.87 4623.67 4558.97 4622.23 4577.6 4607.9 C 4580.46 4603.6 4579.03 4596.44 4581.9 4592.14 C 4584.76 4587.84 4587.63 4584.97 4590.5 4583.54 C 4596.23 4579.24 4601.96 4577.8 4606.26 4576.37 C 4616.29 4576.37 4624.89 4580.67 4644.96 4573.51 C 4647.82 4572.07 4639.22 4562.04 4643.52 4563.47 C 4653.56 4564.91 4659.29 4576.37 4669.32 4577.8 C 4689.39 4580.67 4693.68 4562.04 4708.02 4574.94 C 4715.18 4582.1 4718.05 4603.6 4726.65 4597.87 C 4735.25 4592.14 4719.48 4576.37 4722.35 4566.34 C 4723.78 4560.61 4749.58 4563.47 4768.21 4586.4 C 4772.51 4590.7 4756.74 4586.4 4751.01 4583.54 C 4738.11 4576.37 4732.38 4554.87 4735.25 4539.11 C 4736.68 4530.51 4745.28 4523.34 4752.44 4517.61 C 4758.18 4513.31 4772.51 4517.61 4771.08 4510.45 C 4768.21 4501.85 4751.01 4501.85 4748.14 4493.25 C 4729.51 4453.12 4778.24 4484.65 4778.24 4467.45 C 4779.67 4458.85 4768.21 4463.15 4772.51 4453.12 C 4776.81 4443.09 4789.71 4435.92 4788.27 4424.46 C 4786.84 4402.96 4769.64 4441.65 4749.58 4443.09 C 4736.68 4443.09 4753.88 4415.86 4749.58 4404.39 C 4746.71 4398.66 4739.55 4415.86 4733.81 4414.42 C 4716.62 4408.69 4730.95 4392.93 4729.51 4384.33 L 4712.32 4348.5 C 4710.88 4331.3 4758.18 4284.01 4759.61 4284.01 C 4762.48 4285.44 4761.04 4292.6 4763.91 4291.17 C 4775.38 4288.31 4791.14 4285.44 4794.01 4275.41 C 4796.87 4266.81 4776.81 4275.41 4768.21 4273.97 C 4762.48 4272.54 4756.74 4265.37 4749.58 4266.81 C 4740.98 4269.67 4736.68 4281.14 4728.08 4284.01 C 4720.91 4286.87 4712.32 4281.14 4705.15 4282.57 C 4696.55 4285.44 4692.25 4292.6 4685.09 4295.47 C 4682.22 4296.9 4677.92 4296.9 4676.49 4294.04 C 4675.05 4289.74 4683.65 4282.57 4679.35 4281.14 C 4675.05 4279.71 4676.49 4289.74 4673.62 4292.6 C 4672.19 4294.04 4669.32 4292.6 4667.89 4291.17 C 4660.72 4282.57 4660.72 4261.08 4644.96 4258.21 C 4624.89 4253.91 4652.12 4278.27 4637.79 4301.2 C 4632.06 4309.8 4624.89 4286.87 4619.16 4278.27 C 4577.6 4223.81 4620.59 4286.87 4564.7 4253.91 C 4550.37 4245.31 4584.76 4252.48 4583.33 4245.31 C 4581.9 4212.35 4587.63 4215.21 4607.69 4192.28 C 4644.96 4150.72 4581.9 4199.45 4627.76 4157.89 C 4632.06 4153.59 4642.09 4153.59 4644.96 4147.86 C 4650.69 4137.82 4623.46 4104.86 4622.03 4101.99 C 4620.59 4097.69 4629.19 4097.69 4630.63 4093.39 C 4634.92 4083.36 4630.63 4070.46 4634.92 4059 C 4640.66 4046.1 4652.12 4060.43 4665.02 4066.16 C 4712.32 4087.66 4640.66 4053.27 4640.66 4050.4 C 4636.36 4041.8 4640.66 4030.34 4642.09 4018.87 C 4642.09 4011.7 4647.82 4003.11 4643.52 3997.37 C 4642.09 3995.94 4584.76 4027.47 4580.46 4031.77 C 4569 4041.8 4587.63 4061.87 4584.76 4077.63 C 4583.33 4083.36 4571.87 4081.93 4567.57 4086.23 C 4564.7 4089.1 4561.83 4093.39 4561.83 4097.69 C 4560.4 4101.99 4563.27 4107.73 4561.83 4110.59 C 4557.53 4122.06 4546.07 4127.79 4541.77 4139.26 C 4540.34 4142.12 4548.93 4147.86 4546.07 4149.29 C 4536.04 4155.02 4523.14 4159.32 4511.67 4155.02 C 4503.07 4150.72 4523.14 4139.26 4527.44 4132.09 C 4536.04 4120.62 4533.17 4101.99 4541.77 4090.53 L 4571.87 4064.73 C 4569 4057.57 4553.23 4067.6 4550.37 4060.43 C 4547.5 4054.7 4589.06 4030.34 4569 4026.04 C 4560.4 4023.17 4556.1 4044.67 4548.93 4040.37 C 4540.34 4033.2 4569 4027.47 4574.73 4017.44 C 4580.46 4005.97 4576.16 3980.17 4580.46 3970.14 C 4583.33 3961.54 4590.5 3952.94 4597.66 3951.51 C 4604.83 3950.08 4617.73 3965.84 4617.73 3958.68 C 4617.73 3951.51 4601.96 3951.51 4597.66 3944.35 C 4587.63 3924.28 4646.39 3914.25 4647.82 3914.25 C 4652.12 3909.95 4629.19 3915.68 4629.19 3909.95 C 4629.19 3901.35 4643.52 3899.92 4646.39 3891.32 C 4649.26 3887.02 4637.79 3894.18 4633.49 3898.48 C 4623.46 3905.65 4624.89 3908.52 4616.29 3912.82 C 4597.66 3922.85 4580.46 3938.61 4567.57 3932.88 C 4569 3935.75 4570.43 3937.18 4573.3 3938.61 C 4574.73 3941.48 4580.46 3944.35 4577.6 3947.21 C 4566.13 3957.24 4508.81 3977.31 4498.77 3957.24 C 4497.34 3955.81 4498.77 3951.51 4500.21 3951.51 C 4528.87 3940.05 4505.94 3983.04 4541.77 3934.31 C 4564.7 3904.22 4474.41 3919.98 4538.9 3898.48 C 4541.77 3904.22 4544.64 3909.95 4548.93 3914.25 C 4550.37 3917.12 4553.23 3919.98 4556.1 3921.41 C 4553.23 3917.12 4550.37 3911.38 4548.93 3904.22 C 4547.5 3899.92 4558.97 3904.22 4563.27 3904.22 C 4574.73 3904.22 4586.2 3909.95 4596.23 3905.65 C 4609.13 3899.92 4518.84 3901.35 4530.3 3884.15 C 4538.9 3872.69 4576.16 3888.45 4579.03 3882.72 C 4584.76 3868.39 4543.2 3851.19 4583.33 3841.16 C 4593.36 3838.29 4603.4 3851.19 4614.86 3848.32 C 4653.56 3839.72 4604.83 3813.93 4604.83 3813.93 C 4609.13 3803.9 4630.63 3816.79 4637.79 3808.19 C 4644.96 3799.6 4639.22 3785.26 4639.22 3773.8 C 4639.22 3765.2 4640.66 3756.6 4637.79 3750.87 C 4636.36 3748 4589.06 3697.84 4620.59 3684.94 C 4627.76 3680.64 4629.19 3707.87 4637.79 3705.01 C 4649.26 3702.14 4633.49 3669.18 4650.69 3679.21 C 4673.62 3692.11 4675.05 3693.54 4675.05 3692.11 C 4679.35 3673.48 4663.59 3672.04 4665.02 3653.41 C 4665.02 3646.25 4683.65 3662.01 4686.52 3654.85 C 4692.25 3644.81 4679.35 3631.92 4680.79 3621.88 C 4680.79 3617.58 4685.09 3629.05 4689.39 3629.05 C 4699.42 3633.35 4715.18 3641.95 4722.35 3633.35 C 4728.08 3626.18 4708.02 3619.02 4705.15 3610.42 C 4697.98 3586.05 4719.48 3604.69 4719.48 3604.69 C 4722.35 3601.82 4715.18 3596.09 4716.62 3593.22 C 4719.48 3591.79 4726.65 3598.95 4726.65 3596.09 C 4726.65 3591.79 4716.62 3588.92 4718.05 3584.62 C 4719.48 3574.59 4728.08 3567.42 4733.81 3558.82 M 5155.5 3296.15 C 5158.49 3298.25 5158.23 3295.42 5159.46 3297.99 C 5160.85 3300.88 5162.19 3300.6 5162 3295.34 C 5161.79 3292.55 5163.76 3292.26 5170.93 3293.69 C 5175.23 3295.12 5163.76 3300.85 5160.9 3305.15 C 5160.9 3306.59 5166.63 3310.89 5165.2 3312.32 C 5162.33 3313.75 5156.6 3306.59 5155.16 3310.89 C 5155.16 3313.75 5163.76 3313.75 5163.76 3318.05 C 5165.2 3330.95 5146.56 3326.65 5148 3330.95 C 5149.43 3349.58 5163.65 3347.35 5134.99 3377.45 C 5133.55 3380.32 5128.43 3376.13 5129.87 3373.27 C 5138.46 3354.63 5149.43 3335.25 5142.27 3330.95 C 5136.53 3325.22 5133.82 3338.96 5126.66 3334.66 C 5116.63 3330.36 5121.66 3330.43 5118.8 3320.39 C 5117.36 3317.53 5123.41 3314.24 5126.27 3312.8 C 5137.74 3311.37 5135.46 3314.11 5145.5 3309.81 C 5152.66 3305.51 5151.2 3303.32 5155.5 3296.15 M 4967.42 3545.93 C 4948.79 3583.19 4862.8 3502.93 4922.99 3498.63 C 4931.59 3498.63 4940.19 3508.66 4940.19 3518.7 C 4941.62 3524.43 4922.99 3522.99 4925.86 3527.29 C 4930.16 3535.89 4943.06 3535.89 4951.65 3538.76 C 4955.95 3541.63 4961.69 3543.06 4967.42 3545.93 M 4611.99 3576.02 C 4604.83 3587.49 4584.76 3598.95 4581.9 3607.55 C 4580.46 3614.72 4579.03 3654.85 4560.4 3654.85 C 4550.37 3653.41 4551.8 3637.65 4544.64 3630.48 C 4536.04 3624.75 4547.5 3654.85 4538.9 3659.15 C 4534.6 3660.58 4528.87 3653.41 4526 3656.28 C 4515.97 3662.01 4514.54 3682.08 4503.07 3682.08 C 4493.04 3682.08 4484.44 3664.88 4490.17 3656.28 C 4495.91 3646.25 4520.27 3663.45 4524.57 3651.98 C 4528.87 3641.95 4504.51 3640.51 4500.21 3629.05 C 4498.77 3624.75 4510.24 3626.18 4513.11 3623.32 C 4514.54 3621.88 4508.81 3623.32 4508.81 3621.88 C 4505.94 3616.15 4504.51 3610.42 4505.94 3604.69 C 4518.84 3568.86 4528.87 3601.82 4548.93 3606.12 C 4550.37 3606.12 4541.77 3584.62 4546.07 3580.32 C 4560.4 3563.12 4560.4 3578.89 4574.73 3577.46 C 4576.16 3576.02 4576.16 3571.72 4577.6 3571.72 C 4586.2 3564.56 4594.8 3553.09 4604.83 3554.52 C 4611.99 3555.96 4609.13 3568.86 4611.99 3576.02 " }
+ }
+ }
+ } // END gb type: G 1
+ // PATH visit ie count: 395
+ DemoShape {
+ objectName: "ie"
+ ShapePath {
+ objectName: "svg_path:ie"
+ // boundingRect: 3847.16, 4125.47 570.555x643.865
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 4406.23 4375.13 C 4409.1 4382.29 4421.99 4389.46 4416.26 4395.19 C 4410.53 4403.79 4393.33 4396.62 4384.73 4402.36 C 4380.43 4406.65 4396.2 4409.52 4396.2 4415.25 C 4397.63 4426.72 4390.47 4438.18 4390.47 4449.65 C 4391.9 4459.68 4401.93 4465.41 4401.93 4475.45 C 4401.93 4482.61 4390.47 4486.91 4390.47 4495.51 C 4389.03 4501.24 4400.5 4506.98 4397.63 4512.71 C 4393.33 4518.44 4381.87 4505.54 4379 4511.28 C 4374.7 4517.01 4386.17 4522.74 4387.6 4529.91 C 4387.6 4532.77 4384.73 4535.64 4384.73 4538.51 C 4386.17 4557.14 4391.9 4568.6 4386.17 4587.23 C 4381.87 4600.13 4331.71 4673.22 4323.11 4680.39 C 4320.24 4683.26 4313.07 4684.69 4311.64 4690.42 C 4308.77 4700.45 4313.07 4711.92 4311.64 4721.95 C 4311.64 4723.38 4308.77 4723.38 4308.77 4721.95 C 4304.48 4711.92 4301.61 4724.82 4287.28 4719.08 C 4281.54 4717.65 4281.54 4709.05 4275.81 4707.62 C 4265.78 4704.75 4234.25 4720.52 4227.08 4711.92 C 4225.65 4709.05 4232.82 4709.05 4232.82 4706.19 C 4234.25 4701.89 4222.78 4710.49 4218.49 4709.05 C 4196.99 4707.62 4189.82 4696.15 4169.76 4703.32 C 4166.89 4704.75 4175.49 4710.49 4175.49 4714.79 C 4174.06 4721.95 4148.26 4727.68 4148.26 4727.68 C 4142.53 4727.68 4139.66 4719.08 4133.93 4720.52 C 4123.9 4724.82 4119.6 4737.72 4111 4743.45 C 4080.9 4759.21 4099.53 4716.22 4072.3 4720.52 C 4063.7 4721.95 4079.47 4737.72 4076.6 4744.88 C 4072.3 4750.61 4062.27 4750.61 4055.1 4754.91 C 4050.81 4757.78 4047.94 4766.38 4042.21 4767.81 C 4039.34 4767.81 4040.77 4762.08 4037.91 4760.65 C 4035.04 4757.78 4030.74 4754.91 4026.44 4756.35 C 4023.57 4759.21 4026.44 4767.81 4022.14 4769.25 C 4007.81 4770.68 3982.01 4753.48 3973.41 4754.91 C 3961.95 4756.35 3953.35 4764.95 3943.32 4766.38 C 3914.65 4769.25 3959.08 4746.31 3936.15 4750.61 C 3927.55 4753.48 3893.16 4757.78 3890.29 4754.91 C 3884.56 4746.31 3939.02 4730.55 3944.75 4717.65 C 3946.18 4714.79 3940.45 4714.79 3937.59 4713.35 C 3936.15 4710.49 3903.19 4717.65 3861.63 4713.35 C 3860.19 4713.35 3864.49 4711.92 3883.12 4706.19 C 3885.99 4706.19 3888.86 4704.75 3888.86 4701.89 C 3888.86 4700.45 3884.56 4699.02 3885.99 4697.59 C 3898.89 4686.12 3893.16 4704.75 3910.35 4697.59 C 3918.95 4694.72 3943.32 4693.29 3937.59 4686.12 C 3936.15 4684.69 3871.66 4693.29 3868.79 4693.29 C 3855.89 4690.42 3845.86 4670.36 3847.3 4656.03 C 3848.73 4650.29 3860.19 4661.76 3864.49 4658.89 C 3868.79 4656.03 3861.63 4651.73 3861.63 4647.43 C 3864.49 4634.53 3887.42 4641.69 3900.32 4638.83 C 3910.35 4637.39 3923.25 4638.83 3930.42 4631.66 C 3936.15 4627.36 3916.09 4617.33 3908.92 4621.63 C 3907.49 4624.5 3911.79 4630.23 3908.92 4631.66 C 3907.49 4633.09 3864.49 4611.6 3855.89 4600.13 C 3854.46 4597.27 3864.49 4604.43 3867.36 4603 C 3870.23 4601.57 3867.36 4595.83 3870.23 4594.4 C 3877.39 4590.1 3897.46 4591.53 3907.49 4592.97 C 3920.39 4592.97 3928.99 4605.86 3940.45 4611.6 C 3951.92 4617.33 3918.95 4588.67 3924.69 4577.2 C 3928.99 4567.17 3946.18 4574.34 3956.22 4570.04 C 3961.95 4567.17 3961.95 4558.57 3967.68 4557.14 C 3969.11 4557.14 4060.84 4572.9 4070.87 4567.17 C 4078.04 4562.87 4052.24 4564.3 4047.94 4557.14 C 4046.51 4552.84 4052.24 4545.67 4050.8 4541.37 C 4049.37 4538.51 4020.71 4567.17 4006.38 4565.74 C 4003.51 4564.3 4014.98 4557.14 4010.68 4555.7 C 4006.38 4554.27 4000.64 4560 3994.91 4558.57 C 3989.18 4557.14 3984.88 4552.84 3982.01 4548.54 C 3980.58 4545.67 3983.45 4542.81 3982.01 4541.37 C 3982.01 4541.37 3940.45 4549.97 3934.72 4547.1 C 3930.42 4545.67 3944.75 4547.1 3949.05 4545.67 C 3960.52 4539.94 3969.11 4532.77 3980.58 4528.47 C 3983.45 4525.61 3989.18 4529.91 3992.05 4528.47 C 3997.78 4522.74 3999.21 4512.71 4006.38 4505.54 C 4009.24 4502.68 4013.54 4501.24 4017.84 4499.81 C 4020.71 4496.94 4007.81 4499.81 4004.94 4495.51 C 3999.21 4488.35 4017.84 4482.61 4023.57 4476.88 C 4027.87 4471.15 4030.74 4463.98 4037.91 4462.55 C 4042.21 4461.12 4043.64 4472.58 4047.94 4471.15 C 4059.4 4471.15 4070.87 4466.85 4080.9 4461.12 C 4083.77 4459.68 4069.44 4462.55 4069.44 4458.25 C 4069.44 4453.95 4079.47 4455.38 4079.47 4451.08 C 4078.04 4446.78 4049.37 4452.52 4017.84 4443.92 C 3987.75 4435.32 4017.84 4436.75 4010.68 4412.39 C 4003.51 4386.59 3963.38 4405.22 3951.92 4388.02 C 3920.39 4346.46 3979.15 4363.66 3980.58 4363.66 C 3982.01 4362.23 3974.85 4357.93 3976.28 4356.49 C 3982.01 4346.46 3989.18 4336.43 4000.64 4333.56 C 4010.68 4330.7 4022.14 4339.3 4033.61 4339.3 C 4036.47 4339.3 4039.34 4337.86 4040.77 4335 C 4059.4 4290.57 4006.38 4323.53 4003.51 4310.63 C 4000.64 4304.9 4016.41 4310.63 4020.71 4306.33 C 4029.31 4300.6 4014.98 4286.27 4013.54 4284.84 C 4013.54 4274.8 4026.44 4261.9 4019.28 4253.31 C 4013.54 4247.57 4002.08 4269.07 3996.34 4263.34 C 3990.61 4257.61 4004.94 4250.44 4007.81 4243.27 C 4009.24 4237.54 3994.91 4238.97 4007.81 4236.11 C 4020.71 4233.24 4088.07 4233.24 4095.23 4251.87 C 4099.53 4261.9 4093.8 4277.67 4102.4 4280.54 C 4111 4283.4 4111 4266.2 4118.16 4261.9 C 4131.06 4254.74 4168.32 4297.73 4169.76 4293.43 C 4174.06 4283.4 4159.73 4271.94 4161.16 4260.47 C 4164.03 4247.57 4207.02 4251.87 4207.02 4250.44 C 4218.49 4246.14 4227.08 4237.54 4234.25 4228.94 C 4238.55 4223.21 4212.75 4228.94 4212.75 4226.08 C 4208.45 4210.31 4212.75 4231.81 4194.12 4230.38 C 4191.26 4230.38 4195.55 4224.64 4195.55 4221.78 C 4195.55 4220.34 4195.55 4217.48 4194.12 4217.48 C 4188.39 4214.61 4159.73 4213.18 4164.03 4195.98 C 4172.62 4167.32 4201.29 4198.85 4209.89 4195.98 C 4215.62 4193.11 4192.69 4184.51 4196.99 4178.78 C 4202.72 4173.05 4214.19 4184.51 4221.35 4183.08 C 4228.52 4181.65 4215.62 4171.62 4211.32 4167.32 C 4208.45 4164.45 4221.35 4164.45 4222.78 4160.15 C 4228.52 4135.79 4166.89 4160.15 4241.42 4125.75 C 4245.72 4124.32 4251.45 4128.62 4257.18 4130.05 C 4277.25 4135.79 4300.18 4134.35 4317.37 4145.82 C 4327.41 4152.98 4291.58 4167.32 4297.31 4177.35 C 4303.04 4183.08 4314.51 4173.05 4321.67 4165.88 C 4324.54 4163.02 4317.37 4150.12 4323.11 4150.12 C 4331.71 4152.98 4337.44 4164.45 4343.17 4171.62 C 4311.64 4171.62 4320.24 4207.44 4297.31 4221.78 C 4293.01 4224.64 4255.75 4213.18 4258.61 4221.78 C 4260.05 4230.38 4278.68 4234.67 4275.81 4243.27 C 4275.81 4243.27 4219.92 4257.61 4218.49 4257.61 C 4212.75 4264.77 4268.65 4339.3 4290.14 4337.86 C 4356.07 4332.13 4277.25 4303.47 4338.87 4294.87 C 4354.64 4292 4347.47 4326.4 4354.64 4340.73 C 4357.5 4346.46 4371.83 4343.6 4370.4 4353.63 C 4367.53 4360.79 4357.5 4373.69 4364.67 4375.13 C 4413.4 4392.32 4383.3 4359.36 4404.8 4372.26 L 4410.53 4379.42 C 4409.1 4377.99 4407.66 4376.56 4406.23 4375.13 " }
+ }
+ }
+ // PATH visit nl count: 240
+ DemoShape {
+ objectName: "nl"
+ ShapePath {
+ objectName: "svg_path:nl"
+ // boundingRect: 5402.28, 4689.92 473.179x508.107
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 5655.95 4754.91 L 5630.15 4774.69 C 5615.82 4824.85 5678.88 4800.78 5655.95 4817.97 C 5645.92 4825.14 5630.15 4822.27 5624.42 4832.3 C 5617.26 4842.34 5639.95 4851.49 5631.35 4860.08 C 5628.48 4864.38 5610.09 4873.87 5612.96 4878.17 C 5612.96 4878.17 5630.92 4885 5630.92 4885 C 5639.52 4885 5671.19 4827.02 5694.55 4840.9 C 5703.26 4846.08 5691.78 4836.6 5688.91 4826.57 C 5686.05 4816.54 5708.98 4806.51 5704.68 4796.48 C 5698.95 4783.58 5664.55 4800.78 5664.55 4787.88 C 5665.98 4777.84 5671.72 4769.25 5673.15 4759.21 C 5673.15 4756.35 5670.28 4753.48 5668.85 4750.61 C 5667.42 4747.75 5651.37 4763.46 5657.1 4756.29 L 5628.72 4773.55 C 5618.69 4776.41 5607.22 4772.11 5597.19 4772.11 C 5592.89 4772.11 5588.59 4769.25 5587.16 4770.68 C 5568.53 4793.61 5574.26 4839.47 5565.66 4863.83 C 5557.06 4885.33 5539.86 4909.7 5524.1 4926.89 C 5518.37 4934.06 5514.07 4942.66 5506.9 4946.96 C 5502.6 4949.82 5492.57 4944.09 5491.14 4949.82 C 5488.27 4958.42 5501.17 4969.89 5496.87 4978.49 C 5482.54 5001.42 5438.11 4989.95 5448.14 4995.69 C 5459.61 5002.85 5468.21 5012.88 5481.1 5018.62 C 5486.84 5020.05 5492.57 5011.45 5496.87 5012.88 C 5502.6 5014.32 5512.63 5022.92 5508.33 5027.22 C 5501.17 5031.51 5482.54 5015.75 5482.54 5024.35 C 5483.97 5037.25 5521.23 5035.81 5514.07 5045.85 C 5492.57 5074.51 5472.51 5021.48 5463.91 5020.05 C 5436.68 5012.88 5410.88 5017.18 5415.18 5027.22 C 5415.18 5028.65 5459.61 5058.75 5468.21 5057.31 C 5471.07 5057.31 5469.64 5051.58 5472.51 5048.71 C 5478.24 5044.41 5483.97 5054.45 5489.7 5055.88 C 5495.44 5058.74 5504.04 5053.01 5509.77 5058.74 C 5512.63 5061.61 5515.5 5065.91 5516.93 5070.21 C 5521.23 5071.64 5524.1 5073.08 5528.4 5071.64 C 5538.43 5071.64 5511.2 5038.68 5545.6 5048.71 C 5548.46 5048.71 5542.73 5055.88 5544.16 5057.31 C 5561.36 5071.64 5565.66 5037.25 5578.56 5047.28 C 5582.86 5051.58 5567.09 5060.18 5571.39 5063.04 C 5604.36 5080.24 5575.69 5058.75 5601.49 5053.01 C 5605.79 5051.58 5612.96 5093.14 5622.99 5094.57 C 5645.92 5098.87 5676.01 5097.44 5694.65 5121.8 C 5696.08 5124.67 5688.91 5127.54 5687.48 5130.4 C 5684.61 5139 5677.45 5146.17 5676.01 5154.77 C 5676.01 5157.63 5683.18 5156.2 5681.75 5159.07 C 5680.31 5163.37 5673.15 5164.8 5671.72 5170.53 C 5661.68 5206.36 5686.05 5196.33 5708.98 5197.76 L 5710.41 5197.76 C 5707.54 5193.46 5706.11 5189.16 5706.11 5187.73 C 5707.54 5182 5717.58 5182 5719.01 5176.26 C 5723.31 5151.9 5681.75 5143.3 5694.65 5140.44 C 5698.95 5139 5701.81 5144.73 5704.68 5144.73 C 5704.68 5144.73 5731.91 5120.37 5731.91 5120.37 C 5733.34 5113.21 5714.71 5121.8 5720.44 5110.34 C 5724.74 5100.31 5734.77 5093.14 5739.07 5083.11 C 5751.97 5051.58 5720.44 5028.65 5711.84 5012.88 C 5710.41 5010.02 5713.28 5005.72 5714.71 5002.85 C 5714.71 4999.99 5711.84 4995.69 5714.71 4994.25 C 5721.88 4989.95 5730.48 4994.25 5739.07 4991.39 C 5741.94 4989.95 5736.21 4981.35 5739.07 4982.79 C 5747.67 4985.65 5753.41 4995.69 5763.44 4995.69 C 5773.47 4997.12 5812.17 4984.22 5816.47 4974.19 C 5820.76 4964.16 5789.24 4962.72 5802.13 4954.12 C 5825.06 4938.36 5852.29 4932.63 5846.56 4896.8 C 5845.13 4876.73 5792.1 4885.33 5805 4863.83 C 5806.43 4859.54 5813.6 4863.83 5813.6 4860.97 C 5815.03 4856.67 5807.87 4852.37 5809.3 4848.07 C 5809.3 4845.2 5813.6 4845.2 5816.47 4845.2 C 5826.5 4845.2 5835.1 4849.5 5845.13 4849.5 C 5856.59 4849.5 5846.56 4826.57 5850.86 4815.11 C 5853.73 4805.07 5878.09 4759.21 5875.23 4734.85 C 5846.56 4749.18 5843.7 4696.15 5819.33 4690.42 C 5797.83 4686.12 5684.61 4710.49 5671.72 4730.55 C 5664.55 4739.15 5661.68 4747.75 5655.95 4754.91 M 5405.15 5058.74 C 5402.28 5096.01 5432.38 5071.64 5453.87 5081.68 C 5456.74 5083.11 5451.01 5088.84 5453.87 5091.71 C 5471.07 5103.17 5485.4 5083.11 5496.87 5073.08 C 5489.7 5065.91 5481.1 5060.18 5478.24 5058.74 C 5471.07 5057.31 5469.64 5070.21 5462.47 5070.21 C 5438.11 5070.21 5453.87 5055.88 5425.21 5051.58 C 5418.05 5050.15 5410.88 5050.15 5402.28 5053.01 L 5405.15 5058.74 " }
+ }
+ }
+ // PATH visit be count: 157
+ DemoShape {
+ objectName: "be"
+ ShapePath {
+ objectName: "svg_path:be"
+ // boundingRect: 5301.96, 5045.41 450.877x384.166
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 5671.72 5427.07 C 5667.42 5425.64 5663.12 5424.2 5658.82 5424.2 C 5648.78 5422.77 5638.75 5432.8 5630.15 5428.5 C 5622.99 5425.64 5628.72 5412.74 5624.42 5407 C 5621.55 5402.7 5614.39 5405.57 5610.09 5402.7 C 5608.66 5401.27 5615.82 5398.41 5615.82 5396.97 C 5614.39 5394.11 5612.96 5391.24 5610.09 5389.81 C 5607.22 5388.37 5602.92 5394.11 5601.49 5392.67 C 5595.76 5388.37 5594.32 5379.77 5588.59 5375.47 C 5578.56 5371.18 5564.23 5375.47 5558.5 5366.88 C 5552.76 5361.14 5564.23 5352.54 5564.23 5345.38 C 5562.79 5339.65 5554.2 5338.21 5554.2 5332.48 C 5554.2 5325.31 5559.93 5319.58 5561.36 5313.85 C 5562.79 5309.55 5569.96 5305.25 5567.09 5302.38 C 5557.06 5293.78 5548.46 5319.58 5538.43 5328.18 C 5518.37 5346.81 5471.07 5335.35 5475.37 5326.75 C 5479.67 5319.58 5486.84 5315.28 5486.84 5308.12 C 5486.84 5303.82 5475.37 5303.82 5476.8 5298.08 C 5476.8 5289.48 5488.27 5282.32 5488.27 5273.72 C 5485.4 5249.36 5426.64 5263.69 5423.78 5252.22 C 5422.34 5246.49 5426.64 5239.32 5425.21 5233.59 C 5420.91 5210.66 5389.38 5233.59 5380.78 5213.53 C 5375.05 5202.06 5383.65 5169.1 5365.02 5161.93 C 5356.42 5157.63 5349.25 5177.7 5337.79 5177.7 C 5306.26 5177.7 5304.82 5123.24 5301.96 5103.17 C 5303.39 5101.74 5303.39 5101.74 5303.39 5101.74 C 5329.19 5094.57 5369.32 5063.04 5402.28 5053.01 L 5405.15 5058.74 C 5402.28 5096.01 5432.38 5071.64 5453.87 5081.68 C 5456.74 5083.11 5451.01 5088.84 5453.87 5091.71 C 5471.07 5103.17 5485.4 5083.11 5496.87 5073.08 C 5505.47 5080.24 5514.07 5085.98 5516.93 5084.54 C 5521.23 5081.68 5519.8 5075.94 5516.93 5070.21 C 5521.23 5071.64 5524.1 5073.08 5528.4 5071.64 C 5538.43 5071.64 5511.2 5038.68 5545.6 5048.71 C 5548.46 5048.71 5542.73 5055.88 5544.16 5057.31 C 5561.36 5071.64 5565.66 5037.25 5578.56 5047.28 C 5582.86 5051.58 5567.09 5060.18 5571.39 5063.04 C 5604.36 5080.24 5575.69 5058.74 5601.49 5053.01 C 5605.79 5051.58 5612.96 5093.14 5622.99 5094.57 C 5645.92 5098.87 5676.01 5097.44 5694.65 5121.8 C 5696.08 5124.67 5688.91 5127.54 5687.48 5130.4 C 5684.61 5139 5677.45 5146.17 5676.01 5154.77 C 5676.01 5157.63 5683.18 5156.2 5681.75 5159.07 C 5680.31 5163.37 5673.15 5164.8 5671.72 5170.53 C 5661.68 5206.36 5686.05 5196.33 5708.98 5197.76 L 5710.41 5197.76 C 5716.14 5207.79 5724.74 5223.56 5726.18 5223.56 C 5729.04 5226.43 5737.64 5220.69 5737.64 5224.99 C 5739.07 5230.72 5727.61 5236.46 5729.04 5242.19 C 5731.91 5250.79 5744.81 5249.36 5749.11 5256.52 C 5764.87 5282.32 5726.18 5286.62 5716.14 5313.85 C 5700.38 5286.62 5671.72 5339.65 5665.98 5355.41 C 5661.68 5368.31 5704.68 5424.2 5673.15 5421.34 L 5671.72 5427.07 " }
+ }
+ }
+ // PATH visit lu count: 29
+ DemoShape {
+ objectName: "lu"
+ ShapePath {
+ objectName: "svg_path:lu"
+ // boundingRect: 5665.68, 5306.2 99.2084x135.428
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 5739.07 5441.4 C 5731.91 5438.53 5723.31 5434.23 5714.71 5434.23 C 5707.54 5434.23 5700.38 5441.4 5691.78 5441.4 C 5690.35 5442.83 5691.78 5437.1 5690.35 5435.67 C 5684.61 5432.8 5678.88 5429.93 5671.72 5427.07 L 5673.15 5421.34 C 5704.68 5424.2 5661.68 5368.31 5665.98 5355.41 C 5671.72 5339.65 5700.38 5286.62 5716.14 5313.85 C 5714.71 5321.01 5713.28 5328.18 5716.14 5339.65 C 5723.31 5369.74 5757.71 5372.61 5760.57 5376.91 C 5780.64 5401.27 5723.31 5412.74 5739.07 5441.4 " }
+ }
+ }
+ // PATH visit de count: 584
+ DemoShape {
+ objectName: "de"
+ ShapePath {
+ objectName: "svg_path:de"
+ // boundingRect: 5692.15, 4428.15 1080.38x1437.92
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6657.73 4638.83 C 6660.6 4661.76 6683.53 4706.19 6686.4 4720.52 C 6690.7 4754.91 6644.83 4777.84 6654.87 4805.07 C 6660.6 4822.27 6715.06 4829.44 6715.06 4850.94 C 6715.06 4872.43 6697.86 4879.6 6710.76 4895.36 C 6715.06 4902.53 6725.09 4905.4 6727.96 4914 C 6732.26 4921.16 6726.53 4931.19 6727.96 4938.36 C 6729.39 4942.66 6735.12 4941.23 6736.56 4944.09 C 6743.72 4954.12 6726.53 4978.49 6722.23 4987.09 C 6713.63 4999.99 6735.12 5007.15 6739.42 5020.05 C 6742.29 5027.22 6732.26 5035.81 6736.56 5041.55 C 6742.29 5050.15 6755.19 5047.28 6760.92 5053.01 C 6780.99 5071.64 6772.39 5176.26 6755.19 5179.13 C 6736.56 5183.43 6706.46 5104.61 6690.7 5149.03 C 6687.83 5156.2 6713.63 5161.93 6707.89 5166.23 C 6690.7 5180.56 6666.33 5182 6649.13 5192.03 C 6646.27 5193.46 6649.13 5200.63 6646.27 5202.06 C 6634.8 5207.79 6619.04 5199.2 6610.44 5206.36 C 6603.27 5212.09 6606.14 5223.56 6598.97 5229.29 C 6596.11 5232.16 6593.24 5222.13 6588.94 5223.56 C 6576.04 5226.43 6574.61 5243.62 6566.01 5249.36 C 6561.71 5253.66 6553.11 5246.49 6548.81 5250.79 C 6544.51 5253.66 6548.81 5262.25 6544.51 5266.55 C 6538.78 5273.72 6523.02 5260.82 6518.72 5260.82 C 6482.89 5266.55 6477.15 5293.78 6465.69 5316.71 C 6462.82 5322.45 6462.82 5302.38 6457.09 5298.08 C 6452.79 5292.35 6444.19 5290.92 6437.03 5289.48 C 6424.13 5286.62 6451.36 5332.48 6480.02 5351.11 C 6482.89 5352.54 6492.92 5353.98 6494.35 5362.58 C 6498.65 5382.64 6480.02 5375.47 6477.15 5391.24 C 6475.72 5395.54 6507.25 5448.57 6511.55 5452.87 C 6535.91 5477.23 6541.65 5460.03 6557.41 5475.8 C 6567.44 5485.83 6568.88 5501.59 6578.91 5510.19 C 6601.84 5525.96 6663.47 5547.45 6662.03 5584.72 C 6660.6 5623.41 6652 5614.81 6621.9 5607.65 C 6619.04 5607.65 6616.17 5609.08 6614.74 5611.95 C 6611.87 5626.28 6620.47 5642.04 6606.14 5653.51 C 6593.24 5663.54 6533.05 5677.87 6531.61 5700.8 C 6531.61 5700.8 6566.01 5748.1 6566.01 5749.53 C 6567.44 5750.96 6547.38 5768.16 6554.55 5773.89 C 6570.31 5788.23 6586.08 5766.73 6573.18 5814.02 C 6573.18 5816.89 6570.31 5821.19 6567.44 5821.19 C 6521.58 5808.29 6557.41 5785.36 6535.91 5783.93 C 6524.45 5783.93 6514.42 5793.96 6502.95 5792.53 C 6497.22 5791.09 6498.65 5781.06 6494.35 5779.63 C 6485.75 5776.76 6477.15 5783.93 6469.99 5781.06 C 6467.12 5779.63 6472.86 5771.03 6469.99 5771.03 C 6452.79 5772.46 6468.56 5795.39 6464.26 5796.83 C 6444.19 5802.56 6411.23 5793.96 6392.6 5801.12 C 6386.87 5803.99 6389.73 5814.02 6384 5816.89 C 6378.27 5821.19 6371.1 5814.02 6365.37 5818.32 C 6361.07 5819.76 6366.8 5826.92 6363.93 5829.79 C 6362.5 5831.22 6313.77 5844.12 6308.04 5841.25 C 6305.17 5838.39 6308.04 5831.22 6305.17 5828.35 C 6303.74 5825.49 6296.58 5825.49 6295.14 5822.62 C 6293.71 5819.76 6302.31 5821.19 6300.88 5818.32 C 6295.14 5799.69 6255.01 5822.62 6243.55 5809.72 C 6243.55 5809.72 6246.41 5808.29 6246.41 5806.86 C 6237.82 5801.12 6246.41 5825.49 6243.55 5835.52 C 6243.55 5841.25 6216.32 5874.22 6206.29 5864.18 C 6200.55 5858.45 6216.32 5849.85 6212.02 5844.12 C 6210.59 5842.69 6206.29 5848.42 6203.42 5848.42 C 6200.55 5848.42 6196.25 5846.99 6194.82 5844.12 C 6191.95 5839.82 6201.99 5834.09 6194.82 5828.35 C 6189.09 5824.06 6186.22 5816.89 6180.49 5814.02 C 6176.19 5811.16 6170.46 5818.32 6164.72 5816.89 C 6161.86 5814.02 6164.72 5806.86 6161.86 5803.99 C 6154.69 5798.26 6156.13 5824.06 6133.19 5816.89 C 6117.43 5811.16 6107.4 5795.39 6091.63 5791.09 C 6090.2 5791.09 6042.91 5793.96 6042.91 5793.96 C 6037.17 5791.09 6037.17 5781.06 6031.44 5778.19 C 6030.01 5775.33 6034.31 5785.36 6031.44 5786.79 C 6028.57 5788.23 6022.84 5786.79 6021.41 5783.93 C 6019.97 5779.63 6027.14 5773.89 6024.27 5771.03 C 6002.78 5749.53 5978.41 5788.23 5982.71 5789.66 C 5991.31 5791.09 5999.91 5788.23 6008.51 5789.66 C 6014.24 5791.09 5998.48 5792.53 5994.18 5795.39 C 5991.31 5796.83 5995.61 5803.99 5994.18 5805.42 C 5958.35 5824.06 5984.15 5796.83 5955.48 5798.26 C 5951.18 5798.26 5885.26 5814.02 5880.96 5805.42 C 5879.52 5802.56 5889.56 5801.12 5888.12 5799.69 C 5875.22 5789.66 5869.49 5832.65 5869.49 5782.49 C 5868.06 5762.43 5882.39 5743.8 5883.82 5723.73 C 5883.82 5720.87 5878.09 5720.87 5878.09 5716.57 C 5876.66 5709.4 5879.52 5702.24 5882.39 5695.07 C 5882.39 5695.07 5902.46 5662.11 5902.46 5662.11 C 5903.89 5653.51 5905.32 5621.98 5909.62 5611.95 C 5921.09 5589.02 5949.75 5571.82 5965.51 5544.59 C 5971.25 5534.56 5885.26 5524.52 5879.52 5520.22 C 5875.22 5515.92 5878.09 5507.33 5873.79 5503.03 C 5866.63 5497.29 5820.76 5515.92 5817.9 5511.63 C 5816.47 5507.33 5822.2 5501.59 5819.33 5498.73 C 5776.34 5460.03 5805 5518.79 5787.8 5500.16 C 5773.47 5485.83 5766.3 5452.87 5754.84 5445.7 C 5750.54 5441.4 5741.94 5445.7 5739.07 5441.4 C 5723.31 5412.74 5780.64 5401.27 5760.57 5376.91 C 5757.71 5372.61 5723.31 5369.74 5716.14 5339.65 C 5703.24 5286.62 5769.17 5289.48 5749.11 5256.52 C 5744.81 5249.36 5731.91 5250.79 5729.04 5242.19 C 5727.61 5236.46 5739.07 5230.72 5737.64 5224.99 C 5737.64 5220.69 5729.04 5226.43 5726.18 5223.56 C 5724.74 5222.13 5704.68 5192.03 5706.11 5187.73 C 5707.54 5182 5717.58 5182 5719.01 5176.26 C 5723.31 5151.9 5681.75 5143.3 5694.65 5140.44 C 5698.95 5139 5701.81 5144.73 5704.68 5144.73 C 5704.68 5144.73 5731.91 5120.37 5731.91 5120.37 C 5733.34 5113.21 5714.71 5121.8 5720.44 5110.34 C 5724.74 5100.31 5734.77 5093.14 5739.07 5083.11 C 5751.97 5051.58 5720.44 5028.65 5711.84 5012.88 C 5710.41 5010.02 5713.28 5005.72 5714.71 5002.85 C 5714.71 4999.99 5711.84 4995.69 5714.71 4994.25 C 5721.88 4989.95 5730.48 4994.25 5739.07 4991.39 C 5741.94 4989.95 5736.21 4981.35 5739.07 4982.79 C 5747.67 4985.65 5753.41 4995.69 5763.44 4995.69 C 5773.47 4997.12 5812.17 4984.22 5816.47 4974.19 C 5820.76 4964.16 5789.24 4962.72 5802.13 4954.12 C 5825.06 4938.36 5852.29 4932.63 5846.56 4896.8 C 5845.13 4876.73 5792.1 4885.33 5805 4863.83 C 5806.43 4859.54 5813.6 4863.83 5813.6 4860.97 C 5815.03 4856.67 5807.87 4852.37 5809.3 4848.07 C 5809.3 4845.2 5813.6 4845.2 5816.47 4845.2 C 5826.5 4845.2 5835.1 4849.5 5845.13 4849.5 C 5856.59 4849.5 5846.56 4826.57 5850.86 4815.11 C 5853.73 4805.07 5878.09 4759.21 5875.23 4734.85 C 5879.52 4733.42 5883.82 4730.55 5888.12 4724.82 C 5893.86 4719.08 5880.96 4721.95 5868.06 4721.95 C 5865.19 4719.08 5860.89 4720.52 5858.03 4720.52 C 5855.16 4720.52 5853.73 4719.08 5852.29 4717.65 C 5840.83 4703.32 5872.36 4658.89 5883.82 4653.16 C 5890.99 4648.86 5899.59 4657.46 5906.75 4657.46 C 6014.24 4653.16 5794.97 4644.56 5958.35 4648.86 C 5959.78 4648.86 5964.08 4650.29 5965.51 4651.73 C 5974.11 4667.49 5979.85 4733.42 6015.68 4719.08 C 6027.14 4714.79 6004.21 4615.9 6037.17 4615.9 C 6040.04 4615.9 6040.04 4624.5 6044.34 4625.93 C 6048.64 4628.8 6055.8 4628.8 6061.54 4628.8 C 6075.87 4631.66 6091.63 4617.33 6104.53 4623.06 C 6114.56 4627.36 6114.56 4641.69 6120.3 4650.29 C 6127.46 4661.76 6166.16 4734.85 6166.16 4686.12 C 6166.16 4676.09 6144.66 4678.96 6136.06 4670.36 C 6121.73 4657.46 6124.6 4624.5 6097.37 4620.2 C 6093.07 4620.2 6060.1 4627.36 6062.97 4607.3 C 6064.4 4594.4 6084.47 4554.27 6071.57 4538.51 C 6067.27 4534.21 6035.74 4549.97 6031.44 4534.21 C 6030.01 4531.34 6040.04 4534.21 6041.47 4531.34 C 6044.34 4528.47 6031.44 4522.74 6034.31 4522.74 C 6048.64 4518.44 6073 4532.77 6077.3 4504.11 C 6077.3 4501.24 6044.34 4465.41 6041.47 4459.68 C 6037.17 4449.65 6035.74 4438.18 6035.74 4428.15 C 6045.77 4429.59 6062.97 4428.15 6067.27 4428.15 C 6085.9 4431.02 6104.53 4441.05 6123.16 4443.92 C 6121.73 4448.22 6121.73 4449.65 6124.6 4448.22 C 6130.33 4443.92 6133.19 4436.75 6138.93 4435.32 C 6141.79 4435.32 6137.49 4442.48 6138.93 4445.35 C 6143.23 4448.22 6148.96 4446.78 6153.26 4448.22 C 6163.29 4449.65 6173.32 4449.65 6180.49 4453.95 C 6184.79 4456.82 6186.22 4462.55 6186.22 4466.85 C 6186.22 4468.28 6183.36 4466.85 6183.36 4466.85 C 6180.49 4468.28 6174.76 4471.15 6177.62 4472.58 C 6179.06 4475.45 6186.22 4469.71 6187.66 4472.58 C 6191.95 4479.75 6190.52 4488.35 6187.66 4495.51 C 6184.79 4502.68 6164.72 4506.98 6170.46 4514.14 C 6177.62 4524.17 6204.85 4498.38 6206.29 4511.28 C 6207.72 4519.87 6191.95 4535.64 6200.55 4538.51 C 6210.59 4542.81 6210.59 4518.44 6220.62 4517.01 C 6229.22 4515.58 6262.18 4544.24 6269.35 4541.37 C 6270.78 4541.37 6299.44 4508.41 6309.47 4525.61 C 6329.54 4562.87 6229.22 4601.57 6283.68 4607.3 C 6295.14 4608.73 6303.74 4594.4 6315.21 4594.4 C 6329.54 4595.83 6313.77 4637.39 6345.3 4615.9 C 6352.47 4611.6 6356.77 4575.77 6369.67 4568.6 C 6388.3 4558.57 6408.36 4554.27 6416.96 4574.34 C 6415.53 4570.04 6414.1 4562.87 6415.53 4561.44 C 6424.13 4551.4 6435.59 4547.1 6442.76 4538.51 C 6451.36 4528.47 6452.79 4512.71 6461.39 4502.68 C 6464.26 4499.81 6482.89 4506.98 6500.09 4512.71 C 6507.25 4511.28 6514.42 4509.84 6517.28 4509.84 C 6527.32 4509.84 6521.58 4529.91 6527.32 4538.51 C 6531.61 4544.24 6541.65 4541.37 6545.95 4547.1 C 6554.55 4554.27 6558.84 4574.34 6571.74 4572.9 C 6581.78 4572.9 6593.24 4565.74 6604.71 4568.6 C 6609.01 4570.04 6598.97 4577.2 6598.97 4581.5 C 6600.41 4592.97 6604.71 4604.43 6609.01 4615.9 C 6613.31 4627.36 6627.64 4631.66 6639.1 4635.96 C 6647.7 4638.83 6653.43 4640.26 6657.73 4638.83 M 6527.32 4528.47 C 6530.18 4515.58 6524.45 4499.81 6528.75 4486.91 C 6533.05 4474.01 6576.04 4511.28 6567.44 4498.38 C 6561.71 4489.78 6543.08 4494.08 6538.78 4484.05 C 6537.35 4475.45 6550.25 4468.28 6555.98 4461.12 C 6561.71 4456.82 6551.68 4478.31 6557.41 4479.75 C 6567.44 4482.61 6577.48 4478.31 6586.08 4481.18 C 6594.67 4484.05 6574.61 4495.51 6577.48 4504.11 C 6580.34 4514.14 6593.24 4515.58 6597.54 4525.61 C 6600.41 4528.47 6581.78 4542.81 6558.84 4548.54 C 6547.38 4552.84 6537.35 4535.64 6527.32 4528.47 " }
+ }
+ }
+ // PATH visit fr count: 891
+ DemoShape {
+ objectName: "fr"
+ ShapePath {
+ objectName: "svg_path:fr"
+ // boundingRect: 4338.59, 5103.17 1784.64x1875.74
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 5301.96 5103.17 C 5304.82 5123.24 5306.26 5177.7 5337.79 5177.7 C 5349.25 5177.7 5356.42 5157.63 5365.02 5161.93 C 5383.65 5169.1 5375.05 5202.06 5380.78 5213.53 C 5389.38 5233.59 5420.91 5210.66 5425.21 5233.59 C 5426.64 5239.32 5422.34 5246.49 5423.78 5252.22 C 5426.64 5263.69 5485.4 5249.36 5488.27 5273.72 C 5488.27 5282.32 5476.8 5289.48 5476.8 5298.08 C 5475.37 5303.82 5486.84 5303.82 5486.84 5308.12 C 5486.84 5315.28 5479.67 5319.58 5475.37 5326.75 C 5471.07 5335.35 5518.37 5346.81 5538.43 5328.18 C 5548.46 5319.58 5557.06 5293.78 5567.09 5302.38 C 5569.96 5305.25 5562.79 5309.55 5561.36 5313.85 C 5559.93 5319.58 5554.2 5325.31 5554.2 5332.48 C 5554.2 5338.21 5562.79 5339.65 5564.23 5345.38 C 5564.23 5352.54 5552.76 5361.14 5558.5 5366.88 C 5564.23 5375.47 5578.56 5371.18 5588.59 5375.47 C 5594.32 5379.77 5595.76 5388.37 5601.49 5392.67 C 5602.92 5394.11 5607.22 5388.37 5610.09 5389.81 C 5612.96 5391.24 5614.39 5394.11 5615.82 5396.97 C 5615.82 5398.41 5608.66 5401.27 5610.09 5402.7 C 5614.39 5405.57 5621.55 5402.7 5624.42 5407 C 5628.72 5412.74 5622.99 5425.64 5630.15 5428.5 C 5638.75 5432.8 5648.78 5422.77 5658.82 5424.2 C 5663.12 5424.2 5667.42 5425.64 5671.72 5427.07 C 5678.88 5429.93 5684.61 5432.8 5690.35 5435.67 C 5691.78 5437.1 5690.35 5442.83 5691.78 5441.4 C 5700.38 5441.4 5707.54 5434.23 5714.71 5434.23 C 5723.31 5434.23 5731.91 5438.53 5739.07 5441.4 C 5741.94 5445.7 5750.54 5441.4 5754.84 5445.7 C 5766.3 5452.87 5773.47 5485.83 5787.8 5500.16 C 5805 5518.79 5776.34 5460.03 5819.33 5498.73 C 5822.2 5501.59 5816.47 5507.33 5817.9 5511.63 C 5820.76 5515.92 5866.63 5497.29 5873.79 5503.03 C 5878.09 5507.33 5875.23 5515.92 5879.52 5520.22 C 5885.26 5524.52 5971.25 5534.56 5965.51 5544.59 C 5949.75 5571.82 5921.09 5589.02 5909.62 5611.95 C 5905.32 5621.98 5903.89 5653.51 5902.46 5662.11 L 5882.39 5695.07 C 5879.52 5702.24 5876.66 5709.4 5878.09 5716.57 C 5878.09 5720.87 5883.82 5720.87 5883.82 5723.73 C 5882.39 5743.8 5868.06 5762.43 5869.49 5782.49 C 5869.49 5801.12 5869.49 5806.86 5870.93 5806.86 C 5822.2 5857.02 5839.4 5802.56 5799.27 5814.02 C 5796.4 5815.46 5802.13 5821.19 5802.13 5824.06 C 5802.13 5825.49 5799.27 5824.06 5799.27 5824.06 C 5794.97 5829.79 5783.5 5835.52 5786.37 5842.69 C 5787.8 5849.85 5809.3 5835.52 5809.3 5842.69 C 5807.87 5862.75 5800.7 5846.99 5793.53 5852.72 C 5780.64 5862.75 5769.17 5891.41 5759.14 5900.01 C 5750.54 5910.05 5730.48 5905.75 5724.74 5917.21 C 5720.44 5927.24 5729.04 5940.14 5724.74 5948.74 C 5719.01 5964.51 5693.21 5964.51 5683.18 5978.84 C 5665.98 6000.33 5684.61 6020.4 5677.45 6043.33 C 5676.01 6047.63 5661.68 6040.46 5661.68 6059.09 C 5661.68 6061.96 5654.52 6064.83 5655.95 6064.83 C 5673.15 6069.13 5693.21 6061.96 5701.81 6044.76 C 5703.24 6041.9 5696.08 6039.03 5694.65 6036.16 C 5694.65 6031.86 5693.21 6029 5696.08 6026.13 C 5708.98 6006.07 5749.11 6006.07 5767.74 6014.67 C 5773.47 6016.1 5762 6024.7 5763.44 6029 C 5763.44 6036.16 5772.04 6039.03 5773.47 6046.2 C 5774.9 6054.8 5754.84 6053.36 5763.44 6067.69 C 5766.3 6070.56 5774.9 6066.26 5776.34 6070.56 C 5779.2 6074.86 5772.04 6082.03 5773.47 6086.32 C 5774.9 6089.19 5780.64 6080.59 5782.07 6083.46 C 5789.23 6090.62 5789.23 6100.66 5794.97 6109.26 C 5729.04 6147.95 5766.3 6130.75 5786.37 6165.15 C 5789.23 6170.88 5783.5 6180.91 5787.8 6186.65 C 5792.1 6196.68 5806.43 6198.11 5810.73 6206.71 C 5817.9 6219.61 5772.04 6256.87 5764.87 6258.3 C 5760.57 6259.74 5757.71 6251.14 5753.41 6251.14 C 5746.24 6252.57 5733.34 6252.57 5733.34 6259.74 C 5730.48 6282.67 5777.77 6301.3 5784.94 6312.77 C 5789.23 6321.36 5790.67 6332.83 5790.67 6344.29 C 5789.23 6348.59 5776.34 6341.43 5774.9 6345.73 C 5773.47 6348.59 5777.77 6351.46 5776.34 6352.89 C 5772.04 6360.06 5764.87 6364.36 5759.14 6370.09 C 5754.84 6378.69 5769.17 6382.99 5770.6 6387.29 C 5770.6 6390.16 5763.44 6390.16 5763.44 6393.02 C 5762 6413.09 5799.27 6440.32 5815.03 6444.62 C 5823.63 6447.48 5833.66 6443.18 5842.26 6443.18 C 5850.86 6441.75 5860.89 6436.02 5868.06 6440.32 C 5889.56 6456.08 5840.83 6489.04 5847.99 6514.84 C 5846.56 6514.84 5822.2 6523.44 5819.33 6526.31 C 5817.9 6527.74 5822.2 6533.47 5819.33 6533.47 C 5817.9 6532.04 5819.33 6526.31 5817.9 6526.31 C 5809.3 6527.74 5799.27 6529.17 5793.53 6534.91 C 5789.23 6539.21 5796.4 6549.24 5790.67 6553.54 C 5784.94 6557.84 5774.9 6552.1 5767.74 6556.4 C 5762 6560.7 5763.44 6572.17 5756.27 6577.9 C 5750.54 6580.77 5743.37 6573.6 5737.64 6576.47 C 5733.34 6577.9 5736.21 6585.07 5731.91 6589.37 C 5727.61 6595.1 5716.14 6596.53 5713.28 6603.7 C 5711.84 6609.43 5724.74 6618.03 5719.01 6622.33 C 5711.84 6629.49 5697.51 6622.33 5687.48 6623.76 C 5674.58 6626.63 5665.98 6639.53 5654.52 6643.83 C 5641.62 6648.13 5651.65 6628.06 5622.99 6626.63 C 5604.36 6625.2 5624.42 6633.79 5621.55 6636.66 C 5618.69 6639.53 5612.96 6639.53 5608.66 6638.09 C 5604.36 6638.09 5600.06 6638.09 5598.62 6635.23 C 5598.62 6632.36 5608.66 6625.2 5588.59 6615.16 C 5584.29 6613.73 5579.99 6615.16 5574.26 6615.16 C 5564.23 6612.3 5552.76 6613.73 5545.6 6606.56 C 5538.43 6599.4 5545.6 6585.07 5539.86 6577.9 C 5537 6573.6 5531.27 6583.63 5525.53 6583.63 C 5516.93 6583.63 5499.74 6586.5 5501.17 6577.9 C 5502.6 6566.44 5528.4 6570.73 5529.83 6559.27 C 5529.83 6547.8 5514.07 6539.21 5504.04 6540.64 C 5495.44 6542.07 5511.2 6557.84 5506.9 6563.57 C 5499.74 6570.73 5488.27 6575.03 5478.24 6573.6 C 5455.31 6572.17 5462.47 6532.04 5458.17 6537.77 C 5438.11 6560.7 5482.54 6570.73 5469.64 6576.47 C 5423.78 6595.1 5443.84 6565 5442.41 6559.27 C 5440.98 6553.54 5390.81 6547.8 5386.52 6544.94 C 5375.05 6539.21 5360.72 6520.57 5346.39 6529.17 C 5330.62 6537.77 5307.69 6566.44 5291.93 6575.03 C 5277.59 6582.2 5248.93 6576.47 5231.73 6600.83 C 5228.87 6603.7 5236.03 6609.43 5236.03 6613.73 C 5233.17 6628.06 5214.54 6636.66 5213.1 6652.43 C 5213.1 6656.72 5223.13 6655.29 5224.57 6659.59 C 5231.73 6685.39 5213.1 6678.22 5223.13 6706.89 C 5226 6715.48 5234.6 6722.65 5243.2 6729.82 L 5237.47 6729.82 C 5214.54 6724.08 5188.74 6711.19 5167.24 6725.52 C 5162.94 6728.38 5168.67 6739.85 5162.94 6741.28 C 5148.61 6745.58 5124.25 6716.92 5109.91 6716.92 C 5097.02 6716.92 5086.98 6724.08 5075.52 6725.52 C 5066.92 6726.95 5065.49 6711.19 5062.62 6708.32 C 5055.45 6702.59 5043.99 6699.72 5033.96 6695.42 C 5048.29 6676.79 5045.42 6658.16 5008.16 6661.02 C 5006.73 6661.02 5002.43 6666.76 4999.56 6673.92 L 4999.56 6672.49 C 4992.39 6668.19 4996.69 6655.29 4989.53 6649.56 C 4985.23 6645.26 4976.63 6645.26 4969.46 6643.83 C 4966.6 6643.83 4962.3 6645.26 4960.87 6643.83 C 4945.1 6629.49 4930.77 6612.3 4906.4 6608 C 4889.21 6605.13 4896.37 6638.09 4896.37 6638.09 C 4880.61 6636.66 4867.71 6625.2 4853.38 6626.63 C 4849.08 6626.63 4849.08 6635.23 4844.78 6635.23 C 4836.18 6633.79 4831.88 6623.76 4823.28 6622.33 C 4814.68 6620.9 4808.95 6629.49 4800.35 6628.06 C 4781.72 6628.06 4788.89 6609.43 4778.85 6600.83 C 4758.79 6583.63 4740.16 6609.43 4721.53 6595.1 C 4720.09 6595.1 4705.76 6562.14 4704.33 6560.7 C 4685.7 6546.37 4654.17 6553.54 4634.1 6534.91 C 4631.24 6532.04 4635.54 6524.87 4634.1 6522.01 C 4631.24 6519.14 4622.64 6534.91 4614.04 6532.04 C 4595.41 6523.44 4636.97 6506.24 4624.07 6491.91 C 4619.77 6486.18 4611.17 6483.31 4604.01 6481.88 C 4602.57 6481.88 4604.01 6489.04 4601.14 6487.61 C 4601.14 6487.61 4585.38 6473.28 4576.78 6464.68 C 4585.38 6463.25 4591.11 6461.81 4596.84 6458.95 C 4638.4 6438.88 4655.6 6301.3 4671.37 6258.3 C 4674.23 6251.14 4677.1 6241.11 4684.26 6238.24 C 4690 6235.37 4702.9 6248.27 4705.76 6241.11 C 4710.06 6233.94 4700.03 6222.48 4691.43 6221.04 C 4684.26 6219.61 4682.83 6239.67 4675.67 6236.81 C 4661.33 6232.51 4682.83 6209.58 4685.7 6195.25 C 4691.43 6172.31 4707.19 6087.76 4722.96 6076.29 C 4728.69 6073.43 4748.76 6104.96 4754.49 6119.29 C 4774.55 6162.28 4747.32 6122.15 4761.66 6168.02 C 4764.52 6176.61 4773.12 6183.78 4775.99 6192.38 C 4777.42 6196.68 4771.69 6209.58 4775.99 6208.14 C 4783.15 6206.71 4787.45 6196.68 4786.02 6190.95 C 4783.15 6182.35 4773.12 6179.48 4767.39 6172.31 C 4755.92 6153.68 4771.69 6119.29 4760.22 6097.79 C 4754.49 6086.32 4744.46 6076.29 4735.86 6066.26 C 4730.13 6059.09 4722.96 6051.93 4714.36 6046.2 C 4722.96 6049.06 4732.99 6051.93 4741.59 6047.63 C 4751.62 6041.9 4720.09 6027.57 4722.96 6016.1 C 4725.83 6004.63 4738.72 5998.9 4740.16 5987.44 C 4741.59 5978.84 4731.56 5971.67 4731.56 5963.07 C 4731.56 5953.04 4740.16 5943.01 4740.16 5932.98 C 4740.16 5930.11 4732.99 5930.11 4728.69 5931.54 C 4725.83 5932.98 4728.69 5941.58 4725.83 5940.14 C 4718.66 5937.28 4717.23 5925.81 4711.49 5921.51 C 4707.19 5920.08 4701.46 5925.81 4697.16 5922.94 C 4692.86 5920.08 4697.16 5910.05 4692.86 5907.18 C 4667.07 5882.82 4681.4 5935.84 4657.03 5888.55 C 4655.6 5884.25 4654.17 5852.72 4648.44 5845.55 C 4647 5844.12 4642.7 5845.55 4641.27 5844.12 C 4635.54 5835.52 4628.37 5825.49 4624.07 5815.46 C 4618.34 5801.12 4647 5786.79 4649.87 5781.06 C 4655.6 5768.16 4621.2 5759.56 4621.2 5758.13 C 4618.34 5748.1 4618.34 5742.37 4619.77 5738.07 L 4618.34 5738.07 C 4559.58 5732.33 4614.04 5718 4615.47 5686.47 C 4616.91 5667.84 4556.71 5683.61 4555.28 5676.44 C 4552.41 5664.97 4569.61 5660.67 4578.21 5654.94 C 4579.64 5653.51 4571.04 5657.81 4569.61 5656.38 C 4568.18 5654.94 4569.61 5650.64 4568.18 5649.21 C 4563.88 5649.21 4562.45 5653.51 4558.15 5654.94 C 4546.68 5654.94 4535.21 5646.34 4525.18 5650.64 C 4523.75 5652.08 4522.32 5653.51 4522.32 5654.94 L 4522.32 5653.51 C 4525.18 5634.88 4539.51 5616.25 4509.42 5613.38 C 4493.65 5611.95 4497.95 5632.01 4486.49 5624.85 C 4477.89 5619.11 4466.42 5600.48 4457.82 5596.18 C 4452.09 5594.75 4444.93 5600.48 4440.63 5596.18 C 4421.99 5581.85 4444.93 5564.65 4409.1 5567.52 C 4406.23 5568.95 4411.96 5576.12 4409.1 5577.55 C 4399.06 5581.85 4384.73 5590.45 4376.13 5583.28 C 4367.53 5577.55 4376.13 5561.79 4373.27 5551.75 C 4368.97 5534.56 4350.34 5538.86 4338.87 5531.69 C 4337.44 5530.26 4341.74 5525.96 4344.6 5525.96 C 4358.94 5523.09 4373.27 5524.52 4387.6 5524.52 C 4390.47 5524.52 4397.63 5530.26 4397.63 5527.39 C 4397.63 5524.52 4384.73 5504.46 4376.13 5490.13 L 4377.57 5491.56 C 4384.73 5494.43 4393.33 5491.56 4401.93 5492.99 C 4407.66 5492.99 4420.56 5498.73 4416.26 5495.86 C 4406.23 5490.13 4389.03 5492.99 4384.73 5482.96 C 4381.87 5474.36 4404.8 5477.23 4406.23 5468.63 C 4410.53 5451.43 4320.24 5517.36 4350.34 5439.97 C 4351.77 5439.97 4399.06 5421.34 4411.96 5421.34 C 4427.73 5421.34 4391.9 5439.97 4426.29 5429.93 C 4436.33 5428.5 4446.36 5417.04 4456.39 5419.9 C 4464.99 5422.77 4460.69 5439.97 4469.29 5442.83 C 4475.02 5442.83 4470.72 5425.64 4476.46 5424.2 C 4486.49 5424.2 4493.65 5442.83 4503.69 5439.97 C 4512.28 5437.1 4506.55 5418.47 4515.15 5412.74 C 4518.02 5409.87 4523.75 5418.47 4528.05 5418.47 C 4536.65 5418.47 4546.68 5409.87 4553.85 5414.17 C 4561.01 5418.47 4555.28 5432.8 4562.45 5437.1 C 4566.74 5439.97 4566.74 5429.93 4571.04 5427.07 C 4578.21 5421.34 4601.14 5482.96 4606.87 5494.43 C 4608.31 5495.86 4608.31 5488.69 4611.17 5488.69 C 4619.77 5482.96 4628.37 5481.53 4638.4 5477.23 C 4645.57 5472.93 4651.3 5461.46 4658.47 5464.33 C 4671.37 5470.06 4659.9 5520.22 4694.3 5505.89 C 4704.33 5501.59 4698.6 5482.96 4705.76 5472.93 C 4712.93 5462.9 4714.36 5492.99 4722.96 5494.43 C 4731.56 5495.86 4738.72 5491.56 4747.32 5492.99 C 4755.92 5492.99 4773.12 5505.89 4775.99 5497.29 C 4778.85 5481.53 4745.89 5470.06 4750.19 5454.3 C 4753.06 5444.27 4755.92 5434.23 4760.22 5424.2 C 4757.36 5424.2 4754.49 5422.77 4755.92 5417.04 C 4758.79 5407 4767.39 5395.54 4767.39 5384.07 C 4767.39 5381.21 4758.79 5385.51 4757.36 5382.64 C 4754.49 5375.47 4757.36 5364.01 4753.06 5356.84 C 4751.62 5352.54 4743.02 5352.54 4740.16 5348.24 C 4737.29 5339.65 4735.86 5328.18 4737.29 5319.58 C 4737.29 5315.28 4744.46 5313.85 4744.46 5310.98 C 4744.46 5300.95 4738.72 5290.92 4737.29 5280.89 C 4737.29 5278.02 4740.16 5286.62 4741.59 5288.05 C 4753.06 5293.78 4763.09 5299.52 4774.55 5302.38 C 4777.42 5303.82 4781.72 5300.95 4784.59 5300.95 C 4796.05 5300.95 4807.52 5295.22 4817.55 5300.95 C 4823.28 5302.38 4820.41 5310.98 4817.55 5316.71 C 4816.12 5319.58 4811.82 5318.15 4808.95 5321.01 C 4804.65 5325.31 4820.41 5371.18 4821.85 5371.18 C 4880.61 5379.77 4796.05 5343.95 4841.91 5361.14 C 4863.41 5369.74 4890.64 5374.04 4910.7 5384.07 C 4917.87 5388.37 4922.17 5398.41 4929.34 5399.84 C 4962.3 5404.14 4955.13 5385.51 4979.5 5378.34 C 4985.23 5376.91 4990.96 5376.91 4998.13 5376.91 C 4982.36 5374.04 4968.03 5364.01 4988.1 5329.61 C 5002.43 5306.68 5098.45 5303.82 5124.25 5289.48 C 5134.28 5285.19 5141.44 5276.59 5151.48 5270.85 C 5160.08 5266.55 5175.84 5267.99 5180.14 5259.39 C 5184.44 5250.79 5168.67 5246.49 5167.24 5237.89 C 5165.81 5233.59 5167.24 5226.43 5170.11 5223.56 C 5172.97 5220.69 5178.71 5226.43 5178.71 5223.56 C 5178.71 5220.69 5171.54 5220.69 5171.54 5216.39 C 5171.54 5206.36 5183.01 5137.57 5185.87 5130.4 C 5191.6 5113.21 5238.9 5110.34 5251.8 5108.91 C 5269 5107.47 5286.19 5106.04 5301.96 5103.17 M 6105.96 6666.76 C 6126.03 6681.09 6106.97 6713.83 6106.97 6729.59 C 6113.9 6739.57 6115.28 6745.68 6118.86 6754.18 C 6123.16 6764.21 6123.16 6795.74 6123.16 6804.34 C 6124.6 6828.7 6100.23 6847.34 6100.23 6864.53 C 6100.23 6883.16 6107.4 6908.96 6091.63 6926.16 C 6090.2 6929.03 6083.03 6926.16 6083.03 6929.03 C 6083.03 6933.33 6093.07 6929.03 6091.63 6931.89 C 6091.63 6937.63 6083.03 6937.63 6080.17 6941.92 C 6075.87 6951.96 6080.17 6964.86 6073 6973.45 C 6052.94 6993.52 6058.67 6951.96 6054.37 6950.52 C 6035.74 6944.79 6024.27 6959.12 6011.38 6937.63 C 6008.51 6933.33 6017.11 6927.59 6014.24 6921.86 C 6009.94 6913.26 5992.74 6910.4 5994.18 6900.36 C 5994.18 6888.9 6017.11 6883.16 6015.68 6871.7 C 6014.24 6863.1 5991.31 6880.3 5989.88 6871.7 C 5987.01 6860.23 6009.94 6854.5 6008.51 6844.47 C 6007.08 6833 5985.58 6835.87 5982.71 6824.41 C 5981.28 6815.81 5999.91 6812.94 5999.91 6802.91 C 5999.91 6794.31 5985.58 6790.01 5981.28 6781.41 C 5979.85 6775.68 5995.61 6779.98 5998.48 6775.68 C 6001.34 6765.65 5999.91 6752.75 6005.64 6744.15 C 6007.08 6741.28 6009.94 6751.31 6012.81 6749.88 C 6019.97 6748.45 6021.41 6738.42 6028.57 6735.55 C 6034.31 6732.68 6041.47 6738.42 6047.2 6735.55 C 6054.37 6731.25 6054.37 6718.35 6061.54 6716.92 C 6070.14 6715.48 6077.3 6726.95 6087.33 6726.95 C 6095.93 6726.95 6085.9 6708.32 6087.33 6698.29 C 6088.77 6686.82 6100.23 6678.22 6105.96 6666.76 M 5774.9 6543.5 C 5786.37 6543.5 5796.4 6533.47 5796.4 6523.44 C 5796.4 6513.41 5786.37 6504.81 5774.9 6504.81 C 5763.44 6504.81 5753.41 6513.41 5753.41 6523.44 C 5753.41 6533.47 5763.44 6543.5 5774.9 6543.5 C 5786.37 6543.5 5763.44 6543.5 5774.9 6543.5 " }
+ }
+ }
+ // PATH visit es count: 617
+ DemoShape {
+ objectName: "es"
+ ShapePath {
+ objectName: "svg_path:es"
+ // boundingRect: 3543.69, 6217.38 1812.65x1726.33
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 5033.96 6695.42 C 5043.99 6699.72 5055.45 6702.59 5062.62 6708.32 C 5065.49 6711.18 5066.92 6726.95 5075.52 6725.52 C 5086.98 6724.08 5097.02 6716.92 5109.91 6716.92 C 5124.25 6716.92 5148.61 6745.58 5162.94 6741.28 C 5168.67 6739.85 5162.94 6728.38 5167.24 6725.52 C 5188.74 6711.19 5214.54 6724.08 5237.47 6729.82 L 5243.2 6729.82 C 5250.36 6736.98 5256.1 6744.15 5253.23 6752.75 C 5250.36 6764.21 5231.73 6761.35 5224.57 6768.51 C 5218.84 6774.24 5241.77 6807.21 5227.43 6831.57 C 5220.27 6847.34 5158.64 6871.7 5141.44 6878.87 C 5125.68 6884.6 5101.32 6890.33 5089.85 6900.36 C 5081.25 6906.1 5068.35 6927.59 5055.45 6930.46 C 5006.73 6944.79 4919.3 6934.76 4880.61 6966.29 C 4870.58 6974.89 4850.51 6982.05 4853.38 6994.95 C 4857.68 7004.98 4879.17 7003.55 4877.74 7013.58 C 4877.74 7017.88 4833.31 7027.91 4830.45 7029.35 C 4813.25 7042.25 4807.52 7066.61 4793.18 7080.94 C 4753.06 7122.5 4700.03 7162.63 4674.23 7215.66 C 4662.77 7237.16 4678.53 7305.95 4691.43 7324.58 C 4695.73 7330.31 4748.76 7360.41 4735.86 7369.01 C 4725.83 7377.61 4701.46 7380.47 4691.43 7387.64 C 4688.56 7390.51 4688.56 7397.67 4685.7 7399.1 C 4682.83 7401.97 4678.53 7397.67 4675.67 7397.67 C 4664.2 7400.54 4651.3 7401.97 4641.27 7409.14 C 4636.97 7413.44 4639.84 7423.47 4634.1 7427.77 C 4629.8 7430.63 4622.64 7424.9 4619.77 7427.77 C 4614.04 7434.93 4618.34 7446.4 4614.04 7452.13 C 4608.31 7457.86 4596.84 7452.13 4592.54 7459.3 C 4586.81 7467.9 4595.41 7480.79 4591.11 7489.39 C 4579.64 7518.06 4553.85 7485.09 4552.41 7530.96 C 4552.41 7542.42 4575.34 7546.72 4572.48 7556.75 C 4568.18 7566.78 4552.41 7565.35 4540.95 7563.92 C 4533.78 7563.92 4526.62 7556.75 4519.45 7555.32 C 4489.35 7546.72 4453.52 7561.05 4427.73 7578.25 C 4383.3 7606.91 4383.3 7662.81 4337.44 7685.74 C 4320.24 7695.77 4314.51 7648.48 4271.51 7659.94 C 4262.91 7662.81 4258.61 7674.27 4248.58 7677.14 C 4235.68 7681.44 4205.59 7657.07 4192.69 7655.64 C 4175.49 7651.34 4156.86 7661.37 4138.23 7658.51 C 4128.2 7655.64 4122.46 7647.04 4112.43 7644.18 C 4093.8 7637.01 4009.24 7624.11 3993.48 7626.98 C 3976.28 7631.28 3967.68 7652.77 3951.92 7659.94 C 3928.99 7668.54 3900.32 7642.74 3868.79 7658.51 C 3843 7671.41 3845.86 7682.87 3832.96 7701.5 C 3831.53 7702.94 3822.93 7694.34 3817.2 7698.64 C 3808.6 7705.8 3801.43 7717.27 3789.97 7721.57 C 3785.67 7723 3785.67 7712.97 3781.37 7710.1 C 3775.64 7705.8 3767.04 7704.37 3762.74 7700.07 C 3757.01 7692.9 3755.57 7682.87 3748.41 7677.14 C 3744.11 7672.84 3735.51 7677.14 3731.21 7672.84 C 3721.18 7662.81 3706.85 7615.51 3703.98 7601.18 C 3703.98 7599.75 3725.48 7621.25 3722.61 7608.35 C 3722.61 7604.05 3714.01 7608.35 3712.58 7605.48 C 3711.14 7602.61 3718.31 7599.75 3716.88 7596.88 C 3712.58 7572.52 3691.08 7601.18 3692.51 7559.62 C 3692.51 7559.62 3748.41 7533.82 3735.51 7530.96 C 3724.04 7528.09 3716.88 7552.45 3705.41 7549.59 C 3695.38 7548.15 3699.68 7530.96 3695.38 7522.36 C 3691.08 7513.76 3685.35 7506.59 3679.62 7499.43 C 3679.62 7499.43 3640.92 7462.16 3640.92 7462.16 C 3640.92 7460.73 3640.92 7460.73 3640.92 7459.3 C 3639.49 7462.16 3638.05 7463.6 3635.19 7463.6 C 3626.59 7465.03 3622.29 7453.56 3615.12 7449.27 C 3609.39 7446.4 3583.59 7442.1 3564.96 7442.1 C 3567.83 7381.91 3553.5 7371.87 3600.79 7330.31 C 3609.39 7323.15 3607.96 7307.38 3616.56 7303.08 C 3619.42 7301.65 3689.65 7300.22 3672.45 7271.55 C 3668.15 7264.39 3653.82 7275.85 3646.65 7271.55 C 3639.49 7265.82 3640.92 7254.35 3638.05 7245.76 C 3635.19 7237.16 3635.19 7227.12 3629.45 7219.96 C 3628.02 7217.09 3619.42 7219.96 3619.42 7217.09 C 3623.72 7207.06 3633.75 7199.89 3638.05 7189.86 C 3646.65 7172.66 3622.29 7185.56 3646.65 7165.5 C 3668.15 7148.3 3696.81 7151.17 3699.68 7113.9 C 3699.68 7111.04 3672.45 7092.41 3666.72 7080.94 C 3658.12 7062.31 3668.15 7032.21 3668.15 7029.35 C 3666.72 7027.91 3643.79 6983.49 3643.79 6982.05 C 3642.35 6979.19 3648.09 6983.49 3650.95 6983.49 C 3663.85 6987.79 3672.45 6989.22 3685.35 6992.09 C 3718.31 7000.68 3725.48 7003.55 3742.67 6970.59 C 3762.74 6930.46 3741.24 6944.79 3736.94 6914.69 C 3732.64 6887.46 3777.07 6900.36 3778.5 6880.3 C 3779.94 6865.97 3764.17 6878.87 3772.77 6861.67 C 3774.2 6858.8 3779.94 6858.8 3781.37 6854.5 C 3785.67 6844.47 3785.67 6834.44 3787.1 6824.41 C 3788.54 6812.94 3797.13 6801.47 3795.7 6790.01 C 3795.7 6790.01 3782.8 6755.61 3782.8 6755.61 C 3787.1 6751.31 3797.13 6759.91 3802.87 6757.05 C 3802.87 6757.05 3832.96 6725.52 3832.96 6725.52 C 3854.46 6714.05 3894.59 6712.62 3906.06 6681.09 C 3914.65 6652.43 3877.39 6655.29 3867.36 6653.86 C 3860.19 6652.43 3873.09 6597.97 3867.36 6595.1 C 3840.13 6580.77 3834.4 6593.67 3801.43 6580.77 C 3795.7 6577.9 3797.13 6563.57 3784.24 6572.17 C 3781.37 6575.03 3787.1 6582.2 3784.24 6585.07 C 3764.17 6602.26 3742.67 6582.2 3736.94 6580.77 C 3731.21 6577.9 3724.04 6585.07 3719.74 6582.2 C 3716.88 6580.77 3728.34 6577.9 3725.48 6575.03 C 3724.04 6569.3 3716.88 6567.87 3711.14 6567.87 C 3702.55 6566.44 3692.51 6569.3 3683.91 6567.87 C 3682.48 6566.44 3685.35 6563.57 3685.35 6560.7 C 3685.35 6559.27 3686.78 6553.54 3685.35 6554.97 C 3678.18 6556.4 3660.98 6576.47 3648.09 6567.87 C 3613.69 6543.5 3681.05 6537.77 3663.85 6520.57 C 3660.98 6517.71 3655.25 6520.57 3652.38 6517.71 C 3650.95 6514.84 3658.12 6509.11 3655.25 6507.68 C 3629.45 6501.94 3593.63 6510.54 3572.13 6522.01 C 3574.99 6510.54 3559.23 6500.51 3559.23 6489.04 C 3557.8 6484.74 3566.4 6494.78 3569.26 6491.91 C 3572.13 6490.48 3566.4 6484.74 3567.83 6481.88 C 3570.69 6468.98 3592.19 6473.28 3602.22 6464.68 C 3605.09 6463.25 3610.82 6456.08 3606.52 6457.51 C 3597.92 6458.95 3589.33 6467.55 3580.73 6468.98 C 3573.56 6470.41 3570.69 6457.51 3576.43 6451.78 C 3585.03 6446.05 3603.66 6450.35 3606.52 6440.32 C 3607.96 6431.72 3587.89 6443.18 3580.73 6440.32 C 3574.99 6437.45 3570.69 6430.28 3569.26 6424.55 C 3567.83 6410.22 3582.16 6428.85 3583.59 6427.42 C 3586.46 6421.69 3583.59 6414.52 3587.89 6408.79 C 3592.19 6401.62 3605.09 6400.19 3606.52 6391.59 C 3613.69 6321.36 3556.36 6401.62 3544.9 6332.83 C 3536.3 6285.53 3576.43 6299.87 3577.86 6292.7 C 3579.29 6288.4 3566.4 6292.7 3562.1 6289.83 C 3560.66 6286.97 3566.4 6284.1 3567.83 6284.1 C 3583.59 6281.24 3592.19 6285.53 3603.66 6284.1 C 3640.92 6281.24 3691.08 6249.71 3725.48 6235.37 C 3738.37 6229.64 3752.71 6211.01 3765.61 6219.61 C 3771.34 6223.91 3757.01 6241.11 3764.17 6242.54 C 3775.64 6243.97 3782.8 6223.91 3794.27 6223.91 C 3832.96 6223.91 3830.1 6291.27 3865.93 6299.87 C 3873.09 6301.3 3875.96 6285.53 3883.12 6286.97 C 3923.25 6297 3901.76 6298.43 3941.88 6304.17 C 3951.92 6305.6 3970.55 6301.3 3979.15 6302.73 C 3987.75 6305.6 3992.05 6314.2 4000.64 6314.2 C 4012.11 6314.2 4022.14 6299.87 4033.61 6302.73 C 4043.64 6305.6 4045.07 6322.8 4053.67 6329.96 C 4062.27 6337.13 4079.47 6328.53 4088.07 6337.13 C 4090.93 6340 4082.33 6344.29 4085.2 6345.73 C 4088.07 6350.03 4096.67 6340 4100.97 6342.86 C 4136.8 6367.23 4229.95 6413.09 4277.25 6398.75 C 4280.11 6397.32 4280.11 6391.59 4282.98 6390.16 C 4288.71 6388.72 4294.44 6390.16 4300.18 6390.16 C 4311.64 6391.59 4341.74 6388.72 4350.34 6405.92 C 4350.34 6407.35 4348.9 6410.22 4347.47 6411.65 C 4358.94 6414.52 4390.47 6444.62 4401.93 6444.62 C 4406.23 6444.62 4399.06 6434.58 4401.93 6431.72 C 4404.8 6427.42 4410.53 6425.99 4416.26 6424.55 C 4457.82 6418.82 4430.59 6423.12 4447.79 6443.18 C 4453.52 6448.92 4464.99 6440.32 4470.72 6444.62 C 4479.32 6448.92 4483.62 6458.95 4490.79 6463.25 C 4509.42 6473.28 4549.55 6471.85 4576.78 6464.68 C 4585.38 6473.28 4601.14 6487.61 4601.14 6487.61 C 4604.01 6489.04 4602.57 6481.88 4604.01 6481.88 C 4611.17 6483.31 4619.77 6486.18 4624.07 6491.91 C 4636.97 6506.24 4595.41 6523.44 4614.04 6532.04 C 4622.64 6534.91 4631.24 6519.14 4634.1 6522.01 C 4635.54 6524.87 4631.24 6532.04 4634.1 6534.91 C 4654.17 6553.54 4685.7 6546.37 4704.33 6560.7 C 4705.76 6562.14 4720.09 6595.1 4721.53 6595.1 C 4740.16 6609.43 4758.79 6583.63 4778.85 6600.83 C 4788.89 6609.43 4781.72 6628.06 4800.35 6628.06 C 4808.95 6629.49 4814.68 6620.9 4823.28 6622.33 C 4831.88 6623.76 4836.18 6633.79 4844.78 6635.23 C 4849.08 6635.23 4849.08 6626.63 4853.38 6626.63 C 4867.71 6625.19 4880.61 6636.66 4896.37 6638.09 C 4896.37 6638.09 4889.21 6605.13 4906.4 6608 C 4930.77 6612.3 4945.1 6629.49 4960.87 6643.83 C 4962.3 6645.26 4966.6 6643.83 4969.46 6643.83 C 4976.63 6645.26 4985.23 6645.26 4989.53 6649.56 C 4996.69 6655.29 4992.39 6668.19 4999.56 6672.49 L 4999.56 6673.92 C 4992.39 6688.25 4989.53 6708.32 5033.96 6695.42 M 3785.67 7755.96 L 3795.7 7797.52 L 3827.23 7797.52 C 3827.23 7793.23 3825.8 7790.36 3825.8 7786.06 C 3824.36 7777.46 3821.5 7768.86 3824.36 7760.26 C 3825.8 7755.96 3838.7 7754.53 3837.26 7750.23 C 3828.66 7730.17 3788.54 7754.53 3785.67 7755.96 M 4155.43 7929.38 L 4176.92 7943.71 C 4176.92 7930.81 4179.79 7916.48 4178.36 7905.01 C 4175.49 7893.55 4164.03 7922.21 4155.43 7929.38 M 4182.66 7940.84 L 4185.52 7936.54 C 4184.09 7936.54 4182.66 7936.54 4182.66 7939.41 C 4182.66 7940.84 4182.66 7940.84 4182.66 7940.84 M 5193.04 7188.43 C 5171.54 7261.52 5197.34 7209.93 5233.17 7240.02 C 5243.2 7248.62 5170.11 7318.85 5164.37 7317.41 C 5157.21 7317.41 5158.64 7301.65 5151.48 7297.35 C 5129.98 7284.45 5128.55 7307.38 5121.38 7285.88 C 5118.51 7275.85 5121.38 7262.95 5114.21 7257.22 C 5108.48 7251.49 5099.88 7261.52 5094.15 7260.09 C 5084.12 7258.65 5074.09 7254.35 5065.49 7248.62 C 5064.05 7247.19 5066.92 7244.32 5068.35 7242.89 C 5104.18 7215.66 5148.61 7197.03 5193.04 7188.43 M 5343.52 7189.86 C 5376.48 7272.99 5337.79 7215.66 5317.72 7205.63 C 5309.12 7202.76 5297.66 7209.93 5289.06 7204.19 C 5253.23 7179.83 5300.53 7169.8 5343.52 7189.86 M 5088.42 7324.58 C 5079.82 7333.18 5066.92 7333.18 5066.92 7340.34 C 5066.92 7346.08 5072.65 7357.54 5079.82 7357.54 C 5088.42 7357.54 5094.15 7357.54 5105.61 7347.51 C 5117.08 7337.48 5105.61 7330.31 5105.61 7330.31 L 5089.85 7324.58 L 5088.42 7324.58 M 5054.02 7281.59 C 5042.56 7284.45 5039.69 7291.62 5033.96 7298.78 C 5028.22 7307.38 5019.63 7320.28 5032.52 7326.01 C 5039.69 7330.31 5061.19 7318.85 5065.49 7310.25 C 5068.35 7303.08 5076.95 7301.65 5076.95 7293.05 C 5076.95 7285.88 5066.92 7280.15 5066.92 7280.15 L 5054.02 7281.59 " }
+ }
+ }
+ // PATH visit pt count: 265
+ DemoShape {
+ objectName: "pt"
+ ShapePath {
+ objectName: "svg_path:pt"
+ // boundingRect: 3325.32, 6505.85 582.011x959.21
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 3564.96 7442.1 C 3567.83 7381.91 3553.5 7371.87 3600.79 7330.31 C 3609.39 7323.15 3607.96 7307.38 3616.56 7303.08 C 3619.42 7301.65 3689.65 7300.22 3672.45 7271.55 C 3668.15 7264.39 3653.82 7275.85 3646.65 7271.55 C 3639.49 7265.82 3640.92 7254.35 3638.05 7245.76 C 3635.19 7237.16 3635.19 7227.12 3629.45 7219.96 C 3628.02 7217.09 3619.42 7219.96 3619.42 7217.09 C 3623.72 7207.06 3633.75 7199.89 3638.05 7189.86 C 3646.65 7172.66 3622.29 7185.56 3646.65 7165.5 C 3668.15 7148.3 3696.81 7151.17 3699.68 7113.9 C 3699.68 7111.04 3672.45 7092.41 3666.72 7080.94 C 3658.12 7062.31 3668.15 7032.21 3668.15 7029.35 C 3666.72 7027.91 3643.79 6983.49 3643.79 6982.05 C 3642.35 6979.19 3648.09 6983.49 3650.95 6983.49 C 3663.85 6987.79 3672.45 6989.22 3685.35 6992.09 C 3718.31 7000.68 3725.48 7003.55 3742.67 6970.59 C 3762.74 6930.46 3741.24 6944.79 3736.94 6914.69 C 3732.64 6887.46 3777.07 6900.36 3778.5 6880.3 C 3779.94 6865.97 3764.17 6878.87 3772.77 6861.67 C 3774.2 6858.8 3779.94 6858.8 3781.37 6854.5 C 3785.67 6844.47 3785.67 6834.44 3787.1 6824.41 C 3788.54 6812.94 3797.13 6801.47 3795.7 6790.01 C 3795.7 6790.01 3782.8 6755.61 3782.8 6755.61 C 3787.1 6751.31 3797.13 6759.91 3802.87 6757.05 C 3802.87 6757.05 3832.96 6725.52 3832.96 6725.52 C 3854.46 6714.05 3894.59 6712.62 3906.06 6681.09 C 3914.65 6652.43 3877.39 6655.29 3867.36 6653.86 C 3860.19 6652.43 3873.09 6597.97 3867.36 6595.1 C 3840.13 6580.77 3834.4 6593.67 3801.43 6580.77 C 3795.7 6577.9 3797.13 6563.57 3784.24 6572.17 C 3781.37 6575.03 3787.1 6582.2 3784.24 6585.07 C 3764.17 6602.26 3742.67 6582.2 3736.94 6580.77 C 3731.21 6577.9 3724.04 6585.07 3719.74 6582.2 C 3716.88 6580.77 3728.34 6577.9 3725.48 6575.03 C 3724.04 6569.3 3716.88 6567.87 3711.14 6567.87 C 3702.55 6566.44 3692.51 6569.3 3683.91 6567.87 C 3682.48 6566.44 3685.35 6563.57 3685.35 6560.7 C 3685.35 6559.27 3686.78 6553.54 3685.35 6554.97 C 3678.18 6556.4 3660.98 6576.47 3648.09 6567.87 C 3613.69 6543.5 3681.05 6537.77 3663.85 6520.57 C 3660.98 6517.71 3655.25 6520.57 3652.38 6517.71 C 3650.95 6514.84 3658.12 6509.11 3655.25 6507.68 C 3629.45 6501.94 3593.63 6510.54 3572.13 6522.01 C 3569.26 6536.34 3533.43 6543.5 3542.03 6556.4 C 3547.76 6563.57 3564.96 6560.7 3567.83 6569.3 C 3570.69 6576.47 3552.06 6567.87 3547.76 6573.6 C 3539.17 6586.5 3540.6 6648.13 3543.46 6663.89 C 3543.46 6668.19 3550.63 6669.62 3550.63 6675.36 C 3549.2 6699.72 3539.17 6714.05 3529.13 6734.12 C 3524.83 6742.71 3506.2 6761.35 3516.23 6759.91 C 3526.27 6757.05 3526.27 6735.55 3536.3 6732.68 C 3543.46 6731.25 3547.76 6745.58 3544.9 6751.31 C 3537.73 6761.35 3517.67 6757.05 3510.5 6768.51 C 3500.47 6785.71 3494.74 6805.77 3483.27 6822.97 C 3478.97 6830.14 3470.37 6834.44 3471.81 6841.6 C 3471.81 6848.77 3484.7 6851.64 3487.57 6858.8 C 3489 6861.67 3480.41 6850.2 3476.11 6853.07 C 3468.94 6857.37 3466.07 6868.83 3460.34 6877.43 C 3446.01 6900.36 3430.24 6929.03 3413.05 6949.09 C 3408.75 6954.82 3400.15 6956.26 3394.42 6963.42 C 3392.98 6964.86 3401.58 6969.15 3398.71 6970.59 C 3387.25 6973.45 3372.92 6963.42 3365.75 6970.59 C 3358.59 6977.75 3371.48 6989.22 3368.62 6997.82 C 3364.32 7012.15 3321.32 7072.34 3325.62 7086.67 C 3328.49 7096.71 3335.66 7105.31 3341.39 7115.34 C 3355.72 7139.7 3349.99 7151.17 3349.99 7151.17 C 3361.45 7154.03 3374.35 7152.6 3385.82 7151.17 C 3391.55 7151.17 3394.42 7144 3400.15 7145.43 C 3405.88 7145.43 3408.75 7154.03 3415.91 7155.47 C 3420.21 7156.9 3420.21 7136.84 3423.08 7141.13 C 3427.38 7148.3 3420.21 7158.33 3423.08 7164.07 C 3427.38 7169.8 3447.44 7172.66 3441.71 7175.53 C 3403.01 7194.16 3425.94 7145.43 3394.42 7149.73 C 3385.82 7151.17 3404.45 7162.63 3407.31 7169.8 C 3408.75 7178.4 3407.31 7188.43 3405.88 7198.46 C 3403.01 7217.09 3378.65 7234.29 3378.65 7248.62 C 3378.65 7254.35 3387.25 7258.65 3387.25 7264.39 C 3388.68 7274.42 3372.92 7293.05 3382.95 7295.92 C 3385.82 7295.92 3395.85 7291.62 3392.98 7294.48 C 3390.12 7298.78 3381.52 7295.92 3378.65 7300.22 C 3371.48 7314.55 3375.78 7331.75 3368.62 7346.08 C 3362.89 7357.54 3328.49 7404.84 3331.36 7420.6 C 3332.79 7426.33 3342.82 7414.87 3348.55 7414.87 C 3357.15 7413.44 3365.75 7416.3 3374.35 7414.87 C 3378.65 7414.87 3382.95 7410.57 3387.25 7412 C 3410.18 7417.74 3484.7 7466.46 3494.74 7465.03 C 3499.04 7465.03 3543.46 7443.53 3546.33 7442.1 C 3550.63 7442.1 3557.8 7442.1 3564.96 7442.1 " }
+ }
+ }
+ // PATH visit at count: 269
+ DemoShape {
+ objectName: "at"
+ ShapePath {
+ objectName: "svg_path:at"
+ // boundingRect: 6123.16, 5513.73 961.654x499.661
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6128.9 5816.89 C 6128.9 5826.92 6147.53 5829.79 6141.79 5844.12 C 6140.36 5846.99 6134.63 5845.55 6133.19 5846.99 C 6131.76 5851.29 6127.46 5862.75 6123.16 5875.65 L 6130.33 5891.41 L 6130.33 5908.61 C 6144.66 5910.05 6167.59 5912.91 6169.02 5912.91 C 6171.89 5915.78 6164.72 5920.08 6167.59 5922.94 C 6174.76 5931.54 6183.36 5937.28 6193.39 5941.58 C 6216.32 5953.04 6220.62 5914.34 6236.38 5917.21 C 6266.48 5921.51 6234.95 5937.28 6244.98 5941.58 C 6267.91 5953.04 6247.85 5938.71 6270.78 5941.58 C 6282.24 5943.01 6293.71 5973.1 6316.64 5960.21 C 6316.64 5960.21 6335.27 5921.51 6335.27 5921.51 C 6346.74 5912.91 6396.9 5918.64 6409.8 5917.21 C 6431.29 5915.78 6448.49 5897.15 6469.99 5897.15 C 6475.72 5897.15 6459.96 5904.31 6458.52 5910.05 C 6451.36 5940.14 6469.99 5920.08 6481.45 5937.28 C 6484.32 5941.58 6477.15 5947.31 6480.02 5953.04 C 6481.45 5955.91 6485.75 5950.17 6488.62 5953.04 C 6515.85 5973.1 6523.02 5975.97 6560.28 5981.7 C 6603.27 5987.44 6647.7 5988.87 6692.13 5996.04 C 6703.59 5997.47 6715.06 6006.07 6726.53 6007.5 C 6737.99 6008.93 6749.46 6000.33 6760.92 6001.77 C 6768.09 6003.2 6772.39 6013.23 6779.55 6013.23 C 6782.42 6014.67 6780.99 6006.07 6783.85 6004.63 C 6791.02 5997.47 6802.48 5994.6 6809.65 5986 C 6815.38 5981.7 6811.08 5971.67 6815.38 5967.37 C 6842.61 5945.87 6871.28 5973.1 6897.07 5961.64 C 6905.67 5957.34 6907.1 5944.44 6915.7 5941.58 C 6917.14 5940.14 6964.43 5947.31 6965.86 5945.87 C 6970.16 5944.44 6961.56 5938.71 6960.13 5934.41 C 6957.26 5920.08 6963 5911.48 6973.03 5908.61 C 6998.83 5879.95 7010.29 5875.65 7023.19 5854.15 C 7026.06 5848.42 7000.26 5818.32 7007.43 5803.99 C 7017.46 5783.93 7041.82 5805.42 7038.96 5776.76 C 7037.52 5752.4 6994.53 5755.26 7003.13 5748.1 C 7023.19 5730.9 7074.78 5753.83 7083.38 5738.07 C 7084.82 5732.33 7064.75 5686.47 7084.82 5677.87 C 7067.62 5659.24 7053.29 5640.61 7040.39 5619.11 C 7028.92 5600.48 7070.49 5570.39 7040.39 5548.89 C 7036.09 5546.02 7030.36 5554.62 7026.06 5554.62 C 7018.89 5553.19 7004.56 5534.56 6994.53 5537.42 C 6987.36 5540.29 6984.5 5551.75 6975.9 5556.05 C 6970.16 5557.49 6963 5554.62 6957.26 5554.62 C 6918.57 5550.32 6885.61 5518.79 6844.04 5518.79 C 6836.88 5518.79 6831.15 5527.39 6823.98 5527.39 C 6821.11 5525.96 6823.98 5518.79 6821.11 5517.36 C 6815.38 5514.49 6806.78 5511.63 6802.48 5515.92 C 6801.05 5518.79 6803.92 5560.35 6801.05 5563.22 C 6798.18 5567.52 6788.15 5557.49 6782.42 5561.79 C 6782.42 5561.79 6770.95 5599.05 6770.95 5599.05 C 6760.92 5601.91 6750.89 5589.02 6740.86 5589.02 C 6735.12 5589.02 6736.56 5600.48 6730.82 5601.91 C 6700.73 5613.38 6683.53 5583.28 6660.6 5571.82 L 6659.17 5571.82 C 6660.6 5576.12 6662.03 5580.42 6662.03 5584.72 C 6660.6 5623.41 6652 5614.81 6621.9 5607.65 C 6619.04 5607.65 6616.17 5609.08 6614.74 5611.95 C 6611.87 5626.28 6620.47 5642.04 6606.14 5653.51 C 6593.24 5663.54 6533.05 5677.87 6531.61 5700.8 C 6531.61 5700.8 6566.01 5748.1 6566.01 5749.53 C 6567.44 5750.96 6547.38 5768.16 6554.55 5773.89 C 6570.31 5788.23 6586.08 5766.73 6573.18 5814.02 C 6573.18 5816.89 6570.31 5821.19 6567.44 5821.19 C 6521.58 5808.29 6557.41 5785.36 6535.91 5783.93 C 6524.45 5783.93 6514.42 5793.96 6502.95 5792.53 C 6497.22 5791.09 6498.65 5781.06 6494.35 5779.63 C 6485.75 5776.76 6477.15 5783.93 6469.99 5781.06 C 6467.12 5779.63 6472.86 5771.03 6469.99 5771.03 C 6452.79 5772.46 6468.56 5795.39 6464.26 5796.83 C 6444.19 5802.56 6411.23 5793.96 6392.6 5801.12 C 6386.87 5803.99 6389.73 5814.02 6384 5816.89 C 6378.27 5821.19 6371.1 5814.02 6365.37 5818.32 C 6361.07 5819.76 6366.8 5826.92 6363.93 5829.79 C 6362.5 5831.22 6313.77 5844.12 6308.04 5841.25 C 6305.17 5838.39 6308.04 5831.22 6305.17 5828.35 C 6303.74 5825.49 6296.58 5825.49 6295.14 5822.62 C 6293.71 5819.76 6302.31 5821.19 6300.88 5818.32 C 6295.14 5799.69 6255.01 5822.62 6243.55 5809.72 C 6243.55 5809.72 6246.41 5808.29 6246.41 5806.86 C 6237.82 5801.12 6246.41 5825.49 6243.55 5835.52 C 6243.55 5841.25 6216.32 5874.22 6206.29 5864.18 C 6200.55 5858.45 6216.32 5849.85 6212.02 5844.12 C 6210.59 5842.69 6206.29 5848.42 6203.42 5848.42 C 6200.55 5848.42 6196.25 5846.99 6194.82 5844.12 C 6191.95 5839.82 6201.99 5834.09 6194.82 5828.35 C 6189.09 5824.06 6186.22 5816.89 6180.49 5814.02 C 6176.19 5811.16 6170.46 5818.32 6164.72 5816.89 C 6161.86 5814.02 6164.72 5806.86 6161.86 5803.99 C 6154.69 5798.26 6156.13 5824.06 6133.19 5816.89 C 6131.76 5815.46 6130.33 5815.46 6128.9 5815.46 L 6128.9 5816.89 " }
+ }
+ }
+ // PATH visit it count: 711
+ DemoShape {
+ objectName: "it"
+ ShapePath {
+ objectName: "svg_path:it"
+ // boundingRect: 5733.22, 5897.15 1686.97x1929.69
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7023.19 7523.79 C 7004.56 7533.82 6942.93 7682.87 6957.27 7702.94 C 6963 7711.53 6991.66 7711.53 6983.06 7717.27 C 6975.9 7721.57 6977.33 7707.24 6973.03 7717.27 C 6973.03 7720.13 6970.16 7723 6971.6 7727.3 C 6974.46 7738.76 6991.66 7744.5 6995.96 7755.96 C 6997.39 7758.83 6991.66 7757.4 6988.79 7760.26 C 6981.63 7764.56 6974.46 7768.86 6970.16 7776.03 C 6965.86 7783.19 6964.43 7791.79 6963 7798.96 C 6961.56 7807.56 6967.3 7819.02 6963 7826.19 C 6960.13 7830.49 6955.83 7811.86 6928.6 7813.29 C 6898.51 7816.16 6911.4 7826.19 6871.28 7807.56 C 6854.08 7800.39 6849.78 7768.86 6831.15 7757.4 C 6805.35 7741.63 6782.42 7757.4 6753.76 7745.93 C 6749.46 7744.5 6745.16 7743.06 6740.86 7740.2 C 6735.12 7735.9 6732.26 7725.87 6725.09 7723 C 6719.36 7718.7 6712.19 7723 6705.03 7720.13 C 6676.36 7710.1 6656.3 7682.87 6624.77 7674.27 C 6611.87 7671.41 6597.54 7684.3 6584.64 7678.57 C 6581.78 7677.14 6547.38 7639.88 6548.81 7634.14 C 6548.81 7628.41 6557.41 7625.54 6557.41 7621.25 C 6558.84 7616.95 6553.11 7615.51 6553.11 7611.21 C 6553.11 7605.48 6555.98 7598.31 6560.28 7592.58 C 6564.58 7586.85 6573.18 7585.42 6578.91 7579.68 C 6586.08 7573.95 6593.24 7552.45 6598.97 7559.62 C 6601.84 7563.92 6606.14 7592.58 6619.04 7591.15 C 6667.77 7583.98 6619.04 7548.15 6680.66 7551.02 C 6696.43 7551.02 6683.53 7568.22 6692.13 7572.52 C 6697.86 7575.38 6706.46 7568.22 6713.63 7571.08 C 6717.93 7572.52 6716.49 7581.12 6719.36 7582.55 C 6727.96 7588.28 6739.42 7594.02 6749.46 7594.02 C 6750.89 7594.02 6786.72 7576.82 6786.72 7576.82 C 6798.18 7575.38 6822.55 7583.98 6839.75 7579.68 C 6875.57 7572.52 6874.14 7558.19 6892.77 7548.15 C 6935.77 7525.22 6904.24 7556.75 6945.8 7553.89 C 6974.46 7552.45 6954.4 7536.69 6965.86 7525.22 C 6970.16 7520.92 6970.16 7538.12 6974.46 7536.69 C 7000.26 7530.96 7000.26 7508.03 7023.19 7523.79 M 6077.3 6994.95 C 6081.6 7015.02 6144.66 7010.72 6136.06 7042.25 C 6134.63 7050.85 6110.26 7047.98 6113.13 7058.01 C 6116 7068.04 6136.06 7065.18 6143.23 7073.78 C 6146.09 7076.64 6136.06 7078.08 6136.06 7082.37 C 6144.66 7133.97 6157.56 7060.88 6160.42 7129.67 C 6161.86 7154.03 6136.06 7162.63 6128.9 7182.7 C 6121.73 7201.33 6143.23 7201.33 6146.09 7217.09 C 6146.09 7222.83 6138.93 7228.56 6138.93 7235.72 C 6134.63 7283.02 6140.36 7348.94 6113.13 7396.24 C 6111.7 7399.1 6113.13 7391.94 6110.26 7390.51 C 6090.2 7383.34 6064.4 7363.28 6040.04 7364.71 C 6035.74 7364.71 6050.07 7370.44 6050.07 7374.74 C 6048.64 7380.47 6041.47 7383.34 6040.04 7390.51 C 6031.44 7423.47 6054.37 7417.74 6014.24 7436.37 C 5958.35 7462.16 5992.74 7409.14 5965.51 7397.67 C 5972.45 7414.82 5952.05 7381.82 5944.02 7373.31 C 5935.42 7344.64 5955.48 7326.01 5959.78 7301.65 C 5961.21 7291.62 5955.48 7283.02 5958.35 7272.99 C 5959.78 7264.39 5975.55 7260.09 5972.68 7251.49 C 5971.25 7244.32 5955.48 7251.49 5951.18 7245.76 C 5946.88 7238.59 5954.05 7229.99 5954.05 7222.83 C 5954.05 7221.39 5948.32 7221.39 5949.75 7219.96 C 5952.62 7215.66 5961.21 7217.09 5964.08 7211.36 C 5969.81 7197.03 5948.32 7126.8 5939.72 7116.77 C 5922.52 7098.14 5911.05 7159.77 5915.35 7088.11 C 5916.79 7078.08 5926.82 7070.91 5928.25 7060.88 C 5929.69 7055.14 5922.52 7049.41 5926.82 7045.11 C 5928.25 7043.68 5938.28 7073.78 5964.08 7073.78 C 5985.58 7075.21 6064.4 7010.72 6077.3 6994.95 M 6667.77 5991.74 C 6664.9 6020.4 6629.07 6014.67 6626.2 6037.6 C 6623.34 6054.8 6656.3 6051.93 6663.47 6057.66 C 6673.5 6066.26 6646.27 6077.73 6641.97 6090.62 C 6636.24 6109.26 6659.17 6089.19 6662.03 6096.36 C 6664.9 6103.52 6649.13 6122.15 6659.17 6127.89 C 6673.5 6136.49 6730.82 6170.88 6680.66 6168.02 C 6683.53 6160.85 6683.53 6155.12 6677.8 6147.95 C 6650.57 6122.15 6651.2 6140.92 6640.24 6150.79 C 6637.93 6153.84 6637.67 6150.82 6637.67 6150.82 C 6637.67 6136.49 6610.44 6133.62 6597.54 6139.35 C 6590.37 6143.65 6597.54 6159.42 6591.81 6165.15 C 6586.08 6170.88 6576.04 6166.58 6567.44 6169.45 C 6547.38 6178.05 6528.75 6200.98 6505.82 6205.28 C 6498.65 6206.71 6491.49 6199.55 6485.75 6203.84 C 6480.02 6205.28 6485.75 6213.88 6482.89 6219.61 C 6481.45 6223.91 6477.15 6229.64 6474.29 6233.94 C 6475.72 6232.51 6475.72 6231.07 6477.15 6231.07 C 6484.32 6231.07 6478.59 6246.84 6484.32 6249.71 C 6487.19 6251.14 6488.62 6241.11 6491.49 6243.97 C 6494.35 6249.71 6500.09 6261.17 6494.35 6265.47 C 6491.49 6266.9 6490.05 6269.77 6491.49 6272.64 C 6492.92 6274.07 6495.79 6271.2 6498.65 6271.2 C 6507.25 6278.37 6521.58 6282.67 6523.02 6292.7 C 6531.61 6332.83 6497.22 6317.06 6487.19 6341.43 C 6484.32 6348.59 6502.95 6425.99 6507.25 6434.58 C 6515.85 6448.92 6616.17 6520.57 6636.24 6532.04 C 6656.3 6543.5 6662.03 6536.34 6680.66 6552.1 C 6700.73 6569.3 6727.96 6682.52 6743.72 6711.19 C 6759.49 6741.28 6792.45 6768.51 6818.25 6788.58 C 6826.85 6794.31 6839.75 6795.74 6846.91 6802.91 C 6851.21 6805.77 6846.91 6814.37 6849.78 6817.24 C 6888.47 6857.37 6961.56 6844.47 7007.43 6837.3 C 7017.46 6835.87 7038.96 6820.11 7051.85 6834.44 C 7087.68 6874.57 6980.2 6888.9 7034.66 6926.16 C 7086.25 6960.56 7157.91 6957.69 7210.94 6984.92 C 7229.57 6994.95 7236.73 7010.72 7258.23 7017.88 C 7275.43 7025.05 7322.72 7032.21 7337.05 7043.68 C 7342.79 7047.98 7337.05 7058.01 7341.35 7062.31 C 7358.55 7086.67 7404.41 7082.37 7418.74 7132.54 C 7424.48 7151.17 7411.58 7145.43 7407.28 7158.33 C 7404.41 7165.5 7408.71 7194.16 7402.98 7197.03 C 7397.25 7201.33 7387.21 7197.03 7378.62 7192.73 C 7334.19 7175.53 7352.82 7149.73 7329.89 7119.64 C 7316.99 7102.44 7275.43 7119.64 7253.93 7112.47 C 7225.27 7102.44 7209.5 7069.48 7182.27 7098.14 C 7179.41 7102.44 7143.58 7174.1 7143.58 7176.96 C 7143.58 7181.26 7150.74 7185.56 7149.31 7191.3 C 7143.58 7218.53 7114.91 7217.09 7139.28 7248.62 C 7153.61 7265.82 7157.91 7250.06 7173.67 7254.35 C 7190.87 7260.09 7202.34 7277.29 7218.1 7284.45 C 7223.83 7287.32 7232.43 7284.45 7235.3 7290.18 C 7238.17 7294.48 7229.57 7300.22 7229.57 7305.95 C 7231 7346.08 7249.63 7308.82 7246.76 7360.41 C 7245.33 7390.51 7188 7379.04 7162.21 7409.14 C 7143.58 7430.63 7167.94 7452.13 7162.21 7477.93 C 7157.91 7499.43 7143.58 7493.69 7129.24 7505.16 C 7094.85 7535.26 7119.21 7604.05 7047.55 7583.98 C 7033.22 7579.68 7017.46 7542.42 7026.06 7526.66 C 7031.79 7518.06 7046.12 7519.49 7053.29 7512.32 C 7057.59 7506.59 7057.59 7497.99 7059.02 7490.83 C 7060.45 7483.66 7066.19 7477.93 7064.75 7470.76 C 7063.32 7462.16 7048.99 7457.86 7050.42 7449.27 C 7056.15 7423.47 7090.55 7437.8 7100.58 7423.47 C 7124.95 7394.81 7096.28 7390.51 7084.82 7374.74 C 7073.35 7357.54 7079.08 7333.18 7070.49 7314.55 C 7066.19 7303.08 7060.45 7293.05 7053.29 7284.45 C 7051.85 7281.59 7046.12 7281.59 7043.26 7278.72 C 7017.46 7240.02 7044.69 7262.95 7027.49 7219.96 C 7005.99 7165.5 6993.09 7192.73 6950.1 7195.6 C 6948.67 7195.6 6951.53 7191.3 6950.1 7188.43 C 6944.37 7182.7 6937.2 7175.53 6928.6 7171.23 C 6920 7166.93 6902.8 7171.23 6897.07 7161.2 C 6891.34 7151.17 6908.54 7141.13 6907.1 7129.67 C 6905.67 7118.2 6897.07 7108.17 6891.34 7099.57 C 6856.94 7052.28 6851.21 7118.2 6811.08 7103.87 C 6806.78 7102.44 6838.31 7079.51 6823.98 7069.48 C 6772.39 7032.21 6822.55 7070.91 6766.65 7063.74 C 6762.35 7062.31 6766.65 7055.14 6765.22 7050.85 C 6759.49 7033.65 6729.39 6987.79 6715.06 6982.05 C 6676.36 6967.72 6706.46 6994.95 6693.56 6992.09 C 6680.66 6989.22 6669.2 6973.45 6654.87 6974.89 C 6641.97 6977.75 6631.94 6994.95 6620.47 6992.09 C 6607.57 6989.22 6609.01 6970.59 6600.41 6961.99 C 6590.37 6953.39 6564.58 6956.26 6555.98 6950.52 L 6534.48 6920.43 C 6524.45 6911.83 6508.68 6910.4 6500.09 6900.36 C 6482.89 6874.57 6502.95 6878.87 6482.89 6863.1 C 6474.29 6855.93 6464.26 6850.2 6454.22 6844.47 C 6451.36 6843.04 6445.62 6847.34 6442.76 6845.9 C 6426.99 6833 6426.99 6808.64 6411.23 6795.74 C 6384 6772.81 6366.8 6790.01 6338.14 6778.54 C 6333.84 6777.11 6345.3 6772.81 6348.17 6769.94 C 6356.77 6758.48 6339.57 6739.85 6335.27 6735.55 C 6332.4 6732.68 6326.67 6735.55 6323.81 6732.68 C 6319.51 6726.95 6319.51 6718.35 6313.77 6714.05 C 6306.61 6708.32 6292.28 6712.62 6286.54 6704.02 C 6282.24 6698.29 6295.14 6686.82 6287.98 6682.52 C 6279.38 6675.36 6265.05 6683.95 6253.58 6679.66 C 6246.41 6676.79 6257.88 6663.89 6257.88 6655.29 C 6262.18 6626.63 6257.88 6616.6 6242.12 6592.23 C 6212.02 6549.24 6233.52 6510.54 6197.69 6477.58 C 6194.82 6473.28 6189.09 6471.85 6184.79 6470.41 C 6183.36 6470.41 6181.92 6474.71 6180.49 6473.28 C 6173.32 6470.41 6169.02 6461.81 6161.86 6461.81 C 6157.56 6461.81 6166.16 6474.71 6161.86 6473.28 C 6150.39 6470.41 6141.79 6461.81 6131.76 6454.65 C 6127.46 6451.78 6091.63 6414.52 6080.17 6414.52 C 6077.3 6414.52 6081.6 6421.69 6078.73 6423.12 C 6075.87 6424.55 6071.57 6423.12 6070.14 6420.25 C 6062.97 6411.65 6032.87 6397.32 6018.54 6397.32 C 6012.81 6397.32 5975.55 6418.82 5972.68 6424.55 C 5969.81 6430.28 5974.11 6438.88 5969.81 6443.18 C 5965.51 6448.92 5954.05 6447.48 5949.75 6451.78 C 5939.72 6461.81 5932.55 6484.74 5921.09 6494.78 C 5902.46 6513.41 5869.49 6511.98 5847.99 6514.84 C 5840.83 6489.04 5889.56 6456.08 5868.06 6440.32 C 5860.89 6436.02 5850.86 6441.75 5842.26 6443.18 C 5833.66 6443.18 5823.63 6447.48 5815.03 6444.62 C 5799.27 6440.32 5762 6413.09 5763.44 6393.02 C 5763.44 6390.16 5770.6 6390.16 5770.6 6387.29 C 5769.17 6382.99 5754.84 6378.69 5759.14 6370.09 C 5764.87 6364.36 5772.04 6360.06 5776.34 6352.89 C 5777.77 6351.46 5773.47 6348.59 5774.9 6345.73 C 5776.34 6341.43 5789.23 6348.59 5790.67 6344.29 C 5790.67 6332.83 5789.23 6321.36 5784.94 6312.77 C 5777.77 6301.3 5730.48 6282.67 5733.34 6259.74 C 5733.34 6252.57 5746.24 6252.57 5753.41 6251.14 C 5757.71 6251.14 5760.57 6259.74 5764.87 6258.3 C 5772.04 6256.87 5817.9 6219.61 5810.73 6206.71 C 5806.43 6198.11 5792.1 6196.68 5787.8 6186.65 C 5783.5 6180.91 5789.24 6170.88 5786.37 6165.15 C 5766.3 6130.75 5729.04 6147.95 5794.97 6109.26 C 5809.3 6135.05 5847.99 6106.39 5866.63 6102.09 C 5879.52 6099.22 5889.56 6116.42 5902.46 6116.42 C 5903.89 6116.42 5944.02 6074.86 5944.02 6067.69 C 5945.45 6060.53 5932.55 6057.66 5933.98 6051.93 C 5936.85 6043.33 5948.32 6040.46 5955.48 6034.73 C 5962.65 6029 5968.38 6023.27 5975.55 6017.53 C 5978.41 6016.1 5981.28 6013.23 5982.71 6014.67 C 5985.58 6023.27 5984.15 6031.86 5984.15 6040.46 C 5984.15 6044.76 5979.85 6050.5 5981.28 6056.23 C 5991.31 6074.86 6014.24 6079.16 6030.01 6084.89 C 6031.44 6084.89 6034.31 6087.76 6034.31 6090.62 C 6032.87 6096.36 6022.84 6100.66 6024.27 6106.39 C 6025.71 6112.12 6035.74 6109.26 6040.04 6113.56 C 6045.77 6120.72 6048.64 6145.08 6057.24 6137.92 C 6081.6 6119.29 6052.94 6125.02 6052.94 6110.69 C 6052.94 6079.16 6087.33 6070.56 6093.07 6044.76 C 6093.07 6041.9 6077.3 6003.2 6101.67 6007.5 C 6120.3 6010.37 6097.37 6039.03 6134.63 6047.63 C 6157.56 6054.8 6138.93 6026.13 6177.62 6033.3 C 6189.09 6034.73 6183.36 6057.66 6193.39 6063.39 C 6199.12 6066.26 6210.59 6060.53 6209.15 6054.8 C 6206.29 6050.5 6200.55 6047.63 6199.12 6041.9 C 6199.12 6036.16 6207.72 6031.86 6206.29 6026.13 C 6203.42 6018.97 6177.62 6003.2 6197.69 5988.87 C 6223.48 5970.24 6213.45 6011.8 6243.55 6004.63 C 6260.75 5998.9 6233.52 5978.84 6233.52 5975.97 C 6236.38 5970.24 6240.68 5965.94 6242.12 5960.21 C 6244.98 5954.47 6243.55 5947.31 6244.98 5941.58 C 6267.91 5953.04 6247.85 5938.71 6270.78 5941.58 C 6282.24 5943.01 6293.71 5973.1 6316.64 5960.21 L 6335.27 5921.51 C 6346.74 5912.91 6396.9 5918.64 6409.8 5917.21 C 6431.29 5915.78 6448.49 5897.15 6469.99 5897.15 C 6475.72 5897.15 6459.96 5904.31 6458.52 5910.05 C 6451.36 5940.14 6469.99 5920.08 6481.45 5937.28 C 6484.32 5941.58 6477.15 5947.31 6480.02 5953.04 C 6481.45 5955.91 6485.75 5950.17 6488.62 5953.04 C 6515.85 5973.1 6523.02 5975.97 6560.28 5981.7 C 6596.11 5986 6631.94 5988.87 6667.77 5991.74 L 6667.77 5991.74 " }
+ }
+ }
+ // PATH visit gr count: 707
+ DemoShape {
+ objectName: "gr"
+ ShapePath {
+ objectName: "svg_path:gr"
+ // boundingRect: 7642.32, 6646.69 1068.32x1299.36
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 8708.59 7823.32 C 8692.83 7853.42 8721.49 7833.35 8705.73 7877.78 C 8704.29 7882.08 8588.21 7907.88 8576.74 7912.18 C 8568.14 7915.04 8487.89 7952.31 8479.29 7945.14 C 8453.49 7916.48 8499.35 7919.34 8417.66 7909.31 C 8334.54 7899.28 8437.73 7919.34 8367.5 7919.34 C 8363.2 7919.34 8361.77 7912.18 8357.47 7912.18 C 8348.87 7910.74 8338.84 7912.18 8328.8 7913.61 C 8318.77 7916.48 8307.31 7922.21 8297.27 7923.64 C 8275.78 7923.64 8284.38 7896.41 8285.81 7887.81 C 8285.81 7876.35 8281.51 7863.45 8285.81 7853.42 C 8301.57 7808.99 8315.91 7859.15 8325.94 7863.45 C 8335.97 7867.75 8347.44 7862.02 8357.47 7857.72 C 8364.63 7854.85 8368.93 7836.22 8374.67 7841.95 C 8381.83 7847.69 8357.47 7853.42 8360.33 7860.58 C 8361.77 7867.75 8374.67 7863.45 8381.83 7866.32 C 8389 7869.18 8394.73 7879.22 8403.33 7880.65 C 8410.5 7882.08 8416.23 7876.35 8423.39 7873.48 C 8437.73 7869.18 8447.76 7857.72 8460.66 7853.42 C 8476.42 7849.12 8492.19 7850.55 8507.95 7847.69 C 8517.98 7844.82 8509.38 7856.28 8517.98 7857.72 C 8533.75 7862.02 8549.51 7849.12 8566.71 7850.55 C 8571.01 7850.55 8573.88 7859.15 8578.18 7857.72 C 8585.34 7856.28 8611.14 7834.79 8624.04 7837.65 C 8626.9 7839.09 8602.54 7869.18 8636.94 7879.22 C 8644.1 7882.08 8644.1 7866.32 8649.83 7862.02 C 8679.93 7831.92 8675.63 7870.62 8708.59 7823.32 M 8413.36 6738.42 L 8414.79 6736.98 L 8416.23 6736.98 C 8417.66 6736.98 8462.09 6722.65 8462.09 6722.65 C 8493.62 6698.29 8403.33 6658.16 8459.22 6646.69 L 8460.66 6646.69 L 8464.96 6646.69 L 8466.39 6646.69 L 8467.82 6646.69 C 8470.69 6646.69 8473.55 6646.69 8474.99 6648.13 L 8477.85 6648.13 C 8509.38 6655.29 8552.38 6685.39 8530.88 6711.19 C 8515.12 6728.38 8499.35 6719.78 8493.62 6748.45 L 8505.08 6790.01 C 8503.65 6800.04 8483.59 6833 8473.55 6838.74 L 8472.12 6854.5 C 8447.76 6841.6 8493.62 6840.17 8470.69 6820.11 C 8459.22 6811.51 8413.36 6831.57 8399.03 6830.14 C 8353.17 6827.27 8347.44 6797.18 8317.34 6834.44 C 8310.17 6843.04 8303.01 6850.2 8294.41 6855.93 C 8254.28 6877.43 8275.78 6827.27 8235.65 6851.64 C 8219.88 6861.67 8231.35 6886.03 8199.82 6900.36 C 8172.59 6911.83 8169.72 6877.43 8143.93 6910.4 C 8133.89 6923.29 8151.09 6921.86 8155.39 6924.73 C 8168.29 6931.89 8166.86 6953.39 8178.32 6964.86 C 8181.19 6967.72 8186.92 6966.29 8192.65 6964.86 C 8194.09 6964.86 8195.52 6963.42 8196.95 6960.56 C 8196.95 6957.69 8191.22 6951.96 8195.52 6951.96 C 8201.25 6950.52 8201.25 6963.42 8206.99 6964.86 C 8222.75 6969.15 8255.71 6974.89 8261.45 6996.39 C 8272.91 7037.95 8224.18 6977.75 8224.18 6977.75 C 8208.42 6969.15 8162.56 6963.42 8158.26 6990.65 C 8153.96 7007.85 8191.22 7002.12 8202.69 7013.58 C 8211.28 7022.18 8209.85 7050.85 8199.82 7046.55 C 8176.89 7037.95 8175.46 7012.15 8152.53 7000.68 C 8146.79 6999.25 8141.06 7002.12 8135.33 7000.68 C 8128.16 7000.68 8119.56 6993.52 8115.26 6999.25 C 8082.3 7035.08 8178.32 7045.11 8171.16 7058.01 C 8168.29 7062.31 8131.03 7060.88 8119.56 7058.01 C 8115.26 7056.58 8105.23 7010.72 8105.23 7010.72 C 8093.77 6996.39 8053.64 7004.98 8037.87 6993.52 C 8007.78 6970.59 8037.87 6974.89 8037.87 6946.22 C 8037.87 6940.49 8022.11 6956.26 8013.51 6963.42 C 8006.34 6970.59 7994.88 6976.32 7993.44 6986.35 C 7992.01 6993.52 8006.34 6994.95 8007.78 7002.12 L 7997.74 7045.11 C 8002.04 7072.34 8035.01 7085.24 8050.77 7102.44 L 8069.4 7139.7 C 8079.43 7149.73 8152.53 7185.56 8141.06 7202.76 C 8138.19 7207.06 8082.3 7178.4 8078 7179.83 C 8073.7 7179.83 8078 7188.43 8078 7192.73 C 8075.13 7202.76 8059.37 7224.26 8069.4 7222.83 C 8072.27 7221.39 8070.83 7214.23 8073.7 7215.66 C 8078 7217.09 8076.57 7224.26 8080.87 7227.12 C 8090.9 7234.29 8122.43 7234.29 8098.06 7255.79 C 8078 7272.99 8057.94 7274.42 8030.71 7285.88 C 8026.41 7287.32 8039.31 7291.62 8043.6 7290.18 C 8047.9 7288.75 8050.77 7281.59 8056.5 7281.59 C 8062.24 7281.59 8062.24 7290.18 8066.54 7293.05 C 8069.4 7294.48 8072.27 7291.62 8075.13 7290.18 C 8086.6 7290.18 8098.06 7287.32 8108.1 7291.62 C 8116.7 7294.48 8115.26 7308.82 8123.86 7310.25 C 8133.89 7313.11 8143.93 7297.35 8152.53 7301.65 C 8161.12 7305.95 8151.09 7324.58 8159.69 7328.88 C 8171.16 7336.05 8185.49 7315.98 8195.52 7323.15 C 8201.25 7326.01 8195.52 7348.94 8218.45 7350.38 C 8247.11 7351.81 8255.71 7341.78 8275.78 7364.71 C 8291.54 7381.91 8267.18 7374.74 8267.18 7384.77 C 8267.18 7391.94 8275.78 7396.24 8277.21 7401.97 C 8280.08 7409.14 8275.78 7417.74 8278.64 7423.47 C 8285.81 7436.37 8304.44 7442.1 8288.68 7466.46 C 8285.81 7470.76 8280.08 7467.9 8275.78 7466.46 C 8272.91 7463.6 8275.78 7459.3 8274.34 7456.43 C 8272.91 7453.56 8255.71 7442.1 8254.28 7442.1 C 8251.41 7440.67 8249.98 7447.83 8247.11 7446.4 C 8237.08 7439.23 8221.32 7410.57 8206.99 7406.27 C 8188.35 7400.54 8198.39 7422.04 8188.35 7424.9 C 8171.16 7430.63 8122.43 7444.97 8126.73 7452.13 C 8131.03 7462.16 8148.23 7457.86 8158.26 7462.16 C 8162.56 7463.6 8149.66 7466.46 8149.66 7470.76 C 8149.66 7475.06 8156.82 7475.06 8158.26 7477.93 C 8161.12 7483.66 8156.82 7492.26 8161.12 7496.56 C 8176.89 7512.32 8204.12 7503.73 8218.45 7520.92 C 8227.05 7530.96 8195.52 7533.82 8182.62 7533.82 C 8162.56 7535.26 8146.79 7533.82 8141.06 7532.39 C 8136.76 7530.96 8139.63 7523.79 8136.76 7519.49 C 8135.33 7518.06 8131.03 7518.06 8129.59 7518.06 C 8118.13 7518.06 8102.36 7509.46 8095.2 7518.06 C 8083.73 7533.82 8131.03 7569.65 8136.76 7579.68 C 8138.19 7585.42 8133.89 7591.15 8135.33 7595.45 C 8138.19 7599.75 8146.79 7595.45 8149.66 7599.75 C 8155.39 7609.78 8172.59 7641.31 8178.32 7654.21 C 8184.05 7667.11 8166.86 7658.51 8166.86 7662.81 C 8166.86 7667.11 8172.59 7668.54 8174.02 7671.41 C 8174.02 7674.27 8168.29 7678.57 8169.72 7681.44 C 8171.16 7684.3 8204.12 7702.94 8201.25 7714.4 C 8198.39 7718.7 8188.35 7718.7 8182.62 7717.27 C 8178.32 7714.4 8179.76 7705.8 8175.46 7702.94 C 8172.59 7700.07 8166.86 7707.24 8162.56 7704.37 C 8138.19 7685.74 8128.16 7628.41 8092.33 7678.57 C 8090.9 7681.44 8093.77 7711.53 8096.63 7728.73 C 8098.06 7733.03 8098.06 7743.06 8093.77 7741.63 C 8057.94 7737.33 8080.87 7701.5 8069.4 7685.74 C 8067.97 7684.3 8063.67 7685.74 8062.24 7684.3 C 8053.64 7675.71 8049.34 7665.67 8042.17 7655.64 C 8037.87 7648.48 8035.01 7635.58 8026.41 7634.14 C 7957.61 7618.38 8019.24 7684.3 7994.88 7695.77 C 7953.32 7714.4 7964.78 7667.11 7963.35 7657.07 C 7963.35 7654.21 7959.05 7654.21 7957.61 7654.21 C 7944.72 7655.64 7937.55 7638.44 7934.68 7622.68 C 7931.82 7601.18 7949.02 7625.54 7951.88 7592.58 C 7953.32 7569.65 7916.05 7540.99 7897.42 7535.26 C 7893.12 7533.82 7888.82 7543.85 7885.96 7539.55 C 7880.22 7532.39 7885.96 7519.49 7878.79 7512.32 C 7870.19 7503.73 7831.5 7515.19 7851.56 7487.96 C 7852.99 7486.53 7858.73 7489.39 7860.16 7486.53 C 7883.09 7470.76 7871.62 7457.86 7878.79 7430.63 C 7878.79 7427.77 7881.66 7436.37 7884.52 7436.37 C 7885.96 7436.37 7881.66 7430.63 7883.09 7430.63 C 7897.42 7430.63 7911.75 7444.97 7923.22 7433.5 C 7938.98 7417.74 7917.49 7416.3 7946.15 7397.67 C 7966.21 7384.77 7980.55 7406.27 7996.31 7412 C 8019.24 7420.6 8046.47 7417.74 8070.83 7424.9 C 8085.17 7427.77 8093.77 7444.97 8108.1 7446.4 C 8113.83 7447.83 8125.3 7440.67 8123.86 7433.5 C 8121 7427.77 8105.23 7434.93 8102.36 7427.77 C 8088.03 7394.81 8138.19 7443.53 8155.39 7396.24 C 8156.82 7391.94 8145.36 7399.1 8139.63 7396.24 C 8136.76 7394.81 8139.63 7387.64 8136.76 7387.64 C 8133.89 7387.64 8132.46 7404.84 8125.3 7396.24 C 8113.83 7381.91 8105.23 7401.97 8086.6 7393.37 C 8076.57 7387.64 8073.7 7370.44 8062.24 7369.01 C 8055.07 7367.58 8059.37 7390.51 8050.77 7390.51 C 8024.97 7389.07 8042.17 7330.31 8023.54 7363.28 C 8020.67 7369.01 8030.71 7380.47 8024.97 7384.77 C 8014.94 7394.81 7969.08 7376.17 7959.05 7380.47 C 7940.42 7387.64 7926.09 7404.84 7898.85 7412 C 7883.09 7416.3 7878.79 7391.94 7870.19 7387.64 C 7865.89 7386.21 7871.62 7397.67 7868.76 7400.54 C 7863.03 7409.14 7852.99 7416.3 7844.39 7420.6 C 7841.53 7422.04 7842.96 7414.87 7841.53 7414.87 C 7825.76 7407.7 7841.53 7394.81 7831.5 7379.04 C 7825.76 7370.44 7818.6 7361.84 7810 7356.11 C 7807.13 7354.68 7802.83 7360.41 7801.4 7357.54 C 7794.23 7351.81 7797.1 7338.91 7789.93 7334.61 C 7775.6 7324.58 7749.81 7363.28 7771.3 7310.25 C 7768.44 7308.82 7765.57 7307.38 7761.27 7304.52 C 7756.97 7300.22 7759.84 7293.05 7756.97 7288.75 C 7741.21 7268.69 7701.08 7250.06 7683.88 7235.72 C 7678.15 7232.86 7686.75 7218.53 7682.45 7214.23 C 7678.15 7209.93 7656.65 7225.69 7642.32 7191.3 C 7643.75 7189.86 7643.75 7189.86 7645.18 7188.43 C 7646.62 7187 7643.75 7185.56 7645.18 7185.56 C 7669.55 7181.26 7685.31 7201.33 7683.88 7164.07 C 7683.88 7155.47 7703.94 7168.36 7699.64 7155.47 L 7681.01 7126.8 C 7682.45 7116.77 7726.88 7102.44 7729.74 7098.14 C 7729.74 7096.71 7738.34 7039.38 7738.34 7039.38 C 7748.37 7000.68 7756.97 7047.98 7771.3 6993.52 C 7775.6 6976.32 7755.54 6970.59 7755.54 6951.96 C 7765.57 6953.39 7861.59 6923.29 7867.33 6918.99 C 7883.09 6901.8 7871.62 6878.87 7900.29 6868.83 C 7910.32 6864.53 7966.21 6871.7 7974.81 6865.97 C 7979.11 6863.1 7977.68 6854.5 7981.98 6851.64 C 7984.84 6850.2 7989.14 6858.8 7992.01 6855.93 C 8009.08 6842.57 8015.32 6824.48 8011.68 6807.71 C 8017.85 6823.69 8018.06 6796.74 8017.81 6794.31 C 8016.62 6813.43 8016.88 6812.94 8035.01 6812.94 C 8040.74 6812.94 8047.9 6817.24 8052.2 6812.94 C 8057.94 6810.07 8057.94 6798.61 8063.67 6795.74 C 8079.43 6785.71 8100.93 6791.44 8118.13 6785.71 C 8158.26 6771.38 8168.29 6754.18 8194.09 6738.42 C 8198.39 6735.55 8205.55 6747.01 8211.29 6744.15 C 8219.88 6742.71 8224.18 6728.38 8232.78 6731.25 C 8244.25 6732.68 8247.11 6748.45 8257.15 6754.18 C 8258.58 6757.05 8262.88 6751.31 8265.75 6752.75 C 8270.04 6754.18 8272.91 6764.21 8277.21 6762.78 C 8282.94 6761.35 8282.94 6749.88 8288.68 6748.45 C 8304.44 6744.15 8323.07 6752.75 8337.4 6759.91 C 8340.27 6761.35 8337.4 6768.51 8341.7 6768.51 C 8361.77 6769.94 8393.3 6745.58 8413.36 6738.42 M 7830.06 7293.05 C 7847.26 7261.52 7804.27 7295.92 7802.83 7294.48 C 7792.8 7290.18 7784.2 7271.55 7775.6 7277.29 C 7772.74 7277.29 7782.77 7298.78 7781.34 7307.38 C 7795.67 7307.38 7821.46 7307.38 7830.06 7293.05 M 7805.7 7463.6 C 7794.23 7473.63 7789.93 7459.3 7779.9 7457.86 C 7774.17 7457.86 7768.44 7465.03 7762.7 7463.6 C 7752.67 7460.73 7756.97 7427.77 7748.37 7434.93 C 7744.07 7437.8 7751.24 7447.83 7748.37 7453.56 C 7736.91 7477.93 7729.74 7436.37 7761.27 7394.81 C 7765.57 7401.97 7771.3 7409.14 7774.17 7417.74 C 7774.17 7419.17 7769.87 7432.07 7777.04 7433.5 C 7784.2 7434.93 7821.46 7450.7 7805.7 7463.6 M 8295.84 7341.78 C 8288.68 7333.18 8278.64 7326.01 8267.18 7321.71 C 8249.98 7315.98 8225.62 7343.21 8206.99 7324.58 C 8204.12 7320.28 8212.72 7314.55 8209.85 7310.25 C 8205.55 7297.35 8121 7257.22 8105.23 7260.09 C 8096.63 7262.95 8092.33 7272.99 8085.17 7277.29 C 8080.87 7278.72 8088.03 7268.69 8090.9 7265.82 C 8095.2 7261.52 8100.93 7257.22 8105.23 7252.92 C 8110.96 7247.19 8115.26 7242.89 8121 7238.59 C 8129.59 7232.86 8138.19 7228.56 8146.79 7224.26 C 8171.16 7237.16 8168.29 7250.06 8192.65 7262.95 C 8196.95 7265.82 8202.69 7267.25 8208.42 7267.25 C 8211.28 7267.25 8212.72 7261.52 8215.58 7262.95 C 8222.75 7265.82 8228.48 7274.42 8237.08 7277.29 C 8245.68 7278.72 8275.78 7261.52 8281.51 7271.55 C 8282.94 7272.99 8280.08 7274.42 8280.08 7275.85 C 8278.64 7277.29 8277.21 7280.15 8278.64 7281.59 C 8284.38 7287.32 8294.41 7287.32 8298.71 7294.48 C 8298.71 7295.92 8295.84 7338.91 8310.17 7348.94 C 8325.94 7360.41 8390.43 7338.91 8364.63 7381.91 C 8350.3 7404.84 8311.61 7376.17 8303.01 7366.14 C 8301.57 7363.28 8307.31 7361.84 8307.31 7358.98 C 8305.87 7354.68 8301.57 7350.38 8297.27 7347.51 C 8294.41 7347.51 8291.54 7354.68 8290.11 7351.81 C 8290.11 7348.94 8298.71 7344.64 8295.84 7341.78 " }
+ }
+ }
+ // PATH visit ee count: 241
+ DemoShape {
+ objectName: "ee"
+ ShapePath {
+ objectName: "svg_path:ee"
+ // boundingRect: 7343.68, 3343.25 605.443x415.507
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7607.92 3723.03 C 7619.39 3720.17 7663.82 3678.61 7676.71 3668.57 C 7685.31 3662.84 7701.08 3690.07 7696.78 3671.44 C 7696.78 3670.01 7695.35 3670.01 7695.35 3668.57 C 7695.35 3665.71 7692.48 3661.41 7695.35 3661.41 C 7708.24 3661.41 7724.01 3682.91 7732.61 3685.77 C 7736.91 3687.21 7741.21 3678.61 7745.51 3678.61 C 7749.81 3680.04 7748.37 3688.64 7752.67 3690.07 C 7761.27 3692.94 7769.87 3684.34 7778.47 3687.21 C 7782.77 3688.64 7777.04 3697.24 7779.9 3700.1 C 7785.63 3708.7 7831.5 3738.8 7841.53 3737.37 C 7842.96 3737.37 7870.19 3707.27 7870.19 3707.27 C 7875.92 3705.84 7918.92 3721.6 7920.35 3714.44 C 7923.22 3707.27 7913.19 3687.21 7917.49 3682.91 C 7920.35 3680.04 7927.52 3680.04 7928.95 3675.74 C 7930.38 3672.87 7924.65 3671.44 7924.65 3667.14 C 7923.22 3664.27 7923.22 3659.98 7924.65 3657.11 C 7941.85 3642.78 7943.28 3658.54 7949.02 3641.34 C 7950.45 3637.04 7937.55 3639.91 7933.25 3635.61 C 7923.22 3625.58 7928.95 3618.41 7926.09 3608.38 C 7924.65 3604.08 7901.72 3591.18 7897.42 3586.88 C 7885.96 3566.82 7898.85 3575.42 7898.85 3556.79 C 7898.85 3530.99 7870.19 3503.76 7865.89 3477.96 C 7864.46 3462.2 7881.66 3450.73 7885.96 3439.27 C 7885.96 3439.27 7887.39 3403.44 7887.39 3403.44 C 7887.39 3396.27 7881.66 3387.67 7885.96 3381.94 C 7900.29 3351.84 7924.65 3364.74 7890.26 3343.25 C 7888.82 3348.98 7885.96 3354.71 7875.92 3360.44 C 7863.03 3369.04 7728.31 3370.48 7705.38 3367.61 C 7701.08 3367.61 7698.21 3360.44 7692.48 3361.88 C 7665.25 3367.61 7726.88 3383.37 7681.01 3377.64 C 7673.85 3376.21 7672.41 3360.44 7665.25 3361.88 C 7659.52 3363.31 7669.55 3374.78 7666.68 3380.51 C 7662.38 3389.11 7653.78 3397.71 7645.18 3402.01 C 7630.85 3409.17 7615.09 3412.04 7599.32 3409.17 C 7593.59 3407.74 7589.29 3396.27 7584.99 3399.14 C 7577.83 3403.44 7595.02 3419.2 7587.86 3420.64 C 7573.53 3424.94 7530.53 3426.37 7521.93 3447.87 C 7519.07 3455.03 7534.83 3462.2 7530.53 3467.93 C 7523.37 3479.4 7480.37 3476.53 7474.64 3489.43 C 7454.57 3543.89 7491.84 3509.49 7500.43 3532.42 C 7504.73 3545.32 7483.24 3561.09 7488.97 3573.99 C 7494.7 3589.75 7517.63 3605.51 7520.5 3611.25 C 7529.1 3624.15 7514.77 3638.48 7533.4 3648.51 C 7540.56 3651.38 7549.16 3644.21 7556.33 3647.08 C 7563.49 3648.51 7567.79 3662.84 7574.96 3658.54 C 7584.99 3652.81 7584.99 3598.35 7615.09 3634.18 C 7617.95 3638.48 7607.92 3642.78 7607.92 3648.51 C 7607.92 3659.98 7613.66 3671.44 7613.66 3682.91 C 7613.66 3711.57 7602.19 3695.8 7605.06 3721.6 L 7607.92 3723.03 M 7451.71 3612.68 C 7470.34 3625.58 7468.91 3627.01 7491.84 3637.04 C 7501.87 3642.78 7474.64 3624.15 7480.37 3635.61 C 7486.1 3648.51 7481.8 3629.88 7471.77 3631.31 C 7463.17 3632.75 7471.77 3651.38 7464.61 3658.54 C 7463.17 3659.98 7457.44 3651.38 7456.01 3654.24 C 7453.14 3657.11 7457.44 3664.27 7456.01 3670.01 C 7448.84 3688.64 7434.51 3682.91 7430.21 3684.34 C 7427.34 3684.34 7427.34 3690.07 7424.48 3691.51 C 7415.88 3695.8 7405.85 3698.67 7398.68 3702.97 C 7397.25 3702.97 7397.25 3704.4 7397.25 3705.84 C 7397.25 3717.3 7400.11 3730.2 7397.25 3741.67 C 7394.38 3748.83 7384.35 3763.16 7378.62 3757.43 C 7361.42 3737.37 7394.38 3725.9 7391.51 3710.14 C 7390.08 3705.84 7382.92 3711.57 7378.62 3708.7 C 7370.02 3704.4 7357.12 3701.54 7352.82 3691.51 C 7349.95 3685.77 7365.72 3682.91 7364.28 3677.17 C 7362.85 3665.71 7339.92 3661.41 7344.22 3651.38 C 7347.09 3642.78 7361.42 3662.84 7371.45 3662.84 C 7380.05 3662.84 7367.15 3639.91 7375.75 3635.61 C 7380.05 3634.18 7385.78 3648.51 7387.21 3644.21 C 7397.25 3609.81 7418.74 3622.71 7451.71 3612.68 M 7400.11 3529.56 C 7402.98 3529.56 7407.28 3528.12 7408.71 3530.99 C 7411.58 3533.86 7407.28 3541.02 7411.58 3543.89 C 7417.31 3546.76 7427.34 3539.59 7433.08 3542.46 C 7481.8 3571.12 7435.94 3565.39 7433.08 3571.12 C 7421.61 3598.35 7461.74 3575.42 7408.71 3605.51 C 7392.95 3614.11 7391.51 3579.72 7391.51 3579.72 C 7385.78 3563.95 7359.98 3569.69 7349.95 3566.82 C 7339.92 3565.39 7371.45 3561.09 7381.48 3558.22 C 7394.38 3555.35 7397.25 3538.16 7400.11 3529.56 " }
+ }
+ }
+ // PATH visit lv count: 185
+ DemoShape {
+ objectName: "lv"
+ ShapePath {
+ objectName: "svg_path:lv"
+ // boundingRect: 7317.75, 3661.41 750.748x453.11
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7341.35 4114.29 C 7347.09 4114.29 7352.82 4115.72 7357.12 4111.42 C 7362.85 4102.82 7361.42 4089.93 7367.15 4081.33 C 7378.62 4066.99 7424.48 4038.33 7441.68 4034.03 C 7451.71 4031.17 7497.57 4034.03 7507.6 4031.17 C 7513.33 4029.73 7517.63 4018.27 7523.37 4018.27 C 7533.4 4019.7 7539.13 4044.06 7544.86 4036.9 C 7549.16 4034.03 7547.73 4026.87 7552.03 4022.57 C 7564.93 4009.67 7570.66 4024 7586.42 4022.57 C 7595.02 4021.13 7599.32 4012.53 7607.92 4012.53 C 7609.36 4011.1 7652.35 4022.57 7653.78 4021.13 C 7660.95 4019.7 7663.82 4009.67 7670.98 4008.23 C 7683.88 4005.37 7683.88 4026.87 7695.35 4009.67 C 7703.94 3999.64 7703.94 3975.27 7721.14 3972.41 C 7735.47 3969.54 7741.21 4015.4 7759.84 4015.4 C 7778.47 4015.4 7797.1 4003.94 7815.73 4009.67 C 7842.96 4016.83 7885.96 4069.86 7918.92 4075.59 C 7954.75 4081.33 7956.18 4026.87 7977.68 4028.3 C 7984.84 4028.3 7989.14 4036.9 7994.88 4036.9 C 7999.18 4036.9 7996.31 4026.87 7999.18 4025.43 C 8007.78 4022.57 8019.24 4031.17 8026.41 4025.43 C 8026.41 4025.43 8033.57 3982.44 8036.44 3978.14 C 8039.31 3969.54 8066.54 3948.04 8066.54 3946.61 C 8078 3905.05 8036.44 3866.35 8012.08 3846.29 C 8002.04 3839.12 8022.11 3826.22 8002.04 3823.36 C 7994.88 3821.92 7989.14 3833.39 7981.98 3831.96 C 7979.11 3831.96 7981.98 3827.66 7981.98 3824.79 C 7981.98 3823.36 7983.41 3820.49 7983.41 3819.06 C 7983.41 3809.02 7986.28 3798.99 7984.84 3788.96 C 7983.41 3784.66 7973.38 3786.09 7973.38 3781.79 C 7973.38 3763.16 8007.78 3751.7 7960.48 3734.5 C 7954.75 3733.07 7946.15 3733.07 7941.85 3728.77 C 7937.55 3723.03 7944.72 3713 7938.98 3710.14 C 7933.25 3705.84 7927.52 3713 7920.35 3714.44 C 7918.92 3721.6 7875.92 3705.84 7870.19 3707.27 C 7870.19 3707.27 7842.96 3737.37 7841.53 3737.37 C 7831.5 3738.8 7785.63 3708.7 7779.9 3700.1 C 7777.04 3697.24 7782.77 3688.64 7778.47 3687.21 C 7769.87 3684.34 7761.27 3692.94 7752.67 3690.07 C 7748.37 3688.64 7749.81 3680.04 7745.51 3678.61 C 7741.21 3678.61 7736.91 3687.21 7732.61 3685.77 C 7724.01 3682.91 7708.24 3661.41 7695.35 3661.41 C 7692.48 3661.41 7695.35 3665.71 7695.35 3668.57 C 7695.35 3670.01 7696.78 3670.01 7696.78 3671.44 C 7701.08 3690.07 7685.31 3662.84 7676.71 3668.57 C 7663.82 3678.61 7619.39 3720.17 7607.92 3723.03 L 7605.06 3721.6 C 7606.49 3724.47 7606.49 3728.77 7606.49 3733.07 C 7612.22 3763.16 7649.48 3804.73 7640.89 3837.69 C 7636.59 3857.75 7602.19 3899.31 7580.69 3902.18 C 7572.09 3903.61 7566.36 3895.01 7559.19 3893.58 C 7544.86 3889.28 7542 3899.31 7533.4 3884.98 C 7523.37 3870.65 7523.37 3850.59 7510.47 3839.12 C 7497.57 3826.22 7467.47 3820.49 7453.14 3807.59 C 7444.54 3800.43 7453.14 3781.79 7444.54 3777.49 C 7431.64 3771.76 7421.61 3791.83 7410.15 3798.99 C 7395.81 3810.46 7375.75 3813.32 7362.85 3827.66 C 7328.45 3863.48 7358.55 3905.05 7345.65 3940.88 C 7341.35 3955.21 7322.72 3960.94 7318.42 3975.27 C 7315.56 3983.87 7322.72 3992.47 7322.72 4002.5 C 7322.72 4012.53 7318.42 4022.57 7321.29 4031.17 C 7321.29 4032.6 7322.72 4034.03 7324.16 4034.03 C 7309.82 4051.23 7332.75 4094.22 7341.35 4114.29 " }
+ }
+ }
+ // PATH visit lt count: 204
+ DemoShape {
+ objectName: "lt"
+ ShapePath {
+ objectName: "svg_path:lt"
+ // boundingRect: 7341.35, 3972.28 628.209x494.871
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7579.26 4400.92 C 7582.13 4398.06 7582.13 4392.32 7586.42 4390.89 C 7607.92 4383.72 7597.89 4402.36 7612.22 4408.09 C 7652.35 4425.29 7636.59 4393.76 7662.38 4431.02 C 7670.98 4442.48 7662.38 4458.25 7670.98 4463.98 C 7688.18 4475.45 7729.74 4452.52 7741.21 4446.78 C 7752.67 4442.48 7758.4 4468.28 7771.3 4456.82 C 7779.9 4451.08 7781.34 4436.75 7789.93 4431.02 C 7797.1 4426.72 7805.7 4431.02 7812.86 4429.59 C 7820.03 4426.72 7799.97 4410.95 7805.7 4405.22 C 7812.86 4396.62 7827.2 4399.49 7837.23 4395.19 C 7860.16 4382.29 7828.63 4383.72 7844.39 4367.96 C 7848.69 4362.23 7881.66 4346.46 7884.52 4365.09 C 7885.96 4369.39 7878.79 4372.26 7878.79 4376.56 C 7877.36 4389.46 7906.02 4379.42 7908.89 4372.26 C 7921.79 4340.73 7877.36 4349.33 7877.36 4343.6 C 7875.92 4337.86 7890.26 4297.73 7890.26 4296.3 C 7890.26 4296.3 7877.36 4256.17 7877.36 4256.17 C 7877.36 4254.74 7883.09 4254.74 7883.09 4251.87 C 7900.29 4198.85 7887.39 4254.74 7914.62 4211.74 C 7921.79 4203.15 7914.62 4188.81 7920.35 4178.78 C 7927.52 4168.75 7949.02 4183.08 7957.61 4173.05 C 7961.91 4167.32 7956.18 4158.72 7957.61 4152.98 C 7960.48 4148.68 7967.65 4148.68 7969.08 4144.39 C 7974.81 4124.32 7927.52 4150.12 7928.95 4137.22 C 7930.38 4115.72 7940.42 4097.09 7931.82 4075.59 L 7931.82 4074.16 C 7927.52 4075.59 7923.22 4075.59 7918.92 4075.59 C 7885.96 4069.86 7842.96 4016.83 7815.73 4009.67 C 7797.1 4003.94 7778.47 4015.4 7759.84 4015.4 C 7741.21 4015.4 7735.47 3969.54 7721.14 3972.41 C 7703.94 3975.27 7703.94 3999.64 7695.35 4009.67 C 7683.88 4026.87 7683.88 4005.37 7670.98 4008.23 C 7663.82 4009.67 7660.95 4019.7 7653.78 4021.13 C 7652.35 4022.57 7609.36 4011.1 7607.92 4012.53 C 7599.32 4012.53 7595.02 4021.13 7586.42 4022.57 C 7570.66 4024 7564.93 4009.67 7552.03 4022.57 C 7547.73 4026.87 7549.16 4034.03 7544.86 4036.9 C 7539.13 4044.06 7533.4 4019.7 7523.37 4018.27 C 7517.63 4018.27 7513.33 4029.73 7507.6 4031.17 C 7497.57 4034.03 7451.71 4031.17 7441.67 4034.03 C 7424.48 4038.33 7378.62 4066.99 7367.15 4081.33 C 7361.42 4089.93 7362.85 4102.82 7357.12 4111.42 C 7352.82 4115.72 7347.09 4114.29 7341.35 4114.29 C 7342.79 4117.16 7342.79 4118.59 7344.22 4120.02 C 7354.25 4157.28 7337.05 4134.35 7351.39 4170.18 C 7352.82 4173.05 7354.25 4175.91 7357.12 4178.78 C 7359.98 4184.51 7362.85 4188.81 7365.72 4193.11 C 7372.88 4203.15 7378.62 4214.61 7380.05 4226.08 C 7381.48 4234.67 7375.75 4243.27 7377.18 4251.87 C 7378.62 4254.74 7380.05 4244.71 7382.92 4244.71 C 7388.65 4243.27 7388.65 4254.74 7390.08 4259.04 C 7390.08 4261.9 7390.08 4263.34 7390.08 4264.77 C 7394.38 4263.34 7395.81 4257.61 7400.11 4257.61 C 7404.41 4259.04 7404.41 4266.2 7408.71 4269.07 C 7425.91 4279.1 7454.57 4277.67 7473.2 4283.4 C 7476.07 4284.84 7471.77 4289.13 7474.64 4289.13 C 7496.14 4289.13 7516.2 4270.5 7536.26 4276.24 C 7542 4277.67 7539.13 4289.13 7544.86 4293.43 C 7546.3 4294.87 7550.6 4290.57 7553.46 4292 C 7559.19 4294.87 7564.93 4299.17 7569.23 4304.9 C 7577.83 4319.23 7547.73 4357.93 7579.26 4400.92 M 7357.12 4178.78 C 7355.69 4180.21 7355.69 4181.65 7355.69 4181.65 C 7351.39 4193.11 7361.42 4204.58 7362.85 4216.04 C 7364.28 4230.38 7361.42 4246.14 7355.69 4263.34 C 7355.69 4263.34 7357.12 4263.34 7357.12 4263.34 C 7361.42 4254.74 7362.85 4246.14 7364.28 4234.67 C 7364.28 4228.94 7365.72 4208.88 7365.72 4193.11 C 7365.72 4174.48 7364.28 4161.58 7357.12 4178.78 " }
+ }
+ }
+ // PATH visit pl count: 369
+ DemoShape {
+ objectName: "pl"
+ ShapePath {
+ objectName: "svg_path:pl"
+ // boundingRect: 6620.47, 4386.96 1245.42x1032.41
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6763.79 5169.1 C 6802.48 5163.37 6756.62 5131.84 6796.75 5139 C 6822.55 5141.87 6801.05 5153.33 6816.81 5164.8 C 6839.75 5183.43 6858.38 5170.53 6877.01 5182 C 6882.74 5186.3 6881.31 5193.46 6884.17 5197.76 C 6887.04 5199.2 6888.47 5194.9 6889.91 5194.9 C 6894.21 5193.46 6898.51 5190.6 6899.94 5193.46 C 6904.24 5197.76 6899.94 5204.93 6904.24 5209.23 C 6904.24 5209.23 6935.77 5192.03 6942.93 5196.33 C 6981.63 5214.96 6927.17 5222.13 6930.03 5240.76 C 6930.03 5240.76 6963 5260.82 6963 5260.82 C 6967.3 5266.55 6975.9 5303.82 6997.39 5295.22 C 7003.13 5292.35 7001.69 5282.32 7005.99 5278.02 C 7013.16 5272.29 7027.49 5275.15 7031.79 5266.55 C 7033.22 5263.69 7001.69 5232.16 7010.29 5230.72 C 7031.79 5227.86 7056.15 5249.36 7069.05 5253.66 C 7074.78 5255.09 7081.95 5252.22 7087.68 5252.22 C 7093.42 5252.22 7100.58 5255.09 7106.31 5252.22 C 7109.18 5250.79 7107.75 5245.06 7109.18 5242.19 C 7110.61 5240.76 7113.48 5240.76 7113.48 5242.19 C 7130.68 5276.59 7119.21 5255.09 7100.58 5272.29 C 7094.85 5276.59 7114.91 5278.02 7120.65 5282.32 C 7124.95 5286.62 7123.51 5293.78 7127.81 5298.08 C 7130.68 5300.95 7134.98 5302.38 7137.84 5302.38 C 7179.41 5298.08 7132.11 5295.22 7156.48 5285.19 C 7159.34 5283.75 7159.34 5289.48 7162.21 5290.92 C 7167.94 5295.22 7183.71 5299.52 7190.87 5300.95 C 7199.47 5303.82 7212.37 5299.52 7219.53 5305.25 C 7220.97 5306.68 7233.87 5341.08 7235.3 5342.51 C 7241.03 5346.81 7252.5 5342.51 7258.23 5348.24 C 7263.96 5355.41 7259.66 5368.31 7265.4 5375.47 C 7268.26 5379.77 7278.29 5372.61 7282.59 5376.91 C 7286.89 5381.21 7279.73 5391.24 7284.03 5395.54 C 7306.96 5414.17 7325.59 5341.08 7339.92 5351.11 C 7348.52 5356.84 7348.52 5369.74 7355.69 5376.91 C 7357.12 5378.34 7362.85 5374.04 7364.28 5375.47 C 7365.72 5378.34 7359.98 5384.07 7362.85 5385.51 C 7370.02 5386.94 7377.18 5379.77 7382.92 5382.64 C 7392.95 5386.94 7384.35 5414.17 7384.35 5417.04 C 7384.35 5424.2 7397.25 5412.74 7404.41 5411.3 C 7410.15 5409.87 7415.88 5421.34 7421.61 5418.47 C 7423.04 5417.04 7440.24 5372.61 7460.31 5368.31 C 7466.04 5366.88 7471.77 5372.61 7477.5 5372.61 C 7483.24 5371.18 7486.1 5362.58 7491.84 5364.01 C 7503.3 5365.44 7510.47 5381.21 7521.93 5381.21 C 7530.53 5381.21 7537.7 5372.61 7542 5365.44 C 7543.43 5362.58 7534.83 5362.58 7534.83 5359.71 C 7539.13 5339.65 7547.73 5359.71 7553.46 5356.84 C 7557.76 5355.41 7557.76 5346.81 7562.06 5346.81 C 7583.56 5341.08 7632.29 5342.51 7650.92 5356.84 C 7655.22 5361.14 7653.78 5371.18 7659.52 5374.04 C 7668.12 5379.77 7678.15 5379.77 7688.18 5382.64 C 7706.81 5388.37 7724.01 5389.81 7741.21 5392.67 C 7748.37 5392.67 7755.54 5389.81 7762.7 5391.24 C 7765.57 5392.67 7771.3 5399.84 7769.87 5396.97 C 7752.67 5342.51 7767 5399.84 7742.64 5369.74 C 7736.91 5364.01 7748.37 5353.98 7746.94 5346.81 C 7742.64 5332.48 7728.31 5321.01 7725.44 5305.25 C 7721.14 5293.78 7808.57 5131.84 7820.03 5121.8 C 7845.83 5100.31 7861.59 5131.84 7865.89 5080.24 C 7865.89 5068.78 7837.23 5042.98 7835.8 5041.55 C 7828.63 5027.22 7861.59 5028.65 7857.29 5024.35 C 7834.36 5007.15 7792.8 4979.92 7779.9 4952.69 C 7777.04 4945.52 7782.77 4935.49 7779.9 4926.89 C 7775.6 4916.86 7762.7 4909.7 7758.4 4899.66 C 7746.94 4873.87 7777.04 4825.14 7748.37 4802.21 C 7736.91 4793.61 7722.58 4789.31 7709.68 4787.88 C 7706.81 4786.44 7703.94 4793.61 7701.08 4792.18 C 7659.52 4776.41 7749.81 4700.45 7759.84 4690.42 C 7761.27 4688.99 7764.14 4686.12 7764.14 4683.26 C 7759.84 4604.43 7696.78 4537.07 7670.98 4463.98 C 7662.38 4458.25 7670.98 4442.48 7662.38 4431.02 C 7636.59 4393.76 7652.35 4425.29 7612.22 4408.09 C 7597.89 4402.36 7607.92 4383.72 7586.42 4390.89 C 7582.13 4392.32 7582.13 4399.49 7577.83 4402.36 C 7511.9 4441.05 7318.42 4448.22 7236.73 4436.75 C 7210.94 4463.98 7170.81 4479.75 7137.84 4466.85 C 7109.18 4455.38 7114.91 4428.15 7106.31 4410.95 C 7103.45 4406.65 7093.42 4395.19 7099.15 4396.62 C 7103.45 4396.62 7107.75 4396.62 7110.61 4398.06 C 7103.45 4393.76 7094.85 4389.46 7086.25 4388.02 C 7034.66 4379.42 6977.33 4425.29 6938.63 4448.22 C 6925.74 4455.38 6909.97 4452.52 6899.94 4461.12 C 6875.57 4482.61 6869.84 4517.01 6862.68 4518.44 C 6809.65 4531.34 6759.49 4554.27 6709.33 4574.34 C 6696.43 4580.07 6687.83 4595.83 6674.93 4598.7 C 6667.77 4601.57 6660.6 4594.4 6653.43 4597.27 C 6640.54 4604.43 6670.63 4635.96 6657.73 4638.83 C 6660.6 4661.76 6683.53 4706.19 6686.4 4720.52 C 6690.7 4754.91 6644.83 4777.84 6654.87 4805.07 C 6660.6 4822.27 6715.06 4829.44 6715.06 4850.94 C 6715.06 4872.43 6697.86 4879.6 6710.76 4895.36 C 6715.06 4902.53 6725.09 4905.4 6727.96 4914 C 6732.26 4921.16 6726.53 4931.19 6727.96 4938.36 C 6729.39 4942.66 6735.12 4941.23 6736.56 4944.09 C 6743.72 4954.12 6726.53 4978.49 6722.23 4987.09 C 6713.63 4999.99 6735.12 5007.15 6739.42 5020.05 C 6742.29 5027.22 6732.26 5035.81 6736.56 5041.55 C 6742.29 5050.15 6755.19 5047.28 6760.92 5053.01 C 6778.12 5068.78 6775.25 5141.87 6763.79 5169.1 M 6620.47 4628.8 C 6620.47 4628.8 6621.9 4628.8 6621.9 4628.8 M 6621.9 4628.8 C 6623.34 4630.23 6624.77 4630.23 6626.2 4631.66 C 6624.77 4630.23 6623.34 4630.23 6621.9 4628.8 M 6629.07 4631.66 C 6629.07 4633.09 6629.07 4633.09 6629.07 4633.09 C 6630.5 4633.09 6630.5 4633.09 6630.5 4633.09 M 6630.5 4633.09 C 6630.5 4633.09 6630.5 4633.09 6631.94 4633.09 C 6630.5 4633.09 6630.5 4633.09 6630.5 4633.09 M 6633.37 4633.09 C 6633.37 4633.09 6633.37 4634.53 6634.8 4634.53 C 6633.37 4634.53 6633.37 4633.09 6633.37 4633.09 M 6634.8 4634.53 C 6636.24 4634.53 6636.24 4634.53 6636.24 4634.53 M 6636.24 4634.53 C 6637.67 4634.53 6637.67 4634.53 6637.67 4635.96 C 6637.67 4634.53 6637.67 4634.53 6636.24 4634.53 M 6637.67 4635.96 C 6639.1 4635.96 6639.1 4635.96 6639.1 4635.96 M 6639.1 4635.96 C 6640.54 4635.96 6640.54 4635.96 6640.54 4635.96 C 6640.54 4635.96 6640.54 4635.96 6639.1 4635.96 M 6652 4638.83 C 6652 4638.83 6652 4638.83 6653.43 4638.83 C 6653.43 4638.83 6652 4638.83 6652 4638.83 M 7129.24 4408.09 C 7134.98 4410.95 7140.71 4413.82 7145.01 4419.55 C 7146.44 4420.99 7146.44 4426.72 7143.58 4425.29 C 7133.54 4418.12 7130.68 4412.39 7129.24 4408.09 " }
+ }
+ }
+ // PATH visit cz count: 218
+ DemoShape {
+ objectName: "cz"
+ ShapePath {
+ objectName: "svg_path:cz"
+ // boundingRect: 6433.73, 5135.41 831.662x469.065
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7050.42 5566.09 C 7071.92 5481.53 7119.21 5553.19 7162.21 5498.73 C 7165.07 5494.43 7160.77 5487.26 7163.64 5482.96 C 7167.94 5477.23 7177.97 5480.1 7182.27 5474.36 C 7182.27 5474.36 7193.74 5429.93 7193.74 5428.5 C 7206.64 5405.57 7241.03 5386.94 7265.4 5375.47 C 7259.66 5368.31 7263.96 5355.41 7258.23 5348.24 C 7252.5 5342.51 7241.03 5346.81 7235.3 5342.51 C 7233.87 5341.08 7220.97 5306.68 7219.53 5305.25 C 7212.37 5299.52 7199.47 5303.82 7190.87 5300.95 C 7183.71 5299.52 7167.94 5295.22 7162.21 5290.92 C 7159.34 5289.48 7159.34 5283.75 7156.48 5285.19 C 7132.11 5295.22 7179.41 5298.08 7137.84 5302.38 C 7134.98 5302.38 7130.68 5300.95 7127.81 5298.08 C 7123.51 5293.78 7124.95 5286.62 7120.65 5282.32 C 7114.91 5278.02 7094.85 5276.59 7100.58 5272.29 C 7119.21 5255.09 7130.68 5276.59 7113.48 5242.19 C 7113.48 5240.76 7110.61 5240.76 7109.18 5242.19 C 7107.75 5245.06 7109.18 5250.79 7106.31 5252.22 C 7100.58 5255.09 7093.42 5252.22 7087.68 5252.22 C 7081.95 5252.22 7074.78 5255.09 7069.05 5253.66 C 7056.15 5249.36 7031.79 5227.86 7010.29 5230.72 C 7001.69 5232.16 7033.22 5263.69 7031.79 5266.55 C 7027.49 5275.15 7013.16 5272.29 7005.99 5278.02 C 7001.69 5282.32 7003.13 5292.35 6997.39 5295.22 C 6975.9 5303.82 6967.3 5266.55 6963 5260.82 C 6963 5260.82 6930.03 5240.76 6930.03 5240.76 C 6927.17 5222.13 6981.63 5214.96 6942.93 5196.33 C 6935.77 5192.03 6904.24 5209.23 6904.24 5209.23 C 6899.94 5204.93 6904.24 5197.76 6899.94 5193.46 C 6898.51 5190.6 6894.21 5193.46 6889.91 5194.9 C 6888.47 5194.9 6887.04 5199.2 6884.17 5197.76 C 6881.31 5193.46 6882.74 5186.3 6877.01 5182 C 6858.38 5170.53 6839.75 5183.43 6816.81 5164.8 C 6801.05 5153.33 6822.55 5141.87 6796.75 5139 C 6756.62 5131.84 6802.48 5163.37 6763.79 5169.1 C 6760.92 5174.83 6759.49 5179.13 6755.19 5179.13 C 6736.56 5183.43 6706.46 5104.61 6690.7 5149.03 C 6687.83 5156.2 6713.63 5161.93 6707.89 5166.23 C 6690.7 5180.56 6666.33 5182 6649.13 5192.03 C 6646.27 5193.46 6649.13 5200.63 6646.27 5202.06 C 6634.8 5207.79 6619.04 5199.2 6610.44 5206.36 C 6603.27 5212.09 6606.14 5223.56 6598.97 5229.29 C 6596.11 5232.16 6593.24 5222.13 6588.94 5223.56 C 6576.04 5226.43 6574.61 5243.62 6566.01 5249.36 C 6561.71 5253.66 6553.11 5246.49 6548.81 5250.79 C 6544.51 5253.66 6548.81 5262.25 6544.51 5266.55 C 6538.78 5273.72 6523.02 5260.82 6518.72 5260.82 C 6482.89 5266.55 6477.15 5293.78 6465.69 5316.71 C 6462.82 5322.45 6462.82 5302.38 6457.09 5298.08 C 6452.79 5292.35 6444.19 5290.92 6437.03 5289.48 C 6424.13 5286.62 6451.36 5332.48 6480.02 5351.11 C 6482.89 5352.54 6492.92 5353.98 6494.35 5362.58 C 6498.65 5382.64 6480.02 5375.47 6477.15 5391.24 C 6475.72 5395.54 6507.25 5448.57 6511.55 5452.87 C 6535.91 5477.23 6541.65 5460.03 6557.41 5475.8 C 6567.44 5485.83 6568.88 5501.59 6578.91 5510.19 C 6598.97 5524.52 6649.13 5543.16 6659.17 5571.82 L 6660.6 5571.82 C 6683.53 5583.28 6700.73 5613.38 6730.82 5601.91 C 6736.56 5600.48 6735.12 5589.02 6740.86 5589.02 C 6750.89 5589.02 6760.92 5601.91 6770.95 5599.05 C 6770.95 5599.05 6782.42 5561.79 6782.42 5561.79 C 6788.15 5557.49 6798.18 5567.52 6801.05 5563.22 C 6803.92 5560.35 6801.05 5518.79 6802.48 5515.92 C 6806.78 5511.63 6815.38 5514.49 6821.11 5517.36 C 6823.98 5518.79 6821.11 5525.96 6823.98 5527.39 C 6831.15 5527.39 6836.88 5518.79 6844.05 5518.79 C 6885.61 5518.79 6918.57 5550.32 6957.27 5554.62 C 6963 5554.62 6970.16 5557.49 6975.9 5556.05 C 6984.5 5551.75 6987.36 5540.29 6994.53 5537.42 C 7004.56 5534.56 7018.89 5553.19 7026.06 5554.62 C 7030.36 5554.62 7036.09 5546.02 7040.39 5548.89 C 7047.55 5554.62 7050.42 5560.35 7050.42 5566.09 " }
+ }
+ }
+ // PATH visit sk count: 145
+ DemoShape {
+ objectName: "sk"
+ ShapePath {
+ objectName: "svg_path:sk"
+ // boundingRect: 7038.35, 5343.67 688.663x378.89
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7726.88 5389.81 C 7728.31 5404.14 7718.28 5424.2 7716.84 5438.53 C 7715.41 5448.57 7716.84 5458.6 7712.54 5467.2 C 7708.24 5477.23 7695.35 5480.1 7693.91 5490.13 C 7691.05 5500.16 7698.21 5510.19 7698.21 5521.66 C 7698.21 5533.12 7676.71 5533.12 7665.25 5535.99 C 7659.52 5537.42 7653.78 5544.59 7649.48 5541.72 C 7622.25 5533.12 7643.75 5505.89 7607.92 5504.46 C 7593.59 5504.46 7584.99 5524.52 7570.66 5525.96 C 7546.3 5530.26 7514.77 5504.46 7493.27 5531.69 C 7484.67 5543.15 7490.4 5581.85 7474.64 5589.02 C 7468.91 5590.45 7460.31 5584.72 7454.57 5587.58 C 7431.64 5601.91 7464.61 5611.95 7428.78 5617.68 C 7417.31 5619.11 7391.51 5587.58 7380.05 5607.65 C 7375.75 5614.81 7382.92 5627.71 7377.18 5633.44 C 7361.42 5649.21 7316.99 5639.18 7296.93 5653.51 C 7278.29 5664.97 7298.36 5685.04 7304.09 5693.64 C 7309.82 5703.67 7188 5725.17 7170.81 5722.3 C 7143.58 5716.57 7123.51 5679.31 7097.72 5676.44 C 7091.98 5675.01 7087.68 5676.44 7084.82 5677.87 C 7067.62 5659.24 7053.29 5640.61 7040.39 5619.11 C 7031.79 5604.78 7053.29 5584.72 7051.85 5566.09 C 7071.92 5481.53 7119.21 5551.75 7162.21 5498.73 C 7165.07 5494.43 7160.77 5487.26 7163.64 5482.96 C 7167.94 5477.23 7177.97 5480.1 7182.27 5474.36 C 7182.27 5474.36 7193.74 5429.93 7193.74 5428.5 C 7206.64 5405.57 7241.03 5386.94 7265.4 5375.47 C 7268.26 5379.77 7278.29 5372.61 7282.59 5376.91 C 7286.89 5381.21 7279.73 5391.24 7284.03 5395.54 C 7306.96 5414.17 7325.59 5341.08 7339.92 5351.11 C 7348.52 5356.84 7348.52 5369.74 7355.69 5376.91 C 7357.12 5378.34 7362.85 5374.04 7364.28 5375.47 C 7365.72 5378.34 7359.98 5384.07 7362.85 5385.51 C 7370.02 5386.94 7377.18 5379.77 7382.92 5382.64 C 7392.95 5386.94 7384.35 5414.17 7384.35 5417.04 C 7384.35 5424.2 7397.25 5412.74 7404.41 5411.3 C 7410.15 5409.87 7415.88 5421.34 7421.61 5418.47 C 7423.04 5417.04 7440.24 5372.61 7460.31 5368.31 C 7466.04 5366.88 7471.77 5372.61 7477.5 5372.61 C 7483.24 5371.18 7486.1 5362.58 7491.84 5364.01 C 7503.3 5365.44 7510.47 5381.21 7521.93 5381.21 C 7530.53 5381.21 7537.7 5372.61 7542 5365.44 C 7543.43 5362.58 7534.83 5362.58 7534.83 5359.71 C 7539.13 5339.65 7547.73 5359.71 7553.46 5356.84 C 7557.76 5355.41 7557.76 5346.81 7562.06 5346.81 C 7583.56 5341.08 7632.29 5342.51 7650.92 5356.84 C 7655.22 5361.14 7653.78 5371.18 7659.52 5374.04 C 7668.12 5379.77 7678.15 5379.77 7688.18 5382.64 C 7701.08 5386.94 7713.98 5388.37 7726.88 5389.81 " }
+ }
+ }
+ // PATH visit si count: 115
+ DemoShape {
+ objectName: "si"
+ ShapePath {
+ objectName: "svg_path:si"
+ // boundingRect: 6626.03, 5905.75 415.794x290.052
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7041.82 5974.54 C 7031.79 5973.1 7007.43 5967.37 7001.69 5968.81 C 6985.93 5971.67 7007.43 5991.74 6998.83 5997.47 C 6991.66 6000.33 6983.06 5996.04 6975.9 5998.9 C 6973.03 6000.33 6977.33 6006.07 6974.46 6008.93 C 6960.13 6020.4 6937.2 6021.83 6922.87 6034.73 C 6905.67 6050.5 6954.4 6090.62 6932.9 6106.39 C 6882.74 6142.22 6914.27 6082.03 6881.31 6129.32 C 6877.01 6135.05 6894.21 6136.49 6897.07 6143.65 C 6899.94 6149.38 6882.74 6147.95 6882.74 6153.68 C 6882.74 6156.55 6897.07 6175.18 6891.34 6182.35 C 6875.57 6198.11 6852.64 6173.75 6838.31 6173.75 C 6816.81 6173.75 6858.38 6193.81 6822.55 6183.78 C 6812.52 6180.91 6799.62 6165.15 6795.32 6156.55 C 6792.45 6153.68 6789.58 6145.08 6788.15 6147.95 C 6782.42 6156.55 6783.85 6166.58 6778.12 6173.75 C 6759.49 6198.11 6733.69 6173.75 6712.19 6180.91 C 6709.33 6182.35 6715.06 6188.08 6712.19 6190.95 C 6699.3 6198.11 6673.5 6196.68 6660.6 6190.95 C 6660.6 6190.95 6660.6 6189.51 6662.03 6189.51 C 6669.2 6182.35 6677.8 6175.18 6680.66 6168.02 C 6730.82 6170.88 6673.5 6136.49 6659.17 6127.89 C 6649.13 6122.15 6664.9 6103.52 6662.03 6096.36 C 6659.17 6089.19 6636.24 6109.26 6641.97 6090.62 C 6646.27 6077.73 6673.5 6066.26 6663.47 6057.66 C 6656.3 6051.93 6623.34 6054.8 6626.2 6037.6 C 6642.26 6003.66 6667.17 5992.19 6692.13 5996.04 C 6703.59 5997.47 6715.06 6006.07 6726.53 6007.5 C 6737.99 6008.93 6749.46 6000.33 6760.92 6001.77 C 6768.09 6003.2 6772.39 6013.23 6779.55 6013.23 C 6782.42 6014.67 6780.99 6006.07 6783.85 6004.63 C 6791.02 5997.47 6802.48 5994.6 6809.65 5986 C 6815.38 5981.7 6811.08 5971.67 6815.38 5967.37 C 6842.61 5945.87 6871.28 5973.1 6897.07 5961.64 C 6905.67 5957.34 6907.1 5944.44 6915.7 5941.58 C 6917.14 5940.14 6964.43 5947.31 6965.86 5945.87 C 6970.16 5944.44 6961.56 5938.71 6960.13 5934.41 C 6955.83 5911.48 6973.03 5905.75 6994.53 5905.75 C 7000.26 5905.75 7011.73 5950.17 7041.82 5974.54 " }
+ }
+ }
+ // PATH visit hu count: 167
+ DemoShape {
+ objectName: "hu"
+ ShapePath {
+ objectName: "svg_path:hu"
+ // boundingRect: 6971.6, 5504.46 834.102x584.228
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6994.53 5905.75 C 7000.26 5905.75 7011.73 5950.17 7041.82 5974.54 C 7080.52 6006.07 7113.48 6023.27 7143.58 6061.96 C 7147.88 6067.69 7176.54 6063.39 7179.41 6064.83 C 7190.87 6069.13 7199.47 6079.16 7209.5 6083.46 C 7229.57 6092.06 7292.63 6090.62 7311.26 6074.86 C 7314.12 6071.99 7315.56 6051.93 7325.59 6051.93 C 7352.82 6047.63 7348.52 6053.36 7372.88 6026.13 C 7377.18 6021.83 7382.92 6036.16 7395.81 6026.13 C 7408.71 6016.1 7417.31 5998.9 7433.08 5990.3 C 7466.04 5968.81 7438.81 5990.3 7463.17 5991.74 C 7464.61 5991.74 7501.87 5980.27 7503.3 5980.27 C 7510.47 5980.27 7517.63 5987.44 7524.8 5987.44 C 7527.66 5986 7529.1 5980.27 7531.96 5978.84 C 7540.56 5975.97 7583.56 5971.67 7586.42 5961.64 C 7589.29 5955.91 7567.79 5948.74 7593.59 5944.44 C 7603.62 5943.01 7613.65 5953.04 7622.25 5950.17 C 7649.48 5940.14 7640.89 5888.55 7648.05 5872.78 C 7650.92 5868.48 7658.08 5868.48 7660.95 5864.18 C 7660.95 5864.18 7676.71 5814.02 7676.71 5814.02 C 7679.58 5806.86 7683.88 5801.12 7683.88 5793.96 C 7683.88 5791.09 7676.71 5791.09 7676.71 5788.23 C 7678.15 5775.33 7693.91 5735.2 7701.08 5723.73 C 7703.94 5720.87 7711.11 5723.73 7711.11 5719.43 C 7718.28 5692.2 7708.24 5672.14 7739.77 5642.04 C 7749.81 5630.58 7761.27 5637.74 7777.04 5632.01 C 7792.8 5624.85 7794.23 5603.35 7805.7 5591.88 C 7788.5 5577.55 7811.43 5580.42 7792.8 5567.52 C 7789.93 5566.09 7785.63 5563.22 7781.34 5563.22 C 7775.6 5564.65 7771.3 5571.82 7765.57 5570.39 C 7756.97 5564.65 7755.54 5550.32 7746.94 5544.59 C 7742.64 5541.72 7736.91 5548.89 7732.61 5547.45 C 7709.68 5538.86 7731.17 5523.09 7698.21 5521.66 C 7698.21 5533.12 7676.71 5533.12 7665.25 5535.99 C 7659.52 5537.42 7653.78 5544.59 7649.48 5541.72 C 7622.25 5533.12 7643.75 5505.89 7607.92 5504.46 C 7593.59 5504.46 7584.99 5524.52 7570.66 5525.96 C 7546.3 5530.26 7514.77 5504.46 7493.27 5531.69 C 7484.67 5543.15 7490.4 5581.85 7474.64 5589.02 C 7468.91 5590.45 7460.31 5584.72 7454.57 5587.58 C 7431.64 5601.91 7464.61 5611.95 7428.78 5617.68 C 7417.31 5619.11 7391.51 5587.58 7380.05 5607.65 C 7375.75 5614.81 7382.92 5627.71 7377.18 5633.44 C 7361.42 5649.21 7316.99 5639.18 7296.93 5653.51 C 7278.29 5664.97 7298.36 5685.04 7304.09 5693.64 C 7309.82 5703.67 7188 5725.17 7170.81 5722.3 C 7143.58 5716.57 7123.51 5679.31 7097.72 5676.44 C 7091.98 5675.01 7087.68 5676.44 7084.82 5677.87 C 7064.75 5686.47 7084.82 5732.33 7083.38 5738.07 C 7074.78 5753.83 7023.19 5730.9 7003.13 5748.1 C 6994.53 5755.26 7037.52 5752.4 7038.96 5776.76 C 7041.82 5805.42 7017.46 5783.93 7007.43 5803.99 C 7000.26 5818.32 7026.06 5848.42 7023.19 5854.15 C 7010.29 5875.65 6998.83 5879.95 6973.03 5908.61 C 6973.03 5908.61 6973.03 5908.61 6971.6 5910.05 C 6974.46 5908.61 6975.9 5907.18 6978.76 5907.18 L 6994.53 5905.75 " }
+ }
+ }
+ // PATH visit ro count: 264
+ DemoShape {
+ objectName: "ro"
+ ShapePath {
+ objectName: "svg_path:ro"
+ // boundingRect: 7524.8, 5424.2 1255.45x932.997
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 8695.69 6202.41 C 8672.76 6206.71 8635.5 6219.61 8614 6200.98 C 8606.84 6195.25 8605.41 6175.18 8596.81 6179.48 C 8592.51 6180.91 8589.64 6186.65 8585.34 6188.08 C 8550.95 6206.71 8596.81 6178.05 8559.54 6179.48 C 8548.08 6179.48 8539.48 6192.38 8528.01 6193.81 C 8503.65 6196.68 8523.72 6162.28 8459.22 6186.65 C 8431.99 6198.11 8381.83 6221.04 8360.33 6242.54 C 8335.97 6266.9 8331.67 6297 8300.14 6317.06 C 8271.48 6334.26 8249.98 6322.8 8221.32 6321.36 C 8198.39 6319.93 8174.02 6318.5 8152.53 6325.66 C 8143.93 6328.53 8141.06 6340 8132.46 6342.86 C 8092.33 6357.19 8045.04 6334.26 8004.91 6338.56 C 7973.38 6341.43 7963.35 6367.23 7928.95 6352.89 C 7904.59 6342.86 7959.05 6311.33 7946.15 6305.6 C 7923.22 6295.57 7854.43 6281.24 7858.73 6243.97 C 7860.16 6231.07 7907.45 6229.64 7893.12 6222.48 C 7741.21 6146.52 7877.36 6215.31 7815.73 6251.14 C 7805.7 6258.3 7799.97 6232.51 7789.93 6228.21 C 7782.77 6223.91 7772.74 6228.21 7765.57 6228.21 C 7756.97 6228.21 7748.37 6232.51 7739.77 6229.64 C 7735.47 6226.77 7738.34 6219.61 7735.47 6213.88 C 7728.31 6199.54 7666.68 6222.48 7701.08 6198.11 C 7709.68 6192.38 7731.17 6186.65 7724.01 6180.91 C 7716.84 6176.61 7702.51 6180.91 7699.64 6172.31 C 7698.21 6163.72 7728.31 6147.95 7708.24 6136.49 C 7681.01 6120.72 7626.55 6127.89 7607.92 6094.92 C 7603.62 6087.76 7603.62 6053.36 7603.62 6044.76 L 7603.62 6041.9 C 7599.32 6043.33 7596.46 6051.93 7593.59 6049.06 C 7583.56 6043.33 7582.13 6027.56 7573.53 6020.4 C 7564.93 6014.67 7552.03 6016.1 7543.43 6008.93 C 7534.83 6004.63 7530.53 5994.6 7524.8 5987.44 C 7527.66 5986 7529.1 5980.27 7531.96 5978.84 C 7540.56 5975.97 7583.56 5971.67 7586.42 5961.64 C 7589.29 5955.91 7567.79 5948.74 7593.59 5944.44 C 7603.62 5943.01 7613.65 5953.04 7622.25 5950.17 C 7649.48 5940.14 7640.89 5888.55 7648.05 5872.78 C 7650.92 5868.48 7658.08 5868.48 7660.95 5864.18 L 7676.71 5814.02 C 7679.58 5806.86 7683.88 5801.12 7683.88 5793.96 C 7683.88 5791.09 7676.71 5791.09 7676.71 5788.23 C 7678.15 5775.33 7693.91 5735.2 7701.08 5723.73 C 7703.94 5720.87 7711.11 5723.73 7711.11 5719.43 C 7718.28 5692.2 7708.24 5672.14 7739.77 5642.04 C 7749.81 5630.58 7761.27 5637.74 7777.04 5632.01 C 7792.8 5624.85 7794.23 5603.35 7805.7 5591.88 C 7810 5594.75 7818.6 5589.02 7828.63 5561.79 C 7830.06 5558.92 7830.06 5554.62 7832.93 5554.62 C 7837.23 5554.62 7873.06 5571.82 7877.36 5571.82 C 7880.22 5571.82 7881.66 5564.65 7884.52 5563.22 C 7903.15 5556.05 7950.45 5574.68 7973.38 5568.95 C 7986.28 5564.65 7994.88 5553.19 8007.78 5551.75 C 8030.71 5550.32 8046.47 5580.42 8062.24 5584.72 C 8083.73 5591.88 8090.9 5551.75 8096.63 5546.02 C 8122.43 5518.79 8188.35 5521.66 8208.42 5501.59 C 8247.11 5464.33 8191.22 5495.86 8218.45 5460.03 C 8225.62 5451.43 8237.08 5444.27 8248.55 5441.4 L 8249.98 5437.1 L 8255.71 5424.2 C 8254.28 5427.07 8254.28 5432.8 8254.28 5435.67 C 8274.34 5425.64 8287.24 5434.23 8303.01 5447.13 C 8333.1 5468.63 8338.84 5504.46 8363.2 5528.82 C 8403.33 5567.52 8512.25 5636.31 8525.15 5692.2 C 8535.18 5728.03 8522.28 5768.16 8530.88 5803.99 C 8533.75 5819.76 8559.54 5855.59 8558.11 5871.35 C 8558.11 5877.08 8546.65 5878.52 8548.08 5884.25 C 8549.51 5889.98 8558.11 5887.11 8562.41 5889.98 C 8566.71 5892.85 8566.71 5902.88 8572.44 5902.88 C 8575.31 5904.31 8579.61 5901.45 8582.47 5904.31 C 8586.77 5908.61 8585.34 5917.21 8591.07 5921.51 C 8596.81 5927.24 8658.43 5930.11 8657 5922.94 C 8657 5921.51 8636.94 5917.21 8651.27 5908.61 C 8659.87 5904.31 8669.9 5905.75 8678.5 5901.45 C 8692.83 5894.28 8698.56 5874.22 8714.33 5868.48 C 8745.86 5857.02 8761.62 5868.48 8768.79 5895.71 C 8767.35 5897.15 8763.05 5895.71 8763.05 5897.15 C 8763.05 5901.45 8765.92 5904.31 8768.79 5905.75 C 8771.65 5907.18 8774.52 5901.45 8775.95 5904.31 C 8778.82 5910.05 8780.25 5958.77 8780.25 5964.51 C 8778.82 5983.14 8735.82 5984.57 8724.36 5993.17 C 8717.19 5998.9 8717.19 6018.97 8708.59 6017.53 C 8707.16 6016.1 8718.63 5968.81 8698.56 5957.34 C 8691.4 5954.47 8682.8 5965.94 8678.5 5973.1 C 8672.76 5983.14 8698.56 5991.74 8697.13 6003.2 C 8697.13 6014.67 8675.63 6021.83 8681.36 6030.43 C 8687.1 6039.03 8698.28 6018.2 8708.31 6018.2 C 8711.18 6016.77 8707.16 6024.7 8704.29 6027.56 C 8700.9 6029.41 8703.48 6030 8702.86 6031.86 C 8711.46 6039.03 8677.06 6094.92 8677.06 6096.36 C 8678.5 6106.39 8691.4 6156.55 8698.56 6202.41 L 8695.69 6202.41 " }
+ }
+ }
+ // PATH visit bg count: 211
+ DemoShape {
+ objectName: "bg"
+ ShapePath {
+ objectName: "svg_path:bg"
+ // boundingRect: 7863.73, 6177.87 840.036x636.981
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 8477.85 6648.13 C 8470.69 6646.69 8463.52 6646.69 8459.22 6646.69 C 8403.33 6658.16 8493.62 6698.29 8462.09 6722.65 C 8462.09 6722.65 8417.66 6736.98 8416.23 6736.98 C 8396.16 6744.15 8363.2 6769.94 8341.7 6768.51 C 8337.4 6768.51 8340.27 6761.35 8337.4 6759.91 C 8323.07 6752.75 8304.44 6744.15 8288.68 6748.45 C 8282.94 6749.88 8282.94 6761.35 8277.21 6762.78 C 8272.91 6764.21 8270.04 6754.18 8265.75 6752.75 C 8262.88 6751.31 8258.58 6757.05 8257.15 6754.18 C 8247.11 6748.45 8244.25 6732.68 8232.78 6731.25 C 8224.18 6728.38 8219.88 6742.71 8211.29 6744.15 C 8205.55 6747.01 8198.39 6735.55 8194.09 6738.42 C 8168.29 6754.18 8158.26 6771.38 8118.13 6785.71 C 8100.93 6791.44 8079.43 6785.71 8063.67 6795.74 C 8057.94 6798.61 8057.94 6810.07 8052.2 6812.94 C 8047.9 6817.24 8040.74 6812.94 8035.01 6812.94 C 8000.45 6809.98 8017.43 6764.43 8014.94 6734.12 C 8004.91 6724.08 7989.14 6695.42 7976.25 6686.82 C 7969.08 6682.52 7959.05 6686.82 7950.45 6682.52 C 7947.58 6681.09 7908.89 6650.99 7908.89 6648.13 C 7916.05 6635.23 7936.12 6632.36 7928.95 6613.73 C 7927.52 6608 7923.22 6605.13 7918.92 6602.26 C 7916.05 6600.83 7911.75 6603.7 7910.32 6600.83 C 7908.89 6593.67 7910.32 6586.5 7910.32 6579.33 C 7908.89 6570.73 7898.85 6562.14 7903.15 6554.97 C 7944.72 6486.18 7897.42 6570.73 7943.28 6534.91 C 7947.58 6530.61 7977.68 6477.58 7969.08 6468.98 C 7957.61 6460.38 7903.15 6434.58 7900.29 6431.72 C 7883.09 6417.39 7855.86 6357.19 7865.89 6335.7 C 7870.19 6325.66 7885.96 6327.1 7891.69 6318.5 C 7895.99 6308.47 7890.26 6295.57 7894.56 6285.53 C 7913.19 6294.13 7936.12 6299.87 7946.15 6305.6 C 7959.05 6311.33 7904.59 6342.86 7928.95 6352.89 C 7963.35 6367.23 7973.38 6341.43 8004.91 6338.56 C 8045.04 6334.26 8092.33 6357.19 8132.46 6342.86 C 8141.06 6340 8143.93 6328.53 8152.53 6325.66 C 8174.02 6318.5 8198.39 6319.93 8221.32 6321.36 C 8249.98 6322.8 8271.48 6334.26 8300.14 6317.06 C 8331.67 6297 8335.97 6266.9 8360.33 6242.54 C 8381.83 6221.04 8431.99 6198.11 8459.22 6186.65 C 8523.72 6162.28 8503.65 6196.68 8528.01 6193.81 C 8539.48 6192.38 8548.08 6179.48 8559.54 6179.48 C 8596.81 6178.05 8550.95 6206.71 8585.34 6188.08 C 8589.64 6186.65 8592.51 6180.91 8596.81 6179.48 C 8605.41 6175.18 8606.84 6195.25 8614 6200.98 C 8635.5 6219.61 8672.76 6206.71 8695.69 6202.41 L 8698.56 6202.41 C 8704.29 6238.24 8707.16 6271.2 8697.13 6276.94 C 8691.4 6279.8 8684.23 6269.77 8677.06 6271.2 C 8624.04 6285.53 8658.43 6284.1 8642.67 6315.63 C 8641.23 6321.36 8631.2 6321.36 8628.34 6325.66 C 8628.34 6328.53 8636.94 6327.1 8636.94 6329.96 C 8639.8 6352.89 8638.37 6367.23 8644.1 6390.16 C 8645.53 6398.75 8654.13 6410.22 8649.83 6417.39 C 8645.53 6424.55 8632.64 6420.25 8625.47 6424.55 C 8624.04 6424.55 8629.77 6427.42 8629.77 6428.85 C 8626.9 6434.58 8619.74 6436.02 8618.3 6441.75 C 8616.87 6446.05 8625.47 6450.35 8622.6 6453.22 C 8616.87 6461.81 8603.97 6464.68 8598.24 6473.28 C 8572.44 6507.68 8635.5 6456.08 8651.27 6494.78 C 8652.7 6497.64 8648.4 6501.94 8648.4 6504.81 C 8649.83 6506.24 8681.36 6524.87 8688.53 6530.61 C 8692.83 6536.34 8697.13 6540.64 8698.56 6544.94 L 8695.7 6544.94 C 8687.1 6546.37 8678.5 6544.94 8669.9 6547.8 C 8658.43 6552.1 8649.83 6562.14 8639.8 6569.3 C 8636.94 6570.73 8639.8 6576.47 8638.37 6575.03 C 8605.41 6567.87 8595.37 6542.07 8562.41 6559.27 L 8538.05 6586.5 C 8528.01 6590.8 8515.12 6583.63 8507.95 6590.8 C 8499.35 6597.96 8507.95 6612.3 8502.22 6620.9 C 8493.62 6630.93 8464.96 6626.63 8477.85 6648.13 " }
+ }
+ }
+ // PATH visit cy count: 64
+ DemoShape {
+ objectName: "cy"
+ ShapePath {
+ objectName: "svg_path:cy"
+ // boundingRect: 9613.09, 7409.12 313.697x281.029
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 9926.78 7409.14 C 9925.35 7432.07 9860.86 7487.96 9849.39 7513.76 C 9836.49 7545.29 9900.99 7549.59 9893.82 7561.05 C 9889.52 7566.78 9878.06 7559.62 9872.32 7562.49 C 9866.59 7563.92 9865.16 7572.52 9859.43 7575.38 C 9850.83 7581.12 9836.49 7575.38 9829.33 7582.55 C 9823.6 7589.72 9830.76 7602.61 9826.46 7609.78 C 9815 7629.84 9764.84 7644.18 9753.37 7659.94 C 9749.07 7665.67 9757.67 7675.71 9753.37 7682.87 C 9741.91 7704.37 9730.44 7671.41 9726.14 7671.41 C 9717.54 7671.41 9711.81 7682.87 9703.21 7685.74 C 9698.91 7687.17 9651.62 7680.01 9650.18 7680.01 C 9645.88 7677.14 9647.32 7669.97 9644.45 7665.67 C 9640.15 7661.37 9602.89 7621.25 9615.79 7622.68 C 9622.95 7622.68 9627.25 7629.84 9634.42 7629.84 C 9647.32 7628.41 9650.18 7602.61 9653.05 7598.31 C 9663.08 7582.55 9690.31 7594.02 9700.34 7585.42 C 9707.51 7581.12 9706.08 7569.65 9704.64 7561.05 C 9703.21 7553.89 9690.31 7545.29 9696.04 7538.12 C 9700.34 7532.39 9711.81 7540.99 9720.41 7539.55 C 9737.61 7538.12 9779.17 7528.09 9794.93 7519.49 C 9815 7508.03 9925.35 7407.7 9926.78 7409.14 " }
+ }
+ }
+ // PATH visit mt count: 16
+ DemoShape {
+ objectName: "mt"
+ ShapePath {
+ objectName: "svg_path:mt"
+ // boundingRect: 6811.81, 7968.28 95.416x55.3057
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6829.95 7968.49 C 6819.72 7970.54 6811.81 7987.11 6811.81 7987.11 C 6830 7987.11 6837.23 7979.49 6829.95 8008.6 C 6822.67 8037.71 6917.26 8019.74 6906.35 7994.27 C 6895.43 7968.8 6855.01 7986.86 6840.46 7972.31 C 6836.82 7968.67 6833.36 7967.81 6829.95 7968.49 " }
+ }
+ }
+ // PATH visit hr count: 339
+ DemoShape {
+ objectName: "hr"
+ ShapePath {
+ objectName: "svg_path:hr"
+ // boundingRect: 6647.7, 5968.58 787.166x732.576
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7367.15 6701.15 L 7364.28 6691.12 C 7347.09 6662.46 7319.86 6672.49 7288.33 6648.13 C 7279.73 6640.96 7276.86 6628.06 7266.83 6625.2 C 7261.1 6622.33 7253.93 6628.06 7246.76 6629.49 C 7249.63 6630.93 7251.06 6630.93 7253.93 6632.36 C 7256.8 6633.79 7259.66 6642.39 7256.8 6642.39 C 7233.87 6640.96 7216.67 6619.46 7193.74 6616.6 C 7183.71 6615.16 7172.24 6618.03 7162.21 6616.6 C 7159.34 6616.6 7156.48 6609.43 7155.04 6610.86 C 7152.18 6612.3 7152.18 6619.46 7155.04 6620.9 C 7163.64 6623.76 7173.67 6619.46 7183.71 6620.9 C 7192.3 6622.33 7202.34 6625.2 7212.37 6630.93 C 7215.23 6632.36 7213.8 6638.09 7216.67 6639.53 C 7265.4 6665.32 7255.36 6623.76 7331.32 6683.95 C 7341.35 6691.12 7354.25 6698.29 7367.15 6701.15 M 7391.51 6241.11 C 7378.62 6226.78 7400.11 6233.94 7405.85 6223.91 C 7407.28 6222.48 7407.28 6219.61 7405.85 6218.18 C 7387.21 6199.55 7400.11 6192.38 7401.55 6173.75 C 7402.98 6166.58 7438.81 6195.25 7434.51 6166.58 C 7414.44 6166.58 7391.51 6160.85 7374.32 6149.38 C 7367.15 6143.65 7380.05 6140.79 7380.05 6139.35 C 7380.05 6135.05 7365.72 6132.19 7374.32 6123.59 C 7375.75 6122.15 7385.78 6130.75 7387.21 6122.15 C 7388.65 6106.39 7358.55 6125.02 7358.55 6110.69 C 7361.42 6077.73 7367.15 6086.32 7342.79 6051.93 L 7341.35 6049.06 C 7337.05 6050.5 7332.75 6050.5 7325.59 6051.93 C 7315.56 6051.93 7314.12 6071.99 7311.26 6074.86 C 7292.63 6090.62 7229.57 6092.06 7209.5 6083.46 C 7199.47 6079.16 7190.87 6069.13 7179.41 6064.83 C 7176.54 6063.39 7147.88 6067.69 7143.58 6061.96 C 7113.48 6023.27 7080.52 6006.07 7041.82 5974.54 C 7031.79 5973.1 7007.43 5967.37 7001.69 5968.81 C 6985.93 5971.67 7007.43 5991.74 6998.83 5997.47 C 6991.66 6000.33 6983.06 5996.04 6975.9 5998.9 C 6973.03 6000.33 6977.33 6006.07 6974.46 6008.93 C 6960.13 6020.4 6937.2 6021.83 6922.87 6034.73 C 6905.67 6050.5 6954.4 6090.62 6932.9 6106.39 C 6882.74 6142.22 6914.27 6082.03 6881.31 6129.32 C 6877.01 6135.05 6894.21 6136.49 6897.07 6143.65 C 6899.94 6149.38 6882.74 6147.95 6882.74 6153.68 C 6882.74 6156.55 6897.07 6175.18 6891.34 6182.35 C 6875.57 6198.11 6852.64 6173.75 6838.31 6173.75 C 6816.81 6173.75 6858.38 6193.81 6822.55 6183.78 C 6812.52 6180.91 6799.62 6165.15 6795.32 6156.55 C 6792.45 6153.68 6789.58 6145.08 6788.15 6147.95 C 6782.42 6156.55 6783.85 6166.58 6778.12 6173.75 C 6759.49 6198.11 6733.69 6173.75 6712.19 6180.91 C 6709.33 6182.35 6715.06 6188.08 6712.19 6190.95 C 6699.3 6198.11 6673.5 6196.68 6660.6 6190.95 C 6656.3 6190.95 6647.7 6186.65 6647.7 6190.95 C 6649.13 6202.41 6659.17 6211.01 6663.47 6221.04 C 6663.47 6222.48 6660.6 6222.48 6660.6 6223.91 C 6662.03 6235.37 6659.17 6246.84 6664.9 6255.44 C 6664.9 6255.44 6666.33 6255.44 6666.33 6256.87 C 6660.6 6265.47 6683.53 6275.5 6690.7 6285.53 C 6696.43 6294.13 6700.73 6331.4 6707.89 6322.8 C 6715.06 6314.2 6719.36 6302.73 6725.09 6292.7 C 6726.53 6288.4 6727.96 6284.1 6727.96 6279.8 C 6727.96 6278.37 6723.66 6279.8 6723.66 6276.94 C 6723.66 6275.5 6723.66 6269.77 6725.09 6271.2 C 6730.82 6272.64 6726.53 6305.6 6742.29 6279.8 C 6745.16 6272.64 6745.16 6265.47 6745.16 6256.87 C 6745.16 6255.44 6742.29 6254.01 6742.29 6251.14 C 6742.29 6249.71 6743.72 6255.44 6745.16 6254.01 C 6756.62 6248.27 6746.59 6209.58 6759.49 6209.58 C 6801.05 6206.71 6786.72 6222.48 6812.52 6243.97 C 6818.25 6248.27 6826.85 6243.97 6831.15 6248.27 C 6856.94 6278.37 6826.85 6315.63 6855.51 6347.16 C 6864.11 6357.19 6888.47 6378.69 6898.51 6385.86 C 6909.97 6391.59 6945.8 6397.32 6934.33 6401.62 C 6914.27 6407.35 6877.01 6365.79 6882.74 6420.25 C 6884.17 6437.45 6950.1 6474.71 6960.13 6487.61 C 6963 6489.04 6948.67 6483.31 6951.53 6484.74 C 6960.13 6490.48 6997.39 6501.94 6998.83 6506.24 C 7001.69 6516.27 6993.09 6532.04 7000.26 6540.64 C 7004.56 6543.5 7048.99 6527.74 7050.42 6526.31 C 7079.08 6520.57 7059.02 6532.04 7094.85 6544.94 C 7106.31 6547.8 7119.21 6543.5 7130.68 6547.8 C 7146.44 6553.54 7156.48 6579.33 7173.67 6587.93 C 7193.74 6596.53 7206.64 6602.26 7212.37 6608 C 7212.37 6609.43 7213.8 6608 7213.8 6608 C 7216.67 6608 7219.53 6605.13 7220.97 6608 C 7226.7 6618.03 7218.1 6618.03 7231 6623.76 C 7235.3 6619.46 7245.33 6619.46 7246.76 6613.73 C 7253.93 6595.1 7192.3 6570.73 7186.57 6560.7 C 7180.84 6552.1 7185.14 6539.21 7179.41 6530.61 C 7175.11 6524.87 7165.07 6526.31 7157.91 6523.44 C 7127.81 6504.81 7109.18 6470.41 7083.38 6448.92 C 7074.78 6443.18 7041.82 6410.22 7031.79 6405.92 C 7027.49 6404.49 7021.76 6410.22 7017.46 6407.35 C 7010.29 6400.19 7027.49 6384.42 7027.49 6381.56 C 7026.06 6375.82 7018.89 6374.39 7014.59 6370.09 C 7003.13 6352.89 6998.83 6329.96 6983.06 6315.63 C 6977.33 6308.47 6965.86 6308.47 6958.7 6302.73 C 6952.97 6298.43 6942.93 6215.31 6974.46 6216.74 C 6988.79 6216.74 7013.16 6248.27 7026.06 6255.44 L 7037.52 6254.01 L 7026.06 6255.44 C 7043.25 6254.01 7040.39 6226.77 7048.99 6215.31 C 7064.75 6196.68 7071.92 6219.61 7091.98 6215.31 C 7097.72 6213.88 7097.72 6202.41 7103.45 6198.11 C 7103.45 6198.11 7112.05 6206.71 7136.41 6213.88 C 7139.28 6215.31 7143.58 6216.74 7147.88 6215.31 C 7150.74 6215.31 7152.18 6209.58 7156.48 6208.14 C 7173.67 6206.71 7212.37 6228.21 7231 6225.34 C 7235.3 6225.34 7239.6 6222.48 7241.03 6218.18 C 7245.33 6215.31 7243.9 6205.28 7249.63 6205.28 C 7258.23 6203.84 7263.96 6212.44 7272.56 6215.31 C 7288.33 6219.61 7269.7 6208.14 7278.29 6205.28 C 7286.89 6202.41 7296.93 6202.41 7305.52 6205.28 C 7311.26 6206.71 7315.56 6215.31 7321.29 6215.31 C 7325.59 6215.31 7321.29 6205.28 7325.59 6205.28 C 7358.55 6206.71 7349.95 6242.54 7365.72 6245.41 C 7375.75 6246.84 7384.35 6243.97 7391.51 6241.11 " }
+ }
+ }
+ // PATH visit ba count: 150
+ DemoShape {
+ objectName: "ba"
+ ShapePath {
+ objectName: "svg_path:ba"
+ // boundingRect: 6952.52, 6198.11 537.243x503.041
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7430.21 6546.37 C 7415.88 6536.34 7415.88 6526.31 7400.11 6527.74 L 7402.98 6523.44 L 7400.11 6527.74 C 7380.05 6550.67 7382.92 6583.63 7377.18 6590.8 C 7365.72 6605.13 7361.42 6583.63 7357.12 6605.13 C 7349.95 6646.69 7394.38 6648.13 7361.42 6679.66 L 7368.58 6678.22 L 7370.02 6701.15 C 7352.82 6659.59 7324.16 6673.92 7288.33 6648.13 C 7279.73 6640.96 7276.86 6628.06 7266.83 6625.19 C 7259.66 6622.33 7251.06 6630.93 7242.47 6630.93 C 7238.17 6630.93 7228.13 6629.49 7229.57 6625.19 C 7232.43 6619.46 7245.33 6620.9 7246.76 6613.73 C 7253.93 6595.1 7192.3 6570.73 7186.57 6560.7 C 7180.84 6552.1 7185.14 6539.21 7179.41 6530.61 C 7175.11 6524.87 7165.07 6526.31 7157.91 6523.44 C 7127.81 6504.81 7109.18 6470.41 7083.38 6448.92 C 7074.78 6443.18 7041.82 6410.22 7031.79 6405.92 C 7027.49 6404.49 7021.76 6410.22 7017.46 6407.35 C 7010.29 6400.19 7027.49 6384.42 7027.49 6381.56 C 7026.06 6375.82 7018.89 6374.39 7014.59 6370.09 C 7003.13 6352.89 6998.83 6329.96 6983.06 6315.63 C 6977.33 6308.47 6965.86 6308.47 6958.7 6302.73 C 6952.97 6298.43 6942.93 6215.31 6974.46 6216.74 C 6988.79 6216.74 7013.16 6248.27 7026.06 6255.44 L 7037.52 6254.01 L 7026.06 6255.44 C 7043.25 6254.01 7040.39 6226.77 7048.99 6215.31 C 7064.75 6196.68 7071.92 6219.61 7091.98 6215.31 C 7097.72 6213.88 7097.72 6202.41 7103.45 6198.11 C 7103.45 6198.11 7112.05 6206.71 7136.41 6213.88 C 7139.28 6215.31 7143.58 6216.74 7147.88 6215.31 C 7150.74 6215.31 7152.18 6209.58 7156.48 6208.14 C 7173.67 6206.71 7212.37 6228.21 7231 6225.34 C 7235.3 6225.34 7239.6 6222.48 7241.03 6218.18 C 7245.33 6215.31 7243.9 6205.28 7249.63 6205.28 C 7258.23 6203.84 7263.96 6212.44 7272.56 6215.31 C 7288.33 6219.61 7269.7 6208.14 7278.29 6205.28 C 7286.89 6202.41 7296.93 6202.41 7305.52 6205.28 C 7311.26 6206.71 7315.56 6215.31 7321.29 6215.31 C 7325.59 6215.31 7321.29 6205.28 7325.59 6205.28 C 7358.55 6206.71 7349.95 6242.54 7365.72 6245.41 C 7392.95 6248.27 7407.28 6228.21 7433.08 6229.64 C 7433.08 6274.07 7407.28 6304.17 7411.58 6331.4 C 7417.31 6362.93 7425.91 6340 7445.97 6348.59 C 7458.87 6354.33 7461.74 6372.96 7474.64 6380.12 C 7478.94 6381.56 7493.27 6377.26 7488.97 6390.16 C 7480.37 6418.82 7444.54 6387.29 7437.38 6397.32 C 7437.38 6397.32 7501.87 6457.51 7484.67 6476.15 C 7478.94 6480.45 7471.77 6466.11 7464.61 6468.98 C 7456.01 6471.85 7454.57 6483.31 7448.84 6490.48 C 7438.81 6493.34 7411.58 6487.61 7410.15 6494.78 C 7405.85 6514.84 7441.67 6516.27 7430.21 6546.37 L 7424.48 6546.37 L 7430.21 6546.37 " }
+ }
+ }
+ // PATH visit me count: 95
+ DemoShape {
+ objectName: "me"
+ ShapePath {
+ objectName: "svg_path:me"
+ // boundingRect: 7357.01, 6465.39 262.356x329.8
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7364.97 6687.77 L 7367.84 6697.8 C 7375.01 6699.23 7380.74 6699.23 7387.9 6699.23 C 7385.04 6702.09 7382.17 6704.96 7383.61 6709.26 C 7403.67 6753.66 7407.97 6712.12 7428.03 6722.15 C 7432.33 6725.01 7429.47 6730.74 7432.33 6735.04 C 7443.8 6749.36 7458.13 6746.49 7468.16 6762.25 C 7471.03 6766.55 7468.16 6775.14 7472.46 6779.44 C 7481.06 6788.03 7492.53 6792.33 7503.99 6795.19 C 7503.99 6785.17 7503.99 6775.14 7503.99 6766.55 C 7503.99 6760.82 7506.86 6753.66 7503.99 6747.93 C 7499.69 6740.77 7486.79 6740.77 7485.36 6733.6 C 7485.36 6732.17 7531.22 6639.08 7535.52 6636.21 C 7536.95 6634.78 7544.12 6670.59 7555.59 6672.02 C 7555.59 6672.02 7588.55 6646.95 7588.55 6646.95 C 7585.54 6631.87 7588.54 6632.67 7583.46 6618.14 C 7580.05 6608.4 7591.54 6613.99 7599.36 6607.55 C 7602.79 6604.72 7601.08 6600.33 7596.56 6595.16 C 7591.96 6589.89 7626.09 6589.56 7618.17 6583.5 C 7602.45 6571.48 7583.24 6550.99 7567.94 6543.88 C 7558.06 6539.29 7570.45 6519.73 7560.82 6510.26 C 7542.78 6492.52 7499.02 6481.72 7485.36 6472.93 C 7479.63 6477.23 7472.46 6462.91 7465.3 6465.77 C 7456.7 6468.64 7455.26 6480.1 7449.53 6487.26 C 7439.5 6490.12 7412.27 6484.39 7410.84 6491.55 C 7406.54 6511.61 7442.36 6513.04 7430.9 6543.11 L 7425.17 6543.11 L 7430.9 6543.11 C 7416.57 6533.09 7416.57 6523.06 7400.8 6524.5 L 7403.67 6520.2 L 7400.8 6524.5 C 7380.74 6547.41 7383.61 6580.35 7377.87 6587.51 C 7366.41 6601.84 7362.11 6580.35 7357.81 6601.84 C 7350.64 6643.37 7395.07 6644.8 7362.11 6676.31 L 7369.27 6674.88 L 7370.71 6697.8 C 7369.27 6693.5 7367.84 6690.64 7364.97 6687.77 " }
+ }
+ }
+ // PATH visit rs count: 210
+ DemoShape {
+ objectName: "rs"
+ ShapePath {
+ objectName: "svg_path:rs"
+ // boundingRect: 7329.87, 5973.8 653.296x724.318
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7686.47 6519.21 C 7710.27 6519.21 7743.85 6594.24 7757.24 6601.67 C 7761.71 6604.65 7793.42 6622.31 7799.37 6623.8 C 7809.79 6626.77 7800.22 6642.13 7807.66 6649.57 C 7827 6670.39 7807.61 6669.72 7803.14 6690.54 C 7803.14 6690.54 7802.46 6680.35 7802.46 6680.35 L 7798.59 6688.08 C 7835.79 6713.35 7799.84 6683.67 7808.77 6677.72 C 7825.14 6667.31 7792.71 6681.11 7812.05 6679.62 C 7819.49 6679.62 7806.4 6677.77 7809.37 6670.34 C 7810.86 6664.39 7800.51 6689.04 7802 6684.58 C 7815.39 6651.86 7783.7 6698.19 7811.97 6693.73 C 7835.77 6689.27 7899.74 6648.87 7919.08 6666.72 C 7926.52 6653.34 7947.35 6650.36 7939.91 6631.03 C 7938.42 6625.08 7933.96 6622.11 7929.49 6619.14 C 7926.52 6617.65 7922.05 6620.62 7920.57 6617.65 C 7919.08 6610.21 7920.57 6602.78 7920.57 6595.34 C 7919.08 6586.42 7908.66 6577.5 7913.13 6570.07 C 7956.28 6498.69 7907.18 6586.42 7954.79 6549.25 C 7959.25 6544.79 7990.5 6489.77 7981.57 6480.85 C 7969.67 6471.93 7913.13 6445.16 7910.15 6442.19 C 7892.3 6427.32 7864.03 6364.87 7874.44 6342.56 C 7878.91 6332.16 7895.27 6333.64 7901.22 6324.72 C 7905.69 6314.31 7899.74 6300.93 7904.2 6290.52 C 7883.37 6281.6 7864.03 6268.22 7867 6247.4 C 7868.49 6234.02 7917.59 6232.53 7902.71 6225.1 C 7745 6146.29 7886.35 6217.66 7822.37 6254.83 C 7811.95 6262.27 7806 6235.5 7795.58 6231.04 C 7788.14 6226.58 7777.73 6231.04 7770.29 6231.04 C 7761.36 6231.04 7752.43 6235.5 7743.51 6232.53 C 7739.04 6229.56 7742.02 6222.12 7739.04 6216.17 C 7731.6 6201.3 7667.62 6225.1 7703.33 6199.82 C 7712.26 6193.87 7734.58 6187.92 7727.14 6181.97 C 7719.7 6177.51 7704.82 6181.97 7701.85 6173.05 C 7700.36 6164.13 7731.6 6147.77 7710.77 6135.88 C 7682.5 6119.52 7625.96 6126.96 7606.62 6092.76 C 7602.16 6085.32 7602.16 6049.64 7602.16 6040.71 C 7602.16 6039.23 7602.16 6037.74 7602.16 6037.74 C 7597.69 6039.23 7594.72 6048.15 7591.74 6045.17 C 7581.33 6039.23 7579.84 6022.87 7570.91 6015.44 C 7561.98 6009.49 7548.59 6010.98 7539.67 6003.54 C 7530.74 5999.08 7526.27 5988.67 7520.32 5981.24 C 7512.88 5981.24 7505.44 5973.8 7498 5973.8 C 7496.52 5973.8 7457.83 5985.7 7456.34 5985.7 C 7431.05 5984.21 7459.32 5961.91 7425.1 5984.21 C 7408.73 5993.13 7399.8 6010.98 7386.41 6021.38 C 7373.02 6031.79 7367.07 6016.92 7362.6 6021.38 C 7343.26 6043.69 7341.77 6045.17 7329.87 6045.17 L 7331.36 6048.15 C 7356.65 6083.84 7350.7 6074.91 7347.73 6109.11 C 7347.73 6123.98 7378.97 6104.65 7377.48 6121.01 C 7376 6129.93 7365.58 6121.01 7364.09 6122.5 C 7355.17 6131.42 7370.04 6134.39 7370.04 6138.85 C 7370.04 6140.34 7356.65 6143.31 7364.09 6149.26 C 7381.95 6161.16 7405.75 6167.1 7426.58 6167.1 C 7431.05 6196.84 7393.85 6167.1 7392.36 6174.54 C 7390.88 6193.87 7377.48 6201.3 7396.83 6220.63 C 7398.31 6222.12 7398.31 6225.1 7396.83 6226.58 C 7390.88 6236.99 7368.56 6229.56 7381.95 6244.43 C 7396.83 6239.96 7408.73 6231.04 7425.1 6232.53 C 7425.1 6278.63 7398.31 6309.85 7402.78 6338.1 C 7408.73 6370.82 7417.66 6347.02 7438.49 6355.95 C 7451.88 6361.89 7454.85 6381.22 7468.25 6388.66 C 7472.71 6390.15 7487.59 6385.69 7483.13 6399.07 C 7474.2 6428.81 7437 6396.09 7429.56 6406.5 C 7429.56 6406.5 7496.52 6468.95 7478.66 6488.28 C 7513.64 6506.5 7496.1 6499.47 7550.63 6522.44 C 7569.16 6528.4 7551.87 6560.03 7568.26 6566.54 C 7594.22 6576.84 7585.73 6586.82 7603.45 6596.18 C 7619.99 6604.9 7586.21 6581.29 7596.56 6588.65 C 7613.68 6600.84 7622.04 6574.55 7619 6582.51 C 7640.91 6586.18 7688.39 6511.04 7686.47 6519.21 " }
+ }
+ }
+ // PATH visit rs-km count: 77
+ DemoShape {
+ objectName: "rs-km"
+ ShapePath {
+ objectName: "svg_path:rs-km"
+ // boundingRect: 7585.83, 6524.48 229.594x247.068
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7673.25 6767.22 C 7673.25 6767.22 7679.97 6731.22 7667.49 6711.54 C 7655 6691.86 7618.04 6686.58 7618.04 6686.58 L 7601.24 6653.93 L 7589.72 6642.41 C 7589.72 6642.41 7582.04 6612.65 7588.28 6613.61 C 7591.4 6614.09 7596.27 6613.15 7599.01 6609.48 C 7601.74 6605.81 7603.51 6599.41 7612.11 6593.65 C 7619.29 6588.84 7636.24 6580.56 7639.48 6577.69 C 7647.63 6570.47 7659.58 6559.6 7659.58 6559.6 C 7659.58 6559.6 7684.51 6528.38 7687.01 6525.43 C 7693.08 6518.26 7715.44 6553.98 7718.92 6555.67 C 7726.84 6559.51 7749.14 6604.1 7752.98 6605.06 C 7756.82 6606.02 7796.14 6627.05 7799.02 6628.01 C 7801.9 6628.97 7805.74 6637.61 7802.86 6640.49 C 7799.98 6643.37 7816.78 6658.25 7815.34 6661.61 C 7813.9 6664.97 7814.38 6676.01 7811.02 6678.41 C 7807.66 6680.81 7815.76 6695.1 7811.92 6696.06 C 7808.08 6697.02 7797.1 6688.5 7797.1 6688.5 L 7793.92 6681.24 C 7793.92 6681.24 7788.46 6675.05 7780.3 6680.33 C 7772.14 6685.62 7763.02 6700.5 7765.42 6703.86 C 7767.82 6707.22 7773.58 6710.1 7771.66 6712.5 C 7769.74 6714.9 7763.5 6703.38 7751.01 6702.42 C 7738.53 6701.46 7708.77 6715.38 7706.37 6718.74 C 7703.97 6722.1 7700.13 6724.98 7698.69 6726.9 C 7697.25 6728.82 7695.33 6748.5 7695.33 6752.34 C 7695.33 6756.18 7691.01 6771.54 7691.01 6771.54 L 7673.25 6767.22 " }
+ }
+ }
+ // PATH visit mk count: 74
+ DemoShape {
+ objectName: "mk"
+ ShapePath {
+ objectName: "svg_path:mk"
+ // boundingRect: 7665.12, 6644.19 351.987x307.82
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7669.54 6768.51 C 7668.11 6778.54 7666.68 6788.58 7668.11 6797.18 C 7668.11 6807.21 7676.71 6815.81 7676.71 6824.41 C 7675.28 6828.71 7663.81 6828.71 7665.24 6833 C 7666.68 6843.04 7681.01 6848.77 7682.44 6858.8 C 7683.88 6863.1 7675.28 6867.4 7675.28 6871.7 C 7675.28 6883.17 7712.54 6936.19 7718.27 6944.79 C 7726.87 6954.82 7732.6 6939.06 7736.9 6939.06 C 7742.64 6937.63 7749.8 6939.06 7754.1 6941.93 C 7756.97 6944.79 7752.67 6951.96 7755.53 6951.96 C 7765.57 6953.39 7861.59 6923.29 7867.32 6918.99 C 7883.09 6901.8 7871.62 6878.87 7900.28 6868.83 C 7910.32 6864.53 7966.21 6871.7 7974.81 6865.97 C 7979.11 6863.1 7977.68 6854.5 7981.97 6851.64 C 7984.84 6850.2 7989.14 6858.8 7992.01 6855.94 C 8009.71 6803.88 8022.37 6809.24 8014.94 6734.12 C 8004.91 6724.08 7989.14 6695.42 7976.24 6686.82 C 7969.08 6682.52 7959.04 6686.82 7950.44 6682.52 C 7947.58 6681.09 7908.88 6650.99 7908.88 6648.13 C 7890.25 6630.93 7844.39 6675.36 7821.46 6679.66 C 7794.23 6683.96 7776.33 6672.35 7763.43 6703.88 C 7761.99 6708.18 7774.17 6706.89 7772.73 6712.62 C 7769.87 6719.78 7756.97 6702.59 7749.8 6702.59 C 7731.17 6704.02 7713.97 6716.92 7698.21 6726.95 C 7689.61 6732.68 7709.67 6792.88 7673.84 6768.51 L 7669.54 6768.51 " }
+ }
+ }
+ // PATH visit al count: 145
+ DemoShape {
+ objectName: "al"
+ ShapePath {
+ objectName: "svg_path:al"
+ // boundingRect: 7484.67, 6639.48 287.237x551.811
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7755.54 6951.96 C 7755.54 6970.59 7775.6 6976.32 7771.3 6993.52 C 7756.97 7047.98 7748.37 7000.68 7738.34 7039.38 C 7738.34 7039.38 7729.74 7096.71 7729.74 7098.14 C 7726.88 7102.44 7682.45 7116.77 7681.01 7126.8 C 7681.01 7126.8 7699.64 7155.47 7699.64 7155.47 C 7703.94 7168.36 7683.88 7155.47 7683.88 7164.07 C 7685.31 7201.33 7669.55 7181.26 7645.18 7185.56 C 7643.75 7185.56 7646.62 7187 7645.18 7188.43 C 7643.75 7189.86 7643.75 7189.86 7642.32 7191.3 C 7640.89 7189.86 7640.89 7188.43 7639.45 7185.56 C 7639.45 7182.7 7639.45 7179.83 7639.45 7176.96 C 7639.45 7168.36 7643.75 7161.2 7640.89 7154.03 C 7638.02 7149.73 7629.42 7154.03 7626.55 7149.73 C 7619.39 7144 7620.82 7132.54 7615.09 7126.8 C 7605.06 7113.9 7513.33 7089.54 7523.37 7066.61 C 7527.67 7056.58 7547.73 7068.04 7552.03 7059.44 C 7553.46 7056.58 7524.8 7037.95 7520.5 7022.18 C 7519.07 7017.88 7526.23 7016.45 7526.23 7012.15 C 7527.67 7006.42 7526.23 6999.25 7527.67 6993.52 C 7527.67 6986.35 7523.37 6979.19 7527.67 6974.89 C 7530.53 6969.15 7542 6973.45 7546.3 6969.15 C 7549.16 6964.86 7546.3 6953.39 7542 6953.39 C 7536.26 6954.82 7540.56 6966.29 7536.26 6970.59 C 7527.67 6977.75 7536.26 6921.86 7537.7 6907.53 C 7540.56 6890.33 7516.2 6898.93 7516.2 6881.73 C 7516.2 6868.83 7514.77 6855.93 7521.93 6845.9 C 7526.23 6840.17 7539.13 6845.9 7544.86 6840.17 C 7547.73 6837.3 7537.7 6835.87 7536.26 6831.57 C 7534.83 6821.54 7544.86 6807.21 7537.7 6800.04 C 7529.1 6791.44 7514.77 6801.47 7503.3 6798.61 C 7503.3 6788.58 7503.3 6778.54 7503.3 6769.94 C 7503.3 6764.21 7506.17 6757.05 7503.3 6751.31 C 7499 6744.15 7486.1 6744.15 7484.67 6736.98 C 7484.67 6735.55 7530.53 6642.39 7534.83 6639.53 C 7536.26 6638.09 7543.43 6673.92 7554.9 6675.36 C 7554.9 6675.36 7584.99 6650.99 7584.99 6650.99 C 7607.92 6650.99 7603.62 6686.82 7616.52 6693.99 C 7620.82 6696.85 7625.12 6691.12 7630.85 6692.55 C 7640.89 6695.42 7650.92 6699.72 7658.08 6706.89 C 7685.31 6736.98 7665.25 6767.08 7668.12 6797.18 C 7668.12 6807.21 7676.71 6815.81 7676.71 6824.41 C 7675.28 6828.7 7663.82 6828.7 7665.25 6833 C 7666.68 6843.04 7681.01 6848.77 7682.45 6858.8 C 7683.88 6863.1 7675.28 6867.4 7675.28 6871.7 C 7675.28 6883.16 7712.54 6936.19 7718.28 6944.79 C 7726.88 6954.82 7732.61 6939.06 7736.91 6939.06 C 7742.64 6937.63 7749.81 6939.06 7754.11 6941.92 C 7756.97 6944.79 7752.67 6951.96 7755.54 6951.96 " }
+ }
+ }
+ // PATH visit is count: 553
+ DemoShape {
+ objectName: "is"
+ ShapePath {
+ objectName: "svg_path:is"
+ // boundingRect: 3559.94, 1656.42 811.066x675.17
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 3676.75 2152.28 C 3672.45 2150.85 3580.73 2106.42 3576.43 2103.56 C 3570.69 2099.26 3589.33 2093.53 3593.63 2087.79 C 3597.92 2080.63 3593.63 2059.13 3600.79 2062 C 3612.26 2066.3 3605.09 2084.93 3612.26 2093.53 C 3613.69 2096.39 3615.12 2086.36 3619.42 2086.36 C 3630.89 2084.93 3643.79 2094.96 3653.82 2090.66 C 3698.25 2073.46 3639.49 2057.7 3693.95 2053.4 C 3703.98 2053.4 3714.01 2060.56 3724.04 2062 C 3731.21 2063.43 3712.58 2051.96 3706.85 2049.1 C 3703.98 2049.1 3701.11 2049.1 3698.25 2049.1 C 3686.78 2047.66 3675.32 2050.53 3665.28 2046.23 C 3658.12 2041.93 3682.48 2049.1 3688.21 2043.36 C 3691.08 2040.5 3676.75 2039.06 3678.18 2034.77 C 3678.18 2027.6 3683.91 2021.87 3691.08 2019 C 3695.38 2017.57 3702.55 2021.87 3708.28 2021.87 C 3718.31 2020.43 3729.78 2019 3738.37 2011.83 C 3742.67 2008.97 3726.91 2010.4 3722.61 2007.54 C 3718.31 2006.1 3682.48 2023.3 3672.45 2021.87 C 3665.28 2020.43 3676.75 2006.1 3678.18 1998.94 C 3678.18 1990.34 3672.45 1983.17 3673.88 1974.57 C 3675.32 1965.97 3691.08 1960.24 3688.21 1950.21 C 3686.78 1944.48 3673.88 1953.08 3668.15 1950.21 C 3663.85 1948.78 3665.28 1937.31 3660.98 1940.18 C 3659.55 1941.61 3665.28 1948.78 3662.42 1947.34 C 3650.95 1941.61 3639.49 1922.98 3629.45 1915.81 C 3628.02 1912.95 3623.72 1914.38 3620.86 1914.38 C 3617.99 1914.38 3615.12 1918.68 3613.69 1917.25 C 3610.82 1915.81 3612.26 1908.65 3607.96 1907.21 C 3602.22 1904.35 3523.4 1904.35 3580.73 1877.12 C 3596.49 1869.95 3593.63 1884.28 3607.96 1892.88 C 3620.86 1900.05 3640.92 1881.42 3653.82 1888.58 C 3659.55 1891.45 3643.79 1902.91 3649.52 1907.21 C 3653.82 1911.51 3656.68 1898.61 3662.42 1897.18 C 3672.45 1894.32 3683.91 1888.58 3692.51 1894.32 C 3698.25 1900.05 3689.65 1911.51 3691.08 1918.68 C 3691.08 1922.98 3691.08 1911.51 3692.51 1910.08 C 3699.68 1901.48 3807.17 1971.71 3769.9 1910.08 C 3764.17 1901.48 3749.84 1917.25 3738.37 1915.81 C 3725.48 1914.38 3715.44 1898.61 3709.71 1887.15 C 3709.71 1887.15 3711.14 1885.72 3712.58 1884.28 C 3728.34 1872.82 3754.14 1869.95 3771.34 1868.52 C 3779.94 1868.52 3789.97 1877.12 3795.7 1869.95 C 3807.17 1859.92 3752.71 1864.22 3754.14 1848.45 C 3755.57 1838.42 3782.8 1842.72 3782.8 1831.26 C 3781.37 1763.9 3732.64 1838.42 3729.78 1825.52 C 3728.34 1816.92 3735.51 1808.33 3732.64 1801.16 C 3731.21 1798.29 3726.91 1806.89 3724.04 1806.89 C 3721.18 1806.89 3726.91 1799.73 3724.04 1799.73 C 3714.01 1802.59 3714.01 1819.79 3706.85 1801.16 C 3705.41 1799.73 3708.28 1793.99 3706.85 1793.99 C 3701.11 1798.29 3701.11 1808.33 3693.95 1809.76 C 3689.65 1809.76 3691.08 1799.73 3686.78 1798.29 C 3678.18 1795.43 3638.05 1815.49 3636.62 1792.56 C 3636.62 1788.26 3643.79 1788.26 3643.79 1783.96 C 3639.49 1771.06 3605.09 1772.5 3607.96 1758.16 C 3619.42 1697.97 3653.82 1783.96 3660.98 1779.66 C 3681.05 1766.76 3638.05 1725.2 3666.72 1725.2 C 3692.51 1723.77 3675.32 1821.22 3708.28 1776.8 C 3712.58 1771.06 3691.08 1763.9 3696.81 1759.6 C 3702.55 1752.43 3718.31 1768.2 3725.48 1761.03 C 3729.78 1753.86 3706.85 1761.03 3701.11 1755.3 C 3692.51 1746.7 3689.65 1733.8 3688.21 1722.34 C 3682.48 1673.61 3715.44 1766.76 3728.34 1752.43 C 3732.64 1748.13 3683.91 1709.44 3705.41 1699.4 C 3715.44 1695.11 3722.61 1722.34 3732.64 1718.04 C 3738.37 1713.74 3705.41 1667.87 3746.97 1682.21 C 3781.37 1695.11 3742.67 1755.3 3781.37 1779.66 C 3781.37 1781.1 3805.73 1769.63 3807.17 1740.97 C 3807.17 1735.23 3798.57 1738.1 3795.7 1733.8 C 3791.4 1729.5 3775.64 1702.27 3781.37 1693.67 C 3791.4 1682.21 3811.47 1703.7 3825.8 1710.87 C 3830.1 1712.3 3812.9 1708 3812.9 1703.7 C 3811.47 1699.4 3820.07 1700.84 3822.93 1699.4 C 3824.36 1697.97 3824.36 1695.11 3822.93 1695.11 C 3804.3 1696.54 3805.73 1725.2 3784.24 1677.91 C 3765.61 1636.35 3805.73 1667.87 3807.17 1667.87 C 3807.17 1667.87 3808.6 1666.44 3808.6 1665.01 C 3808.6 1662.14 3805.73 1656.41 3808.6 1657.84 C 3812.9 1662.14 3812.9 1670.74 3818.63 1675.04 C 3824.36 1679.34 3834.4 1677.91 3840.13 1682.21 C 3854.46 1693.67 3832.96 1708 3834.4 1715.17 C 3834.4 1718.04 3841.56 1715.17 3844.43 1716.6 C 3845.86 1718.04 3855.89 1726.63 3853.03 1732.37 C 3850.16 1733.8 3843 1732.37 3844.43 1735.23 C 3847.3 1740.97 3858.76 1739.53 3858.76 1746.7 C 3860.19 1756.73 3845.86 1766.76 3848.73 1776.8 C 3851.59 1785.39 3873.09 1779.66 3871.66 1788.26 C 3871.66 1796.86 3850.16 1782.53 3847.3 1789.69 C 3845.86 1796.86 3911.79 1825.52 3845.86 1835.56 C 3834.4 1838.42 3830.1 1818.36 3818.63 1814.06 C 3814.33 1812.62 3820.07 1822.66 3822.93 1826.96 C 3825.8 1838.42 3838.7 1848.45 3835.83 1858.49 C 3832.96 1869.95 3811.47 1869.95 3810.03 1881.42 C 3807.17 1888.58 3827.23 1884.28 3828.66 1891.45 C 3831.53 1898.61 3820.07 1905.78 3817.2 1912.95 C 3814.33 1921.55 3804.3 1938.74 3812.9 1940.18 C 3843 1945.91 3841.56 1885.72 3860.19 1875.68 C 3918.95 1844.15 3878.83 1884.28 3877.39 1890.02 C 3868.79 1925.84 3911.79 1887.15 3914.65 1884.28 C 3933.29 1864.22 3926.12 1806.89 3940.45 1808.33 C 3990.61 1812.62 3946.18 1875.68 3974.85 1898.61 C 3997.78 1918.68 3984.88 1872.82 3990.61 1861.35 C 3992.05 1854.19 3997.78 1845.59 4004.94 1842.72 C 4009.24 1841.29 4014.98 1844.15 4020.71 1847.02 C 4022.14 1848.45 4022.14 1852.75 4023.57 1852.75 C 4035.04 1849.89 4046.51 1834.12 4056.54 1839.85 C 4066.57 1844.15 4065.14 1859.92 4066.57 1871.38 C 4066.57 1878.55 4057.97 1882.85 4059.4 1890.02 C 4066.57 1925.84 4083.77 1864.22 4073.74 1917.25 C 4069.44 1947.34 4059.4 1931.58 4066.57 1958.81 C 4078.04 2007.54 4078.04 1865.65 4092.37 1862.79 C 4129.63 1852.75 4121.03 1914.38 4123.9 1928.71 C 4123.9 1931.58 4123.9 1921.55 4126.76 1920.11 C 4128.2 1918.68 4133.93 1920.11 4132.5 1918.68 C 4123.9 1900.05 4135.36 1927.28 4135.36 1927.28 C 4136.79 1927.28 4136.79 1922.98 4138.23 1921.55 C 4145.39 1910.08 4159.73 1890.02 4175.49 1888.58 C 4191.26 1887.15 4189.82 1920.11 4202.72 1928.71 C 4208.45 1931.58 4199.85 1915.81 4204.15 1911.51 C 4212.75 1901.48 4218.49 1924.41 4218.49 1924.41 C 4219.92 1927.28 4217.05 1917.25 4218.49 1914.38 C 4221.35 1910.08 4228.52 1907.21 4229.95 1901.48 C 4232.82 1895.75 4229.95 1888.58 4229.95 1881.42 C 4231.38 1872.82 4229.95 1861.35 4237.12 1857.05 C 4247.15 1852.75 4257.18 1859.92 4267.21 1862.79 C 4293.01 1868.52 4271.51 1879.98 4274.38 1890.02 C 4277.25 1895.75 4285.84 1894.32 4288.71 1898.61 C 4298.74 1920.11 4245.72 1925.84 4293.01 1951.64 C 4298.74 1954.51 4298.74 1938.74 4304.48 1935.88 C 4310.21 1933.01 4317.37 1938.74 4323.11 1935.88 C 4336.01 1931.58 4350.34 1900.05 4368.97 1934.44 C 4371.83 1940.18 4356.07 1937.31 4348.9 1938.74 C 4327.41 1943.04 4318.81 1940.18 4304.48 1963.11 C 4303.04 1965.97 4311.64 1965.97 4311.64 1968.84 C 4313.07 1973.14 4304.48 1976.01 4307.34 1977.44 C 4314.51 1983.17 4327.41 1978.87 4334.57 1984.6 C 4357.5 2001.8 4303.04 2033.33 4300.18 2036.2 C 4297.31 2037.63 4305.91 2031.9 4310.21 2033.33 C 4320.24 2036.2 4330.27 2043.36 4338.87 2050.53 C 4340.3 2051.96 4331.71 2051.96 4331.71 2054.83 C 4331.71 2059.13 4338.87 2063.43 4338.87 2067.73 C 4338.87 2070.59 4333.14 2066.3 4331.71 2067.73 C 4321.67 2073.46 4294.44 2084.93 4304.48 2089.23 C 4317.37 2093.53 4328.84 2062 4338.87 2070.59 C 4343.17 2076.33 4347.47 2082.06 4353.2 2086.36 C 4356.07 2089.23 4361.8 2083.49 4363.24 2087.79 C 4370.4 2100.69 4377.57 2125.05 4360.37 2136.52 C 4351.77 2142.25 4338.87 2140.82 4330.27 2145.12 C 4325.97 2147.99 4320.24 2153.72 4323.11 2156.58 C 4327.41 2160.88 4401.93 2169.48 4343.17 2203.88 C 4330.27 2211.04 4297.31 2180.95 4297.31 2179.52 C 4287.28 2166.62 4343.17 2202.45 4328.84 2215.34 C 4321.67 2221.08 4310.21 2201.01 4303.04 2206.75 C 4297.31 2211.04 4317.37 2215.34 4320.24 2222.51 C 4321.67 2225.38 4314.51 2228.24 4311.64 2229.68 C 4298.74 2235.41 4281.54 2251.17 4271.51 2241.14 C 4264.35 2233.98 4265.78 2205.31 4258.61 2212.48 C 4251.45 2221.08 4274.38 2233.98 4268.65 2244.01 C 4264.35 2252.61 4245.72 2242.57 4238.55 2249.74 C 4229.95 2256.91 4235.68 2274.1 4228.52 2282.7 C 4225.65 2287 4217.05 2278.4 4212.75 2279.84 C 4202.72 2282.7 4196.99 2295.6 4186.96 2297.03 C 4178.36 2298.47 4182.66 2261.21 4159.73 2255.47 C 4152.56 2254.04 4161.16 2271.24 4156.86 2278.4 C 4155.43 2281.27 4149.69 2275.54 4146.83 2278.4 C 4145.39 2278.4 4152.56 2282.7 4151.13 2282.7 C 4112.43 2289.87 4133.93 2279.84 4098.1 2285.57 C 4079.47 2288.44 4063.7 2304.2 4045.07 2311.37 C 4042.21 2312.8 4036.47 2314.23 4033.61 2312.8 C 4023.57 2308.5 4020.71 2292.74 4010.68 2292.74 C 4002.08 2294.17 4004.94 2312.8 3997.78 2315.67 C 3992.05 2317.1 3990.61 2305.63 3984.88 2302.77 C 3976.28 2299.9 3944.75 2298.47 3933.29 2294.17 C 3913.22 2288.44 3911.79 2322.83 3907.49 2327.13 C 3897.46 2335.73 3844.43 2330 3834.4 2325.7 C 3808.6 2317.1 3787.1 2275.54 3774.2 2262.64 C 3755.57 2246.87 3739.81 2259.77 3729.78 2231.11 C 3728.34 2223.94 3721.18 2215.34 3724.04 2208.18 C 3726.91 2198.15 3754.14 2193.85 3745.54 2189.55 C 3736.94 2185.25 3729.78 2212.48 3722.61 2206.75 C 3714.01 2199.58 3732.64 2183.81 3726.91 2173.78 C 3724.04 2168.05 3716.88 2186.68 3711.14 2183.81 C 3701.11 2178.08 3701.11 2163.75 3692.51 2156.58 C 3688.21 2152.29 3682.48 2153.72 3676.75 2152.28 " }
+ }
+ }
+ // PATH visit by count: 321
+ DemoShape {
+ objectName: "by"
+ ShapePath {
+ objectName: "svg_path:by"
+ // boundingRect: 7670.98, 3935.51 1022.16x977.052
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7768.44 4911.13 C 7785.63 4906.83 7751.24 4893.93 7774.17 4886.77 C 7781.34 4883.9 7791.37 4883.9 7799.97 4885.33 C 7802.83 4885.33 7802.83 4892.5 7807.13 4893.93 C 7821.46 4898.23 7841.53 4830.87 7847.26 4826.57 C 7858.73 4820.84 7873.06 4823.71 7884.52 4819.41 C 7901.72 4812.24 7914.62 4797.91 7933.25 4792.18 C 7940.42 4790.74 7947.58 4793.61 7954.75 4793.61 C 7961.91 4793.61 7969.08 4793.61 7976.25 4793.61 C 7997.74 4790.74 8017.81 4777.84 8037.87 4779.28 C 8042.17 4779.28 8043.6 4785.01 8047.9 4786.44 C 8083.73 4790.74 8128.16 4786.44 8161.12 4777.84 C 8176.89 4773.55 8165.42 4787.88 8174.02 4795.04 C 8176.89 4796.48 8182.62 4789.31 8185.49 4792.18 C 8188.35 4795.04 8179.76 4803.64 8184.05 4805.07 C 8219.88 4819.41 8194.09 4796.48 8211.29 4792.18 C 8268.61 4776.41 8217.02 4800.78 8235.65 4810.81 C 8238.52 4813.67 8242.81 4817.97 8244.25 4815.11 C 8251.41 4810.81 8244.25 4785.01 8251.41 4785.01 C 8258.58 4783.58 8271.48 4796.48 8280.08 4786.44 C 8285.81 4780.71 8285.81 4759.21 8294.41 4763.51 C 8307.31 4772.11 8275.78 4785.01 8311.61 4785.01 C 8324.51 4785.01 8317.34 4756.35 8337.4 4770.68 C 8374.67 4796.48 8330.24 4787.88 8360.33 4795.04 C 8368.93 4796.48 8357.47 4774.98 8363.2 4767.81 C 8371.8 4756.35 8384.7 4736.28 8396.16 4742.02 C 8407.63 4747.75 8419.09 4787.88 8430.56 4785.01 C 8450.62 4779.28 8437.73 4754.91 8460.66 4749.18 C 8469.25 4747.75 8476.42 4757.78 8483.59 4756.35 C 8530.88 4747.75 8452.06 4731.98 8517.98 4736.28 C 8550.95 4739.15 8533.75 4774.98 8571.01 4766.38 C 8606.84 4759.21 8549.51 4720.52 8549.51 4704.75 C 8549.51 4693.29 8552.38 4666.06 8553.81 4656.03 C 8560.98 4615.9 8571.01 4601.57 8576.74 4600.13 C 8586.77 4597.27 8598.24 4601.57 8606.84 4597.27 C 8612.57 4594.4 8609.71 4584.37 8615.44 4581.5 C 8628.34 4572.9 8646.97 4574.34 8662.73 4570.04 C 8658.43 4567.17 8661.3 4561.44 8658.43 4557.14 C 8658.43 4557.14 8628.34 4538.51 8628.34 4537.07 C 8626.9 4534.21 8632.64 4529.91 8631.2 4527.04 C 8626.9 4517.01 8616.87 4511.28 8615.44 4502.68 C 8614 4498.38 8622.6 4496.94 8622.6 4492.64 C 8619.74 4482.61 8599.67 4482.61 8598.24 4471.15 C 8596.81 4466.85 8605.41 4465.41 8606.84 4461.11 C 8611.14 4443.92 8559.54 4433.88 8555.24 4422.42 C 8549.51 4410.95 8559.54 4396.62 8558.11 4383.72 C 8558.11 4360.79 8626.9 4390.89 8645.53 4379.42 C 8658.43 4372.26 8672.76 4345.03 8671.33 4330.7 C 8671.33 4329.26 8665.6 4330.7 8667.03 4329.26 C 8674.2 4322.1 8687.1 4319.23 8691.4 4310.63 C 8695.7 4304.9 8691.4 4292 8685.66 4289.13 C 8661.3 4280.54 8658.43 4290.57 8645.53 4276.24 C 8641.23 4270.5 8655.57 4250.44 8639.8 4251.87 C 8622.6 4253.31 8614 4247.57 8589.64 4254.74 C 8581.04 4257.61 8572.44 4266.2 8563.84 4263.34 C 8556.68 4260.47 8569.58 4220.34 8556.68 4211.74 C 8552.38 4208.88 8507.95 4208.88 8493.62 4198.85 C 8470.69 4184.51 8485.02 4170.18 8452.06 4160.15 C 8446.32 4157.28 8460.66 4132.92 8454.92 4131.49 C 8434.86 4131.49 8427.69 4130.05 8404.76 4114.29 C 8394.73 4108.56 8413.36 4078.46 8410.5 4072.73 C 8401.9 4059.83 8421.96 4069.86 8416.23 4061.26 C 8411.93 4054.1 8377.53 4026.87 8378.97 4019.7 C 8384.7 3999.64 8387.56 3985.3 8376.1 3965.24 C 8373.23 3959.51 8364.63 3970.97 8358.9 3970.97 C 8357.47 3970.97 8311.61 3942.31 8311.61 3942.31 C 8275.78 3927.98 8301.57 3939.44 8281.51 3948.04 C 8278.64 3949.47 8261.45 3946.61 8257.15 3950.91 C 8249.98 3960.94 8238.52 3988.17 8227.05 3993.9 C 8204.12 4005.37 8205.55 3946.61 8205.55 3946.61 C 8161.12 3936.58 8169.72 3942.31 8139.63 3965.24 C 8132.46 3970.97 8126.73 3950.91 8119.56 3945.18 C 8095.2 3927.98 8102.36 3975.27 8066.54 3946.61 C 8066.54 3948.04 8039.31 3969.54 8036.44 3978.14 C 8033.57 3982.44 8026.41 4025.43 8026.41 4025.43 C 8019.24 4031.17 8007.78 4022.57 7999.18 4025.43 C 7996.31 4026.87 7999.18 4036.9 7994.88 4036.9 C 7989.14 4036.9 7984.84 4028.3 7977.68 4028.3 C 7959.05 4026.87 7956.18 4068.43 7931.82 4074.16 L 7931.82 4075.59 C 7940.42 4097.09 7930.38 4115.72 7928.95 4137.22 C 7927.52 4150.12 7974.81 4124.32 7969.08 4144.39 C 7967.65 4148.68 7960.48 4148.68 7957.61 4152.98 C 7956.18 4158.72 7961.91 4167.32 7957.61 4173.05 C 7949.02 4183.08 7927.52 4168.75 7920.35 4178.78 C 7914.62 4188.81 7921.79 4203.15 7914.62 4211.74 C 7887.39 4254.74 7900.29 4198.85 7883.09 4251.87 C 7883.09 4254.74 7877.36 4254.74 7877.36 4256.17 C 7877.36 4256.17 7890.26 4296.3 7890.26 4296.3 C 7890.26 4297.73 7875.92 4337.86 7877.36 4343.6 C 7877.36 4349.33 7921.79 4340.73 7908.89 4372.26 C 7906.02 4379.42 7877.36 4389.46 7878.79 4376.56 C 7878.79 4372.26 7885.96 4369.39 7884.52 4365.09 C 7881.66 4346.46 7848.69 4362.23 7844.39 4367.96 C 7828.63 4383.72 7860.16 4382.29 7837.23 4395.19 C 7827.2 4399.49 7812.86 4396.62 7805.7 4405.22 C 7799.97 4410.95 7820.03 4426.72 7812.86 4429.59 C 7805.7 4431.02 7797.1 4426.72 7789.93 4431.02 C 7781.34 4436.75 7779.9 4451.08 7771.3 4456.82 C 7758.4 4468.28 7752.67 4442.48 7741.21 4446.78 C 7729.74 4452.52 7688.18 4475.45 7670.98 4463.98 C 7696.78 4537.07 7759.84 4604.43 7764.14 4683.26 C 7764.14 4686.12 7761.27 4688.99 7759.84 4690.42 C 7749.81 4700.45 7659.52 4776.41 7701.08 4792.18 C 7703.94 4793.61 7706.81 4786.44 7709.68 4787.88 C 7722.58 4789.31 7736.91 4793.61 7748.37 4802.21 C 7777.04 4825.14 7746.94 4873.87 7758.4 4899.66 C 7759.84 4903.96 7764.14 4908.26 7767 4912.56 L 7768.44 4911.13 " }
+ }
+ }
+ // PATH visit no count: 1982
+ DemoShape {
+ objectName: "no"
+ ShapePath {
+ objectName: "svg_path:no"
+ // boundingRect: 5717.75, 1239.32 1829.56x2604.54
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6918.57 1718.04 C 6920 1718.04 6920 1718.04 6920 1719.47 M 7152.18 1963.11 L 7150.74 1963.11 M 7529.1 1421.37 C 7537.7 1432.84 7557.76 1460.07 7540.56 1465.8 C 7523.37 1471.53 7478.94 1448.6 7493.27 1461.5 C 7520.5 1484.43 7466.04 1517.39 7458.87 1534.59 C 7458.87 1536.02 7476.07 1597.65 7456.01 1590.48 C 7401.55 1568.99 7466.04 1533.16 7447.41 1501.63 C 7418.74 1451.47 7374.32 1470.1 7337.05 1429.97 L 7335.62 1422.8 C 7334.19 1422.8 7332.75 1422.8 7331.32 1424.24 C 7331.32 1422.8 7329.89 1422.8 7329.89 1421.37 L 7329.89 1424.24 C 7301.22 1429.97 7306.96 1462.93 7282.59 1470.1 C 7261.1 1475.83 7269.7 1445.73 7245.33 1472.96 C 7242.47 1477.26 7245.33 1484.43 7243.9 1490.16 C 7242.47 1491.6 7226.7 1521.69 7226.7 1523.13 C 7222.4 1534.59 7231 1571.85 7232.43 1581.89 C 7232.43 1586.18 7229.57 1589.05 7229.57 1593.35 C 7229.57 1601.95 7231 1609.12 7232.43 1617.71 C 7236.73 1629.18 7246.76 1637.78 7248.2 1649.24 C 7255.36 1699.4 7242.47 1665.01 7225.27 1680.77 C 7205.2 1700.84 7223.83 1706.57 7218.1 1730.93 C 7216.67 1736.67 7210.94 1739.53 7209.5 1745.27 C 7209.5 1746.7 7190.87 1729.5 7188 1728.07 C 7183.71 1725.2 7130.68 1706.57 7126.38 1712.3 C 7120.65 1718.04 7124.95 1729.5 7120.65 1735.23 C 7103.45 1765.33 7080.52 1746.7 7056.15 1751 C 7048.99 1751 7038.96 1758.16 7033.22 1753.86 C 7026.06 1748.13 7028.92 1736.67 7024.62 1729.5 C 7014.59 1712.3 6981.63 1667.87 6964.43 1657.84 C 6907.1 1624.88 6932.9 1710.87 6932.9 1710.87 C 6924.3 1712.3 6917.14 1702.27 6908.54 1702.27 C 6904.24 1702.27 6897.07 1706.57 6898.51 1709.44 C 6899.94 1710.87 6901.37 1712.3 6902.8 1712.3 L 6899.94 1712.3 C 6888.47 1715.17 6872.71 1708 6866.98 1718.04 C 6862.68 1725.2 6881.31 1729.5 6885.61 1736.67 C 6889.91 1745.27 6892.77 1755.3 6892.77 1765.33 C 6892.77 1782.53 6868.41 1795.43 6872.71 1812.62 C 6874.14 1825.52 6924.3 1822.66 6877.01 1852.75 C 6864.11 1861.35 6765.22 1812.62 6745.16 1838.42 C 6736.56 1849.89 6772.39 1931.58 6743.72 1943.04 L 6697.86 1922.98 C 6689.26 1922.98 6662.03 1973.14 6660.6 1984.6 C 6657.73 2000.37 6657.73 2020.43 6646.27 2034.77 C 6637.67 2044.8 6621.9 2029.03 6626.2 2054.83 C 6631.94 2086.36 6669.2 2089.23 6653.43 2127.92 C 6644.83 2152.28 6597.54 2208.18 6588.94 2233.98 C 6586.08 2246.87 6604.71 2258.34 6600.41 2271.24 L 6568.88 2298.47 C 6557.41 2304.2 6534.48 2291.3 6528.75 2304.2 L 6543.08 2360.09 C 6543.08 2360.09 6535.91 2403.09 6535.91 2404.52 L 6540.21 2457.55 C 6538.78 2463.28 6531.61 2464.72 6530.18 2470.45 C 6517.28 2509.14 6497.22 2549.27 6484.32 2589.4 C 6475.72 2615.2 6535.91 2600.87 6525.88 2671.09 C 6515.85 2738.45 6477.15 2671.09 6418.39 2708.35 C 6399.76 2719.82 6368.23 2768.55 6363.93 2791.48 C 6362.5 2798.64 6371.1 2804.38 6369.67 2810.11 C 6368.23 2825.87 6348.17 2835.91 6348.17 2851.67 C 6348.17 3025.08 6369.67 2724.12 6369.67 2897.53 C 6369.67 2906.13 6359.64 2911.86 6359.64 2920.46 C 6358.2 2930.49 6363.93 2939.09 6365.37 2949.13 C 6365.37 2957.72 6358.2 2967.76 6359.64 2976.36 C 6361.07 2997.85 6384 3016.48 6386.87 3037.98 C 6389.73 3069.51 6366.8 3109.64 6373.97 3139.74 C 6382.57 3172.7 6437.03 3169.83 6442.76 3205.66 L 6426.99 3262.99 C 6415.53 3273.02 6386.87 3265.85 6388.3 3280.19 C 6395.46 3341.81 6432.73 3326.05 6416.96 3407.74 L 6418.39 3449.3 C 6405.5 3482.26 6359.64 3485.13 6356.77 3495.16 C 6355.34 3498.03 6362.5 3499.46 6362.5 3503.76 C 6365.37 3509.49 6368.23 3516.66 6365.37 3522.39 C 6361.07 3530.99 6346.74 3532.42 6343.87 3542.46 C 6335.27 3568.25 6375.4 3625.58 6343.87 3670.01 C 6330.97 3688.64 6325.24 3674.31 6320.94 3659.98 C 6322.37 3659.98 6322.37 3659.98 6323.81 3658.54 C 6329.54 3654.24 6320.94 3642.78 6316.64 3637.04 C 6286.54 3589.75 6302.31 3659.98 6255.01 3615.55 C 6250.71 3611.25 6259.31 3602.65 6259.31 3596.92 C 6259.31 3588.32 6250.71 3596.92 6250.71 3596.92 C 6242.12 3584.02 6244.98 3562.52 6247.85 3565.39 C 6247.85 3562.52 6246.41 3561.09 6246.41 3559.65 C 6243.55 3545.32 6239.25 3532.42 6236.38 3518.09 C 6234.95 3509.49 6247.85 3490.86 6237.82 3490.86 C 6201.99 3490.86 6263.61 3535.29 6227.78 3559.65 C 6226.35 3559.65 6224.92 3558.22 6224.92 3556.79 C 6223.48 3548.19 6227.78 3538.16 6224.92 3530.99 C 6220.62 3523.82 6206.29 3510.93 6204.85 3518.09 C 6203.42 3529.56 6217.75 3536.72 6222.05 3546.76 C 6224.92 3552.49 6216.32 3565.39 6219.18 3572.55 C 6230.65 3602.65 6239.25 3586.88 6230.65 3606.95 C 6230.65 3608.38 6230.65 3612.68 6227.78 3612.68 C 6224.92 3612.68 6222.05 3604.08 6219.18 3606.95 C 6209.15 3625.58 6222.05 3649.94 6210.59 3649.94 C 6204.85 3649.94 6212.02 3631.31 6207.72 3634.18 C 6183.36 3649.94 6229.22 3665.71 6169.02 3665.71 C 6161.86 3665.71 6169.02 3651.38 6163.29 3647.08 C 6156.13 3642.78 6143.23 3627.01 6138.93 3634.18 C 6134.63 3644.21 6153.26 3649.94 6157.56 3659.98 C 6158.99 3664.27 6120.3 3667.14 6116 3681.47 C 6114.56 3687.21 6130.33 3692.94 6126.03 3697.24 C 6118.86 3705.84 6100.23 3698.67 6093.07 3705.84 C 6088.77 3711.57 6107.4 3713 6105.96 3718.73 C 6097.37 3777.49 6085.9 3701.54 6078.73 3731.63 C 6077.3 3734.5 6083.03 3737.37 6081.6 3740.23 C 6074.43 3750.26 6037.17 3794.69 6035.74 3796.13 C 6032.87 3798.99 6028.57 3790.39 6025.71 3793.26 C 6017.11 3800.43 6018.54 3819.06 6007.08 3820.49 C 5998.48 3823.36 5997.04 3798.99 5987.01 3801.86 C 5976.98 3804.72 5981.28 3823.36 5974.11 3829.09 C 5968.38 3833.39 5958.35 3826.22 5952.62 3829.09 C 5948.32 3830.52 5946.88 3836.25 5942.58 3837.69 C 5928.25 3846.29 5932.55 3833.39 5912.49 3833.39 C 5902.46 3833.39 5890.99 3849.15 5885.26 3841.99 C 5879.52 3834.82 5903.89 3829.09 5901.02 3820.49 C 5896.72 3813.32 5888.12 3831.96 5879.52 3831.96 C 5868.06 3833.39 5856.59 3829.09 5845.13 3824.79 C 5820.76 3813.32 5868.06 3797.56 5872.36 3787.53 C 5873.79 3781.79 5863.76 3776.06 5858.03 3777.49 C 5852.29 3778.93 5853.73 3791.83 5846.56 3793.26 C 5837.96 3794.69 5829.36 3788.96 5820.76 3784.66 C 5803.57 3777.49 5793.53 3751.7 5784.94 3745.97 C 5780.64 3741.67 5772.04 3744.53 5766.3 3741.67 C 5756.27 3735.93 5739.07 3698.67 5740.51 3688.64 C 5740.51 3680.04 5750.54 3674.31 5753.41 3664.27 C 5754.84 3655.68 5743.37 3634.18 5753.41 3637.04 C 5764.87 3639.91 5756.27 3664.27 5767.74 3668.57 C 5773.47 3672.87 5774.9 3654.24 5782.07 3651.38 C 5784.94 3648.51 5787.8 3651.38 5790.67 3654.24 C 5787.8 3647.08 5786.37 3639.91 5783.5 3632.75 C 5782.07 3622.71 5803.57 3625.58 5809.3 3618.41 C 5812.17 3615.55 5796.4 3618.41 5797.83 3614.11 C 5800.7 3606.95 5807.87 3602.65 5815.03 3599.78 C 5823.63 3594.05 5840.83 3598.35 5845.13 3588.32 C 5850.86 3575.42 5810.73 3594.05 5802.13 3585.45 C 5796.4 3592.62 5793.53 3592.62 5793.53 3579.72 C 5794.97 3568.25 5819.33 3565.39 5816.47 3555.35 C 5812.17 3543.89 5794.97 3541.02 5783.5 3543.89 C 5764.87 3549.62 5772.04 3594.05 5753.41 3592.62 C 5743.37 3592.62 5750.54 3573.99 5746.24 3566.82 C 5743.37 3562.52 5737.64 3559.65 5731.91 3556.79 C 5731.91 3556.79 5731.91 3558.22 5733.34 3558.22 C 5731.91 3558.22 5731.91 3558.22 5730.48 3559.65 C 5730.48 3558.22 5731.91 3556.79 5731.91 3556.79 C 5727.61 3553.92 5723.31 3551.05 5723.31 3546.76 C 5723.31 3539.59 5740.51 3552.49 5746.24 3548.19 C 5754.84 3541.02 5751.97 3522.39 5762 3518.09 C 5769.17 3513.79 5773.47 3529.56 5780.64 3530.99 C 5783.5 3532.42 5779.2 3523.82 5782.07 3522.39 C 5784.94 3519.53 5789.24 3523.82 5792.1 3522.39 C 5797.83 3520.96 5777.77 3520.96 5779.2 3515.23 C 5779.2 3513.79 5837.96 3488 5833.66 3488 C 5787.8 3485.13 5816.47 3523.82 5772.04 3483.7 C 5770.6 3482.26 5779.2 3483.7 5780.64 3480.83 C 5783.5 3477.96 5780.64 3470.8 5784.94 3469.36 C 5790.67 3465.06 5797.83 3470.8 5803.57 3466.5 C 5806.43 3465.06 5802.13 3460.77 5802.13 3457.9 C 5805 3452.17 5807.87 3447.87 5810.73 3445 C 5817.9 3440.7 5833.66 3445 5835.1 3437.83 C 5836.53 3429.24 5816.47 3430.67 5816.47 3423.5 C 5815.03 3420.64 5817.9 3416.34 5820.76 3412.04 C 5817.9 3412.04 5813.6 3404.87 5810.73 3407.74 C 5803.57 3412.04 5799.27 3419.2 5794.97 3424.94 C 5789.24 3436.4 5792.1 3450.73 5783.5 3459.33 C 5779.2 3463.63 5780.64 3447.87 5779.2 3443.57 C 5770.6 3427.8 5763.44 3432.1 5769.17 3417.77 C 5774.9 3406.3 5797.83 3380.51 5784.94 3383.37 C 5770.6 3387.67 5760.57 3450.73 5743.37 3419.2 C 5739.07 3409.17 5734.77 3397.71 5731.91 3386.24 C 5730.48 3374.78 5751.97 3386.24 5777.77 3377.64 C 5783.5 3376.21 5786.37 3370.48 5787.8 3363.31 C 5777.77 3373.34 5759.14 3351.84 5759.14 3346.11 C 5759.14 3333.21 5779.2 3330.35 5790.67 3324.61 C 5787.8 3308.85 5774.9 3323.18 5763.44 3336.08 C 5763.44 3336.08 5762 3336.08 5762 3334.65 C 5760.57 3333.21 5759.14 3330.35 5756.27 3327.48 C 5760.57 3331.78 5762 3337.51 5763.44 3336.08 C 5756.27 3343.25 5750.54 3348.98 5746.24 3350.41 C 5739.07 3350.41 5741.94 3336.08 5739.07 3330.35 C 5737.64 3327.48 5711.84 3301.68 5719.01 3300.25 C 5729.04 3300.25 5737.64 3308.85 5746.24 3316.02 C 5746.24 3314.58 5744.81 3311.72 5746.24 3308.85 C 5749.11 3298.82 5766.3 3298.82 5774.9 3291.65 C 5776.34 3290.22 5773.47 3284.49 5770.6 3285.92 C 5759.14 3287.35 5740.51 3301.68 5730.48 3295.95 C 5717.58 3288.79 5727.61 3255.82 5734.77 3251.52 C 5744.81 3244.36 5756.27 3265.85 5769.17 3265.85 C 5770.6 3267.29 5799.27 3232.89 5830.8 3248.66 C 5840.83 3252.96 5842.26 3267.29 5852.29 3271.59 C 5862.33 3275.89 5843.7 3250.09 5876.66 3241.49 C 5918.22 3230.03 5875.22 3303.12 5919.65 3300.25 C 5946.88 3297.38 5909.62 3274.45 5911.05 3255.82 C 5912.49 3235.76 5985.58 3283.05 5975.55 3232.89 C 5974.11 3222.86 5955.48 3241.49 5946.88 3238.62 C 5941.15 3237.19 5939.72 3228.59 5939.72 3222.86 C 5939.72 3207.09 5979.85 3191.33 5965.51 3185.6 C 5929.69 3171.27 5941.15 3237.19 5938.28 3244.36 C 5938.28 3245.79 5921.09 3250.09 5906.75 3251.52 C 5903.89 3252.96 5905.32 3247.22 5903.89 3245.79 C 5893.86 3240.06 5882.39 3237.19 5872.36 3231.46 L 5870.93 3230.03 C 5866.63 3240.06 5862.33 3247.22 5859.46 3250.09 C 5858.03 3251.52 5813.6 3231.46 5802.13 3231.46 C 5797.83 3230.03 5800.7 3238.62 5797.83 3240.06 C 5782.07 3247.22 5763.44 3250.09 5747.67 3245.79 C 5744.81 3245.79 5723.31 3221.43 5726.18 3217.13 C 5733.34 3207.09 5746.24 3202.8 5756.27 3199.93 C 5767.74 3197.06 5789.23 3209.96 5789.23 3198.5 C 5790.67 3188.46 5767.74 3195.63 5756.27 3197.06 C 5749.11 3197.06 5743.37 3207.09 5736.21 3204.23 C 5706.11 3192.76 5741.94 3177 5750.54 3177 C 5764.87 3178.43 5789.23 3195.63 5806.43 3184.16 C 5823.63 3171.27 5756.27 3188.46 5747.67 3168.4 C 5744.81 3159.8 5780.64 3161.23 5773.47 3156.93 C 5764.87 3152.63 5751.97 3158.37 5744.81 3151.2 C 5721.88 3132.57 5756.27 3095.31 5777.77 3093.87 C 5793.53 3093.87 5813.6 3096.74 5822.2 3109.64 C 5826.5 3115.37 5813.6 3125.4 5817.9 3131.14 C 5822.2 3136.87 5819.33 3113.94 5825.06 3112.51 C 5832.23 3111.07 5835.1 3125.4 5842.26 3126.84 C 5847.99 3126.84 5830.8 3116.81 5833.66 3113.94 C 5840.83 3105.34 5875.22 3135.44 5901.02 3111.07 C 5905.32 3106.77 5893.86 3103.91 5888.12 3103.91 C 5837.96 3101.04 5912.49 3115.37 5870.93 3118.24 C 5863.76 3119.67 5819.33 3098.17 5799.27 3095.31 C 5792.1 3095.31 5786.37 3091.01 5779.2 3091.01 C 5770.6 3091.01 5754.84 3103.91 5753.41 3095.31 C 5751.97 3085.28 5774.9 3085.28 5777.77 3075.24 C 5780.64 3062.35 5751.97 3056.61 5751.97 3042.28 C 5751.97 3036.55 5766.3 3042.28 5769.17 3046.58 C 5773.47 3053.75 5773.47 3070.94 5782.07 3069.51 C 5790.67 3068.08 5772.04 3045.15 5780.64 3043.71 C 5792.1 3042.28 5797.83 3065.21 5809.3 3065.21 C 5817.9 3065.21 5815.03 3037.98 5820.76 3043.71 C 5827.93 3049.45 5817.9 3062.35 5817.9 3072.38 C 5817.9 3073.81 5815.03 3078.11 5816.47 3078.11 C 5825.06 3075.24 5820.76 3056.61 5829.36 3056.61 C 5833.66 3056.61 5830.8 3066.64 5835.1 3069.51 C 5843.7 3073.81 5858.03 3082.41 5862.33 3073.81 C 5865.19 3066.64 5843.7 3069.51 5839.4 3063.78 C 5832.23 3055.18 5829.36 3045.15 5829.36 3035.12 C 5827.93 3022.22 5849.43 3012.18 5862.33 3015.05 C 5868.06 3016.48 5865.19 3027.95 5868.06 3033.68 C 5872.36 3042.28 5879.52 3048.01 5882.39 3056.61 C 5882.39 3059.48 5875.22 3065.21 5878.09 3065.21 C 5882.39 3065.21 5883.82 3059.48 5883.82 3056.61 C 5878.09 3027.95 5846.56 3009.32 5896.72 3000.72 C 5913.92 2997.85 5916.79 3022.22 5916.79 3045.15 C 5918.22 3042.28 5919.65 3037.98 5922.52 3036.55 C 5931.12 3029.38 5955.48 3045.15 5952.62 3035.12 C 5949.75 3023.65 5929.69 3033.68 5919.65 3026.52 C 5912.49 3020.78 5915.35 3007.88 5909.62 3000.72 C 5902.46 2990.69 5863.76 2977.79 5858.03 2973.49 C 5856.59 2972.06 5862.33 2967.76 5866.63 2967.76 C 5872.36 2966.32 5879.52 2966.32 5885.26 2969.19 C 5888.12 2970.62 5886.69 2977.79 5889.56 2977.79 C 5893.86 2979.22 5911.05 2949.13 5931.12 2964.89 C 5936.85 2969.19 5928.25 2983.52 5935.42 2986.39 C 5941.15 2989.25 5932.55 2970.62 5938.28 2967.76 C 5948.32 2962.02 5969.81 2993.55 5969.81 2994.99 C 5978.41 2996.42 5995.61 2989.25 5991.31 2982.09 C 5987.01 2973.49 5969.81 2987.82 5962.65 2982.09 C 5952.62 2973.49 5972.68 2957.72 5988.45 2951.99 C 5976.98 2956.29 5966.95 2959.16 5956.92 2959.16 C 5870.93 2960.59 5946.88 2950.56 5944.02 2933.36 C 5942.58 2921.89 5923.95 2923.33 5916.79 2913.3 C 5906.75 2901.83 5911.05 2891.8 5949.75 2896.1 C 5958.35 2897.53 5958.35 2913.3 5966.95 2916.16 C 5972.68 2917.6 5976.98 2907.56 5982.71 2907.56 C 5991.31 2906.13 5998.48 2906.13 6007.08 2907.56 C 6005.64 2906.13 6004.21 2903.26 6002.78 2901.83 C 6001.34 2893.23 6012.81 2884.63 6021.41 2883.2 C 6025.71 2881.77 6021.41 2891.8 6024.27 2896.1 C 6028.57 2901.83 6038.61 2901.83 6041.47 2909 C 6044.34 2914.73 6035.74 2923.33 6041.47 2927.63 C 6048.64 2934.79 6067.27 2947.69 6073 2937.66 C 6077.3 2927.63 6050.07 2923.33 6048.64 2911.86 C 6048.64 2904.7 6064.4 2910.43 6070.14 2906.13 C 6073 2903.26 6060.1 2909 6057.24 2906.13 C 6055.8 2904.7 6065.84 2898.96 6062.97 2900.4 C 6055.8 2901.83 6050.07 2911.86 6044.34 2909 C 6034.31 2903.26 6031.44 2890.37 6028.57 2880.33 C 6025.71 2876.03 6035.74 2884.63 6040.04 2886.07 C 6044.34 2888.93 6025.71 2871.73 6040.04 2874.6 C 6041.47 2874.6 6098.8 2904.7 6093.07 2866 C 6087.33 2818.71 6047.2 2907.56 6055.8 2845.94 C 6057.24 2833.04 6091.63 2810.11 6101.67 2815.84 C 6110.26 2820.14 6100.23 2838.77 6107.4 2847.37 C 6110.26 2851.67 6114.56 2840.2 6118.86 2837.34 C 6126.03 2834.47 6146.09 2834.47 6140.36 2828.74 C 6136.06 2823.01 6116 2843.07 6117.43 2834.47 C 6117.43 2824.44 6133.19 2821.57 6140.36 2812.97 C 6148.96 2804.38 6148.96 2787.18 6160.42 2785.74 C 6169.02 2782.88 6164.72 2800.08 6169.02 2807.24 C 6171.89 2812.97 6181.92 2815.84 6181.92 2823.01 C 6181.92 2831.61 6169.02 2835.91 6170.46 2844.5 C 6171.89 2850.24 6179.06 2837.34 6184.79 2838.77 C 6193.39 2840.2 6203.42 2861.7 6207.72 2854.54 C 6212.02 2843.07 6190.52 2834.47 6191.95 2823.01 C 6191.95 2811.54 6250.71 2825.87 6252.15 2825.87 C 6262.18 2818.71 6263.61 2802.94 6262.18 2791.48 C 6260.75 2785.74 6243.55 2797.21 6244.98 2791.48 C 6249.28 2772.85 6308.04 2769.98 6305.17 2755.65 C 6303.74 2744.18 6269.35 2744.18 6276.51 2734.15 C 6282.24 2724.12 6308.04 2726.98 6309.47 2715.52 C 6313.77 2658.19 6262.18 2728.42 6250.71 2738.45 C 6244.98 2742.75 6227.78 2747.05 6232.08 2754.21 C 6237.82 2764.25 6255.01 2758.51 6265.05 2762.81 C 6266.48 2762.81 6262.18 2764.25 6260.75 2765.68 C 6253.58 2769.98 6244.98 2772.85 6237.82 2778.58 C 6230.65 2784.31 6194.82 2815.84 6183.36 2812.97 C 6176.19 2811.54 6179.06 2800.08 6176.19 2794.34 C 6173.32 2788.61 6163.29 2784.31 6166.16 2778.58 C 6170.46 2768.55 6184.79 2767.11 6193.39 2759.95 C 6194.82 2758.51 6187.66 2764.25 6184.79 2762.81 C 6181.92 2762.81 6189.09 2755.65 6187.66 2757.08 C 6177.62 2761.38 6161.86 2784.31 6147.53 2778.58 C 6126.03 2769.98 6157.56 2751.35 6173.32 2741.32 C 6181.92 2737.02 6193.39 2737.02 6199.12 2728.42 C 6203.42 2724.12 6186.22 2732.72 6180.49 2731.28 C 6177.62 2731.28 6179.06 2728.42 6179.06 2725.55 C 6180.49 2715.52 6179.06 2705.49 6184.79 2696.89 C 6187.65 2692.59 6196.25 2699.75 6200.55 2696.89 C 6203.42 2696.89 6194.82 2696.89 6194.82 2694.02 C 6194.82 2689.72 6196.25 2685.42 6197.69 2681.12 C 6201.99 2676.82 6212.02 2686.86 6214.89 2681.12 C 6217.75 2676.82 6213.45 2668.22 6217.75 2662.49 C 6222.05 2656.76 6234.95 2658.19 6237.82 2649.59 C 6239.25 2643.86 6219.18 2636.69 6224.92 2635.26 C 6234.95 2632.4 6243.55 2649.59 6253.58 2646.73 C 6260.75 2645.29 6242.12 2638.13 6237.82 2630.96 C 6230.65 2620.93 6252.15 2609.46 6263.61 2602.3 C 6275.08 2595.13 6279.38 2628.1 6293.71 2633.83 C 6302.31 2636.69 6313.77 2633.83 6319.51 2626.66 C 6322.37 2622.36 6299.44 2626.66 6302.31 2620.93 C 6306.61 2612.33 6325.24 2613.76 6329.54 2605.17 C 6336.7 2585.1 6285.11 2638.13 6298.01 2583.67 C 6299.44 2575.07 6302.31 2569.34 6303.74 2566.47 C 6298.01 2570.77 6292.28 2575.07 6285.11 2577.94 C 6263.61 2587.97 6325.24 2550.7 6320.94 2544.97 C 6319.51 2542.11 6316.64 2542.11 6312.34 2543.54 L 6312.34 2542.11 C 6312.34 2539.24 6302.31 2542.11 6303.74 2539.24 C 6308.04 2529.21 6352.47 2532.07 6359.63 2529.21 C 6362.5 2526.34 6356.77 2519.18 6359.63 2516.31 C 6381.13 2496.24 6409.8 2509.14 6416.96 2467.58 C 6416.96 2460.42 6405.5 2474.75 6399.76 2477.61 C 6392.6 2479.05 6385.43 2477.61 6378.27 2477.61 C 6372.53 2479.05 6362.5 2487.65 6362.5 2481.91 C 6365.37 2463.28 6416.96 2463.28 6402.63 2438.92 C 6401.2 2434.62 6388.3 2437.48 6378.27 2437.48 C 6379.7 2440.35 6382.57 2443.22 6385.43 2448.95 C 6378.27 2456.12 6372.53 2464.72 6365.37 2471.88 C 6362.5 2473.31 6356.77 2481.91 6355.34 2477.61 C 6353.9 2470.45 6353.9 2430.32 6365.37 2428.89 C 6365.37 2424.59 6365.37 2421.72 6368.23 2415.99 C 6372.53 2404.52 6391.16 2410.25 6401.2 2404.52 C 6404.06 2403.09 6394.03 2404.52 6392.6 2403.09 C 6388.3 2397.36 6384 2390.19 6382.57 2384.46 C 6381.13 2375.86 6394.03 2342.9 6402.63 2342.9 C 6414.1 2341.46 6419.83 2360.09 6431.29 2362.96 C 6435.59 2364.39 6426.99 2352.93 6425.56 2347.2 C 6422.69 2340.03 6424.13 2327.13 6416.96 2322.83 C 6409.8 2318.53 6396.9 2330 6389.73 2324.26 C 6366.8 2305.63 6414.1 2312.8 6421.26 2309.93 C 6434.16 2304.2 6435.59 2285.57 6455.66 2289.87 C 6464.26 2292.74 6468.56 2304.2 6477.15 2304.2 C 6484.32 2304.2 6465.69 2295.6 6464.26 2289.87 L 6464.26 2288.44 C 6462.82 2288.44 6461.39 2289.87 6459.96 2289.87 C 6457.09 2289.87 6459.96 2281.27 6457.09 2279.84 C 6442.76 2276.97 6432.73 2302.77 6416.96 2301.33 C 6366.8 2294.17 6444.19 2284.14 6452.79 2276.97 C 6455.66 2275.54 6447.06 2275.54 6442.76 2275.54 C 6432.73 2274.1 6424.13 2278.4 6414.1 2276.97 C 6401.2 2274.1 6405.5 2251.17 6408.36 2239.71 C 6418.39 2186.68 6439.89 2209.61 6475.72 2195.28 C 6495.79 2186.68 6452.79 2172.35 6447.06 2162.32 C 6444.19 2156.58 6459.96 2160.88 6465.69 2158.02 C 6471.42 2155.15 6441.33 2133.65 6477.15 2145.12 C 6480.02 2145.12 6472.86 2150.85 6472.86 2153.72 C 6474.29 2173.78 6481.45 2143.69 6481.45 2140.82 C 6481.45 2139.39 6484.32 2132.22 6485.75 2135.09 C 6490.05 2139.39 6484.32 2152.28 6491.49 2153.72 C 6502.95 2155.15 6518.72 2149.42 6521.58 2139.39 C 6525.88 2129.35 6505.82 2125.05 6504.38 2115.02 C 6504.38 2113.59 6538.78 2093.53 6550.25 2099.26 C 6557.41 2102.12 6543.08 2125.05 6551.68 2123.62 C 6560.28 2123.62 6550.25 2103.56 6557.41 2099.26 C 6558.84 2097.82 6558.84 2097.82 6560.28 2096.39 C 6558.84 2094.96 6557.41 2093.53 6555.98 2092.09 C 6551.68 2089.23 6564.58 2080.63 6560.28 2079.19 C 6555.98 2077.76 6553.11 2087.79 6547.38 2090.66 C 6533.05 2096.39 6515.85 2107.86 6501.52 2099.26 C 6495.79 2094.96 6512.98 2086.36 6517.28 2079.19 C 6525.88 2060.56 6528.75 2017.57 6550.25 2033.33 C 6557.41 2039.06 6528.75 2047.66 6535.91 2053.4 C 6541.65 2059.13 6550.25 2043.36 6560.28 2041.93 C 6567.44 2041.93 6574.61 2044.8 6580.34 2050.53 C 6588.94 2059.13 6604.71 2090.66 6600.41 2079.19 C 6597.54 2069.16 6583.21 2060.56 6586.08 2049.1 C 6587.51 2039.06 6604.71 2037.63 6610.44 2029.03 C 6613.31 2024.73 6601.84 2031.9 6597.54 2033.33 C 6576.04 2040.5 6568.88 2050.53 6558.84 2023.3 C 6553.11 2008.97 6566.01 2017.57 6567.44 2010.4 C 6568.88 2008.97 6563.14 2011.83 6563.14 2010.4 C 6558.84 1997.5 6598.97 2017.57 6604.71 2010.4 C 6614.74 1993.2 6570.31 1978.87 6567.44 1978.87 C 6564.58 1978.87 6570.31 1984.6 6568.88 1987.47 C 6564.58 1996.07 6540.21 2026.17 6531.61 2021.87 C 6521.58 2017.57 6520.15 2001.8 6524.45 1991.77 C 6527.32 1983.17 6541.65 1988.9 6547.38 1983.17 C 6551.68 1978.87 6534.48 1988.9 6531.61 1984.6 C 6525.88 1976.01 6550.25 1970.27 6557.41 1976.01 C 6560.28 1977.44 6554.55 1984.6 6555.98 1983.17 C 6578.91 1960.24 6616.17 1974.57 6613.31 1943.04 C 6613.31 1938.74 6604.71 1947.34 6600.41 1944.48 C 6597.54 1943.04 6601.84 1937.31 6598.97 1935.88 C 6597.54 1934.44 6597.54 1941.61 6596.11 1941.61 C 6590.37 1944.48 6586.08 1943.04 6581.78 1943.04 C 6573.18 1944.48 6561.71 1951.64 6558.84 1944.48 C 6540.21 1907.21 6596.11 1933.01 6597.54 1933.01 C 6606.14 1925.84 6598.97 1910.08 6603.27 1900.05 C 6604.71 1895.75 6607.57 1910.08 6609.01 1915.81 C 6610.44 1917.25 6614.74 1914.38 6614.74 1915.81 C 6614.74 1924.41 6609.01 1933.01 6611.87 1940.18 C 6611.87 1943.04 6643.4 1988.9 6650.57 1980.31 C 6667.77 1960.24 6596.11 1897.18 6617.6 1887.15 C 6624.77 1882.85 6639.1 1894.32 6650.57 1904.35 C 6649.13 1900.05 6649.13 1895.75 6646.27 1892.88 C 6639.1 1887.15 6617.6 1890.02 6620.47 1879.98 C 6621.9 1868.52 6640.54 1869.95 6652 1868.52 C 6713.63 1858.49 6662.03 1888.58 6700.73 1907.21 C 6727.96 1920.11 6669.2 1862.79 6695 1862.79 C 6702.16 1862.79 6706.46 1871.38 6712.19 1871.38 C 6720.79 1869.95 6733.69 1869.95 6732.26 1862.79 C 6723.66 1802.59 6687.83 1862.79 6673.5 1859.92 C 6670.63 1858.49 6679.23 1855.62 6677.8 1852.75 C 6677.8 1849.89 6673.5 1845.59 6670.63 1847.02 C 6666.33 1848.45 6666.33 1857.05 6660.6 1857.05 C 6652 1858.49 6637.67 1859.92 6633.37 1851.32 C 6630.5 1845.59 6631.94 1838.42 6634.8 1832.69 C 6627.64 1834.12 6619.04 1835.56 6614.74 1839.85 C 6607.57 1847.02 6614.74 1865.65 6606.14 1871.38 C 6600.41 1875.68 6606.14 1854.19 6600.41 1849.89 C 6593.24 1845.59 6584.64 1847.02 6577.48 1848.45 C 6571.74 1849.89 6574.61 1861.35 6570.31 1865.65 C 6566.01 1869.95 6560.28 1874.25 6555.98 1878.55 C 6550.25 1884.28 6545.95 1890.02 6541.65 1895.75 L 6541.65 1890.02 C 6545.95 1879.98 6554.55 1869.95 6555.98 1859.92 C 6557.41 1854.19 6550.25 1847.02 6553.11 1841.29 C 6560.28 1834.12 6574.61 1836.99 6581.78 1829.82 C 6611.87 1801.16 6534.48 1809.76 6576.04 1765.33 C 6606.14 1730.93 6600.41 1795.43 6593.24 1826.96 C 6596.11 1821.22 6600.41 1816.92 6604.71 1815.49 C 6607.57 1814.06 6610.44 1824.09 6611.87 1821.22 C 6616.17 1814.06 6609 1745.27 6633.37 1778.23 C 6646.27 1795.43 6650.57 1808.33 6650.57 1815.49 C 6656.3 1812.62 6660.6 1809.76 6664.9 1809.76 C 6670.63 1809.76 6673.5 1821.22 6679.23 1821.22 C 6690.7 1822.66 6703.59 1819.79 6713.63 1814.06 C 6727.96 1805.46 6683.53 1816.92 6680.66 1798.29 C 6680.66 1792.56 6687.83 1785.39 6695 1785.39 L 6697.86 1785.39 C 6696.43 1783.96 6695 1783.96 6695 1781.1 C 6695 1772.5 6712.19 1775.36 6719.36 1769.63 C 6720.79 1768.2 6713.63 1768.2 6712.19 1766.76 C 6702.16 1759.6 6697.86 1743.83 6700.73 1730.93 C 6702.16 1719.47 6730.82 1729.5 6733.69 1718.04 C 6736.56 1709.44 6729.39 1700.84 6726.53 1692.24 C 6726.53 1702.27 6725.09 1708 6723.66 1709.44 C 6713.63 1709.44 6703.59 1700.84 6695 1706.57 C 6680.66 1712.3 6677.8 1730.93 6669.2 1743.83 C 6662.03 1739.53 6649.13 1739.53 6649.13 1732.37 C 6652 1720.9 6670.63 1719.47 6673.5 1708 C 6676.36 1702.27 6657.73 1715.17 6656.3 1709.44 C 6653.43 1700.84 6669.2 1693.67 6667.77 1683.64 C 6667.77 1677.91 6647.7 1685.07 6652 1682.21 C 6672.07 1672.17 6705.03 1680.77 6690.7 1662.14 C 6684.96 1656.41 6663.47 1659.28 6669.2 1654.98 C 6676.36 1650.68 6687.83 1663.58 6696.43 1659.28 C 6705.03 1653.54 6700.73 1634.91 6709.33 1632.05 C 6723.66 1627.75 6726.53 1654.98 6727.96 1677.91 C 6727.96 1669.31 6730.82 1662.14 6733.69 1654.98 C 6740.86 1643.51 6743.72 1683.64 6753.76 1689.37 C 6763.79 1695.11 6776.69 1689.37 6786.72 1685.07 C 6788.15 1685.07 6743.72 1669.31 6746.59 1659.28 C 6749.46 1649.24 6759.49 1636.35 6769.52 1637.78 C 6779.55 1637.78 6778.12 1656.41 6785.29 1663.58 C 6792.45 1670.74 6802.48 1669.31 6811.08 1676.47 C 6815.38 1680.77 6819.68 1699.4 6822.55 1693.67 C 6834.01 1659.28 6749.46 1667.87 6780.99 1600.52 C 6786.72 1589.05 6811.08 1574.72 6822.55 1583.32 C 6832.58 1589.05 6819.68 1656.41 6819.68 1659.28 C 6822.55 1662.14 6826.85 1654.98 6828.28 1650.68 C 6829.71 1642.08 6822.55 1630.61 6828.28 1623.45 C 6832.58 1616.28 6851.21 1627.75 6852.64 1619.15 C 6852.64 1610.55 6832.58 1611.98 6829.71 1603.38 C 6826.85 1596.22 6841.18 1534.59 6862.68 1560.39 C 6868.41 1568.99 6862.68 1649.24 6862.68 1652.11 C 6859.81 1662.14 6841.18 1679.34 6851.21 1682.21 C 6877.01 1690.81 6864.11 1640.64 6872.71 1634.91 C 6881.31 1629.18 6894.21 1634.91 6902.8 1630.61 C 6907.1 1627.75 6895.64 1624.88 6892.77 1622.01 C 6882.74 1613.41 6861.24 1601.95 6869.84 1581.89 C 6907.1 1498.76 6869.84 1593.35 6912.84 1564.69 C 6924.3 1557.52 6912.84 1536.02 6921.44 1525.99 C 6924.3 1523.13 6928.6 1527.42 6931.47 1530.29 C 6934.33 1533.16 6932.9 1538.89 6935.77 1541.76 C 6942.93 1547.49 6951.53 1548.92 6958.7 1554.65 C 6964.43 1558.95 6968.73 1567.55 6975.9 1570.42 C 6983.06 1571.85 6963 1560.39 6960.13 1553.22 C 6960.13 1551.79 6965.86 1551.79 6965.86 1548.92 C 6968.73 1528.86 6964.43 1498.76 6961.56 1500.19 C 6954.4 1500.19 6912.84 1536.02 6901.37 1494.46 C 6895.64 1471.53 6937.2 1461.5 6948.67 1464.37 C 6958.7 1465.8 6964.43 1478.7 6974.46 1481.56 C 6977.33 1481.56 6973.03 1472.96 6975.9 1471.53 C 6978.76 1468.66 6984.5 1471.53 6987.36 1472.96 C 6995.96 1472.96 7005.99 1471.53 7013.16 1474.4 C 7031.79 1480.13 7007.43 1521.69 7046.12 1518.83 C 7053.29 1517.39 7057.59 1507.36 7056.15 1500.19 C 7056.15 1495.9 7047.55 1501.63 7043.26 1500.19 C 7038.96 1498.76 7031.79 1497.33 7031.79 1493.03 C 7031.79 1487.3 7048.99 1490.16 7043.26 1480.13 C 7037.52 1471.53 7036.09 1462.93 7041.82 1435.7 L 7041.82 1432.84 C 7038.96 1438.57 7034.66 1444.3 7030.36 1451.47 C 7023.19 1444.3 7010.29 1440 7007.43 1431.4 C 7000.26 1405.61 7086.25 1343.98 7053.29 1412.77 C 7066.19 1401.31 7084.82 1414.2 7089.12 1409.91 C 7096.28 1405.61 7073.35 1405.61 7070.49 1398.44 C 7060.45 1374.08 7103.45 1378.38 7106.31 1374.08 C 7112.05 1361.18 7067.62 1345.41 7067.62 1341.11 C 7069.05 1332.51 7089.12 1352.58 7091.98 1345.41 C 7097.72 1335.38 7079.08 1323.92 7081.95 1312.45 C 7086.25 1300.98 7165.07 1308.15 7166.51 1312.45 C 7169.37 1316.75 7130.68 1386.97 7132.11 1391.27 C 7132.11 1392.71 7133.54 1392.71 7134.98 1392.71 C 7132.11 1392.71 7130.68 1392.71 7130.68 1394.14 C 7133.54 1398.44 7142.14 1391.27 7146.44 1394.14 C 7165.07 1407.04 7122.08 1412.77 7146.44 1419.94 C 7155.04 1422.8 7140.71 1435.7 7139.28 1444.3 C 7139.28 1447.17 7136.41 1488.73 7160.77 1464.37 C 7165.07 1460.07 7160.77 1451.47 7162.21 1445.73 C 7163.64 1437.14 7167.94 1431.4 7170.81 1424.24 C 7173.67 1415.64 7157.91 1425.67 7162.21 1412.77 C 7170.81 1388.41 7193.74 1293.82 7209.5 1285.22 C 7209.5 1285.22 7219.53 1322.48 7219.53 1325.35 C 7216.67 1333.95 7202.34 1338.25 7203.77 1348.28 C 7205.2 1354.01 7218.1 1338.25 7222.4 1343.98 C 7228.13 1351.15 7216.67 1369.78 7225.27 1374.08 C 7235.3 1381.24 7249.63 1374.08 7258.23 1366.91 C 7263.96 1361.18 7245.33 1356.88 7245.33 1349.71 C 7245.33 1339.68 7241.03 1326.78 7246.76 1318.18 C 7251.06 1308.15 7265.4 1306.72 7272.56 1298.12 C 7275.43 1295.25 7265.4 1298.12 7262.53 1296.69 C 7252.5 1292.39 7235.3 1289.52 7235.3 1278.05 C 7233.87 1269.45 7256.8 1279.49 7261.1 1272.32 C 7266.83 1262.29 7253.93 1250.82 7256.8 1240.79 C 7258.23 1233.63 7268.26 1255.12 7275.43 1250.82 C 7286.89 1246.52 7295.49 1236.49 7318.42 1252.26 C 7335.62 1263.72 7288.33 1325.35 7284.03 1333.95 C 7282.59 1336.81 7292.63 1329.65 7294.06 1326.78 C 7302.66 1319.62 7306.96 1303.85 7316.99 1300.98 C 7324.16 1299.55 7319.86 1313.88 7318.42 1321.05 C 7314.12 1331.08 7296.93 1341.11 7302.66 1351.15 C 7308.39 1359.74 7322.72 1333.95 7331.32 1339.68 C 7332.75 1341.11 7334.19 1342.55 7334.19 1343.98 C 7337.05 1342.55 7342.79 1346.85 7345.65 1345.41 C 7354.25 1336.81 7332.75 1329.65 7332.75 1329.65 C 7331.32 1323.92 7329.89 1275.19 7335.62 1266.59 C 7361.42 1233.63 7378.62 1296.69 7380.05 1296.69 C 7473.2 1300.98 7332.75 1240.79 7424.48 1269.45 C 7471.77 1283.79 7437.38 1285.22 7430.21 1303.85 C 7428.78 1306.72 7437.38 1305.28 7440.24 1303.85 C 7448.84 1300.98 7456.01 1293.82 7464.61 1292.39 C 7467.47 1292.39 7464.61 1298.12 7466.04 1299.55 C 7470.34 1302.42 7476.07 1302.42 7480.37 1303.85 C 7481.8 1305.28 7483.24 1308.15 7486.1 1309.58 C 7493.27 1315.32 7504.73 1319.62 7510.47 1328.21 C 7511.9 1331.08 7504.73 1331.08 7501.87 1333.95 C 7494.7 1338.25 7484.67 1339.68 7480.37 1346.85 C 7471.77 1358.31 7478.94 1378.38 7467.47 1384.11 C 7435.94 1399.87 7397.25 1378.38 7371.45 1407.04 C 7367.15 1409.91 7380.05 1402.74 7385.78 1402.74 C 7392.95 1402.74 7398.68 1404.17 7405.85 1405.61 C 7407.28 1407.04 7408.71 1408.47 7410.15 1408.47 C 7421.61 1409.91 7437.38 1401.31 7444.54 1411.34 C 7450.27 1418.5 7427.34 1428.54 7431.64 1437.14 C 7435.94 1444.3 7441.68 1417.07 7450.27 1418.5 C 7460.31 1421.37 7457.44 1444.3 7453.14 1458.63 C 7454.57 1455.77 7456.01 1454.33 7457.44 1452.9 C 7467.47 1448.6 7481.8 1455.77 7493.27 1451.47 C 7496.14 1450.03 7484.67 1445.73 7484.67 1441.43 C 7483.24 1431.4 7484.67 1422.8 7487.54 1414.2 C 7488.97 1411.34 7486.1 1422.8 7490.4 1422.8 C 7501.87 1425.67 7513.33 1422.8 7524.8 1421.37 L 7529.1 1421.37 M 6914.27 1716.6 L 6915.7 1716.6 L 6914.27 1716.6 M 7175.11 1278.05 C 7176.54 1289.52 7156.48 1292.39 7149.31 1300.98 C 7091.98 1245.09 7169.37 1245.09 7175.11 1278.05 M 6957.27 1389.84 C 6963 1389.84 6965.86 1399.87 6970.16 1397.01 C 6975.9 1392.71 6960.13 1376.94 6965.86 1378.38 C 6968.73 1379.81 6968.73 1386.97 6973.03 1386.97 C 6975.9 1385.54 6970.16 1378.38 6973.03 1376.94 C 6985.93 1371.21 6980.2 1407.04 6993.09 1388.41 C 6995.96 1385.54 6985.93 1379.81 6988.79 1376.94 C 6997.39 1365.48 7011.73 1361.18 7024.62 1352.58 C 7024.62 1354.01 7023.19 1354.01 7023.19 1354.01 C 7023.19 1355.44 7028.92 1355.44 7028.92 1356.88 C 7027.49 1362.61 7021.76 1368.34 7018.89 1374.08 C 7007.43 1405.61 6991.66 1418.5 6957.27 1424.24 C 6960.13 1415.64 6970.16 1405.61 6965.86 1397.01 C 6964.43 1395.57 6961.56 1394.14 6958.7 1394.14 C 6958.7 1392.71 6955.83 1389.84 6957.27 1389.84 M 6947.23 1395.57 L 6941.5 1395.57 C 6938.63 1394.14 6941.5 1395.57 6947.23 1395.57 M 6861.24 1497.33 C 6864.11 1491.6 6872.71 1488.73 6879.87 1485.86 C 6881.31 1484.43 6885.61 1485.86 6884.17 1487.3 C 6879.87 1498.76 6872.71 1508.79 6866.98 1518.83 C 6865.54 1511.66 6858.38 1504.49 6861.24 1497.33 M 6834.01 1533.16 L 6835.45 1536.02 C 6835.45 1537.46 6834.01 1538.89 6834.01 1540.32 C 6834.01 1537.46 6832.58 1536.02 6834.01 1533.16 M 6758.06 1560.39 C 6769.52 1528.86 6884.17 1536.02 6785.29 1581.89 C 6778.12 1573.29 6755.19 1570.42 6758.06 1560.39 M 6558.84 1793.99 C 6561.71 1796.86 6553.11 1799.73 6554.55 1804.03 C 6557.41 1819.79 6594.67 1818.36 6543.08 1839.85 C 6537.35 1841.29 6531.61 1834.12 6524.45 1831.26 C 6561.71 1801.16 6540.21 1796.86 6540.21 1805.46 C 6541.65 1822.66 6523.02 1811.19 6514.42 1816.92 C 6507.25 1819.79 6501.52 1844.15 6500.09 1835.56 C 6495.79 1811.19 6533.05 1769.63 6558.84 1793.99 M 6444.19 1924.41 C 6447.06 1915.81 6441.33 1900.05 6449.92 1898.61 C 6502.95 1888.58 6455.66 1938.74 6438.46 1943.04 C 6438.46 1941.61 6435.59 1941.61 6435.59 1940.18 C 6438.46 1934.44 6442.76 1930.14 6444.19 1924.41 M 6090.2 2742.75 C 6097.37 2744.18 6093.07 2754.21 6094.5 2761.38 C 6093.07 2762.81 6090.2 2764.25 6088.77 2764.25 C 6083.03 2765.68 6075.87 2765.68 6070.14 2767.11 C 6009.94 2775.71 6088.77 2741.32 6090.2 2742.75 M 5823.63 3010.75 C 5829.36 3007.88 5836.53 3010.75 5843.7 3012.18 C 5843.7 3010.75 5843.7 3009.32 5845.13 3009.32 C 5845.13 3007.88 5846.56 3010.75 5846.56 3012.18 C 5845.13 3013.62 5843.7 3013.62 5842.26 3015.05 L 5843.7 3013.62 C 5837.96 3016.48 5832.23 3023.65 5826.5 3023.65 C 5822.2 3023.65 5820.76 3013.62 5823.63 3010.75 M 5730.48 3561.09 L 5733.34 3558.22 C 5736.21 3572.55 5741.94 3588.32 5734.77 3602.65 C 5719.01 3631.31 5727.61 3578.28 5730.48 3561.09 M 5753.41 3455.03 L 5753.41 3453.6 L 5753.41 3452.17 C 5756.27 3455.03 5760.57 3456.47 5760.57 3459.33 C 5769.17 3520.96 5737.64 3476.53 5750.54 3456.47 C 5750.54 3456.47 5750.54 3457.9 5749.11 3459.33 C 5750.54 3457.9 5753.41 3456.47 5753.41 3455.03 M 6303.74 2546.41 C 6298.01 2547.84 6293.71 2549.27 6289.41 2549.27 C 6279.38 2547.84 6292.28 2547.84 6303.74 2546.41 M 7145.01 1391.27 C 7142.14 1392.71 7137.84 1392.71 7134.98 1392.71 C 7139.28 1392.71 7147.88 1389.84 7145.01 1391.27 M 6902.8 1713.74 L 6904.24 1713.74 L 6902.8 1713.74 M 6907.1 1715.17 L 6909.97 1715.17 L 6907.1 1715.17 M 6911.4 1715.17 C 6911.4 1716.6 6911.4 1716.6 6912.84 1716.6 C 6911.4 1716.6 6911.4 1716.6 6911.4 1715.17 " }
+ }
+ }
+ // PATH visit ua count: 886
+ DemoShape {
+ objectName: "ua"
+ ShapePath {
+ objectName: "svg_path:ua"
+ // boundingRect: 7693.24, 4446.56 2146.03x1480.63
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 9710.38 5187.73 L 9710.38 5186.3 C 9698.91 5173.4 9706.08 5169.1 9708.94 5156.2 C 9711.81 5140.44 9691.75 5160.5 9691.75 5151.9 C 9693.18 5141.87 9684.58 5107.47 9688.88 5098.87 C 9690.31 5098.87 9731.87 5060.18 9731.87 5060.18 C 9734.74 5054.45 9727.57 5047.28 9727.57 5041.55 C 9726.14 5035.81 9724.71 5028.65 9729.01 5022.92 C 9750.51 4997.12 9817.86 5008.58 9837.93 4978.49 C 9842.23 4972.76 9835.06 4964.16 9832.2 4956.99 C 9830.76 4954.12 9825.03 4952.69 9825.03 4949.82 C 9825.03 4945.53 9833.63 4945.53 9833.63 4942.66 C 9833.63 4938.36 9827.9 4935.49 9826.46 4931.19 C 9825.03 4924.03 9826.46 4915.43 9827.9 4906.83 C 9827.9 4903.96 9832.2 4901.1 9832.2 4898.23 C 9837.93 4881.03 9812.13 4896.8 9812.13 4896.8 C 9807.83 4893.93 9816.43 4886.77 9815 4881.03 C 9809.26 4871 9800.67 4860.97 9793.5 4852.37 C 9783.47 4842.34 9767.7 4866.7 9766.27 4850.94 C 9759.1 4805.07 9763.4 4817.97 9789.2 4806.51 C 9790.63 4805.07 9802.1 4780.71 9793.5 4783.58 C 9773.44 4787.88 9763.4 4802.21 9746.21 4787.88 C 9727.57 4772.11 9760.54 4767.81 9764.84 4760.65 C 9769.14 4756.35 9763.4 4749.18 9764.84 4744.88 C 9769.14 4726.25 9786.33 4716.22 9769.14 4697.59 C 9763.4 4690.42 9753.37 4688.99 9747.64 4683.26 C 9740.47 4674.66 9757.67 4656.03 9746.21 4654.59 C 9718.98 4653.16 9736.17 4676.09 9714.68 4680.39 C 9641.58 4691.85 9733.31 4670.36 9674.55 4660.32 C 9655.92 4657.46 9654.48 4678.96 9638.72 4668.92 C 9632.99 4666.06 9628.69 4658.89 9624.39 4654.59 C 9611.49 4645.99 9617.22 4678.96 9601.46 4678.96 C 9591.42 4678.96 9581.39 4676.09 9571.36 4671.79 C 9567.06 4668.92 9564.19 4658.89 9558.46 4660.32 C 9551.29 4660.32 9548.43 4674.66 9539.83 4674.66 C 9529.8 4674.66 9522.63 4664.62 9511.17 4663.19 C 9473.9 4657.46 9529.8 4713.35 9491.1 4706.19 C 9481.07 4704.75 9478.2 4691.85 9469.6 4687.56 C 9463.87 4684.69 9455.27 4688.99 9448.11 4686.12 C 9426.61 4678.96 9410.84 4651.73 9392.21 4637.39 C 9386.48 4633.09 9333.45 4687.56 9314.82 4713.35 C 9309.09 4721.95 9294.76 4706.19 9284.73 4704.75 C 9278.99 4704.75 9278.99 4713.35 9274.69 4716.22 C 9267.53 4720.52 9261.8 4703.32 9253.2 4700.45 C 9218.8 4691.85 9211.63 4742.02 9187.27 4729.12 C 9177.24 4724.82 9168.64 4719.08 9161.47 4711.92 C 9155.74 4706.19 9150.01 4699.02 9148.58 4690.42 C 9148.58 4686.12 9157.17 4684.69 9155.74 4680.39 C 9155.74 4674.66 9118.48 4641.69 9117.05 4637.39 C 9115.61 4633.09 9124.21 4625.93 9121.35 4623.06 C 9119.91 4621.63 9117.05 4621.63 9115.61 4621.63 C 9109.88 4623.06 9105.58 4627.36 9099.85 4627.36 C 9089.82 4628.8 9088.38 4611.6 9082.65 4604.43 C 9081.22 4601.57 9078.35 4601.57 9075.48 4603 C 9069.75 4605.86 9039.65 4624.5 9033.92 4624.5 C 9031.06 4625.93 9031.06 4617.33 9026.76 4617.33 C 9016.72 4617.33 9006.69 4618.76 8998.09 4621.63 C 8995.23 4623.06 8986.63 4643.13 8979.46 4628.8 C 8978.03 4625.93 8983.76 4623.06 8983.76 4620.2 C 8985.19 4615.9 8985.19 4610.16 8982.33 4607.3 C 8978.03 4603 8972.3 4605.86 8968 4601.57 C 8960.83 4597.27 8969.43 4582.93 8963.7 4577.2 C 8957.96 4570.04 8937.9 4565.74 8942.2 4557.14 C 8947.93 4549.97 8980.9 4542.81 8972.3 4529.91 C 8960.83 4512.71 8933.6 4514.14 8925 4506.98 C 8919.27 4502.68 8914.97 4496.94 8913.54 4491.21 C 8910.67 4485.48 8914.97 4478.31 8912.1 4475.45 C 8906.37 4469.71 8897.77 4469.71 8892.04 4465.41 C 8883.44 4459.68 8879.14 4448.22 8869.11 4446.78 C 8857.64 4443.92 8853.34 4469.71 8840.44 4469.71 C 8836.15 4469.71 8837.58 4459.68 8833.28 4459.68 C 8777.39 4455.38 8811.78 4486.91 8778.82 4501.24 C 8778.82 4501.24 8717.19 4505.54 8715.76 4505.54 C 8712.89 4505.54 8722.93 4548.54 8710.03 4561.44 C 8701.43 4568.6 8691.4 4575.77 8679.93 4577.2 C 8675.63 4577.2 8677.06 4568.6 8672.76 4565.74 C 8669.9 4564.3 8665.6 4571.47 8662.73 4570.04 C 8646.97 4574.33 8628.34 4572.9 8615.44 4581.5 C 8609.7 4584.37 8612.57 4594.4 8606.84 4597.27 C 8598.24 4601.57 8586.77 4597.27 8576.74 4600.13 C 8571.01 4601.57 8560.98 4615.9 8553.81 4656.03 C 8552.38 4666.06 8549.51 4693.29 8549.51 4704.75 C 8549.51 4720.52 8606.84 4759.21 8571.01 4766.38 C 8533.75 4774.98 8550.95 4739.15 8517.98 4736.28 C 8452.06 4731.98 8530.88 4747.75 8483.59 4756.35 C 8476.42 4757.78 8469.25 4747.75 8460.66 4749.18 C 8437.73 4754.91 8450.62 4779.28 8430.56 4785.01 C 8419.09 4787.88 8407.63 4747.75 8396.16 4742.02 C 8384.7 4736.28 8371.8 4756.35 8363.2 4767.81 C 8357.47 4774.98 8368.93 4796.48 8360.33 4795.04 C 8330.24 4787.88 8374.67 4796.48 8337.4 4770.68 C 8317.34 4756.35 8324.51 4785.01 8311.61 4785.01 C 8275.78 4785.01 8307.31 4772.11 8294.41 4763.51 C 8285.81 4759.21 8285.81 4780.71 8280.08 4786.44 C 8271.48 4796.48 8258.58 4783.58 8251.41 4785.01 C 8244.25 4785.01 8251.41 4810.81 8244.25 4815.11 C 8242.81 4817.97 8238.52 4813.67 8235.65 4810.81 C 8217.02 4800.78 8268.61 4776.41 8211.28 4792.18 C 8194.09 4796.48 8219.88 4819.41 8184.05 4805.07 C 8179.76 4803.64 8188.35 4795.04 8185.49 4792.18 C 8182.62 4789.31 8176.89 4796.48 8174.02 4795.04 C 8165.42 4787.88 8176.89 4773.55 8161.12 4777.84 C 8128.16 4786.44 8083.73 4790.74 8047.9 4786.44 C 8043.6 4785.01 8042.17 4779.28 8037.87 4779.28 C 8017.81 4777.84 7997.74 4790.74 7976.25 4793.61 C 7969.08 4793.61 7961.91 4793.61 7954.75 4793.61 C 7947.58 4793.61 7940.42 4790.74 7933.25 4792.18 C 7914.62 4797.91 7901.72 4812.24 7884.52 4819.41 C 7873.06 4823.71 7858.73 4820.84 7847.26 4826.57 C 7841.53 4830.87 7821.46 4898.23 7807.13 4893.93 C 7802.83 4892.5 7802.83 4885.33 7799.97 4885.33 C 7791.37 4883.9 7781.34 4883.9 7774.17 4886.77 C 7751.24 4893.93 7785.63 4906.83 7768.44 4911.13 L 7767 4912.56 C 7767 4912.56 7767 4911.13 7767 4911.13 C 7771.3 4916.86 7777.04 4921.16 7779.9 4926.89 C 7782.77 4935.49 7777.04 4945.52 7779.9 4952.69 C 7792.8 4979.92 7834.36 5007.15 7857.29 5024.35 C 7861.59 5028.65 7828.63 5027.22 7835.8 5041.55 C 7837.23 5042.98 7865.89 5068.78 7865.89 5080.24 C 7861.59 5131.84 7845.83 5100.31 7820.03 5121.8 C 7808.57 5131.84 7721.14 5293.78 7725.44 5305.25 C 7728.31 5321.01 7742.64 5332.48 7746.94 5346.81 C 7748.37 5353.98 7736.91 5364.01 7742.64 5369.74 C 7767 5399.84 7752.67 5342.51 7769.87 5396.97 C 7771.3 5399.84 7765.57 5392.67 7762.7 5391.24 C 7755.54 5389.81 7748.37 5392.67 7741.21 5392.67 C 7736.91 5391.24 7731.17 5391.24 7726.88 5389.81 C 7728.31 5404.14 7718.28 5424.2 7716.84 5438.53 C 7715.41 5448.57 7716.84 5458.6 7712.54 5467.2 C 7708.24 5477.23 7695.35 5480.1 7693.91 5490.13 C 7691.05 5500.16 7698.21 5510.19 7698.21 5521.66 C 7731.17 5523.09 7709.68 5538.86 7732.61 5547.45 C 7736.91 5548.89 7742.64 5541.72 7746.94 5544.59 C 7755.54 5550.32 7756.97 5564.65 7765.57 5570.39 C 7771.3 5571.82 7775.6 5564.65 7781.34 5563.22 C 7785.63 5563.22 7789.93 5566.09 7792.8 5567.52 C 7811.43 5580.42 7788.5 5577.55 7805.7 5591.88 C 7810 5594.75 7818.6 5589.02 7828.63 5561.79 C 7830.06 5558.92 7830.06 5554.62 7832.93 5554.62 C 7837.23 5554.62 7873.06 5571.82 7877.36 5571.82 C 7880.22 5571.82 7881.66 5564.65 7884.52 5563.22 C 7903.15 5556.05 7950.45 5574.68 7973.38 5568.95 C 7986.28 5564.65 7994.88 5553.19 8007.78 5551.75 C 8030.71 5550.32 8046.47 5580.42 8062.24 5584.72 C 8083.73 5591.88 8090.9 5551.75 8096.63 5546.02 C 8122.43 5518.79 8188.35 5521.66 8208.42 5501.59 C 8247.11 5464.33 8191.22 5495.86 8218.45 5460.03 C 8225.62 5451.43 8237.08 5444.27 8248.55 5441.4 L 8249.98 5437.1 L 8255.71 5424.2 C 8255.71 5422.77 8255.71 5421.34 8255.71 5421.34 L 8257.15 5415.6 L 8271.48 5418.47 C 8270.04 5415.6 8267.18 5412.74 8267.18 5409.87 C 8267.18 5408.44 8321.64 5401.27 8324.51 5399.84 C 8351.74 5386.94 8343.14 5365.44 8381.83 5368.31 C 8397.6 5369.74 8414.79 5375.47 8427.69 5385.51 C 8431.99 5388.37 8427.69 5398.41 8431.99 5398.41 C 8442.02 5398.41 8452.06 5388.37 8463.52 5386.94 C 8469.25 5385.51 8462.09 5399.84 8463.52 5405.57 C 8466.39 5417.04 8473.55 5395.54 8476.42 5398.41 C 8480.72 5404.14 8480.72 5411.3 8485.02 5415.6 C 8490.75 5421.34 8485.02 5396.97 8493.62 5394.11 C 8542.35 5381.21 8520.85 5409.87 8555.24 5424.2 C 8569.58 5429.93 8559.54 5401.27 8572.44 5408.44 C 8605.41 5429.93 8582.47 5460.03 8592.51 5491.56 C 8593.94 5498.73 8629.77 5524.52 8638.37 5527.39 C 8655.57 5533.12 8634.07 5501.59 8654.13 5513.06 C 8679.93 5527.39 8646.97 5567.52 8665.6 5556.05 C 8672.76 5551.75 8668.46 5574.68 8674.2 5580.42 C 8682.8 5586.15 8718.63 5587.58 8727.22 5594.75 L 8741.56 5639.18 C 8741.56 5642.04 8732.96 5642.04 8734.39 5644.91 C 8737.26 5654.94 8770.22 5659.24 8770.22 5660.67 C 8761.62 5666.41 8751.59 5667.84 8744.42 5673.57 C 8741.56 5676.44 8742.99 5679.31 8741.56 5682.17 C 8734.39 5689.34 8727.22 5662.11 8715.76 5662.11 C 8707.16 5662.11 8715.76 5689.34 8707.16 5687.9 C 8701.43 5686.47 8682.8 5672.14 8682.8 5672.14 C 8679.93 5676.44 8681.36 5685.04 8675.63 5686.47 C 8672.76 5687.9 8671.33 5676.44 8669.9 5679.31 C 8662.73 5685.04 8664.17 5695.07 8659.87 5700.8 C 8658.43 5702.24 8655.57 5700.8 8654.13 5699.37 C 8654.13 5696.5 8657 5695.07 8657 5692.2 C 8655.57 5685.04 8652.7 5677.87 8649.83 5672.14 C 8644.1 5662.11 8631.2 5683.61 8624.04 5692.2 C 8603.97 5715.13 8639.8 5726.6 8642.67 5738.07 C 8644.1 5745.23 8636.94 5752.4 8636.94 5759.56 C 8636.94 5766.73 8645.53 5772.46 8642.67 5778.19 C 8639.8 5783.93 8629.77 5783.93 8624.04 5789.66 C 8611.14 5799.69 8631.2 5805.42 8624.04 5815.46 C 8618.3 5822.62 8609.7 5826.92 8605.41 5832.65 C 8592.51 5852.72 8614 5884.25 8603.97 5885.68 C 8596.81 5887.11 8589.64 5884.25 8582.47 5885.68 L 8583.91 5885.68 L 8576.74 5895.71 C 8576.74 5900.01 8575.31 5904.31 8572.44 5902.88 C 8575.31 5904.31 8579.61 5901.45 8582.47 5904.31 C 8586.77 5908.61 8585.34 5917.21 8591.07 5921.51 C 8596.81 5927.24 8658.43 5930.11 8657 5922.94 C 8657 5921.51 8636.94 5917.21 8651.27 5908.61 C 8659.87 5904.31 8669.9 5905.75 8678.5 5901.45 C 8692.83 5894.28 8698.56 5874.22 8714.33 5868.48 C 8745.86 5857.02 8761.62 5868.48 8768.79 5895.71 C 8771.65 5887.11 8783.12 5854.15 8765.92 5848.42 C 8763.05 5846.99 8767.35 5854.15 8764.49 5854.15 C 8763.05 5855.59 8763.05 5849.85 8761.62 5848.42 C 8753.02 5845.55 8758.75 5871.35 8748.72 5852.72 C 8740.12 5839.82 8741.56 5819.76 8740.12 5805.42 C 8740.12 5799.69 8730.09 5785.36 8735.82 5788.23 C 8748.72 5792.53 8745.86 5825.49 8758.75 5821.19 C 8801.75 5803.99 8773.09 5799.69 8773.09 5781.06 C 8773.09 5778.19 8775.95 5786.79 8778.82 5785.36 C 8783.12 5783.93 8771.65 5772.46 8775.95 5769.6 C 8783.12 5766.73 8791.72 5772.46 8798.88 5769.6 C 8804.62 5768.16 8808.92 5762.43 8811.78 5756.7 C 8823.25 5730.9 8836.15 5699.37 8844.74 5670.71 C 8846.18 5664.97 8834.71 5659.24 8837.58 5653.51 C 8839.01 5649.21 8847.61 5647.78 8850.48 5643.48 C 8860.51 5624.85 8831.85 5627.71 8843.31 5611.95 C 8874.84 5571.82 8917.84 5589.02 8917.84 5576.12 C 8919.27 5564.65 8903.5 5548.89 8913.54 5541.72 C 8920.7 5535.99 8916.4 5564.65 8926.43 5570.39 C 8930.73 5574.68 8936.47 5563.22 8943.63 5561.79 C 8950.8 5560.35 8959.4 5561.79 8968 5560.35 C 8986.63 5557.49 8963.7 5525.96 8963.7 5525.96 C 8962.26 5508.76 8976.6 5511.63 8968 5497.29 C 8972.3 5497.29 8976.6 5495.86 8976.6 5498.73 C 8978.03 5581.85 9025.32 5554.62 9069.75 5524.52 C 9075.48 5520.22 9056.85 5534.56 9051.12 5540.29 C 9043.95 5548.89 9053.99 5564.65 9041.09 5566.09 C 9016.72 5570.39 8989.49 5571.82 8965.13 5574.68 C 8963.7 5574.68 8927.87 5574.68 8932.17 5578.98 C 8940.77 5586.15 8949.37 5596.18 8960.83 5599.05 C 8965.13 5600.48 8957.96 5587.58 8962.26 5586.15 C 8970.86 5581.85 8980.9 5587.58 8990.93 5586.15 C 8993.79 5586.15 9005.26 5578.98 9006.69 5587.58 C 9009.56 5601.91 8973.73 5611.95 8983.76 5621.98 C 8988.06 5627.71 9038.22 5616.25 9045.39 5617.68 C 9049.69 5619.11 9045.39 5629.15 9049.69 5630.58 C 9059.72 5634.88 9072.62 5636.31 9084.08 5632.01 C 9095.55 5629.15 9101.28 5614.81 9111.31 5610.51 C 9121.35 5604.78 9134.24 5607.65 9142.84 5603.35 C 9145.71 5601.91 9139.98 5594.75 9142.84 5593.32 C 9145.71 5590.45 9151.44 5597.62 9154.31 5596.18 C 9161.47 5593.32 9157.17 5573.25 9164.34 5576.12 C 9177.24 5578.98 9188.7 5606.21 9198.74 5597.62 C 9205.9 5590.45 9204.47 5577.55 9213.07 5571.82 C 9237.43 5557.49 9211.63 5596.18 9227.4 5610.51 C 9233.13 5614.81 9247.46 5597.62 9246.03 5604.78 C 9246.03 5609.08 9201.6 5654.94 9193 5659.24 C 9188.7 5660.67 9184.4 5653.51 9180.1 5656.38 C 9178.67 5659.24 9187.27 5662.11 9185.84 5664.97 C 9185.84 5664.97 9155.74 5692.2 9154.31 5692.2 C 9151.44 5695.07 9148.58 5699.37 9147.14 5703.67 C 9147.14 5706.54 9151.44 5707.97 9151.44 5709.4 C 9150.01 5710.84 9098.41 5746.66 9117.05 5753.83 C 9152.87 5768.16 9145.71 5743.8 9175.81 5743.8 C 9185.84 5745.23 9217.37 5771.03 9227.4 5766.73 C 9240.3 5762.43 9250.33 5746.66 9267.53 5763.86 C 9306.22 5803.99 9273.26 5798.26 9280.43 5821.19 C 9287.59 5841.25 9297.62 5848.42 9296.19 5849.85 C 9290.46 5855.59 9270.39 5855.59 9273.26 5864.18 C 9277.56 5874.22 9296.19 5868.48 9306.22 5872.78 C 9316.26 5875.65 9316.26 5885.68 9329.15 5885.68 C 9400.81 5877.08 9386.48 5793.96 9432.34 5762.43 C 9440.94 5755.26 9449.54 5749.53 9461.01 5748.1 C 9466.74 5748.1 9473.9 5755.26 9478.2 5752.4 C 9488.24 5742.37 9479.64 5722.3 9489.67 5713.7 C 9499.7 5706.54 9505.43 5709.4 9508.3 5696.5 C 9509.73 5695.07 9505.43 5696.5 9504 5695.07 C 9496.83 5687.9 9508.3 5673.57 9512.6 5672.14 C 9519.77 5667.84 9529.8 5667.84 9538.4 5669.27 C 9547 5670.71 9552.73 5682.17 9561.33 5680.74 C 9571.36 5679.31 9574.23 5666.41 9582.82 5662.11 C 9589.99 5659.24 9598.59 5662.11 9605.76 5659.24 C 9634.42 5649.21 9611.49 5621.98 9614.35 5599.05 C 9617.22 5587.58 9634.42 5576.12 9627.25 5567.52 C 9627.25 5567.52 9568.49 5578.98 9567.06 5580.42 C 9558.46 5586.15 9565.63 5603.35 9557.03 5607.65 C 9551.29 5610.51 9544.13 5607.65 9538.4 5604.78 C 9534.1 5603.35 9535.53 5594.75 9531.23 5594.75 C 9521.2 5594.75 9529.8 5616.25 9524.06 5624.85 C 9516.9 5632.01 9509.73 5640.61 9499.7 5643.48 C 9475.34 5650.64 9395.08 5573.25 9382.18 5556.05 C 9377.88 5551.75 9357.82 5515.92 9360.68 5513.06 C 9369.28 5507.33 9377.88 5528.82 9387.91 5527.39 C 9420.88 5521.66 9412.28 5455.73 9425.18 5435.67 C 9429.48 5429.93 9438.07 5428.5 9440.94 5422.77 C 9448.11 5412.74 9448.11 5399.84 9455.27 5391.24 C 9476.77 5362.58 9488.24 5365.44 9506.87 5374.04 C 9508.3 5375.47 9508.3 5371.18 9509.73 5369.74 C 9522.63 5355.41 9506.87 5328.18 9541.26 5326.75 C 9552.73 5325.31 9559.89 5348.24 9568.49 5342.51 C 9578.53 5336.78 9565.63 5319.58 9569.93 5308.12 C 9569.93 5305.25 9602.89 5267.99 9604.32 5267.99 C 9610.05 5265.12 9617.22 5279.45 9620.09 5275.15 C 9622.95 5270.85 9624.39 5232.16 9632.99 5224.99 C 9644.45 5216.39 9680.28 5202.06 9694.61 5199.2 C 9698.91 5199.2 9700.34 5213.53 9703.21 5209.23 C 9707.51 5203.49 9706.08 5193.46 9710.38 5187.73 " }
+ }
+ }
+ // PATH visit ch count: 232
+ DemoShape {
+ objectName: "ch"
+ ShapePath {
+ objectName: "svg_path:ch"
+ // boundingRect: 5655.76, 5764.54 593.483x374.682
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6244.98 5941.58 C 6243.55 5947.31 6244.98 5954.47 6242.12 5960.21 C 6240.68 5965.94 6236.38 5970.24 6233.52 5975.97 C 6233.52 5978.84 6260.75 5998.9 6243.55 6004.63 C 6213.45 6011.8 6223.48 5970.24 6197.69 5988.87 C 6177.62 6003.2 6203.42 6018.97 6206.29 6026.13 C 6207.72 6031.86 6199.12 6036.16 6199.12 6041.9 C 6200.55 6047.63 6206.29 6050.5 6209.15 6054.8 C 6210.59 6060.53 6199.12 6066.26 6193.39 6063.39 C 6183.36 6057.66 6189.09 6034.73 6177.62 6033.3 C 6138.93 6026.13 6157.56 6054.8 6134.63 6047.63 C 6097.37 6039.03 6120.3 6010.37 6101.67 6007.5 C 6077.3 6003.2 6093.07 6041.9 6093.07 6044.76 C 6087.33 6070.56 6052.94 6079.16 6052.94 6110.69 C 6052.94 6125.02 6081.6 6119.29 6057.24 6137.92 C 6048.64 6145.08 6045.77 6120.72 6040.04 6113.56 C 6035.74 6109.26 6025.71 6112.12 6024.27 6106.39 C 6022.84 6100.66 6032.87 6096.36 6034.31 6090.62 C 6034.31 6087.76 6031.44 6084.89 6030.01 6084.89 C 6014.24 6079.16 5991.31 6074.86 5981.28 6056.23 C 5979.85 6050.5 5984.15 6044.76 5984.15 6040.46 C 5984.15 6031.86 5985.58 6023.27 5982.71 6014.67 C 5981.28 6013.23 5978.41 6016.1 5975.55 6017.53 C 5968.38 6023.27 5962.65 6029 5955.48 6034.73 C 5948.32 6040.46 5936.85 6043.33 5933.98 6051.93 C 5932.55 6057.66 5945.45 6060.53 5944.02 6067.69 C 5944.02 6074.86 5903.89 6116.42 5902.46 6116.42 C 5889.56 6116.42 5879.52 6099.22 5866.63 6102.09 C 5847.99 6106.39 5809.3 6135.05 5794.97 6109.26 C 5789.23 6100.66 5789.23 6090.62 5782.07 6083.46 C 5780.64 6080.59 5774.9 6089.19 5773.47 6086.32 C 5772.04 6082.03 5779.2 6074.86 5776.34 6070.56 C 5774.9 6066.26 5766.3 6070.56 5763.44 6067.69 C 5754.84 6053.36 5774.9 6054.8 5773.47 6046.2 C 5772.04 6039.03 5763.44 6036.16 5763.44 6029 C 5762 6024.7 5773.47 6016.1 5767.74 6014.67 C 5749.11 6006.07 5708.98 6006.07 5696.08 6026.13 C 5693.21 6029 5694.65 6031.86 5694.65 6036.16 C 5696.08 6039.03 5703.24 6041.9 5701.81 6044.76 C 5693.21 6061.96 5673.15 6069.13 5655.95 6064.83 C 5654.52 6064.83 5661.68 6061.96 5661.68 6059.09 C 5661.68 6040.46 5676.01 6047.63 5677.45 6043.33 C 5684.61 6020.4 5665.98 6000.33 5683.18 5978.84 C 5693.21 5964.51 5719.01 5964.51 5724.74 5948.74 C 5729.04 5940.14 5720.44 5927.24 5724.74 5917.21 C 5730.48 5905.75 5750.54 5910.05 5759.14 5900.01 C 5769.17 5891.41 5780.64 5862.75 5793.53 5852.72 C 5800.7 5846.99 5807.87 5862.75 5809.3 5842.69 C 5809.3 5835.52 5787.8 5849.85 5786.37 5842.69 C 5783.5 5835.52 5794.97 5829.79 5799.27 5824.06 C 5799.27 5824.06 5802.13 5825.49 5802.13 5824.06 C 5802.13 5821.19 5796.4 5815.46 5799.27 5814.02 C 5839.4 5802.56 5822.2 5857.02 5870.93 5806.86 C 5872.36 5806.86 5875.22 5803.99 5876.66 5801.12 L 5879.52 5798.26 C 5882.39 5796.83 5885.26 5796.83 5888.12 5799.69 C 5889.56 5801.12 5879.52 5802.56 5880.96 5805.42 C 5885.26 5814.02 5951.18 5798.26 5955.48 5798.26 C 5984.15 5796.83 5958.35 5824.06 5994.18 5805.42 C 5995.61 5803.99 5991.31 5796.83 5994.18 5795.39 C 5998.48 5792.53 6014.24 5791.09 6008.51 5789.66 C 5999.91 5788.23 5991.31 5791.09 5982.71 5789.66 C 5978.41 5788.23 6002.78 5749.53 6024.27 5771.03 C 6027.14 5773.89 6019.97 5779.63 6021.41 5783.93 C 6022.84 5786.79 6028.57 5788.23 6031.44 5786.79 C 6034.31 5785.36 6030.01 5775.33 6031.44 5778.19 C 6037.17 5781.06 6037.17 5791.09 6042.91 5793.96 C 6042.91 5793.96 6090.2 5791.09 6091.63 5791.09 C 6105.96 5793.96 6116 5808.29 6128.9 5815.46 L 6128.9 5816.89 C 6128.9 5826.92 6147.53 5829.79 6141.79 5844.12 C 6140.36 5846.99 6134.63 5845.55 6133.19 5846.99 C 6128.9 5854.15 6114.56 5905.75 6117.43 5907.18 C 6120.3 5908.61 6167.59 5912.91 6169.02 5912.91 C 6171.89 5915.78 6164.72 5920.08 6167.59 5922.94 C 6174.76 5931.54 6183.36 5937.28 6193.39 5941.58 C 6216.32 5953.04 6220.62 5914.34 6236.38 5917.21 C 6240.68 5917.21 6243.55 5918.64 6246.41 5920.08 L 6244.98 5932.98 C 6243.55 5937.28 6242.12 5940.14 6244.98 5941.58 " }
+ }
+ }
+ // PATH visit md count: 155
+ DemoShape {
+ objectName: "md"
+ ShapePath {
+ objectName: "svg_path:md"
+ // boundingRect: 8254.28, 5368.05 515.94x534.916
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 8254.28 5435.67 C 8274.34 5425.64 8287.24 5434.23 8303.01 5447.13 C 8333.1 5468.63 8338.84 5504.46 8363.2 5528.82 C 8403.33 5567.52 8512.25 5636.31 8525.15 5692.2 C 8535.18 5728.03 8522.28 5768.16 8530.88 5803.99 C 8533.75 5819.76 8559.54 5855.59 8558.11 5871.35 C 8558.11 5877.08 8546.65 5878.52 8548.08 5884.25 C 8549.51 5889.98 8558.11 5887.11 8562.41 5889.98 C 8566.71 5892.85 8566.71 5902.88 8572.44 5902.88 C 8579.61 5904.31 8573.88 5887.11 8579.61 5885.68 C 8588.21 5884.25 8596.81 5887.11 8603.97 5885.68 C 8614 5884.25 8592.51 5852.72 8605.41 5832.65 C 8609.71 5826.92 8618.3 5822.62 8624.04 5815.46 C 8631.2 5805.42 8611.14 5799.69 8624.04 5789.66 C 8629.77 5783.93 8639.8 5783.93 8642.67 5778.19 C 8645.53 5772.46 8636.94 5766.73 8636.94 5759.56 C 8636.94 5752.4 8644.1 5745.23 8642.67 5738.07 C 8639.8 5726.6 8603.97 5715.13 8624.04 5692.2 C 8631.2 5683.61 8644.1 5662.11 8649.83 5672.14 C 8652.7 5677.87 8655.57 5685.04 8657 5692.2 C 8657 5695.07 8654.13 5696.5 8654.13 5699.37 C 8655.57 5700.8 8658.43 5702.24 8659.87 5700.8 C 8664.17 5695.07 8662.73 5685.04 8669.9 5679.31 C 8671.33 5676.44 8672.76 5687.9 8675.63 5686.47 C 8681.36 5685.04 8679.93 5676.44 8682.8 5672.14 C 8682.8 5672.14 8701.43 5686.47 8707.16 5687.9 C 8715.76 5689.34 8707.16 5662.11 8715.76 5662.11 C 8727.22 5662.11 8734.39 5689.34 8741.56 5682.17 C 8742.99 5679.31 8741.56 5676.44 8744.42 5673.57 C 8751.59 5667.84 8761.62 5666.41 8770.22 5660.67 C 8770.22 5659.24 8737.26 5654.94 8734.39 5644.91 C 8732.96 5642.04 8741.56 5642.04 8741.56 5639.18 L 8727.22 5594.75 C 8718.63 5587.58 8682.8 5586.15 8674.2 5580.42 C 8668.46 5574.68 8672.76 5551.75 8665.6 5556.05 C 8646.97 5567.52 8679.93 5527.39 8654.13 5513.06 C 8634.07 5501.59 8655.57 5533.12 8638.37 5527.39 C 8629.77 5524.52 8593.94 5498.73 8592.51 5491.56 C 8582.47 5460.03 8605.41 5429.93 8572.44 5408.44 C 8559.54 5401.27 8569.58 5429.93 8555.24 5424.2 C 8520.85 5409.87 8542.35 5381.21 8493.62 5394.11 C 8485.02 5396.97 8490.75 5421.34 8485.02 5415.6 C 8480.72 5411.3 8480.72 5404.14 8476.42 5398.41 C 8473.55 5395.54 8466.39 5417.04 8463.52 5405.57 C 8462.09 5399.84 8469.25 5385.51 8463.52 5386.94 C 8452.06 5388.37 8442.02 5398.41 8431.99 5398.41 C 8427.69 5398.41 8431.99 5388.37 8427.69 5385.51 C 8414.79 5375.47 8397.6 5369.74 8381.83 5368.31 C 8343.14 5365.44 8351.74 5386.94 8324.51 5399.84 C 8321.64 5401.27 8267.18 5408.44 8267.18 5409.87 C 8267.18 5415.6 8274.34 5418.47 8274.34 5424.2 C 8275.78 5429.93 8262.88 5415.6 8258.58 5418.47 C 8252.85 5421.34 8255.71 5429.93 8254.28 5435.67 " }
+ }
+ }
+ // PATH visit ad count: 7
+ DemoShape {
+ objectName: "ad"
+ ShapePath {
+ objectName: "svg_path:ad"
+ // boundingRect: 4995.66, 6660.73 46.2837x38.6574
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 5033.96 6695.42 C 4969.46 6714.05 5005.29 6661.02 5008.16 6661.02 C 5045.42 6658.16 5048.29 6676.79 5033.96 6695.42 " }
+ }
+ }
+ // PATH visit mc count: 16
+ DemoShape {
+ objectName: "mc"
+ ShapePath {
+ objectName: "svg_path:mc"
+ // boundingRect: 5753.41, 6504.81 42.995x38.6956
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 5774.9 6543.5 C 5786.37 6543.5 5796.4 6533.47 5796.4 6523.44 C 5796.4 6513.41 5786.37 6504.81 5774.9 6504.81 C 5763.44 6504.81 5753.41 6513.41 5753.41 6523.44 C 5753.41 6533.47 5763.44 6543.5 5774.9 6543.5 C 5786.37 6543.5 5763.44 6543.5 5774.9 6543.5 " }
+ }
+ }
+ // PATH visit li count: 9
+ DemoShape {
+ objectName: "li"
+ ShapePath {
+ objectName: "svg_path:li"
+ // boundingRect: 6117.15, 5875.65 13.1832x32.9627
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 6123.16 5875.65 L 6130.33 5891.41 L 6130.33 5908.61 C 6123.16 5908.61 6118.86 5907.18 6117.43 5907.18 C 6116 5905.75 6120.3 5891.41 6123.16 5875.65 " }
+ }
+ }
+ // PATH visit ru-kgd count: 71
+ DemoShape {
+ objectName: "ru-kgd"
+ ShapePath {
+ objectName: "svg_path:ru-kgd"
+ // boundingRect: 7236.73, 4257.61 342.527x184.111
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 7579.26 4400.92 C 7547.73 4357.93 7577.83 4319.23 7569.23 4304.9 C 7564.93 4299.17 7559.2 4294.87 7553.46 4292 C 7550.6 4290.57 7546.3 4294.87 7544.87 4293.44 C 7539.13 4289.14 7542 4277.67 7536.27 4276.24 C 7516.2 4270.51 7496.14 4289.14 7474.64 4289.14 C 7471.77 4289.14 7476.07 4284.84 7473.21 4283.4 C 7454.58 4277.67 7425.91 4279.1 7408.71 4269.07 C 7404.41 4266.21 7404.41 4259.04 7400.12 4257.61 C 7395.82 4257.61 7394.38 4263.34 7390.08 4264.77 C 7391.52 4269.07 7391.52 4271.94 7390.08 4274.8 C 7388.65 4277.67 7381.48 4273.37 7381.48 4276.24 C 7380.05 4292 7400.12 4306.33 7395.82 4322.1 C 7391.52 4340.73 7315.56 4339.3 7318.43 4330.7 C 7319.86 4327.83 7321.29 4324.97 7322.72 4322.1 C 7306.96 4339.3 7289.76 4336.43 7266.83 4342.16 C 7263.96 4342.16 7259.67 4340.73 7259.67 4343.6 C 7258.23 4355.06 7263.96 4366.53 7263.96 4377.99 C 7263.96 4398.06 7253.93 4419.55 7236.73 4436.75 C 7318.43 4448.22 7511.9 4441.05 7577.83 4402.36 C 7577.83 4402.36 7577.83 4402.36 7579.26 4400.92 M 7357.12 4263.34 C 7357.12 4263.34 7355.69 4263.34 7355.69 4263.34 C 7354.25 4267.64 7351.39 4273.37 7349.95 4277.67 C 7352.82 4273.37 7355.69 4267.64 7357.12 4263.34 " }
+ }
+ }
+ // PATH visit im count: 10
+ DemoShape {
+ objectName: "im"
+ ShapePath {
+ objectName: "svg_path:im"
+ // boundingRect: 4575.97, 4351.37 54.7176x60.3339
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 4630.62 4351.37 C 4623.46 4352.8 4616.29 4352.8 4609.13 4357.1 C 4603.39 4361.4 4601.96 4371.43 4596.23 4377.16 C 4534.6 4433.06 4633.49 4418.73 4630.62 4351.37 " }
+ }
+ }
+ // PATH visit fo count: 26
+ DemoShape {
+ objectName: "fo"
+ ShapePath {
+ objectName: "svg_path:fo"
+ // boundingRect: 4732.49, 2985.47 44.4281x176.62
+ strokeColor: "#aa805d"
+ strokeWidth: 4.58365
+ fillColor: "#fffff7df"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 4736.79 3126.26 C 4735.36 3139.16 4732.49 3162.09 4732.49 3162.09 C 4732.49 3162.09 4735.36 3139.16 4736.79 3126.26 M 4758.29 2985.81 C 4741.09 3021.64 4765.45 2977.21 4769.75 2998.71 C 4769.75 3001.58 4762.59 3001.58 4762.59 3003.01 C 4765.45 3008.74 4776.92 3007.31 4776.92 3013.04 C 4776.92 3023.07 4774.05 3040.27 4764.02 3040.27 C 4755.42 3038.84 4765.45 3021.64 4764.02 3013.04 C 4759.72 2998.71 4716.72 2982.95 4758.29 2985.81 " }
+ }
+ }
+ } // END countries type: G 1
+ } // END Europakarte type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/Gear.qml b/examples/quick/quickshapes/weatherforecast/Gear.qml
new file mode 100644
index 0000000000..ef6db2b090
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/Gear.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+Gear_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/Gear_generated.qml b/examples/quick/quickshapes/weatherforecast/Gear_generated.qml
new file mode 100644
index 0000000000..473f7235e0
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/Gear_generated.qml
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 Framework7
+// SPDX-License-Identifier: MIT
+// Generated from: assets/gear-alt-stroke.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 56x56
+ // size 56x56
+ implicitWidth: 56
+ implicitHeight: 56
+ // START svg4 type: DOC 0
+ Item { // structure node
+ transform: [
+ Scale { xScale: width / 56; yScale: height / 56 }
+ ]
+ objectName: "svg4"
+ // skipping DEFS "defs8"
+ // PATH visit path2 count: 78
+ DemoShape {
+ objectName: "path2"
+ // Translate 4, 4
+ x: 4
+ y: 4
+ ShapePath {
+ objectName: "svg_path:path2"
+ // boundingRect: 0.682945, 0.682945 46.7504x46.7504
+ strokeColor: "#333333"
+ strokeWidth: 3
+ fillColor: "transparent"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 18.7918 47.4333 C 16.3367 46.8901 14.0229 45.9709 11.9139 44.7392 L 12.0378 42.3144 C 12.2067 39.0073 9.65397 36.4638 6.35565 36.6322 L 3.67513 36.7691 C 2.41991 34.7754 1.44994 32.584 0.822479 30.2522 L 2.92622 28.3529 C 5.38411 26.1339 5.37761 22.5303 2.92622 20.3171 L 0.682945 18.2919 C 1.23975 16.0095 2.12294 13.855 3.28025 11.8807 L 6.35565 12.0378 C 9.66271 12.2067 12.2062 9.65397 12.0378 6.35565 L 11.8807 3.28025 C 13.855 2.12294 16.0095 1.23975 18.2919 0.682945 L 20.3171 2.92622 C 22.5361 5.38411 26.1397 5.37761 28.3529 2.92622 L 30.2522 0.822479 C 32.584 1.44994 34.7754 2.41991 36.7691 3.67513 L 36.6322 6.35565 C 36.4633 9.66271 39.016 12.2062 42.3144 12.0378 L 44.7392 11.9139 C 45.9709 14.0229 46.8901 16.3367 47.4333 18.7918 L 45.7438 20.3171 C 43.2859 22.5361 43.2924 26.1397 45.7438 28.3529 L 47.304 29.7614 C 46.6874 32.2638 45.6783 34.6117 44.3457 36.736 L 42.3144 36.6322 C 39.0073 36.4633 36.4638 39.016 36.6322 42.3144 L 36.736 44.3457 C 34.6117 45.6783 32.2638 46.6874 29.7614 47.304 L 28.3529 45.7438 C 26.1339 43.2859 22.5303 43.2924 20.3171 45.7438 L 18.7918 47.4333 M 24 34 C 29.5228 34 34 29.5228 34 24 C 34 18.4772 29.5228 14 24 14 C 18.4772 14 14 18.4772 14 24 C 14 29.5228 18.4772 34 24 34 " }
+ }
+ }
+ } // END svg4 type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/MapLabel.qml b/examples/quick/quickshapes/weatherforecast/MapLabel.qml
new file mode 100644
index 0000000000..66ba91ecde
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/MapLabel.qml
@@ -0,0 +1,65 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: mapLabel
+ property url iconSource
+ property string label
+ property int degrees
+
+ radius: 8 * mainWindow.designWindowWidthRatio
+ color: Qt.rgba(0, 0, 0, 0.05)
+ height: 42 * mainWindow.designWindowHeightRatio
+ width: labelText.width + divider.width + labelDegrees.width + iconLoader.width + row.padding * 5
+
+ x: -height / 2
+ y: -height / 2
+
+ Row {
+ id: row
+ anchors.fill: parent
+ padding: 8 * mainWindow.designWindowWidthRatio
+ spacing: padding
+
+ Text {
+ id: labelText
+ text: mapLabel.label
+ color: "#333"
+ font.family: workSansRegular.font.family
+ font.pixelSize: 22 * mainWindow.designWindowHeightRatio
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Rectangle {
+ id: divider
+ color: "#333"
+ width: 2 * mainWindow.designWindowWidthRatio
+ antialiasing: true
+ height: labelText.implicitHeight
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Text {
+ id: labelDegrees
+ text: degrees+"°"
+ color: "#333"
+ font.family: workSansRegular.font.family
+ font.pixelSize: 22 * mainWindow.designWindowHeightRatio
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Loader {
+ id: iconLoader
+ source: mapLabel.iconSource
+
+ // TODO: implement PreserveAspectRatio in Shapes
+ property real aspectRatio: implicitWidth / implicitHeight
+ width: 25 * mainWindow.designWindowWidthRatio
+ height: width / aspectRatio
+
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/weatherforecast/SettingsDrawer.qml b/examples/quick/quickshapes/weatherforecast/SettingsDrawer.qml
new file mode 100644
index 0000000000..deb6b02b95
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/SettingsDrawer.qml
@@ -0,0 +1,117 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+Page {
+ id: settingsDrawer
+ default property alias content: contentLayout.children
+ required property bool isLandscape
+ readonly property bool isMobile: Qt.platform.os === "android" || Qt.platform.os === "ios"
+ property bool isOpen: false
+
+ title: "Settings"
+
+ onIsLandscapeChanged: updateState();
+ Component.onCompleted: updateState();
+ onIsOpenChanged: updateState();
+ x: 0
+ y: 0
+
+ function updateState() {
+ if (isOpen)
+ if (isLandscape)
+ settingsDrawer.state = "Landscape_Visible"
+ else
+ settingsDrawer.state = "Portrait_Visible"
+ else
+ if (isLandscape)
+ settingsDrawer.state = "Landscape_Hidden"
+ else
+ settingsDrawer.state = "Portrait_Hidden"
+ }
+
+ states: [
+ State {
+ name: "Landscape_Visible"
+ PropertyChanges {
+ settingsDrawer.x: 0
+ }
+ },
+ State {
+ name: "Landscape_Hidden"
+ PropertyChanges {
+ settingsDrawer.x: -settingsDrawer.width
+ }
+ },
+ State {
+ name: "Portrait_Visible"
+ PropertyChanges {
+ settingsDrawer.y: 0
+ }
+ },
+ State {
+ name: "Portrait_Hidden"
+ PropertyChanges {
+ settingsDrawer.y: -settingsDrawer.height
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "Landscape_Visible"
+ to: "Landscape_Hidden"
+ NumberAnimation {
+ duration: 400
+ property: "x"
+ easing.type: Easing.InOutQuad
+ }
+ },
+ Transition {
+ from: "Landscape_Hidden"
+ to: "Landscape_Visible"
+ NumberAnimation {
+ duration: 400
+ property: "x"
+ easing.type: Easing.InOutQuad
+ }
+ },
+ Transition {
+ from: "Portrait_Visible"
+ to: "Portrait_Hidden"
+ NumberAnimation {
+ duration: 400
+ property: "y"
+ easing.type: Easing.InOutQuad
+ }
+ },
+ Transition {
+ from: "Portrait_Hidden"
+ to: "Portrait_Visible"
+ NumberAnimation {
+ duration: 400
+ property: "y"
+ easing.type: Easing.InOutQuad
+ }
+ }
+ ]
+
+ header: Label {
+ text: settingsDrawer.title
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pointSize: 20
+ }
+
+ ScrollView {
+ anchors.fill: parent
+ padding: 10
+
+ ColumnLayout {
+ id: contentLayout
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/weatherforecast/Sun.qml b/examples/quick/quickshapes/weatherforecast/Sun.qml
new file mode 100644
index 0000000000..b65a11371d
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/Sun.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+Sun_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/SunBehindCloud.qml b/examples/quick/quickshapes/weatherforecast/SunBehindCloud.qml
new file mode 100644
index 0000000000..7ca3eee981
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/SunBehindCloud.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SunBehindCloud_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/SunBehindCloud_generated.qml b/examples/quick/quickshapes/weatherforecast/SunBehindCloud_generated.qml
new file mode 100644
index 0000000000..b64fe5bf57
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/SunBehindCloud_generated.qml
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/sun-behind-cloud-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START type: DOC 0
+ DemoShape { //combined path container
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 24.455, 35.8525 49.3712x49.8728
+ strokeColor: "transparent"
+ fillColor: "#fffcc11a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 29.53 45.25 C 22.39 54.42 22.51 70.73 31.64 78.8 C 42 87.97 56.57 87.88 65.74 79.44 C 76.73 69.33 76.1 52.49 66.38 42.96 C 56.61 33.37 39.15 32.87 29.53 45.25 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 32.2619, 41.832 10.7315x13.1421
+ strokeColor: "transparent"
+ fillColor: "#fffee269"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 40.99 42.1 C 38.17 40.85 35.08 44.25 33.73 46.58 C 32.38 48.91 31.13 53.48 34 54.64 C 36.87 55.8 38.93 53.83 40.99 50.43 C 43.05 47.03 44.21 43.54 40.99 42.1 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 47.9692, 15.7549 13.1149x17.8884
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 48.34 29.31 C 47.66 30.59 47.89 31.57 49.24 31.9 C 50.6 32.22 59.06 33.6 59.77 33.64 C 60.8 33.7 61.19 32.93 61.06 32.02 C 60.93 31.12 58.86 18.45 58.67 17.28 C 58.48 16.12 57.25 14.95 56.08 16.5 C 54.93 18.07 48.85 28.34 48.34 29.31 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 69.1814, 34.4063 18.2257x15.4262
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 70.25 38.1 C 68.92 38.51 68.96 39.33 69.67 40.36 C 70.38 41.39 75.29 48.89 75.75 49.41 C 76.21 49.93 76.98 50.06 77.62 49.28 C 78.26 48.5 86.34 37.19 86.93 36.48 C 87.9 35.32 87.32 34.09 85.83 34.48 C 84.69 34.78 71.09 37.85 70.25 38.1 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 10.8789, 71.5427 18.0836x15.5928
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 19.84 72.49 C 20.74 71 21.91 71.39 22.75 72.81 C 23.33 73.78 28.18 80.31 28.63 81.02 C 29.08 81.73 29.28 83.09 27.85 83.41 C 26.43 83.73 14.86 86.77 13.05 87.09 C 11.24 87.41 10.21 85.99 11.37 84.44 C 12.05 83.54 19.32 73.33 19.84 72.49 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 3.98165, 48.399 18.0509x13.2527
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 17.38 61.24 C 19.03 61.98 19.81 61.71 20.16 60.53 C 20.61 58.98 21.97 51.87 22.03 50.25 C 22.07 49.2 21.64 48.18 20.03 48.44 C 18.41 48.7 6.78 50.31 5.29 50.83 C 3.56 51.43 3.69 53.33 4.84 53.93 C 6.2 54.65 16.22 60.72 17.38 61.24 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 22.4845, 22.1259 15.5758x18.5311
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 37.48 34.1 C 38.41 33.36 38.13 32.29 37.29 31.71 C 36.45 31.13 26.07 23.62 24.75 22.6 C 23.59 21.7 22.16 22.02 22.55 24.09 C 22.83 25.57 26.14 38.57 26.36 39.47 C 26.62 40.5 27.39 41.02 28.3 40.37 C 29.21 39.72 36.51 34.87 37.48 34.1 " }
+ }
+ // PATH visit count: 28
+ ShapePath {
+ // boundingRect: 19.1486, 46.294 104.389x55.916
+ strokeColor: "transparent"
+ fillColor: "#ffe2ebee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 109.13 81.18 C 109.13 81.18 110.58 52.73 87.55 47.18 C 64.52 41.63 57.81 63.97 57.81 63.97 C 57.81 63.97 50.13 62.18 43.01 68.1 C 37.42 72.75 36.63 79.73 36.63 79.73 C 36.63 79.73 29.28 78.61 23.16 84.55 C 18.73 88.85 19.16 94.37 19.16 94.37 L 28.08 102.21 L 118.73 99.76 L 123.5 94.64 C 123.5 94.64 124.26 88.96 118.96 84.69 C 113.66 80.41 109.13 81.18 109.13 81.18 " }
+ }
+ // PATH visit count: 43
+ ShapePath {
+ // boundingRect: 19.168, 92.23 104.332x12.3586
+ strokeColor: "transparent"
+ fillColor: "#ffb9ced3"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 40.64 92.23 C 40.64 92.23 44.25 96.31 55.7 96.04 C 67.15 95.77 70.16 92.77 70.16 92.77 C 70.16 92.77 75.18 98.04 89.69 96.92 C 104.2 95.8 108.37 92.74 108.37 92.74 C 108.37 92.74 111.91 95.27 116.39 95.34 C 120.86 95.41 123.5 94.63 123.5 94.63 C 123.5 94.63 123.03 98.19 121.47 100.47 C 119.91 102.75 117.31 104.49 113.2 104.46 C 109.08 104.43 96.56 104.54 83.85 104.52 C 71.14 104.5 34.82 104.71 29.45 104.48 C 25.36 104.31 22.39 101.9 21.02 99.86 C 18.83 96.6 19.19 93.16 19.19 93.16 C 19.19 93.16 23.31 96.39 28.87 96.15 C 36.47 95.83 40.64 92.23 40.64 92.23 " }
+ }
+ } // END type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/SunBehindLargeCloud_generated.qml b/examples/quick/quickshapes/weatherforecast/SunBehindLargeCloud_generated.qml
new file mode 100644
index 0000000000..3122c64b9c
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/SunBehindLargeCloud_generated.qml
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/sun-behind-large-cloud-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START type: DOC 0
+ DemoShape { //combined path container
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 25.7146, 32.9147 33.1229x33.4701
+ strokeColor: "transparent"
+ fillColor: "#fffcc11a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 29.12 39.22 C 24.33 45.38 24.41 56.32 30.53 61.74 C 37.48 67.89 47.26 67.83 53.41 62.17 C 60.79 55.38 60.36 44.08 53.84 37.69 C 47.29 31.25 35.58 30.91 29.12 39.22 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 30.9585, 36.9296 7.20214x8.82548
+ strokeColor: "transparent"
+ fillColor: "#fffee269"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 36.81 37.11 C 34.92 36.27 32.84 38.55 31.94 40.12 C 31.04 41.68 30.2 44.75 32.12 45.53 C 34.04 46.31 35.43 44.99 36.81 42.7 C 38.2 40.41 38.98 38.07 36.81 37.11 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 41.4903, 19.4387 8.81367x11.9934
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 41.74 28.53 C 41.28 29.39 41.44 30.05 42.35 30.26 C 43.26 30.48 48.94 31.4 49.42 31.43 C 50.11 31.47 50.37 30.95 50.29 30.35 C 50.21 29.75 48.82 21.24 48.69 20.46 C 48.56 19.68 47.74 18.9 46.96 19.94 C 46.18 20.98 42.09 27.88 41.74 28.53 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 55.7317, 31.9409 12.2375x10.3504
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 56.45 34.42 C 55.56 34.7 55.58 35.24 56.06 35.94 C 56.54 36.63 59.83 41.66 60.14 42.01 C 60.44 42.36 60.96 42.44 61.4 41.92 C 61.83 41.4 67.25 33.81 67.65 33.33 C 68.3 32.55 67.91 31.73 66.91 31.99 C 66.14 32.19 57.01 34.25 56.45 34.42 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 16.6111, 56.8641 12.1295x10.4676
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 22.62 57.5 C 23.23 56.5 24.01 56.76 24.57 57.72 C 24.96 58.37 28.21 62.75 28.52 63.23 C 28.82 63.71 28.95 64.62 28 64.83 C 27.05 65.05 19.28 67.09 18.07 67.3 C 16.86 67.52 16.16 66.56 16.94 65.52 C 17.39 64.91 22.27 58.06 22.62 57.5 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 11.9801, 41.3307 12.1119x8.89368
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 20.97 49.95 C 22.08 50.44 22.6 50.27 22.83 49.47 C 23.13 48.43 24.04 43.66 24.09 42.57 C 24.12 41.86 23.83 41.18 22.75 41.36 C 21.67 41.53 13.86 42.62 12.86 42.96 C 11.7 43.36 11.78 44.64 12.56 45.04 C 13.47 45.53 20.19 49.6 20.97 49.95 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 24.4062, 23.699 10.4393x12.4393
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 34.46 31.74 C 35.08 31.24 34.89 30.53 34.33 30.14 C 33.77 29.75 26.8 24.71 25.92 24.02 C 25.14 23.41 24.19 23.63 24.45 25.02 C 24.64 26.02 26.86 34.74 27.01 35.34 C 27.18 36.03 27.7 36.38 28.31 35.95 C 28.91 35.51 33.81 32.26 34.46 31.74 " }
+ }
+ // PATH visit count: 28
+ ShapePath {
+ // boundingRect: 4.23467, 37.495 119.251x63.625
+ strokeColor: "transparent"
+ fillColor: "#ffe2ebee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 107.33 76.86 C 107.19 76.4 111.85 42.97 81.48 38.03 C 54.28 33.61 48.38 58.03 48.38 58.03 C 48.38 58.03 38.05 56.3 30.62 63.64 C 24.49 69.69 23.69 76.5 23.69 76.5 C 23.69 76.5 16.1 75 9.46 80.07 C 2.78 85.17 4.41 90.5 4.41 90.5 L 19.77 101.12 L 110.32 98.4 L 123.18 94.96 C 123.18 94.96 125.39 87.15 117.93 81.16 C 111.91 76.33 107.33 76.86 107.33 76.86 " }
+ }
+ // PATH visit count: 40
+ ShapePath {
+ // boundingRect: 4.26296, 87.98 119.212x15.8916
+ strokeColor: "transparent"
+ fillColor: "#ffb9ced3"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 30.29 90.24 C 30.29 90.24 24.88 93.78 16.16 92.84 C 6.17 91.76 4.34 87.98 4.34 87.98 C 4.34 87.98 3.25 95.38 9.27 100.37 C 14.71 104.88 21.56 103.72 31.1 103.78 C 40.64 103.84 104.73 103.75 110.79 103.81 C 115.75 103.86 119.76 102.31 121.88 98.18 C 123.89 94.28 123.43 92.16 123.43 92.16 C 123.43 92.16 119.82 94.4 111.96 93.48 C 105.17 92.69 101.85 89.95 101.85 89.95 C 101.85 89.95 93.22 94.37 78.81 94.19 C 63.71 94 58.58 88.46 58.58 88.46 C 58.58 88.46 53.68 93.1 42.91 92.83 C 32.14 92.56 30.29 90.24 30.29 90.24 " }
+ }
+ } // END type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/SunBehindRainCloud.qml b/examples/quick/quickshapes/weatherforecast/SunBehindRainCloud.qml
new file mode 100644
index 0000000000..9a3c593174
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/SunBehindRainCloud.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SunBehindRainCloud_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/SunBehindRainCloud_generated.qml b/examples/quick/quickshapes/weatherforecast/SunBehindRainCloud_generated.qml
new file mode 100644
index 0000000000..130129af53
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/SunBehindRainCloud_generated.qml
@@ -0,0 +1,138 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/sun-behind-rain-cloud-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START type: DOC 0
+ DemoShape { //combined path container
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 34.0183, 21.2222 41.6055x42.0205
+ strokeColor: "transparent"
+ fillColor: "#fffcc11a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 38.29 29.14 C 32.28 36.87 32.38 50.61 40.07 57.41 C 48.8 65.13 61.08 65.06 68.81 57.95 C 78.07 49.43 77.54 35.24 69.35 27.21 C 61.11 19.13 46.4 18.71 38.29 29.14 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 40.5962, 26.2619 9.04604x11.0908
+ strokeColor: "transparent"
+ fillColor: "#fffee269"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 47.95 26.49 C 45.57 25.43 42.97 28.3 41.83 30.27 C 40.7 32.23 39.64 36.08 42.06 37.07 C 44.48 38.05 46.21 36.39 47.95 33.52 C 49.69 30.64 50.67 27.7 47.95 26.49 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 53.8243, 4.29781 11.0661x15.0649
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 54.14 15.71 C 53.56 16.79 53.76 17.62 54.9 17.89 C 56.04 18.16 63.18 19.32 63.78 19.36 C 64.65 19.41 64.98 18.76 64.87 18 C 64.76 17.24 63.02 6.56 62.85 5.58 C 62.69 4.6 61.65 3.62 60.67 4.93 C 59.69 6.24 54.58 14.89 54.14 15.71 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 71.6983, 20.0076 15.3551x12.9961
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 72.6 23.12 C 71.48 23.47 71.51 24.15 72.11 25.03 C 72.71 25.9 76.85 32.22 77.23 32.65 C 77.61 33.09 78.26 33.19 78.81 32.54 C 79.36 31.89 86.16 22.35 86.65 21.76 C 87.47 20.78 86.98 19.74 85.72 20.07 C 84.78 20.32 73.31 22.9 72.6 23.12 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 22.5769, 51.2931 15.2318x13.1453
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 30.12 52.09 C 30.88 50.84 31.86 51.16 32.57 52.36 C 33.05 53.18 37.14 58.68 37.53 59.28 C 37.91 59.88 38.07 61.02 36.88 61.3 C 35.68 61.57 25.93 64.13 24.41 64.4 C 22.89 64.67 22.01 63.47 22.99 62.17 C 23.56 61.4 29.69 52.8 30.12 52.09 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 16.7694, 31.7949 15.2134x11.1614
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 28.06 42.61 C 29.45 43.23 30.11 43.01 30.4 42.01 C 30.78 40.7 31.92 34.71 31.98 33.35 C 32.02 32.46 31.65 31.61 30.29 31.83 C 28.93 32.05 19.13 33.41 17.87 33.85 C 16.42 34.36 16.52 35.96 17.49 36.46 C 18.63 37.06 27.08 42.18 28.06 42.61 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 32.3542, 9.63925 13.1208x15.6136
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 44.99 19.74 C 45.77 19.11 45.53 18.22 44.83 17.72 C 44.12 17.23 35.37 10.9 34.26 10.04 C 33.28 9.28 32.08 9.55 32.41 11.29 C 32.64 12.54 35.43 23.5 35.62 24.25 C 35.84 25.12 36.49 25.56 37.25 25.01 C 38.02 24.48 44.18 20.39 44.99 19.74 " }
+ }
+ // PATH visit count: 27
+ ShapePath {
+ // boundingRect: 19.6233, 35.0519 92.0786x46.5981
+ strokeColor: "transparent"
+ fillColor: "#ffe2ebee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 99.22 65.16 C 99.22 65.16 102.76 41.69 81.62 36.13 C 58.67 30.09 53.29 51.4 53.29 51.4 C 53.29 51.4 46.61 49.58 40.44 54.54 C 35.61 58.42 34.78 65.26 34.78 65.26 C 34.78 65.26 25.37 64.94 21.33 71.43 C 17.49 77.6 21.33 81.65 21.33 81.65 L 76.87 79.42 L 111.06 79.62 C 111.06 79.62 113.53 73.2 108.78 68.75 C 104.02 64.31 99.22 65.16 99.22 65.16 " }
+ }
+ // PATH visit count: 52
+ ShapePath {
+ // boundingRect: 19.64, 64.4059 92.0852x22.3779
+ strokeColor: "transparent"
+ fillColor: "#ffb9ced3"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 61.29 67.49 C 61.29 67.49 65.54 71.74 76.67 71.03 C 87.8 70.32 93.66 66.38 94.98 65.77 C 96.3 65.16 97.81 63.75 98.32 64.76 C 98.83 65.77 97.71 68.6 96.2 70.63 C 94.68 72.65 93.83 73.88 93.57 74.78 C 93.37 75.49 96.71 76.5 102.98 76.8 C 108.34 77.06 111.72 75.98 111.72 75.98 C 111.72 75.98 111.87 79.35 110.56 81.98 C 109.34 84.41 108.07 85.2 106.05 85.61 C 105.01 85.82 85.38 85.81 68.49 86.21 C 51.6 86.61 29.54 87.32 25.8 86.11 C 22.06 84.9 21.14 82.53 20.66 81.61 C 19.82 80.03 19.64 77.56 19.64 77.56 C 19.64 77.56 23.78 79.64 29.65 78.83 C 35.52 78.02 38.65 74.99 39.46 74.68 C 40.27 74.38 44.11 76.3 50.18 74.78 C 58.66 72.65 61.29 67.49 61.29 67.49 " }
+ }
+ // PATH visit count: 10
+ ShapePath {
+ // boundingRect: 37.616, 90.8719 11.1663x16.7914
+ strokeColor: "transparent"
+ fillColor: "#ff4fc2f7"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 47.26 90.91 C 45.94 90.43 40.21 94.54 38.15 100.02 C 35.11 108.11 45.94 110.81 48.22 103.02 C 49.09 100.06 49.08 91.57 47.26 90.91 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 80.0867, 96.7588 11.3411x16.8179
+ strokeColor: "transparent"
+ fillColor: "#ff4fc2f7"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 90.07 96.81 C 88.9 96.29 84.25 99.83 82.14 102.8 C 80.12 105.64 78.32 110.44 83.28 112.9 C 88.69 115.57 91.15 109.76 91.38 105.54 C 91.59 101.72 91.1 97.27 90.07 96.81 " }
+ }
+ // PATH visit count: 10
+ ShapePath {
+ // boundingRect: 56.5834, 106.381 11.2326x16.7162
+ strokeColor: "transparent"
+ fillColor: "#ff4fc2f7"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 66.52 106.4 C 65.05 106.05 59.41 110.63 57.39 114.9 C 53.34 123.46 65.78 126.48 67.32 118.27 C 68.3 113.06 67.72 106.68 66.52 106.4 " }
+ }
+ // PATH visit count: 10
+ ShapePath {
+ // boundingRect: 58.021, 112.999 4.24576x7.23228
+ strokeColor: "transparent"
+ fillColor: "#ffafe6f8"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 61.24 113.05 C 60.07 112.58 57.79 115.45 58.04 118 C 58.3 120.55 60.4 120.78 61.4 119.37 C 62.74 117.5 62.4 113.51 61.24 113.05 " }
+ }
+ // PATH visit count: 10
+ ShapePath {
+ // boundingRect: 38.6898, 97.5009 4.26287x6.93974
+ strokeColor: "transparent"
+ fillColor: "#ffafe6f8"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 41.68 97.56 C 40.57 97.04 38.67 100.08 38.69 101.65 C 38.74 104.85 41.1 105.06 42.2 103.48 C 43.3 101.91 43.25 98.3 41.68 97.56 " }
+ }
+ // PATH visit count: 10
+ ShapePath {
+ // boundingRect: 81.401, 103.683 4.67559x7.32839
+ strokeColor: "transparent"
+ fillColor: "#ffafe6f8"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 84.83 103.77 C 83.51 103.14 81.24 106.05 81.41 108.16 C 81.71 111.8 84.6 111.58 85.57 109.59 C 86.54 107.6 86.02 104.34 84.83 103.77 " }
+ }
+ } // END type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/SunBehindSmallCloud.qml b/examples/quick/quickshapes/weatherforecast/SunBehindSmallCloud.qml
new file mode 100644
index 0000000000..b9d39522bf
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/SunBehindSmallCloud.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SunBehindSmallCloud_generated {
+}
diff --git a/examples/quick/quickshapes/weatherforecast/SunBehindSmallCloud_generated.qml b/examples/quick/quickshapes/weatherforecast/SunBehindSmallCloud_generated.qml
new file mode 100644
index 0000000000..da849732cf
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/SunBehindSmallCloud_generated.qml
@@ -0,0 +1,106 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/sun-behind-small-cloud-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START type: DOC 0
+ DemoShape { //combined path container
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 27.7018, 34.4865 57.227x57.809
+ strokeColor: "transparent"
+ fillColor: "#fffcc11a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 33.58 45.38 C 25.31 56.01 25.45 74.91 36.02 84.27 C 48.03 94.9 64.92 94.79 75.55 85.01 C 88.3 73.29 87.56 53.77 76.3 42.72 C 64.98 31.61 44.74 31.03 33.58 45.38 " }
+ }
+ // PATH visit count: 13
+ ShapePath {
+ // boundingRect: 36.7609, 41.4189 12.4351x15.2413
+ strokeColor: "transparent"
+ fillColor: "#fffee269"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 46.87 41.73 C 43.6 40.28 40.02 44.22 38.46 46.92 C 36.9 49.62 35.45 54.92 38.77 56.27 C 42.09 57.62 44.48 55.34 46.87 51.39 C 49.26 47.44 50.61 43.39 46.87 41.73 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 54.9601, 11.2148 15.2177x20.7191
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 55.39 26.91 C 54.6 28.4 54.87 29.53 56.44 29.91 C 58.01 30.28 67.83 31.88 68.65 31.93 C 69.85 32 70.3 31.11 70.15 30.06 C 70 29.01 67.6 14.32 67.38 12.98 C 67.16 11.64 65.73 10.28 64.38 12.08 C 63.03 13.87 55.99 25.79 55.39 26.91 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 78.1267, 30.7303 20.5286x19.0219
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 79.23 37.03 C 77.75 37.67 77.9 38.61 78.85 39.71 C 79.8 40.81 86.41 48.83 86.99 49.37 C 87.58 49.91 88.49 49.96 89.13 48.98 C 89.77 48 97.65 33.95 98.24 33.06 C 99.21 31.6 98.39 30.26 96.72 30.89 C 95.46 31.38 80.16 36.63 79.23 37.03 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 68.7867, 86.5617 18.1037x20.9806
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 80.19 86.84 C 81.38 86.2 82.44 86.69 82.74 88.04 C 83.04 89.39 86.34 103.17 86.86 105.72 C 87.12 107 85.66 108.04 84.46 107.29 C 83.26 106.54 70.24 97.46 69.4 96.73 C 68.2 95.68 69.03 94.59 69.62 94.03 C 70.82 92.91 79.06 87.44 80.19 86.84 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 42.1527, 94.3999 15.7215x21.5623
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 43.93 94.41 C 42.42 94.27 41.98 95.61 42.21 96.81 C 42.43 98.01 44.68 113.21 44.83 114.34 C 45.05 115.99 46.78 116.66 47.9 115.01 C 49.02 113.36 56.72 101.03 57.34 99.88 C 58.46 97.78 57.64 97.33 56.52 96.81 C 55.39 96.28 45.5 94.56 43.93 94.41 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 11.9586, 75.8565 20.9654x18.0662
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 22.35 76.95 C 23.4 75.23 24.75 75.68 25.72 77.32 C 26.39 78.45 32.01 86.01 32.54 86.83 C 33.06 87.65 33.29 89.23 31.64 89.6 C 29.99 89.97 16.58 93.5 14.48 93.87 C 12.38 94.24 11.18 92.6 12.53 90.8 C 13.32 89.76 21.76 77.93 22.35 76.95 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 3.98342, 49.0428 20.9099x15.3499
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 19.5 63.92 C 21.42 64.77 22.32 64.46 22.72 63.1 C 23.24 61.3 24.82 53.06 24.89 51.19 C 24.94 49.97 24.44 48.79 22.57 49.09 C 20.7 49.39 7.22 51.26 5.5 51.86 C 3.5 52.56 3.64 54.76 4.98 55.46 C 6.54 56.28 18.16 63.32 19.5 63.92 " }
+ }
+ // PATH visit count: 19
+ ShapePath {
+ // boundingRect: 25.4244, 18.5677 18.0479x21.4831
+ strokeColor: "transparent"
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 42.8 32.45 C 43.88 31.59 43.55 30.35 42.58 29.68 C 41.61 29.01 29.57 20.3 28.05 19.12 C 26.7 18.07 25.05 18.45 25.5 20.84 C 25.82 22.56 29.66 37.63 29.92 38.67 C 30.22 39.87 31.12 40.47 32.17 39.72 C 33.22 38.97 41.68 33.35 42.8 32.45 " }
+ }
+ // PATH visit count: 28
+ ShapePath {
+ // boundingRect: 40.2592, 40.77 84.0624x44.63
+ strokeColor: "transparent"
+ fillColor: "#ffe2ebee"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 112.86 68.59 C 112.87 67.94 115.07 45.51 95.37 41.36 C 76.33 37.34 71.34 55.18 71.34 55.18 C 71.34 55.18 64.59 54.35 59.64 58.11 C 54.69 61.88 54.41 68.09 54.41 68.09 C 54.41 68.09 46.45 67.79 42.7 72.22 C 39.31 76.22 40.44 79.83 40.44 79.83 L 62.41 85.4 L 117.94 83.91 L 124.31 80.05 C 124.31 80.05 124.67 75 121.38 71.8 C 116.84 67.4 112.86 68.59 112.86 68.59 " }
+ }
+ // PATH visit count: 34
+ ShapePath {
+ // boundingRect: 40.262, 77.3 84.038x10.2109
+ strokeColor: "transparent"
+ fillColor: "#ffbacdd2"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 61.29 77.3 C 61.29 77.3 55.2 79.98 47.66 79.32 C 43.18 78.93 40.32 77.6 40.32 77.6 C 40.32 77.6 39.55 82.78 43.62 85.33 C 46.67 87.24 49.58 87.53 55 87.51 C 60.42 87.49 109.74 87.39 113.29 87.35 C 116.85 87.31 120.23 86.68 121.96 85.07 C 124.14 83.04 124.3 80.05 124.3 80.05 C 124.3 80.05 120.88 81.07 116.12 80.45 C 111.38 79.83 108.62 77.71 108.62 77.71 C 108.62 77.71 105.21 80.62 87.58 80.94 C 68.56 81.29 61.29 77.3 61.29 77.3 " }
+ }
+ } // END type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/Sun_generated.qml b/examples/quick/quickshapes/weatherforecast/Sun_generated.qml
new file mode 100644
index 0000000000..c5711f12a2
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/Sun_generated.qml
@@ -0,0 +1,119 @@
+// Copyright (C) 2023 Googlefonts
+// SPDX-License-Identifier: Apache-2.0
+// Generated from: assets/sun-svgrepo-com.svg
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ // viewBox 0, 0 128x128
+ // size 800x800
+ implicitWidth: 800
+ implicitHeight: 800
+ // START svg1524 type: DOC 0
+ DemoShape { //combined path container
+ transform: [
+ Scale { xScale: width / 128; yScale: height / 128 }
+ ]
+ objectName: "svg1524"
+ // skipping DEFS "defs1528"
+ // PATH visit path1504 count: 13
+ ShapePath {
+ objectName: "svg_path:path1504"
+ // boundingRect: 30.5107, 29.1663 67.158x67.8384
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#fffcc11a"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 37.41 41.95 C 27.7 54.43 27.87 76.6 40.28 87.59 C 54.37 100.06 74.2 99.93 86.67 88.46 C 101.62 74.7 100.76 51.8 87.54 38.83 C 74.25 25.79 50.5 25.11 37.41 41.95 " }
+ }
+ // PATH visit path1506 count: 13
+ ShapePath {
+ objectName: "svg_path:path1506"
+ // boundingRect: 41.1383, 37.3059 14.591x17.8796
+ strokeColor: "transparent"
+ fillColor: "#fffee269"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 53 37.67 C 49.16 35.97 44.96 40.6 43.13 43.76 C 41.3 46.93 39.6 53.14 43.5 54.73 C 47.4 56.31 50.2 53.63 53.01 49 C 55.8 44.37 57.39 39.62 53 37.67 " }
+ }
+ // PATH visit path1508 count: 19
+ ShapePath {
+ objectName: "svg_path:path1508"
+ // boundingRect: 62.4917, 1.85223 17.8593x24.3131
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 63 20.27 C 62.07 22.01 62.38 23.35 64.23 23.79 C 66.08 24.23 77.59 26.1 78.56 26.16 C 79.97 26.25 80.49 25.19 80.32 23.96 C 80.14 22.73 77.33 5.5 77.07 3.92 C 76.81 2.34 75.14 0.76 73.55 2.87 C 71.96 4.98 63.7 18.96 63 20.27 " }
+ }
+ // PATH visit path1510 count: 19
+ ShapePath {
+ objectName: "svg_path:path1510"
+ // boundingRect: 91.3437, 27.2032 24.7856x20.9868
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 92.8 32.23 C 90.99 32.79 91.04 33.9 92.01 35.31 C 92.98 36.72 99.66 46.91 100.27 47.62 C 100.89 48.32 101.94 48.5 102.82 47.44 C 103.7 46.39 114.68 30.99 115.48 30.03 C 116.8 28.45 116.01 26.78 113.99 27.3 C 112.45 27.71 93.94 31.88 92.8 32.23 " }
+ }
+ // PATH visit path1512 count: 19
+ ShapePath {
+ objectName: "svg_path:path1512"
+ // boundingRect: 100.595, 61.4539 24.575x18.2962
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 106.6 61.86 C 105.3 61.12 103.61 61.33 103.17 63 C 102.73 64.67 100.8 76.8 100.62 77.86 C 100.44 78.92 101.24 79.97 102.55 79.71 C 103.86 79.45 122 76.76 123.21 76.46 C 125.32 75.93 126.02 73.82 123.83 72.24 C 122.41 71.21 107.83 62.56 106.6 61.86 " }
+ }
+ // PATH visit path1514 count: 19
+ ShapePath {
+ objectName: "svg_path:path1514"
+ // boundingRect: 78.7207, 90.2732 21.2359x24.6316
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 92.09 90.6 C 93.49 89.85 94.73 90.42 95.08 92.01 C 95.43 93.59 99.3 109.77 99.92 112.76 C 100.23 114.26 98.51 115.49 97.11 114.61 C 95.7 113.73 80.42 103.08 79.44 102.21 C 78.03 100.98 79.01 99.7 79.7 99.05 C 81.1 97.72 90.77 91.31 92.09 90.6 " }
+ }
+ // PATH visit path1516 count: 19
+ ShapePath {
+ objectName: "svg_path:path1516"
+ // boundingRect: 47.4529, 99.4674 18.4556x25.2985
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 49.54 99.48 C 47.77 99.31 47.25 100.89 47.52 102.29 C 47.78 103.7 50.42 121.53 50.6 122.86 C 50.86 124.79 52.89 125.59 54.2 123.65 C 55.51 121.71 64.55 107.25 65.28 105.89 C 66.6 103.43 65.63 102.9 64.31 102.29 C 63 101.68 51.39 99.66 49.54 99.48 " }
+ }
+ // PATH visit path1518 count: 19
+ ShapePath {
+ objectName: "svg_path:path1518"
+ // boundingRect: 12.0424, 77.7167 24.6027x21.2165
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 24.23 79 C 25.46 76.98 27.04 77.51 28.19 79.44 C 28.97 80.76 35.57 89.64 36.19 90.6 C 36.81 91.57 37.07 93.41 35.14 93.85 C 33.19 94.3 17.46 98.43 15 98.87 C 12.54 99.31 11.13 97.38 12.71 95.27 C 13.63 94.03 23.53 80.15 24.23 79 " }
+ }
+ // PATH visit path1520 count: 19
+ ShapePath {
+ objectName: "svg_path:path1520"
+ // boundingRect: 2.66887, 46.235 24.5552x18.0221
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 20.89 63.7 C 23.14 64.7 24.2 64.34 24.67 62.73 C 25.29 60.62 27.13 50.95 27.22 48.75 C 27.28 47.32 26.69 45.94 24.49 46.29 C 22.29 46.64 6.47 48.85 4.45 49.55 C 2.1 50.37 2.27 52.95 3.83 53.77 C 5.68 54.74 19.3 63 20.89 63.7 " }
+ }
+ // PATH visit path1522 count: 19
+ ShapePath {
+ objectName: "svg_path:path1522"
+ // boundingRect: 27.8408, 10.482 21.1791x25.207
+ strokeColor: "#b0ab9d"
+ strokeWidth: 0.888
+ fillColor: "#ffffa722"
+ fillRule: ShapePath.WindingFill
+ PathSvg { path: "M 48.23 26.78 C 49.5 25.77 49.11 24.32 47.97 23.53 C 46.83 22.74 32.71 12.53 30.92 11.13 C 29.34 9.9 27.4 10.34 27.93 13.15 C 28.31 15.17 32.81 32.85 33.12 34.07 C 33.47 35.48 34.53 36.18 35.76 35.3 C 36.97 34.43 46.91 27.84 48.23 26.78 " }
+ }
+ } // END svg1524 type: DOC 0
+}
diff --git a/examples/quick/quickshapes/weatherforecast/assets/Europe.svg b/examples/quick/quickshapes/weatherforecast/assets/Europe.svg
new file mode 100644
index 0000000000..2717687f51
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/Europe.svg
@@ -0,0 +1,395 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xml:space="preserve"
+ width="443.42206"
+ height="455.37424"
+ viewBox="1754 161 6480.483 6957.5929"
+ version="1.0"
+ id="Europakarte"
+ sodipodi:version="0.32"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ sodipodi:docname="Europe.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ style="fill:none;enable-background:new"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"><sodipodi:namedview
+ inkscape:window-height="1833"
+ inkscape:window-width="2935"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ inkscape:zoom="2.8284271"
+ inkscape:cx="147.78532"
+ inkscape:cy="236.88077"
+ inkscape:window-x="1670"
+ inkscape:window-y="50"
+ inkscape:current-layer="countries"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ showgrid="false"
+ inkscape:pagecheckerboard="0"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"><inkscape:grid
+ type="xygrid"
+ id="grid2717"
+ originx="-1559.5366"
+ originy="-991.63777" /></sodipodi:namedview>
+ <metadata
+ id="metadata"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF>
+ </metadata>
+
+ <defs
+ style="fill:none"
+ id="defs">
+ <inkscape:perspective
+ id="perspective81"
+ inkscape:persp3d-origin="340 : -814.07375 : 1"
+ inkscape:vp_z="680 : -727.40708 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : -727.40708 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <style
+ id="style10"
+ type="text/css">
+ * { fill:none; }
+ path { fill:#C0C0C0; stroke:white; stroke-width:8; }
+ .europe {}
+ .eu {}
+ #ad {}
+ #al {}
+ #am {}
+ #at {}
+ #az {}
+ #ba {}
+ #be {}
+ #bg {}
+ #by {}
+ #ch {}
+ #cs {}
+ #cy {}
+ #cz {}
+ #de {}
+ #dk {}
+ #dz {}
+ #ee {}
+ #es {}
+ #fi {}
+ #fr {}
+ #gb {}
+ #ge {}
+ #gl {}
+ #gr {}
+ #hr {}
+ #hu {}
+ #ie {}
+ #il {}
+ #iq {}
+ #ir {}
+ #is {}
+ #it {}
+ #jo {}
+ #kz {}
+ #lb {}
+ #li {}
+ #lt {}
+ #lu {}
+ #lv {}
+ #ma {}
+ #mc {}
+ #md {}
+ #mk {}
+ #mt {}
+ #nl {}
+ #no {}
+ #pl {}
+ #pt {}
+ #rs {}
+ #rs-km {}
+ #ro {}
+ #ru {}
+ #ru-kgd {}
+ #sa {}
+ #se {}
+ #si {}
+ #sk {}
+ #sm {}
+ #sy {}
+ #tm {}
+ #tn {}
+ #tr {}
+ #ua {}
+ </style>
+ <style
+ type="text/css"
+ id="style2412">
+ * { fill:none; }
+ path { fill:#C0C0C0; stroke:white; stroke-width:8; }
+
+ .gmt /* GMT/WET (UTC) */
+ { fill:#5F61BF; }
+ .gmt-nodst
+ { fill:#C0BFFD; }
+
+ .cet /* CET (UTC+1) */
+ { fill:#C15F61; }
+ .cet-nodst
+ { fill:#FBBFC0; }
+
+ .eet /* EET (UTC+2) */
+ { fill:#D7D65C; }
+
+ .msk /* MSK (UTC+3) */
+ { fill:#5EBF5F }
+ </style>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : -461.226 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09449 : -461.226 : 1"
+ inkscape:persp3d-origin="372.04724 : -636.6197 : 1"
+ id="perspective2459" /><inkscape:perspective
+ id="perspective2460"
+ inkscape:persp3d-origin="372.04724 : -636.6197 : 1"
+ inkscape:vp_z="744.09449 : -461.226 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : -461.226 : 1"
+ sodipodi:type="inkscape:persp3d" /></defs><g
+ style="display:inline;opacity:1;fill:none"
+ inkscape:label="countries"
+ id="countries"
+ inkscape:groupmode="layer"
+ transform="translate(-1630.407,-991.63776)"><path
+ id="fi"
+ class="eu europe"
+ d="m 7814.2981,3152.6347 c 8.599,-32.9629 35.8292,-67.3588 53.0271,-96.0222 44.4282,-78.824 71.6583,-169.1135 101.7548,-253.6703 22.9306,-61.6261 30.0965,-77.391 32.9628,-143.3165 1.4332,-10.0322 7.1658,-21.4975 2.8663,-31.5297 -11.4653,-32.9628 -139.0171,-80.2573 -171.9798,-103.1879 -4.2995,-2.8663 47.2944,-81.6905 11.4653,-108.9207 -12.8985,-10.032 -40.1287,0 -48.7276,-14.3315 -37.2624,-64.4925 8.5989,-34.3961 -2.8663,-53.0273 -17.198,-25.7969 -35.8292,-5.7325 -57.3266,-31.5296 -18.6312,-21.4975 -5.7327,-22.9305 4.2995,-42.9949 5.7325,-12.8985 -28.6634,-12.8985 -28.6634,-18.6312 1.4332,-10.0322 1.4332,-61.6261 1.4332,-63.0593 -30.0966,-25.7969 5.7326,-10.0322 14.3317,-20.0642 5.7326,-8.599 -30.0966,-60.1931 -34.3961,-65.9258 -24.3637,-32.9627 -51.5939,-71.6582 -80.2572,-100.3215 -4.2995,-4.2995 -27.2302,-21.4974 -31.5297,-30.0964 -21.4975,-40.1286 47.2945,-141.8835 20.0644,-166.2473 -7.1659,-8.5989 -20.0644,-8.5989 -28.6634,-14.3316 -15.7647,-11.4654 -22.9306,-31.5297 -38.6954,-41.5619 -1.4332,0 -50.1608,-7.1658 -51.594,-7.1658 -8.599,-7.1659 -11.4653,-18.6311 -17.198,-28.6633 -31.5297,-51.5939 5.7327,-47.2944 -10.0322,-84.5568 -7.1658,-17.1979 -34.3959,-1.4332 -28.6632,-11.4652 7.1657,-12.8987 17.1979,-21.4976 25.7969,-32.9629 -45.8613,-21.4975 14.3317,-55.8935 -4.2995,-87.4232 -28.6633,-50.1607 -73.0915,-31.5295 -110.3537,-71.6583 l -1.4332,-7.1657 c -1.4332,0 -2.8663,0 -4.2995,1.4332 0,-1.4332 -1.4332,-1.4332 -1.4332,-2.8665 v 2.8665 c -28.6632,5.7325 -22.9307,38.6954 -47.2944,45.8613 -21.4975,5.7326 -12.8985,-24.3639 -37.2624,2.8663 -2.8662,4.2995 0,11.4652 -1.4332,17.1979 -1.433,1.4333 -17.1979,31.5297 -17.1979,32.9629 -4.2995,11.4653 4.2995,48.7276 5.7327,58.7598 0,4.2995 -2.8663,7.1658 -2.8663,11.4653 0,8.599 1.4331,15.7647 2.8663,24.3639 4.2995,11.4652 14.3317,20.0642 15.7647,31.5296 7.1659,50.1607 -5.7325,15.7648 -22.9305,31.5295 -20.0644,20.0644 -1.4332,25.7971 -7.1659,50.1608 -1.4332,5.7328 -7.1658,8.599 -8.599,14.3317 0,1.4332 -18.6312,-15.7647 -21.4975,-17.1979 -4.2994,-2.8663 -57.3265,-21.4975 -61.626,-15.7649 -5.7327,5.7327 -1.4333,17.1981 -5.7327,22.9306 -17.198,30.0966 -40.1287,11.4654 -64.4926,15.7649 -7.1657,0 -14.3316,4.2995 -20.0642,2.8663 l -5.7327,-4.2995 c -2.8663,-5.7327 -2.8663,-14.3317 -5.7327,-20.0642 -10.0321,-17.1981 -42.9949,-61.6263 -60.1929,-71.6583 -37.2623,-21.4976 -38.6956,8.599 -35.8291,31.5296 l -8.599,30.0965 c 0,0 1.4331,0 1.4331,0 l -1.4331,1.4332 5.7325,7.1658 c -1.433,2.8662 -7.1657,4.2995 0,8.599 24.3639,18.6311 65.9258,53.0271 91.7227,64.4925 30.0965,12.8984 54.4603,1.433 81.6905,28.6632 5.7326,4.2995 1.4331,17.198 7.1658,20.0644 4.2995,1.433 7.1657,-5.7327 10.0322,-4.2995 11.4652,4.2995 15.7647,18.6312 24.3637,27.2301 2.8664,2.8663 11.4654,2.8663 10.0322,7.1658 -15.7648,30.0965 1.4332,31.5297 5.7327,65.9256 1.4332,5.7327 -4.2995,11.4654 -4.2995,17.198 0,0 0,0 0,0 0,0 0,0 0,0 -1.4332,1.4332 -1.4332,1.4332 -1.4332,1.4332 0,0 0,0 0,0 0,14.3317 21.4975,2.8663 27.2302,8.599 18.6311,14.3316 -11.4653,34.396 -2.8663,48.7276 8.599,20.0644 42.9949,38.6955 45.8612,61.6262 1.4332,17.198 -11.4653,71.6583 -10.0321,78.8241 12.8985,45.8613 44.4281,61.6261 61.6261,98.8884 5.7327,-10.0322 12.8985,-18.6312 32.9629,-7.1659 10.032,5.7327 4.2995,25.7971 14.3315,31.5296 8.599,5.7327 20.0644,-5.7325 30.0966,-2.8662 4.2995,0 4.2995,8.5989 7.1658,11.4654 8.5989,4.2995 8.5989,-12.8986 17.1979,1.433 15.7649,32.9629 4.2995,61.6261 10.0322,75.9578 4.2995,12.8985 42.9949,-15.7647 25.797,25.797 -4.2995,10.0322 -18.6312,7.1659 -27.2302,12.8986 -8.599,5.7325 -18.6312,10.032 -24.3637,18.631 -2.8664,2.8664 1.433,10.0322 -1.4332,12.8986 -2.8663,2.8663 -10.0322,-4.2995 -11.4653,-1.4332 -4.2995,10.0322 -5.7327,22.9307 -5.7327,34.396 0,7.1659 4.2995,14.3316 1.4332,21.4974 -1.4332,12.8986 -18.6312,18.6312 -21.4976,30.0966 -2.8663,8.599 4.2995,18.6312 1.4332,25.7969 -7.1657,15.7648 -22.9306,12.8985 -24.3637,34.396 -1.4332,4.2995 7.1658,8.599 7.1658,14.3317 -4.2995,27.2301 -42.995,22.9306 -44.4282,47.2944 -1.4332,10.0322 -8.5989,35.8291 -10.0322,37.2623 -5.7327,0 -12.8984,-14.3317 -18.6311,-10.0322 -12.8985,11.4654 7.1659,40.1287 -5.7326,48.7278 -4.2996,2.8663 -12.8986,-15.7649 -11.4654,-10.0322 0,7.1658 10.0322,10.0322 12.8985,17.1979 12.8984,47.2945 -4.2995,15.7648 -24.3638,38.6955 -4.2995,5.7327 4.2995,20.0644 -1.4332,20.0644 -8.599,-1.4333 -4.2995,-18.6312 -11.4652,-20.0644 -11.4654,-2.8663 -25.7971,1.4332 -31.5297,11.4654 -22.9307,38.6954 25.797,11.4652 22.9305,34.3959 -2.8662,14.3317 -40.1286,38.6954 -41.5617,54.4603 -7.1659,94.589 1.4331,-2.8663 15.7648,32.9629 1.4332,1.4331 -12.8985,27.23 -1.4331,34.3959 1.4331,1.4332 4.2995,-5.7327 5.7326,-4.2995 2.8664,4.2995 -1.4331,14.3317 4.2995,15.7648 2.8664,1.4332 -2.8663,-12.8985 1.4332,-12.8985 4.2995,-1.4332 5.7327,4.2995 7.1658,8.599 8.5989,22.9307 -1.4331,60.193 2.8664,68.792 2.8663,5.7327 11.4652,2.8663 15.7647,7.1658 5.7327,7.1659 8.599,15.7649 12.8985,22.9306 1.4332,2.8665 -1.4331,7.1659 0,10.0322 2.8664,4.2995 8.599,4.2995 11.4654,7.1658 1.4331,1.4332 -5.7327,0 -5.7327,1.4332 -1.4332,2.8663 1.4332,5.7327 2.8663,8.599 7.1659,8.599 22.9307,12.8985 21.4976,22.9307 -1.4332,8.5989 -24.3639,1.4332 -25.7971,10.0322 -2.8663,11.4652 14.3317,18.6311 17.1981,28.6632 1.4331,4.2995 -5.7327,8.599 -4.2996,14.3317 1.4332,2.8662 10.0322,2.8662 10.0322,7.1659 -1.4331,18.631 -15.7648,-1.4332 -10.0322,14.3315 1.4332,5.7327 8.5991,10.0322 7.1659,14.3317 -1.4332,5.7327 -15.7649,-1.4332 -17.198,2.8663 -1.4332,37.2623 14.3317,41.5618 25.797,57.3267 2.8662,2.8663 -11.4653,5.7326 -8.599,10.0321 1.4332,7.1659 4.2995,12.8986 8.599,18.6312 31.5296,41.5618 7.1657,-1.4331 37.2623,-7.1658 5.7326,0 -5.7327,15.7647 0,20.0642 8.599,4.2995 18.6311,-2.8662 28.6632,-2.8662 8.599,1.4332 12.8985,15.7649 20.0644,15.7649 7.1658,0 10.0322,-8.5992 15.7648,-8.5992 2.8664,-1.433 0,4.2995 1.4332,5.7327 4.2995,1.4332 7.1658,0 11.4653,-1.4332 1.4332,-1.4331 1.4332,-10.032 2.8664,-7.1658 14.3317,31.5297 -8.599,37.2624 -2.8664,38.6955 2.8664,0 35.8291,-24.3638 40.1286,-27.23 1.4332,-1.4333 4.2995,-8.5992 5.7327,-10.0322 0,0 0,0 0,0 -7.1658,21.4974 -25.7969,45.8613 8.599,58.7598 5.7327,2.8662 17.1979,-10.0322 20.0644,-4.2995 2.8663,8.599 -20.0644,21.4974 -11.4654,24.3637 8.5989,4.2995 7.1659,-20.0642 15.7649,-21.4974 7.1657,-1.4333 1.433,14.3316 -1.4332,20.0642 -4.2995,7.1659 -12.8985,10.0322 -20.0643,15.7649 -2.8664,4.2995 -14.3317,14.3317 -10.0322,11.4653 44.4281,-17.198 27.2302,-45.8612 50.1608,-60.1929 7.1658,-4.2995 -11.4654,18.6312 -4.2995,22.9307 10.0321,5.7325 22.9306,-1.4333 32.9628,-7.166 7.1659,-2.8662 10.0322,-14.3315 15.7649,-18.6311 8.5989,-7.1658 47.2944,-11.4653 51.5939,-17.198 4.2995,-4.2995 -10.0322,-11.4653 -5.7327,-15.7648 7.1659,-5.7327 18.6311,-1.4332 28.6633,-2.8664 2.8663,0 8.599,1.4332 10.0322,-1.4331 2.8663,-4.2994 -2.8664,-10.0321 -1.4332,-12.8984 1.4332,-2.8664 1.4332,7.1657 4.2995,8.599 1.4332,1.433 38.6956,-21.4975 41.5618,-25.797 1.4333,-1.4332 -5.7327,-30.0966 8.599,-27.2301 2.8663,0 55.8934,48.7276 42.9949,-4.2995 -2.8662,-10.0322 -28.6632,-11.4654 -24.3637,-22.9307 2.8663,-10.0322 21.4975,-4.2995 31.5297,-1.4332 5.7325,1.4332 5.7325,17.1979 11.4652,14.3317 14.3317,-10.0322 -7.1658,-58.7598 10.0322,-48.7277 14.3317,10.0321 -24.3639,45.8612 20.0643,31.5297 68.792,-22.9307 10.0322,-8.599 24.3638,-30.0966 1.4332,-1.4331 2.8663,2.8664 5.7327,2.8664 2.8663,0 7.1658,0 8.599,-2.8664 1.4332,-2.8663 -4.2995,-12.8985 0,-12.8985 1.4332,0 24.3638,20.0644 31.5297,17.198 10.0322,-2.8663 17.1979,-11.4653 22.9306,-18.631 1.4331,0 1.4331,-1.4332 1.4331,-2.8664 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="ccccccccccccccsscccccccsccccscccccccscccssscccccccccccccccccsccccccccccccsccccccscsssccccccccsccccccccccscccscsssscscscccscccscccscccccscccccsccccscccccsssscccsccccssscccc" /><path
+ id="se"
+ class="eu europe"
+ d="m 6447.058,4352.1943 c 2.8665,-8.599 -2.8663,-12.8984 -11.4652,-17.1979 l -5.7327,-14.3317 c 11.4654,7.1657 30.0964,11.4652 24.3638,24.3639 0,2.8662 -2.8664,5.7325 -7.1659,7.1657 z m 5.7327,-65.9256 c 20.0644,-22.9306 -2.8662,-21.4974 -4.2995,-50.1608 0,-8.599 8.599,-14.3317 10.0322,-22.9306 0,-2.8663 -5.7327,-5.7328 -7.1659,-8.599 -18.6312,-2.8663 -42.9949,-8.599 -58.7598,2.8662 15.7649,-20.0642 42.9951,-14.3315 64.4925,-10.032 -11.4652,-18.6312 -18.6312,-32.9629 -15.7647,-35.8291 5.7325,-12.8987 25.7969,15.7647 38.6954,10.032 11.4653,-4.2995 -28.6634,-21.4975 -22.9307,-32.9627 4.2995,-10.0322 24.3639,1.4331 28.6634,-7.1659 7.1658,-10.0321 2.8663,-24.3638 -2.8664,-34.396 -2.8663,-5.7327 -11.4653,-1.4332 -17.198,-2.8662 -11.4653,-4.2995 -8.599,-24.3639 -17.198,-34.3961 -5.7327,-7.1658 -17.1979,-7.1658 -22.9306,-14.3317 -2.8663,-5.7326 1.4332,-12.8985 -1.4332,-18.631 -2.8663,-7.1659 -20.0643,-30.0966 -20.0643,-31.5297 0,-2.8664 5.7327,-4.2995 4.2995,-5.7327 -1.4332,-2.8663 -7.1658,-1.4332 -10.0322,-2.8663 -7.1658,-5.7327 -12.8985,-14.3316 -17.198,-21.4976 -7.1657,-12.8984 1.4333,-47.2944 -8.5989,-63.0593 -2.8663,-4.2995 -12.8985,1.4332 -17.198,-2.8662 -4.2995,-5.7327 -2.8664,-15.7648 -2.8664,-24.3638 -2.8663,-21.4976 37.2623,-71.6583 18.6312,-94.5889 -25.797,-32.9629 -11.4653,55.8934 -65.9256,1.433 -8.599,-8.5989 5.7327,-34.3959 5.7327,-44.4281 -1.4332,-17.198 -18.6312,-81.6903 -18.6312,-81.6903 5.7327,-7.1659 20.0642,5.7325 28.6632,4.2995 4.2995,14.3315 10.0322,28.6632 22.9307,10.032 31.5296,-44.4281 -8.599,-101.7547 0,-127.5517 2.8664,-10.0321 17.198,-11.4654 21.4976,-20.0643 2.8663,-5.7326 0,-12.8985 -2.8664,-18.6312 0,-4.2995 -7.1658,-5.7326 -5.7327,-8.599 2.8664,-10.0321 48.7277,-12.8985 61.6262,-45.8612 l -1.4332,-41.5618 c 15.7647,-81.6905 -21.4975,-65.9256 -28.6634,-127.5519 -1.4331,-14.3315 27.2302,-7.1657 38.6956,-17.1979 l 15.7647,-57.3266 c -5.7325,-35.8292 -60.1928,-32.9629 -68.7918,-65.9256 -7.1659,-30.0966 15.7647,-70.2252 12.8984,-101.7549 -2.8664,-21.4974 -25.797,-40.1286 -27.2301,-61.6261 -1.4332,-8.5989 5.7327,-18.6311 5.7327,-27.2301 -1.4332,-10.0321 -7.1659,-18.6312 -5.7327,-28.6632 0,-8.599 10.0322,-14.3317 10.0322,-22.9307 0,-173.413 -21.4976,127.5517 -21.4976,-45.8613 0,-15.7648 20.0644,-25.797 21.4976,-41.5619 1.4331,-5.7326 -7.1659,-11.4653 -5.7327,-18.6312 4.2995,-22.9305 35.8291,-71.6583 54.4603,-83.1235 58.7598,-37.2624 97.4552,30.0964 107.4874,-37.2624 10.0321,-70.225 -50.1608,-55.8934 -41.5618,-81.6903 12.8985,-40.1286 32.9628,-80.2573 45.8613,-118.9528 1.4331,-5.7327 8.599,-7.1658 10.0321,-12.8985 l -4.2995,-53.0271 c 0,-1.4332 7.1659,-44.4281 7.1659,-44.4281 l -14.3317,-55.8936 c 5.7327,-12.8985 28.6634,0 40.1286,-5.7325 l 31.5297,-27.2302 c 4.2995,-12.8985 -14.3317,-24.3639 -11.4653,-37.2623 8.599,-25.797 55.8934,-81.6905 64.4924,-106.0543 15.7649,-38.6955 -21.4975,-41.5618 -27.2302,-73.0915 -4.2995,-25.7969 11.4654,-10.0322 20.0644,-20.0643 11.4653,-14.3316 11.4653,-34.396 14.3317,-50.1607 1.4331,-11.4654 28.6632,-61.6263 37.2622,-61.6263 l 45.8614,20.0644 c 28.6633,-11.4654 -7.1658,-93.1557 1.4332,-104.6211 20.0642,-25.797 118.9527,22.9306 131.8511,14.3317 47.2946,-30.0965 -2.8662,-27.2302 -4.2995,-40.1287 -4.2994,-17.1979 20.0644,-30.0964 20.0644,-47.2944 0,-10.0322 -2.8664,-20.0644 -7.1659,-28.6633 -4.2995,-7.1658 -22.9307,-11.4653 -18.631,-18.6312 5.7325,-10.0322 21.4974,-2.8663 32.9627,-5.7327 h 2.8664 c 4.2995,2.8664 11.4653,2.8664 17.198,7.1659 17.198,11.4652 -5.7327,10.0322 5.7325,17.198 24.3639,18.6311 65.9258,53.0271 91.7227,64.4925 30.0965,12.8984 54.4603,1.433 81.6905,28.6632 5.7326,4.2995 1.4331,17.198 7.1658,20.0644 4.2995,1.433 7.1657,-5.7327 10.0322,-4.2995 11.4652,4.2995 15.7647,18.6312 24.3637,27.2301 2.8664,2.8663 11.4654,2.8663 10.0322,7.1658 -15.7648,30.0965 1.4332,31.5297 5.7327,65.9256 1.4332,5.7327 -4.2995,11.4654 -4.2995,17.198 -2.8664,17.1981 18.6312,4.2995 25.797,10.0322 18.6311,14.3316 -11.4653,34.396 -2.8663,48.7276 8.599,20.0644 42.9949,38.6955 45.8612,61.6262 1.4332,17.198 -11.4653,71.6583 -10.0321,78.8241 12.8985,45.8613 44.4281,61.6261 61.6261,98.8884 -4.2995,5.7327 -8.599,12.8985 -14.3317,15.7648 -5.7327,2.8664 -10.0322,-11.4653 -17.1979,-10.0321 -2.8663,1.4331 -4.2995,8.599 -7.1658,8.599 -11.4654,1.4331 -24.3639,-8.599 -34.3961,-2.8664 -7.1658,4.2995 4.2995,21.4976 -2.8663,25.7971 -30.0964,18.631 -40.1286,-15.7649 -61.6261,-20.0644 -2.8664,0 2.8663,8.599 2.8663,12.8984 0,2.8665 1.4332,5.7328 0,8.599 -1.4332,10.0322 -10.0322,18.6312 -7.1657,27.2302 0,1.4332 1.0302,3.4038 -0.5785,3.3619 -6.2271,-0.1621 -16.6197,-3.3619 -25.2186,-7.6614 -4.2995,-1.4332 -8.599,-12.8985 -10.0321,-8.599 -2.8664,15.7649 35.8292,10.0322 38.6954,25.797 2.8663,8.5989 -17.1979,2.8664 -25.7969,7.1659 -1.4332,1.433 4.2995,1.433 7.1658,1.433 1.4332,1.4333 17.1979,11.4655 8.5989,18.6312 -1.433,1.4332 -2.8662,-7.1657 -5.7325,-5.7327 -1.4332,1.4332 4.2995,7.1659 1.433,8.5991 -11.4652,7.1658 -44.4281,0 -50.1608,21.4975 0,4.2995 8.599,0 11.4654,1.4331 10.0322,7.1659 18.6312,17.1981 22.9307,28.6633 8.599,25.797 -34.3961,37.2624 -35.8292,65.9256 -1.4332,34.3961 45.8612,37.2624 57.3266,63.0593 1.4333,1.4332 -28.6633,38.6956 -34.3959,51.5941 -8.599,17.1979 -11.4654,87.423 -28.6634,90.2894 -2.8662,0 -2.8662,-10.0322 -5.7327,-8.5991 -10.032,1.4332 4.2995,24.3639 -5.7325,28.6634 -4.2995,2.8663 -2.8664,-17.198 -7.1659,-14.3317 -1.4332,0 -1.4332,1.4332 -1.4332,2.8664 h -1.4331 c -2.8664,4.2995 2.8663,12.8985 -1.4332,15.7648 -4.2995,5.7327 -21.4975,2.8662 -25.797,11.4654 -4.2995,5.7325 -2.8662,17.1979 -10.0322,20.0642 -8.5989,2.8663 -21.4974,-12.8985 -27.2301,-5.7327 -5.7327,8.599 15.7649,20.0644 11.4654,30.0966 -4.2995,7.1658 -14.3317,-14.3317 -20.0644,-10.0322 -7.1658,5.7327 1.4332,17.198 0,25.7969 -1.4332,10.0322 -2.8663,24.3639 -12.8985,30.0965 -8.599,4.2995 -18.6312,-10.0321 -28.6634,-8.599 -5.7325,0 12.8987,4.2995 14.3317,10.0322 1.4332,2.8663 -5.7327,4.2995 -8.5989,4.2995 -2.8663,0 4.2994,-7.1658 1.4332,-10.0322 -2.8665,-1.4331 -8.599,-1.4331 -10.0322,1.4332 -1.4332,4.2995 8.599,8.599 5.7327,12.8985 -2.8665,2.8664 -11.4654,-5.7327 -15.7649,-1.4332 -2.8663,2.8664 7.1659,4.2996 8.599,8.5991 0,1.4331 -5.7326,-1.4332 -7.1658,0 -2.8663,5.7326 0,15.7648 -4.2995,20.0642 -5.7327,5.7327 -27.2302,1.4333 -21.4975,5.7327 8.599,7.1658 34.396,-4.2994 31.5297,5.7327 -4.2995,11.4653 -17.198,18.6311 -24.3639,28.6633 -2.8663,2.8663 -5.7327,8.599 -10.0322,10.0322 -4.2995,1.4332 -12.8985,0 -12.8985,-4.2995 1.4333,-4.2995 11.4654,-1.4332 12.8985,-5.7327 4.2995,-8.599 -11.4652,2.8663 -17.1979,4.2995 1.4332,4.2995 4.2994,8.5989 7.1659,12.8984 4.2995,10.0322 11.4652,37.2624 0,34.3961 -2.8665,0 -1.4332,-14.3317 -4.2995,-11.4654 -4.2995,7.1659 1.433,18.6312 -4.2995,25.797 -1.4332,4.2994 -8.5991,-1.4331 -12.8986,-1.4331 -10.0321,1.4331 -22.9306,0 -31.5297,5.7325 -22.9305,17.1982 15.7649,24.3639 32.9629,44.4283 1.4332,1.4331 -5.7327,2.8663 -7.1658,1.4331 0,-1.4331 -4.2995,-1.4331 -4.2995,0 -1.4332,11.4654 1.4331,22.9307 0,34.3961 -1.4332,7.1657 -7.1659,12.8984 -8.599,20.0642 -1.4332,8.599 -1.4332,17.198 1.4331,25.797 0,1.4332 4.2995,-2.8663 5.7327,-1.4331 5.7327,10.032 21.4975,24.3637 12.8985,32.9627 -8.599,8.599 -21.4975,-15.7648 -32.9629,-11.4653 -32.9627,11.4653 22.9307,41.5619 18.6312,73.0914 0,5.7327 -14.3317,-2.8663 -14.3317,1.4332 -1.4331,7.1659 10.0322,8.599 11.4654,14.3317 1.4332,2.8663 -5.7327,1.4332 -5.7327,4.2995 0,5.7327 0,12.8985 2.8664,18.6312 1.4331,5.7327 0,12.8984 2.8663,18.6311 1.4332,2.8663 7.1658,-2.8664 7.1658,0 1.4332,1.4331 -5.7326,1.4331 -7.1658,2.8663 -1.4332,2.8663 -1.4332,7.1658 0,10.0322 2.8663,7.1658 12.8985,11.4653 14.3317,20.0643 1.4331,8.5989 -7.1659,17.1979 -5.7327,25.797 0,2.8663 4.2995,-1.4332 7.1658,0 4.2995,1.4331 1.4332,11.4653 5.7327,11.4653 15.7649,0 45.8613,-8.599 60.193,0 5.7325,2.8663 0,12.8985 2.8663,18.6312 4.2995,8.599 12.8984,14.3317 21.4974,20.0642 1.4332,1.4332 7.1659,-4.2995 8.599,-1.433 2.8664,2.8662 -4.2995,8.5989 -1.4331,10.032 1.4331,0 74.5246,-28.6632 35.8292,14.3317 -5.7327,7.1659 -18.6312,-12.8985 -27.2302,-10.0322 -7.1659,2.8664 10.0321,8.5991 14.3317,14.3317 5.7326,8.599 7.1658,21.4974 15.7648,27.2301 4.2995,1.4333 7.1657,-14.3316 12.8984,-11.4652 7.166,2.8663 2.8663,15.7647 8.599,20.0642 21.4975,24.3639 37.2624,8.599 32.9629,47.2946 -1.4332,14.3317 -14.3317,-1.4332 -15.7649,-1.4332 -7.1658,1.4332 -8.599,10.0322 -14.3316,14.3317 -12.8986,14.3316 0,27.2301 -40.1286,45.8613 -1.4332,1.4331 7.1658,14.3315 7.1658,15.7647 -2.8663,4.2995 -10.0322,-2.8662 -15.7649,-1.4332 -10.0321,1.4332 -20.0643,10.0322 -31.5297,10.0322 -11.4652,0 -1.433,-21.4974 -1.433,-31.5296 1.433,-10.0322 -7.166,-8.599 -11.4654,-14.3317 0,-1.4331 1.4332,-4.2995 0,-7.1658 -5.7326,-8.599 -7.1658,-21.4974 -17.198,-25.7969 -7.1658,-2.8665 -17.198,4.2995 -21.4975,11.4652 -1.4332,4.2995 11.4653,-1.433 12.8985,1.4332 5.7327,10.0322 1.4332,22.9307 7.1659,31.5297 4.2995,7.1658 25.797,5.7327 21.4975,12.8985 -17.198,32.9628 -58.7598,-17.198 -67.3588,-14.3317 -11.4654,2.8664 -17.198,18.6312 -30.0966,18.6312 -8.599,0 -10.032,-18.6312 -18.631,-17.198 -7.1659,1.4332 0,15.7647 -4.2995,22.9307 -1.4332,2.8662 -5.7327,-2.8665 -10.0322,-2.8665 -11.4654,1.4333 -24.3639,1.4333 -34.3959,7.166 -2.8665,1.433 4.2994,5.7325 7.1657,8.5989 11.4653,7.1658 25.797,-4.2995 34.396,-7.1659 14.3316,-1.433 28.6633,17.1981 41.5618,12.8986 5.7327,-2.8664 -11.4653,-20.0643 -4.2995,-18.6311 8.599,0 11.4653,15.7647 20.0643,20.0642 8.5991,2.8664 21.4976,-7.1658 28.6633,0 4.2995,4.2995 -11.4652,12.8985 -8.599,20.0644 2.8663,4.2995 14.3317,1.4331 18.6312,-2.8664 2.8663,-2.8663 -8.599,-10.0321 -5.7327,-11.4653 7.1658,-2.8663 12.8985,5.7327 18.6312,7.1658 11.4653,2.8664 21.4975,10.0322 31.5296,5.7327 7.1658,-2.8663 1.4333,-18.6312 7.1658,-22.9307 7.1659,-4.2995 17.198,4.2995 24.3639,0 10.0322,-4.2995 11.4653,-22.9307 22.9307,-24.3637 8.5989,-1.4332 22.9306,12.8985 18.6311,20.0642 -7.1659,10.0322 -24.3638,0 -35.8291,4.2995 -1.4332,0 2.8663,4.2995 4.2995,5.7327 4.2995,8.599 12.8985,17.198 12.8985,27.2302 1.433,2.8663 -7.1659,-1.4332 -10.0322,1.4332 -10.0322,5.7325 -21.4975,11.4653 -27.2302,21.4974 -5.7327,10.0321 -1.4332,22.9307 -5.7327,32.9628 -2.8663,5.7327 -7.1658,-21.4975 -14.3315,-18.6311 -4.2997,2.8663 7.1657,18.6311 2.8662,15.7648 -14.3316,-5.7327 5.7327,-54.4603 -17.1979,-51.5939 0,0 4.2995,63.0591 -21.4975,81.6903 -8.5991,5.7327 -21.4976,1.4332 -31.5296,2.8664 -4.2995,1.4331 11.4652,4.2995 11.4652,8.599 5.7327,17.198 -15.7647,5.7326 -18.6311,8.599 -2.8663,1.4331 8.5989,1.4331 8.5989,5.7327 1.4333,1.4331 -4.2994,-1.4332 -7.1657,0 -1.4332,1.4331 -2.8665,4.2995 -4.2995,4.2995 -2.8664,0 -75.9578,-10.0322 -70.2252,2.8663 1.4332,2.8663 5.7327,2.8663 10.0322,2.8663 1.4332,0 2.8664,-4.2995 4.2994,-4.2995 2.8665,1.4332 2.8665,10.0322 7.166,8.599 2.8662,0 -1.4332,-8.599 2.8662,-8.599 22.9307,-5.7326 41.5619,0 51.5941,22.9306 0,2.8663 -7.1659,-1.4332 -8.5991,0 -5.7326,5.7327 7.1659,14.3317 -8.599,14.3317 -11.4653,0 -25.797,-15.7649 -32.9627,-7.1657 -7.166,8.5989 21.4974,4.2994 30.0964,10.032 5.7327,4.2995 17.198,30.0966 12.8985,37.2624 -2.8663,5.7327 -17.198,-4.2995 -18.6312,1.4332 -1.4331,7.1657 30.0966,45.8612 20.0644,58.7598 -7.1659,8.599 -24.3639,-4.2996 -34.3961,0 -4.2995,0 1.4332,5.7325 4.2996,8.599 5.7326,7.1657 28.6633,14.3315 28.6633,25.7969 0,10.0322 -18.6312,15.7648 -17.198,27.2302 0,8.599 14.3317,8.599 20.0642,15.7647 1.4333,2.8665 -5.7325,2.8665 -7.1657,4.2995 -5.7327,10.0322 -11.4653,21.4975 -12.8985,32.9629 -2.8663,8.599 17.198,40.1286 15.7648,42.9949 -2.8663,2.8664 -10.0321,-2.8663 -14.3316,1.4332 -11.4654,11.4653 12.8985,34.3959 8.599,53.0271 0,4.2994 -11.4654,0 -12.8985,4.2994 -2.8664,4.2995 7.1658,10.0323 4.2995,14.3318 -15.7649,47.2944 -8.599,-34.3959 -15.7649,14.3317 -4.2995,30.0964 -5.7327,77.391 -30.0964,100.3215 -1.4332,1.4332 -5.7327,0 -7.1659,-2.8663 0,-2.8663 4.2996,-5.7327 2.8664,-8.599 0,-4.2995 -24.3639,-1.4332 -24.3639,-1.4332 -1.4331,-1.4331 2.8664,-7.1658 1.4332,-7.1658 -14.3317,1.4333 -15.7649,8.599 -22.9307,15.7648 -12.8984,10.0322 -4.2994,-11.4653 -8.5989,-10.0321 -10.0322,5.7326 -22.9307,8.599 -25.797,8.599 -7.1659,1.4331 -12.8985,-1.4332 -20.0644,-1.4332 -5.7325,0 -14.3317,-4.2995 -15.7647,1.4332 -2.8664,8.599 12.8985,17.198 8.599,24.3638 -7.166,7.1659 -20.0644,-1.4331 -30.0965,1.4332 -8.5991,2.8663 -10.0322,12.8985 -14.3317,20.0642 -37.2623,54.4603 10.0322,37.2624 8.599,74.5247 -1.4332,11.4653 -8.599,21.4975 -17.1979,30.0965 -7.166,7.1657 -30.0966,-7.1658 -34.3961,-7.1658 -34.396,-2.8664 -24.3638,21.4975 -60.1929,21.4975 -11.4654,0 -21.4975,-7.166 -31.5297,-8.599 -5.7327,0 -14.3316,4.2995 -20.0643,1.433 -10.0321,-5.7325 15.7649,-10.032 15.7649,-10.032 0,-8.599 -8.599,-15.7649 -7.166,-24.3639 0,-8.599 11.4654,-12.8985 14.3317,-20.0642 0,0 -25.7969,-35.8292 -45.8613,-67.3588 -1.4331,5.7325 -8.599,11.4652 -8.599,18.6311 1.4332,30.0965 30.0966,27.2301 -1.4331,55.8934 0,1.4332 0,1.4332 -1.4332,1.4332 z m 448.5809,-355.4251 c 2.8663,-4.2995 8.599,-2.8663 12.8985,-4.2995 -10.0322,22.9307 -30.0965,121.8191 -34.3959,151.9155 -1.4333,7.166 4.2994,63.0594 -14.3317,64.4926 -2.8665,0 -8.599,-64.4926 -8.599,-68.792 2.8663,-41.5619 31.5296,-63.0593 38.6954,-101.7547 2.8663,-17.198 -7.1658,-24.3639 5.7327,-41.5619 z m 193.4774,-38.6954 c 0,8.599 -11.4654,10.032 -17.1981,15.7647 -18.631,21.4975 14.3317,5.7327 -12.8985,27.2302 -21.4974,15.7649 -8.599,15.7649 -20.0642,37.2624 -4.2995,7.1657 -14.3317,28.6632 -18.6312,20.0642 -4.2995,-8.599 7.1658,-20.0642 11.4653,-30.0964 -1.4331,1.4332 -2.8663,4.2994 -4.2995,4.2994 -1.4331,0 -1.4331,-4.2994 -1.4331,-5.7327 -1.4332,-10.032 -15.7649,-27.2301 -15.7649,-30.0964 0,-8.599 5.7327,-17.198 5.7327,-25.7969 -1.4332,-10.0322 -10.0322,-17.198 -10.0322,-27.2302 -1.433,-7.1658 41.5619,-75.9578 47.2946,-78.8241 10.032,-4.2996 21.4974,0 31.5296,-1.4332 15.7648,22.9307 -2.8664,25.797 -15.7649,55.8934 -15.7647,37.2624 20.0644,25.7971 20.0644,38.6956 z m -50.1608,-1175.1959 c 0,2.8662 0,-1.4332 -1.4332,-5.7327 1.4332,1.4332 1.4332,4.2995 1.4332,5.7327 z m -640.6251,1540.6531 -1.4332,-4.2995 c 0,-8.599 -2.8663,-14.3317 -7.1658,-17.198 l -1.4332,-5.7327 c 2.8663,0 5.7327,1.4332 7.1659,4.2995 1.4331,5.7327 2.8663,14.3317 2.8663,22.9307 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccccccccccccccccccscccccccscccccsccccccccccccssccccccccccccccccccccccsccccccccccsccccccccscccccscccccccccccccccccccccccccccccccccscccsccscccsccccccccccccccccsccccscscccsccscscccccccccccccccscccccccccscccccccccscscscccccccccsccccccccccscsccccsssccccccscccccsccccccccccccscccccscsccccccccccccccccscccscscsccccccccsccc" /><path
+ id="dk"
+ class="eu europe"
+ d="m 6123.1627,4443.917 c -18.6312,-2.8664 -37.2624,-12.8986 -55.8936,-15.7649 -4.2994,0 -21.4974,1.4332 -31.5296,0 0,-7.1658 0,-14.3317 1.4332,-22.9307 0,-7.1657 11.4653,-15.7647 5.7327,-21.4974 5.7326,-7.1658 -2.8664,-18.6312 -2.8664,-28.6634 -1.4332,-8.599 2.8664,-21.4974 -2.8663,-27.2302 -12.8985,-12.8984 -50.1608,1.4333 -57.3266,-24.3637 -4.2995,-15.7649 10.0321,-32.9628 10.0321,-51.594 l 2.8664,2.8664 c 44.4281,21.4975 15.7647,-35.8291 12.8985,-41.5618 -1.4333,-2.8663 -2.8665,-11.4653 -7.166,-8.599 -7.1657,2.8662 -5.7325,15.7647 -12.8984,17.1979 -4.2995,1.4332 -1.4331,-10.0322 -1.4331,-14.3317 1.4331,-10.032 4.2995,-20.0642 4.2995,-28.6632 1.4331,-8.599 -7.1659,-20.0644 0,-25.7971 1.4331,-1.433 4.2995,-2.8663 5.7326,-2.8663 -1.4331,-2.8662 -7.1658,-64.4923 7.1659,-58.7598 8.599,2.8665 2.8662,17.198 7.1657,24.3639 12.8985,30.0965 74.5247,-44.4281 77.391,-44.4281 30.0965,-8.5991 -4.2995,38.6954 1.4332,45.8612 5.7326,7.1659 17.198,1.4332 27.2302,-1.4331 -2.8664,0 -4.2996,0 -4.2996,-1.4332 -2.8663,-7.1658 5.7327,-15.7647 1.4332,-21.4974 -2.8663,-4.2995 -11.4653,7.1657 -12.8985,2.8662 -2.8663,-5.7325 8.599,-10.032 8.599,-15.7647 1.4332,-32.9629 -20.0643,-22.9307 7.1659,-42.9951 5.7326,-5.7325 22.9305,-4.2995 27.23,0 2.8664,2.8664 -4.2995,15.7649 -1.4331,12.8986 5.7326,-5.7327 5.7326,-18.6311 12.8985,-22.9306 5.7327,-2.8665 12.8985,0 18.6312,0 7.1658,4.2995 41.5617,12.8984 41.5617,12.8984 2.8664,8.599 -4.2995,18.6311 -2.8663,28.6633 0,7.1659 11.4654,14.3316 8.599,21.4976 -2.8663,5.7325 -12.8985,-1.4332 -18.6312,0 -8.599,1.433 21.4976,7.1657 20.0644,15.7647 0,2.8663 -1.4332,4.2995 -1.4332,5.7327 5.7327,1.4331 7.1659,14.3317 14.3317,15.7648 17.198,4.2995 42.9949,-20.0643 50.1608,12.8985 2.8663,11.4654 -31.5296,68.792 -42.995,63.0593 -10.0321,-5.7326 -10.0321,-24.3638 -21.4975,-28.6633 -12.8985,-4.2995 -11.4653,70.2251 -15.7648,78.8241 -7.1659,18.6312 -18.6311,31.5296 -38.6955,31.5296 -10.0321,0 -31.5297,-15.7647 -28.6632,-5.7327 4.2994,10.0322 27.2301,4.2995 32.9627,15.7649 v 4.2995 h 2.8664 c 5.7326,0 7.1658,10.0322 11.4653,10.0322 15.7649,-4.2995 27.2301,-28.6634 42.995,-22.9307 8.599,4.2995 12.8985,17.198 22.9305,18.6312 5.7327,1.4331 7.166,-8.5991 11.4654,-11.4654 25.797,53.0271 -5.7327,7.1659 4.2995,27.2302 8.599,15.7649 35.8292,81.6905 -17.1979,75.9578 -11.4654,-1.4333 -21.4975,-10.0322 -32.9629,-12.8985 -4.2995,0 -11.4653,7.1658 -15.7647,2.8663 -11.4655,-8.599 28.6632,-5.7326 5.7325,-18.6312 -35.8291,-18.6311 0,12.8986 -11.4652,12.8986 -4.2995,0 -14.3317,-42.9951 -14.3317,-44.4281 -4.2995,-10.0322 -18.6312,-15.7649 -21.4975,-27.2302 0,-1.4332 1.4332,-1.4332 1.4332,-1.4332 -11.4654,4.2995 -27.2301,7.1658 -27.2301,8.599 -2.8665,5.7327 14.3315,0 17.1979,5.7327 1.4332,4.2995 -8.599,5.7325 -8.599,10.032 -1.4332,8.5992 0,17.1981 2.8663,24.3639 1.4332,4.2995 8.599,5.7327 8.599,10.0322 0,15.7648 -25.7969,21.4975 -28.6632,37.2622 0,5.7327 11.4652,-2.8663 15.7647,0 10.0322,7.1659 18.6312,17.1981 20.0644,28.6634 1.4331,5.7327 -10.0322,-11.4653 -15.7649,-10.0322 -7.1657,2.8664 -15.7647,15.7649 -20.0642,21.4976 z m 270.8682,-237.9055 v 1.433 c 15.7649,-11.4652 40.1287,-5.7325 57.3266,-2.8662 1.4332,2.8662 7.1659,5.7327 7.1659,8.599 -1.4332,8.5989 -10.0322,14.3316 -10.0322,22.9306 1.4333,28.6634 24.3639,27.2302 4.2995,50.1608 -7.1657,7.1658 -18.6311,8.599 -25.7969,15.7648 -12.8985,14.3317 2.8663,18.6312 15.7647,25.7969 l 8.599,21.4976 c -11.4652,10.0321 -42.9949,5.7326 -42.9949,18.6311 1.4332,15.7649 35.8291,37.2623 0,38.6955 -12.8985,0 -22.9307,-8.5989 -34.3959,-14.3316 5.7325,2.8662 12.8984,5.7327 14.3315,1.4332 2.8664,-10.0322 -5.7325,-21.4975 -12.8985,-30.0966 -2.8662,-2.8663 -4.2994,5.7327 -7.1657,5.7327 -8.599,0 -14.3317,-7.1658 -21.4975,-8.599 -7.1659,-1.4332 -14.3317,4.2995 -20.0644,2.8663 -2.8663,0 -2.8663,-4.2995 -2.8663,-5.7326 8.599,-22.9306 -17.1979,-17.1979 -15.7649,-21.4974 1.4333,-5.7327 11.4655,-4.2997 12.8985,-10.0322 5.7327,-41.5619 -7.1657,-30.0966 -30.0964,-50.1608 -2.8663,-2.8663 -11.4653,-10.0322 -7.1658,-11.4653 12.8985,-7.1659 81.6905,8.599 68.7919,-35.8292 -2.8663,-10.0321 -18.6311,-7.1659 -27.2302,-12.8986 -4.2995,-2.8662 10.0322,2.8665 14.3317,2.8665 7.1659,2.8662 14.3317,5.7325 21.4975,5.7325 21.4976,0 55.8935,-10.032 48.7277,34.3961 -1.4332,7.1658 -20.0644,14.3317 -14.3317,18.6312 25.797,21.4974 17.198,-30.0966 12.8985,-50.1609 -1.4332,-8.5989 -24.3639,-1.4332 -21.4975,-8.5989 1.4331,-5.7327 4.2995,-8.599 7.1658,-12.8985 z m 28.6634,229.3064 h -7.1659 v 2.8664 c 1.4332,2.8663 0,4.2995 0,5.7327 -4.2995,2.8663 -10.0321,7.1658 -11.4653,10.0321 -4.2995,2.8664 -7.1659,5.7327 -8.599,10.0321 l -1.4332,1.4333 v 1.433 -2.8663 c -8.599,0 -18.6312,-1.4332 -22.9306,0 -7.1658,1.4333 -12.8985,14.3317 -21.4975,12.8985 -11.4653,-1.4332 -17.198,-15.7647 -27.2302,-21.4974 -7.1657,-4.2995 -17.1979,-7.1658 -22.9306,-12.8985 -4.2995,-2.8664 11.4654,-5.7327 25.7969,-31.5297 20.0644,17.198 50.1608,21.4975 67.3588,35.8292 l 1.4332,1.4332 c -8.599,-12.8986 -27.2301,-22.9307 -20.0642,-34.3961 2.8663,-5.7325 11.4652,-2.8663 15.7647,-4.2995 10.0322,10.0322 31.5297,8.599 34.396,21.4975 0,1.4332 0,2.8664 -1.4331,4.2995 z m -432.8161,-209.2422 v 11.4654 c -1.4331,-8.599 -1.4331,-17.1981 0,-11.4654 z m 250.804,-273.7346 c 4.2994,15.7649 -10.0321,93.1557 -35.8292,84.5567 -8.599,-2.8663 -15.7647,-12.8985 -25.7969,-12.8985 10.0322,-12.8984 -32.9629,-2.8662 -44.4282,0 -10.0322,2.8665 -18.6311,11.4655 -28.6633,12.8985 -4.2995,0 -7.1658,-7.1658 -11.4653,-7.1658 -11.4653,-1.4332 -22.9307,0 -32.9629,2.8663 -4.2994,1.4332 -1.433,10.0322 -5.7325,12.8985 -20.0644,8.5991 1.4331,-48.7276 -27.2302,27.2302 -1.4332,1.4332 7.1658,41.5618 5.7326,41.5618 -47.2944,-1.4332 -21.4975,-90.2895 5.7327,-100.3215 24.3639,-11.4654 58.7598,1.4331 83.1237,-8.5991 32.9627,-15.7648 25.7969,-61.6261 55.8934,-78.8241 10.0322,-4.2995 22.9306,-1.4332 32.9628,-5.7327 10.0322,-5.7325 15.7648,-25.7969 27.2302,-25.7969 21.4974,0 -11.4654,15.7649 -12.8985,28.6633 -2.8664,20.0643 7.1658,7.1658 14.3316,28.6633 z m 131.8513,487.2763 -12.8985,1.4332 c 10.0322,2.8664 18.6312,7.1659 25.7969,14.3317 h 1.4332 c -4.2994,-5.7327 -10.0321,-10.0322 -14.3316,-15.7649 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="ccccccscccscccccccccsccccccsscccccccccccccscccscccccccccscccccccscccccccccccccccccccsscccccccscscccccccccccccccccccccccccccccsccccccccccccsccccccc" /><g
+ id="gb"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
+ sodipodi:nodetypes="csccscccccccccccccscccccccc"
+ style="display:inline;opacity:1;fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
+ d="m 4338.9677,4169.7186 c 4.2995,5.7328 10.0322,10.0322 15.7649,7.1658 8.599,-4.2995 11.4653,-18.6312 20.0643,-22.9306 8.5991,-5.7326 18.6312,7.1659 27.2301,5.7327 10.0322,0 17.1982,-7.1658 25.7971,-5.7327 10.0321,1.4332 15.7648,11.4654 24.3638,14.3316 4.2995,2.8665 12.8984,-2.8662 14.3317,1.4332 5.7327,10.0322 8.5989,24.3638 5.7327,35.8292 0,4.2995 -5.7327,5.7327 -5.7327,8.599 15.7647,61.6261 8.5989,-18.6312 25.7969,35.8291 5.7327,15.7648 -31.5296,37.2624 -31.5296,37.2624 24.3638,-5.7327 50.1608,-24.3639 45.8613,32.9627 -5.7327,55.8935 -45.8613,25.7971 -57.3266,38.6956 -10.0322,10.032 -2.8664,32.9627 -28.6634,28.6632 -5.7327,0 -10.0322,-5.7326 -14.3317,-10.0322 l -5.7325,-7.1658 c -21.4976,-12.8985 8.599,20.0644 -40.1287,2.8663 -7.1659,-1.4331 2.8663,-14.3315 5.7326,-21.4975 1.4332,-10.0322 -12.8985,-7.1657 -15.7648,-12.8984 -7.1659,-14.3317 0,-48.7277 -15.7648,-45.8614 -61.6261,8.599 17.1981,37.2624 -48.7277,42.9951 -21.4974,1.4331 -77.3908,-73.0915 -71.6582,-80.2573 1.4332,0 57.3266,-14.3317 57.3266,-14.3317 2.8664,-8.5989 -15.7648,-12.8984 -17.198,-21.4974 -2.8663,-8.599 34.3961,2.8663 38.6955,0 22.9307,-14.3317 14.3316,-50.1609 45.8612,-50.1609 z"
+ class="eu europe"
+ id="gb-nir"
+ transform="translate(-2.3653846e-5)" /><path
+ sodipodi:nodetypes="cccsccccccssccccsccccccccccsccccccccccssccccsccccccccccsccccccssccccccsccssccccccccscccscsccccccscsccscccccscccccccscccsscccscccccccssccccsssccccsccscccccccsccccscssccsscccsccccccsccccccsssssccccccccscccccsccscccscccccccccssccscccccccccccccsscccccsssccccccccscccscccscccsccccsccccccccccccccccccsccsscccccccc"
+ style="display:inline;opacity:1;fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
+ d="m 4586.1972,4531.9437 c 25.7969,20.0642 47.2944,31.5296 20.0642,44.4281 m -15.7647,7.1658 c -4.2995,2.8662 -8.599,4.2995 -12.8985,4.2995 -4.2995,0 4.2995,-5.7328 2.8663,-8.599 -8.599,-30.0965 -30.0965,-35.8292 5.7327,-47.2944 m 147.616,-973.1196 c 8.599,4.2995 27.2302,12.8985 28.6634,22.9307 0,7.1658 -20.0644,20.0642 -14.3317,15.7647 8.599,-5.7325 11.4653,-22.9306 21.4975,-21.4974 8.599,2.8663 -1.4332,21.4974 5.7327,27.2301 5.7325,4.2995 8.599,-11.4652 14.3317,-12.8984 21.4974,-5.7327 70.225,2.8663 98.8883,7.1657 11.4654,1.4333 27.2301,-8.5989 34.396,0 7.1658,7.166 -11.4654,14.3317 -15.7648,22.9307 -2.8663,8.599 0,17.198 -2.8663,25.797 -2.8663,7.1659 -93.1558,61.6262 -110.3537,74.5247 -28.6634,21.4974 12.8984,-2.8665 27.23,8.599 5.7327,2.8662 -7.1658,8.5989 -12.8985,12.8984 -4.2995,4.2995 -7.1658,11.4653 -14.3315,12.8985 -2.8665,0 0,-10.0322 -2.8665,-10.0322 -5.7327,-1.4331 -10.0322,4.2995 -14.3316,5.7327 -10.0321,4.2995 -11.4653,-7.1658 -15.7648,2.8663 -11.4654,25.7971 15.7648,34.3961 22.9307,25.7971 1.433,-2.8664 -5.7327,-7.1659 -2.8665,-7.1659 7.166,-1.4331 14.3317,4.2995 22.9307,4.2995 15.7649,-1.4331 35.8292,-18.6312 55.8935,-14.3317 10.0322,1.4332 18.6312,11.4654 28.6633,14.3317 24.3639,7.1659 104.6211,5.7327 117.5196,34.396 2.8664,8.599 4.2995,17.198 1.4332,25.797 l -32.9629,32.9627 c -4.2994,5.7327 -7.1657,12.8986 -8.5989,20.0644 -1.4332,2.8663 1.4332,5.7327 0,7.1658 -11.4653,21.4976 -71.6583,106.0543 -90.2895,107.4874 -28.6632,2.8664 -38.6954,-11.4653 -63.0593,7.1659 -4.2995,2.8663 8.599,4.2995 12.8986,2.8663 11.4653,-1.4332 21.4975,-8.599 31.5295,-10.0322 20.0644,-1.4331 4.2997,12.8986 5.7327,17.1981 4.2995,10.032 22.9307,12.8985 21.4975,22.9305 -4.2995,31.5297 -34.3959,4.2995 -42.9949,5.7327 -44.4282,7.1659 35.8291,25.797 -57.3266,18.6311 -1.4332,0 -1.4332,15.7649 0,15.7649 10.0322,4.2996 -1.4332,-4.2995 8.599,-2.8663 11.4652,2.8663 35.8291,21.4974 48.7276,12.8984 5.7327,-4.2994 7.1657,-14.3316 12.8984,-17.1979 2.8663,-1.4332 57.3266,38.6954 63.0593,44.4281 10.0322,7.1659 14.3317,42.9951 24.3639,54.4603 5.7326,5.7327 14.3316,7.1658 18.6311,12.8985 1.4332,2.8663 -1.4331,5.7327 -1.4331,10.0322 -1.4332,8.599 -1.4332,18.6312 -1.4332,27.2302 1.4332,25.7969 11.4654,131.8511 20.0644,153.3487 1.433,2.8663 4.2995,-5.7327 7.1657,-4.2995 5.7327,1.4331 10.0322,8.599 15.7648,11.4653 25.7971,15.7647 20.0644,2.8663 44.4283,32.9628 8.5989,10.0321 4.2995,27.2302 11.4652,38.6955 7.1658,15.7647 27.2302,1.4332 27.2302,30.0964 0,8.599 -15.7649,8.599 -17.198,17.198 -5.7327,22.9306 21.4975,55.8935 27.2302,75.9577 7.1657,27.2302 -15.7649,-12.8984 -17.198,17.1982 0,7.1657 51.5939,61.626 25.797,88.8562 -20.0644,20.0643 -28.6634,-2.8664 -42.9951,32.9627 -1.4331,4.2995 10.0322,-1.433 12.8985,1.4332 5.7327,4.2995 7.1659,12.8985 11.4654,17.198 4.2995,4.2995 11.4652,11.4654 15.7647,8.599 28.6634,-18.6312 7.166,-37.2622 37.2624,-32.9627 48.7276,7.1657 127.5518,28.6632 113.2201,106.0542 -4.2995,24.3639 -37.2623,88.8562 -61.6261,97.4552 -8.5991,2.8664 -21.4976,-14.3317 -24.3639,-7.1658 -12.8984,25.797 54.4604,18.6312 -2.8662,34.396 -4.2995,1.4332 -7.1659,-1.4332 -11.4654,-4.2995 -1.4331,0 -1.4331,-2.8663 -2.8663,-2.8663 -11.4654,1.4331 -28.6634,-1.4332 -34.3959,7.1658 -4.2997,8.599 21.4974,-2.8663 27.23,4.2995 5.7327,5.7327 1.4332,17.1979 -2.8663,24.3638 -11.4653,18.6312 -42.9949,5.7326 -58.7598,11.4653 -4.2995,1.4332 -15.7648,5.7327 -11.4653,8.599 1.4331,0 42.9949,-12.8985 41.5617,5.7327 -1.433,7.1658 -17.1979,-1.4332 -22.9305,2.8663 -4.2995,4.2995 11.4653,11.4654 17.198,8.599 2.8662,-2.8663 2.8662,-11.4653 7.1657,-11.4653 5.7327,-1.4332 11.4654,4.2995 17.198,8.599 20.0644,18.6312 -41.5617,8.599 0,15.7649 20.0644,2.8663 41.5618,-12.8986 63.0593,-4.2995 5.7327,1.433 -8.599,8.599 -10.0322,14.3315 -2.8663,10.0322 -1.4331,20.0644 -2.8663,31.5297 -4.2995,14.3317 -30.0964,8.599 -44.4281,15.7649 -5.7327,4.2995 -1.4332,18.6311 -8.599,21.4974 -25.7969,8.599 -12.8985,-14.3317 -28.6634,-4.2995 -15.7647,10.0322 -30.0964,5.7327 -45.8611,12.8985 -7.166,2.8663 -10.0322,14.3317 -18.6312,14.3317 -12.8985,0 -35.8292,-24.3639 -48.7276,-27.2302 -10.0322,-1.4332 -81.6905,15.7648 -85.99,0 -1.4332,-7.1657 18.6312,-1.4332 4.2995,-11.4654 -8.599,-5.7325 -4.2995,7.1659 -7.1659,11.4654 -2.8663,1.4332 -7.1658,-1.4332 -8.599,-4.2995 0,-4.2994 10.0322,-8.5989 5.7327,-8.5989 -4.2995,-1.4332 -8.599,8.5989 -12.8985,7.1657 -4.2995,-1.433 4.2995,-8.5989 1.4332,-12.8984 -10.0322,-12.8985 -4.2995,12.8984 -8.599,11.4654 -17.1981,-5.7327 -30.0965,-34.3961 -34.396,-30.0966 -5.7326,7.1659 11.4654,17.1979 10.0322,25.7969 -1.4332,14.3317 -81.6905,-4.2995 -84.5568,0 -4.2995,7.1659 10.0322,17.1981 8.599,25.7971 -1.4332,4.2995 -8.599,-1.4332 -12.8985,-1.4332 h -1.4332 c -7.1658,-4.2995 -30.0964,-20.0642 -37.2622,-17.198 -8.5991,4.2995 -5.7327,17.198 -10.0322,24.3638 -2.8664,2.8664 2.8663,-8.599 1.4331,-12.8985 -2.8663,-7.1658 -10.0321,-12.8985 -17.198,-18.6312 -18.6312,-18.631 -73.0914,-31.5295 -98.8884,-21.4974 0,0 -15.7648,68.792 -37.2623,78.8242 -17.1979,7.1657 -47.2945,-34.3961 -58.7598,-40.1287 -7.1659,-4.2995 14.3317,12.8985 -4.2995,8.599 -17.1979,-4.2995 -41.5618,-21.4974 -61.626,-15.7649 -7.166,1.4332 -4.2997,14.3317 -10.0322,17.198 -8.599,2.8664 -18.6312,-2.8663 -28.6634,-2.8663 -1.4332,0 -2.8663,0 -4.2995,1.4332 -2.8663,7.1658 -20.0642,42.9949 -25.797,42.9949 -8.5989,-1.4332 -4.2994,-20.0642 -10.0321,-27.2301 -8.599,-14.3317 -42.995,-4.2995 -42.995,-20.0643 0,-10.0322 57.3266,-12.8984 58.7599,-12.8984 5.7325,-1.4333 11.4652,-7.166 15.7647,-12.8987 2.8663,-1.433 0,-7.1657 2.8663,-8.5989 2.8664,-2.8663 8.5991,1.4332 11.4654,0 10.0322,-8.599 12.8985,-21.4975 22.9307,-28.6633 4.2995,-2.8664 11.4652,2.8663 15.7647,1.4331 4.2995,-2.8663 2.8664,-8.599 7.1659,-11.4653 7.1658,-7.1657 20.0643,-8.5989 27.2302,-17.1979 4.2995,-5.7327 2.8663,-38.6956 12.8985,-42.9951 5.7325,-1.4331 11.4652,10.0322 17.1979,11.4654 32.9627,2.8663 -5.7327,-47.2945 74.5246,-32.9628 27.2301,4.2994 63.0593,41.5618 90.2895,20.0643 8.5989,-7.1659 5.7327,-21.4974 10.0321,-30.0965 1.4331,-2.8663 70.2251,-45.8612 78.8241,-55.8934 8.599,-10.0322 -37.2622,20.0642 -42.9949,24.3639 -7.1659,2.8662 -14.3317,4.2994 -21.4975,5.7325 -4.2995,0 -7.1659,-7.1658 -11.4654,-5.7325 -35.8291,10.032 -27.2301,40.1286 -70.225,15.7647 -14.3317,-10.0322 -11.4655,-34.3959 -24.3639,-47.2944 -11.4653,-11.4654 -32.9627,14.3315 -47.2944,7.1658 -2.8665,-1.4332 -5.7327,-45.8614 -7.1659,-50.1609 -1.4331,-2.8662 -2.8663,5.7327 -5.7326,5.7327 -10.0322,1.4333 2.8663,-10.0321 -31.5297,0 -4.2994,0 -2.8664,8.599 -5.7327,11.4653 -8.5989,2.8664 -20.0642,5.7327 -28.6633,2.8664 -7.1658,-1.4332 -20.0643,-10.0322 -17.198,-15.7649 4.2995,-8.5989 18.6312,-2.8662 27.2302,-4.2995 4.2995,-1.4332 -35.8292,-2.8662 -40.1287,-8.5989 -5.7327,-8.599 -2.8662,-20.0643 -2.8662,-30.0965 0,-28.6633 53.0271,-12.8984 60.1928,-15.7649 8.599,-2.8663 10.0322,-17.1979 20.0644,-18.6311 7.1658,0 14.3317,7.1659 21.4975,5.7327 22.9306,-5.7327 68.792,-44.4282 77.391,-64.4924 2.8662,-5.7327 -2.8665,-61.6262 -11.4654,-70.2252 -24.3638,-30.0965 -51.5939,55.8935 -61.6261,-2.8663 -2.8663,-10.0322 27.2301,-11.4654 45.8613,-25.797 2.8663,-4.2995 1.4332,-11.4654 4.2995,-15.7649 2.8663,-4.2994 5.7327,-7.1657 8.599,-8.5989 5.7327,-4.2995 11.4652,-5.7327 15.7647,-7.1658 10.0322,0 18.6312,4.2995 38.6956,-2.8664 2.8663,-1.4331 -5.7327,-11.4653 -1.4332,-10.0321 10.0322,1.4331 15.7649,12.8985 25.7969,14.3316 20.0644,2.8664 24.3639,-15.7648 38.6956,-2.8663 7.1658,7.1657 10.0322,28.6633 18.6312,22.9306 8.599,-5.7327 -7.1659,-21.4974 -4.2995,-31.5296 1.433,-5.7327 27.23,-2.8663 45.8612,20.0642 4.2995,4.2997 -11.4653,0 -17.198,-2.8662 -12.8985,-7.1658 -18.6312,-28.6633 -15.7647,-44.4282 1.433,-8.5989 10.032,-15.7647 17.1979,-21.4974 5.7327,-4.2995 20.0643,0 18.6312,-7.1659 -2.8664,-8.599 -20.0644,-8.599 -22.9307,-17.198 -18.6311,-40.1286 30.0965,-8.599 30.0965,-25.7969 1.4332,-8.599 -10.0322,-4.2995 -5.7327,-14.3317 4.2995,-10.0322 17.1981,-17.198 15.7649,-28.6633 -1.4332,-21.4976 -18.6312,17.198 -38.6956,18.6311 -12.8985,0 4.2996,-27.23 0,-38.6955 -2.8663,-5.7327 -10.0321,11.4655 -15.7648,10.0322 -17.1979,-5.7327 -2.8662,-21.4974 -4.2994,-30.0964 l -17.198,-35.8291 c -1.4332,-17.1981 45.8613,-64.4926 47.2944,-64.4926 2.8664,1.4333 1.4332,8.599 4.2995,7.1658 11.4654,-2.8663 27.2301,-5.7325 30.0966,-15.7647 2.8662,-8.599 -17.1981,0 -25.7971,-1.4332 -5.7326,-1.4331 -11.4653,-8.599 -18.6312,-7.1658 -8.599,2.8663 -12.8985,14.3317 -21.4975,17.1979 -7.1657,2.8663 -15.7647,-2.8662 -22.9306,-1.4332 -8.599,2.8665 -12.8985,10.0322 -20.0643,12.8985 -2.8663,1.4332 -7.1659,1.4332 -8.599,-1.4331 -1.4332,-4.2994 7.1658,-11.4654 2.8663,-12.8984 -4.2995,-1.4332 -2.8663,8.599 -5.7327,11.4652 -1.433,1.4332 -4.2995,0 -5.7325,-1.4332 -7.166,-8.599 -7.166,-30.0964 -22.9307,-32.9627 -20.0644,-4.2995 7.1658,20.0642 -7.1659,42.9949 -5.7326,8.599 -12.8985,-14.3317 -18.6311,-22.9307 -41.5618,-54.4603 1.4331,8.599 -54.4603,-24.3637 -14.3317,-8.599 20.0643,-1.4332 18.6312,-8.599 -1.4332,-32.9628 4.2995,-30.0966 24.3637,-53.0272 37.2624,-41.5617 -25.7969,7.1659 20.0644,-34.396 4.2995,-4.2994 14.3316,-4.2994 17.198,-10.0321 5.7327,-10.0321 -21.4975,-42.995 -22.9307,-45.8612 -1.4332,-4.2995 7.1658,-4.2995 8.599,-8.599 4.2995,-10.0322 0,-22.9307 4.2995,-34.3961 5.7327,-12.8986 17.198,1.4332 30.0964,7.1659 47.2946,21.4975 -24.3637,-12.8986 -24.3637,-15.7649 -4.2995,-8.5991 0,-20.0642 1.4331,-31.5296 0,-7.1658 5.7327,-15.7648 1.4332,-21.4975 -1.4332,-1.4332 -58.7598,30.0965 -63.0593,34.3959 -11.4653,10.0321 7.1659,30.0966 4.2995,45.8614 -1.4331,5.7327 -12.8985,4.2995 -17.198,8.599 -2.8663,2.8664 -5.7327,7.1659 -5.7327,11.4654 -1.4331,4.2995 1.4332,10.032 0,12.8984 -4.2995,11.4653 -15.7647,17.198 -20.0642,28.6633 -1.4333,2.8664 7.1657,8.599 4.2995,10.0322 -10.0322,5.7327 -22.9307,10.0322 -34.396,5.7327 -8.599,-4.2995 11.4653,-15.7649 15.7648,-22.9307 8.599,-11.4654 5.7327,-30.0964 14.3317,-41.5618 l 30.0964,-25.797 c -2.8663,-7.1658 -18.6312,2.8663 -21.4975,-4.2995 -2.8662,-5.7327 38.6955,-30.0964 18.6312,-34.3959 -8.599,-2.8664 -12.8985,18.631 -20.0644,14.3315 -8.599,-7.1657 20.0644,-12.8984 25.7971,-22.9305 5.7326,-11.4654 1.4331,-37.2624 5.7326,-47.2945 2.8664,-8.599 10.0322,-17.198 17.1979,-18.6312 7.166,-1.4331 20.0644,14.3317 20.0644,7.1659 0,-7.1659 -15.7649,-7.1659 -20.0644,-14.3317 -10.032,-20.0644 48.7278,-30.0964 50.1609,-30.0964 4.2995,-4.2995 -18.6312,1.433 -18.6312,-4.2995 0,-8.5992 14.3317,-10.0322 17.1981,-18.6312 2.8663,-4.2995 -8.5991,2.8663 -12.8986,7.1658 -10.0321,7.1657 -8.599,10.0322 -17.198,14.3317 -18.6312,10.0321 -35.8291,25.7969 -48.7276,20.0642 1.4332,2.8664 2.8663,4.2995 5.7327,5.7327 1.4332,2.8663 7.1658,5.7327 4.2995,8.599 -11.4654,10.0322 -68.792,30.0964 -78.8242,10.0322 -1.4331,-1.4332 0,-5.7327 1.4332,-5.7327 28.6634,-11.4653 5.7327,31.5296 41.5619,-17.198 22.9306,-30.0964 -67.3588,-14.3317 -2.8663,-35.8291 2.8663,5.7327 5.7325,11.4654 10.032,15.7649 1.4332,2.8662 4.2995,5.7325 7.1659,7.1657 -2.8664,-4.2995 -5.7327,-10.0322 -7.1659,-17.1979 -1.433,-4.2995 10.0322,0 14.3317,0 11.4654,0 22.9307,5.7327 32.9629,1.433 12.8985,-5.7325 -77.391,-4.2995 -65.9257,-21.4974 8.5991,-11.4653 45.8613,4.2995 48.7277,-1.4331 5.7326,-14.3317 -35.8291,-31.5296 4.2995,-41.5618 10.0321,-2.8663 20.0642,10.0322 31.5297,7.1657 38.6954,-8.5989 -10.0322,-34.3959 -10.0322,-34.3959 4.2995,-10.0322 25.7969,2.8663 32.9627,-5.7327 7.1659,-8.599 1.4332,-22.9307 1.4332,-34.3959 0,-8.599 1.4332,-17.198 -1.4332,-22.9307 -1.4331,-2.8663 -48.7276,-53.0271 -17.198,-65.9256 7.1659,-4.2995 8.599,22.9307 17.198,20.0643 11.4654,-2.8663 -4.2995,-35.8292 12.8986,-25.797 22.9305,12.8985 24.3637,14.3317 24.3637,12.8985 4.2995,-18.6312 -11.4652,-20.0642 -10.0322,-38.6954 0,-7.1659 18.6312,8.599 21.4976,1.433 5.7326,-10.032 -7.1659,-22.9305 -5.7327,-32.9627 0,-4.2995 4.2995,7.1658 8.599,7.1658 10.0322,4.2995 25.797,12.8985 32.9629,4.2995 5.7325,-7.1658 -14.3317,-14.3317 -17.1981,-22.9307 -7.1658,-24.3637 14.3317,-5.7325 14.3317,-5.7325 2.8664,-2.8665 -4.2995,-8.599 -2.8663,-11.4654 2.8663,-1.4331 10.0322,5.7327 10.0322,2.8664 0,-4.2995 -10.0322,-7.1659 -8.599,-11.4654 1.4331,-10.0322 10.032,-17.198 15.7647,-25.797 z m 421.6906,-262.675 c 2.9907,2.103 2.7244,-0.7325 3.9597,1.8388 1.3878,2.8888 2.7299,2.6131 2.5377,-2.6496 -0.2081,-2.7926 1.7617,-3.0831 8.9275,-1.6499 4.2995,1.4333 -7.1658,7.166 -10.032,11.4654 0,1.4331 5.7325,5.7326 4.2995,7.1658 -2.8665,1.4332 -8.599,-5.7327 -10.0322,-1.4332 0,2.8664 8.5989,2.8664 8.5989,7.1659 1.4333,12.8985 -17.1979,8.599 -15.7647,12.8985 1.4331,18.6312 15.6514,16.4032 -13.012,46.4998 -1.4331,2.8663 -6.5543,-1.3185 -5.1211,-4.1848 8.599,-18.6312 19.5662,-38.0155 12.4004,-42.315 -5.7327,-5.7327 -8.4407,8.0069 -15.6065,3.7074 -10.0322,-4.2995 -4.9978,-4.2318 -7.864,-14.264 -1.4333,-2.8663 4.613,-6.1584 7.4793,-7.5916 11.4654,-1.4332 9.1889,1.3057 19.2211,-2.9938 7.1658,-4.2995 5.709,-6.4937 10.0084,-13.6597 z m -188.0845,249.7765 c -18.6312,37.2623 -104.6212,-42.995 -44.4282,-47.2945 8.599,0 17.198,10.0322 17.198,20.0644 1.4332,5.7327 -17.198,4.2995 -14.3317,8.599 4.2995,8.599 17.198,8.599 25.797,11.4652 4.2995,2.8665 10.0322,4.2995 15.7649,7.1659 z m -355.4252,30.0965 c -7.1657,11.4654 -27.2301,22.9307 -30.0964,31.5296 -1.4332,7.1658 -2.8663,47.2944 -21.4975,47.2944 -10.0322,-1.433 -8.5989,-17.1979 -15.7649,-24.3637 -8.5989,-5.7327 2.8665,24.3637 -5.7325,28.6634 -4.2997,1.433 -10.0322,-5.7327 -12.8986,-2.8664 -10.0321,5.7327 -11.4653,25.7969 -22.9306,25.7969 -10.0322,0 -18.6311,-17.198 -12.8986,-25.7969 5.7327,-10.0322 30.0966,7.1659 34.3961,-4.2995 4.2995,-10.0322 -20.0644,-11.4653 -24.3639,-22.9307 -1.4332,-4.2995 10.0322,-2.8663 12.8985,-5.7326 1.4332,-1.4332 -4.2995,0 -4.2995,-1.4332 -2.8663,-5.7327 -4.2995,-11.4654 -2.8663,-17.1979 12.8985,-35.8292 22.9307,-2.8665 42.9949,1.433 1.4332,0 -7.1657,-21.4974 -2.8662,-25.7969 14.3316,-17.198 14.3316,-1.4332 28.6633,-2.8663 1.4331,-1.4332 1.4331,-5.7327 2.8663,-5.7327 8.599,-7.1658 17.198,-18.6312 27.2302,-17.198 7.1657,1.4332 4.2995,14.3317 7.1657,21.4975 z"
+ class="eu europe"
+ id="gb-main" /></g><path
+ id="ie"
+ class="eu europe"
+ d="m 4406.2301,4375.125 c 2.8663,7.1658 15.7648,14.3317 10.0322,20.0642 -5.7327,8.599 -22.9306,1.4333 -31.5296,7.166 -4.2995,4.2994 11.4652,7.1657 11.4652,12.8984 1.4333,11.4653 -5.7327,22.9307 -5.7327,34.396 1.4333,10.0322 11.4654,15.7649 11.4654,25.7969 0,7.1659 -11.4654,11.4654 -11.4654,20.0644 -1.433,5.7327 10.0322,11.4653 7.166,17.198 -4.2995,5.7327 -15.7649,-7.1658 -18.6312,-1.4332 -4.2995,5.7327 7.1657,11.4654 8.599,18.6311 0,2.8665 -2.8663,5.7327 -2.8663,8.599 1.433,18.6312 7.1658,30.0966 1.433,48.7278 -4.2994,12.8984 -54.4601,85.9898 -63.0591,93.1557 -2.8664,2.8663 -10.0322,4.2995 -11.4654,10.0321 -2.8663,10.0322 1.4332,21.4976 0,31.5296 0,1.4332 -2.8663,1.4332 -2.8663,0 -4.2995,-10.032 -7.1659,2.8664 -21.4975,-2.8663 -5.7327,-1.4332 -5.7327,-10.0322 -11.4654,-11.4652 -10.0322,-2.8665 -41.5618,12.8984 -48.7276,4.2995 -1.4332,-2.8665 5.7327,-2.8665 5.7327,-5.7327 1.4331,-4.2995 -10.0322,4.2995 -14.3317,2.8662 -21.4974,-1.433 -28.6633,-12.8984 -48.7276,-5.7325 -2.8664,1.433 5.7326,7.1658 5.7326,11.4652 -1.4331,7.1658 -27.23,12.8985 -27.23,12.8985 -5.7327,0 -8.5992,-8.599 -14.3317,-7.1658 -10.0322,4.2995 -14.3317,17.198 -22.9307,22.9307 -30.0964,15.7648 -11.4654,-27.2301 -38.6954,-22.9307 -8.5991,1.4331 7.1658,17.198 4.2995,24.3638 -4.2995,5.7327 -14.3317,5.7327 -21.4976,10.0322 -4.2995,2.8663 -7.1658,11.4654 -12.8985,12.8984 -2.8663,0 -1.4331,-5.7327 -4.2995,-7.1657 -2.8663,-2.8664 -7.1658,-5.7327 -11.4652,-4.2995 -2.8665,2.8663 0,11.4652 -4.2995,12.8985 -14.3317,1.4332 -40.1287,-15.7649 -48.7276,-14.3317 -11.4655,1.4332 -20.0644,10.0322 -30.0966,11.4654 -28.6633,2.8663 15.7649,-20.0644 -7.1658,-15.7649 -8.599,2.8663 -42.9949,7.1658 -45.8613,4.2995 -5.7327,-8.599 48.7276,-24.3638 54.4603,-37.2624 1.4332,-2.8663 -4.2995,-2.8663 -7.1658,-4.2995 -1.4332,-2.8662 -34.3961,4.2995 -75.9578,0 -1.4332,0 2.8663,-1.433 21.4975,-7.1657 2.8663,0 5.7325,-1.4333 5.7325,-4.2995 0,-1.4332 -4.2994,-2.8663 -2.8662,-4.2995 12.8984,-11.4654 7.1659,7.1657 24.3638,0 8.599,-2.8663 32.9628,-4.2995 27.2302,-11.4654 -1.4332,-1.4331 -65.9257,7.1659 -68.792,7.1659 -12.8985,-2.8664 -22.9307,-22.9307 -21.4975,-37.2624 1.4331,-5.7327 12.8985,5.7327 17.198,2.8665 4.2995,-2.8665 -2.8663,-7.166 -2.8663,-11.4655 2.8663,-12.8984 25.797,-5.7327 38.6955,-8.5989 10.0321,-1.4332 22.9306,0 30.0964,-7.1658 5.7327,-4.2995 -14.3317,-14.3317 -21.4975,-10.0322 -1.4332,2.8663 2.8663,8.599 0,10.0322 -1.4332,1.4331 -44.4281,-20.0644 -53.0271,-31.5297 -1.4332,-2.8664 8.599,4.2995 11.4653,2.8663 2.8664,-1.4332 0,-7.1657 2.8664,-8.599 7.1658,-4.2995 27.2302,-2.8662 37.2622,-1.4332 12.8985,0 21.4976,12.8985 32.9629,18.6312 11.4653,5.7327 -21.4975,-22.9307 -15.7649,-34.3959 4.2996,-10.0322 21.4976,-2.8664 31.5296,-7.1659 5.7327,-2.8663 5.7327,-11.4653 11.4654,-12.8985 1.4333,0 93.1558,15.7649 103.188,10.0322 7.1658,-4.2995 -18.6312,-2.8663 -22.9307,-10.0322 -1.4332,-4.2995 4.2995,-11.4653 2.8663,-15.7648 -1.4331,-2.8664 -30.0964,25.797 -44.4281,24.3638 -2.8663,-1.4331 8.599,-8.599 4.2995,-10.0321 -4.2995,-1.4332 -10.0321,4.2995 -15.7648,2.8663 -5.7327,-1.4332 -10.0322,-5.7327 -12.8985,-10.0322 -1.4332,-2.8663 1.4331,-5.7327 0,-7.1658 0,0 -41.5618,8.599 -47.2945,5.7326 -4.2995,-1.4331 10.0322,0 14.3317,-1.4331 11.4652,-5.7327 20.0644,-12.8984 31.5296,-17.1979 2.8663,-2.8665 8.599,1.433 11.4653,0 5.7327,-5.7327 7.1659,-15.7649 14.3317,-22.9307 2.8664,-2.8664 7.1659,-4.2995 11.4654,-5.7327 2.8663,-2.8663 -10.0322,0 -12.8985,-4.2995 -5.7327,-7.1658 12.8985,-12.8985 18.631,-18.6312 4.2995,-5.7327 7.1659,-12.8985 14.3317,-14.3317 4.2995,-1.433 5.7327,10.0322 10.0322,8.599 11.4653,0 22.9307,-4.2995 32.9629,-10.032 2.8663,-1.4332 -11.4654,1.433 -11.4654,-2.8665 0,-4.2994 10.0322,-2.8662 10.0322,-7.1657 -1.4332,-4.2995 -30.0965,1.4332 -61.6261,-7.1658 -30.0966,-8.5991 0,-7.1659 -7.1659,-31.5296 -7.1658,-25.7971 -47.2944,-7.166 -58.7598,-24.3639 -31.5297,-41.5619 27.2301,-24.3639 28.6633,-24.3639 1.4332,-1.4331 -5.7327,-5.7326 -4.2995,-7.1658 5.7327,-10.0322 12.8985,-20.0642 24.3639,-22.9306 10.0321,-2.8663 21.4975,5.7327 32.9627,5.7327 2.8665,0 5.7327,-1.4333 7.1659,-4.2995 18.6311,-44.4282 -34.396,-11.4654 -37.2623,-24.3639 -2.8663,-5.7326 12.8985,0 17.198,-4.2995 8.5989,-5.7326 -5.7327,-20.0643 -7.1658,-21.4975 0,-10.0322 12.8985,-22.9306 5.7325,-31.5296 -5.7325,-5.7327 -17.1979,15.7649 -22.9306,10.0322 -5.7326,-5.7327 8.5991,-12.8985 11.4654,-20.0644 1.4332,-5.7326 -12.8985,-4.2995 0,-7.1658 12.8985,-2.8663 80.2572,-2.8663 87.423,15.7648 4.2995,10.0322 -1.433,25.7971 7.1659,28.6633 8.599,2.8663 8.599,-14.3316 15.7648,-18.6311 12.8985,-7.1658 50.1608,35.8291 51.594,31.5296 4.2995,-10.0322 -10.0322,-21.4974 -8.599,-32.9628 2.8663,-12.8985 45.8612,-8.599 45.8612,-10.0321 11.4654,-4.2995 20.0644,-12.8985 27.2302,-21.4975 4.2995,-5.7327 -21.4975,0 -21.4975,-2.8664 -4.2995,-15.7647 0,5.7327 -18.6311,4.2995 -2.8663,0 1.4332,-5.7327 1.4332,-8.599 0,-1.4332 0,-4.2995 -1.4332,-4.2995 -5.7326,-2.8662 -34.396,-4.2994 -30.0965,-21.4974 8.599,-28.6634 37.2624,2.8663 45.8614,0 5.7327,-2.8663 -17.198,-11.4654 -12.8985,-17.198 5.7325,-5.7327 17.198,5.7326 24.3637,4.2995 7.1659,-1.4332 -5.7325,-11.4654 -10.0322,-15.7649 -2.8663,-2.8663 10.0322,-2.8663 11.4654,-7.1658 5.7327,-24.3638 -55.8935,0 18.6312,-34.3959 4.2995,-1.4332 10.0322,2.8663 15.7648,4.2995 20.0644,5.7326 42.995,4.2995 60.193,15.7648 10.0322,7.1657 -25.797,21.4974 -20.0644,31.5296 5.7327,5.7327 17.198,-4.2995 24.3639,-11.4653 2.8663,-2.8664 -4.2995,-15.7649 1.4332,-15.7649 8.599,2.8663 14.3315,14.3317 20.0642,21.4975 v 0 c -31.5296,0 -22.9306,35.8291 -45.8613,50.1608 -4.2995,2.8663 -41.5617,-8.5989 -38.6954,0 1.4332,8.599 20.0642,12.8985 17.1979,21.4975 0,0 -55.8933,14.3317 -57.3266,14.3317 -5.7327,7.1659 50.1609,81.6905 71.6583,80.2572 65.9256,-5.7327 -12.8984,-34.3959 48.7276,-42.9949 15.7648,-2.8664 8.599,31.5297 15.7648,45.8614 2.8664,5.7325 17.1981,2.8662 15.7649,12.8984 -2.8663,7.1658 -12.8985,20.0643 -5.7327,21.4975 48.7276,17.198 18.6312,-15.7649 40.1286,-2.8663 l 5.7327,7.1658 c -1.4332,-1.4332 -2.8663,-2.8663 -4.2995,-4.2995 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="nl"
+ class="eu europe"
+ d="m 5655.9506,4754.9139 -25.7969,19.779 c -14.3317,50.1608 48.7276,26.0823 25.7969,43.2803 -10.0322,7.1657 -25.7969,4.2995 -31.5296,14.3317 -7.1658,10.0322 15.529,19.1809 6.93,27.7799 -2.8662,4.2996 -21.2617,13.7819 -18.3953,18.0814 0,0 17.9643,6.8323 17.9643,6.8323 8.599,0 40.2665,-57.9742 63.6283,-44.0947 8.7124,5.1761 -2.7685,-4.2994 -5.6348,-14.3316 -2.8664,-10.0322 20.0642,-20.0643 15.7647,-30.0965 -5.7327,-12.8985 -40.1286,4.2995 -40.1286,-8.599 1.4332,-10.0322 7.1658,-18.6311 8.599,-28.6633 0,-2.8663 -2.8663,-5.7327 -4.2995,-8.599 -1.4332,-2.8663 -17.4834,12.8426 -11.7507,5.6768 l -28.3779,17.2539 c -10.0322,2.8662 -21.4975,-1.4333 -31.5297,-1.4333 -4.2995,0 -8.599,-2.8662 -10.0322,-1.433 -18.6312,22.9305 -12.8985,68.7918 -21.4974,93.1557 -8.599,21.4975 -25.797,45.8613 -41.5619,63.0593 -5.7325,7.1658 -10.032,15.7648 -17.198,20.0643 -4.2994,2.8664 -14.3316,-2.8663 -15.7647,2.8664 -2.8664,8.599 10.032,20.0642 5.7326,28.6632 -14.3316,22.9307 -58.7597,11.4654 -48.7277,17.198 11.4654,7.1659 20.0644,17.1979 32.9629,22.9306 5.7327,1.4333 11.4653,-7.1657 15.7648,-5.7327 5.7327,1.4333 15.7649,10.0322 11.4654,14.3317 -7.166,4.2995 -25.797,-11.4652 -25.797,-2.8663 1.4331,12.8985 38.6954,11.4653 31.5297,21.4975 -21.4976,28.6634 -41.5619,-24.3637 -50.1609,-25.7969 -27.2301,-7.166 -53.0271,-2.8663 -48.7276,7.1657 0,1.4332 44.4281,31.5297 53.0271,30.0966 2.8663,0 1.4332,-5.7327 4.2995,-8.5991 5.7327,-4.2995 11.4653,5.7327 17.198,7.1659 5.7327,2.8663 14.3317,-2.8663 20.0644,2.8663 2.8663,2.8664 5.7325,7.1659 7.1657,11.4654 4.2995,1.4331 7.1658,2.8663 11.4653,1.4331 10.0322,0 -17.198,-32.9628 17.1981,-22.9307 2.8663,0 -2.8664,7.1659 -1.4332,8.5991 17.198,14.3316 21.4975,-20.0644 34.3959,-10.0322 4.2995,4.2995 -11.4652,12.8985 -7.1657,15.7648 32.9627,17.1979 4.2995,-4.2995 30.0964,-10.0321 4.2995,-1.4332 11.4654,40.1287 21.4975,41.5617 22.9306,4.2995 53.0271,2.8664 71.6582,27.2302 1.4333,2.8664 -5.7325,5.7327 -7.1657,8.599 -2.8663,8.5989 -10.0322,15.7649 -11.4654,24.3638 0,2.8663 7.1659,1.4331 5.7327,4.2995 -1.4332,4.2995 -8.599,5.7327 -10.0322,11.4653 -10.0321,35.8291 14.3317,25.7971 37.2623,27.2302 h 1.4332 c -2.8664,-4.2995 -4.2995,-8.599 -4.2995,-10.0322 1.4331,-5.7326 11.4653,-5.7326 12.8985,-11.4653 4.2995,-24.3639 -37.2623,-32.9629 -24.3639,-35.8291 4.2995,-1.4333 7.166,4.2994 10.0322,4.2994 0,0 27.2302,-24.3638 27.2302,-24.3638 1.4332,-7.1658 -17.198,1.4332 -11.4653,-10.0321 4.2995,-10.0322 14.3316,-17.1979 18.6311,-27.2301 12.8984,-31.5297 -18.6311,-54.4604 -27.2302,-70.2253 -1.4331,-2.8662 1.4332,-7.1657 2.8664,-10.032 0,-2.8664 -2.8664,-7.1659 0,-8.599 7.1658,-4.2995 15.7648,0 24.3638,-2.8664 2.8664,-1.4331 -2.8663,-10.0321 0,-8.599 8.5989,2.8664 14.3317,12.8985 24.3638,12.8985 10.0322,1.4332 48.7277,-11.4653 53.0272,-21.4975 4.2994,-10.0322 -27.2302,-11.4652 -14.3317,-20.0642 22.9306,-15.7649 50.1608,-21.4976 44.4281,-57.3268 -1.4331,-20.0642 -54.4602,-11.4652 -41.5617,-32.9627 1.4331,-4.2995 8.599,0 8.599,-2.8664 1.433,-4.2995 -5.7327,-8.599 -4.2995,-12.8985 0,-2.8663 4.2995,-2.8663 7.1658,-2.8663 10.0321,0 18.6311,4.2995 28.6633,4.2995 11.4653,0 1.4331,-22.9306 5.7326,-34.3959 2.8664,-10.0322 27.2301,-55.8935 24.3639,-80.2573 -28.6634,14.3316 -31.5297,-38.6955 -55.8936,-44.4282 -21.4974,-4.2995 -134.7174,20.0644 -147.616,40.1287 -7.1658,8.599 -10.0321,17.198 -15.7648,24.3638 z m -250.804,303.8311 c -2.8663,37.2623 27.2302,12.8985 48.7276,22.9306 2.8663,1.4333 -2.8662,7.166 0,10.0322 17.198,11.4653 31.5297,-8.5989 42.995,-18.6311 -7.1658,-7.1658 -15.7648,-12.8985 -18.6311,-14.3317 -7.1659,-1.4331 -8.599,11.4654 -15.7649,11.4654 -24.3637,0 -8.599,-14.3317 -37.2623,-18.6312 -7.1658,-1.4332 -14.3316,-1.4332 -22.9306,1.4332 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccssscccccscccsccccsccccccccscccccccccccscccsccccccscscccscccccssccccccscccsccc" /><path
+ id="be"
+ class="eu europe"
+ d="m 5671.7154,5427.0686 c -4.2995,-1.4332 -8.599,-2.8663 -12.8985,-2.8663 -10.0321,-1.4332 -20.0642,8.599 -28.6632,4.2995 -7.1659,-2.8664 -1.4332,-15.7649 -5.7327,-21.4976 -2.8663,-4.2995 -10.0322,-1.4331 -14.3317,-4.2995 -1.4332,-1.4331 5.7327,-4.2995 5.7327,-5.7325 -1.4332,-2.8665 -2.8663,-5.7327 -5.7327,-7.166 -2.8663,-1.433 -7.1658,4.2995 -8.599,2.8665 -5.7327,-4.2995 -7.1658,-12.8985 -12.8985,-17.198 -10.0322,-4.2996 -24.3639,0 -30.0964,-8.5991 -5.7327,-5.7326 5.7325,-14.3316 5.7325,-21.4975 -1.433,-5.7325 -10.032,-7.1658 -10.032,-12.8985 0,-7.1657 5.7326,-12.8984 7.1658,-18.6311 1.4332,-4.2995 8.599,-8.599 5.7327,-11.4653 -10.0322,-8.599 -18.6312,17.198 -28.6634,25.7969 -20.0642,18.6312 -67.3588,7.1658 -63.0593,-1.4332 4.2995,-7.1657 11.4654,-11.4652 11.4654,-18.631 0,-4.2995 -11.4654,-4.2995 -10.0322,-10.0322 0,-8.599 11.4653,-15.7649 11.4653,-24.3639 -2.8663,-24.3637 -61.6261,-10.0322 -64.4924,-21.4974 -1.4332,-5.7327 2.8663,-12.8985 1.4331,-18.6312 -4.2995,-22.9307 -35.8292,0 -44.4282,-20.0642 -5.7325,-11.4655 2.8665,-44.4282 -15.7647,-51.5941 -8.599,-4.2995 -15.7649,15.7649 -27.2302,15.7649 -31.5296,0 -32.9628,-54.4603 -35.8291,-74.5247 1.4332,-1.4331 1.4332,-1.4331 1.4332,-1.4331 25.7969,-7.1659 65.9256,-38.6955 98.8884,-48.7276 l 2.8663,5.7326 c -2.8663,37.2623 27.2302,12.8985 48.7276,22.9306 2.8663,1.4333 -2.8662,7.166 0,10.0322 17.198,11.4653 31.5297,-8.5989 42.995,-18.6311 8.5991,7.1657 17.1981,12.8984 20.0643,11.4652 4.2995,-2.8663 2.8663,-8.599 0,-14.3315 4.2995,1.4331 7.1658,2.8663 11.4653,1.4331 10.0322,0 -17.198,-32.9628 17.1981,-22.9307 2.8663,0 -2.8664,7.1659 -1.4332,8.5991 17.198,14.3316 21.4975,-20.0644 34.3959,-10.0322 4.2995,4.2995 -11.4652,12.8985 -7.1657,15.7648 32.9627,17.1979 4.2995,-4.2995 30.0964,-10.0321 4.2995,-1.4332 11.4654,40.1287 21.4975,41.5617 22.9306,4.2995 53.0271,2.8664 71.6582,27.2302 1.4333,2.8664 -5.7325,5.7327 -7.1657,8.599 -2.8663,8.5989 -10.0322,15.7649 -11.4654,24.3638 0,2.8663 7.1659,1.4331 5.7327,4.2995 -1.4332,4.2995 -8.599,5.7327 -10.0322,11.4653 -10.0321,35.8291 14.3317,25.7971 37.2623,27.2302 h 1.4332 c 5.7326,10.0321 14.3317,25.7969 15.7648,25.7969 2.8664,2.8664 11.4654,-2.8663 11.4654,1.4332 1.4331,5.7327 -10.0322,11.4654 -8.599,17.198 2.8663,8.599 15.7648,7.1659 20.0643,14.3317 15.7649,25.7969 -22.9307,30.0964 -32.9629,57.3266 -15.7648,-27.2302 -44.4281,25.7971 -50.1607,41.5618 -4.2995,12.8985 38.6954,68.792 7.1658,65.9256 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="lu"
+ class="eu europe"
+ d="m 5739.0742,5441.4003 c -7.1658,-2.8664 -15.7648,-7.1659 -24.3638,-7.1659 -7.1659,0 -14.3317,7.1659 -22.9306,7.1659 -1.4332,1.433 0,-4.2995 -1.4332,-5.7327 -5.7326,-2.8663 -11.4653,-5.7327 -18.6312,-8.599 l 1.4332,-5.7327 c 31.5296,2.8664 -11.4653,-53.0271 -7.1658,-65.9256 5.7326,-15.7647 34.3959,-68.792 50.1607,-41.5618 -1.4331,7.1659 -2.8663,14.3316 0,25.7971 7.1659,30.0964 41.5618,32.9627 44.4283,37.2622 20.0642,24.3638 -37.2624,35.8291 -21.4976,64.4925 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="de"
+ class="eu europe"
+ d="m 6657.7334,4638.8275 c 2.8664,22.9306 25.7969,67.3588 28.6634,81.6904 4.2995,34.396 -41.5619,57.3266 -31.5297,84.5568 5.7327,17.198 60.1929,24.3637 60.1929,45.8613 0,21.4975 -17.198,28.6633 -4.2995,44.4282 4.2995,7.1657 14.3317,10.032 17.1981,18.6311 4.2995,7.1658 -1.4332,17.198 0,24.3638 1.4331,4.2995 7.1658,2.8663 8.599,5.7327 7.1658,10.0322 -10.0322,34.3959 -14.3317,42.9949 -8.599,12.8985 12.8985,20.0644 17.198,32.9629 2.8662,7.1657 -7.1658,15.7647 -2.8663,21.4974 5.7325,8.599 18.631,5.7327 24.3637,11.4654 20.0644,18.6311 11.4654,123.2522 -5.7327,126.1185 -18.631,4.2995 -48.7276,-74.5246 -64.4923,-30.0965 -2.8663,7.1658 22.9306,12.8985 17.1979,17.198 -17.1979,14.3317 -41.5618,15.7649 -58.7598,25.797 -2.8663,1.4332 0,8.5991 -2.8663,10.0321 -11.4654,5.7327 -27.2301,-2.8662 -35.8291,4.2995 -7.1659,5.7327 -4.2995,17.198 -11.4654,22.9307 -2.8663,2.8664 -5.7326,-7.1658 -10.0321,-5.7327 -12.8986,2.8664 -14.3317,20.0644 -22.9306,25.7971 -4.2995,4.2995 -12.8985,-2.8664 -17.198,1.4331 -4.2995,2.8664 0,11.4654 -4.2995,15.7649 -5.7327,7.1657 -21.4976,-5.7327 -25.7971,-5.7327 -35.8291,5.7327 -41.5617,32.9628 -53.0271,55.8935 -2.8663,5.7325 -2.8663,-14.3317 -8.599,-18.6312 -4.2995,-5.7327 -12.8985,-7.1659 -20.0642,-8.599 -12.8986,-2.8664 14.3315,42.9949 42.9949,61.6261 2.8663,1.4332 12.8985,2.8663 14.3317,11.4653 4.2995,20.0644 -14.3317,12.8986 -17.198,28.6634 -1.4332,4.2994 30.0964,57.3265 34.3959,61.626 24.3638,24.3639 30.0965,7.166 45.8614,22.9307 10.032,10.0322 11.4652,25.797 21.4974,34.3959 22.9307,15.7649 84.5568,37.2624 83.1236,74.5247 -1.4331,38.6955 -10.0321,30.0965 -40.1287,22.9307 -2.8662,0 -5.7325,1.4331 -7.1657,4.2995 -2.8664,14.3317 5.7327,30.0964 -8.599,41.5617 -12.8985,10.0322 -73.0915,24.3639 -74.5247,47.2946 0,0 34.3961,47.2944 34.3961,48.7276 1.433,1.4332 -18.6312,18.6311 -11.4654,24.3639 15.7649,14.3315 31.5296,-7.166 18.6311,40.1286 0,2.8663 -2.8662,7.1658 -5.7327,7.1658 -45.8612,-12.8985 -10.032,-35.8292 -31.5296,-37.2624 -11.4653,0 -21.4975,10.0322 -32.9627,8.599 -5.7327,-1.4331 -4.2995,-11.4653 -8.599,-12.8985 -8.599,-2.8663 -17.198,4.2995 -24.3639,1.4332 -2.8663,-1.4332 2.8664,-10.0322 0,-10.0322 -17.198,1.4332 -1.4332,24.3639 -5.7327,25.797 -20.0643,5.7327 -53.0271,-2.8663 -71.6583,4.2995 -5.7326,2.8664 -2.8663,12.8986 -8.599,15.7649 -5.7325,4.2995 -12.8984,-2.8663 -18.631,1.4332 -4.2996,1.433 1.4331,8.599 -1.4332,11.4652 -1.4332,1.4333 -50.1608,14.3317 -55.8936,11.4654 -2.8662,-2.8664 0,-10.0321 -2.8662,-12.8986 -1.4332,-2.8663 -8.599,-2.8663 -10.0322,-5.7325 -1.4331,-2.8665 7.1659,-1.4332 5.7327,-4.2995 -5.7327,-18.6312 -45.8614,4.2995 -57.3266,-8.599 0,0 2.8663,-1.4332 2.8663,-2.8664 -8.599,-5.7327 0,18.6311 -2.8663,28.6633 0,5.7327 -27.2302,38.6955 -37.2624,28.6633 -5.7327,-5.7326 10.0322,-14.3316 5.7327,-20.0643 -1.4332,-1.4332 -5.7327,4.2995 -8.599,4.2995 -2.8664,0 -7.1659,-1.4332 -8.5989,-4.2995 -2.8665,-4.2995 7.1657,-10.0322 0,-15.7649 -5.7327,-4.2995 -8.599,-11.4652 -14.3317,-14.3315 -4.2995,-2.8664 -10.0322,4.2995 -15.7649,2.8663 -2.8663,-2.8663 0,-10.0322 -2.8663,-12.8985 -7.1658,-5.7327 -5.7327,20.0642 -28.6634,12.8985 -15.7647,-5.7327 -25.7969,-21.4975 -41.5617,-25.797 -1.4332,0 -48.7276,2.8663 -48.7276,2.8663 -5.7327,-2.8663 -5.7327,-12.8985 -11.4654,-15.7648 -1.4332,-2.8664 2.8663,7.1658 0,8.599 -2.8663,1.4331 -8.599,0 -10.0322,-2.8664 -1.4331,-4.2995 5.7327,-10.032 2.8664,-12.8985 -21.4976,-21.4974 -45.8613,17.198 -41.5618,18.6312 8.599,1.4332 17.198,-1.4332 25.7969,0 5.7327,1.4332 -10.0322,2.8663 -14.3316,5.7327 -2.8663,1.4331 1.4332,8.599 0,10.0322 -35.8292,18.631 -10.0321,-8.5991 -38.6955,-7.1659 -4.2995,0 -70.225,15.7649 -74.5245,7.1659 -1.4332,-2.8664 8.5989,-4.2996 7.1657,-5.7327 -12.8984,-10.0322 -18.6311,32.9627 -18.6311,-17.198 -1.4331,-20.0644 12.8984,-38.6955 14.3316,-58.7598 0,-2.8664 -5.7327,-2.8664 -5.7327,-7.1659 -1.433,-7.1658 1.4333,-14.3315 4.2995,-21.4974 0,0 20.0644,-32.9628 20.0644,-32.9628 1.4332,-8.5991 2.8663,-40.1287 7.1658,-50.1608 11.4654,-22.9307 40.1286,-40.1286 55.8935,-67.3588 5.7327,-10.0322 -80.2572,-20.0644 -85.9899,-24.3639 -4.2995,-4.2995 -1.4333,-12.8984 -5.7327,-17.1979 -7.1658,-5.7327 -53.0272,12.8984 -55.8934,8.599 -1.4332,-4.2995 4.2995,-10.0322 1.433,-12.8985 -42.9949,-38.6954 -14.3315,20.0644 -31.5296,1.4332 -14.3316,-14.3317 -21.4975,-47.2946 -32.9627,-54.4603 -4.2995,-4.2995 -12.8985,0 -15.7649,-4.2995 -15.7648,-28.6634 41.5618,-40.1287 21.4976,-64.4925 -2.8665,-4.2995 -37.2624,-7.1658 -44.4283,-37.2622 -12.8985,-53.0273 53.0272,-50.1609 32.9629,-83.1237 -4.2995,-7.1658 -17.198,-5.7327 -20.0643,-14.3317 -1.4332,-5.7326 10.0321,-11.4653 8.599,-17.198 0,-4.2995 -8.599,1.4332 -11.4654,-1.4332 -1.4331,-1.4331 -21.4975,-31.5296 -20.0643,-35.8291 1.4331,-5.7326 11.4653,-5.7326 12.8985,-11.4653 4.2995,-24.3639 -37.2623,-32.9629 -24.3639,-35.8291 4.2995,-1.4333 7.166,4.2994 10.0322,4.2994 0,0 27.2302,-24.3638 27.2302,-24.3638 1.4332,-7.1658 -17.198,1.4332 -11.4653,-10.0321 4.2995,-10.0322 14.3316,-17.1979 18.6311,-27.2301 12.8984,-31.5297 -18.6311,-54.4604 -27.2302,-70.2253 -1.4331,-2.8662 1.4332,-7.1657 2.8664,-10.032 0,-2.8664 -2.8664,-7.1659 0,-8.599 7.1658,-4.2995 15.7648,0 24.3638,-2.8664 2.8664,-1.4331 -2.8663,-10.0321 0,-8.599 8.5989,2.8664 14.3317,12.8985 24.3638,12.8985 10.0322,1.4332 48.7277,-11.4653 53.0272,-21.4975 4.2994,-10.0322 -27.2302,-11.4652 -14.3317,-20.0642 22.9306,-15.7649 50.1608,-21.4976 44.4281,-57.3268 -1.4331,-20.0642 -54.4602,-11.4652 -41.5617,-32.9627 1.4331,-4.2995 8.599,0 8.599,-2.8664 1.433,-4.2995 -5.7327,-8.599 -4.2995,-12.8985 0,-2.8663 4.2995,-2.8663 7.1658,-2.8663 10.0321,0 18.6311,4.2995 28.6633,4.2995 11.4653,0 1.4331,-22.9306 5.7326,-34.3959 2.8664,-10.0322 27.2301,-55.8935 24.3639,-80.2573 4.2995,-1.4332 8.5989,-4.2995 12.8984,-10.0322 5.7327,-5.7327 -7.1657,-2.8664 -20.0642,-2.8664 0,0 0,0 0,0 -2.8664,-2.8663 -7.1659,-1.4331 -10.0322,-1.4331 -2.8664,0 -4.2995,-1.4332 -5.7327,-2.8664 -11.4653,-14.3315 20.0644,-58.7596 31.5296,-64.4923 7.1658,-4.2995 15.7649,4.2994 22.9307,4.2994 107.4874,-4.2994 -111.7869,-12.8984 51.5939,-8.5989 1.4332,0 5.7327,1.433 7.1659,2.8662 8.599,15.7649 14.3317,81.6905 50.1609,67.3588 11.4652,-4.2995 -11.4655,-103.1879 21.4974,-103.1879 2.8663,0 2.8663,8.599 7.1658,10.0322 4.2995,2.8663 11.4654,2.8663 17.1979,2.8663 14.3317,2.8664 30.0966,-11.4653 42.9951,-5.7326 10.0322,4.2995 10.0322,18.631 15.7648,27.23 7.1659,11.4654 45.8613,84.5569 45.8613,35.8292 0,-10.0321 -21.4975,-7.1658 -30.0964,-15.7648 -14.3317,-12.8985 -11.4655,-45.8613 -38.6956,-50.1608 -4.2995,0 -37.2622,7.1659 -34.3959,-12.8985 1.4332,-12.8985 21.4974,-53.0271 8.5989,-68.792 -4.2995,-4.2995 -35.8291,11.4654 -40.1286,-4.2995 -1.4332,-2.8663 8.599,0 10.0322,-2.8663 2.8663,-2.8662 -10.0322,-8.5989 -7.1659,-8.5989 14.3317,-4.2995 38.6956,10.0322 42.995,-18.6312 0,-2.8663 -32.9628,-38.6954 -35.8291,-44.4281 -4.2995,-10.0322 -5.7327,-21.4975 -5.7327,-31.5297 10.0322,1.4332 27.2302,0 31.5296,0 18.6312,2.8663 37.2624,12.8985 55.8936,15.7649 -1.4332,4.2995 -1.4332,5.7326 1.433,4.2995 5.7328,-4.2995 8.599,-11.4654 14.3317,-12.8986 2.8663,0 -1.4332,7.1659 0,10.0322 4.2995,2.8664 10.0322,1.4332 14.3317,2.8664 10.0322,1.4331 20.0643,1.4331 27.2302,5.7326 4.2995,2.8664 5.7327,8.5989 5.7327,12.8984 0,1.4332 -2.8664,0 -2.8664,0 -2.8663,1.4332 -8.599,4.2995 -5.7327,5.7327 1.4332,2.8663 8.5991,-2.8663 10.0321,0 4.2995,7.1659 2.8665,15.7649 0,22.9307 -2.8662,7.1658 -22.9306,11.4653 -17.1979,18.6312 7.1658,10.0322 34.3959,-15.7649 35.8291,-2.8664 1.4332,8.5991 -14.3317,24.3638 -5.7327,27.2301 10.0322,4.2995 10.0322,-20.0642 20.0644,-21.4974 8.599,-1.4331 41.5619,27.2301 48.7276,24.3638 1.4332,0 30.0965,-32.9628 40.1287,-15.7649 20.0643,37.2624 -80.2573,75.9578 -25.797,81.6905 11.4653,1.4332 20.0643,-12.8985 31.5297,-12.8985 14.3316,1.4333 -1.4332,42.995 30.0964,21.4975 7.1658,-4.2995 11.4653,-40.1286 24.3639,-47.2944 18.631,-10.0322 38.6954,-14.3317 47.2944,5.7326 -1.4332,-4.2995 -2.8663,-11.4653 -1.4332,-12.8985 8.599,-10.0321 20.0644,-14.3317 27.2301,-22.9307 8.599,-10.032 10.0322,-25.7969 18.6312,-35.8291 2.8663,-2.8663 21.4975,4.2995 38.6954,10.0322 7.1659,-1.4332 14.3317,-2.8663 17.1981,-2.8663 10.0321,0 4.2995,20.0642 10.0321,28.6632 4.2995,5.7327 14.3317,2.8664 18.6312,8.599 8.599,7.1659 12.8984,27.2302 25.7969,25.7971 10.0322,0 21.4976,-7.1659 32.9629,-4.2995 4.2995,1.4331 -5.7327,8.599 -5.7327,12.8985 1.4332,11.4652 5.7327,22.9306 10.0322,34.3959 4.2995,11.4654 18.6311,15.7649 30.0964,20.0644 8.599,2.8663 14.3317,4.2995 18.6312,2.8663 z m -130.4181,-110.3537 c 2.8664,-12.8985 -2.8663,-28.6634 1.4332,-41.5619 4.2995,-12.8985 47.2944,24.3638 38.6954,11.4653 -5.7325,-8.599 -24.3637,-4.2995 -28.6632,-14.3316 -1.4332,-8.5991 11.4653,-15.7649 17.198,-22.9306 5.7327,-4.2995 -4.2995,17.1979 1.4332,18.6311 10.032,2.8663 20.0642,-1.4332 28.6632,1.4331 8.599,2.8664 -11.4653,14.3317 -8.599,22.9307 2.8664,10.0322 15.7649,11.4654 20.0644,21.4974 2.8663,2.8665 -15.7649,17.198 -38.6956,22.9307 -11.4652,4.2995 -21.4974,-12.8985 -31.5296,-20.0642 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="fr"
+ class="eu europe"
+ d="m 5301.9587,5103.1731 c 2.8663,20.0644 4.2995,74.5247 35.8291,74.5247 11.4653,0 18.6312,-20.0644 27.2302,-15.7649 18.6312,7.1659 10.0322,40.1286 15.7647,51.5941 8.599,20.0642 40.1287,-2.8665 44.4282,20.0642 1.4332,5.7327 -2.8663,12.8985 -1.4331,18.6312 2.8663,11.4652 61.6261,-2.8663 64.4924,21.4974 0,8.599 -11.4653,15.7649 -11.4653,24.3639 -1.4332,5.7327 10.0322,5.7327 10.0322,10.0322 0,7.1658 -7.1659,11.4653 -11.4654,18.631 -4.2995,8.599 42.9951,20.0644 63.0593,1.4332 10.0322,-8.5989 18.6312,-34.3959 28.6634,-25.7969 2.8663,2.8663 -4.2995,7.1658 -5.7327,11.4653 -1.4332,5.7327 -7.1658,11.4654 -7.1658,18.6311 0,5.7327 8.599,7.166 10.032,12.8985 0,7.1659 -11.4652,15.7649 -5.7325,21.4975 5.7325,8.5991 20.0642,4.2995 30.0964,8.5991 5.7327,4.2995 7.1658,12.8985 12.8985,17.198 1.4332,1.433 5.7327,-4.2995 8.599,-2.8665 2.8664,1.4333 4.2995,4.2995 5.7327,7.166 0,1.433 -7.1659,4.2994 -5.7327,5.7325 4.2995,2.8664 11.4654,0 14.3317,4.2995 4.2995,5.7327 -1.4332,18.6312 5.7327,21.4976 8.599,4.2995 18.6311,-5.7327 28.6632,-4.2995 4.2995,0 8.599,1.4331 12.8985,2.8663 7.1659,2.8663 12.8986,5.7327 18.6312,8.599 1.4332,1.4332 0,7.1657 1.4332,5.7327 8.5989,0 15.7647,-7.1659 22.9306,-7.1659 8.599,0 17.198,4.2995 24.3638,7.1659 2.8664,4.2995 11.4654,0 15.7649,4.2995 11.4652,7.1657 18.6311,40.1286 32.9627,54.4603 17.1981,18.6312 -11.4653,-40.1286 31.5296,-1.4332 2.8665,2.8663 -2.8662,8.599 -1.433,12.8985 2.8662,4.2994 48.7276,-14.3317 55.8934,-8.599 4.2994,4.2995 1.4332,12.8984 5.7327,17.1979 5.7327,4.2995 91.7226,14.3317 85.9899,24.3639 -15.7649,27.2302 -44.4281,44.4281 -55.8935,67.3588 -4.2995,10.0321 -5.7326,41.5617 -7.1658,50.1608 l -20.0644,32.9628 c -2.8662,7.1659 -5.7325,14.3316 -4.2995,21.4974 0,4.2995 5.7327,4.2995 5.7327,7.1659 -1.4332,20.0643 -15.7647,38.6954 -14.3316,58.7598 0,18.6311 0,24.3638 1.4332,24.3638 -48.7276,50.1608 -31.5297,-4.2995 -71.6583,7.1659 -2.8663,1.4331 2.8663,7.1658 2.8663,10.032 0,1.4332 -2.8663,0 -2.8663,0 -4.2995,5.7327 -15.7649,11.4654 -12.8985,18.6312 1.4331,7.1659 22.9307,-7.1658 22.9307,0 -1.4332,20.0644 -8.599,4.2995 -15.7649,10.0322 -12.8985,10.0322 -24.3638,38.6954 -34.3959,47.2944 -8.599,10.0322 -28.6634,5.7327 -34.396,17.1981 -4.2995,10.0321 4.2995,22.9305 0,31.5297 -5.7327,15.7647 -31.5296,15.7647 -41.5618,30.0964 -17.198,21.4975 1.4332,41.5619 -5.7327,64.4924 -1.4332,4.2995 -15.7648,-2.8663 -15.7648,15.7649 0,2.8663 -7.1659,5.7327 -5.7327,5.7327 17.198,4.2995 37.2624,-2.8664 45.8614,-20.0644 1.433,-2.8663 -5.7327,-5.7327 -7.166,-8.599 0,-4.2995 -1.433,-7.1658 1.4333,-10.0322 12.8984,-20.0642 53.0271,-20.0642 71.6582,-11.4652 5.7327,1.433 -5.7327,10.0321 -4.2995,14.3316 0,7.1658 8.599,10.0321 10.0322,17.198 1.4331,8.599 -18.6311,7.1658 -10.0322,21.4975 2.8663,2.8662 11.4653,-1.4333 12.8985,2.8662 2.8663,4.2995 -4.2995,11.4654 -2.8663,15.7649 1.4331,2.8663 7.1658,-5.7327 8.599,-2.8664 7.1658,7.1659 7.1658,17.1981 12.8985,25.7971 -65.9256,38.6954 -28.6634,21.4975 -8.599,55.8934 2.8663,5.7327 -2.8664,15.7649 1.4331,21.4974 4.2996,10.0322 18.6312,11.4655 22.9307,20.0644 7.1659,12.8985 -38.6955,50.1609 -45.8612,51.5939 -4.2995,1.4332 -7.166,-7.1657 -11.4654,-7.1657 -7.1658,1.4332 -20.0643,1.4332 -20.0643,8.5989 -2.8664,22.9307 44.4281,41.5619 51.5939,53.0272 4.2995,8.5989 5.7327,20.0643 5.7327,31.5296 -1.4332,4.2995 -14.3317,-2.8663 -15.7649,1.4332 -1.4331,2.8663 2.8664,5.7327 1.4332,7.1658 -4.2995,7.1659 -11.4652,11.4654 -17.1979,17.198 -4.2995,8.5991 10.0321,12.8986 11.4652,17.1981 0,2.8662 -7.1658,2.8662 -7.1658,5.7325 -1.4332,20.0644 35.8292,47.2946 51.5939,51.5941 8.599,2.8662 18.6312,-1.4333 27.2302,-1.4333 8.599,-1.4332 18.6312,-7.1657 25.7971,-2.8662 21.4974,15.7647 -27.2302,48.7276 -20.0644,74.5245 -1.4332,0 -25.7969,8.599 -28.6634,11.4654 -1.433,1.4331 2.8665,7.1658 0,7.1658 -1.433,-1.4332 0,-7.1658 -1.433,-7.1658 -8.599,1.4331 -18.6312,2.8663 -24.3639,8.599 -4.2995,4.2995 2.8664,14.3316 -2.8663,18.6311 -5.7327,4.2996 -15.7649,-1.4331 -22.9307,2.8664 -5.7327,4.2995 -4.2995,15.7647 -11.4652,21.4974 -5.7327,2.8663 -12.8986,-4.2995 -18.6312,-1.4332 -4.2995,1.4332 -1.4332,8.599 -5.7327,12.8985 -4.2995,5.7327 -15.7649,7.1659 -18.6312,14.3317 -1.4332,5.7327 11.4654,14.3317 5.7327,18.6312 -7.1659,7.1659 -21.4974,0 -31.5296,1.4332 -12.8985,2.8662 -21.4975,15.7647 -32.9629,20.0642 -12.8985,4.2995 -2.8663,-15.7647 -31.5296,-17.198 -18.6312,-1.4332 1.4332,7.166 -1.4331,10.0322 -2.8664,2.8663 -8.599,2.8663 -12.8986,1.4333 -4.2995,0 -8.599,0 -10.0321,-2.8665 0,-2.8662 10.0321,-10.0322 -10.0322,-20.0642 -4.2995,-1.4332 -8.599,0 -14.3317,0 -10.0322,-2.8664 -21.4974,-1.4332 -28.6632,-8.599 -7.1659,-7.1659 0,-21.4976 -5.7327,-28.6634 -2.8664,-4.2995 -8.599,5.7327 -14.3317,5.7327 -8.599,0 -25.7969,2.8663 -24.3639,-5.7327 1.4333,-11.4652 27.2302,-7.1657 28.6634,-18.6311 0,-11.4653 -15.7647,-20.0643 -25.7969,-18.6312 -8.599,1.4332 7.1657,17.1981 2.8662,22.9306 -7.1657,7.166 -18.6311,11.4655 -28.6632,10.0322 -22.9306,-1.4332 -15.7649,-41.5618 -20.0644,-35.8291 -20.0642,22.9307 24.3639,32.9629 11.4654,38.6954 -45.8613,18.6312 -25.7971,-11.4652 -27.2301,-17.1979 -1.4332,-5.7327 -51.5941,-11.4653 -55.8936,-14.3317 -11.4652,-5.7326 -25.7969,-24.3638 -40.1286,-15.7648 -15.7647,8.599 -38.6954,37.2624 -54.4603,45.8614 -14.3317,7.1657 -42.9949,1.433 -60.1929,25.7969 -2.8664,2.8663 4.2995,8.599 4.2995,12.8985 -2.8664,14.3317 -21.4976,22.9306 -22.9307,38.6954 0,4.2995 10.0322,2.8664 11.4653,7.1659 7.1659,25.797 -11.4653,18.6312 -1.4331,47.2944 2.8663,8.599 11.4653,15.7649 20.0643,22.9307 h -5.7327 c -22.9307,-5.7327 -48.7276,-18.6312 -70.2251,-4.2995 -4.2995,2.8663 1.4332,14.3317 -4.2995,15.7649 -14.3317,4.2995 -38.6954,-24.3639 -53.0271,-24.3639 -12.8985,0 -22.9307,7.1658 -34.3959,8.599 -8.5992,1.4332 -10.0322,-14.3317 -12.8986,-17.198 -7.1658,-5.7327 -18.6311,-8.599 -28.6633,-12.8984 14.3317,-18.6312 11.4653,-37.2624 -25.7969,-34.3961 -1.4332,0 -5.7327,5.7327 -8.5991,12.8986 v -1.4332 c -7.1658,-4.2995 -2.8663,-17.198 -10.0321,-22.9307 -4.2995,-4.2995 -12.8985,-4.2995 -20.0644,-5.7327 -2.8663,0 -7.1658,1.4332 -8.599,0 -15.7647,-14.3315 -30.0964,-31.5296 -54.4603,-35.8291 -17.198,-2.8663 -10.032,30.0966 -10.032,30.0966 -15.7649,-1.4333 -28.6634,-12.8987 -42.9951,-11.4655 -4.2995,0 -4.2995,8.599 -8.599,8.599 -8.599,-1.433 -12.8984,-11.4652 -21.4974,-12.8984 -8.599,-1.4333 -14.3317,7.1659 -22.9307,5.7327 -18.6312,0 -11.4653,-18.6312 -21.4975,-27.2302 -20.0643,-17.198 -38.6955,8.599 -57.3266,-5.7327 -1.4332,0 -15.7649,-32.9627 -17.1981,-34.3959 -18.631,-14.3317 -50.1606,-7.1659 -70.225,-25.797 -2.8663,-2.8664 1.4332,-10.0322 0,-12.8985 -2.8663,-2.8664 -11.4653,12.8985 -20.0643,10.0321 -18.6312,-8.599 22.9305,-25.797 10.0321,-40.1286 -4.2995,-5.7326 -12.8985,-8.599 -20.0643,-10.0322 -1.4332,0 0,7.1659 -2.8664,5.7327 0,0 -15.7648,-14.3317 -24.3637,-22.9307 8.5989,-1.4331 14.3316,-2.8663 20.0642,-5.7326 41.5619,-20.0643 58.7598,-157.6482 74.5247,-200.6433 2.8663,-7.1657 5.7326,-17.1979 12.8985,-20.0642 5.7327,-2.8663 18.6312,10.0322 21.4974,2.8663 4.2995,-7.1658 -5.7327,-18.6312 -14.3316,-20.0643 -7.1658,-1.4332 -8.599,18.6312 -15.7648,15.7648 -14.3317,-4.2995 7.1658,-27.2302 10.0322,-41.5619 5.7326,-22.9305 21.4975,-107.4874 37.2622,-118.9527 5.7327,-2.8662 25.797,28.6634 31.5297,42.995 20.0644,42.995 -7.1658,2.8664 7.1659,48.7277 2.8663,8.599 11.4652,15.7648 14.3315,24.3638 1.4332,4.2994 -4.2995,17.1979 0,15.7648 7.1659,-1.4332 11.4654,-11.4654 10.0322,-17.1981 -2.8663,-8.5989 -12.8985,-11.4652 -18.6312,-18.631 -11.4652,-18.6312 4.2995,-53.0272 -7.1657,-74.5247 -5.7327,-11.4653 -15.7649,-21.4975 -24.3639,-31.5297 -5.7326,-7.1657 -12.8985,-14.3316 -21.4975,-20.0642 v 0 c 8.599,2.8663 18.6312,5.7326 27.2302,1.4331 10.0322,-5.7326 -21.4975,-20.0643 -18.6312,-31.5297 2.8664,-11.4652 15.7649,-17.1979 17.198,-28.6632 1.4332,-8.599 -8.599,-15.7649 -8.599,-24.3639 0,-10.032 8.599,-20.0642 8.599,-30.0964 0,-2.8663 -7.1658,-2.8663 -11.4653,-1.4332 -2.8663,1.4332 0,10.0322 -2.8663,8.5989 -7.1659,-2.8662 -8.5989,-14.3315 -14.3316,-18.631 -4.2995,-1.4332 -10.0322,4.2995 -14.3317,1.4331 -4.2995,-2.8663 0,-12.8985 -4.2995,-15.7648 -25.797,-24.3639 -11.4653,28.6633 -35.8292,-18.6312 -1.4332,-4.2994 -2.8662,-35.8291 -8.599,-42.9949 -1.4332,-1.4332 -5.7327,0 -7.1659,-1.4332 -5.7325,-8.599 -12.8984,-18.6312 -17.1979,-28.6633 -5.7326,-14.3317 22.9306,-28.6633 25.7971,-34.396 5.7325,-12.8985 -28.6634,-21.4974 -28.6634,-22.9306 -2.8663,-10.0322 -2.8663,-15.7648 -1.4332,-20.0643 h -1.4331 c -58.7598,-5.7327 -4.2995,-20.0644 -2.8664,-51.594 1.4332,-18.6312 -58.7598,-2.8663 -60.1929,-10.0322 -2.8664,-11.4653 14.3316,-15.7648 22.9307,-21.4975 1.433,-1.4332 -7.1659,2.8663 -8.5991,1.4332 -1.4331,-1.4332 0,-5.7327 -1.4331,-7.1659 -4.2995,0 -5.7327,4.2995 -10.0322,5.7327 -11.4653,0 -22.9307,-8.599 -32.9629,-4.2995 -1.433,1.4332 -2.8663,2.8663 -2.8663,4.2995 v -1.4332 c 2.8663,-18.631 17.198,-37.2622 -12.8984,-40.1286 -15.7649,-1.4331 -11.4654,18.6312 -22.9307,11.4654 -8.599,-5.7327 -20.0642,-24.3639 -28.6634,-28.6634 -5.7325,-1.4331 -12.8984,4.2995 -17.1979,0 -18.6312,-14.3317 4.2995,-31.5296 -31.5297,-28.6632 -2.8663,1.4331 2.8664,8.599 0,10.032 -10.032,4.2995 -24.3637,12.8986 -32.9627,5.7327 -8.599,-5.7327 0,-21.4974 -2.8664,-31.5296 -4.2995,-17.198 -22.9307,-12.8985 -34.396,-20.0643 -1.4332,-1.4332 2.8663,-5.7327 5.7327,-5.7327 14.3317,-2.8662 28.6633,-1.4332 42.995,-1.4332 2.8662,0 10.0322,5.7327 10.0322,2.8664 0,-2.8664 -12.8985,-22.9306 -21.4975,-37.2623 l 1.4331,1.4332 c 7.1659,2.8663 15.7649,0 24.3638,1.4331 5.7327,0 18.6312,5.7327 14.3317,2.8664 -10.0322,-5.7327 -27.2301,-2.8664 -31.5296,-12.8985 -2.8664,-8.5991 20.0642,-5.7327 21.4974,-14.3317 4.2995,-17.1979 -85.9899,48.7277 -55.8935,-28.6633 1.4332,0 48.7278,-18.6312 61.6262,-18.6312 15.7648,0 -20.0643,18.6312 14.3316,8.599 10.0322,-1.4331 20.0644,-12.8985 30.0966,-10.0321 8.5989,2.8663 4.2995,20.0643 12.8984,22.9305 5.7327,0 1.4332,-17.1979 7.1658,-18.631 10.0322,0 17.198,18.631 27.2302,15.7648 8.599,-2.8663 2.8664,-21.4975 11.4654,-27.2302 2.8663,-2.8663 8.599,5.7327 12.8984,5.7327 8.599,0 18.6312,-8.599 25.797,-4.2995 7.1658,4.2995 1.4332,18.6312 8.599,22.9307 4.2995,2.8663 4.2995,-7.1659 8.599,-10.0322 7.1659,-5.7327 30.0964,55.8935 35.8291,67.3588 1.4332,1.4332 1.4332,-5.7327 4.2995,-5.7327 8.599,-5.7326 17.198,-7.1658 27.2302,-11.4653 7.1659,-4.2995 12.8984,-15.7649 20.0643,-12.8985 12.8985,5.7326 1.4331,55.8934 35.8292,41.5617 10.032,-4.2994 4.2995,-22.9305 11.4652,-32.9627 7.1658,-10.0322 8.599,20.0643 17.198,21.4975 8.599,1.4332 15.7649,-2.8663 24.3639,-1.4332 8.599,0 25.7969,12.8984 28.6632,4.2995 2.8664,-15.7648 -30.0964,-27.2302 -25.7969,-42.9949 2.8664,-10.0322 5.7327,-20.0644 10.0322,-30.0965 -2.8663,0 -5.7327,-1.4332 -4.2995,-7.1659 2.8663,-10.0322 11.4652,-21.4975 11.4652,-32.9627 0,-2.8664 -8.5989,1.433 -10.032,-1.4332 -2.8664,-7.1658 0,-18.6312 -4.2995,-25.797 -1.4332,-4.2995 -10.0322,-4.2995 -12.8986,-8.599 -2.8663,-8.5989 -4.2995,-20.0644 -2.8663,-28.6633 0,-4.2995 7.1658,-5.7327 7.1658,-8.599 0,-10.0322 -5.7326,-20.0644 -7.1658,-30.0965 0,-2.8664 2.8663,5.7326 4.2995,7.1658 11.4654,5.7327 21.4974,11.4653 32.9629,14.3317 2.8662,1.4332 7.1657,-1.4332 10.032,-1.4332 11.4654,0 22.9307,-5.7327 32.9629,0 5.7327,1.4332 2.8662,10.0322 0,15.7649 -1.4332,2.8663 -5.7327,1.4331 -8.599,4.2995 -4.2995,4.2995 11.4652,50.1607 12.8985,50.1607 58.7598,8.5991 -25.797,-27.2301 20.0643,-10.0321 21.4975,8.599 48.7277,12.8985 68.7919,22.9307 7.1659,4.2995 11.4654,14.3315 18.6312,15.7647 32.9628,4.2995 25.7969,-14.3317 50.1608,-21.4974 5.7327,-1.4332 11.4653,-1.4332 18.6312,-1.4332 -15.7649,-2.8663 -30.0965,-12.8985 -10.0322,-47.2944 14.3317,-22.9307 110.3537,-25.797 136.1508,-40.1287 10.032,-4.2995 17.198,-12.8984 27.2302,-18.6311 8.5989,-4.2995 24.3637,-2.8663 28.6632,-11.4655 4.2995,-8.5989 -11.4653,-12.8984 -12.8985,-21.4974 -1.4332,-4.2995 0,-11.4653 2.8663,-14.3317 2.8664,-2.8663 8.599,2.8664 8.599,0 0,-2.8663 -7.1658,-2.8663 -7.1658,-7.1658 0,-10.0322 11.4654,-78.824 14.3317,-85.9899 5.7327,-17.198 53.0271,-20.0643 65.9256,-21.4975 17.1979,-1.4332 34.3959,-2.8663 50.1608,-5.7327 z m 804.0059,1563.5838 c 20.0644,14.3317 1.0072,47.0705 1.0072,62.8354 6.9309,9.9826 8.3042,16.0925 11.8914,24.5878 4.2995,10.032 4.2995,41.5617 4.2995,50.1606 1.433,24.3639 -22.9307,42.9951 -22.9307,60.1931 0,18.6311 7.1658,44.4281 -8.599,61.6261 -1.4332,2.8664 -8.599,0 -8.599,2.8664 0,4.2995 10.0321,0 8.599,2.8663 0,5.7327 -8.599,5.7327 -11.4654,10.0322 -4.2995,10.0322 0,22.9306 -7.1657,31.5296 -20.0644,20.0643 -14.3317,-21.4974 -18.6312,-22.9307 -18.6312,-5.7327 -30.0965,8.599 -42.9949,-12.8984 -2.8665,-4.2995 5.7325,-10.0322 2.8662,-15.7649 -4.2994,-8.599 -21.4974,-11.4653 -20.0643,-21.4975 0,-11.4653 22.9306,-17.198 21.4976,-28.6634 -1.4333,-8.5989 -24.3639,8.5992 -25.7971,0 -2.8663,-11.4652 20.0644,-17.1979 18.6311,-27.23 -1.433,-11.4654 -22.9306,-8.5991 -25.7969,-20.0644 -1.4332,-8.5989 17.198,-11.4652 17.198,-21.4974 0,-8.599 -14.3317,-12.8985 -18.6312,-21.4975 -1.4331,-5.7327 14.3317,-1.4332 17.1979,-5.7327 2.8665,-10.0322 1.4333,-22.9306 7.166,-31.5296 1.4332,-2.8663 4.2995,7.1657 7.1657,5.7327 7.1659,-1.4332 8.599,-11.4654 15.7649,-14.3317 5.7326,-2.8664 12.8985,2.8663 18.6312,0 7.1658,-4.2995 7.1658,-17.198 14.3315,-18.6312 8.599,-1.4332 15.7649,10.0322 25.7971,10.0322 8.599,0 -1.4332,-18.6312 0,-28.6634 1.4331,-11.4652 12.8985,-20.0642 18.6311,-31.5296 z m -331.0613,-123.2522 c 11.4654,0 21.4976,-10.0322 21.4976,-20.0644 0,-10.0322 -10.0322,-18.6312 -21.4976,-18.6312 -11.4653,0 -21.4974,8.599 -21.4974,18.6312 0,10.0322 10.0321,20.0644 21.4974,20.0644 11.4654,0 -11.4653,0 0,0 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cscccsccsccccsccccsccccccccsccccccccccccccccscccccccccscscccccccccccccccccccccccccccsccsscccccccccsccccscscccccccsccccccccccscscccccccccccccscscccccccccccccccccccscccccsscscsscccsccccccccccccscccccccccccccccsscccccccsccccccsscccccccsscccccsccccscccscsscccccccccscssscccccccscscccccscccccscccccccsccssssss" /><path
+ id="es"
+ class="eu europe"
+ d="m 5033.9567,6695.4203 c 10.0322,4.2994 21.4975,7.1657 28.6633,12.8984 2.8664,2.8663 4.2994,18.6312 12.8986,17.198 11.4652,-1.4332 21.4974,-8.599 34.3959,-8.599 14.3317,0 38.6954,28.6634 53.0271,24.3639 5.7327,-1.4332 0,-12.8986 4.2995,-15.7649 21.4975,-14.3317 47.2944,-1.4332 70.2251,4.2995 h 5.7327 c 7.1658,7.1659 12.8985,14.3317 10.0322,22.9307 -2.8664,11.4652 -21.4975,8.5989 -28.6634,15.7647 -5.7326,5.7327 17.198,38.6956 2.8664,63.0593 -7.1659,15.7649 -68.792,40.1286 -85.9899,47.2946 -15.7649,5.7327 -40.1287,11.4652 -51.5941,21.4974 -8.599,5.7327 -21.4974,27.2302 -34.3959,30.0965 -48.7276,14.3316 -136.1508,4.2994 -174.8462,35.8291 -10.0322,8.599 -30.0965,15.7649 -27.2302,28.6634 4.2995,10.0322 25.797,8.5989 24.3639,18.6311 0,4.2995 -44.4281,14.3317 -47.2946,15.7648 -17.1979,12.8985 -22.9306,37.2623 -37.2622,51.594 -40.1286,41.5617 -93.1559,81.6904 -118.9528,134.7176 -11.4653,21.4975 4.2995,90.2893 17.198,108.9205 4.2995,5.7327 57.3266,35.8292 44.4281,44.4281 -10.0321,8.599 -34.3959,11.4654 -44.4281,18.6312 -2.8663,2.8664 -2.8663,10.0322 -5.7326,11.4654 -2.8664,2.8663 -7.1659,-1.4332 -10.0322,-1.4332 -11.4654,2.8663 -24.3639,4.2995 -34.3961,11.4653 -4.2995,4.2995 -1.433,14.3317 -7.1657,18.6312 -4.2995,2.8664 -11.4653,-2.8663 -14.3317,0 -5.7326,7.1659 -1.4331,18.6311 -5.7326,24.3638 -5.7327,5.7327 -17.1981,0 -21.4976,7.1658 -5.7325,8.599 2.8664,21.4975 -1.4331,30.0966 -11.4654,28.6632 -37.2623,-4.2997 -38.6955,41.5617 0,11.4654 22.9307,15.7649 20.0644,25.7969 -4.2995,10.0322 -20.0644,8.5992 -31.5297,7.1659 -7.1659,0 -14.3317,-7.1659 -21.4974,-8.5989 -30.0966,-8.599 -65.9258,5.7327 -91.7227,22.9306 -44.4281,28.6633 -44.4281,84.5568 -90.2895,107.4875 -17.1979,10.032 -22.9306,-37.2624 -65.9255,-25.7971 -8.599,2.8664 -12.8985,14.3317 -22.9307,17.1981 -12.8985,4.2995 -42.9949,-20.0644 -55.8934,-21.4976 -17.1981,-4.2995 -35.8291,5.7327 -54.4603,2.8664 -10.0322,-2.8664 -15.7649,-11.4654 -25.797,-14.3317 -18.6311,-7.1658 -103.1879,-20.0644 -118.9528,-17.198 -17.198,4.2995 -25.797,25.797 -41.5618,32.9628 -22.9306,8.5991 -51.5939,-17.198 -83.1236,-1.4331 -25.7969,12.8985 -22.9307,24.3637 -35.8291,42.9949 -1.4332,1.4332 -10.0322,-7.1658 -15.7649,-2.8663 -8.599,7.1658 -15.7648,18.6312 -27.2301,22.9307 -4.2994,1.4331 -4.2994,-8.599 -8.5991,-11.4654 -5.7325,-4.2995 -14.3315,-5.7326 -18.631,-10.0322 -5.7327,-7.1658 -7.1659,-17.198 -14.3317,-22.9305 -4.2995,-4.2995 -12.8985,0 -17.198,-4.2995 -10.0322,-10.0322 -24.3638,-57.3268 -27.2301,-71.6583 0,-1.4332 21.4974,20.0642 18.6312,7.1658 0,-4.2995 -8.599,0 -10.0322,-2.8663 -1.4332,-2.8664 5.7327,-5.7327 4.2995,-8.599 -4.2995,-24.3639 -25.797,4.2995 -24.3639,-37.2623 0,0 55.8935,-25.797 42.995,-28.6634 -11.4654,-2.8663 -18.6311,21.4974 -30.0966,18.6312 -10.032,-1.4332 -5.7325,-18.6312 -10.032,-27.2302 -4.2995,-8.599 -10.0322,-15.7648 -15.7649,-22.9307 0,0 -38.6954,-37.2622 -38.6954,-37.2622 0,-1.4332 0,-1.4332 0,-2.8664 -1.4332,2.8664 -2.8664,4.2995 -5.7327,4.2995 -8.599,1.4332 -12.8985,-10.0321 -20.0643,-14.3317 -5.7327,-2.8663 -31.5296,-7.1658 -50.1608,-7.1658 2.8663,-60.1928 -11.4654,-70.225 35.8291,-111.7869 8.599,-7.1658 7.1658,-22.9307 15.7648,-27.2301 2.8664,-1.4333 73.0915,-2.8663 55.8935,-31.5297 -4.2995,-7.1658 -18.6311,4.2995 -25.7969,0 -7.1659,-5.7327 -5.7327,-17.198 -8.5991,-25.7969 -2.8663,-8.599 -2.8663,-18.6312 -8.599,-25.797 -1.4331,-2.8664 -10.0321,0 -10.0321,-2.8664 4.2995,-10.0321 14.3316,-17.198 18.6311,-27.2302 8.5991,-17.1979 -15.7648,-4.2995 8.5991,-24.3637 21.4974,-17.198 50.1607,-14.3317 53.0271,-51.594 0,-2.8663 -27.2302,-21.4975 -32.9629,-32.9628 -8.599,-18.6311 1.4332,-48.7276 1.4332,-51.594 -1.4332,-1.4331 -24.3638,-45.8613 -24.3638,-47.2944 -1.4331,-2.8664 4.2994,1.4331 7.1659,1.4331 12.8984,4.2996 21.4974,5.7327 34.3959,8.5991 32.9629,8.599 40.1286,11.4652 57.3266,-21.4976 20.0644,-40.1286 -1.4332,-25.797 -5.7327,-55.8934 -4.2995,-27.2302 40.1286,-14.3317 41.5618,-34.3959 1.4333,-14.3317 -14.3316,-1.4332 -5.7327,-18.6312 1.4333,-2.8664 7.166,-2.8664 8.599,-7.1659 4.2997,-10.0321 4.2997,-20.0643 5.7327,-30.0965 1.4332,-11.4652 10.0322,-22.9306 8.599,-34.3959 0,0 -12.8985,-34.3961 -12.8985,-34.3961 4.2995,-4.2995 14.3317,4.2995 20.0644,1.4332 0,0 30.0965,-31.5296 30.0965,-31.5296 21.4974,-11.4653 61.6261,-12.8985 73.0915,-44.4281 8.5989,-28.6634 -28.6634,-25.797 -38.6956,-27.2302 -7.1658,-1.4332 5.7327,-55.8934 0,-58.7598 -27.2302,-14.3317 -32.9627,-1.4331 -65.9256,-14.3317 -5.7327,-2.8663 -4.2995,-17.198 -17.198,-8.599 -2.8664,2.8665 2.8663,10.0322 0,12.8985 -20.0643,17.1981 -41.5618,-2.8663 -47.2945,-4.2995 -5.7326,-2.8663 -12.8985,4.2995 -17.198,1.4332 -2.8662,-1.4332 8.599,-4.2995 5.7327,-7.1657 -1.4332,-5.7327 -8.5989,-7.166 -14.3316,-7.166 -8.599,-1.433 -18.6312,1.4333 -27.2302,0 -1.4331,-1.433 1.4332,-4.2995 1.4332,-7.1657 0,-1.4332 1.4332,-7.1659 0,-5.7327 -7.1658,1.4332 -24.3639,21.4974 -37.2624,12.8984 -34.3959,-24.3637 32.9629,-30.0964 15.7649,-47.2944 -2.8664,-2.8664 -8.5989,0 -11.4654,-2.8664 -1.433,-2.8663 5.7327,-8.599 2.8665,-10.032 -25.7971,-5.7327 -61.6263,2.8662 -83.1237,14.3316 2.8664,-11.4654 -12.8985,-21.4976 -12.8985,-32.9628 -1.4332,-4.2995 7.1658,5.7327 10.0322,2.8663 2.8663,-1.4331 -2.8664,-7.1658 -1.4332,-10.0322 2.8664,-12.8985 24.3639,-8.599 34.3959,-17.198 2.8664,-1.4331 8.5991,-8.599 4.2996,-7.1658 -8.5991,1.4332 -17.1981,10.0322 -25.797,11.4653 -7.1658,1.4332 -10.0321,-11.4653 -4.2995,-17.198 8.5989,-5.7327 27.2301,-1.433 30.0965,-11.4652 1.4331,-8.599 -18.6311,2.8662 -25.797,0 -5.7326,-2.8665 -10.0321,-10.0322 -11.4653,-15.7649 -1.4332,-14.3317 12.8985,4.2995 14.3317,2.8664 2.8663,-5.7327 0,-12.8985 4.2995,-18.6312 4.2995,-7.1659 17.1979,-8.599 18.6311,-17.1979 7.1658,-70.2253 -50.1608,10.032 -61.6262,-58.7599 -8.599,-47.2945 31.5297,-32.9628 32.9629,-40.1286 1.4332,-4.2995 -11.4654,0 -15.7649,-2.8664 -1.4331,-2.8663 4.2995,-5.7326 5.7327,-5.7326 15.7649,-2.8664 24.3639,1.4331 35.8291,0 37.2624,-2.8664 87.4232,-34.396 121.8191,-48.7276 12.8985,-5.7327 27.2302,-24.3639 40.1287,-15.7649 5.7327,4.2995 -8.599,21.4975 -1.4332,22.9307 11.4654,1.4332 18.6311,-18.6312 30.0965,-18.6312 38.6955,0 35.8292,67.3588 71.6583,75.9578 7.1658,1.4332 10.0321,-14.3317 17.198,-12.8985 40.1286,10.0322 18.6312,11.4653 58.7598,17.198 10.0321,1.4332 28.6632,-2.8663 37.2622,-1.4332 8.599,2.8664 12.8985,11.4654 21.4976,11.4654 11.4653,0 21.4975,-14.3317 32.9627,-11.4654 10.0322,2.8664 11.4654,20.0643 20.0644,27.2301 8.599,7.1659 25.797,-1.4331 34.3959,7.1659 2.8663,2.8663 -5.7327,7.1658 -2.8662,8.599 2.8662,4.2995 11.4652,-5.7327 15.7647,-2.8663 35.8292,24.3638 128.9849,70.2251 176.2795,55.8934 2.8662,-1.4332 2.8662,-7.1657 5.7325,-8.599 5.7327,-1.4332 11.4654,0 17.1981,0 11.4653,1.4333 41.5617,-1.4332 50.1607,15.7648 0,1.4332 -1.4331,4.2995 -2.8663,5.7327 11.4654,2.8664 42.9949,32.9629 54.4603,32.9629 4.2995,0 -2.8662,-10.0322 0,-12.8985 2.8663,-4.2995 8.599,-5.7327 14.3317,-7.1659 41.5617,-5.7326 14.3317,-1.4331 31.5297,18.6311 5.7325,5.7327 17.1979,-2.8662 22.9306,1.4333 8.599,4.2994 12.8985,14.3316 20.0643,18.6311 18.6312,10.0321 58.7598,8.599 85.99,1.4331 8.5989,8.599 24.3637,22.9307 24.3637,22.9307 2.8664,1.4332 1.4332,-5.7327 2.8664,-5.7327 7.1658,1.4332 15.7648,4.2996 20.0643,10.0322 12.8984,14.3316 -28.6633,31.5296 -10.0321,40.1286 8.599,2.8664 17.198,-12.8985 20.0643,-10.0321 1.4332,2.8663 -2.8663,10.0321 0,12.8985 20.0644,18.6311 51.594,11.4653 70.225,25.797 1.4332,1.4332 15.7649,34.3959 17.1981,34.3959 18.6311,14.3317 37.2623,-11.4653 57.3266,5.7327 10.0322,8.599 2.8663,27.2302 21.4975,27.2302 8.599,1.4332 14.3317,-7.166 22.9307,-5.7327 8.599,1.4332 12.8984,11.4654 21.4974,12.8984 4.2995,0 4.2995,-8.599 8.599,-8.599 14.3317,-1.4332 27.2302,10.0322 42.9951,11.4655 0,0 -7.166,-32.9629 10.032,-30.0966 24.3639,4.2995 38.6956,21.4976 54.4603,35.8291 1.4332,1.4332 5.7327,0 8.599,0 7.1659,1.4332 15.7649,1.4332 20.0644,5.7327 7.1658,5.7327 2.8663,18.6312 10.0321,22.9307 v 1.4332 c -7.1658,14.3315 -10.0321,34.3959 34.396,21.4975 z m -1248.2872,1060.5425 10.032,41.5619 h 31.5297 c 0,-4.2995 -1.4331,-7.1659 -1.4331,-11.4654 -1.4332,-8.599 -4.2995,-17.198 -1.4332,-25.797 1.4332,-4.2995 14.3317,-5.7327 12.8985,-10.0322 -8.599,-20.0642 -48.7277,4.2995 -51.5939,5.7327 0,0 0,0 0,0 z m 369.7566,173.413 21.4975,14.3317 c 0,-12.8984 2.8664,-27.2301 1.4332,-38.6954 -2.8664,-11.4654 -14.3317,17.198 -22.9307,24.3637 0,0 0,0 0,0 z m 27.2302,11.4654 2.8663,-4.2994 c -1.4331,0 -2.8663,0 -2.8663,2.8662 0,1.4332 0,1.4332 0,1.4332 z m 1010.3818,-752.4119 c -21.4975,73.0913 4.2994,21.4974 40.1286,51.5939 10.0322,8.5989 -63.0593,78.8242 -68.7919,77.3909 -7.1659,0 -5.7327,-15.7649 -12.8984,-20.0643 -21.4976,-12.8985 -22.9307,10.0321 -30.0966,-11.4653 -2.8663,-10.0322 0,-22.9307 -7.1658,-28.6634 -5.7327,-5.7325 -14.3317,4.2995 -20.0644,2.8664 -10.032,-1.4332 -20.0642,-5.7327 -28.6632,-11.4654 -1.4332,-1.433 1.433,-4.2995 2.8663,-5.7325 35.8291,-27.2302 80.2572,-45.8614 124.6854,-54.4603 z m 150.4824,1.433 c 32.9628,83.1237 -5.7327,25.7971 -25.7971,15.7649 -8.5989,-2.8663 -20.0642,4.2995 -28.6632,-1.4332 -35.8291,-24.3637 11.4653,-34.3959 54.4603,-14.3317 z m -255.1035,134.7176 c -8.5989,8.599 -21.4976,8.599 -21.4976,15.7649 0,5.7327 5.7328,17.198 12.8987,17.198 8.5989,0 14.3315,0 25.7969,-10.0322 11.4653,-10.0322 0,-17.198 0,-17.198 l -15.7649,-5.7327 z m -34.396,-42.9949 c -11.4653,2.8663 -14.3316,10.0322 -20.0643,17.198 -5.7327,8.5989 -14.3317,21.4974 -1.4332,27.2301 7.1659,4.2995 28.6634,-7.1657 32.9629,-15.7647 2.8663,-7.1659 11.4652,-8.5992 11.4652,-17.1981 0,-7.1658 -10.0322,-12.8985 -10.0322,-12.8985 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="pt"
+ class="eu europe"
+ d="m 3564.9619,7442.0995 c 2.8663,-60.1928 -11.4654,-70.225 35.8291,-111.7869 8.599,-7.1658 7.1658,-22.9307 15.7648,-27.2301 2.8664,-1.4333 73.0915,-2.8663 55.8935,-31.5297 -4.2995,-7.1658 -18.6311,4.2995 -25.7969,0 -7.1659,-5.7327 -5.7327,-17.198 -8.5991,-25.7969 -2.8663,-8.599 -2.8663,-18.6312 -8.599,-25.797 -1.4331,-2.8664 -10.0321,0 -10.0321,-2.8664 4.2995,-10.0321 14.3316,-17.198 18.6311,-27.2302 8.5991,-17.1979 -15.7648,-4.2995 8.5991,-24.3637 21.4974,-17.198 50.1607,-14.3317 53.0271,-51.594 0,-2.8663 -27.2302,-21.4975 -32.9629,-32.9628 -8.599,-18.6311 1.4332,-48.7276 1.4332,-51.594 -1.4332,-1.4331 -24.3638,-45.8613 -24.3638,-47.2944 -1.4331,-2.8664 4.2994,1.4331 7.1659,1.4331 12.8984,4.2996 21.4974,5.7327 34.3959,8.5991 32.9629,8.599 40.1286,11.4652 57.3266,-21.4976 20.0644,-40.1286 -1.4332,-25.797 -5.7327,-55.8934 -4.2995,-27.2302 40.1286,-14.3317 41.5618,-34.3959 1.4333,-14.3317 -14.3316,-1.4332 -5.7327,-18.6312 1.4333,-2.8664 7.166,-2.8664 8.599,-7.1659 4.2997,-10.0321 4.2997,-20.0643 5.7327,-30.0965 1.4332,-11.4652 10.0322,-22.9306 8.599,-34.3959 0,0 -12.8985,-34.3961 -12.8985,-34.3961 4.2995,-4.2995 14.3317,4.2995 20.0644,1.4332 0,0 30.0965,-31.5296 30.0965,-31.5296 21.4974,-11.4653 61.6261,-12.8985 73.0915,-44.4281 8.5989,-28.6634 -28.6634,-25.797 -38.6956,-27.2302 -7.1658,-1.4332 5.7327,-55.8934 0,-58.7598 -27.2302,-14.3317 -32.9627,-1.4331 -65.9256,-14.3317 -5.7327,-2.8663 -4.2995,-17.198 -17.198,-8.599 -2.8664,2.8665 2.8663,10.0322 0,12.8985 -20.0643,17.1981 -41.5618,-2.8663 -47.2945,-4.2995 -5.7326,-2.8663 -12.8985,4.2995 -17.198,1.4332 -2.8662,-1.4332 8.599,-4.2995 5.7327,-7.1657 -1.4332,-5.7327 -8.5989,-7.166 -14.3316,-7.166 -8.599,-1.433 -18.6312,1.4333 -27.2302,0 -1.4331,-1.433 1.4332,-4.2995 1.4332,-7.1657 0,-1.4332 1.4332,-7.1659 0,-5.7327 -7.1658,1.4332 -24.3639,21.4974 -37.2624,12.8984 -34.3959,-24.3637 32.9629,-30.0964 15.7649,-47.2944 -2.8664,-2.8664 -8.5989,0 -11.4654,-2.8664 -1.433,-2.8663 5.7327,-8.599 2.8665,-10.032 -25.7971,-5.7327 -61.6263,2.8662 -83.1237,14.3316 -2.8663,14.3316 -38.6954,21.4975 -30.0965,34.396 5.7327,7.1657 22.9307,4.2995 25.797,12.8985 2.8664,7.1657 -15.7648,-1.4333 -20.0643,4.2994 -8.5989,12.8985 -7.1659,74.5246 -4.2995,90.2895 0,4.2995 7.1658,5.7327 7.1658,11.4653 -1.4332,24.3638 -11.4652,38.6955 -21.4974,58.7598 -4.2995,8.599 -22.9307,27.2301 -12.8985,25.7969 10.032,-2.8663 10.032,-24.3637 20.0642,-27.2301 7.1659,-1.4331 11.4654,12.8986 8.599,18.6311 -7.1658,10.0322 -27.2301,5.7327 -34.3959,17.198 -10.0322,17.1981 -15.7649,37.2624 -27.2302,54.4603 -4.2995,7.1659 -12.8985,11.4654 -11.4652,18.6312 0,7.1659 12.8984,10.0322 15.7647,17.198 1.4332,2.8664 -7.1658,-8.599 -11.4652,-5.7326 -7.166,4.2995 -10.0322,15.7648 -15.7649,24.3637 -14.3317,22.9307 -30.0965,51.5941 -47.2944,71.6583 -4.2995,5.7327 -12.8985,7.1659 -18.6312,14.3317 -1.4332,1.4332 7.1657,5.7327 4.2995,7.1658 -11.4654,2.8664 -25.797,-7.1658 -32.9629,0 -7.1658,7.1659 5.7327,18.6312 2.8664,27.2301 -4.2995,14.3317 -47.2945,74.5248 -42.995,88.8563 2.8664,10.0322 10.0322,18.6312 15.7649,28.6634 14.3317,24.3638 8.5989,35.8291 8.5989,35.8291 11.4653,2.8663 24.3638,1.4332 35.8292,0 5.7327,0 8.599,-7.1658 14.3317,-5.7327 5.7325,0 8.599,8.599 15.7647,10.0322 4.2995,1.4332 4.2995,-18.6312 7.1658,-14.3317 4.2996,7.1659 -2.8663,17.198 0,22.9307 4.2996,5.7327 24.3639,8.599 18.6312,11.4654 -38.6954,18.631 -15.7648,-30.0966 -47.2944,-25.7971 -8.599,1.4332 10.0322,12.8985 12.8984,20.0644 1.4333,8.599 0,18.6312 -1.4332,28.6632 -2.8662,18.6312 -27.2301,35.8292 -27.2301,50.1608 0,5.7327 8.599,10.0322 8.599,15.7649 1.4332,10.0322 -14.3316,28.6633 -4.2995,31.5297 2.8664,0 12.8986,-4.2995 10.0322,-1.4332 -2.8663,4.2995 -11.4653,1.4332 -14.3317,5.7327 -7.1658,14.3317 -2.8663,31.5296 -10.0321,45.8613 -5.7327,11.4653 -40.1286,58.7597 -37.2623,74.5246 1.4332,5.7327 11.4652,-5.7327 17.1979,-5.7327 8.599,-1.4332 17.198,1.4332 25.797,0 4.2995,0 8.599,-4.2995 12.8985,-2.8663 22.9306,5.7327 97.4553,54.4603 107.4874,53.0271 4.2996,0 48.7277,-21.4975 51.594,-22.9307 4.2995,0 11.4653,0 18.6312,0 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="at"
+ class="eu europe"
+ d="m 6128.8952,5816.8897 c 0,10.0322 18.6312,12.8984 12.8985,27.2301 -1.4331,2.8663 -7.1658,1.4332 -8.599,2.8663 -1.433,4.2995 -5.7325,15.7649 -10.032,28.6634 l 7.1658,15.7647 v 17.198 c 14.3316,1.4332 37.2623,4.2996 38.6954,4.2996 2.8664,2.8663 -4.2995,7.1658 -1.4331,10.0321 7.1658,8.599 15.7648,14.3317 25.797,18.6312 22.9306,11.4654 27.2301,-27.2302 42.9949,-24.3638 30.0965,4.2995 -1.4331,20.0643 8.5989,24.3638 22.9307,11.4654 2.8665,-2.8663 25.7971,0 11.4653,1.4332 22.9307,31.5296 45.8612,18.6311 0,0 18.6312,-38.6954 18.6312,-38.6954 11.4654,-8.599 61.6261,-2.8664 74.5247,-4.2995 21.4975,-1.4332 38.6954,-20.0644 60.1929,-20.0644 5.7327,0 -10.0322,7.1658 -11.4653,12.8985 -7.1659,30.0964 11.4653,10.0322 22.9307,27.2302 2.8663,4.2995 -4.2995,10.0322 -1.4332,15.7649 1.4332,2.8662 5.7327,-2.8665 8.599,0 27.2301,20.0642 34.3959,22.9305 71.6583,28.6632 42.9949,5.7327 87.423,7.1659 131.8513,14.3317 11.4652,1.4332 22.9305,10.0322 34.3959,11.4652 11.4653,1.4332 22.9307,-7.1657 34.3959,-5.7325 7.1659,1.433 11.4654,11.4652 18.6312,11.4652 2.8663,1.4333 1.4332,-7.1657 4.2995,-8.5989 7.1658,-7.1658 18.6312,-10.0322 25.7969,-18.6312 5.7327,-4.2995 1.4333,-14.3317 5.7327,-18.6312 27.2302,-21.4975 55.8936,5.7327 81.6905,-5.7326 8.599,-4.2994 10.0322,-17.1981 18.6312,-20.0643 1.4331,-1.4333 48.7276,5.7327 50.1607,4.2994 4.2995,-1.4332 -4.2995,-7.1657 -5.7326,-11.4652 -2.8664,-14.3317 2.8663,-22.9307 12.8985,-25.7971 v 0 c 25.7969,-28.6632 37.2624,-32.9627 50.1608,-54.4602 2.8663,-5.7327 -22.9306,-35.8291 -15.7649,-50.1608 10.0322,-20.0644 34.396,1.4332 31.5297,-27.2302 -1.4332,-24.3638 -44.4281,-21.4974 -35.8292,-28.6633 20.0644,-17.198 71.6583,5.7327 80.2573,-10.0321 1.4332,-5.7327 -18.6312,-51.594 1.4332,-60.193 -17.198,-18.6312 -31.5296,-37.2624 -44.4281,-58.7598 -11.4654,-18.6312 30.0964,-48.7276 0,-70.2251 -4.2995,-2.8664 -10.0322,5.7327 -14.3317,5.7327 -7.1658,-1.4332 -21.4975,-20.0644 -31.5296,-17.1981 -7.1658,2.8664 -10.0322,14.3317 -18.6312,18.6312 -5.7327,1.4332 -12.8985,-1.4331 -18.6312,-1.4331 -38.6954,-4.2996 -71.6583,-35.8291 -113.22,-35.8291 -7.1659,0 -12.8985,8.5989 -20.0644,8.5989 -2.8663,-1.4332 0,-8.5989 -2.8663,-10.0321 -5.7327,-2.8665 -14.3316,-5.7327 -18.6311,-1.4333 -1.4332,2.8665 1.4332,44.4282 -1.4332,47.2946 -2.8663,4.2995 -12.8985,-5.7327 -18.6312,-1.4332 0,0 -11.4653,37.2623 -11.4653,37.2623 -10.0322,2.8663 -20.0644,-10.0322 -30.0964,-10.0322 -5.7327,0 -4.2995,11.4653 -10.0322,12.8985 -30.0965,11.4653 -47.2946,-18.6312 -70.2251,-30.0964 h -1.4332 c 1.4332,4.2995 2.8663,8.599 2.8663,12.8984 -1.4331,38.6955 -10.0321,30.0965 -40.1287,22.9307 -2.8662,0 -5.7325,1.4331 -7.1657,4.2995 -2.8664,14.3317 5.7327,30.0964 -8.599,41.5617 -12.8985,10.0322 -73.0915,24.3639 -74.5247,47.2946 0,0 34.3961,47.2944 34.3961,48.7276 1.433,1.4332 -18.6312,18.6311 -11.4654,24.3639 15.7649,14.3315 31.5296,-7.166 18.6311,40.1286 0,2.8663 -2.8662,7.1658 -5.7327,7.1658 -45.8612,-12.8985 -10.032,-35.8292 -31.5296,-37.2624 -11.4653,0 -21.4975,10.0322 -32.9627,8.599 -5.7327,-1.4331 -4.2995,-11.4653 -8.599,-12.8985 -8.599,-2.8663 -17.198,4.2995 -24.3639,1.4332 -2.8663,-1.4332 2.8664,-10.0322 0,-10.0322 -17.198,1.4332 -1.4332,24.3639 -5.7327,25.797 -20.0643,5.7327 -53.0271,-2.8663 -71.6583,4.2995 -5.7326,2.8664 -2.8663,12.8986 -8.599,15.7649 -5.7325,4.2995 -12.8984,-2.8663 -18.631,1.4332 -4.2996,1.433 1.4331,8.599 -1.4332,11.4652 -1.4332,1.4333 -50.1608,14.3317 -55.8936,11.4654 -2.8662,-2.8664 0,-10.0321 -2.8662,-12.8986 -1.4332,-2.8663 -8.599,-2.8663 -10.0322,-5.7325 -1.4331,-2.8665 7.1659,-1.4332 5.7327,-4.2995 -5.7327,-18.6312 -45.8614,4.2995 -57.3266,-8.599 0,0 2.8663,-1.4332 2.8663,-2.8664 -8.599,-5.7327 0,18.6311 -2.8663,28.6633 0,5.7327 -27.2302,38.6955 -37.2624,28.6633 -5.7327,-5.7326 10.0322,-14.3316 5.7327,-20.0643 -1.4332,-1.4332 -5.7327,4.2995 -8.599,4.2995 -2.8664,0 -7.1659,-1.4332 -8.5989,-4.2995 -2.8665,-4.2995 7.1657,-10.0322 0,-15.7649 -5.7327,-4.2995 -8.599,-11.4652 -14.3317,-14.3315 -4.2995,-2.8664 -10.0322,4.2995 -15.7649,2.8663 -2.8663,-2.8663 0,-10.0322 -2.8663,-12.8985 -7.1658,-5.7327 -5.7327,20.0642 -28.6634,12.8985 -1.433,-1.4332 -2.8662,-1.4332 -4.2995,-1.4332 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="it"
+ class="eu europe"
+ d="m 7023.1907,7523.79 c -18.6312,10.0322 -80.2572,159.0813 -65.9257,179.1457 5.7327,8.599 34.3961,8.599 25.7971,14.3317 -7.1659,4.2995 -5.7327,-10.0322 -10.0322,0 0,2.8663 -2.8664,5.7326 -1.4332,10.0321 2.8664,11.4654 20.0644,17.1981 24.3639,28.6633 1.433,2.8663 -4.2995,1.4332 -7.1659,4.2995 -7.1658,4.2995 -14.3316,8.599 -18.6312,15.7649 -4.2995,7.1658 -5.7326,15.7648 -7.1658,22.9305 -1.4332,8.599 4.2995,20.0644 0,27.2302 -2.8663,4.2995 -7.1658,-14.3315 -34.3959,-12.8985 -30.0966,2.8665 -17.198,12.8985 -57.3266,-5.7327 -17.1981,-7.1657 -21.4976,-38.6954 -40.1287,-50.1607 -25.7971,-15.7649 -48.7277,0 -77.3909,-11.4654 -4.2995,-1.433 -8.599,-2.8662 -12.8985,-5.7325 -5.7327,-4.2997 -8.599,-14.3317 -15.7649,-17.1981 -5.7327,-4.2995 -12.8985,0 -20.0643,-2.8663 -28.6633,-10.0322 -48.7276,-37.2624 -80.2572,-45.8613 -12.8986,-2.8663 -27.2302,10.0321 -40.1287,4.2994 -2.8664,-1.433 -37.2623,-38.6954 -35.8291,-44.4281 0,-5.7327 8.599,-8.599 8.599,-12.8985 1.433,-4.2994 -4.2995,-5.7327 -4.2995,-10.0321 0,-5.7326 2.8663,-12.8985 7.1658,-18.6312 4.2994,-5.7326 12.8984,-7.1658 18.6311,-12.8985 7.1658,-5.7326 14.3317,-27.2302 20.0643,-20.0642 2.8664,4.2994 7.1659,32.9627 20.0644,31.5296 48.7276,-7.1659 0,-42.995 61.6261,-40.1286 15.7649,0 2.8662,17.1979 11.4654,21.4974 5.7325,2.8663 14.3315,-4.2995 21.4974,-1.4332 4.2995,1.4332 2.8663,10.0322 5.7326,11.4654 8.5991,5.7326 20.0644,11.4653 30.0966,11.4653 1.433,0 37.2622,-17.198 37.2622,-17.198 11.4654,-1.4332 35.8291,7.1658 53.0272,2.8663 35.8292,-7.1658 34.396,-21.4975 53.0271,-31.5296 42.9949,-22.9306 11.4653,8.5989 53.0271,5.7327 28.6634,-1.4333 8.599,-17.198 20.0643,-28.6633 4.2995,-4.2996 4.2995,12.8985 8.5991,11.4653 25.797,-5.7327 25.797,-28.6634 48.7276,-12.8985 m -945.8894,-528.8381 c 4.2995,20.0642 67.3588,15.7647 58.7599,47.2944 -1.4333,8.599 -25.7971,5.7327 -22.9307,15.7649 2.8663,10.0322 22.9307,7.1657 30.0964,15.7647 2.8663,2.8664 -7.1657,4.2995 -7.1657,8.599 8.5989,51.594 21.4974,-21.4975 24.3637,47.2946 1.4332,24.3637 -24.3637,32.9627 -31.5297,53.0271 -7.1657,18.6311 14.3317,18.6311 17.198,34.3959 0,5.7327 -7.1658,11.4654 -7.1658,18.6312 -4.2995,47.2945 1.4332,113.2201 -25.7969,160.5145 -1.4332,2.8664 0,-4.2995 -2.8664,-5.7326 -20.0643,-7.1659 -45.8612,-27.2301 -70.2251,-25.7971 -4.2995,0 10.0322,5.7328 10.0322,10.0322 -1.4332,5.7327 -8.599,8.599 -10.0322,15.7649 -8.599,32.9628 14.3317,27.2302 -25.797,45.8612 -55.8935,25.7971 -21.4974,-27.2301 -48.7276,-38.6954 6.9358,17.1436 -13.4659,-15.8504 -21.4974,-24.3639 -8.599,-28.6632 11.4652,-47.2944 15.7647,-71.6583 1.4332,-10.032 -4.2995,-18.631 -1.4332,-28.6632 1.4332,-8.599 17.1981,-12.8985 14.3317,-21.4974 -1.4331,-7.166 -17.198,0 -21.4975,-5.7327 -4.2995,-7.1658 2.8663,-15.7649 2.8663,-22.9307 0,-1.4332 -5.7325,-1.4332 -4.2995,-2.8663 2.8665,-4.2995 11.4654,-2.8664 14.3317,-8.599 5.7327,-14.3317 -15.7647,-84.5569 -24.3637,-94.5889 -17.1981,-18.6312 -28.6634,42.9949 -24.3639,-28.6634 1.4332,-10.0322 11.4653,-17.198 12.8985,-27.2302 1.4332,-5.7327 -5.7327,-11.4652 -1.4332,-15.7647 1.4332,-1.4332 11.4654,28.6632 37.2623,28.6632 21.4975,1.4332 100.3217,-63.0593 113.2201,-78.824 m 590.4643,-1003.216 c -2.8663,28.6634 -38.6954,22.9307 -41.5619,45.8613 -2.8662,17.198 30.0966,14.3316 37.2624,20.0643 10.0322,8.5989 -17.198,20.0644 -21.4975,32.9628 -5.7327,18.6312 17.198,-1.4332 20.0643,5.7327 2.8664,7.1658 -12.8985,25.797 -2.8663,31.5297 14.3317,8.599 71.6583,42.9949 21.4975,40.1286 2.8662,-7.1659 2.8662,-12.8986 -2.8663,-20.0644 -27.2302,-25.7969 -26.6015,-7.0348 -37.5539,2.8344 -2.3099,3.0587 -2.5748,0.032 -2.5748,0.032 0,-14.3315 -27.2301,-17.198 -40.1286,-11.4653 -7.1659,4.2995 0,20.0643 -5.7327,25.797 -5.7327,5.7327 -15.7649,1.4332 -24.3639,4.2995 -20.0642,8.599 -38.6954,31.5296 -61.6261,35.8291 -7.1657,1.4332 -14.3316,-5.7325 -20.0642,-1.433 -5.7327,1.433 0,10.032 -2.8664,15.7647 -1.4331,4.2995 -5.7326,10.0322 -8.599,14.3317 1.4332,-1.4332 1.4332,-2.8664 2.8664,-2.8664 7.1658,0 1.4331,15.7649 7.1658,18.6312 2.8663,1.4332 4.2995,-8.599 7.1658,-5.7326 2.8664,5.7326 8.5989,17.198 2.8664,21.4974 -2.8664,1.4331 -4.2995,4.2995 -2.8664,7.1658 1.4332,1.4332 4.2994,-1.433 7.1659,-1.433 8.5989,7.1657 22.9306,11.4652 24.3637,21.4974 8.599,40.1286 -25.7969,24.3637 -35.8291,48.7276 -2.8663,7.1658 15.7649,84.5568 20.0643,93.1558 8.599,14.3316 108.9207,85.9899 128.9849,97.4552 20.0644,11.4654 25.797,4.2995 44.4282,20.0644 20.0643,17.198 47.2945,130.4181 63.0593,159.0813 15.7649,30.0966 48.7276,57.3266 74.5245,77.391 8.599,5.7327 21.4976,7.1658 28.6634,14.3317 4.2995,2.8663 0,11.4652 2.8663,14.3317 38.6955,40.1286 111.7869,27.2301 157.6482,20.0642 10.0322,-1.4332 31.5297,-17.198 44.4282,-2.8663 35.8291,40.1287 -71.6583,54.4603 -17.198,91.7226 51.594,34.396 123.2523,31.5296 176.2794,58.7598 18.6312,10.0322 25.797,25.7969 47.2944,32.9628 17.198,7.1658 64.4925,14.3317 78.8242,25.797 5.7326,4.2995 0,14.3317 4.2995,18.6312 17.198,24.3637 63.0593,20.0642 77.3909,70.225 5.7327,18.6312 -7.1658,12.8985 -11.4653,25.797 -2.8663,7.1659 1.4332,35.8291 -4.2995,38.6955 -5.7327,4.2995 -15.7649,0 -24.3639,-4.2994 -44.4281,-17.198 -25.7969,-42.9951 -48.7276,-73.0915 -12.8985,-17.198 -54.4603,0 -75.9578,-7.1658 -28.6632,-10.0322 -44.4281,-42.9951 -71.6583,-14.3317 -2.8662,4.2995 -38.6954,75.9578 -38.6954,78.8241 0,4.2994 7.1658,8.5989 5.7326,14.3316 -5.7326,27.2302 -34.3959,25.797 -10.0321,57.3266 14.3317,17.198 18.6312,1.4333 34.396,5.7327 17.1979,5.7327 28.6633,22.9307 44.4281,30.0965 5.7327,2.8664 14.3317,0 17.198,5.7327 2.8664,4.2995 -5.7326,10.0322 -5.7326,15.7647 1.4331,40.1288 20.0642,2.8665 17.198,54.4604 -1.4333,30.0965 -58.7599,18.6311 -84.5568,48.7276 -18.6312,21.4976 5.7326,42.995 0,68.792 -4.2995,21.4974 -18.6312,15.7649 -32.9629,27.2301 -34.3959,30.0965 -10.0322,98.8885 -81.6904,78.8241 -14.3317,-4.2995 -30.0965,-41.5617 -21.4975,-57.3266 5.7327,-8.599 20.0644,-7.1658 27.2302,-14.3317 4.2995,-5.7326 4.2995,-14.3315 5.7327,-21.4974 1.433,-7.1658 7.1657,-12.8985 5.7325,-20.0643 -1.433,-8.599 -15.7647,-12.8985 -14.3315,-21.4976 5.7325,-25.7969 40.1286,-11.4653 50.1607,-25.7969 24.3638,-28.6633 -4.2995,-32.9628 -15.7648,-48.7277 -11.4654,-17.1979 -5.7327,-41.5618 -14.3317,-60.1928 -4.2995,-11.4654 -10.0322,-21.4976 -17.1979,-30.0966 -1.4332,-2.8663 -7.1658,-2.8663 -10.0322,-5.7326 -25.797,-38.6955 1.4332,-15.7649 -15.7648,-58.7598 -21.4976,-54.4603 -34.3961,-27.2301 -77.391,-24.3639 -1.4332,0 1.4332,-4.2995 0,-7.1657 -5.7327,-5.7327 -12.8984,-12.8985 -21.4974,-17.198 -8.599,-4.2995 -25.797,0 -31.5297,-10.0322 -5.7327,-10.0322 11.4653,-20.0644 10.0322,-31.5296 -1.4332,-11.4655 -10.0322,-21.4975 -15.7649,-30.0965 -34.3959,-47.2945 -40.1286,18.631 -80.2572,4.2995 -4.2995,-1.4332 27.2301,-24.3639 12.8984,-34.3961 -51.5939,-37.2622 -1.4332,1.4332 -57.3266,-5.7325 -4.2995,-1.4332 0,-8.5992 -1.4332,-12.8986 -5.7325,-17.198 -35.8291,-63.0592 -50.1608,-68.7919 -38.6954,-14.3317 -8.599,12.8985 -21.4975,10.0322 -12.8984,-2.8664 -24.3637,-18.6312 -38.6954,-17.1981 -12.8985,2.8664 -22.9307,20.0644 -34.3959,17.1981 -12.8986,-2.8664 -11.4654,-21.4976 -20.0644,-30.0966 -10.0322,-8.599 -35.8292,-5.7326 -44.4281,-11.4653 l -21.4975,-30.0964 c -10.0322,-8.5991 -25.7969,-10.0322 -34.3961,-20.0644 -17.1979,-25.7969 2.8665,-21.4974 -17.1979,-37.2623 -8.599,-7.1658 -18.6312,-12.8985 -28.6633,-18.6311 -2.8664,-1.4332 -8.5989,2.8663 -11.4654,1.4331 -15.7647,-12.8985 -15.7647,-37.2622 -31.5296,-50.1608 -27.2302,-22.9306 -44.4281,-5.7326 -73.0914,-17.198 -4.2995,-1.4331 7.1658,-5.7326 10.0321,-8.599 8.599,-11.4652 -8.599,-30.0964 -12.8985,-34.3959 -2.8663,-2.8664 -8.599,0 -11.4653,-2.8664 -4.2994,-5.7326 -4.2994,-14.3316 -10.0321,-18.6311 -7.1658,-5.7327 -21.4975,-1.4332 -27.2302,-10.0322 -4.2995,-5.7327 8.599,-17.1979 1.4332,-21.4974 -8.599,-7.1659 -22.9307,1.433 -34.3961,-2.8664 -7.1657,-2.8663 4.2997,-15.7648 4.2997,-24.3638 4.2995,-28.6634 0,-38.6955 -15.7649,-63.0593 -30.0965,-42.995 -8.599,-81.6905 -44.4282,-114.6533 -2.8662,-4.2995 -8.5989,-5.7326 -12.8984,-7.1658 -1.4332,0 -2.8665,4.2995 -4.2995,2.8663 -7.1659,-2.8663 -11.4654,-11.4653 -18.6312,-11.4653 -4.2995,0 4.2995,12.8985 0,11.4653 -11.4653,-2.8663 -20.0644,-11.4653 -30.0964,-18.631 -4.2995,-2.8665 -40.1287,-40.1287 -51.5941,-40.1287 -2.8663,0 1.4332,7.1658 -1.433,8.599 -2.8665,1.4331 -7.166,0 -8.5992,-2.8664 -7.1657,-8.599 -37.2622,-22.9307 -51.5939,-22.9307 -5.7327,0 -42.9949,21.4976 -45.8613,27.2302 -2.8663,5.7327 1.4332,14.3317 -2.8663,18.6311 -4.2995,5.7327 -15.7649,4.2995 -20.0644,8.599 -10.032,10.0322 -17.1979,32.9629 -28.6632,42.9951 -18.6312,18.631 -51.594,17.198 -73.0915,20.0642 -7.1658,-25.7969 41.5618,-58.7598 20.0644,-74.5245 -7.1659,-4.2995 -17.1981,1.433 -25.7971,2.8662 -8.599,0 -18.6312,4.2995 -27.2302,1.4333 -15.7647,-4.2995 -53.0271,-31.5297 -51.5939,-51.5941 0,-2.8663 7.1658,-2.8663 7.1658,-5.7325 -1.4331,-4.2995 -15.7647,-8.599 -11.4652,-17.1981 5.7327,-5.7326 12.8984,-10.0321 17.1979,-17.198 1.4332,-1.4331 -2.8663,-4.2995 -1.4332,-7.1658 1.4332,-4.2995 14.3317,2.8663 15.7649,-1.4332 0,-11.4653 -1.4332,-22.9307 -5.7327,-31.5296 -7.1658,-11.4653 -54.4603,-30.0965 -51.5939,-53.0272 0,-7.1657 12.8985,-7.1657 20.0643,-8.5989 4.2994,0 7.1659,8.5989 11.4654,7.1657 7.1657,-1.433 53.0271,-38.6954 45.8612,-51.5939 -4.2995,-8.5989 -18.6311,-10.0322 -22.9307,-20.0644 -4.2995,-5.7325 1.4332,-15.7647 -1.4331,-21.4974 -20.0644,-34.3959 -57.3266,-17.198 8.599,-55.8934 14.3317,25.797 53.0271,-2.8664 71.6583,-7.1659 12.8985,-2.8663 22.9306,14.3317 35.8291,14.3317 1.4332,0 41.5619,-41.5619 41.5619,-48.7276 1.433,-7.1658 -11.4654,-10.0322 -10.0322,-15.7649 2.8663,-8.599 14.3317,-11.4653 21.4974,-17.198 7.1658,-5.7326 12.8985,-11.4653 20.0644,-17.198 2.8663,-1.4332 5.7326,-4.2995 7.1658,-2.8662 2.8663,8.5989 1.4332,17.1979 1.4332,25.7969 0,4.2995 -4.2995,10.0322 -2.8664,15.7649 10.0322,18.631 32.9628,22.9305 48.7276,28.6632 1.4332,0 4.2995,2.8663 4.2995,5.7327 -1.4331,5.7327 -11.4653,10.0322 -10.0321,15.7648 1.4331,5.7327 11.4653,2.8664 15.7648,7.1659 5.7327,7.1658 8.599,31.5296 17.198,24.3637 24.3638,-18.6311 -4.2995,-12.8984 -4.2995,-27.2301 0,-31.5297 34.396,-40.1287 40.1286,-65.9256 0,-2.8663 -15.7648,-41.5619 8.599,-37.2624 18.6312,2.8665 -4.2995,31.5297 32.9628,40.1287 22.9307,7.1659 4.2995,-21.4975 42.995,-14.3316 11.4654,1.4331 5.7327,24.3638 15.7649,30.0965 5.7327,2.8662 17.1979,-2.8663 15.7647,-8.599 -2.8663,-4.2995 -8.599,-7.1659 -10.032,-12.8985 0,-5.7327 8.5989,-10.0322 7.1657,-15.7649 -2.8663,-7.1658 -28.6633,-22.9307 -8.599,-37.2622 25.797,-18.6312 15.7648,22.9305 45.8614,15.7648 17.1979,-5.7327 -10.0322,-25.797 -10.0322,-28.6634 2.8663,-5.7326 7.1658,-10.0321 8.599,-15.7648 2.8662,-5.7327 1.4332,-12.8984 2.8662,-18.6311 v 0 c 22.9307,11.4654 2.8665,-2.8663 25.7971,0 11.4653,1.4332 22.9307,31.5296 45.8612,18.6311 l 18.6312,-38.6954 c 11.4654,-8.599 61.6261,-2.8664 74.5247,-4.2995 21.4975,-1.4332 38.6954,-20.0644 60.1929,-20.0644 5.7327,0 -10.0322,7.1658 -11.4653,12.8985 -7.1659,30.0964 11.4653,10.0322 22.9307,27.2302 2.8663,4.2995 -4.2995,10.0322 -1.4332,15.7649 1.4332,2.8662 5.7327,-2.8665 8.599,0 27.2301,20.0642 34.3959,22.9305 71.6583,28.6632 35.8291,4.2995 71.6582,7.1659 107.4874,10.0322 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccscccccccccccccssccccsccsccccsccccsccccccccccccccccccscccccsccccccsccccccscccccsscccccccscccccccccccccccccccscccccccccccccsccccccccccsssccccscsccccccccccccsccccccccccsccsscsscsccccccccccccccccccccsccsccscccsccsccccccccccccssscccscssccc" /><path
+ id="gr"
+ class="eu europe"
+ d="m 8708.5935,7823.3216 c -15.7648,30.0965 12.8984,10.0322 -2.8663,54.4603 -1.4332,4.2995 -117.5196,30.0965 -128.9849,34.396 -8.599,2.8663 -88.8562,40.1286 -97.4552,32.9628 -25.7971,-28.6633 20.0642,-25.7969 -61.6263,-35.8291 -83.1235,-10.0322 20.0644,10.0322 -50.1608,10.0322 -4.2995,0 -5.7325,-7.1659 -10.032,-7.1659 -8.599,-1.4332 -18.6312,0 -28.6634,1.4332 -10.0322,2.8663 -21.4975,8.599 -31.5297,10.0322 -21.4974,0 -12.8984,-27.2302 -11.4652,-35.8292 0,-11.4654 -4.2995,-24.3638 0,-34.396 15.7647,-44.4282 30.0964,5.7327 40.1286,10.0322 10.0322,4.2995 21.4975,-1.4333 31.5297,-5.7327 7.1657,-2.8663 11.4652,-21.4975 17.1979,-15.7648 7.1658,5.7327 -17.1979,11.4653 -14.3317,18.6312 1.4333,7.1658 14.3317,2.8663 21.4975,5.7325 7.1659,2.8665 12.8985,12.8985 21.4975,14.3317 7.1659,1.4332 12.8986,-4.2995 20.0643,-7.1657 14.3317,-4.2995 24.3639,-15.7649 37.2624,-20.0644 15.7648,-4.2995 31.5297,-2.8663 47.2944,-5.7326 10.0322,-2.8664 1.4332,8.599 10.0322,10.0321 15.7648,4.2994 31.5297,-8.599 48.7276,-7.1658 4.2995,0 7.1658,8.599 11.4653,7.1658 7.1659,-1.4331 32.9628,-22.9307 45.8613,-20.0643 2.8664,1.4331 -21.4974,31.5297 12.8985,41.5617 7.1659,2.8664 7.1659,-12.8985 12.8985,-17.198 30.0965,-30.0964 25.7971,8.599 58.7598,-38.6954 m -295.2321,-1084.9064 1.4332,-1.4331 v 0 h 1.4332 v 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 c 1.433,0 45.8612,-14.3317 45.8612,-14.3317 31.5296,-24.3639 -58.7598,-64.4925 -2.8663,-75.9578 v 0 0 0 h 1.4332 v 0 0 0 0 0 h 4.2995 v 0 h 1.4331 v 0 h 1.4332 v 0 0 0 c 2.8663,0 5.7327,0 7.1657,1.4331 v 0 h 2.8665 v 0 c 31.5296,7.1659 74.5245,37.2624 53.0271,63.0593 -15.7648,17.198 -31.5297,8.599 -37.2624,37.2624 l 11.4654,41.5618 c -1.4332,10.0321 -21.4974,42.9949 -31.5296,48.7276 l -1.4332,15.7648 c -24.3638,-12.8985 21.4974,-14.3317 -1.4332,-34.396 -11.4653,-8.5989 -57.3266,11.4653 -71.6583,10.0322 -45.8612,-2.8664 -51.5939,-32.9628 -81.6904,4.2995 -7.1659,8.599 -14.3317,15.7648 -22.9306,21.4975 -40.1287,21.4974 -18.6312,-28.6634 -58.7598,-4.2995 -15.7649,10.0322 -4.2995,34.3959 -35.8292,48.7276 -27.2301,11.4653 -30.0964,-22.9307 -55.8935,10.0322 -10.0321,12.8985 7.1659,11.4653 11.4654,14.3317 12.8985,7.1658 11.4652,28.6632 22.9307,40.1286 2.8662,2.8663 8.5989,1.4331 14.3316,0 1.4331,0 2.8663,-1.4332 4.2995,-4.2995 0,-2.8664 -5.7327,-8.5989 -1.4332,-8.5989 5.7327,-1.4333 5.7327,11.4652 11.4653,12.8984 15.7649,4.2995 48.7276,10.0321 54.4603,31.5297 11.4654,41.5617 -37.2624,-18.6312 -37.2624,-18.6312 -15.7647,-8.599 -61.626,-14.3317 -65.9255,12.8985 -4.2995,17.1979 32.9628,11.4652 44.4281,22.9306 8.599,8.599 7.1659,37.2623 -2.8663,32.9628 -22.9307,-8.599 -24.3639,-34.396 -47.2945,-45.8612 -5.7326,-1.4332 -11.4653,1.433 -17.198,0 -7.1658,0 -15.7648,-7.1659 -20.0642,-1.4332 -32.9629,35.8291 63.0593,45.8613 55.8933,58.7598 -2.8662,4.2995 -40.1286,2.8662 -51.5939,0 -4.2994,-1.4332 -14.3316,-47.2946 -14.3316,-47.2946 -11.4653,-14.3315 -51.5939,-5.7325 -67.3588,-17.1979 -30.0965,-22.9307 0,-18.6312 0,-47.2945 0,-5.7327 -15.7648,10.0322 -24.3638,17.198 -7.1659,7.1658 -18.6311,12.8985 -20.0644,22.9307 -1.4332,7.1658 12.8985,8.599 14.3317,15.7647 l -10.0322,42.9951 c 4.2995,27.2302 37.2624,40.1286 53.0271,57.3266 l 18.6312,37.2623 c 10.0322,10.0321 83.1236,45.8612 71.6583,63.0593 -2.8663,4.2995 -58.7598,-24.3638 -63.0593,-22.9306 -4.2995,0 0,8.599 0,12.8985 -2.8663,10.0321 -18.6312,31.5296 -8.599,30.0964 2.8663,-1.4332 1.4332,-8.599 4.2995,-7.1658 4.2995,1.4331 2.8663,8.599 7.1658,11.4653 10.0322,7.1658 41.5618,7.1658 17.1981,28.6634 -20.0644,17.1979 -40.1286,18.6311 -67.3588,30.0964 -4.2995,1.4332 8.599,5.7327 12.8985,4.2995 4.2995,-1.4332 7.1657,-8.599 12.8984,-8.599 5.7327,0 5.7327,8.599 10.0322,11.4653 2.8663,1.4332 5.7326,-1.4331 8.599,-2.8663 11.4653,0 22.9307,-2.8663 32.9627,1.4332 8.599,2.8663 7.166,17.198 15.7649,18.6312 10.0322,2.8663 20.0643,-12.8986 28.6633,-8.5992 8.5991,4.2995 -1.4331,22.9307 7.1659,27.2302 11.4652,7.1659 25.7969,-12.8985 35.8291,-5.7326 5.7327,2.8663 0,25.797 22.9307,27.2302 28.6632,1.4331 37.2622,-8.5991 57.3266,14.3315 15.7647,17.1982 -8.599,10.0322 -8.599,20.0644 0,7.1658 8.599,11.4653 10.0322,17.198 2.8663,7.1658 -1.4332,15.7649 1.4331,21.4975 7.1659,12.8984 25.7971,18.6311 10.0322,42.995 -2.8663,4.2995 -8.599,1.4331 -12.8985,0 -2.8663,-2.8664 0,-7.1659 -1.4332,-10.0322 -1.4331,-2.8663 -18.6312,-14.3317 -20.0643,-14.3317 -2.8664,-1.4332 -4.2995,5.7327 -7.1659,4.2995 -10.032,-7.1657 -25.7969,-35.8291 -40.1286,-40.1286 -18.6312,-5.7327 -8.599,15.7649 -18.6312,18.6312 -17.198,5.7327 -65.9256,20.0642 -61.6261,27.2301 4.2995,10.0322 21.4975,5.7327 31.5297,10.0322 4.2995,1.4331 -8.599,4.2995 -8.599,8.599 0,4.2995 7.1659,4.2995 8.599,7.1658 2.8664,5.7327 -1.4331,14.3317 2.8664,18.6312 15.7647,15.7647 42.9949,7.1657 57.3266,24.3637 8.599,10.0322 -22.9307,12.8986 -35.8292,12.8986 -20.0643,1.4331 -35.8291,0 -41.5618,-1.4332 -4.2995,-1.4332 -1.4332,-8.599 -4.2995,-12.8985 -1.4332,-1.4332 -5.7327,-1.4332 -7.1658,-1.4332 -11.4654,0 -27.2301,-8.599 -34.396,0 -11.4653,15.7649 35.8291,51.5941 41.5618,61.6261 1.4332,5.7327 -2.8663,11.4654 -1.4332,15.7649 2.8664,4.2995 11.4654,0 14.3317,4.2995 5.7327,10.0322 22.9307,41.5618 28.6634,54.4603 5.7325,12.8985 -11.4655,4.2995 -11.4655,8.599 0,4.2995 5.7328,5.7327 7.166,8.599 0,2.8663 -5.7327,7.1657 -4.2995,10.0322 1.433,2.8662 34.3959,21.4974 31.5296,32.9627 -2.8664,4.2995 -12.8986,4.2995 -18.6312,2.8664 -4.2994,-2.8664 -2.8664,-11.4654 -7.1659,-14.3317 -2.8662,-2.8664 -8.599,4.2995 -12.8984,1.4332 -24.3638,-18.6311 -34.396,-75.9579 -70.2251,-25.7971 -1.4332,2.8665 1.4332,32.9629 4.2995,50.1609 1.4332,4.2995 1.4332,14.3317 -2.8663,12.8984 -35.8291,-4.2994 -12.8986,-40.1286 -24.3639,-55.8933 -1.4332,-1.4333 -5.7327,0 -7.1658,-1.4333 -8.5989,-8.5989 -12.8984,-18.6311 -20.0643,-28.6633 -4.2995,-7.1658 -7.1658,-20.0643 -15.7648,-21.4975 -68.792,-15.7647 -7.1659,50.1608 -31.5296,61.6261 -41.5619,18.6312 -30.0966,-28.6632 -31.5297,-38.6954 0,-2.8663 -4.2995,-2.8663 -5.7327,-2.8663 -12.8985,1.4331 -20.0644,-15.7649 -22.9307,-31.5296 -2.8662,-21.4975 14.3317,2.8662 17.198,-30.0966 1.4332,-22.9305 -35.8291,-51.5939 -54.4603,-57.3266 -4.2995,-1.4331 -8.599,8.599 -11.4653,4.2995 -5.7327,-7.1658 0,-20.0643 -7.1658,-27.2302 -8.5989,-8.599 -47.2945,2.8664 -27.2301,-24.3637 1.4331,-1.4332 7.1658,1.4332 8.599,-1.4332 22.9306,-15.7648 11.4652,-28.6633 18.6311,-55.8934 0,-2.8664 2.8663,5.7325 5.7326,5.7325 1.4332,0 -2.8663,-5.7325 -1.4331,-5.7325 14.3316,0 28.6633,14.3315 40.1287,2.8663 15.7647,-15.7648 -5.7327,-17.198 22.9306,-35.8292 20.0643,-12.8985 34.396,8.599 50.1607,14.3317 22.9307,8.599 50.1608,5.7327 74.5247,12.8985 14.3317,2.8663 22.9307,20.0642 37.2622,21.4974 5.7327,1.4332 17.1981,-5.7327 15.7649,-12.8984 -2.8662,-5.7327 -18.6311,1.4332 -21.4974,-5.7327 -14.3317,-32.9628 35.8291,15.7648 53.0271,-31.5297 1.4332,-4.2995 -10.0322,2.8664 -15.7649,0 -2.8663,-1.4331 0,-8.599 -2.8663,-8.599 -2.8663,0 -4.2995,17.198 -11.4653,8.599 -11.4654,-14.3315 -20.0643,5.7327 -38.6955,-2.8663 -10.0322,-5.7327 -12.8985,-22.9306 -24.3638,-24.3639 -7.1659,-1.433 -2.8664,21.4976 -11.4654,21.4976 -25.7969,-1.4332 -8.5989,-60.193 -27.2301,-27.2301 -2.8663,5.7325 7.1659,17.1979 1.4332,21.4974 -10.0322,10.0322 -55.8935,-8.5989 -65.9256,-4.2995 -18.6312,7.1658 -32.9629,24.3638 -60.193,31.5297 -15.7648,4.2995 -20.0643,-20.0644 -28.6632,-24.3639 -4.2997,-1.4331 1.433,10.0322 -1.4332,12.8985 -5.7328,8.599 -15.7649,15.7649 -24.3639,20.0644 -2.8663,1.4332 -1.4331,-5.7327 -2.8663,-5.7327 -15.7649,-7.1658 0,-20.0643 -10.0322,-35.8292 -5.7327,-8.5989 -12.8985,-17.1979 -21.4975,-22.9306 -2.8662,-1.4331 -7.1657,4.2995 -8.5989,1.4332 -7.166,-5.7327 -4.2995,-18.6312 -11.4654,-22.9307 -14.3316,-10.0322 -40.1287,28.6634 -18.6311,-24.3637 -2.8664,-1.4332 -5.7327,-2.8665 -10.0322,-5.7327 -4.2995,-4.2995 -1.4332,-11.4654 -4.2995,-15.7649 -15.7649,-20.0643 -55.8935,-38.6954 -73.0915,-53.0271 -5.7327,-2.8663 2.8663,-17.198 -1.4332,-21.4975 -4.2995,-4.2995 -25.7969,11.4653 -40.1286,-22.9307 1.4332,-1.4332 1.4332,-1.4332 2.8664,-2.8662 1.4331,-1.4333 -1.4332,-2.8665 0,-2.8665 24.3638,-4.2995 40.1286,15.7649 38.6954,-21.4974 0,-8.599 20.0644,4.2995 15.7649,-8.599 l -18.6311,-28.6634 c 1.433,-10.032 45.8613,-24.3637 48.7276,-28.6632 0,-1.4332 8.599,-58.7598 8.599,-58.7598 10.0321,-38.6954 18.6311,8.599 32.9628,-45.8613 4.2995,-17.198 -15.7649,-22.9307 -15.7649,-41.5617 10.0322,1.433 106.0542,-28.6634 111.7869,-32.9629 15.7649,-17.198 4.2995,-40.1286 32.9629,-50.1608 10.0322,-4.2995 65.9256,2.8662 74.5246,-2.8663 4.2995,-2.8664 2.8664,-11.4654 7.1659,-14.3317 2.8663,-1.4332 7.1658,7.1658 10.032,4.2995 17.074,-13.3664 23.3109,-31.4532 19.6723,-48.2271 6.1679,15.9809 6.373,-10.9646 6.1248,-13.399 -1.1923,19.1243 -0.9238,18.6312 17.198,18.6312 5.7325,0 12.8985,4.2995 17.198,0 5.7327,-2.8664 5.7327,-14.3317 11.4652,-17.1981 15.7649,-10.0321 37.2624,-4.2995 54.4603,-10.0321 40.1287,-14.3317 50.1609,-31.5296 75.9578,-47.2945 4.2995,-2.8663 11.4654,8.5989 17.198,5.7327 8.599,-1.4332 12.8984,-15.7649 21.4976,-12.8985 11.4652,1.4331 14.3315,17.198 24.3637,22.9307 1.4332,2.8662 5.7327,-2.8665 8.599,-1.4332 4.2995,1.4332 7.1659,11.4652 11.4654,10.0321 5.7326,-1.4332 5.7326,-12.8984 11.4653,-14.3316 15.7649,-4.2995 34.3959,4.2995 48.7276,11.4652 2.8664,1.4332 0,8.599 4.2995,8.599 20.0644,1.4332 51.594,-22.9305 71.6583,-30.0964 z m -583.2985,554.6351 c 17.1981,-31.5297 -25.797,2.8664 -27.23,1.4332 -10.0322,-4.2995 -18.6312,-22.9307 -27.2302,-17.198 -2.8664,0 7.1658,21.4975 5.7326,30.0964 14.3317,0 40.1286,0 48.7276,-14.3316 z m -24.3638,170.5467 c -11.4654,10.0322 -15.7648,-4.2995 -25.7969,-5.7326 -5.7327,0 -11.4654,7.1658 -17.198,5.7326 -10.0322,-2.8663 -5.7327,-35.8291 -14.3317,-28.6632 -4.2994,2.8662 2.8663,12.8984 0,18.6311 -11.4654,24.3638 -18.6311,-17.1981 12.8985,-58.7598 4.2995,7.1658 10.0322,14.3316 12.8985,22.9307 0,1.4331 -4.2995,14.3315 2.8663,15.7648 7.1659,1.4332 44.4281,17.1979 28.6633,30.0964 z m 490.1426,-121.8191 c -7.1657,-8.599 -17.1979,-15.7648 -28.6632,-20.0643 -17.198,-5.7327 -41.5618,21.4975 -60.193,2.8663 -2.8663,-4.2995 5.7327,-10.032 2.8664,-14.3315 -4.2995,-12.8986 -88.8562,-53.0273 -104.6211,-50.1609 -8.599,2.8663 -12.8985,12.8985 -20.0643,17.198 -4.2996,1.4332 2.8663,-8.599 5.7326,-11.4654 4.2995,-4.2995 10.0322,-8.599 14.3317,-12.8985 5.7327,-5.7325 10.0322,-10.032 15.7649,-14.3315 8.5989,-5.7327 17.1979,-10.0322 25.7969,-14.3317 24.3637,12.8985 21.4975,25.797 45.8613,38.6954 4.2995,2.8663 10.0321,4.2995 15.7648,4.2995 2.8663,0 4.2995,-5.7327 7.1658,-4.2995 7.1659,2.8663 12.8986,11.4654 21.4976,14.3317 8.5989,1.4332 38.6954,-15.7649 44.4281,-5.7327 1.4331,1.4332 -1.4332,2.8664 -1.4332,4.2995 -1.4332,1.4332 -2.8663,4.2995 -1.4332,5.7327 5.7327,5.7327 15.7649,5.7327 20.0644,12.8985 0,1.4332 -2.8665,44.4281 11.4652,54.4603 15.7649,11.4653 80.2573,-10.0322 54.4603,32.9629 -14.3316,22.9305 -53.0271,-5.7327 -61.6261,-15.7649 -1.4332,-2.8663 4.2995,-4.2995 4.2995,-7.1658 -1.4332,-4.2995 -5.7327,-8.599 -10.0322,-11.4654 -2.8662,0 -5.7327,7.1659 -7.1657,4.2995 0,-2.8663 8.599,-7.1658 5.7325,-10.0322 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cccccscccccscccscccccccccccccccccccccccccccccccccscsccccccccccccccccssccscccccccccccccscccccccsccccccccsccccccscccccccssccccsccccsccccsccccccsssccccsccccsscsccccccccscccccccccscccssscccccccssccccccccccscccccccccscsccccccccccscccccccccccccccscsccccccscccccccscccccccsscccsccccsccccccc" /><path
+ id="ee"
+ class="eu europe"
+ d="m 7607.9223,3723.0345 c 11.4654,-2.8663 55.8935,-44.4281 68.792,-54.4602 8.5989,-5.7327 24.3637,21.4975 20.0642,2.8663 0,-1.4332 -1.4331,-1.4332 -1.4331,-2.8663 0,-2.8664 -2.8664,-7.1659 0,-7.1659 12.8985,0 28.6633,21.4975 37.2623,24.3639 4.2994,1.4331 8.5989,-7.1659 12.8984,-7.1659 4.2995,1.4332 2.8664,10.0322 7.1659,11.4654 8.599,2.8663 17.198,-5.7327 25.797,-2.8664 4.2995,1.4332 -1.4332,10.0322 1.4332,12.8986 5.7326,8.599 51.5939,38.6954 61.6261,37.2622 1.4332,0 28.6634,-30.0964 28.6634,-30.0964 5.7325,-1.4332 48.7276,14.3317 50.1606,7.1657 2.8665,-7.1657 -7.1657,-27.2301 -2.8662,-31.5296 2.8662,-2.8663 10.0322,-2.8663 11.4652,-7.1658 1.4332,-2.8663 -4.2995,-4.2995 -4.2995,-8.599 -1.433,-2.8662 -1.433,-7.1657 0,-10.0322 17.1981,-14.3316 18.6312,1.4333 24.3639,-15.7647 1.4332,-4.2995 -11.4654,-1.4332 -15.7649,-5.7327 -10.032,-10.0322 -4.2995,-17.198 -7.1658,-27.2302 -1.4332,-4.2995 -24.3638,-17.1979 -28.6633,-21.4974 -11.4653,-20.0644 1.4332,-11.4654 1.4332,-30.0965 0,-25.7971 -28.6632,-53.0272 -32.9629,-78.8242 -1.433,-15.7647 15.7649,-27.2301 20.0644,-38.6954 0,0 1.4332,-35.8291 1.4332,-35.8291 0,-7.1659 -5.7327,-15.7649 -1.4332,-21.4975 14.3317,-30.0966 38.6954,-17.1981 4.2995,-38.6956 -1.4332,5.7327 -4.2995,11.4655 -14.3317,17.198 -12.8985,8.5991 -147.616,10.0322 -170.5467,7.1659 -4.2995,0 -7.1658,-7.1659 -12.8985,-5.7327 -27.2301,5.7327 34.3961,21.4975 -11.4652,15.7649 -7.166,-1.4332 -8.599,-17.1981 -15.7649,-15.7649 -5.7326,1.4332 4.2995,12.8985 1.4332,18.6312 -4.2995,8.599 -12.8985,17.198 -21.4975,21.4975 -14.3317,7.1659 -30.0966,10.0322 -45.8613,7.1659 -5.7327,-1.4332 -10.0322,-12.8986 -14.3317,-10.0322 -7.1658,4.2995 10.0322,20.0642 2.8664,21.4974 -14.3317,4.2995 -57.3267,5.7327 -65.9257,27.2302 -2.8663,7.1658 12.8985,14.3317 8.599,20.0642 -7.1658,11.4654 -50.1607,8.5992 -55.8934,21.4976 -20.0644,54.4602 17.198,20.0643 25.7969,42.995 4.2995,12.8984 -17.1979,28.6633 -11.4652,41.5618 5.7325,15.7647 28.6632,31.5296 31.5296,37.2622 8.599,12.8986 -5.7327,27.2302 12.8985,37.2624 7.1658,2.8662 15.7648,-4.2995 22.9307,-1.4333 7.1657,1.4333 11.4652,15.7649 18.631,11.4655 10.0322,-5.7327 10.0322,-60.1931 40.1286,-24.3639 2.8665,4.2995 -7.1657,8.599 -7.1657,14.3317 0,11.4654 5.7327,22.9306 5.7327,34.3959 0,28.6634 -11.4654,12.8986 -8.599,38.6956 z m -156.2151,-110.3537 c 18.6312,12.8985 17.198,14.3317 40.1287,24.3639 10.0321,5.7326 -17.198,-12.8985 -11.4653,-1.4332 5.7325,12.8985 1.4331,-5.7327 -8.5991,-4.2995 -8.599,1.4332 0,20.0642 -7.1658,27.2302 -1.4332,1.4332 -7.1658,-7.166 -8.599,-4.2995 -2.8663,2.8662 1.4332,10.0322 0,15.7647 -7.1658,18.6312 -21.4974,12.8985 -25.7969,14.3317 -2.8665,0 -2.8665,5.7327 -5.7327,7.1659 -8.599,4.2995 -18.6312,7.1658 -25.797,11.4653 -1.4332,0 -1.4332,1.4332 -1.4332,2.8663 0,11.4654 2.8663,24.3638 0,35.8291 -2.8663,7.1659 -12.8985,21.4976 -18.6312,15.7649 -17.1979,-20.0644 15.7649,-31.5297 12.8985,-47.2945 -1.4331,-4.2995 -8.599,1.4332 -12.8985,-1.4331 -8.599,-4.2995 -21.4974,-7.1659 -25.7969,-17.198 -2.8663,-5.7327 12.8984,-8.5991 11.4652,-14.3317 -1.433,-11.4654 -24.3637,-15.7649 -20.0642,-25.7971 2.8663,-8.5989 17.198,11.4654 27.2302,11.4654 8.5989,0 -4.2995,-22.9306 4.2994,-27.2301 4.2995,-1.4332 10.0322,12.8985 11.4653,8.599 10.0322,-34.396 31.5297,-21.4975 64.4925,-31.5297 z m -51.594,-83.1235 c 2.8664,0 7.1659,-1.4332 8.5991,1.433 2.8663,2.8665 -1.4332,10.0322 2.8663,12.8985 5.7327,2.8664 15.7647,-4.2995 21.4974,-1.4331 48.7277,28.6633 2.8663,22.9307 0,28.6633 -11.4652,27.2301 28.6634,4.2995 -24.3637,34.396 -15.7649,8.599 -17.1981,-25.797 -17.1981,-25.797 -5.7326,-15.7648 -31.5297,-10.0321 -41.5617,-12.8985 -10.0322,-1.4331 21.4975,-5.7326 31.5296,-8.599 12.8985,-2.8663 15.7648,-20.0642 18.6311,-28.6632 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="lv"
+ class="eu europe"
+ d="m 7341.3535,4114.2888 c 5.7326,0 11.4653,1.4332 15.7648,-2.8663 5.7327,-8.599 4.2995,-21.4974 10.0322,-30.0966 11.4652,-14.3316 57.3266,-42.9949 74.5245,-47.2944 10.0322,-2.8662 55.8936,0 65.9256,-2.8662 5.7327,-1.4333 10.0322,-12.8985 15.7649,-12.8985 10.0322,1.4331 15.7648,25.7969 21.4975,18.631 4.2995,-2.8663 2.8664,-10.032 7.1659,-14.3315 12.8984,-12.8986 18.631,1.433 34.3959,0 8.599,-1.4332 12.8985,-10.0322 21.4975,-10.0322 1.4332,-1.4332 44.4281,10.0322 45.8613,8.599 7.1658,-1.4332 10.0322,-11.4654 17.198,-12.8985 12.8984,-2.8664 12.8984,18.6312 24.3638,1.4331 8.599,-10.0321 8.599,-34.396 25.797,-37.2622 14.3317,-2.8665 20.0642,42.9949 38.6954,42.9949 18.6312,0 37.2624,-11.4653 55.8935,-5.7327 27.2302,7.1659 70.2251,60.193 103.188,65.9257 35.8291,5.7325 37.2622,-48.7276 58.7598,-47.2946 7.1658,0 11.4653,8.599 17.198,8.599 4.2994,0 1.433,-10.032 4.2994,-11.4652 8.599,-2.8663 20.0643,5.7327 27.2302,0 0,0 7.1658,-42.995 10.0321,-47.2944 2.8664,-8.5992 30.0965,-30.0966 30.0965,-31.5297 11.4653,-41.5618 -30.0965,-80.2574 -54.4603,-100.3217 -10.0322,-7.1657 10.0322,-20.0643 -10.0322,-22.9306 -7.1657,-1.4332 -12.8984,10.0322 -20.0642,8.599 -2.8664,0 0,-4.2995 0,-7.1658 0,-1.4332 1.433,-4.2995 1.433,-5.7327 0,-10.0322 2.8665,-20.0643 1.4333,-30.0965 -1.4333,-4.2994 -11.4654,-2.8664 -11.4654,-7.1659 0,-18.631 34.396,-30.0964 -12.8985,-47.2944 -5.7326,-1.4332 -14.3316,-1.4332 -18.6311,-5.7327 -4.2996,-5.7327 2.8663,-15.7647 -2.8664,-18.6311 -5.7327,-4.2995 -11.4652,2.8664 -18.6312,4.2994 -1.433,7.166 -44.4281,-8.5989 -50.1606,-7.1657 0,0 -27.2302,30.0964 -28.6634,30.0964 -10.0322,1.4332 -55.8935,-28.6632 -61.6261,-37.2622 -2.8664,-2.8664 2.8663,-11.4654 -1.4332,-12.8986 -8.599,-2.8663 -17.198,5.7327 -25.797,2.8664 -4.2995,-1.4332 -2.8664,-10.0322 -7.1659,-11.4654 -4.2995,0 -8.599,8.599 -12.8984,7.1659 -8.599,-2.8664 -24.3638,-24.3639 -37.2623,-24.3639 -2.8664,0 0,4.2995 0,7.1659 0,1.4331 1.4331,1.4331 1.4331,2.8663 4.2995,18.6312 -11.4653,-8.599 -20.0642,-2.8663 -12.8985,10.0321 -57.3266,51.5939 -68.792,54.4602 l -2.8663,-1.433 c 1.433,2.8662 1.433,7.1657 1.433,11.4652 5.7328,30.0966 42.9951,71.6583 34.3961,104.6212 -4.2995,20.0642 -38.6955,61.6261 -60.193,64.4923 -8.599,1.4332 -14.3317,-7.1657 -21.4975,-8.5989 -14.3316,-4.2995 -17.1979,5.7327 -25.7969,-8.599 -10.0322,-14.3316 -10.0322,-34.396 -22.9307,-45.8612 -12.8984,-12.8986 -42.995,-18.6312 -57.3266,-31.5297 -8.599,-7.1659 0,-25.7971 -8.599,-30.0966 -12.8986,-5.7325 -22.9306,14.3317 -34.396,21.4976 -14.3317,11.4653 -34.396,14.3316 -47.2944,28.6633 -34.3961,35.8291 -4.2995,77.391 -17.198,113.2201 -4.2995,14.3317 -22.9307,20.0642 -27.2302,34.3959 -2.8664,8.599 4.2995,17.198 4.2995,27.2302 0,10.0322 -4.2995,20.0644 -1.4332,28.6634 0,1.433 1.4332,2.8662 2.8663,2.8662 0,0 0,0 0,0 -14.3315,17.1979 8.5991,60.1931 17.1981,80.2573 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="lt"
+ class="eu europe"
+ d="m 7579.2589,4400.9219 c 2.8664,-2.8662 2.8664,-8.5989 7.1659,-10.0321 21.4975,-7.1658 11.4653,11.4654 25.797,17.1981 40.1286,17.1979 24.3638,-14.3317 50.1608,22.9305 8.599,11.4654 0,27.2301 8.599,32.9628 17.1979,11.4653 58.7598,-11.4652 70.225,-17.1979 11.4654,-4.2995 17.198,21.4974 30.0966,10.0322 8.599,-5.7327 10.0321,-20.0644 18.6311,-25.7971 7.1659,-4.2995 15.7648,0 22.9306,-1.4331 7.1659,-2.8664 -12.8985,-18.6312 -7.1658,-24.3639 7.1658,-8.5989 21.4975,-5.7327 31.5297,-10.0322 22.9307,-12.8984 -8.599,-11.4652 7.1658,-27.2301 4.2995,-5.7326 37.2623,-21.4975 40.1286,-2.8663 1.4332,4.2995 -5.7326,7.1659 -5.7326,11.4654 -1.4332,12.8985 27.2302,2.8663 30.0965,-4.2995 12.8985,-31.5296 -31.5297,-22.9307 -31.5297,-28.6634 -1.4332,-5.7327 12.8985,-45.8613 12.8985,-47.2945 0,0 -12.8985,-40.1286 -12.8985,-40.1286 0,-1.4331 5.7327,-1.4331 5.7327,-4.2995 17.198,-53.0271 4.2995,2.8664 31.5297,-40.1287 7.1658,-8.5989 0,-22.9306 5.7325,-32.9627 7.166,-10.0322 28.6634,4.2995 37.2624,-5.7327 4.2995,-5.7327 -1.4332,-14.3317 0,-20.0644 2.8663,-4.2995 10.0322,-4.2995 11.4653,-8.599 5.7327,-20.0642 -41.5617,5.7327 -40.1287,-7.1657 1.4332,-21.4975 11.4654,-40.1287 2.8665,-61.6261 v -1.4332 c -4.2995,1.4332 -8.599,1.4332 -12.8985,1.4332 -32.9629,-5.7327 -75.9578,-58.7598 -103.188,-65.9257 -18.6311,-5.7326 -37.2623,5.7327 -55.8935,5.7327 -18.6312,0 -24.3637,-45.8614 -38.6954,-42.9949 -17.198,2.8662 -17.198,27.2301 -25.797,37.2622 -11.4654,17.1981 -11.4654,-4.2995 -24.3638,-1.4331 -7.1658,1.4331 -10.0322,11.4653 -17.198,12.8985 -1.4332,1.4332 -44.4281,-10.0322 -45.8613,-8.599 -8.599,0 -12.8985,8.599 -21.4975,10.0322 -15.7649,1.433 -21.4975,-12.8986 -34.3959,0 -4.2995,4.2995 -2.8664,11.4652 -7.1659,14.3315 -5.7327,7.1659 -11.4653,-17.1979 -21.4975,-18.631 -5.7327,0 -10.0322,11.4652 -15.7649,12.8985 -10.032,2.8662 -55.8934,0 -65.9256,2.8662 -17.1979,4.2995 -63.0593,32.9628 -74.5245,47.2944 -5.7327,8.5992 -4.2995,21.4976 -10.0322,30.0966 -4.2995,4.2995 -10.0322,2.8663 -15.7648,2.8663 1.4331,2.8663 1.4331,4.2995 2.8663,5.7327 10.0322,37.2624 -7.1658,14.3317 7.1658,50.1608 1.4332,2.8663 2.8664,5.7326 5.7327,8.599 2.8662,5.7326 5.7327,10.0321 8.5989,14.3317 7.166,10.0321 12.8985,21.4975 14.3317,32.9627 1.4332,8.599 -4.2995,17.198 -2.8662,25.797 1.433,2.8664 2.8662,-7.1658 5.7325,-7.1658 5.7327,-1.4332 5.7327,10.0322 7.1659,14.3317 0,2.8663 0,4.2995 0,5.7327 4.2995,-1.4332 5.7326,-7.1659 10.0321,-7.1659 4.2996,1.4332 4.2996,8.599 8.5991,11.4654 17.198,10.032 45.8612,8.599 64.4924,14.3315 2.8664,1.4332 -1.4332,5.7327 1.4332,5.7327 21.4974,0 41.5618,-18.6311 61.6261,-12.8984 5.7327,1.4332 2.8663,12.8984 8.599,17.1979 1.4332,1.4332 5.7327,-2.8663 8.5989,-1.4332 5.7327,2.8664 11.4654,7.1659 15.7649,12.8986 8.599,14.3316 -21.4974,53.0271 10.0321,96.022 z m -222.1406,-222.1406 c -1.4332,1.4331 -1.4332,2.8663 -1.4332,2.8663 -4.2995,11.4654 5.7327,22.9307 7.1659,34.3959 1.433,14.3317 -1.4332,30.0966 -7.1659,47.2946 0,0 1.4332,0 1.4332,0 4.2995,-8.599 5.7327,-17.198 7.1657,-28.6634 0,-5.7326 1.4332,-25.7969 1.4332,-41.5617 0,-18.6312 -1.4332,-31.5296 -8.5989,-14.3317 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="pl"
+ class="eu europe"
+ d="m 6763.7877,5169.0988 c 38.6955,-5.7327 -7.1659,-37.2623 32.9628,-30.0966 25.7969,2.8665 4.2995,14.3317 20.0644,25.7971 22.9306,18.6311 41.5617,5.7326 60.1928,17.198 5.7327,4.2995 4.2995,11.4653 7.1659,15.7648 2.8663,1.4332 4.2995,-2.8663 5.7326,-2.8663 4.2995,-1.4332 8.599,-4.2995 10.0322,-1.4332 4.2995,4.2995 0,11.4654 4.2995,15.7649 0,0 31.5296,-17.1981 38.6956,-12.8985 38.6954,18.631 -15.7649,25.7969 -12.8987,44.4281 0,0 32.9629,20.0643 32.9629,20.0643 4.2995,5.7327 12.8985,42.995 34.3959,34.3959 5.7327,-2.8663 4.2995,-12.8985 8.599,-17.198 7.1659,-5.7325 21.4976,-2.8663 25.7971,-11.4652 1.4331,-2.8665 -30.0966,-34.396 -21.4974,-35.8292 21.4974,-2.8664 45.8611,18.6312 58.7596,22.9307 5.7327,1.4331 12.8986,-1.4332 18.6312,-1.4332 5.7327,0 12.8985,2.8663 18.6312,0 2.8663,-1.4332 1.4332,-7.1658 2.8663,-10.0322 1.4332,-1.4331 4.2994,-1.4331 4.2994,0 17.1982,34.3961 5.7327,12.8985 -12.8984,30.0966 -5.7326,4.2995 14.3317,5.7325 20.0644,10.032 4.2994,4.2995 2.8662,11.4654 7.1657,15.7649 2.8665,2.8663 7.1659,4.2995 10.0322,4.2995 41.5619,-4.2995 -5.7327,-7.1659 18.6312,-17.198 2.8663,-1.4332 2.8663,4.2995 5.7327,5.7326 5.7326,4.2995 21.4975,8.599 28.6632,10.0322 8.599,2.8664 21.4975,-1.4332 28.6634,4.2995 1.4331,1.4332 14.3317,35.8291 15.7648,37.2623 5.7327,4.2995 17.1979,0 22.9306,5.7327 5.7327,7.1658 1.4332,20.0643 7.1659,27.2302 2.8663,4.2995 12.8985,-2.8664 17.198,1.4331 4.2995,4.2995 -2.8664,14.3317 1.4331,18.6311 22.9306,18.6312 41.5618,-54.4603 55.8935,-44.4281 8.599,5.7327 8.599,18.6312 15.7648,25.797 1.4332,1.4332 7.1659,-2.8663 8.5989,-1.4331 1.4332,2.8663 -4.2995,8.599 -1.433,10.032 7.1657,1.4333 14.3317,-5.7325 20.0642,-2.8662 10.0322,4.2995 1.4332,31.5296 1.4332,34.3959 0,7.1659 12.8985,-4.2995 20.0644,-5.7326 5.7326,-1.4332 11.4653,10.0321 17.198,7.1658 1.433,-1.4332 18.6312,-45.8613 38.6954,-50.1608 5.7327,-1.4332 11.4653,4.2995 17.198,4.2995 5.7327,-1.4332 8.5989,-10.0322 14.3317,-8.599 11.4652,1.4332 18.6311,17.198 30.0964,17.198 8.599,0 15.7649,-8.599 20.0644,-15.7648 1.433,-2.8664 -7.1659,-2.8664 -7.1659,-5.7327 4.2995,-20.0642 12.8986,0 18.6311,-2.8663 4.2995,-1.4332 4.2995,-10.0322 8.599,-10.0322 21.4976,-5.7327 70.2252,-4.2995 88.8563,10.0322 4.2996,4.2995 2.8664,14.3316 8.5991,17.198 8.599,5.7327 18.631,5.7327 28.6632,8.599 18.6312,5.7327 35.8292,7.1657 53.0271,10.0322 7.1659,0 14.3317,-2.8665 21.4976,-1.4332 2.8663,1.4332 8.599,8.5989 7.1658,5.7327 -17.198,-54.4604 -2.8663,2.8662 -27.2302,-27.2302 -5.7327,-5.7327 5.7327,-15.7649 4.2995,-22.9307 -4.2995,-14.3317 -18.6311,-25.7969 -21.4974,-41.5618 -4.2995,-11.4653 83.1235,-173.413 94.5889,-183.4452 25.797,-21.4975 41.5617,10.0322 45.8612,-41.5619 0,-11.4652 -28.6632,-37.2622 -30.0964,-38.6954 -7.1658,-14.3317 25.7969,-12.8985 21.4974,-17.198 -22.9305,-17.1979 -64.4923,-44.4281 -77.3908,-71.6582 -2.8664,-7.1658 2.8663,-17.198 0,-25.797 -4.2995,-10.0322 -17.198,-17.198 -21.4976,-27.2301 -11.4653,-25.797 18.6312,-74.5248 -10.0321,-97.4553 -11.4654,-8.5991 -25.797,-12.8986 -38.6955,-14.3317 -2.8663,-1.4332 -5.7326,5.7326 -8.599,4.2995 -41.5617,-15.7649 48.7276,-91.7226 58.7598,-101.7548 1.4332,-1.4331 4.2995,-4.2995 4.2995,-7.1658 -4.2995,-78.8241 -67.3588,-146.1829 -93.1557,-219.2744 -8.599,-5.7327 0,-21.4974 -8.599,-32.9628 -25.797,-37.2622 -10.0322,-5.7326 -50.1608,-22.9305 -14.3317,-5.7327 -4.2995,-24.3639 -25.797,-17.1981 -4.2995,1.4332 -4.2995,8.5989 -8.599,11.4654 -65.9256,38.6954 -259.403,45.8613 -341.0934,34.3959 -25.797,27.2301 -65.9256,42.995 -98.8885,30.0964 -28.6633,-11.4652 -22.9306,-38.6954 -31.5296,-55.8934 -2.8663,-4.2995 -12.8985,-15.7649 -7.1658,-14.3316 4.2995,0 8.599,0 11.4653,1.4332 -7.1658,-4.2995 -15.7648,-8.599 -24.3638,-10.0322 -51.594,-8.599 -108.9206,37.2623 -147.616,60.193 -12.8987,7.1658 -28.6634,4.2995 -38.6956,12.8985 -24.3637,21.4974 -30.0964,55.8934 -37.2624,57.3266 -53.0271,12.8984 -103.1877,35.8291 -153.3486,55.8934 -12.8984,5.7327 -21.4974,21.4976 -34.396,24.3638 -7.1658,2.8663 -14.3316,-4.2995 -21.4975,-1.4332 -12.8985,7.1659 17.198,38.6956 4.2995,41.5619 2.8664,22.9306 25.7969,67.3588 28.6634,81.6904 4.2995,34.396 -41.5619,57.3266 -31.5297,84.5568 5.7327,17.198 60.1929,24.3637 60.1929,45.8613 0,21.4975 -17.198,28.6633 -4.2995,44.4282 4.2995,7.1657 14.3317,10.032 17.1981,18.6311 4.2995,7.1658 -1.4332,17.198 0,24.3638 1.4331,4.2995 7.1658,2.8663 8.599,5.7327 7.1658,10.0322 -10.0322,34.3959 -14.3317,42.9949 -8.599,12.8985 12.8985,20.0644 17.198,32.9629 2.8662,7.1657 -7.1658,15.7647 -2.8663,21.4974 5.7325,8.599 18.631,5.7327 24.3637,11.4654 17.198,15.7648 14.3317,88.8563 2.8664,116.0864 z m -143.3165,-540.3035 c 0,0 1.433,0 1.433,0 m 0,0 c 1.4333,1.4332 2.8665,1.4332 4.2995,2.8664 -1.433,-1.4332 -2.8662,-1.4332 -4.2995,-2.8664 z m 7.166,2.8664 c 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,1.4331 0,1.4331 0,1.4331 0,0 0,0 0,0 1.433,0 1.433,0 1.433,0 m 0,0 c 0,0 0,0 1.4332,0 -1.4332,0 -1.4332,0 -1.4332,0 z m 2.8665,0 c 0,0 0,1.4332 1.433,1.4332 -1.433,0 -1.433,-1.4332 -1.433,-1.4332 z m 1.433,1.4332 c 0,0 0,0 0,0 1.4332,0 1.4332,0 1.4332,0 0,0 0,0 0,0 m 0,0 c 1.4332,0 1.4332,0 1.4332,1.4332 0,-1.4332 0,-1.4332 -1.4332,-1.4332 z m 1.4332,1.4332 c 1.4331,0 1.4331,0 1.4331,0 m 0,0 c 1.4332,0 1.4332,0 1.4332,0 0,0 0,0 -1.4332,0 z m 12.8986,2.8663 c 0,0 0,0 0,0 0,0 0,0 0,0 z m 0,0 c 0,0 0,0 1.4331,0 0,0 -1.4331,0 -1.4331,0 z m 477.2441,-230.7396 c 5.7327,2.8662 11.4653,5.7325 15.7648,11.4652 1.4332,1.4332 1.4332,7.1658 -1.4331,5.7327 -10.0322,-7.1659 -12.8984,-12.8984 -14.3317,-17.1979 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="cz"
+ class="eu europe"
+ d="m 7050.4209,5566.0857 c 21.4974,-84.5568 68.7918,-12.8985 111.7869,-67.3588 2.8663,-4.2995 -1.4332,-11.4653 1.4331,-15.7648 4.2995,-5.7327 14.3317,-2.8664 18.6311,-8.5991 0,0 11.4655,-44.4281 11.4655,-45.8612 12.8984,-22.9307 47.2944,-41.5618 71.6582,-53.0271 -5.7327,-7.1659 -1.4332,-20.0644 -7.1659,-27.2302 -5.7327,-5.7327 -17.1979,-1.4332 -22.9306,-5.7327 -1.4331,-1.4332 -14.3317,-35.8291 -15.7648,-37.2623 -7.1659,-5.7327 -20.0644,-1.4331 -28.6634,-4.2995 -7.1657,-1.4332 -22.9306,-5.7327 -28.6632,-10.0322 -2.8664,-1.4331 -2.8664,-7.1658 -5.7327,-5.7326 -24.3639,10.0321 22.9307,12.8985 -18.6312,17.198 -2.8663,0 -7.1657,-1.4332 -10.0322,-4.2995 -4.2995,-4.2995 -2.8663,-11.4654 -7.1657,-15.7649 -5.7327,-4.2995 -25.797,-5.7325 -20.0644,-10.032 18.6311,-17.1981 30.0966,4.2995 12.8984,-30.0966 0,-1.4331 -2.8662,-1.4331 -4.2994,0 -1.4331,2.8664 0,8.599 -2.8663,10.0322 -5.7327,2.8663 -12.8985,0 -18.6312,0 -5.7326,0 -12.8985,2.8663 -18.6312,1.4332 -12.8985,-4.2995 -37.2622,-25.7971 -58.7596,-22.9307 -8.5992,1.4332 22.9305,32.9627 21.4974,35.8292 -4.2995,8.5989 -18.6312,5.7327 -25.7971,11.4652 -4.2995,4.2995 -2.8663,14.3317 -8.599,17.198 -21.4974,8.5991 -30.0964,-28.6632 -34.3959,-34.3959 0,0 -32.9629,-20.0643 -32.9629,-20.0643 -2.8662,-18.6312 51.5941,-25.7971 12.8987,-44.4281 -7.166,-4.2996 -38.6956,12.8985 -38.6956,12.8985 -4.2995,-4.2995 0,-11.4654 -4.2995,-15.7649 -1.4332,-2.8663 -5.7327,0 -10.0322,1.4332 -1.4331,0 -2.8663,4.2995 -5.7326,2.8663 -2.8664,-4.2995 -1.4332,-11.4653 -7.1659,-15.7648 -18.6311,-11.4654 -37.2622,1.4331 -60.1928,-17.198 -15.7649,-11.4654 5.7325,-22.9306 -20.0644,-25.7971 -40.1287,-7.1657 5.7327,24.3639 -32.9628,30.0966 -2.8664,5.7326 -4.2994,10.0321 -8.5991,10.0321 -18.631,4.2995 -48.7276,-74.5246 -64.4923,-30.0965 -2.8663,7.1658 22.9306,12.8985 17.1979,17.198 -17.1979,14.3317 -41.5618,15.7649 -58.7598,25.797 -2.8663,1.4332 0,8.5991 -2.8663,10.0321 -11.4654,5.7327 -27.2301,-2.8662 -35.8291,4.2995 -7.1659,5.7327 -4.2995,17.198 -11.4654,22.9307 -2.8663,2.8664 -5.7326,-7.1658 -10.0321,-5.7327 -12.8986,2.8664 -14.3317,20.0644 -22.9306,25.7971 -4.2995,4.2995 -12.8985,-2.8664 -17.198,1.4331 -4.2995,2.8664 0,11.4654 -4.2995,15.7649 -5.7327,7.1657 -21.4976,-5.7327 -25.7971,-5.7327 -35.8291,5.7327 -41.5617,32.9628 -53.0271,55.8935 -2.8663,5.7325 -2.8663,-14.3317 -8.599,-18.6312 -4.2995,-5.7327 -12.8985,-7.1659 -20.0642,-8.599 -12.8986,-2.8664 14.3315,42.9949 42.9949,61.6261 2.8663,1.4332 12.8985,2.8663 14.3317,11.4653 4.2995,20.0644 -14.3317,12.8986 -17.198,28.6634 -1.4332,4.2994 30.0964,57.3265 34.3959,61.626 24.3638,24.3639 30.0965,7.166 45.8614,22.9307 10.032,10.0322 11.4652,25.797 21.4974,34.3959 20.0643,14.3317 70.2251,32.9629 80.2573,61.6263 h 1.4332 c 22.9305,11.4652 40.1286,41.5617 70.2251,30.0964 5.7327,-1.4332 4.2995,-12.8985 10.0322,-12.8985 10.032,0 20.0642,12.8985 30.0964,10.0322 0,0 11.4653,-37.2623 11.4653,-37.2623 5.7327,-4.2995 15.7649,5.7327 18.6312,1.4332 2.8664,-2.8664 0,-44.4281 1.4332,-47.2946 4.2995,-4.2994 12.8984,-1.4332 18.6311,1.4333 2.8663,1.4332 0,8.5989 2.8663,10.0321 7.1659,0 12.8985,-8.5989 20.0644,-8.5989 41.5617,0 74.5246,31.5295 113.22,35.8291 5.7327,0 12.8985,2.8663 18.6312,1.4331 8.599,-4.2995 11.4654,-15.7648 18.6312,-18.6312 10.0321,-2.8663 24.3638,15.7649 31.5296,17.1981 4.2995,0 10.0322,-8.5991 14.3317,-5.7327 7.1658,5.7327 10.0322,11.4653 10.0322,17.198 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="sk"
+ class="eu europe"
+ d="m 7726.8751,5389.8062 c 1.4331,14.3317 -8.5991,34.3961 -10.0322,48.7277 -1.4332,10.0321 0,20.0643 -4.2995,28.6633 -4.2995,10.0322 -17.198,12.8985 -18.6312,22.9307 -2.8662,10.0322 4.2995,20.0642 4.2995,31.5296 0,11.4653 -21.4974,11.4653 -32.9628,14.3317 -5.7326,1.4331 -11.4653,8.599 -15.7648,5.7326 -27.2302,-8.599 -5.7327,-35.8292 -41.5618,-37.2622 -14.3317,0 -22.9307,20.0642 -37.2624,21.4974 -24.3637,4.2995 -55.8934,-21.4974 -77.3908,5.7327 -8.599,11.4653 -2.8665,50.1607 -18.6312,57.3266 -5.7327,1.4332 -14.3317,-4.2995 -20.0644,-1.4332 -22.9307,14.3317 10.0322,24.3639 -25.7969,30.0965 -11.4653,1.4332 -37.2624,-30.0965 -48.7277,-10.0321 -4.2995,7.1658 2.8663,20.0643 -2.8662,25.797 -15.7649,15.7647 -60.1931,5.7327 -80.2573,20.0642 -18.6312,11.4654 1.4331,31.5297 7.1658,40.1286 5.7327,10.0322 -116.0865,31.5297 -133.2844,28.6634 -27.2302,-5.7327 -47.2946,-42.9949 -73.0915,-45.8613 -5.7327,-1.4331 -10.0322,0 -12.8985,1.4332 -17.198,-18.6312 -31.5296,-37.2624 -44.4281,-58.7598 -8.599,-14.3317 12.8985,-34.396 11.4653,-53.0271 20.0643,-84.5568 67.3587,-14.3317 110.3538,-67.3588 2.8663,-4.2995 -1.4332,-11.4653 1.4331,-15.7648 4.2995,-5.7327 14.3317,-2.8664 18.6311,-8.5991 0,0 11.4655,-44.4281 11.4655,-45.8612 12.8984,-22.9307 47.2944,-41.5618 71.6582,-53.0271 2.8663,4.2995 12.8985,-2.8664 17.198,1.4331 4.2995,4.2995 -2.8664,14.3317 1.4331,18.6311 22.9306,18.6312 41.5618,-54.4603 55.8935,-44.4281 8.599,5.7327 8.599,18.6312 15.7648,25.797 1.4332,1.4332 7.1659,-2.8663 8.5989,-1.4331 1.4332,2.8663 -4.2995,8.599 -1.433,10.032 7.1657,1.4333 14.3317,-5.7325 20.0642,-2.8662 10.0322,4.2995 1.4332,31.5296 1.4332,34.3959 0,7.1659 12.8985,-4.2995 20.0644,-5.7326 5.7326,-1.4332 11.4653,10.0321 17.198,7.1658 1.433,-1.4332 18.6312,-45.8613 38.6954,-50.1608 5.7327,-1.4332 11.4653,4.2995 17.198,4.2995 5.7327,-1.4332 8.5989,-10.0322 14.3317,-8.599 11.4652,1.4332 18.6311,17.198 30.0964,17.198 8.599,0 15.7649,-8.599 20.0644,-15.7648 1.433,-2.8664 -7.1659,-2.8664 -7.1659,-5.7327 4.2995,-20.0642 12.8986,0 18.6311,-2.8663 4.2995,-1.4332 4.2995,-10.0322 8.599,-10.0322 21.4976,-5.7327 70.2252,-4.2995 88.8563,10.0322 4.2996,4.2995 2.8664,14.3316 8.5991,17.198 8.599,5.7327 18.631,5.7327 28.6632,8.599 12.8985,4.2995 25.797,5.7327 38.6956,7.1657 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="si"
+ class="eu europe"
+ d="m 7041.8218,5974.5379 c -10.0321,-1.4332 -34.396,-7.1659 -40.1287,-5.7327 -15.7647,2.8663 5.7327,22.9307 -2.8663,28.6634 -7.1657,2.8663 -15.7647,-1.4332 -22.9306,1.4331 -2.8663,1.4332 1.4332,7.1659 -1.4331,10.0321 -14.3317,11.4655 -37.2623,12.8985 -51.594,25.797 -17.198,15.7649 31.5296,55.8935 10.0322,71.6583 -50.1609,35.8291 -18.6312,-24.3638 -51.5941,22.9307 -4.2995,5.7327 12.8985,7.1659 15.7649,14.3316 2.8663,5.7327 -14.3317,4.2995 -14.3317,10.0322 0,2.8663 14.3317,21.4975 8.599,28.6633 -15.7647,15.7649 -38.6954,-8.599 -53.0271,-8.599 -21.4974,0 20.0643,20.0644 -15.7649,10.0322 -10.032,-2.8663 -22.9305,-18.6312 -27.23,-27.2302 -2.8664,-2.8663 -5.7327,-11.4653 -7.1659,-8.599 -5.7327,8.599 -4.2995,18.6312 -10.0322,25.797 -18.631,24.3639 -44.4281,0 -65.9256,7.1659 -2.8663,1.4331 2.8663,7.1658 0,10.032 -12.8985,7.166 -38.6954,5.7327 -51.5939,0 0,0 0,-1.433 1.4331,-1.433 7.1659,-7.1659 15.7649,-14.3317 18.6312,-21.4975 50.1608,2.8663 -7.1658,-31.5296 -21.4975,-40.1286 -10.0322,-5.7327 5.7327,-24.3639 2.8663,-31.5297 -2.8663,-7.1659 -25.797,12.8985 -20.0643,-5.7327 4.2995,-12.8984 31.5297,-24.3639 21.4975,-32.9628 -7.1658,-5.7327 -40.1286,-2.8663 -37.2624,-20.0643 16.0545,-33.9389 40.963,-45.4022 65.9258,-41.5618 11.4652,1.4332 22.9305,10.0322 34.3959,11.4652 11.4653,1.4332 22.9307,-7.1657 34.3959,-5.7325 7.1659,1.433 11.4654,11.4652 18.6312,11.4652 2.8663,1.4333 1.4332,-7.1657 4.2995,-8.5989 7.1658,-7.1658 18.6312,-10.0322 25.7969,-18.6312 5.7327,-4.2995 1.4333,-14.3317 5.7327,-18.6312 27.2302,-21.4975 55.8936,5.7327 81.6905,-5.7326 8.599,-4.2994 10.0322,-17.1981 18.6312,-20.0643 1.4331,-1.4333 48.7276,5.7327 50.1607,4.2994 4.2995,-1.4332 -4.2995,-7.1657 -5.7326,-11.4652 -4.2995,-22.9307 12.8985,-28.6634 34.396,-28.6634 5.7327,0 17.1979,44.4281 47.2944,68.792 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccccscsccccccccccsccccscccccccccsc" /><path
+ id="hu"
+ class="eu europe"
+ d="m 6994.5274,5905.7459 c 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 5.7327,0 17.1979,44.4281 47.2944,68.792 38.6955,31.5297 71.6582,48.7276 101.7548,87.4231 4.2995,5.7327 32.9627,1.4332 35.8292,2.8664 11.4652,4.2995 20.0642,14.3315 30.0964,18.631 20.0644,8.5991 83.1236,7.1659 101.7547,-8.599 2.8665,-2.8662 4.2995,-22.9306 14.3317,-22.9306 27.2302,-4.2995 22.9307,1.4332 47.2946,-25.797 4.2995,-4.2995 10.032,10.0322 22.9305,0 12.8986,-10.0322 21.4976,-27.2301 37.2623,-35.8291 32.9629,-21.4975 5.7327,0 30.0965,1.4332 1.4332,0 38.6955,-11.4654 40.1286,-11.4654 7.1659,0 14.3317,7.1659 21.4976,7.1659 2.8663,-1.4332 4.2995,-7.1659 7.1658,-8.599 8.599,-2.8664 51.594,-7.1659 54.4603,-17.198 2.8663,-5.7327 -18.6312,-12.8984 7.1658,-17.1981 10.0322,-1.433 20.0644,8.5992 28.6633,5.7327 27.2302,-10.0322 18.6312,-61.6261 25.797,-77.3909 2.8663,-4.2995 10.0322,-4.2995 12.8985,-8.599 0,0 15.7649,-50.1607 15.7649,-50.1607 2.8662,-7.1659 7.1657,-12.8986 7.1657,-20.0644 0,-2.8663 -7.1657,-2.8663 -7.1657,-5.7327 1.433,-12.8985 17.1979,-53.0271 24.3637,-64.4924 2.8664,-2.8664 10.0322,0 10.0322,-4.2995 7.1658,-27.2301 -2.8663,-47.2945 28.6634,-77.391 10.032,-11.4652 21.4974,-4.2994 37.2622,-10.0321 15.7649,-7.1658 17.1979,-28.6633 28.6633,-40.1287 -17.1979,-14.3317 5.7328,-11.4652 -12.8984,-24.3637 -2.8664,-1.4332 -7.1659,-4.2995 -11.4654,-4.2995 -5.7326,1.4331 -10.0321,8.599 -15.7648,7.1658 -8.599,-5.7327 -10.0322,-20.0644 -18.6312,-25.797 -4.2995,-2.8664 -10.0322,4.2995 -14.3316,2.8663 -22.9307,-8.599 -1.4331,-24.3637 -34.396,-25.797 0,11.4653 -21.4974,11.4653 -32.9628,14.3317 -5.7326,1.4331 -11.4653,8.599 -15.7648,5.7326 -27.2302,-8.599 -5.7327,-35.8292 -41.5618,-37.2622 -14.3317,0 -22.9307,20.0642 -37.2624,21.4974 -24.3637,4.2995 -55.8934,-21.4974 -77.3908,5.7327 -8.599,11.4653 -2.8665,50.1607 -18.6312,57.3266 -5.7327,1.4332 -14.3317,-4.2995 -20.0644,-1.4332 -22.9307,14.3317 10.0322,24.3639 -25.7969,30.0965 -11.4653,1.4332 -37.2624,-30.0965 -48.7277,-10.0321 -4.2995,7.1658 2.8663,20.0643 -2.8662,25.797 -15.7649,15.7647 -60.1931,5.7327 -80.2573,20.0642 -18.6312,11.4654 1.4331,31.5297 7.1658,40.1286 5.7327,10.0322 -116.0865,31.5297 -133.2844,28.6634 -27.2302,-5.7327 -47.2946,-42.9949 -73.0915,-45.8613 -5.7327,-1.4331 -10.0322,0 -12.8985,1.4332 0,0 0,0 0,0 -20.0644,8.599 0,54.4603 -1.4332,60.193 -8.599,15.7648 -60.1929,-7.1659 -80.2573,10.0321 -8.5989,7.1659 34.396,4.2995 35.8292,28.6633 2.8663,28.6634 -21.4975,7.1658 -31.5297,27.2302 -7.1657,14.3317 18.6312,44.4281 15.7649,50.1608 -12.8984,21.4975 -24.3639,25.797 -50.1608,54.4602 v 0 c 0,0 0,0 -1.4332,1.4332 2.8664,-1.4332 4.2995,-2.8663 7.1659,-2.8663 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ id="ro"
+ class="eu europe"
+ d="m 8695.695,6202.4112 c -22.9307,4.2995 -60.1929,17.198 -81.6905,-1.4332 -7.1658,-5.7327 -8.599,-25.7969 -17.1979,-21.4974 -4.2995,1.4332 -7.1658,7.1657 -11.4653,8.599 -34.3959,18.6311 11.4653,-10.0322 -25.797,-8.599 -11.4654,0 -20.0643,12.8985 -31.5296,14.3317 -24.3639,2.8662 -4.2995,-31.5297 -68.792,-7.166 -27.2302,11.4655 -77.391,34.3961 -98.8885,55.8936 -24.3637,24.3637 -28.6633,54.4603 -60.1928,74.5245 -28.6634,17.198 -50.1609,5.7327 -78.8242,4.2995 -22.9307,-1.433 -47.2944,-2.8662 -68.792,4.2995 -8.599,2.8664 -11.4653,14.3317 -20.0643,17.1981 -40.1286,14.3316 -87.4232,-8.5991 -127.5518,-4.2996 -31.5296,2.8664 -41.5617,28.6634 -75.9578,14.3317 -24.3637,-10.0321 30.0966,-41.5617 17.1981,-47.2944 -22.9306,-10.0322 -91.7226,-24.3639 -87.4231,-61.6261 1.4332,-12.8986 48.7276,-14.3317 34.3959,-21.4976 -151.9156,-75.9578 -15.7648,-7.1658 -77.3909,28.6634 -10.0322,7.1657 -15.7649,-18.6312 -25.797,-22.9307 -7.1658,-4.2995 -17.198,0 -24.3638,0 -8.599,0 -17.198,4.2995 -25.7969,1.4332 -4.2995,-2.8664 -1.4332,-10.0322 -4.2995,-15.7649 -7.1659,-14.3315 -68.792,8.599 -34.3961,-15.7647 8.599,-5.7327 30.0966,-11.4655 22.9307,-17.198 -7.1658,-4.2995 -21.4975,0 -24.3638,-8.599 -1.4332,-8.599 28.6633,-24.3639 8.599,-35.8291 -27.2301,-15.7649 -81.6905,-8.599 -100.3216,-41.5619 -4.2995,-7.1659 -4.2995,-41.5618 -4.2995,-50.1608 v -2.8663 c -4.2995,1.4331 -7.1658,10.0321 -10.0322,7.1658 -10.0321,-5.7327 -11.4653,-21.4975 -20.0643,-28.6632 -8.599,-5.7327 -21.4974,-4.2997 -30.0966,-11.4655 -8.5989,-4.2994 -12.8984,-14.3316 -18.631,-21.4974 2.8663,-1.4332 4.2995,-7.1659 7.1658,-8.599 8.599,-2.8664 51.594,-7.1659 54.4603,-17.198 2.8663,-5.7327 -18.6312,-12.8984 7.1658,-17.1981 10.0322,-1.433 20.0644,8.5992 28.6633,5.7327 27.2302,-10.0322 18.6312,-61.6261 25.797,-77.3909 2.8663,-4.2995 10.0322,-4.2995 12.8985,-8.599 l 15.7649,-50.1607 c 2.8662,-7.1659 7.1657,-12.8986 7.1657,-20.0644 0,-2.8663 -7.1657,-2.8663 -7.1657,-5.7327 1.433,-12.8985 17.1979,-53.0271 24.3637,-64.4924 2.8664,-2.8664 10.0322,0 10.0322,-4.2995 7.1658,-27.2301 -2.8663,-47.2945 28.6634,-77.391 10.032,-11.4652 21.4974,-4.2994 37.2622,-10.0321 15.7649,-7.1658 17.1979,-28.6633 28.6633,-40.1287 4.2995,2.8664 12.8985,-2.8663 22.9307,-30.0964 1.4331,-2.8663 1.4331,-7.1658 4.2995,-7.1658 4.2995,0 40.1286,17.198 44.4281,17.198 2.8663,0 4.2995,-7.1659 7.1658,-8.599 18.6312,-7.1659 65.9257,11.4653 88.8563,5.7326 12.8986,-4.2995 21.4976,-15.7648 34.396,-17.198 22.9307,-1.4332 38.6954,28.6634 54.4603,32.9628 21.4975,7.1658 28.6633,-32.9628 34.396,-38.6955 25.7969,-27.23 91.7225,-24.3638 111.7869,-44.4281 38.6954,-37.2623 -17.198,-5.7326 10.0322,-41.5617 7.1658,-8.599 18.6312,-15.7649 30.0964,-18.6312 l 1.4332,-4.2995 5.7326,-12.8985 v 0 c -1.4331,2.8663 -1.4331,8.599 -1.4331,11.4653 20.0643,-10.0322 32.9628,-1.4332 48.7276,11.4654 30.0965,21.4974 35.8292,57.3266 60.1929,81.6903 40.1287,38.6956 149.0493,107.4874 161.9478,163.381 10.0322,35.8291 -2.8663,75.9577 5.7327,111.7869 2.8663,15.7647 28.6633,51.5939 27.2301,67.3588 0,5.7327 -11.4654,7.1658 -10.0322,12.8985 1.4333,5.7327 10.0322,2.8662 14.3317,5.7327 4.2995,2.8662 4.2995,12.8984 10.0322,12.8984 2.8663,1.4331 7.1658,-1.4332 10.0321,1.4331 4.2996,4.2995 2.8664,12.8986 8.5991,17.1981 5.7326,5.7326 67.3588,8.599 65.9256,1.4331 0,-1.4331 -20.0644,-5.7326 -5.7327,-14.3317 8.599,-4.2995 18.6312,-2.8663 27.2301,-7.1658 14.3317,-7.1657 20.0644,-27.2301 35.8292,-32.9628 31.5296,-11.4653 47.2944,0 54.4603,27.2301 -1.4332,1.4332 -5.7327,0 -5.7327,1.4332 0,4.2995 2.8664,7.1658 5.7327,8.599 2.8663,1.4332 5.7327,-4.2995 7.1658,-1.4332 2.8664,5.7327 4.2995,54.4603 4.2995,60.193 -1.4331,18.6312 -44.4282,20.0644 -55.8934,28.6634 -7.1659,5.7326 -7.1659,25.7969 -15.7649,24.3637 -1.4331,-1.4332 10.0322,-48.7276 -10.0321,-60.1928 -7.1659,-2.8665 -15.7649,8.5989 -20.0644,15.7647 -5.7327,10.0322 20.0644,18.6312 18.6312,30.0964 0,11.4655 -21.4974,18.6312 -15.7649,27.2302 5.7327,8.599 16.9165,-12.2282 26.9486,-12.2282 2.8664,-1.4332 -1.1515,6.4956 -4.0179,9.3619 -3.39,1.8409 -0.8114,2.4342 -1.4331,4.2995 8.599,7.1658 -25.7971,63.0593 -25.7971,64.4925 1.4332,10.0321 14.3317,60.1929 21.4976,106.0542 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccsccscccccsccscscccccsccccccccccccscccccccssccccccccccsccccccsscscccsscsssccccccscccccc" /><path
+ id="bg"
+ class="eu europe"
+ d="m 8477.8539,6648.1257 c -7.1659,-1.4331 -14.3317,-1.4331 -18.6312,-1.4331 -55.8935,11.4653 34.3959,51.5939 2.8663,75.9578 0,0 -44.4282,14.3317 -45.8612,14.3317 -20.0644,7.1658 -53.0273,32.9627 -74.5247,31.5295 -4.2995,0 -1.4331,-7.1658 -4.2995,-8.599 -14.3317,-7.1657 -32.9627,-15.7647 -48.7276,-11.4652 -5.7327,1.4332 -5.7327,12.8984 -11.4653,14.3316 -4.2995,1.4331 -7.1659,-8.5989 -11.4654,-10.0321 -2.8663,-1.4333 -7.1658,4.2994 -8.599,1.4332 -10.0322,-5.7327 -12.8985,-21.4976 -24.3637,-22.9307 -8.5992,-2.8664 -12.8986,11.4653 -21.4976,12.8985 -5.7326,2.8662 -12.8985,-8.599 -17.198,-5.7327 -25.7969,15.7649 -35.8291,32.9628 -75.9578,47.2945 -17.1979,5.7326 -38.6954,0 -54.4603,10.0321 -5.7325,2.8664 -5.7325,14.3317 -11.4652,17.1981 -4.2995,4.2995 -11.4655,0 -17.198,0 -34.5518,-2.9572 -17.5714,-48.5095 -20.0644,-78.8242 -10.0322,-10.0322 -25.7969,-38.6954 -38.6954,-47.2944 -7.1659,-4.2995 -17.198,0 -25.797,-4.2995 -2.8664,-1.4332 -41.5618,-31.5297 -41.5618,-34.3961 7.1658,-12.8985 27.2301,-15.7647 20.0642,-34.3959 -1.433,-5.7327 -5.7325,-8.599 -10.032,-11.4653 -2.8664,-1.4332 -7.1659,1.4331 -8.599,-1.4332 -1.4332,-7.1658 0,-14.3317 0,-21.4975 -1.4332,-8.5989 -11.4654,-17.1979 -7.1659,-24.3638 41.5618,-68.7919 -5.7327,15.7649 40.1286,-20.0643 4.2995,-4.2995 34.3961,-57.3267 25.797,-65.9257 -11.4653,-8.599 -65.9256,-34.3959 -68.7919,-37.2622 -17.198,-14.3317 -44.4281,-74.5247 -34.3961,-96.0222 4.2997,-10.0322 20.0644,-8.599 25.7971,-17.1979 4.2995,-10.0322 -1.4332,-22.9307 2.8663,-32.9629 18.6312,8.599 41.5618,14.3317 51.594,20.0644 12.8985,5.7327 -41.5618,37.2623 -17.1981,47.2944 34.3961,14.3317 44.4282,-11.4653 75.9578,-14.3317 40.1286,-4.2995 87.4232,18.6312 127.5518,4.2996 8.599,-2.8664 11.4653,-14.3317 20.0643,-17.1981 21.4976,-7.1657 45.8613,-5.7325 68.792,-4.2995 28.6633,1.4332 50.1608,12.8985 78.8242,-4.2995 31.5295,-20.0642 35.8291,-50.1608 60.1928,-74.5245 21.4975,-21.4975 71.6583,-44.4281 98.8885,-55.8936 64.4925,-24.3637 44.4281,10.0322 68.792,7.166 11.4653,-1.4332 20.0642,-14.3317 31.5296,-14.3317 37.2623,-1.4332 -8.5989,27.2301 25.797,8.599 4.2995,-1.4333 7.1658,-7.1658 11.4653,-8.599 8.5989,-4.2995 10.0321,15.7647 17.1979,21.4974 21.4976,18.6312 58.7598,5.7327 81.6905,1.4332 h 2.8664 c 5.7326,35.8292 8.599,68.7921 -1.4332,74.5246 -5.7327,2.8664 -12.8985,-7.1658 -20.0644,-5.7325 -53.0271,14.3315 -18.631,12.8984 -34.3959,44.4281 -1.4332,5.7325 -11.4653,5.7325 -14.3317,10.032 0,2.8664 8.599,1.4332 8.599,4.2995 2.8664,22.9307 1.4332,37.2624 7.1659,60.193 1.4331,8.599 10.0321,20.0643 5.7326,27.2302 -4.2995,7.1658 -17.198,2.8663 -24.3638,7.1658 -1.4332,0 4.2995,2.8664 4.2995,4.2995 -2.8663,5.7327 -10.0322,7.1659 -11.4654,12.8984 -1.4331,4.2995 7.1659,8.5992 4.2996,11.4654 -5.7327,8.599 -18.6311,11.4653 -24.3638,20.0643 -25.797,34.3961 37.2623,-17.198 53.0271,21.4976 1.4332,2.8663 -2.8663,7.1658 -2.8663,10.032 1.4331,1.4332 32.9627,20.0644 40.1286,25.7971 4.2995,5.7326 8.599,10.0321 10.0322,14.3316 h -2.8664 c -8.599,1.4332 -17.198,0 -25.7969,2.8664 -11.4653,4.2995 -20.0644,14.3317 -30.0965,21.4975 -2.8664,1.4332 0,7.1657 -1.4332,5.7327 -32.9629,-7.166 -42.9949,-32.9629 -75.9578,-15.7649 l -24.3639,27.2301 c -10.032,4.2995 -22.9305,-2.8663 -30.0964,4.2995 -8.599,7.1659 0,21.4975 -5.7327,30.0964 -8.599,10.0322 -37.2622,5.7327 -24.3637,27.2302 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cccccscccccccccccccsccccccccccccccccsccsccccccccccccccscccccccccccccccccc" /><path
+ id="cy"
+ class="eu europe"
+ d="m 9926.7843,7409.1367 c -1.4331,22.9306 -65.9256,78.8242 -77.3908,104.6211 -12.8987,31.5297 51.5939,35.8292 44.4281,47.2946 -4.2995,5.7325 -15.7649,-1.4332 -21.4976,1.433 -5.7326,1.4332 -7.1658,10.0322 -12.8985,12.8985 -8.599,5.7327 -22.9307,0 -30.0964,7.1659 -5.7327,7.1658 1.4332,20.0643 -2.8663,27.2302 -11.4654,20.0642 -61.6262,34.3959 -73.0915,50.1607 -4.2995,5.7327 4.2995,15.7649 0,22.9306 -11.4653,21.4976 -22.9307,-11.4652 -27.2302,-11.4652 -8.5989,0 -14.3316,11.4652 -22.9306,14.3317 -4.2995,1.433 -51.5939,-5.7327 -53.0271,-5.7327 -4.2995,-2.8663 -2.8663,-10.0322 -5.7327,-14.3317 -4.2995,-4.2995 -41.5617,-44.4282 -28.6633,-42.9949 7.1658,0 11.4653,7.1657 18.6312,7.1657 12.8985,-1.4332 15.7648,-27.2301 18.631,-31.5296 10.0322,-15.7648 37.2624,-4.2995 47.2946,-12.8985 7.1658,-4.2995 5.7327,-15.7647 4.2995,-24.3637 -1.4332,-7.1659 -14.3317,-15.7649 -8.599,-22.9307 4.2995,-5.7327 15.7648,2.8663 24.3637,1.4331 17.1981,-1.4331 58.7598,-11.4653 74.5247,-20.0643 20.0643,-11.4654 130.4181,-111.7869 131.8512,-110.3538 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ sodipodi:nodetypes="cssssc"
+ style="display:inline;fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 6829.9519,7968.4904 c -10.2335,2.0464 -18.1436,18.6209 -18.1436,18.6211 18.1928,-3e-4 25.4206,-7.6225 18.1436,21.4858 -7.2772,29.1084 87.3099,11.1464 76.3942,-14.3239 -10.9158,-25.4703 -51.3355,-7.4093 -65.89,-21.9633 -3.6385,-3.6388 -7.093,-4.5019 -10.5042,-3.8197 z"
+ id="mt"
+ class="eu europe" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 7367.1505,6701.153 -2.8665,-10.0322 c -17.1979,-28.6634 -44.4281,-18.6312 -75.9577,-42.9951 -8.599,-7.1658 -11.4653,-20.0642 -21.4975,-22.9307 -5.7326,-2.8662 -12.8985,2.8665 -20.0642,4.2997 2.8662,1.433 4.2995,1.433 7.1657,2.8663 2.8663,1.4332 5.7327,10.0321 2.8663,10.0321 -22.9305,-1.4332 -40.1286,-22.9306 -63.0591,-25.797 -10.0322,-1.4331 -21.4976,1.4332 -31.5297,0 -2.8664,0 -5.7327,-7.1658 -7.1659,-5.7326 -2.8663,1.4331 -2.8663,8.599 0,10.032 8.599,2.8665 18.6312,-1.433 28.6634,0 8.5989,1.4333 18.6311,4.2995 28.6632,10.0322 2.8664,1.4333 1.4332,7.166 4.2995,8.599 48.7277,25.7971 38.6955,-15.7647 114.6533,44.4281 10.0322,7.166 22.9307,14.3317 35.8292,17.1982 z m 24.3637,-460.0463 c -12.8985,-14.3317 8.599,-7.1658 14.3317,-17.198 1.4332,-1.4332 1.4332,-4.2995 0,-5.7327 -18.6312,-18.631 -5.7327,-25.7969 -4.2995,-44.4281 1.4332,-7.1658 37.2623,21.4974 32.9629,-7.1658 -20.0644,0 -42.9951,-5.7327 -60.1931,-17.198 -7.1657,-5.7327 5.7327,-8.5989 5.7327,-10.0322 0,-4.2994 -14.3317,-7.1657 -5.7327,-15.7649 1.4332,-1.433 11.4654,7.166 12.8985,-1.433 1.4332,-15.7649 -28.6632,2.8663 -28.6632,-11.4654 2.8663,-32.9627 8.599,-24.3638 -15.7649,-58.7598 l -1.4331,-2.8663 c -4.2995,1.4332 -8.599,1.4332 -15.7649,2.8663 -10.0322,0 -11.4652,20.0644 -14.3317,22.9306 -18.6311,15.7649 -81.6903,17.1981 -101.7547,8.599 -10.0322,-4.2995 -18.6312,-14.3315 -30.0964,-18.631 -2.8665,-1.4332 -31.5297,2.8663 -35.8292,-2.8664 -30.0966,-38.6955 -63.0593,-55.8934 -101.7548,-87.4231 -10.0321,-1.4332 -34.396,-7.1659 -40.1287,-5.7327 -15.7647,2.8663 5.7327,22.9307 -2.8663,28.6634 -7.1657,2.8663 -15.7647,-1.4332 -22.9306,1.4331 -2.8663,1.4332 1.4332,7.1659 -1.4331,10.0321 -14.3317,11.4655 -37.2623,12.8985 -51.594,25.797 -17.198,15.7649 31.5296,55.8935 10.0322,71.6583 -50.1609,35.8291 -18.6312,-24.3638 -51.5941,22.9307 -4.2995,5.7327 12.8985,7.1659 15.7649,14.3316 2.8663,5.7327 -14.3317,4.2995 -14.3317,10.0322 0,2.8663 14.3317,21.4975 8.599,28.6633 -15.7647,15.7649 -38.6954,-8.599 -53.0271,-8.599 -21.4974,0 20.0643,20.0644 -15.7649,10.0322 -10.032,-2.8663 -22.9305,-18.6312 -27.23,-27.2302 -2.8664,-2.8663 -5.7327,-11.4653 -7.1659,-8.599 -5.7327,8.599 -4.2995,18.6312 -10.0322,25.797 -18.631,24.3639 -44.4281,0 -65.9256,7.1659 -2.8663,1.4331 2.8663,7.1658 0,10.032 -12.8985,7.166 -38.6954,5.7327 -51.5939,0 -4.2995,0 -12.8986,-4.2995 -12.8986,0 1.4332,11.4654 11.4654,20.0644 15.7649,30.0966 0,1.4331 -2.8663,1.4331 -2.8663,2.8663 1.4331,11.4654 -1.4332,22.9307 4.2995,31.5297 0,0 1.4331,0 1.4331,1.4332 -5.7326,8.5989 17.1979,18.6311 24.3639,28.6632 5.7327,8.599 10.0321,45.8613 17.1979,37.2623 7.1658,-8.5989 11.4653,-20.0643 17.198,-30.0964 1.4332,-4.2995 2.8664,-8.599 2.8664,-12.8985 0,-1.4332 -4.2995,0 -4.2995,-2.8664 0,-1.4331 0,-7.1658 1.4331,-5.7325 5.7327,1.433 1.4332,34.3959 17.1979,8.5989 2.8665,-7.1659 2.8665,-14.3317 2.8665,-22.9306 0,-1.4332 -2.8665,-2.8665 -2.8665,-5.7327 0,-1.4332 1.4333,4.2995 2.8665,2.8662 11.4652,-5.7325 1.433,-44.4281 14.3317,-44.4281 41.5617,-2.8663 27.23,12.8985 53.0271,34.3961 5.7325,4.2995 14.3315,0 18.6311,4.2995 25.797,30.0964 -4.2996,67.3588 24.3638,98.8884 8.599,10.0321 32.9628,31.5297 42.9949,38.6954 11.4654,5.7328 47.2945,11.4653 35.8291,15.7648 -20.0642,5.7327 -57.3266,-35.8291 -51.5939,18.6312 1.4332,17.1979 67.3588,54.4603 77.391,67.3588 2.8663,1.4332 -11.4654,-4.2995 -8.599,-2.8663 8.599,5.7327 45.8612,17.198 47.2944,21.4974 2.8663,10.0322 -5.7327,25.797 1.4333,34.396 4.2994,2.8664 48.7276,-12.8985 50.1608,-14.3316 28.6632,-5.7327 8.599,5.7326 44.4281,18.6311 11.4653,2.8664 24.3637,-1.4331 35.8292,2.8664 15.7647,5.7326 25.7969,31.5296 42.9949,40.1286 20.0644,8.599 32.9628,14.3317 38.6954,20.0643 0,1.4332 1.4332,0 1.4332,0 2.8663,0 5.7327,-2.8663 7.1658,0 5.7327,10.0322 -2.8663,10.0322 10.0322,15.7649 4.2995,-4.2995 14.3316,-4.2995 15.7649,-10.0322 7.1657,-18.6312 -54.4604,-42.9949 -60.1931,-53.0271 -5.7327,-8.599 -1.4332,-21.4975 -7.1657,-30.0965 -4.2995,-5.7327 -14.3317,-4.2995 -21.4975,-7.1659 -30.0966,-18.6312 -48.7277,-53.0271 -74.5247,-74.5246 -8.599,-5.7327 -41.5618,-38.6955 -51.5939,-42.995 -4.2995,-1.4331 -10.0322,4.2995 -14.3317,1.4332 -7.1657,-7.1658 10.0322,-22.9307 10.0322,-25.7969 -1.4332,-5.7327 -8.599,-7.166 -12.8985,-11.4654 -11.4654,-17.198 -15.7649,-40.1287 -31.5296,-54.4602 -5.7327,-7.1659 -17.1981,-7.1659 -24.3639,-12.8986 -5.7327,-4.2995 -15.7647,-87.4231 15.7649,-85.9899 14.3316,0 38.6954,31.5297 51.5939,38.6955 l 11.4653,-1.4333 -11.4653,1.4333 c 17.198,-1.4333 14.3317,-28.6634 22.9307,-40.1287 15.7647,-18.6312 22.9306,4.2995 42.9949,0 5.7327,-1.4332 5.7327,-12.8985 11.4654,-17.1979 0,0 8.599,8.5989 32.9627,15.7647 2.8664,1.4332 7.1659,2.8664 11.4654,1.4332 2.8663,0 4.2995,-5.7327 8.599,-7.1658 17.198,-1.4332 55.8934,20.0643 74.5246,17.198 4.2995,0 8.5989,-2.8664 10.0322,-7.1659 4.2994,-2.8663 2.8662,-12.8985 8.5989,-12.8985 8.599,-1.433 14.3317,7.1659 22.9307,10.0322 15.7648,4.2995 -2.8663,-7.1658 5.7327,-10.0322 8.599,-2.8663 18.6312,-2.8663 27.2302,0 5.7325,1.4332 10.032,10.0322 15.7647,10.0322 4.2995,0 0,-10.0322 4.2995,-10.0322 32.9629,1.4332 24.3639,37.2624 40.1286,40.1287 10.0322,1.4332 18.6312,-1.4331 25.797,-4.2995 z"
+ class="europe"
+ id="hr" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 7430.2098,6546.371 c -14.3317,-10.0322 -14.3317,-20.0643 -30.0966,-18.6312 l 2.8664,-4.2995 -2.8664,4.2995 c -20.0643,22.9307 -17.198,55.8935 -22.9305,63.0593 -11.4655,14.3317 -15.7649,-7.1658 -20.0644,14.3317 -7.1658,41.5618 37.2623,42.9949 4.2995,74.5246 l 7.1659,-1.4331 1.433,22.9307 c -17.1979,-41.5619 -45.8613,-27.2302 -81.6904,-53.0273 -8.599,-7.1658 -11.4653,-20.0642 -21.4975,-22.9307 -7.1658,-2.8662 -15.7647,5.7327 -24.3637,5.7327 -4.2995,0 -14.3317,-1.433 -12.8985,-5.7327 2.8663,-5.7325 15.7647,-4.2995 17.198,-11.4652 7.1657,-18.6312 -54.4604,-42.9949 -60.1931,-53.0271 -5.7327,-8.599 -1.4332,-21.4975 -7.1657,-30.0965 -4.2995,-5.7327 -14.3317,-4.2995 -21.4975,-7.1659 -30.0966,-18.6312 -48.7277,-53.0271 -74.5247,-74.5246 -8.599,-5.7327 -41.5618,-38.6955 -51.5939,-42.995 -4.2995,-1.4331 -10.0322,4.2995 -14.3317,1.4332 -7.1657,-7.1658 10.0322,-22.9307 10.0322,-25.7969 -1.4332,-5.7327 -8.599,-7.166 -12.8985,-11.4654 -11.4654,-17.198 -15.7649,-40.1287 -31.5296,-54.4602 -5.7327,-7.1659 -17.1981,-7.1659 -24.3639,-12.8986 -5.7327,-4.2995 -15.7647,-87.4231 15.7649,-85.9899 14.3316,0 38.6954,31.5297 51.5939,38.6955 l 11.4653,-1.4333 -11.4653,1.4333 c 17.198,-1.4333 14.3317,-28.6634 22.9307,-40.1287 15.7647,-18.6312 22.9306,4.2995 42.9949,0 5.7327,-1.4332 5.7327,-12.8985 11.4654,-17.1979 0,0 8.599,8.5989 32.9627,15.7647 2.8664,1.4332 7.1659,2.8664 11.4654,1.4332 2.8663,0 4.2995,-5.7327 8.599,-7.1658 17.198,-1.4332 55.8934,20.0643 74.5246,17.198 4.2995,0 8.5989,-2.8664 10.0322,-7.1659 4.2994,-2.8663 2.8662,-12.8985 8.5989,-12.8985 8.599,-1.433 14.3317,7.1659 22.9307,10.0322 15.7648,4.2995 -2.8663,-7.1658 5.7327,-10.0322 8.599,-2.8663 18.6312,-2.8663 27.2302,0 5.7325,1.4332 10.032,10.0322 15.7647,10.0322 4.2995,0 0,-10.0322 4.2995,-10.0322 32.9629,1.4332 24.3639,37.2624 40.1286,40.1287 27.2302,2.8664 41.5619,-17.198 67.3588,-15.7648 0,44.4281 -25.7969,74.5246 -21.4974,101.7547 5.7327,31.5297 14.3317,8.599 34.3959,17.198 12.8985,5.7327 15.7649,24.3639 28.6634,31.5296 4.2995,1.4333 18.6312,-2.8662 14.3317,10.0322 -8.599,28.6634 -44.4282,-2.8662 -51.5941,7.1658 0,0 64.4925,60.193 47.2946,78.8242 -5.7327,4.2995 -12.8986,-10.0322 -20.0644,-7.1659 -8.599,2.8664 -10.0322,14.3317 -15.7648,21.4976 -10.0322,2.8663 -37.2623,-2.8664 -38.6955,4.2995 -4.2995,20.0642 31.5296,21.4974 20.0644,51.5939 h -5.7327 z"
+ class="europe"
+ id="ba" /><path
+ sodipodi:nodetypes="cccccccscscccccssssssccccccccccsccccc"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 7364.9738,6687.7722 2.8671,10.0257 c 7.1648,1.4322 12.8977,1.4322 20.0636,1.4322 -2.8659,2.8645 -5.7329,5.7289 -4.2988,10.0257 20.0636,44.3997 24.3636,2.8645 44.4273,12.8903 4.3,2.8645 1.4341,8.5935 4.3,12.8902 11.4659,14.3226 25.7966,11.4581 35.8295,27.2128 2.866,4.2967 0,12.8903 4.2989,17.187 8.5989,8.5936 20.0648,12.8902 31.5296,15.7549 0,-10.0259 0,-20.0516 0,-28.6452 0,-5.7291 2.867,-12.8903 0,-18.6193 -4.2989,-7.1611 -17.1978,-7.1611 -18.6307,-14.3223 0,-1.4323 45.8614,-94.5287 50.1614,-97.3932 1.4329,-1.4322 8.5988,34.3741 20.0636,35.8063 0,0 32.9682,-25.0655 32.9682,-25.0655 -3.0114,-15.0806 -0.01,-14.2809 -5.0926,-28.8088 -3.4107,-9.7457 8.0836,-4.1504 15.8967,-10.589 3.4356,-2.8306 1.7184,-7.223 -2.8023,-12.3987 -4.5968,-5.2613 29.5383,-5.5936 21.6127,-11.6552 -15.7205,-12.0245 -34.9289,-32.514 -50.2297,-39.624 -9.876,-4.5889 2.5088,-24.1417 -7.1227,-33.6155 -18.0348,-17.7407 -61.7911,-28.5418 -75.4553,-37.3262 -5.733,4.2967 -12.8989,-10.0258 -20.0648,-7.1613 -8.5989,2.8645 -10.0318,14.3225 -15.7648,21.4839 -10.0318,2.8644 -37.2625,-2.8646 -38.6955,4.2968 -4.2988,20.0513 31.5296,21.4836 20.0648,51.5609 h -5.7329 5.7329 c -14.3318,-10.0258 -14.3318,-20.0515 -30.0966,-18.6193 l 2.8659,-4.2968 -2.8659,4.2968 c -20.0648,22.9161 -17.1977,55.8578 -22.9307,63.0191 -11.4659,14.3225 -15.7648,-7.1613 -20.0648,14.3225 -7.1659,41.5353 37.2625,42.9675 4.3,74.477 l 7.1659,-1.4321 1.433,22.916 c -1.433,-4.2969 -2.8659,-7.1614 -5.733,-10.0257 z"
+ class="europe"
+ id="me" /><path
+ sodipodi:nodetypes="cccccccccccccccccccccccccsccscscccccscccccsccscccccsscccscccccccccsssscc"
+ id="rs"
+ class="europe"
+ d="m 7686.4663,6519.2109 c 23.8063,0 57.3844,75.0265 70.7747,82.4612 4.4642,2.9738 36.1762,20.6419 42.1279,22.1285 10.4161,2.9742 0.8486,18.3344 8.2882,25.769 19.3422,20.8174 -0.048,20.1553 -4.5124,40.9724 0,0 -0.6894,-10.1895 -0.6894,-10.1895 l -3.8636,7.7237 c 37.1977,25.2782 1.2506,-4.4095 10.1777,-10.3572 16.3669,-10.4087 -16.0594,3.392 3.2837,1.905 7.4397,0 -5.6552,-1.8498 -2.6786,-9.2845 1.4877,-5.9479 -8.861,18.6972 -7.3733,14.2362 13.3915,-32.7126 -18.3044,13.6139 9.965,9.1532 23.8064,-4.4609 87.7699,-44.8543 107.1133,-27.0108 7.4395,-13.3826 28.2694,-16.3564 20.8298,-35.6866 -1.4877,-5.9478 -5.9507,-8.9218 -10.4149,-11.8955 -2.9754,-1.4872 -7.4396,1.4869 -8.9273,-1.4872 -1.4876,-7.4346 0,-14.8694 0,-22.304 -1.4876,-8.9216 -11.9037,-17.8434 -7.4396,-25.2781 43.1486,-71.3733 -5.9518,16.3565 41.6609,-20.8173 4.4642,-4.4607 35.7102,-59.4778 26.7829,-68.3994 -11.9038,-8.9217 -68.4438,-35.6867 -71.4192,-38.6605 -17.8556,-14.8695 -46.1249,-77.3213 -35.7101,-99.6254 4.4642,-10.4087 20.8311,-8.9217 26.7818,-17.8433 4.4642,-10.4087 -1.4877,-23.7913 2.9764,-34.1998 -20.8308,-8.9217 -40.173,-22.3042 -37.1976,-43.1213 1.4875,-13.3827 50.5892,-14.8694 35.7101,-22.3043 -157.7173,-78.8081 -16.3668,-7.4347 -80.3476,29.739 -10.4149,7.4345 -16.3669,-19.3304 -26.7817,-23.7911 -7.4395,-4.4609 -17.8545,0 -25.2941,0 -8.9272,0 -17.8543,4.4607 -26.7817,1.487 -4.4641,-2.9741 -1.4888,-10.4088 -4.4641,-16.3566 -7.4396,-14.8692 -71.4192,8.9217 -35.7102,-16.3562 8.9284,-5.9478 31.2459,-11.8958 23.8064,-17.8434 -7.4384,-4.461 -22.3175,0 -25.2941,-8.9216 -1.4877,-8.9218 29.7583,-25.2782 8.9273,-37.1737 -28.2694,-16.3564 -84.8094,-8.9216 -104.1527,-43.1213 -4.4629,-7.4349 -4.4629,-43.1216 -4.4629,-52.0432 0,-1.4871 0,-2.974 0,-2.974 -4.4644,1.4869 -7.4396,10.4087 -10.4163,7.4347 -10.4149,-5.9478 -11.9026,-22.3041 -20.8298,-29.7387 -8.9272,-5.9479 -22.3186,-4.461 -31.246,-11.8957 -8.9271,-4.4608 -13.3913,-14.8693 -19.3432,-22.3041 -7.4395,0 -14.8792,-7.4349 -22.3186,-7.4349 -1.4877,0 -40.1733,11.8957 -41.661,11.8957 -25.294,-1.4869 2.9765,-23.7912 -31.2459,-1.4869 -16.3668,8.9215 -25.294,26.7649 -38.6854,37.1735 -13.3902,10.4086 -19.3422,-4.4609 -23.8063,0 -19.3422,22.3042 -20.83,23.7911 -32.7337,23.7911 l 1.4878,2.974 c 25.2951,35.6867 19.3431,26.7651 16.3667,60.9646 0,14.8696 31.246,-4.4608 29.7581,11.8958 -1.4875,8.9216 -11.9025,0 -13.3913,1.4867 -8.9273,8.9219 5.9519,11.8957 5.9519,16.3565 0,1.4871 -13.3903,4.4608 -5.9519,10.4086 17.8557,11.8955 41.6619,17.8434 62.4918,17.8434 4.4642,29.7388 -32.7336,0 -34.2212,7.4346 -1.4878,19.3305 -14.8793,26.7653 4.4643,46.0953 1.4875,1.487 1.4875,4.4609 0,5.9479 -5.9521,10.4085 -28.2707,2.9738 -14.8792,17.8434 14.8792,-4.4609 26.7816,-13.3825 43.1484,-11.8955 0,46.0951 -26.7817,77.321 -22.3186,105.5729 5.9518,32.7129 14.8791,8.9216 35.7101,17.8434 13.3915,5.9478 16.3669,25.278 29.7583,32.7128 4.4629,1.487 19.342,-2.9739 14.8792,10.4086 -8.9286,29.739 -46.1252,-2.9737 -53.5647,7.4346 0,0 66.9549,62.4518 49.1004,81.7821 34.9816,18.2199 17.4364,11.1863 71.9716,34.1526 18.5275,5.9612 1.2349,37.5971 17.6313,44.1048 25.9517,10.3007 17.4622,20.279 35.1904,29.6348 16.5342,8.7264 -17.2431,-14.8826 -6.899,-7.524 17.1235,12.1829 25.4817,-14.1028 22.4484,-6.1471 21.9102,3.6734 69.3821,-71.4706 67.4624,-63.2949 z"
+ style="display:inline;opacity:1;fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new" /><path
+ sodipodi:nodetypes="cscccsssscssssssssccssssssscc"
+ id="rs-km"
+ d="m 7673.2462,6767.2235 c 0,0 6.7207,-36.0036 -5.7605,-55.6855 -12.4812,-19.682 -49.4449,-24.9625 -49.4449,-24.9625 l -16.8017,-32.6432 -11.5211,-11.5212 c 0,0 -7.6808,-29.7629 -1.4402,-28.8028 3.1203,0.48 7.9963,-0.4574 10.7311,-4.1265 2.7347,-3.6691 4.4988,-10.0698 13.1004,-15.834 7.1812,-4.8123 24.1275,-13.0844 27.3667,-15.9534 8.1572,-7.2247 20.1065,-18.0991 20.1065,-18.0991 0,0 24.9298,-31.2167 27.431,-34.1702 6.0694,-7.1672 28.4299,28.5539 31.9115,30.2442 7.9103,3.8404 30.2146,48.4298 34.055,49.3899 3.8404,0.9601 43.1584,21.9904 46.0387,22.9505 2.8803,0.9601 6.7207,9.601 3.8404,12.4812 -2.8803,2.8803 13.9214,17.7618 12.4812,21.1221 -1.4401,3.3604 -0.9601,14.4015 -4.3204,16.8017 -3.3603,2.4002 4.7413,16.6833 0.9009,17.6434 -3.8403,0.9601 -14.8223,-7.5624 -14.8223,-7.5624 l -3.1828,-7.2598 c 0,0 -5.458,-6.1815 -13.6189,-0.901 -8.1608,5.2805 -17.2817,20.162 -14.8814,23.5223 2.4002,3.3604 8.1608,6.2406 6.2406,8.6409 -1.9202,2.4002 -8.1608,-9.1209 -20.6421,-10.081 -12.4812,-0.9601 -42.2441,12.9613 -44.6444,16.3216 -2.4002,3.3603 -6.2406,6.2406 -7.6807,8.1608 -1.4402,1.9202 -3.3604,21.6022 -3.3604,25.4425 0,3.8404 -4.3204,19.2019 -4.3204,19.2019 z"
+ style="fill:#fff7df;fill-opacity:1;fill-rule:evenodd;stroke:#aa805d;stroke-width:4.58365;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ class="europe" /><path
+ id="mk"
+ class="europe"
+ d="m 7669.5443,6768.5121 c -1.4331,10.0322 -2.8663,20.0644 -1.4331,28.6634 0,10.0322 8.599,18.6312 8.599,27.2301 -1.4332,4.2995 -12.8985,4.2995 -11.4654,8.599 1.4332,10.0322 15.7649,15.7649 17.1979,25.797 1.4332,4.2995 -7.1657,8.5989 -7.1657,12.8984 0,11.4654 37.2623,64.4926 42.9949,73.0915 8.5991,10.0322 14.3317,-5.7327 18.6311,-5.7327 5.7327,-1.433 12.8985,0 17.198,2.8665 2.8664,2.8662 -1.4331,10.0322 1.4332,10.0322 10.0322,1.433 106.0542,-28.6634 111.7869,-32.9629 15.7649,-17.198 4.2995,-40.1286 32.9629,-50.1608 10.0322,-4.2995 65.9256,2.8662 74.5246,-2.8663 4.2995,-2.8664 2.8664,-11.4654 7.1659,-14.3317 2.8663,-1.4332 7.1658,7.1658 10.032,4.2995 17.7081,-52.0544 30.3676,-46.6927 22.9307,-121.8191 -10.0322,-10.0322 -25.7969,-38.6954 -38.6954,-47.2944 -7.1659,-4.2995 -17.198,0 -25.797,-4.2995 -2.8664,-1.4332 -41.5618,-31.5297 -41.5618,-34.3961 -18.6312,-17.198 -64.4925,27.2302 -87.4232,31.5297 -27.2302,4.2994 -45.1333,-7.3084 -58.0318,24.2211 -1.4332,4.2997 10.7374,3.009 9.3042,8.7417 -2.8663,7.1658 -15.7648,-10.0322 -22.9307,-10.0322 -18.631,1.4332 -35.8291,14.3317 -51.5939,24.3639 -8.599,5.7326 11.4653,65.9256 -24.3639,41.5617 z"
+ style="display:inline;opacity:1;fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
+ sodipodi:nodetypes="cccccsccccccsscccscccccccc" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 7755.5383,6951.957 c 0,18.631 20.0644,24.3637 15.7649,41.5617 -14.3317,54.4603 -22.9307,7.1659 -32.9628,45.8613 0,0 -8.599,57.3266 -8.599,58.7598 -2.8663,4.2995 -47.2946,18.6312 -48.7276,28.6632 0,0 18.6311,28.6634 18.6311,28.6634 4.2995,12.8985 -15.7649,0 -15.7649,8.599 1.4332,37.2623 -14.3316,17.1979 -38.6954,21.4974 -1.4332,0 1.4331,1.4332 0,2.8665 -1.4332,1.433 -1.4332,1.433 -2.8664,2.8662 -1.4331,-1.4332 -1.4331,-2.8662 -2.8663,-5.7327 0,-2.8662 0,-5.7325 0,-8.5989 0,-8.599 4.2995,-15.7648 1.4332,-22.9307 -2.8664,-4.2995 -11.4654,0 -14.3317,-4.2995 -7.1657,-5.7326 -5.7327,-17.198 -11.4654,-22.9307 -10.032,-12.8984 -101.7547,-37.2622 -91.7225,-60.1929 4.2995,-10.0321 24.3639,1.4333 28.6634,-7.1657 1.433,-2.8664 -27.2302,-21.4976 -31.5297,-37.2624 -1.4332,-4.2995 5.7326,-5.7327 5.7326,-10.0322 1.4332,-5.7327 0,-12.8984 1.4332,-18.6311 0,-7.1658 -4.2995,-14.3317 0,-18.6312 2.8663,-5.7326 14.3317,-1.4331 18.6312,-5.7326 2.8663,-4.2995 0,-15.7649 -4.2995,-15.7649 -5.7327,1.4332 -1.4332,12.8985 -5.7327,17.198 -8.599,7.1659 0,-48.7276 1.4332,-63.0593 2.8663,-17.198 -21.4975,-8.599 -21.4975,-25.797 0,-12.8984 -1.4332,-25.7969 5.7326,-35.8291 4.2995,-5.7327 17.198,0 22.9307,-5.7327 2.8664,-2.8663 -7.1658,-4.2995 -8.599,-8.599 -1.4332,-10.032 8.599,-24.3637 1.4332,-31.5296 -8.599,-8.599 -22.9307,1.4332 -34.3961,-1.4331 0,-10.0322 0,-20.0644 0,-28.6634 0,-5.7327 2.8664,-12.8985 0,-18.6312 -4.2995,-7.1657 -17.198,-7.1657 -18.631,-14.3315 0,-1.4332 45.8612,-94.589 50.1607,-97.4554 1.4332,-1.433 8.5989,34.3961 20.0644,35.8292 0,0 30.0964,-24.3638 30.0964,-24.3638 22.9307,0 18.6312,35.8292 31.5296,42.9949 4.2995,2.8663 8.599,-2.8662 14.3317,-1.4332 10.0322,2.8665 20.0643,7.1659 27.2302,14.3317 27.2301,30.0966 7.1658,60.193 10.0322,90.2895 0,10.0322 8.599,18.6312 8.599,27.2301 -1.4332,4.2995 -12.8985,4.2995 -11.4654,8.599 1.4332,10.0322 15.7649,15.7649 17.1979,25.797 1.4332,4.2995 -7.1657,8.5989 -7.1657,12.8984 0,11.4654 37.2623,64.4926 42.9949,73.0915 8.5991,10.0322 14.3317,-5.7327 18.6311,-5.7327 5.7327,-1.433 12.8985,0 17.198,2.8665 2.8664,2.8662 -1.4331,10.0322 1.4332,10.0322 z"
+ class="europe"
+ id="al" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 3676.7488,2152.285 c -4.2995,-1.4331 -96.0221,-45.8614 -100.3216,-48.7276 -5.7326,-4.2995 12.8984,-10.0322 17.1979,-15.7648 4.2995,-7.1659 0,-28.6634 7.1659,-25.7971 11.4653,4.2995 4.2995,22.9307 11.4653,31.5297 1.4332,2.8664 2.8664,-7.1658 7.1659,-7.1658 11.4653,-1.4332 24.3638,8.599 34.396,4.2995 44.4281,-17.198 -14.3317,-32.9629 40.1286,-37.2624 10.0322,0 20.0644,7.1659 30.0964,8.599 7.1659,1.4332 -11.4652,-10.032 -17.1979,-12.8985 -2.8663,0 -5.7327,0 -8.599,0 -11.4653,-1.433 -22.9307,1.4332 -32.9629,-2.8662 -7.1658,-4.2995 17.1981,2.8662 22.9307,-2.8663 2.8664,-2.8664 -11.4653,-4.2997 -10.0321,-8.5991 0,-7.1658 5.7326,-12.8985 12.8985,-15.7648 4.2995,-1.4332 11.4653,2.8663 17.198,2.8663 10.0322,-1.4331 21.4974,-2.8663 30.0964,-10.0321 4.2995,-2.8664 -11.4653,-1.4332 -15.7647,-4.2995 -4.2995,-1.4332 -40.1287,15.7648 -50.1609,14.3316 -7.1659,-1.4331 4.2995,-15.7648 5.7327,-22.9307 0,-8.599 -5.7327,-15.7647 -4.2996,-24.3637 1.4332,-8.599 17.1981,-14.3317 14.3317,-24.3639 -1.4331,-5.7326 -14.3317,2.8664 -20.0643,0 -4.2995,-1.4331 -2.8664,-12.8985 -7.1659,-10.0321 -1.433,1.4331 4.2995,8.599 1.4332,7.1658 -11.4652,-5.7327 -22.9306,-24.3639 -32.9628,-31.5296 -1.4331,-2.8663 -5.7326,-1.4332 -8.599,-1.4332 -2.8663,0 -5.7326,4.2994 -7.1658,2.8664 -2.8663,-1.4332 -1.4332,-8.599 -5.7327,-10.0322 -5.7327,-2.8663 -84.5567,-2.8663 -27.2301,-30.0965 15.7649,-7.1659 12.8984,7.1658 27.2301,15.7648 12.8985,7.1659 32.9629,-11.4653 45.8614,-4.2995 5.7327,2.8663 -10.0322,14.3317 -4.2995,18.6312 4.2995,4.2995 7.1657,-8.599 12.8984,-10.0322 10.0322,-2.8663 21.4975,-8.599 30.0965,-2.8663 5.7327,5.7327 -2.8663,17.198 -1.4331,24.3637 0,4.2995 0,-7.1657 1.4331,-8.5989 7.1659,-8.599 114.6533,61.6262 77.391,0 -5.7327,-8.599 -20.0643,7.1659 -31.5297,5.7327 -12.8985,-1.4332 -22.9307,-17.198 -28.6634,-28.6634 0,0 1.4333,-1.4331 2.8665,-2.8663 15.7647,-11.4653 41.5618,-14.3317 58.7598,-15.7647 8.599,0 18.6311,8.5989 24.3637,1.433 11.4654,-10.0322 -42.9949,-5.7325 -41.5617,-21.4974 1.4331,-10.0322 28.6632,-5.7327 28.6632,-17.198 -1.4332,-67.3588 -50.1608,7.1658 -53.0271,-5.7327 -1.4332,-8.599 5.7327,-17.198 2.8663,-24.3637 -1.4331,-2.8665 -5.7326,5.7325 -8.599,5.7325 -2.8663,0 2.8664,-7.1657 0,-7.1657 -10.032,2.8662 -10.032,20.0642 -17.1979,1.4332 -1.4333,-1.4332 1.4332,-7.1659 0,-7.1659 -5.7327,4.2994 -5.7327,14.3316 -12.8985,15.7648 -4.2995,0 -2.8663,-10.0321 -7.1658,-11.4654 -8.599,-2.8662 -48.7277,17.198 -50.1608,-5.7325 0,-4.2997 7.1658,-4.2997 7.1658,-8.5991 -4.2995,-12.8985 -38.6955,-11.4653 -35.8292,-25.797 11.4654,-60.1929 45.8614,25.797 53.0271,21.4975 20.0644,-12.8985 -22.9306,-54.4602 5.7327,-54.4602 25.797,-1.4332 8.599,96.022 41.5619,51.5939 4.2995,-5.7327 -17.198,-12.8985 -11.4654,-17.198 5.7327,-7.1659 21.4976,8.599 28.6633,1.4332 4.2995,-7.1659 -18.6311,0 -24.3638,-5.7327 -8.599,-8.599 -11.4653,-21.4974 -12.8985,-32.9628 -5.7326,-48.7276 27.2301,44.4281 40.1286,30.0964 4.2995,-4.2995 -44.4281,-42.9949 -22.9307,-53.0271 10.0322,-4.2995 17.1982,22.9307 27.2302,18.6312 5.7327,-4.2995 -27.2302,-50.1609 14.3317,-35.8292 34.3959,12.8985 -4.2995,73.0915 34.3959,97.4553 0,1.4332 24.3639,-10.0321 25.7971,-38.6955 0,-5.7327 -8.599,-2.8662 -11.4654,-7.1657 -4.2995,-4.2995 -20.0642,-31.5297 -14.3317,-40.1287 10.0322,-11.4654 30.0966,10.0321 44.4283,17.198 4.2995,1.4331 -12.8986,-2.8664 -12.8986,-7.1659 -1.4331,-4.2995 7.1659,-2.8663 10.0322,-4.2995 1.4332,-1.4331 1.4332,-4.2995 0,-4.2995 -18.6312,1.4332 -17.198,30.0966 -38.6955,-17.198 -18.6311,-41.5618 21.4975,-10.0322 22.9307,-10.0322 0,0 1.4331,-1.433 1.4331,-2.8662 0,-2.8663 -2.8663,-8.599 0,-7.1658 4.2995,4.2995 4.2995,12.8985 10.0322,17.198 5.7327,4.2995 15.7649,2.8662 21.4974,7.1657 14.3317,11.4654 -7.1657,25.797 -5.7325,32.9629 0,2.8663 7.1657,0 10.032,1.4331 1.4332,1.4332 11.4654,10.0322 8.599,15.7649 -2.8663,1.4332 -10.032,0 -8.599,2.8662 2.8664,5.7327 14.3317,4.2995 14.3317,11.4654 1.4332,10.0322 -12.8985,20.0643 -10.0322,30.0965 2.8664,8.599 24.3639,2.8663 22.9307,11.4652 0,8.5992 -21.4975,-5.7325 -24.3638,1.4333 -1.4332,7.1659 64.4924,35.8291 -1.4332,45.8613 -11.4652,2.8663 -15.7647,-17.198 -27.2301,-21.4975 -4.2995,-1.4332 1.4332,8.599 4.2995,12.8985 2.8664,11.4653 15.7649,21.4975 12.8984,31.5297 -2.8662,11.4652 -24.3637,11.4652 -25.7969,22.9306 -2.8663,7.1658 17.198,2.8663 18.6312,10.0321 2.8663,7.1659 -8.599,14.3317 -11.4654,21.4976 -2.8663,8.599 -12.8985,25.7969 -4.2995,27.2301 30.0966,5.7326 28.6633,-54.4603 47.2945,-64.4925 58.7598,-31.5296 18.6312,8.599 17.198,14.3317 -8.599,35.8292 34.3959,-2.8664 37.2623,-5.7327 18.6312,-20.0642 11.4653,-77.391 25.797,-75.9578 50.1608,4.2995 5.7327,67.3588 34.3959,90.2895 22.9307,20.0642 10.0322,-25.797 15.7649,-37.2624 1.4331,-7.1657 7.1658,-15.7647 14.3317,-18.6311 4.2995,-1.4331 10.0321,1.4332 15.7648,4.2995 1.4332,1.4332 1.4332,5.7327 2.8662,5.7327 11.4654,-2.8663 22.9307,-18.6312 32.9629,-12.8985 10.0322,4.2995 8.599,20.0642 10.0322,31.5296 0,7.1658 -8.599,11.4653 -7.1659,18.6312 7.1659,35.8292 24.3639,-25.7969 14.3317,27.2302 -4.2995,30.0964 -14.3317,14.3315 -7.1658,41.5617 11.4653,48.7277 11.4653,-93.1558 25.7969,-96.022 37.2624,-10.0322 28.6634,51.5939 31.5297,65.9255 0,2.8663 0,-7.1657 2.8663,-8.5989 1.4332,-1.4333 7.1659,0 5.7327,-1.4333 -8.599,-18.631 2.8663,8.599 2.8663,8.599 1.4332,0 1.4332,-4.2995 2.8664,-5.7325 7.1657,-11.4654 21.4974,-31.5297 37.2622,-32.9629 15.7649,-1.4332 14.3317,31.5297 27.2301,40.1286 5.7327,2.8663 -2.8662,-12.8984 1.4333,-17.1979 8.5989,-10.0322 14.3316,12.8984 14.3316,12.8984 1.4333,2.8663 -1.4332,-7.1657 0,-10.0321 2.8663,-4.2995 10.0322,-7.1658 11.4653,-12.8985 2.8664,-5.7327 0,-12.8985 0,-20.0643 1.4332,-8.599 0,-20.0644 7.1659,-24.3638 10.0321,-4.2995 20.0643,2.8662 30.0965,5.7327 25.7969,5.7327 4.2995,17.1979 7.1657,27.2301 2.8665,5.7326 11.4654,4.2995 14.3317,8.599 10.0322,21.4975 -42.9949,27.2302 4.2995,53.0271 5.7327,2.8663 5.7327,-12.8985 11.4654,-15.7647 5.7326,-2.8665 12.8985,2.8662 18.6312,0 12.8985,-4.2997 27.23,-35.8292 45.8612,-1.4333 2.8664,5.7327 -12.8985,2.8663 -20.0643,4.2995 -21.4974,4.2995 -30.0965,1.4332 -44.4281,24.3638 -1.4332,2.8664 7.1658,2.8664 7.1658,5.7327 1.4332,4.2995 -7.1658,7.1659 -4.2995,8.599 7.1658,5.7327 20.0644,1.4332 27.2302,7.1659 22.9306,17.1979 -31.5297,48.7276 -34.396,51.5939 -2.8664,1.4332 5.7326,-4.2995 10.0321,-2.8663 10.0322,2.8663 20.0644,10.0322 28.6633,17.1979 1.4333,1.4333 -7.1657,1.4333 -7.1657,4.2995 0,4.2995 7.1657,8.599 7.1657,12.8985 0,2.8663 -5.7327,-1.4332 -7.1657,0 -10.0322,5.7327 -37.2624,17.198 -27.2302,21.4975 12.8985,4.2995 24.3637,-27.2302 34.3959,-18.6312 4.2995,5.7327 8.599,11.4654 14.3317,15.7649 2.8663,2.8663 8.599,-2.8663 10.0322,1.4332 7.1658,12.8985 14.3316,37.2622 -2.8664,48.7276 -8.599,5.7326 -21.4975,4.2995 -30.0964,8.599 -4.2995,2.8663 -10.0322,8.599 -7.1658,11.4653 4.2995,4.2995 78.824,12.8984 20.0642,47.2945 -12.8984,7.1658 -45.8613,-22.9307 -45.8613,-24.3639 -10.0321,-12.8984 45.8613,22.9307 31.5296,35.8292 -7.1657,5.7327 -18.6311,-14.3317 -25.7969,-8.599 -5.7327,4.2995 14.3317,8.599 17.198,15.7649 1.4332,2.8663 -5.7327,5.7325 -8.599,7.1658 -12.8985,5.7327 -30.0965,21.4974 -40.1286,11.4652 -7.1658,-7.1657 -5.7328,-35.8291 -12.8985,-28.6632 -7.1658,8.599 15.7647,21.4975 10.0322,31.5296 -4.2995,8.599 -22.9307,-1.4332 -30.0966,5.7326 -8.599,7.1659 -2.8663,24.3639 -10.0321,32.9629 -2.8664,4.2995 -11.4654,-4.2995 -15.7649,-2.8663 -10.0322,2.8663 -15.7647,15.7647 -25.7969,17.198 -8.599,1.4332 -4.2995,-35.8292 -27.2302,-41.5619 -7.1657,-1.4332 1.4332,15.7649 -2.8662,22.9307 -1.4333,2.8663 -7.166,-2.8663 -10.0322,0 -1.4333,0 5.7327,4.2995 4.2995,4.2995 -38.6955,7.1657 -17.198,-2.8663 -53.0272,2.8662 -18.6311,2.8665 -34.396,18.6312 -53.0271,25.7971 -2.8664,1.4331 -8.5989,2.8663 -11.4654,1.4331 -10.0322,-4.2995 -12.8984,-20.0642 -22.9306,-20.0642 -8.599,1.4332 -5.7326,20.0642 -12.8985,22.9306 -5.7327,1.4331 -7.1658,-10.0322 -12.8985,-12.8984 -8.599,-2.8665 -40.1286,-4.2995 -51.5939,-8.599 -20.0644,-5.7327 -21.4976,28.6632 -25.7971,32.9627 -10.032,8.599 -63.0593,2.8664 -73.0913,-1.4332 -25.7971,-8.599 -47.2946,-50.1607 -60.193,-63.0592 -18.6312,-15.7649 -34.396,-2.8664 -44.4282,-31.5296 -1.4332,-7.1659 -8.599,-15.7649 -5.7327,-22.9307 2.8664,-10.0322 30.0966,-14.3317 21.4975,-18.6312 -8.599,-4.2995 -15.7648,22.9307 -22.9305,17.198 -8.599,-7.1658 10.032,-22.9307 4.2994,-32.9629 -2.8664,-5.7325 -10.0321,12.8986 -15.7648,10.0322 -10.0322,-5.7327 -10.0322,-20.0642 -18.6312,-27.2301 -4.2995,-4.2995 -10.0321,-2.8663 -15.7648,-4.2995 z"
+ class="europe"
+ id="is" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 7768.4368,4911.1289 c 17.198,-4.2995 -17.198,-17.1979 5.7327,-24.3637 7.1658,-2.8664 17.198,-2.8664 25.7969,-1.4332 2.8665,0 2.8665,7.1657 7.166,8.599 14.3315,4.2994 34.3959,-63.0593 40.1286,-67.3588 11.4653,-5.7327 25.7969,-2.8663 37.2622,-7.1658 17.198,-7.1659 30.0966,-21.4976 48.7276,-27.2302 7.1659,-1.4332 14.3317,1.4331 21.4976,1.4331 7.1658,0 14.3316,0 21.4975,0 21.4974,-2.8663 41.5618,-15.7648 61.6261,-14.3315 4.2995,0 5.7327,5.7325 10.0322,7.1657 35.8291,4.2995 80.2572,0 113.2201,-8.599 15.7647,-4.2994 4.2995,10.0322 12.8985,17.198 2.8662,1.4332 8.5989,-5.7327 11.4652,-2.8663 2.8663,2.8663 -5.7327,11.4653 -1.4332,12.8985 35.8292,14.3317 10.0322,-8.599 27.2302,-12.8985 57.3267,-15.7649 5.7327,8.599 24.3639,18.6312 2.8662,2.8663 7.1657,7.1658 8.5989,4.2995 7.1658,-4.2995 0,-30.0966 7.1658,-30.0966 7.1659,-1.4331 20.0644,11.4654 28.6634,1.4332 5.7327,-5.7327 5.7327,-27.2301 14.3317,-22.9306 12.8984,8.5989 -18.6312,21.4974 17.1979,21.4974 12.8985,0 5.7327,-28.6632 25.797,-14.3315 37.2623,25.7969 -7.1658,17.1979 22.9306,24.3637 8.599,1.4332 -2.8662,-20.0642 2.8663,-27.2302 8.5992,-11.4652 21.4976,-31.5296 32.9629,-25.7969 11.4654,5.7327 22.9307,45.8613 34.3959,42.9949 20.0644,-5.7325 7.1659,-30.0964 30.0966,-35.8291 8.599,-1.4331 15.7648,8.599 22.9307,7.1659 47.2944,-8.599 -31.5297,-24.3639 34.3959,-20.0644 32.9629,2.8664 15.7648,38.6956 53.0271,30.0966 35.8291,-7.1659 -21.4974,-45.8614 -21.4974,-61.6263 0,-11.4652 2.8662,-38.6954 4.2994,-48.7276 7.1658,-40.1286 17.198,-54.4603 22.9307,-55.8934 10.0322,-2.8664 21.4975,1.4331 30.0964,-2.8664 5.7328,-2.8663 2.8663,-12.8985 8.599,-15.7647 12.8985,-8.599 31.5297,-7.1659 47.2946,-11.4654 -4.2995,-2.8663 -1.4332,-8.599 -4.2995,-12.8985 0,0 -30.0966,-18.6312 -30.0966,-20.0643 -1.4331,-2.8664 4.2995,-7.1659 2.8664,-10.0322 -4.2995,-10.0321 -14.3317,-15.7648 -15.7649,-24.3638 -1.4332,-4.2995 7.1659,-5.7326 7.1659,-10.0321 -2.8664,-10.0322 -22.9306,-10.0322 -24.3638,-21.4976 -1.4332,-4.2995 7.1657,-5.7325 8.5989,-10.032 4.2995,-17.198 -47.2944,-27.2302 -51.5938,-38.6956 -5.7327,-11.4653 4.2994,-25.7969 2.8662,-38.6954 0,-22.9307 68.792,7.1658 87.4231,-4.2995 12.8986,-7.1658 27.2301,-34.3959 25.7971,-48.7276 0,-1.4332 -5.7327,0 -4.2995,-1.4332 7.1657,-7.1658 20.0642,-10.0322 24.3637,-18.6312 4.2995,-5.7326 0,-18.6312 -5.7327,-21.4975 -24.3637,-8.599 -27.23,1.4332 -40.1286,-12.8984 -4.2995,-5.7327 10.0322,-25.797 -5.7326,-24.3639 -17.198,1.4332 -25.7971,-4.2995 -50.1608,2.8664 -8.599,2.8663 -17.198,11.4653 -25.797,8.599 -7.1659,-2.8664 5.7326,-42.9951 -7.1659,-51.5941 -4.2995,-2.8662 -48.7276,-2.8662 -63.0593,-12.8984 -22.9306,-14.3317 -8.599,-28.6633 -41.5617,-38.6955 -5.7327,-2.8662 8.599,-27.2301 2.8663,-28.6633 -20.0644,0 -27.2302,-1.4331 -50.1608,-17.198 -10.0322,-5.7327 8.599,-35.8292 5.7327,-41.5618 -8.599,-12.8985 11.4652,-2.8663 5.7327,-11.4653 -4.2995,-7.1658 -38.6956,-34.3959 -37.2624,-41.5618 5.7327,-20.0643 8.599,-34.396 -2.8663,-54.4604 -2.8664,-5.7325 -11.4654,5.7327 -17.1981,5.7327 -1.433,0 -47.2944,-28.6632 -47.2944,-28.6632 -35.8291,-14.3317 -10.0322,-2.8664 -30.0964,5.7326 -2.8664,1.4332 -20.0644,-1.4331 -24.3639,2.8664 -7.1658,10.032 -18.6312,37.2622 -30.0964,42.9949 -22.9307,11.4653 -21.4975,-47.2944 -21.4975,-47.2944 -44.4281,-10.0322 -35.8291,-4.2995 -65.9257,18.631 -7.1658,5.7327 -12.8985,-14.3315 -20.0643,-20.0642 -24.3638,-17.198 -17.1979,30.0964 -53.0271,1.4332 0,1.4331 -27.2301,22.9305 -30.0965,31.5297 -2.8663,4.2994 -10.0321,47.2944 -10.0321,47.2944 -7.1659,5.7327 -18.6312,-2.8663 -27.2302,0 -2.8664,1.4332 0,11.4652 -4.2994,11.4652 -5.7327,0 -10.0322,-8.599 -17.198,-8.599 -18.6312,-1.433 -21.4976,40.1287 -45.8613,45.8614 v 1.4332 c 8.5989,21.4974 -1.4333,40.1286 -2.8665,61.6261 -1.433,12.8984 45.8614,-12.8985 40.1287,7.1657 -1.4331,4.2995 -8.599,4.2995 -11.4653,8.599 -1.4332,5.7327 4.2995,14.3317 0,20.0644 -8.599,10.0322 -30.0964,-4.2995 -37.2624,5.7327 -5.7325,10.0321 1.4333,24.3638 -5.7325,32.9627 -27.2302,42.9951 -14.3317,-12.8984 -31.5297,40.1287 0,2.8664 -5.7327,2.8664 -5.7327,4.2995 0,0 12.8985,40.1286 12.8985,40.1286 0,1.4332 -14.3317,41.5618 -12.8985,47.2945 0,5.7327 44.4282,-2.8662 31.5297,28.6634 -2.8663,7.1658 -31.5297,17.198 -30.0965,4.2995 0,-4.2995 7.1658,-7.1659 5.7326,-11.4654 -2.8663,-18.6312 -35.8291,-2.8663 -40.1286,2.8663 -15.7648,15.7649 15.7649,14.3317 -7.1658,27.2301 -10.0322,4.2995 -24.3639,1.4333 -31.5297,10.0322 -5.7327,5.7327 14.3317,21.4975 7.1658,24.3639 -7.1658,1.4331 -15.7647,-2.8664 -22.9306,1.4331 -8.599,5.7327 -10.0321,20.0644 -18.6311,25.7971 -12.8986,11.4652 -18.6312,-14.3317 -30.0966,-10.0322 -11.4652,5.7327 -53.0271,28.6632 -70.225,17.1979 25.7969,73.0915 88.8562,140.4503 93.1557,219.2744 0,2.8663 -2.8663,5.7327 -4.2995,7.1658 -10.0322,10.0322 -100.3215,85.9899 -58.7598,101.7548 2.8664,1.4331 5.7327,-5.7327 8.599,-4.2995 12.8985,1.4331 27.2301,5.7326 38.6955,14.3317 28.6633,22.9305 -1.4332,71.6583 10.0321,97.4553 1.4332,4.2994 5.7327,8.5989 8.5991,12.8984 z"
+ class="europe"
+ id="by" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 6904.2379,1713.7363 v 0 m 14.3317,4.2995 c 1.4332,0 1.4332,0 1.4332,1.4332 m 232.1728,243.6381 h -1.4332 m 0,1.4332 v 0 m 378.3558,-543.1699 c 8.599,11.4654 28.6632,38.6956 11.4653,44.4282 -17.198,5.7327 -61.6261,-17.198 -47.2944,-4.2995 27.2301,22.9306 -27.2302,55.8935 -34.3961,73.0915 0,1.4332 17.1981,63.0593 -2.8663,55.8935 -54.4603,-21.4976 10.0322,-57.3267 -8.599,-88.8564 -28.6633,-50.1607 -73.0915,-31.5295 -110.3537,-71.6583 l -1.4332,-7.1657 c -1.4332,0 -2.8663,0 -4.2995,1.4332 0,-1.4332 -1.4332,-1.4332 -1.4332,-2.8665 v 2.8665 c -28.6632,5.7325 -22.9307,38.6954 -47.2944,45.8613 -21.4975,5.7326 -12.8985,-24.3639 -37.2624,2.8663 -2.8662,4.2995 0,11.4652 -1.4332,17.1979 -1.433,1.4333 -17.1979,31.5297 -17.1979,32.9629 -4.2995,11.4653 4.2995,48.7276 5.7327,58.7598 0,4.2995 -2.8663,7.1658 -2.8663,11.4653 0,8.599 1.4331,15.7647 2.8663,24.3639 4.2995,11.4652 14.3317,20.0642 15.7647,31.5296 7.1659,50.1607 -5.7325,15.7648 -22.9305,31.5295 -20.0644,20.0644 -1.4332,25.7971 -7.1659,50.1608 -1.4332,5.7328 -7.1658,8.599 -8.599,14.3317 0,1.4332 -18.6312,-15.7647 -21.4975,-17.1979 -4.2994,-2.8663 -57.3265,-21.4975 -61.626,-15.7649 -5.7327,5.7327 -1.4333,17.1981 -5.7327,22.9306 -17.198,30.0966 -40.1287,11.4654 -64.4926,15.7649 -7.1657,0 -17.1979,7.1658 -22.9306,2.8663 -7.1658,-5.7327 -4.2995,-17.1979 -8.599,-24.3637 -10.0321,-17.1981 -42.9949,-61.6263 -60.1929,-71.6583 -57.3266,-32.9629 -31.5296,53.0271 -31.5296,53.0271 -8.599,1.4331 -15.7649,-8.599 -24.3639,-8.599 -4.2995,0 -11.4653,4.2995 -10.0322,7.1658 1.4332,1.4332 2.8664,2.8663 4.2996,2.8663 h -2.8664 c -11.4653,2.8664 -27.2302,-4.2995 -32.9627,5.7327 -4.2997,7.1659 14.3315,11.4654 18.631,18.6312 4.2995,8.5989 7.1659,18.6311 7.1659,28.6633 0,17.198 -24.3638,30.0965 -20.0644,47.2944 1.4333,12.8985 51.5941,10.0322 4.2995,40.1287 -12.8984,8.5989 -111.7869,-40.1287 -131.8511,-14.3317 -8.599,11.4654 27.2301,93.1557 -1.4332,104.6211 l -45.8614,-20.0644 c -8.599,0 -35.8291,50.1609 -37.2622,61.6263 -2.8664,15.7647 -2.8664,35.8291 -14.3317,50.1607 -8.599,10.0321 -24.3639,-5.7326 -20.0644,20.0643 5.7327,31.5297 42.9951,34.396 27.2302,73.0915 -8.599,24.3638 -55.8934,80.2573 -64.4924,106.0543 -2.8664,12.8984 15.7648,24.3638 11.4653,37.2623 l -31.5297,27.2302 c -11.4652,5.7325 -34.3959,-7.166 -40.1286,5.7325 l 14.3317,55.8936 c 0,0 -7.1659,42.9949 -7.1659,44.4281 l 4.2995,53.0271 c -1.4331,5.7327 -8.599,7.1658 -10.0321,12.8985 -12.8985,38.6955 -32.9628,78.8242 -45.8613,118.9528 -8.599,25.7969 51.5939,11.4653 41.5618,81.6903 -10.0322,67.3588 -48.7276,0 -107.4874,37.2624 -18.6312,11.4652 -50.1608,60.193 -54.4603,83.1235 -1.4332,7.1659 7.1658,12.8986 5.7327,18.6312 -1.4332,15.7649 -21.4976,25.7971 -21.4976,41.5619 0,173.413 21.4976,-127.5517 21.4976,45.8613 0,8.599 -10.0322,14.3317 -10.0322,22.9307 -1.4332,10.032 4.2995,18.6311 5.7327,28.6632 0,8.599 -7.1659,18.6312 -5.7327,27.2301 1.4331,21.4975 24.3637,40.1287 27.2301,61.6261 2.8663,31.5297 -20.0643,71.6583 -12.8984,101.7549 8.599,32.9627 63.0593,30.0964 68.7918,65.9256 l -15.7647,57.3266 c -11.4654,10.0322 -40.1287,2.8664 -38.6956,17.1979 7.1659,61.6263 44.4281,45.8614 28.6634,127.5519 l 1.4332,41.5618 c -12.8985,32.9627 -58.7598,35.8291 -61.6262,45.8612 -1.4331,2.8664 5.7327,4.2995 5.7327,8.599 2.8664,5.7327 5.7327,12.8986 2.8664,18.6312 -4.2996,8.5989 -18.6312,10.0322 -21.4976,20.0643 -8.599,25.797 31.5296,83.1236 0,127.5517 -12.8985,18.6312 -18.6312,4.2995 -22.9307,-10.032 1.4332,0 1.4332,0 2.8664,-1.4332 5.7327,-4.2995 -2.8664,-15.7649 -7.1659,-21.4975 -30.0964,-47.2946 -14.3315,22.9307 -61.6261,-21.4976 -4.2994,-4.2995 4.2995,-12.8985 4.2995,-18.631 0,-8.599 -8.5989,0 -8.5989,0 -8.599,-12.8985 -5.7328,-34.3961 -2.8663,-31.5297 0,-2.8664 -1.4332,-4.2995 -1.4332,-5.7327 -2.8663,-14.3317 -7.1658,-27.2301 -10.0322,-41.5618 -1.4331,-8.599 11.4654,-27.2302 1.4332,-27.2302 -35.8292,0 25.7969,44.4281 -10.0322,68.792 -1.4331,0 -2.8663,-1.4332 -2.8663,-2.8663 -1.4332,-8.599 2.8663,-18.6311 0,-25.7971 -4.2995,-7.1657 -18.6312,-20.0642 -20.0644,-12.8984 -1.4331,11.4654 12.8985,18.6311 17.1981,28.6633 2.8663,5.7327 -5.7327,18.6312 -2.8664,25.797 11.4654,30.0964 20.0644,14.3317 11.4654,34.3959 0,1.4332 0,5.7327 -2.8664,5.7327 -2.8663,0 -5.7326,-8.599 -8.599,-5.7327 -10.0322,18.6312 2.8664,42.9951 -8.599,42.9951 -5.7327,0 1.4332,-18.6312 -2.8663,-15.7649 -24.3638,15.7649 21.4975,31.5296 -38.6955,31.5296 -7.1658,0 0,-14.3317 -5.7326,-18.6312 -7.1659,-4.2994 -20.0644,-20.0642 -24.3639,-12.8984 -4.2995,10.0322 14.3317,15.7649 18.6312,25.7971 1.4332,4.2995 -37.2623,7.1657 -41.5618,21.4974 -1.4331,5.7326 14.3317,11.4653 10.0322,15.7648 -7.1658,8.599 -25.797,1.4332 -32.9629,8.599 -4.2995,5.7327 14.3317,7.1659 12.8985,12.8984 -8.599,58.7598 -20.0643,-17.1979 -27.23,12.8986 -1.4333,2.8663 4.2994,5.7326 2.8662,8.599 -7.1659,10.0321 -44.4281,54.4602 -45.8613,55.8934 -2.8663,2.8664 -7.1658,-5.7327 -10.0322,-2.8663 -8.599,7.1658 -7.1658,25.797 -18.631,27.2302 -8.5992,2.8663 -10.0322,-21.4975 -20.0644,-18.6312 -10.0322,2.8663 -5.7327,21.4975 -12.8985,27.2302 -5.7327,4.2995 -15.7649,-2.8664 -21.4974,0 -4.2995,1.4331 -5.7328,7.1658 -10.0322,8.599 -14.3317,8.5989 -10.0322,-4.2995 -30.0965,-4.2995 -10.0322,0 -21.4976,15.7647 -27.2301,8.599 -5.7327,-7.1658 18.6311,-12.8985 15.7647,-21.4975 -4.2995,-7.1659 -12.8985,11.4653 -21.4974,11.4653 -11.4653,1.4332 -22.9307,-2.8663 -34.396,-7.1658 -24.3639,-11.4654 22.9307,-27.2302 27.2302,-37.2624 1.4331,-5.7327 -8.599,-11.4652 -14.3317,-10.0322 -5.7327,1.4333 -4.2995,14.3317 -11.4654,15.7649 -8.599,1.4331 -17.198,-4.2995 -25.797,-8.5989 -17.1979,-7.166 -27.2301,-32.9629 -35.8291,-38.6956 -4.2995,-4.2995 -12.8985,-1.4331 -18.6312,-4.2995 -10.032,-5.7326 -27.2301,-42.9949 -25.7969,-53.0271 0,-8.599 10.0322,-14.3317 12.8985,-24.3637 1.4332,-8.599 -10.0322,-30.0966 0,-27.2302 11.4654,2.8663 2.8664,27.2302 14.3316,31.5296 5.7327,4.2995 7.1658,-14.3316 14.3317,-17.1981 2.8663,-2.8662 5.7326,0 8.599,2.8665 -2.8664,-7.166 -4.2995,-14.3317 -7.1659,-21.4975 -1.4331,-10.0322 20.0644,-7.1659 25.7971,-14.3317 2.8663,-2.8664 -12.8985,0 -11.4654,-4.2995 2.8664,-7.1659 10.0322,-11.4654 17.1979,-14.3317 8.599,-5.7327 25.7971,-1.4332 30.0966,-11.4652 5.7326,-12.8986 -34.396,5.7325 -42.995,-2.8664 -5.7326,7.1659 -8.599,7.1659 -8.599,-5.7327 1.4332,-11.4653 25.7969,-14.3316 22.9307,-24.3638 -4.2995,-11.4654 -21.4975,-14.3317 -32.9629,-11.4654 -18.631,5.7327 -11.4653,50.1608 -30.0964,48.7278 -10.0322,0 -2.8663,-18.6312 -7.1658,-25.7971 -2.8664,-4.2995 -8.599,-7.1658 -14.3317,-10.0321 0,0 0,1.4331 1.4332,1.4331 -1.4332,0 -1.4332,0 -2.8664,1.4332 0,-1.4332 1.4332,-2.8663 1.4332,-2.8663 -4.2995,-2.8664 -8.599,-5.7327 -8.599,-10.0322 0,-7.1659 17.198,5.7327 22.9307,1.4332 8.599,-7.1659 5.7325,-25.797 15.7647,-30.0965 7.1659,-4.2995 11.4654,11.4654 18.6312,12.8984 2.8663,1.4333 -1.4332,-7.1657 1.4332,-8.5989 2.8663,-2.8663 7.1658,1.4332 10.0322,0 5.7326,-1.4331 -14.3317,-1.4331 -12.8986,-7.1658 0,-1.4332 58.7598,-27.2302 54.4603,-27.2302 -45.8613,-2.8663 -17.1979,35.8292 -61.6261,-4.2995 -1.4332,-1.4332 7.1658,0 8.599,-2.8662 2.8663,-2.8665 0,-10.0322 4.2995,-11.4655 5.7327,-4.2994 12.8985,1.4333 18.6312,-2.8662 2.8663,-1.4332 -1.4332,-5.7327 -1.4332,-8.599 2.8664,-5.7327 5.7327,-10.0322 8.599,-12.8985 7.1659,-4.2995 22.9306,0 24.3638,-7.1659 1.4332,-8.599 -18.6311,-7.1658 -18.6311,-14.3317 -1.4333,-2.8663 1.4332,-7.1658 4.2994,-11.4652 -2.8662,0 -7.1657,-7.1658 -10.0321,-4.2995 -7.1658,4.2995 -11.4653,11.4652 -15.7648,17.1979 -5.7327,11.4653 -2.8663,25.797 -11.4654,34.396 -4.2995,4.2994 -2.8663,-11.4653 -4.2995,-15.7648 -8.599,-15.7649 -15.7648,-11.4654 -10.0321,-25.7969 5.7326,-11.4655 28.6633,-37.2624 15.7648,-34.3961 -14.3317,4.2995 -24.3637,67.3588 -41.5618,35.8291 -4.2995,-10.032 -8.599,-21.4974 -11.4653,-32.9627 -1.4332,-11.4654 20.0642,0 45.8613,-8.599 5.7326,-1.4332 8.599,-7.1659 10.0321,-14.3317 -10.0321,10.0322 -28.6632,-11.4654 -28.6632,-17.1979 0,-12.8985 20.0642,-15.7649 31.5296,-21.4976 -2.8664,-15.7648 -15.7649,-1.4331 -27.2302,11.4654 0,0 -1.4332,0 -1.4332,-1.4332 -1.433,-1.4331 -2.8662,-4.2995 -5.7325,-7.1658 4.2995,4.2995 5.7325,10.0322 7.1657,8.599 -7.1657,7.1657 -12.8984,12.8984 -17.1979,14.3317 -7.1659,0 -4.2995,-14.3317 -7.1659,-20.0644 -1.4331,-2.8663 -27.2302,-28.6633 -20.0643,-30.0965 10.0322,0 18.6312,8.599 27.2302,15.7648 0,-1.4331 -1.4332,-4.2995 0,-7.1658 2.8663,-10.0322 20.0642,-10.0322 28.6632,-17.198 1.4332,-1.4332 -1.4331,-7.1659 -4.2995,-5.7327 -11.4652,1.4333 -30.0964,15.7649 -40.1286,10.0322 -12.8985,-7.1659 -2.8663,-40.1286 4.2995,-44.4281 10.0322,-7.1659 21.4976,14.3317 34.396,14.3317 1.4331,1.4331 30.0965,-32.9629 61.6261,-17.1981 10.0322,4.2995 11.4653,18.6312 21.4975,22.9307 10.0322,4.2995 -8.599,-21.4975 24.3639,-30.0965 41.5617,-11.4652 -1.4332,61.6261 42.9949,58.7598 27.2301,-2.8662 -10.0322,-25.7969 -8.599,-44.4281 1.4332,-20.0644 74.5246,27.2302 64.4925,-22.9307 -1.4332,-10.0322 -20.0644,8.599 -28.6634,5.7327 -5.7327,-1.4332 -7.1657,-10.0322 -7.1657,-15.7649 0,-15.7647 40.1286,-31.5296 25.7969,-37.2623 -35.8291,-14.3316 -24.3639,51.594 -27.2301,58.7598 0,1.4332 -17.198,5.7327 -31.5297,7.1659 -2.8663,1.4331 -1.4332,-4.2995 -2.8663,-5.7327 -10.0322,-5.7327 -21.4976,-8.599 -31.5296,-14.3317 l -1.4332,-1.433 c -4.2995,10.032 -8.599,17.1979 -11.4654,20.0642 -1.4331,1.4332 -45.8612,-18.6312 -57.3266,-18.6312 -4.2995,-1.433 -1.4331,7.1659 -4.2995,8.599 -15.7648,7.1659 -34.396,10.0322 -50.1609,5.7327 -2.8662,0 -24.3637,-24.3639 -21.4974,-28.6634 7.1659,-10.032 20.0644,-14.3315 30.0966,-17.1979 11.4652,-2.8663 32.9627,10.0322 32.9627,-1.4331 1.4332,-10.0322 -21.4975,-2.8664 -32.9627,-1.4332 -7.1659,0 -12.8986,10.0322 -20.0644,7.1658 -30.0965,-11.4653 5.7327,-27.2302 14.3317,-27.2302 14.3317,1.4332 38.6954,18.6312 55.8934,7.1659 17.1979,-12.8985 -50.1607,4.2995 -58.7599,-15.7649 -2.8662,-8.599 32.9629,-7.1657 25.7971,-11.4652 -8.5989,-4.2995 -21.4976,1.4332 -28.6633,-5.7327 -22.9307,-18.6312 11.4654,-55.8934 32.9628,-57.3266 15.7648,0 35.8292,2.8662 44.4282,15.7647 4.2994,5.7327 -8.599,15.7649 -4.2995,21.4976 4.2995,5.7326 1.433,-17.1981 7.1657,-18.6312 7.166,-1.4332 10.0322,12.8985 17.198,14.3317 5.7327,0 -11.4653,-10.0322 -8.599,-12.8986 7.1659,-8.599 41.5619,21.4976 67.3588,-2.8663 4.2995,-4.2995 -7.1658,-7.1657 -12.8985,-7.1657 -50.1608,-2.8665 24.3639,11.4652 -17.1979,14.3316 -7.1658,1.4331 -51.5941,-20.0643 -71.6583,-22.9306 -7.1658,0 -12.8985,-4.2995 -20.0644,-4.2995 -8.599,0 -24.3637,12.8985 -25.7969,4.2995 -1.4333,-10.0322 21.4974,-10.0322 24.3638,-20.0644 2.8663,-12.8985 -25.7971,-18.6312 -25.7971,-32.9629 0,-5.7325 14.3317,0 17.1981,4.2997 4.2995,7.1657 4.2995,24.3637 12.8985,22.9305 8.599,-1.4331 -10.0322,-24.3638 -1.4332,-25.797 11.4654,-1.4332 17.198,21.4975 28.6634,21.4975 8.599,0 5.7325,-27.2302 11.4652,-21.4975 7.166,5.7327 -2.8662,18.6312 -2.8662,28.6634 0,1.4331 -2.8665,5.7326 -1.4332,5.7326 8.5989,-2.8663 4.2994,-21.4975 12.8984,-21.4975 4.2995,0 1.4332,10.0322 5.7327,12.8985 8.599,4.2995 22.9307,12.8986 27.2302,4.2995 2.8663,-7.1658 -18.6312,-4.2995 -22.9307,-10.0321 -7.1657,-8.599 -10.0322,-18.6312 -10.0322,-28.6633 -1.433,-12.8985 20.0644,-22.9307 32.9629,-20.0643 5.7327,1.4331 2.8663,12.8985 5.7327,18.631 4.2995,8.599 11.4653,14.3317 14.3315,22.9307 0,2.8664 -7.1657,8.599 -4.2995,8.599 4.2995,0 5.7327,-5.7326 5.7327,-8.599 -5.7327,-28.6632 -37.2623,-47.2944 12.8985,-55.8934 17.198,-2.8664 20.0644,21.4975 20.0644,44.4281 1.4331,-2.8664 2.8663,-7.1659 5.7326,-8.5989 8.599,-7.166 32.9628,8.5989 30.0966,-1.4332 -2.8665,-11.4653 -22.9307,-1.4333 -32.9629,-8.599 -7.1658,-5.7327 -4.2995,-18.6312 -10.0322,-25.797 -7.1658,-10.0322 -45.8612,-22.9306 -51.5939,-27.2301 -1.4332,-1.4332 4.2995,-5.7327 8.599,-5.7327 5.7327,-1.4333 12.8985,-1.4333 18.6312,1.4332 2.8662,1.433 1.433,8.599 4.2994,8.599 4.2995,1.4332 21.4975,-28.6634 41.5618,-12.8985 5.7327,4.2995 -2.8663,18.6312 4.2996,21.4974 5.7325,2.8663 -2.8664,-15.7649 2.8663,-18.6311 10.0322,-5.7327 31.5296,25.7969 31.5296,27.2301 8.599,1.4332 25.797,-5.7327 21.4975,-12.8985 -4.2995,-8.5989 -21.4975,5.7327 -28.6634,0 -10.032,-8.5989 10.0322,-24.3638 25.7971,-30.0964 -11.4654,4.2995 -21.4976,7.1658 -31.5297,7.1658 -85.9899,1.4332 -10.0322,-8.599 -12.8984,-25.797 -1.4332,-11.4654 -20.0644,-10.0322 -27.2302,-20.0644 -10.0322,-11.4652 -5.7327,-21.4974 32.9627,-17.1979 8.599,1.4332 8.599,17.1979 17.198,20.0644 5.7327,1.433 10.0322,-8.599 15.7649,-8.599 8.599,-1.4332 15.7647,-1.4332 24.3639,0 -1.4332,-1.4332 -2.8665,-4.2995 -4.2997,-5.7327 -1.433,-8.599 10.0322,-17.198 18.6312,-18.6312 4.2995,-1.4332 0,8.599 2.8664,12.8985 4.2995,5.7327 14.3316,5.7327 17.198,12.8984 2.8663,5.7328 -5.7327,14.3317 0,18.6312 7.1658,7.1658 25.7969,20.0644 31.5297,10.0322 4.2994,-10.0322 -22.9307,-14.3317 -24.3639,-25.7969 0,-7.1659 15.7649,-1.4332 21.4974,-5.7327 2.8665,-2.8663 -10.032,2.8662 -12.8984,0 -1.4331,-1.4332 8.5989,-7.1659 5.7327,-5.7327 -7.1658,1.4332 -12.8985,11.4654 -18.6312,8.5989 -10.0322,-5.7325 -12.8985,-18.6311 -15.7648,-28.6632 -2.8664,-4.2995 7.1658,4.2995 11.4653,5.7326 4.2995,2.8664 -14.3317,-14.3316 0,-11.4653 1.4332,0 58.7598,30.0965 53.0271,-8.599 -5.7326,-47.2945 -45.8612,41.5619 -37.2622,-20.0644 1.4331,-12.8984 35.8291,-35.8291 45.8612,-30.0964 8.599,4.2995 -1.4331,22.9307 5.7327,31.5297 2.8663,4.2995 7.1659,-7.1658 11.4654,-10.0322 7.1658,-2.8663 27.23,-2.8663 21.4974,-8.599 -4.2994,-5.7327 -24.3638,14.3317 -22.9306,5.7327 0,-10.0322 15.7647,-12.8985 22.9306,-21.4975 8.599,-8.599 8.599,-25.7971 20.0643,-27.2301 8.599,-2.8665 4.2995,14.3317 8.599,21.4974 2.8664,5.7327 12.8984,8.599 12.8984,15.7648 0,8.5991 -12.8984,12.8986 -11.4652,21.4976 1.4332,5.7325 8.599,-7.1659 14.3317,-5.7327 8.599,1.4332 18.6311,22.9306 22.9306,15.7647 4.2995,-11.4652 -17.1979,-20.0642 -15.7649,-31.5296 0,-11.4653 58.7599,2.8664 60.1931,2.8664 10.0322,-7.1659 11.4652,-22.9307 10.0322,-34.3961 -1.4333,-5.7325 -18.6312,5.7327 -17.1982,0 4.2995,-18.631 63.0593,-21.4974 60.1931,-35.8291 -1.4332,-11.4653 -35.8292,-11.4653 -28.6634,-21.4975 5.7327,-10.032 31.5296,-7.1657 32.9629,-18.6311 4.2995,-57.3266 -47.2944,12.8986 -58.7598,22.9306 -5.7328,4.2995 -22.9307,8.599 -18.6312,15.7649 5.7327,10.0322 22.9306,4.2995 32.9628,8.599 1.4332,0 -2.8662,1.4332 -4.2995,2.8663 -7.1659,4.2995 -15.7649,7.1659 -22.9306,12.8985 -7.1658,5.7327 -42.9949,37.2623 -54.4603,34.396 -7.1658,-1.4332 -4.2995,-12.8984 -7.1658,-18.6311 -2.8664,-5.7328 -12.8985,-10.0322 -10.0322,-15.7649 4.2995,-10.0321 18.6312,-11.4653 27.2302,-18.6311 1.4332,-1.4332 -5.7328,4.2995 -8.599,2.8663 -2.8665,0 4.2995,-7.1659 2.8662,-5.7327 -10.0321,4.2995 -25.7969,27.2302 -40.1286,21.4975 -21.4974,-8.599 10.0322,-27.2302 25.797,-37.2623 8.5989,-4.2994 20.0644,-4.2994 25.7971,-12.8984 4.2994,-4.2995 -12.8985,4.2995 -18.6312,2.8662 -2.8664,0 -1.4332,-2.8662 -1.4332,-5.7327 1.4332,-10.0321 0,-20.0642 5.7327,-28.6632 2.8662,-4.2995 11.4652,2.8663 15.7647,0 2.8664,0 -5.7325,0 -5.7325,-2.8664 0,-4.2995 1.433,-8.599 2.8662,-12.8985 4.2995,-4.2995 14.3317,5.7327 17.198,0 2.8663,-4.2995 -1.4332,-12.8985 2.8663,-18.6312 4.2996,-5.7325 17.1981,-4.2994 20.0644,-12.8984 1.4332,-5.7327 -18.6312,-12.8985 -12.8985,-14.3317 10.0322,-2.8663 18.6312,14.3317 28.6632,11.4654 7.1659,-1.4332 -11.4652,-8.599 -15.7647,-15.7649 -7.1658,-10.0321 14.3317,-21.4975 25.7969,-28.6632 11.4654,-7.1659 15.7649,25.7969 30.0966,31.5296 8.599,2.8663 20.0643,0 25.797,-7.1659 2.8662,-4.2995 -20.0644,0 -17.198,-5.7326 4.2995,-8.5991 22.9305,-7.1659 27.2301,-15.7649 7.1658,-20.0642 -44.4282,32.9629 -31.5296,-21.4974 1.4331,-8.599 4.2995,-14.3317 5.7326,-17.198 -5.7326,4.2995 -11.4653,8.599 -18.6312,11.4653 -21.4975,10.0322 40.1286,-27.2302 35.8291,-32.9627 -1.433,-2.8665 -4.2995,-2.8665 -8.599,-1.4332 v -1.4333 c 0,-2.8662 -10.032,0 -8.5989,-2.8662 4.2994,-10.0322 48.7276,-7.1659 55.8935,-10.0322 2.8663,-2.8663 -2.8664,-10.0322 0,-12.8985 21.4974,-20.0644 50.1608,-7.1658 57.3266,-48.7276 0,-7.1659 -11.4653,7.1658 -17.198,10.0322 -7.1659,1.433 -14.3317,0 -21.4974,0 -5.7327,1.433 -15.7649,10.032 -15.7649,4.2995 2.8664,-18.6312 54.4603,-18.6312 40.1286,-42.9951 -1.4331,-4.2995 -14.3317,-1.4331 -24.3637,-1.4331 1.433,2.8663 4.2995,5.7326 7.1657,11.4653 -7.1657,7.1658 -12.8984,15.7648 -20.0642,22.9307 -2.8664,1.4332 -8.5991,10.0322 -10.0322,5.7327 -1.4332,-7.1659 -1.4332,-47.2946 10.0322,-48.7278 0,-4.2995 0,-7.1658 2.8663,-12.8985 4.2995,-11.4652 22.9306,-5.7325 32.9628,-11.4652 2.8663,-1.4332 -7.1659,0 -8.5991,-1.4332 -4.2995,-5.7327 -8.599,-12.8985 -10.032,-18.6312 -1.4333,-8.599 11.4652,-41.5617 20.0642,-41.5617 11.4654,-1.4332 17.198,17.198 28.6634,20.0642 4.2995,1.4332 -4.2995,-10.0322 -5.7327,-15.7647 -2.8663,-7.1659 -1.4332,-20.0644 -8.599,-24.3639 -7.1658,-4.2995 -20.0644,7.1659 -27.2302,1.4332 -22.9306,-18.6312 24.3639,-11.4654 31.5297,-14.3317 12.8985,-5.7327 14.3317,-24.3639 34.3959,-20.0644 8.599,2.8665 12.8985,14.3317 21.4976,14.3317 7.1658,0 -11.4654,-8.599 -12.8986,-14.3317 v -1.433 c -1.4331,0 -2.8663,1.433 -4.2995,1.433 -2.8663,0 0,-8.5989 -2.8663,-10.032 -14.3317,-2.8664 -24.3639,22.9307 -40.1286,21.4974 -50.1608,-7.1657 27.2301,-17.1979 35.8291,-24.3638 2.8663,-1.4331 -5.7327,-1.4331 -10.0322,-1.4331 -10.0322,-1.4332 -18.6311,2.8663 -28.6632,1.4331 -12.8985,-2.8663 -8.599,-25.797 -5.7327,-37.2622 10.0322,-53.0272 31.5297,-30.0966 67.3588,-44.4282 20.0642,-8.599 -22.9307,-22.9306 -28.6634,-32.9628 -2.8663,-5.7327 12.8985,-1.4332 18.6312,-4.2995 5.7327,-2.8663 -24.3637,-24.3639 11.4654,-12.8985 2.8663,0 -4.2995,5.7327 -4.2995,8.599 1.4331,20.0642 8.599,-10.0322 8.599,-12.8985 0,-1.4332 2.8663,-8.599 4.2995,-5.7327 4.2995,4.2995 -1.4332,17.198 5.7326,18.6312 11.4654,1.4332 27.2301,-4.2995 30.0965,-14.3317 4.2995,-10.0322 -15.7649,-14.3317 -17.1981,-24.3637 0,-1.4332 34.3961,-21.4976 45.8614,-15.7649 7.1659,2.8662 -7.1658,25.7969 1.4332,24.3638 8.599,0 -1.4332,-20.0643 5.7327,-24.3638 1.433,-1.4332 1.433,-1.4332 2.8663,-2.8663 -1.4333,-1.4332 -2.8663,-2.8664 -4.2995,-4.2995 -4.2995,-2.8664 8.5989,-11.4654 4.2995,-12.8985 -4.2995,-1.4332 -7.1658,8.599 -12.8985,11.4653 -14.3317,5.7327 -31.5297,17.1979 -45.8614,8.599 -5.7327,-4.2995 11.4655,-12.8985 15.7649,-20.0643 8.599,-18.6312 11.4653,-61.6262 32.9628,-45.8613 7.1659,5.7325 -21.4975,14.3317 -14.3317,20.0642 5.7327,5.7327 14.3317,-10.032 24.3639,-11.4652 7.1657,0 14.3316,2.8662 20.0643,8.5989 8.599,8.599 24.3638,40.1287 20.0643,28.6634 -2.8663,-10.0322 -17.198,-18.6312 -14.3317,-30.0966 1.4332,-10.0322 18.6312,-11.4652 24.3639,-20.0642 2.8663,-4.2995 -8.599,2.8663 -12.8985,4.2995 -21.4976,7.1658 -28.6634,17.1979 -38.6956,-10.0322 -5.7325,-14.3317 7.166,-5.7327 8.599,-12.8985 1.4332,-1.4332 -4.2995,1.4332 -4.2995,0 -4.2995,-12.8985 35.8292,7.1658 41.5619,0 10.0322,-17.1979 -34.3959,-31.5296 -37.2624,-31.5296 -2.8663,0 2.8665,5.7327 1.4332,8.5989 -4.2995,8.599 -28.6633,38.6955 -37.2623,34.396 -10.0321,-4.2995 -11.4653,-20.0643 -7.1658,-30.0965 2.8663,-8.5989 17.198,-2.8662 22.9307,-8.5989 4.2995,-4.2995 -12.8985,5.7327 -15.7649,1.4332 -5.7326,-8.599 18.6312,-14.3317 25.7971,-8.599 2.8663,1.4331 -2.8664,8.599 -1.4332,7.1658 22.9306,-22.9307 60.193,-8.599 57.3266,-40.1287 0,-4.2995 -8.599,4.2995 -12.8985,1.4332 -2.8663,-1.4332 1.4332,-7.1659 -1.4332,-8.5989 -1.4331,-1.4333 -1.4331,5.7325 -2.8663,5.7325 -5.7327,2.8664 -10.0322,1.4332 -14.3317,1.4332 -8.599,1.4332 -20.0642,8.599 -22.9307,1.4332 -18.6311,-37.2623 37.2624,-11.4654 38.6956,-11.4654 8.599,-7.1657 1.4331,-22.9306 5.7326,-32.9627 1.4332,-4.2996 4.2995,10.0321 5.7327,15.7648 1.4332,1.4332 5.7327,-1.4332 5.7327,0 0,8.5989 -5.7327,17.1979 -2.8664,24.3638 0,2.8663 31.5296,48.7277 38.6955,40.1287 17.198,-20.0644 -54.4603,-83.1237 -32.9628,-93.1559 7.1659,-4.2995 21.4974,7.1659 32.9628,17.1981 -1.4332,-4.2995 -1.4332,-8.5991 -4.2995,-11.4654 -7.1659,-5.7327 -28.6633,-2.8663 -25.7969,-12.8985 1.433,-11.4652 20.0642,-10.0322 31.5296,-11.4652 61.6261,-10.0322 10.0321,20.0642 48.7276,38.6954 27.2302,12.8985 -31.5296,-44.4281 -5.7327,-44.4281 7.1658,0 11.4653,8.5989 17.198,8.5989 8.599,-1.4332 21.4975,-1.4332 20.0644,-8.5989 -8.599,-60.1931 -44.4281,0 -58.7598,-2.8665 -2.8664,-1.433 5.7325,-4.2995 4.2995,-7.1657 0,-2.8663 -4.2995,-7.1658 -7.1659,-5.7327 -4.2995,1.4332 -4.2995,10.0322 -10.0321,10.0322 -8.599,1.4332 -22.9307,2.8662 -27.2301,-5.7328 -2.8665,-5.7325 -1.4333,-12.8984 1.433,-18.631 -7.1658,1.4331 -15.7647,2.8663 -20.0642,7.1658 -7.1659,7.1658 0,25.7969 -8.599,31.5296 -5.7327,4.2995 0,-17.1979 -5.7327,-21.4974 -7.1658,-4.2995 -15.7648,-2.8664 -22.9307,-1.4332 -5.7327,1.4332 -2.8663,12.8984 -7.1657,17.1979 -4.2995,4.2995 -10.0322,8.599 -14.3317,12.8985 -5.7327,5.7327 -10.0322,11.4654 -14.3317,17.198 v -5.7326 c 4.2995,-10.0322 12.8985,-20.0644 14.3317,-30.0966 1.4332,-5.7325 -5.7327,-12.8984 -2.8663,-18.631 7.1658,-7.1659 21.4974,-4.2995 28.6632,-11.4654 30.0965,-28.6632 -47.2944,-20.0643 -5.7327,-64.4924 30.0966,-34.3961 24.3639,30.0965 17.1981,61.6261 2.8663,-5.7327 7.1658,-10.0322 11.4653,-11.4654 2.8663,-1.4331 5.7327,8.599 7.1658,5.7327 4.2996,-7.1658 -2.8742,-75.9578 21.4976,-42.9949 12.8984,17.198 17.1979,30.0964 17.1979,37.2622 5.7327,-2.8663 10.0322,-5.7326 14.3317,-5.7326 5.7326,0 8.599,11.4653 14.3315,11.4653 11.4655,1.4332 24.3639,-1.4332 34.3961,-7.1658 14.3317,-8.5989 -30.0966,2.8663 -32.9628,-15.7649 0,-5.7325 7.1659,-12.8984 14.3316,-12.8984 h 2.8663 c -1.433,-1.4332 -2.8663,-1.4332 -2.8663,-4.2995 0,-8.599 17.198,-5.7327 24.3638,-11.4653 1.4332,-1.4332 -5.7326,-1.4332 -7.1658,-2.8664 -10.0322,-7.1658 -14.3317,-22.9307 -11.4653,-35.8292 1.4331,-11.4652 30.0965,-1.433 32.9628,-12.8984 2.8664,-8.599 -4.2995,-17.198 -7.1658,-25.797 0,10.0322 -1.4332,15.7648 -2.8663,17.198 -10.0322,0 -20.0644,-8.599 -28.6634,-2.8663 -14.3316,5.7326 -17.1979,24.3637 -25.7969,37.2622 -7.1659,-4.2995 -20.0644,-4.2995 -20.0644,-11.4652 2.8664,-11.4653 21.4975,-12.8985 24.3639,-24.3639 2.8663,-5.7326 -15.7649,7.1659 -17.198,1.4332 -2.8664,-8.599 12.8985,-15.7648 11.4653,-25.797 0,-5.7327 -20.0644,1.4333 -15.7648,-1.4332 20.0643,-10.0322 53.0271,-1.4332 38.6955,-20.0642 -5.7328,-5.7327 -27.2302,-2.8664 -21.4975,-7.1659 7.1658,-4.2995 18.6312,8.599 27.2302,4.2995 8.5989,-5.7326 4.2994,-24.3638 12.8984,-27.2302 14.3317,-4.2995 17.198,22.9307 18.6312,45.8613 0,-8.5989 2.8663,-15.7647 5.7326,-22.9306 7.1659,-11.4653 10.0322,28.6633 20.0644,34.396 10.0321,5.7326 22.9306,0 32.9627,-4.2994 1.4332,0 -42.9949,-15.7649 -40.1287,-25.7971 2.8665,-10.0321 12.8987,-22.9307 22.9307,-21.4975 10.0322,0 8.599,18.6312 15.7649,25.797 7.1658,7.1659 17.198,5.7327 25.797,12.8984 4.2994,4.2995 8.5989,22.9307 11.4652,17.1981 11.4654,-34.396 -73.0913,-25.7971 -41.5617,-93.1558 5.7326,-11.4653 30.0965,-25.797 41.5617,-17.198 10.0322,5.7327 -2.8663,73.0915 -2.8663,75.9578 2.8663,2.8664 7.1658,-4.2995 8.599,-8.599 1.4332,-8.599 -5.7327,-20.0643 0,-27.2302 4.2995,-7.1657 22.9307,4.2995 24.3639,-4.2995 0,-8.599 -20.0644,-7.1657 -22.9307,-15.7647 -2.8664,-7.1659 11.4653,-68.792 32.9627,-42.9951 5.7328,8.599 0,88.8564 0,91.7227 -2.8662,10.0322 -21.4974,27.2302 -11.4652,30.0964 25.7969,8.599 12.8985,-41.5618 21.4974,-47.2944 8.599,-5.7327 21.4975,0 30.0966,-4.2995 4.2995,-2.8664 -7.1659,-5.7327 -10.0322,-8.5989 -10.0322,-8.599 -31.5296,-20.0644 -22.9306,-40.1287 37.2623,-83.1237 0,11.4653 42.9949,-17.1981 11.4654,-7.1658 0,-28.6632 8.599,-38.6954 2.8664,-2.8663 7.1659,1.4332 10.0322,4.2995 2.8662,2.8663 1.4332,8.599 4.2994,11.4654 7.166,5.7325 15.7649,7.1658 22.9307,12.8985 5.7327,4.2995 10.0322,12.8984 17.198,15.7647 7.1659,1.4332 -12.8985,-10.0322 -15.7648,-17.1979 0,-1.4333 5.7326,-1.4333 5.7326,-4.2995 2.8664,-20.0644 -1.4331,-50.1609 -4.2995,-48.7277 -7.1658,0 -48.7276,35.8292 -60.1929,-5.7327 -5.7327,-22.9306 35.8292,-32.9628 47.2944,-30.0964 10.0322,1.4331 15.7649,14.3317 25.7971,17.198 2.8663,0 -1.4332,-8.599 1.4331,-10.0322 2.8664,-2.8663 8.599,0 11.4654,1.4332 8.599,0 18.631,-1.4332 25.7969,1.4332 18.6312,5.7326 -5.7327,47.2944 32.9629,44.4281 7.1658,-1.4332 11.4653,-11.4654 10.032,-18.6312 0,-4.2994 -8.5989,1.4331 -12.8984,0 -4.2995,-1.4332 -11.4653,-2.8664 -11.4653,-7.1659 0,-5.7325 17.198,-2.8663 11.4653,-12.8984 -5.7327,-8.599 -7.1658,-17.198 -1.4332,-44.4282 v -2.8663 c -2.8663,5.7328 -7.1658,11.4653 -11.4653,18.6312 -7.1658,-7.1659 -20.0642,-11.4654 -22.9307,-20.0644 -7.1657,-25.7969 78.8242,-87.423 45.8614,-18.6311 12.8984,-11.4653 31.5296,1.4332 35.8291,-2.8663 7.1658,-4.2995 -15.7649,-4.2995 -18.6312,-11.4653 -10.0322,-24.3639 32.9629,-20.0644 35.8292,-24.3639 5.7327,-12.8984 -38.6955,-28.6633 -38.6955,-32.9628 1.4331,-8.599 21.4975,11.4654 24.3638,4.2995 5.7327,-10.0321 -12.8985,-21.4975 -10.0321,-32.9628 4.2995,-11.4654 83.1236,-4.2994 84.5568,0 2.8663,4.2995 -35.8291,74.5246 -34.3961,78.8241 0,1.4332 1.4332,1.4332 2.8664,1.4332 -2.8664,0 -4.2994,0 -4.2994,1.4331 2.8662,4.2996 11.4652,-2.8663 15.7647,0 18.6312,12.8986 -24.3637,18.6312 0,25.7971 8.599,2.8663 -5.7327,15.7647 -7.1658,24.3637 0,2.8664 -2.8664,44.4281 21.4975,20.0644 4.2995,-4.2995 0,-12.8985 1.4332,-18.6312 1.4331,-8.599 5.7326,-14.3317 8.599,-21.4974 2.8663,-8.599 -12.8985,1.433 -8.599,-11.4654 8.599,-24.3638 31.5297,-118.9527 47.2944,-127.5517 0,0 10.0322,37.2622 10.0322,40.1286 -2.8664,8.599 -17.198,12.8985 -15.7649,22.9307 1.4332,5.7326 14.3317,-10.0322 18.6312,-4.2995 5.7327,7.1658 -5.7327,25.7969 2.8664,30.0964 10.0321,7.1658 24.3637,0 32.9627,-7.1657 5.7327,-5.7327 -12.8985,-10.0322 -12.8985,-17.1981 0,-10.0321 -4.2994,-22.9306 1.4333,-31.5297 4.2995,-10.032 18.6311,-11.4653 25.7969,-20.0642 2.8663,-2.8663 -7.1658,0 -10.0322,-1.4332 -10.0322,-4.2995 -27.2301,-7.1658 -27.2301,-18.6312 -1.4331,-8.599 21.4974,1.4332 25.797,-5.7326 5.7326,-10.0322 -7.1659,-21.4976 -4.2996,-31.5296 1.4332,-7.1659 11.4654,14.3317 18.6312,10.032 11.4654,-4.2995 20.0644,-14.3315 42.995,1.4332 17.198,11.4654 -30.0965,73.0915 -34.396,81.6905 -1.4331,2.8663 8.599,-4.2995 10.0322,-7.1658 8.5989,-7.1659 12.8984,-22.9306 22.9306,-25.7971 7.1658,-1.433 2.8663,12.8985 1.4332,20.0644 -4.2994,10.0322 -21.4974,20.0643 -15.7649,30.0965 5.7327,8.599 20.0644,-17.198 28.6634,-11.4653 1.4332,1.4331 2.8663,2.8663 2.8663,4.2995 2.8664,-1.4332 8.599,2.8663 11.4654,1.4331 8.599,-8.599 -12.8985,-15.7648 -12.8985,-15.7648 -1.4332,-5.7327 -2.8664,-54.4603 2.8663,-63.0593 25.797,-32.9628 42.9949,30.0965 44.4281,30.0965 93.1558,4.2994 -47.2944,-55.8934 44.4282,-27.2302 47.2944,14.3317 12.8984,15.7649 5.7327,34.3961 -1.4332,2.8662 7.1657,1.433 10.0322,0 8.5989,-2.8665 15.7647,-10.0322 24.3637,-11.4654 2.8663,0 0,5.7327 1.4332,7.1659 4.2995,2.8663 10.0322,2.8663 14.3317,4.2995 1.4331,1.433 2.8663,4.2995 5.7325,5.7325 7.166,5.7327 18.6312,10.0322 24.3639,18.6312 1.4332,2.8664 -5.7327,2.8664 -8.599,5.7327 -7.1659,4.2995 -17.1979,5.7327 -21.4974,12.8985 -8.5991,11.4652 -1.4332,31.5296 -12.8986,37.2623 -31.5297,15.7648 -70.2251,-5.7327 -96.022,22.9307 -4.2995,2.8663 8.5989,-4.2995 14.3316,-4.2995 7.1658,0 12.8985,1.4331 20.0643,2.8663 1.4332,1.4332 2.8664,2.8663 4.2995,2.8663 11.4654,1.4332 27.2301,-7.1658 34.396,2.8664 5.7326,7.1658 -17.1981,17.198 -12.8986,25.7969 4.2995,7.1658 10.0322,-20.0643 18.6312,-18.6311 10.0322,2.8662 7.1659,25.7969 2.8664,40.1286 1.4331,-2.8663 2.8663,-4.2995 4.2995,-5.7327 10.0321,-4.2995 24.3638,2.8664 35.8292,-1.4331 2.8662,-1.4332 -8.599,-5.7327 -8.599,-10.0322 -1.4332,-10.0322 0,-18.6311 2.8663,-27.2301 1.4332,-2.8663 -1.4333,8.599 2.8662,8.599 11.4654,2.8662 22.9307,0 34.3961,-1.4333 z m -614.8281,295.2322 h 1.4332 z m 260.8362,-438.5487 c 1.433,11.4654 -18.6312,14.3317 -25.7971,22.9306 -57.3266,-55.8933 20.0644,-55.8933 25.7971,-22.9306 z m -217.8413,111.7869 c 5.7327,0 8.599,10.0322 12.8985,7.1659 5.7327,-4.2995 -10.0321,-20.0644 -4.2995,-18.6312 2.8664,1.4332 2.8664,8.599 7.1659,8.599 2.8663,-1.4332 -2.8664,-8.599 0,-10.0322 12.8985,-5.7325 7.1658,30.0966 20.0642,11.4654 2.8665,-2.8664 -7.1657,-8.599 -4.2994,-11.4654 8.5989,-11.4652 22.9306,-15.7647 35.8291,-24.3637 0,1.4331 -1.4331,1.4331 -1.4331,1.4331 0,1.4332 5.7326,1.4332 5.7326,2.8664 -1.4331,5.7327 -7.1658,11.4652 -10.0321,17.1979 -11.4654,31.5297 -27.2301,44.4282 -61.6262,50.1609 2.8664,-8.599 12.8985,-18.6312 8.599,-27.2302 -1.4331,-1.4332 -4.2995,-2.8664 -7.1658,-2.8664 0,-1.4331 -2.8663,-4.2995 -1.4332,-4.2995 z m -10.0321,5.7327 h -5.7327 c -2.8662,-1.4332 0,0 5.7327,0 z m -85.9899,101.7547 c 2.8663,-5.7325 11.4652,-8.599 18.6312,-11.4652 1.433,-1.4333 5.7325,0 4.2994,1.4332 -4.2994,11.4652 -11.4654,21.4974 -17.1979,31.5296 -1.4332,-7.1659 -8.5991,-14.3317 -5.7327,-21.4976 z m -27.2302,35.8292 1.4332,2.8664 c 0,1.4331 -1.4332,2.8663 -1.4332,4.2995 0,-2.8664 -1.4332,-4.2995 0,-7.1659 z m -75.9578,27.2301 c 11.4653,-31.5296 126.1186,-24.3637 27.2302,21.4976 -7.1659,-8.5991 -30.0966,-11.4654 -27.2302,-21.4976 z m -199.2101,233.6061 c 2.8665,2.8664 -5.7325,5.7327 -4.2994,10.0321 2.8664,15.7648 40.1286,14.3317 -11.4653,35.8292 -5.7327,1.4332 -11.4654,-5.7327 -18.6312,-8.599 37.2624,-30.0964 15.7648,-34.3959 15.7648,-25.7969 1.4332,17.1979 -17.198,5.7325 -25.797,11.4652 -7.1658,2.8663 -12.8985,27.2302 -14.3317,18.6312 -4.2995,-24.3639 32.9629,-65.9256 58.7598,-41.5618 z m -114.6532,130.418 c 2.8663,-8.5989 -2.8662,-24.3637 5.7328,-25.7969 53.0271,-10.0322 5.7325,40.1286 -11.4655,44.4281 0,-1.4332 -2.8662,-1.4332 -2.8662,-2.8663 2.8662,-5.7327 7.1657,-10.0322 8.5989,-15.7649 z m -353.9919,818.3376 c 7.1658,1.4332 2.8663,11.4654 4.2995,18.6312 -1.4332,1.4332 -4.2995,2.8664 -5.7327,2.8664 -5.7326,1.4331 -12.8985,1.4331 -18.6312,2.8663 -60.1928,8.599 18.6312,-25.797 20.0644,-24.3639 z m -266.5689,268.0021 c 5.7327,-2.8664 12.8986,0 20.0644,1.4331 0,-1.4331 0,-2.8663 1.4332,-2.8663 0,-1.4332 1.4331,1.4332 1.4331,2.8663 -1.4331,1.4332 -2.8663,1.4332 -4.2995,2.8664 l 1.4332,-1.4332 c -5.7327,2.8663 -11.4652,10.0322 -17.198,10.0322 -4.2994,0 -5.7327,-10.0322 -2.8664,-12.8985 z m -93.1557,550.3356 2.8664,-2.8664 c 2.8663,14.3317 8.599,30.0966 1.4331,44.4281 -15.7648,28.6634 -7.1658,-24.3637 -4.2995,-41.5617 z m 22.9307,-106.0543 v -1.4331 -1.4332 c 2.8664,2.8663 7.1659,4.2995 7.1659,7.1658 8.5989,61.6262 -22.9307,17.1981 -10.0322,-2.8663 0,0 0,1.4332 -1.4332,2.8663 1.4332,-1.4331 4.2995,-2.8663 4.2995,-4.2995 z m 550.3356,-908.6271 c -5.7326,1.4332 -10.0321,2.8665 -14.3317,2.8665 -10.0321,-1.4333 2.8664,-1.4333 14.3317,-2.8665 z M 7145.0097,1391.274 c -2.8663,1.4332 -7.1658,1.4332 -10.0321,1.4332 4.2995,0 12.8985,-2.8664 10.0321,-1.4332 z m -242.2049,322.4623 h 1.4331 z m 4.2995,1.4332 h 2.8663 z m 4.2995,0 c 0,1.4331 0,1.4331 1.4331,1.4331 -1.4331,0 -1.4331,0 -1.4331,-1.4331 z"
+ class="europe"
+ id="no"
+ sodipodi:nodetypes="cccccccccsccccccccccccccscccccccccccccscccccsccccccccccccccccccccccssccccccccccccsccccccsccccscccccccscscscccccccccsccscccssccccscccccccccccccccccccccccccsccccccccccccccccccscccccccsccccccscccccccccscccccsscccccscccccccccccccccscccccccsccscscscscscccccscscccccccccscccccccccccccccccccccccscccccccccccccccsccccccccccccccscsccccsccccscsccsccccsccsccccccsccccccccccccccccccccscccsccsccccccccsccscccccccccsccccccsccssccccssccsscscccccccccsccccccccsccccccscscccsscccccccccccscccscscccccccccccccccccccscscsccccccccccccscccccsccccccccccccccsscccccccccccccccccccscccccccccsccccccccsccccccccccccccccccccscscccccccccccccccccccccccccccscccccccccccccscccccccccccccccccccccccccccccccccccccccscccccscccccscccscsccccccccccc" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 9710.3764,5187.7299 v -1.4331 c -11.4654,-12.8985 -4.2995,-17.198 -1.4332,-30.0966 2.8663,-15.7647 -17.198,4.2996 -17.198,-4.2995 1.4331,-10.032 -7.1659,-44.4281 -2.8664,-53.0271 1.4332,0 42.995,-38.6954 42.995,-38.6954 2.8663,-5.7327 -4.2994,-12.8985 -4.2994,-18.6312 -1.4333,-5.7327 -2.8665,-12.8985 1.433,-18.6312 21.4976,-25.7969 88.8564,-14.3317 108.9207,-44.4281 4.2994,-5.7327 -2.8663,-14.3317 -5.7327,-21.4974 -1.4331,-2.8663 -7.1658,-4.2995 -7.1658,-7.1658 0,-4.2995 8.599,-4.2995 8.599,-7.1659 0,-4.2995 -5.7327,-7.1658 -7.1658,-11.4653 -1.4332,-7.1659 0,-15.7649 1.4331,-24.3639 0,-2.8663 4.2995,-5.7327 4.2995,-8.599 5.7327,-17.1979 -20.0643,-1.4332 -20.0643,-1.4332 -4.2995,-2.8662 4.2995,-10.032 2.8663,-15.7647 -5.7326,-10.0322 -14.3317,-20.0644 -21.4975,-28.6634 -10.0322,-10.032 -25.7969,14.3317 -27.2301,-1.4331 -7.1658,-45.8613 -2.8663,-32.9628 22.9306,-44.4281 1.4333,-1.4332 12.8985,-25.7971 4.2995,-22.9307 -20.0642,4.2995 -30.0964,18.6312 -47.2944,4.2995 -18.6311,-15.7649 14.3316,-20.0644 18.6311,-27.2301 4.2996,-4.2995 -1.4331,-11.4654 0,-15.7649 4.2996,-18.6312 21.4976,-28.6632 4.2996,-47.2944 -5.7327,-7.1659 -15.7649,-8.599 -21.4976,-14.3317 -7.1658,-8.599 10.0322,-27.2302 -1.4331,-28.6634 -27.2301,-1.433 -10.0322,21.4976 -31.5296,25.7971 -73.0915,11.4653 18.6312,-10.0322 -40.1287,-20.0644 -18.6311,-2.8663 -20.0643,18.6312 -35.8291,8.599 -5.7327,-2.8663 -10.0322,-10.032 -14.3317,-14.3317 -12.8985,-8.599 -7.1659,24.3639 -22.9307,24.3639 -10.0321,0 -20.0643,-2.8663 -30.0964,-7.1658 -4.2995,-2.8664 -7.1659,-12.8984 -12.8985,-11.4654 -7.1659,0 -10.0322,14.3317 -18.6311,14.3317 -10.0322,0 -17.198,-10.0322 -28.6634,-11.4653 -37.2624,-5.7327 18.6312,50.1607 -20.0643,42.995 -10.0322,-1.4333 -12.8986,-14.3317 -21.4976,-18.6312 -5.7325,-2.8663 -14.3315,1.4332 -21.4974,-1.4332 -21.4975,-7.1658 -37.2622,-34.396 -55.8934,-48.7276 -5.7327,-4.2995 -58.7598,50.1608 -77.391,75.9577 -5.7327,8.599 -20.0644,-7.1657 -30.0964,-8.599 -5.7327,0 -5.7327,8.599 -10.0322,11.4655 -7.1658,4.2994 -12.8985,-12.8985 -21.4975,-15.7649 -34.3961,-8.599 -41.5618,41.5618 -65.9257,28.6633 -10.0321,-4.2995 -18.631,-10.0322 -25.797,-17.1979 -5.7327,-5.7327 -11.4652,-12.8985 -12.8984,-21.4976 0,-4.2995 8.599,-5.7326 7.1657,-10.0321 0,-5.7327 -37.2622,-38.6956 -38.6954,-42.995 -1.4332,-4.2995 7.1659,-11.4653 4.2995,-14.3316 -1.4332,-1.4332 -4.2995,-1.4332 -5.7327,-1.4332 -5.7325,1.4332 -10.032,5.7327 -15.7647,5.7327 -10.0322,1.4331 -11.4653,-15.7649 -17.198,-22.9307 -1.4332,-2.8664 -4.2995,-2.8664 -7.1659,-1.4332 -5.7326,2.8663 -35.8292,21.4975 -41.5617,21.4975 -2.8664,1.4332 -2.8664,-7.1658 -7.1659,-7.1658 -10.0321,0 -20.0643,1.4331 -28.6633,4.2995 -2.8664,1.4332 -11.4654,21.4975 -18.6311,7.1658 -1.4333,-2.8663 4.2994,-5.7326 4.2994,-8.599 1.4333,-4.2995 1.4333,-10.0322 -1.4332,-12.8985 -4.2995,-4.2995 -10.0322,-1.4332 -14.3316,-5.7327 -7.1658,-4.2995 1.4332,-18.6312 -4.2995,-24.3637 -5.7326,-7.1659 -25.797,-11.4654 -21.4975,-20.0644 5.7327,-7.1658 38.6956,-14.3317 30.0964,-27.2302 -11.4652,-17.1979 -38.6954,-15.7647 -47.2944,-22.9306 -5.7327,-4.2995 -10.0322,-10.0321 -11.4652,-15.7648 -2.8664,-5.7327 1.433,-12.8985 -1.4332,-15.7649 -5.7327,-5.7326 -14.3317,-5.7326 -20.0644,-10.032 -8.599,-5.7327 -12.8985,-17.198 -22.9307,-18.6312 -11.4653,-2.8663 -15.7647,22.9306 -28.6632,22.9306 -4.2995,0 -2.8663,-10.0321 -7.1659,-10.0321 -55.8934,-4.2995 -21.4975,27.2301 -54.4602,41.5618 0,0 -61.6262,4.2995 -63.0593,4.2995 -2.8664,0 7.1658,42.9949 -5.7327,55.8934 -8.599,7.1659 -18.6312,14.3317 -30.0965,15.7649 -4.2994,0 -2.8664,-8.599 -7.1659,-11.4654 -2.8662,-1.4331 -7.1657,5.7327 -10.032,4.2995 -15.7649,4.2995 -34.3961,2.8664 -47.2946,11.4654 -5.7327,2.8662 -2.8662,12.8984 -8.599,15.7647 -8.5989,4.2995 -20.0642,0 -30.0964,2.8664 -5.7327,1.4331 -15.7649,15.7648 -22.9307,55.8934 -1.4332,10.0322 -4.2994,37.2624 -4.2994,48.7276 0,15.7649 57.3265,54.4604 21.4974,61.6263 -37.2623,8.599 -20.0642,-27.2302 -53.0271,-30.0966 -65.9256,-4.2995 12.8985,11.4654 -34.3959,20.0644 -7.1659,1.4331 -14.3317,-8.599 -22.9307,-7.1659 -22.9307,5.7327 -10.0322,30.0966 -30.0966,35.8291 -11.4652,2.8664 -22.9305,-37.2622 -34.3959,-42.9949 -11.4653,-5.7327 -24.3637,14.3317 -32.9629,25.7969 -5.7325,7.166 5.7327,28.6634 -2.8663,27.2302 -30.0964,-7.1658 14.3317,1.4332 -22.9306,-24.3637 -20.0643,-14.3317 -12.8985,14.3315 -25.797,14.3315 -35.8291,0 -4.2995,-12.8985 -17.1979,-21.4974 -8.599,-4.2995 -8.599,17.1979 -14.3317,22.9306 -8.599,10.0322 -21.4975,-2.8663 -28.6634,-1.4332 -7.1658,0 0,25.7971 -7.1658,30.0966 -1.4332,2.8663 -5.7327,-1.4332 -8.5989,-4.2995 -18.6312,-10.0322 32.9628,-34.3961 -24.3639,-18.6312 -17.198,4.2995 8.599,27.2302 -27.2302,12.8985 -4.2995,-1.4332 4.2995,-10.0322 1.4332,-12.8985 -2.8663,-2.8664 -8.599,4.2995 -11.4652,2.8663 -8.599,-7.1658 2.8662,-21.4974 -12.8985,-17.198 -32.9629,8.599 -77.391,12.8985 -113.2201,8.599 -4.2995,-1.4332 -5.7327,-7.1657 -10.0322,-7.1657 -20.0643,-1.4333 -40.1287,11.4652 -61.6261,14.3315 -7.1659,0 -14.3317,0 -21.4975,0 -7.1659,0 -14.3317,-2.8663 -21.4976,-1.4331 -18.631,5.7326 -31.5296,20.0643 -48.7276,27.2302 -11.4653,4.2995 -25.7969,1.4331 -37.2622,7.1658 -5.7327,4.2995 -25.7971,71.6582 -40.1286,67.3588 -4.2995,-1.4333 -4.2995,-8.599 -7.166,-8.599 -8.5989,-1.4332 -18.6311,-1.4332 -25.7969,1.4332 -22.9307,7.1658 11.4653,20.0642 -5.7327,24.3637 l -1.4331,1.4332 c 0,0 0,-1.4332 0,-1.4332 4.2995,5.7327 10.0321,10.0322 12.8985,15.7649 2.8663,8.599 -2.8664,18.6312 0,25.797 12.8985,27.2301 54.4603,54.4603 77.3908,71.6582 4.2995,4.2995 -28.6632,2.8663 -21.4974,17.198 1.4332,1.4332 30.0964,27.2302 30.0964,38.6954 -4.2995,51.5941 -20.0642,20.0644 -45.8612,41.5619 -11.4654,10.0322 -98.8884,171.9799 -94.5889,183.4452 2.8663,15.7649 17.1979,27.2301 21.4974,41.5618 1.4332,7.1658 -10.0322,17.198 -4.2995,22.9307 24.3639,30.0964 10.0322,-27.2302 27.2302,27.2302 1.4332,2.8662 -4.2995,-4.2995 -7.1658,-5.7327 -7.1659,-1.4333 -14.3317,1.4332 -21.4976,1.4332 -4.2995,-1.4332 -10.032,-1.4332 -14.3315,-2.8665 1.4331,14.3317 -8.5991,34.3961 -10.0322,48.7277 -1.4332,10.0321 0,20.0643 -4.2995,28.6633 -4.2995,10.0322 -17.198,12.8985 -18.6312,22.9307 -2.8662,10.0322 4.2995,20.0642 4.2995,31.5296 32.9629,1.4333 11.4653,17.198 34.396,25.797 4.2994,1.4332 10.0321,-5.7327 14.3316,-2.8663 8.599,5.7326 10.0322,20.0643 18.6312,25.797 5.7327,1.4332 10.0322,-5.7327 15.7648,-7.1658 4.2995,0 8.599,2.8663 11.4654,4.2995 18.6312,12.8985 -4.2995,10.032 12.8984,24.3637 4.2995,2.8664 12.8985,-2.8663 22.9307,-30.0964 1.4331,-2.8663 1.4331,-7.1658 4.2995,-7.1658 4.2995,0 40.1286,17.198 44.4281,17.198 2.8663,0 4.2995,-7.1659 7.1658,-8.599 18.6312,-7.1659 65.9257,11.4653 88.8563,5.7326 12.8986,-4.2995 21.4976,-15.7648 34.396,-17.198 22.9307,-1.4332 38.6954,28.6634 54.4603,32.9628 21.4975,7.1658 28.6633,-32.9628 34.396,-38.6955 25.7969,-27.23 91.7225,-24.3638 111.7869,-44.4281 38.6954,-37.2623 -17.198,-5.7326 10.0322,-41.5617 7.1658,-8.599 18.6312,-15.7649 30.0964,-18.6312 l 1.4332,-4.2995 5.7326,-12.8985 c 0,-1.4332 0,-2.8664 0,-2.8664 l 1.4332,-5.7326 14.3317,2.8663 c -1.4332,-2.8663 -4.2995,-5.7327 -4.2995,-8.599 0,-1.4332 54.4603,-8.599 57.3266,-10.0322 27.2302,-12.8984 18.6312,-34.3959 57.3266,-31.5296 15.7649,1.4332 32.9629,7.1659 45.8613,17.1979 4.2995,2.8665 0,12.8985 4.2995,12.8985 10.0322,0 20.0644,-10.032 31.5297,-11.4652 5.7327,-1.4333 -1.4332,12.8984 0,18.6311 2.8663,11.4653 10.0322,-10.0322 12.8985,-7.1659 4.2994,5.7327 4.2994,12.8986 8.5989,17.1981 5.7327,5.7326 0,-18.6311 8.599,-21.4976 48.7276,-12.8984 27.2302,15.7649 61.6263,30.0966 14.3315,5.7326 4.2994,-22.9307 17.1979,-15.7649 32.9627,21.4975 10.0321,51.5941 20.0643,83.1237 1.4332,7.1658 37.2623,32.9627 45.8613,35.8291 17.198,5.7326 -4.2995,-25.797 15.7648,-14.3316 25.797,14.3316 -7.1658,54.4603 11.4654,42.9949 7.1657,-4.2995 2.8662,18.6312 8.5989,24.3639 8.599,5.7325 44.4282,7.1657 53.0272,14.3316 l 14.3316,44.4282 c 0,2.8662 -8.5989,2.8662 -7.1657,5.7325 2.8662,10.0322 35.8291,14.3317 35.8291,15.7649 -8.5991,5.7327 -18.6312,7.1658 -25.7971,12.8985 -2.8663,2.8663 -1.4331,5.7327 -2.8663,8.599 -7.1657,7.1658 -14.3316,-20.0643 -25.7969,-20.0643 -8.599,0 0,27.2301 -8.599,25.797 -5.7327,-1.4332 -24.3639,-15.7649 -24.3639,-15.7649 -2.8663,4.2995 -1.4332,12.8985 -7.1657,14.3317 -2.8665,1.4332 -4.2995,-10.0322 -5.7327,-7.1658 -7.1658,5.7326 -5.7328,15.7648 -10.0322,21.4975 -1.4331,1.4332 -4.2995,0 -5.7327,-1.4333 0,-2.8662 2.8664,-4.2994 2.8664,-7.1657 -1.4332,-7.1659 -4.2995,-14.3317 -7.1659,-20.0644 -5.7326,-10.0321 -18.6311,11.4654 -25.797,20.0644 -20.0642,22.9306 15.7649,34.3959 18.6312,45.8613 1.4332,7.1658 -5.7327,14.3316 -5.7327,21.4975 0,7.1657 8.599,12.8984 5.7327,18.6311 -2.8663,5.7326 -12.8985,5.7326 -18.6312,11.4653 -12.8985,10.0322 7.1659,15.7649 0,25.797 -5.7327,7.1659 -14.3317,11.4654 -18.6312,17.1979 -12.8984,20.0644 8.599,51.5941 -1.433,53.0273 -7.1659,1.433 -14.3317,-1.4332 -21.4976,0 h 1.4332 l -7.1658,10.032 c 0,4.2995 -1.4332,8.599 -4.2995,7.1659 2.8663,1.4331 7.1658,-1.4332 10.0321,1.4331 4.2996,4.2995 2.8664,12.8986 8.5991,17.1981 5.7326,5.7326 67.3588,8.599 65.9256,1.4331 0,-1.4331 -20.0644,-5.7326 -5.7327,-14.3317 8.599,-4.2995 18.6312,-2.8663 27.2301,-7.1658 14.3317,-7.1657 20.0644,-27.2301 35.8292,-32.9628 31.5296,-11.4653 47.2944,0 54.4603,27.2301 2.8663,-8.599 14.3317,-41.5617 -2.8663,-47.2944 -2.8664,-1.4332 1.4331,5.7327 -1.4332,5.7327 -1.4332,1.4331 -1.4332,-4.2995 -2.8664,-5.7327 -8.599,-2.8663 -2.8663,22.9307 -12.8985,4.2995 -8.599,-12.8985 -7.1658,-32.9629 -8.599,-47.2944 0,-5.7327 -10.032,-20.0644 -4.2995,-17.1981 12.8985,4.2995 10.0322,37.2623 22.9307,32.9629 42.9951,-17.198 14.3317,-21.4975 14.3317,-40.1287 0,-2.8663 2.8663,5.7327 5.7327,4.2995 4.2995,-1.4332 -7.1659,-12.8985 -2.8664,-15.7647 7.1659,-2.8665 15.7649,2.8662 22.9306,0 5.7327,-1.4333 10.0322,-7.166 12.8985,-12.8987 11.4654,-25.7969 24.3639,-57.3266 32.9629,-85.9898 1.4332,-5.7327 -10.0322,-11.4654 -7.1658,-17.1981 1.4331,-4.2995 10.0321,-5.7326 12.8985,-10.032 10.032,-18.6312 -18.6312,-15.7649 -7.1659,-31.5297 31.5296,-40.1286 74.5247,-22.9307 74.5247,-35.8291 1.433,-11.4654 -14.3317,-27.2302 -4.2995,-34.3961 7.1657,-5.7326 2.8663,22.9307 12.8984,28.6634 4.2995,4.2995 10.0321,-7.1658 17.198,-8.599 7.1658,-1.4332 15.7648,0 24.3638,-1.4332 18.6311,-2.8663 -4.2995,-34.396 -4.2995,-34.396 -1.4331,-17.1979 12.8984,-14.3316 4.2995,-28.6633 4.2994,0 8.5989,-1.4331 8.5989,1.4332 1.4332,83.1235 48.7278,55.8935 93.1559,25.7969 5.7326,-4.2995 -12.8985,10.0322 -18.6312,15.7649 -7.1659,8.599 2.8663,24.3638 -10.0322,25.797 -24.3637,4.2995 -51.5938,5.7327 -75.9577,8.599 -1.4332,0 -37.2624,0 -32.9629,4.2994 8.599,7.1658 17.1981,17.198 28.6634,20.0644 4.2995,1.4331 -2.8663,-11.4654 1.4332,-12.8986 8.599,-4.2995 18.6312,1.4332 28.6632,0 2.8664,0 14.3317,-7.1658 15.7649,1.4332 2.8663,14.3317 -32.9628,24.3639 -22.9307,34.396 4.2995,5.7327 54.4604,-5.7326 61.6261,-4.2995 4.2995,1.4332 0,11.4654 4.2995,12.8986 10.0322,4.2995 22.9307,5.7325 34.396,1.4331 11.4654,-2.8663 17.1981,-17.198 27.2301,-21.4975 10.0322,-5.7327 22.9307,-2.8663 31.5297,-7.1658 2.8664,-1.4332 -2.8663,-8.599 0,-10.0322 2.8664,-2.8663 8.599,4.2995 11.4654,2.8663 7.1657,-2.8663 2.8663,-22.9307 10.0322,-20.0642 12.8984,2.8662 24.3637,30.0964 34.3959,21.4974 7.1658,-7.1658 5.7327,-20.0644 14.3317,-25.7969 24.3637,-14.3317 -1.4332,24.3637 14.3315,38.6954 5.7327,4.2995 20.0644,-12.8985 18.6312,-5.7327 0,4.2995 -44.4281,50.1608 -53.0271,54.4603 -4.2995,1.4332 -8.599,-5.7327 -12.8985,-2.8663 -1.4332,2.8663 7.1658,5.7327 5.7327,8.599 0,0 -30.0966,27.2302 -31.5296,27.2302 -2.8664,2.8663 -5.7327,7.1657 -7.1659,11.4652 0,2.8665 4.2995,4.2995 4.2995,5.7327 -1.4331,1.4333 -53.0272,37.2624 -34.396,44.4282 35.8292,14.3316 28.6634,-10.0322 58.7598,-10.0322 10.0322,1.4332 41.5619,27.2301 51.5939,22.9306 12.8986,-4.2995 22.9307,-20.0642 40.1288,-2.8662 38.6954,40.1286 5.7326,34.3959 12.8985,57.3266 7.1658,20.0643 17.1979,27.2301 15.7647,28.6633 -5.7327,5.7326 -25.7969,5.7326 -22.9306,14.3316 4.2995,10.0322 22.9306,4.2995 32.9628,8.599 10.0322,2.8664 10.0322,12.8986 22.9307,12.8986 71.6583,-8.599 57.3266,-91.7227 103.1879,-123.2524 8.599,-7.1657 17.198,-12.8984 28.6633,-14.3316 5.7327,0 12.8984,7.1659 17.1979,4.2995 10.0322,-10.0321 1.4332,-30.0965 11.4654,-38.6955 10.0322,-7.1657 15.7648,-4.2995 18.6312,-17.1979 1.4331,-1.4332 -2.8664,0 -4.2995,-1.4332 -7.1659,-7.1658 4.2995,-21.4975 8.599,-22.9307 7.1658,-4.2995 17.198,-4.2995 25.7969,-2.8663 8.599,1.4332 14.3317,12.8985 22.9307,11.4653 10.0322,-1.4331 12.8985,-14.3316 21.4975,-18.6311 7.1659,-2.8664 15.7649,0 22.9306,-2.8664 28.6634,-10.0322 5.7327,-37.2623 8.599,-60.1929 2.8663,-11.4654 20.0644,-22.9306 12.8985,-31.5296 0,0 -58.7598,11.4652 -60.1929,12.8985 -8.599,5.7325 -1.4332,22.9306 -10.0322,27.2301 -5.7327,2.8663 -12.8985,0 -18.6312,-2.8664 -4.2994,-1.4331 -2.8662,-10.0321 -7.1657,-10.0321 -10.0322,0 -1.4332,21.4975 -7.1659,30.0965 -7.1658,7.1658 -14.3317,15.7647 -24.3638,18.6312 -24.3639,7.1657 -104.6211,-70.2253 -117.5196,-87.4232 -4.2995,-4.2995 -24.3639,-40.1287 -21.4975,-42.9949 8.599,-5.7327 17.198,15.7647 27.2302,14.3316 32.9627,-5.7327 24.3637,-71.6582 37.2622,-91.7226 4.2995,-5.7327 12.8985,-7.1658 15.7649,-12.8985 7.1658,-10.0322 7.1658,-22.9307 14.3317,-31.5296 21.4975,-28.6634 32.9627,-25.797 51.5939,-17.198 1.4332,1.4332 1.4332,-2.8664 2.8663,-4.2995 12.8986,-14.3317 -2.8663,-41.5619 31.5296,-42.9951 11.4654,-1.433 18.6312,21.4976 27.2302,15.7649 10.0322,-5.7327 -2.8663,-22.9306 1.4332,-34.3959 0,-2.8664 32.9629,-40.1286 34.3959,-40.1286 5.7327,-2.8665 12.8985,11.4652 15.7649,7.1657 2.8663,-4.2994 4.2995,-42.9949 12.8985,-50.1608 11.4653,-8.599 47.2944,-22.9307 61.6261,-25.7969 4.2995,0 5.7327,14.3317 8.599,10.0322 4.2995,-5.7327 2.8664,-15.7649 7.1659,-21.4976 z"
+ class="europe"
+ id="ua" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 6244.9816,5941.5751 c -1.433,5.7327 0,12.8984 -2.8662,18.6311 -1.4332,5.7327 -5.7327,10.0322 -8.599,15.7648 0,2.8664 27.2301,22.9307 10.0322,28.6634 -30.0966,7.1657 -20.0644,-34.396 -45.8614,-15.7648 -20.0643,14.3315 5.7327,30.0964 8.599,37.2622 1.4332,5.7327 -7.1657,10.0322 -7.1657,15.7649 1.433,5.7326 7.1657,8.599 10.032,12.8985 1.4332,5.7327 -10.032,11.4652 -15.7647,8.599 -10.0322,-5.7327 -4.2995,-28.6634 -15.7649,-30.0965 -38.6955,-7.1659 -20.0643,21.4975 -42.995,14.3316 -37.2623,-8.599 -14.3316,-37.2622 -32.9628,-40.1287 -24.3638,-4.2995 -8.599,34.3961 -8.599,37.2624 -5.7326,25.7969 -40.1286,34.3959 -40.1286,65.9256 0,14.3317 28.6633,8.599 4.2995,27.2301 -8.599,7.1659 -11.4653,-17.1979 -17.198,-24.3637 -4.2995,-4.2995 -14.3317,-1.4332 -15.7648,-7.1659 -1.4332,-5.7326 8.599,-10.0321 10.0321,-15.7648 0,-2.8664 -2.8663,-5.7327 -4.2995,-5.7327 -15.7648,-5.7327 -38.6954,-10.0322 -48.7276,-28.6632 -1.4331,-5.7327 2.8664,-11.4654 2.8664,-15.7649 0,-8.599 1.4331,-17.198 -1.4332,-25.7969 -1.4332,-1.4333 -4.2995,1.433 -7.1658,2.8662 -7.1659,5.7327 -12.8986,11.4654 -20.0644,17.198 -7.1657,5.7327 -18.6311,8.599 -21.4974,17.198 -1.4332,5.7327 11.4652,8.5991 10.0322,15.7649 0,7.1657 -40.1287,48.7276 -41.5619,48.7276 -12.8985,0 -22.9306,-17.198 -35.8291,-14.3317 -18.6312,4.2995 -57.3266,32.9629 -71.6583,7.1659 -5.7327,-8.599 -5.7327,-18.6312 -12.8985,-25.7971 -1.4332,-2.8663 -7.1659,5.7327 -8.599,2.8664 -1.4332,-4.2995 5.7326,-11.4654 2.8663,-15.7649 -1.4332,-4.2995 -10.0322,0 -12.8985,-2.8662 -8.5989,-14.3317 11.4653,-12.8985 10.0322,-21.4975 -1.4332,-7.1659 -10.0322,-10.0322 -10.0322,-17.198 -1.4332,-4.2995 10.0322,-12.8986 4.2995,-14.3316 -18.6311,-8.599 -58.7598,-8.599 -71.6582,11.4652 -2.8663,2.8664 -1.4333,5.7327 -1.4333,10.0322 1.4333,2.8663 8.599,5.7327 7.166,8.599 -8.599,17.198 -28.6634,24.3639 -45.8614,20.0644 -1.4332,0 5.7327,-2.8664 5.7327,-5.7327 0,-18.6312 14.3316,-11.4654 15.7648,-15.7649 7.1659,-22.9305 -11.4653,-42.9949 5.7327,-64.4924 10.0322,-14.3317 35.8291,-14.3317 41.5618,-30.0964 4.2995,-8.5992 -4.2995,-21.4976 0,-31.5297 5.7326,-11.4654 25.797,-7.1659 34.396,-17.1981 10.0321,-8.599 21.4974,-37.2622 34.3959,-47.2944 7.1659,-5.7327 14.3317,10.0322 15.7649,-10.0322 0,-7.1658 -21.4976,7.1659 -22.9307,0 -2.8664,-7.1658 8.599,-12.8985 12.8985,-18.6312 0,0 2.8663,1.4332 2.8663,0 0,-2.8662 -5.7326,-8.5989 -2.8663,-10.032 40.1286,-11.4654 22.9307,42.9949 71.6583,-7.1659 1.4332,0 4.2995,-2.8663 5.7327,-5.7327 l 2.8663,-2.8663 c 2.8662,-1.4332 5.7327,-1.4332 8.5989,1.4332 1.4332,1.4331 -8.5989,2.8663 -7.1657,5.7327 4.2995,8.599 70.225,-7.1659 74.5245,-7.1659 28.6634,-1.4332 2.8663,25.7969 38.6955,7.1659 1.4332,-1.4332 -2.8663,-8.5991 0,-10.0322 4.2994,-2.8664 20.0643,-4.2995 14.3316,-5.7327 -8.5989,-1.4332 -17.1979,1.4332 -25.7969,0 -4.2995,-1.4332 20.0642,-40.1286 41.5618,-18.6312 2.8663,2.8665 -4.2995,8.599 -2.8664,12.8985 1.4332,2.8664 7.1659,4.2995 10.0322,2.8664 2.8663,-1.4332 -1.4332,-11.4654 0,-8.599 5.7327,2.8663 5.7327,12.8985 11.4654,15.7648 0,0 47.2944,-2.8663 48.7276,-2.8663 14.3316,2.8663 24.3638,17.198 37.2622,24.3638 v 1.4332 c 0,10.0322 18.6312,12.8984 12.8985,27.2301 -1.4331,2.8663 -7.1658,1.4332 -8.599,2.8663 -4.2995,7.1659 -18.631,58.7598 -15.7647,60.193 2.8663,1.4331 50.1608,5.7327 51.5939,5.7327 2.8664,2.8663 -4.2995,7.1658 -1.4331,10.0321 7.1658,8.599 15.7648,14.3317 25.797,18.6312 22.9306,11.4654 27.2301,-27.2302 42.9949,-24.3638 4.2995,0 7.1659,1.4331 10.0322,2.8663 l -1.4333,12.8985 c -1.433,4.2995 -2.8662,7.1657 0,8.599 z"
+ class="europe"
+ id="ch" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 8254.28,5435.6676 c 20.0643,-10.0322 32.9628,-1.4332 48.7276,11.4654 30.0965,21.4974 35.8292,57.3266 60.1929,81.6903 40.1287,38.6956 149.0493,107.4874 161.9478,163.381 10.0322,35.8291 -2.8663,75.9577 5.7327,111.7869 2.8663,15.7647 28.6633,51.5939 27.2301,67.3588 0,5.7327 -11.4654,7.1658 -10.0322,12.8985 1.4333,5.7327 10.0322,2.8662 14.3317,5.7327 4.2995,2.8662 4.2995,12.8984 10.0322,12.8984 7.1658,1.4331 1.4331,-15.7649 7.1658,-17.1979 8.599,-1.4332 17.198,1.433 24.3639,0 10.032,-1.4332 -11.4654,-32.9629 1.433,-53.0273 4.2995,-5.7325 12.8985,-10.032 18.6312,-17.1979 7.1659,-10.0321 -12.8985,-15.7648 0,-25.797 5.7327,-5.7327 15.7649,-5.7327 18.6312,-11.4653 2.8663,-5.7327 -5.7327,-11.4654 -5.7327,-18.6311 0,-7.1659 7.1659,-14.3317 5.7327,-21.4975 -2.8663,-11.4654 -38.6954,-22.9307 -18.6312,-45.8613 7.1659,-8.599 20.0644,-30.0965 25.797,-20.0644 2.8664,5.7327 5.7327,12.8985 7.1659,20.0644 0,2.8663 -2.8664,4.2995 -2.8664,7.1657 1.4332,1.4333 4.2996,2.8665 5.7327,1.4333 4.2994,-5.7327 2.8664,-15.7649 10.0322,-21.4975 1.4332,-2.8664 2.8662,8.599 5.7327,7.1658 5.7325,-1.4332 4.2994,-10.0322 7.1657,-14.3317 0,0 18.6312,14.3317 24.3639,15.7649 8.599,1.4331 0,-25.797 8.599,-25.797 11.4653,0 18.6312,27.2301 25.7969,20.0643 1.4332,-2.8663 0,-5.7327 2.8663,-8.599 7.1659,-5.7327 17.198,-7.1658 25.7971,-12.8985 0,-1.4332 -32.9629,-5.7327 -35.8291,-15.7649 -1.4332,-2.8663 7.1657,-2.8663 7.1657,-5.7325 l -14.3316,-44.4282 c -8.599,-7.1659 -44.4282,-8.5991 -53.0272,-14.3316 -5.7327,-5.7327 -1.4332,-28.6634 -8.5989,-24.3639 -18.6312,11.4654 14.3316,-28.6633 -11.4654,-42.9949 -20.0643,-11.4654 1.4332,20.0642 -15.7648,14.3316 -8.599,-2.8664 -44.4281,-28.6633 -45.8613,-35.8291 -10.0322,-31.5296 12.8984,-61.6262 -20.0643,-83.1237 -12.8985,-7.1658 -2.8664,21.4975 -17.1979,15.7649 -34.3961,-14.3317 -12.8987,-42.995 -61.6263,-30.0966 -8.599,2.8665 -2.8663,27.2302 -8.599,21.4976 -4.2995,-4.2995 -4.2995,-11.4654 -8.5989,-17.1981 -2.8663,-2.8663 -10.0322,18.6312 -12.8985,7.1659 -1.4332,-5.7327 5.7327,-20.0644 0,-18.6311 -11.4653,1.4332 -21.4975,11.4652 -31.5297,11.4652 -4.2995,0 0,-10.032 -4.2995,-12.8985 -12.8984,-10.032 -30.0964,-15.7647 -45.8613,-17.1979 -38.6954,-2.8663 -30.0964,18.6312 -57.3266,31.5296 -2.8663,1.4332 -57.3266,8.599 -57.3266,10.0322 0,5.7327 7.1658,8.599 7.1658,14.3317 1.4332,5.7326 -11.4653,-8.599 -15.7648,-5.7327 -5.7327,2.8663 -2.8664,11.4653 -4.2995,17.198 z"
+ class="europe"
+ id="md" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 5033.9567,6695.4203 c -64.4925,18.6311 -28.6633,-34.3961 -25.7969,-34.3961 37.2622,-2.8663 40.1286,15.7649 25.7969,34.3961 z"
+ class="europe"
+ id="ad" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 5774.9033,6543.5047 c 11.4654,0 21.4976,-10.0322 21.4976,-20.0644 0,-10.0322 -10.0322,-18.6312 -21.4976,-18.6312 -11.4653,0 -21.4974,8.599 -21.4974,18.6312 0,10.0322 10.0321,20.0644 21.4974,20.0644 11.4654,0 -11.4653,0 0,0 z"
+ class="europe"
+ id="mc" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 6123.1627,5875.6495 7.1658,15.7647 v 17.198 c -7.1658,0 -11.4653,-1.4331 -12.8985,-1.4331 -1.4332,-1.4332 2.8663,-15.7649 5.7327,-31.5296 z"
+ class="europe"
+ id="li" /><path
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 7579.2613,4400.9232 c -31.5297,-42.995 -1.4332,-81.6905 -10.0322,-96.0221 -4.2995,-5.7327 -10.0321,-10.0322 -15.7648,-12.8985 -2.8663,-1.4332 -7.1658,2.8663 -8.599,1.4331 -5.7327,-4.2995 -2.8663,-15.7648 -8.599,-17.198 -20.0643,-5.7326 -40.1286,12.8985 -61.6261,12.8985 -2.8664,0 1.4331,-4.2995 -1.4332,-5.7326 -18.6311,-5.7327 -47.2945,-4.2995 -64.4925,-14.3317 -4.2995,-2.8663 -4.2995,-10.0321 -8.599,-11.4653 -4.2995,0 -5.7326,5.7326 -10.0321,7.1658 1.4331,4.2995 1.4331,7.1658 0,10.0322 -1.4332,2.8663 -8.599,-1.4332 -8.599,1.4331 -1.4332,15.7649 18.6311,30.0965 14.3316,45.8614 -4.2995,18.6311 -80.2572,17.1979 -77.3909,8.599 1.4331,-2.8664 2.8663,-5.7327 4.2995,-8.599 -15.7648,17.1979 -32.9628,14.3316 -55.8935,20.0643 -2.8663,0 -7.1658,-1.4332 -7.1658,1.4331 -1.4332,11.4654 4.2995,22.9307 4.2995,34.396 0,20.0644 -10.0322,41.5618 -27.2302,58.7598 81.6905,11.4654 275.1679,4.2995 341.0935,-34.3959 0,0 0,0 1.4332,-1.4332 z m -222.1407,-137.584 c 0,0 -1.4332,0 -1.4332,0 -1.4332,4.2995 -4.2995,10.0322 -5.7327,14.3317 2.8664,-4.2995 5.7327,-10.0322 7.1659,-14.3317 z"
+ class="europe ru"
+ id="ru-kgd" /><path
+ sodipodi:nodetypes="cccc"
+ style="display:inline;opacity:1;fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
+ d="m 4630.6239,4351.3664 c -7.1658,1.4333 -14.3317,1.4333 -21.4974,5.7327 -5.7328,4.2995 -7.166,14.3316 -12.8985,20.0643 -61.6263,55.8935 37.2622,41.5618 34.3959,-25.797 z"
+ class="europe"
+ id="im" />
+
+
+<path
+ d="m 4736.7888,3126.2627 c -1.4332,12.8984 -4.2995,35.8292 -4.2995,35.8292 0,0 2.8663,-22.9308 4.2995,-35.8292 z m 21.4974,-140.4503 c -17.1979,35.8292 7.1658,-8.599 11.4653,12.8985 0,2.8663 -7.1658,2.8663 -7.1658,4.2996 2.8663,5.7325 14.3317,4.2995 14.3317,10.0321 0,10.0321 -2.8664,27.2301 -12.8985,27.2301 -8.5989,-1.4331 1.4331,-18.6311 0,-27.2301 -4.2995,-14.3317 -47.2945,-30.0964 -5.7327,-27.2302 z"
+ style="fill:#fff7df;fill-opacity:1;stroke:#aa805d;stroke-width:4.58365;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="fo"
+ inkscape:label="#fo" /></g>
+
+
+</svg>
diff --git a/examples/quick/quickshapes/weatherforecast/assets/OFL.txt b/examples/quick/quickshapes/weatherforecast/assets/OFL.txt
new file mode 100644
index 0000000000..d3a046cf57
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2019 The Work Sans Project Authors (https://github.com/weiweihuanghuang/Work-Sans)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/examples/quick/quickshapes/weatherforecast/assets/WorkSans-Regular.ttf b/examples/quick/quickshapes/weatherforecast/assets/WorkSans-Regular.ttf
new file mode 100644
index 0000000000..20c7240371
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/WorkSans-Regular.ttf
Binary files differ
diff --git a/examples/quick/quickshapes/weatherforecast/assets/apache.txt b/examples/quick/quickshapes/weatherforecast/assets/apache.txt
new file mode 100644
index 0000000000..989e2c59e9
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/apache.txt
@@ -0,0 +1,201 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. \ No newline at end of file
diff --git a/examples/quick/quickshapes/weatherforecast/assets/attribution.txt b/examples/quick/quickshapes/weatherforecast/assets/attribution.txt
new file mode 100644
index 0000000000..7d71070406
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/attribution.txt
@@ -0,0 +1,10 @@
+The SVG files in this directory are converted to QML using the svgtoqml tool. They are not directly
+used by the example, but are provided as .svg for users who might want to modify them on their own.
+The font file is required to build the example.
+
+The following third-party assets are used in this example:
+
+- Weather icons from the Noto Emoji collection: https://www.svgrepo.com/collection/noto-emojis/ (Author: googlefonts, License: Apache, see apache.txt))
+- "Europe.svg" is a modified version of https://commons.wikimedia.org/wiki/File:Europ%C3%A4ische_Wirtschafts-_und_W%C3%A4hrungsunion-2009.svg (Cc-by-3.0 license)
+- "Work Sans" font by Wei Huang (Licensed under Open Font License, see OFL.txt)
+- "gear alt fill" from "Framework7 iOS icons" by Framework7. (licensed under MIT license)
diff --git a/examples/quick/quickshapes/weatherforecast/assets/cloud-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/cloud-svgrepo-com.svg
new file mode 100644
index 0000000000..2dac977b7c
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/cloud-svgrepo-com.svg
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+
+<svg
+ width="800px"
+ height="800px"
+ viewBox="0 0 128 128"
+ aria-hidden="true"
+ role="img"
+ class="iconify iconify--noto"
+ preserveAspectRatio="xMidYMid meet"
+ version="1.1"
+ id="svg1047"
+ sodipodi:docname="cloud-svgrepo-com.svg"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1051" />
+ <sodipodi:namedview
+ id="namedview1049"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.805"
+ inkscape:cx="399.37888"
+ inkscape:cy="400"
+ inkscape:window-width="3840"
+ inkscape:window-height="2065"
+ inkscape:window-x="1200"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1047" />
+ <path
+ d="M23.45 62.3c.72-.72-1.27-9.29 7.6-15.91s14.92-2.67 15.77-2.96c.84-.28 4.79-17.6 21.4-22.1s33.93 3.94 38.01 18.02c3.73 12.87.84 21.54 1.27 22.1c.42.56 8.45.28 13.09 7.74s2.96 12.11 2.96 12.11l-29.56 9.15h-47.3S5.02 79.47 4.6 77.5c-.42-1.97.53-8.37 7.32-12.25c5.9-3.37 10.26-1.68 11.53-2.95z"
+ fill="#e4eaee"
+ id="path1043" />
+ <path
+ d="M35.16 92.84s-15.78 3.3-26.45-4.96C2.29 82.9 4.63 74.83 4.63 74.83s4.6 4.65 13.89 5.91c9.29 1.27 19.71.84 19.71.84s2.6 4.44 12.39 6.48c12.27 2.55 18.74-3.73 18.74-3.73s3.36 4.02 15.19 4.3c11.83.28 18.46-7.98 19.57-8.17c.56-.09 3.82 2.87 10.28 1.83c6.15-.99 9.39-3.66 9.39-3.66s.89 6.62-5.3 10.7c-4.83 3.18-13.23 3.52-13.23 3.52s-1.28 4.91-7.05 8.48c-5.36 3.33-14.6 4.44-21.44 2.4c-8.59-2.56-10.72-6.47-10.72-6.47s-6.4 3.75-16.4 2.48c-9.45-1.18-14.49-6.9-14.49-6.9z"
+ fill="#bacdd2"
+ id="path1045"
+ style="stroke:none;stroke-opacity:1" />
+ <path
+ id="path1183"
+ style="stroke-width:6.25;fill:none;stroke:#000000;stroke-opacity:1"
+ d="M 484.66211 125.7207 C 465.45998 125.60837 445.83984 128.03906 426.375 133.3125 C 322.5625 161.4375 297.875 269.6875 292.625 271.4375 C 287.3125 273.25 249.5 248.5625 194.0625 289.9375 C 138.625 331.3125 151.0625 384.875 146.5625 389.375 C 138.625 397.3125 111.375 386.75 74.5 407.8125 C 44.21865 425.11613 32.528534 450.43334 29.265625 467.99414 C 29.212598 467.94196 28.9375 467.6875 28.9375 467.6875 C 28.9375 467.6875 14.3125 518.125 54.4375 549.25 C 121.125 600.875 219.75 580.25 219.75 580.25 C 219.75 580.25 251.25 616 310.3125 623.375 C 372.8125 631.3125 412.8125 607.875 412.8125 607.875 C 412.8125 607.875 426.125 632.3125 479.8125 648.3125 C 522.5625 661.0625 580.3125 654.125 613.8125 633.3125 C 649.875 611 657.875 580.3125 657.875 580.3125 C 657.875 580.3125 710.375 578.1875 740.5625 558.3125 C 770.74033 538.42146 773.97809 508.95537 773.94922 496.91797 C 774.56495 484.39547 772.04478 461.95157 753.6875 432.4375 C 724.6875 385.8125 674.5 387.5625 671.875 384.0625 C 669.1875 380.5625 687.25 326.375 663.9375 245.9375 C 643.21875 174.4375 567.87135 126.20749 484.66211 125.7207 z "
+ transform="scale(0.16)" />
+</svg>
diff --git a/examples/quick/quickshapes/weatherforecast/assets/cloud-with-lightning-and-rain-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/cloud-with-lightning-and-rain-svgrepo-com.svg
new file mode 100644
index 0000000000..78a958817c
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/cloud-with-lightning-and-rain-svgrepo-com.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--noto" preserveAspectRatio="xMidYMid meet"><path d="M78 67.82l-22.83 3.97s-7.6 19.36-8.07 20.67s-.45 3.38 1.62 3.38c2.63 0 15.98-.07 15.98-.07s-3.24 15.63-3.43 17.03s1.31 2.63 2.63 1.22s19.51-24.79 20.3-25.93c1.41-2.04.05-3.92-1.55-4.01c-1.6-.09-12.32.14-12.32.14L78 67.82z" fill="#feca18"></path><path d="M71.92 70.54s-5.09 11.26-5.28 11.9c-.56 1.9 1.9 2.67 2.6 1.13c.7-1.55 6.76-14.08 6.76-14.08l-4.08 1.05z" fill="#fdb900"></path><path d="M49.99 92.17c1.34-.04 13.1-.07 14.08-.14c1.83-.14 2.11 2.39 0 2.39c-2.19 0-13.73.04-14.43.07c-1.62.08-1.84-2.25.35-2.32z" fill="#fdb900"></path><path d="M59.42 69.01s-6 15.06-6.34 16.19c-1.41 4.72 3.66 4.58 5 1.9c.66-1.33 7.11-14.99 7.39-17.46c.28-2.46-6.05-.63-6.05-.63z" fill="#ffe36a"></path><path d="M61.01 86.57c-.86-.59-1.9-.07-2.53.92c-.63.99-1.55 2.6-.14 3.45c1.27.76 2.39-.77 2.89-1.48c.49-.71 1.12-1.98-.22-2.89z" fill="#ffe36a"></path><path d="M71.34 89.8c-2.29-.49-3.03 1.41-3.52 3.66c-.49 2.25-2.33 9.53-2.67 11.33c-.49 2.53 1.83 2.89 2.82.56c.74-1.74 3.95-9.59 4.36-10.84c.63-1.89 1.33-4.21-.99-4.71z" fill="#ffe36a"></path><path d="M107.92 46.26s2.6-34.98-27.31-39.42C53.72 2.85 48.44 28.38 48.44 28.38s-10.16-2.37-17.46 4.15c-7.25 6.48-6.69 14.15-6.69 14.15S6.41 43.86 4.58 59.21C2.87 73.55 24.01 69.98 24.01 69.98l100.38-7.39s2.38-7.54-5.21-13.23c-4.51-3.39-11.26-3.1-11.26-3.1z" fill="#e4eaee"></path><radialGradient id="IconifyId17ecdb2904d178eab6090" cx="65.811" cy="75.277" r="29.434" fx="52.634" fy="75.403" gradientTransform="matrix(.0288 -.9996 1.4975 .0432 -48.813 137.809)" gradientUnits="userSpaceOnUse"><stop offset=".193" stop-color="#ffe366"></stop><stop offset=".331" stop-color="#eddd82"></stop><stop offset=".571" stop-color="#d0d4ad"></stop><stop offset=".759" stop-color="#bfcfc7"></stop><stop offset=".87" stop-color="#b8cdd1"></stop></radialGradient><path d="M53.81 54.19c.84-.28 5.63 3.81 19.43 3.94c18.96.19 33.32-10.98 33.32-10.98s.48 3.4-1.03 5.82c-1.88 3-4.79 3.57-4.79 3.57s5.26 3.66 12.48 3.1c6.51-.51 11.22-2.72 11.22-2.72s.63 3.21.28 6.34c-.7 6.24-4.36 8.78-8.4 9.24c-2.97.34-28.34.46-38.76.56c-9.39.09-57.24 1.29-62.13.75c-5.07-.56-8.92-3.61-10.32-7.98c-1.07-3.31-.56-5.44-.56-5.44s9.32 2.47 14.55 1.88c6.57-.75 10.98-4.88 10.98-4.88s3.94 2.25 12.67 1.5c7.51-.66 11.06-4.7 11.06-4.7z" fill="url(#IconifyId17ecdb2904d178eab6090)"></path><path d="M103.9 74.2c-1.02-.57-4.65 2.82-6.69 4.86c-1.25 1.25-5.23 4.52-5.77 7.67c-1.34 7.74 7.53 9.5 11.05 5.91c2.66-2.71 2.18-8 2.18-9.78c.01-2.47.5-7.96-.77-8.66z" fill="#52c0ee"></path><path d="M31.02 78.36c-.97-.56-4.45 2.72-6.41 4.71c-1.2 1.21-5.01 4.38-5.55 7.46c-1.32 7.55 7.13 9.32 10.51 5.84c2.55-2.63 2.13-7.8 2.14-9.54c.01-2.41.51-7.77-.69-8.47z" fill="#52c0ee"></path><path d="M47.15 100.74c-1.09-.41-4.76 2.93-6.8 4.97c-1.25 1.25-4.95 4.45-5.49 7.6c-1.34 7.74 7.25 9.57 10.77 5.98c2.66-2.71 2.22-8 2.18-9.78c-.1-4.64.09-8.49-.66-8.77z" fill="#52c0ee"></path><path d="M97.78 84.22c-1.4-.73-3.1-.42-4.18 1.83c-1.08 2.25-.19 4.04.94 4.55c1.3.6 2.91.05 3.99-1.78s.42-3.99-.75-4.6z" fill="#b2e6fe"></path><path d="M40.01 110.93c-1.44-.5-3.02.03-3.74 2.34c-.72 2.31.39 3.89 1.54 4.23c1.33.39 2.79-.36 3.57-2.27c.77-1.91-.17-3.88-1.37-4.3z" fill="#b2e6fe"></path><path d="M24.07 87.97c-1.52-.42-3.12.23-3.7 2.66c-.58 2.43.66 3.99 1.87 4.25c1.4.31 2.85-.56 3.53-2.58c.67-2-.42-3.98-1.7-4.33z" fill="#b2e6fe"></path></svg> \ No newline at end of file
diff --git a/examples/quick/quickshapes/weatherforecast/assets/cloud-with-lightning-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/cloud-with-lightning-svgrepo-com.svg
new file mode 100644
index 0000000000..88ff853912
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/cloud-with-lightning-svgrepo-com.svg
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+
+<svg
+ width="800px"
+ height="800px"
+ viewBox="0 0 128 128"
+ aria-hidden="true"
+ role="img"
+ class="iconify iconify--noto"
+ preserveAspectRatio="xMidYMid meet"
+ version="1.1"
+ id="svg1314"
+ sodipodi:docname="cloud-with-lightning-svgrepo-com.svg"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1318" />
+ <sodipodi:namedview
+ id="namedview1316"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.805"
+ inkscape:cx="399.37888"
+ inkscape:cy="400"
+ inkscape:window-width="3840"
+ inkscape:window-height="2065"
+ inkscape:window-x="1200"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1314" />
+ <path
+ d="M79.83 71.24L57 75.21s-7.6 19.36-8.07 20.67c-.47 1.31-.45 3.38 1.62 3.38c2.63 0 15.98-.07 15.98-.07s-3.24 15.63-3.43 17.03s1.31 2.63 2.63 1.22c1.31-1.41 19.51-24.79 20.3-25.93c1.41-2.04.05-3.92-1.55-4.01c-1.6-.09-12.32.14-12.32.14l7.67-16.4z"
+ fill="#feca18"
+ id="path1287"
+ style="stroke:#b0ab9d;stroke-opacity:1" />
+ <path
+ d="M73.71 75.04s-5.09 11.26-5.28 11.9c-.56 1.9 1.9 2.67 2.6 1.13c.7-1.55 6.76-14.08 6.76-14.08l-4.08 1.05z"
+ fill="#fdb900"
+ id="path1289" />
+ <path
+ d="M50.83 96.3c1.34-.04 13.1-.07 14.08-.14c1.83-.14 2.11 2.39 0 2.39c-2.19 0-13.73.04-14.43.07c-1.62.08-1.83-2.25.35-2.32z"
+ fill="#fdb900"
+ id="path1291" />
+ <path
+ d="M59.98 75.96s-6 15.06-6.34 16.19c-1.41 4.72 3.66 4.58 5 1.9c.66-1.33 7.11-14.99 7.39-17.46c.29-2.46-6.05-.63-6.05-.63z"
+ fill="#ffe36a"
+ id="path1293" />
+ <path
+ d="M62.52 91.16c-.86-.59-1.9-.07-2.53.92c-.63.99-1.55 2.6-.14 3.45c1.27.76 2.39-.77 2.89-1.48c.48-.7 1.11-1.97-.22-2.89z"
+ fill="#ffe36a"
+ id="path1295" />
+ <path
+ d="M72.09 94.4c-2.29-.49-3.03 1.41-3.52 3.66c-.49 2.25-2.33 9.53-2.67 11.33c-.49 2.53 1.83 2.89 2.82.56c.74-1.74 3.95-9.59 4.36-10.84c.63-1.89 1.33-4.22-.99-4.71z"
+ fill="#ffe36a"
+ id="path1297" />
+ <path
+ d="M107.35 50.2s1.29-34.78-27.45-39C53.01 7.26 48.23 32.6 48.23 32.6s-11.68-2.67-18.02 4.79s-6.48 13.23-6.48 13.23s-17.6-1.69-19.15 12.67s11.54 8.45 11.54 8.45l107.13-4.72s2.95-8.03-4.65-13.73c-4.49-3.37-11.25-3.09-11.25-3.09z"
+ fill="#e4eaee"
+ id="path1299" />
+ <radialGradient
+ id="IconifyId17ecdb2904d178eab6089"
+ cx="65.427"
+ cy="79.054"
+ r="29.294"
+ fx="52.312"
+ fy="79.18"
+ gradientTransform="matrix(.0288 -.9996 1.4975 .0432 -54.843 141.038)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset=".193"
+ stop-color="#ffe366"
+ id="stop1301" />
+ <stop
+ offset=".331"
+ stop-color="#eddd82"
+ id="stop1303" />
+ <stop
+ offset=".571"
+ stop-color="#d0d4ad"
+ id="stop1305" />
+ <stop
+ offset=".759"
+ stop-color="#bfcfc7"
+ id="stop1307" />
+ <stop
+ offset=".87"
+ stop-color="#b8cdd1"
+ id="stop1309" />
+ </radialGradient>
+ <path
+ d="M58.78 55.69c.84-.28 6.05 4.22 19.85 3.8s26.61-7.32 26.61-7.32s-.7 4.93-2.25 7.32s-4.22 5.21-4.22 5.21s7.18 1.69 13.66.84c6.48-.84 11.26-3.94 11.26-3.94s.84 3.24.28 6.34c-.72 3.97-4.22 6.9-7.18 7.32s-29.56 1.76-39.98 2.11c-8.45.29-56.63.68-61.52.14c-5.07-.56-8.87-2.82-10.28-7.18c-1.07-3.31-.56-5.35-.56-5.35s6.36 2.73 11.54 1.83c6.48-1.13 10-6.19 10-6.19s6.34 3.24 15.49 2.11c9.14-1.13 17.3-7.04 17.3-7.04z"
+ fill="url(#IconifyId17ecdb2904d178eab6089)"
+ id="path1312" />
+ <path
+ id="path1377"
+ style="stroke-width:6.25;fill:none;stroke:#b0ab9d;stroke-opacity:1"
+ d="M 462.46484 67.416016 C 326.82772 69.1539 301.4375 203.75 301.4375 203.75 C 301.4375 203.75 228.4375 187.0625 188.8125 233.6875 C 149.1875 280.3125 148.3125 316.375 148.3125 316.375 C 148.3125 316.375 38.3125 305.8125 28.625 395.5625 C 28.219648 399.31789 27.988274 402.83249 27.890625 406.15625 C 27.88823 406.15523 27.8125 406.125 27.8125 406.125 C 27.8125 406.125 24.625 418.875 31.3125 439.5625 C 40.125 466.8125 63.875 480.9375 95.5625 484.4375 C 126.125 487.8125 427.25 485.375 480.0625 483.5625 C 545.1875 481.375 711.4375 473 729.9375 470.375 C 748.4375 467.75 770.3125 449.4375 774.8125 424.625 C 778.04161 406.74956 773.86966 388.33772 773.19727 385.5625 C 771.11652 369.23452 763.28052 349.58539 741.25 333.0625 C 713.1875 312 670.9375 313.75 670.9375 313.75 C 670.9375 313.75 679 96.375 499.375 70 C 486.24512 68.076172 473.95951 67.268737 462.46484 67.416016 z "
+ transform="scale(0.16)" />
+</svg>
diff --git a/examples/quick/quickshapes/weatherforecast/assets/cloud-with-rain-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/cloud-with-rain-svgrepo-com.svg
new file mode 100644
index 0000000000..93f690c7bb
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/cloud-with-rain-svgrepo-com.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--noto" preserveAspectRatio="xMidYMid meet"><path d="M107.62 46.29s1.72-32.32-23.89-38.72C54.12.17 48.29 28.49 48.29 28.49s-11.55-2.19-18.74 5.62c-6.03 6.55-5.46 12.96-5.46 12.96s-7.96-1.72-14.21 3.75s-5.31 10.61-5.31 10.61l18.58 7.18s93.83.47 94.3.16c.47-.31 6.25-9.37 6.25-9.37s-.94-7.81-6.4-10.93c-5.47-3.12-9.68-2.18-9.68-2.18z" fill="#e2ebee"></path><path d="M14.25 73.45c4.68.16 35.29.78 53.86.62s46.81.14 50.73-1.71c3.07-1.45 4.9-3.85 5.19-7.21c.28-3.21-.34-5.75-.34-5.75s-4.37 3.16-12.65 3.28c-5.15.07-15.61-.78-13.58-2.65c.57-.53 8.66-6.66 8.27-12.33c-.09-1.34-7.34 5.31-14.21 8.12c-6.87 2.81-13.39 4.98-22.47 4.4c-7.65-.5-10.97-3.61-10.97-3.61s-3.69 4.33-14.78 5.26c-11.09.94-13.48-3.23-13.48-3.23s-5.37 4.06-14.39 3.94c-9-.14-10.77-3.38-10.77-3.38s-.85 5.77 1.03 9.14c1.92 3.43 5.41 5.01 8.56 5.11z" fill="#b9ced2"></path><path d="M69.99 81.57c-1.71-.6-7.81 4.23-10.56 9c-2.76 4.79-.96 8.29 1.99 9.73c3.32 1.62 8.1.62 9.46-4.1c1.34-4.67.95-13.98-.89-14.63z" fill="#4fc4f8"></path><path d="M37.66 79.19c-1.55-.55-6.58 4.12-8.84 6.64c-3.03 3.39-5.37 9.71.38 12.27c5.79 2.57 9.55-1.64 9.84-7.46c.28-5.42-.1-10.99-1.38-11.45z" fill="#4fc4f8"></path><path d="M49.87 101.65c-1.65-.89-8.58 5.26-10.79 8.93c-2.2 3.67-2.06 8.38 2.44 10.31s8.47-.54 9.3-6.97c.58-4.56.64-11.41-.95-12.27z" fill="#4fc4f8"></path><path d="M83.38 102.18c-1.19-.67-9.43 5.69-11.28 10.32c-3.67 9.19 8.87 13.29 11.72 4.65c1.66-5.04 1.03-14.14-.44-14.97z" fill="#4fc4f8"></path><path d="M103.87 82.37c-1.63-1-8.48 4.64-10.76 8.88c-2.88 5.36-.64 9.4 2.5 10.68c3.26 1.32 7.45.05 8.87-4.78c2.05-6.96.97-13.8-.61-14.78z" fill="#4fc4f8"></path><path d="M96.4 91.09c-1.08.13-2.42 1.67-2.63 4.3s1.45 3.96 3.13 3.29c1.58-.63 1.84-2.88 1.84-4.3s-.67-3.5-2.34-3.29z" fill="#aee6fd"></path><path d="M63 90.26c-1.14-.32-3.26 1.44-3.63 4.29c-.29 2.2.56 3.53 2.27 3.66c2.38.18 3.13-2.75 3.07-4.54c-.04-1.02-.22-2.99-1.71-3.41z" fill="#aee6fd"></path><path d="M42.45 109.79c-1.06-.08-2.84 1.7-2.84 4.47s1.44 3.97 3.14 3.6c1.7-.38 2.04-3.63 1.93-4.92c-.11-1.29-.66-3.03-2.23-3.15z" fill="#aee6fd"></path><path d="M30.91 88.44c-1.43-.41-3.26 1.4-3.48 3.44c-.23 2.04.72 3.52 2.23 3.75c1.51.23 2.8-1.59 2.95-3.71c.16-2.12-.37-3.11-1.7-3.48z" fill="#aee6fd"></path><path d="M76.4 110.97c-1.32-.11-3.21 1.84-3.16 4.79s1.58 3.45 2.59 3.51c1.45.09 2.46-1.62 2.59-4.3c.11-2.25-.4-3.87-2.02-4z" fill="#aee6fd"></path></svg> \ No newline at end of file
diff --git a/examples/quick/quickshapes/weatherforecast/assets/cloud-with-snow-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/cloud-with-snow-svgrepo-com.svg
new file mode 100644
index 0000000000..978570a9cd
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/cloud-with-snow-svgrepo-com.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--noto" preserveAspectRatio="xMidYMid meet"><path d="M108.55 45.69s1.97-34.42-27.94-38.86C53.72 2.85 48.44 28.38 48.44 28.38s-9.52-2.44-16.82 4.08C24.36 38.93 25 46.11 25 46.11S6.84 44.28 5.01 59.63c-1.71 14.34 19 10.35 19 10.35l100.38-7.39s2.38-7.54-5.21-13.23c-4.51-3.39-10.63-3.67-10.63-3.67z" fill="#e4eaee"></path><path d="M56.95 55.83c.84-.28 3.37 2.96 17.18 2.96c14.36 0 32.1-12.11 32.1-12.11s-.07 4.74-1.55 7.18c-1.97 3.24-6.48 5.07-6.48 5.07s6.48 2.67 15.06 1.97c6.51-.53 11.17-3.99 11.17-3.99s.63 3.21.28 6.34c-.7 6.24-4.32 8.02-8.35 8.49c-2.97.34-28.39 1.21-38.81 1.31c-9.39.09-56.96.21-61.85-.33c-5.07-.56-8.87-3.24-10.28-7.6C4.35 61.81 5 58.64 5 58.64s5.77 2.25 12.67 2.25c6.61 0 11.54-3.1 11.54-3.1s5.21 3.57 13.94 2.82c7.54-.64 13.8-4.78 13.8-4.78z" fill="#b9ced2"></path><path d="M104.07 81.59s2.25-3.66 2.85-4.4c.6-.74 1.62-1.55 2.89-.84c1.27.7.84 2.15.49 2.78c-.35.63-2.46 4.72-2.46 4.72s3.98.04 4.86.04s2.46.7 2.46 2.25c0 1.34-.63 2.18-2.15 2.25s-5.31.07-5.31.07s2.04 3.34 2.64 4.36c.6 1.02.67 2.25-.21 2.99c-1.13.95-2.6.42-3.48-.95c-.66-1.04-2.71-4.01-2.71-4.01s-2.43 3.52-3.06 4.29c-.63.77-1.79 1.69-2.96.81c-1.44-1.09-.61-2.32 0-3.27l2.6-4.08s-4.43-.11-5.7-.11c-1.27 0-2.36-.7-2.29-2.15c.07-1.48.99-2.08 2.5-2.11c1.51-.04 5.53-.04 5.53-.04s-2.46-3.52-3.1-4.54c-.63-1.02-.7-2.32.28-3.06c.99-.74 2.15-.32 2.89.63s3.44 4.37 3.44 4.37z" fill="#92c9f4"></path><path d="M65.21 85.22s2.25-3.66 2.85-4.4c.6-.74 1.62-1.55 2.89-.84c1.27.7.84 2.15.49 2.78c-.35.63-2.46 4.72-2.46 4.72s3.98.04 4.86.04s2.46.7 2.46 2.25c0 1.34-.63 2.18-2.15 2.25c-1.51.07-5.31.07-5.31.07s2.04 3.34 2.64 4.36s.67 2.25-.21 2.99c-1.13.95-2.6.42-3.48-.95c-.66-1.04-2.71-4.01-2.71-4.01s-2.43 3.52-3.06 4.29c-.63.77-1.79 1.69-2.96.81c-1.44-1.09-.61-2.32 0-3.27l2.6-4.08s-4.43-.11-5.7-.11c-1.27 0-2.36-.7-2.29-2.15c.07-1.48.99-2.08 2.5-2.11c1.51-.04 5.53-.04 5.53-.04s-2.46-3.52-3.1-4.54c-.63-1.02-.7-2.32.28-3.06c.99-.74 2.15-.32 2.89.63s3.44 4.37 3.44 4.37z" fill="#92c9f4"></path><path d="M44.19 105.43s2.25-3.66 2.85-4.4c.6-.74 1.62-1.55 2.89-.84c1.27.7.84 2.15.49 2.78c-.35.63-2.46 4.72-2.46 4.72s3.98.04 4.86.04s2.46.7 2.46 2.25c0 1.34-.63 2.18-2.15 2.25c-1.51.07-5.31.07-5.31.07s2.04 3.34 2.64 4.36c.6 1.02.67 2.25-.21 2.99c-1.13.95-2.6.42-3.48-.95c-.66-1.04-2.71-4.01-2.71-4.01s-2.43 3.52-3.06 4.29c-.63.77-1.79 1.69-2.96.81c-1.44-1.09-.61-2.32 0-3.27l2.6-4.08s-4.43-.11-5.7-.11s-2.36-.7-2.29-2.15c.07-1.48.99-2.08 2.5-2.11c1.51-.04 5.53-.04 5.53-.04s-2.46-3.52-3.1-4.54c-.63-1.02-.7-2.32.28-3.06c.99-.74 2.15-.32 2.89.63s3.44 4.37 3.44 4.37z" fill="#92c9f4"></path><path d="M26.9 82.16s2.31-3.63 2.92-4.36s1.65-1.52 2.9-.8c1.26.72.81 2.16.45 2.79s-2.54 4.68-2.54 4.68s3.98.1 4.86.11c.88.01 2.45.74 2.43 2.29c-.02 1.34-.67 2.17-2.18 2.22c-1.51.05-5.31-.01-5.31-.01s1.99 3.37 2.57 4.4s.64 2.26-.26 2.99c-1.14.93-2.61.38-3.47-1c-.65-1.05-2.65-4.05-2.65-4.05s-2.48 3.48-3.13 4.25c-.65.76-1.81 1.67-2.97.76c-1.43-1.11-.57-2.33.05-3.27l2.67-4.04s-4.43-.17-5.7-.19c-1.27-.02-2.35-.74-2.25-2.18c.09-1.48 1.02-2.06 2.53-2.07s5.53.05 5.53.05s-2.41-3.56-3.03-4.59c-.62-1.03-.67-2.33.33-3.06c1-.72 2.15-.28 2.88.68c.71.94 3.37 4.4 3.37 4.4z" fill="#92c9f4"></path><path d="M87.08 105.15s2.25-3.66 2.85-4.4c.6-.74 1.62-1.55 2.89-.84c1.27.7.84 2.15.49 2.78c-.35.63-2.46 4.72-2.46 4.72s3.98.04 4.86.04s2.46.7 2.46 2.25c0 1.34-.63 2.18-2.15 2.25s-5.31.07-5.31.07s2.04 3.34 2.64 4.36c.6 1.02.67 2.25-.21 2.99c-1.13.95-2.6.42-3.48-.95c-.66-1.04-2.71-4.01-2.71-4.01s-2.43 3.52-3.06 4.29c-.63.77-1.79 1.69-2.96.81c-1.44-1.09-.61-2.32 0-3.27l2.6-4.08s-4.43-.11-5.7-.11c-1.27 0-2.36-.7-2.29-2.15c.07-1.48.99-2.08 2.5-2.11s5.53-.04 5.53-.04s-2.46-3.52-3.1-4.54c-.63-1.02-.7-2.32.28-3.06c.99-.74 2.15-.32 2.89.63s3.44 4.37 3.44 4.37z" fill="#92c9f4"></path></svg> \ No newline at end of file
diff --git a/examples/quick/quickshapes/weatherforecast/assets/gear-alt-stroke.svg b/examples/quick/quickshapes/weatherforecast/assets/gear-alt-stroke.svg
new file mode 100644
index 0000000000..674e35b272
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/gear-alt-stroke.svg
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="56"
+ viewBox="0 0 56 56"
+ width="56"
+ version="1.1"
+ id="svg4"
+ sodipodi:docname="gear-alt-fill.svg"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ id="namedview6"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="11.5"
+ inkscape:cx="28.043478"
+ inkscape:cy="28"
+ inkscape:window-width="1402"
+ inkscape:window-height="1783"
+ inkscape:window-x="1563"
+ inkscape:window-y="282"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg4" />
+ <path
+ d="m18.7917819 47.4333262c-2.455097-.5432286-4.7688821-1.4624372-6.8778614-2.694132l.1238514-2.4248414c.168912-3.3070638-2.38380448-5.850592-5.68212655-5.6821266l-2.68051418.1369103c-1.2552175-1.9937532-2.22519257-4.185172-2.85265254-6.5169816l2.10374479-1.8992856c2.45788604-2.2190084 2.45138898-5.8225976 0-8.0357404l-2.24327877-2.0252585c.55680792-2.282373 1.43999294-4.4368497 2.59730287-6.4111778l3.07539783.1570793c3.30706381.168912 5.85059205-2.38380448 5.68212655-5.68212655l-.1570793-3.07539783c1.9743281-1.15730993 4.1288048-2.04049495 6.4111778-2.59730287l2.0252585 2.24327877c2.2190084 2.45788604 5.8225976 2.45138898 8.0357404 0l1.8992856-2.10374479c2.3318096.62745997 4.5232284 1.59743504 6.5169869 2.85265588l-.1369156 2.68051084c-.1689119 3.30706381 2.3838045 5.85059205 5.6821266 5.68212655l2.4248414-.1238514c1.2316948 2.1089793 2.1509034 4.4227644 2.694132 6.8778614l-1.6895515 1.525347c-2.457886 2.2190084-2.4513889 5.8225976 0 8.0357404l1.5602089 1.408575c-.6165867 2.5023605-1.6256955 4.8502225-2.9582811 6.9745407l-2.0313497-.1037588c-3.3070638-.1689119-5.850592 2.3838045-5.6821266 5.6821266l.1037537 2.0313529c-2.1243131 1.3325824-4.4721751 2.3416912-6.9745356 2.9582779l-1.408575-1.5602089c-2.2190084-2.457886-5.8225976-2.4513889-8.0357404 0zm5.2082181-13.4333262c5.5228475 0 10-4.4771525 10-10s-4.4771525-10-10-10-10 4.4771525-10 10 4.4771525 10 10 10z"
+ fill-rule="evenodd"
+ transform="translate(4 4)"
+ id="path2"
+ style="fill:none;stroke:#333333;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none" />
+</svg>
diff --git a/examples/quick/quickshapes/weatherforecast/assets/sun-behind-cloud-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/sun-behind-cloud-svgrepo-com.svg
new file mode 100644
index 0000000000..790e64f824
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/sun-behind-cloud-svgrepo-com.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--noto" preserveAspectRatio="xMidYMid meet"><path d="M29.53 45.25c-7.14 9.17-7.02 25.48 2.11 33.55c10.36 9.17 24.93 9.08 34.1.64c10.99-10.11 10.36-26.95.64-36.48c-9.77-9.59-27.23-10.09-36.85 2.29z" fill="#fcc11a"></path><path d="M40.99 42.1c-2.82-1.25-5.91 2.15-7.26 4.48s-2.6 6.9.27 8.06s4.93-.81 6.99-4.21c2.06-3.4 3.22-6.89 0-8.33z" fill="#fee269"></path><path d="M48.34 29.31c-.68 1.28-.45 2.26.9 2.59c1.36.32 9.82 1.7 10.53 1.74c1.03.06 1.42-.71 1.29-1.62c-.13-.9-2.2-13.57-2.39-14.74c-.19-1.16-1.42-2.33-2.59-.78c-1.15 1.57-7.23 11.84-7.74 12.81z" fill="#ffa722"></path><path d="M70.25 38.1c-1.33.41-1.29 1.23-.58 2.26c.71 1.03 5.62 8.53 6.08 9.05s1.23.65 1.87-.13s8.72-12.09 9.31-12.8c.97-1.16.39-2.39-1.1-2c-1.14.3-14.74 3.37-15.58 3.62z" fill="#ffa722"></path><path d="M19.84 72.49c.9-1.49 2.07-1.1 2.91.32c.58.97 5.43 7.5 5.88 8.21c.45.71.65 2.07-.78 2.39c-1.42.32-12.99 3.36-14.8 3.68s-2.84-1.1-1.68-2.65c.68-.9 7.95-11.11 8.47-11.95z" fill="#ffa722"></path><path d="M17.38 61.24c1.65.74 2.43.47 2.78-.71c.45-1.55 1.81-8.66 1.87-10.28c.04-1.05-.39-2.07-2-1.81c-1.62.26-13.25 1.87-14.74 2.39c-1.73.6-1.6 2.5-.45 3.1c1.36.72 11.38 6.79 12.54 7.31z" fill="#ffa722"></path><path d="M37.48 34.1c.93-.74.65-1.81-.19-2.39c-.84-.58-11.22-8.09-12.54-9.11c-1.16-.9-2.59-.58-2.2 1.49c.28 1.48 3.59 14.48 3.81 15.38c.26 1.03 1.03 1.55 1.94.9c.91-.65 8.21-5.5 9.18-6.27z" fill="#ffa722"></path><path d="M109.13 81.18s1.45-28.45-21.58-34s-29.74 16.79-29.74 16.79s-7.68-1.79-14.8 4.13c-5.59 4.65-6.38 11.63-6.38 11.63s-7.35-1.12-13.47 4.82c-4.43 4.3-4 9.82-4 9.82l8.92 7.84l90.65-2.45l4.77-5.12s.76-5.68-4.54-9.95c-5.3-4.28-9.83-3.51-9.83-3.51z" fill="#e2ebee"></path><path d="M40.64 92.23s3.61 4.08 15.06 3.81s14.46-3.27 14.46-3.27s5.02 5.27 19.53 4.15s18.68-4.18 18.68-4.18s3.54 2.53 8.02 2.6c4.47.07 7.11-.71 7.11-.71s-.47 3.56-2.03 5.84c-1.56 2.28-4.16 4.02-8.27 3.99c-4.12-.03-16.64.08-29.35.06c-12.71-.02-49.03.19-54.4-.04c-4.09-.17-7.06-2.58-8.43-4.62c-2.19-3.26-1.83-6.7-1.83-6.7s4.12 3.23 9.68 2.99c7.6-.32 11.77-3.92 11.77-3.92z" fill="#b9ced3"></path></svg> \ No newline at end of file
diff --git a/examples/quick/quickshapes/weatherforecast/assets/sun-behind-large-cloud-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/sun-behind-large-cloud-svgrepo-com.svg
new file mode 100644
index 0000000000..dd83ecaa80
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/sun-behind-large-cloud-svgrepo-com.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--noto" preserveAspectRatio="xMidYMid meet"><path d="M29.12 39.22c-4.79 6.16-4.71 17.1 1.41 22.52c6.95 6.15 16.73 6.09 22.88.43c7.38-6.79 6.95-18.09.43-24.48c-6.55-6.44-18.26-6.78-24.72 1.53z" fill="#fcc11a"></path><path d="M36.81 37.11c-1.89-.84-3.97 1.44-4.87 3.01c-.9 1.56-1.74 4.63.18 5.41s3.31-.54 4.69-2.83c1.39-2.29 2.17-4.63 0-5.59z" fill="#fee269"></path><path d="M41.74 28.53c-.46.86-.3 1.52.61 1.73c.91.22 6.59 1.14 7.07 1.17c.69.04.95-.48.87-1.08s-1.47-9.11-1.6-9.89c-.13-.78-.95-1.56-1.73-.52s-4.87 7.94-5.22 8.59z" fill="#ffa722"></path><path d="M56.45 34.42c-.89.28-.87.82-.39 1.52c.48.69 3.77 5.72 4.08 6.07c.3.35.82.43 1.26-.09c.43-.52 5.85-8.11 6.25-8.59c.65-.78.26-1.6-.74-1.34c-.77.2-9.9 2.26-10.46 2.43z" fill="#ffa722"></path><path d="M22.62 57.5c.61-1 1.39-.74 1.95.22c.39.65 3.64 5.03 3.95 5.51c.3.48.43 1.39-.52 1.6c-.95.22-8.72 2.26-9.93 2.47c-1.21.22-1.91-.74-1.13-1.78c.45-.61 5.33-7.46 5.68-8.02z" fill="#ffa722"></path><path d="M20.97 49.95c1.11.49 1.63.32 1.86-.48c.3-1.04 1.21-5.81 1.26-6.9c.03-.71-.26-1.39-1.34-1.21c-1.08.17-8.89 1.26-9.89 1.6c-1.16.4-1.08 1.68-.3 2.08c.91.49 7.63 4.56 8.41 4.91z" fill="#ffa722"></path><path d="M34.46 31.74c.62-.5.43-1.21-.13-1.6c-.56-.39-7.53-5.43-8.41-6.12c-.78-.61-1.73-.39-1.47 1c.19 1 2.41 9.72 2.56 10.32c.17.69.69 1.04 1.3.61c.6-.44 5.5-3.69 6.15-4.21z" fill="#ffa722"></path><path d="M107.33 76.86c-.14-.46 4.52-33.89-25.85-38.83c-27.2-4.42-33.1 20-33.1 20s-10.33-1.73-17.76 5.61c-6.13 6.05-6.93 12.86-6.93 12.86S16.1 75 9.46 80.07C2.78 85.17 4.41 90.5 4.41 90.5l15.36 10.62l90.55-2.72l12.86-3.44s2.21-7.81-5.25-13.8c-6.02-4.83-10.6-4.3-10.6-4.3z" fill="#e2ebee"></path><path d="M30.29 90.24s-5.41 3.54-14.13 2.6c-9.99-1.08-11.82-4.86-11.82-4.86s-1.09 7.4 4.93 12.39c5.44 4.51 12.29 3.35 21.83 3.41s73.63-.03 79.69.03c4.96.05 8.97-1.5 11.09-5.63c2.01-3.9 1.55-6.02 1.55-6.02s-3.61 2.24-11.47 1.32c-6.79-.79-10.11-3.53-10.11-3.53s-8.63 4.42-23.04 4.24c-15.1-.19-20.23-5.73-20.23-5.73s-4.9 4.64-15.67 4.37s-12.62-2.59-12.62-2.59z" fill="#b9ced3"></path></svg> \ No newline at end of file
diff --git a/examples/quick/quickshapes/weatherforecast/assets/sun-behind-rain-cloud-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/sun-behind-rain-cloud-svgrepo-com.svg
new file mode 100644
index 0000000000..5f67942417
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/sun-behind-rain-cloud-svgrepo-com.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--noto" preserveAspectRatio="xMidYMid meet"><path d="M38.29 29.14c-6.01 7.73-5.91 21.47 1.78 28.27c8.73 7.72 21.01 7.65 28.74.54c9.26-8.52 8.73-22.71.54-30.74c-8.24-8.08-22.95-8.5-31.06 1.93z" fill="#fcc11a"></path><path d="M47.95 26.49c-2.38-1.06-4.98 1.81-6.12 3.78c-1.13 1.96-2.19 5.81.23 6.8c2.42.98 4.15-.68 5.89-3.55c1.74-2.88 2.72-5.82 0-7.03z" fill="#fee269"></path><path d="M54.14 15.71c-.58 1.08-.38 1.91.76 2.18c1.14.27 8.28 1.43 8.88 1.47c.87.05 1.2-.6 1.09-1.36c-.11-.76-1.85-11.44-2.02-12.42c-.16-.98-1.2-1.96-2.18-.65s-6.09 9.96-6.53 10.78z" fill="#ffa722"></path><path d="M72.6 23.12c-1.12.35-1.09 1.03-.49 1.91c.6.87 4.74 7.19 5.12 7.62c.38.44 1.03.54 1.58-.11s7.35-10.19 7.84-10.78c.82-.98.33-2.02-.93-1.69c-.94.25-12.41 2.83-13.12 3.05z" fill="#ffa722"></path><path d="M30.12 52.09c.76-1.25 1.74-.93 2.45.27c.48.82 4.57 6.32 4.96 6.92c.38.6.54 1.74-.65 2.02c-1.2.27-10.95 2.83-12.47 3.1s-2.4-.93-1.42-2.23c.57-.77 6.7-9.37 7.13-10.08z" fill="#ffa722"></path><path d="M28.06 42.61c1.39.62 2.05.4 2.34-.6c.38-1.31 1.52-7.3 1.58-8.66c.04-.89-.33-1.74-1.69-1.52s-11.16 1.58-12.42 2.02c-1.45.51-1.35 2.11-.38 2.61c1.14.6 9.59 5.72 10.57 6.15z" fill="#ffa722"></path><path d="M44.99 19.74c.78-.63.54-1.52-.16-2.02c-.71-.49-9.46-6.82-10.57-7.68c-.98-.76-2.18-.49-1.85 1.25c.23 1.25 3.02 12.21 3.21 12.96c.22.87.87 1.31 1.63.76c.77-.53 6.93-4.62 7.74-5.27z" fill="#ffa722"></path><path d="M99.22 65.16s3.54-23.47-17.6-29.03C58.67 30.09 53.29 51.4 53.29 51.4s-6.68-1.82-12.85 3.14c-4.83 3.88-5.66 10.72-5.66 10.72s-9.41-.32-13.45 6.17c-3.84 6.17 0 10.22 0 10.22l55.54-2.23l34.19.2s2.47-6.42-2.28-10.87c-4.76-4.44-9.56-3.59-9.56-3.59z" fill="#e2ebee"></path><path d="M61.29 67.49s4.25 4.25 15.38 3.54s16.99-4.65 18.31-5.26c1.32-.61 2.83-2.02 3.34-1.01c.51 1.01-.61 3.84-2.12 5.87c-1.52 2.02-2.37 3.25-2.63 4.15c-.2.71 3.14 1.72 9.41 2.02c5.36.26 8.74-.82 8.74-.82s.15 3.37-1.16 6c-1.22 2.43-2.49 3.22-4.51 3.63c-1.04.21-20.67.2-37.56.6c-16.89.4-38.95 1.11-42.69-.1s-4.66-3.58-5.14-4.5c-.84-1.58-1.02-4.05-1.02-4.05s4.14 2.08 10.01 1.27s9-3.84 9.81-4.15c.81-.3 4.65 1.62 10.72.1c8.48-2.13 11.11-7.29 11.11-7.29z" fill="#b9ced3"></path><path d="M47.26 90.91c-1.32-.48-7.05 3.63-9.11 9.11c-3.04 8.09 7.79 10.79 10.07 3c.87-2.96.86-11.45-.96-12.11z" fill="#4fc2f7"></path><path d="M90.07 96.81c-1.17-.52-5.82 3.02-7.93 5.99c-2.02 2.84-3.82 7.64 1.14 10.1c5.41 2.67 7.87-3.14 8.1-7.36c.21-3.82-.28-8.27-1.31-8.73z" fill="#4fc2f7"></path><path d="M66.52 106.4c-1.47-.35-7.11 4.23-9.13 8.5c-4.05 8.56 8.39 11.58 9.93 3.37c.98-5.21.4-11.59-.8-11.87z" fill="#4fc2f7"></path><path d="M61.24 113.05c-1.17-.47-3.45 2.4-3.2 4.95c.26 2.55 2.36 2.78 3.36 1.37c1.34-1.87 1-5.86-.16-6.32z" fill="#afe6f8"></path><path d="M41.68 97.56c-1.11-.52-3.01 2.52-2.99 4.09c.05 3.2 2.41 3.41 3.51 1.83c1.1-1.57 1.05-5.18-.52-5.92z" fill="#afe6f8"></path><path d="M84.83 103.77c-1.32-.63-3.59 2.28-3.42 4.39c.3 3.64 3.19 3.42 4.16 1.43s.45-5.25-.74-5.82z" fill="#afe6f8"></path></svg> \ No newline at end of file
diff --git a/examples/quick/quickshapes/weatherforecast/assets/sun-behind-small-cloud-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/sun-behind-small-cloud-svgrepo-com.svg
new file mode 100644
index 0000000000..d9f0a6bcc4
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/sun-behind-small-cloud-svgrepo-com.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--noto" preserveAspectRatio="xMidYMid meet"><path d="M33.58 45.38c-8.27 10.63-8.13 29.53 2.44 38.89c12.01 10.63 28.9 10.52 39.53.74c12.75-11.72 12.01-31.24.75-42.29c-11.32-11.11-31.56-11.69-42.72 2.66z" fill="#fcc11a"></path><path d="M46.87 41.73c-3.27-1.45-6.85 2.49-8.41 5.19c-1.56 2.7-3.01 8 .31 9.35c3.32 1.35 5.71-.93 8.1-4.88s3.74-8 0-9.66z" fill="#fee269"></path><path d="M55.39 26.91c-.79 1.49-.52 2.62 1.05 3c1.57.37 11.39 1.97 12.21 2.02c1.2.07 1.65-.82 1.5-1.87c-.15-1.05-2.55-15.74-2.77-17.08s-1.65-2.7-3-.9c-1.35 1.79-8.39 13.71-8.99 14.83z" fill="#ffa722"></path><path d="M79.23 37.03c-1.48.64-1.33 1.58-.38 2.68s7.56 9.12 8.14 9.66c.59.54 1.5.59 2.14-.39s8.52-15.03 9.11-15.92c.97-1.46.15-2.8-1.52-2.17c-1.26.49-16.56 5.74-17.49 6.14z" fill="#ffa722"></path><path d="M80.19 86.84c1.19-.64 2.25-.15 2.55 1.2c.3 1.35 3.6 15.13 4.12 17.68c.26 1.28-1.2 2.32-2.4 1.57c-1.2-.75-14.22-9.83-15.06-10.56c-1.2-1.05-.37-2.14.22-2.7c1.2-1.12 9.44-6.59 10.57-7.19z" fill="#ffa722"></path><path d="M43.93 94.41c-1.51-.14-1.95 1.2-1.72 2.4c.22 1.2 2.47 16.4 2.62 17.53c.22 1.65 1.95 2.32 3.07.67c1.12-1.65 8.82-13.98 9.44-15.13c1.12-2.1.3-2.55-.82-3.07c-1.13-.53-11.02-2.25-12.59-2.4z" fill="#ffa722"></path><path d="M22.35 76.95c1.05-1.72 2.4-1.27 3.37.37c.67 1.13 6.29 8.69 6.82 9.51c.52.82.75 2.4-.9 2.77c-1.65.37-15.06 3.9-17.16 4.27s-3.3-1.27-1.95-3.07c.79-1.04 9.23-12.87 9.82-13.85z" fill="#ffa722"></path><path d="M19.5 63.92c1.92.85 2.82.54 3.22-.82c.52-1.8 2.1-10.04 2.17-11.91c.05-1.22-.45-2.4-2.32-2.1S7.22 51.26 5.5 51.86c-2 .7-1.86 2.9-.52 3.6c1.56.82 13.18 7.86 14.52 8.46z" fill="#ffa722"></path><path d="M42.8 32.45c1.08-.86.75-2.1-.22-2.77c-.97-.67-13.01-9.38-14.53-10.56c-1.35-1.05-3-.67-2.55 1.72c.32 1.72 4.16 16.79 4.42 17.83c.3 1.2 1.2 1.8 2.25 1.05s9.51-6.37 10.63-7.27z" fill="#ffa722"></path><path d="M112.86 68.59c.01-.65 2.21-23.08-17.49-27.23c-19.04-4.02-24.03 13.82-24.03 13.82s-6.75-.83-11.7 2.93c-4.95 3.77-5.23 9.98-5.23 9.98s-7.96-.3-11.71 4.13c-3.39 4-2.26 7.61-2.26 7.61l21.97 5.57l55.53-1.49l6.37-3.86s.36-5.05-2.93-8.25c-4.54-4.4-8.52-3.21-8.52-3.21z" fill="#e2ebee"></path><path d="M61.29 77.3s-6.09 2.68-13.63 2.02c-4.48-.39-7.34-1.72-7.34-1.72s-.77 5.18 3.3 7.73c3.05 1.91 5.96 2.2 11.38 2.18s54.74-.12 58.29-.16c3.56-.04 6.94-.67 8.67-2.28c2.18-2.03 2.34-5.02 2.34-5.02s-3.42 1.02-8.18.4c-4.74-.62-7.5-2.74-7.5-2.74s-3.41 2.91-21.04 3.23c-19.02.35-26.29-3.64-26.29-3.64z" fill="#bacdd2"></path></svg> \ No newline at end of file
diff --git a/examples/quick/quickshapes/weatherforecast/assets/sun-svgrepo-com.svg b/examples/quick/quickshapes/weatherforecast/assets/sun-svgrepo-com.svg
new file mode 100644
index 0000000000..b5a51c8b21
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/assets/sun-svgrepo-com.svg
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+
+<svg
+ width="800px"
+ height="800px"
+ viewBox="0 0 128 128"
+ aria-hidden="true"
+ role="img"
+ class="iconify iconify--noto"
+ preserveAspectRatio="xMidYMid meet"
+ version="1.1"
+ id="svg1524"
+ sodipodi:docname="sun-svgrepo-com.svg"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1528" />
+ <sodipodi:namedview
+ id="namedview1526"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.805"
+ inkscape:cx="399.37888"
+ inkscape:cy="400"
+ inkscape:window-width="3840"
+ inkscape:window-height="2065"
+ inkscape:window-x="1200"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg1524" />
+ <path
+ d="M37.41 41.95c-9.71 12.48-9.54 34.65 2.87 45.64c14.09 12.47 33.92 12.34 46.39.87c14.95-13.76 14.09-36.66.87-49.63c-13.29-13.04-37.04-13.72-50.13 3.12z"
+ fill="#fcc11a"
+ id="path1504"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M53 37.67c-3.84-1.7-8.04 2.93-9.87 6.09c-1.83 3.17-3.53 9.38.37 10.97c3.9 1.58 6.7-1.1 9.51-5.73c2.79-4.63 4.38-9.38-.01-11.33z"
+ fill="#fee269"
+ id="path1506" />
+ <path
+ d="M63 20.27c-.93 1.74-.62 3.08 1.23 3.52c1.85.44 13.36 2.31 14.33 2.37c1.41.09 1.93-.97 1.76-2.2c-.18-1.23-2.99-18.46-3.25-20.04S75.14.76 73.55 2.87S63.7 18.96 63 20.27z"
+ fill="#ffa722"
+ id="path1508"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M92.8 32.23c-1.81.56-1.76 1.67-.79 3.08c.97 1.41 7.65 11.6 8.26 12.31c.62.7 1.67.88 2.55-.18c.88-1.05 11.86-16.45 12.66-17.41c1.32-1.58.53-3.25-1.49-2.73c-1.54.41-20.05 4.58-21.19 4.93z"
+ fill="#ffa722"
+ id="path1510"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M106.6 61.86c-1.3-.74-2.99-.53-3.43 1.14c-.44 1.67-2.37 13.8-2.55 14.86s.62 2.11 1.93 1.85s19.45-2.95 20.66-3.25c2.11-.53 2.81-2.64.62-4.22c-1.42-1.03-16-9.68-17.23-10.38z"
+ fill="#ffa722"
+ id="path1512"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M92.09 90.6c1.4-.75 2.64-.18 2.99 1.41c.35 1.58 4.22 17.76 4.84 20.75c.31 1.5-1.41 2.73-2.81 1.85c-1.41-.88-16.69-11.53-17.67-12.4c-1.41-1.23-.43-2.51.26-3.16c1.4-1.33 11.07-7.74 12.39-8.45z"
+ fill="#ffa722"
+ id="path1514"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M49.54 99.48c-1.77-.17-2.29 1.41-2.02 2.81c.26 1.41 2.9 19.24 3.08 20.57c.26 1.93 2.29 2.73 3.6.79s10.35-16.4 11.08-17.76c1.32-2.46.35-2.99-.97-3.6c-1.31-.61-12.92-2.63-14.77-2.81z"
+ fill="#ffa722"
+ id="path1516"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M24.23 79c1.23-2.02 2.81-1.49 3.96.44c.78 1.32 7.38 10.2 8 11.16c.62.97.88 2.81-1.05 3.25c-1.95.45-17.68 4.58-20.14 5.02c-2.46.44-3.87-1.49-2.29-3.6c.92-1.24 10.82-15.12 11.52-16.27z"
+ fill="#ffa722"
+ id="path1518"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M20.89 63.7c2.25 1 3.31.64 3.78-.97c.62-2.11 2.46-11.78 2.55-13.98c.06-1.43-.53-2.81-2.73-2.46S6.47 48.85 4.45 49.55c-2.35.82-2.18 3.4-.62 4.22c1.85.97 15.47 9.23 17.06 9.93z"
+ fill="#ffa722"
+ id="path1520"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M48.23 26.78c1.27-1.01.88-2.46-.26-3.25c-1.14-.79-15.26-11-17.05-12.4c-1.58-1.23-3.52-.79-2.99 2.02c.38 2.02 4.88 19.7 5.19 20.92c.35 1.41 1.41 2.11 2.64 1.23c1.21-.87 11.15-7.46 12.47-8.52z"
+ fill="#ffa722"
+ id="path1522"
+ style="stroke:#b0ab9d;stroke-opacity:1;stroke-width:0.888;stroke-miterlimit:4;stroke-dasharray:none" />
+</svg>
diff --git a/examples/quick/quickshapes/weatherforecast/doc/images/qml-weatherforecast-example.png b/examples/quick/quickshapes/weatherforecast/doc/images/qml-weatherforecast-example.png
new file mode 100644
index 0000000000..2ebd68f768
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/doc/images/qml-weatherforecast-example.png
Binary files differ
diff --git a/examples/quick/quickshapes/weatherforecast/doc/images/settings.png b/examples/quick/quickshapes/weatherforecast/doc/images/settings.png
new file mode 100644
index 0000000000..80788dda6d
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/doc/images/settings.png
Binary files differ
diff --git a/examples/quick/quickshapes/weatherforecast/doc/images/zoomed.png b/examples/quick/quickshapes/weatherforecast/doc/images/zoomed.png
new file mode 100644
index 0000000000..22bb3b9e24
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/doc/images/zoomed.png
Binary files differ
diff --git a/examples/quick/quickshapes/weatherforecast/doc/src/weatherforecast.qdoc b/examples/quick/quickshapes/weatherforecast/doc/src/weatherforecast.qdoc
new file mode 100644
index 0000000000..acc625ece1
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/doc/src/weatherforecast.qdoc
@@ -0,0 +1,125 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \title Weather Forecast Example
+ \example quickshapes/weatherforecast
+ \image qml-weatherforecast-example.png
+ \brief A Qt Quick Shapes example demonstrating different ways to uses shapes in an application.
+ \ingroup qtquickexamples
+ \examplecategory {Graphics}
+
+ This example is designed to look like a weather forecast app for Europe. It demonstrates
+ a few different ways the \l{QtQuick.Shapes} API might be useful in an application, and how
+ different properties can affect the quality and performance of the rendering.
+
+ All graphics in the example originated as \c .svg files which were converted to QML
+ using the \l{svgtoqml} tool.
+
+ \section1 Different Types of Shapes
+
+ The \l{QtQuick.Shapes} module provides APIs to render vector graphics in a Qt Quick
+ application. This is an alternative to using a raster \l{Image} for 2D graphics and is useful
+ if the target size of the image is not known up front. With an \l{Image}, the rasterized pixmap
+ is precreated at a specific size and scaling the image will either add or subtract information.
+ This will result in pixelation or blurring, depending on whether its \c smooth property is set.
+
+ With \l{QtQuick.Shapes} the shapes are described mathematically, as a set of curves and line
+ segments, and thus they can be scaled arbitrarily without introducing artifacts.
+
+ \image qml-weatherforecast-example.png
+
+ In the Weather Forecast example, there are three main uses of shapes:
+
+ \list
+ \li User interface icons, such as weather symbols and the cog wheel on the "Settings" button.
+ \li The overview map of Europe.
+ \li Country name labels visible when zooming into the map.
+ \endlist
+
+ \section2 User Interface Icons
+
+ While using appropriately sized raster images for icons is usually preferred for performance
+ reasons, it may be inconvenient if the same application is targeting many different screen
+ sizes. In order for the icons to look crisp on all screens, you have to pregenerate a large
+ set of different sizes. \l{QtQuick.Shapes} makes it possible for the application to do this
+ adaptation at runtime, by including the icons as shapes and rendering them at a size appropriate
+ for the current screen size.
+
+ When running the Weather Forecast example on a desktop system, this can be seen by making the
+ application window larger or smaller. The weather symbols adapt to the window size without
+ reducing quality.
+
+ In addition, animated effects, such as zooms, can be added without quality loss. This can be
+ seen by hovering over the weather symbols.
+
+ So for any use case where the target size is unknown, or the number of possibilities is so large
+ that creating specific pixmaps for all would be impractical, using shapes for icons can be
+ useful and worth the performance cost of rasterizing the shapes at runtime.
+
+ \section2 Map of Europe
+
+ The Weather Forecast example also uses a vectorized map of Europe. This makes it easy to
+ create effects such as zooming into the map when we want to emphasize a specific country. In
+ addition, since each country is a separate sub-shape, we can apply different fills and strokes
+ to them independently.
+
+ Click on the map to zoom into a country. The scale will be adjusted so that the country shape
+ fills the window and the fill color will be slightly darker.
+
+ \image zoomed.png
+
+ \note The weather symbols on the zoomed map are the same assets as in the original view, but at
+ a much larger size.
+
+ \section2 Country Name Labels
+
+ When zooming on a country, its name appears across the country shape. This label is also created
+ with \l{QtQuick.Shapes}, by using the \l{PathText} component type which converts a string into
+ renderable shape.
+
+ This is not the typical way to create text labels in Qt Quick. For most purposes, using the
+ \l{Text} element is the most efficient and convenient approach. However, there are certain
+ visualization effects supported by \l{QtQuick.Shapes} which is not supported by the regular
+ \l{Text} element. Fill gradients is one such effect.
+
+ \snippet quickshapes/weatherforecast/main.qml textShape
+
+ To illustrate this, the Weather Forecast example applies a subtle radial gradient to the opacity
+ of the country labels, making them seem to blend into the map on the edges.
+
+ \section1 Different Types of Renderers
+
+ The Weather Forecast example illustrates multiple different use cases for \l{QtQuick.Shapes}.
+ In addition, it can be used to see the upsides and downsides of the different renderers the
+ module supports.
+
+ Click on the cog wheel in the upper left corner to open a settings panel, where different
+ approaches to rasterization can be selected.
+
+ \image settings.png
+
+ \l{QtQuick.Shapes} supports two different renderers, selectable using the
+ \l{ QtQuick.Shapes::Shape::preferredRendererType}{preferredRendererType} property. The preferred
+ renderer in the example is \c CurveRenderer. This supports hardware-accelerated curves and
+ built-in antialiasing. It is not the default renderer in \l{QtQuick.Shapes}, but in the example
+ it is automatically selected on startup.
+
+ The default renderer in Qt Quick Shapes is the \c GeometryRenderer. This flattens the curves of
+ the shape into very small straight lines and draws the shape as a polygon. Redrawing the shape
+ is more efficient than with the \c CurveRenderer, but at certain scales, the fact that curves
+ are approximated by lines will be visible.
+
+ In addition, the \c GeometryRenderer does not support antialiasing, so you will typically want
+ to enable multi-sampling when using it. This can be done by rendering the shapes into layers and
+ enabling multi-sampling on these. Since we will render the shapes into off-screen buffers in
+ this mode, it consumes additional graphics memory as well as an additional rendering pass for
+ each shape.
+
+ \snippet quickshapes/weatherforecast/main.qml msaa
+
+ The settings panel also provides the option to use the \c GeometryRenderer together with 4x
+ multi-sampling, so that the quality of this can be compared to the built-in antialiasing of
+ the \c CurveRenderer.
+
+ \include examples-run.qdocinc
+*/
diff --git a/examples/quick/quickshapes/weatherforecast/main.cpp b/examples/quick/quickshapes/weatherforecast/main.cpp
new file mode 100644
index 0000000000..52264b8a6e
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/main.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+#endif
+ QGuiApplication app(argc, argv);
+ app.setOrganizationName("QtProject");
+
+ QQmlApplicationEngine engine;
+ const QUrl url(QStringLiteral("qrc:/qt/qml/weatherforecast/main.qml"));
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ &app, [url](QObject *obj, const QUrl &objUrl) {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ }, Qt::QueuedConnection);
+ engine.load(url);
+
+ return app.exec();
+}
diff --git a/examples/quick/quickshapes/weatherforecast/main.qml b/examples/quick/quickshapes/weatherforecast/main.qml
new file mode 100644
index 0000000000..5ba8ad5c2a
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/main.qml
@@ -0,0 +1,358 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Window {
+ id: mainWindow
+
+ property real designWindowWidthRatio: topLevel.zoomRect.width / 997
+ property real designWindowHeightRatio: topLevel.zoomRect.height / 1024
+ property int preferredRendererType: curveRendererCheckBox.checked ? Shape.CurveRenderer : Shape.GeometryRenderer
+
+ title: qsTr("Weather Forecast")
+ width: 1280
+ height: 960
+ visible: true
+ color: "white"
+
+
+ FontLoader {
+ id: workSansRegular
+ source: "assets/WorkSans-Regular.ttf"
+ }
+
+ Rectangle {
+ id: bgFill
+ color: "#9FCBF9"
+ anchors.fill: parent
+ }
+
+ Item {
+ id: mapContainer
+ anchors.left: !settingsDrawer.isLandscape ? parent.left : settingsDrawer.right
+ anchors.top: !settingsDrawer.isLandscape ? settingsDrawer.bottom : parent.top
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ layer.enabled: msaaCheckBox.checked
+ layer.samples: 4
+
+ Item {
+ property rect selectedRect
+ property rect zoomTarget: Qt.rect(0, 0, mapshape.width, mapshape.height)
+ property rect zoomRect: zoomTarget
+ property real zoomScale: Math.min(width / zoomRect.width, height / zoomRect.height)
+ property real subScale: 1
+ property rect overlayRect
+ property real overlayRotation: 0
+
+ anchors.fill: parent
+ anchors.margins: 10
+ id: topLevel
+ transformOrigin: Item.TopLeft
+ scale: zoomScale > 0 ? zoomScale : 1
+ Behavior on zoomRect { PropertyAnimation { duration: 500 } }
+
+ Europe {
+ id: mapshape
+ x: (topLevel.width / topLevel.scale - topLevel.zoomRect.width) / 2 - topLevel.zoomRect.x
+ y: (topLevel.height / topLevel.scale - topLevel.zoomRect.height) / 2 - topLevel.zoomRect.y
+ onZoomTo: (child, name, childRect, textRect) => {
+ if (!child) {
+ topLevel.zoomTarget = Qt.rect(0, 0, mapshape.width, mapshape.height)
+ return
+ }
+ let x_in = childRect.x
+ let y_in = childRect.y
+ let w_in = childRect.width
+ let h_in = childRect.height
+
+ // try to center the target in the map
+ var x, y, w, h
+ let ar_in = h_in / w_in
+ let ar = height / width
+ if (ar_in < ar) {
+ // in rect too short
+ h = w_in * ar
+ y = y_in - (h - h_in) / 2
+ x = x_in
+ w = w_in
+ } else {
+ // in rect too narrow
+ w = h_in / ar
+ x = x_in - (w - w_in) / 2
+ y = y_in
+ h = h_in
+ }
+ topLevel.selectedRect = Qt.rect(x_in, y_in, w_in, h_in)
+ topLevel.subScale = Math.min(mapshape.width / w, mapshape.height / h)
+ topLevel.zoomTarget = Qt.rect(x, y, w, h)
+ if (textRect) {
+ topLevel.overlayRect = Qt.rect(textRect.x, textRect.y, textRect.width, textRect.height)
+ topLevel.overlayRotation = textRect.rotation
+ } else {
+ topLevel.overlayRect = topLevel.zoomTarget
+ topLevel.overlayRotation = 0
+ }
+ textLayer.label = name
+ }
+ Item {
+ id: globalLayer
+ anchors.fill: parent
+ opacity: mapshape.zoomedIn ? 0 : 1
+ z: 2
+ Behavior on opacity {
+ NumberAnimation { duration: 500 }
+ }
+
+ Repeater {
+ model: ListModel {
+ ListElement {
+ itemSource: "Cloud.qml"
+ temperature: 22
+ placeName: "Oslo"
+ itemX: 0.440
+ itemY: 0.320
+ }
+
+ ListElement {
+ itemSource: "CloudWithLightning.qml"
+ placeName: "Helsinki"
+ temperature: 20
+ itemX: 0.615
+ itemY: 0.300
+ }
+
+ ListElement {
+ itemSource: "SunBehindCloud.qml"
+ placeName: "Berlin"
+ temperature: 24
+ itemX: 0.490
+ itemY: 0.540
+ }
+
+ ListElement {
+ itemSource: "SunBehindCloud.qml"
+ placeName: "London"
+ temperature: 22
+ itemX: 0.249
+ itemY: 0.550
+ }
+ ListElement {
+ itemSource: "SunBehindCloud.qml"
+ placeName: "Paris"
+ temperature: 22
+ itemX: 0.293
+ itemY: 0.630
+ }
+ ListElement {
+ itemSource: "SunBehindCloud.qml"
+ placeName: "Rome"
+ temperature: 22
+ itemX: 0.496
+ itemY: 0.827
+ }
+ ListElement {
+ itemSource: "SunBehindCloud.qml"
+ placeName: "Budapest"
+ temperature: 22
+ itemX: 0.612
+ itemY: 0.666
+ }
+ ListElement {
+ itemSource: "SunBehindCloud.qml"
+ placeName: "Sofia"
+ temperature: 22
+ itemX: 0.719
+ itemY: 0.781
+ }
+ ListElement {
+ itemSource: "SunBehindCloud.qml"
+ placeName: "Kyiv"
+ temperature: 22
+ itemX: 0.796
+ itemY: 0.543
+ }
+
+
+ ListElement {
+ itemSource: "Sun.qml"
+ placeName: "Madrid"
+ temperature: 28
+ itemX: 0.140
+ itemY: 0.830
+ }
+
+ ListElement {
+ itemSource: "CloudWithSnow.qml"
+ placeName: "Reykjavik"
+ temperature: 12
+ itemX: 0.050
+ itemY: 0.120
+ }
+ }
+
+ delegate: BouncyShape {
+ x: itemX * mapshape.width
+ y: itemY * mapshape.height
+ amount: 1.5
+ MapLabel {
+ iconSource: itemSource
+ label: placeName
+ degrees: temperature
+ }
+ }
+ }
+
+ } // end globalLayer
+ } // end Europe
+
+ Item {
+ id: textLayer
+ opacity: mapshape.zoomedIn ? 1 : 0
+ z: 2
+ x: mapshape.x + topLevel.overlayRect.x
+ y: mapshape.y + topLevel.overlayRect.y
+ width: topLevel.overlayRect.width
+ height: topLevel.overlayRect.height
+ rotation: topLevel.overlayRotation
+ property string label: ""
+ property real localScale: 10 / topLevel.subScale
+
+ Behavior on opacity {
+ NumberAnimation { duration: 800}
+ }
+
+ Item {
+ anchors.fill: parent
+ DemoShape {
+ id: textShape
+ property real s: Math.min(parent.width / boundingRect.width,
+ parent.height / boundingRect.height)
+
+ anchors.centerIn: parent
+ scale: Number.isNaN(s) ? 1 : 0.5 * s
+
+//! [textShape]
+ ShapePath {
+ strokeColor: "transparent"
+ strokeWidth: 1
+ joinStyle: ShapePath.RoundJoin
+ fillRule: ShapePath.WindingFill
+
+ fillGradient: RadialGradient {
+ centerX: textShape.width / 2
+ centerY: textShape.height / 2
+ centerRadius: textShape.width / 2
+ focalX: centerX; focalY: centerY
+ GradientStop { position: 0; color: "#b0ab9d7f" }
+ GradientStop { position: 1; color: "#5cab9d7f" }
+ }
+ PathText {
+ id: textPath
+ text: textLayer.label
+ font.family: workSansRegular.font.family
+ font.pixelSize: 64
+ }
+ }
+//! [textShape]
+ }
+ }
+ }
+ Item {
+ id: localLayer
+ property real localScale: 10 / topLevel.subScale
+
+ opacity: mapshape.zoomedIn ? 1 : 0
+ z: 2
+ x: mapshape.x + topLevel.selectedRect.x
+ y: mapshape.y + topLevel.selectedRect.y
+ width: topLevel.selectedRect.width
+ height: topLevel.selectedRect.height
+
+ Item {
+ scale: localLayer.localScale
+ x: 2 * parent.width / 3
+ y: 2 * parent.height / 3
+ BouncyShape {
+ hoverEnabled: mapshape.zoomedIn
+ Sun {
+ width: 10
+ height: 10
+ }
+ }
+ }
+ Item {
+ scale: localLayer.localScale
+ x: parent.width / 10
+ y: parent.height / 10
+ BouncyShape {
+ hoverEnabled: mapshape.zoomedIn
+ CloudWithLightning {
+ width: 10
+ height: 10
+ }
+ }
+ }
+ Behavior on opacity { NumberAnimation{ duration: 800 } }
+ }
+ }
+ }
+
+ Rectangle {
+ id: gear
+ anchors.top: mapContainer.top
+ anchors.left: mapContainer.left
+ anchors.leftMargin: 30
+ anchors.topMargin: 30
+ radius: 8
+ width: 44
+ height: width
+ color: Qt.rgba(0, 0, 0, hoverHandler.hovered ? 0.21 : 0.07)
+//! [msaa]
+ layer.enabled: msaaCheckBox.checked
+ layer.samples: 4
+//! [msaa]
+
+ Gear {
+ anchors.centerIn: parent
+ width: 24
+ height: width
+ }
+
+ HoverHandler {
+ id: hoverHandler
+ }
+
+ TapHandler {
+ onTapped: (eventPoint, button) => {
+ settingsDrawer.isOpen = !settingsDrawer.isOpen
+ }
+ }
+ }
+
+ SettingsDrawer {
+ id: settingsDrawer
+ isLandscape: mainWindow.width > mainWindow.height
+ width: isLandscape ? implicitWidth : mainWindow.width
+ height: isLandscape ? mainWindow.height : mainWindow.height * 0.33
+
+ RadioButton {
+ id: curveRendererCheckBox
+ text: qsTr("Curve Renderer")
+ checked: true
+ }
+
+ RadioButton {
+ id: geometryRendererCheckBox
+ text: qsTr("Geometry Renderer")
+ }
+ RadioButton {
+ id: msaaCheckBox
+ text: qsTr("Geometry Renderer 4x MSAA")
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/weatherforecast/qt_attribution.json b/examples/quick/quickshapes/weatherforecast/qt_attribution.json
new file mode 100644
index 0000000000..6ea84fb1b0
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/qt_attribution.json
@@ -0,0 +1,55 @@
+[
+ {
+ "Id": "weather-icons",
+ "Name": "Weather icons from the Noto Emoji collection",
+ "QDocModule": "qtquick",
+ "QtUsage": "Used in the Weather Forecast Example",
+ "QtParts": [ "examples" ],
+ "Files": [ "assets/cloud-with-snow-svgrepo-com.svg", "CloudWithSnow_generated.qml",
+ "assets/cloud-svgrepo-com.svg", "Cloud_generated.qml",
+ "assets/cloud-with-lightning-and-rain-svgrepo-com.svg", "CloudWithLightningAndRain_generated.qml",
+ "assets/cloud-with-lightning-svgrepo-com.svg", "CloudWithLightning_generated.qml",
+ "assets/cloud-with-rain-svgrepo-com.svg", "CloudWithRain_generated.qml",
+ "assets/sun-svgrepo-com.svg", "Sun_generated.qml",
+ "assets/sun-behind-cloud-svgrepo-com.svg", "SunBehindCloud_generated.qml",
+ "assets/sun-behind-large-cloud-svgrepo-com.svg", "SunBehindLargeCloud_generated.qml",
+ "assets/sun-behind-rain-cloud-svgrepo-com.svg", "SunBehindRainCloud_generated.qml",
+ "assets/sun-behind-small-cloud-svgrepo-com.svg", "SunBehindSmallCloud_generated.qml" ],
+ "LicenseId": "Apache-2.0",
+ "License": "Apache License 2.0",
+ "Copyright": "Copyright (C) 2023 Googlefonts"
+ },
+ {
+ "Id": "map-of-europe",
+ "Name": "Map of Europe from Wikipedia",
+ "QDocModule": "qtquick",
+ "QtUsage": "Used in the Weather Forecast Example",
+ "QtParts": [ "examples" ],
+ "Files": [ "assets/Europe.svg", "Europe_generated.qml" ],
+ "LicenseId": "CC-BY-3.0",
+ "License": "Creative Commons Attribution 3.0 Unported",
+ "Copyright": "Copyright (C) 2010 Kolja21"
+ },
+ {
+ "Id": "work-sans-font",
+ "Name": "Work Sans font by Wei Huang",
+ "QDocModule": "qtquick",
+ "QtUsage": "Used in the Weather Forecast Example",
+ "QtParts": [ "examples" ],
+ "Files": "assets/WorkSans-Regular.ttf",
+ "LicenseId": "OFL-1.1",
+ "License": "SIL Open Font License 1.1",
+ "Copyright": "Copyright (C) 2020 Wei Huang"
+ },
+ {
+ "Id": "gear-icon",
+ "Name": "Gear Icon",
+ "QDocModule": "qtquick",
+ "QtUsage": "Used in the Weather Forecast Example",
+ "QtParts": [ "examples" ],
+ "Files": [ "assets/gear-alt-stroke.svg", "Gear_generated.qml" ],
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "Copyright": "Copyright (C) 2023 Framework7"
+ }
+]
diff --git a/examples/quickcontrols/sidepanel/qtquickcontrols2.conf b/examples/quick/quickshapes/weatherforecast/qtquickcontrols2.conf
index b6c7c87ea1..b6c7c87ea1 100644
--- a/examples/quickcontrols/sidepanel/qtquickcontrols2.conf
+++ b/examples/quick/quickshapes/weatherforecast/qtquickcontrols2.conf
diff --git a/examples/quick/quickshapes/weatherforecast/weatherforecast.pro b/examples/quick/quickshapes/weatherforecast/weatherforecast.pro
new file mode 100644
index 0000000000..6279bc7794
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/weatherforecast.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+
+QT += quick qml
+SOURCES += main.cpp
+RESOURCES += \
+ weatherforecast.qrc \
+ ../../shared/shared.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/quickshapes/weatherforecast
+INSTALLS += target
diff --git a/examples/quick/quickshapes/weatherforecast/weatherforecast.qrc b/examples/quick/quickshapes/weatherforecast/weatherforecast.qrc
new file mode 100644
index 0000000000..ccbdb417a1
--- /dev/null
+++ b/examples/quick/quickshapes/weatherforecast/weatherforecast.qrc
@@ -0,0 +1,33 @@
+<RCC>
+ <qresource prefix="/qt/qml/weatherforecast">
+ <file>main.qml</file>
+ <file>Europe.qml</file>
+ <file>Europe_generated.qml</file>
+ <file>BouncyShape.qml</file>
+ <file>Cloud.qml</file>
+ <file>Cloud_generated.qml</file>
+ <file>CloudWithLightning.qml</file>
+ <file>CloudWithLightning_generated.qml</file>
+ <file>CloudWithRain.qml</file>
+ <file>CloudWithRain_generated.qml</file>
+ <file>CloudWithSnow.qml</file>
+ <file>CloudWithSnow_generated.qml</file>
+ <file>Gear.qml</file>
+ <file>Gear_generated.qml</file>
+ <file>MapLabel.qml</file>
+ <file>SettingsDrawer.qml</file>
+ <file>Sun.qml</file>
+ <file>Sun_generated.qml</file>
+ <file>SunBehindCloud.qml</file>
+ <file>SunBehindCloud_generated.qml</file>
+ <file>SunBehindRainCloud.qml</file>
+ <file>SunBehindRainCloud_generated.qml</file>
+ <file>SunBehindSmallCloud.qml</file>
+ <file>SunBehindSmallCloud_generated.qml</file>
+ <file>DemoShape.qml</file>
+ <file>assets/WorkSans-Regular.ttf</file>
+ </qresource>
+ <qresource prefix="/">
+ <file>qtquickcontrols2.conf</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/CMakeLists.txt b/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/CMakeLists.txt
index 14813bc9ec..aa1cd2f0cc 100644
--- a/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/CMakeLists.txt
+++ b/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(qquickwidgetversuswindow_opengl LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/quickwidgets/qquickwidgetversuswindow_opengl")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickWidgets ShaderTools Widgets)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(qquickwidgetversuswindow_opengl
WIN32
@@ -24,11 +18,11 @@ qt_add_executable(qquickwidgetversuswindow_opengl
)
target_link_libraries(qquickwidgetversuswindow_opengl PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickWidgets
- Qt::Widgets
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickWidgets
+ Qt6::Widgets
)
qt_add_qml_module(qquickwidgetversuswindow_opengl
@@ -48,7 +42,16 @@ qt6_add_shaders(qquickwidgetversuswindow_opengl "shaders"
)
install(TARGETS qquickwidgetversuswindow_opengl
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET qquickwidgetversuswindow_opengl
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/doc/src/qquickwidgetversuswindow_opengl.qdoc b/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/doc/src/qquickwidgetversuswindow_opengl.qdoc
index 001d19f4e6..ced0eb2d34 100644
--- a/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/doc/src/qquickwidgetversuswindow_opengl.qdoc
+++ b/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/doc/src/qquickwidgetversuswindow_opengl.qdoc
@@ -5,6 +5,7 @@
\title QQuickWidget - QQuickView Comparison Example
\example quickwidgets/qquickwidgetversuswindow_opengl
\brief Demonstrates the benefits of QQuickWidget over a QQuickView embedded as a native window.
+ \examplecategory {User Interface Components}
\image qquickwidgetversuswindow-opengl-example.jpg
The example allows showing the same Qt Quick scene within the same
diff --git a/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/main.cpp b/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/main.cpp
index 8c95775577..8d8e4236b9 100644
--- a/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/main.cpp
+++ b/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/main.cpp
@@ -9,8 +9,6 @@
int main(int argc, char **argv)
{
- qputenv("QML_BAD_GUI_RENDER_LOOP", "1"); // QTBUG-39507
-
QApplication app(argc, argv);
// this example and QQuickWidget are only functional when rendering with OpenGL
diff --git a/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/wobble.frag b/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/wobble.frag
index 9fddf1a162..6aadad661f 100644
--- a/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/wobble.frag
+++ b/examples/quick/quickwidgets/qquickwidgetversuswindow_opengl/wobble.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
diff --git a/examples/quick/quickwidgets/quickwidget/CMakeLists.txt b/examples/quick/quickwidgets/quickwidget/CMakeLists.txt
index ceb8b39da1..7a878a2b28 100644
--- a/examples/quick/quickwidgets/quickwidget/CMakeLists.txt
+++ b/examples/quick/quickwidgets/quickwidget/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(quickwidget LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/quickwidgets/quickwidget")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickWidgets Widgets)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(quickwidget
WIN32
@@ -21,11 +15,11 @@ qt_add_executable(quickwidget
)
target_link_libraries(quickwidget PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickWidgets
- Qt::Widgets
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickWidgets
+ Qt6::Widgets
)
qt_add_qml_module(quickwidget
@@ -37,7 +31,16 @@ qt_add_qml_module(quickwidget
)
install(TARGETS quickwidget
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET quickwidget
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/quickwidgets/quickwidget/doc/src/quickwidget.qdoc b/examples/quick/quickwidgets/quickwidget/doc/src/quickwidget.qdoc
index 285acc5802..0a80b32c4a 100644
--- a/examples/quick/quickwidgets/quickwidget/doc/src/quickwidget.qdoc
+++ b/examples/quick/quickwidgets/quickwidget/doc/src/quickwidget.qdoc
@@ -5,6 +5,8 @@
\title Qt Quick Widgets Example
\example quickwidgets/quickwidget
\brief Demonstrates how to mix QML with a Qt Widgets application using the QQuickWidget class.
+ \examplecategory {User Interface Components}
+
\image qtquickwidgets-example.png
*/
diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp
index ecac279a2f..f8e0b78844 100644
--- a/examples/quick/quickwidgets/quickwidget/main.cpp
+++ b/examples/quick/quickwidgets/quickwidget/main.cpp
@@ -63,14 +63,22 @@ MainWindow::MainWindow()
setCentralWidget(centralWidget);
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
- fileMenu->addAction(tr("Grab framebuffer"), this, &MainWindow::grabFramebuffer);
- fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToPixmap);
- fileMenu->addAction(tr("Grab via grabToImage"), this, &MainWindow::grabToImage);
+ auto grabAction = fileMenu->addAction(tr("Grab framebuffer"), this, &MainWindow::grabFramebuffer);
+ auto renderAction = fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToPixmap);
+ auto grabToImageAction = fileMenu->addAction(tr("Grab via grabToImage"), this, &MainWindow::grabToImage);
fileMenu->addAction(tr("Quit"), qApp, &QCoreApplication::quit);
QMenu *windowMenu = menuBar()->addMenu(tr("&Window"));
windowMenu->addAction(tr("Add tab widget"), this,
[this, centralWidget] { createQuickWidgetsInTabs(centralWidget); });
+
+ connect(m_quickWidget, &QObject::destroyed, this,
+ [this, grabAction, renderAction, grabToImageAction] {
+ m_quickWidget = nullptr;
+ grabAction->setEnabled(false);
+ renderAction->setEnabled(false);
+ grabToImageAction->setEnabled(false);
+ });
}
void MainWindow::createQuickWidgetsInTabs(QMdiArea *mdiArea)
@@ -104,10 +112,12 @@ void MainWindow::createQuickWidgetsInTabs(QMdiArea *mdiArea)
if (widget->parent()) {
widget->setAttribute(Qt::WA_DeleteOnClose, true);
widget->setParent(nullptr);
+ connect(this, &QObject::destroyed, widget, &QWidget::close);
widget->show();
btn->setText(msgFromTopLevel);
} else {
widget->setAttribute(Qt::WA_DeleteOnClose, false);
+ disconnect(this, &QObject::destroyed, widget, &QWidget::close);
tabWidget->addTab(widget, widget->windowTitle());
btn->setText(msgToTopLevel);
}
@@ -123,6 +133,7 @@ void MainWindow::quickWidgetStatusChanged(QQuickWidget::Status status)
{
if (status == QQuickWidget::Error) {
QStringList errors;
+ Q_ASSERT(m_quickWidget);
const auto widgetErrors = m_quickWidget->errors();
for (const QQmlError &error : widgetErrors)
errors.append(error.toString());
@@ -147,12 +158,14 @@ template<class T> void saveToFile(QWidget *parent, T *saveable)
void MainWindow::grabFramebuffer()
{
+ Q_ASSERT(m_quickWidget);
QImage image = m_quickWidget->grabFramebuffer();
saveToFile(this, &image);
}
void MainWindow::renderToPixmap()
{
+ Q_ASSERT(m_quickWidget);
QPixmap pixmap(m_quickWidget->size());
m_quickWidget->render(&pixmap);
saveToFile(this, &pixmap);
@@ -165,6 +178,7 @@ void MainWindow::grabToImage()
fd.setDefaultSuffix("png");
fd.selectFile("test_grabToImage.png");
if (fd.exec() == QDialog::Accepted) {
+ Q_ASSERT(m_quickWidget);
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "performLayerBasedGrab",
Q_ARG(QVariant, fd.selectedFiles().first()));
}
@@ -189,8 +203,9 @@ int main(int argc, char **argv)
optMultipleSample = parser.isSet(multipleSampleOption);
- MainWindow mainWindow;
- mainWindow.show();
+ MainWindow *mainWindow = new MainWindow;
+ mainWindow->setAttribute(Qt::WA_DeleteOnClose, true);
+ mainWindow->show();
return app.exec();
}
diff --git a/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml b/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml
index 355c02f0cd..7fdc1978ce 100644
--- a/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml
+++ b/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml
@@ -31,6 +31,8 @@ Rectangle {
text.api = "OpenGL on QRhi";
else if (api === GraphicsInfo.Direct3D11)
text.api = "D3D11 on QRhi";
+ else if (api === GraphicsInfo.Direct3D12)
+ text.api = "D3D12 on QRhi";
else if (api === GraphicsInfo.Vulkan)
text.api = "Vulkan on QRhi";
else if (api === GraphicsInfo.Metal)
diff --git a/examples/quick/rendercontrol/CMakeLists.txt b/examples/quick/rendercontrol/CMakeLists.txt
index b0255dda6f..d58899ae69 100644
--- a/examples/quick/rendercontrol/CMakeLists.txt
+++ b/examples/quick/rendercontrol/CMakeLists.txt
@@ -1,7 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(rendercontrol_opengl)
if(WIN32)
qt_internal_add_example(rendercontrol_d3d11)
endif()
+if(TARGET Qt6::Widgets)
+ qt_internal_add_example(rendercontrol_rhi)
+endif()
diff --git a/examples/quick/rendercontrol/rendercontrol_d3d11/CMakeLists.txt b/examples/quick/rendercontrol/rendercontrol_d3d11/CMakeLists.txt
index ccdd6ed528..ac124d3fa5 100644
--- a/examples/quick/rendercontrol/rendercontrol_d3d11/CMakeLists.txt
+++ b/examples/quick/rendercontrol/rendercontrol_d3d11/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(rendercontrol_d3d11 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/rendercontrol/rendercontrol_d3d11")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(rendercontrol_d3d11example WIN32 MACOSX_BUNDLE
engine.cpp engine.h
main.cpp
@@ -21,10 +17,10 @@ qt_add_executable(rendercontrol_d3d11example WIN32 MACOSX_BUNDLE
)
target_link_libraries(rendercontrol_d3d11example PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
d3d11
dxgi
dxguid
@@ -34,13 +30,21 @@ target_link_libraries(rendercontrol_d3d11example PUBLIC
# Resources:
qt_add_qml_module(rendercontrol_d3d11example
URI rendercontrol
- AUTO_RESOURCE_PREFIX
QML_FILES
"demo.qml"
)
install(TARGETS rendercontrol_d3d11example
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET rendercontrol_d3d11example
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/rendercontrol/rendercontrol_d3d11/demo.qml b/examples/quick/rendercontrol/rendercontrol_d3d11/demo.qml
index 4591ef3c44..ef1fe3507a 100644
--- a/examples/quick/rendercontrol/rendercontrol_d3d11/demo.qml
+++ b/examples/quick/rendercontrol/rendercontrol_d3d11/demo.qml
@@ -9,7 +9,7 @@ Rectangle {
property bool keyDown: false
gradient: Gradient {
- GradientStop { position: 0; color: mouse.pressed ? "lightsteelblue" : (keyDown ? "blue" : "steelblue") }
+ GradientStop { position: 0; color: mouse.pressed ? "lightsteelblue" : (root.keyDown ? "blue" : "steelblue") }
GradientStop { position: 1; color: "black" }
}
diff --git a/examples/quick/rendercontrol/rendercontrol_d3d11/doc/src/rendercontrol_d3d11.qdoc b/examples/quick/rendercontrol/rendercontrol_d3d11/doc/src/rendercontrol_d3d11.qdoc
index ad9189e139..03c8816033 100644
--- a/examples/quick/rendercontrol/rendercontrol_d3d11/doc/src/rendercontrol_d3d11.qdoc
+++ b/examples/quick/rendercontrol/rendercontrol_d3d11/doc/src/rendercontrol_d3d11.qdoc
@@ -5,5 +5,7 @@
\title QQuickRenderControl D3D11 Example
\example rendercontrol/rendercontrol_d3d11
\brief Shows how to render a Qt Quick scene into a texture that is then used by a non-Quick based Direct3D 11 renderer.
+ \examplecategory {Graphics}
+
\image rendercontrol-d3d11-example.jpg
*/
diff --git a/examples/quick/rendercontrol/rendercontrol_d3d11/quad.frag b/examples/quick/rendercontrol/rendercontrol_d3d11/quad.frag
index 09914ccdbe..a72c43db0f 100644
--- a/examples/quick/rendercontrol/rendercontrol_d3d11/quad.frag
+++ b/examples/quick/rendercontrol/rendercontrol_d3d11/quad.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
struct PSIn
{
float2 coord : TEXCOORD0;
diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt b/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt
index 85399d8701..4ea0551c12 100644
--- a/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt
+++ b/examples/quick/rendercontrol/rendercontrol_opengl/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(rendercontrol_opengl LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/rendercontrol/rendercontrol_opengl")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(rendercontrol_openglexample WIN32 MACOSX_BUNDLE
cuberenderer.cpp cuberenderer.h
@@ -21,23 +15,31 @@ qt_add_executable(rendercontrol_openglexample WIN32 MACOSX_BUNDLE
)
target_link_libraries(rendercontrol_openglexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::OpenGL
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+ Qt6::Qml
+ Qt6::Quick
)
# Resources:
qt_add_qml_module(rendercontrol_openglexample
URI rendercontrol
- AUTO_RESOURCE_PREFIX
QML_FILES
"demo.qml"
)
install(TARGETS rendercontrol_openglexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET rendercontrol_openglexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp b/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp
index 430ccbbf5b..266b176e86 100644
--- a/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp
+++ b/examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.cpp
@@ -45,7 +45,6 @@ void CubeRenderer::init(QWindow *w, QOpenGLContext *share)
return;
QOpenGLFunctions *f = m_context->functions();
- f->glClearColor(0.0f, 0.1f, 0.25f, 1.0f);
f->glViewport(0, 0, w->width() * w->devicePixelRatio(), w->height() * w->devicePixelRatio());
static const char *vertexShaderSource =
@@ -148,6 +147,7 @@ void CubeRenderer::render(QWindow *w, QOpenGLContext *share, uint texture)
return;
QOpenGLFunctions *f = m_context->functions();
+ f->glClearColor(0.0f, 0.1f, 0.25f, 1.0f);
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (texture) {
diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/demo.qml b/examples/quick/rendercontrol/rendercontrol_opengl/demo.qml
index 2907281502..ca6ba7b2b4 100644
--- a/examples/quick/rendercontrol/rendercontrol_opengl/demo.qml
+++ b/examples/quick/rendercontrol/rendercontrol_opengl/demo.qml
@@ -9,7 +9,7 @@ Rectangle {
property bool keyDown: false
gradient: Gradient {
- GradientStop { position: 0; color: mouse.pressed ? "lightsteelblue" : (keyDown ? "blue" : "steelblue") }
+ GradientStop { position: 0; color: mouse.pressed ? "lightsteelblue" : (root.keyDown ? "blue" : "steelblue") }
GradientStop { position: 1; color: "black" }
}
diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol_opengl.qdoc b/examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol_opengl.qdoc
index a4f3fb4664..d4dc328081 100644
--- a/examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol_opengl.qdoc
+++ b/examples/quick/rendercontrol/rendercontrol_opengl/doc/src/rendercontrol_opengl.qdoc
@@ -5,6 +5,8 @@
\title QQuickRenderControl OpenGL Example
\example rendercontrol/rendercontrol_opengl
\brief Shows how to render a Qt Quick scene into a texture that is then used by a non-Quick based OpenGL renderer.
+ \examplecategory {Graphics}
+
\image rendercontrol-opengl-example.jpg
The Qt Quick scene is managed by a QQuickWindow that is not visible
diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/CMakeLists.txt b/examples/quick/rendercontrol/rendercontrol_rhi/CMakeLists.txt
new file mode 100644
index 0000000000..c38f26fcca
--- /dev/null
+++ b/examples/quick/rendercontrol/rendercontrol_rhi/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(rendercontrol_rhi LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick Widgets)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(rendercontrol_rhi WIN32 MACOSX_BUNDLE
+ main.cpp
+)
+
+target_link_libraries(rendercontrol_rhi PRIVATE
+ Qt6::Core
+ Qt6::GuiPrivate
+ Qt6::Quick
+ Qt6::Widgets
+)
+
+qt_add_qml_module(rendercontrol_rhi
+ URI rendercontrolrhiexample
+ QML_FILES
+ "demo.qml"
+ RESOURCE_PREFIX
+ "/"
+ NO_RESOURCE_TARGET_PATH
+)
+
+install(TARGETS rendercontrol_rhi
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET rendercontrol_rhi
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/demo.qml b/examples/quick/rendercontrol/rendercontrol_rhi/demo.qml
new file mode 100644
index 0000000000..707d9cb3a4
--- /dev/null
+++ b/examples/quick/rendercontrol/rendercontrol_rhi/demo.qml
@@ -0,0 +1,156 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Particles
+
+Rectangle {
+ id: root
+
+ gradient: Gradient {
+ GradientStop { position: 0; color: "steelblue" }
+ GradientStop { position: 1; color: "black" }
+ }
+
+ Text {
+ anchors.centerIn: parent
+ text: "Qt Quick in a texture"
+ font.pointSize: 40
+ color: "white"
+
+ SequentialAnimation on rotation {
+ PauseAnimation { duration: 2500 }
+ NumberAnimation { from: 0; to: 360; duration: 5000; easing.type: Easing.InOutCubic }
+ loops: Animation.Infinite
+ }
+ }
+
+ ParticleSystem {
+ id: particles
+ anchors.fill: parent
+
+ ImageParticle {
+ id: smoke
+ system: particles
+ anchors.fill: parent
+ groups: ["A", "B"]
+ source: "qrc:///particleresources/glowdot.png"
+ colorVariation: 0
+ color: "#00111111"
+ }
+ ImageParticle {
+ id: flame
+ anchors.fill: parent
+ system: particles
+ groups: ["C", "D"]
+ source: "qrc:///particleresources/glowdot.png"
+ colorVariation: 0.1
+ color: "#00ff400f"
+ }
+
+ Emitter {
+ id: fire
+ system: particles
+ group: "C"
+
+ y: parent.height
+ width: parent.width
+
+ emitRate: 350
+ lifeSpan: 3500
+
+ acceleration: PointDirection { y: -17; xVariation: 3 }
+ velocity: PointDirection {xVariation: 3}
+
+ size: 24
+ sizeVariation: 8
+ endSize: 4
+ }
+
+ TrailEmitter {
+ id: fireSmoke
+ group: "B"
+ system: particles
+ follow: "C"
+ width: root.width
+ height: root.height - 68
+
+ emitRatePerParticle: 1
+ lifeSpan: 2000
+
+ velocity: PointDirection {y:-17*6; yVariation: -17; xVariation: 3}
+ acceleration: PointDirection {xVariation: 3}
+
+ size: 36
+ sizeVariation: 8
+ endSize: 16
+ }
+
+ TrailEmitter {
+ id: fireballFlame
+ anchors.fill: parent
+ system: particles
+ group: "D"
+ follow: "E"
+
+ emitRatePerParticle: 120
+ lifeSpan: 180
+ emitWidth: TrailEmitter.ParticleSize
+ emitHeight: TrailEmitter.ParticleSize
+ emitShape: EllipseShape{}
+
+ size: 16
+ sizeVariation: 4
+ endSize: 4
+ }
+
+ TrailEmitter {
+ id: fireballSmoke
+ anchors.fill: parent
+ system: particles
+ group: "A"
+ follow: "E"
+
+ emitRatePerParticle: 128
+ lifeSpan: 2400
+ emitWidth: TrailEmitter.ParticleSize
+ emitHeight: TrailEmitter.ParticleSize
+ emitShape: EllipseShape{}
+
+ velocity: PointDirection {yVariation: 16; xVariation: 16}
+ acceleration: PointDirection {y: -16}
+
+ size: 24
+ sizeVariation: 8
+ endSize: 8
+ }
+
+ Emitter {
+ id: balls
+ system: particles
+ group: "E"
+
+ y: parent.height
+ width: parent.width
+
+ emitRate: 2
+ lifeSpan: 7000
+
+ velocity: PointDirection {y:-17*4*2; xVariation: 6*6}
+ acceleration: PointDirection {y: 17*2; xVariation: 6*6}
+
+ size: 8
+ sizeVariation: 4
+ }
+
+ Turbulence { //A bit of turbulence makes the smoke look better
+ anchors.fill: parent
+ groups: ["A","B"]
+ strength: 32
+ system: particles
+ }
+ }
+
+ onWidthChanged: particles.reset()
+ onHeightChanged: particles.reset()
+}
diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/doc/images/rendercontrol-rhi-example.jpg b/examples/quick/rendercontrol/rendercontrol_rhi/doc/images/rendercontrol-rhi-example.jpg
new file mode 100644
index 0000000000..369f1edf84
--- /dev/null
+++ b/examples/quick/rendercontrol/rendercontrol_rhi/doc/images/rendercontrol-rhi-example.jpg
Binary files differ
diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/doc/src/rendercontrol_rhi.qdoc b/examples/quick/rendercontrol/rendercontrol_rhi/doc/src/rendercontrol_rhi.qdoc
new file mode 100644
index 0000000000..521dee3abc
--- /dev/null
+++ b/examples/quick/rendercontrol/rendercontrol_rhi/doc/src/rendercontrol_rhi.qdoc
@@ -0,0 +1,140 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \title QQuickRenderControl RHI Example
+ \example rendercontrol/rendercontrol_rhi
+ \brief Shows how to render a Qt Quick scene into a QRhiTexture.
+ \examplecategory {Graphics}
+ \image rendercontrol-rhi-example.jpg
+
+ This example demonstrates how to set up a Qt Quick scene that has its
+ rendering redirected into a \l QRhiTexture. The application is then free to
+ do whatever it wants with the resulting texture from each frame.
+ This example is a QWidget-based application that performs a readback of the
+ image data, and then displays the collected per-frame renders with CPU and
+ GPU-based timing information for each.
+
+ By using Qt's 3D graphics API abstraction, this example is not tied to any
+ particular graphics API. At startup, a dialog is shown with the platforms'
+ potentially supported 3D APIs.
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp apiselect
+
+ \note It is not guaranteed that all selections will be functional on a given
+ platform.
+
+ Once a selection is made, a QML file is loaded. However, we will not simply
+ create a \l QQuickView instance and \l{QQuickView::show()}{show()} it.
+ Rather, the \l QQuickWindow that manages the Qt Quick scene is never shown
+ on-screen. Instead, the application takes control over when and to where
+ render, via \l QQuickRenderControl.
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp load-1
+
+ Once the object tree is instantiated, the root item (a \l Rectangle) is
+ queried, its size is ensured to be valid and then propagated.
+
+ \note Scenes that use the \l Window element within the object tree are not
+ supported.
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp load-instantiate
+
+ At this point there are no rendering resources initialized, i.e., nothing has
+ been done with the native 3D graphics API yet. A \l QRhi is instantiated
+ only in the next step, and that is what triggers setting up the Vulkan,
+ Metal, Direct 3D, etc. rendering system under the hood.
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp load-graphicsinit
+
+ \note This application uses a model where Qt creates an instance of QRhi.
+ This is not the only possible approach: if the application maintains its own
+ QRhi (and so OpenGL context, Vulkan device, etc.), then Qt Quick can be
+ requested to adopt and use that existing QRhi. That is done via passing a \l
+ QQuickGraphicsDevice created by \l{QQuickGraphicsDevice::fromRhi()} to
+ \l QQuickWindow, similarly to how \l QQuickGraphicsConfiguration is set in
+ the snippet above. Consider for example the case of wanting to use the Qt
+ Quick rendered textures in a QRhiWidget: in that case the QRhiWidget's QRhi
+ will need to passed on to Qt Quick, instead of letting Qt Quick create its
+ own.
+
+ Once \l QQuickRenderControl::initialize() succeeds, the renderer is live and
+ ready to go. For that, we need a color buffer to render into.
+
+ \l QQuickRenderTarget is a lightweight implicitly-shared class that carries
+ (but those not own) various sets of native or QRhi objects that describe
+ textures, render targets, or similar. Calling
+ \l{QQuickWindow::setRenderTarget()}{setRenderTarget()} on the \l
+ QQuickWindow (remember that we have a QQuickWindow that is not visible
+ on-screen) is what triggers redirecting the Qt Quick scene graph's rendering
+ into the texture provided by the application. When working with \l QRhi (and
+ not with native 3D API objects such as OpenGL texture IDs or VkImage
+ objects), the application should set up a \l QRhiTextureRenderTarget and
+ then pass it to Qt Quick via \l QQuickRenderTarget::fromRhiRenderTarget().
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp texture-setup
+
+ \note Always provide a depth-stencil buffer for Qt Quick since both of these
+ buffers and the depth and stencil test may get utilized by the Qt Quick
+ scenegraph when rendering.
+
+ The main render loop is the following. This also shows how to perform
+ GPU->CPU readbacks of images. Once a QImage is available, the
+ QWidget-based user interface updates accordingly. We will omit diving
+ into the details for that here.
+
+ The example also demonstrates a simple way of measuring the cost of
+ rendering a frame on the CPU and the GPU. Offscreen-rendered frames are well
+ suited for this due to certain internal QRhi behavior, which implies that
+ operations that otherwise are asynchronous (in the sense that they complete
+ only when rendering a subsequent frame), are guaranteed to be ready once \l
+ QRhi::endOffscreenFrame() (i.e., QQuickRenderControl::endFrame()) returns.
+ We use this knowledge when reading back the texture, and it applies also to
+ GPU timestamps as well. That is why the application can display the GPU time
+ for each frame, while guaranteeing that the time actually refers to that
+ particular frame (not an earlier one). See
+ \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()} for
+ details around GPU timings. The CPU side timings are taken using
+ \l QElapsedTimer.
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp render-core
+
+ One important piece is the stepping of Qt Quick animations. As we do not
+ have an on-screen window that can drive the animation system either via
+ measuring elapsed time, an ordinary timer, or presentation rate-based
+ throttling, redirecting the Qt Quick rendering often implies that the
+ driving of animations needs to be taken over by the application. Otherwise,
+ animations function based on a plain system timer, but the actual elapsed
+ time will often have nothing to do with what the offscreen-rendered scene is
+ expected to perceive. Consider rendering 5 frames in a row, in a tight loop.
+ How the animations in those 5 frames move depends on the speed with which
+ the CPU executes the loop iterations. That is almost never ideal. To ensure
+ consistent animations, install a custom QAnimationDriver. While this is
+ an undocumented (but public) API meant for advanced users, the example here
+ provides a simple example of using it.
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp anim-driver
+
+ The application has a \l QSlider that can be used to change the animation
+ step value from the default 16 milliseconds to something else. Note the call
+ to the setStep() function of our QAnimationDriver subclass.
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp anim-slider
+
+ \note Installing the custom animation driver is made optional via the
+ \c animCheckBox check box. This allows comparing the effect of having and
+ not having a custom animation driver installed. In addition, on some
+ platforms (and perhaps depending on the theme), having the custom driver
+ enabled may lead to lags in widget drawing. This is as expected, because if
+ some widget animation (e.g. highlight of a QPushButton or QCheckBox) is
+ managed via \l QPropertyAnimation and similar, then those animation are
+ driven by the same QAnimationDriver, and that does not advance until a new
+ frame is requested by clicking on the buttons.
+
+ Advancing the animations is done before each frame (i.e., before the
+ QQuickRenderControl::beginFrame() call) by simply calling advance():
+
+ \snippet rendercontrol/rendercontrol_rhi/main.cpp anim-step
+
+ \sa QRhi, QQuickRenderControl, QQuickWindow
+*/
diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/main.cpp b/examples/quick/rendercontrol/rendercontrol_rhi/main.cpp
new file mode 100644
index 0000000000..76f2ade0a9
--- /dev/null
+++ b/examples/quick/rendercontrol/rendercontrol_rhi/main.cpp
@@ -0,0 +1,640 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QMainWindow>
+#include <QApplication>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QMenuBar>
+#include <QStatusBar>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QLabel>
+#include <QScrollArea>
+#include <QGridLayout>
+#include <QPushButton>
+#include <QSlider>
+#include <QCheckBox>
+#include <QScrollBar>
+#include <QListWidget>
+#include <QPainter>
+
+#include <QQuickWindow>
+#include <QQuickRenderControl>
+#include <QQuickRenderTarget>
+#include <QQuickGraphicsDevice>
+#include <QQuickGraphicsConfiguration>
+#include <QQuickItem>
+#include <QQmlEngine>
+#include <QQmlComponent>
+
+#include <QAnimationDriver>
+#include <QElapsedTimer>
+
+#include <rhi/qrhi.h>
+
+//! [anim-driver]
+class AnimationDriver : public QAnimationDriver
+{
+public:
+ AnimationDriver(QObject *parent = nullptr)
+ : QAnimationDriver(parent),
+ m_step(16)
+ {
+ }
+
+ void setStep(int milliseconds)
+ {
+ m_step = milliseconds;
+ }
+
+ void advance() override
+ {
+ m_elapsed += m_step;
+ advanceAnimation();
+ }
+
+ qint64 elapsed() const override
+ {
+ return m_elapsed;
+ }
+
+private:
+ int m_step;
+ qint64 m_elapsed = 0;
+};
+//! [anim-driver]
+
+class ImageLabel;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+ ~MainWindow();
+
+ void load(const QString &filename);
+ void render();
+
+private slots:
+ void openRequested();
+
+private:
+ void reset();
+ void stepAnimations();
+
+ AnimationDriver *m_animationDriver = nullptr;
+
+ std::unique_ptr<QQuickRenderControl> m_renderControl;
+ std::unique_ptr<QQuickWindow> m_scene;
+ std::unique_ptr<QQmlEngine> m_qmlEngine;
+ std::unique_ptr<QQmlComponent> m_qmlComponent;
+
+ std::unique_ptr<QRhiTexture> m_texture;
+ std::unique_ptr<QRhiRenderBuffer> m_ds;
+ std::unique_ptr<QRhiTextureRenderTarget> m_rt;
+ std::unique_ptr<QRhiRenderPassDescriptor> m_rpDesc;
+
+ qint64 m_frameCount = 0;
+ QSize m_thumbnailSize;
+ QVector<double> m_cpuTimes;
+ QVector<double> m_gpuTimes;
+
+ QLabel *m_apiMsg;
+ QLabel *m_statusMsg;
+ QLabel *m_avgCpuMsg;
+ QLabel *m_avgGpuMsg;
+ QLabel *m_driverInfoMsg;
+
+ QScrollArea *m_scrollArea;
+ QGridLayout *m_grid;
+ QVector<QWidget *> m_gridWidgets;
+
+public:
+#if QT_CONFIG(vulkan)
+ QVulkanInstance *m_vulkanInstance = nullptr;
+#endif
+
+ ImageLabel *m_focus = nullptr;
+ QLabel *m_fullSizeViewerWindow = nullptr;
+ QVector<QImage> m_frames;
+
+ bool m_mirrorVertically = false;
+};
+
+MainWindow::MainWindow()
+{
+ QWidget *centralWidget = new QWidget(this);
+ QVBoxLayout *vlayout = new QVBoxLayout(centralWidget);
+ QHBoxLayout *controlLayout = new QHBoxLayout;
+ vlayout->addLayout(controlLayout);
+
+ QPushButton *btn = new QPushButton(tr("Next frame"));
+ QObject::connect(btn, &QPushButton::clicked, btn, [this] {
+ render();
+ });
+ controlLayout->addWidget(btn);
+
+ QPushButton *btnMulti = new QPushButton(tr("Next 10 frames"));
+ QObject::connect(btnMulti, &QPushButton::clicked, btn, [this] {
+ for (int i = 0; i < 10; ++i)
+ QMetaObject::invokeMethod(this, &MainWindow::render, Qt::QueuedConnection);
+ });
+ controlLayout->addWidget(btnMulti);
+
+//! [anim-slider]
+ QSlider *animSlider = new QSlider;
+ animSlider->setOrientation(Qt::Horizontal);
+ animSlider->setMinimum(1);
+ animSlider->setMaximum(1000);
+ QLabel *animLabel = new QLabel;
+ QObject::connect(animSlider, &QSlider::valueChanged, animSlider, [this, animLabel, animSlider] {
+ if (m_animationDriver)
+ m_animationDriver->setStep(animSlider->value());
+ animLabel->setText(tr("Simulated elapsed time per frame: %1 ms").arg(animSlider->value()));
+ });
+ animSlider->setValue(16);
+ QCheckBox *animCheckBox = new QCheckBox(tr("Custom animation driver"));
+ animCheckBox->setToolTip(tr("Note: Installing the custom animation driver makes widget drawing unreliable, depending on the platform.\n"
+ "This is due to widgets themselves relying on QPropertyAnimation and similar, which are driven by the same QAnimationDriver.\n"
+ "In any case, the functionality of the widgets are not affected, just the rendering may lag behind.\n"
+ "When not checked, Qt Quick animations advance based on the system time, i.e. the time elapsed since the last press of the Next button."));
+ QObject::connect(animCheckBox, &QCheckBox::stateChanged, animCheckBox, [this, animCheckBox, animSlider, animLabel] {
+ if (animCheckBox->isChecked()) {
+ animSlider->setEnabled(true);
+ animLabel->setEnabled(true);
+ m_animationDriver = new AnimationDriver(this);
+ m_animationDriver->install();
+ m_animationDriver->setStep(animSlider->value());
+ } else {
+ animSlider->setEnabled(false);
+ animLabel->setEnabled(false);
+ delete m_animationDriver;
+ m_animationDriver = nullptr;
+ }
+ });
+ animSlider->setEnabled(false);
+ animLabel->setEnabled(false);
+ controlLayout->addWidget(animCheckBox);
+ controlLayout->addWidget(animLabel);
+ controlLayout->addWidget(animSlider);
+//! [anim-slider]
+
+ QCheckBox *mirrorCheckBox = new QCheckBox(tr("Mirror vertically"));
+ QObject::connect(mirrorCheckBox, &QCheckBox::stateChanged, mirrorCheckBox, [this, mirrorCheckBox] {
+ m_mirrorVertically = mirrorCheckBox->isChecked();
+ });
+ controlLayout->addWidget(mirrorCheckBox);
+
+ QGridLayout *gridLayout = new QGridLayout;
+ vlayout->addLayout(gridLayout);
+
+ QWidget *viewport = new QWidget;
+ m_grid = new QGridLayout(viewport);
+ m_scrollArea = new QScrollArea;
+ m_scrollArea->setWidgetResizable(true);
+ m_scrollArea->setWidget(viewport);
+ QObject::connect(m_scrollArea->verticalScrollBar(), &QScrollBar::rangeChanged, this, [this] {
+ m_scrollArea->verticalScrollBar()->setSliderPosition(m_scrollArea->verticalScrollBar()->maximum());
+ });
+
+ gridLayout->addWidget(m_scrollArea);
+ setCentralWidget(centralWidget);
+
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+ fileMenu->addAction(tr("&Open"), this, &MainWindow::openRequested);
+ fileMenu->addAction(tr("E&xit"), qApp, &QCoreApplication::quit);
+
+ m_statusMsg = new QLabel;
+ statusBar()->addWidget(m_statusMsg);
+ m_avgCpuMsg = new QLabel;
+ statusBar()->addWidget(m_avgCpuMsg);
+ m_avgGpuMsg = new QLabel;
+ statusBar()->addWidget(m_avgGpuMsg);
+ m_apiMsg = new QLabel;
+ statusBar()->addWidget(m_apiMsg);
+ m_driverInfoMsg = new QLabel;
+ statusBar()->addWidget(m_driverInfoMsg);
+}
+
+MainWindow::~MainWindow()
+{
+ delete m_fullSizeViewerWindow;
+}
+
+void MainWindow::openRequested()
+{
+ QString filename = QFileDialog::getOpenFileName(this, tr("Open file"), QString(), tr("QML files (*.qml);;All files (*.*)"));
+ if (!filename.isEmpty())
+ load(filename);
+}
+
+void MainWindow::reset()
+{
+ m_rpDesc.reset();
+ m_rt.reset();
+ m_ds.reset();
+ m_texture.reset();
+
+ m_qmlComponent.reset();
+ m_qmlEngine.reset();
+ m_scene.reset();
+ m_renderControl.reset();
+
+ m_apiMsg->setText(QString());
+ m_statusMsg->setText(QString());
+ m_avgCpuMsg->setText(QString());
+ m_avgGpuMsg->setText(QString());
+ m_driverInfoMsg->setText(QString());
+
+ m_frameCount = 0;
+
+ qDeleteAll(m_gridWidgets);
+ m_gridWidgets.clear();
+
+ delete m_fullSizeViewerWindow;
+ m_fullSizeViewerWindow = nullptr;
+ m_focus = nullptr;
+}
+
+//! [load-1]
+void MainWindow::load(const QString &filename)
+{
+ reset();
+
+ m_renderControl.reset(new QQuickRenderControl);
+ m_scene.reset(new QQuickWindow(m_renderControl.get()));
+
+ // enable lastCompletedGpuTime() on QRhiCommandBuffer, if supported by the underlying 3D API
+ QQuickGraphicsConfiguration config;
+ config.setTimestamps(true);
+ m_scene->setGraphicsConfiguration(config);
+
+#if QT_CONFIG(vulkan)
+ if (m_scene->graphicsApi() == QSGRendererInterface::Vulkan)
+ m_scene->setVulkanInstance(m_vulkanInstance);
+#endif
+
+ m_qmlEngine.reset(new QQmlEngine);
+ m_qmlComponent.reset(new QQmlComponent(m_qmlEngine.get(), QUrl::fromLocalFile(filename)));
+ if (m_qmlComponent->isError()) {
+ for (const QQmlError &error : m_qmlComponent->errors())
+ qWarning() << error.url() << error.line() << error;
+ QMessageBox::critical(this, tr("Cannot load QML scene"), tr("Failed to load %1").arg(filename));
+ reset();
+ return;
+ }
+//! [load-1]
+
+//! [load-instantiate]
+ QObject *rootObject = m_qmlComponent->create();
+ if (m_qmlComponent->isError()) {
+ for (const QQmlError &error : m_qmlComponent->errors())
+ qWarning() << error.url() << error.line() << error;
+ QMessageBox::critical(this, tr("Cannot load QML scene"), tr("Failed to create component"));
+ reset();
+ return;
+ }
+
+ QQuickItem *rootItem = qobject_cast<QQuickItem *>(rootObject);
+ if (!rootItem) {
+ // Get rid of the on-screen window, if the root object was a Window
+ if (QQuickWindow *w = qobject_cast<QQuickWindow *>(rootObject))
+ delete w;
+ QMessageBox::critical(this,
+ tr("Invalid root item in QML scene"),
+ tr("Root object is not a QQuickItem. If this is a scene with Window in it, note that such scenes are not supported."));
+ reset();
+ return;
+ }
+
+ if (rootItem->size().width() < 16)
+ rootItem->setSize(QSizeF(640, 360));
+
+ m_scene->contentItem()->setSize(rootItem->size());
+ m_scene->setGeometry(0, 0, rootItem->width(), rootItem->height());
+
+ rootItem->setParentItem(m_scene->contentItem());
+
+ m_statusMsg->setText(tr("QML scene loaded"));
+//! [load-instantiate]
+
+//! [load-graphicsinit]
+ const bool initSuccess = m_renderControl->initialize();
+ if (!initSuccess) {
+ QMessageBox::critical(this, tr("Cannot initialize renderer"), tr("QQuickRenderControl::initialize() failed"));
+ reset();
+ return;
+ }
+
+ const QSGRendererInterface::GraphicsApi api = m_scene->rendererInterface()->graphicsApi();
+ switch (api) {
+ case QSGRendererInterface::OpenGL:
+ m_apiMsg->setText(tr("OpenGL"));
+ break;
+ case QSGRendererInterface::Direct3D11:
+ m_apiMsg->setText(tr("D3D11"));
+ break;
+ case QSGRendererInterface::Direct3D12:
+ m_apiMsg->setText(tr("D3D12"));
+ break;
+ case QSGRendererInterface::Vulkan:
+ m_apiMsg->setText(tr("Vulkan"));
+ break;
+ case QSGRendererInterface::Metal:
+ m_apiMsg->setText(tr("Metal"));
+ break;
+ default:
+ m_apiMsg->setText(tr("Unknown 3D API"));
+ break;
+ }
+
+ QRhi *rhi = m_renderControl->rhi();
+ if (!rhi) {
+ QMessageBox::critical(this, tr("Cannot render"), tr("No QRhi from QQuickRenderControl"));
+ reset();
+ return;
+ }
+
+ m_driverInfoMsg->setText(QString::fromUtf8(rhi->driverInfo().deviceName));
+//! [load-graphicsinit]
+
+//! [texture-setup]
+ const QSize pixelSize = rootItem->size().toSize(); // no scaling, i.e. the item size is in pixels
+
+ m_texture.reset(rhi->newTexture(QRhiTexture::RGBA8, pixelSize, 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ if (!m_texture->create()) {
+ QMessageBox::critical(this, tr("Cannot render"), tr("Cannot create texture object"));
+ reset();
+ return;
+ }
+
+ m_ds.reset(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, 1));
+ if (!m_ds->create()) {
+ QMessageBox::critical(this, tr("Cannot render"), tr("Cannot create depth-stencil buffer"));
+ reset();
+ return;
+ }
+
+ QRhiTextureRenderTargetDescription rtDesc(QRhiColorAttachment(m_texture.get()));
+ rtDesc.setDepthStencilBuffer(m_ds.get());
+ m_rt.reset(rhi->newTextureRenderTarget(rtDesc));
+ m_rpDesc.reset(m_rt->newCompatibleRenderPassDescriptor());
+ m_rt->setRenderPassDescriptor(m_rpDesc.get());
+ if (!m_rt->create()) {
+ QMessageBox::critical(this, tr("Cannot render"), tr("Cannot create render target"));
+ reset();
+ return;
+ }
+
+ m_scene->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(m_rt.get()));
+//! [texture-setup]
+
+ render();
+}
+
+class ImageLabel : public QLabel
+{
+public:
+ ImageLabel(MainWindow *mw, int frameIndex)
+ : m_mainWindow(mw),
+ m_frameIndex(frameIndex)
+ {
+ }
+
+ void mousePressEvent(QMouseEvent *) override {
+ if (m_mainWindow->m_focus != this) {
+ ImageLabel *oldFocus = m_mainWindow->m_focus;
+ m_mainWindow->m_focus = this;
+ if (oldFocus)
+ oldFocus->update();
+ update();
+ }
+ }
+
+ void paintEvent(QPaintEvent *e) override {
+
+ if (m_mainWindow->m_focus == this) {
+ QPainter p(this);
+ p.fillRect(0, 0, width(), height(), Qt::blue);
+ p.end();
+ QLabel::paintEvent(e);
+ p.begin(this);
+ p.setOpacity(0.5);
+ p.fillRect(0, 0, width(), height(), Qt::blue);
+ } else {
+ QLabel::paintEvent(e);
+ }
+ }
+
+ void mouseDoubleClickEvent(QMouseEvent *) override {
+ QLabel *&w(m_mainWindow->m_fullSizeViewerWindow);
+ if (!w)
+ w = new QLabel;
+ const QImage &image(m_mainWindow->m_frames[m_frameIndex]);
+ w->resize(image.width(), image.height());
+ w->setPixmap(QPixmap::fromImage(image));
+ w->show();
+ w->activateWindow();
+ }
+
+ MainWindow *m_mainWindow;
+ int m_frameIndex;
+};
+
+void MainWindow::render()
+{
+ if (!m_renderControl)
+ return;
+
+ if (m_frameCount > 0)
+ stepAnimations();
+
+ // this is only here to communicate the possibly changed mirrorVertically flag
+ QQuickRenderTarget quickRt = QQuickRenderTarget::fromRhiRenderTarget(m_rt.get());
+ quickRt.setMirrorVertically(m_mirrorVertically);
+ m_scene->setRenderTarget(quickRt);
+
+//! [render-core]
+ QElapsedTimer cpuTimer;
+ cpuTimer.start();
+
+ m_renderControl->polishItems();
+
+ m_renderControl->beginFrame();
+
+ m_renderControl->sync();
+ m_renderControl->render();
+
+ QRhi *rhi = m_renderControl->rhi();
+ QRhiReadbackResult readResult;
+ QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
+ readbackBatch->readBackTexture(m_texture.get(), &readResult);
+ m_renderControl->commandBuffer()->resourceUpdate(readbackBatch);
+
+ m_renderControl->endFrame();
+
+ const double gpuRenderTimeMs = m_renderControl->commandBuffer()->lastCompletedGpuTime() * 1000.0;
+ const double cpuRenderTimeMs = cpuTimer.nsecsElapsed() / 1000000.0;
+
+ // m_renderControl->begin/endFrame() is based on QRhi's
+ // begin/endOffscreenFrame() under the hood, meaning it does not do
+ // pipelining, unlike swapchain-based frames, and therefore the readback is
+ // guaranteed to complete once endFrame() returns.
+ QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888_Premultiplied);
+ QImage result;
+ if (rhi->isYUpInFramebuffer())
+ result = wrapperImage.mirrored();
+ else
+ result = wrapperImage.copy();
+
+//! [render-core]
+
+ m_gpuTimes.append(gpuRenderTimeMs);
+ m_cpuTimes.append(cpuRenderTimeMs);
+
+ m_frames.append(result);
+
+ if (m_thumbnailSize.isEmpty())
+ m_thumbnailSize = size() / 4;
+
+ const QImage thumbnail = result.scaled(m_thumbnailSize, Qt::KeepAspectRatio);
+
+ ImageLabel *image = new ImageLabel(this, m_frames.count() - 1);
+ image->setPixmap(QPixmap::fromImage(thumbnail));
+ image->setAlignment(Qt::AlignCenter);
+
+ QLabel *label = new QLabel(tr("Frame %1\nCPU: %2 ms GPU: %3 ms").arg(m_frameCount).arg(cpuRenderTimeMs, 0, 'f', 4).arg(gpuRenderTimeMs, 0, 'f', 4));
+ label->setAlignment(Qt::AlignCenter);
+ QWidget *container = new QWidget;
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->addWidget(image);
+ layout->addWidget(label);
+ container->setLayout(layout);
+ m_grid->addWidget(container, m_frameCount / 3, m_frameCount % 3);
+ m_gridWidgets.append(container);
+
+ m_scrollArea->verticalScrollBar()->setSliderPosition(m_scrollArea->verticalScrollBar()->maximum());
+
+ m_frameCount += 1;
+
+ double v = 0;
+ for (double t : m_cpuTimes)
+ v += t;
+ v /= m_cpuTimes.count();
+ m_avgCpuMsg->setText(tr("Avg. CPU render time: %1 ms").arg(v, 0, 'f', 4));
+ if (m_cpuTimes.count() > 64) {
+ m_cpuTimes.clear();
+ m_cpuTimes.append(v);
+ }
+ v = 0;
+ for (double t : m_gpuTimes)
+ v += t;
+ v /= m_gpuTimes.count();
+ m_avgGpuMsg->setText(tr("Avg. GPU render time: %1 ms").arg(v, 0, 'f', 4));
+ if (m_gpuTimes.count() > 64) {
+ m_gpuTimes.clear();
+ m_gpuTimes.append(v);
+ }
+}
+
+//! [anim-step]
+void MainWindow::stepAnimations()
+{
+ if (m_animationDriver) {
+ // Now the Qt Quick scene will think that <slider value> milliseconds have
+ // elapsed and update animations accordingly when doing the next frame.
+ m_animationDriver->advance();
+ }
+}
+//! [anim-step]
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+#if QT_CONFIG(vulkan)
+ QVulkanInstance vulkanInstance;
+#endif
+
+ MainWindow mainWindow;
+
+//! [apiselect]
+ QDialog apiSelect;
+ QVBoxLayout *selLayout = new QVBoxLayout;
+ selLayout->addWidget(new QLabel(QObject::tr("Select graphics API to use")));
+ QListWidget *apiList = new QListWidget;
+ QVarLengthArray<QSGRendererInterface::GraphicsApi, 5> apiValues;
+#ifdef Q_OS_WIN
+ apiList->addItem("Direct3D 11");
+ apiValues.append(QSGRendererInterface::Direct3D11);
+ apiList->addItem("Direct3D 12");
+ apiValues.append(QSGRendererInterface::Direct3D12);
+#endif
+#if QT_CONFIG(metal)
+ apiList->addItem("Metal");
+ apiValues.append(QSGRendererInterface::Metal);
+#endif
+#if QT_CONFIG(vulkan)
+ apiList->addItem("Vulkan");
+ apiValues.append(QSGRendererInterface::Vulkan);
+#endif
+#if QT_CONFIG(opengl)
+ apiList->addItem("OpenGL / OpenGL ES");
+ apiValues.append(QSGRendererInterface::OpenGL);
+#endif
+ if (apiValues.isEmpty()) {
+ QMessageBox::critical(nullptr, QObject::tr("No 3D graphics API"), QObject::tr("No 3D graphics APIs are supported in this Qt build"));
+ return 1;
+ }
+//! [apiselect]
+ apiList->setCurrentRow(0);
+ selLayout->addWidget(apiList);
+ QPushButton *okBtn = new QPushButton("Ok");
+ okBtn->setDefault(true);
+ selLayout->addWidget(okBtn);
+ apiSelect.setLayout(selLayout);
+ apiSelect.resize(320, 200);
+ apiSelect.show();
+
+ QObject::connect(okBtn, &QPushButton::clicked, okBtn, [apiList, &apiSelect, &apiValues, &mainWindow
+#if QT_CONFIG(vulkan)
+ , &vulkanInstance
+#endif
+ ] {
+ const QSGRendererInterface::GraphicsApi api = apiValues[apiList->currentRow()];
+ QQuickWindow::setGraphicsApi(api);
+
+#if QT_CONFIG(vulkan)
+ if (api == QSGRendererInterface::Vulkan) {
+ vulkanInstance.setExtensions(QQuickGraphicsConfiguration::preferredInstanceExtensions());
+ if (!vulkanInstance.create()) {
+ QMessageBox::critical(nullptr, QObject::tr("Cannot initialize Vulkan"), QObject::tr("Failed to create VkInstance"));
+ return;
+ }
+ mainWindow.m_vulkanInstance = &vulkanInstance;
+ }
+#endif
+
+ mainWindow.resize(1280, 720);
+ mainWindow.show();
+ mainWindow.load(QLatin1String(":/demo.qml"));
+ // load() renders one frame, add 19 more
+ for (int i = 1; i <= 19; ++i) {
+ mainWindow.render();
+ // have to process events, e.g. to get queued metacalls delivered
+ QCoreApplication::processEvents();
+ }
+
+ apiSelect.close();
+ });
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/rendercontrol_rhi.pro b/examples/quick/rendercontrol/rendercontrol_rhi/rendercontrol_rhi.pro
new file mode 100644
index 0000000000..cd397f4451
--- /dev/null
+++ b/examples/quick/rendercontrol/rendercontrol_rhi/rendercontrol_rhi.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+
+QT += gui-private widgets qml quick
+
+SOURCES += main.cpp
+
+RESOURCES += rendercontrol_rhi.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/rendercontrol/rendercontrol_rhi
+INSTALLS += target
diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/rendercontrol_rhi.qrc b/examples/quick/rendercontrol/rendercontrol_rhi/rendercontrol_rhi.qrc
new file mode 100644
index 0000000000..6d9c32d786
--- /dev/null
+++ b/examples/quick/rendercontrol/rendercontrol_rhi/rendercontrol_rhi.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>demo.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/responsivelayouts/CMakeLists.txt b/examples/quick/responsivelayouts/CMakeLists.txt
new file mode 100644
index 0000000000..699e71a433
--- /dev/null
+++ b/examples/quick/responsivelayouts/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(responsivelayouts LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(responsivelayoutsexample
+ WIN32
+ MACOSX_BUNDLE
+ main.cpp
+)
+
+qt_add_qml_module(responsivelayoutsexample
+ URI responsivelayouts
+ QML_FILES
+ "responsivelayouts.qml"
+ RESOURCES
+ "icons/grid.svg"
+ "icons/text.svg"
+ "icons/settings.svg"
+)
+
+target_link_libraries(responsivelayoutsexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+install(TARGETS responsivelayoutsexample
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET responsivelayoutsexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/responsivelayouts/doc/images/qtquicklayouts-example-responsivelayouts.png b/examples/quick/responsivelayouts/doc/images/qtquicklayouts-example-responsivelayouts.png
new file mode 100644
index 0000000000..4cc8c14098
--- /dev/null
+++ b/examples/quick/responsivelayouts/doc/images/qtquicklayouts-example-responsivelayouts.png
Binary files differ
diff --git a/examples/quick/responsivelayouts/doc/src/qtquickresponsiveLayouts-examples.qdoc b/examples/quick/responsivelayouts/doc/src/qtquickresponsiveLayouts-examples.qdoc
new file mode 100644
index 0000000000..7c204fc5b4
--- /dev/null
+++ b/examples/quick/responsivelayouts/doc/src/qtquickresponsiveLayouts-examples.qdoc
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \title Qt Quick Layouts - Responsive Layout Example
+ \example responsivelayouts
+ \brief Demonstrates how to use LayoutItemProxy to make a responsive UI.
+ \ingroup qtquickexamples
+ \examplecategory {User Interface Components}
+
+ This example shows how to use LayoutProxyItems in combination with
+ \l{Qt Quick Layouts}{layouts} to create responsive layouts.
+
+ \include examples-run.qdocinc
+
+ \section1 Creating items
+
+ The \l {LayoutItemProxy} type allows to use the same item in different
+ layouts, although only one layout can be visible at the same time. This
+ can be used to create responsive layouts that adapt to the window or
+ screen size.
+
+ \image qtquicklayouts-example-responsivelayouts.png
+
+
+ First we need to define all items that should appear in or UI at some point.
+ We use a AnnotatedRect, which is a simple Rectangle with some added text.
+
+ \snippet responsivelayouts/responsivelayouts.qml item definition
+
+ \section1 Creating layouts
+
+ We can now declare various layouts using \l {LayoutItemProxy}{LayoutItemProxies},
+ targeting the previously declare items. A single layout can be defined as follows.
+
+ \snippet responsivelayouts/responsivelayouts.qml first layout
+
+ This snippet shows multiple ways to use the \l {LayoutItemProxy}.
+ The simplest method is to add \l {LayoutItemProxy}{LayoutItemProxies}
+ to a Layout like the RowLayout here. In addition we set an additional
+ \l {Layout} attached property to the LayoutProxyItem that will affect
+ the target item only in this particular layout. Further, we see that the
+ item \c {d} is not used in the first layout. Then it is automatically
+ hidden by the \l {LayoutItemProxy} in the second layout.
+ Another way of using it is shown by setting a \l {LayoutItemProxy} as
+ the content of a \l {Flickable}.
+
+ Another layout is declared as follows.
+
+ \snippet responsivelayouts/responsivelayouts.qml second layout
+
+ Here we show that \l {LayoutItemProxy}{LayoutItemProxies} can be used
+ together with real \l {Item}{Items} on the same hierarchy level. Generally,
+ the \l {LayoutItemProxy} is flexible and allows nested structures of
+ items and layouts.
+
+ \section1 Setting the layout
+
+ After two layouts, \c {smallLayout} and \c {largeLayout} are defined,
+ we can continue with setting the layout that fits to the current size of
+ the application. We define a new function for this code that we call
+ when the window is initialized and whenever the width changes:
+
+ \snippet responsivelayouts/responsivelayouts.qml setting the layout
+
+ Alternatively to calling this function after initialization we can hide all
+ but the correct layout for the initial size in the declarative code.
+*/
diff --git a/examples/quick/responsivelayouts/icons/grid.svg b/examples/quick/responsivelayouts/icons/grid.svg
new file mode 100644
index 0000000000..fd542d7dc4
--- /dev/null
+++ b/examples/quick/responsivelayouts/icons/grid.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4 13C2.89543 13 2 13.8954 2 15V20C2 21.1046 2.89543 22 4 22H9C10.1046 22 11 21.1046 11 20V15C11 13.8954 10.1046 13 9 13H4ZM15 13C13.8954 13 13 13.8954 13 15V20C13 21.1046 13.8954 22 15 22H20C21.1046 22 22 21.1046 22 20V15C22 13.8954 21.1046 13 20 13H15Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4 2C2.89543 2 2 2.89543 2 4V9C2 10.1046 2.89543 11 4 11H9C10.1046 11 11 10.1046 11 9V4C11 2.89543 10.1046 2 9 2H4ZM15 2C13.8954 2 13 2.89543 13 4V9C13 10.1046 13.8954 11 15 11H20C21.1046 11 22 10.1046 22 9V4C22 2.89543 21.1046 2 20 2H15Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/quick/responsivelayouts/icons/settings.svg b/examples/quick/responsivelayouts/icons/settings.svg
new file mode 100644
index 0000000000..3a13ccae16
--- /dev/null
+++ b/examples/quick/responsivelayouts/icons/settings.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.84998 4C8.84998 2.89543 9.74541 2 10.85 2H13.1222C14.2268 2 15.1222 2.89543 15.1222 4V4.59509C15.1229 4.59732 15.125 4.6023 15.1306 4.61031C15.1434 4.62838 15.1691 4.65242 15.2077 4.66935C15.852 4.95163 16.4515 5.31609 16.9931 5.74921C17.0261 5.77554 17.0594 5.787 17.0815 5.78982C17.0912 5.79106 17.0966 5.79055 17.0989 5.79008L17.9363 5.33741C18.8691 4.8332 20.0332 5.14455 20.5897 6.04706L21.6744 7.80604C22.2777 8.78435 21.9342 10.0686 20.9231 10.6152L19.9875 11.1209C19.9859 11.1226 19.9824 11.1269 19.9781 11.1358C19.9683 11.156 19.9595 11.1904 19.9636 11.2327C19.9877 11.4855 20 11.7414 20 12C20 12.2586 19.9877 12.5145 19.9636 12.7673C19.9595 12.8096 19.9683 12.844 19.9781 12.8642C19.9824 12.8732 19.9859 12.8774 19.9875 12.8791L20.923 13.3848C21.9341 13.9314 22.2776 15.2157 21.6743 16.194L20.5897 17.9529C20.0331 18.8554 18.869 19.1668 17.9362 18.6626L17.0989 18.2099C17.0966 18.2095 17.0912 18.209 17.0815 18.2102C17.0594 18.213 17.026 18.2245 16.9931 18.2508C16.4515 18.6839 15.852 19.0484 15.2077 19.3306C15.1691 19.3476 15.1434 19.3716 15.1306 19.3897C15.125 19.3977 15.1229 19.4027 15.1222 19.4049V20C15.1222 21.1046 14.2268 22 13.1222 22H10.85C9.74541 22 8.84998 21.1046 8.84998 20V19.3928C8.84931 19.3906 8.84725 19.3857 8.84163 19.3777C8.82894 19.3597 8.80351 19.3357 8.76508 19.3187C8.12675 19.0362 7.53272 18.6729 6.99586 18.242C6.96292 18.2156 6.92951 18.2041 6.90737 18.2012C6.89759 18.2 6.8922 18.2005 6.8899 18.201L6.03592 18.6626C5.10317 19.1668 3.93904 18.8554 3.38251 17.9529L2.29784 16.194C1.69457 15.2156 2.03805 13.9314 3.04914 13.3848L4.01099 12.8649C4.01265 12.8632 4.01607 12.859 4.02042 12.85C4.03023 12.8299 4.03897 12.7956 4.03503 12.7534C4.0118 12.5051 3.99995 12.2538 3.99995 12C3.99995 11.7462 4.0118 11.4949 4.03503 11.2466C4.03897 11.2045 4.03022 11.1701 4.02042 11.15C4.01607 11.1411 4.01265 11.1368 4.01098 11.1352L3.04909 10.6152C2.038 10.0686 1.69452 8.78435 2.29779 7.80604L3.38245 6.04707C3.93898 5.14456 5.10312 4.83322 6.03587 5.33742L6.88988 5.79907C6.89218 5.79953 6.89757 5.80005 6.90735 5.79879C6.92949 5.79594 6.9629 5.78441 6.99584 5.75797C7.53271 5.32709 8.12674 4.96379 8.76508 4.68128C8.80351 4.66427 8.82894 4.64029 8.84163 4.62231C8.84725 4.61434 8.84931 4.60938 8.84998 4.60716V4ZM8.85024 4.60578C8.85029 4.60578 8.85026 4.60619 8.85001 4.60703L8.85024 4.60578ZM6.88852 5.79864C6.88854 5.7986 6.88898 5.79869 6.88977 5.79904L6.88852 5.79864ZM4.00988 11.1343C4.00991 11.1342 4.01026 11.1344 4.0109 11.1351L4.00988 11.1343ZM4.00989 12.8658C4.00988 12.8657 4.01021 12.8654 4.01091 12.865L4.00989 12.8658ZM6.88854 18.2014C6.88851 18.2013 6.8889 18.2012 6.88979 18.201L6.88854 18.2014ZM19.9886 12.88C19.9885 12.88 19.9881 12.8797 19.9876 12.8792L19.9886 12.88ZM19.9886 11.12C19.9887 11.12 19.9883 11.1204 19.9876 11.1208L19.9886 11.12ZM17.1003 5.78965C17.1003 5.78968 17.0999 5.78986 17.099 5.79006L17.1003 5.78965ZM15.122 4.5937C15.122 4.59371 15.1221 4.59414 15.1222 4.59496L15.122 4.5937ZM13.1222 4L10.85 4V4.60808C10.85 5.49562 10.2774 6.19908 9.57449 6.51017C9.09654 6.7217 8.65096 6.99408 8.24769 7.31774C7.63839 7.80676 6.73261 7.98755 5.93958 7.55888L5.08481 7.09682L4.00015 8.8558L4.9628 9.37617C5.7538 9.80375 6.09888 10.6574 6.02633 11.4329C6.00889 11.6193 5.99995 11.8085 5.99995 12C5.99995 12.1915 6.00889 12.3807 6.02633 12.5671C6.09889 13.3426 5.75381 14.1963 4.9628 14.6239L4.0002 15.1442L5.08486 16.9032L5.9396 16.4411C6.73263 16.0125 7.63841 16.1933 8.2477 16.6823C8.65097 17.0059 9.09654 17.2783 9.57449 17.4898C10.2774 17.8009 10.85 18.5044 10.85 19.3919V20H13.1222V19.404C13.1222 18.5133 13.6987 17.8083 14.4051 17.4988C14.8875 17.2874 15.3372 17.0142 15.744 16.6889C16.3532 16.2017 17.2575 16.0222 18.0492 16.4501L18.8873 16.9032L19.972 15.1442L19.0357 14.6381C18.2434 14.2098 17.8986 13.3541 17.9726 12.5776C17.9907 12.3878 18 12.1951 18 12C18 11.8049 17.9907 11.6123 17.9726 11.4224C17.8986 10.6459 18.2434 9.79023 19.0357 9.36192L19.972 8.85579L18.8874 7.09681L18.0492 7.54989C17.2575 7.97786 16.3532 7.79836 15.744 7.31116C15.3372 6.98582 14.8875 6.71259 14.4051 6.50124C13.6987 6.19173 13.1222 5.48675 13.1222 4.596L13.1222 4Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/quick/responsivelayouts/icons/text.svg b/examples/quick/responsivelayouts/icons/text.svg
new file mode 100644
index 0000000000..a5066b7aff
--- /dev/null
+++ b/examples/quick/responsivelayouts/icons/text.svg
@@ -0,0 +1,4 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 11C3.94772 11 3.5 11.4477 3.5 12C3.5 12.5523 3.94772 13 4.5 13H20.5C21.0523 13 21.5 12.5523 21.5 12C21.5 11.4477 21.0523 11 20.5 11H4.5ZM3.5 16C3.5 15.4477 3.94772 15 4.5 15H20.5C21.0523 15 21.5 15.4477 21.5 16C21.5 16.5523 21.0523 17 20.5 17H4.5C3.94772 17 3.5 16.5523 3.5 16ZM3.5 20C3.5 19.4477 3.94772 19 4.5 19H20.5C21.0523 19 21.5 19.4477 21.5 20C21.5 20.5523 21.0523 21 20.5 21H4.5C3.94772 21 3.5 20.5523 3.5 20Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 3C3.94772 3 3.5 3.44772 3.5 4C3.5 4.55228 3.94772 5 4.5 5H20.5C21.0523 5 21.5 4.55228 21.5 4C21.5 3.44772 21.0523 3 20.5 3H4.5ZM4.5 7C3.94772 7 3.5 7.44772 3.5 8C3.5 8.55228 3.94772 9 4.5 9H20.5C21.0523 9 21.5 8.55228 21.5 8C21.5 7.44772 21.0523 7 20.5 7H4.5Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/quick/responsivelayouts/main.cpp b/examples/quick/responsivelayouts/main.cpp
new file mode 100644
index 0000000000..9eb03f16eb
--- /dev/null
+++ b/examples/quick/responsivelayouts/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char* argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/qt/qml/responsivelayouts/responsivelayouts.qml")));
+
+ return app.exec();
+}
diff --git a/examples/quick/responsivelayouts/responsivelayouts.pro b/examples/quick/responsivelayouts/responsivelayouts.pro
new file mode 100644
index 0000000000..5450a6585a
--- /dev/null
+++ b/examples/quick/responsivelayouts/responsivelayouts.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+QT += qml quick
+
+SOURCES += main.cpp
+
+RESOURCES += \
+ responsivelayouts.qrc
+EXAMPLE_FILES = \
+ responsivelayouts.qml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/responsivelayouts
+INSTALLS += target
+
diff --git a/examples/quick/responsivelayouts/responsivelayouts.qml b/examples/quick/responsivelayouts/responsivelayouts.qml
new file mode 100644
index 0000000000..f9ef99612d
--- /dev/null
+++ b/examples/quick/responsivelayouts/responsivelayouts.qml
@@ -0,0 +1,160 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Window {
+ id: appWindow
+
+ visible: true
+ title: qsTr("Responsive layouts with LayoutItemProxy")
+
+ minimumHeight: 500
+ minimumWidth: 250
+
+ //! [item definition]
+ Rectangle {
+ id: contentItem
+ Layout.fillWidth: true
+ implicitHeight: grid.implicitHeight
+ implicitWidth: grid.implicitWidth
+ color: "#00414A"
+
+ GridLayout {
+ anchors.fill: parent
+ id: grid
+ columns: 2
+ anchors.margins: 8
+ Repeater {
+ model: 18
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.margins: 8
+ implicitWidth: 200
+ implicitHeight: width
+ radius: width / 10
+ gradient: Gradient {
+ GradientStop { position: -0.2; color: "#2CDE85" }
+ GradientStop { position: 1.2; color: "#00414A" }
+ }
+ Text {
+ color: "#ffffff"
+ font.pointSize: 22
+ anchors.centerIn: parent
+ text: index
+ }
+ }
+ }
+ }
+ }
+
+ Button {
+ id: a
+ text: "Text"
+ icon.source: "./icons/text.svg"
+ Layout.fillWidth: true
+ Layout.margins: 3
+ }
+
+ Button {
+ id: b
+ text: "Grid 1"
+ icon.source: "./icons/grid.svg"
+ Layout.fillWidth: true
+ Layout.margins: 3
+ }
+
+ Button {
+ id: c
+ text: "Grid 2"
+ icon.source: "./icons/grid.svg"
+ Layout.fillWidth: true
+ Layout.margins: 3
+ }
+
+ Button {
+ id: d
+ text: "Settings"
+ icon.source: "./icons/settings.svg"
+ Layout.fillWidth: true
+ Layout.margins: 3
+ }
+ //! [item definition]
+
+ //! [first layout]
+ ColumnLayout {
+ id: smallLayout
+ anchors.fill: parent
+
+ //! [proxy in flickable]
+ Flickable {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ contentWidth: width
+ contentHeight: gl.implicitHeight
+ clip: true
+ ScrollIndicator.vertical: ScrollIndicator { }
+ LayoutItemProxy {
+ id: gl
+ width: parent.width
+ height: implicitHeight
+ target: contentItem
+ }
+ }
+ //! [proxy in flickable]
+
+ //! [proxy in layout]
+ RowLayout {
+ Layout.fillHeight: false
+ Layout.fillWidth: true
+ Layout.margins: 5
+ //! [layout property on proxy]
+ LayoutItemProxy{ target: a; }
+ //! [layout property on proxy]
+ LayoutItemProxy{ target: b; }
+ LayoutItemProxy{ target: c; }
+ }
+ //! [proxy in layout]
+ }
+ //! [first layout]
+
+ //! [second layout]
+ RowLayout {
+ id: largeLayout
+ anchors.fill: parent
+ ColumnLayout {
+ Layout.minimumWidth: 100
+ Layout.margins: 2
+ LayoutItemProxy{ target: a }
+ LayoutItemProxy{ target: b }
+ LayoutItemProxy{ target: c }
+ //! [spacer item]
+ Item { Layout.fillHeight: true }
+ //! [spacer item]
+ LayoutItemProxy{ target: d }
+ }
+
+ LayoutItemProxy {
+ Layout.fillHeight: true
+ Layout.fillWidth: false
+ target: contentItem
+ }
+ }
+ //! [second layout]
+
+ //! [setting the layout]
+ function setFittingLayout() {
+ if (width < 450) {
+ smallLayout.visible = true
+ largeLayout.visible = false
+ } else {
+ smallLayout.visible = false
+ largeLayout.visible = true
+ }
+ }
+ onWidthChanged: setFittingLayout()
+ Component.onCompleted: setFittingLayout()
+ //! [setting the layout]
+}
diff --git a/examples/quick/responsivelayouts/responsivelayouts.qrc b/examples/quick/responsivelayouts/responsivelayouts.qrc
new file mode 100644
index 0000000000..76c435692a
--- /dev/null
+++ b/examples/quick/responsivelayouts/responsivelayouts.qrc
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/qt/qml/responsivelayouts">
+ <file>responsivelayouts.qml</file>
+ <file>icons/grid.svg</file>
+ <file>icons/text.svg</file>
+ <file>icons/settings.svg</file>
+</qresource>
+</RCC>
diff --git a/examples/quick/righttoleft/CMakeLists.txt b/examples/quick/righttoleft/CMakeLists.txt
deleted file mode 100644
index d040edce6d..0000000000
--- a/examples/quick/righttoleft/CMakeLists.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(righttoleft LANGUAGES CXX)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/righttoleft")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_standard_project_setup()
-
-add_subdirectory("../shared" "shared")
-
-qt_add_executable(righttoleftexample
- WIN32
- MACOSX_BUNDLE
- main.cpp
-)
-
-qt_add_qml_module(righttoleftexample
- URI righttoleft
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "layoutdirection/layoutdirection.qml"
- "layoutmirroring/layoutmirroring.qml"
- "righttoleft.qml"
- "textalignment/textalignment.qml"
-)
-
-target_link_libraries(righttoleftexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-install(TARGETS righttoleftexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
-
-bundle_shared(righttoleftexample)
diff --git a/examples/quick/righttoleft/doc/images/qml-righttoleft-example.png b/examples/quick/righttoleft/doc/images/qml-righttoleft-example.png
deleted file mode 100644
index 03c3c48f10..0000000000
--- a/examples/quick/righttoleft/doc/images/qml-righttoleft-example.png
+++ /dev/null
Binary files differ
diff --git a/examples/quick/righttoleft/doc/src/righttoleft.qdoc b/examples/quick/righttoleft/doc/src/righttoleft.qdoc
deleted file mode 100644
index f5b390e186..0000000000
--- a/examples/quick/righttoleft/doc/src/righttoleft.qdoc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-/*!
- \title Qt Quick Examples - Right to Left
- \example righttoleft
- \brief This is a collection of QML Right to Left examples.
- \image qml-righttoleft-example.png
- \ingroup qtquickexamples
-
- \e{Right to Left} is a collection of small QML examples relating to
- right to left (RTL) localization support. For more information,
- visit \l{Right-to-left User Interfaces}.
-
- These are the examples in Right to Left:
- \list
- \li \e{Layout Direction} - shows RTL layout direction
- \li \e{Layout Mirroring} - shows automatic mirroring of horizontal layouts
- in RTL locales
- \li \e{Text Alignment} - shows automatic mirroring of text alignment in RTL
- locales
- \endlist
-
- \include examples-run.qdocinc
-*/
diff --git a/examples/quick/righttoleft/layoutdirection/layoutdirection.qml b/examples/quick/righttoleft/layoutdirection/layoutdirection.qml
deleted file mode 100644
index c996278d88..0000000000
--- a/examples/quick/righttoleft/layoutdirection/layoutdirection.qml
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- id: root
- property bool mirror
- property int direction: (Qt.application as Application).layoutDirection
- LayoutMirroring.enabled: mirror
- LayoutMirroring.childrenInherit: true
- width: 320
- height: 480
- Column {
- id: columnA
- spacing: 10
- x: 10
- y: 10
- width: 140
-
- Item {
- id: rowCell
- }
- Text {
- text: "Row"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-
- Row {
- layoutDirection: root.direction
- spacing: 10
- move: Transition {
- NumberAnimation {
- properties: "x"
- }
- }
- Repeater {
- model: 3
- Loader {
- required property int index
- sourceComponent: PositionerDelegate {}
- }
- }
- }
-
- Text {
- text: "Grid"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-
- Grid {
- layoutDirection: root.direction
- spacing: 10; columns: 3
- move: Transition {
- NumberAnimation {
- properties: "x"
- }
- }
- Repeater {
- model: 8
- Loader {
- required property int index
- sourceComponent: PositionerDelegate {}
- }
- }
- }
-
- Text {
- text: "Flow"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-
- Flow {
- layoutDirection: root.direction
- spacing: 10; width: parent.width
- move: Transition {
- NumberAnimation {
- properties: "x"
- }
- }
- Repeater {
- model: 8
- Loader {
- required property int index
- sourceComponent: PositionerDelegate {}
- }
- }
- }
- }
- Column {
- id: columnB
- spacing: 10
- x: 160
- y: 10
-
- Text {
- text: "ListView"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-
- ListView {
- id: listView
- clip: true
- width: parent.width; height: 40
- layoutDirection: root.direction
- orientation: Qt.Horizontal
- model: 48
- delegate: ViewDelegate {}
- }
-
- Text {
- text: "GridView"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-
- GridView {
- clip: true
- width: 150; height: 160
- cellWidth: 50; cellHeight: 50
- layoutDirection: root.direction
- model: 48
- delegate: ViewDelegate {}
- }
-
- Rectangle {
- height: 50; width: parent.width
- color: mouseArea.pressed ? "black" : "gray"
- Column {
- anchors.centerIn: parent
- Text {
- text: root.direction ? "Right to left" : "Left to right"
- color: "white"
- font.pixelSize: 16
- anchors.horizontalCenter: parent.horizontalCenter
- }
- Text {
- text: "(click here to toggle)"
- color: "white"
- font.pixelSize: 10
- font.italic: true
- anchors.horizontalCenter: parent.horizontalCenter
- }
- }
- MouseArea {
- id: mouseArea
- anchors.fill: parent
- onClicked: {
- if (root.direction == Qt.LeftToRight) {
- root.direction = Qt.RightToLeft;
- } else {
- root.direction = Qt.LeftToRight;
- }
- }
- }
- }
-
- Rectangle {
- height: 50; width: parent.width
- color: mouseArea2.pressed ? "black" : "gray"
- Column {
- anchors.centerIn: parent
- Text {
- text: root.mirror ? "Mirrored" : "Not mirrored"
- color: "white"
- font.pixelSize: 16
- anchors.horizontalCenter: parent.horizontalCenter
- }
- Text {
- text: "(click here to toggle)"
- color: "white"
- font.pixelSize: 10
- font.italic: true
- anchors.horizontalCenter: parent.horizontalCenter
- }
- }
- MouseArea {
- id: mouseArea2
- anchors.fill: parent
- onClicked: {
- root.mirror = !root.mirror;
- }
- }
- }
- }
-
- component PositionerDelegate : Rectangle {
- width: 40
- height: 40
- property int lightness: parent.index + 1;
- color: Qt.rgba(0.8 / lightness, 0.8 / lightness, 0.8 / lightness, 1.0)
- Text {
- text: parent.lightness
- color: "white"
- font.pixelSize: 18
- anchors.centerIn: parent
- }
- }
-
- component ViewDelegate : Item {
- id: delegateItem
- required property int index
- width: (listView.effectiveLayoutDirection == Qt.LeftToRight ? (index == 48 - 1) : (index == 0)) ? 40 : 50
- Rectangle {
- width: 40; height: 40
- color: Qt.rgba(0.5 + (48 - delegateItem.index) * Math.random() / 48,
- 0.3 + delegateItem.index * Math.random() / 48,
- 0.3 * Math.random(),
- 1.0)
- Text {
- text: delegateItem.index + 1
- color: "white"
- font.pixelSize: 18
- anchors.centerIn: parent
- }
- }
- }
-}
-
diff --git a/examples/quick/righttoleft/layoutdirection/layoutdirection.qmlproject b/examples/quick/righttoleft/layoutdirection/layoutdirection.qmlproject
deleted file mode 100644
index 33cbb3c9e2..0000000000
--- a/examples/quick/righttoleft/layoutdirection/layoutdirection.qmlproject
+++ /dev/null
@@ -1,16 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "layoutdirection.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/righttoleft/layoutmirroring/layoutmirroring.qml b/examples/quick/righttoleft/layoutmirroring/layoutmirroring.qml
deleted file mode 100644
index e5e2b16623..0000000000
--- a/examples/quick/righttoleft/layoutmirroring/layoutmirroring.qml
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- id: root
- property bool mirror: (Qt.application as Application).layoutDirection == Qt.RightToLeft
- LayoutMirroring.enabled: mirror
- LayoutMirroring.childrenInherit: true
- width: 320
- height: 480
- color: "lightsteelblue"
-
- Column {
- spacing: 10
- anchors { left: parent.left; right: parent.right; top: parent.top; margins: 10 }
-
- Text {
- text: "Text alignment"
- anchors.left: parent.left
- }
-
- Rectangle {
- id: textStrings
- width: 148
- height: 85
- color: "white"
- anchors.left: parent.left
- Column {
- spacing: 5
- width: parent.width
- anchors { fill: parent; margins: 5 }
- Text {
- id: englishText
- width: parent.width
- text: "English text"
- }
- Text {
- id: arabicText
- width: parent.width
- text: "النص العربي"
- }
- Text {
- id: leftAlignedText
- width: parent.width
- text: "Text aligned to left"
- horizontalAlignment: Text.AlignLeft
- }
- Text {
- id: rightAlignedText
- width: parent.width
- text: "Text aligned to right"
- horizontalAlignment: Text.AlignRight
- }
- }
- }
-
- Text {
- text: "Item x"
- anchors.left: parent.left
- }
- Rectangle {
- id: items
- color: Qt.rgba(0.2, 0.2, 0.2, 0.6)
- width: 275; height: 95
- anchors.left: parent.left
- Rectangle {
- y: 5; x: 5
- width: 130; height: 40
- Text {
- text: "Item with x: 5\n(not mirrored)"
- anchors.centerIn: parent
- }
- }
- Rectangle {
- color: Qt.rgba(0.7, 0.7, 0.7)
- y: 50; x: mirror(5)
- width: 130; height: 40
- function mirror(value) {
- return LayoutMirroring.enabled ? (parent.width - width - value) : value;
- }
- Text {
- text: "Item with x: " + parent.x + "\n(manually mirrored)"
- anchors.centerIn: parent
- }
- }
- }
- Text {
- text: "Item anchors"
- anchors.left: parent.left
- }
-
- Rectangle {
- id: anchoredItems
- color: Qt.rgba(0.2, 0.2, 0.2, 0.6)
- width: 270; height: 170
- anchors.left: parent.left
- Rectangle {
- id: blackRectangle
- color: "black"
- width: 180; height: 90
- anchors { horizontalCenter: parent.horizontalCenter; horizontalCenterOffset: 30 }
- Text {
- text: "Horizontal center anchored\nwith offset 30\nto the horizontal center\nof the parent."
- color: "white"
- anchors.centerIn: parent
- }
- }
- Rectangle {
- id: whiteRectangle
- color: "white"
- width: 120; height: 70
- anchors { left: parent.left; bottom: parent.bottom }
- Text {
- text: "Left side anchored\nto the left side\nof the parent."
- color: "black"
- anchors.centerIn: parent
- }
- }
- Rectangle {
- id: grayRectangle
- color: Qt.rgba(0.7, 0.7, 0.7)
- width: 140; height: 90
- anchors { right: parent.right; bottom: parent.bottom }
- Text {
- text: "Right side anchored\nto the right side\nof the parent."
- anchors.centerIn: parent
- }
- }
- }
- }
-
- Rectangle {
- id: mirrorButton
- color: mouseArea2.pressed ? "black" : "gray"
- height: 50; width: 160
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.margins: 10
- Column {
- anchors.centerIn: parent
- Text {
- text: root.mirror ? "Mirrored" : "Not mirrored"
- color: "white"
- font.pixelSize: 16
- anchors.horizontalCenter: parent.horizontalCenter
- }
- Text {
- text: "(click here to toggle)"
- color: "white"
- font.pixelSize: 10
- font.italic: true
- anchors.horizontalCenter: parent.horizontalCenter
- }
- }
- MouseArea {
- id: mouseArea2
- anchors.fill: parent
- onClicked: {
- root.mirror = !root.mirror;
- }
- }
- }
-}
-
diff --git a/examples/quick/righttoleft/layoutmirroring/layoutmirroring.qmlproject b/examples/quick/righttoleft/layoutmirroring/layoutmirroring.qmlproject
deleted file mode 100644
index 72bc04a8a9..0000000000
--- a/examples/quick/righttoleft/layoutmirroring/layoutmirroring.qmlproject
+++ /dev/null
@@ -1,16 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "layoutmirroring.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/righttoleft/main.cpp b/examples/quick/righttoleft/main.cpp
deleted file mode 100644
index 08be06c4d5..0000000000
--- a/examples/quick/righttoleft/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(righttoleft/righttoleft)
diff --git a/examples/quick/righttoleft/righttoleft.pro b/examples/quick/righttoleft/righttoleft.pro
deleted file mode 100644
index 248564e07f..0000000000
--- a/examples/quick/righttoleft/righttoleft.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-RESOURCES += \
- righttoleft.qrc \
- ../shared/shared.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/righttoleft
-INSTALLS += target
diff --git a/examples/quick/righttoleft/righttoleft.qml b/examples/quick/righttoleft/righttoleft.qml
deleted file mode 100644
index 34d5724868..0000000000
--- a/examples/quick/righttoleft/righttoleft.qml
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import shared as Examples
-
-Item {
- height: 480
- width: 320
- Examples.LauncherList {
- id: ll
- anchors.fill: parent
- Component.onCompleted: {
- addExample("Layout Direction", "Shows RTL layouting in positioners and views", Qt.resolvedUrl("layoutdirection/layoutdirection.qml"));
- addExample("Layout Mirroring", "Shows RTL layouting in basic text and anchors", Qt.resolvedUrl("layoutmirroring/layoutmirroring.qml"));
- addExample("Text Alignment", "Shows RTL layouting in various text elements", Qt.resolvedUrl("textalignment/textalignment.qml"));
- }
- }
-}
diff --git a/examples/quick/righttoleft/righttoleft.qmlproject b/examples/quick/righttoleft/righttoleft.qmlproject
deleted file mode 100644
index f394d9030e..0000000000
--- a/examples/quick/righttoleft/righttoleft.qmlproject
+++ /dev/null
@@ -1,16 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "righttoleft.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/righttoleft/righttoleft.qrc b/examples/quick/righttoleft/righttoleft.qrc
deleted file mode 100644
index 07355828cc..0000000000
--- a/examples/quick/righttoleft/righttoleft.qrc
+++ /dev/null
@@ -1,8 +0,0 @@
-<RCC>
- <qresource prefix="/qt/qml/righttoleft">
- <file>righttoleft.qml</file>
- <file>layoutdirection/layoutdirection.qml</file>
- <file>layoutmirroring/layoutmirroring.qml</file>
- <file>textalignment/textalignment.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/quick/righttoleft/textalignment/textalignment.qml b/examples/quick/righttoleft/textalignment/textalignment.qml
deleted file mode 100644
index 49acc37531..0000000000
--- a/examples/quick/righttoleft/textalignment/textalignment.qml
+++ /dev/null
@@ -1,421 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- id: root
- color: "white"
- //width: containerColumn.width
- //height: containerColumn.height + containerColumn.anchors.topMargin
- width: 320
- height: 480
-
- property bool mirror: false
- property int pxSz: 18
- property variant horizontalAlignment: undefined
-
- property variant editorType: ["Plain Text", "Styled Text", "Plain Rich Text", "Italic Rich Text", "Plain TextEdit", "Italic TextEdit", "TextInput"]
- property variant text: ["", " ", "Hello world!", "مرحبا العالم!", "Hello world! Hello!\nHello world! Hello!", "مرحبا العالم! مرحبا! مرحبا العالم! مرحبا!" ,"مرحبا العالم! مرحبا! مرحبا Hello world!\nالعالم! مرحبا!"]
- property variant description: ["empty text", "white-space-only text", "left-to-right text", "right-to-left text", "multi-line left-to-right text", "multi-line right-to-left text", "multi-line bidi text"]
- property variant textComponents: [plainTextComponent, styledTextComponent, richTextComponent, italicRichTextComponent, plainTextEdit, italicTextEdit, textInput]
-
- function shortText(horizontalAlignment) {
-
- // all the different QML editors have
- // the same alignment values
- switch (horizontalAlignment) {
- case Text.AlignLeft:
- return "L";
- case Text.AlignRight:
- return "R";
- case Text.AlignHCenter:
- return "C";
- case Text.AlignJustify:
- return "J";
- default:
- return "Error";
- }
- }
- Column {
- id: containerColumn
- spacing: 10
- width: editorTypeRow.width
- anchors { top: parent.top; topMargin: 5 }
- ListView {
- width: 320
- height: 320
- id: editorTypeRow
- model: root.editorType.length
- orientation: ListView.Horizontal
- cacheBuffer: 1000//Load the really expensive ones async if possible
- delegate: Item {
- id: delegate
-
- width: editorColumn.width
- height: editorColumn.height
-
- required property int index
-
- Column {
- id: editorColumn
- spacing: 5
- width: textColumn.width+10
- Text {
- text: root.editorType[delegate.index]
- font.pixelSize: 16
- anchors.horizontalCenter: parent.horizontalCenter
- }
- Column {
- id: textColumn
- spacing: 5
- anchors.horizontalCenter: parent.horizontalCenter
- Repeater {
- model: root.textComponents.length
- delegate: root.textComponents[delegate.index]
- }
- }
- }
- }
- }
- Column {
- spacing: 2
- width: parent.width
- Rectangle {
- // button
- height: 50; width: parent.width
- color: mouseArea.pressed ? "black" : "lightgray"
- Column {
- anchors.centerIn: parent
- Text {
- text: root.mirror ? "Mirrored" : "Not mirrored"
- color: "white"
- font.pixelSize: 16
- anchors.horizontalCenter: parent.horizontalCenter
- }
- Text {
- text: "(click here to toggle)"
- color: "white"
- font.pixelSize: 10
- font.italic: true
- anchors.horizontalCenter: parent.horizontalCenter
- }
- }
- MouseArea {
- id: mouseArea
- property int index: 0
- anchors.fill: parent
- onClicked: root.mirror = !root.mirror
- }
- }
- Rectangle {
- // button
- height: 50; width: parent.width
- color: mouseArea2.pressed ? "black" : "gray"
- Column {
- anchors.centerIn: parent
- Text {
- text: {
- if (root.horizontalAlignment == undefined)
- return "Implict alignment";
- switch (root.horizontalAlignment) {
- case Text.AlignLeft:
- return "Left alignment";
- case Text.AlignRight:
- return "Right alignment";
- case Text.AlignHCenter:
- return "Center alignment";
- case Text.AlignJustify:
- return "Justify alignment";
- }
- }
- color: "white"
- font.pixelSize: 16
- anchors.horizontalCenter: parent.horizontalCenter
- }
- Text {
- text: "(click here to toggle)"
- color: "white"
- font.pixelSize: 10
- font.italic: true
- anchors.horizontalCenter: parent.horizontalCenter
- }
- }
- MouseArea {
- id: mouseArea2
- property int index: 0
- anchors.fill: parent
- onClicked: {
- if (index < 0) {
- root.horizontalAlignment = undefined;
- } else {
- root.horizontalAlignment = Math.pow(2, index);
- }
- index = (index + 2) % 5 - 1;
- }
- }
- }
- }
- }
-
- Component {
- id: plainTextComponent
- Text {
- required property int index
-
- width: 180
- text: root.text[index]
- font.pixelSize: root.pxSz
- wrapMode: Text.WordWrap
- horizontalAlignment: root.horizontalAlignment
- LayoutMirroring.enabled: root.mirror
- textFormat: Text.RichText
- Rectangle {
- z: -1
- color: Qt.rgba(0.8, 0.2, 0.2, 0.3)
- anchors.fill: parent
- }
- Text {
- text: root.description[parent.index]
- color: Qt.rgba(1,1,1,1.0)
- anchors.centerIn: parent
- font.pixelSize: root.pxSz - 2
- Rectangle {
- z: -1
- color: Qt.rgba(0.3, 0, 0, 0.3)
- anchors { fill: parent; margins: -3 }
- }
- }
- Text {
- color: "white"
- text: root.shortText(parent.horizontalAlignment)
- anchors { top: parent.top; right: parent.right; margins: 2 }
- }
- }
- }
-
- Component {
- id: styledTextComponent
- Text {
- required property int index
-
- width: 180
- text: root.text[index]
- font.pixelSize: root.pxSz
- wrapMode: Text.WordWrap
- horizontalAlignment: root.horizontalAlignment
- LayoutMirroring.enabled: root.mirror
- textFormat: Text.RichText
- style: Text.Sunken
- styleColor: "white"
- Rectangle {
- z: -1
- color: Qt.rgba(0.8, 0.2, 0.2, 0.3)
- anchors.fill: parent
- }
- Text {
- text: root.description[parent.index]
- color: Qt.rgba(1,1,1,1.0)
- anchors.centerIn: parent
- font.pixelSize: root.pxSz - 2
- Rectangle {
- z: -1
- color: Qt.rgba(0.3, 0, 0, 0.3)
- anchors { fill: parent; margins: -3 }
- }
- }
- Text {
- color: "white"
- text: root.shortText(parent.horizontalAlignment)
- anchors { top: parent.top; right: parent.right; margins: 2 }
- }
- }
- }
-
- Component {
- id: richTextComponent
- Text {
- required property int index
-
- width: 180
- text: root.text[index]
- font.pixelSize: root.pxSz
- wrapMode: Text.WordWrap
- horizontalAlignment: root.horizontalAlignment
- LayoutMirroring.enabled: root.mirror
- textFormat: Text.RichText
- Rectangle {
- z: -1
- color: Qt.rgba(0.8, 0.2, 0.2, 0.3)
- anchors.fill: parent
- }
- Text {
- text: root.description[parent.index]
- color: Qt.rgba(1,1,1,1.0)
- anchors.centerIn: parent
- font.pixelSize: root.pxSz - 2
- Rectangle {
- z: -1
- color: Qt.rgba(0.3, 0, 0, 0.3)
- anchors { fill: parent; margins: -3 }
- }
- }
- Text {
- color: "white"
- text: root.shortText(parent.horizontalAlignment)
- anchors { top: parent.top; right: parent.right; margins: 2 }
- }
- }
- }
-
- Component {
- id: italicRichTextComponent
- Text {
- required property int index
-
- width: 180
- text: "<i>" + root.text[index] + "</i>"
- font.pixelSize: root.pxSz
- wrapMode: Text.WordWrap
- horizontalAlignment: root.horizontalAlignment
- LayoutMirroring.enabled: root.mirror
- textFormat: Text.RichText
- property variant backgroundColor: Qt.rgba(0.8, 0.2, 0.2, 0.3)
- Rectangle {
- z: -1
- color: parent.backgroundColor
- anchors.fill: parent
- }
- Text {
- text: root.description[parent.index]
- color: Qt.rgba(1,1,1,1.0)
- anchors.centerIn: parent
- font.pixelSize: root.pxSz - 2
- Rectangle {
- z: -1
- color: Qt.rgba(0.3, 0, 0, 0.3)
- anchors { fill: parent; margins: -3 }
- }
- }
- Text {
- color: "white"
- text: root.shortText(parent.horizontalAlignment)
- anchors { top: parent.top; right: parent.right; margins: 2 }
- }
- }
- }
-
- Component {
- id: plainTextEdit
- TextEdit {
- required property int index
-
- width: 180
- text: root.text[index]
- font.pixelSize: root.pxSz
- cursorVisible: true
- wrapMode: TextEdit.WordWrap
- horizontalAlignment: root.horizontalAlignment
- LayoutMirroring.enabled: root.mirror
- Rectangle {
- z: -1
- color: Qt.rgba(0.5, 0.5, 0.2, 0.3)
- anchors.fill: parent
- }
- Text {
- text: root.description[parent.index]
- color: Qt.rgba(1,1,1,1.0)
- anchors.centerIn: parent
- font.pixelSize: root.pxSz - 2
- Rectangle {
- z: -1
- color: Qt.rgba(0.3, 0, 0, 0.3)
- anchors { fill: parent; margins: -3 }
- }
- }
- Text {
- color: "white"
- text: root.shortText(parent.horizontalAlignment)
- anchors { top: parent.top; right: parent.right; margins: 2 }
- }
- }
- }
-
- Component {
- id: italicTextEdit
- TextEdit {
- required property int index
-
- width: 180
- text: "<i>" + root.text[index] + "<i>"
- font.pixelSize: root.pxSz
- cursorVisible: true
- wrapMode: TextEdit.WordWrap
- textFormat: TextEdit.RichText
- horizontalAlignment: root.horizontalAlignment
- LayoutMirroring.enabled: root.mirror
- Rectangle {
- z: -1
- color: Qt.rgba(0.5, 0.5, 0.2, 0.3)
- anchors.fill: parent
- }
- Text {
- text: root.description[parent.index]
- color: Qt.rgba(1,1,1,1.0)
- anchors.centerIn: parent
- font.pixelSize: root.pxSz - 2
- Rectangle {
- z: -1
- color: Qt.rgba(0.3, 0, 0, 0.3)
- anchors { fill: parent; margins: -3 }
- }
- }
- Text {
- color: "white"
- text: root.shortText(parent.horizontalAlignment)
- anchors { top: parent.top; right: parent.right; margins: 2 }
- }
- }
- }
-
- Component {
- id: textInput
- Item {
- id: textDelegate
- required property int index
- width: 180
- height: textInput.text.length > 20 ? 3*textInput.height : textInput.height
- TextInput {
- id: textInput
- width: 180
- text: root.text[textDelegate.index]
- font.pixelSize: root.pxSz
- cursorVisible: true
- horizontalAlignment: root.horizontalAlignment
- LayoutMirroring.enabled: root.mirror
- Rectangle {
- z: -1
- color: Qt.rgba(0.6, 0.4, 0.2, 0.3)
- anchors.fill: parent
- }
- Text {
- text: root.description[textDelegate.index]
- color: Qt.rgba(1,1,1,1.0)
- anchors.centerIn: parent
- font.pixelSize: root.pxSz - 2
- Rectangle {
- z: -1
- color: Qt.rgba(0.3, 0, 0, 0.3)
- anchors { fill: parent; margins: -3 }
- }
- }
- Text {
- color: "white"
- text: root.shortText(parent.horizontalAlignment)
- anchors { top: parent.top; right: parent.right; margins: 2 }
- }
- }
- }
- }
-}
-
diff --git a/examples/quick/righttoleft/textalignment/textalignment.qmlproject b/examples/quick/righttoleft/textalignment/textalignment.qmlproject
deleted file mode 100644
index e4b5061364..0000000000
--- a/examples/quick/righttoleft/textalignment/textalignment.qmlproject
+++ /dev/null
@@ -1,16 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "textalignment.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/scenegraph/CMakeLists.txt b/examples/quick/scenegraph/CMakeLists.txt
index 834267b46e..19ab9fc73d 100644
--- a/examples/quick/scenegraph/CMakeLists.txt
+++ b/examples/quick/scenegraph/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(customgeometry)
qt_internal_add_example(custommaterial)
@@ -7,8 +7,9 @@ qt_internal_add_example(graph)
qt_internal_add_example(threadedanimation)
qt_internal_add_example(twotextureproviders)
qt_internal_add_example(customrendernode)
+qt_internal_add_example(rhiunderqml)
+qt_internal_add_example(rhitextureitem)
if(QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3)
- qt_internal_add_example(fboitem)
qt_internal_add_example(openglunderqml)
endif()
if(IOS OR MACOS)
diff --git a/examples/quick/scenegraph/customgeometry/CMakeLists.txt b/examples/quick/scenegraph/customgeometry/CMakeLists.txt
index 5662266f41..14b585aa0d 100644
--- a/examples/quick/scenegraph/customgeometry/CMakeLists.txt
+++ b/examples/quick/scenegraph/customgeometry/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(customgeometry_declarative LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/customgeometry")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick)
qt_standard_project_setup()
@@ -20,9 +14,9 @@ qt_add_executable(customgeometry_declarative WIN32 MACOSX_BUNDLE
)
target_link_libraries(customgeometry_declarative PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
)
qt_add_qml_module(customgeometry_declarative
@@ -33,7 +27,16 @@ qt_add_qml_module(customgeometry_declarative
)
install(TARGETS customgeometry_declarative
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET customgeometry_declarative
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc b/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc
index 80ba600a7d..3ac09dd367 100644
--- a/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc
+++ b/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc
@@ -4,12 +4,13 @@
/*!
\example scenegraph/customgeometry
\title Scene Graph - Custom Geometry
+ \examplecategory {Graphics}
\ingroup qtquickexamples
\brief Shows how to implement a custom geometry in the Qt Quick Scene Graph.
- The custom geometry example shows how to create a QQuickItem which
+ The custom geometry example shows how to create a \l QQuickItem that
uses the scene graph API to build a custom geometry for the scene
- graph. It does this by creating a BezierCurve item which is made
+ graph. It does this by creating a \c BezierCurve item, which is made
part of the CustomGeometry module and makes use of this in a QML
file.
@@ -114,7 +115,7 @@
\snippet scenegraph/customgeometry/beziercurve.cpp 6
The scene graph API provides a few commonly used material
- implementations. In this example we use the QSGFlatColorMaterial
+ implementations. In this example we use the QSGFlatColorMaterial,
which will fill the shape defined by the geometry with a solid
color. Again we pass the ownership of the material to the node, so
it can be cleaned up by the scene graph.
@@ -129,7 +130,7 @@
\snippet scenegraph/customgeometry/beziercurve.cpp 8
- To fill the geometry, we first extract the vertex array from
+ To fill the geometry we first extract the vertex array from
it. Since we are using one of the default attribute sets, we can
use the convenience function QSGGeometry::vertexDataAsPoint2D().
Then we go through each segment and calculate its position and
@@ -137,7 +138,7 @@
\snippet scenegraph/customgeometry/beziercurve.cpp 9
- In the end of the function, we return the node so the scene graph
+ At the end of the function we return the node so the scene graph
can render it.
\section1 Application Entry-Point
@@ -152,9 +153,26 @@
To make use of the BezierCurve item, we need to register it in the QML
engine, using the QML_ELEMENT macro. This gives it the name
BezierCurve and makes it part of the \c {CustomGeometry 1.0}
- module as defined in the customgeometry.pro file:
-
- \quotefile scenegraph/customgeometry/customgeometry.pro
+ module as defined in the project's build files:
+
+ \if defined(onlinedocs)
+ \tab {build-qt-app}{tab-cmake}{CMake}{checked}
+ \tab {build-qt-app}{tab-qmake}{qmake}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section1 Using CMake
+ \endif
+ \quotefile scenegraph/customgeometry/CMakeLists.txt
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+ \else
+ \section1 Using qmake
+ \endif
+ \quotefile scenegraph/customgeometry/customgeometry.pro
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
As the bezier curve is drawn as line strips, we specify that
the view should be multisampled to get antialiasing. This is not
diff --git a/examples/quick/scenegraph/custommaterial/CMakeLists.txt b/examples/quick/scenegraph/custommaterial/CMakeLists.txt
index eacdd80642..8fe276d248 100644
--- a/examples/quick/scenegraph/custommaterial/CMakeLists.txt
+++ b/examples/quick/scenegraph/custommaterial/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(custommaterial_declarative LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/custommaterial")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
qt_standard_project_setup()
@@ -20,10 +14,10 @@ qt_add_executable(custommaterial_declarative WIN32 MACOSX_BUNDLE
)
target_link_libraries(custommaterial_declarative PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(custommaterial_declarative
@@ -46,7 +40,16 @@ qt6_add_shaders(custommaterial_declarative "shaders"
)
install(TARGETS custommaterial_declarative
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET custommaterial_declarative
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc b/examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc
index 997db4a88f..2bbcbbe263 100644
--- a/examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc
+++ b/examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc
@@ -4,6 +4,7 @@
/*!
\example scenegraph/custommaterial
\title Scene Graph - Custom Material
+ \examplecategory {Graphics}
\ingroup qtquickexamples
\brief Shows how to implement a custom material in the Qt Quick Scene Graph.
diff --git a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag
index 0e5f63e7a8..72d63bb32c 100644
--- a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag
+++ b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
//! [1]
#version 440
@@ -27,21 +30,30 @@ void main()
c.x = (vTexCoord.x - 0.5) / ubuf.zoom + ubuf.center.x;
c.y = aspect_ratio * (vTexCoord.y - 0.5) / ubuf.zoom + ubuf.center.y;
- int i;
+ int iLast;
z = c;
- for (i = 0; i < ubuf.limit; i++) {
+ for (int i = 0; i < 1000000; i++) {
+ if (i >= ubuf.limit)
+ {
+ iLast = i;
+ break;
+ }
float x = (z.x * z.x - z.y * z.y) + c.x;
float y = (z.y * z.x + z.x * z.y) + c.y;
- if ((x * x + y * y) > 4.0) break;
+ if ((x * x + y * y) > 4.0)
+ {
+ iLast = i;
+ break;
+ }
z.x = x;
z.y = y;
}
- if (i == ubuf.limit) {
+ if (iLast == ubuf.limit) {
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
- float f = (i * 1.0) / ubuf.limit;
+ float f = (iLast * 1.0) / ubuf.limit;
fragColor = mix(color1, color2, sqrt(f));
}
}
diff --git a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsb b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsb
index 550c0377ae..97ec1c2633 100644
--- a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsb
+++ b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsb b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsb
index ba2904d1c9..37105cfe60 100644
--- a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsb
+++ b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/customrendernode/CMakeLists.txt b/examples/quick/scenegraph/customrendernode/CMakeLists.txt
index bff303029a..c00f019839 100644
--- a/examples/quick/scenegraph/customrendernode/CMakeLists.txt
+++ b/examples/quick/scenegraph/customrendernode/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(customrendernode LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/customrendernode")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
qt_standard_project_setup()
@@ -20,10 +14,10 @@ qt_add_executable(customrendernode WIN32 MACOSX_BUNDLE
)
target_link_libraries(customrendernode PRIVATE
- Qt::Core
- Qt::GuiPrivate
- Qt::Qml
- Qt::QuickPrivate
+ Qt6::Core
+ Qt6::GuiPrivate
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(customrendernode
@@ -35,6 +29,8 @@ qt_add_qml_module(customrendernode
)
qt6_add_shaders(customrendernode "shaders"
+ # gl_VertexId is in earliest OpenGL ES 3.00, and GLSL 130
+ GLSL "300 es,130"
PREFIX
"/scenegraph/customrendernode"
FILES
@@ -43,7 +39,16 @@ qt6_add_shaders(customrendernode "shaders"
)
install(TARGETS customrendernode
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET customrendernode
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/customrendernode/customrender.cpp b/examples/quick/scenegraph/customrendernode/customrender.cpp
index e95341438e..ff5274c163 100644
--- a/examples/quick/scenegraph/customrendernode/customrender.cpp
+++ b/examples/quick/scenegraph/customrendernode/customrender.cpp
@@ -1,21 +1,20 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "customrender.h"
#include <QSGTextureProvider>
#include <QSGRenderNode>
#include <QSGTransformNode>
-#include <QSGRendererInterface>
#include <QQuickWindow>
#include <QFile>
-#include <private/qrhi_p.h>
-#include <private/qsgrendernode_p.h>
+#include <rhi/qrhi.h>
+
+//![node]
class CustomRenderNode : public QSGRenderNode
{
public:
CustomRenderNode(QQuickWindow *window);
- virtual ~CustomRenderNode();
void setVertices(const QList<QVector2D> &vertices);
@@ -26,44 +25,31 @@ public:
QSGRenderNode::StateFlags changedStates() const override;
protected:
- QQuickWindow *m_window = nullptr;
- QRhiBuffer *m_vertexBuffer = nullptr;
- QRhiBuffer *m_uniformBuffer = nullptr;
- QRhiShaderResourceBindings *m_resourceBindings = nullptr;
- QRhiGraphicsPipeline *m_pipeLine = nullptr;
+ QQuickWindow *m_window;
+ std::unique_ptr<QRhiBuffer> m_vertexBuffer;
+ std::unique_ptr<QRhiBuffer> m_uniformBuffer;
+ std::unique_ptr<QRhiShaderResourceBindings> m_resourceBindings;
+ std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
QList<QRhiShaderStage> m_shaders;
bool m_verticesDirty = true;
QList<QVector2D> m_vertices;
};
+//![node]
-CustomRenderNode::CustomRenderNode(QQuickWindow *window) : m_window(window)
+CustomRenderNode::CustomRenderNode(QQuickWindow *window)
+ : m_window(window)
{
- Q_ASSERT(QFile::exists(":/scenegraph/customrendernode/shaders/customrender.vert.qsb"));
- Q_ASSERT(QFile::exists(":/scenegraph/customrendernode/shaders/customrender.frag.qsb"));
-
QFile file;
file.setFileName(":/scenegraph/customrendernode/shaders/customrender.vert.qsb");
- file.open(QFile::ReadOnly);
- m_shaders.append(
- QRhiShaderStage(QRhiShaderStage::Vertex, QShader::fromSerialized(file.readAll())));
+ if (!file.open(QFile::ReadOnly))
+ qFatal("Failed to load vertex shader");
+ m_shaders.append(QRhiShaderStage(QRhiShaderStage::Vertex, QShader::fromSerialized(file.readAll())));
file.close();
file.setFileName(":/scenegraph/customrendernode/shaders/customrender.frag.qsb");
- file.open(QFile::ReadOnly);
- m_shaders.append(
- QRhiShaderStage(QRhiShaderStage::Fragment, QShader::fromSerialized(file.readAll())));
-}
-
-CustomRenderNode::~CustomRenderNode()
-{
- if (m_pipeLine)
- delete m_pipeLine;
- if (m_resourceBindings)
- delete m_resourceBindings;
- if (m_vertexBuffer)
- delete m_vertexBuffer;
- if (m_uniformBuffer)
- delete m_uniformBuffer;
+ if (!file.open(QFile::ReadOnly))
+ qFatal("Failed to load fragment shader");
+ m_shaders.append(QRhiShaderStage(QRhiShaderStage::Fragment, QShader::fromSerialized(file.readAll())));
}
void CustomRenderNode::setVertices(const QList<QVector2D> &vertices)
@@ -77,148 +63,129 @@ void CustomRenderNode::setVertices(const QList<QVector2D> &vertices)
markDirty(QSGNode::DirtyGeometry);
}
+//![node-release]
void CustomRenderNode::releaseResources()
{
- if (m_vertexBuffer) {
- delete m_vertexBuffer;
- m_vertexBuffer = nullptr;
- }
-
- if (m_uniformBuffer) {
- delete m_uniformBuffer;
- m_uniformBuffer = nullptr;
- }
-
- if (m_pipeLine) {
- delete m_pipeLine;
- m_pipeLine = nullptr;
- }
-
- if (m_resourceBindings) {
- delete m_resourceBindings;
- m_resourceBindings = nullptr;
- }
-
+ m_vertexBuffer.reset();
+ m_uniformBuffer.reset();
+ m_pipeline.reset();
+ m_resourceBindings.reset();
}
+//![node-release]
+//![node-flags]
QSGRenderNode::RenderingFlags CustomRenderNode::flags() const
{
- // We are rendering 2D content directly into the scene graph
- return { QSGRenderNode::NoExternalRendering | QSGRenderNode::DepthAwareRendering };
+ // We are rendering 2D content directly into the scene graph using QRhi, no
+ // direct usage of a 3D API. Hence NoExternalRendering. This is a minor
+ // optimization.
+
+ // Additionally, the node takes the item transform into account by relying
+ // on projectionMatrix() and matrix() (see prepare()) and never rendering at
+ // other Z coordinates. Hence DepthAwareRendering. This is a potentially
+ // bigger optimization.
+
+ return QSGRenderNode::NoExternalRendering | QSGRenderNode::DepthAwareRendering;
}
+//![node-flags]
QSGRenderNode::StateFlags CustomRenderNode::changedStates() const
{
- return {QSGRenderNode::StateFlag::ViewportState | QSGRenderNode::StateFlag::CullState};
+ // In Qt 6 only ViewportState and ScissorState matter, the rest is ignored.
+ return QSGRenderNode::StateFlag::ViewportState | QSGRenderNode::StateFlag::CullState;
}
+//![node-prepare]
void CustomRenderNode::prepare()
{
- QSGRendererInterface *renderInterface = m_window->rendererInterface();
- QRhiSwapChain *swapChain = static_cast<QRhiSwapChain *>(
- renderInterface->getResource(m_window, QSGRendererInterface::RhiSwapchainResource));
- QRhi *rhi = static_cast<QRhi *>(
- renderInterface->getResource(m_window, QSGRendererInterface::RhiResource));
- Q_ASSERT(swapChain);
- Q_ASSERT(rhi);
-
+ QRhi *rhi = m_window->rhi();
QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
if (m_verticesDirty) {
- if (m_vertexBuffer) {
- delete m_vertexBuffer;
- m_vertexBuffer = nullptr;
- }
+ m_vertexBuffer.reset();
m_verticesDirty = false;
}
if (!m_vertexBuffer) {
- m_vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
- m_vertices.count() * sizeof(QVector2D));
+ m_vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
+ m_vertices.count() * sizeof(QVector2D)));
m_vertexBuffer->create();
- resourceUpdates->uploadStaticBuffer(m_vertexBuffer, m_vertices.constData());
+ resourceUpdates->uploadStaticBuffer(m_vertexBuffer.get(), m_vertices.constData());
}
-
+//![node-prepare]
if (!m_uniformBuffer) {
- m_uniformBuffer = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
+ m_uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68));
m_uniformBuffer->create();
}
if (!m_resourceBindings) {
- m_resourceBindings = rhi->newShaderResourceBindings();
+ m_resourceBindings.reset(rhi->newShaderResourceBindings());
m_resourceBindings->setBindings({ QRhiShaderResourceBinding::uniformBuffer(
0,
QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
- m_uniformBuffer) });
+ m_uniformBuffer.get()) });
m_resourceBindings->create();
}
- if (!m_pipeLine) {
+ if (!m_pipeline) {
+ m_pipeline.reset(rhi->newGraphicsPipeline());
- m_pipeLine = rhi->newGraphicsPipeline();
-
- //
// If layer.enabled == true on our QQuickItem, the rendering face is flipped for
// backends with isYUpInFrameBuffer == true (OpenGL). This does not happen with
// RHI backends with isYUpInFrameBuffer == false. We swap the triangle winding
// order to work around this.
- //
- m_pipeLine->setFrontFace(QSGRenderNodePrivate::get(this)->m_rt.rt->resourceType() == QRhiResource::TextureRenderTarget
+ m_pipeline->setFrontFace(renderTarget()->resourceType() == QRhiResource::TextureRenderTarget
&& rhi->isYUpInFramebuffer()
? QRhiGraphicsPipeline::CW
: QRhiGraphicsPipeline::CCW);
- m_pipeLine->setCullMode(QRhiGraphicsPipeline::Back);
- m_pipeLine->setTopology(QRhiGraphicsPipeline::TriangleStrip);
+ m_pipeline->setCullMode(QRhiGraphicsPipeline::Back);
+ m_pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
QRhiGraphicsPipeline::TargetBlend blend;
blend.enable = true;
- m_pipeLine->setTargetBlends({ blend });
- m_pipeLine->setShaderResourceBindings(m_resourceBindings);
- m_pipeLine->setShaderStages(m_shaders.cbegin(), m_shaders.cend());
- m_pipeLine->setDepthTest(true);
+ m_pipeline->setTargetBlends({ blend });
+ m_pipeline->setShaderResourceBindings(m_resourceBindings.get());
+ m_pipeline->setShaderStages(m_shaders.cbegin(), m_shaders.cend());
+ m_pipeline->setDepthTest(true);
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 2 * sizeof(float) } });
inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
- m_pipeLine->setVertexInputLayout(inputLayout);
- m_pipeLine->setRenderPassDescriptor(QSGRenderNodePrivate::get(this)->m_rt.rpDesc);
- m_pipeLine->create();
+ m_pipeline->setVertexInputLayout(inputLayout);
+ m_pipeline->setRenderPassDescriptor(renderTarget()->renderPassDescriptor());
+ m_pipeline->create();
}
- QMatrix4x4 mvp = *projectionMatrix() * *matrix();
- float opacity = inheritedOpacity();
-
- resourceUpdates->updateDynamicBuffer(m_uniformBuffer, 0, 64, mvp.constData());
- resourceUpdates->updateDynamicBuffer(m_uniformBuffer, 64, 4, &opacity);
+ const QMatrix4x4 mvp = *projectionMatrix() * *matrix();
+ const float opacity = inheritedOpacity();
- swapChain->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
+ resourceUpdates->updateDynamicBuffer(m_uniformBuffer.get(), 0, 64, mvp.constData());
+ resourceUpdates->updateDynamicBuffer(m_uniformBuffer.get(), 64, 4, &opacity);
+ commandBuffer()->resourceUpdate(resourceUpdates);
}
-void CustomRenderNode::render(const RenderState *state)
+//![node-render]
+void CustomRenderNode::render(const RenderState *)
{
-
- QSGRendererInterface *renderInterface = m_window->rendererInterface();
- QRhiSwapChain *swapChain = static_cast<QRhiSwapChain *>(
- renderInterface->getResource(m_window, QSGRendererInterface::RhiSwapchainResource));
- Q_ASSERT(swapChain);
-
- QRhiCommandBuffer *cb = swapChain->currentFrameCommandBuffer();
- Q_ASSERT(cb);
-
- cb->setGraphicsPipeline(m_pipeLine);
- QSize renderTargetSize = QSGRenderNodePrivate::get(this)->m_rt.rt->pixelSize();
+ QRhiCommandBuffer *cb = commandBuffer();
+ cb->setGraphicsPipeline(m_pipeline.get());
+ QSize renderTargetSize = renderTarget()->pixelSize();
cb->setViewport(QRhiViewport(0, 0, renderTargetSize.width(), renderTargetSize.height()));
cb->setShaderResources();
- QRhiCommandBuffer::VertexInput vertexBindings[] = { { m_vertexBuffer, 0 } };
+ QRhiCommandBuffer::VertexInput vertexBindings[] = { { m_vertexBuffer.get(), 0 } };
cb->setVertexInput(0, 1, vertexBindings);
cb->draw(m_vertices.count());
}
+//![node-render]
-CustomRender::CustomRender(QQuickItem *parent) : QQuickItem(parent)
+//![item-ctor]
+CustomRender::CustomRender(QQuickItem *parent)
+ : QQuickItem(parent)
{
setFlag(ItemHasContents, true);
connect(this, &CustomRender::verticesChanged, this, &CustomRender::update);
}
+//![item-ctor]
-const QList<QVector2D> &CustomRender::vertices() const
+QList<QVector2D> CustomRender::vertices() const
{
return m_vertices;
}
@@ -232,6 +199,7 @@ void CustomRender::setVertices(const QList<QVector2D> &newVertices)
emit verticesChanged();
}
+//![item-update]
QSGNode *CustomRender::updatePaintNode(QSGNode *old, UpdatePaintNodeData *)
{
CustomRenderNode *node = static_cast<CustomRenderNode *>(old);
@@ -243,3 +211,4 @@ QSGNode *CustomRender::updatePaintNode(QSGNode *old, UpdatePaintNodeData *)
return node;
}
+//![item-update]
diff --git a/examples/quick/scenegraph/customrendernode/customrender.h b/examples/quick/scenegraph/customrendernode/customrender.h
index 962551c0b1..530e692678 100644
--- a/examples/quick/scenegraph/customrendernode/customrender.h
+++ b/examples/quick/scenegraph/customrendernode/customrender.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CUSTOMRENDER_H
#define CUSTOMRENDER_H
@@ -7,6 +7,7 @@
#include <QQuickItem>
#include <QVector2D>
+//![item]
class CustomRender : public QQuickItem
{
Q_OBJECT
@@ -16,11 +17,10 @@ class CustomRender : public QQuickItem
public:
explicit CustomRender(QQuickItem *parent = nullptr);
- const QList<QVector2D> &vertices() const;
+ QList<QVector2D> vertices() const;
void setVertices(const QList<QVector2D> &newVertices);
signals:
-
void verticesChanged();
protected:
@@ -29,5 +29,6 @@ protected:
private:
QList<QVector2D> m_vertices;
};
+//![item]
#endif // CUSTOMRENDER_H
diff --git a/examples/quick/scenegraph/customrendernode/doc/images/customrendernode-example.gif b/examples/quick/scenegraph/customrendernode/doc/images/customrendernode-example.gif
deleted file mode 100644
index c4b36d19fb..0000000000
--- a/examples/quick/scenegraph/customrendernode/doc/images/customrendernode-example.gif
+++ /dev/null
Binary files differ
diff --git a/examples/quick/scenegraph/customrendernode/doc/images/customrendernode-example.jpg b/examples/quick/scenegraph/customrendernode/doc/images/customrendernode-example.jpg
new file mode 100644
index 0000000000..db4b50bbc0
--- /dev/null
+++ b/examples/quick/scenegraph/customrendernode/doc/images/customrendernode-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc b/examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc
index 1972d99c4d..3e3dd7d023 100644
--- a/examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc
+++ b/examples/quick/scenegraph/customrendernode/doc/src/customrendernode.qdoc
@@ -4,23 +4,125 @@
/*!
\example scenegraph/customrendernode
\title Scene Graph - Custom QSGRenderNode
+ \examplecategory {Graphics}
\ingroup qtquickexamples
\brief Shows how to use QSGRenderNode to implement custom rendering in the Qt Quick scenegraph.
- The custom render node example shows how to implement an item that is rendered using
- a custom QSGRenderNode.
+ The custom render node example shows how to implement a QQuickItem subclass
+ that is backed by a scene graph node derived from QSGRenderNode, providing
+ it's own QRhi-based rendering.
- \image customrendernode-example.gif
+ \image customrendernode-example.jpg
- QSGRenderNode allows direct access to the Render Hardware Interface (RHI) within the
- scenegraph. This example demonstrates how to create QSGRenderNode based render node
- and manage it with a custom item. The render node creates an RHI pipeline, updates
- vertex and uniform buffers, and renders into the RHI command buffer.
+ \note This example demonstrates advanced, low-level functionality performing
+ portable, cross-platform 3D rendering, while relying on APIs with limited
+ compatibility guarantee from the Qt Gui module. To be able to use the QRhi
+ APIs, the application links to \c{Qt::GuiPrivate} and includes
+ \c{<rhi/qrhi.h>}.
- \warning This example demonstrates advanced, low-level functionality
- performing portable, cross-platform 3D rendering, while relying on private
- APIs from the Qt Gui and Qt Quick modules. Developers are encouraged to
- carefully evaluate the potential lack of source and binary compatibility
- guarantees before using these APIs in their applications.
+ QSGRenderNode allows direct access to the Render Hardware Interface (RHI)
+ within the scenegraph. This example demonstrates how to create QSGRenderNode
+ based render node and manage it with a custom item. The render node creates
+ an RHI pipeline, updates vertex and uniform buffers, and renders into the
+ RHI command buffer.
- */
+ In practice this is a portable, cross-platform approach to perform custom
+ rendering inline with the scenegraph's own rendering, without resorting to a
+ native 3D API such as OpenGL, Metal, or Vulkan. Rather, the application uses
+ Qt's graphics and shader abstraction layer.
+
+ QSGRenderNode is the enabler for one of the three ways to integrate custom
+ 2D/3D rendering into a Qt Quick scene. The other two options are to perform
+ the rendering \c before or \c after the Qt Quick scene's own rendering,
+ or to generate a whole separate render pass targeting a dedicated render
+ target (a texture) and then have an item in the scene display the texture.
+ The QSGRenderNode-based approach is similar to the former, in the sense
+ that no additional render passes or render targets are involved, and allows
+ injecting custom rendering commands "inline" with the Qt Quick scene's
+ own rendering.
+
+ Refer to the following examples for these three approaches:
+
+ \list
+
+ \li \l{Scene Graph - RHI Under QML} - Demonstrates an "underlay" approach
+ based on the \l{QQuickWindow::beforeRendering()} signal. No additional
+ render pass and resources are needed, but composition and blending with the
+ rest of the Qt Quick scene is quite limited. Rendering "under" or "over" the
+ Qt Quick scene is the simplest approach.
+
+ \li \l{Scene Graph - RHI Texture Item} - Demonstrates creating a custom
+ QQuickItem that renders into a texture and displays a quad textured with the
+ generated content. This is very flexible and allows complete blending and
+ composition of the resulting 2D image with the rest of the Qt Quick scene.
+ That comes at the expense of an additional render pass and render target.
+
+ \li This example - Demonstrates the "inline" approach, where the Qt Quick
+ scene graph calls into the custom item and node implementation during the
+ main render pass. This approach can be great for performance (no extra
+ render passes, texturing, and blending are involved), but has potential
+ pitfalls and is the most complicated method.
+
+ \endlist
+
+ The custom item derives from QQuickItem. Most importantly, it reimplements
+ \l{QQuickItem::}{updatePaintNode()}.
+
+ \snippet scenegraph/customrendernode/customrender.h item
+
+ The constructor sets the \l{QQuickItem::}{ItemHasContents} flag to indicate
+ that this is a visual item.
+
+ \snippet scenegraph/customrendernode/customrender.cpp item-ctor
+
+ The updatePaintNode() implementation creates an instance of the custom
+ scenegraph node, if not yet done. The backing QSGNode tree for this item
+ consists of a single node, an instance of a QSGRenderNode-derived class.
+ When Qt Quick's threaded rendering model is in use, this function is called
+ on the render thread with the main thread blocked. That is why it is safe to
+ access main thread data (such as data stored in QQuickItems). The node, the
+ instance of the QSGRenderNode subclass, is going to "live on" the render
+ thread.
+
+ \snippet scenegraph/customrendernode/customrender.cpp item-update
+
+ The \c CustomRenderNode class derives from QSGRenderNode, reimplementing a
+ number of virtual functions. To manage QRhi resources (buffers, pipelines,
+ etc.), smart pointers are quite useful in this case, because the node is
+ destroyed by the scene graph together with the rest of the scene on the
+ render thread (if there is one) while the QRhi is still available, and
+ therefore releasing resources from the destructor or via smart pointers is
+ legal and safe.
+
+ \snippet scenegraph/customrendernode/customrender.cpp node
+
+ Well-behaving QSGRenderNode subclasses also reimplement
+ \l{QSGRenderNode::}{releaseResources()}, which in this case can be a simple
+ set of reset() calls.
+
+ \snippet scenegraph/customrendernode/customrender.cpp node-release
+
+ This QSGRenderNode is performing its rendering through the QRhi APIs (and
+ not directly through OpenGL, Vulkan, Metal, etc.), and it takes the item
+ transform into account (as it only really does 2D rendering). Hence
+ specifying the appropriate flags, which may bring a small performance
+ improvement.
+
+ \snippet scenegraph/customrendernode/customrender.cpp node-flags
+
+ The prepare() and render() functions are called every time the Qt Quick
+ scene renders. The first is called when preparing (but not yet recording)
+ the render pass. This typically creates resources, such as buffers,
+ textures, and graphics pipelines, if not yet done, and enqueues uploading
+ data to them.
+
+ \snippet scenegraph/customrendernode/customrender.cpp node-prepare
+
+ The render() function is called while the recording of a render pass,
+ targeting either the QQuickWindow's swapchain, or a texture (in case of
+ layered items, or when within a ShaderEffectSource), is active.
+
+ \snippet scenegraph/customrendernode/customrender.cpp node-render
+
+ \sa QSGRenderNode, QRhi, {Scene Graph - RHI Under QML}, {Scene Graph - RHI Texture Item}, {Qt Quick Scene Graph}
+*/
diff --git a/examples/quick/scenegraph/customrendernode/main.cpp b/examples/quick/scenegraph/customrendernode/main.cpp
index 5cc870b1aa..0ad93ec1de 100644
--- a/examples/quick/scenegraph/customrendernode/main.cpp
+++ b/examples/quick/scenegraph/customrendernode/main.cpp
@@ -1,12 +1,15 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
-#include <QtQuick/QQuickView>
+#include <QQuickView>
#include <QSurfaceFormat>
int main(int argc, char **argv)
{
+ QGuiApplication app(argc, argv);
+
+ // On macOS, request a core profile context in the unlikely case of using OpenGL.
#ifdef Q_OS_MACOS
QSurfaceFormat format = QSurfaceFormat::defaultFormat();
format.setMajorVersion(4);
@@ -15,30 +18,31 @@ int main(int argc, char **argv)
QSurfaceFormat::setDefaultFormat(format);
#endif
- QGuiApplication app(argc, argv);
-
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:///scenegraph/customrendernode/main.qml"));
- view.setColor(QColor(0, 0, 0));
+ view.setColor(Qt::black);
view.show();
QString api;
switch (view.graphicsApi()) {
- case QSGRendererInterface::GraphicsApi::OpenGLRhi:
+ case QSGRendererInterface::OpenGL:
api = "RHI OpenGL";
break;
- case QSGRendererInterface::GraphicsApi::Direct3D11Rhi:
- api = "RHI Direct3D";
+ case QSGRendererInterface::Direct3D11:
+ api = "RHI Direct 3D 11";
+ break;
+ case QSGRendererInterface::Direct3D12:
+ api = "RHI Direct 3D 12";
break;
- case QSGRendererInterface::GraphicsApi::VulkanRhi:
+ case QSGRendererInterface::Vulkan:
api = "RHI Vulkan";
break;
- case QSGRendererInterface::GraphicsApi::MetalRhi:
+ case QSGRendererInterface::Metal:
api = "RHI Metal";
break;
- case QSGRendererInterface::GraphicsApi::NullRhi:
+ case QSGRendererInterface::Null:
api = "RHI Null";
break;
default:
@@ -48,5 +52,5 @@ int main(int argc, char **argv)
view.setTitle(QStringLiteral("Custom QSGRenderNode - ") + api);
- return QGuiApplication::exec();
+ return app.exec();
}
diff --git a/examples/quick/scenegraph/customrendernode/main.qml b/examples/quick/scenegraph/customrendernode/main.qml
index 5c9bc240ad..6a098e47f7 100644
--- a/examples/quick/scenegraph/customrendernode/main.qml
+++ b/examples/quick/scenegraph/customrendernode/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -45,11 +45,10 @@ Item {
anchors.bottom: parent.bottom
anchors.margins: 20
wrapMode: Text.WordWrap
- text: qsTr("This example creates a custom scenegraph QSGRenderNode render node and " +
- "demonstrates its use. The render node is placed in front of a red " +
- "rectangle, and behind a white rectangle. Rendering is demonstrated " +
- "directly into the scenegraph, and as a layered item. Opacity and " +
- "rotation transform changes are exercised.")
+ text: qsTr("This example creates a custom QQuickItem backed by a QSGRenderNode in the scene graph. " +
+ "The render node is placed in front of a red rectangle, and behind a white rectangle. " +
+ "Rendering is demonstrated directly into the scenegraph, and as a layered, texture-backed item (layer.enabled set to true). " +
+ "Opacity and rotation transform changes are exercised as well.")
Rectangle {
z:-1
@@ -103,6 +102,11 @@ Item {
}
+ Button {
+ text: qsTr("Toggle custom item visibility")
+ onClicked: custom.visible = !custom.visible
+ }
+
CustomRender {
id: custom
width: Math.min(parent.width, parent.height)
diff --git a/examples/quick/scenegraph/customrendernode/shaders/customrender.frag b/examples/quick/scenegraph/customrendernode/shaders/customrender.frag
index 5d284b7696..d9586ba90e 100644
--- a/examples/quick/scenegraph/customrendernode/shaders/customrender.frag
+++ b/examples/quick/scenegraph/customrendernode/shaders/customrender.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec3 color;
diff --git a/examples/quick/scenegraph/d3d11underqml/CMakeLists.txt b/examples/quick/scenegraph/d3d11underqml/CMakeLists.txt
index 2a08c39d59..dec029d8da 100644
--- a/examples/quick/scenegraph/d3d11underqml/CMakeLists.txt
+++ b/examples/quick/scenegraph/d3d11underqml/CMakeLists.txt
@@ -1,17 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(d3d11underqml LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/d3d11underqml")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_add_executable(d3d11underqml
@@ -25,10 +19,10 @@ set_target_properties(d3d11underqml PROPERTIES
)
target_link_libraries(d3d11underqml PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
d3d11
d3dcompiler
)
@@ -45,7 +39,16 @@ qt_add_qml_module(d3d11underqml
)
install(TARGETS d3d11underqml
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET d3d11underqml
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
index 944d9de611..3d5a171d49 100644
--- a/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
+++ b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
@@ -4,6 +4,7 @@
/*!
\example scenegraph/d3d11underqml
\title Scene Graph - Direct3D 11 Under QML
+ \examplecategory {Graphics}
\ingroup qtquickexamples
\brief Shows how to render directly with Direct3D 11 under a Qt Quick scene.
diff --git a/examples/quick/scenegraph/d3d11underqml/squircle.frag b/examples/quick/scenegraph/d3d11underqml/squircle.frag
index a907c84e58..713b2d89f2 100644
--- a/examples/quick/scenegraph/d3d11underqml/squircle.frag
+++ b/examples/quick/scenegraph/d3d11underqml/squircle.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cbuffer buf : register(b0)
{
float ubuf_t : packoffset(c0);
diff --git a/examples/quick/scenegraph/fboitem/CMakeLists.txt b/examples/quick/scenegraph/fboitem/CMakeLists.txt
deleted file mode 100644
index f3fbce11aa..0000000000
--- a/examples/quick/scenegraph/fboitem/CMakeLists.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(fboitem LANGUAGES CXX)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/fboitem")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_standard_project_setup()
-
-qt_add_executable(fboitem WIN32 MACOSX_BUNDLE
- ../shared/logorenderer.cpp ../shared/logorenderer.h
- fboinsgrenderer.cpp fboinsgrenderer.h
- main.cpp
-)
-
-target_include_directories(fboitem PUBLIC
- ../shared
-)
-
-target_link_libraries(fboitem PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-qt_add_qml_module(fboitem
- URI SceneGraphRendering
- QML_FILES main.qml
- RESOURCES shaders/checker.frag.qsb
- RESOURCE_PREFIX /scenegraph/fboitem
- NO_RESOURCE_TARGET_PATH
-)
-
-install(TARGETS fboitem
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpg b/examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpg
deleted file mode 100644
index 306b8bab20..0000000000
--- a/examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc b/examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc
deleted file mode 100644
index daa784bf79..0000000000
--- a/examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
- \example scenegraph/fboitem
- \title Scene Graph - Rendering FBOs
- \ingroup qtquickexamples
-
- \brief Shows how to use FramebufferObjects with Qt Quick.
-
- \image fboitem-example.jpg
- */
diff --git a/examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp b/examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp
deleted file mode 100644
index e4c0e61843..0000000000
--- a/examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "fboinsgrenderer.h"
-#include "logorenderer.h"
-
-#include <QOpenGLFramebufferObject>
-
-#include <QtQuick/QQuickWindow>
-#include <qsgsimpletexturenode.h>
-
-class LogoInFboRenderer : public QQuickFramebufferObject::Renderer
-{
-public:
- LogoInFboRenderer()
- {
- logo.initialize();
- }
-
- void render() override {
- logo.render();
- update();
- }
-
- QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override {
- QOpenGLFramebufferObjectFormat format;
- format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
- format.setSamples(4);
- return new QOpenGLFramebufferObject(size, format);
- }
-
- LogoRenderer logo;
-};
-
-QQuickFramebufferObject::Renderer *FboInSGRenderer::createRenderer() const
-{
- return new LogoInFboRenderer();
-}
diff --git a/examples/quick/scenegraph/fboitem/fboinsgrenderer.h b/examples/quick/scenegraph/fboitem/fboinsgrenderer.h
deleted file mode 100644
index c6eb745d5f..0000000000
--- a/examples/quick/scenegraph/fboitem/fboinsgrenderer.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef FBOINSGRENDERER_H
-#define FBOINSGRENDERER_H
-
-#include <QtQuick/QQuickFramebufferObject>
-
-class LogoRenderer;
-
-class FboInSGRenderer : public QQuickFramebufferObject
-{
- Q_OBJECT
- QML_NAMED_ELEMENT(Renderer)
-public:
- Renderer *createRenderer() const;
-};
-
-#endif
diff --git a/examples/quick/scenegraph/fboitem/fboitem.pro b/examples/quick/scenegraph/fboitem/fboitem.pro
deleted file mode 100644
index 180c2288e2..0000000000
--- a/examples/quick/scenegraph/fboitem/fboitem.pro
+++ /dev/null
@@ -1,20 +0,0 @@
-QT += qml quick
-
-CONFIG += qmltypes
-QML_IMPORT_NAME = SceneGraphRendering
-QML_IMPORT_MAJOR_VERSION = 1
-
-HEADERS += fboinsgrenderer.h
-SOURCES += fboinsgrenderer.cpp main.cpp
-
-INCLUDEPATH += ../shared
-HEADERS += ../shared/logorenderer.h
-SOURCES += ../shared/logorenderer.cpp
-
-RESOURCES += fboitem.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/fboitem
-INSTALLS += target
-
-OTHER_FILES += \
- main.qml
diff --git a/examples/quick/scenegraph/fboitem/fboitem.qrc b/examples/quick/scenegraph/fboitem/fboitem.qrc
deleted file mode 100644
index 1782429798..0000000000
--- a/examples/quick/scenegraph/fboitem/fboitem.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/scenegraph/fboitem">
- <file>main.qml</file>
- <file>shaders/checker.frag.qsb</file>
- </qresource>
-</RCC>
diff --git a/examples/quick/scenegraph/fboitem/main.cpp b/examples/quick/scenegraph/fboitem/main.cpp
deleted file mode 100644
index bbf3805002..0000000000
--- a/examples/quick/scenegraph/fboitem/main.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QGuiApplication>
-
-#include <QtQuick/QQuickView>
-
-#include "fboinsgrenderer.h"
-
-int main(int argc, char **argv)
-{
- QGuiApplication app(argc, argv);
-
- QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
-
- QQuickView view;
- view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.setSource(QUrl("qrc:///scenegraph/fboitem/main.qml"));
- view.show();
-
- return QGuiApplication::exec();
-}
diff --git a/examples/quick/scenegraph/fboitem/main.qml b/examples/quick/scenegraph/fboitem/main.qml
deleted file mode 100644
index db2df19197..0000000000
--- a/examples/quick/scenegraph/fboitem/main.qml
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-import SceneGraphRendering
-
-Item {
- width: 400
- height: 400
-
- // The checkers background
- ShaderEffect {
- id: tileBackground
- anchors.fill: parent
-
- property real tileSize: 16
- property color color1: Qt.rgba(0.9, 0.9, 0.9, 1);
- property color color2: Qt.rgba(0.85, 0.85, 0.85, 1);
-
- property size pixelSize: Qt.size(width / tileSize, height / tileSize);
-
- fragmentShader: "qrc:/scenegraph/fboitem/shaders/checker.frag.qsb"
- }
-
- Renderer {
- id: renderer
- anchors.fill: parent
- anchors.margins: 10
-
- // The transform is just to show something interesting..
- transform: [
- Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
- Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
- Scale { id: scale; },
- Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
- ]
- }
-
- // Just to show something interesting
- SequentialAnimation {
- PauseAnimation { duration: 5000 }
- ParallelAnimation {
- NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
- NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
- }
- NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic }
- NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic }
- NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic }
- NumberAnimation { target: renderer; property: "opacity"; to: 0.5; duration: 1000; easing.type: Easing.InOutCubic }
- PauseAnimation { duration: 1000 }
- NumberAnimation { target: renderer; property: "opacity"; to: 0.8; duration: 1000; easing.type: Easing.InOutCubic }
- ParallelAnimation {
- NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
- NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
- }
- running: true
- loops: Animation.Infinite
- }
-
- Rectangle {
- id: labelFrame
- anchors.margins: -10
- radius: 5
- color: "white"
- border.color: "black"
- opacity: 0.8
- anchors.fill: label
- }
-
- Text {
- id: label
- anchors.bottom: renderer.bottom
- anchors.left: renderer.left
- anchors.right: renderer.right
- anchors.margins: 20
- wrapMode: Text.WordWrap
- text: qsTr("The blue rectangle with the vintage 'Q' is an FBO, rendered by the application on the scene graph rendering thread. The FBO is managed and displayed using the QQuickFramebufferObject convenience class.")
- }
-
-
-}
diff --git a/examples/quick/scenegraph/fboitem/shaders/checker.frag b/examples/quick/scenegraph/fboitem/shaders/checker.frag
deleted file mode 100644
index 1e4131d026..0000000000
--- a/examples/quick/scenegraph/fboitem/shaders/checker.frag
+++ /dev/null
@@ -1,22 +0,0 @@
-#version 440
-
-layout(std140, binding = 0) uniform buf {
- mat4 qt_Matrix;
- float qt_Opacity;
-
- vec4 color1;
- vec4 color2;
- vec2 pixelSize;
-} ubuf;
-
-layout(location = 0) in vec2 qt_TexCoord0;
-layout(location = 0) out vec4 fragColor;
-
-void main()
-{
- vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * ubuf.pixelSize));
- if (tc.x != tc.y)
- fragColor = ubuf.color1;
- else
- fragColor = ubuf.color2;
-}
diff --git a/examples/quick/scenegraph/fboitem/shaders/checker.frag.qsb b/examples/quick/scenegraph/fboitem/shaders/checker.frag.qsb
deleted file mode 100644
index 77cbf0b867..0000000000
--- a/examples/quick/scenegraph/fboitem/shaders/checker.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/examples/quick/scenegraph/graph/CMakeLists.txt b/examples/quick/scenegraph/graph/CMakeLists.txt
index fee1e498d7..36d8c34f19 100644
--- a/examples/quick/scenegraph/graph/CMakeLists.txt
+++ b/examples/quick/scenegraph/graph/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(graph LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/graph")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick)
qt_standard_project_setup()
@@ -28,9 +22,9 @@ set_target_properties(graph PROPERTIES
)
target_link_libraries(graph PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
)
qt_add_qml_module(graph
@@ -47,7 +41,16 @@ qt_add_qml_module(graph
)
install(TARGETS graph
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET graph
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/graph/doc/src/graph.qdoc b/examples/quick/scenegraph/graph/doc/src/graph.qdoc
index fb9dc08357..a76652498c 100644
--- a/examples/quick/scenegraph/graph/doc/src/graph.qdoc
+++ b/examples/quick/scenegraph/graph/doc/src/graph.qdoc
@@ -4,6 +4,7 @@
/*!
\example scenegraph/graph
\title Scene Graph - Graph
+ \examplecategory {Graphics}
\ingroup qtquickexamples
\brief Demonstrates how one can combine custom materials and geometries
diff --git a/examples/quick/scenegraph/graph/main.cpp b/examples/quick/scenegraph/graph/main.cpp
index 1b56c03daf..7070196a79 100644
--- a/examples/quick/scenegraph/graph/main.cpp
+++ b/examples/quick/scenegraph/graph/main.cpp
@@ -9,8 +9,6 @@
int main(int argc, char *argv[])
{
- qputenv("QSG_RHI", "1"); // ### Qt 6 remove, this will be the default anyway
-
QGuiApplication a(argc, argv);
QQuickView view;
diff --git a/examples/quick/scenegraph/graph/main.qml b/examples/quick/scenegraph/graph/main.qml
index e9b684c3ba..5f2abbbd58 100644
--- a/examples/quick/scenegraph/graph/main.qml
+++ b/examples/quick/scenegraph/graph/main.qml
@@ -15,27 +15,23 @@ Item {
anchors.margins: 100
function newSample(i) {
- return (Math.sin(i / 100.0 * Math.PI * 2) + 1) * 0.4 + Math.random() * 0.05;
+ return (Math.sin(i / 200.0 * Math.PI * 2) + 1) * 0.4 + Math.random() * 0.05;
}
Component.onCompleted: {
- for (var i=0; i<100; ++i)
+ for (var i=0; i<graph.offset; ++i)
appendSample(newSample(i));
}
- property int offset: 100;
+ property int offset: 200
}
- Timer {
- id: timer
- interval: 500
- repeat: true
+ FrameAnimation {
running: true
onTriggered: {
graph.removeFirstSample();
graph.appendSample(graph.newSample(++graph.offset));
}
-
}
Rectangle {
diff --git a/examples/quick/scenegraph/graph/shaders/line.frag b/examples/quick/scenegraph/graph/shaders/line.frag
index 44b5b24c94..b9531cede6 100644
--- a/examples/quick/scenegraph/graph/shaders/line.frag
+++ b/examples/quick/scenegraph/graph/shaders/line.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in float vT;
diff --git a/examples/quick/scenegraph/graph/shaders/noisy.frag b/examples/quick/scenegraph/graph/shaders/noisy.frag
index 8cea9de02b..0b7cb1306b 100644
--- a/examples/quick/scenegraph/graph/shaders/noisy.frag
+++ b/examples/quick/scenegraph/graph/shaders/noisy.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 vTexCoord;
diff --git a/examples/quick/scenegraph/metaltextureimport/CMakeLists.txt b/examples/quick/scenegraph/metaltextureimport/CMakeLists.txt
index 70cb10dc6a..f76489116a 100644
--- a/examples/quick/scenegraph/metaltextureimport/CMakeLists.txt
+++ b/examples/quick/scenegraph/metaltextureimport/CMakeLists.txt
@@ -1,17 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(metaltextureimport LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/metaltextureimport")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_add_executable(metaltextureimport
@@ -29,10 +23,10 @@ set_target_properties(metaltextureimport PROPERTIES
target_link_libraries(metaltextureimport PUBLIC
"-framework Metal"
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
if(MACOS)
@@ -53,7 +47,16 @@ qt_add_qml_module(metaltextureimport
)
install(TARGETS metaltextureimport
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET metaltextureimport
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc b/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
index e8383c7cf8..3b4912829e 100644
--- a/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
+++ b/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
@@ -4,6 +4,7 @@
/*!
\example scenegraph/metaltextureimport
\title Scene Graph - Metal Texture Import
+ \examplecategory {Graphics}
\ingroup qtquickexamples
\brief Shows how to use a texture created directly with Metal.
@@ -63,9 +64,8 @@
After copying the values we need, sync() also performs some graphics
resource initialization. The MTLDevice is queried from the scenegraph. Once
a MTLTexture is available, a QSGTexture wrapping (not owning) it is created
- via QQuickWindow::createTextureFromNativeObject(). This function is a
- modern equivalent to QQuickWindow::createTextureFromId() that is not tied
- to OpenGL. Finally, the QSGTexture is associated with the underlying
+ via \l QNativeInterface::QSGOpenGLTexture::fromNative().
+ Finally, the QSGTexture is associated with the underlying
materials by calling the base class' setTexture() function.
\snippet scenegraph/metaltextureimport/metaltextureimport.mm 6
diff --git a/examples/quick/scenegraph/metaltextureimport/squircle.frag b/examples/quick/scenegraph/metaltextureimport/squircle.frag
index 15f34624fe..8303e97006 100644
--- a/examples/quick/scenegraph/metaltextureimport/squircle.frag
+++ b/examples/quick/scenegraph/metaltextureimport/squircle.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#include <metal_stdlib>
#include <simd/simd.h>
diff --git a/examples/quick/scenegraph/metalunderqml/CMakeLists.txt b/examples/quick/scenegraph/metalunderqml/CMakeLists.txt
index 9bd54c637e..21c7b5c763 100644
--- a/examples/quick/scenegraph/metalunderqml/CMakeLists.txt
+++ b/examples/quick/scenegraph/metalunderqml/CMakeLists.txt
@@ -1,17 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(metalunderqml LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/metalunderqml")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_add_executable(metalunderqml
@@ -26,10 +20,10 @@ set_target_properties(metalunderqml PROPERTIES
target_link_libraries(metalunderqml PUBLIC
"-framework Metal"
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
if(MACOS)
@@ -50,7 +44,16 @@ qt_add_qml_module(metalunderqml
)
install(TARGETS metalunderqml
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET metalunderqml
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
index b8e3750271..fa41ca489f 100644
--- a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
+++ b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
@@ -4,6 +4,7 @@
/*!
\example scenegraph/metalunderqml
\title Scene Graph - Metal Under QML
+ \examplecategory {Graphics}
\ingroup qtquickexamples
\brief Shows how to render directly with Metal under a Qt Quick scene.
diff --git a/examples/quick/scenegraph/metalunderqml/squircle.frag b/examples/quick/scenegraph/metalunderqml/squircle.frag
index 15f34624fe..8303e97006 100644
--- a/examples/quick/scenegraph/metalunderqml/squircle.frag
+++ b/examples/quick/scenegraph/metalunderqml/squircle.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#include <metal_stdlib>
#include <simd/simd.h>
diff --git a/examples/quick/scenegraph/openglunderqml/CMakeLists.txt b/examples/quick/scenegraph/openglunderqml/CMakeLists.txt
index cd64bb6172..8a95221811 100644
--- a/examples/quick/scenegraph/openglunderqml/CMakeLists.txt
+++ b/examples/quick/scenegraph/openglunderqml/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(openglunderqml LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/openglunderqml")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_standard_project_setup()
@@ -26,10 +20,10 @@ set_target_properties(openglunderqml PROPERTIES
)
target_link_libraries(openglunderqml PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(openglunderqml
@@ -40,7 +34,16 @@ qt_add_qml_module(openglunderqml
)
install(TARGETS openglunderqml
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET openglunderqml
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
index 317c824d06..aba95b471a 100644
--- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
+++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
@@ -4,7 +4,9 @@
/*!
\example scenegraph/openglunderqml
\title Scene Graph - OpenGL Under QML
+ \examplecategory {Graphics}
\ingroup qtquickexamples
+ \examplecategory {Mobile}
\brief Shows how to render OpenGL under a Qt Quick scene.
\image openglunderqml-example.jpg
diff --git a/examples/quick/scenegraph/rhitextureitem/CMakeLists.txt b/examples/quick/scenegraph/rhitextureitem/CMakeLists.txt
new file mode 100644
index 0000000000..9440c7b25b
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/CMakeLists.txt
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(rhitextureitem LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
+
+qt_standard_project_setup()
+
+qt_add_executable(rhitextureitem WIN32 MACOSX_BUNDLE
+ rhitextureitem.cpp rhitextureitem.h
+ main.cpp
+)
+
+target_include_directories(rhitextureitem PUBLIC
+ ../shared
+)
+
+target_link_libraries(rhitextureitem PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::GuiPrivate
+ Qt6::Qml
+ Qt6::Quick
+)
+
+qt_add_qml_module(rhitextureitem
+ URI
+ SceneGraphRendering
+ QML_FILES
+ main.qml
+ SettingsDrawer.qml
+ RESOURCE_PREFIX
+ /scenegraph/rhitextureitem
+ RESOURCES
+ icon_settings.png
+ icon_settings@2x.png
+ icon_settings@3x.png
+ icon_settings@4x.png
+ NO_RESOURCE_TARGET_PATH
+)
+
+qt_add_shaders(rhitextureitem "rhitextureitem_shaders"
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ /scenegraph/rhitextureitem
+ FILES
+ shaders/color.vert
+ shaders/color.frag
+)
+
+qt_add_shaders(rhitextureitem "rhitextureitem_effect_shaders"
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ /scenegraph/rhitextureitem
+ FILES
+ shaders/checker.frag
+)
+
+install(TARGETS rhitextureitem
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET rhitextureitem
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/rhitextureitem/SettingsDrawer.qml b/examples/quick/scenegraph/rhitextureitem/SettingsDrawer.qml
new file mode 100644
index 0000000000..2b7e146c43
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/SettingsDrawer.qml
@@ -0,0 +1,117 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+Page {
+ id: settingsDrawer
+ default property alias content: contentLayout.children
+ required property bool isLandscape
+ readonly property bool isMobile: Qt.platform.os === "android" || Qt.platform.os === "ios"
+ property bool isOpen: isMobile ? false : true
+
+ title: "Settings"
+
+ onIsLandscapeChanged: updateState();
+ Component.onCompleted: updateState();
+ onIsOpenChanged: updateState();
+ x: 0
+ y: 0
+
+ function updateState() {
+ if (isOpen)
+ if (isLandscape)
+ settingsDrawer.state = "Landscape_Visible"
+ else
+ settingsDrawer.state = "Portrait_Visible"
+ else
+ if (isLandscape)
+ settingsDrawer.state = "Landscape_Hidden"
+ else
+ settingsDrawer.state = "Portrait_Hidden"
+ }
+
+ states: [
+ State {
+ name: "Landscape_Visible"
+ PropertyChanges {
+ settingsDrawer.x: 0
+ }
+ },
+ State {
+ name: "Landscape_Hidden"
+ PropertyChanges {
+ settingsDrawer.x: -settingsDrawer.width
+ }
+ },
+ State {
+ name: "Portrait_Visible"
+ PropertyChanges {
+ settingsDrawer.y: 0
+ }
+ },
+ State {
+ name: "Portrait_Hidden"
+ PropertyChanges {
+ settingsDrawer.y: -settingsDrawer.height
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "Landscape_Visible"
+ to: "Landscape_Hidden"
+ NumberAnimation {
+ duration: 400
+ property: "x"
+ easing.type: Easing.InOutQuad
+ }
+ },
+ Transition {
+ from: "Landscape_Hidden"
+ to: "Landscape_Visible"
+ NumberAnimation {
+ duration: 400
+ property: "x"
+ easing.type: Easing.InOutQuad
+ }
+ },
+ Transition {
+ from: "Portrait_Visible"
+ to: "Portrait_Hidden"
+ NumberAnimation {
+ duration: 400
+ property: "y"
+ easing.type: Easing.InOutQuad
+ }
+ },
+ Transition {
+ from: "Portrait_Hidden"
+ to: "Portrait_Visible"
+ NumberAnimation {
+ duration: 400
+ property: "y"
+ easing.type: Easing.InOutQuad
+ }
+ }
+ ]
+
+ header: Label {
+ text: settingsDrawer.title
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pointSize: 20
+ }
+
+ ScrollView {
+ anchors.fill: parent
+ padding: 10
+
+ ColumnLayout {
+ id: contentLayout
+ }
+ }
+}
diff --git a/examples/quick/scenegraph/rhitextureitem/doc/images/rhitextureitem-example.jpg b/examples/quick/scenegraph/rhitextureitem/doc/images/rhitextureitem-example.jpg
new file mode 100644
index 0000000000..909d0619fe
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/doc/images/rhitextureitem-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/rhitextureitem/doc/src/rhitextureitem.qdoc b/examples/quick/scenegraph/rhitextureitem/doc/src/rhitextureitem.qdoc
new file mode 100644
index 0000000000..ee4031a7d9
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/doc/src/rhitextureitem.qdoc
@@ -0,0 +1,106 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example scenegraph/rhitextureitem
+ \title Scene Graph - RHI Texture Item
+ \examplecategory {Graphics}
+ \ingroup qtquickexamples
+
+ \brief Shows how to implement a custom QQuickItem that displays a QRhi-rendered texture.
+
+ \image rhitextureitem-example.jpg
+
+ This example shows how to implement an item that performs cross-platform,
+ portable 3D rendering into a texture using the QRhi APIs and then displays
+ that image.
+
+ \note This example demonstrates advanced, low-level functionality performing
+ portable, cross-platform 3D rendering, while relying on APIs with limited
+ compatibility guarantee from the Qt Gui module. To be able to use the QRhi
+ APIs, the application links to \c{Qt::GuiPrivate} and includes
+ \c{<rhi/qrhi.h>}.
+
+ \section2 Comparison with other approaches
+
+ The \l{Scene Graph - RHI Under QML}{RHI Under QML} example shows how to
+ implement portable, cross-platform 3D rendering with the \l QRhi APIs in a
+ manner where the custom rendering is issued before the Qt Quick scene
+ graph's own rendering, effectively providing an "underlay". That approach
+ is efficient since now additional render targets and render passes are
+ needed, the custom rendering is injected in the main render pass before the
+ scene graph's own draw calls.
+
+ In contrast, this example involves a separate render target, a QRhiTexture,
+ the \l{QRhiTexture::pixelSize()}{dimensions} of which match the
+ QQuickItem's size in the scene, and a whole render pass that is used to
+ clear and then draw into that texture. The texture is then sampled in the
+ main render pass and is used to texture a quad, effectively displaying a 2D
+ image.
+
+ Compared to the underlay/overlay approach, this allows displaying,
+ blending, and transforming the flattened 2D image of the 3D rendering
+ anywhere in the Qt Quick scene since here we have a true QQuickItem. This
+ comes at the expense of being more expensive in terms of resources and
+ performance since it involves rendering to a texture first.
+
+ \section2 Overview
+
+ The example is implemented using \l QQuickRhiItem and
+ \l QQuickRhiItemRenderer. QQuickRhiItem is a convenience class that can be
+ subclassed to easily and quickly get a fully featured, custom QQuickItem that
+ displays the contents of a QRhiTexture by using QSGSimpleTextureNode under the
+ hood. The contents of the texture is generated by the application-provided
+ logic implemented in its QQuickRhiItemRenderer subclass.
+
+ \c ExampleRhiItem is a QQuickRhiItem subclass that offers a few properties,
+ such as \c angle and \c backgroundAlpha. These are going to be read,
+ written, and animated from QML. In order to support Qt Quick's threaded
+ rendering model, the QQQuickRhiItemRenderer has a virtual
+ \l{QQuickRhiItemRenderer::synchronize()}{synchronize()} function that can
+ be reimplemented to safely perform copying of data between the
+ QQuickRhiItem (belonging to the main/GUI thread) and the
+ QQuickRhiItemRenderer (belonging to the render thread, if there is one).
+
+ \snippet scenegraph/rhitextureitem/rhitextureitem.cpp 0
+
+ \l{QQuickRhiItemRenderer::initialize()}{initialize()} is called at least
+ once before the first call to render(), but may in practice be invoked
+ multiple times: if the QQuickItem geometry changes (due to some layout
+ change, resizing the window, etc.), if QQuickRhiItem setting such as the
+ sample count and texture format change, or if the item is reparented so
+ that is belong to a new QQuickWindow, these all trigger calling
+ initialize() again because they imply that one or more of the resources
+ QQuickRhiItem-managed resources change, which is going to have implications
+ on the subclass as well. The example code here is prepared to handle these
+ special situations (changing QRhi, changing sample count, changing texture
+ format). (as it does not hold on to the texture used as the color buffer,
+ the case when the texture is recreated due to a different size needs no
+ special handling)
+
+ \snippet scenegraph/rhitextureitem/rhitextureitem.cpp 1
+
+ The rest if initialize() is straightforward QRhi-based code.
+
+ The 3D scene uses a perspective projection, which is calculated based on
+ the output size, queried from the QRhiRenderTarget for convenience (because
+ this works regardless of using multisampling or not, whereas accessing
+ \l{QQuickRhiItemRenderer::colorTexture()}{colorTexture()} and
+ \l{QQuickRhiItemRenderer::msaaColorBuffer()}{msaaColorBuffer()} would need
+ branching logic based on which of the objects happens to be valid)
+
+ Note the usage of \l QRhi::clipSpaceCorrMatrix() to cater for the
+ coordinate system differences between 3D graphics APIs.
+
+ \snippet scenegraph/rhitextureitem/rhitextureitem.cpp 2
+
+ The implementation of \l{QQuickRhiItemRenderer::render()}{render()} records
+ the drawing of a single triangle. The uniform buffer with the 4x4 matrix is
+ updated every time since we expect the rotation angle to change. The clear
+ color has the item-provided background alpha baked in. Remember the need to
+ premultiply the alpha value in the red, green, and blue components as well.
+
+ \snippet scenegraph/rhitextureitem/rhitextureitem.cpp 3
+
+ \sa QQuickRhiItem, {Scene Graph - RHI Under QML}, {Scene Graph - Custom QSGRenderNode}
+ */
diff --git a/examples/quick/scenegraph/rhitextureitem/icon_settings.png b/examples/quick/scenegraph/rhitextureitem/icon_settings.png
new file mode 100644
index 0000000000..943316040d
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/icon_settings.png
Binary files differ
diff --git a/examples/quick/scenegraph/rhitextureitem/icon_settings@2x.png b/examples/quick/scenegraph/rhitextureitem/icon_settings@2x.png
new file mode 100644
index 0000000000..b3dbf8fa28
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/icon_settings@2x.png
Binary files differ
diff --git a/examples/quick/scenegraph/rhitextureitem/icon_settings@3x.png b/examples/quick/scenegraph/rhitextureitem/icon_settings@3x.png
new file mode 100644
index 0000000000..0532debcfc
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/icon_settings@3x.png
Binary files differ
diff --git a/examples/quick/scenegraph/rhitextureitem/icon_settings@4x.png b/examples/quick/scenegraph/rhitextureitem/icon_settings@4x.png
new file mode 100644
index 0000000000..3b6df374dc
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/icon_settings@4x.png
Binary files differ
diff --git a/examples/quick/scenegraph/rhitextureitem/main.cpp b/examples/quick/scenegraph/rhitextureitem/main.cpp
new file mode 100644
index 0000000000..5092552691
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/main.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QQuickView>
+#include "rhitextureitem.h"
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/rhitextureitem/main.qml"));
+ view.show();
+
+ return QGuiApplication::exec();
+}
diff --git a/examples/quick/scenegraph/rhitextureitem/main.qml b/examples/quick/scenegraph/rhitextureitem/main.qml
new file mode 100644
index 0000000000..dc386d2cf2
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/main.qml
@@ -0,0 +1,259 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import SceneGraphRendering
+
+Item {
+ width: 800
+ height: 600
+
+ // The checkers background
+ ShaderEffect {
+ id: tileBackground
+ anchors.fill: parent
+
+ property real tileSize: 16
+ property color color1: Qt.rgba(0.9, 0.9, 0.9, 1);
+ property color color2: Qt.rgba(0.85, 0.85, 0.85, 1);
+
+ property size pixelSize: Qt.size(width / tileSize, height / tileSize);
+
+ fragmentShader: "qrc:/scenegraph/rhitextureitem/shaders/checker.frag.qsb"
+ }
+
+ //! [0]
+ ExampleRhiItem {
+ id: renderer
+ anchors.fill: parent
+ anchors.margins: 10
+
+ mirrorVertically: cbMirror.checked
+ sampleCount: cbMSAA.checked ? 4 : 1
+ fixedColorBufferWidth: cbSize.checked ? slSize.value.toFixed(0) : 0
+ fixedColorBufferHeight: cbSize.checked ? slSize.value.toFixed(0) : 0
+ alphaBlending: cbBlend.checked
+ colorBufferFormat: rdFormatRGBA8.checked ? ExampleRhiItem.TextureFormat.RGBA8
+ : rdFormatFP16.checked ? ExampleRhiItem.TextureFormat.RGBA16F
+ : rdFormatFP32.checked ? ExampleRhiItem.TextureFormat.RGBA32F
+ : ExampleRhiItem.TextureFormat.RGB10A2
+
+ // custom properties provided by the QQuickRhiItem subclass: angle, backgroundAlpha
+ NumberAnimation on angle {
+ from: 0
+ to: 360
+ duration: 5000
+ loops: Animation.Infinite
+ running: cbAnimate.checked
+ }
+
+ backgroundAlpha: slAlpha.value
+ //! [0]
+
+ // The transform is just to show something interesting..
+ transform: [
+ Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
+ Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
+ Scale { id: scale; },
+ Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
+ ]
+ }
+
+ // Just to show something interesting
+ SequentialAnimation {
+ PauseAnimation { duration: 5000 }
+ ParallelAnimation {
+ NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
+ NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
+ }
+ NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: renderer; property: "opacity"; to: 0.5; duration: 1000; easing.type: Easing.InOutCubic }
+ PauseAnimation { duration: 1000 }
+ NumberAnimation { target: renderer; property: "opacity"; to: 1.0; duration: 1000; easing.type: Easing.InOutCubic }
+ ParallelAnimation {
+ NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
+ NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
+ }
+ running: cbAnimate.checked
+ loops: Animation.Infinite
+ }
+
+ Rectangle {
+ id: labelFrame
+ anchors.margins: -10
+ radius: 5
+ color: "white"
+ border.color: "black"
+ anchors.fill: label
+ }
+
+ Text {
+ id: label
+ anchors.bottom: renderer.bottom
+ anchors.left: renderer.left
+ anchors.right: renderer.right
+ anchors.margins: 20
+ wrapMode: Text.WordWrap
+ text: qsTr("The triangle on the blue background is rendered by working directly the QRhi APIs on the scene graph render thread. The custom QQuickItem subclasses QQuickRhiItem, and it in effect draws a quad textured with the QRhiTexture containing the custom 3D rendering. Thanks to being a true QQuickItem, 2D/2.5D transforms, blending/opacity, stacking, clipping, etc. all work as usual.")
+ }
+
+ RowLayout {
+ anchors.top: renderer.top
+ anchors.right: renderer.right
+ anchors.margins: 10
+ Item {
+ id: settingsButton
+ implicitWidth: 64
+ implicitHeight: 64
+ Image {
+ anchors.centerIn: parent
+ source: "icon_settings.png"
+ }
+ HoverHandler {
+ id: hoverHandler
+ }
+ }
+ Text {
+ Layout.alignment: Qt.AlignVCenter
+ text: settingsDrawer.title
+ color: "white"
+ font.pointSize: 16
+ }
+ TapHandler {
+ // qmllint disable signal-handler-parameters
+ onTapped: settingsDrawer.isOpen = !settingsDrawer.isOpen;
+ // qmllint enable signal-handler-parameters
+ }
+ }
+
+ SettingsDrawer {
+ id: settingsDrawer
+ title: qsTr("Settings")
+ isOpen: false
+ isLandscape: true
+ width: isLandscape ? implicitWidth * 1.2 : parent.width
+ height: isLandscape ? parent.height * 0.8 : parent.height * 0.33
+
+ CheckBox {
+ id: cbAnimate
+ text: qsTr("Animate")
+ checked: true
+ }
+
+ CheckBox {
+ id: cbMirror
+ text: qsTr("Mirror vertically")
+ checked: false
+ }
+
+ CheckBox {
+ id: cbMSAA
+ text: qsTr("Enable 4x MSAA")
+ checked: false
+ }
+
+ CheckBox {
+ id: cbSize
+ text: qsTr("Use fixed size")
+ checked: false
+ }
+ RowLayout {
+ Label {
+ text: qsTr("Texture size")
+ }
+ Slider {
+ id: slSize
+ enabled: cbSize.checked
+ from: 8
+ to: 2048
+ value: 128
+ Layout.fillWidth: false
+ }
+ }
+
+ Label {
+ text: qsTr("Backing texture size: %1x%2 pixels").arg(renderer.effectiveColorBufferSize.width).arg(renderer.effectiveColorBufferSize.height)
+ }
+
+ Label {
+ text: qsTr("Item logical size: %1x%2").arg(renderer.width).arg(renderer.height)
+ }
+
+ CheckBox {
+ id: cbBlend
+ text: qsTr("Force alpha blending\nregardless of item opacity")
+ checked: false
+ }
+ RowLayout {
+ Label {
+ text: qsTr("Background alpha")
+ }
+ Slider {
+ id: slAlpha
+ from: 0
+ to: 1.0
+ value: 1.0
+ Layout.fillWidth: false
+ }
+ }
+
+ Label {
+ text: qsTr("Background alpha: %1").arg(renderer.backgroundAlpha.toFixed(2))
+ }
+
+ Label {
+ text: qsTr("Item opacity: %1").arg(renderer.opacity.toFixed(2))
+ }
+
+ Label {
+ property string apiName:
+ if (GraphicsInfo.api === GraphicsInfo.OpenGL)
+ "OpenGL";
+ else if (GraphicsInfo.api === GraphicsInfo.Direct3D11)
+ "D3D11";
+ else if (GraphicsInfo.api === GraphicsInfo.Direct3D12)
+ "D3D12";
+ else if (GraphicsInfo.api === GraphicsInfo.Vulkan)
+ "Vulkan";
+ else if (GraphicsInfo.api === GraphicsInfo.Metal)
+ "Metal";
+ else if (GraphicsInfo.api === GraphicsInfo.Null)
+ "Null";
+ else
+ "Unknown API";
+ text: "The underlying graphics API is " + apiName
+ }
+
+ GroupBox {
+ title: qsTr("Texture format")
+ ColumnLayout {
+ RadioButton {
+ id: rdFormatRGBA8
+ text: qsTr("8-bit RGBA")
+ checked: true
+ Layout.fillWidth: false
+ }
+ RadioButton {
+ id: rdFormatFP16
+ text: qsTr("Half-float RGBA")
+ Layout.fillWidth: false
+ }
+ RadioButton {
+ id: rdFormatFP32
+ text: qsTr("Float RGBA")
+ Layout.fillWidth: false
+ }
+ RadioButton {
+ id: rdFormatRGB10A2
+ text: qsTr("10-bit RGB, 2-bit A")
+ Layout.fillWidth: false
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/checker.frag.qsb b/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/checker.frag.qsb
new file mode 100644
index 0000000000..31ad25de20
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/checker.frag.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/color.frag.qsb b/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/color.frag.qsb
new file mode 100644
index 0000000000..10b0bbc436
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/color.frag.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/color.vert.qsb b/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/color.vert.qsb
new file mode 100644
index 0000000000..72a0574852
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/prebuilts_for_qmake/color.vert.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/rhitextureitem/rhitextureitem.cpp b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.cpp
new file mode 100644
index 0000000000..2b96320869
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.cpp
@@ -0,0 +1,141 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "rhitextureitem.h"
+#include <QFile>
+
+//![0]
+QQuickRhiItemRenderer *ExampleRhiItem::createRenderer()
+{
+ return new ExampleRhiItemRenderer;
+}
+
+void ExampleRhiItem::setAngle(float a)
+{
+ if (m_angle == a)
+ return;
+
+ m_angle = a;
+ emit angleChanged();
+ update();
+}
+
+void ExampleRhiItem::setBackgroundAlpha(float a)
+{
+ if (m_alpha == a)
+ return;
+
+ m_alpha = a;
+ emit backgroundAlphaChanged();
+ update();
+}
+
+void ExampleRhiItemRenderer::synchronize(QQuickRhiItem *rhiItem)
+{
+ ExampleRhiItem *item = static_cast<ExampleRhiItem *>(rhiItem);
+ if (item->angle() != m_angle)
+ m_angle = item->angle();
+ if (item->backgroundAlpha() != m_alpha)
+ m_alpha = item->backgroundAlpha();
+}
+//![0]
+
+static QShader getShader(const QString &name)
+{
+ QFile f(name);
+ return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
+}
+
+static float vertexData[] = {
+ 0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
+ 0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
+};
+
+//![1]
+void ExampleRhiItemRenderer::initialize(QRhiCommandBuffer *cb)
+{
+ if (m_rhi != rhi()) {
+ m_rhi = rhi();
+ m_pipeline.reset();
+ }
+
+ if (m_sampleCount != renderTarget()->sampleCount()) {
+ m_sampleCount = renderTarget()->sampleCount();
+ m_pipeline.reset();
+ }
+
+ QRhiTexture *finalTex = m_sampleCount > 1 ? resolveTexture() : colorTexture();
+ if (m_textureFormat != finalTex->format()) {
+ m_textureFormat = finalTex->format();
+ m_pipeline.reset();
+ }
+//![1]
+//![2]
+ if (!m_pipeline) {
+ m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)));
+ m_vbuf->create();
+
+ m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64));
+ m_ubuf->create();
+
+ m_srb.reset(m_rhi->newShaderResourceBindings());
+ m_srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, m_ubuf.get()),
+ });
+ m_srb->create();
+
+ m_pipeline.reset(m_rhi->newGraphicsPipeline());
+ m_pipeline->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/scenegraph/rhitextureitem/shaders/color.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/scenegraph/rhitextureitem/shaders/color.frag.qsb")) }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
+ });
+ m_pipeline->setSampleCount(m_sampleCount);
+ m_pipeline->setVertexInputLayout(inputLayout);
+ m_pipeline->setShaderResourceBindings(m_srb.get());
+ m_pipeline->setRenderPassDescriptor(renderTarget()->renderPassDescriptor());
+ m_pipeline->create();
+
+ QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData);
+ cb->resourceUpdate(resourceUpdates);
+ }
+
+ const QSize outputSize = renderTarget()->pixelSize();
+ m_viewProjection = m_rhi->clipSpaceCorrMatrix();
+ m_viewProjection.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
+ m_viewProjection.translate(0, 0, -4);
+//![2]
+}
+
+//![3]
+void ExampleRhiItemRenderer::render(QRhiCommandBuffer *cb)
+{
+ QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ QMatrix4x4 modelViewProjection = m_viewProjection;
+ modelViewProjection.rotate(m_angle, 0, 1, 0);
+ resourceUpdates->updateDynamicBuffer(m_ubuf.get(), 0, 64, modelViewProjection.constData());
+
+ // Qt Quick expects premultiplied alpha
+ const QColor clearColor = QColor::fromRgbF(0.5f * m_alpha, 0.5f * m_alpha, 0.7f * m_alpha, m_alpha);
+ cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, resourceUpdates);
+
+ cb->setGraphicsPipeline(m_pipeline.get());
+ const QSize outputSize = renderTarget()->pixelSize();
+ cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(3);
+
+ cb->endPass();
+}
+//![3]
diff --git a/examples/quick/scenegraph/rhitextureitem/rhitextureitem.h b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.h
new file mode 100644
index 0000000000..745cc3bf12
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef RHITEXTUREITEM_H
+#define RHITEXTUREITEM_H
+
+#include <QQuickRhiItem>
+#include <rhi/qrhi.h>
+
+class ExampleRhiItemRenderer : public QQuickRhiItemRenderer
+{
+public:
+ void initialize(QRhiCommandBuffer *cb) override;
+ void synchronize(QQuickRhiItem *item) override;
+ void render(QRhiCommandBuffer *cb) override;
+
+private:
+ QRhi *m_rhi = nullptr;
+ int m_sampleCount = 1;
+ QRhiTexture::Format m_textureFormat = QRhiTexture::RGBA8;
+
+ std::unique_ptr<QRhiBuffer> m_vbuf;
+ std::unique_ptr<QRhiBuffer> m_ubuf;
+ std::unique_ptr<QRhiShaderResourceBindings> m_srb;
+ std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
+
+ QMatrix4x4 m_viewProjection;
+ float m_angle = 0.0f;
+ float m_alpha = 1.0f;
+};
+
+class ExampleRhiItem : public QQuickRhiItem
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(ExampleRhiItem)
+ Q_PROPERTY(float angle READ angle WRITE setAngle NOTIFY angleChanged)
+ Q_PROPERTY(float backgroundAlpha READ backgroundAlpha WRITE setBackgroundAlpha NOTIFY backgroundAlphaChanged)
+
+public:
+ QQuickRhiItemRenderer *createRenderer() override;
+
+ float angle() const { return m_angle; }
+ void setAngle(float a);
+
+ float backgroundAlpha() const { return m_alpha; }
+ void setBackgroundAlpha(float a);
+
+signals:
+ void angleChanged();
+ void backgroundAlphaChanged();
+
+private:
+ float m_angle = 0.0f;
+ float m_alpha = 1.0f;
+};
+
+#endif
diff --git a/examples/quick/scenegraph/rhitextureitem/rhitextureitem.pro b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.pro
new file mode 100644
index 0000000000..4d5c132a42
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.pro
@@ -0,0 +1,16 @@
+QT += gui-private qml quick
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = SceneGraphRendering
+QML_IMPORT_MAJOR_VERSION = 1
+
+HEADERS += rhitextureitem.h
+SOURCES += rhitextureitem.cpp main.cpp
+
+RESOURCES += rhitextureitem.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/rhitextureitem
+INSTALLS += target
+
+OTHER_FILES += \
+ main.qml
diff --git a/examples/quick/scenegraph/rhitextureitem/rhitextureitem.qrc b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.qrc
new file mode 100644
index 0000000000..ad996ef3f2
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/rhitextureitem.qrc
@@ -0,0 +1,8 @@
+<RCC>
+ <qresource prefix="/scenegraph/rhitextureitem">
+ <file>main.qml</file>
+ <file alias="shaders/color.vert.qsb">prebuilts_for_qmake/color.vert.qsb</file>
+ <file alias="shaders/color.frag.qsb">prebuilts_for_qmake/color.frag.qsb</file>
+ <file alias="shaders/checker.frag.qsb">prebuilts_for_qmake/checker.frag.qsb</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/rhitextureitem/shaders/checker.frag b/examples/quick/scenegraph/rhitextureitem/shaders/checker.frag
new file mode 100644
index 0000000000..53f4a8aed1
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/shaders/checker.frag
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#version 440
+
+layout(std140, binding = 0) uniform buf {
+ // built-in Qt data
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ // custom data
+ vec4 color1;
+ vec4 color2;
+ vec2 pixelSize;
+};
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize));
+ if (tc.x != tc.y)
+ fragColor = color1;
+ else
+ fragColor = color2;
+}
diff --git a/examples/quick/scenegraph/rhitextureitem/shaders/color.frag b/examples/quick/scenegraph/rhitextureitem/shaders/color.frag
new file mode 100644
index 0000000000..b7e36638e3
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/shaders/color.frag
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#version 440
+
+layout(location = 0) in vec3 v_color;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(v_color, 1.0);
+}
diff --git a/examples/quick/scenegraph/rhitextureitem/shaders/color.vert b/examples/quick/scenegraph/rhitextureitem/shaders/color.vert
new file mode 100644
index 0000000000..e876f290e7
--- /dev/null
+++ b/examples/quick/scenegraph/rhitextureitem/shaders/color.vert
@@ -0,0 +1,16 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+
+layout(location = 0) out vec3 v_color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+};
+
+void main()
+{
+ v_color = color;
+ gl_Position = mvp * position;
+}
diff --git a/examples/quick/scenegraph/rhiunderqml/CMakeLists.txt b/examples/quick/scenegraph/rhiunderqml/CMakeLists.txt
new file mode 100644
index 0000000000..c1fdc114b6
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/CMakeLists.txt
@@ -0,0 +1,61 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(rhiunderqml LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
+
+qt_standard_project_setup()
+
+qt_add_executable(rhiunderqml WIN32 MACOSX_BUNDLE
+ main.cpp
+ rhisquircle.cpp rhisquircle.h
+)
+
+set_target_properties(rhiunderqml PROPERTIES
+ # Prevent name clash with build subdirectory on case-insensitive file systems
+ OUTPUT_NAME rhiunderqmlapp
+)
+
+target_link_libraries(rhiunderqml PRIVATE
+ Qt6::Core
+ Qt6::GuiPrivate
+ Qt6::Qml
+ Qt6::Quick
+)
+
+qt_add_qml_module(rhiunderqml
+ URI RhiUnderQML
+ QML_FILES
+ main.qml
+ RESOURCE_PREFIX /scenegraph/rhiunderqml
+ NO_RESOURCE_TARGET_PATH
+)
+
+qt_add_shaders(rhiunderqml "rhiunderqml_shaders"
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ /scenegraph/rhiunderqml
+ BASE
+ ../shared
+ FILES
+ ../shared/squircle_rhi.vert
+ ../shared/squircle_rhi.frag
+)
+
+install(TARGETS rhiunderqml
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET rhiunderqml
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/rhiunderqml/doc/images/rhiunderqml-example.jpg b/examples/quick/scenegraph/rhiunderqml/doc/images/rhiunderqml-example.jpg
new file mode 100644
index 0000000000..e9c5bde3f0
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/doc/images/rhiunderqml-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/rhiunderqml/doc/src/rhiunderqml.qdoc b/examples/quick/scenegraph/rhiunderqml/doc/src/rhiunderqml.qdoc
new file mode 100644
index 0000000000..175148c9c0
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/doc/src/rhiunderqml.qdoc
@@ -0,0 +1,236 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example scenegraph/rhiunderqml
+ \title Scene Graph - RHI Under QML
+ \examplecategory {Graphics}
+ \ingroup qtquickexamples
+ \brief Shows how to render directly with \l QRhi under a Qt Quick scene.
+
+ \image rhiunderqml-example.jpg
+
+ \section1 Introduction
+
+ The RHI Under QML example shows how an application can make use of the \l
+ QQuickWindow::beforeRendering() and \l
+ QQuickWindow::beforeRenderPassRecording() signals to draw custom \l{QRhi}-based
+ content under a Qt Quick scene.
+
+ Applications that wish to render \l QRhi content on top of the Qt Quick scene,
+ can do so by connecting to the \l QQuickWindow::afterRendering() and \l
+ QQuickWindow::afterRenderPassRecording() signals.
+
+ In this example, we will also see how it is possible to have values that
+ are exposed to QML which affect the QRhi-based rendering. We animate the
+ threshold value using a NumberAnimation in the QML file and this float
+ value is then passed on in a uniform buffer to the fragment shader.
+
+ The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under
+ QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11
+ Under QML}, \l{Scene Graph - Metal Under QML}{Metal Under QML}, and \l{Scene
+ Graph - Vulkan Under QML}{Vulkan Under QML} examples. Those examples render
+ the same content by directly using a 3D API. This example on the other hand
+ is fully cross-platform and portable, as it inherently supports operating
+ with all the 3D APIs supported by QRhi (such as, OpenGL, Vulkan, Metal,
+ Direct 3D 11 and 12).
+
+ \note This example demonstrates advanced, low-level functionality performing
+ portable, cross-platform 3D rendering, while relying on APIs with limited
+ compatibility guarantee from the Qt Gui module. To be able to use the QRhi
+ APIs, the application links to \c{Qt::GuiPrivate} and includes
+ \c{<rhi/qrhi.h>}.
+
+ Adding custom rendering as an underlay/overlay is one of the three ways to integrate
+ custom 2D/3D rendering into a Qt Quick scene. The other two options are to perform
+ the rendering "inline" with the Qt Quick scene's own rendering using QSGRenderNode,
+ or to generate a whole separate render pass targeting a dedicated render target
+ (a texture) and then have an item in the scene display the texture.
+ Refer to the \l{Scene Graph - RHI Texture Item} and the
+ \l{Scene Graph - Custom QSGRenderNode} examples regarding those approaches.
+
+ \section1 Core Concepts
+
+ The beforeRendering() signal is emitted at the start of every frame, before
+ the scene graph starts its rendering, thus any \l QRhi draw calls that are
+ made as a response to this signal, will stack under the Qt Quick items.
+ However, there are two signals that are relevant here: the application's own
+ \l QRhi commands should be recorded onto the same command buffer that is
+ used by the scene graph, and what's more, the commands should belong to the
+ same render pass. beforeRendering() on its own is not sufficient for this
+ because it gets emitted at the start of the frame, before starting to record
+ a render pass via \l QRhiCommandBuffer::beginPass(). By also connecting to
+ beforeRenderPassRecording(), the application's own commands and the scene
+ graph's own rendering will end up in the right order:
+
+ \list
+ \li The scene graph's render loop calls \l QRhi::beginFrame()
+ \li \l QQuickWindow::beforeRendering() is emitted - the application prepares resources for its custom rendering
+ \li The scene graph calls \l QRhiCommandBuffer::beginPass()
+ \li \l QQuickWindow::beforeRenderPassRecording() is emitted - the application records draw calls
+ \li The scene graph records draw calls
+ \endlist
+
+ \section1 Walkthrough
+
+ The custom rendering is encapsulated within a custom QQuickItem. \c
+ RhiSquircle derives from \l QQuickItem, and is exposed to QML (note the
+ \c{QML_ELEMENT}). The QML scene instantiates \c RhiSquircle. Note however
+ that this is not a visual item: the \l QQuickItem::ItemHasContents flag is
+ not set. Thus the item's position and size has no relevance and it does not
+ reimplement \l{QQuickItem::updatePaintNode()}{updatePaintNode()}.
+
+ \snippet scenegraph/rhiunderqml/rhisquircle.h 0
+
+ Instead, when the item gets associated with a \l QQuickWindow, it connects
+ to the \l{QQuickWindow::beforeSynchronizing()} signal. Using
+ Qt::DirectConnection is important since this signal is emitted on the Qt
+ Quick render thread, if there is one. We want the connected slot to be
+ invoked on this same thread.
+
+ \snippet scenegraph/rhiunderqml/rhisquircle.cpp init
+
+ In the scene graph's synchronizing phase, the rendering infrastructure is
+ created, if not yet done, and the data relevant for rendering is
+ synchronized, i.e. copied from the \c RhiSquircle item, that lives on the
+ main thread, to the \c SquircleRenderer object that lives on the render
+ thread. (if there is no render thread, then both objects live on the main
+ thread) Accessing data is safe because the main thread is blocked while the
+ render thread is executing its synchronize phase. See \l{Qt Quick Scene
+ Graph} for more information on the scene graph threading and rendering
+ model.
+
+ In addition to the value of \c t, the associated QQuickWindow pointer is
+ copied as well. While the \c SquircleRenderer could query
+ \l{QQuickItem::window()}{window()} on the \c RhiSquircle item even when
+ operating on the render thread, that is, in theory, not entirely safe. Hence
+ making a copy.
+
+ When setting up the \c SquircleRenderer, connections to the
+ \l{QQuickWindow::beforeRendering()}{beforeRendering()} and
+ \l{QQuickWindow::beforeRenderPassRecording()}{beforeRenderPassRecording()}
+ are made, which are the key to be able to act and inject the application's
+ custom 3D rendering commands at the appropriate time.
+
+ \snippet scenegraph/rhiunderqml/rhisquircle.cpp sync
+
+ When \l{QQuickWindow::beforeRendering()}{beforeRendering()} is emitted, the
+ QRhi resources needed for our custom rendering, such as \l QRhiBuffer, \l
+ QRhiGraphicsPipeline, and related objects, are created if not yet done.
+
+ The data in the buffers is updated (more precisely, the data update
+ operations are enqueued) using \l QRhiResourceUpdateBatch and \l
+ QRhiCommandBuffer::resourceUpdate(). The vertex buffer does not change its
+ contents once the initial set of vertices are uploaded to it. The uniform
+ buffer however is a \l{QRhiBuffer::Dynamic}{dynamic} buffer, as is typical
+ for such buffers. Its content, some regions at least, is updated for every
+ frame. Hence the unconditional call to
+ \l{QRhiResourceUpdateBatch::updateDynamicBuffer()}{updateDynamicBuffer()}
+ for offset 0 and a byte size of 4 (which is \c{sizeof(float)} since the C++
+ \c float type happens to match GLSL's 32-bit \c float). What is stored at
+ that position is the value of \c t, and that is updated in every frame,
+ meaning in every invocation of frameStart().
+
+ There is an additional float value in the buffer, starting at offset 4. This
+ is used to cater to the coordinate system differences of the 3D APIs: when
+ \l{QRhi::isYUpInNDC()}{isYUpInNDC()} returns \c false, which is the case
+ with Vulkan in particular, the value is set to -1.0 which leads to flipping
+ the Y value in the 2 component vector that is passed on (with interpolation)
+ to the fragment shader based on which the color is calculated. This way the
+ output on the screen is identical (i.e. the top-left corner is green-ish,
+ the bottom-left is red-ish), regardless of which 3D API is in use. This
+ value is updated only once in the uniform buffer, similarly to the vertex
+ buffer. This highlights an issue low-level rendering code that aims to be
+ portable often needs to deal with: the coordinate system differences in
+ normalized device coordinates (NDC) and in images and framebuffers. For
+ example, the NDC uses a origin-at-bottom-left system everywhere except
+ Vulkan. Whereas framebuffers use an origin-at-top-left system everywhere
+ except OpenGL. Typical renderers that work with a perspective projection can
+ often be oblivious to this problem by conveniently relying on
+ \l{QRhi::clipSpaceCorrMatrix()}, which is a matrix that can be multiplied in
+ to the projection matrix, and applies both an Y flip when needed, and also
+ caters to the fact that clip space depth runs \c{-1..1} with OpenGL but
+ \c{0..1} everywhere else. However, in some cases, such as in this example,
+ this is not applicable. Rather, the application and shader logic needs to
+ perform the necessary adjustment of vertex and UV positions as appropriate
+ based on querying \l QRhi::isYUpInNDC() and \l QRhi::isYUpInFramebuffer().
+
+ To gain access to the \l QRhi and \l QRhiSwapChain objects Qt Quick uses,
+ they can simply be queried from the \l QQuickWindow. Note that this assumes
+ that the QQuickWindow is a regular, on-screen window. If it used \l
+ QQuickRenderControl instead, e.g. to perform off-screen rendering into a
+ texture, querying the swapchain would be wrong since there is no swapchain
+ then.
+
+ Due to the signal being emitted after Qt Quick calls \l QRhi::beginFrame(),
+ it is already possible to query the command buffer and render target from
+ the swapchain. This is what allows to conveniently issue a \l
+ QRhiCommandBuffer::resourceUpdate() on the object returned from \l
+ QRhiSwapChain::currentFrameCommandBuffer(). When creating a graphics
+ pipeline, a QRhiRenderPassDescriptor can be retrieved from the
+ QRhiRenderTarget returned from \l QRhiSwapChain::currentFrameRenderTarget().
+ (note that this means the graphics pipeline built here is suitable only for
+ rendering to the swapchain, or at best another render target that is
+ \l{QRhiRenderPassDescriptor::isCompatible()}{compatible} with it; it is
+ likely that if we wanted to render to a texture, then a different
+ QRhiRenderPassDescriptor, and so a different graphics pipeline, would be
+ needed since the texture and swapchain formats may differ)
+
+ \snippet scenegraph/rhiunderqml/rhisquircle.cpp frame-start
+
+ Finally, upon \l QQuickWindow::beforeRenderPassRecording(), a draw call for
+ a triangle strip with 4 vertices is recorded. This example simply draws a
+ quad in practice, and calculates the pixel colors using the logic in the
+ fragment shaders, but applications are free to do more complicated drawing:
+ creating multiple graphics pipelines and recording multiple draw calls is
+ perfectly fine as well. The important thing to keep in mind is that whatever
+ is recorded on the \l QRhiCommandBuffer retrieved from the window's
+ \l{QRhiSwapChain}{swapchain}, it is effectively prepended before the Qt Quick
+ scene graph's own rendering within the main render pass.
+
+ \note This means that if depth buffer usage with depth testing and writing
+ out depth values is involved, then the Qt Quick content may be affected by
+ the values written to the depth buffer. See \l{Qt Quick Scene Graph Default
+ Renderer} for details on the scene graph's renderer, in particular the
+ sections about the handling of \e opaque and \e{alpha blended} primitives.
+
+ To get the window size in pixels, \l QRhiRenderTarget::pixelSize() is used.
+ This is convenient because this way the example does not need to calculate
+ the viewport size by other means and does not have to worry about applying
+ the \l{QWindow::devicePixelRatio()}{high DPI scale factor}, if there is any.
+
+ \snippet scenegraph/rhiunderqml/rhisquircle.cpp frame-render
+
+ The vertex and fragment shaders go through the standard QRhi shader
+ conditioning pipeline. Initially written as Vulkan-compatible GLSL, they get
+ compiled to SPIR-V and then transpiled to other shading languages by Qt's
+ tools. When using CMake, the example relies on the \c qt_add_shaders command
+ that makes it simple and convenient to bundle the shaders with the
+ application and perform the necessary processing at build time. See \l{Qt
+ Shader Tools Build System Integration} for details.
+
+ Specifying \c BASE helps removing the \c{../shared} prefix, while \c PREFIX
+ adds the intended \c{/scenegraph/rhiunderqml} prefix. Thus the final path is
+ \c{:/scenegraph/rhiunderqml/squircle_rhi.vert.qsb}.
+
+ \badcode
+ qt_add_shaders(rhiunderqml "rhiunderqml_shaders"
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ /scenegraph/rhiunderqml
+ BASE
+ ../shared
+ FILES
+ ../shared/squircle_rhi.vert
+ ../shared/squircle_rhi.frag
+ )
+ \endcode
+
+ To support qmake, the example still ships the \c{.qsb} files that would
+ normally be generated at build time, and lists them in the qrc file. This
+ approach is however not recommended for new applications that use CMake as
+ the build system.
+
+ \sa {Scene Graph - RHI Texture Item}, {Scene Graph - Custom QSGRenderNode}
+ */
diff --git a/examples/quick/scenegraph/rhiunderqml/main.cpp b/examples/quick/scenegraph/rhiunderqml/main.cpp
new file mode 100644
index 0000000000..51b8d18ddb
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QtQuick/QQuickView>
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/rhiunderqml/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/examples/quick/scenegraph/rhiunderqml/main.qml b/examples/quick/scenegraph/rhiunderqml/main.qml
new file mode 100644
index 0000000000..3faa028457
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/main.qml
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import RhiUnderQML
+
+Item {
+ width: 320
+ height: 480
+
+ RhiSquircle {
+ SequentialAnimation on t {
+ NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
+ NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
+ loops: Animation.Infinite
+ running: true
+ }
+ }
+
+ Rectangle {
+ color: Qt.rgba(1, 1, 1, 0.7)
+ radius: 10
+ border.width: 1
+ border.color: "white"
+ anchors.fill: label
+ anchors.margins: -10
+ }
+
+ Text {
+ id: label
+ color: "black"
+ wrapMode: Text.WordWrap
+ text: qsTr("The background here is a squircle rendered with QRhi using the beforeRendering() and beforeRenderPassRecording() signals in QQuickWindow. This text label and its border is rendered using QML")
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ }
+}
diff --git a/examples/quick/scenegraph/rhiunderqml/prebuilts_for_qmake/squircle_rhi.frag.qsb b/examples/quick/scenegraph/rhiunderqml/prebuilts_for_qmake/squircle_rhi.frag.qsb
new file mode 100644
index 0000000000..077a31f945
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/prebuilts_for_qmake/squircle_rhi.frag.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/rhiunderqml/prebuilts_for_qmake/squircle_rhi.vert.qsb b/examples/quick/scenegraph/rhiunderqml/prebuilts_for_qmake/squircle_rhi.vert.qsb
new file mode 100644
index 0000000000..1e87cf09a7
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/prebuilts_for_qmake/squircle_rhi.vert.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/rhiunderqml/rhisquircle.cpp b/examples/quick/scenegraph/rhiunderqml/rhisquircle.cpp
new file mode 100644
index 0000000000..135cf128e3
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/rhisquircle.cpp
@@ -0,0 +1,224 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "rhisquircle.h"
+#include <QtQuick/QQuickWindow>
+#include <QtCore/QFile>
+#include <QtCore/QRunnable>
+
+#include <rhi/qrhi.h>
+
+class SquircleRenderer : public QObject
+{
+ Q_OBJECT
+public:
+ void setT(qreal t) { m_t = t; }
+ void setWindow(QQuickWindow *window) { m_window = window; }
+
+public slots:
+ void frameStart();
+ void mainPassRecordingStart();
+
+private:
+ qreal m_t = 0;
+ QQuickWindow *m_window;
+ QShader m_vertexShader;
+ QShader m_fragmentShader;
+ std::unique_ptr<QRhiBuffer> m_vertexBuffer;
+ std::unique_ptr<QRhiBuffer> m_uniformBuffer;
+ std::unique_ptr<QRhiShaderResourceBindings> m_srb;
+ std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
+};
+
+//! [init]
+RhiSquircle::RhiSquircle()
+{
+ connect(this, &QQuickItem::windowChanged, this, &RhiSquircle::handleWindowChanged);
+}
+
+void RhiSquircle::handleWindowChanged(QQuickWindow *win)
+{
+ if (win) {
+ connect(win, &QQuickWindow::beforeSynchronizing, this, &RhiSquircle::sync, Qt::DirectConnection);
+ connect(win, &QQuickWindow::sceneGraphInvalidated, this, &RhiSquircle::cleanup, Qt::DirectConnection);
+ // Ensure we start with cleared to black. The squircle's blend mode relies on this.
+ win->setColor(Qt::black);
+ }
+}
+//! [init]
+
+// The safe way to release custom graphics resources is to both connect to
+// sceneGraphInvalidated() and implement releaseResources(). To support
+// threaded render loops the latter performs the SquircleRenderer destruction
+// via scheduleRenderJob(). Note that the RhiSquircle may be gone by the time
+// the QRunnable is invoked.
+
+void RhiSquircle::cleanup()
+{
+ // This function is invoked on the render thread, if there is one.
+
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+class CleanupJob : public QRunnable
+{
+public:
+ CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { }
+ void run() override { delete m_renderer; }
+private:
+ SquircleRenderer *m_renderer;
+};
+
+void RhiSquircle::releaseResources()
+{
+ window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage);
+ m_renderer = nullptr;
+}
+
+void RhiSquircle::setT(qreal t)
+{
+ if (t == m_t)
+ return;
+ m_t = t;
+ emit tChanged();
+ if (window())
+ window()->update();
+}
+
+//! [sync]
+void RhiSquircle::sync()
+{
+ // This function is invoked on the render thread, if there is one.
+
+ if (!m_renderer) {
+ m_renderer = new SquircleRenderer;
+ // Initializing resources is done before starting to record the
+ // renderpass, regardless of wanting an underlay or overlay.
+ connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection);
+ // Here we want an underlay and therefore connect to
+ // beforeRenderPassRecording. Changing to afterRenderPassRecording
+ // would render the squircle on top (overlay).
+ connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection);
+ }
+ m_renderer->setT(m_t);
+ m_renderer->setWindow(window());
+}
+//! [sync]
+
+static QShader getShader(const QString &name)
+{
+ QFile f(name);
+ if (f.open(QIODevice::ReadOnly))
+ return QShader::fromSerialized(f.readAll());
+
+ return QShader();
+}
+
+static const float vertices[] = {
+ -1, -1,
+ 1, -1,
+ -1, 1,
+ 1, 1
+};
+
+//! [frame-start]
+void SquircleRenderer::frameStart()
+{
+ // This function is invoked on the render thread, if there is one.
+
+ QRhi *rhi = m_window->rhi();
+ if (!rhi) {
+ qWarning("QQuickWindow is not using QRhi for rendering");
+ return;
+ }
+ QRhiSwapChain *swapChain = m_window->swapChain();
+ if (!swapChain) {
+ qWarning("No QRhiSwapChain?");
+ return;
+ }
+ QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
+
+ if (!m_pipeline) {
+ m_vertexShader = getShader(QLatin1String(":/scenegraph/rhiunderqml/squircle_rhi.vert.qsb"));
+ if (!m_vertexShader.isValid())
+ qWarning("Failed to load vertex shader; rendering will be incorrect");
+
+ m_fragmentShader = getShader(QLatin1String(":/scenegraph/rhiunderqml/squircle_rhi.frag.qsb"));
+ if (!m_fragmentShader.isValid())
+ qWarning("Failed to load fragment shader; rendering will be incorrect");
+
+ m_vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
+ m_vertexBuffer->create();
+ resourceUpdates->uploadStaticBuffer(m_vertexBuffer.get(), vertices);
+
+ const quint32 UBUF_SIZE = 4 + 4; // 2 floats
+ m_uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, UBUF_SIZE));
+ m_uniformBuffer->create();
+
+ float yDir = rhi->isYUpInNDC() ? 1.0f : -1.0f;
+ resourceUpdates->updateDynamicBuffer(m_uniformBuffer.get(), 4, 4, &yDir);
+
+ m_srb.reset(rhi->newShaderResourceBindings());
+ const auto visibleToAll = QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
+ m_srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, visibleToAll, m_uniformBuffer.get())
+ });
+ m_srb->create();
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 2 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 }
+ });
+
+ m_pipeline.reset(rhi->newGraphicsPipeline());
+ m_pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.enable = true;
+ blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
+ blend.srcAlpha = QRhiGraphicsPipeline::SrcAlpha;
+ blend.dstColor = QRhiGraphicsPipeline::One;
+ blend.dstAlpha = QRhiGraphicsPipeline::One;
+ m_pipeline->setTargetBlends({ blend });
+ m_pipeline->setShaderStages({
+ { QRhiShaderStage::Vertex, m_vertexShader },
+ { QRhiShaderStage::Fragment, m_fragmentShader }
+ });
+ m_pipeline->setVertexInputLayout(inputLayout);
+ m_pipeline->setShaderResourceBindings(m_srb.get());
+ m_pipeline->setRenderPassDescriptor(swapChain->currentFrameRenderTarget()->renderPassDescriptor());
+ m_pipeline->create();
+ }
+
+ float t = m_t;
+ resourceUpdates->updateDynamicBuffer(m_uniformBuffer.get(), 0, 4, &t);
+
+ swapChain->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
+}
+//! [frame-start]
+
+//! [frame-render]
+void SquircleRenderer::mainPassRecordingStart()
+{
+ // This function is invoked on the render thread, if there is one.
+
+ QRhi *rhi = m_window->rhi();
+ QRhiSwapChain *swapChain = m_window->swapChain();
+ if (!rhi || !swapChain)
+ return;
+
+ const QSize outputPixelSize = swapChain->currentFrameRenderTarget()->pixelSize();
+ QRhiCommandBuffer *cb = m_window->swapChain()->currentFrameCommandBuffer();
+ cb->setViewport({ 0.0f, 0.0f, float(outputPixelSize.width()), float(outputPixelSize.height()) });
+ cb->setGraphicsPipeline(m_pipeline.get());
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput vbufBinding(m_vertexBuffer.get(), 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(4);
+}
+//! [frame-render]
+
+#include "rhisquircle.moc"
diff --git a/examples/quick/scenegraph/rhiunderqml/rhisquircle.h b/examples/quick/scenegraph/rhiunderqml/rhisquircle.h
new file mode 100644
index 0000000000..507e7b930f
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/rhisquircle.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef RHISQUIRCLE_H
+#define RHISQUIRCLE_H
+
+#include <QtQuick/QQuickItem>
+#include <QtQuick/QQuickWindow>
+
+class SquircleRenderer;
+
+//! [0]
+class RhiSquircle : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
+ QML_ELEMENT
+
+public:
+ RhiSquircle();
+
+ qreal t() const { return m_t; }
+ void setT(qreal t);
+
+signals:
+ void tChanged();
+
+public slots:
+ void sync();
+ void cleanup();
+
+private slots:
+ void handleWindowChanged(QQuickWindow *win);
+
+private:
+ void releaseResources() override;
+
+ qreal m_t = 0;
+ SquircleRenderer *m_renderer = nullptr;
+};
+//! [0]
+
+#endif
diff --git a/examples/quick/scenegraph/rhiunderqml/rhiunderqml.pro b/examples/quick/scenegraph/rhiunderqml/rhiunderqml.pro
new file mode 100644
index 0000000000..af85005e80
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/rhiunderqml.pro
@@ -0,0 +1,11 @@
+QT += gui-private qml quick
+CONFIG += qmltypes
+QML_IMPORT_NAME = RhiUnderQML
+QML_IMPORT_MAJOR_VERSION = 1
+
+HEADERS += rhisquircle.h
+SOURCES += rhisquircle.cpp main.cpp
+RESOURCES += rhiunderqml.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/rhiunderqml
+INSTALLS += target
diff --git a/examples/quick/scenegraph/rhiunderqml/rhiunderqml.qrc b/examples/quick/scenegraph/rhiunderqml/rhiunderqml.qrc
new file mode 100644
index 0000000000..690cb36444
--- /dev/null
+++ b/examples/quick/scenegraph/rhiunderqml/rhiunderqml.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/scenegraph/rhiunderqml">
+ <file>main.qml</file>
+ <file alias="squircle_rhi.vert.qsb">prebuilts_for_qmake/squircle_rhi.vert.qsb</file>
+ <file alias="squircle_rhi.frag.qsb">prebuilts_for_qmake/squircle_rhi.frag.qsb</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro
index a438bb8341..c37e822680 100644
--- a/examples/quick/scenegraph/scenegraph.pro
+++ b/examples/quick/scenegraph/scenegraph.pro
@@ -2,8 +2,7 @@ TEMPLATE = subdirs
qtConfig(opengl(es1|es2)?) {
SUBDIRS += \
- fboitem \
- openglunderqml \
+ openglunderqml
}
SUBDIRS += \
@@ -11,7 +10,9 @@ SUBDIRS += \
custommaterial \
graph \
threadedanimation \
- twotextureproviders
+ twotextureproviders \
+ rhiunderqml \
+ rhitextureitem
macos|ios {
SUBDIRS += \
diff --git a/examples/quick/scenegraph/shared/logorenderer.cpp b/examples/quick/scenegraph/shared/logorenderer.cpp
deleted file mode 100644
index b95ae0d21d..0000000000
--- a/examples/quick/scenegraph/shared/logorenderer.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "logorenderer.h"
-#include <QPainter>
-#include <QPaintEngine>
-#include <qmath.h>
-
-LogoRenderer::LogoRenderer()
-{
-}
-
-LogoRenderer::~LogoRenderer()
-{
-}
-
-
-void LogoRenderer::paintQtLogo()
-{
- program1.enableAttributeArray(normalAttr1);
- program1.enableAttributeArray(vertexAttr1);
- program1.setAttributeArray(vertexAttr1, vertices.constData());
- program1.setAttributeArray(normalAttr1, normals.constData());
- glDrawArrays(GL_TRIANGLES, 0, vertices.size());
- program1.disableAttributeArray(normalAttr1);
- program1.disableAttributeArray(vertexAttr1);
-}
-
-
-void LogoRenderer::initialize()
-{
- initializeOpenGLFunctions();
-
- glClearColor(0.1f, 0.1f, 0.2f, 1.0f);
-
- const char *vsrc1 =
- "attribute highp vec4 vertex;\n"
- "attribute mediump vec3 normal;\n"
- "uniform mediump mat4 matrix;\n"
- "varying mediump vec4 color;\n"
- "void main(void)\n"
- "{\n"
- " vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
- " float angle = max(dot(normal, toLight), 0.0);\n"
- " vec3 col = vec3(0.40, 1.0, 0.0);\n"
- " color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n"
- " color = clamp(color, 0.0, 1.0);\n"
- " gl_Position = matrix * vertex;\n"
- "}\n";
-
- const char *fsrc1 =
- "varying mediump vec4 color;\n"
- "void main(void)\n"
- "{\n"
- " gl_FragColor = color;\n"
- "}\n";
-
- program1.addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vsrc1);
- program1.addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fsrc1);
- program1.link();
-
- vertexAttr1 = program1.attributeLocation("vertex");
- normalAttr1 = program1.attributeLocation("normal");
- matrixUniform1 = program1.uniformLocation("matrix");
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-
- m_fAngle = 0;
- m_fScale = 1;
- createGeometry();
-}
-
-void LogoRenderer::render()
-{
- glDepthMask(true);
-
- glClearColor(0.5f, 0.5f, 0.7f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-
- glFrontFace(GL_CW);
- glCullFace(GL_FRONT);
- glEnable(GL_CULL_FACE);
- glEnable(GL_DEPTH_TEST);
-
- QMatrix4x4 modelview;
- modelview.rotate(m_fAngle, 0.0f, 1.0f, 0.0f);
- modelview.rotate(m_fAngle, 1.0f, 0.0f, 0.0f);
- modelview.rotate(m_fAngle, 0.0f, 0.0f, 1.0f);
- modelview.scale(m_fScale);
- modelview.translate(0.0f, -0.2f, 0.0f);
-
- program1.bind();
- program1.setUniformValue(matrixUniform1, modelview);
- paintQtLogo();
- program1.release();
-
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_CULL_FACE);
-
- m_fAngle += 1.0f;
-}
-
-void LogoRenderer::createGeometry()
-{
- vertices.clear();
- normals.clear();
-
- qreal x1 = +0.06f;
- qreal y1 = -0.14f;
- qreal x2 = +0.14f;
- qreal y2 = -0.06f;
- qreal x3 = +0.08f;
- qreal y3 = +0.00f;
- qreal x4 = +0.30f;
- qreal y4 = +0.22f;
-
- quad(x1, y1, x2, y2, y2, x2, y1, x1);
- quad(x3, y3, x4, y4, y4, x4, y3, x3);
-
- extrude(x1, y1, x2, y2);
- extrude(x2, y2, y2, x2);
- extrude(y2, x2, y1, x1);
- extrude(y1, x1, x1, y1);
- extrude(x3, y3, x4, y4);
- extrude(x4, y4, y4, x4);
- extrude(y4, x4, y3, x3);
-
- const qreal Pi = M_PI;
- const int NumSectors = 100;
-
- for (int i = 0; i < NumSectors; ++i) {
- qreal angle1 = (i * 2 * Pi) / NumSectors;
- qreal x5 = 0.30 * sin(angle1);
- qreal y5 = 0.30 * cos(angle1);
- qreal x6 = 0.20 * sin(angle1);
- qreal y6 = 0.20 * cos(angle1);
-
- qreal angle2 = ((i + 1) * 2 * Pi) / NumSectors;
- qreal x7 = 0.20 * sin(angle2);
- qreal y7 = 0.20 * cos(angle2);
- qreal x8 = 0.30 * sin(angle2);
- qreal y8 = 0.30 * cos(angle2);
-
- quad(x5, y5, x6, y6, x7, y7, x8, y8);
-
- extrude(x6, y6, x7, y7);
- extrude(x8, y8, x5, y5);
- }
-
- for (int i = 0;i < vertices.size();i++)
- vertices[i] *= 2.0f;
-}
-
-void LogoRenderer::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4)
-{
- vertices << QVector3D(x1, y1, -0.05f);
- vertices << QVector3D(x2, y2, -0.05f);
- vertices << QVector3D(x4, y4, -0.05f);
-
- vertices << QVector3D(x3, y3, -0.05f);
- vertices << QVector3D(x4, y4, -0.05f);
- vertices << QVector3D(x2, y2, -0.05f);
-
- QVector3D n = QVector3D::normal
- (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f));
-
- normals << n;
- normals << n;
- normals << n;
-
- normals << n;
- normals << n;
- normals << n;
-
- vertices << QVector3D(x4, y4, 0.05f);
- vertices << QVector3D(x2, y2, 0.05f);
- vertices << QVector3D(x1, y1, 0.05f);
-
- vertices << QVector3D(x2, y2, 0.05f);
- vertices << QVector3D(x4, y4, 0.05f);
- vertices << QVector3D(x3, y3, 0.05f);
-
- n = QVector3D::normal
- (QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f));
-
- normals << n;
- normals << n;
- normals << n;
-
- normals << n;
- normals << n;
- normals << n;
-}
-
-void LogoRenderer::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
-{
- vertices << QVector3D(x1, y1, +0.05f);
- vertices << QVector3D(x2, y2, +0.05f);
- vertices << QVector3D(x1, y1, -0.05f);
-
- vertices << QVector3D(x2, y2, -0.05f);
- vertices << QVector3D(x1, y1, -0.05f);
- vertices << QVector3D(x2, y2, +0.05f);
-
- QVector3D n = QVector3D::normal
- (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));
-
- normals << n;
- normals << n;
- normals << n;
-
- normals << n;
- normals << n;
- normals << n;
-}
diff --git a/examples/quick/scenegraph/shared/logorenderer.h b/examples/quick/scenegraph/shared/logorenderer.h
deleted file mode 100644
index 06c49baab1..0000000000
--- a/examples/quick/scenegraph/shared/logorenderer.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef LOGORENDERER_H
-#define LOGORENDERER_H
-
-#include <QtGui/qvector3d.h>
-#include <QtGui/qmatrix4x4.h>
-#include <qopenglshaderprogram.h>
-#include <qopenglfunctions.h>
-
-#include <QTime>
-#include <QList>
-
-class LogoRenderer : protected QOpenGLFunctions
-{
-public:
- LogoRenderer();
- ~LogoRenderer();
-
- void render();
- void initialize();
-
-private:
-
- qreal m_fAngle;
- qreal m_fScale;
-
- void paintQtLogo();
- void createGeometry();
- void quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4);
- void extrude(qreal x1, qreal y1, qreal x2, qreal y2);
-
- QList<QVector3D> vertices;
- QList<QVector3D> normals;
- QOpenGLShaderProgram program1;
- int vertexAttr1;
- int normalAttr1;
- int matrixUniform1;
-};
-#endif
diff --git a/examples/quick/scenegraph/shared/squircle_rhi.frag b/examples/quick/scenegraph/shared/squircle_rhi.frag
index 8da62b93e6..83669bdfaa 100644
--- a/examples/quick/scenegraph/shared/squircle_rhi.frag
+++ b/examples/quick/scenegraph/shared/squircle_rhi.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 coords;
@@ -5,12 +8,13 @@ layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
float t;
-} ubuf;
+ float y_dir;
+};
void main()
{
- float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));
- i = smoothstep(ubuf.t - 0.8, ubuf.t + 0.8, i);
- i = floor(i * 20.) / 20.;
- fragColor = vec4(coords * .5 + .5, i, i);
+ float i = 1.0 - (pow(abs(coords.x), 4.0) + pow(abs(coords.y), 4.0));
+ i = smoothstep(t - 0.8, t + 0.8, i);
+ i = floor(i * 20.0) / 20.0;
+ fragColor = vec4(coords * 0.5 + 0.5, i, i);
}
diff --git a/examples/quick/scenegraph/shared/squircle_rhi.vert b/examples/quick/scenegraph/shared/squircle_rhi.vert
index b57dfdfe10..bf079ab786 100644
--- a/examples/quick/scenegraph/shared/squircle_rhi.vert
+++ b/examples/quick/scenegraph/shared/squircle_rhi.vert
@@ -1,13 +1,16 @@
#version 440
layout(location = 0) in vec4 vertices;
-
layout(location = 0) out vec2 coords;
-out gl_PerVertex { vec4 gl_Position; };
+layout(std140, binding = 0) uniform buf {
+ float t;
+ float y_dir;
+};
void main()
{
gl_Position = vertices;
coords = vertices.xy;
+ coords.y *= y_dir;
}
diff --git a/examples/quick/scenegraph/threadedanimation/CMakeLists.txt b/examples/quick/scenegraph/threadedanimation/CMakeLists.txt
index a0e98f5e99..0945a13c98 100644
--- a/examples/quick/scenegraph/threadedanimation/CMakeLists.txt
+++ b/examples/quick/scenegraph/threadedanimation/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(threadedanimation LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/threadedanimation")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_standard_project_setup()
@@ -20,10 +14,10 @@ qt_add_executable(threadedanimation WIN32 MACOSX_BUNDLE
)
target_link_libraries(threadedanimation PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(threadedanimation
@@ -35,7 +29,16 @@ qt_add_qml_module(threadedanimation
)
install(TARGETS threadedanimation
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET threadedanimation
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/threadedanimation/doc/images/threadedanimation-example.jpg b/examples/quick/scenegraph/threadedanimation/doc/images/threadedanimation-example.jpg
new file mode 100644
index 0000000000..42c886b7ab
--- /dev/null
+++ b/examples/quick/scenegraph/threadedanimation/doc/images/threadedanimation-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/threadedanimation/doc/src/threadedanimation.qdoc b/examples/quick/scenegraph/threadedanimation/doc/src/threadedanimation.qdoc
new file mode 100644
index 0000000000..01b842d07a
--- /dev/null
+++ b/examples/quick/scenegraph/threadedanimation/doc/src/threadedanimation.qdoc
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example scenegraph/threadedanimation
+ \title Scene Graph - Threaded Animation
+ \examplecategory {Graphics}
+ \ingroup qtquickexamples
+ \brief Shows benefits of custom items animating independently of the main thread while using the threaded render loop of Qt Quick.
+
+ \image threadedanimation-example.jpg
+
+ This example shows the fundamental concept behind the \l Animator types, by
+ implementing a custom item that effectively animates itself by not relying
+ on the standard Qt Quick animation framework driven on the main thread.
+
+ The left and right spinners should behave identically under normal
+ conditions. However, once the example blocks the main thread by peforming
+ some heavy operation, it will become noticeable that the left spinner is not
+ animating smoothly anymore.
+
+ \note This example should be run with the \c threaded render loop of Qt
+ Quick. This is the default in most cases. There are no positive effects when
+ using the \c basic render loop, because there everything, including all
+ rendering, happens on the main thread.
+
+ Applications without custom QQuickItem implementations can get the same
+ benefits by using one of the \l Animator types, such as \l XAnimator or \l
+ OpacityAnimator from QML. Whereas when building custom items, similar
+ results can be achieved by following the example's implementation.
+
+ \sa {Qt Quick Scene Graph}
+ */
diff --git a/examples/quick/scenegraph/twotextureproviders/CMakeLists.txt b/examples/quick/scenegraph/twotextureproviders/CMakeLists.txt
index 153b9d123b..cf597d3f0b 100644
--- a/examples/quick/scenegraph/twotextureproviders/CMakeLists.txt
+++ b/examples/quick/scenegraph/twotextureproviders/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(twotextureproviders LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/twotextureproviders")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
qt_standard_project_setup()
@@ -20,10 +14,10 @@ qt_add_executable(twotextureproviders WIN32 MACOSX_BUNDLE
)
target_link_libraries(twotextureproviders PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(twotextureproviders
@@ -47,7 +41,16 @@ qt6_add_shaders(twotextureproviders "shaders"
)
install(TARGETS twotextureproviders
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET twotextureproviders
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/twotextureproviders/doc/src/twotextureproviders.qdoc b/examples/quick/scenegraph/twotextureproviders/doc/src/twotextureproviders.qdoc
index a639c9b350..1bdd1ab551 100644
--- a/examples/quick/scenegraph/twotextureproviders/doc/src/twotextureproviders.qdoc
+++ b/examples/quick/scenegraph/twotextureproviders/doc/src/twotextureproviders.qdoc
@@ -4,6 +4,7 @@
/*!
\example scenegraph/twotextureproviders
\title Scene Graph - Two Texture Providers
+ \examplecategory {Graphics}
\ingroup qtquickexamples
\brief Shows how to combine two textures from two texture providers in a custom scene graph node.
diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/checker.frag b/examples/quick/scenegraph/twotextureproviders/shaders/checker.frag
index 0932bc8c37..c684a2cedd 100644
--- a/examples/quick/scenegraph/twotextureproviders/shaders/checker.frag
+++ b/examples/quick/scenegraph/twotextureproviders/shaders/checker.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag
index bc68160c1f..8587ae5316 100644
--- a/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag
+++ b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 vTexCoord;
diff --git a/examples/quick/scenegraph/vulkantextureimport/CMakeLists.txt b/examples/quick/scenegraph/vulkantextureimport/CMakeLists.txt
index 246fb2d6d2..778fe5bd87 100644
--- a/examples/quick/scenegraph/vulkantextureimport/CMakeLists.txt
+++ b/examples/quick/scenegraph/vulkantextureimport/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(vulkantextureimport LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/vulkantextureimport")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_standard_project_setup()
@@ -25,10 +19,10 @@ set_target_properties(vulkantextureimport PROPERTIES
)
target_link_libraries(vulkantextureimport PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(vulkantextureimport
@@ -43,7 +37,16 @@ qt_add_qml_module(vulkantextureimport
)
install(TARGETS vulkantextureimport
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET vulkantextureimport
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/vulkantextureimport/doc/src/vulkantextureimport.qdoc b/examples/quick/scenegraph/vulkantextureimport/doc/src/vulkantextureimport.qdoc
index 0d5571054a..97e7d8045f 100644
--- a/examples/quick/scenegraph/vulkantextureimport/doc/src/vulkantextureimport.qdoc
+++ b/examples/quick/scenegraph/vulkantextureimport/doc/src/vulkantextureimport.qdoc
@@ -4,8 +4,9 @@
/*!
\example scenegraph/vulkantextureimport
\title Scene Graph - Vulkan Texture Import
+ \examplecategory {Graphics}
\ingroup qtquickexamples
- \brief Shows how to use a texture created directly with use a texture created directly withulkan.
+ \brief Shows how to use a texture created directly with Vulkan.
\image vulkantextureimport-example.jpg
diff --git a/examples/quick/scenegraph/vulkanunderqml/CMakeLists.txt b/examples/quick/scenegraph/vulkanunderqml/CMakeLists.txt
index 79cbc60477..12b07b4518 100644
--- a/examples/quick/scenegraph/vulkanunderqml/CMakeLists.txt
+++ b/examples/quick/scenegraph/vulkanunderqml/CMakeLists.txt
@@ -1,15 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(vulkanunderqml LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/scenegraph/vulkanunderqml")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_standard_project_setup()
@@ -25,10 +19,10 @@ set_target_properties(vulkanunderqml PROPERTIES
)
target_link_libraries(vulkanunderqml PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(vulkanunderqml
@@ -43,7 +37,16 @@ qt_add_qml_module(vulkanunderqml
)
install(TARGETS vulkanunderqml
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET vulkanunderqml
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc b/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc
index 08b84da7df..bb29731554 100644
--- a/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc
+++ b/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc
@@ -4,36 +4,20 @@
/*!
\example scenegraph/vulkanunderqml
\title Scene Graph - Vulkan Under QML
+ \examplecategory {Graphics}
\ingroup qtquickexamples
- \brief Shows how to render directly with vulkan under a Qt Quick scene.
+ \brief Shows how to render directly with Vulkan under a Qt Quick scene.
\image vulkanunderqml-example.jpg
- The Vulkan Under QML example shows how an application can make use of the
- \l QQuickWindow::beforeRendering() and \l
- QQuickWindow::beforeRenderPassRecording() signals to draw custom Vulkan
- content under a Qt Quick scene. This signal is emitted at the start of
- every frame, before the scene graph starts its rendering, thus any Vulkan
- draw calls that are made as a response to this signal, will stack under the
- Qt Quick items. There are two signals, because the custom Vulkan commands
- are recorded onto the same command buffer that is used by the scene graph.
- beforeRendering() on its own is not sufficient for this because it gets
- emitted at the start of the frame, before recording the start of a
- renderpass instance via
- \l{https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBeginRenderPass.html}{vkCmdBeginRenderPass}.
- By also connecting to beforeRenderPassRecording(), the application's own
- commands and the scene graph's scaffolding will end up in the right order.
+ \note Compiling this example requires an SDK. See \l{Vulkan Integration} for
+ information on what to install.
- As an alternative, applications that wish to render Vulkan content
- on top of the Qt Quick scene, can do so by connecting to the \l
- QQuickWindow::afterRendering() and \l
- QQuickWindow::afterRenderPassRecording() signals.
+ \section1 Overview
- In this example, we will also see how it is possible to have
- values that are exposed to QML which affect the Vulkan
- rendering. We animate the threshold value using a NumberAnimation
- in the QML file and this value is used by the SPIR-V shader
- program that draws the squircles.
+ This example makes use of the \l QQuickWindow::beforeRendering()
+ and \l QQuickWindow::beforeRenderPassRecording() signals to draw custom Vulkan
+ content under a Qt Quick scene. QML is used to render a text label on top.
The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under
QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11
@@ -41,4 +25,60 @@
examples, they all render the same custom content, just via different
native APIs.
+ The particulars of utilizing QML will be covered in this documentation
+ without delving into the detail of custom Vulkan rendering.
+
+ \section1 Affecting Vulkan rendering from QML
+
+ The example shows how to use values that are exposed to QML to control
+ Vulkan rendering.
+
+ To expose the threshold value \c t to QML, in the definition of \c VulkanSquircle,
+ we use the \l Q_OBJECT, \l Q_PROPERTY, and \l QML_ELEMENT macros like so:
+
+ \quotefromfile scenegraph/vulkanunderqml/vulkansquircle.h
+ \skipto class VulkanSquircle
+ \printto public:
+
+ We then go on to declare public and private items:
+
+ \printto };
+
+ Then in \c main.qml we animate the threshold value using a \l NumberAnimation.
+
+ \quotefromfile scenegraph/vulkanunderqml/main.qml
+ \skipto VulkanSquircle {
+ \printto running: true
+ The \c t variable is ultimately used by the SPIR-V shader program that draws
+ the squircles.
+
+ \section1 Using signals to render custom Vulkan content
+
+ The \l QQuickWindow::beforeRendering()
+ and \l QQuickWindow::beforeRenderPassRecording() signals are what are used.
+
+ The QQuickWindow::beforeRendering() signal is emitted at the start of
+ every frame, before the scene graph starts its rendering. This means any Vulkan
+ draw calls that are made as a response to this signal, will stack under the
+ Qt Quick items. There are two signals because the custom Vulkan commands
+ are recorded onto the same command buffer used by the scene graph.
+
+ The beforeRendering() function on its own is not sufficient for this, because
+ it gets emitted at the start of the frame, before recording the start of a
+ \c renderpass instance by using
+ \l{https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBeginRenderPass.html}{vkCmdBeginRenderPass}.
+
+ The solution: by connecting to beforeRenderPassRecording(), the application's
+ own commands and the scene graph's scaffolding will end up in the right order.
+
+ Connecting the signals is done by the \c sync() function:
+
+ \quotefromfile scenegraph/vulkanunderqml/vulkansquircle.cpp
+ \skipto void VulkanSquircle::sync()
+ \printto m_renderer->setWindow(window());
+
+ Another way you can render Vulkan content on top of the Qt Quick scene is by
+ connecting to the \l QQuickWindow::afterRendering() and
+ \l QQuickWindow::afterRenderPassRecording() signals.
+
*/
diff --git a/examples/quick/shadereffects/CMakeLists.txt b/examples/quick/shadereffects/CMakeLists.txt
index 3441dfaa1c..b86a3fa5d7 100644
--- a/examples/quick/shadereffects/CMakeLists.txt
+++ b/examples/quick/shadereffects/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(shadereffects LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/shadereffects")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(shadereffectsexample
WIN32
@@ -21,15 +15,14 @@ qt_add_executable(shadereffectsexample
)
target_link_libraries(shadereffectsexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(shadereffectsexample
URI shadereffects
- AUTO_RESOURCE_PREFIX
QML_FILES
"shadereffects.qml"
RESOURCES
@@ -53,7 +46,16 @@ qt6_add_shaders(shadereffectsexample "shaders"
)
install(TARGETS shadereffectsexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET shadereffectsexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/shadereffects/content/shaders/blur.frag b/examples/quick/shadereffects/content/shaders/blur.frag
index 0c914d4244..b73a213348 100644
--- a/examples/quick/shadereffects/content/shaders/blur.frag
+++ b/examples/quick/shadereffects/content/shaders/blur.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
diff --git a/examples/quick/shadereffects/content/shaders/colorize.frag b/examples/quick/shadereffects/content/shaders/colorize.frag
index bc8067cc8c..eb50da9344 100644
--- a/examples/quick/shadereffects/content/shaders/colorize.frag
+++ b/examples/quick/shadereffects/content/shaders/colorize.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
diff --git a/examples/quick/shadereffects/content/shaders/outline.frag b/examples/quick/shadereffects/content/shaders/outline.frag
index 26df69c5fe..11e9b74af6 100644
--- a/examples/quick/shadereffects/content/shaders/outline.frag
+++ b/examples/quick/shadereffects/content/shaders/outline.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
diff --git a/examples/quick/shadereffects/content/shaders/shadow.frag b/examples/quick/shadereffects/content/shaders/shadow.frag
index 8247517b6d..f42fa6c0b0 100644
--- a/examples/quick/shadereffects/content/shaders/shadow.frag
+++ b/examples/quick/shadereffects/content/shaders/shadow.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
diff --git a/examples/quick/shadereffects/content/shaders/wobble.frag b/examples/quick/shadereffects/content/shaders/wobble.frag
index a34481c2f2..a97f9d17f5 100644
--- a/examples/quick/shadereffects/content/shaders/wobble.frag
+++ b/examples/quick/shadereffects/content/shaders/wobble.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
diff --git a/examples/quick/shadereffects/doc/src/shadereffects.qdoc b/examples/quick/shadereffects/doc/src/shadereffects.qdoc
index b846989cdd..134a14c618 100644
--- a/examples/quick/shadereffects/doc/src/shadereffects.qdoc
+++ b/examples/quick/shadereffects/doc/src/shadereffects.qdoc
@@ -6,6 +6,7 @@
\image qml-shadereffects-example.png
\brief A Qt Quick example demonstrating the use of shader effects.
\ingroup qtquickexamples
+ \examplecategory {Graphics}
This example demonstrates a couple of visual effects that you can perform
with shaders in Qt Quick. It applies five different effects on a text and
diff --git a/examples/quick/shadereffects/shadereffects.qml b/examples/quick/shadereffects/shadereffects.qml
index 3176ed2b34..daf3927246 100644
--- a/examples/quick/shadereffects/shadereffects.qml
+++ b/examples/quick/shadereffects/shadereffects.qml
@@ -78,7 +78,7 @@ Rectangle {
property variant source: theSource
property real amplitude: 0.04 * wobbleSlider.value
property real frequency: 20
- property real time: 0
+ property real time
NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 }
//! [fragment]
fragmentShader: "content/shaders/wobble.frag.qsb"
@@ -114,7 +114,7 @@ Rectangle {
fragmentShader: "content/shaders/blur.frag.qsb"
}
}
- property real angle: 0
+ property real angle
property variant offset: Qt.point(15.0 * Math.cos(angle), 15.0 * Math.sin(angle))
NumberAnimation on angle { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 6000 }
property variant delta: Qt.size(offset.x / width, offset.y / height)
@@ -158,8 +158,8 @@ Rectangle {
height: 160
//! [properties]
property variant source: theSource
- property real bend: 0
- property real minimize: 0
+ property real bend
+ property real minimize
property real side: genieSlider.value
SequentialAnimation on bend {
loops: Animation.Infinite
diff --git a/examples/quick/shapes/CMakeLists.txt b/examples/quick/shapes/CMakeLists.txt
deleted file mode 100644
index 40fd459bf0..0000000000
--- a/examples/quick/shapes/CMakeLists.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(shapes LANGUAGES CXX)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/shapes")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_standard_project_setup()
-
-add_subdirectory("../shared" "shared")
-
-qt_add_executable(shapesexample
- WIN32
- MACOSX_BUNDLE
- main.cpp
-)
-
-target_link_libraries(shapesexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-add_dependencies(shapesexample shapes_shared)
-
-qt_add_qml_module(shapesexample
- URI shapes
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "clippedtigers.qml"
- "interactive.qml"
- "cubicCurve.qml"
- "ellipticalArcs.qml"
- "gradientSpreadModes.qml"
- "arcDirection.qml"
- "largeOrSmallArc.qml"
- "arcRotation.qml"
- "tigerLoader.qml"
- "text.qml"
- "strokeOrFill.qml"
- "dashPattern.qml"
- "linearGradient.qml"
- "radialGradient.qml"
- "fillRules.qml"
- "joinStyles.qml"
- "capStyles.qml"
- "quadraticCurve.qml"
- "main.qml"
- "sampling.qml"
- "shapegallery.qml"
- "tapableTriangle.qml"
- "tiger.qml"
-)
-
-install(TARGETS shapesexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
-
-bundle_shared(shapesexample)
diff --git a/examples/quick/shapes/arcDirection.qml b/examples/quick/shapes/arcDirection.qml
deleted file mode 100644
index 7f457719ac..0000000000
--- a/examples/quick/shapes/arcDirection.qml
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
-
- Rectangle {
- width: 100
- height: 100
- anchors.centerIn: parent
- border.color: "gray"
-
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 4; startY: 4
- PathArc {
- id: arc
- x: 96; y: 96
- radiusX: 100; radiusY: 100
- direction: model.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise
- }
- }
- }
- }
- }
-
- Column {
- anchors.right: parent.right
- Text {
- text: "Clockwise (sweep 1)"
- color: "red"
- }
- Text {
- text: "Counter clockwise (sweep 0)"
- color: "blue"
- }
- }
-}
diff --git a/examples/quick/shapes/arcRotation.qml b/examples/quick/shapes/arcRotation.qml
deleted file mode 100644
index 178c3cda0d..0000000000
--- a/examples/quick/shapes/arcRotation.qml
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
-
- Repeater {
- model: 2
- Shape {
- width: 200
- height: 200
- anchors.centerIn: parent
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 50; startY: 100
- PathArc {
- x: 150; y: 100
- radiusX: 50; radiusY: 20
- xAxisRotation: model.index === 0 ? 0 : 45
- }
- }
- }
- }
-
- Repeater {
- model: 2
- Shape {
- width: 200
- height: 200
- anchors.centerIn: parent
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: model.index === 0 ? "red" : "blue"
-
- startX: 50; startY: 100
- PathArc {
- x: 150; y: 100
- radiusX: 50; radiusY: 20
- xAxisRotation: model.index === 0 ? 0 : 45
- direction: PathArc.Counterclockwise
- }
- }
- }
- }
-
- Column {
- anchors.right: parent.right
- Text {
- text: "0 degrees"
- color: "red"
- }
- Text {
- text: "45 degrees"
- color: "blue"
- }
- }
-}
diff --git a/examples/quick/shapes/capStyles.qml b/examples/quick/shapes/capStyles.qml
deleted file mode 100644
index 35a048c3d1..0000000000
--- a/examples/quick/shapes/capStyles.qml
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256; height: 256
-
- Shape {
- anchors.centerIn: parent
- width: 200
- height: 100
-
- ShapePath {
- id: capTest
- strokeColor: "green"
- strokeWidth: 20
- fillColor: "transparent"
-
- property int capStyleIdx: 0
- property variant styles: [ ShapePath.FlatCap, ShapePath.SquareCap, ShapePath.RoundCap ]
- property variant styleTexts: [ "FlatCap", "SquareCap", "RoundCap" ]
-
- capStyle: styles[capStyleIdx]
-
- startX: 40; startY: 30
- PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
- PathLine { x: 150; y: 80 }
- PathQuad { x: 160; y: 30; controlX: 200; controlY: 80 }
- }
- }
-
- Timer {
- interval: 1000
- repeat: true
- running: true
- onTriggered: capTest.capStyleIdx = (capTest.capStyleIdx + 1) % capTest.styles.length
- }
-
- Text {
- id: txt
- anchors.right: parent.right
- text: capTest.styleTexts[capTest.capStyleIdx]
- }
-}
diff --git a/examples/quick/shapes/clippedtigers.qml b/examples/quick/shapes/clippedtigers.qml
deleted file mode 100644
index 6ec170fa24..0000000000
--- a/examples/quick/shapes/clippedtigers.qml
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- id: root
- width: 1024
- height: 768
-
- property color col: "lightsteelblue"
- gradient: Gradient {
- GradientStop { position: 0.0; color: Qt.tint(root.col, "#20FFFFFF") }
- GradientStop { position: 0.1; color: Qt.tint(root.col, "#20AAAAAA") }
- GradientStop { position: 0.9; color: Qt.tint(root.col, "#20666666") }
- GradientStop { position: 1.0; color: Qt.tint(root.col, "#20000000") }
- }
-
- Rectangle {
- id: scissorRect
- width: 200
- height: 200
- x: 150
- property real centerY: parent.height / 2 - height / 2
- property real dy: 0
- y: centerY + dy
- clip: true
-
- Loader {
- id: loader1
- width: parent.width
- height: parent.height
- y: 25 - scissorRect.dy
- source: "tiger.qml"
- asynchronous: true
- visible: status === Loader.Ready
- }
-
- SequentialAnimation on dy {
- loops: Animation.Infinite
- running: loader1.status === Loader.Ready && loader1.item.status === Shape.Ready
- NumberAnimation {
- from: 0
- to: -scissorRect.centerY
- duration: 2000
- }
- NumberAnimation {
- from: -scissorRect.centerY
- to: scissorRect.centerY
- duration: 4000
- }
- NumberAnimation {
- from: scissorRect.centerY
- to: 0
- duration: 2000
- }
- }
- }
-
- // With a more complex transformation (like rotation), stenciling is used
- // instead of scissoring, this is more expensive. It may also trigger a
- // slower code path for Shapes, depending on the path rendering backend
- // in use, and may affect rendering quality as well.
- Rectangle {
- id: stencilRect
- width: 300
- height: 200
- anchors.right: parent.right
- anchors.rightMargin: 100
- anchors.verticalCenter: parent.verticalCenter
- clip: true // NB! still clips to bounding rect (not shape)
-
- Loader {
- id: loader2
- width: parent.width
- height: parent.height
- source: "tiger.qml"
- asynchronous: true
- visible: status === Loader.Ready
- }
-
- NumberAnimation on rotation {
- from: 0
- to: 360
- duration: 5000
- loops: Animation.Infinite
- }
- }
-}
diff --git a/examples/quick/shapes/cubicCurve.qml b/examples/quick/shapes/cubicCurve.qml
deleted file mode 100644
index 9c35a869bf..0000000000
--- a/examples/quick/shapes/cubicCurve.qml
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
-
- Item {
- width: 200
- height: 200
- anchors.centerIn: parent
-
- Shape {
- id: shape
- anchors.fill: parent
-
- ShapePath {
- strokeWidth: 4
- strokeColor: "black"
- fillGradient: ConicalGradient {
- id: conGrad
- centerX: 100; centerY: 75
- NumberAnimation on angle { from: 0; to: 360; duration: 10000; loops: Animation.Infinite }
- GradientStop { position: 0; color: "#00000000" }
- GradientStop { position: 0.10; color: "#ffe0cc73" }
- GradientStop { position: 0.17; color: "#ffc6a006" }
- GradientStop { position: 0.46; color: "#ff600659" }
- GradientStop { position: 0.72; color: "#ff0680ac" }
- GradientStop { position: 0.92; color: "#ffb9d9e6" }
- GradientStop { position: 1.00; color: "#00000000" }
- }
-
- startX: 50; startY: 100
- PathCubic {
- x: 150; y: 100
- control1X: cp1.x; control1Y: cp1.y
- control2X: cp2.x; control2Y: cp2.y
- }
- }
- }
-
- Rectangle {
- id: cp1
- color: "red"
- width: 10; height: 10
- SequentialAnimation {
- loops: Animation.Infinite
- running: true
- NumberAnimation {
- target: cp1
- property: "x"
- from: 0
- to: shape.width - cp1.width
- duration: 5000
- }
- NumberAnimation {
- target: cp1
- property: "x"
- from: shape.width - cp1.width
- to: 0
- duration: 5000
- }
- NumberAnimation {
- target: cp1
- property: "y"
- from: 0
- to: shape.height - cp1.height
- duration: 5000
- }
- NumberAnimation {
- target: cp1
- property: "y"
- from: shape.height - cp1.height
- to: 0
- duration: 5000
- }
- }
- }
-
- Rectangle {
- id: cp2
- color: "blue"
- width: 10; height: 10
- x: shape.width - width
- SequentialAnimation {
- loops: Animation.Infinite
- running: true
- NumberAnimation {
- target: cp2
- property: "y"
- from: 0
- to: shape.height - cp2.height
- duration: 5000
- }
- NumberAnimation {
- target: cp2
- property: "y"
- from: shape.height - cp2.height
- to: 0
- duration: 5000
- }
- NumberAnimation {
- target: cp2
- property: "x"
- from: shape.width - cp2.width
- to: 0
- duration: 5000
- }
- NumberAnimation {
- target: cp2
- property: "x"
- from: 0
- to: shape.width - cp2.width
- duration: 5000
- }
- }
- }
- }
-
- Text {
- anchors.right: parent.right
- anchors.top: parent.top
- text: "Conical gradient angle: " + Math.round(conGrad.angle)
- }
-}
diff --git a/examples/quick/shapes/dashPattern.qml b/examples/quick/shapes/dashPattern.qml
deleted file mode 100644
index 06b50dea2b..0000000000
--- a/examples/quick/shapes/dashPattern.qml
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
- Shape {
- width: 200
- height: 150
- anchors.centerIn: parent
- ShapePath {
- strokeWidth: 4
- strokeColor: "red"
- fillGradient: RadialGradient {
- centerX: 100; centerY: 100; centerRadius: 100
- SequentialAnimation on focalRadius {
- loops: Animation.Infinite
- NumberAnimation { from: 1; to: 20; duration: 2000 }
- NumberAnimation { from: 20; to: 1; duration: 2000 }
- }
- SequentialAnimation on focalX {
- loops: Animation.Infinite
- NumberAnimation { from: 50; to: 150; duration: 3000 }
- NumberAnimation { from: 150; to: 50; duration: 3000 }
- }
- SequentialAnimation on focalY {
- loops: Animation.Infinite
- NumberAnimation { from: 50; to: 150; duration: 1000 }
- NumberAnimation { from: 150; to: 50; duration: 1000 }
- }
- GradientStop { position: 0; color: "#ffffff" }
- GradientStop { position: 0.11; color: "#f9ffa0" }
- GradientStop { position: 0.13; color: "#f9ff99" }
- GradientStop { position: 0.14; color: "#f3ff86" }
- GradientStop { position: 0.49; color: "#93b353" }
- GradientStop { position: 0.87; color: "#264619" }
- GradientStop { position: 0.96; color: "#0c1306" }
- GradientStop { position: 1; color: "#000000" }
- }
- fillColor: "blue" // ignored with the gradient set
- strokeStyle: ShapePath.DashLine
- dashPattern: [ 1, 4 ]
- startX: 20; startY: 20
- PathLine { x: 180; y: 130 }
- PathLine { x: 20; y: 130 }
- PathLine { x: 20; y: 20 }
- }
- }
-}
diff --git a/examples/quick/shapes/doc/src/shapes.qdoc b/examples/quick/shapes/doc/src/shapes.qdoc
deleted file mode 100644
index fd97661b6f..0000000000
--- a/examples/quick/shapes/doc/src/shapes.qdoc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-/*!
- \title Qt Quick Examples - Shapes
- \example shapes
- \image qml-shapes-example.png
- \brief A Qt Quick example demonstrating the use of shape items.
- \ingroup qtquickexamples
-
- This example demonstrates the usage of the \l Shape type in Qt Quick. Shapes
- allow efficiently rendering stroked and filled lines, curves, and arcs in Qt
- Quick scenes.
-
- \include examples-run.qdocinc
-*/
diff --git a/examples/quick/shapes/ellipticalArcs.qml b/examples/quick/shapes/ellipticalArcs.qml
deleted file mode 100644
index 024d5e76f6..0000000000
--- a/examples/quick/shapes/ellipticalArcs.qml
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
- Shape {
- id: shape
- width: 220
- height: 200
- anchors.centerIn: parent
-
- ShapePath {
- fillGradient: LinearGradient {
- y2: shape.height
- GradientStop { position: 0; color: "yellow" }
- GradientStop { position: 1; color: "green" }
- }
-
- startX: 10; startY: 100
- PathArc {
- relativeX: 50; y: 100
- radiusX: 25; radiusY: 25
- }
- PathArc {
- relativeX: 50; y: 100
- radiusX: 25; radiusY: 35
- }
- PathArc {
- relativeX: 50; y: 100
- radiusX: 25; radiusY: 60
- }
- PathArc {
- relativeX: 50; y: 100
- radiusX: 50; radiusY: 120
- }
- }
- }
-
- Shape {
- width: 120
- height: 130
- anchors.bottom: parent.bottom
- anchors.right: parent.right
-
- scale: 0.5
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "darkBlue"
- strokeWidth: 20
- capStyle: ShapePath.RoundCap
-
- PathAngleArc {
- centerX: 65; centerY: 95
- radiusX: 45; radiusY: 45
- startAngle: -180
- SequentialAnimation on sweepAngle {
- loops: Animation.Infinite
- NumberAnimation { to: 360; duration: 2000 }
- NumberAnimation { to: 0; duration: 2000 }
- }
- }
- }
- }
-}
diff --git a/examples/quick/shapes/fillRules.qml b/examples/quick/shapes/fillRules.qml
deleted file mode 100644
index ca7ff75330..0000000000
--- a/examples/quick/shapes/fillRules.qml
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
- Shape {
- width: 100
- height: 100
- anchors.centerIn: parent
- ShapePath {
- id: star
- strokeColor: "blue"
- fillColor: "magenta"
- strokeWidth: 2
- PathMove { x: 90; y: 50 }
- PathLine { x: 50 + 40 * Math.cos(0.8 * 1 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 1 * Math.PI) }
- PathLine { x: 50 + 40 * Math.cos(0.8 * 2 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 2 * Math.PI) }
- PathLine { x: 50 + 40 * Math.cos(0.8 * 3 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 3 * Math.PI) }
- PathLine { x: 50 + 40 * Math.cos(0.8 * 4 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 4 * Math.PI) }
- PathLine { x: 90; y: 50 }
- }
- NumberAnimation on rotation {
- from: 0
- to: 360
- duration: 5000
- loops: Animation.Infinite
- }
- }
- Timer {
- interval: 2000
- onTriggered: star.fillRule = (star.fillRule === ShapePath.OddEvenFill ? ShapePath.WindingFill : ShapePath.OddEvenFill)
- repeat: true
- running: true
- }
- Text {
- anchors.right: parent.right
- text: star.fillRule === ShapePath.OddEvenFill ? "OddEvenFill" : "WindingFill"
- }
-}
diff --git a/examples/quick/shapes/gradientSpreadModes.qml b/examples/quick/shapes/gradientSpreadModes.qml
deleted file mode 100644
index 75b9a5b3db..0000000000
--- a/examples/quick/shapes/gradientSpreadModes.qml
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
- Rectangle {
- border.color: "black"
- width: 200
- height: 200
- anchors.centerIn: parent
-
- Shape {
- anchors.fill: parent
-
- ShapePath {
- strokeColor: "transparent"
-
- fillGradient: LinearGradient {
- id: grad
- y1: 50; y2: 150
- GradientStop { position: 0; color: "black" }
- GradientStop { position: 1; color: "red" }
- }
-
- startX: 10; startY: 10
- PathLine { relativeX: 180; relativeY: 0 }
- PathLine { relativeX: 0; relativeY: 180 }
- PathLine { relativeX: -180; relativeY: 0 }
- PathLine { relativeX: 0; relativeY: -180 }
- }
- }
-
- Timer {
- id: spreadTimer
- interval: 3000
- running: true
- repeat: true
- property variant spreads: [ ShapeGradient.PadSpread, ShapeGradient.RepeatSpread, ShapeGradient.ReflectSpread ]
- property variant spreadTexts: [ "PadSpread", "RepeatSpread", "ReflectSpread" ]
- property int spreadIdx: 0
- onTriggered: { spreadIdx = (spreadIdx + 1) % spreads.length; grad.spread = spreads[spreadIdx] }
- }
-
-
- Shape {
- anchors.fill: parent
- ShapePath {
- strokeColor: "gray"
- strokeWidth: 2
- fillColor: "transparent"
- PathMove { x: 0; y: 50 }
- PathLine { relativeX: 200; relativeY: 0 }
- PathMove { x: 0; y: 150 }
- PathLine { relativeX: 200; relativeY: 0 }
- }
- }
- }
-
- Text {
- anchors.right: parent.right
- text: spreadTimer.spreadTexts[spreadTimer.spreadIdx]
- }
-}
diff --git a/examples/quick/shapes/interactive.qml b/examples/quick/shapes/interactive.qml
deleted file mode 100644
index f9e2359a91..0000000000
--- a/examples/quick/shapes/interactive.qml
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Shapes
-
-Rectangle {
- id: root
- width: 1024
- height: 768
- color: palette.window
-
- RowLayout {
- id: topRow
- x: 20
- y: 10
- spacing: 20
-
- ButtonGroup {
- id: toolButtons
- buttons: drawingTools.children
- }
-
- RowLayout {
- id: drawingTools
- ToolButton {
- text: qsTr("Line")
- checkable: true
- checked: true
-
- property Component shapeType: Component {
- ShapePath {
- id: lineShapePath
- strokeColor: root.palette.windowText
- strokeWidth: widthSlider.value
- fillColor: "transparent"
- PathLine {
- id: pathSegment
- x: lineShapePath.startX + 1
- y: lineShapePath.startY + 1
- }
- function finishCreation() {
- createStartEndHandles(this, pathSegment);
- }
- }
- }
- }
- ToolButton {
- text: qsTr("Quadratic")
- checkable: true
-
- property Component shapeType: Component {
- ShapePath {
- id: quadShapePath
- strokeColor: root.palette.windowText
- strokeWidth: widthSlider.value
- fillColor: fillSwitch.checked ? "green" : "transparent"
- PathQuad {
- id: pathSegment
- x: quadShapePath.startx + 1
- y: quadShapePath.startY + 1
- controlX: quadShapePath.startX + 50
- controlY: quadShapePath.startY + 50
- }
- function finishCreation() {
- createStartEndHandles(this, pathSegment);
- pointDragHandle.createObject(canvas, {
- idleColor: "blue",
- target: pathSegment, xprop: "controlX", yprop: "controlY"
- });
- }
- }
- }
- }
- ToolButton {
- text: qsTr("Cubic")
- checkable: true
-
- property Component shapeType: Component {
- ShapePath {
- id: cubicShapePath
- strokeColor: root.palette.windowText
- strokeWidth: widthSlider.value
- fillColor: fillSwitch.checked ? "green" : "transparent"
- PathCubic {
- id: pathSegment
- x: cubicShapePath.startX + 1
- y: cubicShapePath.startY + 1
- control1X: cubicShapePath.startX + 50;
- control1Y: cubicShapePath.startY + 50;
- control2X: cubicShapePath.startX + 150;
- control2Y: cubicShapePath.startY + 50;
- }
- function finishCreation() {
- createStartEndHandles(this, pathSegment);
- pointDragHandle.createObject(canvas, {
- idleColor: "blue",
- target: pathSegment, xprop: "control1X", yprop: "control1Y"
- });
- pointDragHandle.createObject(canvas, {
- idleColor: "lightBlue",
- target: pathSegment, xprop: "control2X", yprop: "control2Y"
- });
- }
- }
- }
- }
- }
-
- Label {
- text: qsTr("Width")
- }
- Slider {
- id: widthSlider
- from: 1
- to: 60
- value: 4
- }
-
- Switch {
- id: showHandlesSwitch
- text: qsTr("Handles")
- }
-
- Switch {
- id: fillSwitch
- text: qsTr("Fill")
- }
- }
-
- Component {
- id: pointDragHandle
-
- Rectangle {
- id: rect
- property variant target
- property string xprop
- property string yprop
- property color idleColor: "red"
-
- width: 20
- height: width
- visible: showHandlesSwitch.checked
- color: hh.hovered ? "yellow" : idleColor
- border.color: "grey"
-
- property real halfWidth: width / 2
- property bool complete: false
- Binding { target: rect.target; property: xprop; value: x + halfWidth; when: complete }
- Binding { target: rect.target; property: yprop; value: y + halfWidth; when: complete }
-
- DragHandler { }
-
- HoverHandler { id: hh }
-
- Component.onCompleted: {
- x = target[xprop] - halfWidth;
- y = target[yprop] - halfWidth;
- complete = true;
- }
- }
- }
-
- function createStartEndHandles(shapePath, pathSegment) {
- pointDragHandle.createObject(canvas, {
- idleColor: "red",
- target: shapePath, xprop: "startX", yprop: "startY"
- });
- pointDragHandle.createObject(canvas, {
- idleColor: "red",
- target: pathSegment, xprop: "x", yprop: "y"
- });
- }
-
- Rectangle {
- id: canvas
- color: palette.base
- width: root.width - 40
- height: root.height - y - 20
- x: 20
- anchors.top: topRow.bottom
- anchors.topMargin: 20
-
- DragHandler {
- target: null
- grabPermissions: DragHandler.TakeOverForbidden
- property ShapePath activePath: null
- onActiveChanged: {
- const tool = toolButtons.checkedButton;
- if (active) {
- activePath = tool.shapeType.createObject(root, {
- startX: centroid.position.x, startY: centroid.position.y
- });
- shape.data.push(activePath);
- } else {
- activePath.finishCreation();
- activePath = null;
- }
- }
- onCentroidChanged: if (activePath) {
- var pathObj = activePath.pathElements[0];
- pathObj.x = centroid.position.x;
- pathObj.y = centroid.position.y;
- }
- }
-
- Shape {
- id: shape
- anchors.fill: parent
- }
- }
-}
diff --git a/examples/quick/shapes/joinStyles.qml b/examples/quick/shapes/joinStyles.qml
deleted file mode 100644
index 4d6410f059..0000000000
--- a/examples/quick/shapes/joinStyles.qml
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
-
- Shape {
- width: 120
- height: 120
- anchors.centerIn: parent
-
- ShapePath {
- id: joinTest
-
- strokeColor: "black"
- strokeWidth: 16
- fillColor: "transparent"
- capStyle: ShapePath.RoundCap
-
- property int joinStyleIdx: 0
- property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ]
- property variant styleTexts: [ "BevelJoin", "MiterJoin", "RoundJoin" ]
-
- joinStyle: styles[joinStyleIdx]
-
- startX: 30
- startY: 30
- PathLine { x: 100; y: 100 }
- PathLine { x: 30; y: 100 }
- }
- }
-
- Timer {
- interval: 1000
- repeat: true
- running: true
- onTriggered: joinTest.joinStyleIdx = (joinTest.joinStyleIdx + 1) % joinTest.styles.length
- }
-
- Text {
- id: txt
- anchors.right: parent.right
- text: joinTest.styleTexts[joinTest.joinStyleIdx]
- }
-}
diff --git a/examples/quick/shapes/largeOrSmallArc.qml b/examples/quick/shapes/largeOrSmallArc.qml
deleted file mode 100644
index 0ac934ca1c..0000000000
--- a/examples/quick/shapes/largeOrSmallArc.qml
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
-
- Repeater {
- model: 2
- Shape {
- width: 200
- height: 200
- anchors.centerIn: parent
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 50; startY: 100
- PathArc {
- x: 100; y: 150
- radiusX: 50; radiusY: 50
- useLargeArc: model.index === 1
- }
- }
- }
- }
-
- Column {
- anchors.right: parent.right
- Text {
- text: "Small"
- color: "red"
- }
- Text {
- text: "Large"
- color: "blue"
- }
- }
-}
diff --git a/examples/quick/shapes/linearGradient.qml b/examples/quick/shapes/linearGradient.qml
deleted file mode 100644
index 8ab6d7407d..0000000000
--- a/examples/quick/shapes/linearGradient.qml
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
- Shape {
- id: shape
- anchors.fill: parent
-
- ShapePath {
- id: p
- strokeWidth: 5
- strokeColor: "blue"
- strokeStyle: ShapePath.DashLine
- dashPattern: [ 1, 4, 4, 4 ]
- fillColor: "lightBlue"
-
- property real xr: 70
- property real yr: 30
- startX: shape.width / 2 - xr
- startY: shape.height / 2 - yr
- PathArc {
- x: shape.width / 2 + p.xr
- y: shape.height / 2 + p.yr
- radiusX: p.xr; radiusY: p.yr
- useLargeArc: true
- }
- PathArc {
- x: shape.width / 2 - p.xr
- y: shape.height / 2 - p.yr
- radiusX: p.xr; radiusY: p.yr
- useLargeArc: true
- }
- }
- }
-}
diff --git a/examples/quick/shapes/main.cpp b/examples/quick/shapes/main.cpp
deleted file mode 100644
index 1679876d37..0000000000
--- a/examples/quick/shapes/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(shapes/main)
diff --git a/examples/quick/shapes/main.qml b/examples/quick/shapes/main.qml
deleted file mode 100644
index c39aa19ed3..0000000000
--- a/examples/quick/shapes/main.qml
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import shared
-
-Item {
- width: 1280
- height: 720
- LauncherList {
- anchors.fill: parent
- Component.onCompleted: {
- addExample("Shape Gallery", "Simple path rendering examples", Qt.resolvedUrl("shapegallery.qml"))
- addExample("Interactive Shape", "Dynamic, interactive path rendering examples", Qt.resolvedUrl("interactive.qml"))
- addExample("Super- and multisampling", "Improving quality", Qt.resolvedUrl("sampling.qml"))
- addExample("Clip My Tiger!", "Clip examples, a.k.a. What Not To Do", Qt.resolvedUrl("clippedtigers.qml"))
- }
- }
-}
diff --git a/examples/quick/shapes/quadraticCurve.qml b/examples/quick/shapes/quadraticCurve.qml
deleted file mode 100644
index b20bd73394..0000000000
--- a/examples/quick/shapes/quadraticCurve.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
-
- Item {
- width: 200
- height: 100
- anchors.centerIn: parent
-
- Shape {
- id: shape
- anchors.fill: parent
-
- ShapePath {
- strokeWidth: 4
- strokeColor: "black"
- fillColor: "transparent"
-
- startX: 50
- startY: 50
- PathQuad {
- x: 150; y: 50
- controlX: cp.x; controlY: cp.y
- }
- }
- }
-
- Rectangle {
- id: cp
- color: "red"
- width: 10
- height: 10
- SequentialAnimation on x {
- loops: Animation.Infinite
- NumberAnimation {
- from: 0
- to: shape.width - cp.width
- duration: 5000
- }
- NumberAnimation {
- from: shape.width - cp.width
- to: 0
- duration: 5000
- }
- }
- }
- }
-}
diff --git a/examples/quick/shapes/radialGradient.qml b/examples/quick/shapes/radialGradient.qml
deleted file mode 100644
index 5fd3670b28..0000000000
--- a/examples/quick/shapes/radialGradient.qml
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
- Shape {
- width: 200
- height: 150
- anchors.centerIn: parent
- ShapePath {
- strokeWidth: 4
- strokeColor: "red"
- fillGradient: LinearGradient {
- x1: 20; y1: 20
- x2: 180; y2: 130
- GradientStop { position: 0; color: "blue" }
- GradientStop { position: 0.2; color: "green" }
- GradientStop { position: 0.4; color: "red" }
- GradientStop { position: 0.6; color: "yellow" }
- GradientStop { position: 1; color: "cyan" }
- }
- fillColor: "blue" // ignored with the gradient set
- strokeStyle: ShapePath.DashLine
- dashPattern: [ 1, 4 ]
- startX: 20; startY: 20
- PathLine { x: 180; y: 130 }
- PathLine { x: 20; y: 130 }
- PathLine { x: 20; y: 20 }
- }
- transform: Rotation { origin.x: 100; origin.y: 50; axis { x: 0; y: 1; z: 0 }
- SequentialAnimation on angle {
- NumberAnimation { from: 0; to: 75; duration: 2000 }
- NumberAnimation { from: 75; to: -75; duration: 4000 }
- NumberAnimation { from: -75; to: 0; duration: 2000 }
- loops: Animation.Infinite
- }
- }
- }
-}
diff --git a/examples/quick/shapes/sampling.qml b/examples/quick/shapes/sampling.qml
deleted file mode 100644
index e88aa380a2..0000000000
--- a/examples/quick/shapes/sampling.qml
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- id: root
- width: 1024
- height: 768
-
- property color col: "lightsteelblue"
- gradient: Gradient {
- GradientStop { position: 0.0; color: Qt.tint(root.col, "#20FFFFFF") }
- GradientStop { position: 0.1; color: Qt.tint(root.col, "#20AAAAAA") }
- GradientStop { position: 0.9; color: Qt.tint(root.col, "#20666666") }
- GradientStop { position: 1.0; color: Qt.tint(root.col, "#20000000") }
- }
-
- Row {
- anchors.fill: parent
- anchors.margins: 20
- spacing: 40
-
- Column {
- spacing: 40
-
- Text {
- text: "Original"
- }
-
- // A simple Shape without anything special.
- Rectangle {
- color: "lightGray"
- width: 400
- height: 200
-
- Shape {
- x: 30
- y: 20
- width: 50
- height: 50
- scale: 2
-
- ShapePath {
- strokeColor: "green"
- NumberAnimation on strokeWidth { from: 1; to: 20; duration: 5000 }
- fillColor: "transparent"
- capStyle: ShapePath.RoundCap
-
- startX: 40; startY: 30
- PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
- PathLine { x: 150; y: 80 }
- PathQuad { x: 160; y: 30; controlX: 200; controlY: 80 }
- }
- }
- }
-
- Text {
- text: "Supersampling (2x)"
- }
-
- // Now let's use 2x supersampling via layers. This way the entire subtree
- // is rendered into an FBO twice the size and then drawn with linear
- // filtering. This allows having some level of AA even when there is no
- // support for multisample framebuffers.
- Rectangle {
- id: supersampledItem
- color: "lightGray"
- width: 400
- height: 200
-
- layer.enabled: true
- layer.smooth: true
- layer.textureSize: Qt.size(supersampledItem.width * 2, supersampledItem.height * 2)
-
- Shape {
- x: 30
- y: 20
- width: 50
- height: 50
- scale: 2
-
- ShapePath {
- strokeColor: "green"
- NumberAnimation on strokeWidth { from: 1; to: 20; duration: 5000 }
- fillColor: "transparent"
- capStyle: ShapePath.RoundCap
-
- startX: 40; startY: 30
- PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
- PathLine { x: 150; y: 80 }
- PathQuad { x: 160; y: 30; controlX: 200; controlY: 80 }
- }
- }
- }
- }
-
- Column {
- spacing: 40
-
- Text {
- text: "Multisampling (4x)"
- }
-
- // Now let's use 4x MSAA, again via layers. This needs support for
- // multisample renderbuffers and framebuffer blits.
- Rectangle {
- color: "lightGray"
- width: 400
- height: 200
-
- layer.enabled: true
- layer.smooth: true
- layer.samples: 4
-
- Shape {
- x: 30
- y: 20
- width: 50
- height: 50
- scale: 2
-
- ShapePath {
- strokeColor: "green"
- NumberAnimation on strokeWidth { from: 1; to: 20; duration: 5000 }
- fillColor: "transparent"
- capStyle: ShapePath.RoundCap
-
- startX: 40; startY: 30
- PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
- PathLine { x: 150; y: 80 }
- PathQuad { x: 160; y: 30; controlX: 200; controlY: 80 }
- }
- }
- }
- }
- }
-}
diff --git a/examples/quick/shapes/shapegallery.qml b/examples/quick/shapes/shapegallery.qml
deleted file mode 100644
index dec1b4a5c5..0000000000
--- a/examples/quick/shapes/shapegallery.qml
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- id: root
- width: 1024
- height: 768
-
- property color col: "lightsteelblue"
- gradient: Gradient {
- GradientStop { position: 0.0; color: Qt.tint(root.col, "#20FFFFFF") }
- GradientStop { position: 0.1; color: Qt.tint(root.col, "#20AAAAAA") }
- GradientStop { position: 0.9; color: Qt.tint(root.col, "#20666666") }
- GradientStop { position: 1.0; color: Qt.tint(root.col, "#20000000") }
- }
-
- ListModel {
- id: pathGalleryModel
- ListElement {
- name: "Stroke and fill"
- shapeUrl: "tapableTriangle.qml"
- }
- ListElement {
- name: "Stroke or fill only"
- shapeUrl: "strokeOrFill.qml"
- }
- ListElement {
- name: "Dash pattern"
- shapeUrl: "linearGradient.qml"
- }
- ListElement {
- name: "Linear gradient"
- shapeUrl: "radialGradient.qml"
- }
- ListElement {
- name: "Radial gradient"
- shapeUrl: "dashPattern.qml"
- }
- ListElement {
- name: "Fill rules"
- shapeUrl: "fillRules.qml"
- }
- ListElement {
- name: "Join styles"
- shapeUrl: "joinStyles.qml"
- }
- ListElement {
- name: "Cap styles"
- shapeUrl: "capStyles.qml"
- }
- ListElement {
- name: "Quadratic curve"
- shapeUrl: "quadraticCurve.qml"
- }
- ListElement {
- name: "Cubic curve"
- shapeUrl: "cubicCurve.qml"
- }
- ListElement {
- name: "Elliptical arc"
- shapeUrl: "ellipticalArcs.qml"
- }
- ListElement {
- name: "Gradient spread modes"
- shapeUrl: "gradientSpreadModes.qml"
- }
- ListElement {
- name: "Arc direction"
- shapeUrl: "arcDirection.qml"
- }
- ListElement {
- name: "Large/small arc"
- shapeUrl: "largeOrSmallArc.qml"
- }
- ListElement {
- name: "Arc rotation"
- shapeUrl: "arcRotation.qml"
- }
- ListElement {
- name: "Tiger"
- shapeUrl: "tigerLoader.qml"
- }
- ListElement {
- name: "Text"
- shapeUrl: "text.qml"
- }
- }
-
- property int gridSpacing: 10
-
- Component {
- id: pathGalleryDelegate
- Rectangle {
- border.color: "purple"
- width: grid.cellWidth - root.gridSpacing
- height: grid.cellHeight - root.gridSpacing
- Column {
- anchors.fill: parent
- anchors.margins: 4
- Item {
- width: parent.width
- height: parent.height - delegText.height
- Loader {
- source: Qt.resolvedUrl(shapeUrl)
- anchors.fill: parent
- }
- }
- Text {
- id: delegText
- text: model.name
- font.pointSize: 16
- anchors.horizontalCenter: parent.horizontalCenter
- }
- }
- }
- }
-
- Rectangle {
- anchors.fill: parent
- anchors.margins: 10
- color: "lightBlue"
- clip: true
-
- GridView {
- id: grid
- anchors.fill: parent
- anchors.margins: root.gridSpacing
- cellWidth: 300
- cellHeight: 300
- delegate: pathGalleryDelegate
- model: pathGalleryModel
- }
- }
-
- Text {
- anchors.right: parent.right
- Shape { id: dummyShape; ShapePath { } } // used only to get the renderer type
- color: "darkBlue"
- font.pointSize: 12
- property variant rendererStrings: [ "Unknown", "Generic (QtGui triangulator)", "GL_NV_path_rendering", "Software (QPainter)" ]
- text: "Active Shape backend: " + rendererStrings[dummyShape.rendererType]
- SequentialAnimation on opacity {
- NumberAnimation { from: 1; to: 0; duration: 5000 }
- PauseAnimation { duration: 5000 }
- NumberAnimation { from: 0; to: 1; duration: 1000 }
- PauseAnimation { duration: 5000 }
- loops: Animation.Infinite
- }
- }
-}
diff --git a/examples/quick/shapes/shapes.pro b/examples/quick/shapes/shapes.pro
deleted file mode 100644
index eb2baae0b4..0000000000
--- a/examples/quick/shapes/shapes.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-RESOURCES += \
- shapes.qrc \
- ../shared/shared.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/shapes
-INSTALLS += target
diff --git a/examples/quick/shapes/shapes.qrc b/examples/quick/shapes/shapes.qrc
deleted file mode 100644
index 846984b301..0000000000
--- a/examples/quick/shapes/shapes.qrc
+++ /dev/null
@@ -1,27 +0,0 @@
-<RCC>
- <qresource prefix="/qt/qml/shapes">
- <file>main.qml</file>
- <file>shapegallery.qml</file>
- <file>interactive.qml</file>
- <file>sampling.qml</file>
- <file>clippedtigers.qml</file>
- <file>tiger.qml</file>
- <file>tapableTriangle.qml</file>
- <file>strokeOrFill.qml</file>
- <file>dashPattern.qml</file>
- <file>linearGradient.qml</file>
- <file>radialGradient.qml</file>
- <file>fillRules.qml</file>
- <file>joinStyles.qml</file>
- <file>capStyles.qml</file>
- <file>quadraticCurve.qml</file>
- <file>cubicCurve.qml</file>
- <file>ellipticalArcs.qml</file>
- <file>gradientSpreadModes.qml</file>
- <file>arcDirection.qml</file>
- <file>largeOrSmallArc.qml</file>
- <file>arcRotation.qml</file>
- <file>tigerLoader.qml</file>
- <file>text.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/quick/shapes/strokeOrFill.qml b/examples/quick/shapes/strokeOrFill.qml
deleted file mode 100644
index e5a4f350a4..0000000000
--- a/examples/quick/shapes/strokeOrFill.qml
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256; height: 256
-
- Shape {
- id: circ1
- anchors.fill: parent
-
- ShapePath {
- id: p1
- fillColor: "transparent" // stroke only
- strokeWidth: 4
-
- SequentialAnimation on strokeColor {
- loops: Animation.Infinite
- ColorAnimation {
- from: "black"
- to: "yellow"
- duration: 5000
- }
- ColorAnimation {
- from: "yellow"
- to: "green"
- duration: 5000
- }
- ColorAnimation {
- from: "green"
- to: "black"
- duration: 5000
- }
- }
-
- property real r: 60
- startX: circ1.width / 2 - r
- startY: circ1.height / 2 - r
- PathArc {
- x: circ1.width / 2 + p1.r
- y: circ1.height / 2 + p1.r
- radiusX: p1.r; radiusY: p1.r
- useLargeArc: true
- }
- PathArc {
- x: circ1.width / 2 - p1.r
- y: circ1.height / 2 - p1.r
- radiusX: p1.r; radiusY: p1.r
- useLargeArc: true
- }
- }
- }
-
- Shape {
- id: circ2
- anchors.fill: parent
-
- SequentialAnimation on opacity {
- loops: Animation.Infinite
- NumberAnimation { from: 1.0; to: 0.0; duration: 5000 }
- NumberAnimation { from: 0.0; to: 1.0; duration: 5000 }
- }
-
- ShapePath {
- id: p2
- strokeWidth: -1 // or strokeColor: "transparent"
-
- SequentialAnimation on fillColor {
- loops: Animation.Infinite
- ColorAnimation {
- from: "gray"
- to: "purple"
- duration: 3000
- }
- ColorAnimation {
- from: "purple"
- to: "red"
- duration: 3000
- }
- ColorAnimation {
- from: "red"
- to: "gray"
- duration: 3000
- }
- }
-
- property real r: 40
- startX: circ2.width / 2 - r
- startY: circ2.height / 2 - r
- PathArc {
- x: circ2.width / 2 + p2.r
- y: circ2.height / 2 + p2.r
- radiusX: p2.r; radiusY: p2.r
- useLargeArc: true
- }
- PathArc {
- x: circ2.width / 2 - p2.r
- y: circ2.height / 2 - p2.r
- radiusX: p2.r; radiusY: p2.r
- useLargeArc: true
- }
- }
- }
-}
diff --git a/examples/quick/shapes/tapableTriangle.qml b/examples/quick/shapes/tapableTriangle.qml
deleted file mode 100644
index a369293300..0000000000
--- a/examples/quick/shapes/tapableTriangle.qml
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- width: 256
- height: 256
- color: th.pressed ? "steelBlue" : "lightGray"
- containmentMask: ctr
-
- TapHandler { id: th }
-
- Shape {
- id: ctr
- anchors.fill: parent
- containsMode: Shape.FillContains
-
- ShapePath {
- strokeColor: "red"
- fillColor: "blue"
-
- SequentialAnimation on strokeWidth {
- loops: Animation.Infinite
- NumberAnimation { from: 1; to: 30; duration: 5000 }
- NumberAnimation { from: 30; to: 1; duration: 5000 }
- PauseAnimation { duration: 2000 }
- }
-
- startX: 30; startY: 30
- PathLine { x: ctr.width - 30; y: ctr.height - 30 }
- PathLine { x: 30; y: ctr.height - 30 }
- PathLine { x: 30; y: 30 }
- }
-
- // Besides ShapePath, Shape supports visual and non-visual objects too, allowing
- // free mixing without going through extra hoops:
- Rectangle {
- id: testRect
- color: "green"
- opacity: 0.3
- width: 20
- height: 20
- anchors.right: parent.right
- }
- Timer {
- interval: 100
- repeat: true
- onTriggered: testRect.width = testRect.width > 1 ? testRect.width - 1 : 20
- running: true
- }
- }
-}
diff --git a/examples/quick/shapes/text.qml b/examples/quick/shapes/text.qml
deleted file mode 100644
index 141bd8cd8d..0000000000
--- a/examples/quick/shapes/text.qml
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
-
- Shape {
- anchors.centerIn: parent
- width: 200
- height: 100
-
- ShapePath {
- id: capTest
- strokeColor: "black"
- strokeWidth: 1
- fillColor: "black"
-
- PathText { x: 0; y: 0; font.family: "Arial"; font.pixelSize: 150; text: "Qt!" }
- }
- }
-}
diff --git a/examples/quick/shapes/tiger.qml b/examples/quick/shapes/tiger.qml
deleted file mode 100644
index 148039aff1..0000000000
--- a/examples/quick/shapes/tiger.qml
+++ /dev/null
@@ -1,3667 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Shape {
- asynchronous: true
- width: 494; height: 510
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -122.304; y: 84.285 }
- PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 }
- PathCubic { control1X: -123.851; control1Y: 86.141; control2X: -140.305; control2Y: 38.066; x: -160.833; y: 40.309 }
- PathCubic { control1X: -160.833; control1Y: 40.309; control2X: -143.05; control2Y: 32.956; x: -122.304; y: 84.285 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -118.774; y: 81.262 }
- PathCubic { control1X: -118.774; control1Y: 81.262; control2X: -119.323; control2Y: 83.078; x: -120.092; y: 82.779 }
- PathCubic { control1X: -120.86; control1Y: 82.481; control2X: -119.977; control2Y: 31.675; x: -140.043; y: 26.801 }
- PathCubic { control1X: -140.043; control1Y: 26.801; control2X: -120.82; control2Y: 25.937; x: -118.774; y: 81.262 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -91.284; y: 123.59 }
- PathCubic { control1X: -91.284; control1Y: 123.59; control2X: -89.648; control2Y: 124.55; x: -90.118; y: 125.227 }
- PathCubic { control1X: -90.589; control1Y: 125.904; control2X: -139.763; control2Y: 113.102; x: -149.218; y: 131.459 }
- PathCubic { control1X: -149.218; control1Y: 131.459; control2X: -145.539; control2Y: 112.572; x: -91.284; y: 123.59 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -94.093; y: 133.801 }
- PathCubic { control1X: -94.093; control1Y: 133.801; control2X: -92.237; control2Y: 134.197; x: -92.471; y: 134.988 }
- PathCubic { control1X: -92.704; control1Y: 135.779; control2X: -143.407; control2Y: 139.121; x: -146.597; y: 159.522 }
- PathCubic { control1X: -146.597; control1Y: 159.522; control2X: -149.055; control2Y: 140.437; x: -94.093; y: 133.801 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -98.304; y: 128.276 }
- PathCubic { control1X: -98.304; control1Y: 128.276; control2X: -96.526; control2Y: 128.939; x: -96.872; y: 129.687 }
- PathCubic { control1X: -97.218; control1Y: 130.435; control2X: -147.866; control2Y: 126.346; x: -153.998; y: 146.064 }
- PathCubic { control1X: -153.998; control1Y: 146.064; control2X: -153.646; control2Y: 126.825; x: -98.304; y: 128.276 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -109.009; y: 110.072 }
- PathCubic { control1X: -109.009; control1Y: 110.072; control2X: -107.701; control2Y: 111.446; x: -108.34; y: 111.967 }
- PathCubic { control1X: -108.979; control1Y: 112.488; control2X: -152.722; control2Y: 86.634; x: -166.869; y: 101.676 }
- PathCubic { control1X: -166.869; control1Y: 101.676; control2X: -158.128; control2Y: 84.533; x: -109.009; y: 110.072 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -116.554; y: 114.263 }
- PathCubic { control1X: -116.554; control1Y: 114.263; control2X: -115.098; control2Y: 115.48; x: -115.674; y: 116.071 }
- PathCubic { control1X: -116.25; control1Y: 116.661; control2X: -162.638; control2Y: 95.922; x: -174.992; y: 112.469 }
- PathCubic { control1X: -174.992; control1Y: 112.469; control2X: -168.247; control2Y: 94.447; x: -116.554; y: 114.263 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -119.154; y: 118.335 }
- PathCubic { control1X: -119.154; control1Y: 118.335; control2X: -117.546; control2Y: 119.343; x: -118.036; y: 120.006 }
- PathCubic { control1X: -118.526; control1Y: 120.669; control2X: -167.308; control2Y: 106.446; x: -177.291; y: 124.522 }
- PathCubic { control1X: -177.291; control1Y: 124.522; control2X: -173.066; control2Y: 105.749; x: -119.154; y: 118.335 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -108.42; y: 118.949 }
- PathCubic { control1X: -108.42; control1Y: 118.949; control2X: -107.298; control2Y: 120.48; x: -107.999; y: 120.915 }
- PathCubic { control1X: -108.7; control1Y: 121.35; control2X: -148.769; control2Y: 90.102; x: -164.727; y: 103.207 }
- PathCubic { control1X: -164.727; control1Y: 103.207; control2X: -153.862; control2Y: 87.326; x: -108.42; y: 118.949 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -128.2; y: 90 }
- PathCubic { control1X: -128.2; control1Y: 90; control2X: -127.6; control2Y: 91.8; x: -128.4; y: 92 }
- PathCubic { control1X: -129.2; control1Y: 92.2; control2X: -157.8; control2Y: 50.2; x: -177.001; y: 57.8 }
- PathCubic { control1X: -177.001; control1Y: 57.8; control2X: -161.8; control2Y: 46; x: -128.2; y: 90 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -127.505; y: 96.979 }
- PathCubic { control1X: -127.505; control1Y: 96.979; control2X: -126.53; control2Y: 98.608; x: -127.269; y: 98.975 }
- PathCubic { control1X: -128.007; control1Y: 99.343; control2X: -164.992; control2Y: 64.499; x: -182.101; y: 76.061 }
- PathCubic { control1X: -182.101; control1Y: 76.061; control2X: -169.804; control2Y: 61.261; x: -127.505; y: 96.979 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.172
- PathMove { x: -127.62; y: 101.349 }
- PathCubic { control1X: -127.62; control1Y: 101.349; control2X: -126.498; control2Y: 102.88; x: -127.199; y: 103.315 }
- PathCubic { control1X: -127.9; control1Y: 103.749; control2X: -167.969; control2Y: 72.502; x: -183.927; y: 85.607 }
- PathCubic { control1X: -183.927; control1Y: 85.607; control2X: -173.062; control2Y: 69.726; x: -127.62; y: 101.349 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 1
- PathMove { x: -129.83; y: 103.065 }
- PathCubic { control1X: -129.327; control1Y: 109.113; control2X: -128.339; control2Y: 115.682; x: -126.6; y: 118.801 }
- PathCubic { control1X: -126.6; control1Y: 118.801; control2X: -130.2; control2Y: 131.201; x: -121.4; y: 144.401 }
- PathCubic { control1X: -121.4; control1Y: 144.401; control2X: -121.8; control2Y: 151.601; x: -120.2; y: 154.801 }
- PathCubic { control1X: -120.2; control1Y: 154.801; control2X: -116.2; control2Y: 163.201; x: -111.4; y: 164.001 }
- PathCubic { control1X: -107.516; control1Y: 164.648; control2X: -98.793; control2Y: 167.717; x: -88.932; y: 169.121 }
- PathCubic { control1X: -88.932; control1Y: 169.121; control2X: -71.8; control2Y: 183.201; x: -75; y: 196.001 }
- PathCubic { control1X: -75; control1Y: 196.001; control2X: -75.4; control2Y: 212.401; x: -79; y: 214.001 }
- PathCubic { control1X: -79; control1Y: 214.001; control2X: -67.4; control2Y: 202.801; x: -77; y: 219.601 }
- PathLine { x: -81.4; y: 238.401 }
- PathCubic { control1X: -81.4; control1Y: 238.401; control2X: -55.8; control2Y: 216.801; x: -71.4; y: 235.201 }
- PathLine { x: -81.4; y: 261.201 }
- PathCubic { control1X: -81.4; control1Y: 261.201; control2X: -61.8; control2Y: 242.801; x: -69; y: 251.201 }
- PathLine { x: -72.2; y: 260.001 }
- PathCubic { control1X: -72.2; control1Y: 260.001; control2X: -29; control2Y: 232.801; x: -59.8; y: 262.401 }
- PathCubic { control1X: -59.8; control1Y: 262.401; control2X: -51.8; control2Y: 258.801; x: -47.4; y: 261.601 }
- PathCubic { control1X: -47.4; control1Y: 261.601; control2X: -40.6; control2Y: 260.401; x: -41.4; y: 262.001 }
- PathCubic { control1X: -41.4; control1Y: 262.001; control2X: -62.2; control2Y: 272.401; x: -65.8; y: 290.801 }
- PathCubic { control1X: -65.8; control1Y: 290.801; control2X: -57.4; control2Y: 280.801; x: -60.6; y: 291.601 }
- PathLine { x: -60.2; y: 303.201 }
- PathCubic { control1X: -60.2; control1Y: 303.201; control2X: -56.2; control2Y: 281.601; x: -56.6; y: 319.201 }
- PathCubic { control1X: -56.6; control1Y: 319.201; control2X: -37.4; control2Y: 301.201; x: -49; y: 322.001 }
- PathLine { x: -49; y: 338.801 }
- PathCubic { control1X: -49; control1Y: 338.801; control2X: -33.8; control2Y: 322.401; x: -40.2; y: 335.201 }
- PathCubic { control1X: -40.2; control1Y: 335.201; control2X: -30.2; control2Y: 326.401; x: -34.2; y: 341.601 }
- PathCubic { control1X: -34.2; control1Y: 341.601; control2X: -35; control2Y: 352.001; x: -30.6; y: 340.801 }
- PathCubic { control1X: -30.6; control1Y: 340.801; control2X: -14.6; control2Y: 310.201; x: -20.6; y: 336.401 }
- PathCubic { control1X: -20.6; control1Y: 336.401; control2X: -21.4; control2Y: 355.601; x: -16.6; y: 340.801 }
- PathCubic { control1X: -16.6; control1Y: 340.801; control2X: -16.2; control2Y: 351.201; x: -7; y: 358.401 }
- PathCubic { control1X: -7; control1Y: 358.401; control2X: -8.2; control2Y: 307.601; x: 4.6; y: 343.601 }
- PathLine { x: 8.6; y: 360.001 }
- PathCubic { control1X: 8.6; control1Y: 360.001; control2X: 11.4; control2Y: 350.801; x: 11; y: 345.601 }
- PathCubic { control1X: 11; control1Y: 345.601; control2X: 25.8; control2Y: 329.201; x: 19; y: 353.601 }
- PathCubic { control1X: 19; control1Y: 353.601; control2X: 34.2; control2Y: 330.801; x: 31; y: 344.001 }
- PathCubic { control1X: 31; control1Y: 344.001; control2X: 23.4; control2Y: 360.001; x: 25; y: 364.801 }
- PathCubic { control1X: 25; control1Y: 364.801; control2X: 41.8; control2Y: 330.001; x: 43; y: 328.401 }
- PathCubic { control1X: 43; control1Y: 328.401; control2X: 41; control2Y: 370.802; x: 51.8; y: 334.801 }
- PathCubic { control1X: 51.8; control1Y: 334.801; control2X: 57.4; control2Y: 346.801; x: 54.6; y: 351.201 }
- PathCubic { control1X: 54.6; control1Y: 351.201; control2X: 62.6; control2Y: 343.201; x: 61.8; y: 340.001 }
- PathCubic { control1X: 61.8; control1Y: 340.001; control2X: 66.4; control2Y: 331.801; x: 69.2; y: 345.401 }
- PathCubic { control1X: 69.2; control1Y: 345.401; control2X: 71; control2Y: 354.801; x: 72.6; y: 351.601 }
- PathCubic { control1X: 72.6; control1Y: 351.601; control2X: 76.6; control2Y: 375.602; x: 77.8; y: 352.801 }
- PathCubic { control1X: 77.8; control1Y: 352.801; control2X: 79.4; control2Y: 339.201; x: 72.2; y: 327.601 }
- PathCubic { control1X: 72.2; control1Y: 327.601; control2X: 73; control2Y: 324.401; x: 70.2; y: 320.401 }
- PathCubic { control1X: 70.2; control1Y: 320.401; control2X: 83.8; control2Y: 342.001; x: 76.6; y: 313.201 }
- PathCubic { control1X: 76.6; control1Y: 313.201; control2X: 87.801; control2Y: 321.201; x: 89.001; y: 321.201 }
- PathCubic { control1X: 89.001; control1Y: 321.201; control2X: 75.4; control2Y: 298.001; x: 84.2; y: 302.801 }
- PathCubic { control1X: 84.2; control1Y: 302.801; control2X: 79; control2Y: 292.401; x: 97.001; y: 304.401 }
- PathCubic { control1X: 97.001; control1Y: 304.401; control2X: 81; control2Y: 288.401; x: 98.601; y: 298.001 }
- PathCubic { control1X: 98.601; control1Y: 298.001; control2X: 106.601; control2Y: 304.401; x: 99.001; y: 294.401 }
- PathCubic { control1X: 99.001; control1Y: 294.401; control2X: 84.6; control2Y: 278.401; x: 106.601; y: 296.401 }
- PathCubic { control1X: 106.601; control1Y: 296.401; control2X: 118.201; control2Y: 312.801; x: 119.001; y: 315.601 }
- PathCubic { control1X: 119.001; control1Y: 315.601; control2X: 109.001; control2Y: 286.401; x: 104.601; y: 283.601 }
- PathCubic { control1X: 104.601; control1Y: 283.601; control2X: 113.001; control2Y: 247.201; x: 154.201; y: 262.801 }
- PathCubic { control1X: 154.201; control1Y: 262.801; control2X: 161.001; control2Y: 280.001; x: 165.401; y: 261.601 }
- PathCubic { control1X: 165.401; control1Y: 261.601; control2X: 178.201; control2Y: 255.201; x: 189.401; y: 282.801 }
- PathCubic { control1X: 189.401; control1Y: 282.801; control2X: 193.401; control2Y: 269.201; x: 192.601; y: 266.401 }
- PathCubic { control1X: 192.601; control1Y: 266.401; control2X: 199.401; control2Y: 267.601; x: 198.601; y: 266.401 }
- PathCubic { control1X: 198.601; control1Y: 266.401; control2X: 211.801; control2Y: 270.801; x: 213.001; y: 270.001 }
- PathCubic { control1X: 213.001; control1Y: 270.001; control2X: 219.801; control2Y: 276.801; x: 220.201; y: 273.201 }
- PathCubic { control1X: 220.201; control1Y: 273.201; control2X: 229.401; control2Y: 276.001; x: 227.401; y: 272.401 }
- PathCubic { control1X: 227.401; control1Y: 272.401; control2X: 236.201; control2Y: 288.001; x: 236.601; y: 291.601 }
- PathLine { x: 239.001; y: 277.601 }
- PathLine { x: 241.001; y: 280.401 }
- PathCubic { control1X: 241.001; control1Y: 280.401; control2X: 242.601; control2Y: 272.801; x: 241.801; y: 271.601 }
- PathCubic { control1X: 241.001; control1Y: 270.401; control2X: 261.801; control2Y: 278.401; x: 266.601; y: 299.201 }
- PathLine { x: 268.601; y: 307.601 }
- PathCubic { control1X: 268.601; control1Y: 307.601; control2X: 274.601; control2Y: 292.801; x: 273.001; y: 288.801 }
- PathCubic { control1X: 273.001; control1Y: 288.801; control2X: 278.201; control2Y: 289.601; x: 278.601; y: 294.001 }
- PathCubic { control1X: 278.601; control1Y: 294.001; control2X: 282.601; control2Y: 270.801; x: 277.801; y: 264.801 }
- PathCubic { control1X: 277.801; control1Y: 264.801; control2X: 282.201; control2Y: 264.001; x: 283.401; y: 267.601 }
- PathLine { x: 283.401; y: 260.401 }
- PathCubic { control1X: 283.401; control1Y: 260.401; control2X: 290.601; control2Y: 261.201; x: 290.601; y: 258.801 }
- PathCubic { control1X: 290.601; control1Y: 258.801; control2X: 295.001; control2Y: 254.801; x: 297.001; y: 259.601 }
- PathCubic { control1X: 297.001; control1Y: 259.601; control2X: 284.601; control2Y: 224.401; x: 303.001; y: 243.601 }
- PathCubic { control1X: 303.001; control1Y: 243.601; control2X: 310.201; control2Y: 254.401; x: 306.601; y: 235.601 }
- PathCubic { control1X: 303.001; control1Y: 216.801; control2X: 299.001; control2Y: 215.201; x: 303.801; y: 214.801 }
- PathCubic { control1X: 303.801; control1Y: 214.801; control2X: 304.601; control2Y: 211.201; x: 302.601; y: 209.601 }
- PathCubic { control1X: 300.601; control1Y: 208.001; control2X: 303.801; control2Y: 209.601; x: 303.801; y: 209.601 }
- PathCubic { control1X: 303.801; control1Y: 209.601; control2X: 308.601; control2Y: 213.601; x: 303.401; y: 191.601 }
- PathCubic { control1X: 303.401; control1Y: 191.601; control2X: 309.801; control2Y: 193.201; x: 297.801; y: 164.001 }
- PathCubic { control1X: 297.801; control1Y: 164.001; control2X: 300.601; control2Y: 161.601; x: 296.601; y: 153.201 }
- PathCubic { control1X: 296.601; control1Y: 153.201; control2X: 304.601; control2Y: 157.601; x: 307.401; y: 156.001 }
- PathCubic { control1X: 307.401; control1Y: 156.001; control2X: 307.001; control2Y: 154.401; x: 303.801; y: 150.401 }
- PathCubic { control1X: 303.801; control1Y: 150.401; control2X: 282.201; control2Y: 95.6; x: 302.601; y: 117.601 }
- PathCubic { control1X: 302.601; control1Y: 117.601; control2X: 314.451; control2Y: 131.151; x: 308.051; y: 108.351 }
- PathCubic { control1X: 308.051; control1Y: 108.351; control2X: 298.94; control2Y: 84.341; x: 299.717; y: 80.045 }
- PathLine { x: -129.83; y: 103.065 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeColor: "#000000"
- strokeWidth: 1
- PathMove { x: 299.717; y: 80.245 }
- PathCubic { control1X: 300.345; control1Y: 80.426; control2X: 302.551; control2Y: 81.55; x: 303.801; y: 83.2 }
- PathCubic { control1X: 303.801; control1Y: 83.2; control2X: 310.601; control2Y: 94; x: 305.401; y: 75.6 }
- PathCubic { control1X: 305.401; control1Y: 75.6; control2X: 296.201; control2Y: 46.8; x: 305.001; y: 58 }
- PathCubic { control1X: 305.001; control1Y: 58; control2X: 311.001; control2Y: 65.2; x: 307.801; y: 51.6 }
- PathCubic { control1X: 303.936; control1Y: 35.173; control2X: 301.401; control2Y: 28.8; x: 301.401; y: 28.8 }
- PathCubic { control1X: 301.401; control1Y: 28.8; control2X: 313.001; control2Y: 33.6; x: 286.201; y: -6 }
- PathLine { x: 295.001; y: -2.4 }
- PathCubic { control1X: 295.001; control1Y: -2.4; control2X: 275.401; control2Y: -42; x: 253.801; y: -47.2 }
- PathLine { x: 245.801; y: -53.2 }
- PathCubic { control1X: 245.801; control1Y: -53.2; control2X: 284.201; control2Y: -91.2; x: 271.401; y: -128 }
- PathCubic { control1X: 271.401; control1Y: -128; control2X: 264.601; control2Y: -133.2; x: 255.001; y: -124 }
- PathCubic { control1X: 255.001; control1Y: -124; control2X: 248.601; control2Y: -119.2; x: 242.601; y: -120.8 }
- PathCubic { control1X: 242.601; control1Y: -120.8; control2X: 211.801; control2Y: -119.6; x: 209.801; y: -119.6 }
- PathCubic { control1X: 207.801; control1Y: -119.6; control2X: 173.001; control2Y: -156.8; x: 107.401; y: -139.2 }
- PathCubic { control1X: 107.401; control1Y: -139.2; control2X: 102.201; control2Y: -137.2; x: 97.801; y: -138.4 }
- PathCubic { control1X: 97.801; control1Y: -138.4; control2X: 79.4; control2Y: -154.4; x: 30.6; y: -131.6 }
- PathCubic { control1X: 30.6; control1Y: -131.6; control2X: 20.6; control2Y: -129.6; x: 19; y: -129.6 }
- PathCubic { control1X: 17.4; control1Y: -129.6; control2X: 14.6; control2Y: -129.6; x: 6.6; y: -123.2 }
- PathCubic { control1X: -1.4; control1Y: -116.8; control2X: -1.8; control2Y: -116; x: -3.8; y: -114.4 }
- PathCubic { control1X: -3.8; control1Y: -114.4; control2X: -20.2; control2Y: -103.2; x: -25; y: -102.4 }
- PathCubic { control1X: -25; control1Y: -102.4; control2X: -36.6; control2Y: -96; x: -41; y: -86 }
- PathLine { x: -44.6; y: -84.8 }
- PathCubic { control1X: -44.6; control1Y: -84.8; control2X: -46.2; control2Y: -77.6; x: -46.6; y: -76.4 }
- PathCubic { control1X: -46.6; control1Y: -76.4; control2X: -51.4; control2Y: -72.8; x: -52.2; y: -67.2 }
- PathCubic { control1X: -52.2; control1Y: -67.2; control2X: -61; control2Y: -61.2; x: -60.6; y: -56.8 }
- PathCubic { control1X: -60.6; control1Y: -56.8; control2X: -62.2; control2Y: -51.6; x: -63; y: -46.8 }
- PathCubic { control1X: -63; control1Y: -46.8; control2X: -70.2; control2Y: -42; x: -69.4; y: -39.2 }
- PathCubic { control1X: -69.4; control1Y: -39.2; control2X: -77; control2Y: -25.2; x: -75.8; y: -18.4 }
- PathCubic { control1X: -75.8; control1Y: -18.4; control2X: -82.2; control2Y: -18.8; x: -85; y: -16.4 }
- PathCubic { control1X: -85; control1Y: -16.4; control2X: -85.8; control2Y: -11.6; x: -87.4; y: -11.2 }
- PathCubic { control1X: -87.4; control1Y: -11.2; control2X: -90.2; control2Y: -10; x: -87.8; y: -6 }
- PathCubic { control1X: -87.8; control1Y: -6; control2X: -89.4; control2Y: -3.2; x: -89.8; y: -1.6 }
- PathCubic { control1X: -89.8; control1Y: -1.6; control2X: -89; control2Y: 1.2; x: -93.4; y: 6.8 }
- PathCubic { control1X: -93.4; control1Y: 6.8; control2X: -99.8; control2Y: 25.6; x: -97.8; y: 30.8 }
- PathCubic { control1X: -97.8; control1Y: 30.8; control2X: -97.4; control2Y: 35.6; x: -100.2; y: 37.2 }
- PathCubic { control1X: -100.2; control1Y: 37.2; control2X: -103.8; control2Y: 36.8; x: -95.4; y: 48.8 }
- PathCubic { control1X: -95.4; control1Y: 48.8; control2X: -94.6; control2Y: 50; x: -97.8; y: 52.4 }
- PathCubic { control1X: -97.8; control1Y: 52.4; control2X: -115; control2Y: 56; x: -117.4; y: 72.4 }
- PathCubic { control1X: -117.4; control1Y: 72.4; control2X: -131; control2Y: 87.2; x: -131; y: 92.4 }
- PathCubic { control1X: -131; control1Y: 94.705; control2X: -130.729; control2Y: 97.852; x: -130.03; y: 102.465 }
- PathCubic { control1X: -130.03; control1Y: 102.465; control2X: -130.6; control2Y: 110.801; x: -103; y: 111.601 }
- PathCubic { control1X: -75.4; control1Y: 112.401; control2X: 299.717; control2Y: 80.245; x: 299.717; y: 80.245 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: -115.6; y: 102.6 }
- PathCubic { control1X: -140.6; control1Y: 63.2; control2X: -126.2; control2Y: 119.601; x: -126.2; y: 119.601 }
- PathCubic { control1X: -117.4; control1Y: 154.001; control2X: 12.2; control2Y: 116.401; x: 12.2; y: 116.401 }
- PathCubic { control1X: 12.2; control1Y: 116.401; control2X: 181.001; control2Y: 86; x: 192.201; y: 82 }
- PathCubic { control1X: 203.401; control1Y: 78; control2X: 298.601; control2Y: 84.4; x: 298.601; y: 84.4 }
- PathLine { x: 293.001; y: 67.6 }
- PathCubic { control1X: 228.201; control1Y: 21.2; control2X: 209.001; control2Y: 44.4; x: 195.401; y: 40.4 }
- PathCubic { control1X: 181.801; control1Y: 36.4; control2X: 184.201; control2Y: 46; x: 181.001; y: 46.8 }
- PathCubic { control1X: 177.801; control1Y: 47.6; control2X: 138.601; control2Y: 22.8; x: 132.201; y: 23.6 }
- PathCubic { control1X: 125.801; control1Y: 24.4; control2X: 100.459; control2Y: 0.649; x: 115.401; y: 32.4 }
- PathCubic { control1X: 131.401; control1Y: 66.4; control2X: 57; control2Y: 71.6; x: 40.2; y: 60.4 }
- PathCubic { control1X: 23.4; control1Y: 49.2; control2X: 47.4; control2Y: 78.8; x: 47.4; y: 78.8 }
- PathCubic { control1X: 65.8; control1Y: 98.8; control2X: 31.4; control2Y: 82; x: 31.4; y: 82 }
- PathCubic { control1X: -3; control1Y: 69.2; control2X: -27; control2Y: 94.8; x: -30.2; y: 95.6 }
- PathCubic { control1X: -33.4; control1Y: 96.4; control2X: -38.2; control2Y: 99.6; x: -39; y: 93.2 }
- PathCubic { control1X: -39.8; control1Y: 86.8; control2X: -47.31; control2Y: 70.099; x: -79; y: 96.4 }
- PathCubic { control1X: -99; control1Y: 113.001; control2X: -112.8; control2Y: 91; x: -112.8; y: 91 }
- PathLine { x: -115.6; y: 102.6 }
- }
-
- ShapePath {
- fillColor: "#e87f3a"
- strokeWidth: -1
- PathMove { x: 133.51; y: 25.346 }
- PathCubic { control1X: 127.11; control1Y: 26.146; control2X: 101.743; control2Y: 2.407; x: 116.71; y: 34.146 }
- PathCubic { control1X: 133.31; control1Y: 69.346; control2X: 58.31; control2Y: 73.346; x: 41.51; y: 62.146 }
- PathCubic { control1X: 24.709; control1Y: 50.946; control2X: 48.71; control2Y: 80.546; x: 48.71; y: 80.546 }
- PathCubic { control1X: 67.11; control1Y: 100.546; control2X: 32.709; control2Y: 83.746; x: 32.709; y: 83.746 }
- PathCubic { control1X: -1.691; control1Y: 70.946; control2X: -25.691; control2Y: 96.546; x: -28.891; y: 97.346 }
- PathCubic { control1X: -32.091; control1Y: 98.146; control2X: -36.891; control2Y: 101.346; x: -37.691; y: 94.946 }
- PathCubic { control1X: -38.491; control1Y: 88.546; control2X: -45.87; control2Y: 72.012; x: -77.691; y: 98.146 }
- PathCubic { control1X: -98.927; control1Y: 115.492; control2X: -112.418; control2Y: 94.037; x: -112.418; y: 94.037 }
- PathLine { x: -115.618; y: 104.146 }
- PathCubic { control1X: -140.618; control1Y: 64.346; control2X: -125.546; control2Y: 122.655; x: -125.546; y: 122.655 }
- PathCubic { control1X: -116.745; control1Y: 157.056; control2X: 13.509; control2Y: 118.146; x: 13.509; y: 118.146 }
- PathCubic { control1X: 13.509; control1Y: 118.146; control2X: 182.31; control2Y: 87.746; x: 193.51; y: 83.746 }
- PathCubic { control1X: 204.71; control1Y: 79.746; control2X: 299.038; control2Y: 86.073; x: 299.038; y: 86.073 }
- PathLine { x: 293.51; y: 68.764 }
- PathCubic { control1X: 228.71; control1Y: 22.364; control2X: 210.31; control2Y: 46.146; x: 196.71; y: 42.146 }
- PathCubic { control1X: 183.11; control1Y: 38.146; control2X: 185.51; control2Y: 47.746; x: 182.31; y: 48.546 }
- PathCubic { control1X: 179.11; control1Y: 49.346; control2X: 139.91; control2Y: 24.546; x: 133.51; y: 25.346 }
- }
-
- ShapePath {
- fillColor: "#ea8c4d"
- strokeWidth: -1
- PathMove { x: 134.819; y: 27.091 }
- PathCubic { control1X: 128.419; control1Y: 27.891; control2X: 103.685; control2Y: 3.862; x: 118.019; y: 35.891 }
- PathCubic { control1X: 134.219; control1Y: 72.092; control2X: 59.619; control2Y: 75.092; x: 42.819; y: 63.892 }
- PathCubic { control1X: 26.019; control1Y: 52.692; control2X: 50.019; control2Y: 82.292; x: 50.019; y: 82.292 }
- PathCubic { control1X: 68.419; control1Y: 102.292; control2X: 34.019; control2Y: 85.492; x: 34.019; y: 85.492 }
- PathCubic { control1X: -0.381; control1Y: 72.692; control2X: -24.382; control2Y: 98.292; x: -27.582; y: 99.092 }
- PathCubic { control1X: -30.782; control1Y: 99.892; control2X: -35.582; control2Y: 103.092; x: -36.382; y: 96.692 }
- PathCubic { control1X: -37.182; control1Y: 90.292; control2X: -44.43; control2Y: 73.925; x: -76.382; y: 99.892 }
- PathCubic { control1X: -98.855; control1Y: 117.983; control2X: -112.036; control2Y: 97.074; x: -112.036; y: 97.074 }
- PathLine { x: -115.636; y: 105.692 }
- PathCubic { control1X: -139.436; control1Y: 66.692; control2X: -124.891; control2Y: 125.71; x: -124.891; y: 125.71 }
- PathCubic { control1X: -116.091; control1Y: 160.11; control2X: 14.819; control2Y: 119.892; x: 14.819; y: 119.892 }
- PathCubic { control1X: 14.819; control1Y: 119.892; control2X: 183.619; control2Y: 89.492; x: 194.819; y: 85.492 }
- PathCubic { control1X: 206.019; control1Y: 81.492; control2X: 299.474; control2Y: 87.746; x: 299.474; y: 87.746 }
- PathLine { x: 294.02; y: 69.928 }
- PathCubic { control1X: 229.219; control1Y: 23.528; control2X: 211.619; control2Y: 47.891; x: 198.019; y: 43.891 }
- PathCubic { control1X: 184.419; control1Y: 39.891; control2X: 186.819; control2Y: 49.491; x: 183.619; y: 50.292 }
- PathCubic { control1X: 180.419; control1Y: 51.092; control2X: 141.219; control2Y: 26.291; x: 134.819; y: 27.091 }
- }
-
- ShapePath {
- fillColor: "#ec9961"
- strokeWidth: -1
- PathMove { x: 136.128; y: 28.837 }
- PathCubic { control1X: 129.728; control1Y: 29.637; control2X: 104.999; control2Y: 5.605; x: 119.328; y: 37.637 }
- PathCubic { control1X: 136.128; control1Y: 75.193; control2X: 60.394; control2Y: 76.482; x: 44.128; y: 65.637 }
- PathCubic { control1X: 27.328; control1Y: 54.437; control2X: 51.328; control2Y: 84.037; x: 51.328; y: 84.037 }
- PathCubic { control1X: 69.728; control1Y: 104.037; control2X: 35.328; control2Y: 87.237; x: 35.328; y: 87.237 }
- PathCubic { control1X: 0.928; control1Y: 74.437; control2X: -23.072; control2Y: 100.037; x: -26.272; y: 100.837 }
- PathCubic { control1X: -29.472; control1Y: 101.637; control2X: -34.272; control2Y: 104.837; x: -35.072; y: 98.437 }
- PathCubic { control1X: -35.872; control1Y: 92.037; control2X: -42.989; control2Y: 75.839; x: -75.073; y: 101.637 }
- PathCubic { control1X: -98.782; control1Y: 120.474; control2X: -111.655; control2Y: 100.11; x: -111.655; y: 100.11 }
- PathLine { x: -115.655; y: 107.237 }
- PathCubic { control1X: -137.455; control1Y: 70.437; control2X: -124.236; control2Y: 128.765; x: -124.236; y: 128.765 }
- PathCubic { control1X: -115.436; control1Y: 163.165; control2X: 16.128; control2Y: 121.637; x: 16.128; y: 121.637 }
- PathCubic { control1X: 16.128; control1Y: 121.637; control2X: 184.928; control2Y: 91.237; x: 196.129; y: 87.237 }
- PathCubic { control1X: 207.329; control1Y: 83.237; control2X: 299.911; control2Y: 89.419; x: 299.911; y: 89.419 }
- PathLine { x: 294.529; y: 71.092 }
- PathCubic { control1X: 229.729; control1Y: 24.691; control2X: 212.929; control2Y: 49.637; x: 199.329; y: 45.637 }
- PathCubic { control1X: 185.728; control1Y: 41.637; control2X: 188.128; control2Y: 51.237; x: 184.928; y: 52.037 }
- PathCubic { control1X: 181.728; control1Y: 52.837; control2X: 142.528; control2Y: 28.037; x: 136.128; y: 28.837 }
- }
-
- ShapePath {
- fillColor: "#eea575"
- strokeWidth: -1
- PathMove { x: 137.438; y: 30.583 }
- PathCubic { control1X: 131.037; control1Y: 31.383; control2X: 106.814; control2Y: 7.129; x: 120.637; y: 39.383 }
- PathCubic { control1X: 137.438; control1Y: 78.583; control2X: 62.237; control2Y: 78.583; x: 45.437; y: 67.383 }
- PathCubic { control1X: 28.637; control1Y: 56.183; control2X: 52.637; control2Y: 85.783; x: 52.637; y: 85.783 }
- PathCubic { control1X: 71.037; control1Y: 105.783; control2X: 36.637; control2Y: 88.983; x: 36.637; y: 88.983 }
- PathCubic { control1X: 2.237; control1Y: 76.183; control2X: -21.763; control2Y: 101.783; x: -24.963; y: 102.583 }
- PathCubic { control1X: -28.163; control1Y: 103.383; control2X: -32.963; control2Y: 106.583; x: -33.763; y: 100.183 }
- PathCubic { control1X: -34.563; control1Y: 93.783; control2X: -41.548; control2Y: 77.752; x: -73.763; y: 103.383 }
- PathCubic { control1X: -98.709; control1Y: 122.965; control2X: -111.273; control2Y: 103.146; x: -111.273; y: 103.146 }
- PathLine { x: -115.673; y: 108.783 }
- PathCubic { control1X: -135.473; control1Y: 73.982; control2X: -123.582; control2Y: 131.819; x: -123.582; y: 131.819 }
- PathCubic { control1X: -114.782; control1Y: 166.22; control2X: 17.437; control2Y: 123.383; x: 17.437; y: 123.383 }
- PathCubic { control1X: 17.437; control1Y: 123.383; control2X: 186.238; control2Y: 92.983; x: 197.438; y: 88.983 }
- PathCubic { control1X: 208.638; control1Y: 84.983; control2X: 300.347; control2Y: 91.092; x: 300.347; y: 91.092 }
- PathLine { x: 295.038; y: 72.255 }
- PathCubic { control1X: 230.238; control1Y: 25.855; control2X: 214.238; control2Y: 51.383; x: 200.638; y: 47.383 }
- PathCubic { control1X: 187.038; control1Y: 43.383; control2X: 189.438; control2Y: 52.983; x: 186.238; y: 53.783 }
- PathCubic { control1X: 183.038; control1Y: 54.583; control2X: 143.838; control2Y: 29.783; x: 137.438; y: 30.583 }
- }
-
- ShapePath {
- fillColor: "#f1b288"
- strokeWidth: -1
- PathMove { x: 138.747; y: 32.328 }
- PathCubic { control1X: 132.347; control1Y: 33.128; control2X: 106.383; control2Y: 9.677; x: 121.947; y: 41.128 }
- PathCubic { control1X: 141.147; control1Y: 79.928; control2X: 63.546; control2Y: 80.328; x: 46.746; y: 69.128 }
- PathCubic { control1X: 29.946; control1Y: 57.928; control2X: 53.946; control2Y: 87.528; x: 53.946; y: 87.528 }
- PathCubic { control1X: 72.346; control1Y: 107.528; control2X: 37.946; control2Y: 90.728; x: 37.946; y: 90.728 }
- PathCubic { control1X: 3.546; control1Y: 77.928; control2X: -20.454; control2Y: 103.528; x: -23.654; y: 104.328 }
- PathCubic { control1X: -26.854; control1Y: 105.128; control2X: -31.654; control2Y: 108.328; x: -32.454; y: 101.928 }
- PathCubic { control1X: -33.254; control1Y: 95.528; control2X: -40.108; control2Y: 79.665; x: -72.454; y: 105.128 }
- PathCubic { control1X: -98.636; control1Y: 125.456; control2X: -110.891; control2Y: 106.183; x: -110.891; y: 106.183 }
- PathLine { x: -115.691; y: 110.328 }
- PathCubic { control1X: -133.691; control1Y: 77.128; control2X: -122.927; control2Y: 134.874; x: -122.927; y: 134.874 }
- PathCubic { control1X: -114.127; control1Y: 169.274; control2X: 18.746; control2Y: 125.128; x: 18.746; y: 125.128 }
- PathCubic { control1X: 18.746; control1Y: 125.128; control2X: 187.547; control2Y: 94.728; x: 198.747; y: 90.728 }
- PathCubic { control1X: 209.947; control1Y: 86.728; control2X: 300.783; control2Y: 92.764; x: 300.783; y: 92.764 }
- PathLine { x: 295.547; y: 73.419 }
- PathCubic { control1X: 230.747; control1Y: 27.019; control2X: 215.547; control2Y: 53.128; x: 201.947; y: 49.128 }
- PathCubic { control1X: 188.347; control1Y: 45.128; control2X: 190.747; control2Y: 54.728; x: 187.547; y: 55.528 }
- PathCubic { control1X: 184.347; control1Y: 56.328; control2X: 145.147; control2Y: 31.528; x: 138.747; y: 32.328 }
- }
-
- ShapePath {
- fillColor: "#f3bf9c"
- strokeWidth: -1
- PathMove { x: 140.056; y: 34.073 }
- PathCubic { control1X: 133.655; control1Y: 34.873; control2X: 107.313; control2Y: 11.613; x: 123.255; y: 42.873 }
- PathCubic { control1X: 143.656; control1Y: 82.874; control2X: 64.855; control2Y: 82.074; x: 48.055; y: 70.874 }
- PathCubic { control1X: 31.255; control1Y: 59.674; control2X: 55.255; control2Y: 89.274; x: 55.255; y: 89.274 }
- PathCubic { control1X: 73.655; control1Y: 109.274; control2X: 39.255; control2Y: 92.474; x: 39.255; y: 92.474 }
- PathCubic { control1X: 4.855; control1Y: 79.674; control2X: -19.145; control2Y: 105.274; x: -22.345; y: 106.074 }
- PathCubic { control1X: -25.545; control1Y: 106.874; control2X: -30.345; control2Y: 110.074; x: -31.145; y: 103.674 }
- PathCubic { control1X: -31.945; control1Y: 97.274; control2X: -38.668; control2Y: 81.578; x: -71.145; y: 106.874 }
- PathCubic { control1X: -98.564; control1Y: 127.947; control2X: -110.509; control2Y: 109.219; x: -110.509; y: 109.219 }
- PathLine { x: -115.709; y: 111.874 }
- PathCubic { control1X: -131.709; control1Y: 81.674; control2X: -122.273; control2Y: 137.929; x: -122.273; y: 137.929 }
- PathCubic { control1X: -113.473; control1Y: 172.329; control2X: 20.055; control2Y: 126.874; x: 20.055; y: 126.874 }
- PathCubic { control1X: 20.055; control1Y: 126.874; control2X: 188.856; control2Y: 96.474; x: 200.056; y: 92.474 }
- PathCubic { control1X: 211.256; control1Y: 88.474; control2X: 301.22; control2Y: 94.437; x: 301.22; y: 94.437 }
- PathLine { x: 296.056; y: 74.583 }
- PathCubic { control1X: 231.256; control1Y: 28.183; control2X: 216.856; control2Y: 54.874; x: 203.256; y: 50.874 }
- PathCubic { control1X: 189.656; control1Y: 46.873; control2X: 192.056; control2Y: 56.474; x: 188.856; y: 57.274 }
- PathCubic { control1X: 185.656; control1Y: 58.074; control2X: 146.456; control2Y: 33.273; x: 140.056; y: 34.073 }
- }
-
- ShapePath {
- fillColor: "#f5ccb0"
- strokeWidth: -1
- PathMove { x: 141.365; y: 35.819 }
- PathCubic { control1X: 134.965; control1Y: 36.619; control2X: 107.523; control2Y: 13.944; x: 124.565; y: 44.619 }
- PathCubic { control1X: 146.565; control1Y: 84.219; control2X: 66.164; control2Y: 83.819; x: 49.364; y: 72.619 }
- PathCubic { control1X: 32.564; control1Y: 61.419; control2X: 56.564; control2Y: 91.019; x: 56.564; y: 91.019 }
- PathCubic { control1X: 74.964; control1Y: 111.019; control2X: 40.564; control2Y: 94.219; x: 40.564; y: 94.219 }
- PathCubic { control1X: 6.164; control1Y: 81.419; control2X: -17.836; control2Y: 107.019; x: -21.036; y: 107.819 }
- PathCubic { control1X: -24.236; control1Y: 108.619; control2X: -29.036; control2Y: 111.819; x: -29.836; y: 105.419 }
- PathCubic { control1X: -30.636; control1Y: 99.019; control2X: -37.227; control2Y: 83.492; x: -69.836; y: 108.619 }
- PathCubic { control1X: -98.491; control1Y: 130.438; control2X: -110.127; control2Y: 112.256; x: -110.127; y: 112.256 }
- PathLine { x: -115.727; y: 113.419 }
- PathCubic { control1X: -130.128; control1Y: 85.019; control2X: -121.618; control2Y: 140.983; x: -121.618; y: 140.983 }
- PathCubic { control1X: -112.818; control1Y: 175.384; control2X: 21.364; control2Y: 128.619; x: 21.364; y: 128.619 }
- PathCubic { control1X: 21.364; control1Y: 128.619; control2X: 190.165; control2Y: 98.219; x: 201.365; y: 94.219 }
- PathCubic { control1X: 212.565; control1Y: 90.219; control2X: 301.656; control2Y: 96.11; x: 301.656; y: 96.11 }
- PathLine { x: 296.565; y: 75.746 }
- PathCubic { control1X: 231.765; control1Y: 29.346; control2X: 218.165; control2Y: 56.619; x: 204.565; y: 52.619 }
- PathCubic { control1X: 190.965; control1Y: 48.619; control2X: 193.365; control2Y: 58.219; x: 190.165; y: 59.019 }
- PathCubic { control1X: 186.965; control1Y: 59.819; control2X: 147.765; control2Y: 35.019; x: 141.365; y: 35.819 }
- }
-
- ShapePath {
- fillColor: "#f8d8c4"
- strokeWidth: -1
- PathMove { x: 142.674; y: 37.565 }
- PathCubic { control1X: 136.274; control1Y: 38.365; control2X: 108.832; control2Y: 15.689; x: 125.874; y: 46.365 }
- PathCubic { control1X: 147.874; control1Y: 85.965; control2X: 67.474; control2Y: 85.565; x: 50.674; y: 74.365 }
- PathCubic { control1X: 33.874; control1Y: 63.165; control2X: 57.874; control2Y: 92.765; x: 57.874; y: 92.765 }
- PathCubic { control1X: 76.274; control1Y: 112.765; control2X: 41.874; control2Y: 95.965; x: 41.874; y: 95.965 }
- PathCubic { control1X: 7.473; control1Y: 83.165; control2X: -16.527; control2Y: 108.765; x: -19.727; y: 109.565 }
- PathCubic { control1X: -22.927; control1Y: 110.365; control2X: -27.727; control2Y: 113.565; x: -28.527; y: 107.165 }
- PathCubic { control1X: -29.327; control1Y: 100.765; control2X: -35.786; control2Y: 85.405; x: -68.527; y: 110.365 }
- PathCubic { control1X: -98.418; control1Y: 132.929; control2X: -109.745; control2Y: 115.293; x: -109.745; y: 115.293 }
- PathLine { x: -115.745; y: 114.965 }
- PathCubic { control1X: -129.346; control1Y: 88.564; control2X: -120.963; control2Y: 144.038; x: -120.963; y: 144.038 }
- PathCubic { control1X: -112.163; control1Y: 178.438; control2X: 22.673; control2Y: 130.365; x: 22.673; y: 130.365 }
- PathCubic { control1X: 22.673; control1Y: 130.365; control2X: 191.474; control2Y: 99.965; x: 202.674; y: 95.965 }
- PathCubic { control1X: 213.874; control1Y: 91.965; control2X: 302.093; control2Y: 97.783; x: 302.093; y: 97.783 }
- PathLine { x: 297.075; y: 76.91 }
- PathCubic { control1X: 232.274; control1Y: 30.51; control2X: 219.474; control2Y: 58.365; x: 205.874; y: 54.365 }
- PathCubic { control1X: 192.274; control1Y: 50.365; control2X: 194.674; control2Y: 59.965; x: 191.474; y: 60.765 }
- PathCubic { control1X: 188.274; control1Y: 61.565; control2X: 149.074; control2Y: 36.765; x: 142.674; y: 37.565 }
- }
-
- ShapePath {
- fillColor: "#fae5d7"
- strokeWidth: -1
- PathMove { x: 143.983; y: 39.31 }
- PathCubic { control1X: 137.583; control1Y: 40.11; control2X: 110.529; control2Y: 17.223; x: 127.183; y: 48.11 }
- PathCubic { control1X: 149.183; control1Y: 88.91; control2X: 68.783; control2Y: 87.31; x: 51.983; y: 76.11 }
- PathCubic { control1X: 35.183; control1Y: 64.91; control2X: 59.183; control2Y: 94.51; x: 59.183; y: 94.51 }
- PathCubic { control1X: 77.583; control1Y: 114.51; control2X: 43.183; control2Y: 97.71; x: 43.183; y: 97.71 }
- PathCubic { control1X: 8.783; control1Y: 84.91; control2X: -15.217; control2Y: 110.51; x: -18.417; y: 111.31 }
- PathCubic { control1X: -21.618; control1Y: 112.11; control2X: -26.418; control2Y: 115.31; x: -27.218; y: 108.91 }
- PathCubic { control1X: -28.018; control1Y: 102.51; control2X: -34.346; control2Y: 87.318; x: -67.218; y: 112.11 }
- PathCubic { control1X: -98.345; control1Y: 135.42; control2X: -109.363; control2Y: 118.329; x: -109.363; y: 118.329 }
- PathLine { x: -115.764; y: 116.51 }
- PathCubic { control1X: -128.764; control1Y: 92.51; control2X: -120.309; control2Y: 147.093; x: -120.309; y: 147.093 }
- PathCubic { control1X: -111.509; control1Y: 181.493; control2X: 23.983; control2Y: 132.11; x: 23.983; y: 132.11 }
- PathCubic { control1X: 23.983; control1Y: 132.11; control2X: 192.783; control2Y: 101.71; x: 203.983; y: 97.71 }
- PathCubic { control1X: 215.183; control1Y: 93.71; control2X: 302.529; control2Y: 99.456; x: 302.529; y: 99.456 }
- PathLine { x: 297.583; y: 78.074 }
- PathCubic { control1X: 232.783; control1Y: 31.673; control2X: 220.783; control2Y: 60.11; x: 207.183; y: 56.11 }
- PathCubic { control1X: 193.583; control1Y: 52.11; control2X: 195.983; control2Y: 61.71; x: 192.783; y: 62.51 }
- PathCubic { control1X: 189.583; control1Y: 63.31; control2X: 150.383; control2Y: 38.51; x: 143.983; y: 39.31 }
- }
-
- ShapePath {
- fillColor: "#fcf2eb"
- strokeWidth: -1
- PathMove { x: 145.292; y: 41.055 }
- PathCubic { control1X: 138.892; control1Y: 41.855; control2X: 112.917; control2Y: 18.411; x: 128.492; y: 49.855 }
- PathCubic { control1X: 149.692; control1Y: 92.656; control2X: 70.092; control2Y: 89.056; x: 53.292; y: 77.856 }
- PathCubic { control1X: 36.492; control1Y: 66.656; control2X: 60.492; control2Y: 96.256; x: 60.492; y: 96.256 }
- PathCubic { control1X: 78.892; control1Y: 116.256; control2X: 44.492; control2Y: 99.456; x: 44.492; y: 99.456 }
- PathCubic { control1X: 10.092; control1Y: 86.656; control2X: -13.908; control2Y: 112.256; x: -17.108; y: 113.056 }
- PathCubic { control1X: -20.308; control1Y: 113.856; control2X: -25.108; control2Y: 117.056; x: -25.908; y: 110.656 }
- PathCubic { control1X: -26.708; control1Y: 104.256; control2X: -32.905; control2Y: 89.232; x: -65.908; y: 113.856 }
- PathCubic { control1X: -98.273; control1Y: 137.911; control2X: -108.982; control2Y: 121.365; x: -108.982; y: 121.365 }
- PathLine { x: -115.782; y: 118.056 }
- PathCubic { control1X: -128.582; control1Y: 94.856; control2X: -119.654; control2Y: 150.147; x: -119.654; y: 150.147 }
- PathCubic { control1X: -110.854; control1Y: 184.547; control2X: 25.292; control2Y: 133.856; x: 25.292; y: 133.856 }
- PathCubic { control1X: 25.292; control1Y: 133.856; control2X: 194.093; control2Y: 103.456; x: 205.293; y: 99.456 }
- PathCubic { control1X: 216.493; control1Y: 95.456; control2X: 302.965; control2Y: 101.128; x: 302.965; y: 101.128 }
- PathLine { x: 298.093; y: 79.237 }
- PathCubic { control1X: 233.292; control1Y: 32.837; control2X: 222.093; control2Y: 61.856; x: 208.493; y: 57.856 }
- PathCubic { control1X: 194.893; control1Y: 53.855; control2X: 197.293; control2Y: 63.456; x: 194.093; y: 64.256 }
- PathCubic { control1X: 190.892; control1Y: 65.056; control2X: 151.692; control2Y: 40.255; x: 145.292; y: 41.055 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: -115.8; y: 119.601 }
- PathCubic { control1X: -128.6; control1Y: 97.6; control2X: -119; control2Y: 153.201; x: -119; y: 153.201 }
- PathCubic { control1X: -110.2; control1Y: 187.601; control2X: 26.6; control2Y: 135.601; x: 26.6; y: 135.601 }
- PathCubic { control1X: 26.6; control1Y: 135.601; control2X: 195.401; control2Y: 105.2; x: 206.601; y: 101.2 }
- PathCubic { control1X: 217.801; control1Y: 97.2; control2X: 303.401; control2Y: 102.8; x: 303.401; y: 102.8 }
- PathLine { x: 298.601; y: 80.4 }
- PathCubic { control1X: 233.801; control1Y: 34; control2X: 223.401; control2Y: 63.6; x: 209.801; y: 59.6 }
- PathCubic { control1X: 196.201; control1Y: 55.6; control2X: 198.601; control2Y: 65.2; x: 195.401; y: 66 }
- PathCubic { control1X: 192.201; control1Y: 66.8; control2X: 153.001; control2Y: 42; x: 146.601; y: 42.8 }
- PathCubic { control1X: 140.201; control1Y: 43.6; control2X: 114.981; control2Y: 19.793; x: 129.801; y: 51.6 }
- PathCubic { control1X: 152.028; control1Y: 99.307; control2X: 69.041; control2Y: 89.227; x: 54.6; y: 79.6 }
- PathCubic { control1X: 37.8; control1Y: 68.4; control2X: 61.8; control2Y: 98; x: 61.8; y: 98 }
- PathCubic { control1X: 80.2; control1Y: 118.001; control2X: 45.8; control2Y: 101.2; x: 45.8; y: 101.2 }
- PathCubic { control1X: 11.4; control1Y: 88.4; control2X: -12.6; control2Y: 114.001; x: -15.8; y: 114.801 }
- PathCubic { control1X: -19; control1Y: 115.601; control2X: -23.8; control2Y: 118.801; x: -24.6; y: 112.401 }
- PathCubic { control1X: -25.4; control1Y: 106; control2X: -31.465; control2Y: 91.144; x: -64.6; y: 115.601 }
- PathCubic { control1X: -98.2; control1Y: 140.401; control2X: -108.6; control2Y: 124.401; x: -108.6; y: 124.401 }
- PathLine { x: -115.8; y: 119.601 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -74.2; y: 149.601 }
- PathCubic { control1X: -74.2; control1Y: 149.601; control2X: -81.4; control2Y: 161.201; x: -60.6; y: 174.401 }
- PathCubic { control1X: -60.6; control1Y: 174.401; control2X: -59.2; control2Y: 175.801; x: -77.2; y: 171.601 }
- PathCubic { control1X: -77.2; control1Y: 171.601; control2X: -83.4; control2Y: 169.601; x: -85; y: 159.201 }
- PathCubic { control1X: -85; control1Y: 159.201; control2X: -89.8; control2Y: 154.801; x: -94.6; y: 149.201 }
- PathCubic { control1X: -99.4; control1Y: 143.601; control2X: -74.2; control2Y: 149.601; x: -74.2; y: 149.601 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 65.8; y: 102 }
- PathCubic { control1X: 65.8; control1Y: 102; control2X: 83.498; control2Y: 128.821; x: 82.9; y: 133.601 }
- PathCubic { control1X: 81.6; control1Y: 144.001; control2X: 81.4; control2Y: 153.601; x: 84.6; y: 157.601 }
- PathCubic { control1X: 87.801; control1Y: 161.601; control2X: 96.601; control2Y: 194.801; x: 96.601; y: 194.801 }
- PathCubic { control1X: 96.601; control1Y: 194.801; control2X: 96.201; control2Y: 196.001; x: 108.601; y: 158.001 }
- PathCubic { control1X: 108.601; control1Y: 158.001; control2X: 120.201; control2Y: 142.001; x: 100.201; y: 123.601 }
- PathCubic { control1X: 100.201; control1Y: 123.601; control2X: 65; control2Y: 94.8; x: 65.8; y: 102 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -54.2; y: 176.401 }
- PathCubic { control1X: -54.2; control1Y: 176.401; control2X: -43; control2Y: 183.601; x: -57.4; y: 214.801 }
- PathLine { x: -51; y: 212.401 }
- PathCubic { control1X: -51; control1Y: 212.401; control2X: -51.8; control2Y: 223.601; x: -55; y: 226.001 }
- PathLine { x: -47.8; y: 222.801 }
- PathCubic { control1X: -47.8; control1Y: 222.801; control2X: -43; control2Y: 230.801; x: -47; y: 235.601 }
- PathCubic { control1X: -47; control1Y: 235.601; control2X: -30.2; control2Y: 243.601; x: -31; y: 250.001 }
- PathCubic { control1X: -31; control1Y: 250.001; control2X: -24.6; control2Y: 242.001; x: -28.6; y: 235.601 }
- PathCubic { control1X: -32.6; control1Y: 229.201; control2X: -39.8; control2Y: 233.201; x: -39; y: 214.801 }
- PathLine { x: -47.8; y: 218.001 }
- PathCubic { control1X: -47.8; control1Y: 218.001; control2X: -42.2; control2Y: 209.201; x: -42.2; y: 202.801 }
- PathLine { x: -50.2; y: 205.201 }
- PathCubic { control1X: -50.2; control1Y: 205.201; control2X: -34.731; control2Y: 178.623; x: -45.4; y: 177.201 }
- PathCubic { control1X: -51.4; control1Y: 176.401; control2X: -54.2; control2Y: 176.401; x: -54.2; y: 176.401 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -21.8; y: 193.201 }
- PathCubic { control1X: -21.8; control1Y: 193.201; control2X: -19; control2Y: 188.801; x: -21.8; y: 189.601 }
- PathCubic { control1X: -24.6; control1Y: 190.401; control2X: -55.8; control2Y: 205.201; x: -61.8; y: 214.801 }
- PathCubic { control1X: -61.8; control1Y: 214.801; control2X: -27.4; control2Y: 190.401; x: -21.8; y: 193.201 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -11.4; y: 201.201 }
- PathCubic { control1X: -11.4; control1Y: 201.201; control2X: -8.6; control2Y: 196.801; x: -11.4; y: 197.601 }
- PathCubic { control1X: -14.2; control1Y: 198.401; control2X: -45.4; control2Y: 213.201; x: -51.4; y: 222.801 }
- PathCubic { control1X: -51.4; control1Y: 222.801; control2X: -17; control2Y: 198.401; x: -11.4; y: 201.201 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 1.8; y: 186.001 }
- PathCubic { control1X: 1.8; control1Y: 186.001; control2X: 4.6; control2Y: 181.601; x: 1.8; y: 182.401 }
- PathCubic { control1X: -1; control1Y: 183.201; control2X: -32.2; control2Y: 198.001; x: -38.2; y: 207.601 }
- PathCubic { control1X: -38.2; control1Y: 207.601; control2X: -3.8; control2Y: 183.201; x: 1.8; y: 186.001 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -21.4; y: 229.601 }
- PathCubic { control1X: -21.4; control1Y: 229.601; control2X: -21.4; control2Y: 223.601; x: -24.2; y: 224.401 }
- PathCubic { control1X: -27; control1Y: 225.201; control2X: -63; control2Y: 242.801; x: -69; y: 252.401 }
- PathCubic { control1X: -69; control1Y: 252.401; control2X: -27; control2Y: 226.801; x: -21.4; y: 229.601 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -20.2; y: 218.801 }
- PathCubic { control1X: -20.2; control1Y: 218.801; control2X: -19; control2Y: 214.001; x: -21.8; y: 214.801 }
- PathCubic { control1X: -23.8; control1Y: 214.801; control2X: -50.2; control2Y: 226.401; x: -56.2; y: 236.001 }
- PathCubic { control1X: -56.2; control1Y: 236.001; control2X: -26.6; control2Y: 214.401; x: -20.2; y: 218.801 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -34.6; y: 266.401 }
- PathLine { x: -44.6; y: 274.001 }
- PathCubic { control1X: -44.6; control1Y: 274.001; control2X: -34.2; control2Y: 266.401; x: -30.6; y: 267.601 }
- PathCubic { control1X: -30.6; control1Y: 267.601; control2X: -37.4; control2Y: 278.801; x: -38.2; y: 284.001 }
- PathCubic { control1X: -38.2; control1Y: 284.001; control2X: -27.8; control2Y: 271.201; x: -22.2; y: 271.601 }
- PathCubic { control1X: -22.2; control1Y: 271.601; control2X: -14.6; control2Y: 272.001; x: -14.6; y: 282.801 }
- PathCubic { control1X: -14.6; control1Y: 282.801; control2X: -9; control2Y: 272.401; x: -5.8; y: 272.801 }
- PathCubic { control1X: -5.8; control1Y: 272.801; control2X: -4.6; control2Y: 279.201; x: -5.8; y: 286.001 }
- PathCubic { control1X: -5.8; control1Y: 286.001; control2X: -1.8; control2Y: 278.401; x: 2.2; y: 280.001 }
- PathCubic { control1X: 2.2; control1Y: 280.001; control2X: 8.6; control2Y: 278.001; x: 7.8; y: 289.601 }
- PathCubic { control1X: 7.8; control1Y: 289.601; control2X: 7.8; control2Y: 300.001; x: 7; y: 302.801 }
- PathCubic { control1X: 7; control1Y: 302.801; control2X: 12.6; control2Y: 276.401; x: 15; y: 276.001 }
- PathCubic { control1X: 15; control1Y: 276.001; control2X: 23; control2Y: 274.801; x: 27.8; y: 283.601 }
- PathCubic { control1X: 27.8; control1Y: 283.601; control2X: 23.8; control2Y: 276.001; x: 28.6; y: 278.001 }
- PathCubic { control1X: 28.6; control1Y: 278.001; control2X: 39.4; control2Y: 279.601; x: 42.6; y: 286.401 }
- PathCubic { control1X: 42.6; control1Y: 286.401; control2X: 35.8; control2Y: 274.401; x: 41.4; y: 277.601 }
- PathCubic { control1X: 41.4; control1Y: 277.601; control2X: 48.2; control2Y: 277.601; x: 49.4; y: 284.001 }
- PathCubic { control1X: 49.4; control1Y: 284.001; control2X: 57.8; control2Y: 305.201; x: 59.8; y: 306.801 }
- PathCubic { control1X: 59.8; control1Y: 306.801; control2X: 52.2; control2Y: 285.201; x: 53.8; y: 285.201 }
- PathCubic { control1X: 53.8; control1Y: 285.201; control2X: 51.8; control2Y: 273.201; x: 57; y: 288.001 }
- PathCubic { control1X: 57; control1Y: 288.001; control2X: 53.8; control2Y: 274.001; x: 59.4; y: 274.801 }
- PathCubic { control1X: 65; control1Y: 275.601; control2X: 69.4; control2Y: 285.601; x: 77.8; y: 283.201 }
- PathCubic { control1X: 77.8; control1Y: 283.201; control2X: 87.401; control2Y: 288.801; x: 89.401; y: 219.601 }
- PathLine { x: -34.6; y: 266.401 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -29.8; y: 173.601 }
- PathCubic { control1X: -29.8; control1Y: 173.601; control2X: -15; control2Y: 167.601; x: 25; y: 173.601 }
- PathCubic { control1X: 25; control1Y: 173.601; control2X: 32.2; control2Y: 174.001; x: 39; y: 165.201 }
- PathCubic { control1X: 45.8; control1Y: 156.401; control2X: 72.6; control2Y: 149.201; x: 79; y: 151.201 }
- PathLine { x: 88.601; y: 157.601 }
- PathLine { x: 89.401; y: 158.801 }
- PathCubic { control1X: 89.401; control1Y: 158.801; control2X: 101.801; control2Y: 169.201; x: 102.201; y: 176.801 }
- PathCubic { control1X: 102.601; control1Y: 184.401; control2X: 87.801; control2Y: 232.401; x: 78.2; y: 248.401 }
- PathCubic { control1X: 68.6; control1Y: 264.401; control2X: 59; control2Y: 276.801; x: 39.8; y: 274.401 }
- PathCubic { control1X: 39.8; control1Y: 274.401; control2X: 19; control2Y: 270.401; x: -6.6; y: 274.401 }
- PathCubic { control1X: -6.6; control1Y: 274.401; control2X: -35.8; control2Y: 272.801; x: -38.6; y: 264.801 }
- PathCubic { control1X: -41.4; control1Y: 256.801; control2X: -27.4; control2Y: 241.601; x: -27.4; y: 241.601 }
- PathCubic { control1X: -27.4; control1Y: 241.601; control2X: -23; control2Y: 233.201; x: -24.2; y: 218.801 }
- PathCubic { control1X: -25.4; control1Y: 204.401; control2X: -25; control2Y: 176.401; x: -29.8; y: 173.601 }
- }
-
- ShapePath {
- fillColor: "#e5668c"
- strokeWidth: -1
- PathMove { x: -7.8; y: 175.601 }
- PathCubic { control1X: 0.6; control1Y: 194.001; control2X: -29; control2Y: 259.201; x: -29; y: 259.201 }
- PathCubic { control1X: -31; control1Y: 260.801; control2X: -16.34; control2Y: 266.846; x: -6.2; y: 264.401 }
- PathCubic { control1X: 4.746; control1Y: 261.763; control2X: 45; control2Y: 266.001; x: 45; y: 266.001 }
- PathCubic { control1X: 68.6; control1Y: 250.401; control2X: 81.4; control2Y: 206.001; x: 81.4; y: 206.001 }
- PathCubic { control1X: 81.4; control1Y: 206.001; control2X: 91.801; control2Y: 182.001; x: 74.2; y: 178.801 }
- PathCubic { control1X: 56.6; control1Y: 175.601; control2X: -7.8; control2Y: 175.601; x: -7.8; y: 175.601 }
- }
-
- ShapePath {
- fillColor: "#b23259"
- strokeWidth: -1
- PathMove { x: -9.831; y: 206.497 }
- PathCubic { control1X: -6.505; control1Y: 193.707; control2X: -4.921; control2Y: 181.906; x: -7.8; y: 175.601 }
- PathCubic { control1X: -7.8; control1Y: 175.601; control2X: 54.6; control2Y: 182.001; x: 65.8; y: 161.201 }
- PathCubic { control1X: 70.041; control1Y: 153.326; control2X: 84.801; control2Y: 184.001; x: 84.4; y: 193.601 }
- PathCubic { control1X: 84.4; control1Y: 193.601; control2X: 21.4; control2Y: 208.001; x: 6.6; y: 196.801 }
- PathLine { x: -9.831; y: 206.497 }
- }
-
- ShapePath {
- fillColor: "#a5264c"
- strokeWidth: -1
- PathMove { x: -5.4; y: 222.801 }
- PathCubic { control1X: -5.4; control1Y: 222.801; control2X: -3.4; control2Y: 230.001; x: -5.8; y: 234.001 }
- PathCubic { control1X: -5.8; control1Y: 234.001; control2X: -7.4; control2Y: 234.801; x: -8.6; y: 235.201 }
- PathCubic { control1X: -8.6; control1Y: 235.201; control2X: -7.4; control2Y: 238.801; x: -1.4; y: 240.401 }
- PathCubic { control1X: -1.4; control1Y: 240.401; control2X: 0.6; control2Y: 244.801; x: 3; y: 245.201 }
- PathCubic { control1X: 5.4; control1Y: 245.601; control2X: 10.2; control2Y: 251.201; x: 14.2; y: 250.001 }
- PathCubic { control1X: 18.2; control1Y: 248.801; control2X: 29.4; control2Y: 244.801; x: 29.4; y: 244.801 }
- PathCubic { control1X: 29.4; control1Y: 244.801; control2X: 35; control2Y: 241.601; x: 43.8; y: 245.201 }
- PathCubic { control1X: 43.8; control1Y: 245.201; control2X: 46.175; control2Y: 244.399; x: 46.6; y: 240.401 }
- PathCubic { control1X: 47.1; control1Y: 235.701; control2X: 50.2; control2Y: 232.001; x: 52.2; y: 230.001 }
- PathCubic { control1X: 54.2; control1Y: 228.001; control2X: 63.8; control2Y: 215.201; x: 62.6; y: 214.801 }
- PathCubic { control1X: 61.4; control1Y: 214.401; control2X: -5.4; control2Y: 222.801; x: -5.4; y: 222.801 }
- }
-
- ShapePath {
- fillColor: "#ff727f"
- strokeColor: "#000000"
- strokeWidth: 1
- PathMove { x: -9.8; y: 174.401 }
- PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -12.6; control2Y: 196.801; x: -9.4; y: 205.201 }
- PathCubic { control1X: -6.2; control1Y: 213.601; control2X: -7; control2Y: 215.601; x: -7.8; y: 219.601 }
- PathCubic { control1X: -8.6; control1Y: 223.601; control2X: -4.2; control2Y: 233.601; x: 1.4; y: 239.601 }
- PathLine { x: 13.4; y: 241.201 }
- PathCubic { control1X: 13.4; control1Y: 241.201; control2X: 28.6; control2Y: 237.601; x: 37.8; y: 240.401 }
- PathCubic { control1X: 37.8; control1Y: 240.401; control2X: 46.794; control2Y: 241.744; x: 50.2; y: 226.801 }
- PathCubic { control1X: 50.2; control1Y: 226.801; control2X: 55; control2Y: 220.401; x: 62.2; y: 217.601 }
- PathCubic { control1X: 69.4; control1Y: 214.801; control2X: 76.6; control2Y: 173.201; x: 72.6; y: 165.201 }
- PathCubic { control1X: 68.6; control1Y: 157.201; control2X: 54.2; control2Y: 152.801; x: 38.2; y: 168.401 }
- PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -8.2; y: 249.201 }
- PathCubic { control1X: -8.2; control1Y: 249.201; control2X: -9; control2Y: 247.201; x: -13.4; y: 246.801 }
- PathCubic { control1X: -13.4; control1Y: 246.801; control2X: -35.8; control2Y: 243.201; x: -44.2; y: 230.801 }
- PathCubic { control1X: -44.2; control1Y: 230.801; control2X: -51; control2Y: 225.201; x: -46.6; y: 236.801 }
- PathCubic { control1X: -46.6; control1Y: 236.801; control2X: -36.2; control2Y: 257.201; x: -29.4; y: 260.001 }
- PathCubic { control1X: -29.4; control1Y: 260.001; control2X: -13; control2Y: 264.001; x: -8.2; y: 249.201 }
- }
-
- ShapePath {
- fillColor: "#cc3f4c"
- strokeWidth: -1
- PathMove { x: 71.742; y: 185.229 }
- PathCubic { control1X: 72.401; control1Y: 177.323; control2X: 74.354; control2Y: 168.709; x: 72.6; y: 165.201 }
- PathCubic { control1X: 66.154; control1Y: 152.307; control2X: 49.181; control2Y: 157.695; x: 38.2; y: 168.401 }
- PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 }
- PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -11.545; control2Y: 188.364; x: -10.705; y: 198.376 }
- PathCubic { control1X: -10.705; control1Y: 198.376; control2X: 26.6; control2Y: 186.801; x: 27.4; y: 192.401 }
- PathCubic { control1X: 27.4; control1Y: 192.401; control2X: 29; control2Y: 189.201; x: 38.2; y: 189.201 }
- PathCubic { control1X: 47.4; control1Y: 189.201; control2X: 70.142; control2Y: 188.029; x: 71.742; y: 185.229 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#a51926"
- strokeWidth: 2
- PathMove { x: 28.6; y: 175.201 }
- PathCubic { control1X: 28.6; control1Y: 175.201; control2X: 33.4; control2Y: 180.001; x: 29.8; y: 189.601 }
- PathCubic { control1X: 29.8; control1Y: 189.601; control2X: 15.4; control2Y: 205.601; x: 17.4; y: 219.601 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -19.4; y: 260.001 }
- PathCubic { control1X: -19.4; control1Y: 260.001; control2X: -23.8; control2Y: 247.201; x: -15; y: 254.001 }
- PathCubic { control1X: -15; control1Y: 254.001; control2X: -10.2; control2Y: 256.001; x: -11.4; y: 257.601 }
- PathCubic { control1X: -12.6; control1Y: 259.201; control2X: -18.2; control2Y: 263.201; x: -19.4; y: 260.001 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -14.36; y: 261.201 }
- PathCubic { control1X: -14.36; control1Y: 261.201; control2X: -17.88; control2Y: 250.961; x: -10.84; y: 256.401 }
- PathCubic { control1X: -10.84; control1Y: 256.401; control2X: -6.419; control2Y: 258.849; x: -7.96; y: 259.281 }
- PathCubic { control1X: -12.52; control1Y: 260.561; control2X: -7.96; control2Y: 263.121; x: -14.36; y: 261.201 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -9.56; y: 261.201 }
- PathCubic { control1X: -9.56; control1Y: 261.201; control2X: -13.08; control2Y: 250.961; x: -6.04; y: 256.401 }
- PathCubic { control1X: -6.04; control1Y: 256.401; control2X: -1.665; control2Y: 258.711; x: -3.16; y: 259.281 }
- PathCubic { control1X: -6.52; control1Y: 260.561; control2X: -3.16; control2Y: 263.121; x: -9.56; y: 261.201 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -2.96; y: 261.401 }
- PathCubic { control1X: -2.96; control1Y: 261.401; control2X: -6.48; control2Y: 251.161; x: 0.56; y: 256.601 }
- PathCubic { control1X: 0.56; control1Y: 256.601; control2X: 4.943; control2Y: 258.933; x: 3.441; y: 259.481 }
- PathCubic { control1X: 0.48; control1Y: 260.561; control2X: 3.441; control2Y: 263.321; x: -2.96; y: 261.401 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: 3.52; y: 261.321 }
- PathCubic { control1X: 3.52; control1Y: 261.321; control2X: 0; control2Y: 251.081; x: 7.041; y: 256.521 }
- PathCubic { control1X: 7.041; control1Y: 256.521; control2X: 10.881; control2Y: 258.121; x: 9.921; y: 259.401 }
- PathCubic { control1X: 8.961; control1Y: 260.681; control2X: 9.921; control2Y: 263.241; x: 3.52; y: 261.321 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: 10.2; y: 262.001 }
- PathCubic { control1X: 10.2; control1Y: 262.001; control2X: 5.4; control2Y: 249.601; x: 14.6; y: 256.001 }
- PathCubic { control1X: 14.6; control1Y: 256.001; control2X: 19.4; control2Y: 258.001; x: 18.2; y: 259.601 }
- PathCubic { control1X: 17; control1Y: 261.201; control2X: 18.2; control2Y: 264.401; x: 10.2; y: 262.001 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#a5264c"
- strokeWidth: 2
- PathMove { x: -18.2; y: 244.801 }
- PathCubic { control1X: -18.2; control1Y: 244.801; control2X: -5; control2Y: 242.001; x: 1; y: 245.201 }
- PathCubic { control1X: 1; control1Y: 245.201; control2X: 7; control2Y: 246.401; x: 8.2; y: 246.001 }
- PathCubic { control1X: 9.4; control1Y: 245.601; control2X: 12.6; control2Y: 245.201; x: 12.6; y: 245.201 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#a5264c"
- strokeWidth: 2
- PathMove { x: 15.8; y: 253.601 }
- PathCubic { control1X: 15.8; control1Y: 253.601; control2X: 27.8; control2Y: 240.001; x: 39.8; y: 244.401 }
- PathCubic { control1X: 46.816; control1Y: 246.974; control2X: 45.8; control2Y: 243.601; x: 46.6; y: 240.801 }
- PathCubic { control1X: 47.4; control1Y: 238.001; control2X: 47.6; control2Y: 233.801; x: 52.6; y: 230.801 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: 33; y: 237.601 }
- PathCubic { control1X: 33; control1Y: 237.601; control2X: 29; control2Y: 226.801; x: 26.2; y: 239.601 }
- PathCubic { control1X: 23.4; control1Y: 252.401; control2X: 20.2; control2Y: 256.001; x: 18.6; y: 258.801 }
- PathCubic { control1X: 18.6; control1Y: 258.801; control2X: 18.6; control2Y: 264.001; x: 27; y: 263.601 }
- PathCubic { control1X: 27; control1Y: 263.601; control2X: 37.8; control2Y: 263.201; x: 38.2; y: 260.401 }
- PathCubic { control1X: 38.6; control1Y: 257.601; control2X: 37; control2Y: 246.001; x: 33; y: 237.601 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#a5264c"
- strokeWidth: 2
- PathMove { x: 47; y: 244.801 }
- PathCubic { control1X: 47; control1Y: 244.801; control2X: 50.6; control2Y: 242.401; x: 53; y: 243.601 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#a5264c"
- strokeWidth: 2
- PathMove { x: 53.5; y: 228.401 }
- PathCubic { control1X: 53.5; control1Y: 228.401; control2X: 56.4; control2Y: 223.501; x: 61.2; y: 222.701 }
- }
-
- ShapePath {
- fillColor: "#b2b2b2"
- strokeWidth: -1
- PathMove { x: -25.8; y: 265.201 }
- PathCubic { control1X: -25.8; control1Y: 265.201; control2X: -7.8; control2Y: 268.401; x: -3.4; y: 266.801 }
- PathCubic { control1X: -3.4; control1Y: 266.801; control2X: 5.4; control2Y: 266.801; x: -3; y: 268.801 }
- PathCubic { control1X: -3; control1Y: 268.801; control2X: -15.8; control2Y: 268.801; x: -23.8; y: 267.601 }
- PathCubic { control1X: -23.8; control1Y: 267.601; control2X: -35.4; control2Y: 262.001; x: -25.8; y: 265.201 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -11.8; y: 172.001 }
- PathCubic { control1X: -11.8; control1Y: 172.001; control2X: 5.8; control2Y: 172.001; x: 7.8; y: 172.801 }
- PathCubic { control1X: 7.8; control1Y: 172.801; control2X: 15; control2Y: 203.601; x: 11.4; y: 211.201 }
- PathCubic { control1X: 11.4; control1Y: 211.201; control2X: 10.2; control2Y: 214.001; x: 7.4; y: 208.401 }
- PathCubic { control1X: 7.4; control1Y: 208.401; control2X: -11; control2Y: 175.601; x: -14.2; y: 173.601 }
- PathCubic { control1X: -17.4; control1Y: 171.601; control2X: -13; control2Y: 172.001; x: -11.8; y: 172.001 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -88.9; y: 169.301 }
- PathCubic { control1X: -88.9; control1Y: 169.301; control2X: -80; control2Y: 171.001; x: -67.4; y: 173.601 }
- PathCubic { control1X: -67.4; control1Y: 173.601; control2X: -62.6; control2Y: 196.001; x: -59.4; y: 200.801 }
- PathCubic { control1X: -56.2; control1Y: 205.601; control2X: -59.8; control2Y: 205.601; x: -63.4; y: 202.801 }
- PathCubic { control1X: -67; control1Y: 200.001; control2X: -81.8; control2Y: 186.001; x: -83.8; y: 181.601 }
- PathCubic { control1X: -85.8; control1Y: 177.201; control2X: -88.9; control2Y: 169.301; x: -88.9; y: 169.301 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -67.039; y: 173.818 }
- PathCubic { control1X: -67.039; control1Y: 173.818; control2X: -61.239; control2Y: 175.366; x: -60.23; y: 177.581 }
- PathCubic { control1X: -59.222; control1Y: 179.795; control2X: -61.432; control2Y: 183.092; x: -61.432; y: 183.092 }
- PathCubic { control1X: -61.432; control1Y: 183.092; control2X: -62.432; control2Y: 186.397; x: -63.634; y: 184.235 }
- PathCubic { control1X: -64.836; control1Y: 182.072; control2X: -67.708; control2Y: 174.412; x: -67.039; y: 173.818 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -67; y: 173.601 }
- PathCubic { control1X: -67; control1Y: 173.601; control2X: -63.4; control2Y: 178.801; x: -59.8; y: 178.801 }
- PathCubic { control1X: -56.2; control1Y: 178.801; control2X: -55.818; control2Y: 178.388; x: -53; y: 179.001 }
- PathCubic { control1X: -48.4; control1Y: 180.001; control2X: -48.8; control2Y: 178.001; x: -42.2; y: 179.201 }
- PathCubic { control1X: -39.56; control1Y: 179.681; control2X: -37; control2Y: 178.801; x: -34.2; y: 180.001 }
- PathCubic { control1X: -31.4; control1Y: 181.201; control2X: -28.2; control2Y: 180.401; x: -27; y: 178.401 }
- PathCubic { control1X: -25.8; control1Y: 176.401; control2X: -21; control2Y: 172.201; x: -21; y: 172.201 }
- PathCubic { control1X: -21; control1Y: 172.201; control2X: -33.8; control2Y: 174.001; x: -36.6; y: 174.801 }
- PathCubic { control1X: -36.6; control1Y: 174.801; control2X: -59; control2Y: 176.001; x: -67; y: 173.601 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -22.4; y: 173.801 }
- PathCubic { control1X: -22.4; control1Y: 173.801; control2X: -28.85; control2Y: 177.301; x: -29.25; y: 179.701 }
- PathCubic { control1X: -29.65; control1Y: 182.101; control2X: -24; control2Y: 185.801; x: -24; y: 185.801 }
- PathCubic { control1X: -24; control1Y: 185.801; control2X: -21.25; control2Y: 190.401; x: -20.65; y: 188.001 }
- PathCubic { control1X: -20.05; control1Y: 185.601; control2X: -21.6; control2Y: 174.201; x: -22.4; y: 173.801 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -59.885; y: 179.265 }
- PathCubic { control1X: -59.885; control1Y: 179.265; control2X: -52.878; control2Y: 190.453; x: -52.661; y: 179.242 }
- PathCubic { control1X: -52.661; control1Y: 179.242; control2X: -52.104; control2Y: 177.984; x: -53.864; y: 177.962 }
- PathCubic { control1X: -59.939; control1Y: 177.886; control2X: -58.418; control2Y: 173.784; x: -59.885; y: 179.265 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -52.707; y: 179.514 }
- PathCubic { control1X: -52.707; control1Y: 179.514; control2X: -44.786; control2Y: 190.701; x: -45.422; y: 179.421 }
- PathCubic { control1X: -45.422; control1Y: 179.421; control2X: -45.415; control2Y: 179.089; x: -47.168; y: 178.936 }
- PathCubic { control1X: -51.915; control1Y: 178.522; control2X: -51.57; control2Y: 174.004; x: -52.707; y: 179.514 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -45.494; y: 179.522 }
- PathCubic { control1X: -45.494; control1Y: 179.522; control2X: -37.534; control2Y: 190.15; x: -38.203; y: 180.484 }
- PathCubic { control1X: -38.203; control1Y: 180.484; control2X: -38.084; control2Y: 179.251; x: -39.738; y: 178.95 }
- PathCubic { control1X: -43.63; control1Y: 178.244; control2X: -43.841; control2Y: 174.995; x: -45.494; y: 179.522 }
- }
-
- ShapePath {
- fillColor: "#ffffcc"
- strokeColor: "#000000"
- strokeWidth: 0.5
- PathMove { x: -38.618; y: 179.602 }
- PathCubic { control1X: -38.618; control1Y: 179.602; control2X: -30.718; control2Y: 191.163; x: -30.37; y: 181.382 }
- PathCubic { control1X: -30.37; control1Y: 181.382; control2X: -28.726; control2Y: 180.004; x: -30.472; y: 179.782 }
- PathCubic { control1X: -36.29; control1Y: 179.042; control2X: -35.492; control2Y: 174.588; x: -38.618; y: 179.602 }
- }
-
- ShapePath {
- fillColor: "#e5e5b2"
- strokeWidth: -1
- PathMove { x: -74.792; y: 183.132 }
- PathLine { x: -82.45; y: 181.601 }
- PathCubic { control1X: -85.05; control1Y: 176.601; control2X: -87.15; control2Y: 170.451; x: -87.15; y: 170.451 }
- PathCubic { control1X: -87.15; control1Y: 170.451; control2X: -80.8; control2Y: 171.451; x: -68.3; y: 174.251 }
- PathCubic { control1X: -68.3; control1Y: 174.251; control2X: -67.424; control2Y: 177.569; x: -65.952; y: 183.364 }
- PathLine { x: -74.792; y: 183.132 }
- }
-
- ShapePath {
- fillColor: "#e5e5b2"
- strokeWidth: -1
- PathMove { x: -9.724; y: 178.47 }
- PathCubic { control1X: -11.39; control1Y: 175.964; control2X: -12.707; control2Y: 174.206; x: -13.357; y: 173.8 }
- PathCubic { control1X: -16.37; control1Y: 171.917; control2X: -12.227; control2Y: 172.294; x: -11.098; y: 172.294 }
- PathCubic { control1X: -11.098; control1Y: 172.294; control2X: 5.473; control2Y: 172.294; x: 7.356; y: 173.047 }
- PathCubic { control1X: 7.356; control1Y: 173.047; control2X: 7.88; control2Y: 175.289; x: 8.564; y: 178.68 }
- PathCubic { control1X: 8.564; control1Y: 178.68; control2X: -1.524; control2Y: 176.67; x: -9.724; y: 178.47 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: 43.88; y: 40.321 }
- PathCubic { control1X: 71.601; control1Y: 44.281; control2X: 97.121; control2Y: 8.641; x: 98.881; y: -1.04 }
- PathCubic { control1X: 100.641; control1Y: -10.72; control2X: 90.521; control2Y: -22.6; x: 90.521; y: -22.6 }
- PathCubic { control1X: 91.841; control1Y: -25.68; control2X: 87.001; control2Y: -39.76; x: 81.721; y: -49 }
- PathCubic { control1X: 76.441; control1Y: -58.24; control2X: 60.54; control2Y: -57.266; x: 43; y: -58.24 }
- PathCubic { control1X: 27.16; control1Y: -59.12; control2X: 8.68; control2Y: -35.8; x: 7.36; y: -34.04 }
- PathCubic { control1X: 6.04; control1Y: -32.28; control2X: 12.2; control2Y: 6.001; x: 13.52; y: 11.721 }
- PathCubic { control1X: 14.84; control1Y: 17.441; control2X: 12.2; control2Y: 43.841; x: 12.2; y: 43.841 }
- PathCubic { control1X: 46.44; control1Y: 34.741; control2X: 16.16; control2Y: 36.361; x: 43.88; y: 40.321 }
- }
-
- ShapePath {
- fillColor: "#ea8e51"
- strokeWidth: -1
- PathMove { x: 8.088; y: -33.392 }
- PathCubic { control1X: 6.792; control1Y: -31.664; control2X: 12.84; control2Y: 5.921; x: 14.136; y: 11.537 }
- PathCubic { control1X: 15.432; control1Y: 17.153; control2X: 12.84; control2Y: 43.073; x: 12.84; y: 43.073 }
- PathCubic { control1X: 45.512; control1Y: 34.193; control2X: 16.728; control2Y: 35.729; x: 43.944; y: 39.617 }
- PathCubic { control1X: 71.161; control1Y: 43.505; control2X: 96.217; control2Y: 8.513; x: 97.945; y: -0.992 }
- PathCubic { control1X: 99.673; control1Y: -10.496; control2X: 89.737; control2Y: -22.16; x: 89.737; y: -22.16 }
- PathCubic { control1X: 91.033; control1Y: -25.184; control2X: 86.281; control2Y: -39.008; x: 81.097; y: -48.08 }
- PathCubic { control1X: 75.913; control1Y: -57.152; control2X: 60.302; control2Y: -56.195; x: 43.08; y: -57.152 }
- PathCubic { control1X: 27.528; control1Y: -58.016; control2X: 9.384; control2Y: -35.12; x: 8.088; y: -33.392 }
- }
-
- ShapePath {
- fillColor: "#efaa7c"
- strokeWidth: -1
- PathMove { x: 8.816; y: -32.744 }
- PathCubic { control1X: 7.544; control1Y: -31.048; control2X: 13.48; control2Y: 5.841; x: 14.752; y: 11.353 }
- PathCubic { control1X: 16.024; control1Y: 16.865; control2X: 13.48; control2Y: 42.305; x: 13.48; y: 42.305 }
- PathCubic { control1X: 44.884; control1Y: 33.145; control2X: 17.296; control2Y: 35.097; x: 44.008; y: 38.913 }
- PathCubic { control1X: 70.721; control1Y: 42.729; control2X: 95.313; control2Y: 8.385; x: 97.009; y: -0.944 }
- PathCubic { control1X: 98.705; control1Y: -10.272; control2X: 88.953; control2Y: -21.72; x: 88.953; y: -21.72 }
- PathCubic { control1X: 90.225; control1Y: -24.688; control2X: 85.561; control2Y: -38.256; x: 80.473; y: -47.16 }
- PathCubic { control1X: 75.385; control1Y: -56.064; control2X: 60.063; control2Y: -55.125; x: 43.16; y: -56.064 }
- PathCubic { control1X: 27.896; control1Y: -56.912; control2X: 10.088; control2Y: -34.44; x: 8.816; y: -32.744 }
- }
-
- ShapePath {
- fillColor: "#f4c6a8"
- strokeWidth: -1
- PathMove { x: 9.544; y: -32.096 }
- PathCubic { control1X: 8.296; control1Y: -30.432; control2X: 14.12; control2Y: 5.761; x: 15.368; y: 11.169 }
- PathCubic { control1X: 16.616; control1Y: 16.577; control2X: 14.12; control2Y: 41.537; x: 14.12; y: 41.537 }
- PathCubic { control1X: 43.556; control1Y: 32.497; control2X: 17.864; control2Y: 34.465; x: 44.072; y: 38.209 }
- PathCubic { control1X: 70.281; control1Y: 41.953; control2X: 94.409; control2Y: 8.257; x: 96.073; y: -0.895 }
- PathCubic { control1X: 97.737; control1Y: -10.048; control2X: 88.169; control2Y: -21.28; x: 88.169; y: -21.28 }
- PathCubic { control1X: 89.417; control1Y: -24.192; control2X: 84.841; control2Y: -37.504; x: 79.849; y: -46.24 }
- PathCubic { control1X: 74.857; control1Y: -54.976; control2X: 59.824; control2Y: -54.055; x: 43.24; y: -54.976 }
- PathCubic { control1X: 28.264; control1Y: -55.808; control2X: 10.792; control2Y: -33.76; x: 9.544; y: -32.096 }
- }
-
- ShapePath {
- fillColor: "#f9e2d3"
- strokeWidth: -1
- PathMove { x: 10.272; y: -31.448 }
- PathCubic { control1X: 9.048; control1Y: -29.816; control2X: 14.76; control2Y: 5.681; x: 15.984; y: 10.985 }
- PathCubic { control1X: 17.208; control1Y: 16.289; control2X: 14.76; control2Y: 40.769; x: 14.76; y: 40.769 }
- PathCubic { control1X: 42.628; control1Y: 31.849; control2X: 18.432; control2Y: 33.833; x: 44.136; y: 37.505 }
- PathCubic { control1X: 69.841; control1Y: 41.177; control2X: 93.505; control2Y: 8.129; x: 95.137; y: -0.848 }
- PathCubic { control1X: 96.769; control1Y: -9.824; control2X: 87.385; control2Y: -20.84; x: 87.385; y: -20.84 }
- PathCubic { control1X: 88.609; control1Y: -23.696; control2X: 84.121; control2Y: -36.752; x: 79.225; y: -45.32 }
- PathCubic { control1X: 74.329; control1Y: -53.888; control2X: 59.585; control2Y: -52.985; x: 43.32; y: -53.888 }
- PathCubic { control1X: 28.632; control1Y: -54.704; control2X: 11.496; control2Y: -33.08; x: 10.272; y: -31.448 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: 44.2; y: 36.8 }
- PathCubic { control1X: 69.4; control1Y: 40.4; control2X: 92.601; control2Y: 8; x: 94.201; y: -0.8 }
- PathCubic { control1X: 95.801; control1Y: -9.6; control2X: 86.601; control2Y: -20.4; x: 86.601; y: -20.4 }
- PathCubic { control1X: 87.801; control1Y: -23.2; control2X: 83.4; control2Y: -36; x: 78.6; y: -44.4 }
- PathCubic { control1X: 73.8; control1Y: -52.8; control2X: 59.346; control2Y: -51.914; x: 43.4; y: -52.8 }
- PathCubic { control1X: 29; control1Y: -53.6; control2X: 12.2; control2Y: -32.4; x: 11; y: -30.8 }
- PathCubic { control1X: 9.8; control1Y: -29.2; control2X: 15.4; control2Y: 5.6; x: 16.6; y: 10.8 }
- PathCubic { control1X: 17.8; control1Y: 16; control2X: 15.4; control2Y: 40; x: 15.4; y: 40 }
- PathCubic { control1X: 40.9; control1Y: 31.4; control2X: 19; control2Y: 33.2; x: 44.2; y: 36.8 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 90.601; y: 2.8 }
- PathCubic { control1X: 90.601; control1Y: 2.8; control2X: 62.8; control2Y: 10.4; x: 51.2; y: 8.8 }
- PathCubic { control1X: 51.2; control1Y: 8.8; control2X: 35.4; control2Y: 2.2; x: 26.6; y: 24 }
- PathCubic { control1X: 26.6; control1Y: 24; control2X: 23; control2Y: 31.2; x: 21; y: 33.2 }
- PathCubic { control1X: 19; control1Y: 35.2; control2X: 90.601; control2Y: 2.8; x: 90.601; y: 2.8 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 94.401; y: 0.6 }
- PathCubic { control1X: 94.401; control1Y: 0.6; control2X: 65.4; control2Y: 12.8; x: 55.4; y: 12.4 }
- PathCubic { control1X: 55.4; control1Y: 12.4; control2X: 39; control2Y: 7.8; x: 30.6; y: 22.4 }
- PathCubic { control1X: 30.6; control1Y: 22.4; control2X: 22.2; control2Y: 31.6; x: 19; y: 33.2 }
- PathCubic { control1X: 19; control1Y: 33.2; control2X: 18.6; control2Y: 34.8; x: 25; y: 30.8 }
- PathLine { x: 35.4; y: 36 }
- PathCubic { control1X: 35.4; control1Y: 36; control2X: 50.2; control2Y: 45.6; x: 59.8; y: 29.6 }
- PathCubic { control1X: 59.8; control1Y: 29.6; control2X: 63.8; control2Y: 18.4; x: 63.8; y: 16.4 }
- PathCubic { control1X: 63.8; control1Y: 14.4; control2X: 85; control2Y: 8.8; x: 86.601; y: 8.4 }
- PathCubic { control1X: 88.201; control1Y: 8; control2X: 94.801; control2Y: 3.8; x: 94.401; y: 0.6 }
- }
-
- ShapePath {
- fillColor: "#99cc32"
- strokeWidth: -1
- PathMove { x: 47; y: 36.514 }
- PathCubic { control1X: 40.128; control1Y: 36.514; control2X: 31.755; control2Y: 32.649; x: 31.755; y: 26.4 }
- PathCubic { control1X: 31.755; control1Y: 20.152; control2X: 40.128; control2Y: 13.887; x: 47; y: 13.887 }
- PathCubic { control1X: 53.874; control1Y: 13.887; control2X: 59.446; control2Y: 18.952; x: 59.446; y: 25.2 }
- PathCubic { control1X: 59.446; control1Y: 31.449; control2X: 53.874; control2Y: 36.514; x: 47; y: 36.514 }
- }
-
- ShapePath {
- fillColor: "#659900"
- strokeWidth: -1
- PathMove { x: 43.377; y: 19.83 }
- PathCubic { control1X: 38.531; control1Y: 20.552; control2X: 33.442; control2Y: 22.055; x: 33.514; y: 21.839 }
- PathCubic { control1X: 35.054; control1Y: 17.22; control2X: 41.415; control2Y: 13.887; x: 47; y: 13.887 }
- PathCubic { control1X: 51.296; control1Y: 13.887; control2X: 55.084; control2Y: 15.865; x: 57.32; y: 18.875 }
- PathCubic { control1X: 57.32; control1Y: 18.875; control2X: 52.004; control2Y: 18.545; x: 43.377; y: 19.83 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: 55.4; y: 19.6 }
- PathCubic { control1X: 55.4; control1Y: 19.6; control2X: 51; control2Y: 16.4; x: 51; y: 18.6 }
- PathCubic { control1X: 51; control1Y: 18.6; control2X: 54.6; control2Y: 23; x: 55.4; y: 19.6 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 45.4; y: 27.726 }
- PathCubic { control1X: 42.901; control1Y: 27.726; control2X: 40.875; control2Y: 25.7; x: 40.875; y: 23.2 }
- PathCubic { control1X: 40.875; control1Y: 20.701; control2X: 42.901; control2Y: 18.675; x: 45.4; y: 18.675 }
- PathCubic { control1X: 47.9; control1Y: 18.675; control2X: 49.926; control2Y: 20.701; x: 49.926; y: 23.2 }
- PathCubic { control1X: 49.926; control1Y: 25.7; control2X: 47.9; control2Y: 27.726; x: 45.4; y: 27.726 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: -58.6; y: 14.4 }
- PathCubic { control1X: -58.6; control1Y: 14.4; control2X: -61.8; control2Y: -6.8; x: -59.4; y: -11.2 }
- PathCubic { control1X: -59.4; control1Y: -11.2; control2X: -48.6; control2Y: -21.2; x: -49; y: -24.8 }
- PathCubic { control1X: -49; control1Y: -24.8; control2X: -49.4; control2Y: -42.8; x: -50.6; y: -43.6 }
- PathCubic { control1X: -51.8; control1Y: -44.4; control2X: -59.4; control2Y: -50.4; x: -65.4; y: -44 }
- PathCubic { control1X: -65.4; control1Y: -44; control2X: -75.8; control2Y: -26; x: -75; y: -19.6 }
- PathLine { x: -75; y: -17.6 }
- PathCubic { control1X: -75; control1Y: -17.6; control2X: -82.6; control2Y: -18; x: -84.2; y: -16 }
- PathCubic { control1X: -84.2; control1Y: -16; control2X: -85.4; control2Y: -10.8; x: -86.6; y: -10.4 }
- PathCubic { control1X: -86.6; control1Y: -10.4; control2X: -89.4; control2Y: -8; x: -87.4; y: -5.2 }
- PathCubic { control1X: -87.4; control1Y: -5.2; control2X: -89.4; control2Y: -2.8; x: -89; y: 1.2 }
- PathLine { x: -81.4; y: 5.2 }
- PathCubic { control1X: -81.4; control1Y: 5.2; control2X: -79.4; control2Y: 19.6; x: -68.6; y: 24.8 }
- PathCubic { control1X: -63.764; control1Y: 27.129; control2X: -60.6; control2Y: 20.4; x: -58.6; y: 14.4 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: -59.6; y: 12.56 }
- PathCubic { control1X: -59.6; control1Y: 12.56; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 }
- PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 }
- PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 }
- PathCubic { control1X: -53.48; control1Y: -40.36; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 }
- PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 }
- PathLine { x: -74.36; y: -16.24 }
- PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 }
- PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 }
- PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 }
- PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 }
- PathLine { x: -80.12; y: 4.28 }
- PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 }
- PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.96; x: -59.6; y: 12.56 }
- }
-
- ShapePath {
- fillColor: "#eb955c"
- strokeWidth: -1
- PathMove { x: -51.05; y: -42.61 }
- PathCubic { control1X: -52.14; control1Y: -43.47; control2X: -59.63; control2Y: -49.24; x: -65.48; y: -43 }
- PathCubic { control1X: -65.48; control1Y: -43; control2X: -75.62; control2Y: -25.45; x: -74.84; y: -19.21 }
- PathLine { x: -74.84; y: -17.26 }
- PathCubic { control1X: -74.84; control1Y: -17.26; control2X: -82.25; control2Y: -17.65; x: -83.81; y: -15.7 }
- PathCubic { control1X: -83.81; control1Y: -15.7; control2X: -84.98; control2Y: -10.63; x: -86.15; y: -10.24 }
- PathCubic { control1X: -86.15; control1Y: -10.24; control2X: -88.88; control2Y: -7.9; x: -86.93; y: -5.17 }
- PathCubic { control1X: -86.93; control1Y: -5.17; control2X: -88.88; control2Y: -2.83; x: -88.49; y: 1.07 }
- PathLine { x: -81.08; y: 4.97 }
- PathCubic { control1X: -81.08; control1Y: 4.97; control2X: -79.13; control2Y: 19.01; x: -68.6; y: 24.08 }
- PathCubic { control1X: -63.886; control1Y: 26.35; control2X: -60.8; control2Y: 19.79; x: -58.85; y: 13.94 }
- PathCubic { control1X: -58.85; control1Y: 13.94; control2X: -61.97; control2Y: -6.73; x: -59.63; y: -11.02 }
- PathCubic { control1X: -59.63; control1Y: -11.02; control2X: -49.1; control2Y: -20.77; x: -49.49; y: -24.28 }
- PathCubic { control1X: -49.49; control1Y: -24.28; control2X: -49.88; control2Y: -41.83; x: -51.05; y: -42.61 }
- }
-
- ShapePath {
- fillColor: "#f2b892"
- strokeWidth: -1
- PathMove { x: -51.5; y: -41.62 }
- PathCubic { control1X: -52.48; control1Y: -42.54; control2X: -59.86; control2Y: -48.08; x: -65.56; y: -42 }
- PathCubic { control1X: -65.56; control1Y: -42; control2X: -75.44; control2Y: -24.9; x: -74.68; y: -18.82 }
- PathLine { x: -74.68; y: -16.92 }
- PathCubic { control1X: -74.68; control1Y: -16.92; control2X: -81.9; control2Y: -17.3; x: -83.42; y: -15.4 }
- PathCubic { control1X: -83.42; control1Y: -15.4; control2X: -84.56; control2Y: -10.46; x: -85.7; y: -10.08 }
- PathCubic { control1X: -85.7; control1Y: -10.08; control2X: -88.36; control2Y: -7.8; x: -86.46; y: -5.14 }
- PathCubic { control1X: -86.46; control1Y: -5.14; control2X: -88.36; control2Y: -2.86; x: -87.98; y: 0.94 }
- PathLine { x: -80.76; y: 4.74 }
- PathCubic { control1X: -80.76; control1Y: 4.74; control2X: -78.86; control2Y: 18.42; x: -68.6; y: 23.36 }
- PathCubic { control1X: -64.006; control1Y: 25.572; control2X: -61; control2Y: 19.18; x: -59.1; y: 13.48 }
- PathCubic { control1X: -59.1; control1Y: 13.48; control2X: -62.14; control2Y: -6.66; x: -59.86; y: -10.84 }
- PathCubic { control1X: -59.86; control1Y: -10.84; control2X: -49.6; control2Y: -20.34; x: -49.98; y: -23.76 }
- PathCubic { control1X: -49.98; control1Y: -23.76; control2X: -50.36; control2Y: -40.86; x: -51.5; y: -41.62 }
- }
-
- ShapePath {
- fillColor: "#f8dcc8"
- strokeWidth: -1
- PathMove { x: -51.95; y: -40.63 }
- PathCubic { control1X: -52.82; control1Y: -41.61; control2X: -60.09; control2Y: -46.92; x: -65.64; y: -41 }
- PathCubic { control1X: -65.64; control1Y: -41; control2X: -75.26; control2Y: -24.35; x: -74.52; y: -18.43 }
- PathLine { x: -74.52; y: -16.58 }
- PathCubic { control1X: -74.52; control1Y: -16.58; control2X: -81.55; control2Y: -16.95; x: -83.03; y: -15.1 }
- PathCubic { control1X: -83.03; control1Y: -15.1; control2X: -84.14; control2Y: -10.29; x: -85.25; y: -9.92 }
- PathCubic { control1X: -85.25; control1Y: -9.92; control2X: -87.84; control2Y: -7.7; x: -85.99; y: -5.11 }
- PathCubic { control1X: -85.99; control1Y: -5.11; control2X: -87.84; control2Y: -2.89; x: -87.47; y: 0.81 }
- PathLine { x: -80.44; y: 4.51 }
- PathCubic { control1X: -80.44; control1Y: 4.51; control2X: -78.59; control2Y: 17.83; x: -68.6; y: 22.64 }
- PathCubic { control1X: -64.127; control1Y: 24.794; control2X: -61.2; control2Y: 18.57; x: -59.35; y: 13.02 }
- PathCubic { control1X: -59.35; control1Y: 13.02; control2X: -62.31; control2Y: -6.59; x: -60.09; y: -10.66 }
- PathCubic { control1X: -60.09; control1Y: -10.66; control2X: -50.1; control2Y: -19.91; x: -50.47; y: -23.24 }
- PathCubic { control1X: -50.47; control1Y: -23.24; control2X: -50.84; control2Y: -39.89; x: -51.95; y: -40.63 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: -59.6; y: 12.46 }
- PathCubic { control1X: -59.6; control1Y: 12.46; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 }
- PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 }
- PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 }
- PathCubic { control1X: -53.16; control1Y: -40.68; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 }
- PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 }
- PathLine { x: -74.36; y: -16.24 }
- PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 }
- PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 }
- PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 }
- PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 }
- PathLine { x: -80.12; y: 4.28 }
- PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 }
- PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.86; x: -59.6; y: 12.46 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -62.7; y: 6.2 }
- PathCubic { control1X: -62.7; control1Y: 6.2; control2X: -84.3; control2Y: -4; x: -85.2; y: -4.8 }
- PathCubic { control1X: -85.2; control1Y: -4.8; control2X: -76.1; control2Y: 3.4; x: -75.3; y: 3.4 }
- PathCubic { control1X: -74.5; control1Y: 3.4; control2X: -62.7; control2Y: 6.2; x: -62.7; y: 6.2 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -79.8; y: 0 }
- PathCubic { control1X: -79.8; control1Y: 0; control2X: -61.4; control2Y: 3.6; x: -61.4; y: 8 }
- PathCubic { control1X: -61.4; control1Y: 10.912; control2X: -61.643; control2Y: 24.331; x: -67; y: 22.8 }
- PathCubic { control1X: -75.4; control1Y: 20.4; control2X: -71.8; control2Y: 6; x: -79.8; y: 0 }
- }
-
- ShapePath {
- fillColor: "#99cc32"
- strokeWidth: -1
- PathMove { x: -71.4; y: 3.8 }
- PathCubic { control1X: -71.4; control1Y: 3.8; control2X: -62.422; control2Y: 5.274; x: -61.4; y: 8 }
- PathCubic { control1X: -60.8; control1Y: 9.6; control2X: -60.137; control2Y: 17.908; x: -65.6; y: 19 }
- PathCubic { control1X: -70.152; control1Y: 19.911; control2X: -72.382; control2Y: 9.69; x: -71.4; y: 3.8 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 14.595; y: 46.349 }
- PathCubic { control1X: 14.098; control1Y: 44.607; control2X: 15.409; control2Y: 44.738; x: 17.2; y: 44.2 }
- PathCubic { control1X: 19.2; control1Y: 43.6; control2X: 31.4; control2Y: 39.8; x: 32.2; y: 37.2 }
- PathCubic { control1X: 33; control1Y: 34.6; control2X: 46.2; control2Y: 39; x: 46.2; y: 39 }
- PathCubic { control1X: 48; control1Y: 39.8; control2X: 52.4; control2Y: 42.4; x: 52.4; y: 42.4 }
- PathCubic { control1X: 57.2; control1Y: 43.6; control2X: 63.8; control2Y: 44; x: 63.8; y: 44 }
- PathCubic { control1X: 66.2; control1Y: 45; control2X: 69.6; control2Y: 47.8; x: 69.6; y: 47.8 }
- PathCubic { control1X: 84.2; control1Y: 58; control2X: 96.601; control2Y: 50.8; x: 96.601; y: 50.8 }
- PathCubic { control1X: 116.601; control1Y: 44.2; control2X: 110.601; control2Y: 27; x: 110.601; y: 27 }
- PathCubic { control1X: 107.601; control1Y: 18; control2X: 110.801; control2Y: 14.6; x: 110.801; y: 14.6 }
- PathCubic { control1X: 111.001; control1Y: 10.8; control2X: 118.201; control2Y: 17.2; x: 118.201; y: 17.2 }
- PathCubic { control1X: 120.801; control1Y: 21.4; control2X: 121.601; control2Y: 26.4; x: 121.601; y: 26.4 }
- PathCubic { control1X: 129.601; control1Y: 37.6; control2X: 126.201; control2Y: 19.8; x: 126.201; y: 19.8 }
- PathCubic { control1X: 126.401; control1Y: 18.8; control2X: 123.601; control2Y: 15.2; x: 123.601; y: 14 }
- PathCubic { control1X: 123.601; control1Y: 12.8; control2X: 121.801; control2Y: 9.4; x: 121.801; y: 9.4 }
- PathCubic { control1X: 118.801; control1Y: 6; control2X: 121.201; control2Y: -1; x: 121.201; y: -1 }
- PathCubic { control1X: 123.001; control1Y: -14.8; control2X: 120.801; control2Y: -13; x: 120.801; y: -13 }
- PathCubic { control1X: 119.601; control1Y: -14.8; control2X: 110.401; control2Y: -4.8; x: 110.401; y: -4.8 }
- PathCubic { control1X: 108.201; control1Y: -1.4; control2X: 102.201; control2Y: 0.2; x: 102.201; y: 0.2 }
- PathCubic { control1X: 99.401; control1Y: 2; control2X: 96.001; control2Y: 0.6; x: 96.001; y: 0.6 }
- PathCubic { control1X: 93.401; control1Y: 0.2; control2X: 87.801; control2Y: 7.2; x: 87.801; y: 7.2 }
- PathCubic { control1X: 90.601; control1Y: 7; control2X: 93.001; control2Y: 11.4; x: 95.401; y: 11.6 }
- PathCubic { control1X: 97.801; control1Y: 11.8; control2X: 99.601; control2Y: 9.2; x: 101.201; y: 8.6 }
- PathCubic { control1X: 102.801; control1Y: 8; control2X: 105.601; control2Y: 13.8; x: 105.601; y: 13.8 }
- PathCubic { control1X: 106.001; control1Y: 16.4; control2X: 100.401; control2Y: 21.2; x: 100.401; y: 21.2 }
- PathCubic { control1X: 100.001; control1Y: 25.8; control2X: 98.401; control2Y: 24.2; x: 98.401; y: 24.2 }
- PathCubic { control1X: 95.401; control1Y: 23.6; control2X: 94.201; control2Y: 27.4; x: 93.201; y: 32 }
- PathCubic { control1X: 92.201; control1Y: 36.6; control2X: 88.001; control2Y: 37; x: 88.001; y: 37 }
- PathCubic { control1X: 86.401; control1Y: 44.4; control2X: 85.2; control2Y: 41.4; x: 85.2; y: 41.4 }
- PathCubic { control1X: 85; control1Y: 35.8; control2X: 79; control2Y: 41.6; x: 79; y: 41.6 }
- PathCubic { control1X: 77.8; control1Y: 43.6; control2X: 73.2; control2Y: 41.4; x: 73.2; y: 41.4 }
- PathCubic { control1X: 66.4; control1Y: 39.4; control2X: 68.8; control2Y: 37.4; x: 68.8; y: 37.4 }
- PathCubic { control1X: 70.6; control1Y: 35.2; control2X: 81.8; control2Y: 37.4; x: 81.8; y: 37.4 }
- PathCubic { control1X: 84; control1Y: 35.8; control2X: 76; control2Y: 31.8; x: 76; y: 31.8 }
- PathCubic { control1X: 75.4; control1Y: 30; control2X: 76.4; control2Y: 25.6; x: 76.4; y: 25.6 }
- PathCubic { control1X: 77.6; control1Y: 22.4; control2X: 84.4; control2Y: 16.8; x: 84.4; y: 16.8 }
- PathCubic { control1X: 93.801; control1Y: 15.6; control2X: 91.001; control2Y: 14; x: 91.001; y: 14 }
- PathCubic { control1X: 84.801; control1Y: 8.8; control2X: 79; control2Y: 16.4; x: 79; y: 16.4 }
- PathCubic { control1X: 76.8; control1Y: 22.6; control2X: 59.4; control2Y: 37.6; x: 59.4; y: 37.6 }
- PathCubic { control1X: 54.6; control1Y: 41; control2X: 57.2; control2Y: 34.2; x: 53.2; y: 37.6 }
- PathCubic { control1X: 49.2; control1Y: 41; control2X: 28.6; control2Y: 32; x: 28.6; y: 32 }
- PathCubic { control1X: 17.038; control1Y: 30.807; control2X: 14.306; control2Y: 46.549; x: 10.777; y: 43.429 }
- PathCubic { control1X: 10.777; control1Y: 43.429; control2X: 16.195; control2Y: 51.949; x: 14.595; y: 46.349 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 209.401; y: -120 }
- PathCubic { control1X: 209.401; control1Y: -120; control2X: 183.801; control2Y: -112; x: 181.001; y: -93.2 }
- PathCubic { control1X: 181.001; control1Y: -93.2; control2X: 178.601; control2Y: -70.4; x: 199.001; y: -52.8 }
- PathCubic { control1X: 199.001; control1Y: -52.8; control2X: 199.401; control2Y: -46.4; x: 201.401; y: -43.2 }
- PathCubic { control1X: 201.401; control1Y: -43.2; control2X: 199.801; control2Y: -38.4; x: 218.601; y: -46 }
- PathLine { x: 245.801; y: -54.4 }
- PathCubic { control1X: 245.801; control1Y: -54.4; control2X: 252.201; control2Y: -56.8; x: 257.401; y: -65.6 }
- PathCubic { control1X: 262.601; control1Y: -74.4; control2X: 277.801; control2Y: -93.2; x: 274.201; y: -118.4 }
- PathCubic { control1X: 274.201; control1Y: -118.4; control2X: 275.401; control2Y: -129.6; x: 269.401; y: -130 }
- PathCubic { control1X: 269.401; control1Y: -130; control2X: 261.001; control2Y: -131.6; x: 253.801; y: -124 }
- PathCubic { control1X: 253.801; control1Y: -124; control2X: 247.001; control2Y: -120.8; x: 244.601; y: -121.2 }
- PathLine { x: 209.401; y: -120 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 264.022; y: -120.99 }
- PathCubic { control1X: 264.022; control1Y: -120.99; control2X: 266.122; control2Y: -129.92; x: 261.282; y: -125.08 }
- PathCubic { control1X: 261.282; control1Y: -125.08; control2X: 254.242; control2Y: -119.36; x: 246.761; y: -119.36 }
- PathCubic { control1X: 246.761; control1Y: -119.36; control2X: 232.241; control2Y: -117.16; x: 227.841; y: -103.96 }
- PathCubic { control1X: 227.841; control1Y: -103.96; control2X: 223.881; control2Y: -77.12; x: 231.801; y: -71.4 }
- PathCubic { control1X: 231.801; control1Y: -71.4; control2X: 236.641; control2Y: -63.92; x: 243.681; y: -70.52 }
- PathCubic { control1X: 250.722; control1Y: -77.12; control2X: 266.222; control2Y: -107.35; x: 264.022; y: -120.99 }
- }
-
- ShapePath {
- fillColor: "#323232"
- strokeWidth: -1
- PathMove { x: 263.648; y: -120.632 }
- PathCubic { control1X: 263.648; control1Y: -120.632; control2X: 265.738; control2Y: -129.376; x: 260.986; y: -124.624 }
- PathCubic { control1X: 260.986; control1Y: -124.624; control2X: 254.074; control2Y: -119.008; x: 246.729; y: -119.008 }
- PathCubic { control1X: 246.729; control1Y: -119.008; control2X: 232.473; control2Y: -116.848; x: 228.153; y: -103.888 }
- PathCubic { control1X: 228.153; control1Y: -103.888; control2X: 224.265; control2Y: -77.536; x: 232.041; y: -71.92 }
- PathCubic { control1X: 232.041; control1Y: -71.92; control2X: 236.793; control2Y: -64.576; x: 243.705; y: -71.056 }
- PathCubic { control1X: 250.618; control1Y: -77.536; control2X: 265.808; control2Y: -107.24; x: 263.648; y: -120.632 }
- }
-
- ShapePath {
- fillColor: "#666666"
- strokeWidth: -1
- PathMove { x: 263.274; y: -120.274 }
- PathCubic { control1X: 263.274; control1Y: -120.274; control2X: 265.354; control2Y: -128.832; x: 260.69; y: -124.168 }
- PathCubic { control1X: 260.69; control1Y: -124.168; control2X: 253.906; control2Y: -118.656; x: 246.697; y: -118.656 }
- PathCubic { control1X: 246.697; control1Y: -118.656; control2X: 232.705; control2Y: -116.536; x: 228.465; y: -103.816 }
- PathCubic { control1X: 228.465; control1Y: -103.816; control2X: 224.649; control2Y: -77.952; x: 232.281; y: -72.44 }
- PathCubic { control1X: 232.281; control1Y: -72.44; control2X: 236.945; control2Y: -65.232; x: 243.729; y: -71.592 }
- PathCubic { control1X: 250.514; control1Y: -77.952; control2X: 265.394; control2Y: -107.13; x: 263.274; y: -120.274 }
- }
-
- ShapePath {
- fillColor: "#999999"
- strokeWidth: -1
- PathMove { x: 262.9; y: -119.916 }
- PathCubic { control1X: 262.9; control1Y: -119.916; control2X: 264.97; control2Y: -128.288; x: 260.394; y: -123.712 }
- PathCubic { control1X: 260.394; control1Y: -123.712; control2X: 253.738; control2Y: -118.304; x: 246.665; y: -118.304 }
- PathCubic { control1X: 246.665; control1Y: -118.304; control2X: 232.937; control2Y: -116.224; x: 228.777; y: -103.744 }
- PathCubic { control1X: 228.777; control1Y: -103.744; control2X: 225.033; control2Y: -78.368; x: 232.521; y: -72.96 }
- PathCubic { control1X: 232.521; control1Y: -72.96; control2X: 237.097; control2Y: -65.888; x: 243.753; y: -72.128 }
- PathCubic { control1X: 250.41; control1Y: -78.368; control2X: 264.98; control2Y: -107.02; x: 262.9; y: -119.916 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 262.526; y: -119.558 }
- PathCubic { control1X: 262.526; control1Y: -119.558; control2X: 264.586; control2Y: -127.744; x: 260.098; y: -123.256 }
- PathCubic { control1X: 260.098; control1Y: -123.256; control2X: 253.569; control2Y: -117.952; x: 246.633; y: -117.952 }
- PathCubic { control1X: 246.633; control1Y: -117.952; control2X: 233.169; control2Y: -115.912; x: 229.089; y: -103.672 }
- PathCubic { control1X: 229.089; control1Y: -103.672; control2X: 225.417; control2Y: -78.784; x: 232.761; y: -73.48 }
- PathCubic { control1X: 232.761; control1Y: -73.48; control2X: 237.249; control2Y: -66.544; x: 243.777; y: -72.664 }
- PathCubic { control1X: 250.305; control1Y: -78.784; control2X: 264.566; control2Y: -106.91; x: 262.526; y: -119.558 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: 262.151; y: -119.2 }
- PathCubic { control1X: 262.151; control1Y: -119.2; control2X: 264.201; control2Y: -127.2; x: 259.801; y: -122.8 }
- PathCubic { control1X: 259.801; control1Y: -122.8; control2X: 253.401; control2Y: -117.6; x: 246.601; y: -117.6 }
- PathCubic { control1X: 246.601; control1Y: -117.6; control2X: 233.401; control2Y: -115.6; x: 229.401; y: -103.6 }
- PathCubic { control1X: 229.401; control1Y: -103.6; control2X: 225.801; control2Y: -79.2; x: 233.001; y: -74 }
- PathCubic { control1X: 233.001; control1Y: -74; control2X: 237.401; control2Y: -67.2; x: 243.801; y: -73.2 }
- PathCubic { control1X: 250.201; control1Y: -79.2; control2X: 264.151; control2Y: -106.8; x: 262.151; y: -119.2 }
- }
-
- ShapePath {
- fillColor: "#992600"
- strokeWidth: -1
- PathMove { x: 50.6; y: 84 }
- PathCubic { control1X: 50.6; control1Y: 84; control2X: 30.2; control2Y: 64.8; x: 22.2; y: 64 }
- PathCubic { control1X: 22.2; control1Y: 64; control2X: -12.2; control2Y: 60; x: -27; y: 78 }
- PathCubic { control1X: -27; control1Y: 78; control2X: -9.4; control2Y: 57.6; x: 18.2; y: 63.2 }
- PathCubic { control1X: 18.2; control1Y: 63.2; control2X: -3.4; control2Y: 58.8; x: -15.8; y: 62 }
- PathCubic { control1X: -15.8; control1Y: 62; control2X: -32.6; control2Y: 62; x: -42.2; y: 76 }
- PathLine { x: -45; y: 80.8 }
- PathCubic { control1X: -45; control1Y: 80.8; control2X: -41; control2Y: 66; x: -22.6; y: 60 }
- PathCubic { control1X: -22.6; control1Y: 60; control2X: 0.2; control2Y: 55.2; x: 11; y: 60 }
- PathCubic { control1X: 11; control1Y: 60; control2X: -10.6; control2Y: 53.2; x: -20.6; y: 55.2 }
- PathCubic { control1X: -20.6; control1Y: 55.2; control2X: -51; control2Y: 52.8; x: -63.8; y: 79.2 }
- PathCubic { control1X: -63.8; control1Y: 79.2; control2X: -59.8; control2Y: 64.8; x: -45; y: 57.6 }
- PathCubic { control1X: -45; control1Y: 57.6; control2X: -31.4; control2Y: 48.8; x: -11; y: 51.6 }
- PathCubic { control1X: -11; control1Y: 51.6; control2X: 3.4; control2Y: 54.8; x: 8.6; y: 57.2 }
- PathCubic { control1X: 13.8; control1Y: 59.6; control2X: 12.6; control2Y: 56.8; x: 4.2; y: 52 }
- PathCubic { control1X: 4.2; control1Y: 52; control2X: -1.4; control2Y: 42; x: -15.4; y: 42.4 }
- PathCubic { control1X: -15.4; control1Y: 42.4; control2X: -58.2; control2Y: 46; x: -68.6; y: 58 }
- PathCubic { control1X: -68.6; control1Y: 58; control2X: -55; control2Y: 46.8; x: -44.6; y: 44 }
- PathCubic { control1X: -44.6; control1Y: 44; control2X: -22.2; control2Y: 36; x: -13.8; y: 36.8 }
- PathCubic { control1X: -13.8; control1Y: 36.8; control2X: 11; control2Y: 37.8; x: 18.6; y: 33.8 }
- PathCubic { control1X: 18.6; control1Y: 33.8; control2X: 7.4; control2Y: 38.8; x: 10.6; y: 42 }
- PathCubic { control1X: 13.8; control1Y: 45.2; control2X: 20.6; control2Y: 52.8; x: 20.6; y: 54 }
- PathCubic { control1X: 20.6; control1Y: 55.2; control2X: 44.8; control2Y: 77.3; x: 48.4; y: 81.7 }
- PathLine { x: 50.6; y: 84 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 189; y: 278 }
- PathCubic { control1X: 189; control1Y: 278; control2X: 173.5; control2Y: 241.5; x: 161; y: 232 }
- PathCubic { control1X: 161; control1Y: 232; control2X: 187; control2Y: 248; x: 190.5; y: 266 }
- PathCubic { control1X: 190.5; control1Y: 266; control2X: 190.5; control2Y: 276; x: 189; y: 278 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 236; y: 285.5 }
- PathCubic { control1X: 236; control1Y: 285.5; control2X: 209.5; control2Y: 230.5; x: 191; y: 206.5 }
- PathCubic { control1X: 191; control1Y: 206.5; control2X: 234.5; control2Y: 244; x: 239.5; y: 270.5 }
- PathLine { x: 240; y: 276 }
- PathLine { x: 237; y: 273.5 }
- PathCubic { control1X: 237; control1Y: 273.5; control2X: 236.5; control2Y: 282.5; x: 236; y: 285.5 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 292.5; y: 237 }
- PathCubic { control1X: 292.5; control1Y: 237; control2X: 230; control2Y: 177.5; x: 228.5; y: 175 }
- PathCubic { control1X: 228.5; control1Y: 175; control2X: 289; control2Y: 241; x: 292; y: 248.5 }
- PathCubic { control1X: 292; control1Y: 248.5; control2X: 290; control2Y: 239.5; x: 292.5; y: 237 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 104; y: 280.5 }
- PathCubic { control1X: 104; control1Y: 280.5; control2X: 123.5; control2Y: 228.5; x: 142.5; y: 251 }
- PathCubic { control1X: 142.5; control1Y: 251; control2X: 157.5; control2Y: 261; x: 157; y: 264 }
- PathCubic { control1X: 157; control1Y: 264; control2X: 153; control2Y: 257.5; x: 135; y: 258 }
- PathCubic { control1X: 135; control1Y: 258; control2X: 116; control2Y: 255; x: 104; y: 280.5 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 294.5; y: 153 }
- PathCubic { control1X: 294.5; control1Y: 153; control2X: 249.5; control2Y: 124.5; x: 242; y: 123 }
- PathCubic { control1X: 230.193; control1Y: 120.639; control2X: 291.5; control2Y: 152; x: 296.5; y: 162.5 }
- PathCubic { control1X: 296.5; control1Y: 162.5; control2X: 298.5; control2Y: 160; x: 294.5; y: 153 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 143.801; y: 259.601 }
- PathCubic { control1X: 143.801; control1Y: 259.601; control2X: 164.201; control2Y: 257.601; x: 171.001; y: 250.801 }
- PathLine { x: 175.401; y: 254.401 }
- PathLine { x: 193.001; y: 216.001 }
- PathLine { x: 196.601; y: 221.201 }
- PathCubic { control1X: 196.601; control1Y: 221.201; control2X: 211.001; control2Y: 206.401; x: 210.201; y: 198.401 }
- PathCubic { control1X: 209.401; control1Y: 190.401; control2X: 223.001; control2Y: 204.401; x: 223.001; y: 204.401 }
- PathCubic { control1X: 223.001; control1Y: 204.401; control2X: 222.201; control2Y: 192.801; x: 229.401; y: 199.601 }
- PathCubic { control1X: 229.401; control1Y: 199.601; control2X: 227.001; control2Y: 184.001; x: 235.401; y: 192.001 }
- PathCubic { control1X: 235.401; control1Y: 192.001; control2X: 224.864; control2Y: 161.844; x: 247.401; y: 187.601 }
- PathCubic { control1X: 253.001; control1Y: 194.001; control2X: 248.601; control2Y: 187.201; x: 248.601; y: 187.201 }
- PathCubic { control1X: 248.601; control1Y: 187.201; control2X: 222.601; control2Y: 139.201; x: 244.201; y: 153.601 }
- PathCubic { control1X: 244.201; control1Y: 153.601; control2X: 246.201; control2Y: 130.801; x: 245.001; y: 126.401 }
- PathCubic { control1X: 243.801; control1Y: 122.001; control2X: 241.801; control2Y: 99.6; x: 237.001; y: 94.4 }
- PathCubic { control1X: 232.201; control1Y: 89.2; control2X: 237.401; control2Y: 87.6; x: 243.001; y: 92.8 }
- PathCubic { control1X: 243.001; control1Y: 92.8; control2X: 231.801; control2Y: 68.8; x: 245.001; y: 80.8 }
- PathCubic { control1X: 245.001; control1Y: 80.8; control2X: 241.401; control2Y: 65.6; x: 237.001; y: 62.8 }
- PathCubic { control1X: 237.001; control1Y: 62.8; control2X: 231.401; control2Y: 45.6; x: 246.601; y: 56.4 }
- PathCubic { control1X: 246.601; control1Y: 56.4; control2X: 242.201; control2Y: 44; x: 239.001; y: 40.8 }
- PathCubic { control1X: 239.001; control1Y: 40.8; control2X: 227.401; control2Y: 13.2; x: 234.601; y: 18 }
- PathLine { x: 239.001; y: 21.6 }
- PathCubic { control1X: 239.001; control1Y: 21.6; control2X: 232.201; control2Y: 7.6; x: 238.601; y: 12 }
- PathCubic { control1X: 245.001; control1Y: 16.4; control2X: 245.001; control2Y: 16; x: 245.001; y: 16 }
- PathCubic { control1X: 245.001; control1Y: 16; control2X: 223.801; control2Y: -17.2; x: 244.201; y: 0.4 }
- PathCubic { control1X: 244.201; control1Y: 0.4; control2X: 236.042; control2Y: -13.518; x: 232.601; y: -20.4 }
- PathCubic { control1X: 232.601; control1Y: -20.4; control2X: 213.801; control2Y: -40.8; x: 228.201; y: -34.4 }
- PathLine { x: 233.001; y: -32.8 }
- PathCubic { control1X: 233.001; control1Y: -32.8; control2X: 224.201; control2Y: -42.8; x: 216.201; y: -44.4 }
- PathCubic { control1X: 208.201; control1Y: -46; control2X: 218.601; control2Y: -52.4; x: 225.001; y: -50.4 }
- PathCubic { control1X: 231.401; control1Y: -48.4; control2X: 247.001; control2Y: -40.8; x: 247.001; y: -40.8 }
- PathCubic { control1X: 247.001; control1Y: -40.8; control2X: 259.801; control2Y: -22; x: 263.801; y: -21.6 }
- PathCubic { control1X: 263.801; control1Y: -21.6; control2X: 243.801; control2Y: -29.2; x: 249.801; y: -21.2 }
- PathCubic { control1X: 249.801; control1Y: -21.2; control2X: 264.201; control2Y: -7.2; x: 257.001; y: -7.6 }
- PathCubic { control1X: 257.001; control1Y: -7.6; control2X: 251.001; control2Y: -0.4; x: 255.801; y: 8.4 }
- PathCubic { control1X: 255.801; control1Y: 8.4; control2X: 237.342; control2Y: -9.991; x: 252.201; y: 15.6 }
- PathLine { x: 259.001; y: 32 }
- PathCubic { control1X: 259.001; control1Y: 32; control2X: 234.601; control2Y: 7.2; x: 245.801; y: 29.2 }
- PathCubic { control1X: 245.801; control1Y: 29.2; control2X: 263.001; control2Y: 52.8; x: 265.001; y: 53.2 }
- PathCubic { control1X: 267.001; control1Y: 53.6; control2X: 271.401; control2Y: 62.4; x: 271.401; y: 62.4 }
- PathLine { x: 267.001; y: 60.4 }
- PathLine { x: 272.201; y: 69.2 }
- PathCubic { control1X: 272.201; control1Y: 69.2; control2X: 261.001; control2Y: 57.2; x: 267.001; y: 70.4 }
- PathLine { x: 272.601; y: 84.8 }
- PathCubic { control1X: 272.601; control1Y: 84.8; control2X: 252.201; control2Y: 62.8; x: 265.801; y: 92.4 }
- PathCubic { control1X: 265.801; control1Y: 92.4; control2X: 249.401; control2Y: 87.2; x: 258.201; y: 104.4 }
- PathCubic { control1X: 258.201; control1Y: 104.4; control2X: 256.601; control2Y: 120.401; x: 257.001; y: 125.601 }
- PathCubic { control1X: 257.401; control1Y: 130.801; control2X: 258.601; control2Y: 159.201; x: 254.201; y: 167.201 }
- PathCubic { control1X: 249.801; control1Y: 175.201; control2X: 260.201; control2Y: 194.401; x: 262.201; y: 198.401 }
- PathCubic { control1X: 264.201; control1Y: 202.401; control2X: 267.801; control2Y: 213.201; x: 259.001; y: 204.001 }
- PathCubic { control1X: 250.201; control1Y: 194.801; control2X: 254.601; control2Y: 200.401; x: 256.601; y: 209.201 }
- PathCubic { control1X: 258.601; control1Y: 218.001; control2X: 264.601; control2Y: 233.601; x: 263.801; y: 239.201 }
- PathCubic { control1X: 263.801; control1Y: 239.201; control2X: 262.601; control2Y: 240.401; x: 259.401; y: 236.801 }
- PathCubic { control1X: 259.401; control1Y: 236.801; control2X: 244.601; control2Y: 214.001; x: 246.201; y: 228.401 }
- PathCubic { control1X: 246.201; control1Y: 228.401; control2X: 245.001; control2Y: 236.401; x: 241.801; y: 245.201 }
- PathCubic { control1X: 241.801; control1Y: 245.201; control2X: 238.601; control2Y: 256.001; x: 238.601; y: 247.201 }
- PathCubic { control1X: 238.601; control1Y: 247.201; control2X: 235.401; control2Y: 230.401; x: 232.601; y: 238.001 }
- PathCubic { control1X: 229.801; control1Y: 245.601; control2X: 226.201; control2Y: 251.601; x: 223.401; y: 254.001 }
- PathCubic { control1X: 220.601; control1Y: 256.401; control2X: 215.401; control2Y: 233.601; x: 214.201; y: 244.001 }
- PathCubic { control1X: 214.201; control1Y: 244.001; control2X: 202.201; control2Y: 231.601; x: 197.401; y: 248.001 }
- PathLine { x: 185.801; y: 264.401 }
- PathCubic { control1X: 185.801; control1Y: 264.401; control2X: 185.401; control2Y: 252.001; x: 184.201; y: 258.001 }
- PathCubic { control1X: 184.201; control1Y: 258.001; control2X: 154.201; control2Y: 264.001; x: 143.801; y: 259.601 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 109.401; y: -97.2 }
- PathCubic { control1X: 109.401; control1Y: -97.2; control2X: 97.801; control2Y: -105.2; x: 93.801; y: -104.8 }
- PathCubic { control1X: 89.801; control1Y: -104.4; control2X: 121.401; control2Y: -113.6; x: 162.601; y: -86 }
- PathCubic { control1X: 162.601; control1Y: -86; control2X: 167.401; control2Y: -83.2; x: 171.001; y: -83.6 }
- PathCubic { control1X: 171.001; control1Y: -83.6; control2X: 174.201; control2Y: -81.2; x: 171.401; y: -77.6 }
- PathCubic { control1X: 171.401; control1Y: -77.6; control2X: 162.601; control2Y: -68; x: 173.801; y: -56.8 }
- PathCubic { control1X: 173.801; control1Y: -56.8; control2X: 192.201; control2Y: -50; x: 186.601; y: -58.8 }
- PathCubic { control1X: 186.601; control1Y: -58.8; control2X: 197.401; control2Y: -54.8; x: 199.801; y: -50.8 }
- PathCubic { control1X: 202.201; control1Y: -46.8; control2X: 201.001; control2Y: -50.8; x: 201.001; y: -50.8 }
- PathCubic { control1X: 201.001; control1Y: -50.8; control2X: 194.601; control2Y: -58; x: 188.601; y: -63.2 }
- PathCubic { control1X: 188.601; control1Y: -63.2; control2X: 183.401; control2Y: -65.2; x: 180.601; y: -73.6 }
- PathCubic { control1X: 177.801; control1Y: -82; control2X: 175.401; control2Y: -92; x: 179.801; y: -95.2 }
- PathCubic { control1X: 179.801; control1Y: -95.2; control2X: 175.801; control2Y: -90.8; x: 176.601; y: -94.8 }
- PathCubic { control1X: 177.401; control1Y: -98.8; control2X: 181.001; control2Y: -102.4; x: 182.601; y: -102.8 }
- PathCubic { control1X: 184.201; control1Y: -103.2; control2X: 200.601; control2Y: -119; x: 207.401; y: -119.4 }
- PathCubic { control1X: 207.401; control1Y: -119.4; control2X: 198.201; control2Y: -118; x: 195.201; y: -119 }
- PathCubic { control1X: 192.201; control1Y: -120; control2X: 165.601; control2Y: -131.4; x: 159.601; y: -132.6 }
- PathCubic { control1X: 159.601; control1Y: -132.6; control2X: 142.801; control2Y: -139.2; x: 154.801; y: -137.2 }
- PathCubic { control1X: 154.801; control1Y: -137.2; control2X: 190.601; control2Y: -133.4; x: 208.801; y: -120.2 }
- PathCubic { control1X: 208.801; control1Y: -120.2; control2X: 201.601; control2Y: -128.6; x: 183.201; y: -135.6 }
- PathCubic { control1X: 183.201; control1Y: -135.6; control2X: 161.001; control2Y: -148.2; x: 125.801; y: -143.2 }
- PathCubic { control1X: 125.801; control1Y: -143.2; control2X: 108.001; control2Y: -140; x: 100.201; y: -138.2 }
- PathCubic { control1X: 100.201; control1Y: -138.2; control2X: 97.601; control2Y: -138.8; x: 97.001; y: -139.2 }
- PathCubic { control1X: 96.401; control1Y: -139.6; control2X: 84.6; control2Y: -148.6; x: 57; y: -141.6 }
- PathCubic { control1X: 57; control1Y: -141.6; control2X: 40; control2Y: -137; x: 31.4; y: -132.2 }
- PathCubic { control1X: 31.4; control1Y: -132.2; control2X: 16.2; control2Y: -131; x: 12.6; y: -127.8 }
- PathCubic { control1X: 12.6; control1Y: -127.8; control2X: -6; control2Y: -113.2; x: -8; y: -112.4 }
- PathCubic { control1X: -10; control1Y: -111.6; control2X: -21.4; control2Y: -104; x: -22.2; y: -103.6 }
- PathCubic { control1X: -22.2; control1Y: -103.6; control2X: 2.4; control2Y: -110.2; x: 4.8; y: -112.6 }
- PathCubic { control1X: 7.2; control1Y: -115; control2X: 24.6; control2Y: -117.6; x: 27; y: -116.2 }
- PathCubic { control1X: 29.4; control1Y: -114.8; control2X: 37.8; control2Y: -115.4; x: 28.2; y: -114.8 }
- PathCubic { control1X: 28.2; control1Y: -114.8; control2X: 103.801; control2Y: -100; x: 104.601; y: -98 }
- PathCubic { control1X: 105.401; control1Y: -96; control2X: 109.401; control2Y: -97.2; x: 109.401; y: -97.2 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: 180.801; y: -106.4 }
- PathCubic { control1X: 180.801; control1Y: -106.4; control2X: 170.601; control2Y: -113.8; x: 168.601; y: -113.8 }
- PathCubic { control1X: 166.601; control1Y: -113.8; control2X: 154.201; control2Y: -124; x: 150.001; y: -123.6 }
- PathCubic { control1X: 145.801; control1Y: -123.2; control2X: 133.601; control2Y: -133.2; x: 106.201; y: -125 }
- PathCubic { control1X: 106.201; control1Y: -125; control2X: 105.601; control2Y: -127; x: 109.201; y: -127.8 }
- PathCubic { control1X: 109.201; control1Y: -127.8; control2X: 115.601; control2Y: -130; x: 116.001; y: -130.6 }
- PathCubic { control1X: 116.001; control1Y: -130.6; control2X: 136.201; control2Y: -134.8; x: 143.401; y: -131.2 }
- PathCubic { control1X: 143.401; control1Y: -131.2; control2X: 152.601; control2Y: -128.6; x: 158.801; y: -122.4 }
- PathCubic { control1X: 158.801; control1Y: -122.4; control2X: 170.001; control2Y: -119.2; x: 173.201; y: -120.2 }
- PathCubic { control1X: 173.201; control1Y: -120.2; control2X: 182.001; control2Y: -118; x: 182.401; y: -116.2 }
- PathCubic { control1X: 182.401; control1Y: -116.2; control2X: 188.201; control2Y: -113.2; x: 186.401; y: -110.6 }
- PathCubic { control1X: 186.401; control1Y: -110.6; control2X: 186.801; control2Y: -109; x: 180.801; y: -106.4 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: 168.33; y: -108.509 }
- PathCubic { control1X: 169.137; control1Y: -107.877; control2X: 170.156; control2Y: -107.779; x: 170.761; y: -106.97 }
- PathCubic { control1X: 170.995; control1Y: -106.656; control2X: 170.706; control2Y: -106.33; x: 170.391; y: -106.233 }
- PathCubic { control1X: 169.348; control1Y: -105.916; control2X: 168.292; control2Y: -106.486; x: 167.15; y: -105.898 }
- PathCubic { control1X: 166.748; control1Y: -105.691; control2X: 166.106; control2Y: -105.873; x: 165.553; y: -106.022 }
- PathCubic { control1X: 163.921; control1Y: -106.463; control2X: 162.092; control2Y: -106.488; x: 160.401; y: -105.8 }
- PathCubic { control1X: 158.416; control1Y: -106.929; control2X: 156.056; control2Y: -106.345; x: 153.975; y: -107.346 }
- PathCubic { control1X: 153.917; control1Y: -107.373; control2X: 153.695; control2Y: -107.027; x: 153.621; y: -107.054 }
- PathCubic { control1X: 150.575; control1Y: -108.199; control2X: 146.832; control2Y: -107.916; x: 144.401; y: -110.2 }
- PathCubic { control1X: 141.973; control1Y: -110.612; control2X: 139.616; control2Y: -111.074; x: 137.188; y: -111.754 }
- PathCubic { control1X: 135.37; control1Y: -112.263; control2X: 133.961; control2Y: -113.252; x: 132.341; y: -114.084 }
- PathCubic { control1X: 130.964; control1Y: -114.792; control2X: 129.507; control2Y: -115.314; x: 127.973; y: -115.686 }
- PathCubic { control1X: 126.11; control1Y: -116.138; control2X: 124.279; control2Y: -116.026; x: 122.386; y: -116.546 }
- PathCubic { control1X: 122.293; control1Y: -116.571; control2X: 122.101; control2Y: -116.227; x: 122.019; y: -116.254 }
- PathCubic { control1X: 121.695; control1Y: -116.362; control2X: 121.405; control2Y: -116.945; x: 121.234; y: -116.892 }
- PathCubic { control1X: 119.553; control1Y: -116.37; control2X: 118.065; control2Y: -117.342; x: 116.401; y: -117 }
- PathCubic { control1X: 115.223; control1Y: -118.224; control2X: 113.495; control2Y: -117.979; x: 111.949; y: -118.421 }
- PathCubic { control1X: 108.985; control1Y: -119.269; control2X: 105.831; control2Y: -117.999; x: 102.801; y: -119 }
- PathCubic { control1X: 106.914; control1Y: -120.842; control2X: 111.601; control2Y: -119.61; x: 115.663; y: -121.679 }
- PathCubic { control1X: 117.991; control1Y: -122.865; control2X: 120.653; control2Y: -121.763; x: 123.223; y: -122.523 }
- PathCubic { control1X: 123.71; control1Y: -122.667; control2X: 124.401; control2Y: -122.869; x: 124.801; y: -122.2 }
- PathCubic { control1X: 124.935; control1Y: -122.335; control2X: 125.117; control2Y: -122.574; x: 125.175; y: -122.546 }
- PathCubic { control1X: 127.625; control1Y: -121.389; control2X: 129.94; control2Y: -120.115; x: 132.422; y: -119.049 }
- PathCubic { control1X: 132.763; control1Y: -118.903; control2X: 133.295; control2Y: -119.135; x: 133.547; y: -118.933 }
- PathCubic { control1X: 135.067; control1Y: -117.717; control2X: 137.01; control2Y: -117.82; x: 138.401; y: -116.6 }
- PathCubic { control1X: 140.099; control1Y: -117.102; control2X: 141.892; control2Y: -116.722; x: 143.621; y: -117.346 }
- PathCubic { control1X: 143.698; control1Y: -117.373; control2X: 143.932; control2Y: -117.032; x: 143.965; y: -117.054 }
- PathCubic { control1X: 145.095; control1Y: -117.802; control2X: 146.25; control2Y: -117.531; x: 147.142; y: -117.227 }
- PathCubic { control1X: 147.48; control1Y: -117.112; control2X: 148.143; control2Y: -116.865; x: 148.448; y: -116.791 }
- PathCubic { control1X: 149.574; control1Y: -116.515; control2X: 150.43; control2Y: -116.035; x: 151.609; y: -115.852 }
- PathCubic { control1X: 151.723; control1Y: -115.834; control2X: 151.908; control2Y: -116.174; x: 151.98; y: -116.146 }
- PathCubic { control1X: 153.103; control1Y: -115.708; control2X: 154.145; control2Y: -115.764; x: 154.801; y: -114.6 }
- PathCubic { control1X: 154.936; control1Y: -114.735; control2X: 155.101; control2Y: -114.973; x: 155.183; y: -114.946 }
- PathCubic { control1X: 156.21; control1Y: -114.608; control2X: 156.859; control2Y: -113.853; x: 157.96; y: -113.612 }
- PathCubic { control1X: 158.445; control1Y: -113.506; control2X: 159.057; control2Y: -112.88; x: 159.633; y: -112.704 }
- PathCubic { control1X: 162.025; control1Y: -111.973; control2X: 163.868; control2Y: -110.444; x: 166.062; y: -109.549 }
- PathCubic { control1X: 166.821; control1Y: -109.239; control2X: 167.697; control2Y: -109.005; x: 168.33; y: -108.509 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: 91.696; y: -122.739 }
- PathCubic { control1X: 89.178; control1Y: -124.464; control2X: 86.81; control2Y: -125.57; x: 84.368; y: -127.356 }
- PathCubic { control1X: 84.187; control1Y: -127.489; control2X: 83.827; control2Y: -127.319; x: 83.625; y: -127.441 }
- PathCubic { control1X: 82.618; control1Y: -128.05; control2X: 81.73; control2Y: -128.631; x: 80.748; y: -129.327 }
- PathCubic { control1X: 80.209; control1Y: -129.709; control2X: 79.388; control2Y: -129.698; x: 78.88; y: -129.956 }
- PathCubic { control1X: 76.336; control1Y: -131.248; control2X: 73.707; control2Y: -131.806; x: 71.2; y: -133 }
- PathCubic { control1X: 71.882; control1Y: -133.638; control2X: 73.004; control2Y: -133.394; x: 73.6; y: -134.2 }
- PathCubic { control1X: 73.795; control1Y: -133.92; control2X: 74.033; control2Y: -133.636; x: 74.386; y: -133.827 }
- PathCubic { control1X: 76.064; control1Y: -134.731; control2X: 77.914; control2Y: -134.884; x: 79.59; y: -134.794 }
- PathCubic { control1X: 81.294; control1Y: -134.702; control2X: 83.014; control2Y: -134.397; x: 84.789; y: -134.125 }
- PathCubic { control1X: 85.096; control1Y: -134.078; control2X: 85.295; control2Y: -133.555; x: 85.618; y: -133.458 }
- PathCubic { control1X: 87.846; control1Y: -132.795; control2X: 90.235; control2Y: -133.32; x: 92.354; y: -132.482 }
- PathCubic { control1X: 93.945; control1Y: -131.853; control2X: 95.515; control2Y: -131.03; x: 96.754; y: -129.755 }
- PathCubic { control1X: 97.006; control1Y: -129.495; control2X: 96.681; control2Y: -129.194; x: 96.401; y: -129 }
- PathCubic { control1X: 96.789; control1Y: -129.109; control2X: 97.062; control2Y: -128.903; x: 97.173; y: -128.59 }
- PathCubic { control1X: 97.257; control1Y: -128.351; control2X: 97.257; control2Y: -128.049; x: 97.173; y: -127.81 }
- PathCubic { control1X: 97.061; control1Y: -127.498; control2X: 96.782; control2Y: -127.397; x: 96.408; y: -127.346 }
- PathCubic { control1X: 95.001; control1Y: -127.156; control2X: 96.773; control2Y: -128.536; x: 96.073; y: -128.088 }
- PathCubic { control1X: 94.8; control1Y: -127.274; control2X: 95.546; control2Y: -125.868; x: 94.801; y: -124.6 }
- PathCubic { control1X: 94.521; control1Y: -124.794; control2X: 94.291; control2Y: -125.012; x: 94.401; y: -125.4 }
- PathCubic { control1X: 94.635; control1Y: -124.878; control2X: 94.033; control2Y: -124.588; x: 93.865; y: -124.272 }
- PathCubic { control1X: 93.48; control1Y: -123.547; control2X: 92.581; control2Y: -122.132; x: 91.696; y: -122.739 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: 59.198; y: -115.391 }
- PathCubic { control1X: 56.044; control1Y: -116.185; control2X: 52.994; control2Y: -116.07; x: 49.978; y: -117.346 }
- PathCubic { control1X: 49.911; control1Y: -117.374; control2X: 49.688; control2Y: -117.027; x: 49.624; y: -117.054 }
- PathCubic { control1X: 48.258; control1Y: -117.648; control2X: 47.34; control2Y: -118.614; x: 46.264; y: -119.66 }
- PathCubic { control1X: 45.351; control1Y: -120.548; control2X: 43.693; control2Y: -120.161; x: 42.419; y: -120.648 }
- PathCubic { control1X: 42.095; control1Y: -120.772; control2X: 41.892; control2Y: -121.284; x: 41.591; y: -121.323 }
- PathCubic { control1X: 40.372; control1Y: -121.48; control2X: 39.445; control2Y: -122.429; x: 38.4; y: -123 }
- PathCubic { control1X: 40.736; control1Y: -123.795; control2X: 43.147; control2Y: -123.764; x: 45.609; y: -124.148 }
- PathCubic { control1X: 45.722; control1Y: -124.166; control2X: 45.867; control2Y: -123.845; x: 46; y: -123.845 }
- PathCubic { control1X: 46.136; control1Y: -123.845; control2X: 46.266; control2Y: -124.066; x: 46.4; y: -124.2 }
- PathCubic { control1X: 46.595; control1Y: -123.92; control2X: 46.897; control2Y: -123.594; x: 47.154; y: -123.848 }
- PathCubic { control1X: 47.702; control1Y: -124.388; control2X: 48.258; control2Y: -124.198; x: 48.798; y: -124.158 }
- PathCubic { control1X: 48.942; control1Y: -124.148; control2X: 49.067; control2Y: -123.845; x: 49.2; y: -123.845 }
- PathCubic { control1X: 49.336; control1Y: -123.845; control2X: 49.467; control2Y: -124.156; x: 49.6; y: -124.156 }
- PathCubic { control1X: 49.736; control1Y: -124.155; control2X: 49.867; control2Y: -123.845; x: 50; y: -123.845 }
- PathCubic { control1X: 50.136; control1Y: -123.845; control2X: 50.266; control2Y: -124.066; x: 50.4; y: -124.2 }
- PathCubic { control1X: 51.092; control1Y: -123.418; control2X: 51.977; control2Y: -123.972; x: 52.799; y: -123.793 }
- PathCubic { control1X: 53.837; control1Y: -123.566; control2X: 54.104; control2Y: -122.418; x: 55.178; y: -122.12 }
- PathCubic { control1X: 59.893; control1Y: -120.816; control2X: 64.03; control2Y: -118.671; x: 68.393; y: -116.584 }
- PathCubic { control1X: 68.7; control1Y: -116.437; control2X: 68.91; control2Y: -116.189; x: 68.8; y: -115.8 }
- PathCubic { control1X: 69.067; control1Y: -115.8; control2X: 69.38; control2Y: -115.888; x: 69.57; y: -115.756 }
- PathCubic { control1X: 70.628; control1Y: -115.024; control2X: 71.669; control2Y: -114.476; x: 72.366; y: -113.378 }
- PathCubic { control1X: 72.582; control1Y: -113.039; control2X: 72.253; control2Y: -112.632; x: 72.02; y: -112.684 }
- PathCubic { control1X: 67.591; control1Y: -113.679; control2X: 63.585; control2Y: -114.287; x: 59.198; y: -115.391 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: 45.338; y: -71.179 }
- PathCubic { control1X: 43.746; control1Y: -72.398; control2X: 43.162; control2Y: -74.429; x: 42.034; y: -76.221 }
- PathCubic { control1X: 41.82; control1Y: -76.561; control2X: 42.094; control2Y: -76.875; x: 42.411; y: -76.964 }
- PathCubic { control1X: 42.971; control1Y: -77.123; control2X: 43.514; control2Y: -76.645; x: 43.923; y: -76.443 }
- PathCubic { control1X: 45.668; control1Y: -75.581; control2X: 47.203; control2Y: -74.339; x: 49.2; y: -74.2 }
- PathCubic { control1X: 51.19; control1Y: -71.966; control2X: 55.45; control2Y: -71.581; x: 55.457; y: -68.2 }
- PathCubic { control1X: 55.458; control1Y: -67.341; control2X: 54.03; control2Y: -68.259; x: 53.6; y: -67.4 }
- PathCubic { control1X: 51.149; control1Y: -68.403; control2X: 48.76; control2Y: -68.3; x: 46.38; y: -69.767 }
- PathCubic { control1X: 45.763; control1Y: -70.148; control2X: 46.093; control2Y: -70.601; x: 45.338; y: -71.179 }
- }
-
- ShapePath {
- fillColor: "#cc7226"
- strokeWidth: -1
- PathMove { x: 17.8; y: -123.756 }
- PathCubic { control1X: 17.935; control1Y: -123.755; control2X: 24.966; control2Y: -123.522; x: 24.949; y: -123.408 }
- PathCubic { control1X: 24.904; control1Y: -123.099; control2X: 17.174; control2Y: -122.05; x: 16.81; y: -122.22 }
- PathCubic { control1X: 16.646; control1Y: -122.296; control2X: 9.134; control2Y: -119.866; x: 9; y: -120 }
- PathCubic { control1X: 9.268; control1Y: -120.135; control2X: 17.534; control2Y: -123.756; x: 17.8; y: -123.756 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 33.2; y: -114 }
- PathCubic { control1X: 33.2; control1Y: -114; control2X: 18.4; control2Y: -112.2; x: 14; y: -111 }
- PathCubic { control1X: 9.6; control1Y: -109.8; control2X: -9; control2Y: -102.2; x: -12; y: -100.2 }
- PathCubic { control1X: -12; control1Y: -100.2; control2X: -25.4; control2Y: -94.8; x: -42.4; y: -74.8 }
- PathCubic { control1X: -42.4; control1Y: -74.8; control2X: -34.8; control2Y: -78.2; x: -32.6; y: -81 }
- PathCubic { control1X: -32.6; control1Y: -81; control2X: -19; control2Y: -93.6; x: -19.2; y: -91 }
- PathCubic { control1X: -19.2; control1Y: -91; control2X: -7; control2Y: -99.6; x: -7.6; y: -97.4 }
- PathCubic { control1X: -7.6; control1Y: -97.4; control2X: 16.8; control2Y: -108.6; x: 14.8; y: -105.4 }
- PathCubic { control1X: 14.8; control1Y: -105.4; control2X: 36.4; control2Y: -110; x: 35.4; y: -108 }
- PathCubic { control1X: 35.4; control1Y: -108; control2X: 54.2; control2Y: -103.6; x: 51.4; y: -103.4 }
- PathCubic { control1X: 51.4; control1Y: -103.4; control2X: 45.6; control2Y: -102.2; x: 52; y: -98.6 }
- PathCubic { control1X: 52; control1Y: -98.6; control2X: 48.6; control2Y: -94.2; x: 43.2; y: -98.2 }
- PathCubic { control1X: 37.8; control1Y: -102.2; control2X: 40.8; control2Y: -100; x: 35.8; y: -99 }
- PathCubic { control1X: 35.8; control1Y: -99; control2X: 33.2; control2Y: -98.2; x: 28.6; y: -102.2 }
- PathCubic { control1X: 28.6; control1Y: -102.2; control2X: 23; control2Y: -106.8; x: 14.2; y: -103.2 }
- PathCubic { control1X: 14.2; control1Y: -103.2; control2X: -16.4; control2Y: -90.6; x: -18.4; y: -90 }
- PathCubic { control1X: -18.4; control1Y: -90; control2X: -22; control2Y: -87.2; x: -24.4; y: -83.6 }
- PathCubic { control1X: -24.4; control1Y: -83.6; control2X: -30.2; control2Y: -79.2; x: -33.2; y: -77.8 }
- PathCubic { control1X: -33.2; control1Y: -77.8; control2X: -46; control2Y: -66.2; x: -47.2; y: -64.8 }
- PathCubic { control1X: -47.2; control1Y: -64.8; control2X: -50.6; control2Y: -59.6; x: -51.4; y: -59.2 }
- PathCubic { control1X: -51.4; control1Y: -59.2; control2X: -45; control2Y: -63; x: -43; y: -65 }
- PathCubic { control1X: -43; control1Y: -65; control2X: -29; control2Y: -75; x: -23.6; y: -75.8 }
- PathCubic { control1X: -23.6; control1Y: -75.8; control2X: -19.2; control2Y: -78.8; x: -18.4; y: -80.2 }
- PathCubic { control1X: -18.4; control1Y: -80.2; control2X: -4; control2Y: -89.4; x: 0.2; y: -89.4 }
- PathCubic { control1X: 0.2; control1Y: -89.4; control2X: 9.4; control2Y: -84.2; x: 11.8; y: -91.2 }
- PathCubic { control1X: 11.8; control1Y: -91.2; control2X: 17.6; control2Y: -93; x: 23.2; y: -91.8 }
- PathCubic { control1X: 23.2; control1Y: -91.8; control2X: 26.4; control2Y: -94.4; x: 25.6; y: -96.6 }
- PathCubic { control1X: 25.6; control1Y: -96.6; control2X: 27.2; control2Y: -98.4; x: 28.2; y: -94.6 }
- PathCubic { control1X: 28.2; control1Y: -94.6; control2X: 31.6; control2Y: -91; x: 36.4; y: -93 }
- PathCubic { control1X: 36.4; control1Y: -93; control2X: 40.4; control2Y: -93.2; x: 38.4; y: -90.8 }
- PathCubic { control1X: 38.4; control1Y: -90.8; control2X: 34; control2Y: -87; x: 22.2; y: -86.8 }
- PathCubic { control1X: 22.2; control1Y: -86.8; control2X: 9.8; control2Y: -86.2; x: -6.6; y: -78.6 }
- PathCubic { control1X: -6.6; control1Y: -78.6; control2X: -36.4; control2Y: -68.2; x: -45.6; y: -57.8 }
- PathCubic { control1X: -45.6; control1Y: -57.8; control2X: -52; control2Y: -49; x: -57.4; y: -47.8 }
- PathCubic { control1X: -57.4; control1Y: -47.8; control2X: -63.2; control2Y: -47; x: -69.2; y: -39.6 }
- PathCubic { control1X: -69.2; control1Y: -39.6; control2X: -59.4; control2Y: -45.4; x: -50.4; y: -45.4 }
- PathCubic { control1X: -50.4; control1Y: -45.4; control2X: -46.4; control2Y: -47.8; x: -50.2; y: -44.2 }
- PathCubic { control1X: -50.2; control1Y: -44.2; control2X: -53.8; control2Y: -36.6; x: -52.2; y: -31.2 }
- PathCubic { control1X: -52.2; control1Y: -31.2; control2X: -52.8; control2Y: -26; x: -53.6; y: -24.4 }
- PathCubic { control1X: -53.6; control1Y: -24.4; control2X: -61.4; control2Y: -11.6; x: -61.4; y: -9.2 }
- PathCubic { control1X: -61.4; control1Y: -6.8; control2X: -60.2; control2Y: 3; x: -59.8; y: 3.6 }
- PathCubic { control1X: -59.4; control1Y: 4.2; control2X: -60.8; control2Y: 2; x: -57; y: 4.4 }
- PathCubic { control1X: -53.2; control1Y: 6.8; control2X: -50.4; control2Y: 8.4; x: -49.6; y: 11.2 }
- PathCubic { control1X: -48.8; control1Y: 14; control2X: -51.6; control2Y: 5.8; x: -51.8; y: 4 }
- PathCubic { control1X: -52; control1Y: 2.2; control2X: -56.2; control2Y: -5; x: -55.4; y: -7.4 }
- PathCubic { control1X: -55.4; control1Y: -7.4; control2X: -54.4; control2Y: -6.4; x: -53.6; y: -5 }
- PathCubic { control1X: -53.6; control1Y: -5; control2X: -54.2; control2Y: -5.6; x: -53.6; y: -9.2 }
- PathCubic { control1X: -53.6; control1Y: -9.2; control2X: -52.8; control2Y: -14.4; x: -51.4; y: -17.6 }
- PathCubic { control1X: -50; control1Y: -20.8; control2X: -48; control2Y: -24.6; x: -47.6; y: -25.4 }
- PathCubic { control1X: -47.2; control1Y: -26.2; control2X: -47.2; control2Y: -32; x: -45.8; y: -29.4 }
- PathLine { x: -42.4; y: -26.8 }
- PathCubic { control1X: -42.4; control1Y: -26.8; control2X: -45.2; control2Y: -29.4; x: -43; y: -31.6 }
- PathCubic { control1X: -43; control1Y: -31.6; control2X: -44; control2Y: -37.2; x: -42.2; y: -39.8 }
- PathCubic { control1X: -42.2; control1Y: -39.8; control2X: -35.2; control2Y: -48.2; x: -33.6; y: -49.2 }
- PathCubic { control1X: -32; control1Y: -50.2; control2X: -33.4; control2Y: -49.8; x: -33.4; y: -49.8 }
- PathCubic { control1X: -33.4; control1Y: -49.8; control2X: -27.4; control2Y: -54; x: -33.2; y: -52.4 }
- PathCubic { control1X: -33.2; control1Y: -52.4; control2X: -37.2; control2Y: -50.8; x: -40.2; y: -50.8 }
- PathCubic { control1X: -40.2; control1Y: -50.8; control2X: -47.8; control2Y: -48.8; x: -43.8; y: -53 }
- PathCubic { control1X: -39.8; control1Y: -57.2; control2X: -29.8; control2Y: -62.6; x: -26; y: -62.4 }
- PathLine { x: -25.2; y: -60.8 }
- PathLine { x: -14; y: -63.2 }
- PathLine { x: -15.2; y: -62.4 }
- PathCubic { control1X: -15.2; control1Y: -62.4; control2X: -15.4; control2Y: -62.6; x: -11.2; y: -63 }
- PathCubic { control1X: -7; control1Y: -63.4; control2X: -1.2; control2Y: -62; x: 0.2; y: -63.8 }
- PathCubic { control1X: 1.6; control1Y: -65.6; control2X: 5; control2Y: -66.6; x: 4.6; y: -65.2 }
- PathCubic { control1X: 4.2; control1Y: -63.8; control2X: 4; control2Y: -61.8; x: 4; y: -61.8 }
- PathCubic { control1X: 4; control1Y: -61.8; control2X: 9; control2Y: -67.6; x: 8.4; y: -65.4 }
- PathCubic { control1X: 7.8; control1Y: -63.2; control2X: -0.4; control2Y: -58; x: -1.8; y: -51.8 }
- PathLine { x: 8.6; y: -60 }
- PathLine { x: 12.2; y: -63 }
- PathCubic { control1X: 12.2; control1Y: -63; control2X: 15.8; control2Y: -60.8; x: 16; y: -62.4 }
- PathCubic { control1X: 16.2; control1Y: -64; control2X: 20.8; control2Y: -69.8; x: 22; y: -69.6 }
- PathCubic { control1X: 23.2; control1Y: -69.4; control2X: 25.2; control2Y: -72.2; x: 25; y: -69.6 }
- PathCubic { control1X: 24.8; control1Y: -67; control2X: 32.4; control2Y: -61.6; x: 32.4; y: -61.6 }
- PathCubic { control1X: 32.4; control1Y: -61.6; control2X: 35.6; control2Y: -63.4; x: 37; y: -62 }
- PathCubic { control1X: 38.4; control1Y: -60.6; control2X: 42.6; control2Y: -81.8; x: 42.6; y: -81.8 }
- PathLine { x: 67.6; y: -92.4 }
- PathLine { x: 111.201; y: -95.8 }
- PathLine { x: 94.201; y: -102.6 }
- PathLine { x: 33.2; y: -114 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#4c0000"
- strokeWidth: 2
- PathMove { x: 51.4; y: 85 }
- PathCubic { control1X: 51.4; control1Y: 85; control2X: 36.4; control2Y: 68.2; x: 28; y: 65.6 }
- PathCubic { control1X: 28; control1Y: 65.6; control2X: 14.6; control2Y: 58.8; x: -10; y: 66.6 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#4c0000"
- strokeWidth: 2
- PathMove { x: 24.8; y: 64.2 }
- PathCubic { control1X: 24.8; control1Y: 64.2; control2X: -0.4; control2Y: 56.2; x: -15.8; y: 60.4 }
- PathCubic { control1X: -15.8; control1Y: 60.4; control2X: -34.2; control2Y: 62.4; x: -42.6; y: 76.2 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#4c0000"
- strokeWidth: 2
- PathMove { x: 21.2; y: 63 }
- PathCubic { control1X: 21.2; control1Y: 63; control2X: 4.2; control2Y: 55.8; x: -10.6; y: 53.6 }
- PathCubic { control1X: -10.6; control1Y: 53.6; control2X: -27.2; control2Y: 51; x: -43.8; y: 58.2 }
- PathCubic { control1X: -43.8; control1Y: 58.2; control2X: -56; control2Y: 64.2; x: -61.4; y: 74.4 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#4c0000"
- strokeWidth: 2
- PathMove { x: 22.2; y: 63.4 }
- PathCubic { control1X: 22.2; control1Y: 63.4; control2X: 6.8; control2Y: 52.4; x: 5.8; y: 51 }
- PathCubic { control1X: 5.8; control1Y: 51; control2X: -1.2; control2Y: 40; x: -14.2; y: 39.6 }
- PathCubic { control1X: -14.2; control1Y: 39.6; control2X: -35.6; control2Y: 40.4; x: -52.8; y: 48.4 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 20.895; y: 54.407 }
- PathCubic { control1X: 22.437; control1Y: 55.87; control2X: 49.4; control2Y: 84.8; x: 49.4; y: 84.8 }
- PathCubic { control1X: 84.6; control1Y: 121.401; control2X: 56.6; control2Y: 87.2; x: 56.6; y: 87.2 }
- PathCubic { control1X: 49; control1Y: 82.4; control2X: 39.8; control2Y: 63.6; x: 39.8; y: 63.6 }
- PathCubic { control1X: 38.6; control1Y: 60.8; control2X: 53.8; control2Y: 70.8; x: 53.8; y: 70.8 }
- PathCubic { control1X: 57.8; control1Y: 71.6; control2X: 71.4; control2Y: 90.8; x: 71.4; y: 90.8 }
- PathCubic { control1X: 64.6; control1Y: 88.4; control2X: 69.4; control2Y: 95.6; x: 69.4; y: 95.6 }
- PathCubic { control1X: 72.2; control1Y: 97.6; control2X: 92.601; control2Y: 113.201; x: 92.601; y: 113.201 }
- PathCubic { control1X: 96.201; control1Y: 117.201; control2X: 100.201; control2Y: 118.801; x: 100.201; y: 118.801 }
- PathCubic { control1X: 114.201; control1Y: 113.601; control2X: 107.801; control2Y: 126.801; x: 107.801; y: 126.801 }
- PathCubic { control1X: 110.201; control1Y: 133.601; control2X: 115.801; control2Y: 122.001; x: 115.801; y: 122.001 }
- PathCubic { control1X: 127.001; control1Y: 105.2; control2X: 110.601; control2Y: 107.601; x: 110.601; y: 107.601 }
- PathCubic { control1X: 80.6; control1Y: 110.401; control2X: 73.8; control2Y: 94.4; x: 73.8; y: 94.4 }
- PathCubic { control1X: 71.4; control1Y: 92; control2X: 80.2; control2Y: 94.4; x: 80.2; y: 94.4 }
- PathCubic { control1X: 88.601; control1Y: 96.4; control2X: 73; control2Y: 82; x: 73; y: 82 }
- PathCubic { control1X: 75.4; control1Y: 82; control2X: 84.6; control2Y: 88.8; x: 84.6; y: 88.8 }
- PathCubic { control1X: 95.001; control1Y: 98; control2X: 97.001; control2Y: 96; x: 97.001; y: 96 }
- PathCubic { control1X: 115.001; control1Y: 87.2; control2X: 125.401; control2Y: 94.8; x: 125.401; y: 94.8 }
- PathCubic { control1X: 127.401; control1Y: 96.4; control2X: 121.801; control2Y: 103.2; x: 123.401; y: 108.401 }
- PathCubic { control1X: 125.001; control1Y: 113.601; control2X: 129.801; control2Y: 126.001; x: 129.801; y: 126.001 }
- PathCubic { control1X: 127.401; control1Y: 127.601; control2X: 127.801; control2Y: 138.401; x: 127.801; y: 138.401 }
- PathCubic { control1X: 144.601; control1Y: 161.601; control2X: 135.001; control2Y: 159.601; x: 135.001; y: 159.601 }
- PathCubic { control1X: 119.401; control1Y: 159.201; control2X: 134.201; control2Y: 166.801; x: 134.201; y: 166.801 }
- PathCubic { control1X: 137.401; control1Y: 168.801; control2X: 146.201; control2Y: 176.001; x: 146.201; y: 176.001 }
- PathCubic { control1X: 143.401; control1Y: 174.801; control2X: 141.801; control2Y: 180.001; x: 141.801; y: 180.001 }
- PathCubic { control1X: 146.601; control1Y: 184.001; control2X: 143.801; control2Y: 188.801; x: 143.801; y: 188.801 }
- PathCubic { control1X: 137.801; control1Y: 190.001; control2X: 136.601; control2Y: 194.001; x: 136.601; y: 194.001 }
- PathCubic { control1X: 143.401; control1Y: 202.001; control2X: 133.401; control2Y: 202.401; x: 133.401; y: 202.401 }
- PathCubic { control1X: 137.001; control1Y: 206.801; control2X: 132.201; control2Y: 218.801; x: 132.201; y: 218.801 }
- PathCubic { control1X: 127.401; control1Y: 218.801; control2X: 121.001; control2Y: 224.401; x: 121.001; y: 224.401 }
- PathCubic { control1X: 123.401; control1Y: 229.201; control2X: 113.001; control2Y: 234.801; x: 113.001; y: 234.801 }
- PathCubic { control1X: 104.601; control1Y: 236.401; control2X: 107.401; control2Y: 243.201; x: 107.401; y: 243.201 }
- PathCubic { control1X: 99.401; control1Y: 249.201; control2X: 97.001; control2Y: 265.201; x: 97.001; y: 265.201 }
- PathCubic { control1X: 96.201; control1Y: 275.601; control2X: 93.801; control2Y: 278.801; x: 99.001; y: 276.801 }
- PathCubic { control1X: 104.201; control1Y: 274.801; control2X: 103.401; control2Y: 262.401; x: 103.401; y: 262.401 }
- PathCubic { control1X: 98.601; control1Y: 246.801; control2X: 141.401; control2Y: 230.801; x: 141.401; y: 230.801 }
- PathCubic { control1X: 145.401; control1Y: 229.201; control2X: 146.201; control2Y: 224.001; x: 146.201; y: 224.001 }
- PathCubic { control1X: 148.201; control1Y: 224.401; control2X: 157.001; control2Y: 232.001; x: 157.001; y: 232.001 }
- PathCubic { control1X: 164.601; control1Y: 243.201; control2X: 165.001; control2Y: 234.001; x: 165.001; y: 234.001 }
- PathCubic { control1X: 166.201; control1Y: 230.401; control2X: 164.601; control2Y: 224.401; x: 164.601; y: 224.401 }
- PathCubic { control1X: 170.601; control1Y: 202.801; control2X: 156.601; control2Y: 196.401; x: 156.601; y: 196.401 }
- PathCubic { control1X: 146.601; control1Y: 162.801; control2X: 160.601; control2Y: 171.201; x: 160.601; y: 171.201 }
- PathCubic { control1X: 163.401; control1Y: 176.801; control2X: 174.201; control2Y: 182.001; x: 174.201; y: 182.001 }
- PathLine { x: 177.801; y: 179.601 }
- PathCubic { control1X: 176.201; control1Y: 174.801; control2X: 184.601; control2Y: 168.801; x: 184.601; y: 168.801 }
- PathCubic { control1X: 187.401; control1Y: 175.201; control2X: 193.401; control2Y: 167.201; x: 193.401; y: 167.201 }
- PathCubic { control1X: 197.001; control1Y: 142.801; control2X: 209.401; control2Y: 157.201; x: 209.401; y: 157.201 }
- PathCubic { control1X: 213.401; control1Y: 158.401; control2X: 214.601; control2Y: 151.601; x: 214.601; y: 151.601 }
- PathCubic { control1X: 218.201; control1Y: 141.201; control2X: 214.601; control2Y: 127.601; x: 214.601; y: 127.601 }
- PathCubic { control1X: 218.201; control1Y: 127.201; control2X: 227.801; control2Y: 133.201; x: 227.801; y: 133.201 }
- PathCubic { control1X: 230.601; control1Y: 129.601; control2X: 221.401; control2Y: 112.801; x: 225.401; y: 115.201 }
- PathCubic { control1X: 229.401; control1Y: 117.601; control2X: 233.801; control2Y: 119.201; x: 233.801; y: 119.201 }
- PathCubic { control1X: 234.601; control1Y: 117.201; control2X: 224.601; control2Y: 104.801; x: 224.601; y: 104.801 }
- PathCubic { control1X: 220.201; control1Y: 102; control2X: 215.001; control2Y: 81.6; x: 215.001; y: 81.6 }
- PathCubic { control1X: 222.201; control1Y: 85.2; control2X: 212.201; control2Y: 70; x: 212.201; y: 70 }
- PathCubic { control1X: 212.201; control1Y: 66.8; control2X: 218.201; control2Y: 55.6; x: 218.201; y: 55.6 }
- PathCubic { control1X: 217.401; control1Y: 48.8; control2X: 218.201; control2Y: 49.2; x: 218.201; y: 49.2 }
- PathCubic { control1X: 221.001; control1Y: 50.4; control2X: 229.001; control2Y: 52; x: 222.201; y: 45.6 }
- PathCubic { control1X: 215.401; control1Y: 39.2; control2X: 223.001; control2Y: 34.4; x: 223.001; y: 34.4 }
- PathCubic { control1X: 227.401; control1Y: 31.6; control2X: 213.801; control2Y: 32; x: 213.801; y: 32 }
- PathCubic { control1X: 208.601; control1Y: 27.6; control2X: 209.001; control2Y: 23.6; x: 209.001; y: 23.6 }
- PathCubic { control1X: 217.001; control1Y: 25.6; control2X: 202.601; control2Y: 11.2; x: 200.201; y: 7.6 }
- PathCubic { control1X: 197.801; control1Y: 4; control2X: 207.401; control2Y: -1.2; x: 207.401; y: -1.2 }
- PathCubic { control1X: 220.601; control1Y: -4.8; control2X: 209.001; control2Y: -8; x: 209.001; y: -8 }
- PathCubic { control1X: 189.401; control1Y: -7.6; control2X: 200.201; control2Y: -18.4; x: 200.201; y: -18.4 }
- PathCubic { control1X: 206.201; control1Y: -18; control2X: 204.601; control2Y: -20.4; x: 204.601; y: -20.4 }
- PathCubic { control1X: 199.401; control1Y: -21.6; control2X: 189.801; control2Y: -28; x: 189.801; y: -28 }
- PathCubic { control1X: 185.801; control1Y: -31.6; control2X: 189.401; control2Y: -30.8; x: 189.401; y: -30.8 }
- PathCubic { control1X: 206.201; control1Y: -29.6; control2X: 177.401; control2Y: -40.8; x: 177.401; y: -40.8 }
- PathCubic { control1X: 185.401; control1Y: -40.8; control2X: 167.401; control2Y: -51.2; x: 167.401; y: -51.2 }
- PathCubic { control1X: 165.401; control1Y: -52.8; control2X: 162.201; control2Y: -60.4; x: 162.201; y: -60.4 }
- PathCubic { control1X: 156.201; control1Y: -65.6; control2X: 151.401; control2Y: -72.4; x: 151.401; y: -72.4 }
- PathCubic { control1X: 151.001; control1Y: -76.8; control2X: 146.201; control2Y: -81.6; x: 146.201; y: -81.6 }
- PathCubic { control1X: 134.601; control1Y: -95.2; control2X: 129.001; control2Y: -94.8; x: 129.001; y: -94.8 }
- PathCubic { control1X: 114.201; control1Y: -98.4; control2X: 109.001; control2Y: -97.6; x: 109.001; y: -97.6 }
- PathLine { x: 56.2; y: -93.2 }
- PathCubic { control1X: 29.8; control1Y: -80.4; control2X: 37.6; control2Y: -59.4; x: 37.6; y: -59.4 }
- PathCubic { control1X: 44; control1Y: -51; control2X: 53.2; control2Y: -54.8; x: 53.2; y: -54.8 }
- PathCubic { control1X: 57.8; control1Y: -61; control2X: 69.4; control2Y: -58.8; x: 69.4; y: -58.8 }
- PathCubic { control1X: 89.801; control1Y: -55.6; control2X: 87.201; control2Y: -59.2; x: 87.201; y: -59.2 }
- PathCubic { control1X: 84.801; control1Y: -63.8; control2X: 68.6; control2Y: -70; x: 68.4; y: -70.6 }
- PathCubic { control1X: 68.2; control1Y: -71.2; control2X: 59.4; control2Y: -74.6; x: 59.4; y: -74.6 }
- PathCubic { control1X: 56.4; control1Y: -75.8; control2X: 52; control2Y: -85; x: 52; y: -85 }
- PathCubic { control1X: 48.8; control1Y: -88.4; control2X: 64.6; control2Y: -82.6; x: 64.6; y: -82.6 }
- PathCubic { control1X: 63.4; control1Y: -81.6; control2X: 70.8; control2Y: -77.6; x: 70.8; y: -77.6 }
- PathCubic { control1X: 88.201; control1Y: -78.6; control2X: 98.801; control2Y: -67.8; x: 98.801; y: -67.8 }
- PathCubic { control1X: 109.601; control1Y: -51.2; control2X: 109.801; control2Y: -59.4; x: 109.801; y: -59.4 }
- PathCubic { control1X: 112.601; control1Y: -68.8; control2X: 100.801; control2Y: -90; x: 100.801; y: -90 }
- PathCubic { control1X: 101.201; control1Y: -92; control2X: 109.401; control2Y: -85.4; x: 109.401; y: -85.4 }
- PathCubic { control1X: 110.801; control1Y: -87.4; control2X: 111.601; control2Y: -81.6; x: 111.601; y: -81.6 }
- PathCubic { control1X: 111.801; control1Y: -79.2; control2X: 115.601; control2Y: -71.2; x: 115.601; y: -71.2 }
- PathCubic { control1X: 118.401; control1Y: -58.2; control2X: 122.001; control2Y: -65.6; x: 122.001; y: -65.6 }
- PathLine { x: 126.601; y: -56.2 }
- PathCubic { control1X: 128.001; control1Y: -53.6; control2X: 122.001; control2Y: -46; x: 122.001; y: -46 }
- PathCubic { control1X: 121.801; control1Y: -43.2; control2X: 122.601; control2Y: -43.4; x: 117.001; y: -35.8 }
- PathCubic { control1X: 111.401; control1Y: -28.2; control2X: 114.801; control2Y: -23.8; x: 114.801; y: -23.8 }
- PathCubic { control1X: 113.401; control1Y: -17.2; control2X: 122.201; control2Y: -17.6; x: 122.201; y: -17.6 }
- PathCubic { control1X: 124.801; control1Y: -15.4; control2X: 128.201; control2Y: -15.4; x: 128.201; y: -15.4 }
- PathCubic { control1X: 130.001; control1Y: -13.4; control2X: 132.401; control2Y: -14; x: 132.401; y: -14 }
- PathCubic { control1X: 134.001; control1Y: -17.8; control2X: 140.201; control2Y: -15.8; x: 140.201; y: -15.8 }
- PathCubic { control1X: 141.601; control1Y: -18.2; control2X: 149.801; control2Y: -18.6; x: 149.801; y: -18.6 }
- PathCubic { control1X: 150.801; control1Y: -21.2; control2X: 151.201; control2Y: -22.8; x: 154.601; y: -23.4 }
- PathCubic { control1X: 158.001; control1Y: -24; control2X: 133.401; control2Y: -67; x: 133.401; y: -67 }
- PathCubic { control1X: 139.801; control1Y: -67.8; control2X: 131.601; control2Y: -80.2; x: 131.601; y: -80.2 }
- PathCubic { control1X: 129.401; control1Y: -86.8; control2X: 140.801; control2Y: -72.2; x: 143.001; y: -70.8 }
- PathCubic { control1X: 145.201; control1Y: -69.4; control2X: 146.201; control2Y: -67.2; x: 144.601; y: -67.4 }
- PathCubic { control1X: 143.001; control1Y: -67.6; control2X: 141.201; control2Y: -65.4; x: 142.601; y: -65.2 }
- PathCubic { control1X: 144.001; control1Y: -65; control2X: 157.001; control2Y: -50; x: 160.401; y: -39.8 }
- PathCubic { control1X: 163.801; control1Y: -29.6; control2X: 169.801; control2Y: -25.6; x: 176.001; y: -19.6 }
- PathCubic { control1X: 182.201; control1Y: -13.6; control2X: 181.401; control2Y: 10.6; x: 181.401; y: 10.6 }
- PathCubic { control1X: 181.001; control1Y: 19.4; control2X: 187.001; control2Y: 30; x: 187.001; y: 30 }
- PathCubic { control1X: 189.001; control1Y: 33.8; control2X: 184.801; control2Y: 52; x: 184.801; y: 52 }
- PathCubic { control1X: 182.801; control1Y: 54.2; control2X: 184.201; control2Y: 55; x: 184.201; y: 55 }
- PathCubic { control1X: 185.201; control1Y: 56.2; control2X: 192.001; control2Y: 69.4; x: 192.001; y: 69.4 }
- PathCubic { control1X: 190.201; control1Y: 69.2; control2X: 193.801; control2Y: 72.8; x: 193.801; y: 72.8 }
- PathCubic { control1X: 199.001; control1Y: 78.8; control2X: 192.601; control2Y: 75.8; x: 192.601; y: 75.8 }
- PathCubic { control1X: 186.601; control1Y: 74.2; control2X: 193.601; control2Y: 84; x: 193.601; y: 84 }
- PathCubic { control1X: 194.801; control1Y: 85.8; control2X: 185.801; control2Y: 81.2; x: 185.801; y: 81.2 }
- PathCubic { control1X: 176.601; control1Y: 80.6; control2X: 188.201; control2Y: 87.8; x: 188.201; y: 87.8 }
- PathCubic { control1X: 196.801; control1Y: 95; control2X: 185.401; control2Y: 90.6; x: 185.401; y: 90.6 }
- PathCubic { control1X: 180.801; control1Y: 88.8; control2X: 184.001; control2Y: 95.6; x: 184.001; y: 95.6 }
- PathCubic { control1X: 187.201; control1Y: 97.2; control2X: 204.401; control2Y: 104.2; x: 204.401; y: 104.2 }
- PathCubic { control1X: 204.801; control1Y: 108.001; control2X: 201.801; control2Y: 113.001; x: 201.801; y: 113.001 }
- PathCubic { control1X: 202.201; control1Y: 117.001; control2X: 200.001; control2Y: 120.401; x: 200.001; y: 120.401 }
- PathCubic { control1X: 198.801; control1Y: 128.601; control2X: 198.201; control2Y: 129.401; x: 198.201; y: 129.401 }
- PathCubic { control1X: 194.001; control1Y: 129.601; control2X: 186.601; control2Y: 143.401; x: 186.601; y: 143.401 }
- PathCubic { control1X: 184.801; control1Y: 146.001; control2X: 174.601; control2Y: 158.001; x: 174.601; y: 158.001 }
- PathCubic { control1X: 172.601; control1Y: 165.001; control2X: 154.601; control2Y: 157.801; x: 154.601; y: 157.801 }
- PathCubic { control1X: 148.001; control1Y: 161.201; control2X: 150.001; control2Y: 157.801; x: 150.001; y: 157.801 }
- PathCubic { control1X: 149.601; control1Y: 155.601; control2X: 154.401; control2Y: 149.601; x: 154.401; y: 149.601 }
- PathCubic { control1X: 161.401; control1Y: 147.001; control2X: 158.801; control2Y: 136.201; x: 158.801; y: 136.201 }
- PathCubic { control1X: 162.801; control1Y: 134.801; control2X: 151.601; control2Y: 132.001; x: 151.801; y: 130.801 }
- PathCubic { control1X: 152.001; control1Y: 129.601; control2X: 157.801; control2Y: 128.201; x: 157.801; y: 128.201 }
- PathCubic { control1X: 165.801; control1Y: 126.201; control2X: 161.401; control2Y: 123.801; x: 161.401; y: 123.801 }
- PathCubic { control1X: 160.801; control1Y: 119.801; control2X: 163.801; control2Y: 114.201; x: 163.801; y: 114.201 }
- PathCubic { control1X: 175.401; control1Y: 113.401; control2X: 163.801; control2Y: 97.2; x: 163.801; y: 97.2 }
- PathCubic { control1X: 153.001; control1Y: 89.6; control2X: 152.001; control2Y: 83.8; x: 152.001; y: 83.8 }
- PathCubic { control1X: 164.601; control1Y: 75.6; control2X: 156.401; control2Y: 63.2; x: 156.601; y: 59.6 }
- PathCubic { control1X: 156.801; control1Y: 56; control2X: 158.001; control2Y: 34.4; x: 158.001; y: 34.4 }
- PathCubic { control1X: 156.001; control1Y: 28.2; control2X: 153.001; control2Y: 14.6; x: 153.001; y: 14.6 }
- PathCubic { control1X: 155.201; control1Y: 9.4; control2X: 162.601; control2Y: -3.2; x: 162.601; y: -3.2 }
- PathCubic { control1X: 165.401; control1Y: -7.4; control2X: 174.201; control2Y: -12.2; x: 172.001; y: -15.2 }
- PathCubic { control1X: 169.801; control1Y: -18.2; control2X: 162.001; control2Y: -16.4; x: 162.001; y: -16.4 }
- PathCubic { control1X: 154.201; control1Y: -17.8; control2X: 154.801; control2Y: -12.6; x: 154.801; y: -12.6 }
- PathCubic { control1X: 153.201; control1Y: -11.6; control2X: 152.401; control2Y: -6.6; x: 152.401; y: -6.6 }
- PathCubic { control1X: 151.68; control1Y: 1.333; control2X: 142.801; control2Y: 7.6; x: 142.801; y: 7.6 }
- PathCubic { control1X: 131.601; control1Y: 13.8; control2X: 140.801; control2Y: 17.8; x: 140.801; y: 17.8 }
- PathCubic { control1X: 146.801; control1Y: 24.4; control2X: 137.001; control2Y: 24.6; x: 137.001; y: 24.6 }
- PathCubic { control1X: 126.001; control1Y: 22.8; control2X: 134.201; control2Y: 33; x: 134.201; y: 33 }
- PathCubic { control1X: 145.001; control1Y: 45.8; control2X: 142.001; control2Y: 48.6; x: 142.001; y: 48.6 }
- PathCubic { control1X: 131.801; control1Y: 49.6; control2X: 144.401; control2Y: 58.8; x: 144.401; y: 58.8 }
- PathCubic { control1X: 144.401; control1Y: 58.8; control2X: 143.601; control2Y: 56.8; x: 143.801; y: 58.6 }
- PathCubic { control1X: 144.001; control1Y: 60.4; control2X: 147.001; control2Y: 64.6; x: 147.801; y: 66.6 }
- PathCubic { control1X: 148.601; control1Y: 68.6; control2X: 144.601; control2Y: 68.8; x: 144.601; y: 68.8 }
- PathCubic { control1X: 145.201; control1Y: 78.4; control2X: 129.801; control2Y: 74.2; x: 129.801; y: 74.2 }
- PathCubic { control1X: 129.801; control1Y: 74.2; control2X: 129.801; control2Y: 74.2; x: 128.201; y: 74.4 }
- PathCubic { control1X: 126.601; control1Y: 74.6; control2X: 115.401; control2Y: 73.8; x: 109.601; y: 71.6 }
- PathCubic { control1X: 103.801; control1Y: 69.4; control2X: 97.001; control2Y: 69.4; x: 97.001; y: 69.4 }
- PathCubic { control1X: 97.001; control1Y: 69.4; control2X: 93.001; control2Y: 71.2; x: 85.4; y: 71 }
- PathCubic { control1X: 77.8; control1Y: 70.8; control2X: 69.8; control2Y: 73.6; x: 69.8; y: 73.6 }
- PathCubic { control1X: 65.4; control1Y: 73.2; control2X: 74; control2Y: 68.8; x: 74.2; y: 69 }
- PathCubic { control1X: 74.4; control1Y: 69.2; control2X: 80; control2Y: 63.6; x: 72; y: 64.2 }
- PathCubic { control1X: 50.203; control1Y: 65.835; control2X: 39.4; control2Y: 55.6; x: 39.4; y: 55.6 }
- PathCubic { control1X: 37.4; control1Y: 54.2; control2X: 34.8; control2Y: 51.4; x: 34.8; y: 51.4 }
- PathCubic { control1X: 24.8; control1Y: 49.4; control2X: 36.2; control2Y: 63.8; x: 36.2; y: 63.8 }
- PathCubic { control1X: 37.4; control1Y: 65.2; control2X: 36; control2Y: 66.2; x: 36; y: 66.2 }
- PathCubic { control1X: 35.2; control1Y: 64.6; control2X: 27.4; control2Y: 59.2; x: 27.4; y: 59.2 }
- PathCubic { control1X: 24.589; control1Y: 58.227; control2X: 23.226; control2Y: 56.893; x: 20.895; y: 54.407 }
- }
-
- ShapePath {
- fillColor: "#4c0000"
- strokeWidth: -1
- PathMove { x: -3; y: 42.8 }
- PathCubic { control1X: -3; control1Y: 42.8; control2X: 8.6; control2Y: 48.4; x: 11.2; y: 51.2 }
- PathCubic { control1X: 13.8; control1Y: 54; control2X: 27.8; control2Y: 65.4; x: 27.8; y: 65.4 }
- PathCubic { control1X: 27.8; control1Y: 65.4; control2X: 22.4; control2Y: 63.4; x: 19.8; y: 61.6 }
- PathCubic { control1X: 17.2; control1Y: 59.8; control2X: 6.4; control2Y: 51.6; x: 6.4; y: 51.6 }
- PathCubic { control1X: 6.4; control1Y: 51.6; control2X: 2.6; control2Y: 45.6; x: -3; y: 42.8 }
- }
-
- ShapePath {
- fillColor: "#99cc32"
- strokeWidth: -1
- PathMove { x: -61.009; y: 11.603 }
- PathCubic { control1X: -60.672; control1Y: 11.455; control2X: -61.196; control2Y: 8.743; x: -61.4; y: 8.2 }
- PathCubic { control1X: -62.422; control1Y: 5.474; control2X: -71.4; control2Y: 4; x: -71.4; y: 4 }
- PathCubic { control1X: -71.627; control1Y: 5.365; control2X: -71.682; control2Y: 6.961; x: -71.576; y: 8.599 }
- PathCubic { control1X: -71.576; control1Y: 8.599; control2X: -66.708; control2Y: 14.118; x: -61.009; y: 11.603 }
- }
-
- ShapePath {
- fillColor: "#659900"
- strokeWidth: -1
- PathMove { x: -61.009; y: 11.403 }
- PathCubic { control1X: -61.458; control1Y: 11.561; control2X: -61.024; control2Y: 8.669; x: -61.2; y: 8.2 }
- PathCubic { control1X: -62.222; control1Y: 5.474; control2X: -71.4; control2Y: 3.9; x: -71.4; y: 3.9 }
- PathCubic { control1X: -71.627; control1Y: 5.265; control2X: -71.682; control2Y: 6.861; x: -71.576; y: 8.499 }
- PathCubic { control1X: -71.576; control1Y: 8.499; control2X: -67.308; control2Y: 13.618; x: -61.009; y: 11.403 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -65.4; y: 11.546 }
- PathCubic { control1X: -66.025; control1Y: 11.546; control2X: -66.531; control2Y: 10.406; x: -66.531; y: 9 }
- PathCubic { control1X: -66.531; control1Y: 7.595; control2X: -66.025; control2Y: 6.455; x: -65.4; y: 6.455 }
- PathCubic { control1X: -64.775; control1Y: 6.455; control2X: -64.268; control2Y: 7.595; x: -64.268; y: 9 }
- PathCubic { control1X: -64.268; control1Y: 10.406; control2X: -64.775; control2Y: 11.546; x: -65.4; y: 11.546 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -65.4; y: 9 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -111; y: 109.601 }
- PathCubic { control1X: -111; control1Y: 109.601; control2X: -116.6; control2Y: 119.601; x: -91.8; y: 113.601 }
- PathCubic { control1X: -91.8; control1Y: 113.601; control2X: -77.8; control2Y: 112.401; x: -75.4; y: 110.001 }
- PathCubic { control1X: -74.2; control1Y: 110.801; control2X: -65.834; control2Y: 113.734; x: -63; y: 114.401 }
- PathCubic { control1X: -56.2; control1Y: 116.001; control2X: -47.8; control2Y: 106; x: -47.8; y: 106 }
- PathCubic { control1X: -47.8; control1Y: 106; control2X: -43.2; control2Y: 95.5; x: -40.4; y: 95.5 }
- PathCubic { control1X: -37.6; control1Y: 95.5; control2X: -40.8; control2Y: 97.1; x: -40.8; y: 97.1 }
- PathCubic { control1X: -40.8; control1Y: 97.1; control2X: -47.4; control2Y: 107.201; x: -47; y: 108.801 }
- PathCubic { control1X: -47; control1Y: 108.801; control2X: -52.2; control2Y: 128.801; x: -68.2; y: 129.601 }
- PathCubic { control1X: -68.2; control1Y: 129.601; control2X: -84.35; control2Y: 130.551; x: -83; y: 136.401 }
- PathCubic { control1X: -83; control1Y: 136.401; control2X: -74.2; control2Y: 134.001; x: -71.8; y: 136.401 }
- PathCubic { control1X: -71.8; control1Y: 136.401; control2X: -61; control2Y: 136.001; x: -69; y: 142.401 }
- PathLine { x: -75.8; y: 154.001 }
- PathCubic { control1X: -75.8; control1Y: 154.001; control2X: -75.66; control2Y: 157.919; x: -85.8; y: 154.401 }
- PathCubic { control1X: -95.6; control1Y: 151.001; control2X: -105.9; control2Y: 138.101; x: -105.9; y: 138.101 }
- PathCubic { control1X: -105.9; control1Y: 138.101; control2X: -121.85; control2Y: 123.551; x: -111; y: 109.601 }
- }
-
- ShapePath {
- fillColor: "#e59999"
- strokeWidth: -1
- PathMove { x: -112.2; y: 113.601 }
- PathCubic { control1X: -112.2; control1Y: 113.601; control2X: -114.2; control2Y: 123.201; x: -77.4; y: 112.801 }
- PathCubic { control1X: -77.4; control1Y: 112.801; control2X: -73; control2Y: 112.801; x: -70.6; y: 113.601 }
- PathCubic { control1X: -68.2; control1Y: 114.401; control2X: -56.2; control2Y: 117.201; x: -54.2; y: 116.001 }
- PathCubic { control1X: -54.2; control1Y: 116.001; control2X: -61.4; control2Y: 129.601; x: -73; y: 128.001 }
- PathCubic { control1X: -73; control1Y: 128.001; control2X: -86.2; control2Y: 129.601; x: -85.8; y: 134.401 }
- PathCubic { control1X: -85.8; control1Y: 134.401; control2X: -81.8; control2Y: 141.601; x: -77; y: 144.001 }
- PathCubic { control1X: -77; control1Y: 144.001; control2X: -74.2; control2Y: 146.401; x: -74.6; y: 149.601 }
- PathCubic { control1X: -75; control1Y: 152.801; control2X: -77.8; control2Y: 154.401; x: -79.8; y: 155.201 }
- PathCubic { control1X: -81.8; control1Y: 156.001; control2X: -85; control2Y: 152.801; x: -86.6; y: 152.801 }
- PathCubic { control1X: -88.2; control1Y: 152.801; control2X: -96.6; control2Y: 146.401; x: -101; y: 141.601 }
- PathCubic { control1X: -105.4; control1Y: 136.801; control2X: -113.8; control2Y: 124.801; x: -113.4; y: 122.001 }
- PathCubic { control1X: -113; control1Y: 119.201; control2X: -112.2; control2Y: 113.601; x: -112.2; y: 113.601 }
- }
-
- ShapePath {
- fillColor: "#b26565"
- strokeWidth: -1
- PathMove { x: -109; y: 131.051 }
- PathCubic { control1X: -106.4; control1Y: 135.001; control2X: -103.2; control2Y: 139.201; x: -101; y: 141.601 }
- PathCubic { control1X: -96.6; control1Y: 146.401; control2X: -88.2; control2Y: 152.801; x: -86.6; y: 152.801 }
- PathCubic { control1X: -85; control1Y: 152.801; control2X: -81.8; control2Y: 156.001; x: -79.8; y: 155.201 }
- PathCubic { control1X: -77.8; control1Y: 154.401; control2X: -75; control2Y: 152.801; x: -74.6; y: 149.601 }
- PathCubic { control1X: -74.2; control1Y: 146.401; control2X: -77; control2Y: 144.001; x: -77; y: 144.001 }
- PathCubic { control1X: -80.066; control1Y: 142.468; control2X: -82.806; control2Y: 138.976; x: -84.385; y: 136.653 }
- PathCubic { control1X: -84.385; control1Y: 136.653; control2X: -84.2; control2Y: 139.201; x: -89.4; y: 138.401 }
- PathCubic { control1X: -94.6; control1Y: 137.601; control2X: -99.8; control2Y: 134.801; x: -101.4; y: 131.601 }
- PathCubic { control1X: -103; control1Y: 128.401; control2X: -105.4; control2Y: 126.001; x: -103.8; y: 129.601 }
- PathCubic { control1X: -102.2; control1Y: 133.201; control2X: -99.8; control2Y: 136.801; x: -98.2; y: 137.201 }
- PathCubic { control1X: -96.6; control1Y: 137.601; control2X: -97; control2Y: 138.801; x: -99.4; y: 138.401 }
- PathCubic { control1X: -101.8; control1Y: 138.001; control2X: -104.6; control2Y: 137.601; x: -109; y: 132.401 }
- }
-
- ShapePath {
- fillColor: "#992600"
- strokeWidth: -1
- PathMove { x: -111.6; y: 110.001 }
- PathCubic { control1X: -111.6; control1Y: 110.001; control2X: -109.8; control2Y: 96.4; x: -108.6; y: 92.4 }
- PathCubic { control1X: -108.6; control1Y: 92.4; control2X: -109.4; control2Y: 85.6; x: -107; y: 81.4 }
- PathCubic { control1X: -104.6; control1Y: 77.2; control2X: -102.6; control2Y: 71; x: -99.6; y: 65.6 }
- PathCubic { control1X: -96.6; control1Y: 60.2; control2X: -96.4; control2Y: 56.2; x: -92.4; y: 54.6 }
- PathCubic { control1X: -88.4; control1Y: 53; control2X: -82.4; control2Y: 44.4; x: -79.6; y: 43.4 }
- PathCubic { control1X: -76.8; control1Y: 42.4; control2X: -77; control2Y: 43.2; x: -77; y: 43.2 }
- PathCubic { control1X: -77; control1Y: 43.2; control2X: -70.2; control2Y: 28.4; x: -56.6; y: 32.4 }
- PathCubic { control1X: -56.6; control1Y: 32.4; control2X: -72.8; control2Y: 29.6; x: -57; y: 20.2 }
- PathCubic { control1X: -57; control1Y: 20.2; control2X: -61.8; control2Y: 21.3; x: -58.5; y: 14.3 }
- PathCubic { control1X: -56.299; control1Y: 9.632; control2X: -56.8; control2Y: 16.4; x: -67.8; y: 28.2 }
- PathCubic { control1X: -67.8; control1Y: 28.2; control2X: -72.8; control2Y: 36.8; x: -78; y: 39.8 }
- PathCubic { control1X: -83.2; control1Y: 42.8; control2X: -95.2; control2Y: 49.8; x: -96.4; y: 53.6 }
- PathCubic { control1X: -97.6; control1Y: 57.4; control2X: -100.8; control2Y: 63.2; x: -102.8; y: 64.8 }
- PathCubic { control1X: -104.8; control1Y: 66.4; control2X: -107.6; control2Y: 70.6; x: -108; y: 74 }
- PathCubic { control1X: -108; control1Y: 74; control2X: -109.2; control2Y: 78; x: -110.6; y: 79.2 }
- PathCubic { control1X: -112; control1Y: 80.4; control2X: -112.2; control2Y: 83.6; x: -112.2; y: 85.6 }
- PathCubic { control1X: -112.2; control1Y: 87.6; control2X: -114.2; control2Y: 90.4; x: -114; y: 92.8 }
- PathCubic { control1X: -114; control1Y: 92.8; control2X: -113.2; control2Y: 111.801; x: -113.6; y: 113.801 }
- PathLine { x: -111.6; y: 110.001 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: -120.2; y: 114.601 }
- PathCubic { control1X: -120.2; control1Y: 114.601; control2X: -122.2; control2Y: 113.201; x: -126.6; y: 119.201 }
- PathCubic { control1X: -126.6; control1Y: 119.201; control2X: -119.3; control2Y: 152.201; x: -119.3; y: 153.601 }
- PathCubic { control1X: -119.3; control1Y: 153.601; control2X: -118.2; control2Y: 151.501; x: -119.5; y: 144.301 }
- PathCubic { control1X: -120.8; control1Y: 137.101; control2X: -121.7; control2Y: 124.401; x: -121.7; y: 124.401 }
- PathLine { x: -120.2; y: 114.601 }
- }
-
- ShapePath {
- fillColor: "#992600"
- strokeWidth: -1
- PathMove { x: -98.6; y: 54 }
- PathCubic { control1X: -98.6; control1Y: 54; control2X: -116.2; control2Y: 57.2; x: -115.8; y: 86.4 }
- PathLine { x: -116.6; y: 111.201 }
- PathCubic { control1X: -116.6; control1Y: 111.201; control2X: -117.8; control2Y: 85.6; x: -119; y: 84 }
- PathCubic { control1X: -120.2; control1Y: 82.4; control2X: -116.2; control2Y: 71.2; x: -119.4; y: 77.2 }
- PathCubic { control1X: -119.4; control1Y: 77.2; control2X: -133.4; control2Y: 91.2; x: -125.4; y: 112.401 }
- PathCubic { control1X: -125.4; control1Y: 112.401; control2X: -123.9; control2Y: 115.701; x: -126.9; y: 111.101 }
- PathCubic { control1X: -126.9; control1Y: 111.101; control2X: -131.5; control2Y: 98.5; x: -130.4; y: 92.1 }
- PathCubic { control1X: -130.4; control1Y: 92.1; control2X: -130.2; control2Y: 89.9; x: -128.3; y: 87.1 }
- PathCubic { control1X: -128.3; control1Y: 87.1; control2X: -119.7; control2Y: 75.4; x: -117; y: 73.1 }
- PathCubic { control1X: -117; control1Y: 73.1; control2X: -115.2; control2Y: 58.7; x: -99.8; y: 53.5 }
- PathCubic { control1X: -99.8; control1Y: 53.5; control2X: -94.1; control2Y: 51.2; x: -98.6; y: 54 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 40.8; y: -12.2 }
- PathCubic { control1X: 41.46; control1Y: -12.554; control2X: 41.451; control2Y: -13.524; x: 42.031; y: -13.697 }
- PathCubic { control1X: 43.18; control1Y: -14.041; control2X: 43.344; control2Y: -15.108; x: 43.862; y: -15.892 }
- PathCubic { control1X: 44.735; control1Y: -17.211; control2X: 44.928; control2Y: -18.744; x: 45.51; y: -20.235 }
- PathCubic { control1X: 45.782; control1Y: -20.935; control2X: 45.809; control2Y: -21.89; x: 45.496; y: -22.55 }
- PathCubic { control1X: 44.322; control1Y: -25.031; control2X: 43.62; control2Y: -27.48; x: 42.178; y: -29.906 }
- PathCubic { control1X: 41.91; control1Y: -30.356; control2X: 41.648; control2Y: -31.15; x: 41.447; y: -31.748 }
- PathCubic { control1X: 40.984; control1Y: -33.132; control2X: 39.727; control2Y: -34.123; x: 38.867; y: -35.443 }
- PathCubic { control1X: 38.579; control1Y: -35.884; control2X: 39.104; control2Y: -36.809; x: 38.388; y: -36.893 }
- PathCubic { control1X: 37.491; control1Y: -36.998; control2X: 36.042; control2Y: -37.578; x: 35.809; y: -36.552 }
- PathCubic { control1X: 35.221; control1Y: -33.965; control2X: 36.232; control2Y: -31.442; x: 37.2; y: -29 }
- PathCubic { control1X: 36.418; control1Y: -28.308; control2X: 36.752; control2Y: -27.387; x: 36.904; y: -26.62 }
- PathCubic { control1X: 37.614; control1Y: -23.014; control2X: 36.416; control2Y: -19.662; x: 35.655; y: -16.188 }
- PathCubic { control1X: 35.632; control1Y: -16.084; control2X: 35.974; control2Y: -15.886; x: 35.946; y: -15.824 }
- PathCubic { control1X: 34.724; control1Y: -13.138; control2X: 33.272; control2Y: -10.693; x: 31.453; y: -8.312 }
- PathCubic { control1X: 30.695; control1Y: -7.32; control2X: 29.823; control2Y: -6.404; x: 29.326; y: -5.341 }
- PathCubic { control1X: 28.958; control1Y: -4.554; control2X: 28.55; control2Y: -3.588; x: 28.8; y: -2.6 }
- PathCubic { control1X: 25.365; control1Y: 0.18; control2X: 23.115; control2Y: 4.025; x: 20.504; y: 7.871 }
- PathCubic { control1X: 20.042; control1Y: 8.551; control2X: 20.333; control2Y: 9.76; x: 20.884; y: 10.029 }
- PathCubic { control1X: 21.697; control1Y: 10.427; control2X: 22.653; control2Y: 9.403; x: 23.123; y: 8.557 }
- PathCubic { control1X: 23.512; control1Y: 7.859; control2X: 23.865; control2Y: 7.209; x: 24.356; y: 6.566 }
- PathCubic { control1X: 24.489; control1Y: 6.391; control2X: 24.31; control2Y: 5.972; x: 24.445; y: 5.851 }
- PathCubic { control1X: 27.078; control1Y: 3.504; control2X: 28.747; control2Y: 0.568; x: 31.2; y: -1.8 }
- PathCubic { control1X: 33.15; control1Y: -2.129; control2X: 34.687; control2Y: -3.127; x: 36.435; y: -4.14 }
- PathCubic { control1X: 36.743; control1Y: -4.319; control2X: 37.267; control2Y: -4.07; x: 37.557; y: -4.265 }
- PathCubic { control1X: 39.31; control1Y: -5.442; control2X: 39.308; control2Y: -7.478; x: 39.414; y: -9.388 }
- PathCubic { control1X: 39.464; control1Y: -10.272; control2X: 39.66; control2Y: -11.589; x: 40.8; y: -12.2 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 31.959; y: -16.666 }
- PathCubic { control1X: 32.083; control1Y: -16.743; control2X: 31.928; control2Y: -17.166; x: 32.037; y: -17.382 }
- PathCubic { control1X: 32.199; control1Y: -17.706; control2X: 32.602; control2Y: -17.894; x: 32.764; y: -18.218 }
- PathCubic { control1X: 32.873; control1Y: -18.434; control2X: 32.71; control2Y: -18.814; x: 32.846; y: -18.956 }
- PathCubic { control1X: 35.179; control1Y: -21.403; control2X: 35.436; control2Y: -24.427; x: 34.4; y: -27.4 }
- PathCubic { control1X: 35.424; control1Y: -28.02; control2X: 35.485; control2Y: -29.282; x: 35.06; y: -30.129 }
- PathCubic { control1X: 34.207; control1Y: -31.829; control2X: 34.014; control2Y: -33.755; x: 33.039; y: -35.298 }
- PathCubic { control1X: 32.237; control1Y: -36.567; control2X: 30.659; control2Y: -37.811; x: 29.288; y: -36.508 }
- PathCubic { control1X: 28.867; control1Y: -36.108; control2X: 28.546; control2Y: -35.321; x: 28.824; y: -34.609 }
- PathCubic { control1X: 28.888; control1Y: -34.446; control2X: 29.173; control2Y: -34.3; x: 29.146; y: -34.218 }
- PathCubic { control1X: 29.039; control1Y: -33.894; control2X: 28.493; control2Y: -33.67; x: 28.487; y: -33.398 }
- PathCubic { control1X: 28.457; control1Y: -31.902; control2X: 27.503; control2Y: -30.391; x: 28.133; y: -29.062 }
- PathCubic { control1X: 28.905; control1Y: -27.433; control2X: 29.724; control2Y: -25.576; x: 30.4; y: -23.8 }
- PathCubic { control1X: 29.166; control1Y: -21.684; control2X: 30.199; control2Y: -19.235; x: 28.446; y: -17.358 }
- PathCubic { control1X: 28.31; control1Y: -17.212; control2X: 28.319; control2Y: -16.826; x: 28.441; y: -16.624 }
- PathCubic { control1X: 28.733; control1Y: -16.138; control2X: 29.139; control2Y: -15.732; x: 29.625; y: -15.44 }
- PathCubic { control1X: 29.827; control1Y: -15.319; control2X: 30.175; control2Y: -15.317; x: 30.375; y: -15.441 }
- PathCubic { control1X: 30.953; control1Y: -15.803; control2X: 31.351; control2Y: -16.29; x: 31.959; y: -16.666 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 94.771; y: -26.977 }
- PathCubic { control1X: 96.16; control1Y: -25.185; control2X: 96.45; control2Y: -22.39; x: 94.401; y: -21 }
- PathCubic { control1X: 94.951; control1Y: -17.691; control2X: 98.302; control2Y: -19.67; x: 100.401; y: -20.2 }
- PathCubic { control1X: 100.292; control1Y: -20.588; control2X: 100.519; control2Y: -20.932; x: 100.802; y: -20.937 }
- PathCubic { control1X: 101.859; control1Y: -20.952; control2X: 102.539; control2Y: -21.984; x: 103.601; y: -21.8 }
- PathCubic { control1X: 104.035; control1Y: -23.357; control2X: 105.673; control2Y: -24.059; x: 106.317; y: -25.439 }
- PathCubic { control1X: 108.043; control1Y: -29.134; control2X: 107.452; control2Y: -33.407; x: 104.868; y: -36.653 }
- PathCubic { control1X: 104.666; control1Y: -36.907; control2X: 104.883; control2Y: -37.424; x: 104.759; y: -37.786 }
- PathCubic { control1X: 104.003; control1Y: -39.997; control2X: 101.935; control2Y: -40.312; x: 100.001; y: -41 }
- PathCubic { control1X: 98.824; control1Y: -44.875; control2X: 98.163; control2Y: -48.906; x: 96.401; y: -52.6 }
- PathCubic { control1X: 94.787; control1Y: -52.85; control2X: 94.089; control2Y: -54.589; x: 92.752; y: -55.309 }
- PathCubic { control1X: 91.419; control1Y: -56.028; control2X: 90.851; control2Y: -54.449; x: 90.892; y: -53.403 }
- PathCubic { control1X: 90.899; control1Y: -53.198; control2X: 91.351; control2Y: -52.974; x: 91.181; y: -52.609 }
- PathCubic { control1X: 91.105; control1Y: -52.445; control2X: 90.845; control2Y: -52.334; x: 90.845; y: -52.2 }
- PathCubic { control1X: 90.846; control1Y: -52.065; control2X: 91.067; control2Y: -51.934; x: 91.201; y: -51.8 }
- PathCubic { control1X: 90.283; control1Y: -50.98; control2X: 88.86; control2Y: -50.503; x: 88.565; y: -49.358 }
- PathCubic { control1X: 87.611; control1Y: -45.648; control2X: 90.184; control2Y: -42.523; x: 91.852; y: -39.322 }
- PathCubic { control1X: 92.443; control1Y: -38.187; control2X: 91.707; control2Y: -36.916; x: 90.947; y: -35.708 }
- PathCubic { control1X: 90.509; control1Y: -35.013; control2X: 90.617; control2Y: -33.886; x: 90.893; y: -33.03 }
- PathCubic { control1X: 91.645; control1Y: -30.699; control2X: 93.236; control2Y: -28.96; x: 94.771; y: -26.977 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 57.611; y: -8.591 }
- PathCubic { control1X: 56.124; control1Y: -6.74; control2X: 52.712; control2Y: -4.171; x: 55.629; y: -2.243 }
- PathCubic { control1X: 55.823; control1Y: -2.114; control2X: 56.193; control2Y: -2.11; x: 56.366; y: -2.244 }
- PathCubic { control1X: 58.387; control1Y: -3.809; control2X: 60.39; control2Y: -4.712; x: 62.826; y: -5.294 }
- PathCubic { control1X: 62.95; control1Y: -5.323; control2X: 63.224; control2Y: -4.856; x: 63.593; y: -5.017 }
- PathCubic { control1X: 65.206; control1Y: -5.72; control2X: 67.216; control2Y: -5.662; x: 68.4; y: -7 }
- PathCubic { control1X: 72.167; control1Y: -6.776; control2X: 75.732; control2Y: -7.892; x: 79.123; y: -9.2 }
- PathCubic { control1X: 80.284; control1Y: -9.648; control2X: 81.554; control2Y: -10.207; x: 82.755; y: -10.709 }
- PathCubic { control1X: 84.131; control1Y: -11.285; control2X: 85.335; control2Y: -12.213; x: 86.447; y: -13.354 }
- PathCubic { control1X: 86.58; control1Y: -13.49; control2X: 86.934; control2Y: -13.4; x: 87.201; y: -13.4 }
- PathCubic { control1X: 87.161; control1Y: -14.263; control2X: 88.123; control2Y: -14.39; x: 88.37; y: -15.012 }
- PathCubic { control1X: 88.462; control1Y: -15.244; control2X: 88.312; control2Y: -15.64; x: 88.445; y: -15.742 }
- PathCubic { control1X: 90.583; control1Y: -17.372; control2X: 91.503; control2Y: -19.39; x: 90.334; y: -21.767 }
- PathCubic { control1X: 90.049; control1Y: -22.345; control2X: 89.8; control2Y: -22.963; x: 89.234; y: -23.439 }
- PathCubic { control1X: 88.149; control1Y: -24.35; control2X: 87.047; control2Y: -23.496; x: 86; y: -23.8 }
- PathCubic { control1X: 85.841; control1Y: -23.172; control2X: 85.112; control2Y: -23.344; x: 84.726; y: -23.146 }
- PathCubic { control1X: 83.867; control1Y: -22.707; control2X: 82.534; control2Y: -23.292; x: 81.675; y: -22.854 }
- PathCubic { control1X: 80.313; control1Y: -22.159; control2X: 79.072; control2Y: -21.99; x: 77.65; y: -21.613 }
- PathCubic { control1X: 77.338; control1Y: -21.531; control2X: 76.56; control2Y: -21.627; x: 76.4; y: -21 }
- PathCubic { control1X: 76.266; control1Y: -21.134; control2X: 76.118; control2Y: -21.368; x: 76.012; y: -21.346 }
- PathCubic { control1X: 74.104; control1Y: -20.95; control2X: 72.844; control2Y: -20.736; x: 71.543; y: -19.044 }
- PathCubic { control1X: 71.44; control1Y: -18.911; control2X: 70.998; control2Y: -19.09; x: 70.839; y: -18.955 }
- PathCubic { control1X: 69.882; control1Y: -18.147; control2X: 69.477; control2Y: -16.913; x: 68.376; y: -16.241 }
- PathCubic { control1X: 68.175; control1Y: -16.118; control2X: 67.823; control2Y: -16.286; x: 67.629; y: -16.157 }
- PathCubic { control1X: 66.983; control1Y: -15.726; control2X: 66.616; control2Y: -15.085; x: 65.974; y: -14.638 }
- PathCubic { control1X: 65.645; control1Y: -14.409; control2X: 65.245; control2Y: -14.734; x: 65.277; y: -14.99 }
- PathCubic { control1X: 65.522; control1Y: -16.937; control2X: 66.175; control2Y: -18.724; x: 65.6; y: -20.6 }
- PathCubic { control1X: 67.677; control1Y: -23.12; control2X: 70.194; control2Y: -25.069; x: 72; y: -27.8 }
- PathCubic { control1X: 72.015; control1Y: -29.966; control2X: 72.707; control2Y: -32.112; x: 72.594; y: -34.189 }
- PathCubic { control1X: 72.584; control1Y: -34.382; control2X: 72.296; control2Y: -35.115; x: 72.17; y: -35.462 }
- PathCubic { control1X: 71.858; control1Y: -36.316; control2X: 72.764; control2Y: -37.382; x: 71.92; y: -38.106 }
- PathCubic { control1X: 70.516; control1Y: -39.309; control2X: 69.224; control2Y: -38.433; x: 68.4; y: -37 }
- PathCubic { control1X: 66.562; control1Y: -36.61; control2X: 64.496; control2Y: -35.917; x: 62.918; y: -37.151 }
- PathCubic { control1X: 61.911; control1Y: -37.938; control2X: 61.333; control2Y: -38.844; x: 60.534; y: -39.9 }
- PathCubic { control1X: 59.549; control1Y: -41.202; control2X: 59.884; control2Y: -42.638; x: 59.954; y: -44.202 }
- PathCubic { control1X: 59.96; control1Y: -44.33; control2X: 59.645; control2Y: -44.466; x: 59.645; y: -44.6 }
- PathCubic { control1X: 59.646; control1Y: -44.735; control2X: 59.866; control2Y: -44.866; x: 60; y: -45 }
- PathCubic { control1X: 59.294; control1Y: -45.626; control2X: 59.019; control2Y: -46.684; x: 58; y: -47 }
- PathCubic { control1X: 58.305; control1Y: -48.092; control2X: 57.629; control2Y: -48.976; x: 56.758; y: -49.278 }
- PathCubic { control1X: 54.763; control1Y: -49.969; control2X: 53.086; control2Y: -48.057; x: 51.194; y: -47.984 }
- PathCubic { control1X: 50.68; control1Y: -47.965; control2X: 50.213; control2Y: -49.003; x: 49.564; y: -49.328 }
- PathCubic { control1X: 49.132; control1Y: -49.544; control2X: 48.428; control2Y: -49.577; x: 48.066; y: -49.311 }
- PathCubic { control1X: 47.378; control1Y: -48.807; control2X: 46.789; control2Y: -48.693; x: 46.031; y: -48.488 }
- PathCubic { control1X: 44.414; control1Y: -48.052; control2X: 43.136; control2Y: -46.958; x: 41.656; y: -46.103 }
- PathCubic { control1X: 40.171; control1Y: -45.246; control2X: 39.216; control2Y: -43.809; x: 38.136; y: -42.489 }
- PathCubic { control1X: 37.195; control1Y: -41.337; control2X: 37.059; control2Y: -38.923; x: 38.479; y: -38.423 }
- PathCubic { control1X: 40.322; control1Y: -37.773; control2X: 41.626; control2Y: -40.476; x: 43.592; y: -40.15 }
- PathCubic { control1X: 43.904; control1Y: -40.099; control2X: 44.11; control2Y: -39.788; x: 44; y: -39.4 }
- PathCubic { control1X: 44.389; control1Y: -39.291; control2X: 44.607; control2Y: -39.52; x: 44.8; y: -39.8 }
- PathCubic { control1X: 45.658; control1Y: -38.781; control2X: 46.822; control2Y: -38.444; x: 47.76; y: -37.571 }
- PathCubic { control1X: 48.73; control1Y: -36.667; control2X: 50.476; control2Y: -37.085; x: 51.491; y: -36.088 }
- PathCubic { control1X: 53.02; control1Y: -34.586; control2X: 52.461; control2Y: -31.905; x: 54.4; y: -30.6 }
- PathCubic { control1X: 53.814; control1Y: -29.287; control2X: 53.207; control2Y: -28.01; x: 52.872; y: -26.583 }
- PathCubic { control1X: 52.59; control1Y: -25.377; control2X: 53.584; control2Y: -24.18; x: 54.795; y: -24.271 }
- PathCubic { control1X: 56.053; control1Y: -24.365; control2X: 56.315; control2Y: -25.124; x: 56.8; y: -26.2 }
- PathCubic { control1X: 57.067; control1Y: -25.933; control2X: 57.536; control2Y: -25.636; x: 57.495; y: -25.42 }
- PathCubic { control1X: 57.038; control1Y: -23.033; control2X: 56.011; control2Y: -21.04; x: 55.553; y: -18.609 }
- PathCubic { control1X: 55.494; control1Y: -18.292; control2X: 55.189; control2Y: -18.09; x: 54.8; y: -18.2 }
- PathCubic { control1X: 54.332; control1Y: -14.051; control2X: 50.28; control2Y: -11.657; x: 47.735; y: -8.492 }
- PathCubic { control1X: 47.332; control1Y: -7.99; control2X: 47.328; control2Y: -6.741; x: 47.737; y: -6.338 }
- PathCubic { control1X: 49.14; control1Y: -4.951; control2X: 51.1; control2Y: -6.497; x: 52.8; y: -7 }
- PathCubic { control1X: 53.013; control1Y: -8.206; control2X: 53.872; control2Y: -9.148; x: 55.204; y: -9.092 }
- PathCubic { control1X: 55.46; control1Y: -9.082; control2X: 55.695; control2Y: -9.624; x: 56.019; y: -9.754 }
- PathCubic { control1X: 56.367; control1Y: -9.892; control2X: 56.869; control2Y: -9.668; x: 57.155; y: -9.866 }
- PathCubic { control1X: 58.884; control1Y: -11.061; control2X: 60.292; control2Y: -12.167; x: 62.03; y: -13.356 }
- PathCubic { control1X: 62.222; control1Y: -13.487; control2X: 62.566; control2Y: -13.328; x: 62.782; y: -13.436 }
- PathCubic { control1X: 63.107; control1Y: -13.598; control2X: 63.294; control2Y: -13.985; x: 63.617; y: -14.17 }
- PathCubic { control1X: 63.965; control1Y: -14.37; control2X: 64.207; control2Y: -14.08; x: 64.4; y: -13.8 }
- PathCubic { control1X: 63.754; control1Y: -13.451; control2X: 63.75; control2Y: -12.494; x: 63.168; y: -12.292 }
- PathCubic { control1X: 62.393; control1Y: -12.024; control2X: 61.832; control2Y: -11.511; x: 61.158; y: -11.064 }
- PathCubic { control1X: 60.866; control1Y: -10.871; control2X: 60.207; control2Y: -11.119; x: 60.103; y: -10.94 }
- PathCubic { control1X: 59.505; control1Y: -9.912; control2X: 58.321; control2Y: -9.474; x: 57.611; y: -8.591 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 2.2; y: -58 }
- PathCubic { control1X: 2.2; control1Y: -58; control2X: -7.038; control2Y: -60.872; x: -18.2; y: -35.2 }
- PathCubic { control1X: -18.2; control1Y: -35.2; control2X: -20.6; control2Y: -30; x: -23; y: -28 }
- PathCubic { control1X: -25.4; control1Y: -26; control2X: -36.6; control2Y: -22.4; x: -38.6; y: -18.4 }
- PathLine { x: -49; y: -2.4 }
- PathCubic { control1X: -49; control1Y: -2.4; control2X: -34.2; control2Y: -18.4; x: -31; y: -20.8 }
- PathCubic { control1X: -31; control1Y: -20.8; control2X: -23; control2Y: -29.2; x: -26.2; y: -22.4 }
- PathCubic { control1X: -26.2; control1Y: -22.4; control2X: -40.2; control2Y: -11.6; x: -39; y: -2.4 }
- PathCubic { control1X: -39; control1Y: -2.4; control2X: -44.6; control2Y: 12; x: -45.4; y: 14 }
- PathCubic { control1X: -45.4; control1Y: 14; control2X: -29.4; control2Y: -18; x: -27; y: -19.2 }
- PathCubic { control1X: -24.6; control1Y: -20.4; control2X: -23.4; control2Y: -20.4; x: -24.6; y: -16.8 }
- PathCubic { control1X: -25.8; control1Y: -13.2; control2X: -26.2; control2Y: 3.2; x: -29; y: 5.2 }
- PathCubic { control1X: -29; control1Y: 5.2; control2X: -21; control2Y: -15.2; x: -21.8; y: -18.4 }
- PathCubic { control1X: -21.8; control1Y: -18.4; control2X: -18.6; control2Y: -22; x: -16.2; y: -16.8 }
- PathLine { x: -17.4; y: -0.8 }
- PathLine { x: -13; y: 11.2 }
- PathCubic { control1X: -13; control1Y: 11.2; control2X: -15.4; control2Y: 0; x: -13.8; y: -15.6 }
- PathCubic { control1X: -13.8; control1Y: -15.6; control2X: -15.8; control2Y: -26; x: -11.8; y: -20.4 }
- PathCubic { control1X: -7.8; control1Y: -14.8; control2X: 1.8; control2Y: -8.8; x: 1.8; y: -4 }
- PathCubic { control1X: 1.8; control1Y: -4; control2X: -3.4; control2Y: -21.6; x: -12.6; y: -26.4 }
- PathLine { x: -16.6; y: -20.4 }
- PathLine { x: -17.8; y: -22.4 }
- PathCubic { control1X: -17.8; control1Y: -22.4; control2X: -21.4; control2Y: -23.2; x: -17; y: -30 }
- PathCubic { control1X: -12.6; control1Y: -36.8; control2X: -13; control2Y: -37.6; x: -13; y: -37.6 }
- PathCubic { control1X: -13; control1Y: -37.6; control2X: -6.6; control2Y: -30.4; x: -5; y: -30.4 }
- PathCubic { control1X: -5; control1Y: -30.4; control2X: 8.2; control2Y: -38; x: 9.4; y: -13.6 }
- PathCubic { control1X: 9.4; control1Y: -13.6; control2X: 16.2; control2Y: -28; x: 7; y: -34.8 }
- PathCubic { control1X: 7; control1Y: -34.8; control2X: -7.8; control2Y: -36.8; x: -6.6; y: -42 }
- PathLine { x: 0.6; y: -54.4 }
- PathCubic { control1X: 4.2; control1Y: -59.6; control2X: 2.6; control2Y: -56.8; x: 2.6; y: -56.8 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -17.8; y: -41.6 }
- PathCubic { control1X: -17.8; control1Y: -41.6; control2X: -30.6; control2Y: -41.6; x: -33.8; y: -36.4 }
- PathLine { x: -41; y: -26.8 }
- PathCubic { control1X: -41; control1Y: -26.8; control2X: -23.8; control2Y: -36.8; x: -19.8; y: -38 }
- PathCubic { control1X: -15.8; control1Y: -39.2; control2X: -17.8; control2Y: -41.6; x: -17.8; y: -41.6 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -57.8; y: -35.2 }
- PathCubic { control1X: -57.8; control1Y: -35.2; control2X: -59.8; control2Y: -34; x: -60.2; y: -31.2 }
- PathCubic { control1X: -60.6; control1Y: -28.4; control2X: -63; control2Y: -28; x: -62.2; y: -25.2 }
- PathCubic { control1X: -61.4; control1Y: -22.4; control2X: -59.4; control2Y: -20; x: -59.4; y: -24 }
- PathCubic { control1X: -59.4; control1Y: -28; control2X: -57.8; control2Y: -30; x: -57; y: -31.2 }
- PathCubic { control1X: -56.2; control1Y: -32.4; control2X: -54.6; control2Y: -36.8; x: -57.8; y: -35.2 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -66.6; y: 26 }
- PathCubic { control1X: -66.6; control1Y: 26; control2X: -75; control2Y: 22; x: -78.2; y: 18.4 }
- PathCubic { control1X: -81.4; control1Y: 14.8; control2X: -80.948; control2Y: 19.966; x: -85.8; y: 19.6 }
- PathCubic { control1X: -91.647; control1Y: 19.159; control2X: -90.6; control2Y: 3.2; x: -90.6; y: 3.2 }
- PathLine { x: -94.6; y: 10.8 }
- PathCubic { control1X: -94.6; control1Y: 10.8; control2X: -95.8; control2Y: 25.2; x: -87.8; y: 22.8 }
- PathCubic { control1X: -83.893; control1Y: 21.628; control2X: -82.6; control2Y: 23.2; x: -84.2; y: 24 }
- PathCubic { control1X: -85.8; control1Y: 24.8; control2X: -78.6; control2Y: 25.2; x: -81.4; y: 26.8 }
- PathCubic { control1X: -84.2; control1Y: 28.4; control2X: -69.8; control2Y: 23.2; x: -72.2; y: 33.6 }
- PathLine { x: -66.6; y: 26 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -79.2; y: 40.4 }
- PathCubic { control1X: -79.2; control1Y: 40.4; control2X: -94.6; control2Y: 44.8; x: -98.2; y: 35.2 }
- PathCubic { control1X: -98.2; control1Y: 35.2; control2X: -103; control2Y: 37.6; x: -100.8; y: 40.6 }
- PathCubic { control1X: -98.6; control1Y: 43.6; control2X: -97.4; control2Y: 44; x: -97.4; y: 44 }
- PathCubic { control1X: -97.4; control1Y: 44; control2X: -92; control2Y: 45.2; x: -92.6; y: 46 }
- PathCubic { control1X: -93.2; control1Y: 46.8; control2X: -95.6; control2Y: 50.2; x: -95.6; y: 50.2 }
- PathCubic { control1X: -95.6; control1Y: 50.2; control2X: -85.4; control2Y: 44.2; x: -79.2; y: 40.4 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: 149.201; y: 118.601 }
- PathCubic { control1X: 148.774; control1Y: 120.735; control2X: 147.103; control2Y: 121.536; x: 145.201; y: 122.201 }
- PathCubic { control1X: 143.284; control1Y: 121.243; control2X: 140.686; control2Y: 118.137; x: 138.801; y: 120.201 }
- PathCubic { control1X: 138.327; control1Y: 119.721; control2X: 137.548; control2Y: 119.661; x: 137.204; y: 118.999 }
- PathCubic { control1X: 136.739; control1Y: 118.101; control2X: 137.011; control2Y: 117.055; x: 136.669; y: 116.257 }
- PathCubic { control1X: 136.124; control1Y: 114.985; control2X: 135.415; control2Y: 113.619; x: 135.601; y: 112.201 }
- PathCubic { control1X: 137.407; control1Y: 111.489; control2X: 138.002; control2Y: 109.583; x: 137.528; y: 107.82 }
- PathCubic { control1X: 137.459; control1Y: 107.563; control2X: 137.03; control2Y: 107.366; x: 137.23; y: 107.017 }
- PathCubic { control1X: 137.416; control1Y: 106.694; control2X: 137.734; control2Y: 106.467; x: 138.001; y: 106.2 }
- PathCubic { control1X: 137.866; control1Y: 106.335; control2X: 137.721; control2Y: 106.568; x: 137.61; y: 106.548 }
- PathCubic { control1X: 137; control1Y: 106.442; control2X: 137.124; control2Y: 105.805; x: 137.254; y: 105.418 }
- PathCubic { control1X: 137.839; control1Y: 103.672; control2X: 139.853; control2Y: 103.408; x: 141.201; y: 104.6 }
- PathCubic { control1X: 141.457; control1Y: 104.035; control2X: 141.966; control2Y: 104.229; x: 142.401; y: 104.2 }
- PathCubic { control1X: 142.351; control1Y: 103.621; control2X: 142.759; control2Y: 103.094; x: 142.957; y: 102.674 }
- PathCubic { control1X: 143.475; control1Y: 101.576; control2X: 145.104; control2Y: 102.682; x: 145.901; y: 102.07 }
- PathCubic { control1X: 146.977; control1Y: 101.245; control2X: 148.04; control2Y: 100.546; x: 149.118; y: 101.149 }
- PathCubic { control1X: 150.927; control1Y: 102.162; control2X: 152.636; control2Y: 103.374; x: 153.835; y: 105.115 }
- PathCubic { control1X: 154.41; control1Y: 105.949; control2X: 154.65; control2Y: 107.23; x: 154.592; y: 108.188 }
- PathCubic { control1X: 154.554; control1Y: 108.835; control2X: 153.173; control2Y: 108.483; x: 152.83; y: 109.412 }
- PathCubic { control1X: 152.185; control1Y: 111.16; control2X: 154.016; control2Y: 111.679; x: 154.772; y: 113.017 }
- PathCubic { control1X: 154.97; control1Y: 113.366; control2X: 154.706; control2Y: 113.67; x: 154.391; y: 113.768 }
- PathCubic { control1X: 153.98; control1Y: 113.896; control2X: 153.196; control2Y: 113.707; x: 153.334; y: 114.16 }
- PathCubic { control1X: 154.306; control1Y: 117.353; control2X: 151.55; control2Y: 118.031; x: 149.201; y: 118.601 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeWidth: -1
- PathMove { x: 139.6; y: 138.201 }
- PathCubic { control1X: 139.593; control1Y: 136.463; control2X: 137.992; control2Y: 134.707; x: 139.201; y: 133.001 }
- PathCubic { control1X: 139.336; control1Y: 133.135; control2X: 139.467; control2Y: 133.356; x: 139.601; y: 133.356 }
- PathCubic { control1X: 139.736; control1Y: 133.356; control2X: 139.867; control2Y: 133.135; x: 140.001; y: 133.001 }
- PathCubic { control1X: 141.496; control1Y: 135.217; control2X: 145.148; control2Y: 136.145; x: 145.006; y: 138.991 }
- PathCubic { control1X: 144.984; control1Y: 139.438; control2X: 143.897; control2Y: 140.356; x: 144.801; y: 141.001 }
- PathCubic { control1X: 142.988; control1Y: 142.349; control2X: 142.933; control2Y: 144.719; x: 142.001; y: 146.601 }
- PathCubic { control1X: 140.763; control1Y: 146.315; control2X: 139.551; control2Y: 145.952; x: 138.401; y: 145.401 }
- PathCubic { control1X: 138.753; control1Y: 143.915; control2X: 138.636; control2Y: 142.231; x: 139.456; y: 140.911 }
- PathCubic { control1X: 139.89; control1Y: 140.213; control2X: 139.603; control2Y: 139.134; x: 139.6; y: 138.201 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -26.6; y: 129.201 }
- PathCubic { control1X: -26.6; control1Y: 129.201; control2X: -43.458; control2Y: 139.337; x: -29.4; y: 124.001 }
- PathCubic { control1X: -20.6; control1Y: 114.401; control2X: -10.6; control2Y: 108.801; x: -10.6; y: 108.801 }
- PathCubic { control1X: -10.6; control1Y: 108.801; control2X: -0.2; control2Y: 104.4; x: 3.4; y: 103.2 }
- PathCubic { control1X: 7; control1Y: 102; control2X: 22.2; control2Y: 96.8; x: 25.4; y: 96.4 }
- PathCubic { control1X: 28.6; control1Y: 96; control2X: 38.2; control2Y: 92; x: 45; y: 96 }
- PathCubic { control1X: 51.8; control1Y: 100; control2X: 59.8; control2Y: 104.4; x: 59.8; y: 104.4 }
- PathCubic { control1X: 59.8; control1Y: 104.4; control2X: 43.4; control2Y: 96; x: 39.8; y: 98.4 }
- PathCubic { control1X: 36.2; control1Y: 100.8; control2X: 29; control2Y: 100.4; x: 23; y: 103.6 }
- PathCubic { control1X: 23; control1Y: 103.6; control2X: 8.2; control2Y: 108.001; x: 5; y: 110.001 }
- PathCubic { control1X: 1.8; control1Y: 112.001; control2X: -8.6; control2Y: 123.601; x: -10.2; y: 122.801 }
- PathCubic { control1X: -11.8; control1Y: 122.001; control2X: -9.8; control2Y: 121.601; x: -8.6; y: 118.801 }
- PathCubic { control1X: -7.4; control1Y: 116.001; control2X: -9.4; control2Y: 114.401; x: -17.4; y: 120.801 }
- PathCubic { control1X: -25.4; control1Y: 127.201; control2X: -26.6; control2Y: 129.201; x: -26.6; y: 129.201 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -19.195; y: 123.234 }
- PathCubic { control1X: -19.195; control1Y: 123.234; control2X: -17.785; control2Y: 110.194; x: -9.307; y: 111.859 }
- PathCubic { control1X: -9.307; control1Y: 111.859; control2X: -1.081; control2Y: 107.689; x: 1.641; y: 105.721 }
- PathCubic { control1X: 1.641; control1Y: 105.721; control2X: 9.78; control2Y: 104.019; x: 11.09; y: 103.402 }
- PathCubic { control1X: 29.569; control1Y: 94.702; control2X: 44.288; control2Y: 99.221; x: 44.835; y: 98.101 }
- PathCubic { control1X: 45.381; control1Y: 96.982; control2X: 65.006; control2Y: 104.099; x: 68.615; y: 108.185 }
- PathCubic { control1X: 69.006; control1Y: 108.628; control2X: 58.384; control2Y: 102.588; x: 48.686; y: 100.697 }
- PathCubic { control1X: 40.413; control1Y: 99.083; control2X: 18.811; control2Y: 100.944; x: 7.905; y: 106.48 }
- PathCubic { control1X: 4.932; control1Y: 107.989; control2X: -4.013; control2Y: 113.773; x: -6.544; y: 113.662 }
- PathCubic { control1X: -9.075; control1Y: 113.55; control2X: -19.195; control2Y: 123.234; x: -19.195; y: 123.234 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -23; y: 148.801 }
- PathCubic { control1X: -23; control1Y: 148.801; control2X: -38.2; control2Y: 146.401; x: -21.4; y: 144.801 }
- PathCubic { control1X: -21.4; control1Y: 144.801; control2X: -3.4; control2Y: 142.801; x: 0.6; y: 137.601 }
- PathCubic { control1X: 0.6; control1Y: 137.601; control2X: 14.2; control2Y: 128.401; x: 17; y: 128.001 }
- PathCubic { control1X: 19.8; control1Y: 127.601; control2X: 49.8; control2Y: 120.401; x: 50.2; y: 118.001 }
- PathCubic { control1X: 50.6; control1Y: 115.601; control2X: 56.2; control2Y: 115.601; x: 57.8; y: 116.401 }
- PathCubic { control1X: 59.4; control1Y: 117.201; control2X: 58.6; control2Y: 118.401; x: 55.8; y: 119.201 }
- PathCubic { control1X: 53; control1Y: 120.001; control2X: 21.8; control2Y: 136.401; x: 15.4; y: 137.601 }
- PathCubic { control1X: 9; control1Y: 138.801; control2X: -2.6; control2Y: 146.401; x: -7.4; y: 147.601 }
- PathCubic { control1X: -12.2; control1Y: 148.801; control2X: -23; control2Y: 148.801; x: -23; y: 148.801 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -3.48; y: 141.403 }
- PathCubic { control1X: -3.48; control1Y: 141.403; control2X: -12.062; control2Y: 140.574; x: -3.461; y: 139.755 }
- PathCubic { control1X: -3.461; control1Y: 139.755; control2X: 5.355; control2Y: 136.331; x: 7.403; y: 133.668 }
- PathCubic { control1X: 7.403; control1Y: 133.668; control2X: 14.367; control2Y: 128.957; x: 15.8; y: 128.753 }
- PathCubic { control1X: 17.234; control1Y: 128.548; control2X: 31.194; control2Y: 124.861; x: 31.399; y: 123.633 }
- PathCubic { control1X: 31.604; control1Y: 122.404; control2X: 65.67; control2Y: 109.823; x: 70.09; y: 113.013 }
- PathCubic { control1X: 73.001; control1Y: 115.114; control2X: 63.1; control2Y: 113.437; x: 53.466; y: 117.847 }
- PathCubic { control1X: 52.111; control1Y: 118.467; control2X: 18.258; control2Y: 133.054; x: 14.981; y: 133.668 }
- PathCubic { control1X: 11.704; control1Y: 134.283; control2X: 5.765; control2Y: 138.174; x: 3.307; y: 138.788 }
- PathCubic { control1X: 0.85; control1Y: 139.403; control2X: -3.48; control2Y: 141.403; x: -3.48; y: 141.403 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -11.4; y: 143.601 }
- PathCubic { control1X: -11.4; control1Y: 143.601; control2X: -6.2; control2Y: 143.201; x: -7.4; y: 144.801 }
- PathCubic { control1X: -8.6; control1Y: 146.401; control2X: -11; control2Y: 145.601; x: -11; y: 145.601 }
- PathLine { x: -11.4; y: 143.601 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -18.6; y: 145.201 }
- PathCubic { control1X: -18.6; control1Y: 145.201; control2X: -13.4; control2Y: 144.801; x: -14.6; y: 146.401 }
- PathCubic { control1X: -15.8; control1Y: 148.001; control2X: -18.2; control2Y: 147.201; x: -18.2; y: 147.201 }
- PathLine { x: -18.6; y: 145.201 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -29; y: 146.801 }
- PathCubic { control1X: -29; control1Y: 146.801; control2X: -23.8; control2Y: 146.401; x: -25; y: 148.001 }
- PathCubic { control1X: -26.2; control1Y: 149.601; control2X: -28.6; control2Y: 148.801; x: -28.6; y: 148.801 }
- PathLine { x: -29; y: 146.801 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -36.6; y: 147.601 }
- PathCubic { control1X: -36.6; control1Y: 147.601; control2X: -31.4; control2Y: 147.201; x: -32.6; y: 148.801 }
- PathCubic { control1X: -33.8; control1Y: 150.401; control2X: -36.2; control2Y: 149.601; x: -36.2; y: 149.601 }
- PathLine { x: -36.6; y: 147.601 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 1.8; y: 108.001 }
- PathCubic { control1X: 1.8; control1Y: 108.001; control2X: 6.2; control2Y: 108.001; x: 5; y: 109.601 }
- PathCubic { control1X: 3.8; control1Y: 111.201; control2X: 0.6; control2Y: 110.801; x: 0.6; y: 110.801 }
- PathLine { x: 1.8; y: 108.001 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -8.2; y: 113.601 }
- PathCubic { control1X: -8.2; control1Y: 113.601; control2X: -1.694; control2Y: 111.46; x: -4.2; y: 114.801 }
- PathCubic { control1X: -5.4; control1Y: 116.401; control2X: -7.8; control2Y: 115.601; x: -7.8; y: 115.601 }
- PathLine { x: -8.2; y: 113.601 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -19.4; y: 118.401 }
- PathCubic { control1X: -19.4; control1Y: 118.401; control2X: -14.2; control2Y: 118.001; x: -15.4; y: 119.601 }
- PathCubic { control1X: -16.6; control1Y: 121.201; control2X: -19; control2Y: 120.401; x: -19; y: 120.401 }
- PathLine { x: -19.4; y: 118.401 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -27; y: 124.401 }
- PathCubic { control1X: -27; control1Y: 124.401; control2X: -21.8; control2Y: 124.001; x: -23; y: 125.601 }
- PathCubic { control1X: -24.2; control1Y: 127.201; control2X: -26.6; control2Y: 126.401; x: -26.6; y: 126.401 }
- PathLine { x: -27; y: 124.401 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -33.8; y: 129.201 }
- PathCubic { control1X: -33.8; control1Y: 129.201; control2X: -28.6; control2Y: 128.801; x: -29.8; y: 130.401 }
- PathCubic { control1X: -31; control1Y: 132.001; control2X: -33.4; control2Y: 131.201; x: -33.4; y: 131.201 }
- PathLine { x: -33.8; y: 129.201 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 5.282; y: 135.598 }
- PathCubic { control1X: 5.282; control1Y: 135.598; control2X: 12.203; control2Y: 135.066; x: 10.606; y: 137.195 }
- PathCubic { control1X: 9.009; control1Y: 139.325; control2X: 5.814; control2Y: 138.26; x: 5.814; y: 138.26 }
- PathLine { x: 5.282; y: 135.598 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 15.682; y: 130.798 }
- PathCubic { control1X: 15.682; control1Y: 130.798; control2X: 22.603; control2Y: 130.266; x: 21.006; y: 132.395 }
- PathCubic { control1X: 19.409; control1Y: 134.525; control2X: 16.214; control2Y: 133.46; x: 16.214; y: 133.46 }
- PathLine { x: 15.682; y: 130.798 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 26.482; y: 126.398 }
- PathCubic { control1X: 26.482; control1Y: 126.398; control2X: 33.403; control2Y: 125.866; x: 31.806; y: 127.995 }
- PathCubic { control1X: 30.209; control1Y: 130.125; control2X: 27.014; control2Y: 129.06; x: 27.014; y: 129.06 }
- PathLine { x: 26.482; y: 126.398 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 36.882; y: 121.598 }
- PathCubic { control1X: 36.882; control1Y: 121.598; control2X: 43.803; control2Y: 121.066; x: 42.206; y: 123.195 }
- PathCubic { control1X: 40.609; control1Y: 125.325; control2X: 37.414; control2Y: 124.26; x: 37.414; y: 124.26 }
- PathLine { x: 36.882; y: 121.598 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 9.282; y: 103.598 }
- PathCubic { control1X: 9.282; control1Y: 103.598; control2X: 16.203; control2Y: 103.066; x: 14.606; y: 105.195 }
- PathCubic { control1X: 13.009; control1Y: 107.325; control2X: 9.014; control2Y: 107.06; x: 9.014; y: 107.06 }
- PathLine { x: 9.282; y: 103.598 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 19.282; y: 100.398 }
- PathCubic { control1X: 19.282; control1Y: 100.398; control2X: 26.203; control2Y: 99.866; x: 24.606; y: 101.995 }
- PathCubic { control1X: 23.009; control1Y: 104.125; control2X: 18.614; control2Y: 103.86; x: 18.614; y: 103.86 }
- PathLine { x: 19.282; y: 100.398 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -3.4; y: 140.401 }
- PathCubic { control1X: -3.4; control1Y: 140.401; control2X: 1.8; control2Y: 140.001; x: 0.6; y: 141.601 }
- PathCubic { control1X: -0.6; control1Y: 143.201; control2X: -3; control2Y: 142.401; x: -3; y: 142.401 }
- PathLine { x: -3.4; y: 140.401 }
- }
-
- ShapePath {
- fillColor: "#992600"
- strokeWidth: -1
- PathMove { x: -76.6; y: 41.2 }
- PathCubic { control1X: -76.6; control1Y: 41.2; control2X: -81; control2Y: 50; x: -81.4; y: 53.2 }
- PathCubic { control1X: -81.4; control1Y: 53.2; control2X: -80.6; control2Y: 44.4; x: -79.4; y: 42.4 }
- PathCubic { control1X: -78.2; control1Y: 40.4; control2X: -76.6; control2Y: 41.2; x: -76.6; y: 41.2 }
- }
-
- ShapePath {
- fillColor: "#992600"
- strokeWidth: -1
- PathMove { x: -95; y: 55.2 }
- PathCubic { control1X: -95; control1Y: 55.2; control2X: -98.2; control2Y: 69.6; x: -97.8; y: 72.4 }
- PathCubic { control1X: -97.8; control1Y: 72.4; control2X: -99; control2Y: 60.8; x: -98.6; y: 59.6 }
- PathCubic { control1X: -98.2; control1Y: 58.4; control2X: -95; control2Y: 55.2; x: -95; y: 55.2 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -74.2; y: -19.4 }
- PathLine { x: -74.4; y: -16.2 }
- PathLine { x: -76.6; y: -16 }
- PathCubic { control1X: -76.6; control1Y: -16; control2X: -62.4; control2Y: -3.4; x: -61.8; y: 4.2 }
- PathCubic { control1X: -61.8; control1Y: 4.2; control2X: -61; control2Y: -4; x: -74.2; y: -19.4 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -70.216; y: -18.135 }
- PathCubic { control1X: -70.647; control1Y: -18.551; control2X: -70.428; control2Y: -19.296; x: -70.836; y: -19.556 }
- PathCubic { control1X: -71.645; control1Y: -20.072; control2X: -69.538; control2Y: -20.129; x: -69.766; y: -20.845 }
- PathCubic { control1X: -70.149; control1Y: -22.051; control2X: -69.962; control2Y: -22.072; x: -70.084; y: -23.348 }
- PathCubic { control1X: -70.141; control1Y: -23.946; control2X: -69.553; control2Y: -25.486; x: -69.168; y: -25.926 }
- PathCubic { control1X: -67.722; control1Y: -27.578; control2X: -69.046; control2Y: -30.51; x: -67.406; y: -32.061 }
- PathCubic { control1X: -67.102; control1Y: -32.35; control2X: -66.726; control2Y: -32.902; x: -66.441; y: -33.32 }
- PathCubic { control1X: -65.782; control1Y: -34.283; control2X: -64.598; control2Y: -34.771; x: -63.648; y: -35.599 }
- PathCubic { control1X: -63.33; control1Y: -35.875; control2X: -63.531; control2Y: -36.702; x: -62.962; y: -36.61 }
- PathCubic { control1X: -62.248; control1Y: -36.495; control2X: -61.007; control2Y: -36.625; x: -61.052; y: -35.784 }
- PathCubic { control1X: -61.165; control1Y: -33.664; control2X: -62.494; control2Y: -31.944; x: -63.774; y: -30.276 }
- PathCubic { control1X: -63.323; control1Y: -29.572; control2X: -63.781; control2Y: -28.937; x: -64.065; y: -28.38 }
- PathCubic { control1X: -65.4; control1Y: -25.76; control2X: -65.211; control2Y: -22.919; x: -65.385; y: -20.079 }
- PathCubic { control1X: -65.39; control1Y: -19.994; control2X: -65.697; control2Y: -19.916; x: -65.689; y: -19.863 }
- PathCubic { control1X: -65.336; control1Y: -17.528; control2X: -64.752; control2Y: -15.329; x: -63.873; y: -13.1 }
- PathCubic { control1X: -63.507; control1Y: -12.17; control2X: -63.036; control2Y: -11.275; x: -62.886; y: -10.348 }
- PathCubic { control1X: -62.775; control1Y: -9.662; control2X: -62.672; control2Y: -8.829; x: -63.08; y: -8.124 }
- PathCubic { control1X: -61.045; control1Y: -5.234; control2X: -62.354; control2Y: -2.583; x: -61.185; y: 0.948 }
- PathCubic { control1X: -60.978; control1Y: 1.573; control2X: -59.286; control2Y: 3.487; x: -59.749; y: 3.326 }
- PathCubic { control1X: -62.262; control1Y: 2.455; control2X: -62.374; control2Y: 2.057; x: -62.551; y: 1.304 }
- PathCubic { control1X: -62.697; control1Y: 0.681; control2X: -63.027; control2Y: -0.696; x: -63.264; y: -1.298 }
- PathCubic { control1X: -63.328; control1Y: -1.462; control2X: -63.499; control2Y: -3.346; x: -63.577; y: -3.468 }
- PathCubic { control1X: -65.09; control1Y: -5.85; control2X: -63.732; control2Y: -5.674; x: -65.102; y: -8.032 }
- PathCubic { control1X: -66.53; control1Y: -8.712; control2X: -67.496; control2Y: -9.816; x: -68.619; y: -10.978 }
- PathCubic { control1X: -68.817; control1Y: -11.182; control2X: -67.674; control2Y: -11.906; x: -67.855; y: -12.119 }
- PathCubic { control1X: -68.947; control1Y: -13.408; control2X: -70.1; control2Y: -14.175; x: -69.764; y: -15.668 }
- PathCubic { control1X: -69.609; control1Y: -16.358; control2X: -69.472; control2Y: -17.415; x: -70.216; y: -18.135 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -73.8; y: -16.4 }
- PathCubic { control1X: -73.8; control1Y: -16.4; control2X: -73.4; control2Y: -9.6; x: -71; y: -8 }
- PathCubic { control1X: -68.6; control1Y: -6.4; control2X: -69.8; control2Y: -7.2; x: -73; y: -8.4 }
- PathCubic { control1X: -76.2; control1Y: -9.6; control2X: -75; control2Y: -10.4; x: -75; y: -10.4 }
- PathCubic { control1X: -75; control1Y: -10.4; control2X: -77.8; control2Y: -10; x: -75.4; y: -8 }
- PathCubic { control1X: -73; control1Y: -6; control2X: -69.4; control2Y: -3.6; x: -71; y: -3.6 }
- PathCubic { control1X: -72.6; control1Y: -3.6; control2X: -80.2; control2Y: -7.6; x: -80.2; y: -10.4 }
- PathCubic { control1X: -80.2; control1Y: -13.2; control2X: -81.2; control2Y: -17.3; x: -81.2; y: -17.3 }
- PathCubic { control1X: -81.2; control1Y: -17.3; control2X: -80.1; control2Y: -18.1; x: -75.3; y: -18 }
- PathCubic { control1X: -75.3; control1Y: -18; control2X: -73.9; control2Y: -17.3; x: -73.8; y: -16.4 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -74.6; y: 2.2 }
- PathCubic { control1X: -74.6; control1Y: 2.2; control2X: -83.12; control2Y: -0.591; x: -101.6; y: 2.8 }
- PathCubic { control1X: -101.6; control1Y: 2.8; control2X: -92.569; control2Y: 0.722; x: -73.8; y: 3 }
- PathCubic { control1X: -63.5; control1Y: 4.25; control2X: -74.6; control2Y: 2.2; x: -74.6; y: 2.2 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -72.502; y: 2.129 }
- PathCubic { control1X: -72.502; control1Y: 2.129; control2X: -80.748; control2Y: -1.389; x: -99.453; y: 0.392 }
- PathCubic { control1X: -99.453; control1Y: 0.392; control2X: -90.275; control2Y: -0.897; x: -71.774; y: 2.995 }
- PathCubic { control1X: -61.62; control1Y: 5.131; control2X: -72.502; control2Y: 2.129; x: -72.502; y: 2.129 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -70.714; y: 2.222 }
- PathCubic { control1X: -70.714; control1Y: 2.222; control2X: -78.676; control2Y: -1.899; x: -97.461; y: -1.514 }
- PathCubic { control1X: -97.461; control1Y: -1.514; control2X: -88.213; control2Y: -2.118; x: -70.052; y: 3.14 }
- PathCubic { control1X: -60.086; control1Y: 6.025; control2X: -70.714; control2Y: 2.222; x: -70.714; y: 2.222 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -69.444; y: 2.445 }
- PathCubic { control1X: -69.444; control1Y: 2.445; control2X: -76.268; control2Y: -1.862; x: -93.142; y: -2.96 }
- PathCubic { control1X: -93.142; control1Y: -2.96; control2X: -84.803; control2Y: -2.79; x: -68.922; y: 3.319 }
- PathCubic { control1X: -60.206; control1Y: 6.672; control2X: -69.444; control2Y: 2.445; x: -69.444; y: 2.445 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 45.84; y: 12.961 }
- PathCubic { control1X: 45.84; control1Y: 12.961; control2X: 44.91; control2Y: 13.605; x: 45.124; y: 12.424 }
- PathCubic { control1X: 45.339; control1Y: 11.243; control2X: 73.547; control2Y: -1.927; x: 77.161; y: -1.677 }
- PathCubic { control1X: 77.161; control1Y: -1.677; control2X: 46.913; control2Y: 11.529; x: 45.84; y: 12.961 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 42.446; y: 13.6 }
- PathCubic { control1X: 42.446; control1Y: 13.6; control2X: 41.57; control2Y: 14.315; x: 41.691; y: 13.121 }
- PathCubic { control1X: 41.812; control1Y: 11.927; control2X: 68.899; control2Y: -3.418; x: 72.521; y: -3.452 }
- PathCubic { control1X: 72.521; control1Y: -3.452; control2X: 43.404; control2Y: 12.089; x: 42.446; y: 13.6 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 39.16; y: 14.975 }
- PathCubic { control1X: 39.16; control1Y: 14.975; control2X: 38.332; control2Y: 15.747; x: 38.374; y: 14.547 }
- PathCubic { control1X: 38.416; control1Y: 13.348; control2X: 58.233; control2Y: -2.149; x: 68.045; y: -4.023 }
- PathCubic { control1X: 68.045; control1Y: -4.023; control2X: 50.015; control2Y: 4.104; x: 39.16; y: 14.975 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 36.284; y: 16.838 }
- PathCubic { control1X: 36.284; control1Y: 16.838; control2X: 35.539; control2Y: 17.532; x: 35.577; y: 16.453 }
- PathCubic { control1X: 35.615; control1Y: 15.373; control2X: 53.449; control2Y: 1.426; x: 62.28; y: -0.26 }
- PathCubic { control1X: 62.28; control1Y: -0.26; control2X: 46.054; control2Y: 7.054; x: 36.284; y: 16.838 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 4.6; y: 164.801 }
- PathCubic { control1X: 4.6; control1Y: 164.801; control2X: -10.6; control2Y: 162.401; x: 6.2; y: 160.801 }
- PathCubic { control1X: 6.2; control1Y: 160.801; control2X: 24.2; control2Y: 158.801; x: 28.2; y: 153.601 }
- PathCubic { control1X: 28.2; control1Y: 153.601; control2X: 41.8; control2Y: 144.401; x: 44.6; y: 144.001 }
- PathCubic { control1X: 47.4; control1Y: 143.601; control2X: 63.8; control2Y: 140.001; x: 64.2; y: 137.601 }
- PathCubic { control1X: 64.6; control1Y: 135.201; control2X: 70.6; control2Y: 132.801; x: 72.2; y: 133.601 }
- PathCubic { control1X: 73.8; control1Y: 134.401; control2X: 73.8; control2Y: 143.601; x: 71; y: 144.401 }
- PathCubic { control1X: 68.2; control1Y: 145.201; control2X: 49.4; control2Y: 152.401; x: 43; y: 153.601 }
- PathCubic { control1X: 36.6; control1Y: 154.801; control2X: 25; control2Y: 162.401; x: 20.2; y: 163.601 }
- PathCubic { control1X: 15.4; control1Y: 164.801; control2X: 4.6; control2Y: 164.801; x: 4.6; y: 164.801 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 77.6; y: 127.401 }
- PathCubic { control1X: 77.6; control1Y: 127.401; control2X: 74.6; control2Y: 129.001; x: 73.4; y: 131.601 }
- PathCubic { control1X: 73.4; control1Y: 131.601; control2X: 67; control2Y: 142.201; x: 52.8; y: 145.401 }
- PathCubic { control1X: 52.8; control1Y: 145.401; control2X: 29.8; control2Y: 154.401; x: 22; y: 156.401 }
- PathCubic { control1X: 22; control1Y: 156.401; control2X: 8.6; control2Y: 161.401; x: 1.2; y: 160.601 }
- PathCubic { control1X: 1.2; control1Y: 160.601; control2X: -5.8; control2Y: 160.801; x: 0.4; y: 162.401 }
- PathCubic { control1X: 0.4; control1Y: 162.401; control2X: 20.6; control2Y: 160.401; x: 24; y: 158.601 }
- PathCubic { control1X: 24; control1Y: 158.601; control2X: 39.6; control2Y: 153.401; x: 42.6; y: 150.801 }
- PathCubic { control1X: 45.6; control1Y: 148.201; control2X: 63.8; control2Y: 143.201; x: 66; y: 141.201 }
- PathCubic { control1X: 68.2; control1Y: 139.201; control2X: 78; control2Y: 130.801; x: 77.6; y: 127.401 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 18.882; y: 158.911 }
- PathCubic { control1X: 18.882; control1Y: 158.911; control2X: 24.111; control2Y: 158.685; x: 22.958; y: 160.234 }
- PathCubic { control1X: 21.805; control1Y: 161.784; control2X: 19.357; control2Y: 160.91; x: 19.357; y: 160.91 }
- PathLine { x: 18.882; y: 158.911 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 11.68; y: 160.263 }
- PathCubic { control1X: 11.68; control1Y: 160.263; control2X: 16.908; control2Y: 160.037; x: 15.756; y: 161.586 }
- PathCubic { control1X: 14.603; control1Y: 163.136; control2X: 12.155; control2Y: 162.263; x: 12.155; y: 162.263 }
- PathLine { x: 11.68; y: 160.263 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 1.251; y: 161.511 }
- PathCubic { control1X: 1.251; control1Y: 161.511; control2X: 6.48; control2Y: 161.284; x: 5.327; y: 162.834 }
- PathCubic { control1X: 4.174; control1Y: 164.383; control2X: 1.726; control2Y: 163.51; x: 1.726; y: 163.51 }
- PathLine { x: 1.251; y: 161.511 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -6.383; y: 162.055 }
- PathCubic { control1X: -6.383; control1Y: 162.055; control2X: -1.154; control2Y: 161.829; x: -2.307; y: 163.378 }
- PathCubic { control1X: -3.46; control1Y: 164.928; control2X: -5.908; control2Y: 164.054; x: -5.908; y: 164.054 }
- PathLine { x: -6.383; y: 162.055 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 35.415; y: 151.513 }
- PathCubic { control1X: 35.415; control1Y: 151.513; control2X: 42.375; control2Y: 151.212; x: 40.84; y: 153.274 }
- PathCubic { control1X: 39.306; control1Y: 155.336; control2X: 36.047; control2Y: 154.174; x: 36.047; y: 154.174 }
- PathLine { x: 35.415; y: 151.513 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 45.73; y: 147.088 }
- PathCubic { control1X: 45.73; control1Y: 147.088; control2X: 51.689; control2Y: 143.787; x: 51.155; y: 148.849 }
- PathCubic { control1X: 50.885; control1Y: 151.405; control2X: 46.362; control2Y: 149.749; x: 46.362; y: 149.749 }
- PathLine { x: 45.73; y: 147.088 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 54.862; y: 144.274 }
- PathCubic { control1X: 54.862; control1Y: 144.274; control2X: 62.021; control2Y: 140.573; x: 60.287; y: 146.035 }
- PathCubic { control1X: 59.509; control1Y: 148.485; control2X: 55.493; control2Y: 146.935; x: 55.493; y: 146.935 }
- PathLine { x: 54.862; y: 144.274 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 64.376; y: 139.449 }
- PathCubic { control1X: 64.376; control1Y: 139.449; control2X: 68.735; control2Y: 134.548; x: 69.801; y: 141.21 }
- PathCubic { control1X: 70.207; control1Y: 143.748; control2X: 65.008; control2Y: 142.11; x: 65.008; y: 142.11 }
- PathLine { x: 64.376; y: 139.449 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 26.834; y: 155.997 }
- PathCubic { control1X: 26.834; control1Y: 155.997; control2X: 32.062; control2Y: 155.77; x: 30.91; y: 157.32 }
- PathCubic { control1X: 29.757; control1Y: 158.869; control2X: 27.308; control2Y: 157.996; x: 27.308; y: 157.996 }
- PathLine { x: 26.834; y: 155.997 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 62.434; y: 34.603 }
- PathCubic { control1X: 62.434; control1Y: 34.603; control2X: 61.708; control2Y: 35.268; x: 61.707; y: 34.197 }
- PathCubic { control1X: 61.707; control1Y: 33.127; control2X: 79.191; control2Y: 19.863; x: 88.034; y: 18.479 }
- PathCubic { control1X: 88.034; control1Y: 18.479; control2X: 71.935; control2Y: 25.208; x: 62.434; y: 34.603 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: 65.4; y: 98.4 }
- PathCubic { control1X: 65.4; control1Y: 98.4; control2X: 87.401; control2Y: 120.801; x: 96.601; y: 124.401 }
- PathCubic { control1X: 96.601; control1Y: 124.401; control2X: 105.801; control2Y: 135.601; x: 101.801; y: 161.601 }
- PathCubic { control1X: 101.801; control1Y: 161.601; control2X: 98.601; control2Y: 169.201; x: 95.401; y: 148.401 }
- PathCubic { control1X: 95.401; control1Y: 148.401; control2X: 98.601; control2Y: 123.201; x: 87.401; y: 139.201 }
- PathCubic { control1X: 87.401; control1Y: 139.201; control2X: 79; control2Y: 129.301; x: 85.4; y: 129.601 }
- PathCubic { control1X: 85.4; control1Y: 129.601; control2X: 88.601; control2Y: 131.601; x: 89.001; y: 130.001 }
- PathCubic { control1X: 89.401; control1Y: 128.401; control2X: 81.4; control2Y: 114.801; x: 64.2; y: 100.4 }
- PathCubic { control1X: 47; control1Y: 86; control2X: 65.4; control2Y: 98.4; x: 65.4; y: 98.4 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 7; y: 137.201 }
- PathCubic { control1X: 7; control1Y: 137.201; control2X: 6.8; control2Y: 135.401; x: 8.6; y: 136.201 }
- PathCubic { control1X: 10.4; control1Y: 137.001; control2X: 104.601; control2Y: 143.201; x: 136.201; y: 167.201 }
- PathCubic { control1X: 136.201; control1Y: 167.201; control2X: 91.001; control2Y: 144.001; x: 7; y: 137.201 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 17.4; y: 132.801 }
- PathCubic { control1X: 17.4; control1Y: 132.801; control2X: 17.2; control2Y: 131.001; x: 19; y: 131.801 }
- PathCubic { control1X: 20.8; control1Y: 132.601; control2X: 157.401; control2Y: 131.601; x: 181.001; y: 164.001 }
- PathCubic { control1X: 181.001; control1Y: 164.001; control2X: 159.001; control2Y: 138.801; x: 17.4; y: 132.801 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 29; y: 128.801 }
- PathCubic { control1X: 29; control1Y: 128.801; control2X: 28.8; control2Y: 127.001; x: 30.6; y: 127.801 }
- PathCubic { control1X: 32.4; control1Y: 128.601; control2X: 205.801; control2Y: 115.601; x: 229.401; y: 148.001 }
- PathCubic { control1X: 229.401; control1Y: 148.001; control2X: 219.801; control2Y: 122.401; x: 29; y: 128.801 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 39; y: 124.001 }
- PathCubic { control1X: 39; control1Y: 124.001; control2X: 38.8; control2Y: 122.201; x: 40.6; y: 123.001 }
- PathCubic { control1X: 42.4; control1Y: 123.801; control2X: 164.601; control2Y: 85.2; x: 188.201; y: 117.601 }
- PathCubic { control1X: 188.201; control1Y: 117.601; control2X: 174.801; control2Y: 93; x: 39; y: 124.001 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -19; y: 146.801 }
- PathCubic { control1X: -19; control1Y: 146.801; control2X: -19.2; control2Y: 145.001; x: -17.4; y: 145.801 }
- PathCubic { control1X: -15.6; control1Y: 146.601; control2X: 2.2; control2Y: 148.801; x: 4.2; y: 187.601 }
- PathCubic { control1X: 4.2; control1Y: 187.601; control2X: -3; control2Y: 145.601; x: -19; y: 146.801 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -27.8; y: 148.401 }
- PathCubic { control1X: -27.8; control1Y: 148.401; control2X: -28; control2Y: 146.601; x: -26.2; y: 147.401 }
- PathCubic { control1X: -24.4; control1Y: 148.201; control2X: -10.2; control2Y: 143.601; x: -13; y: 182.401 }
- PathCubic { control1X: -13; control1Y: 182.401; control2X: -11.8; control2Y: 147.201; x: -27.8; y: 148.401 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -35.8; y: 148.801 }
- PathCubic { control1X: -35.8; control1Y: 148.801; control2X: -36; control2Y: 147.001; x: -34.2; y: 147.801 }
- PathCubic { control1X: -32.4; control1Y: 148.601; control2X: -17; control2Y: 149.201; x: -29.4; y: 171.601 }
- PathCubic { control1X: -29.4; control1Y: 171.601; control2X: -19.8; control2Y: 147.601; x: -35.8; y: 148.801 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 11.526; y: 104.465 }
- PathCubic { control1X: 11.526; control1Y: 104.465; control2X: 11.082; control2Y: 106.464; x: 12.631; y: 105.247 }
- PathCubic { control1X: 28.699; control1Y: 92.622; control2X: 61.141; control2Y: 33.72; x: 116.826; y: 28.086 }
- PathCubic { control1X: 116.826; control1Y: 28.086; control2X: 78.518; control2Y: 15.976; x: 11.526; y: 104.465 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 22.726; y: 102.665 }
- PathCubic { control1X: 22.726; control1Y: 102.665; control2X: 21.363; control2Y: 101.472; x: 23.231; y: 100.847 }
- PathCubic { control1X: 25.099; control1Y: 100.222; control2X: 137.541; control2Y: 27.72; x: 176.826; y: 35.686 }
- PathCubic { control1X: 176.826; control1Y: 35.686; control2X: 149.719; control2Y: 28.176; x: 22.726; y: 102.665 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 1.885; y: 108.767 }
- PathCubic { control1X: 1.885; control1Y: 108.767; control2X: 1.376; control2Y: 110.366; x: 3.087; y: 109.39 }
- PathCubic { control1X: 12.062; control1Y: 104.27; control2X: 15.677; control2Y: 47.059; x: 59.254; y: 45.804 }
- PathCubic { control1X: 59.254; control1Y: 45.804; control2X: 26.843; control2Y: 31.09; x: 1.885; y: 108.767 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -18.038; y: 119.793 }
- PathCubic { control1X: -18.038; control1Y: 119.793; control2X: -19.115; control2Y: 121.079; x: -17.162; y: 120.825 }
- PathCubic { control1X: -6.916; control1Y: 119.493; control2X: 14.489; control2Y: 78.222; x: 58.928; y: 83.301 }
- PathCubic { control1X: 58.928; control1Y: 83.301; control2X: 26.962; control2Y: 68.955; x: -18.038; y: 119.793 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -6.8; y: 113.667 }
- PathCubic { control1X: -6.8; control1Y: 113.667; control2X: -7.611; control2Y: 115.136; x: -5.742; y: 114.511 }
- PathCubic { control1X: 4.057; control1Y: 111.237; control2X: 17.141; control2Y: 66.625; x: 61.729; y: 63.078 }
- PathCubic { control1X: 61.729; control1Y: 63.078; control2X: 27.603; control2Y: 55.135; x: -6.8; y: 113.667 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -25.078; y: 124.912 }
- PathCubic { control1X: -25.078; control1Y: 124.912; control2X: -25.951; control2Y: 125.954; x: -24.369; y: 125.748 }
- PathCubic { control1X: -16.07; control1Y: 124.669; control2X: 1.268; control2Y: 91.24; x: 37.264; y: 95.354 }
- PathCubic { control1X: 37.264; control1Y: 95.354; control2X: 11.371; control2Y: 83.734; x: -25.078; y: 124.912 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -32.677; y: 130.821 }
- PathCubic { control1X: -32.677; control1Y: 130.821; control2X: -33.682; control2Y: 131.866; x: -32.091; y: 131.748 }
- PathCubic { control1X: -27.923; control1Y: 131.439; control2X: 2.715; control2Y: 98.36; x: 21.183; y: 113.862 }
- PathCubic { control1X: 21.183; control1Y: 113.862; control2X: 9.168; control2Y: 95.139; x: -32.677; y: 130.821 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 36.855; y: 98.898 }
- PathCubic { control1X: 36.855; control1Y: 98.898; control2X: 35.654; control2Y: 97.543; x: 37.586; y: 97.158 }
- PathCubic { control1X: 39.518; control1Y: 96.774; control2X: 160.221; control2Y: 39.061; x: 198.184; y: 51.927 }
- PathCubic { control1X: 198.184; control1Y: 51.927; control2X: 172.243; control2Y: 41.053; x: 36.855; y: 98.898 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 3.4; y: 163.201 }
- PathCubic { control1X: 3.4; control1Y: 163.201; control2X: 3.2; control2Y: 161.401; x: 5; y: 162.201 }
- PathCubic { control1X: 6.8; control1Y: 163.001; control2X: 22.2; control2Y: 163.601; x: 9.8; y: 186.001 }
- PathCubic { control1X: 9.8; control1Y: 186.001; control2X: 19.4; control2Y: 162.001; x: 3.4; y: 163.201 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 13.8; y: 161.601 }
- PathCubic { control1X: 13.8; control1Y: 161.601; control2X: 13.6; control2Y: 159.801; x: 15.4; y: 160.601 }
- PathCubic { control1X: 17.2; control1Y: 161.401; control2X: 35; control2Y: 163.601; x: 37; y: 202.401 }
- PathCubic { control1X: 37; control1Y: 202.401; control2X: 29.8; control2Y: 160.401; x: 13.8; y: 161.601 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 20.6; y: 160.001 }
- PathCubic { control1X: 20.6; control1Y: 160.001; control2X: 20.4; control2Y: 158.201; x: 22.2; y: 159.001 }
- PathCubic { control1X: 24; control1Y: 159.801; control2X: 48.6; control2Y: 163.201; x: 72.2; y: 195.601 }
- PathCubic { control1X: 72.2; control1Y: 195.601; control2X: 36.6; control2Y: 158.801; x: 20.6; y: 160.001 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 28.225; y: 157.972 }
- PathCubic { control1X: 28.225; control1Y: 157.972; control2X: 27.788; control2Y: 156.214; x: 29.678; y: 156.768 }
- PathCubic { control1X: 31.568; control1Y: 157.322; control2X: 52.002; control2Y: 155.423; x: 90.099; y: 189.599 }
- PathCubic { control1X: 90.099; control1Y: 189.599; control2X: 43.924; control2Y: 154.656; x: 28.225; y: 157.972 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 38.625; y: 153.572 }
- PathCubic { control1X: 38.625; control1Y: 153.572; control2X: 38.188; control2Y: 151.814; x: 40.078; y: 152.368 }
- PathCubic { control1X: 41.968; control1Y: 152.922; control2X: 76.802; control2Y: 157.423; x: 128.499; y: 192.399 }
- PathCubic { control1X: 128.499; control1Y: 192.399; control2X: 54.324; control2Y: 150.256; x: 38.625; y: 153.572 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -1.8; y: 142.001 }
- PathCubic { control1X: -1.8; control1Y: 142.001; control2X: -2; control2Y: 140.201; x: -0.2; y: 141.001 }
- PathCubic { control1X: 1.6; control1Y: 141.801; control2X: 55; control2Y: 144.401; x: 85.4; y: 171.201 }
- PathCubic { control1X: 85.4; control1Y: 171.201; control2X: 50.499; control2Y: 146.426; x: -1.8; y: 142.001 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: -11.8; y: 146.001 }
- PathCubic { control1X: -11.8; control1Y: 146.001; control2X: -12; control2Y: 144.201; x: -10.2; y: 145.001 }
- PathCubic { control1X: -8.4; control1Y: 145.801; control2X: 16.2; control2Y: 149.201; x: 39.8; y: 181.601 }
- PathCubic { control1X: 39.8; control1Y: 181.601; control2X: 4.2; control2Y: 144.801; x: -11.8; y: 146.001 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 49.503; y: 148.962 }
- PathCubic { control1X: 49.503; control1Y: 148.962; control2X: 48.938; control2Y: 147.241; x: 50.864; y: 147.655 }
- PathCubic { control1X: 52.79; control1Y: 148.068; control2X: 87.86; control2Y: 150.004; x: 141.981; y: 181.098 }
- PathCubic { control1X: 141.981; control1Y: 181.098; control2X: 64.317; control2Y: 146.704; x: 49.503; y: 148.962 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 57.903; y: 146.562 }
- PathCubic { control1X: 57.903; control1Y: 146.562; control2X: 57.338; control2Y: 144.841; x: 59.264; y: 145.255 }
- PathCubic { control1X: 61.19; control1Y: 145.668; control2X: 96.26; control2Y: 147.604; x: 150.381; y: 178.698 }
- PathCubic { control1X: 150.381; control1Y: 178.698; control2X: 73.317; control2Y: 143.904; x: 57.903; y: 146.562 }
- }
-
- ShapePath {
- fillColor: "#ffffff"
- strokeColor: "#000000"
- strokeWidth: 0.1
- PathMove { x: 67.503; y: 141.562 }
- PathCubic { control1X: 67.503; control1Y: 141.562; control2X: 66.938; control2Y: 139.841; x: 68.864; y: 140.255 }
- PathCubic { control1X: 70.79; control1Y: 140.668; control2X: 113.86; control2Y: 145.004; x: 203.582; y: 179.298 }
- PathCubic { control1X: 203.582; control1Y: 179.298; control2X: 82.917; control2Y: 138.904; x: 67.503; y: 141.562 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -43.8; y: 148.401 }
- PathCubic { control1X: -43.8; control1Y: 148.401; control2X: -38.6; control2Y: 148.001; x: -39.8; y: 149.601 }
- PathCubic { control1X: -41; control1Y: 151.201; control2X: -43.4; control2Y: 150.401; x: -43.4; y: 150.401 }
- PathLine { x: -43.8; y: 148.401 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -13; y: 162.401 }
- PathCubic { control1X: -13; control1Y: 162.401; control2X: -7.8; control2Y: 162.001; x: -9; y: 163.601 }
- PathCubic { control1X: -10.2; control1Y: 165.201; control2X: -12.6; control2Y: 164.401; x: -12.6; y: 164.401 }
- PathLine { x: -13; y: 162.401 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -21.8; y: 162.001 }
- PathCubic { control1X: -21.8; control1Y: 162.001; control2X: -16.6; control2Y: 161.601; x: -17.8; y: 163.201 }
- PathCubic { control1X: -19; control1Y: 164.801; control2X: -21.4; control2Y: 164.001; x: -21.4; y: 164.001 }
- PathLine { x: -21.8; y: 162.001 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -117.169; y: 150.182 }
- PathCubic { control1X: -117.169; control1Y: 150.182; control2X: -112.124; control2Y: 151.505; x: -113.782; y: 152.624 }
- PathCubic { control1X: -115.439; control1Y: 153.744; control2X: -117.446; control2Y: 152.202; x: -117.446; y: 152.202 }
- PathLine { x: -117.169; y: 150.182 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -115.169; y: 140.582 }
- PathCubic { control1X: -115.169; control1Y: 140.582; control2X: -110.124; control2Y: 141.905; x: -111.782; y: 143.024 }
- PathCubic { control1X: -113.439; control1Y: 144.144; control2X: -115.446; control2Y: 142.602; x: -115.446; y: 142.602 }
- PathLine { x: -115.169; y: 140.582 }
- }
-
- ShapePath {
- fillColor: "#000000"
- strokeWidth: -1
- PathMove { x: -122.369; y: 136.182 }
- PathCubic { control1X: -122.369; control1Y: 136.182; control2X: -117.324; control2Y: 137.505; x: -118.982; y: 138.624 }
- PathCubic { control1X: -120.639; control1Y: 139.744; control2X: -122.646; control2Y: 138.202; x: -122.646; y: 138.202 }
- PathLine { x: -122.369; y: 136.182 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -42.6; y: 211.201 }
- PathCubic { control1X: -42.6; control1Y: 211.201; control2X: -44.2; control2Y: 211.201; x: -48.2; y: 213.201 }
- PathCubic { control1X: -50.2; control1Y: 213.201; control2X: -61.4; control2Y: 216.801; x: -67; y: 226.801 }
- PathCubic { control1X: -67; control1Y: 226.801; control2X: -54.6; control2Y: 217.201; x: -42.6; y: 211.201 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 45.116; y: 303.847 }
- PathCubic { control1X: 45.257; control1Y: 304.105; control2X: 45.312; control2Y: 304.525; x: 45.604; y: 304.542 }
- PathCubic { control1X: 46.262; control1Y: 304.582; control2X: 47.495; control2Y: 304.883; x: 47.37; y: 304.247 }
- PathCubic { control1X: 46.522; control1Y: 299.941; control2X: 45.648; control2Y: 295.004; x: 41.515; y: 293.197 }
- PathCubic { control1X: 40.876; control1Y: 292.918; control2X: 39.434; control2Y: 293.331; x: 39.36; y: 294.215 }
- PathCubic { control1X: 39.233; control1Y: 295.739; control2X: 39.116; control2Y: 297.088; x: 39.425; y: 298.554 }
- PathCubic { control1X: 39.725; control1Y: 299.975; control2X: 41.883; control2Y: 299.985; x: 42.8; y: 298.601 }
- PathCubic { control1X: 43.736; control1Y: 300.273; control2X: 44.168; control2Y: 302.116; x: 45.116; y: 303.847 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 34.038; y: 308.581 }
- PathCubic { control1X: 34.786; control1Y: 309.994; control2X: 34.659; control2Y: 311.853; x: 36.074; y: 312.416 }
- PathCubic { control1X: 36.814; control1Y: 312.71; control2X: 38.664; control2Y: 311.735; x: 38.246; y: 310.661 }
- PathCubic { control1X: 37.444; control1Y: 308.6; control2X: 37.056; control2Y: 306.361; x: 35.667; y: 304.55 }
- PathCubic { control1X: 35.467; control1Y: 304.288; control2X: 35.707; control2Y: 303.755; x: 35.547; y: 303.427 }
- PathCubic { control1X: 34.953; control1Y: 302.207; control2X: 33.808; control2Y: 301.472; x: 32.4; y: 301.801 }
- PathCubic { control1X: 31.285; control1Y: 304.004; control2X: 32.433; control2Y: 306.133; x: 33.955; y: 307.842 }
- PathCubic { control1X: 34.091; control1Y: 307.994; control2X: 33.925; control2Y: 308.37; x: 34.038; y: 308.581 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -5.564; y: 303.391 }
- PathCubic { control1X: -5.672; control1Y: 303.014; control2X: -5.71; control2Y: 302.551; x: -5.545; y: 302.23 }
- PathCubic { control1X: -5.014; control1Y: 301.197; control2X: -4.221; control2Y: 300.075; x: -4.558; y: 299.053 }
- PathCubic { control1X: -4.906; control1Y: 297.997; control2X: -6.022; control2Y: 298.179; x: -6.672; y: 298.748 }
- PathCubic { control1X: -7.807; control1Y: 299.742; control2X: -7.856; control2Y: 301.568; x: -8.547; y: 302.927 }
- PathCubic { control1X: -8.743; control1Y: 303.313; control2X: -8.692; control2Y: 303.886; x: -9.133; y: 304.277 }
- PathCubic { control1X: -9.607; control1Y: 304.698; control2X: -10.047; control2Y: 306.222; x: -9.951; y: 306.793 }
- PathCubic { control1X: -9.898; control1Y: 307.106; control2X: -10.081; control2Y: 317.014; x: -9.859; y: 316.751 }
- PathCubic { control1X: -9.24; control1Y: 316.018; control2X: -6.19; control2Y: 306.284; x: -6.121; y: 305.392 }
- PathCubic { control1X: -6.064; control1Y: 304.661; control2X: -5.332; control2Y: 304.196; x: -5.564; y: 303.391 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -31.202; y: 296.599 }
- PathCubic { control1X: -28.568; control1Y: 294.1; control2X: -25.778; control2Y: 291.139; x: -26.22; y: 287.427 }
- PathCubic { control1X: -26.336; control1Y: 286.451; control2X: -28.111; control2Y: 286.978; x: -28.298; y: 287.824 }
- PathCubic { control1X: -29.1; control1Y: 291.449; control2X: -31.139; control2Y: 294.11; x: -33.707; y: 296.502 }
- PathCubic { control1X: -35.903; control1Y: 298.549; control2X: -37.765; control2Y: 304.893; x: -38; y: 305.401 }
- PathCubic { control1X: -34.303; control1Y: 300.145; control2X: -32.046; control2Y: 297.399; x: -31.202; y: 296.599 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -44.776; y: 290.635 }
- PathCubic { control1X: -44.253; control1Y: 290.265; control2X: -44.555; control2Y: 289.774; x: -44.338; y: 289.442 }
- PathCubic { control1X: -43.385; control1Y: 287.984; control2X: -42.084; control2Y: 286.738; x: -42.066; y: 285 }
- PathCubic { control1X: -42.063; control1Y: 284.723; control2X: -42.441; control2Y: 284.414; x: -42.776; y: 284.638 }
- PathCubic { control1X: -43.053; control1Y: 284.822; control2X: -43.395; control2Y: 284.952; x: -43.503; y: 285.082 }
- PathCubic { control1X: -45.533; control1Y: 287.531; control2X: -46.933; control2Y: 290.202; x: -48.376; y: 293.014 }
- PathCubic { control1X: -48.559; control1Y: 293.371; control2X: -49.703; control2Y: 297.862; x: -49.39; y: 297.973 }
- PathCubic { control1X: -49.151; control1Y: 298.058; control2X: -47.431; control2Y: 293.877; x: -47.221; y: 293.763 }
- PathCubic { control1X: -45.958; control1Y: 293.077; control2X: -45.946; control2Y: 291.462; x: -44.776; y: 290.635 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -28.043; y: 310.179 }
- PathCubic { control1X: -27.599; control1Y: 309.31; control2X: -26.023; control2Y: 308.108; x: -26.136; y: 307.219 }
- PathCubic { control1X: -26.254; control1Y: 306.291; control2X: -25.786; control2Y: 304.848; x: -26.698; y: 305.536 }
- PathCubic { control1X: -27.955; control1Y: 306.484; control2X: -31.404; control2Y: 307.833; x: -31.674; y: 313.641 }
- PathCubic { control1X: -31.7; control1Y: 314.212; control2X: -28.726; control2Y: 311.519; x: -28.043; y: 310.179 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -13.6; y: 293.001 }
- PathCubic { control1X: -13.2; control1Y: 292.333; control2X: -12.492; control2Y: 292.806; x: -12.033; y: 292.543 }
- PathCubic { control1X: -11.385; control1Y: 292.171; control2X: -10.774; control2Y: 291.613; x: -10.482; y: 290.964 }
- PathCubic { control1X: -9.512; control1Y: 288.815; control2X: -7.743; control2Y: 286.995; x: -7.6; y: 284.601 }
- PathCubic { control1X: -9.091; control1Y: 283.196; control2X: -9.77; control2Y: 285.236; x: -10.4; y: 286.201 }
- PathCubic { control1X: -11.723; control1Y: 284.554; control2X: -12.722; control2Y: 286.428; x: -14.022; y: 286.947 }
- PathCubic { control1X: -14.092; control1Y: 286.975; control2X: -14.305; control2Y: 286.628; x: -14.38; y: 286.655 }
- PathCubic { control1X: -15.557; control1Y: 287.095; control2X: -16.237; control2Y: 288.176; x: -17.235; y: 288.957 }
- PathCubic { control1X: -17.406; control1Y: 289.091; control2X: -17.811; control2Y: 288.911; x: -17.958; y: 289.047 }
- PathCubic { control1X: -18.61; control1Y: 289.65; control2X: -19.583; control2Y: 289.975; x: -19.863; y: 290.657 }
- PathCubic { control1X: -20.973; control1Y: 293.364; control2X: -24.113; control2Y: 295.459; x: -26; y: 303.001 }
- PathCubic { control1X: -25.619; control1Y: 303.91; control2X: -21.488; control2Y: 296.359; x: -21.001; y: 295.661 }
- PathCubic { control1X: -20.165; control1Y: 294.465; control2X: -20.047; control2Y: 297.322; x: -18.771; y: 296.656 }
- PathCubic { control1X: -18.72; control1Y: 296.629; control2X: -18.534; control2Y: 296.867; x: -18.4; y: 297.001 }
- PathCubic { control1X: -18.206; control1Y: 296.721; control2X: -17.988; control2Y: 296.492; x: -17.6; y: 296.601 }
- PathCubic { control1X: -17.6; control1Y: 296.201; control2X: -17.734; control2Y: 295.645; x: -17.533; y: 295.486 }
- PathCubic { control1X: -16.296; control1Y: 294.509; control2X: -16.38; control2Y: 293.441; x: -15.6; y: 292.201 }
- PathCubic { control1X: -15.142; control1Y: 292.99; control2X: -14.081; control2Y: 292.271; x: -13.6; y: 293.001 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 46.2; y: 347.401 }
- PathCubic { control1X: 46.2; control1Y: 347.401; control2X: 53.6; control2Y: 327.001; x: 49.2; y: 315.801 }
- PathCubic { control1X: 49.2; control1Y: 315.801; control2X: 60.6; control2Y: 337.401; x: 56; y: 348.601 }
- PathCubic { control1X: 56; control1Y: 348.601; control2X: 55.6; control2Y: 338.201; x: 51.6; y: 333.201 }
- PathCubic { control1X: 51.6; control1Y: 333.201; control2X: 47.6; control2Y: 346.001; x: 46.2; y: 347.401 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 31.4; y: 344.801 }
- PathCubic { control1X: 31.4; control1Y: 344.801; control2X: 36.8; control2Y: 336.001; x: 28.8; y: 317.601 }
- PathCubic { control1X: 28.8; control1Y: 317.601; control2X: 28; control2Y: 338.001; x: 21.2; y: 349.001 }
- PathCubic { control1X: 21.2; control1Y: 349.001; control2X: 35.4; control2Y: 328.801; x: 31.4; y: 344.801 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 21.4; y: 342.801 }
- PathCubic { control1X: 21.4; control1Y: 342.801; control2X: 21.2; control2Y: 322.801; x: 21.6; y: 319.801 }
- PathCubic { control1X: 21.6; control1Y: 319.801; control2X: 17.8; control2Y: 336.401; x: 7.6; y: 346.001 }
- PathCubic { control1X: 7.6; control1Y: 346.001; control2X: 22; control2Y: 334.001; x: 21.4; y: 342.801 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 11.8; y: 310.801 }
- PathCubic { control1X: 11.8; control1Y: 310.801; control2X: 17.8; control2Y: 324.401; x: 7.8; y: 342.801 }
- PathCubic { control1X: 7.8; control1Y: 342.801; control2X: 14.2; control2Y: 330.601; x: 9.4; y: 323.601 }
- PathCubic { control1X: 9.4; control1Y: 323.601; control2X: 12; control2Y: 320.201; x: 11.8; y: 310.801 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -7.4; y: 342.401 }
- PathCubic { control1X: -7.4; control1Y: 342.401; control2X: -8.4; control2Y: 326.801; x: -6.6; y: 324.601 }
- PathCubic { control1X: -6.6; control1Y: 324.601; control2X: -6.4; control2Y: 318.201; x: -6.8; y: 317.201 }
- PathCubic { control1X: -6.8; control1Y: 317.201; control2X: -2.8; control2Y: 311.001; x: -2.6; y: 318.401 }
- PathCubic { control1X: -2.6; control1Y: 318.401; control2X: -1.2; control2Y: 326.201; x: 1.6; y: 330.801 }
- PathCubic { control1X: 1.6; control1Y: 330.801; control2X: 5.2; control2Y: 336.201; x: 5; y: 342.601 }
- PathCubic { control1X: 5; control1Y: 342.601; control2X: -5; control2Y: 312.401; x: -7.4; y: 342.401 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -11; y: 314.801 }
- PathCubic { control1X: -11; control1Y: 314.801; control2X: -17.6; control2Y: 325.601; x: -19.4; y: 344.601 }
- PathCubic { control1X: -19.4; control1Y: 344.601; control2X: -20.8; control2Y: 338.401; x: -17; y: 324.001 }
- PathCubic { control1X: -17; control1Y: 324.001; control2X: -12.8; control2Y: 308.601; x: -11; y: 314.801 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -32.8; y: 334.601 }
- PathCubic { control1X: -32.8; control1Y: 334.601; control2X: -27.8; control2Y: 329.201; x: -26.4; y: 324.201 }
- PathCubic { control1X: -26.4; control1Y: 324.201; control2X: -22.8; control2Y: 308.401; x: -29.2; y: 317.001 }
- PathCubic { control1X: -29.2; control1Y: 317.001; control2X: -29; control2Y: 325.001; x: -37.2; y: 332.401 }
- PathCubic { control1X: -37.2; control1Y: 332.401; control2X: -32.4; control2Y: 330.001; x: -32.8; y: 334.601 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -38.6; y: 329.601 }
- PathCubic { control1X: -38.6; control1Y: 329.601; control2X: -35.2; control2Y: 312.201; x: -34.4; y: 311.401 }
- PathCubic { control1X: -34.4; control1Y: 311.401; control2X: -32.6; control2Y: 308.001; x: -35.4; y: 311.201 }
- PathCubic { control1X: -35.4; control1Y: 311.201; control2X: -44.2; control2Y: 330.401; x: -48.2; y: 337.001 }
- PathCubic { control1X: -48.2; control1Y: 337.001; control2X: -40.2; control2Y: 327.801; x: -38.6; y: 329.601 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -44.4; y: 313.001 }
- PathCubic { control1X: -44.4; control1Y: 313.001; control2X: -32.8; control2Y: 290.601; x: -54.6; y: 316.401 }
- PathCubic { control1X: -54.6; control1Y: 316.401; control2X: -43.6; control2Y: 306.601; x: -44.4; y: 313.001 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: -59.8; y: 298.401 }
- PathCubic { control1X: -59.8; control1Y: 298.401; control2X: -55; control2Y: 279.601; x: -52.4; y: 279.801 }
- PathCubic { control1X: -52.4; control1Y: 279.801; control2X: -44.2; control2Y: 270.801; x: -50.8; y: 281.401 }
- PathCubic { control1X: -50.8; control1Y: 281.401; control2X: -56.8; control2Y: 291.001; x: -56.2; y: 300.801 }
- PathCubic { control1X: -56.2; control1Y: 300.801; control2X: -56.8; control2Y: 291.201; x: -59.8; y: 298.401 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 270.5; y: 287 }
- PathCubic { control1X: 270.5; control1Y: 287; control2X: 258.5; control2Y: 277; x: 256; y: 273.5 }
- PathCubic { control1X: 256; control1Y: 273.5; control2X: 269.5; control2Y: 292; x: 269.5; y: 299 }
- PathCubic { control1X: 269.5; control1Y: 299; control2X: 272; control2Y: 291.5; x: 270.5; y: 287 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 276; y: 265 }
- PathCubic { control1X: 276; control1Y: 265; control2X: 255; control2Y: 250; x: 251.5; y: 242.5 }
- PathCubic { control1X: 251.5; control1Y: 242.5; control2X: 278; control2Y: 272; x: 278; y: 276.5 }
- PathCubic { control1X: 278; control1Y: 276.5; control2X: 278.5; control2Y: 267.5; x: 276; y: 265 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 293; y: 111 }
- PathCubic { control1X: 293; control1Y: 111; control2X: 281; control2Y: 103; x: 279.5; y: 105 }
- PathCubic { control1X: 279.5; control1Y: 105; control2X: 290; control2Y: 111.5; x: 292.5; y: 120 }
- PathCubic { control1X: 292.5; control1Y: 120; control2X: 291; control2Y: 111; x: 293; y: 111 }
- }
-
- ShapePath {
- fillColor: "#cccccc"
- strokeWidth: -1
- PathMove { x: 301.5; y: 191.5 }
- PathLine { x: 284; y: 179.5 }
- PathCubic { control1X: 284; control1Y: 179.5; control2X: 303; control2Y: 196.5; x: 303.5; y: 200.5 }
- PathLine { x: 301.5; y: 191.5 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#000000"
- strokeWidth: 1
- PathMove { x: -89.25; y: 169 }
- PathLine { x: -67.25; y: 173.75 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#000000"
- strokeWidth: 1
- PathMove { x: -39; y: 331 }
- PathCubic { control1X: -39; control1Y: 331; control2X: -39.5; control2Y: 327.5; x: -48.5; y: 338 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#000000"
- strokeWidth: 1
- PathMove { x: -33.5; y: 336 }
- PathCubic { control1X: -33.5; control1Y: 336; control2X: -31.5; control2Y: 329.5; x: -38; y: 334 }
- }
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: "#000000"
- strokeWidth: 1
- PathMove { x: 20.5; y: 344.5 }
- PathCubic { control1X: 20.5; control1Y: 344.5; control2X: 22; control2Y: 333.5; x: 10.5; y: 346.5 }
- }
-
- Shape {
- x: -184; y: -144; width: parent.width; height: parent.height
- opacity: tapHandler.pressed ? 1 : 0
- containsMode: Shape.FillContains
-
- TapHandler { id: tapHandler }
-
- ShapePath {
- strokeColor: "red"
- strokeWidth: 4
- fillColor: "transparent"
- PathSvg { path: `m 325.03711,0.5
- c -26.61408,6.4494547 -49.95197,2.1018066 -76.21132,1.0771669
- -22.26577,7.6817151 -47.96405,9.3627181 -65.67832,25.8497861
- -15.74718,12.80008 -41.1564,19.605644 -45.74903,40.600391
- -12.46933,17.76181 -25.36105,35.720146 -29.20117,57.999996
- -18.709864,3.10961 -16.347355,30.83801 -22.385143,46.675
- -6.848711,11.2677 11.07278,24.69174 -8.514666,27.97383
- -10.266901,5.61543 -12.859313,28.96588 -13.732346,5.78143
- 0.940083,-11.53398 -13.486195,-38.30626 -16.81701,-34.20231
- 14.608079,7.8234 21.299281,50.52979 11.380052,48.14418
- -3.406456,-15.12428 -26.181106,-38.29457 -31.849471,-35.62945
- 16.851912,6.41472 35.569884,31.75215 28.172486,47.93115
- -7.906485,-15.42757 -37.758959,-35.53783 -44.275447,-31.28685
- 18.975831,1.7428 37.986009,20.68109 42.87115,37.14427 C
- 42.279655,225.774 9.879724,213.57795 4.7080253,219.04989
- 20.780803,212.57418 55.055919,239.88547 49.602579,241.25683
- 38.186641,230.40078 6.6930104,222.77983 2.5752529,228.41774 c
- 13.6045481,-8.33065 49.4437901,14.89041 43.5525671,14.2358
- -9.759981,-7.96123 -43.5842921,7.36937 -17.554974,-1.20248
- 9.464499,-3.73452 40.555672,12.80659 16.398749,5.14121
- -9.1987,-7.28225 -39.0013156,3.37352 -14.121965,-2.12828
- 13.244874,-0.0206 35.758428,14.62706 10.562447,6.42228
- -10.780465,-8.4873 -47.8282254,11.10651 -21.027329,-0.003
- 11.640859,-4.82877 52.615601,10.74471 24.234828,8.2659
- -10.695834,-7.03902 -42.9384162,8.93905 -34.227854,5.58373
- 9.077539,-8.56443 49.068801,-5.28097 43.06838,0.45546
- -10.900893,-0.7118 -27.449619,17.27258 -10.00187,3.46526
- 15.705191,-9.18198 18.344231,9.31645 1.10807,8.73907
- -9.908444,1.77856 -21.108189,20.66671 -7.974821,4.92019
- 15.750746,-14.10374 34.01348,2.07267 9.796961,8.69337
- -8.17128,5.49929 -12.642664,19.13654 -3.994573,4.19708
- 9.044753,-8.7077 23.850399,-13.64552 21.404959,4.02329
- 12.509737,17.12562 51.158782,11.0442 45.106112,43.34009
- -0.65006,10.05318 -3.79228,13.95389 1.62128,14.30064
- -4.30913,8.82737 -14.652714,37.9591 2.92144,17.46024
- 7.37972,-3.68333 -7.62399,16.24161 -7.98007,23.83761
- -9.336865,18.77418 19.74873,-18.55943 6.62229,5.46195
- 5.46464,-3.7389 36.23886,-19.41901 14.78167,0.58987
- -8.59505,4.55644 29.29441,-2.99423 8.95489,6.47134 -9.22562,5.54437
- -24.09765,26.79976 -11.73274,22.20385 -0.81685,5.4936
- -1.58629,21.47626 2.34158,9.14886 1.61237,14.67029
- -2.38384,25.22225 12.26908,15.1741 -4.40761,8.01039
- -8.23679,36.91214 5.12235,17.92578 1.53454,2.99551 9.37569,3.1726
- 7.15304,14.93579 3.51234,-11.31873 18.4607,-29.83809
- 12.36869,-6.48005 -0.22629,16.26174 5.44303,-7.24791
- 6.56926,10.49819 12.45412,28.9931 3.40908,-41.89883
- 17.52051,-9.19238 3.23093,11.1924 6.53006,29.46941 7.55984,5.1249
- 15.37236,-19.52583 4.09776,20.07416 12.64063,1.48215
- 18.11247,-24.55068 -8.92586,38.39355 6.73828,6.62225
- 4.55353,-6.91007 15.35028,-38.88977 12.55806,-13.78666
- 1.05309,27.02664 11.54743,-24.40259 12.40657,6.86306
- -1.72561,13.28253 11.85393,-24.15909 13.85568,-1.38002
- 3.12455,8.33539 8.76536,26.46432 8.73882,5.09231 3.57025,-10.37352
- -16.025,-37.75672 0.20707,-22.5788 -1.2458,-14.17213
- -2.38918,-16.90145 10.85489,-6.71468 -16.57629,-17.22152
- 0.19706,-26.08949 5.7751,-19.14889 -14.91681,-16.1674
- 19.74174,7.19334 2.31875,-9.86869 -4.32508,-15.23278
- 27.25228,29.12341 20.27514,18.81172 -11.97527,-18.92603
- -17.96305,-45.80333 11.70099,-51.52566 17.19069,-9.57351
- 31.17452,21.93154 38.50541,1.56304 16.26048,-4.6633
- 22.3749,38.26516 24.86349,9.11316 5.94153,-9.9731 30.14313,6.97379
- 36.34294,4.75012 7.07435,18.27732 8.06778,14.78971 11.04264,3.86016
- 2.73754,-15.85945 28.7269,10.06391 28.09146,25.96561 3.00672,2.4754
- 6.55025,-22.10264 11.23552,-14.43872 2.84155,-11.4823
- -3.28976,-27.88574 4.24895,-25.5189 -0.61494,-11.53957
- 22.83611,0.11011 10.64648,-15.28756 -6.5587,-21.38598
- 9.32959,-3.0159 13.5107,-4.69375 -1.38592,-16.74533
- -8.66673,-31.83316 -1.90087,-41.0875 2.39623,-15.14303
- -12.50533,-44.45478 -4.70573,-48.49375 15.08472,3.42779
- -20.39159,-42.17451 -1.69776,-40.85728 24.07272,21.63552
- -3.65989,-30.10299 2.27233,-33.17152 16.90643,17.53071
- -12.7383,-38.42821 6.79531,-21.57013 -4.50946,-21.08135
- -2.53357,-37.43561 -15.5535,-55.59527 -11.0035,-12.40086
- -1.87775,-7.12745 1.34831,-8.11755 C 468.27562,118.9774
- 451.40746,102.656 430.98897,92.119168 439.06192,78.203836
- 455.88012,60.123881 457.38638,40.337815 463.2373,23.183067
- 450.82861,4.7342783 435.04883,22.626367 409.5188,28.206712
- 386.3569,24.131269 365.63904,8.0954152 352.788,2.8857182
- 338.88892,0.40735091 325.03711,0.5 Z m -219.0625,357.04297
- -0.97656,0.88476 z`
- }
- }
- }
-}
-
diff --git a/examples/quick/shapes/tigerLoader.qml b/examples/quick/shapes/tigerLoader.qml
deleted file mode 100644
index 302d8cac27..0000000000
--- a/examples/quick/shapes/tigerLoader.qml
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Shapes
-
-Rectangle {
- color: "lightGray"
- width: 256
- height: 256
-
- Item {
- anchors.fill: parent
-
- Text {
- anchors.centerIn: parent
- text: "Loading"
- // Phase #1: Loader loads tiger.qml. After this we have our item.
- // Phase #2: With some backends (generic) the item will start async processing. Wait for this too.
- visible: shapeLoader.status != Loader.Ready || shapeLoader.item.status === Shape.Processing
- }
-
- Loader {
- id: shapeLoader
- anchors.fill: parent
- source: "tiger.qml"
- asynchronous: true
- visible: status == Loader.Ready
- scale: 0.4
- }
- }
-}
diff --git a/examples/quick/shared/CMakeLists.txt b/examples/quick/shared/CMakeLists.txt
index 159f1e8699..7382df7689 100644
--- a/examples/quick/shared/CMakeLists.txt
+++ b/examples/quick/shared/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
set_source_files_properties(CheckBox.qml TabSet.qml TextField.qml
PROPERTIES
@@ -16,13 +16,11 @@ qt_add_qml_module(${PROJECT_NAME}_shared
URI shared
VERSION 2.2
PLUGIN_TARGET ${PROJECT_NAME}_shared
- AUTO_RESOURCE_PREFIX
SOURCES
"shared.h"
QML_FILES
"Button.qml"
"CheckBox.qml"
- "FlickrRssModel.qml"
"Label.qml"
"LauncherList.qml"
"SimpleLauncherDelegate.qml"
@@ -38,23 +36,3 @@ qt_add_qml_module(${PROJECT_NAME}_shared
"images/slider_handle.png"
"images/tab.png"
)
-
-include(QtBundleQmlModuleForMacOS.cmake)
-# Puts the shared qml module plugin and qmldir into the macOS app bundle directory.
-# Only call this function if your main project has the MACOSX_BUNDLE option set.
-function(bundle_shared app_target)
- set(qml_plugin_target "${PROJECT_NAME}_shared")
- set(qml_module_uri "shared")
- add_qml_module_to_macos_app_bundle(
- "${app_target}" "${qml_plugin_target}" "${qml_module_uri}")
-endfunction()
-
-set(INSTALL_SHAREDDIR "${INSTALL_EXAMPLESDIR}/quick/${PROJECT_NAME}/shared")
-install(TARGETS ${PROJECT_NAME}_shared
- RUNTIME DESTINATION "${INSTALL_SHAREDDIR}"
- LIBRARY DESTINATION "${INSTALL_SHAREDDIR}"
-)
-
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
- DESTINATION "${INSTALL_SHAREDDIR}"
-)
diff --git a/examples/quick/shared/FlickrRssModel.qml b/examples/quick/shared/FlickrRssModel.qml
deleted file mode 100644
index e78d1ffa21..0000000000
--- a/examples/quick/shared/FlickrRssModel.qml
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-ListModel {
- id: flickrImages
- property string tags : ""
- readonly property string queryUrl : "http://api.flickr.com/services/feeds/photos_public.gne?"
-
- function encodeParams(x) {
- return encodeURIComponent(x.replace(" ",","));
- }
- function fetchImages(format) {
- var requestURL = queryUrl + (tags ? "tags="+encodeParams(tags)+"&" : "") + "format=" + format + "&nojsoncallback=1";
- var xhr = new XMLHttpRequest;
- xhr.onreadystatechange = function() {
- if (xhr.readyState === XMLHttpRequest.DONE) {
-
- if (xhr.status !== 200) {
- console.log("Failed to get images from flickr. status code: " + xhr.status);
- return;
- }
-
- var jsonText = xhr.responseText;
- var objArray = JSON.parse(jsonText.replace(/\'/g,"'"))
- if (objArray.errors !== undefined)
- console.log("Error fetching tweets: " + objArray.errors[0].message)
- else {
- for (var key in objArray.items) {
- var rssItem = objArray.items[key];
- var jsonObject = "{ \"title\": \"" + rssItem.title +"\",\"media\": \"" + rssItem.media.m + "\", \"thumbnail\": \"" + rssItem.media.m.replace(/\_m\.jpg/,"_s.jpg") +"\"}"
- flickrImages.append(JSON.parse(jsonObject));
- }
- }
- }
- }
- xhr.open("GET", requestURL, true);
- xhr.send();
- }
- Component.onCompleted: {
- fetchImages("json");
- }
-}
-
diff --git a/examples/quick/shared/LauncherList.qml b/examples/quick/shared/LauncherList.qml
index ee8fc3984e..f86ab80446 100644
--- a/examples/quick/shared/LauncherList.qml
+++ b/examples/quick/shared/LauncherList.qml
@@ -174,22 +174,26 @@ Rectangle {
id: back
source: "images/back.png"
anchors.verticalCenter: parent.verticalCenter
- anchors.verticalCenterOffset: 2
+ anchors.verticalCenterOffset: 1
anchors.left: parent.left
- anchors.leftMargin: 16
+ anchors.leftMargin: 6
+ width: 38
+ height: 31
+ fillMode: Image.Pad
+ horizontalAlignment: Image.AlignHCenter
+ verticalAlignment: Image.AlignVCenter
TapHandler {
id: tapHandler
enabled: root.activePageCount > 0
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ longPressThreshold: 0
onTapped: {
pageContainer.children[pageContainer.children.length - 1].exit()
}
}
Rectangle {
- anchors.centerIn: back
- width: 38
- height: 31
- anchors.verticalCenterOffset: -1
+ anchors.fill: parent
opacity: tapHandler.pressed ? 1 : 0
Behavior on opacity { NumberAnimation{ duration: 100 }}
gradient: Gradient {
diff --git a/examples/quick/shared/QtBundleQmlModuleForMacOS.cmake b/examples/quick/shared/QtBundleQmlModuleForMacOS.cmake
deleted file mode 100644
index 06d1487323..0000000000
--- a/examples/quick/shared/QtBundleQmlModuleForMacOS.cmake
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-function(add_qml_module_to_macos_app_bundle app_target qml_plugin_target qml_module_uri)
- if(QT6_IS_SHARED_LIBS_BUILD AND APPLE)
- # The application's main.cpp adds an explicit QML import path to look for qml module plugins
- # under a PlugIns subdirectory of a macOS app bundle.
- # Copy the qmldir and shared library qml plugin.
-
- # Ensure the executable depends on the plugin so the plugin is copied
- # only after it was built.
- add_dependencies(${app_target} ${qml_plugin_target})
-
- set(app_dir "$<TARGET_FILE_DIR:${app_target}>")
-
- string(REGEX REPLACE "[^A-Za-z0-9]" "_" escaped_uri "${qml_module_uri}")
-
- set(dest_module_dir_in_app_bundle "${app_dir}/../PlugIns/${escaped_uri}")
-
- set(qml_plugin_dir "$<TARGET_FILE_DIR:${qml_plugin_target}>")
- set(qmldir_file "${qml_plugin_dir}/qmldir")
-
- add_custom_command(TARGET ${app_target} POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E make_directory ${dest_module_dir_in_app_bundle}
- COMMAND ${CMAKE_COMMAND} -E copy_if_different
- $<TARGET_FILE:${qml_plugin_target}> ${dest_module_dir_in_app_bundle}
- COMMAND ${CMAKE_COMMAND} -E copy_if_different
- ${qmldir_file} ${dest_module_dir_in_app_bundle}
- )
- endif()
-endfunction()
-
diff --git a/examples/quick/shared/SimpleLauncherDelegate.qml b/examples/quick/shared/SimpleLauncherDelegate.qml
index c6ca9802d2..a6ac090b34 100644
--- a/examples/quick/shared/SimpleLauncherDelegate.qml
+++ b/examples/quick/shared/SimpleLauncherDelegate.qml
@@ -9,7 +9,7 @@ Rectangle {
property Item exampleItem
width: ListView.view.width
- height: button.implicitHeight + 22
+ height: col.implicitHeight + 22
signal clicked()
@@ -22,10 +22,18 @@ Rectangle {
GradientStop {
position: 1
Behavior on color {ColorAnimation { duration: 100 }}
- color: tapHandler.pressed ? "#e0e0e0" : button.containsMouse ? "#f5f5f5" : "#eee"
+ color: tapHandler.pressed ? "#e0e0e0" : hoverHandler.hovered ? "#f5f5f5" : "#eee"
}
}
+ TapHandler {
+ id: tapHandler
+ onTapped: container.clicked()
+ }
+ HoverHandler {
+ id: hoverHandler
+ }
+
Image {
id: image
opacity: 0.7
@@ -36,53 +44,28 @@ Rectangle {
anchors.rightMargin: 16
}
- Item {
- id: button
- anchors.top: parent.top
+ Column {
+ id: col
+ spacing: 2
anchors.left: parent.left
- anchors.bottom: parent.bottom
anchors.right:image.left
- implicitHeight: col.height
- height: implicitHeight
- width: buttonLabel.width + 20
- property alias containsMouse: hoverHandler.hovered
-
- TapHandler {
- id: tapHandler
- onTapped: container.clicked()
- }
- HoverHandler {
- id: hoverHandler
+ anchors.margins: 10
+ anchors.verticalCenter: parent.verticalCenter
+ Text {
+ width: parent.width
+ text: container.name
+ color: "black"
+ font.pixelSize: 22
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ styleColor: "white"
+ style: Text.Raised
}
-
- Column {
- spacing: 2
- id: col
- anchors.verticalCenter: parent.verticalCenter
+ Text {
width: parent.width
- Text {
- id: buttonLabel
- anchors.left: parent.left
- anchors.leftMargin: 10
- anchors.right: parent.right
- anchors.rightMargin: 10
- text: container.name
- color: "black"
- font.pixelSize: 22
- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- styleColor: "white"
- style: Text.Raised
-
- }
- Text {
- id: buttonLabel2
- anchors.left: parent.left
- anchors.leftMargin: 10
- text: container.description
- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- color: "#666"
- font.pixelSize: 12
- }
+ text: container.description
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ color: "#666"
+ font.pixelSize: 12
}
}
diff --git a/examples/quick/shared/qmldir b/examples/quick/shared/qmldir
index 5ccdf60219..8a3d856a08 100644
--- a/examples/quick/shared/qmldir
+++ b/examples/quick/shared/qmldir
@@ -1,7 +1,6 @@
module shared
Button 2.0 Button.qml
CheckBox 2.1 CheckBox.qml
-FlickrRssModel 2.0 FlickrRssModel.qml
Label 2.0 Label.qml
LauncherList 2.0 LauncherList.qml
SimpleLauncherDelegate 2.0 SimpleLauncherDelegate.qml
diff --git a/examples/quick/shared/shared.qrc b/examples/quick/shared/shared.qrc
index 67f69c9c8d..054db1f352 100644
--- a/examples/quick/shared/shared.qrc
+++ b/examples/quick/shared/shared.qrc
@@ -2,7 +2,6 @@
<qresource prefix="/qt/qml/shared">
<file>Button.qml</file>
<file>CheckBox.qml</file>
- <file>FlickrRssModel.qml</file>
<file>Images.qml</file>
<file>Label.qml</file>
<file>LauncherList.qml</file>
diff --git a/examples/quick/tableview/gameoflife/CMakeLists.txt b/examples/quick/tableview/gameoflife/CMakeLists.txt
index 8195fba9f6..0af748fc80 100644
--- a/examples/quick/tableview/gameoflife/CMakeLists.txt
+++ b/examples/quick/tableview/gameoflife/CMakeLists.txt
@@ -1,34 +1,27 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(gameoflife LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tableview/gameoflife")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(gameoflife
+ WIN32
+ MACOSX_BUNDLE
gameoflifemodel.cpp gameoflifemodel.h
main.cpp
)
-set_target_properties(gameoflife PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(gameoflife PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+target_link_libraries(gameoflife PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(gameoflife
@@ -41,7 +34,16 @@ qt_add_qml_module(gameoflife
)
install(TARGETS gameoflife
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET gameoflife
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc b/examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc
index c7fffacecf..b8d594d598 100644
--- a/examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc
+++ b/examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc
@@ -5,6 +5,7 @@
\example tableview/gameoflife
\brief The \e{Conway’s Game of Life} example shows how the QML TableView
type can be used to display a C++ model that the user can pan around.
+ \examplecategory {Graphics}
\image gameoflife.png
\ingroup qtquickexamples
diff --git a/examples/quick/tableview/gameoflife/main.qml b/examples/quick/tableview/gameoflife/main.qml
index bedb624223..55c5b193f8 100644
--- a/examples/quick/tableview/gameoflife/main.qml
+++ b/examples/quick/tableview/gameoflife/main.qml
@@ -74,6 +74,7 @@ ApplicationWindow {
text: qsTr("Next")
onClicked: gameOfLifeModel.nextStep()
Layout.rightMargin: 50
+ Layout.fillWidth: false
}
//! [next]
@@ -82,10 +83,12 @@ ApplicationWindow {
from: 0
to: 1
value: 0.9
+ Layout.fillWidth: false
}
Button {
text: timer.running ? "â™â™" : "â–¶ï¸"
+ Layout.fillWidth: false
onClicked: timer.running = !timer.running
}
}
diff --git a/examples/quick/tableview/pixelator/CMakeLists.txt b/examples/quick/tableview/pixelator/CMakeLists.txt
index 6de15b8615..de24974bcd 100644
--- a/examples/quick/tableview/pixelator/CMakeLists.txt
+++ b/examples/quick/tableview/pixelator/CMakeLists.txt
@@ -1,34 +1,27 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(qml_pixelator LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tableview/pixelator")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(qml_pixelator
+ WIN32
+ MACOSX_BUNDLE
imagemodel.cpp imagemodel.h
main.cpp
)
-set_target_properties(qml_pixelator PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
-target_link_libraries(qml_pixelator PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+target_link_libraries(qml_pixelator PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(qml_pixelator
@@ -39,7 +32,16 @@ qt_add_qml_module(qml_pixelator
)
install(TARGETS qml_pixelator
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET qml_pixelator
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tableview/pixelator/doc/src/pixelator.qdoc b/examples/quick/tableview/pixelator/doc/src/pixelator.qdoc
index 552b70e198..35d3ac6732 100644
--- a/examples/quick/tableview/pixelator/doc/src/pixelator.qdoc
+++ b/examples/quick/tableview/pixelator/doc/src/pixelator.qdoc
@@ -5,6 +5,7 @@
\example tableview/pixelator
\brief The Pixelator example shows how a QML TableView and a delegate
can be used for custom table models.
+ \examplecategory {Graphics}
\image qt-pixelator.png
\ingroup qtquickexamples
diff --git a/examples/quick/text/CMakeLists.txt b/examples/quick/text/CMakeLists.txt
index 31076c5339..5560256793 100644
--- a/examples/quick/text/CMakeLists.txt
+++ b/examples/quick/text/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(text LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/text")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../shared" "shared")
@@ -23,17 +17,16 @@ qt_add_executable(textexample
)
target_link_libraries(textexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(textexample text_shared)
qt_add_qml_module(textexample
URI text
- AUTO_RESOURCE_PREFIX
QML_FILES
"fonts/availableFonts.qml"
"fonts/banner.qml"
@@ -43,7 +36,6 @@ qt_add_qml_module(textexample
"imgtag/imgtag.qml"
"styledtext-layout.qml"
"text.qml"
- "textselection/textselection.qml"
RESOURCES
"fonts/content/fonts/tarzeau_ocr_a.ttf"
"imgtag/images/face-sad.png"
@@ -52,16 +44,19 @@ qt_add_qml_module(textexample
"imgtag/images/heart200.png"
"imgtag/images/qtlogo.png"
"imgtag/images/starfish_2.png"
- "textselection/pics/endHandle.png"
- "textselection/pics/endHandle.sci"
- "textselection/pics/startHandle.png"
- "textselection/pics/startHandle.sci"
)
install(TARGETS textexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(textexample)
+qt_generate_deploy_qml_app_script(
+ TARGET textexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/text/doc/src/text.qdoc b/examples/quick/text/doc/src/text.qdoc
index 725f8c04bb..9aa5fb2f06 100644
--- a/examples/quick/text/doc/src/text.qdoc
+++ b/examples/quick/text/doc/src/text.qdoc
@@ -6,6 +6,7 @@
\brief This is a collection of QML examples relating to text.
\image qml-text-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e Text is a collection of small QML examples relating to text. Each example
is a small QML file, usually containing or emphasizing a particular type or
diff --git a/examples/quick/text/fonts/availableFonts.qml b/examples/quick/text/fonts/availableFonts.qml
index 2564bb6447..7f4f938b48 100644
--- a/examples/quick/text/fonts/availableFonts.qml
+++ b/examples/quick/text/fonts/availableFonts.qml
@@ -4,7 +4,9 @@
import QtQuick
Rectangle {
- width: 320; height: 480; color: "steelblue"
+ width: 320
+ height: 480
+ color: "steelblue"
ListView {
anchors.fill: parent
@@ -13,7 +15,8 @@ Rectangle {
//! [model]
delegate: Item {
- height: 40; width: ListView.view.width
+ height: 40
+ width: ListView.view.width
required property string modelData
Text {
anchors.centerIn: parent
diff --git a/examples/quick/text/fonts/banner.qml b/examples/quick/text/fonts/banner.qml
index 2c0742018a..aae93baea2 100644
--- a/examples/quick/text/fonts/banner.qml
+++ b/examples/quick/text/fonts/banner.qml
@@ -8,17 +8,36 @@ Rectangle {
property int pixelSize: screen.height * 1.25
property color textColor: "lightsteelblue"
- property string text: "Hello world! "
+ readonly property string text: qsTr("Hello world! ")
- width: 320; height: 480
+ width: 320
+ height: 480
color: "steelblue"
Row {
y: -screen.height / 4.5
- NumberAnimation on x { from: 0; to: -text.width; duration: 6000; loops: Animation.Infinite }
- Text { id: text; font.pixelSize: screen.pixelSize; color: screen.textColor; text: screen.text }
- Text { font.pixelSize: screen.pixelSize; color: screen.textColor; text: screen.text }
- Text { font.pixelSize: screen.pixelSize; color: screen.textColor; text: screen.text }
+ NumberAnimation on x {
+ from: 0
+ to: -text.width
+ duration: 6000
+ loops: Animation.Infinite
+ }
+ Text {
+ id: text
+ font.pixelSize: screen.pixelSize
+ color: screen.textColor
+ text: screen.text
+ }
+ Text {
+ font.pixelSize: screen.pixelSize
+ color: screen.textColor
+ text: screen.text
+ }
+ Text {
+ font.pixelSize: screen.pixelSize
+ color: screen.textColor
+ text: screen.text
+ }
}
}
diff --git a/examples/quick/text/fonts/fonts.qml b/examples/quick/text/fonts/fonts.qml
index 8e0f906493..84cb6baa20 100644
--- a/examples/quick/text/fonts/fonts.qml
+++ b/examples/quick/text/fonts/fonts.qml
@@ -5,20 +5,32 @@ import QtQuick
Rectangle {
id: root
- property string myText: "The quick brown fox jumps over the lazy dog."
+ readonly property string myText: qsTr("The quick brown fox jumps over the lazy dog.")
- width: 320; height: 480
+ width: 320
+ height: 480
color: "steelblue"
//! [fontloaderlocal]
- FontLoader { id: localFont; source: "content/fonts/tarzeau_ocr_a.ttf" }
+ FontLoader {
+ id: localFont
+ source: "content/fonts/tarzeau_ocr_a.ttf"
+ }
//! [fontloaderlocal]
//! [fontloaderremote]
- FontLoader { id: webFont; source: "http://www.princexml.com/fonts/steffmann/Starburst.ttf" }
+ FontLoader {
+ id: webFont
+ source: "http://www.princexml.com/fonts/steffmann/Starburst.ttf"
+ }
//! [fontloaderremote]
Column {
- anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 10 }
+ anchors {
+ fill: parent
+ leftMargin: 10
+ rightMargin: 10
+ topMargin: 10
+ }
spacing: 15
Text {
@@ -37,7 +49,11 @@ Rectangle {
width: parent.width
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
- font { family: "Times"; pixelSize: 20; capitalization: Font.AllUppercase }
+ font {
+ family: "Times"
+ pixelSize: 20
+ capitalization: Font.AllUppercase
+ }
}
Text {
text: root.myText
@@ -45,21 +61,35 @@ Rectangle {
width: parent.width
horizontalAlignment: Text.AlignRight
wrapMode: Text.WordWrap
- font { family: "Courier"; pixelSize: 20; weight: Font.Bold; capitalization: Font.AllLowercase }
+ font {
+ family: "Courier"
+ pixelSize: 20
+ weight: Font.Bold
+ capitalization: Font.AllLowercase
+ }
}
Text {
text: root.myText
color: "lightsteelblue"
width: parent.width
wrapMode: Text.WordWrap
- font { family: "Courier"; pixelSize: 20; italic: true; capitalization: Font.SmallCaps }
+ font {
+ family: "Courier"
+ pixelSize: 20
+ italic: true
+ capitalization: Font.SmallCaps
+ }
}
Text {
text: root.myText
color: "lightsteelblue"
width: parent.width
wrapMode: Text.WordWrap
- font { family: localFont.name; pixelSize: 20; capitalization: Font.Capitalize }
+ font {
+ family: localFont.name
+ pixelSize: 20
+ capitalization: Font.Capitalize
+ }
}
Text {
text: {
@@ -70,7 +100,8 @@ Rectangle {
color: "lightsteelblue"
width: parent.width
wrapMode: Text.WordWrap
- font.family: webFont.name; font.pixelSize: 20
+ font.family: webFont.name
+ font.pixelSize: 20
}
}
}
diff --git a/examples/quick/text/fonts/hello.qml b/examples/quick/text/fonts/hello.qml
index e0cdc26ea6..89760b0b4b 100644
--- a/examples/quick/text/fonts/hello.qml
+++ b/examples/quick/text/fonts/hello.qml
@@ -6,24 +6,31 @@ import QtQuick
Rectangle {
id: screen
- width: 320; height: 480
+ width: 320
+ height: 480
color: "black"
Item {
id: container
- x: screen.width / 2; y: screen.height / 2
+ x: screen.width / 2
+ y: screen.height / 2
Text {
id: text
anchors.centerIn: parent
color: "white"
- text: "Hello world!"
+ text: qsTr("Hello world!")
font.pixelSize: 32
//! [letterspacing]
SequentialAnimation on font.letterSpacing {
- loops: Animation.Infinite;
- NumberAnimation { from: 0; to: 50; easing.type: Easing.InQuad; duration: 3000 }
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 0
+ to: 50
+ easing.type: Easing.InQuad
+ duration: 3000
+ }
ScriptAction {
script: {
container.y = (screen.height / 4) + (Math.random() * screen.height / 2)
@@ -34,9 +41,15 @@ Rectangle {
//! [letterspacing]
SequentialAnimation on opacity {
- loops: Animation.Infinite;
- NumberAnimation { from: 1; to: 0; duration: 2600 }
- PauseAnimation { duration: 400 }
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1
+ to: 0
+ duration: 2600
+ }
+ PauseAnimation {
+ duration: 400
+ }
}
}
}
diff --git a/examples/quick/text/styledtext-layout.qml b/examples/quick/text/styledtext-layout.qml
index 4f63d3ea90..1f99729488 100644
--- a/examples/quick/text/styledtext-layout.qml
+++ b/examples/quick/text/styledtext-layout.qml
@@ -5,11 +5,12 @@ import QtQuick
Rectangle {
id: main
- width: 320; height: 480
- focus: true
- property real offset: 0
- property real margin: 8
+ readonly property real margin: 8
+
+ width: 320
+ height: 480
+ focus: true
Text {
id: myText
@@ -47,11 +48,10 @@ Rectangle {
Text {
id: theEndText
- text: "THE\nEND"
+ text: qsTr("THE\nEND")
anchors.centerIn: parent
font.pixelSize: myText.font.pixelSize / 2
}
}
}
-
}
diff --git a/examples/quick/text/text.qml b/examples/quick/text/text.qml
index 8f1c24ebc2..f16c0bf13a 100644
--- a/examples/quick/text/text.qml
+++ b/examples/quick/text/text.qml
@@ -8,15 +8,14 @@ Item {
height: 480
width: 320
LauncherList {
- id: ll
anchors.fill: parent
Component.onCompleted: {
- addExample("Hello", "An Animated Hello World", Qt.resolvedUrl("fonts/hello.qml"));
- addExample("Fonts", "Using various fonts with a Text element", Qt.resolvedUrl("fonts/fonts.qml"));
- addExample("Available Fonts", "A list of your available fonts", Qt.resolvedUrl("fonts/availableFonts.qml"));
- addExample("Banner", "Large, scrolling text", Qt.resolvedUrl("fonts/banner.qml"));
- addExample("Img tag", "Embedding images into text", Qt.resolvedUrl("imgtag/imgtag.qml"));
- addExample("Text Layout", "Flowing text around items", Qt.resolvedUrl("styledtext-layout.qml"));
+ addExample(qsTr("Hello"), qsTr("An Animated Hello World"), Qt.resolvedUrl("fonts/hello.qml"));
+ addExample(qsTr("Fonts"), qsTr("Using various fonts with a Text element"), Qt.resolvedUrl("fonts/fonts.qml"));
+ addExample(qsTr("Available Fonts"), qsTr("A list of your available fonts"), Qt.resolvedUrl("fonts/availableFonts.qml"));
+ addExample(qsTr("Banner"), qsTr("Large, scrolling text"), Qt.resolvedUrl("fonts/banner.qml"));
+ addExample(qsTr("Img tag"), qsTr("Embedding images into text"), Qt.resolvedUrl("imgtag/imgtag.qml"));
+ addExample(qsTr("Text Layout"), qsTr("Flowing text around items"), Qt.resolvedUrl("styledtext-layout.qml"));
}
}
}
diff --git a/examples/quick/text/text.qrc b/examples/quick/text/text.qrc
index a59d66fdbe..8d727c2831 100644
--- a/examples/quick/text/text.qrc
+++ b/examples/quick/text/text.qrc
@@ -13,11 +13,6 @@
<file>imgtag/images/heart200.png</file>
<file>imgtag/images/qtlogo.png</file>
<file>imgtag/images/starfish_2.png</file>
- <file>textselection/textselection.qml</file>
- <file>textselection/pics/endHandle.png</file>
- <file>textselection/pics/endHandle.sci</file>
- <file>textselection/pics/startHandle.png</file>
- <file>textselection/pics/startHandle.sci</file>
<file>styledtext-layout.qml</file>
<file>imgtag/TextWithImage.qml</file>
</qresource>
diff --git a/examples/quick/text/textselection/textselection.qml b/examples/quick/text/textselection/textselection.qml
deleted file mode 100644
index d4224ee14c..0000000000
--- a/examples/quick/text/textselection/textselection.qml
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick
-
-Rectangle {
- id: editor
- color: "lightGrey"
- width: 640; height: 480
-
- Rectangle {
- color: "white"
- anchors.fill: parent
- anchors.margins: 20
-
- BorderImage {
- id: startHandle
- source: "pics/startHandle.sci"
- opacity: 0.0
- width: 10
- x: edit.positionToRectangle(edit.selectionStart).x - flick.contentX-width
- y: edit.positionToRectangle(edit.selectionStart).y - flick.contentY
- height: edit.positionToRectangle(edit.selectionStart).height
- }
-
- BorderImage {
- id: endHandle
- source: "pics/endHandle.sci"
- opacity: 0.0
- width: 10
- x: edit.positionToRectangle(edit.selectionEnd).x - flick.contentX
- y: edit.positionToRectangle(edit.selectionEnd).y - flick.contentY
- height: edit.positionToRectangle(edit.selectionEnd).height
- }
-
- Flickable {
- id: flick
-
- anchors.fill: parent
- contentWidth: edit.contentWidth
- contentHeight: edit.contentHeight
- interactive: true
- clip: true
-
- function ensureVisible(r) {
- if (contentX >= r.x)
- contentX = r.x;
- else if (contentX+width <= r.x+r.width)
- contentX = r.x+r.width-width;
- if (contentY >= r.y)
- contentY = r.y;
- else if (contentY+height <= r.y+r.height)
- contentY = r.y+r.height-height;
- }
-
- TextEdit {
- id: edit
- width: flick.width
- height: flick.height
- focus: true
- wrapMode: TextEdit.Wrap
- textFormat: TextEdit.RichText
-
- onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)
-
- text: "<h1>Text Selection</h1>"
- +"<p>This example is a whacky text selection mechanisms, showing how these can be implemented in the TextEdit element, to cater for whatever style is appropriate for the target platform."
- +"<p><b>Press-and-hold</b> to select a word, then drag the selection handles."
- +"<p><b>Drag outside the selection</b> to scroll the text."
- +"<p><b>Click inside the selection</b> to cut/copy/paste/cancel selection."
- +"<p>It's too whacky to let you paste if there is no current selection."
-
- }
- }
-
- Item {
- id: menu
- opacity: 0.0
- width: 100
- height: 120
- anchors.centerIn: parent
-
- Rectangle {
- border.width: 1
- border.color: "darkBlue"
- radius: 15
- color: "#806080FF"
- anchors.fill: parent
- }
-
- Column {
- anchors.centerIn: parent
- spacing: 8
-
- Rectangle {
- border.width: 1
- border.color: "darkBlue"
- color: "#ff7090FF"
- width: 60
- height: 16
-
- Text { anchors.centerIn: parent; text: "Cut" }
-
- MouseArea {
- anchors.fill: parent
- onClicked: { edit.cut(); editor.state = "" }
- }
- }
-
- Rectangle {
- border.width: 1
- border.color: "darkBlue"
- color: "#ff7090FF"
- width: 60
- height: 16
-
- Text { anchors.centerIn: parent; text: "Copy" }
-
- MouseArea {
- anchors.fill: parent
- onClicked: { edit.copy(); editor.state = "selection" }
- }
- }
-
- Rectangle {
- border.width: 1
- border.color: "darkBlue"
- color: "#ff7090FF"
- width: 60
- height: 16
-
- Text { anchors.centerIn: parent; text: "Paste" }
-
- MouseArea {
- anchors.fill: parent
- onClicked: { edit.paste(); edit.cursorPosition = edit.selectionEnd; editor.state = "" }
- }
- }
-
- Rectangle {
- border.width: 1
- border.color: "darkBlue"
- color: "#ff7090FF"
- width: 60
- height: 16
-
- Text { anchors.centerIn: parent; text: "Deselect" }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- edit.cursorPosition = edit.selectionEnd;
- edit.deselect();
- editor.state = ""
- }
- }
- }
- }
- }
- }
-
- states: [
- State {
- name: "selection"
- PropertyChanges {
- startHandle.opacity: 1.0
- endHandle.opacity: 1.0
- }
- },
- State {
- name: "menu"
- PropertyChanges {
- startHandle.opacity: 0.5
- endHandle.opacity: 0.5
- menu.opacity: 1.0
- }
- }
- ]
-}
diff --git a/examples/quick/threading/CMakeLists.txt b/examples/quick/threading/CMakeLists.txt
deleted file mode 100644
index 0eb73afc76..0000000000
--- a/examples/quick/threading/CMakeLists.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(threading LANGUAGES CXX)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/threading")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_standard_project_setup()
-
-add_subdirectory("../shared" "shared")
-
-qt_add_executable(threadingexample
- WIN32
- MACOSX_BUNDLE
- main.cpp
-)
-
-target_link_libraries(threadingexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-add_dependencies(threadingexample threading_shared)
-
-qt_add_qml_module(threadingexample
- URI threading
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "threadedlistmodel/dataloader.mjs"
- "threadedlistmodel/timedisplay.qml"
- "threading.qml"
- "workerscript/Spinner.qml"
- "workerscript/workerscript.mjs"
- "workerscript/workerscript.qml"
-)
-
-install(TARGETS threadingexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
-
-bundle_shared(threadingexample)
diff --git a/examples/quick/threading/doc/images/qml-threading-example.png b/examples/quick/threading/doc/images/qml-threading-example.png
deleted file mode 100644
index 6ed79ab190..0000000000
--- a/examples/quick/threading/doc/images/qml-threading-example.png
+++ /dev/null
Binary files differ
diff --git a/examples/quick/threading/doc/src/threading.qdoc b/examples/quick/threading/doc/src/threading.qdoc
deleted file mode 100644
index fb90c32ab6..0000000000
--- a/examples/quick/threading/doc/src/threading.qdoc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-/*!
- \title Qt Quick Examples - Threading
- \example threading
- \brief This is a collection of QML multithreading examples.
- \image qml-threading-example.png
- \ingroup qtquickexamples
-
- \e Threading is a collection of QML multithreading examples.
-
- \include examples-run.qdocinc
-
- \section1 Threaded ListModel
-
- \e{Threaded ListModel} contains a \l ListView and a \l ListModel.
- The ListModel object is updated asynchronously in another thread, and the
- results propagate back to the main thread. A timer requests updates from the
- worker thread periodically:
- \snippet threading/threadedlistmodel/timedisplay.qml 0
-
- Inside the worker thread, the ListModel is synchronized once the data is
- finished loading:
- \snippet threading/threadedlistmodel/dataloader.mjs 0
-
- \section1 WorkerScript
-
- \e WorkerScript contains an example of using a \l WorkerScript to offload
- expensive calculations into another thread. This keeps the UI from being
- blocked. This example calculates numbers in Pascal's Triangle, and not in a
- very optimal way, so it will often take several seconds to complete the
- calculation. By doing this in a WorkerScript in another thread, the UI is
- not blocked during this time.
-
- When the UI needs another value, a request is sent to the WorkerScript:
- \snippet threading/workerscript/workerscript.qml 0
-
- The workerscript then is free to take a really long time to calculate it:
- \snippet threading/workerscript/workerscript.mjs 0
-
- When it's done, the result returns to the main scene via the WorkerScript
- type:
- \snippet threading/workerscript/workerscript.qml 1
-*/
diff --git a/examples/quick/threading/main.cpp b/examples/quick/threading/main.cpp
deleted file mode 100644
index f9df4b795a..0000000000
--- a/examples/quick/threading/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(threading/threading)
diff --git a/examples/quick/threading/threadedlistmodel/dataloader.mjs b/examples/quick/threading/threadedlistmodel/dataloader.mjs
deleted file mode 100644
index f94e1bf895..0000000000
--- a/examples/quick/threading/threadedlistmodel/dataloader.mjs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-// ![0]
-WorkerScript.onMessage = function(msg) {
- if (msg.action == 'appendCurrentTime') {
- var data = {'time': new Date().toTimeString()};
- msg.model.append(data);
- msg.model.sync(); // updates the changes to the list
- }
-}
-// ![0]
diff --git a/examples/quick/threading/threadedlistmodel/doc/src/threadedlistmodel.qdoc b/examples/quick/threading/threadedlistmodel/doc/src/threadedlistmodel.qdoc
deleted file mode 100644
index 1f4d7b38e2..0000000000
--- a/examples/quick/threading/threadedlistmodel/doc/src/threadedlistmodel.qdoc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
- \title Threaded ListModel Example
- \example threading/threadedlistmodel
- \brief The Threaded ListModel example shows how to use a ListModel from multiple
- threads using WorkerScript.
-
- This example shows how to use a ListModel from multiple threads using
- WorkerScript.
-*/
-
diff --git a/examples/quick/threading/threadedlistmodel/threadedlistmodel.qmlproject b/examples/quick/threading/threadedlistmodel/threadedlistmodel.qmlproject
deleted file mode 100644
index bfc1c25bf9..0000000000
--- a/examples/quick/threading/threadedlistmodel/threadedlistmodel.qmlproject
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QmlProject 1.1
-
-Project {
- mainFile: "timedisplay.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/threading/threadedlistmodel/timedisplay.qml b/examples/quick/threading/threadedlistmodel/timedisplay.qml
deleted file mode 100644
index 9c14fb1d11..0000000000
--- a/examples/quick/threading/threadedlistmodel/timedisplay.qml
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- color: "white"
- width: 200
- height: 300
-
- ListView {
- anchors.fill: parent
- model: listModel
- delegate: Component {
- Text {
- required property string time
- text: time
- }
- }
-
- ListModel { id: listModel }
-
- WorkerScript {
- id: worker
- source: "dataloader.mjs"
- }
-
-// ![0]
- Timer {
- id: timer
- interval: 2000; repeat: true
- running: true
- triggeredOnStart: true
-
- onTriggered: {
- var msg = {'action': 'appendCurrentTime', 'model': listModel};
- worker.sendMessage(msg);
- }
- }
-// ![0]
- }
-}
diff --git a/examples/quick/threading/threading.pro b/examples/quick/threading/threading.pro
deleted file mode 100644
index cd9ed25e0c..0000000000
--- a/examples/quick/threading/threading.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-RESOURCES += \
- threading.qrc \
- ../shared/shared.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/threading
-INSTALLS += target
diff --git a/examples/quick/threading/threading.qml b/examples/quick/threading/threading.qml
deleted file mode 100644
index 55a61ee155..0000000000
--- a/examples/quick/threading/threading.qml
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import shared as Examples
-
-Item {
- height: 480
- width: 320
- Examples.LauncherList {
- id: ll
- anchors.fill: parent
- Component.onCompleted: {
- addExample("Threaded ListModel", "Updates a ListModel in another thread", Qt.resolvedUrl("threadedlistmodel/timedisplay.qml"));
- addExample("WorkerScript", "Performs calculations in another thread", Qt.resolvedUrl("workerscript/workerscript.qml"));
- }
- }
-}
diff --git a/examples/quick/threading/threading.qmlproject b/examples/quick/threading/threading.qmlproject
deleted file mode 100644
index 877dcd7d63..0000000000
--- a/examples/quick/threading/threading.qmlproject
+++ /dev/null
@@ -1,17 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "threading.qml"
- importPaths: ["../"]
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/threading/threading.qrc b/examples/quick/threading/threading.qrc
deleted file mode 100644
index e20d9a111e..0000000000
--- a/examples/quick/threading/threading.qrc
+++ /dev/null
@@ -1,10 +0,0 @@
-<RCC>
- <qresource prefix="/qt/qml/threading">
- <file>threading.qml</file>
- <file>threadedlistmodel/timedisplay.qml</file>
- <file>threadedlistmodel/dataloader.mjs</file>
- <file>workerscript/Spinner.qml</file>
- <file>workerscript/workerscript.mjs</file>
- <file>workerscript/workerscript.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/quick/threading/workerscript/Spinner.qml b/examples/quick/threading/workerscript/Spinner.qml
deleted file mode 100644
index c21433961e..0000000000
--- a/examples/quick/threading/workerscript/Spinner.qml
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- width: 64
- height: 64
- property alias value: list.currentIndex
- property alias label: caption.text
-
- Text {
- id: caption
- text: "Spinner"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-
- Rectangle {
- anchors.top: caption.bottom
- anchors.topMargin: 4
- anchors.horizontalCenter: parent.horizontalCenter
- height: 48
- width: 32
- color: "black"
- ListView {
- id: list
- anchors.fill: parent
- highlightRangeMode: ListView.StrictlyEnforceRange
- preferredHighlightBegin: height/3
- preferredHighlightEnd: height/3
- clip: true
- model: 64
- delegate: Text {
- required property int index
- font.pixelSize: 18;
- color: "white";
- text: index;
- anchors.horizontalCenter: parent.horizontalCenter
- }
- }
- Rectangle {
- anchors.fill: parent
- gradient: Gradient {
- GradientStop { position: 0.0; color: "#FF000000" }
- GradientStop { position: 0.2; color: "#00000000" }
- GradientStop { position: 0.8; color: "#00000000" }
- GradientStop { position: 1.0; color: "#FF000000" }
- }
- }
- }
-}
diff --git a/examples/quick/threading/workerscript/workerscript.mjs b/examples/quick/threading/workerscript/workerscript.mjs
deleted file mode 100644
index 560c304b47..0000000000
--- a/examples/quick/threading/workerscript/workerscript.mjs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-//Will be initialized when WorkerScript{} is instantiated
-var cache = new Array(64);
-for (var i = 0; i < 64; i++)
- cache[i] = new Array(64);
-
-function triangle(row, column) {
- if (cache[row][column])
- return cache[row][column]
- if (column < 0 || column > row)
- return -1;
- if (column == 0 || column == row)
- return 1;
- return triangle(row-1, column-1) + triangle(row-1, column);
-}
-//! [0]
-WorkerScript.onMessage = function(message) {
- //Calculate result (may take a while, using a naive algorithm)
- var calculatedResult = triangle(message.row, message.column);
- //Send result back to main thread
- WorkerScript.sendMessage( { row: message.row,
- column: message.column,
- result: calculatedResult} );
-}
-//! [0]
diff --git a/examples/quick/threading/workerscript/workerscript.qml b/examples/quick/threading/workerscript/workerscript.qml
deleted file mode 100644
index 3d01f90968..0000000000
--- a/examples/quick/threading/workerscript/workerscript.qml
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- width: 320; height: 480
-
-//! [1]
- WorkerScript {
- id: myWorker
- source: "workerscript.mjs"
-
- onMessage: (messageObject) => {
- if (messageObject.row == rowSpinner.value && messageObject.column == columnSpinner.value){ //Not an old result
- if (messageObject.result == -1)
- resultText.text = "Column must be <= Row";
- else
- resultText.text = messageObject.result;
- }
- }
- }
-//! [1]
- Row {
- y: 24
- spacing: 24
- anchors.horizontalCenter: parent.horizontalCenter
-//! [0]
- Spinner {
- id: rowSpinner
- label: "Row"
- onValueChanged: {
- resultText.text = "Loading...";
- myWorker.sendMessage( { row: rowSpinner.value, column: columnSpinner.value } );
- }
- }
-//! [0]
-
- Spinner {
- id: columnSpinner
- label: "Column"
- onValueChanged: {
- resultText.text = "Loading...";
- myWorker.sendMessage( { row: rowSpinner.value, column: columnSpinner.value } );
- }
- }
- }
-
- Text {
- id: resultText
- y: 180
- width: parent.width
- horizontalAlignment: Text.AlignHCenter
- wrapMode: Text.WordWrap
- font.pixelSize: 32
- }
-
- Text {
- text: "Pascal's Triangle Calculator"
- anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; bottomMargin: 50 }
- }
-}
diff --git a/examples/quick/threading/workerscript/workerscript.qmlproject b/examples/quick/threading/workerscript/workerscript.qmlproject
deleted file mode 100644
index 2eb5b7fe9f..0000000000
--- a/examples/quick/threading/workerscript/workerscript.qmlproject
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QmlProject 1.1
-
-Project {
- mainFile: "workerscript.qml"
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/touchinteraction/CMakeLists.txt b/examples/quick/touchinteraction/CMakeLists.txt
deleted file mode 100644
index 3a0ced72f9..0000000000
--- a/examples/quick/touchinteraction/CMakeLists.txt
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(touchinteraction LANGUAGES CXX)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/touchinteraction")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-
-qt_standard_project_setup()
-
-add_subdirectory("../shared" "shared")
-
-qt_add_executable(touchinteractionexample
- WIN32
- MACOSX_BUNDLE
- main.cpp
-)
-
-target_link_libraries(touchinteractionexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
-)
-
-add_dependencies(touchinteractionexample touchinteraction_shared)
-
-qt_add_qml_module(touchinteractionexample
- URI touchinteraction
- AUTO_RESOURCE_PREFIX
- QML_FILES
- "flickable/Panel.qml"
- "flickable/basic-flickable.qml"
- "flickable/corkboards.qml"
- "multipointtouch/AugmentedTouchPoint.qml"
- "multipointtouch/BearWhackParticleSystem.qml"
- "multipointtouch/ParticleFlame.qml"
- "multipointtouch/bearwhack.qml"
- "multipointtouch/multiflame.qml"
- "pincharea/flickresize.qml"
- "touchinteraction.qml"
- RESOURCES
- "flickable/cork.jpg"
- "flickable/note-yellow.png"
- "flickable/tack.png"
- "multipointtouch/Bear0.png"
- "multipointtouch/Bear1.png"
- "multipointtouch/Bear2.png"
- "multipointtouch/Bear3.png"
- "multipointtouch/BearB.png"
- "multipointtouch/blur-circle.png"
- "multipointtouch/blur-circle3.png"
- "multipointtouch/heart-blur.png"
- "multipointtouch/title.png"
- "pincharea/qt-logo.jpg"
-)
-
-install(TARGETS touchinteractionexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
-
-bundle_shared(touchinteractionexample)
diff --git a/examples/quick/touchinteraction/doc/images/qml-touchinteraction-example.png b/examples/quick/touchinteraction/doc/images/qml-touchinteraction-example.png
deleted file mode 100644
index d4f0cab201..0000000000
--- a/examples/quick/touchinteraction/doc/images/qml-touchinteraction-example.png
+++ /dev/null
Binary files differ
diff --git a/examples/quick/touchinteraction/doc/src/touchinteraction.qdoc b/examples/quick/touchinteraction/doc/src/touchinteraction.qdoc
deleted file mode 100644
index 519e79e741..0000000000
--- a/examples/quick/touchinteraction/doc/src/touchinteraction.qdoc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-/*!
- \title Qt Quick Examples - Touch Interaction
- \example touchinteraction
- \brief A collection of QML Touch Interaction examples.
- \image qml-touchinteraction-example.png
-
- \e{Touch Interaction} is a collection of small QML examples relating to
- touch interaction methods. For more information, visit
- \l{Important Concepts In Qt Quick - User Input}.
-
- \include examples-run.qdocinc
-
- \section1 Multipoint Flames Example
-
- \e{Multipoint Flames} demonstrates distinguishing different fingers in a
- \l MultiPointTouchArea, by assigning a different colored flame to each touch
- point.
-
- The MultipointTouchArea sets up multiple touch points:
- \snippet touchinteraction/multipointtouch/multiflame.qml 0
-
- The flames are then simply bound to the coordinates of the touch point, and
- whether it is currently pressed, as follows:
- \snippet touchinteraction/multipointtouch/multiflame.qml 1
-
- \section1 Bear-Whack Example
-
- \e{Bear-Whack} demonstrates using \l MultiPointTouchArea to add multiple
- finger support to a simple game. The interaction with the game
- is done through a SpriteGoal that follows the TouchPoint. The TouchPoints
- added to the MultiPointTouchArea are a component with the relevant logic
- embedded into it:
- \snippet touchinteraction/multipointtouch/AugmentedTouchPoint.qml 0
-
- \section1 Flick Resize Example
-
- \e{Flick Resize} uses a \l PinchArea to implement a \e{pinch-to-resize}
- behavior. This is easily achieved by listening to the PinchArea signals and
- responding to user input.
- \snippet touchinteraction/pincharea/flickresize.qml 0
-
- \section1 Flickable Example
-
- \e Flickable is a simple example demonstrating the \l Flickable type.
- \snippet touchinteraction/flickable/basic-flickable.qml 0
-
- \section1 Corkboards Example
-
- \e Corkboards shows another use for \l Flickable, with QML types within the
- flickable object that respond to mouse and keyboard interaction. This
- behavior does not require special code as the Qt Quick types already
- cooperate with the Flickable type for accepting touch events.
-*/
diff --git a/examples/quick/touchinteraction/flickable/Panel.qml b/examples/quick/touchinteraction/flickable/Panel.qml
deleted file mode 100644
index 25c864799e..0000000000
--- a/examples/quick/touchinteraction/flickable/Panel.qml
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Item {
- required property string name
- required property var notes
-
- property real horizontalVelocity: 0
-
- id: page
- width: ListView.view.width+40; height: ListView.view.height
-
-
- Image {
- source: "cork.jpg"
- width: page.ListView.view.width
- height: page.ListView.view.height
- fillMode: Image.PreserveAspectCrop
- clip: true
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: page.focus = false;
- }
-
- Text {
- text: page.name; x: 15; y: 8; height: 40; width: 370
- font.pixelSize: 18; font.bold: true; color: "white"
- style: Text.Outline; styleColor: "black"
- }
-
- Repeater {
- model: page.notes
- Item {
- id: stickyPage
- required property string noteText
-
- property int randomX: Math.random() * (page.ListView.view.width-0.5*stickyImage.width) +100
- property int randomY: Math.random() * (page.ListView.view.height-0.5*stickyImage.height) +50
-
- x: randomX; y: randomY
-
- rotation: -page.horizontalVelocity / 100
- Behavior on rotation {
- SpringAnimation { spring: 2.0; damping: 0.15 }
- }
-
- Item {
- id: sticky
- scale: 0.7
-
- Image {
- id: stickyImage
- x: 8 + -width * 0.6 / 2; y: -20
- source: "note-yellow.png"
- scale: 0.6; transformOrigin: Item.TopLeft
- }
-
- TextEdit {
- id: myText
- x: -104; y: 36; width: 215; height: 200
- font.pixelSize: 24
- readOnly: false
- rotation: -8
- text: stickyPage.noteText
- }
-
- Item {
- x: stickyImage.x; y: -20
- width: stickyImage.width * stickyImage.scale
- height: stickyImage.height * stickyImage.scale
-
- MouseArea {
- id: mouse
- anchors.fill: parent
- drag.target: stickyPage
- drag.axis: Drag.XAndYAxis
- drag.minimumY: 0
- drag.maximumY: page.height - 80
- drag.minimumX: 100
- drag.maximumX: page.width - 140
- onClicked: myText.forceActiveFocus()
- }
- }
- }
-
- Image {
- x: -width / 2; y: -height * 0.5 / 2
- source: "tack.png"
- scale: 0.7; transformOrigin: Item.TopLeft
- }
-
- states: State {
- name: "pressed"
- when: mouse.pressed
- PropertyChanges {
- sticky {
- rotation: 8
- scale: 1
- }
- page.z: 8
- }
- }
-
- transitions: Transition {
- NumberAnimation { properties: "rotation,scale"; duration: 200 }
- }
- }
- }
-}
diff --git a/examples/quick/touchinteraction/flickable/basic-flickable.qml b/examples/quick/touchinteraction/flickable/basic-flickable.qml
deleted file mode 100644
index d529f0bec2..0000000000
--- a/examples/quick/touchinteraction/flickable/basic-flickable.qml
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
- //![0]
-Rectangle {
- width: 320
- height: 480
- Flickable {
- anchors.fill: parent
- contentWidth: 1200
- contentHeight: 1200
- Rectangle {
- width: 1000
- height: 1000
- //![0]
- x: 100
- y: 100
- radius: 128
- border.width: 4
- border.color: "black"
- gradient: Gradient {
- GradientStop { position: 0.0; color: "#000000" }
- GradientStop { position: 0.2; color: "#888888" }
- GradientStop { position: 0.4; color: "#FFFFFF" }
- GradientStop { position: 0.6; color: "#FFFFFF" }
- GradientStop { position: 0.8; color: "#888888" }
- GradientStop { position: 1.0; color: "#000000" }
- }
- }
- }
-}
diff --git a/examples/quick/touchinteraction/flickable/corkboards.qml b/examples/quick/touchinteraction/flickable/corkboards.qml
deleted file mode 100644
index a8b9032dfd..0000000000
--- a/examples/quick/touchinteraction/flickable/corkboards.qml
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- width: 320; height: 480
- color: "#464646"
-
- ListModel {
- id: list
-
- ListElement {
- name: "Panel One"
- notes: [
- ListElement { noteText: "Tap to edit" },
- ListElement { noteText: "Drag to move" },
- ListElement { noteText: "Flick to scroll" }
- ]
- }
-
- ListElement {
- name: "Panel Two"
- notes: [
- ListElement { noteText: "Note One" },
- ListElement { noteText: "Note Two" }
- ]
- }
-
- ListElement {
- name: "Panel Three"
- notes: [
- ListElement { noteText: "Note Three" }
- ]
- }
- }
-
- ListView {
- id: flickable
-
- anchors.fill: parent
- focus: true
- highlightRangeMode: ListView.StrictlyEnforceRange
- orientation: ListView.Horizontal
- snapMode: ListView.SnapOneItem
- model: list
- delegate: Panel {
- horizontalVelocity: flickable.horizontalVelocity
- }
- }
-}
diff --git a/examples/quick/touchinteraction/main.cpp b/examples/quick/touchinteraction/main.cpp
deleted file mode 100644
index e268ff0391..0000000000
--- a/examples/quick/touchinteraction/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(touchinteraction/touchinteraction)
diff --git a/examples/quick/touchinteraction/multipointtouch/AugmentedTouchPoint.qml b/examples/quick/touchinteraction/multipointtouch/AugmentedTouchPoint.qml
deleted file mode 100644
index b1d162bbd3..0000000000
--- a/examples/quick/touchinteraction/multipointtouch/AugmentedTouchPoint.qml
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick
-import QtQuick.Particles
-
-//! [0]
-TouchPoint {
- id: container
- property ParticleSystem system
- onPressedChanged: {
- if (pressed) {
- timer.restart();
- child.enabled = true;
- system.explode(x,y);
- }
- }
- property QtObject obj: Timer {
- id: timer
- interval: 100
- running: false
- repeat: false
- onTriggered: container.child.enabled = false
- }
- property Item child: SpriteGoal {
- enabled: false
- x: container.area.x - 16
- y: container.area.y - 16
- width: container.area.width + 32
- height: container.area.height + 32 //+32 so it doesn't have to hit the exact center
- system: container.system
- parent: container.system
- goalState: "falling"
- }
-}
-//! [0]
diff --git a/examples/quick/touchinteraction/multipointtouch/BearWhackParticleSystem.qml b/examples/quick/touchinteraction/multipointtouch/BearWhackParticleSystem.qml
deleted file mode 100644
index 6537f37087..0000000000
--- a/examples/quick/touchinteraction/multipointtouch/BearWhackParticleSystem.qml
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick
-import QtQuick.Particles
-
-ParticleSystem {
- id: particleSystem
-
- property int score
-
- function explode(x,y) {
- fireEmitter.burst(100,x,y);
- }
-
- Emitter {
- id: emitter
- group: "bears"
- width: parent.width
- emitRate: 1
- NumberAnimation on emitRate {
- id: goFaster
- from: 1
- to: 16
- running: particleSystem.running
- loops: 1
- duration: 60000 * 5
- easing.type: Easing.Linear
- }
- lifeSpan: 4000 + 800*(16-emitRate)
- maximumEmitted: 128
- size: 64
- velocity: PointDirection{ y: 40 + 10 * emitter.emitRate }
- }
-
- Emitter {
- id: fireEmitter
- enabled: false
- maximumEmitted: 6000
- group: "flame"
- emitRate: 1000
- size: 16
- endSize: 8
- velocity: CumulativeDirection { AngleDirection {angleVariation: 180; magnitudeVariation: 120;} PointDirection { y: -60 }}
- lifeSpan: 400
- }
- Emitter {
- id: heartEmitter
- enabled: false
- maximumEmitted: 6000
- group: "hearts"
- emitRate: 1000
- size: 16
- endSize: 8
- velocity: AngleDirection {angleVariation: 180; magnitudeVariation: 180;}
- lifeSpan: 600
- }
- Emitter {
- id: bloodEmitter
- enabled: false
- maximumEmitted: 6000
- group: "blood"
- emitRate: 1000
- size: 16
- endSize: 8
- velocity: CumulativeDirection { AngleDirection {angleVariation: 180; magnitudeVariation: 80;} PointDirection { y: 40 }}
- lifeSpan: 600
- }
-
- Affector {
- width: parent.width
- height: 64
- once: true
- y: parent.height - 32
- groups: "bears"
- onAffectParticles: function(particles) {
- for (var i=0;i<particles.length; i++) {
- if (particles[i].animationIndex != 0) {
- particleSystem.score++;
- bloodEmitter.burst(100, particles[i].x, particles[i].y);
- } else {
- particleSystem.score--;
- heartEmitter.burst(100, particles[i].x, particles[i].y);
- }
- particles[i].update = 1.0;
- particles[i].t -= 1000.0;
- }
- }
- }
- ImageParticle {
- groups: ["flame"]
- source: "blur-circle.png"
- z: 4
- colorVariation: 0.1
- color: "#ffa24d"
- alpha: 0.4
- }
- ImageParticle {
- groups: ["blood"]
- color: "red"
- z: 2
- source: "blur-circle3.png"
- alpha: 0.2
- }
- ImageParticle {
- groups: ["hearts"]
- color: "#ff66AA"
- z: 3
- source: "heart-blur.png"
- alpha: 0.4
- autoRotation: true
- }
- ImageParticle {
- groups: ["bears"]
- z: 1
- spritesInterpolate: false
- sprites:[
- Sprite{
- name: "floating"
- source: "Bear1.png"
- frameCount: 9
- frameWidth: 256
- frameHeight: 256
- frameDuration: 80
- to: {"still":0, "flailing":0}
- },
- Sprite{
- name: "flailing"
- source: "Bear2.png"
- frameCount: 8
- frameWidth: 256
- frameHeight: 256
- frameDuration: 80
- to: {"falling":1}
- },
- Sprite{
- name: "falling"
- source: "Bear3.png"
- frameCount: 5
- frameWidth: 256
- frameHeight: 256
- frameDuration: 80
- to: {"falling":1}
- }
- ]
- }
-}
diff --git a/examples/quick/touchinteraction/multipointtouch/ParticleFlame.qml b/examples/quick/touchinteraction/multipointtouch/ParticleFlame.qml
deleted file mode 100644
index 66907b1f0d..0000000000
--- a/examples/quick/touchinteraction/multipointtouch/ParticleFlame.qml
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Particles
-
-ParticleSystem {
- anchors.fill: parent
-
- property alias emitterX: emitter.x
- property alias emitterY: emitter.y
-
- property alias color: img.color
- property alias emitting: emitter.enabled
- ImageParticle {
- id: img
- source: "blur-circle.png"
- colorVariation: 0.1
- color: "#ff521d"
- alpha: 0
- }
- Emitter {
- id: emitter
- velocityFromMovement: 10
- emitRate: 80
- lifeSpan: 1500
- velocity: PointDirection{ y: -90; yVariation: 50; }
- acceleration: PointDirection{ xVariation: 100; yVariation: 90; }
- size: 51
- sizeVariation: 53
- endSize: 64
- }
-}
diff --git a/examples/quick/touchinteraction/multipointtouch/bearwhack.qml b/examples/quick/touchinteraction/multipointtouch/bearwhack.qml
deleted file mode 100644
index c5f7dc585c..0000000000
--- a/examples/quick/touchinteraction/multipointtouch/bearwhack.qml
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick
-import QtQuick.Particles
-
-Item {
- id: root
- height: 480
- width: 320
- Item {
- id: startScreen
- anchors.fill: parent
- z: 1000
- Image {
- source: "title.png"
- anchors.centerIn: parent
- }
- MouseArea{
- anchors.fill: parent
- onClicked: {//Game Start
- parent.visible = false;
- }
- }
- }
- Rectangle {
- id: bg
- anchors.fill: parent
- gradient: Gradient {
- GradientStop { position: 0.0; color: "DarkBlue" }
- GradientStop { position: 0.8; color: "SkyBlue" }
- GradientStop { position: 0.81; color: "ForestGreen" }
- GradientStop { position: 1.0; color: "DarkGreen" }
- }
- }
-
- BearWhackParticleSystem {
- id: particleSystem
- anchors.fill: parent
- running: !startScreen.visible
- }
-
- property int score: particleSystem.score
-
- Text {
- anchors.right: parent.right
- anchors.margins: 4
- anchors.top: parent.top
- color: "white"
- function padded(num) {
- var ret = num.toString();
-
- if (ret >= 0)
- return ret.padStart(6, "0");
- else
- return "-" + ret.substr(1).padStart(6, "0");
- }
- text: "Score: " + padded(root.score)
- }
- MultiPointTouchArea {
- anchors.fill: parent
- touchPoints: [//Support up to 4 touches at once?
- AugmentedTouchPoint{ system: particleSystem },
- AugmentedTouchPoint{ system: particleSystem },
- AugmentedTouchPoint{ system: particleSystem },
- AugmentedTouchPoint{ system: particleSystem }
- ]
- }
- MouseArea{
- anchors.fill: parent
- id: ma
- onPressedChanged: {
- if (pressed) {
- timer.restart();
- sgoal.enabled = true;
- particleSystem.explode(mouseX,mouseY);
- }
- }
- Timer {
- id: timer
- interval: 100
- running: false
- repeat: false
- onTriggered: sgoal.enabled = false
- }
- SpriteGoal {
- id: sgoal
- x: ma.mouseX - 16
- y: ma.mouseY - 16
- width: 32
- height: 32
- system: particleSystem
- parent: particleSystem
- goalState: "falling"
- enabled: false
- }
- }
-}
diff --git a/examples/quick/touchinteraction/multipointtouch/multiflame.qml b/examples/quick/touchinteraction/multipointtouch/multiflame.qml
deleted file mode 100644
index 3e03bf2815..0000000000
--- a/examples/quick/touchinteraction/multipointtouch/multiflame.qml
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick
-
-Rectangle {
- width: 360
- height: 480
- color: "black"
-
-//! [0]
- MultiPointTouchArea {
- anchors.fill: parent
- minimumTouchPoints: 1
- maximumTouchPoints: 5
- touchPoints: [
- TouchPoint { id: touch1 },
- TouchPoint { id: touch2 },
- TouchPoint { id: touch11 },
- TouchPoint { id: touch21 },
- TouchPoint { id: touch31 }
- ]
- }
-//! [0]
-
-//! [1]
- ParticleFlame {
- color: "red"
- emitterX: touch1.x
- emitterY: touch1.y
- emitting: touch1.pressed
- }
-//! [1]
- ParticleFlame {
- color: "green"
- emitterX: touch2.x
- emitterY: touch2.y
- emitting: touch2.pressed
- }
- ParticleFlame {
- color: "yellow"
- emitterX: touch11.x
- emitterY: touch11.y
- emitting: touch11.pressed
- }
- ParticleFlame {
- color: "blue"
- emitterX: touch21.x
- emitterY: touch21.y
- emitting: touch21.pressed
- }
- ParticleFlame {
- color: "violet"
- emitterX: touch31.x
- emitterY: touch31.y
- emitting: touch31.pressed
- }
-}
diff --git a/examples/quick/touchinteraction/pincharea/flickresize.qml b/examples/quick/touchinteraction/pincharea/flickresize.qml
deleted file mode 100644
index 16108be1a7..0000000000
--- a/examples/quick/touchinteraction/pincharea/flickresize.qml
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- width: 640
- height: 360
- color: "gray"
-
- Flickable {
- id: flick
- anchors.fill: parent
- contentWidth: 500
- contentHeight: 500
-
- PinchArea {
- width: Math.max(flick.contentWidth, flick.width)
- height: Math.max(flick.contentHeight, flick.height)
-
- property real initialWidth
- property real initialHeight
- //![0]
- onPinchStarted: {
- initialWidth = flick.contentWidth
- initialHeight = flick.contentHeight
- }
-
- onPinchUpdated: (pinch)=> {
- // adjust content pos due to drag
- flick.contentX += pinch.previousCenter.x - pinch.center.x
- flick.contentY += pinch.previousCenter.y - pinch.center.y
-
- // resize content
- flick.resizeContent(initialWidth * pinch.scale, initialHeight * pinch.scale, pinch.center)
- }
-
- onPinchFinished: {
- // Move its content within bounds.
- flick.returnToBounds()
- }
- //![0]
-
- Rectangle {
- width: flick.contentWidth
- height: flick.contentHeight
- color: "white"
- Image {
- anchors.fill: parent
- source: "qt-logo.jpg"
- MouseArea {
- anchors.fill: parent
- onDoubleClicked: {
- flick.contentWidth = 500
- flick.contentHeight = 500
- }
- }
- }
- }
- }
- }
-}
diff --git a/examples/quick/touchinteraction/touchinteraction.pro b/examples/quick/touchinteraction/touchinteraction.pro
deleted file mode 100644
index 0ae0a41cce..0000000000
--- a/examples/quick/touchinteraction/touchinteraction.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-RESOURCES += \
- touchinteraction.qrc \
- ../shared/shared.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/touchinteraction
-INSTALLS += target
diff --git a/examples/quick/touchinteraction/touchinteraction.qml b/examples/quick/touchinteraction/touchinteraction.qml
deleted file mode 100644
index 6961e48fd4..0000000000
--- a/examples/quick/touchinteraction/touchinteraction.qml
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import shared
-
-Item {
- height: 480
- width: 320
- LauncherList {
- id: ll
- anchors.fill: parent
- Component.onCompleted: {
- addExample("Multipoint Flames", "Create multiple flames with multiple fingers", Qt.resolvedUrl("multipointtouch/multiflame.qml"));
- addExample("Bear-Whack", "Use multiple touches to knock all the bears down", Qt.resolvedUrl("multipointtouch/bearwhack.qml"));
- addExample("Flick Resize", "Manipulate images using pinch gestures", Qt.resolvedUrl("pincharea/flickresize.qml"));
- addExample("Flickable", "A viewport you can move with touch gestures", Qt.resolvedUrl("flickable/basic-flickable.qml"));
- addExample("Corkboards", "Uses touch input on items inside a Flickable", Qt.resolvedUrl("flickable/corkboards.qml"));
- }
- }
-}
diff --git a/examples/quick/touchinteraction/touchinteraction.qmlproject b/examples/quick/touchinteraction/touchinteraction.qmlproject
deleted file mode 100644
index 50254f287f..0000000000
--- a/examples/quick/touchinteraction/touchinteraction.qmlproject
+++ /dev/null
@@ -1,17 +0,0 @@
-import QmlProject 1.1
-
-Project {
- mainFile: "touchinteraction.qml"
- importPaths: [ "../" ]
-
- /* Include .qml, .js, and image files from current directory and subdirectories */
- QmlFiles {
- directory: "."
- }
- JavaScriptFiles {
- directory: "."
- }
- ImageFiles {
- directory: "."
- }
-}
diff --git a/examples/quick/touchinteraction/touchinteraction.qrc b/examples/quick/touchinteraction/touchinteraction.qrc
deleted file mode 100644
index 6d0427b053..0000000000
--- a/examples/quick/touchinteraction/touchinteraction.qrc
+++ /dev/null
@@ -1,27 +0,0 @@
-<RCC>
- <qresource prefix="/qt/qml/touchinteraction">
- <file>touchinteraction.qml</file>
- <file>flickable/basic-flickable.qml</file>
- <file>flickable/corkboards.qml</file>
- <file>flickable/cork.jpg</file>
- <file>flickable/note-yellow.png</file>
- <file>flickable/Panel.qml</file>
- <file>flickable/tack.png</file>
- <file>multipointtouch/bearwhack.qml</file>
- <file>multipointtouch/multiflame.qml</file>
- <file>multipointtouch/AugmentedTouchPoint.qml</file>
- <file>multipointtouch/Bear0.png</file>
- <file>multipointtouch/Bear1.png</file>
- <file>multipointtouch/Bear2.png</file>
- <file>multipointtouch/Bear3.png</file>
- <file>multipointtouch/BearB.png</file>
- <file>multipointtouch/BearWhackParticleSystem.qml</file>
- <file>multipointtouch/blur-circle.png</file>
- <file>multipointtouch/blur-circle3.png</file>
- <file>multipointtouch/heart-blur.png</file>
- <file>multipointtouch/ParticleFlame.qml</file>
- <file>multipointtouch/title.png</file>
- <file>pincharea/flickresize.qml</file>
- <file>pincharea/qt-logo.jpg</file>
- </qresource>
-</RCC>
diff --git a/examples/quick/tutorials/dynamicview/CMakeLists.txt b/examples/quick/tutorials/dynamicview/CMakeLists.txt
index 27a94e84f0..2245f3cdfb 100644
--- a/examples/quick/tutorials/dynamicview/CMakeLists.txt
+++ b/examples/quick/tutorials/dynamicview/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(dynamicview1)
qt_internal_add_example(dynamicview2)
diff --git a/examples/quick/tutorials/dynamicview/dynamicview1/CMakeLists.txt b/examples/quick/tutorials/dynamicview/dynamicview1/CMakeLists.txt
index 4cc0d60742..a1ba04f8e9 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview1/CMakeLists.txt
+++ b/examples/quick/tutorials/dynamicview/dynamicview1/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(dynamicview1 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tutorials/dynamicview/dynamicview1")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(dynamicview1
main.cpp
)
@@ -25,21 +21,29 @@ set_target_properties(dynamicview1 PROPERTIES
qt_add_qml_module(dynamicview1
URI dynamicview
- AUTO_RESOURCE_PREFIX
QML_FILES
dynamicview.qml
PetsModel.qml
)
target_link_libraries(dynamicview1 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS dynamicview1
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET dynamicview1
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tutorials/dynamicview/dynamicview1/PetsModel.qml b/examples/quick/tutorials/dynamicview/dynamicview1/PetsModel.qml
index 45c43909e4..0ebbb1d12e 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview1/PetsModel.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview1/PetsModel.qml
@@ -6,59 +6,59 @@ import QtQuick
ListModel {
ListElement {
- name: "Polly"
- type: "Parrot"
+ name: qsTr("Polly")
+ type: qsTr("Parrot")
age: 12
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Penny"
- type: "Turtle"
+ name: qsTr("Penny")
+ type: qsTr("Turtle")
age: 4
- size: "Small"
+ size: qsTr("Small")
}
//![0]
ListElement {
- name: "Warren"
- type: "Rabbit"
+ name: qsTr("Warren")
+ type: qsTr("Rabbit")
age: 2
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Spot"
- type: "Dog"
+ name: qsTr("Spot")
+ type: qsTr("Dog")
age: 9
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Schrödinger"
- type: "Cat"
+ name: qsTr("Schrödinger")
+ type: qsTr("Cat")
age: 2
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Joey"
- type: "Kangaroo"
+ name: qsTr("Joey")
+ type: qsTr("Kangaroo")
age: 1
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Kimba"
- type: "Bunny"
+ name: qsTr("Kimba")
+ type: qsTr("Bunny")
age: 65
- size: "Large"
+ size: qsTr("Large")
}
ListElement {
- name: "Rover"
- type: "Dog"
+ name: qsTr("Rover")
+ type: qsTr("Dog")
age: 5
- size: "Large"
+ size: qsTr("Large")
}
ListElement {
- name: "Tiny"
- type: "Elephant"
+ name: qsTr("Tiny")
+ type: qsTr("Elephant")
age: 15
- size: "Large"
+ size: qsTr("Large")
}
//![1]
}
diff --git a/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml b/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml
index ec776d4153..3cf9194729 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml
@@ -8,7 +8,8 @@ import QtQuick
Rectangle {
id: root
- width: 300; height: 400
+ width: 300
+ height: 400
//![1]
Component {
@@ -32,12 +33,15 @@ Rectangle {
Column {
id: column
- anchors { fill: parent; margins: 2 }
+ anchors {
+ fill: parent
+ margins: 2
+ }
- Text { text: 'Name: ' + name }
- Text { text: 'Type: ' + type }
- Text { text: 'Age: ' + age }
- Text { text: 'Size: ' + size }
+ Text { text: qsTr('Name: ') + content.name }
+ Text { text: qsTr('Type: ') + content.type }
+ Text { text: qsTr('Age: ') + content.age }
+ Text { text: qsTr('Size: ') + content.size }
}
}
}
@@ -46,7 +50,10 @@ Rectangle {
ListView {
id: view
- anchors { fill: parent; margins: 2 }
+ anchors {
+ fill: parent
+ margins: 2
+ }
model: PetsModel {}
delegate: dragDelegate
diff --git a/examples/quick/tutorials/dynamicview/dynamicview2/CMakeLists.txt b/examples/quick/tutorials/dynamicview/dynamicview2/CMakeLists.txt
index 6db9439a77..d1d65497f5 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview2/CMakeLists.txt
+++ b/examples/quick/tutorials/dynamicview/dynamicview2/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(dynamicview2 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tutorials/dynamicview/dynamicview2")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(dynamicview2
main.cpp
)
@@ -25,21 +21,29 @@ set_target_properties(dynamicview2 PROPERTIES
qt_add_qml_module(dynamicview2
URI dynamicview
- AUTO_RESOURCE_PREFIX
QML_FILES
dynamicview.qml
PetsModel.qml
)
target_link_libraries(dynamicview2 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS dynamicview2
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET dynamicview2
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tutorials/dynamicview/dynamicview2/PetsModel.qml b/examples/quick/tutorials/dynamicview/dynamicview2/PetsModel.qml
index acdab53c20..457d8e5dcf 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview2/PetsModel.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview2/PetsModel.qml
@@ -5,57 +5,57 @@ import QtQuick
ListModel {
ListElement {
- name: "Polly"
- type: "Parrot"
+ name: qsTr("Polly")
+ type: qsTr("Parrot")
age: 12
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Penny"
- type: "Turtle"
+ name: qsTr("Penny")
+ type: qsTr("Turtle")
age: 4
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Warren"
- type: "Rabbit"
+ name: qsTr("Warren")
+ type: qsTr("Rabbit")
age: 2
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Spot"
- type: "Dog"
+ name: qsTr("Spot")
+ type: qsTr("Dog")
age: 9
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Schrödinger"
- type: "Cat"
+ name: qsTr("Schrödinger")
+ type: qsTr("Cat")
age: 2
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Joey"
- type: "Kangaroo"
+ name: qsTr("Joey")
+ type: qsTr("Kangaroo")
age: 1
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Kimba"
- type: "Bunny"
+ name: qsTr("Kimba")
+ type: qsTr("Bunny")
age: 65
- size: "Large"
+ size: qsTr("Large")
}
ListElement {
- name: "Rover"
- type: "Dog"
+ name: qsTr("Rover")
+ type: qsTr("Dog")
age: 5
- size: "Large"
+ size: qsTr("Large")
}
ListElement {
- name: "Tiny"
- type: "Elephant"
+ name: qsTr("Tiny")
+ type: qsTr("Elephant")
age: 15
- size: "Large"
+ size: qsTr("Large")
}
}
diff --git a/examples/quick/tutorials/dynamicview/dynamicview2/dynamicview.qml b/examples/quick/tutorials/dynamicview/dynamicview2/dynamicview.qml
index 99b1bbdaa1..d1e954ba27 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview2/dynamicview.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview2/dynamicview.qml
@@ -1,12 +1,14 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
Rectangle {
id: root
- width: 300; height: 400
+ width: 300
+ height: 400
//![0]
Component {
@@ -17,8 +19,15 @@ Rectangle {
id: dragArea
property bool held: false
-
- anchors { left: parent.left; right: parent.right }
+ required property string name
+ required property string type
+ required property string size
+ required property int age
+
+ anchors {
+ left: parent?.left
+ right: parent?.right
+ }
height: content.height
drag.target: held ? content : undefined
@@ -34,7 +43,8 @@ Rectangle {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
- width: dragArea.width; height: column.implicitHeight + 4
+ width: dragArea.width
+ height: column.implicitHeight + 4
border.width: 1
border.color: "lightsteelblue"
@@ -47,21 +57,30 @@ Rectangle {
states: State {
when: dragArea.held
- ParentChange { target: content; parent: root }
+ ParentChange {
+ target: content
+ parent: root
+ }
AnchorChanges {
target: content
- anchors { horizontalCenter: undefined; verticalCenter: undefined }
+ anchors {
+ horizontalCenter: undefined
+ verticalCenter: undefined
+ }
}
}
//![4]
Column {
id: column
- anchors { fill: parent; margins: 2 }
+ anchors {
+ fill: parent
+ margins: 2
+ }
- Text { text: 'Name: ' + name }
- Text { text: 'Type: ' + type }
- Text { text: 'Age: ' + age }
- Text { text: 'Size: ' + size }
+ Text { text: qsTr('Name: ') + dragArea.name }
+ Text { text: qsTr('Type: ') + dragArea.type }
+ Text { text: qsTr('Age: ') + dragArea.age }
+ Text { text: qsTr('Size: ') + dragArea.size }
}
//![2]
}
@@ -73,7 +92,10 @@ Rectangle {
ListView {
id: view
- anchors { fill: parent; margins: 2 }
+ anchors {
+ fill: parent
+ margins: 2
+ }
model: PetsModel {}
delegate: dragDelegate
diff --git a/examples/quick/tutorials/dynamicview/dynamicview3/CMakeLists.txt b/examples/quick/tutorials/dynamicview/dynamicview3/CMakeLists.txt
index 6b7a4b1605..5c561f1112 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview3/CMakeLists.txt
+++ b/examples/quick/tutorials/dynamicview/dynamicview3/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(dynamicview3 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tutorials/dynamicview/dynamicview3")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(dynamicview3
main.cpp
)
@@ -25,21 +21,29 @@ set_target_properties(dynamicview3 PROPERTIES
qt_add_qml_module(dynamicview3
URI dynamicview
- AUTO_RESOURCE_PREFIX
QML_FILES
dynamicview.qml
PetsModel.qml
)
target_link_libraries(dynamicview3 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS dynamicview3
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET dynamicview3
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tutorials/dynamicview/dynamicview3/PetsModel.qml b/examples/quick/tutorials/dynamicview/dynamicview3/PetsModel.qml
index acdab53c20..457d8e5dcf 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview3/PetsModel.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview3/PetsModel.qml
@@ -5,57 +5,57 @@ import QtQuick
ListModel {
ListElement {
- name: "Polly"
- type: "Parrot"
+ name: qsTr("Polly")
+ type: qsTr("Parrot")
age: 12
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Penny"
- type: "Turtle"
+ name: qsTr("Penny")
+ type: qsTr("Turtle")
age: 4
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Warren"
- type: "Rabbit"
+ name: qsTr("Warren")
+ type: qsTr("Rabbit")
age: 2
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Spot"
- type: "Dog"
+ name: qsTr("Spot")
+ type: qsTr("Dog")
age: 9
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Schrödinger"
- type: "Cat"
+ name: qsTr("Schrödinger")
+ type: qsTr("Cat")
age: 2
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Joey"
- type: "Kangaroo"
+ name: qsTr("Joey")
+ type: qsTr("Kangaroo")
age: 1
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Kimba"
- type: "Bunny"
+ name: qsTr("Kimba")
+ type: qsTr("Bunny")
age: 65
- size: "Large"
+ size: qsTr("Large")
}
ListElement {
- name: "Rover"
- type: "Dog"
+ name: qsTr("Rover")
+ type: qsTr("Dog")
age: 5
- size: "Large"
+ size: qsTr("Large")
}
ListElement {
- name: "Tiny"
- type: "Elephant"
+ name: qsTr("Tiny")
+ type: qsTr("Elephant")
age: 15
- size: "Large"
+ size: qsTr("Large")
}
}
diff --git a/examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml b/examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml
index ea88da4e53..1212ac066c 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml
@@ -1,13 +1,15 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
import QtQml.Models
//![0]
Rectangle {
id: root
- width: 300; height: 400
+ width: 300
+ height: 400
Component {
id: dragDelegate
@@ -16,8 +18,15 @@ Rectangle {
id: dragArea
property bool held: false
-
- anchors { left: parent.left; right: parent.right }
+ required property string name
+ required property string type
+ required property string size
+ required property int age
+
+ anchors {
+ left: parent?.left
+ right: parent?.right
+ }
height: content.height
drag.target: held ? content : undefined
@@ -33,7 +42,8 @@ Rectangle {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
- width: dragArea.width; height: column.implicitHeight + 4
+ width: dragArea.width
+ height: column.implicitHeight + 4
border.width: 1
border.color: "lightsteelblue"
@@ -51,29 +61,41 @@ Rectangle {
states: State {
when: dragArea.held
- ParentChange { target: content; parent: root }
+ ParentChange {
+ target: content
+ parent: root
+ }
AnchorChanges {
target: content
- anchors { horizontalCenter: undefined; verticalCenter: undefined }
+ anchors {
+ horizontalCenter: undefined
+ verticalCenter: undefined
+ }
}
}
Column {
id: column
- anchors { fill: parent; margins: 2 }
+ anchors {
+ fill: parent
+ margins: 2
+ }
- Text { text: 'Name: ' + name }
- Text { text: 'Type: ' + type }
- Text { text: 'Age: ' + age }
- Text { text: 'Size: ' + size }
+ Text { text: qsTr('Name: ') + dragArea.name }
+ Text { text: qsTr('Type: ') + dragArea.type }
+ Text { text: qsTr('Age: ') + dragArea.age }
+ Text { text: qsTr('Size: ') + dragArea.size }
}
//![2]
}
//![3]
DropArea {
- anchors { fill: parent; margins: 10 }
+ anchors {
+ fill: parent
+ margins: 10
+ }
- onEntered: (drag)=> {
+ onEntered: (drag) => {
visualModel.items.move(
drag.source.DelegateModel.itemsIndex,
dragArea.DelegateModel.itemsIndex)
@@ -94,7 +116,10 @@ Rectangle {
ListView {
id: view
- anchors { fill: parent; margins: 2 }
+ anchors {
+ fill: parent
+ margins: 2
+ }
model: visualModel
diff --git a/examples/quick/tutorials/dynamicview/dynamicview4/CMakeLists.txt b/examples/quick/tutorials/dynamicview/dynamicview4/CMakeLists.txt
index aff35996da..a3f0ae04e9 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview4/CMakeLists.txt
+++ b/examples/quick/tutorials/dynamicview/dynamicview4/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(dynamicview4 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tutorials/dynamicview/dynamicview4")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(dynamicview4
main.cpp
)
@@ -25,7 +21,6 @@ set_target_properties(dynamicview4 PROPERTIES
qt_add_qml_module(dynamicview4
URI dynamicview
- AUTO_RESOURCE_PREFIX
QML_FILES
dynamicview.qml
ListSelector.qml
@@ -33,14 +28,23 @@ qt_add_qml_module(dynamicview4
)
target_link_libraries(dynamicview4 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS dynamicview4
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET dynamicview4
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tutorials/dynamicview/dynamicview4/ListSelector.qml b/examples/quick/tutorials/dynamicview/dynamicview4/ListSelector.qml
index 42a200a936..ee788417d9 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview4/ListSelector.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview4/ListSelector.qml
@@ -1,6 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
Item {
@@ -11,12 +12,17 @@ Item {
property alias label: labelText.text
property bool expanded
- width: 100; height: labelText.implicitHeight + 26
+ width: 100
+ height: labelText.implicitHeight + 26
Rectangle {
- anchors { left: parent.left; right: parent.right; bottom: parent.bottom; }
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
- height: labelText.implicitHeight + 4 + (expanded ? 20 * view.count : 20)
+ height: labelText.implicitHeight + 4 + (selector.expanded ? 20 * view.count : 20)
Behavior on height { NumberAnimation { duration: 300 } }
radius: 2
@@ -31,13 +37,20 @@ Item {
Text {
id: labelText
- anchors { left: parent.left; top: parent.top; margins: 2 }
+
+ anchors {
+ left: parent.left
+ top: parent.top
+ margins: 2
+ }
}
Rectangle {
anchors {
- left: parent.left; top: labelText.bottom;
- right: parent.right; bottom: parent.bottom;
+ left: parent.left
+ top: labelText.bottom
+ right: parent.right
+ bottom: parent.bottom
margins: 2
leftMargin: 10
}
@@ -53,7 +66,13 @@ Item {
clip: true
delegate: Text {
- anchors { left: parent.left; right: parent.right }
+ required property int index
+ required property string modelData
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
height: 20
verticalAlignment: Text.AlignVCenter
@@ -64,13 +83,16 @@ Item {
anchors.fill: parent
onClicked: {
- view.currentIndex = index
+ parent.ListView.view.currentIndex = parent.index
selector.expanded = !selector.expanded
}
}
}
highlight: Rectangle {
- anchors { left: parent.left; right: parent.right }
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
height: 20
radius: 2
diff --git a/examples/quick/tutorials/dynamicview/dynamicview4/PetsModel.qml b/examples/quick/tutorials/dynamicview/dynamicview4/PetsModel.qml
index acdab53c20..457d8e5dcf 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview4/PetsModel.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview4/PetsModel.qml
@@ -5,57 +5,57 @@ import QtQuick
ListModel {
ListElement {
- name: "Polly"
- type: "Parrot"
+ name: qsTr("Polly")
+ type: qsTr("Parrot")
age: 12
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Penny"
- type: "Turtle"
+ name: qsTr("Penny")
+ type: qsTr("Turtle")
age: 4
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Warren"
- type: "Rabbit"
+ name: qsTr("Warren")
+ type: qsTr("Rabbit")
age: 2
- size: "Small"
+ size: qsTr("Small")
}
ListElement {
- name: "Spot"
- type: "Dog"
+ name: qsTr("Spot")
+ type: qsTr("Dog")
age: 9
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Schrödinger"
- type: "Cat"
+ name: qsTr("Schrödinger")
+ type: qsTr("Cat")
age: 2
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Joey"
- type: "Kangaroo"
+ name: qsTr("Joey")
+ type: qsTr("Kangaroo")
age: 1
- size: "Medium"
+ size: qsTr("Medium")
}
ListElement {
- name: "Kimba"
- type: "Bunny"
+ name: qsTr("Kimba")
+ type: qsTr("Bunny")
age: 65
- size: "Large"
+ size: qsTr("Large")
}
ListElement {
- name: "Rover"
- type: "Dog"
+ name: qsTr("Rover")
+ type: qsTr("Dog")
age: 5
- size: "Large"
+ size: qsTr("Large")
}
ListElement {
- name: "Tiny"
- type: "Elephant"
+ name: qsTr("Tiny")
+ type: qsTr("Elephant")
age: 15
- size: "Large"
+ size: qsTr("Large")
}
}
diff --git a/examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml b/examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml
index cb454c0d46..1a354d12d9 100644
--- a/examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml
+++ b/examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml
@@ -1,13 +1,15 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
import QtQml.Models
Rectangle {
id: root
- width: 300; height: 400
+ width: 300
+ height: 400
Component {
id: dragDelegate
@@ -16,8 +18,15 @@ Rectangle {
id: dragArea
property bool held: false
-
- anchors { left: parent.left; right: parent.right }
+ required property string name
+ required property string type
+ required property string size
+ required property int age
+
+ anchors {
+ left: parent?.left
+ right: parent?.right
+ }
height: content.height
enabled: visualModel.sortOrder == visualModel.lessThan.length
@@ -35,7 +44,8 @@ Rectangle {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
- width: dragArea.width; height: column.implicitHeight + 4
+ width: dragArea.width
+ height: column.implicitHeight + 4
border.width: 1
border.color: "lightsteelblue"
@@ -53,28 +63,40 @@ Rectangle {
states: State {
when: dragArea.held
- ParentChange { target: content; parent: root }
+ ParentChange {
+ target: content
+ parent: root
+ }
AnchorChanges {
target: content
- anchors { horizontalCenter: undefined; verticalCenter: undefined }
+ anchors {
+ horizontalCenter: undefined
+ verticalCenter: undefined
+ }
}
}
Column {
id: column
- anchors { fill: parent; margins: 2 }
+ anchors {
+ fill: parent
+ margins: 2
+ }
- Text { text: 'Name: ' + name }
- Text { text: 'Type: ' + type }
- Text { text: 'Age: ' + age }
- Text { text: 'Size: ' + size }
+ Text { text: qsTr('Name: ') + dragArea.name }
+ Text { text: qsTr('Type: ') + dragArea.type }
+ Text { text: qsTr('Age: ') + dragArea.age }
+ Text { text: qsTr('Size: ') + dragArea.size }
}
}
DropArea {
- anchors { fill: parent; margins: 10 }
+ anchors {
+ fill: parent
+ margins: 10
+ }
- onEntered: (drag)=> {
+ onEntered: (drag) => {
visualModel.items.move(
drag.source.DelegateModel.itemsIndex,
dragArea.DelegateModel.itemsIndex)
@@ -110,11 +132,11 @@ Rectangle {
//![6]
//![3]
function insertPosition(lessThan, item) {
- var lower = 0
- var upper = items.count
+ let lower = 0
+ let upper = items.count
while (lower < upper) {
- var middle = Math.floor(lower + (upper - lower) / 2)
- var result = lessThan(item.model, items.get(middle).model);
+ const middle = Math.floor(lower + (upper - lower) / 2)
+ const result = lessThan(item.model, items.get(middle).model)
if (result) {
upper = middle
} else {
@@ -126,8 +148,8 @@ Rectangle {
function sort(lessThan) {
while (unsortedItems.count > 0) {
- var item = unsortedItems.get(0)
- var index = insertPosition(lessThan, item)
+ const item = unsortedItems.get(0)
+ const index = insertPosition(lessThan, item)
item.groups = "items"
items.move(item.itemsIndex, index)
@@ -162,7 +184,10 @@ Rectangle {
id: view
anchors {
- left: parent.left; top: parent.top; right: parent.right; bottom: orderSelector.top;
+ left: parent.left
+ top: parent.top
+ right: parent.right
+ bottom: orderSelector.top
margins: 2
}
@@ -175,9 +200,14 @@ Rectangle {
ListSelector {
id: orderSelector
- anchors { left: parent.left; right: parent.right; bottom: parent.bottom; margins: 2 }
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ margins: 2
+ }
- label: "Sort By"
- list: [ "Name", "Type", "Age", "Size", "Custom" ]
+ label: qsTr("Sort By")
+ list: [ qsTr("Name"), qsTr("Type"), qsTr("Age"), qsTr("Size"), qsTr("Custom") ]
}
}
diff --git a/examples/quick/tutorials/samegame/CMakeLists.txt b/examples/quick/tutorials/samegame/CMakeLists.txt
index 49b043e18d..4582f44309 100644
--- a/examples/quick/tutorials/samegame/CMakeLists.txt
+++ b/examples/quick/tutorials/samegame/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(samegame1)
qt_internal_add_example(samegame2)
diff --git a/examples/quick/tutorials/samegame/samegame1/CMakeLists.txt b/examples/quick/tutorials/samegame/samegame1/CMakeLists.txt
index 8cc399ccd0..0ce11660a8 100644
--- a/examples/quick/tutorials/samegame/samegame1/CMakeLists.txt
+++ b/examples/quick/tutorials/samegame/samegame1/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(samegame1 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tutorials/samegame/samegame1")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(samegame1
main.cpp
)
@@ -25,7 +21,6 @@ set_target_properties(samegame1 PROPERTIES
qt_add_qml_module(samegame1
URI samegame
- AUTO_RESOURCE_PREFIX
QML_FILES
"Block.qml"
"Button.qml"
@@ -36,14 +31,23 @@ qt_add_qml_module(samegame1
)
target_link_libraries(samegame1 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS samegame1
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET samegame1
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tutorials/samegame/samegame2/CMakeLists.txt b/examples/quick/tutorials/samegame/samegame2/CMakeLists.txt
index 1233e4d407..3fe42e4cd4 100644
--- a/examples/quick/tutorials/samegame/samegame2/CMakeLists.txt
+++ b/examples/quick/tutorials/samegame/samegame2/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(samegame2 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tutorials/samegame/samegame2")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(samegame2
main.cpp
)
@@ -25,7 +21,6 @@ set_target_properties(samegame2 PROPERTIES
qt_add_qml_module(samegame2
URI samegame
- AUTO_RESOURCE_PREFIX
QML_FILES
"Block.qml"
"Button.qml"
@@ -37,14 +32,23 @@ qt_add_qml_module(samegame2
)
target_link_libraries(samegame2 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS samegame2
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET samegame2
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tutorials/samegame/samegame3/CMakeLists.txt b/examples/quick/tutorials/samegame/samegame3/CMakeLists.txt
index 59fa1302a6..4bd5124fbe 100644
--- a/examples/quick/tutorials/samegame/samegame3/CMakeLists.txt
+++ b/examples/quick/tutorials/samegame/samegame3/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(samegame3 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tutorials/samegame/samegame3")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(samegame3
main.cpp
)
@@ -25,7 +21,6 @@ set_target_properties(samegame3 PROPERTIES
qt_add_qml_module(samegame3
URI samegame
- AUTO_RESOURCE_PREFIX
QML_FILES
"Block.qml"
"Button.qml"
@@ -40,14 +35,23 @@ qt_add_qml_module(samegame3
)
target_link_libraries(samegame3 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS samegame3
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET samegame3
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tutorials/samegame/samegame4/CMakeLists.txt b/examples/quick/tutorials/samegame/samegame4/CMakeLists.txt
index 77fdb83718..7760ee1abf 100644
--- a/examples/quick/tutorials/samegame/samegame4/CMakeLists.txt
+++ b/examples/quick/tutorials/samegame/samegame4/CMakeLists.txt
@@ -1,19 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(samegame4 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/tutorials/samegame/samegame4")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
qt_add_executable(samegame4
main.cpp
)
@@ -25,7 +21,6 @@ set_target_properties(samegame4 PROPERTIES
qt_add_qml_module(samegame4
URI samegame
- AUTO_RESOURCE_PREFIX
QML_FILES
"BoomBlock.qml"
"Button.qml"
@@ -47,14 +42,23 @@ qt_add_qml_module(samegame4
)
target_link_libraries(samegame4 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
install(TARGETS samegame4
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET samegame4
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/tutorials/samegame/samegame4/samegame.js b/examples/quick/tutorials/samegame/samegame4/samegame.js
index 80b175d450..99d34c64a0 100644
--- a/examples/quick/tutorials/samegame/samegame4/samegame.js
+++ b/examples/quick/tutorials/samegame/samegame4/samegame.js
@@ -27,10 +27,6 @@ function startNewGame() {
maxRow = Math.floor(gameCanvas.height / gameCanvas.blockSize);
maxIndex = maxRow * maxColumn;
- //Close dialogs
- nameInputDialog.hide();
- dialog.hide();
-
//Initialize Board
board = new Array(maxIndex);
gameCanvas.score = 0;
@@ -41,6 +37,10 @@ function startNewGame() {
}
}
+ //Close dialogs
+ nameInputDialog.hide();
+ dialog.hide();
+
gameDuration = new Date();
}
@@ -191,6 +191,9 @@ function floodMoveCheck(column, row, type) {
//![2]
function saveHighScore(name) {
+ if (gameCanvas.score == 0)
+ return;
+
if (scoresURL != "")
sendHighScore(name);
diff --git a/examples/quick/views/CMakeLists.txt b/examples/quick/views/CMakeLists.txt
index 0eadd33a5a..6800983c6e 100644
--- a/examples/quick/views/CMakeLists.txt
+++ b/examples/quick/views/CMakeLists.txt
@@ -1,45 +1,36 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(views LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/views")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
add_subdirectory("../shared" "shared")
qt_add_executable(viewsexample
+ WIN32
+ MACOSX_BUNDLE
main.cpp
)
-set_target_properties(viewsexample PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
-)
-
target_link_libraries(viewsexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
add_dependencies(viewsexample views_shared)
qt_add_qml_module(viewsexample
URI views
- AUTO_RESOURCE_PREFIX
QML_FILES
"delegatemodel/dragselection.qml"
- "delegatemodel/slideshow.qml"
"gridview/gridview-example.qml"
"listview/content/PetsModel.qml"
"listview/content/PressAndHoldButton.qml"
@@ -88,9 +79,16 @@ qt_add_qml_module(viewsexample
)
install(TARGETS viewsexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(viewsexample)
+qt_generate_deploy_qml_app_script(
+ TARGET viewsexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/views/delegatemodel/dragselection.qml b/examples/quick/views/delegatemodel/dragselection.qml
index 1a66264e1d..57a769fb29 100644
--- a/examples/quick/views/delegatemodel/dragselection.qml
+++ b/examples/quick/views/delegatemodel/dragselection.qml
@@ -5,6 +5,8 @@ import QtQml
import QtQuick
import QtQml.Models
+pragma ComponentBehavior: Bound
+
Item {
id: root
@@ -15,6 +17,7 @@ Item {
Component {
id: packageDelegate
+//![0]
Package {
id: packageRoot
@@ -33,19 +36,39 @@ Item {
Item {
id: draggable
+ Drag.active: visibleContainer.drag.active
+//![0]
+
width: 64
height: 64
- Drag.active: visibleContainer.drag.active
-
- anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
states: State {
when: visibleContainer.drag.active
- AnchorChanges { target: draggable; anchors { horizontalCenter: undefined; verticalCenter: undefined} }
- ParentChange { target: selectionView; parent: draggable; x: 0; y: 0 }
- PropertyChanges { root.dragging: true }
- ParentChange { target: draggable; parent: root }
+ AnchorChanges {
+ target: draggable
+ anchors {
+ horizontalCenter: undefined
+ verticalCenter: undefined
+ }
+ }
+ ParentChange {
+ target: selectionView
+ parent: draggable
+ x: 0
+ y: 0
+ }
+ PropertyChanges {
+ root.dragging: true
+ }
+ ParentChange {
+ target: draggable
+ parent: root
+ }
}
}
DropArea {
@@ -72,8 +95,16 @@ Item {
radius: 8
gradient: Gradient {
- GradientStop { id: gradientStart; position: 0.0; color: "#8AC953" }
- GradientStop { id: gradientEnd; position: 1.0; color: "#8BC953" }
+ GradientStop {
+ id: gradientStart
+ position: 0.0
+ color: "#8AC953"
+ }
+ GradientStop {
+ id: gradientEnd
+ position: 1.0
+ color: "#8BC953"
+ }
}
border.width: 2
@@ -91,8 +122,13 @@ Item {
}
Rectangle {
- anchors { right: parent.right; top: parent.top; margins: 3 }
- width: 12; height: 12
+ anchors {
+ right: parent.right
+ top: parent.top
+ margins: 3
+ }
+ width: 12
+ height: 12
color: packageRoot.DelegateModel.inSelected ? "black" : "white"
radius: 6
@@ -108,27 +144,55 @@ Item {
states: [
State {
name: "selected"
- ParentChange { target: content; parent: selectionContainer; x: 3; y: 3 }
+ ParentChange {
+ target: content
+ parent: selectionContainer
+ x: 3
+ y: 3
+ }
PropertyChanges {
packageRoot.DelegateModel.inItems: visibleContainer.drag.active
gradientStart.color: "#017423"
}
- PropertyChanges { gradientStart.color: "#007423" }
+ PropertyChanges {
+ gradientStart.color: "#007423"
+ }
}, State {
name: "visible"
- PropertyChanges { packageRoot.DelegateModel.inItems: true }
- ParentChange { target: content; parent: visibleContainer; x: 3; y: 3 }
- PropertyChanges { gradientStart.color: "#8AC953" }
- PropertyChanges { gradientStart.color: "#8BC953" }
+ PropertyChanges {
+ packageRoot.DelegateModel.inItems: true
+ }
+ ParentChange {
+ target: content
+ parent: visibleContainer
+ x: 3
+ y: 3
+ }
+ PropertyChanges {
+ gradientStart.color: "#8AC953"
+ }
+ PropertyChanges {
+ gradientStart.color: "#8BC953"
+ }
}
]
transitions: Transition {
- PropertyAction { target: packageRoot; properties: "DelegateModel.inItems" }
+ PropertyAction {
+ target: packageRoot
+ properties: "DelegateModel.inItems"
+ }
ParentAnimation {
target: content
- NumberAnimation { target: content; properties: "x,y"; duration: 500 }
+ NumberAnimation {
+ target: content
+ properties: "x,y"
+ duration: 500
+ }
+ }
+ ColorAnimation {
+ targets: [gradientStart, gradientEnd]
+ duration: 500
}
- ColorAnimation { targets: [gradientStart, gradientEnd]; duration: 500 }
}
}
}
@@ -139,7 +203,10 @@ Item {
model: 35
delegate: packageDelegate
- groups: DelegateModelGroup { id: selectedItems; name: "selected" }
+ groups: DelegateModelGroup {
+ id: selectedItems
+ name: "selected"
+ }
Component.onCompleted: parts.selection.filterOnGroup = "selected"
}
@@ -155,13 +222,16 @@ Item {
path: Path {
startX: 0
startY: 0
- PathLine { x: 64; y: 64 }
+ PathLine {
+ x: 64
+ y: 64
+ }
}
}
GridView {
id: itemsView
- anchors { fill: parent }
+ anchors.fill: parent
cellWidth: 64
cellHeight: 64
model: visualModel.parts.visible
diff --git a/examples/quick/views/delegatemodel/flipable.pro b/examples/quick/views/delegatemodel/flipable.pro
deleted file mode 100644
index ba00bf792c..0000000000
--- a/examples/quick/views/delegatemodel/flipable.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-TEMPLATE = app
-
-QT += quick qml
-SOURCES += main.cpp
-
-RESOURCES += flipable.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/customitems/flipable
-INSTALLS += target
diff --git a/examples/quick/views/delegatemodel/flipable.qrc b/examples/quick/views/delegatemodel/flipable.qrc
deleted file mode 100644
index 7918ccf00e..0000000000
--- a/examples/quick/views/delegatemodel/flipable.qrc
+++ /dev/null
@@ -1,9 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>flipable.qml</file>
- <file>content/5_heart.png</file>
- <file>content/9_club.png</file>
- <file>content/back.png</file>
- <file>content/Card.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/quick/views/delegatemodel/main.cpp b/examples/quick/views/delegatemodel/main.cpp
deleted file mode 100644
index 8eb2fc72c3..0000000000
--- a/examples/quick/views/delegatemodel/main.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include "../../shared/shared.h"
-DECLARATIVE_EXAMPLE_MAIN(flipable)
diff --git a/examples/quick/views/delegatemodel/slideshow.qml b/examples/quick/views/delegatemodel/slideshow.qml
deleted file mode 100644
index c11a72147f..0000000000
--- a/examples/quick/views/delegatemodel/slideshow.qml
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQml.Models
-import shared as Shared
-
-Rectangle {
- id: root
-
- property Item displayItem: null
-
- width: 300; height: 400
-
- color: "black"
-
- Shared.FlickrRssModel {
- id: flickrModel
- tags: "fjords,mountains"
- }
- DelegateModel {
- id: visualModel
-
- model: flickrModel
- delegate: Item {
- id: delegateItem
-
- width: 76; height: 76
-
- required property string thumbnail
-
- Rectangle {
- id: image
- x: 0; y: 0; width: 76; height: 76
- border.width: 1
- border.color: "white"
- color: "black"
-
- Image {
- anchors.fill: parent
- anchors.leftMargin: 1
- anchors.topMargin: 1
-
- source: delegateItem.thumbnail
- fillMode: Image.PreserveAspectFit
- }
-
- MouseArea {
- id: clickArea
- anchors.fill: parent
-
- onClicked: root.displayItem = root.displayItem !== delegateItem ? delegateItem : null
- }
-
- states: [
- State {
- when: root.displayItem === delegateItem
- name: "inDisplay";
- ParentChange { target: image; parent: imageContainer; x: 75; y: 75; width: 150; height: 150 }
- PropertyChanges {
- image.z: 2
- delegateItem.DelegateModel.inItems: false
- }
- },
- State {
- when: root.displayItem !== delegateItem
- name: "inList";
- ParentChange { target: image; parent: delegateItem; x: 2; y: 2; width: 75; height: 75 }
- PropertyChanges {
- image.z: 1
- delegateItem.DelegateModel.inItems: true
- }
- }
- ]
-
- transitions: [
- Transition {
- from: "inList"
- SequentialAnimation {
- PropertyAction { target: delegateItem; property: "DelegateModel.inPersistedItems"; value: true }
- ParentAnimation {
- target: image;
- via: root
- NumberAnimation { target: image; properties: "x,y,width,height"; duration: 1000 }
- }
- }
- }, Transition {
- from: "inDisplay"
- SequentialAnimation {
- ParentAnimation {
- target: image
- NumberAnimation { target: image; properties: "x,y,width,height"; duration: 1000 }
- }
- PropertyAction { target: delegateItem; property: "DelegateModel.inPersistedItems"; value: false }
- }
- }
- ]
- }
- }
- }
-
-
- PathView {
- id: imagePath
-
- anchors { left: parent.left; top: imageContainer.bottom; right: parent.right; bottom: parent.bottom }
- model: visualModel
-
- pathItemCount: 7
- path: Path {
- startX: -50; startY: 0
- PathQuad { x: 150; y: 50; controlX: 0; controlY: 50 }
- PathQuad { x: 350; y: 0; controlX: 300; controlY: 50 }
- }
- }
-
- Item {
- id: imageContainer
- anchors { fill: parent; bottomMargin: 100 }
- }
-}
diff --git a/examples/quick/views/doc/src/views.qdoc b/examples/quick/views/doc/src/views.qdoc
index 8e6e6c1846..7e73ce31d7 100644
--- a/examples/quick/views/doc/src/views.qdoc
+++ b/examples/quick/views/doc/src/views.qdoc
@@ -7,6 +7,7 @@
\brief This is a collection of QML model-view examples.
\image qml-modelviews-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e Views is a collection of small QML examples relating to model and view
functionality. They demonstrate how to show data from a model using the
@@ -63,7 +64,7 @@
\section1 Using Packages
- \e Packages use the \l [QML]{Package} type to transition delegates between
+ \e Packages uses the \l [QML]{Package} type to transition delegates between
two views.
It has a Package object which defines delegate items for each view and an
@@ -76,6 +77,11 @@
\snippet views/package/view.qml 0
+ \e{Draggable Selections} demonstrates the use of Package to group together
+ multiple selected delegates for drag-and-drop within an item view.
+
+ \snippet views/delegatemodel/dragselection.qml 0
+
\section1 Using ObjectModel
\e ObjectModel uses an ObjectModel for the model instead of a \l ListModel.
diff --git a/examples/quick/views/gridview/gridview-example.qml b/examples/quick/views/gridview/gridview-example.qml
index 3f849565ca..f340a97b4c 100644
--- a/examples/quick/views/gridview/gridview-example.qml
+++ b/examples/quick/views/gridview/gridview-example.qml
@@ -4,7 +4,8 @@
import QtQuick
Rectangle {
- width: 300; height: 400
+ width: 300
+ height: 400
color: "white"
ListModel {
@@ -20,26 +21,36 @@ Rectangle {
//! [0]
GridView {
anchors.fill: parent
- cellWidth: 100; cellHeight: 100
+ cellWidth: 100
+ cellHeight: 100
focus: true
model: appModel
- highlight: Rectangle { width: 80; height: 80; color: "lightsteelblue" }
+ highlight: Rectangle {
+ width: 80
+ height: 80
+ color: "lightsteelblue"
+ }
delegate: Item {
required property string icon
required property string name
required property int index
- width: 100; height: 100
+ width: 100
+ height: 100
Image {
id: myIcon
- y: 20; anchors.horizontalCenter: parent.horizontalCenter
+ y: 20
+ anchors.horizontalCenter: parent.horizontalCenter
source: parent.icon
}
Text {
- anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }
+ anchors {
+ top: myIcon.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
text: parent.name
}
MouseArea {
diff --git a/examples/quick/views/listview/content/PressAndHoldButton.qml b/examples/quick/views/listview/content/PressAndHoldButton.qml
index cdd2fb963d..51ab9435d2 100644
--- a/examples/quick/views/listview/content/PressAndHoldButton.qml
+++ b/examples/quick/views/listview/content/PressAndHoldButton.qml
@@ -8,7 +8,7 @@ Image {
property int repeatDelay: 300
property int repeatDuration: 75
- property bool pressed: false
+ property bool pressed
signal clicked
diff --git a/examples/quick/views/listview/content/SmallText.qml b/examples/quick/views/listview/content/SmallText.qml
index a808e3a27d..756610f4b3 100644
--- a/examples/quick/views/listview/content/SmallText.qml
+++ b/examples/quick/views/listview/content/SmallText.qml
@@ -4,6 +4,6 @@
import QtQuick
Text {
- font.pixelSize: 12
+ font.pointSize: 9
}
diff --git a/examples/quick/views/listview/displaymargin.qml b/examples/quick/views/listview/displaymargin.qml
index c4c32bdd64..0030b82b55 100644
--- a/examples/quick/views/listview/displaymargin.qml
+++ b/examples/quick/views/listview/displaymargin.qml
@@ -34,7 +34,8 @@ Item {
Rectangle {
id: header
- width: parent.width; height: 40
+ width: parent.width
+ height: 40
color: "#AAFF0000"
Text {
@@ -47,7 +48,8 @@ Item {
Rectangle {
id: footer
anchors.bottom: parent.bottom
- width: parent.width; height: 40
+ width: parent.width
+ height: 40
color: "#AAFF0000"
Text {
diff --git a/examples/quick/views/listview/dynamiclist.qml b/examples/quick/views/listview/dynamiclist.qml
index fe32ce23d7..736088fbf7 100644
--- a/examples/quick/views/listview/dynamiclist.qml
+++ b/examples/quick/views/listview/dynamiclist.qml
@@ -6,9 +6,12 @@ import "content"
// This example shows how items can be dynamically added to and removed from
// a ListModel, and how these list modifications can be animated.
+pragma ComponentBehavior: Bound
+
Rectangle {
id: container
- width: 500; height: 400
+ width: 500
+ height: 400
color: "#343434"
// The model:
@@ -16,30 +19,48 @@ Rectangle {
id: fruitModel
ListElement {
- name: "Apple"; cost: 2.45
+ name: "Apple"
+ cost: 2.45
attributes: [
- ListElement { description: "Core" },
- ListElement { description: "Deciduous" }
+ ListElement {
+ description: "Core"
+ },
+ ListElement {
+ description: "Deciduous"
+ }
]
}
ListElement {
- name: "Banana"; cost: 1.95
+ name: "Banana"
+ cost: 1.95
attributes: [
- ListElement { description: "Tropical" },
- ListElement { description: "Seedless" }
+ ListElement {
+ description: "Tropical"
+ },
+ ListElement {
+ description: "Seedless"
+ }
]
}
ListElement {
- name: "Cumquat"; cost: 3.25
+ name: "Kumquat"
+ cost: 3.25
attributes: [
- ListElement { description: "Citrus" }
+ ListElement {
+ description: "Citrus"
+ }
]
}
ListElement {
- name: "Durian"; cost: 9.95
+ name: "Durian"
+ cost: 9.95
attributes: [
- ListElement { description: "Tropical" },
- ListElement { description: "Smelly" }
+ ListElement {
+ description: "Tropical"
+ },
+ ListElement {
+ description: "Smelly"
+ }
]
}
}
@@ -51,7 +72,8 @@ Rectangle {
Item {
//! [0]
id: delegateItem
- width: listView.width; height: 80
+ width: listView.width
+ height: 80
clip: true
required property int index
@@ -83,7 +105,7 @@ Rectangle {
Column {
anchors {
left: arrows.right
- horizontalCenter: parent.horizontalCenter;
+ horizontalCenter: parent.horizontalCenter
bottom: parent.verticalCenter
}
@@ -155,18 +177,42 @@ Rectangle {
//! [1]
SequentialAnimation {
id: addAnimation
- PropertyAction { target: delegateItem; property: "height"; value: 0 }
- NumberAnimation { target: delegateItem; property: "height"; to: 80; duration: 250; easing.type: Easing.InOutQuad }
+ PropertyAction {
+ target: delegateItem
+ property: "height"
+ value: 0
+ }
+ NumberAnimation {
+ target: delegateItem
+ property: "height"
+ to: 80
+ duration: 250
+ easing.type: Easing.InOutQuad
+ }
}
ListView.onAdd: addAnimation.start()
SequentialAnimation {
id: removeAnimation
- PropertyAction { target: delegateItem; property: "ListView.delayRemove"; value: true }
- NumberAnimation { target: delegateItem; property: "height"; to: 0; duration: 250; easing.type: Easing.InOutQuad }
+ PropertyAction {
+ target: delegateItem
+ property: "ListView.delayRemove"
+ value: true
+ }
+ NumberAnimation {
+ target: delegateItem
+ property: "height"
+ to: 0
+ duration: 250
+ easing.type: Easing.InOutQuad
+ }
// Make sure delayRemove is set back to false so that the item can be destroyed
- PropertyAction { target: delegateItem; property: "ListView.delayRemove"; value: false }
+ PropertyAction {
+ target: delegateItem
+ property: "ListView.delayRemove"
+ value: false
+ }
}
ListView.onRemove: removeAnimation.start()
}
@@ -177,8 +223,10 @@ Rectangle {
ListView {
id: listView
anchors {
- left: parent.left; top: parent.top;
- right: parent.right; bottom: buttons.top;
+ left: parent.left
+ top: parent.top
+ right: parent.right
+ bottom: buttons.top
margins: 20
}
model: fruitModel
@@ -187,11 +235,15 @@ Rectangle {
Row {
id: buttons
- anchors { left: parent.left; bottom: parent.bottom; margins: 20 }
+ anchors {
+ left: parent.left
+ bottom: parent.bottom
+ margins: 20
+ }
spacing: 10
TextButton {
- text: "Add an item"
+ text: qsTr("Add an item")
onClicked: {
fruitModel.append({
"name": "Pizza Margarita",
@@ -202,7 +254,7 @@ Rectangle {
}
TextButton {
- text: "Remove all items"
+ text: qsTr("Remove all items")
onClicked: fruitModel.clear()
}
}
diff --git a/examples/quick/views/listview/expandingdelegates.qml b/examples/quick/views/listview/expandingdelegates.qml
index 26f2288922..5c1811a00a 100644
--- a/examples/quick/views/listview/expandingdelegates.qml
+++ b/examples/quick/views/listview/expandingdelegates.qml
@@ -8,7 +8,8 @@ import "content"
Rectangle {
id: page
- width: 400; height: 240
+ width: 400
+ height: 240
color: "black"
// Delegate for the recipes. This delegate has two modes:
@@ -31,13 +32,16 @@ Rectangle {
// want to fade.
property real detailsOpacity : 0
//! [0]
- width: listView.width
+ width: ListView.view.width
height: 70
// A simple rounded rectangle for the background
Rectangle {
id: background
- x: 2; y: 2; width: parent.width - x*2; height: parent.height - y*2
+ x: 2
+ y: 2
+ width: parent.width - x * 2
+ height: parent.height - y * 2
color: "ivory"
border.color: "orange"
radius: 5
@@ -58,26 +62,32 @@ Rectangle {
Row {
id: topLayout
- x: 10; y: 10; height: recipeImage.height; width: parent.width
+ x: 10
+ y: 10
+ height: recipeImage.height
+ width: parent.width
spacing: 10
Image {
id: recipeImage
- width: 50; height: 50
+ width: 50
+ height: 50
source: recipe.picture
}
//! [1]
Column {
- width: background.width - recipeImage.width - 20; height: recipeImage.height
+ width: background.width - recipeImage.width - 20
+ height: recipeImage.height
spacing: 5
Text {
text: recipe.title
- font.bold: true; font.pointSize: 16
+ font.bold: true
+ font.pointSize: 16
}
SmallText {
- text: "Ingredients"
+ text: qsTr("Ingredients")
font.bold: true
opacity: recipe.detailsOpacity
}
@@ -94,22 +104,32 @@ Rectangle {
//! [2]
Item {
id: details
- x: 10; width: parent.width - 20
-
- anchors { top: topLayout.bottom; topMargin: 10; bottom: parent.bottom; bottomMargin: 10 }
+ x: 10
+ width: parent.width - 20
+
+ anchors {
+ top: topLayout.bottom
+ topMargin: 10
+ bottom: parent.bottom
+ bottomMargin: 10
+ }
opacity: recipe.detailsOpacity
//! [2]
SmallText {
id: methodTitle
anchors.top: parent.top
- text: "Method"
- font.pointSize: 12; font.bold: true
+ text: qsTr("Method")
+ font.pointSize: 12
+ font.bold: true
}
Flickable {
id: flick
width: parent.width
- anchors { top: methodTitle.bottom; bottom: parent.bottom }
+ anchors {
+ top: methodTitle.bottom
+ bottom: parent.bottom
+ }
contentHeight: methodText.height
clip: true
@@ -122,13 +142,19 @@ Rectangle {
}
Image {
- anchors { right: flick.right; top: flick.top }
+ anchors {
+ right: flick.right
+ top: flick.top
+ }
source: "content/pics/moreUp.png"
opacity: flick.atYBeginning ? 0 : 1
}
Image {
- anchors { right: flick.right; bottom: flick.bottom }
+ anchors {
+ right: flick.right
+ bottom: flick.bottom
+ }
source: "content/pics/moreDown.png"
opacity: flick.atYEnd ? 0 : 1
}
@@ -138,9 +164,12 @@ Rectangle {
// A button to close the detailed view, i.e. set the state back to default ('').
TextButton {
y: 10
- anchors { right: background.right; rightMargin: 10 }
+ anchors {
+ right: background.right
+ rightMargin: 10
+ }
opacity: recipe.detailsOpacity
- text: "Close"
+ text: qsTr("Close")
onClicked: recipe.state = '';
}
@@ -180,8 +209,14 @@ Rectangle {
transitions: Transition {
// Make the state changes smooth
ParallelAnimation {
- ColorAnimation { property: "color"; duration: 500 }
- NumberAnimation { duration: 300; properties: "detailsOpacity,x,contentY,height,width" }
+ ColorAnimation {
+ property: "color"
+ duration: 500
+ }
+ NumberAnimation {
+ duration: 300
+ properties: "detailsOpacity,x,contentY,height,width"
+ }
}
}
}
@@ -192,7 +227,7 @@ Rectangle {
ListView {
id: listView
anchors.fill: parent
- model: RecipesModel {}
+ model: RecipesModel { }
delegate: recipeDelegate
}
}
diff --git a/examples/quick/views/listview/highlight.qml b/examples/quick/views/listview/highlight.qml
index 137347203a..1f9b9c015c 100644
--- a/examples/quick/views/listview/highlight.qml
+++ b/examples/quick/views/listview/highlight.qml
@@ -9,13 +9,15 @@ import QtQuick
import "content"
Rectangle {
- width: 200; height: 300
+ width: 200
+ height: 300
// Define a delegate component. The component will be
// instantiated for each visible item in the list.
component PetDelegate: Item {
id: pet
- width: 200; height: 55
+ width: 200
+ height: 55
required property int index
required property string name
@@ -23,9 +25,15 @@ Rectangle {
required property int age
Column {
- SmallText { text: 'Name: ' + pet.name }
- SmallText { text: 'Type: ' + pet.type }
- SmallText { text: 'Age: ' + pet.age }
+ SmallText {
+ text: 'Name: ' + pet.name
+ }
+ SmallText {
+ text: 'Type: ' + pet.type
+ }
+ SmallText {
+ text: 'Age: ' + pet.age
+ }
}
// indent the item if it is the current item
states: State {
@@ -34,7 +42,10 @@ Rectangle {
PropertyChanges { pet.x: 20 }
}
transitions: Transition {
- NumberAnimation { properties: "x"; duration: 200 }
+ NumberAnimation {
+ properties: "x"
+ duration: 200
+ }
}
MouseArea {
anchors.fill: parent
@@ -45,24 +56,31 @@ Rectangle {
//! [0]
// Define a highlight with customized movement between items.
component HighlightBar : Rectangle {
- width: 200; height: 50
+ width: 200
+ height: 50
color: "#FFFF88"
- y: listView.currentItem.y
- Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } }
+ y: ListView.view.currentItem.y
+ Behavior on y {
+ SpringAnimation {
+ spring: 2
+ damping: 0.1
+ }
+ }
}
ListView {
id: listView
- width: 200; height: parent.height
+ width: 200
+ height: parent.height
x: 30
- model: PetsModel {}
- delegate: PetDelegate {}
+ model: PetsModel { }
+ delegate: PetDelegate { }
focus: true
// Set the highlight delegate. Note we must also set highlightFollowsCurrentItem
// to false so the highlight delegate can control how the highlight is moved.
- highlight: HighlightBar {}
+ highlight: HighlightBar { }
highlightFollowsCurrentItem: false
}
//! [0]
diff --git a/examples/quick/views/listview/highlightranges.qml b/examples/quick/views/listview/highlightranges.qml
index 78f8b197d3..91304ae08b 100644
--- a/examples/quick/views/listview/highlightranges.qml
+++ b/examples/quick/views/listview/highlightranges.qml
@@ -4,6 +4,8 @@
import QtQuick
import "content"
+pragma ComponentBehavior: Bound
+
//! [0]
Rectangle {
id: root
@@ -30,17 +32,26 @@ Rectangle {
}
}
- PauseAnimation { duration: 500 }
+ PauseAnimation {
+ duration: 500
+ }
}
//! [0]
- MouseArea{
+ MouseArea {
id: ma
z: 1
anchors.fill: parent
- onClicked: { z = 1 - z; if (anim.running) anim.stop(); else anim.restart();}
+ onClicked: function () {
+ z = 1 - z;
+ if (anim.running)
+ anim.stop();
+ else
+ anim.restart();
+ }
}
- width: 320; height: 480
+ width: 320
+ height: 480
// This example shows the same model in three different ListView items,
// with different highlight ranges. The highlight ranges are set by the
@@ -68,12 +79,16 @@ Rectangle {
//! [1]
ListView {
id: list1
- height: 50; width: parent.width
- model: PetsModel {id: aModel}
+ height: 50
+ width: parent.width
+ model: PetsModel {
+ id: aModel
+ }
delegate: petDelegate
orientation: ListView.Horizontal
-
- highlight: Rectangle { color: "lightsteelblue" }
+ highlight: Rectangle {
+ color: "lightsteelblue"
+ }
currentIndex: root.current
onCurrentIndexChanged: root.current = currentIndex
focus: true
@@ -82,29 +97,33 @@ Rectangle {
ListView {
id: list2
y: 160
- height: 50; width: parent.width
- model: PetsModel {}
+ height: 50
+ width: parent.width
+ model: PetsModel { }
delegate: petDelegate
orientation: ListView.Horizontal
-
- highlight: Rectangle { color: "yellow" }
+ highlight: Rectangle {
+ color: "yellow"
+ }
currentIndex: root.current
- preferredHighlightBegin: 80; preferredHighlightEnd: 220
+ preferredHighlightBegin: 80
+ preferredHighlightEnd: 220
highlightRangeMode: ListView.ApplyRange
}
ListView {
id: list3
y: 320
- height: 50; width: parent.width
+ height: 50
+ width: parent.width
model: PetsModel {}
delegate: petDelegate
orientation: ListView.Horizontal
-
highlight: Rectangle { color: "yellow" }
currentIndex: root.current
onCurrentIndexChanged: root.current = currentIndex
- preferredHighlightBegin: 125; preferredHighlightEnd: 125
+ preferredHighlightBegin: 125
+ preferredHighlightEnd: 125
highlightRangeMode: ListView.StrictlyEnforceRange
}
//! [1]
@@ -123,9 +142,15 @@ Rectangle {
Column {
id: column
- Text { text: 'Name: ' + petDelegateItem.name }
- Text { text: 'Type: ' + petDelegateItem.type }
- Text { text: 'Age: ' + petDelegateItem.age }
+ Text {
+ text: 'Name: ' + petDelegateItem.name
+ }
+ Text {
+ text: 'Type: ' + petDelegateItem.type
+ }
+ Text {
+ text: 'Age: ' + petDelegateItem.age
+ }
}
MouseArea {
diff --git a/examples/quick/views/listview/sections.qml b/examples/quick/views/listview/sections.qml
index 5b2dfc67cc..83370958fa 100644
--- a/examples/quick/views/listview/sections.qml
+++ b/examples/quick/views/listview/sections.qml
@@ -5,7 +5,7 @@
// the ListView.section attached property.
import QtQuick
-import "content"
+import QtQuick.Controls
Rectangle {
id: container
@@ -14,21 +14,67 @@ Rectangle {
ListModel {
id: animalsModel
- ListElement { name: "Ant"; size: "Tiny" }
- ListElement { name: "Flea"; size: "Tiny" }
- ListElement { name: "Parrot"; size: "Small" }
- ListElement { name: "Guinea pig"; size: "Small" }
- ListElement { name: "Rat"; size: "Small" }
- ListElement { name: "Butterfly"; size: "Small" }
- ListElement { name: "Dog"; size: "Medium" }
- ListElement { name: "Cat"; size: "Medium" }
- ListElement { name: "Pony"; size: "Medium" }
- ListElement { name: "Koala"; size: "Medium" }
- ListElement { name: "Horse"; size: "Large" }
- ListElement { name: "Tiger"; size: "Large" }
- ListElement { name: "Giraffe"; size: "Large" }
- ListElement { name: "Elephant"; size: "Huge" }
- ListElement { name: "Whale"; size: "Huge" }
+
+ ListElement {
+ name: "Ant"
+ size: "Tiny"
+ }
+ ListElement {
+ name: "Flea"
+ size: "Tiny"
+ }
+ ListElement {
+ name: "Parrot"
+ size: "Small"
+ }
+ ListElement {
+ name: "Guinea pig"
+ size: "Small"
+ }
+ ListElement {
+ name: "Rat"
+ size: "Small"
+ }
+ ListElement {
+ name: "Butterfly"
+ size: "Small"
+ }
+ ListElement {
+ name: "Dog"
+ size: "Medium"
+ }
+ ListElement {
+ name: "Cat"
+ size: "Medium"
+ }
+ ListElement {
+ name: "Pony"
+ size: "Medium"
+ }
+ ListElement {
+ name: "Koala"
+ size: "Medium"
+ }
+ ListElement {
+ name: "Horse"
+ size: "Large"
+ }
+ ListElement {
+ name: "Tiger"
+ size: "Large"
+ }
+ ListElement {
+ name: "Giraffe"
+ size: "Large"
+ }
+ ListElement {
+ name: "Elephant"
+ size: "Huge"
+ }
+ ListElement {
+ name: "Whale"
+ size: "Huge"
+ }
}
//! [0]
@@ -36,7 +82,7 @@ Rectangle {
Component {
id: sectionHeading
Rectangle {
- width: container.width
+ width: ListView.view.width
height: childrenRect.height
color: "lightsteelblue"
@@ -58,6 +104,7 @@ Rectangle {
model: animalsModel
delegate: Text {
required property string name
+
text: name
font.pixelSize: 18
}
@@ -73,19 +120,23 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.bottomMargin: 1
spacing: 1
- ToggleButton {
- label: "CurrentLabelAtStart"
- onToggled: {
- if (active)
+
+ CheckBox {
+ id: labelAtStartCheckBox
+ text: qsTr("CurrentLabelAtStart")
+ onClicked: {
+ if (checked)
view.section.labelPositioning |= ViewSection.CurrentLabelAtStart
else
view.section.labelPositioning &= ~ViewSection.CurrentLabelAtStart
}
}
- ToggleButton {
- label: "NextLabelAtEnd"
- onToggled: {
- if (active)
+
+ CheckBox {
+ id: labelAtEndCheckBox
+ text: qsTr("NextLabelAtEnd")
+ onClicked: {
+ if (checked)
view.section.labelPositioning |= ViewSection.NextLabelAtEnd
else
view.section.labelPositioning &= ~ViewSection.NextLabelAtEnd
diff --git a/examples/quick/views/objectmodel/objectmodel.qml b/examples/quick/views/objectmodel/objectmodel.qml
index 898ef7f9e2..f0b116a247 100644
--- a/examples/quick/views/objectmodel/objectmodel.qml
+++ b/examples/quick/views/objectmodel/objectmodel.qml
@@ -7,6 +7,8 @@
import QtQuick
import QtQml.Models
+pragma ComponentBehavior: Bound
+
Rectangle {
id: root
color: "lightgray"
@@ -19,23 +21,41 @@ Rectangle {
id: itemModel
Rectangle {
- width: view.width; height: view.height
+ width: view.width
+ height: view.height
color: "#FFFEF0"
- Text { text: "Page 1"; font.bold: true; anchors.centerIn: parent }
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("Page 1")
+ font.bold: true
+ }
Component.onDestruction: if (root.printDestruction) print("destroyed 1")
}
Rectangle {
- width: view.width; height: view.height
+ width: view.width
+ height: view.height
color: "#F0FFF7"
- Text { text: "Page 2"; font.bold: true; anchors.centerIn: parent }
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("Page 2")
+ font.bold: true
+ }
Component.onDestruction: if (root.printDestruction) print("destroyed 2")
}
Rectangle {
- width: view.width; height: view.height
+ width: view.width
+ height: view.height
color: "#F4F0FF"
- Text { text: "Page 3"; font.bold: true; anchors.centerIn: parent }
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("Page 3")
+ font.bold: true
+ }
Component.onDestruction: if (root.printDestruction) print("destroyed 3")
}
@@ -43,18 +63,27 @@ Rectangle {
ListView {
id: view
- anchors { fill: parent; bottomMargin: 30 }
+ anchors {
+ fill: parent
+ bottomMargin: 30
+ }
model: itemModel
- preferredHighlightBegin: 0; preferredHighlightEnd: 0
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
highlightRangeMode: ListView.StrictlyEnforceRange
orientation: ListView.Horizontal
- snapMode: ListView.SnapOneItem; flickDeceleration: 2000
+ snapMode: ListView.SnapOneItem
+ flickDeceleration: 2000
cacheBuffer: 200
}
//! [0]
Rectangle {
- width: root.width; height: 30
- anchors { top: view.bottom; bottom: parent.bottom }
+ width: root.width
+ height: 30
+ anchors {
+ top: view.bottom
+ bottom: parent.bottom
+ }
color: "gray"
Row {
@@ -63,17 +92,18 @@ Rectangle {
Repeater {
model: itemModel.count
-
- Rectangle {
+ delegate: Rectangle {
required property int index
- width: 5; height: 5
+ width: 5
+ height: 5
radius: 3
- color: view.currentIndex == index ? "blue" : "white"
+ color: view.currentIndex === index ? "blue" : "white"
MouseArea {
- width: 20; height: 20
anchors.centerIn: parent
+ width: 20
+ height: 20
onClicked: view.currentIndex = parent.index
}
}
diff --git a/examples/quick/views/package/Delegate.qml b/examples/quick/views/package/Delegate.qml
index 2e810aebec..7f3eeaa833 100644
--- a/examples/quick/views/package/Delegate.qml
+++ b/examples/quick/views/package/Delegate.qml
@@ -11,26 +11,50 @@ Package {
required property int index
required property string display
- Text { id: listDelegate; width: parent.width; height: 25; text: 'Empty'; Package.name: 'list' }
- Text { id: gridDelegate; width: parent.width / 2; height: 50; text: 'Empty'; Package.name: 'grid' }
+ Text {
+ id: listDelegate
+ width: parent.width
+ height: 25
+ text: 'Empty'
+ Package.name: 'list'
+ }
+
+ Text {
+ id: gridDelegate
+ width: parent.width / 2
+ height: 50
+ text: 'Empty'
+ Package.name: 'grid'
+ }
Rectangle {
id: wrapper
- width: parent.width; height: 25
+ width: parent?.width ?? 0
+ height: 25
color: 'lightsteelblue'
- Text { text: delegate.display; anchors.centerIn: parent }
+ Text {
+ text: delegate.display
+ anchors.centerIn: parent
+ }
state: delegate.upTo > delegate.index ? 'inGrid' : 'inList'
states: [
State {
name: 'inList'
- ParentChange { target: wrapper; parent: listDelegate }
+ ParentChange {
+ target: wrapper
+ parent: listDelegate
+ }
},
State {
name: 'inGrid'
ParentChange {
- target: wrapper; parent: gridDelegate
- x: 0; y: 0; width: gridDelegate.width; height: gridDelegate.height
+ target: wrapper
+ parent: gridDelegate
+ x: 0
+ y: 0
+ width: gridDelegate.width
+ height: gridDelegate.height
}
}
]
@@ -38,7 +62,10 @@ Package {
transitions: [
Transition {
ParentAnimation {
- NumberAnimation { properties: 'x,y,width,height'; duration: 300 }
+ NumberAnimation {
+ properties: 'x,y,width,height'
+ duration: 300
+ }
}
}
]
diff --git a/examples/quick/views/package/view.qml b/examples/quick/views/package/view.qml
index 696557ed7c..a9a0c46198 100644
--- a/examples/quick/views/package/view.qml
+++ b/examples/quick/views/package/view.qml
@@ -4,16 +4,24 @@
import QtQuick
import QtQml.Models
+pragma ComponentBehavior: Bound
+
Rectangle {
id: root
color: "white"
width: 320
height: 480
- property int upTo: 0
+ property int upTo
SequentialAnimation on upTo {
loops: -1
- NumberAnimation { to: 8; duration: 3500 }
- NumberAnimation { to: 0; duration: 3500 }
+ NumberAnimation {
+ to: 8
+ duration: 3500
+ }
+ NumberAnimation {
+ to: 0
+ duration: 3500
+ }
}
ListModel {
@@ -38,14 +46,14 @@ Rectangle {
ListView {
id: lv
- height: parent.height/2
+ height: parent.height / 2
width: parent.width
model: visualModel.parts.list
}
GridView {
- y: parent.height/2
- height: parent.height/2
+ y: parent.height / 2
+ height: parent.height / 2
width: parent.width
cellWidth: width / 2
cellHeight: 50
diff --git a/examples/quick/views/pathview/pathview-example.qml b/examples/quick/views/pathview/pathview-example.qml
index 19fd470c04..f1ecd099c1 100644
--- a/examples/quick/views/pathview/pathview-example.qml
+++ b/examples/quick/views/pathview/pathview-example.qml
@@ -4,24 +4,47 @@
import QtQuick
Rectangle {
- width: 400; height: 240
+ width: 400
+ height: 240
color: "white"
ListModel {
id: appModel
- ListElement { name: "Music"; icon: "pics/AudioPlayer_48.png" }
- ListElement { name: "Movies"; icon: "pics/VideoPlayer_48.png" }
- ListElement { name: "Camera"; icon: "pics/Camera_48.png" }
- ListElement { name: "Calendar"; icon: "pics/DateBook_48.png" }
- ListElement { name: "Messaging"; icon: "pics/EMail_48.png" }
- ListElement { name: "Todo List"; icon: "pics/TodoList_48.png" }
- ListElement { name: "Contacts"; icon: "pics/AddressBook_48.png" }
+ ListElement {
+ name: "Music"
+ icon: "pics/AudioPlayer_48.png"
+ }
+ ListElement {
+ name: "Movies"
+ icon: "pics/VideoPlayer_48.png"
+ }
+ ListElement {
+ name: "Camera"
+ icon: "pics/Camera_48.png"
+ }
+ ListElement {
+ name: "Calendar"
+ icon: "pics/DateBook_48.png"
+ }
+ ListElement {
+ name: "Messaging"
+ icon: "pics/EMail_48.png"
+ }
+ ListElement {
+ name: "Todo List"
+ icon: "pics/TodoList_48.png"
+ }
+ ListElement {
+ name: "Contacts"
+ icon: "pics/AddressBook_48.png"
+ }
}
Component {
id: appDelegate
Item {
- width: 100; height: 100
+ width: 100
+ height: 100
scale: PathView.iconScale
required property string name
@@ -30,11 +53,15 @@ Rectangle {
Image {
id: myIcon
- y: 20; anchors.horizontalCenter: parent.horizontalCenter
+ y: 20
+ anchors.horizontalCenter: parent.horizontalCenter
source: parent.icon
}
Text {
- anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }
+ anchors {
+ top: myIcon.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
text: parent.name
}
@@ -47,7 +74,11 @@ Rectangle {
Component {
id: appHighlight
- Rectangle { width: 80; height: 80; color: "lightsteelblue" }
+ Rectangle {
+ width: 80
+ height: 80
+ color: "lightsteelblue"
+ }
}
PathView {
@@ -62,11 +93,30 @@ Rectangle {
path: Path {
startX: 10
startY: 50
- PathAttribute { name: "iconScale"; value: 0.5 }
- PathQuad { x: 200; y: 150; controlX: 50; controlY: 200 }
- PathAttribute { name: "iconScale"; value: 1.0 }
- PathQuad { x: 390; y: 50; controlX: 350; controlY: 200 }
- PathAttribute { name: "iconScale"; value: 0.5 }
+ PathAttribute {
+ name: "iconScale"
+ value: 0.5
+ }
+ PathQuad {
+ x: 200
+ y: 150
+ controlX: 50
+ controlY: 200
+ }
+ PathAttribute {
+ name: "iconScale"
+ value: 1.0
+ }
+ PathQuad {
+ x: 390
+ y: 50
+ controlX: 350
+ controlY: 200
+ }
+ PathAttribute {
+ name: "iconScale"
+ value: 0.5
+ }
}
}
}
diff --git a/examples/quick/views/views.qml b/examples/quick/views/views.qml
index 1c47df4fcb..77b16fa6bb 100644
--- a/examples/quick/views/views.qml
+++ b/examples/quick/views/views.qml
@@ -19,9 +19,8 @@ Item {
addExample("Sections", "ListView section headers and footers", Qt.resolvedUrl("listview/sections.qml"))
addExample("Packages", "Transitions between a ListView and GridView", Qt.resolvedUrl("package/view.qml"))
addExample("PathView", "A simple PathView", Qt.resolvedUrl("pathview/pathview-example.qml"))
- addExample("ObjectModel", "Using a ObjectModel", Qt.resolvedUrl("objectmodel/objectmodel.qml"))
+ addExample("ObjectModel", "Using an ObjectModel", Qt.resolvedUrl("objectmodel/objectmodel.qml"))
addExample("Display Margins", "A ListView with display margins", Qt.resolvedUrl("listview/displaymargin.qml"))
- addExample("DelegateModel", "A PathView using DelegateModel to instantiate delegates", Qt.resolvedUrl("delegatemodel/slideshow.qml"))
addExample("Draggable Selections", "Enabling drag-and-drop on DelegateModel delegates", Qt.resolvedUrl("delegatemodel/dragselection.qml"))
}
}
diff --git a/examples/quick/views/views.qrc b/examples/quick/views/views.qrc
index 67e708ff93..9b16e81321 100644
--- a/examples/quick/views/views.qrc
+++ b/examples/quick/views/views.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/qt/qml/views">
+
<file>gridview/gridview-example.qml</file>
<file>gridview/pics/AddressBook_48.png</file>
<file>gridview/pics/AudioPlayer_48.png</file>
@@ -41,7 +42,6 @@
<file>pathview/pics/EMail_48.png</file>
<file>pathview/pics/TodoList_48.png</file>
<file>pathview/pics/VideoPlayer_48.png</file>
- <file>delegatemodel/slideshow.qml</file>
<file>delegatemodel/dragselection.qml</file>
<file>objectmodel/objectmodel.qml</file>
<file>views.qml</file>
diff --git a/examples/quick/window/CMakeLists.txt b/examples/quick/window/CMakeLists.txt
index 31932654ea..58bd8fd7e9 100644
--- a/examples/quick/window/CMakeLists.txt
+++ b/examples/quick/window/CMakeLists.txt
@@ -1,18 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(window LANGUAGES CXX)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/window")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
-qt_standard_project_setup()
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory("../shared" "shared")
@@ -23,15 +17,14 @@ qt_add_executable(windowexample
)
target_link_libraries(windowexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_add_qml_module(windowexample
URI window
- AUTO_RESOURCE_PREFIX
QML_FILES
"AllScreens.qml"
"CurrentScreen.qml"
@@ -42,9 +35,16 @@ qt_add_qml_module(windowexample
add_dependencies(windowexample window_shared)
install(TARGETS windowexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-bundle_shared(windowexample)
+qt_generate_deploy_qml_app_script(
+ TARGET windowexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quick/window/Splash.qml b/examples/quick/window/Splash.qml
index 0a7da219db..6989425548 100644
--- a/examples/quick/window/Splash.qml
+++ b/examples/quick/window/Splash.qml
@@ -24,19 +24,20 @@ Window {
Image {
id: splashImage
source: Images.qtLogo
- MouseArea {
- anchors.fill: parent
- onClicked: Qt.quit()
+ TapHandler {
+ onTapped: splash.exit()
}
}
+
+ function exit() {
+ splash.visible = false
+ splash.timeout()
+ }
+
//! [timer]
Timer {
- interval: splash.timeoutInterval; running: true; repeat: false
- onTriggered: {
- splash.visible = false
- splash.timeout()
- }
+ interval: splash.timeoutInterval; running: splash.visible; repeat: false
+ onTriggered: splash.exit()
}
//! [timer]
- Component.onCompleted: visible = true
}
diff --git a/examples/quick/window/doc/src/window.qdoc b/examples/quick/window/doc/src/window.qdoc
index 3dfbf1cd0c..ca3b90b76e 100644
--- a/examples/quick/window/doc/src/window.qdoc
+++ b/examples/quick/window/doc/src/window.qdoc
@@ -6,6 +6,7 @@
\brief This example demonstrates the Window and Screen types in QML.
\image qml-window-example.png
\ingroup qtquickexamples
+ \examplecategory {Graphics}
\e{Window and Screen} shows how to:
diff --git a/examples/quick/window/window.qml b/examples/quick/window/window.qml
index 47cc5257bb..99ff4403e4 100644
--- a/examples/quick/window/window.qml
+++ b/examples/quick/window/window.qml
@@ -7,12 +7,11 @@ import QtQuick.Controls
QtObject {
id: root
property real defaultSpacing: 10
- property SystemPalette palette: SystemPalette { }
property var controlWindow: Window {
width: col.implicitWidth + root.defaultSpacing * 2
height: col.implicitHeight + root.defaultSpacing * 2
- color: root.palette.window
+ color: palette.window
title: "Control Window"
Column {
id: col
@@ -90,13 +89,13 @@ QtObject {
" and has visibility " + parent.visibilityToString(root.testWindow.visibility)
}
Rectangle {
- color: root.palette.text
+ color: palette.text
width: parent.width
height: 1
}
CurrentScreen { }
Rectangle {
- color: root.palette.text
+ color: palette.text
width: parent.width
height: 1
}
@@ -117,35 +116,44 @@ QtObject {
anchors.centerIn: parent
text: "Second Window"
}
- MouseArea {
- anchors.fill: parent
- onClicked: root.testWindow.color = "#e0c31e"
+ TapHandler {
+ onTapped: root.testWindow.color = Qt.rgba(0.25 + Math.random() / 2,
+ 0.65 + Math.random() / 4,
+ 0.15 + Math.random() / 3)
}
Button {
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.margins: root.defaultSpacing
- text: root.testWindow.visibility === Window.FullScreen ? "exit fullscreen" : "go fullscreen"
- width: 150
- onClicked: {
- if (root.testWindow.visibility === Window.FullScreen)
- root.testWindow.visibility = Window.AutomaticVisibility
- else
- root.testWindow.visibility = Window.FullScreen
+ id: fullscreenButton
+ anchors {
+ right: parent.right
+ top: parent.top
+ margins: root.defaultSpacing
}
+ width: height
+ checkable: true
+ Binding on checked { value: root.testWindow.visibility === Window.FullScreen }
+ onClicked: root.testWindow.visibility = checked ? Window.FullScreen : Window.AutomaticVisibility
+ text: "⛶"
+ ToolTip.visible: hovered
+ ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
+ ToolTip.text: root.testWindow.visibility === Window.FullScreen ? qsTr("restore") : qsTr("fill screen")
}
Button {
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: root.defaultSpacing
- text: "X"
- width: 30
+ text: "✘"
+ width: height
+ height: fullscreenButton.height
onClicked: root.testWindow.close()
+ ToolTip.visible: hovered
+ ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
+ ToolTip.text: qsTr("close this window")
}
}
}
property var splashWindow: Splash {
+ visible: true
onTimeout: root.controlWindow.visible = true
}
}
diff --git a/examples/quick/window/window.qrc b/examples/quick/window/window.qrc
index 89d1de1b1f..206491cc13 100644
--- a/examples/quick/window/window.qrc
+++ b/examples/quick/window/window.qrc
@@ -1,5 +1,5 @@
<RCC>
- <qresource prefix="/window">
+ <qresource prefix="/qt/qml/window">
<file>window.qml</file>
<file>Splash.qml</file>
<file>CurrentScreen.qml</file>
diff --git a/examples/quickcontrols/CMakeLists.txt b/examples/quickcontrols/CMakeLists.txt
index e4021bf430..362fa34698 100644
--- a/examples/quickcontrols/CMakeLists.txt
+++ b/examples/quickcontrols/CMakeLists.txt
@@ -1,21 +1,21 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(attachedstyleproperties)
qt_internal_add_example(gallery)
-if (TARGET Qt::Sql)
+if (TARGET Qt6::Sql)
add_subdirectory(chattutorial)
endif()
-if (QT_FEATURE_quicktemplates2_calendar AND TARGET Qt::Sql)
+if (QT_FEATURE_quicktemplates2_calendar AND TARGET Qt6::Sql)
qt_internal_add_example(eventcalendar)
endif()
qt_internal_add_example(texteditor)
qt_internal_add_example(contactlist)
-qt_internal_add_example(sidepanel)
-qt_internal_add_example(swipetoremove)
qt_internal_add_example(wearable)
qt_internal_add_example(imagine/automotive)
-qt_internal_add_example(imagine/musicplayer)
-if(TARGET Qt::Widgets)
+if (TARGET Qt6::Svg AND NOT ANDROID AND NOT IOS)
+ qt_internal_add_example(filesystemexplorer)
+endif()
+if(TARGET Qt6::Widgets)
qt_internal_add_example(flatstyle)
endif()
diff --git a/examples/quickcontrols/attachedstyleproperties/CMakeLists.txt b/examples/quickcontrols/attachedstyleproperties/CMakeLists.txt
index 4228052a26..0e4f2778fa 100644
--- a/examples/quickcontrols/attachedstyleproperties/CMakeLists.txt
+++ b/examples/quickcontrols/attachedstyleproperties/CMakeLists.txt
@@ -3,14 +3,9 @@ project(attachedstyleproperties LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/attachedstyleproperties")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml QuickControls2)
-qt_standard_project_setup()
+
+qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory(MyStyle)
@@ -28,24 +23,25 @@ qt_add_qml_module(attachedstylepropertiesexample
)
target_link_libraries(attachedstylepropertiesexample PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::QuickControls2
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::QuickControls2
)
add_dependencies(attachedstylepropertiesexample MyStyle)
install(TARGETS attachedstylepropertiesexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-include(../../quick/shared/QtBundleQmlModuleForMacOS.cmake)
-
-set(app_target "attachedstylepropertiesexample")
-set(qml_plugin_target "MyStyleplugin")
-set(qml_module_uri "MyStyle")
-add_qml_module_to_macos_app_bundle(
- "${app_target}" "${qml_plugin_target}" "${qml_module_uri}")
+qt_generate_deploy_qml_app_script(
+ TARGET attachedstylepropertiesexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/attachedstyleproperties/MyStyle/CMakeLists.txt b/examples/quickcontrols/attachedstyleproperties/MyStyle/CMakeLists.txt
index 9cb00a81e7..0ff17420b9 100644
--- a/examples/quickcontrols/attachedstyleproperties/MyStyle/CMakeLists.txt
+++ b/examples/quickcontrols/attachedstyleproperties/MyStyle/CMakeLists.txt
@@ -2,8 +2,6 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
message(FATAL_ERROR "MyStyle should be built as part of the 'attachedstyleproperties' project, and not in isolation.")
endif()
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/attachedstyleproperties")
-
qt_policy(SET QTP0001 NEW)
qt_add_qml_module(MyStyle
URI MyStyle
@@ -28,18 +26,22 @@ generate_export_header(MyStyle)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml QuickControls2)
target_link_libraries(MyStyle PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::QuickControls2
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::QuickControls2
)
install(TARGETS MyStyle
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
+install(TARGETS MyStyleplugin
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/MyStyle"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/MyStyle"
+)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
- DESTINATION "${INSTALL_EXAMPLEDIR}"
+ DESTINATION "${CMAKE_INSTALL_BINDIR}/MyStyle"
)
diff --git a/examples/quickcontrols/attachedstyleproperties/MyStyle/Label.qml b/examples/quickcontrols/attachedstyleproperties/MyStyle/Label.qml
index 3d045aa70e..202528a732 100644
--- a/examples/quickcontrols/attachedstyleproperties/MyStyle/Label.qml
+++ b/examples/quickcontrols/attachedstyleproperties/MyStyle/Label.qml
@@ -1,7 +1,6 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick
import QtQuick.Templates as T
import MyStyle
diff --git a/examples/quickcontrols/attachedstyleproperties/MyStyle/Popup.qml b/examples/quickcontrols/attachedstyleproperties/MyStyle/Popup.qml
index 0f5e753f60..25c4ace64a 100644
--- a/examples/quickcontrols/attachedstyleproperties/MyStyle/Popup.qml
+++ b/examples/quickcontrols/attachedstyleproperties/MyStyle/Popup.qml
@@ -1,6 +1,7 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates as T
import MyStyle
diff --git a/examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp b/examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp
index 40f7af702e..feadb138f7 100644
--- a/examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp
+++ b/examples/quickcontrols/attachedstyleproperties/MyStyle/mystyle.cpp
@@ -108,8 +108,6 @@ QColor MyStyle::toolBarColor() const
QColor MyStyle::popupColor() const
{
-// const QColor winColor = windowColor();
-// return m_theme == Light ? winColor.darker(120) : winColor.lighter(120);
return windowColor().lighter(120);
}
diff --git a/examples/quickcontrols/attachedstyleproperties/attachedstyleproperties.qml b/examples/quickcontrols/attachedstyleproperties/attachedstyleproperties.qml
index e08e19801f..95ff4418e8 100644
--- a/examples/quickcontrols/attachedstyleproperties/attachedstyleproperties.qml
+++ b/examples/quickcontrols/attachedstyleproperties/attachedstyleproperties.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
@@ -32,6 +32,7 @@ ApplicationWindow {
Switch {
id: darkModeSwitch
text: qsTr("Dark mode")
+ Layout.fillWidth: false
}
}
}
@@ -48,11 +49,13 @@ ApplicationWindow {
RowLayout {
Button {
text: qsTr("Open Popup")
+ Layout.fillWidth: false
onClicked: popup.open()
}
Button {
text: qsTr("Open Window")
+ Layout.fillWidth: false
onClicked: {
if (!childWindow.active)
childWindow.show()
@@ -65,8 +68,8 @@ ApplicationWindow {
Popup {
id: popup
- dim: true
anchors.centerIn: parent
+ closePolicy: Popup.NoAutoClose
ColumnLayout {
anchors.centerIn: parent
@@ -80,6 +83,7 @@ ApplicationWindow {
Button {
text: qsTr("Close Popup")
Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: false
onClicked: popup.close()
}
}
@@ -103,6 +107,7 @@ ApplicationWindow {
Button {
text: qsTr("Close Window")
Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: false
onClicked: childWindow.close()
}
}
diff --git a/examples/quickcontrols/attachedstyleproperties/doc/src/qtquickcontrols-attachedstyleproperties.qdoc b/examples/quickcontrols/attachedstyleproperties/doc/src/qtquickcontrols-attachedstyleproperties.qdoc
index 4e273ffd38..1b0116c7ec 100644
--- a/examples/quickcontrols/attachedstyleproperties/doc/src/qtquickcontrols-attachedstyleproperties.qdoc
+++ b/examples/quickcontrols/attachedstyleproperties/doc/src/qtquickcontrols-attachedstyleproperties.qdoc
@@ -6,6 +6,7 @@
\keyword Qt Quick Controls - Attached Style Properties Example
\title Qt Quick Controls - Attached Style Properties Example
\ingroup qtquickcontrols-examples
+ \examplecategory {Graphics}
\brief Demonstrates use of QQuickAttachedPropertyPropagator.
\image qtquickcontrols-attachedstyleproperties.png
diff --git a/examples/quickcontrols/chattutorial/CMakeLists.txt b/examples/quickcontrols/chattutorial/CMakeLists.txt
index 4da558f2f5..83744b78cf 100644
--- a/examples/quickcontrols/chattutorial/CMakeLists.txt
+++ b/examples/quickcontrols/chattutorial/CMakeLists.txt
@@ -1,10 +1,16 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_internal_add_example(chapter1)
qt_internal_add_example(chapter2)
qt_internal_add_example(chapter3)
-if (TARGET Qt::Sql)
+if (TARGET Qt6::Sql)
qt_internal_add_example(chapter4)
+ if(TARGET chattutorial-chapter4)
+ set_target_properties(chattutorial-chapter4 PROPERTIES UNITY_BUILD OFF)
+ endif()
qt_internal_add_example(chapter5)
+ if(TARGET chattutorial-chapter5)
+ set_target_properties(chattutorial-chapter5 PROPERTIES UNITY_BUILD OFF)
+ endif()
endif()
diff --git a/examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt
index 549790b9e8..c17c054418 100644
--- a/examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt
+++ b/examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt
@@ -1,17 +1,11 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter1 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/chattutorial/chapter1")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_add_executable(chattutorial-chapter1 WIN32 MACOSX_BUNDLE
@@ -19,17 +13,17 @@ qt_add_executable(chattutorial-chapter1 WIN32 MACOSX_BUNDLE
)
target_link_libraries(chattutorial-chapter1 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_policy(SET QTP0001 NEW)
qt_add_qml_module(chattutorial-chapter1
- URI chapter1
+ URI chattutorial
QML_FILES
- "main.qml"
+ "Main.qml"
)
qt6_add_resources(chattutorial-chapter1 "conf"
@@ -40,7 +34,16 @@ qt6_add_resources(chattutorial-chapter1 "conf"
)
install(TARGETS chattutorial-chapter1
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chattutorial-chapter1
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/chattutorial/chapter1/Main.qml b/examples/quickcontrols/chattutorial/chapter1/Main.qml
new file mode 100644
index 0000000000..19496f44eb
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter1/Main.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 540
+ height: 960
+ visible: true
+
+ Page {
+ anchors.fill: parent
+ header: Label {
+ padding: 10
+ text: qsTr("Contacts")
+ font.pixelSize: 20
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+}
+
diff --git a/examples/quickcontrols/chattutorial/chapter1/chapter1.pro b/examples/quickcontrols/chattutorial/chapter1/chapter1.pro
index 40c7d2ff7d..323b0709d4 100644
--- a/examples/quickcontrols/chattutorial/chapter1/chapter1.pro
+++ b/examples/quickcontrols/chattutorial/chapter1/chapter1.pro
@@ -1,12 +1,13 @@
TEMPLATE = app
QT += qml quick
-CONFIG += c++11
SOURCES += main.cpp
-resources.files = main.qml
-resources.prefix = qt/qml/chapter1/
+resources.files = \
+ Main.qml \
+ qmldir
+resources.prefix = qt/qml/chattutorial/
RESOURCES += resources \
qtquickcontrols2.conf
diff --git a/examples/quickcontrols/chattutorial/chapter1/main.cpp b/examples/quickcontrols/chattutorial/chapter1/main.cpp
index 7252c0c3ad..73d4bf3b63 100644
--- a/examples/quickcontrols/chattutorial/chapter1/main.cpp
+++ b/examples/quickcontrols/chattutorial/chapter1/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
@@ -9,7 +9,7 @@ int main(int argc, char *argv[])
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter1/main.qml")));
+ engine.loadFromModule("chattutorial", "Main");
return app.exec();
}
diff --git a/examples/quickcontrols/chattutorial/chapter1/main.qml b/examples/quickcontrols/chattutorial/chapter1/main.qml
deleted file mode 100644
index d0651b5816..0000000000
--- a/examples/quickcontrols/chattutorial/chapter1/main.qml
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
- width: 540
- height: 960
- visible: true
-
- Page {
- anchors.fill: parent
- header: Label {
- padding: 10
- text: qsTr("Contacts")
- font.pixelSize: 20
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
- }
-}
-
diff --git a/examples/quickcontrols/chattutorial/chapter1/qmldir b/examples/quickcontrols/chattutorial/chapter1/qmldir
new file mode 100644
index 0000000000..1044557225
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter1/qmldir
@@ -0,0 +1,2 @@
+module chattutorial
+Main 1.0 Main.qml
diff --git a/examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt
index 561861afc0..b6e37b4449 100644
--- a/examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt
+++ b/examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt
@@ -1,17 +1,11 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter2 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/chattutorial/chapter2")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_add_executable(chattutorial-chapter2 WIN32 MACOSX_BUNDLE
@@ -19,17 +13,17 @@ qt_add_executable(chattutorial-chapter2 WIN32 MACOSX_BUNDLE
)
target_link_libraries(chattutorial-chapter2 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_policy(SET QTP0001 NEW)
qt_add_qml_module(chattutorial-chapter2
- URI chapter2
+ URI chattutorial
QML_FILES
- "main.qml"
+ "Main.qml"
RESOURCES
"images/Albert_Einstein.png"
"images/Albert_Einstein@2x.png"
@@ -53,7 +47,16 @@ qt6_add_resources(chattutorial-chapter2 "conf"
)
install(TARGETS chattutorial-chapter2
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chattutorial-chapter2
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/chattutorial/chapter2/Main.qml b/examples/quickcontrols/chattutorial/chapter2/Main.qml
new file mode 100644
index 0000000000..daa4360fb9
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter2/Main.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 540
+ height: 960
+ visible: true
+
+ Page {
+ anchors.fill: parent
+ header: Label {
+ padding: 10
+ text: qsTr("Contacts")
+ font.pixelSize: 20
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+ topMargin: 48
+ leftMargin: 48
+ bottomMargin: 48
+ rightMargin: 48
+ spacing: 20
+ model: ["Albert Einstein", "Ernest Hemingway", "Hans Gude"]
+ delegate: ItemDelegate {
+ id: contactDelegate
+ text: modelData
+ width: listView.width - listView.leftMargin - listView.rightMargin
+ height: avatar.implicitHeight
+ leftPadding: avatar.implicitWidth + 32
+
+ required property string modelData
+
+ Image {
+ id: avatar
+ source: "images/" + contactDelegate.modelData.replace(" ", "_") + ".png"
+ }
+ }
+ }
+ }
+}
+
diff --git a/examples/quickcontrols/chattutorial/chapter2/chapter2.pro b/examples/quickcontrols/chattutorial/chapter2/chapter2.pro
index 8679db986a..88f70ce08a 100644
--- a/examples/quickcontrols/chattutorial/chapter2/chapter2.pro
+++ b/examples/quickcontrols/chattutorial/chapter2/chapter2.pro
@@ -1,7 +1,6 @@
TEMPLATE = app
QT += qml quick
-CONFIG += c++11
SOURCES += main.cpp
@@ -18,8 +17,9 @@ resources.files = \
images/Hans_Gude@2x.png \
images/Hans_Gude@3x.png \
images/Hans_Gude@4x.png \
- main.qml
-resources.prefix = qt/qml/chapter2/
+ Main.qml \
+ qmldir
+resources.prefix = qt/qml/chattutorial/
RESOURCES += resources \
qtquickcontrols2.conf
diff --git a/examples/quickcontrols/chattutorial/chapter2/main.cpp b/examples/quickcontrols/chattutorial/chapter2/main.cpp
index 07f034a827..73d4bf3b63 100644
--- a/examples/quickcontrols/chattutorial/chapter2/main.cpp
+++ b/examples/quickcontrols/chattutorial/chapter2/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
@@ -9,7 +9,7 @@ int main(int argc, char *argv[])
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter2/main.qml")));
+ engine.loadFromModule("chattutorial", "Main");
return app.exec();
}
diff --git a/examples/quickcontrols/chattutorial/chapter2/main.qml b/examples/quickcontrols/chattutorial/chapter2/main.qml
deleted file mode 100644
index 851b482b01..0000000000
--- a/examples/quickcontrols/chattutorial/chapter2/main.qml
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
- width: 540
- height: 960
- visible: true
-
- Page {
- anchors.fill: parent
- header: Label {
- padding: 10
- text: qsTr("Contacts")
- font.pixelSize: 20
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
-
- ListView {
- id: listView
- anchors.fill: parent
- topMargin: 48
- leftMargin: 48
- bottomMargin: 48
- rightMargin: 48
- spacing: 20
- model: ["Albert Einstein", "Ernest Hemingway", "Hans Gude"]
- delegate: ItemDelegate {
- text: modelData
- width: listView.width - listView.leftMargin - listView.rightMargin
- height: avatar.implicitHeight
- leftPadding: avatar.implicitWidth + 32
-
- Image {
- id: avatar
- source: "images/" + modelData.replace(" ", "_") + ".png"
- }
- }
- }
- }
-}
-
diff --git a/examples/quickcontrols/chattutorial/chapter2/qmldir b/examples/quickcontrols/chattutorial/chapter2/qmldir
new file mode 100644
index 0000000000..1044557225
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter2/qmldir
@@ -0,0 +1,2 @@
+module chattutorial
+Main 1.0 Main.qml
diff --git a/examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt
index 92ac5683fd..a101f3ebb3 100644
--- a/examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt
+++ b/examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt
@@ -1,17 +1,11 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter3 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/chattutorial/chapter3")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_add_executable(chattutorial-chapter3 WIN32 MACOSX_BUNDLE
@@ -19,19 +13,19 @@ qt_add_executable(chattutorial-chapter3 WIN32 MACOSX_BUNDLE
)
target_link_libraries(chattutorial-chapter3 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
qt_policy(SET QTP0001 NEW)
qt_add_qml_module(chattutorial-chapter3
- URI chapter3
+ URI chattutorial
QML_FILES
"ContactPage.qml"
"ConversationPage.qml"
- "main.qml"
+ "Main.qml"
RESOURCES
"images/Albert_Einstein.png"
"images/Albert_Einstein@2x.png"
@@ -55,7 +49,16 @@ qt6_add_resources(chattutorial-chapter3 "conf"
)
install(TARGETS chattutorial-chapter3
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chattutorial-chapter3
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/chattutorial/chapter3/ContactPage.qml b/examples/quickcontrols/chattutorial/chapter3/ContactPage.qml
index 88979a40b2..939d911964 100644
--- a/examples/quickcontrols/chattutorial/chapter3/ContactPage.qml
+++ b/examples/quickcontrols/chattutorial/chapter3/ContactPage.qml
@@ -1,6 +1,8 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls
@@ -25,15 +27,19 @@ Page {
spacing: 20
model: ["Albert Einstein", "Ernest Hemingway", "Hans Gude"]
delegate: ItemDelegate {
+ id: contactDelegate
text: modelData
width: listView.width - listView.leftMargin - listView.rightMargin
height: avatar.implicitHeight
leftPadding: avatar.implicitWidth + 32
+
+ required property string modelData
+
onClicked: root.StackView.view.push("ConversationPage.qml", { inConversationWith: modelData })
Image {
id: avatar
- source: "images/" + modelData.replace(" ", "_") + ".png"
+ source: "images/" + contactDelegate.modelData.replace(" ", "_") + ".png"
}
}
}
diff --git a/examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml b/examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml
index e61d014e6e..9a4f1861bd 100644
--- a/examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml
+++ b/examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml
@@ -1,6 +1,8 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
@@ -21,7 +23,7 @@ Page {
Label {
id: pageTitle
- text: inConversationWith
+ text: root.inConversationWith
font.pixelSize: 20
anchors.centerIn: parent
}
@@ -41,28 +43,30 @@ Page {
spacing: 12
model: 10
delegate: Row {
- readonly property bool sentByMe: index % 2 == 0
-
+ id: messageDelegate
anchors.right: sentByMe ? listView.contentItem.right : undefined
spacing: 6
+ required property int index
+ readonly property bool sentByMe: index % 2 == 0
+
Rectangle {
id: avatar
width: height
height: parent.height
color: "grey"
- visible: !sentByMe
+ visible: !messageDelegate.sentByMe
}
Rectangle {
width: 80
height: 40
- color: sentByMe ? "lightgrey" : "steelblue"
+ color: messageDelegate.sentByMe ? "lightgrey" : "steelblue"
Label {
anchors.centerIn: parent
- text: index
- color: sentByMe ? "black" : "white"
+ text: messageDelegate.index
+ color: messageDelegate.sentByMe ? "black" : "white"
}
}
}
@@ -73,6 +77,7 @@ Page {
Pane {
id: pane
Layout.fillWidth: true
+ Layout.fillHeight: false
RowLayout {
width: parent.width
@@ -88,6 +93,7 @@ Page {
id: sendButton
text: qsTr("Send")
enabled: messageField.length > 0
+ Layout.fillWidth: false
}
}
}
diff --git a/examples/quickcontrols/chattutorial/chapter3/Main.qml b/examples/quickcontrols/chattutorial/chapter3/Main.qml
new file mode 100644
index 0000000000..bb968e9f7a
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter3/Main.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 540
+ height: 960
+ visible: true
+
+ StackView {
+ id: stackView
+ anchors.fill: parent
+ initialItem: ContactPage {}
+ }
+}
+
diff --git a/examples/quickcontrols/chattutorial/chapter3/chapter3.pro b/examples/quickcontrols/chattutorial/chapter3/chapter3.pro
index 5314e2dd36..b0eebae94a 100644
--- a/examples/quickcontrols/chattutorial/chapter3/chapter3.pro
+++ b/examples/quickcontrols/chattutorial/chapter3/chapter3.pro
@@ -1,7 +1,6 @@
TEMPLATE = app
QT += qml quick
-CONFIG += c++11
SOURCES += main.cpp
@@ -20,8 +19,9 @@ resources.files = \
images/Hans_Gude@2x.png \
images/Hans_Gude@3x.png \
images/Hans_Gude@4x.png \
- main.qml
-resources.prefix = qt/qml/chapter3/
+ Main.qml \
+ qmldir
+resources.prefix = qt/qml/chattutorial/
RESOURCES += resources \
qtquickcontrols2.conf
diff --git a/examples/quickcontrols/chattutorial/chapter3/main.cpp b/examples/quickcontrols/chattutorial/chapter3/main.cpp
index 71e250bed5..73d4bf3b63 100644
--- a/examples/quickcontrols/chattutorial/chapter3/main.cpp
+++ b/examples/quickcontrols/chattutorial/chapter3/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
@@ -9,7 +9,7 @@ int main(int argc, char *argv[])
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter3/main.qml")));
+ engine.loadFromModule("chattutorial", "Main");
return app.exec();
}
diff --git a/examples/quickcontrols/chattutorial/chapter3/main.qml b/examples/quickcontrols/chattutorial/chapter3/main.qml
deleted file mode 100644
index da080e3a45..0000000000
--- a/examples/quickcontrols/chattutorial/chapter3/main.qml
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
- id: window
- width: 540
- height: 960
- visible: true
-
- StackView {
- id: stackView
- anchors.fill: parent
- initialItem: ContactPage {}
- }
-}
-
diff --git a/examples/quickcontrols/chattutorial/chapter3/qmldir b/examples/quickcontrols/chattutorial/chapter3/qmldir
new file mode 100644
index 0000000000..16e455a37b
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter3/qmldir
@@ -0,0 +1,4 @@
+module chattutorial
+ContactPage 1.0 ContactPage.qml
+ConversationPage 1.0 ConversationPage.qml
+Main 1.0 Main.qml
diff --git a/examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt
index cc56b319e7..2d5b930e45 100644
--- a/examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt
+++ b/examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt
@@ -1,17 +1,11 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter4 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/chattutorial/chapter4")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick Sql)
qt_add_executable(chattutorial-chapter4 WIN32 MACOSX_BUNDLE
@@ -21,20 +15,20 @@ qt_add_executable(chattutorial-chapter4 WIN32 MACOSX_BUNDLE
)
target_link_libraries(chattutorial-chapter4 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
- Qt::Sql
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::Sql
)
qt_policy(SET QTP0001 NEW)
qt_add_qml_module(chattutorial-chapter4
- URI chapter4
+ URI chattutorial
QML_FILES
"ContactPage.qml"
"ConversationPage.qml"
- "main.qml"
+ "Main.qml"
RESOURCES
"images/Albert_Einstein.png"
"images/Albert_Einstein@2x.png"
@@ -58,7 +52,16 @@ qt6_add_resources(chattutorial-chapter4 "conf"
)
install(TARGETS chattutorial-chapter4
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chattutorial-chapter4
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/chattutorial/chapter4/ContactPage.qml b/examples/quickcontrols/chattutorial/chapter4/ContactPage.qml
index 739d0b5402..2ed2243289 100644
--- a/examples/quickcontrols/chattutorial/chapter4/ContactPage.qml
+++ b/examples/quickcontrols/chattutorial/chapter4/ContactPage.qml
@@ -1,10 +1,12 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls
-import io.qt.examples.chattutorial
+import chattutorial
Page {
id: root
@@ -27,15 +29,21 @@ Page {
spacing: 20
model: SqlContactModel {}
delegate: ItemDelegate {
+ id: contactDelegate
text: model.display
width: listView.width - listView.leftMargin - listView.rightMargin
height: avatar.implicitHeight
leftPadding: avatar.implicitWidth + 32
+
+ // Use "model" rather than the specific "display" role, because it
+ // would conflict with the display property of ItemDelegate.
+ required property var model
+
onClicked: root.StackView.view.push("ConversationPage.qml", { inConversationWith: model.display })
Image {
id: avatar
- source: "images/" + model.display.replace(" ", "_") + ".png"
+ source: "images/" + contactDelegate.model.display.replace(" ", "_") + ".png"
}
}
}
diff --git a/examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml b/examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml
index ef5b959aec..87ed487c66 100644
--- a/examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml
+++ b/examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml
@@ -1,11 +1,13 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-import io.qt.examples.chattutorial
+import chattutorial
Page {
id: root
@@ -23,7 +25,7 @@ Page {
Label {
id: pageTitle
- text: inConversationWith
+ text: root.inConversationWith
font.pixelSize: 20
anchors.centerIn: parent
}
@@ -42,34 +44,40 @@ Page {
verticalLayoutDirection: ListView.BottomToTop
spacing: 12
model: SqlConversationModel {
- recipient: inConversationWith
+ recipient: root.inConversationWith
}
delegate: Column {
+ id: conversationDelegate
anchors.right: sentByMe ? listView.contentItem.right : undefined
spacing: 6
- readonly property bool sentByMe: model.recipient !== "Me"
+ required property string author
+ required property string recipient
+ required property date timestamp
+ required property string message
+ readonly property bool sentByMe: recipient !== "Me"
Row {
id: messageRow
spacing: 6
- anchors.right: sentByMe ? parent.right : undefined
+ anchors.right: conversationDelegate.sentByMe ? parent.right : undefined
Image {
id: avatar
- source: !sentByMe ? "images/" + model.author.replace(" ", "_") + ".png" : ""
+ source: !conversationDelegate.sentByMe
+ ? "images/" + conversationDelegate.author.replace(" ", "_") + ".png" : ""
}
Rectangle {
width: Math.min(messageText.implicitWidth + 24,
- listView.width - (!sentByMe ? avatar.width + messageRow.spacing : 0))
+ listView.width - (!conversationDelegate.sentByMe ? avatar.width + messageRow.spacing : 0))
height: messageText.implicitHeight + 24
- color: sentByMe ? "lightgrey" : "steelblue"
+ color: conversationDelegate.sentByMe ? "lightgrey" : "steelblue"
Label {
id: messageText
- text: model.message
- color: sentByMe ? "black" : "white"
+ text: conversationDelegate.message
+ color: conversationDelegate.sentByMe ? "black" : "white"
anchors.fill: parent
anchors.margins: 12
wrapMode: Label.Wrap
@@ -79,9 +87,9 @@ Page {
Label {
id: timestampText
- text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm")
+ text: Qt.formatDateTime(conversationDelegate.timestamp, "d MMM hh:mm")
color: "lightgrey"
- anchors.right: sentByMe ? parent.right : undefined
+ anchors.right: conversationDelegate.sentByMe ? parent.right : undefined
}
}
@@ -91,6 +99,7 @@ Page {
Pane {
id: pane
Layout.fillWidth: true
+ Layout.fillHeight: false
RowLayout {
width: parent.width
@@ -106,9 +115,10 @@ Page {
id: sendButton
text: qsTr("Send")
enabled: messageField.length > 0
+ Layout.fillWidth: false
onClicked: {
- listView.model.sendMessage(inConversationWith, messageField.text);
- messageField.text = "";
+ listView.model.sendMessage(root.inConversationWith, messageField.text)
+ messageField.text = ""
}
}
}
diff --git a/examples/quickcontrols/chattutorial/chapter4/Main.qml b/examples/quickcontrols/chattutorial/chapter4/Main.qml
new file mode 100644
index 0000000000..bb968e9f7a
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter4/Main.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 540
+ height: 960
+ visible: true
+
+ StackView {
+ id: stackView
+ anchors.fill: parent
+ initialItem: ContactPage {}
+ }
+}
+
diff --git a/examples/quickcontrols/chattutorial/chapter4/chapter4.pro b/examples/quickcontrols/chattutorial/chapter4/chapter4.pro
index 399b3506c2..4f40fd726b 100644
--- a/examples/quickcontrols/chattutorial/chapter4/chapter4.pro
+++ b/examples/quickcontrols/chattutorial/chapter4/chapter4.pro
@@ -1,7 +1,11 @@
TEMPLATE = app
QT += qml quick sql
-CONFIG += c++11
+CONFIG += c++11 qmltypes
+
+QML_IMPORT_PATH = $$pwd/.
+QML_IMPORT_NAME = chattutorial
+QML_IMPORT_MAJOR_VERSION = 1
HEADERS += sqlcontactmodel.h \
sqlconversationmodel.h
@@ -25,8 +29,9 @@ resources.files = \
images/Hans_Gude@2x.png \
images/Hans_Gude@3x.png \
images/Hans_Gude@4x.png \
- main.qml
-resources.prefix = qt/qml/chapter4/
+ Main.qml \
+ qmldir
+resources.prefix = qt/qml/chattutorial/
RESOURCES += resources \
qtquickcontrols2.conf
diff --git a/examples/quickcontrols/chattutorial/chapter4/main.cpp b/examples/quickcontrols/chattutorial/chapter4/main.cpp
index 4c7289deb3..bcb6c1e923 100644
--- a/examples/quickcontrols/chattutorial/chapter4/main.cpp
+++ b/examples/quickcontrols/chattutorial/chapter4/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtCore>
@@ -7,9 +7,6 @@
#include <QSqlError>
#include <QtQml>
-#include "sqlcontactmodel.h"
-#include "sqlconversationmodel.h"
-
static void connectToDatabase()
{
QSqlDatabase database = QSqlDatabase::database();
@@ -37,13 +34,10 @@ int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
- qmlRegisterType<SqlContactModel>("io.qt.examples.chattutorial", 1, 0, "SqlContactModel");
- qmlRegisterType<SqlConversationModel>("io.qt.examples.chattutorial", 1, 0, "SqlConversationModel");
-
connectToDatabase();
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter4/main.qml")));
+ engine.loadFromModule("chattutorial", "Main");
if (engine.rootObjects().isEmpty())
return -1;
diff --git a/examples/quickcontrols/chattutorial/chapter4/main.qml b/examples/quickcontrols/chattutorial/chapter4/main.qml
deleted file mode 100644
index da080e3a45..0000000000
--- a/examples/quickcontrols/chattutorial/chapter4/main.qml
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
- id: window
- width: 540
- height: 960
- visible: true
-
- StackView {
- id: stackView
- anchors.fill: parent
- initialItem: ContactPage {}
- }
-}
-
diff --git a/examples/quickcontrols/chattutorial/chapter4/qmldir b/examples/quickcontrols/chattutorial/chapter4/qmldir
new file mode 100644
index 0000000000..27f99a4777
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter4/qmldir
@@ -0,0 +1,5 @@
+module chattutorial
+ContactPage 1.0 ContactPage.qml
+ConversationPage 1.0 ConversationPage.qml
+Main 1.0 Main.qml
+typeinfo chapter4.qmltypes
diff --git a/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp b/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp
index 0bccc0d7af..189924deec 100644
--- a/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp
+++ b/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "sqlcontactmodel.h"
@@ -37,7 +37,7 @@ SqlContactModel::SqlContactModel(QObject *parent) :
if (!query.exec("SELECT * FROM Contacts"))
qFatal("Contacts SELECT query failed: %s", qPrintable(query.lastError().text()));
- setQuery(query);
+ setQuery(std::move(query));
if (lastError().isValid())
qFatal("Cannot set query on SqlContactModel: %s", qPrintable(lastError().text()));
}
diff --git a/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h b/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h
index a6d24a4774..c7f9a154eb 100644
--- a/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h
+++ b/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h
@@ -1,13 +1,17 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SQLCONTACTMODEL_H
#define SQLCONTACTMODEL_H
+#include <QQmlEngine>
#include <QSqlQueryModel>
class SqlContactModel : public QSqlQueryModel
{
+ Q_OBJECT
+ QML_ELEMENT
+
public:
SqlContactModel(QObject *parent = nullptr);
};
diff --git a/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp b/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp
index 1a1594b094..5be01de52c 100644
--- a/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp
+++ b/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "sqlconversationmodel.h"
diff --git a/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h b/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h
index 8a3cdd4c43..b4917c0eff 100644
--- a/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h
+++ b/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h
@@ -1,14 +1,16 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SQLCONVERSATIONMODEL_H
#define SQLCONVERSATIONMODEL_H
+#include <QQmlEngine>
#include <QSqlTableModel>
class SqlConversationModel : public QSqlTableModel
{
Q_OBJECT
+ QML_ELEMENT
Q_PROPERTY(QString recipient READ recipient WRITE setRecipient NOTIFY recipientChanged)
public:
diff --git a/examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml b/examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml
index 3fa10c7f13..1b46cfec4e 100644
--- a/examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml
+++ b/examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick.Controls
diff --git a/examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt
index dfffa0f6de..fa20c96d2d 100644
--- a/examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt
+++ b/examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt
@@ -1,17 +1,11 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(chapter5 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/chattutorial/chapter5")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick Sql)
qt_add_executable(chattutorial-chapter5 WIN32 MACOSX_BUNDLE
@@ -21,22 +15,22 @@ qt_add_executable(chattutorial-chapter5 WIN32 MACOSX_BUNDLE
)
target_link_libraries(chattutorial-chapter5 PRIVATE
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Quick
- Qt::Sql
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::Sql
)
qt_policy(SET QTP0001 NEW)
qt_add_qml_module(chattutorial-chapter5
- URI chapter5
+ URI chattutorial
QML_FILES
"+Material/ChatToolBar.qml"
"ChatToolBar.qml"
"ContactPage.qml"
"ConversationPage.qml"
- "main.qml"
+ "Main.qml"
RESOURCES
"images/Albert_Einstein.png"
"images/Albert_Einstein@2x.png"
@@ -60,7 +54,16 @@ qt6_add_resources(chattutorial-chapter5 "conf"
)
install(TARGETS chattutorial-chapter5
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET chattutorial-chapter5
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml b/examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml
index 73f2c655a9..af3a7a3f8c 100644
--- a/examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml
+++ b/examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick.Controls
diff --git a/examples/quickcontrols/chattutorial/chapter5/ContactPage.qml b/examples/quickcontrols/chattutorial/chapter5/ContactPage.qml
index 920c824090..d7254a52b3 100644
--- a/examples/quickcontrols/chattutorial/chapter5/ContactPage.qml
+++ b/examples/quickcontrols/chattutorial/chapter5/ContactPage.qml
@@ -1,10 +1,12 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls
-import io.qt.examples.chattutorial
+import chattutorial
Page {
id: root
@@ -27,15 +29,21 @@ Page {
spacing: 20
model: SqlContactModel {}
delegate: ItemDelegate {
+ id: contactDelegate
text: model.display
width: listView.width - listView.leftMargin - listView.rightMargin
height: avatar.implicitHeight
leftPadding: avatar.implicitWidth + 32
+
+ // Use "model" rather than the specific "display" role, because it
+ // would conflict with the display property of ItemDelegate.
+ required property var model
+
onClicked: root.StackView.view.push("ConversationPage.qml", { inConversationWith: model.display })
Image {
id: avatar
- source: "images/" + model.display.replace(" ", "_") + ".png"
+ source: "images/" + contactDelegate.model.display.replace(" ", "_") + ".png"
}
}
}
diff --git a/examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml b/examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml
index 6e209b7a85..db45deac5b 100644
--- a/examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml
+++ b/examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml
@@ -1,11 +1,13 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-import io.qt.examples.chattutorial
+import chattutorial
Page {
id: root
@@ -23,7 +25,7 @@ Page {
Label {
id: pageTitle
- text: inConversationWith
+ text: root.inConversationWith
font.pixelSize: 20
anchors.centerIn: parent
}
@@ -42,33 +44,39 @@ Page {
verticalLayoutDirection: ListView.BottomToTop
spacing: 12
model: SqlConversationModel {
- recipient: inConversationWith
+ recipient: root.inConversationWith
}
delegate: Column {
+ id: conversationDelegate
anchors.right: sentByMe ? listView.contentItem.right : undefined
spacing: 6
- readonly property bool sentByMe: model.recipient !== "Me"
+ required property string author
+ required property string recipient
+ required property date timestamp
+ required property string message
+ readonly property bool sentByMe: recipient !== "Me"
Row {
id: messageRow
spacing: 6
- anchors.right: sentByMe ? parent.right : undefined
+ anchors.right: conversationDelegate.sentByMe ? parent.right : undefined
Image {
id: avatar
- source: !sentByMe ? "images/" + model.author.replace(" ", "_") + ".png" : ""
+ source: !conversationDelegate.sentByMe
+ ? "images/" + conversationDelegate.author.replace(" ", "_") + ".png" : ""
}
Rectangle {
width: Math.min(messageText.implicitWidth + 24, listView.width - avatar.width - messageRow.spacing)
height: messageText.implicitHeight + 24
- color: sentByMe ? "lightgrey" : "steelblue"
+ color: conversationDelegate.sentByMe ? "lightgrey" : "steelblue"
Label {
id: messageText
- text: model.message
- color: sentByMe ? "black" : "white"
+ text: conversationDelegate.message
+ color: conversationDelegate.sentByMe ? "black" : "white"
anchors.fill: parent
anchors.margins: 12
wrapMode: Label.Wrap
@@ -78,9 +86,9 @@ Page {
Label {
id: timestampText
- text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm")
+ text: Qt.formatDateTime(conversationDelegate.timestamp, "d MMM hh:mm")
color: "lightgrey"
- anchors.right: sentByMe ? parent.right : undefined
+ anchors.right: conversationDelegate.sentByMe ? parent.right : undefined
}
}
@@ -90,6 +98,7 @@ Page {
Pane {
id: pane
Layout.fillWidth: true
+ Layout.fillHeight: false
RowLayout {
width: parent.width
@@ -105,9 +114,10 @@ Page {
id: sendButton
text: qsTr("Send")
enabled: messageField.length > 0
+ Layout.fillWidth: false
onClicked: {
- listView.model.sendMessage(inConversationWith, messageField.text);
- messageField.text = "";
+ listView.model.sendMessage(root.inConversationWith, messageField.text)
+ messageField.text = ""
}
}
}
diff --git a/examples/quickcontrols/chattutorial/chapter5/Main.qml b/examples/quickcontrols/chattutorial/chapter5/Main.qml
new file mode 100644
index 0000000000..bb968e9f7a
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter5/Main.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 540
+ height: 960
+ visible: true
+
+ StackView {
+ id: stackView
+ anchors.fill: parent
+ initialItem: ContactPage {}
+ }
+}
+
diff --git a/examples/quickcontrols/chattutorial/chapter5/chapter5.pro b/examples/quickcontrols/chattutorial/chapter5/chapter5.pro
index 9f44cb0459..aba1e33cdb 100644
--- a/examples/quickcontrols/chattutorial/chapter5/chapter5.pro
+++ b/examples/quickcontrols/chattutorial/chapter5/chapter5.pro
@@ -1,7 +1,11 @@
TEMPLATE = app
QT += qml quick sql
-CONFIG += c++11
+CONFIG += c++11 qmltypes
+
+QML_IMPORT_PATH = $$pwd/.
+QML_IMPORT_NAME = chattutorial
+QML_IMPORT_MAJOR_VERSION = 1
HEADERS += sqlcontactmodel.h \
sqlconversationmodel.h
@@ -27,8 +31,9 @@ resources.files = \
images/Hans_Gude@2x.png \
images/Hans_Gude@3x.png \
images/Hans_Gude@4x.png \
- main.qml
-resources.prefix = qt/qml/chapter5/
+ Main.qml \
+ qmldir
+resources.prefix = qt/qml/chattutorial/
RESOURCES += resources \
qtquickcontrols2.conf
diff --git a/examples/quickcontrols/chattutorial/chapter5/main.cpp b/examples/quickcontrols/chattutorial/chapter5/main.cpp
index a084655830..bcb6c1e923 100644
--- a/examples/quickcontrols/chattutorial/chapter5/main.cpp
+++ b/examples/quickcontrols/chattutorial/chapter5/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtCore>
@@ -7,9 +7,6 @@
#include <QSqlError>
#include <QtQml>
-#include "sqlcontactmodel.h"
-#include "sqlconversationmodel.h"
-
static void connectToDatabase()
{
QSqlDatabase database = QSqlDatabase::database();
@@ -37,13 +34,10 @@ int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
- qmlRegisterType<SqlContactModel>("io.qt.examples.chattutorial", 1, 0, "SqlContactModel");
- qmlRegisterType<SqlConversationModel>("io.qt.examples.chattutorial", 1, 0, "SqlConversationModel");
-
connectToDatabase();
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter5/main.qml")));
+ engine.loadFromModule("chattutorial", "Main");
if (engine.rootObjects().isEmpty())
return -1;
diff --git a/examples/quickcontrols/chattutorial/chapter5/main.qml b/examples/quickcontrols/chattutorial/chapter5/main.qml
deleted file mode 100644
index da080e3a45..0000000000
--- a/examples/quickcontrols/chattutorial/chapter5/main.qml
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
- id: window
- width: 540
- height: 960
- visible: true
-
- StackView {
- id: stackView
- anchors.fill: parent
- initialItem: ContactPage {}
- }
-}
-
diff --git a/examples/quickcontrols/chattutorial/chapter5/qmldir b/examples/quickcontrols/chattutorial/chapter5/qmldir
new file mode 100644
index 0000000000..4ae7752b55
--- /dev/null
+++ b/examples/quickcontrols/chattutorial/chapter5/qmldir
@@ -0,0 +1,6 @@
+module chattutorial
+ChatToolBar 1.0 ChatToolBar.qml
+ContactPage 1.0 ContactPage.qml
+ConversationPage 1.0 ConversationPage.qml
+Main 1.0 Main.qml
+typeinfo chapter5.qmltypes
diff --git a/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp b/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp
index 0bccc0d7af..189924deec 100644
--- a/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp
+++ b/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "sqlcontactmodel.h"
@@ -37,7 +37,7 @@ SqlContactModel::SqlContactModel(QObject *parent) :
if (!query.exec("SELECT * FROM Contacts"))
qFatal("Contacts SELECT query failed: %s", qPrintable(query.lastError().text()));
- setQuery(query);
+ setQuery(std::move(query));
if (lastError().isValid())
qFatal("Cannot set query on SqlContactModel: %s", qPrintable(lastError().text()));
}
diff --git a/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h b/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h
index a6d24a4774..c7f9a154eb 100644
--- a/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h
+++ b/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h
@@ -1,13 +1,17 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SQLCONTACTMODEL_H
#define SQLCONTACTMODEL_H
+#include <QQmlEngine>
#include <QSqlQueryModel>
class SqlContactModel : public QSqlQueryModel
{
+ Q_OBJECT
+ QML_ELEMENT
+
public:
SqlContactModel(QObject *parent = nullptr);
};
diff --git a/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp b/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp
index 1a1594b094..5be01de52c 100644
--- a/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp
+++ b/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "sqlconversationmodel.h"
diff --git a/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h b/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h
index 8a3cdd4c43..b4917c0eff 100644
--- a/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h
+++ b/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h
@@ -1,14 +1,16 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SQLCONVERSATIONMODEL_H
#define SQLCONVERSATIONMODEL_H
+#include <QQmlEngine>
#include <QSqlTableModel>
class SqlConversationModel : public QSqlTableModel
{
Q_OBJECT
+ QML_ELEMENT
Q_PROPERTY(QString recipient READ recipient WRITE setRecipient NOTIFY recipientChanged)
public:
diff --git a/examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc b/examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc
index c34c20d508..a42ef81575 100644
--- a/examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc
+++ b/examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
-\example chattutorial
+\page qtquickcontrols-chattutorial-example.html
\keyword Qt Quick Controls - Chat Tutorial
\title Qt Quick Controls - Chat Tutorial
\keyword Qt Quick Controls 2 - Chat Tutorial
@@ -21,16 +21,12 @@ When setting up a new project, it's easiest to use
basic "Hello World" application with the following files:
\list
-\li \c MainForm.ui.qml - Defines the default UI
-\li \c main.qml - Embeds the default UI in a Window
-\li \c qml.qrc - Lists the \c .qml files that are built into the binary
+\li \c CMakeLists.txt - Instructs CMake how our project should be built
+\li \c Main.qml - Provides a default UI containing an empty Window
\li \c main.cpp - Loads \c main.qml
-\li \c chattutorial.pro - Provides the qmake configuration
+\li \c qtquickcontrols2.conf - Tells the application which style it should use
\endlist
-\note Delete the \c MainForm.ui.qml and \c qml.qrc files from the project, as
-we will not use them in this tutorial.
-
\section2 main.cpp
The default code in \c main.cpp has two includes:
@@ -46,42 +42,35 @@ does. QCoreApplication is sufficient for non-graphical applications.
QGuiApplication is sufficient for graphical applications that do not use
\l {Qt Widgets}, while QApplication is required for those that do.
-The second include makes QQmlApplicationEngine available, along with
-some useful functions required for making C++ types accessible from QML.
+The second include makes QQmlApplicationEngine available, allowing us to
+load our QML.
Within \c main(), we set up the application object and QML engine:
\skipto main
\printuntil }
-It begins with enabling \l {High DPI}{high DPI scaling}, which is not
-part of the default code. It is necessary to do so before the application
-object is constructed.
-
-After that's done, we construct the application object, passing any application
-arguments provided by the user.
-
-Next, the QML engine is created. \l QQmlApplicationEngine is a convenient
-wrapper over QQmlEngine, providing the \l {QQmlApplicationEngine::load}{load()}
-function to easily load QML for an application. It also adds some convenience
-for using \l {Using File Selectors with Qt Quick Controls}{file selectors}.
+\l QQmlApplicationEngine is a convenient wrapper over QQmlEngine, providing the
+\l {QQmlApplicationEngine::}{loadFromModule} function to easily load QML for an
+application. It also adds some convenience for using \l {Using File Selectors
+with Qt Quick Controls}{file selectors}.
Once we've set up things in C++, we can move on to the user interface in QML.
-\section2 main.qml
+\section2 Main.qml
Let's modify the default QML code to suit our needs.
-\quotefromfile chattutorial/chapter1/main.qml
+\quotefromfile chattutorial/chapter1/Main.qml
\skipto import
\printuntil import QtQuick.Controls
-First, import the \l {Qt Quick} module. This gives us
-access to graphical primitives such as \l Item, \l Rectangle, \l Text, and so
-on.
-For the full list of types, see the \l {Qt Quick QML Types} documentation.
+You'll notice that the \l {Qt Quick} module has already been imported. This
+gives us access to graphical primitives such as \l Item, \l Rectangle, \l Text,
+and so on. For the full list of types, see the \l {Qt Quick QML Types}
+documentation.
-Next, import the Qt Quick Controls module. Amongst other things, this
+Add an import of the Qt Quick Controls module. Amongst other things, this
provides access to \l ApplicationWindow, which will replace the existing
root type, \c Window:
@@ -90,6 +79,7 @@ root type, \c Window:
\dots
\skipto }
\skipuntil }
+\skipuntil }
\printuntil }
ApplicationWindow is a \l Window with some added convenience for creating a
@@ -121,14 +111,13 @@ which screen the user is viewing, it is much easier to use \l Page.
For now, we'll just add one page, but in the next chapter, we'll demonstrate
how to navigate between several pages.
-\quotefromfile chattutorial/chapter1/main.qml
+\quotefromfile chattutorial/chapter1/Main.qml
\skipto Page
\printuntil }
\printuntil }
-We replace the default \c{MainForm {...}} code block with a Page, which is
-sized to occupy all the space on the window using the \l {Item::}{anchors.fill}
-property.
+First, we add a Page, which is sized to occupy all the space on the window
+using the \l {Item::}{anchors.fill} property.
Then, we assign a \l Label to its \l {Page::}{header} property. Label extends
the primitive \l Text item from the Qt Quick module by adding
@@ -155,49 +144,15 @@ bounds, both horizontally and vertically.
\section2 The Project File
-The \c .pro or \l {Creating Project Files}{project} file contains all of the
-information needed by \l {qmake Manual}{qmake} to generate a Makefile, which is
-then used to compile and link the application.
-
-\quotefromfile chattutorial/chapter1/chapter1.pro
-\printline TEMPLATE
-
-The first line tells \c qmake which kind of project this is. We're building an
-application, so we use the \c app template.
-
-\printline QT
-
-The next line declares the Qt libraries that we want to use from C++.
+The \c CMakeLists.txt file contains all of the information needed by \l {Build
+with CMake}{CMake} to build our project into an executable that we can run.
-\printline CONFIG
+For an in-depth explanation of this file, see \l {Building a QML application}.
-This line states that a C++11 compatible compiler is required to build the
-project.
-
-\printline SOURCES
-
-The \c SOURCES variable lists all of the source files that should be compiled.
-A similar variable, \c HEADERS, is available for header files.
-
-\printuntil qtquickcontrols2
-
-The next line tells \c qmake that we have a collection of
-\l {The Qt Resource System}{resources} that should be built into the
-executable.
-
-\skipto target
-\printline
-
-This line replaces deployment settings that come with the default project file.
-It determines where the example is copied, on running "\c{make install}".
-
-Now we can build and run the application:
+Here is what our application currently looks like when run:
\borderedimage qtquickcontrols-chattutorial-chapter1.png
-\noautolist
-\generatelist examplefiles .*chapter1.*
-
\section1 Chapter 2: Lists
In this chapter, we'll explain how to create a list of interactive items using
@@ -211,7 +166,7 @@ can display text, be checked on and off, and react to mouse clicks.
Here is our ListView:
-\quotefromfile chattutorial/chapter2/main.qml
+\quotefromfile chattutorial/chapter2/Main.qml
\dots 8
\codeline
\skipto ListView
@@ -272,15 +227,13 @@ vertical space.
\borderedimage qtquickcontrols-chattutorial-chapter2.png
-\generatelist examplefiles .*(chapter2|shared).*
-\generatelist exampleimages .*shared.*(Einstein|Hemingway|Gude)\.png
\section1 Chapter 3: Navigation
In this chapter, you'll learn how to use \l StackView to navigate between pages
in an application. Here's the revised \c main.qml:
-\quotefromfile chattutorial/chapter3/main.qml
+\quotefromfile chattutorial/chapter3/Main.qml
\skipto import
\printuntil }
\printuntil }
@@ -499,8 +452,6 @@ Finally, the button is only enabled when there is actually a message to send.
\borderedimage qtquickcontrols-chattutorial-chapter3.gif
-\generatelist examplefiles .*(chapter3|shared).*
-\generatelist exampleimages .*shared.*(Einstein|Hemingway|Gude)\.png
\section1 Chapter 4: Models
@@ -644,8 +595,8 @@ changes to be done to \c ContactPage.qml. To be able to use the types,
we must first import them using the URI we set in \c main.cpp:
\quotefromfile chattutorial/chapter4/ContactPage.qml
-\skipto import io.qt.examples.chattutorial
-\printline import io.qt.examples.chattutorial
+\skipto import chattutorial
+\printline import chattutorial
We then replace the dummy model with the proper one:
@@ -718,8 +669,6 @@ text field to make way for future input.
\borderedimage qtquickcontrols-chattutorial-chapter4.gif
-\generatelist examplefiles .*(chapter4|shared).*
-\generatelist exampleimages
\section1 Chapter 5: Styling
@@ -848,8 +797,6 @@ After building and running the application, you should see these results:
\borderedimage qtquickcontrols-chattutorial-chapter5-contacts-universal-dark.png
\borderedimage qtquickcontrols-chattutorial-chapter5-conversations-universal-dark.png
-\generatelist examplefiles .*(chapter5|shared).*
-\generatelist exampleimages
\section1 Summary
diff --git a/examples/quickcontrols/contactlist/CMakeLists.txt b/examples/quickcontrols/contactlist/CMakeLists.txt
index d353e3374f..7de83d98c3 100644
--- a/examples/quickcontrols/contactlist/CMakeLists.txt
+++ b/examples/quickcontrols/contactlist/CMakeLists.txt
@@ -1,19 +1,14 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(contactlist LANGUAGES CXX)
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/contactlist")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick)
+qt_standard_project_setup(REQUIRES 6.6)
+
qt_add_executable(contactlistexample WIN32 MACOSX_BUNDLE
contactmodel.cpp contactmodel.h
main.cpp
@@ -21,25 +16,33 @@ qt_add_executable(contactlistexample WIN32 MACOSX_BUNDLE
qt_add_qml_module(contactlistexample
URI contactlist
- NO_RESOURCE_TARGET_PATH
QML_FILES
"ContactDelegate.ui.qml"
"ContactDialog.qml"
"ContactForm.ui.qml"
"ContactView.ui.qml"
"SectionDelegate.ui.qml"
- "contactlist.qml"
+ "ContactList.qml"
"designer/Backend/ContactModel.qml"
)
target_link_libraries(contactlistexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Quick
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
)
install(TARGETS contactlistexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET contactlistexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/contactlist/ContactDelegate.ui.qml b/examples/quickcontrols/contactlist/ContactDelegate.ui.qml
index 3591b2f3fd..e1e6127bb5 100644
--- a/examples/quickcontrols/contactlist/ContactDelegate.ui.qml
+++ b/examples/quickcontrols/contactlist/ContactDelegate.ui.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
@@ -7,14 +7,18 @@ import QtQuick.Controls
ItemDelegate {
id: delegate
-
checkable: true
+ required property string fullName
+ required property string address
+ required property string city
+ required property string number
+
contentItem: ColumnLayout {
spacing: 10
Label {
- text: fullName
+ text: delegate.fullName
font.bold: true
elide: Text.ElideRight
Layout.fillWidth: true
@@ -34,7 +38,7 @@ ItemDelegate {
}
Label {
- text: address
+ text: delegate.address
font.bold: true
elide: Text.ElideRight
Layout.fillWidth: true
@@ -46,7 +50,7 @@ ItemDelegate {
}
Label {
- text: city
+ text: delegate.city
font.bold: true
elide: Text.ElideRight
Layout.fillWidth: true
@@ -58,7 +62,7 @@ ItemDelegate {
}
Label {
- text: number
+ text: delegate.number
font.bold: true
elide: Text.ElideRight
Layout.fillWidth: true
@@ -74,6 +78,7 @@ ItemDelegate {
PropertyChanges {
// TODO: When Qt Design Studio supports generalized grouped properties, change to:
// grid.visible: true
+ // qmllint disable Quick.property-changes-parsed
target: grid
visible: true
}
diff --git a/examples/quickcontrols/contactlist/ContactDialog.qml b/examples/quickcontrols/contactlist/ContactDialog.qml
index 9bb990f2ad..3f287447ef 100644
--- a/examples/quickcontrols/contactlist/ContactDialog.qml
+++ b/examples/quickcontrols/contactlist/ContactDialog.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
@@ -41,5 +41,9 @@ Dialog {
id: form
}
- onAccepted: finished(form.fullName.text, form.address.text, form.city.text, form.number.text)
+ onAccepted: {
+ if (form.fullName.text && form.address.text && form.city.text && form.number.text) {
+ finished(form.fullName.text, form.address.text, form.city.text, form.number.text);
+ }
+ }
}
diff --git a/examples/quickcontrols/contactlist/ContactForm.ui.qml b/examples/quickcontrols/contactlist/ContactForm.ui.qml
index 918da57f30..56c9186194 100644
--- a/examples/quickcontrols/contactlist/ContactForm.ui.qml
+++ b/examples/quickcontrols/contactlist/ContactForm.ui.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
diff --git a/examples/quickcontrols/contactlist/ContactList.qml b/examples/quickcontrols/contactlist/ContactList.qml
new file mode 100644
index 0000000000..121b38f352
--- /dev/null
+++ b/examples/quickcontrols/contactlist/ContactList.qml
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+
+ property int currentContact: -1
+
+ width: 320
+ height: 480
+ visible: true
+ title: qsTr("Contact List")
+
+ ContactDialog {
+ id: contactDialog
+ onFinished: function(fullName, address, city, number) {
+ if (window.currentContact === -1)
+ contactView.model.append(fullName, address, city, number)
+ else
+ contactView.model.set(window.currentContact, fullName, address, city, number)
+ }
+ }
+
+ Menu {
+ id: contactMenu
+ x: parent.width / 2 - width / 2
+ y: parent.height / 2 - height / 2
+ modal: true
+
+ Label {
+ padding: 10
+ font.bold: true
+ width: parent.width
+ horizontalAlignment: Qt.AlignHCenter
+ text: window.currentContact >= 0 ? contactView.model.get(window.currentContact).fullName : ""
+ }
+ MenuItem {
+ text: qsTr("Edit...")
+ onTriggered: contactDialog.editContact(contactView.model.get(window.currentContact))
+ }
+ MenuItem {
+ text: qsTr("Remove")
+ onTriggered: contactView.model.remove(window.currentContact)
+ }
+ }
+
+ ContactView {
+ id: contactView
+ anchors.fill: parent
+ onPressAndHold: function(index) {
+ window.currentContact = index
+ contactMenu.open()
+ }
+ }
+
+ RoundButton {
+ text: qsTr("+")
+ highlighted: true
+ anchors.margins: 10
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ onClicked: {
+ window.currentContact = -1
+ contactDialog.createContact()
+ }
+ }
+}
diff --git a/examples/quickcontrols/contactlist/ContactView.ui.qml b/examples/quickcontrols/contactlist/ContactView.ui.qml
index 6e80d05bac..7d5b3cf6c8 100644
--- a/examples/quickcontrols/contactlist/ContactView.ui.qml
+++ b/examples/quickcontrols/contactlist/ContactView.ui.qml
@@ -1,9 +1,11 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls
-import Backend
+import contactlist
ListView {
id: listView
@@ -25,6 +27,9 @@ ListView {
delegate: ContactDelegate {
id: delegate
width: listView.width
+
+ required property int index
+
onPressAndHold: listView.pressAndHold(index)
}
diff --git a/examples/quickcontrols/contactlist/SectionDelegate.ui.qml b/examples/quickcontrols/contactlist/SectionDelegate.ui.qml
index 05b2147288..1ed587abd7 100644
--- a/examples/quickcontrols/contactlist/SectionDelegate.ui.qml
+++ b/examples/quickcontrols/contactlist/SectionDelegate.ui.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
@@ -7,9 +7,11 @@ import QtQuick.Controls
ToolBar {
id: background
+ required property string section
+
Label {
id: label
- text: section
+ text: background.section
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
diff --git a/examples/quickcontrols/contactlist/contactlist.pro b/examples/quickcontrols/contactlist/contactlist.pro
index ceb99cbc47..2fead83186 100644
--- a/examples/quickcontrols/contactlist/contactlist.pro
+++ b/examples/quickcontrols/contactlist/contactlist.pro
@@ -2,6 +2,12 @@ TEMPLATE = app
TARGET = contactlist
QT += quick
+CONFIG += qmltypes
+
+QML_IMPORT_PATH = $$pwd/.
+QML_IMPORT_NAME = contactlist
+QML_IMPORT_MAJOR_VERSION = 1
+
HEADERS += \
contactmodel.h
@@ -9,15 +15,20 @@ SOURCES += \
main.cpp \
contactmodel.cpp
-RESOURCES += \
+qml_resources.files = \
+ qmldir \
ContactDelegate.ui.qml \
ContactDialog.qml \
ContactForm.ui.qml \
- contactlist.qml \
+ ContactList.qml \
ContactView.ui.qml \
designer/Backend/ContactModel.qml \
SectionDelegate.ui.qml
+qml_resources.prefix = /qt/qml/contactlist
+
+RESOURCES += qml_resources
+
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH = $$PWD/designer
diff --git a/examples/quickcontrols/contactlist/contactlist.qml b/examples/quickcontrols/contactlist/contactlist.qml
deleted file mode 100644
index 28f887f2db..0000000000
--- a/examples/quickcontrols/contactlist/contactlist.qml
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
- id: window
-
- property int currentContact: -1
-
- width: 320
- height: 480
- visible: true
- title: qsTr("Contact List")
-
- ContactDialog {
- id: contactDialog
- onFinished: function(fullName, address, city, number) {
- if (currentContact === -1)
- contactView.model.append(fullName, address, city, number)
- else
- contactView.model.set(currentContact, fullName, address, city, number)
- }
- }
-
- Menu {
- id: contactMenu
- x: parent.width / 2 - width / 2
- y: parent.height / 2 - height / 2
- modal: true
-
- Label {
- padding: 10
- font.bold: true
- width: parent.width
- horizontalAlignment: Qt.AlignHCenter
- text: currentContact >= 0 ? contactView.model.get(currentContact).fullName : ""
- }
- MenuItem {
- text: qsTr("Edit...")
- onTriggered: contactDialog.editContact(contactView.model.get(currentContact))
- }
- MenuItem {
- text: qsTr("Remove")
- onTriggered: contactView.model.remove(currentContact)
- }
- }
-
- ContactView {
- id: contactView
- anchors.fill: parent
- onPressAndHold: {
- currentContact = index
- contactMenu.open()
- }
- }
-
- RoundButton {
- text: qsTr("+")
- highlighted: true
- anchors.margins: 10
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- onClicked: {
- currentContact = -1
- contactDialog.createContact()
- }
- }
-}
diff --git a/examples/quickcontrols/contactlist/contactmodel.cpp b/examples/quickcontrols/contactlist/contactmodel.cpp
index 1add2e7776..85ebad0515 100644
--- a/examples/quickcontrols/contactlist/contactmodel.cpp
+++ b/examples/quickcontrols/contactlist/contactmodel.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "contactmodel.h"
diff --git a/examples/quickcontrols/contactlist/contactmodel.h b/examples/quickcontrols/contactlist/contactmodel.h
index 564ec0fab3..78a06b8132 100644
--- a/examples/quickcontrols/contactlist/contactmodel.h
+++ b/examples/quickcontrols/contactlist/contactmodel.h
@@ -1,14 +1,16 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CONTACTMODEL_H
#define CONTACTMODEL_H
#include <QAbstractListModel>
+#include <QQmlEngine>
class ContactModel : public QAbstractListModel
{
Q_OBJECT
+ QML_ELEMENT
public:
enum ContactRole {
diff --git a/examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml b/examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml
index c7a3f4817b..850b444d08 100644
--- a/examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml
+++ b/examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
diff --git a/examples/quickcontrols/contactlist/doc/src/qtquickcontrols-contactlist.qdoc b/examples/quickcontrols/contactlist/doc/src/qtquickcontrols-contactlist.qdoc
index 7decce2e05..bb9e935d6d 100644
--- a/examples/quickcontrols/contactlist/doc/src/qtquickcontrols-contactlist.qdoc
+++ b/examples/quickcontrols/contactlist/doc/src/qtquickcontrols-contactlist.qdoc
@@ -6,6 +6,8 @@
\title Qt Quick Controls - Contact List
\keyword Qt Quick Controls 2 - Contact List
\ingroup qtquickcontrols-examples
+ \examplecategory {User Interface Components}
+ \examplecategory {Mobile}
\brief A QML app using Qt Quick Controls and a C++ class that implements
a simple contact list.
diff --git a/examples/quickcontrols/contactlist/main.cpp b/examples/quickcontrols/contactlist/main.cpp
index 32a88def4c..2f9f15fd36 100644
--- a/examples/quickcontrols/contactlist/main.cpp
+++ b/examples/quickcontrols/contactlist/main.cpp
@@ -1,19 +1,15 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
#include <QQmlApplicationEngine>
-#include "contactmodel.h"
-
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
- qmlRegisterType<ContactModel>("Backend", 1, 0, "ContactModel");
-
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/contactlist.qml")));
+ engine.loadFromModule("contactlist", "ContactList");
return app.exec();
}
diff --git a/examples/quickcontrols/contactlist/qmldir b/examples/quickcontrols/contactlist/qmldir
new file mode 100644
index 0000000000..2d0af68242
--- /dev/null
+++ b/examples/quickcontrols/contactlist/qmldir
@@ -0,0 +1,8 @@
+module contactlist
+
+ContactDelegate 1.0 ContactDelegate.ui.qml
+ContactDialog 1.0 ContactDialog.qml
+ContactForm 1.0 ContactForm.ui.qml
+ContactList 1.0 ContactList.qml
+ContactView 1.0 ContactView.ui.qml
+SectionDelegate 1.0 SectionDelegate.ui.qml
diff --git a/examples/quickcontrols/eventcalendar/CMakeLists.txt b/examples/quickcontrols/eventcalendar/CMakeLists.txt
index 92299b1870..43ed385a3e 100644
--- a/examples/quickcontrols/eventcalendar/CMakeLists.txt
+++ b/examples/quickcontrols/eventcalendar/CMakeLists.txt
@@ -1,17 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(eventcalendar LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/eventcalendar")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Sql)
qt_add_executable(eventcalendar
@@ -27,10 +21,10 @@ set_target_properties(eventcalendar PROPERTIES
)
target_link_libraries(eventcalendar PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Qml
- Qt::Sql
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Sql
)
qt_add_qml_module(eventcalendar
@@ -44,7 +38,16 @@ qt_add_qml_module(eventcalendar
)
install(TARGETS eventcalendar
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET eventcalendar
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/eventcalendar/doc/src/qtquickcontrols-eventcalendar.qdoc b/examples/quickcontrols/eventcalendar/doc/src/qtquickcontrols-eventcalendar.qdoc
index 567ad12fa1..450b1d37a8 100644
--- a/examples/quickcontrols/eventcalendar/doc/src/qtquickcontrols-eventcalendar.qdoc
+++ b/examples/quickcontrols/eventcalendar/doc/src/qtquickcontrols-eventcalendar.qdoc
@@ -6,6 +6,7 @@
\keyword Qt Quick Controls - Event Calendar
\title Qt Quick Controls - Event Calendar
\ingroup qtquickcontrols-examples
+ \examplecategory {User Interface Components}
\brief A simple event calendar.
\image qtquickcalendar-eventcalendar.png
diff --git a/examples/quickcontrols/eventcalendar/eventmodel.cpp b/examples/quickcontrols/eventcalendar/eventmodel.cpp
index a380e00d11..0b225883ff 100644
--- a/examples/quickcontrols/eventcalendar/eventmodel.cpp
+++ b/examples/quickcontrols/eventcalendar/eventmodel.cpp
@@ -30,7 +30,7 @@ QDate EventModel::date() const
return m_date;
}
-void EventModel::setDate(const QDate &date)
+void EventModel::setDate(QDate date)
{
if (date == m_date)
return;
diff --git a/examples/quickcontrols/eventcalendar/eventmodel.h b/examples/quickcontrols/eventcalendar/eventmodel.h
index 3dc391fdd7..62a30df33b 100644
--- a/examples/quickcontrols/eventcalendar/eventmodel.h
+++ b/examples/quickcontrols/eventcalendar/eventmodel.h
@@ -34,7 +34,7 @@ public:
void setEventDatabase(SqlEventDatabase *eventDatabase);
QDate date() const;
- void setDate(const QDate &date);
+ void setDate(QDate date);
int rowCount(const QModelIndex & = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
diff --git a/examples/quickcontrols/eventcalendar/sqleventdatabase.cpp b/examples/quickcontrols/eventcalendar/sqleventdatabase.cpp
index e958448695..3d845f3b3b 100644
--- a/examples/quickcontrols/eventcalendar/sqleventdatabase.cpp
+++ b/examples/quickcontrols/eventcalendar/sqleventdatabase.cpp
@@ -13,7 +13,7 @@ SqlEventDatabase::SqlEventDatabase()
createConnection();
}
-QVector<Event> SqlEventDatabase::eventsForDate(const QDate &date)
+QVector<Event> SqlEventDatabase::eventsForDate(QDate date)
{
const QString queryStr = QString::fromLatin1("SELECT * FROM Event WHERE '%1' >= startDate AND '%1' <= endDate").arg(date.toString("yyyy-MM-dd"));
QSqlQuery query(queryStr);
diff --git a/examples/quickcontrols/eventcalendar/sqleventdatabase.h b/examples/quickcontrols/eventcalendar/sqleventdatabase.h
index aa59bfb6b1..b867b7860d 100644
--- a/examples/quickcontrols/eventcalendar/sqleventdatabase.h
+++ b/examples/quickcontrols/eventcalendar/sqleventdatabase.h
@@ -19,7 +19,7 @@ class SqlEventDatabase : public QObject
public:
SqlEventDatabase();
- QVector<Event> eventsForDate(const QDate &date);
+ QVector<Event> eventsForDate(QDate date);
private:
static void createConnection();
diff --git a/examples/quickcontrols/filesystemexplorer/CMakeLists.txt b/examples/quickcontrols/filesystemexplorer/CMakeLists.txt
new file mode 100644
index 0000000000..26427a6ed1
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/CMakeLists.txt
@@ -0,0 +1,84 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(filesystemexplorer LANGUAGES CXX)
+
+if (ANDROID OR IOS)
+ message(FATAL_ERROR "Platform is not supported")
+ return()
+endif ()
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2 Svg)
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(filesystemexplorer
+ main.cpp
+)
+
+set_target_properties(filesystemexplorer
+ PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+set_source_files_properties(qml/Colors.qml
+ PROPERTIES
+ QT_QML_SINGLETON_TYPE TRUE
+)
+
+qt_add_qml_module(filesystemexplorer
+ URI FileSystemModule
+ VERSION 1.0
+ QML_FILES
+ "Main.qml"
+ "qml/About.qml"
+ "qml/Colors.qml"
+ "qml/Editor.qml"
+ "qml/FileSystemView.qml"
+ "qml/MyMenu.qml"
+ "qml/MyMenuBar.qml"
+ "qml/Sidebar.qml"
+ "qml/ResizeButton.qml"
+ "qml/WindowDragHandler.qml"
+ RESOURCES
+ "icons/folder_closed.svg"
+ "icons/folder_open.svg"
+ "icons/generic_file.svg"
+ "icons/globe.svg"
+ "icons/info_sign.svg"
+ "icons/light_bulb.svg"
+ "icons/read.svg"
+ "icons/resize.svg"
+ "icons/qt_logo.svg"
+ "icons/app_icon.svg"
+ SOURCES
+ filesystemmodel.cpp
+ filesystemmodel.h
+ linenumbermodel.cpp
+ linenumbermodel.h
+)
+
+target_link_libraries(filesystemexplorer
+ PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
+ Qt6::Svg
+)
+
+install(TARGETS filesystemexplorer
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET filesystemexplorer
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/filesystemexplorer/Main.qml b/examples/quickcontrols/filesystemexplorer/Main.qml
new file mode 100644
index 0000000000..4d9ce7f2da
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/Main.qml
@@ -0,0 +1,184 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+import QtQuick.Controls.Basic
+import QtQuick.Layouts
+import FileSystemModule
+
+pragma ComponentBehavior: Bound
+
+ApplicationWindow {
+ id: root
+
+ property bool expandPath: false
+ property bool showLineNumbers: true
+ property string currentFilePath: ""
+
+ width: 1100
+ height: 600
+ minimumWidth: 200
+ minimumHeight: 100
+ visible: true
+ color: Colors.background
+ flags: Qt.Window | Qt.FramelessWindowHint
+ title: qsTr("File System Explorer Example")
+
+ function getInfoText() : string {
+ let out = root.currentFilePath
+ if (!out)
+ return qsTr("File System Explorer")
+ return root.expandPath ? out : out.substring(out.lastIndexOf("/") + 1, out.length)
+ }
+
+ menuBar: MyMenuBar {
+ dragWindow: root
+ infoText: root.getInfoText()
+ MyMenu {
+ title: qsTr("File")
+
+ Action {
+ text: qsTr("Increase Font")
+ shortcut: StandardKey.ZoomIn
+ onTriggered: editor.text.font.pixelSize += 1
+ }
+ Action {
+ text: qsTr("Decrease Font")
+ shortcut: StandardKey.ZoomOut
+ onTriggered: editor.text.font.pixelSize -= 1
+ }
+ Action {
+ text: root.showLineNumbers ? qsTr("Toggle Line Numbers OFF")
+ : qsTr("Toggle Line Numbers ON")
+ shortcut: "Ctrl+L"
+ onTriggered: root.showLineNumbers = !root.showLineNumbers
+ }
+ Action {
+ text: root.expandPath ? qsTr("Toggle Short Path")
+ : qsTr("Toggle Expand Path")
+ enabled: root.currentFilePath
+ onTriggered: root.expandPath = !root.expandPath
+ }
+ Action {
+ text: qsTr("Reset Filesystem")
+ enabled: sidebar.currentTabIndex === 1
+ onTriggered: fileSystemView.rootIndex = undefined
+ }
+ Action {
+ text: qsTr("Exit")
+ onTriggered: Qt.exit(0)
+ shortcut: StandardKey.Quit
+ }
+ }
+
+ MyMenu {
+ title: qsTr("Edit")
+
+ Action {
+ text: qsTr("Cut")
+ shortcut: StandardKey.Cut
+ enabled: editor.text.selectedText.length > 0
+ onTriggered: editor.text.cut()
+ }
+ Action {
+ text: qsTr("Copy")
+ shortcut: StandardKey.Copy
+ enabled: editor.text.selectedText.length > 0
+ onTriggered: editor.text.copy()
+ }
+ Action {
+ text: qsTr("Paste")
+ shortcut: StandardKey.Paste
+ enabled: editor.text.canPaste
+ onTriggered: editor.text.paste()
+ }
+ Action {
+ text: qsTr("Select All")
+ shortcut: StandardKey.SelectAll
+ enabled: editor.text.length > 0
+ onTriggered: editor.text.selectAll()
+ }
+ Action {
+ text: qsTr("Undo")
+ shortcut: StandardKey.Undo
+ enabled: editor.text.canUndo
+ onTriggered: editor.text.undo()
+ }
+ }
+ }
+ // Set up the layout of the main components in a row:
+ // [ Sidebar, Navigation, Editor ]
+ RowLayout {
+ anchors.fill: parent
+ spacing: 0
+
+ // Stores the buttons that navigate the application.
+ Sidebar {
+ id: sidebar
+ dragWindow: root
+ Layout.preferredWidth: 50
+ Layout.fillHeight: true
+ }
+
+ // Allows resizing parts of the UI.
+ SplitView {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ // Customized handle to drag between the Navigation and the Editor.
+ handle: Rectangle {
+ implicitWidth: 10
+ color: SplitHandle.pressed ? Colors.color2 : Colors.background
+ border.color: SplitHandle.hovered ? Colors.color2 : Colors.background
+ opacity: SplitHandle.hovered || navigationView.width < 15 ? 1.0 : 0.0
+
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 1400
+ }
+ }
+ }
+
+ Rectangle {
+ id: navigationView
+ color: Colors.surface1
+ SplitView.preferredWidth: 250
+ SplitView.fillHeight: true
+ // The stack-layout provides different views, based on the
+ // selected buttons inside the sidebar.
+ StackLayout {
+ anchors.fill: parent
+ currentIndex: sidebar.currentTabIndex
+
+ // Shows the help text.
+ Text {
+ text: qsTr("This example shows how to use and visualize the file system.\n\n"
+ + "Customized Qt Quick Components have been used to achieve this look.\n\n"
+ + "You can edit the files but they won't be changed on the file system.\n\n"
+ + "Click on the folder icon to the left to get started.")
+ wrapMode: TextArea.Wrap
+ color: Colors.text
+ }
+
+ // Shows the files on the file system.
+ FileSystemView {
+ id: fileSystemView
+ color: Colors.surface1
+ onFileClicked: path => root.currentFilePath = path
+ }
+ }
+ }
+
+ // The main view that contains the editor.
+ Editor {
+ id: editor
+ showLineNumbers: root.showLineNumbers
+ currentFilePath: root.currentFilePath
+ SplitView.fillWidth: true
+ SplitView.fillHeight: true
+ }
+ }
+ }
+
+ ResizeButton {
+ resizeWindow: root
+ }
+}
diff --git a/examples/quickcontrols/filesystemexplorer/doc/images/qtquickcontrols-filesystemexplorer.webp b/examples/quickcontrols/filesystemexplorer/doc/images/qtquickcontrols-filesystemexplorer.webp
new file mode 100644
index 0000000000..10ad0d26e7
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/doc/images/qtquickcontrols-filesystemexplorer.webp
Binary files differ
diff --git a/examples/quickcontrols/filesystemexplorer/doc/src/qtquickcontrols-filesystemexplorer.qdoc b/examples/quickcontrols/filesystemexplorer/doc/src/qtquickcontrols-filesystemexplorer.qdoc
new file mode 100644
index 0000000000..686c2ab48a
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/doc/src/qtquickcontrols-filesystemexplorer.qdoc
@@ -0,0 +1,123 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+ \example filesystemexplorer
+ \examplecategory {Application Examples}
+ \meta tags {quickcontrols, layout, styling, treeview}
+ \title File System Explorer
+ \ingroup qtquickcontrols-examples
+ \brief A desktop QML app utilizing customized Qt Quick Controls to display
+ text files from a filesystem.
+
+ In this example, a modern layout is used that consists of three major
+ components. There is an icon-based \e {Sidebar} to the left, followed by a
+ resizable TreeView displaying the file system from a QFileSystemModel, and
+ finally the TextArea displaying the selected text files. There is a common
+ look and feel across all operating systems. We accomplish this by using
+ customized quick controls and frameless windows, with our own window
+ decorations. When launching this application from the command-line, you
+ have the option to provide an initial directory as a parameter. This
+ initial directory will be used by the TreeView to set the starting point
+ for displaying the directory structure.
+
+ \image qtquickcontrols-filesystemexplorer.webp
+
+ \include examples-run.qdocinc
+
+ \section1 Modern layout and structure
+
+ To begin with, we are providing the colors throughout a singleton QML
+ object. In this way, we can provide more structured control over the
+ appearance of the application.
+
+ \quotefromfile filesystemexplorer/qml/Colors.qml
+ \skipto pragma
+ \printuntil }
+
+ Since we do not want to rely on the operating system's window decoration
+ and instead want to provide our own, we use the \c FramelessWindowHint flag
+ inside the ApplicationWindow. In order to achieve an equivalent interaction
+ with the window, we override the \c contentItem property of our customized
+ MenuBar and display some information text as well as interaction
+ possibilities for dragging or closing the application. \l {Inline
+ Components} have been used to simplify this process.
+
+ \quotefromfile filesystemexplorer/qml/MyMenuBar.qml
+ \skipto component InteractionButton
+ \printuntil id: maximize
+ \dots
+
+ The \e {Sidebar} on the left includes checkable navigation buttons on top
+ and one-shot buttons on the bottom. A ButtonGroup and a Container are used
+ to ensure that only one entry is active at any given time. It is then
+ possible to provide different views using a property alias for the current
+ position, along with a StackLayout.
+
+ This technique allows us to simply extend the functionality by adding
+ another button and the corresponding element inside the StackLayout.
+
+ \quotefromfile filesystemexplorer/Main.qml
+ \skipto StackLayout {
+ \printuntil /^\s{16}\}$/
+
+ The StackLayout includes, besides some information text, the \e
+ {FileSystemView}. This custom component displays files and folders and
+ populates it with data from a \l {Using C++ Models with Qt Quick Views}{C++
+ model}. We can then select the files and read them accordingly.
+
+ \quotefromfile filesystemexplorer/filesystemmodel.cpp
+ \skipto readFile
+ \printuntil /^\s{0}\}$/
+
+ By right-clicking on a folder in the TreeView, a popup Menu is opened,
+ which allows control over the \c rootIndex property of the TreeView.
+
+ \quotefromfile filesystemexplorer/qml/FileSystemView.qml
+ \skipto MyMenu
+ \printuntil /^\s{8}\}$/
+
+ By using a SplitView, we are able to dynamically share the space between
+ the StackLayout and the Editor. Our Editor contains the TextArea that
+ displays the opened file and provides us with all the functionality needed
+ to edit text files. Additionally, we provide a visualization of the line
+ numbers, which can be toggled on and off in the Menu.
+
+ \quotefromfile filesystemexplorer/Main.qml
+ \skipto Editor {
+ \printuntil /^\s{12}\}$/
+
+ \section1 Custom components
+
+ For a better understanding of the customization process, investigate
+ \l {Customizing a Control} {this} article first. We are using reusable and
+ customized components throughout this example.
+
+ For instance, the \e {MyMenu} component customizes Menu's \c background
+ property as well as its delegates' \c contentItem and \c background
+ properties.
+
+ \quotefile filesystemexplorer/qml/MyMenu.qml
+
+ Another example is the customization of the ScrollIndicator inside the \e
+ {FileSystemView}, which additionally uses customized animations. Here we
+ also override the \c contentItem.
+
+ \quotefromfile filesystemexplorer/qml/FileSystemView.qml
+ \skipto ScrollIndicator.vertical
+ \printuntil /^\s{8}\}$/
+
+ \section1 Python version
+
+ If you're interested in the Python version of this example, you can find it
+ \l{https://doc.qt.io/qtforpython-6/examples/example_quickcontrols_filesystemexplorer.html}
+ {here}. This showcases the usage of Qt for Python and demonstrates how it
+ can be used to create the same application.
+
+ Additionally, there is a detailed
+ \l {https://doc.qt.io/qtforpython-6/tutorials/extendedexplorer/extendedexplorer.html}
+ {tutorial} available that provides step-by-step instructions on how to
+ extend this example with additional features. This tutorial can be helpful
+ if you want to explore and learn more about building upon the existing
+ functionality of the filesystem explorer.
+*/
+
diff --git a/examples/quickcontrols/filesystemexplorer/filesystemexplorer.pro b/examples/quickcontrols/filesystemexplorer/filesystemexplorer.pro
new file mode 100644
index 0000000000..371baac41a
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/filesystemexplorer.pro
@@ -0,0 +1,60 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+android|ios {
+ error( "Platform not supported" )
+}
+
+QT += quick
+
+CONFIG += qmltypes
+
+QML_IMPORT_PATH = $$pwd/.
+QML_IMPORT_NAME = FileSystemModule
+QML_IMPORT_MAJOR_VERSION = 1
+
+TARGET = filesystemexplorer
+TEMPLATE = app
+
+SOURCES += \
+ main.cpp \
+ filesystemmodel.cpp \
+ linenumbermodel.cpp \
+
+HEADERS += \
+ filesystemmodel.h \
+ linenumbermodel.h \
+
+qml_resources.files = \
+ qmldir \
+ Main.qml \
+ qml/About.qml \
+ qml/Colors.qml \
+ qml/Editor.qml \
+ qml/MyMenu.qml \
+ qml/Sidebar.qml \
+ qml/MyMenuBar.qml \
+ qml/ResizeButton.qml \
+ qml/FileSystemView.qml \
+ qml/WindowDragHandler.qml \
+
+qml_resources.prefix = /qt/qml/FileSystemModule
+
+theme_resources.files = \
+ icons/folder_closed.svg \
+ icons/folder_open.svg \
+ icons/generic_file.svg \
+ icons/globe.svg \
+ icons/info_sign.svg \
+ icons/light_bulb.svg \
+ icons/read.svg \
+ icons/resize.svg \
+ icons/qt_logo.svg \
+ icons/app_icon.svg
+
+theme_resources.prefix = /qt/qml/FileSystemModule
+
+RESOURCES += qml_resources theme_resources
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quickcontrols/filesystemexplorer
+INSTALLS += target
diff --git a/examples/quickcontrols/filesystemexplorer/filesystemmodel.cpp b/examples/quickcontrols/filesystemexplorer/filesystemmodel.cpp
new file mode 100644
index 0000000000..b580bced05
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/filesystemmodel.cpp
@@ -0,0 +1,89 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "filesystemmodel.h"
+
+#include <QStandardPaths>
+#include <QMimeDatabase>
+#include <QTextDocument>
+#include <QTextObject>
+
+FileSystemModel::FileSystemModel(QObject *parent) : QFileSystemModel(parent)
+{
+ setFilter(QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot);
+ setInitialDirectory();
+}
+
+QString FileSystemModel::readFile(const QString &filePath)
+{
+ // Don't issue errors for an empty path, as the initial binding
+ // will result in an empty path, and that's OK.
+ if (filePath.isEmpty())
+ return {};
+
+ QFile file(filePath);
+
+ if (file.size() >= 2'000'000)
+ return tr("File size is too big.\nYou can read files up to %1 MB.").arg(2);
+
+ static const QMimeDatabase db;
+ const QMimeType mime = db.mimeTypeForFile(QFileInfo(file));
+
+ // Check if the mimetype is supported and return the content.
+ const auto mimeTypesForFile = mime.parentMimeTypes();
+ for (const auto &m : mimeTypesForFile) {
+ if (m.contains("text", Qt::CaseInsensitive)
+ || mime.comment().contains("text", Qt::CaseInsensitive)) {
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ return tr("Error opening the File!");
+
+ QTextStream stream(&file);
+ return stream.readAll();
+ }
+ }
+ return tr("Filetype not supported!");
+}
+
+// This function gets called from Editor.qml
+int FileSystemModel::currentLineNumber(QQuickTextDocument *textDocument, int cursorPosition)
+{
+ if (QTextDocument *td = textDocument->textDocument()) {
+ QTextBlock tb = td->findBlock(cursorPosition);
+ return tb.blockNumber();
+ }
+ return -1;
+}
+
+int FileSystemModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 1;
+}
+
+QModelIndex FileSystemModel::rootIndex() const
+{
+ return m_rootIndex;
+}
+
+void FileSystemModel::setRootIndex(const QModelIndex index)
+{
+ if (index == m_rootIndex)
+ return;
+ m_rootIndex = index;
+ emit rootIndexChanged();
+}
+
+void FileSystemModel::setInitialDirectory(const QString &path)
+{
+ QDir dir(path);
+ if (dir.makeAbsolute())
+ setRootPath(dir.path());
+ else
+ setRootPath(getDefaultRootDir());
+ setRootIndex(QFileSystemModel::index(dir.path(), 0));
+}
+
+QString FileSystemModel::getDefaultRootDir()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
+}
diff --git a/examples/quickcontrols/filesystemexplorer/filesystemmodel.h b/examples/quickcontrols/filesystemexplorer/filesystemmodel.h
new file mode 100644
index 0000000000..c79af4aa82
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/filesystemmodel.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FILESYSTEMMODEL_H
+#define FILESYSTEMMODEL_H
+
+#include <QFileSystemModel>
+#include <QQuickTextDocument>
+
+class FileSystemModel : public QFileSystemModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+ Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged)
+public:
+ explicit FileSystemModel(QObject *parent = nullptr);
+
+ // Functions invokable from QML
+ Q_INVOKABLE QString readFile(const QString &filePath);
+ Q_INVOKABLE int currentLineNumber(QQuickTextDocument *textDocument, int cursorPosition);
+
+ // Overridden functions
+ int columnCount(const QModelIndex &parent) const override;
+
+ // Member functions from here
+ QModelIndex rootIndex() const;
+ void setRootIndex(const QModelIndex index);
+ void setInitialDirectory(const QString &path = getDefaultRootDir());
+
+ static QString getDefaultRootDir();
+
+signals:
+ void rootIndexChanged();
+
+private:
+ QModelIndex m_rootIndex;
+};
+
+#endif
diff --git a/examples/quickcontrols/filesystemexplorer/icons/app_icon.svg b/examples/quickcontrols/filesystemexplorer/icons/app_icon.svg
new file mode 100644
index 0000000000..5aae4221f4
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/app_icon.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#EBDBB2" d="M13.25 8.5a.75.75 0 1 1-.75-.75.75.75 0 0 1 .75.75zM9.911 21.35l.816.578C10.819 21.798 13 18.666 13 13h-1a15.503 15.503 0 0 1-2.089 8.35zM4 6.703V10a2.002 2.002 0 0 1-2 2v1a2.002 2.002 0 0 1 2 2v3.297A3.707 3.707 0 0 0 7.703 22H9v-1H7.703A2.706 2.706 0 0 1 5 18.297V15a2.999 2.999 0 0 0-1.344-2.5A2.999 2.999 0 0 0 5 10V6.703A2.706 2.706 0 0 1 7.703 4H9V3H7.703A3.707 3.707 0 0 0 4 6.703zM20 10V6.703A3.707 3.707 0 0 0 16.297 3H15v1h1.297A2.706 2.706 0 0 1 19 6.703V10a2.999 2.999 0 0 0 1.344 2.5A2.999 2.999 0 0 0 19 15v3.297A2.706 2.706 0 0 1 16.297 21H15v1h1.297A3.707 3.707 0 0 0 20 18.297V15a2.002 2.002 0 0 1 2-2v-1a2.002 2.002 0 0 1-2-2z"/><path fill="none" d="M0 0h24v24H0z"/></svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/folder_closed.svg b/examples/quickcontrols/filesystemexplorer/icons/folder_closed.svg
new file mode 100644
index 0000000000..36f119c96f
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/folder_closed.svg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ viewBox="-10 0 1792 1792"
+ id="svg51"
+ sodipodi:docname="folder_closed.svg"
+ width="1792"
+ height="1792"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs55" />
+ <sodipodi:namedview
+ id="namedview53"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.45033482"
+ inkscape:cx="842.70632"
+ inkscape:cy="896"
+ inkscape:window-width="1846"
+ inkscape:window-height="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg51" />
+ <path
+ fill="currentColor"
+ d="m 1718,672 v 704 q 0,92 -66,158 -66,66 -158,66 H 278 q -92,0 -158,-66 -66,-66 -66,-158 V 416 q 0,-92 66,-158 66,-66 158,-66 h 320 q 92,0 158,66 66,66 66,158 v 32 h 672 q 92,0 158,66 66,66 66,158 z"
+ id="path49" />
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/folder_open.svg b/examples/quickcontrols/filesystemexplorer/icons/folder_open.svg
new file mode 100644
index 0000000000..daa55a7a1f
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/folder_open.svg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ viewBox="-10 0 1792 1792"
+ id="svg139"
+ sodipodi:docname="folder_open.svg"
+ width="1792"
+ height="1792"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs143" />
+ <sodipodi:namedview
+ id="namedview141"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.24358259"
+ inkscape:cx="149.84651"
+ inkscape:cy="1098.1901"
+ inkscape:window-width="1846"
+ inkscape:window-height="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg139" />
+ <path
+ fill="currentColor"
+ d="M 1590,1376 V 672 q 0,-40 -28,-68 -28,-28 -68,-28 H 790 q -40,0 -68,-28 -28,-28 -28,-68 v -64 q 0,-40 -28,-68 -28,-28 -68,-28 H 278 q -40,0 -68,28 -28,28 -28,68 v 960 q 0,40 28,68 28,28 68,28 h 1216 q 40,0 68,-28 28,-28 28,-68 z m 128,-704 v 704 q 0,92 -66,158 -66,66 -158,66 H 278 q -92,0 -158,-66 -66,-66 -66,-158 V 416 q 0,-92 66,-158 66,-66 158,-66 h 320 q 92,0 158,66 66,66 66,158 v 32 h 672 q 92,0 158,66 66,66 66,158 z"
+ id="path137" />
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/generic_file.svg b/examples/quickcontrols/filesystemexplorer/icons/generic_file.svg
new file mode 100644
index 0000000000..9c855676e4
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/generic_file.svg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ viewBox="-10 0 1792 1792"
+ id="svg147"
+ sodipodi:docname="generic_file.svg"
+ width="1792"
+ height="1792"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs151" />
+ <sodipodi:namedview
+ id="namedview149"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.12179129"
+ inkscape:cx="-578.85911"
+ inkscape:cy="1687.3127"
+ inkscape:window-width="1846"
+ inkscape:window-height="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg147" />
+ <path
+ fill="currentColor"
+ d="m 1586,476 q 14,14 28,36 H 1142 V 40 q 22,14 36,28 z m -476,164 h 544 v 1056 q 0,40 -28,68 -28,28 -68,28 H 214 q -40,0 -68,-28 -28,-28 -28,-68 V 96 Q 118,56 146,28 174,0 214,0 h 800 v 544 q 0,40 28,68 28,28 68,28 z m 160,736 v -64 q 0,-14 -9,-23 -9,-9 -23,-9 H 534 q -14,0 -23,9 -9,9 -9,23 v 64 q 0,14 9,23 9,9 23,9 h 704 q 14,0 23,-9 9,-9 9,-23 z m 0,-256 v -64 q 0,-14 -9,-23 -9,-9 -23,-9 H 534 q -14,0 -23,9 -9,9 -9,23 v 64 q 0,14 9,23 9,9 23,9 h 704 q 14,0 23,-9 9,-9 9,-23 z m 0,-256 v -64 q 0,-14 -9,-23 -9,-9 -23,-9 H 534 q -14,0 -23,9 -9,9 -9,23 v 64 q 0,14 9,23 9,9 23,9 h 704 q 14,0 23,-9 9,-9 9,-23 z"
+ id="path145" />
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/globe.svg b/examples/quickcontrols/filesystemexplorer/icons/globe.svg
new file mode 100644
index 0000000000..0814338136
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/globe.svg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ viewBox="-10 0 1792 1792"
+ id="svg155"
+ sodipodi:docname="globe.svg"
+ width="1792"
+ height="1792"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs159" />
+ <sodipodi:namedview
+ id="namedview157"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.12179129"
+ inkscape:cx="504.9622"
+ inkscape:cy="1720.1558"
+ inkscape:window-width="1846"
+ inkscape:window-height="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg155" />
+ <path
+ fill="currentColor"
+ d="m 886,128 q 209,0 385.5,103 176.5,103 279.5,279.5 103,176.5 103,385.5 0,209 -103,385.5 Q 1448,1458 1271.5,1561 1095,1664 886,1664 677,1664 500.5,1561 324,1458 221,1281.5 118,1105 118,896 118,687 221,510.5 324,334 500.5,231 677,128 886,128 Z m 274,521 q -2,1 -9.5,9.5 -7.5,8.5 -13.5,9.5 2,0 4.5,-5 2.5,-5 5,-11 2.5,-6 3.5,-7 6,-7 22,-15 14,-6 52,-12 34,-8 51,11 -2,-2 9.5,-13 11.5,-11 14.5,-12 3,-2 15,-4.5 12,-2.5 15,-7.5 l 2,-22 q -12,1 -17.5,-7 -5.5,-8 -6.5,-21 0,2 -6,8 0,-7 -4.5,-8 -4.5,-1 -11.5,1 -7,2 -9,1 -10,-3 -15,-7.5 -5,-4.5 -8,-16.5 -3,-12 -4,-15 -2,-5 -9.5,-10.5 -7.5,-5.5 -9.5,-10.5 -1,-2 -2.5,-5.5 -1.5,-3.5 -3,-6.5 -1.5,-3 -4,-5.5 -2.5,-2.5 -5.5,-2.5 -3,0 -7,5 -4,5 -7.5,10 -3.5,5 -4.5,5 -3,-2 -6,-1.5 -3,0.5 -4.5,1 -1.5,0.5 -4.5,3 -3,2.5 -5,3.5 -3,2 -8.5,3 -5.5,1 -8.5,2 15,-5 -1,-11 -10,-4 -16,-3 9,-4 7.5,-12 -1.5,-8 -8.5,-14 h 5 q -1,-4 -8.5,-8.5 -7.5,-4.5 -17.5,-8.5 -10,-4 -13,-6 -8,-5 -34,-9.5 -26,-4.5 -33,-0.5 -5,6 -4.5,10.5 0.5,4.5 4,14 3.5,9.5 3.5,12.5 1,6 -5.5,13 -6.5,7 -6.5,12 0,7 14,15.5 14,8.5 10,21.5 -3,8 -16,16 -13,8 -16,12 -5,8 -1.5,18.5 3.5,10.5 10.5,16.5 2,2 1.5,4 -0.5,2 -3.5,4.5 -3,2.5 -5.5,4 -2.5,1.5 -6.5,3.5 l -3,2 q -11,5 -20.5,-6 -9.5,-11 -13.5,-26 -7,-25 -16,-30 -23,-8 -29,1 -5,-13 -41,-26 -25,-9 -58,-4 6,-1 0,-15 -7,-15 -19,-12 3,-6 4,-17.5 1,-11.5 1,-13.5 3,-13 12,-23 1,-1 7,-8.5 6,-7.5 9.5,-13.5 3.5,-6 0.5,-6 35,4 50,-11 5,-5 11.5,-17 6.5,-12 10.5,-17 9,-6 14,-5.5 5,0.5 14.5,5.5 9.5,5 14.5,5 14,1 15.5,-11 1.5,-12 -7.5,-20 12,1 3,-17 -5,-7 -8,-9 -12,-4 -27,5 -8,4 2,8 -1,-1 -9.5,10.5 Q 927,340 919,346 q -8,6 -16,-5 -1,-1 -5.5,-13.5 Q 893,315 888,314 q -8,0 -16,15 3,-8 -11,-15 -14,-7 -24,-8 19,-12 -8,-27 -7,-4 -20.5,-5 -13.5,-1 -19.5,4 -5,7 -5.5,11.5 -0.5,4.5 5,8 5.5,3.5 10.5,5.5 5,2 11.5,4 6.5,2 8.5,3 14,10 8,14 -2,1 -8.5,3.5 -6.5,2.5 -11.5,4.5 -5,2 -6,4 -3,4 0,14 3,10 -2,14 -5,-5 -9,-17.5 -4,-12.5 -7,-16.5 7,9 -25,6 l -10,-1 q -4,0 -16,2 -12,2 -20.5,1 -8.5,-1 -13.5,-8 -4,-8 0,-20 1,-4 4,-2 -4,-3 -11,-9.5 -7,-6.5 -10,-8.5 -46,15 -94,41 6,1 12,-1 5,-2 13,-6.5 8,-4.5 10,-5.5 34,-14 42,-7 l 5,-5 q 14,16 20,25 -7,-4 -30,-1 -20,6 -22,12 7,12 5,18 -4,-3 -11.5,-10 -7.5,-7 -14.5,-11 -7,-4 -15,-5 -16,0 -22,1 -146,80 -235,222 7,7 12,8 4,1 5,9 1,8 2.5,11 1.5,3 11.5,-3 9,8 3,19 1,-1 44,27 19,17 21,21 3,11 -10,18 -1,-2 -9,-9 -8,-7 -9,-4 -3,5 0.5,18.5 3.5,13.5 10.5,12.5 -7,0 -9.5,16 -2.5,16 -2.5,35.5 0,19.5 -1,23.5 l 2,1 q -3,12 5.5,34.5 8.5,22.5 21.5,19.5 -13,3 20,43 6,8 8,9 3,2 12,7.5 9,5.5 15,10 6,4.5 10,10.5 4,5 10,22.5 6,17.5 14,23.5 -2,6 9.5,20 11.5,14 10.5,23 -1,0 -2.5,1 -1.5,1 -2.5,1 3,7 15.5,14 12.5,7 15.5,13 1,3 2,10 1,7 3,11 2,4 8,2 2,-20 -24,-62 -15,-25 -17,-29 -3,-5 -5.5,-15.5 Q 541,919 539,915 q 2,0 6,1.5 4,1.5 8.5,3.5 4.5,2 7.5,4 3,2 2,3 -3,7 2,17.5 5,10.5 12,18.5 7,8 17,19 10,11 12,13 6,6 14,19.5 8,13.5 0,13.5 9,0 20,10 11,10 17,20 5,8 8,26 3,18 5,24 2,7 8.5,13.5 6.5,6.5 12.5,9.5 l 16,8 q 0,0 13,7 5,2 18.5,10.5 13.5,8.5 21.5,11.5 10,4 16,4 6,0 14.5,-2.5 8.5,-2.5 13.5,-3.5 15,-2 29,15 14,17 21,21 36,19 55,11 -2,1 0.5,7.5 2.5,6.5 8,15.5 5.5,9 9,14.5 3.5,5.5 5.5,8.5 5,6 18,15 13,9 18,15 6,-4 7,-9 -3,8 7,20 10,12 18,10 14,-3 14,-32 -31,15 -49,-18 0,-1 -2.5,-5.5 -2.5,-4.5 -4,-8.5 -1.5,-4 -2.5,-8.5 -1,-4.5 0,-7.5 1,-3 5,-3 9,0 10,-3.5 1,-3.5 -2,-12.5 -3,-9 -4,-13 -1,-8 -11,-20 -10,-12 -12,-15 -5,9 -16,8 -11,-1 -16,-9 0,1 -1.5,5.5 -1.5,4.5 -1.5,6.5 -13,0 -15,-1 1,-3 2.5,-17.5 1.5,-14.5 3.5,-22.5 1,-4 5.5,-12 4.5,-8 7.5,-14.5 3,-6.5 4,-12.5 1,-6 -4.5,-9.5 -5.5,-3.5 -17.5,-2.5 -19,1 -26,20 -1,3 -3,10.5 -2,7.5 -5,11.5 -3,4 -9,7 -7,3 -24,2 -17,-1 -24,-5 -13,-8 -22.5,-29 -9.5,-21 -9.5,-37 0,-10 2.5,-26.5 2.5,-16.5 3,-25 0.5,-8.5 -5.5,-24.5 3,-2 9,-9.5 6,-7.5 10,-10.5 2,-1 4.5,-1.5 2.5,-0.5 4.5,0 2,0.5 4,-1.5 2,-2 3,-6 -1,-1 -4,-3 -3,-3 -4,-3 7,3 28.5,-1.5 21.5,-4.5 27.5,1.5 15,11 22,-2 0,-1 -2.5,-9.5 Q 870,931 872,926 q 5,27 29,9 3,3 15.5,5 12.5,2 17.5,5 3,2 7,5.5 4,3.5 5.5,4.5 1.5,1 5,-0.5 3.5,-1.5 8.5,-6.5 10,14 12,24 11,40 19,44 7,3 11,2 4,-1 4.5,-9.5 0.5,-8.5 0,-14 Q 1006,989 1005,982 l -1,-8 v -18 l -1,-8 q -15,-3 -18.5,-12 -3.5,-9 1.5,-18.5 5,-9.5 15,-18.5 1,-1 8,-3.5 7,-2.5 15.5,-6.5 8.5,-4 12.5,-8 21,-19 15,-35 7,0 11,-9 -1,0 -5,-3 -4,-3 -7.5,-5 -3.5,-2 -4.5,-2 9,-5 2,-16 5,-3 7.5,-11 2.5,-8 7.5,-10 9,12 21,2 7,-8 1,-16 5,-7 20.5,-10.5 15.5,-3.5 18.5,-9.5 7,2 8,-2 1,-4 1,-12 0,-8 3,-12 4,-5 15,-9 11,-4 13,-5 l 17,-11 q 3,-4 0,-4 18,2 31,-11 10,-11 -6,-20 3,-6 -3,-9.5 -6,-3.5 -15,-5.5 3,-1 11.5,-0.5 8.5,0.5 10.5,-1.5 15,-10 -7,-16 -17,-5 -43,12 z m -163,877 q 206,-36 351,-189 -3,-3 -12.5,-4.5 -9.5,-1.5 -12.5,-3.5 -18,-7 -24,-8 1,-7 -2.5,-13 -3.5,-6 -8,-9 -4.5,-3 -12.5,-8 -8,-5 -11,-7 -2,-2 -7,-6 -5,-4 -7,-5.5 -2,-1.5 -7.5,-4.5 -5.5,-3 -8.5,-2 -3,1 -10,1 l -3,1 q -3,1 -5.5,2.5 -2.5,1.5 -5.5,3 -3,1.5 -4,3 -1,1.5 0,2.5 -21,-17 -36,-22 -5,-1 -11,-5.5 -6,-4.5 -10.5,-7 -4.5,-2.5 -10,-1.5 -5.5,1 -11.5,7 -5,5 -6,15 -1,10 -2,13 -7,-5 0,-17.5 7,-12.5 2,-18.5 -3,-6 -10.5,-4.5 -7.5,1.5 -12,4.5 -4.5,3 -11.5,8.5 -7,5.5 -9,6.5 -2,1 -8.5,5.5 -6.5,4.5 -8.5,7.5 -3,4 -6,12 -3,8 -5,11 -2,-4 -11.5,-6.5 -9.5,-2.5 -9.5,-5.5 2,10 4,35 2,25 5,38 7,31 -12,48 -27,25 -29,40 -4,22 12,26 0,7 -8,20.5 -8,13.5 -7,21.5 0,6 2,16 z"
+ id="path153" />
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/info_sign.svg b/examples/quickcontrols/filesystemexplorer/icons/info_sign.svg
new file mode 100644
index 0000000000..517f763604
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/info_sign.svg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ viewBox="-10 0 1792 1792"
+ id="svg163"
+ sodipodi:docname="info_sign.svg"
+ width="1792"
+ height="1792"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs167" />
+ <sodipodi:namedview
+ id="namedview165"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.48716518"
+ inkscape:cx="72.870561"
+ inkscape:cy="896"
+ inkscape:window-width="1846"
+ inkscape:window-height="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg163" />
+ <path
+ fill="currentColor"
+ d="m 1142,1376 v -160 q 0,-14 -9,-23 -9,-9 -23,-9 h -96 V 672 q 0,-14 -9,-23 -9,-9 -23,-9 H 662 q -14,0 -23,9 -9,9 -9,23 v 160 q 0,14 9,23 9,9 23,9 h 96 v 320 h -96 q -14,0 -23,9 -9,9 -9,23 v 160 q 0,14 9,23 9,9 23,9 h 448 q 14,0 23,-9 9,-9 9,-23 z M 1014,480 V 320 q 0,-14 -9,-23 -9,-9 -23,-9 H 790 q -14,0 -23,9 -9,9 -9,23 v 160 q 0,14 9,23 9,9 23,9 h 192 q 14,0 23,-9 9,-9 9,-23 z m 640,416 q 0,209 -103,385.5 Q 1448,1458 1271.5,1561 1095,1664 886,1664 677,1664 500.5,1561 324,1458 221,1281.5 118,1105 118,896 118,687 221,510.5 324,334 500.5,231 677,128 886,128 1095,128 1271.5,231 1448,334 1551,510.5 1654,687 1654,896 Z"
+ id="path161" />
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/light_bulb.svg b/examples/quickcontrols/filesystemexplorer/icons/light_bulb.svg
new file mode 100644
index 0000000000..ed2ed55fb3
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/light_bulb.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ viewBox="-10 0 1538 1538"
+ id="svg4"
+ sodipodi:docname="light_bulb.svg"
+ width="1538"
+ height="1538"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ id="namedview6"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ fit-margin-top="1"
+ fit-margin-left="1"
+ fit-margin-right="1"
+ fit-margin-bottom="1"
+ lock-margins="true"
+ inkscape:zoom="0.16"
+ inkscape:cx="1234.375"
+ inkscape:cy="409.375"
+ inkscape:window-width="1846"
+ inkscape:window-height="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4" />
+ <path
+ fill="currentColor"
+ d="m 983,449 q 0,13 -9.5,22.5 Q 964,481 951,481 938,481 928.5,471.5 919,462 919,449 919,403 865,378 811,353 759,353 746,353 736.5,343.5 727,334 727,321 q 0,-13 9.5,-22.5 9.5,-9.5 22.5,-9.5 50,0 99.5,16 49.5,16 87,54 37.5,38 37.5,90 z m 160,0 q 0,-72 -34.5,-134 -34.5,-62 -90,-101.5 Q 963,174 895.5,151.5 828,129 759,129 690,129 622.5,151.5 555,174 499.5,213.5 444,253 409.5,315 375,377 375,449 q 0,101 68,180 10,11 30.5,33 20.5,22 30.5,33 128,153 141,298 h 228 q 13,-145 141,-298 10,-11 30.5,-33 20.5,-22 30.5,-33 68,-79 68,-180 z m 128,0 q 0,155 -103,268 -45,49 -74.5,87 -29.5,38 -59.5,95.5 -30,57.5 -34,107.5 47,28 47,82 0,37 -25,64 25,27 25,64 0,52 -45,81 13,23 13,47 0,46 -31.5,71 -31.5,25 -77.5,25 -20,44 -60,70 -40,26 -87,26 -47,0 -87,-26 -40,-26 -60,-70 -46,0 -77.5,-25 -31.5,-25 -31.5,-71 0,-24 13,-47 -45,-29 -45,-81 0,-37 25,-64 -25,-27 -25,-64 0,-54 47,-82 Q 514,957 484,899.5 454,842 424.5,804 395,766 350,717 247,604 247,449 247,350 291.5,264.5 336,179 408.5,122.5 481,66 572.5,33.5 664,1 759,1 q 95,0 186.5,32.5 91.5,32.5 164,89 72.5,56.5 117,142 Q 1271,350 1271,449 Z"
+ id="path2" />
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/qt_logo.svg b/examples/quickcontrols/filesystemexplorer/icons/qt_logo.svg
new file mode 100644
index 0000000000..062daff3e9
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/qt_logo.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="462pt"
+ height="339pt"
+ viewBox="0 0 462 339"
+ version="1.1"
+ id="svg2"
+>
+ <path
+ fill="#41cd52"
+ d=" M 63.50 0.00 L 462.00 0.00 L 462.00 274.79 C 440.60 296.26 419.13 317.66 397.61 339.00 L 0.00 339.00 L 0.00 63.39 C 21.08 42.18 42.34 21.13 63.50 0.00 Z"
+ id="path6"/>
+ <path
+ d=" M 122.37 71.33 C 137.50 61.32 156.21 58.79 174.00 58.95 C 190.94 59.16 208.72 62.13 222.76 72.24 C 232.96 79.41 239.59 90.48 244.01 101.93 C 251.16 120.73 253.26 141.03 253.50 161.01 C 253.53 181.13 252.62 201.69 245.96 220.86 C 241.50 233.90 233.01 245.48 221.81 253.52 C 229.87 266.58 238.09 279.54 246.15 292.60 C 236.02 297.27 225.92 301.97 215.78 306.62 C 207.15 292.38 198.56 278.11 189.90 263.89 C 178.19 265.81 166.21 265.66 154.44 264.36 C 140.34 262.67 125.97 258.37 115.09 248.88 C 106.73 241.64 101.48 231.51 97.89 221.21 C 92.01 203.79 90.43 185.25 90.16 166.97 C 90.02 147.21 91.28 127.14 97.24 108.18 C 101.85 93.92 109.48 79.69 122.37 71.33 Z"
+ id="path8"
+ fill="#ffffff"/>
+ <path
+ d=" M 294.13 70.69 C 304.73 70.68 315.33 70.68 325.93 70.69 C 325.96 84.71 325.92 98.72 325.95 112.74 C 339.50 112.76 353.05 112.74 366.60 112.75 C 366.37 121.85 366.12 130.95 365.86 140.05 C 352.32 140.08 338.79 140.04 325.25 140.07 C 325.28 163.05 325.18 186.03 325.30 209.01 C 325.56 215.30 325.42 221.94 328.19 227.75 C 330.21 232.23 335.65 233.38 340.08 233.53 C 348.43 233.50 356.77 233.01 365.12 232.86 C 365.63 241.22 366.12 249.59 366.60 257.95 C 349.99 260.74 332.56 264.08 316.06 258.86 C 309.11 256.80 302.63 252.19 299.81 245.32 C 294.76 233.63 294.35 220.62 294.13 208.07 C 294.11 185.40 294.13 162.74 294.12 140.07 C 286.73 140.05 279.34 140.08 271.95 140.05 C 271.93 130.96 271.93 121.86 271.95 112.76 C 279.34 112.73 286.72 112.77 294.11 112.74 C 294.14 98.72 294.10 84.71 294.13 70.69 Z"
+ id="path10"
+ fill="#ffffff"/>
+ <path
+ fill="#41cd52"
+ d=" M 160.51 87.70 C 170.80 86.36 181.60 86.72 191.34 90.61 C 199.23 93.73 205.93 99.84 209.47 107.58 C 214.90 119.31 216.98 132.26 218.03 145.05 C 219.17 162.07 219.01 179.25 216.66 196.17 C 215.01 206.24 212.66 216.85 205.84 224.79 C 198.92 232.76 188.25 236.18 178.01 236.98 C 167.21 237.77 155.82 236.98 146.07 231.87 C 140.38 228.84 135.55 224.09 132.73 218.27 C 129.31 211.30 127.43 203.69 126.11 196.07 C 122.13 171.91 121.17 146.91 126.61 122.89 C 128.85 113.83 132.11 104.53 138.73 97.70 C 144.49 91.85 152.51 88.83 160.51 87.70 Z"
+ id="path12"/>
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/read.svg b/examples/quickcontrols/filesystemexplorer/icons/read.svg
new file mode 100644
index 0000000000..c3af473d21
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/read.svg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ viewBox="-11 0 1792 1792"
+ id="svg184"
+ sodipodi:docname="read.svg"
+ width="1792"
+ height="1792"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs188" />
+ <sodipodi:namedview
+ id="namedview186"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="0.24358259"
+ inkscape:cx="519.33104"
+ inkscape:cy="1089.9794"
+ inkscape:window-width="1846"
+ inkscape:window-height="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg184" />
+ <path
+ fill="currentColor"
+ d="m 1691.8576,478 q 40,57 18,129 l -275,906 q -19,64 -76.5,107.5 -57.5,43.5 -122.5,43.5 H 312.85764 q -77,0 -148.5,-53.5 Q 92.857644,1557 64.857644,1479 q -24,-67 -2,-127 0,-4 3,-27 3,-23 4,-37 1,-8 -3,-21.5 -4,-13.5 -3,-19.5 2,-11 8,-21 6,-10 16.5,-23.5 10.5,-13.5 16.499996,-23.5 23,-38 45,-91.5 22,-53.5 30,-91.5 3,-10 0.5,-30 -2.5,-20 -0.5,-28 3,-11 17,-28 14,-17 17,-23 21,-36 42,-92 21,-56 25,-90 1,-9 -2.5,-32 -3.5,-23 0.5,-28 4,-13 22,-30.5 18,-17.5 22,-22.5 19,-26 42.5,-84.5 23.5,-58.5 27.5,-96.5 1,-8 -3,-25.5 -4,-17.5 -2,-26.5 2,-8 9,-18 7,-10 18,-23 11,-13 17,-21 8,-12 16.5,-30.5 8.5,-18.5 15,-35 6.5,-16.5 16,-36 9.5,-19.5 19.5,-32 10,-12.5 26.5,-23.5 16.5,-11 36,-11.5 19.5,-0.5 47.5,5.5 l -1,3 q 38,-9 51,-9 h 760.99996 q 74,0 114,56 40,56 18,130 l -274,906 q -36,119 -71.5,153.5 -35.5,34.5 -128.5,34.5 H 208.85764 q -27,0 -38,15 -11,16 -1,43 24,70 144,70 h 922.99996 q 29,0 56,-15.5 27,-15.5 35,-41.5 l 300,-987 q 7,-22 5,-57 38,15 59,43 z m -1063.99996,2 q -4,13 2,22.5 6,9.5 20,9.5 h 607.99996 q 13,0 25.5,-9.5 12.5,-9.5 16.5,-22.5 l 21,-64 q 4,-13 -2,-22.5 -6,-9.5 -20,-9.5 H 690.85764 q -13,0 -25.5,9.5 -12.5,9.5 -16.5,22.5 z m -83,256 q -4,13 2,22.5 6,9.5 20,9.5 h 607.99996 q 13,0 25.5,-9.5 12.5,-9.5 16.5,-22.5 l 21,-64 q 4,-13 -2,-22.5 -6,-9.5 -20,-9.5 H 607.85764 q -13,0 -25.5,9.5 -12.5,9.5 -16.5,22.5 z"
+ id="path182" />
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/icons/resize.svg b/examples/quickcontrols/filesystemexplorer/icons/resize.svg
new file mode 100644
index 0000000000..e86d612f3f
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/icons/resize.svg
@@ -0,0 +1,6 @@
+<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'>
+ <line x1="00" y1="100" x2="100" y2="00" stroke="black" stroke-width="3" />
+ <line x1="20" y1="100" x2="100" y2="20" stroke="black" stroke-width="3" />
+ <line x1="40" y1="100" x2="100" y2="40" stroke="black" stroke-width="3" />
+ <line x1="60" y1="100" x2="100" y2="60" stroke="black" stroke-width="3" />
+</svg>
diff --git a/examples/quickcontrols/filesystemexplorer/linenumbermodel.cpp b/examples/quickcontrols/filesystemexplorer/linenumbermodel.cpp
new file mode 100644
index 0000000000..5a7982dca9
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/linenumbermodel.cpp
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "linenumbermodel.h"
+
+#include <QQmlInfo>
+
+/*!
+ When using an integer model based on the line count of the editor,
+ any changes in that line count cause all delegates to be destroyed
+ and recreated. That's inefficient, so instead, we add/remove model
+ items as necessary ourselves, based on the lineCount property.
+*/
+LineNumberModel::LineNumberModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+int LineNumberModel::lineCount() const
+{
+ return m_lineCount;
+}
+
+void LineNumberModel::setLineCount(int lineCount)
+{
+ if (lineCount < 0) {
+ qmlWarning(this) << "lineCount must be greater than zero";
+ return;
+ }
+
+ if (m_lineCount == lineCount)
+ return;
+
+ if (m_lineCount < lineCount) {
+ beginInsertRows(QModelIndex(), m_lineCount, lineCount - 1);
+ m_lineCount = lineCount;
+ endInsertRows();
+ } else if (m_lineCount > lineCount) {
+ beginRemoveRows(QModelIndex(), lineCount, m_lineCount - 1);
+ m_lineCount = lineCount;
+ endRemoveRows();
+ }
+
+ emit lineCountChanged();
+}
+
+int LineNumberModel::rowCount(const QModelIndex &) const
+{
+ return m_lineCount;
+}
+
+QVariant LineNumberModel::data(const QModelIndex &index, int role) const
+{
+ if (!checkIndex(index) || role != Qt::DisplayRole)
+ return QVariant();
+
+ return index.row();
+}
diff --git a/examples/quickcontrols/filesystemexplorer/linenumbermodel.h b/examples/quickcontrols/filesystemexplorer/linenumbermodel.h
new file mode 100644
index 0000000000..1ec800ffd8
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/linenumbermodel.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef LINENUMBERMODEL_H
+#define LINENUMBERMODEL_H
+
+#include <QAbstractItemModel>
+#include <QQmlEngine>
+
+class LineNumberModel : public QAbstractListModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int lineCount READ lineCount WRITE setLineCount NOTIFY lineCountChanged)
+
+public:
+ explicit LineNumberModel(QObject *parent = nullptr);
+
+ int lineCount() const;
+ void setLineCount(int lineCount);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+signals:
+ void lineCountChanged();
+
+private:
+ int m_lineCount = 0;
+};
+
+#endif // LINENUMBERMODEL_H
diff --git a/examples/quickcontrols/filesystemexplorer/main.cpp b/examples/quickcontrols/filesystemexplorer/main.cpp
new file mode 100644
index 0000000000..9a43fa9d6d
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/main.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "filesystemmodel.h"
+
+#include <QGuiApplication>
+#include <QCommandLineParser>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ // Initialize the static application object.
+ QGuiApplication app(argc, argv);
+ QGuiApplication::setOrganizationName("QtProject");
+ QGuiApplication::setApplicationName("File System Explorer");
+ QGuiApplication::setApplicationVersion(QT_VERSION_STR);
+ QGuiApplication::setWindowIcon(QIcon(":/qt/qml/FileSystemModule/icons/app_icon.svg"));
+
+ // Setup the parser and parse the command-line arguments.
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Qt Filesystemexplorer Example");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("", QGuiApplication::translate(
+ "main", "Initial directory"),"[path]");
+ parser.process(app);
+ const auto args = parser.positionalArguments();
+
+ // Load the QML entry point.
+ QQmlApplicationEngine engine;
+ engine.loadFromModule("FileSystemModule", "Main");
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ // Set the initial directory if provided
+ if (args.length() == 1) {
+ auto *fileSystemModel = engine.singletonInstance<FileSystemModel*>(
+ "FileSystemModule","FileSystemModel");
+ fileSystemModel->setInitialDirectory(args[0]);
+ }
+
+ return QGuiApplication::exec(); // Start the event loop.
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/About.qml b/examples/quickcontrols/filesystemexplorer/qml/About.qml
new file mode 100644
index 0000000000..0d308a2a1d
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/About.qml
@@ -0,0 +1,93 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Basic
+import FileSystemModule
+
+ApplicationWindow {
+ id: root
+ width: 650
+ height: 550
+ flags: Qt.Window | Qt.FramelessWindowHint
+ color: Colors.surface1
+
+ menuBar: MyMenuBar {
+ id: menuBar
+
+ dragWindow: root
+ implicitHeight: 30
+ infoText: "About Qt"
+ }
+
+ Image {
+ id: logo
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.margins: 20
+
+ source: "../icons/qt_logo.svg"
+ sourceSize.width: 80
+ sourceSize.height: 80
+ fillMode: Image.PreserveAspectFit
+
+ smooth: true
+ antialiasing: true
+ asynchronous: true
+ }
+
+ ScrollView {
+ anchors.top: logo.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+
+ TextArea {
+ selectedTextColor: Colors.textFile
+ selectionColor: Colors.selection
+ horizontalAlignment: Text.AlignHCenter
+ textFormat: Text.RichText
+
+ text: qsTr("<h3>About Qt</h3>"
+ + "<p>This program uses Qt version %1.</p>"
+ + "<p>Qt is a C++ toolkit for cross-platform application "
+ + "development.</p>"
+ + "<p>Qt provides single-source portability across all major desktop "
+ + "operating systems. It is also available for embedded Linux and other "
+ + "embedded and mobile operating systems.</p>"
+ + "<p>Qt is available under multiple licensing options designed "
+ + "to accommodate the needs of our various users.</p>"
+ + "<p>Qt licensed under our commercial license agreement is appropriate "
+ + "for development of proprietary/commercial software where you do not "
+ + "want to share any source code with third parties or otherwise cannot "
+ + "comply with the terms of GNU (L)GPL.</p>"
+ + "<p>Qt licensed under GNU (L)GPL is appropriate for the "
+ + "development of Qt&nbsp;applications provided you can comply with the terms "
+ + "and conditions of the respective licenses.</p>"
+ + "<p>Please see <a href=\"http://%2/\">%2</a> "
+ + "for an overview of Qt licensing.</p>"
+ + "<p>Copyright (C) %3 The Qt Company Ltd and other "
+ + "contributors.</p>"
+ + "<p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p>"
+ + "<p>Qt is The Qt Company Ltd product developed as an open source "
+ + "project. See <a href=\"http://%4/\">%4</a> for more information.</p>")
+ .arg(Application.version).arg("qt.io/licensing").arg("2023").arg("qt.io")
+ color: Colors.textFile
+ wrapMode: Text.WordWrap
+ readOnly: true
+ antialiasing: true
+ background: null
+
+ onLinkActivated: function(link) {
+ Qt.openUrlExternally(link)
+ }
+ }
+ }
+
+ ResizeButton {
+ resizeWindow: root
+ }
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/Colors.qml b/examples/quickcontrols/filesystemexplorer/qml/Colors.qml
new file mode 100644
index 0000000000..2856677738
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/Colors.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+pragma Singleton
+
+QtObject {
+ readonly property color background: "#292828"
+ readonly property color surface1: "#171819"
+ readonly property color surface2: "#090A0C"
+ readonly property color text: "#D4BE98"
+ readonly property color textFile: "#E1D2B7"
+ readonly property color disabledText: "#2C313A"
+ readonly property color selection: "#4B4A4A"
+ readonly property color active: "#292828"
+ readonly property color inactive: "#383737"
+ readonly property color folder: "#383737"
+ readonly property color icon: "#383737"
+ readonly property color iconIndicator: "#D5B35D"
+ readonly property color color1: "#A7B464"
+ readonly property color color2: "#D3869B"
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/Editor.qml b/examples/quickcontrols/filesystemexplorer/qml/Editor.qml
new file mode 100644
index 0000000000..2f995c88c9
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/Editor.qml
@@ -0,0 +1,161 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import FileSystemModule
+
+pragma ComponentBehavior: Bound
+
+// This is the text editor that displays the currently open file, including
+// their corresponding line numbers.
+Rectangle {
+ id: root
+
+ required property string currentFilePath
+ required property bool showLineNumbers
+ property alias text: textArea
+ property int currentLineNumber: -1
+ property int rowHeight: Math.ceil(fontMetrics.lineSpacing)
+
+ color: Colors.background
+
+ onWidthChanged: textArea.update()
+ onHeightChanged: textArea.update()
+
+ RowLayout {
+ anchors.fill: parent
+ // We use a flickable to synchronize the position of the editor and
+ // the line numbers. This is necessary because the line numbers can
+ // extend the available height.
+ Flickable {
+ id: lineNumbers
+
+ // Calculate the width based on the logarithmic scale.
+ Layout.preferredWidth: fontMetrics.averageCharacterWidth
+ * (Math.floor(Math.log10(textArea.lineCount)) + 1) + 10
+ Layout.fillHeight: true
+ Layout.fillWidth: false
+
+ interactive: false
+ contentY: editorFlickable.contentY
+ visible: textArea.text !== "" && root.showLineNumbers
+
+ Column {
+ anchors.fill: parent
+ Repeater {
+ id: repeatedLineNumbers
+
+ model: LineNumberModel {
+ lineCount: textArea.text !== "" ? textArea.lineCount : 0
+ }
+
+ delegate: Item {
+ required property int index
+
+ width: parent.width
+ height: root.rowHeight
+ Label {
+ id: numbers
+
+ text: parent.index + 1
+
+ width: parent.width
+ height: parent.height
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+
+ color: (root.currentLineNumber === parent.index)
+ ? Colors.iconIndicator : Qt.darker(Colors.text, 2)
+ font: textArea.font
+ }
+ Rectangle {
+ id: indicator
+
+ anchors.left: numbers.right
+ width: 1
+ height: parent.height
+ color: Qt.darker(Colors.text, 3)
+ }
+ }
+ }
+ }
+ }
+
+ Flickable {
+ id: editorFlickable
+
+ property alias textArea: textArea
+
+ // We use an inline component to customize the horizontal and vertical
+ // scroll-bars. This is convenient when the component is only used in one file.
+ component MyScrollBar: ScrollBar {
+ id: scrollBar
+ background: Rectangle {
+ implicitWidth: scrollBar.interactive ? 8 : 4
+ implicitHeight: scrollBar.interactive ? 8 : 4
+
+ opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
+ color: Colors.background
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 500
+ }
+ }
+ }
+ contentItem: Rectangle {
+ implicitWidth: scrollBar.interactive ? 8 : 4
+ implicitHeight: scrollBar.interactive ? 8 : 4
+ opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
+ color: Colors.color1
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 1000
+ }
+ }
+ }
+ }
+
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ ScrollBar.horizontal: MyScrollBar {}
+ ScrollBar.vertical: MyScrollBar {}
+
+ boundsBehavior: Flickable.StopAtBounds
+
+ TextArea.flickable: TextArea {
+ id: textArea
+ anchors.fill: parent
+
+ focus: false
+ topPadding: 0
+ leftPadding: 10
+
+ text: FileSystemModel.readFile(root.currentFilePath)
+ tabStopDistance: fontMetrics.averageCharacterWidth * 4
+
+ // Grab the current line number from the C++ interface.
+ onCursorPositionChanged: {
+ root.currentLineNumber = FileSystemModel.currentLineNumber(
+ textArea.textDocument, textArea.cursorPosition)
+ }
+
+ color: Colors.textFile
+ selectedTextColor: Colors.textFile
+ selectionColor: Colors.selection
+
+ textFormat: TextEdit.PlainText
+ renderType: Text.QtRendering
+ selectByMouse: true
+ antialiasing: true
+ background: null
+ }
+
+ FontMetrics {
+ id: fontMetrics
+ font: textArea.font
+ }
+ }
+ }
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/FileSystemView.qml b/examples/quickcontrols/filesystemexplorer/qml/FileSystemView.qml
new file mode 100644
index 0000000000..db955168c5
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/FileSystemView.qml
@@ -0,0 +1,156 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Effects
+import QtQuick.Controls.Basic
+import FileSystemModule
+
+pragma ComponentBehavior: Bound
+
+// This is the file system view which gets populated by the C++ model.
+Rectangle {
+ id: root
+
+ signal fileClicked(string filePath)
+ property alias rootIndex: fileSystemTreeView.rootIndex
+
+ TreeView {
+ id: fileSystemTreeView
+
+ property int lastIndex: -1
+
+ anchors.fill: parent
+ model: FileSystemModel
+ rootIndex: FileSystemModel.rootIndex
+ boundsBehavior: Flickable.StopAtBounds
+ boundsMovement: Flickable.StopAtBounds
+ clip: true
+
+ Component.onCompleted: fileSystemTreeView.toggleExpanded(0)
+
+ // The delegate represents a single entry in the filesystem.
+ delegate: TreeViewDelegate {
+ id: treeDelegate
+ indentation: 8
+ implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
+ implicitHeight: 25
+
+ // Since we have the 'ComponentBehavior Bound' pragma, we need to
+ // require these properties from our model. This is a convenient way
+ // to bind the properties provided by the model's role names.
+ required property int index
+ required property url filePath
+ required property string fileName
+
+ indicator: Image {
+ id: directoryIcon
+
+ x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation)
+ anchors.verticalCenter: parent.verticalCenter
+ source: treeDelegate.hasChildren ? (treeDelegate.expanded
+ ? "../icons/folder_open.svg" : "../icons/folder_closed.svg")
+ : "../icons/generic_file.svg"
+ sourceSize.width: 20
+ sourceSize.height: 20
+ fillMode: Image.PreserveAspectFit
+
+ smooth: true
+ antialiasing: true
+ asynchronous: true
+ }
+
+ contentItem: Text {
+ text: treeDelegate.fileName
+ color: Colors.text
+ }
+
+ background: Rectangle {
+ color: (treeDelegate.index === fileSystemTreeView.lastIndex)
+ ? Colors.selection
+ : (hoverHandler.hovered ? Colors.active : "transparent")
+ }
+
+ // We color the directory icons with this MultiEffect, where we overlay
+ // the colorization color ontop of the SVG icons.
+ MultiEffect {
+ id: iconOverlay
+
+ anchors.fill: directoryIcon
+ source: directoryIcon
+ colorization: 1.0
+ brightness: 1.0
+ colorizationColor: {
+ const isFile = treeDelegate.index === fileSystemTreeView.lastIndex
+ && !treeDelegate.hasChildren;
+ if (isFile)
+ return Qt.lighter(Colors.folder, 3)
+
+ const isExpandedFolder = treeDelegate.expanded && treeDelegate.hasChildren;
+ if (isExpandedFolder)
+ return Colors.color2
+ else
+ return Colors.folder
+ }
+ }
+
+ HoverHandler {
+ id: hoverHandler
+ }
+
+ TapHandler {
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onSingleTapped: (eventPoint, button) => {
+ switch (button) {
+ case Qt.LeftButton:
+ fileSystemTreeView.toggleExpanded(treeDelegate.row)
+ fileSystemTreeView.lastIndex = treeDelegate.index
+ // If this model item doesn't have children, it means it's
+ // representing a file.
+ if (!treeDelegate.hasChildren)
+ root.fileClicked(treeDelegate.filePath)
+ break;
+ case Qt.RightButton:
+ if (treeDelegate.hasChildren)
+ contextMenu.popup();
+ break;
+ }
+ }
+ }
+
+ MyMenu {
+ id: contextMenu
+ Action {
+ text: qsTr("Set as root index")
+ onTriggered: {
+ fileSystemTreeView.rootIndex = fileSystemTreeView.index(treeDelegate.row, 0)
+ }
+ }
+ Action {
+ text: qsTr("Reset root index")
+ onTriggered: fileSystemTreeView.rootIndex = undefined
+ }
+ }
+ }
+
+ // Provide our own custom ScrollIndicator for the TreeView.
+ ScrollIndicator.vertical: ScrollIndicator {
+ active: true
+ implicitWidth: 15
+
+ contentItem: Rectangle {
+ implicitWidth: 6
+ implicitHeight: 6
+
+ color: Colors.color1
+ opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0
+
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 500
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/MyMenu.qml b/examples/quickcontrols/filesystemexplorer/qml/MyMenu.qml
new file mode 100644
index 0000000000..1f1d30c566
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/MyMenu.qml
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Basic
+import FileSystemModule
+
+Menu {
+ id: root
+
+ delegate: MenuItem {
+ id: menuItem
+ contentItem: Item {
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.leftMargin: 5
+
+ text: menuItem.text
+ color: enabled ? Colors.text : Colors.disabledText
+ }
+ Rectangle {
+ id: indicator
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ width: 6
+ height: parent.height
+
+ visible: menuItem.highlighted
+ color: Colors.color2
+ }
+ }
+ background: Rectangle {
+ implicitWidth: 210
+ implicitHeight: 35
+ color: menuItem.highlighted ? Colors.active : "transparent"
+ }
+ }
+ background: Rectangle {
+ implicitWidth: 210
+ implicitHeight: 35
+ color: Colors.surface2
+ }
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/MyMenuBar.qml b/examples/quickcontrols/filesystemexplorer/qml/MyMenuBar.qml
new file mode 100644
index 0000000000..4874a2c03f
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/MyMenuBar.qml
@@ -0,0 +1,177 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls.Basic
+import FileSystemModule
+
+// The MenuBar also serves as a controller for our window as we don't use any decorations.
+MenuBar {
+ id: root
+
+ required property ApplicationWindow dragWindow
+ property alias infoText: windowInfo.text
+
+ // Customization of the top level menus inside the MenuBar
+ delegate: MenuBarItem {
+ id: menuBarItem
+
+ contentItem: Text {
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+
+ text: menuBarItem.text
+ font: menuBarItem.font
+ elide: Text.ElideRight
+ color: menuBarItem.highlighted ? Colors.textFile : Colors.text
+ opacity: enabled ? 1.0 : 0.3
+ }
+
+ background: Rectangle {
+ id: background
+
+ color: menuBarItem.highlighted ? Colors.selection : "transparent"
+ Rectangle {
+ id: indicator
+
+ width: 0; height: 3
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+
+ color: Colors.color1
+ states: State {
+ name: "active"
+ when: menuBarItem.highlighted
+ PropertyChanges {
+ indicator.width: background.width - 2
+ }
+ }
+ transitions: Transition {
+ NumberAnimation {
+ properties: "width"
+ duration: 175
+ }
+ }
+ }
+ }
+ }
+ // We use the contentItem property as a place to attach our window decorations. Beneath
+ // the usual menu entries within a MenuBar, it includes a centered information text, along
+ // with the minimize, maximize, and close buttons.
+ contentItem: RowLayout {
+ id: windowBar
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ spacing: root.spacing
+ Repeater {
+ id: menuBarItems
+
+ Layout.alignment: Qt.AlignLeft
+ model: root.contentModel
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Text {
+ id: windowInfo
+
+ width: parent.width; height: parent.height
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: windowActions.width
+ color: Colors.text
+ clip: true
+ }
+ }
+
+ RowLayout {
+ id: windowActions
+
+ Layout.alignment: Qt.AlignRight
+ Layout.fillHeight: true
+
+ spacing: 0
+
+ component InteractionButton: Rectangle {
+ id: interactionButton
+
+ signal action()
+ property alias hovered: hoverHandler.hovered
+
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+
+ color: hovered ? Colors.background : "transparent"
+ HoverHandler {
+ id: hoverHandler
+ }
+ TapHandler {
+ id: tapHandler
+ onTapped: interactionButton.action()
+ }
+ }
+
+ InteractionButton {
+ id: minimize
+
+ onAction: root.dragWindow.showMinimized()
+ Rectangle {
+ anchors.centerIn: parent
+ color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ height: 2
+ width: parent.height - 14
+ }
+ }
+
+ InteractionButton {
+ id: maximize
+
+ onAction: root.dragWindow.showMaximized()
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 7
+ border.color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ border.width: 2
+ color: "transparent"
+ }
+ }
+
+ InteractionButton {
+ id: close
+
+ color: hovered ? "#ec4143" : "transparent"
+ onAction: root.dragWindow.close()
+ Rectangle {
+ anchors.centerIn: parent
+ width: parent.height - 8; height: 2
+
+ rotation: 45
+ antialiasing: true
+ transformOrigin: Item.Center
+ color: parent.hovered ? Colors.iconIndicator : Colors.icon
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: parent.height
+ height: parent.width
+
+ antialiasing: true
+ color: parent.color
+ }
+ }
+ }
+ }
+ }
+
+ background: Rectangle {
+ color: Colors.surface2
+ // Make the empty space drag the specified root window.
+ WindowDragHandler {
+ dragWindow: root.dragWindow
+ }
+ }
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/ResizeButton.qml b/examples/quickcontrols/filesystemexplorer/qml/ResizeButton.qml
new file mode 100644
index 0000000000..0df65bf82f
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/ResizeButton.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick.Controls
+import FileSystemModule
+
+Button {
+ required property ApplicationWindow resizeWindow
+
+ icon.width: 20; icon.height: 20
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ rightPadding: 3
+ bottomPadding: 3
+
+ icon.source: "../icons/resize.svg"
+ icon.color: hovered ? Colors.iconIndicator : Colors.icon
+
+ background: null
+ checkable: false
+ display: AbstractButton.IconOnly
+ onPressed: resizeWindow.startSystemResize(Qt.BottomEdge | Qt.RightEdge)
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/Sidebar.qml b/examples/quickcontrols/filesystemexplorer/qml/Sidebar.qml
new file mode 100644
index 0000000000..f739e0f930
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/Sidebar.qml
@@ -0,0 +1,140 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls.Basic
+import FileSystemModule
+
+Rectangle {
+ id: root
+
+ property alias currentTabIndex: topBar.currentIndex
+ required property ApplicationWindow dragWindow
+ readonly property int tabBarSpacing: 10
+
+ color: Colors.surface2
+
+ component SidebarEntry: Button {
+ id: sidebarButton
+
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
+
+ icon.color: down || checked ? Colors.iconIndicator : Colors.icon
+ icon.width: 27
+ icon.height: 27
+
+ topPadding: 0
+ rightPadding: 0
+ bottomPadding: 0
+ leftPadding: 0
+ background: null
+
+ Rectangle {
+ id: indicator
+
+ anchors.verticalCenter: parent.verticalCenter
+ x: 2
+ width: 4
+ height: sidebarButton.icon.height * 1.2
+
+ visible: sidebarButton.checked
+ color: Colors.color1
+ }
+ }
+
+ // TabBar is designed to be horizontal, whereas we need a vertical bar.
+ // We can easily achieve that by using a Container.
+ component TabBar: Container {
+ id: tabBarComponent
+
+ Layout.fillWidth: true
+ Layout.fillHeight: false
+
+ // ButtonGroup ensures that only one button can be checked at a time.
+ ButtonGroup {
+ buttons: tabBarComponent.contentChildren
+
+ // We have to manage the currentIndex ourselves, which we do by setting it to the index
+ // of the currently checked button. We use setCurrentIndex instead of setting the
+ // currentIndex property to avoid breaking bindings. See "Managing the Current Index"
+ // in Container's documentation for more information.
+ onCheckedButtonChanged: tabBarComponent.setCurrentIndex(
+ Math.max(0, buttons.indexOf(checkedButton)))
+ }
+
+ contentItem: ColumnLayout {
+ spacing: tabBarComponent.spacing
+ Repeater {
+ model: tabBarComponent.contentModel
+ }
+ }
+ }
+
+ ColumnLayout {
+ anchors.fill: root
+ anchors.topMargin: root.tabBarSpacing
+ anchors.bottomMargin: root.tabBarSpacing
+
+ spacing: root.tabBarSpacing
+ TabBar {
+ id: topBar
+
+ spacing: root.tabBarSpacing
+ // Shows help text when clicked.
+ SidebarEntry {
+ id: infoTab
+ icon.source: "../icons/light_bulb.svg"
+ checkable: true
+ checked: true
+ }
+
+ // Shows the file system when clicked.
+ SidebarEntry {
+ id: filesystemTab
+
+ icon.source: "../icons/read.svg"
+ checkable: true
+ }
+ }
+
+ // This item acts as a spacer to expand between the checkable and non-checkable buttons.
+ Item {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ // Make the empty space drag our main window.
+ WindowDragHandler {
+ dragWindow: root.dragWindow
+ }
+ }
+
+ TabBar {
+ id: bottomBar
+
+ spacing: root.tabBarSpacing
+ // Opens the Qt website in the system's web browser.
+ SidebarEntry {
+ id: qtWebsiteButton
+ icon.source: "../icons/globe.svg"
+ checkable: false
+ onClicked: Qt.openUrlExternally("https://www.qt.io/")
+ }
+
+ // Opens the About Qt Window.
+ SidebarEntry {
+ id: aboutQtButton
+
+ icon.source: "../icons/info_sign.svg"
+ checkable: false
+ onClicked: aboutQtWindow.visible = !aboutQtWindow.visible
+ }
+ }
+ }
+
+ About {
+ id: aboutQtWindow
+ visible: false
+ }
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qml/WindowDragHandler.qml b/examples/quickcontrols/filesystemexplorer/qml/WindowDragHandler.qml
new file mode 100644
index 0000000000..0e140aca37
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qml/WindowDragHandler.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+// Allows dragging the window when placed on an unused section of the UI.
+DragHandler {
+
+ required property ApplicationWindow dragWindow
+
+ target: null
+ onActiveChanged: {
+ if (active) dragWindow.startSystemMove()
+ }
+}
diff --git a/examples/quickcontrols/filesystemexplorer/qmldir b/examples/quickcontrols/filesystemexplorer/qmldir
new file mode 100644
index 0000000000..f94e68a8ac
--- /dev/null
+++ b/examples/quickcontrols/filesystemexplorer/qmldir
@@ -0,0 +1,12 @@
+module FileSystemModule
+
+Main 1.0 Main.qml
+About 1.0 qml/About.qml
+MyMenu 1.0 qml/MyMenu.qml
+Editor 1.0 qml/Editor.qml
+Sidebar 1.0 qml/Sidebar.qml
+MyMenuBar 1.0 qml/MyMenuBar.qml
+singleton Colors 1.0 qml/Colors.qml
+ResizeButton 1.0 qml/ResizeButton.qml
+FileSystemView 1.0 qml/FileSystemView.qml
+WindowDragHandler 1.0 qml/WindowDragHandler.qml
diff --git a/examples/quickcontrols/flatstyle/CMakeLists.txt b/examples/quickcontrols/flatstyle/CMakeLists.txt
index c505c03bca..8c78720a58 100644
--- a/examples/quickcontrols/flatstyle/CMakeLists.txt
+++ b/examples/quickcontrols/flatstyle/CMakeLists.txt
@@ -1,17 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(flatstyle LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/flatstyle")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2 Widgets)
qt_add_executable(flatstyleexample WIN32 MACOSX_BUNDLE
@@ -35,15 +29,24 @@ qt_add_qml_module(flatstyleexample
)
target_link_libraries(flatstyleexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickControls2
- Qt::Widgets
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
+ Qt6::Widgets
)
install(TARGETS flatstyleexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET flatstyleexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/flatstyle/MainForm.ui.qml b/examples/quickcontrols/flatstyle/MainForm.ui.qml
index 389ee893c3..ff05a2dcef 100644
--- a/examples/quickcontrols/flatstyle/MainForm.ui.qml
+++ b/examples/quickcontrols/flatstyle/MainForm.ui.qml
@@ -94,6 +94,7 @@ Item {
id: button
text: qsTr("Change Color")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ Layout.fillWidth: false
}
}
}
diff --git a/examples/quickcontrols/flatstyle/doc/src/qtquickcontrols-flatstyle.qdoc b/examples/quickcontrols/flatstyle/doc/src/qtquickcontrols-flatstyle.qdoc
index 3f5a982b7d..eeb63b7084 100644
--- a/examples/quickcontrols/flatstyle/doc/src/qtquickcontrols-flatstyle.qdoc
+++ b/examples/quickcontrols/flatstyle/doc/src/qtquickcontrols-flatstyle.qdoc
@@ -7,6 +7,7 @@
\title Qt Quick Controls - Flat Style
\keyword Qt Quick Controls 2 - Flat Style
\ingroup qtquickcontrols-examples
+ \examplecategory {Graphics}
\brief A QML app using Qt Quick Controls and a QML plugin
with customized controls.
diff --git a/examples/quickcontrols/gallery/CMakeLists.txt b/examples/quickcontrols/gallery/CMakeLists.txt
index baa7d948f1..40f393d6d2 100644
--- a/examples/quickcontrols/gallery/CMakeLists.txt
+++ b/examples/quickcontrols/gallery/CMakeLists.txt
@@ -1,17 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(gallery_controls2 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/gallery")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
qt_add_executable(galleryexample WIN32 MACOSX_BUNDLE
@@ -83,21 +77,30 @@ qt_add_qml_module(galleryexample
)
target_link_libraries(galleryexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickControls2
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
)
if(UNIX AND NOT APPLE AND CMAKE_CROSSCOMPILING)
find_package(Qt6 REQUIRED COMPONENTS QuickTemplates2)
# Work around QTBUG-86533
- target_link_libraries(galleryexample PRIVATE Qt::QuickTemplates2)
+ target_link_libraries(galleryexample PRIVATE Qt6::QuickTemplates2)
endif()
install(TARGETS galleryexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET galleryexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/gallery/doc/src/qtquickcontrols-gallery.qdoc b/examples/quickcontrols/gallery/doc/src/qtquickcontrols-gallery.qdoc
index 2fc1ed8f46..2b17866f21 100644
--- a/examples/quickcontrols/gallery/doc/src/qtquickcontrols-gallery.qdoc
+++ b/examples/quickcontrols/gallery/doc/src/qtquickcontrols-gallery.qdoc
@@ -7,6 +7,8 @@
\title Qt Quick Controls - Gallery
\keyword Qt Quick Controls 2 - Gallery
\ingroup qtquickcontrols-examples
+ \examplecategory {User Interface Components}
+ \examplecategory {Mobile}
\brief A gallery of controls.
\raw HTML
@@ -35,5 +37,20 @@
graphical appearance of a control, allows you to interact with the control,
and explains in which circumstances it is handy to use this control.
+ When the application is in portrait mode, the drawer is an interactive side
+ panel that can be swiped open from the left edge. It appears on top of the
+ content, blocking user interaction through its modal background. When the
+ application is in landscape mode, the drawer and the content are laid out
+ side-by-side.
+
+ The current orientation of the application is determined by comparing the
+ width and height of the window and orientation mode configuration in the
+ settings:
+
+ \snippet gallery/gallery.qml orientation
+
+ As this example supports all built-in Qt Quick Controls styles, it uses
+ \l {Run-Time Style Selection}{runtime style selection}.
+
\include examples-run.qdocinc
*/
diff --git a/examples/quickcontrols/gallery/gallery.qml b/examples/quickcontrols/gallery/gallery.qml
index 35eaed9f11..5caab6aba6 100644
--- a/examples/quickcontrols/gallery/gallery.qml
+++ b/examples/quickcontrols/gallery/gallery.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtCore
import QtQuick
import QtQuick.Layouts
@@ -13,7 +15,11 @@ ApplicationWindow {
width: 360
height: 520
visible: true
- title: "Qt Quick Controls"
+ title: qsTr("Qt Quick Controls")
+
+ //! [orientation]
+ readonly property bool portraitMode: !orientationCheckBox.checked || window.width < window.height
+ //! [orientation]
function help() {
let displayingControl = listView.currentIndex !== -1
@@ -41,7 +47,7 @@ ApplicationWindow {
Shortcut {
sequence: StandardKey.HelpContents
- onActivated: help()
+ onActivated: window.help()
}
Action {
@@ -72,14 +78,16 @@ ApplicationWindow {
RowLayout {
spacing: 20
anchors.fill: parent
+ anchors.leftMargin: !window.portraitMode ? drawer.width : undefined
ToolButton {
action: navigateBackAction
+ visible: window.portraitMode
}
Label {
id: titleLabel
- text: listView.currentItem ? listView.currentItem.text : "Gallery"
+ text: listView.currentItem ? (listView.currentItem as ItemDelegate).text : qsTr("Gallery")
font.pixelSize: 20
elide: Label.ElideRight
horizontalAlignment: Qt.AlignHCenter
@@ -96,15 +104,15 @@ ApplicationWindow {
transformOrigin: Menu.TopRight
Action {
- text: "Settings"
+ text: qsTr("Settings")
onTriggered: settingsDialog.open()
}
Action {
- text: "Help"
- onTriggered: help()
+ text: qsTr("Help")
+ onTriggered: window.help()
}
Action {
- text: "About"
+ text: qsTr("About")
onTriggered: aboutDialog.open()
}
}
@@ -114,9 +122,13 @@ ApplicationWindow {
Drawer {
id: drawer
+
width: Math.min(window.width, window.height) / 3 * 2
height: window.height
- interactive: stackView.depth === 1
+ modal: window.portraitMode
+ interactive: window.portraitMode ? (stackView.depth === 1) : false
+ position: window.portraitMode ? 0 : 1
+ visible: !window.portraitMode
ListView {
id: listView
@@ -125,53 +137,63 @@ ApplicationWindow {
currentIndex: -1
anchors.fill: parent
+ model: ListModel {
+ ListElement { title: qsTr("BusyIndicator"); source: "qrc:/pages/BusyIndicatorPage.qml" }
+ ListElement { title: qsTr("Button"); source: "qrc:/pages/ButtonPage.qml" }
+ ListElement { title: qsTr("CheckBox"); source: "qrc:/pages/CheckBoxPage.qml" }
+ ListElement { title: qsTr("ComboBox"); source: "qrc:/pages/ComboBoxPage.qml" }
+ ListElement { title: qsTr("DelayButton"); source: "qrc:/pages/DelayButtonPage.qml" }
+ ListElement { title: qsTr("Dial"); source: "qrc:/pages/DialPage.qml" }
+ ListElement { title: qsTr("Dialog"); source: "qrc:/pages/DialogPage.qml" }
+ ListElement { title: qsTr("Delegates"); source: "qrc:/pages/DelegatePage.qml" }
+ ListElement { title: qsTr("Frame"); source: "qrc:/pages/FramePage.qml" }
+ ListElement { title: qsTr("GroupBox"); source: "qrc:/pages/GroupBoxPage.qml" }
+ ListElement { title: qsTr("PageIndicator"); source: "qrc:/pages/PageIndicatorPage.qml" }
+ ListElement { title: qsTr("ProgressBar"); source: "qrc:/pages/ProgressBarPage.qml" }
+ ListElement { title: qsTr("RadioButton"); source: "qrc:/pages/RadioButtonPage.qml" }
+ ListElement { title: qsTr("RangeSlider"); source: "qrc:/pages/RangeSliderPage.qml" }
+ ListElement { title: qsTr("ScrollBar"); source: "qrc:/pages/ScrollBarPage.qml" }
+ ListElement { title: qsTr("ScrollIndicator"); source: "qrc:/pages/ScrollIndicatorPage.qml" }
+ ListElement { title: qsTr("Slider"); source: "qrc:/pages/SliderPage.qml" }
+ ListElement { title: qsTr("SpinBox"); source: "qrc:/pages/SpinBoxPage.qml" }
+ ListElement { title: qsTr("StackView"); source: "qrc:/pages/StackViewPage.qml" }
+ ListElement { title: qsTr("SwipeView"); source: "qrc:/pages/SwipeViewPage.qml" }
+ ListElement { title: qsTr("Switch"); source: "qrc:/pages/SwitchPage.qml" }
+ ListElement { title: qsTr("TabBar"); source: "qrc:/pages/TabBarPage.qml" }
+ ListElement { title: qsTr("TextArea"); source: "qrc:/pages/TextAreaPage.qml" }
+ ListElement { title: qsTr("TextField"); source: "qrc:/pages/TextFieldPage.qml" }
+ ListElement { title: qsTr("ToolTip"); source: "qrc:/pages/ToolTipPage.qml" }
+ ListElement { title: qsTr("Tumbler"); source: "qrc:/pages/TumblerPage.qml" }
+ }
+
delegate: ItemDelegate {
- width: listView.width
- text: model.title
+ id: delegateItem
+ width: ListView.view.width
+ text: title
highlighted: ListView.isCurrentItem
+
+ required property int index
+ required property var model
+ required property string title
+ required property string source
+
onClicked: {
listView.currentIndex = index
- stackView.push(model.source)
- drawer.close()
+ stackView.push(source)
+ if (window.portraitMode)
+ drawer.close()
}
}
- model: ListModel {
- ListElement { title: "BusyIndicator"; source: "qrc:/pages/BusyIndicatorPage.qml" }
- ListElement { title: "Button"; source: "qrc:/pages/ButtonPage.qml" }
- ListElement { title: "CheckBox"; source: "qrc:/pages/CheckBoxPage.qml" }
- ListElement { title: "ComboBox"; source: "qrc:/pages/ComboBoxPage.qml" }
- ListElement { title: "DelayButton"; source: "qrc:/pages/DelayButtonPage.qml" }
- ListElement { title: "Dial"; source: "qrc:/pages/DialPage.qml" }
- ListElement { title: "Dialog"; source: "qrc:/pages/DialogPage.qml" }
- ListElement { title: "Delegates"; source: "qrc:/pages/DelegatePage.qml" }
- ListElement { title: "Frame"; source: "qrc:/pages/FramePage.qml" }
- ListElement { title: "GroupBox"; source: "qrc:/pages/GroupBoxPage.qml" }
- ListElement { title: "PageIndicator"; source: "qrc:/pages/PageIndicatorPage.qml" }
- ListElement { title: "ProgressBar"; source: "qrc:/pages/ProgressBarPage.qml" }
- ListElement { title: "RadioButton"; source: "qrc:/pages/RadioButtonPage.qml" }
- ListElement { title: "RangeSlider"; source: "qrc:/pages/RangeSliderPage.qml" }
- ListElement { title: "ScrollBar"; source: "qrc:/pages/ScrollBarPage.qml" }
- ListElement { title: "ScrollIndicator"; source: "qrc:/pages/ScrollIndicatorPage.qml" }
- ListElement { title: "Slider"; source: "qrc:/pages/SliderPage.qml" }
- ListElement { title: "SpinBox"; source: "qrc:/pages/SpinBoxPage.qml" }
- ListElement { title: "StackView"; source: "qrc:/pages/StackViewPage.qml" }
- ListElement { title: "SwipeView"; source: "qrc:/pages/SwipeViewPage.qml" }
- ListElement { title: "Switch"; source: "qrc:/pages/SwitchPage.qml" }
- ListElement { title: "TabBar"; source: "qrc:/pages/TabBarPage.qml" }
- ListElement { title: "TextArea"; source: "qrc:/pages/TextAreaPage.qml" }
- ListElement { title: "TextField"; source: "qrc:/pages/TextFieldPage.qml" }
- ListElement { title: "ToolTip"; source: "qrc:/pages/ToolTipPage.qml" }
- ListElement { title: "Tumbler"; source: "qrc:/pages/TumblerPage.qml" }
- }
-
ScrollIndicator.vertical: ScrollIndicator { }
}
}
StackView {
id: stackView
+
anchors.fill: parent
+ anchors.leftMargin: !window.portraitMode ? drawer.width : undefined
initialItem: Pane {
id: pane
@@ -187,7 +209,7 @@ ApplicationWindow {
}
Label {
- text: "Qt Quick Controls provides a set of controls that can be used to build complete interfaces in Qt Quick."
+ text: qsTr("Qt Quick Controls provides a set of controls that can be used to build complete interfaces in Qt Quick.")
anchors.margins: 20
anchors.top: logo.bottom
anchors.left: parent.left
@@ -203,6 +225,7 @@ ApplicationWindow {
source: "images/arrow.png"
anchors.left: parent.left
anchors.bottom: parent.bottom
+ visible: window.portraitMode
}
}
}
@@ -214,7 +237,7 @@ ApplicationWindow {
width: Math.round(Math.min(window.width, window.height) / 3 * 2)
modal: true
focus: true
- title: "Settings"
+ title: qsTr("Settings")
standardButtons: Dialog.Ok | Dialog.Cancel
onAccepted: {
@@ -234,7 +257,7 @@ ApplicationWindow {
spacing: 10
Label {
- text: "Style:"
+ text: qsTr("Style:")
}
ComboBox {
@@ -250,8 +273,15 @@ ApplicationWindow {
}
}
+ CheckBox {
+ id: orientationCheckBox
+ text: qsTr("Enable Landscape")
+ checked: false
+ Layout.fillWidth: true
+ }
+
Label {
- text: "Restart required"
+ text: qsTr("Restart required")
color: "#e41e25"
opacity: styleBox.currentIndex !== styleBox.styleIndex ? 1.0 : 0.0
horizontalAlignment: Label.AlignHCenter
@@ -266,7 +296,7 @@ ApplicationWindow {
id: aboutDialog
modal: true
focus: true
- title: "About"
+ title: qsTr("About")
x: (window.width - width) / 2
y: window.height / 6
width: Math.min(window.width, window.height) / 3 * 2
@@ -278,15 +308,15 @@ ApplicationWindow {
Label {
width: aboutDialog.availableWidth
- text: "The Qt Quick Controls module delivers the next generation user interface controls based on Qt Quick."
+ text: qsTr("The Qt Quick Controls module delivers the next generation user interface controls based on Qt Quick.")
wrapMode: Label.Wrap
font.pixelSize: 12
}
Label {
width: aboutDialog.availableWidth
- text: "In comparison to Qt Quick Controls 1, Qt Quick Controls "
- + "are an order of magnitude simpler, lighter, and faster."
+ text: qsTr("In comparison to Qt Quick Controls 1, Qt Quick Controls "
+ + "are an order of magnitude simpler, lighter, and faster.")
wrapMode: Label.Wrap
font.pixelSize: 12
}
diff --git a/examples/quickcontrols/gallery/images/qt-logo.png b/examples/quickcontrols/gallery/images/qt-logo.png
index dff7729515..05684292b8 100644
--- a/examples/quickcontrols/gallery/images/qt-logo.png
+++ b/examples/quickcontrols/gallery/images/qt-logo.png
Binary files differ
diff --git a/examples/quickcontrols/gallery/images/qt-logo@2x.png b/examples/quickcontrols/gallery/images/qt-logo@2x.png
index dbd73aab77..2b7683ca0f 100644
--- a/examples/quickcontrols/gallery/images/qt-logo@2x.png
+++ b/examples/quickcontrols/gallery/images/qt-logo@2x.png
Binary files differ
diff --git a/examples/quickcontrols/gallery/images/qt-logo@3x.png b/examples/quickcontrols/gallery/images/qt-logo@3x.png
index 68e763b597..7cc98348d9 100644
--- a/examples/quickcontrols/gallery/images/qt-logo@3x.png
+++ b/examples/quickcontrols/gallery/images/qt-logo@3x.png
Binary files differ
diff --git a/examples/quickcontrols/gallery/images/qt-logo@4x.png b/examples/quickcontrols/gallery/images/qt-logo@4x.png
index 08fd882572..0e1bc9d93c 100644
--- a/examples/quickcontrols/gallery/images/qt-logo@4x.png
+++ b/examples/quickcontrols/gallery/images/qt-logo@4x.png
Binary files differ
diff --git a/examples/quickcontrols/gallery/pages/BusyIndicatorPage.qml b/examples/quickcontrols/gallery/pages/BusyIndicatorPage.qml
index 616e645688..7cdc4b49f5 100644
--- a/examples/quickcontrols/gallery/pages/BusyIndicatorPage.qml
+++ b/examples/quickcontrols/gallery/pages/BusyIndicatorPage.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls
@@ -15,8 +17,8 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "BusyIndicator is used to indicate activity while content is being loaded,"
- + " or when the UI is blocked waiting for a resource to become available."
+ text: qsTr("BusyIndicator is used to indicate activity while content is being loaded,"
+ + " or when the UI is blocked waiting for a resource to become available.")
}
BusyIndicator {
diff --git a/examples/quickcontrols/gallery/pages/ButtonPage.qml b/examples/quickcontrols/gallery/pages/ButtonPage.qml
index 06051767e9..e23bfb8a89 100644
--- a/examples/quickcontrols/gallery/pages/ButtonPage.qml
+++ b/examples/quickcontrols/gallery/pages/ButtonPage.qml
@@ -16,8 +16,8 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "Button presents a push-button that can be pushed or clicked by the user. "
- + "Buttons are normally used to perform an action, or to answer a question."
+ text: qsTr("Button presents a push-button that can be pushed or clicked by the user. "
+ + "Buttons are normally used to perform an action, or to answer a question.")
}
ColumnLayout {
@@ -25,17 +25,17 @@ ScrollablePage {
anchors.horizontalCenter: parent.horizontalCenter
Button {
- text: "First"
+ text: qsTr("First")
Layout.fillWidth: true
}
Button {
id: button
- text: "Second"
+ text: qsTr("Second")
highlighted: true
Layout.fillWidth: true
}
Button {
- text: "Third"
+ text: qsTr("Third")
enabled: false
Layout.fillWidth: true
}
diff --git a/examples/quickcontrols/gallery/pages/CheckBoxPage.qml b/examples/quickcontrols/gallery/pages/CheckBoxPage.qml
index 003e44c82c..474bab37fd 100644
--- a/examples/quickcontrols/gallery/pages/CheckBoxPage.qml
+++ b/examples/quickcontrols/gallery/pages/CheckBoxPage.qml
@@ -15,8 +15,8 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "CheckBox presents an option button that can be toggled on or off. "
- + "Check boxes are typically used to select one or more options from a set of options."
+ text: qsTr("CheckBox presents an option button that can be toggled on or off. "
+ + "Check boxes are typically used to select one or more options from a set of options.")
}
Column {
@@ -24,14 +24,14 @@ ScrollablePage {
anchors.horizontalCenter: parent.horizontalCenter
CheckBox {
- text: "First"
+ text: qsTr("First")
checked: true
}
CheckBox {
- text: "Second"
+ text: qsTr("Second")
}
CheckBox {
- text: "Third"
+ text: qsTr("Third")
checked: true
enabled: false
}
diff --git a/examples/quickcontrols/gallery/pages/ComboBoxPage.qml b/examples/quickcontrols/gallery/pages/ComboBoxPage.qml
index 35c3d60d16..075859717e 100644
--- a/examples/quickcontrols/gallery/pages/ComboBoxPage.qml
+++ b/examples/quickcontrols/gallery/pages/ComboBoxPage.qml
@@ -15,12 +15,12 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "ComboBox is a combined button and popup list. It presents "
- + "a list of options to the user that occupies minimal screen space."
+ text: qsTr("ComboBox is a combined button and popup list. It presents "
+ + "a list of options to the user that occupies minimal screen space.")
}
ComboBox {
- model: ["First", "Second", "Third"]
+ model: [qsTr("First"), qsTr("Second"), qsTr("Third")]
anchors.horizontalCenter: parent.horizontalCenter
}
@@ -28,21 +28,22 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "ComboBox can be made \l editable. An editable combo box auto-"
- + "completes its text based on what is available in the model."
+ text: qsTr("ComboBox can be made editable. An editable combo box auto-"
+ + "completes its text based on what is available in the model.")
}
ComboBox {
+ id: comboBox
+
editable: true
model: ListModel {
- id: model
- ListElement { text: "Banana" }
- ListElement { text: "Apple" }
- ListElement { text: "Coconut" }
+ ListElement { text: qsTr("Banana") }
+ ListElement { text: qsTr("Apple") }
+ ListElement { text: qsTr("Coconut") }
}
onAccepted: {
if (find(editText) === -1)
- model.append({text: editText})
+ comboBox.model.append({text: comboBox.editText})
}
anchors.horizontalCenter: parent.horizontalCenter
}
diff --git a/examples/quickcontrols/gallery/pages/DelayButtonPage.qml b/examples/quickcontrols/gallery/pages/DelayButtonPage.qml
index 4c0e8725b9..7e4470bf34 100644
--- a/examples/quickcontrols/gallery/pages/DelayButtonPage.qml
+++ b/examples/quickcontrols/gallery/pages/DelayButtonPage.qml
@@ -15,12 +15,12 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "DelayButton is a checkable button that incorporates a delay before the "
- + "button is activated. This delay prevents accidental presses."
+ text: qsTr("DelayButton is a checkable button that incorporates a delay before the "
+ + "button is activated. This delay prevents accidental presses.")
}
DelayButton {
- text: "DelayButton"
+ text: qsTr("DelayButton")
anchors.horizontalCenter: parent.horizontalCenter
}
}
diff --git a/examples/quickcontrols/gallery/pages/DelegatePage.qml b/examples/quickcontrols/gallery/pages/DelegatePage.qml
index c57388cc6a..519fffc6d2 100644
--- a/examples/quickcontrols/gallery/pages/DelegatePage.qml
+++ b/examples/quickcontrols/gallery/pages/DelegatePage.qml
@@ -1,94 +1,14 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
Pane {
- padding: 0
-
- property var delegateComponentMap: {
- "ItemDelegate": itemDelegateComponent,
- "SwipeDelegate": swipeDelegateComponent,
- "CheckDelegate": checkDelegateComponent,
- "RadioDelegate": radioDelegateComponent,
- "SwitchDelegate": switchDelegateComponent
- }
-
- Component {
- id: itemDelegateComponent
-
- ItemDelegate {
- text: labelText
- width: parent.width
- }
- }
-
- Component {
- id: swipeDelegateComponent
-
- SwipeDelegate {
- id: swipeDelegate
- text: labelText
- width: parent.width
-
- Component {
- id: removeComponent
-
- Rectangle {
- color: SwipeDelegate.pressed ? "#333" : "#444"
- width: parent.width
- height: parent.height
- clip: true
-
- SwipeDelegate.onClicked: view.model.remove(ourIndex)
-
- Label {
- font.pixelSize: swipeDelegate.font.pixelSize
- text: "Remove"
- color: "white"
- anchors.centerIn: parent
- }
- }
- }
-
- swipe.left: removeComponent
- swipe.right: removeComponent
- }
- }
-
- Component {
- id: checkDelegateComponent
-
- CheckDelegate {
- text: labelText
- }
- }
-
- ButtonGroup {
- id: radioButtonGroup
- }
-
- Component {
- id: radioDelegateComponent
-
- RadioDelegate {
- text: labelText
- ButtonGroup.group: radioButtonGroup
- }
- }
-
- Component {
- id: switchDelegateComponent
-
- SwitchDelegate {
- text: labelText
- }
- }
-
ColumnLayout {
- id: column
spacing: 40
anchors.fill: parent
anchors.topMargin: 20
@@ -97,76 +17,162 @@ Pane {
Layout.fillWidth: true
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "Delegate controls are used as delegates in views such as ListView."
+ text: qsTr("Delegate controls are used as delegates in views such as ListView.")
+ }
+
+ ButtonGroup {
+ id: radioButtonGroup
}
ListView {
id: listView
- Layout.fillWidth: true
- Layout.fillHeight: true
clip: true
- model: ListModel {
- ListElement { type: "ItemDelegate"; text: "ItemDelegate" }
- ListElement { type: "ItemDelegate"; text: "ItemDelegate" }
- ListElement { type: "ItemDelegate"; text: "ItemDelegate" }
- ListElement { type: "SwipeDelegate"; text: "SwipeDelegate" }
- ListElement { type: "SwipeDelegate"; text: "SwipeDelegate" }
- ListElement { type: "SwipeDelegate"; text: "SwipeDelegate" }
- ListElement { type: "CheckDelegate"; text: "CheckDelegate" }
- ListElement { type: "CheckDelegate"; text: "CheckDelegate" }
- ListElement { type: "CheckDelegate"; text: "CheckDelegate" }
- ListElement { type: "RadioDelegate"; text: "RadioDelegate" }
- ListElement { type: "RadioDelegate"; text: "RadioDelegate" }
- ListElement { type: "RadioDelegate"; text: "RadioDelegate" }
- ListElement { type: "SwitchDelegate"; text: "SwitchDelegate" }
- ListElement { type: "SwitchDelegate"; text: "SwitchDelegate" }
- ListElement { type: "SwitchDelegate"; text: "SwitchDelegate" }
- }
-
section.property: "type"
section.delegate: Pane {
- width: listView.width
+ id: sectionPane
+ required property string section
+ width: ListView.view.width
height: sectionLabel.implicitHeight + 20
-
Label {
id: sectionLabel
- text: section
+ text: sectionPane.section
anchors.centerIn: parent
}
}
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ model: ListModel {
+ ListElement { type: "ItemDelegate"; value: qsTr("ItemDelegate1") }
+ ListElement { type: "ItemDelegate"; value: qsTr("ItemDelegate2") }
+ ListElement { type: "ItemDelegate"; value: qsTr("ItemDelegate3") }
+ ListElement { type: "SwipeDelegate"; value: qsTr("SwipeDelegate1") }
+ ListElement { type: "SwipeDelegate"; value: qsTr("SwipeDelegate2") }
+ ListElement { type: "SwipeDelegate"; value: qsTr("SwipeDelegate3") }
+ ListElement { type: "CheckDelegate"; value: qsTr("CheckDelegate1") }
+ ListElement { type: "CheckDelegate"; value: qsTr("CheckDelegate2") }
+ ListElement { type: "CheckDelegate"; value: qsTr("CheckDelegate3") }
+ ListElement { type: "RadioDelegate"; value: qsTr("RadioDelegate1") }
+ ListElement { type: "RadioDelegate"; value: qsTr("RadioDelegate2") }
+ ListElement { type: "RadioDelegate"; value: qsTr("RadioDelegate3") }
+ ListElement { type: "SwitchDelegate"; value: qsTr("SwitchDelegate1") }
+ ListElement { type: "SwitchDelegate"; value: qsTr("SwitchDelegate2") }
+ ListElement { type: "SwitchDelegate"; value: qsTr("SwitchDelegate3") }
+ }
+
delegate: Loader {
id: delegateLoader
- width: listView.width
- sourceComponent: delegateComponentMap[text]
+ width: ListView.view.width
+ sourceComponent: delegateComponentMap[type]
+
+ required property string value
+ required property string type
+ required property var model
+ required property int index
- property string labelText: text
property ListView view: listView
- property int ourIndex: index
- // Can't find a way to do this in the SwipeDelegate component itself,
- // so do it here instead.
- SequentialAnimation {
- id: removeAnimation
+ readonly property var delegateComponentMap: {
+ "ItemDelegate": itemDelegateComponent,
+ "SwipeDelegate": swipeDelegateComponent,
+ "CheckDelegate": checkDelegateComponent,
+ "RadioDelegate": radioDelegateComponent,
+ "SwitchDelegate": switchDelegateComponent
+ }
+
+ Component {
+ id: itemDelegateComponent
+
+ ItemDelegate {
+ text: delegateLoader.value
+ width: delegateLoader.width
+ }
+ }
+
+ Component {
+ id: swipeDelegateComponent
+
+ SwipeDelegate {
+ id: swipeDelegate
+ text: delegateLoader.value
+ width: delegateLoader.width
+
+ Component {
+ id: removeComponent
+
+ Rectangle {
+ color: SwipeDelegate.pressed ? "#333" : "#444"
+ width: parent.width
+ height: parent.height
+ clip: true
+
+ SwipeDelegate.onClicked: {
+ if (delegateLoader.view !== undefined)
+ delegateLoader.view.model.remove(delegateLoader.index)
+ }
+
+ Label {
+ font.pixelSize: swipeDelegate.font.pixelSize
+ text: qsTr("Remove")
+ color: "white"
+ anchors.centerIn: parent
+ }
+ }
+ }
+
+ SequentialAnimation {
+ id: removeAnimation
+
+ PropertyAction {
+ target: delegateLoader
+ property: "ListView.delayRemove"
+ value: true
+ }
+ NumberAnimation {
+ target: swipeDelegate
+ property: "height"
+ to: 0
+ easing.type: Easing.InOutQuad
+ }
+ PropertyAction {
+ target: delegateLoader
+ property: "ListView.delayRemove"
+ value: false
+ }
+ }
+
+ swipe.left: removeComponent
+ swipe.right: removeComponent
+ ListView.onRemove: removeAnimation.start()
+ }
+ }
- PropertyAction {
- target: delegateLoader
- property: "ListView.delayRemove"
- value: true
+ Component {
+ id: checkDelegateComponent
+
+ CheckDelegate {
+ text: delegateLoader.value
}
- NumberAnimation {
- target: item
- property: "height"
- to: 0
- easing.type: Easing.InOutQuad
+ }
+
+ Component {
+ id: radioDelegateComponent
+
+ RadioDelegate {
+ text: delegateLoader.value
+
+ ButtonGroup.group: radioButtonGroup
}
- PropertyAction {
- target: delegateLoader
- property: "ListView.delayRemove"
- value: false
+ }
+
+ Component {
+ id: switchDelegateComponent
+
+ SwitchDelegate {
+ text: delegateLoader.value
}
}
- ListView.onRemove: removeAnimation.start()
}
}
}
diff --git a/examples/quickcontrols/gallery/pages/DialPage.qml b/examples/quickcontrols/gallery/pages/DialPage.qml
index 17c9e090ba..6de9703689 100644
--- a/examples/quickcontrols/gallery/pages/DialPage.qml
+++ b/examples/quickcontrols/gallery/pages/DialPage.qml
@@ -15,8 +15,8 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "The Dial is similar to a traditional dial knob that is found on devices such as "
- + "stereos or industrial equipment. It allows the user to specify a value within a range."
+ text: qsTr("The Dial is similar to a traditional dial knob that is found on devices such as "
+ + "stereos or industrial equipment. It allows the user to specify a value within a range.")
}
Dial {
diff --git a/examples/quickcontrols/gallery/pages/DialogPage.qml b/examples/quickcontrols/gallery/pages/DialogPage.qml
index 982a7f38d4..d07fa8be54 100644
--- a/examples/quickcontrols/gallery/pages/DialogPage.qml
+++ b/examples/quickcontrols/gallery/pages/DialogPage.qml
@@ -18,14 +18,14 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "Dialog is a popup that is mostly used for short-term tasks "
- + "and brief communications with the user."
+ text: qsTr("Dialog is a popup that is mostly used for short-term tasks "
+ + "and brief communications with the user.")
}
Button {
- text: "Message"
+ text: qsTr("Message")
anchors.horizontalCenter: parent.horizontalCenter
- width: buttonWidth
+ width: page.buttonWidth
onClicked: messageDialog.open()
Dialog {
@@ -34,19 +34,19 @@ ScrollablePage {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
- title: "Message"
+ title: qsTr("Message")
Label {
- text: "Lorem ipsum dolor sit amet..."
+ text: qsTr("Lorem ipsum dolor sit amet...")
}
}
}
Button {
id: button
- text: "Confirmation"
+ text: qsTr("Confirmation")
anchors.horizontalCenter: parent.horizontalCenter
- width: buttonWidth
+ width: page.buttonWidth
onClicked: confirmationDialog.open()
Dialog {
@@ -57,17 +57,17 @@ ScrollablePage {
parent: Overlay.overlay
modal: true
- title: "Confirmation"
+ title: qsTr("Confirmation")
standardButtons: Dialog.Yes | Dialog.No
Column {
spacing: 20
anchors.fill: parent
Label {
- text: "The document has been modified.\nDo you want to save your changes?"
+ text: qsTr("The document has been modified.\nDo you want to save your changes?")
}
CheckBox {
- text: "Do not ask again"
+ text: qsTr("Do not ask again")
anchors.right: parent.right
}
}
@@ -75,9 +75,9 @@ ScrollablePage {
}
Button {
- text: "Content"
+ text: qsTr("Content")
anchors.horizontalCenter: parent.horizontalCenter
- width: buttonWidth
+ width: page.buttonWidth
onClicked: contentDialog.open()
Dialog {
@@ -90,7 +90,7 @@ ScrollablePage {
parent: Overlay.overlay
modal: true
- title: "Content"
+ title: qsTr("Content")
standardButtons: Dialog.Close
Flickable {
@@ -114,13 +114,13 @@ ScrollablePage {
Label {
width: parent.width
- text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc finibus "
+ text: qsTr("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc finibus "
+ "in est quis laoreet. Interdum et malesuada fames ac ante ipsum primis "
+ "in faucibus. Curabitur eget justo sollicitudin enim faucibus bibendum. "
+ "Suspendisse potenti. Vestibulum cursus consequat mauris id sollicitudin. "
+ "Duis facilisis hendrerit consectetur. Curabitur sapien tortor, efficitur "
+ "id auctor nec, efficitur et nisl. Ut venenatis eros in nunc placerat, "
- + "eu aliquam enim suscipit."
+ + "eu aliquam enim suscipit.")
wrapMode: Label.Wrap
}
}
@@ -137,9 +137,9 @@ ScrollablePage {
}
Button {
- text: "Input"
+ text: qsTr("Input")
anchors.horizontalCenter: parent.horizontalCenter
- width: buttonWidth
+ width: page.buttonWidth
onClicked: inputDialog.open()
Dialog {
@@ -151,7 +151,7 @@ ScrollablePage {
focus: true
modal: true
- title: "Input"
+ title: qsTr("Input")
standardButtons: Dialog.Ok | Dialog.Cancel
ColumnLayout {
@@ -159,16 +159,16 @@ ScrollablePage {
anchors.fill: parent
Label {
elide: Label.ElideRight
- text: "Please enter the credentials:"
+ text: qsTr("Please enter the credentials:")
Layout.fillWidth: true
}
TextField {
focus: true
- placeholderText: "Username"
+ placeholderText: qsTr("Username")
Layout.fillWidth: true
}
TextField {
- placeholderText: "Password"
+ placeholderText: qsTr("Password")
echoMode: TextField.PasswordEchoOnEdit
Layout.fillWidth: true
}
diff --git a/examples/quickcontrols/gallery/pages/FramePage.qml b/examples/quickcontrols/gallery/pages/FramePage.qml
index 8526442537..b32d8af00c 100644
--- a/examples/quickcontrols/gallery/pages/FramePage.qml
+++ b/examples/quickcontrols/gallery/pages/FramePage.qml
@@ -17,7 +17,7 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "Frame is used to layout a logical group of controls together, within a visual frame."
+ text: qsTr("Frame is used to layout a logical group of controls together, within a visual frame.")
}
Frame {
@@ -28,17 +28,17 @@ ScrollablePage {
width: page.itemWidth
RadioButton {
- text: "First"
+ text: qsTr("First")
checked: true
width: parent.width
}
RadioButton {
id: button
- text: "Second"
+ text: qsTr("Second")
width: parent.width
}
RadioButton {
- text: "Third"
+ text: qsTr("Third")
width: parent.width
}
}
diff --git a/examples/quickcontrols/gallery/pages/GroupBoxPage.qml b/examples/quickcontrols/gallery/pages/GroupBoxPage.qml
index 9e24d8e6ca..a30adf8a8f 100644
--- a/examples/quickcontrols/gallery/pages/GroupBoxPage.qml
+++ b/examples/quickcontrols/gallery/pages/GroupBoxPage.qml
@@ -17,11 +17,11 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "A GroupBox provides a frame, a title on top of it, and a logical group of controls within that frame."
+ text: qsTr("A GroupBox provides a frame, a title on top of it, and a logical group of controls within that frame.")
}
GroupBox {
- title: "Title"
+ title: qsTr("Title")
anchors.horizontalCenter: parent.horizontalCenter
Column {
@@ -29,17 +29,17 @@ ScrollablePage {
width: page.itemWidth
RadioButton {
- text: "First"
+ text: qsTr("First")
checked: true
width: parent.width
}
RadioButton {
id: button
- text: "Second"
+ text: qsTr("Second")
width: parent.width
}
RadioButton {
- text: "Third"
+ text: qsTr("Third")
width: parent.width
}
}
diff --git a/examples/quickcontrols/gallery/pages/PageIndicatorPage.qml b/examples/quickcontrols/gallery/pages/PageIndicatorPage.qml
index e83c86563e..13620c1217 100644
--- a/examples/quickcontrols/gallery/pages/PageIndicatorPage.qml
+++ b/examples/quickcontrols/gallery/pages/PageIndicatorPage.qml
@@ -15,7 +15,7 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "PageIndicator is used to indicate the currently active page in a container of pages."
+ text: qsTr("PageIndicator is used to indicate the currently active page in a container of pages.")
}
PageIndicator {
diff --git a/examples/quickcontrols/gallery/pages/ProgressBarPage.qml b/examples/quickcontrols/gallery/pages/ProgressBarPage.qml
index d712aae10c..2a3f7158e1 100644
--- a/examples/quickcontrols/gallery/pages/ProgressBarPage.qml
+++ b/examples/quickcontrols/gallery/pages/ProgressBarPage.qml
@@ -15,8 +15,8 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "ProgressBar indicates the progress of an operation. It can be set in an "
- + "indeterminate mode to indicate that the length of the operation is unknown."
+ text: qsTr("ProgressBar indicates the progress of an operation. It can be set in an "
+ + "indeterminate mode to indicate that the length of the operation is unknown.")
}
ProgressBar {
diff --git a/examples/quickcontrols/gallery/pages/RadioButtonPage.qml b/examples/quickcontrols/gallery/pages/RadioButtonPage.qml
index 644543c007..6939d47dba 100644
--- a/examples/quickcontrols/gallery/pages/RadioButtonPage.qml
+++ b/examples/quickcontrols/gallery/pages/RadioButtonPage.qml
@@ -15,8 +15,8 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "RadioButton presents an option button that can be toggled on or off. "
- + "Radio buttons are typically used to select one option from a set of options."
+ text: qsTr("RadioButton presents an option button that can be toggled on or off. "
+ + "Radio buttons are typically used to select one option from a set of options.")
}
Column {
@@ -24,14 +24,14 @@ ScrollablePage {
anchors.horizontalCenter: parent.horizontalCenter
RadioButton {
- text: "First"
+ text: qsTr("First")
}
RadioButton {
- text: "Second"
+ text: qsTr("Second")
checked: true
}
RadioButton {
- text: "Third"
+ text: qsTr("Third")
enabled: false
}
}
diff --git a/examples/quickcontrols/gallery/pages/RangeSliderPage.qml b/examples/quickcontrols/gallery/pages/RangeSliderPage.qml
index 0ca235822f..03b78040a0 100644
--- a/examples/quickcontrols/gallery/pages/RangeSliderPage.qml
+++ b/examples/quickcontrols/gallery/pages/RangeSliderPage.qml
@@ -15,7 +15,7 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "RangeSlider is used to select a range specified by two values, by sliding each handle along a track."
+ text: qsTr("RangeSlider is used to select a range specified by two values, by sliding each handle along a track.")
}
RangeSlider {
diff --git a/examples/quickcontrols/gallery/pages/ScrollBarPage.qml b/examples/quickcontrols/gallery/pages/ScrollBarPage.qml
index 248e74ca7f..cb23c7e580 100644
--- a/examples/quickcontrols/gallery/pages/ScrollBarPage.qml
+++ b/examples/quickcontrols/gallery/pages/ScrollBarPage.qml
@@ -23,9 +23,9 @@ Flickable {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "ScrollBar is an interactive bar that can be used to scroll to a specific position. "
+ text: qsTr("ScrollBar is an interactive bar that can be used to scroll to a specific position. "
+ "A scroll bar can be either vertical or horizontal, and can be attached to any Flickable, "
- + "such as ListView and GridView."
+ + "such as ListView and GridView.")
}
Image {
diff --git a/examples/quickcontrols/gallery/pages/ScrollIndicatorPage.qml b/examples/quickcontrols/gallery/pages/ScrollIndicatorPage.qml
index 04ce97483a..5e9c7f5240 100644
--- a/examples/quickcontrols/gallery/pages/ScrollIndicatorPage.qml
+++ b/examples/quickcontrols/gallery/pages/ScrollIndicatorPage.qml
@@ -23,9 +23,9 @@ Flickable {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "ScrollIndicator is a non-interactive indicator that indicates the current scroll position. "
+ text: qsTr("ScrollIndicator is a non-interactive indicator that indicates the current scroll position. "
+ "A scroll indicator can be either vertical or horizontal, and can be attached to any Flickable, "
- + "such as ListView and GridView."
+ + "such as ListView and GridView.")
}
Image {
diff --git a/examples/quickcontrols/gallery/pages/SliderPage.qml b/examples/quickcontrols/gallery/pages/SliderPage.qml
index fd03680a0e..bdcfed9fad 100644
--- a/examples/quickcontrols/gallery/pages/SliderPage.qml
+++ b/examples/quickcontrols/gallery/pages/SliderPage.qml
@@ -15,7 +15,7 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "Slider is used to select a value by sliding a handle along a track."
+ text: qsTr("Slider is used to select a value by sliding a handle along a track.")
}
Slider {
diff --git a/examples/quickcontrols/gallery/pages/SpinBoxPage.qml b/examples/quickcontrols/gallery/pages/SpinBoxPage.qml
index 18c9b06c1f..538a7ef209 100644
--- a/examples/quickcontrols/gallery/pages/SpinBoxPage.qml
+++ b/examples/quickcontrols/gallery/pages/SpinBoxPage.qml
@@ -15,8 +15,8 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "SpinBox allows the user to choose an integer value by clicking the up or down indicator buttons, "
- + "by pressing up or down on the keyboard, or by entering a text value in the input field."
+ text: qsTr("SpinBox allows the user to choose an integer value by clicking the up or down indicator buttons, "
+ + "by pressing up or down on the keyboard, or by entering a text value in the input field.")
}
SpinBox {
diff --git a/examples/quickcontrols/gallery/pages/StackViewPage.qml b/examples/quickcontrols/gallery/pages/StackViewPage.qml
index 4472150e56..3df1dd114e 100644
--- a/examples/quickcontrols/gallery/pages/StackViewPage.qml
+++ b/examples/quickcontrols/gallery/pages/StackViewPage.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls
@@ -23,26 +25,33 @@ StackView {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "StackView provides a stack-based navigation model which can be used with a set of interlinked pages. "
+ text: qsTr("StackView provides a stack-based navigation model which can be used with a set of interlinked pages. "
+ "Items are pushed onto the stack as the user navigates deeper into the material, and popped off again "
- + "when he chooses to go back."
+ + "when he chooses to go back.")
}
Button {
id: button
- text: "Push"
+ text: qsTr("Push")
anchors.horizontalCenter: parent.horizontalCenter
width: Math.max(button.implicitWidth, Math.min(button.implicitWidth * 2, pane.availableWidth / 3))
onClicked: stackView.push(page)
}
Button {
- text: "Pop"
+ text: qsTr("Pop")
enabled: stackView.depth > 1
width: Math.max(button.implicitWidth, Math.min(button.implicitWidth * 2, pane.availableWidth / 3))
anchors.horizontalCenter: parent.horizontalCenter
onClicked: stackView.pop()
}
+
+ Label {
+ width: parent.width
+ wrapMode: Label.Wrap
+ horizontalAlignment: Qt.AlignHCenter
+ text: qsTr("Stack Depth:") + " " + stackView.depth
+ }
}
}
}
diff --git a/examples/quickcontrols/gallery/pages/SwipeViewPage.qml b/examples/quickcontrols/gallery/pages/SwipeViewPage.qml
index f982f4cf26..ebb2031ff3 100644
--- a/examples/quickcontrols/gallery/pages/SwipeViewPage.qml
+++ b/examples/quickcontrols/gallery/pages/SwipeViewPage.qml
@@ -16,8 +16,8 @@ Pane {
model: 3
Pane {
- width: view.width
- height: view.height
+ width: SwipeView.view.width
+ height: SwipeView.view.height
Column {
spacing: 40
@@ -27,8 +27,8 @@ Pane {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "SwipeView provides a navigation model that simplifies horizontal paged scrolling. "
- + "The page indicator on the bottom shows which is the presently active page."
+ text: qsTr("SwipeView provides a navigation model that simplifies horizontal paged scrolling. "
+ + "The page indicator on the bottom shows which is the presently active page.")
}
Image {
diff --git a/examples/quickcontrols/gallery/pages/SwitchPage.qml b/examples/quickcontrols/gallery/pages/SwitchPage.qml
index cca200582e..dbda7da0a2 100644
--- a/examples/quickcontrols/gallery/pages/SwitchPage.qml
+++ b/examples/quickcontrols/gallery/pages/SwitchPage.qml
@@ -15,8 +15,8 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "Switch is an option button that can be dragged or toggled on or off. "
- + "Switches are typically used to select between two states."
+ text: qsTr("Switch is an option button that can be dragged or toggled on or off. "
+ + "Switches are typically used to select between two states.")
}
Column {
@@ -24,14 +24,14 @@ ScrollablePage {
anchors.horizontalCenter: parent.horizontalCenter
Switch {
- text: "First"
+ text: qsTr("First")
}
Switch {
- text: "Second"
+ text: qsTr("Second")
checked: true
}
Switch {
- text: "Third"
+ text: qsTr("Third")
enabled: false
}
}
diff --git a/examples/quickcontrols/gallery/pages/TabBarPage.qml b/examples/quickcontrols/gallery/pages/TabBarPage.qml
index c03e4a014c..b2ce35586f 100644
--- a/examples/quickcontrols/gallery/pages/TabBarPage.qml
+++ b/examples/quickcontrols/gallery/pages/TabBarPage.qml
@@ -16,8 +16,8 @@ Page {
model: 3
Pane {
- width: swipeView.width
- height: swipeView.height
+ width: SwipeView.view.width
+ height: SwipeView.view.height
Column {
spacing: 40
@@ -27,8 +27,8 @@ Page {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "TabBar is a bar with icons or text which allows the user "
- + "to switch between different subtasks, views, or modes."
+ text: qsTr("TabBar is a bar with icons or text which allows the user "
+ + "to switch between different subtasks, views, or modes.")
}
Image {
@@ -45,13 +45,13 @@ Page {
currentIndex: swipeView.currentIndex
TabButton {
- text: "First"
+ text: qsTr("First")
}
TabButton {
- text: "Second"
+ text: qsTr("Second")
}
TabButton {
- text: "Third"
+ text: qsTr("Third")
}
}
}
diff --git a/examples/quickcontrols/gallery/pages/TextAreaPage.qml b/examples/quickcontrols/gallery/pages/TextAreaPage.qml
index 6a09846973..1cc7d7c194 100644
--- a/examples/quickcontrols/gallery/pages/TextAreaPage.qml
+++ b/examples/quickcontrols/gallery/pages/TextAreaPage.qml
@@ -15,15 +15,15 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "TextArea is a multi-line text editor."
+ text: qsTr("TextArea is a multi-line text editor.")
}
TextArea {
- width: pane.availableWidth / 3
+ width: page.availableWidth / 3
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: TextArea.Wrap
- text: "TextArea\n...\n...\n..."
+ text: qsTr("TextArea\n...\n...\n...")
}
}
}
diff --git a/examples/quickcontrols/gallery/pages/TextFieldPage.qml b/examples/quickcontrols/gallery/pages/TextFieldPage.qml
index 2b06894c8f..fd4c6894ac 100644
--- a/examples/quickcontrols/gallery/pages/TextFieldPage.qml
+++ b/examples/quickcontrols/gallery/pages/TextFieldPage.qml
@@ -15,12 +15,12 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "TextField is a single-line text editor."
+ text: qsTr("TextField is a single-line text editor.")
}
TextField {
id: field
- placeholderText: "TextField"
+ placeholderText: qsTr("TextField")
anchors.horizontalCenter: parent.horizontalCenter
}
}
diff --git a/examples/quickcontrols/gallery/pages/ToolTipPage.qml b/examples/quickcontrols/gallery/pages/ToolTipPage.qml
index dd92c89b4f..9a6cfc5729 100644
--- a/examples/quickcontrols/gallery/pages/ToolTipPage.qml
+++ b/examples/quickcontrols/gallery/pages/ToolTipPage.qml
@@ -15,16 +15,16 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "A tool tip is a short piece of text that informs the user of a control's function."
+ text: qsTr("A tool tip is a short piece of text that informs the user of a control's function.")
}
Button {
- text: "Tip"
+ text: qsTr("Tip")
anchors.horizontalCenter: parent.horizontalCenter
ToolTip.timeout: 5000
ToolTip.visible: pressed
- ToolTip.text: "This is a tool tip."
+ ToolTip.text: qsTr("This is a tool tip.")
}
}
}
diff --git a/examples/quickcontrols/gallery/pages/TumblerPage.qml b/examples/quickcontrols/gallery/pages/TumblerPage.qml
index 4d01f02e89..c8119bb76c 100644
--- a/examples/quickcontrols/gallery/pages/TumblerPage.qml
+++ b/examples/quickcontrols/gallery/pages/TumblerPage.qml
@@ -15,7 +15,7 @@ ScrollablePage {
width: parent.width
wrapMode: Label.Wrap
horizontalAlignment: Qt.AlignHCenter
- text: "Tumbler is used to select a value by spinning a wheel."
+ text: qsTr("Tumbler is used to select a value by spinning a wheel.")
}
Tumbler {
diff --git a/examples/quickcontrols/imagine/automotive/CMakeLists.txt b/examples/quickcontrols/imagine/automotive/CMakeLists.txt
index 94e79a10a8..161d4b92b6 100644
--- a/examples/quickcontrols/imagine/automotive/CMakeLists.txt
+++ b/examples/quickcontrols/imagine/automotive/CMakeLists.txt
@@ -1,17 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(automotive LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/imagine/automotive")
-
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
qt_add_executable(automotiveexample WIN32 MACOSX_BUNDLE
@@ -132,14 +126,23 @@ qt_add_qml_module(automotiveexample
)
target_link_libraries(automotiveexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickControls2
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
)
install(TARGETS automotiveexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET automotiveexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/imagine/automotive/doc/src/qtquickcontrols-automotive.qdoc b/examples/quickcontrols/imagine/automotive/doc/src/qtquickcontrols-automotive.qdoc
index 81503200f3..8ac7c9c5bd 100644
--- a/examples/quickcontrols/imagine/automotive/doc/src/qtquickcontrols-automotive.qdoc
+++ b/examples/quickcontrols/imagine/automotive/doc/src/qtquickcontrols-automotive.qdoc
@@ -7,6 +7,7 @@
\title Qt Quick Controls - Imagine Style Example: Automotive
\keyword Qt Quick Controls 2 - Imagine Style Example: Automotive
\ingroup qtquickcontrols-examples
+ \examplecategory {Graphics}
\brief An automotive user interface using custom Imagine style assets.
This example demonstrates how custom Imagine style assets can be used to
diff --git a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered.png b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered.png
index 26add20cfc..1f96090fcf 100644
--- a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered.png
+++ b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered@2x.png b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered@2x.png
index 01d8136d51..58b7516310 100644
--- a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered@2x.png
+++ b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-hovered@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed.png b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed.png
index 435acd1466..9d08faa0af 100644
--- a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed.png
+++ b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed@2x.png b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed@2x.png
index 9bab57904d..7e2c17236d 100644
--- a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed@2x.png
+++ b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background-pressed@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background.png b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background.png
index 8aab4d3280..384de617a2 100644
--- a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background.png
+++ b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background@2x.png b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background@2x.png
index a856971185..7874f03888 100644
--- a/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background@2x.png
+++ b/examples/quickcontrols/imagine/automotive/imagine-assets/dial-background@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/automotive/qml/automotive.qml b/examples/quickcontrols/imagine/automotive/qml/automotive.qml
index 5f7868e760..9ed2f5996d 100644
--- a/examples/quickcontrols/imagine/automotive/qml/automotive.qml
+++ b/examples/quickcontrols/imagine/automotive/qml/automotive.qml
@@ -113,7 +113,7 @@ ApplicationWindow {
Item {}
ColumnLayout {
- spacing: 16
+ spacing: 12
ButtonGroup {
id: viewButtonGroup
@@ -129,6 +129,7 @@ ApplicationWindow {
Button {
text: qsTr("Compact")
font.pixelSize: fontSizeExtraSmall
+ checkable: true
checked: true
Layout.fillWidth: true
@@ -156,13 +157,8 @@ ApplicationWindow {
stepSize: 1
Layout.alignment: Qt.AlignHCenter
- Layout.minimumWidth: 64
- Layout.minimumHeight: 64
Layout.preferredWidth: 128
Layout.preferredHeight: 128
- Layout.maximumWidth: 128
- Layout.maximumHeight: 128
- Layout.fillHeight: true
Label {
text: volumeDial.value.toFixed(0)
@@ -177,7 +173,7 @@ ApplicationWindow {
}
RowLayout {
- Layout.topMargin: 16
+ Layout.topMargin: 8
LargeLabel {
id: radioOption
@@ -235,18 +231,18 @@ ApplicationWindow {
}
model: ListModel {
- ListElement { name: "V-Radio"; frequency: "105.5 MHz" }
- ListElement { name: "World News"; frequency: "93.4 MHz" }
- ListElement { name: "TekStep FM"; frequency: "95.0 MHz" }
- ListElement { name: "Classic Radio"; frequency: "89.9 MHz" }
- ListElement { name: "Buena Vista FM"; frequency: "100.8 MHz" }
- ListElement { name: "Drive-by Radio"; frequency: "99.1 MHz" }
- ListElement { name: "Unknown #1"; frequency: "104.5 MHz" }
- ListElement { name: "Unknown #2"; frequency: "91.2 MHz" }
- ListElement { name: "Unknown #3"; frequency: "93.8 MHz" }
- ListElement { name: "Unknown #4"; frequency: "80.4 MHz" }
- ListElement { name: "Unknown #5"; frequency: "101.1 MHz" }
- ListElement { name: "Unknown #6"; frequency: "92.2 MHz" }
+ ListElement { name: "V-Radio"; frequency: "105.5" }
+ ListElement { name: "World News"; frequency: "93.4" }
+ ListElement { name: "TekStep FM"; frequency: "95.0" }
+ ListElement { name: "Classic Radio"; frequency: "89.9" }
+ ListElement { name: "Buena Vista FM"; frequency: "100.8" }
+ ListElement { name: "Drive-by Radio"; frequency: "99.1" }
+ ListElement { name: "Unknown #1"; frequency: "104.5" }
+ ListElement { name: "Unknown #2"; frequency: "91.2" }
+ ListElement { name: "Unknown #3"; frequency: "93.8" }
+ ListElement { name: "Unknown #4"; frequency: "80.4" }
+ ListElement { name: "Unknown #5"; frequency: "101.1" }
+ ListElement { name: "Unknown #6"; frequency: "92.2" }
}
delegate: ItemDelegate {
id: stationDelegate
@@ -262,12 +258,16 @@ ApplicationWindow {
text: model.name
font: stationDelegate.font
horizontalAlignment: Text.AlignLeft
+ elide: Text.ElideRight
+
Layout.fillWidth: true
}
Label {
text: model.frequency
font: stationDelegate.font
horizontalAlignment: Text.AlignRight
+ elide: Text.ElideRight
+
Layout.fillWidth: true
}
}
@@ -277,30 +277,32 @@ ApplicationWindow {
Frame {
Layout.fillWidth: true
+ Layout.fillHeight: false
- RowLayout {
+ ColumnLayout {
anchors.fill: parent
Label {
text: qsTr("Sort by")
font.pixelSize: fontSizeExtraSmall
-
- Layout.alignment: Qt.AlignTop
}
ColumnLayout {
RadioButton {
text: qsTr("Name")
font.pixelSize: fontSizeExtraSmall
+ Layout.fillWidth: false
}
RadioButton {
text: qsTr("Frequency")
font.pixelSize: fontSizeExtraSmall
+ Layout.fillWidth: false
}
RadioButton {
text: qsTr("Favourites")
font.pixelSize: fontSizeExtraSmall
checked: true
+ Layout.fillWidth: false
}
}
}
@@ -535,6 +537,7 @@ ApplicationWindow {
currentIndex: 1
Layout.fillHeight: true
+ Layout.fillWidth: false
ButtonGroup {
buttons: rightTabBarContentLayout.children
@@ -559,6 +562,7 @@ ApplicationWindow {
Layout.maximumHeight: navigationFeatureButton.height
Layout.fillHeight: true
+ Layout.fillWidth: false
}
FeatureButton {
text: qsTr("Air Con.")
@@ -567,6 +571,7 @@ ApplicationWindow {
Layout.maximumHeight: navigationFeatureButton.height
Layout.fillHeight: true
+ Layout.fillWidth: false
}
FeatureButton {
text: qsTr("Seats")
@@ -574,6 +579,7 @@ ApplicationWindow {
Layout.maximumHeight: navigationFeatureButton.height
Layout.fillHeight: true
+ Layout.fillWidth: false
}
FeatureButton {
text: qsTr("Statistics")
@@ -581,6 +587,7 @@ ApplicationWindow {
Layout.maximumHeight: navigationFeatureButton.height
Layout.fillHeight: true
+ Layout.fillWidth: false
}
}
}
diff --git a/examples/quickcontrols/imagine/automotive/qtquickcontrols2.conf b/examples/quickcontrols/imagine/automotive/qtquickcontrols2.conf
index c7fc7a6169..94601428d0 100644
--- a/examples/quickcontrols/imagine/automotive/qtquickcontrols2.conf
+++ b/examples/quickcontrols/imagine/automotive/qtquickcontrols2.conf
@@ -3,8 +3,6 @@ Style=Imagine
[Imagine]
Path=:/imagine-assets
-
-[Imagine\Palette]
-Text=#6affcd
-ButtonText=#6affcd
-WindowText=#6affcd
+Palette\Text=#6affcd
+Palette\ButtonText=#6affcd
+Palette\WindowText=#6affcd
diff --git a/examples/quickcontrols/imagine/musicplayer/CMakeLists.txt b/examples/quickcontrols/imagine/musicplayer/CMakeLists.txt
deleted file mode 100644
index cc3d5b27b0..0000000000
--- a/examples/quickcontrols/imagine/musicplayer/CMakeLists.txt
+++ /dev/null
@@ -1,187 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-project(musicplayer LANGUAGES CXX)
-
-set(CMAKE_AUTOMOC ON)
-
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/imagine/musicplayer")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
-
-qt_add_executable(musicplayerexample WIN32 MACOSX_BUNDLE
- musicplayer.cpp
-)
-
-qt_add_qml_module(musicplayerexample
- URI musicplayer
- NO_RESOURCE_TARGET_PATH
- QML_FILES
- "musicplayer.qml"
- RESOURCES
- "icons/musicplayer/32x32/bluetooth.png"
- "icons/musicplayer/32x32/cart.png"
- "icons/musicplayer/32x32/cloud.png"
- "icons/musicplayer/32x32/favorite.png"
- "icons/musicplayer/32x32/filter.png"
- "icons/musicplayer/32x32/folder.png"
- "icons/musicplayer/32x32/message.png"
- "icons/musicplayer/32x32/music.png"
- "icons/musicplayer/32x32/next.png"
- "icons/musicplayer/32x32/pause.png"
- "icons/musicplayer/32x32/power.png"
- "icons/musicplayer/32x32/previous.png"
- "icons/musicplayer/32x32/repeat.png"
- "icons/musicplayer/32x32/save.png"
- "icons/musicplayer/32x32/settings.png"
- "icons/musicplayer/32x32/shuffle.png"
- "icons/musicplayer/32x32/stop.png"
- "icons/musicplayer/32x32@2/bluetooth.png"
- "icons/musicplayer/32x32@2/cart.png"
- "icons/musicplayer/32x32@2/cloud.png"
- "icons/musicplayer/32x32@2/favorite.png"
- "icons/musicplayer/32x32@2/filter.png"
- "icons/musicplayer/32x32@2/folder.png"
- "icons/musicplayer/32x32@2/grid.png"
- "icons/musicplayer/32x32@2/message.png"
- "icons/musicplayer/32x32@2/music.png"
- "icons/musicplayer/32x32@2/next.png"
- "icons/musicplayer/32x32@2/pause.png"
- "icons/musicplayer/32x32@2/power.png"
- "icons/musicplayer/32x32@2/previous.png"
- "icons/musicplayer/32x32@2/repeat.png"
- "icons/musicplayer/32x32@2/save.png"
- "icons/musicplayer/32x32@2/settings.png"
- "icons/musicplayer/32x32@2/shuffle.png"
- "icons/musicplayer/32x32@2/stop.png"
- "icons/musicplayer/index.theme"
- "images/album-cover.jpg"
- "imagine-assets/applicationwindow-background.png"
- "imagine-assets/button-background-checked-hovered.9.png"
- "imagine-assets/button-background-checked-hovered@2x.9.png"
- "imagine-assets/button-background-checked.9.png"
- "imagine-assets/button-background-checked@2x.9.png"
- "imagine-assets/button-background-disabled.9.png"
- "imagine-assets/button-background-disabled@2x.9.png"
- "imagine-assets/button-background-hovered.9.png"
- "imagine-assets/button-background-hovered@2x.9.png"
- "imagine-assets/button-background-pressed.9.png"
- "imagine-assets/button-background-pressed@2x.9.png"
- "imagine-assets/button-background.9.png"
- "imagine-assets/button-background@2x.9.png"
- "imagine-assets/combobox-background-hovered.9.png"
- "imagine-assets/combobox-background-hovered@2x.9.png"
- "imagine-assets/combobox-background-open.9.png"
- "imagine-assets/combobox-background-open@2x.9.png"
- "imagine-assets/combobox-background-pressed.9.png"
- "imagine-assets/combobox-background-pressed@2x.9.png"
- "imagine-assets/combobox-background.9.png"
- "imagine-assets/combobox-background@2x.9.png"
- "imagine-assets/combobox-indicator-hovered.png"
- "imagine-assets/combobox-indicator-hovered@2x.png"
- "imagine-assets/combobox-indicator-open.png"
- "imagine-assets/combobox-indicator-open@2x.png"
- "imagine-assets/combobox-indicator-pressed.png"
- "imagine-assets/combobox-indicator-pressed@2x.png"
- "imagine-assets/combobox-indicator.png"
- "imagine-assets/combobox-indicator@2x.png"
- "imagine-assets/combobox-popup.9.png"
- "imagine-assets/combobox-popup@2x.9.png"
- "imagine-assets/dial-background-hovered.png"
- "imagine-assets/dial-background-hovered@2x.png"
- "imagine-assets/dial-background-pressed.png"
- "imagine-assets/dial-background-pressed@2x.png"
- "imagine-assets/dial-background.png"
- "imagine-assets/dial-background@2x.png"
- "imagine-assets/dial-handle-pressed.png"
- "imagine-assets/dial-handle-pressed@2x.png"
- "imagine-assets/dial-handle.png"
- "imagine-assets/dial-handle@2x.png"
- "imagine-assets/frame-background.9.png"
- "imagine-assets/frame-background@2x.9.png"
- "imagine-assets/itemdelegate-background-checked.9.png"
- "imagine-assets/itemdelegate-background-checked@2x.9.png"
- "imagine-assets/itemdelegate-background-disabled.9.png"
- "imagine-assets/itemdelegate-background-disabled@2x.9.png"
- "imagine-assets/itemdelegate-background-hovered.9.png"
- "imagine-assets/itemdelegate-background-hovered@2x.9.png"
- "imagine-assets/itemdelegate-background-pressed.9.png"
- "imagine-assets/itemdelegate-background-pressed@2x.9.png"
- "imagine-assets/itemdelegate-background.9.png"
- "imagine-assets/itemdelegate-background@2x.9.png"
- "imagine-assets/roundbutton-background-checked-hovered.png"
- "imagine-assets/roundbutton-background-checked-hovered@2x.png"
- "imagine-assets/roundbutton-background-checked.png"
- "imagine-assets/roundbutton-background-checked@2x.png"
- "imagine-assets/roundbutton-background-disabled.png"
- "imagine-assets/roundbutton-background-disabled@2x.png"
- "imagine-assets/roundbutton-background-hovered.png"
- "imagine-assets/roundbutton-background-hovered@2x.png"
- "imagine-assets/roundbutton-background-pressed.png"
- "imagine-assets/roundbutton-background-pressed@2x.png"
- "imagine-assets/roundbutton-background.png"
- "imagine-assets/roundbutton-background@2x.png"
- "imagine-assets/scrollbar-handle-interactive-hovered.png"
- "imagine-assets/scrollbar-handle-interactive-hovered@2x.png"
- "imagine-assets/scrollbar-handle-interactive-pressed.png"
- "imagine-assets/scrollbar-handle-interactive-pressed@2x.png"
- "imagine-assets/scrollbar-handle-interactive.png"
- "imagine-assets/scrollbar-handle-interactive@2x.png"
- "imagine-assets/slider-background-horizontal-disabled.9.png"
- "imagine-assets/slider-background-horizontal-disabled@2x.9.png"
- "imagine-assets/slider-background-horizontal.9.png"
- "imagine-assets/slider-background-horizontal@2x.9.png"
- "imagine-assets/slider-background-vertical-disabled.9.png"
- "imagine-assets/slider-background-vertical-disabled@2x.9.png"
- "imagine-assets/slider-background-vertical.9.png"
- "imagine-assets/slider-background-vertical@2x.9.png"
- "imagine-assets/slider-handle-disabled.png"
- "imagine-assets/slider-handle-disabled@2x.png"
- "imagine-assets/slider-handle-hovered.png"
- "imagine-assets/slider-handle-hovered@2x.png"
- "imagine-assets/slider-handle.png"
- "imagine-assets/slider-handle@2x.png"
- "imagine-assets/slider-progress-horizontal.9.png"
- "imagine-assets/slider-progress-horizontal@2x.9.png"
- "imagine-assets/slider-progress-vertical-disabled.9.png"
- "imagine-assets/slider-progress-vertical-disabled@2x.9.png"
- "imagine-assets/slider-progress-vertical.9.png"
- "imagine-assets/slider-progress-vertical@2x.9.png"
- "imagine-assets/textfield-background-disabled.9.png"
- "imagine-assets/textfield-background-disabled@2x.9.png"
- "imagine-assets/textfield-background.9.png"
- "imagine-assets/textfield-background@2x.9.png"
- "imagine-assets/toolbar-background.9.png"
- "imagine-assets/toolbar-background@2x.9.png"
- "imagine-assets/toolbutton-background-checked-hovered.9.png"
- "imagine-assets/toolbutton-background-checked-hovered@2x.9.png"
- "imagine-assets/toolbutton-background-checked.9.png"
- "imagine-assets/toolbutton-background-checked@2x.9.png"
- "imagine-assets/toolbutton-background-hovered.9.png"
- "imagine-assets/toolbutton-background-hovered@2x.9.png"
- "imagine-assets/toolbutton-background-pressed.9.png"
- "imagine-assets/toolbutton-background-pressed@2x.9.png"
- "imagine-assets/toolbutton-background.9.png"
- "imagine-assets/toolbutton-background@2x.9.png"
- "imagine-assets/tooltip-background.9.png"
- "imagine-assets/tooltip-background@2x.9.png"
- "qtquickcontrols2.conf"
-)
-
-target_link_libraries(musicplayerexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickControls2
-)
-
-install(TARGETS musicplayerexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
-)
diff --git a/examples/quickcontrols/imagine/musicplayer/doc/images/qtquickcontrols-musicplayer.png b/examples/quickcontrols/imagine/musicplayer/doc/images/qtquickcontrols-musicplayer.png
deleted file mode 100644
index 25b6344f3e..0000000000
--- a/examples/quickcontrols/imagine/musicplayer/doc/images/qtquickcontrols-musicplayer.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/doc/src/qtquickcontrols-musicplayer.qdoc b/examples/quickcontrols/imagine/musicplayer/doc/src/qtquickcontrols-musicplayer.qdoc
deleted file mode 100644
index fa36097225..0000000000
--- a/examples/quickcontrols/imagine/musicplayer/doc/src/qtquickcontrols-musicplayer.qdoc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
- \example imagine/musicplayer
- \keyword Qt Quick Controls - Imagine Style Example: Music Player
- \title Qt Quick Controls - Imagine Style Example: Music Player
- \keyword Qt Quick Controls 2 - Imagine Style Example: Music Player
- \ingroup qtquickcontrols-examples
- \brief An audio player user interface using custom Imagine style assets.
-
- This example demonstrates how custom Imagine style assets
- can be used to style a user interface for an audio player application.
-
- \image qtquickcontrols-musicplayer.png
-
- \omit
- The assets are generated from a Photoshop file: \e musicplayer.psd.
- This file can found in the \e design folder in the example directory.
- All objects related to a particular asset are collected in a layer group.
- For example, the assets for Button's background element are under a layer group with the following name:
-
- \badcode
- 100% button-background.9.png, 200% button-background@2x.9.png, 300% button-background@3x.9.png, 400% button-background@4x.9.png
- \endcode
-
- Using Photoshop's \l {Generate Image Assets}{https://helpx.adobe.com/photoshop/using/generate-assets-layers.html} feature,
- the size and name for each asset are encoded into the name of the layer group.
-
- Where applicable, \l {Smart Objects}{https://helpx.adobe.com/photoshop/using/create-smart-objects.html}
- are used to reduce duplication for common objects such as 9-patch lines.
- \endomit
-
- \include examples-run.qdocinc
-*/
diff --git a/examples/quickcontrols/imagine/musicplayer/musicplayer.cpp b/examples/quickcontrols/imagine/musicplayer/musicplayer.cpp
deleted file mode 100644
index 54727af696..0000000000
--- a/examples/quickcontrols/imagine/musicplayer/musicplayer.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QSettings>
-#include <QIcon>
-
-int main(int argc, char *argv[])
-{
- QGuiApplication::setApplicationName("Music Player");
- QGuiApplication::setOrganizationName("QtProject");
-
- QGuiApplication app(argc, argv);
-
- QIcon::setThemeName("musicplayer");
-
- QQmlApplicationEngine engine;
- engine.load(QUrl("qrc:/musicplayer.qml"));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
diff --git a/examples/quickcontrols/imagine/musicplayer/musicplayer.qml b/examples/quickcontrols/imagine/musicplayer/musicplayer.qml
deleted file mode 100644
index 7d321534a7..0000000000
--- a/examples/quickcontrols/imagine/musicplayer/musicplayer.qml
+++ /dev/null
@@ -1,420 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import QtQuick.Window
-
-ApplicationWindow {
- id: window
- width: 1280
- height: 720
- visible: true
- title: "Qt Quick Controls - Imagine Style Example: Music Player"
-
- Component.onCompleted: {
- x = Screen.width / 2 - width / 2
- y = Screen.height / 2 - height / 2
- }
-
- Shortcut {
- sequence: "Ctrl+Q"
- onActivated: Qt.quit()
- }
-
- header: ToolBar {
- RowLayout {
- id: headerRowLayout
- anchors.fill: parent
- spacing: 0
-
- ToolButton {
- icon.name: "grid"
- }
- ToolButton {
- icon.name: "settings"
- }
- ToolButton {
- icon.name: "filter"
- }
- ToolButton {
- icon.name: "message"
- }
- ToolButton {
- icon.name: "music"
- }
- ToolButton {
- icon.name: "cloud"
- }
- ToolButton {
- icon.name: "bluetooth"
- }
- ToolButton {
- icon.name: "cart"
- }
-
- Item {
- Layout.fillWidth: true
- }
-
- ToolButton {
- icon.name: "power"
- onClicked: Qt.quit()
- }
- }
- }
-
- Label {
- text: "Qtify"
- font.pixelSize: Qt.application.font.pixelSize * 1.3
- anchors.centerIn: header
- z: header.z + 1
- }
-
- RowLayout {
- spacing: 115
- anchors.fill: parent
- anchors.margins: 70
-
- ColumnLayout {
- spacing: 0
- Layout.preferredWidth: 230
-
- RowLayout {
- Layout.maximumHeight: 170
-
- ColumnLayout {
- Label {
- text: "12 dB"
- Layout.fillHeight: true
- }
- Label {
- text: "6 dB"
- Layout.fillHeight: true
- }
- Label {
- text: "0 dB"
- Layout.fillHeight: true
- }
- Label {
- text: "-6 dB"
- Layout.fillHeight: true
- }
- Label {
- text: "-12 dB"
- Layout.fillHeight: true
- }
- }
-
- Repeater {
- model: 7
-
- Slider {
- value: Math.random()
- orientation: Qt.Vertical
-
- Layout.fillWidth: true
- Layout.fillHeight: true
- }
- }
- }
-
- RowLayout {
- spacing: 10
- Layout.topMargin: 23
-
- ComboBox {
- currentIndex: 1
- model: ["Blues", "Classical", "Jazz", "Metal"]
- Layout.fillWidth: true
- }
-
- Button {
- icon.name: "folder"
- }
-
- Button {
- icon.name: "save"
- enabled: false
- }
- }
-
- Dial {
- Layout.alignment: Qt.AlignHCenter
- Layout.topMargin: 50
- }
-
- Label {
- text: "Volume"
-
- Layout.alignment: Qt.AlignHCenter
- Layout.topMargin: 12
- }
- }
-
- ColumnLayout {
- spacing: 26
- Layout.preferredWidth: 230
-
- Item {
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- Image {
- anchors.fill: parent
- fillMode: Image.PreserveAspectCrop
- source: "images/album-cover.jpg"
- }
- }
-
- Item {
- id: songLabelContainer
- clip: true
-
- Layout.fillWidth: true
- Layout.preferredHeight: songNameLabel.implicitHeight
-
- SequentialAnimation {
- running: true
- loops: Animation.Infinite
-
- PauseAnimation {
- duration: 2000
- }
- ParallelAnimation {
- XAnimator {
- target: songNameLabel
- from: 0
- to: songLabelContainer.width - songNameLabel.implicitWidth
- duration: 5000
- }
- OpacityAnimator {
- target: leftGradient
- from: 0
- to: 1
- }
- }
- OpacityAnimator {
- target: rightGradient
- from: 1
- to: 0
- }
- PauseAnimation {
- duration: 1000
- }
- OpacityAnimator {
- target: rightGradient
- from: 0
- to: 1
- }
- ParallelAnimation {
- XAnimator {
- target: songNameLabel
- from: songLabelContainer.width - songNameLabel.implicitWidth
- to: 0
- duration: 5000
- }
- OpacityAnimator {
- target: leftGradient
- from: 0
- to: 1
- }
- }
- OpacityAnimator {
- target: leftGradient
- from: 1
- to: 0
- }
- }
-
- Rectangle {
- id: leftGradient
- gradient: Gradient {
- GradientStop {
- position: 0
- color: "#dfe4ea"
- }
- GradientStop {
- position: 1
- color: "#00dfe4ea"
- }
- }
-
- width: height
- height: parent.height
- anchors.left: parent.left
- z: 1
- rotation: -90
- opacity: 0
- }
-
- Label {
- id: songNameLabel
- text: "Edvard Grieg - In the Hall of the Mountain King"
- font.pixelSize: Qt.application.font.pixelSize * 1.4
- }
-
- Rectangle {
- id: rightGradient
- gradient: Gradient {
- GradientStop {
- position: 0
- color: "#00dfe4ea"
- }
- GradientStop {
- position: 1
- color: "#dfe4ea"
- }
- }
-
- width: height
- height: parent.height
- anchors.right: parent.right
- rotation: -90
- }
- }
-
- RowLayout {
- spacing: 8
- Layout.alignment: Qt.AlignHCenter
-
- RoundButton {
- icon.name: "favorite"
- icon.width: 32
- icon.height: 32
- }
- RoundButton {
- icon.name: "stop"
- icon.width: 32
- icon.height: 32
- }
- RoundButton {
- icon.name: "previous"
- icon.width: 32
- icon.height: 32
- }
- RoundButton {
- icon.name: "pause"
- icon.width: 32
- icon.height: 32
- }
- RoundButton {
- icon.name: "next"
- icon.width: 32
- icon.height: 32
- }
- RoundButton {
- icon.name: "repeat"
- icon.width: 32
- icon.height: 32
- }
- RoundButton {
- icon.name: "shuffle"
- icon.width: 32
- icon.height: 32
- }
- }
-
- Slider {
- id: seekSlider
- value: 113
- to: 261
-
- Layout.fillWidth: true
-
- ToolTip {
- parent: seekSlider.handle
- visible: seekSlider.pressed
- text: pad(Math.floor(value / 60)) + ":" + pad(Math.floor(value % 60))
- y: parent.height
-
- readonly property int value: seekSlider.valueAt(seekSlider.position)
-
- function pad(number) {
- if (number <= 9)
- return "0" + number;
- return number;
- }
- }
- }
- }
-
- ColumnLayout {
- spacing: 16
- Layout.preferredWidth: 230
-
- ButtonGroup {
- buttons: libraryRowLayout.children
- }
-
- RowLayout {
- id: libraryRowLayout
- Layout.alignment: Qt.AlignHCenter
-
- Button {
- text: "Files"
- checked: true
- }
- Button {
- text: "Playlists"
- checkable: true
- }
- Button {
- text: "Favourites"
- checkable: true
- }
- }
-
- RowLayout {
- TextField {
- Layout.fillWidth: true
- }
- Button {
- icon.name: "folder"
- }
- }
-
- Frame {
- id: filesFrame
- leftPadding: 1
- rightPadding: 1
-
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- ListView {
- id: filesListView
- clip: true
- anchors.fill: parent
- model: ListModel {
- Component.onCompleted: {
- for (var i = 0; i < 100; ++i) {
- append({
- author: "Author",
- album: "Album",
- track: "Track 0" + (i % 9 + 1),
- });
- }
- }
- }
- delegate: ItemDelegate {
- text: model.author + " - " + model.album + " - " + model.track
- width: filesListView.width
- }
-
- ScrollBar.vertical: ScrollBar {
- parent: filesFrame
- policy: ScrollBar.AlwaysOn
- anchors.top: parent.top
- anchors.topMargin: filesFrame.topPadding
- anchors.right: parent.right
- anchors.rightMargin: 1
- anchors.bottom: parent.bottom
- anchors.bottomMargin: filesFrame.bottomPadding
- }
- }
- }
- }
- }
-}
diff --git a/examples/quickcontrols/imagine/musicplayer/qtquickcontrols2.conf b/examples/quickcontrols/imagine/musicplayer/qtquickcontrols2.conf
deleted file mode 100644
index 33eab6a563..0000000000
--- a/examples/quickcontrols/imagine/musicplayer/qtquickcontrols2.conf
+++ /dev/null
@@ -1,10 +0,0 @@
-[Controls]
-Style=Imagine
-
-[Imagine]
-Path=:/imagine-assets
-
-[Imagine\Palette]
-Text=#6b7b89
-ButtonText=#6b7b89
-WindowText=#6b7b89
diff --git a/examples/quickcontrols/ios/todolist/AppSettings.qml b/examples/quickcontrols/ios/todolist/AppSettings.qml
index 764ba8b8fc..37a9fe33e9 100644
--- a/examples/quickcontrols/ios/todolist/AppSettings.qml
+++ b/examples/quickcontrols/ios/todolist/AppSettings.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
pragma Singleton
diff --git a/examples/quickcontrols/ios/todolist/CMakeLists.txt b/examples/quickcontrols/ios/todolist/CMakeLists.txt
index ea96d606c7..64d7624679 100644
--- a/examples/quickcontrols/ios/todolist/CMakeLists.txt
+++ b/examples/quickcontrols/ios/todolist/CMakeLists.txt
@@ -3,12 +3,6 @@ project(todolist LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/todolist")
-
find_package(Qt6 COMPONENTS Gui Qml Quick QuickControls2)
qt_add_executable(todolist
@@ -77,7 +71,16 @@ qt_add_qml_module(todolist
set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME ON)
install(TARGETS todolist
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET todolist
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/ios/todolist/Database.qml b/examples/quickcontrols/ios/todolist/Database.qml
index 31bbd8c1e7..eb85402b26 100644
--- a/examples/quickcontrols/ios/todolist/Database.qml
+++ b/examples/quickcontrols/ios/todolist/Database.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
pragma Singleton
diff --git a/examples/quickcontrols/ios/todolist/FontSizePage.qml b/examples/quickcontrols/ios/todolist/FontSizePage.qml
index 2b0e5b16db..89c8bd4518 100644
--- a/examples/quickcontrols/ios/todolist/FontSizePage.qml
+++ b/examples/quickcontrols/ios/todolist/FontSizePage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/examples/quickcontrols/ios/todolist/HomePage.qml b/examples/quickcontrols/ios/todolist/HomePage.qml
index 94221146e2..39753e0be7 100644
--- a/examples/quickcontrols/ios/todolist/HomePage.qml
+++ b/examples/quickcontrols/ios/todolist/HomePage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -56,7 +56,7 @@ Page {
}
Image {
- source: Qt.styleHints.appearance === Qt.Dark ? "images/close-white.png"
+ source: Qt.styleHints.colorScheme === Qt.Dark ? "images/close-white.png"
: "images/close.png"
width: 20
height: 20
@@ -141,6 +141,7 @@ Page {
enabled: newProjectTextField.length > 0
Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: false
onClicked: {
let results = Database.newProject(newProjectTextField.text)
diff --git a/examples/quickcontrols/ios/todolist/MaxTasksPage.qml b/examples/quickcontrols/ios/todolist/MaxTasksPage.qml
index 509a460357..5b1a854617 100644
--- a/examples/quickcontrols/ios/todolist/MaxTasksPage.qml
+++ b/examples/quickcontrols/ios/todolist/MaxTasksPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -29,6 +29,7 @@ Page {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 10
+ Layout.fillWidth: false
onValueModified: AppSettings.maxTasks = maxTasksSpinbox.value
}
diff --git a/examples/quickcontrols/ios/todolist/NavBar.qml b/examples/quickcontrols/ios/todolist/NavBar.qml
index 4f57353dca..717993dfda 100644
--- a/examples/quickcontrols/ios/todolist/NavBar.qml
+++ b/examples/quickcontrols/ios/todolist/NavBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/examples/quickcontrols/ios/todolist/ProjectPage.qml b/examples/quickcontrols/ios/todolist/ProjectPage.qml
index a38ad04554..8319206fe9 100644
--- a/examples/quickcontrols/ios/todolist/ProjectPage.qml
+++ b/examples/quickcontrols/ios/todolist/ProjectPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -53,6 +53,7 @@ Page {
value: root.completedTasks
to: root.totalTasks
Layout.leftMargin: 20
+ Layout.fillWidth: false
}
}
@@ -159,7 +160,7 @@ Page {
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
icon.source: "images/close.png"
- icon.color: Qt.styleHints.appearance === Qt.Dark ? "white" : "black"
+ icon.color: Qt.styleHints.colorScheme === Qt.Dark ? "white" : "black"
onClicked: {
Database.deleteTask(taskList.taskId)
diff --git a/examples/quickcontrols/ios/todolist/SettingsPage.qml b/examples/quickcontrols/ios/todolist/SettingsPage.qml
index 4d2d7bef9f..0f6f708974 100644
--- a/examples/quickcontrols/ios/todolist/SettingsPage.qml
+++ b/examples/quickcontrols/ios/todolist/SettingsPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -52,7 +52,7 @@ Page {
required property string page
Image {
- source: Qt.styleHints.appearance === Qt.Dark ? "images/back-white.png"
+ source: Qt.styleHints.colorScheme === Qt.Dark ? "images/back-white.png"
: "images/back.png"
width: 20
height: 20
diff --git a/examples/quickcontrols/ios/todolist/ToggleCompletedTasksPage.qml b/examples/quickcontrols/ios/todolist/ToggleCompletedTasksPage.qml
index df2145866a..7e42eaf8f3 100644
--- a/examples/quickcontrols/ios/todolist/ToggleCompletedTasksPage.qml
+++ b/examples/quickcontrols/ios/todolist/ToggleCompletedTasksPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -27,6 +27,7 @@ Page {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 10
+ Layout.fillWidth: false
onClicked: AppSettings.showDoneTasks = checked
}
diff --git a/examples/quickcontrols/ios/todolist/main.qml b/examples/quickcontrols/ios/todolist/main.qml
index a0067643f1..d4318dfd69 100644
--- a/examples/quickcontrols/ios/todolist/main.qml
+++ b/examples/quickcontrols/ios/todolist/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
diff --git a/examples/quickcontrols/ios/todolist/src/main.cpp b/examples/quickcontrols/ios/todolist/src/main.cpp
index 4f158402a2..f0b0e194fb 100644
--- a/examples/quickcontrols/ios/todolist/src/main.cpp
+++ b/examples/quickcontrols/ios/todolist/src/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/examples/quickcontrols/quickcontrols.pro b/examples/quickcontrols/quickcontrols.pro
index b7fc57bca6..7baf731218 100644
--- a/examples/quickcontrols/quickcontrols.pro
+++ b/examples/quickcontrols/quickcontrols.pro
@@ -4,11 +4,12 @@ SUBDIRS += \
chattutorial \
texteditor \
contactlist \
- sidepanel \
- swipetoremove \
wearable \
- imagine/automotive \
- imagine/musicplayer
+ imagine/automotive
+
+win32|macos|unix {
+ qtHaveModule(svg): SUBDIRS += filesystemexplorer
+}
qtHaveModule(sql): SUBDIRS += eventcalendar
qtHaveModule(widgets): SUBDIRS += flatstyle
diff --git a/examples/quickcontrols/sidepanel/sidepanel.cpp b/examples/quickcontrols/sidepanel/sidepanel.cpp
deleted file mode 100644
index c78329d9de..0000000000
--- a/examples/quickcontrols/sidepanel/sidepanel.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
-
- QQmlApplicationEngine engine;
- engine.load(QUrl("qrc:/sidepanel.qml"));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
diff --git a/examples/quickcontrols/sidepanel/sidepanel.qml b/examples/quickcontrols/sidepanel/sidepanel.qml
deleted file mode 100644
index de767a9bb6..0000000000
--- a/examples/quickcontrols/sidepanel/sidepanel.qml
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
- id: window
- width: 360
- height: 520
- visible: true
- title: qsTr("Side Panel")
-
- //! [orientation]
- readonly property bool inPortrait: window.width < window.height
- //! [orientation]
-
- ToolBar {
- id: overlayHeader
-
- z: 1
- width: parent.width
- parent: Overlay.overlay
-
- Label {
- id: label
- anchors.centerIn: parent
- text: "Qt Quick Controls"
- }
- }
-
- Drawer {
- id: drawer
-
- y: overlayHeader.height
- width: window.width / 2
- height: window.height - overlayHeader.height
-
- modal: inPortrait
- interactive: inPortrait
- position: inPortrait ? 0 : 1
- visible: !inPortrait
-
- ListView {
- id: listView
- anchors.fill: parent
-
- headerPositioning: ListView.OverlayHeader
- header: Pane {
- id: header
- z: 2
- width: parent.width
-
- contentHeight: logo.height
-
- Image {
- id: logo
- width: parent.width
- source: "images/qt-logo.png"
- fillMode: implicitWidth > width ? Image.PreserveAspectFit : Image.Pad
- }
-
- MenuSeparator {
- parent: header
- width: parent.width
- anchors.verticalCenter: parent.bottom
- visible: !listView.atYBeginning
- }
- }
-
- footer: ItemDelegate {
- id: footer
- text: qsTr("Footer")
- width: parent.width
-
- MenuSeparator {
- parent: footer
- width: parent.width
- anchors.verticalCenter: parent.top
- }
- }
-
- model: 10
-
- delegate: ItemDelegate {
- text: qsTr("Title %1").arg(index + 1)
- width: listView.width
- }
-
- ScrollIndicator.vertical: ScrollIndicator { }
- }
- }
-
- Flickable {
- id: flickable
-
- anchors.fill: parent
- anchors.topMargin: overlayHeader.height
- anchors.leftMargin: !inPortrait ? drawer.width : undefined
-
- topMargin: 20
- bottomMargin: 20
- contentHeight: column.height
-
- Column {
- id: column
- spacing: 20
- anchors.margins: 20
- anchors.left: parent.left
- anchors.right: parent.right
-
- Label {
- font.pixelSize: 22
- width: parent.width
- elide: Label.ElideRight
- horizontalAlignment: Qt.AlignHCenter
- text: qsTr("Side Panel Example")
- }
-
- Label {
- width: parent.width
- wrapMode: Label.WordWrap
- text: qsTr("This example demonstrates how Drawer can be used as a non-closable persistent side panel.\n\n" +
- "When the application is in portrait mode, the drawer is an interactive side panel that can " +
- "be swiped open from the left edge. When the application is in landscape mode, the drawer " +
- "and the content are laid out side by side.\n\nThe application is currently in %1 mode.").arg(inPortrait ? qsTr("portrait") : qsTr("landscape"))
- }
- }
-
- ScrollIndicator.vertical: ScrollIndicator { }
- }
-}
diff --git a/examples/quickcontrols/swipetoremove/swipetoremove.cpp b/examples/quickcontrols/swipetoremove/swipetoremove.cpp
deleted file mode 100644
index 0bd8e24521..0000000000
--- a/examples/quickcontrols/swipetoremove/swipetoremove.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QFontDatabase>
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
-
- QFontDatabase::addApplicationFont(":/fonts/fontello.ttf");
-
- QQmlApplicationEngine engine;
- engine.load(QUrl("qrc:/swipetoremove.qml"));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
diff --git a/examples/quickcontrols/swipetoremove/swipetoremove.qml b/examples/quickcontrols/swipetoremove/swipetoremove.qml
deleted file mode 100644
index a7820eb024..0000000000
--- a/examples/quickcontrols/swipetoremove/swipetoremove.qml
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Controls.Material
-
-ApplicationWindow {
- id: window
- width: 300
- height: 400
- visible: true
- title: qsTr("Swipe to Remove")
-
- ListView {
- id: listView
- anchors.fill: parent
-
- delegate: SwipeDelegate {
- id: delegate
-
- text: modelData
- width: listView.width
-
- //! [delegate]
- swipe.right: Rectangle {
- width: parent.width
- height: parent.height
-
- clip: true
- color: SwipeDelegate.pressed ? "#555" : "#666"
-
- Label {
- font.family: "Fontello"
- text: delegate.swipe.complete ? "\ue805" // icon-cw-circled
- : "\ue801" // icon-cancel-circled-1
-
- padding: 20
- anchors.fill: parent
- horizontalAlignment: Qt.AlignRight
- verticalAlignment: Qt.AlignVCenter
-
- opacity: 2 * -delegate.swipe.position
-
- color: Material.color(delegate.swipe.complete ? Material.Green : Material.Red, Material.Shade200)
- Behavior on color { ColorAnimation { } }
- }
-
- Label {
- text: qsTr("Removed")
- color: "white"
-
- padding: 20
- anchors.fill: parent
- horizontalAlignment: Qt.AlignLeft
- verticalAlignment: Qt.AlignVCenter
-
- opacity: delegate.swipe.complete ? 1 : 0
- Behavior on opacity { NumberAnimation { } }
- }
-
- SwipeDelegate.onClicked: delegate.swipe.close()
- SwipeDelegate.onPressedChanged: undoTimer.stop()
- }
- //! [delegate]
-
- //! [removal]
- Timer {
- id: undoTimer
- interval: 3600
- onTriggered: listModel.remove(index)
- }
-
- swipe.onCompleted: undoTimer.start()
- //! [removal]
- }
-
- model: ListModel {
- id: listModel
- ListElement { text: "Lorem ipsum dolor sit amet" }
- ListElement { text: "Curabitur sit amet risus" }
- ListElement { text: "Suspendisse vehicula nisi" }
- ListElement { text: "Mauris imperdiet libero" }
- ListElement { text: "Sed vitae dui aliquet augue" }
- ListElement { text: "Praesent in elit eu nulla" }
- ListElement { text: "Etiam vitae magna" }
- ListElement { text: "Pellentesque eget elit euismod" }
- ListElement { text: "Nulla at enim porta" }
- ListElement { text: "Fusce tincidunt odio" }
- ListElement { text: "Ut non ex a ligula molestie" }
- ListElement { text: "Nam vitae justo scelerisque" }
- ListElement { text: "Vestibulum pulvinar tellus" }
- ListElement { text: "Quisque dignissim leo sed gravida" }
- }
-
- //! [transitions]
- remove: Transition {
- SequentialAnimation {
- PauseAnimation { duration: 125 }
- NumberAnimation { property: "height"; to: 0; easing.type: Easing.InOutQuad }
- }
- }
-
- displaced: Transition {
- SequentialAnimation {
- PauseAnimation { duration: 125 }
- NumberAnimation { property: "y"; easing.type: Easing.InOutQuad }
- }
- }
- //! [transitions]
-
- ScrollIndicator.vertical: ScrollIndicator { }
- }
-
- Label {
- id: placeholder
- text: qsTr("Swipe no more")
-
- anchors.margins: 60
- anchors.fill: parent
-
- opacity: 0.5
- visible: listView.count === 0
-
- horizontalAlignment: Qt.AlignHCenter
- verticalAlignment: Qt.AlignVCenter
- wrapMode: Label.WordWrap
- font.pixelSize: 18
- }
-}
diff --git a/examples/quickcontrols/texteditor/CMakeLists.txt b/examples/quickcontrols/texteditor/CMakeLists.txt
index 37c86142da..6fa24b41c6 100644
--- a/examples/quickcontrols/texteditor/CMakeLists.txt
+++ b/examples/quickcontrols/texteditor/CMakeLists.txt
@@ -1,21 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(texteditor LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/texteditor")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2 OPTIONAL_COMPONENTS Widgets)
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
qt_add_executable(texteditorexample WIN32 MACOSX_BUNDLE
- documenthandler.cpp
texteditor.cpp
)
@@ -33,18 +26,12 @@ qt_add_qml_module(texteditorexample
)
target_link_libraries(texteditorexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickControls2
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
)
-if(TARGET Qt::Widgets)
- target_link_libraries(texteditorexample PUBLIC
- Qt::Widgets
- )
-endif()
-
if(CMAKE_CROSSCOMPILING)
target_compile_definitions(texteditorexample PUBLIC
QT_EXTRA_FILE_SELECTOR="touch"
@@ -52,7 +39,16 @@ if(CMAKE_CROSSCOMPILING)
endif()
install(TARGETS texteditorexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET texteditorexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc b/examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc
index b1d7e842cf..40ab1915e6 100644
--- a/examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc
+++ b/examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc
@@ -6,95 +6,158 @@
\title Qt Quick Controls - Text Editor
\keyword Qt Quick Controls 2 - Text Editor
\ingroup qtquickcontrols-examples
- \brief A QML app using Qt Quick Controls and a C++ class to
- provide a fully-functional rich-text editor application.
+ \examplecategory {Graphics}
+ \brief A rich-text editor app using Qt Quick Controls.
- The \e {Text Editor Example} presents a sample HTML file using the TextArea
- control, preserving the HTML formatting. The application comes with two user
- interfaces; one for traditional desktop platforms with a mouse pointer, and
- another simpler, touch-oriented version.
+ The \e {Text Editor Example} allows WYSIWYG editing of an HTML, Markdown or
+ plain text file. The application comes with two user interfaces: one for
+ larger screens, and a simplified UI for small touch-based devices. Both are
+ "pure" QML. \c texteditor.cpp contains the \c main() function, which calls
+ QFontDatabase::addApplicationFont() to add an icon font. (\l FontLoader
+ would be an alternative way to achieve the same result.)
\section1 Desktop User Interface
\image qtquickcontrols-texteditor-desktop.jpg
The desktop version is a complete text editor with capabilities for formatting
- text, and opening and saving HTML and plain text files. It demonstrates the
- native-looking dialogs and menus using the \l{Qt Labs Platform} module. These
- types are mostly suitable for desktop platforms with support for multiple
- top-level windows, a mouse pointer, and moderate screen size.
+ text, and opening and saving HTML, Markdown and plain text files.
- The desktop UI uses FileDialog for opening and saving files:
+ In the \l {https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller}{model-view-control (MVC)}
+ design pattern, the \e control layer includes the set of operations that
+ can be performed. In Qt Quick Controls, the \l Action type is used to
+ encapsulate a single operation or command. Accordingly, we begin with a
+ set of Action objects:
\quotefromfile texteditor/qml/texteditor.qml
- \skipto FileDialog
- \printuntil /\bsaveAs\b/
- \printline }
+ \skipto Action
+ \printuntil openAction
+ \printto Action
- It uses FontDialog and ColorDialog for choosing fonts and colors:
+ The \l Action for opening a file must first prompt the user if the existing
+ document has been changed, to avoid losing the user's changes. Otherwise
+ it simply opens the FileDialog which is declared further below.
- \skipto FontDialog
- \printuntil /.*colorDialog$/
- \printuntil /^\s{4}\}$/
+ The \l Action for saving the file is enabled only if there are changes to save:
- It also uses \l[QML QtLabsPlatform]{Menu} and
- \l[QML QtLabsPlatform]{MenuItem} that provide a context menu to format text
- within:
+ \printuntil saveAction
+ \printto Action
- \skipto /\bMenu\b/
- \printuntil /^\s{4}\}$/
+ \skipto quitAction
+ \skipuntil }
- \note There is also a standard menubar with more options than the
- context menu.
+ The \l Action for copying selected text is enabled only if some text is selected:
- \section1 Touch User Interface
+ \printuntil copyAction
+ \printuntil }
- \image qtquickcontrols-texteditor-touch.jpg
+ \skipto pasteAction
+ \skipuntil }
- The touch user interface is a simplified version of the text editor. It is
- suitable for touch devices with limited screen size. The example uses
- \l{Using File Selectors with Qt Quick Controls}{file selectors} to load
- the appropriate user interface automatically.
+ Each Action to change text formatting (such as bold, italic and alignment)
+ is \l {Action::}{checkable}, and its boolean \c checked state
+ is in sync with the relevant property in the
+ \l {TextEdit::selectedText}{selected text}.
+ Since declarative bidirectional synchronization is difficult, we use
+ an \c onTriggered script to change the property when the Action is
+ activated. The \l {TextEdit::}{cursorSelection} property
+ is new in Qt 6.7 and makes this much easier than it was.
+
+ \printuntil boldAction
+ \printto Action
+
+ \skipto alignLeftAction
+ \skipuntil }
+
+ \printuntil alignCenterAction
+ \printto Action
+
+ We have a \l MenuBar containing the hierarchy of \l {Menu}{Menus} and
+ MenuItems. Each \l MenuItem merely needs to bind the relevant
+ \l {AbstractButton::}{action}, which encapsulates the
+ UI representation and the implementation.
+
+ \skipto MenuBar
+ \printuntil copyAction
+ \printuntil }
+ \dots 8
+
+ The same \l Action objects are reused in the \l ToolBar; but here we
+ override each Action's \l {AbstractButton::}{text} property to
+ choose a textual icon from our icon font:
+
+ \skipto ToolBar
+ \printuntil copyButton
+ \printuntil }
+ \dots 12
- Unlike the desktop version, which uses top-level dialogs, the touch version
- uses the QML \l Dialog type, which is not a top-level window. This type of
- dialog is fully supported on mobile and embedded platforms that do not support
- multiple top-level windows.
+ The main part of the text editor is a \l TextArea inside a \l Flickable:
- \quotefromfile texteditor/qml/+touch/texteditor.qml
- \skipto /\bDialog\b/
- \printuntil /^\s{4}\}$/
+ \skipto Flickable
+ \printuntil persistentSelection
+ \dots 12
- \section1 C++ Backend
+ A \l ScrollBar is attached to the vertical axis. Since word-wrapping is
+ enabled via \l {TextEdit::}{wrapMode}, we don't need a horizontal
+ ScrollBar.
- Both user interfaces use the same C++ backend, which supports opening, formatting,
- and editing a document. The C++ class, \c DocumentHandler, extends QObject and is
- registered as a QML type under the namespace \c {io.qt.examples.texteditor 1.0}.
+ The \l {TextArea::flickable}{TextArea.flickable} attached property is used
+ so that when the text cursor is moved out of the viewport (for example via
+ arrow keys, or by typing a lot of text), \l TextArea scrolls the
+ \l Flickable to keep the cursor visible.
- The following snippets show how the type is registered under a namespace and later
- imported and instantiated by \e main.qml. For more information about registering C++
- classes as QML types, see \l {Defining QML Types from C++}.
+ There is a context menu; we use a TapHandler to detect a right-click and
+ open it:
- QML type registration:
+ \skipto TapHandler
+ \printuntil }
- \code
- #include <QtQml/qqml.h>
- ...
- qmlRegisterType<DocumentHandler>("io.qt.examples.texteditor", 1, 0, "DocumentHandler");
- ...
- \endcode
+ The context \l Menu contains \l {MenuItem}{MenuItems} that reuse the same
+ \l Action objects as the main \l MenuBar and \l ToolBar are using.
+ As before, it's enough to bind \l {AbstractButton::}{action} to the
+ reusable Action that represents the operation to be performed. However,
+ we override each menu item's \l {MenuItem::}{text} to omit the
+ underlined mnemonics on the context menu.
- QML namespace import:
+ \skipto Menu
+ \printuntil MenuItem
+ \printuntil }
+ \dots 8
- \code
- import io.qt.examples.texteditor 1.0
- \endcode
+ We consistently use the \l qsTr function to enable translation of UI text,
+ so that the application will make sense regardless of the end user's native
+ language.
- QML instance:
+ We use several kinds of \l {Qt Quick Dialogs QML Types}{dialogs}:
\quotefromfile texteditor/qml/texteditor.qml
- \skipto DocumentHandler
- \printuntil /^\s{4}\}$/
+ \skipto FileDialog
+ \printuntil discardDialog
+ \printuntil }
+ \printuntil }
+
+ It's generally easier to declare separate instances for each purpose.
+ We have two instances of \l {QtQuick.Dialogs::}{FileDialog}, for opening
+ and saving files respectively. This became easier in Qt 6.7, with new
+ features in \l TextDocument.
+
+ A \l {QtQuick.Dialogs::}{FontDialog} and a \l {QtQuick.Dialogs::ColorDialog}{ColorDialog}
+ allow changing text formatting. (In Markdown format, there's no syntax to
+ represent specific font and color choices; but font characteristics such as
+ bold, italic and monospace are saved. In HTML format, all formatting is
+ saved.)
+
+ We have a \l {QtQuick.Dialogs::}{MessageDialog} to show error messages, and
+ two more for prompting the user what to do when a file has been modified.
+
+ \section1 Touch User Interface
+
+ \image qtquickcontrols-texteditor-touch.jpg
+
+ The touch user interface is a simplified version of the text editor. It is
+ suitable for touch devices with limited screen size. The example uses
+ \l{Using File Selectors with Qt Quick Controls}{file selectors} to load
+ the appropriate user interface automatically.
\include examples-run.qdocinc
*/
diff --git a/examples/quickcontrols/texteditor/documenthandler.cpp b/examples/quickcontrols/texteditor/documenthandler.cpp
deleted file mode 100644
index f6ed6aef84..0000000000
--- a/examples/quickcontrols/texteditor/documenthandler.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "documenthandler.h"
-
-#include <QFile>
-#include <QFileInfo>
-#include <QFileSelector>
-#include <QMimeDatabase>
-#include <QQmlFile>
-#include <QQmlFileSelector>
-#include <QQuickTextDocument>
-#include <QTextCharFormat>
-#include <QStringDecoder>
-#include <QTextDocument>
-#include <QDebug>
-
-DocumentHandler::DocumentHandler(QObject *parent)
- : QObject(parent)
- , m_document(nullptr)
- , m_cursorPosition(-1)
- , m_selectionStart(0)
- , m_selectionEnd(0)
-{
-}
-
-QQuickTextDocument *DocumentHandler::document() const
-{
- return m_document;
-}
-
-void DocumentHandler::setDocument(QQuickTextDocument *document)
-{
- if (document == m_document)
- return;
-
- if (m_document)
- disconnect(m_document->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
- m_document = document;
- if (m_document)
- connect(m_document->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
- emit documentChanged();
-}
-
-int DocumentHandler::cursorPosition() const
-{
- return m_cursorPosition;
-}
-
-void DocumentHandler::setCursorPosition(int position)
-{
- if (position == m_cursorPosition)
- return;
-
- m_cursorPosition = position;
- reset();
- emit cursorPositionChanged();
-}
-
-int DocumentHandler::selectionStart() const
-{
- return m_selectionStart;
-}
-
-void DocumentHandler::setSelectionStart(int position)
-{
- if (position == m_selectionStart)
- return;
-
- m_selectionStart = position;
- emit selectionStartChanged();
-}
-
-int DocumentHandler::selectionEnd() const
-{
- return m_selectionEnd;
-}
-
-void DocumentHandler::setSelectionEnd(int position)
-{
- if (position == m_selectionEnd)
- return;
-
- m_selectionEnd = position;
- emit selectionEndChanged();
-}
-
-QColor DocumentHandler::textColor() const
-{
- QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return QColor(Qt::black);
- QTextCharFormat format = cursor.charFormat();
- return format.foreground().color();
-}
-
-void DocumentHandler::setTextColor(const QColor &color)
-{
- QTextCharFormat format;
- format.setForeground(QBrush(color));
- mergeFormatOnWordOrSelection(format);
- emit textColorChanged();
-}
-
-Qt::Alignment DocumentHandler::alignment() const
-{
- QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return Qt::AlignLeft;
- return textCursor().blockFormat().alignment();
-}
-
-void DocumentHandler::setAlignment(Qt::Alignment alignment)
-{
- QTextBlockFormat format;
- format.setAlignment(alignment);
- QTextCursor cursor = textCursor();
- cursor.mergeBlockFormat(format);
- emit alignmentChanged();
-}
-
-QString DocumentHandler::fileName() const
-{
- const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl);
- const QString fileName = QFileInfo(filePath).fileName();
- if (fileName.isEmpty())
- return QStringLiteral("untitled.txt");
- return fileName;
-}
-
-QString DocumentHandler::fileType() const
-{
- return QFileInfo(fileName()).suffix();
-}
-
-QUrl DocumentHandler::fileUrl() const
-{
- return m_fileUrl;
-}
-
-void DocumentHandler::load(const QUrl &fileUrl)
-{
- if (fileUrl == m_fileUrl)
- return;
-
- QQmlEngine *engine = qmlEngine(this);
- if (!engine) {
- qWarning() << "load() called before DocumentHandler has QQmlEngine";
- return;
- }
-
- const QUrl path = engine->interceptUrl(fileUrl, QQmlAbstractUrlInterceptor::UrlString);
- const QString fileName = QQmlFile::urlToLocalFileOrQrc(path);
- if (QFile::exists(fileName)) {
- QMimeType mime = QMimeDatabase().mimeTypeForFile(fileName);
- QFile file(fileName);
- if (file.open(QFile::ReadOnly)) {
- QByteArray data = file.readAll();
- if (QTextDocument *doc = textDocument()) {
- doc->setBaseUrl(path.adjusted(QUrl::RemoveFilename));
- doc->setModified(false);
- if (mime.inherits("text/markdown")) {
- emit loaded(QString::fromUtf8(data), Qt::MarkdownText);
- } else {
- auto encoding = QStringConverter::encodingForHtml(data);
- if (encoding) {
- QStringDecoder decoder(*encoding);
- emit loaded(decoder(data), Qt::AutoText);
- } else {
- // fall back to utf8
- emit loaded(QString::fromUtf8(data), Qt::AutoText);
- }
- }
- }
-
- reset();
- }
- }
-
- m_fileUrl = fileUrl;
- emit fileUrlChanged();
-}
-
-void DocumentHandler::saveAs(const QUrl &fileUrl)
-{
- QTextDocument *doc = textDocument();
- if (!doc)
- return;
-
- const QString filePath = fileUrl.toLocalFile();
- const bool isHtml = QFileInfo(filePath).suffix().contains(QLatin1String("htm"));
- QFile file(filePath);
- if (!file.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) {
- emit error(tr("Cannot save: ") + file.errorString());
- return;
- }
- file.write((isHtml ? doc->toHtml() : doc->toPlainText()).toUtf8());
- file.close();
-
- if (fileUrl == m_fileUrl)
- return;
-
- m_fileUrl = fileUrl;
- emit fileUrlChanged();
-}
-
-void DocumentHandler::reset()
-{
- emit alignmentChanged();
- emit textColorChanged();
- emit fontChanged();
-}
-
-QTextCursor DocumentHandler::textCursor() const
-{
- QTextDocument *doc = textDocument();
- if (!doc)
- return QTextCursor();
-
- QTextCursor cursor = QTextCursor(doc);
- if (m_selectionStart != m_selectionEnd) {
- cursor.setPosition(m_selectionStart);
- cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
- } else {
- cursor.setPosition(m_cursorPosition);
- }
- return cursor;
-}
-
-QTextDocument *DocumentHandler::textDocument() const
-{
- if (!m_document)
- return nullptr;
-
- return m_document->textDocument();
-}
-
-void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
-{
- QTextCursor cursor = textCursor();
- if (!cursor.hasSelection())
- cursor.select(QTextCursor::WordUnderCursor);
- cursor.mergeCharFormat(format);
-}
-
-bool DocumentHandler::modified() const
-{
- return m_document && m_document->textDocument()->isModified();
-}
-
-void DocumentHandler::setModified(bool m)
-{
- if (m_document)
- m_document->textDocument()->setModified(m);
-}
-
-QFont DocumentHandler::font() const
-{
- QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return m_document->textDocument()->defaultFont();
- QTextCharFormat format = cursor.charFormat();
- return format.font();
-}
-
-void DocumentHandler::setFont(const QFont & font){
-
- QTextCursor cursor = textCursor();
- if (!cursor.isNull() && cursor.charFormat().font() == font)
- return;
-
- QTextCharFormat format;
- format.setFont(font);
- mergeFormatOnWordOrSelection(format);
-
- emit fontChanged();
-}
-
-bool DocumentHandler::bold() const
-{
- const QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return m_document->textDocument()->defaultFont().bold();
- return cursor.charFormat().font().bold();
-}
-
-void DocumentHandler::setBold(bool bold)
-{
- const QTextCursor cursor = textCursor();
- if (!cursor.isNull() && cursor.charFormat().font().bold() == bold)
- return;
-
- QFont font = cursor.charFormat().font();
- font.setBold(bold);
- QTextCharFormat format;
- format.setFont(font);
- mergeFormatOnWordOrSelection(format);
-
- emit boldChanged();
-}
-
-bool DocumentHandler::underline() const
-{
- const QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return m_document->textDocument()->defaultFont().underline();
- return cursor.charFormat().font().underline();
-}
-
-void DocumentHandler::setUnderline(bool underline)
-{
- const QTextCursor cursor = textCursor();
- if (!cursor.isNull() && cursor.charFormat().font().underline() == underline)
- return;
-
- QFont font = cursor.charFormat().font();
- font.setUnderline(underline);
- QTextCharFormat format;
- format.setFont(font);
- mergeFormatOnWordOrSelection(format);
-
- emit underlineChanged();
-}
-
-bool DocumentHandler::italic() const
-{
- const QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return m_document->textDocument()->defaultFont().italic();
- return cursor.charFormat().font().italic();
-}
-
-void DocumentHandler::setItalic(bool italic)
-{
- const QTextCursor cursor = textCursor();
- if (!cursor.isNull() && cursor.charFormat().font().italic() == italic)
- return;
-
- QFont font = cursor.charFormat().font();
- font.setItalic(italic);
- QTextCharFormat format;
- format.setFont(font);
- mergeFormatOnWordOrSelection(format);
-
- emit italicChanged();
-}
-
-#include "moc_documenthandler.cpp"
diff --git a/examples/quickcontrols/texteditor/documenthandler.h b/examples/quickcontrols/texteditor/documenthandler.h
deleted file mode 100644
index b750997885..0000000000
--- a/examples/quickcontrols/texteditor/documenthandler.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef DOCUMENTHANDLER_H
-#define DOCUMENTHANDLER_H
-
-#include <QFont>
-#include <QObject>
-#include <QTextCursor>
-#include <QUrl>
-
-QT_BEGIN_NAMESPACE
-class QTextDocument;
-class QQuickTextDocument;
-QT_END_NAMESPACE
-
-class DocumentHandler : public QObject
-{
- Q_OBJECT
-
- Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
- Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
- Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
- Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
-
- Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged)
- Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
-
- Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
-
- Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
- Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY underlineChanged)
- Q_PROPERTY(bool italic READ italic WRITE setItalic NOTIFY italicChanged)
-
- Q_PROPERTY(QString fileName READ fileName NOTIFY fileUrlChanged)
- Q_PROPERTY(QString fileType READ fileType NOTIFY fileUrlChanged)
- Q_PROPERTY(QUrl fileUrl READ fileUrl NOTIFY fileUrlChanged)
-
- Q_PROPERTY(bool modified READ modified WRITE setModified NOTIFY modifiedChanged)
-
-public:
- explicit DocumentHandler(QObject *parent = nullptr);
-
- QQuickTextDocument *document() const;
- void setDocument(QQuickTextDocument *document);
-
- int cursorPosition() const;
- void setCursorPosition(int position);
-
- int selectionStart() const;
- void setSelectionStart(int position);
-
- int selectionEnd() const;
- void setSelectionEnd(int position);
-
- QColor textColor() const;
- void setTextColor(const QColor &color);
-
- Qt::Alignment alignment() const;
- void setAlignment(Qt::Alignment alignment);
-
- QFont font() const;
- void setFont(const QFont & font);
-
- bool bold() const;
- void setBold(bool bold);
-
- bool underline() const;
- void setUnderline(bool underline);
-
- bool italic() const;
- void setItalic(bool italic);
-
- QString fileName() const;
- QString fileType() const;
- QUrl fileUrl() const;
-
- bool modified() const;
- void setModified(bool m);
-
-public Q_SLOTS:
- void load(const QUrl &fileUrl);
- void saveAs(const QUrl &fileUrl);
-
-Q_SIGNALS:
- void documentChanged();
- void cursorPositionChanged();
- void selectionStartChanged();
- void selectionEndChanged();
-
- void fontChanged();
- void boldChanged();
- void underlineChanged();
- void italicChanged();
- void textColorChanged();
- void alignmentChanged();
-
- void textChanged();
- void fileUrlChanged();
-
- void loaded(const QString &text, int format);
- void error(const QString &message);
-
- void modifiedChanged();
-
-private:
- void reset();
- QTextCursor textCursor() const;
- QTextDocument *textDocument() const;
- void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
-
- QQuickTextDocument *m_document;
-
- int m_cursorPosition;
- int m_selectionStart;
- int m_selectionEnd;
-
- QUrl m_fileUrl;
-};
-
-#endif // DOCUMENTHANDLER_H
diff --git a/examples/quickcontrols/texteditor/example.md b/examples/quickcontrols/texteditor/example.md
index e385227e3b..f54edab5ae 100644
--- a/examples/quickcontrols/texteditor/example.md
+++ b/examples/quickcontrols/texteditor/example.md
@@ -1,10 +1,10 @@
# Markdown in Qt Quick
The Text, TextEdit and TextArea items support rich text formatted in HTML.
-Since Qt 5.14, they now support two dialects of Markdown as well:
-[The CommonMark Specification](https://spec.commonmark.org/0.29/) is the
+Since Qt 5.14, they support two dialects of Markdown as well:
+[The CommonMark Specification](https://spec.commonmark.org/0.31.2/) is the
conservative formal specification, while
-[GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown)
+[GitHub Flavored Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)
adds extra features such as task lists and tables.
If you are viewing this document in the Qt Quick Controls Text Editor example,
@@ -36,7 +36,7 @@ Block {
Block quotes can be nested, and block quotes can include indented code blocks.
-In [The CommonMark Specification](https://spec.commonmark.org/0.29/)
+In [The CommonMark Specification](https://spec.commonmark.org/0.31.2/)
John MacFarlane writes:
> What distinguishes Markdown from many other lightweight markup syntaxes,
@@ -158,7 +158,7 @@ Some Qt Widgets also support Markdown.
[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)
+[QTextDocument::toMarkdown](https://doc.qt.io/qt-6/qtextdocument.html#toMarkdown)
to rewrite the Markdown format, and save it back to a file.
If you have the [KDE Kate Editor](https://kate-editor.org/) installed on your
diff --git a/examples/quickcontrols/texteditor/fonts/fontello.ttf b/examples/quickcontrols/texteditor/fonts/fontello.ttf
index f02b44ec12..2db6e2bc4b 100644
--- a/examples/quickcontrols/texteditor/fonts/fontello.ttf
+++ b/examples/quickcontrols/texteditor/fonts/fontello.ttf
Binary files differ
diff --git a/examples/quickcontrols/texteditor/images/qt-logo.png b/examples/quickcontrols/texteditor/images/qt-logo.png
index 2ebc01aafa..63d0f222d8 100644
--- a/examples/quickcontrols/texteditor/images/qt-logo.png
+++ b/examples/quickcontrols/texteditor/images/qt-logo.png
Binary files differ
diff --git a/examples/quickcontrols/texteditor/qml/+touch/texteditor.qml b/examples/quickcontrols/texteditor/qml/+touch/texteditor.qml
index 2a0d9f117f..376a8341eb 100644
--- a/examples/quickcontrols/texteditor/qml/+touch/texteditor.qml
+++ b/examples/quickcontrols/texteditor/qml/+touch/texteditor.qml
@@ -4,17 +4,73 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import QtQuick.Window
-
-import io.qt.examples.texteditor
// TODO:
// - make designer-friendly
ApplicationWindow {
id: window
+ width: 480
+ height: 640
visible: true
- title: document.fileName + " - Text Editor Example"
+ title: textArea.textDocument.source +
+ " - Text Editor Example" + (textArea.textDocument.modified ? " *" : "")
+
+ Action {
+ id: boldAction
+ shortcut: StandardKey.Bold
+ checkable: true
+ checked: textArea.cursorSelection.font.bold
+ onTriggered: textArea.cursorSelection.font = Qt.font({ bold: checked })
+ }
+
+ Action {
+ id: italicAction
+ shortcut: StandardKey.Italic
+ checkable: true
+ checked: textArea.cursorSelection.font.italic
+ onTriggered: textArea.cursorSelection.font = Qt.font({ italic: checked })
+ }
+
+ Action {
+ id: underlineAction
+ shortcut: StandardKey.Underline
+ checkable: true
+ checked: textArea.cursorSelection.font.underline
+ onTriggered: textArea.cursorSelection.font = Qt.font({ underline: checked })
+ }
+
+ Action {
+ id: alignLeftAction
+ shortcut: "Ctrl+{"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignLeft
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignLeft
+ }
+
+ Action {
+ id: alignCenterAction
+ shortcut: "Ctrl+|"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignCenter
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignCenter
+ }
+
+ Action {
+ id: alignRightAction
+ shortcut: "Ctrl+}"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignRight
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignRight
+ }
+
+ Action {
+ id: alignJustifyAction
+ shortcut: "Ctrl+Alt+}"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignJustify
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignJustify
+ }
header: ToolBar {
leftPadding: 5
@@ -26,9 +82,10 @@ ApplicationWindow {
ToolButton {
id: doneEditingButton
font.family: "fontello"
- text: "\uE80A" // icon-ok
+ text: "\uE809" // icon-ok
opacity: !textArea.readOnly ? 1 : 0
onClicked: textArea.readOnly = true
+ Layout.fillWidth: false
}
Label {
@@ -43,6 +100,7 @@ ApplicationWindow {
font.family: "fontello"
text: "\uF142" // icon-ellipsis-vert
onClicked: menu.open()
+ Layout.fillWidth: false
Menu {
id: menu
@@ -56,23 +114,6 @@ ApplicationWindow {
}
}
- DocumentHandler {
- id: document
- document: textArea.textDocument
- cursorPosition: textArea.cursorPosition
- selectionStart: textArea.selectionStart
- selectionEnd: textArea.selectionEnd
- // textColor: TODO
- Component.onCompleted: document.load("qrc:/texteditor.html")
- onLoaded: {
- textArea.text = text
- }
- onError: {
- errorDialog.text = message
- errorDialog.visible = true
- }
- }
-
Flickable {
id: flickable
flickableDirection: Flickable.VerticalFlick
@@ -94,6 +135,23 @@ ApplicationWindow {
background: null
onLinkActivated: Qt.openUrlExternally(link)
+
+ Component.onCompleted: textDocument.source = "qrc:/texteditor.html"
+
+ textDocument.onStatusChanged: {
+ // a message lookup table using computed properties:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
+ const statusMessages = {
+ [ TextDocument.ReadError ]: qsTr("Failed to load “%1â€"),
+ [ TextDocument.WriteError ]: qsTr("Failed to save “%1â€"),
+ [ TextDocument.NonLocalFileError ]: qsTr("Not a local file: “%1â€"),
+ }
+ const err = statusMessages[textDocument.status]
+ if (err) {
+ errorDialog.text = err.arg(textDocument.source)
+ errorDialog.open()
+ }
+ }
}
ScrollBar.vertical: ScrollBar {}
@@ -117,27 +175,21 @@ ApplicationWindow {
font.family: "fontello"
// Don't want to close the virtual keyboard when this is clicked.
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.bold
- onClicked: document.bold = !document.bold
+ action: boldAction
}
ToolButton {
id: italicButton
text: "\uE801" // icon-italic
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.italic
- onClicked: document.italic = !document.italic
+ action: italicAction
}
ToolButton {
id: underlineButton
text: "\uF0CD" // icon-underline
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.underline
- onClicked: document.underline = !document.underline
+ action: underlineAction
}
ToolSeparator {}
@@ -147,36 +199,28 @@ ApplicationWindow {
text: "\uE803" // icon-align-left
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.alignment == Qt.AlignLeft
- onClicked: document.alignment = Qt.AlignLeft
+ action: alignLeftAction
}
ToolButton {
id: alignCenterButton
text: "\uE804" // icon-align-center
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.alignment == Qt.AlignHCenter
- onClicked: document.alignment = Qt.AlignHCenter
+ action: alignCenterAction
}
ToolButton {
id: alignRightButton
text: "\uE805" // icon-align-right
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.alignment == Qt.AlignRight
- onClicked: document.alignment = Qt.AlignRight
+ action: alignRightAction
}
ToolButton {
id: alignJustifyButton
text: "\uE806" // icon-align-justify
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.alignment == Qt.AlignJustify
- onClicked: document.alignment = Qt.AlignJustify
+ action: alignJustifyAction
}
}
}
diff --git a/examples/quickcontrols/texteditor/qml/texteditor.qml b/examples/quickcontrols/texteditor/qml/texteditor.qml
index decf0801e4..154a531752 100644
--- a/examples/quickcontrols/texteditor/qml/texteditor.qml
+++ b/examples/quickcontrols/texteditor/qml/texteditor.qml
@@ -6,9 +6,6 @@ import QtCore
import QtQuick.Controls
import QtQuick.Window
import QtQuick.Dialogs
-import Qt.labs.platform as Platform
-
-import io.qt.examples.texteditor
// TODO:
// - make designer-friendly
@@ -18,7 +15,8 @@ ApplicationWindow {
width: 1024
height: 600
visible: true
- title: document.fileName + " - Text Editor Example"
+ title: textArea.textDocument.source +
+ " - Text Editor Example" + (textArea.textDocument.modified ? " *" : "")
Component.onCompleted: {
x = Screen.width / 2 - width / 2
@@ -27,122 +25,194 @@ ApplicationWindow {
Action {
id: openAction
+ text: qsTr("&Open")
shortcut: StandardKey.Open
- onTriggered: openDialog.open()
+ onTriggered: {
+ if (textArea.textDocument.modified)
+ discardDialog.open()
+ else
+ openDialog.open()
+ }
+ }
+
+ Action {
+ id: saveAction
+ text: qsTr("&Save…")
+ shortcut: StandardKey.Save
+ enabled: textArea.textDocument.modified
+ onTriggered: textArea.textDocument.save()
}
Action {
id: saveAsAction
+ text: qsTr("Save &As…")
shortcut: StandardKey.SaveAs
onTriggered: saveDialog.open()
}
Action {
id: quitAction
+ text: qsTr("&Quit")
shortcut: StandardKey.Quit
onTriggered: close()
}
Action {
id: copyAction
+ text: qsTr("&Copy")
shortcut: StandardKey.Copy
+ enabled: textArea.selectedText
onTriggered: textArea.copy()
}
Action {
id: cutAction
+ text: qsTr("Cu&t")
shortcut: StandardKey.Cut
+ enabled: textArea.selectedText
onTriggered: textArea.cut()
}
Action {
id: pasteAction
+ text: qsTr("&Paste")
shortcut: StandardKey.Paste
+ enabled: textArea.canPaste
onTriggered: textArea.paste()
}
Action {
id: boldAction
+ text: qsTr("&Bold")
shortcut: StandardKey.Bold
- onTriggered: document.bold = !document.bold
+ checkable: true
+ checked: textArea.cursorSelection.font.bold
+ onTriggered: textArea.cursorSelection.font = Qt.font({ bold: checked })
}
Action {
id: italicAction
+ text: qsTr("&Italic")
shortcut: StandardKey.Italic
- onTriggered: document.italic = !document.italic
+ checkable: true
+ checked: textArea.cursorSelection.font.italic
+ onTriggered: textArea.cursorSelection.font = Qt.font({ italic: checked })
}
Action {
id: underlineAction
+ text: qsTr("&Underline")
shortcut: StandardKey.Underline
- onTriggered: document.underline = !document.underline
+ checkable: true
+ checked: textArea.cursorSelection.font.underline
+ onTriggered: textArea.cursorSelection.font = Qt.font({ underline: checked })
+ }
+
+ Action {
+ id: strikeoutAction
+ text: qsTr("&Strikeout")
+ checkable: true
+ checked: textArea.cursorSelection.font.strikeout
+ onTriggered: textArea.cursorSelection.font = Qt.font({ strikeout: checked })
+ }
+
+ Action {
+ id: alignLeftAction
+ text: qsTr("Align &Left")
+ shortcut: "Ctrl+{"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignLeft
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignLeft
+ }
+
+ Action {
+ id: alignCenterAction
+ text: qsTr("&Center")
+ shortcut: "Ctrl+|"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignCenter
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignCenter
+ }
+
+ Action {
+ id: alignRightAction
+ text: qsTr("Align &Right")
+ shortcut: "Ctrl+}"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignRight
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignRight
+ }
+
+ Action {
+ id: alignJustifyAction
+ text: qsTr("&Justify")
+ shortcut: "Ctrl+Alt+}"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignJustify
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignJustify
}
- Platform.MenuBar {
- Platform.Menu {
+ menuBar: MenuBar {
+ Menu {
title: qsTr("&File")
- Platform.MenuItem {
- text: qsTr("&Open")
- onTriggered: openDialog.open()
+ MenuItem {
+ action: openAction
}
- Platform.MenuItem {
- text: qsTr("&Save As...")
- onTriggered: saveDialog.open()
+ MenuItem {
+ action: saveAction
}
- Platform.MenuItem {
- text: qsTr("&Quit")
- onTriggered: close()
+ MenuItem {
+ action: saveAsAction
+ }
+ MenuItem {
+ action: quitAction
}
}
- Platform.Menu {
+ Menu {
title: qsTr("&Edit")
- Platform.MenuItem {
- text: qsTr("&Copy")
- enabled: textArea.selectedText
- onTriggered: textArea.copy()
+ MenuItem {
+ action: copyAction
}
- Platform.MenuItem {
- text: qsTr("Cu&t")
- enabled: textArea.selectedText
- onTriggered: textArea.cut()
+ MenuItem {
+ action: cutAction
}
- Platform.MenuItem {
- text: qsTr("&Paste")
- enabled: textArea.canPaste
- onTriggered: textArea.paste()
+ MenuItem {
+ action: pasteAction
}
}
- Platform.Menu {
+ Menu {
title: qsTr("F&ormat")
- Platform.MenuItem {
- text: qsTr("&Bold")
- checkable: true
- checked: document.bold
- onTriggered: document.bold = !document.bold
+ MenuItem {
+ action: boldAction
+ }
+ MenuItem {
+ action: italicAction
+ }
+ MenuItem {
+ action: underlineAction
+ }
+ MenuItem {
+ action: strikeoutAction
+ }
+
+ MenuSeparator {}
+
+ MenuItem {
+ action: alignLeftAction
}
- Platform.MenuItem {
- text: qsTr("&Italic")
- checkable: true
- checked: document.italic
- onTriggered: document.italic = !document.italic
+ MenuItem {
+ action: alignCenterAction
}
- Platform.MenuItem {
- text: qsTr("&Underline")
- checkable: true
- checked: document.underline
- onTriggered: document.underline = !document.underline
+ MenuItem {
+ action: alignJustifyAction
}
- Platform.MenuItem {
- text: qsTr("&Strikeout")
- checkable: true
- checked: document.strikeout
- onTriggered: document.strikeout = !document.strikeout
+ MenuItem {
+ action: alignRightAction
}
}
}
@@ -153,28 +223,29 @@ ApplicationWindow {
selectedNameFilter.index: 1
nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)", "Markdown files (*.md *.markdown)"]
currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
- onAccepted: document.load(selectedFile)
+ onAccepted: {
+ textArea.textDocument.modified = false // we asked earlier, if necessary
+ textArea.textDocument.source = selectedFile
+ }
}
FileDialog {
id: saveDialog
fileMode: FileDialog.SaveFile
- defaultSuffix: document.fileType
nameFilters: openDialog.nameFilters
- selectedNameFilter.index: document.fileType === "txt" ? 0 : 1
currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
- onAccepted: document.saveAs(selectedFile)
+ onAccepted: textArea.textDocument.saveAs(selectedFile)
}
FontDialog {
id: fontDialog
- onAccepted: document.font = selectedFont
+ onAccepted: textArea.cursorSelection.font = selectedFont
}
ColorDialog {
id: colorDialog
selectedColor: "black"
- onAccepted: document.textColor = selectedColor
+ onAccepted: textArea.cursorSelection.color = selectedColor
}
MessageDialog {
@@ -187,7 +258,23 @@ ApplicationWindow {
title: qsTr("Quit?")
text: qsTr("The file has been modified. Quit anyway?")
buttons: MessageDialog.Yes | MessageDialog.No
- onButtonClicked: function (button, role) { if (role === MessageDialog.YesRole) Qt.quit() }
+ onButtonClicked: function (button, role) {
+ if (role === MessageDialog.YesRole) {
+ textArea.textDocument.modified = false
+ Qt.quit()
+ }
+ }
+ }
+
+ MessageDialog {
+ id : discardDialog
+ title: qsTr("Discard changes?")
+ text: qsTr("The file has been modified. Open a new file anyway?")
+ buttons: MessageDialog.Yes | MessageDialog.No
+ onButtonClicked: function (button, role) {
+ if (role === MessageDialog.YesRole)
+ openDialog.open()
+ }
}
header: ToolBar {
@@ -206,6 +293,13 @@ ApplicationWindow {
action: openAction
focusPolicy: Qt.TabFocus
}
+ ToolButton {
+ id: saveButton
+ text: "\uE80A" // icon-floppy-disk
+ font.family: "fontello"
+ action: saveAction
+ focusPolicy: Qt.TabFocus
+ }
ToolSeparator {
contentItem.visible: fileRow.y === editRow.y
}
@@ -218,7 +312,6 @@ ApplicationWindow {
text: "\uF0C5" // icon-docs
font.family: "fontello"
focusPolicy: Qt.TabFocus
- enabled: textArea.selectedText
action: copyAction
}
ToolButton {
@@ -226,7 +319,6 @@ ApplicationWindow {
text: "\uE802" // icon-scissors
font.family: "fontello"
focusPolicy: Qt.TabFocus
- enabled: textArea.selectedText
action: cutAction
}
ToolButton {
@@ -234,7 +326,6 @@ ApplicationWindow {
text: "\uF0EA" // icon-paste
font.family: "fontello"
focusPolicy: Qt.TabFocus
- enabled: textArea.canPaste
action: pasteAction
}
ToolSeparator {
@@ -249,8 +340,6 @@ ApplicationWindow {
text: "\uE800" // icon-bold
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.bold
action: boldAction
}
ToolButton {
@@ -258,8 +347,6 @@ ApplicationWindow {
text: "\uE801" // icon-italic
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.italic
action: italicAction
}
ToolButton {
@@ -267,8 +354,6 @@ ApplicationWindow {
text: "\uF0CD" // icon-underline
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.underline
action: underlineAction
}
ToolButton {
@@ -276,21 +361,19 @@ ApplicationWindow {
text: "\uF0CC"
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.strikeout
- onClicked: document.strikeout = !document.strikeout
+ action: strikeoutAction
}
ToolButton {
id: fontFamilyToolButton
text: qsTr("\uE808") // icon-font
font.family: "fontello"
- font.bold: document.bold
- font.italic: document.italic
- font.underline: document.underline
- font.strikeout: document.strikeout
+ font.bold: textArea.cursorSelection.font.bold
+ font.italic: textArea.cursorSelection.font.italic
+ font.underline: textArea.cursorSelection.font.underline
+ font.strikeout: textArea.cursorSelection.font.strikeout
focusPolicy: Qt.TabFocus
onClicked: function () {
- fontDialog.selectedFont = document.font
+ fontDialog.selectedFont = textArea.cursorSelection.font
fontDialog.open()
}
}
@@ -300,14 +383,14 @@ ApplicationWindow {
font.family: "fontello"
focusPolicy: Qt.TabFocus
onClicked: function () {
- colorDialog.selectedColor = document.textColor
+ colorDialog.selectedColor = textArea.cursorSelection.color
colorDialog.open()
}
Rectangle {
width: aFontMetrics.width + 3
height: 2
- color: document.textColor
+ color: textArea.cursorSelection.color
parent: textColorButton.contentItem
anchors.horizontalCenter: parent.horizontalCenter
anchors.baseline: parent.baseline
@@ -332,79 +415,43 @@ ApplicationWindow {
text: "\uE803" // icon-align-left
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.alignment == Qt.AlignLeft
- onClicked: document.alignment = Qt.AlignLeft
+ action: alignLeftAction
}
ToolButton {
id: alignCenterButton
text: "\uE804" // icon-align-center
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.alignment == Qt.AlignHCenter
- onClicked: document.alignment = Qt.AlignHCenter
+ action: alignCenterAction
}
ToolButton {
id: alignRightButton
text: "\uE805" // icon-align-right
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.alignment == Qt.AlignRight
- onClicked: document.alignment = Qt.AlignRight
+ action: alignRightAction
}
ToolButton {
id: alignJustifyButton
text: "\uE806" // icon-align-justify
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.alignment == Qt.AlignJustify
- onClicked: document.alignment = Qt.AlignJustify
+ action: alignJustifyAction
}
}
}
}
- DocumentHandler {
- id: document
- document: textArea.textDocument
- cursorPosition: textArea.cursorPosition
- selectionStart: textArea.selectionStart
- selectionEnd: textArea.selectionEnd
-
- property alias family: document.font.family
- property alias bold: document.font.bold
- property alias italic: document.font.italic
- property alias underline: document.font.underline
- property alias strikeout: document.font.strikeout
- property alias size: document.font.pointSize
-
- Component.onCompleted: {
- if (Qt.application.arguments.length === 2)
- document.load("file:" + Qt.application.arguments[1]);
- else
- document.load("qrc:/texteditor.html")
- }
- onLoaded: function (text, format) {
- textArea.textFormat = format
- textArea.text = text
- }
- onError: function (message) {
- errorDialog.text = message
- errorDialog.open()
- }
- }
-
Flickable {
id: flickable
flickableDirection: Flickable.VerticalFlick
anchors.fill: parent
+ ScrollBar.vertical: ScrollBar {}
+
TextArea.flickable: TextArea {
id: textArea
- textFormat: Qt.RichText
+ textFormat: Qt.AutoText
wrapMode: TextArea.Wrap
focus: true
selectByMouse: true
@@ -418,60 +465,76 @@ ApplicationWindow {
bottomPadding: 0
background: null
- MouseArea {
+ TapHandler {
acceptedButtons: Qt.RightButton
- anchors.fill: parent
- onClicked: contextMenu.open()
+ onTapped: contextMenu.popup()
}
onLinkActivated: function (link) {
Qt.openUrlExternally(link)
}
- }
- ScrollBar.vertical: ScrollBar {}
+ Component.onCompleted: {
+ if (Qt.application.arguments.length === 2)
+ textDocument.source = "file:" + Qt.application.arguments[1]
+ else
+ textDocument.source = "qrc:/texteditor.html"
+ }
+
+ textDocument.onStatusChanged: {
+ // a message lookup table using computed properties:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
+ const statusMessages = {
+ [ TextDocument.ReadError ]: qsTr("Failed to load “%1â€"),
+ [ TextDocument.WriteError ]: qsTr("Failed to save “%1â€"),
+ [ TextDocument.NonLocalFileError ]: qsTr("Not a local file: “%1â€"),
+ }
+ const err = statusMessages[textDocument.status]
+ if (err) {
+ errorDialog.text = err.arg(textDocument.source)
+ errorDialog.open()
+ }
+ }
+ }
}
- Platform.Menu {
+ Menu {
id: contextMenu
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Copy")
- enabled: textArea.selectedText
- onTriggered: textArea.copy()
+ action: copyAction
}
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Cut")
- enabled: textArea.selectedText
- onTriggered: textArea.cut()
+ action: cutAction
}
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Paste")
- enabled: textArea.canPaste
- onTriggered: textArea.paste()
+ action: pasteAction
}
- Platform.MenuSeparator {}
+ MenuSeparator {}
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Font...")
onTriggered: function () {
- fontDialog.selectedFont = document.font
+ fontDialog.selectedFont = textArea.cursorSelection.font
fontDialog.open()
}
}
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Color...")
onTriggered: function () {
- colorDialog.selectedColor = document.textColor
+ colorDialog.selectedColor = textArea.cursorSelection.color
colorDialog.open()
}
}
}
onClosing: function (close) {
- if (document.modified) {
+ if (textArea.textDocument.modified) {
quitDialog.open()
close.accepted = false
}
diff --git a/examples/quickcontrols/texteditor/texteditor.cpp b/examples/quickcontrols/texteditor/texteditor.cpp
index 79cd6d8071..36f27bc5c2 100644
--- a/examples/quickcontrols/texteditor/texteditor.cpp
+++ b/examples/quickcontrols/texteditor/texteditor.cpp
@@ -1,35 +1,23 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#ifdef QT_WIDGETS_LIB
-#include <QApplication>
-#else
#include <QGuiApplication>
-#endif
#include <QFontDatabase>
#include <QDebug>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickStyle>
-#include "documenthandler.h"
-
int main(int argc, char *argv[])
{
QGuiApplication::setApplicationName("Text Editor");
QGuiApplication::setOrganizationName("QtProject");
-#ifdef QT_WIDGETS_LIB
- QApplication app(argc, argv);
-#else
QGuiApplication app(argc, argv);
-#endif
if (QFontDatabase::addApplicationFont(":/fonts/fontello.ttf") == -1)
qWarning() << "Failed to load fontello.ttf";
- qmlRegisterType<DocumentHandler>("io.qt.examples.texteditor", 1, 0, "DocumentHandler");
-
QStringList selectors;
#ifdef QT_EXTRA_FILE_SELECTOR
selectors += QT_EXTRA_FILE_SELECTOR;
diff --git a/examples/quickcontrols/texteditor/texteditor.html b/examples/quickcontrols/texteditor/texteditor.html
index 3c59a053c5..0cd406ea1c 100644
--- a/examples/quickcontrols/texteditor/texteditor.html
+++ b/examples/quickcontrols/texteditor/texteditor.html
@@ -20,24 +20,15 @@
Qt Quick Controls 2
</h2>
<p align="center">
- This example demonstrates a modern rich text editor. The UI uses Qt Labs Platforms to provide native menus and dialogs.
+ This example demonstrates a modern rich text editor.
+ In recent Qt versions, components in
+ <a href="https://doc.qt.io/qt-6/qtquick-controls-qmlmodule.html">Qt Quick Controls</a> and
+ <a href="https://doc.qt.io/qt-6/qtquick-dialogs-qmlmodule.html">Qt Quick Dialogs</a>
+ are implemented via their native-platform counterparts as much as possible.
+ <a href="https://doc.qt.io/qt-6/qml-qtquick-controls-textarea.html">TextArea</a>,
+ <a href="https://doc.qt.io/qt-6/qml-qtquick-textedit.html#cursorSelection-prop">TextSelection</a> and
+ <a href="https://doc.qt.io/qt-6/qml-qtquick-textedit.html#textDocument-prop">TextDocument</a>
+ provide text-editing API.
</p>
- <br />
- <br />
- <br />
-
- <p>
- Below you'll find a list of the native controls used in this application.
- </p>
-
- <ul>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-menu.html">Menu</a> - provides a QML API for native platform menu popups.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-menubar.html">MenuBar</a> - provides a QML API for native platform menubars.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-menuitem.html">MenuItem</a> - provides a QML API for native platform menu items.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-filedialog.html">FileDialog</a> - provides a QML API for native platform file dialogs.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-fontdialog.html">FontDialog</a> - provides a QML API for native platform font dialogs.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-colordialog.html">ColorDialog</a> - provides a QML API for native platform color dialogs.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-messagedialog.html">MessageDialog</a> - provides a QML API for native platform message dialogs.</li>
- </ul>
</body>
</html>
diff --git a/examples/quickcontrols/texteditor/texteditor.pro b/examples/quickcontrols/texteditor/texteditor.pro
index 5f021865d3..c224fe6c41 100644
--- a/examples/quickcontrols/texteditor/texteditor.pro
+++ b/examples/quickcontrols/texteditor/texteditor.pro
@@ -1,16 +1,11 @@
TEMPLATE = app
TARGET = texteditor
QT += quick quickcontrols2
-qtHaveModule(widgets): QT += widgets
cross_compile: DEFINES += QT_EXTRA_FILE_SELECTOR=\\\"touch\\\"
-HEADERS += \
- documenthandler.h
-
SOURCES += \
- texteditor.cpp \
- documenthandler.cpp
+ texteditor.cpp
OTHER_FILES += \
qml/*.qml
diff --git a/examples/quickcontrols/wearable/CMakeLists.txt b/examples/quickcontrols/wearable/CMakeLists.txt
index 2963040b79..eb3c17c3e9 100644
--- a/examples/quickcontrols/wearable/CMakeLists.txt
+++ b/examples/quickcontrols/wearable/CMakeLists.txt
@@ -1,206 +1,68 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
-project(wearable LANGUAGES CXX)
+project(wearableexample VERSION 1 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2 OPTIONAL_COMPONENTS Positioning Location)
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/wearable")
+qt_standard_project_setup()
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
+add_subdirectory(Wearable)
+add_subdirectory(WearableSettings)
+add_subdirectory(WearableStyle)
-qt_add_executable(wearable
+qt_add_executable(wearableexample WIN32 MACOSX_BUNDLE
wearable.cpp
)
-set_target_properties(wearable PROPERTIES
- WIN32_EXECUTABLE TRUE
- MACOSX_BUNDLE TRUE
+target_link_libraries(wearableexample PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
)
-target_link_libraries(wearable PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickControls2
+if(TARGET Qt6::Positioning)
+ target_link_libraries(wearableexample PUBLIC
+ Qt6::Positioning
+ )
+endif()
+
+if(TARGET Qt6::Location)
+target_link_libraries(wearableexample PUBLIC
+ Qt6::Location
)
+endif()
if(UNIX AND NOT APPLE AND CMAKE_CROSSCOMPILING)
find_package(Qt6 REQUIRED COMPONENTS QuickTemplates2)
# Work around QTBUG-86533
- target_link_libraries(wearable PRIVATE Qt::QuickTemplates2)
+ target_link_libraries(wearableexample PRIVATE Qt6::QuickTemplates2)
endif()
-# Resources:
-set(wearable_resource_files
- "icons/wearable/36x36/alarms.png"
- "icons/wearable/36x36/fitness.png"
- "icons/wearable/36x36/navigation.png"
- "icons/wearable/36x36/notifications.png"
- "icons/wearable/36x36/settings.png"
- "icons/wearable/36x36/weather.png"
- "icons/wearable/36x36/worldclock.png"
- "icons/wearable/36x36@2/alarms.png"
- "icons/wearable/36x36@2/fitness.png"
- "icons/wearable/36x36@2/navigation.png"
- "icons/wearable/36x36@2/notifications.png"
- "icons/wearable/36x36@2/settings.png"
- "icons/wearable/36x36@2/weather.png"
- "icons/wearable/36x36@2/worldclock.png"
- "icons/wearable/index.theme"
- "images/back.png"
- "images/back@2x.png"
- "images/background-dark.png"
- "images/background-light.png"
- "images/home.png"
- "images/home@2x.png"
- "qml/Alarms/AlarmsPage.qml"
- "qml/DemoMode.qml"
- "qml/DemoModeIndicator.qml"
- "qml/Fitness/FitnessPage.qml"
- "qml/Fitness/fitness.js"
- "qml/Fitness/images/man-running-dark.png"
- "qml/Fitness/images/man-running-dark@2x.png"
- "qml/Fitness/images/man-running-light.png"
- "qml/Fitness/images/man-running-light@2x.png"
- "qml/Fitness/images/man-walking-dark.png"
- "qml/Fitness/images/man-walking-dark@2x.png"
- "qml/Fitness/images/man-walking-light.png"
- "qml/Fitness/images/man-walking-light@2x.png"
- "qml/LauncherPage.qml"
- "qml/NaviButton.qml"
- "qml/Navigation/NavigationPage.qml"
- "qml/Navigation/RouteElement.qml"
- "qml/Navigation/images/end.png"
- "qml/Navigation/images/end@2x.png"
- "qml/Navigation/images/leftturn-dark.png"
- "qml/Navigation/images/leftturn-dark@2x.png"
- "qml/Navigation/images/leftturn-light.png"
- "qml/Navigation/images/leftturn-light@2x.png"
- "qml/Navigation/images/navigation-dark.png"
- "qml/Navigation/images/navigation-dark@2x.png"
- "qml/Navigation/images/navigation-light.png"
- "qml/Navigation/images/navigation-light@2x.png"
- "qml/Navigation/images/rightturn-dark.png"
- "qml/Navigation/images/rightturn-dark@2x.png"
- "qml/Navigation/images/rightturn-light.png"
- "qml/Navigation/images/rightturn-light@2x.png"
- "qml/Navigation/images/start.png"
- "qml/Navigation/images/start@2x.png"
- "qml/Navigation/images/straight-dark.png"
- "qml/Navigation/images/straight-dark@2x.png"
- "qml/Navigation/images/straight-light.png"
- "qml/Navigation/images/straight-light@2x.png"
- "qml/Navigation/images/uturn.png"
- "qml/Navigation/images/uturn@2x.png"
- "qml/Navigation/navigation.js"
- "qml/Navigation/walk_route.json"
- "qml/Notifications/NotificationsPage.qml"
- "qml/Notifications/images/avatarf-dark.png"
- "qml/Notifications/images/avatarf-dark@2x.png"
- "qml/Notifications/images/avatarf-light.png"
- "qml/Notifications/images/avatarf-light@2x.png"
- "qml/Notifications/images/avatarm-dark.png"
- "qml/Notifications/images/avatarm-dark@2x.png"
- "qml/Notifications/images/avatarm-light.png"
- "qml/Notifications/images/avatarm-light@2x.png"
- "qml/Notifications/images/missedcall-dark.png"
- "qml/Notifications/images/missedcall-dark@2x.png"
- "qml/Notifications/images/missedcall-light.png"
- "qml/Notifications/images/missedcall-light@2x.png"
- "qml/Notifications/notifications.js"
- "qml/Settings/SettingsPage.qml"
- "qml/Settings/images/bluetooth-dark.png"
- "qml/Settings/images/bluetooth-dark@2x.png"
- "qml/Settings/images/bluetooth-light.png"
- "qml/Settings/images/bluetooth-light@2x.png"
- "qml/Settings/images/brightness-dark.png"
- "qml/Settings/images/brightness-dark@2x.png"
- "qml/Settings/images/brightness-light.png"
- "qml/Settings/images/brightness-light@2x.png"
- "qml/Settings/images/demo-mode-dark.png"
- "qml/Settings/images/demo-mode-dark@2x.png"
- "qml/Settings/images/demo-mode-light.png"
- "qml/Settings/images/demo-mode-light@2x.png"
- "qml/Settings/images/demo-mode-white.png"
- "qml/Settings/images/demo-mode-white@2x.png"
- "qml/Settings/images/theme-dark.png"
- "qml/Settings/images/theme-dark@2x.png"
- "qml/Settings/images/theme-light.png"
- "qml/Settings/images/theme-light@2x.png"
- "qml/Settings/images/wifi-dark.png"
- "qml/Settings/images/wifi-dark@2x.png"
- "qml/Settings/images/wifi-light.png"
- "qml/Settings/images/wifi-light@2x.png"
- "qml/Style/PageIndicator.qml"
- "qml/Style/Slider.qml"
- "qml/Style/Switch.qml"
- "qml/Style/UIStyle.qml"
- "qml/Style/qmldir"
- "qml/SwipeViewPage.qml"
- "qml/Weather/WeatherPage.qml"
- "qml/Weather/images/humidity-dark.png"
- "qml/Weather/images/humidity-dark@2x.png"
- "qml/Weather/images/humidity-light.png"
- "qml/Weather/images/humidity-light@2x.png"
- "qml/Weather/images/pressure-dark.png"
- "qml/Weather/images/pressure-dark@2x.png"
- "qml/Weather/images/pressure-light.png"
- "qml/Weather/images/pressure-light@2x.png"
- "qml/Weather/images/sunrise-dark.png"
- "qml/Weather/images/sunrise-dark@2x.png"
- "qml/Weather/images/sunrise-light.png"
- "qml/Weather/images/sunrise-light@2x.png"
- "qml/Weather/images/sunset-dark.png"
- "qml/Weather/images/sunset-dark@2x.png"
- "qml/Weather/images/sunset-light.png"
- "qml/Weather/images/sunset-light@2x.png"
- "qml/Weather/images/temperature-dark.png"
- "qml/Weather/images/temperature-dark@2x.png"
- "qml/Weather/images/temperature-light.png"
- "qml/Weather/images/temperature-light@2x.png"
- "qml/Weather/images/wind-dark.png"
- "qml/Weather/images/wind-dark@2x.png"
- "qml/Weather/images/wind-light.png"
- "qml/Weather/images/wind-light@2x.png"
- "qml/Weather/weather.js"
- "qml/Weather/weather.json"
- "qml/WorldClock/Clock.qml"
- "qml/WorldClock/WorldClockPage.qml"
- "qml/WorldClock/images/center.png"
- "qml/WorldClock/images/center@2x.png"
- "qml/WorldClock/images/second.png"
- "qml/WorldClock/images/second@2x.png"
- "qml/WorldClock/images/swissdaydial.png"
- "qml/WorldClock/images/swissdaydial@2x.png"
- "qml/WorldClock/images/swissdayhour.png"
- "qml/WorldClock/images/swissdayhour@2x.png"
- "qml/WorldClock/images/swissdayminute.png"
- "qml/WorldClock/images/swissdayminute@2x.png"
- "qml/WorldClock/images/swissnightdial.png"
- "qml/WorldClock/images/swissnightdial@2x.png"
- "qml/WorldClock/images/swissnighthour.png"
- "qml/WorldClock/images/swissnighthour@2x.png"
- "qml/WorldClock/images/swissnightminute.png"
- "qml/WorldClock/images/swissnightminute@2x.png"
- "wearable.qml"
+set_target_properties(wearableexample PROPERTIES
+ MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.examples.wearable"
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
)
-qt6_add_resources(wearable "wearable"
- PREFIX
- "/"
- FILES
- ${wearable_resource_files}
+install(TARGETS wearableexample
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
-install(TARGETS wearable
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+qt_generate_deploy_qml_app_script(
+ TARGET wearableexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
+
diff --git a/examples/quickcontrols/wearable/Wearable/AlarmsPage.qml b/examples/quickcontrols/wearable/Wearable/AlarmsPage.qml
new file mode 100644
index 0000000000..79377a6c25
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/AlarmsPage.qml
@@ -0,0 +1,172 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls as QQC2
+import WearableStyle
+
+Item {
+ function addAlarm() {
+ alarms.append(
+ {
+ "title": qsTr("New alarm"),
+ "hour": 0,
+ "minute": 0,
+ "nextRing": qsTr("Thursday 20. Nov."),
+ "armed": false
+ })
+ }
+
+ QQC2.AbstractButton {
+ id: plusButton
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.topMargin: 5
+ anchors.leftMargin: 5
+
+ width: height
+ height: 40
+ onClicked: parent.addAlarm()
+
+ Image {
+ anchors.centerIn: parent
+ width: 32
+ height: 32
+ source: UIStyle.iconPath("plus")
+ }
+ }
+
+ ListModel {
+ id: alarms
+ ListElement {
+ title: qsTr("Workday morning")
+ hour: 6
+ minute: 30
+ nextRing: qsTr("Thursday 20. Nov.")
+ armed: false
+ }
+ ListElement {
+ title: qsTr("Weekend morning")
+ hour: 8
+ minute: 40
+ nextRing: qsTr("Saturday 22. Nov.")
+ armed: false
+ }
+ }
+
+ property alias alarmList: listview
+
+ ListView {
+ id: listview
+
+ anchors.fill: parent
+ anchors.margins: 15
+ anchors.topMargin: 40 + 15
+ spacing: 10
+
+ model: alarms
+
+ delegate: ListHeaderItem {
+ id: alarmItem
+
+ property alias onSwitch: onSwitch
+ property alias minuteTumbler: minuteTumbler
+ property alias hourTumbler: hourTumbler
+
+ required property string title
+ required property string nextRing
+ required property bool armed
+
+ width: parent.width
+ height: 94
+
+ Item {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 28
+
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.margins: 10
+ text: alarmItem.title
+ color: UIStyle.titletextColor
+ font: UIStyle.h3
+ }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.margins: 10
+ text: alarmItem.nextRing
+ color: UIStyle.titletextColor
+ font: UIStyle.p1
+ }
+ }
+
+ Item {
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 66
+
+ Switch {
+ id: onSwitch
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.margins: 15
+ checked: alarmItem.armed
+ }
+
+ QQC2.Tumbler {
+ id: minuteTumbler
+ height: parent.height
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.rightMargin: 0
+ visibleItemCount: 1
+ model: 60
+
+ delegate: Text {
+ required property int modelData
+ text: (modelData < 10 ? "0" : "") + modelData
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: 5
+ font: UIStyle.tumblerFont
+ color: UIStyle.textColor
+ }
+ }
+
+ Text {
+ id: timespacer
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: minuteTumbler.left
+ verticalAlignment: Text.AlignVCenter
+ font: UIStyle.tumblerFont
+ color: UIStyle.textColor
+ text: ":"
+ }
+
+ QQC2.Tumbler {
+ id: hourTumbler
+ height: parent.height
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: timespacer.left
+ visibleItemCount: 1
+ model: 12
+
+ delegate: Text {
+ required property int modelData
+ text: (modelData < 10 ? "0" : "") + modelData
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ rightPadding: 5
+ font: UIStyle.tumblerFont
+ color: UIStyle.textColor
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/AppSettings.qml b/examples/quickcontrols/wearable/Wearable/AppSettings.qml
new file mode 100644
index 0000000000..3af505867c
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/AppSettings.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma Singleton
+
+import QtCore
+
+Settings {
+ property bool wireless
+ property bool bluetooth
+ property int brightness
+ property bool darkTheme
+ property bool demoMode
+}
diff --git a/examples/quickcontrols/wearable/Wearable/CMakeLists.txt b/examples/quickcontrols/wearable/Wearable/CMakeLists.txt
new file mode 100644
index 0000000000..05cc1115a8
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/CMakeLists.txt
@@ -0,0 +1,152 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(wearable LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
+
+qt_policy(SET QTP0001 NEW)
+qt_add_qml_module(wearable
+ URI Wearable
+ PLUGIN_TARGET wearable
+ QML_FILES
+ AlarmsPage.qml
+ Clock.qml
+ DemoMode.qml
+ DemoModeIndicator.qml
+ FitnessPage.qml
+ LauncherPage.qml
+ ListHeaderItem.qml
+ ListItem.qml
+ Main.qml
+ MenuHeader.qml
+ NavigationPage.qml
+ NavigationFallbackPage.qml
+ NotificationsPage.qml
+ RouteElement.qml
+ SettingsPage.qml
+ SwipeViewPage.qml
+ WeatherPage.qml
+ WorldClockPage.qml
+ fitness.js
+ navigation.js
+ notifications.js
+ weather.js
+ RESOURCES
+ icons/wind-light.svg
+ icons/wind-dark.svg
+ icons/wifi-light.svg
+ icons/wifi-dark.svg
+ icons/weather-light.svg
+ icons/weather-dark.svg
+ icons/uturnright-light.svg
+ icons/uturnright-dark.svg
+ icons/uturnleft-light.svg
+ icons/uturnleft-dark.svg
+ icons/thermometer-light.svg
+ icons/thermometer-dark.svg
+ icons/sunset-light.svg
+ icons/sunset-dark.svg
+ icons/sunrise-light.svg
+ icons/sunrise-dark.svg
+ icons/sun-light.svg
+ icons/sun-dark.svg
+ icons/settings-light.svg
+ icons/settings-dark.svg
+ icons/right-light.svg
+ icons/right-dark.svg
+ icons/pressure-light.svg
+ icons/pressure-dark.svg
+ icons/plus-light.svg
+ icons/plus-dark.svg
+ icons/notification-light.svg
+ icons/notification-dark.svg
+ icons/maps-light.svg
+ icons/maps-dark.svg
+ icons/lightright-light.svg
+ icons/lightright-dark.svg
+ icons/lightleft-light.svg
+ icons/lightleft-dark.svg
+ icons/left-light.svg
+ icons/left-dark.svg
+ icons/hearth-light.svg
+ icons/hearth-dark.svg
+ icons/forward-light.svg
+ icons/forward-dark.svg
+ icons/drop-light.svg
+ icons/drop-dark.svg
+ icons/demomode-light.svg
+ icons/demomode-dark.svg
+ icons/darkmode-light.svg
+ icons/darkmode-dark.svg
+ icons/clock-light.svg
+ icons/clock-dark.svg
+ icons/bluetooth-light.svg
+ icons/bluetooth-dark.svg
+ icons/bell-light.svg
+ icons/bell-dark.svg
+ icons/bearright-light.svg
+ icons/bearright-dark.svg
+ icons/bearleft-light.svg
+ icons/bearleft-dark.svg
+ icons/back-light.svg
+ icons/back-dark.svg
+ icons/weather-thundershower-light.svg
+ icons/weather-thundershower-dark.svg
+ icons/weather-sunny-very-few-clouds-light.svg
+ icons/weather-sunny-very-few-clouds-dark.svg
+ icons/weather-sunny-light.svg
+ icons/weather-sunny-dark.svg
+ icons/weather-storm-light.svg
+ icons/weather-storm-dark.svg
+ icons/weather-snow-light.svg
+ icons/weather-snow-dark.svg
+ icons/weather-sleet-light.svg
+ icons/weather-sleet-dark.svg
+ icons/weather-showers-scattered-light.svg
+ icons/weather-showers-scattered-dark.svg
+ icons/weather-showers-light.svg
+ icons/weather-showers-dark.svg
+ icons/weather-overcast-light.svg
+ icons/weather-overcast-dark.svg
+ icons/weather-icy-light.svg
+ icons/weather-icy-dark.svg
+ icons/weather-haze-light.svg
+ icons/weather-haze-dark.svg
+ icons/weather-fog-light.svg
+ icons/weather-fog-dark.svg
+ icons/weather-few-clouds-light.svg
+ icons/weather-few-clouds-dark.svg
+ images/fitness-man-walking-light.svg
+ images/fitness-man-running-light.svg
+ images/fitness-man-walking-dark.svg
+ images/fitness-man-running-dark.svg
+ images/fallbackmap.png
+ fallbackroute.json
+ weather.json
+)
+
+target_link_libraries(wearable PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
+)
+
+if(UNIX AND NOT APPLE AND CMAKE_CROSSCOMPILING)
+ find_package(Qt6 REQUIRED COMPONENTS QuickTemplates2)
+
+ # Work around QTBUG-86533
+ target_link_libraries(wearable PRIVATE Qt6::QuickTemplates2)
+endif()
+
+install(TARGETS wearable
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/Wearable"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/Wearable"
+)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
+ DESTINATION "${CMAKE_INSTALL_BINDIR}/Wearable"
+)
diff --git a/examples/quickcontrols/wearable/Wearable/Clock.qml b/examples/quickcontrols/wearable/Wearable/Clock.qml
new file mode 100644
index 0000000000..638a336a9e
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/Clock.qml
@@ -0,0 +1,248 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Shapes
+import Wearable
+import WearableStyle
+
+SwipeViewPage {
+ id: clock
+
+ required property real shift
+ required property string cityName
+
+ property int hours
+ property int minutes
+ property int seconds
+ property bool night: false
+ property bool internationalTime: true //Unset for local time
+
+ function timeChanged() {
+ const date = new Date
+ hours = internationalTime ? date.getUTCHours() + Math.floor(clock.shift) : date.getHours()
+ night = (hours < 7 || hours > 19)
+ minutes = internationalTime ? date.getUTCMinutes() + ((clock.shift % 1) * 60) : date.getMinutes()
+ seconds = date.getUTCSeconds()
+ }
+
+ Timer {
+ interval: 100
+ running: true
+ repeat: true
+ onTriggered: clock.timeChanged()
+ }
+
+ Rectangle {
+
+ anchors.centerIn: parent
+
+ id: background
+ color: clock.night ? "#000000" : "#FFFFFF"
+ radius: width / 2
+ width: 200
+ height: 200
+
+ Repeater {
+ model: 12
+ Rectangle {
+ id: minuteMarker
+ x: parent.width / 2 + 73 ; y: parent.height / 2 - 3
+ width: 14
+ height: 6
+ color: clock.night ? "#FFFFFF" : "#000000"
+ antialiasing: true
+ required property int index
+ transform: Rotation {
+ angle: minuteMarker.index / 12 * 360
+ origin.x: -73
+ origin.y: 3
+ }
+ }
+ }
+
+ Repeater {
+ model: 60
+ Rectangle {
+ id: secondMarker
+ x: parent.width / 2 + 90 ; y: parent.height / 2 - 1
+ width: 8
+ height: 2
+ color: clock.night ? "#FFFFFF" : "#000000"
+ antialiasing: true
+ required property int index
+ transform: Rotation {
+ angle: secondMarker.index / 60 * 360
+ origin.x: -90
+ origin.y: 1
+ }
+ }
+ }
+
+ Shape {
+ id: hourHand
+ x: parent.width / 2
+ y: parent.width / 2
+ preferredRendererType: Shape.CurveRenderer
+
+ ShapePath {
+ fillColor: clock.night ? "#FFFFFF" : "#000000"
+ strokeWidth:0
+
+ startX: 5
+ startY: 14
+ PathLine {
+ x: -5
+ y: 14
+ }
+ PathLine {
+ x: -4
+ y: -71
+ }
+ PathLine {
+ x: 4
+ y: -71
+ }
+ }
+
+ transform: Rotation {
+ id: hourRotation
+ angle: clock.hours * 6 + clock.minutes * 6 / 12
+ Behavior on angle {
+ SpringAnimation {
+ spring: 2
+ damping: 0.2
+ modulus: 360
+ }
+ }
+ }
+ }
+
+ Shape {
+ id: minuteHand
+ x: parent.width / 2
+ y: parent.width / 2
+ preferredRendererType: Shape.CurveRenderer
+
+ ShapePath {
+ fillColor: clock.night ? "#FFFFFF" : "#000000"
+ strokeWidth:0
+
+ startX: 5
+ startY: 14
+ PathLine {
+ x: -5
+ y: 14
+ }
+ PathLine {
+ x: -4
+ y: -89
+ }
+ PathLine {
+ x: 4
+ y: -89
+ }
+ }
+
+ transform: Rotation {
+ id: minuteRotation
+ angle: clock.minutes * 6
+ Behavior on angle {
+ SpringAnimation {
+ spring: 2
+ damping: 0.2
+ modulus: 360
+ }
+ }
+ }
+ }
+
+ Shape {
+ id: secondHand
+ x: parent.width / 2
+ y: parent.width / 2
+ preferredRendererType: Shape.CurveRenderer
+
+ ShapePath {
+ fillColor: UIStyle.highlightColor
+ strokeWidth: 0
+
+ startX: 1
+ startY: 14
+ PathLine {
+ x: -1
+ y: 14
+ }
+ PathLine {
+ x: -1
+ y: -89
+ }
+ PathLine {
+ x: 1
+ y: -89
+ }
+ }
+
+ ShapePath {
+ fillColor: UIStyle.highlightColor
+ strokeWidth: 0
+
+ startX: 0
+ startY: 3
+ PathArc {
+ x: 0
+ y: -3
+ radiusX: 3
+ radiusY: 3
+ }
+ PathArc {
+ x: 0
+ y: 3
+ radiusX: 3
+ radiusY: 3
+ }
+ PathMove {
+ x: 0
+ y: -75
+ }
+ PathArc {
+ x: 0
+ y: -65
+ radiusX: 5
+ radiusY: 5
+ }
+ PathArc {
+ x: 0
+ y: -75
+ radiusX: 5
+ radiusY: 5
+ }
+ }
+
+ transform: Rotation {
+ id: secondsRotation
+ angle: clock.seconds * 6
+ Behavior on angle {
+ SpringAnimation {
+ spring: 2
+ damping: 0.2
+ modulus: 360
+ }
+ }
+ }
+ }
+ }
+
+ Text {
+ id: cityLabel
+ anchors.top: background.bottom
+ anchors.margins: 10
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ text: parent.cityName
+ color: UIStyle.textColor
+ font: UIStyle.h2
+ }
+
+}
diff --git a/examples/quickcontrols/wearable/Wearable/DemoMode.qml b/examples/quickcontrols/wearable/Wearable/DemoMode.qml
new file mode 100644
index 0000000000..3a040edeaf
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/DemoMode.qml
@@ -0,0 +1,174 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+import WearableSettings
+
+Item {
+ id: demoItem
+ required property StackView stackView
+
+ SequentialAnimation {
+ id: demoModeAnimation
+ loops: 2 //darkMode and lightMode
+ running: WearableSettings.demoMode
+
+ // Go back to the launcher page.
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.pop(null) }
+ PauseAnimation { duration: 2000 }
+
+
+ // Open the world clock page.
+ ScriptAction { script: demoItem.stackView.currentItem.currentIndex = 0 }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.launched(qsTr("World Clock"), "WorldClockPage.qml", "") }
+ PauseAnimation { duration: 2000 }
+
+ // Swipe across a few times.
+ SequentialAnimation {
+ loops: 6
+
+ ScriptAction { script: demoItem.stackView.currentItem.children[0].incrementCurrentIndex() }
+ PauseAnimation { duration: 2500 }
+ }
+
+ // Go back to the launcher page.
+ ScriptAction { script: demoItem.stackView.pop(null) }
+ PauseAnimation { duration: 2000 }
+
+
+
+ // Open the navigation page.
+ ScriptAction { script: demoItem.stackView.currentItem.incrementCurrentIndex() }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.launched(qsTr("Navigation"), "NavigationPage.qml", "NavigationFallbackPage.qml") }
+ PauseAnimation { duration: 2000 }
+
+ // Scroll through the route.
+ SequentialAnimation {
+ loops: 23
+
+ ScriptAction { script: demoItem.stackView.currentItem.incrementPoint() }
+ PauseAnimation { duration: 2000 }
+ }
+
+ // Go back to the launcher page.
+ ScriptAction { script: demoItem.stackView.pop(null) }
+ PauseAnimation { duration: 2000 }
+
+
+
+ // Open the weather page.
+ ScriptAction { script: demoItem.stackView.currentItem.incrementCurrentIndex() }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.launched(qsTr("Weather"), "WeatherPage.qml", "") }
+ PauseAnimation { duration: 2000 }
+
+ // Flick down..
+ SequentialAnimation {
+ loops: 3
+ ScriptAction { script: demoItem.stackView.currentItem.children[0].flick(0, - 800) }
+ PauseAnimation { duration: 2000 }
+ }
+
+ // Go back to the launcher page.
+ ScriptAction { script: demoItem.stackView.pop(null) }
+ PauseAnimation { duration: 2000 }
+
+
+
+ // Open the fitness page.
+ ScriptAction { script: demoItem.stackView.currentItem.incrementCurrentIndex() }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.launched(qsTr("Fitness"), "FitnessPage.qml", "") }
+ PauseAnimation { duration: 2000 }
+
+ // Swipe across once.
+ SequentialAnimation {
+ loops: 1
+
+ ScriptAction { script: demoItem.stackView.currentItem.children[0].incrementCurrentIndex() }
+ PauseAnimation { duration: 2000 }
+ }
+
+ // Go back to the launcher page.
+ ScriptAction { script: demoItem.stackView.pop(null) }
+ PauseAnimation { duration: 2000 }
+
+ // Open the notifications page.
+ ScriptAction { script: demoItem.stackView.currentItem.incrementCurrentIndex() }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.launched(qsTr("Notifications"), "NotificationsPage.qml", "") }
+ PauseAnimation { duration: 2000 }
+
+ // Go back to the launcher page.
+ ScriptAction { script: demoItem.stackView.pop(null) }
+ PauseAnimation { duration: 2000 }
+
+
+
+ // Open the alarms page.
+ ScriptAction { script: demoItem.stackView.currentItem.incrementCurrentIndex() }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.launched(qsTr("Alarm"), "AlarmsPage.qml", "") }
+ PauseAnimation { duration: 2000 }
+
+ // Toggle the switch.
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(0).onSwitch.toggle() }
+ PauseAnimation { duration: 500 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(1).onSwitch.toggle() }
+ PauseAnimation { duration: 2000 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(0).hourTumbler.currentIndex = 8 }
+ PauseAnimation { duration: 200 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(0).minuteTumbler.currentIndex = 15 }
+ PauseAnimation { duration: 300 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(1).hourTumbler.currentIndex = 9 }
+ PauseAnimation { duration: 200 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(1).minuteTumbler.currentIndex = 45 }
+ PauseAnimation { duration: 2000 }
+ ScriptAction { script: demoItem.stackView.currentItem.addAlarm() }
+ PauseAnimation { duration: 2000 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(2).onSwitch.toggle() }
+ PauseAnimation { duration: 100 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(0).onSwitch.toggle() }
+ PauseAnimation { duration: 100 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(1).onSwitch.toggle() }
+ PauseAnimation { duration: 2000 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(2).hourTumbler.currentIndex = 10 }
+ PauseAnimation { duration: 200 }
+ ScriptAction { script: demoItem.stackView.currentItem.alarmList.itemAtIndex(2).minuteTumbler.currentIndex = 30 }
+ PauseAnimation { duration: 2000 }
+
+ // Go back to the launcher page.
+ ScriptAction { script: demoItem.stackView.pop(null) }
+ PauseAnimation { duration: 2000 }
+
+
+ // Open the settings page.
+ ScriptAction { script: demoItem.stackView.currentItem.incrementCurrentIndex() }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.launched(qsTr("Settings"), "SettingsPage.qml", "") }
+ PauseAnimation { duration: 3000 }
+
+ // Toggle the switches.
+ ScriptAction { script: demoItem.stackView.currentItem.listView.brightnessItem.value = 80 }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.listView.bluetoothItem.toggle() }
+ PauseAnimation { duration: 1000 }
+ ScriptAction { script: demoItem.stackView.currentItem.listView.wifiItem.toggle() }
+ PauseAnimation { duration: 1000 }
+ SequentialAnimation {
+ loops: 2
+ ScriptAction { script: demoItem.stackView.currentItem.children[0].flick(0, - 800) }
+ PauseAnimation { duration: 500 }
+ }
+ ScriptAction { script: demoItem.stackView.currentItem.listView.darkmodeItem.toggle() }
+ PauseAnimation { duration: 3000 }
+
+ // Go back to the launcher page.
+ ScriptAction { script: demoItem.stackView.pop(null) }
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/DemoModeIndicator.qml b/examples/quickcontrols/wearable/Wearable/DemoModeIndicator.qml
new file mode 100644
index 0000000000..3e3f090803
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/DemoModeIndicator.qml
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls as QQC2
+import WearableStyle
+import QtQuick.Effects
+
+Item {
+ id: indicator
+ width: row.implicitWidth + margins * 2
+ height: row.implicitHeight + margins * 2
+
+ opacity: 0.8
+
+ readonly property int margins: 12
+
+ Behavior on y {
+ NumberAnimation {}
+ }
+
+ Rectangle {
+ id: demoModeIndicatorBg
+ anchors.fill: parent
+ radius: 20
+ color: UIStyle.colorRed
+ }
+
+ MultiEffect {
+ source: demoModeIndicatorBg
+ anchors.fill: parent
+ shadowEnabled: true
+ shadowBlur: 0.3
+ shadowHorizontalOffset: 2
+ shadowVerticalOffset: 2
+ opacity: 0.5
+ }
+ Row {
+ id: row
+ spacing: 8
+ anchors.fill: parent
+ anchors.leftMargin: parent.margins
+ anchors.rightMargin: parent.margins
+
+ Image {
+ source: UIStyle.iconPath("demomode")
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ QQC2.Label {
+ id: instructionLabel
+ text: qsTr("Tap screen to use")
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/FitnessPage.qml b/examples/quickcontrols/wearable/Wearable/FitnessPage.qml
new file mode 100644
index 0000000000..245d501842
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/FitnessPage.qml
@@ -0,0 +1,283 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls as QQC2
+import QtQuick.Shapes
+import Wearable
+import WearableStyle
+
+import "fitness.js" as FitnessData
+
+Item {
+
+ component AngularProgressBar : Shape {
+ id: apb
+ preferredRendererType: Shape.CurveRenderer
+
+ property real value: 0.5
+ property int rad: 75
+
+ ShapePath {
+ id: bgR
+ strokeColor: UIStyle.buttonBackground
+ strokeWidth: 8
+ capStyle: ShapePath.RoundCap
+ fillColor: "#00000000"
+ property real sA: (90 + 45) / 180 * Math.PI
+ property real eA: (360 + 45) / 180 * Math.PI
+ property real mA: (sA + eA) / 2
+ startX: apb.rad * Math.cos(bgR.sA)
+ startY: apb.rad * Math.sin(bgR.sA)
+ PathArc {
+ x: apb.rad * Math.cos(bgR.mA)
+ y: apb.rad * Math.sin(bgR.mA)
+ radiusX: apb.rad
+ radiusY: apb.rad
+ }
+ PathArc {
+ x: apb.rad * Math.cos(bgR.eA)
+ y: apb.rad * Math.sin(bgR.eA)
+ radiusX: apb.rad
+ radiusY: apb.rad
+ }
+ }
+
+ ShapePath {
+ id: fgR
+ strokeColor: UIStyle.buttonProgress
+ strokeWidth: 8
+ capStyle: ShapePath.RoundCap
+ fillColor: "#00000000"
+ property real sA: bgR.sA
+ property real eA: sA + (bgR.eA - sA) * apb.value
+ property real mA: (sA + eA) / 2
+ startX: apb.rad * Math.cos(fgR.sA)
+ startY: apb.rad * Math.sin(fgR.sA)
+ PathArc {
+ x: apb.rad * Math.cos(fgR.mA)
+ y: apb.rad * Math.sin(fgR.mA)
+ radiusX: apb.rad
+ radiusY: apb.rad
+ }
+ PathArc {
+ x: apb.rad * Math.cos(fgR.eA)
+ y: apb.rad * Math.sin(fgR.eA)
+ radiusX: apb.rad
+ radiusY: apb.rad
+ }
+ }
+ }
+
+ QQC2.SwipeView {
+ id: svFitnessContainer
+
+ anchors.fill: parent
+
+ SwipeViewPage {
+ id: fitnessPage1
+
+ Image {
+ id: walkingDude
+ anchors.centerIn: parent
+ source: UIStyle.themeImagePath("fitness-man-walking")
+ }
+
+ AngularProgressBar {
+ anchors.horizontalCenter: walkingDude.horizontalCenter
+ anchors.bottom: walkingDude.bottom
+ anchors.horizontalCenterOffset: 40
+ value: FitnessData.getWalkingSteps() / FitnessData.getWalkingGoal()
+ }
+
+ Column {
+ anchors.top: walkingDude.bottom
+ anchors.topMargin: 15
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ spacing: -7
+ Item {
+ width: childrenRect.width
+ height: childrenRect.height
+
+ Text {
+ id: steps
+ text: FitnessData.getWalkingSteps()
+ font: UIStyle.h2
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.bottom: steps.bottom
+ anchors.left: steps.right
+ anchors.leftMargin: 5
+ text: qsTr("steps")
+ font: UIStyle.p2
+ color: UIStyle.textColor
+ }
+ }
+
+ Item {
+ width: childrenRect.width
+ height: childrenRect.height
+
+ Text {
+ id: time
+ text: FitnessData.getWalkingTime()
+ font: UIStyle.h2
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.bottom: time.bottom
+ anchors.left: time.right
+ anchors.leftMargin: 5
+ text: qsTr("min")
+ font: UIStyle.p2
+ color: UIStyle.textColor
+ }
+ }
+
+ Item {
+ width: childrenRect.width
+ height: childrenRect.height
+
+ Text {
+ id: cal
+ text: FitnessData.getWalkingCalories()
+ font: UIStyle.h2
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.bottom: cal.bottom
+ anchors.left: cal.right
+ anchors.leftMargin: 5
+ text: qsTr("cal")
+ font: UIStyle.p2
+ color: UIStyle.textColor
+ }
+ }
+ }
+ }
+
+ SwipeViewPage {
+ id: fitnessPage2
+
+ Image {
+ id: runningDude
+ anchors.centerIn: parent
+ source: UIStyle.themeImagePath("fitness-man-running")
+ }
+
+ AngularProgressBar {
+ anchors.horizontalCenter: runningDude.horizontalCenter
+ anchors.bottom: runningDude.bottom
+ anchors.horizontalCenterOffset: 40
+ value: FitnessData.getRunningDistance() / FitnessData.getRunningGoal()
+ }
+
+ Row {
+ anchors.top: runningDude.bottom
+ anchors.horizontalCenter: runningDude.horizontalCenter
+ anchors.topMargin: 15
+
+ spacing: 10
+
+ Item {
+ width: childrenRect.width
+ height: childrenRect.height
+
+ Text {
+ id: goal
+ text: qsTr("Goal")
+ font: UIStyle.p2
+ color: UIStyle.textColor
+ }
+ Text {
+ anchors.bottom: goal.bottom
+ anchors.left: goal.right
+ anchors.leftMargin: 5
+ text: FitnessData.getRunningGoal() + " km"
+ font: UIStyle.h2
+ color: UIStyle.textColor
+ }
+ }
+
+ Rectangle {
+ width: 1
+ height: 75
+ color: UIStyle.textColor
+ }
+
+ Column {
+ spacing: -7
+ Item {
+ width: childrenRect.width
+ height: childrenRect.height
+ Text {
+ id: distance
+ text: qsTr("Distance")
+ font: UIStyle.p2
+ color: UIStyle.textColor
+ }
+ Text {
+ anchors.bottom: distance.bottom
+ anchors.left: distance.right
+ anchors.leftMargin: 5
+ text: FitnessData.getRunningDistance() + " km"
+ font: UIStyle.h2
+ color: UIStyle.textColor
+ }
+ }
+
+ Item {
+ width: childrenRect.width
+ height: childrenRect.height
+ Text {
+ id: runningtime
+ text: FitnessData.getRunningTime()
+ font: UIStyle.h2
+ color: UIStyle.textColor
+ }
+ Text {
+ anchors.bottom: runningtime.bottom
+ anchors.left: runningtime.right
+ anchors.leftMargin: 5
+ text: "min"
+ font: UIStyle.p2
+ color: UIStyle.textColor
+ }
+ }
+
+ Item {
+ width: childrenRect.width
+ height: childrenRect.height
+ Text {
+ id: runningcal
+ text: FitnessData.getRunningCalories()
+ font: UIStyle.h2
+ color: UIStyle.textColor
+ }
+ Text {
+ anchors.bottom: runningcal.bottom
+ anchors.left: runningcal.right
+ anchors.leftMargin: 5
+ text: qsTr("calories")
+ font: UIStyle.p2
+ color: UIStyle.textColor
+ }
+ }
+ }
+ }
+ }
+ }
+
+ QQC2.PageIndicator {
+ count: svFitnessContainer.count
+ currentIndex: svFitnessContainer.currentIndex
+
+ anchors.bottom: svFitnessContainer.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/LauncherPage.qml b/examples/quickcontrols/wearable/Wearable/LauncherPage.qml
new file mode 100644
index 0000000000..4c3e423e90
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/LauncherPage.qml
@@ -0,0 +1,185 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls as QQC2
+import WearableStyle
+
+//! [LauncherPage start]
+PathView {
+ id: circularView
+
+ signal launched(string title, string page, string fallbackpage)
+//! [LauncherPage start]
+
+ readonly property int cX: width / 2
+ readonly property int cY: height / 2
+ readonly property int size: Math.min(width - 80, height)
+ readonly property int itemSize: size / 5
+ readonly property int radius: size / 2 - itemSize / 4
+
+ snapMode: PathView.SnapToItem
+
+//! [Model start]
+ model: ListModel {
+//! [Model start]
+ ListElement {
+ title: qsTr("World Clock")
+ pageIcon: "clock"
+ page: "WorldClockPage.qml"
+ fallback: ""
+ }
+//! [Model mid]
+ ListElement {
+ title: qsTr("Navigation")
+ pageIcon: "maps"
+ page: "NavigationPage.qml"
+ fallback: "NavigationFallbackPage.qml"
+ }
+//! [Model mid]
+ ListElement {
+ title: qsTr("Weather")
+ pageIcon: "weather"
+ page: "WeatherPage.qml"
+ fallback: "WeatherPage.qml"
+ }
+ ListElement {
+ title: qsTr("Fitness")
+ pageIcon: "hearth"
+ page: "FitnessPage.qml"
+ fallback: ""
+ }
+ ListElement {
+ title: qsTr("Notifications")
+ pageIcon: "notification"
+ page: "NotificationsPage.qml"
+ fallback: ""
+ }
+ ListElement {
+ title: qsTr("Alarm")
+ pageIcon: "bell"
+ page: "AlarmsPage.qml"
+ fallback: ""
+ }
+//! [Model end]
+ ListElement {
+ title: qsTr("Settings")
+ pageIcon: "settings"
+ page: "SettingsPage.qml"
+ fallback: ""
+ }
+ }
+//! [Model end]
+
+//! [Delegate start]
+ delegate: QQC2.RoundButton {
+//! [Delegate start]
+ width: circularView.itemSize
+ height: circularView.itemSize
+
+ required property string title
+ required property string pageIcon
+ required property string page
+ required property string fallback
+ required property int index
+
+//! [Delegate mid]
+ icon.width: 36
+ icon.height: 36
+ icon.source: UIStyle.iconPath(pageIcon)
+ icon.color: UIStyle.textColor
+//! [Delegate mid]
+ padding: 12
+
+ background: Rectangle {
+ radius: width / 2
+ color: parent.PathView.isCurrentItem ? UIStyle.highlightColor : UIStyle.buttonBackground
+ border.width: 1
+ border.color: UIStyle.buttonGrayOutLine
+
+ Rectangle {
+ radius: parent.radius
+ anchors.fill: parent
+ gradient: Gradient {
+ GradientStop {
+ position: 0.0
+ color: UIStyle.gradientOverlay1
+ }
+ GradientStop {
+ position: 1.0
+ color: UIStyle.gradientOverlay2
+ }
+ }
+ }
+ }
+
+//! [Delegate end]
+ onClicked: {
+ if (PathView.isCurrentItem)
+ circularView.launched(title, Qt.resolvedUrl(page), Qt.resolvedUrl(fallback))
+ else
+ circularView.currentIndex = index
+ }
+ }
+//! [Delegate end]
+
+ path: Path {
+ startX: circularView.cX
+ startY: circularView.cY
+ PathAttribute {
+ name: "itemOpacity"
+ value: 1.0
+ }
+ PathLine {
+ x: circularView.cX + circularView.radius
+ y: circularView.cY
+ }
+ PathAttribute {
+ name: "itemOpacity"
+ value: 0.7
+ }
+ PathArc {
+ x: circularView.cX - circularView.radius
+ y: circularView.cY
+ radiusX: circularView.radius
+ radiusY: circularView.radius
+ useLargeArc: true
+ direction: PathArc.Clockwise
+ }
+ PathAttribute {
+ name: "itemOpacity"
+ value: 0.5
+ }
+ PathArc {
+ x: circularView.cX + circularView.radius
+ y: circularView.cY
+ radiusX: circularView.radius
+ radiusY: circularView.radius
+ useLargeArc: true
+ direction: PathArc.Clockwise
+ }
+ PathAttribute {
+ name: "itemOpacity"
+ value: 0.3
+ }
+ }
+
+ Text {
+ id: appTitle
+
+ property Item currentItem: circularView.currentItem
+
+ visible: currentItem ? currentItem.PathView.itemOpacity === 1.0 : 0
+
+ text: currentItem ? currentItem.title : ""
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: circularView.itemSize / 2 + height / 2
+
+ font: UIStyle.h1
+ color: UIStyle.textColor
+ }
+//! [LauncherPage end]
+}
+//! [LauncherPage end]
diff --git a/examples/quickcontrols/wearable/Wearable/ListHeaderItem.qml b/examples/quickcontrols/wearable/Wearable/ListHeaderItem.qml
new file mode 100644
index 0000000000..3cf82ff211
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/ListHeaderItem.qml
@@ -0,0 +1,83 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Effects
+import WearableStyle
+
+Item {
+ Rectangle {
+ id: listitem
+ width: parent.width
+ height: parent.height
+
+ radius: 8
+ color: UIStyle.listItemBackground
+
+ Shape {
+ id: header
+ property int ra: parent.radius
+ property int h: 28
+ property int w: parent.width
+
+ preferredRendererType: Shape.CurveRenderer
+
+ ShapePath { //Shape because Rectangle does not support diagonal gradient
+ strokeWidth: 0
+
+ startX: 0
+ startY: header.ra
+
+ PathArc {
+ x: header.ra
+ y: 0
+ radiusX: header.ra
+ radiusY: header.ra
+ }
+ PathLine {
+ x: header.w - header.ra
+ y: 0
+ }
+ PathArc {
+ x: header.w
+ y: header.ra
+ radiusX: header.ra
+ radiusY: header.ra
+ }
+ PathLine {
+ x: header.w
+ y: header.h
+ }
+ PathLine {
+ x: 0
+ y: header.h
+ }
+ fillGradient: LinearGradient {
+ x1: 0
+ y1: 0
+ x2: 2
+ y2: 1.3 * header.h
+ GradientStop {
+ position: 0.0
+ color: UIStyle.listHeader1
+ }
+ GradientStop {
+ position: 1.0
+ color: UIStyle.listHeader2
+ }
+ }
+ }
+ }
+ }
+
+ MultiEffect {
+ source: listitem
+ anchors.fill: parent
+ shadowEnabled: true
+ shadowBlur: 0.3
+ shadowHorizontalOffset: 2
+ shadowVerticalOffset: 2
+ opacity: 0.5
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/ListItem.qml b/examples/quickcontrols/wearable/Wearable/ListItem.qml
new file mode 100644
index 0000000000..b935a4c728
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/ListItem.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Effects
+import WearableStyle
+
+Item {
+ Rectangle {
+ id: listitem
+ width: parent.width
+ height: parent.height
+ radius: 8
+ color: UIStyle.listItemBackground
+ }
+
+ MultiEffect {
+ source: listitem
+ anchors.fill: parent
+ shadowEnabled: true
+ shadowBlur: 0.3
+ shadowHorizontalOffset: 2
+ shadowVerticalOffset: 2
+ opacity: 0.5
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/Main.qml b/examples/quickcontrols/wearable/Wearable/Main.qml
new file mode 100644
index 0000000000..4bd2d268ac
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/Main.qml
@@ -0,0 +1,125 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls as QQC2
+import QtQuick.Shapes
+
+import WearableSettings
+import WearableStyle
+
+//! [window start]
+QQC2.ApplicationWindow {
+ id: window
+//! [window start]
+ visible: true
+ width: 380
+ height: 380
+ title: qsTr("Wearable")
+
+ background: Shape {//Shape because Rectangle does not support diagonal gradient
+ preferredRendererType: Shape.CurveRenderer
+
+ ShapePath {
+ strokeWidth: 0
+ startX: 0; startY: 0
+
+ PathLine {
+ x: window.width
+ y: 0
+ }
+ PathLine {
+ x: window.width
+ y: window.height
+ }
+ PathLine {
+ x: 0
+ y: window.height
+ }
+ fillGradient: LinearGradient {
+ x1: window.width / 3
+ y1: 0
+ x2: window.width
+ y2: 1.3 * window.height
+ GradientStop {
+ position: 0.0
+ color: UIStyle.background1
+ }
+ GradientStop {
+ position: 0.5
+ color: UIStyle.background2
+ }
+ GradientStop {
+ position: 1.0
+ color: UIStyle.background3
+ }
+ }
+ }
+ }
+
+//! [stackview start]
+ QQC2.StackView {
+ id: stackView
+//! [stackview start]
+
+ anchors.fill: parent
+
+ focus: true
+
+//! [onLaunched connection]
+ initialItem: LauncherPage {
+ onLaunched: (title, page, fallback) => {
+ var createdPage = Qt.createComponent(page)
+ if (createdPage.status !== Component.Ready)
+ createdPage = Qt.createComponent(fallback)
+ stackView.push(createdPage)
+ header.title = title
+ }
+ }
+//! [onLaunched connection]
+//! [stackview end]
+ }
+
+//! [stackview end]
+ MenuHeader {
+ id: header
+
+ anchors.top: parent.top
+ width: parent.width
+
+ title: ""
+
+ enabled: stackView.depth > 1
+
+ onBackClicked: stackView.pop()
+ }
+
+//! [DemoMode]
+ DemoMode {
+ stackView: stackView
+ }
+
+//! [DemoMode]
+//! [DemoModeIndicator]
+ DemoModeIndicator {
+ id: demoModeIndicator
+ y: WearableSettings.demoMode ? header.height + 3 : -height - 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ z: header.z + 1
+ }
+
+//! [DemoModeIndicator]
+//! [MouseArea]
+ MouseArea {
+ enabled: WearableSettings.demoMode
+ anchors.fill: parent
+ onClicked: {
+ // Stop demo mode and return to the launcher page.
+ WearableSettings.demoMode = false
+ stackView.pop(null)
+ }
+ }
+//! [MouseArea]
+//! [window end]
+}
+//! [window end]
diff --git a/examples/quickcontrols/wearable/Wearable/MenuHeader.qml b/examples/quickcontrols/wearable/Wearable/MenuHeader.qml
new file mode 100644
index 0000000000..ade7cabe12
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/MenuHeader.qml
@@ -0,0 +1,92 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls as QQC2
+import WearableStyle
+import QtQuick.Shapes
+
+Item {
+ id: header
+ property alias title: labelText.text
+ signal backClicked()
+
+ height: 50
+
+ Shape {//Shape because Rectangle does not support diagonal gradient
+ id: background
+ preferredRendererType: Shape.CurveRenderer
+
+ ShapePath {
+ strokeWidth: 0
+
+ startX: 0
+ startY: 0
+ PathLine {
+ x: header.width
+ y: 0
+ }
+ PathLine {
+ x: header.width
+ y: header.height
+ }
+ PathLine {
+ x: 0
+ y: header.height
+ }
+ fillGradient: LinearGradient {
+ x1: header.width / 3
+ y1: 0
+ x2: header.width
+ y2: 1.3 * header.parent.height
+ GradientStop {
+ position: 0.0
+ color: UIStyle.background1
+ }
+ GradientStop {
+ position: 0.5
+ color: UIStyle.background2
+ }
+ GradientStop {
+ position: 1.0
+ color: UIStyle.background3
+ }
+ }
+ }
+ }
+
+ QQC2.AbstractButton {
+ id: backButton
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.topMargin: 0
+ anchors.leftMargin: 0
+
+ width: height
+ height: 40
+
+ onClicked: header.backClicked()
+
+ Image {
+ width: 40
+ height: 40
+ anchors.centerIn: parent
+ source: UIStyle.iconPath("back")
+ }
+ }
+
+ QQC2.Label {
+ id: labelText
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: UIStyle.textColor
+ font: UIStyle.h2
+ }
+
+ transform: Translate {
+ Behavior on y { NumberAnimation { } }
+ y: header.enabled ? 0 : -52
+ }
+
+
+}
diff --git a/examples/quickcontrols/wearable/Wearable/NavigationFallbackPage.qml b/examples/quickcontrols/wearable/Wearable/NavigationFallbackPage.qml
new file mode 100644
index 0000000000..d72f22655f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/NavigationFallbackPage.qml
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import WearableStyle
+import QtQuick.Effects
+
+import "navigation.js" as NavigationData
+
+Item {
+ id: navigationPage
+
+ Image {
+ id: view
+ anchors.fill: parent
+ source: "qrc:/qt/qml/Wearable/images/fallbackmap.png"
+
+ Item {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.margins: 10
+ anchors.topMargin: 60
+
+ height: dummyWarning.implicitHeight + 20
+
+ opacity: 0.8
+
+ Rectangle {
+ id: dummyWarningBg
+ anchors.fill: parent
+ radius: 20
+ color: UIStyle.colorRed
+ }
+
+ MultiEffect {
+ source: dummyWarningBg
+ anchors.fill: parent
+ shadowEnabled: true
+ shadowBlur: 0.3
+ shadowHorizontalOffset: 2
+ shadowVerticalOffset: 2
+ opacity: 0.5
+ }
+
+ Text {
+ id: dummyWarning
+ text: qsTr("Fallback mode. Install QtLocation for the real mapping experience.")
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.margins: 10
+ wrapMode: Text.WordWrap
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+
+ }
+
+ ListModel {
+ id: routeInfoModel
+ }
+
+ ListView {
+ id: routeView
+
+ height: 90
+ anchors.bottom: navigationPage.bottom
+ anchors.left: navigationPage.left
+ anchors.right: navigationPage.right
+ anchors.rightMargin: 10
+ anchors.leftMargin: 15
+ anchors.bottomMargin: 20
+
+ spacing: 12
+
+ clip: true
+ focus: true
+
+ boundsBehavior: Flickable.StopAtBounds
+ snapMode: ListView.SnapToItem
+
+ property int centeredIndex: 0
+
+ model: routeInfoModel
+
+ delegate: RouteElement {
+ width: routeView.width - 10
+ height: routeView.height - 10
+ }
+ }
+
+ Component.onCompleted: {
+ NavigationData.requestNavigationRoute(routeInfoModel)
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/NavigationPage.qml b/examples/quickcontrols/wearable/Wearable/NavigationPage.qml
new file mode 100644
index 0000000000..a83d7c137b
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/NavigationPage.qml
@@ -0,0 +1,228 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtLocation
+import QtPositioning
+import WearableStyle
+
+Item {
+ id: navigationPage
+
+ MapView {
+ id: view
+ anchors.fill: parent
+
+ map.plugin: Plugin {
+ name: "osm"
+ }
+ map.zoomLevel: 16
+
+ RouteModel {
+ id: routeModel
+ plugin : view.map.plugin
+ query: RouteQuery {
+ id: routeQuery
+ }
+ onStatusChanged: {
+ if (status == RouteModel.Ready) {
+ routeInfoModel.populate()
+ }
+ }
+ }
+
+ property real lat: 59.9485
+ property real lon: 10.7686
+
+ map.center: QtPositioning.coordinate(lat, lon)
+
+ Behavior on lon { PropertyAnimation { duration: 500; easing.type: Easing.InOutQuad } }
+ Behavior on lat { PropertyAnimation { duration: 500; easing.type: Easing.InOutQuad } }
+
+
+ MapItemView {
+ parent: view.map
+ model: routeModel
+ delegate: MapRoute {
+ id: route
+
+ required property var routeData
+ route: routeData
+ line.color: UIStyle.highlightColor
+ line.width: 5
+ smooth: true
+ opacity: 1
+ }
+ autoFitViewport: true
+ }
+
+ Component.onCompleted: {
+ var startCoordinate = QtPositioning.coordinate(59.9485, 10.7686);
+ var endCoordinate = QtPositioning.coordinate(59.9645, 10.671);
+
+ routeQuery.clearWaypoints();
+ routeQuery.addWaypoint(startCoordinate)
+ routeQuery.addWaypoint(endCoordinate)
+ routeQuery.travelModes = RouteQuery.CarTravel
+ routeQuery.routeOptimizations = RouteQuery.FastestRoute
+
+ routeModel.update();
+ }
+ }
+
+ ListModel {
+ id: routeInfoModel
+
+ function formatIconInstruction(direction)
+ {
+ switch (direction){
+ case RouteManeuver.NoDirection:
+ return "";
+ case RouteManeuver.DirectionForward:
+ return "forward";
+ case RouteManeuver.DirectionBearRight:
+ return "bearright";
+ case RouteManeuver.DirectionRight:
+ case RouteManeuver.DirectionHardRight:
+ return "right";
+ case RouteManeuver.DirectionLightRight:
+ return "lightright";
+ case RouteManeuver.DirectionUTurnRight:
+ return "uturnright";
+ case RouteManeuver.DirectionUTurnLeft:
+ return "uturleft";
+ case RouteManeuver.DirectionHardLeft:
+ case RouteManeuver.DirectionLeft:
+ return "left";
+ case RouteManeuver.DirectionLightLeft:
+ return "lightleft";
+ case RouteManeuver.DirectionBearLeft:
+ return "bearleft";
+ }
+ }
+
+ function formatShortInstruction(direction)
+ {
+ switch (direction){
+ case RouteManeuver.NoDirection:
+ return "";
+ case RouteManeuver.DirectionForward:
+ return "Move forward";
+ case RouteManeuver.DirectionBearRight:
+ return "Bear right";
+ case RouteManeuver.DirectionRight:
+ return "Turn right";
+ case RouteManeuver.DirectionHardRight:
+ return "Turn hard right";
+ case RouteManeuver.DirectionLightRight:
+ return "Turn slightly right";
+ case RouteManeuver.DirectionUTurnRight:
+ return "Uturn right";
+ case RouteManeuver.DirectionUTurnLeft:
+ return "Uturn left";
+ case RouteManeuver.DirectionHardLeft:
+ return "Turn hard left";
+ case RouteManeuver.DirectionLeft:
+ return "Turn left";
+ case RouteManeuver.DirectionLightLeft:
+ return "Turn slightly left";
+ case RouteManeuver.DirectionBearLeft:
+ return "Bear left";
+ }
+ }
+
+ function formatTime(sec)
+ {
+ var value = sec
+ var seconds = value % 60
+ value /= 60
+ value = (value > 1) ? Math.round(value) : 0
+ var minutes = value % 60
+ value /= 60
+ value = (value > 1) ? Math.round(value) : 0
+ var hours = value
+ if (hours > 0) value = hours + "h:"+ minutes + "m"
+ else value = minutes + "min"
+ return value
+ }
+
+ function formatDistance(meters)
+ {
+ var dist = Math.round(meters)
+ if (dist > 1000 ){
+ if (dist > 100000){
+ dist = Math.round(dist / 1000)
+ }
+ else{
+ dist = Math.round(dist / 100)
+ dist = dist / 10
+ }
+ dist = dist + " km"
+ }
+ else{
+ dist = dist + " m"
+ }
+ return dist
+ }
+
+ function populate()
+ {
+ routeInfoModel.clear()
+ if (routeModel.count > 0) {
+ for (var i = 0; i < routeModel.get(0).segments.length; i++) {
+ routeInfoModel.append({
+ "icon": formatIconInstruction(routeModel.get(0).segments[i].maneuver.direction),
+ "shortInfo": formatShortInstruction(routeModel.get(0).segments[i].maneuver.direction),
+ "instruction": routeModel.get(0).segments[i].maneuver.instructionText,
+ "distance": qsTr("in") + " " + formatDistance(routeModel.get(0).segments[i].maneuver.distanceToNextInstruction)
+ });
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: routeView
+
+ height: 90
+ anchors.bottom: navigationPage.bottom
+ anchors.left: navigationPage.left
+ anchors.right: navigationPage.right
+ anchors.rightMargin: 10
+ anchors.leftMargin: 15
+ anchors.bottomMargin: 20
+
+ spacing: 12
+
+ clip: true
+ focus: true
+
+ boundsBehavior: Flickable.StopAtBounds
+ snapMode: ListView.SnapToItem
+
+ property int centeredIndex: 0
+
+ model: routeInfoModel
+ delegate: RouteElement {
+ width: routeView.width - 10
+ height: routeView.height - 10
+ }
+
+ onContentYChanged: {
+ centeredIndex = indexAt(10, contentY)
+ if (centeredIndex > -1) {
+ view.lat = routeModel.get(0).segments[centeredIndex].maneuver.position.latitude
+ view.lon = routeModel.get(0).segments[centeredIndex].maneuver.position.longitude
+ }
+ }
+ }
+
+ function incrementPoint() {
+ routeView.centeredIndex++
+ if (routeView.centeredIndex >= routeInfoModel.count)
+ routeView.centeredIndex = 0
+ routeView.positionViewAtIndex(routeView.centeredIndex, ListView.Center)
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/NotificationsPage.qml b/examples/quickcontrols/wearable/Wearable/NotificationsPage.qml
new file mode 100644
index 0000000000..30c70f4a99
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/NotificationsPage.qml
@@ -0,0 +1,77 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import WearableStyle
+import "notifications.js" as NotificationData
+
+
+Item {
+ id: notificationpage
+
+ ListView {
+ id: listview
+
+ anchors.fill: parent
+ anchors.margins: 15
+ anchors.topMargin: 40 + 15
+ spacing: 10
+ clip: false
+
+ model: ListModel {
+ id: missedCallsList
+ }
+
+ delegate: ListHeaderItem {
+ id: notificationItem
+
+ required property string name
+ required property string date
+ required property string time
+ required property string text
+
+ width: parent.width
+ height: 80
+
+ Item {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 25
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.margins: 10
+ text: notificationItem.name
+ color: UIStyle.titletextColor
+ font: UIStyle.h3
+ }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.margins: 10
+ text: notificationItem.date + " " + notificationItem.time
+ color: UIStyle.titletextColor
+ font: UIStyle.p1
+ }
+ }
+
+
+ Text {
+ anchors.fill: parent
+ anchors.margins: 10
+ anchors.topMargin: 30
+ text: notificationItem.text
+ wrapMode: Text.WordWrap
+ color: UIStyle.textColor
+ font: UIStyle.p1
+ lineHeight: UIStyle.p1lineHeight
+ lineHeightMode: Text.FixedHeight
+ }
+ }
+
+ Component.onCompleted: {
+ NotificationData.populateData(missedCallsList)
+ }
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/RouteElement.qml b/examples/quickcontrols/wearable/Wearable/RouteElement.qml
new file mode 100644
index 0000000000..fe21b6af27
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/RouteElement.qml
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import WearableStyle
+
+ListHeaderItem {
+ id: routeListItem
+ required property string icon
+ required property string shortInfo
+ required property string instruction
+ required property string distance
+
+ Item {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 28
+
+ Image {
+ id: naviIcon
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.margins: 5
+ height: 20
+ width: height
+ source: UIStyle.iconPath(routeListItem.icon)
+ fillMode: Image.PreserveAspectFit
+ }
+
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: naviIcon.right
+ anchors.margins: 5
+ wrapMode: Text.WordWrap
+ text: routeListItem.shortInfo
+ font: UIStyle.h3
+ verticalAlignment: Text.AlignVCenter
+ padding: 1
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.margins: 10
+ wrapMode: Text.WordWrap
+ text: routeListItem.distance
+ font: UIStyle.h3
+ verticalAlignment: Text.AlignVCenter
+ padding: 1
+ color: UIStyle.textColor
+ }
+ }
+
+ Text {
+ id: instructionText
+ anchors.fill: parent
+ anchors.margins: 5
+ anchors.topMargin: 25
+ wrapMode: Text.WordWrap
+ text: routeListItem.instruction
+ font: UIStyle.p1
+ lineHeight: UIStyle.p1lineHeight
+ lineHeightMode: Text.FixedHeight
+ verticalAlignment: Text.AlignVCenter
+ padding: 1
+ color: UIStyle.textColor
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/SettingsPage.qml b/examples/quickcontrols/wearable/Wearable/SettingsPage.qml
new file mode 100644
index 0000000000..082893cef3
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/SettingsPage.qml
@@ -0,0 +1,136 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import WearableStyle
+import WearableSettings
+
+Item {
+ id: settingspage
+
+ property alias listView: listViewItem
+
+ component SettingsItem: ListItem {
+ id: settingsItem
+
+ property string title
+ property string icon
+
+ Image {
+ id: itemIcon
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.margins: 15
+ source: UIStyle.iconPath(settingsItem.icon)
+ height: 40
+ width: 40
+ }
+ Text {
+ anchors.left: itemIcon.right
+ anchors.verticalCenter: itemIcon.verticalCenter
+ anchors.margins: 15
+ text: settingsItem.title
+ color: UIStyle.textColor
+ font: UIStyle.h3
+ }
+ }
+
+ component SettingsBoolItem: SettingsItem {
+ height: 70
+
+ property alias checked: onSwitchItem.checked
+
+ Switch {
+ id: onSwitchItem
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: 15
+ onCheckedChanged: parent.onCheckedChanged()
+ }
+ function toggle() { onSwitchItem.toggle() }
+ signal onCheckedChanged()
+
+ }
+
+ component SettingsIntItem: SettingsItem {
+ height: 110
+
+ property alias value: valueSliderItem.value
+
+ Slider {
+ id: valueSliderItem
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 15
+ from: 0
+ to: 100
+ onValueChanged: parent.onValueChanged()
+ }
+
+ signal onValueChanged()
+ }
+
+ Flickable {
+ id: listViewItem
+
+ anchors.fill: parent
+ anchors.margins: 15
+ anchors.topMargin: 40 + 15
+ contentHeight: content.height
+
+ // aliases for the demo mode
+ property alias brightnessItem: brightnessItem
+ property alias bluetoothItem: bluetoothItem
+ property alias wifiItem: wifiItem
+ property alias darkmodeItem: darkmodeItem
+ property alias demomodeItem: demomodeItem
+
+ Column {
+ id: content
+ spacing: 10
+ width: parent.width
+
+ SettingsIntItem {
+ id: brightnessItem
+ width: parent.width
+ title: qsTr("Brightness")
+ icon: "sun"
+ value: WearableSettings.brightness
+ onValueChanged: WearableSettings.brightness = value
+ }
+ SettingsBoolItem {
+ id: bluetoothItem
+ width: parent.width
+ title: qsTr("Bluetooth")
+ icon: "bluetooth"
+ checked: WearableSettings.bluetooth
+ onCheckedChanged: WearableSettings.bluetooth = checked
+ }
+ SettingsBoolItem {
+ id: wifiItem
+ width: parent.width
+ title: qsTr("Wi-Fi")
+ icon: "wifi"
+ checked: WearableSettings.wireless
+ onCheckedChanged: WearableSettings.wireless = checked
+ }
+ SettingsBoolItem {
+ id: darkmodeItem
+ width: parent.width
+ title: qsTr("Change theme")
+ icon: "darkmode"
+ checked: WearableSettings.darkTheme
+ onCheckedChanged: WearableSettings.darkTheme = checked
+ }
+ SettingsBoolItem {
+ id: demomodeItem
+ width: parent.width
+ title: qsTr("Demo mode")
+ icon: "demomode"
+ checked: WearableSettings.demoMode
+ onCheckedChanged: WearableSettings.demoMode = checked
+ }
+ }
+ }
+}
diff --git a/examples/quickcontrols/wearable/qml/SwipeViewPage.qml b/examples/quickcontrols/wearable/Wearable/SwipeViewPage.qml
index 1f005363dc..1f005363dc 100644
--- a/examples/quickcontrols/wearable/qml/SwipeViewPage.qml
+++ b/examples/quickcontrols/wearable/Wearable/SwipeViewPage.qml
diff --git a/examples/quickcontrols/wearable/Wearable/WeatherPage.qml b/examples/quickcontrols/wearable/Wearable/WeatherPage.qml
new file mode 100644
index 0000000000..df0b6753e6
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/WeatherPage.qml
@@ -0,0 +1,330 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import WearableStyle
+import "weather.js" as WeatherData
+
+Item {
+ Flickable {
+ id: flick
+ anchors.fill:parent
+ anchors.margins: 15
+ anchors.topMargin: 40 + 15
+ contentWidth: width
+ contentHeight: column.height
+
+ Column {
+ id: column
+ spacing: 10
+
+ Item {
+ width: flick.contentWidth
+ height: 100
+
+ Row {
+ id: townName
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ width: childrenRect.width
+ height: childrenRect.height
+
+ spacing: 10
+ Image {
+ id: sunIcon
+ source: (wDataCntr.weatherData
+ && wDataCntr.weatherData.weather
+ && wDataCntr.weatherData.weather[0]
+ && wDataCntr.weatherData.weather[0].icon) ?
+ UIStyle.iconPath("weather-" + WeatherData.iconSelect(wDataCntr.weatherData.weather[0].icon)) : ""
+ width: 64
+ height: width
+ sourceSize.width: width
+ sourceSize.height: height
+ }
+ Text {
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.name) ?
+ wDataCntr.weatherData.name : ""
+ anchors.verticalCenter: sunIcon.verticalCenter
+ color: UIStyle.textColor
+ font: UIStyle.h1
+ }
+ }
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: townName.bottom
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.weather
+ && wDataCntr.weatherData.weather[0]
+ && wDataCntr.weatherData.weather[0].main) ?
+ wDataCntr.weatherData.weather[0].main : ""
+ color: UIStyle.textColor
+ font: UIStyle.h3
+ }
+ }
+
+ ListItem {
+ width: flick.contentWidth
+ height: 92
+
+ Image {
+ id: thermoimage
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.leftMargin: 20
+ anchors.topMargin: 17
+ source: UIStyle.iconPath("thermometer")
+ }
+
+ Text {
+ anchors.left: thermoimage.right
+ anchors.verticalCenter: thermoimage.verticalCenter
+ anchors.leftMargin: 5
+ text: qsTr("Temperature")
+ font: UIStyle.h4
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.right: parent.right
+ anchors.verticalCenter: thermoimage.verticalCenter
+ anchors.rightMargin: 20
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.main
+ && wDataCntr.weatherData.main.temp) ?
+ WeatherData.formatTemp(wDataCntr.weatherData.main.temp) : ""
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ }
+
+ Text {
+ id: maxtxt
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.leftMargin: 20
+ anchors.bottomMargin: 17
+ text: qsTr("Max")
+ font: UIStyle.h4
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.left: maxtxt.right
+ anchors.verticalCenter: maxtxt.verticalCenter
+ anchors.leftMargin: 10
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.main
+ && wDataCntr.weatherData.main.temp_max) ?
+ WeatherData.formatTemp(wDataCntr.weatherData.main.temp_max) : ""
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ }
+
+ Text {
+ id: mintxt
+ anchors.horizontalCenter: maxtxt.horizontalCenter
+ anchors.verticalCenter: maxtxt.verticalCenter
+ anchors.horizontalCenterOffset: parent.width / 2
+ text: qsTr("Min")
+ font: UIStyle.h4
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.left: mintxt.right
+ anchors.verticalCenter: mintxt.verticalCenter
+ anchors.leftMargin: 10
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.main
+ && wDataCntr.weatherData.main.temp_min) ?
+ WeatherData.formatTemp(wDataCntr.weatherData.main.temp_min) : ""
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ }
+ }
+
+ ListItem {
+ width: flick.contentWidth
+ height: 50
+
+ Image {
+ id: sunriseIcon
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 20
+ anchors.topMargin: 17
+ source: UIStyle.iconPath("sunrise")
+ }
+
+ Text {
+ id: sunriseText
+ anchors.left: sunriseIcon.right
+ anchors.verticalCenter: sunriseIcon.verticalCenter
+ anchors.leftMargin: 5
+ text: qsTr("Sunrise")
+ font: UIStyle.h4
+ color: UIStyle.textColor
+ }
+ Text {
+ anchors.left: sunriseText.right
+ anchors.verticalCenter: sunriseText.verticalCenter
+ anchors.leftMargin: 15
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.sys
+ && wDataCntr.weatherData.sys.sunrise) ?
+ WeatherData.getTimeHMS(wDataCntr.weatherData.sys.sunrise) : ""
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ }
+
+ Image {
+ id: sunsetIcon
+ anchors.right: sunsetText.left
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.rightMargin: 5
+ source: UIStyle.iconPath("sunset")
+ }
+
+ Text {
+ id: sunsetText
+ anchors.right: sunsetValue.left
+ anchors.verticalCenter: sunsetIcon.verticalCenter
+ anchors.rightMargin: 15
+ text: qsTr("Sunset")
+ font: UIStyle.h4
+ color: UIStyle.textColor
+ }
+
+ Text {
+ id: sunsetValue
+ anchors.right: parent.right
+ anchors.verticalCenter: sunsetText.verticalCenter
+ anchors.rightMargin: 20
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.sys
+ && wDataCntr.weatherData.sys.sunset) ?
+ WeatherData.getTimeHMS(wDataCntr.weatherData.sys.sunset) : ""
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ }
+ }
+
+ ListItem {
+ width: flick.contentWidth
+ height: 50
+
+ Image {
+ id: windIcon
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 20
+ anchors.topMargin: 17
+ source: UIStyle.iconPath("wind")
+ }
+
+ Text {
+ id: windText
+ anchors.left: windIcon.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 5
+ text: qsTr("Wind")
+ font: UIStyle.h4
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.rightMargin: 20
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.wind
+ && wDataCntr.weatherData.wind.speed) ?
+ Math.round(wDataCntr.weatherData.wind.speed * 1.61) + " km/h" : ""
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ }
+ }
+
+ ListItem {
+ width: flick.contentWidth
+ height: 50
+
+ Image {
+ id: humidityIcon
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 20
+ anchors.topMargin: 17
+ source: UIStyle.iconPath("drop")
+ }
+
+ Text {
+ id: humidityText
+ anchors.left: humidityIcon.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 5
+ text: qsTr("Humidity")
+ font: UIStyle.h4
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.rightMargin: 20
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.main
+ && wDataCntr.weatherData.main.humidity) ?
+ wDataCntr.weatherData.main.humidity + " %" : ""
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ }
+ }
+
+ ListItem {
+ width: flick.contentWidth
+ height: 50
+
+ Image {
+ id: pressureIcon
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 20
+ anchors.topMargin: 17
+ source: UIStyle.iconPath("pressure")
+ }
+
+ Text {
+ id: pressureText
+ anchors.left: pressureIcon.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 5
+ text: qsTr("HPA")
+ font: UIStyle.h4
+ color: UIStyle.textColor
+ }
+
+ Text {
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.rightMargin: 20
+ text: (wDataCntr.weatherData
+ && wDataCntr.weatherData.main
+ && wDataCntr.weatherData.main.pressure) ?
+ Math.round(wDataCntr.weatherData.main.pressure) + " hpa" : ""
+ font: UIStyle.h3
+ color: UIStyle.textColor
+ }
+ }
+ }
+ }
+
+ QtObject {
+ id: wDataCntr
+ property var weatherData
+ }
+ Component.onCompleted: {
+ WeatherData.requestWeatherData(wDataCntr)
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/WorldClockPage.qml b/examples/quickcontrols/wearable/Wearable/WorldClockPage.qml
new file mode 100644
index 0000000000..c90442eeac
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/WorldClockPage.qml
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls as QQC2
+
+Item {
+ QQC2.SwipeView {
+ id: svWatchContainer
+
+ anchors.fill: parent
+
+ ListModel {
+ id: placesList
+ ListElement {
+ cityName: "New York"
+ shift: -4
+ }
+ ListElement {
+ cityName: "London"
+ shift: 0
+ }
+ ListElement {
+ cityName: "Oslo"
+ shift: 1
+ }
+ ListElement {
+ cityName: "Mumbai"
+ shift: 5.5
+ }
+ ListElement {
+ cityName: "Tokyo"
+ shift: 9
+ }
+ ListElement {
+ cityName: "Brisbane"
+ shift: 10
+ }
+ ListElement {
+ cityName: "Los Angeles"
+ shift: -8
+ }
+ }
+
+ Repeater {
+ model: placesList
+ delegate: Clock {
+ }
+ }
+ }
+
+ QQC2.PageIndicator {
+ count: svWatchContainer.count
+ currentIndex: svWatchContainer.currentIndex
+
+ anchors.bottom: svWatchContainer.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/fallbackroute.json b/examples/quickcontrols/wearable/Wearable/fallbackroute.json
new file mode 100644
index 0000000000..b7aa5f4a9e
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/fallbackroute.json
@@ -0,0 +1,78 @@
+{
+ "route": {
+ "steps": [
+ {"direction": 4,
+ "instructionText": "Head South onto Sandakerveien",
+ "distanceToNextInstruction": 176.5 },
+ {"direction": 3,
+ "instructionText": "Enter the roundabout and take the first exit onto Kristoffer Aamots gate",
+ "distanceToNextInstruction": 3.5 },
+ {"direction": 4,
+ "instructionText": "exit roundabout to/onto Kristoffer Aamots gate",
+ "distanceToNextInstruction": 434.2 },
+ {"direction": 3,
+ "instructionText": "Enter the roundabout and take the second exit onto Kristoffer Aamots gate",
+ "distanceToNextInstruction": 26.5 },
+ {"direction": 3,
+ "instructionText": "exit roundabout to/onto Kristoffer Aamots gate",
+ "distanceToNextInstruction": 183.4 },
+ {"direction": 3,
+ "instructionText": "Enter the roundabout and take the first exit onto Maridalsveien",
+ "distanceToNextInstruction": 3.4 },
+ {"direction": 4,
+ "instructionText": "exit roundabout to/onto Maridalsveien",
+ "distanceToNextInstruction": 796.2 },
+ {"direction": 3,
+ "instructionText": "Enter the roundabout and take the second exit",
+ "distanceToNextInstruction": 38 },
+ {"direction": 1,
+ "instructionText": "exit roundabout to/onto ",
+ "distanceToNextInstruction": 220.9 },
+ {"direction": 10,
+ "instructionText": "Merge slightly left on TÃ¥sentunnelen",
+ "distanceToNextInstruction": 1041.4 },
+ {"direction": 1,
+ "instructionText": "Continue straight onto Torgny Segerstedts vei",
+ "distanceToNextInstruction": 1435.2 },
+ {"direction": 3,
+ "instructionText": "Take the ramp on the right",
+ "distanceToNextInstruction": 340.8 },
+ {"direction": 3,
+ "instructionText": "Turn slightly right onto Slemdalsveien",
+ "distanceToNextInstruction": 1073.2 },
+ {"direction": 4,
+ "instructionText": "Continue right onto Risalléen",
+ "distanceToNextInstruction": 303.7 },
+ {"direction": 1,
+ "instructionText": "Continue straight onto Frognerseterveien",
+ "distanceToNextInstruction": 943.6 },
+ {"direction": 1,
+ "instructionText": "Continue straight onto Ankerveien",
+ "distanceToNextInstruction": 731.3 },
+ {"direction": 4,
+ "instructionText": "Turn right onto Holmenkollveien",
+ "distanceToNextInstruction": 125 },
+ {"direction": 4,
+ "instructionText": "Continue right onto Holmenkollveien",
+ "distanceToNextInstruction": 116.7 },
+ {"direction": 9,
+ "instructionText": "Continue left onto Holmenkollveien",
+ "distanceToNextInstruction": 593.6 },
+ {"direction": 4,
+ "instructionText": "At the end of the road, turn right onto Holmenkollveien",
+ "distanceToNextInstruction": 455.1 },
+ {"direction": 9,
+ "instructionText": "Turn left",
+ "distanceToNextInstruction": 299.1 },
+ {"direction": 4,
+ "instructionText": "Turn right",
+ "distanceToNextInstruction": 110.5 },
+ {"direction": 4,
+ "instructionText": "Turn right",
+ "distanceToNextInstruction": 142.9 },
+ {"direction": 9,
+ "instructionText": "You have arrived at your destination, on the left",
+ "distanceToNextInstruction": 0 }
+ ]
+ }
+}
diff --git a/examples/quickcontrols/wearable/Wearable/fitness.js b/examples/quickcontrols/wearable/Wearable/fitness.js
new file mode 100644
index 0000000000..220d1d24b9
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/fitness.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+function getWalkingSteps() {
+ return 4000.;
+}
+
+function getWalkingGoal() {
+ return 8000.;
+}
+
+function getWalkingTime() {
+ return 40.;
+}
+
+function getWalkingCalories() {
+ return 100.;
+}
+
+function getRunningGoal() {
+ return 8.;
+}
+
+function getRunningDistance() {
+ return 4.;
+}
+
+function getRunningTime() {
+ return 16.;
+}
+
+function getRunningCalories() {
+ return 200.;
+}
diff --git a/examples/quickcontrols/wearable/Wearable/icons/back-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/back-dark.svg
new file mode 100644
index 0000000000..a2bf8c30ff
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/back-dark.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11 20C11 19.4477 11.4477 19 12 19H27C27.5523 19 28 19.4477 28 20C28 20.5523 27.5523 21 27 21H12C11.4477 21 11 20.5523 11 20Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.7058 12.2929C20.0981 12.6834 20.0981 13.3166 19.7058 13.7071L13.3838 20L19.7058 26.2929C20.0981 26.6834 20.0981 27.3166 19.7058 27.7071C19.3134 28.0976 18.6773 28.0976 18.285 27.7071L11.3237 20.7778C10.8921 20.3482 10.8921 19.6518 11.3237 19.2222L18.285 12.2929C18.6773 11.9024 19.3134 11.9024 19.7058 12.2929Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/back-light.svg b/examples/quickcontrols/wearable/Wearable/icons/back-light.svg
new file mode 100644
index 0000000000..53c97ddc73
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/back-light.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11 20C11 19.4477 11.4477 19 12 19H27C27.5523 19 28 19.4477 28 20C28 20.5523 27.5523 21 27 21H12C11.4477 21 11 20.5523 11 20Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.7058 12.2929C20.0981 12.6834 20.0981 13.3166 19.7058 13.7071L13.3838 20L19.7058 26.2929C20.0981 26.6834 20.0981 27.3166 19.7058 27.7071C19.3134 28.0976 18.6773 28.0976 18.285 27.7071L11.3237 20.7778C10.8921 20.3482 10.8921 19.6518 11.3237 19.2222L18.285 12.2929C18.6773 11.9024 19.3134 11.9024 19.7058 12.2929Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/bearleft-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/bearleft-dark.svg
new file mode 100644
index 0000000000..cce617b716
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/bearleft-dark.svg
@@ -0,0 +1,7 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 16L17 11V5" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="2 1"/>
+<path d="M12 16L7 11V5" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M12 16V20" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10 7L7 4L4 7" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M20 7L17 4L14 7" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="2 1"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/bearleft-light.svg b/examples/quickcontrols/wearable/Wearable/icons/bearleft-light.svg
new file mode 100644
index 0000000000..e03ae2cdc6
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/bearleft-light.svg
@@ -0,0 +1,7 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 16L17 11V5" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="2 1"/>
+<path d="M12 16L7 11V5" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M12 16V20" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10 7L7 4L4 7" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M20 7L17 4L14 7" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="2 1"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/bearright-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/bearright-dark.svg
new file mode 100644
index 0000000000..c7c3e71006
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/bearright-dark.svg
@@ -0,0 +1,7 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 16L7 11V5" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="2 1"/>
+<path d="M12 16L17 11V5" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M12 16V20" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M14 7L17 4L20 7" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M4 7L7 4L10 7" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="2 1"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/bearright-light.svg b/examples/quickcontrols/wearable/Wearable/icons/bearright-light.svg
new file mode 100644
index 0000000000..5e6b77ec1b
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/bearright-light.svg
@@ -0,0 +1,7 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 16L7 11V5" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="2 1"/>
+<path d="M12 16L17 11V5" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M12 16V20" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M14 7L17 4L20 7" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M4 7L7 4L10 7" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="2 1"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/bell-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/bell-dark.svg
new file mode 100644
index 0000000000..9b010af6b5
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/bell-dark.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3334 2.66671C17.3334 1.93033 16.7365 1.33337 16.0001 1.33337C15.2637 1.33337 14.6667 1.93033 14.6667 2.66671V4.11031C12.9044 4.40383 11.2703 5.27349 10.0063 6.60077C8.43031 8.25552 7.55565 10.4866 7.55565 12.8C7.55565 16.9696 6.70611 19.5542 5.928 21.0521C5.53753 21.8037 5.15882 22.2939 4.89904 22.5819C4.7688 22.7262 4.66748 22.8208 4.6082 22.8726C4.57854 22.8986 4.55933 22.9139 4.55223 22.9195L4.5513 22.9202C4.08705 23.2564 3.88898 23.8527 4.06178 24.4009C4.23674 24.9559 4.75147 25.3334 5.33343 25.3334H26.6668C27.2487 25.3334 27.7635 24.9559 27.9384 24.4009C28.1112 23.8527 27.9131 23.2564 27.4489 22.9202L27.448 22.9195C27.4409 22.9139 27.4217 22.8986 27.392 22.8726C27.3327 22.8208 27.2314 22.7262 27.1012 22.5819C26.8414 22.2939 26.4627 21.8037 26.0722 21.0521C25.2941 19.5542 24.4445 16.9696 24.4445 12.8C24.4445 10.4866 23.5699 8.25552 21.9939 6.60077C20.7298 5.27348 19.0958 4.40382 17.3334 4.11031V2.66671ZM23.9148 22.6667C23.8451 22.5437 23.7753 22.4153 23.7058 22.2814C22.7061 20.357 21.7779 17.3416 21.7779 12.8C21.7779 11.153 21.1541 9.58564 20.0629 8.43985C18.9738 7.29627 17.5109 6.66671 16.0001 6.66671C14.4893 6.66671 13.0264 7.29627 11.9373 8.43985C10.8461 9.58564 10.2223 11.153 10.2223 12.8C10.2223 17.3416 9.29408 20.357 8.29442 22.2814C8.22485 22.4153 8.15506 22.5437 8.08535 22.6667H23.9148ZM12.5926 26.9581C13.2049 26.549 14.0329 26.7136 14.442 27.3259C14.575 27.525 14.7812 27.7121 15.0577 27.8503C15.3345 27.9887 15.6601 28.0667 16.0001 28.0667C16.3401 28.0667 16.6657 27.9887 16.9425 27.8503C17.219 27.7121 17.4252 27.525 17.5582 27.3259C17.9673 26.7136 18.7953 26.549 19.4076 26.9581C20.0198 27.3673 20.1845 28.1953 19.7754 28.8075C19.3664 29.4195 18.7946 29.9057 18.1351 30.2354C17.4759 30.565 16.7403 30.7334 16.0001 30.7334C15.2599 30.7334 14.5243 30.565 13.8651 30.2354C13.2056 29.9057 12.6338 29.4195 12.2248 28.8075C11.8157 28.1953 11.9804 27.3673 12.5926 26.9581Z" fill="#E6E6E6"/>
+<path d="M3.99984 13.4C3.26346 13.4 2.65914 12.8016 2.72771 12.0684C2.88779 10.357 3.36183 8.76192 4.14984 7.28329C4.92622 5.82649 5.91149 4.56487 7.10564 3.49845C7.65054 3.01184 8.48356 3.14375 8.91597 3.73257V3.73257C9.3488 4.32196 9.21456 5.1448 8.68174 5.64562C7.81466 6.46063 7.09292 7.41208 6.5165 8.49996C5.92856 9.6096 5.55971 10.7995 5.40995 12.0696C5.32372 12.8009 4.73622 13.4 3.99984 13.4V13.4ZM27.9998 13.4C27.2635 13.4 26.676 12.8009 26.5897 12.0696C26.44 10.7995 26.0711 9.6096 25.4832 8.49996C24.9068 7.41208 24.185 6.46063 23.3179 5.64562C22.7851 5.1448 22.6509 4.32196 23.0837 3.73257V3.73257C23.5161 3.14375 24.3491 3.01184 24.894 3.49845C26.0882 4.56487 27.0735 5.82649 27.8498 7.28329C28.6378 8.76192 29.1119 10.357 29.272 12.0684C29.3405 12.8016 28.7362 13.4 27.9998 13.4V13.4Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/bell-light.svg b/examples/quickcontrols/wearable/Wearable/icons/bell-light.svg
new file mode 100644
index 0000000000..b090fd6649
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/bell-light.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3333 2.66732C17.3333 1.93094 16.7364 1.33398 16 1.33398C15.2636 1.33398 14.6667 1.93094 14.6667 2.66732V4.11092C12.9043 4.40444 11.2703 5.2741 10.0062 6.60138C8.43022 8.25613 7.55556 10.4872 7.55556 12.8007C7.55556 16.9702 6.70602 19.5548 5.92791 21.0527C5.53744 21.8043 5.15873 22.2945 4.89895 22.5825C4.76871 22.7268 4.66739 22.8214 4.60811 22.8733C4.57845 22.8992 4.55924 22.9145 4.55214 22.9201L4.55121 22.9208C4.08696 23.257 3.88888 23.8533 4.06169 24.4015C4.23665 24.9565 4.75138 25.334 5.33334 25.334H26.6667C27.2486 25.334 27.7634 24.9565 27.9383 24.4015C28.1111 23.8533 27.9131 23.257 27.4488 22.9208L27.4479 22.9201C27.4408 22.9145 27.4216 22.8992 27.3919 22.8733C27.3326 22.8214 27.2313 22.7268 27.1011 22.5825C26.8413 22.2945 26.4626 21.8043 26.0721 21.0527C25.294 19.5548 24.4445 16.9702 24.4445 12.8007C24.4445 10.4872 23.5698 8.25613 21.9938 6.60138C20.7298 5.27409 19.0957 4.40443 17.3333 4.11092V2.66732ZM23.9148 22.6673C23.845 22.5443 23.7753 22.4159 23.7057 22.282C22.706 20.3576 21.7778 17.3422 21.7778 12.8007C21.7778 11.1536 21.154 9.58625 20.0628 8.44046C18.9737 7.29688 17.5108 6.66732 16 6.66732C14.4892 6.66732 13.0263 7.29688 11.9372 8.44046C10.846 9.58625 10.2222 11.1536 10.2222 12.8007C10.2222 17.3422 9.29399 20.3576 8.29432 22.282C8.22476 22.4159 8.15497 22.5443 8.08526 22.6673H23.9148ZM12.5925 26.9587C13.2048 26.5496 14.0328 26.7143 14.4419 27.3265C14.575 27.5256 14.7811 27.7127 15.0576 27.8509C15.3344 27.9893 15.66 28.0673 16 28.0673C16.34 28.0673 16.6656 27.9893 16.9424 27.8509C17.2189 27.7127 17.4251 27.5256 17.5581 27.3265C17.9672 26.7143 18.7952 26.5496 19.4075 26.9587C20.0197 27.3679 20.1844 28.1959 19.7753 28.8081C19.3663 29.4202 18.7945 29.9063 18.135 30.2361C17.4758 30.5657 16.7402 30.734 16 30.734C15.2598 30.734 14.5242 30.5657 13.865 30.2361C13.2055 29.9063 12.6337 29.4202 12.2247 28.8081C11.8156 28.1959 11.9803 27.3679 12.5925 26.9587Z" fill="#191919"/>
+<path d="M3.99999 13.3994C3.26361 13.3994 2.65929 12.801 2.72787 12.0678C2.88794 10.3563 3.36198 8.7613 4.14999 7.28268C4.92637 5.82588 5.91164 4.56426 7.1058 3.49784C7.65069 3.01123 8.48371 3.14314 8.91613 3.73196C9.34896 4.32135 9.21471 5.14419 8.68189 5.64501C7.81482 6.46002 7.09307 7.41147 6.51666 8.49935C5.92872 9.60899 5.55986 10.7989 5.4101 12.0689C5.32387 12.8003 4.73637 13.3994 3.99999 13.3994ZM28 13.3994C27.2636 13.3994 26.6761 12.8003 26.5899 12.0689C26.4401 10.7989 26.0713 9.60899 25.4833 8.49935C24.9069 7.41147 24.1852 6.46002 23.3181 5.64501C22.7853 5.14419 22.651 4.32135 23.0839 3.73196C23.5163 3.14314 24.3493 3.01123 24.8942 3.49784C26.0883 4.56426 27.0736 5.82588 27.85 7.28268C28.638 8.7613 29.112 10.3563 29.2721 12.0678C29.3407 12.801 28.7364 13.3994 28 13.3994Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/bluetooth-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/bluetooth-dark.svg
new file mode 100644
index 0000000000..b6227d7072
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/bluetooth-dark.svg
@@ -0,0 +1,3 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18.6445 3.74032C19.1541 3.52925 19.7406 3.64592 20.1306 4.03591L27.6306 11.5359C28.1631 12.0684 28.1631 12.9319 27.6306 13.4644L21.0948 20.0002L27.6306 26.5359C28.1631 27.0685 28.1631 27.9319 27.6306 28.4644L20.1306 35.9644C19.7406 36.3544 19.1541 36.4711 18.6445 36.26C18.135 36.0489 17.8027 35.5517 17.8027 35.0002V23.2923L12.6306 28.4644C12.0981 28.9969 11.2347 28.9969 10.7021 28.4644C10.1696 27.9319 10.1696 27.0685 10.7021 26.5359L17.2379 20.0002L10.7021 13.4644C10.1696 12.9319 10.1696 12.0684 10.7021 11.5359C11.2347 11.0034 12.0981 11.0034 12.6306 11.5359L17.8027 16.708V5.00015C17.8027 4.44861 18.135 3.95138 18.6445 3.74032ZM20.53 23.2923L24.7379 27.5002L20.53 31.708V23.2923ZM20.53 16.708V8.29226L24.7379 12.5002L20.53 16.708Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/bluetooth-light.svg b/examples/quickcontrols/wearable/Wearable/icons/bluetooth-light.svg
new file mode 100644
index 0000000000..3e07b06b50
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/bluetooth-light.svg
@@ -0,0 +1,3 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18.6449 3.74056C19.1545 3.52949 19.741 3.64616 20.131 4.03616L27.631 11.5362C28.1635 12.0687 28.1635 12.9321 27.631 13.4646L21.0952 20.0004L27.631 26.5362C28.1635 27.0687 28.1635 27.9321 27.631 28.4646L20.131 35.9646C19.741 36.3546 19.1545 36.4713 18.6449 36.2602C18.1353 36.0492 17.8031 35.5519 17.8031 35.0004V23.2925L12.631 28.4646C12.0984 28.9972 11.235 28.9972 10.7025 28.4646C10.17 27.9321 10.17 27.0687 10.7025 26.5362L17.2383 20.0004L10.7025 13.4646C10.17 12.9321 10.17 12.0687 10.7025 11.5362C11.235 11.0036 12.0984 11.0036 12.631 11.5362L17.8031 16.7083V5.0004C17.8031 4.44886 18.1353 3.95162 18.6449 3.74056ZM20.5304 23.2925L24.7383 27.5004L20.5304 31.7083V23.2925ZM20.5304 16.7083V8.29251L24.7383 12.5004L20.5304 16.7083Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/clock-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/clock-dark.svg
new file mode 100644
index 0000000000..6b9e71fe0f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/clock-dark.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66667 17.3334C6.66667 12.1787 10.8453 8.00004 16 8.00004C21.1547 8.00004 25.3333 12.1787 25.3333 17.3334C25.3333 22.488 21.1547 26.6667 16 26.6667C10.8453 26.6667 6.66667 22.488 6.66667 17.3334ZM16 5.33337C9.37258 5.33337 4 10.706 4 17.3334C4 23.9608 9.37258 29.3334 16 29.3334C22.6274 29.3334 28 23.9608 28 17.3334C28 10.706 22.6274 5.33337 16 5.33337ZM17.3333 12C17.3333 11.2637 16.7364 10.6667 16 10.6667C15.2636 10.6667 14.6667 11.2637 14.6667 12V18C14.6667 18.5051 14.952 18.9668 15.4037 19.1926L19.4037 21.1926C20.0624 21.5219 20.8633 21.255 21.1926 20.5963C21.5219 19.9377 21.2549 19.1368 20.5963 18.8075L17.3333 17.176V12Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.72353 8.39048C1.20283 8.91118 1.20283 9.7554 1.72353 10.2761C2.24423 10.7968 3.08845 10.7968 3.60915 10.2761L8.94248 4.94277C9.46318 4.42207 9.46318 3.57785 8.94248 3.05715C8.42178 2.53645 7.57756 2.53645 7.05686 3.05715L1.72353 8.39048ZM24.9425 3.05715C24.4218 2.53645 23.5776 2.53645 23.0569 3.05715C22.5362 3.57785 22.5362 4.42207 23.0569 4.94277L28.3902 10.2761C28.9109 10.7968 29.7551 10.7968 30.2758 10.2761C30.7965 9.7554 30.7965 8.91118 30.2758 8.39048L24.9425 3.05715Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/clock-light.svg b/examples/quickcontrols/wearable/Wearable/icons/clock-light.svg
new file mode 100644
index 0000000000..b0f195142c
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/clock-light.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66667 17.334C6.66667 12.1793 10.8453 8.00065 16 8.00065C21.1547 8.00065 25.3333 12.1793 25.3333 17.334C25.3333 22.4886 21.1547 26.6673 16 26.6673C10.8453 26.6673 6.66667 22.4886 6.66667 17.334ZM16 5.33398C9.37258 5.33398 4 10.7066 4 17.334C4 23.9614 9.37258 29.334 16 29.334C22.6274 29.334 28 23.9614 28 17.334C28 10.7066 22.6274 5.33398 16 5.33398ZM17.3333 12.0007C17.3333 11.2643 16.7364 10.6673 16 10.6673C15.2636 10.6673 14.6667 11.2643 14.6667 12.0007V18.0007C14.6667 18.5057 14.952 18.9674 15.4037 19.1932L19.4037 21.1932C20.0624 21.5225 20.8633 21.2556 21.1926 20.5969C21.5219 19.9383 21.2549 19.1374 20.5963 18.8081L17.3333 17.1766V12.0007Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.72384 8.38987C1.20314 8.91057 1.20314 9.75479 1.72384 10.2755C2.24454 10.7962 3.08876 10.7962 3.60946 10.2755L8.94279 4.94216C9.46349 4.42146 9.46349 3.57724 8.94279 3.05654C8.42209 2.53584 7.57787 2.53584 7.05717 3.05654L1.72384 8.38987ZM24.9428 3.05654C24.4221 2.53584 23.5779 2.53584 23.0572 3.05654C22.5365 3.57724 22.5365 4.42146 23.0572 4.94216L28.3905 10.2755C28.9112 10.7962 29.7554 10.7962 30.2761 10.2755C30.7968 9.75479 30.7968 8.91057 30.2761 8.38987L24.9428 3.05654Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/darkmode-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/darkmode-dark.svg
new file mode 100644
index 0000000000..b3e8da56ba
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/darkmode-dark.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9997 33.3334C27.3635 33.3334 33.333 27.3638 33.333 20C33.333 12.6362 27.3635 6.66671 19.9997 6.66671C12.6359 6.66671 6.66634 12.6362 6.66634 20C6.66634 27.3638 12.6359 33.3334 19.9997 33.3334ZM19.9997 36.6667C29.2044 36.6667 36.6663 29.2048 36.6663 20C36.6663 10.7953 29.2044 3.33337 19.9997 3.33337C10.7949 3.33337 3.33301 10.7953 3.33301 20C3.33301 29.2048 10.7949 36.6667 19.9997 36.6667Z" fill="#E6E6E6"/>
+<path d="M19.9997 3.33337C10.7949 3.33337 3.33301 10.7953 3.33301 20C3.33301 29.2048 10.7949 36.6667 19.9997 36.6667V3.33337Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/darkmode-light.svg b/examples/quickcontrols/wearable/Wearable/icons/darkmode-light.svg
new file mode 100644
index 0000000000..ca3ef4f4e6
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/darkmode-light.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9997 33.3333C27.3635 33.3333 33.333 27.3638 33.333 20C33.333 12.6362 27.3635 6.66665 19.9997 6.66665C12.6359 6.66665 6.66634 12.6362 6.66634 20C6.66634 27.3638 12.6359 33.3333 19.9997 33.3333ZM19.9997 36.6666C29.2044 36.6666 36.6663 29.2047 36.6663 20C36.6663 10.7952 29.2044 3.33331 19.9997 3.33331C10.7949 3.33331 3.33301 10.7952 3.33301 20C3.33301 29.2047 10.7949 36.6666 19.9997 36.6666Z" fill="#191919"/>
+<path d="M19.9997 3.33331C10.7949 3.33331 3.33301 10.7952 3.33301 20C3.33301 29.2047 10.7949 36.6666 19.9997 36.6666V3.33331Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/demomode-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/demomode-dark.svg
new file mode 100644
index 0000000000..9c95aa1f16
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/demomode-dark.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21.2804 23.9331C20.9637 23.5531 20.4947 23.3334 20 23.3334C19.5054 23.3334 19.0363 23.5531 18.7197 23.9331L10.3863 33.9331C9.97231 34.4299 9.88307 35.1213 10.1574 35.707C10.4317 36.2926 11.02 36.6667 11.6667 36.6667H28.3334C28.9801 36.6667 29.5684 36.2926 29.8427 35.707C30.117 35.1213 30.0277 34.4299 29.6137 33.9331L21.2804 23.9331Z" fill="#E6E6E6"/>
+<path d="M6.66634 8.33333H33.333V25H28.333V28.3333H33.333C35.174 28.3333 36.6663 26.841 36.6663 25V8.33333C36.6663 6.49238 35.174 5 33.333 5H6.66634C4.82539 5 3.33301 6.49238 3.33301 8.33333V25C3.33301 26.841 4.82539 28.3333 6.66634 28.3333H11.6663V25H6.66634V8.33333Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/demomode-light.svg b/examples/quickcontrols/wearable/Wearable/icons/demomode-light.svg
new file mode 100644
index 0000000000..eae661be65
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/demomode-light.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21.2804 23.933C20.9637 23.553 20.4947 23.3333 20 23.3333C19.5054 23.3333 19.0363 23.553 18.7197 23.933L10.3863 33.933C9.97231 34.4298 9.88307 35.1213 10.1574 35.7069C10.4317 36.2925 11.02 36.6666 11.6667 36.6666H28.3334C28.9801 36.6666 29.5684 36.2925 29.8427 35.7069C30.117 35.1213 30.0277 34.4298 29.6137 33.933L21.2804 23.933Z" fill="#191919"/>
+<path d="M6.66634 8.33333H33.333V25H28.333V28.3333H33.333C35.174 28.3333 36.6663 26.841 36.6663 25V8.33333C36.6663 6.49238 35.174 5 33.333 5H6.66634C4.82539 5 3.33301 6.49238 3.33301 8.33333V25C3.33301 26.841 4.82539 28.3333 6.66634 28.3333H11.6663V25H6.66634V8.33333Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/drop-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/drop-dark.svg
new file mode 100644
index 0000000000..0672ea10d4
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/drop-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 22C9.78333 22 7.89583 21.2125 6.3375 19.6375C4.77917 18.0626 4 16.1598 4 13.9293C4 12.8056 4.20833 11.767 4.625 10.8135C5.04167 9.85997 5.61667 9.00863 6.35 8.25946L11.2992 3.2928C11.6883 2.9024 12.3117 2.9024 12.7008 3.2928L17.65 8.25946C18.3833 9.00863 18.9583 9.85997 19.375 10.8135C19.7917 11.767 20 12.8056 20 13.9293C20 16.1598 19.2208 18.0626 17.6625 19.6375C16.1042 21.2125 14.2167 22 12 22ZM12 20C13.6667 20 15.2086 19.5829 16.5 18.1818C17.7097 16.8692 17.9963 15.63 18 13.8272C18.0018 12.9638 17.9569 12.4528 17.6625 11.6435C17.3638 10.8225 16.5 9.74078 16.5 9.74078L12 5.45005L7.75 9.74078C7.16667 10.3027 6.72917 10.9369 6.4375 11.6435C6.14583 12.3501 6 13.1121 6 13.9293C6 15.5809 6.58333 16.9984 7.75 18.1818C8.91667 19.3651 10.3333 20 12 20Z" fill="#E6E6E6"/>
+<path d="M12.7846 18.7537C12.641 18.8951 12.4678 18.9741 12.265 18.9907C11.1837 19.0573 10.1109 18.7661 9.04646 18.1173C7.98205 17.4685 7.30623 16.3872 7.01901 14.8734C6.96832 14.6405 7.01901 14.4367 7.17107 14.262C7.32313 14.0873 7.51742 14 7.75396 14C7.93981 14 8.10454 14.0582 8.24815 14.1747C8.39176 14.2911 8.48046 14.4408 8.51426 14.6238C8.717 15.7384 9.20697 16.5078 9.98416 16.932C10.7614 17.3563 11.4963 17.5434 12.189 17.4935C12.4256 17.4769 12.6199 17.5392 12.7719 17.6807C12.924 17.8221 13 18.0092 13 18.2421C13 18.4417 12.9282 18.6123 12.7846 18.7537Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/drop-light.svg b/examples/quickcontrols/wearable/Wearable/icons/drop-light.svg
new file mode 100644
index 0000000000..c045ddfed8
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/drop-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 22C9.78333 22 7.89583 21.2125 6.3375 19.6375C4.77917 18.0626 4 16.1598 4 13.9293C4 12.8056 4.20833 11.767 4.625 10.8135C5.04167 9.85997 5.61667 9.00863 6.35 8.25946L11.2992 3.2928C11.6883 2.9024 12.3117 2.9024 12.7008 3.2928L17.65 8.25946C18.3833 9.00863 18.9583 9.85997 19.375 10.8135C19.7917 11.767 20 12.8056 20 13.9293C20 16.1598 19.2208 18.0626 17.6625 19.6375C16.1042 21.2125 14.2167 22 12 22ZM12 20C13.6667 20 15.2086 19.5829 16.5 18.1818C17.7097 16.8692 17.9963 15.63 18 13.8272C18.0018 12.9638 17.9569 12.4528 17.6625 11.6435C17.3638 10.8225 16.5 9.74078 16.5 9.74078L12 5.45005L7.75 9.74078C7.16667 10.3027 6.72917 10.9369 6.4375 11.6435C6.14583 12.3501 6 13.1121 6 13.9293C6 15.5809 6.58333 16.9984 7.75 18.1818C8.91667 19.3651 10.3333 20 12 20Z" fill="#191919"/>
+<path d="M12.7846 18.7537C12.641 18.8951 12.4678 18.9741 12.265 18.9907C11.1837 19.0573 10.1109 18.7661 9.04646 18.1173C7.98205 17.4685 7.30623 16.3872 7.01901 14.8734C6.96832 14.6405 7.01901 14.4367 7.17107 14.262C7.32313 14.0873 7.51742 14 7.75396 14C7.93981 14 8.10454 14.0582 8.24815 14.1747C8.39176 14.2911 8.48046 14.4408 8.51426 14.6238C8.717 15.7384 9.20697 16.5078 9.98416 16.932C10.7614 17.3563 11.4963 17.5434 12.189 17.4935C12.4256 17.4769 12.6199 17.5392 12.7719 17.6807C12.924 17.8221 13 18.0092 13 18.2421C13 18.4417 12.9282 18.6123 12.7846 18.7537Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/forward-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/forward-dark.svg
new file mode 100644
index 0000000000..f53165f667
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/forward-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 5C11.4477 5 11 5.55964 11 6.25V18.75C11 19.4404 11.4477 20 12 20C12.5523 20 13 19.4404 13 18.75L12.9999 6.25C13 5.625 12.5527 5 12 5Z" fill="#2CDE85"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.82428 10.7372C6.2314 11.1103 6.86397 11.0828 7.23716 10.6757L12 5.47989L16.7629 10.6757C17.136 11.0828 17.7686 11.1103 18.1757 10.7372C18.5828 10.364 18.6104 9.7314 18.2372 9.32428L12.7372 3.32428C12.5477 3.11765 12.2803 3 12 3C11.7197 3 11.4523 3.11765 11.2629 3.32428L5.76285 9.32428C5.38966 9.73139 5.41716 10.364 5.82428 10.7372Z" fill="#2CDE85"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/forward-light.svg b/examples/quickcontrols/wearable/Wearable/icons/forward-light.svg
new file mode 100644
index 0000000000..3c872b6320
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/forward-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 5C11.4477 5 11 5.55964 11 6.25V18.75C11 19.4404 11.4477 20 12 20C12.5523 20 13 19.4404 13 18.75L12.9999 6.25C13 5.625 12.5527 5 12 5Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.82428 10.7372C6.2314 11.1103 6.86397 11.0828 7.23716 10.6757L12 5.47989L16.7629 10.6757C17.136 11.0828 17.7686 11.1103 18.1757 10.7372C18.5828 10.364 18.6104 9.7314 18.2372 9.32428L12.7372 3.32428C12.5477 3.11765 12.2803 3 12 3C11.7197 3 11.4523 3.11765 11.2629 3.32428L5.76285 9.32428C5.38966 9.73139 5.41716 10.364 5.82428 10.7372Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/hearth-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/hearth-dark.svg
new file mode 100644
index 0000000000..1a2ff53afc
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/hearth-dark.svg
@@ -0,0 +1,3 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16.6602 27.4192C16.2826 27.7513 15.7171 27.7513 15.3394 27.4192L14.0665 26.2997C11.8221 24.3161 9.9665 22.6049 8.49984 21.1662C7.03317 19.7275 5.8665 18.436 4.99984 17.2916C4.13317 16.1471 3.52762 15.0954 3.18317 14.1362C2.83873 13.1771 2.6665 12.1962 2.6665 11.1935C2.6665 9.14441 3.3665 7.43324 4.7665 6.05995C6.1665 4.68665 7.91095 4 9.99984 4C11.1554 4 12.2554 4.23978 13.2998 4.71935C14.3443 5.19891 15.2443 5.87466 15.9998 6.74659C16.7554 5.87466 17.6554 5.19891 18.6998 4.71935C19.7443 4.23978 20.8443 4 21.9998 4C24.0887 4 25.8332 4.68665 27.2332 6.05995C28.6332 7.43324 29.3332 9.14441 29.3332 11.1935C29.3332 12.1962 29.161 13.1771 28.8165 14.1362C28.4721 15.0954 27.8665 16.1471 26.9998 17.2916C26.1332 18.436 24.9665 19.7275 23.4998 21.1662C22.0332 22.6049 20.1776 24.3161 17.9332 26.2997L16.6602 27.4192ZM15.9998 24.4687C18.1332 22.594 19.8887 20.9864 21.2665 19.6458C22.6443 18.3052 23.7332 17.139 24.5332 16.1471C25.3332 15.1553 25.8887 14.2725 26.1998 13.4986C26.5109 12.7248 26.6665 11.9564 26.6665 11.1935C26.6665 9.88556 26.2221 8.79564 25.3332 7.92371C24.4443 7.05177 23.3332 6.6158 21.9998 6.6158C20.9554 6.6158 19.9887 6.90463 19.0998 7.48229C18.608 7.80193 18.2012 8.16996 17.8794 8.58638C17.4137 9.18908 16.7615 9.68937 15.9998 9.68937V9.68937C15.2382 9.68937 14.5859 9.18908 14.1203 8.58638C13.7985 8.16996 13.3917 7.80193 12.8998 7.48229C12.0109 6.90463 11.0443 6.6158 9.99984 6.6158C8.6665 6.6158 7.55539 7.05177 6.6665 7.92371C5.77762 8.79564 5.33317 9.88556 5.33317 11.1935C5.33317 11.9564 5.48873 12.7248 5.79984 13.4986C6.11095 14.2725 6.6665 15.1553 7.4665 16.1471C8.2665 17.139 9.35539 18.3052 10.7332 19.6458C12.1109 20.9864 13.8665 22.594 15.9998 24.4687Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/hearth-light.svg b/examples/quickcontrols/wearable/Wearable/icons/hearth-light.svg
new file mode 100644
index 0000000000..155329518f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/hearth-light.svg
@@ -0,0 +1,3 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16.6604 27.4192C16.2828 27.7513 15.7172 27.7513 15.3396 27.4192L14.0667 26.2997C11.8222 24.3161 9.96666 22.6049 8.49999 21.1662C7.03332 19.7275 5.86666 18.436 4.99999 17.2916C4.13332 16.1471 3.52777 15.0954 3.18332 14.1362C2.83888 13.1771 2.66666 12.1962 2.66666 11.1935C2.66666 9.14441 3.36666 7.43324 4.76666 6.05995C6.16666 4.68665 7.9111 4 9.99999 4C11.1555 4 12.2555 4.23978 13.3 4.71935C14.3444 5.19891 15.2444 5.87466 16 6.74659C16.7555 5.87466 17.6555 5.19891 18.7 4.71935C19.7444 4.23978 20.8444 4 22 4C24.0889 4 25.8333 4.68665 27.2333 6.05995C28.6333 7.43324 29.3333 9.14441 29.3333 11.1935C29.3333 12.1962 29.1611 13.1771 28.8167 14.1362C28.4722 15.0954 27.8667 16.1471 27 17.2916C26.1333 18.436 24.9667 19.7275 23.5 21.1662C22.0333 22.6049 20.1778 24.3161 17.9333 26.2997L16.6604 27.4192ZM16 24.4687C18.1333 22.594 19.8889 20.9864 21.2667 19.6458C22.6444 18.3052 23.7333 17.139 24.5333 16.1471C25.3333 15.1553 25.8889 14.2725 26.2 13.4986C26.5111 12.7248 26.6667 11.9564 26.6667 11.1935C26.6667 9.88556 26.2222 8.79564 25.3333 7.92371C24.4444 7.05177 23.3333 6.6158 22 6.6158C20.9555 6.6158 19.9889 6.90463 19.1 7.48229C18.6081 7.80193 18.2013 8.16996 17.8796 8.58638C17.4139 9.18908 16.7616 9.68937 16 9.68937C15.2383 9.68937 14.5861 9.18908 14.1204 8.58638C13.7987 8.16996 13.3918 7.80193 12.9 7.48229C12.0111 6.90463 11.0444 6.6158 9.99999 6.6158C8.66666 6.6158 7.55555 7.05177 6.66666 7.92371C5.77777 8.79564 5.33332 9.88556 5.33332 11.1935C5.33332 11.9564 5.48888 12.7248 5.79999 13.4986C6.1111 14.2725 6.66666 15.1553 7.46666 16.1471C8.26666 17.139 9.35555 18.3052 10.7333 19.6458C12.1111 20.9864 13.8667 22.594 16 24.4687Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/left-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/left-dark.svg
new file mode 100644
index 0000000000..78abe43ad0
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/left-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6 9.5C6 8.94772 6.44772 8.5 7 8.5H19C19.5523 8.5 20 8.94772 20 9.5V19.5C20 20.0523 19.5523 20.5 19 20.5C18.4477 20.5 18 20.0523 18 19.5V10.5003L7 10.5C6.44772 10.5 6 10.0523 6 9.5Z" fill="#2CDE85"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.7372 3.32428C13.1103 3.7314 13.0828 4.36397 12.6757 4.73716L7.47989 9.50001L12.6757 14.2629C13.0828 14.636 13.1103 15.2686 12.7372 15.6757C12.364 16.0828 11.7314 16.1104 11.3243 15.7372L5.32428 10.2372C5.11765 10.0477 5 9.78031 5 9.50001C5 9.2197 5.11765 8.95226 5.32428 8.76285L11.3243 3.26285C11.7314 2.88966 12.364 2.91716 12.7372 3.32428Z" fill="#2CDE85"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/left-light.svg b/examples/quickcontrols/wearable/Wearable/icons/left-light.svg
new file mode 100644
index 0000000000..636a1ee0a5
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/left-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6 9.5C6 8.94772 6.44772 8.5 7 8.5H19C19.5523 8.5 20 8.94772 20 9.5V19.5C20 20.0523 19.5523 20.5 19 20.5C18.4477 20.5 18 20.0523 18 19.5V10.5003L7 10.5C6.44772 10.5 6 10.0523 6 9.5Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.7372 3.32428C13.1103 3.7314 13.0828 4.36397 12.6757 4.73716L7.47989 9.50001L12.6757 14.2629C13.0828 14.636 13.1103 15.2686 12.7372 15.6757C12.364 16.0828 11.7314 16.1104 11.3243 15.7372L5.32428 10.2372C5.11765 10.0477 5 9.78031 5 9.50001C5 9.2197 5.11765 8.95226 5.32428 8.76285L11.3243 3.26285C11.7314 2.88966 12.364 2.91716 12.7372 3.32428Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/lightleft-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/lightleft-dark.svg
new file mode 100644
index 0000000000..2fe8216d9f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/lightleft-dark.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.0005 19.9999C12.0005 20.5522 12.4482 20.9999 13.0005 20.9999C13.5528 20.9999 14.0005 20.5522 14.0005 19.9999H12.0005ZM7.17889 3.01613C6.63551 2.91733 6.11493 3.27774 6.01613 3.82111L4.40616 12.6759C4.30737 13.2193 4.66777 13.7399 5.21115 13.8387C5.75452 13.9375 6.27511 13.5771 6.3739 13.0337L7.80498 5.16276L15.6759 6.59384C16.2193 6.69263 16.7399 6.33223 16.8387 5.78885C16.9375 5.24548 16.5771 4.72489 16.0337 4.6261L7.17889 3.01613ZM14.0005 19.9999C14.0005 15.9397 13.6211 13.4098 12.6069 11.0435C11.6181 8.73658 10.0317 6.62227 7.82219 3.43079L6.17781 4.56921C8.46832 7.87773 9.88218 9.76337 10.7686 11.8314C11.6296 13.8401 12.0005 16.0601 12.0005 19.9999H14.0005Z" fill="#2CDE85"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/lightleft-light.svg b/examples/quickcontrols/wearable/Wearable/icons/lightleft-light.svg
new file mode 100644
index 0000000000..aea6ac6cad
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/lightleft-light.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.0005 19.9999C12.0005 20.5522 12.4482 20.9999 13.0005 20.9999C13.5528 20.9999 14.0005 20.5522 14.0005 19.9999H12.0005ZM7.17889 3.01613C6.63551 2.91733 6.11493 3.27774 6.01613 3.82111L4.40616 12.6759C4.30737 13.2193 4.66777 13.7399 5.21115 13.8387C5.75452 13.9375 6.27511 13.5771 6.3739 13.0337L7.80498 5.16276L15.6759 6.59384C16.2193 6.69263 16.7399 6.33223 16.8387 5.78885C16.9375 5.24548 16.5771 4.72489 16.0337 4.6261L7.17889 3.01613ZM14.0005 19.9999C14.0005 15.9397 13.6211 13.4098 12.6069 11.0435C11.6181 8.73658 10.0317 6.62227 7.82219 3.43079L6.17781 4.56921C8.46832 7.87773 9.88218 9.76337 10.7686 11.8314C11.6296 13.8401 12.0005 16.0601 12.0005 19.9999H14.0005Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/lightright-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/lightright-dark.svg
new file mode 100644
index 0000000000..89b5db0394
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/lightright-dark.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.9995 19.9999C11.9995 20.5522 11.5518 20.9999 10.9995 20.9999C10.4472 20.9999 9.99953 20.5522 9.99953 19.9999H11.9995ZM16.8211 3.01613C17.3645 2.91733 17.8851 3.27774 17.9839 3.82111L19.5938 12.6759C19.6926 13.2193 19.3322 13.7399 18.7889 13.8387C18.2455 13.9375 17.7249 13.5771 17.6261 13.0337L16.195 5.16276L8.32406 6.59384C7.78068 6.69263 7.2601 6.33223 7.1613 5.78885C7.0625 5.24548 7.42291 4.72489 7.96629 4.6261L16.8211 3.01613ZM9.99953 19.9999C9.99953 15.9397 10.3789 13.4098 11.3931 11.0435C12.3819 8.73658 13.9683 6.62227 16.1778 3.43079L17.8222 4.56921C15.5317 7.87773 14.1178 9.76337 13.2314 11.8314C12.3704 13.8401 11.9995 16.0601 11.9995 19.9999H9.99953Z" fill="#2CDE85"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/lightright-light.svg b/examples/quickcontrols/wearable/Wearable/icons/lightright-light.svg
new file mode 100644
index 0000000000..58f24020da
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/lightright-light.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.9995 19.9999C11.9995 20.5522 11.5518 20.9999 10.9995 20.9999C10.4472 20.9999 9.99953 20.5522 9.99953 19.9999H11.9995ZM16.8211 3.01613C17.3645 2.91733 17.8851 3.27774 17.9839 3.82111L19.5938 12.6759C19.6926 13.2193 19.3322 13.7399 18.7889 13.8387C18.2455 13.9375 17.7249 13.5771 17.6261 13.0337L16.195 5.16276L8.32406 6.59384C7.78068 6.69263 7.2601 6.33223 7.1613 5.78885C7.0625 5.24548 7.42291 4.72489 7.96629 4.6261L16.8211 3.01613ZM9.99953 19.9999C9.99953 15.9397 10.3789 13.4098 11.3931 11.0435C12.3819 8.73658 13.9683 6.62227 16.1778 3.43079L17.8222 4.56921C15.5317 7.87773 14.1178 9.76337 13.2314 11.8314C12.3704 13.8401 11.9995 16.0601 11.9995 19.9999H9.99953Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/maps-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/maps-dark.svg
new file mode 100644
index 0000000000..7c8339606c
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/maps-dark.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.3772 22.8242C21.9333 20.0647 24.0002 16.7686 24.0002 13.5757C24.0002 11.3706 23.1431 9.26753 21.6359 7.72614C20.1308 6.18679 18.1023 5.33329 16.0002 5.33329C13.898 5.33329 11.8695 6.18679 10.3644 7.72614C8.85724 9.26753 8.00016 11.3706 8.00016 13.5757C8.00016 16.7686 10.067 20.0647 12.6231 22.8242C13.8427 24.1407 15.0707 25.2246 16.0002 25.9826C16.9296 25.2246 18.1577 24.1407 19.3772 22.8242ZM16.0002 29.3333C16.0002 29.3333 26.6668 22.0606 26.6668 13.5757C26.6668 10.6824 25.543 7.90768 23.5426 5.86182C21.5422 3.81597 18.8291 2.66663 16.0002 2.66663C13.1712 2.66663 10.4581 3.81597 8.45769 5.86182C6.4573 7.90768 5.3335 10.6824 5.3335 13.5757C5.3335 22.0606 16.0002 29.3333 16.0002 29.3333Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 12C15.2636 12 14.6667 12.597 14.6667 13.3334C14.6667 14.0698 15.2636 14.6667 16 14.6667C16.7364 14.6667 17.3333 14.0698 17.3333 13.3334C17.3333 12.597 16.7364 12 16 12ZM12 13.3334C12 11.1242 13.7909 9.33337 16 9.33337C18.2091 9.33337 20 11.1242 20 13.3334C20 15.5425 18.2091 17.3334 16 17.3334C13.7909 17.3334 12 15.5425 12 13.3334Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/maps-light.svg b/examples/quickcontrols/wearable/Wearable/icons/maps-light.svg
new file mode 100644
index 0000000000..07fb6e7b87
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/maps-light.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.377 22.8235C21.9332 20.0641 24 16.768 24 13.5751C24 11.37 23.1429 9.26692 21.6358 7.72553C20.1307 6.18618 18.1022 5.33268 16 5.33268C13.8979 5.33268 11.8694 6.18618 10.3642 7.72553C8.85709 9.26692 8.00001 11.37 8.00001 13.5751C8.00001 16.768 10.0669 20.0641 12.623 22.8235C13.8425 24.1401 15.0705 25.224 16 25.9819C16.9295 25.224 18.1575 24.1401 19.377 22.8235ZM16 29.3327C16 29.3327 26.6667 22.06 26.6667 13.5751C26.6667 10.6818 25.5429 7.90707 23.5425 5.86121C21.5421 3.81536 18.829 2.66602 16 2.66602C13.171 2.66602 10.4579 3.81536 8.45754 5.86121C6.45715 7.90707 5.33334 10.6818 5.33334 13.5751C5.33334 22.06 16 29.3327 16 29.3327Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 12.0007C15.2636 12.0007 14.6667 12.5976 14.6667 13.334C14.6667 14.0704 15.2636 14.6673 16 14.6673C16.7364 14.6673 17.3333 14.0704 17.3333 13.334C17.3333 12.5976 16.7364 12.0007 16 12.0007ZM12 13.334C12 11.1248 13.7909 9.33398 16 9.33398C18.2091 9.33398 20 11.1248 20 13.334C20 15.5431 18.2091 17.334 16 17.334C13.7909 17.334 12 15.5431 12 13.334Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/notification-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/notification-dark.svg
new file mode 100644
index 0000000000..07aa882140
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/notification-dark.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0003 5.33337C21.8914 5.33337 26.667 10.109 26.667 16C26.667 21.8911 21.8914 26.6667 16.0003 26.6667C10.1093 26.6667 5.33366 21.8911 5.33366 16C5.33366 10.109 10.1093 5.33337 16.0003 5.33337ZM16.0003 2.66671C23.3641 2.66671 29.3337 8.63624 29.3337 16C29.3337 23.3638 23.3641 29.3334 16.0003 29.3334C8.63653 29.3334 2.66699 23.3638 2.66699 16C2.66699 8.63624 8.63653 2.66671 16.0003 2.66671Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0003 17.3334C15.2639 17.3334 14.667 16.7364 14.667 16V10.6667C14.667 9.93033 15.2639 9.33337 16.0003 9.33337C16.7367 9.33337 17.3337 9.93033 17.3337 10.6667V16C17.3337 16.7364 16.7367 17.3334 16.0003 17.3334ZM16.0003 20C16.7367 20 17.3337 20.597 17.3337 21.3334C17.3337 22.0698 16.7367 22.6667 16.0003 22.6667C15.2639 22.6667 14.667 22.0698 14.667 21.3334C14.667 20.597 15.2639 20 16.0003 20Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/notification-light.svg b/examples/quickcontrols/wearable/Wearable/icons/notification-light.svg
new file mode 100644
index 0000000000..b4ac2bdb4e
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/notification-light.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0003 5.33331C21.8914 5.33331 26.667 10.1089 26.667 16C26.667 21.891 21.8914 26.6666 16.0003 26.6666C10.1093 26.6666 5.33366 21.891 5.33366 16C5.33366 10.1089 10.1093 5.33331 16.0003 5.33331ZM16.0003 2.66665C23.3641 2.66665 29.3337 8.63618 29.3337 16C29.3337 23.3638 23.3641 29.3333 16.0003 29.3333C8.63653 29.3333 2.66699 23.3638 2.66699 16C2.66699 8.63618 8.63653 2.66665 16.0003 2.66665Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0003 17.3333C15.2639 17.3333 14.667 16.7364 14.667 16V10.6666C14.667 9.93027 15.2639 9.33331 16.0003 9.33331C16.7367 9.33331 17.3337 9.93027 17.3337 10.6666V16C17.3337 16.7364 16.7367 17.3333 16.0003 17.3333ZM16.0003 20C16.7367 20 17.3337 20.5969 17.3337 21.3333C17.3337 22.0697 16.7367 22.6666 16.0003 22.6666C15.2639 22.6666 14.667 22.0697 14.667 21.3333C14.667 20.5969 15.2639 20 16.0003 20Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/plus-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/plus-dark.svg
new file mode 100644
index 0000000000..e4ef49ca4f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/plus-dark.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10.667 16C10.667 15.2636 11.2639 14.6666 12.0003 14.6666H20.0003C20.7367 14.6666 21.3337 15.2636 21.3337 16C21.3337 16.7363 20.7367 17.3333 20.0003 17.3333H12.0003C11.2639 17.3333 10.667 16.7363 10.667 16Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0003 21.3334C15.2639 21.3334 14.667 20.7364 14.667 20L14.667 12C14.667 11.2637 15.2639 10.6667 16.0003 10.6667C16.7367 10.6667 17.3337 11.2637 17.3337 12L17.3337 20C17.3337 20.7364 16.7367 21.3334 16.0003 21.3334Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/plus-light.svg b/examples/quickcontrols/wearable/Wearable/icons/plus-light.svg
new file mode 100644
index 0000000000..f1adca5444
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/plus-light.svg
@@ -0,0 +1,5 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10.667 16C10.667 15.2636 11.2639 14.6667 12.0003 14.6667H20.0003C20.7367 14.6667 21.3337 15.2636 21.3337 16C21.3337 16.7364 20.7367 17.3333 20.0003 17.3333H12.0003C11.2639 17.3333 10.667 16.7364 10.667 16Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0003 21.3333C15.2639 21.3333 14.667 20.7364 14.667 20L14.667 12C14.667 11.2636 15.2639 10.6667 16.0003 10.6667C16.7367 10.6667 17.3337 11.2636 17.3337 12L17.3337 20C17.3337 20.7364 16.7367 21.3333 16.0003 21.3333Z" fill="#191919"/>
+</svg>
+
diff --git a/examples/quickcontrols/wearable/Wearable/icons/pressure-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/pressure-dark.svg
new file mode 100644
index 0000000000..270a8411a8
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/pressure-dark.svg
@@ -0,0 +1,9 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12Z" fill="#E6E6E6"/>
+<path d="M12 3V4.5" stroke="#E6E6E6" stroke-width="2" stroke-linecap="round"/>
+<path d="M4.5 13C5.05228 13 5.5 12.5523 5.5 12C5.5 11.4477 5.05228 11 4.5 11V13ZM3 13H4.5V11H3V13Z" fill="#E6E6E6"/>
+<path d="M13 19.5C13 18.9477 12.5523 18.5 12 18.5C11.4477 18.5 11 18.9477 11 19.5H13ZM13 21V19.5H11V21H13Z" fill="#E6E6E6"/>
+<path d="M19.5 11C18.9477 11 18.5 11.4477 18.5 12C18.5 12.5523 18.9477 13 19.5 13V11ZM21 11H19.5V13H21V11Z" fill="#E6E6E6"/>
+<circle cx="12" cy="12" r="2" fill="#E6E6E6"/>
+<path d="M16 12.75C16.4142 12.75 16.75 12.4142 16.75 12C16.75 11.5858 16.4142 11.25 16 11.25V12.75ZM12 12.75L16 12.75V11.25L12 11.25V12.75Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/pressure-light.svg b/examples/quickcontrols/wearable/Wearable/icons/pressure-light.svg
new file mode 100644
index 0000000000..b9a5773724
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/pressure-light.svg
@@ -0,0 +1,9 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12Z" fill="#0D0D0D"/>
+<path d="M12 3V4.5" stroke="#191919" stroke-width="2" stroke-linecap="round"/>
+<path d="M4.5 13C5.05228 13 5.5 12.5523 5.5 12C5.5 11.4477 5.05228 11 4.5 11V13ZM3 13H4.5V11H3V13Z" fill="#191919"/>
+<path d="M13 19.5C13 18.9477 12.5523 18.5 12 18.5C11.4477 18.5 11 18.9477 11 19.5H13ZM13 21V19.5H11V21H13Z" fill="#191919"/>
+<path d="M19.5 11C18.9477 11 18.5 11.4477 18.5 12C18.5 12.5523 18.9477 13 19.5 13V11ZM21 11H19.5V13H21V11Z" fill="#191919"/>
+<circle cx="12" cy="12" r="2" fill="#191919"/>
+<path d="M16 12.75C16.4142 12.75 16.75 12.4142 16.75 12C16.75 11.5858 16.4142 11.25 16 11.25V12.75ZM12 12.75L16 12.75V11.25L12 11.25V12.75Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/right-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/right-dark.svg
new file mode 100644
index 0000000000..842b3ea034
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/right-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18 9.5C18 8.94772 17.5523 8.5 17 8.5H5C4.44771 8.5 4 8.94772 4 9.5V19.5C4 20.0523 4.44771 20.5 5 20.5C5.55229 20.5 6 20.0523 6 19.5V10.5003L17 10.5C17.5523 10.5 18 10.0523 18 9.5Z" fill="#2CDE85"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2628 3.32428C10.8897 3.7314 10.9172 4.36397 11.3243 4.73716L16.5201 9.50001L11.3243 14.2629C10.9172 14.636 10.8897 15.2686 11.2628 15.6757C11.636 16.0828 12.2686 16.1104 12.6757 15.7372L18.6757 10.2372C18.8824 10.0477 19 9.78031 19 9.50001C19 9.2197 18.8824 8.95226 18.6757 8.76285L12.6757 3.26285C12.2686 2.88966 11.636 2.91716 11.2628 3.32428Z" fill="#2CDE85"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/right-light.svg b/examples/quickcontrols/wearable/Wearable/icons/right-light.svg
new file mode 100644
index 0000000000..bbde2d6031
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/right-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18 9.5C18 8.94772 17.5523 8.5 17 8.5H5C4.44771 8.5 4 8.94772 4 9.5V19.5C4 20.0523 4.44771 20.5 5 20.5C5.55229 20.5 6 20.0523 6 19.5V10.5003L17 10.5C17.5523 10.5 18 10.0523 18 9.5Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2628 3.32428C10.8897 3.7314 10.9172 4.36397 11.3243 4.73716L16.5201 9.50001L11.3243 14.2629C10.9172 14.636 10.8897 15.2686 11.2628 15.6757C11.636 16.0828 12.2686 16.1104 12.6757 15.7372L18.6757 10.2372C18.8824 10.0477 19 9.78031 19 9.50001C19 9.2197 18.8824 8.95226 18.6757 8.76285L12.6757 3.26285C12.2686 2.88966 11.636 2.91716 11.2628 3.32428Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/settings-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/settings-dark.svg
new file mode 100644
index 0000000000..ee707b771e
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/settings-dark.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7996 5.33329C11.7996 3.86053 12.9936 2.66663 14.4663 2.66663H17.496C18.9687 2.66663 20.1626 3.86053 20.1626 5.33329V6.12674C20.1635 6.12972 20.1663 6.13636 20.1739 6.14704C20.1909 6.17113 20.2251 6.20318 20.2767 6.22576C21.1357 6.60213 21.9351 7.08808 22.6572 7.66557C22.7011 7.70069 22.7455 7.71596 22.775 7.71972C22.788 7.72138 22.7951 7.72069 22.7982 7.72007L23.9147 7.11651C25.1584 6.44423 26.7106 6.85936 27.4526 8.06271L28.8989 10.408C29.7032 11.7124 29.2452 13.4248 27.8971 14.1535L26.6497 14.8278C26.6475 14.8301 26.6429 14.8358 26.6371 14.8477C26.624 14.8746 26.6124 14.9206 26.6178 14.9769C26.6499 15.3139 26.6663 15.6552 26.6663 16C26.6663 16.3447 26.6499 16.686 26.6178 17.0231C26.6124 17.0794 26.624 17.1253 26.6371 17.1522C26.6429 17.1642 26.6475 17.1699 26.6497 17.1721L27.897 17.8464C29.2452 18.5751 29.7031 20.2875 28.8988 21.5919L27.4526 23.9372C26.7105 25.1406 25.1583 25.5557 23.9147 24.8834L22.7982 24.2799C22.7951 24.2793 22.7879 24.2786 22.7749 24.2802C22.7455 24.284 22.7011 24.2993 22.6571 24.3344C21.935 24.9119 21.1357 25.3978 20.2767 25.7742C20.2251 25.7967 20.1909 25.8288 20.1739 25.8529C20.1663 25.8636 20.1635 25.8702 20.1626 25.8732V26.6666C20.1626 28.1394 18.9687 29.3333 17.496 29.3333H14.4663C12.9935 29.3333 11.7996 28.1394 11.7996 26.6666V25.8571C11.7988 25.8541 11.796 25.8475 11.7885 25.8369C11.7716 25.8129 11.7377 25.7809 11.6864 25.7583C10.8353 25.3816 10.0433 24.8972 9.32749 24.3227C9.28356 24.2874 9.23902 24.2721 9.2095 24.2683C9.19646 24.2666 9.18928 24.2673 9.18621 24.2679L8.04757 24.8834C6.80391 25.5557 5.25172 25.1405 4.50968 23.9372L3.06347 21.5919C2.2591 20.2875 2.71708 18.5751 4.0652 17.8464L5.34766 17.1531C5.34987 17.1509 5.35443 17.1452 5.36024 17.1333C5.37331 17.1065 5.38497 17.0607 5.37971 17.0045C5.34875 16.6735 5.33294 16.3384 5.33294 16C5.33294 15.6616 5.34874 15.3265 5.37971 14.9955C5.38497 14.9392 5.37331 14.8935 5.36023 14.8666C5.35443 14.8547 5.34987 14.8491 5.34765 14.8468L4.06513 14.1536C2.71701 13.4248 2.25903 11.7124 3.06339 10.408L4.50961 8.06272C5.25165 6.85937 6.80383 6.44425 8.0475 7.11652L9.18618 7.73205C9.18925 7.73267 9.19644 7.73336 9.20947 7.73168C9.23899 7.72788 9.28354 7.71251 9.32746 7.67726C10.0433 7.10275 10.8353 6.61834 11.6864 6.24167C11.7377 6.21899 11.7716 6.18701 11.7885 6.16304C11.796 6.15241 11.7988 6.1458 11.7996 6.14284V5.33329ZM11.8 6.14101C11.8001 6.141 11.8 6.14154 11.7997 6.14267ZM9.18436 7.73148C9.18439 7.73142 9.18499 7.73154 9.18603 7.73202ZM5.34619 14.8456C5.34622 14.8456 5.34669 14.8459 5.34755 14.8467ZM5.34619 17.1543C5.34618 17.1542 5.34661 17.1538 5.34755 17.1532ZM9.18439 24.2685C9.18436 24.2684 9.18487 24.2682 9.18606 24.2679ZM26.6511 17.1733C26.651 17.1733 26.6505 17.1729 26.6498 17.1722ZM26.6512 14.8266C26.6512 14.8267 26.6508 14.8271 26.6498 14.8277ZM22.8 7.71949C22.8001 7.71954 22.7996 7.71978 22.7984 7.72003ZM20.1623 6.1249C20.1623 6.12491 20.1625 6.12547 20.1626 6.12658ZM17.496 5.33329L14.4663 5.33329V6.14406C14.4663 7.32746 13.7029 8.2654 12.7657 8.68019C12.1284 8.96223 11.5343 9.3254 10.9966 9.75695C10.1842 10.409 8.97649 10.65 7.91912 10.0785L6.77942 9.46239L5.33321 11.8077L6.61674 12.5015C7.67141 13.0716 8.13151 14.2099 8.03478 15.2438C8.01153 15.4924 7.99961 15.7446 7.99961 16C7.99961 16.2553 8.01153 16.5075 8.03479 16.7561C8.13152 17.7901 7.67142 18.9283 6.61675 19.4984L5.33328 20.1922L6.77949 22.5375L7.91915 21.9215C8.97651 21.3499 10.1842 21.591 10.9966 22.243C11.5343 22.6745 12.1284 23.0377 12.7657 23.3197C13.7029 23.7345 14.4663 24.6725 14.4663 25.8559V26.6666H17.496V25.872C17.496 24.6843 18.2646 23.7443 19.2065 23.3316C19.8497 23.0498 20.4492 22.6855 20.9917 22.2518C21.8039 21.6022 23.0096 21.3628 24.0652 21.9335L25.1828 22.5375L26.629 20.1922L25.3806 19.5174C24.3242 18.9464 23.8644 17.8055 23.9631 16.7701C23.9872 16.517 23.9996 16.2601 23.9996 16C23.9996 15.7398 23.9872 15.483 23.9631 15.2299C23.8644 14.1945 24.3242 13.0536 25.3806 12.4825L26.629 11.8077L25.1828 9.46237L24.0653 10.0665C23.0097 10.6371 21.804 10.3978 20.9917 9.74817C20.4493 9.31439 19.8497 8.95008 19.2065 8.66828C18.2646 8.2556 17.496 7.31563 17.496 6.12796L17.496 5.33329Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0003 18.6666C17.4731 18.6666 18.667 17.4727 18.667 16C18.667 14.5272 17.4731 13.3333 16.0003 13.3333C14.5276 13.3333 13.3337 14.5272 13.3337 16C13.3337 17.4727 14.5276 18.6666 16.0003 18.6666ZM16.0003 21.3333C18.9458 21.3333 21.3337 18.9455 21.3337 16C21.3337 13.0544 18.9458 10.6666 16.0003 10.6666C13.0548 10.6666 10.667 13.0544 10.667 16C10.667 18.9455 13.0548 21.3333 16.0003 21.3333Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/settings-light.svg b/examples/quickcontrols/wearable/Wearable/icons/settings-light.svg
new file mode 100644
index 0000000000..bf17c3661a
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/settings-light.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.8 5.33268C11.8 3.85992 12.9939 2.66602 14.4667 2.66602H17.4963C18.9691 2.66602 20.163 3.85992 20.163 5.33268V6.12613C20.1639 6.12911 20.1667 6.13575 20.1742 6.14643C20.1913 6.17052 20.2255 6.20257 20.277 6.22515C21.1361 6.60152 21.9354 7.08747 22.6575 7.66496C22.7014 7.70007 22.7459 7.71535 22.7753 7.71911C22.7883 7.72077 22.7955 7.72008 22.7986 7.71945L23.9151 7.1159C25.1588 6.44362 26.711 6.85875 27.453 8.0621L28.8992 10.4074C29.7036 11.7118 29.2456 13.4242 27.8975 14.1529L26.6501 14.8272C26.6478 14.8295 26.6433 14.8351 26.6375 14.8471C26.6244 14.874 26.6128 14.9199 26.6181 14.9763C26.6502 15.3133 26.6666 15.6546 26.6666 15.9993C26.6666 16.3441 26.6502 16.6854 26.6181 17.0225C26.6127 17.0788 26.6244 17.1247 26.6375 17.1516C26.6433 17.1636 26.6478 17.1693 26.6501 17.1715L27.8974 17.8458C29.2455 18.5745 29.7035 20.2869 28.8991 21.5913L27.4529 23.9366C26.7109 25.1399 25.1587 25.5551 23.915 24.8828L22.7985 24.2793C22.7955 24.2786 22.7883 24.278 22.7753 24.2796C22.7459 24.2834 22.7014 24.2986 22.6575 24.3338C21.9354 24.9112 21.1361 25.3972 20.277 25.7735C20.2255 25.7961 20.1913 25.8282 20.1742 25.8523C20.1667 25.8629 20.1639 25.8696 20.163 25.8726V26.666C20.163 28.1388 18.9691 29.3327 17.4963 29.3327H14.4667C12.9939 29.3327 11.8 28.1388 11.8 26.666V25.8565C11.7991 25.8535 11.7964 25.8469 11.7889 25.8363C11.772 25.8123 11.738 25.7803 11.6868 25.7576C10.8357 25.381 10.0437 24.8966 9.32786 24.3221C9.28393 24.2868 9.23939 24.2715 9.20987 24.2677C9.19683 24.266 9.18964 24.2667 9.18658 24.2673L8.04794 24.8828C6.80427 25.5551 5.25209 25.1399 4.51005 23.9366L3.06383 21.5913C2.25947 20.2869 2.71744 18.5745 4.06556 17.8458L5.34802 17.1525C5.35024 17.1503 5.3548 17.1446 5.3606 17.1327C5.37368 17.1059 5.38534 17.0601 5.38008 17.0039C5.34911 16.6729 5.33331 16.3378 5.33331 15.9993C5.33331 15.6609 5.34911 15.3259 5.38008 14.9948C5.38534 14.9386 5.37367 14.8929 5.3606 14.866C5.35479 14.8541 5.35024 14.8485 5.34802 14.8462L4.06549 14.1529C2.71737 13.4242 2.2594 11.7118 3.06376 10.4074L4.50998 8.06211C5.25202 6.85876 6.8042 6.44364 8.04787 7.11591L9.18655 7.73144C9.18962 7.73206 9.1968 7.73275 9.20984 7.73107C9.23936 7.72727 9.2839 7.7119 9.32783 7.67665C10.0436 7.10214 10.8357 6.61773 11.6868 6.24106C11.738 6.21838 11.772 6.1864 11.7889 6.16243C11.7964 6.1518 11.7991 6.14519 11.8 6.14222V5.33268ZM11.8004 6.14039C11.8004 6.14039 11.8004 6.14093 11.8001 6.14206L11.8004 6.14039ZM9.18473 7.73087C9.18475 7.73081 9.18535 7.73093 9.1864 7.73141L9.18473 7.73087ZM5.34655 14.845C5.34658 14.845 5.34706 14.8453 5.34791 14.8461L5.34655 14.845ZM5.34655 17.1537C5.34655 17.1536 5.34698 17.1532 5.34791 17.1526L5.34655 17.1537ZM9.18476 24.2679C9.18472 24.2678 9.18524 24.2676 9.18643 24.2673L9.18476 24.2679ZM26.6515 17.1727C26.6514 17.1727 26.6509 17.1723 26.6501 17.1716L26.6515 17.1727ZM26.6515 14.826C26.6516 14.8261 26.6512 14.8265 26.6502 14.8271L26.6515 14.826ZM22.8004 7.71888C22.8005 7.71893 22.7999 7.71917 22.7987 7.71942L22.8004 7.71888ZM20.1626 6.12429C20.1627 6.12429 20.1629 6.12486 20.163 6.12597L20.1626 6.12429ZM17.4963 5.33268L14.4667 5.33268V6.14345C14.4667 7.32685 13.7033 8.26479 12.766 8.67958C12.1288 8.96162 11.5347 9.32479 10.997 9.75634C10.1846 10.4084 8.97685 10.6494 7.91949 10.0779L6.77979 9.46178L5.33357 11.8071L6.61711 12.5009C7.67178 13.071 8.13188 14.2092 8.03515 15.2432C8.0119 15.4918 7.99998 15.744 7.99998 15.9993C7.99998 16.2547 8.0119 16.5069 8.03515 16.7555C8.13189 17.7895 7.67179 18.9277 6.61711 19.4978L5.33364 20.1916L6.77986 22.5369L7.91951 21.9209C8.97688 21.3493 10.1846 21.5904 10.997 22.2424C11.5347 22.6739 12.1288 23.0371 12.766 23.3191C13.7033 23.7339 14.4667 24.6719 14.4667 25.8552V26.666H17.4963V25.8714C17.4963 24.6837 18.265 23.7437 19.2069 23.331C19.8501 23.0492 20.4496 22.6849 20.992 22.2512C21.8043 21.6016 23.01 21.3622 24.0656 21.9328L25.1831 22.5369L26.6293 20.1916L25.381 19.5168C24.3245 18.9457 23.8648 17.8049 23.9635 16.7695C23.9876 16.5164 24 16.2595 24 15.9993C24 15.7392 23.9876 15.4824 23.9635 15.2293C23.8648 14.1939 24.3245 13.053 25.381 12.4819L26.6294 11.8071L25.1832 9.46176L24.0656 10.0659C23.01 10.6365 21.8043 10.3972 20.992 9.74756C20.4496 9.31378 19.8501 8.94947 19.2069 8.66767C18.265 8.25499 17.4963 7.31501 17.4963 6.12735L17.4963 5.33268Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 18.666C17.4727 18.666 18.6666 17.4721 18.6666 15.9993C18.6666 14.5266 17.4727 13.3327 16 13.3327C14.5272 13.3327 13.3333 14.5266 13.3333 15.9993C13.3333 17.4721 14.5272 18.666 16 18.666ZM16 21.3327C18.9455 21.3327 21.3333 18.9449 21.3333 15.9993C21.3333 13.0538 18.9455 10.666 16 10.666C13.0544 10.666 10.6666 13.0538 10.6666 15.9993C10.6666 18.9449 13.0544 21.3327 16 21.3327Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/sun-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/sun-dark.svg
new file mode 100644
index 0000000000..adbfaa5067
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/sun-dark.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M20 26.6667C23.6819 26.6667 26.6667 23.6819 26.6667 20C26.6667 16.3181 23.6819 13.3333 20 13.3333C16.3181 13.3333 13.3333 16.3181 13.3333 20C13.3333 23.6819 16.3181 26.6667 20 26.6667ZM20 30C25.5228 30 30 25.5228 30 20C30 14.4772 25.5228 10 20 10C14.4772 10 10 14.4772 10 20C10 25.5228 14.4772 30 20 30Z" fill="#E6E6E6"/>
+<path d="M20.0003 8.33329C19.0798 8.33329 18.3337 7.5871 18.3337 6.66663V3.33329C18.3337 2.41282 19.0798 1.66663 20.0003 1.66663C20.9208 1.66663 21.667 2.41282 21.667 3.33329V6.66663C21.667 7.5871 20.9208 8.33329 20.0003 8.33329ZM30.5704 11.7632C29.9334 12.4002 28.9006 12.4002 28.2636 11.7632C27.6325 11.1322 27.6258 10.1111 28.2484 9.47175L30.6126 7.04431C31.2597 6.37985 32.3267 6.37752 32.9767 7.03915C33.6127 7.68651 33.6081 8.72551 32.9664 9.36723L30.5704 11.7632ZM33.3337 21.6666C32.4132 21.6666 31.667 20.9204 31.667 20C31.667 19.0795 32.4132 18.3333 33.3337 18.3333H36.667C37.5875 18.3333 38.3337 19.0795 38.3337 20C38.3337 20.9204 37.5875 21.6666 36.667 21.6666H33.3337ZM20.0003 38.3333C19.0798 38.3333 18.3337 37.5871 18.3337 36.6666V33.3333C18.3337 32.4128 19.0798 31.6666 20.0003 31.6666C20.9208 31.6666 21.667 32.4128 21.667 33.3333V36.6666C21.667 37.5871 20.9208 38.3333 20.0003 38.3333ZM11.7213 11.7362C11.0873 12.3476 10.0824 12.3452 9.45131 11.7307L7.04488 9.38764C6.38033 8.74058 6.37794 7.67356 7.03958 7.02352C7.68691 6.38755 8.72585 6.39215 9.36752 7.03382L11.7425 9.4088C12.3877 10.054 12.3781 11.1029 11.7213 11.7362ZM32.9582 32.9164C32.298 33.5997 31.2029 33.5994 30.543 32.9158L28.2303 30.5198C27.6169 29.8843 27.6258 28.8744 28.2504 28.2499C28.8749 27.6254 29.8846 27.6164 30.5201 28.2296L32.9165 30.5419C33.584 31.186 33.6027 32.2493 32.9582 32.9164ZM3.33366 21.6666C2.41318 21.6666 1.66699 20.9204 1.66699 20C1.66699 19.0795 2.41318 18.3333 3.33366 18.3333H6.66699C7.58747 18.3333 8.33366 19.0795 8.33366 20C8.33366 20.9204 7.58747 21.6666 6.66699 21.6666H3.33366ZM9.3962 32.9791C8.74205 33.6332 7.68001 33.6285 7.03167 32.9686C6.39139 32.3169 6.39602 31.2709 7.04204 30.6249L9.37458 28.2924C10.026 27.641 11.0761 27.6222 11.7503 28.25C12.4142 28.891 12.4235 29.9518 11.771 30.6043L9.3962 32.9791Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/sun-light.svg b/examples/quickcontrols/wearable/Wearable/icons/sun-light.svg
new file mode 100644
index 0000000000..2b1cc0636f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/sun-light.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M20 26.6667C23.6819 26.6667 26.6667 23.6819 26.6667 20C26.6667 16.3181 23.6819 13.3333 20 13.3333C16.3181 13.3333 13.3333 16.3181 13.3333 20C13.3333 23.6819 16.3181 26.6667 20 26.6667ZM20 30C25.5228 30 30 25.5228 30 20C30 14.4772 25.5228 10 20 10C14.4772 10 10 14.4772 10 20C10 25.5228 14.4772 30 20 30Z" fill="#191919"/>
+<path d="M20 8.33268C19.0795 8.33268 18.3334 7.58649 18.3334 6.66601V3.33268C18.3334 2.41221 19.0795 1.66602 20 1.66602C20.9205 1.66602 21.6667 2.41221 21.6667 3.33268V6.66602C21.6667 7.58649 20.9205 8.33268 20 8.33268ZM30.5701 11.7626C29.9331 12.3996 28.9003 12.3996 28.2633 11.7626C27.6322 11.1315 27.6255 10.1105 28.2481 9.47114L30.6123 7.0437C31.2594 6.37924 32.3264 6.37691 32.9764 7.03854C33.6124 7.6859 33.6078 8.7249 32.9661 9.36662L30.5701 11.7626ZM33.3333 21.666C32.4129 21.666 31.6667 20.9198 31.6667 19.9993C31.6667 19.0789 32.4129 18.3327 33.3334 18.3327H36.6667C37.5872 18.3327 38.3334 19.0789 38.3334 19.9993C38.3334 20.9198 37.5872 21.666 36.6667 21.666H33.3333ZM20 38.3327C19.0795 38.3327 18.3334 37.5865 18.3334 36.666V33.3327C18.3334 32.4122 19.0795 31.666 20 31.666C20.9205 31.666 21.6667 32.4122 21.6667 33.3327V36.666C21.6667 37.5865 20.9205 38.3327 20 38.3327ZM11.721 11.7356C11.087 12.347 10.0821 12.3446 9.451 11.7301L7.04458 9.38703C6.38003 8.73997 6.37764 7.67295 7.03928 7.02291C7.68661 6.38694 8.72554 6.39154 9.36722 7.03321L11.7422 9.40819C12.3874 10.0534 12.3778 11.1023 11.721 11.7356ZM32.9579 32.9158C32.2977 33.5991 31.2026 33.5988 30.5427 32.9152L28.23 30.5192C27.6166 29.8836 27.6255 28.8738 28.2501 28.2493C28.8746 27.6248 29.8843 27.6157 30.5198 28.229L32.9162 30.5413C33.5837 31.1854 33.6024 32.2487 32.9579 32.9158ZM3.33335 21.666C2.41288 21.666 1.66669 20.9198 1.66669 19.9993C1.66669 19.0789 2.41288 18.3327 3.33335 18.3327H6.66669C7.58716 18.3327 8.33335 19.0789 8.33335 19.9993C8.33335 20.9198 7.58716 21.666 6.66669 21.666H3.33335ZM9.3959 32.9785C8.74174 33.6326 7.6797 33.6279 7.03136 32.968C6.39108 32.3163 6.39571 31.2703 7.04173 30.6243L9.37428 28.2918C10.0257 27.6404 11.0758 27.6216 11.75 28.2493C12.4139 28.8903 12.4232 29.9512 11.7707 30.6037L9.3959 32.9785Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/sunrise-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/sunrise-dark.svg
new file mode 100644
index 0000000000..2e3c4530d1
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/sunrise-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21 19H3C2.44772 19 2 19.4477 2 20C2 20.5523 2.44772 21 3 21H21C21.5523 21 22 20.5523 22 20C22 19.4477 21.5523 19 21 19Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.707 2.29289C12.3165 1.90237 11.6833 1.90237 11.2928 2.29289L8.29277 5.29289C7.90225 5.68342 7.90225 6.31658 8.29277 6.70711C8.6833 7.09763 9.31646 7.09763 9.70698 6.70711L11.0039 5.41024C11.0012 5.43981 10.9999 5.46975 10.9999 5.5V8C10.9999 8.55228 11.4476 9 11.9999 9C12.5522 9 12.9999 8.55228 12.9999 8V5.5C12.9999 5.46975 12.9985 5.43981 12.9959 5.41024L14.2928 6.70711C14.6833 7.09763 15.3165 7.09763 15.707 6.70711C16.0975 6.31658 16.0975 5.68342 15.707 5.29289L12.707 2.29289ZM12.0896 4.50397L11.9999 4.41421L11.9101 4.50397C11.9397 4.50134 11.9696 4.5 11.9999 4.5C12.0301 4.5 12.0601 4.50134 12.0896 4.50397ZM4.95391 10.3375L4.23752 9.60402C3.85644 9.21387 3.86011 8.58976 4.24576 8.20412C4.63463 7.81524 5.26512 7.81524 5.65399 8.20412L6.37895 8.92908C6.77504 9.32517 6.7694 9.96908 6.36642 10.3582C5.97001 10.7409 5.33893 10.7317 4.95391 10.3375ZM17.6624 8.95403L18.3959 8.23765C18.786 7.85657 19.4101 7.86024 19.7958 8.24588C20.1846 8.63476 20.1846 9.26524 19.7958 9.65412L19.0708 10.3791C18.6747 10.7752 18.0308 10.7695 17.6417 10.3665C17.259 9.97014 17.2682 9.33905 17.6624 8.95403ZM14.7749 12.825C15.5915 13.375 16.1915 14.1 16.5749 15H7.42487C7.8082 14.1 8.4082 13.375 9.22487 12.825C10.0415 12.275 10.9665 12 11.9999 12C13.0332 12 13.9582 12.275 14.7749 12.825ZM5.25998 15.0231C4.96299 16.087 5.8953 17 6.99987 17H16.9999C18.1044 17 19.0367 16.087 18.7398 15.0231C18.4278 13.9057 17.8354 12.9105 16.9624 12.0375C15.604 10.6792 13.9499 10 11.9999 10C10.0499 10 8.3957 10.6792 7.03737 12.0375C6.16436 12.9105 5.5719 13.9057 5.25998 15.0231Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/sunrise-light.svg b/examples/quickcontrols/wearable/Wearable/icons/sunrise-light.svg
new file mode 100644
index 0000000000..1c1967a6bc
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/sunrise-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21 19H3C2.44772 19 2 19.4477 2 20C2 20.5523 2.44772 21 3 21H21C21.5523 21 22 20.5523 22 20C22 19.4477 21.5523 19 21 19Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.707 2.29289C12.3165 1.90237 11.6833 1.90237 11.2928 2.29289L8.29277 5.29289C7.90225 5.68342 7.90225 6.31658 8.29277 6.70711C8.6833 7.09763 9.31646 7.09763 9.70698 6.70711L11.0039 5.41024C11.0012 5.43981 10.9999 5.46975 10.9999 5.5V8C10.9999 8.55228 11.4476 9 11.9999 9C12.5522 9 12.9999 8.55228 12.9999 8V5.5C12.9999 5.46975 12.9985 5.43981 12.9959 5.41024L14.2928 6.70711C14.6833 7.09763 15.3165 7.09763 15.707 6.70711C16.0975 6.31658 16.0975 5.68342 15.707 5.29289L12.707 2.29289ZM12.0896 4.50397L11.9999 4.41421L11.9101 4.50397C11.9397 4.50134 11.9696 4.5 11.9999 4.5C12.0301 4.5 12.0601 4.50134 12.0896 4.50397ZM4.95391 10.3375L4.23752 9.60402C3.85644 9.21387 3.86011 8.58976 4.24576 8.20412C4.63463 7.81524 5.26512 7.81524 5.65399 8.20412L6.37895 8.92908C6.77504 9.32517 6.7694 9.96908 6.36642 10.3582C5.97001 10.7409 5.33893 10.7317 4.95391 10.3375ZM17.6624 8.95403L18.3959 8.23765C18.786 7.85657 19.4101 7.86024 19.7958 8.24588C20.1846 8.63476 20.1846 9.26524 19.7958 9.65412L19.0708 10.3791C18.6747 10.7752 18.0308 10.7695 17.6417 10.3665C17.259 9.97014 17.2682 9.33905 17.6624 8.95403ZM14.7749 12.825C15.5915 13.375 16.1915 14.1 16.5749 15H7.42487C7.8082 14.1 8.4082 13.375 9.22487 12.825C10.0415 12.275 10.9665 12 11.9999 12C13.0332 12 13.9582 12.275 14.7749 12.825ZM5.25998 15.0231C4.96299 16.087 5.8953 17 6.99987 17H16.9999C18.1044 17 19.0367 16.087 18.7398 15.0231C18.4278 13.9057 17.8354 12.9105 16.9624 12.0375C15.604 10.6792 13.9499 10 11.9999 10C10.0499 10 8.3957 10.6792 7.03737 12.0375C6.16436 12.9105 5.5719 13.9057 5.25998 15.0231Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/sunset-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/sunset-dark.svg
new file mode 100644
index 0000000000..89524e397a
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/sunset-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21 19H3C2.44772 19 2 19.4477 2 20C2 20.5523 2.44772 21 3 21H21C21.5523 21 22 20.5523 22 20C22 19.4477 21.5523 19 21 19Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0039 5.58976C11.0012 5.56019 10.9999 5.53025 10.9999 5.5V3C10.9999 2.44772 11.4476 2 11.9999 2C12.5522 2 12.9999 2.44772 12.9999 3V5.5C12.9999 5.53025 12.9985 5.56019 12.9959 5.58976L14.2928 4.29289C14.6833 3.90237 15.3165 3.90237 15.707 4.29289C16.0975 4.68342 16.0975 5.31658 15.707 5.70711L12.707 8.70711C12.3165 9.09763 11.6833 9.09763 11.2928 8.70711L8.29277 5.70711C7.90225 5.31658 7.90225 4.68342 8.29277 4.29289C8.68329 3.90237 9.31646 3.90237 9.70698 4.29289L11.0039 5.58976ZM11.9101 6.49603L11.9999 6.58579L12.0896 6.49603C12.0601 6.49866 12.0301 6.5 11.9999 6.5C11.9696 6.5 11.9397 6.49866 11.9101 6.49603ZM4.95391 10.3375L4.23752 9.60403C3.85644 9.21388 3.86011 8.58977 4.24576 8.20413C4.63463 7.81525 5.26512 7.81525 5.65399 8.20413L6.37895 8.92909C6.77504 9.32518 6.7694 9.96909 6.36642 10.3582C5.97001 10.7409 5.33893 10.7317 4.95391 10.3375ZM17.6624 8.95404L18.3959 8.23766C18.786 7.85658 19.4101 7.86025 19.7958 8.24589C20.1846 8.63477 20.1846 9.26525 19.7958 9.65413L19.0708 10.3791C18.6747 10.7752 18.0308 10.7695 17.6417 10.3666C17.259 9.97015 17.2682 9.33906 17.6624 8.95404ZM14.7749 12.825C15.5915 13.375 16.1915 14.1 16.5749 15H7.42487C7.8082 14.1 8.4082 13.375 9.22487 12.825C10.0415 12.275 10.9665 12 11.9999 12C13.0332 12 13.9582 12.275 14.7749 12.825ZM5.25998 15.0231C4.96299 16.087 5.8953 17 6.99987 17H16.9999C18.1044 17 19.0367 16.087 18.7398 15.0231C18.4278 13.9057 17.8354 12.9105 16.9624 12.0375C15.604 10.6792 13.9499 10 11.9999 10C10.0499 10 8.3957 10.6792 7.03737 12.0375C6.16436 12.9105 5.5719 13.9057 5.25998 15.0231Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/sunset-light.svg b/examples/quickcontrols/wearable/Wearable/icons/sunset-light.svg
new file mode 100644
index 0000000000..e260fd1d5f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/sunset-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21 19H3C2.44772 19 2 19.4477 2 20C2 20.5523 2.44772 21 3 21H21C21.5523 21 22 20.5523 22 20C22 19.4477 21.5523 19 21 19Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0039 5.58976C11.0012 5.56019 10.9999 5.53025 10.9999 5.5V3C10.9999 2.44772 11.4476 2 11.9999 2C12.5522 2 12.9999 2.44772 12.9999 3V5.5C12.9999 5.53025 12.9985 5.56019 12.9959 5.58976L14.2928 4.29289C14.6833 3.90237 15.3165 3.90237 15.707 4.29289C16.0975 4.68342 16.0975 5.31658 15.707 5.70711L12.707 8.70711C12.3165 9.09763 11.6833 9.09763 11.2928 8.70711L8.29277 5.70711C7.90225 5.31658 7.90225 4.68342 8.29277 4.29289C8.68329 3.90237 9.31646 3.90237 9.70698 4.29289L11.0039 5.58976ZM11.9101 6.49603L11.9999 6.58579L12.0896 6.49603C12.0601 6.49866 12.0301 6.5 11.9999 6.5C11.9696 6.5 11.9397 6.49866 11.9101 6.49603ZM4.95391 10.3375L4.23752 9.60403C3.85644 9.21388 3.86011 8.58977 4.24576 8.20413C4.63463 7.81525 5.26512 7.81525 5.65399 8.20413L6.37895 8.92909C6.77504 9.32518 6.7694 9.96909 6.36642 10.3582C5.97001 10.7409 5.33893 10.7317 4.95391 10.3375ZM17.6624 8.95404L18.3959 8.23766C18.786 7.85658 19.4101 7.86025 19.7958 8.24589C20.1846 8.63477 20.1846 9.26525 19.7958 9.65413L19.0708 10.3791C18.6747 10.7752 18.0308 10.7695 17.6417 10.3666C17.259 9.97015 17.2682 9.33906 17.6624 8.95404ZM14.7749 12.825C15.5915 13.375 16.1915 14.1 16.5749 15H7.42487C7.8082 14.1 8.4082 13.375 9.22487 12.825C10.0415 12.275 10.9665 12 11.9999 12C13.0332 12 13.9582 12.275 14.7749 12.825ZM5.25998 15.0231C4.96299 16.087 5.8953 17 6.99987 17H16.9999C18.1044 17 19.0367 16.087 18.7398 15.0231C18.4278 13.9057 17.8354 12.9105 16.9624 12.0375C15.604 10.6792 13.9499 10 11.9999 10C10.0499 10 8.3957 10.6792 7.03737 12.0375C6.16436 12.9105 5.5719 13.9057 5.25998 15.0231Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/thermometer-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/thermometer-dark.svg
new file mode 100644
index 0000000000..4a4888318d
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/thermometer-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10 13V11H14V13C14.6667 13.6667 16 15.6 16 18C16 20.4 13.3333 21 12 21C10.6667 21 8 20.4 8 18C8 15.6 9.33333 13.6667 10 13Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C10.3431 2 9 3.34315 9 5V12.9997C7.78742 13.9103 7 15.3632 7 17C7 19.7614 9.23858 22 12 22C14.7614 22 17 19.7614 17 17C17 15.3632 16.2126 13.9103 15 12.9997V9V6V5C15 3.34315 13.6569 2 12 2ZM13 5C12.4477 5 12 5.44772 12 6C12 6.55228 12.4477 7 13 7V8C12.4477 8 12 8.44772 12 9C12 9.55228 12.4477 10 13 10V13C13 13.6288 13.2902 14.1899 13.744 14.5565L13.743 14.5579L13.7761 14.5819C13.7918 14.5941 13.8076 14.606 13.8237 14.6177C14.5407 15.1675 15 16.0301 15 17C15 18.6568 13.6569 20 12 20C10.3431 20 9 18.6568 9 17C9 16.0301 9.45933 15.1675 10.1763 14.6177C10.1924 14.606 10.2082 14.594 10.2239 14.5819L10.257 14.5579L10.256 14.5565C10.7098 14.1899 11 13.6288 11 13V5C11 4.44772 11.4477 4 12 4C12.5523 4 13 4.44772 13 5Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/thermometer-light.svg b/examples/quickcontrols/wearable/Wearable/icons/thermometer-light.svg
new file mode 100644
index 0000000000..6019435c79
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/thermometer-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10 13V11H14V13C14.6667 13.6667 16 15.6 16 18C16 20.4 13.3333 21 12 21C10.6667 21 8 20.4 8 18C8 15.6 9.33333 13.6667 10 13Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C10.3431 2 9 3.34315 9 5V12.9997C7.78742 13.9103 7 15.3632 7 17C7 19.7614 9.23858 22 12 22C14.7614 22 17 19.7614 17 17C17 15.3632 16.2126 13.9103 15 12.9997V9V6V5C15 3.34315 13.6569 2 12 2ZM13 5C12.4477 5 12 5.44772 12 6C12 6.55228 12.4477 7 13 7V8C12.4477 8 12 8.44772 12 9C12 9.55228 12.4477 10 13 10V13C13 13.6288 13.2902 14.1899 13.744 14.5565L13.743 14.5579L13.7761 14.5819C13.7918 14.5941 13.8076 14.606 13.8237 14.6177C14.5407 15.1675 15 16.0301 15 17C15 18.6568 13.6569 20 12 20C10.3431 20 9 18.6568 9 17C9 16.0301 9.45933 15.1675 10.1763 14.6177C10.1924 14.606 10.2082 14.594 10.2239 14.5819L10.257 14.5579L10.256 14.5565C10.7098 14.1899 11 13.6288 11 13V5C11 4.44772 11.4477 4 12 4C12.5523 4 13 4.44772 13 5Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/uturnleft-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/uturnleft-dark.svg
new file mode 100644
index 0000000000..299dd053d1
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/uturnleft-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M19 20C19 15.5943 19 12.4999 19 9.99996C19 2.00003 6.99889 2.00001 7 9.99997C7.00035 12.5 7 15.5826 7 20" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10 17L7 20L4 17" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/uturnleft-light.svg b/examples/quickcontrols/wearable/Wearable/icons/uturnleft-light.svg
new file mode 100644
index 0000000000..f7d3786a3c
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/uturnleft-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M19 20C19 15.5943 19 12.4999 19 9.99996C19 2.00003 6.99889 2.00001 7 9.99997C7.00035 12.5 7 15.5826 7 20" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10 17L7 20L4 17" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/uturnright-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/uturnright-dark.svg
new file mode 100644
index 0000000000..35b44d1e7a
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/uturnright-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5 20C5 15.5943 5 12.4999 5 9.99996C5 2.00003 17.0011 2.00001 17 9.99997C16.9997 12.5 17 15.5826 17 20" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M20 17L17 20L14 17" stroke="#2CDE85" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/uturnright-light.svg b/examples/quickcontrols/wearable/Wearable/icons/uturnright-light.svg
new file mode 100644
index 0000000000..5eda60955f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/uturnright-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5 20C5 15.5943 5 12.4999 5 9.99996C5 2.00003 17.0011 2.00001 17 9.99997C16.9997 12.5 17 15.5826 17 20" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M20 17L17 20L14 17" stroke="#191919" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-dark.svg
new file mode 100644
index 0000000000..a3ee0f2b08
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-dark.svg
@@ -0,0 +1,10 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21.771 19.0705C21.207 15.8248 18.231 13.3334 14.667 13.3334C12.051 13.3334 9.66299 14.6819 8.40299 16.8305C5.17499 17.2305 2.66699 19.8934 2.66699 23.0477C2.66699 26.5105 5.63099 29.3334 9.26699 29.3334H21.267C24.243 29.3334 26.667 27.0248 26.667 24.1905C26.667 21.5162 24.519 19.3105 21.771 19.0705ZM21.267 27.0477H9.26699C6.95099 27.0477 5.06699 25.2534 5.06699 23.0477C5.06699 21.0591 6.63899 19.3562 8.71499 19.0934L9.90299 18.9448L10.491 17.9505C11.343 16.5105 12.939 15.6191 14.667 15.6191C16.995 15.6191 19.023 17.2648 19.407 19.4477L19.707 21.1848L21.555 21.3448C23.079 21.4819 24.267 22.7277 24.267 24.1905C24.267 25.7677 22.923 27.0477 21.267 27.0477Z" fill="#E6E6E6"/>
+<path d="M20 5.81111C19.3975 5.81111 18.9091 5.30992 18.9091 4.69168V2.45281C18.9091 1.83456 19.3975 1.33337 20 1.33337C20.6025 1.33337 21.0909 1.83456 21.0909 2.45281V4.69168C21.0909 5.30992 20.6025 5.81111 20 5.81111Z" fill="#E6E6E6"/>
+<path d="M26.9186 8.11486C26.5016 8.54271 25.8256 8.54271 25.4087 8.11486C24.9956 7.691 24.9912 7.0052 25.3988 6.57577L26.9462 4.94535C27.3698 4.49906 28.0682 4.4975 28.4936 4.94189C28.9099 5.3767 28.9069 6.07455 28.4869 6.50557L26.9186 8.11486Z" fill="#E6E6E6"/>
+<path d="M28.7273 14.7666C28.1248 14.7666 27.6364 14.2654 27.6364 13.6472C27.6364 13.0289 28.1248 12.5277 28.7273 12.5277H30.9091C31.5116 12.5277 32 13.0289 32 13.6472C32 14.2654 31.5116 14.7666 30.9091 14.7666H28.7273Z" fill="#E6E6E6"/>
+<path d="M14.581 8.09674C14.166 8.50739 13.5083 8.50575 13.0952 8.09304L11.5201 6.51928C11.0851 6.08467 11.0835 5.368 11.5166 4.93139C11.9403 4.50423 12.6203 4.50732 13.0403 4.93831L14.5949 6.53349C15.0172 6.96682 15.0109 7.67135 14.581 8.09674Z" fill="#E6E6E6"/>
+<path d="M28.4815 22.3226C28.0494 22.7816 27.3326 22.7814 26.9007 22.3222L25.3869 20.7129C24.9854 20.2861 24.9913 19.6078 25.4001 19.1883C25.8088 18.7688 26.4697 18.7628 26.8857 19.1747L28.4542 20.7278C28.8911 21.1604 28.9034 21.8745 28.4815 22.3226Z" fill="#E6E6E6"/>
+<path d="M9.09091 14.7666C8.48842 14.7666 8 14.2654 8 13.6472C8 13.0289 8.48842 12.5277 9.09091 12.5277H11.2727C11.8752 12.5277 12.3636 13.0289 12.3636 13.6472C12.3636 13.7854 12.3392 13.9179 12.2945 14.0401C11.7584 14.2234 11.2481 14.4677 10.7739 14.7666H9.09091Z" fill="#E6E6E6"/>
+<path d="M13.4553 13.7472C13.4548 13.7139 13.4545 13.6806 13.4545 13.6472C13.4545 9.93767 16.385 6.93055 20 6.93055C23.615 6.93055 26.5455 9.93767 26.5455 13.6472C26.5455 16.3322 25.0101 18.6492 22.7911 19.7242C22.4629 19.6302 22.1217 19.5658 21.7707 19.5343C21.6757 18.9735 21.5123 18.4346 21.2893 17.9262C23.0699 17.3619 24.3636 15.6596 24.3636 13.6472C24.3636 11.1742 22.41 9.16942 20 9.16942C17.59 9.16942 15.6364 11.1742 15.6364 13.6472C15.6364 13.6685 15.6365 13.6898 15.6368 13.7111C15.3194 13.6689 14.9955 13.6472 14.6667 13.6472C14.2561 13.6472 13.8511 13.6812 13.4553 13.7472Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-few-clouds-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-few-clouds-dark.svg
new file mode 100644
index 0000000000..2d1f8e680e
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-few-clouds-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.9086 8C12.541 8 11.3358 8.63698 10.5823 9.61164L9 8.38836C10.1243 6.93413 11.9088 6 13.9086 6C16.5884 6 18.8958 7.68436 19.7262 10.0561C21.6158 10.3753 23.0551 12.0196 23.0551 14C23.0551 15.0953 22.6135 16.0895 21.901 16.8108L20.4781 15.4054C20.8359 15.0431 21.0551 14.5481 21.0551 14C21.0551 12.8954 20.1596 12 19.0551 12H18.0551C18.0551 9.81787 16.2261 8 13.9086 8ZM11 10C13.673 10 15.905 11.8686 16.328 14.3029C18.389 14.4829 20 16.1371 20 18.1429C20 20.2686 18.182 22 15.95 22H6.95C4.223 22 2 19.8829 2 17.2857C2 14.92 3.881 12.9229 6.302 12.6229C7.247 11.0114 9.038 10 11 10ZM6.95 20.2857H15.95C17.192 20.2857 18.2 19.3257 18.2 18.1429C18.2 17.0457 17.309 16.1114 16.166 16.0086L14.78 15.8886L14.555 14.5857C14.267 12.9486 12.746 11.7143 11 11.7143C9.704 11.7143 8.507 12.3829 7.868 13.4629L7.427 14.2086L6.536 14.32C4.979 14.5171 3.8 15.7943 3.8 17.2857C3.8 18.94 5.213 20.2857 6.95 20.2857Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9 3.3583C9.45187 3.3583 9.81818 2.98241 9.81818 2.51873V0.839576C9.81818 0.375891 9.45187 0 9 0C8.54813 0 8.18182 0.375891 8.18182 0.839576V2.51873C8.18182 2.98241 8.54813 3.3583 9 3.3583ZM3.81106 5.08612C4.12377 5.407 4.63078 5.407 4.94349 5.08611C5.25328 4.76822 5.2566 4.25387 4.95092 3.9318L3.79035 2.70898C3.47267 2.37426 2.94888 2.37309 2.62978 2.70638C2.31756 3.03249 2.31982 3.55588 2.63484 3.87914L3.81106 5.08612ZM3.27273 9.23534C3.27273 9.69902 2.90641 10.0749 2.45455 10.0749H0.818182C0.366312 10.0749 0 9.69902 0 9.23534C0 8.77165 0.366312 8.39576 0.818182 8.39576H2.45455C2.90641 8.39576 3.27273 8.77165 3.27273 9.23534ZM13.0642 5.07252C13.3755 5.38051 13.8688 5.37929 14.1786 5.06975L15.3599 3.88943C15.6862 3.56347 15.6874 3.02597 15.3625 2.69851C15.0448 2.37815 14.5347 2.38046 14.2197 2.7037L13.0538 3.90008C12.7371 4.22509 12.7418 4.75348 13.0642 5.07252ZM13.4005 7C12.5979 5.3394 10.9289 4.19788 9 4.19788C6.28878 4.19788 4.09091 6.45323 4.09091 9.23534C4.09091 11.2491 5.24243 12.9869 6.90665 13.7931C7.15281 13.7226 7.50001 13.5 7.50001 13.5C7.50001 13.5 7.86575 12.8259 8.03302 12.4446C6.69758 12.0214 5.72727 10.7447 5.72727 9.23534C5.72727 7.3806 7.19252 5.87703 9 5.87703C9.97093 5.87703 10.8431 6.3109 11.4425 7H13.4005Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-few-clouds-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-few-clouds-light.svg
new file mode 100644
index 0000000000..7eae3666c0
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-few-clouds-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.9086 8C12.541 8 11.3358 8.63698 10.5823 9.61164L9 8.38836C10.1243 6.93413 11.9088 6 13.9086 6C16.5884 6 18.8958 7.68436 19.7262 10.0561C21.6158 10.3753 23.0551 12.0196 23.0551 14C23.0551 15.0953 22.6135 16.0895 21.901 16.8108L20.4781 15.4054C20.8359 15.0431 21.0551 14.5481 21.0551 14C21.0551 12.8954 20.1596 12 19.0551 12H18.0551C18.0551 9.81787 16.2261 8 13.9086 8ZM11 10C13.673 10 15.905 11.8686 16.328 14.3029C18.389 14.4829 20 16.1371 20 18.1429C20 20.2686 18.182 22 15.95 22H6.95C4.223 22 2 19.8829 2 17.2857C2 14.92 3.881 12.9229 6.302 12.6229C7.247 11.0114 9.038 10 11 10ZM6.95 20.2857H15.95C17.192 20.2857 18.2 19.3257 18.2 18.1429C18.2 17.0457 17.309 16.1114 16.166 16.0086L14.78 15.8886L14.555 14.5857C14.267 12.9486 12.746 11.7143 11 11.7143C9.704 11.7143 8.507 12.3829 7.868 13.4629L7.427 14.2086L6.536 14.32C4.979 14.5171 3.8 15.7943 3.8 17.2857C3.8 18.94 5.213 20.2857 6.95 20.2857Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9 3.3583C9.45187 3.3583 9.81818 2.98241 9.81818 2.51873V0.839576C9.81818 0.375891 9.45187 0 9 0C8.54813 0 8.18182 0.375891 8.18182 0.839576V2.51873C8.18182 2.98241 8.54813 3.3583 9 3.3583ZM3.81106 5.08612C4.12377 5.407 4.63078 5.407 4.94349 5.08611C5.25328 4.76822 5.2566 4.25387 4.95092 3.9318L3.79035 2.70898C3.47267 2.37426 2.94888 2.37309 2.62978 2.70638C2.31756 3.03249 2.31982 3.55588 2.63484 3.87914L3.81106 5.08612ZM3.27273 9.23534C3.27273 9.69902 2.90641 10.0749 2.45455 10.0749H0.818182C0.366312 10.0749 0 9.69902 0 9.23534C0 8.77165 0.366312 8.39576 0.818182 8.39576H2.45455C2.90641 8.39576 3.27273 8.77165 3.27273 9.23534ZM13.0642 5.07252C13.3755 5.38051 13.8688 5.37929 14.1786 5.06975L15.3599 3.88943C15.6862 3.56347 15.6874 3.02597 15.3625 2.69851C15.0448 2.37815 14.5347 2.38046 14.2197 2.7037L13.0538 3.90008C12.7371 4.22509 12.7418 4.75348 13.0642 5.07252ZM13.4005 7C12.5979 5.3394 10.9289 4.19788 9 4.19788C6.28878 4.19788 4.09091 6.45323 4.09091 9.23534C4.09091 11.2491 5.24243 12.9869 6.90665 13.7931C7.15281 13.7226 7.50001 13.5 7.50001 13.5C7.50001 13.5 7.86575 12.8259 8.03302 12.4446C6.69758 12.0214 5.72727 10.7447 5.72727 9.23534C5.72727 7.3806 7.19252 5.87703 9 5.87703C9.97093 5.87703 10.8431 6.3109 11.4425 7H13.4005Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-fog-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-fog-dark.svg
new file mode 100644
index 0000000000..9c8648157a
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-fog-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3 13C2.44772 13 2 13.4477 2 14C2 14.5523 2.44772 15 3 15H21C21.5523 15 22 14.5523 22 14C22 13.4477 21.5523 13 21 13H3ZM15 16C14.4477 16 14 16.4477 14 17C14 17.5523 14.4477 18 15 18H21C21.5523 18 22 17.5523 22 17C22 16.4477 21.5523 16 21 16H15ZM9 17C9 16.4477 9.44772 16 10 16H12C12.5523 16 13 16.4477 13 17C13 17.5523 12.5523 18 12 18H10C9.44772 18 9 17.5523 9 17ZM19 19C18.4477 19 18 19.4477 18 20C18 20.5523 18.4477 21 19 21H21C21.5523 21 22 20.5523 22 20C22 19.4477 21.5523 19 21 19H19ZM2 17C2 16.4477 2.44772 16 3 16H7C7.55228 16 8 16.4477 8 17C8 17.5523 7.55228 18 7 18H3C2.44772 18 2 17.5523 2 17ZM3 19C2.44772 19 2 19.4477 2 20C2 20.5523 2.44772 21 3 21H16C16.5523 21 17 20.5523 17 20C17 19.4477 16.5523 19 16 19H3Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C14.673 3 16.905 4.86857 17.328 7.30286C19.389 7.48286 21 9.13714 21 11.1429C21 11.4373 20.9651 11.7242 20.8991 12H19.0126C19.1331 11.7375 19.2 11.4476 19.2 11.1429C19.2 10.0457 18.309 9.11143 17.166 9.00857L15.78 8.88857L15.555 7.58571C15.267 5.94857 13.746 4.71429 12 4.71429C10.704 4.71429 9.507 5.38286 8.868 6.46286L8.427 7.20857L7.536 7.32C5.979 7.51714 4.8 8.79429 4.8 10.2857C4.8 10.9226 5.00944 11.5138 5.36617 12H3.33939C3.12035 11.4683 3 10.89 3 10.2857C3 7.92 4.881 5.92286 7.302 5.62286C8.247 4.01143 10.038 3 12 3Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-fog-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-fog-light.svg
new file mode 100644
index 0000000000..cb3c3ff562
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-fog-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3 13C2.44772 13 2 13.4477 2 14C2 14.5523 2.44772 15 3 15H21C21.5523 15 22 14.5523 22 14C22 13.4477 21.5523 13 21 13H3ZM15 16C14.4477 16 14 16.4477 14 17C14 17.5523 14.4477 18 15 18H21C21.5523 18 22 17.5523 22 17C22 16.4477 21.5523 16 21 16H15ZM9 17C9 16.4477 9.44772 16 10 16H12C12.5523 16 13 16.4477 13 17C13 17.5523 12.5523 18 12 18H10C9.44772 18 9 17.5523 9 17ZM19 19C18.4477 19 18 19.4477 18 20C18 20.5523 18.4477 21 19 21H21C21.5523 21 22 20.5523 22 20C22 19.4477 21.5523 19 21 19H19ZM2 17C2 16.4477 2.44772 16 3 16H7C7.55228 16 8 16.4477 8 17C8 17.5523 7.55228 18 7 18H3C2.44772 18 2 17.5523 2 17ZM3 19C2.44772 19 2 19.4477 2 20C2 20.5523 2.44772 21 3 21H16C16.5523 21 17 20.5523 17 20C17 19.4477 16.5523 19 16 19H3Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C14.673 3 16.905 4.86857 17.328 7.30286C19.389 7.48286 21 9.13714 21 11.1429C21 11.4373 20.9651 11.7242 20.8991 12H19.0126C19.1331 11.7375 19.2 11.4476 19.2 11.1429C19.2 10.0457 18.309 9.11143 17.166 9.00857L15.78 8.88857L15.555 7.58571C15.267 5.94857 13.746 4.71429 12 4.71429C10.704 4.71429 9.507 5.38286 8.868 6.46286L8.427 7.20857L7.536 7.32C5.979 7.51714 4.8 8.79429 4.8 10.2857C4.8 10.9226 5.00944 11.5138 5.36617 12H3.33939C3.12035 11.4683 3 10.89 3 10.2857C3 7.92 4.881 5.92286 7.302 5.62286C8.247 4.01143 10.038 3 12 3Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-haze-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-haze-dark.svg
new file mode 100644
index 0000000000..f2b8dff07c
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-haze-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18 14C17.4477 14 17 14.4477 17 15C17 15.5523 17.4477 16 18 16H20C20.5523 16 21 15.5523 21 15C21 14.4477 20.5523 14 20 14H18ZM2 19C2 18.4477 2.44772 18 3 18H13C13.5523 18 14 18.4477 14 19C14 19.5523 13.5523 20 13 20H3C2.44772 20 2 19.5523 2 19ZM16 19C16 18.4477 16.4477 18 17 18H20C20.5523 18 21 18.4477 21 19C21 19.5523 20.5523 20 20 20H17C16.4477 20 16 19.5523 16 19ZM12 15C12 14.4477 12.4477 14 13 14H14C14.5523 14 15 14.4477 15 15C15 15.5523 14.5523 16 14 16H13C12.4477 16 12 15.5523 12 15ZM5 14C4.44772 14 4 14.4477 4 15C4 15.5523 4.44772 16 5 16H9C9.55228 16 10 15.5523 10 15C10 14.4477 9.55228 14 9 14H5Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10 5C10 4.44772 10.4477 4 11 4H21C21.5523 4 22 4.44772 22 5C22 5.55228 21.5523 6 21 6H11C10.4477 6 10 5.55228 10 5ZM4 9C3.44772 9 3 9.44772 3 10C3 10.5523 3.44772 11 4 11H7C7.55228 11 8 10.5523 8 10C8 9.44772 7.55228 9 7 9H4ZM9 10C9 9.44772 9.44772 9 10 9H12C12.5523 9 13 9.44772 13 10C13 10.5523 12.5523 11 12 11H10C9.44772 11 9 10.5523 9 10ZM14 10C14 9.44772 14.4477 9 15 9H19C19.5523 9 20 9.44772 20 10C20 10.5523 19.5523 11 19 11H15C14.4477 11 14 10.5523 14 10ZM5 4C4.44772 4 4 4.44772 4 5C4 5.55228 4.44772 6 5 6H8C8.55228 6 9 5.55228 9 5C9 4.44772 8.55228 4 8 4H5Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-haze-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-haze-light.svg
new file mode 100644
index 0000000000..790d55db30
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-haze-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18 14C17.4477 14 17 14.4477 17 15C17 15.5523 17.4477 16 18 16H20C20.5523 16 21 15.5523 21 15C21 14.4477 20.5523 14 20 14H18ZM2 19C2 18.4477 2.44772 18 3 18H13C13.5523 18 14 18.4477 14 19C14 19.5523 13.5523 20 13 20H3C2.44772 20 2 19.5523 2 19ZM16 19C16 18.4477 16.4477 18 17 18H20C20.5523 18 21 18.4477 21 19C21 19.5523 20.5523 20 20 20H17C16.4477 20 16 19.5523 16 19ZM12 15C12 14.4477 12.4477 14 13 14H14C14.5523 14 15 14.4477 15 15C15 15.5523 14.5523 16 14 16H13C12.4477 16 12 15.5523 12 15ZM5 14C4.44772 14 4 14.4477 4 15C4 15.5523 4.44772 16 5 16H9C9.55228 16 10 15.5523 10 15C10 14.4477 9.55228 14 9 14H5Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10 5C10 4.44772 10.4477 4 11 4H21C21.5523 4 22 4.44772 22 5C22 5.55228 21.5523 6 21 6H11C10.4477 6 10 5.55228 10 5ZM4 9C3.44772 9 3 9.44772 3 10C3 10.5523 3.44772 11 4 11H7C7.55228 11 8 10.5523 8 10C8 9.44772 7.55228 9 7 9H4ZM9 10C9 9.44772 9.44772 9 10 9H12C12.5523 9 13 9.44772 13 10C13 10.5523 12.5523 11 12 11H10C9.44772 11 9 10.5523 9 10ZM14 10C14 9.44772 14.4477 9 15 9H19C19.5523 9 20 9.44772 20 10C20 10.5523 19.5523 11 19 11H15C14.4477 11 14 10.5523 14 10ZM5 4C4.44772 4 4 4.44772 4 5C4 5.55228 4.44772 6 5 6H8C8.55228 6 9 5.55228 9 5C9 4.44772 8.55228 4 8 4H5Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-icy-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-icy-dark.svg
new file mode 100644
index 0000000000..8dcc521d0b
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-icy-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.5 14C5.5 13.7239 5.27614 13.5 5 13.5C4.72386 13.5 4.5 13.7239 4.5 14V14.5H4C3.72386 14.5 3.5 14.7239 3.5 15C3.5 15.2761 3.72386 15.5 4 15.5H4.5V16C4.5 16.2761 4.72386 16.5 5 16.5C5.27614 16.5 5.5 16.2761 5.5 16V15.5H6C6.27614 15.5 6.5 15.2761 6.5 15C6.5 14.7239 6.27614 14.5 6 14.5H5.5V14ZM19 17.5C19.2761 17.5 19.5 17.7239 19.5 18V18.5H20C20.2761 18.5 20.5 18.7239 20.5 19C20.5 19.2761 20.2761 19.5 20 19.5H19.5V20C19.5 20.2761 19.2761 20.5 19 20.5C18.7239 20.5 18.5 20.2761 18.5 20V19.5H18C17.7239 19.5 17.5 19.2761 17.5 19C17.5 18.7239 17.7239 18.5 18 18.5H18.5V18C18.5 17.7239 18.7239 17.5 19 17.5Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3C3.34315 3 2 4.34315 2 6V8.13641C2 10.57 4.74498 11.9909 6.73199 10.5859L8.07869 9.63373C8.20627 10.3839 8.55665 12.4117 8.93864 14.3371C9.14782 15.3914 9.37318 16.4495 9.58162 17.2576C9.68325 17.6517 9.79594 18.0463 9.9166 18.3664C9.9722 18.5139 10.0636 18.739 10.1992 18.9595C10.2643 19.0654 10.3939 19.2608 10.6025 19.452C10.791 19.6247 11.2706 20 12 20C12.7294 20 13.209 19.6247 13.3975 19.452C13.6061 19.2608 13.7357 19.0654 13.8008 18.9595C13.9364 18.739 14.0278 18.5139 14.0834 18.3664C14.2041 18.0463 14.3168 17.6517 14.4184 17.2576C14.6268 16.4495 14.8522 15.3914 15.0614 14.3371C15.0882 14.2016 15.115 14.0656 15.1415 13.9295C15.2309 14.0726 15.3664 14.259 15.5612 14.4364C15.8149 14.6675 16.3045 15 17.0001 15C17.6961 15 18.1857 14.6672 18.4393 14.4362C18.6879 14.2097 18.84 13.9684 18.9227 13.8216C19.091 13.5233 19.1969 13.2076 19.2632 12.9845C19.4049 12.5086 19.5136 11.9392 19.5941 11.4492C19.6775 10.9417 19.7428 10.4419 19.7869 10.0739C19.8091 9.88865 19.8263 9.73377 19.8381 9.62424C19.8439 9.56941 19.8485 9.52579 19.8516 9.49523L19.8552 9.45935L19.8562 9.44916L19.8565 9.44607L19.8566 9.44504L19.8567 9.44466C19.8567 9.44451 19.8567 9.44437 18.148 9.27752L19.8567 9.44436L19.993 8.0486L20.6548 7.70544C21.4812 7.27691 22 6.42355 22 5.49261C22 4.11598 20.884 3 19.5074 3H5ZM13.882 9H13.3052H12.0284H12.0161H11.9839H11.9716H10.6948H10.118C10.0563 9 10.0089 9.053 10.0191 9.11393C10.0387 9.2298 10.0726 9.4297 10.118 9.69352C10.1765 10.0329 10.2542 10.478 10.345 10.986L10.3475 11C10.5233 11.9833 10.7482 13.1998 10.9793 14.3415C11.1065 14.97 11.2356 15.5757 11.3594 16.1074C11.6172 17.2144 11.8522 18 12 18C12.1478 18 12.3828 17.2144 12.6406 16.1074C12.7644 15.5757 12.8935 14.97 13.0207 14.3415C13.2518 13.1998 13.4767 11.9833 13.6525 11L13.655 10.986C13.7458 10.478 13.8235 10.0329 13.882 9.69352C13.9274 9.4297 13.9613 9.2298 13.9809 9.11393C13.9911 9.053 13.9437 9 13.882 9ZM13.882 7H17.4266C17.5868 7 17.7447 6.9615 17.8869 6.88775L19.7341 5.92993C19.8975 5.84524 20 5.67659 20 5.49261C20 5.22055 19.7794 5 19.5074 5H5C4.44772 5 4 5.44772 4 6V8.13641C4 8.94759 4.91499 9.42124 5.57733 8.95292L7.5608 7.55047C8.06733 7.19232 8.67243 7 9.29279 7H10.118H13.882ZM10.3283 16.986C10.3283 16.9859 10.3294 16.9831 10.3318 16.9779C10.3295 16.9835 10.3283 16.9861 10.3283 16.986ZM13.6712 16.9848C13.671 16.9845 13.6709 16.9842 13.6707 16.9838L13.6682 16.9779C13.6706 16.9831 13.6717 16.9859 13.6717 16.986C13.6717 16.986 13.6715 16.9857 13.6712 16.9848ZM16.4005 11.25C16.5529 12.144 16.7612 13 17.0001 13C17.2389 13 17.4472 12.144 17.5997 11.25C17.6079 11.2017 17.616 11.1534 17.6239 11.105C17.7759 10.1742 17.8661 9.25 17.8661 9.25H16.1341C16.1341 9.25 16.2243 10.1743 16.3763 11.1051C16.3842 11.1534 16.3923 11.2018 16.4005 11.25Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-icy-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-icy-light.svg
new file mode 100644
index 0000000000..374f02d62b
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-icy-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.5 14C5.5 13.7239 5.27614 13.5 5 13.5C4.72386 13.5 4.5 13.7239 4.5 14V14.5H4C3.72386 14.5 3.5 14.7239 3.5 15C3.5 15.2761 3.72386 15.5 4 15.5H4.5V16C4.5 16.2761 4.72386 16.5 5 16.5C5.27614 16.5 5.5 16.2761 5.5 16V15.5H6C6.27614 15.5 6.5 15.2761 6.5 15C6.5 14.7239 6.27614 14.5 6 14.5H5.5V14ZM19 17.5C19.2761 17.5 19.5 17.7239 19.5 18V18.5H20C20.2761 18.5 20.5 18.7239 20.5 19C20.5 19.2761 20.2761 19.5 20 19.5H19.5V20C19.5 20.2761 19.2761 20.5 19 20.5C18.7239 20.5 18.5 20.2761 18.5 20V19.5H18C17.7239 19.5 17.5 19.2761 17.5 19C17.5 18.7239 17.7239 18.5 18 18.5H18.5V18C18.5 17.7239 18.7239 17.5 19 17.5Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3C3.34315 3 2 4.34315 2 6V8.13641C2 10.57 4.74498 11.9909 6.73199 10.5859L8.07869 9.63373C8.20627 10.3839 8.55665 12.4117 8.93864 14.3371C9.14782 15.3914 9.37318 16.4495 9.58162 17.2576C9.68325 17.6517 9.79594 18.0463 9.9166 18.3664C9.9722 18.5139 10.0636 18.739 10.1992 18.9595C10.2643 19.0654 10.3939 19.2608 10.6025 19.452C10.791 19.6247 11.2706 20 12 20C12.7294 20 13.209 19.6247 13.3975 19.452C13.6061 19.2608 13.7357 19.0654 13.8008 18.9595C13.9364 18.739 14.0278 18.5139 14.0834 18.3664C14.2041 18.0463 14.3168 17.6517 14.4184 17.2576C14.6268 16.4495 14.8522 15.3914 15.0614 14.3371C15.0882 14.2016 15.115 14.0656 15.1415 13.9295C15.2309 14.0726 15.3664 14.259 15.5612 14.4364C15.8149 14.6675 16.3045 15 17.0001 15C17.6961 15 18.1857 14.6672 18.4393 14.4362C18.6879 14.2097 18.84 13.9684 18.9227 13.8216C19.091 13.5233 19.1969 13.2076 19.2632 12.9845C19.4049 12.5086 19.5136 11.9392 19.5941 11.4492C19.6775 10.9417 19.7428 10.4419 19.7869 10.0739C19.8091 9.88865 19.8263 9.73377 19.8381 9.62424C19.8439 9.56941 19.8485 9.52579 19.8516 9.49523L19.8552 9.45935L19.8562 9.44916L19.8565 9.44607L19.8566 9.44504L19.8567 9.44466C19.8567 9.44451 19.8567 9.44437 18.148 9.27752L19.8567 9.44436L19.993 8.0486L20.6548 7.70544C21.4812 7.27691 22 6.42355 22 5.49261C22 4.11598 20.884 3 19.5074 3H5ZM13.882 9H13.3052H12.0284H12.0161H11.9839H11.9716H10.6948H10.118C10.0563 9 10.0089 9.053 10.0191 9.11393C10.0387 9.2298 10.0726 9.4297 10.118 9.69352C10.1765 10.0329 10.2542 10.478 10.345 10.986L10.3475 11C10.5233 11.9833 10.7482 13.1998 10.9793 14.3415C11.1065 14.97 11.2356 15.5757 11.3594 16.1074C11.6172 17.2144 11.8522 18 12 18C12.1478 18 12.3828 17.2144 12.6406 16.1074C12.7644 15.5757 12.8935 14.97 13.0207 14.3415C13.2518 13.1998 13.4767 11.9833 13.6525 11L13.655 10.986C13.7458 10.478 13.8235 10.0329 13.882 9.69352C13.9274 9.4297 13.9613 9.2298 13.9809 9.11393C13.9911 9.053 13.9437 9 13.882 9ZM13.882 7H17.4266C17.5868 7 17.7447 6.9615 17.8869 6.88775L19.7341 5.92993C19.8975 5.84524 20 5.67659 20 5.49261C20 5.22055 19.7794 5 19.5074 5H5C4.44772 5 4 5.44772 4 6V8.13641C4 8.94759 4.91499 9.42124 5.57733 8.95292L7.5608 7.55047C8.06733 7.19232 8.67243 7 9.29279 7H10.118H13.882ZM10.3283 16.986C10.3283 16.9859 10.3294 16.9831 10.3318 16.9779C10.3295 16.9835 10.3283 16.9861 10.3283 16.986ZM13.6712 16.9848C13.671 16.9845 13.6709 16.9842 13.6707 16.9838L13.6682 16.9779C13.6706 16.9831 13.6717 16.9859 13.6717 16.986C13.6717 16.986 13.6715 16.9857 13.6712 16.9848ZM16.4005 11.25C16.5529 12.144 16.7612 13 17.0001 13C17.2389 13 17.4472 12.144 17.5997 11.25C17.6079 11.2017 17.616 11.1534 17.6239 11.105C17.7759 10.1742 17.8661 9.25 17.8661 9.25H16.1341C16.1341 9.25 16.2243 10.1743 16.3763 11.1051C16.3842 11.1534 16.3923 11.2018 16.4005 11.25Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-light.svg
new file mode 100644
index 0000000000..98a58847d9
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-light.svg
@@ -0,0 +1,10 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21.7707 19.0711C21.2067 15.8254 18.2307 13.334 14.6667 13.334C12.0507 13.334 9.66269 14.6826 8.40269 16.8311C5.17469 17.2311 2.66669 19.894 2.66669 23.0483C2.66669 26.5111 5.63069 29.334 9.26669 29.334H21.2667C24.2427 29.334 26.6667 27.0254 26.6667 24.1911C26.6667 21.5168 24.5187 19.3111 21.7707 19.0711ZM21.2667 27.0483H9.26669C6.95069 27.0483 5.06669 25.254 5.06669 23.0483C5.06669 21.0597 6.63869 19.3568 8.71469 19.094L9.90269 18.9454L10.4907 17.9511C11.3427 16.5111 12.9387 15.6197 14.6667 15.6197C16.9947 15.6197 19.0227 17.2654 19.4067 19.4483L19.7067 21.1854L21.5547 21.3454C23.0787 21.4826 24.2667 22.7283 24.2667 24.1911C24.2667 25.7683 22.9227 27.0483 21.2667 27.0483Z" fill="#191919"/>
+<path d="M20 5.81172C19.3975 5.81172 18.9091 5.31053 18.9091 4.69229V2.45342C18.9091 1.83517 19.3975 1.33398 20 1.33398C20.6025 1.33398 21.0909 1.83517 21.0909 2.45342V4.69229C21.0909 5.31053 20.6025 5.81172 20 5.81172Z" fill="#191919"/>
+<path d="M26.9186 8.11547C26.5016 8.54332 25.8256 8.54332 25.4087 8.11547C24.9956 7.69161 24.9912 7.00581 25.3988 6.57638L26.9462 4.94596C27.3698 4.49967 28.0682 4.49811 28.4936 4.9425C28.9099 5.37731 28.9069 6.07516 28.4869 6.50618L26.9186 8.11547Z" fill="#191919"/>
+<path d="M28.7273 14.7672C28.1248 14.7672 27.6364 14.266 27.6364 13.6478C27.6364 13.0295 28.1248 12.5283 28.7273 12.5283H30.9091C31.5116 12.5283 32 13.0295 32 13.6478C32 14.266 31.5116 14.7672 30.9091 14.7672H28.7273Z" fill="#191919"/>
+<path d="M14.581 8.09735C14.166 8.508 13.5083 8.50637 13.0952 8.09365L11.5201 6.51989C11.0851 6.08528 11.0835 5.36861 11.5166 4.932C11.9403 4.50484 12.6203 4.50793 13.0403 4.93892L14.5949 6.5341C15.0172 6.96743 15.0109 7.67196 14.581 8.09735Z" fill="#191919"/>
+<path d="M28.4815 22.3232C28.0494 22.7822 27.3326 22.782 26.9007 22.3228L25.3869 20.7135C24.9854 20.2867 24.9913 19.6084 25.4001 19.1889C25.8088 18.7695 26.4697 18.7634 26.8857 19.1753L28.4542 20.7284C28.8911 21.161 28.9034 21.8752 28.4815 22.3232Z" fill="#191919"/>
+<path d="M9.09091 14.7672C8.48842 14.7672 8 14.266 8 13.6478C8 13.0295 8.48842 12.5283 9.09091 12.5283H11.2727C11.8752 12.5283 12.3636 13.0295 12.3636 13.6478C12.3636 13.786 12.3392 13.9185 12.2945 14.0407C11.7584 14.224 11.2481 14.4683 10.7739 14.7672H9.09091Z" fill="#191919"/>
+<path d="M13.4553 13.7479C13.4548 13.7146 13.4545 13.6812 13.4545 13.6478C13.4545 9.93828 16.385 6.93116 20 6.93116C23.615 6.93116 26.5455 9.93828 26.5455 13.6478C26.5455 16.3328 25.0101 18.6498 22.7911 19.7248C22.4629 19.6308 22.1217 19.5664 21.7707 19.5349C21.6757 18.9741 21.5123 18.4352 21.2893 17.9268C23.0699 17.3626 24.3636 15.6602 24.3636 13.6478C24.3636 11.1748 22.41 9.17003 20 9.17003C17.59 9.17003 15.6364 11.1748 15.6364 13.6478C15.6364 13.6691 15.6365 13.6904 15.6368 13.7117C15.3194 13.6695 14.9955 13.6478 14.6667 13.6478C14.2561 13.6478 13.8511 13.6819 13.4553 13.7479Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-overcast-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-overcast-dark.svg
new file mode 100644
index 0000000000..0f372b1e97
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-overcast-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.92 13.02C17.45 10.18 14.97 8 12 8C9.82 8 7.83 9.18 6.78 11.06C4.09 11.41 2 13.74 2 16.5C2 19.53 4.47 22 7.5 22H17.5C19.98 22 22 19.98 22 17.5C22 15.16 20.21 13.23 17.92 13.02ZM17.5 20H7.5C5.57 20 4 18.43 4 16.5C4 14.76 5.31 13.27 7.04 13.04L8.03 12.91L8.52 12.04C9.23 10.78 10.56 10 12 10C13.94 10 15.63 11.44 15.95 13.35L16.2 14.87L17.74 15.01C19.01 15.13 20 16.22 20 17.5C20 18.88 18.88 20 17.5 20Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5823 6.61164C11.3358 5.63698 12.541 5 13.9086 5C16.2261 5 18.0551 6.81787 18.0551 9H19.0551C20.1596 9 21.0551 9.89543 21.0551 11C21.0551 11.5481 20.8359 12.0431 20.4781 12.4054L21.901 13.8108C22.6135 13.0895 23.0551 12.0953 23.0551 11C23.0551 9.01958 21.6158 7.37531 19.7262 7.05607C18.8958 4.68436 16.5884 3 13.9086 3C11.9088 3 10.1243 3.93413 9 5.38836L10.5823 6.61164Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-overcast-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-overcast-light.svg
new file mode 100644
index 0000000000..d5d696e7cf
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-overcast-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.92 13.02C17.45 10.18 14.97 8 12 8C9.82 8 7.83 9.18 6.78 11.06C4.09 11.41 2 13.74 2 16.5C2 19.53 4.47 22 7.5 22H17.5C19.98 22 22 19.98 22 17.5C22 15.16 20.21 13.23 17.92 13.02ZM17.5 20H7.5C5.57 20 4 18.43 4 16.5C4 14.76 5.31 13.27 7.04 13.04L8.03 12.91L8.52 12.04C9.23 10.78 10.56 10 12 10C13.94 10 15.63 11.44 15.95 13.35L16.2 14.87L17.74 15.01C19.01 15.13 20 16.22 20 17.5C20 18.88 18.88 20 17.5 20Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5823 6.61164C11.3358 5.63698 12.541 5 13.9086 5C16.2261 5 18.0551 6.81787 18.0551 9H19.0551C20.1596 9 21.0551 9.89543 21.0551 11C21.0551 11.5481 20.8359 12.0431 20.4781 12.4054L21.901 13.8108C22.6135 13.0895 23.0551 12.0953 23.0551 11C23.0551 9.01958 21.6158 7.37531 19.7262 7.05607C18.8958 4.68436 16.5884 3 13.9086 3C11.9088 3 10.1243 3.93413 9 5.38836L10.5823 6.61164Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-showers-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-showers-dark.svg
new file mode 100644
index 0000000000..3f7935f4d6
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-showers-dark.svg
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g opacity="0.95">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.50004 16.134C7.97834 15.8578 8.58993 16.0217 8.86607 16.5L10.3661 19.0981C10.6422 19.5764 10.4783 20.188 10 20.4641C9.52175 20.7403 8.91016 20.5764 8.63402 20.0981L7.13402 17.5C6.85788 17.0217 7.02175 16.4101 7.50004 16.134ZM11.5 16.134C11.9783 15.8578 12.5899 16.0217 12.8661 16.5L15.3661 20.8301C15.6422 21.3084 15.4783 21.92 15 22.1962C14.5218 22.4723 13.9102 22.3084 13.634 21.8301L11.134 17.5C10.8579 17.0217 11.0218 16.4101 11.5 16.134ZM16.8661 16.5C16.5899 16.0217 15.9783 15.8578 15.5 16.134C15.0218 16.4101 14.8579 17.0217 15.134 17.5L16.634 20.0981C16.9102 20.5764 17.5218 20.7403 18 20.4641C18.4783 20.188 18.6422 19.5764 18.3661 19.0981L16.8661 16.5Z" fill="#E6E6E6"/>
+<path d="M17.328 7.30286C16.905 4.86857 14.673 3 12 3C10.038 3 8.247 4.01143 7.302 5.62286C4.881 5.92286 3 7.92 3 10.2857C3 12.8829 5.223 15 7.95 15H16.95C19.182 15 21 13.2686 21 11.1429C21 9.13714 19.389 7.48286 17.328 7.30286ZM16.95 13.2857H7.95C6.213 13.2857 4.8 11.94 4.8 10.2857C4.8 8.79429 5.979 7.51714 7.536 7.32L8.427 7.20857L8.868 6.46286C9.507 5.38286 10.704 4.71429 12 4.71429C13.746 4.71429 15.267 5.94857 15.555 7.58571L15.78 8.88857L17.166 9.00857C18.309 9.11143 19.2 10.0457 19.2 11.1429C19.2 12.3257 18.192 13.2857 16.95 13.2857Z" fill="#E6E6E6"/>
+</g>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-showers-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-showers-light.svg
new file mode 100644
index 0000000000..5e7a5b3393
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-showers-light.svg
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g opacity="0.95">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.50004 16.134C7.97834 15.8578 8.58993 16.0217 8.86607 16.5L10.3661 19.0981C10.6422 19.5764 10.4783 20.188 10 20.4641C9.52175 20.7403 8.91016 20.5764 8.63402 20.0981L7.13402 17.5C6.85788 17.0217 7.02175 16.4101 7.50004 16.134ZM11.5 16.134C11.9783 15.8578 12.5899 16.0217 12.8661 16.5L15.3661 20.8301C15.6422 21.3084 15.4783 21.92 15 22.1962C14.5218 22.4723 13.9102 22.3084 13.634 21.8301L11.134 17.5C10.8579 17.0217 11.0218 16.4101 11.5 16.134ZM16.8661 16.5C16.5899 16.0217 15.9783 15.8578 15.5 16.134C15.0218 16.4101 14.8579 17.0217 15.134 17.5L16.634 20.0981C16.9102 20.5764 17.5218 20.7403 18 20.4641C18.4783 20.188 18.6422 19.5764 18.3661 19.0981L16.8661 16.5Z" fill="#191919"/>
+<path d="M17.328 7.30286C16.905 4.86857 14.673 3 12 3C10.038 3 8.247 4.01143 7.302 5.62286C4.881 5.92286 3 7.92 3 10.2857C3 12.8829 5.223 15 7.95 15H16.95C19.182 15 21 13.2686 21 11.1429C21 9.13714 19.389 7.48286 17.328 7.30286ZM16.95 13.2857H7.95C6.213 13.2857 4.8 11.94 4.8 10.2857C4.8 8.79429 5.979 7.51714 7.536 7.32L8.427 7.20857L8.868 6.46286C9.507 5.38286 10.704 4.71429 12 4.71429C13.746 4.71429 15.267 5.94857 15.555 7.58571L15.78 8.88857L17.166 9.00857C18.309 9.11143 19.2 10.0457 19.2 11.1429C19.2 12.3257 18.192 13.2857 16.95 13.2857Z" fill="#191919"/>
+</g>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-showers-scattered-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-showers-scattered-dark.svg
new file mode 100644
index 0000000000..35e947d9d6
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-showers-scattered-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 19.5C7.08333 19.5 6.72917 19.3583 6.4375 19.075C6.14583 18.7917 6 18.4417 6 18.025C6 17.825 6.0375 17.6333 6.1125 17.45C6.1875 17.2667 6.3 17.1083 6.45 16.975L7.5 16L8.55 16.975C8.7 17.1083 8.8125 17.2667 8.8875 17.45C8.9625 17.6333 9 17.825 9 18.025C9 18.4417 8.85417 18.7917 8.5625 19.075C8.27083 19.3583 7.91667 19.5 7.5 19.5ZM16.4375 19.075C16.7292 19.3583 17.0833 19.5 17.5 19.5C17.9167 19.5 18.2708 19.3583 18.5625 19.075C18.8542 18.7917 19 18.4417 19 18.025C19 17.825 18.9625 17.6333 18.8875 17.45C18.8125 17.2667 18.7 17.1083 18.55 16.975L17.5 16L16.45 16.975C16.3 17.1083 16.1875 17.2667 16.1125 17.45C16.0375 17.6333 16 17.825 16 18.025C16 18.4417 16.1458 18.7917 16.4375 19.075ZM11.4375 20.075C11.7292 20.3583 12.0833 20.5 12.5 20.5C12.9167 20.5 13.2708 20.3583 13.5625 20.075C13.8542 19.7917 14 19.4417 14 19.025C14 18.825 13.9625 18.6333 13.8875 18.45C13.8125 18.2667 13.7 18.1083 13.55 17.975L12.5 17L11.45 17.975C11.3 18.1083 11.1875 18.2667 11.1125 18.45C11.0375 18.6333 11 18.825 11 19.025C11 19.4417 11.1458 19.7917 11.4375 20.075Z" fill="#E6E6E6"/>
+<path d="M17.328 7.30286C16.905 4.86857 14.673 3 12 3C10.038 3 8.247 4.01143 7.302 5.62286C4.881 5.92286 3 7.92 3 10.2857C3 12.8829 5.223 15 7.95 15H16.95C19.182 15 21 13.2686 21 11.1429C21 9.13714 19.389 7.48286 17.328 7.30286ZM16.95 13.2857H7.95C6.213 13.2857 4.8 11.94 4.8 10.2857C4.8 8.79429 5.979 7.51714 7.536 7.32L8.427 7.20857L8.868 6.46286C9.507 5.38286 10.704 4.71429 12 4.71429C13.746 4.71429 15.267 5.94857 15.555 7.58571L15.78 8.88857L17.166 9.00857C18.309 9.11143 19.2 10.0457 19.2 11.1429C19.2 12.3257 18.192 13.2857 16.95 13.2857Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-showers-scattered-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-showers-scattered-light.svg
new file mode 100644
index 0000000000..2d7e68ce9a
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-showers-scattered-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 19.5C7.08333 19.5 6.72917 19.3583 6.4375 19.075C6.14583 18.7917 6 18.4417 6 18.025C6 17.825 6.0375 17.6333 6.1125 17.45C6.1875 17.2667 6.3 17.1083 6.45 16.975L7.5 16L8.55 16.975C8.7 17.1083 8.8125 17.2667 8.8875 17.45C8.9625 17.6333 9 17.825 9 18.025C9 18.4417 8.85417 18.7917 8.5625 19.075C8.27083 19.3583 7.91667 19.5 7.5 19.5ZM16.4375 19.075C16.7292 19.3583 17.0833 19.5 17.5 19.5C17.9167 19.5 18.2708 19.3583 18.5625 19.075C18.8542 18.7917 19 18.4417 19 18.025C19 17.825 18.9625 17.6333 18.8875 17.45C18.8125 17.2667 18.7 17.1083 18.55 16.975L17.5 16L16.45 16.975C16.3 17.1083 16.1875 17.2667 16.1125 17.45C16.0375 17.6333 16 17.825 16 18.025C16 18.4417 16.1458 18.7917 16.4375 19.075ZM11.4375 20.075C11.7292 20.3583 12.0833 20.5 12.5 20.5C12.9167 20.5 13.2708 20.3583 13.5625 20.075C13.8542 19.7917 14 19.4417 14 19.025C14 18.825 13.9625 18.6333 13.8875 18.45C13.8125 18.2667 13.7 18.1083 13.55 17.975L12.5 17L11.45 17.975C11.3 18.1083 11.1875 18.2667 11.1125 18.45C11.0375 18.6333 11 18.825 11 19.025C11 19.4417 11.1458 19.7917 11.4375 20.075Z" fill="#191919"/>
+<path d="M17.328 7.30286C16.905 4.86857 14.673 3 12 3C10.038 3 8.247 4.01143 7.302 5.62286C4.881 5.92286 3 7.92 3 10.2857C3 12.8829 5.223 15 7.95 15H16.95C19.182 15 21 13.2686 21 11.1429C21 9.13714 19.389 7.48286 17.328 7.30286ZM16.95 13.2857H7.95C6.213 13.2857 4.8 11.94 4.8 10.2857C4.8 8.79429 5.979 7.51714 7.536 7.32L8.427 7.20857L8.868 6.46286C9.507 5.38286 10.704 4.71429 12 4.71429C13.746 4.71429 15.267 5.94857 15.555 7.58571L15.78 8.88857L17.166 9.00857C18.309 9.11143 19.2 10.0457 19.2 11.1429C19.2 12.3257 18.192 13.2857 16.95 13.2857Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-sleet-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-sleet-dark.svg
new file mode 100644
index 0000000000..b5de2d1189
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-sleet-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.41421 16L5 17.4142L6.41421 18.8284L7.82843 17.4142L6.41421 16ZM11.866 16.5002C11.5899 16.0219 10.9783 15.858 10.5 16.1341C10.0217 16.4103 9.85785 17.0219 10.134 17.5002L12.634 21.8303C12.9101 22.3086 13.5217 22.4725 14 22.1963C14.4783 21.9202 14.6422 21.3086 14.366 20.8303L11.866 16.5002ZM7 20.4142L8.41421 19L9.82843 20.4142L8.41421 21.8284L7 20.4142ZM16.5 19.5C16.0833 19.5 15.7292 19.3583 15.4375 19.075C15.1458 18.7917 15 18.4417 15 18.025C15 17.825 15.0375 17.6333 15.1125 17.45C15.1875 17.2667 15.3 17.1083 15.45 16.975L16.5 16L17.55 16.975C17.7 17.1083 17.8125 17.2667 17.8875 17.45C17.9625 17.6333 18 17.825 18 18.025C18 18.4417 17.8542 18.7917 17.5625 19.075C17.2708 19.3583 16.9167 19.5 16.5 19.5Z" fill="#E6E6E6"/>
+<path d="M17.328 7.30286C16.905 4.86857 14.673 3 12 3C10.038 3 8.247 4.01143 7.302 5.62286C4.881 5.92286 3 7.92 3 10.2857C3 12.8829 5.223 15 7.95 15H16.95C19.182 15 21 13.2686 21 11.1429C21 9.13714 19.389 7.48286 17.328 7.30286ZM16.95 13.2857H7.95C6.213 13.2857 4.8 11.94 4.8 10.2857C4.8 8.79429 5.979 7.51714 7.536 7.32L8.427 7.20857L8.868 6.46286C9.507 5.38286 10.704 4.71429 12 4.71429C13.746 4.71429 15.267 5.94857 15.555 7.58571L15.78 8.88857L17.166 9.00857C18.309 9.11143 19.2 10.0457 19.2 11.1429C19.2 12.3257 18.192 13.2857 16.95 13.2857Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-sleet-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-sleet-light.svg
new file mode 100644
index 0000000000..a22fcf10af
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-sleet-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.41421 16L5 17.4142L6.41421 18.8284L7.82843 17.4142L6.41421 16ZM11.866 16.5002C11.5899 16.0219 10.9783 15.858 10.5 16.1341C10.0217 16.4103 9.85785 17.0219 10.134 17.5002L12.634 21.8303C12.9101 22.3086 13.5217 22.4725 14 22.1963C14.4783 21.9202 14.6422 21.3086 14.366 20.8303L11.866 16.5002ZM7 20.4142L8.41421 19L9.82843 20.4142L8.41421 21.8284L7 20.4142ZM16.5 19.5C16.0833 19.5 15.7292 19.3583 15.4375 19.075C15.1458 18.7917 15 18.4417 15 18.025C15 17.825 15.0375 17.6333 15.1125 17.45C15.1875 17.2667 15.3 17.1083 15.45 16.975L16.5 16L17.55 16.975C17.7 17.1083 17.8125 17.2667 17.8875 17.45C17.9625 17.6333 18 17.825 18 18.025C18 18.4417 17.8542 18.7917 17.5625 19.075C17.2708 19.3583 16.9167 19.5 16.5 19.5Z" fill="#191919"/>
+<path d="M17.328 7.30286C16.905 4.86857 14.673 3 12 3C10.038 3 8.247 4.01143 7.302 5.62286C4.881 5.92286 3 7.92 3 10.2857C3 12.8829 5.223 15 7.95 15H16.95C19.182 15 21 13.2686 21 11.1429C21 9.13714 19.389 7.48286 17.328 7.30286ZM16.95 13.2857H7.95C6.213 13.2857 4.8 11.94 4.8 10.2857C4.8 8.79429 5.979 7.51714 7.536 7.32L8.427 7.20857L8.868 6.46286C9.507 5.38286 10.704 4.71429 12 4.71429C13.746 4.71429 15.267 5.94857 15.555 7.58571L15.78 8.88857L17.166 9.00857C18.309 9.11143 19.2 10.0457 19.2 11.1429C19.2 12.3257 18.192 13.2857 16.95 13.2857Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-snow-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-snow-dark.svg
new file mode 100644
index 0000000000..10eff6522b
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-snow-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5 17.4142L6.41421 16L7.82843 17.4142L6.41421 18.8284L5 17.4142ZM7 20.4142L8.41421 19L9.82843 20.4142L8.41421 21.8284L7 20.4142ZM15.4142 16L14 17.4142L15.4142 18.8284L16.8284 17.4142L15.4142 16ZM11 16.5C11.2761 16.5 11.5 16.7239 11.5 17V17.5H12C12.2761 17.5 12.5 17.7239 12.5 18C12.5 18.2761 12.2761 18.5 12 18.5H11.5V19C11.5 19.2761 11.2761 19.5 11 19.5C10.7239 19.5 10.5 19.2761 10.5 19V18.5H10C9.72386 18.5 9.5 18.2761 9.5 18C9.5 17.7239 9.72386 17.5 10 17.5H10.5V17C10.5 16.7239 10.7239 16.5 11 16.5ZM18 18.5C18.2761 18.5 18.5 18.7239 18.5 19V19.5H19C19.2761 19.5 19.5 19.7239 19.5 20C19.5 20.2761 19.2761 20.5 19 20.5H18.5V21C18.5 21.2761 18.2761 21.5 18 21.5C17.7239 21.5 17.5 21.2761 17.5 21V20.5H17C16.7239 20.5 16.5 20.2761 16.5 20C16.5 19.7239 16.7239 19.5 17 19.5H17.5V19C17.5 18.7239 17.7239 18.5 18 18.5Z" fill="#E6E6E6"/>
+<path d="M17.328 7.30286C16.905 4.86857 14.673 3 12 3C10.038 3 8.247 4.01143 7.302 5.62286C4.881 5.92286 3 7.92 3 10.2857C3 12.8829 5.223 15 7.95 15H16.95C19.182 15 21 13.2686 21 11.1429C21 9.13714 19.389 7.48286 17.328 7.30286ZM16.95 13.2857H7.95C6.213 13.2857 4.8 11.94 4.8 10.2857C4.8 8.79429 5.979 7.51714 7.536 7.32L8.427 7.20857L8.868 6.46286C9.507 5.38286 10.704 4.71429 12 4.71429C13.746 4.71429 15.267 5.94857 15.555 7.58571L15.78 8.88857L17.166 9.00857C18.309 9.11143 19.2 10.0457 19.2 11.1429C19.2 12.3257 18.192 13.2857 16.95 13.2857Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-snow-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-snow-light.svg
new file mode 100644
index 0000000000..2737265160
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-snow-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5 17.4142L6.41421 16L7.82843 17.4142L6.41421 18.8284L5 17.4142ZM7 20.4142L8.41421 19L9.82843 20.4142L8.41421 21.8284L7 20.4142ZM15.4142 16L14 17.4142L15.4142 18.8284L16.8284 17.4142L15.4142 16ZM11 16.5C11.2761 16.5 11.5 16.7239 11.5 17V17.5H12C12.2761 17.5 12.5 17.7239 12.5 18C12.5 18.2761 12.2761 18.5 12 18.5H11.5V19C11.5 19.2761 11.2761 19.5 11 19.5C10.7239 19.5 10.5 19.2761 10.5 19V18.5H10C9.72386 18.5 9.5 18.2761 9.5 18C9.5 17.7239 9.72386 17.5 10 17.5H10.5V17C10.5 16.7239 10.7239 16.5 11 16.5ZM18 18.5C18.2761 18.5 18.5 18.7239 18.5 19V19.5H19C19.2761 19.5 19.5 19.7239 19.5 20C19.5 20.2761 19.2761 20.5 19 20.5H18.5V21C18.5 21.2761 18.2761 21.5 18 21.5C17.7239 21.5 17.5 21.2761 17.5 21V20.5H17C16.7239 20.5 16.5 20.2761 16.5 20C16.5 19.7239 16.7239 19.5 17 19.5H17.5V19C17.5 18.7239 17.7239 18.5 18 18.5Z" fill="#191919"/>
+<path d="M17.328 7.30286C16.905 4.86857 14.673 3 12 3C10.038 3 8.247 4.01143 7.302 5.62286C4.881 5.92286 3 7.92 3 10.2857C3 12.8829 5.223 15 7.95 15H16.95C19.182 15 21 13.2686 21 11.1429C21 9.13714 19.389 7.48286 17.328 7.30286ZM16.95 13.2857H7.95C6.213 13.2857 4.8 11.94 4.8 10.2857C4.8 8.79429 5.979 7.51714 7.536 7.32L8.427 7.20857L8.868 6.46286C9.507 5.38286 10.704 4.71429 12 4.71429C13.746 4.71429 15.267 5.94857 15.555 7.58571L15.78 8.88857L17.166 9.00857C18.309 9.11143 19.2 10.0457 19.2 11.1429C19.2 12.3257 18.192 13.2857 16.95 13.2857Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-storm-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-storm-dark.svg
new file mode 100644
index 0000000000..52a5c3fc2a
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-storm-dark.svg
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g opacity="0.95">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1821 16.0299C13.2177 16.0548 13.2453 16.0934 13.2603 16.1395C13.2753 16.1855 13.2768 16.2364 13.2647 16.2837L12.6098 18.8438H13.8181C13.8536 18.8438 13.8884 18.8563 13.918 18.8797C13.9477 18.9032 13.971 18.9366 13.9851 18.9758C13.9992 19.015 14.0034 19.0584 13.9972 19.1004C13.9911 19.1425 13.9748 19.1815 13.9505 19.2126L11.0415 22.9311C11.0133 22.9671 10.9759 22.9906 10.9352 22.9977C10.8946 23.0048 10.8531 22.9952 10.8176 22.9703C10.7821 22.9455 10.7546 22.9069 10.7397 22.8608C10.7247 22.8148 10.7232 22.764 10.7353 22.7167L11.3902 20.1562H10.1819C10.1464 20.1562 10.1116 20.1437 10.082 20.1203C10.0523 20.0968 10.029 20.0634 10.0149 20.0242C10.0008 19.985 9.9966 19.9416 10.0028 19.8996C10.0089 19.8575 10.0252 19.8185 10.0495 19.7874L12.9585 16.0689C12.9866 16.0329 13.024 16.0095 13.0646 16.0023C13.1052 15.9952 13.1466 16.0047 13.1821 16.0295V16.0299ZM15.4375 19.075C15.7292 19.3583 16.0833 19.5 16.5 19.5C16.9167 19.5 17.2708 19.3583 17.5625 19.075C17.8542 18.7917 18 18.4417 18 18.025C18 17.825 17.9625 17.6333 17.8875 17.45C17.8125 17.2667 17.7 17.1083 17.55 16.975L16.5 16L15.45 16.975C15.3 17.1083 15.1875 17.2667 15.1125 17.45C15.0375 17.6333 15 17.825 15 18.025C15 18.4417 15.1458 18.7917 15.4375 19.075ZM7.5 19.5C7.08333 19.5 6.72917 19.3583 6.4375 19.075C6.14583 18.7917 6 18.4417 6 18.025C6 17.825 6.0375 17.6333 6.1125 17.45C6.1875 17.2667 6.3 17.1083 6.45 16.975L7.5 16L8.55 16.975C8.7 17.1083 8.8125 17.2667 8.8875 17.45C8.9625 17.6333 9 17.825 9 18.025C9 18.4417 8.85417 18.7917 8.5625 19.075C8.27083 19.3583 7.91667 19.5 7.5 19.5Z" fill="#E6E6E6"/>
+<path d="M17.92 6.02C17.45 3.18 14.97 1 12 1C9.82 1 7.83 2.18 6.78 4.06C4.09 4.41 2 6.74 2 9.5C2 12.53 4.47 15 7.5 15H17.5C19.98 15 22 12.98 22 10.5C22 8.16 20.21 6.23 17.92 6.02ZM17.5 13H7.5C5.57 13 4 11.43 4 9.5C4 7.76 5.31 6.27 7.04 6.04L8.03 5.91L8.52 5.04C9.23 3.78 10.56 3 12 3C13.94 3 15.63 4.44 15.95 6.35L16.2 7.87L17.74 8.01C19.01 8.13 20 9.22 20 10.5C20 11.88 18.88 13 17.5 13Z" fill="#E6E6E6"/>
+</g>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-storm-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-storm-light.svg
new file mode 100644
index 0000000000..a0d4bde293
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-storm-light.svg
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g opacity="0.95">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1821 16.0299C13.2177 16.0548 13.2453 16.0934 13.2603 16.1395C13.2753 16.1855 13.2768 16.2364 13.2647 16.2837L12.6098 18.8438H13.8181C13.8536 18.8438 13.8884 18.8563 13.918 18.8797C13.9477 18.9032 13.971 18.9366 13.9851 18.9758C13.9992 19.015 14.0034 19.0584 13.9972 19.1004C13.9911 19.1425 13.9748 19.1815 13.9505 19.2126L11.0415 22.9311C11.0133 22.9671 10.9759 22.9906 10.9352 22.9977C10.8946 23.0048 10.8531 22.9952 10.8176 22.9703C10.7821 22.9455 10.7546 22.9069 10.7397 22.8608C10.7247 22.8148 10.7232 22.764 10.7353 22.7167L11.3902 20.1562H10.1819C10.1464 20.1562 10.1116 20.1437 10.082 20.1203C10.0523 20.0968 10.029 20.0634 10.0149 20.0242C10.0008 19.985 9.9966 19.9416 10.0028 19.8996C10.0089 19.8575 10.0252 19.8185 10.0495 19.7874L12.9585 16.0689C12.9866 16.0329 13.024 16.0095 13.0646 16.0023C13.1052 15.9952 13.1466 16.0047 13.1821 16.0295V16.0299ZM15.4375 19.075C15.7292 19.3583 16.0833 19.5 16.5 19.5C16.9167 19.5 17.2708 19.3583 17.5625 19.075C17.8542 18.7917 18 18.4417 18 18.025C18 17.825 17.9625 17.6333 17.8875 17.45C17.8125 17.2667 17.7 17.1083 17.55 16.975L16.5 16L15.45 16.975C15.3 17.1083 15.1875 17.2667 15.1125 17.45C15.0375 17.6333 15 17.825 15 18.025C15 18.4417 15.1458 18.7917 15.4375 19.075ZM7.5 19.5C7.08333 19.5 6.72917 19.3583 6.4375 19.075C6.14583 18.7917 6 18.4417 6 18.025C6 17.825 6.0375 17.6333 6.1125 17.45C6.1875 17.2667 6.3 17.1083 6.45 16.975L7.5 16L8.55 16.975C8.7 17.1083 8.8125 17.2667 8.8875 17.45C8.9625 17.6333 9 17.825 9 18.025C9 18.4417 8.85417 18.7917 8.5625 19.075C8.27083 19.3583 7.91667 19.5 7.5 19.5Z" fill="#191919"/>
+<path d="M17.92 6.02C17.45 3.18 14.97 1 12 1C9.82 1 7.83 2.18 6.78 4.06C4.09 4.41 2 6.74 2 9.5C2 12.53 4.47 15 7.5 15H17.5C19.98 15 22 12.98 22 10.5C22 8.16 20.21 6.23 17.92 6.02ZM17.5 13H7.5C5.57 13 4 11.43 4 9.5C4 7.76 5.31 6.27 7.04 6.04L8.03 5.91L8.52 5.04C9.23 3.78 10.56 3 12 3C13.94 3 15.63 4.44 15.95 6.35L16.2 7.87L17.74 8.01C19.01 8.13 20 9.22 20 10.5C20 11.88 18.88 13 17.5 13Z" fill="#191919"/>
+</g>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-dark.svg
new file mode 100644
index 0000000000..18b469918e
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16ZM12 18C15.3137 18 18 15.3137 18 12C18 8.68629 15.3137 6 12 6C8.68629 6 6 8.68629 6 12C6 15.3137 8.68629 18 12 18Z" fill="#E6E6E6"/>
+<path d="M12 5C11.4477 5 11 4.55228 11 4V2C11 1.44772 11.4477 1 12 1C12.5523 1 13 1.44772 13 2V4C13 4.55228 12.5523 5 12 5ZM18.342 7.05796C17.9598 7.44016 17.3402 7.44016 16.958 7.05796C16.5793 6.67932 16.5753 6.06669 16.9489 5.68308L18.3673 4.22661C18.7556 3.82793 19.3958 3.82654 19.7858 4.22351C20.1674 4.61193 20.1647 5.23533 19.7796 5.62036L18.342 7.05796ZM20 13C19.4477 13 19 12.5523 19 12C19 11.4477 19.4477 11 20 11H22C22.5523 11 23 11.4477 23 12C23 12.5523 22.5523 13 22 13H20ZM12 23C11.4477 23 11 22.5523 11 22V20C11 19.4477 11.4477 19 12 19C12.5523 19 13 19.4477 13 20V22C13 22.5523 12.5523 23 12 23ZM7.03261 7.04177C6.65218 7.40861 6.04923 7.40715 5.67059 7.03847L4.22673 5.63261C3.828 5.24437 3.82657 4.60416 4.22355 4.21414C4.61195 3.83256 5.23531 3.83531 5.62032 4.22032L7.0453 5.6453C7.43241 6.03241 7.42669 6.66177 7.03261 7.04177ZM19.7747 19.7498C19.3786 20.1598 18.7215 20.1597 18.3256 19.7495L16.938 18.3119C16.57 17.9306 16.5753 17.3247 16.9501 16.9499C17.3247 16.5753 17.9306 16.5698 18.3119 16.9378L19.7497 18.3252C20.1502 18.7116 20.1614 19.3496 19.7747 19.7498ZM2 13C1.44772 13 1 12.5523 1 12C1 11.4477 1.44772 11 2 11H4C4.55228 11 5 11.4477 5 12C5 12.5523 4.55228 13 4 13H2ZM5.63753 19.7875C5.24503 20.18 4.60781 20.1771 4.21881 19.7812C3.83464 19.3902 3.83742 18.7626 4.22503 18.375L5.62456 16.9754C6.01538 16.5846 6.64547 16.5734 7.05 16.95C7.44833 17.3346 7.45392 17.9711 7.06239 18.3626L5.63753 19.7875Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-light.svg
new file mode 100644
index 0000000000..227c104e31
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16ZM12 18C15.3137 18 18 15.3137 18 12C18 8.68629 15.3137 6 12 6C8.68629 6 6 8.68629 6 12C6 15.3137 8.68629 18 12 18Z" fill="#191919"/>
+<path d="M12 5C11.4477 5 11 4.55228 11 4V2C11 1.44772 11.4477 1 12 1C12.5523 1 13 1.44772 13 2V4C13 4.55228 12.5523 5 12 5ZM18.342 7.05796C17.9598 7.44016 17.3402 7.44016 16.958 7.05796C16.5793 6.67932 16.5753 6.06669 16.9489 5.68308L18.3673 4.22661C18.7556 3.82793 19.3958 3.82654 19.7858 4.22351C20.1674 4.61193 20.1647 5.23533 19.7796 5.62036L18.342 7.05796ZM20 13C19.4477 13 19 12.5523 19 12C19 11.4477 19.4477 11 20 11H22C22.5523 11 23 11.4477 23 12C23 12.5523 22.5523 13 22 13H20ZM12 23C11.4477 23 11 22.5523 11 22V20C11 19.4477 11.4477 19 12 19C12.5523 19 13 19.4477 13 20V22C13 22.5523 12.5523 23 12 23ZM7.03261 7.04177C6.65218 7.40861 6.04923 7.40715 5.67059 7.03847L4.22673 5.63261C3.828 5.24437 3.82657 4.60416 4.22355 4.21414C4.61195 3.83256 5.23531 3.83531 5.62032 4.22032L7.0453 5.6453C7.43241 6.03241 7.42669 6.66177 7.03261 7.04177ZM19.7747 19.7498C19.3786 20.1598 18.7215 20.1597 18.3256 19.7495L16.938 18.3119C16.57 17.9306 16.5753 17.3247 16.9501 16.9499C17.3247 16.5753 17.9306 16.5698 18.3119 16.9378L19.7497 18.3252C20.1502 18.7116 20.1614 19.3496 19.7747 19.7498ZM2 13C1.44772 13 1 12.5523 1 12C1 11.4477 1.44772 11 2 11H4C4.55228 11 5 11.4477 5 12C5 12.5523 4.55228 13 4 13H2ZM5.63753 19.7875C5.24503 20.18 4.60781 20.1771 4.21881 19.7812C3.83464 19.3902 3.83742 18.7626 4.22503 18.375L5.62456 16.9754C6.01538 16.5846 6.64547 16.5734 7.05 16.95C7.44833 17.3346 7.45392 17.9711 7.06239 18.3626L5.63753 19.7875Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-very-few-clouds-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-very-few-clouds-dark.svg
new file mode 100644
index 0000000000..94a428d68b
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-very-few-clouds-dark.svg
@@ -0,0 +1,10 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16.328 14.3029C15.905 11.8686 13.673 10 11 10C9.038 10 7.247 11.0114 6.302 12.6229C3.881 12.9229 2 14.92 2 17.2857C2 19.8829 4.223 22 6.95 22H15.95C18.182 22 20 20.2686 20 18.1429C20 16.1371 18.389 14.4829 16.328 14.3029ZM15.95 20.2857H6.95C5.213 20.2857 3.8 18.94 3.8 17.2857C3.8 15.7943 4.979 14.5171 6.536 14.32L7.427 14.2086L7.868 13.4629C8.507 12.3829 9.704 11.7143 11 11.7143C12.746 11.7143 14.267 12.9486 14.555 14.5857L14.78 15.8886L16.166 16.0086C17.309 16.1114 18.2 17.0457 18.2 18.1429C18.2 19.3257 17.192 20.2857 15.95 20.2857Z" fill="#E6E6E6"/>
+<path d="M15 4.3583C14.5481 4.3583 14.1818 3.98241 14.1818 3.51873V1.83958C14.1818 1.37589 14.5481 1 15 1C15.4519 1 15.8182 1.37589 15.8182 1.83958V3.51873C15.8182 3.98241 15.4519 4.3583 15 4.3583Z" fill="#E6E6E6"/>
+<path d="M20.1889 6.08612C19.8762 6.40701 19.3692 6.40701 19.0565 6.08611C18.7467 5.76822 18.7434 5.25387 19.0491 4.9318L20.2096 3.70898C20.5273 3.37426 21.0511 3.37309 21.3702 3.70638C21.6824 4.03249 21.6802 4.55588 21.3652 4.87914L20.1889 6.08612Z" fill="#E6E6E6"/>
+<path d="M21.5455 11.0749C21.0936 11.0749 20.7273 10.699 20.7273 10.2353C20.7273 9.77165 21.0936 9.39576 21.5455 9.39576H23.1818C23.6337 9.39576 24 9.77165 24 10.2353C24 10.699 23.6337 11.0749 23.1818 11.0749H21.5455Z" fill="#E6E6E6"/>
+<path d="M10.9358 6.07252C10.6245 6.38051 10.1312 6.37929 9.82139 6.06975L8.64006 4.88943C8.31382 4.56347 8.31265 4.02597 8.63745 3.69851C8.95523 3.37815 9.46526 3.38046 9.78026 3.7037L10.9462 4.90008C11.2629 5.22509 11.2582 5.75348 10.9358 6.07252Z" fill="#E6E6E6"/>
+<path d="M21.3611 16.7419C21.0371 17.0861 20.4994 17.086 20.1755 16.7416L19.0402 15.5346C18.7391 15.2145 18.7434 14.7058 19.05 14.3912C19.3566 14.0766 19.8523 14.0721 20.1643 14.381L21.3407 15.5458C21.6684 15.8702 21.6775 16.4059 21.3611 16.7419Z" fill="#E6E6E6"/>
+<path d="M6.81818 11.0749C6.36631 11.0749 6 10.699 6 10.2353C6 9.77165 6.36631 9.39576 6.81818 9.39576H8.45455C8.90641 9.39576 9.27273 9.77165 9.27273 10.2353C9.27273 10.339 9.2544 10.4384 9.22089 10.5301C8.81878 10.6675 8.43606 10.8507 8.08042 11.0749H6.81818Z" fill="#E6E6E6"/>
+<path d="M10.0914 10.3104C10.0911 10.2854 10.0909 10.2604 10.0909 10.2353C10.0909 7.45323 12.2888 5.19788 15 5.19788C17.7112 5.19788 19.9091 7.45323 19.9091 10.2353C19.9091 12.2491 18.7576 13.9869 17.0934 14.7931C16.8472 14.7226 16.5913 14.6743 16.328 14.6507C16.2568 14.2301 16.1343 13.8259 15.967 13.4446C17.3024 13.0214 18.2727 11.7447 18.2727 10.2353C18.2727 8.3806 16.8075 6.87703 15 6.87703C13.1925 6.87703 11.7273 8.3806 11.7273 10.2353C11.7273 10.2513 11.7274 10.2673 11.7276 10.2833C11.4895 10.2517 11.2466 10.2353 11 10.2353C10.6921 10.2353 10.3883 10.2609 10.0914 10.3104Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-very-few-clouds-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-very-few-clouds-light.svg
new file mode 100644
index 0000000000..e91d2cf75e
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-sunny-very-few-clouds-light.svg
@@ -0,0 +1,10 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16.328 14.3029C15.905 11.8686 13.673 10 11 10C9.038 10 7.247 11.0114 6.302 12.6229C3.881 12.9229 2 14.92 2 17.2857C2 19.8829 4.223 22 6.95 22H15.95C18.182 22 20 20.2686 20 18.1429C20 16.1371 18.389 14.4829 16.328 14.3029ZM15.95 20.2857H6.95C5.213 20.2857 3.8 18.94 3.8 17.2857C3.8 15.7943 4.979 14.5171 6.536 14.32L7.427 14.2086L7.868 13.4629C8.507 12.3829 9.704 11.7143 11 11.7143C12.746 11.7143 14.267 12.9486 14.555 14.5857L14.78 15.8886L16.166 16.0086C17.309 16.1114 18.2 17.0457 18.2 18.1429C18.2 19.3257 17.192 20.2857 15.95 20.2857Z" fill="#191919"/>
+<path d="M15 4.3583C14.5481 4.3583 14.1818 3.98241 14.1818 3.51873V1.83958C14.1818 1.37589 14.5481 1 15 1C15.4519 1 15.8182 1.37589 15.8182 1.83958V3.51873C15.8182 3.98241 15.4519 4.3583 15 4.3583Z" fill="#191919"/>
+<path d="M20.1889 6.08612C19.8762 6.40701 19.3692 6.40701 19.0565 6.08611C18.7467 5.76822 18.7434 5.25387 19.0491 4.9318L20.2096 3.70898C20.5273 3.37426 21.0511 3.37309 21.3702 3.70638C21.6824 4.03249 21.6802 4.55588 21.3652 4.87914L20.1889 6.08612Z" fill="#191919"/>
+<path d="M21.5455 11.0749C21.0936 11.0749 20.7273 10.699 20.7273 10.2353C20.7273 9.77165 21.0936 9.39576 21.5455 9.39576H23.1818C23.6337 9.39576 24 9.77165 24 10.2353C24 10.699 23.6337 11.0749 23.1818 11.0749H21.5455Z" fill="#191919"/>
+<path d="M10.9358 6.07252C10.6245 6.38051 10.1312 6.37929 9.82139 6.06975L8.64006 4.88943C8.31382 4.56347 8.31265 4.02597 8.63745 3.69851C8.95523 3.37815 9.46526 3.38046 9.78026 3.7037L10.9462 4.90008C11.2629 5.22509 11.2582 5.75348 10.9358 6.07252Z" fill="#191919"/>
+<path d="M21.3611 16.7419C21.0371 17.0861 20.4994 17.086 20.1755 16.7416L19.0402 15.5346C18.7391 15.2145 18.7434 14.7058 19.05 14.3912C19.3566 14.0766 19.8523 14.0721 20.1643 14.381L21.3407 15.5458C21.6684 15.8702 21.6775 16.4059 21.3611 16.7419Z" fill="#191919"/>
+<path d="M6.81818 11.0749C6.36631 11.0749 6 10.699 6 10.2353C6 9.77165 6.36631 9.39576 6.81818 9.39576H8.45455C8.90641 9.39576 9.27273 9.77165 9.27273 10.2353C9.27273 10.339 9.2544 10.4384 9.22089 10.5301C8.81878 10.6675 8.43606 10.8507 8.08042 11.0749H6.81818Z" fill="#191919"/>
+<path d="M10.0914 10.3104C10.0911 10.2854 10.0909 10.2604 10.0909 10.2353C10.0909 7.45323 12.2888 5.19788 15 5.19788C17.7112 5.19788 19.9091 7.45323 19.9091 10.2353C19.9091 12.2491 18.7576 13.9869 17.0934 14.7931C16.8472 14.7226 16.5913 14.6743 16.328 14.6507C16.2568 14.2301 16.1343 13.8259 15.967 13.4446C17.3024 13.0214 18.2727 11.7447 18.2727 10.2353C18.2727 8.3806 16.8075 6.87703 15 6.87703C13.1925 6.87703 11.7273 8.3806 11.7273 10.2353C11.7273 10.2513 11.7274 10.2673 11.7276 10.2833C11.4895 10.2517 11.2466 10.2353 11 10.2353C10.6921 10.2353 10.3883 10.2609 10.0914 10.3104Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-thundershower-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-thundershower-dark.svg
new file mode 100644
index 0000000000..1e1134d562
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-thundershower-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.26026 16.1394C9.24527 16.0933 9.21773 16.0547 9.18214 16.0299C9.14664 16.0051 9.10522 15.9951 9.06462 16.0023C9.02402 16.0094 8.98662 16.0329 8.95851 16.0688L6.04953 19.7874C6.02517 19.8185 6.00891 19.8575 6.00275 19.8995C5.9966 19.9416 6.00082 19.9849 6.0149 20.0242C6.02897 20.0634 6.05229 20.0968 6.08196 20.1202C6.11164 20.1437 6.14637 20.1562 6.18188 20.1562H7.3902L6.73532 22.7167C6.7232 22.764 6.72474 22.8148 6.73968 22.8608C6.75462 22.9069 6.78208 22.9455 6.8176 22.9703C6.85311 22.9952 6.89457 23.0048 6.93523 22.9977C6.97589 22.9906 7.01334 22.9671 7.04149 22.9311L9.95047 19.2125C9.97483 19.1815 9.99109 19.1425 9.99725 19.1004C10.0034 19.0583 9.99918 19.015 9.9851 18.9758C9.97103 18.9366 9.94771 18.9032 9.91804 18.8797C9.88836 18.8562 9.85363 18.8437 9.81812 18.8438H8.6098L9.26468 16.2836C9.27681 16.2363 9.27525 16.1855 9.26026 16.1394ZM15.2603 16.1395C15.2453 16.0934 15.2177 16.0548 15.1821 16.0299V16.0295C15.1466 16.0047 15.1052 15.9952 15.0646 16.0023C15.024 16.0095 14.9866 16.0329 14.9585 16.0689L12.0495 19.7874C12.0252 19.8185 12.0089 19.8575 12.0028 19.8996C11.9966 19.9416 12.0008 19.985 12.0149 20.0242C12.029 20.0634 12.0523 20.0968 12.082 20.1203C12.1116 20.1437 12.1464 20.1562 12.1819 20.1562H13.3902L12.7353 22.7167C12.7232 22.764 12.7247 22.8148 12.7397 22.8608C12.7546 22.9069 12.7821 22.9455 12.8176 22.9703C12.8531 22.9952 12.8946 23.0048 12.9352 22.9977C12.9759 22.9906 13.0133 22.9671 13.0415 22.9311L15.9505 19.2126C15.9748 19.1815 15.9911 19.1425 15.9972 19.1004C16.0034 19.0584 15.9992 19.015 15.9851 18.9758C15.971 18.9366 15.9477 18.9032 15.918 18.8797C15.8884 18.8563 15.8536 18.8438 15.8181 18.8438H14.6098L15.2647 16.2837C15.2768 16.2364 15.2753 16.1855 15.2603 16.1395Z" fill="#E6E6E6"/>
+<path d="M17.92 6.02C17.45 3.18 14.97 1 12 1C9.82 1 7.83 2.18 6.78 4.06C4.09 4.41 2 6.74 2 9.5C2 12.53 4.47 15 7.5 15H17.5C19.98 15 22 12.98 22 10.5C22 8.16 20.21 6.23 17.92 6.02ZM17.5 13H7.5C5.57 13 4 11.43 4 9.5C4 7.76 5.31 6.27 7.04 6.04L8.03 5.91L8.52 5.04C9.23 3.78 10.56 3 12 3C13.94 3 15.63 4.44 15.95 6.35L16.2 7.87L17.74 8.01C19.01 8.13 20 9.22 20 10.5C20 11.88 18.88 13 17.5 13Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/weather-thundershower-light.svg b/examples/quickcontrols/wearable/Wearable/icons/weather-thundershower-light.svg
new file mode 100644
index 0000000000..8f5af95c62
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/weather-thundershower-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.26026 16.1394C9.24527 16.0933 9.21773 16.0547 9.18214 16.0299C9.14664 16.0051 9.10522 15.9951 9.06462 16.0023C9.02402 16.0094 8.98662 16.0329 8.95851 16.0688L6.04953 19.7874C6.02517 19.8185 6.00891 19.8575 6.00275 19.8995C5.9966 19.9416 6.00082 19.9849 6.0149 20.0242C6.02897 20.0634 6.05229 20.0968 6.08196 20.1202C6.11164 20.1437 6.14637 20.1562 6.18188 20.1562H7.3902L6.73532 22.7167C6.7232 22.764 6.72474 22.8148 6.73968 22.8608C6.75462 22.9069 6.78208 22.9455 6.8176 22.9703C6.85311 22.9952 6.89457 23.0048 6.93523 22.9977C6.97589 22.9906 7.01334 22.9671 7.04149 22.9311L9.95047 19.2125C9.97483 19.1815 9.99109 19.1425 9.99725 19.1004C10.0034 19.0583 9.99918 19.015 9.9851 18.9758C9.97103 18.9366 9.94771 18.9032 9.91804 18.8797C9.88836 18.8562 9.85363 18.8437 9.81812 18.8438H8.6098L9.26468 16.2836C9.27681 16.2363 9.27525 16.1855 9.26026 16.1394ZM15.2603 16.1395C15.2453 16.0934 15.2177 16.0548 15.1821 16.0299V16.0295C15.1466 16.0047 15.1052 15.9952 15.0646 16.0023C15.024 16.0095 14.9866 16.0329 14.9585 16.0689L12.0495 19.7874C12.0252 19.8185 12.0089 19.8575 12.0028 19.8996C11.9966 19.9416 12.0008 19.985 12.0149 20.0242C12.029 20.0634 12.0523 20.0968 12.082 20.1203C12.1116 20.1437 12.1464 20.1562 12.1819 20.1562H13.3902L12.7353 22.7167C12.7232 22.764 12.7247 22.8148 12.7397 22.8608C12.7546 22.9069 12.7821 22.9455 12.8176 22.9703C12.8531 22.9952 12.8946 23.0048 12.9352 22.9977C12.9759 22.9906 13.0133 22.9671 13.0415 22.9311L15.9505 19.2126C15.9748 19.1815 15.9911 19.1425 15.9972 19.1004C16.0034 19.0584 15.9992 19.015 15.9851 18.9758C15.971 18.9366 15.9477 18.9032 15.918 18.8797C15.8884 18.8563 15.8536 18.8438 15.8181 18.8438H14.6098L15.2647 16.2837C15.2768 16.2364 15.2753 16.1855 15.2603 16.1395Z" fill="#191919"/>
+<path d="M17.92 6.02C17.45 3.18 14.97 1 12 1C9.82 1 7.83 2.18 6.78 4.06C4.09 4.41 2 6.74 2 9.5C2 12.53 4.47 15 7.5 15H17.5C19.98 15 22 12.98 22 10.5C22 8.16 20.21 6.23 17.92 6.02ZM17.5 13H7.5C5.57 13 4 11.43 4 9.5C4 7.76 5.31 6.27 7.04 6.04L8.03 5.91L8.52 5.04C9.23 3.78 10.56 3 12 3C13.94 3 15.63 4.44 15.95 6.35L16.2 7.87L17.74 8.01C19.01 8.13 20 9.22 20 10.5C20 11.88 18.88 13 17.5 13Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/wifi-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/wifi-dark.svg
new file mode 100644
index 0000000000..b6227d7072
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/wifi-dark.svg
@@ -0,0 +1,3 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18.6445 3.74032C19.1541 3.52925 19.7406 3.64592 20.1306 4.03591L27.6306 11.5359C28.1631 12.0684 28.1631 12.9319 27.6306 13.4644L21.0948 20.0002L27.6306 26.5359C28.1631 27.0685 28.1631 27.9319 27.6306 28.4644L20.1306 35.9644C19.7406 36.3544 19.1541 36.4711 18.6445 36.26C18.135 36.0489 17.8027 35.5517 17.8027 35.0002V23.2923L12.6306 28.4644C12.0981 28.9969 11.2347 28.9969 10.7021 28.4644C10.1696 27.9319 10.1696 27.0685 10.7021 26.5359L17.2379 20.0002L10.7021 13.4644C10.1696 12.9319 10.1696 12.0684 10.7021 11.5359C11.2347 11.0034 12.0981 11.0034 12.6306 11.5359L17.8027 16.708V5.00015C17.8027 4.44861 18.135 3.95138 18.6445 3.74032ZM20.53 23.2923L24.7379 27.5002L20.53 31.708V23.2923ZM20.53 16.708V8.29226L24.7379 12.5002L20.53 16.708Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/wifi-light.svg b/examples/quickcontrols/wearable/Wearable/icons/wifi-light.svg
new file mode 100644
index 0000000000..1d90443cf4
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/wifi-light.svg
@@ -0,0 +1,4 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M23.3328 33.3294C23.3328 35.1704 21.8404 36.6628 19.9995 36.6628C18.1585 36.6628 16.6661 35.1704 16.6661 33.3294C16.6661 31.4885 18.1585 29.9961 19.9995 29.9961C21.8404 29.9961 23.3328 31.4885 23.3328 33.3294Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.47026 16.2138C8.77419 12.2 14.2971 9.99935 20.0002 9.99935C25.7033 9.99935 31.2262 12.2 35.5302 16.2138C36.2033 16.8416 37.258 16.8048 37.8858 16.1317C38.5135 15.4585 38.4768 14.4039 37.8036 13.7761C32.9001 9.20312 26.5709 6.66602 20.0002 6.66602C13.4295 6.66602 7.10028 9.20312 2.19683 13.7761C1.52366 14.4039 1.48688 15.4585 2.11467 16.1317C2.74246 16.8048 3.7971 16.8416 4.47026 16.2138ZM20.0015 18.3314C16.2134 18.3314 12.4961 19.9013 9.50517 22.8513C8.84983 23.4977 7.79458 23.4905 7.1482 22.8351C6.50182 22.1798 6.50908 21.1245 7.16442 20.4782C10.7246 16.9666 15.2636 14.9981 20.0015 14.9981C24.7393 14.9981 29.2783 16.9666 32.8385 20.4782C33.4938 21.1245 33.5011 22.1798 32.8547 22.8351C32.2083 23.4905 31.1531 23.4977 30.4978 22.8513C27.5068 19.9013 23.7895 18.3314 20.0015 18.3314ZM20.0006 26.6635C18.5723 26.6635 17.172 27.1273 15.9939 28.0018C15.2548 28.5504 14.2108 28.3959 13.6622 27.6568C13.1136 26.9177 13.2681 25.8738 14.0072 25.3252C15.7505 24.0313 17.8451 23.3301 20.0006 23.3301C22.156 23.3301 24.2506 24.0313 25.9939 25.3252C26.733 25.8738 26.8875 26.9177 26.3389 27.6568C25.7903 28.3959 24.7464 28.5504 24.0072 28.0018C22.8291 27.1273 21.4288 26.6635 20.0006 26.6635Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/wind-dark.svg b/examples/quickcontrols/wearable/Wearable/icons/wind-dark.svg
new file mode 100644
index 0000000000..95464c4ec0
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/wind-dark.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3 14C2.44772 14 2 14.4477 2 15C2 15.5523 2.44772 16 3 16H12C13.1046 16 14 16.8954 14 18C14 19.1046 13.1046 20 12 20C10.8954 20 10 19.1046 10 18C10 17.4477 9.55228 17 9 17C8.44772 17 8 17.4477 8 18C8 20.2091 9.79086 22 12 22C14.2091 22 16 20.2091 16 18C16 15.7909 14.2091 14 12 14H3Z" fill="#E6E6E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 6C12 6.55229 12.4477 7 13 7C13.5523 7 14 6.55228 14 6C14 4.89543 14.8954 4 16 4C17.1046 4 18 4.89543 18 6C18 7.10457 17.1046 8 16 8H3C2.44772 8 2 8.44772 2 9C2 9.55228 2.44772 10 3 10H16C18.2091 10 20 8.20914 20 6C20 3.79086 18.2091 2 16 2C13.7909 2 12 3.79086 12 6ZM3 11C2.44772 11 2 11.4477 2 12C2 12.5523 2.44772 13 3 13H18C19.1046 13 20 13.8954 20 15C20 16.1046 19.1046 17 18 17C17.4477 17 17 17.4477 17 18C17 18.5523 17.4477 19 18 19C20.2091 19 22 17.2091 22 15C22 12.7909 20.2091 11 18 11H3Z" fill="#E6E6E6"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/wind-light.svg b/examples/quickcontrols/wearable/Wearable/icons/wind-light.svg
new file mode 100644
index 0000000000..7146903e27
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/wind-light.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3 14C2.44772 14 2 14.4477 2 15C2 15.5523 2.44772 16 3 16H12C13.1046 16 14 16.8954 14 18C14 19.1046 13.1046 20 12 20C10.8954 20 10 19.1046 10 18C10 17.4477 9.55228 17 9 17C8.44772 17 8 17.4477 8 18C8 20.2091 9.79086 22 12 22C14.2091 22 16 20.2091 16 18C16 15.7909 14.2091 14 12 14H3Z" fill="#191919"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 6C12 6.55229 12.4477 7 13 7C13.5523 7 14 6.55228 14 6C14 4.89543 14.8954 4 16 4C17.1046 4 18 4.89543 18 6C18 7.10457 17.1046 8 16 8H3C2.44772 8 2 8.44772 2 9C2 9.55228 2.44772 10 3 10H16C18.2091 10 20 8.20914 20 6C20 3.79086 18.2091 2 16 2C13.7909 2 12 3.79086 12 6ZM3 11C2.44772 11 2 11.4477 2 12C2 12.5523 2.44772 13 3 13H18C19.1046 13 20 13.8954 20 15C20 16.1046 19.1046 17 18 17C17.4477 17 17 17.4477 17 18C17 18.5523 17.4477 19 18 19C20.2091 19 22 17.2091 22 15C22 12.7909 20.2091 11 18 11H3Z" fill="#191919"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/icons/yellowsun.svg b/examples/quickcontrols/wearable/Wearable/icons/yellowsun.svg
new file mode 100644
index 0000000000..bf57285374
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/icons/yellowsun.svg
@@ -0,0 +1,4 @@
+<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M28 38C33.5228 38 38 33.5228 38 28C38 22.4772 33.5228 18 28 18C22.4772 18 18 22.4772 18 28C18 33.5228 22.4772 38 28 38ZM28 43C36.2843 43 43 36.2843 43 28C43 19.7157 36.2843 13 28 13C19.7157 13 13 19.7157 13 28C13 36.2843 19.7157 43 28 43Z" fill="#FFD600"/>
+<path d="M27.5 10.5C26.3954 10.5 25.5 9.60457 25.5 8.5V2.5C25.5 1.39543 26.3954 0.5 27.5 0.5H28.5C29.6046 0.5 30.5 1.39543 30.5 2.5V8.5C30.5 9.60457 29.6046 10.5 28.5 10.5H27.5ZM43.5392 15.9608C42.7582 16.7418 41.4918 16.7418 40.7108 15.9608L40.0831 15.3331C39.3094 14.5594 39.3011 13.3074 40.0646 12.5235L44.261 8.21475C45.0437 7.41107 46.3342 7.40825 47.1204 8.2085L47.7982 8.8984C48.5675 9.6814 48.5619 10.9381 47.7857 11.7143L43.5392 15.9608ZM47.5 30.5C46.3954 30.5 45.5 29.6046 45.5 28.5V27.5C45.5 26.3954 46.3954 25.5 47.5 25.5H53.5C54.6046 25.5 55.5 26.3954 55.5 27.5V28.5C55.5 29.6046 54.6046 30.5 53.5 30.5H47.5ZM27.5 55.5C26.3954 55.5 25.5 54.6046 25.5 53.5V47.5C25.5 46.3954 26.3954 45.5 27.5 45.5H28.5C29.6046 45.5 30.5 46.3954 30.5 47.5V53.5C30.5 54.6046 29.6046 55.5 28.5 55.5H27.5ZM15.27 15.9048C14.4926 16.6545 13.2603 16.6515 12.4865 15.8981L8.21508 11.739C7.41125 10.9563 7.40836 9.66569 8.20868 8.87942L8.8984 8.20179C9.6814 7.43253 10.9381 7.43809 11.7143 8.21425L15.9348 12.4348C16.7259 13.2259 16.7143 14.5121 15.9089 15.2887L15.27 15.9048ZM47.064 47.7605C46.2775 48.5746 44.9728 48.5743 44.1866 47.7598L40.0521 43.4763C39.2946 42.6915 39.3056 41.4444 40.0769 40.6731L40.6733 40.0767C41.4445 39.3055 42.6914 39.2943 43.4763 40.0517L47.7474 44.1729C48.5426 44.9402 48.5648 46.207 47.797 47.0018L47.064 47.7605ZM2.5 30.5C1.39543 30.5 0.5 29.6046 0.5 28.5V27.5C0.5 26.3954 1.39543 25.5 2.5 25.5H8.5C9.60457 25.5 10.5 26.3954 10.5 27.5V28.5C10.5 29.6046 9.60457 30.5 8.5 30.5H2.5ZM11.7393 47.8232C10.9533 48.6092 9.67735 48.6035 8.8984 47.8106L8.20179 47.1016C7.43253 46.3186 7.43809 45.0619 8.21425 44.2857L12.4479 40.0521C13.2093 39.2907 14.4368 39.2688 15.2249 40.0025L15.625 40.375L15.9732 40.7112C16.7777 41.488 16.789 42.7735 15.9983 43.5642L11.7393 47.8232Z" fill="#FFD600"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/images/fallbackmap.png b/examples/quickcontrols/wearable/Wearable/images/fallbackmap.png
new file mode 100644
index 0000000000..bf1ae037f7
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/images/fallbackmap.png
Binary files differ
diff --git a/examples/quickcontrols/wearable/Wearable/images/fitness-man-running-dark.svg b/examples/quickcontrols/wearable/Wearable/images/fitness-man-running-dark.svg
new file mode 100644
index 0000000000..ea313bbc71
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/images/fitness-man-running-dark.svg
@@ -0,0 +1,3 @@
+<svg width="62" height="78" viewBox="0 0 62 78" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M35.3752 11.8396C36.7712 13.239 38.4184 13.9388 40.3169 13.9388C42.1596 13.9388 43.7649 13.225 45.1329 11.7976C46.501 10.3701 47.185 8.73273 47.185 6.88541C47.185 5.0381 46.5149 3.42872 45.1748 2.05723C43.8347 0.685736 42.2154 0 40.3169 0C38.4184 0 36.7712 0.671741 35.3752 2.01524C33.9793 3.35875 33.2813 4.98212 33.2813 6.88541C33.2813 8.78871 33.9793 10.4401 35.3752 11.8396ZM3.29629 57.7703C8.78005 58.8751 21.8066 61.4606 21.8066 61.4606C25.5181 61.6227 26.6937 60.5763 27.7178 56.9635L31.1036 47.2742L38.4743 54.1597V75.1518C38.4743 78.9494 45.5099 78.9494 45.5099 75.1518C45.5099 74.1322 45.5321 71.8127 45.5596 68.9539C45.6036 64.3631 45.661 58.3818 45.661 54.1597C45.661 49.4683 43.1689 47.2742 43.1689 47.2742L38.1392 41.9842L40.3169 31.4882C42.6063 34.1752 45.3982 36.3024 48.6926 37.8698C52.0429 39.4372 55.7202 40.2209 59.4135 40.2209C63.1069 40.2209 62.6084 33.1675 59.4135 33.1675C56.2186 33.1675 53.1318 32.3838 50.4515 30.8164C47.8271 29.305 45.7891 27.2898 44.3373 24.7707L40.9032 19.2288C39.3397 16.9336 37.3854 15.7861 35.0402 15.7861C34.7052 15.7861 34.2445 15.8421 33.6582 15.954C33.0719 16.066 32.6113 16.1219 32.2762 16.1219L17.7456 22.4856C14.6914 24.0251 14.5358 25.4411 14.2604 27.9476L14.2566 27.9821C14.2566 27.9821 14.101 35.9703 14.101 40.2209C14.101 44.4715 20.969 44.4715 20.969 40.2209V28.2134L27.3346 25.7783L20.7399 53.4658C20.7399 53.4658 9.46632 51.6708 4.6364 50.717C-0.193514 49.7631 -2.18747 56.6655 3.29629 57.7703Z" fill="#2CDE85"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/images/fitness-man-running-light.svg b/examples/quickcontrols/wearable/Wearable/images/fitness-man-running-light.svg
new file mode 100644
index 0000000000..296d0b4309
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/images/fitness-man-running-light.svg
@@ -0,0 +1,3 @@
+<svg width="62" height="78" viewBox="0 0 62 78" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M35.3752 11.8396C36.7712 13.239 38.4184 13.9388 40.3169 13.9388C42.1596 13.9388 43.7649 13.225 45.1329 11.7976C46.501 10.3701 47.185 8.73273 47.185 6.88541C47.185 5.0381 46.5149 3.42872 45.1748 2.05723C43.8347 0.685736 42.2154 0 40.3169 0C38.4184 0 36.7712 0.671741 35.3752 2.01524C33.9793 3.35875 33.2813 4.98212 33.2813 6.88541C33.2813 8.78871 33.9793 10.4401 35.3752 11.8396ZM3.29629 57.7703C8.78005 58.8751 21.8066 61.4606 21.8066 61.4606C25.5181 61.6227 26.6937 60.5763 27.7178 56.9635L31.1036 47.2742L38.4743 54.1597V75.1518C38.4743 78.9494 45.5099 78.9494 45.5099 75.1518C45.5099 74.1322 45.5321 71.8127 45.5596 68.9539C45.6036 64.3631 45.661 58.3818 45.661 54.1597C45.661 49.4683 43.1689 47.2742 43.1689 47.2742L38.1392 41.9842L40.3169 31.4882C42.6063 34.1752 45.3982 36.3024 48.6926 37.8698C52.0429 39.4372 55.7202 40.2209 59.4135 40.2209C63.1069 40.2209 62.6084 33.1675 59.4135 33.1675C56.2186 33.1675 53.1318 32.3838 50.4515 30.8164C47.8271 29.305 45.7891 27.2898 44.3373 24.7707L40.9032 19.2288C39.3397 16.9336 37.3854 15.7861 35.0402 15.7861C34.7052 15.7861 34.2445 15.8421 33.6582 15.954C33.0719 16.066 32.6113 16.1219 32.2762 16.1219L17.7456 22.4856C14.6914 24.0251 14.5358 25.4411 14.2604 27.9476L14.2566 27.9821C14.2566 27.9821 14.101 35.9703 14.101 40.2209C14.101 44.4715 20.969 44.4715 20.969 40.2209V28.2134L27.3346 25.7783L20.7399 53.4658C20.7399 53.4658 9.46632 51.6708 4.6364 50.717C-0.193514 49.7631 -2.18747 56.6655 3.29629 57.7703Z" fill="#19545C"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/images/fitness-man-walking-dark.svg b/examples/quickcontrols/wearable/Wearable/images/fitness-man-walking-dark.svg
new file mode 100644
index 0000000000..10a1d2421f
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/images/fitness-man-walking-dark.svg
@@ -0,0 +1,3 @@
+<svg width="44" height="78" viewBox="0 0 44 78" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4523 11.7338C16.8354 13.1207 18.4676 13.8142 20.3487 13.8142C22.1745 13.8142 23.7651 13.1069 25.1206 11.6922C26.4761 10.2774 27.1539 8.6547 27.1539 6.82389C27.1539 4.99308 26.4899 3.39808 25.1621 2.03885C23.8342 0.679608 22.2298 0 20.3487 0C18.4676 0 16.8354 0.665739 15.4523 1.99724C14.0691 3.32873 13.3775 4.9376 13.3775 6.82389C13.3775 8.71018 14.0691 10.3468 15.4523 11.7338ZM6.46298 74.2821C8.09671 71.3108 15.6242 57.4448 15.6242 57.4448L19.3587 47.5488L26.6618 54.3727V75.1772C26.6618 78.9409 33.633 78.9409 33.633 75.1772C33.633 74.1667 33.655 71.868 33.6822 69.0347C33.7258 64.485 33.7827 58.5571 33.7827 54.3727C33.7827 49.7233 31.3134 47.5488 31.3134 47.5488L26.3298 42.3061L26.6618 29.7128C26.6618 29.7128 37.8363 39.6174 41.0335 39.6174C44.2307 39.6174 45.4782 34.17 41.5273 32.6843C37.5765 31.1987 32.4711 25.2463 32.4711 25.2463L29.0685 19.7539C27.5194 17.4793 26.6618 17.3324 26.6618 17.3324C26.0683 16.2311 13.2224 16.1882 12.8837 17.3324L5.96968 22.2847C3.15235 23.7051 2.95593 25.618 2.72317 27.8848C2.70477 28.064 2.68614 28.2455 2.66596 28.429C2.66596 28.429 2.51175 36.3458 2.51175 40.5585C2.51175 44.7711 9.31693 44.7711 9.31693 40.5585V28.6583L12.8837 26.7415V42.3061L9.0899 53.685C9.0899 53.685 3.00484 65.4635 0.536321 70.3204C-1.93219 75.1772 4.82925 77.2534 6.46298 74.2821Z" fill="#2CDE85"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/images/fitness-man-walking-light.svg b/examples/quickcontrols/wearable/Wearable/images/fitness-man-walking-light.svg
new file mode 100644
index 0000000000..268d4ac880
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/images/fitness-man-walking-light.svg
@@ -0,0 +1,3 @@
+<svg width="44" height="78" viewBox="0 0 44 78" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4523 11.7338C16.8354 13.1207 18.4676 13.8142 20.3487 13.8142C22.1745 13.8142 23.7651 13.1069 25.1206 11.6922C26.4761 10.2774 27.1539 8.6547 27.1539 6.82389C27.1539 4.99308 26.4899 3.39808 25.1621 2.03885C23.8342 0.679608 22.2298 0 20.3487 0C18.4676 0 16.8354 0.665739 15.4523 1.99724C14.0691 3.32873 13.3775 4.9376 13.3775 6.82389C13.3775 8.71018 14.0691 10.3468 15.4523 11.7338ZM6.46298 74.2821C8.09671 71.3108 15.6242 57.4448 15.6242 57.4448L19.3587 47.5488L26.6618 54.3727V75.1772C26.6618 78.9409 33.633 78.9409 33.633 75.1772C33.633 74.1667 33.655 71.868 33.6822 69.0347C33.7258 64.485 33.7827 58.5571 33.7827 54.3727C33.7827 49.7233 31.3134 47.5488 31.3134 47.5488L26.3298 42.3061L26.6618 29.7128C26.6618 29.7128 37.8363 39.6174 41.0335 39.6174C44.2307 39.6174 45.4782 34.17 41.5273 32.6843C37.5765 31.1987 32.4711 25.2463 32.4711 25.2463L29.0685 19.7539C27.5194 17.4793 26.6618 17.3324 26.6618 17.3324C26.0683 16.2311 13.2224 16.1882 12.8837 17.3324L5.96968 22.2847C3.15235 23.7051 2.95593 25.618 2.72317 27.8848C2.70477 28.064 2.68614 28.2455 2.66596 28.429C2.66596 28.429 2.51175 36.3458 2.51175 40.5585C2.51175 44.7711 9.31693 44.7711 9.31693 40.5585V28.6583L12.8837 26.7415V42.3061L9.0899 53.685C9.0899 53.685 3.00484 65.4635 0.536321 70.3204C-1.93219 75.1772 4.82925 77.2534 6.46298 74.2821Z" fill="#19545C"/>
+</svg>
diff --git a/examples/quickcontrols/wearable/Wearable/navigation.js b/examples/quickcontrols/wearable/Wearable/navigation.js
new file mode 100644
index 0000000000..0f3e7fa9be
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/navigation.js
@@ -0,0 +1,119 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+function requestNavigationRoute(rModel) {
+ var xhr = new XMLHttpRequest;
+ xhr.open("GET", "fallbackroute.json");
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState === XMLHttpRequest.DONE) {
+ var a = JSON.parse(xhr.responseText);
+ var steps = a.route.steps;
+
+ for (var step in steps) {
+ var direction = steps[step].direction;
+ var instructionText = steps[step].instructionText;
+ var distanceToNextInstruction = steps[step].distanceToNextInstruction;
+
+ rModel.append({
+ icon: formatIconInstruction(direction),
+ shortInfo: formatShortInstruction(direction),
+ instruction: instructionText,
+ distance: qsTr("in") + " " + formatDistance(distanceToNextInstruction)
+ });
+ }
+ }
+ }
+ xhr.send();
+}
+
+function formatIconInstruction(direction)
+{
+ switch (direction){
+ case 0:
+ return "";
+ case 1:
+ return "forward";
+ case 2:
+ return "bearright";
+ case 3:
+ case 4:
+ return "right";
+ case 5:
+ return "lightright";
+ case 6:
+ return "uturnright";
+ case 7:
+ return "uturleft";
+ case 8:
+ case 9:
+ return "left";
+ case 10:
+ return "lightleft";
+ case 11:
+ return "bearleft";
+ }
+}
+
+function formatShortInstruction(direction)
+{
+ switch (direction){
+ case 0:
+ return "";
+ case 1:
+ return "Move forward";
+ case 2:
+ return "Bear right";
+ case 3:
+ return "Turn right";
+ case 4:
+ return "Turn hard right";
+ case 5:
+ return "Turn slightly right";
+ case 6:
+ return "Uturn right";
+ case 7:
+ return "Uturn left";
+ case 8:
+ return "Turn hard left";
+ case 9:
+ return "Turn left";
+ case 10:
+ return "Turn slightly left";
+ case 11:
+ return "Bear left";
+ }
+}
+
+function formatTime(sec)
+{
+ var value = sec
+ var seconds = value % 60
+ value /= 60
+ value = (value > 1) ? Math.round(value) : 0
+ var minutes = value % 60
+ value /= 60
+ value = (value > 1) ? Math.round(value) : 0
+ var hours = value
+ if (hours > 0) value = hours + "h:"+ minutes + "m"
+ else value = minutes + "min"
+ return value
+}
+
+function formatDistance(meters)
+{
+ var dist = Math.round(meters)
+ if (dist > 1000 ){
+ if (dist > 100000){
+ dist = Math.round(dist / 1000)
+ }
+ else{
+ dist = Math.round(dist / 100)
+ dist = dist / 10
+ }
+ dist = dist + " km"
+ }
+ else{
+ dist = dist + " m"
+ }
+ return dist
+}
diff --git a/examples/quickcontrols/wearable/Wearable/notifications.js b/examples/quickcontrols/wearable/Wearable/notifications.js
new file mode 100644
index 0000000000..d278cfb87b
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/notifications.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+function populateData(listData) {
+ listData.append({
+ name: "John Doe",
+ gender: "m",
+ date: "02/15/2017",
+ time: "09:20 am",
+ text: "I found the bug that has been bothering us for years! It was in your code segment :P"
+ });
+
+ listData.append({
+ name: "Jane Worldege",
+ gender: "f",
+ date: "02/06/2017",
+ time: "10:15 am",
+ text: "John told me about your bug. One more such thing and you're fired!"
+ });
+
+ listData.append({
+ name: "Jennifer Wang",
+ gender: "f",
+ date: "02/03/2017",
+ time: "05:16 pm",
+ text: "Hey! Have you seen the new Qt version? It can do 3D stuff now!"
+ });
+}
diff --git a/examples/quickcontrols/wearable/Wearable/qmldir b/examples/quickcontrols/wearable/Wearable/qmldir
new file mode 100644
index 0000000000..550b8bb1c1
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/qmldir
@@ -0,0 +1,19 @@
+# This file only exists for qmake.
+
+module Wearable
+prefer :/qt/qml/Wearable/
+AlarmsPage 254.0 AlarmsPage.qml
+Clock 254.0 Clock.qml
+DemoMode 254.0 DemoMode.qml
+DemoModeIndicator 254.0 DemoModeIndicator.qml
+FitnessPage 254.0 FitnessPage.qml
+LauncherPage 254.0 LauncherPage.qml
+Main 254.0 Main.qml
+NavigationPage 254.0 NavigationPage.qml
+NavigationFallbackPage 254.0 NavigationFallbackPage.qml
+NotificationsPage 254.0 NotificationsPage.qml
+RouteElement 254.0 RouteElement.qml
+SettingsPage 254.0 SettingsPage.qml
+SwipeViewPage 254.0 SwipeViewPage.qml
+WeatherPage 254.0 WeatherPage.qml
+WorldClockPage 254.0 WorldClockPage.qml
diff --git a/examples/quickcontrols/wearable/Wearable/weather.js b/examples/quickcontrols/wearable/Wearable/weather.js
new file mode 100644
index 0000000000..a9804b686d
--- /dev/null
+++ b/examples/quickcontrols/wearable/Wearable/weather.js
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+function formatTemp(farenheit) {
+ let celsius = Math.round((farenheit -32) * 5/9)
+ return celsius + "°C"
+}
+
+function requestWeatherData(cntr) {
+ var xhr = new XMLHttpRequest;
+ xhr.open("GET", "weather.json");
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState === XMLHttpRequest.DONE) {
+ cntr.weatherData = JSON.parse(xhr.responseText)
+ }
+ }
+ xhr.send();
+}
+
+function getTimeHMS(utcTime) {
+ var date = new Date(utcTime * 1000);
+ // Hours part from the timestamp
+ var hours = "0" + date.getHours();
+ // Minutes part from the timestamp
+ var minutes = "0" + date.getMinutes();
+
+ // Will display time in 10:30:23 format
+ return hours.substr(-2) + ':' + minutes.substr(-2);
+}
+
+function iconSelect(code) {
+ if (code === "01d" || code === "01n")
+ return "sunny";
+ else if (code === "02d" || code === "02n")
+ return "sunny-very-few-clouds";
+ else if (code === "03d" || code === "03n")
+ return "few-clouds";
+ else if (code === "04d" || code === "04n")
+ return "overcast";
+ else if (code === "09d" || code === "09n" || code === "10d" || code === "10n")
+ return "showers";
+ else if (code === "11d" || code === "11n")
+ return "thundershower";
+ else if (code === "13d" || code === "13n")
+ return "snow";
+ else if (code === "50d" || code === "50n")
+ return "fog";
+
+ return "sunny"; // default choice
+}
diff --git a/examples/quickcontrols/wearable/qml/Weather/weather.json b/examples/quickcontrols/wearable/Wearable/weather.json
index 2cce95c60b..2cce95c60b 100644
--- a/examples/quickcontrols/wearable/qml/Weather/weather.json
+++ b/examples/quickcontrols/wearable/Wearable/weather.json
diff --git a/examples/quickcontrols/wearable/WearableSettings/CMakeLists.txt b/examples/quickcontrols/wearable/WearableSettings/CMakeLists.txt
new file mode 100644
index 0000000000..9a839346bd
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableSettings/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(wearablesettings LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
+
+set_source_files_properties(
+ WearableSettings.qml
+ PROPERTIES
+ QT_QML_SINGLETON_TYPE TRUE
+)
+
+qt_policy(SET QTP0001 NEW)
+qt_add_qml_module(wearablesettings
+ URI WearableSettings
+ PLUGIN_TARGET wearablesettings
+ QML_FILES
+ WearableSettings.qml
+)
+
+target_link_libraries(wearablesettings PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
+)
+
+if(UNIX AND NOT APPLE AND CMAKE_CROSSCOMPILING)
+ find_package(Qt6 REQUIRED COMPONENTS QuickTemplates2)
+
+ # Work around QTBUG-86533
+ target_link_libraries(wearablesettings PRIVATE Qt6::QuickTemplates2)
+endif()
+
+install(TARGETS wearablesettings
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/WearableSettings"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/WearableSettings"
+)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
+ DESTINATION "${CMAKE_INSTALL_BINDIR}/WearableSettings"
+)
diff --git a/examples/quickcontrols/wearable/WearableSettings/WearableSettings.qml b/examples/quickcontrols/wearable/WearableSettings/WearableSettings.qml
new file mode 100644
index 0000000000..3af505867c
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableSettings/WearableSettings.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma Singleton
+
+import QtCore
+
+Settings {
+ property bool wireless
+ property bool bluetooth
+ property int brightness
+ property bool darkTheme
+ property bool demoMode
+}
diff --git a/examples/quickcontrols/wearable/WearableSettings/qmldir b/examples/quickcontrols/wearable/WearableSettings/qmldir
new file mode 100644
index 0000000000..b11b06ab0b
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableSettings/qmldir
@@ -0,0 +1,5 @@
+# This file only exists for qmake.
+
+module WearableSettings
+prefer :/qt/qml/WearableSettings/
+singleton WearableSettings 254.0 WearableSettings.qml
diff --git a/examples/quickcontrols/wearable/WearableStyle/CMakeLists.txt b/examples/quickcontrols/wearable/WearableStyle/CMakeLists.txt
new file mode 100644
index 0000000000..3ccd90ff2b
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(wearablestyle LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
+
+set_source_files_properties(UIStyle.qml
+ PROPERTIES
+ QT_QML_SINGLETON_TYPE TRUE
+)
+
+qt_policy(SET QTP0001 NEW)
+qt_add_qml_module(wearablestyle
+ URI WearableStyle
+ PLUGIN_TARGET wearablestyle
+ QML_FILES
+ PageIndicator.qml
+ Slider.qml
+ Switch.qml
+ UIStyle.qml
+ RESOURCES
+ fonts/TitilliumWeb-Bold.ttf
+ fonts/TitilliumWeb-SemiBold.ttf
+ fonts/TitilliumWeb-Regular.ttf
+)
+
+target_link_libraries(wearablestyle PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
+)
+
+if(UNIX AND NOT APPLE AND CMAKE_CROSSCOMPILING)
+ find_package(Qt6 REQUIRED COMPONENTS QuickTemplates2)
+
+ # Work around QTBUG-86533
+ target_link_libraries(wearablestyle PRIVATE Qt6::QuickTemplates2)
+endif()
+
+install(TARGETS wearablestyle
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/WearableStyle"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/WearableStyle"
+)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
+ DESTINATION "${CMAKE_INSTALL_BINDIR}/WearableStyle"
+)
diff --git a/examples/quickcontrols/wearable/WearableStyle/PageIndicator.qml b/examples/quickcontrols/wearable/WearableStyle/PageIndicator.qml
new file mode 100644
index 0000000000..bfc25c78d1
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/PageIndicator.qml
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Templates as T
+
+T.PageIndicator {
+ id: control
+
+ implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding
+ implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding
+
+ spacing: 6
+ padding: 6
+ bottomPadding: 7
+
+ delegate: Rectangle {
+ required property int index
+
+ implicitWidth: 16
+ implicitHeight: 16
+
+ radius: width / 2
+ color: index === control.currentIndex ? UIStyle.buttonProgress : UIStyle.pageIndicatorColor
+ border.color: UIStyle.indicatorOutlineColor
+
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 100
+ }
+ }
+ }
+
+ contentItem: Row {
+ spacing: control.spacing
+
+ Repeater {
+ model: control.count
+ delegate: control.delegate
+ }
+ }
+}
diff --git a/examples/quickcontrols/wearable/WearableStyle/Slider.qml b/examples/quickcontrols/wearable/WearableStyle/Slider.qml
new file mode 100644
index 0000000000..894e3a37df
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/Slider.qml
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Templates as T
+
+T.Slider {
+ id: control
+
+ implicitWidth: 200
+ implicitHeight: 32
+
+ handle: Rectangle {
+ x: control.visualPosition * (control.width - width)
+ y: (control.height - height) / 2
+ width: 32
+ height: width
+ radius: width/2
+
+ color: control.pressed ? UIStyle.buttonGrayPressed : UIStyle.buttonGray
+ border.color: UIStyle.buttonGrayOutLine
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ radius: parent.radius
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: UIStyle.gradientOverlay1 }
+ GradientStop { position: 1.0; color: UIStyle.gradientOverlay2 }
+ }
+ }
+
+ }
+
+ background: Rectangle {
+ y: (control.height - height) / 2
+ height: 6
+ radius: height / 2
+ color: UIStyle.buttonBackground
+
+ Rectangle {
+ width: control.visualPosition * parent.width
+ height: parent.height
+ color: UIStyle.buttonProgress
+ radius: height / 2
+ }
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ radius: parent.radius
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: UIStyle.gradientOverlay1 }
+ GradientStop { position: 1.0; color: UIStyle.gradientOverlay2 }
+ }
+ }
+ }
+}
+
diff --git a/examples/quickcontrols/wearable/WearableStyle/Switch.qml b/examples/quickcontrols/wearable/WearableStyle/Switch.qml
new file mode 100644
index 0000000000..1ab153c528
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/Switch.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Templates as T
+
+T.Switch {
+ id: control
+
+ implicitWidth: 64
+ implicitHeight: 34
+
+ indicator: Rectangle {
+ x: 2 + control.visualPosition * (control.width - width - 4)
+ y: (control.height - height) / 2
+ width: height
+ height: parent.height - 4
+ radius: height / 2
+
+ color: control.down ? UIStyle.buttonGrayPressed : UIStyle.buttonGray
+ border.color: UIStyle.buttonGrayOutLine
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ radius: parent.radius
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: UIStyle.gradientOverlay1 }
+ GradientStop { position: 1.0; color: UIStyle.gradientOverlay2 }
+ }
+ }
+
+ Behavior on x {
+ enabled: !control.pressed
+ SmoothedAnimation { velocity: 200 }
+ }
+ }
+
+ background: Rectangle {
+ radius: 17
+ color: control.checked ? UIStyle.buttonProgress : UIStyle.buttonBackground
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ radius: parent.radius
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: UIStyle.gradientOverlay1 }
+ GradientStop { position: 1.0; color: UIStyle.gradientOverlay2 }
+ }
+ }
+ }
+}
+
diff --git a/examples/quickcontrols/wearable/WearableStyle/UIStyle.qml b/examples/quickcontrols/wearable/WearableStyle/UIStyle.qml
new file mode 100644
index 0000000000..bd2f47f8c0
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/UIStyle.qml
@@ -0,0 +1,114 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma Singleton
+
+import QtQuick
+import WearableSettings
+
+Item {
+ id: uiStyle
+
+ property font h1: Qt.font({
+ family: fontLoaderSemibold.font.family,
+ weight: fontLoaderSemibold.font.weight,
+ pixelSize: 24
+ })
+ property int h1lineHeight: 28
+
+ property font h2: Qt.font({
+ family: fontLoaderSemibold.font.family,
+ weight: fontLoaderSemibold.font.weight,
+ pixelSize: 20
+ })
+ property int h2lineHeight: 24
+
+ property font h3: Qt.font({
+ family: fontLoaderRegular.font.family,
+ weight: fontLoaderRegular.font.weight,
+ pixelSize: 16
+ })
+ property int h3lineHeight: 20
+
+ property font h4: Qt.font({
+ family: fontLoaderBold.font.family,
+ weight: fontLoaderBold.font.weight,
+ pixelSize: 16
+ })
+ property int h4lineHeight: 20
+
+ property font p1: Qt.font({
+ family: fontLoaderRegular.font.family,
+ weight: fontLoaderRegular.font.weight,
+ pixelSize: 14
+ })
+ property int p1lineHeight: 16
+
+ property font p2: Qt.font({
+ family: fontLoaderRegular.font.family,
+ weight: fontLoaderRegular.font.weight,
+ pixelSize: 20
+ })
+ property int p2lineHeight: 24
+
+ property font tumblerFont: Qt.font({
+ family: fontLoaderRegular.font.family,
+ weight: fontLoaderRegular.font.weight,
+ pixelSize: 32
+ })
+
+ FontLoader {
+ id: fontLoaderBold
+ source: "qrc:/qt/qml/WearableStyle/fonts/TitilliumWeb-Bold.ttf"
+ }
+
+ FontLoader {
+ id: fontLoaderSemibold
+ source: "qrc:/qt/qml/WearableStyle/fonts/TitilliumWeb-SemiBold.ttf"
+ }
+
+ FontLoader {
+ id: fontLoaderRegular
+ source: "qrc:/qt/qml/WearableStyle/fonts/TitilliumWeb-Regular.ttf"
+ }
+
+ readonly property color colorRed: "#E91E63"
+
+ readonly property color buttonGray: WearableSettings.darkTheme ? "#808080" : "#f3f3f4"
+ readonly property color buttonGrayPressed: WearableSettings.darkTheme ? "#707070" : "#cecfd5"
+ readonly property color buttonGrayOutLine: WearableSettings.darkTheme ? "#0D0D0D" : "#999999"
+
+ readonly property color buttonBackground: WearableSettings.darkTheme ? "#262626" : "#CCCCCC"
+ readonly property color buttonProgress: WearableSettings.darkTheme ? "#28C878" : "#19545C"
+
+ readonly property color gradientOverlay1: "#00000000"
+ readonly property color gradientOverlay2: "#1E000000"
+
+ readonly property color background1: WearableSettings.darkTheme ? "#00414A" : "#ABF2CE"
+ readonly property color background2: WearableSettings.darkTheme ? "#07243E" : "#E6E6E6"
+ readonly property color background3: WearableSettings.darkTheme ? "#262626" : "#E6E6E6"
+
+ readonly property color textColor: WearableSettings.darkTheme ? "#E6E6E6" : "#191919"
+ readonly property color titletextColor: WearableSettings.darkTheme ? "#2CDE85" : "#191919"
+
+ readonly property color highlightColor: WearableSettings.darkTheme ? "#33676E" : "#28C878"
+
+ readonly property color pageIndicatorColor: WearableSettings.darkTheme ? "#00000000" : "#E6E6E6"
+ readonly property color indicatorOutlineColor: WearableSettings.darkTheme ? "#707070" : "#999999"
+
+ readonly property color listHeader1: WearableSettings.darkTheme ? "#9E00414A" : "#9E2CDE85"
+ readonly property color listHeader2: WearableSettings.darkTheme ? "#9E0C1C1F" : "#9E00414A"
+
+ readonly property color listItemBackground: WearableSettings.darkTheme ? "#00414A" : "#EAFCF3"
+
+ function imagePath(baseImagePath) {
+ return `qrc:/qt/qml/Wearable/images/${baseImagePath}.png`
+ }
+
+ function themeImagePath(baseImagePath) {
+ return `qrc:/qt/qml/Wearable/images/${baseImagePath}${(WearableSettings.darkTheme ? "-dark" : "-light")}.svg`
+ }
+ function iconPath(baseImagePath) {
+ return `qrc:/qt/qml/Wearable/icons/${baseImagePath}${(WearableSettings.darkTheme ? "-dark" : "-light")}.svg`
+ }
+}
diff --git a/examples/quickcontrols/wearable/WearableStyle/fonts/OFL.txt b/examples/quickcontrols/wearable/WearableStyle/fonts/OFL.txt
new file mode 100644
index 0000000000..330d7a2af7
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/fonts/OFL.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2009-2011 by Accademia di Belle Arti di Urbino and students of MA course of Visual design. Some rights reserved.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-Bold.ttf b/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-Bold.ttf
new file mode 100644
index 0000000000..b3d8c34f79
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-Bold.ttf
Binary files differ
diff --git a/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-Regular.ttf b/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-Regular.ttf
new file mode 100644
index 0000000000..e0e2dc888c
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-Regular.ttf
Binary files differ
diff --git a/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-SemiBold.ttf b/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-SemiBold.ttf
new file mode 100644
index 0000000000..2a1a0bca99
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/fonts/TitilliumWeb-SemiBold.ttf
Binary files differ
diff --git a/examples/quickcontrols/wearable/WearableStyle/qmldir b/examples/quickcontrols/wearable/WearableStyle/qmldir
new file mode 100644
index 0000000000..d29b115ba0
--- /dev/null
+++ b/examples/quickcontrols/wearable/WearableStyle/qmldir
@@ -0,0 +1,8 @@
+# This file only exists for qmake.
+
+module WearableStyle
+prefer :/qt/qml/WearableStyle/
+PageIndicator 254.0 PageIndicator.qml
+Slider 254.0 Slider.qml
+Switch 254.0 Switch.qml
+singleton UIStyle 254.0 UIStyle.qml
diff --git a/examples/quickcontrols/wearable/doc/images/qtquickcontrols-wearable.png b/examples/quickcontrols/wearable/doc/images/qtquickcontrols-wearable.png
index c61055340c..fced79cc52 100644
--- a/examples/quickcontrols/wearable/doc/images/qtquickcontrols-wearable.png
+++ b/examples/quickcontrols/wearable/doc/images/qtquickcontrols-wearable.png
Binary files differ
diff --git a/examples/quickcontrols/wearable/doc/src/qtquickcontrols-wearable.qdoc b/examples/quickcontrols/wearable/doc/src/qtquickcontrols-wearable.qdoc
index b6665003fd..4088a1f56e 100644
--- a/examples/quickcontrols/wearable/doc/src/qtquickcontrols-wearable.qdoc
+++ b/examples/quickcontrols/wearable/doc/src/qtquickcontrols-wearable.qdoc
@@ -7,6 +7,8 @@
\title Qt Quick Controls - Wearable Demo
\keyword Qt Quick Controls 2 - Wearable Demo
\ingroup qtquickcontrols-examples
+ \examplecategory {Embedded}
+ \examplecategory {Mobile}
\brief Demonstrates an application launcher designed for wearable devices.
\image qtquickcontrols-wearable.png
@@ -19,22 +21,17 @@
The main .qml file, \c wearable.qml, consists of an ApplicationWindow, a StackView
for a stack-based navigation model, and buttons for interactive navigation.
- \quotefromfile wearable/wearable.qml
- \skipuntil import "qml/Style"
- \printuntil id: window
- \dots
- \skipto header
- \printuntil id: homeButton
+ \snippet wearable/Wearable/Main.qml window start
+ \dots 4
+ \snippet wearable/Wearable/Main.qml stackview start
\dots 8
- \skipto onClicked
- \printuntil }
- \printuntil id: backButton
+ \snippet wearable/Wearable/Main.qml onLaunched connection
\dots 8
- \skipto onClicked
- \printuntil id: stackView
- \dots 8
- \skipuntil initialItem
- \printuntil /^\}/
+ \snippet wearable/Wearable/Main.qml stackview end
+ \snippet wearable/Wearable/Main.qml DemoMode
+ \snippet wearable/Wearable/Main.qml DemoModeIndicator
+ \snippet wearable/Wearable/Main.qml MouseArea
+ \snippet wearable/Wearable/Main.qml window end
\section1 Styling
@@ -44,79 +41,41 @@
singleton type for various styling attributes, such as fonts and colors.
\list
- \li \c qml/Style/PageIndicator.qml
- \li \c qml/Style/Slider.qml
- \li \c qml/Style/Switch.qml
- \li \c qml/Style/UIStyle.qml
+ \li \c WearableStyle/PageIndicator.qml
+ \li \c WearableStyle/Slider.qml
+ \li \c WearableStyle/Switch.qml
+ \li \c WearableStyle/UIStyle.qml
\endlist
The style is applied in \c main() in \c wearable.cpp:
\snippet wearable/wearable.cpp style
- The main benefit of using the built-in styling system is that the style selection
- is fully transparent to the application code. There is no need to import a specific
- folder that contains the styled controls. This way, the application can be run with
- other styles too.
-
- \section1 Custom Type
-
- The demo application contains a custom button type implemented in
- \c qml/NaviButton.qml. The navigation button is used as a home and back
- button in \c wearable.qml. \c NaviButton extends the AbstractButton type
- with properties that control the slide in and slide out transitions and
- the button image.
-
- \quotefromfile wearable/qml/NaviButton.qml
- \skipuntil import "Style"
- \printuntil id: image
- \dots 8
- \skipuntil }
- \printuntil background:
- \dots 8
- \skipto }
- \printuntil /^\}/
-
- \section1 Icons
-
- The demo ships a custom icon theme. The icons are bundled into the \c :/icons
- folder in the application's resources. The \c index.theme file lists the contents
- of the icon theme:
-
- \quotefile wearable/icons/wearable/index.theme
-
- Finally, the icon theme is selected in \c main():
-
- \snippet wearable/wearable.cpp icons
-
- The icons are used on the Launcher Page, which is presented below. See
- \l {Icons in Qt Quick Controls} for more details about icons and themes.
+ The main benefit of using the built-in styling system is that the style
+ selection is fully transparent to the application code. There is no need
+ to import a specific folder that contains the styled controls. This way,
+ the application can be run with other styles too.
\section1 Launcher Page
The application launcher is implemented using a circular PathView in
\c LauncherPage.qml. Each application is in a separate .qml file,
- which is added to the ListModel on the launcher page.
-
- \quotefromfile wearable/qml/LauncherPage.qml
- \skipto PathView
- \printuntil signal launched
- \dots
- \skipto ListModel
- \printuntil }
+ which are added to the ListModel on the launcher page. For some applications
+ a fallback option is provided to handle optional dependencies like QtLocation.
+
+ \snippet wearable/Wearable/LauncherPage.qml LauncherPage start
+ \dots 4
+ \snippet wearable/Wearable/LauncherPage.qml Model start
+ \snippet wearable/Wearable/LauncherPage.qml Model mid
\dots 8
- \printline ListElement
- \skipto "Settings"
- \printuntil delegate:
+ \snippet wearable/Wearable/LauncherPage.qml Model end
+ \snippet wearable/Wearable/LauncherPage.qml Delegate start
\dots 8
- \skipto icon.width
- \printuntil icon.name
+ \snippet wearable/Wearable/LauncherPage.qml Delegate mid
\dots 8
- \skipto onClicked:
- \printto path:
- \dots
- \skipto /^\}/
- \printline }
+ \snippet wearable/Wearable/LauncherPage.qml Delegate end
+ \dots 4
+ \snippet wearable/Wearable/LauncherPage.qml LauncherPage end
\section1 Applications
@@ -124,24 +83,25 @@
or communication means are typically offered by wearable devices.
Most applications have their own JavaScript files that act as dummy
- application backends. They illustrate fetching external data and help
- manipulating or converting the data. In the \c Navigation and \c Weather
- applications, data acquisition is implemented using \l XMLHttpRequest to
- read from local files. These files were generated by storing responses from
- remote servers in JSON format. This code can be easily modified to acquire
- data from remote servers.
+ application backends. They demonstrate how to fetch and manipulate or
+ convert external data. For example, the \c Weather application reads data
+ from local files using \l XMLHttpRequest. These files were generated by
+ storing responses from remote servers in JSON format. This code can be
+ easily modified to acquire data from remote servers.
\section2 Navigation
- This application displays a walking route from source to destination. This
- route information is obtained using the REST API provided by
- \l {https://www.openstreetmap.org/}. The API response is in JSON format, which
- is parsed using JavaScript by the application. Currently, it is not possible
- to specify the source and destination from within the application, but it can
- be added based on the device's capabilities. For example, user input
- could be implemented as screens with input fields, or can be communicated
- over Bluetooth/Wifi from a paired application running on another device
- such as a smart phone or PC.
+ This application uses the QtLocation module to display a route within Oslo.
+ If QtLocation is not installed, it shows a static image as the map and route
+ information based on a JSON file. Currently, it is not possible to specify
+ the source and destination from within the application, but it can be added
+ based on the device's capabilities. For example, you can collect necessary
+ info. using one of the following methods:
+ \list
+ \li Implement additional screens to collect input from user
+ \li Communicate with another device (smart phone or PC)
+ over Bluetooth or WiFi channels.
+ \endlist
\section2 Weather
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36/alarms.png b/examples/quickcontrols/wearable/icons/wearable/36x36/alarms.png
deleted file mode 100644
index 252dcf938f..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36/alarms.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36/fitness.png b/examples/quickcontrols/wearable/icons/wearable/36x36/fitness.png
deleted file mode 100644
index 4b0679e271..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36/fitness.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36/navigation.png b/examples/quickcontrols/wearable/icons/wearable/36x36/navigation.png
deleted file mode 100644
index 08e73005fd..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36/navigation.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36/notifications.png b/examples/quickcontrols/wearable/icons/wearable/36x36/notifications.png
deleted file mode 100644
index 6309312122..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36/notifications.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36/settings.png b/examples/quickcontrols/wearable/icons/wearable/36x36/settings.png
deleted file mode 100644
index c1552fb360..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36/settings.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36/weather.png b/examples/quickcontrols/wearable/icons/wearable/36x36/weather.png
deleted file mode 100644
index 61be79a24f..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36/weather.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36/worldclock.png b/examples/quickcontrols/wearable/icons/wearable/36x36/worldclock.png
deleted file mode 100644
index d23e32e512..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36/worldclock.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36@2/alarms.png b/examples/quickcontrols/wearable/icons/wearable/36x36@2/alarms.png
deleted file mode 100644
index 6047e231fc..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36@2/alarms.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36@2/fitness.png b/examples/quickcontrols/wearable/icons/wearable/36x36@2/fitness.png
deleted file mode 100644
index 1d2caa3385..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36@2/fitness.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36@2/navigation.png b/examples/quickcontrols/wearable/icons/wearable/36x36@2/navigation.png
deleted file mode 100644
index 7d4a62b9e1..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36@2/navigation.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36@2/notifications.png b/examples/quickcontrols/wearable/icons/wearable/36x36@2/notifications.png
deleted file mode 100644
index 8d3b333350..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36@2/notifications.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36@2/settings.png b/examples/quickcontrols/wearable/icons/wearable/36x36@2/settings.png
deleted file mode 100644
index e89188743d..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36@2/settings.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36@2/weather.png b/examples/quickcontrols/wearable/icons/wearable/36x36@2/weather.png
deleted file mode 100644
index 0a2ae7d54e..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36@2/weather.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/36x36@2/worldclock.png b/examples/quickcontrols/wearable/icons/wearable/36x36@2/worldclock.png
deleted file mode 100644
index ca6eb879e1..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/36x36@2/worldclock.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/icons/wearable/index.theme b/examples/quickcontrols/wearable/icons/wearable/index.theme
deleted file mode 100644
index da702d4a92..0000000000
--- a/examples/quickcontrols/wearable/icons/wearable/index.theme
+++ /dev/null
@@ -1,14 +0,0 @@
-[Icon Theme]
-Name=Wearable
-Comment=Qt Quick Controls 2 Wearable Demo Icon Theme
-
-Directories=36x36,36x36@2
-
-[36x36]
-Size=36
-Type=Fixed
-
-[36x36@2]
-Size=36
-Scale=2
-Type=Fixed
diff --git a/examples/quickcontrols/wearable/images/back.png b/examples/quickcontrols/wearable/images/back.png
deleted file mode 100644
index cf80aa65f5..0000000000
--- a/examples/quickcontrols/wearable/images/back.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/back@2x.png b/examples/quickcontrols/wearable/images/back@2x.png
deleted file mode 100644
index 02f27981e0..0000000000
--- a/examples/quickcontrols/wearable/images/back@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/back@3x.png b/examples/quickcontrols/wearable/images/back@3x.png
deleted file mode 100644
index 6d2d6cf120..0000000000
--- a/examples/quickcontrols/wearable/images/back@3x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/back@4x.png b/examples/quickcontrols/wearable/images/back@4x.png
deleted file mode 100644
index 03193681b1..0000000000
--- a/examples/quickcontrols/wearable/images/back@4x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/background-dark.png b/examples/quickcontrols/wearable/images/background-dark.png
deleted file mode 100644
index 6a8c0761aa..0000000000
--- a/examples/quickcontrols/wearable/images/background-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/background-light.png b/examples/quickcontrols/wearable/images/background-light.png
deleted file mode 100644
index 913befab82..0000000000
--- a/examples/quickcontrols/wearable/images/background-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/home.png b/examples/quickcontrols/wearable/images/home.png
deleted file mode 100644
index b6ee1459f7..0000000000
--- a/examples/quickcontrols/wearable/images/home.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/home@2x.png b/examples/quickcontrols/wearable/images/home@2x.png
deleted file mode 100644
index d5595b2378..0000000000
--- a/examples/quickcontrols/wearable/images/home@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/home@3x.png b/examples/quickcontrols/wearable/images/home@3x.png
deleted file mode 100644
index 9a5188d22b..0000000000
--- a/examples/quickcontrols/wearable/images/home@3x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/images/home@4x.png b/examples/quickcontrols/wearable/images/home@4x.png
deleted file mode 100644
index c4d2eb515a..0000000000
--- a/examples/quickcontrols/wearable/images/home@4x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Alarms/AlarmsPage.qml b/examples/quickcontrols/wearable/qml/Alarms/AlarmsPage.qml
deleted file mode 100644
index 65b34b83cb..0000000000
--- a/examples/quickcontrols/wearable/qml/Alarms/AlarmsPage.qml
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import ".."
-import "../Style"
-
-Item {
- QQC2.SwipeView {
- id: svAlarmsContainer
-
- anchors.fill: parent
-
- Repeater {
- model: ListModel {
- ListElement { name: qsTr("Week Days"); state: true; time: "06:00 AM" }
- ListElement { name: qsTr("Week Ends"); state: false; time: "07:30 AM" }
- }
-
- SwipeViewPage {
- property alias stateSwitch: stateSwitch
-
- Column {
- spacing: 30
- anchors.centerIn: parent
-
- QQC2.Switch {
- id: stateSwitch
- checked: model.state
- anchors.left: nameLabel.right
- }
-
- Text {
- text: model.time
- anchors.horizontalCenter: parent.horizontalCenter
- verticalAlignment: Text.AlignVCenter
- height: UIStyle.fontSizeXL
- font.bold: stateSwitch.checked
- font.pixelSize: stateSwitch.checked ? UIStyle.fontSizeXL : UIStyle.fontSizeL
- font.letterSpacing: 4
- color: UIStyle.themeColorQtGray1
- }
-
- Text {
- id: nameLabel
- text: model.name
- anchors.horizontalCenter: parent.horizontalCenter
- font.pixelSize: UIStyle.fontSizeS
- font.italic: true
- font.bold: true
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray2
- }
- }
- }
- }
- }
-
- QQC2.PageIndicator {
- count: svAlarmsContainer.count
- currentIndex: svAlarmsContainer.currentIndex
-
- anchors.bottom: svAlarmsContainer.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/DemoMode.qml b/examples/quickcontrols/wearable/qml/DemoMode.qml
deleted file mode 100644
index d7d4b6e61f..0000000000
--- a/examples/quickcontrols/wearable/qml/DemoMode.qml
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-
-import "Style"
-
-Item {
- property StackView stackView
-
- SequentialAnimation {
- id: demoModeAnimation
- running: settings.demoMode
-
- // Set brightness back to normal.
- ScriptAction { script: settings.brightness = 0 }
-
- // Go back to the launcher page.
- PauseAnimation { duration: 1000 }
- ScriptAction { script: stackView.pop(null) }
- PauseAnimation { duration: 2000 }
-
- // Open the world clock page.
- ScriptAction { script: stackView.currentItem.launched(Qt.resolvedUrl("WorldClock/WorldClockPage.qml")) }
- PauseAnimation { duration: 2000 }
-
- // Swipe across a few times.
- SequentialAnimation {
- loops: 6
-
- ScriptAction { script: stackView.currentItem.children[0].incrementCurrentIndex() }
- PauseAnimation { duration: 2500 }
- }
-
-
- // Go back to the launcher page.
- ScriptAction { script: stackView.pop(null) }
- PauseAnimation { duration: 2000 }
-
- // Open the navigation page.
- ScriptAction { script: stackView.currentItem.incrementCurrentIndex() }
- PauseAnimation { duration: 1000 }
- ScriptAction { script: stackView.currentItem.launched(Qt.resolvedUrl("Navigation/NavigationPage.qml")) }
- PauseAnimation { duration: 2000 }
-
- // Flick down a few times.
- SequentialAnimation {
- loops: 6
-
- ScriptAction { script: stackView.currentItem.routeListView.incrementCurrentIndex() }
- PauseAnimation { duration: 2000 }
- }
-
-
- // Go back to the launcher page.
- ScriptAction { script: stackView.pop(null) }
- PauseAnimation { duration: 2000 }
-
- // Open the weather page.
- ScriptAction { script: stackView.currentItem.incrementCurrentIndex() }
- PauseAnimation { duration: 1000 }
- ScriptAction { script: stackView.currentItem.launched(Qt.resolvedUrl("Weather/WeatherPage.qml")) }
- PauseAnimation { duration: 2000 }
-
- // Swipe across a few times.
- SequentialAnimation {
- loops: 4
-
- ScriptAction { script: stackView.currentItem.children[0].incrementCurrentIndex() }
- PauseAnimation { duration: 2000 }
- }
-
-
- // Go back to the launcher page.
- ScriptAction { script: stackView.pop(null) }
- PauseAnimation { duration: 2000 }
-
- // Open the fitness page.
- ScriptAction { script: stackView.currentItem.incrementCurrentIndex() }
- PauseAnimation { duration: 1000 }
- ScriptAction { script: stackView.currentItem.launched(Qt.resolvedUrl("Fitness/FitnessPage.qml")) }
- PauseAnimation { duration: 2000 }
-
- // Swipe across a few times.
- SequentialAnimation {
- loops: 2
-
- ScriptAction { script: stackView.currentItem.children[0].incrementCurrentIndex() }
- PauseAnimation { duration: 2000 }
- }
-
-
- // Go back to the launcher page.
- ScriptAction { script: stackView.pop(null) }
- PauseAnimation { duration: 2000 }
-
- // Open the notifications page.
- ScriptAction { script: stackView.currentItem.incrementCurrentIndex() }
- PauseAnimation { duration: 1000 }
- ScriptAction { script: stackView.currentItem.launched(Qt.resolvedUrl("Notifications/NotificationsPage.qml")) }
-
- // Flick down a few times.
- SequentialAnimation {
- loops: 3
-
- PauseAnimation { duration: 2000 }
- ScriptAction { script: stackView.currentItem.incrementCurrentIndex() }
- }
-
-
- // Go back to the launcher page.
- ScriptAction { script: stackView.pop(null) }
- PauseAnimation { duration: 2000 }
-
- // Open the alarms page.
- ScriptAction { script: stackView.currentItem.incrementCurrentIndex() }
- PauseAnimation { duration: 1000 }
- ScriptAction { script: stackView.currentItem.launched(Qt.resolvedUrl("Alarms/AlarmsPage.qml")) }
- PauseAnimation { duration: 2000 }
-
- // Toggle the switch.
- ScriptAction { script: stackView.currentItem.children[0].currentItem.stateSwitch.toggle() }
- PauseAnimation { duration: 2000 }
-
- // Go to the next alarm.
- ScriptAction { script: stackView.currentItem.children[0].incrementCurrentIndex() }
- PauseAnimation { duration: 2000 }
-
- // Toggle the switch there too.
- ScriptAction { script: stackView.currentItem.children[0].currentItem.stateSwitch.toggle() }
- PauseAnimation { duration: 2000 }
-
-
- // Go back to the launcher page.
- ScriptAction { script: stackView.pop(null) }
- PauseAnimation { duration: 2000 }
-
- // Open the settings page.
- ScriptAction { script: stackView.currentItem.incrementCurrentIndex() }
- PauseAnimation { duration: 1000 }
- ScriptAction { script: stackView.currentItem.launched(Qt.resolvedUrl("Settings/SettingsPage.qml")) }
- PauseAnimation { duration: 3000 }
-
- // Toggle the switches.
- ScriptAction { script: stackView.currentItem.children[0].currentItem.bluetoothSwitch.toggle() }
- PauseAnimation { duration: 1000 }
- ScriptAction { script: stackView.currentItem.children[0].currentItem.wirelessSwitch.toggle() }
- PauseAnimation { duration: 3000 }
-
- // Go to the next page.
- ScriptAction { script: stackView.currentItem.children[0].incrementCurrentIndex() }
-
- // Play with the brightness slider.
- // First, set it to full brightness so we start in the correct state.
- ScriptAction {
- script: {
- var brightnessSlider = stackView.currentItem.children[0].currentItem.brightnessSlider
- brightnessSlider.value = 0
- // increase()/decrease() are not a result of user interaction and
- // hence moved() will not be emitted, so we do it ourselves.
- brightnessSlider.moved()
- }
- }
-
- // Decrease the brightness.
- SequentialAnimation {
- loops: 3
-
- PauseAnimation { duration: 1000 }
- ScriptAction {
- script: {
- var brightnessSlider = stackView.currentItem.children[0].currentItem.brightnessSlider
- brightnessSlider.decrease()
- brightnessSlider.moved()
- }
- }
- }
-
- // Increase the brightness back to full.
- PauseAnimation { duration: 3000 }
- SequentialAnimation {
- loops: 3
-
- PauseAnimation { duration: 1000 }
- ScriptAction {
- script: {
- var brightnessSlider = stackView.currentItem.children[0].currentItem.brightnessSlider
- brightnessSlider.increase()
- brightnessSlider.moved()
- }
- }
- }
-
- // Toggle the dark theme switch.
- PauseAnimation { duration: 2000 }
- ScriptAction {
- script: {
- var darkThemeSwitch = stackView.currentItem.children[0].currentItem.darkThemeSwitch
- darkThemeSwitch.toggle()
- // As above, only proper user interaction results in toggled() being emitted,
- // so we do it ourselves.
- darkThemeSwitch.toggled()
- }
- }
- PauseAnimation { duration: 4000 }
-
- // Go back to the launcher page.
- ScriptAction { script: stackView.pop(null) }
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/DemoModeIndicator.qml b/examples/quickcontrols/wearable/qml/DemoModeIndicator.qml
deleted file mode 100644
index 977c600653..0000000000
--- a/examples/quickcontrols/wearable/qml/DemoModeIndicator.qml
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import "Style"
-
-Item {
- id: root
- width: row.implicitWidth + margins * 2
-
- readonly property int topMargin: 24
- readonly property int margins: 12
-
- Behavior on y {
- NumberAnimation {}
- }
-
- Rectangle {
- id: demoModeIndicatorBg
- anchors.fill: parent
- anchors.topMargin: -topMargin
- radius: 20
- color: UIStyle.colorRed
- }
-
- Row {
- id: row
- spacing: 8
- anchors.fill: parent
- anchors.leftMargin: margins
- anchors.rightMargin: margins
-
- Image {
- source: "Settings/images/demo-mode-white.png"
- width: height
- height: instructionLabel.height * 2
- anchors.verticalCenter: parent.verticalCenter
- }
- QQC2.Label {
- id: instructionLabel
- text: "Tap screen to use"
- color: UIStyle.colorQtGray10
- anchors.verticalCenter: parent.verticalCenter
- }
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Fitness/FitnessPage.qml b/examples/quickcontrols/wearable/qml/Fitness/FitnessPage.qml
deleted file mode 100644
index c1047de9aa..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/FitnessPage.qml
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import ".."
-import "../Style"
-import "fitness.js" as FitnessData
-
-Item {
- QQC2.SwipeView {
- id: svFitnessContainer
-
- anchors.fill: parent
-
- SwipeViewPage {
- id: fitnessPage1
-
- Column {
- anchors.centerIn: parent
- spacing: 15
-
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- text: qsTr("Steps: ") + FitnessData.getSteps()
- font.italic: true
- font.pixelSize: UIStyle.fontSizeM
- color: UIStyle.themeColorQtGray1
- }
- Image {
- anchors.horizontalCenter: parent.horizontalCenter
- source: UIStyle.themeImagePath("images/man-walking")
- }
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- text: qsTr("Calories: ") + FitnessData.getCalories()
- font.pixelSize: UIStyle.fontSizeS
- font.italic: true
- color: UIStyle.themeColorQtGray3
- }
- }
- }
-
- SwipeViewPage {
- id: fitnessPage2
-
- Column {
- anchors.centerIn: parent
- spacing: 15
-
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- text: qsTr("Distance: ")
- + FitnessData.getDistance()
- + qsTr(" miles")
- font.italic: true
- font.pixelSize: UIStyle.fontSizeM
- color: UIStyle.themeColorQtGray1
- }
- Image {
- anchors.horizontalCenter: parent.horizontalCenter
- source: UIStyle.themeImagePath("images/man-running")
- }
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- text: qsTr("Time: ")
- + FitnessData.getTime()
- + qsTr(" mins")
- font.pixelSize: UIStyle.fontSizeS
- font.italic: true
- color: UIStyle.themeColorQtGray3
- }
- }
- }
- }
-
- QQC2.PageIndicator {
- count: svFitnessContainer.count
- currentIndex: svFitnessContainer.currentIndex
-
- anchors.bottom: svFitnessContainer.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Fitness/fitness.js b/examples/quickcontrols/wearable/qml/Fitness/fitness.js
deleted file mode 100644
index 95ce06cdc1..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/fitness.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-function getSteps() {
- return 105;
-}
-
-function getCalories() {
- return 150;
-}
-
-function getDistance() {
- return 10;
-}
-
-function getTime() {
- return "00:30";
-}
diff --git a/examples/quickcontrols/wearable/qml/Fitness/images/man-running-dark.png b/examples/quickcontrols/wearable/qml/Fitness/images/man-running-dark.png
deleted file mode 100644
index 6c791d6170..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/images/man-running-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Fitness/images/man-running-dark@2x.png b/examples/quickcontrols/wearable/qml/Fitness/images/man-running-dark@2x.png
deleted file mode 100644
index e9abae9de4..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/images/man-running-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Fitness/images/man-running-light.png b/examples/quickcontrols/wearable/qml/Fitness/images/man-running-light.png
deleted file mode 100644
index e7eef12cb3..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/images/man-running-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Fitness/images/man-running-light@2x.png b/examples/quickcontrols/wearable/qml/Fitness/images/man-running-light@2x.png
deleted file mode 100644
index 79861f5718..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/images/man-running-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-dark.png b/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-dark.png
deleted file mode 100644
index 6d455b4925..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-dark@2x.png b/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-dark@2x.png
deleted file mode 100644
index a3ac08baf3..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-light.png b/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-light.png
deleted file mode 100644
index f963f8897b..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-light@2x.png b/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-light@2x.png
deleted file mode 100644
index 1b4d7592d6..0000000000
--- a/examples/quickcontrols/wearable/qml/Fitness/images/man-walking-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/LauncherPage.qml b/examples/quickcontrols/wearable/qml/LauncherPage.qml
deleted file mode 100644
index 4017a81c57..0000000000
--- a/examples/quickcontrols/wearable/qml/LauncherPage.qml
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import "Style"
-
-PathView {
- id: circularView
-
- signal launched(string page)
-
- readonly property int cX: width / 2
- readonly property int cY: height / 2
- readonly property int itemSize: size / 4
- readonly property int size: Math.min(width - 80, height)
- readonly property int radius: size / 2 - itemSize / 3
-
- snapMode: PathView.SnapToItem
-
- model: ListModel {
- ListElement {
- title: qsTr("World Clock")
- icon: "worldclock"
- page: "WorldClock/WorldClockPage.qml"
- }
- ListElement {
- title: qsTr("Navigation")
- icon: "navigation"
- page: "Navigation/NavigationPage.qml"
- }
- ListElement {
- title: qsTr("Weather")
- icon: "weather"
- page: "Weather/WeatherPage.qml"
- }
- ListElement {
- title: qsTr("Fitness")
- icon: "fitness"
- page: "Fitness/FitnessPage.qml"
- }
- ListElement {
- title: qsTr("Notifications")
- icon: "notifications"
- page: "Notifications/NotificationsPage.qml"
- }
- ListElement {
- title: qsTr("Alarm")
- icon: "alarms"
- page: "Alarms/AlarmsPage.qml"
- }
- ListElement {
- title: qsTr("Settings")
- icon: "settings"
- page: "Settings/SettingsPage.qml"
- }
- }
-
- delegate: QQC2.RoundButton {
- width: circularView.itemSize
- height: circularView.itemSize
-
- property string title: model.title
-
- icon.width: 36
- icon.height: 36
- icon.name: model.icon
- opacity: PathView.itemOpacity
- padding: 12
-
- background: Rectangle {
- radius: width / 2
- border.width: 3
- border.color: parent.PathView.isCurrentItem ? UIStyle.colorQtPrimGreen : UIStyle.themeColorQtGray4
- }
-
- onClicked: {
- if (PathView.isCurrentItem)
- circularView.launched(Qt.resolvedUrl(page))
- else
- circularView.currentIndex = index
- }
- }
-
- path: Path {
- startX: circularView.cX
- startY: circularView.cY
- PathAttribute {
- name: "itemOpacity"
- value: 1.0
- }
- PathLine {
- x: circularView.cX + circularView.radius
- y: circularView.cY
- }
- PathAttribute {
- name: "itemOpacity"
- value: 0.7
- }
- PathArc {
- x: circularView.cX - circularView.radius
- y: circularView.cY
- radiusX: circularView.radius
- radiusY: circularView.radius
- useLargeArc: true
- direction: PathArc.Clockwise
- }
- PathAttribute {
- name: "itemOpacity"
- value: 0.5
- }
- PathArc {
- x: circularView.cX + circularView.radius
- y: circularView.cY
- radiusX: circularView.radius
- radiusY: circularView.radius
- useLargeArc: true
- direction: PathArc.Clockwise
- }
- PathAttribute {
- name: "itemOpacity"
- value: 0.3
- }
- }
-
- Text {
- id: appTitle
-
- property Item currentItem: circularView.currentItem
-
- visible: currentItem ? currentItem.PathView.itemOpacity === 1.0 : 0
-
- text: currentItem ? currentItem.title : ""
- anchors.centerIn: parent
- anchors.verticalCenterOffset: (circularView.itemSize + height) / 2
-
- font.bold: true
- font.pixelSize: circularView.itemSize / 3
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/NaviButton.qml b/examples/quickcontrols/wearable/qml/NaviButton.qml
deleted file mode 100644
index 3c1c30fb7f..0000000000
--- a/examples/quickcontrols/wearable/qml/NaviButton.qml
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import "Style"
-
-QQC2.AbstractButton {
- id: button
-
- property int edge: Qt.TopEdge
- property alias imageSource: image.source
-
- contentItem: Image {
- id: image
- fillMode: Image.Pad
- sourceSize { width: 40; height: 40 } // ### TODO: resize the image
- }
-
- background: Rectangle {
- height: button.height * 4
- width: height
- radius: width / 2
-
- anchors.horizontalCenter: button.horizontalCenter
- anchors.top: edge === Qt.BottomEdge ? button.top : undefined
- anchors.bottom: edge === Qt.TopEdge ? button.bottom : undefined
-
- color: UIStyle.colorQtGray2
- }
-
- transform: Translate {
- Behavior on y { NumberAnimation { } }
- y: enabled ? 0 : edge === Qt.TopEdge ? -button.height : button.height
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Navigation/NavigationPage.qml b/examples/quickcontrols/wearable/qml/Navigation/NavigationPage.qml
deleted file mode 100644
index f9fdd61b47..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/NavigationPage.qml
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import "../Style"
-import "navigation.js" as NavigationData
-
-Item {
- property alias routeListView: routeView
-
- Column {
- anchors.fill: parent
- anchors.margins: 2
- spacing: 2
-
- Rectangle {
- anchors.horizontalCenter: parent.horizontalCenter
- width: parent.width
- height: titleRow.height
-
- color: UIStyle.themeColorQtGray9
-
- Row {
- id: titleRow
- spacing: 10
- anchors.centerIn: parent
-
- Image {
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/navigation")
- fillMode: Image.PreserveAspectCrop
- }
- Text {
- anchors.verticalCenter: parent.verticalCenter
- text: qsTr("Walking")
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 2
- color: UIStyle.themeColorQtGray2
- }
- }
- }
-
- ListModel {
- id: routeModel
- }
-
- ListView {
- id: routeView
-
- width: parent.width
- height: parent.height - titleRow.height - parent.spacing
- property var imageList: [UIStyle.themeImagePath("images/straight"),
- UIStyle.themeImagePath("images/leftturn"),
- UIStyle.themeImagePath("images/rightturn"),
- "images/uturn.png",
- "images/start.png",
- "images/end.png"]
-
- clip: true
- focus: true
- boundsBehavior: Flickable.StopAtBounds
- snapMode: ListView.SnapToItem
- model: routeModel
- delegate: RouteElement {
- width: routeView.width
- height: routeView.height
- }
- }
- }
- Component.onCompleted: {
- NavigationData.requestNavigationRoute(routeModel)
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Navigation/RouteElement.qml b/examples/quickcontrols/wearable/qml/Navigation/RouteElement.qml
deleted file mode 100644
index dd4aebbb2f..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/RouteElement.qml
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import "../Style"
-
-Rectangle {
- color: UIStyle.themeColorQtGray8
-
- Row {
- spacing: 5
- width: parent.width - 80
- anchors.centerIn: parent
-
- Image {
- id: img
- anchors.verticalCenter: parent.verticalCenter
- source: navImage
- fillMode: Image.PreserveAspectFit
- }
-
- Column {
- spacing: 5
- width: parent.width - img.width
- anchors.verticalCenter: parent.verticalCenter
-
- Text {
- width: parent.width
- wrapMode: Text.WordWrap
- text: navInstruction
- font.pixelSize: UIStyle.fontSizeS
- verticalAlignment: Text.AlignVCenter
- padding: 1
- color: UIStyle.themeColorQtGray1
- }
-
- Text {
- width: parent.width
- wrapMode: Text.WordWrap
- text: navAuxInfo
- font.pixelSize: UIStyle.fontSizeXS
- verticalAlignment: Text.AlignVCenter
- padding: 1
- color: UIStyle.themeColorQtGray2
- }
- }
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/end.png b/examples/quickcontrols/wearable/qml/Navigation/images/end.png
deleted file mode 100644
index 4805ad8207..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/end.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/end@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/end@2x.png
deleted file mode 100644
index e91d537465..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/end@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-dark.png b/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-dark.png
deleted file mode 100644
index 66d258f5ac..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-dark@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-dark@2x.png
deleted file mode 100644
index 639deb81cf..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-light.png b/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-light.png
deleted file mode 100644
index 1346e8eaff..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-light@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-light@2x.png
deleted file mode 100644
index 268a6283f9..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/leftturn-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/marker.png b/examples/quickcontrols/wearable/qml/Navigation/images/marker.png
deleted file mode 100644
index 4805ad8207..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/marker.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/navigation-dark.png b/examples/quickcontrols/wearable/qml/Navigation/images/navigation-dark.png
deleted file mode 100644
index b80b7b610f..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/navigation-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/navigation-dark@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/navigation-dark@2x.png
deleted file mode 100644
index b198aa3d9a..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/navigation-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/navigation-light.png b/examples/quickcontrols/wearable/qml/Navigation/images/navigation-light.png
deleted file mode 100644
index 08e73005fd..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/navigation-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/navigation-light@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/navigation-light@2x.png
deleted file mode 100644
index 7d4a62b9e1..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/navigation-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-dark.png b/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-dark.png
deleted file mode 100644
index 6a779dadc4..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-dark@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-dark@2x.png
deleted file mode 100644
index 13143c2e2c..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-light.png b/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-light.png
deleted file mode 100644
index a6be08c1a1..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-light@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-light@2x.png
deleted file mode 100644
index 58f04b5052..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/rightturn-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/start.png b/examples/quickcontrols/wearable/qml/Navigation/images/start.png
deleted file mode 100644
index 41de60e60c..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/start.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/start@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/start@2x.png
deleted file mode 100644
index 6fe3ba9dbd..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/start@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/straight-dark.png b/examples/quickcontrols/wearable/qml/Navigation/images/straight-dark.png
deleted file mode 100644
index 3f4ab66430..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/straight-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/straight-dark@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/straight-dark@2x.png
deleted file mode 100644
index f5baa6da42..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/straight-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/straight-light.png b/examples/quickcontrols/wearable/qml/Navigation/images/straight-light.png
deleted file mode 100644
index 1321b97aa5..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/straight-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/straight-light@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/straight-light@2x.png
deleted file mode 100644
index a2d3c4c2ab..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/straight-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/uturn.png b/examples/quickcontrols/wearable/qml/Navigation/images/uturn.png
deleted file mode 100644
index 596046ca9a..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/uturn.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/images/uturn@2x.png b/examples/quickcontrols/wearable/qml/Navigation/images/uturn@2x.png
deleted file mode 100644
index 659f103573..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/images/uturn@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Navigation/navigation.js b/examples/quickcontrols/wearable/qml/Navigation/navigation.js
deleted file mode 100644
index e82cccbea5..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/navigation.js
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-function requestNavigationRoute(rModel) {
- var xhr = new XMLHttpRequest;
- xhr.open("GET", "walk_route.json");
- xhr.onreadystatechange = function () {
- if (xhr.readyState === XMLHttpRequest.DONE) {
- var a = JSON.parse(xhr.responseText);
- var steps = a.routes[0].legs[0].steps;
-
- for (var step in steps) {
- var maneuver = steps[step].maneuver;
- var duration = steps[step].duration;
- var distance = steps[step].distance;
-
- rModel.append({
- navInstruction: maneuver.instruction,
- navImage: getNavigationImage(
- maneuver.type,
- maneuver.modifier,
- routeView.imageList),
- navAuxInfo: getAuxInfo(distance,
- duration)
- });
- }
- }
- }
- xhr.send();
-}
-
-function getNavigationImage(maneuverType, maneuverModifier, imageList) {
- var imageToReturn;
- if (maneuverType === "depart") {
- imageToReturn = imageList[4];
- } else if (maneuverType === "arrive") {
- imageToReturn = imageList[5];
- } else if (maneuverType === "turn") {
- if (maneuverModifier.search("left") >= 0)
- imageToReturn = imageList[1];
- else if (maneuverModifier.search("right") >= 0)
- imageToReturn = imageList[2];
- else
- imageToReturn = imageList[0];
- } else {
- if (maneuverModifier === "uturn") {
- imageToReturn = imageList[3];
- } else {
- imageToReturn = imageList[0];
- }
- }
-
- return imageToReturn;
-}
-
-function getAuxInfo(distInMeters, timeInSecs) {
- var distance = convertDistance(distInMeters);
- if (distance.length > 0)
- return "Distance: " + distance + "\nTime: " + formatSeconds(
- timeInSecs);
- else
- return "";
-}
-
-function convertDistance(meter) {
- var dist = "";
- var feet = (meter * 0.3048).toPrecision(6);
- var miles = (meter * 0.000621371).toPrecision(6);
-
- if (Math.floor(miles) > 1) {
- dist += Math.floor(miles) + " mi";
- feet = ((miles - Math.floor(miles)) * 0.3048).toPrecision(6);
- }
- if (Math.floor(feet) > 1)
- dist += Math.floor(feet) + " ft";
-
- return dist
-}
-
-function formatSeconds(seconds) {
- var date = new Date(1970, 0, 1);
- date.setSeconds(seconds);
- return date.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1");
-}
diff --git a/examples/quickcontrols/wearable/qml/Navigation/walk_route.json b/examples/quickcontrols/wearable/qml/Navigation/walk_route.json
deleted file mode 100644
index e69b5cc187..0000000000
--- a/examples/quickcontrols/wearable/qml/Navigation/walk_route.json
+++ /dev/null
@@ -1 +0,0 @@
-{"routes":[{"legs":[{"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[144],"location":[-122.394553,37.795984]},{"out":1,"location":[-122.393896,37.795268],"bearings":[60,150,225,330],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.393179,37.79453],"bearings":[60,135,180,225,315],"entry":[true,true,true,true,false],"in":4},{"out":1,"location":[-122.393072,37.79443],"bearings":[45,135,225,315],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.392787,37.79415],"bearings":[60,135,315],"entry":[true,true,false],"in":2},{"out":1,"location":[-122.392537,37.793915],"bearings":[45,135,225,315],"entry":[true,true,true,false],"in":3},{"out":0,"location":[-122.392448,37.793831],"bearings":[135,225,315],"entry":[true,true,false],"in":2}],"geometry":"{_ueF|d`jVRQdB}ARQh@g@XUVWVW^a@RUv@w@l@q@PQNQ","maneuver":{"bearing_after":144,"location":[-122.394553,37.795984],"bearing_before":0,"type":"depart","instruction":"Head southeast on The Embarcadero"},"duration":230.8,"distance":314.8,"name":"The Embarcadero","mode":"walking"},{"intersections":[{"out":2,"location":[-122.392361,37.793748],"bearings":[45,135,225,315],"entry":[true,true,true,false],"in":3}],"geometry":"}qteFfw_jVHJBDHJ","maneuver":{"bearing_after":225,"type":"turn","modifier":"right","bearing_before":140,"location":[-122.392361,37.793748],"instruction":"Turn right"},"duration":13.2,"distance":18.5,"name":"","mode":"walking"},{"intersections":[{"out":1,"location":[-122.39251,37.793631],"bearings":[45,135,225,315],"entry":[false,true,true,true],"in":0},{"out":1,"location":[-122.391284,37.792459],"bearings":[45,150,225,330],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.391217,37.792342],"bearings":[45,165,225,330],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.391165,37.792231],"bearings":[45,165,225,345],"entry":[true,true,true,false],"in":3},{"out":0,"location":[-122.390884,37.791536],"bearings":[150,285,345],"entry":[true,true,false],"in":2},{"out":1,"location":[-122.390254,37.790827],"bearings":[45,135,210,315],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.390144,37.790745],"bearings":[45,135,225,315],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.390038,37.790665],"bearings":[45,135,240,315],"entry":[true,true,true,false],"in":3},{"out":0,"location":[-122.389245,37.790036],"bearings":[135,225,315],"entry":[true,true,false],"in":2},{"out":1,"location":[-122.38871,37.789596],"bearings":[45,150,210,315],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.388618,37.789494],"bearings":[45,150,225,330],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.388527,37.789394],"bearings":[45,150,225,330],"entry":[true,true,true,false],"in":3},{"out":1,"location":[-122.388005,37.787907],"bearings":[0,180,270],"entry":[false,true,true],"in":0},{"out":1,"location":[-122.387985,37.787622],"bearings":[0,180,240],"entry":[false,true,true],"in":0},{"out":2,"location":[-122.388005,37.787197],"bearings":[0,90,180,270],"entry":[false,true,true,true],"in":0},{"out":2,"location":[-122.388015,37.787093],"bearings":[0,90,180,270],"entry":[false,true,true,true],"in":0},{"out":2,"location":[-122.388022,37.786994],"bearings":[0,90,180,285],"entry":[false,true,true,true],"in":0},{"out":1,"location":[-122.388163,37.785186],"bearings":[0,180,315],"entry":[false,true,true],"in":0},{"out":1,"location":[-122.388184,37.784944],"bearings":[0,180,315],"entry":[false,true,true],"in":0},{"out":2,"location":[-122.388191,37.784855],"bearings":[0,90,180,270],"entry":[false,true,true,true],"in":0},{"out":2,"location":[-122.388198,37.784748],"bearings":[0,90,180,270],"entry":[false,true,true,true],"in":0},{"out":2,"location":[-122.388207,37.784633],"bearings":[0,90,180,270],"entry":[false,true,true,true],"in":0},{"out":2,"location":[-122.388309,37.783373],"bearings":[0,60,180,270],"entry":[false,true,true,true],"in":0},{"out":1,"location":[-122.388376,37.782545],"bearings":[0,180,315],"entry":[false,true,true],"in":0},{"out":1,"location":[-122.388394,37.782316],"bearings":[0,180,315],"entry":[false,true,true],"in":0}],"geometry":"eqteFdx_jVf@k@\\a@|@iAV[FGJMPOTOPKDAPITItAa@r@WXOXQXSTUTWRYNUNS|B}CdAwAPSTQRQ\\W^Q\\KZIrB]^EPCH?\\CZ?Z?\\?XBT@R?~IZF?H@f@@N@T@V@zFRbDLl@@pADV@","maneuver":{"bearing_after":139,"type":"turn","modifier":"left","bearing_before":226,"location":[-122.39251,37.793631],"instruction":"Turn left onto The Embarcadero"},"duration":1055.9,"distance":1450.3,"name":"The Embarcadero","mode":"walking"},{"intersections":[{"out":2,"location":[-122.388431,37.781791],"bearings":[0,90,180,285],"entry":[false,true,true,true],"in":0},{"out":2,"location":[-122.389672,37.779922],"bearings":[45,135,225,315],"entry":[false,true,true,true],"in":0},{"out":1,"location":[-122.390803,37.77905],"bearings":[45,225,315],"entry":[false,true,true],"in":0},{"out":2,"location":[-122.391916,37.778155],"bearings":[45,135,225,315],"entry":[false,true,true,true],"in":0},{"out":2,"location":[-122.393073,37.777251],"bearings":[45,75,225,315],"entry":[false,true,true,true],"in":0}],"geometry":"egreFt~~iVR@bABXDVDXHVNXNXVVZp@|@z@hAnCvDLRNTHLFJbArAzApBdB`CTZZb@\\b@@@pCvD","maneuver":{"bearing_after":183,"type":"new name","modifier":"straight","bearing_before":183,"location":[-122.388431,37.781791],"instruction":"Continue straight onto King Street"},"duration":567.4,"distance":782.5,"name":"King Street","mode":"walking"},{"intersections":[{"out":3,"location":[-122.394003,37.776513],"bearings":[45,135,225,300],"entry":[false,true,true,true],"in":0},{"out":2,"location":[-122.394218,37.776486],"bearings":[45,135,225,315],"entry":[false,true,true,true],"in":0}],"geometry":"efqeFna`jVGJ?NJNNR","maneuver":{"bearing_after":313,"type":"turn","modifier":"straight","bearing_before":225,"location":[-122.394003,37.776513],"instruction":"Go straight"},"duration":26.1,"distance":36.2,"name":"","mode":"walking"},{"intersections":[{"out":2,"location":[-122.394321,37.776405],"bearings":[45,180,315],"entry":[false,true,true],"in":0},{"out":1,"location":[-122.394729,37.776487],"bearings":[45,180],"entry":[false,true],"in":0}],"geometry":"qeqeFnc`jVe@p@A@V\\DF","maneuver":{"bearing_after":315,"type":"turn","modifier":"right","bearing_before":225,"location":[-122.394321,37.776405],"instruction":"Turn right"},"duration":39.9,"distance":55.6,"name":"","mode":"walking"},{"intersections":[{"in":0,"entry":[true],"bearings":[45],"location":[-122.394768,37.776455]}],"geometry":"{eqeFhf`jV","maneuver":{"bearing_after":0,"type":"arrive","modifier":"straight","bearing_before":225,"location":[-122.394729,37.776487],"instruction":"You have arrived at your destination, straight ahead"},"duration":0,"distance":0,"name":"","mode":"walking"}],"summary":"The Embarcadero, King Street","duration":1933.3,"distance":2657.8}],"geometry":"{_ueF|d`jV|LuLV\\`EyElHaD~I_LvB_AdGk@he@|AbBd@bZl`@R~@g@r@\\d@","duration":1933.3,"distance":2657.8}],"waypoints":[{"name":"The Embarcadero","location":[-122.394553,37.795984]},{"name":"","location":[-122.394768,37.776455]}],"code":"Ok"}
diff --git a/examples/quickcontrols/wearable/qml/Notifications/NotificationsPage.qml b/examples/quickcontrols/wearable/qml/Notifications/NotificationsPage.qml
deleted file mode 100644
index 6cbf2d92cf..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/NotificationsPage.qml
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import "../Style"
-import "notifications.js" as NotificationData
-
-ListView {
- id: missedCallsView
-
- clip: true
- focus: true
- boundsBehavior: Flickable.StopAtBounds
- snapMode: ListView.SnapToItem
-
- model: ListModel {
- id: missedCallsList
- }
-
- Image {
- id: missedCallIcon
- width: parent.width / 2
- anchors.right: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/missedcall")
- fillMode: Image.Pad
- }
-
- delegate: Item {
- height: missedCallsView.height
- width: missedCallsView.width / 2
- anchors.left: missedCallsView.contentItem.horizontalCenter
-
- Column {
- spacing: 15
- anchors.verticalCenter: parent.verticalCenter
-
- Image {
- anchors.horizontalCenter: parent.horizontalCenter
- source: qsTr("images/avatar%1-%2.png").arg(model.gender).arg(UIStyle.darkTheme ? "dark" : "light")
- }
-
- Text {
- text: model.name
- anchors.horizontalCenter: parent.horizontalCenter
- font.bold: true
- font.pixelSize: UIStyle.fontSizeS
- color: UIStyle.themeColorQtGray1
- }
-
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- text: date + " " + time
- font.pixelSize: UIStyle.fontSizeXS
- font.italic: true
- color: UIStyle.themeColorQtGray2
- }
- }
- }
-
- Component.onCompleted: {
- NotificationData.populateData(missedCallsList)
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-dark.png b/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-dark.png
deleted file mode 100644
index 37bc1f291b..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-dark@2x.png b/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-dark@2x.png
deleted file mode 100644
index efcd7cba74..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-light.png b/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-light.png
deleted file mode 100644
index e38b260724..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-light@2x.png b/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-light@2x.png
deleted file mode 100644
index 1f28beac6c..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/avatarf-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-dark.png b/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-dark.png
deleted file mode 100644
index 5a2a394d8c..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-dark@2x.png b/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-dark@2x.png
deleted file mode 100644
index 1a19b0fc8e..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-light.png b/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-light.png
deleted file mode 100644
index f0fc7a9606..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-light@2x.png b/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-light@2x.png
deleted file mode 100644
index 64a0f1f9ac..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/avatarm-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-dark.png b/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-dark.png
deleted file mode 100644
index 26bcf98ad3..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-dark@2x.png b/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-dark@2x.png
deleted file mode 100644
index 5eb54ce809..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-light.png b/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-light.png
deleted file mode 100644
index 641e1f0c43..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-light@2x.png b/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-light@2x.png
deleted file mode 100644
index e8d2d81c9a..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/images/missedcall-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Notifications/notifications.js b/examples/quickcontrols/wearable/qml/Notifications/notifications.js
deleted file mode 100644
index 62f46e0a0b..0000000000
--- a/examples/quickcontrols/wearable/qml/Notifications/notifications.js
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-function populateData(listData) {
- listData.append({
- name: "John Doe",
- gender: "m",
- date: "02/15/2017",
- time: "09:20 am"
- });
-
- listData.append({
- name: "Jane Worldege",
- gender: "f",
- date: "02/06/2017",
- time: "10:15 am"
- });
-
- listData.append({
- name: "Jennifer Wang",
- gender: "f",
- date: "02/03/2017",
- time: "05:16 pm"
- });
-}
diff --git a/examples/quickcontrols/wearable/qml/Settings/SettingsPage.qml b/examples/quickcontrols/wearable/qml/Settings/SettingsPage.qml
deleted file mode 100644
index 68fb17990d..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/SettingsPage.qml
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import ".."
-import "../Style"
-
-Item {
-
- QQC2.SwipeView {
- id: svSettingsContainer
-
- anchors.fill: parent
-
- SwipeViewPage {
- id: settingsPage1
-
- property alias bluetoothSwitch: bluetoothSwitch
- property alias wirelessSwitch: wirelessSwitch
-
- Column {
- anchors.centerIn: parent
- spacing: 25
-
- Row {
- spacing: 50
- Image {
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/bluetooth")
- }
- QQC2.Switch {
- id: bluetoothSwitch
- anchors.verticalCenter: parent.verticalCenter
- checked: settings.bluetooth
- onToggled: settings.bluetooth = checked
- }
- }
- Row {
- spacing: 50
- Image {
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/wifi")
- }
- QQC2.Switch {
- id: wirelessSwitch
- anchors.verticalCenter: parent.verticalCenter
- checked: settings.wireless
- onToggled: settings.wireless = checked
- }
- }
- }
- }
-
- SwipeViewPage {
- id: settingsPage2
-
- property alias brightnessSlider: brightnessSlider
- property alias darkThemeSwitch: darkThemeSwitch
-
- Column {
- anchors.centerIn: parent
- spacing: 2
-
- Column {
- Image {
- anchors.horizontalCenter: parent.horizontalCenter
- source: UIStyle.themeImagePath("images/brightness")
- }
- QQC2.Slider {
- id: brightnessSlider
- anchors.horizontalCenter: parent.horizontalCenter
- from: 0
- to: 5
- stepSize: 1
- value: settings.brightness
- onMoved: settings.brightness = value
- }
- }
- Column {
- anchors.horizontalCenter: parent.horizontalCenter
-
- Image {
- anchors.horizontalCenter: parent.horizontalCenter
- source: UIStyle.themeImagePath("images/theme")
- }
- QQC2.Switch {
- id: darkThemeSwitch
- anchors.horizontalCenter: parent.horizontalCenter
- checked: settings.darkTheme
- onToggled: settings.darkTheme = checked
- }
- }
- }
- }
-
- SwipeViewPage {
- id: settingsPage3
-
- Column {
- anchors.centerIn: parent
-
- Column {
- anchors.horizontalCenter: parent.horizontalCenter
- spacing: 6
-
- Image {
- anchors.horizontalCenter: parent.horizontalCenter
- source: UIStyle.themeImagePath("images/demo-mode")
- }
- QQC2.Switch {
- id: demoModeSwitch
- anchors.horizontalCenter: parent.horizontalCenter
- checked: settings.demoMode
- onToggled: settings.demoMode = checked
- }
- }
- }
- }
- }
-
- QQC2.PageIndicator {
- count: svSettingsContainer.count
- currentIndex: svSettingsContainer.currentIndex
-
- anchors.bottom: svSettingsContainer.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-dark.png b/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-dark.png
deleted file mode 100644
index 0f1f4d0a54..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-dark@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-dark@2x.png
deleted file mode 100644
index 7496fc5685..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-light.png b/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-light.png
deleted file mode 100644
index 6e297cfab3..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-light@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-light@2x.png
deleted file mode 100644
index 62d084032f..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/bluetooth-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/brightness-dark.png b/examples/quickcontrols/wearable/qml/Settings/images/brightness-dark.png
deleted file mode 100644
index f9eb07b583..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/brightness-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/brightness-dark@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/brightness-dark@2x.png
deleted file mode 100644
index 3efc5a6c4b..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/brightness-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/brightness-light.png b/examples/quickcontrols/wearable/qml/Settings/images/brightness-light.png
deleted file mode 100644
index ce3023b565..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/brightness-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/brightness-light@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/brightness-light@2x.png
deleted file mode 100644
index dc01aae1fd..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/brightness-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-dark.png b/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-dark.png
deleted file mode 100644
index e93c417d79..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-dark@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-dark@2x.png
deleted file mode 100644
index faa1bf6d11..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-light.png b/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-light.png
deleted file mode 100644
index c38d146d37..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-light@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-light@2x.png
deleted file mode 100644
index b9d6409cbc..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-white.png b/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-white.png
deleted file mode 100644
index aac8ef3f6c..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-white.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-white@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-white@2x.png
deleted file mode 100644
index b4d8954f5d..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode-white@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode.svg b/examples/quickcontrols/wearable/qml/Settings/images/demo-mode.svg
deleted file mode 100644
index ad1ce44873..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/demo-mode.svg
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="64"
- height="64"
- viewBox="0 0 16.933333 16.933333"
- version="1.1"
- id="svg8"
- inkscape:version="0.92.3 (d244b95, 2018-08-02)"
- sodipodi:docname="demo-mode.svg"
- inkscape:export-filename="/home/mitch/dev/qt5.12/qtquickcontrols2/examples/quickcontrols2/wearable/qml/Settings/images/demo-mode-light@2x.png"
- inkscape:export-xdpi="192"
- inkscape:export-ydpi="192">
- <defs
- id="defs2" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="15.839192"
- inkscape:cx="26.498574"
- inkscape:cy="25.685519"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- units="px"
- fit-margin-top="1"
- fit-margin-left="1"
- fit-margin-right="1"
- fit-margin-bottom="1"
- inkscape:window-width="3706"
- inkscape:window-height="2031"
- inkscape:window-x="134"
- inkscape:window-y="55"
- inkscape:window-maximized="1"
- inkscape:snap-global="false">
- <inkscape:grid
- type="xygrid"
- id="grid3713"
- originx="-0.0070880335"
- originy="-280.58354" />
- </sodipodi:namedview>
- <metadata
- id="metadata5">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-0.00708803,0.51688025)">
- <path
- style="fill:#141f3a;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 1.6489221,4.3034973 h 0.7205171 v 7.0102767 c 0.01422,0.27678 0.1921164,0.348966 0.392741,0.392741 H 14.169383 c 0.276028,-0.04241 0.382882,-0.208883 0.423747,-0.423747 V 4.3094031 h 0.692464 v 7.0279949 c -0.111554,0.831949 -0.589986,1.000696 -1.104399,1.104399 H 9.3324693 l 2.2589977,2.258998 c -0.0189,0.375139 -0.204956,0.516261 -0.501999,0.502 L 8.8304698,12.943797 v 1.972563 c -0.051861,0.342631 -0.6681357,0.36382 -0.7323291,0 V 12.949702 L 5.8391425,15.208701 C 5.5142667,15.206145 5.3522558,15.056484 5.3725781,14.742136 L 7.7172116,12.397503 H 2.7415096 C 2.1429244,12.357576 1.6879444,12.049089 1.6444927,11.300486 Z"
- id="path826"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccccccccccccccccccc" />
- <rect
- style="fill:#141f3a;fill-opacity:1;stroke:none;stroke-width:0.2619375;stroke-miterlimit:4;stroke-dasharray:0.523875, 0.2619375;stroke-dashoffset:0"
- id="rect828"
- width="16.044523"
- height="1.9126476"
- x="0.441401"
- y="2.0340116"
- rx="1.0583333"
- ry="1.0583333" />
- <path
- style="fill:#141f3a;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="M 7.8323763,1.681278 V 1.1615701 c 0.1987987,-0.67546179 1.0821701,-0.66495897 1.2992931,0 V 1.681278 Z"
- id="path830"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccc" />
- </g>
-</svg>
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/theme-dark.png b/examples/quickcontrols/wearable/qml/Settings/images/theme-dark.png
deleted file mode 100644
index d4c4a8c094..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/theme-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/theme-dark@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/theme-dark@2x.png
deleted file mode 100644
index 30d8c6de28..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/theme-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/theme-light.png b/examples/quickcontrols/wearable/qml/Settings/images/theme-light.png
deleted file mode 100644
index e7a0771b94..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/theme-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/theme-light@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/theme-light@2x.png
deleted file mode 100644
index 83bc878b03..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/theme-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/theme.svg b/examples/quickcontrols/wearable/qml/Settings/images/theme.svg
deleted file mode 100644
index bf3f1e57d7..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/theme.svg
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="64"
- height="64"
- viewBox="0 0 16.933333 16.933333"
- version="1.1"
- id="svg8"
- inkscape:version="0.92.3 (2405546, 2018-03-11)"
- sodipodi:docname="theme.svg"
- inkscape:export-filename="C:\dev\qt5.11\qtquickcontrols2\examples\quickcontrols2\wearable\qml\Settings\images\theme@2x.png"
- inkscape:export-xdpi="192"
- inkscape:export-ydpi="192">
- <defs
- id="defs2" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="7.9195959"
- inkscape:cx="-11.371874"
- inkscape:cy="28.405259"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- units="px"
- fit-margin-top="1"
- fit-margin-left="1"
- fit-margin-right="1"
- fit-margin-bottom="1"
- inkscape:window-width="3840"
- inkscape:window-height="2066"
- inkscape:window-x="-11"
- inkscape:window-y="-11"
- inkscape:window-maximized="1"
- inkscape:snap-global="false">
- <inkscape:grid
- type="xygrid"
- id="grid3713"
- originx="-0.0070880335"
- originy="-280.58354" />
- </sodipodi:namedview>
- <metadata
- id="metadata5">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-0.00708803,0.51688025)">
- <path
- style="fill:#141f3a;fill-opacity:1;stroke:none;stroke-width:0.25668776px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="M 2.7296861,14.56437 H 12.831038 C 13.464556,14.411157 15.01089,14.101758 15.231756,11.825604 15.232242,10.48538 13.96701,8.4813248 11.69084,9.0467292 10.687852,7.3630573 9.1232541,7.2558172 8.2817051,7.654427 6.9050791,4.9744381 2.9222535,5.8560797 2.9703307,8.9435956 1.7108806,9.1004362 0.37826375,9.7182846 0.27167137,11.745388 0.31660717,13.546059 1.5831259,14.350467 2.7296861,14.56437 Z"
- id="path4535"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccccccc" />
- <path
- style="fill:#141f3a;fill-opacity:1;stroke:none;stroke-width:0.25668776px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="M 11.392726,0.92763044 C 8.8246143,2.2754379 7.9809615,4.3443829 8.9780532,7.1830936 10.167295,7.1426991 11.108291,7.6664967 11.822182,8.7064442 12.799984,8.6194307 13.765772,8.6795708 14.658208,9.6382815 15.605885,9.3257847 16.200803,8.852941 16.675838,8.325607 13.735088,9.4033595 8.6151974,5.957728 11.392726,0.92763044 Z"
- id="path4537"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccccc" />
- </g>
-</svg>
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/wifi-dark.png b/examples/quickcontrols/wearable/qml/Settings/images/wifi-dark.png
deleted file mode 100644
index 14bfe8f5c8..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/wifi-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/wifi-dark@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/wifi-dark@2x.png
deleted file mode 100644
index b70d04290b..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/wifi-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/wifi-light.png b/examples/quickcontrols/wearable/qml/Settings/images/wifi-light.png
deleted file mode 100644
index 2de34118a7..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/wifi-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Settings/images/wifi-light@2x.png b/examples/quickcontrols/wearable/qml/Settings/images/wifi-light@2x.png
deleted file mode 100644
index 64b5f1725c..0000000000
--- a/examples/quickcontrols/wearable/qml/Settings/images/wifi-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Style/PageIndicator.qml b/examples/quickcontrols/wearable/qml/Style/PageIndicator.qml
deleted file mode 100644
index 593b50afb5..0000000000
--- a/examples/quickcontrols/wearable/qml/Style/PageIndicator.qml
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Templates as T
-import "."
-
-T.PageIndicator {
- id: control
-
- implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding
- implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding
-
- spacing: 6
- padding: 6
- bottomPadding: 7
-
- delegate: Rectangle {
- implicitWidth: 8
- implicitHeight: 8
-
- radius: width / 2
- color: UIStyle.themeColorQtGray3
-
- opacity: index === control.currentIndex ? 1.0 : 0.35
-
- Behavior on opacity {
- OpacityAnimator {
- duration: 100
- }
- }
- }
-
- contentItem: Row {
- spacing: control.spacing
-
- Repeater {
- model: control.count
- delegate: control.delegate
- }
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Style/Slider.qml b/examples/quickcontrols/wearable/qml/Style/Slider.qml
deleted file mode 100644
index 79c664280f..0000000000
--- a/examples/quickcontrols/wearable/qml/Style/Slider.qml
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Templates as T
-import "."
-
-T.Slider {
- id: control
-
- implicitWidth: 200
- implicitHeight: 26
-
- handle: Rectangle {
- x: control.visualPosition * (control.width - width)
- y: (control.height - height) / 2
- width: 20
- height: 15
-
- radius: 5
- color: control.pressed ? "#f0f0f0" : "#f6f6f6"
- border.color: UIStyle.themeColorQtGray7
- }
-
- background: Rectangle {
- y: (control.height - height) / 2
- height: 4
- radius: 2
- color: UIStyle.themeColorQtGray3
-
- Rectangle {
- width: control.visualPosition * parent.width
- height: parent.height
- color: UIStyle.colorQtAuxGreen2
- radius: 2
- }
- }
-}
-
diff --git a/examples/quickcontrols/wearable/qml/Style/Switch.qml b/examples/quickcontrols/wearable/qml/Style/Switch.qml
deleted file mode 100644
index 6219bf231e..0000000000
--- a/examples/quickcontrols/wearable/qml/Style/Switch.qml
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Templates as T
-import "."
-
-T.Switch {
- id: control
-
- implicitWidth: 48
- implicitHeight: 26
-
- indicator: Rectangle {
- x: control.visualPosition * (control.width - width)
- y: (control.height - height) / 2
- width: 26
- height: 26
-
- radius: 13
- color: control.down ? UIStyle.themeColorQtGray6 : UIStyle.themeColorQtGray10
- border.color: !control.checked ? "#999999"
- : (control.down ? UIStyle.colorQtAuxGreen2
- : UIStyle.colorQtAuxGreen1)
-
- Behavior on x {
- enabled: !control.pressed
- SmoothedAnimation { velocity: 200 }
- }
- }
-
- background: Rectangle {
- radius: 13
- color: control.checked ? UIStyle.colorQtAuxGreen2 : UIStyle.colorRed
- border.color: control.checked ? UIStyle.colorQtAuxGreen2
- : UIStyle.themeColorQtGray6
- }
-}
-
diff --git a/examples/quickcontrols/wearable/qml/Style/UIStyle.qml b/examples/quickcontrols/wearable/qml/Style/UIStyle.qml
deleted file mode 100644
index 524b8394f5..0000000000
--- a/examples/quickcontrols/wearable/qml/Style/UIStyle.qml
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-pragma Singleton
-
-QtObject {
- id: uiStyle
-
- // Font Sizes
- readonly property int fontSizeXXS: 10
- readonly property int fontSizeXS: 15
- readonly property int fontSizeS: 20
- readonly property int fontSizeM: 25
- readonly property int fontSizeL: 30
- readonly property int fontSizeXL: 35
- readonly property int fontSizeXXL: 40
-
- // Color Scheme
- // Green
- readonly property color colorQtPrimGreen: "#41cd52"
- readonly property color colorQtAuxGreen1: "#21be2b"
- readonly property color colorQtAuxGreen2: "#17a81a"
-
- // Red
- readonly property color colorRed: "#e6173d"
-
- // Gray
- readonly property color colorQtGray1: "#09102b"
- readonly property color colorQtGray2: "#222840"
- readonly property color colorQtGray3: "#3a4055"
- readonly property color colorQtGray4: "#53586b"
- readonly property color colorQtGray5: "#53586b"
- readonly property color colorQtGray6: "#848895"
- readonly property color colorQtGray7: "#9d9faa"
- readonly property color colorQtGray8: "#b5b7bf"
- readonly property color colorQtGray9: "#cecfd5"
- readonly property color colorQtGray10: "#f3f3f4"
-
- // Light/dark versions of the colors above.
- // Some UI elements always use a specific color regardless of theme,
- // which is why we have both sets: so that those elements don't need to hard-code the hex string.
- readonly property color themeColorQtGray1: darkTheme ? colorQtGray10 : colorQtGray1
- readonly property color themeColorQtGray2: darkTheme ? colorQtGray9 : colorQtGray2
- readonly property color themeColorQtGray3: darkTheme ? colorQtGray8 : colorQtGray3
- readonly property color themeColorQtGray4: darkTheme ? colorQtGray7 : colorQtGray4
- readonly property color themeColorQtGray5: darkTheme ? colorQtGray6 : colorQtGray5
- readonly property color themeColorQtGray6: darkTheme ? colorQtGray5 : colorQtGray6
- readonly property color themeColorQtGray7: darkTheme ? colorQtGray4 : colorQtGray7
- readonly property color themeColorQtGray8: darkTheme ? colorQtGray3 : colorQtGray8
- readonly property color themeColorQtGray9: darkTheme ? colorQtGray2 : colorQtGray9
- readonly property color themeColorQtGray10: darkTheme ? colorQtGray1 : colorQtGray10
-
- property bool darkTheme: false
-
- function themeImagePath(baseImagePath) {
- return baseImagePath + (darkTheme ? "-dark" : "-light") + ".png"
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Style/qmldir b/examples/quickcontrols/wearable/qml/Style/qmldir
deleted file mode 100644
index a04072bed6..0000000000
--- a/examples/quickcontrols/wearable/qml/Style/qmldir
+++ /dev/null
@@ -1 +0,0 @@
-singleton UIStyle 1.0 UIStyle.qml
diff --git a/examples/quickcontrols/wearable/qml/Weather/WeatherPage.qml b/examples/quickcontrols/wearable/qml/Weather/WeatherPage.qml
deleted file mode 100644
index d110fabdf9..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/WeatherPage.qml
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import ".."
-import "../Style"
-import "weather.js" as WeatherData
-
-Item {
- QQC2.SwipeView {
- id: svWeatherContainer
-
- anchors.fill: parent
-
- SwipeViewPage {
- id: weatherPage1
-
- Row {
- anchors.centerIn: parent
- spacing: 2
-
- Image {
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/temperature")
- }
-
- Column {
- anchors.verticalCenter: parent.verticalCenter
- spacing: 40
-
- Text {
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.main
- && wDataCntr.weatherData.main.temp) ?
- qsTr("Avg: ")
- + String(wDataCntr.weatherData.main.temp)
- + " °F" : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
-
- Text {
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.main
- && wDataCntr.weatherData.main.temp_min) ?
- qsTr("Min: ")
- + String(wDataCntr.weatherData.main.temp_min)
- + " °F" : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
-
- Text {
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.main
- && wDataCntr.weatherData.main.temp_max) ?
- qsTr("Max: ")
- + String(wDataCntr.weatherData.main.temp_max)
- + " °F " : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
- }
- }
- }
-
- SwipeViewPage {
- id: weatherPage2
-
- Column {
- spacing: 40
- anchors.centerIn: parent
-
- Row {
- spacing: 20
- anchors.horizontalCenter: parent.horizontalCenter
-
- Image {
- id: wImg
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/wind")
- }
-
- Text {
- anchors.verticalCenter: parent.verticalCenter
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.wind
- && wDataCntr.weatherData.wind.speed) ?
- String(wDataCntr.weatherData.wind.speed)
- + " mph" : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
- }
-
- Row {
- spacing: 20
- anchors.horizontalCenter: parent.horizontalCenter
-
- Image {
- id: hImg
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/humidity")
- }
-
- Text {
- anchors.verticalCenter: parent.verticalCenter
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.main
- && wDataCntr.weatherData.main.humidity) ?
- String(wDataCntr.weatherData.main.humidity)
- + " %" : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
- }
- }
- }
-
- SwipeViewPage {
- id: weatherPage3
-
- Row {
- anchors.centerIn: parent
- spacing: 10
-
- Image {
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/pressure")
- }
-
- Column {
- anchors.verticalCenter: parent.verticalCenter
- spacing: 40
-
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.main
- && wDataCntr.weatherData.main.pressure) ?
- String(wDataCntr.weatherData.main.pressure)
- + " hPa" : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
-
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.main
- && wDataCntr.weatherData.main.sea_level) ?
- String(wDataCntr.weatherData.main.sea_level)
- + " hPa" : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
-
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.main
- && wDataCntr.weatherData.main.grnd_level) ?
- String(wDataCntr.weatherData.main.grnd_level)
- + " hPa" : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
- }
- }
- }
-
- SwipeViewPage {
- id: weatherPage4
-
- Column {
- spacing: 40
- anchors.centerIn: parent
-
- Row {
- spacing: 30
- anchors.horizontalCenter: parent.horizontalCenter
-
- Image {
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/sunrise")
- }
-
- Text {
- anchors.verticalCenter: parent.verticalCenter
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.sys
- && wDataCntr.weatherData.sys.sunrise) ?
- WeatherData.getTimeHMS(wDataCntr.weatherData.sys.sunrise)
- : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
- }
-
- Row {
- spacing: 30
- anchors.horizontalCenter: parent.horizontalCenter
-
- Image {
- anchors.verticalCenter: parent.verticalCenter
- source: UIStyle.themeImagePath("images/sunset")
- }
-
- Text {
- anchors.verticalCenter: parent.verticalCenter
- text: (wDataCntr.weatherData
- && wDataCntr.weatherData.sys
- && wDataCntr.weatherData.sys.sunset) ?
- WeatherData.getTimeHMS(wDataCntr.weatherData.sys.sunset)
- : "N/A"
- font.pixelSize: UIStyle.fontSizeM
- font.letterSpacing: 1
- color: UIStyle.themeColorQtGray1
- }
- }
- }
- }
- }
-
- QtObject {
- id: wDataCntr
- property var weatherData
- }
-
- QQC2.PageIndicator {
- count: svWeatherContainer.count
- currentIndex: svWeatherContainer.currentIndex
-
- anchors.bottom: svWeatherContainer.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- }
- Component.onCompleted: {
- WeatherData.requestWeatherData(wDataCntr)
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/humidity-dark.png b/examples/quickcontrols/wearable/qml/Weather/images/humidity-dark.png
deleted file mode 100644
index 5d82238cdb..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/humidity-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/humidity-dark@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/humidity-dark@2x.png
deleted file mode 100644
index d83bb4f00c..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/humidity-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/humidity-light.png b/examples/quickcontrols/wearable/qml/Weather/images/humidity-light.png
deleted file mode 100644
index 5f3bf714e6..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/humidity-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/humidity-light@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/humidity-light@2x.png
deleted file mode 100644
index a23364c52c..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/humidity-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/pressure-dark.png b/examples/quickcontrols/wearable/qml/Weather/images/pressure-dark.png
deleted file mode 100644
index b2870a55a6..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/pressure-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/pressure-dark@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/pressure-dark@2x.png
deleted file mode 100644
index 061f9b8266..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/pressure-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/pressure-light.png b/examples/quickcontrols/wearable/qml/Weather/images/pressure-light.png
deleted file mode 100644
index 7b1b158734..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/pressure-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/pressure-light@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/pressure-light@2x.png
deleted file mode 100644
index 1a40c6422a..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/pressure-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/sunrise-dark.png b/examples/quickcontrols/wearable/qml/Weather/images/sunrise-dark.png
deleted file mode 100644
index 9cac22e1e1..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/sunrise-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/sunrise-dark@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/sunrise-dark@2x.png
deleted file mode 100644
index fe32682386b..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/sunrise-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/sunrise-light.png b/examples/quickcontrols/wearable/qml/Weather/images/sunrise-light.png
deleted file mode 100644
index f67a5dff34..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/sunrise-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/sunrise-light@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/sunrise-light@2x.png
deleted file mode 100644
index 86f5483761..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/sunrise-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/sunset-dark.png b/examples/quickcontrols/wearable/qml/Weather/images/sunset-dark.png
deleted file mode 100644
index 0068d30539..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/sunset-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/sunset-dark@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/sunset-dark@2x.png
deleted file mode 100644
index e515bcc37c..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/sunset-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/sunset-light.png b/examples/quickcontrols/wearable/qml/Weather/images/sunset-light.png
deleted file mode 100644
index 495f78a786..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/sunset-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/sunset-light@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/sunset-light@2x.png
deleted file mode 100644
index d220e829d1..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/sunset-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/temperature-dark.png b/examples/quickcontrols/wearable/qml/Weather/images/temperature-dark.png
deleted file mode 100644
index a207d9440d..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/temperature-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/temperature-dark@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/temperature-dark@2x.png
deleted file mode 100644
index c0e4ef39bf..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/temperature-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/temperature-light.png b/examples/quickcontrols/wearable/qml/Weather/images/temperature-light.png
deleted file mode 100644
index c4f7b61c2e..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/temperature-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/temperature-light@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/temperature-light@2x.png
deleted file mode 100644
index 50d4f4c43c..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/temperature-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/wind-dark.png b/examples/quickcontrols/wearable/qml/Weather/images/wind-dark.png
deleted file mode 100644
index 6a530491af..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/wind-dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/wind-dark@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/wind-dark@2x.png
deleted file mode 100644
index 487e3e78a3..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/wind-dark@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/wind-light.png b/examples/quickcontrols/wearable/qml/Weather/images/wind-light.png
deleted file mode 100644
index 97824bee8f..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/wind-light.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/images/wind-light@2x.png b/examples/quickcontrols/wearable/qml/Weather/images/wind-light@2x.png
deleted file mode 100644
index 7f94bd8542..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/images/wind-light@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/Weather/weather.js b/examples/quickcontrols/wearable/qml/Weather/weather.js
deleted file mode 100644
index c08fd96e9e..0000000000
--- a/examples/quickcontrols/wearable/qml/Weather/weather.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-function requestWeatherData(cntr) {
- var xhr = new XMLHttpRequest;
- xhr.open("GET", "weather.json");
- xhr.onreadystatechange = function () {
- if (xhr.readyState === XMLHttpRequest.DONE) {
- cntr.weatherData = JSON.parse(xhr.responseText)
- }
- }
- xhr.send();
-}
-
-function getTimeHMS(utcTime) {
- var date = new Date(utcTime * 1000);
- // Hours part from the timestamp
- var hours = date.getHours();
- var ampm = Math.floor((hours / 12)) ? " PM" : " AM";
- hours = (hours % 12);
-
- // Minutes part from the timestamp
- var minutes = "0" + date.getMinutes();
- // Seconds part from the timestamp
- var seconds = "0" + date.getSeconds();
-
- // Will display time in 10:30:23 format
- return hours % 12 + ':' + minutes.substr(-2) + ':' + seconds.substr(-2)
- + ampm;
-}
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/Clock.qml b/examples/quickcontrols/wearable/qml/WorldClock/Clock.qml
deleted file mode 100644
index e89489b045..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/Clock.qml
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import ".."
-import "../Style"
-
-SwipeViewPage {
- id: clock
-
- property int hours
- property int minutes
- property int seconds
- property real shift: timeShift
- property bool night: false
- property bool internationalTime: true //Unset for local time
-
- function getWatchFaceImage(imageName) {
- return "images/" + imageName
- }
-
- function timeChanged() {
- var date = new Date
- hours = internationalTime ? date.getUTCHours() + Math.floor(
- clock.shift) : date.getHours()
- night = (hours < 7 || hours > 19)
- minutes = internationalTime ?
- date.getUTCMinutes() + ((clock.shift % 1) * 60) : date.getMinutes()
- seconds = date.getUTCSeconds()
- }
-
- Timer {
- interval: 100
- running: true
- repeat: true
- onTriggered: clock.timeChanged()
- }
-
- Item {
- anchors.centerIn: parent
-
- width: 200
- height: 220
-
- Rectangle {
- color: clock.night ? UIStyle.colorQtGray1 : UIStyle.colorQtGray10
- radius: width / 2
- width: parent.width
- height: parent.width
- }
-
- Image {
- id: background
- source: getWatchFaceImage("swissdaydial.png")
- visible: clock.night == false
- }
- Image {
- source: getWatchFaceImage("swissnightdial.png")
- visible: clock.night == true
- }
-
- Image {
- x: 92.5
- y: 27
- source: getWatchFaceImage(clock.night ?
- "swissnighthour.png"
- : "swissdayhour.png")
- transform: Rotation {
- id: hourRotation
- origin.x: 7.5
- origin.y: 73
- angle: (clock.hours * 30) + (clock.minutes * 0.5)
- Behavior on angle {
- SpringAnimation {
- spring: 2
- damping: 0.2
- modulus: 360
- }
- }
- }
- }
-
- Image {
- x: 93.5
- y: 17
- source: getWatchFaceImage(clock.night ?
- "swissnightminute.png"
- : "swissdayminute.png")
- transform: Rotation {
- id: minuteRotation
- origin.x: 6.5
- origin.y: 83
- angle: clock.minutes * 6
- Behavior on angle {
- SpringAnimation {
- spring: 2
- damping: 0.2
- modulus: 360
- }
- }
- }
- }
-
- Image {
- x: 97.5
- y: 20
- source: getWatchFaceImage("second.png")
- transform: Rotation {
- id: secondRotation
- origin.x: 2.5
- origin.y: 80
- angle: clock.seconds * 6
- Behavior on angle {
- SpringAnimation {
- spring: 2
- damping: 0.2
- modulus: 360
- }
- }
- }
- }
-
- Image {
- anchors.centerIn: background
- source: getWatchFaceImage("center.png")
- }
-
- Text {
- id: cityLabel
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 2
- anchors.horizontalCenter: parent.horizontalCenter
-
- text: cityName
- color: UIStyle.themeColorQtGray1
- font.pixelSize: UIStyle.fontSizeXS
- font.letterSpacing: 2
- }
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/WorldClockPage.qml b/examples/quickcontrols/wearable/qml/WorldClock/WorldClockPage.qml
deleted file mode 100644
index 34a51da5cf..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/WorldClockPage.qml
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls as QQC2
-import "../Style"
-
-Item {
- QQC2.SwipeView {
- id: svWatchContainer
-
- anchors.fill: parent
-
- ListModel {
- id: placesList
- ListElement {
- cityName: "New York"
- timeShift: -4
- }
- ListElement {
- cityName: "London"
- timeShift: 0
- }
- ListElement {
- cityName: "Oslo"
- timeShift: 1
- }
- ListElement {
- cityName: "Mumbai"
- timeShift: 5.5
- }
- ListElement {
- cityName: "Tokyo"
- timeShift: 9
- }
- ListElement {
- cityName: "Brisbane"
- timeShift: 10
- }
- ListElement {
- cityName: "Los Angeles"
- timeShift: -8
- }
- }
-
- Repeater {
- model: placesList
- delegate: Clock {
- }
- }
- }
-
- QQC2.PageIndicator {
- count: svWatchContainer.count
- currentIndex: svWatchContainer.currentIndex
-
- anchors.bottom: svWatchContainer.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- }
-}
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/center.png b/examples/quickcontrols/wearable/qml/WorldClock/images/center.png
deleted file mode 100644
index 0dec43a0aa..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/center.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/center@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/center@2x.png
deleted file mode 100644
index dffc5d60e4..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/center@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/clock-night.png b/examples/quickcontrols/wearable/qml/WorldClock/images/clock-night.png
deleted file mode 100644
index 2de330243a..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/clock-night.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/clock-night@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/clock-night@2x.png
deleted file mode 100644
index f675acea69..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/clock-night@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/clock.png b/examples/quickcontrols/wearable/qml/WorldClock/images/clock.png
deleted file mode 100644
index 686fb8c2f7..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/clock.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/second.png b/examples/quickcontrols/wearable/qml/WorldClock/images/second.png
deleted file mode 100644
index 1e64f357df..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/second.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/second@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/second@2x.png
deleted file mode 100644
index cebc1deb06..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/second@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdaydial.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissdaydial.png
deleted file mode 100644
index 0e6321537a..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdaydial.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdaydial@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissdaydial@2x.png
deleted file mode 100644
index ed5de8c646..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdaydial@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayhour.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayhour.png
deleted file mode 100644
index 4a835ac3f0..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayhour.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayhour@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayhour@2x.png
deleted file mode 100644
index 70f3fa0eee..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayhour@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayminute.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayminute.png
deleted file mode 100644
index 048acc7b3f..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayminute.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayminute@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayminute@2x.png
deleted file mode 100644
index 4cc6ce64b4..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissdayminute@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightdial.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightdial.png
deleted file mode 100644
index ee5a15bb9b..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightdial.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightdial@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightdial@2x.png
deleted file mode 100644
index ec7e5c77dc..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightdial@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnighthour.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissnighthour.png
deleted file mode 100644
index c5b41cb043..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnighthour.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnighthour@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissnighthour@2x.png
deleted file mode 100644
index bbbc36f1d7..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnighthour@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightminute.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightminute.png
deleted file mode 100644
index 026c742446..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightminute.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightminute@2x.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightminute@2x.png
deleted file mode 100644
index 8ad69047a4..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissnightminute@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/qml/WorldClock/images/swissseconds.png b/examples/quickcontrols/wearable/qml/WorldClock/images/swissseconds.png
deleted file mode 100644
index 06446ac0fe..0000000000
--- a/examples/quickcontrols/wearable/qml/WorldClock/images/swissseconds.png
+++ /dev/null
Binary files differ
diff --git a/examples/quickcontrols/wearable/wearable.cpp b/examples/quickcontrols/wearable/wearable.cpp
index 861d4b7610..b211ef7ab5 100644
--- a/examples/quickcontrols/wearable/wearable.cpp
+++ b/examples/quickcontrols/wearable/wearable.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
@@ -8,21 +8,23 @@
int main(int argc, char *argv[])
{
+ // Allow navigation.js to "GET" walk_route.json with XMLHttpRequest.
+ qputenv("QML_XHR_ALLOW_FILE_READ", "1");
+
QCoreApplication::setApplicationName("Wearable");
QCoreApplication::setOrganizationName("QtProject");
QGuiApplication app(argc, argv);
//! [style]
- QQuickStyle::setStyle(QStringLiteral("qrc:/qml/Style"));
+ QQuickStyle::setStyle(QStringLiteral("WearableStyle"));
//! [style]
- //! [icons]
- QIcon::setThemeName(QStringLiteral("wearable"));
- //! [icons]
-
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/wearable.qml")));
+#ifdef Q_OS_MACOS
+ engine.addImportPath(app.applicationDirPath() + "/../PlugIns");
+#endif
+ engine.loadFromModule("Wearable", "Main");
return app.exec();
}
diff --git a/examples/quickcontrols/wearable/wearable.pro b/examples/quickcontrols/wearable/wearable.pro
index 9ca5e53dfe..861c5eb214 100644
--- a/examples/quickcontrols/wearable/wearable.pro
+++ b/examples/quickcontrols/wearable/wearable.pro
@@ -1,6 +1,13 @@
TEMPLATE = app
TARGET = wearable
QT += quick quickcontrols2
+qtHaveModule(positioning): QT += positioning
+qtHaveModule(location): QT += location
+
+QML_IMPORT_PATH += \
+ Wearable \
+ WearableSettings \
+ WearableStyle
SOURCES += \
wearable.cpp
diff --git a/examples/quickcontrols/wearable/wearable.qml b/examples/quickcontrols/wearable/wearable.qml
deleted file mode 100644
index 27887354dc..0000000000
--- a/examples/quickcontrols/wearable/wearable.qml
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtCore
-import QtQuick
-import QtQuick.Controls as QQC2
-import "qml"
-import "qml/Style"
-
-QQC2.ApplicationWindow {
- id: window
- visible: true
- width: 320
- height: 320
- title: qsTr("Wearable")
-
- Settings {
- id: settings
- property bool wireless
- property bool bluetooth
- property int brightness
- property bool darkTheme
- property bool demoMode
- }
-
- Binding {
- target: UIStyle
- property: "darkTheme"
- value: settings.darkTheme
- }
-
- // We need the settings object both here and in SettingsPage,
- // so for convenience, we declare it as a property of the root object so that
- // it will be available to all of the QML files that we load.
- property alias settings: settings
-
- background: Image {
- source: "images/background-" + (settings.darkTheme ? "dark" : "light") + ".png"
- }
-
- header: NaviButton {
- id: homeButton
-
- edge: Qt.TopEdge
- enabled: stackView.depth > 1
- imageSource: "images/home.png"
-
- onClicked: stackView.pop(null)
- }
-
- footer: NaviButton {
- id: backButton
-
- edge: Qt.BottomEdge
- enabled: stackView.depth > 1
- imageSource: "images/back.png"
-
- onClicked: stackView.pop()
- }
-
- QQC2.StackView {
- id: stackView
-
- focus: true
- anchors.fill: parent
-
- initialItem: LauncherPage {
- onLaunched: stackView.push(page)
- }
- }
-
- DemoMode {
- stackView: stackView
- }
-
- DemoModeIndicator {
- id: demoModeIndicator
- y: settings.demoMode ? -height : -height * 2
- anchors.horizontalCenter: parent.horizontalCenter
- height: header.height
- z: window.header.z + 1
- }
-
- MouseArea {
- enabled: settings.demoMode
- anchors.fill: parent
- onClicked: {
- // Stop demo mode and return to the launcher page.
- settings.demoMode = false
- stackView.pop(null)
- }
- }
-}
diff --git a/examples/quickcontrols/wearable/wearable.qrc b/examples/quickcontrols/wearable/wearable.qrc
index db59d038f6..b8fc839be9 100644
--- a/examples/quickcontrols/wearable/wearable.qrc
+++ b/examples/quickcontrols/wearable/wearable.qrc
@@ -1,155 +1,135 @@
+<!-- This file only exists for qmake. -->
<RCC>
- <qresource prefix="/">
- <file>wearable.qml</file>
- <file>qml/LauncherPage.qml</file>
- <file>qml/NaviButton.qml</file>
- <file>icons/wearable/index.theme</file>
- <file>icons/wearable/36x36/alarms.png</file>
- <file>icons/wearable/36x36/fitness.png</file>
- <file>icons/wearable/36x36/navigation.png</file>
- <file>icons/wearable/36x36/notifications.png</file>
- <file>icons/wearable/36x36/settings.png</file>
- <file>icons/wearable/36x36/weather.png</file>
- <file>icons/wearable/36x36/worldclock.png</file>
- <file>icons/wearable/36x36@2/alarms.png</file>
- <file>icons/wearable/36x36@2/fitness.png</file>
- <file>icons/wearable/36x36@2/navigation.png</file>
- <file>icons/wearable/36x36@2/notifications.png</file>
- <file>icons/wearable/36x36@2/settings.png</file>
- <file>icons/wearable/36x36@2/weather.png</file>
- <file>icons/wearable/36x36@2/worldclock.png</file>
- <file>images/background-light.png</file>
- <file>images/background-dark.png</file>
- <file>images/back.png</file>
- <file>images/back@2x.png</file>
- <file>images/home.png</file>
- <file>images/home@2x.png</file>
- <file>qml/Alarms/AlarmsPage.qml</file>
- <file>qml/Fitness/fitness.js</file>
- <file>qml/Fitness/FitnessPage.qml</file>
- <file>qml/Fitness/images/man-running-light.png</file>
- <file>qml/Fitness/images/man-running-light@2x.png</file>
- <file>qml/Fitness/images/man-walking-light.png</file>
- <file>qml/Fitness/images/man-walking-light@2x.png</file>
- <file>qml/Fitness/images/man-running-dark.png</file>
- <file>qml/Fitness/images/man-running-dark@2x.png</file>
- <file>qml/Fitness/images/man-walking-dark.png</file>
- <file>qml/Fitness/images/man-walking-dark@2x.png</file>
- <file>qml/Navigation/navigation.js</file>
- <file>qml/Navigation/NavigationPage.qml</file>
- <file>qml/Navigation/RouteElement.qml</file>
- <file>qml/Navigation/walk_route.json</file>
- <file>qml/Navigation/images/end.png</file>
- <file>qml/Navigation/images/end@2x.png</file>
- <file>qml/Navigation/images/leftturn-light.png</file>
- <file>qml/Navigation/images/leftturn-light@2x.png</file>
- <file>qml/Navigation/images/leftturn-dark.png</file>
- <file>qml/Navigation/images/leftturn-dark@2x.png</file>
- <file>qml/Navigation/images/navigation-light.png</file>
- <file>qml/Navigation/images/navigation-light@2x.png</file>
- <file>qml/Navigation/images/navigation-dark.png</file>
- <file>qml/Navigation/images/navigation-dark@2x.png</file>
- <file>qml/Navigation/images/rightturn-light.png</file>
- <file>qml/Navigation/images/rightturn-light@2x.png</file>
- <file>qml/Navigation/images/rightturn-dark.png</file>
- <file>qml/Navigation/images/rightturn-dark@2x.png</file>
- <file>qml/Navigation/images/start.png</file>
- <file>qml/Navigation/images/start@2x.png</file>
- <file>qml/Navigation/images/straight-light.png</file>
- <file>qml/Navigation/images/straight-light@2x.png</file>
- <file>qml/Navigation/images/straight-dark.png</file>
- <file>qml/Navigation/images/straight-dark@2x.png</file>
- <file>qml/Navigation/images/uturn.png</file>
- <file>qml/Navigation/images/uturn@2x.png</file>
- <file>qml/Notifications/notifications.js</file>
- <file>qml/Notifications/NotificationsPage.qml</file>
- <file>qml/Notifications/images/avatarf-light.png</file>
- <file>qml/Notifications/images/avatarf-light@2x.png</file>
- <file>qml/Notifications/images/avatarm-light.png</file>
- <file>qml/Notifications/images/avatarm-light@2x.png</file>
- <file>qml/Notifications/images/missedcall-light.png</file>
- <file>qml/Notifications/images/missedcall-light@2x.png</file>
- <file>qml/Notifications/images/avatarf-dark.png</file>
- <file>qml/Notifications/images/avatarf-dark@2x.png</file>
- <file>qml/Notifications/images/avatarm-dark.png</file>
- <file>qml/Notifications/images/avatarm-dark@2x.png</file>
- <file>qml/Notifications/images/missedcall-dark.png</file>
- <file>qml/Notifications/images/missedcall-dark@2x.png</file>
- <file>qml/Settings/SettingsPage.qml</file>
- <file>qml/Settings/images/bluetooth-light.png</file>
- <file>qml/Settings/images/bluetooth-light@2x.png</file>
- <file>qml/Settings/images/brightness-light.png</file>
- <file>qml/Settings/images/brightness-light@2x.png</file>
- <file>qml/Settings/images/demo-mode-light.png</file>
- <file>qml/Settings/images/demo-mode-light@2x.png</file>
- <file>qml/Settings/images/demo-mode-dark.png</file>
- <file>qml/Settings/images/demo-mode-dark@2x.png</file>
- <file>qml/Settings/images/demo-mode-white.png</file>
- <file>qml/Settings/images/demo-mode-white@2x.png</file>
- <file>qml/Settings/images/theme-light.png</file>
- <file>qml/Settings/images/theme-light@2x.png</file>
- <file>qml/Settings/images/wifi-light.png</file>
- <file>qml/Settings/images/wifi-light@2x.png</file>
- <file>qml/Settings/images/bluetooth-dark.png</file>
- <file>qml/Settings/images/bluetooth-dark@2x.png</file>
- <file>qml/Settings/images/brightness-dark.png</file>
- <file>qml/Settings/images/brightness-dark@2x.png</file>
- <file>qml/Settings/images/theme-dark.png</file>
- <file>qml/Settings/images/theme-dark@2x.png</file>
- <file>qml/Settings/images/wifi-dark.png</file>
- <file>qml/Settings/images/wifi-dark@2x.png</file>
- <file>qml/Style/qmldir</file>
- <file>qml/Style/PageIndicator.qml</file>
- <file>qml/Style/Slider.qml</file>
- <file>qml/Style/Switch.qml</file>
- <file>qml/Style/UIStyle.qml</file>
- <file>qml/Weather/weather.js</file>
- <file>qml/Weather/weather.json</file>
- <file>qml/Weather/WeatherPage.qml</file>
- <file>qml/Weather/images/humidity-light.png</file>
- <file>qml/Weather/images/humidity-light@2x.png</file>
- <file>qml/Weather/images/pressure-light.png</file>
- <file>qml/Weather/images/pressure-light@2x.png</file>
- <file>qml/Weather/images/sunrise-light.png</file>
- <file>qml/Weather/images/sunrise-light@2x.png</file>
- <file>qml/Weather/images/sunset-light.png</file>
- <file>qml/Weather/images/sunset-light@2x.png</file>
- <file>qml/Weather/images/temperature-light.png</file>
- <file>qml/Weather/images/temperature-light@2x.png</file>
- <file>qml/Weather/images/wind-light.png</file>
- <file>qml/Weather/images/wind-light@2x.png</file>
- <file>qml/Weather/images/humidity-dark.png</file>
- <file>qml/Weather/images/humidity-dark@2x.png</file>
- <file>qml/Weather/images/pressure-dark.png</file>
- <file>qml/Weather/images/pressure-dark@2x.png</file>
- <file>qml/Weather/images/sunrise-dark.png</file>
- <file>qml/Weather/images/sunrise-dark@2x.png</file>
- <file>qml/Weather/images/sunset-dark.png</file>
- <file>qml/Weather/images/sunset-dark@2x.png</file>
- <file>qml/Weather/images/temperature-dark.png</file>
- <file>qml/Weather/images/temperature-dark@2x.png</file>
- <file>qml/Weather/images/wind-dark.png</file>
- <file>qml/Weather/images/wind-dark@2x.png</file>
- <file>qml/WorldClock/Clock.qml</file>
- <file>qml/WorldClock/WorldClockPage.qml</file>
- <file>qml/WorldClock/images/center.png</file>
- <file>qml/WorldClock/images/center@2x.png</file>
- <file>qml/WorldClock/images/second.png</file>
- <file>qml/WorldClock/images/second@2x.png</file>
- <file>qml/WorldClock/images/swissdaydial.png</file>
- <file>qml/WorldClock/images/swissdaydial@2x.png</file>
- <file>qml/WorldClock/images/swissdayhour.png</file>
- <file>qml/WorldClock/images/swissdayhour@2x.png</file>
- <file>qml/WorldClock/images/swissdayminute.png</file>
- <file>qml/WorldClock/images/swissdayminute@2x.png</file>
- <file>qml/WorldClock/images/swissnightdial.png</file>
- <file>qml/WorldClock/images/swissnightdial@2x.png</file>
- <file>qml/WorldClock/images/swissnighthour.png</file>
- <file>qml/WorldClock/images/swissnighthour@2x.png</file>
- <file>qml/WorldClock/images/swissnightminute.png</file>
- <file>qml/WorldClock/images/swissnightminute@2x.png</file>
- <file>qml/SwipeViewPage.qml</file>
- <file>qml/DemoMode.qml</file>
- <file>qml/DemoModeIndicator.qml</file>
+ <qresource prefix="/qt/qml/">
+ <file>Wearable/qmldir</file>
+ <file>Wearable/AlarmsPage.qml</file>
+ <file>Wearable/Clock.qml</file>
+ <file>Wearable/DemoMode.qml</file>
+ <file>Wearable/DemoModeIndicator.qml</file>
+ <file>Wearable/FitnessPage.qml</file>
+ <file>Wearable/LauncherPage.qml</file>
+ <file>Wearable/Main.qml</file>
+ <file>Wearable/MenuHeader.qml</file>
+ <file>Wearable/NavigationPage.qml</file>
+ <file>Wearable/NavigationFallbackPage.qml</file>
+ <file>Wearable/NotificationsPage.qml</file>
+ <file>Wearable/RouteElement.qml</file>
+ <file>Wearable/SettingsPage.qml</file>
+ <file>Wearable/SwipeViewPage.qml</file>
+ <file>Wearable/WeatherPage.qml</file>
+ <file>Wearable/WorldClockPage.qml</file>
+ <file>Wearable/ListHeaderItem.qml</file>
+ <file>Wearable/ListItem.qml</file>
+
+ <file>Wearable/fitness.js</file>
+ <file>Wearable/navigation.js</file>
+ <file>Wearable/notifications.js</file>
+ <file>Wearable/weather.js</file>
+
+ <file>Wearable/fallbackroute.json</file>
+ <file>Wearable/weather.json</file>
+
+ <file>Wearable/icons/wind-light.svg</file>
+ <file>Wearable/icons/wind-dark.svg</file>
+ <file>Wearable/icons/wifi-light.svg</file>
+ <file>Wearable/icons/wifi-dark.svg</file>
+ <file>Wearable/icons/weather-light.svg</file>
+ <file>Wearable/icons/weather-dark.svg</file>
+ <file>Wearable/icons/uturnright-light.svg</file>
+ <file>Wearable/icons/uturnright-dark.svg</file>
+ <file>Wearable/icons/uturnleft-light.svg</file>
+ <file>Wearable/icons/uturnleft-dark.svg</file>
+ <file>Wearable/icons/thermometer-light.svg</file>
+ <file>Wearable/icons/thermometer-dark.svg</file>
+ <file>Wearable/icons/sunset-light.svg</file>
+ <file>Wearable/icons/sunset-dark.svg</file>
+ <file>Wearable/icons/sunrise-light.svg</file>
+ <file>Wearable/icons/sunrise-dark.svg</file>
+ <file>Wearable/icons/sun-light.svg</file>
+ <file>Wearable/icons/sun-dark.svg</file>
+ <file>Wearable/icons/settings-light.svg</file>
+ <file>Wearable/icons/settings-dark.svg</file>
+ <file>Wearable/icons/right-light.svg</file>
+ <file>Wearable/icons/right-dark.svg</file>
+ <file>Wearable/icons/pressure-light.svg</file>
+ <file>Wearable/icons/pressure-dark.svg</file>
+ <file>Wearable/icons/plus-light.svg</file>
+ <file>Wearable/icons/plus-dark.svg</file>
+ <file>Wearable/icons/notification-light.svg</file>
+ <file>Wearable/icons/notification-dark.svg</file>
+ <file>Wearable/icons/maps-light.svg</file>
+ <file>Wearable/icons/maps-dark.svg</file>
+ <file>Wearable/icons/lightright-light.svg</file>
+ <file>Wearable/icons/lightright-dark.svg</file>
+ <file>Wearable/icons/lightleft-light.svg</file>
+ <file>Wearable/icons/lightleft-dark.svg</file>
+ <file>Wearable/icons/left-light.svg</file>
+ <file>Wearable/icons/left-dark.svg</file>
+ <file>Wearable/icons/hearth-light.svg</file>
+ <file>Wearable/icons/hearth-dark.svg</file>
+ <file>Wearable/icons/forward-light.svg</file>
+ <file>Wearable/icons/forward-dark.svg</file>
+ <file>Wearable/icons/drop-light.svg</file>
+ <file>Wearable/icons/drop-dark.svg</file>
+ <file>Wearable/icons/demomode-light.svg</file>
+ <file>Wearable/icons/demomode-dark.svg</file>
+ <file>Wearable/icons/darkmode-light.svg</file>
+ <file>Wearable/icons/darkmode-dark.svg</file>
+ <file>Wearable/icons/clock-light.svg</file>
+ <file>Wearable/icons/clock-dark.svg</file>
+ <file>Wearable/icons/bluetooth-light.svg</file>
+ <file>Wearable/icons/bluetooth-dark.svg</file>
+ <file>Wearable/icons/bell-light.svg</file>
+ <file>Wearable/icons/bell-dark.svg</file>
+ <file>Wearable/icons/bearright-light.svg</file>
+ <file>Wearable/icons/bearright-dark.svg</file>
+ <file>Wearable/icons/bearleft-light.svg</file>
+ <file>Wearable/icons/bearleft-dark.svg</file>
+ <file>Wearable/icons/back-light.svg</file>
+ <file>Wearable/icons/back-dark.svg</file>
+ <file>Wearable/icons/weather-thundershower-light.svg</file>
+ <file>Wearable/icons/weather-thundershower-dark.svg</file>
+ <file>Wearable/icons/weather-sunny-very-few-clouds-light.svg</file>
+ <file>Wearable/icons/weather-sunny-very-few-clouds-dark.svg</file>
+ <file>Wearable/icons/weather-sunny-light.svg</file>
+ <file>Wearable/icons/weather-sunny-dark.svg</file>
+ <file>Wearable/icons/weather-storm-light.svg</file>
+ <file>Wearable/icons/weather-storm-dark.svg</file>
+ <file>Wearable/icons/weather-snow-light.svg</file>
+ <file>Wearable/icons/weather-snow-dark.svg</file>
+ <file>Wearable/icons/weather-sleet-light.svg</file>
+ <file>Wearable/icons/weather-sleet-dark.svg</file>
+ <file>Wearable/icons/weather-showers-scattered-light.svg</file>
+ <file>Wearable/icons/weather-showers-scattered-dark.svg</file>
+ <file>Wearable/icons/weather-showers-light.svg</file>
+ <file>Wearable/icons/weather-showers-dark.svg</file>
+ <file>Wearable/icons/weather-overcast-light.svg</file>
+ <file>Wearable/icons/weather-overcast-dark.svg</file>
+ <file>Wearable/icons/weather-icy-light.svg</file>
+ <file>Wearable/icons/weather-icy-dark.svg</file>
+ <file>Wearable/icons/weather-haze-light.svg</file>
+ <file>Wearable/icons/weather-haze-dark.svg</file>
+ <file>Wearable/icons/weather-fog-light.svg</file>
+ <file>Wearable/icons/weather-fog-dark.svg</file>
+ <file>Wearable/icons/weather-few-clouds-light.svg</file>
+ <file>Wearable/icons/weather-few-clouds-dark.svg</file>
+
+ <file>Wearable/images/fitness-man-walking-light.svg</file>
+ <file>Wearable/images/fitness-man-running-light.svg</file>
+ <file>Wearable/images/fitness-man-walking-dark.svg</file>
+ <file>Wearable/images/fitness-man-running-dark.svg</file>
+ <file>Wearable/images/fallbackmap.png</file>
+
+ <file>WearableSettings/qmldir</file>
+ <file>WearableSettings/WearableSettings.qml</file>
+
+ <file>WearableStyle/qmldir</file>
+ <file>WearableStyle/PageIndicator.qml</file>
+ <file>WearableStyle/Slider.qml</file>
+ <file>WearableStyle/Switch.qml</file>
+ <file>WearableStyle/UIStyle.qml</file>
+ <file>WearableStyle/fonts/TitilliumWeb-Bold.ttf</file>
+ <file>WearableStyle/fonts/TitilliumWeb-SemiBold.ttf</file>
+ <file>WearableStyle/fonts/TitilliumWeb-Regular.ttf</file>
</qresource>
</RCC>
diff --git a/src/3rdparty/masm/WeakRandom.h b/src/3rdparty/masm/WeakRandom.h
index 924bac00c4..435f5861f8 100644
--- a/src/3rdparty/masm/WeakRandom.h
+++ b/src/3rdparty/masm/WeakRandom.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MASM_WEAKRANDOM_H
#define MASM_WEAKRANDOM_H
diff --git a/src/3rdparty/masm/config.h b/src/3rdparty/masm/config.h
index 72035f6a6a..f765c05257 100644
--- a/src/3rdparty/masm/config.h
+++ b/src/3rdparty/masm/config.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MASM_CONFIG_H
#define MASM_CONFIG_H
diff --git a/src/3rdparty/masm/qt_attribution.json b/src/3rdparty/masm/qt_attribution.json
index aab413ad40..77b144d738 100644
--- a/src/3rdparty/masm/qt_attribution.json
+++ b/src/3rdparty/masm/qt_attribution.json
@@ -8,17 +8,17 @@
"License": "BSD 2-clause \"Simplified\" License",
"LicenseId": "BSD-2-Clause",
"LicenseFile": "LICENSE",
- "Copyright": "Copyright (C) 2003-2018 Apple Inc. All rights reserved.
-Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
-Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved. (http://www.torchmobile.com/)
-Copyright (C) 2009, 2010 University of Szeged
-Copyright (C) 2009-2011 STMicroelectronics. All rights reserved.
-Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.
-Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
-Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.
-Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
-Copyright (C) 2011 Google Inc. All rights reserved.
-Copyright (C) 2013 Samsung Electronics. All rights reserved.
-Copyright (C) 2015 Cisco Systems, Inc. All rights reserved.
-Copyright (c) 2002-2009 Vivek Thampi"
+ "Copyright": ["Copyright (C) 2003-2018 Apple Inc. All rights reserved.",
+ "Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)",
+ "Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved. (http://www.torchmobile.com/)",
+ "Copyright (C) 2009, 2010 University of Szeged",
+ "Copyright (C) 2009-2011 STMicroelectronics. All rights reserved.",
+ "Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.",
+ "Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged",
+ "Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.",
+ "Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.",
+ "Copyright (C) 2011 Google Inc. All rights reserved.",
+ "Copyright (C) 2013 Samsung Electronics. All rights reserved.",
+ "Copyright (C) 2015 Cisco Systems, Inc. All rights reserved.",
+ "Copyright (c) 2002-2009 Vivek Thampi"]
}
diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h
index 9ab19871c7..876a12fce5 100644
--- a/src/3rdparty/masm/stubs/ExecutableAllocator.h
+++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MASM_EXECUTABLEALLOCATOR_H
#define MASM_EXECUTABLEALLOCATOR_H
diff --git a/src/3rdparty/masm/stubs/JSGlobalData.h b/src/3rdparty/masm/stubs/JSGlobalData.h
index 14c238acfb..22b597d36e 100644
--- a/src/3rdparty/masm/stubs/JSGlobalData.h
+++ b/src/3rdparty/masm/stubs/JSGlobalData.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MASM_JSGLOBALDATA_H
#define MASM_JSGLOBALDATA_H
diff --git a/src/3rdparty/masm/stubs/Options.cpp b/src/3rdparty/masm/stubs/Options.cpp
index 6689aca383..639e0655db 100644
--- a/src/3rdparty/masm/stubs/Options.cpp
+++ b/src/3rdparty/masm/stubs/Options.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "config.h"
#include "Options.h"
diff --git a/src/3rdparty/masm/stubs/Options.h b/src/3rdparty/masm/stubs/Options.h
index 6339c06033..0f742fb6d2 100644
--- a/src/3rdparty/masm/stubs/Options.h
+++ b/src/3rdparty/masm/stubs/Options.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef OPTIONS_H
#define OPTIONS_H
diff --git a/src/3rdparty/masm/stubs/SuperSampler.h b/src/3rdparty/masm/stubs/SuperSampler.h
index 422de528e1..95d1739bec 100644
--- a/src/3rdparty/masm/stubs/SuperSampler.h
+++ b/src/3rdparty/masm/stubs/SuperSampler.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#pragma once
diff --git a/src/3rdparty/masm/stubs/WTFStubs.cpp b/src/3rdparty/masm/stubs/WTFStubs.cpp
index facba7d937..f876cda032 100644
--- a/src/3rdparty/masm/stubs/WTFStubs.cpp
+++ b/src/3rdparty/masm/stubs/WTFStubs.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
diff --git a/src/3rdparty/masm/stubs/WTFStubs.h b/src/3rdparty/masm/stubs/WTFStubs.h
index 91b8838558..81f850dc1d 100644
--- a/src/3rdparty/masm/stubs/WTFStubs.h
+++ b/src/3rdparty/masm/stubs/WTFStubs.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef WTFSTUBS_H
#define WTFSTUBS_H
diff --git a/src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h b/src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h
index 43868feadb..206916e820 100644
--- a/src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h
+++ b/src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#pragma once
diff --git a/src/3rdparty/masm/stubs/runtime/VM.h b/src/3rdparty/masm/stubs/runtime/VM.h
index 94cce814f3..e00ee543e6 100644
--- a/src/3rdparty/masm/stubs/runtime/VM.h
+++ b/src/3rdparty/masm/stubs/runtime/VM.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MASM_VM_H
#define MASM_VM_H
diff --git a/src/3rdparty/masm/stubs/wtf/FastAllocBase.h b/src/3rdparty/masm/stubs/wtf/FastAllocBase.h
index c7f99a30a5..c8b468092e 100644
--- a/src/3rdparty/masm/stubs/wtf/FastAllocBase.h
+++ b/src/3rdparty/masm/stubs/wtf/FastAllocBase.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef FASTALLOCBASE_H
#define FASTALLOCBASE_H
diff --git a/src/3rdparty/masm/stubs/wtf/FastMalloc.h b/src/3rdparty/masm/stubs/wtf/FastMalloc.h
index 7d6f86b32d..f94a7b3845 100644
--- a/src/3rdparty/masm/stubs/wtf/FastMalloc.h
+++ b/src/3rdparty/masm/stubs/wtf/FastMalloc.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef FASTMALLOC_H
#define FASTMALLOC_H
diff --git a/src/3rdparty/masm/stubs/wtf/HashMap.h b/src/3rdparty/masm/stubs/wtf/HashMap.h
index 888c6cceb0..3f257412b3 100644
--- a/src/3rdparty/masm/stubs/wtf/HashMap.h
+++ b/src/3rdparty/masm/stubs/wtf/HashMap.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HASHMAP_H
#define HASHMAP_H
diff --git a/src/3rdparty/masm/stubs/wtf/HashSet.h b/src/3rdparty/masm/stubs/wtf/HashSet.h
index 3765c9a8b1..741f448ff3 100644
--- a/src/3rdparty/masm/stubs/wtf/HashSet.h
+++ b/src/3rdparty/masm/stubs/wtf/HashSet.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HASHSET_H
#define HASHSET_H
diff --git a/src/3rdparty/masm/stubs/wtf/Noncopyable.h b/src/3rdparty/masm/stubs/wtf/Noncopyable.h
index 2d4de9cd83..d712bd9e71 100644
--- a/src/3rdparty/masm/stubs/wtf/Noncopyable.h
+++ b/src/3rdparty/masm/stubs/wtf/Noncopyable.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef NONCOPYABLE_H
#define NONCOPYABLE_H
diff --git a/src/3rdparty/masm/stubs/wtf/Optional.h b/src/3rdparty/masm/stubs/wtf/Optional.h
index c4064476c7..3f7f99eb55 100644
--- a/src/3rdparty/masm/stubs/wtf/Optional.h
+++ b/src/3rdparty/masm/stubs/wtf/Optional.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#pragma once
diff --git a/src/3rdparty/masm/stubs/wtf/OwnPtr.h b/src/3rdparty/masm/stubs/wtf/OwnPtr.h
index eee828bb68..980cec41c2 100644
--- a/src/3rdparty/masm/stubs/wtf/OwnPtr.h
+++ b/src/3rdparty/masm/stubs/wtf/OwnPtr.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef OWNPTR_H
#define OWNPTR_H
diff --git a/src/3rdparty/masm/stubs/wtf/PassOwnPtr.h b/src/3rdparty/masm/stubs/wtf/PassOwnPtr.h
index 0e18132f77..a97990d9c2 100644
--- a/src/3rdparty/masm/stubs/wtf/PassOwnPtr.h
+++ b/src/3rdparty/masm/stubs/wtf/PassOwnPtr.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef PASSOWNPTR_H
#define PASSOWNPTR_H
diff --git a/src/3rdparty/masm/stubs/wtf/PassRefPtr.h b/src/3rdparty/masm/stubs/wtf/PassRefPtr.h
index cc03a5d651..aec7d6c038 100644
--- a/src/3rdparty/masm/stubs/wtf/PassRefPtr.h
+++ b/src/3rdparty/masm/stubs/wtf/PassRefPtr.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef PASSREFPTR_H
#define PASSREFPTR_H
diff --git a/src/3rdparty/masm/stubs/wtf/RefCounted.h b/src/3rdparty/masm/stubs/wtf/RefCounted.h
index 332ef301aa..5f62f72293 100644
--- a/src/3rdparty/masm/stubs/wtf/RefCounted.h
+++ b/src/3rdparty/masm/stubs/wtf/RefCounted.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef REFCOUNTED_H
#define REFCOUNTED_H
diff --git a/src/3rdparty/masm/stubs/wtf/RefPtr.h b/src/3rdparty/masm/stubs/wtf/RefPtr.h
index e7e918649f..14e60ca235 100644
--- a/src/3rdparty/masm/stubs/wtf/RefPtr.h
+++ b/src/3rdparty/masm/stubs/wtf/RefPtr.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef REFPTR_H
#define REFPTR_H
diff --git a/src/3rdparty/masm/stubs/wtf/TypeTraits.h b/src/3rdparty/masm/stubs/wtf/TypeTraits.h
index 04c2608a4d..c75bb2444d 100644
--- a/src/3rdparty/masm/stubs/wtf/TypeTraits.h
+++ b/src/3rdparty/masm/stubs/wtf/TypeTraits.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef TYPETRAITS_H
#define TYPETRAITS_H
diff --git a/src/3rdparty/masm/stubs/wtf/UnusedParam.h b/src/3rdparty/masm/stubs/wtf/UnusedParam.h
index 6d80b5a90b..4f1156060d 100644
--- a/src/3rdparty/masm/stubs/wtf/UnusedParam.h
+++ b/src/3rdparty/masm/stubs/wtf/UnusedParam.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef UNUSEDPARAM_H
#define UNUSEDPARAM_H
diff --git a/src/3rdparty/masm/stubs/wtf/Vector.h b/src/3rdparty/masm/stubs/wtf/Vector.h
index cee647a4b5..44b7e783c8 100644
--- a/src/3rdparty/masm/stubs/wtf/Vector.h
+++ b/src/3rdparty/masm/stubs/wtf/Vector.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef VECTOR_H
#define VECTOR_H
diff --git a/src/3rdparty/masm/stubs/wtf/text/CString.h b/src/3rdparty/masm/stubs/wtf/text/CString.h
index 7129f5049e..646d3367b9 100644
--- a/src/3rdparty/masm/stubs/wtf/text/CString.h
+++ b/src/3rdparty/masm/stubs/wtf/text/CString.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef CSTRING_H
#define CSTRING_H
diff --git a/src/3rdparty/masm/stubs/wtf/text/StringBuilder.h b/src/3rdparty/masm/stubs/wtf/text/StringBuilder.h
index a382f6da83..d51c0c7c21 100644
--- a/src/3rdparty/masm/stubs/wtf/text/StringBuilder.h
+++ b/src/3rdparty/masm/stubs/wtf/text/StringBuilder.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#pragma once
#include <wtf/text/WTFString.h>
diff --git a/src/3rdparty/masm/stubs/wtf/text/WTFString.h b/src/3rdparty/masm/stubs/wtf/text/WTFString.h
index da5183f734..17346593f9 100644
--- a/src/3rdparty/masm/stubs/wtf/text/WTFString.h
+++ b/src/3rdparty/masm/stubs/wtf/text/WTFString.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef WTFSTRING_H
#define WTFSTRING_H
diff --git a/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h b/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h
index 34e331f887..44a7d65571 100644
--- a/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h
+++ b/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef UNICODE_H
#define UNICODE_H
diff --git a/src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp b/src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp
index 99c925f406..386536ed05 100644
--- a/src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp
+++ b/src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "config.h"
#include "yarr/YarrUnicodeProperties.h"
diff --git a/src/3rdparty/masm/wtf/OSAllocatorIntegrity.cpp b/src/3rdparty/masm/wtf/OSAllocatorIntegrity.cpp
index 27f72073c4..ae0f19281d 100644
--- a/src/3rdparty/masm/wtf/OSAllocatorIntegrity.cpp
+++ b/src/3rdparty/masm/wtf/OSAllocatorIntegrity.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) Rolland Dudemaine All rights reserved.
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) Rolland Dudemaine All rights reserved.
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "config.h"
#include "OSAllocator.h"
diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
index a8990a92b4..5f95d0e9c8 100644
--- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
+++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
@@ -114,11 +114,6 @@ void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable,
if (result == MAP_FAILED)
CRASH();
- while (madvise(result, bytes, MADV_DONTNEED)) {
- if (errno != EAGAIN)
- CRASH();
- }
-
if (fd != -1)
close(fd);
#else
@@ -250,8 +245,10 @@ void OSAllocator::decommit(void* address, size_t bytes)
mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0);
#elif OS(LINUX)
while (madvise(address, bytes, MADV_DONTNEED)) {
- if (errno != EAGAIN)
- CRASH();
+ if (errno != EAGAIN) {
+ memset(address, 0, bytes); // We rely on madvise to zero-out the memory
+ break;
+ }
}
if (mprotect(address, bytes, PROT_NONE))
CRASH();
diff --git a/src/3rdparty/masm/wtf/Platform.h b/src/3rdparty/masm/wtf/Platform.h
index 29decfadcf..4146879c5a 100644
--- a/src/3rdparty/masm/wtf/Platform.h
+++ b/src/3rdparty/masm/wtf/Platform.h
@@ -45,6 +45,9 @@
/* ==== Platform adaptation macros: these describe properties of the target environment. ==== */
/* CPU() - the target CPU architecture */
+#ifdef Q_OS_VXWORKS
+#undef CPU
+#endif // Q_OS_VXWORKS
#define CPU(WTF_FEATURE) WTF_CPU_##WTF_FEATURE
/* HAVE() - specific system features (headers, functions or similar) that are present or not */
#define HAVE(WTF_FEATURE) HAVE_##WTF_FEATURE
@@ -224,6 +227,7 @@
|| defined(__ARM_ARCH_6K__) \
|| defined(__ARM_ARCH_6Z__) \
|| defined(__ARM_ARCH_6ZK__) \
+ || defined(__ARM_ARCH_6KZ__) \
|| defined(__ARM_ARCH_6T2__) \
|| defined(__ARMV6__)
#define WTF_ARM_ARCH_VERSION 6
@@ -442,6 +446,11 @@
#define WTF_OS_RTEMS 1
#endif
+/* OS(VXWORKS) - VXWORKS */
+#if defined(__vxworks)
+#define WTF_OS_VXWORKS 1
+#endif
+
#define WTF_OS_WIN ERROR "USE WINDOWS WITH OS NOT WIN"
#define WTF_OS_MAC ERROR "USE MAC_OS_X WITH OS NOT MAC"
@@ -457,6 +466,7 @@
|| OS(QNX) \
|| OS(RTEMS) \
|| OS(SOLARIS) \
+ || OS(VXWORKS) \
|| defined(unix) \
|| defined(__unix) \
|| defined(__unix__)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a9948bc013..ca83200cf5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,10 +4,11 @@
# Generated from src.pro.
# special case skip regeneration
-if (NOT QT_FEATURE_commandlineparser)
+if(NOT QT_FEATURE_commandlineparser)
message(FATAL_ERROR "QT_FEATURE_commandlineparser required to compile qmltyperegistrar")
endif()
+add_subdirectory(qmltoolingsettings)
add_subdirectory(qmltyperegistrar)
add_subdirectory(qmlintegration)
add_subdirectory(qml)
@@ -15,6 +16,10 @@ add_subdirectory(qmlmodels)
add_subdirectory(core)
+if(QT_FEATURE_qml_network)
+ add_subdirectory(qmlnetwork)
+endif()
+
if(QT_FEATURE_qml_worker_script)
add_subdirectory(qmlworkerscript)
endif()
@@ -23,7 +28,7 @@ if(TARGET Qt::Sql)
add_subdirectory(qmllocalstorage)
endif()
-if (QT_FEATURE_qml_xmllistmodel)
+if(QT_FEATURE_qml_xmllistmodel)
add_subdirectory(qmlxmllistmodel)
endif()
@@ -34,7 +39,7 @@ if(TARGET Qt::Gui AND TARGET Qt::qsb AND QT_FEATURE_qml_animation)
find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Test)
if(QT_FEATURE_testlib AND TARGET Qt::Test)
add_subdirectory(qmltest)
- if(QT_FEATURE_network)
+ if(QT_FEATURE_network AND QT_FEATURE_quick_itemview)
add_subdirectory(quicktestutils)
endif()
endif()
@@ -51,6 +56,10 @@ if(TARGET Qt::Gui AND TARGET Qt::qsb AND QT_FEATURE_qml_animation)
add_subdirectory(quickshapes)
endif()
+ if(TARGET Qt::Svg)
+ add_subdirectory(quickvectorimage)
+ endif()
+
if(TARGET Qt::Widgets)
add_subdirectory(quickwidgets)
endif()
@@ -58,10 +67,12 @@ if(TARGET Qt::Gui AND TARGET Qt::qsb AND QT_FEATURE_qml_animation)
add_subdirectory(quicktemplates)
add_subdirectory(quickcontrolsimpl)
add_subdirectory(quickcontrols)
- add_subdirectory(quickdialogs)
+ if(QT_FEATURE_quicktemplates2_container)
+ add_subdirectory(quickdialogs)
+ endif()
add_subdirectory(quicknativestyle)
- if(QT_FEATURE_testlib AND QT_FEATURE_network AND TARGET Qt::Test AND TARGET Qt::QuickControls2)
+ if(QT_FEATURE_testlib AND QT_FEATURE_network AND QT_FEATURE_quick_itemview AND TARGET Qt::Test AND TARGET Qt::QuickControls2 AND NOT (WASM AND BUILD_SHARED_LIBS))
add_subdirectory(quickcontrolstestutils)
endif()
else()
@@ -79,13 +90,18 @@ add_subdirectory(labs)
# These need to be included here since they have dependencies on the modules
# specified above.
-if(QT_FEATURE_qml_devtools)
- add_subdirectory(qmlcompiler)
- add_subdirectory(qmldom)
+add_subdirectory(qmlcompiler)
+add_subdirectory(qmldom)
+
+# Build qmlcachegen now, so that we can use it in src/imports.
+if(QT_FEATURE_xmlstreamwriter)
+ add_subdirectory(../tools/qmlcachegen qmlcachegen)
+endif()
- # Build qmlcachegen now, so that we can use it in src/imports.
- if(QT_FEATURE_qml_devtools AND QT_FEATURE_xmlstreamwriter)
- add_subdirectory(../tools/qmlcachegen qmlcachegen)
+if(QT_FEATURE_thread AND QT_FEATURE_filesystemwatcher AND QT_FEATURE_processenvironment
+ AND TARGET Qt::LanguageServerPrivate)
+ if (NOT CMAKE_CROSSCOMPILING OR QT_FORCE_BUILD_TOOLS)
+ add_subdirectory(qmlls)
endif()
endif()
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index fb6f15ffec..1446f9844e 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -15,15 +15,23 @@ qt_internal_add_qml_module(QmlCore
qqmlstandardpaths_p.h
qqmlstandardpaths.cpp
qqmlcoreglobal_p.h
- qqmlsettings_p.h
- qqmlsettings.cpp
DEFINES
QT_BUILD_QML_CORE_LIB
PUBLIC_LIBRARIES
Qt::Core
Qt::Qml
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
+ )
+
+qt_internal_extend_target(QmlCore CONDITION QT_FEATURE_settings
+ SOURCES
+ qqmlsettings_p.h
+ qqmlsettings.cpp
+)
+
+qt_internal_extend_target(QmlCore CONDITION QT_FEATURE_permissions
+ SOURCES
+ qqmlpermissions_p.h
)
qt_internal_add_docs(QmlCore
diff --git a/src/core/doc/qtqmlcore.qdocconf b/src/core/doc/qtqmlcore.qdocconf
index 6e1ebb4fe6..a20baad3f5 100644
--- a/src/core/doc/qtqmlcore.qdocconf
+++ b/src/core/doc/qtqmlcore.qdocconf
@@ -33,5 +33,5 @@ navigation.qmltypespage = "Qt QML Core QML Types"
tagfile = qtqmlcore.tags
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/core/doc/src/includes/qmlpermissions.qdocinc b/src/core/doc/src/includes/qmlpermissions.qdocinc
new file mode 100644
index 0000000000..6255394511
--- /dev/null
+++ b/src/core/doc/src/includes/qmlpermissions.qdocinc
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+//! [type-docs]
+ \qmltype \1
+ \inqmlmodule QtCore
+ \ingroup qml-permissions
+
+ \brief Access to the user's \3.
+
+ This type represents a \2, and can be used to
+ check the status of and request access to the user's \3.
+
+ See \2 for documentation of the individual properties
+ and detailed usage requirements.
+
+ \sa {QML Application Permissions}, {Application Permissions}, \2
+//! [type-docs]
+
+//! [status-docs]
+ \qmlproperty Qt::PermissionStatus \1::status
+
+ This property holds the current status of the permission.
+
+ \sa QCoreApplication::checkPermission
+//! [status-docs]
+
+//! [request-docs]
+ \qmlmethod \1::request()
+
+ Requests the permission with its current properties.
+
+ \sa QCoreApplication::requestPermission
+//! [request-docs]
diff --git a/src/core/doc/src/qmlpermissions.qdoc b/src/core/doc/src/qmlpermissions.qdoc
new file mode 100644
index 0000000000..fab0bba0d2
--- /dev/null
+++ b/src/core/doc/src/qmlpermissions.qdoc
@@ -0,0 +1,193 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qml-application-permissions.html
+ \title QML Application Permissions
+ \brief Managing application permissions via QML
+
+ Many features of today's devices and operating systems can have
+ significant privacy, security, and performance implications if
+ misused. It's therefore increasingly common for platforms to
+ require explicit consent from the user before accessing these
+ features.
+
+ The \l{Qt QML Core} module exposes the Qt C++
+ \l [QtCore] {Application Permissions} functionality to QML via a set of
+ permission types that can be used to check or request permission in a cross
+ platform manner.
+
+ \annotatedlist qml-permissions
+
+ \section1 Usage
+
+ To check and request a specific permission in your application,
+ include an instance of the appropriate permission type, and set
+ any of its properties if needed:
+
+ \qml
+ CalendarPermission {
+ id: calendarPermission
+ accessMode: CalendarPermission.ReadWrite
+ }
+ \endqml
+
+ The type can be used to check the current state of the
+ permissions, for example to drive a state based UI:
+
+ \qml
+ states: [
+ State {
+ name: "waitingForPermission"
+ when: calendarPermission.status == Qt.PermissionStatus.Undetermined
+ PropertyChanges { target: permissionRequestItem; visible: true }
+ },
+ State {
+ name: "permissionDenied"
+ when: calendarPermission.status == Qt.PermissionStatus.Denied
+ PropertyChanges { target: permissionDeniedItem; visible: true }
+ }
+ ]
+ \endqml
+
+ In the example above, two permission specific items will be overlaid if the
+ permission status is not granted. The request UI could perhaps look like:
+
+ \qml
+ Rectangle {
+ id: permissionRequestItem
+ anchors.fill: parent
+ visible: false
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("We need your permission to access the calendar."
+ + "Please tap this screen to request permission.")
+
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: calendarPermission.request()
+ }
+ }
+ \endqml
+
+ With a corresponding denied UI:
+
+ \qml
+ Rectangle {
+ id: permissionDeniedItem
+ anchors.fill: parent
+ color: "red"
+ visible: false
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("We need your permission to access the calendar,"
+ + "but permission was not granted. Please resolve.")
+ }
+ }
+ \endqml
+
+ \section2 Changing permission properties
+
+ The properties of a permission can be changed, even after
+ a request has been initiated by calling `request()`. This
+ will update the status, if it changes a result of the new
+ property values, but will not result in an automatic request
+ using the new set of properties.
+
+ For example, if upgrading an already granted calendar permission
+ for access mode \c Qt.CalendarPermission.ReadOnly to
+ \c Qt.CalendarPermission.ReadWrite, the platform will
+ respond in one of three ways:
+
+ \list
+ \li By implicitly granting the extended permission, for example
+ because the platform doesn't distinguish between the two
+ access modes, which will result in no state change.
+ \li By moving the status back to \ Undetermined, so that
+ the user can be consulted again for access to the now
+ extended permission.
+ \li By moving the status to \c Denied, for example if permissions
+ can not be upgraded once initially requested.
+ \endlist
+
+ All these states should then move the application's UI into
+ the appropriate state, where the user is informed of the
+ new state, with the possibility of requesting the new
+ permission if possible, or reverting to a less extensive
+ permission.
+
+ \section2 Interaction between permission items
+
+ Although the permission state is ultimately tied to the
+ underlying application, each permission item reports its own
+ status independently of all other items, and needs to be
+ independently requested if needed.
+
+ For example, requesting calendar access for one item will
+ not update the status of another CalendarPermission item,
+ even if these have the exact same properties.
+*/
+
+/*!
+ \include qmlpermissions.qdocinc {type-docs} {CalendarPermission} {QCalendarPermission} {calendar}
+ \since 6.6
+*/
+/*!
+ \include qmlpermissions.qdocinc {status-docs} {CalendarPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {request-docs} {CalendarPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {type-docs} {ContactsPermission} {QContactsPermission} {contacts}
+ \since 6.6
+*/
+/*!
+ \include qmlpermissions.qdocinc {status-docs} {ContactsPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {request-docs} {ContactsPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {type-docs} {CameraPermission} {QCameraPermission} {camera}
+ \since 6.6
+*/
+/*!
+ \include qmlpermissions.qdocinc {status-docs} {CameraPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {request-docs} {CameraPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {type-docs} {MicrophonePermission} {QMicrophonePermission} {microphone}
+ \since 6.6
+*/
+/*!
+ \include qmlpermissions.qdocinc {status-docs} {MicrophonePermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {request-docs} {MicrophonePermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {type-docs} {BluetoothPermission} {QBluetoothPermission} {Bluetooth peripherals}
+ \since 6.6
+*/
+/*!
+ \include qmlpermissions.qdocinc {status-docs} {BluetoothPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {request-docs} {BluetoothPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {type-docs} {LocationPermission} {QLocationPermission} {location}
+ \since 6.6
+*/
+/*!
+ \include qmlpermissions.qdocinc {status-docs} {LocationPermission}
+*/
+/*!
+ \include qmlpermissions.qdocinc {request-docs} {LocationPermission}
+*/
diff --git a/src/core/doc/src/qtqmlcore-qmltypes.qdoc b/src/core/doc/src/qtqmlcore-qmltypes.qdoc
index c49dc6acd3..9136d5e0af 100644
--- a/src/core/doc/src/qtqmlcore-qmltypes.qdoc
+++ b/src/core/doc/src/qtqmlcore-qmltypes.qdoc
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \qmlmodule QtCore 6.\QtMinorVersion
+ \qmlmodule QtCore
\title Qt QML Core QML Types
\ingroup qmlmodules
\brief Provides core system functionality in QML.
diff --git a/src/core/doc/src/qtqmlcore.qdoc b/src/core/doc/src/qtqmlcore.qdoc
index 59ec6ea556..2c8b0ef8b1 100644
--- a/src/core/doc/src/qtqmlcore.qdoc
+++ b/src/core/doc/src/qtqmlcore.qdoc
@@ -7,6 +7,14 @@
\brief The Qt QML Core module provides core system functionality in QML.
+ \section1 Application Permissions
+
+ The module exposes the Qt C++ \l [QtCore] {Application Permissions}
+ functionality to QML via a set of permission types that
+ can be used to check or request permission in a cross
+ platform manner. See \l{QML Application Permissions}
+ for more details.
+
\section1 QML Types
\generatelist {qmltypesbymodule QtCore}
@@ -15,6 +23,6 @@
\list
\li \l{Qt Core}
- \li \l{Qt QML}
+ \li \l{Qt Qml}
\endlist
*/
diff --git a/src/core/qqmlcoreglobal_p.h b/src/core/qqmlcoreglobal_p.h
index 686a61d0b1..57bb5b0f93 100644
--- a/src/core/qqmlcoreglobal_p.h
+++ b/src/core/qqmlcoreglobal_p.h
@@ -16,6 +16,6 @@
//
#include <QtCore/qglobal.h>
-#include <QtQmlCore/private/qtqmlcoreexports_p.h>
+#include <QtQmlCore/qtqmlcoreexports.h>
#endif // QQMLCOREGLOBAL_P_H
diff --git a/src/core/qqmlpermissions_p.h b/src/core/qqmlpermissions_p.h
new file mode 100644
index 0000000000..6bba2d1c6a
--- /dev/null
+++ b/src/core/qqmlpermissions_p.h
@@ -0,0 +1,117 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLPERMISSIONS_P_H
+#define QQMLPERMISSIONS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmlglobal_p.h>
+
+#if QT_CONFIG(permissions)
+
+#include <QtQml/qqmlregistration.h>
+
+#include <QtCore/qpermissions.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qproperty.h>
+#include <QtCore/qglobal.h>
+
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QML_PERMISSION(Permission) \
+ Q_OBJECT \
+ QML_NAMED_ELEMENT(Permission) \
+public: \
+ Q_PROPERTY(Qt::PermissionStatus status READ status NOTIFY statusChanged) \
+ Qt::PermissionStatus status() const { return qApp->checkPermission(m_permission); } \
+ Q_SIGNAL void statusChanged(); \
+ Q_INVOKABLE void request() { \
+ const auto previousStatus = status(); \
+ qApp->requestPermission(m_permission, this, \
+ [this, previousStatus](const QPermission &permission) { \
+ if (previousStatus != permission.status()) \
+ emit statusChanged(); \
+ }); \
+ } \
+private: \
+ Q##Permission m_permission; \
+public:
+
+#define QML_PERMISSION_PROPERTY(PropertyType, getterName, setterName) \
+ Q_PROPERTY(PropertyType getterName READ getterName WRITE setterName NOTIFY getterName##Changed) \
+ PropertyType getterName() const { return m_permission.getterName(); } \
+ void setterName(const PropertyType &value) { \
+ const auto previousValue = m_permission.getterName(); \
+ const auto previousStatus = status(); \
+ m_permission.setterName(value); \
+ if (m_permission.getterName() != previousValue) { \
+ emit getterName##Changed(); \
+ if (status() != previousStatus) \
+ emit statusChanged(); \
+ } \
+ } \
+ Q_SIGNAL void getterName##Changed();
+
+
+struct QQmlQLocationPermission : public QObject
+{
+ QML_PERMISSION(LocationPermission)
+ QML_ADDED_IN_VERSION(6, 6)
+ QML_EXTENDED_NAMESPACE(QLocationPermission)
+ QML_PERMISSION_PROPERTY(QLocationPermission::Availability, availability, setAvailability)
+ QML_PERMISSION_PROPERTY(QLocationPermission::Accuracy, accuracy, setAccuracy)
+};
+
+struct QQmlCalendarPermission : public QObject
+{
+ QML_PERMISSION(CalendarPermission)
+ QML_ADDED_IN_VERSION(6, 6)
+ QML_EXTENDED_NAMESPACE(QCalendarPermission)
+ QML_PERMISSION_PROPERTY(QCalendarPermission::AccessMode, accessMode, setAccessMode)
+};
+
+struct QQmlContactsPermission : public QObject
+{
+ QML_PERMISSION(ContactsPermission)
+ QML_ADDED_IN_VERSION(6, 6)
+ QML_EXTENDED_NAMESPACE(QContactsPermission)
+ QML_PERMISSION_PROPERTY(QContactsPermission::AccessMode, accessMode, setAccessMode)
+};
+
+struct QQmlBluetoothPermission : public QObject
+{
+ QML_PERMISSION(BluetoothPermission)
+ QML_ADDED_IN_VERSION(6, 6)
+ QML_EXTENDED_NAMESPACE(QBluetoothPermission)
+ QML_PERMISSION_PROPERTY(QBluetoothPermission::CommunicationModes, communicationModes, setCommunicationModes)
+};
+
+struct QQmlCameraPermission : public QObject
+{
+ QML_PERMISSION(CameraPermission)
+ QML_ADDED_IN_VERSION(6, 6)
+};
+
+struct QQmlMicrophonePermission : public QObject
+{
+ QML_PERMISSION(MicrophonePermission)
+ QML_ADDED_IN_VERSION(6, 6)
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(permissions)
+
+#endif // QQMLPERMISSIONS_P_H
diff --git a/src/core/qqmlsettings.cpp b/src/core/qqmlsettings.cpp
index 1b4481cfc7..0b3bc9ebd3 100644
--- a/src/core/qqmlsettings.cpp
+++ b/src/core/qqmlsettings.cpp
@@ -2,15 +2,18 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlsettings_p.h"
-#include <qcoreevent.h>
-#include <qcoreapplication.h>
-#include <qloggingcategory.h>
-#include <qsettings.h>
-#include <qpointer.h>
-#include <qjsvalue.h>
-#include <qqmlinfo.h>
-#include <qdebug.h>
-#include <qhash.h>
+
+#include <QtQml/qjsvalue.h>
+#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qsettings.h>
QT_BEGIN_NAMESPACE
@@ -236,7 +239,9 @@ QSettings *QQmlSettingsPrivate::instance() const
return settings;
QQmlSettings *q = const_cast<QQmlSettings *>(q_func());
- settings = location.isLocalFile() ? new QSettings(location.toLocalFile(), QSettings::IniFormat, q) : new QSettings(q);
+ settings = QQmlFile::isLocalFile(location)
+ ? new QSettings(QQmlFile::urlToLocalFileOrQrc(location), QSettings::IniFormat, q)
+ : new QSettings(q);
if (settings->status() != QSettings::NoError) {
// TODO: can't print out the enum due to the following error:
@@ -379,6 +384,8 @@ QQmlSettings::~QQmlSettings()
This property holds the name of the settings category.
Categories can be used to group related settings together.
+
+ \sa QSettings::group
*/
QString QQmlSettings::category() const
{
diff --git a/src/core/qqmlsettings_p.h b/src/core/qqmlsettings_p.h
index 90d664efaf..1a6a5be463 100644
--- a/src/core/qqmlsettings_p.h
+++ b/src/core/qqmlsettings_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQmlSettingsPrivate;
-class Q_QMLCORE_PRIVATE_EXPORT QQmlSettings : public QObject, public QQmlParserStatus
+class Q_QMLCORE_EXPORT QQmlSettings : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
diff --git a/src/core/qqmlstandardpaths.cpp b/src/core/qqmlstandardpaths.cpp
index ea4c534568..a06b9e3454 100644
--- a/src/core/qqmlstandardpaths.cpp
+++ b/src/core/qqmlstandardpaths.cpp
@@ -28,12 +28,8 @@ static QList<QUrl> toUrlList(const QStringList &paths)
{
QList<QUrl> urls;
urls.reserve(paths.size());
- for (const QString &path : paths) {
- QUrl url = QUrl(path);
- if (url.scheme().isEmpty())
- url = QUrl::fromLocalFile(path);
- urls += url;
- }
+ for (const QString &path : paths)
+ urls += QUrl::fromLocalFile(path);
return urls;
}
diff --git a/src/core/qqmlstandardpaths_p.h b/src/core/qqmlstandardpaths_p.h
index fb4d1494b0..b9e82f1656 100644
--- a/src/core/qqmlstandardpaths_p.h
+++ b/src/core/qqmlstandardpaths_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQmlEngine;
class QJSEngine;
-class Q_QMLCORE_PRIVATE_EXPORT QQmlStandardPaths : public QObject
+class Q_QMLCORE_EXPORT QQmlStandardPaths : public QObject
{
Q_OBJECT
QML_SINGLETON
diff --git a/src/core/qqmlsysteminformation_p.h b/src/core/qqmlsysteminformation_p.h
index 868bce1823..e01a5c42c6 100644
--- a/src/core/qqmlsysteminformation_p.h
+++ b/src/core/qqmlsysteminformation_p.h
@@ -20,7 +20,7 @@
#include <QtQml/qqmlregistration.h>
QT_BEGIN_NAMESPACE
-class Q_QMLCORE_PRIVATE_EXPORT QQmlSystemInformation : public QObject
+class Q_QMLCORE_EXPORT QQmlSystemInformation : public QObject
{
Q_OBJECT
QML_SINGLETON
diff --git a/src/effects/CMakeLists.txt b/src/effects/CMakeLists.txt
index 034dbb1542..f461116818 100644
--- a/src/effects/CMakeLists.txt
+++ b/src/effects/CMakeLists.txt
@@ -26,45 +26,13 @@ qt_internal_add_qml_module(QuickEffectsPrivate
Qt::QmlPrivate
Qt::QuickPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
-
-# Resources:
-set(effects_resource_files
- "data/BlurItem.qml"
- "data/shaders/bluritems.vert.qsb"
- "data/shaders/bluritems.frag.qsb"
- "data/shaders/multieffect_c.vert.qsb"
- "data/shaders/multieffect_cs.vert.qsb"
- "data/shaders/multieffect_c0.frag.qsb"
- "data/shaders/multieffect_cb1.frag.qsb"
- "data/shaders/multieffect_cb2.frag.qsb"
- "data/shaders/multieffect_cb3.frag.qsb"
- "data/shaders/multieffect_cbs1.frag.qsb"
- "data/shaders/multieffect_cbs2.frag.qsb"
- "data/shaders/multieffect_cbs3.frag.qsb"
- "data/shaders/multieffect_cm0.frag.qsb"
- "data/shaders/multieffect_cmb1.frag.qsb"
- "data/shaders/multieffect_cmb2.frag.qsb"
- "data/shaders/multieffect_cmb3.frag.qsb"
- "data/shaders/multieffect_cmbs1.frag.qsb"
- "data/shaders/multieffect_cmbs2.frag.qsb"
- "data/shaders/multieffect_cmbs3.frag.qsb"
- "data/shaders/multieffect_cms0.frag.qsb"
- "data/shaders/multieffect_cms1.frag.qsb"
- "data/shaders/multieffect_cms2.frag.qsb"
- "data/shaders/multieffect_cms3.frag.qsb"
- "data/shaders/multieffect_cs0.frag.qsb"
- "data/shaders/multieffect_cs1.frag.qsb"
- "data/shaders/multieffect_cs2.frag.qsb"
- "data/shaders/multieffect_cs3.frag.qsb"
-)
+ )
qt_internal_add_resource(QuickEffectsPrivate "effects"
PREFIX
"/"
FILES
- ${effects_resource_files}
+ "data/BlurItem.qml"
)
qt_internal_extend_target(QuickEffectsPrivate CONDITION MSVC
@@ -80,3 +48,323 @@ qt_internal_extend_target(QuickEffectsPrivate CONDITION EXISTS "qqml_enable_gcov
-fprofile-arcs
-ftest-coverage
)
+
+set(EFFECT_SHADER_ARGS
+ SILENT
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ PREFIX
+ "/"
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders1"
+ ${EFFECT_SHADER_ARGS}
+ BATCHABLE
+ FILES
+ "data/shaders/bluritems.vert"
+ "data/shaders/bluritems.frag"
+)
+
+# Multieffect fragment shaders
+# c = Common color effects
+# m = Mask
+# b = Blur
+# s = Shadow
+# [n] = Amount of blur items used
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders2"
+ ${EFFECT_SHADER_ARGS}
+ BATCHABLE
+ FILES
+ "data/shaders/multieffect.vert"
+ OUTPUTS
+ "data/shaders/multieffect_c.vert.qsb"
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders3"
+ ${EFFECT_SHADER_ARGS}
+ BATCHABLE
+ FILES
+ "data/shaders/multieffect.vert"
+ OUTPUTS
+ "data/shaders/multieffect_cs.vert.qsb"
+ DEFINES
+ SHADOW
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders4"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_c0.frag.qsb"
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders5"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cm0.frag.qsb"
+ DEFINES
+ MASK
+)
+
+# Special shaders for non-blurred shadows (cs0 and cms0)
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders6"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cs0.frag.qsb"
+ DEFINES
+ SHADOW
+ BL0
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders7"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cms0.frag.qsb"
+ DEFINES
+ MASK
+ SHADOW
+ BL0
+)
+
+# Shaders for different blur levels
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders8"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cb1.frag.qsb"
+ DEFINES
+ BLUR
+ BL1
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders9"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cs1.frag.qsb"
+ DEFINES
+ SHADOW
+ BL1
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders10"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cmb1.frag.qsb"
+ DEFINES
+ MASK
+ BLUR
+ BL1
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders11"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cms1.frag.qsb"
+ DEFINES
+ MASK
+ SHADOW
+ BL1
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders12"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cbs1.frag.qsb"
+ DEFINES
+ BLUR
+ SHADOW
+ BL1
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders13"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cmbs1.frag.qsb"
+ DEFINES
+ MASK
+ BLUR
+ SHADOW
+ BL1
+)
+
+# level 2
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders14"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cb2.frag.qsb"
+ DEFINES
+ BLUR
+ BL1
+ BL2
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders15"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cs2.frag.qsb"
+ DEFINES
+ SHADOW
+ BL1
+ BL2
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders16"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cmb2.frag.qsb"
+ DEFINES
+ MASK
+ BLUR
+ BL1
+ BL2
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders17"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cms2.frag.qsb"
+ DEFINES
+ MASK
+ SHADOW
+ BL1
+ BL2
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders18"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cbs2.frag.qsb"
+ DEFINES
+ BLUR
+ SHADOW
+ BL1
+ BL2
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders19"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cmbs2.frag.qsb"
+ DEFINES
+ MASK
+ BLUR
+ SHADOW
+ BL1
+ BL2
+)
+
+# level 3
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders20"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cb3.frag.qsb"
+ DEFINES
+ BLUR
+ BL1
+ BL2
+ BL3
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders21"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cs3.frag.qsb"
+ DEFINES
+ SHADOW
+ BL1
+ BL2
+ BL3
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders22"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cmb3.frag.qsb"
+ DEFINES
+ MASK
+ BLUR
+ BL1
+ BL2
+ BL3
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders23"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cms3.frag.qsb"
+ DEFINES
+ MASK
+ SHADOW
+ BL1
+ BL2
+ BL3
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders24"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cbs3.frag.qsb"
+ DEFINES
+ BLUR
+ SHADOW
+ BL1
+ BL2
+ BL3
+)
+
+qt_internal_add_shaders(QuickEffectsPrivate "multieffect_shaders25"
+ ${EFFECT_SHADER_ARGS}
+ FILES
+ "data/shaders/multieffect.frag"
+ OUTPUTS
+ "data/shaders/multieffect_cmbs3.frag.qsb"
+ DEFINES
+ MASK
+ BLUR
+ SHADOW
+ BL1
+ BL2
+ BL3
+)
diff --git a/src/effects/data/shaders/bluritems.frag b/src/effects/data/shaders/bluritems.frag
index 4198e2f39d..cf3eab7356 100644
--- a/src/effects/data/shaders/bluritems.frag
+++ b/src/effects/data/shaders/bluritems.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 texCoord0;
diff --git a/src/effects/data/shaders/bluritems.frag.qsb b/src/effects/data/shaders/bluritems.frag.qsb
deleted file mode 100644
index 61e85d0daa..0000000000
--- a/src/effects/data/shaders/bluritems.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/bluritems.vert.qsb b/src/effects/data/shaders/bluritems.vert.qsb
deleted file mode 100644
index 97020e2f27..0000000000
--- a/src/effects/data/shaders/bluritems.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/compile.bat b/src/effects/data/shaders/compile.bat
deleted file mode 100644
index a24a4b2a72..0000000000
--- a/src/effects/data/shaders/compile.bat
+++ /dev/null
@@ -1,46 +0,0 @@
-:: Copyright (C) 2022 The Qt Company Ltd.
-:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-:: Multieffect fragment shaders ::
-:: c = Common color effects
-:: m = Mask
-:: b = Blur
-:: s = Shadow
-:: [n] = Amount of blur items used
-
-qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_c0.frag.qsb multieffect.frag
-qsb -DMASK --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cm0.frag.qsb multieffect.frag
-
-:: Special shaders for non-blurred shadows
-qsb -DBL0 -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cs0.frag.qsb multieffect.frag
-qsb -DBL0 -DMASK -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cms0.frag.qsb multieffect.frag
-
-:: Shaders for different blur levels
-qsb -DBL1 -DBLUR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cb1.frag.qsb multieffect.frag
-qsb -DBL1 -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cs1.frag.qsb multieffect.frag
-qsb -DBL1 -DMASK -DBLUR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cmb1.frag.qsb multieffect.frag
-qsb -DBL1 -DMASK -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cms1.frag.qsb multieffect.frag
-qsb -DBL1 -DBLUR -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cbs1.frag.qsb multieffect.frag
-qsb -DBL1 -DMASK -DBLUR -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cmbs1.frag.qsb multieffect.frag
-
-qsb -DBL1 -DBL2 -DBLUR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cb2.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cs2.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DMASK -DBLUR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cmb2.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DMASK -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cms2.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DBLUR -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cbs2.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DMASK -DBLUR -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cmbs2.frag.qsb multieffect.frag
-
-qsb -DBL1 -DBL2 -DBL3 -DBLUR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cb3.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DBL3 -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cs3.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DBL3 -DMASK -DBLUR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cmb3.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DBL3 -DMASK -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cms3.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DBL3 -DBLUR -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cbs3.frag.qsb multieffect.frag
-qsb -DBL1 -DBL2 -DBL3 -DMASK -DBLUR -DSHADOW --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cmbs3.frag.qsb multieffect.frag
-
-:: Multieffect vertex shaders ::
-qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_c.vert.qsb multieffect.vert
-qsb -DSHADOW -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o multieffect_cs.vert.qsb multieffect.vert
-
-:: Bluritems shaders ::
-qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o bluritems.frag.qsb bluritems.frag
-qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o bluritems.vert.qsb bluritems.vert
diff --git a/src/effects/data/shaders/multieffect.frag b/src/effects/data/shaders/multieffect.frag
index 972e87fd41..94099f503f 100644
--- a/src/effects/data/shaders/multieffect.frag
+++ b/src/effects/data/shaders/multieffect.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 texCoord;
diff --git a/src/effects/data/shaders/multieffect_c.vert.qsb b/src/effects/data/shaders/multieffect_c.vert.qsb
deleted file mode 100644
index e95790de4d..0000000000
--- a/src/effects/data/shaders/multieffect_c.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_c0.frag.qsb b/src/effects/data/shaders/multieffect_c0.frag.qsb
deleted file mode 100644
index d57768566d..0000000000
--- a/src/effects/data/shaders/multieffect_c0.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cb1.frag.qsb b/src/effects/data/shaders/multieffect_cb1.frag.qsb
deleted file mode 100644
index 5765ff7fa8..0000000000
--- a/src/effects/data/shaders/multieffect_cb1.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cb2.frag.qsb b/src/effects/data/shaders/multieffect_cb2.frag.qsb
deleted file mode 100644
index 8e5d1da445..0000000000
--- a/src/effects/data/shaders/multieffect_cb2.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cb3.frag.qsb b/src/effects/data/shaders/multieffect_cb3.frag.qsb
deleted file mode 100644
index 1dd589836f..0000000000
--- a/src/effects/data/shaders/multieffect_cb3.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cbs1.frag.qsb b/src/effects/data/shaders/multieffect_cbs1.frag.qsb
deleted file mode 100644
index bbcee6cb9d..0000000000
--- a/src/effects/data/shaders/multieffect_cbs1.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cbs2.frag.qsb b/src/effects/data/shaders/multieffect_cbs2.frag.qsb
deleted file mode 100644
index db04f68296..0000000000
--- a/src/effects/data/shaders/multieffect_cbs2.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cbs3.frag.qsb b/src/effects/data/shaders/multieffect_cbs3.frag.qsb
deleted file mode 100644
index a32d6446f2..0000000000
--- a/src/effects/data/shaders/multieffect_cbs3.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cm0.frag.qsb b/src/effects/data/shaders/multieffect_cm0.frag.qsb
deleted file mode 100644
index d29a83245a..0000000000
--- a/src/effects/data/shaders/multieffect_cm0.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cmb1.frag.qsb b/src/effects/data/shaders/multieffect_cmb1.frag.qsb
deleted file mode 100644
index 8c439307e2..0000000000
--- a/src/effects/data/shaders/multieffect_cmb1.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cmb2.frag.qsb b/src/effects/data/shaders/multieffect_cmb2.frag.qsb
deleted file mode 100644
index 7f040f6b1a..0000000000
--- a/src/effects/data/shaders/multieffect_cmb2.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cmb3.frag.qsb b/src/effects/data/shaders/multieffect_cmb3.frag.qsb
deleted file mode 100644
index 13700a6c0c..0000000000
--- a/src/effects/data/shaders/multieffect_cmb3.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cmbs1.frag.qsb b/src/effects/data/shaders/multieffect_cmbs1.frag.qsb
deleted file mode 100644
index 8f551a2fd4..0000000000
--- a/src/effects/data/shaders/multieffect_cmbs1.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cmbs2.frag.qsb b/src/effects/data/shaders/multieffect_cmbs2.frag.qsb
deleted file mode 100644
index 23496052da..0000000000
--- a/src/effects/data/shaders/multieffect_cmbs2.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cmbs3.frag.qsb b/src/effects/data/shaders/multieffect_cmbs3.frag.qsb
deleted file mode 100644
index 14beb5200f..0000000000
--- a/src/effects/data/shaders/multieffect_cmbs3.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cms0.frag.qsb b/src/effects/data/shaders/multieffect_cms0.frag.qsb
deleted file mode 100644
index c064df1dcf..0000000000
--- a/src/effects/data/shaders/multieffect_cms0.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cms1.frag.qsb b/src/effects/data/shaders/multieffect_cms1.frag.qsb
deleted file mode 100644
index b3f91d5f84..0000000000
--- a/src/effects/data/shaders/multieffect_cms1.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cms2.frag.qsb b/src/effects/data/shaders/multieffect_cms2.frag.qsb
deleted file mode 100644
index 85b1d179b2..0000000000
--- a/src/effects/data/shaders/multieffect_cms2.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cms3.frag.qsb b/src/effects/data/shaders/multieffect_cms3.frag.qsb
deleted file mode 100644
index 440bb16fe0..0000000000
--- a/src/effects/data/shaders/multieffect_cms3.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cs.vert.qsb b/src/effects/data/shaders/multieffect_cs.vert.qsb
deleted file mode 100644
index 2e1cce7fde..0000000000
--- a/src/effects/data/shaders/multieffect_cs.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cs0.frag.qsb b/src/effects/data/shaders/multieffect_cs0.frag.qsb
deleted file mode 100644
index 994a9ac973..0000000000
--- a/src/effects/data/shaders/multieffect_cs0.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cs1.frag.qsb b/src/effects/data/shaders/multieffect_cs1.frag.qsb
deleted file mode 100644
index d6a2071e32..0000000000
--- a/src/effects/data/shaders/multieffect_cs1.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cs2.frag.qsb b/src/effects/data/shaders/multieffect_cs2.frag.qsb
deleted file mode 100644
index 3b8fb53f84..0000000000
--- a/src/effects/data/shaders/multieffect_cs2.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/data/shaders/multieffect_cs3.frag.qsb b/src/effects/data/shaders/multieffect_cs3.frag.qsb
deleted file mode 100644
index 872a993fdd..0000000000
--- a/src/effects/data/shaders/multieffect_cs3.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/effects/qgfxsourceproxy_p.h b/src/effects/qgfxsourceproxy_p.h
index ef22e17027..600f750ad8 100644
--- a/src/effects/qgfxsourceproxy_p.h
+++ b/src/effects/qgfxsourceproxy_p.h
@@ -26,12 +26,11 @@ class QQuickShaderEffectSource;
class QGfxSourceProxy : public QQuickItem
{
Q_OBJECT
- Q_PROPERTY(QQuickItem *input READ input WRITE setInput NOTIFY inputChanged RESET resetInput)
- Q_PROPERTY(QQuickItem *output READ output NOTIFY outputChanged)
- Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged)
- Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
- Q_PROPERTY(Interpolation interpolation READ interpolation WRITE setInterpolation NOTIFY interpolationChanged)
- Q_ENUMS(Interpolation)
+ Q_PROPERTY(QQuickItem *input READ input WRITE setInput NOTIFY inputChanged RESET resetInput FINAL)
+ Q_PROPERTY(QQuickItem *output READ output NOTIFY outputChanged FINAL)
+ Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged FINAL)
+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged FINAL)
+ Q_PROPERTY(Interpolation interpolation READ interpolation WRITE setInterpolation NOTIFY interpolationChanged FINAL)
public:
enum class Interpolation {
@@ -39,6 +38,7 @@ public:
Nearest,
Linear
};
+ Q_ENUM(Interpolation)
QGfxSourceProxy(QQuickItem *parentItem = nullptr);
~QGfxSourceProxy();
diff --git a/src/effects/qquickmultieffect.cpp b/src/effects/qquickmultieffect.cpp
index 14e9a4ee45..7561649fbf 100644
--- a/src/effects/qquickmultieffect.cpp
+++ b/src/effects/qquickmultieffect.cpp
@@ -11,6 +11,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
/*!
+ \keyword Qt Quick Effects
\qmlmodule QtQuick.Effects
\title Qt Quick Effects QML Types
\ingroup qmlmodules
@@ -31,11 +32,13 @@ Q_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
\ingroup qtquick-effects
\brief Applies post-processing effect to an item.
- The MultiEffect type applies a post-processing effect to \l source item.
- Compared to \c {Qt Graphical Effects} module, MultiEffect combines multiple
- effects (blur, shadow, colorization etc.) into a single item and shader which
- makes it better for multiple effects. There are several shader variations
- and the most optimal one gets selected based on the features used.
+ The MultiEffect type, the successor to the deprecated Qt Graphical Effects
+ from Qt 5, applies a post-processing effect to the \l source item. Compared
+ to the Qt Graphical Effects module, MultiEffect combines multiple
+ effects (blur, shadow, colorization etc.) into a single item and shader
+ which makes it better for multiple effects. There are several shader
+ variations and the most optimal one gets selected based on the features
+ used.
MultiEffect is designed specifically for most common effects and can be easily animated.
If the MultiEffect doesn't contain the effect you need, consider implementing a custom
@@ -147,7 +150,7 @@ Q_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
*/
/*!
- \qmlsignal QtQuick::MultiEffect::shaderChanged()
+ \qmlsignal QtQuick.Effects::MultiEffect::shaderChanged()
This signal is emitted when the used shader changes.
\sa fragmentShader, vertexShader
@@ -164,7 +167,7 @@ QQuickMultiEffect::~QQuickMultiEffect()
}
/*!
- \qmlproperty Item QtQuick::MultiEffect::source
+ \qmlproperty Item QtQuick.Effects::MultiEffect::source
This property holds the item to be used as a source for the effect.
If needed, MultiEffect will internally generate a \l ShaderEffectSource
@@ -173,6 +176,11 @@ QQuickMultiEffect::~QQuickMultiEffect()
\note It is not supported to let the effect include itself, for instance
by setting source to the effect's parent.
+ \note If the source item has \l {QtQuick::Item::layer.enabled} {layer.enabled} set to true,
+ it will be used directly. This is good for the performance and often desired, when the source
+ is hidden. But if the source remains visible and the effect adds padding (autoPaddingEnabled,
+ paddingRect), that padding can affect the appearance of the source item.
+
\sa hasProxySource
*/
QQuickItem *QQuickMultiEffect::source() const
@@ -188,7 +196,7 @@ void QQuickMultiEffect::setSource(QQuickItem *item)
}
/*!
- \qmlproperty bool QtQuick::MultiEffect::autoPaddingEnabled
+ \qmlproperty bool QtQuick.Effects::MultiEffect::autoPaddingEnabled
When blur or shadow effects are enabled and this is set to true (default),
the item size is padded automatically based on blurMax and blurMultiplier.
@@ -215,7 +223,7 @@ void QQuickMultiEffect::setAutoPaddingEnabled(bool enabled)
}
/*!
- \qmlproperty rect QtQuick::MultiEffect::paddingRect
+ \qmlproperty rect QtQuick.Effects::MultiEffect::paddingRect
Set this to increase item size manually so that blur and/or shadows will fit.
If autoPaddingEnabled is true and paddingRect is not set, the item is padded
@@ -247,7 +255,7 @@ void QQuickMultiEffect::setPaddingRect(const QRectF &rect)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::brightness
+ \qmlproperty real QtQuick.Effects::MultiEffect::brightness
This property defines how much the source brightness is increased or
decreased.
@@ -268,7 +276,7 @@ void QQuickMultiEffect::setBrightness(qreal brightness)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::contrast
+ \qmlproperty real QtQuick.Effects::MultiEffect::contrast
This property defines how much the source contrast is increased or
decreased.
@@ -289,7 +297,7 @@ void QQuickMultiEffect::setContrast(qreal contrast)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::saturation
+ \qmlproperty real QtQuick.Effects::MultiEffect::saturation
This property defines how much the source saturation is increased or
decreased.
@@ -310,7 +318,7 @@ void QQuickMultiEffect::setSaturation(qreal saturation)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::colorization
+ \qmlproperty real QtQuick.Effects::MultiEffect::colorization
This property defines how much the source is colorized with the
colorizationColor.
@@ -331,7 +339,7 @@ void QQuickMultiEffect::setColorization(qreal colorization)
}
/*!
- \qmlproperty color QtQuick::MultiEffect::colorizationColor
+ \qmlproperty color QtQuick.Effects::MultiEffect::colorizationColor
This property defines the RGBA color value which is used to
colorize the source.
@@ -353,7 +361,7 @@ void QQuickMultiEffect::setColorizationColor(const QColor &color)
}
/*!
- \qmlproperty bool QtQuick::MultiEffect::blurEnabled
+ \qmlproperty bool QtQuick.Effects::MultiEffect::blurEnabled
Enables the blur effect.
@@ -372,7 +380,7 @@ void QQuickMultiEffect::setBlurEnabled(bool enabled)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::blur
+ \qmlproperty real QtQuick.Effects::MultiEffect::blur
This property defines how much blur (radius) is applied to the source.
@@ -397,7 +405,7 @@ void QQuickMultiEffect::setBlur(qreal blur)
}
/*!
- \qmlproperty int QtQuick::MultiEffect::blurMax
+ \qmlproperty int QtQuick.Effects::MultiEffect::blurMax
This property defines the maximum pixel radius that blur with value
1.0 will reach.
@@ -425,7 +433,7 @@ void QQuickMultiEffect::setBlurMax(int blurMax)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::blurMultiplier
+ \qmlproperty real QtQuick.Effects::MultiEffect::blurMultiplier
This property defines a multiplier for extending the blur radius.
@@ -452,7 +460,7 @@ void QQuickMultiEffect::setBlurMultiplier(qreal blurMultiplier)
}
/*!
- \qmlproperty bool QtQuick::MultiEffect::shadowEnabled
+ \qmlproperty bool QtQuick.Effects::MultiEffect::shadowEnabled
Enables the shadow effect.
@@ -471,7 +479,7 @@ void QQuickMultiEffect::setShadowEnabled(bool enabled)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::shadowOpacity
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowOpacity
This property defines the opacity of the drop shadow. This value
is multiplied with the \c shadowColor alpha value.
@@ -492,7 +500,7 @@ void QQuickMultiEffect::setShadowOpacity(qreal shadowOpacity)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::shadowBlur
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowBlur
This property defines how much blur (radius) is applied to the shadow.
@@ -517,7 +525,7 @@ void QQuickMultiEffect::setShadowBlur(qreal shadowBlur)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::shadowHorizontalOffset
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowHorizontalOffset
This property defines the horizontal offset of the shadow from the
item center.
@@ -542,7 +550,7 @@ void QQuickMultiEffect::setShadowHorizontalOffset(qreal offset)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::shadowVerticalOffset
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowVerticalOffset
This property defines the vertical offset of the shadow from the
item center.
@@ -567,7 +575,7 @@ void QQuickMultiEffect::setShadowVerticalOffset(qreal offset)
}
/*!
- \qmlproperty color QtQuick::MultiEffect::shadowColor
+ \qmlproperty color QtQuick.Effects::MultiEffect::shadowColor
This property defines the RGBA color value which is used for
the shadow. It is useful for example when a shadow is used for
@@ -589,7 +597,7 @@ void QQuickMultiEffect::setShadowColor(const QColor &color)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::shadowScale
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowScale
This property defines the scale of the shadow. Scaling is applied from
the center of the item.
@@ -614,7 +622,7 @@ void QQuickMultiEffect::setShadowScale(qreal shadowScale)
}
/*!
- \qmlproperty bool QtQuick::MultiEffect::maskEnabled
+ \qmlproperty bool QtQuick.Effects::MultiEffect::maskEnabled
Enables the mask effect.
@@ -633,7 +641,7 @@ void QQuickMultiEffect::setMaskEnabled(bool enabled)
}
/*!
- \qmlproperty Item QtQuick::MultiEffect::maskSource
+ \qmlproperty Item QtQuick.Effects::MultiEffect::maskSource
Source item for the mask effect. Should point to ShaderEffectSource,
item with \l {QtQuick::Item::layer.enabled} {layer.enabled} set to \c true,
@@ -653,7 +661,7 @@ void QQuickMultiEffect::setMaskSource(QQuickItem *item)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::maskThresholdMin
+ \qmlproperty real QtQuick.Effects::MultiEffect::maskThresholdMin
This property defines a lower threshold value for the mask pixels.
The mask pixels that have an alpha value below this property are used
@@ -677,7 +685,7 @@ void QQuickMultiEffect::setMaskThresholdMin(qreal threshold)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::maskSpreadAtMin
+ \qmlproperty real QtQuick.Effects::MultiEffect::maskSpreadAtMin
This property defines the smoothness of the mask edges near the
maskThresholdMin. Setting higher spread values softens the transition
@@ -700,7 +708,7 @@ void QQuickMultiEffect::setMaskSpreadAtMin(qreal spread)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::maskThresholdMax
+ \qmlproperty real QtQuick.Effects::MultiEffect::maskThresholdMax
This property defines an upper threshold value for the mask pixels.
The mask pixels that have an alpha value below this property are used
@@ -724,7 +732,7 @@ void QQuickMultiEffect::setMaskThresholdMax(qreal threshold)
}
/*!
- \qmlproperty real QtQuick::MultiEffect::maskSpreadAtMax
+ \qmlproperty real QtQuick.Effects::MultiEffect::maskSpreadAtMax
This property defines the smoothness of the mask edges near the
maskThresholdMax. Using higher spread values softens the transition
@@ -747,7 +755,7 @@ void QQuickMultiEffect::setMaskSpreadAtMax(qreal spread)
}
/*!
- \qmlproperty bool QtQuick::MultiEffect::maskInverted
+ \qmlproperty bool QtQuick.Effects::MultiEffect::maskInverted
This property switches the mask to the opposite side; instead of
masking away the content outside maskThresholdMin and maskThresholdMax,
@@ -768,7 +776,8 @@ void QQuickMultiEffect::setMaskInverted(bool inverted)
}
/*!
- \qmlproperty rect QtQuick::MultiEffect::itemRect
+ \qmlproperty rect QtQuick.Effects::MultiEffect::itemRect
+ \readonly
Read-only access to effect item rectangle. This can be used e.g. to see
the area item covers.
@@ -782,7 +791,7 @@ QRectF QQuickMultiEffect::itemRect() const
}
/*!
- \qmlproperty string QtQuick::MultiEffect::fragmentShader
+ \qmlproperty string QtQuick.Effects::MultiEffect::fragmentShader
\readonly
Read-only access to filename of the currently used fragment shader.
@@ -794,7 +803,7 @@ QString QQuickMultiEffect::fragmentShader() const
}
/*!
- \qmlproperty string QtQuick::MultiEffect::vertexShader
+ \qmlproperty string QtQuick.Effects::MultiEffect::vertexShader
\readonly
Read-only access to filename of the currently used vertex shader.
@@ -806,7 +815,7 @@ QString QQuickMultiEffect::vertexShader() const
}
/*!
- \qmlproperty bool QtQuick::MultiEffect::hasProxySource
+ \qmlproperty bool QtQuick.Effects::MultiEffect::hasProxySource
\readonly
Returns true when the MultiEffect internally creates \l ShaderEffectSource
@@ -1490,9 +1499,11 @@ void QQuickMultiEffectPrivate::updateColorizationColor()
if (!m_shaderEffect)
return;
- int alpha = std::clamp(int(m_colorizationColor.alpha() * m_colorization), 0, 255);
- QColor colorizationColor = m_colorizationColor;
- colorizationColor.setAlpha(alpha);
+ float alpha = std::clamp(float(m_colorizationColor.alphaF() * m_colorization), 0.0f, 1.0f);
+ QVector4D colorizationColor(m_colorizationColor.redF(),
+ m_colorizationColor.greenF(),
+ m_colorizationColor.blueF(),
+ alpha);
m_shaderEffect->setProperty("colorizationColor", colorizationColor);
}
@@ -1501,9 +1512,12 @@ void QQuickMultiEffectPrivate::updateShadowColor()
if (!m_shaderEffect)
return;
- int alpha = std::clamp(int(m_shadowColor.alpha() * m_shadowOpacity), 0, 255);
- QColor shadowColor = m_shadowColor;
- shadowColor.setAlpha(alpha);
+ float alpha = std::clamp(float(m_shadowColor.alphaF() * m_shadowOpacity), 0.0f, 1.0f);
+ QVector4D shadowColor(m_shadowColor.redF(),
+ m_shadowColor.greenF(),
+ m_shadowColor.blueF(),
+ alpha);
+
m_shaderEffect->setProperty("shadowColor", shadowColor);
}
@@ -1558,8 +1572,8 @@ void QQuickMultiEffectPrivate::updateBlurItemSizes(bool forceUpdate)
// First blur item size to be half of th source item
// extended size, rounded to next divisible by 16.
QSizeF sourceSize = itemRect().size();
- QSizeF firstItemSize(std::ceil(sourceSize.width() / 32) * 16,
- std::ceil(sourceSize.height() / 32) * 16);
+ QSizeF firstItemSize(std::ceil(sourceSize.width() / 16) * 8,
+ std::ceil(sourceSize.height() / 16) * 8);
if (!forceUpdate && m_firstBlurItemSize == firstItemSize)
return;
@@ -1582,7 +1596,7 @@ void QQuickMultiEffectPrivate::updateBlurItemSizes(bool forceUpdate)
void QQuickMultiEffectPrivate::updateEffectShaders()
{
Q_Q(QQuickMultiEffect);
- if (!q->isComponentComplete())
+ if (!q->isComponentComplete() || !m_shaderEffect)
return;
QString vShader = QStringLiteral("multieffect_c");
@@ -1649,6 +1663,10 @@ void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel)
if (!m_shaderEffect)
return;
+ const auto engine = qmlEngine(q);
+ if (!engine)
+ return;
+
// Lowest blur level uses 3 items, highest 5 items.
int itemsAmount = blurLevel == 0 ? 0 : blurLevel + 2;
@@ -1656,7 +1674,6 @@ void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel)
// Add more blur items.
// Note that by design blur items are only added and never reduced
// during the lifetime of the effect component.
- const auto engine = qmlEngine(q);
QUrl blurVs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.vert.qsb"));
QUrl blurFs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.frag.qsb"));
QQmlComponent blurComponent(engine, QUrl(QStringLiteral("qrc:/data/BlurItem.qml")));
@@ -1680,11 +1697,12 @@ void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel)
}
// Set the blur items source components
- static const auto dummyShaderSource = new QQuickShaderEffectSource(q);
+ if (!m_dummyShaderSource)
+ m_dummyShaderSource = new QQuickShaderEffectSource(q);
for (int i = 0; i < m_blurEffects.size(); i++) {
auto *blurEffect = m_blurEffects[i];
auto sourceItem = (i >= itemsAmount) ?
- static_cast<QQuickItem *>(dummyShaderSource) : (i == 0) ?
+ static_cast<QQuickItem *>(m_dummyShaderSource) : (i == 0) ?
static_cast<QQuickItem *>(m_shaderSource->output()) :
static_cast<QQuickItem *>(m_blurEffects[i - 1]);
auto sourceVariant = QVariant::fromValue<QQuickItem*>(sourceItem);
diff --git a/src/effects/qquickmultieffect_p.h b/src/effects/qquickmultieffect_p.h
index 1ba6143ac8..8438a0947b 100644
--- a/src/effects/qquickmultieffect_p.h
+++ b/src/effects/qquickmultieffect_p.h
@@ -29,7 +29,7 @@ QT_BEGIN_NAMESPACE
class QQuickMultiEffectPrivate;
-class Q_QUICKEFFECTS_PRIVATE_EXPORT QQuickMultiEffect : public QQuickItem
+class Q_QUICKEFFECTS_EXPORT QQuickMultiEffect : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickItem *source READ source WRITE setSource NOTIFY sourceChanged)
diff --git a/src/effects/qquickmultieffect_p_p.h b/src/effects/qquickmultieffect_p_p.h
index 2df33db45b..7d635e2e25 100644
--- a/src/effects/qquickmultieffect_p_p.h
+++ b/src/effects/qquickmultieffect_p_p.h
@@ -27,6 +27,7 @@ QT_REQUIRE_CONFIG(quick_shadereffect);
QT_BEGIN_NAMESPACE
class QQuickShaderEffect;
+class QQuickShaderEffectSource;
class QQuickMultiEffectPrivate : public QQuickItemPrivate
{
@@ -145,6 +146,7 @@ private:
QQuickItem *m_sourceItem = nullptr;
QGfxSourceProxy *m_shaderSource = nullptr;
QQuickShaderEffect *m_shaderEffect = nullptr;
+ QQuickShaderEffectSource *m_dummyShaderSource = nullptr;
QVector<QQuickShaderEffect *> m_blurEffects;
bool m_autoPaddingEnabled = true;
QRectF m_paddingRect;
diff --git a/src/effects/qtquickeffectsglobal_p.h b/src/effects/qtquickeffectsglobal_p.h
index 9629624c36..09ea4fef23 100644
--- a/src/effects/qtquickeffectsglobal_p.h
+++ b/src/effects/qtquickeffectsglobal_p.h
@@ -16,6 +16,6 @@
//
#include <QtCore/qglobal.h>
-#include <QtQuickEffects/private/qtquickeffectsexports_p.h>
+#include <QtQuickEffects/qtquickeffectsexports.h>
#endif // QTQUICKEFFECTSGLOBAL_P_H
diff --git a/src/imports/CMakeLists.txt b/src/imports/CMakeLists.txt
index 29dd2ce5c0..e07c5b955d 100644
--- a/src/imports/CMakeLists.txt
+++ b/src/imports/CMakeLists.txt
@@ -1,2 +1 @@
-add_subdirectory(builtins)
add_subdirectory(tooling)
diff --git a/src/imports/builtins/CMakeLists.txt b/src/imports/builtins/CMakeLists.txt
deleted file mode 100644
index 54269aa51e..0000000000
--- a/src/imports/builtins/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-set(qml_type_files builtins.qmltypes jsroot.qmltypes)
-
-qt_path_join(qml_install_dir "${QT_INSTALL_DIR}" "${INSTALL_QMLDIR}")
-qt_copy_or_install(FILES ${qml_type_files}
- DESTINATION ${qml_install_dir}
-)
-
-# in prefix builds we also need to copy the files into the build directory of
-# the module, so that they are located together with the QML modules
-if(QT_WILL_INSTALL)
- qt_path_join(qml_build_dir "${QT_BUILD_DIR}" "${INSTALL_QMLDIR}")
- file(COPY ${qml_type_files} DESTINATION ${qml_build_dir})
-endif()
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes
deleted file mode 100644
index 7a74279ee8..0000000000
--- a/src/imports/builtins/builtins.qmltypes
+++ /dev/null
@@ -1,273 +0,0 @@
-import QtQuick.tooling 1.2
-
-Module {
- dependencies: []
-
- Component {
- name: "void"
- extension: "undefined"
- exports: ["QML/void 1.0"]
- accessSemantics: "none"
- }
-
- Component {
- name: "std::nullptr_t"
- accessSemantics: "none"
- }
-
- Component {
- name: "QVariant"
- accessSemantics: "value"
- exports: ["QML/var 1.0", "QML/variant 1.0"]
- exportMetaObjectRevisions: [256, 256]
- }
-
- Component {
- name: "QVariantList"
- valueType: "QVariant"
- accessSemantics: "sequence"
- }
-
- Component {
- name: "QVariantMap"
- accessSemantics: "value"
- }
-
- Component {
- name: "QJSValue"
- accessSemantics: "value"
- }
-
- Component {
- file: "private/qqmlengine_p.h"
- name: "QObject"
- extension: "Object"
- accessSemantics: "reference"
- exports: ["QML/QtObject 1.0"]
- exportMetaObjectRevisions: [256]
- Property {
- name: "objectName"
- type: "QString"
- bindable: "bindableObjectName"
- read: "objectName"
- write: "setObjectName"
- notify: "objectNameChanged"
- index: 0
- }
- Signal {
- name: "objectNameChanged"
- Parameter { name: "objectName"; type: "QString" }
- }
- Method {
- name: "_q_reregisterTimers"
- Parameter { type: "void"; isPointer: true }
- }
- Method { name: "toString"; type: "string" }
- Method { name: "destroy" }
- Method {
- name: "destroy"
- Parameter { name: "delay"; type: "int" }
- }
- }
-
- Component {
- file: "qqmlcomponent.h"
- name: "QQmlComponent"
- accessSemantics: "reference"
- prototype: "QObject"
- exports: ["QML/Component 1.0"]
- exportMetaObjectRevisions: [256]
- attachedType: "QQmlComponentAttached"
- Enum {
- name: "CompilationMode"
- values: ["PreferSynchronous", "Asynchronous"]
- }
- Enum {
- name: "Status"
- values: ["Null", "Ready", "Loading", "Error"]
- }
- Property {
- name: "progress"
- type: "double"
- read: "progress"
- notify: "progressChanged"
- index: 0
- isReadonly: true
- }
- Property {
- name: "status"
- type: "Status"
- read: "status"
- notify: "statusChanged"
- index: 1
- isReadonly: true
- }
- Property { name: "url"; type: "QUrl"; read: "url"; index: 2; isReadonly: true }
- Signal {
- name: "statusChanged"
- Parameter { type: "QQmlComponent::Status" }
- }
- Signal {
- name: "progressChanged"
- Parameter { type: "double" }
- }
- Method {
- name: "loadUrl"
- Parameter { name: "url"; type: "QUrl" }
- }
- Method {
- name: "loadUrl"
- Parameter { name: "url"; type: "QUrl" }
- Parameter { name: "mode"; type: "CompilationMode" }
- }
- Method {
- name: "setData"
- Parameter { type: "QByteArray" }
- Parameter { name: "baseUrl"; type: "QUrl" }
- }
- Method { name: "errorString"; type: "QString" }
- Method { name: "createObject"; isJavaScriptFunction: true }
- Method {
- name: "createObject"
- type: "QObject"
- isPointer: true
- Parameter { name: "parent"; type: "QObject"; isPointer: true }
- Parameter { name: "properties"; type: "QVariantMap" }
- }
- Method {
- name: "createObject"
- type: "QObject"
- isPointer: true
- Parameter { name: "parent"; type: "QObject"; isPointer: true }
- }
- Method { name: "createObject"; type: "QObject"; isPointer: true }
- Method { name: "incubateObject"; isJavaScriptFunction: true }
- }
-
- Component {
- file: "private/qqmlcomponentattached_p.h"
- name: "QQmlComponentAttached"
- accessSemantics: "reference"
- prototype: "QObject"
- Signal { name: "completed" }
- Signal { name: "destruction" }
- }
-
- Component {
- file: "qqmllist.h"
- name: "QQmlListProperty<QObject>"
- accessSemantics: "sequence"
- valueType: "QObject"
- }
-
- Component {
- file: "qobject.h"
- name: "QObjectList"
- accessSemantics: "sequence"
- valueType: "QObject"
- }
-
- Component {
- name: "int"
- extension: "Number"
- exports: ["QML/int 1.0"]
- exportMetaObjectRevisions: [256]
- accessSemantics: "value"
- }
-
- Component {
- name: "uint"
- extension: "Number"
- accessSemantics: "value"
- }
-
- Component {
- name: "qlonglong"
- accessSemantics: "value"
- }
-
- Component {
- name: "qulonglong"
- accessSemantics: "value"
- }
-
- Component {
- name: "float"
- extension: "Number"
- accessSemantics: "value"
- }
-
- Component {
- name: "double"
- extension: "Number"
- exports: ["QML/real 1.0", "QML/double 1.0"]
- exportMetaObjectRevisions: [256]
- accessSemantics: "value"
- }
-
- Component {
- name: "QChar"
- accessSemantics: "value"
- }
-
- Component {
- name: "QString"
- extension: "String"
- exports: ["QML/string 1.0"]
- exportMetaObjectRevisions: [256]
- accessSemantics: "value"
- }
-
- Component {
- name: "QStringList"
- valueType: "QString"
- accessSemantics: "sequence"
- }
-
- Component {
- name: "bool"
- extension: "Boolean"
- exports: ["QML/bool 1.0"]
- exportMetaObjectRevisions: [256]
- accessSemantics: "value"
- }
-
- Component {
- name: "QDateTime"
- extension: "Date"
- exports: ["QML/date 1.0"]
- exportMetaObjectRevisions: [256]
- accessSemantics: "value"
- }
-
- Component {
- name: "QDate"
- accessSemantics: "value"
- }
-
- Component {
- name: "QTime"
- accessSemantics: "value"
- }
-
- Component {
- name: "QUrl"
- extension: "Url"
- exports: ["QML/url 1.0"]
- exportMetaObjectRevisions: [256]
- accessSemantics: "value"
- }
-
- Component {
- name: "QRegularExpression"
- exports: ["QML/regexp 1.0"]
- extension: "RegExp"
- accessSemantics: "value"
- }
-
- Component {
- name: "QByteArray"
- extension: "ArrayBuffer"
- accessSemantics: "value"
- }
-}
diff --git a/src/imports/builtins/jsroot.qmltypes b/src/imports/builtins/jsroot.qmltypes
deleted file mode 100644
index 572d810336..0000000000
--- a/src/imports/builtins/jsroot.qmltypes
+++ /dev/null
@@ -1,3395 +0,0 @@
-import QtQuick.tooling 1.2
-
-// This file describes the plugin-supplied types contained in the library.
-// It is used for QML tooling purposes only.
-//
-// This file was auto-generated by qmltyperegistrar.
-
-Module {
- Component {
- name: "Array"
- accessSemantics: "reference"
- prototype: "ArrayPrototype"
- Property { name: "length"; type: "number" }
- }
- Component {
- name: "ArrayBuffer"
- accessSemantics: "reference"
- prototype: "ArrayBufferPrototype"
- }
- Component {
- name: "ArrayBufferPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "byteLength"; type: "number" }
- Method {
- name: "constructor"
- type: "ArrayBuffer"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "slice"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- }
- Component {
- name: "ArrayPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "length"; type: "number" }
- Method {
- name: "constructor"
- type: "Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "toLocaleString"; isJavaScriptFunction: true }
- Method {
- name: "concat"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "copyWithin"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method { name: "entries"; isJavaScriptFunction: true }
- Method {
- name: "fill"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "find"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "findIndex"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "includes"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "join"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "keys"; isJavaScriptFunction: true }
- Method { name: "pop"; isJavaScriptFunction: true }
- Method {
- name: "push"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "reverse"; isJavaScriptFunction: true }
- Method { name: "shift"; isJavaScriptFunction: true }
- Method {
- name: "slice"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "sort"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "splice"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "unshift"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "indexOf"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "lastIndexOf"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "every"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "some"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "forEach"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "map"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "filter"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "reduce"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "reduceRight"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "values"; isJavaScriptFunction: true }
- }
- Component {
- name: "Atomics"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method {
- name: "add"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "and"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "compareExchange"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "exchange"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "isLockFree"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "load"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "or"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "store"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "sub"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "wait"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "wake"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "xor"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "Boolean"; accessSemantics: "reference"; prototype: "BooleanPrototype" }
- Component {
- name: "BooleanPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method {
- name: "constructor"
- type: "Boolean"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "valueOf"; isJavaScriptFunction: true }
- }
- Component {
- name: "Console"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method { name: "debug"; isJavaScriptFunction: true }
- Method { name: "log"; isJavaScriptFunction: true }
- Method { name: "info"; isJavaScriptFunction: true }
- Method { name: "warn"; isJavaScriptFunction: true }
- Method { name: "error"; isJavaScriptFunction: true }
- Method { name: "assert"; isJavaScriptFunction: true }
- Method { name: "count"; isJavaScriptFunction: true }
- Method { name: "profile"; isJavaScriptFunction: true }
- Method { name: "profileEnd"; isJavaScriptFunction: true }
- Method { name: "time"; isJavaScriptFunction: true }
- Method { name: "timeEnd"; isJavaScriptFunction: true }
- Method { name: "trace"; isJavaScriptFunction: true }
- Method { name: "exception"; isJavaScriptFunction: true }
- }
- Component { name: "DataView"; accessSemantics: "reference"; prototype: "DataViewPrototype" }
- Component {
- name: "DataViewPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "buffer"; type: "DataViewPrototypeBuffer" }
- Property { name: "byteLength"; type: "number" }
- Property { name: "byteOffset"; type: "number" }
- Method {
- name: "constructor"
- type: "DataView"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getInt8"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getUint8"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getInt16"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getUint16"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getInt32"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getUint32"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getFloat32"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getFloat64"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setInt8"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setUint8"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setInt16"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setUint16"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setInt32"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setUint32"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setFloat32"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setFloat64"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "getUInt8"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getUInt16"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "getUInt32"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setUInt8"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setUInt16"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setUInt32"
- isJavaScriptFunction: true
- Parameter {}
- }
- }
- Component {
- name: "DataViewPrototypeBuffer"
- accessSemantics: "reference"
- prototype: "ArrayBufferPrototype"
- }
- Component { name: "Date"; accessSemantics: "reference"; prototype: "DatePrototype" }
- Component {
- name: "DatePrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method {
- name: "constructor"
- type: "Date"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "toDateString"; isJavaScriptFunction: true }
- Method { name: "toTimeString"; isJavaScriptFunction: true }
- Method { name: "toLocaleString"; isJavaScriptFunction: true }
- Method { name: "toLocaleDateString"; isJavaScriptFunction: true }
- Method { name: "toLocaleTimeString"; isJavaScriptFunction: true }
- Method { name: "valueOf"; isJavaScriptFunction: true }
- Method { name: "getTime"; isJavaScriptFunction: true }
- Method { name: "getYear"; isJavaScriptFunction: true }
- Method { name: "getFullYear"; isJavaScriptFunction: true }
- Method { name: "getUTCFullYear"; isJavaScriptFunction: true }
- Method { name: "getMonth"; isJavaScriptFunction: true }
- Method { name: "getUTCMonth"; isJavaScriptFunction: true }
- Method { name: "getDate"; isJavaScriptFunction: true }
- Method { name: "getUTCDate"; isJavaScriptFunction: true }
- Method { name: "getDay"; isJavaScriptFunction: true }
- Method { name: "getUTCDay"; isJavaScriptFunction: true }
- Method { name: "getHours"; isJavaScriptFunction: true }
- Method { name: "getUTCHours"; isJavaScriptFunction: true }
- Method { name: "getMinutes"; isJavaScriptFunction: true }
- Method { name: "getUTCMinutes"; isJavaScriptFunction: true }
- Method { name: "getSeconds"; isJavaScriptFunction: true }
- Method { name: "getUTCSeconds"; isJavaScriptFunction: true }
- Method { name: "getMilliseconds"; isJavaScriptFunction: true }
- Method { name: "getUTCMilliseconds"; isJavaScriptFunction: true }
- Method { name: "getTimezoneOffset"; isJavaScriptFunction: true }
- Method {
- name: "setTime"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setMilliseconds"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setUTCMilliseconds"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setSeconds"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setUTCSeconds"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setMinutes"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setUTCMinutes"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setHours"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setUTCHours"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setDate"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setUTCDate"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setMonth"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setUTCMonth"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setYear"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "setFullYear"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setUTCFullYear"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method { name: "toUTCString"; isJavaScriptFunction: true }
- Method { name: "toGMTString"; isJavaScriptFunction: true }
- Method { name: "toISOString"; isJavaScriptFunction: true }
- Method {
- name: "toJSON"
- isJavaScriptFunction: true
- Parameter {}
- }
- }
- Component {
- name: "Error"
- accessSemantics: "reference"
- prototype: "ErrorPrototype"
- Property { name: "stack"; type: "string" }
- Property { name: "fileName" }
- Property { name: "lineNumber" }
- }
- Component {
- name: "ErrorPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "message"; type: "string" }
- Property { name: "name"; type: "string" }
- Method {
- name: "constructor"
- type: "Error"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- }
- Component {
- name: "EvalError"
- accessSemantics: "reference"
- prototype: "EvalErrorPrototype"
- Property { name: "stack"; type: "string" }
- Property { name: "fileName" }
- Property { name: "lineNumber" }
- }
- Component {
- name: "EvalErrorPrototype"
- accessSemantics: "reference"
- prototype: "ErrorPrototype"
- Property { name: "message"; type: "string" }
- Property { name: "name"; type: "string" }
- Method {
- name: "constructor"
- type: "EvalError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- }
- Component {
- name: "Float32Array"
- accessSemantics: "reference"
- prototype: "Float32ArrayPrototype"
- }
- Component {
- name: "Float32ArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Float32Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component {
- name: "Float64Array"
- accessSemantics: "reference"
- prototype: "Float64ArrayPrototype"
- }
- Component {
- name: "Float64ArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Float64Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component {
- name: "Function"
- accessSemantics: "reference"
- prototype: "FunctionPrototype"
- Property { name: "prototype"; type: "Object" }
- Property { name: "name"; type: "string"; isReadonly: true }
- Property { name: "length"; type: "number"; isReadonly: true }
- }
- Component {
- name: "FunctionPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "name"; type: "string"; isReadonly: true }
- Property { name: "length"; type: "number"; isReadonly: true }
- Property { name: "caller" }
- Property { name: "arguments" }
- Method {
- name: "constructor"
- type: "Function"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method {
- name: "apply"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "call"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "bind"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "connect"; isJavaScriptFunction: true }
- Method { name: "disconnect"; isJavaScriptFunction: true }
- }
- Component {
- name: "GlobalObject"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "Atomics"; type: "Atomics" }
- Property { name: "Math"; type: "Math" }
- Property { name: "JSON"; type: "JSON" }
- Property { name: "Reflect"; type: "Reflect" }
- Property { name: "undefined"; isReadonly: true }
- Property { name: "NaN"; type: "number"; isReadonly: true }
- Property { name: "Infinity"; type: "number"; isReadonly: true }
- Property { name: "Qt"; type: "Qt" }
- Property { name: "console"; type: "Console" }
- Method {
- name: "Object"
- type: "Object"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "String"
- type: "String"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "Symbol"; type: "undefined"; isConstructor: true; isJavaScriptFunction: true }
- Method {
- name: "Number"
- type: "Number"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "Boolean"
- type: "Boolean"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "Array"
- type: "Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "Function"
- type: "Function"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "Date"
- type: "Date"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "RegExp"
- type: "RegExp"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Error"
- type: "Error"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "EvalError"
- type: "EvalError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "RangeError"
- type: "RangeError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "ReferenceError"
- type: "ReferenceError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "SyntaxError"
- type: "SyntaxError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "TypeError"
- type: "TypeError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "URIError"
- type: "URIError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "Promise"
- type: "Promise"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "URL"; type: "URL"; isConstructor: true; isJavaScriptFunction: true }
- Method {
- name: "URLSearchParams"
- type: "URLSearchParams"
- isConstructor: true
- isJavaScriptFunction: true
- }
- Method {
- name: "SharedArrayBuffer"
- type: "SharedArrayBuffer"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "ArrayBuffer"
- type: "ArrayBuffer"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "DataView"
- type: "DataView"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "WeakSet"; type: "WeakSet"; isConstructor: true; isJavaScriptFunction: true }
- Method { name: "Set"; type: "Set"; isConstructor: true; isJavaScriptFunction: true }
- Method { name: "WeakMap"; type: "WeakMap"; isConstructor: true; isJavaScriptFunction: true }
- Method { name: "Map"; type: "Map"; isConstructor: true; isJavaScriptFunction: true }
- Method {
- name: "Int8Array"
- type: "Int8Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Uint8Array"
- type: "Uint8Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Int16Array"
- type: "Int16Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Uint16Array"
- type: "Uint16Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Int32Array"
- type: "Int32Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Uint32Array"
- type: "Uint32Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Uint8ClampedArray"
- type: "Uint8ClampedArray"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Float32Array"
- type: "Float32Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Float64Array"
- type: "Float64Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "Proxy"
- type: "Proxy"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "eval"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "parseInt"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "parseFloat"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "isNaN"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "isFinite"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "decodeURI"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "decodeURIComponent"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "encodeURI"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "encodeURIComponent"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "escape"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "unescape"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "qsTranslate"; isJavaScriptFunction: true }
- Method { name: "QT_TRANSLATE_NOOP"; isJavaScriptFunction: true }
- Method { name: "qsTr"; isJavaScriptFunction: true }
- Method { name: "QT_TR_NOOP"; isJavaScriptFunction: true }
- Method { name: "qsTrId"; isJavaScriptFunction: true }
- Method { name: "QT_TRID_NOOP"; isJavaScriptFunction: true }
- Method { name: "print"; isJavaScriptFunction: true }
- Method { name: "gc"; isJavaScriptFunction: true }
- }
- Component { name: "Int16Array"; accessSemantics: "reference"; prototype: "Int16ArrayPrototype" }
- Component {
- name: "Int16ArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Int16Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "Int32Array"; accessSemantics: "reference"; prototype: "Int32ArrayPrototype" }
- Component {
- name: "Int32ArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Int32Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "Int8Array"; accessSemantics: "reference"; prototype: "Int8ArrayPrototype" }
- Component {
- name: "Int8ArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Int8Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component {
- name: "IntrinsicTypedArrayPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "buffer" }
- Property { name: "byteLength" }
- Property { name: "byteOffset" }
- Property { name: "length" }
- Method {
- name: "copyWithin"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method { name: "entries"; isJavaScriptFunction: true }
- Method {
- name: "every"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "fill"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "filter"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "find"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "findIndex"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "forEach"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "includes"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "indexOf"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "join"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "keys"; isJavaScriptFunction: true }
- Method {
- name: "lastIndexOf"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "map"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "reduce"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "reduceRight"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "reverse"; isJavaScriptFunction: true }
- Method {
- name: "some"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "set"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "slice"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "subarray"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method { name: "toLocaleString"; isJavaScriptFunction: true }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "values"; isJavaScriptFunction: true }
- }
- Component {
- name: "JSON"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method {
- name: "parse"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "stringify"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "Map"; accessSemantics: "reference"; prototype: "MapPrototype" }
- Component {
- name: "MapPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "size"; type: "number" }
- Method { name: "constructor"; type: "Map"; isConstructor: true; isJavaScriptFunction: true }
- Method { name: "clear"; isJavaScriptFunction: true }
- Method {
- name: "delete"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "forEach"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "get"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "has"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "keys"; isJavaScriptFunction: true }
- Method {
- name: "set"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method { name: "values"; isJavaScriptFunction: true }
- Method { name: "entries"; isJavaScriptFunction: true }
- }
- Component {
- name: "Math"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "E"; type: "number"; isReadonly: true }
- Property { name: "LN2"; type: "number"; isReadonly: true }
- Property { name: "LN10"; type: "number"; isReadonly: true }
- Property { name: "LOG2E"; type: "number"; isReadonly: true }
- Property { name: "LOG10E"; type: "number"; isReadonly: true }
- Property { name: "PI"; type: "number"; isReadonly: true }
- Property { name: "SQRT1_2"; type: "number"; isReadonly: true }
- Property { name: "SQRT2"; type: "number"; isReadonly: true }
- Method {
- name: "abs"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "acos"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "acosh"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "asin"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "asinh"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "atan"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "atanh"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "atan2"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "cbrt"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "ceil"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "clz32"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "cos"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "cosh"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "exp"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "expm1"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "floor"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "fround"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "hypot"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "imul"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "log"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "log10"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "log1p"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "log2"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "max"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "min"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "pow"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method { name: "random"; isJavaScriptFunction: true }
- Method {
- name: "round"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "sign"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "sin"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "sinh"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "sqrt"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "tan"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "tanh"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "trunc"
- isJavaScriptFunction: true
- Parameter {}
- }
- }
- Component { name: "Number"; accessSemantics: "reference"; prototype: "NumberPrototype" }
- Component {
- name: "NumberPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method {
- name: "constructor"
- type: "Number"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "toString"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toLocaleString"; isJavaScriptFunction: true }
- Method { name: "valueOf"; isJavaScriptFunction: true }
- Method {
- name: "toFixed"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "toExponential"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "toPrecision"
- isJavaScriptFunction: true
- Parameter {}
- }
- }
- Component { name: "Object"; accessSemantics: "reference"; prototype: "ObjectPrototype" }
- Component {
- name: "ObjectPrototype"
- accessSemantics: "reference"
- Property { name: "__proto__" }
- Method {
- name: "constructor"
- type: "Object"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "toLocaleString"; isJavaScriptFunction: true }
- Method { name: "valueOf"; isJavaScriptFunction: true }
- Method {
- name: "hasOwnProperty"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "isPrototypeOf"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "propertyIsEnumerable"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "__defineGetter__"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "__defineSetter__"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "Promise"; accessSemantics: "reference"; prototype: "PromisePrototype" }
- Component {
- name: "PromisePrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method {
- name: "constructor"
- type: "Promise"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "then"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "catch"
- isJavaScriptFunction: true
- Parameter {}
- }
- }
- Component { name: "Proxy"; accessSemantics: "reference"; prototype: "ObjectPrototype" }
- Component {
- name: "Qt"
- accessSemantics: "reference"
- prototype: "QtPrototype"
- Property { name: "objectName"; type: "string" }
- Property { name: "application"; type: "QtApplication" }
- Property { name: "platform"; type: "QtPlatform" }
- Property { name: "inputMethod"; type: "QtInputMethod" }
- Property { name: "styleHints"; type: "QtStyleHints" }
- Property { name: "uiLanguage"; type: "string" }
- Method { name: "objectNameChanged"; isJavaScriptFunction: true }
- Method { name: "include"; isJavaScriptFunction: true }
- Method { name: "isQtObject"; isJavaScriptFunction: true }
- Method { name: "color"; isJavaScriptFunction: true }
- Method { name: "rgba"; isJavaScriptFunction: true }
- Method { name: "hsla"; isJavaScriptFunction: true }
- Method { name: "hsva"; isJavaScriptFunction: true }
- Method { name: "colorEqual"; isJavaScriptFunction: true }
- Method { name: "rect"; isJavaScriptFunction: true }
- Method { name: "point"; isJavaScriptFunction: true }
- Method { name: "size"; isJavaScriptFunction: true }
- Method { name: "vector2d"; isJavaScriptFunction: true }
- Method { name: "vector3d"; isJavaScriptFunction: true }
- Method { name: "vector4d"; isJavaScriptFunction: true }
- Method { name: "quaternion"; isJavaScriptFunction: true }
- Method { name: "matrix4x4"; isJavaScriptFunction: true }
- Method { name: "lighter"; isJavaScriptFunction: true }
- Method { name: "darker"; isJavaScriptFunction: true }
- Method { name: "alpha"; isJavaScriptFunction: true }
- Method { name: "tint"; isJavaScriptFunction: true }
- Method { name: "formatDate"; isJavaScriptFunction: true }
- Method { name: "formatTime"; isJavaScriptFunction: true }
- Method { name: "formatDateTime"; isJavaScriptFunction: true }
- Method { name: "locale"; isJavaScriptFunction: true }
- Method { name: "url"; isJavaScriptFunction: true }
- Method { name: "resolvedUrl"; isJavaScriptFunction: true }
- Method { name: "openUrlExternally"; isJavaScriptFunction: true }
- Method { name: "font"; isJavaScriptFunction: true }
- Method { name: "fontFamilies"; isJavaScriptFunction: true }
- Method { name: "md5"; isJavaScriptFunction: true }
- Method { name: "btoa"; isJavaScriptFunction: true }
- Method { name: "atob"; isJavaScriptFunction: true }
- Method { name: "quit"; isJavaScriptFunction: true }
- Method { name: "exit"; isJavaScriptFunction: true }
- Method { name: "createQmlObject"; isJavaScriptFunction: true }
- Method { name: "createComponent"; isJavaScriptFunction: true }
- Method { name: "binding"; isJavaScriptFunction: true }
- Method { name: "callLater"; isJavaScriptFunction: true }
- }
- Component {
- name: "QtApplication"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "objectName"; type: "string" }
- Property { name: "arguments"; type: "QtApplicationArguments" }
- Property { name: "name"; type: "string" }
- Property { name: "version"; type: "string" }
- Property { name: "organization"; type: "string" }
- Property { name: "domain"; type: "string" }
- Method { name: "objectNameChanged"; isJavaScriptFunction: true }
- Method { name: "aboutToQuit"; isJavaScriptFunction: true }
- Method { name: "nameChanged"; isJavaScriptFunction: true }
- Method { name: "versionChanged"; isJavaScriptFunction: true }
- Method { name: "organizationChanged"; isJavaScriptFunction: true }
- Method { name: "domainChanged"; isJavaScriptFunction: true }
- Method { name: "setName"; isJavaScriptFunction: true }
- Method { name: "setVersion"; isJavaScriptFunction: true }
- Method { name: "setOrganization"; isJavaScriptFunction: true }
- Method { name: "setDomain"; isJavaScriptFunction: true }
- }
- Component {
- name: "QtApplicationArguments"
- accessSemantics: "reference"
- prototype: "ArrayPrototype"
- Property { name: "0"; type: "string" }
- Property { name: "1"; type: "string" }
- Property { name: "length"; type: "number" }
- }
- Component {
- name: "QtInputMethod"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "objectName"; type: "string" }
- Method { name: "objectNameChanged"; isJavaScriptFunction: true }
- }
- Component {
- name: "QtPlatform"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "objectName"; type: "string" }
- Property { name: "os"; type: "string" }
- Property { name: "pluginName"; type: "string" }
- Method { name: "objectNameChanged"; isJavaScriptFunction: true }
- }
- Component {
- name: "QtPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "color0"; type: "number"; isReadonly: true }
- Property { name: "color1"; type: "number"; isReadonly: true }
- Property { name: "black"; type: "number"; isReadonly: true }
- Property { name: "white"; type: "number"; isReadonly: true }
- Property { name: "darkGray"; type: "number"; isReadonly: true }
- Property { name: "gray"; type: "number"; isReadonly: true }
- Property { name: "lightGray"; type: "number"; isReadonly: true }
- Property { name: "red"; type: "number"; isReadonly: true }
- Property { name: "green"; type: "number"; isReadonly: true }
- Property { name: "blue"; type: "number"; isReadonly: true }
- Property { name: "cyan"; type: "number"; isReadonly: true }
- Property { name: "magenta"; type: "number"; isReadonly: true }
- Property { name: "yellow"; type: "number"; isReadonly: true }
- Property { name: "darkRed"; type: "number"; isReadonly: true }
- Property { name: "darkGreen"; type: "number"; isReadonly: true }
- Property { name: "darkBlue"; type: "number"; isReadonly: true }
- Property { name: "darkCyan"; type: "number"; isReadonly: true }
- Property { name: "darkMagenta"; type: "number"; isReadonly: true }
- Property { name: "darkYellow"; type: "number"; isReadonly: true }
- Property { name: "transparent"; type: "number"; isReadonly: true }
- Property { name: "Unknown"; type: "number"; isReadonly: true }
- Property { name: "Light"; type: "number"; isReadonly: true }
- Property { name: "Dark"; type: "number"; isReadonly: true }
- Property { name: "NoButton"; type: "number"; isReadonly: true }
- Property { name: "LeftButton"; type: "number"; isReadonly: true }
- Property { name: "RightButton"; type: "number"; isReadonly: true }
- Property { name: "MiddleButton"; type: "number"; isReadonly: true }
- Property { name: "BackButton"; type: "number"; isReadonly: true }
- Property { name: "XButton1"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton1"; type: "number"; isReadonly: true }
- Property { name: "ForwardButton"; type: "number"; isReadonly: true }
- Property { name: "XButton2"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton2"; type: "number"; isReadonly: true }
- Property { name: "TaskButton"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton3"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton4"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton5"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton6"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton7"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton8"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton9"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton10"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton11"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton12"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton13"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton14"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton15"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton16"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton17"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton18"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton19"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton20"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton21"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton22"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton23"; type: "number"; isReadonly: true }
- Property { name: "ExtraButton24"; type: "number"; isReadonly: true }
- Property { name: "AllButtons"; type: "number"; isReadonly: true }
- Property { name: "MaxMouseButton"; type: "number"; isReadonly: true }
- Property { name: "MouseButtonMask"; type: "number"; isReadonly: true }
- Property { name: "Horizontal"; type: "number"; isReadonly: true }
- Property { name: "Vertical"; type: "number"; isReadonly: true }
- Property { name: "NoFocus"; type: "number"; isReadonly: true }
- Property { name: "TabFocus"; type: "number"; isReadonly: true }
- Property { name: "ClickFocus"; type: "number"; isReadonly: true }
- Property { name: "StrongFocus"; type: "number"; isReadonly: true }
- Property { name: "WheelFocus"; type: "number"; isReadonly: true }
- Property { name: "NoTabFocus"; type: "number"; isReadonly: true }
- Property { name: "TabFocusTextControls"; type: "number"; isReadonly: true }
- Property { name: "TabFocusListControls"; type: "number"; isReadonly: true }
- Property { name: "TabFocusAllControls"; type: "number"; isReadonly: true }
- Property { name: "AscendingOrder"; type: "number"; isReadonly: true }
- Property { name: "DescendingOrder"; type: "number"; isReadonly: true }
- Property { name: "KeepEmptyParts"; type: "number"; isReadonly: true }
- Property { name: "SkipEmptyParts"; type: "number"; isReadonly: true }
- Property { name: "AlignLeft"; type: "number"; isReadonly: true }
- Property { name: "AlignLeading"; type: "number"; isReadonly: true }
- Property { name: "AlignRight"; type: "number"; isReadonly: true }
- Property { name: "AlignTrailing"; type: "number"; isReadonly: true }
- Property { name: "AlignHCenter"; type: "number"; isReadonly: true }
- Property { name: "AlignJustify"; type: "number"; isReadonly: true }
- Property { name: "AlignAbsolute"; type: "number"; isReadonly: true }
- Property { name: "AlignHorizontal_Mask"; type: "number"; isReadonly: true }
- Property { name: "AlignTop"; type: "number"; isReadonly: true }
- Property { name: "AlignBottom"; type: "number"; isReadonly: true }
- Property { name: "AlignVCenter"; type: "number"; isReadonly: true }
- Property { name: "AlignBaseline"; type: "number"; isReadonly: true }
- Property { name: "AlignVertical_Mask"; type: "number"; isReadonly: true }
- Property { name: "AlignCenter"; type: "number"; isReadonly: true }
- Property { name: "TextSingleLine"; type: "number"; isReadonly: true }
- Property { name: "TextDontClip"; type: "number"; isReadonly: true }
- Property { name: "TextExpandTabs"; type: "number"; isReadonly: true }
- Property { name: "TextShowMnemonic"; type: "number"; isReadonly: true }
- Property { name: "TextWordWrap"; type: "number"; isReadonly: true }
- Property { name: "TextWrapAnywhere"; type: "number"; isReadonly: true }
- Property { name: "TextDontPrint"; type: "number"; isReadonly: true }
- Property { name: "TextIncludeTrailingSpaces"; type: "number"; isReadonly: true }
- Property { name: "TextHideMnemonic"; type: "number"; isReadonly: true }
- Property { name: "TextJustificationForced"; type: "number"; isReadonly: true }
- Property { name: "TextForceLeftToRight"; type: "number"; isReadonly: true }
- Property { name: "TextForceRightToLeft"; type: "number"; isReadonly: true }
- Property { name: "TextLongestVariant"; type: "number"; isReadonly: true }
- Property { name: "ElideLeft"; type: "number"; isReadonly: true }
- Property { name: "ElideRight"; type: "number"; isReadonly: true }
- Property { name: "ElideMiddle"; type: "number"; isReadonly: true }
- Property { name: "ElideNone"; type: "number"; isReadonly: true }
- Property { name: "Widget"; type: "number"; isReadonly: true }
- Property { name: "Window"; type: "number"; isReadonly: true }
- Property { name: "Dialog"; type: "number"; isReadonly: true }
- Property { name: "Sheet"; type: "number"; isReadonly: true }
- Property { name: "Drawer"; type: "number"; isReadonly: true }
- Property { name: "Popup"; type: "number"; isReadonly: true }
- Property { name: "Tool"; type: "number"; isReadonly: true }
- Property { name: "ToolTip"; type: "number"; isReadonly: true }
- Property { name: "SplashScreen"; type: "number"; isReadonly: true }
- Property { name: "Desktop"; type: "number"; isReadonly: true }
- Property { name: "SubWindow"; type: "number"; isReadonly: true }
- Property { name: "ForeignWindow"; type: "number"; isReadonly: true }
- Property { name: "CoverWindow"; type: "number"; isReadonly: true }
- Property { name: "WindowType_Mask"; type: "number"; isReadonly: true }
- Property { name: "MSWindowsFixedSizeDialogHint"; type: "number"; isReadonly: true }
- Property { name: "MSWindowsOwnDC"; type: "number"; isReadonly: true }
- Property { name: "BypassWindowManagerHint"; type: "number"; isReadonly: true }
- Property { name: "X11BypassWindowManagerHint"; type: "number"; isReadonly: true }
- Property { name: "FramelessWindowHint"; type: "number"; isReadonly: true }
- Property { name: "WindowTitleHint"; type: "number"; isReadonly: true }
- Property { name: "WindowSystemMenuHint"; type: "number"; isReadonly: true }
- Property { name: "WindowMinimizeButtonHint"; type: "number"; isReadonly: true }
- Property { name: "WindowMaximizeButtonHint"; type: "number"; isReadonly: true }
- Property { name: "WindowMinMaxButtonsHint"; type: "number"; isReadonly: true }
- Property { name: "WindowContextHelpButtonHint"; type: "number"; isReadonly: true }
- Property { name: "WindowShadeButtonHint"; type: "number"; isReadonly: true }
- Property { name: "WindowStaysOnTopHint"; type: "number"; isReadonly: true }
- Property { name: "WindowTransparentForInput"; type: "number"; isReadonly: true }
- Property { name: "WindowOverridesSystemGestures"; type: "number"; isReadonly: true }
- Property { name: "WindowDoesNotAcceptFocus"; type: "number"; isReadonly: true }
- Property { name: "MaximizeUsingFullscreenGeometryHint"; type: "number"; isReadonly: true }
- Property { name: "CustomizeWindowHint"; type: "number"; isReadonly: true }
- Property { name: "WindowStaysOnBottomHint"; type: "number"; isReadonly: true }
- Property { name: "WindowCloseButtonHint"; type: "number"; isReadonly: true }
- Property { name: "MacWindowToolBarButtonHint"; type: "number"; isReadonly: true }
- Property { name: "BypassGraphicsProxyWidget"; type: "number"; isReadonly: true }
- Property { name: "NoDropShadowWindowHint"; type: "number"; isReadonly: true }
- Property { name: "WindowFullscreenButtonHint"; type: "number"; isReadonly: true }
- Property { name: "WindowNoState"; type: "number"; isReadonly: true }
- Property { name: "WindowMinimized"; type: "number"; isReadonly: true }
- Property { name: "WindowMaximized"; type: "number"; isReadonly: true }
- Property { name: "WindowFullScreen"; type: "number"; isReadonly: true }
- Property { name: "WindowActive"; type: "number"; isReadonly: true }
- Property { name: "ApplicationSuspended"; type: "number"; isReadonly: true }
- Property { name: "ApplicationHidden"; type: "number"; isReadonly: true }
- Property { name: "ApplicationInactive"; type: "number"; isReadonly: true }
- Property { name: "ApplicationActive"; type: "number"; isReadonly: true }
- Property { name: "PrimaryOrientation"; type: "number"; isReadonly: true }
- Property { name: "PortraitOrientation"; type: "number"; isReadonly: true }
- Property { name: "LandscapeOrientation"; type: "number"; isReadonly: true }
- Property { name: "InvertedPortraitOrientation"; type: "number"; isReadonly: true }
- Property { name: "InvertedLandscapeOrientation"; type: "number"; isReadonly: true }
- Property { name: "WA_Disabled"; type: "number"; isReadonly: true }
- Property { name: "WA_UnderMouse"; type: "number"; isReadonly: true }
- Property { name: "WA_MouseTracking"; type: "number"; isReadonly: true }
- Property { name: "WA_OpaquePaintEvent"; type: "number"; isReadonly: true }
- Property { name: "WA_StaticContents"; type: "number"; isReadonly: true }
- Property { name: "WA_LaidOut"; type: "number"; isReadonly: true }
- Property { name: "WA_PaintOnScreen"; type: "number"; isReadonly: true }
- Property { name: "WA_NoSystemBackground"; type: "number"; isReadonly: true }
- Property { name: "WA_UpdatesDisabled"; type: "number"; isReadonly: true }
- Property { name: "WA_Mapped"; type: "number"; isReadonly: true }
- Property { name: "WA_InputMethodEnabled"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_Visible"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_Hidden"; type: "number"; isReadonly: true }
- Property { name: "WA_ForceDisabled"; type: "number"; isReadonly: true }
- Property { name: "WA_KeyCompression"; type: "number"; isReadonly: true }
- Property { name: "WA_PendingMoveEvent"; type: "number"; isReadonly: true }
- Property { name: "WA_PendingResizeEvent"; type: "number"; isReadonly: true }
- Property { name: "WA_SetPalette"; type: "number"; isReadonly: true }
- Property { name: "WA_SetFont"; type: "number"; isReadonly: true }
- Property { name: "WA_SetCursor"; type: "number"; isReadonly: true }
- Property { name: "WA_NoChildEventsFromChildren"; type: "number"; isReadonly: true }
- Property { name: "WA_WindowModified"; type: "number"; isReadonly: true }
- Property { name: "WA_Resized"; type: "number"; isReadonly: true }
- Property { name: "WA_Moved"; type: "number"; isReadonly: true }
- Property { name: "WA_PendingUpdate"; type: "number"; isReadonly: true }
- Property { name: "WA_InvalidSize"; type: "number"; isReadonly: true }
- Property { name: "WA_CustomWhatsThis"; type: "number"; isReadonly: true }
- Property { name: "WA_LayoutOnEntireRect"; type: "number"; isReadonly: true }
- Property { name: "WA_OutsideWSRange"; type: "number"; isReadonly: true }
- Property { name: "WA_GrabbedShortcut"; type: "number"; isReadonly: true }
- Property { name: "WA_TransparentForMouseEvents"; type: "number"; isReadonly: true }
- Property { name: "WA_PaintUnclipped"; type: "number"; isReadonly: true }
- Property { name: "WA_SetWindowIcon"; type: "number"; isReadonly: true }
- Property { name: "WA_NoMouseReplay"; type: "number"; isReadonly: true }
- Property { name: "WA_DeleteOnClose"; type: "number"; isReadonly: true }
- Property { name: "WA_RightToLeft"; type: "number"; isReadonly: true }
- Property { name: "WA_SetLayoutDirection"; type: "number"; isReadonly: true }
- Property { name: "WA_NoChildEventsForParent"; type: "number"; isReadonly: true }
- Property { name: "WA_ForceUpdatesDisabled"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_Created"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_CompressKeys"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_InPaintEvent"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_Reparented"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_ConfigPending"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_Polished"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_OwnSizePolicy"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_ExplicitShowHide"; type: "number"; isReadonly: true }
- Property { name: "WA_ShowModal"; type: "number"; isReadonly: true }
- Property { name: "WA_MouseNoMask"; type: "number"; isReadonly: true }
- Property { name: "WA_NoMousePropagation"; type: "number"; isReadonly: true }
- Property { name: "WA_Hover"; type: "number"; isReadonly: true }
- Property { name: "WA_InputMethodTransparent"; type: "number"; isReadonly: true }
- Property { name: "WA_QuitOnClose"; type: "number"; isReadonly: true }
- Property { name: "WA_KeyboardFocusChange"; type: "number"; isReadonly: true }
- Property { name: "WA_AcceptDrops"; type: "number"; isReadonly: true }
- Property { name: "WA_DropSiteRegistered"; type: "number"; isReadonly: true }
- Property { name: "WA_WindowPropagation"; type: "number"; isReadonly: true }
- Property { name: "WA_NoX11EventCompression"; type: "number"; isReadonly: true }
- Property { name: "WA_TintedBackground"; type: "number"; isReadonly: true }
- Property { name: "WA_X11OpenGLOverlay"; type: "number"; isReadonly: true }
- Property { name: "WA_AlwaysShowToolTips"; type: "number"; isReadonly: true }
- Property { name: "WA_MacOpaqueSizeGrip"; type: "number"; isReadonly: true }
- Property { name: "WA_SetStyle"; type: "number"; isReadonly: true }
- Property { name: "WA_SetLocale"; type: "number"; isReadonly: true }
- Property { name: "WA_MacShowFocusRect"; type: "number"; isReadonly: true }
- Property { name: "WA_MacNormalSize"; type: "number"; isReadonly: true }
- Property { name: "WA_MacSmallSize"; type: "number"; isReadonly: true }
- Property { name: "WA_MacMiniSize"; type: "number"; isReadonly: true }
- Property { name: "WA_LayoutUsesWidgetRect"; type: "number"; isReadonly: true }
- Property { name: "WA_StyledBackground"; type: "number"; isReadonly: true }
- Property { name: "WA_CanHostQMdiSubWindowTitleBar"; type: "number"; isReadonly: true }
- Property { name: "WA_MacAlwaysShowToolWindow"; type: "number"; isReadonly: true }
- Property { name: "WA_StyleSheet"; type: "number"; isReadonly: true }
- Property { name: "WA_ShowWithoutActivating"; type: "number"; isReadonly: true }
- Property { name: "WA_X11BypassTransientForHint"; type: "number"; isReadonly: true }
- Property { name: "WA_NativeWindow"; type: "number"; isReadonly: true }
- Property { name: "WA_DontCreateNativeAncestors"; type: "number"; isReadonly: true }
- Property { name: "WA_DontShowOnScreen"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeDesktop"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeDock"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeToolBar"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeMenu"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeUtility"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeSplash"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeDialog"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeDropDownMenu"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypePopupMenu"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeToolTip"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeNotification"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeCombo"; type: "number"; isReadonly: true }
- Property { name: "WA_X11NetWmWindowTypeDND"; type: "number"; isReadonly: true }
- Property { name: "WA_SetWindowModality"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_WindowOpacitySet"; type: "number"; isReadonly: true }
- Property { name: "WA_TranslucentBackground"; type: "number"; isReadonly: true }
- Property { name: "WA_AcceptTouchEvents"; type: "number"; isReadonly: true }
- Property { name: "WA_WState_AcceptedTouchBeginEvent"; type: "number"; isReadonly: true }
- Property { name: "WA_TouchPadAcceptSingleTouchEvents"; type: "number"; isReadonly: true }
- Property { name: "WA_X11DoNotAcceptFocus"; type: "number"; isReadonly: true }
- Property { name: "WA_AlwaysStackOnTop"; type: "number"; isReadonly: true }
- Property { name: "WA_TabletTracking"; type: "number"; isReadonly: true }
- Property { name: "WA_ContentsMarginsRespectsSafeArea"; type: "number"; isReadonly: true }
- Property { name: "WA_StyleSheetTarget"; type: "number"; isReadonly: true }
- Property { name: "WA_AttributeCount"; type: "number"; isReadonly: true }
- Property { name: "AA_DontShowIconsInMenus"; type: "number"; isReadonly: true }
- Property { name: "AA_NativeWindows"; type: "number"; isReadonly: true }
- Property { name: "AA_DontCreateNativeWidgetSiblings"; type: "number"; isReadonly: true }
- Property { name: "AA_PluginApplication"; type: "number"; isReadonly: true }
- Property { name: "AA_DontUseNativeMenuBar"; type: "number"; isReadonly: true }
- Property { name: "AA_MacDontSwapCtrlAndMeta"; type: "number"; isReadonly: true }
- Property { name: "AA_Use96Dpi"; type: "number"; isReadonly: true }
- Property { name: "AA_DisableNativeVirtualKeyboard"; type: "number"; isReadonly: true }
- Property { name: "AA_SynthesizeTouchForUnhandledMouseEvents"; type: "number"; isReadonly: true }
- Property { name: "AA_SynthesizeMouseForUnhandledTouchEvents"; type: "number"; isReadonly: true }
- Property { name: "AA_UseHighDpiPixmaps"; type: "number"; isReadonly: true }
- Property { name: "AA_ForceRasterWidgets"; type: "number"; isReadonly: true }
- Property { name: "AA_UseDesktopOpenGL"; type: "number"; isReadonly: true }
- Property { name: "AA_UseOpenGLES"; type: "number"; isReadonly: true }
- Property { name: "AA_UseSoftwareOpenGL"; type: "number"; isReadonly: true }
- Property { name: "AA_ShareOpenGLContexts"; type: "number"; isReadonly: true }
- Property { name: "AA_SetPalette"; type: "number"; isReadonly: true }
- Property { name: "AA_EnableHighDpiScaling"; type: "number"; isReadonly: true }
- Property { name: "AA_DisableHighDpiScaling"; type: "number"; isReadonly: true }
- Property { name: "AA_UseStyleSheetPropagationInWidgetStyles"; type: "number"; isReadonly: true }
- Property { name: "AA_DontUseNativeDialogs"; type: "number"; isReadonly: true }
- Property {
- name: "AA_SynthesizeMouseForUnhandledTabletEvents"
- type: "number"
- isReadonly: true
- }
- Property { name: "AA_CompressHighFrequencyEvents"; type: "number"; isReadonly: true }
- Property { name: "AA_DontCheckOpenGLContextThreadAffinity"; type: "number"; isReadonly: true }
- Property { name: "AA_DisableShaderDiskCache"; type: "number"; isReadonly: true }
- Property { name: "AA_DontShowShortcutsInContextMenus"; type: "number"; isReadonly: true }
- Property { name: "AA_CompressTabletEvents"; type: "number"; isReadonly: true }
- Property { name: "AA_DisableSessionManager"; type: "number"; isReadonly: true }
- Property { name: "AA_AttributeCount"; type: "number"; isReadonly: true }
- Property { name: "ColorMode_Mask"; type: "number"; isReadonly: true }
- Property { name: "AutoColor"; type: "number"; isReadonly: true }
- Property { name: "ColorOnly"; type: "number"; isReadonly: true }
- Property { name: "MonoOnly"; type: "number"; isReadonly: true }
- Property { name: "AlphaDither_Mask"; type: "number"; isReadonly: true }
- Property { name: "ThresholdAlphaDither"; type: "number"; isReadonly: true }
- Property { name: "OrderedAlphaDither"; type: "number"; isReadonly: true }
- Property { name: "DiffuseAlphaDither"; type: "number"; isReadonly: true }
- Property { name: "NoAlpha"; type: "number"; isReadonly: true }
- Property { name: "Dither_Mask"; type: "number"; isReadonly: true }
- Property { name: "DiffuseDither"; type: "number"; isReadonly: true }
- Property { name: "OrderedDither"; type: "number"; isReadonly: true }
- Property { name: "ThresholdDither"; type: "number"; isReadonly: true }
- Property { name: "DitherMode_Mask"; type: "number"; isReadonly: true }
- Property { name: "AutoDither"; type: "number"; isReadonly: true }
- Property { name: "PreferDither"; type: "number"; isReadonly: true }
- Property { name: "AvoidDither"; type: "number"; isReadonly: true }
- Property { name: "NoOpaqueDetection"; type: "number"; isReadonly: true }
- Property { name: "NoFormatConversion"; type: "number"; isReadonly: true }
- Property { name: "TransparentMode"; type: "number"; isReadonly: true }
- Property { name: "OpaqueMode"; type: "number"; isReadonly: true }
- Property { name: "Key_Space"; type: "number"; isReadonly: true }
- Property { name: "Key_Any"; type: "number"; isReadonly: true }
- Property { name: "Key_Exclam"; type: "number"; isReadonly: true }
- Property { name: "Key_QuoteDbl"; type: "number"; isReadonly: true }
- Property { name: "Key_NumberSign"; type: "number"; isReadonly: true }
- Property { name: "Key_Dollar"; type: "number"; isReadonly: true }
- Property { name: "Key_Percent"; type: "number"; isReadonly: true }
- Property { name: "Key_Ampersand"; type: "number"; isReadonly: true }
- Property { name: "Key_Apostrophe"; type: "number"; isReadonly: true }
- Property { name: "Key_ParenLeft"; type: "number"; isReadonly: true }
- Property { name: "Key_ParenRight"; type: "number"; isReadonly: true }
- Property { name: "Key_Asterisk"; type: "number"; isReadonly: true }
- Property { name: "Key_Plus"; type: "number"; isReadonly: true }
- Property { name: "Key_Comma"; type: "number"; isReadonly: true }
- Property { name: "Key_Minus"; type: "number"; isReadonly: true }
- Property { name: "Key_Period"; type: "number"; isReadonly: true }
- Property { name: "Key_Slash"; type: "number"; isReadonly: true }
- Property { name: "Key_0"; type: "number"; isReadonly: true }
- Property { name: "Key_1"; type: "number"; isReadonly: true }
- Property { name: "Key_2"; type: "number"; isReadonly: true }
- Property { name: "Key_3"; type: "number"; isReadonly: true }
- Property { name: "Key_4"; type: "number"; isReadonly: true }
- Property { name: "Key_5"; type: "number"; isReadonly: true }
- Property { name: "Key_6"; type: "number"; isReadonly: true }
- Property { name: "Key_7"; type: "number"; isReadonly: true }
- Property { name: "Key_8"; type: "number"; isReadonly: true }
- Property { name: "Key_9"; type: "number"; isReadonly: true }
- Property { name: "Key_Colon"; type: "number"; isReadonly: true }
- Property { name: "Key_Semicolon"; type: "number"; isReadonly: true }
- Property { name: "Key_Less"; type: "number"; isReadonly: true }
- Property { name: "Key_Equal"; type: "number"; isReadonly: true }
- Property { name: "Key_Greater"; type: "number"; isReadonly: true }
- Property { name: "Key_Question"; type: "number"; isReadonly: true }
- Property { name: "Key_At"; type: "number"; isReadonly: true }
- Property { name: "Key_A"; type: "number"; isReadonly: true }
- Property { name: "Key_B"; type: "number"; isReadonly: true }
- Property { name: "Key_C"; type: "number"; isReadonly: true }
- Property { name: "Key_D"; type: "number"; isReadonly: true }
- Property { name: "Key_E"; type: "number"; isReadonly: true }
- Property { name: "Key_F"; type: "number"; isReadonly: true }
- Property { name: "Key_G"; type: "number"; isReadonly: true }
- Property { name: "Key_H"; type: "number"; isReadonly: true }
- Property { name: "Key_I"; type: "number"; isReadonly: true }
- Property { name: "Key_J"; type: "number"; isReadonly: true }
- Property { name: "Key_K"; type: "number"; isReadonly: true }
- Property { name: "Key_L"; type: "number"; isReadonly: true }
- Property { name: "Key_M"; type: "number"; isReadonly: true }
- Property { name: "Key_N"; type: "number"; isReadonly: true }
- Property { name: "Key_O"; type: "number"; isReadonly: true }
- Property { name: "Key_P"; type: "number"; isReadonly: true }
- Property { name: "Key_Q"; type: "number"; isReadonly: true }
- Property { name: "Key_R"; type: "number"; isReadonly: true }
- Property { name: "Key_S"; type: "number"; isReadonly: true }
- Property { name: "Key_T"; type: "number"; isReadonly: true }
- Property { name: "Key_U"; type: "number"; isReadonly: true }
- Property { name: "Key_V"; type: "number"; isReadonly: true }
- Property { name: "Key_W"; type: "number"; isReadonly: true }
- Property { name: "Key_X"; type: "number"; isReadonly: true }
- Property { name: "Key_Y"; type: "number"; isReadonly: true }
- Property { name: "Key_Z"; type: "number"; isReadonly: true }
- Property { name: "Key_BracketLeft"; type: "number"; isReadonly: true }
- Property { name: "Key_Backslash"; type: "number"; isReadonly: true }
- Property { name: "Key_BracketRight"; type: "number"; isReadonly: true }
- Property { name: "Key_AsciiCircum"; type: "number"; isReadonly: true }
- Property { name: "Key_Underscore"; type: "number"; isReadonly: true }
- Property { name: "Key_QuoteLeft"; type: "number"; isReadonly: true }
- Property { name: "Key_BraceLeft"; type: "number"; isReadonly: true }
- Property { name: "Key_Bar"; type: "number"; isReadonly: true }
- Property { name: "Key_BraceRight"; type: "number"; isReadonly: true }
- Property { name: "Key_AsciiTilde"; type: "number"; isReadonly: true }
- Property { name: "Key_nobreakspace"; type: "number"; isReadonly: true }
- Property { name: "Key_exclamdown"; type: "number"; isReadonly: true }
- Property { name: "Key_cent"; type: "number"; isReadonly: true }
- Property { name: "Key_sterling"; type: "number"; isReadonly: true }
- Property { name: "Key_currency"; type: "number"; isReadonly: true }
- Property { name: "Key_yen"; type: "number"; isReadonly: true }
- Property { name: "Key_brokenbar"; type: "number"; isReadonly: true }
- Property { name: "Key_section"; type: "number"; isReadonly: true }
- Property { name: "Key_diaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_copyright"; type: "number"; isReadonly: true }
- Property { name: "Key_ordfeminine"; type: "number"; isReadonly: true }
- Property { name: "Key_guillemotleft"; type: "number"; isReadonly: true }
- Property { name: "Key_notsign"; type: "number"; isReadonly: true }
- Property { name: "Key_hyphen"; type: "number"; isReadonly: true }
- Property { name: "Key_registered"; type: "number"; isReadonly: true }
- Property { name: "Key_macron"; type: "number"; isReadonly: true }
- Property { name: "Key_degree"; type: "number"; isReadonly: true }
- Property { name: "Key_plusminus"; type: "number"; isReadonly: true }
- Property { name: "Key_twosuperior"; type: "number"; isReadonly: true }
- Property { name: "Key_threesuperior"; type: "number"; isReadonly: true }
- Property { name: "Key_acute"; type: "number"; isReadonly: true }
- Property { name: "Key_mu"; type: "number"; isReadonly: true }
- Property { name: "Key_paragraph"; type: "number"; isReadonly: true }
- Property { name: "Key_periodcentered"; type: "number"; isReadonly: true }
- Property { name: "Key_cedilla"; type: "number"; isReadonly: true }
- Property { name: "Key_onesuperior"; type: "number"; isReadonly: true }
- Property { name: "Key_masculine"; type: "number"; isReadonly: true }
- Property { name: "Key_guillemotright"; type: "number"; isReadonly: true }
- Property { name: "Key_onequarter"; type: "number"; isReadonly: true }
- Property { name: "Key_onehalf"; type: "number"; isReadonly: true }
- Property { name: "Key_threequarters"; type: "number"; isReadonly: true }
- Property { name: "Key_questiondown"; type: "number"; isReadonly: true }
- Property { name: "Key_Agrave"; type: "number"; isReadonly: true }
- Property { name: "Key_Aacute"; type: "number"; isReadonly: true }
- Property { name: "Key_Acircumflex"; type: "number"; isReadonly: true }
- Property { name: "Key_Atilde"; type: "number"; isReadonly: true }
- Property { name: "Key_Adiaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_Aring"; type: "number"; isReadonly: true }
- Property { name: "Key_AE"; type: "number"; isReadonly: true }
- Property { name: "Key_Ccedilla"; type: "number"; isReadonly: true }
- Property { name: "Key_Egrave"; type: "number"; isReadonly: true }
- Property { name: "Key_Eacute"; type: "number"; isReadonly: true }
- Property { name: "Key_Ecircumflex"; type: "number"; isReadonly: true }
- Property { name: "Key_Ediaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_Igrave"; type: "number"; isReadonly: true }
- Property { name: "Key_Iacute"; type: "number"; isReadonly: true }
- Property { name: "Key_Icircumflex"; type: "number"; isReadonly: true }
- Property { name: "Key_Idiaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_ETH"; type: "number"; isReadonly: true }
- Property { name: "Key_Ntilde"; type: "number"; isReadonly: true }
- Property { name: "Key_Ograve"; type: "number"; isReadonly: true }
- Property { name: "Key_Oacute"; type: "number"; isReadonly: true }
- Property { name: "Key_Ocircumflex"; type: "number"; isReadonly: true }
- Property { name: "Key_Otilde"; type: "number"; isReadonly: true }
- Property { name: "Key_Odiaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_multiply"; type: "number"; isReadonly: true }
- Property { name: "Key_Ooblique"; type: "number"; isReadonly: true }
- Property { name: "Key_Ugrave"; type: "number"; isReadonly: true }
- Property { name: "Key_Uacute"; type: "number"; isReadonly: true }
- Property { name: "Key_Ucircumflex"; type: "number"; isReadonly: true }
- Property { name: "Key_Udiaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_Yacute"; type: "number"; isReadonly: true }
- Property { name: "Key_THORN"; type: "number"; isReadonly: true }
- Property { name: "Key_ssharp"; type: "number"; isReadonly: true }
- Property { name: "Key_division"; type: "number"; isReadonly: true }
- Property { name: "Key_ydiaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_Escape"; type: "number"; isReadonly: true }
- Property { name: "Key_Tab"; type: "number"; isReadonly: true }
- Property { name: "Key_Backtab"; type: "number"; isReadonly: true }
- Property { name: "Key_Backspace"; type: "number"; isReadonly: true }
- Property { name: "Key_Return"; type: "number"; isReadonly: true }
- Property { name: "Key_Enter"; type: "number"; isReadonly: true }
- Property { name: "Key_Insert"; type: "number"; isReadonly: true }
- Property { name: "Key_Delete"; type: "number"; isReadonly: true }
- Property { name: "Key_Pause"; type: "number"; isReadonly: true }
- Property { name: "Key_Print"; type: "number"; isReadonly: true }
- Property { name: "Key_SysReq"; type: "number"; isReadonly: true }
- Property { name: "Key_Clear"; type: "number"; isReadonly: true }
- Property { name: "Key_Home"; type: "number"; isReadonly: true }
- Property { name: "Key_End"; type: "number"; isReadonly: true }
- Property { name: "Key_Left"; type: "number"; isReadonly: true }
- Property { name: "Key_Up"; type: "number"; isReadonly: true }
- Property { name: "Key_Right"; type: "number"; isReadonly: true }
- Property { name: "Key_Down"; type: "number"; isReadonly: true }
- Property { name: "Key_PageUp"; type: "number"; isReadonly: true }
- Property { name: "Key_PageDown"; type: "number"; isReadonly: true }
- Property { name: "Key_Shift"; type: "number"; isReadonly: true }
- Property { name: "Key_Control"; type: "number"; isReadonly: true }
- Property { name: "Key_Meta"; type: "number"; isReadonly: true }
- Property { name: "Key_Alt"; type: "number"; isReadonly: true }
- Property { name: "Key_CapsLock"; type: "number"; isReadonly: true }
- Property { name: "Key_NumLock"; type: "number"; isReadonly: true }
- Property { name: "Key_ScrollLock"; type: "number"; isReadonly: true }
- Property { name: "Key_F1"; type: "number"; isReadonly: true }
- Property { name: "Key_F2"; type: "number"; isReadonly: true }
- Property { name: "Key_F3"; type: "number"; isReadonly: true }
- Property { name: "Key_F4"; type: "number"; isReadonly: true }
- Property { name: "Key_F5"; type: "number"; isReadonly: true }
- Property { name: "Key_F6"; type: "number"; isReadonly: true }
- Property { name: "Key_F7"; type: "number"; isReadonly: true }
- Property { name: "Key_F8"; type: "number"; isReadonly: true }
- Property { name: "Key_F9"; type: "number"; isReadonly: true }
- Property { name: "Key_F10"; type: "number"; isReadonly: true }
- Property { name: "Key_F11"; type: "number"; isReadonly: true }
- Property { name: "Key_F12"; type: "number"; isReadonly: true }
- Property { name: "Key_F13"; type: "number"; isReadonly: true }
- Property { name: "Key_F14"; type: "number"; isReadonly: true }
- Property { name: "Key_F15"; type: "number"; isReadonly: true }
- Property { name: "Key_F16"; type: "number"; isReadonly: true }
- Property { name: "Key_F17"; type: "number"; isReadonly: true }
- Property { name: "Key_F18"; type: "number"; isReadonly: true }
- Property { name: "Key_F19"; type: "number"; isReadonly: true }
- Property { name: "Key_F20"; type: "number"; isReadonly: true }
- Property { name: "Key_F21"; type: "number"; isReadonly: true }
- Property { name: "Key_F22"; type: "number"; isReadonly: true }
- Property { name: "Key_F23"; type: "number"; isReadonly: true }
- Property { name: "Key_F24"; type: "number"; isReadonly: true }
- Property { name: "Key_F25"; type: "number"; isReadonly: true }
- Property { name: "Key_F26"; type: "number"; isReadonly: true }
- Property { name: "Key_F27"; type: "number"; isReadonly: true }
- Property { name: "Key_F28"; type: "number"; isReadonly: true }
- Property { name: "Key_F29"; type: "number"; isReadonly: true }
- Property { name: "Key_F30"; type: "number"; isReadonly: true }
- Property { name: "Key_F31"; type: "number"; isReadonly: true }
- Property { name: "Key_F32"; type: "number"; isReadonly: true }
- Property { name: "Key_F33"; type: "number"; isReadonly: true }
- Property { name: "Key_F34"; type: "number"; isReadonly: true }
- Property { name: "Key_F35"; type: "number"; isReadonly: true }
- Property { name: "Key_Super_L"; type: "number"; isReadonly: true }
- Property { name: "Key_Super_R"; type: "number"; isReadonly: true }
- Property { name: "Key_Menu"; type: "number"; isReadonly: true }
- Property { name: "Key_Hyper_L"; type: "number"; isReadonly: true }
- Property { name: "Key_Hyper_R"; type: "number"; isReadonly: true }
- Property { name: "Key_Help"; type: "number"; isReadonly: true }
- Property { name: "Key_Direction_L"; type: "number"; isReadonly: true }
- Property { name: "Key_Direction_R"; type: "number"; isReadonly: true }
- Property { name: "Key_AltGr"; type: "number"; isReadonly: true }
- Property { name: "Key_Multi_key"; type: "number"; isReadonly: true }
- Property { name: "Key_Codeinput"; type: "number"; isReadonly: true }
- Property { name: "Key_SingleCandidate"; type: "number"; isReadonly: true }
- Property { name: "Key_MultipleCandidate"; type: "number"; isReadonly: true }
- Property { name: "Key_PreviousCandidate"; type: "number"; isReadonly: true }
- Property { name: "Key_Mode_switch"; type: "number"; isReadonly: true }
- Property { name: "Key_Kanji"; type: "number"; isReadonly: true }
- Property { name: "Key_Muhenkan"; type: "number"; isReadonly: true }
- Property { name: "Key_Henkan"; type: "number"; isReadonly: true }
- Property { name: "Key_Romaji"; type: "number"; isReadonly: true }
- Property { name: "Key_Hiragana"; type: "number"; isReadonly: true }
- Property { name: "Key_Katakana"; type: "number"; isReadonly: true }
- Property { name: "Key_Hiragana_Katakana"; type: "number"; isReadonly: true }
- Property { name: "Key_Zenkaku"; type: "number"; isReadonly: true }
- Property { name: "Key_Hankaku"; type: "number"; isReadonly: true }
- Property { name: "Key_Zenkaku_Hankaku"; type: "number"; isReadonly: true }
- Property { name: "Key_Touroku"; type: "number"; isReadonly: true }
- Property { name: "Key_Massyo"; type: "number"; isReadonly: true }
- Property { name: "Key_Kana_Lock"; type: "number"; isReadonly: true }
- Property { name: "Key_Kana_Shift"; type: "number"; isReadonly: true }
- Property { name: "Key_Eisu_Shift"; type: "number"; isReadonly: true }
- Property { name: "Key_Eisu_toggle"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_Start"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_End"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_Hanja"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_Jamo"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_Romaja"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_Jeonja"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_Banja"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_PreHanja"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_PostHanja"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangul_Special"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Grave"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Acute"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Circumflex"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Tilde"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Macron"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Breve"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Abovedot"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Diaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Abovering"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Doubleacute"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Caron"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Cedilla"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Ogonek"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Iota"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Voiced_Sound"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Semivoiced_Sound"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowdot"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Hook"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Horn"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Stroke"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Abovecomma"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Abovereversedcomma"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Doublegrave"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowring"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowmacron"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowcircumflex"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowtilde"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowbreve"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowdiaeresis"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Invertedbreve"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowcomma"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Currency"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_a"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_A"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_e"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_E"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_i"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_I"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_o"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_O"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_u"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_U"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Small_Schwa"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Capital_Schwa"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Greek"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Lowline"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Aboveverticalline"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Belowverticalline"; type: "number"; isReadonly: true }
- Property { name: "Key_Dead_Longsolidusoverlay"; type: "number"; isReadonly: true }
- Property { name: "Key_Back"; type: "number"; isReadonly: true }
- Property { name: "Key_Forward"; type: "number"; isReadonly: true }
- Property { name: "Key_Stop"; type: "number"; isReadonly: true }
- Property { name: "Key_Refresh"; type: "number"; isReadonly: true }
- Property { name: "Key_VolumeDown"; type: "number"; isReadonly: true }
- Property { name: "Key_VolumeMute"; type: "number"; isReadonly: true }
- Property { name: "Key_VolumeUp"; type: "number"; isReadonly: true }
- Property { name: "Key_BassBoost"; type: "number"; isReadonly: true }
- Property { name: "Key_BassUp"; type: "number"; isReadonly: true }
- Property { name: "Key_BassDown"; type: "number"; isReadonly: true }
- Property { name: "Key_TrebleUp"; type: "number"; isReadonly: true }
- Property { name: "Key_TrebleDown"; type: "number"; isReadonly: true }
- Property { name: "Key_MediaPlay"; type: "number"; isReadonly: true }
- Property { name: "Key_MediaStop"; type: "number"; isReadonly: true }
- Property { name: "Key_MediaPrevious"; type: "number"; isReadonly: true }
- Property { name: "Key_MediaNext"; type: "number"; isReadonly: true }
- Property { name: "Key_MediaRecord"; type: "number"; isReadonly: true }
- Property { name: "Key_MediaPause"; type: "number"; isReadonly: true }
- Property { name: "Key_MediaTogglePlayPause"; type: "number"; isReadonly: true }
- Property { name: "Key_HomePage"; type: "number"; isReadonly: true }
- Property { name: "Key_Favorites"; type: "number"; isReadonly: true }
- Property { name: "Key_Search"; type: "number"; isReadonly: true }
- Property { name: "Key_Standby"; type: "number"; isReadonly: true }
- Property { name: "Key_OpenUrl"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchMail"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchMedia"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch0"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch1"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch2"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch3"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch4"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch5"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch6"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch7"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch8"; type: "number"; isReadonly: true }
- Property { name: "Key_Launch9"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchA"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchB"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchC"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchD"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchE"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchF"; type: "number"; isReadonly: true }
- Property { name: "Key_MonBrightnessUp"; type: "number"; isReadonly: true }
- Property { name: "Key_MonBrightnessDown"; type: "number"; isReadonly: true }
- Property { name: "Key_KeyboardLightOnOff"; type: "number"; isReadonly: true }
- Property { name: "Key_KeyboardBrightnessUp"; type: "number"; isReadonly: true }
- Property { name: "Key_KeyboardBrightnessDown"; type: "number"; isReadonly: true }
- Property { name: "Key_PowerOff"; type: "number"; isReadonly: true }
- Property { name: "Key_WakeUp"; type: "number"; isReadonly: true }
- Property { name: "Key_Eject"; type: "number"; isReadonly: true }
- Property { name: "Key_ScreenSaver"; type: "number"; isReadonly: true }
- Property { name: "Key_WWW"; type: "number"; isReadonly: true }
- Property { name: "Key_Memo"; type: "number"; isReadonly: true }
- Property { name: "Key_LightBulb"; type: "number"; isReadonly: true }
- Property { name: "Key_Shop"; type: "number"; isReadonly: true }
- Property { name: "Key_History"; type: "number"; isReadonly: true }
- Property { name: "Key_AddFavorite"; type: "number"; isReadonly: true }
- Property { name: "Key_HotLinks"; type: "number"; isReadonly: true }
- Property { name: "Key_BrightnessAdjust"; type: "number"; isReadonly: true }
- Property { name: "Key_Finance"; type: "number"; isReadonly: true }
- Property { name: "Key_Community"; type: "number"; isReadonly: true }
- Property { name: "Key_AudioRewind"; type: "number"; isReadonly: true }
- Property { name: "Key_BackForward"; type: "number"; isReadonly: true }
- Property { name: "Key_ApplicationLeft"; type: "number"; isReadonly: true }
- Property { name: "Key_ApplicationRight"; type: "number"; isReadonly: true }
- Property { name: "Key_Book"; type: "number"; isReadonly: true }
- Property { name: "Key_CD"; type: "number"; isReadonly: true }
- Property { name: "Key_Calculator"; type: "number"; isReadonly: true }
- Property { name: "Key_ToDoList"; type: "number"; isReadonly: true }
- Property { name: "Key_ClearGrab"; type: "number"; isReadonly: true }
- Property { name: "Key_Close"; type: "number"; isReadonly: true }
- Property { name: "Key_Copy"; type: "number"; isReadonly: true }
- Property { name: "Key_Cut"; type: "number"; isReadonly: true }
- Property { name: "Key_Display"; type: "number"; isReadonly: true }
- Property { name: "Key_DOS"; type: "number"; isReadonly: true }
- Property { name: "Key_Documents"; type: "number"; isReadonly: true }
- Property { name: "Key_Excel"; type: "number"; isReadonly: true }
- Property { name: "Key_Explorer"; type: "number"; isReadonly: true }
- Property { name: "Key_Game"; type: "number"; isReadonly: true }
- Property { name: "Key_Go"; type: "number"; isReadonly: true }
- Property { name: "Key_iTouch"; type: "number"; isReadonly: true }
- Property { name: "Key_LogOff"; type: "number"; isReadonly: true }
- Property { name: "Key_Market"; type: "number"; isReadonly: true }
- Property { name: "Key_Meeting"; type: "number"; isReadonly: true }
- Property { name: "Key_MenuKB"; type: "number"; isReadonly: true }
- Property { name: "Key_MenuPB"; type: "number"; isReadonly: true }
- Property { name: "Key_MySites"; type: "number"; isReadonly: true }
- Property { name: "Key_News"; type: "number"; isReadonly: true }
- Property { name: "Key_OfficeHome"; type: "number"; isReadonly: true }
- Property { name: "Key_Option"; type: "number"; isReadonly: true }
- Property { name: "Key_Paste"; type: "number"; isReadonly: true }
- Property { name: "Key_Phone"; type: "number"; isReadonly: true }
- Property { name: "Key_Calendar"; type: "number"; isReadonly: true }
- Property { name: "Key_Reply"; type: "number"; isReadonly: true }
- Property { name: "Key_Reload"; type: "number"; isReadonly: true }
- Property { name: "Key_RotateWindows"; type: "number"; isReadonly: true }
- Property { name: "Key_RotationPB"; type: "number"; isReadonly: true }
- Property { name: "Key_RotationKB"; type: "number"; isReadonly: true }
- Property { name: "Key_Save"; type: "number"; isReadonly: true }
- Property { name: "Key_Send"; type: "number"; isReadonly: true }
- Property { name: "Key_Spell"; type: "number"; isReadonly: true }
- Property { name: "Key_SplitScreen"; type: "number"; isReadonly: true }
- Property { name: "Key_Support"; type: "number"; isReadonly: true }
- Property { name: "Key_TaskPane"; type: "number"; isReadonly: true }
- Property { name: "Key_Terminal"; type: "number"; isReadonly: true }
- Property { name: "Key_Tools"; type: "number"; isReadonly: true }
- Property { name: "Key_Travel"; type: "number"; isReadonly: true }
- Property { name: "Key_Video"; type: "number"; isReadonly: true }
- Property { name: "Key_Word"; type: "number"; isReadonly: true }
- Property { name: "Key_Xfer"; type: "number"; isReadonly: true }
- Property { name: "Key_ZoomIn"; type: "number"; isReadonly: true }
- Property { name: "Key_ZoomOut"; type: "number"; isReadonly: true }
- Property { name: "Key_Away"; type: "number"; isReadonly: true }
- Property { name: "Key_Messenger"; type: "number"; isReadonly: true }
- Property { name: "Key_WebCam"; type: "number"; isReadonly: true }
- Property { name: "Key_MailForward"; type: "number"; isReadonly: true }
- Property { name: "Key_Pictures"; type: "number"; isReadonly: true }
- Property { name: "Key_Music"; type: "number"; isReadonly: true }
- Property { name: "Key_Battery"; type: "number"; isReadonly: true }
- Property { name: "Key_Bluetooth"; type: "number"; isReadonly: true }
- Property { name: "Key_WLAN"; type: "number"; isReadonly: true }
- Property { name: "Key_UWB"; type: "number"; isReadonly: true }
- Property { name: "Key_AudioForward"; type: "number"; isReadonly: true }
- Property { name: "Key_AudioRepeat"; type: "number"; isReadonly: true }
- Property { name: "Key_AudioRandomPlay"; type: "number"; isReadonly: true }
- Property { name: "Key_Subtitle"; type: "number"; isReadonly: true }
- Property { name: "Key_AudioCycleTrack"; type: "number"; isReadonly: true }
- Property { name: "Key_Time"; type: "number"; isReadonly: true }
- Property { name: "Key_Hibernate"; type: "number"; isReadonly: true }
- Property { name: "Key_View"; type: "number"; isReadonly: true }
- Property { name: "Key_TopMenu"; type: "number"; isReadonly: true }
- Property { name: "Key_PowerDown"; type: "number"; isReadonly: true }
- Property { name: "Key_Suspend"; type: "number"; isReadonly: true }
- Property { name: "Key_ContrastAdjust"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchG"; type: "number"; isReadonly: true }
- Property { name: "Key_LaunchH"; type: "number"; isReadonly: true }
- Property { name: "Key_TouchpadToggle"; type: "number"; isReadonly: true }
- Property { name: "Key_TouchpadOn"; type: "number"; isReadonly: true }
- Property { name: "Key_TouchpadOff"; type: "number"; isReadonly: true }
- Property { name: "Key_MicMute"; type: "number"; isReadonly: true }
- Property { name: "Key_Red"; type: "number"; isReadonly: true }
- Property { name: "Key_Green"; type: "number"; isReadonly: true }
- Property { name: "Key_Yellow"; type: "number"; isReadonly: true }
- Property { name: "Key_Blue"; type: "number"; isReadonly: true }
- Property { name: "Key_ChannelUp"; type: "number"; isReadonly: true }
- Property { name: "Key_ChannelDown"; type: "number"; isReadonly: true }
- Property { name: "Key_Guide"; type: "number"; isReadonly: true }
- Property { name: "Key_Info"; type: "number"; isReadonly: true }
- Property { name: "Key_Settings"; type: "number"; isReadonly: true }
- Property { name: "Key_MicVolumeUp"; type: "number"; isReadonly: true }
- Property { name: "Key_MicVolumeDown"; type: "number"; isReadonly: true }
- Property { name: "Key_New"; type: "number"; isReadonly: true }
- Property { name: "Key_Open"; type: "number"; isReadonly: true }
- Property { name: "Key_Find"; type: "number"; isReadonly: true }
- Property { name: "Key_Undo"; type: "number"; isReadonly: true }
- Property { name: "Key_Redo"; type: "number"; isReadonly: true }
- Property { name: "Key_MediaLast"; type: "number"; isReadonly: true }
- Property { name: "Key_Select"; type: "number"; isReadonly: true }
- Property { name: "Key_Yes"; type: "number"; isReadonly: true }
- Property { name: "Key_No"; type: "number"; isReadonly: true }
- Property { name: "Key_Cancel"; type: "number"; isReadonly: true }
- Property { name: "Key_Printer"; type: "number"; isReadonly: true }
- Property { name: "Key_Execute"; type: "number"; isReadonly: true }
- Property { name: "Key_Sleep"; type: "number"; isReadonly: true }
- Property { name: "Key_Play"; type: "number"; isReadonly: true }
- Property { name: "Key_Zoom"; type: "number"; isReadonly: true }
- Property { name: "Key_Exit"; type: "number"; isReadonly: true }
- Property { name: "Key_Context1"; type: "number"; isReadonly: true }
- Property { name: "Key_Context2"; type: "number"; isReadonly: true }
- Property { name: "Key_Context3"; type: "number"; isReadonly: true }
- Property { name: "Key_Context4"; type: "number"; isReadonly: true }
- Property { name: "Key_Call"; type: "number"; isReadonly: true }
- Property { name: "Key_Hangup"; type: "number"; isReadonly: true }
- Property { name: "Key_Flip"; type: "number"; isReadonly: true }
- Property { name: "Key_ToggleCallHangup"; type: "number"; isReadonly: true }
- Property { name: "Key_VoiceDial"; type: "number"; isReadonly: true }
- Property { name: "Key_LastNumberRedial"; type: "number"; isReadonly: true }
- Property { name: "Key_Camera"; type: "number"; isReadonly: true }
- Property { name: "Key_CameraFocus"; type: "number"; isReadonly: true }
- Property { name: "Key_unknown"; type: "number"; isReadonly: true }
- Property { name: "NoModifier"; type: "number"; isReadonly: true }
- Property { name: "ShiftModifier"; type: "number"; isReadonly: true }
- Property { name: "ControlModifier"; type: "number"; isReadonly: true }
- Property { name: "AltModifier"; type: "number"; isReadonly: true }
- Property { name: "MetaModifier"; type: "number"; isReadonly: true }
- Property { name: "KeypadModifier"; type: "number"; isReadonly: true }
- Property { name: "GroupSwitchModifier"; type: "number"; isReadonly: true }
- Property { name: "KeyboardModifierMask"; type: "number"; isReadonly: true }
- Property { name: "META"; type: "number"; isReadonly: true }
- Property { name: "SHIFT"; type: "number"; isReadonly: true }
- Property { name: "CTRL"; type: "number"; isReadonly: true }
- Property { name: "ALT"; type: "number"; isReadonly: true }
- Property { name: "MODIFIER_MASK"; type: "number"; isReadonly: true }
- Property { name: "NoArrow"; type: "number"; isReadonly: true }
- Property { name: "UpArrow"; type: "number"; isReadonly: true }
- Property { name: "DownArrow"; type: "number"; isReadonly: true }
- Property { name: "LeftArrow"; type: "number"; isReadonly: true }
- Property { name: "RightArrow"; type: "number"; isReadonly: true }
- Property { name: "NoPen"; type: "number"; isReadonly: true }
- Property { name: "SolidLine"; type: "number"; isReadonly: true }
- Property { name: "DashLine"; type: "number"; isReadonly: true }
- Property { name: "DotLine"; type: "number"; isReadonly: true }
- Property { name: "DashDotLine"; type: "number"; isReadonly: true }
- Property { name: "DashDotDotLine"; type: "number"; isReadonly: true }
- Property { name: "CustomDashLine"; type: "number"; isReadonly: true }
- Property { name: "FlatCap"; type: "number"; isReadonly: true }
- Property { name: "SquareCap"; type: "number"; isReadonly: true }
- Property { name: "RoundCap"; type: "number"; isReadonly: true }
- Property { name: "MPenCapStyle"; type: "number"; isReadonly: true }
- Property { name: "MiterJoin"; type: "number"; isReadonly: true }
- Property { name: "BevelJoin"; type: "number"; isReadonly: true }
- Property { name: "RoundJoin"; type: "number"; isReadonly: true }
- Property { name: "SvgMiterJoin"; type: "number"; isReadonly: true }
- Property { name: "MPenJoinStyle"; type: "number"; isReadonly: true }
- Property { name: "NoBrush"; type: "number"; isReadonly: true }
- Property { name: "SolidPattern"; type: "number"; isReadonly: true }
- Property { name: "Dense1Pattern"; type: "number"; isReadonly: true }
- Property { name: "Dense2Pattern"; type: "number"; isReadonly: true }
- Property { name: "Dense3Pattern"; type: "number"; isReadonly: true }
- Property { name: "Dense4Pattern"; type: "number"; isReadonly: true }
- Property { name: "Dense5Pattern"; type: "number"; isReadonly: true }
- Property { name: "Dense6Pattern"; type: "number"; isReadonly: true }
- Property { name: "Dense7Pattern"; type: "number"; isReadonly: true }
- Property { name: "HorPattern"; type: "number"; isReadonly: true }
- Property { name: "VerPattern"; type: "number"; isReadonly: true }
- Property { name: "CrossPattern"; type: "number"; isReadonly: true }
- Property { name: "BDiagPattern"; type: "number"; isReadonly: true }
- Property { name: "FDiagPattern"; type: "number"; isReadonly: true }
- Property { name: "DiagCrossPattern"; type: "number"; isReadonly: true }
- Property { name: "LinearGradientPattern"; type: "number"; isReadonly: true }
- Property { name: "RadialGradientPattern"; type: "number"; isReadonly: true }
- Property { name: "ConicalGradientPattern"; type: "number"; isReadonly: true }
- Property { name: "TexturePattern"; type: "number"; isReadonly: true }
- Property { name: "AbsoluteSize"; type: "number"; isReadonly: true }
- Property { name: "RelativeSize"; type: "number"; isReadonly: true }
- Property { name: "ArrowCursor"; type: "number"; isReadonly: true }
- Property { name: "UpArrowCursor"; type: "number"; isReadonly: true }
- Property { name: "CrossCursor"; type: "number"; isReadonly: true }
- Property { name: "WaitCursor"; type: "number"; isReadonly: true }
- Property { name: "IBeamCursor"; type: "number"; isReadonly: true }
- Property { name: "SizeVerCursor"; type: "number"; isReadonly: true }
- Property { name: "SizeHorCursor"; type: "number"; isReadonly: true }
- Property { name: "SizeBDiagCursor"; type: "number"; isReadonly: true }
- Property { name: "SizeFDiagCursor"; type: "number"; isReadonly: true }
- Property { name: "SizeAllCursor"; type: "number"; isReadonly: true }
- Property { name: "BlankCursor"; type: "number"; isReadonly: true }
- Property { name: "SplitVCursor"; type: "number"; isReadonly: true }
- Property { name: "SplitHCursor"; type: "number"; isReadonly: true }
- Property { name: "PointingHandCursor"; type: "number"; isReadonly: true }
- Property { name: "ForbiddenCursor"; type: "number"; isReadonly: true }
- Property { name: "WhatsThisCursor"; type: "number"; isReadonly: true }
- Property { name: "BusyCursor"; type: "number"; isReadonly: true }
- Property { name: "OpenHandCursor"; type: "number"; isReadonly: true }
- Property { name: "ClosedHandCursor"; type: "number"; isReadonly: true }
- Property { name: "DragCopyCursor"; type: "number"; isReadonly: true }
- Property { name: "DragMoveCursor"; type: "number"; isReadonly: true }
- Property { name: "DragLinkCursor"; type: "number"; isReadonly: true }
- Property { name: "LastCursor"; type: "number"; isReadonly: true }
- Property { name: "BitmapCursor"; type: "number"; isReadonly: true }
- Property { name: "CustomCursor"; type: "number"; isReadonly: true }
- Property { name: "PlainText"; type: "number"; isReadonly: true }
- Property { name: "RichText"; type: "number"; isReadonly: true }
- Property { name: "AutoText"; type: "number"; isReadonly: true }
- Property { name: "MarkdownText"; type: "number"; isReadonly: true }
- Property { name: "IgnoreAspectRatio"; type: "number"; isReadonly: true }
- Property { name: "KeepAspectRatio"; type: "number"; isReadonly: true }
- Property { name: "KeepAspectRatioByExpanding"; type: "number"; isReadonly: true }
- Property { name: "LeftDockWidgetArea"; type: "number"; isReadonly: true }
- Property { name: "RightDockWidgetArea"; type: "number"; isReadonly: true }
- Property { name: "TopDockWidgetArea"; type: "number"; isReadonly: true }
- Property { name: "BottomDockWidgetArea"; type: "number"; isReadonly: true }
- Property { name: "DockWidgetArea_Mask"; type: "number"; isReadonly: true }
- Property { name: "AllDockWidgetAreas"; type: "number"; isReadonly: true }
- Property { name: "NoDockWidgetArea"; type: "number"; isReadonly: true }
- Property { name: "LeftToolBarArea"; type: "number"; isReadonly: true }
- Property { name: "RightToolBarArea"; type: "number"; isReadonly: true }
- Property { name: "TopToolBarArea"; type: "number"; isReadonly: true }
- Property { name: "BottomToolBarArea"; type: "number"; isReadonly: true }
- Property { name: "ToolBarArea_Mask"; type: "number"; isReadonly: true }
- Property { name: "AllToolBarAreas"; type: "number"; isReadonly: true }
- Property { name: "NoToolBarArea"; type: "number"; isReadonly: true }
- Property { name: "TextDate"; type: "number"; isReadonly: true }
- Property { name: "ISODate"; type: "number"; isReadonly: true }
- Property { name: "RFC2822Date"; type: "number"; isReadonly: true }
- Property { name: "ISODateWithMs"; type: "number"; isReadonly: true }
- Property { name: "LocalTime"; type: "number"; isReadonly: true }
- Property { name: "UTC"; type: "number"; isReadonly: true }
- Property { name: "OffsetFromUTC"; type: "number"; isReadonly: true }
- Property { name: "TimeZone"; type: "number"; isReadonly: true }
- Property { name: "Monday"; type: "number"; isReadonly: true }
- Property { name: "Tuesday"; type: "number"; isReadonly: true }
- Property { name: "Wednesday"; type: "number"; isReadonly: true }
- Property { name: "Thursday"; type: "number"; isReadonly: true }
- Property { name: "Friday"; type: "number"; isReadonly: true }
- Property { name: "Saturday"; type: "number"; isReadonly: true }
- Property { name: "Sunday"; type: "number"; isReadonly: true }
- Property { name: "ScrollBarAsNeeded"; type: "number"; isReadonly: true }
- Property { name: "ScrollBarAlwaysOff"; type: "number"; isReadonly: true }
- Property { name: "ScrollBarAlwaysOn"; type: "number"; isReadonly: true }
- Property { name: "CaseInsensitive"; type: "number"; isReadonly: true }
- Property { name: "CaseSensitive"; type: "number"; isReadonly: true }
- Property { name: "TopLeftCorner"; type: "number"; isReadonly: true }
- Property { name: "TopRightCorner"; type: "number"; isReadonly: true }
- Property { name: "BottomLeftCorner"; type: "number"; isReadonly: true }
- Property { name: "BottomRightCorner"; type: "number"; isReadonly: true }
- Property { name: "TopEdge"; type: "number"; isReadonly: true }
- Property { name: "LeftEdge"; type: "number"; isReadonly: true }
- Property { name: "RightEdge"; type: "number"; isReadonly: true }
- Property { name: "BottomEdge"; type: "number"; isReadonly: true }
- Property { name: "AutoConnection"; type: "number"; isReadonly: true }
- Property { name: "DirectConnection"; type: "number"; isReadonly: true }
- Property { name: "QueuedConnection"; type: "number"; isReadonly: true }
- Property { name: "BlockingQueuedConnection"; type: "number"; isReadonly: true }
- Property { name: "UniqueConnection"; type: "number"; isReadonly: true }
- Property { name: "SingleShotConnection"; type: "number"; isReadonly: true }
- Property { name: "WidgetShortcut"; type: "number"; isReadonly: true }
- Property { name: "WindowShortcut"; type: "number"; isReadonly: true }
- Property { name: "ApplicationShortcut"; type: "number"; isReadonly: true }
- Property { name: "WidgetWithChildrenShortcut"; type: "number"; isReadonly: true }
- Property { name: "OddEvenFill"; type: "number"; isReadonly: true }
- Property { name: "WindingFill"; type: "number"; isReadonly: true }
- Property { name: "MaskInColor"; type: "number"; isReadonly: true }
- Property { name: "MaskOutColor"; type: "number"; isReadonly: true }
- Property { name: "NoClip"; type: "number"; isReadonly: true }
- Property { name: "ReplaceClip"; type: "number"; isReadonly: true }
- Property { name: "IntersectClip"; type: "number"; isReadonly: true }
- Property { name: "ContainsItemShape"; type: "number"; isReadonly: true }
- Property { name: "IntersectsItemShape"; type: "number"; isReadonly: true }
- Property { name: "ContainsItemBoundingRect"; type: "number"; isReadonly: true }
- Property { name: "IntersectsItemBoundingRect"; type: "number"; isReadonly: true }
- Property { name: "ReplaceSelection"; type: "number"; isReadonly: true }
- Property { name: "AddToSelection"; type: "number"; isReadonly: true }
- Property { name: "FastTransformation"; type: "number"; isReadonly: true }
- Property { name: "SmoothTransformation"; type: "number"; isReadonly: true }
- Property { name: "XAxis"; type: "number"; isReadonly: true }
- Property { name: "YAxis"; type: "number"; isReadonly: true }
- Property { name: "ZAxis"; type: "number"; isReadonly: true }
- Property { name: "MouseFocusReason"; type: "number"; isReadonly: true }
- Property { name: "TabFocusReason"; type: "number"; isReadonly: true }
- Property { name: "BacktabFocusReason"; type: "number"; isReadonly: true }
- Property { name: "ActiveWindowFocusReason"; type: "number"; isReadonly: true }
- Property { name: "PopupFocusReason"; type: "number"; isReadonly: true }
- Property { name: "ShortcutFocusReason"; type: "number"; isReadonly: true }
- Property { name: "MenuBarFocusReason"; type: "number"; isReadonly: true }
- Property { name: "OtherFocusReason"; type: "number"; isReadonly: true }
- Property { name: "NoFocusReason"; type: "number"; isReadonly: true }
- Property { name: "NoContextMenu"; type: "number"; isReadonly: true }
- Property { name: "DefaultContextMenu"; type: "number"; isReadonly: true }
- Property { name: "ActionsContextMenu"; type: "number"; isReadonly: true }
- Property { name: "CustomContextMenu"; type: "number"; isReadonly: true }
- Property { name: "PreventContextMenu"; type: "number"; isReadonly: true }
- Property { name: "ImEnabled"; type: "number"; isReadonly: true }
- Property { name: "ImCursorRectangle"; type: "number"; isReadonly: true }
- Property { name: "ImFont"; type: "number"; isReadonly: true }
- Property { name: "ImCursorPosition"; type: "number"; isReadonly: true }
- Property { name: "ImSurroundingText"; type: "number"; isReadonly: true }
- Property { name: "ImCurrentSelection"; type: "number"; isReadonly: true }
- Property { name: "ImMaximumTextLength"; type: "number"; isReadonly: true }
- Property { name: "ImAnchorPosition"; type: "number"; isReadonly: true }
- Property { name: "ImHints"; type: "number"; isReadonly: true }
- Property { name: "ImPreferredLanguage"; type: "number"; isReadonly: true }
- Property { name: "ImAbsolutePosition"; type: "number"; isReadonly: true }
- Property { name: "ImTextBeforeCursor"; type: "number"; isReadonly: true }
- Property { name: "ImTextAfterCursor"; type: "number"; isReadonly: true }
- Property { name: "ImEnterKeyType"; type: "number"; isReadonly: true }
- Property { name: "ImAnchorRectangle"; type: "number"; isReadonly: true }
- Property { name: "ImInputItemClipRectangle"; type: "number"; isReadonly: true }
- Property { name: "ImReadOnly"; type: "number"; isReadonly: true }
- Property { name: "ImPlatformData"; type: "number"; isReadonly: true }
- Property { name: "ImQueryInput"; type: "number"; isReadonly: true }
- Property { name: "ImQueryAll"; type: "number"; isReadonly: true }
- Property { name: "ImhNone"; type: "number"; isReadonly: true }
- Property { name: "ImhHiddenText"; type: "number"; isReadonly: true }
- Property { name: "ImhSensitiveData"; type: "number"; isReadonly: true }
- Property { name: "ImhNoAutoUppercase"; type: "number"; isReadonly: true }
- Property { name: "ImhPreferNumbers"; type: "number"; isReadonly: true }
- Property { name: "ImhPreferUppercase"; type: "number"; isReadonly: true }
- Property { name: "ImhPreferLowercase"; type: "number"; isReadonly: true }
- Property { name: "ImhNoPredictiveText"; type: "number"; isReadonly: true }
- Property { name: "ImhDate"; type: "number"; isReadonly: true }
- Property { name: "ImhTime"; type: "number"; isReadonly: true }
- Property { name: "ImhPreferLatin"; type: "number"; isReadonly: true }
- Property { name: "ImhMultiLine"; type: "number"; isReadonly: true }
- Property { name: "ImhNoEditMenu"; type: "number"; isReadonly: true }
- Property { name: "ImhNoTextHandles"; type: "number"; isReadonly: true }
- Property { name: "ImhDigitsOnly"; type: "number"; isReadonly: true }
- Property { name: "ImhFormattedNumbersOnly"; type: "number"; isReadonly: true }
- Property { name: "ImhUppercaseOnly"; type: "number"; isReadonly: true }
- Property { name: "ImhLowercaseOnly"; type: "number"; isReadonly: true }
- Property { name: "ImhDialableCharactersOnly"; type: "number"; isReadonly: true }
- Property { name: "ImhEmailCharactersOnly"; type: "number"; isReadonly: true }
- Property { name: "ImhUrlCharactersOnly"; type: "number"; isReadonly: true }
- Property { name: "ImhLatinOnly"; type: "number"; isReadonly: true }
- Property { name: "ImhExclusiveInputMask"; type: "number"; isReadonly: true }
- Property { name: "EnterKeyDefault"; type: "number"; isReadonly: true }
- Property { name: "EnterKeyReturn"; type: "number"; isReadonly: true }
- Property { name: "EnterKeyDone"; type: "number"; isReadonly: true }
- Property { name: "EnterKeyGo"; type: "number"; isReadonly: true }
- Property { name: "EnterKeySend"; type: "number"; isReadonly: true }
- Property { name: "EnterKeySearch"; type: "number"; isReadonly: true }
- Property { name: "EnterKeyNext"; type: "number"; isReadonly: true }
- Property { name: "EnterKeyPrevious"; type: "number"; isReadonly: true }
- Property { name: "ToolButtonIconOnly"; type: "number"; isReadonly: true }
- Property { name: "ToolButtonTextOnly"; type: "number"; isReadonly: true }
- Property { name: "ToolButtonTextBesideIcon"; type: "number"; isReadonly: true }
- Property { name: "ToolButtonTextUnderIcon"; type: "number"; isReadonly: true }
- Property { name: "ToolButtonFollowStyle"; type: "number"; isReadonly: true }
- Property { name: "LeftToRight"; type: "number"; isReadonly: true }
- Property { name: "RightToLeft"; type: "number"; isReadonly: true }
- Property { name: "LayoutDirectionAuto"; type: "number"; isReadonly: true }
- Property { name: "CopyAction"; type: "number"; isReadonly: true }
- Property { name: "MoveAction"; type: "number"; isReadonly: true }
- Property { name: "LinkAction"; type: "number"; isReadonly: true }
- Property { name: "ActionMask"; type: "number"; isReadonly: true }
- Property { name: "TargetMoveAction"; type: "number"; isReadonly: true }
- Property { name: "IgnoreAction"; type: "number"; isReadonly: true }
- Property { name: "Unchecked"; type: "number"; isReadonly: true }
- Property { name: "PartiallyChecked"; type: "number"; isReadonly: true }
- Property { name: "Checked"; type: "number"; isReadonly: true }
- Property { name: "DisplayRole"; type: "number"; isReadonly: true }
- Property { name: "DecorationRole"; type: "number"; isReadonly: true }
- Property { name: "EditRole"; type: "number"; isReadonly: true }
- Property { name: "ToolTipRole"; type: "number"; isReadonly: true }
- Property { name: "StatusTipRole"; type: "number"; isReadonly: true }
- Property { name: "WhatsThisRole"; type: "number"; isReadonly: true }
- Property { name: "FontRole"; type: "number"; isReadonly: true }
- Property { name: "TextAlignmentRole"; type: "number"; isReadonly: true }
- Property { name: "BackgroundRole"; type: "number"; isReadonly: true }
- Property { name: "ForegroundRole"; type: "number"; isReadonly: true }
- Property { name: "CheckStateRole"; type: "number"; isReadonly: true }
- Property { name: "AccessibleTextRole"; type: "number"; isReadonly: true }
- Property { name: "AccessibleDescriptionRole"; type: "number"; isReadonly: true }
- Property { name: "SizeHintRole"; type: "number"; isReadonly: true }
- Property { name: "InitialSortOrderRole"; type: "number"; isReadonly: true }
- Property { name: "DisplayPropertyRole"; type: "number"; isReadonly: true }
- Property { name: "DecorationPropertyRole"; type: "number"; isReadonly: true }
- Property { name: "ToolTipPropertyRole"; type: "number"; isReadonly: true }
- Property { name: "StatusTipPropertyRole"; type: "number"; isReadonly: true }
- Property { name: "WhatsThisPropertyRole"; type: "number"; isReadonly: true }
- Property { name: "UserRole"; type: "number"; isReadonly: true }
- Property { name: "NoItemFlags"; type: "number"; isReadonly: true }
- Property { name: "ItemIsSelectable"; type: "number"; isReadonly: true }
- Property { name: "ItemIsEditable"; type: "number"; isReadonly: true }
- Property { name: "ItemIsDragEnabled"; type: "number"; isReadonly: true }
- Property { name: "ItemIsDropEnabled"; type: "number"; isReadonly: true }
- Property { name: "ItemIsUserCheckable"; type: "number"; isReadonly: true }
- Property { name: "ItemIsEnabled"; type: "number"; isReadonly: true }
- Property { name: "ItemIsAutoTristate"; type: "number"; isReadonly: true }
- Property { name: "ItemNeverHasChildren"; type: "number"; isReadonly: true }
- Property { name: "ItemIsUserTristate"; type: "number"; isReadonly: true }
- Property { name: "MatchExactly"; type: "number"; isReadonly: true }
- Property { name: "MatchContains"; type: "number"; isReadonly: true }
- Property { name: "MatchStartsWith"; type: "number"; isReadonly: true }
- Property { name: "MatchEndsWith"; type: "number"; isReadonly: true }
- Property { name: "MatchRegularExpression"; type: "number"; isReadonly: true }
- Property { name: "MatchWildcard"; type: "number"; isReadonly: true }
- Property { name: "MatchFixedString"; type: "number"; isReadonly: true }
- Property { name: "MatchTypeMask"; type: "number"; isReadonly: true }
- Property { name: "MatchCaseSensitive"; type: "number"; isReadonly: true }
- Property { name: "MatchWrap"; type: "number"; isReadonly: true }
- Property { name: "MatchRecursive"; type: "number"; isReadonly: true }
- Property { name: "NonModal"; type: "number"; isReadonly: true }
- Property { name: "WindowModal"; type: "number"; isReadonly: true }
- Property { name: "ApplicationModal"; type: "number"; isReadonly: true }
- Property { name: "NoTextInteraction"; type: "number"; isReadonly: true }
- Property { name: "TextSelectableByMouse"; type: "number"; isReadonly: true }
- Property { name: "TextSelectableByKeyboard"; type: "number"; isReadonly: true }
- Property { name: "LinksAccessibleByMouse"; type: "number"; isReadonly: true }
- Property { name: "LinksAccessibleByKeyboard"; type: "number"; isReadonly: true }
- Property { name: "TextEditable"; type: "number"; isReadonly: true }
- Property { name: "TextEditorInteraction"; type: "number"; isReadonly: true }
- Property { name: "TextBrowserInteraction"; type: "number"; isReadonly: true }
- Property { name: "MinimumSize"; type: "number"; isReadonly: true }
- Property { name: "PreferredSize"; type: "number"; isReadonly: true }
- Property { name: "MaximumSize"; type: "number"; isReadonly: true }
- Property { name: "MinimumDescent"; type: "number"; isReadonly: true }
- Property { name: "NSizeHints"; type: "number"; isReadonly: true }
- Property { name: "TouchPointUnknownState"; type: "number"; isReadonly: true }
- Property { name: "TouchPointPressed"; type: "number"; isReadonly: true }
- Property { name: "TouchPointMoved"; type: "number"; isReadonly: true }
- Property { name: "TouchPointStationary"; type: "number"; isReadonly: true }
- Property { name: "TouchPointReleased"; type: "number"; isReadonly: true }
- Property { name: "NoGesture"; type: "number"; isReadonly: true }
- Property { name: "GestureStarted"; type: "number"; isReadonly: true }
- Property { name: "GestureUpdated"; type: "number"; isReadonly: true }
- Property { name: "GestureFinished"; type: "number"; isReadonly: true }
- Property { name: "GestureCanceled"; type: "number"; isReadonly: true }
- Property { name: "TapGesture"; type: "number"; isReadonly: true }
- Property { name: "TapAndHoldGesture"; type: "number"; isReadonly: true }
- Property { name: "PanGesture"; type: "number"; isReadonly: true }
- Property { name: "PinchGesture"; type: "number"; isReadonly: true }
- Property { name: "SwipeGesture"; type: "number"; isReadonly: true }
- Property { name: "CustomGesture"; type: "number"; isReadonly: true }
- Property { name: "LastGestureType"; type: "number"; isReadonly: true }
- Property { name: "BeginNativeGesture"; type: "number"; isReadonly: true }
- Property { name: "EndNativeGesture"; type: "number"; isReadonly: true }
- Property { name: "PanNativeGesture"; type: "number"; isReadonly: true }
- Property { name: "ZoomNativeGesture"; type: "number"; isReadonly: true }
- Property { name: "SmartZoomNativeGesture"; type: "number"; isReadonly: true }
- Property { name: "RotateNativeGesture"; type: "number"; isReadonly: true }
- Property { name: "SwipeNativeGesture"; type: "number"; isReadonly: true }
- Property { name: "LogicalMoveStyle"; type: "number"; isReadonly: true }
- Property { name: "VisualMoveStyle"; type: "number"; isReadonly: true }
- Property { name: "PreciseTimer"; type: "number"; isReadonly: true }
- Property { name: "CoarseTimer"; type: "number"; isReadonly: true }
- Property { name: "VeryCoarseTimer"; type: "number"; isReadonly: true }
- Property { name: "NoScrollPhase"; type: "number"; isReadonly: true }
- Property { name: "ScrollBegin"; type: "number"; isReadonly: true }
- Property { name: "ScrollUpdate"; type: "number"; isReadonly: true }
- Property { name: "ScrollEnd"; type: "number"; isReadonly: true }
- Property { name: "ScrollMomentum"; type: "number"; isReadonly: true }
- Property { name: "MouseEventNotSynthesized"; type: "number"; isReadonly: true }
- Property { name: "MouseEventSynthesizedBySystem"; type: "number"; isReadonly: true }
- Property { name: "MouseEventSynthesizedByQt"; type: "number"; isReadonly: true }
- Property { name: "MouseEventSynthesizedByApplication"; type: "number"; isReadonly: true }
- Property { name: "NoMouseEventFlag"; type: "number"; isReadonly: true }
- Property { name: "MouseEventCreatedDoubleClick"; type: "number"; isReadonly: true }
- Property { name: "MouseEventFlagMask"; type: "number"; isReadonly: true }
- Property { name: "ChecksumIso3309"; type: "number"; isReadonly: true }
- Property { name: "ChecksumItuV41"; type: "number"; isReadonly: true }
- Property { name: "Unset"; type: "number"; isReadonly: true }
- Property { name: "Round"; type: "number"; isReadonly: true }
- Property { name: "Ceil"; type: "number"; isReadonly: true }
- Property { name: "Floor"; type: "number"; isReadonly: true }
- Property { name: "RoundPreferFloor"; type: "number"; isReadonly: true }
- Property { name: "PassThrough"; type: "number"; isReadonly: true }
- Property { name: "Undetermined"; type: "number"; isReadonly: true }
- Property { name: "Granted"; type: "number"; isReadonly: true }
- Property { name: "Denied"; type: "number"; isReadonly: true }
- }
- Component {
- name: "QtStyleHints"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "objectName"; type: "string" }
- Method { name: "objectNameChanged"; isJavaScriptFunction: true }
- }
- Component {
- name: "RangeError"
- accessSemantics: "reference"
- prototype: "RangeErrorPrototype"
- Property { name: "stack"; type: "string" }
- Property { name: "fileName" }
- Property { name: "lineNumber" }
- }
- Component {
- name: "RangeErrorPrototype"
- accessSemantics: "reference"
- prototype: "ErrorPrototype"
- Property { name: "message"; type: "string" }
- Property { name: "name"; type: "string" }
- Method {
- name: "constructor"
- type: "RangeError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- }
- Component {
- name: "ReferenceError"
- accessSemantics: "reference"
- prototype: "ReferenceErrorPrototype"
- Property { name: "stack"; type: "string" }
- Property { name: "fileName" }
- Property { name: "lineNumber" }
- }
- Component {
- name: "ReferenceErrorPrototype"
- accessSemantics: "reference"
- prototype: "ErrorPrototype"
- Property { name: "message"; type: "string" }
- Property { name: "name"; type: "string" }
- Method {
- name: "constructor"
- type: "ReferenceError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- }
- Component {
- name: "Reflect"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method {
- name: "apply"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "construct"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "defineProperty"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "deleteProperty"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "get"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "getOwnPropertyDescriptor"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "getPrototypeOf"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "has"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "isExtensible"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "ownKeys"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "preventExtensions"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "set"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- Method {
- name: "setPrototypeOf"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- }
- Component {
- name: "RegExp"
- accessSemantics: "reference"
- prototype: "RegExpPrototype"
- Property { name: "lastIndex"; type: "number" }
- }
- Component {
- name: "RegExpPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "flags"; type: "string" }
- Property { name: "global" }
- Property { name: "ignoreCase" }
- Property { name: "multiline" }
- Property { name: "source"; type: "string" }
- Property { name: "sticky" }
- Property { name: "unicode" }
- Method {
- name: "constructor"
- type: "RegExp"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "exec"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "test"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method {
- name: "compile"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "Set"; accessSemantics: "reference"; prototype: "SetPrototype" }
- Component {
- name: "SetPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "size"; type: "number" }
- Method { name: "constructor"; type: "Set"; isConstructor: true; isJavaScriptFunction: true }
- Method {
- name: "add"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "clear"; isJavaScriptFunction: true }
- Method {
- name: "delete"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "entries"; isJavaScriptFunction: true }
- Method {
- name: "forEach"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "has"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "keys"; isJavaScriptFunction: true }
- Method { name: "values"; isJavaScriptFunction: true }
- }
- Component {
- name: "SharedArrayBuffer"
- accessSemantics: "reference"
- prototype: "SharedArrayBufferPrototype"
- }
- Component {
- name: "SharedArrayBufferPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "byteLength"; type: "number" }
- Method {
- name: "constructor"
- type: "SharedArrayBuffer"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "slice"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- }
- Component {
- name: "String"
- accessSemantics: "reference"
- prototype: "StringPrototype"
- Property { name: "length"; type: "number"; isReadonly: true }
- }
- Component {
- name: "StringPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "length"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "String"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "valueOf"; isJavaScriptFunction: true }
- Method {
- name: "charAt"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "charCodeAt"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "codePointAt"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "concat"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "endsWith"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "indexOf"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "includes"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "lastIndexOf"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "localeCompare"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "match"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "normalize"; isJavaScriptFunction: true }
- Method {
- name: "padEnd"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "padStart"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "repeat"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "replace"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "search"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "slice"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "split"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "startsWith"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "substr"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method {
- name: "substring"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- Method { name: "toLowerCase"; isJavaScriptFunction: true }
- Method { name: "toLocaleLowerCase"; isJavaScriptFunction: true }
- Method { name: "toUpperCase"; isJavaScriptFunction: true }
- Method { name: "toLocaleUpperCase"; isJavaScriptFunction: true }
- Method { name: "trim"; isJavaScriptFunction: true }
- Method { name: "arg"; isJavaScriptFunction: true }
- }
- Component {
- name: "SymbolPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method {
- name: "constructor"
- type: "undefined"
- isConstructor: true
- isJavaScriptFunction: true
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "valueOf"; isJavaScriptFunction: true }
- }
- Component {
- name: "SyntaxError"
- accessSemantics: "reference"
- prototype: "SyntaxErrorPrototype"
- Property { name: "stack"; type: "string" }
- Property { name: "fileName" }
- Property { name: "lineNumber" }
- }
- Component {
- name: "SyntaxErrorPrototype"
- accessSemantics: "reference"
- prototype: "ErrorPrototype"
- Property { name: "message"; type: "string" }
- Property { name: "name"; type: "string" }
- Method {
- name: "constructor"
- type: "SyntaxError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- }
- Component {
- name: "TypeError"
- accessSemantics: "reference"
- prototype: "TypeErrorPrototype"
- Property { name: "stack"; type: "string" }
- Property { name: "fileName" }
- Property { name: "lineNumber" }
- }
- Component {
- name: "TypeErrorPrototype"
- accessSemantics: "reference"
- prototype: "ErrorPrototype"
- Property { name: "message"; type: "string" }
- Property { name: "name"; type: "string" }
- Method {
- name: "constructor"
- type: "TypeError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- }
- Component {
- name: "URIError"
- accessSemantics: "reference"
- prototype: "URIErrorPrototype"
- Property { name: "stack"; type: "string" }
- Property { name: "fileName" }
- Property { name: "lineNumber" }
- }
- Component {
- name: "URIErrorPrototype"
- accessSemantics: "reference"
- prototype: "ErrorPrototype"
- Property { name: "message"; type: "string" }
- Property { name: "name"; type: "string" }
- Method {
- name: "constructor"
- type: "URIError"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- }
- Method { name: "toString"; isJavaScriptFunction: true }
- }
- Component { name: "URL"; accessSemantics: "reference"; prototype: "URLPrototype" }
- Component {
- name: "URLPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Property { name: "hash" }
- Property { name: "host" }
- Property { name: "hostname" }
- Property { name: "href" }
- Property { name: "origin" }
- Property { name: "password" }
- Property { name: "pathname" }
- Property { name: "port" }
- Property { name: "protocol" }
- Property { name: "search" }
- Property { name: "searchParams" }
- Property { name: "username" }
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "toJSON"; isJavaScriptFunction: true }
- }
- Component {
- name: "URLSearchParams"
- accessSemantics: "reference"
- prototype: "URLSearchParamsPrototype"
- }
- Component {
- name: "URLSearchParamsPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method { name: "toString"; isJavaScriptFunction: true }
- Method { name: "sort"; isJavaScriptFunction: true }
- Method { name: "append"; isJavaScriptFunction: true }
- Method { name: "delete"; isJavaScriptFunction: true }
- Method { name: "has"; isJavaScriptFunction: true }
- Method { name: "set"; isJavaScriptFunction: true }
- Method { name: "get"; isJavaScriptFunction: true }
- Method { name: "getAll"; isJavaScriptFunction: true }
- Method { name: "forEach"; isJavaScriptFunction: true }
- Method { name: "entries"; isJavaScriptFunction: true }
- Method { name: "keys"; isJavaScriptFunction: true }
- Method { name: "values"; isJavaScriptFunction: true }
- }
- Component {
- name: "Uint16Array"
- accessSemantics: "reference"
- prototype: "Uint16ArrayPrototype"
- }
- Component {
- name: "Uint16ArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Uint16Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component {
- name: "Uint32Array"
- accessSemantics: "reference"
- prototype: "Uint32ArrayPrototype"
- }
- Component {
- name: "Uint32ArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Uint32Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "Uint8Array"; accessSemantics: "reference"; prototype: "Uint8ArrayPrototype" }
- Component {
- name: "Uint8ArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Uint8Array"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component {
- name: "Uint8ClampedArray"
- accessSemantics: "reference"
- prototype: "Uint8ClampedArrayPrototype"
- }
- Component {
- name: "Uint8ClampedArrayPrototype"
- accessSemantics: "reference"
- prototype: "IntrinsicTypedArrayPrototype"
- Property { name: "BYTES_PER_ELEMENT"; type: "number"; isReadonly: true }
- Method {
- name: "constructor"
- type: "Uint8ClampedArray"
- isConstructor: true
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "WeakMap"; accessSemantics: "reference"; prototype: "WeakMapPrototype" }
- Component {
- name: "WeakMapPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method { name: "constructor"; type: "WeakMap"; isConstructor: true; isJavaScriptFunction: true }
- Method {
- name: "delete"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "get"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "has"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "set"
- isJavaScriptFunction: true
- Parameter {}
- Parameter {}
- }
- }
- Component { name: "WeakSet"; accessSemantics: "reference"; prototype: "WeakSetPrototype" }
- Component {
- name: "WeakSetPrototype"
- accessSemantics: "reference"
- prototype: "ObjectPrototype"
- Method { name: "constructor"; type: "WeakSet"; isConstructor: true; isJavaScriptFunction: true }
- Method {
- name: "add"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "delete"
- isJavaScriptFunction: true
- Parameter {}
- }
- Method {
- name: "has"
- isJavaScriptFunction: true
- Parameter {}
- }
- }
- Component { name: "boolean"; accessSemantics: "value"; prototype: "BooleanPrototype" }
- Component {
- name: "function"
- accessSemantics: "reference"
- prototype: "FunctionPrototype"
- Property { name: "prototype"; type: "Object" }
- Property { name: "name"; type: "string"; isReadonly: true }
- Property { name: "length"; type: "number"; isReadonly: true }
- }
- Component { name: "number"; accessSemantics: "value"; prototype: "NumberPrototype" }
- Component { name: "object"; accessSemantics: "reference"; prototype: "ObjectPrototype" }
- Component { name: "string"; accessSemantics: "value"; prototype: "StringPrototype" }
- Component { name: "symbol"; accessSemantics: "value"; prototype: "SymbolPrototype" }
- Component { name: "undefined"; accessSemantics: "value" }
-}
diff --git a/src/imports/tooling/Component.qml b/src/imports/tooling/Component.qml
index 773fcfecda..6a5cde3c2a 100644
--- a/src/imports/tooling/Component.qml
+++ b/src/imports/tooling/Component.qml
@@ -8,19 +8,22 @@ QtObject {
property string file
required property string name
+ property list<string> aliases: []
property string prototype
- property var exports: []
- property var exportMetaObjectRevisions: []
- property var interfaces: []
- property var deferredNames: []
- property var immediateNames: []
+ property list<string> exports: []
+ property list<int> exportMetaObjectRevisions
+ property list<string> interfaces
+ property list<string> deferredNames
+ property list<string> immediateNames
property string attachedType
property string valueType
property string extension
property bool isSingleton: false
- property bool isCreatable: name.length > 0
+ property bool isCreatable: accessSemantics === "reference" && name.length > 0
+ property bool isStructured: false
property bool isComposite: false
property bool hasCustomParser: false
+ property bool extensionIsJavaScript: false
property bool extensionIsNamespace: false
property string accessSemantics: "reference"
property string defaultProperty
diff --git a/src/imports/tooling/Enum.qml b/src/imports/tooling/Enum.qml
index 43ea330685..04276fdeed 100644
--- a/src/imports/tooling/Enum.qml
+++ b/src/imports/tooling/Enum.qml
@@ -5,6 +5,8 @@ import QML
Member {
property string alias
+ property string type
property bool isFlag: false
+ property bool isScoped: false
property var values: []
}
diff --git a/src/imports/tooling/Method.qml b/src/imports/tooling/Method.qml
index fe4a1e3877..cab07f6e38 100644
--- a/src/imports/tooling/Method.qml
+++ b/src/imports/tooling/Method.qml
@@ -12,4 +12,5 @@ Member {
property bool isPointer: false
property bool isJavaScriptFunction: false
property bool isCloned: false
+ property bool isConstant: false
}
diff --git a/src/imports/tooling/Module.qml b/src/imports/tooling/Module.qml
index 487e235230..4aa9312a36 100644
--- a/src/imports/tooling/Module.qml
+++ b/src/imports/tooling/Module.qml
@@ -5,5 +5,5 @@ import QML
QtObject {
default property list<Component> components
- property var dependencies: []
+ property list<string> dependencies: []
}
diff --git a/src/labs/animation/CMakeLists.txt b/src/labs/animation/CMakeLists.txt
index b54a4bf5e7..57fd2bb170 100644
--- a/src/labs/animation/CMakeLists.txt
+++ b/src/labs/animation/CMakeLists.txt
@@ -17,5 +17,4 @@ qt_internal_add_qml_module(LabsAnimation
Qt::QmlPrivate
Qt::QuickPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
diff --git a/src/labs/animation/qqmlanimationglobal_p.h b/src/labs/animation/qqmlanimationglobal_p.h
index 3054c825b9..8f24f3bf7a 100644
--- a/src/labs/animation/qqmlanimationglobal_p.h
+++ b/src/labs/animation/qqmlanimationglobal_p.h
@@ -17,6 +17,6 @@
// We mean it.
//
-#include <QtLabsAnimation/private/qtlabsanimationexports_p.h>
+#include <QtLabsAnimation/qtlabsanimationexports.h>
#endif // QTLABSANIMATIONGLOBAL_P_H
diff --git a/src/labs/animation/qquickboundaryrule.cpp b/src/labs/animation/qquickboundaryrule.cpp
index 21b34f11ff..ead72be187 100644
--- a/src/labs/animation/qquickboundaryrule.cpp
+++ b/src/labs/animation/qquickboundaryrule.cpp
@@ -111,7 +111,7 @@ void QQuickBoundaryReturnJob::updateState(QAbstractAnimationJob::State newState,
Note that a property cannot have more than one assigned BoundaryRule.
\sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#Behaviors}{Behavior
-example}, {Qt QML}
+example}, {Qt Qml}, {Qt Quick Examples - Pointer Handlers}
*/
QQuickBoundaryRule::QQuickBoundaryRule(QObject *parent)
@@ -129,7 +129,7 @@ QQuickBoundaryRule::~QQuickBoundaryRule()
}
/*!
- \qmlproperty bool QtQuick::BoundaryRule::enabled
+ \qmlproperty bool Qt.labs.animation::BoundaryRule::enabled
This property holds whether the rule will be enforced when the tracked
property changes value.
@@ -152,7 +152,7 @@ void QQuickBoundaryRule::setEnabled(bool enabled)
}
/*!
- \qmlproperty qreal QtQuick::BoundaryRule::minimum
+ \qmlproperty qreal Qt.labs.animation::BoundaryRule::minimum
This property holds the smallest unconstrained value that the property is
allowed to have. If the property is set to a smaller value, it will be
@@ -176,7 +176,7 @@ void QQuickBoundaryRule::setMinimum(qreal minimum)
}
/*!
- \qmlproperty qreal QtQuick::BoundaryRule::minimumOvershoot
+ \qmlproperty qreal Qt.labs.animation::BoundaryRule::minimumOvershoot
This property holds the amount that the property is allowed to be
less than \l minimum. Whenever the value is less than \l minimum
@@ -202,7 +202,7 @@ void QQuickBoundaryRule::setMinimumOvershoot(qreal minimumOvershoot)
}
/*!
- \qmlproperty qreal QtQuick::BoundaryRule::maximum
+ \qmlproperty qreal Qt.labs.animation::BoundaryRule::maximum
This property holds the largest unconstrained value that the property is
allowed to have. If the property is set to a larger value, it will be
@@ -226,7 +226,7 @@ void QQuickBoundaryRule::setMaximum(qreal maximum)
}
/*!
- \qmlproperty qreal QtQuick::BoundaryRule::maximumOvershoot
+ \qmlproperty qreal Qt.labs.animation::BoundaryRule::maximumOvershoot
This property holds the amount that the property is allowed to be
more than \l maximum. Whenever the value is greater than \l maximum
@@ -252,7 +252,7 @@ void QQuickBoundaryRule::setMaximumOvershoot(qreal maximumOvershoot)
}
/*!
- \qmlproperty qreal QtQuick::BoundaryRule::overshootScale
+ \qmlproperty qreal Qt.labs.animation::BoundaryRule::overshootScale
This property holds the amount by which the \l easing is scaled during the
overshoot condition. For example if an Item is restricted from moving more
@@ -284,7 +284,7 @@ void QQuickBoundaryRule::setOvershootScale(qreal overshootScale)
}
/*!
- \qmlproperty qreal QtQuick::BoundaryRule::currentOvershoot
+ \qmlproperty qreal Qt.labs.animation::BoundaryRule::currentOvershoot
This property holds the amount by which the most recently set value of the
intercepted property exceeds \l maximum or is less than \l minimum.
@@ -300,7 +300,7 @@ qreal QQuickBoundaryRule::currentOvershoot() const
}
/*!
- \qmlproperty qreal QtQuick::BoundaryRule::peakOvershoot
+ \qmlproperty qreal Qt.labs.animation::BoundaryRule::peakOvershoot
This property holds the most-positive or most-negative value of
\l currentOvershoot that has been seen, until \l returnToBounds() is called.
@@ -318,7 +318,7 @@ qreal QQuickBoundaryRule::peakOvershoot() const
}
/*!
- \qmlproperty enumeration QtQuick::BoundaryRule::overshootFilter
+ \qmlproperty enumeration Qt.labs.animation::BoundaryRule::overshootFilter
This property specifies the aggregation function that will be applied to
the intercepted property value.
@@ -344,7 +344,7 @@ void QQuickBoundaryRule::setOvershootFilter(OvershootFilter overshootFilter)
}
/*!
- \qmlmethod bool QtQuick::BoundaryRule::returnToBounds
+ \qmlmethod bool Qt.labs.animation::BoundaryRule::returnToBounds
Returns the intercepted property to a value between \l minimum and
\l maximum, such that \l currentOvershoot and \l peakOvershoot are both
@@ -388,7 +388,7 @@ bool QQuickBoundaryRule::returnToBounds()
}
/*!
- \qmlsignal QtQuick::BoundaryRule::returnedToBounds()
+ \qmlsignal Qt.labs.animation::BoundaryRule::returnedToBounds()
This signal is emitted when \l currentOvershoot returns to \c 0 again,
after the \l maximum or \l minimum constraint has been violated.
@@ -399,7 +399,7 @@ bool QQuickBoundaryRule::returnToBounds()
*/
/*!
- \qmlproperty enumeration QtQuick::BoundaryRule::easing
+ \qmlproperty enumeration Qt.labs.animation::BoundaryRule::easing
This property holds the easing curve to be applied in overshoot mode
(whenever the \l minimum or \l maximum constraint is violated, while
@@ -423,7 +423,7 @@ void QQuickBoundaryRule::setEasing(const QEasingCurve &easing)
}
/*!
- \qmlproperty int QtQuick::BoundaryRule::returnDuration
+ \qmlproperty int Qt.labs.animation::BoundaryRule::returnDuration
This property holds the amount of time in milliseconds that
\l returnToBounds() will take to return the target property to the nearest bound.
diff --git a/src/labs/animation/qquickboundaryrule_p.h b/src/labs/animation/qquickboundaryrule_p.h
index a3d7e61343..c0f8f9a905 100644
--- a/src/labs/animation/qquickboundaryrule_p.h
+++ b/src/labs/animation/qquickboundaryrule_p.h
@@ -26,24 +26,24 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractAnimation;
class QQuickBoundaryRulePrivate;
-class Q_LABSANIMATION_PRIVATE_EXPORT QQuickBoundaryRule : public QObject, public QQmlPropertyValueInterceptor, public QQmlParserStatus
+class Q_LABSANIMATION_EXPORT QQuickBoundaryRule : public QObject, public QQmlPropertyValueInterceptor, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_DECLARE_PRIVATE(QQuickBoundaryRule)
Q_INTERFACES(QQmlPropertyValueInterceptor)
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
- Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
- Q_PROPERTY(qreal minimumOvershoot READ minimumOvershoot WRITE setMinimumOvershoot NOTIFY minimumOvershootChanged)
- Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged)
- Q_PROPERTY(qreal maximumOvershoot READ maximumOvershoot WRITE setMaximumOvershoot NOTIFY maximumOvershootChanged)
- Q_PROPERTY(qreal overshootScale READ overshootScale WRITE setOvershootScale NOTIFY overshootScaleChanged)
- Q_PROPERTY(qreal currentOvershoot READ currentOvershoot NOTIFY currentOvershootChanged)
- Q_PROPERTY(qreal peakOvershoot READ peakOvershoot NOTIFY peakOvershootChanged)
- Q_PROPERTY(OvershootFilter overshootFilter READ overshootFilter WRITE setOvershootFilter NOTIFY overshootFilterChanged)
- Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged)
- Q_PROPERTY(int returnDuration READ returnDuration WRITE setReturnDuration NOTIFY returnDurationChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged FINAL)
+ Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged FINAL)
+ Q_PROPERTY(qreal minimumOvershoot READ minimumOvershoot WRITE setMinimumOvershoot NOTIFY minimumOvershootChanged FINAL)
+ Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged FINAL)
+ Q_PROPERTY(qreal maximumOvershoot READ maximumOvershoot WRITE setMaximumOvershoot NOTIFY maximumOvershootChanged FINAL)
+ Q_PROPERTY(qreal overshootScale READ overshootScale WRITE setOvershootScale NOTIFY overshootScaleChanged FINAL)
+ Q_PROPERTY(qreal currentOvershoot READ currentOvershoot NOTIFY currentOvershootChanged FINAL)
+ Q_PROPERTY(qreal peakOvershoot READ peakOvershoot NOTIFY peakOvershootChanged FINAL)
+ Q_PROPERTY(OvershootFilter overshootFilter READ overshootFilter WRITE setOvershootFilter NOTIFY overshootFilterChanged FINAL)
+ Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged FINAL)
+ Q_PROPERTY(int returnDuration READ returnDuration WRITE setReturnDuration NOTIFY returnDurationChanged FINAL)
QML_NAMED_ELEMENT(BoundaryRule)
QML_ADDED_IN_VERSION(1, 0)
@@ -111,6 +111,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickBoundaryRule)
-
#endif // QQUICKBOUNDARYRULE_H
diff --git a/src/labs/folderlistmodel/CMakeLists.txt b/src/labs/folderlistmodel/CMakeLists.txt
index b403097045..bcb7b7d94f 100644
--- a/src/labs/folderlistmodel/CMakeLists.txt
+++ b/src/labs/folderlistmodel/CMakeLists.txt
@@ -20,5 +20,4 @@ qt_internal_add_qml_module(LabsFolderListModel
Qt::QmlPrivate
Qt::QmlModelsPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
diff --git a/src/labs/folderlistmodel/fileproperty_p.h b/src/labs/folderlistmodel/fileproperty_p.h
index 18b5f0918e..801d421ada 100644
--- a/src/labs/folderlistmodel/fileproperty_p.h
+++ b/src/labs/folderlistmodel/fileproperty_p.h
@@ -15,14 +15,18 @@
// We mean it.
//
-#include <QFileInfo>
-#include <QDateTime>
+#include <QtQmlIntegration/qqmlintegration.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdatetime.h>
+
#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
class FileProperty
{
+ Q_GADGET
+ QML_ANONYMOUS
public:
FileProperty(const QFileInfo &info) :
mFileName(info.fileName()),
diff --git a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
index 64a5be796e..af6fd3b2ef 100644
--- a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
+++ b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
@@ -232,16 +232,16 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
Components access names and paths via the following roles:
\list
- \li \c fileName
- \li \c filePath
- \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15)
- \li \c fileUrl (since Qt 5.15)
- \li \c fileBaseName
- \li \c fileSuffix
- \li \c fileSize
- \li \c fileModified
- \li \c fileAccessed
- \li \c fileIsDir
+ \li \c fileName (\c string)
+ \li \c filePath (\c string)
+ \li \c fileURL (\c url) (since Qt 5.2; deprecated since Qt 5.15)
+ \li \c fileUrl (\c url) (since Qt 5.15)
+ \li \c fileBaseName (\c string)
+ \li \c fileSuffix (\c string)
+ \li \c fileSize (\c qlonglong)
+ \li \c fileModified (\c date)
+ \li \c fileAccessed (\c date)
+ \li \c fileIsDir (\c bool)
\endlist
Additionally a file entry can be differentiated from a folder entry via the
@@ -284,6 +284,7 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
Component {
id: fileDelegate
+ required property string fileName
Text { text: fileName }
}
@@ -375,6 +376,7 @@ QHash<int, QByteArray> QQuickFolderListModel::roleNames() const
/*!
\qmlproperty int FolderListModel::count
+ \readonly
Returns the number of items in the current folder that match the
filter criteria.
@@ -399,7 +401,8 @@ QModelIndex QQuickFolderListModel::index(int row, int , const QModelIndex &) con
The value must be a \c file: or \c qrc: URL, or a relative URL.
- The default value is an invalid URL.
+ The default value is the application's working directory at the time
+ when the FolderListModel is first initialized.
*/
QUrl QQuickFolderListModel::folder() const
{
@@ -474,6 +477,7 @@ void QQuickFolderListModel::setRootFolder(const QUrl &path)
/*!
\qmlproperty url FolderListModel::parentFolder
+ \readonly
Returns the URL of the parent of the current \l folder.
*/
@@ -544,15 +548,14 @@ void QQuickFolderListModel::componentComplete()
/*!
\qmlproperty enumeration FolderListModel::sortField
- The \a sortField property contains field to use for sorting. sortField
- may be one of:
- \list
- \li Unsorted - no sorting is applied.
- \li Name - sort by filename
- \li Time - sort by time modified
- \li Size - sort by file size
- \li Type - sort by file type (extension)
- \endlist
+ The \a sortField property contains the field to use for sorting.
+ \c sortField may be one of:
+
+ \value FolderListModel.Unsorted no sorting is applied
+ \value FolderListModel.Name sort by filename (default)
+ \value FolderListModel.Time sort by time modified
+ \value FolderListModel.Size sort by file size
+ \value FolderListModel.Type sort by file type/extension
\sa sortReversed
*/
@@ -800,13 +803,13 @@ void QQuickFolderListModel::setCaseSensitive(bool on)
/*!
\qmlproperty enumeration FolderListModel::status
\since 5.11
+ \readonly
This property holds the status of folder reading. It can be one of:
- \list
- \li FolderListModel.Null - no \a folder has been set
- \li FolderListModel.Ready - the folder has been loaded
- \li FolderListModel.Loading - the folder is currently being loaded
- \endlist
+
+ \value FolderListModel.Null no \a folder has been set
+ \value FolderListModel.Ready the folder has been loaded
+ \value FolderListModel.Loading the folder is currently being loaded
Use this status to provide an update or respond to the status change in some way.
For example, you could:
@@ -867,16 +870,16 @@ void QQuickFolderListModel::setSortCaseSensitive(bool on)
are available:
\list
- \li \c fileName
- \li \c filePath
- \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15)
- \li \c fileUrl (since Qt 5.15)
- \li \c fileBaseName
- \li \c fileSuffix
- \li \c fileSize
- \li \c fileModified
- \li \c fileAccessed
- \li \c fileIsDir
+ \li \c fileName (\c string)
+ \li \c filePath (\c string)
+ \li \c fileURL (\c url) (since Qt 5.2; deprecated since Qt 5.15)
+ \li \c fileUrl (\c url) (since Qt 5.15)
+ \li \c fileBaseName (\c string)
+ \li \c fileSuffix (\c string)
+ \li \c fileSize (\c qlonglong)
+ \li \c fileModified (\c date)
+ \li \c fileAccessed (\c date)
+ \li \c fileIsDir (\c bool)
\endlist
*/
QVariant QQuickFolderListModel::get(int idx, const QString &property) const
diff --git a/src/labs/folderlistmodel/qquickfolderlistmodel_p.h b/src/labs/folderlistmodel/qquickfolderlistmodel_p.h
index 69a3558b43..be3dd2d2b7 100644
--- a/src/labs/folderlistmodel/qquickfolderlistmodel_p.h
+++ b/src/labs/folderlistmodel/qquickfolderlistmodel_p.h
@@ -31,29 +31,29 @@ class QModelIndex;
class QQuickFolderListModelPrivate;
//![class begin]
-class Q_LABSFOLDERLISTMODEL_PRIVATE_EXPORT QQuickFolderListModel : public QAbstractListModel, public QQmlParserStatus
+class Q_LABSFOLDERLISTMODEL_EXPORT QQuickFolderListModel : public QAbstractListModel, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
//![class begin]
//![class props]
- Q_PROPERTY(QUrl folder READ folder WRITE setFolder NOTIFY folderChanged)
- Q_PROPERTY(QUrl rootFolder READ rootFolder WRITE setRootFolder)
- Q_PROPERTY(QUrl parentFolder READ parentFolder NOTIFY folderChanged)
- Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters)
- Q_PROPERTY(SortField sortField READ sortField WRITE setSortField)
- Q_PROPERTY(bool sortReversed READ sortReversed WRITE setSortReversed)
- Q_PROPERTY(bool showFiles READ showFiles WRITE setShowFiles REVISION(2, 1))
- Q_PROPERTY(bool showDirs READ showDirs WRITE setShowDirs)
- Q_PROPERTY(bool showDirsFirst READ showDirsFirst WRITE setShowDirsFirst)
- Q_PROPERTY(bool showDotAndDotDot READ showDotAndDotDot WRITE setShowDotAndDotDot)
- Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden REVISION(2, 1))
- Q_PROPERTY(bool showOnlyReadable READ showOnlyReadable WRITE setShowOnlyReadable)
- Q_PROPERTY(bool caseSensitive READ caseSensitive WRITE setCaseSensitive REVISION(2, 2))
- Q_PROPERTY(int count READ count NOTIFY countChanged)
- Q_PROPERTY(Status status READ status NOTIFY statusChanged REVISION(2, 11))
- Q_PROPERTY(bool sortCaseSensitive READ sortCaseSensitive WRITE setSortCaseSensitive REVISION(2, 12))
+ Q_PROPERTY(QUrl folder READ folder WRITE setFolder NOTIFY folderChanged FINAL)
+ Q_PROPERTY(QUrl rootFolder READ rootFolder WRITE setRootFolder FINAL)
+ Q_PROPERTY(QUrl parentFolder READ parentFolder NOTIFY folderChanged FINAL)
+ Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters FINAL)
+ Q_PROPERTY(SortField sortField READ sortField WRITE setSortField FINAL)
+ Q_PROPERTY(bool sortReversed READ sortReversed WRITE setSortReversed FINAL)
+ Q_PROPERTY(bool showFiles READ showFiles WRITE setShowFiles REVISION(2, 1) FINAL)
+ Q_PROPERTY(bool showDirs READ showDirs WRITE setShowDirs FINAL)
+ Q_PROPERTY(bool showDirsFirst READ showDirsFirst WRITE setShowDirsFirst FINAL)
+ Q_PROPERTY(bool showDotAndDotDot READ showDotAndDotDot WRITE setShowDotAndDotDot FINAL)
+ Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden REVISION(2, 1) FINAL)
+ Q_PROPERTY(bool showOnlyReadable READ showOnlyReadable WRITE setShowOnlyReadable FINAL)
+ Q_PROPERTY(bool caseSensitive READ caseSensitive WRITE setCaseSensitive REVISION(2, 2) FINAL)
+ Q_PROPERTY(int count READ count NOTIFY countChanged FINAL)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged REVISION(2, 11) FINAL)
+ Q_PROPERTY(bool sortCaseSensitive READ sortCaseSensitive WRITE setSortCaseSensitive REVISION(2, 12) FINAL)
//![class props]
QML_NAMED_ELEMENT(FolderListModel)
diff --git a/src/labs/folderlistmodel/qquickfolderlistmodelglobal_p.h b/src/labs/folderlistmodel/qquickfolderlistmodelglobal_p.h
index a7c4ba1879..481ffee36a 100644
--- a/src/labs/folderlistmodel/qquickfolderlistmodelglobal_p.h
+++ b/src/labs/folderlistmodel/qquickfolderlistmodelglobal_p.h
@@ -17,6 +17,6 @@
// We mean it.
//
-#include <QtLabsFolderListModel/private/qtlabsfolderlistmodelexports_p.h>
+#include <QtLabsFolderListModel/qtlabsfolderlistmodelexports.h>
#endif // QTLABSFOLDERLISTMODELGLOBAL_P_H
diff --git a/src/labs/models/CMakeLists.txt b/src/labs/models/CMakeLists.txt
index 3ff03fb0cd..6c6697a5a5 100644
--- a/src/labs/models/CMakeLists.txt
+++ b/src/labs/models/CMakeLists.txt
@@ -16,8 +16,7 @@ qt_internal_add_qml_module(LabsQmlModels
DEPENDENCIES
QtQml.Models/auto
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
qt_internal_extend_target(LabsQmlModels CONDITION QT_FEATURE_qml_table_model
SOURCES
diff --git a/src/labs/models/doc/src/qmllabsmodels.qdoc b/src/labs/models/doc/src/qmllabsmodels.qdoc
index f5e18792ab..8c4fbb17b7 100644
--- a/src/labs/models/doc/src/qmllabsmodels.qdoc
+++ b/src/labs/models/doc/src/qmllabsmodels.qdoc
@@ -12,7 +12,7 @@
To use the types in this module, import the module with the following line:
- \code
+ \qml
import Qt.labs.qmlmodels
- \endcode
+ \endqml
*/
diff --git a/src/labs/models/qqmldelegatecomponent.cpp b/src/labs/models/qqmldelegatecomponent.cpp
index 3a9bb647c6..01dbe7e81f 100644
--- a/src/labs/models/qqmldelegatecomponent.cpp
+++ b/src/labs/models/qqmldelegatecomponent.cpp
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \qmlproperty variant QtQml.Models::DelegateChoice::roleValue
+ \qmlproperty variant Qt.labs.qmlmodels::DelegateChoice::roleValue
This property holds the value used to match the role data for the role provided by \l DelegateChooser::role.
*/
QVariant QQmlDelegateChoice::roleValue() const
@@ -39,7 +39,7 @@ void QQmlDelegateChoice::setRoleValue(const QVariant &value)
}
/*!
- \qmlproperty int QtQml.Models::DelegateChoice::row
+ \qmlproperty int Qt.labs.qmlmodels::DelegateChoice::row
This property holds the value used to match the row value of model elements.
With models that have only the index property (and thus only one column), this property
should be intended as an index, and set to the desired index value.
@@ -51,7 +51,7 @@ void QQmlDelegateChoice::setRoleValue(const QVariant &value)
*/
/*!
- \qmlproperty int QtQml.Models::DelegateChoice::index
+ \qmlproperty int Qt.labs.qmlmodels::DelegateChoice::index
This property holds the value used to match the index value of model elements.
This is effectively an alias for \l row.
@@ -73,7 +73,7 @@ void QQmlDelegateChoice::setRow(int r)
}
/*!
- \qmlproperty int QtQml.Models::DelegateChoice::column
+ \qmlproperty int Qt.labs.qmlmodels::DelegateChoice::column
This property holds the value used to match the column value of model elements.
*/
int QQmlDelegateChoice::column() const
@@ -96,7 +96,7 @@ QQmlComponent *QQmlDelegateChoice::delegate() const
}
/*!
- \qmlproperty Component QtQml.Models::DelegateChoice::delegate
+ \qmlproperty Component Qt.labs.qmlmodels::DelegateChoice::delegate
This property holds the delegate to use if this choice matches the model item.
*/
void QQmlDelegateChoice::setDelegate(QQmlComponent *delegate)
@@ -190,9 +190,13 @@ bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const
*/
/*!
- \qmlproperty string QtQml.Models::DelegateChooser::role
+ \qmlproperty string Qt.labs.qmlmodels::DelegateChooser::role
This property holds the role or the property name used to determine the delegate for a given model item.
+ \note For \l{QAbstractItemModel} based models, including \l{ListModel}, the DelegateChooser will
+ reevaluate the choice when the model signals that the role has changed. For any other type of model,
+ this choice will only be done once when the item for a given model index is created.
+
\sa DelegateChoice
*/
void QQmlDelegateChooser::setRole(const QString &role)
@@ -204,7 +208,7 @@ void QQmlDelegateChooser::setRole(const QString &role)
}
/*!
- \qmlproperty list<DelegateChoice> QtQml.Models::DelegateChooser::choices
+ \qmlproperty list<DelegateChoice> Qt.labs.qmlmodels::DelegateChooser::choices
\qmldefault
The list of DelegateChoices for the chooser.
@@ -233,7 +237,7 @@ void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *p
QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
q->m_choices.append(choice);
connect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
- q->delegateChanged();
+ emit q->delegateChanged();
}
qsizetype QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop)
@@ -254,7 +258,7 @@ void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *pr
for (QQmlDelegateChoice *choice : q->m_choices)
disconnect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
q->m_choices.clear();
- q->delegateChanged();
+ emit q->delegateChanged();
}
void QQmlDelegateChooser::choices_replace(QQmlListProperty<QQmlDelegateChoice> *prop,
@@ -266,7 +270,7 @@ void QQmlDelegateChooser::choices_replace(QQmlListProperty<QQmlDelegateChoice> *
q->m_choices[index] = choice;
connect(choice, &QQmlDelegateChoice::changed, q,
&QQmlAbstractDelegateComponent::delegateChanged);
- q->delegateChanged();
+ emit q->delegateChanged();
}
void QQmlDelegateChooser::choices_removeLast(QQmlListProperty<QQmlDelegateChoice> *prop)
@@ -274,7 +278,7 @@ void QQmlDelegateChooser::choices_removeLast(QQmlListProperty<QQmlDelegateChoice
QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
disconnect(q->m_choices.takeLast(), &QQmlDelegateChoice::changed,
q, &QQmlAbstractDelegateComponent::delegateChanged);
- q->delegateChanged();
+ emit q->delegateChanged();
}
QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const
diff --git a/src/labs/models/qqmldelegatecomponent_p.h b/src/labs/models/qqmldelegatecomponent_p.h
index 254f9d0b7e..2929094fd6 100644
--- a/src/labs/models/qqmldelegatecomponent_p.h
+++ b/src/labs/models/qqmldelegatecomponent_p.h
@@ -25,14 +25,14 @@ QT_REQUIRE_CONFIG(qml_delegate_model);
QT_BEGIN_NAMESPACE
-class Q_LABSQMLMODELS_PRIVATE_EXPORT QQmlDelegateChoice : public QObject
+class Q_LABSQMLMODELS_EXPORT QQmlDelegateChoice : public QObject
{
Q_OBJECT
- Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged)
- Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged)
- Q_PROPERTY(int index READ row WRITE setRow NOTIFY indexChanged)
- Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged)
- Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged FINAL)
+ Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged FINAL)
+ Q_PROPERTY(int index READ row WRITE setRow NOTIFY indexChanged FINAL)
+ Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged FINAL)
+ Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL)
Q_CLASSINFO("DefaultProperty", "delegate")
QML_NAMED_ELEMENT(DelegateChoice)
QML_ADDED_IN_VERSION(1, 0)
@@ -67,17 +67,17 @@ private:
QQmlComponent *m_delegate = nullptr;
};
-class Q_LABSQMLMODELS_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent
+class Q_LABSQMLMODELS_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent
{
Q_OBJECT
- Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged)
- Q_PROPERTY(QQmlListProperty<QQmlDelegateChoice> choices READ choices CONSTANT)
+ Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QQmlDelegateChoice> choices READ choices CONSTANT FINAL)
Q_CLASSINFO("DefaultProperty", "choices")
QML_NAMED_ELEMENT(DelegateChooser)
QML_ADDED_IN_VERSION(1, 0)
public:
- QString role() const { return m_role; }
+ QString role() const final { return m_role; }
void setRole(const QString &role);
virtual QQmlListProperty<QQmlDelegateChoice> choices();
@@ -101,7 +101,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlDelegateChoice)
-QML_DECLARE_TYPE(QQmlDelegateChooser)
-
#endif // QQMLDELEGATECOMPONENT_P_H
diff --git a/src/labs/models/qqmlmodelsglobal_p.h b/src/labs/models/qqmlmodelsglobal_p.h
index 9f1e325e6e..72efdfa4b5 100644
--- a/src/labs/models/qqmlmodelsglobal_p.h
+++ b/src/labs/models/qqmlmodelsglobal_p.h
@@ -17,6 +17,6 @@
// We mean it.
//
-#include <QtLabsQmlModels/private/qtlabsqmlmodelsexports_p.h>
+#include <QtLabsQmlModels/qtlabsqmlmodelsexports.h>
#endif // QTLABSQMLMODELSGLOBAL_P_H
diff --git a/src/labs/models/qqmltablemodel_p.h b/src/labs/models/qqmltablemodel_p.h
index 2df813dfcd..9157c989d3 100644
--- a/src/labs/models/qqmltablemodel_p.h
+++ b/src/labs/models/qqmltablemodel_p.h
@@ -30,7 +30,7 @@ QT_REQUIRE_CONFIG(qml_table_model);
QT_BEGIN_NAMESPACE
-class Q_LABSQMLMODELS_PRIVATE_EXPORT QQmlTableModel : public QAbstractTableModel, public QQmlParserStatus
+class Q_LABSQMLMODELS_EXPORT QQmlTableModel : public QAbstractTableModel, public QQmlParserStatus
{
Q_OBJECT
Q_PROPERTY(int columnCount READ columnCount NOTIFY columnCountChanged FINAL)
@@ -81,6 +81,10 @@ Q_SIGNALS:
void rowCountChanged();
void rowsChanged();
+protected:
+ void classBegin() override;
+ void componentComplete() override;
+
private:
class ColumnRoleMetadata
{
@@ -122,9 +126,6 @@ private:
void doInsert(int rowIndex, const QVariant &row);
- void classBegin() override;
- void componentComplete() override;
-
bool componentCompleted = false;
QVariantList mRows;
QList<QQmlTableModelColumn *> mColumns;
@@ -139,6 +140,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlTableModel)
-
#endif // QQMLTABLEMODEL_P_H
diff --git a/src/labs/models/qqmltablemodelcolumn.cpp b/src/labs/models/qqmltablemodelcolumn.cpp
index f2e6d65f87..356cad476f 100644
--- a/src/labs/models/qqmltablemodelcolumn.cpp
+++ b/src/labs/models/qqmltablemodelcolumn.cpp
@@ -18,27 +18,50 @@ QT_BEGIN_NAMESPACE
TableModelColumn supports all of \l {Qt::ItemDataRole}{Qt's roles},
with the exception of \c Qt::InitialSortOrderRole.
+ Roles can be accessed by as listed below, e.g.
+ \code
+ text: display
+
+ required property string display
+ \endcode
+
+ \table
+ \row \li Qt::DisplayRole \li display
+ \row \li Qt::DecorationRole \li decoration
+ \row \li Qt::EditRole \li edit
+ \row \li Qt::ToolTipRole \li toolTip
+ \row \li Qt::StatusTipRole \li statusTip
+ \row \li Qt::WhatsThisRole \li whatsThis
+ \row \li Qt::FontRole \li font
+ \row \li Qt::TextAlignmentRole \li textAlignment
+ \row \li Qt::BackgroundRole \li background
+ \row \li Qt::ForegroundRole \li foreground
+ \row \li Qt::CheckStateRole \li checkState
+ \row \li Qt::AccessibleTextRole \li accessibleText
+ \row \li Qt::AccessibleDescriptionRole \li accessibleDescription
+ \row \li Qt::SizeHintRole \li sizeHintRoleNam
+ \endtable
\sa TableModel, TableView
*/
-static const QString displayRoleName = QStringLiteral("display");
-static const QString decorationRoleName = QStringLiteral("decoration");
-static const QString editRoleName = QStringLiteral("edit");
-static const QString toolTipRoleName = QStringLiteral("toolTip");
-static const QString statusTipRoleName = QStringLiteral("statusTip");
-static const QString whatsThisRoleName = QStringLiteral("whatsThis");
+static constexpr QLatin1StringView displayRoleName("display");
+static constexpr QLatin1StringView decorationRoleName("decoration");
+static constexpr QLatin1StringView editRoleName("edit");
+static constexpr QLatin1StringView toolTipRoleName("toolTip");
+static constexpr QLatin1StringView statusTipRoleName("statusTip");
+static constexpr QLatin1StringView whatsThisRoleName("whatsThis");
-static const QString fontRoleName = QStringLiteral("font");
-static const QString textAlignmentRoleName = QStringLiteral("textAlignment");
-static const QString backgroundRoleName = QStringLiteral("background");
-static const QString foregroundRoleName = QStringLiteral("foreground");
-static const QString checkStateRoleName = QStringLiteral("checkState");
+static constexpr QLatin1StringView fontRoleName("font");
+static constexpr QLatin1StringView textAlignmentRoleName("textAlignment");
+static constexpr QLatin1StringView backgroundRoleName("background");
+static constexpr QLatin1StringView foregroundRoleName("foreground");
+static constexpr QLatin1StringView checkStateRoleName("checkState");
-static const QString accessibleTextRoleName = QStringLiteral("accessibleText");
-static const QString accessibleDescriptionRoleName = QStringLiteral("accessibleDescription");
+static constexpr QLatin1StringView accessibleTextRoleName("accessibleText");
+static constexpr QLatin1StringView accessibleDescriptionRoleName("accessibleDescription");
-static const QString sizeHintRoleName = QStringLiteral("sizeHint");
+static constexpr QLatin1StringView sizeHintRoleName("sizeHint");
QQmlTableModelColumn::QQmlTableModelColumn(QObject *parent)
@@ -143,21 +166,22 @@ const QHash<QString, QJSValue> QQmlTableModelColumn::getters() const
const QHash<int, QString> QQmlTableModelColumn::supportedRoleNames()
{
- QHash<int, QString> names;
- names[Qt::DisplayRole] = QLatin1String("display");
- names[Qt::DecorationRole] = QLatin1String("decoration");
- names[Qt::EditRole] = QLatin1String("edit");
- names[Qt::ToolTipRole] = QLatin1String("toolTip");
- names[Qt::StatusTipRole] = QLatin1String("statusTip");
- names[Qt::WhatsThisRole] = QLatin1String("whatsThis");
- names[Qt::FontRole] = QLatin1String("font");
- names[Qt::TextAlignmentRole] = QLatin1String("textAlignment");
- names[Qt::BackgroundRole] = QLatin1String("background");
- names[Qt::ForegroundRole] = QLatin1String("foreground");
- names[Qt::CheckStateRole] = QLatin1String("checkState");
- names[Qt::AccessibleTextRole] = QLatin1String("accessibleText");
- names[Qt::AccessibleDescriptionRole] = QLatin1String("accessibleDescription");
- names[Qt::SizeHintRole] = QLatin1String("sizeHint");
+ static const QHash<int, QString> names {
+ {Qt::DisplayRole, displayRoleName},
+ {Qt::DecorationRole, decorationRoleName},
+ {Qt::EditRole, editRoleName},
+ {Qt::ToolTipRole, toolTipRoleName},
+ {Qt::StatusTipRole, statusTipRoleName},
+ {Qt::WhatsThisRole, whatsThisRoleName},
+ {Qt::FontRole, fontRoleName},
+ {Qt::TextAlignmentRole, textAlignmentRoleName},
+ {Qt::BackgroundRole, backgroundRoleName},
+ {Qt::ForegroundRole, foregroundRoleName},
+ {Qt::CheckStateRole, checkStateRoleName},
+ {Qt::AccessibleTextRole, accessibleTextRoleName},
+ {Qt::AccessibleDescriptionRole, accessibleDescriptionRoleName},
+ {Qt::SizeHintRole, sizeHintRoleName}
+ };
return names;
}
diff --git a/src/labs/models/qqmltablemodelcolumn_p.h b/src/labs/models/qqmltablemodelcolumn_p.h
index 04ced79d16..d095343fe6 100644
--- a/src/labs/models/qqmltablemodelcolumn_p.h
+++ b/src/labs/models/qqmltablemodelcolumn_p.h
@@ -27,11 +27,11 @@ QT_REQUIRE_CONFIG(qml_table_model);
QT_BEGIN_NAMESPACE
-class Q_LABSQMLMODELS_PRIVATE_EXPORT QQmlTableModelColumn : public QObject
+class Q_LABSQMLMODELS_EXPORT QQmlTableModelColumn : public QObject
{
Q_OBJECT
Q_PROPERTY(QJSValue display READ display WRITE setDisplay NOTIFY displayChanged FINAL)
- Q_PROPERTY(QJSValue setDisplay READ getSetDisplay WRITE setSetDisplay NOTIFY setDisplayChanged)
+ Q_PROPERTY(QJSValue setDisplay READ getSetDisplay WRITE setSetDisplay NOTIFY setDisplayChanged FINAL)
Q_PROPERTY(QJSValue decoration READ decoration WRITE setDecoration NOTIFY decorationChanged FINAL)
Q_PROPERTY(QJSValue setDecoration READ getSetDecoration WRITE setSetDecoration NOTIFY setDecorationChanged FINAL)
Q_PROPERTY(QJSValue edit READ edit WRITE setEdit NOTIFY editChanged FINAL)
@@ -188,6 +188,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlTableModelColumn)
-
#endif // QQMLTABLEMODELCOLUMN_P_H
diff --git a/src/labs/platform/CMakeLists.txt b/src/labs/platform/CMakeLists.txt
index ff98a386ae..adb36e2216 100644
--- a/src/labs/platform/CMakeLists.txt
+++ b/src/labs/platform/CMakeLists.txt
@@ -1,17 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-#####################################################################
-## qtlabsplatformplugin Plugin:
-#####################################################################
-
-qt_internal_add_qml_module(qtlabsplatformplugin
+qt_internal_add_qml_module(LabsPlatform
URI "Qt.labs.platform"
VERSION "1.1"
CLASS_NAME QtLabsPlatformPlugin
DEPENDENCIES
QtQuick
- PLUGIN_TARGET qtlabsplatformplugin
+ QtQuick.Templates
SOURCES
qquicklabsplatformcolordialog.cpp qquicklabsplatformcolordialog_p.h
qquicklabsplatformdialog.cpp qquicklabsplatformdialog_p.h
@@ -38,54 +34,76 @@ qt_internal_add_qml_module(qtlabsplatformplugin
Qt::QuickTemplates2Private
)
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets
+qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_systemtrayicon
SOURCES
- widgets/qwidgetplatform_p.h
- LIBRARIES
- Qt::Widgets
+ qquicklabsplatformsystemtrayicon.cpp qquicklabsplatformsystemtrayicon_p.h
)
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_systemtrayicon AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformsystemtrayicon.cpp widgets/qwidgetplatformsystemtrayicon_p.h
-)
+if(TARGET Qt::Widgets)
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND (QT_FEATURE_colordialog OR QT_FEATURE_filedialog OR QT_FEATURE_fontdialog OR QT_FEATURE_messagebox)
- SOURCES
- widgets/qwidgetplatformdialog.cpp widgets/qwidgetplatformdialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ SOURCES
+ widgets/qwidgetplatform_p.h
+ LIBRARIES
+ Qt::Widgets
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_colordialog AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformcolordialog.cpp widgets/qwidgetplatformcolordialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_systemtrayicon
+ SOURCES
+ widgets/qwidgetplatformsystemtrayicon.cpp widgets/qwidgetplatformsystemtrayicon_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_filedialog AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformfiledialog.cpp widgets/qwidgetplatformfiledialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_colordialog OR
+ QT_FEATURE_filedialog OR
+ QT_FEATURE_fontdialog OR
+ QT_FEATURE_messagebox
+ SOURCES
+ widgets/qwidgetplatformdialog.cpp widgets/qwidgetplatformdialog_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_fontdialog AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformfontdialog.cpp widgets/qwidgetplatformfontdialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_colordialog
+ SOURCES
+ widgets/qwidgetplatformcolordialog.cpp widgets/qwidgetplatformcolordialog_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_menu AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformmenu.cpp widgets/qwidgetplatformmenu_p.h
- widgets/qwidgetplatformmenuitem.cpp widgets/qwidgetplatformmenuitem_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_filedialog
+ SOURCES
+ widgets/qwidgetplatformfiledialog.cpp widgets/qwidgetplatformfiledialog_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION TARGET Qt::Widgets AND QT_FEATURE_messagebox AND TARGET Qt::Widgets # special case
- SOURCES
- widgets/qwidgetplatformmessagedialog.cpp widgets/qwidgetplatformmessagedialog_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_fontdialog
+ SOURCES
+ widgets/qwidgetplatformfontdialog.cpp widgets/qwidgetplatformfontdialog_p.h
+ )
-qt_internal_extend_target(qtlabsplatformplugin CONDITION QT_FEATURE_systemtrayicon
- SOURCES
- qquicklabsplatformsystemtrayicon.cpp qquicklabsplatformsystemtrayicon_p.h
-)
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_menu
+ SOURCES
+ widgets/qwidgetplatformmenu.cpp widgets/qwidgetplatformmenu_p.h
+ widgets/qwidgetplatformmenuitem.cpp widgets/qwidgetplatformmenuitem_p.h
+ )
+
+ qt_internal_extend_target(LabsPlatform
+ CONDITION
+ QT_FEATURE_messagebox
+ SOURCES
+ widgets/qwidgetplatformmessagedialog.cpp widgets/qwidgetplatformmessagedialog_p.h
+ )
+endif()
-qt_internal_add_docs(qtlabsplatformplugin
+qt_internal_add_docs(LabsPlatform
doc/qtlabsplatform.qdocconf
)
diff --git a/src/labs/platform/doc/qtlabsplatform.qdocconf b/src/labs/platform/doc/qtlabsplatform.qdocconf
index c6d70ad83b..0e22443cb8 100644
--- a/src/labs/platform/doc/qtlabsplatform.qdocconf
+++ b/src/labs/platform/doc/qtlabsplatform.qdocconf
@@ -37,5 +37,5 @@ tagfile = qtlabsplatform.tags
macro.labs = "\\note \\e{Types in Qt.labs modules are not guaranteed to remain compatible in future versions.}"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/labs/platform/doc/snippets/qtlabsplatform-menu-instantiator.qml b/src/labs/platform/doc/snippets/qtlabsplatform-menu-instantiator.qml
index 683d5e3338..f89b28af2e 100644
--- a/src/labs/platform/doc/snippets/qtlabsplatform-menu-instantiator.qml
+++ b/src/labs/platform/doc/snippets/qtlabsplatform-menu-instantiator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import Qt.labs.platform
diff --git a/src/labs/platform/qquicklabsplatformcolordialog_p.h b/src/labs/platform/qquicklabsplatformcolordialog_p.h
index cde2fd0a5d..b9f025731e 100644
--- a/src/labs/platform/qquicklabsplatformcolordialog_p.h
+++ b/src/labs/platform/qquicklabsplatformcolordialog_p.h
@@ -61,6 +61,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformColorDialog)
-
#endif // QQUICKLABSPLATFORMCOLORDIALOG_P_H
diff --git a/src/labs/platform/qquicklabsplatformdialog_p.h b/src/labs/platform/qquicklabsplatformdialog_p.h
index e04b097694..fc222b8ba4 100644
--- a/src/labs/platform/qquicklabsplatformdialog_p.h
+++ b/src/labs/platform/qquicklabsplatformdialog_p.h
@@ -126,6 +126,4 @@ class QPlatformDialogHelperQuickLabsForeign
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformDialog)
-
#endif // QQUICKLABSPLATFORMDIALOG_P_H
diff --git a/src/labs/platform/qquicklabsplatformfiledialog_p.h b/src/labs/platform/qquicklabsplatformfiledialog_p.h
index e526fda7d0..9bfd8906e7 100644
--- a/src/labs/platform/qquicklabsplatformfiledialog_p.h
+++ b/src/labs/platform/qquicklabsplatformfiledialog_p.h
@@ -36,7 +36,7 @@ class QQuickLabsPlatformFileDialog : public QQuickLabsPlatformDialog
Q_PROPERTY(QUrl folder READ folder WRITE setFolder NOTIFY folderChanged FINAL)
Q_PROPERTY(QFileDialogOptions::FileDialogOptions options READ options WRITE setOptions RESET resetOptions NOTIFY optionsChanged FINAL)
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters RESET resetNameFilters NOTIFY nameFiltersChanged FINAL)
- Q_PROPERTY(QQuickLabsPlatformFileNameFilter *selectedNameFilter READ selectedNameFilter CONSTANT)
+ Q_PROPERTY(QQuickLabsPlatformFileNameFilter *selectedNameFilter READ selectedNameFilter CONSTANT FINAL)
Q_PROPERTY(QString defaultSuffix READ defaultSuffix WRITE setDefaultSuffix RESET resetDefaultSuffix NOTIFY defaultSuffixChanged FINAL)
Q_PROPERTY(QString acceptLabel READ acceptLabel WRITE setAcceptLabel RESET resetAcceptLabel NOTIFY acceptLabelChanged FINAL)
Q_PROPERTY(QString rejectLabel READ rejectLabel WRITE setRejectLabel RESET resetRejectLabel NOTIFY rejectLabelChanged FINAL)
@@ -161,6 +161,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformFileDialog)
-
#endif // QQUICKLABSPLATFORMFILEDIALOG_P_H
diff --git a/src/labs/platform/qquicklabsplatformfolderdialog_p.h b/src/labs/platform/qquicklabsplatformfolderdialog_p.h
index f05749a6e4..334b0f6479 100644
--- a/src/labs/platform/qquicklabsplatformfolderdialog_p.h
+++ b/src/labs/platform/qquicklabsplatformfolderdialog_p.h
@@ -73,6 +73,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformFolderDialog)
-
#endif // QQUICKLABSPLATFORMFOLDERDIALOG_P_H
diff --git a/src/labs/platform/qquicklabsplatformfontdialog_p.h b/src/labs/platform/qquicklabsplatformfontdialog_p.h
index 31aed5928f..8f7d07091d 100644
--- a/src/labs/platform/qquicklabsplatformfontdialog_p.h
+++ b/src/labs/platform/qquicklabsplatformfontdialog_p.h
@@ -61,6 +61,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformFontDialog)
-
#endif // QQUICKLABSPLATFORMFONTDIALOG_P_H
diff --git a/src/labs/platform/qquicklabsplatformicon_p.h b/src/labs/platform/qquicklabsplatformicon_p.h
index 5c83940eef..efd178a75b 100644
--- a/src/labs/platform/qquicklabsplatformicon_p.h
+++ b/src/labs/platform/qquicklabsplatformicon_p.h
@@ -29,9 +29,9 @@ class QQuickLabsPlatformIcon
{
Q_GADGET
QML_ANONYMOUS
- Q_PROPERTY(QUrl source READ source WRITE setSource)
- Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(bool mask READ isMask WRITE setMask)
+ Q_PROPERTY(QUrl source READ source WRITE setSource FINAL)
+ Q_PROPERTY(QString name READ name WRITE setName FINAL)
+ Q_PROPERTY(bool mask READ isMask WRITE setMask FINAL)
public:
QUrl source() const;
diff --git a/src/labs/platform/qquicklabsplatformiconloader_p.h b/src/labs/platform/qquicklabsplatformiconloader_p.h
index 20914afe85..0fb406df4f 100644
--- a/src/labs/platform/qquicklabsplatformiconloader_p.h
+++ b/src/labs/platform/qquicklabsplatformiconloader_p.h
@@ -18,7 +18,7 @@
#include <QtCore/qurl.h>
#include <QtCore/qstring.h>
#include <QtGui/qicon.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include "qquicklabsplatformicon_p.h"
diff --git a/src/labs/platform/qquicklabsplatformmenu.cpp b/src/labs/platform/qquicklabsplatformmenu.cpp
index 60a7843001..22dddd7040 100644
--- a/src/labs/platform/qquicklabsplatformmenu.cpp
+++ b/src/labs/platform/qquicklabsplatformmenu.cpp
@@ -321,6 +321,7 @@ void QQuickLabsPlatformMenu::setParentMenu(QQuickLabsPlatformMenu *menu)
emit parentMenuChanged();
}
+#if QT_CONFIG(systemtrayicon)
/*!
\readonly
\qmlproperty SystemTrayIcon Qt.labs.platform::Menu::systemTrayIcon
@@ -342,6 +343,7 @@ void QQuickLabsPlatformMenu::setSystemTrayIcon(QQuickLabsPlatformSystemTrayIcon
destroy();
emit systemTrayIconChanged();
}
+#endif
/*!
\readonly
@@ -648,7 +650,7 @@ void QQuickLabsPlatformMenu::clear()
Opens the menu at the specified \a target item, optionally aligned to a menu \a item.
*/
-void QQuickLabsPlatformMenu::open(QQmlV4Function *args)
+void QQuickLabsPlatformMenu::open(QQmlV4FunctionPtr args)
{
if (!m_handle)
return;
diff --git a/src/labs/platform/qquicklabsplatformmenu_p.h b/src/labs/platform/qquicklabsplatformmenu_p.h
index 14672713bd..21fe2a286f 100644
--- a/src/labs/platform/qquicklabsplatformmenu_p.h
+++ b/src/labs/platform/qquicklabsplatformmenu_p.h
@@ -31,7 +31,6 @@ class QIcon;
class QWindow;
class QQuickItem;
class QPlatformMenu;
-class QQmlV4Function;
class QQuickLabsPlatformMenuBar;
class QQuickLabsPlatformMenuItem;
class QQuickLabsPlatformIconLoader;
@@ -47,7 +46,9 @@ class QQuickLabsPlatformMenu : public QObject, public QQmlParserStatus
Q_PROPERTY(QQmlListProperty<QQuickLabsPlatformMenuItem> items READ items NOTIFY itemsChanged FINAL)
Q_PROPERTY(QQuickLabsPlatformMenuBar *menuBar READ menuBar NOTIFY menuBarChanged FINAL)
Q_PROPERTY(QQuickLabsPlatformMenu *parentMenu READ parentMenu NOTIFY parentMenuChanged FINAL)
+#if QT_CONFIG(systemtrayicon)
Q_PROPERTY(QQuickLabsPlatformSystemTrayIcon *systemTrayIcon READ systemTrayIcon NOTIFY systemTrayIconChanged FINAL)
+#endif
Q_PROPERTY(QQuickLabsPlatformMenuItem *menuItem READ menuItem CONSTANT FINAL)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged FINAL)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL)
@@ -76,8 +77,10 @@ public:
QQuickLabsPlatformMenu *parentMenu() const;
void setParentMenu(QQuickLabsPlatformMenu *menu);
+#if QT_CONFIG(systemtrayicon)
QQuickLabsPlatformSystemTrayIcon *systemTrayIcon() const;
void setSystemTrayIcon(QQuickLabsPlatformSystemTrayIcon *icon);
+#endif
QQuickLabsPlatformMenuItem *menuItem() const;
@@ -113,7 +116,7 @@ public:
Q_INVOKABLE void clear();
public Q_SLOTS:
- void open(QQmlV4Function *args);
+ void open(QQmlV4FunctionPtr args);
void close();
Q_SIGNALS:
@@ -175,6 +178,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformMenu)
-
#endif // QQUICKLABSPLATFORMMENU_P_H
diff --git a/src/labs/platform/qquicklabsplatformmenubar_p.h b/src/labs/platform/qquicklabsplatformmenubar_p.h
index 28a3fe5652..2f08f4eb27 100644
--- a/src/labs/platform/qquicklabsplatformmenubar_p.h
+++ b/src/labs/platform/qquicklabsplatformmenubar_p.h
@@ -84,6 +84,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformMenuBar)
-
#endif // QQUICKLABSPLATFORMMENUBAR_P_H
diff --git a/src/labs/platform/qquicklabsplatformmenuitem.cpp b/src/labs/platform/qquicklabsplatformmenuitem.cpp
index 31e91add36..ab45831b39 100644
--- a/src/labs/platform/qquicklabsplatformmenuitem.cpp
+++ b/src/labs/platform/qquicklabsplatformmenuitem.cpp
@@ -152,6 +152,8 @@ void QQuickLabsPlatformMenuItem::sync()
QKeySequence sequence;
if (m_shortcut.metaType().id() == QMetaType::Int)
sequence = QKeySequence(static_cast<QKeySequence::StandardKey>(m_shortcut.toInt()));
+ else if (m_shortcut.metaType().id() == QMetaType::QKeySequence)
+ sequence = m_shortcut.value<QKeySequence>();
else
sequence = QKeySequence::fromString(m_shortcut.toString());
m_handle->setShortcut(sequence.toString());
@@ -586,6 +588,8 @@ void QQuickLabsPlatformMenuItem::addShortcut()
QKeySequence sequence;
if (m_shortcut.metaType().id() == QMetaType::Int)
sequence = QKeySequence(static_cast<QKeySequence::StandardKey>(m_shortcut.toInt()));
+ else if (m_shortcut.metaType().id() == QMetaType::QKeySequence)
+ sequence = m_shortcut.value<QKeySequence>();
else
sequence = QKeySequence::fromString(m_shortcut.toString());
if (!sequence.isEmpty() && m_enabled) {
@@ -606,6 +610,8 @@ void QQuickLabsPlatformMenuItem::removeShortcut()
QKeySequence sequence;
if (m_shortcut.metaType().id() == QMetaType::Int)
sequence = QKeySequence(static_cast<QKeySequence::StandardKey>(m_shortcut.toInt()));
+ else if (m_shortcut.metaType().id() == QMetaType::QKeySequence)
+ sequence = m_shortcut.value<QKeySequence>();
else
sequence = QKeySequence::fromString(m_shortcut.toString());
QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(m_shortcutId, this, sequence);
diff --git a/src/labs/platform/qquicklabsplatformmenuitem_p.h b/src/labs/platform/qquicklabsplatformmenuitem_p.h
index f02f2536fd..62c4c8a959 100644
--- a/src/labs/platform/qquicklabsplatformmenuitem_p.h
+++ b/src/labs/platform/qquicklabsplatformmenuitem_p.h
@@ -157,6 +157,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformMenuItem)
-
#endif // QQUICKLABSPLATFORMMENUITEM_P_H
diff --git a/src/labs/platform/qquicklabsplatformmenuitemgroup_p.h b/src/labs/platform/qquicklabsplatformmenuitemgroup_p.h
index 531eecb5d3..f4f1cc6ae3 100644
--- a/src/labs/platform/qquicklabsplatformmenuitemgroup_p.h
+++ b/src/labs/platform/qquicklabsplatformmenuitemgroup_p.h
@@ -87,6 +87,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformMenuItemGroup)
-
#endif // QQUICKLABSPLATFORMMENUITEMGROUP_P_H
diff --git a/src/labs/platform/qquicklabsplatformmenuseparator_p.h b/src/labs/platform/qquicklabsplatformmenuseparator_p.h
index cc8c518b0a..364c592c20 100644
--- a/src/labs/platform/qquicklabsplatformmenuseparator_p.h
+++ b/src/labs/platform/qquicklabsplatformmenuseparator_p.h
@@ -29,6 +29,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformMenuSeparator)
-
#endif // QQUICKLABSPLATFORMMENUSEPARATOR_P_H
diff --git a/src/labs/platform/qquicklabsplatformmessagedialog_p.h b/src/labs/platform/qquicklabsplatformmessagedialog_p.h
index 9426448a3e..8b42c78f34 100644
--- a/src/labs/platform/qquicklabsplatformmessagedialog_p.h
+++ b/src/labs/platform/qquicklabsplatformmessagedialog_p.h
@@ -84,6 +84,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformMessageDialog)
-
#endif // QQUICKLABSPLATFORMMESSAGEDIALOG_P_H
diff --git a/src/labs/platform/qquicklabsplatformstandardpaths_p.h b/src/labs/platform/qquicklabsplatformstandardpaths_p.h
index 9941c3081e..80f4995129 100644
--- a/src/labs/platform/qquicklabsplatformstandardpaths_p.h
+++ b/src/labs/platform/qquicklabsplatformstandardpaths_p.h
@@ -54,10 +54,6 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformStandardPaths)
-Q_DECLARE_METATYPE(QStandardPaths::StandardLocation)
-Q_DECLARE_METATYPE(QStandardPaths::LocateOptions)
-
#endif // QT_DEPRECATED_SINCE(6, 4)
#endif // QQUICKLABSPLATFORMSTANDARDPATHS_P_H
diff --git a/src/labs/platform/qquicklabsplatformsystemtrayicon.cpp b/src/labs/platform/qquicklabsplatformsystemtrayicon.cpp
index fa430a69e6..dee37a50f7 100644
--- a/src/labs/platform/qquicklabsplatformsystemtrayicon.cpp
+++ b/src/labs/platform/qquicklabsplatformsystemtrayicon.cpp
@@ -258,10 +258,15 @@ void QQuickLabsPlatformSystemTrayIcon::setMenu(QQuickLabsPlatformMenu *menu)
if (m_menu)
m_menu->setSystemTrayIcon(nullptr);
- if (menu) {
+
+ if (menu)
menu->setSystemTrayIcon(this);
- if (m_handle && m_complete && menu->create())
+
+ if (m_handle && m_complete) {
+ if (menu && menu->create())
m_handle->updateMenu(menu->handle());
+ else
+ m_handle->updateMenu(nullptr);
}
m_menu = menu;
diff --git a/src/labs/platform/qquicklabsplatformsystemtrayicon_p.h b/src/labs/platform/qquicklabsplatformsystemtrayicon_p.h
index 4adac1a8f7..f5c46aa53e 100644
--- a/src/labs/platform/qquicklabsplatformsystemtrayicon_p.h
+++ b/src/labs/platform/qquicklabsplatformsystemtrayicon_p.h
@@ -106,8 +106,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabsPlatformSystemTrayIcon)
-Q_DECLARE_METATYPE(QPlatformSystemTrayIcon::ActivationReason)
-Q_DECLARE_METATYPE(QPlatformSystemTrayIcon::MessageIcon)
-
#endif // QQUICKLABSPLATFORMSYSTEMTRAYICON_P_H
diff --git a/src/labs/platform/widgets/qwidgetplatformfiledialog.cpp b/src/labs/platform/widgets/qwidgetplatformfiledialog.cpp
index 738982b5c6..4a74440424 100644
--- a/src/labs/platform/widgets/qwidgetplatformfiledialog.cpp
+++ b/src/labs/platform/widgets/qwidgetplatformfiledialog.cpp
@@ -16,17 +16,17 @@ QWidgetPlatformFileDialog::QWidgetPlatformFileDialog(QObject *parent)
connect(m_dialog.data(), &QDialog::accepted, this, &QPlatformDialogHelper::accept);
connect(m_dialog.data(), &QDialog::rejected, this, &QPlatformDialogHelper::reject);
- connect(m_dialog.data(), &QFileDialog::fileSelected, [this](const QString &file) {
+ connect(m_dialog.data(), &QFileDialog::fileSelected, this, [this](const QString &file) {
emit fileSelected(QUrl::fromLocalFile(file));
});
- connect(m_dialog.data(), &QFileDialog::filesSelected, [this](const QList<QString> &files) {
+ connect(m_dialog.data(), &QFileDialog::filesSelected, this, [this](const QList<QString> &files) {
QList<QUrl> urls;
urls.reserve(files.size());
for (const QString &file : files)
urls += QUrl::fromLocalFile(file);
emit filesSelected(urls);
});
- connect(m_dialog.data(), &QFileDialog::currentChanged, [this](const QString &path) {
+ connect(m_dialog.data(), &QFileDialog::currentChanged, this, [this](const QString &path) {
emit currentChanged(QUrl::fromLocalFile(path));
});
connect(m_dialog.data(), &QFileDialog::directoryEntered, this, &QWidgetPlatformFileDialog::directoryEntered);
diff --git a/src/labs/platform/widgets/qwidgetplatformmessagedialog.cpp b/src/labs/platform/widgets/qwidgetplatformmessagedialog.cpp
index a9cc0de58b..1bb0e219c8 100644
--- a/src/labs/platform/widgets/qwidgetplatformmessagedialog.cpp
+++ b/src/labs/platform/widgets/qwidgetplatformmessagedialog.cpp
@@ -16,7 +16,7 @@ QWidgetPlatformMessageDialog::QWidgetPlatformMessageDialog(QObject *parent)
connect(m_dialog.data(), &QMessageBox::accepted, this, &QPlatformDialogHelper::accept);
connect(m_dialog.data(), &QMessageBox::rejected, this, &QPlatformDialogHelper::reject);
- connect(m_dialog.data(), &QMessageBox::buttonClicked, [this](QAbstractButton *button) {
+ connect(m_dialog.data(), &QMessageBox::buttonClicked, this, [this](QAbstractButton *button) {
QMessageBox::ButtonRole role = m_dialog->buttonRole(button);
QMessageBox::StandardButton standardButton = m_dialog->standardButton(button);
emit clicked(static_cast<StandardButton>(standardButton), static_cast<ButtonRole>(role));
diff --git a/src/labs/platform/widgets/qwidgetplatformsystemtrayicon.cpp b/src/labs/platform/widgets/qwidgetplatformsystemtrayicon.cpp
index 8ffe4945a0..bb61d58433 100644
--- a/src/labs/platform/widgets/qwidgetplatformsystemtrayicon.cpp
+++ b/src/labs/platform/widgets/qwidgetplatformsystemtrayicon.cpp
@@ -14,7 +14,7 @@ QWidgetPlatformSystemTrayIcon::QWidgetPlatformSystemTrayIcon(QObject *parent)
setParent(parent);
connect(m_systray.data(), &QSystemTrayIcon::messageClicked, this, &QPlatformSystemTrayIcon::messageClicked);
- connect(m_systray.data(), &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) {
+ connect(m_systray.data(), &QSystemTrayIcon::activated, this, [this](QSystemTrayIcon::ActivationReason reason) {
emit activated(static_cast<ActivationReason>(reason));
});
}
diff --git a/src/labs/settings/CMakeLists.txt b/src/labs/settings/CMakeLists.txt
index 3e703c9146..d9d53fab24 100644
--- a/src/labs/settings/CMakeLists.txt
+++ b/src/labs/settings/CMakeLists.txt
@@ -15,5 +15,4 @@ qt_internal_add_qml_module(LabsSettings
Qt::Core
Qt::Qml
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
diff --git a/src/labs/settings/qqmlsettings.cpp b/src/labs/settings/qqmlsettings.cpp
index fe0fa831d2..19ebdaf3c2 100644
--- a/src/labs/settings/qqmlsettings.cpp
+++ b/src/labs/settings/qqmlsettings.cpp
@@ -18,7 +18,7 @@ QT_BEGIN_NAMESPACE
\qmlmodule Qt.labs.settings 1.0
\title Qt Labs Settings QML Types
\ingroup qmlmodules
- \deprecated [6.5] Use \l {QtQmlCore::}{Settings} from Qt QML Core instead.
+ \deprecated [6.5] Use \l [QML] {QtCore::}{Settings} from Qt QML Core instead.
\brief Provides persistent platform-independent application settings.
To use this module, import the module with the following line:
@@ -33,7 +33,7 @@ QT_BEGIN_NAMESPACE
//! \instantiates QQmlSettings
\inqmlmodule Qt.labs.settings
\ingroup settings
- \deprecated [6.5] Use \l {QtQmlCore::}{Settings} from Qt QML Core instead.
+ \deprecated [6.5] Use \l [QML] {QtCore::}{Settings} from Qt QML Core instead.
\brief Provides persistent platform-independent application settings.
The Settings type provides persistent platform-independent application settings.
@@ -198,7 +198,7 @@ QT_BEGIN_NAMESPACE
standard, INI text files are used. See \l QSettings documentation for
more details.
- \sa {QtQmlCore::}{Settings}, QSettings
+ \sa {QtCore::}{Settings}, QSettings
*/
Q_LOGGING_CATEGORY(lcSettings, "qt.labs.settings")
diff --git a/src/labs/settings/qqmlsettings_p.h b/src/labs/settings/qqmlsettings_p.h
index 895df8c5e3..a9de5e3077 100644
--- a/src/labs/settings/qqmlsettings_p.h
+++ b/src/labs/settings/qqmlsettings_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQmlSettingsPrivate;
-class Q_LABSSETTINGS_PRIVATE_EXPORT QQmlSettings : public QObject, public QQmlParserStatus
+class Q_LABSSETTINGS_EXPORT QQmlSettings : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
@@ -64,6 +64,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlSettings)
-
#endif // QQMLSETTINGS_P_H
diff --git a/src/labs/settings/qqmlsettingsglobal_p.h b/src/labs/settings/qqmlsettingsglobal_p.h
index f25d771218..856821f48b 100644
--- a/src/labs/settings/qqmlsettingsglobal_p.h
+++ b/src/labs/settings/qqmlsettingsglobal_p.h
@@ -17,6 +17,6 @@
// We mean it.
//
-#include <QtLabsSettings/private/qtlabssettingsexports_p.h>
+#include <QtLabsSettings/qtlabssettingsexports.h>
#endif // QTLABSSETTINGSGLOBAL_P_H
diff --git a/src/labs/sharedimage/CMakeLists.txt b/src/labs/sharedimage/CMakeLists.txt
index 65f31ac016..8cbe5b62cc 100644
--- a/src/labs/sharedimage/CMakeLists.txt
+++ b/src/labs/sharedimage/CMakeLists.txt
@@ -20,8 +20,7 @@ qt_internal_add_qml_module(LabsSharedImage
Qt::GuiPrivate
Qt::QuickPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
# We need to do additional initialization, so we have to provide our own
# plugin class rather than using the generated one
diff --git a/src/labs/sharedimage/qsharedimageloader.cpp b/src/labs/sharedimage/qsharedimageloader.cpp
index f1313b5c0e..f3c17f9ebb 100644
--- a/src/labs/sharedimage/qsharedimageloader.cpp
+++ b/src/labs/sharedimage/qsharedimageloader.cpp
@@ -4,6 +4,8 @@
#include "qsharedimageloader_p.h"
#include <private/qobject_p.h>
#include <private/qimage_p.h>
+
+#include <QtCore/qpointer.h>
#include <QSharedMemory>
#include <memory>
diff --git a/src/labs/sharedimage/qsharedimageloader_p.h b/src/labs/sharedimage/qsharedimageloader_p.h
index 09f96d2ee1..ca59d95585 100644
--- a/src/labs/sharedimage/qsharedimageloader_p.h
+++ b/src/labs/sharedimage/qsharedimageloader_p.h
@@ -28,7 +28,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcSharedImage);
class QSharedImageLoaderPrivate;
-class Q_LABSSHAREDIMAGE_PRIVATE_EXPORT QSharedImageLoader : public QObject
+class Q_LABSSHAREDIMAGE_EXPORT QSharedImageLoader : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QSharedImageLoader)
diff --git a/src/labs/sharedimage/qsharedimageprovider.cpp b/src/labs/sharedimage/qsharedimageprovider.cpp
index 69f0013b06..33f72447e3 100644
--- a/src/labs/sharedimage/qsharedimageprovider.cpp
+++ b/src/labs/sharedimage/qsharedimageprovider.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qsharedimageprovider_p.h>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qimage_p.h>
#include <QImageReader>
#include <QFileInfo>
diff --git a/src/labs/sharedimage/qsharedimageprovider_p.h b/src/labs/sharedimage/qsharedimageprovider_p.h
index ee81866072..1888f726fd 100644
--- a/src/labs/sharedimage/qsharedimageprovider_p.h
+++ b/src/labs/sharedimage/qsharedimageprovider_p.h
@@ -18,7 +18,7 @@
#include "qtlabssharedimageglobal_p.h"
#include <QQuickImageProvider>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <QScopedPointer>
#include "qsharedimageloader_p.h"
@@ -46,7 +46,7 @@ protected:
QString key(const QString &path, ImageParameters *params) override;
};
-class Q_LABSSHAREDIMAGE_PRIVATE_EXPORT SharedImageProvider : public QQuickImageProviderWithOptions
+class Q_LABSSHAREDIMAGE_EXPORT SharedImageProvider : public QQuickImageProviderWithOptions
{
public:
SharedImageProvider();
diff --git a/src/labs/sharedimage/qtlabssharedimageglobal_p.h b/src/labs/sharedimage/qtlabssharedimageglobal_p.h
index 75b41771df..1045e9f729 100644
--- a/src/labs/sharedimage/qtlabssharedimageglobal_p.h
+++ b/src/labs/sharedimage/qtlabssharedimageglobal_p.h
@@ -17,11 +17,11 @@
// We mean it.
//
-#include <QtLabsSharedImage/private/qtlabssharedimageexports_p.h>
+#include <QtLabsSharedImage/qtlabssharedimageexports.h>
QT_BEGIN_NAMESPACE
-void Q_LABSSHAREDIMAGE_PRIVATE_EXPORT qml_register_types_Qt_labs_sharedimage();
+void Q_LABSSHAREDIMAGE_EXPORT qml_register_types_Qt_labs_sharedimage();
QT_END_NAMESPACE
diff --git a/src/labs/wavefrontmesh/CMakeLists.txt b/src/labs/wavefrontmesh/CMakeLists.txt
index 1c99d35cac..eb68fa2947 100644
--- a/src/labs/wavefrontmesh/CMakeLists.txt
+++ b/src/labs/wavefrontmesh/CMakeLists.txt
@@ -18,5 +18,4 @@ qt_internal_add_qml_module(LabsWavefrontMesh
Qt::GuiPrivate
Qt::QuickPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
diff --git a/src/labs/wavefrontmesh/qqmlwavefrontmeshglobal_p.h b/src/labs/wavefrontmesh/qqmlwavefrontmeshglobal_p.h
index b90d1941d6..ccf5a681f1 100644
--- a/src/labs/wavefrontmesh/qqmlwavefrontmeshglobal_p.h
+++ b/src/labs/wavefrontmesh/qqmlwavefrontmeshglobal_p.h
@@ -17,6 +17,6 @@
// We mean it.
//
-#include <QtLabsWavefrontMesh/private/qtlabswavefrontmeshexports_p.h>
+#include <QtLabsWavefrontMesh/qtlabswavefrontmeshexports.h>
#endif // QTLABSWAVEFRONTMESHGLOBAL_P_H
diff --git a/src/labs/wavefrontmesh/qwavefrontmesh_p.h b/src/labs/wavefrontmesh/qwavefrontmesh_p.h
index 31ed4a64ff..b68f6578eb 100644
--- a/src/labs/wavefrontmesh/qwavefrontmesh_p.h
+++ b/src/labs/wavefrontmesh/qwavefrontmesh_p.h
@@ -25,13 +25,13 @@
QT_BEGIN_NAMESPACE
class QWavefrontMeshPrivate;
-class Q_LABSWAVEFRONTMESH_PRIVATE_EXPORT QWavefrontMesh : public QQuickShaderEffectMesh
+class Q_LABSWAVEFRONTMESH_EXPORT QWavefrontMesh : public QQuickShaderEffectMesh
{
Q_OBJECT
- Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
- Q_PROPERTY(Error lastError READ lastError NOTIFY lastErrorChanged)
- Q_PROPERTY(QVector3D projectionPlaneV READ projectionPlaneV WRITE setProjectionPlaneV NOTIFY projectionPlaneVChanged)
- Q_PROPERTY(QVector3D projectionPlaneW READ projectionPlaneW WRITE setProjectionPlaneW NOTIFY projectionPlaneWChanged)
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged FINAL)
+ Q_PROPERTY(Error lastError READ lastError NOTIFY lastErrorChanged FINAL)
+ Q_PROPERTY(QVector3D projectionPlaneV READ projectionPlaneV WRITE setProjectionPlaneV NOTIFY projectionPlaneVChanged FINAL)
+ Q_PROPERTY(QVector3D projectionPlaneW READ projectionPlaneW WRITE setProjectionPlaneW NOTIFY projectionPlaneWChanged FINAL)
QML_NAMED_ELEMENT(WavefrontMesh)
QML_ADDED_IN_VERSION(1, 0)
diff --git a/src/particles/CMakeLists.txt b/src/particles/CMakeLists.txt
index 8c3e70f91b..9746349d81 100644
--- a/src/particles/CMakeLists.txt
+++ b/src/particles/CMakeLists.txt
@@ -43,6 +43,18 @@ qt_internal_add_qml_module(QuickParticlesPrivate
qquickv4particledata.cpp qquickv4particledata_p.h
qquickwander.cpp qquickwander_p.h
qtquickparticlesglobal_p.h
+ NO_PCH_SOURCES
+ # these undef QT_NO_FOREACH
+ qquickcumulativedirection.cpp
+ qquickimageparticle.cpp
+ qquickitemparticle.cpp
+ qquickparticleaffector.cpp
+ qquickparticleemitter.cpp
+ qquickparticlegroup.cpp
+ qquickparticlepainter.cpp
+ qquicktrailemitter.cpp
+ qquickturbulence.cpp
+ # end undef QT_NO_FOREACH
DEFINES
QT_NO_INTEGER_EVENT_COORDINATES
QT_NO_URL_CAST_FROM_STRING
@@ -52,8 +64,7 @@ qt_internal_add_qml_module(QuickParticlesPrivate
Qt::QmlPrivate
Qt::QuickPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
# Resources:
set(particles_resource_files
@@ -69,26 +80,133 @@ qt_internal_add_resource(QuickParticlesPrivate "particles"
FILES
${particles_resource_files}
)
-set(particles1_resource_files
- "shaders_ng/imageparticle_colored.frag.qsb"
- "shaders_ng/imageparticle_colored.vert.qsb"
- "shaders_ng/imageparticle_coloredpoint.frag.qsb"
- "shaders_ng/imageparticle_coloredpoint.vert.qsb"
- "shaders_ng/imageparticle_deformed.frag.qsb"
- "shaders_ng/imageparticle_deformed.vert.qsb"
- "shaders_ng/imageparticle_simplepoint.frag.qsb"
- "shaders_ng/imageparticle_simplepoint.vert.qsb"
- "shaders_ng/imageparticle_sprite.frag.qsb"
- "shaders_ng/imageparticle_sprite.vert.qsb"
- "shaders_ng/imageparticle_tabled.frag.qsb"
- "shaders_ng/imageparticle_tabled.vert.qsb"
+
+qt_internal_add_shaders(QuickParticlesPrivate "particles_shaders1"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ GLSL
+ "150,120,100es,300es"
+ PREFIX
+ "/particles"
+ FILES
+ "shaders_ng/imageparticle.vert"
+ "shaders_ng/imageparticle.frag"
+ OUTPUTS
+ "shaders_ng/imageparticle_simplepoint.vert.qsb"
+ "shaders_ng/imageparticle_simplepoint.frag.qsb"
+ DEFINES
+ POINT
+)
+
+qt_internal_add_shaders(QuickParticlesPrivate "particles_shaders2"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ GLSL
+ "150,120,100es,300es"
+ PREFIX
+ "/particles"
+ FILES
+ "shaders_ng/imageparticle.vert"
+ "shaders_ng/imageparticle.frag"
+ OUTPUTS
+ "shaders_ng/imageparticle_coloredpoint.vert.qsb"
+ "shaders_ng/imageparticle_coloredpoint.frag.qsb"
+ DEFINES
+ POINT
+ COLOR
)
-qt_internal_add_resource(QuickParticlesPrivate "particles1"
+qt_internal_add_shaders(QuickParticlesPrivate "particles_shaders3"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ GLSL
+ "150,120,100es,300es"
PREFIX
"/particles"
FILES
- ${particles1_resource_files}
+ "shaders_ng/imageparticle.vert"
+ "shaders_ng/imageparticle.frag"
+ OUTPUTS
+ "shaders_ng/imageparticle_colored.vert.qsb"
+ "shaders_ng/imageparticle_colored.frag.qsb"
+ DEFINES
+ COLOR
+)
+
+qt_internal_add_shaders(QuickParticlesPrivate "particles_shaders4"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ GLSL
+ "150,120,100es,300es"
+ PREFIX
+ "/particles"
+ FILES
+ "shaders_ng/imageparticle.vert"
+ "shaders_ng/imageparticle.frag"
+ OUTPUTS
+ "shaders_ng/imageparticle_deformed.vert.qsb"
+ "shaders_ng/imageparticle_deformed.frag.qsb"
+ DEFINES
+ DEFORM
+ COLOR
+)
+
+qt_internal_add_shaders(QuickParticlesPrivate "particles_shaders5"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ GLSL
+ "150,120,100es,300es"
+ PREFIX
+ "/particles"
+ FILES
+ "shaders_ng/imageparticle.vert"
+ "shaders_ng/imageparticle.frag"
+ OUTPUTS
+ "shaders_ng/imageparticle_tabled.vert.qsb"
+ "shaders_ng/imageparticle_tabled.frag.qsb"
+ DEFINES
+ TABLE
+ DEFORM
+ COLOR
+)
+
+qt_internal_add_shaders(QuickParticlesPrivate "particles_shaders6"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ GLSL
+ "150,120,100es,300es"
+ PREFIX
+ "/particles"
+ FILES
+ "shaders_ng/imageparticle.vert"
+ "shaders_ng/imageparticle.frag"
+ OUTPUTS
+ "shaders_ng/imageparticle_sprite.vert.qsb"
+ "shaders_ng/imageparticle_sprite.frag.qsb"
+ DEFINES
+ SPRITE
+ TABLE
+ DEFORM
+ COLOR
+ ZORDER_LOC
+ 8
)
qt_internal_extend_target(QuickParticlesPrivate CONDITION MSVC
diff --git a/src/particles/qquickage_p.h b/src/particles/qquickage_p.h
index 499d915568..a465e04d53 100644
--- a/src/particles/qquickage_p.h
+++ b/src/particles/qquickage_p.h
@@ -18,7 +18,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickAgeAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickAgeAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(int lifeLeft READ lifeLeft WRITE setLifeLeft NOTIFY lifeLeftChanged)
diff --git a/src/particles/qquickangledirection_p.h b/src/particles/qquickangledirection_p.h
index 17e9fddb3b..f22f78a5e4 100644
--- a/src/particles/qquickangledirection_p.h
+++ b/src/particles/qquickangledirection_p.h
@@ -15,12 +15,12 @@
// We mean it.
//
#include "qquickdirection_p.h"
-#include <QtQuickParticles/private/qtquickparticlesexports_p.h>
+#include <QtQuickParticles/qtquickparticlesexports.h>
#include <QtQml/qqml.h>
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickAngleDirection : public QQuickDirection
+class Q_QUICKPARTICLES_EXPORT QQuickAngleDirection : public QQuickDirection
{
Q_OBJECT
Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged)
diff --git a/src/particles/qquickcumulativedirection.cpp b/src/particles/qquickcumulativedirection.cpp
index cf0d7685c1..a4577efbe4 100644
--- a/src/particles/qquickcumulativedirection.cpp
+++ b/src/particles/qquickcumulativedirection.cpp
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qquickcumulativedirection_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/particles/qquickcumulativedirection_p.h b/src/particles/qquickcumulativedirection_p.h
index 826c0ac1d0..dd871d3db5 100644
--- a/src/particles/qquickcumulativedirection_p.h
+++ b/src/particles/qquickcumulativedirection_p.h
@@ -17,11 +17,11 @@
#include "qquickdirection_p.h"
#include <QQmlListProperty>
#include <QtQml/qqml.h>
-#include <QtQuickParticles/private/qtquickparticlesexports_p.h>
+#include <QtQuickParticles/qtquickparticlesexports.h>
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickCumulativeDirection : public QQuickDirection
+class Q_QUICKPARTICLES_EXPORT QQuickCumulativeDirection : public QQuickDirection
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QQuickDirection> directions READ directions)
diff --git a/src/particles/qquickcustomaffector.cpp b/src/particles/qquickcustomaffector.cpp
index 71ada8a5b9..2ba7b4c536 100644
--- a/src/particles/qquickcustomaffector.cpp
+++ b/src/particles/qquickcustomaffector.cpp
@@ -2,25 +2,27 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickcustomaffector_p.h"
-#include <private/qqmlengine_p.h>
+
+#include <private/qquickv4particledata_p.h>
#include <private/qqmlglobal_p.h>
-#include <private/qjsvalue_p.h>
-#include <QQmlEngine>
-#include <QDebug>
+
+#include <QtCore/qdebug.h>
+
QT_BEGIN_NAMESPACE
//TODO: Move docs (and inheritence) to real base when docs can propagate. Currently this pretends to be the base class!
/*!
\qmlsignal QtQuick.Particles::Affector::affectParticles(Array particles, real dt)
- This signal is emitted when particles are selected to be affected. particles contains
- an array of particle objects which can be directly manipulated.
+ This signal is emitted when particles are selected to be affected.
+ \a particles is an array of particle objects which can be directly
+ manipulated.
- dt is the time since the last time it was affected. Use dt to normalize
- trajectory manipulations to real time.
+ \a dt is the time since the last time it was affected. Use \a dt to
+ normalize trajectory manipulations to real time.
- Note that JavaScript is slower to execute, so it is not recommended to use this in
- high-volume particle systems.
+ \note JavaScript is slower to execute, so it is not recommended to use
+ this in high-volume particle systems.
*/
/*!
@@ -65,7 +67,9 @@ QQuickCustomAffector::QQuickCustomAffector(QQuickItem *parent) :
bool QQuickCustomAffector::isAffectConnected()
{
- IS_SIGNAL_CONNECTED(this, QQuickCustomAffector, affectParticles, (const QJSValue &, qreal));
+ IS_SIGNAL_CONNECTED(
+ this, QQuickCustomAffector, affectParticles,
+ (const QList<QQuickV4ParticleData> &, qreal));
}
void QQuickCustomAffector::affectSystem(qreal dt)
@@ -109,19 +113,13 @@ void QQuickCustomAffector::affectSystem(qreal dt)
if (m_onceOff)
dt = 1.0;
- QQmlEngine *qmlEngine = ::qmlEngine(this);
- QV4::ExecutionEngine *v4 = qmlEngine->handle();
-
- QV4::Scope scope(v4);
- QV4::ScopedArrayObject array(scope, v4->newArrayObject(toAffect.size()));
- QV4::ScopedValue v(scope);
- for (int i=0; i<toAffect.size(); i++)
- array->put(i, (v = toAffect[i]->v4Value(m_system)));
+ QList<QQuickV4ParticleData> particles;
+ particles.reserve(toAffect.size());
+ for (QQuickParticleData *data: std::as_const(toAffect))
+ particles.push_back(data->v4Value(m_system));
const auto doAffect = [&](qreal dt) {
affectProperties(toAffect, dt);
- QJSValue particles;
- QJSValuePrivate::setValue(&particles, array);
emit affectParticles(particles, dt);
};
diff --git a/src/particles/qquickcustomaffector_p.h b/src/particles/qquickcustomaffector_p.h
index ae97995265..1280e2b4dd 100644
--- a/src/particles/qquickcustomaffector_p.h
+++ b/src/particles/qquickcustomaffector_p.h
@@ -24,7 +24,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickCustomAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickCustomAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(bool relative READ relative WRITE setRelative NOTIFY relativeChanged)
@@ -75,7 +75,7 @@ public:
Q_SIGNALS:
- void affectParticles(const QJSValue &particles, qreal dt);
+ void affectParticles(const QList<QQuickV4ParticleData> &particles, qreal dt);
void positionChanged(QQuickDirection * arg);
diff --git a/src/particles/qquickdirection_p.h b/src/particles/qquickdirection_p.h
index 534d1a6de2..0d78ca3c1a 100644
--- a/src/particles/qquickdirection_p.h
+++ b/src/particles/qquickdirection_p.h
@@ -19,11 +19,11 @@
#include <QPointF>
#include <QtQml/qqml.h>
#include <QtCore/private/qglobal_p.h>
-#include <QtQuickParticles/private/qtquickparticlesexports_p.h>
+#include <QtQuickParticles/qtquickparticlesexports.h>
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickDirection : public QObject
+class Q_QUICKPARTICLES_EXPORT QQuickDirection : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(NullVector)
diff --git a/src/particles/qquickellipseextruder_p.h b/src/particles/qquickellipseextruder_p.h
index c46b5faaad..f3b38e8bef 100644
--- a/src/particles/qquickellipseextruder_p.h
+++ b/src/particles/qquickellipseextruder_p.h
@@ -18,7 +18,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickEllipseExtruder : public QQuickParticleExtruder
+class Q_QUICKPARTICLES_EXPORT QQuickEllipseExtruder : public QQuickParticleExtruder
{
Q_OBJECT
Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Use base class? If it's still box
diff --git a/src/particles/qquickfriction_p.h b/src/particles/qquickfriction_p.h
index 3f0abffe68..4d51888de8 100644
--- a/src/particles/qquickfriction_p.h
+++ b/src/particles/qquickfriction_p.h
@@ -18,7 +18,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickFrictionAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickFrictionAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged)
diff --git a/src/particles/qquickgravity_p.h b/src/particles/qquickgravity_p.h
index 4793a9eb5b..81e0ebf9c9 100644
--- a/src/particles/qquickgravity_p.h
+++ b/src/particles/qquickgravity_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickGravityAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickGravityAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged)
diff --git a/src/particles/qquickgroupgoal_p.h b/src/particles/qquickgroupgoal_p.h
index 869ec3a339..dedf9a47fb 100644
--- a/src/particles/qquickgroupgoal_p.h
+++ b/src/particles/qquickgroupgoal_p.h
@@ -20,7 +20,7 @@ QT_BEGIN_NAMESPACE
class QQuickStochasticEngine;
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickGroupGoalAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickGroupGoalAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged)
diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp
index 07076c6d29..855726c20a 100644
--- a/src/particles/qquickimageparticle.cpp
+++ b/src/particles/qquickimageparticle.cpp
@@ -1,6 +1,8 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <private/qquickitem_p.h>
@@ -17,7 +19,7 @@
#include <private/qqmlglobal_p.h>
#include <QtQml/qqmlinfo.h>
#include <QtCore/QtMath>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <cmath>
@@ -52,41 +54,45 @@ class ImageMaterialData
class TabledMaterialRhiShader : public QSGMaterialShader
{
public:
- TabledMaterialRhiShader()
+ TabledMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_tabled.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_tabled.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_tabled.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_tabled.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
{
QByteArray *buf = renderState.uniformData();
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
- if (renderState.isMatrixDirty()) {
- const QMatrix4x4 m = renderState.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ }
}
if (renderState.isOpacityDirty()) {
const float opacity = renderState.opacity();
- memcpy(buf->data() + 64, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
}
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
float entry = float(state->entry);
- memcpy(buf->data() + 68, &entry, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
float timestamp = float(state->timestamp);
- memcpy(buf->data() + 72, &timestamp, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &timestamp, 4);
- float *p = reinterpret_cast<float *>(buf->data() + 80);
+ float *p = reinterpret_cast<float *>(buf->data() + 64 * shaderMatrixCount + 16);
for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
*p = state->sizeTable[i];
p += 4;
}
- p = reinterpret_cast<float *>(buf->data() + 80 + (UNIFORM_ARRAY_SIZE * 4 * 4));
+ p = reinterpret_cast<float *>(buf->data() + 64 * shaderMatrixCount + 16 + (UNIFORM_ARRAY_SIZE * 4 * 4));
for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
*p = state->opacityTable[i];
p += 4;
@@ -114,7 +120,7 @@ class TabledMaterial : public ImageMaterial
public:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
Q_UNUSED(renderMode);
- return new TabledMaterialRhiShader;
+ return new TabledMaterialRhiShader(viewCount());
}
QSGMaterialType *type() const override { return &m_type; }
@@ -130,34 +136,38 @@ QSGMaterialType TabledMaterial::m_type;
class DeformableMaterialRhiShader : public QSGMaterialShader
{
public:
- DeformableMaterialRhiShader()
+ DeformableMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_deformed.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_deformed.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_deformed.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_deformed.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
{
QByteArray *buf = renderState.uniformData();
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
- if (renderState.isMatrixDirty()) {
- const QMatrix4x4 m = renderState.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ }
}
if (renderState.isOpacityDirty()) {
const float opacity = renderState.opacity();
- memcpy(buf->data() + 64, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
}
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
float entry = float(state->entry);
- memcpy(buf->data() + 68, &entry, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
float timestamp = float(state->timestamp);
- memcpy(buf->data() + 72, &timestamp, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &timestamp, 4);
return true;
}
@@ -178,7 +188,7 @@ class DeformableMaterial : public ImageMaterial
public:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
Q_UNUSED(renderMode);
- return new DeformableMaterialRhiShader;
+ return new DeformableMaterialRhiShader(viewCount());
}
QSGMaterialType *type() const override { return &m_type; }
@@ -194,41 +204,45 @@ QSGMaterialType DeformableMaterial::m_type;
class ParticleSpriteMaterialRhiShader : public QSGMaterialShader
{
public:
- ParticleSpriteMaterialRhiShader()
+ ParticleSpriteMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_sprite.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_sprite.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_sprite.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_sprite.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
{
QByteArray *buf = renderState.uniformData();
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
- if (renderState.isMatrixDirty()) {
- const QMatrix4x4 m = renderState.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ }
}
if (renderState.isOpacityDirty()) {
const float opacity = renderState.opacity();
- memcpy(buf->data() + 64, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
}
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
float entry = float(state->entry);
- memcpy(buf->data() + 68, &entry, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
float timestamp = float(state->timestamp);
- memcpy(buf->data() + 72, &timestamp, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &timestamp, 4);
- float *p = reinterpret_cast<float *>(buf->data() + 80);
+ float *p = reinterpret_cast<float *>(buf->data() + 64 * shaderMatrixCount + 16);
for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
*p = state->sizeTable[i];
p += 4;
}
- p = reinterpret_cast<float *>(buf->data() + 80 + (UNIFORM_ARRAY_SIZE * 4 * 4));
+ p = reinterpret_cast<float *>(buf->data() + 64 * shaderMatrixCount + 16 + (UNIFORM_ARRAY_SIZE * 4 * 4));
for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
*p = state->opacityTable[i];
p += 4;
@@ -256,7 +270,7 @@ class SpriteMaterial : public ImageMaterial
public:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
Q_UNUSED(renderMode);
- return new ParticleSpriteMaterialRhiShader;
+ return new ParticleSpriteMaterialRhiShader(viewCount());
}
QSGMaterialType *type() const override { return &m_type; }
@@ -272,37 +286,41 @@ QSGMaterialType SpriteMaterial::m_type;
class ColoredPointMaterialRhiShader : public QSGMaterialShader
{
public:
- ColoredPointMaterialRhiShader()
+ ColoredPointMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_coloredpoint.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_coloredpoint.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_coloredpoint.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_coloredpoint.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
{
QByteArray *buf = renderState.uniformData();
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
- if (renderState.isMatrixDirty()) {
- const QMatrix4x4 m = renderState.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ }
}
if (renderState.isOpacityDirty()) {
const float opacity = renderState.opacity();
- memcpy(buf->data() + 64, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
}
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
float entry = float(state->entry);
- memcpy(buf->data() + 68, &entry, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
float timestamp = float(state->timestamp);
- memcpy(buf->data() + 72, &timestamp, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &timestamp, 4);
float dpr = float(state->dpr);
- memcpy(buf->data() + 76, &dpr, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 12, &dpr, 4);
return true;
}
@@ -323,7 +341,7 @@ class ColoredPointMaterial : public ImageMaterial
public:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
Q_UNUSED(renderMode);
- return new ColoredPointMaterialRhiShader;
+ return new ColoredPointMaterialRhiShader(viewCount());
}
QSGMaterialType *type() const override { return &m_type; }
@@ -339,10 +357,11 @@ QSGMaterialType ColoredPointMaterial::m_type;
class ColoredMaterialRhiShader : public ColoredPointMaterialRhiShader
{
public:
- ColoredMaterialRhiShader()
+ ColoredMaterialRhiShader(int viewCount)
+ : ColoredPointMaterialRhiShader(viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_colored.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_colored.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_colored.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_colored.frag.qsb"), viewCount);
}
};
@@ -351,7 +370,7 @@ class ColoredMaterial : public ImageMaterial
public:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
Q_UNUSED(renderMode);
- return new ColoredMaterialRhiShader;
+ return new ColoredMaterialRhiShader(viewCount());
}
QSGMaterialType *type() const override { return &m_type; }
@@ -367,37 +386,41 @@ QSGMaterialType ColoredMaterial::m_type;
class SimplePointMaterialRhiShader : public QSGMaterialShader
{
public:
- SimplePointMaterialRhiShader()
+ SimplePointMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_simplepoint.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_simplepoint.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_simplepoint.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_simplepoint.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
{
QByteArray *buf = renderState.uniformData();
Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(renderState.projectionMatrixCount(), shaderMatrixCount);
- if (renderState.isMatrixDirty()) {
- const QMatrix4x4 m = renderState.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ }
}
if (renderState.isOpacityDirty()) {
const float opacity = renderState.opacity();
- memcpy(buf->data() + 64, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
}
ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
float entry = float(state->entry);
- memcpy(buf->data() + 68, &entry, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 4, &entry, 4);
float timestamp = float(state->timestamp);
- memcpy(buf->data() + 72, &timestamp, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &timestamp, 4);
float dpr = float(state->dpr);
- memcpy(buf->data() + 76, &dpr, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 12, &dpr, 4);
return true;
}
@@ -418,7 +441,7 @@ class SimplePointMaterial : public ImageMaterial
public:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override {
Q_UNUSED(renderMode);
- return new SimplePointMaterialRhiShader;
+ return new SimplePointMaterialRhiShader(viewCount());
}
QSGMaterialType *type() const override { return &m_type; }
@@ -651,13 +674,12 @@ void fillUniformArrayFromImage(float* array, const QImage& img, int size)
For fine-grained control, see sizeTable and opacityTable.
Acceptable values are
- \list
- \li ImageParticle.None: Particles just appear and disappear.
- \li ImageParticle.Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
- \li ImageParticle.Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
- \endlist
- Default value is Fade.
+ \value ImageParticle.None Particles just appear and disappear.
+ \value ImageParticle.Fade Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
+ \value ImageParticle.Scale Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
+
+ The default value is \c ImageParticle.Fade.
*/
/*!
\qmlproperty bool QtQuick.Particles::ImageParticle::spritesInterpolate
@@ -1051,8 +1073,8 @@ void QQuickImageParticle::createEngine()
delete m_spriteEngine;
if (m_sprites.size()) {
m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
- connect(m_spriteEngine, SIGNAL(stateChanged(int)),
- this, SLOT(spriteAdvance(int)), Qt::DirectConnection);
+ connect(m_spriteEngine, &QQuickStochasticEngine::stateChanged,
+ this, &QQuickImageParticle::spriteAdvance, Qt::DirectConnection);
m_explicitAnimation = true;
} else {
m_spriteEngine = nullptr;
@@ -1834,6 +1856,7 @@ void QQuickImageParticle::initialize(int gIdx, int pIdx)
else
getShadowDatum(datum)->color = color;
}
+ break;
default:
break;
}
diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h
index ecf463ba13..f0bfb8a1d6 100644
--- a/src/particles/qquickimageparticle_p.h
+++ b/src/particles/qquickimageparticle_p.h
@@ -17,7 +17,7 @@
#include "qquickparticlepainter_p.h"
#include "qquickdirection_p.h"
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <QQmlListProperty>
#include <QtGui/qcolor.h>
#include <QtQuick/qsgmaterial.h>
@@ -146,7 +146,7 @@ public:
virtual ImageMaterialData *state() = 0;
};
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickImageParticle : public QQuickParticlePainter
+class Q_QUICKPARTICLES_EXPORT QQuickImageParticle : public QQuickParticlePainter
{
Q_OBJECT
Q_PROPERTY(QUrl source READ image WRITE setImage NOTIFY imageChanged)
@@ -358,7 +358,6 @@ private Q_SLOTS:
void spriteAdvance(int spriteIndex);
void spritesUpdate(qreal time = 0 );
void mainThreadFetchImageData();
- void finishBuildParticleNodes(QSGNode **n);
void invalidateSceneGraph();
private:
@@ -372,6 +371,7 @@ private:
QScopedPointer<ImageData> m_opacityTable;
bool loadingSomething();
+ void finishBuildParticleNodes(QSGNode **n);
QColor m_color;
qreal m_color_variation;
diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp
index cd48095b18..f2cd08ab5d 100644
--- a/src/particles/qquickitemparticle.cpp
+++ b/src/particles/qquickitemparticle.cpp
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qquickitemparticle_p.h"
#include <QtQuick/qsgnode.h>
#include <QTimer>
@@ -94,7 +96,11 @@ QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) :
{
setFlag(QQuickItem::ItemHasContents);
clock = new Clock(this);
- clock->start();
+ connect(this, &QQuickItemParticle::systemChanged, this, &QQuickItemParticle::reconnectSystem);
+ connect(this, &QQuickItemParticle::parentChanged, this, &QQuickItemParticle::reconnectParent);
+ connect(this, &QQuickItemParticle::enabledChanged, this, &QQuickItemParticle::updateClock);
+ reconnectSystem(m_system);
+ reconnectParent(parent);
}
QQuickItemParticle::~QQuickItemParticle()
@@ -230,9 +236,10 @@ QSGNode* QQuickItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d)
if (m_pleaseReset)
m_pleaseReset = false;
- prepareNextFrame();
-
- update();//Get called again
+ if (clockShouldUpdate()) {
+ prepareNextFrame();
+ update(); //Get called again
+ }
if (n)
n->markDirty(QSGNode::DirtyMaterial);
return QQuickItem::updatePaintNode(n,d);
@@ -285,6 +292,52 @@ QQuickItemParticleAttached *QQuickItemParticle::qmlAttachedProperties(QObject *o
return new QQuickItemParticleAttached(object);
}
+bool QQuickItemParticle::clockShouldUpdate() const
+{
+ QQuickItem *parentItem = qobject_cast<QQuickItem *>(parent());
+ return (m_system && m_system->isRunning() && !m_system->isPaused() && m_system->isEnabled()
+ && ((parentItem && parentItem->isEnabled()) || !parentItem) && isEnabled());
+}
+
+void QQuickItemParticle::reconnectParent(QQuickItem *parentItem)
+{
+ updateClock();
+ disconnect(m_parentEnabledStateConnection);
+ if (parentItem) {
+ m_parentEnabledStateConnection = connect(parentItem, &QQuickParticleSystem::enabledChanged,
+ this, &QQuickItemParticle::updateClock);
+ }
+}
+
+void QQuickItemParticle::reconnectSystem(QQuickParticleSystem *system)
+{
+ updateClock();
+ disconnect(m_systemRunStateConnection);
+ disconnect(m_systemPauseStateConnection);
+ disconnect(m_systemEnabledStateConnection);
+ if (system) {
+ m_systemRunStateConnection = connect(m_system, &QQuickParticleSystem::runningChanged, this, [this](){
+ QQuickItemParticle::updateClock();
+ });
+ m_systemPauseStateConnection = connect(m_system, &QQuickParticleSystem::pausedChanged, this, [this](){
+ QQuickItemParticle::updateClock();
+ });
+ m_systemEnabledStateConnection = connect(m_system, &QQuickParticleSystem::enabledChanged, this,
+ &QQuickItemParticle::updateClock);
+ }
+}
+
+void QQuickItemParticle::updateClock()
+{
+ if (clockShouldUpdate()) {
+ if (!clock->isRunning())
+ clock->start();
+ } else {
+ if (clock->isRunning())
+ clock->pause();
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qquickitemparticle_p.cpp"
diff --git a/src/particles/qquickitemparticle_p.h b/src/particles/qquickitemparticle_p.h
index 9d4e1b8f62..4f8d81a8df 100644
--- a/src/particles/qquickitemparticle_p.h
+++ b/src/particles/qquickitemparticle_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickItemParticleAttached;
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickItemParticle : public QQuickParticlePainter
+class Q_QUICKPARTICLES_EXPORT QQuickItemParticle : public QQuickParticlePainter
{
Q_OBJECT
Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged)
@@ -71,6 +71,10 @@ protected:
void initialize(int gIdx, int pIdx) override;
void prepareNextFrame();
private:
+ bool clockShouldUpdate() const;
+ void updateClock();
+ void reconnectSystem(QQuickParticleSystem *system);
+ void reconnectParent(QQuickItem *parent);
void processDeletables();
void tick(int time = 0);
QSet<QQuickItem* > m_deletables;
@@ -85,12 +89,16 @@ private:
typedef QTickAnimationProxy<QQuickItemParticle, &QQuickItemParticle::tick> Clock;
Clock *clock;
+ QMetaObject::Connection m_systemRunStateConnection;
+ QMetaObject::Connection m_systemPauseStateConnection;
+ QMetaObject::Connection m_systemEnabledStateConnection;
+ QMetaObject::Connection m_parentEnabledStateConnection;
};
class QQuickItemParticleAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickItemParticle* particle READ particle CONSTANT);
+ Q_PROPERTY(QQuickItemParticle* particle READ particle CONSTANT FINAL);
public:
QQuickItemParticleAttached(QObject* parent)
: QObject(parent), m_mp(0), m_parentItem(nullptr)
diff --git a/src/particles/qquicklineextruder.cpp b/src/particles/qquicklineextruder.cpp
index 400db64226..5a95eceb71 100644
--- a/src/particles/qquicklineextruder.cpp
+++ b/src/particles/qquicklineextruder.cpp
@@ -8,7 +8,7 @@
\qmltype LineShape
\instantiates QQuickLineExtruder
\inqmlmodule QtQuick.Particles
- \inherits Shape
+ \inherits ParticleExtruder
\brief Represents a line for affectors and emitters.
\ingroup qtquick-particles
diff --git a/src/particles/qquicklineextruder_p.h b/src/particles/qquicklineextruder_p.h
index 547bb4bbab..c815e429b9 100644
--- a/src/particles/qquicklineextruder_p.h
+++ b/src/particles/qquicklineextruder_p.h
@@ -16,7 +16,7 @@
//
#include "qquickparticleextruder_p.h"
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickLineExtruder : public QQuickParticleExtruder
+class Q_QUICKPARTICLES_EXPORT QQuickLineExtruder : public QQuickParticleExtruder
{
Q_OBJECT
//Default is topleft to bottom right. Flipped makes it topright to bottom left
diff --git a/src/particles/qquickmaskextruder.cpp b/src/particles/qquickmaskextruder.cpp
index b7b245758f..51d8798ac8 100644
--- a/src/particles/qquickmaskextruder.cpp
+++ b/src/particles/qquickmaskextruder.cpp
@@ -106,7 +106,7 @@ void QQuickMaskExtruder::ensureInitialized(const QRectF &rf)
// Image will in all likelyhood be in this format already, so
// no extra memory or conversion takes place
if (m_img.format() != QImage::Format_ARGB32 && m_img.format() != QImage::Format_ARGB32_Premultiplied)
- m_img = m_img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ m_img = std::move(m_img).convertToFormat(QImage::Format_ARGB32_Premultiplied);
// resample on the fly using 16-bit
int sx = (m_img.width() << 16) / r.width();
diff --git a/src/particles/qquickmaskextruder_p.h b/src/particles/qquickmaskextruder_p.h
index fcceb69a26..50153672fa 100644
--- a/src/particles/qquickmaskextruder_p.h
+++ b/src/particles/qquickmaskextruder_p.h
@@ -15,13 +15,13 @@
// We mean it.
//
#include "qquickparticleextruder_p.h"
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <QUrl>
#include <QImage>
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickMaskExtruder : public QQuickParticleExtruder
+class Q_QUICKPARTICLES_EXPORT QQuickMaskExtruder : public QQuickParticleExtruder
{
Q_OBJECT
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
diff --git a/src/particles/qquickparticleaffector.cpp b/src/particles/qquickparticleaffector.cpp
index 17038f4593..bf28cffde6 100644
--- a/src/particles/qquickparticleaffector.cpp
+++ b/src/particles/qquickparticleaffector.cpp
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qquickparticleaffector_p.h"
#include <QDebug>
#include <private/qqmlglobal_p.h>
diff --git a/src/particles/qquickparticleaffector_p.h b/src/particles/qquickparticleaffector_p.h
index 4327bf4bb7..5bd7c81940 100644
--- a/src/particles/qquickparticleaffector_p.h
+++ b/src/particles/qquickparticleaffector_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleAffector : public QQuickItem
+class Q_QUICKPARTICLES_EXPORT QQuickParticleAffector : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp
index 0498087d55..ff5336a73d 100644
--- a/src/particles/qquickparticleemitter.cpp
+++ b/src/particles/qquickparticleemitter.cpp
@@ -1,11 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qquickparticleemitter_p.h"
-#include <private/qqmlengine_p.h>
+
#include <private/qqmlglobal_p.h>
-#include <private/qjsvalue_p.h>
-#include <QRandomGenerator>
+#include <private/qquickv4particledata_p.h>
+
+#include <QtCore/qrandom.h>
+
QT_BEGIN_NAMESPACE
/*!
@@ -205,10 +209,10 @@ QQuickParticleEmitter::QQuickParticleEmitter(QQuickItem *parent) :
{
//TODO: Reset velocity/acc back to null vector? Or allow null pointer?
- connect(this, SIGNAL(particlesPerSecondChanged(qreal)),
- this, SIGNAL(particleCountChanged()));
- connect(this, SIGNAL(particleDurationChanged(int)),
- this, SIGNAL(particleCountChanged()));
+ connect(this, &QQuickParticleEmitter::particlesPerSecondChanged,
+ this, &QQuickParticleEmitter::particleCountChanged);
+ connect(this, &QQuickParticleEmitter::particleDurationChanged,
+ this, &QQuickParticleEmitter::particleCountChanged);
}
QQuickParticleEmitter::~QQuickParticleEmitter()
@@ -219,7 +223,8 @@ QQuickParticleEmitter::~QQuickParticleEmitter()
bool QQuickParticleEmitter::isEmitConnected()
{
- IS_SIGNAL_CONNECTED(this, QQuickParticleEmitter, emitParticles, (const QJSValue &));
+ IS_SIGNAL_CONNECTED(
+ this, QQuickParticleEmitter, emitParticles, (const QList<QQuickV4ParticleData> &));
}
void QQuickParticleEmitter::reclaculateGroupId() const
@@ -279,15 +284,15 @@ void QQuickParticleEmitter::setMaxParticleCount(int arg)
{
if (m_maxParticleCount != arg) {
if (arg < 0 && m_maxParticleCount >= 0){
- connect(this, SIGNAL(particlesPerSecondChanged(qreal)),
- this, SIGNAL(particleCountChanged()));
- connect(this, SIGNAL(particleDurationChanged(int)),
- this, SIGNAL(particleCountChanged()));
- }else if (arg >= 0 && m_maxParticleCount < 0){
- disconnect(this, SIGNAL(particlesPerSecondChanged(qreal)),
- this, SIGNAL(particleCountChanged()));
- disconnect(this, SIGNAL(particleDurationChanged(int)),
- this, SIGNAL(particleCountChanged()));
+ connect(this, &QQuickParticleEmitter::particlesPerSecondChanged,
+ this, &QQuickParticleEmitter::particleCountChanged);
+ connect(this, &QQuickParticleEmitter::particleDurationChanged,
+ this, &QQuickParticleEmitter::particleCountChanged);
+ } else if (arg >= 0 && m_maxParticleCount < 0){
+ disconnect(this, &QQuickParticleEmitter::particlesPerSecondChanged,
+ this, &QQuickParticleEmitter::particleCountChanged);
+ disconnect(this, &QQuickParticleEmitter::particleDurationChanged,
+ this, &QQuickParticleEmitter::velocityFromMovementChanged);
}
m_overwrite = arg < 0;
m_maxParticleCount = arg;
@@ -447,19 +452,14 @@ void QQuickParticleEmitter::emitWindow(int timeStamp)
m_system->emitParticle(d, this);
if (isEmitConnected()) {
- QQmlEngine *qmlEngine = ::qmlEngine(this);
- QV4::ExecutionEngine *v4 = qmlEngine->handle();
- QV4::Scope scope(v4);
-
//Done after emitParticle so that the Painter::load is done first, this allows you to customize its static variables
//We then don't need to request another reload, because the first reload isn't scheduled until we get back to the render thread
- QV4::ScopedArrayObject array(scope, v4->newArrayObject(toEmit.size()));
- QV4::ScopedValue v(scope);
- for (int i=0; i<toEmit.size(); i++)
- array->put(i, (v = toEmit[i]->v4Value(m_system)));
- QJSValue particles;
- QJSValuePrivate::setValue(&particles, array);
+ QList<QQuickV4ParticleData> particles;
+ particles.reserve(toEmit.size());
+ for (QQuickParticleData *particle : std::as_const(toEmit))
+ particles.push_back(particle->v4Value(m_system));
+
emit emitParticles(particles);//A chance for arbitrary JS changes
}
diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h
index 4120367475..60c942a221 100644
--- a/src/particles/qquickparticleemitter_p.h
+++ b/src/particles/qquickparticleemitter_p.h
@@ -26,7 +26,7 @@
#include <QPointF>
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleEmitter : public QQuickItem
+class Q_QUICKPARTICLES_EXPORT QQuickParticleEmitter : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
@@ -101,7 +101,7 @@ public:
void setVelocityFromMovement(qreal s);
void componentComplete() override;
Q_SIGNALS:
- void emitParticles(const QJSValue &particles);
+ void emitParticles(const QList<QQuickV4ParticleData> &particles);
void particlesPerSecondChanged(qreal);
void particleDurationChanged(int);
void enabledChanged(bool);
diff --git a/src/particles/qquickparticleextruder_p.h b/src/particles/qquickparticleextruder_p.h
index e3c72dd96f..bff9d854a1 100644
--- a/src/particles/qquickparticleextruder_p.h
+++ b/src/particles/qquickparticleextruder_p.h
@@ -20,11 +20,11 @@
#include <QPointF>
#include <QtQml/qqml.h>
#include <QtCore/private/qglobal_p.h>
-#include <QtQuickParticles/private/qtquickparticlesexports_p.h>
+#include <QtQuickParticles/qtquickparticlesexports.h>
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleExtruder : public QObject
+class Q_QUICKPARTICLES_EXPORT QQuickParticleExtruder : public QObject
{
Q_OBJECT
diff --git a/src/particles/qquickparticlegroup.cpp b/src/particles/qquickparticlegroup.cpp
index c4ff4dbd3c..d916fc0a2d 100644
--- a/src/particles/qquickparticlegroup.cpp
+++ b/src/particles/qquickparticlegroup.cpp
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qquickparticlegroup_p.h"
/*!
diff --git a/src/particles/qquickparticlegroup_p.h b/src/particles/qquickparticlegroup_p.h
index 528722243a..6b24ff3cd3 100644
--- a/src/particles/qquickparticlegroup_p.h
+++ b/src/particles/qquickparticlegroup_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleGroup : public QQuickStochasticState,
+class Q_QUICKPARTICLES_EXPORT QQuickParticleGroup : public QQuickStochasticState,
public QQmlParserStatus
{
Q_OBJECT
diff --git a/src/particles/qquickparticlepainter.cpp b/src/particles/qquickparticlepainter.cpp
index bb67611004..ee5942d7fd 100644
--- a/src/particles/qquickparticlepainter.cpp
+++ b/src/particles/qquickparticlepainter.cpp
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qquickparticlepainter_p.h"
#include <QQuickWindow>
#include <QDebug>
@@ -42,12 +44,16 @@ QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent)
void QQuickParticlePainter::itemChange(ItemChange change, const ItemChangeData &data)
{
if (change == QQuickItem::ItemSceneChange) {
- if (m_window)
- disconnect(m_window, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated()));
+ if (m_window) {
+ disconnect(m_window, &QQuickWindow::sceneGraphInvalidated,
+ this, &QQuickParticlePainter::sceneGraphInvalidated);
+ }
m_window = data.window;
m_windowChanged = true;
- if (m_window)
- connect(m_window, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated()), Qt::DirectConnection);
+ if (m_window) {
+ connect(m_window, &QQuickWindow::sceneGraphInvalidated,
+ this, &QQuickParticlePainter::sceneGraphInvalidated, Qt::DirectConnection);
+ }
}
QQuickItem::itemChange(change, data);
}
diff --git a/src/particles/qquickparticlepainter_p.h b/src/particles/qquickparticlepainter_p.h
index 8488971d52..577d64b053 100644
--- a/src/particles/qquickparticlepainter_p.h
+++ b/src/particles/qquickparticlepainter_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticlePainter : public QQuickItem
+class Q_QUICKPARTICLES_EXPORT QQuickParticlePainter : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp
index 655cd4a78e..6631574648 100644
--- a/src/particles/qquickparticlesystem.cpp
+++ b/src/particles/qquickparticlesystem.cpp
@@ -14,6 +14,7 @@
#include "qquicktrailemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
#include <cmath>
#include <QDebug>
@@ -204,8 +205,7 @@ void QQuickParticleDataHeap::insertTimed(QQuickParticleData* data, int time)
int QQuickParticleDataHeap::top()
{
- if (m_end == 0)
- return 1 << 30;
+ Q_ASSERT(!isEmpty());
return m_data[0].time;
}
@@ -355,7 +355,7 @@ bool QQuickParticleGroupData::recycle()
{
m_latestAliveParticles.clear();
- while (dataHeap.top() <= m_system->timeInt) {
+ while (!dataHeap.isEmpty() && dataHeap.top() <= m_system->timeInt) {
for (QQuickParticleData *datum : dataHeap.pop()) {
if (!datum->stillAlive(m_system)) {
freeList.free(datum->index);
@@ -384,113 +384,9 @@ void QQuickParticleGroupData::prepareRecycler(QQuickParticleData* d)
}
}
-QQuickParticleData::QQuickParticleData()
- : index(0)
- , systemIndex(-1)
- , groupId(0)
- , colorOwner(nullptr)
- , rotationOwner(nullptr)
- , deformationOwner(nullptr)
- , animationOwner(nullptr)
- , v4Datum(nullptr)
+QQuickV4ParticleData QQuickParticleData::v4Value(QQuickParticleSystem *particleSystem)
{
- x = 0;
- y = 0;
- t = -1;
- lifeSpan = 0;
- size = 0;
- endSize = 0;
- vx = 0;
- vy = 0;
- ax = 0;
- ay = 0;
- xx = 1;
- xy = 0;
- yx = 0;
- yy = 1;
- rotation = 0;
- rotationVelocity = 0;
- autoRotate = 0;
- animIdx = 0;
- frameDuration = 1;
- frameAt = -1;
- frameCount = 1;
- animT = -1;
- animX = 0;
- animY = 0;
- animWidth = 1;
- animHeight = 1;
- color.r = 255;
- color.g = 255;
- color.b = 255;
- color.a = 255;
- delegate = nullptr;
-}
-
-QQuickParticleData::~QQuickParticleData()
-{
- delete v4Datum;
-}
-
-QQuickParticleData::QQuickParticleData(const QQuickParticleData &other)
-{
- *this = other;
-}
-
-QQuickParticleData &QQuickParticleData::operator=(const QQuickParticleData &other)
-{
- clone(other);
-
- groupId = other.groupId;
- index = other.index;
- systemIndex = other.systemIndex;
- // Lazily initialized
- v4Datum = nullptr;
-
- return *this;
-}
-
-void QQuickParticleData::clone(const QQuickParticleData& other)
-{
- x = other.x;
- y = other.y;
- t = other.t;
- lifeSpan = other.lifeSpan;
- size = other.size;
- endSize = other.endSize;
- vx = other.vx;
- vy = other.vy;
- ax = other.ax;
- ay = other.ay;
- xx = other.xx;
- xy = other.xy;
- yx = other.yx;
- yy = other.yy;
- rotation = other.rotation;
- rotationVelocity = other.rotationVelocity;
- autoRotate = other.autoRotate;
- animIdx = other.animIdx;
- frameDuration = other.frameDuration;
- frameCount = other.frameCount;
- animT = other.animT;
- animX = other.animX;
- animY = other.animY;
- animWidth = other.animWidth;
- animHeight = other.animHeight;
- color = other.color;
- delegate = other.delegate;
-
- colorOwner = other.colorOwner;
- rotationOwner = other.rotationOwner;
- deformationOwner = other.deformationOwner;
- animationOwner = other.animationOwner;
-}
-
-QV4::ReturnedValue QQuickParticleData::v4Value(QQuickParticleSystem* particleSystem)
-{
- if (!v4Datum)
- v4Datum = new QQuickV4ParticleData(qmlEngine(particleSystem)->handle(), this, particleSystem);
- return v4Datum->v4Value();
+ return QQuickV4ParticleData(this, particleSystem);
}
void QQuickParticleData::debugDump(QQuickParticleSystem* particleSystem) const
@@ -589,12 +485,12 @@ void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
void QQuickParticleSystem::finishRegisteringParticleEmitter(QQuickParticleEmitter* e)
{
- connect(e, SIGNAL(particleCountChanged()),
- this, SLOT(emittersChanged()));
- connect(e, SIGNAL(groupChanged(QString)),
- this, SLOT(emittersChanged()));
+ connect(e, &QQuickParticleEmitter::particleCountChanged,
+ this, &QQuickParticleSystem::emittersChanged);
+ connect(e, &QQuickParticleEmitter::groupChanged,
+ this, &QQuickParticleSystem::emittersChanged);
if (m_componentComplete)
- emittersChanged();
+ emitterAdded(e);
e->reset();//Start, so that starttime factors appropriately
}
@@ -833,6 +729,11 @@ void QQuickParticleSystem::emittersChanged()
particleCount += groupData[i]->size();
}
+ postProcessEmitters();
+}
+
+void QQuickParticleSystem::postProcessEmitters()
+{
if (m_debugMode)
qDebug() << "Particle system emitters changed. New particle count: " << particleCount << "in" << groupData.size() << "groups.";
@@ -853,6 +754,30 @@ void QQuickParticleSystem::emittersChanged()
}
+void QQuickParticleSystem::emitterAdded(QQuickParticleEmitter *e)
+{
+ if (!m_componentComplete)
+ return;
+
+ // Populate group and set size.
+ const int groupId = e->groupId();
+ if (groupId == QQuickParticleGroupData::InvalidID) {
+ QQuickParticleGroupData *group = new QQuickParticleGroupData(e->group(), this);
+ group->setSize(e->particleCount());
+ } else {
+ QQuickParticleGroupData *group = groupData[groupId];
+ group->setSize(group->size() + e->particleCount());
+ }
+
+ // groupData can have changed independently, so we still have to iterate it all
+ // to count the particles.
+ particleCount = 0;
+ for (int i = 0, ei = groupData.size(); i != ei; ++i)
+ particleCount += groupData[i]->size();
+
+ postProcessEmitters();
+}
+
void QQuickParticleSystem::createEngine()
{
if (!m_componentComplete)
@@ -902,8 +827,8 @@ void QQuickParticleSystem::createEngine()
stateEngine->setCount(particleCount);
stateEngine->m_states = states;
- connect(stateEngine, SIGNAL(stateChanged(int)),
- this, SLOT(particleStateChange(int)));
+ connect(stateEngine, &QQuickStochasticEngine::stateChanged,
+ this, &QQuickParticleSystem::particleStateChange);
} else {
if (stateEngine)
@@ -923,11 +848,10 @@ void QQuickParticleSystem::moveGroups(QQuickParticleData *d, int newGIdx)
if (!d || newGIdx == d->groupId)
return;
- QQuickParticleData* pd = newDatum(newGIdx, false, d->systemIndex);
+ QQuickParticleData *pd = newDatum(newGIdx, false, d->systemIndex, d);
if (!pd)
return;
- pd->clone(*d);
finishNewDatum(pd);
d->systemIndex = -1;
@@ -950,14 +874,28 @@ int QQuickParticleSystem::nextSystemIndex()
return m_nextIndex++;
}
-QQuickParticleData* QQuickParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex)
+QQuickParticleData *QQuickParticleSystem::newDatum(
+ int groupId, bool respectLimits, int sysIndex,
+ const QQuickParticleData *cloneFrom)
{
Q_ASSERT(groupId < groupData.size());//XXX shouldn't really be an assert
- QQuickParticleData* ret = groupData[groupId]->newDatum(respectLimits);
- if (!ret) {
+ QQuickParticleData *ret = groupData[groupId]->newDatum(respectLimits);
+ if (!ret)
return nullptr;
+
+ if (cloneFrom) {
+ // We need to retain the "identity" information of the new particle data since it may be
+ // "recycled" and still be tracked.
+ const int retainedIndex = ret->index;
+ const int retainedGroupId = ret->groupId;
+ const int retainedSystemIndex = ret->systemIndex;
+ *ret = *cloneFrom;
+ ret->index = retainedIndex;
+ ret->groupId = retainedGroupId;
+ ret->systemIndex = retainedSystemIndex;
}
+
if (sysIndex == -1) {
if (ret->systemIndex == -1)
ret->systemIndex = nextSystemIndex();
diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h
index 90b9434fa9..0b79722696 100644
--- a/src/particles/qquickparticlesystem_p.h
+++ b/src/particles/qquickparticlesystem_p.h
@@ -28,7 +28,7 @@
#include <private/qv4util_p.h>
#include <private/qv4global_p.h>
#include <private/qv4staticvalue_p.h>
-#include "qtquickparticlesglobal_p.h"
+#include <private/qtquickparticlesglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -73,7 +73,7 @@ struct QQuickParticleDataHeapNode{
QSet<QQuickParticleData*> data;//Set ptrs instead?
};
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleDataHeap {
+class Q_QUICKPARTICLES_EXPORT QQuickParticleDataHeap {
//Idea is to do a binary heap, but which also stores a set of int,Node* so that if the int already exists, you can
//add it to the data* list. Pops return the whole list at once.
public:
@@ -83,6 +83,8 @@ public:
int top();
+ bool isEmpty() const { return m_end == 0; }
+
QSet<QQuickParticleData*> pop();
void clear();
@@ -100,7 +102,7 @@ private:
QHash<int,int> m_lookups;
};
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleGroupData {
+class Q_QUICKPARTICLES_EXPORT QQuickParticleGroupData {
class FreeList
{
public:
@@ -208,15 +210,9 @@ struct Color4ub {
uchar a;
};
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleData {
+class Q_QUICKPARTICLES_EXPORT QQuickParticleData
+{
public:
- //TODO: QObject like memory management (without the cost, just attached to system)
- QQuickParticleData();
- ~QQuickParticleData();
-
- QQuickParticleData(const QQuickParticleData &other);
- QQuickParticleData &operator=(const QQuickParticleData &other);
-
//Convenience functions for working backwards, because parameters are from the start of particle life
//If setting multiple parameters at once, doing the conversion yourself will be faster.
@@ -243,55 +239,55 @@ public:
float curAY() const { return ay; }
float curAY(QQuickParticleSystem *) const { return ay; } // used by the macros in qquickv4particledata.cpp
- int index;
- int systemIndex;
+ int index = 0;
+ int systemIndex = -1;
//General Position Stuff
- float x;
- float y;
- float t;
- float lifeSpan;
- float size;
- float endSize;
- float vx;
- float vy;
- float ax;
- float ay;
+ float x = 0;
+ float y = 0;
+ float t = -1;
+ float lifeSpan = 0;
+ float size = 0;
+ float endSize = 0;
+ float vx = 0;
+ float vy = 0;
+ float ax = 0;
+ float ay = 0;
//Painter-specific stuff, now universally shared
//Used by ImageParticle color mode
- Color4ub color;
+ Color4ub color = { 255, 255, 255, 255};
//Used by ImageParticle deform mode
- float xx;
- float xy;
- float yx;
- float yy;
- float rotation;
- float rotationVelocity;
- uchar autoRotate; // Basically a bool
+ float xx = 1;
+ float xy = 0;
+ float yx = 0;
+ float yy = 1;
+ float rotation = 0;
+ float rotationVelocity = 0;
+ uchar autoRotate = 0; // Basically a bool
//Used by ImageParticle Sprite mode
- float animIdx;
- float frameDuration;
- float frameAt;//Used for duration -1
- float frameCount;
- float animT;
- float animX;
- float animY;
- float animWidth;
- float animHeight;
-
- QQuickParticleGroupData::ID groupId;
+ float animIdx = 0;
+ float frameDuration = 1;
+ float frameAt = -1;//Used for duration -1
+ float frameCount = 1;
+ float animT = -1;
+ float animX = 0;
+ float animY = 0;
+ float animWidth = 1;
+ float animHeight = 1;
+
+ QQuickParticleGroupData::ID groupId = 0;
//Used by ImageParticle data shadowing
- QQuickImageParticle* colorOwner;
- QQuickImageParticle* rotationOwner;
- QQuickImageParticle* deformationOwner;
- QQuickImageParticle* animationOwner;
+ QQuickImageParticle* colorOwner = nullptr;
+ QQuickImageParticle* rotationOwner = nullptr;
+ QQuickImageParticle* deformationOwner = nullptr;
+ QQuickImageParticle* animationOwner = nullptr;
//Used by ItemParticle
- QQuickItem* delegate;
+ QQuickItem* delegate = nullptr;
//Used by custom affectors
- float update;
+ float update = 0;
void debugDump(QQuickParticleSystem *particleSystem) const;
bool stillAlive(QQuickParticleSystem *particleSystem) const; //Only checks end, because usually that's all you need and it's a little faster.
@@ -299,17 +295,17 @@ public:
float lifeLeft(QQuickParticleSystem *particleSystem) const;
float curSize(QQuickParticleSystem *particleSystem) const;
- void clone(const QQuickParticleData& other);//Not =, leaves meta-data like index
- QV4::ReturnedValue v4Value(QQuickParticleSystem *particleSystem);
+
+ QQuickV4ParticleData v4Value(QQuickParticleSystem *particleSystem);
void extendLife(float time, QQuickParticleSystem *particleSystem);
static inline constexpr float EPSILON() noexcept { return 0.001f; }
-
-private:
- QQuickV4ParticleData* v4Datum;
};
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleSystem : public QQuickItem
+static_assert(std::is_trivially_copyable_v<QQuickParticleData>);
+static_assert(std::is_trivially_destructible_v<QQuickParticleData>);
+
+class Q_QUICKPARTICLES_EXPORT QQuickParticleSystem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
@@ -368,7 +364,9 @@ private Q_SLOTS:
public:
//These can be called multiple times per frame, performance critical
void emitParticle(QQuickParticleData* p, QQuickParticleEmitter *particleEmitter);
- QQuickParticleData* newDatum(int groupId, bool respectLimits = true, int sysIdx = -1);
+ QQuickParticleData *newDatum(
+ int groupId, bool respectLimits = true, int sysIdx = -1,
+ const QQuickParticleData *cloneFrom = nullptr);
void finishNewDatum(QQuickParticleData*);
void moveGroups(QQuickParticleData *d, int newGIdx);
int nextSystemIndex();
@@ -418,6 +416,8 @@ private:
void searchNextFreeGroupId();
private:
+ void emitterAdded(QQuickParticleEmitter *e);
+ void postProcessEmitters();
void initializeSystem();
void initGroups();
QList<QPointer<QQuickParticleEmitter> > m_emitters;
diff --git a/src/particles/qquickpointattractor.cpp b/src/particles/qquickpointattractor.cpp
index 499fbaa6a6..a7df598f6b 100644
--- a/src/particles/qquickpointattractor.cpp
+++ b/src/particles/qquickpointattractor.cpp
@@ -11,61 +11,57 @@ QT_BEGIN_NAMESPACE
\inqmlmodule QtQuick.Particles
\ingroup qtquick-particles
\inherits Affector
- \brief For attracting particles towards a specific point.
+ \brief Attracts particles towards a specific point.
- Note that the size and position of this element affects which particles it affects.
- The size of the point attracted to is always 0x0, and the location of that point
- is specified by the pointX and pointY properties.
+ Like other affectors, Attractor has the standard properties x, y, width,
+ and height that represent the affected area. The size and position of the
+ Attractor item determine the affected particles.
- Note that Attractor has the standard Item x,y,width and height properties.
- Like other affectors, these represent the affected area. They
- do not represent the 0x0 point which is the target of the attraction.
+ The size of the attracting point is always 0x0, and its location is
+ specified by \l pointX and \l pointY properties.
*/
/*!
- \qmlproperty real QtQuick.Particles::PointAttractor::pointX
+ \qmlproperty real QtQuick.Particles::Attractor::pointX
- The x coordinate of the attracting point. This is relative
- to the x coordinate of the Attractor.
+ The x coordinate of the attracting point, relative
+ to the x coordinate of the Attractor item.
*/
/*!
- \qmlproperty real QtQuick.Particles::PointAttractor::pointY
+ \qmlproperty real QtQuick.Particles::Attractor::pointY
- The y coordinate of the attracting point. This is relative
- to the y coordinate of the Attractor.
+ The y coordinate of the attracting point, relative
+ to the y coordinate of the Attractor item.
*/
/*!
- \qmlproperty real QtQuick.Particles::PointAttractor::strength
+ \qmlproperty real QtQuick.Particles::Attractor::strength
The pull, in units per second, to be exerted on an item one pixel away.
- Depending on how the attraction is proportionalToDistance this may have to
- be very high or very low to have a reasonable effect on particles at a
- distance.
+ Strength, together with the value of \l proportionalToDistance property,
+ determine the exact amount of pull exerted on particles at a distance.
*/
/*!
- \qmlproperty AffectableParameter QtQuick.Particles::Attractor::affectedParameter
-
- What attribute of particles is directly affected.
- \list
- \li Attractor.Position
- \li Attractor.Velocity
- \li Attractor.Acceleration
- \endlist
+ \qmlproperty enumeration QtQuick.Particles::Attractor::affectedParameter
+
+ The attribute of particles that is directly affected.
+
+ \value Attractor.Position Position
+ \value Attractor.Velocity Velocity
+ \value Attractor.Acceleration Acceleration
*/
/*!
- \qmlproperty Proportion QtQuick.Particles::Attractor::proportionalToDistance
+ \qmlproperty enumeration QtQuick.Particles::Attractor::proportionalToDistance
- How the distance from the particle to the point affects the strength of the attraction.
+ The relation between the \l strength of the attraction and the distance from
+ the particle to the attracting point.
- \list
- \li Attractor.Constant
- \li Attractor.Linear
- \li Attractor.InverseLinear
- \li Attractor.Quadratic
- \li Attractor.InverseQuadratic
- \endlist
+ \value Attractor.Constant Constant
+ \value Attractor.Linear Linear
+ \value Attractor.InverseLinear Inverse linear
+ \value Attractor.Quadratic Quadratic
+ \value Attractor.InverseQuadratic Inverse quadratic
*/
diff --git a/src/particles/qquickpointattractor_p.h b/src/particles/qquickpointattractor_p.h
index d0ea8878fc..9bdd281382 100644
--- a/src/particles/qquickpointattractor_p.h
+++ b/src/particles/qquickpointattractor_p.h
@@ -18,7 +18,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickAttractorAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickAttractorAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged)
diff --git a/src/particles/qquickpointdirection_p.h b/src/particles/qquickpointdirection_p.h
index 425ad7da71..48863a1343 100644
--- a/src/particles/qquickpointdirection_p.h
+++ b/src/particles/qquickpointdirection_p.h
@@ -18,7 +18,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickPointDirection : public QQuickDirection
+class Q_QUICKPARTICLES_EXPORT QQuickPointDirection : public QQuickDirection
{
Q_OBJECT
Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
diff --git a/src/particles/qquickrectangleextruder_p.h b/src/particles/qquickrectangleextruder_p.h
index 781df422a9..5ae59d04e0 100644
--- a/src/particles/qquickrectangleextruder_p.h
+++ b/src/particles/qquickrectangleextruder_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickRectangleExtruder : public QQuickParticleExtruder
+class Q_QUICKPARTICLES_EXPORT QQuickRectangleExtruder : public QQuickParticleExtruder
{
Q_OBJECT
Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)
diff --git a/src/particles/qquickspritegoal_p.h b/src/particles/qquickspritegoal_p.h
index 5113593972..143a61d709 100644
--- a/src/particles/qquickspritegoal_p.h
+++ b/src/particles/qquickspritegoal_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickStochasticEngine;
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickSpriteGoalAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickSpriteGoalAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged)
diff --git a/src/particles/qquicktargetdirection_p.h b/src/particles/qquicktargetdirection_p.h
index 3ab2550be6..a83b3121ec 100644
--- a/src/particles/qquicktargetdirection_p.h
+++ b/src/particles/qquicktargetdirection_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
class QQuickItem;
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickTargetDirection : public QQuickDirection
+class Q_QUICKPARTICLES_EXPORT QQuickTargetDirection : public QQuickDirection
{
Q_OBJECT
Q_PROPERTY(qreal targetX READ targetX WRITE setTargetX NOTIFY targetXChanged)
diff --git a/src/particles/qquicktrailemitter.cpp b/src/particles/qquicktrailemitter.cpp
index 4deb96c1e5..e5cd0891a5 100644
--- a/src/particles/qquicktrailemitter.cpp
+++ b/src/particles/qquicktrailemitter.cpp
@@ -1,12 +1,17 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qquicktrailemitter_p.h"
-#include <private/qqmlengine_p.h>
+
#include <private/qqmlglobal_p.h>
-#include <private/qjsvalue_p.h>
-#include <QRandomGenerator>
+#include <private/qquickv4particledata_p.h>
+
+#include <QtCore/qrandom.h>
+
#include <cmath>
+
QT_BEGIN_NAMESPACE
/*!
@@ -31,12 +36,12 @@ QQuickTrailEmitter::QQuickTrailEmitter(QQuickItem *parent) :
, m_defaultEmissionExtruder(new QQuickParticleExtruder(this))
{
//TODO: If followed increased their size
- connect(this, SIGNAL(followChanged(QString)),
- this, SLOT(recalcParticlesPerSecond()));
- connect(this, SIGNAL(particleDurationChanged(int)),
- this, SLOT(recalcParticlesPerSecond()));
- connect(this, SIGNAL(particlesPerParticlePerSecondChanged(int)),
- this, SLOT(recalcParticlesPerSecond()));
+ connect(this, &QQuickTrailEmitter::followChanged,
+ this, &QQuickTrailEmitter::recalcParticlesPerSecond);
+ connect(this, &QQuickTrailEmitter::particleDurationChanged,
+ this, &QQuickTrailEmitter::recalcParticlesPerSecond);
+ connect(this, &QQuickTrailEmitter::particlesPerParticlePerSecondChanged,
+ this, &QQuickTrailEmitter::recalcParticlesPerSecond);
}
/*!
@@ -92,8 +97,9 @@ QQuickTrailEmitter::QQuickTrailEmitter(QQuickItem *parent) :
bool QQuickTrailEmitter::isEmitFollowConnected()
{
- IS_SIGNAL_CONNECTED(this, QQuickTrailEmitter, emitFollowParticles,
- (const QJSValue &, const QJSValue &));
+ IS_SIGNAL_CONNECTED(
+ this, QQuickTrailEmitter, emitFollowParticles,
+ (const QList<QQuickV4ParticleData> &, const QQuickV4ParticleData &));
}
void QQuickTrailEmitter::recalcParticlesPerSecond(){
@@ -232,21 +238,15 @@ void QQuickTrailEmitter::emitWindow(int timeStamp)
m_system->emitParticle(d, this);
if (isEmitConnected() || isEmitFollowConnected()) {
- QQmlEngine *qmlEngine = ::qmlEngine(this);
- QV4::ExecutionEngine *v4 = qmlEngine->handle();
- QV4::Scope scope(v4);
- QV4::ScopedArrayObject array(scope, v4->newArrayObject(toEmit.size()));
- QV4::ScopedValue v(scope);
- for (int i=0; i<toEmit.size(); i++)
- array->put(i, (v = toEmit[i]->v4Value(m_system)));
+ QList<QQuickV4ParticleData> particles;
+ particles.reserve(toEmit.size());
+ for (QQuickParticleData *particle : std::as_const(toEmit))
+ particles.push_back(particle->v4Value(m_system));
- QJSValue particles;
- QJSValuePrivate::setValue(&particles, array);
if (isEmitFollowConnected()) {
//A chance for many arbitrary JS changes
- emit emitFollowParticles(
- particles, QJSValuePrivate::fromReturnedValue(d->v4Value(m_system)));
+ emit emitFollowParticles(particles, d->v4Value(m_system));
} else if (isEmitConnected()) {
emit emitParticles(particles);//A chance for arbitrary JS changes
}
diff --git a/src/particles/qquicktrailemitter_p.h b/src/particles/qquicktrailemitter_p.h
index d6d0bad9b8..1fee0738d1 100644
--- a/src/particles/qquicktrailemitter_p.h
+++ b/src/particles/qquicktrailemitter_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickTrailEmitter : public QQuickParticleEmitter
+class Q_QUICKPARTICLES_EXPORT QQuickTrailEmitter : public QQuickParticleEmitter
{
Q_OBJECT
Q_PROPERTY(QString follow READ follow WRITE setFollow NOTIFY followChanged)
@@ -66,7 +66,9 @@ public:
}
Q_SIGNALS:
- void emitFollowParticles(const QJSValue &particles, const QJSValue &followed);
+ void emitFollowParticles(
+ const QList<QQuickV4ParticleData> &particles,
+ const QQuickV4ParticleData &followed);
void particlesPerParticlePerSecondChanged(int arg);
diff --git a/src/particles/qquickturbulence.cpp b/src/particles/qquickturbulence.cpp
index a507e153f5..ff892983f6 100644
--- a/src/particles/qquickturbulence.cpp
+++ b/src/particles/qquickturbulence.cpp
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qquickturbulence_p.h"
#include "qquickparticlepainter_p.h"//TODO: Why was this needed again?
#include <cmath>
diff --git a/src/particles/qquickturbulence_p.h b/src/particles/qquickturbulence_p.h
index c83b6252dc..bf18aa617a 100644
--- a/src/particles/qquickturbulence_p.h
+++ b/src/particles/qquickturbulence_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickParticlePainter;
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickTurbulenceAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickTurbulenceAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged)
diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp
index 7988259140..14b327d04a 100644
--- a/src/particles/qquickv4particledata.cpp
+++ b/src/particles/qquickv4particledata.cpp
@@ -3,7 +3,6 @@
#include <math.h>
#include "qquickv4particledata_p.h"
-#include "qquickparticlesystem_p.h"//for QQuickParticleData
#include <QDebug>
#include <private/qv4engine_p.h>
#include <private/qv4functionobject_p.h>
@@ -231,268 +230,4 @@ QT_BEGIN_NAMESPACE
The currentSize of the particle, interpolating between startSize and endSize based on the currentTime.
*/
-namespace QV4 {
-namespace Heap {
-struct QV4ParticleData : QV4::Object::Data {
- void init(QQuickParticleData *datum, QQuickParticleSystem* particleSystem)
- {
- Object::init();
- this->datum = datum;
- this->particleSystem = particleSystem;
- }
- QQuickParticleData* datum;//TODO: Guard needed?
- QQuickParticleSystem* particleSystem;
-};
-}
-}
-
-//### Particle data handles are not locked to within certain scopes like QQuickContext2D, but there's no way to reload either...
-struct QV4ParticleData : public QV4::Object
-{
- V4_OBJECT2(QV4ParticleData, QV4::Object)
-};
-
-DEFINE_OBJECT_VTABLE(QV4ParticleData);
-
-class QV4ParticleDataDeletable : public QV4::ExecutionEngine::Deletable
-{
-public:
- QV4ParticleDataDeletable(QV4::ExecutionEngine *engine);
- ~QV4ParticleDataDeletable() override;
-
- QV4::PersistentValue proto;
-};
-
-static QV4::ReturnedValue particleData_discard(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
-{
- QV4::Scope scope(b);
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject);
-
- if (!r || !r->d()->datum)
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));
-
- r->d()->datum->lifeSpan = 0; //Don't kill(), because it could still be in the middle of being created
- RETURN_RESULT(QV4::Encode::undefined());
-}
-
-static QV4::ReturnedValue particleData_lifeLeft(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
-{
- QV4::Scope scope(b);
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject);
-
- if (!r || !r->d()->datum)
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));
-
- RETURN_RESULT(QV4::Encode(r->d()->datum->lifeLeft(r->d()->particleSystem)));
-}
-
-static QV4::ReturnedValue particleData_curSize(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
-{
- QV4::Scope scope(b);
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject);
-
- if (!r || !r->d()->datum)
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));
-
- RETURN_RESULT(QV4::Encode(r->d()->datum->curSize(r->d()->particleSystem)));
-}
-#define COLOR_GETTER_AND_SETTER(VAR, NAME) static QV4::ReturnedValue particleData_get_ ## NAME (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) \
-{ \
- QV4::Scope scope(b); \
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject); \
- if (!r || !r->d()->datum) \
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
-\
- RETURN_RESULT(QV4::Encode((r->d()->datum->color. VAR )/255.0));\
-}\
-\
-static QV4::ReturnedValue particleData_set_ ## NAME (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)\
-{\
- QV4::Scope scope(b); \
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject); \
- if (!r || !r->d()->datum)\
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
-\
- double d = argc ? argv[0].toNumber() : 0; \
- r->d()->datum->color. VAR = qMin(255, qMax(0, (int)::floor(d * 255.0)));\
- RETURN_UNDEFINED(); \
-}
-
-
-#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static QV4::ReturnedValue particleData_get_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) \
-{ \
- QV4::Scope scope(b); \
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject); \
- if (!r || !r->d()->datum) \
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
-\
- RETURN_RESULT(QV4::Encode(r->d()->datum-> VARIABLE));\
-}\
-\
-static QV4::ReturnedValue particleData_set_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)\
-{\
- QV4::Scope scope(b); \
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject); \
- if (!r || !r->d()->datum)\
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
-\
- r->d()->datum-> VARIABLE = (argc && argv[0].toBoolean()) ? 1.0 : 0.0;\
- RETURN_UNDEFINED(); \
-}
-
-#define FLOAT_GETTER_AND_SETTER(VARIABLE) static QV4::ReturnedValue particleData_get_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) \
-{ \
- QV4::Scope scope(b); \
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject); \
- if (!r || !r->d()->datum) \
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
-\
- RETURN_RESULT(QV4::Encode(r->d()->datum-> VARIABLE));\
-}\
-\
-static QV4::ReturnedValue particleData_set_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)\
-{\
- QV4::Scope scope(b); \
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject); \
- if (!r || !r->d()->datum)\
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
-\
- r->d()->datum-> VARIABLE = argc ? argv[0].toNumber() : qt_qnan();\
- RETURN_UNDEFINED(); \
-}
-
-#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static QV4::ReturnedValue particleData_get_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) \
-{ \
- QV4::Scope scope(b); \
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject); \
- if (!r || !r->d()->datum) \
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
-\
- RETURN_RESULT(QV4::Encode(r->d()->datum-> GETTER (r->d()->particleSystem)));\
-}\
-\
-static QV4::ReturnedValue particleData_set_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)\
-{\
- QV4::Scope scope(b); \
- QV4::Scoped<QV4ParticleData> r(scope, *thisObject); \
- if (!r || !r->d()->datum)\
- RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
-\
- r->d()->datum-> SETTER (argc ? argv[0].toNumber() : qt_qnan(), r->d()->particleSystem);\
- RETURN_UNDEFINED(); \
-}
-
-#define REGISTER_ACCESSOR(PROTO, ENGINE, VARIABLE, NAME) \
- PROTO ->defineAccessorProperty( QStringLiteral( #NAME ), particleData_get_ ## VARIABLE , particleData_set_ ## VARIABLE )
-
-COLOR_GETTER_AND_SETTER(r, red)
-COLOR_GETTER_AND_SETTER(g, green)
-COLOR_GETTER_AND_SETTER(b, blue)
-COLOR_GETTER_AND_SETTER(a, alpha)
-SEMIBOOL_GETTER_AND_SETTER(autoRotate)
-SEMIBOOL_GETTER_AND_SETTER(update)
-FLOAT_GETTER_AND_SETTER(x)
-FLOAT_GETTER_AND_SETTER(y)
-FLOAT_GETTER_AND_SETTER(t)
-FLOAT_GETTER_AND_SETTER(lifeSpan)
-FLOAT_GETTER_AND_SETTER(size)
-FLOAT_GETTER_AND_SETTER(endSize)
-FLOAT_GETTER_AND_SETTER(vx)
-FLOAT_GETTER_AND_SETTER(vy)
-FLOAT_GETTER_AND_SETTER(ax)
-FLOAT_GETTER_AND_SETTER(ay)
-FLOAT_GETTER_AND_SETTER(xx)
-FLOAT_GETTER_AND_SETTER(xy)
-FLOAT_GETTER_AND_SETTER(yx)
-FLOAT_GETTER_AND_SETTER(yy)
-FLOAT_GETTER_AND_SETTER(rotation)
-FLOAT_GETTER_AND_SETTER(rotationVelocity)
-FLOAT_GETTER_AND_SETTER(animIdx)
-FLOAT_GETTER_AND_SETTER(frameDuration)
-FLOAT_GETTER_AND_SETTER(frameAt)
-FLOAT_GETTER_AND_SETTER(frameCount)
-FLOAT_GETTER_AND_SETTER(animT)
-FAKE_FLOAT_GETTER_AND_SETTER(curX, curX, setInstantaneousX)
-FAKE_FLOAT_GETTER_AND_SETTER(curVX, curVX, setInstantaneousVX)
-FAKE_FLOAT_GETTER_AND_SETTER(curAX, curAX, setInstantaneousAX)
-FAKE_FLOAT_GETTER_AND_SETTER(curY, curY, setInstantaneousY)
-FAKE_FLOAT_GETTER_AND_SETTER(curVY, curVY, setInstantaneousVY)
-FAKE_FLOAT_GETTER_AND_SETTER(curAY, curAY, setInstantaneousAY)
-
-QV4ParticleDataDeletable::QV4ParticleDataDeletable(QV4::ExecutionEngine *v4)
-{
- QV4::Scope scope(v4);
- QV4::ScopedObject p(scope, v4->newObject());
-
- p->defineDefaultProperty(QStringLiteral("discard"), particleData_discard);
- p->defineDefaultProperty(QStringLiteral("lifeLeft"), particleData_lifeLeft);
- p->defineDefaultProperty(QStringLiteral("currentSize"), particleData_curSize);
-
- REGISTER_ACCESSOR(p, v4, x, initialX);
- REGISTER_ACCESSOR(p, v4, y, initialY);
- REGISTER_ACCESSOR(p, v4, t, t);
- REGISTER_ACCESSOR(p, v4, lifeSpan, lifeSpan);
- REGISTER_ACCESSOR(p, v4, size, startSize);
- REGISTER_ACCESSOR(p, v4, endSize, endSize);
- REGISTER_ACCESSOR(p, v4, vx, initialVX);
- REGISTER_ACCESSOR(p, v4, vy, initialVY);
- REGISTER_ACCESSOR(p, v4, ax, initialAX);
- REGISTER_ACCESSOR(p, v4, ay, initialAY);
- REGISTER_ACCESSOR(p, v4, xx, xDeformationVectorX);
- REGISTER_ACCESSOR(p, v4, xy, xDeformationVectorY);
- REGISTER_ACCESSOR(p, v4, yx, yDeformationVectorX);
- REGISTER_ACCESSOR(p, v4, yy, yDeformationVectorY);
- REGISTER_ACCESSOR(p, v4, rotation, rotation);
- REGISTER_ACCESSOR(p, v4, rotationVelocity, rotationVelocity);
- REGISTER_ACCESSOR(p, v4, autoRotate, autoRotate);
- REGISTER_ACCESSOR(p, v4, animIdx, animationIndex);
- REGISTER_ACCESSOR(p, v4, frameDuration, frameDuration);
- REGISTER_ACCESSOR(p, v4, frameAt, frameAt);
- REGISTER_ACCESSOR(p, v4, frameCount, frameCount);
- REGISTER_ACCESSOR(p, v4, animT, animationT);
- REGISTER_ACCESSOR(p, v4, update, update);
- REGISTER_ACCESSOR(p, v4, curX, x);
- REGISTER_ACCESSOR(p, v4, curVX, vx);
- REGISTER_ACCESSOR(p, v4, curAX, ax);
- REGISTER_ACCESSOR(p, v4, curY, y);
- REGISTER_ACCESSOR(p, v4, curVY, vy);
- REGISTER_ACCESSOR(p, v4, curAY, ay);
- REGISTER_ACCESSOR(p, v4, red, red);
- REGISTER_ACCESSOR(p, v4, green, green);
- REGISTER_ACCESSOR(p, v4, blue, blue);
- REGISTER_ACCESSOR(p, v4, alpha, alpha);
-
- proto = p;
-}
-
-QV4ParticleDataDeletable::~QV4ParticleDataDeletable()
-{
-}
-
-V4_DEFINE_EXTENSION(QV4ParticleDataDeletable, particleV4Data);
-
-
-QQuickV4ParticleData::QQuickV4ParticleData(QV4::ExecutionEngine* v4, QQuickParticleData* datum,
- QQuickParticleSystem *system)
-{
- if (!v4 || !datum)
- return;
-
- QV4::Scope scope(v4);
- QV4ParticleDataDeletable *d = particleV4Data(scope.engine);
- QV4::ScopedObject o(scope, v4->memoryManager->allocate<QV4ParticleData>(datum, system));
- QV4::ScopedObject p(scope, d->proto.value());
- o->setPrototypeUnchecked(p);
- m_v4Value = o;
-}
-
-QQuickV4ParticleData::~QQuickV4ParticleData()
-{
-}
-
-QV4::ReturnedValue QQuickV4ParticleData::v4Value() const
-{
- return m_v4Value.value();
-}
-
QT_END_NAMESPACE
diff --git a/src/particles/qquickv4particledata_p.h b/src/particles/qquickv4particledata_p.h
index 1bc6d24527..d51c4871e8 100644
--- a/src/particles/qquickv4particledata_p.h
+++ b/src/particles/qquickv4particledata_p.h
@@ -15,20 +15,109 @@
// We mean it.
//
-#include <private/qv4persistent_p.h>
-#include <private/qv4value_p.h>
+#include <private/qquickparticlesystem_p.h>
+#include <QtQml/qqml.h>
QT_BEGIN_NAMESPACE
-class QQuickParticleData;
-class QQuickParticleSystem;
-class QQuickV4ParticleData {
+class QQuickV4ParticleData
+{
+ Q_GADGET
+ QML_VALUE_TYPE(particle)
+ QML_ADDED_IN_VERSION(6, 7)
+
+#define Q_QUICK_PARTICLE_ACCESSOR(TYPE, VARIABLE, NAME) \
+ Q_PROPERTY(TYPE NAME READ NAME WRITE set_ ## NAME FINAL) \
+ TYPE NAME() const { return datum ? datum->VARIABLE : TYPE(); } \
+ void set_ ## NAME(TYPE a) { if (datum) datum->VARIABLE = a; }
+
+ Q_QUICK_PARTICLE_ACCESSOR(float, x, initialX)
+ Q_QUICK_PARTICLE_ACCESSOR(float, vx, initialVX)
+ Q_QUICK_PARTICLE_ACCESSOR(float, ax, initialAX)
+ Q_QUICK_PARTICLE_ACCESSOR(float, y, initialY)
+ Q_QUICK_PARTICLE_ACCESSOR(float, vy, initialVY)
+ Q_QUICK_PARTICLE_ACCESSOR(float, ay, initialAY)
+ Q_QUICK_PARTICLE_ACCESSOR(float, t, t)
+ Q_QUICK_PARTICLE_ACCESSOR(float, size, startSize)
+ Q_QUICK_PARTICLE_ACCESSOR(float, endSize, endSize)
+ Q_QUICK_PARTICLE_ACCESSOR(float, lifeSpan, lifeSpan)
+ Q_QUICK_PARTICLE_ACCESSOR(float, rotation, rotation)
+ Q_QUICK_PARTICLE_ACCESSOR(float, rotationVelocity, rotationVelocity)
+ Q_QUICK_PARTICLE_ACCESSOR(bool, autoRotate, autoRotate)
+ Q_QUICK_PARTICLE_ACCESSOR(bool, update, update)
+ Q_QUICK_PARTICLE_ACCESSOR(float, xx, xDeformationVectorX)
+ Q_QUICK_PARTICLE_ACCESSOR(float, yx, yDeformationVectorX)
+ Q_QUICK_PARTICLE_ACCESSOR(float, xy, xDeformationVectorY)
+ Q_QUICK_PARTICLE_ACCESSOR(float, yy, yDeformationVectorY)
+
+ // Undocumented?
+ Q_QUICK_PARTICLE_ACCESSOR(float, animIdx, animationIndex)
+ Q_QUICK_PARTICLE_ACCESSOR(float, frameDuration, frameDuration)
+ Q_QUICK_PARTICLE_ACCESSOR(float, frameAt, frameAt)
+ Q_QUICK_PARTICLE_ACCESSOR(float, frameCount, frameCount)
+ Q_QUICK_PARTICLE_ACCESSOR(float, animT, animationT)
+
+#undef Q_QUICK_PARTICLE_ACCESSOR
+
+#define Q_QUICK_PARTICLE_SYSTEM_ACCESSOR(GETTER, SETTER, NAME) \
+ Q_PROPERTY(float NAME READ NAME WRITE set_ ## NAME) \
+ float NAME() const { return (datum && particleSystem) ? datum->GETTER(particleSystem) : 0; } \
+ void set_ ## NAME(float a) { if (datum && particleSystem) datum->SETTER(a, particleSystem); }
+
+ Q_QUICK_PARTICLE_SYSTEM_ACCESSOR(curX, setInstantaneousX, x)
+ Q_QUICK_PARTICLE_SYSTEM_ACCESSOR(curVX, setInstantaneousVX, vx)
+ Q_QUICK_PARTICLE_SYSTEM_ACCESSOR(curAX, setInstantaneousAX, ax)
+ Q_QUICK_PARTICLE_SYSTEM_ACCESSOR(curY, setInstantaneousY, y)
+ Q_QUICK_PARTICLE_SYSTEM_ACCESSOR(curVY, setInstantaneousVY, vy)
+ Q_QUICK_PARTICLE_SYSTEM_ACCESSOR(curAY, setInstantaneousAY, ay)
+
+#undef Q_QUICK_PARTICLE_SYSTEM_ACCESSOR
+
+#define Q_QUICK_PARTICLE_COLOR_ACCESSOR(VAR, NAME) \
+ Q_PROPERTY(float NAME READ NAME WRITE set_ ## NAME) \
+ float NAME() const { return datum ? datum->color.VAR / 255.0 : 0.0; } \
+ void set_ ## NAME(float a)\
+ {\
+ if (datum)\
+ datum->color.VAR = qMin(255, qMax(0, (int)::floor(a * 255.0)));\
+ }
+
+ Q_QUICK_PARTICLE_COLOR_ACCESSOR(r, red)
+ Q_QUICK_PARTICLE_COLOR_ACCESSOR(g, green)
+ Q_QUICK_PARTICLE_COLOR_ACCESSOR(b, blue)
+ Q_QUICK_PARTICLE_COLOR_ACCESSOR(a, alpha)
+
+#undef Q_QUICK_PARTICLE_COLOR_ACCESSOR
+
+ Q_PROPERTY(float lifeLeft READ lifeLeft)
+ Q_PROPERTY(float currentSize READ currentSize)
+
public:
- QQuickV4ParticleData(QV4::ExecutionEngine*, QQuickParticleData*, QQuickParticleSystem *system);
- ~QQuickV4ParticleData();
- QV4::ReturnedValue v4Value() const;
+ QQuickV4ParticleData() = default;
+ QQuickV4ParticleData(QQuickParticleData *datum, QQuickParticleSystem *system)
+ : datum(datum)
+ , particleSystem(system)
+ {}
+
+ Q_INVOKABLE void discard()
+ {
+ if (datum)
+ datum->lifeSpan = 0;
+ }
+
+ float lifeLeft() const
+ {
+ return (datum && particleSystem) ? datum->lifeLeft(particleSystem) : 0.0;
+ }
+
+ float currentSize() const
+ {
+ return (datum && particleSystem) ? datum->curSize(particleSystem) : 0.0;
+ }
+
private:
- QV4::PersistentValue m_v4Value;
+ QQuickParticleData *datum = nullptr;
+ QQuickParticleSystem *particleSystem = nullptr;
};
diff --git a/src/particles/qquickwander_p.h b/src/particles/qquickwander_p.h
index 10914a76da..e05e91a11e 100644
--- a/src/particles/qquickwander_p.h
+++ b/src/particles/qquickwander_p.h
@@ -28,7 +28,7 @@ struct WanderData{
qreal y_var;
};
-class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickWanderAffector : public QQuickParticleAffector
+class Q_QUICKPARTICLES_EXPORT QQuickWanderAffector : public QQuickParticleAffector
{
Q_OBJECT
Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged)
diff --git a/src/particles/qtquickparticlesglobal_p.h b/src/particles/qtquickparticlesglobal_p.h
index 9905abbc0f..6df7f93429 100644
--- a/src/particles/qtquickparticlesglobal_p.h
+++ b/src/particles/qtquickparticlesglobal_p.h
@@ -16,6 +16,6 @@
//
#include <QtCore/qglobal.h>
-#include <QtQuickParticles/private/qtquickparticlesexports_p.h>
+#include <QtQuickParticles/qtquickparticlesexports.h>
#endif // QTQUICKPARTICLESGLOBAL_P_H
diff --git a/src/particles/shaders_ng/compile.bat b/src/particles/shaders_ng/compile.bat
deleted file mode 100755
index 1bbaec4290..0000000000
--- a/src/particles/shaders_ng/compile.bat
+++ /dev/null
@@ -1,28 +0,0 @@
-:: Copyright (C) 2019 The Qt Company Ltd.
-:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-:: For HLSL we invoke fxc.exe (-c argument) and store the resulting intermediate format
-:: instead of HLSL source, so this needs to be run on Windows from a developer command prompt.
-
-:: For SPIR-V the optimizer is requested (-O argument) which means spirv-opt must be
-:: invokable (e.g. because it's in the PATH from the Vulkan SDK)
-
-qsb -DPOINT -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_simplepoint.vert.qsb imageparticle.vert
-qsb -DPOINT --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_simplepoint.frag.qsb imageparticle.frag
-
-qsb -DPOINT -DCOLOR -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_coloredpoint.vert.qsb imageparticle.vert
-qsb -DPOINT -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_coloredpoint.frag.qsb imageparticle.frag
-
-qsb -DCOLOR -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_colored.vert.qsb imageparticle.vert
-qsb -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_colored.frag.qsb imageparticle.frag
-
-qsb -DDEFORM -DCOLOR -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_deformed.vert.qsb imageparticle.vert
-qsb -DDEFORM -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_deformed.frag.qsb imageparticle.frag
-
-qsb -DTABLE -DDEFORM -DCOLOR -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_tabled.vert.qsb imageparticle.vert
-qsb -DTABLE -DDEFORM -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_tabled.frag.qsb imageparticle.frag
-
-qsb -DSPRITE -DTABLE -DDEFORM -DCOLOR -b --zorder-loc 8 --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_sprite.vert.qsb imageparticle.vert
-qsb -DSPRITE -DTABLE -DDEFORM -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o imageparticle_sprite.frag.qsb imageparticle.frag
-
-
diff --git a/src/particles/shaders_ng/imageparticle.frag b/src/particles/shaders_ng/imageparticle.frag
index 90b79e6ea9..2134f54e1b 100644
--- a/src/particles/shaders_ng/imageparticle.frag
+++ b/src/particles/shaders_ng/imageparticle.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
#if defined(TABLE)
@@ -19,7 +22,11 @@ layout(location = 2) in float fFade;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
float opacity;
float entry;
float timestamp;
diff --git a/src/particles/shaders_ng/imageparticle.vert b/src/particles/shaders_ng/imageparticle.vert
index da3533dc40..52e4a59d62 100644
--- a/src/particles/shaders_ng/imageparticle.vert
+++ b/src/particles/shaders_ng/imageparticle.vert
@@ -44,7 +44,11 @@ layout(location = 2) out float fFade;
#endif
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
float opacity;
float entry;
float timestamp;
@@ -53,18 +57,22 @@ layout(std140, binding = 0) uniform buf {
float opacitytable[64];
} ubuf;
-out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };
-
void main()
{
float t = (ubuf.timestamp - vData.x) / vData.y;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix = ubuf.matrix[gl_ViewIndex];
+#else
+ mat4 matrix = ubuf.matrix;
+#endif
+
if (t < 0. || t > 1.) {
#if defined(DEFORM)
- gl_Position = ubuf.matrix * vec4(vPosRot.x, vPosRot.y, 0., 1.);
+ gl_Position = matrix * vec4(vPosRot.x, vPosRot.y, 0., 1.);
#elif defined(POINT)
gl_PointSize = 0.;
#else
- gl_Position = ubuf.matrix * vec4(vPos.x, vPos.y, 0., 1.);
+ gl_Position = matrix * vec4(vPos.x, vPos.y, 0., 1.);
#endif
} else {
#if defined(SPRITE)
@@ -96,11 +104,11 @@ void main()
if (currentSize <= 0.) {
#if defined(DEFORM)
- gl_Position = ubuf.matrix * vec4(vPosRot.x, vPosRot.y, 0., 1.);
+ gl_Position = matrix * vec4(vPosRot.x, vPosRot.y, 0., 1.);
#elif defined(POINT)
gl_PointSize = 0.;
#else
- gl_Position = ubuf.matrix * vec4(vPos.x, vPos.y, 0., 1.);
+ gl_Position = matrix * vec4(vPos.x, vPos.y, 0., 1.);
#endif
} else {
@@ -146,7 +154,7 @@ void main()
+ vVec.xy * t * vData.y // apply velocity
+ 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration
#endif
- gl_Position = ubuf.matrix * vec4(pos.x, pos.y, 0, 1);
+ gl_Position = matrix * vec4(pos.x, pos.y, 0, 1);
#if defined(COLOR)
fColor = vColor * fade;
diff --git a/src/particles/shaders_ng/imageparticle_colored.frag.qsb b/src/particles/shaders_ng/imageparticle_colored.frag.qsb
deleted file mode 100644
index 5dfc5d1d97..0000000000
--- a/src/particles/shaders_ng/imageparticle_colored.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_colored.vert.qsb b/src/particles/shaders_ng/imageparticle_colored.vert.qsb
deleted file mode 100644
index b2548042ca..0000000000
--- a/src/particles/shaders_ng/imageparticle_colored.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_coloredpoint.frag.qsb b/src/particles/shaders_ng/imageparticle_coloredpoint.frag.qsb
deleted file mode 100644
index 6e0373fd3b..0000000000
--- a/src/particles/shaders_ng/imageparticle_coloredpoint.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_coloredpoint.vert.qsb b/src/particles/shaders_ng/imageparticle_coloredpoint.vert.qsb
deleted file mode 100644
index 09b31c77c3..0000000000
--- a/src/particles/shaders_ng/imageparticle_coloredpoint.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_deformed.frag.qsb b/src/particles/shaders_ng/imageparticle_deformed.frag.qsb
deleted file mode 100644
index 3df7e49cd0..0000000000
--- a/src/particles/shaders_ng/imageparticle_deformed.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_deformed.vert.qsb b/src/particles/shaders_ng/imageparticle_deformed.vert.qsb
deleted file mode 100644
index 121e35fbb0..0000000000
--- a/src/particles/shaders_ng/imageparticle_deformed.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_simplepoint.frag.qsb b/src/particles/shaders_ng/imageparticle_simplepoint.frag.qsb
deleted file mode 100644
index 6199c48589..0000000000
--- a/src/particles/shaders_ng/imageparticle_simplepoint.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_simplepoint.vert.qsb b/src/particles/shaders_ng/imageparticle_simplepoint.vert.qsb
deleted file mode 100644
index ff8a5403d6..0000000000
--- a/src/particles/shaders_ng/imageparticle_simplepoint.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_sprite.frag.qsb b/src/particles/shaders_ng/imageparticle_sprite.frag.qsb
deleted file mode 100644
index 2df0734683..0000000000
--- a/src/particles/shaders_ng/imageparticle_sprite.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_sprite.vert.qsb b/src/particles/shaders_ng/imageparticle_sprite.vert.qsb
deleted file mode 100644
index 5ef575a868..0000000000
--- a/src/particles/shaders_ng/imageparticle_sprite.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_tabled.frag.qsb b/src/particles/shaders_ng/imageparticle_tabled.frag.qsb
deleted file mode 100644
index 08a82fe8ab..0000000000
--- a/src/particles/shaders_ng/imageparticle_tabled.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_tabled.vert.qsb b/src/particles/shaders_ng/imageparticle_tabled.vert.qsb
deleted file mode 100644
index febe5a495a..0000000000
--- a/src/particles/shaders_ng/imageparticle_tabled.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 548ef486f0..42c7c2332b 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -1,8 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from plugins.pro.
-
if(QT_FEATURE_qml_debug AND QT_FEATURE_thread)
add_subdirectory(qmltooling)
endif()
@@ -11,3 +9,7 @@ if(TARGET Qt::Quick)
endif()
add_subdirectory(qmllint)
+
+if(TARGET Qt::QmlLSPrivate)
+ add_subdirectory(qmlls)
+endif()
diff --git a/src/plugins/qmllint/quick/plugin.json b/src/plugins/qmllint/quick/plugin.json
index 442a693b7b..f6d6b5a55d 100644
--- a/src/plugins/qmllint/quick/plugin.json
+++ b/src/plugins/qmllint/quick/plugin.json
@@ -29,6 +29,22 @@
"name": "unexpected-var-type",
"settingsName": "UnexpectedVarType",
"description": "Warn about incompatible types being set on var properties"
+ },
+ {
+ "name": "property-changes-parsed",
+ "settingsName": "PropertyChangesParsed",
+ "description": "Warn about legacy PropertyChanges that rely on the custom parser"
+ },
+ {
+ "name": "controls-attached-property-reuse",
+ "settingsName": "ControlsAttachedPropertyReuse",
+ "description": "Warn about failure to re-use attached objects of controls"
+ },
+ {
+ "name": "attached-property-reuse",
+ "settingsName": "AttachedPropertyReuse",
+ "description": "Warn about failure to re-use any attached objects. Implies ControlsAttachedPropertyReuse.",
+ "enabled": false
}
]
}
diff --git a/src/plugins/qmllint/quick/quicklintplugin.cpp b/src/plugins/qmllint/quick/quicklintplugin.cpp
index 3b89635748..15b947a10c 100644
--- a/src/plugins/qmllint/quick/quicklintplugin.cpp
+++ b/src/plugins/qmllint/quick/quicklintplugin.cpp
@@ -3,17 +3,18 @@
#include "quicklintplugin.h"
-#include <QtQmlCompiler/private/qqmljslogger_p.h>
-
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-static constexpr LoggerWarningId quickLayoutPositioning { "Quick.layout-positioning" };
-static constexpr LoggerWarningId quickAttachedPropertyType { "Quick.attached-property-type" };
-static constexpr LoggerWarningId quickControlsNativeCustomize { "Quick.controls-native-customize" };
-static constexpr LoggerWarningId quickAnchorCombinations { "Quick.anchor-combinations" };
-static constexpr LoggerWarningId quickUnexpectedVarType { "Quick.unexpected-var-type" };
+static constexpr QQmlSA::LoggerWarningId quickLayoutPositioning { "Quick.layout-positioning" };
+static constexpr QQmlSA::LoggerWarningId quickAttachedPropertyType { "Quick.attached-property-type" };
+static constexpr QQmlSA::LoggerWarningId quickControlsNativeCustomize { "Quick.controls-native-customize" };
+static constexpr QQmlSA::LoggerWarningId quickAnchorCombinations { "Quick.anchor-combinations" };
+static constexpr QQmlSA::LoggerWarningId quickUnexpectedVarType { "Quick.unexpected-var-type" };
+static constexpr QQmlSA::LoggerWarningId quickPropertyChangesParsed { "Quick.property-changes-parsed" };
+static constexpr QQmlSA::LoggerWarningId quickControlsAttachedPropertyReuse { "Quick.controls-attached-property-reuse" };
+static constexpr QQmlSA::LoggerWarningId quickAttachedPropertyReuse { "Quick.attached-property-reuse" };
ForbiddenChildrenPropertyValidatorPass::ForbiddenChildrenPropertyValidatorPass(
QQmlSA::PassManager *manager)
@@ -33,11 +34,11 @@ void ForbiddenChildrenPropertyValidatorPass::addWarning(QAnyStringView moduleNam
bool ForbiddenChildrenPropertyValidatorPass::shouldRun(const QQmlSA::Element &element)
{
- if (!element->parentScope())
+ if (!element.parentScope())
return false;
- for (const auto pair : m_types.asKeyValueRange()) {
- if (element->parentScope()->inherits(pair.first))
+ for (const auto &pair : std::as_const(m_types).asKeyValueRange()) {
+ if (element.parentScope().inherits(pair.first))
return true;
}
@@ -46,18 +47,18 @@ bool ForbiddenChildrenPropertyValidatorPass::shouldRun(const QQmlSA::Element &el
void ForbiddenChildrenPropertyValidatorPass::run(const QQmlSA::Element &element)
{
- for (const auto elementPair : m_types.asKeyValueRange()) {
+ for (const auto &elementPair : std::as_const(m_types).asKeyValueRange()) {
const QQmlSA::Element &type = elementPair.first;
- if (!element->parentScope()->inherits(type))
+ if (!element.parentScope().inherits(type))
continue;
for (const auto &warning : elementPair.second) {
- if (!element->hasOwnPropertyBindings(warning.propertyName))
+ if (!element.hasOwnPropertyBindings(warning.propertyName))
continue;
- auto bindings = element->ownPropertyBindings(warning.propertyName);
-
- emitWarning(warning.message, quickLayoutPositioning, bindings.first->sourceLocation());
+ const auto bindings = element.ownPropertyBindings(warning.propertyName);
+ const auto firstBinding = bindings.constBegin().value();
+ emitWarning(warning.message, quickLayoutPositioning, firstBinding.sourceLocation());
}
break;
}
@@ -75,8 +76,7 @@ QString AttachedPropertyTypeValidatorPass::addWarning(TypeDescription attachType
QVarLengthArray<QQmlSA::Element, 4> elements;
const QQmlSA::Element baseType = resolveType(attachType.module, attachType.name);
-
- QString typeName = baseType->attachedTypeName();
+ const QQmlSA::Element attachedType = resolveAttached(attachType.module, attachType.name);
for (const TypeDescription &desc : allowedTypes) {
const QQmlSA::Element type = resolveType(desc.module, desc.name);
@@ -85,36 +85,39 @@ QString AttachedPropertyTypeValidatorPass::addWarning(TypeDescription attachType
elements.push_back(type);
}
- m_attachedTypes.insert({ std::make_pair<>(
- typeName, Warning { elements, allowInDelegate, warning.toString() }) });
+ m_attachedTypes.insert(
+ { std::make_pair<>(attachedType.internalId(),
+ Warning{ elements, allowInDelegate, warning.toString() }) });
- return typeName;
+ return attachedType.internalId();
}
void AttachedPropertyTypeValidatorPass::checkWarnings(const QQmlSA::Element &element,
const QQmlSA::Element &scopeUsedIn,
- const QQmlJS::SourceLocation &location)
+ const QQmlSA::SourceLocation &location)
{
- auto warning = m_attachedTypes.constFind(element->internalName());
+ auto warning = m_attachedTypes.constFind(element.internalId());
if (warning == m_attachedTypes.cend())
return;
for (const QQmlSA::Element &type : warning->allowedTypes) {
- if (scopeUsedIn->inherits(type))
+ if (scopeUsedIn.inherits(type))
return;
}
if (warning->allowInDelegate) {
- if (scopeUsedIn->isPropertyRequired(u"index"_s)
- || scopeUsedIn->isPropertyRequired(u"model"_s))
+ if (scopeUsedIn.isPropertyRequired(u"index"_s)
+ || scopeUsedIn.isPropertyRequired(u"model"_s))
return;
// If the scope is at the root level, we cannot know whether it will be used
// as a delegate or not.
- if (!scopeUsedIn->parentScope() || scopeUsedIn->parentScope()->internalName() == u"global"_s)
- return;
+ // ### TODO: add a method to check whether a scope is the global scope
+ // so that we do not need to use internalId
+ if (!scopeUsedIn.parentScope() || scopeUsedIn.parentScope().internalId() == u"global"_s)
+ return;
- for (const QQmlJSMetaPropertyBinding &binding :
- scopeUsedIn->parentScope()->propertyBindings(u"delegate"_s)) {
+ for (const QQmlSA::Binding &binding :
+ scopeUsedIn.parentScope().propertyBindings(u"delegate"_s)) {
if (!binding.hasObject())
continue;
if (binding.objectType() == scopeUsedIn)
@@ -127,26 +130,31 @@ void AttachedPropertyTypeValidatorPass::checkWarnings(const QQmlSA::Element &ele
void AttachedPropertyTypeValidatorPass::onBinding(const QQmlSA::Element &element,
const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding,
+ const QQmlSA::Binding &binding,
const QQmlSA::Element &bindingScope,
const QQmlSA::Element &value)
{
- Q_UNUSED(element)
- Q_UNUSED(propertyName)
- Q_UNUSED(bindingScope)
Q_UNUSED(value)
- checkWarnings(bindingScope->baseType(), element, binding.sourceLocation());
+ // We can only analyze simple attached bindings since we don't see
+ // the grouped and attached properties that lead up to this here.
+ //
+ // TODO: This is very crude.
+ // We should add API for grouped and attached properties.
+ if (propertyName.count(QLatin1Char('.')) > 1)
+ return;
+
+ checkWarnings(bindingScope.baseType(), element, binding.sourceLocation());
}
void AttachedPropertyTypeValidatorPass::onRead(const QQmlSA::Element &element,
const QString &propertyName,
const QQmlSA::Element &readScope,
- QQmlJS::SourceLocation location)
+ QQmlSA::SourceLocation location)
{
// If the attachment does not have such a property or method then
// it's either a more general error or an enum. Enums are fine.
- if (element->hasProperty(propertyName) || element->hasMethod(propertyName))
+ if (element.hasProperty(propertyName) || element.hasMethod(propertyName))
checkWarnings(element, readScope, location);
}
@@ -154,7 +162,7 @@ void AttachedPropertyTypeValidatorPass::onWrite(const QQmlSA::Element &element,
const QString &propertyName,
const QQmlSA::Element &value,
const QQmlSA::Element &writeScope,
- QQmlJS::SourceLocation location)
+ QQmlSA::SourceLocation location)
{
Q_UNUSED(propertyName)
Q_UNUSED(value)
@@ -177,7 +185,6 @@ ControlsNativeValidatorPass::ControlsNativeValidatorPass(QQmlSA::PassManager *ma
QStringList { "background", "contentItem", "header", "footer", "menuBar" } },
ControlElement { "ComboBox", QStringList { "indicator" } },
ControlElement { "Dial", QStringList { "handle" } },
- ControlElement { "Dialog", QStringList { "header", "footer" } },
ControlElement { "GroupBox", QStringList { "label" } },
ControlElement { "$internal$.QQuickIndicatorButton", QStringList { "indicator" }, false },
ControlElement { "Label", QStringList { "background" } },
@@ -205,7 +212,7 @@ ControlsNativeValidatorPass::ControlsNativeValidatorPass(QQmlSA::PassManager *ma
if (type.isNull())
continue;
- element.inheritsControl = !element.isControl && type->inherits(control);
+ element.inheritsControl = !element.isControl && type.inherits(control);
element.element = type;
}
@@ -221,7 +228,7 @@ bool ControlsNativeValidatorPass::shouldRun(const QQmlSA::Element &element)
// If our element inherits control, we don't have to individually check for them here.
if (controlElement.inheritsControl)
continue;
- if (element->inherits(controlElement.element))
+ if (element.inherits(controlElement.element))
return true;
}
return false;
@@ -230,16 +237,16 @@ bool ControlsNativeValidatorPass::shouldRun(const QQmlSA::Element &element)
void ControlsNativeValidatorPass::run(const QQmlSA::Element &element)
{
for (const ControlElement &controlElement : m_elements) {
- if (element->inherits(controlElement.element)) {
+ if (element.inherits(controlElement.element)) {
for (const QString &propertyName : controlElement.restrictedProperties) {
- if (element->hasOwnPropertyBindings(propertyName)) {
+ if (element.hasOwnPropertyBindings(propertyName)) {
emitWarning(QStringLiteral("Not allowed to override \"%1\" because native "
"styles cannot be customized: See "
"https://doc-snapshots.qt.io/qt6-dev/"
"qtquickcontrols-customize.html#customization-"
"reference for more information.")
.arg(propertyName),
- quickControlsNativeCustomize, element->sourceLocation());
+ quickControlsNativeCustomize, element.sourceLocation());
}
}
// Since all the different types we have rules for don't inherit from each other (except
@@ -253,14 +260,14 @@ void ControlsNativeValidatorPass::run(const QQmlSA::Element &element)
AnchorsValidatorPass::AnchorsValidatorPass(QQmlSA::PassManager *manager)
: QQmlSA::ElementPass(manager)
+ , m_item(resolveType("QtQuick", "Item"))
{
- m_item = resolveType("QtQuick", "Item");
}
bool AnchorsValidatorPass::shouldRun(const QQmlSA::Element &element)
{
- return !m_item.isNull() && element->inherits(m_item)
- && element->hasOwnPropertyBindings(u"anchors"_s);
+ return !m_item.isNull() && element.inherits(m_item)
+ && element.hasOwnPropertyBindings(u"anchors"_s);
}
void AnchorsValidatorPass::run(const QQmlSA::Element &element)
@@ -272,21 +279,22 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element)
u"top"_s, u"bottom"_s, u"verticalCenter"_s,
u"baseline"_s };
- QList<QQmlJSMetaPropertyBinding> anchorBindings = element->propertyBindings(u"anchors"_s);
+ QList<QQmlSA::Binding> anchorBindings = element.propertyBindings(u"anchors"_s);
for (qsizetype i = anchorBindings.size() - 1; i >= 0; i--) {
auto groupType = anchorBindings[i].groupType();
- if (groupType == nullptr)
+ if (groupType.isNull())
continue;
for (const QString &name : properties) {
- auto pair = groupType->ownPropertyBindings(name);
- if (pair.first == pair.second)
+
+ const auto &propertyBindings = groupType.ownPropertyBindings(name);
+ if (propertyBindings.begin() == propertyBindings.end())
continue;
+
bool isUndefined = false;
- for (auto it = pair.first; it != pair.second; it++) {
- if (it->bindingType() == QQmlJSMetaPropertyBinding::Script
- && it->scriptValueType() == QQmlJSMetaPropertyBinding::ScriptValue_Undefined) {
+ for (const auto &propertyBinding : propertyBindings) {
+ if (propertyBinding.hasUndefinedScriptValue()) {
isUndefined = true;
break;
}
@@ -299,14 +307,15 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element)
}
}
- auto ownSourceLocation = [&](QStringList properties) {
- QQmlJS::SourceLocation warnLoc;
+ auto ownSourceLocation = [&](QStringList properties) -> QQmlSA::SourceLocation {
+ QQmlSA::SourceLocation warnLoc;
+
for (const QString &name : properties) {
if (bindings[name] & Own) {
- QQmlSA::Element groupType = anchorBindings[0].groupType();
- auto bindingRange = groupType->ownPropertyBindings(name);
- Q_ASSERT(bindingRange.first != bindingRange.second);
- warnLoc = bindingRange.first->sourceLocation();
+ QQmlSA::Element groupType = QQmlSA::Element{ anchorBindings[0].groupType() };
+ auto bindings = groupType.ownPropertyBindings(name);
+ Q_ASSERT(bindings.begin() != bindings.end());
+ warnLoc = bindings.begin().value().sourceLocation();
break;
}
}
@@ -314,7 +323,7 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element)
};
if ((bindings[u"left"_s] & bindings[u"right"_s] & bindings[u"horizontalCenter"_s]) & Exists) {
- QQmlJS::SourceLocation warnLoc =
+ QQmlSA::SourceLocation warnLoc =
ownSourceLocation({ u"left"_s, u"right"_s, u"horizontalCenter"_s });
if (warnLoc.isValid()) {
@@ -325,7 +334,7 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element)
}
if ((bindings[u"top"_s] & bindings[u"bottom"_s] & bindings[u"verticalCenter"_s]) & Exists) {
- QQmlJS::SourceLocation warnLoc =
+ QQmlSA::SourceLocation warnLoc =
ownSourceLocation({ u"top"_s, u"bottom"_s, u"verticalCenter"_s });
if (warnLoc.isValid()) {
emitWarning("Cannot specify top, bottom, and verticalCenter anchors at the same time.",
@@ -335,7 +344,7 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element)
if ((bindings[u"baseline"_s] & (bindings[u"bottom"_s] | bindings[u"verticalCenter"_s]))
& Exists) {
- QQmlJS::SourceLocation warnLoc =
+ QQmlSA::SourceLocation warnLoc =
ownSourceLocation({ u"baseline"_s, u"bottom"_s, u"verticalCenter"_s });
if (warnLoc.isValid()) {
emitWarning("Baseline anchor cannot be used in conjunction with top, bottom, or "
@@ -347,37 +356,36 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element)
ControlsSwipeDelegateValidatorPass::ControlsSwipeDelegateValidatorPass(QQmlSA::PassManager *manager)
: QQmlSA::ElementPass(manager)
+ , m_swipeDelegate(resolveType("QtQuick.Controls", "SwipeDelegate"))
{
- m_swipeDelegate = resolveType("QtQuick.Controls", "SwipeDelegate");
}
bool ControlsSwipeDelegateValidatorPass::shouldRun(const QQmlSA::Element &element)
{
- return !m_swipeDelegate.isNull() && element->inherits(m_swipeDelegate);
+ return !m_swipeDelegate.isNull() && element.inherits(m_swipeDelegate);
}
void ControlsSwipeDelegateValidatorPass::run(const QQmlSA::Element &element)
{
for (const auto &property : { u"background"_s, u"contentItem"_s }) {
- auto bindings = element->ownPropertyBindings(property);
- for (auto it = bindings.first; it != bindings.second; it++) {
- if (!it->hasObject())
+ for (const auto &binding : element.ownPropertyBindings(property)) {
+ if (!binding.hasObject())
continue;
- const QQmlSA::Element element = it->objectType();
- const auto bindings = element->propertyBindings(u"anchors"_s);
+ const QQmlSA::Element element = QQmlSA::Element{ binding.objectType() };
+ const auto &bindings = element.propertyBindings(u"anchors"_s);
if (bindings.isEmpty())
continue;
- if (bindings.first().bindingType() != QQmlJSMetaPropertyBinding::GroupProperty)
+ if (bindings.first().bindingType() != QQmlSA::BindingType::GroupProperty)
continue;
auto anchors = bindings.first().groupType();
for (const auto &disallowed : { u"fill"_s, u"centerIn"_s, u"left"_s, u"right"_s }) {
- if (anchors->hasPropertyBindings(disallowed)) {
- QQmlJS::SourceLocation location;
- auto ownBindings = anchors->ownPropertyBindings(disallowed);
- if (ownBindings.first != ownBindings.second) {
- location = ownBindings.first->sourceLocation();
+ if (anchors.hasPropertyBindings(disallowed)) {
+ QQmlSA::SourceLocation location;
+ const auto &ownBindings = anchors.ownPropertyBindings(disallowed);
+ if (ownBindings.begin() != ownBindings.end()) {
+ location = ownBindings.begin().value().sourceLocation();
}
emitWarning(
@@ -391,30 +399,31 @@ void ControlsSwipeDelegateValidatorPass::run(const QQmlSA::Element &element)
}
}
- auto swipe = element->ownPropertyBindings(u"swipe"_s);
- if (swipe.first == swipe.second)
+ const auto &swipe = element.ownPropertyBindings(u"swipe"_s);
+ if (swipe.begin() == swipe.end())
return;
- if (swipe.first->bindingType() != QQmlJSMetaPropertyBinding::GroupProperty)
+ const auto firstSwipe = swipe.begin().value();
+ if (firstSwipe.bindingType() != QQmlSA::BindingType::GroupProperty)
return;
- auto group = swipe.first->groupType();
+ auto group = firstSwipe.groupType();
- const std::array ownDirBindings = { group->ownPropertyBindings(u"right"_s),
- group->ownPropertyBindings(u"left"_s),
- group->ownPropertyBindings(u"behind"_s) };
+ const std::array ownDirBindings = { group.ownPropertyBindings(u"right"_s),
+ group.ownPropertyBindings(u"left"_s),
+ group.ownPropertyBindings(u"behind"_s) };
auto ownBindingIterator =
std::find_if(ownDirBindings.begin(), ownDirBindings.end(),
- [](const auto &pair) { return pair.first != pair.second; });
+ [](const auto &bindings) { return bindings.begin() != bindings.end(); });
if (ownBindingIterator == ownDirBindings.end())
return;
- if (group->hasPropertyBindings(u"behind"_s)
- && (group->hasPropertyBindings(u"right"_s) || group->hasPropertyBindings(u"left"_s))) {
+ if (group.hasPropertyBindings(u"behind"_s)
+ && (group.hasPropertyBindings(u"right"_s) || group.hasPropertyBindings(u"left"_s))) {
emitWarning("SwipeDelegate: Cannot set both behind and left/right properties",
- quickAnchorCombinations, ownBindingIterator->first->sourceLocation());
+ quickAnchorCombinations, ownBindingIterator->begin().value().sourceLocation());
}
}
@@ -423,22 +432,14 @@ VarBindingTypeValidatorPass::VarBindingTypeValidatorPass(
const QMultiHash<QString, TypeDescription> &expectedPropertyTypes)
: QQmlSA::PropertyPass(manager)
{
- QMultiHash<QString, QQmlJSScope::ConstPtr> propertyTypes;
-
- for (const auto pair : expectedPropertyTypes.asKeyValueRange()) {
- QQmlSA::Element propType;
-
- if (!pair.second.module.isEmpty()) {
- propType = resolveType(pair.second.module, pair.second.name);
- if (propType.isNull())
- continue;
- } else {
- auto scope = QQmlJSScope::create();
- scope->setInternalName(pair.second.name);
- propType = scope;
- }
-
- propertyTypes.insert(pair.first, propType);
+ QMultiHash<QString, QQmlSA::Element> propertyTypes;
+
+ for (const auto &pair : expectedPropertyTypes.asKeyValueRange()) {
+ const QQmlSA::Element propType = pair.second.module.isEmpty()
+ ? resolveBuiltinType(pair.second.name)
+ : resolveType(pair.second.module, pair.second.name);
+ if (!propType.isNull())
+ propertyTypes.insert(pair.first, propType);
}
m_expectedPropertyTypes = propertyTypes;
@@ -446,7 +447,7 @@ VarBindingTypeValidatorPass::VarBindingTypeValidatorPass(
void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element,
const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding,
+ const QQmlSA::Binding &binding,
const QQmlSA::Element &bindingScope,
const QQmlSA::Element &value)
{
@@ -463,14 +464,14 @@ void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element,
if (!value.isNull()) {
bindingType = value;
} else {
- if (QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType())) {
+ if (QQmlSA::Binding::isLiteralBinding(binding.bindingType())) {
bindingType = resolveLiteralType(binding);
} else {
switch (binding.bindingType()) {
- case QQmlJSMetaPropertyBinding::Object:
- bindingType = binding.objectType();
+ case QQmlSA::BindingType::Object:
+ bindingType = QQmlSA::Element{ binding.objectType() };
break;
- case QQmlJSMetaPropertyBinding::Script:
+ case QQmlSA::BindingType::Script:
break;
default:
return;
@@ -479,11 +480,11 @@ void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element,
}
if (std::find_if(range.first, range.second,
- [&](const QQmlSA::Element &scope) { return bindingType->inherits(scope); })
+ [&](const QQmlSA::Element &scope) { return bindingType.inherits(scope); })
== range.second) {
- const bool bindingTypeIsComposite = bindingType->isComposite();
- if (bindingTypeIsComposite && !bindingType->baseType()) {
+ const bool bindingTypeIsComposite = bindingType.isComposite();
+ if (bindingTypeIsComposite && !bindingType.baseType()) {
/* broken module or missing import, there is nothing we
can really check here, as something is amiss. We
simply skip this binding, and assume that whatever
@@ -492,14 +493,13 @@ void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element,
*/
return;
}
- const QString bindingTypeName = QQmlJSScope::prettyName(
- bindingTypeIsComposite
- ? bindingType->baseType()->internalName()
- : bindingType->internalName());
+ const QString bindingTypeName =
+ bindingTypeIsComposite ? bindingType.baseType().name()
+ : bindingType.name();
QStringList expectedTypeNames;
for (auto it = range.first; it != range.second; it++)
- expectedTypeNames << QQmlJSScope::prettyName(it.value()->internalName());
+ expectedTypeNames << it.value().name();
emitWarning(u"Unexpected type for property \"%1\" expected %2 got %3"_s.arg(
propertyName, expectedTypeNames.join(u", "_s), bindingTypeName),
@@ -507,9 +507,98 @@ void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element,
}
}
+void AttachedPropertyReuse::onRead(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &readScope,
+ QQmlSA::SourceLocation location)
+{
+ const auto range = usedAttachedTypes.equal_range(readScope);
+ const auto attachedTypeAndLocation = std::find_if(
+ range.first, range.second, [&](const ElementAndLocation &elementAndLocation) {
+ return elementAndLocation.element == element;
+ });
+ if (attachedTypeAndLocation != range.second) {
+ const QQmlSA::SourceLocation attachedLocation = attachedTypeAndLocation->location;
+
+ // Ignore enum accesses, as these will not cause the attached object to be created.
+ // Also ignore anything we cannot determine.
+ if (!element.hasProperty(propertyName) && !element.hasMethod(propertyName))
+ return;
+
+ for (QQmlSA::Element scope = readScope.parentScope(); !scope.isNull();
+ scope = scope.parentScope()) {
+ const auto range = usedAttachedTypes.equal_range(scope);
+ bool found = false;
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->element == element) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+
+ const QString id = resolveElementToId(scope, readScope);
+ const QQmlSA::SourceLocation idInsertLocation{ attachedLocation.offset(), 0,
+ attachedLocation.startLine(),
+ attachedLocation.startColumn() };
+ QQmlSA::FixSuggestion suggestion{ "Reference it by id instead:"_L1, idInsertLocation,
+ id.isEmpty() ? u"<id>."_s : (id + '.'_L1) };
+
+ if (id.isEmpty())
+ suggestion.setHint("You first have to give the element an id"_L1);
+ else
+ suggestion.setAutoApplicable();
+
+ emitWarning("Using attached type %1 already initialized in a parent scope."_L1.arg(
+ element.name()),
+ category, attachedLocation, suggestion);
+ }
+
+ return;
+ }
+
+ if (element.hasProperty(propertyName))
+ return; // an actual property
+
+ QQmlSA::Element type = resolveTypeInFileScope(propertyName);
+ QQmlSA::Element attached = resolveAttachedInFileScope(propertyName);
+ if (!type || !attached)
+ return;
+
+ if (category == quickControlsAttachedPropertyReuse) {
+ for (QQmlSA::Element parent = attached; parent; parent = parent.baseType()) {
+ // ### TODO: Make it possible to resolve QQuickAttachedPropertyPropagator
+ // so that we don't have to compare the internal id
+ if (parent.internalId() == "QQuickAttachedPropertyPropagator"_L1) {
+ usedAttachedTypes.insert(readScope, {attached, location});
+ break;
+ }
+ }
+
+ } else {
+ usedAttachedTypes.insert(readScope, {attached, location});
+ }
+}
+
+void AttachedPropertyReuse::onWrite(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
+ QQmlSA::SourceLocation location)
+{
+ Q_UNUSED(value);
+ onRead(element, propertyName, writeScope, location);
+}
+
void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
const QQmlSA::Element &rootElement)
{
+ const QQmlSA::LoggerWarningId attachedReuseCategory = [manager]() {
+ if (manager->isCategoryEnabled(quickAttachedPropertyReuse))
+ return quickAttachedPropertyReuse;
+ if (manager->isCategoryEnabled(qmlAttachedPropertyReuse))
+ return qmlAttachedPropertyReuse;
+ return quickControlsAttachedPropertyReuse;
+ }();
+
const bool hasQuick = manager->hasImportedModule("QtQuick");
const bool hasQuickLayouts = manager->hasImportedModule("QtQuick.Layouts");
const bool hasQuickControls = manager->hasImportedModule("QtQuick.Templates")
@@ -520,6 +609,7 @@ void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
if (hasQuick) {
manager->registerElementPass(std::make_unique<AnchorsValidatorPass>(manager));
+ manager->registerElementPass(std::make_unique<PropertyChangesValidatorPass>(manager));
auto forbiddenChildProperty =
std::make_unique<ForbiddenChildrenPropertyValidatorPass>(manager);
@@ -598,8 +688,12 @@ void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
addAttachedWarning({ "QtQuick.Layouts", "StackLayout" }, { { "QtQuick", "Item" } },
"StackLayout must be attached to an Item");
}
+
+
if (hasQuickControls) {
manager->registerElementPass(std::make_unique<ControlsSwipeDelegateValidatorPass>(manager));
+ manager->registerPropertyPass(std::make_unique<AttachedPropertyReuse>(
+ manager, attachedReuseCategory), "", "");
addAttachedWarning({ "QtQuick.Templates", "ScrollBar" },
{ { "QtQuick", "Flickable" }, { "QtQuick.Templates", "ScrollView" } },
@@ -629,6 +723,9 @@ void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
addVarBindingWarning("QtQuick.Templates", "SpinBox",
{ { "textFromValue", { "", "function" } },
{ "valueFromText", { "", "function" } } });
+ } else if (attachedReuseCategory != quickControlsAttachedPropertyReuse) {
+ manager->registerPropertyPass(std::make_unique<AttachedPropertyReuse>(
+ manager, attachedReuseCategory), "", "");
}
if (manager->hasImportedModule(u"QtQuick.Controls.macOS"_s)
@@ -636,6 +733,67 @@ void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
manager->registerElementPass(std::make_unique<ControlsNativeValidatorPass>(manager));
}
+PropertyChangesValidatorPass::PropertyChangesValidatorPass(QQmlSA::PassManager *manager)
+ : QQmlSA::ElementPass(manager)
+ , m_propertyChanges(resolveType("QtQuick", "PropertyChanges"))
+{
+}
+
+bool PropertyChangesValidatorPass::shouldRun(const QQmlSA::Element &element)
+{
+ return !m_propertyChanges.isNull() && element.inherits(m_propertyChanges);
+}
+
+void PropertyChangesValidatorPass::run(const QQmlSA::Element &element)
+{
+ const QQmlSA::Binding::Bindings bindings = element.ownPropertyBindings();
+
+ const auto target =
+ std::find_if(bindings.constBegin(), bindings.constEnd(),
+ [](const auto binding) { return binding.propertyName() == u"target"_s; });
+ if (target == bindings.constEnd())
+ return;
+
+ QString targetId = u"<id>"_s;
+ const auto targetLocation = target.value().sourceLocation();
+ const QString targetBinding = sourceCode(targetLocation);
+ const QQmlSA::Element targetElement = resolveIdToElement(targetBinding, element);
+ if (!targetElement.isNull())
+ targetId = targetBinding;
+
+ bool hadCustomParsedBindings = false;
+ for (auto it = bindings.constBegin(); it != bindings.constEnd(); ++it) {
+ const auto &propertyName = it.key();
+ const auto &propertyBinding = it.value();
+ if (element.hasProperty(propertyName))
+ continue;
+
+ const QQmlSA::SourceLocation bindingLocation = propertyBinding.sourceLocation();
+ if (!targetElement.isNull() && !targetElement.hasProperty(propertyName)) {
+ emitWarning(
+ "Unknown property \"%1\" in PropertyChanges."_L1.arg(propertyName),
+ quickPropertyChangesParsed, bindingLocation);
+ continue;
+ }
+
+ QString binding = sourceCode(bindingLocation);
+ if (binding.length() > 16)
+ binding = binding.left(13) + "..."_L1;
+
+ hadCustomParsedBindings = true;
+ emitWarning("Property \"%1\" is custom-parsed in PropertyChanges. "
+ "You should phrase this binding as \"%2.%1: %3\""_L1.arg(propertyName, targetId,
+ binding),
+ quickPropertyChangesParsed, bindingLocation);
+ }
+
+ if (hadCustomParsedBindings && !targetElement.isNull()) {
+ emitWarning("You should remove any bindings on the \"target\" property and avoid "
+ "custom-parsed bindings in PropertyChanges.",
+ quickPropertyChangesParsed, targetLocation);
+ }
+}
+
QT_END_NAMESPACE
#include "moc_quicklintplugin.cpp"
diff --git a/src/plugins/qmllint/quick/quicklintplugin.h b/src/plugins/qmllint/quick/quicklintplugin.h
index be368f0880..bdec509976 100644
--- a/src/plugins/qmllint/quick/quicklintplugin.h
+++ b/src/plugins/qmllint/quick/quicklintplugin.h
@@ -9,7 +9,8 @@
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qhash.h>
-#include <QtQmlCompiler/private/qqmlsa_p.h>
+#include <QtQmlCompiler/qqmlsa.h>
+#include "qqmlsaconstants.h"
QT_BEGIN_NAMESPACE
@@ -58,17 +59,17 @@ public:
bool allowInDelegate, QAnyStringView warning);
void onBinding(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope,
+ const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope,
const QQmlSA::Element &value) override;
void onRead(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override;
+ const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override;
void onWrite(const QQmlSA::Element &element, const QString &propertyName,
const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
- QQmlJS::SourceLocation location) override;
+ QQmlSA::SourceLocation location) override;
private:
void checkWarnings(const QQmlSA::Element &element, const QQmlSA::Element &scopeUsedIn,
- const QQmlJS::SourceLocation &location);
+ const QQmlSA::SourceLocation &location);
struct Warning
{
@@ -132,13 +133,53 @@ public:
const QMultiHash<QString, TypeDescription> &expectedPropertyTypes);
void onBinding(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope,
+ const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope,
const QQmlSA::Element &value) override;
private:
QMultiHash<QString, QQmlSA::Element> m_expectedPropertyTypes;
};
+class PropertyChangesValidatorPass : public QQmlSA::ElementPass
+{
+public:
+ PropertyChangesValidatorPass(QQmlSA::PassManager *manager);
+
+ bool shouldRun(const QQmlSA::Element &element) override;
+ void run(const QQmlSA::Element &element) override;
+
+private:
+ QQmlSA::Element m_propertyChanges;
+};
+
+class AttachedPropertyReuse : public QQmlSA::PropertyPass
+{
+public:
+ enum Mode {
+ CheckAll,
+ RestrictToControls
+ };
+
+ AttachedPropertyReuse(QQmlSA::PassManager *manager, QQmlSA::LoggerWarningId category)
+ : QQmlSA::PropertyPass(manager), category(category)
+ {}
+
+ void onRead(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override;
+ void onWrite(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
+ QQmlSA::SourceLocation location) override;
+
+private:
+ struct ElementAndLocation {
+ QQmlSA::Element element;
+ QQmlSA::SourceLocation location;
+ };
+
+ QMultiHash<QQmlSA::Element, ElementAndLocation> usedAttachedTypes;
+ QQmlSA::LoggerWarningId category;
+};
+
QT_END_NAMESPACE
#endif // QUICKLINTPLUGIN_H
diff --git a/src/plugins/qmlls/CMakeLists.txt b/src/plugins/qmlls/CMakeLists.txt
new file mode 100644
index 0000000000..32e53a51c4
--- /dev/null
+++ b/src/plugins/qmlls/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (TARGET Qt::Quick)
+ add_subdirectory(quick)
+endif()
diff --git a/src/plugins/qmlls/quick/CMakeLists.txt b/src/plugins/qmlls/quick/CMakeLists.txt
new file mode 100644
index 0000000000..d306956027
--- /dev/null
+++ b/src/plugins/qmlls/quick/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(QmlLSQuickPlugin
+ CLASS_NAME QQmlLSQuickPlugin
+ OUTPUT_NAME qmllsquickplugin
+ PLUGIN_TYPE qmlls
+ SOURCES
+ qqmllsquickplugin_p.h
+ qqmllsquickplugin.cpp
+ LIBRARIES
+ Qt::QmlCompilerPrivate
+ Qt::QmlLSPrivate
+)
diff --git a/src/plugins/qmlls/quick/plugin.json b/src/plugins/qmlls/quick/plugin.json
new file mode 100644
index 0000000000..42978a9968
--- /dev/null
+++ b/src/plugins/qmlls/quick/plugin.json
@@ -0,0 +1,8 @@
+{
+ "name": "QmlLSQuick",
+ "author": "Qt",
+ "description": "Provide QmlLS utilities for QtQuick",
+ "version": "1.0",
+ "isInternal": true,
+ "Keys" : [ "quick" ]
+}
diff --git a/src/plugins/qmlls/quick/qqmllsquickplugin.cpp b/src/plugins/qmlls/quick/qqmllsquickplugin.cpp
new file mode 100644
index 0000000000..67b95b074a
--- /dev/null
+++ b/src/plugins/qmlls/quick/qqmllsquickplugin.cpp
@@ -0,0 +1,187 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmllsquickplugin_p.h"
+#include <QtQmlLS/private/qqmllsutils_p.h>
+#include <QtQmlLS/private/qqmllscompletion_p.h>
+
+using namespace QLspSpecification;
+using namespace QQmlJS::Dom;
+
+QT_BEGIN_NAMESPACE
+
+std::unique_ptr<QQmlLSCompletionPlugin> QQmlLSQuickPlugin::createCompletionPlugin() const
+{
+ return std::make_unique<QQmlLSQuickCompletionPlugin>();
+}
+
+void QQmlLSQuickCompletionPlugin::suggestSnippetsForLeftHandSideOfBinding(
+ const DomItem &itemAtPosition, BackInsertIterator result) const
+{
+ auto file = itemAtPosition.containingFile().as<QmlFile>();
+ if (!file)
+ return;
+
+ // check if QtQuick has been imported
+ const auto &imports = file->imports();
+ auto it = std::find_if(imports.constBegin(), imports.constEnd(), [](const Import &import) {
+ return import.uri.moduleUri() == u"QtQuick";
+ });
+ if (it == imports.constEnd()) {
+ return;
+ }
+
+ // for default bindings:
+ suggestSnippetsForRightHandSideOfBinding(itemAtPosition, result);
+
+ // check if the user already typed some qualifier, remove its dot and compare it to QtQuick's
+ // qualified name
+ const QString userTypedQualifier = QQmlLSUtils::qualifiersFrom(itemAtPosition);
+ if (!userTypedQualifier.isEmpty()
+ && !it->importId.startsWith(QStringView(userTypedQualifier).chopped(1))) {
+ return;
+ }
+
+ const QByteArray prefixForSnippet =
+ userTypedQualifier.isEmpty() ? it->importId.toUtf8() : QByteArray();
+ const QByteArray prefixWithDotForSnippet =
+ prefixForSnippet.isEmpty() ? QByteArray() : QByteArray(prefixForSnippet).append(u'.');
+
+ auto resolver = file->typeResolver();
+ if (!resolver)
+ return;
+ const auto qquickItemScope = resolver->typeForName(prefixWithDotForSnippet + u"Item"_s);
+ const QQmlJSScope::ConstPtr ownerScope = itemAtPosition.qmlObject().semanticScope();
+ if (!ownerScope || !qquickItemScope)
+ return;
+
+ if (ownerScope->inherits(qquickItemScope)) {
+ result = QQmlLSCompletion::makeSnippet(
+ "states binding with PropertyChanges in State",
+ "states: [\n"
+ "\t"_ba.append(prefixWithDotForSnippet)
+ .append("State {\n"
+ "\t\tname: \"${1:name}\"\n"
+ "\t\t"_ba.append(prefixWithDotForSnippet)
+ .append("PropertyChanges {\n"
+ "\t\t\ttarget: ${2:object}\n"
+ "\t\t}\n"
+ "\t}\n"
+ "]")));
+ result = QQmlLSCompletion::makeSnippet("transitions binding with Transition",
+ "transitions: [\n"
+ "\t"_ba.append(prefixWithDotForSnippet)
+ .append("Transition {\n"
+ "\t\tfrom: \"${1:fromState}\"\n"
+ "\t\tto: \"${2:fromState}\"\n"
+ "\t}\n"
+ "]"));
+ }
+}
+
+void QQmlLSQuickCompletionPlugin::suggestSnippetsForRightHandSideOfBinding(
+ const DomItem &itemAtPosition, BackInsertIterator result) const
+{
+ auto file = itemAtPosition.containingFile().as<QmlFile>();
+ if (!file)
+ return;
+
+ // check if QtQuick has been imported
+ const auto &imports = file->imports();
+ auto it = std::find_if(imports.constBegin(), imports.constEnd(), [](const Import &import) {
+ return import.uri.moduleUri() == u"QtQuick";
+ });
+ if (it == imports.constEnd()) {
+ return;
+ }
+
+ // check if the user already typed some qualifier, remove its dot and compare it to QtQuick's
+ // qualified name
+ const QString userTypedQualifier = QQmlLSUtils::qualifiersFrom(itemAtPosition);
+ if (!userTypedQualifier.isEmpty()
+ && !it->importId.startsWith(QStringView(userTypedQualifier).chopped(1))) {
+ return;
+ }
+
+ const QByteArray prefixForSnippet =
+ userTypedQualifier.isEmpty() ? it->importId.toUtf8() : QByteArray();
+ const QByteArray prefixWithDotForSnippet =
+ prefixForSnippet.isEmpty() ? QByteArray() : QByteArray(prefixForSnippet).append(u'.');
+
+ // Quick completions from Qt Creator's code model
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "BorderImage snippet",
+ "BorderImage {\n"
+ "\tid: ${1:name}\n"
+ "\tsource: \"${2:file}\"\n"
+ "\twidth: ${3:100}; height: ${4:100}\n"
+ "\tborder.left: ${5: 5}; border.top: ${5}\n"
+ "\tborder.right: ${5}; border.bottom: ${5}\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "ColorAnimation snippet",
+ "ColorAnimation {\n"
+ "\tfrom: \"${1:white}\"\n"
+ "\tto: \"${2:black}\"\n"
+ "\tduration: ${3:200}\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "Image snippet",
+ "Image {\n"
+ "\tid: ${1:name}\n"
+ "\tsource: \"${2:file}\"\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "Item snippet",
+ "Item {\n"
+ "\tid: ${1:name}\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "NumberAnimation snippet",
+ "NumberAnimation {\n"
+ "\ttarget: ${1:object}\n"
+ "\tproperty: \"${2:name}\"\n"
+ "\tduration: ${3:200}\n"
+ "\teasing.type: "_ba.append(prefixWithDotForSnippet)
+ .append("Easing.${4:InOutQuad}\n"
+ "}"));
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "NumberAnimation with targets snippet",
+ "NumberAnimation {\n"
+ "\ttargets: [${1:object}]\n"
+ "\tproperties: \"${2:name}\"\n"
+ "\tduration: ${3:200}\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "PauseAnimation snippet",
+ "PauseAnimation {\n"
+ "\tduration: ${1:200}\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "PropertyAction snippet",
+ "PropertyAction {\n"
+ "\ttarget: ${1:object}\n"
+ "\tproperty: \"${2:name}\"\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "PropertyAction with targets snippet",
+ "PropertyAction {\n"
+ "\ttargets: [${1:object}]\n"
+ "\tproperties: \"${2:name}\"\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "PropertyChanges snippet",
+ "PropertyChanges {\n"
+ "\ttarget: ${1:object}\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "State snippet",
+ "State {\n"
+ "\tname: ${1:name}\n"
+ "\t"_ba.append(prefixWithDotForSnippet)
+ .append("PropertyChanges {\n"
+ "\t\ttarget: ${2:object}\n"
+ "\t}\n"
+ "}"));
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "Text snippet",
+ "Text {\n"
+ "\tid: ${1:name}\n"
+ "\ttext: qsTr(\"${2:text}\")\n"
+ "}");
+ result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "Transition snippet",
+ "Transition {\n"
+ "\tfrom: \"${1:fromState}\"\n"
+ "\tto: \"${2:toState}\"\n"
+ "}");
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmlls/quick/qqmllsquickplugin_p.h b/src/plugins/qmlls/quick/qqmllsquickplugin_p.h
new file mode 100644
index 0000000000..da6bc6f827
--- /dev/null
+++ b/src/plugins/qmlls/quick/qqmllsquickplugin_p.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLLSQUICKPLUGIN_H
+#define QMLLSQUICKPLUGIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qplugin.h>
+
+#include <QtQmlLS/private/qqmllsplugin_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlLSQuickCompletionPlugin : public QQmlLSCompletionPlugin
+{
+public:
+ void suggestSnippetsForLeftHandSideOfBinding(const QQmlJS::Dom::DomItem &items,
+ BackInsertIterator result) const override;
+
+ void suggestSnippetsForRightHandSideOfBinding(const QQmlJS::Dom::DomItem &items,
+ BackInsertIterator result) const override;
+};
+
+
+class QQmlLSQuickPlugin : public QObject, QQmlLSPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QmlLSPluginInterface_iid FILE "plugin.json")
+ Q_INTERFACES(QQmlLSPlugin)
+public:
+ std::unique_ptr<QQmlLSCompletionPlugin> createCompletionPlugin() const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLLSQUICKPLUGIN_H
diff --git a/src/plugins/qmltooling/packetprotocol/CMakeLists.txt b/src/plugins/qmltooling/packetprotocol/CMakeLists.txt
index e0a9dd3a48..7a4c42101c 100644
--- a/src/plugins/qmltooling/packetprotocol/CMakeLists.txt
+++ b/src/plugins/qmltooling/packetprotocol/CMakeLists.txt
@@ -16,4 +16,5 @@ qt_internal_add_module(PacketProtocolPrivate
qversionedpacket_p.h
PUBLIC_LIBRARIES
Qt::CorePrivate
+ NO_GENERATE_CPP_EXPORTS
)
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
index 1fcc1e7772..3bd37878a8 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
@@ -15,6 +15,7 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlexpression_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmetaobject.h>
@@ -119,22 +120,14 @@ QDataStream &operator>>(QDataStream &ds,
return ds;
}
-static inline bool isSignalPropertyName(const QString &signalName)
-{
- // see QmlCompiler::isSignalPropertyName
- return signalName.size() >= 3 && signalName.startsWith(QLatin1String("on")) &&
- signalName.at(2).isLetter() && signalName.at(2).isUpper();
-}
-
static bool hasValidSignal(QObject *object, const QString &propertyName)
{
- if (!isSignalPropertyName(propertyName))
+ auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
+ if (!signalName)
return false;
- QString signalName = propertyName.mid(2);
- signalName[0] = signalName.at(0).toLower();
-
- int sigIdx = QQmlPropertyPrivate::findSignalByName(object->metaObject(), signalName.toLatin1()).methodIndex();
+ int sigIdx = QQmlPropertyPrivate::findSignalByName(object->metaObject(), signalName->toLatin1())
+ .methodIndex();
if (sigIdx == -1)
return false;
@@ -305,10 +298,8 @@ void QQmlEngineDebugServiceImpl::buildObjectDump(QDataStream &message,
if (scope) {
const QByteArray methodName = QMetaObjectPrivate::signal(scope->metaObject(),
signalHandler->signalIndex()).name();
- const QLatin1String methodNameStr(methodName);
- if (methodNameStr.size() != 0) {
- prop.name = QLatin1String("on") + QChar(methodNameStr.at(0)).toUpper()
- + methodNameStr.mid(1);
+ if (!methodName.isEmpty()) {
+ prop.name = QQmlSignalNames::signalNameToHandlerName(methodName);
}
}
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index 904749d7f6..90edf86acb 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -188,9 +188,10 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr)
QV4::ScopedValue v(scope);
QV4::Heap::InternalClass *ic = ctxt->internalClass();
for (uint i = 0; i < ic->size; ++i) {
- QString name = ic->keyAt(i);
- names.append(name);
- v = static_cast<QV4::Heap::CallContext *>(ctxt->d())->locals[i];
+ QV4::ScopedValue stringOrSymbol(scope, ic->keyAt(i));
+ QV4::ScopedString propName(scope, stringOrSymbol->toString(scope.engine));
+ names.append(propName->toQString());
+ v = ctxt->getProperty(propName);
collectedRefs.append(addValueRef(v));
}
@@ -220,7 +221,7 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int
frame[QLatin1String("debuggerFrame")] = false;
frame[QLatin1String("func")] = stackFrame.function;
frame[QLatin1String("script")] = stackFrame.source;
- frame[QLatin1String("line")] = stackFrame.line - 1;
+ frame[QLatin1String("line")] = qAbs(stackFrame.line) - 1;
if (stackFrame.column >= 0)
frame[QLatin1String("column")] = stackFrame.column;
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
index 3d7be2b2c1..a407153401 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
@@ -251,9 +251,9 @@ void QV4Debugger::pauseAndWait(PauseReason reason)
bool QV4Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr)
{
- QHash<BreakPoint, QString>::iterator it = m_breakPoints.find(
+ const auto it = m_breakPoints.constFind(
BreakPoint(QUrl(filename).fileName(), linenr));
- if (it == m_breakPoints.end())
+ if (it == m_breakPoints.cend())
return false;
QString condition = it.value();
if (condition.isEmpty())
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
index a1d07e6654..11e1a49512 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
@@ -48,7 +48,8 @@ void QV4DebuggerAgent::debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseR
// if (frame->column > 0)
// body.insert(QStringLiteral("sourceColumn"), frame->column);
QJsonArray breakPoints;
- foreach (int breakPointId, breakPointIds(frame->source(), frame->lineNumber()))
+ const QList<int> ids = breakPointIds(frame->source(), frame->lineNumber());
+ for (int breakPointId : ids)
breakPoints.push_back(breakPointId);
body.insert(QStringLiteral("breakpoints"), breakPoints);
script.insert(QStringLiteral("name"), frame->source());
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
index 6ed24f29d1..847954d3de 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
@@ -3,15 +3,18 @@
#include "qv4debugjob.h"
-#include <private/qv4script_p.h>
#include <private/qqmlcontext_p.h>
-#include <private/qv4qmlcontext_p.h>
-#include <private/qv4qobjectwrapper_p.h>
#include <private/qqmldebugservice_p.h>
#include <private/qv4jscall_p.h>
+#include <private/qv4qmlcontext_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4script_p.h>
+#include <private/qv4stackframe_p.h>
#include <QtQml/qqmlengine.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
QV4DebugJob::~QV4DebugJob()
@@ -229,7 +232,8 @@ GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine)
void GatherSourcesJob::run()
{
- for (QV4::ExecutableCompilationUnit *unit : engine->compilationUnits) {
+ const auto compilationUnits = engine->compilationUnits();
+ for (const auto &unit : compilationUnits) {
QString fileName = unit->fileName();
if (!fileName.isEmpty())
sources.append(fileName);
diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
index 10d52c75ee..84cd1236d9 100644
--- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
@@ -457,9 +457,10 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
QV4::Heap::InternalClass *ic = callContext->internalClass();
QV4::ScopedValue v(scope);
for (uint i = 0; i < ic->size; ++i) {
- QString name = ic->keyAt(i);
- v = callContext->d()->locals[i];
- collector.collect(&output, QString(), name, v);
+ QV4::ScopedValue stringOrSymbol(scope, ic->keyAt(i));
+ QV4::ScopedString propName(scope, stringOrSymbol->toString(scope.engine));
+ v = callContext->getProperty(propName);
+ collector.collect(&output, QString(), propName->toQString(), v);
}
}
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
index d983fdb1b5..5feaa17545 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp
@@ -374,7 +374,7 @@ void QQmlDebugTranslationServiceImpl::engineAboutToBeRemoved(QJSEngine *engine)
void QQmlDebugTranslationServiceImpl::foundTranslationBinding(const TranslationBindingInformation &translationBindingInformation)
{
QObject *scopeObject = translationBindingInformation.scopeObject;
- connect(scopeObject, &QObject::destroyed, [this, scopeObject] () {
+ connect(scopeObject, &QObject::destroyed, this, [this, scopeObject] () {
this->d->objectTranslationBindingMultiMap.remove(scopeObject);
});
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
index c4b42ed1be..30d6a9db5b 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
@@ -138,8 +138,8 @@ void QQmlPreviewBlacklist::Node::remove(const QString &path, int offset)
if (offset == path.size())
return;
- auto it = m_next.find(path.at(offset));
- if (it != m_next.end())
+ auto it = m_next.constFind(path.at(offset));
+ if (it != m_next.cend())
(*it)->remove(path, ++offset);
}
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
index fef0773613..f03de0aa08 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
@@ -40,12 +40,11 @@ bool isRootPath(const QString &path)
class QQmlPreviewFileEngineIterator : public QAbstractFileEngineIterator
{
public:
- QQmlPreviewFileEngineIterator(QDir::Filters filters, const QStringList &filterNames,
- const QStringList &m_entries);
+ QQmlPreviewFileEngineIterator(const QString &path, QDir::Filters filters,
+ const QStringList &filterNames, const QStringList &m_entries);
~QQmlPreviewFileEngineIterator();
- QString next() override;
- bool hasNext() const override;
+ bool advance() override;
QString currentFileName() const override;
private:
@@ -53,10 +52,11 @@ private:
int m_index;
};
-QQmlPreviewFileEngineIterator::QQmlPreviewFileEngineIterator(QDir::Filters filters,
+QQmlPreviewFileEngineIterator::QQmlPreviewFileEngineIterator(const QString &path,
+ QDir::Filters filters,
const QStringList &filterNames,
const QStringList &entries)
- : QAbstractFileEngineIterator(filters, filterNames), m_entries(entries), m_index(0)
+ : QAbstractFileEngineIterator(path, filters, filterNames), m_entries(entries), m_index(0)
{
}
@@ -64,17 +64,13 @@ QQmlPreviewFileEngineIterator::~QQmlPreviewFileEngineIterator()
{
}
-QString QQmlPreviewFileEngineIterator::next()
+bool QQmlPreviewFileEngineIterator::advance()
{
- if (!hasNext())
- return QString();
- ++m_index;
- return currentFilePath();
-}
+ if (m_index >= m_entries.size())
+ return false;
-bool QQmlPreviewFileEngineIterator::hasNext() const
-{
- return m_index < m_entries.size();
+ ++m_index;
+ return true;
}
QString QQmlPreviewFileEngineIterator::currentFileName() const
@@ -215,14 +211,15 @@ uint QQmlPreviewFileEngine::ownerId(QAbstractFileEngine::FileOwner owner) const
return m_fallback ? m_fallback->ownerId(owner) : static_cast<uint>(-2);
}
-QAbstractFileEngine::Iterator *QQmlPreviewFileEngine::beginEntryList(QDir::Filters filters,
- const QStringList &filterNames)
+QAbstractFileEngine::IteratorUniquePtr QQmlPreviewFileEngine::beginEntryList(
+ const QString &path, QDir::Filters filters, const QStringList &filterNames)
{
- return m_fallback ? m_fallback->beginEntryList(filters, filterNames)
- : new QQmlPreviewFileEngineIterator(filters, filterNames, m_entries);
+ return m_fallback ? m_fallback->beginEntryList(path, filters, filterNames)
+ : std::make_unique<QQmlPreviewFileEngineIterator>(
+ path, filters, filterNames, m_entries);
}
-QAbstractFileEngine::Iterator *QQmlPreviewFileEngine::endEntryList()
+QAbstractFileEngine::IteratorUniquePtr QQmlPreviewFileEngine::endEntryList()
{
return m_fallback ? m_fallback->endEntryList() : nullptr;
}
@@ -327,7 +324,7 @@ QString QQmlPreviewFileEngine::owner(FileOwner owner) const
return m_fallback ? m_fallback->owner(owner) : QString();
}
-QDateTime QQmlPreviewFileEngine::fileTime(FileTime time) const
+QDateTime QQmlPreviewFileEngine::fileTime(QFile::FileTime time) const
{
// Files we replace are always newer than the ones we had before. This makes the QML engine
// actually recompile them, rather than pick them from the cache.
@@ -378,7 +375,7 @@ void QQmlPreviewFileEngine::load() const
m_entries = m_loader->entries();
break;
case QQmlPreviewFileLoader::Fallback:
- m_fallback.reset(QAbstractFileEngine::create(m_name));
+ m_fallback = QAbstractFileEngine::create(m_name);
break;
case QQmlPreviewFileLoader::Unknown:
Q_UNREACHABLE();
@@ -391,7 +388,8 @@ QQmlPreviewFileEngineHandler::QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader
{
}
-QAbstractFileEngine *QQmlPreviewFileEngineHandler::create(const QString &fileName) const
+std::unique_ptr<QAbstractFileEngine> QQmlPreviewFileEngineHandler::create(
+ const QString &fileName) const
{
// Don't load compiled QML/JS over the network
if (fileName.endsWith(".qmlc") || fileName.endsWith(".jsc") || isRootPath(fileName)) {
@@ -407,8 +405,10 @@ QAbstractFileEngine *QQmlPreviewFileEngineHandler::create(const QString &fileNam
const QString absolute = relative.startsWith(':') ? relative : absolutePath(relative);
- return m_loader->isBlacklisted(absolute)
- ? nullptr : new QQmlPreviewFileEngine(relative, absolute, m_loader.data());
+ if (m_loader->isBlacklisted(absolute))
+ return {};
+
+ return std::make_unique<QQmlPreviewFileEngine>(relative, absolute, m_loader.data());
}
QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h
index 3d256e80e2..1705e89b5d 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h
@@ -17,10 +17,13 @@
#include "qqmlpreviewfileloader.h"
+#include <QtCore/qpointer.h>
#include <private/qabstractfileengine_p.h>
#include <private/qfsfileengine_p.h>
#include <QtCore/qbuffer.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QQmlPreviewFileEngine : public QAbstractFileEngine
@@ -42,8 +45,9 @@ public:
QString fileName(QAbstractFileEngine::FileName file) const override;
uint ownerId(FileOwner) const override;
- Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
- Iterator *endEntryList() override;
+ IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters,
+ const QStringList &filterNames) override;
+ IteratorUniquePtr endEntryList() override;
// Forwarding to fallback if exists
bool flush() override;
@@ -64,7 +68,7 @@ public:
bool setPermissions(uint perms) override;
QByteArray id() const override;
QString owner(FileOwner) const override;
- QDateTime fileTime(FileTime time) const override;
+ QDateTime fileTime(QFile::FileTime time) const override;
int handle() const override;
qint64 readLine(char *data, qint64 maxlen) override;
qint64 write(const char *data, qint64 len) override;
@@ -80,15 +84,16 @@ private:
mutable QBuffer m_contents;
mutable QStringList m_entries;
- mutable QScopedPointer<QAbstractFileEngine> m_fallback;
+ mutable std::unique_ptr<QAbstractFileEngine> m_fallback;
mutable QQmlPreviewFileLoader::Result m_result = QQmlPreviewFileLoader::Unknown;
};
class QQmlPreviewFileEngineHandler : public QAbstractFileEngineHandler
{
+ Q_DISABLE_COPY_MOVE(QQmlPreviewFileEngineHandler)
public:
QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader *loader);
- QAbstractFileEngine *create(const QString &fileName) const override;
+ std::unique_ptr<QAbstractFileEngine> create(const QString &fileName) const override;
private:
QPointer<QQmlPreviewFileLoader> m_loader;
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
index 7601c59cac..f32954ed34 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
@@ -13,7 +13,7 @@
#include <QtQuick/qquickitem.h>
#include <QtQml/qqmlcomponent.h>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qquickview_p.h>
#include <private/qhighdpiscaling_p.h>
@@ -23,7 +23,8 @@ struct QuitLockDisabler
{
const bool quitLockEnabled;
- QuitLockDisabler() : quitLockEnabled(QCoreApplication::isQuitLockEnabled())
+ Q_NODISCARD_CTOR QuitLockDisabler()
+ : quitLockEnabled(QCoreApplication::isQuitLockEnabled())
{
QCoreApplication::setQuitLockEnabled(false);
}
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
index cf20fc4768..48588c7973 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
@@ -56,14 +56,13 @@ QQmlPreviewPosition::QQmlPreviewPosition()
{
m_savePositionTimer.setSingleShot(true);
m_savePositionTimer.setInterval(500);
- QObject::connect(&m_savePositionTimer, &QTimer::timeout, [this]() {
+ QObject::connect(&m_savePositionTimer, &QTimer::timeout, &m_savePositionTimer, [this]() {
saveWindowPosition();
});
}
QQmlPreviewPosition::~QQmlPreviewPosition()
{
- saveWindowPosition();
}
void QQmlPreviewPosition::takePosition(QWindow *window, InitializeState state)
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
index e1e98537da..336395b131 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
@@ -10,7 +10,7 @@
#include <QtQuick/qquickitem.h>
#include <QtGui/qguiapplication.h>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qversionedpacket_p.h>
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
index be753ed10f..955ec7ecae 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
@@ -62,8 +62,8 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
if (d.locationId != 0)
ds << static_cast<qint64>(d.locationId);
} else {
- auto i = locations.find(d.locationId);
- if (i != locations.end()) {
+ auto i = locations.constFind(d.locationId);
+ if (i != locations.cend()) {
ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType);
ds << (i->url.isEmpty() ? i->location.sourceFile : i->url.toString())
<< static_cast<qint32>(i->location.line)
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
index 1a3e17fcbc..0b50ca9f0a 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
@@ -98,10 +98,10 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message
return finalizeMessages(until, messages, props.start, d);
appendMemoryEvents(props.start, messages, d);
- auto location = m_functionLocations.find(props.id);
+ auto location = m_functionLocations.constFind(props.id);
d << props.start << int(RangeStart) << int(Javascript) << static_cast<qint64>(props.id);
- if (location != m_functionLocations.end()) {
+ if (location != m_functionLocations.cend()) {
messages.push_back(d.squeezedData());
d.clear();
d << props.start << int(RangeLocation) << int(Javascript) << location->file << location->line
diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp
index 9107715f28..6e7c64460f 100644
--- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp
+++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.cpp
@@ -497,8 +497,8 @@ void QQmlDebugServerImpl::receiveMessage()
} else {
if (m_gotHello) {
- QHash<QString, QQmlDebugService *>::Iterator iter = m_plugins.find(name);
- if (iter == m_plugins.end()) {
+ const auto iter = m_plugins.constFind(name);
+ if (iter == m_plugins.cend()) {
qWarning() << "QML Debugger: Message received for missing plugin" << name << '.';
} else {
QQmlDebugService *service = *iter;
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h b/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
index fcbe8eb5b8..112f494df5 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
+++ b/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
@@ -71,6 +71,6 @@ public:
QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
};
-#endif // QSGOPENVGCONTEXT_H
-
QT_END_NAMESPACE
+
+#endif // QSGOPENVGCONTEXT_H
diff --git a/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp b/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
index a616c81be6..fbc0ac704b 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
@@ -386,6 +386,7 @@ QImage::Format qVGImageFormatToQImageFormat(VGImageFormat format)
break;
case VG_sL_8:
qImageFormat = QImage::Format_Grayscale8;
+ break;
default:
qImageFormat = QImage::Format_ARGB32;
break;
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index 1b00d685b6..cbcdc2f8f9 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -31,6 +31,55 @@ if(ANDROID)
"${INSTALL_CMAKE_NAMESPACE}AndroidQmlMacros.cmake")
endif()
+set_source_files_properties(
+ qml/qqmlcomponent.h qml/qqmlcomponent.cpp qml/qqmlcomponent_p.h
+ qml/qqmlcomponentattached_p.h
+ qml/qqmlscriptstring.h qml/qqmlscriptstring_p.h qml/qqmlscriptstring.cpp
+ PROPERTIES
+ SKIP_AUTOMOC TRUE
+)
+
+set(extra_manual_moc_deps "")
+if(QT_FEATURE_qml_network)
+ list(APPEND extra_manual_moc_deps Qt6::Network)
+endif()
+# We want QQmlComponent, QQmlComponentAttached, and QQmlScriptString to be available as metatypes in
+# the builtins, but without depending on QtQml and without duplicating the metaobjects.
+qt_manual_moc(extra_builtins_moc_files
+ OUTPUT_MOC_JSON_FILES extra_builtins_json_list
+ qml/qqmlcomponent.h
+ qml/qqmlcomponentattached_p.h
+ qml/qqmlscriptstring.h
+ TARGETS Qt6::Qml Qt6::Core ${extra_manual_moc_deps}
+)
+
+# The moc files are included directly by qqmlcomponent.cpp and qqmlscriptstring.cpp
+set_source_files_properties(${extra_builtins_moc_files} PROPERTIES HEADER_FILE_ONLY ON)
+
+qt_internal_add_module(QmlBuiltins
+ STATIC
+ NO_GENERATE_METATYPES # we do that manually below
+ SOURCES
+ qqmlbuiltins_p.h
+)
+
+add_custom_target(ExtraBuiltinsJson DEPENDS ${extra_builtins_json_list})
+add_custom_target(ExtraBuiltinsMocs DEPENDS ${extra_builtins_moc_files})
+
+target_link_libraries(QmlBuiltins PRIVATE Qt::Core Qt::QmlIntegration)
+
+# We generally need to copy the files into the build directory,
+# so that they are located together with the QML modules
+qt_path_join(qml_build_dir "${QT_BUILD_DIR}" "${INSTALL_QMLDIR}")
+
+# Simulate conditions that qt6_add_qml_module() would normally set up for us
+set_target_properties(QmlBuiltins PROPERTIES
+ QT_QML_MODULE_VERSION 1.0
+ QT_QML_MODULE_URI QML
+ QT_QML_MODULE_TYPEINFO builtins.qmltypes
+ QT_QML_MODULE_OUTPUT_DIRECTORY ${qml_build_dir}
+)
+
qt_internal_add_qml_module(Qml
URI "QtQml.Base"
VERSION "${PROJECT_VERSION}"
@@ -39,6 +88,8 @@ qt_internal_add_qml_module(Qml
PLUGIN_TARGET qmlplugin
CLASS_NAME QtQmlPlugin
PLUGIN_TYPES qmltooling
+ IMPORTS
+ QML/1.0
SOURCES
../3rdparty/masm/assembler/ARM64Assembler.h
../3rdparty/masm/assembler/ARMv7Assembler.cpp ../3rdparty/masm/assembler/ARMv7Assembler.h
@@ -124,10 +175,11 @@ qt_internal_add_qml_module(Qml
common/qqmljssourcelocation_p.h
common/qv4alloca_p.h
common/qv4calldata_p.h
- common/qv4compileddata_p.h
+ common/qv4compileddata.cpp common/qv4compileddata_p.h
common/qv4staticvalue_p.h
common/qv4stringtoarrayindex_p.h
common/qqmltranslation.cpp common/qqmltranslation_p.h
+ common/qqmlsignalnames_p.h common/qqmlsignalnames.cpp
compat/removed_api.cpp
compiler/qqmlirbuilder.cpp compiler/qqmlirbuilder_p.h
compiler/qv4bytecodegenerator.cpp compiler/qv4bytecodegenerator_p.h
@@ -147,6 +199,7 @@ qt_internal_add_qml_module(Qml
debugger/qqmlprofiler_p.h
inlinecomponentutils_p.h
jsapi/qjsengine.cpp jsapi/qjsengine.h jsapi/qjsengine_p.h
+ jsapi/qjslist.cpp jsapi/qjslist.h
jsapi/qjsmanagedvalue.cpp jsapi/qjsmanagedvalue.h
jsapi/qjsprimitivevalue.cpp jsapi/qjsprimitivevalue.h
jsapi/qjsvalue.cpp jsapi/qjsvalue.h jsapi/qjsvalue_p.h
@@ -202,6 +255,7 @@ qt_internal_add_qml_module(Qml
jsruntime/qv4property_p.h
jsruntime/qv4propertykey.cpp jsruntime/qv4propertykey_p.h
jsruntime/qv4proxy.cpp jsruntime/qv4proxy_p.h
+ jsruntime/qv4qmetaobjectwrapper.cpp jsruntime/qv4qmetaobjectwrapper_p.h
jsruntime/qv4qmlcontext.cpp jsruntime/qv4qmlcontext_p.h
jsruntime/qv4qobjectwrapper.cpp jsruntime/qv4qobjectwrapper_p.h
jsruntime/qv4reflect.cpp jsruntime/qv4reflect_p.h
@@ -234,7 +288,7 @@ qt_internal_add_qml_module(Qml
memory/qv4mm.cpp memory/qv4mm_p.h
memory/qv4mmdefs_p.h
memory/qv4stacklimits.cpp memory/qv4stacklimits_p.h
- memory/qv4writebarrier_p.h
+ memory/qv4writebarrier_p.h memory/qv4writebarrier.cpp
parser/qqmljsast.cpp parser/qqmljsast_p.h
parser/qqmljsastfwd_p.h
parser/qqmljsastvisitor.cpp parser/qqmljsastvisitor_p.h
@@ -267,6 +321,7 @@ qt_internal_add_qml_module(Qml
qml/qqmlboundsignal.cpp qml/qqmlboundsignal_p.h
qml/qqmlbuiltinfunctions.cpp qml/qqmlbuiltinfunctions_p.h
qml/qqmlcomponent.cpp qml/qqmlcomponent.h qml/qqmlcomponent_p.h
+ qml/qqmlcomponentandaliasresolver_p.h
qml/qqmlcomponentattached_p.h
qml/qqmlcontext.cpp qml/qqmlcontext.h qml/qqmlcontext_p.h
qml/qqmlcontextdata.cpp qml/qqmlcontextdata_p.h
@@ -340,7 +395,6 @@ qt_internal_add_qml_module(Qml
qml/qqmltypemodule.cpp qml/qqmltypemodule_p.h
qml/qqmltypemoduleversion.cpp qml/qqmltypemoduleversion_p.h
qml/qqmltypenamecache.cpp qml/qqmltypenamecache_p.h
- qml/qqmltypenotavailable.cpp qml/qqmltypenotavailable_p.h
qml/qqmltypewrapper.cpp qml/qqmltypewrapper_p.h
qml/qqmlvaluetype.cpp qml/qqmlvaluetype_p.h
qml/qqmlvaluetypeproxybinding.cpp qml/qqmlvaluetypeproxybinding_p.h
@@ -362,6 +416,31 @@ qt_internal_add_qml_module(Qml
qmltc/supportlibrary/qqmlcpponassignment_p.h
qmltc/supportlibrary/qqmlcpponassignment.cpp
qmltc/supportlibrary/qqmlcpptypehelpers_p.h
+ NO_UNITY_BUILD_SOURCES
+ jsapi/qjsvalue.cpp # \.
+ jsruntime/qv4engine.cpp # > WTF::String vs. QV4::String, dto. Object
+ jsruntime/qv4jsonobject.cpp # |
+ jsruntime/qv4object.cpp # |
+ jsruntime/qv4proxy.cpp # |
+ jsruntime/qv4qmlcontext.cpp # |
+ jsruntime/qv4reflect.cpp # |
+ jsruntime/qv4regexpobject.cpp # |
+ jsruntime/qv4script.cpp # | ((also?) Function)
+ jsruntime/qv4string.cpp # |
+ jsruntime/qv4stringobject.cpp # | ((also?) Property)
+ jsruntime/qv4typedarray.cpp # |
+ jsruntime/qv4urlobject.cpp # | ((also?) Property)
+ qml/qqmlbuiltinfunctions.cpp # |
+ qml/qqmllistwrapper.cpp # |
+ qml/qqmllocale.cpp # |
+ qml/qqmltypecompiler.cpp # | (also Binding)
+ qml/qqmltypewrapper.cpp # |
+ qml/qqmlxmlhttprequest.cpp # / (also Document)
+ jsruntime/qv4managed.cpp # QQmlJS::Managed vs. QV4::Managed
+ jsruntime/qv4module.cpp # QV4::Compiler::Module vs. QV4::Module
+ jsruntime/qv4vme_moth.cpp # 'MOTH_BEGIN_INSTR' macro redefined (from qv4instr_moth.cpp)
+ qml/qqmltypecompiler.cpp # 'COMPILE_EXCEPTION' macro redefined (from qqmlirbuilder.cpp)
+ qml/qqmlmetamoduleregistration.cpp # declares 'registration' clashing with qml_* files
DEFINES
BUILDING_QT__
ENABLE_ASSEMBLER_WX_EXCLUSIVE=1
@@ -379,9 +458,11 @@ qt_internal_add_qml_module(Qml
WTFReportBacktrace=qmlWTFReportBacktrace
WTF_EXPORT_PRIVATE= # special case
INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_BINARY_DIR}/.generated
${CMAKE_CURRENT_BINARY_DIR}/compiler
${CMAKE_CURRENT_BINARY_DIR}/jsruntime
${CMAKE_CURRENT_BINARY_DIR}/memory
+ ${CMAKE_CURRENT_BINARY_DIR}/qml
${CMAKE_CURRENT_BINARY_DIR}/qmldirparser
../3rdparty/masm
../3rdparty/masm/assembler
@@ -393,11 +474,11 @@ qt_internal_add_qml_module(Qml
../3rdparty/masm/stubs/runtime
../3rdparty/masm/stubs/wtf
../3rdparty/masm/wtf
- .generated
compiler
debugger
jsruntime
memory
+ qml
qmldirparser
LIBRARIES
Qt::CorePrivate
@@ -405,8 +486,10 @@ qt_internal_add_qml_module(Qml
PUBLIC_LIBRARIES
Qt::Core
Qt::QmlIntegration
+ Qt::QmlBuiltins
PRIVATE_MODULE_INTERFACE
Qt::CorePrivate
+ Qt::QmlBuiltinsPrivate
EXTRA_CMAKE_FILES
"${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}qmldirTemplate.cmake.in"
"${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}QmlPluginTemplate.cpp.in"
@@ -419,13 +502,83 @@ qt_internal_add_qml_module(Qml
"${INSTALL_CMAKE_NAMESPACE}QmlFindQmlscInternal.cmake"
${extra_cmake_includes}
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
POLICIES
QTP0001
+ QTP0004
)
+_qt_internal_add_qml_deploy_info_finalizer(Qml)
+
+target_include_directories(QmlBuiltins PRIVATE
+ $<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::QmlPrivate,INTERFACE_INCLUDE_DIRECTORIES>
+)
+
+set(builtins_typeregistration_args
+ MANUAL_MOC_JSON_FILES ${extra_builtins_json_list}
+ REGISTRATIONS_TARGET Qml
+)
+
+get_target_property(qt_namespace ${QT_CMAKE_EXPORT_NAMESPACE}::Core _qt_namespace)
+if(qt_namespace)
+ list(APPEND builtins_typeregistration_args NAMESPACE ${qt_namespace})
+endif()
+
+_qt_internal_qml_type_registration(QmlBuiltins ${builtins_typeregistration_args})
+add_dependencies(QmlBuiltins ExtraBuiltinsJson)
+
+_qt_internal_get_tool_wrapper_script_path(tool_wrapper)
+add_custom_command(
+ OUTPUT
+ "${qml_build_dir}/jsroot.qmltypes"
+ DEPENDS
+ ${QT_CMAKE_EXPORT_NAMESPACE}::qmltyperegistrar
+ COMMAND
+ ${tool_wrapper}
+ $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmltyperegistrar>
+ --jsroot --generate-qmltypes "${qml_build_dir}/jsroot.qmltypes"
+ COMMENT "Generating jsroot.qmltypes"
+ VERBATIM
+)
+
+get_target_property(builtins_metatypes_file "QmlBuiltins" INTERFACE_QT_META_TYPES_BUILD_FILE)
+
+_qt_internal_get_metatypes_install_dir(
+ ""
+ "${INSTALL_ARCHDATADIR}"
+ metatypes_install_dir
+)
+
+_qt_internal_assign_install_metatypes_files_and_properties(
+ QmlBuiltins
+ INSTALL_DIR "${metatypes_install_dir}"
+)
+
+qt_install(
+ FILES "${builtins_metatypes_file}"
+ DESTINATION "${metatypes_install_dir}"
+)
+
+set(builtins_output_files "")
+list(APPEND builtins_output_files "${qml_build_dir}/builtins.qmltypes")
+list(APPEND builtins_output_files "${qml_build_dir}/jsroot.qmltypes")
+
+add_custom_target(BuiltinsOutput DEPENDS ${builtins_output_files})
+
+qt_install(
+ FILES ${builtins_output_files}
+ DESTINATION "${INSTALL_QMLDIR}"
+)
+
+add_dependencies(Qml ExtraBuiltinsMocs)
+add_dependencies(Qml BuiltinsOutput)
+
qt_update_ignore_pch_source(Qml "compat/removed_api.cpp")
+set_source_files_properties(compat/removed_api.cpp
+ jsruntime/qv4mathobject.cpp # math.h issues on Windows
+ PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
+
+
qt_internal_add_qml_module(QmlMeta
URI "QtQml"
VERSION "${PROJECT_VERSION}"
@@ -463,6 +616,12 @@ qt_internal_add_resource(Qml "qmlMetaQmldir"
if(QT_FEATURE_qml_worker_script)
_qt_internal_add_qml_static_plugin_dependency(qmlplugin workerscriptplugin)
endif()
+# Same for the QmlMeta qml plugin, otherwise you get
+# module "QtQuick" version 6.6 cannot be imported because:
+# module "QtQml" plugin "qmlmetaplugin" not found
+# import QtQuick
+# ^
+_qt_internal_add_qml_static_plugin_dependency(qmlplugin QmlMeta)
# special case begin remove the block, handled manually
# QLALR Grammars:
@@ -499,8 +658,6 @@ qt_internal_extend_target(Qml CONDITION WIN32
SOURCES
../3rdparty/masm/wtf/OSAllocatorWin.cpp
jsruntime/qv4compilationunitmapper_win.cpp
- DEFINES
- NOMINMAX
PUBLIC_LIBRARIES
shell32
)
@@ -690,36 +847,15 @@ qt_internal_extend_target(Qml CONDITION DEFINES___contains___WTF_USE_UDIS86=1
../3rdparty/masm/disassembler/udis86/udis86_syn-intel.c
)
-#### Keys ignored in scope 72:.:../3rdparty/masm:../3rdparty/masm/masm.pri:DEFINES___contains___WTF_USE_UDIS86=1:
-# ITAB = "$$PWD/disassembler/udis86/optable.xml"
-# QMAKE_EXTRA_COMPILERS = "udis86"
-# QMAKE_EXTRA_TARGETS = "udis86_tab_cfile"
-# udis86.CONFIG = "no_link"
-# udis86.commands = "$$QMAKE_PYTHON" "$$PWD/disassembler/udis86/itab.py" "${QMAKE_FILE_IN}"
-# udis86.input = "ITAB"
-# udis86.output = "udis86_itab.h"
-# udis86_tab_cfile.depends = "udis86_itab.h"
-# udis86_tab_cfile.target = "$$OUT_PWD/udis86_itab.c"
-
-#### Keys ignored in scope 74:.:../3rdparty/masm:../3rdparty/masm/masm.pri:(CMAKE_BUILD_TYPE STREQUAL Debug):
-# GENERATEDDIR = "$$GENERATEDDIR/debug"
-
-#### Keys ignored in scope 75:.:../3rdparty/masm:../3rdparty/masm/masm.pri:else:
-# GENERATEDDIR = "$$GENERATEDDIR/release"
-
-qt_internal_extend_target(Qml CONDITION (NOT c++11 AND NOT ICC) AND (CLANG)
- COMPILE_OPTIONS
- -Wno-c++0x-extensions
- -Wno-c++11-extensions
-)
-
-qt_internal_extend_target(Qml CONDITION (((NOT c++11 AND NOT ICC) AND (NOT (CLANG))) AND (GCC)) AND ((QT_COMPILER_VERSION_MAJOR STRGREATER 4) OR (QT_COMPILER_VERSION_MINOR STRGREATER 5))
- COMPILE_OPTIONS
- -Wno-c++0x-compat
+qt_internal_generate_tracepoints(Qml qml
+ SOURCES
+ qml/qqmlobjectcreator.cpp
+ qml/qqmlboundsignal.cpp
+ qml/qqmlbinding.cpp
+ qml/qqmltypeloader.cpp
+ jsruntime/qv4vme_moth.cpp
)
-
-qt_internal_create_tracepoints(Qml qtqml.tracepoints)
qt_internal_add_docs(Qml
doc/qtqml.qdocconf
)
@@ -733,3 +869,16 @@ if(QT_FEATURE_private_tests AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.19" AND Q
qt_autogen_tools(qt_target_qml_sources_example ENABLE_AUTOGEN_TOOLS moc)
qt_autogen_tools(qt_target_qml_sources_exampleplugin ENABLE_AUTOGEN_TOOLS moc)
endif()
+
+# The Qml_sync_headers target doesn't exist when this CMakeLists is executed. So the qmlplugin
+# plugin target misses the dependency on Qml_sync_headers target. This leads to the
+# "unknown IID" issue when moc processes plugin sources, because of missing header aliases.
+# Qml_sync_headers target is created later by the finalizer in the directory scope so we add this
+# dependency manually instead of relying on qt_internal_add_qml_module logic. Same is applicable
+# for the QmlMeta and the QmlBuiltins target.
+set_property(TARGET qmlplugin APPEND PROPERTY AUTOGEN_TARGET_DEPENDS
+ Qml_sync_headers)
+set_property(TARGET QmlMeta APPEND PROPERTY AUTOGEN_TARGET_DEPENDS
+ Qml_sync_headers)
+set_property(TARGET QmlBuiltins APPEND PROPERTY AUTOGEN_TARGET_DEPENDS
+ Qml_sync_headers)
diff --git a/src/qml/Qt6AndroidQmlMacros.cmake b/src/qml/Qt6AndroidQmlMacros.cmake
index 0463fcf0a5..5f6f01fd76 100644
--- a/src/qml/Qt6AndroidQmlMacros.cmake
+++ b/src/qml/Qt6AndroidQmlMacros.cmake
@@ -1,10 +1,71 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# The function collects qml root paths and sets the QT_QML_ROOT_PATH property to the ${target}
-# based on the provided qml source files.
+# Recursively scans the potential_qml_modules given as list of targets by LINK_LIBRARIES. Examines
+# .qml files given as part of the QT_QML_MODULE_QML_FILES target property, and collects their
+# directories in out_var. These directories can be used as "root path" for qmlimportscanner.
+function(_qt_internal_find_qml_root_paths potential_qml_modules out_var)
+ set(qml_root_paths "")
+ set(processed "")
+
+ set(potential_qml_modules_queue ${potential_qml_modules})
+ while(TRUE)
+ list(LENGTH potential_qml_modules_queue length)
+ if(${length} STREQUAL "0")
+ break()
+ endif()
+
+ list(POP_FRONT potential_qml_modules_queue lib)
+
+ if(NOT TARGET ${lib})
+ continue()
+ endif()
+
+ list(FIND processed ${lib} found)
+ if(${found} GREATER_EQUAL "0")
+ continue()
+ endif()
+
+ get_target_property(root_paths ${lib} _qt_internal_qml_root_path)
+ if(root_paths)
+ foreach(root_path IN LISTS root_paths)
+ list(APPEND qml_root_paths "${root_path}")
+ endforeach()
+ endif()
+
+ get_target_property(qml_files ${lib} QT_QML_MODULE_QML_FILES)
+
+ foreach(qml_file IN LISTS qml_files)
+ get_filename_component(extension "${qml_file}" LAST_EXT)
+ if(NOT extension STREQUAL ".qml")
+ continue()
+ endif()
+
+ get_filename_component(dir "${qml_file}" DIRECTORY)
+ get_filename_component(absolute_dir "${dir}" ABSOLUTE)
+ list(APPEND qml_root_paths "${absolute_dir}")
+ endforeach()
+
+ # We have to consider all dependencies here, not only QML modules.
+ # Further QML modules may be indirectly linked via an intermediate library that is not
+ # a QML module.
+ get_target_property(dependencies ${lib} LINK_LIBRARIES)
+ foreach(dependency IN LISTS dependencies)
+ list(APPEND potential_qml_modules_queue ${dependency})
+ endforeach()
+
+ list(APPEND processed ${lib})
+ endwhile()
+
+ list(REMOVE_DUPLICATES qml_root_paths)
+ set(${out_var} "${qml_root_paths}" PARENT_SCOPE)
+endfunction()
+
+# The function collects qml root paths and sets the _qt_internal_qml_root_path property to the
+# ${target} based on the provided qml source files. _qt_internal_qml_root_path is used on purpose
+# to not trigger the the QTP0002 warning without user intention.
function(_qt_internal_collect_qml_root_paths target)
- get_target_property(qml_root_paths ${target} QT_QML_ROOT_PATH)
+ get_target_property(qml_root_paths ${target} _qt_internal_qml_root_path)
if(NOT qml_root_paths)
set(qml_root_paths "")
endif()
@@ -19,8 +80,17 @@ function(_qt_internal_collect_qml_root_paths target)
list(APPEND qml_root_paths "${absolute_dir}")
endforeach()
+ get_target_property(potential_qml_modules ${target} LINK_LIBRARIES)
+
+ _qt_internal_find_qml_root_paths(${potential_qml_modules} more_paths)
+ if(more_paths)
+ foreach(path IN LISTS more_paths)
+ list(APPEND qml_root_paths ${path})
+ endforeach()
+ endif()
+
list(REMOVE_DUPLICATES qml_root_paths)
- set_target_properties(${target} PROPERTIES QT_QML_ROOT_PATH "${qml_root_paths}")
+ set_target_properties(${target} PROPERTIES _qt_internal_qml_root_path "${qml_root_paths}")
endfunction()
# The function extracts the required QML properties from the 'target' and
@@ -42,10 +112,22 @@ function(_qt_internal_generate_android_qml_deployment_settings out_var target)
_qt_internal_add_android_deployment_multi_value_property(${out_var} "qml-import-paths"
${target} "_qt_native_qml_import_paths")
- # QML root paths
+ # Primitive QML root path: The target's source directory.
+ # We need this for backwards compatibility because people might not declare a proper QML module
+ # and instead add the .qml files as resources. In that case we won't see them below.
file(TO_CMAKE_PATH "${target_source_dir}" native_target_source_dir)
set_property(TARGET ${target} APPEND PROPERTY
_qt_android_native_qml_root_paths "${native_target_source_dir}")
+
+ # QML root paths, recursively across all linked libraries
+ set(root_paths ${target_source_dir})
+ _qt_internal_find_qml_root_paths(${target} root_paths)
+ foreach(root_path IN LISTS root_paths)
+ file(TO_CMAKE_PATH "${root_path}" native_root_path)
+ set_property(TARGET ${target} APPEND PROPERTY
+ _qt_android_native_qml_root_paths "${native_root_path}")
+ endforeach()
+
_qt_internal_add_android_deployment_list_property(${out_var} "qml-root-path"
${target} "_qt_android_native_qml_root_paths")
diff --git a/src/qml/Qt6QmlBuildInternals.cmake b/src/qml/Qt6QmlBuildInternals.cmake
index 915c71de6c..7b181fabde 100644
--- a/src/qml/Qt6QmlBuildInternals.cmake
+++ b/src/qml/Qt6QmlBuildInternals.cmake
@@ -94,7 +94,6 @@ function(qt_internal_add_qml_module target)
)
# TODO: Remove these once all repos have been updated to not use them
set(ignore_option_args
- SKIP_TYPE_REGISTRATION # Now always done
PLUGIN_OPTIONAL # Now the default
GENERATE_QMLTYPES # Now the default
INSTALL_QMLTYPES # Now the default
@@ -391,6 +390,25 @@ function(qt_internal_add_qml_module target)
FILES ${arg_OUTPUT_DIRECTORY}/$<TARGET_PROPERTY:${target},QT_QML_MODULE_TYPEINFO>
DESTINATION "${arg_INSTALL_DIRECTORY}"
)
+
+ # Assign the install time metatypes file of the backing library to the plugin.
+ # Only do it if the backing library is different from the plugin and we do generate
+ # qml types.
+ # The install time metatypes only apply to Qt's own qml plugins, not to user project
+ # qml plugins.
+ if(arg_PLUGIN_TARGET AND
+ TARGET "${arg_PLUGIN_TARGET}" AND NOT target STREQUAL arg_PLUGIN_TARGET)
+ _qt_internal_get_metatypes_install_dir(
+ ""
+ "${INSTALL_ARCHDATADIR}"
+ install_dir
+ )
+
+ _qt_internal_assign_install_metatypes_files_and_properties(
+ "${arg_PLUGIN_TARGET}"
+ INSTALL_DIR "${install_dir}"
+ )
+ endif()
endif()
if(NOT arg_NO_GENERATE_QMLDIR)
@@ -398,6 +416,18 @@ function(qt_internal_add_qml_module target)
FILES ${arg_OUTPUT_DIRECTORY}/qmldir
DESTINATION "${arg_INSTALL_DIRECTORY}"
)
+
+ get_target_property(extra_qmldirs ${target} _qt_internal_extra_qmldirs)
+ if(extra_qmldirs)
+ foreach(extra_qmldir IN LISTS extra_qmldirs)
+ __qt_get_relative_resource_path_for_file(qmldir_resource_path ${extra_qmldir})
+ get_filename_component(qmldir_dir ${qmldir_resource_path} DIRECTORY)
+ qt_install(
+ FILES ${extra_qmldir}
+ DESTINATION "${arg_INSTALL_DIRECTORY}/${qmldir_dir}"
+ )
+ endforeach()
+ endif()
endif()
if(arg_INSTALL_SOURCE_QMLTYPES)
diff --git a/src/qml/Qt6QmlConfigExtras.cmake.in b/src/qml/Qt6QmlConfigExtras.cmake.in
index 90d1868540..64228fddc3 100644
--- a/src/qml/Qt6QmlConfigExtras.cmake.in
+++ b/src/qml/Qt6QmlConfigExtras.cmake.in
@@ -5,16 +5,12 @@ if(NOT QT_NO_CREATE_TARGETS)
set(__qt_qml_target "${__qt_qml_aliased_target}")
endif()
if("@BUILD_SHARED_LIBS@")
- set_property(TARGET ${__qt_qml_target} PROPERTY
- INTERFACE_QT_EXECUTABLE_FINALIZERS
- _qt_internal_generate_deploy_qml_imports_script
- )
- else()
- set_property(TARGET ${__qt_qml_target} PROPERTY
- INTERFACE_QT_EXECUTABLE_FINALIZERS
- qt@PROJECT_VERSION_MAJOR@_import_qml_plugins
- )
+ _qt_internal_add_qml_deploy_info_finalizer("${__qt_qml_target}")
endif()
+ set_property(TARGET ${__qt_qml_target} APPEND PROPERTY
+ INTERFACE_QT_EXECUTABLE_FINALIZERS
+ qt@PROJECT_VERSION_MAJOR@_import_qml_plugins
+ )
unset(__qt_qml_target)
unset(__qt_qml_aliased_target)
endif()
diff --git a/src/qml/Qt6QmlDeploySupport.cmake b/src/qml/Qt6QmlDeploySupport.cmake
index a7319e8449..a230e71409 100644
--- a/src/qml/Qt6QmlDeploySupport.cmake
+++ b/src/qml/Qt6QmlDeploySupport.cmake
@@ -9,7 +9,7 @@
cmake_minimum_required(VERSION 3.16...3.21)
-function(qt_deploy_qml_imports)
+function(qt6_deploy_qml_imports)
set(no_value_options
NO_QT_IMPORTS
)
@@ -60,6 +60,21 @@ function(qt_deploy_qml_imports)
endfunction()
+if(NOT __QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_deploy_qml_imports)
+ if(__QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_deploy_qml_imports(${ARGV})
+
+ cmake_parse_arguments(PARSE_ARGV 0 arg "" "PLUGINS_FOUND" "")
+ if(arg_PLUGINS_FOUND)
+ set(${arg_PLUGINS_FOUND} ${${arg_PLUGINS_FOUND}} PARENT_SCOPE)
+ endif()
+ else()
+ message(FATAL_ERROR "qt_deploy_qml_imports() is only available in Qt 6.")
+ endif()
+ endfunction()
+endif()
+
function(_qt_internal_deploy_qml_imports_for_target)
set(no_value_options
BUNDLE
@@ -177,10 +192,33 @@ function(_qt_internal_deploy_qml_imports_for_target)
# supports symlinks (which all do in some form now, even
# Windows if the right permissions are set), but we only really
# expect to need this for macOS app bundles.
- set(final_destination "${dest_qmldir}/lib${entry_PLUGIN}.dylib")
+ if(DEFINED __QT_DEPLOY_TARGET_${entry_LINKTARGET}_FILE)
+ set(source_file "${__QT_DEPLOY_TARGET_${entry_LINKTARGET}_FILE}")
+ get_filename_component(source_file_name "${source_file}" NAME)
+ set(final_destination "${dest_qmldir}/${source_file_name}")
+ else()
+ # TODO: This is inconsistent with what we do further down below for the
+ # installation case. There we file(GLOB) any files we find, whereas here we
+ # build the path manually, because the file might not exist yet.
+ # Ideally both cases should use neither file(GLOB) nor manual path building,
+ # and instead rely on available target information or qmldir information.
+ # Currently that is not possible because we don't have all targets exposed
+ # via the __QT_DEPLOY_TARGET_{target} mechanism, only those that are built as
+ # part of the current project, and the qmldir -> qmlimportscanner does print
+ # the full file path, because there is one qmldir, but possibly 2+ plugins
+ # (debug and release).
+ set(plugin_suffix "")
+ if(__QT_DEPLOY_ACTIVE_CONFIG STREQUAL "Debug")
+ string(APPEND plugin_suffix "${__QT_DEPLOY_QT_DEBUG_POSTFIX}")
+ endif()
+
+ set(source_file "${entry_PATH}/lib${entry_PLUGIN}${plugin_suffix}.dylib")
+ set(final_destination "${dest_qmldir}/lib${entry_PLUGIN}${plugin_suffix}.dylib")
+ endif()
+
message(STATUS "Symlinking: ${final_destination}")
file(CREATE_LINK
- "${entry_PATH}/lib${entry_PLUGIN}.dylib"
+ "${source_file}"
"${final_destination}"
SYMBOLIC
)
@@ -192,9 +230,30 @@ function(_qt_internal_deploy_qml_imports_for_target)
continue()
endif()
- file(GLOB files LIST_DIRECTORIES false "${entry_PATH}/*${entry_PLUGIN}*")
- list(FILTER files
- INCLUDE REGEX "^(.*/)?(lib)?${entry_PLUGIN}.*\\.(so|dylib|dll)(\\.[0-9]+)*$")
+ # Deploy the plugin.
+ if(DEFINED __QT_DEPLOY_TARGET_${entry_LINKTARGET}_FILE)
+ set(files "${__QT_DEPLOY_TARGET_${entry_LINKTARGET}_FILE}")
+ else()
+ # We don't know the exact target file name. Fall back to file name heuristics.
+
+ # Construct a regular expression that matches the plugin's file name.
+ set(plugin_regex "^(.*/)?(lib)?${entry_PLUGIN}")
+ if(__QT_DEPLOY_QT_IS_MULTI_CONFIG_BUILD_WITH_DEBUG)
+ # If our application is a release build, do not match any debug suffix.
+ # If our application is a debug build, match exactly a debug suffix.
+ if(__QT_DEPLOY_ACTIVE_CONFIG STREQUAL "Debug")
+ string(APPEND plugin_regex "${__QT_DEPLOY_QT_DEBUG_POSTFIX}")
+ endif()
+ else()
+ # The Qt installation does only contain one build of the plugin. We match any
+ # possible debug suffix, or none.
+ string(APPEND plugin_regex ".*")
+ endif()
+ string(APPEND plugin_regex "\\.(so|dylib|dll)(\\.[0-9]+)*$")
+
+ file(GLOB files LIST_DIRECTORIES false "${entry_PATH}/*${entry_PLUGIN}*")
+ list(FILTER files INCLUDE REGEX "${plugin_regex}")
+ endif()
file(INSTALL ${files} DESTINATION "${install_plugin}" USE_SOURCE_PERMISSIONS)
get_filename_component(dest_plugin_abs "${dest_plugin}" ABSOLUTE)
@@ -212,6 +271,16 @@ function(_qt_internal_deploy_qml_imports_for_target)
endforeach()
endif()
+ # Install runtime dependencies on Windows.
+ if(__QT_DEPLOY_SYSTEM_NAME STREQUAL "Windows")
+ foreach(file IN LISTS __QT_DEPLOY_TARGET_${entry_LINKTARGET}_RUNTIME_DLLS)
+ if(__QT_DEPLOY_VERBOSE)
+ message(STATUS "runtime dependency for QML plugin '${entry_PLUGIN}':")
+ endif()
+ file(INSTALL ${file} DESTINATION "${QT_DEPLOY_PREFIX}/${QT_DEPLOY_BIN_DIR}")
+ endforeach()
+ endif()
+
if(__QT_DEPLOY_TOOL STREQUAL "GRD" AND __QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH)
# The RPATHs of the installed plugins do not match Qt's original lib directory.
# We must set the RPATH to point to QT_DEPLOY_LIBDIR.
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 6b3e8ffb08..a64e2eea6b 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -30,14 +30,14 @@ function(qt6_add_qml_module target)
NO_CACHEGEN
NO_RESOURCE_TARGET_PATH
NO_IMPORT_SCAN
- # TODO: Remove once all usages have also been removed
- SKIP_TYPE_REGISTRATION
ENABLE_TYPE_COMPILER
# Used to mark modules as having static side effects (i.e. if they install an image provider)
__QT_INTERNAL_STATIC_MODULE
# Used to mark modules as being a system module that provides all builtins
__QT_INTERNAL_SYSTEM_MODULE
+ # Give the resource for the qmldir a unique name; TODO: Remove once we can
+ __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE
)
set(args_single
@@ -58,6 +58,8 @@ function(qt6_add_qml_module target)
INSTALL_DIRECTORY
INSTALL_LOCATION
TYPE_COMPILER_NAMESPACE
+ QMLTC_EXPORT_DIRECTIVE
+ QMLTC_EXPORT_FILE_NAME
)
set(args_multi
@@ -108,12 +110,6 @@ function(qt6_add_qml_module target)
)
endif()
- if(arg_SKIP_TYPE_REGISTRATION)
- message(AUTHOR_WARNING
- "SKIP_TYPE_REGISTRATION is no longer used and will be ignored."
- )
- endif()
-
# Mandatory arguments
if (NOT arg_URI)
message(FATAL_ERROR
@@ -147,11 +143,32 @@ function(qt6_add_qml_module target)
)
endif()
- if (DEFINED arg_TYPE_COMPILER_NAMESPACE AND NOT arg_ENABLE_TYPE_COMPILER)
- message(WARNING
- "TYPE_COMPILER_NAMESPACE is set, but ENABLE_TYPE_COMPILER is not specified. "
- "The TYPE_COMPILER_NAMESPACE value will be ignored."
- )
+ if (NOT arg_ENABLE_TYPE_COMPILER)
+ if (DEFINED arg_TYPE_COMPILER_NAMESPACE)
+ message(WARNING
+ "TYPE_COMPILER_NAMESPACE is set, but ENABLE_TYPE_COMPILER is not specified. "
+ "The TYPE_COMPILER_NAMESPACE value will be ignored."
+ )
+ endif()
+
+ if (DEFINED arg_QMLTC_EXPORT_DIRECTIVE)
+ message(WARNING
+ "QMLTC_EXPORT_DIRECTIVE is set, but ENABLE_TYPE_COMPILER is not specified. "
+ "The QMLTC_EXPORT_DIRECTIVE value will be ignored."
+ )
+ endif()
+ if (DEFINED arg_QMLTC_EXPORT_FILE_NAME)
+ message(WARNING
+ "QMLTC_EXPORT_FILE_NAME is set, but ENABLE_TYPE_COMPILER is not specified. "
+ "The QMLTC_EXPORT_FILE_NAME will be ignored."
+ )
+ endif()
+ else()
+ if ((DEFINED arg_QMLTC_EXPORT_FILE_NAME) AND (NOT (DEFINED arg_QMLTC_EXPORT_DIRECTIVE)))
+ message(FATAL_ERROR
+ "Specifying a value for QMLTC_EXPORT_FILE_NAME also requires one for QMLTC_EXPORT_DIRECTIVE."
+ )
+ endif()
endif()
set(is_executable FALSE)
@@ -326,6 +343,7 @@ function(qt6_add_qml_module target)
)
endif()
endif()
+ set_property(GLOBAL APPEND PROPERTY _qt_all_qml_uris ${arg_URI})
set_property(GLOBAL APPEND PROPERTY _qt_all_qml_output_dirs ${arg_OUTPUT_DIRECTORY})
set_property(GLOBAL APPEND PROPERTY _qt_all_qml_targets ${target})
@@ -405,12 +423,12 @@ function(qt6_add_qml_module target)
QT_QML_MODULE_DEPENDENCIES "${dependency}"
)
else()
- string(SUBSTRING ${dependency} 0 ${slash_position} dep_module)
+ string(SUBSTRING ${dependency} 0 ${slash_position} dep_module_uri)
math(EXPR slash_position "${slash_position} + 1")
string(SUBSTRING ${dependency} ${slash_position} -1 dep_version)
if (dep_version MATCHES "^([0-9]+(\\.[0-9]+)?|auto)$")
set_property(TARGET ${target} APPEND PROPERTY
- QT_QML_MODULE_DEPENDENCIES "${dep_module} ${dep_version}"
+ QT_QML_MODULE_DEPENDENCIES "${dep_module_uri} ${dep_version}"
)
else()
message(FATAL_ERROR
@@ -420,6 +438,7 @@ function(qt6_add_qml_module target)
endif()
endif()
endforeach()
+ _qt_internal_collect_qml_module_dependencies(${target})
if(arg_AUTO_RESOURCE_PREFIX)
if(arg_RESOURCE_PREFIX)
@@ -429,6 +448,11 @@ function(qt6_add_qml_module target)
)
else()
set(arg_RESOURCE_PREFIX "/qt/qml")
+ message(DEPRECATION "AUTO_RESOURCE_PREFIX is deprecated. "
+ "Please use the qt_policy(SET) command to set the QTP0001 policy, "
+ "or use the qt_standard_project_setup() command to set your preferred "
+ "REQUIRES to get the preferred behavior. "
+ "Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details.")
endif()
elseif(arg_RESOURCE_PREFIX)
_qt_internal_canonicalize_resource_path("${arg_RESOURCE_PREFIX}" arg_RESOURCE_PREFIX)
@@ -504,49 +528,45 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
)
endif()
- set(ensure_set_properties
- QT_QML_MODULE_PLUGIN_TYPES_FILE
- QT_QML_MODULE_RESOURCES # Original files as provided by the project (absolute)
- QT_QML_MODULE_RESOURCE_PATHS # By qmlcachegen (resource paths)
- QT_QMLCACHEGEN_DIRECT_CALLS
- QT_QMLCACHEGEN_EXECUTABLE
- QT_QMLCACHEGEN_ARGUMENTS
- )
- foreach(prop IN LISTS ensure_set_properties)
- get_target_property(val ${target} ${prop})
- if("${val}" MATCHES "-NOTFOUND$")
- set_target_properties(${target} PROPERTIES ${prop} "")
- endif()
- endforeach()
-
if(NOT arg_NO_GENERATE_QMLTYPES)
set(type_registration_extra_args "")
if(arg_NAMESPACE)
list(APPEND type_registration_extra_args NAMESPACE ${arg_NAMESPACE})
endif()
+ set_target_properties(${target} PROPERTIES _qt_internal_has_qmltypes TRUE)
_qt_internal_qml_type_registration(${target} ${type_registration_extra_args})
+ else()
+ set_target_properties(${target} PROPERTIES _qt_internal_has_qmltypes FALSE)
endif()
set(output_targets)
if(NOT arg_NO_GENERATE_QMLDIR)
_qt_internal_target_generate_qmldir(${target})
+ set_source_files_properties(${arg_OUTPUT_DIRECTORY}/qmldir
+ PROPERTIES GENERATED TRUE
+ )
- # Embed qmldir in qrc. The following comments relate mostly to Qt5->6 transition.
- # The requirement to keep the same resource name might no longer apply, but it doesn't
- # currently appear to cause any hinderance to keep it.
- # The qmldir resource name needs to match the one generated by qmake's qml_module.prf, to
- # ensure that all Q_INIT_RESOURCE(resource_name) calls in Qt code don't lead to undefined
- # symbol errors when linking an application project.
- # The Q_INIT_RESOURCE() calls are not strictly necessary anymore because the CMake Qt
- # build passes around the compiled resources as object files.
- # These object files have global initiliazers that don't get discared when linked into
- # an application (as opposed to when the resource libraries were embedded into the static
- # libraries when Qt was built with qmake).
- # The reason to match the naming is to ensure that applications link successfully regardless
- # if Qt was built with CMake or qmake, while the build system transition phase is still
- # happening.
- string(REPLACE "/" "_" qmldir_resource_name "qmake_${arg_TARGET_PATH}")
+ if(${arg___QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE})
+ # TODO: Make this the default and remove the option
+ string(REPLACE "/" "_" qmldir_resource_name "${target}_qmldir_${arg_TARGET_PATH}")
+ else()
+ # Embed qmldir in qrc. The following comments relate mostly to Qt5->6 transition.
+ # The requirement to keep the same resource name might no longer apply, but it doesn't
+ # currently appear to cause any hinderance to keep it.
+ # The qmldir resource name needs to match the one generated by qmake's qml_module.prf,
+ # to ensure that all Q_INIT_RESOURCE(resource_name) calls in Qt code don't lead to
+ # undefined symbol errors when linking an application project.
+ # The Q_INIT_RESOURCE() calls are not strictly necessary anymore because the CMake Qt
+ # build passes around the compiled resources as object files.
+ # These object files have global initiliazers that don't get discared when linked into
+ # an application (as opposed to when the resource libraries were embedded into the
+ # static libraries when Qt was built with qmake).
+ # The reason to match the naming is to ensure that applications link successfully
+ # regardless if Qt was built with CMake or qmake, while the build system transition
+ # phase is still happening.
+ string(REPLACE "/" "_" qmldir_resource_name "qmake_${arg_TARGET_PATH}")
+ endif()
# The qmldir file ALWAYS has to be under the target path, even in the
# resources. If it isn't, an explicit import can't find it. We need a
@@ -573,6 +593,14 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
PREFIX "${prefix}"
OUTPUT_TARGETS resource_targets
)
+
+ # Save the resource name in a property so we can reference it later in a qml plugin
+ # constructor, to avoid discarding the resource if it's in a static library.
+ __qt_internal_sanitize_resource_name(
+ sanitized_qmldir_resource_name "${qmldir_resource_name}")
+ set_property(TARGET ${target} APPEND PROPERTY
+ _qt_qml_module_sanitized_resource_names "${sanitized_qmldir_resource_name}")
+
list(APPEND output_targets ${resource_targets})
# If we are adding the same file twice, we need a different resource
# name for the second one. It has the same QT_RESOURCE_ALIAS but a
@@ -670,6 +698,9 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
QML_FILES ${arg_QML_FILES}
IMPORT_PATHS ${arg_IMPORT_PATH}
NAMESPACE ${qmltc_namespace}
+ EXPORT_MACRO_NAME ${arg_QMLTC_EXPORT_DIRECTIVE}
+ EXPORT_FILE_NAME ${arg_QMLTC_EXPORT_FILE_NAME}
+ MODULE ${arg_URI}
)
endif()
@@ -677,6 +708,72 @@ Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html for policy details."
set(${arg_OUTPUT_TARGETS} ${output_targets} PARENT_SCOPE)
endif()
+
+ set(ensure_set_properties
+ QT_QML_MODULE_PLUGIN_TYPES_FILE
+ QT_QML_MODULE_QML_FILES
+ QT_QML_MODULE_RESOURCES # Original files as provided by the project (absolute)
+ QT_QML_MODULE_RESOURCE_PATHS # By qmlcachegen (resource paths)
+ QT_QMLCACHEGEN_DIRECT_CALLS
+ QT_QMLCACHEGEN_EXECUTABLE
+ QT_QMLCACHEGEN_ARGUMENTS
+ )
+ foreach(prop IN LISTS ensure_set_properties)
+ get_target_property(val ${target} ${prop})
+ if("${val}" MATCHES "-NOTFOUND$")
+ set_target_properties(${target} PROPERTIES ${prop} "")
+ endif()
+ endforeach()
+
+ if(${QT_QML_GENERATE_QMLLS_INI})
+ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.19.0")
+ # collect all build dirs obtained from all the qt_add_qml_module calls and
+ # write the .qmlls.ini file in a deferred call
+
+ if(NOT "${arg_OUTPUT_DIRECTORY}" STREQUAL "")
+ set(output_folder "${arg_OUTPUT_DIRECTORY}")
+ else()
+ set(output_folder "${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+ string(REPLACE "." ";" uri_bits "${arg_URI}")
+ set(build_folder "${output_folder}")
+ foreach(bit IN LISTS uri_bits)
+ get_filename_component(build_folder "${build_folder}" DIRECTORY)
+ endforeach()
+ get_directory_property(_qmlls_ini_build_folders _qmlls_ini_build_folders)
+ list(APPEND _qmlls_ini_build_folders "${build_folder}")
+ set_directory_properties(PROPERTIES _qmlls_ini_build_folders "${_qmlls_ini_build_folders}")
+
+ # if no call with id 'qmlls_ini_generation_id' was deferred for this directory, do it now
+ cmake_language(DEFER GET_CALL qmlls_ini_generation_id call)
+ if("${call}" STREQUAL "")
+ cmake_language(EVAL CODE
+ "cmake_language(DEFER ID qmlls_ini_generation_id CALL _qt_internal_write_deferred_qmlls_ini_file)"
+ )
+ endif()
+ else()
+ get_property(__qt_internal_generate_qmlls_ini_warning GLOBAL PROPERTY __qt_internal_generate_qmlls_ini_warning)
+ if (NOT "${__qt_internal_generate_qmlls_ini_warning}")
+ message(WARNING "QT_QML_GENERATE_QMLLS_INI is not supported on CMake versions < 3.19, disabling...")
+ set_property(GLOBAL PROPERTY __qt_internal_generate_qmlls_ini_warning ON)
+ endif()
+ endif()
+ endif()
+endfunction()
+
+function(_qt_internal_write_deferred_qmlls_ini_file)
+ set(qmlls_ini_file "${CMAKE_CURRENT_SOURCE_DIR}/.qmlls.ini")
+ get_directory_property(_qmlls_ini_build_folders _qmlls_ini_build_folders)
+ list(REMOVE_DUPLICATES _qmlls_ini_build_folders)
+ if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+ # replace cmake list separator ';' with unix path separator ':'
+ string(REPLACE ";" ":" concatenated_build_dirs "${_qmlls_ini_build_folders}")
+ else()
+ # cmake list separator and windows path separator are both ';', so no replacement needed
+ set(concatenated_build_dirs "${_qmlls_ini_build_folders}")
+ endif()
+ set(file_content "[General]\nbuildDir=${concatenated_build_dirs}\nno-cmake-calls=false\n")
+ file(CONFIGURE OUTPUT "${qmlls_ini_file}" CONTENT "${file_content}")
endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
@@ -725,6 +822,42 @@ macro(_qt_internal_genex_getjoinedproperty var target property item_prefix glue)
set(${var} "$<${have_${var}}:${item_prefix}$<JOIN:${${var}},${glue}${item_prefix}>>")
endmacro()
+
+# Creates a genex that will call a c++ macro on each of the list values.
+# Handles empty lists.
+macro(_qt_internal_genex_get_list_joined_with_macro var input_list macro_name with_ending_semicolon)
+ set(${var} "${input_list}")
+ set(have_${var} "$<BOOL:${${var}}>")
+
+ set(_macro_begin "${macro_name}(")
+ set(_macro_end ")")
+ if(with_ending_semicolon)
+ string(APPEND _macro_end ";")
+ endif()
+ set(_macro_glue "${_macro_end}\n${_macro_begin}")
+
+ string(JOIN "" ${var}
+ "${_macro_begin}"
+ "$<JOIN:${${var}},${_macro_glue}>"
+ "${_macro_end}")
+
+ set(${var} "$<${have_${var}}:${${var}}>")
+
+ unset(_macro_begin)
+ unset(_macro_end)
+ unset(_macro_glue)
+endmacro()
+
+# Reads a target property that contains a list of values and creates a genex that will
+# call a c++ macro on each of the values.
+# Handles empty properties.
+macro(_qt_internal_genex_get_property_joined_with_macro var target property macro_name
+ with_ending_semicolon)
+ _qt_internal_genex_getproperty(${var} ${target} ${property})
+ _qt_internal_genex_get_list_joined_with_macro("${var}" "${${var}}" "${macro_name}"
+ "${with_ending_semicolon}")
+endmacro()
+
macro(_qt_internal_genex_getoption var target property)
set(${var} "$<BOOL:$<TARGET_PROPERTY:${target},${property}>>")
endmacro()
@@ -748,7 +881,8 @@ endfunction()
function(_qt_internal_assign_to_qmllint_targets_folder target)
get_property(folder_name GLOBAL PROPERTY QT_QMLLINTER_TARGETS_FOLDER)
if("${folder_name}" STREQUAL "")
- set(folder_name QmlLinter)
+ get_property(__qt_qt_targets_folder GLOBAL PROPERTY QT_TARGETS_FOLDER)
+ set(folder_name "${__qt_qt_targets_folder}/QmlLinter")
set_property(GLOBAL PROPERTY QT_QMLLINTER_TARGETS_FOLDER ${folder_name})
endif()
set_property(TARGET ${target} PROPERTY FOLDER "${folder_name}")
@@ -807,35 +941,68 @@ function(_qt_internal_target_enable_qmllint target)
_qt_internal_extend_qml_import_paths(import_args)
_qt_internal_get_tool_wrapper_script_path(tool_wrapper)
- set(cmd
- ${tool_wrapper}
- $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint>
+
+ set(qmllint_args
--bare
${import_args}
${qrc_args}
${qmllint_files}
)
+ get_target_property(target_binary_dir ${target} BINARY_DIR)
+ set(qmllint_dir ${target_binary_dir}/.rcc/qmllint)
+ set(qmllint_rsp_path ${qmllint_dir}/${target}.rsp)
+
+ file(GENERATE
+ OUTPUT "${qmllint_rsp_path}"
+ CONTENT "$<JOIN:${qmllint_args},\n>\n"
+ )
+
+ set(cmd
+ ${tool_wrapper}
+ $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint>
+ @${qmllint_rsp_path}
+ )
+
+ set(cmd_dummy ${CMAKE_COMMAND} -E echo "Nothing to do for target ${lint_target}.")
+
# We need this target to depend on all qml type registrations. This is the
# only way we can be sure that all *.qmltypes files for any QML modules we
# depend on will have been generated.
add_custom_target(${lint_target}
- COMMAND "$<${have_qmllint_files}:${cmd}>"
+ COMMAND "$<IF:${have_qmllint_files},${cmd},${cmd_dummy}>"
COMMAND_EXPAND_LISTS
DEPENDS
${QT_CMAKE_EXPORT_NAMESPACE}::qmllint
${qmllint_files}
+ ${qmllint_rsp_path}
$<TARGET_NAME_IF_EXISTS:all_qmltyperegistrations>
WORKING_DIRECTORY "$<TARGET_PROPERTY:${target},SOURCE_DIR>"
)
_qt_internal_assign_to_qmllint_targets_folder(${lint_target})
+ list(APPEND qmllint_args "--json" "${CMAKE_BINARY_DIR}/${lint_target}.json")
+
+ set(qmllint_rsp_path ${qmllint_dir}/${target}_json.rsp)
+
+ file(GENERATE
+ OUTPUT "${qmllint_rsp_path}"
+ CONTENT "$<JOIN:${qmllint_args},\n>\n"
+ )
+
+ set(cmd
+ ${tool_wrapper}
+ $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint>
+ @${qmllint_rsp_path}
+ )
+
add_custom_target(${lint_target_json}
- COMMAND "$<${have_qmllint_files}:${cmd}>" --json ${CMAKE_BINARY_DIR}/${lint_target}.json
+ COMMAND "$<${have_qmllint_files}:${cmd}>"
COMMAND_EXPAND_LISTS
DEPENDS
${QT_CMAKE_EXPORT_NAMESPACE}::qmllint
${qmllint_files}
+ ${qmllint_rsp_path}
$<TARGET_NAME_IF_EXISTS:all_qmltyperegistrations>
WORKING_DIRECTORY "$<TARGET_PROPERTY:${target},SOURCE_DIR>"
)
@@ -846,18 +1013,34 @@ function(_qt_internal_target_enable_qmllint target)
get_target_property(module_uri ${target} QT_QML_MODULE_URI)
_qt_internal_get_tool_wrapper_script_path(tool_wrapper)
+
+ set(qmllint_args
+ ${import_args}
+ ${qrc_args}
+ --module
+ ${module_uri}
+ )
+
+ set(qmllint_rsp_path ${qmllint_dir}/${target}_module.rsp)
+
+ file(GENERATE
+ OUTPUT "${qmllint_rsp_path}"
+ CONTENT "$<JOIN:${qmllint_args},\n>\n"
+ )
+
+ set(cmd
+ ${tool_wrapper}
+ $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint>
+ @${qmllint_rsp_path}
+ )
+
add_custom_target(${lint_target_module}
- COMMAND
- ${tool_wrapper}
- $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint>
- ${import_args}
- ${qrc_args}
- --module
- ${module_uri}
+ COMMAND ${cmd}
COMMAND_EXPAND_LISTS
DEPENDS
${QT_CMAKE_EXPORT_NAMESPACE}::qmllint
${qmllint_files}
+ ${qmllint_rsp_path}
$<TARGET_NAME_IF_EXISTS:all_qmltyperegistrations>
WORKING_DIRECTORY "$<TARGET_PROPERTY:${target},SOURCE_DIR>"
)
@@ -867,32 +1050,12 @@ function(_qt_internal_target_enable_qmllint target)
# Note that the caller is free to change the value of QT_QMLLINT_ALL_TARGET
# for different QML modules if they wish, which means they can implement
# their own grouping of the ${target}_qmllint targets.
- if("${QT_QMLLINT_ALL_TARGET}" STREQUAL "")
- set(QT_QMLLINT_ALL_TARGET all_qmllint)
- endif()
- if(NOT TARGET ${QT_QMLLINT_ALL_TARGET})
- add_custom_target(${QT_QMLLINT_ALL_TARGET})
- endif()
- add_dependencies(${QT_QMLLINT_ALL_TARGET} ${lint_target})
-
- if("${QT_QMLLINT_JSON_ALL_TARGET}" STREQUAL "")
- set(QT_QMLLINT_JSON_ALL_TARGET all_qmllint_json)
- endif()
- if(NOT TARGET ${QT_QMLLINT_JSON_ALL_TARGET})
- add_custom_target(${QT_QMLLINT_JSON_ALL_TARGET})
- _qt_internal_assign_to_qmllint_targets_folder(${QT_QMLLINT_JSON_ALL_TARGET})
- endif()
- add_dependencies(${QT_QMLLINT_JSON_ALL_TARGET} ${lint_target_json})
-
- if("${QT_QMLLINT_MODULE_ALL_TARGET}" STREQUAL "")
- set(QT_QMLLINT_MODULE_ALL_TARGET all_qmllint_module)
- endif()
- if(NOT TARGET ${QT_QMLLINT_MODULE_ALL_TARGET})
- add_custom_target(${QT_QMLLINT_MODULE_ALL_TARGET})
- _qt_internal_assign_to_qmllint_targets_folder(${QT_QMLLINT_MODULE_ALL_TARGET})
- endif()
- add_dependencies(${QT_QMLLINT_MODULE_ALL_TARGET} ${lint_target_module})
-
+ _qt_internal_add_all_qmllint_target(QT_QMLLINT_ALL_TARGET
+ all_qmllint ${lint_target})
+ _qt_internal_add_all_qmllint_target(QT_QMLLINT_JSON_ALL_TARGET
+ all_qmllint_json ${lint_target_json})
+ _qt_internal_add_all_qmllint_target(QT_QMLLINT_MODULE_ALL_TARGET
+ all_qmllint_module ${lint_target_module})
endfunction()
# This is a modified version of __qt_propagate_generated_resource from qtbase.
@@ -944,15 +1107,49 @@ function(_qt_internal_propagate_qmlcache_object_lib
set(${output_generated_target} "${resource_target}" PARENT_SCOPE)
endfunction()
-function(_qt_internal_target_enable_qmlcachegen target output_targets_var qmlcachegen)
+# Create an 'all_qmllint' target. The target's name can be user-controlled by ${target_var} with the
+# default name ${default_target_name}. The parameter ${lint_target} holds the name of the single
+# foo_qmllint target that should be triggered by the all_qmllint target.
+function(_qt_internal_add_all_qmllint_target target_var default_target_name lint_target)
+ set(target_name "${${target_var}}")
+ if("${target_name}" STREQUAL "")
+ set(target_name ${default_target_name})
+ endif()
+ _qt_internal_add_phony_target(${target_name}
+ WARNING_VARIABLE QT_NO_QMLLINT_CREATION_WARNING
+ TARGET_CREATED_HOOK _qt_internal_assign_to_qmllint_targets_folder
+ )
+ _qt_internal_add_phony_target_dependencies(${target_name} ${lint_target})
+endfunction()
- set(output_targets)
+# Hack for the Visual Studio generator. Create the all_qmllint target named ${target} and work
+# around the lack of a working add_dependencies by calling 'cmake --build' for every dependency.
+function(_qt_internal_add_all_qmllint_target_deferred target)
+ get_property(target_dependencies GLOBAL PROPERTY _qt_target_${target}_dependencies)
+ set(target_commands "")
+ foreach(dependency IN LISTS target_dependencies)
+ list(APPEND target_commands
+ COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" -t ${dependency}
+ )
+ endforeach()
+ add_custom_target(${target} ${target_commands})
+ _qt_internal_assign_to_qmllint_targets_folder(${target})
+endfunction()
+
+function(_qt_internal_target_enable_qmlcachegen target qmlcachegen)
set_target_properties(${target} PROPERTIES _qt_cachegen_set_up TRUE)
get_target_property(target_binary_dir ${target} BINARY_DIR)
set(qmlcache_dir ${target_binary_dir}/.rcc/qmlcache)
set(qmlcache_resource_name qmlcache_${target})
+ # Save the resource name in a property so we can reference it later in a qml plugin
+ # constructor, to avoid discarding the resource if it's in a static library.
+ __qt_internal_sanitize_resource_name(
+ sanitized_qmlcache_resource_name "${qmlcache_resource_name}")
+ set_target_properties(${target} PROPERTIES _qt_cachegen_sanitized_resource_name
+ "${sanitized_qmlcache_resource_name}")
+
# INTEGRITY_SYMBOL_UNIQUENESS
# The cache loader file name has to be unique, because the Integrity compiler uses the file name
# for the generation of the translation unit static constructor symbol name.
@@ -984,14 +1181,13 @@ function(_qt_internal_target_enable_qmlcachegen target output_targets_var qmlcac
${tool_wrapper}
${qmlcachegen}
--resource-name "${qmlcache_resource_name}"
- ${qrc_resource_args}
-o "${qmlcache_loader_cpp}"
"@${qmlcache_loader_list}"
)
file(GENERATE
OUTPUT ${qmlcache_loader_list}
- CONTENT "$<JOIN:${qmlcache_resource_paths},\n>\n"
+ CONTENT "$<JOIN:${qrc_resource_args},\n>\n$<JOIN:${qmlcache_resource_paths},\n>\n"
)
add_custom_command(
@@ -1012,6 +1208,7 @@ function(_qt_internal_target_enable_qmlcachegen target output_targets_var qmlcac
${qmlcache_loader_cpp}
TARGET_DIRECTORY ${target}
PROPERTIES GENERATED TRUE
+ SKIP_AUTOGEN TRUE
)
endif()
get_target_property(target_source_dir ${target} SOURCE_DIR)
@@ -1021,25 +1218,11 @@ function(_qt_internal_target_enable_qmlcachegen target output_targets_var qmlcac
endif()
# TODO: Probably need to reject ${target} being an object library as unsupported
- get_target_property(target_type ${target} TYPE)
- if(target_type STREQUAL "STATIC_LIBRARY")
- set(extra_conditions "")
- _qt_internal_propagate_qmlcache_object_lib(
- ${target}
- "${qmlcache_loader_cpp}"
- "${extra_conditions}"
- output_target)
-
- list(APPEND output_targets ${output_target})
- else()
- target_sources(${target} PRIVATE "${qmlcache_loader_cpp}")
- target_link_libraries(${target} PRIVATE
- ${QT_CMAKE_EXPORT_NAMESPACE}::QmlPrivate
- ${QT_CMAKE_EXPORT_NAMESPACE}::Core
- )
- endif()
-
- set(${output_targets_var} ${output_targets} PARENT_SCOPE)
+ target_sources(${target} PRIVATE "${qmlcache_loader_cpp}")
+ target_link_libraries(${target} PRIVATE
+ ${QT_CMAKE_EXPORT_NAMESPACE}::QmlPrivate
+ ${QT_CMAKE_EXPORT_NAMESPACE}::Core
+ )
endfunction()
# We cannot defer writing out the qmldir file to generation time because the
@@ -1240,10 +1423,10 @@ function(_qt_internal_qml_add_qmltc_file_mapping_resource qrc_file target qml_fi
set(${qrc_file} ${generated_qrc_file} PARENT_SCOPE)
endfunction()
-# Compile Qml files (.qml) to C++ source files with Qml Type Compiler (qmltc).
+# Compile Qml files (.qml) to C++ source files with QML type compiler (qmltc).
function(_qt_internal_target_enable_qmltc target)
set(args_option "")
- set(args_single NAMESPACE)
+ set(args_single NAMESPACE EXPORT_MACRO_NAME EXPORT_FILE_NAME MODULE)
set(args_multi QML_FILES IMPORT_PATHS)
cmake_parse_arguments(PARSE_ARGV 1 arg
@@ -1276,6 +1459,15 @@ function(_qt_internal_target_enable_qmltc target)
if(arg_NAMESPACE)
list(APPEND common_args --namespace "${arg_NAMESPACE}")
endif()
+ if(arg_MODULE)
+ list(APPEND common_args --module "${arg_MODULE}")
+ endif()
+ if(arg_EXPORT_MACRO_NAME)
+ list(APPEND common_args --export "${arg_EXPORT_MACRO_NAME}")
+ if(arg_EXPORT_FILE_NAME)
+ list(APPEND common_args --exportInclude "${arg_EXPORT_FILE_NAME}")
+ endif()
+ endif()
get_target_property(output_dir ${target} QT_QML_MODULE_OUTPUT_DIRECTORY)
set(qmldir_file ${output_dir}/qmldir)
@@ -1494,6 +1686,177 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
+# Get extern declaration for types registration function.
+function(_qt_internal_qml_get_types_extern_declaration register_types_function_name out_var)
+ set(with_ending_semicolon FALSE)
+ _qt_internal_genex_get_list_joined_with_macro(
+ content
+ "${register_types_function_name}"
+ "QT_DECLARE_EXTERN_SYMBOL_VOID"
+ ${with_ending_semicolon}
+ )
+
+ string(PREPEND content "\n")
+ set(${out_var} "${content}" PARENT_SCOPE)
+endfunction()
+
+# Get code block that should reference the types registration function in the plugin constructor.
+# Ensures the symbol is not discarded when linking.
+function(_qt_internal_qml_get_types_keep_reference register_types_function_name out_var)
+ set(with_ending_semicolon FALSE)
+ _qt_internal_genex_get_list_joined_with_macro(
+ content
+ "${register_types_function_name}"
+ "QT_KEEP_SYMBOL"
+ ${with_ending_semicolon}
+ )
+
+ string(PREPEND content "\n")
+ set(${out_var} "${content}" PARENT_SCOPE)
+endfunction()
+
+# Get extern declaration for cachegen resource.
+function(_qt_internal_qml_get_cachegen_extern_resource_declaration target out_var)
+ set(with_ending_semicolon FALSE)
+ _qt_internal_genex_get_property_joined_with_macro(
+ content
+ "${target}"
+ "_qt_cachegen_sanitized_resource_name"
+ "QT_DECLARE_EXTERN_RESOURCE"
+ ${with_ending_semicolon}
+ )
+
+ string(PREPEND content "\n")
+ set(${out_var} "${content}" PARENT_SCOPE)
+endfunction()
+
+# Get code block that should reference the cachegen resource in the plugin constructor.
+# Ensures the resource is not discarded when linking.
+function(_qt_internal_qml_get_cachegen_resource_keep_reference target out_var)
+ set(with_ending_semicolon FALSE)
+ _qt_internal_genex_get_property_joined_with_macro(
+ content
+ "${target}"
+ "_qt_cachegen_sanitized_resource_name"
+ "QT_KEEP_RESOURCE"
+ ${with_ending_semicolon}
+ )
+
+ string(PREPEND content "\n")
+ set(${out_var} "${content}" PARENT_SCOPE)
+endfunction()
+
+# Get extern declaration for a regular resource.
+function(_qt_internal_qml_get_resource_extern_declarations target out_var)
+ set(with_ending_semicolon FALSE)
+ _qt_internal_genex_get_property_joined_with_macro(
+ content
+ "${target}"
+ "_qt_qml_module_sanitized_resource_names"
+ "QT_DECLARE_EXTERN_RESOURCE"
+ ${with_ending_semicolon}
+ )
+
+ string(PREPEND content "\n")
+ set(${out_var} "${content}" PARENT_SCOPE)
+endfunction()
+
+# Get code block that should reference regular resources in the plugin constructor.
+# Ensures the resources are not discarded when linking.
+function(_qt_internal_qml_get_resource_keep_references target out_var)
+ set(with_ending_semicolon FALSE)
+ _qt_internal_genex_get_property_joined_with_macro(
+ content
+ "${target}"
+ "_qt_qml_module_sanitized_resource_names"
+ "QT_KEEP_RESOURCE"
+ ${with_ending_semicolon}
+ )
+
+ string(PREPEND content "\n")
+ set(${out_var} "${content}" PARENT_SCOPE)
+endfunction()
+
+# Get 3 different code blocks that should be inserted into various locations of the generated
+# qml plugin cpp file. The code blocks ensure that if a target links to a plugin backed by a static
+# library and initializes the plugin using Q_IMPORT_PLUGIN, none of the resources or type
+# registration functions in the backing library are discarded by the linker.
+function(_qt_internal_qml_get_symbols_to_keep
+ target
+ backing_target_type
+ register_types_function_name
+ out_var_intro
+ out_var_intro_namespaced
+ out_var_constructor)
+ set(intro_content "")
+ set(intro_namespaced_content "")
+
+ # External symbol declarations.
+ _qt_internal_qml_get_types_extern_declaration("${register_types_function_name}" output)
+ string(APPEND intro_namespaced_content "${output}")
+
+ if(backing_target_type STREQUAL "STATIC_LIBRARY")
+ _qt_internal_qml_get_cachegen_extern_resource_declaration(${target} output)
+ string(APPEND intro_content "${output}")
+
+ _qt_internal_qml_get_resource_extern_declarations(${target} output)
+ string(APPEND intro_content "${output}")
+ endif()
+
+
+ # Reference the external symbols in the plugin constructor to prevent the linker from
+ # discarding the symbols.
+ # The types symbol is an exported symbol, so we always reference it.
+ set(constructor_content "")
+
+ _qt_internal_qml_get_types_keep_reference("${register_types_function_name}" output)
+ string(APPEND constructor_content "${output}")
+
+ # Only reference the resources if the backing library is static.
+ # If the backing library is shared, the symbols will not be found at link time because they
+ # are not exported symbols.
+ if(backing_target_type STREQUAL "STATIC_LIBRARY")
+ _qt_internal_qml_get_cachegen_resource_keep_reference(${target} output)
+ string(APPEND constructor_content "${output}")
+
+ _qt_internal_qml_get_resource_keep_references(${target} output)
+ string(APPEND constructor_content "${output}")
+ endif()
+
+ set(${out_var_intro} "${intro_content}" PARENT_SCOPE)
+ set(${out_var_intro_namespaced} "${intro_namespaced_content}" PARENT_SCOPE)
+ set(${out_var_constructor} "${constructor_content}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_set_qml_target_multi_config_output_directory target output_directory)
+ # In multi-config builds we need to make sure that at least one configuration has the dynamic
+ # plugin that is located next to qmldir file, otherwise QML engine won't be able to load the
+ # plugin.
+ get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
+ if(is_multi_config)
+ # We don't care about static plugins here since, they are linked at build time and
+ # their location doesn't affect the runtime.
+ get_target_property(target_type ${target} TYPE)
+ if(target_type STREQUAL "SHARED_LIBRARY" OR target_type STREQUAL "MODULE_LIBRARY")
+ if(NOT "${output_directory}")
+ set(output_directory "${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+
+ list(GET CMAKE_CONFIGURATION_TYPES 0 default_config)
+ string(JOIN "" output_directory_with_default_config
+ "$<IF:$<CONFIG:${default_config}>,"
+ "${output_directory},"
+ "${output_directory}/$<CONFIG>"
+ ">"
+ )
+ set_target_properties(${target} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${output_directory_with_default_config}"
+ LIBRARY_OUTPUT_DIRECTORY "${output_directory_with_default_config}"
+ )
+ endif()
+ endif()
+endfunction()
+
function(qt6_add_qml_plugin target)
set(args_option
STATIC
@@ -1562,13 +1925,17 @@ function(qt6_add_qml_plugin target)
if(NOT arg_CLASS_NAME)
if(NOT "${arg_BACKING_TARGET}" STREQUAL "")
- get_target_property(arg_CLASS_NAME ${target} QT_QML_MODULE_CLASS_NAME)
+ get_target_property(arg_CLASS_NAME ${arg_BACKING_TARGET} QT_QML_MODULE_CLASS_NAME)
endif()
if(NOT arg_CLASS_NAME)
_qt_internal_compute_qml_plugin_class_name_from_uri("${arg_URI}" arg_CLASS_NAME)
endif()
endif()
+ if(arg_BACKING_TARGET AND TARGET "${arg_BACKING_TARGET}")
+ get_target_property(backing_type ${arg_BACKING_TARGET} TYPE)
+ endif()
+
if(TARGET ${target})
# Plugin target already exists. Perform a few sanity checks, but we
# otherwise trust that the target is appropriate for use as a plugin.
@@ -1616,7 +1983,6 @@ function(qt6_add_qml_plugin target)
if(TARGET "${arg_BACKING_TARGET}")
# Ensure that the plugin type we create will be compatible with the
# type of backing target we were given
- get_target_property(backing_type ${arg_BACKING_TARGET} TYPE)
if(backing_type STREQUAL "STATIC_LIBRARY")
if(lib_type STREQUAL "")
set(lib_type STATIC)
@@ -1653,12 +2019,18 @@ function(qt6_add_qml_plugin target)
)
endif()
+ get_target_property(install_rpath ${target} INSTALL_RPATH)
# Ignore any CMAKE_INSTALL_RPATH and set a better default RPATH on platforms
# that support it, if allowed. Projects will often set CMAKE_INSTALL_RPATH
# for executables or backing libraries, but forget about plugins. Because
# the path for QML plugins depends on their URI, it is unlikely that
# CMAKE_INSTALL_RPATH would ever be intended for use with QML plugins.
- if(NOT WIN32 AND NOT QT_NO_QML_PLUGIN_RPATH)
+ #
+ # Avoid setting INSTALL_RPATH if it was set before. This is mostly
+ # applicable for the Qml plugins built in Qt tree, that got INSTALL_RPATH
+ # from the qt_internal_add_plugin function, but also can be the case for the
+ # user Qml plugins created manually.
+ if(NOT WIN32 AND NOT QT_NO_QML_PLUGIN_RPATH AND NOT install_rpath)
# Construct a relative path from a default install location (assumed to
# be qml/target-path) to ${CMAKE_INSTALL_LIBDIR}. This would be
# applicable for Apple too (although unusual) if this is a bare install
@@ -1705,6 +2077,36 @@ function(qt6_add_qml_plugin target)
)
endif()
+ # Work around QTBUG-115152.
+ # Assign / duplicate the backing library's metatypes file to the qml plugin.
+ # This ensures that the metatypes are passed as foreign types to qmltyperegistrar when
+ # a consumer links against the plugin, but not the backing library.
+ # This is needed because the plugin links PRIVATEly to the backing library and thus the
+ # backing library's INTERFACE_SOURCES don't end up in the final consumer's SOURCES.
+ # Arguably doing this is cleaner than changing the linkage to PUBLIC, because that will
+ # propagate not only the INTERFACE_SOURCES, but also other link dependencies, which might
+ # be unwanted.
+ # In general this is a workaround due to CMake's limitations around support for propagating
+ # custom properties across targets to a final consumer target.
+ # https://gitlab.kitware.com/cmake/cmake/-/issues/20416
+ if(arg_BACKING_TARGET
+ AND TARGET "${arg_BACKING_TARGET}" AND NOT arg_BACKING_TARGET STREQUAL target)
+ get_target_property(plugin_meta_types_file "${target}" INTERFACE_QT_META_TYPES_BUILD_FILE)
+ get_target_property(
+ backing_meta_types_file "${arg_BACKING_TARGET}" INTERFACE_QT_META_TYPES_BUILD_FILE)
+ get_target_property(
+ backing_meta_types_file_name
+ "${arg_BACKING_TARGET}" INTERFACE_QT_META_TYPES_FILE_NAME)
+ get_target_property(backing_has_qmltypes "${arg_BACKING_TARGET}" _qt_internal_has_qmltypes)
+ if(backing_has_qmltypes AND NOT plugin_meta_types_file)
+ _qt_internal_assign_build_metatypes_files_and_properties(
+ "${target}"
+ METATYPES_FILE_NAME "${backing_meta_types_file_name}"
+ METATYPES_FILE_PATH "${backing_meta_types_file}"
+ )
+ endif()
+ endif()
+
if(ANDROID)
_qt_internal_get_qml_plugin_output_name(plugin_output_name ${target}
BACKING_TARGET "${arg_BACKING_TARGET}"
@@ -1733,6 +2135,8 @@ function(qt6_add_qml_plugin target)
)
endif()
+ _qt_internal_set_qml_target_multi_config_output_directory(${target} "${arg_OUTPUT_DIRECTORY}")
+
if(NOT arg_NO_GENERATE_PLUGIN_SOURCE)
set(generated_cpp_file_name_base "${target}_${arg_CLASS_NAME}")
set(register_types_function_name "qml_register_types_${escaped_uri}")
@@ -1742,29 +2146,69 @@ function(qt6_add_qml_plugin target)
set(qt_qml_plugin_moc_include_name "${generated_cpp_file_name_base}.moc")
set(qt_qml_plugin_intro "")
set(qt_qml_plugin_outro "")
+ set(qt_qml_plugin_constructor_content "")
+
+ # Get the target that contains the resource names.
+ set(target_with_prop "${target}")
+ if(NOT arg_BACKING_TARGET STREQUAL "" AND TARGET "${arg_BACKING_TARGET}")
+ set(target_with_prop "${arg_BACKING_TARGET}")
+ endif()
+
+ _qt_internal_qml_get_symbols_to_keep(
+ "${target_with_prop}"
+ "${backing_type}"
+ "${register_types_function_name}"
+ extra_intro_content
+ extra_intro_namespaced_content
+ extra_costructor_content
+ )
+
+ string(APPEND qt_qml_plugin_intro "${extra_intro_content}\n\n")
+
if (arg_NAMESPACE)
- string(APPEND qt_qml_plugin_intro "namespace ${arg_NAMESPACE} {\n\n")
+ string(APPEND qt_qml_plugin_intro "namespace ${arg_NAMESPACE} {\n")
string(APPEND qt_qml_plugin_outro "} // namespace ${arg_NAMESPACE}")
endif()
- string(APPEND qt_qml_plugin_intro "extern void ${register_types_function_name}();\nQ_GHS_KEEP_REFERENCE(${register_types_function_name})")
+ string(APPEND qt_qml_plugin_intro "${extra_intro_namespaced_content}")
- # Indenting here is deliberately different so as to make the generated
- # file have sensible indenting
- set(qt_qml_plugin_constructor_content
- "volatile auto registration = &${register_types_function_name};
- Q_UNUSED(registration)"
- )
+ string(APPEND qt_qml_plugin_constructor_content "${extra_costructor_content}")
- set(generated_cpp_file
- "${CMAKE_CURRENT_BINARY_DIR}/${generated_cpp_file_name_base}.cpp"
+ # Configure file from the template.
+ set(generated_cpp_file_in
+ "${CMAKE_CURRENT_BINARY_DIR}/${generated_cpp_file_name_base}_in.cpp"
)
configure_file(
"${__qt_qml_macros_module_base_dir}/Qt6QmlPluginTemplate.cpp.in"
- "${generated_cpp_file}"
+ "${generated_cpp_file_in}"
@ONLY
)
- target_sources(${target} PRIVATE "${generated_cpp_file}")
+
+ get_target_property(template_generated ${target} _qt_qml_plugin_template_generated)
+
+ if(NOT template_generated)
+ # Generate the file to allow adding generator expressions.
+ set(generated_cpp_file
+ "${CMAKE_CURRENT_BINARY_DIR}/${generated_cpp_file_name_base}.cpp"
+ )
+ file(GENERATE OUTPUT "${generated_cpp_file}"
+ INPUT "${generated_cpp_file_in}"
+ )
+
+ # We can't rely on policy CMP0118 since user project controls it
+ set(scope_args)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
+ set(scope_args TARGET_DIRECTORY ${target})
+ endif()
+ set_source_files_properties("${generated_cpp_file}" ${scope_args}
+ PROPERTIES GENERATED TRUE
+ )
+
+ # Because the add_qml_plugin function might be called multiple times on the same target,
+ # Only generate and add the cpp file once.
+ set_target_properties(${target} PROPERTIES _qt_qml_plugin_template_generated TRUE)
+ target_sources(${target} PRIVATE "${generated_cpp_file}")
+ endif()
# The generated cpp file expects to include its moc-ed output file.
set_target_properties(${target} PROPERTIES AUTOMOC TRUE)
@@ -1829,8 +2273,9 @@ function(qt6_target_qml_sources target)
message(FATAL_ERROR "Unknown/unexpected arguments: ${arg_UNPARSED_ARGUMENTS}")
endif()
- if (NOT arg_QML_FILES AND NOT arg_RESOURCES)
- if(NOT arg_NO_LINT)
+ get_target_property(no_lint ${target} QT_QML_MODULE_NO_LINT)
+ if(NOT arg_QML_FILES AND NOT arg_RESOURCES)
+ if(NOT arg_NO_LINT AND NOT no_lint)
_qt_internal_target_enable_qmllint(${target})
endif()
@@ -1849,7 +2294,6 @@ function(qt6_target_qml_sources target)
)
endif()
- get_target_property(no_lint ${target} QT_QML_MODULE_NO_LINT)
get_target_property(no_cachegen ${target} QT_QML_MODULE_NO_CACHEGEN)
get_target_property(no_qmldir ${target} QT_QML_MODULE_NO_GENERATE_QMLDIR)
get_target_property(resource_prefix ${target} QT_QML_MODULE_RESOURCE_PREFIX)
@@ -1974,9 +2418,10 @@ function(qt6_target_qml_sources target)
endif()
endif()
- set(non_qml_files)
- set(output_targets)
- set(copied_files)
+ set(non_qml_cpp_files "")
+ set(non_qml_files "")
+ set(output_targets "")
+ set(copied_files "")
# We want to set source file properties in the target's own scope if we can.
# That's the canonical place the properties will be read from.
@@ -2020,15 +2465,24 @@ function(qt6_target_qml_sources target)
get_filename_component(file_out_dir ${file_out} DIRECTORY)
file(MAKE_DIRECTORY ${file_out_dir})
- execute_process(COMMAND
- ${CMAKE_COMMAND} -E copy_if_different ${file_absolute} ${file_out}
- )
+ if(EXISTS "${file_absolute}")
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.21")
+ # Significantly increases copying speed according to profiling, presumably
+ # because we bypass process creation.
+ file(COPY_FILE "${file_absolute}" "${file_out}" ONLY_IF_DIFFERENT)
+ else()
+ execute_process(COMMAND
+ ${CMAKE_COMMAND} -E copy_if_different ${file_absolute} ${file_out}
+ )
+ endif()
+ endif()
add_custom_command(OUTPUT ${file_out}
COMMAND ${CMAKE_COMMAND} -E copy ${file_absolute} ${file_out}
DEPENDS ${file_absolute}
WORKING_DIRECTORY $<TARGET_PROPERTY:${target},SOURCE_DIR>
VERBATIM
+ COMMENT "Copying ${file_src} to ${file_out}"
)
list(APPEND copied_files ${file_out})
endif()
@@ -2036,11 +2490,16 @@ function(qt6_target_qml_sources target)
endforeach()
set(generated_sources_other_scope)
+ set(extra_qmldirs)
foreach(qml_file_src IN LISTS arg_QML_FILES)
# This is to facilitate updating code that used the earlier tech preview
# API function qt6_target_qml_files()
if(NOT qml_file_src MATCHES "\\.(js|mjs|qml)$")
- list(APPEND non_qml_files ${qml_file_src})
+ if(qml_file_src MATCHES "\\.(cpp|cxx|cc|c|c\\+\\+|h|hh|hxx|hpp|h\\+\\+)")
+ list(APPEND non_qml_cpp_files "${qml_file_src}")
+ else()
+ list(APPEND non_qml_files "${qml_file_src}")
+ endif()
continue()
endif()
@@ -2052,6 +2511,11 @@ function(qt6_target_qml_sources target)
get_filename_component(file_absolute ${qml_file_src} ABSOLUTE)
__qt_get_relative_resource_path_for_file(file_resource_path ${qml_file_src})
+ get_filename_component(qml_file_resource_dir ${file_resource_path} DIRECTORY)
+ if(qml_file_resource_dir)
+ list(APPEND extra_qmldirs "${output_dir}/${qml_file_resource_dir}/qmldir")
+ endif()
+
# For the tooling steps below, run the tools on the copied qml file in
# the build directory, not the source directory. This is required
# because the tools may need to reference imported modules from
@@ -2111,6 +2575,12 @@ function(qt6_target_qml_sources target)
get_source_file_property(qml_file_singleton ${qml_file_src} QT_QML_SINGLETON_TYPE)
get_source_file_property(qml_file_internal ${qml_file_src} QT_QML_INTERNAL_TYPE)
+ if (qml_file_singleton AND qml_file_internal)
+ message(FATAL_ERROR
+ "${qml_file_src} is marked as both internal and as a "
+ "singleton, but singletons cannot be internal!")
+ endif()
+
if (NOT qml_file_versions)
set(qml_file_versions ${qml_module_files_versions})
endif()
@@ -2119,11 +2589,16 @@ function(qt6_target_qml_sources target)
foreach(qml_file_version IN LISTS qml_file_versions)
if (qml_file_singleton)
string(APPEND qmldir_file_contents "singleton ")
+ elseif (qml_file_internal)
+ continue()
endif()
string(APPEND qmldir_file_contents "${qml_file_typename} ${qml_file_version} ${file_resource_path}\n")
endforeach()
if (qml_file_internal)
+ # TODO: Remove when all qmldir parsers can parse internal types with versions.
+ # Instead handle internal types like singletons above.
+ # See QTCREATORBUG-28755
string(APPEND qmldir_file_contents "internal ${qml_file_typename} ${file_resource_path}\n")
endif()
@@ -2140,8 +2615,7 @@ function(qt6_target_qml_sources target)
# after we know there will be at least one file to compile.
get_target_property(is_cachegen_set_up ${target} _qt_cachegen_set_up)
if(NOT is_cachegen_set_up)
- _qt_internal_target_enable_qmlcachegen(${target} resource_target ${qmlcachegen})
- list(APPEND output_targets ${resource_target})
+ _qt_internal_target_enable_qmlcachegen(${target} ${qmlcachegen})
endif()
# We ensured earlier that arg_PREFIX always ends with "/"
@@ -2217,13 +2691,19 @@ function(qt6_target_qml_sources target)
_qt_internal_collect_qml_root_paths("${target}" ${arg_QML_FILES})
endif()
- if(non_qml_files)
- list(JOIN non_qml_files "\n " file_list)
- message(WARNING
- "Only .qml, .js or .mjs files should be added with QML_FILES. "
- "The following files should be added with RESOURCES instead:"
- "\n ${file_list}"
- )
+ if(non_qml_files OR non_qml_cpp_files)
+ if(non_qml_cpp_files)
+ list(JOIN non_qml_cpp_files "\n " file_list)
+ set(wrong_sources "\nwith SOURCES:\n ${file_list}"
+ )
+ endif()
+ if(non_qml_files)
+ list(JOIN non_qml_files "\n " file_list)
+ set(wrong_resources "\nwith RESOURCES:\n ${file_list}")
+ endif()
+
+ message(WARNING "Only .qml, .js or .mjs files should be added with QML_FILES. "
+ "The following files should be added${wrong_sources}${wrong_resources}")
endif()
if(copied_files OR generated_sources_other_scope)
@@ -2271,9 +2751,59 @@ function(qt6_target_qml_sources target)
FILES ${arg_QML_FILES} ${arg_RESOURCES}
OUTPUT_TARGETS resource_targets
)
+ list(APPEND output_targets ${resource_targets})
+
+ # Save the resource name in a property so we can reference it later in a qml plugin
+ # constructor, to avoid discarding the resource if it's in a static library.
+ __qt_internal_sanitize_resource_name(
+ sanitized_resource_name "${resource_name}")
+ set_property(TARGET ${target}
+ APPEND PROPERTY _qt_qml_module_sanitized_resource_names "${sanitized_resource_name}")
+
+ if(extra_qmldirs)
+ list(REMOVE_DUPLICATES extra_qmldirs)
+ __qt_internal_setup_policy(QTP0004 "6.8.0"
+"You need qmldir files for each extra directory that contains .qml files for your module. \
+Check https://doc.qt.io/qt-6/qt-cmake-policy-qtp0004.html for policy details."
+ )
+ qt6_policy(GET QTP0004 generate_extra_qmldirs_policy)
+ if ("${generate_extra_qmldirs_policy}" STREQUAL "NEW")
+ foreach(extra_qmldir IN LISTS extra_qmldirs)
+ set(__qt_qmldir_content "prefer :${arg_PREFIX}")
+ configure_file(
+ ${__qt_qml_macros_module_base_dir}/Qt6qmldirTemplate.cmake.in
+ "${extra_qmldir}"
+ @ONLY
+ )
+
+ set_source_files_properties("${extra_qmldir}"
+ PROPERTIES GENERATED TRUE
+ )
+ endforeach()
+
+ set(extra_qmldirs_targets)
+ qt6_add_resources(${target} "${resource_name}_extra_qmldirs"
+ PREFIX ${arg_PREFIX}
+ FILES ${extra_qmldirs}
+ BASE ${output_dir}
+ OUTPUT_TARGETS extra_qmldirs_targets
+ )
+ list(APPEND output_targets ${extra_qmldirs_targets})
+
+ set_property(TARGET ${target} PROPERTY _qt_internal_extra_qmldirs ${extra_qmldirs})
+
+ # Save the resource name in a property so we can reference it later in a qml plugin
+ # constructor, to avoid discarding the resource if it's in a static library.
+ __qt_internal_sanitize_resource_name(
+ sanitized_extra_qmldirs_resource_name "${resource_name}_extra_qmldirs")
+ set_property(TARGET ${target}
+ APPEND PROPERTY _qt_qml_module_sanitized_resource_names
+ "${sanitized_extra_qmldirs_resource_name}")
+ endif()
+ endif()
+
math(EXPR counter "${counter} + 1")
set_target_properties(${target} PROPERTIES QT_QML_MODULE_RAW_QML_SETS ${counter})
- list(APPEND output_targets ${resource_targets})
if(arg_OUTPUT_TARGETS)
set(${arg_OUTPUT_TARGETS} ${output_targets} PARENT_SCOPE)
@@ -2352,9 +2882,12 @@ endif()
# NAMESPACE: Specifies a namespace the type registration function shall be
# generated into. (OPTIONAL)
#
+# REGISTRATIONS_TARGET: Specifies a separate target the generated .cpp file
+# shall be added to. If not given, the main backing target is used. (OPTIONAL)
+#
function(_qt_internal_qml_type_registration target)
set(args_option)
- set(args_single NAMESPACE)
+ set(args_single NAMESPACE REGISTRATIONS_TARGET)
set(args_multi MANUAL_MOC_JSON_FILES)
get_target_property(skipped ${target} _qt_is_skipped_test)
@@ -2419,7 +2952,7 @@ function(_qt_internal_qml_type_registration target)
set(cmd_args)
set(plugin_types_file "${output_dir}/${qmltypes_output_name}")
- set(generated_marker_file "${target_binary_dir}/.generated/${qmltypes_output_name}")
+ set(generated_marker_file "${target_binary_dir}/.qt/qmltypes/${qmltypes_output_name}")
get_filename_component(generated_marker_dir "${generated_marker_file}" DIRECTORY)
set_target_properties(${target} PROPERTIES
QT_QML_MODULE_PLUGIN_TYPES_FILE ${plugin_types_file}
@@ -2492,28 +3025,6 @@ function(_qt_internal_qml_type_registration target)
message(FATAL_ERROR "Need target metatypes.json file")
endif()
- cmake_policy(PUSH)
-
- _qt_internal_check_depfile_support(has_depfile_support)
- set(registration_cpp_file_dep_args)
- if(has_depfile_support)
- if(POLICY CMP0116)
- # Without explicitly setting this policy to NEW, we get a warning
- # even though we ensure there's actually no problem here.
- # See https://gitlab.kitware.com/cmake/cmake/-/issues/21959
- cmake_policy(SET CMP0116 NEW)
- set(relative_to_dir ${CMAKE_CURRENT_BINARY_DIR})
- else()
- set(relative_to_dir ${CMAKE_BINARY_DIR})
- endif()
- set(dependency_file_cpp "${target_binary_dir}/qmltypes/${type_registration_cpp_file_name}.d")
- set(registration_cpp_file_dep_args DEPFILE ${dependency_file_cpp})
- file(RELATIVE_PATH cpp_file_name "${relative_to_dir}" "${type_registration_cpp_file}")
- file(GENERATE OUTPUT "${dependency_file_cpp}"
- CONTENT "${cpp_file_name}: $<IF:$<BOOL:${genex_list}>,\\\n$<JOIN:${genex_list}, \\\n>, \\\n>"
- )
- endif()
-
_qt_internal_get_tool_wrapper_script_path(tool_wrapper)
add_custom_command(
OUTPUT
@@ -2534,13 +3045,10 @@ function(_qt_internal_qml_type_registration target)
${CMAKE_COMMAND} -E make_directory "${generated_marker_dir}"
COMMAND
${CMAKE_COMMAND} -E touch "${generated_marker_file}"
- ${registration_cpp_file_dep_args}
COMMENT "Automatic QML type registration for target ${target}"
VERBATIM
)
- cmake_policy(POP)
-
# The ${target}_qmllint targets need to depend on the generation of all
# *.qmltypes files in the build. We have no way of reliably working out
# which QML modules a given target depends on at configure time, so we
@@ -2561,20 +3069,25 @@ function(_qt_internal_qml_type_registration target)
endif()
add_dependencies(all_qmltyperegistrations ${target}_qmltyperegistration)
- # Both ${target} (via target_sources) and ${target}_qmltyperegistration (via add_custom_target
- # DEPENDS option) depend on ${type_registration_cpp_file}.
+ set(effective_target ${target})
+ if(arg_REGISTRATIONS_TARGET)
+ set(effective_target ${arg_REGISTRATIONS_TARGET})
+ endif()
+
+ # Both ${effective_target} (via target_sources) and ${target}_qmltyperegistration (via
+ # add_custom_target DEPENDS option) depend on ${type_registration_cpp_file}.
# The new Xcode build system requires a common target to drive the generation of files,
# otherwise project configuration fails.
- # Make ${target} the common target, by adding it as a dependency for
+ # Make ${effective_target} the common target, by adding it as a dependency for
# ${target}_qmltyperegistration.
- # The consequence is that the ${target}_qmllint target will now first build ${target} when using
- # the Xcode generator (mostly only relevant for projects using Qt for iOS).
+ # The consequence is that the ${target}_qmllint target will now first build ${effective_target}
+ # when using the Xcode generator (mostly only relevant for projects using Qt for iOS).
# See QTBUG-95763.
if(CMAKE_GENERATOR STREQUAL "Xcode")
- add_dependencies(${target}_qmltyperegistration ${target})
+ add_dependencies(${target}_qmltyperegistration ${effective_target})
endif()
- target_sources(${target} PRIVATE ${type_registration_cpp_file})
+ target_sources(${effective_target} PRIVATE ${type_registration_cpp_file})
# FIXME: The generated .cpp file has usually lost the path information for
# the headers it #include's. Since these generated .cpp files are in
@@ -2583,7 +3096,7 @@ function(_qt_internal_qml_type_registration target)
# paths are needed, but add the source directory to at least handle
# the common case of headers in the same directory as the target.
# See QTBUG-93443.
- target_include_directories(${target} PRIVATE ${target_source_dir})
+ target_include_directories(${effective_target} PRIVATE ${target_source_dir})
# Circumvent "too many sections" error when doing a 32 bit debug build on Windows with
# MinGW.
@@ -2600,7 +3113,7 @@ function(_qt_internal_qml_type_registration target)
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
set_source_files_properties(
${type_registration_cpp_file}
- TARGET_DIRECTORY ${target}
+ TARGET_DIRECTORY ${effective_target}
PROPERTIES
SKIP_AUTOGEN TRUE
GENERATED TRUE
@@ -2608,7 +3121,7 @@ function(_qt_internal_qml_type_registration target)
)
endif()
- target_include_directories(${target} PRIVATE
+ target_include_directories(${effective_target} PRIVATE
$<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::QmlPrivate,INTERFACE_INCLUDE_DIRECTORIES>
)
endfunction()
@@ -2708,6 +3221,61 @@ function(_qt_internal_collect_qml_import_paths out_var target)
set(${out_var} "${qml_import_paths}" PARENT_SCOPE)
endfunction()
+# This function returns the path to the qmlimportscanner executable.
+# The are a few cases to handle:
+# When used in a user project, the tool should already be built and we can find the path
+# via the imported target.
+# When used in an example that is built as part of the qtdeclarative build (or top-level build),
+# there is no imported target yet. Here we have to differentiate whether the tool will be run at
+# build time or configure time.
+# If at configure time, we show an error, there is nothing to run yet. Such a setup is currently not
+# supported.
+# If at build time, we return the path where the built tool will be located after it is built.
+function(_qt_internal_find_qmlimportscanner_path out_path scan_at_configure_time)
+ # Find location of qmlimportscanner via the imported target.
+ set(tool_path "")
+ set(tool_name "qmlimportscanner")
+ set(import_scanner_target "${QT_CMAKE_EXPORT_NAMESPACE}::${tool_name}")
+ if(TARGET "${import_scanner_target}")
+ get_target_property(tool_path "${import_scanner_target}" IMPORTED_LOCATION)
+ if(NOT tool_path)
+ set(configs "RELWITHDEBINFO;RELEASE;MINSIZEREL;DEBUG")
+ foreach(config ${configs})
+ get_target_property(tool_path
+ "${import_scanner_target}" IMPORTED_LOCATION_${config})
+ if(tool_path)
+ break()
+ endif()
+ endforeach()
+ endif()
+ endif()
+
+ if(NOT QT_BUILDING_QT)
+ set(building_user_project TRUE)
+ else()
+ set(building_user_project FALSE)
+ endif()
+
+ if(NOT EXISTS "${tool_path}"
+ AND (building_user_project OR scan_at_configure_time))
+ message(FATAL_ERROR "The qmlimportscanner tool could not be found.
+Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation was faulty.
+")
+ endif()
+
+ # We are building Qt, the tool is not built yet and we need to run it at build time.
+ if(NOT tool_path)
+ qt_path_join(tool_path
+ "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}"
+ "${tool_name}${CMAKE_EXECUTABLE_SUFFIX}")
+ endif()
+
+ set(${out_path} "${tool_path}" PARENT_SCOPE)
+endfunction()
+
function(_qt_internal_scan_qml_imports target imports_file_var when_to_scan)
if(NOT "${ARGN}" STREQUAL "")
message(FATAL_ERROR "Unknown/unexpected arguments: ${ARGN}")
@@ -2715,39 +3283,29 @@ function(_qt_internal_scan_qml_imports target imports_file_var when_to_scan)
if(when_to_scan STREQUAL "BUILD_PHASE")
set(scan_at_build_time TRUE)
+ set(scan_at_configure_time FALSE)
+ set(imports_file_infix "build")
elseif(when_to_scan STREQUAL "IMMEDIATELY")
set(scan_at_build_time FALSE)
+ set(scan_at_configure_time TRUE)
+ set(imports_file_infix "conf")
else()
message(FATAL_ERROR "Unexpected value for when_to_scan: ${when_to_scan}")
endif()
- # Find location of qmlimportscanner.
- get_target_property(tool_path ${QT_CMAKE_EXPORT_NAMESPACE}::qmlimportscanner IMPORTED_LOCATION)
- if(NOT tool_path)
- set(configs "RELWITHDEBINFO;RELEASE;MINSIZEREL;DEBUG")
- foreach(config ${configs})
- get_target_property(tool_path
- ${QT_CMAKE_EXPORT_NAMESPACE}::qmlimportscanner IMPORTED_LOCATION_${config})
- if(tool_path)
- break()
- endif()
- endforeach()
- endif()
-
- if(NOT EXISTS "${tool_path}")
- message(FATAL_ERROR "The package \"QmlImportScanner\" references the file
- \"${tool_path}\"
-but this file does not exist. Possible reasons include:
-* The file was deleted, renamed, or moved to another location.
-* An install or uninstall procedure did not complete successfully.
-* The installation package was faulty.
-")
- endif()
+ _qt_internal_find_qmlimportscanner_path(tool_path "${scan_at_configure_time}")
get_target_property(target_source_dir ${target} SOURCE_DIR)
get_target_property(target_binary_dir ${target} BINARY_DIR)
- set(out_dir "${target_binary_dir}/.qt_plugins")
- set(imports_file "${out_dir}/Qt6_QmlPlugins_Imports_${target}.cmake")
+ set(out_dir "${target_binary_dir}/.qt/qml_imports")
+
+ # Create separate files for scanning at build time vs configure time. Otherwise calling
+ # ninja clean will re-run qmlimportscanner directly after the clean, which is
+ # both weird and sometimes prints warnings due to the tool not finding qml files that were
+ # cleaned from the build dir.
+ set(file_base_name "${target}_${imports_file_infix}")
+
+ set(imports_file "${out_dir}/${file_base_name}.cmake")
set(${imports_file_var} "${imports_file}" PARENT_SCOPE)
file(MAKE_DIRECTORY ${out_dir})
@@ -2763,7 +3321,12 @@ but this file does not exist. Possible reasons include:
# Construct the -importPath arguments.
set(import_path_arguments)
foreach(path IN LISTS qml_import_paths)
- list(APPEND import_path_arguments -importPath ${path})
+ if(EXISTS "${path}" OR scan_at_build_time)
+ list(APPEND import_path_arguments -importPath ${path})
+ else()
+ message(DEBUG "The import path ${path} is mentioned for ${target}, but it doesn't"
+ " exists.")
+ endif()
endforeach()
list(APPEND cmd_args ${import_path_arguments})
@@ -2781,7 +3344,7 @@ but this file does not exist. Possible reasons include:
# of arguments on the command line
string(LENGTH "${cmd_args}" length)
if(length GREATER 240)
- set(rsp_file "${out_dir}/Qt6_QmlPlugins_Imports_${target}.rsp")
+ set(rsp_file "${out_dir}/${file_base_name}.rsp")
list(JOIN cmd_args "\n" rsp_file_content)
file(WRITE ${rsp_file} "${rsp_file_content}")
set(cmd_args "@${rsp_file}")
@@ -2836,9 +3399,16 @@ endmacro()
# This function is called as a finalizer in qt6_finalize_executable() for any
-# target that links against the Qml library for a statically built Qt.
+# target that links against the Qml library.
function(qt6_import_qml_plugins target)
- if(QT6_IS_SHARED_LIBS_BUILD)
+ if(NOT TARGET ${QT_CMAKE_EXPORT_NAMESPACE}::qmlimportscanner)
+ return()
+ endif()
+
+ get_target_property(is_imported ${QT_CMAKE_EXPORT_NAMESPACE}::qmlimportscanner IMPORTED)
+ if(NOT is_imported)
+ message(DEBUG "qt6_import_qml_plugins is called before qmlimportscanner is built."
+ " Skip calling qmlimportscanner because it doesn't yet exist.")
return()
endif()
@@ -2864,39 +3434,68 @@ function(qt6_import_qml_plugins target)
math(EXPR last_index "${qml_import_scanner_imports_count} - 1")
foreach(index RANGE 0 ${last_index})
_qt_internal_parse_qml_imports_entry(entry ${index})
- if(entry_PATH AND entry_PLUGIN)
- # Sometimes a plugin appears multiple times with different versions.
- # Make sure to process it only once.
- list(FIND added_plugins "${entry_PLUGIN}" _index)
- if(NOT _index EQUAL -1)
- continue()
+ if(entry_PATH)
+ if(NOT entry_PLUGIN)
+ # Check if qml module is built within the build tree, and should have a plugin
+ # target, but its qmldir file is not generated yet.
+ get_property(dirs GLOBAL PROPERTY _qt_all_qml_output_dirs)
+ if(dirs)
+ list(FIND dirs "${entry_PATH}" index)
+ if(NOT index EQUAL -1)
+ get_property(qml_targets GLOBAL PROPERTY _qt_all_qml_targets)
+ list(GET qml_targets ${index} qml_module)
+ if(qml_module AND TARGET ${qml_module})
+ get_target_property(entry_LINKTARGET
+ ${qml_module} QT_QML_MODULE_PLUGIN_TARGET)
+ if(entry_LINKTARGET AND TARGET ${entry_LINKTARGET})
+ get_target_property(entry_PLUGIN ${entry_LINKTARGET}
+ OUTPUT_NAME)
+ endif()
+ endif()
+ endif()
+ endif()
endif()
- list(APPEND added_plugins "${entry_PLUGIN}")
-
- # Link against the Qml plugin.
- # For plugins provided by Qt, we assume those plugin targets are already defined
- # (typically brought in via find_package(Qt6...) ).
- # For other plugins, the targets can come from the project itself.
- #
- if(entry_LINKTARGET)
- if(TARGET ${entry_LINKTARGET})
- list(APPEND plugins_to_link "${entry_LINKTARGET}")
+
+ if(entry_PLUGIN)
+ # Sometimes a plugin appears multiple times with different versions.
+ # Make sure to process it only once.
+ list(FIND added_plugins "${entry_PLUGIN}" _index)
+ if(NOT _index EQUAL -1)
+ continue()
+ endif()
+ list(APPEND added_plugins "${entry_PLUGIN}")
+
+ # Link against the Qml plugin.
+ # For plugins provided by Qt, we assume those plugin targets are already defined
+ # (typically brought in via find_package(Qt6...) ).
+ # For other plugins, the targets can come from the project itself.
+ #
+ if(entry_LINKTARGET)
+ if(TARGET ${entry_LINKTARGET})
+ get_target_property(target_type ${entry_LINKTARGET} TYPE)
+ if(target_type STREQUAL "STATIC_LIBRARY")
+ list(APPEND plugins_to_link "${entry_LINKTARGET}")
+ endif()
+ else()
+ message(WARNING
+ "The qml plugin '${entry_PLUGIN}' is a dependency of '${target}', "
+ "but the link target it defines (${entry_LINKTARGET}) does not "
+ "exist in the current scope. The plugin will not be linked."
+ )
+ endif()
+ elseif(TARGET ${entry_PLUGIN})
+ get_target_property(target_type ${entry_PLUGIN} TYPE)
+ if(target_type STREQUAL "STATIC_LIBRARY")
+ list(APPEND plugins_to_link "${entry_PLUGIN}")
+ endif()
else()
+ # TODO: QTBUG-94605 Figure out if this is a reasonable scenario to support
message(WARNING
"The qml plugin '${entry_PLUGIN}' is a dependency of '${target}', "
- "but the link target it defines (${entry_LINKTARGET}) does not exist "
- "in the current scope. The plugin will not be linked."
+ "but there is no target by that name in the current scope. The plugin "
+ "will not be linked."
)
endif()
- elseif(TARGET ${entry_PLUGIN})
- list(APPEND plugins_to_link "${entry_PLUGIN}")
- else()
- # TODO: QTBUG-94605 Figure out if this is a reasonable scenario to support
- message(WARNING
- "The qml plugin '${entry_PLUGIN}' is a dependency of '${target}', "
- "but there is no target by that name in the current scope. The plugin will "
- "not be linked."
- )
endif()
endif()
endforeach()
@@ -2908,7 +3507,8 @@ function(qt6_import_qml_plugins target)
# across those libraries to the end target (executable or shared library).
# The plugin initializers will be linked via usage requirements from the plugin target.
get_target_property(target_type ${target} TYPE)
- if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY")
+ if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY"
+ OR target_type STREQUAL "MODULE_LIBRARY")
set(link_type "PRIVATE")
else()
set(link_type "INTERFACE")
@@ -2928,6 +3528,17 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
+function(_qt_internal_add_qml_deploy_info_finalizer target)
+ get_property(finalizer_added TARGET ${target} PROPERTY _qt_qml_deploy_finalizer_added)
+ if(NOT finalizer_added)
+ set_property(TARGET ${target} APPEND PROPERTY
+ INTERFACE_QT_EXECUTABLE_FINALIZERS
+ _qt_internal_generate_deploy_qml_imports_script
+ )
+ set_property(TARGET ${target} PROPERTY _qt_qml_deploy_finalizer_added TRUE)
+ endif()
+endfunction()
+
# This function may be called as a finalizer in qt6_finalize_executable() for any
# target that links against the Qml library for a shared Qt.
function(_qt_internal_generate_deploy_qml_imports_script target)
@@ -2983,7 +3594,7 @@ function(_qt_internal_generate_deploy_qml_imports_script target)
file(GENERATE OUTPUT "${filename}" CONTENT
"# Auto-generated deploy QML imports script for target \"${target}\".
# Do not edit, all changes will be lost.
-# This file should only be included by qt_deploy_qml_imports().
+# This file should only be included by qt6_deploy_qml_imports().
set(__qt_opts $<${is_bundle}:BUNDLE>)
if(arg_NO_QT_IMPORTS)
@@ -3015,6 +3626,7 @@ function(qt6_generate_deploy_qml_app_script)
set(no_value_options
NO_UNSUPPORTED_PLATFORM_ERROR
NO_TRANSLATIONS
+ NO_COMPILER_RUNTIME
MACOS_BUNDLE_POST_BUILD
DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
@@ -3034,6 +3646,7 @@ function(qt6_generate_deploy_qml_app_script)
POST_EXCLUDE_REGEXES
POST_INCLUDE_FILES
POST_EXCLUDE_FILES
+ DEPLOY_TOOL_OPTIONS
)
set(multi_value_options
${qt_deploy_runtime_dependencies_options}
@@ -3088,16 +3701,40 @@ function(qt6_generate_deploy_qml_app_script)
string(MAKE_C_IDENTIFIER "${arg_TARGET}" target_id)
set(deploy_script_name "qml_app_${target_id}")
+ get_target_property(is_bundle ${arg_TARGET} MACOSX_BUNDLE)
+
+ set(unsupported_platform_extra_message "")
if(QT6_IS_SHARED_LIBS_BUILD)
set(qt_build_type_string "shared Qt libs")
else()
set(qt_build_type_string "static Qt libs")
endif()
+ if(CMAKE_CROSSCOMPILING)
+ string(APPEND qt_build_type_string ", cross-compiled")
+ endif()
+
+ if(NOT is_bundle)
+ string(APPEND qt_build_type_string ", non-bundle app")
+ set(unsupported_platform_extra_message
+ "Executable targets have to be app bundles to use this command on Apple platforms.")
+ endif()
+
+ set(skip_message
+ "_qt_internal_show_skip_runtime_deploy_message(\"${qt_build_type_string}\"")
+ if(unsupported_platform_extra_message)
+ string(APPEND skip_message
+ "\n EXTRA_MESSAGE \"${unsupported_platform_extra_message}\"")
+ endif()
+ string(APPEND skip_message "\n)")
+
set(common_deploy_args "")
if(arg_NO_TRANSLATIONS)
string(APPEND common_deploy_args " NO_TRANSLATIONS\n")
endif()
+ if(arg_NO_COMPILER_RUNTIME)
+ string(APPEND common_deploy_args " NO_COMPILER_RUNTIME\n")
+ endif()
# Forward the arguments that are exactly the same for qt_deploy_runtime_dependencies.
foreach(var IN LISTS qt_deploy_runtime_dependencies_options)
@@ -3106,29 +3743,75 @@ function(qt6_generate_deploy_qml_app_script)
endif()
endforeach()
- if(APPLE AND NOT IOS AND QT6_IS_SHARED_LIBS_BUILD)
- # TODO: Handle non-bundle applications if possible.
- get_target_property(is_bundle ${arg_TARGET} MACOSX_BUNDLE)
- if(NOT is_bundle)
- message(FATAL_ERROR
- "Executable targets have to be app bundles to use this command "
- "on Apple platforms."
- )
+ _qt_internal_should_skip_deployment_api(skip_deployment skip_reason)
+ _qt_internal_should_skip_post_build_deployment_api(skip_post_build_deployment
+ post_build_skip_reason)
+
+ if(APPLE AND NOT IOS AND QT6_IS_SHARED_LIBS_BUILD AND is_bundle)
+ # TODO: Consider handling non-bundle applications in the future using the generic cmake
+ # runtime dependency feature.
+
+ set(should_post_build FALSE)
+ if(arg_MACOS_BUNDLE_POST_BUILD AND NOT skip_post_build_deployment)
+ set(should_post_build TRUE)
endif()
- qt6_generate_deploy_script(
- TARGET ${arg_TARGET}
- NAME ${deploy_script_name}
- OUTPUT_SCRIPT deploy_script
- CONTENT "
-qt_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
+ # Generate the real deployment script when both post build step either deployment are
+ # enabled.
+ # If we skip deployment, but not the POST_BUILD step, we still need to generate the
+ # regular deploy script to run it during POST_BUILD time.
+ if(NOT skip_deployment OR should_post_build)
+ qt6_generate_deploy_script(
+ TARGET ${arg_TARGET}
+ NAME ${deploy_script_name}
+ OUTPUT_SCRIPT real_deploy_script
+ CONTENT "
+qt6_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
if(NOT DEFINED __QT_DEPLOY_POST_BUILD)
- qt_deploy_runtime_dependencies(
+ qt6_deploy_runtime_dependencies(
EXECUTABLE $<TARGET_FILE_NAME:${arg_TARGET}>.app
ADDITIONAL_MODULES \${plugins_found}
${common_deploy_args})
endif()")
- if(arg_MACOS_BUNDLE_POST_BUILD)
+ endif()
+
+ # Generate a no-op script either if we skip deployment or the post build step.
+ if(skip_deployment)
+ _qt_internal_generate_no_op_deploy_script(
+ FUNCTION_NAME "qt6_generate_deploy_qml_app_script"
+ SKIP_REASON "${skip_reason}"
+ TARGET ${arg_TARGET}
+ NAME ${deploy_script_name}
+ OUTPUT_SCRIPT no_op_deploy_script
+ )
+ endif()
+
+ if(skip_post_build_deployment)
+ _qt_internal_generate_no_op_deploy_script(
+ FUNCTION_NAME "qt6_generate_deploy_qml_app_script"
+ SKIP_REASON "${post_build_skip_reason}"
+ TARGET ${arg_TARGET}
+ NAME ${deploy_script_name}
+ OUTPUT_SCRIPT no_op_post_build_script
+ )
+ endif()
+
+ # Choose which deployment script to use during installation.
+ if(skip_deployment)
+ set(deploy_script "${no_op_deploy_script}")
+ else()
+ set(deploy_script "${real_deploy_script}")
+ endif()
+
+ # Choose which deployment script to use during the post build step.
+ if(should_post_build)
+ set(post_build_deploy_script "${real_deploy_script}")
+ elseif(skip_post_build_deployment)
+ # Explicitly asked to skip post build, show a no-op message.
+ set(post_build_deploy_script "${no_op_post_build_script}")
+ endif()
+
+ if(should_post_build OR skip_post_build_deployment)
# We must not deploy the runtime dependencies, otherwise we interfere
# with CMake's RPATH rewriting at install time. We only need the QML
# imports deployed to the bundle anyway, the build RPATHs will allow
@@ -3139,19 +3822,26 @@ endif()")
-D "QT_DEPLOY_PREFIX=$<TARGET_PROPERTY:${arg_TARGET},BINARY_DIR>"
-D "__QT_DEPLOY_IMPL_DIR=${deploy_impl_dir}"
-D "__QT_DEPLOY_POST_BUILD=TRUE"
- -P "${deploy_script}"
+ -P "${post_build_deploy_script}"
VERBATIM
)
endif()
-
+ elseif(skip_deployment)
+ _qt_internal_generate_no_op_deploy_script(
+ FUNCTION_NAME "qt6_generate_deploy_qml_app_script"
+ SKIP_REASON "${skip_reason}"
+ TARGET ${arg_TARGET}
+ NAME ${deploy_script_name}
+ OUTPUT_SCRIPT deploy_script
+ )
elseif(WIN32 AND QT6_IS_SHARED_LIBS_BUILD)
qt6_generate_deploy_script(
TARGET ${arg_TARGET}
NAME ${deploy_script_name}
OUTPUT_SCRIPT deploy_script
CONTENT "
-qt_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
-qt_deploy_runtime_dependencies(
+qt6_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
+qt6_deploy_runtime_dependencies(
EXECUTABLE $<TARGET_FILE:${arg_TARGET}>
ADDITIONAL_MODULES \${plugins_found}
GENERATE_QT_CONF
@@ -3163,8 +3853,8 @@ ${common_deploy_args})")
NAME ${deploy_script_name}
OUTPUT_SCRIPT deploy_script
CONTENT "
-qt_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
-qt_deploy_runtime_dependencies(
+qt6_deploy_qml_imports(TARGET ${arg_TARGET} PLUGINS_FOUND plugins_found)
+qt6_deploy_runtime_dependencies(
EXECUTABLE $<TARGET_FILE:${arg_TARGET}>
ADDITIONAL_MODULES \${plugins_found}
GENERATE_QT_CONF
@@ -3187,18 +3877,17 @@ ${common_deploy_args})")
NAME ${deploy_script_name}
OUTPUT_SCRIPT deploy_script
CONTENT "
-_qt_internal_show_skip_runtime_deploy_message(\"${qt_build_type_string}\")
-qt_deploy_qml_imports(TARGET ${arg_TARGET} NO_QT_IMPORTS)
+${skip_message}
+qt6_deploy_qml_imports(TARGET ${arg_TARGET} NO_QT_IMPORTS)
")
elseif(NOT arg_NO_UNSUPPORTED_PLATFORM_ERROR AND NOT QT_INTERNAL_NO_UNSUPPORTED_PLATFORM_ERROR)
# Currently we don't deploy runtime dependencies if cross-compiling or using a static Qt.
- # We also don't do it if targeting Linux, but we could provide an option to do
- # so if we had a deploy tool or purely CMake-based deploy implementation.
# Error out by default unless the project opted out of the error.
# This provides us a migration path in the future without breaking compatibility promises.
message(FATAL_ERROR
"Support for installing runtime dependencies is not implemented for "
- "this target platform (${CMAKE_SYSTEM_NAME}, ${qt_build_type_string})."
+ "this target platform (${CMAKE_SYSTEM_NAME}, ${qt_build_type_string}). "
+ ${unsupported_platform_extra_message}
)
else()
qt6_generate_deploy_script(
@@ -3206,8 +3895,7 @@ qt_deploy_qml_imports(TARGET ${arg_TARGET} NO_QT_IMPORTS)
NAME ${deploy_script_name}
OUTPUT_SCRIPT deploy_script
CONTENT "
-include(${QT_DEPLOY_SUPPORT})
-_qt_internal_show_skip_runtime_deploy_message(\"${qt_build_type_string}\")
+${skip_message}
_qt_internal_show_skip_qml_runtime_deploy_message()
")
endif()
@@ -3579,3 +4267,38 @@ function(_qt_internal_add_qml_static_plugin_dependency target dep_target)
"$<${skip_prl_marker}:$<TARGET_NAME:${dep_target}>>")
endif()
endfunction()
+
+function(_qt_internal_collect_qml_module_dependencies target)
+ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.19.0")
+ cmake_language(EVAL CODE
+ "cmake_language(DEFER CALL _qt_internal_collect_qml_module_dependencies_deferred \"${target}\")"
+ )
+ else()
+ _qt_internal_collect_qml_module_dependencies_deferred("${target}")
+ endif()
+endfunction()
+
+function(_qt_internal_collect_qml_module_dependencies_deferred target)
+ get_target_property(deps ${target} QT_QML_MODULE_DEPENDENCIES)
+ if(NOT deps)
+ return()
+ endif()
+ foreach(dep IN LISTS deps)
+ string(REPLACE " " ";" dep "${dep}")
+ list(GET dep 0 dep_module_uri)
+ get_property(qml_uris GLOBAL PROPERTY _qt_all_qml_uris)
+ list(FIND qml_uris "${dep_module_uri}" index)
+ if(index LESS 0)
+ continue()
+ endif()
+ get_property(qml_targets GLOBAL PROPERTY _qt_all_qml_targets)
+ list(GET qml_targets ${index} dep_module)
+ # Make the module target dependent on its non-imported QML dependencies.
+ if(TARGET "${dep_module}")
+ get_target_property(is_imported ${dep_module} IMPORTED)
+ if(NOT is_imported)
+ add_dependencies(${target} ${dep_module})
+ endif()
+ endif()
+ endforeach()
+endfunction()
diff --git a/src/qml/Qt6QmlPluginTemplate.cpp.in b/src/qml/Qt6QmlPluginTemplate.cpp.in
index c96190dae1..f49aceb8d1 100644
--- a/src/qml/Qt6QmlPluginTemplate.cpp.in
+++ b/src/qml/Qt6QmlPluginTemplate.cpp.in
@@ -1,5 +1,6 @@
// This file is autogenerated by CMake. Do not edit.
+#include <QtCore/qtsymbolmacros.h>
#include <QtQml/qqmlextensionplugin.h>
@qt_qml_plugin_intro@
@@ -12,7 +13,7 @@ class @qt_qml_plugin_class_name@ : public QQmlEngineExtensionPlugin
public:
@qt_qml_plugin_class_name@(QObject *parent = nullptr) : QQmlEngineExtensionPlugin(parent)
{
- @qt_qml_plugin_constructor_content@
+@qt_qml_plugin_constructor_content@
}
};
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp
index 7bb2af476c..a50685ba50 100644
--- a/src/qml/animations/qabstractanimationjob.cpp
+++ b/src/qml/animations/qabstractanimationjob.cpp
@@ -37,6 +37,9 @@ void QQmlAnimationTimer::unsetJobTimer(QAbstractAnimationJob *animation)
if (animation->m_timer == this)
animation->m_timer = nullptr;
+ if (animation->m_isPause)
+ runningPauseAnimations.removeOne(animation);
+
if (animation->isGroup()) {
QAnimationGroupJob *group = static_cast<QAnimationGroupJob *>(animation);
if (const auto children = group->children()) {
@@ -214,10 +217,9 @@ void QQmlAnimationTimer::unregisterRunningAnimation(QAbstractAnimationJob *anima
if (animation->m_isGroup)
return;
- if (animation->m_isPause)
- runningPauseAnimations.removeOne(animation);
- else
+ if (!animation->m_isPause)
runningLeafAnimations--;
+
Q_ASSERT(runningLeafAnimations >= 0);
}
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h
index 6330b01bb1..04b47e3ae5 100644
--- a/src/qml/animations/qabstractanimationjob_p.h
+++ b/src/qml/animations/qabstractanimationjob_p.h
@@ -30,7 +30,7 @@ class QAnimationGroupJob;
class QAnimationJobChangeListener;
class QQmlAnimationTimer;
-class Q_QML_PRIVATE_EXPORT QAbstractAnimationJob : public QInheritedListNode
+class Q_QML_EXPORT QAbstractAnimationJob : public QInheritedListNode
{
Q_DISABLE_COPY(QAbstractAnimationJob)
public:
@@ -149,10 +149,10 @@ protected:
friend class QQmlAnimationTimer;
friend class QAnimationGroupJob;
- friend Q_QML_PRIVATE_EXPORT QDebug operator<<(QDebug, const QAbstractAnimationJob *job);
+ friend Q_QML_EXPORT QDebug operator<<(QDebug, const QAbstractAnimationJob *job);
};
-class Q_QML_PRIVATE_EXPORT QAnimationJobChangeListener
+class Q_QML_EXPORT QAnimationJobChangeListener
{
public:
virtual ~QAnimationJobChangeListener();
@@ -162,7 +162,7 @@ public:
virtual void animationCurrentTimeChanged(QAbstractAnimationJob *, int) {}
};
-class Q_QML_PRIVATE_EXPORT QQmlAnimationTimer : public QAbstractAnimationTimer
+class Q_QML_EXPORT QQmlAnimationTimer : public QAbstractAnimationTimer
{
Q_OBJECT
private:
@@ -223,7 +223,7 @@ private:
Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractAnimationJob::ChangeTypes)
-Q_QML_PRIVATE_EXPORT QDebug operator<<(QDebug, const QAbstractAnimationJob *job);
+Q_QML_EXPORT QDebug operator<<(QDebug, const QAbstractAnimationJob *job);
QT_END_NAMESPACE
diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h
index 3bfaf030a6..d276f63868 100644
--- a/src/qml/animations/qanimationgroupjob_p.h
+++ b/src/qml/animations/qanimationgroupjob_p.h
@@ -23,7 +23,7 @@ QT_REQUIRE_CONFIG(qml_animation);
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QAnimationGroupJob : public QAbstractAnimationJob
+class Q_QML_EXPORT QAnimationGroupJob : public QAbstractAnimationJob
{
Q_DISABLE_COPY(QAnimationGroupJob)
public:
diff --git a/src/qml/animations/qanimationjobutil_p.h b/src/qml/animations/qanimationjobutil_p.h
index f4798b127a..fb323d7c89 100644
--- a/src/qml/animations/qanimationjobutil_p.h
+++ b/src/qml/animations/qanimationjobutil_p.h
@@ -15,10 +15,19 @@
// We mean it.
//
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qtconfigmacros.h>
+
#include <type_traits>
QT_REQUIRE_CONFIG(qml_animation);
+#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU_ONLY >= 1300
+# define ACTION_IF_DISABLE_DANGLING_POINTER_WARNING QT_WARNING_DISABLE_GCC("-Wdangling-pointer")
+#else
+# define ACTION_IF_DISABLE_DANGLING_POINTER_WARNING
+#endif
+
// SelfDeletable is used for self-destruction detection along with
// ACTION_IF_DELETED and RETURN_IF_DELETED macros. While using, the objects
// under test should have a member m_selfDeletable of type SelfDeletable
@@ -35,6 +44,8 @@ struct SelfDeletable {
// \param action post process if p was deleted under test.
#define ACTION_IF_DELETED(p, func, action) \
do { \
+ QT_WARNING_PUSH \
+ ACTION_IF_DISABLE_DANGLING_POINTER_WARNING \
static_assert(std::is_same<decltype((p)->m_selfDeletable), SelfDeletable>::value, "m_selfDeletable must be SelfDeletable");\
bool *prevWasDeleted = (p)->m_selfDeletable.m_wasDeleted; \
bool wasDeleted = false; \
@@ -46,6 +57,7 @@ do { \
{action;} \
} \
(p)->m_selfDeletable.m_wasDeleted = prevWasDeleted; \
+ QT_WARNING_POP \
} while (false)
#define RETURN_IF_DELETED(func) \
diff --git a/src/qml/animations/qcontinuinganimationgroupjob_p.h b/src/qml/animations/qcontinuinganimationgroupjob_p.h
index d1d621b684..1112262d4b 100644
--- a/src/qml/animations/qcontinuinganimationgroupjob_p.h
+++ b/src/qml/animations/qcontinuinganimationgroupjob_p.h
@@ -21,7 +21,7 @@ QT_REQUIRE_CONFIG(qml_animation);
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QContinuingAnimationGroupJob : public QAnimationGroupJob
+class Q_QML_EXPORT QContinuingAnimationGroupJob : public QAnimationGroupJob
{
Q_DISABLE_COPY(QContinuingAnimationGroupJob)
public:
diff --git a/src/qml/animations/qparallelanimationgroupjob_p.h b/src/qml/animations/qparallelanimationgroupjob_p.h
index c86806969f..c4708a8e5d 100644
--- a/src/qml/animations/qparallelanimationgroupjob_p.h
+++ b/src/qml/animations/qparallelanimationgroupjob_p.h
@@ -21,7 +21,7 @@ QT_REQUIRE_CONFIG(qml_animation);
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QParallelAnimationGroupJob : public QAnimationGroupJob
+class Q_QML_EXPORT QParallelAnimationGroupJob : public QAnimationGroupJob
{
Q_DISABLE_COPY(QParallelAnimationGroupJob)
public:
diff --git a/src/qml/animations/qpauseanimationjob_p.h b/src/qml/animations/qpauseanimationjob_p.h
index 166dc06a6f..e08186e165 100644
--- a/src/qml/animations/qpauseanimationjob_p.h
+++ b/src/qml/animations/qpauseanimationjob_p.h
@@ -21,7 +21,7 @@ QT_REQUIRE_CONFIG(qml_animation);
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QPauseAnimationJob : public QAbstractAnimationJob
+class Q_QML_EXPORT QPauseAnimationJob : public QAbstractAnimationJob
{
Q_DISABLE_COPY(QPauseAnimationJob)
public:
diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h
index c963043a77..c7d4319b4d 100644
--- a/src/qml/animations/qsequentialanimationgroupjob_p.h
+++ b/src/qml/animations/qsequentialanimationgroupjob_p.h
@@ -22,7 +22,7 @@ QT_REQUIRE_CONFIG(qml_animation);
QT_BEGIN_NAMESPACE
class QPauseAnimationJob;
-class Q_QML_PRIVATE_EXPORT QSequentialAnimationGroupJob : public QAnimationGroupJob
+class Q_QML_EXPORT QSequentialAnimationGroupJob : public QAnimationGroupJob
{
Q_DISABLE_COPY(QSequentialAnimationGroupJob)
public:
diff --git a/src/qml/common/qjsnumbercoercion.cpp b/src/qml/common/qjsnumbercoercion.cpp
index 986a3e97f2..8cd96a4e25 100644
--- a/src/qml/common/qjsnumbercoercion.cpp
+++ b/src/qml/common/qjsnumbercoercion.cpp
@@ -14,6 +14,36 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn bool QJSNumberCoercion::isInteger(double d)
+ \internal
+ \deprecated 6.7
+ */
+
+/*!
+ \fn bool QJSNumberCoercion::isArrayIndex(double d)
+ \internal
+
+ Checks whether \a d contains a value that can serve as an index into an array.
+ For that, \a d must be a non-negative value representable as an unsigned 32bit int.
+ */
+
+/*!
+ \fn bool QJSNumberCoercion::isArrayIndex(qint64 i)
+ \internal
+
+ Checks whether \a i contains a value that can serve as an index into an array.
+ For that, \a d must be a non-negative value representable as an unsigned 32bit int.
+*/
+
+/*!
+ \fn bool QJSNumberCoercion::isArrayIndex(quint64 i)
+ \internal
+
+ Checks whether \a i contains a value that can serve as an index into an array.
+ For that, \a d must be a value representable as an unsigned 32bit int.
+*/
+
+/*!
\fn int QJSNumberCoercion::toInteger(double d)
\internal
diff --git a/src/qml/common/qjsnumbercoercion.h b/src/qml/common/qjsnumbercoercion.h
index 03827f82f1..0023bff6e8 100644
--- a/src/qml/common/qjsnumbercoercion.h
+++ b/src/qml/common/qjsnumbercoercion.h
@@ -12,17 +12,47 @@ QT_BEGIN_NAMESPACE
class QJSNumberCoercion
{
public:
- static constexpr bool isInteger(double d) {
- return equals(d, d) && equals(static_cast<int>(d), d);
+
+ static constexpr bool isInteger(double d)
+ {
+ // Comparing d with itself checks for NaN and comparing d with the min and max values
+ // for int also covers infinities.
+ if (!equals(d, d) || d < (std::numeric_limits<int>::min)()
+ || d > (std::numeric_limits<int>::max)()) {
+ return false;
+ }
+
+ return equals(static_cast<int>(d), d);
+ }
+
+ static constexpr bool isArrayIndex(double d)
+ {
+ return d >= 0
+ && equals(d, d)
+ && d <= (std::numeric_limits<uint>::max)()
+ && equals(static_cast<uint>(d), d);
+ }
+
+ static constexpr bool isArrayIndex(qint64 i)
+ {
+ return i >= 0 && i <= (std::numeric_limits<uint>::max)();
+ }
+
+ static constexpr bool isArrayIndex(quint64 i)
+ {
+ return i <= (std::numeric_limits<uint>::max)();
}
static constexpr int toInteger(double d) {
+ // Check for NaN
if (!equals(d, d))
return 0;
- const int i = static_cast<int>(d);
- if (equals(i, d))
- return i;
+ if (d >= (std::numeric_limits<int>::min)() && d <= (std::numeric_limits<int>::max)()) {
+ const int i = static_cast<int>(d);
+ if (equals(i, d))
+ return i;
+ }
return QJSNumberCoercion(d).toInteger();
}
diff --git a/src/qml/common/qqmljsmemorypool_p.h b/src/qml/common/qqmljsmemorypool_p.h
index a2d18f9e7e..6f0f8ed491 100644
--- a/src/qml/common/qqmljsmemorypool_p.h
+++ b/src/qml/common/qqmljsmemorypool_p.h
@@ -43,13 +43,12 @@ public:
free(_blocks);
}
- qDeleteAll(strings);
}
inline void *allocate(size_t size)
{
size = (size + 7) & ~size_t(7);
- if (Q_LIKELY(_ptr && (_ptr + size < _end))) {
+ if (Q_LIKELY(_ptr && size < size_t(_end - _ptr))) {
void *addr = _ptr;
_ptr += size;
return addr;
@@ -67,9 +66,8 @@ public:
template <typename Tp, typename... Ta> Tp *New(Ta... args)
{ return new (this->allocate(sizeof(Tp))) Tp(args...); }
- QStringView newString(const QString &string) {
- strings.append(new QString(string));
- return QStringView(*strings.last());
+ QStringView newString(QString string) {
+ return strings.emplace_back(std::move(string));
}
private:
@@ -113,7 +111,7 @@ private:
int _blockCount = -1;
char *_ptr = nullptr;
char *_end = nullptr;
- QVector<QString*> strings;
+ QStringList strings;
enum
{
diff --git a/src/qml/common/qqmlsignalnames.cpp b/src/qml/common/qqmlsignalnames.cpp
new file mode 100644
index 0000000000..d2a23205a6
--- /dev/null
+++ b/src/qml/common/qqmlsignalnames.cpp
@@ -0,0 +1,257 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmlsignalnames_p.h"
+#include <iterator>
+#include <algorithm>
+#include <optional>
+#include <string>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::Literals;
+
+static constexpr const QLatin1String On("on");
+static constexpr const QLatin1String Changed("Changed");
+
+static constexpr const qsizetype StrlenOn = On.length();
+static constexpr const qsizetype StrlenChanged = Changed.length();
+
+static std::optional<qsizetype> firstLetterIdx(QStringView name, qsizetype removePrefix = 0,
+ qsizetype removeSuffix = 0)
+{
+ auto end = std::prev(name.cend(), removeSuffix);
+ auto result = std::find_if(std::next(name.cbegin(), removePrefix), end,
+ [](const QChar &c) { return c.isLetter(); });
+ if (result != end)
+ return std::distance(name.begin(), result);
+
+ return {};
+}
+
+static std::optional<QChar> firstLetter(QStringView name, qsizetype removePrefix = 0,
+ qsizetype removeSuffix = 0)
+{
+ if (auto idx = firstLetterIdx(name, removePrefix, removeSuffix))
+ return name[*idx];
+ return {};
+}
+
+enum ChangeCase { ToUpper, ToLower };
+static void changeCaseOfFirstLetter(QString &name, ChangeCase option, qsizetype removePrefix = 0,
+ qsizetype removeSuffix = 0)
+{
+ auto idx = firstLetterIdx(name, removePrefix, removeSuffix);
+ if (!idx)
+ return;
+
+ QChar &changeMe = name[*idx];
+ changeMe = option == ToUpper ? changeMe.toUpper() : changeMe.toLower();
+};
+
+static std::optional<QString> toQStringData(std::optional<QStringView> view)
+{
+ if (view)
+ return view->toString();
+ return std::nullopt;
+}
+
+static QByteArray toUtf8Data(QUtf8StringView view)
+{
+ return QByteArray(view.data(), view.size());
+}
+
+static std::optional<QByteArray> toUtf8Data(std::optional<QUtf8StringView> view)
+{
+ if (view)
+ return toUtf8Data(*view);
+ return std::nullopt;
+}
+
+/*!
+\internal
+\class QQmlSignalNames
+
+QQmlSignalNames contains a list of helper methods to manipulate signal names.
+Always try to use the most specific one, as combining them might lead to incorrect
+results like wrong upper/lower case, for example.
+*/
+
+/*!
+\internal
+Concatenate a prefix to a property name and uppercases the first letter of the property name.
+*/
+QString QQmlSignalNames::addPrefixToPropertyName(QStringView prefix, QStringView propertyName)
+{
+ QString result = prefix.toString().append(propertyName);
+ changeCaseOfFirstLetter(result, ToUpper, prefix.size());
+ return result;
+}
+
+QString QQmlSignalNames::propertyNameToChangedSignalName(QStringView property)
+{
+ return property.toString().append(Changed);
+}
+
+QByteArray QQmlSignalNames::propertyNameToChangedSignalName(QUtf8StringView property)
+{
+ return toUtf8Data(property).append(QByteArrayView(Changed));
+}
+
+QString QQmlSignalNames::propertyNameToChangedHandlerName(QStringView property)
+{
+ return propertyNameToChangedSignalName(signalNameToHandlerName(property));
+}
+
+template<typename View>
+std::optional<View> changedSignalNameToPropertyNameTemplate(View changeSignal)
+{
+ const qsizetype changeSignalSize = changeSignal.size();
+ if (changeSignalSize < StrlenChanged || changeSignal.last(StrlenChanged).compare(Changed) != 0)
+ return std::nullopt;
+
+ const View result = changeSignal.sliced(0, changeSignalSize - StrlenChanged);
+ if (!result.isEmpty())
+ return result;
+
+ return {};
+}
+
+/*!
+\internal
+Obtain a propertyName from its changed signal handler.
+Do not call this on a value obtained from handlerNameToSignalName! Instead use
+changedHandlerNameToPropertyName() directly. Otherwise you might end up with a wrong
+capitalization of _Changed for "on_Changed", for example.
+*/
+
+std::optional<QString> QQmlSignalNames::changedSignalNameToPropertyName(QStringView signalName)
+{
+ return toQStringData(changedSignalNameToPropertyNameTemplate(signalName));
+}
+std::optional<QByteArray>
+QQmlSignalNames::changedSignalNameToPropertyName(QUtf8StringView signalName)
+{
+ return toUtf8Data(changedSignalNameToPropertyNameTemplate(signalName));
+}
+
+/*!
+\internal
+Returns a property name from \a changedHandler.
+This fails for property names starting with an upper-case letter, as it will lower-case it in the
+process.
+*/
+std::optional<QString> QQmlSignalNames::changedHandlerNameToPropertyName(QStringView handler)
+{
+ if (!isChangedHandlerName(handler))
+ return {};
+
+ if (auto withoutChangedSuffix = changedSignalNameToPropertyName(handler)) {
+ return handlerNameToSignalName(*withoutChangedSuffix);
+ }
+ return {};
+}
+
+QString QQmlSignalNames::signalNameToHandlerName(QAnyStringView signal)
+{
+ QString handlerName;
+ handlerName.reserve(StrlenOn + signal.length());
+ handlerName.append(On);
+
+ signal.visit([&handlerName](auto &&s) { handlerName.append(s); });
+
+ changeCaseOfFirstLetter(handlerName, ToUpper, StrlenOn);
+ return handlerName;
+}
+
+enum HandlerType { ChangedHandler, Handler };
+
+template<HandlerType type>
+static std::optional<QString> handlerNameToSignalNameHelper(QStringView handler)
+{
+ if (!QQmlSignalNames::isHandlerName(handler))
+ return {};
+
+ QString signalName = handler.sliced(StrlenOn).toString();
+ Q_ASSERT(!signalName.isEmpty());
+
+ changeCaseOfFirstLetter(signalName, ToLower, 0, type == ChangedHandler ? StrlenChanged : 0);
+ return signalName;
+}
+
+/*!
+\internal
+Returns a signal name from \a handlerName string. Do not use it on changed handlers, see
+changedHandlerNameToSignalName for that!
+*/
+std::optional<QString> QQmlSignalNames::handlerNameToSignalName(QStringView handler)
+{
+ return handlerNameToSignalNameHelper<Handler>(handler);
+}
+
+/*!
+\internal
+Returns a signal name from \a handlerName string. Do not use it on changed handlers, see
+changedHandlerNameToSignalName for that! Accepts improperly capitalized handler names and
+incorrectly resolves signal names that start with '_' or '$'.
+*/
+std::optional<QString> QQmlSignalNames::badHandlerNameToSignalName(QStringView handler)
+{
+ if (handler.size() <= StrlenOn || !handler.startsWith(On))
+ return {};
+
+ QString signalName = handler.sliced(StrlenOn).toString();
+
+ // This is quite wrong. But we need it for backwards compatibility.
+ signalName.front() = signalName.front().toLower();
+
+ return signalName;
+}
+
+/*!
+\internal
+Returns a signal name from \a changedHandlerName string. Makes sure not to lowercase the 'C' from
+Changed.
+*/
+std::optional<QString> QQmlSignalNames::changedHandlerNameToSignalName(QStringView handler)
+{
+ return handlerNameToSignalNameHelper<ChangedHandler>(handler);
+}
+
+bool QQmlSignalNames::isChangedSignalName(QStringView signalName)
+{
+ if (signalName.size() <= StrlenChanged || !signalName.endsWith(Changed))
+ return false;
+
+ if (auto letter = firstLetter(signalName, 0, StrlenChanged))
+ return letter->isLower();
+
+ return true;
+}
+
+bool QQmlSignalNames::isChangedHandlerName(QStringView signalName)
+{
+ if (signalName.size() <= (StrlenOn + StrlenChanged)
+ || !signalName.startsWith(On)
+ || !signalName.endsWith(Changed)) {
+ return false;
+ }
+
+ if (auto letter = firstLetter(signalName, StrlenOn, StrlenChanged))
+ return letter->isUpper();
+
+ return true;
+}
+
+bool QQmlSignalNames::isHandlerName(QStringView signalName)
+{
+ if (signalName.size() <= StrlenOn || !signalName.startsWith(On))
+ return false;
+
+ if (auto letter = firstLetter(signalName, StrlenOn))
+ return letter->isUpper();
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/common/qqmlsignalnames_p.h b/src/qml/common/qqmlsignalnames_p.h
new file mode 100644
index 0000000000..9c3d192666
--- /dev/null
+++ b/src/qml/common/qqmlsignalnames_p.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLSIGNALANDPROPERTYNAMES_P_H
+#define QQMLSIGNALANDPROPERTYNAMES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <cstddef>
+#include <optional>
+
+#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtCore/qstringview.h>
+#include <QtCore/qstring.h>
+#include <type_traits>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_EXPORT QQmlSignalNames
+{
+public:
+ static QString propertyNameToChangedSignalName(QStringView property);
+ static QByteArray propertyNameToChangedSignalName(QUtf8StringView property);
+
+ static QString propertyNameToChangedHandlerName(QStringView property);
+
+ static QString signalNameToHandlerName(QAnyStringView signal);
+
+ static std::optional<QString> changedSignalNameToPropertyName(QStringView changeSignal);
+ static std::optional<QByteArray> changedSignalNameToPropertyName(QUtf8StringView changeSignal);
+
+ static std::optional<QString> changedHandlerNameToPropertyName(QStringView handler);
+ static std::optional<QByteArray> changedHandlerNameToPropertyName(QUtf8StringView handler);
+
+ static std::optional<QString> handlerNameToSignalName(QStringView handler);
+ static std::optional<QString> changedHandlerNameToSignalName(QStringView changedHandler);
+
+ static bool isChangedHandlerName(QStringView signalName);
+ static bool isChangedSignalName(QStringView signalName);
+ static bool isHandlerName(QStringView signalName);
+
+ static QString addPrefixToPropertyName(QStringView prefix, QStringView propertyName);
+
+ // ### Qt7: remove this
+ static std::optional<QString> badHandlerNameToSignalName(QStringView handler);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSIGNALANDPROPERTYNAMES_P_H
diff --git a/src/qml/common/qqmltranslation_p.h b/src/qml/common/qqmltranslation_p.h
index 4a565ff5f7..9849203abe 100644
--- a/src/qml/common/qqmltranslation_p.h
+++ b/src/qml/common/qqmltranslation_p.h
@@ -21,10 +21,10 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlTranslation
+class Q_QML_EXPORT QQmlTranslation
{
public:
- class Q_QML_PRIVATE_EXPORT QsTrData
+ class Q_QML_EXPORT QsTrData
{
QByteArray context;
QByteArray text;
@@ -39,7 +39,7 @@ public:
QString idForQmlDebug() const;
};
- class Q_QML_PRIVATE_EXPORT QsTrIdData
+ class Q_QML_EXPORT QsTrIdData
{
QByteArray id;
int number;
diff --git a/src/qml/common/qv4compileddata.cpp b/src/qml/common/qv4compileddata.cpp
new file mode 100644
index 0000000000..9dee91f713
--- /dev/null
+++ b/src/qml/common/qv4compileddata.cpp
@@ -0,0 +1,431 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qv4compileddata_p.h"
+
+#include <private/inlinecomponentutils_p.h>
+#include <private/qml_compile_hash_p.h>
+#include <private/qqmlscriptdata_p.h>
+#include <private/qqmltypenamecache_p.h>
+#include <private/qv4resolvedtypereference_p.h>
+
+#include <QtQml/qqmlfile.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qstandardpaths.h>
+
+static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
+
+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
+# ifdef Q_OS_LINUX
+// Place on a separate section on Linux so it's easier to check from outside
+// what the hash version is.
+__attribute__((section(".qml_compile_hash")))
+# endif
+const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH;
+static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH,
+ "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
+#else
+# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace CompiledData {
+
+
+bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const
+{
+ if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) {
+ *errorString = QStringLiteral("Magic bytes in the header do not match");
+ return false;
+ }
+
+ if (version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
+ *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2")
+ .arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
+ return false;
+ }
+
+ if (qtVersion != quint32(QT_VERSION)) {
+ *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2")
+ .arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
+ return false;
+ }
+
+ if (sourceTimeStamp) {
+ // Files from the resource system do not have any time stamps, so fall back to the application
+ // executable.
+ if (!expectedSourceTimeStamp.isValid())
+ expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
+
+ if (expectedSourceTimeStamp.isValid()
+ && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) {
+ *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
+ return false;
+ }
+ }
+
+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
+ if (qstrncmp(qml_compile_hash, libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) {
+ *errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2")
+ .arg(QString::fromLatin1(
+ QByteArray(libraryVersionHash, QML_COMPILE_HASH_LENGTH)
+ .toPercentEncoding()),
+ QString::fromLatin1(
+ QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH)
+ .toPercentEncoding()));
+ return false;
+ }
+#else
+#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
+#endif
+ return true;
+}
+
+/*!
+ \internal
+ This function creates a temporary key vector and sorts it to guarantuee a stable
+ hash. This is used to calculate a check-sum on dependent meta-objects.
+ */
+bool ResolvedTypeReferenceMap::addToHash(
+ QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const
+{
+ std::vector<int> keys (size());
+ int i = 0;
+ for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
+ keys[i] = it.key();
+ ++i;
+ }
+ std::sort(keys.begin(), keys.end());
+ for (int key: keys) {
+ if (!this->operator[](key)->addToHash(hash, checksums))
+ return false;
+ }
+
+ return true;
+}
+
+CompilationUnit::CompilationUnit(
+ const Unit *unitData, const QString &fileName, const QString &finalUrlString)
+{
+ setUnitData(unitData, nullptr, fileName, finalUrlString);
+}
+
+CompilationUnit::~CompilationUnit()
+{
+ qDeleteAll(resolvedTypes);
+
+ if (data) {
+ if (data->qmlUnit() != qmlData)
+ free(const_cast<QmlUnit *>(qmlData));
+ qmlData = nullptr;
+
+ if (!(data->flags & QV4::CompiledData::Unit::StaticData))
+ free(const_cast<Unit *>(data));
+ }
+ data = nullptr;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+ constants = nullptr;
+#endif
+}
+
+QString CompilationUnit::localCacheFilePath(const QUrl &url)
+{
+ static const QByteArray envCachePath = qgetenv("QML_DISK_CACHE_PATH");
+
+ const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString cacheFileSuffix
+ = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
+ QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
+ fileNameHash.addData(localSourcePath.toUtf8());
+ QString directory = envCachePath.isEmpty()
+ ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
+ + QLatin1String("/qmlcache/")
+ : QString::fromLocal8Bit(envCachePath) + QLatin1String("/");
+ QDir::root().mkpath(directory);
+ return directory + QString::fromUtf8(fileNameHash.result().toHex())
+ + QLatin1Char('.') + cacheFileSuffix;
+}
+
+bool CompilationUnit::loadFromDisk(
+ const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
+{
+ if (!QQmlFile::isLocalFile(url)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ auto cacheFile = std::make_unique<CompilationUnitMapper>();
+
+ const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
+ for (const QString &cachePath : cachePaths) {
+ Unit *mappedUnit = cacheFile->get(cachePath, sourceTimeStamp, errorString);
+ if (!mappedUnit)
+ continue;
+
+ const Unit *oldData = unitData();
+ const Unit * const oldDataPtr
+ = (oldData && !(oldData->flags & Unit::StaticData))
+ ? oldData
+ : nullptr;
+
+ auto dataPtrRevert = qScopeGuard([this, oldData](){
+ setUnitData(oldData);
+ });
+ setUnitData(mappedUnit);
+
+ if (mappedUnit->sourceFileIndex != 0) {
+ if (mappedUnit->sourceFileIndex >=
+ mappedUnit->stringTableSize + dynamicStrings.size()) {
+ *errorString = QStringLiteral("QML source file index is invalid.");
+ continue;
+ }
+ if (sourcePath !=
+ QQmlFile::urlToLocalFileOrQrc(stringAt(mappedUnit->sourceFileIndex))) {
+ *errorString = QStringLiteral("QML source file has moved to a different location.");
+ continue;
+ }
+ }
+
+ dataPtrRevert.dismiss();
+ free(const_cast<Unit*>(oldDataPtr));
+ backingFile = std::move(cacheFile);
+ return true;
+ }
+
+ return false;
+}
+
+bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
+{
+ if (unitData()->sourceTimeStamp == 0) {
+ *errorString = QStringLiteral("Missing time stamp for source file");
+ return false;
+ }
+
+ if (!QQmlFile::isLocalFile(unitUrl)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ return SaveableUnitPointer(unitData()).saveToDisk<char>(
+ [&unitUrl, errorString](const char *data, quint32 size) {
+ const QString cachePath = localCacheFilePath(unitUrl);
+ if (SaveableUnitPointer::writeDataToFile(
+ cachePath, data, size, errorString)) {
+ CompilationUnitMapper::invalidate(cachePath);
+ return true;
+ }
+
+ return false;
+ });
+}
+
+QStringList CompilationUnit::moduleRequests() const
+{
+ QStringList requests;
+ requests.reserve(data->moduleRequestTableSize);
+ for (uint i = 0; i < data->moduleRequestTableSize; ++i)
+ requests << stringAt(data->moduleRequestTable()[i]);
+ return requests;
+}
+
+ResolvedTypeReference *CompilationUnit::resolvedType(QMetaType type) const
+{
+ for (ResolvedTypeReference *ref : std::as_const(resolvedTypes)) {
+ if (ref->type().typeId() == type)
+ return ref;
+ }
+ return nullptr;
+
+}
+
+int CompilationUnit::totalBindingsCount() const
+{
+ if (!icRootName)
+ return m_totalBindingsCount;
+ return inlineComponentData[*icRootName].totalBindingCount;
+}
+
+int CompilationUnit::totalObjectCount() const
+{
+ if (!icRootName)
+ return m_totalObjectCount;
+ return inlineComponentData[*icRootName].totalObjectCount;
+}
+
+template<typename F>
+void processInlinComponentType(
+ const QQmlType &type,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
+ F &&populateIcData)
+{
+ if (type.isInlineComponentType()) {
+ QString icRootName;
+ if (compilationUnit->icRootName) {
+ icRootName = type.elementName();
+ std::swap(*compilationUnit->icRootName, icRootName);
+ } else {
+ compilationUnit->icRootName = std::make_unique<QString>(type.elementName());
+ }
+
+ populateIcData();
+
+ if (icRootName.isEmpty())
+ compilationUnit->icRootName.reset();
+ else
+ std::swap(*compilationUnit->icRootName, icRootName);
+ } else {
+ populateIcData();
+ }
+}
+
+void CompiledData::CompilationUnit::finalizeCompositeType(const QQmlType &type)
+{
+ // Add to type registry of composites
+ if (propertyCaches.needsVMEMetaObject(/*root object*/0)) {
+ // qmlType is only valid for types that have references to themselves.
+ if (type.isValid()) {
+ qmlType = type;
+ } else {
+ qmlType = QQmlMetaType::findCompositeType(
+ finalUrl(), this, (unitData()->flags & CompiledData::Unit::IsSingleton)
+ ? QQmlMetaType::Singleton
+ : QQmlMetaType::NonSingleton);
+ }
+
+ QQmlMetaType::registerInternalCompositeType(this);
+ } else {
+ const QV4::CompiledData::Object *obj = objectAt(/*root object*/0);
+ auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+ if (const auto compilationUnit = typeRef->compilationUnit())
+ qmlType = compilationUnit->qmlType;
+ else
+ qmlType = typeRef->type();
+ }
+
+ // Collect some data for instantiation later.
+ using namespace icutils;
+ std::vector<QV4::CompiledData::InlineComponent> allICs {};
+ for (int i=0; i != objectCount(); ++i) {
+ const CompiledObject *obj = objectAt(i);
+ for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
+ allICs.push_back(*it);
+ }
+ }
+ NodeList nodes;
+ nodes.resize(allICs.size());
+ std::iota(nodes.begin(), nodes.end(), 0);
+ AdjacencyList adjacencyList;
+ adjacencyList.resize(nodes.size());
+ fillAdjacencyListForInlineComponents(this, adjacencyList, nodes, allICs);
+ bool hasCycle = false;
+ auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
+ Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator
+
+ // We need to first iterate over all inline components,
+ // as the containing component might create instances of them
+ // and in that case we need to add its object count
+ for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
+ const auto &ic = allICs.at(nodeIt->index());
+ const int lastICRoot = ic.objectIndex;
+ for (int i = ic.objectIndex; i<objectCount(); ++i) {
+ const QV4::CompiledData::Object *obj = objectAt(i);
+ bool leftCurrentInlineComponent
+ = (i != lastICRoot
+ && obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot))
+ || !obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent);
+ if (leftCurrentInlineComponent)
+ break;
+ const QString lastICRootName = stringAt(ic.nameIndex);
+ inlineComponentData[lastICRootName].totalBindingCount
+ += obj->nBindings;
+
+ if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
+ const auto type = typeRef->type();
+ if (type.isValid() && type.parserStatusCast() != -1)
+ ++inlineComponentData[lastICRootName].totalParserStatusCount;
+
+ ++inlineComponentData[lastICRootName].totalObjectCount;
+ if (const auto compilationUnit = typeRef->compilationUnit()) {
+ // if the type is an inline component type, we have to extract the information
+ // from it.
+ // This requires that inline components are visited in the correct order.
+ processInlinComponentType(type, compilationUnit, [&]() {
+ auto &icData = inlineComponentData[lastICRootName];
+ icData.totalBindingCount += compilationUnit->totalBindingsCount();
+ icData.totalParserStatusCount += compilationUnit->totalParserStatusCount();
+ icData.totalObjectCount += compilationUnit->totalObjectCount();
+ });
+ }
+ }
+ }
+ }
+ int bindingCount = 0;
+ int parserStatusCount = 0;
+ int objectCount = 0;
+ for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
+ const QV4::CompiledData::Object *obj = objectAt(i);
+ if (obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent))
+ continue;
+
+ bindingCount += obj->nBindings;
+ if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
+ const auto type = typeRef->type();
+ if (type.isValid() && type.parserStatusCast() != -1)
+ ++parserStatusCount;
+ ++objectCount;
+ if (const auto compilationUnit = typeRef->compilationUnit()) {
+ processInlinComponentType(type, compilationUnit, [&](){
+ bindingCount += compilationUnit->totalBindingsCount();
+ parserStatusCount += compilationUnit->totalParserStatusCount();
+ objectCount += compilationUnit->totalObjectCount();
+ });
+ }
+ }
+ }
+
+ m_totalBindingsCount = bindingCount;
+ m_totalParserStatusCount = parserStatusCount;
+ m_totalObjectCount = objectCount;
+}
+
+int CompilationUnit::totalParserStatusCount() const
+{
+ if (!icRootName)
+ return m_totalParserStatusCount;
+ return inlineComponentData[*icRootName].totalParserStatusCount;
+}
+
+bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const
+{
+ if (!dependencyHasher) {
+ for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
+ if (data->dependencyMD5Checksum[i] != 0)
+ return false;
+ }
+ return true;
+ }
+ const QByteArray checksum = dependencyHasher();
+ return checksum.size() == sizeof(data->dependencyMD5Checksum)
+ && memcmp(data->dependencyMD5Checksum, checksum.constData(),
+ sizeof(data->dependencyMD5Checksum)) == 0;
+}
+
+QQmlType CompilationUnit::qmlTypeForComponent(const QString &inlineComponentName) const
+{
+ if (inlineComponentName.isEmpty())
+ return qmlType;
+ return inlineComponentData[inlineComponentName].qmlType;
+}
+
+} // namespace CompiledData
+} // namespace QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index cd96067769..79df230872 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -16,21 +16,29 @@
#include <functional>
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qhash.h>
#include <QtCore/qhashfunctions.h>
-#include <QtCore/qstring.h>
+#include <QtCore/qlocale.h>
#include <QtCore/qscopeguard.h>
-#include <QtCore/qvector.h>
+#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
-#include <QtCore/qhash.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvector.h>
#include <QtCore/qversionnumber.h>
-#include <QtCore/qlocale.h>
#if QT_CONFIG(temporaryfile)
#include <QtCore/qsavefile.h>
#endif
#include <private/qendian_p.h>
+#include <private/qqmlnullablevalue_p.h>
+#include <private/qqmlpropertycachevector_p.h>
+#include <private/qqmlrefcount_p.h>
+#include <private/qqmltype_p.h>
+#include <private/qv4compilationunitmapper_p.h>
#include <private/qv4staticvalue_p.h>
+
#include <functional>
#include <limits.h>
@@ -43,15 +51,17 @@ QT_BEGIN_NAMESPACE
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
-#define QV4_DATA_STRUCTURE_VERSION 0x3B // Add isList flag to method parameters and return types
+#define QV4_DATA_STRUCTURE_VERSION 0x42 // Change metatype computation of AOT-compiled functions
class QIODevice;
class QQmlTypeNameCache;
class QQmlType;
class QQmlEngine;
+class QQmlPropertyData;
+class QQmlScriptData;
namespace QQmlPrivate {
-struct TypedFunction;
+struct AOTCompiledFunction;
}
namespace QmlIR {
@@ -67,9 +77,19 @@ struct InternalClass;
struct Function;
class EvalISelFactory;
+class ResolvedTypeReference;
namespace CompiledData {
+// index is per-object binding index
+using BindingPropertyData = QVector<const QQmlPropertyData *>;
+
+// map from name index
+struct ResolvedTypeReferenceMap: public QHash<int, ResolvedTypeReference*>
+{
+ bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const;
+};
+
struct String;
struct Function;
struct Lookup;
@@ -141,8 +161,8 @@ struct RegExp
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
RegExp_Multiline = 0x04,
- RegExp_Unicode = 0x08,
- RegExp_Sticky = 0x10
+ RegExp_Sticky = 0x08,
+ RegExp_Unicode = 0x10,
};
RegExp() : m_data(QSpecialIntegerBitfieldZero) {}
@@ -268,31 +288,48 @@ struct Block
};
static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
-enum class BuiltinType : unsigned int {
- Var = 0, Int, Bool, Real, String, Url,
- Time, Date, DateTime, Rect, Point, Size,
- InvalidBuiltin
+enum class NamedBuiltin: unsigned int {
+ Void, Var, Int, Bool, Real, String, Url, DateTime, RegExp
+};
+
+enum class CommonType : unsigned int {
+ // Actual named builtins
+ Void = uint(NamedBuiltin::Void),
+ Var = uint(NamedBuiltin::Var),
+ Int = uint(NamedBuiltin::Int),
+ Bool = uint(NamedBuiltin::Bool),
+ Real = uint(NamedBuiltin::Real),
+ String = uint(NamedBuiltin::String),
+ Url = uint(NamedBuiltin::Url),
+ DateTime = uint(NamedBuiltin::DateTime),
+ RegExp = uint(NamedBuiltin::RegExp),
+
+ // Optimization for very common other types
+ Time, Date, Rect, Point, Size,
+
+ // No type specified or not recognized
+ Invalid
};
struct ParameterType
{
enum Flag {
NoFlag = 0x0,
- Builtin = 0x1,
+ Common = 0x1,
List = 0x2,
};
Q_DECLARE_FLAGS(Flags, Flag);
- void set(Flags flags, quint32 typeNameIndexOrBuiltinType)
+ void set(Flags flags, quint32 typeNameIndexOrCommonType)
{
m_data.set<IsListField>(flags.testFlag(List) ? 1 : 0);
- m_data.set<IndexIsBuiltinTypeField>(flags.testFlag(Builtin) ? 1 : 0);
- m_data.set<TypeNameIndexOrBuiltinTypeField>(typeNameIndexOrBuiltinType);
+ m_data.set<IndexIsCommonTypeField>(flags.testFlag(Common) ? 1 : 0);
+ m_data.set<TypeNameIndexOrCommonTypeField>(typeNameIndexOrCommonType);
}
- bool indexIsBuiltinType() const
+ bool indexIsCommonType() const
{
- return m_data.get<IndexIsBuiltinTypeField>() != 0;
+ return m_data.get<IndexIsCommonTypeField>() != 0;
}
bool isList() const
@@ -300,16 +337,16 @@ struct ParameterType
return m_data.get<IsListField>() != 0;
}
- quint32 typeNameIndexOrBuiltinType() const
+ quint32 typeNameIndexOrCommonType() const
{
- return m_data.get<TypeNameIndexOrBuiltinTypeField>();
+ return m_data.get<TypeNameIndexOrCommonTypeField>();
}
private:
- using IndexIsBuiltinTypeField = quint32_le_bitfield_member<0, 1>;
+ using IndexIsCommonTypeField = quint32_le_bitfield_member<0, 1>;
using IsListField = quint32_le_bitfield_member<1, 1>;
- using TypeNameIndexOrBuiltinTypeField = quint32_le_bitfield_member<2, 30>;
- quint32_le_bitfield_union<IndexIsBuiltinTypeField, IsListField, TypeNameIndexOrBuiltinTypeField> m_data;
+ using TypeNameIndexOrCommonTypeField = quint32_le_bitfield_member<2, 30>;
+ quint32_le_bitfield_union<IndexIsCommonTypeField, IsListField, TypeNameIndexOrCommonTypeField> m_data;
};
static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -497,6 +534,7 @@ static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have th
struct TranslationData
{
+ enum { NoContextIndex = std::numeric_limits<quint32>::max() };
quint32_le stringIndex;
quint32_le commentIndex;
qint32_le number;
@@ -669,6 +707,8 @@ struct Binding
}
bool evaluatesToString() const { return type() == Type_String || isTranslationBinding(); }
+ bool isNumberBinding() const { return type() == Type_Number; }
+
bool valueAsBoolean() const
{
if (type() == Type_Boolean)
@@ -748,47 +788,47 @@ static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected
struct Property
{
private:
- using BuiltinTypeOrTypeNameIndexField = quint32_le_bitfield_member<0, 28>;
+ using CommonTypeOrTypeNameIndexField = quint32_le_bitfield_member<0, 28>;
using IsRequiredField = quint32_le_bitfield_member<28, 1>;
- using IsBuiltinTypeField = quint32_le_bitfield_member<29, 1>;
+ using IsCommonTypeField = quint32_le_bitfield_member<29, 1>;
using IsListField = quint32_le_bitfield_member<30, 1>;
using IsReadOnlyField = quint32_le_bitfield_member<31, 1>;
public:
quint32_le nameIndex;
quint32_le_bitfield_union<
- BuiltinTypeOrTypeNameIndexField,
+ CommonTypeOrTypeNameIndexField,
IsRequiredField,
- IsBuiltinTypeField,
+ IsCommonTypeField,
IsListField,
IsReadOnlyField> data;
Location location;
- void setBuiltinType(BuiltinType t)
+ void setCommonType(CommonType t)
{
- data.set<BuiltinTypeOrTypeNameIndexField>(static_cast<quint32>(t));
- data.set<IsBuiltinTypeField>(true);
+ data.set<CommonTypeOrTypeNameIndexField>(static_cast<quint32>(t));
+ data.set<IsCommonTypeField>(true);
}
- BuiltinType builtinType() const {
- if (data.get<IsBuiltinTypeField>() != 0)
- return BuiltinType(data.get<BuiltinTypeOrTypeNameIndexField>());
- return BuiltinType::InvalidBuiltin;
+ CommonType commonType() const {
+ if (data.get<IsCommonTypeField>() != 0)
+ return CommonType(data.get<CommonTypeOrTypeNameIndexField>());
+ return CommonType::Invalid;
}
- void setCustomType(int nameIndex)
+ void setTypeNameIndex(int nameIndex)
{
- data.set<BuiltinTypeOrTypeNameIndexField>(nameIndex);
- data.set<IsBuiltinTypeField>(false);
+ data.set<CommonTypeOrTypeNameIndexField>(nameIndex);
+ data.set<IsCommonTypeField>(false);
}
- int customType() const
+ int typeNameIndex() const
{
- return data.get<IsBuiltinTypeField>() ? -1 : data.get<BuiltinTypeOrTypeNameIndexField>();
+ return data.get<IsCommonTypeField>() ? -1 : data.get<CommonTypeOrTypeNameIndexField>();
}
- bool isBuiltinType() const { return data.get<IsBuiltinTypeField>(); }
- uint builtinTypeOrTypeNameIndex() const { return data.get<BuiltinTypeOrTypeNameIndexField>(); }
+ bool isCommonType() const { return data.get<IsCommonTypeField>(); }
+ uint commonTypeOrTypeNameIndex() const { return data.get<CommonTypeOrTypeNameIndexField>(); }
bool isList() const { return data.get<IsListField>(); }
void setIsList(bool isList) { data.set<IsListField>(isList); }
@@ -1080,6 +1120,7 @@ public:
const Binding *bindingsBegin() const { return bindingTable(); }
const Binding *bindingsEnd() const { return bindingTable() + nBindings; }
+ int bindingCount() const { return nBindings; }
const Property *propertiesBegin() const { return propertyTable(); }
const Property *propertiesEnd() const { return propertyTable() + nProperties; }
@@ -1182,9 +1223,10 @@ struct Unit
ListPropertyAssignReplace
= ListPropertyAssignReplaceIfDefault | ListPropertyAssignReplaceIfNotDefault,
ComponentsBound = 0x200,
- FunctionSignaturesEnforced = 0x400,
+ FunctionSignaturesIgnored = 0x400,
NativeMethodsAcceptThisObject = 0x800,
ValueTypesCopied = 0x1000,
+ ValueTypesAddressable = 0x2000,
};
quint32_le flags;
quint32_le stringTableSize;
@@ -1238,8 +1280,8 @@ struct Unit
}
/* end QML specific fields*/
- QString stringAtInternal(int idx) const {
- Q_ASSERT(idx < int(stringTableSize));
+ QString stringAtInternal(uint idx) const {
+ Q_ASSERT(idx < stringTableSize);
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
const quint32_le offset = offsetTable[idx];
const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset);
@@ -1311,12 +1353,28 @@ struct Unit
return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable);
}
+ const quint32_le *translationContextIndex() const{
+ if ( translationTableSize == 0)
+ return nullptr;
+ return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this))
+ + offsetToTranslationTable
+ + translationTableSize * sizeof(CompiledData::TranslationData)); }
+
+ quint32_le *translationContextIndex() {
+ if ( translationTableSize == 0)
+ return nullptr;
+ return reinterpret_cast<quint32_le*>((reinterpret_cast<char *>(this))
+ + offsetToTranslationTable
+ + translationTableSize * sizeof(CompiledData::TranslationData)); }
+
const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); }
const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); }
const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); }
const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); }
const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); }
+
+ bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const;
};
static_assert(sizeof(Unit) == 248, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
@@ -1355,8 +1413,8 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto prop = obj->propertiesBegin();
auto const propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
- if (!prop->isBuiltinType()) {
- TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex(), prop->location);
+ if (!prop->isCommonType()) {
+ TypeReference &r = this->add(prop->commonTypeOrTypeNameIndex(), prop->location);
r.errorWhenNotFound = true;
}
}
@@ -1385,118 +1443,92 @@ struct TypeReferenceMap : QHash<int, TypeReference>
using DependentTypesHasher = std::function<QByteArray()>;
-// This is how this hooks into the existing structures:
-
-struct CompilationUnitBase
-{
- Q_DISABLE_COPY(CompilationUnitBase)
-
- CompilationUnitBase() = default;
- ~CompilationUnitBase() = default;
-
- CompilationUnitBase(CompilationUnitBase &&other) noexcept { *this = std::move(other); }
-
- CompilationUnitBase &operator=(CompilationUnitBase &&other) noexcept
- {
- if (this != &other) {
- runtimeStrings = other.runtimeStrings;
- other.runtimeStrings = nullptr;
- constants = other.constants;
- other.constants = nullptr;
- runtimeRegularExpressions = other.runtimeRegularExpressions;
- other.runtimeRegularExpressions = nullptr;
- runtimeClasses = other.runtimeClasses;
- other.runtimeClasses = nullptr;
- imports = other.imports;
- other.imports = nullptr;
- }
- return *this;
- }
+struct InlineComponentData {
+
+ InlineComponentData() = default;
+ InlineComponentData(
+ const QQmlType &qmlType, int objectIndex, int nameIndex, int totalObjectCount,
+ int totalBindingCount, int totalParserStatusCount)
+ : qmlType(qmlType)
+ , objectIndex(objectIndex)
+ , nameIndex(nameIndex)
+ , totalObjectCount(totalObjectCount)
+ , totalBindingCount(totalBindingCount)
+ , totalParserStatusCount(totalParserStatusCount)
+ {}
- // pointers either to data->constants() or little-endian memory copy.
- Heap::String **runtimeStrings = nullptr; // Array
- const StaticValue* constants = nullptr;
- QV4::StaticValue *runtimeRegularExpressions = nullptr;
- Heap::InternalClass **runtimeClasses = nullptr;
- const StaticValue** imports = nullptr;
+ QQmlType qmlType;
+ int objectIndex = -1;
+ int nameIndex = -1;
+ int totalObjectCount = 0;
+ int totalBindingCount = 0;
+ int totalParserStatusCount = 0;
};
-Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0);
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const StaticValue *));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const StaticValue *));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const StaticValue *));
-
-struct CompilationUnit : public CompilationUnitBase
+struct CompilationUnit final : public QQmlRefCounted<CompilationUnit>
{
- Q_DISABLE_COPY(CompilationUnit)
+ Q_DISABLE_COPY_MOVE(CompilationUnit)
const Unit *data = nullptr;
const QmlUnit *qmlData = nullptr;
QStringList dynamicStrings;
- const QQmlPrivate::TypedFunction *aotCompiledFunctions = nullptr;
+ const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions = nullptr;
+
+ // pointers either to data->constants() or little-endian memory copy.
+ const StaticValue *constants = nullptr;
+
+ std::unique_ptr<CompilationUnitMapper> backingFile;
+
+ int m_totalBindingsCount = 0; // Number of bindings used in this type
+ int m_totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
+ int m_totalObjectCount = 0; // Number of objects explicitly instantiated
+
+ std::unique_ptr<QString> icRootName;
+ QHash<QString, InlineComponentData> inlineComponentData;
+
+ // index is object index. This allows fast access to the
+ // property data when initializing bindings, avoiding expensive
+ // lookups by string (property name).
+ QVector<BindingPropertyData> bindingPropertyDataPerObject;
+
+ ResolvedTypeReferenceMap resolvedTypes;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+
+ QQmlPropertyCacheVector propertyCaches;
+
+ QQmlType qmlType;
+
+ QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
+
public:
- using CompiledObject = CompiledData::Object;
+ // --- interface for QQmlPropertyCacheCreator
+ using CompiledObject = const CompiledData::Object;
+ using CompiledFunction = const CompiledData::Function;
+ using CompiledBinding = const CompiledData::Binding;
- CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(),
- const QString &finalUrlString = QString())
+ // Empty dummy. We don't need to do this when loading from cache.
+ class IdToObjectMap
{
- setUnitData(unitData, nullptr, fileName, finalUrlString);
- }
+ public:
+ void insert(int, int) {}
+ void clear() {}
- explicit CompilationUnit(const Unit *unitData, const QQmlPrivate::TypedFunction *aotCompiledFunctions,
+ // We have already checked uniqueness of IDs when creating the CU
+ bool contains(int) { return false; }
+ };
+
+ explicit CompilationUnit(const Unit *unitData, const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions,
const QString &fileName = QString(), const QString &finalUrlString = QString())
: CompilationUnit(unitData, fileName, finalUrlString)
{
this->aotCompiledFunctions = aotCompiledFunctions;
}
- ~CompilationUnit()
- {
- if (data) {
- if (data->qmlUnit() != qmlData)
- free(const_cast<QmlUnit *>(qmlData));
- qmlData = nullptr;
+ Q_QML_EXPORT CompilationUnit(
+ const Unit *unitData = nullptr, const QString &fileName = QString(),
+ const QString &finalUrlString = QString());
- if (!(data->flags & QV4::CompiledData::Unit::StaticData))
- free(const_cast<Unit *>(data));
- }
- data = nullptr;
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- delete [] constants;
- constants = nullptr;
-#endif
-
- delete [] imports;
- imports = nullptr;
- }
-
- CompilationUnit(CompilationUnit &&other) noexcept
- {
- *this = std::move(other);
- }
-
- CompilationUnit &operator=(CompilationUnit &&other) noexcept
- {
- if (this != &other) {
- data = other.data;
- other.data = nullptr;
- qmlData = other.qmlData;
- other.qmlData = nullptr;
- dynamicStrings = std::move(other.dynamicStrings);
- aotCompiledFunctions = other.aotCompiledFunctions;
- other.dynamicStrings.clear();
- m_fileName = std::move(other.m_fileName);
- other.m_fileName.clear();
- m_finalUrlString = std::move(other.m_finalUrlString);
- other.m_finalUrlString.clear();
- m_module = other.m_module;
- other.m_module = nullptr;
- CompilationUnitBase::operator=(std::move(other));
- }
- return *this;
- }
+ Q_QML_EXPORT ~CompilationUnit();
const Unit *unitData() const { return data; }
@@ -1530,19 +1562,19 @@ public:
m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex);
}
- QString stringAt(int index) const
+ QString stringAt(uint index) const
{
- if (uint(index) >= data->stringTableSize)
- return dynamicStrings.at(index - data->stringTableSize);
- return data->stringAtInternal(index);
+ if (index < data->stringTableSize)
+ return data->stringAtInternal(index);
+
+ const qsizetype dynamicIndex = index - data->stringTableSize;
+ Q_ASSERT(dynamicIndex < dynamicStrings.size());
+ return dynamicStrings.at(dynamicIndex);
}
QString fileName() const { return m_fileName; }
QString finalUrlString() const { return m_finalUrlString; }
- Heap::Module *module() const { return m_module; }
- void setModule(Heap::Module *module) { m_module = module; }
-
QString bindingValueAsString(const CompiledData::Binding *binding) const
{
using namespace CompiledData;
@@ -1581,11 +1613,151 @@ public:
return constants[binding->value.constantValueIndex].doubleValue();
}
+ Q_QML_EXPORT static QString localCacheFilePath(const QUrl &url);
+ Q_QML_EXPORT bool loadFromDisk(
+ const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
+ Q_QML_EXPORT bool saveToDisk(const QUrl &unitUrl, QString *errorString);
+
+ int importCount() const { return qmlData->nImports; }
+ const CompiledData::Import *importAt(int index) const { return qmlData->importAt(index); }
+
+ Q_QML_EXPORT QStringList moduleRequests() const;
+
+ // url() and fileName() shall be used to load the actual QML/JS code or to show errors or
+ // warnings about that code. They include any potential URL interceptions and thus represent the
+ // "physical" location of the code.
+ //
+ // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code
+ // They are _not_ intercepted and thus represent the "logical" name for the code.
+
+ QUrl url() const
+ {
+ if (!m_url.isValid())
+ m_url = QUrl(fileName());
+ return m_url;
+ }
+
+ QUrl finalUrl() const
+ {
+ if (!m_finalUrl.isValid())
+ m_finalUrl = QUrl(finalUrlString());
+ return m_finalUrl;
+ }
+
+ ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); }
+ ResolvedTypeReference *resolvedType(QMetaType type) const;
+
+ QQmlPropertyCache::ConstPtr rootPropertyCache() const
+ {
+ return propertyCaches.at(/*root object*/0);
+ }
+
+ int objectCount() const { return qmlData->nObjects; }
+ const CompiledObject *objectAt(int index) const { return qmlData->objectAt(index); }
+
+ int totalBindingsCount() const;
+ int totalParserStatusCount() const;
+ int totalObjectCount() const;
+
+ int inlineComponentId(const QString &inlineComponentName) const
+ {
+ for (uint i = 0; i < qmlData->nObjects; ++i) {
+ auto *object = qmlData->objectAt(i);
+ for (auto it = object->inlineComponentsBegin(), end = object->inlineComponentsEnd();
+ it != end; ++it) {
+ if (stringAt(it->nameIndex) == inlineComponentName)
+ return it->objectIndex;
+ }
+ }
+ return -1;
+ }
+
+ void finalizeCompositeType(const QQmlType &type);
+
+ bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const;
+
+ enum class ListPropertyAssignBehavior { Append, Replace, ReplaceIfNotDefault };
+ ListPropertyAssignBehavior listPropertyAssignBehavior() const
+ {
+ if (unitData()->flags & CompiledData::Unit::ListPropertyAssignReplace)
+ return ListPropertyAssignBehavior::Replace;
+ if (unitData()->flags & CompiledData::Unit::ListPropertyAssignReplaceIfNotDefault)
+ return ListPropertyAssignBehavior::ReplaceIfNotDefault;
+ return ListPropertyAssignBehavior::Append;
+ }
+
+ bool ignoresFunctionSignature() const
+ {
+ return unitData()->flags & CompiledData::Unit::FunctionSignaturesIgnored;
+ }
+
+ bool nativeMethodsAcceptThisObjects() const
+ {
+ return unitData()->flags & CompiledData::Unit::NativeMethodsAcceptThisObject;
+ }
+
+ bool valueTypesAreCopied() const
+ {
+ return unitData()->flags & CompiledData::Unit::ValueTypesCopied;
+ }
+
+ bool valueTypesAreAddressable() const
+ {
+ return unitData()->flags & CompiledData::Unit::ValueTypesAddressable;
+ }
+
+ bool componentsAreBound() const
+ {
+ return unitData()->flags & CompiledData::Unit::ComponentsBound;
+ }
+
+ bool isESModule() const
+ {
+ return unitData()->flags & CompiledData::Unit::IsESModule;
+ }
+
+ bool isSharedLibrary() const
+ {
+ return unitData()->flags & CompiledData::Unit::IsSharedLibrary;
+ }
+
+ struct FunctionIterator
+ {
+ FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index)
+ : unit(unit), object(object), index(index) {}
+ const CompiledData::Unit *unit;
+ const CompiledObject *object;
+ int index;
+
+ const CompiledFunction *operator->() const
+ {
+ return unit->functionAt(object->functionOffsetTable()[index]);
+ }
+
+ void operator++() { ++index; }
+ bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; }
+ bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; }
+ };
+
+ FunctionIterator objectFunctionsBegin(const CompiledObject *object) const
+ {
+ return FunctionIterator(unitData(), object, 0);
+ }
+
+ FunctionIterator objectFunctionsEnd(const CompiledObject *object) const
+ {
+ return FunctionIterator(unitData(), object, object->nFunctions);
+ }
+
+ QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const;
+ QMetaType metaType() const { return qmlType.typeId(); }
+
private:
QString m_fileName; // initialized from data->sourceFileIndex
QString m_finalUrlString; // initialized from data->finalUrlIndex
- Heap::Module *m_module = nullptr;
+ mutable QQmlNullableValue<QUrl> m_url;
+ mutable QQmlNullableValue<QUrl> m_finalUrl;
};
class SaveableUnitPointer
diff --git a/src/qml/common/qv4staticvalue_p.h b/src/qml/common/qv4staticvalue_p.h
index e9c3554104..e887cdc674 100644
--- a/src/qml/common/qv4staticvalue_p.h
+++ b/src/qml/common/qv4staticvalue_p.h
@@ -393,7 +393,7 @@ struct StaticValue
}
QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
- int i = int(d);
+ int i = QJSNumberCoercion::toInteger(d);
return (i == d && !(d == 0 && std::signbit(d)));
}
@@ -589,10 +589,17 @@ struct StaticValue
// Has to be aligned to 32 bytes
Q_ASSERT(!(tmp & Lower5Mask));
+ // MinGW produces a bogus warning about array bounds.
+ // There is no array access here.
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_GCC("-Warray-bounds")
+
// Encode the pointer.
_val = storePointerBits<Top1Shift, Top1Mask>(
storePointerBits<Upper3Shift, Upper3Mask>(
storePointerBits<Lower5Shift, Lower5Mask>(tmp)));
+
+ QT_WARNING_POP
}
#elif QT_POINTER_SIZE == 4
QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const
diff --git a/src/qml/common/qv4stringtoarrayindex_p.h b/src/qml/common/qv4stringtoarrayindex_p.h
index fc71ca7072..7bdabd4f3f 100644
--- a/src/qml/common/qv4stringtoarrayindex_p.h
+++ b/src/qml/common/qv4stringtoarrayindex_p.h
@@ -43,7 +43,7 @@ uint stringToArrayIndex(const T *ch, const T *end)
uint x = charToUInt(ch) - '0';
if (x > 9)
return std::numeric_limits<uint>::max();
- if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x
+ if (qMulOverflow(i, uint(10), &i) || qAddOverflow(i, x, &i)) // i = i * 10 + x
return std::numeric_limits<uint>::max();
++ch;
}
diff --git a/src/qml/compat/removed_api.cpp b/src/qml/compat/removed_api.cpp
index 00c899d73c..fe16b7f462 100644
--- a/src/qml/compat/removed_api.cpp
+++ b/src/qml/compat/removed_api.cpp
@@ -24,3 +24,19 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
#endif
+#if QT_QML_REMOVED_SINCE(6, 6)
+#include <QtQml/qqmlprivate.h>
+#include <QtQml/private/qv4executablecompilationunit_p.h>
+#include <QtQml/private/qv4lookup_p.h>
+
+bool QQmlPrivate::AOTCompiledContext::getEnumLookup(uint index, int *target) const
+{
+ using namespace QQmlPrivate;
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ auto mt = QMetaType(l->qmlEnumValueLookup.metaType);
+ QVariant buffer(mt);
+ getEnumLookup(index, buffer.data());
+ *target = buffer.toInt();
+ return true;
+}
+#endif
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 3fd66d302c..a81f8fb1d8 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -59,7 +59,7 @@ bool Parameter::initType(
QV4::CompiledData::ParameterType::Flag listFlag)
{
auto builtinType = stringToBuiltinType(typeName);
- if (builtinType == QV4::CompiledData::BuiltinType::InvalidBuiltin) {
+ if (builtinType == QV4::CompiledData::CommonType::Invalid) {
if (typeName.isEmpty()) {
paramType->set(listFlag, 0);
return false;
@@ -68,31 +68,33 @@ bool Parameter::initType(
paramType->set(listFlag, typeNameIndex);
} else {
Q_ASSERT(quint32(builtinType) < (1u << 31));
- paramType->set(listFlag | QV4::CompiledData::ParameterType::Builtin,
+ paramType->set(listFlag | QV4::CompiledData::ParameterType::Common,
static_cast<quint32>(builtinType));
}
return true;
}
-QV4::CompiledData::BuiltinType Parameter::stringToBuiltinType(const QString &typeName)
+QV4::CompiledData::CommonType Parameter::stringToBuiltinType(const QString &typeName)
{
static const struct TypeNameToType {
const char *name;
size_t nameLength;
- QV4::CompiledData::BuiltinType type;
+ QV4::CompiledData::CommonType type;
} propTypeNameToTypes[] = {
- { "int", strlen("int"), QV4::CompiledData::BuiltinType::Int },
- { "bool", strlen("bool"), QV4::CompiledData::BuiltinType::Bool },
- { "double", strlen("double"), QV4::CompiledData::BuiltinType::Real },
- { "real", strlen("real"), QV4::CompiledData::BuiltinType::Real },
- { "string", strlen("string"), QV4::CompiledData::BuiltinType::String },
- { "url", strlen("url"), QV4::CompiledData::BuiltinType::Url },
- { "date", strlen("date"), QV4::CompiledData::BuiltinType::DateTime },
- { "rect", strlen("rect"), QV4::CompiledData::BuiltinType::Rect },
- { "point", strlen("point"), QV4::CompiledData::BuiltinType::Point },
- { "size", strlen("size"), QV4::CompiledData::BuiltinType::Size },
- { "variant", strlen("variant"), QV4::CompiledData::BuiltinType::Var },
- { "var", strlen("var"), QV4::CompiledData::BuiltinType::Var }
+ { "void", strlen("void"), QV4::CompiledData::CommonType::Void },
+ { "int", strlen("int"), QV4::CompiledData::CommonType::Int },
+ { "bool", strlen("bool"), QV4::CompiledData::CommonType::Bool },
+ { "double", strlen("double"), QV4::CompiledData::CommonType::Real },
+ { "real", strlen("real"), QV4::CompiledData::CommonType::Real },
+ { "string", strlen("string"), QV4::CompiledData::CommonType::String },
+ { "url", strlen("url"), QV4::CompiledData::CommonType::Url },
+ { "date", strlen("date"), QV4::CompiledData::CommonType::DateTime },
+ { "regexp", strlen("regexp"), QV4::CompiledData::CommonType::RegExp },
+ { "rect", strlen("rect"), QV4::CompiledData::CommonType::Rect },
+ { "point", strlen("point"), QV4::CompiledData::CommonType::Point },
+ { "size", strlen("size"), QV4::CompiledData::CommonType::Size },
+ { "variant", strlen("variant"), QV4::CompiledData::CommonType::Var },
+ { "var", strlen("var"), QV4::CompiledData::CommonType::Var }
};
static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
sizeof(propTypeNameToTypes[0]);
@@ -103,7 +105,7 @@ QV4::CompiledData::BuiltinType Parameter::stringToBuiltinType(const QString &typ
return t->type;
}
}
- return QV4::CompiledData::BuiltinType::InvalidBuiltin;
+ return QV4::CompiledData::CommonType::Invalid;
}
void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex,
@@ -440,38 +442,6 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen
return errors.isEmpty();
}
-bool IRBuilder::isSignalPropertyName(const QString &name)
-{
- if (name.size() < 3) return false;
- if (!name.startsWith(QLatin1String("on"))) return false;
- int ns = name.size();
- for (int i = 2; i < ns; ++i) {
- const QChar curr = name.at(i);
- if (curr.unicode() == '_') continue;
- if (curr.isUpper()) return true;
- return false;
- }
- return false; // consists solely of underscores - invalid.
-}
-
-QString IRBuilder::signalNameFromSignalPropertyName(const QString &signalPropertyName)
-{
- Q_ASSERT(signalPropertyName.startsWith(QLatin1String("on")));
- QString signalNameCandidate = signalPropertyName;
- signalNameCandidate.remove(0, 2);
-
- // Note that the property name could start with any alpha or '_' or '$' character,
- // so we need to do the lower-casing of the first alpha character.
- for (int firstAlphaIndex = 0; firstAlphaIndex < signalNameCandidate.size(); ++firstAlphaIndex) {
- if (signalNameCandidate.at(firstAlphaIndex).isUpper()) {
- signalNameCandidate[firstAlphaIndex] = signalNameCandidate.at(firstAlphaIndex).toLower();
- return signalNameCandidate;
- }
- }
-
- Q_UNREACHABLE_RETURN(QString());
-}
-
bool IRBuilder::visit(QQmlJS::AST::UiArrayMemberList *ast)
{
return QQmlJS::AST::Visitor::visit(ast);
@@ -767,10 +737,10 @@ struct PragmaParser
pragma->type = type();
- if (!assign(pragma, node->value)) {
+ if (QQmlJS::AST::UiPragmaValueList *bad = assign(pragma, node->values)) {
builder->recordError(
node->pragmaToken, QCoreApplication::translate(
- "QQmlParser", "Unknown %1 '%2' in pragma").arg(name(), node->value));
+ "QQmlParser", "Unknown %1 '%2' in pragma").arg(name(), bad->value));
return false;
}
@@ -795,63 +765,107 @@ private:
Q_UNREACHABLE_RETURN(Pragma::PragmaType(-1));
}
- static bool assign(Pragma *pragma, QStringView value)
+ template<typename F>
+ static QQmlJS::AST::UiPragmaValueList *iterateValues(
+ QQmlJS::AST::UiPragmaValueList *input, F &&process)
+ {
+ for (QQmlJS::AST::UiPragmaValueList *i = input; i; i = i->next) {
+ if (!process(i->value))
+ return i;
+ }
+ return nullptr;
+ }
+
+ static QQmlJS::AST::UiPragmaValueList *assign(
+ Pragma *pragma, QQmlJS::AST::UiPragmaValueList *values)
{
// We could use QMetaEnum here to make the code more compact,
// but it's probably more expensive.
if constexpr (std::is_same_v<Argument, Pragma::ComponentBehaviorValue>) {
- if (value == "Unbound"_L1) {
- pragma->componentBehavior = Pragma::Unbound;
- return true;
- }
- if (value == "Bound"_L1) {
- pragma->componentBehavior = Pragma::Bound;
- return true;
- }
+ return iterateValues(values, [pragma](QStringView value) {
+ if (value == "Unbound"_L1) {
+ pragma->componentBehavior = Pragma::Unbound;
+ return true;
+ }
+ if (value == "Bound"_L1) {
+ pragma->componentBehavior = Pragma::Bound;
+ return true;
+ }
+ return false;
+ });
} else if constexpr (std::is_same_v<Argument, Pragma::ListPropertyAssignBehaviorValue>) {
- if (value == "Append"_L1) {
- pragma->listPropertyAssignBehavior = Pragma::Append;
- return true;
- }
- if (value == "Replace"_L1) {
- pragma->listPropertyAssignBehavior = Pragma::Replace;
- return true;
- }
- if (value == "ReplaceIfNotDefault"_L1) {
- pragma->listPropertyAssignBehavior = Pragma::ReplaceIfNotDefault;
- return true;
- }
+ return iterateValues(values, [pragma](QStringView value) {
+ if (value == "Append"_L1) {
+ pragma->listPropertyAssignBehavior = Pragma::Append;
+ return true;
+ }
+ if (value == "Replace"_L1) {
+ pragma->listPropertyAssignBehavior = Pragma::Replace;
+ return true;
+ }
+ if (value == "ReplaceIfNotDefault"_L1) {
+ pragma->listPropertyAssignBehavior = Pragma::ReplaceIfNotDefault;
+ return true;
+ }
+ return false;
+ });
} else if constexpr (std::is_same_v<Argument, Pragma::FunctionSignatureBehaviorValue>) {
- if (value == "Ignored"_L1) {
- pragma->functionSignatureBehavior = Pragma::Ignored;
- return true;
- }
- if (value == "Enforced"_L1) {
- pragma->functionSignatureBehavior = Pragma::Enforced;
- return true;
- }
+ return iterateValues(values, [pragma](QStringView value) {
+ if (value == "Ignored"_L1) {
+ pragma->functionSignatureBehavior = Pragma::Ignored;
+ return true;
+ }
+ if (value == "Enforced"_L1) {
+ pragma->functionSignatureBehavior = Pragma::Enforced;
+ return true;
+ }
+ return false;
+ });
} else if constexpr (std::is_same_v<Argument, Pragma::NativeMethodBehaviorValue>) {
- if (value == "AcceptThisObject"_L1) {
- pragma->nativeMethodBehavior = Pragma::AcceptThisObject;
- return true;
- }
- if (value == "RejectThisObject"_L1) {
- pragma->nativeMethodBehavior = Pragma::RejectThisObject;
- return true;
- }
+ return iterateValues(values, [pragma](QStringView value) {
+ if (value == "AcceptThisObject"_L1) {
+ pragma->nativeMethodBehavior = Pragma::AcceptThisObject;
+ return true;
+ }
+ if (value == "RejectThisObject"_L1) {
+ pragma->nativeMethodBehavior = Pragma::RejectThisObject;
+ return true;
+ }
+ return false;
+ });
} else if constexpr (std::is_same_v<Argument, Pragma::ValueTypeBehaviorValue>) {
- if (value == "Reference"_L1) {
- pragma->valueTypeBehavior = Pragma::Reference;
- return true;
- }
- if (value == "Copy"_L1) {
- pragma->valueTypeBehavior = Pragma::Copy;
- return true;
- }
+ pragma->valueTypeBehavior = Pragma::ValueTypeBehaviorValues().toInt();
+ return iterateValues(values, [pragma](QStringView value) {
+ const auto setFlag = [pragma](Pragma::ValueTypeBehaviorValue flag, bool value) {
+ pragma->valueTypeBehavior
+ = Pragma::ValueTypeBehaviorValues(pragma->valueTypeBehavior)
+ .setFlag(flag, value).toInt();
+ };
+
+ if (value == "Reference"_L1) {
+ setFlag(Pragma::Copy, false);
+ return true;
+ }
+ if (value == "Copy"_L1) {
+ setFlag(Pragma::Copy, true);
+ return true;
+ }
+
+ if (value == "Inaddressable"_L1) {
+ setFlag(Pragma::Addressable, false);
+ return true;
+ }
+ if (value == "Addressable"_L1) {
+ setFlag(Pragma::Addressable, true);
+ return true;
+ }
+
+ return false;
+ });
}
- return false;
+ Q_UNREACHABLE_RETURN(nullptr);
}
static bool isUnique(IRBuilder *builder)
@@ -907,6 +921,10 @@ bool IRBuilder::visit(QQmlJS::AST::UiPragma *node)
} else if (node->name == "ValueTypeBehavior"_L1) {
if (!PragmaParser<Pragma::ValueTypeBehaviorValue>::run(this, node, pragma))
return false;
+ } else if (node->name == "Translator"_L1) {
+ pragma->type = Pragma::Translator;
+ pragma->translationContextIndex = registerString(node->values->value.toString());
+
} else {
recordError(node->pragmaToken, QCoreApplication::translate(
"QQmlParser", "Unknown pragma '%1'").arg(node->name));
@@ -1047,12 +1065,12 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
property->setIsReadOnly(node->isReadonly());
property->setIsRequired(node->isRequired());
- const QV4::CompiledData::BuiltinType builtinPropertyType
+ const QV4::CompiledData::CommonType builtinPropertyType
= Parameter::stringToBuiltinType(memberType);
- if (builtinPropertyType != QV4::CompiledData::BuiltinType::InvalidBuiltin)
- property->setBuiltinType(builtinPropertyType);
+ if (builtinPropertyType != QV4::CompiledData::CommonType::Invalid)
+ property->setCommonType(builtinPropertyType);
else
- property->setCustomType(registerString(memberType));
+ property->setTypeNameIndex(registerString(memberType));
QStringView typeModifier = node->typeModifier;
if (typeModifier == QLatin1String("list")) {
@@ -1609,7 +1627,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement)
bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement)
{
- if (property->isBuiltinType() || property->isList())
+ if (property->isCommonType() || property->isList())
return false;
QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
if (!exprStmt)
@@ -1627,10 +1645,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
Unit *jsUnit = nullptr;
+ if (!output.javaScriptCompilationUnit)
+ output.javaScriptCompilationUnit.adopt(new QV4::CompiledData::CompilationUnit);
+
// We may already have unit data if we're loading an ahead-of-time generated cache file.
- if (output.javaScriptCompilationUnit.data) {
- jsUnit = const_cast<Unit *>(output.javaScriptCompilationUnit.data);
- output.javaScriptCompilationUnit.dynamicStrings = output.jsGenerator.stringTable.allStrings();
+ if (output.javaScriptCompilationUnit->unitData()) {
+ jsUnit = const_cast<Unit *>(output.javaScriptCompilationUnit->unitData());
+ output.javaScriptCompilationUnit->dynamicStrings
+ = output.jsGenerator.stringTable.allStrings();
} else {
Unit *createdUnit;
jsUnit = createdUnit = output.jsGenerator.generateUnit();
@@ -1671,10 +1693,9 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
case Pragma::FunctionSignatureBehavior:
switch (p->functionSignatureBehavior) {
case Pragma::Enforced:
- createdUnit->flags |= Unit::FunctionSignaturesEnforced;
break;
case Pragma::Ignored:
- //this is the default;
+ createdUnit->flags |= Unit::FunctionSignaturesIgnored;
break;
}
break;
@@ -1689,14 +1710,19 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
}
break;
case Pragma::ValueTypeBehavior:
- switch (p->valueTypeBehavior) {
- case Pragma::Copy:
+ if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior)
+ .testFlag(Pragma::Copy)) {
createdUnit->flags |= Unit::ValueTypesCopied;
- break;
- case Pragma::Reference:
- //this is the default;
- break;
}
+ if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior)
+ .testFlag(Pragma::Addressable)) {
+ createdUnit->flags |= Unit::ValueTypesAddressable;
+ }
+ break;
+ case Pragma::Translator:
+ if (createdUnit->translationTableSize)
+ if (quint32_le *index = createdUnit->translationContextIndex())
+ *index = p->translationContextIndex;
break;
}
}
@@ -1899,7 +1925,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
}
}
- if (!output.javaScriptCompilationUnit.data) {
+ if (!output.javaScriptCompilationUnit->unitData()) {
// Combine the qml data into the general unit data.
jsUnit = static_cast<QV4::CompiledData::Unit *>(realloc(jsUnit, jsUnit->unitSize + totalSize));
jsUnit->offsetToQmlUnit = jsUnit->unitSize;
@@ -1932,8 +1958,8 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
qDebug() << " " << totalStringSize << "bytes total strings";
}
- output.javaScriptCompilationUnit.setUnitData(jsUnit, qmlUnit, output.jsModule.fileName,
- output.jsModule.finalUrl);
+ output.javaScriptCompilationUnit->setUnitData(
+ jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl);
}
char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const
@@ -1951,9 +1977,9 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
}
JSCodeGen::JSCodeGen(Document *document, const QSet<QString> &globalNames,
- QV4::Compiler::CodegenWarningInterface *interface,
+ QV4::Compiler::CodegenWarningInterface *iface,
bool storeSourceLocations)
- : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/ false, interface,
+ : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/ false, iface,
storeSourceLocations),
document(document)
{
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index d4599c3757..546d1fac58 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -167,6 +167,9 @@ struct PoolList
bool operator!=(const Iterator &rhs) const {
return ptr != rhs.ptr;
}
+
+ operator T *() { return ptr; }
+ operator const T *() const { return ptr; }
};
Iterator begin() { return Iterator(first); }
@@ -224,7 +227,7 @@ struct Parameter : public QV4::CompiledData::Parameter
return initType(type, annotationString, idGenerator(annotationString), Flag::NoFlag);
}
- static QV4::CompiledData::BuiltinType stringToBuiltinType(const QString &typeName);
+ static QV4::CompiledData::CommonType stringToBuiltinType(const QString &typeName);
private:
static bool initType(
@@ -293,7 +296,7 @@ struct Function
Function *next;
};
-struct Q_QML_COMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression
+struct Q_QML_COMPILER_EXPORT CompiledFunctionOrExpression
{
CompiledFunctionOrExpression()
{}
@@ -304,7 +307,7 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression
CompiledFunctionOrExpression *next = nullptr;
};
-struct Q_QML_COMPILER_PRIVATE_EXPORT Object
+struct Q_QML_COMPILER_EXPORT Object
{
Q_DECLARE_TR_FUNCTIONS(Object)
public:
@@ -397,7 +400,7 @@ private:
PoolList<RequiredPropertyExtraData> *requiredPropertyExtraDatas;
};
-struct Q_QML_COMPILER_PRIVATE_EXPORT Pragma
+struct Q_QML_COMPILER_EXPORT Pragma
{
enum PragmaType
{
@@ -408,6 +411,7 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT Pragma
FunctionSignatureBehavior,
NativeMethodBehavior,
ValueTypeBehavior,
+ Translator,
};
enum ListPropertyAssignBehaviorValue
@@ -437,9 +441,10 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT Pragma
enum ValueTypeBehaviorValue
{
- Reference,
- Copy
+ Copy = 0x1,
+ Addressable = 0x2,
};
+ Q_DECLARE_FLAGS(ValueTypeBehaviorValues, ValueTypeBehaviorValue);
PragmaType type;
@@ -448,13 +453,14 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT Pragma
ComponentBehaviorValue componentBehavior;
FunctionSignatureBehaviorValue functionSignatureBehavior;
NativeMethodBehaviorValue nativeMethodBehavior;
- ValueTypeBehaviorValue valueTypeBehavior;
+ ValueTypeBehaviorValues::Int valueTypeBehavior;
+ uint translationContextIndex;
};
QV4::CompiledData::Location location;
};
-struct Q_QML_COMPILER_PRIVATE_EXPORT Document
+struct Q_QML_COMPILER_EXPORT Document
{
Document(bool debugMode);
QString code;
@@ -466,7 +472,13 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT Document
QVector<Object*> objects;
QV4::Compiler::JSUnitGenerator jsGenerator;
- QV4::CompiledData::CompilationUnit javaScriptCompilationUnit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> javaScriptCompilationUnit;
+
+ bool isSingleton() const {
+ return std::any_of(pragmas.constBegin(), pragmas.constEnd(), [](const Pragma *pragma) {
+ return pragma->type == Pragma::Singleton;
+ });
+ }
int registerString(const QString &str) { return jsGenerator.registerString(str); }
QString stringAt(int index) const { return jsGenerator.stringForIndex(index); }
@@ -475,7 +487,7 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT Document
Object* objectAt(int i) const {return objects.at(i);}
};
-class Q_QML_COMPILER_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives
+class Q_QML_COMPILER_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives
{
QmlIR::Document *document;
QQmlJS::Engine *engine;
@@ -489,16 +501,13 @@ public:
void importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column) override;
};
-struct Q_QML_COMPILER_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor
+struct Q_QML_COMPILER_EXPORT IRBuilder : public QQmlJS::AST::Visitor
{
Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator)
public:
IRBuilder(const QSet<QString> &illegalNames);
bool generateFromQml(const QString &code, const QString &url, Document *output);
- static bool isSignalPropertyName(const QString &name);
- static QString signalNameFromSignalPropertyName(const QString &signalPropertyName);
-
using QQmlJS::AST::Visitor::visit;
using QQmlJS::AST::Visitor::endVisit;
@@ -611,7 +620,7 @@ public:
bool insideInlineComponent = false;
};
-struct Q_QML_COMPILER_PRIVATE_EXPORT QmlUnitGenerator
+struct Q_QML_COMPILER_EXPORT QmlUnitGenerator
{
void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher());
@@ -620,7 +629,7 @@ private:
char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const;
};
-struct Q_QML_COMPILER_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
+struct Q_QML_COMPILER_EXPORT JSCodeGen : public QV4::Compiler::Codegen
{
JSCodeGen(Document *document, const QSet<QString> &globalNames,
QV4::Compiler::CodegenWarningInterface *iface =
@@ -665,8 +674,12 @@ void tryGeneratingTranslationBindingBase(QStringView base, QQmlJS::AST::Argument
if (base == QLatin1String("qsTr")) {
QV4::CompiledData::TranslationData translationData;
translationData.number = -1;
- translationData.commentIndex = 0; // empty string
- translationData.contextIndex = 0;
+
+ // empty string
+ translationData.commentIndex = 0;
+
+ // No context (not empty string)
+ translationData.contextIndex = QV4::CompiledData::TranslationData::NoContextIndex;
if (!args || !args->expression)
return; // no arguments, stop
@@ -706,8 +719,12 @@ void tryGeneratingTranslationBindingBase(QStringView base, QQmlJS::AST::Argument
} else if (base == QLatin1String("qsTrId")) {
QV4::CompiledData::TranslationData translationData;
translationData.number = -1;
- translationData.commentIndex = 0; // empty string, but unused
- translationData.contextIndex = 0;
+
+ // empty string, but unused
+ translationData.commentIndex = 0;
+
+ // No context (not empty string)
+ translationData.contextIndex = QV4::CompiledData::TranslationData::NoContextIndex;
if (!args || !args->expression)
return; // no arguments, stop
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index 35e539fb5e..fa14754f85 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -58,7 +58,7 @@ public:
link();
}
- void link() {
+ void link() const {
Q_ASSERT(index >= 0);
Q_ASSERT(generator->labels[index] == -1);
generator->labels[index] = generator->instructions.size();
diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h
index dd92046f2d..0a74fc52ed 100644
--- a/src/qml/compiler/qv4bytecodehandler_p.h
+++ b/src/qml/compiler/qv4bytecodehandler_p.h
@@ -56,7 +56,7 @@ namespace Moth {
#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \
INSTR_##instr(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
-class Q_QML_COMPILER_PRIVATE_EXPORT ByteCodeHandler
+class Q_QML_COMPILER_EXPORT ByteCodeHandler
{
Q_DISABLE_COPY_MOVE(ByteCodeHandler)
public:
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 157f831bb8..25831eab73 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -21,9 +21,6 @@
#include <private/qqmljsdiagnosticmessage_p.h>
#include <cmath>
-#include <iostream>
-
-static const bool disable_lookups = false;
#ifdef CONST
#undef CONST
@@ -33,7 +30,8 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-Q_LOGGING_CATEGORY(lcQmlCompiler, "qt.qml.compiler");
+Q_LOGGING_CATEGORY(lcQmlUsedBeforeDeclared, "qt.qml.usedbeforedeclared");
+Q_LOGGING_CATEGORY(lcQmlInjectedParameter, "qt.qml.injectedparameter");
using namespace QV4;
using namespace QV4::Compiler;
@@ -44,7 +42,7 @@ void CodegenWarningInterface::reportVarUsedBeforeDeclaration(
const QString &name, const QString &fileName, QQmlJS::SourceLocation declarationLocation,
QQmlJS::SourceLocation accessLocation)
{
- qCWarning(lcQmlCompiler).nospace().noquote()
+ qCWarning(lcQmlUsedBeforeDeclared).nospace().noquote()
<< fileName << ":" << accessLocation.startLine << ":" << accessLocation.startColumn
<< " Variable \"" << name << "\" is used before its declaration at "
<< declarationLocation.startLine << ":" << declarationLocation.startColumn << ".";
@@ -92,7 +90,7 @@ void Codegen::generateThrowException(const QString &type, const QString &text)
}
Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict,
- CodegenWarningInterface *interface, bool storeSourceLocations)
+ CodegenWarningInterface *iface, bool storeSourceLocations)
: _module(nullptr),
_returnAddress(-1),
_context(nullptr),
@@ -101,7 +99,7 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict,
_strictMode(strict),
storeSourceLocations(storeSourceLocations),
_fileNameIsUrl(false),
- _interface(interface)
+ _interface(iface)
{
jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
pushExpr();
@@ -714,9 +712,8 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle
RegisterScope scope(this);
Reference iterator = Reference::fromStackSlot(this);
- Reference iteratorValue = Reference::fromStackSlot(this);
- Reference iteratorDone = Reference::fromStackSlot(this);
- Reference::storeConstOnStack(this, Encode(false), iteratorDone.stackSlot());
+ QVarLengthArray<Reference> iteratorValues;
+ Reference ignored;
array.loadInAccumulator();
Instruction::GetIterator iteratorObjInstr;
@@ -724,45 +721,76 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle
bytecodeGenerator->addInstruction(iteratorObjInstr);
iterator.storeConsumeAccumulator();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
+ Reference needsClose = Reference::storeConstOnStack(this, Encode(false));
+
+ for (PatternElementList *p = bindingList; p; p = p->next) {
+ PatternElement *e = p->element;
+ for (Elision *elision = p->elision; elision; elision = elision->next) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ if (!ignored.isValid())
+ ignored = Reference::fromStackSlot(this);
+ next.value = ignored.stackSlot();
+ bytecodeGenerator->addJumpInstruction(next).link(done);
+ }
+
+ if (!e)
+ continue;
+
+ if (e->type != PatternElement::RestElement) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ iteratorValues.push_back(Reference::fromStackSlot(this));
+ next.value = iteratorValues.back().stackSlot();
+ bytecodeGenerator->addJumpInstruction(next).link(done);
+ }
+ }
+
+ // If we've iterated through all the patterns without exhausing the iterator, it needs
+ // to be closed. But we don't close it here because:
+ // a, closing might throw an exception and we want to assign the values before we handle that
+ // b, there might be a rest element that could still continue iterating
+ Reference::fromConst(this, Encode(true)).storeOnStack(needsClose.stackSlot());
+
+ done.link();
+ bytecodeGenerator->checkException();
+
{
- auto cleanup = [this, iterator, iteratorDone]() {
+ ControlFlowUnwindCleanup flow(this, [&]() {
+ BytecodeGenerator::Label skipClose = bytecodeGenerator->newLabel();
+ needsClose.loadInAccumulator();
+ bytecodeGenerator->jumpFalse().link(skipClose);
iterator.loadInAccumulator();
Instruction::IteratorClose close;
- close.done = iteratorDone.stackSlot();
bytecodeGenerator->addInstruction(close);
- };
-
- ControlFlowUnwindCleanup flow(this, cleanup);
+ skipClose.link();
+ });
+ auto it = iteratorValues.constBegin();
for (PatternElementList *p = bindingList; p; p = p->next) {
PatternElement *e = p->element;
- for (Elision *elision = p->elision; elision; elision = elision->next) {
- iterator.loadInAccumulator();
- Instruction::IteratorNext next;
- next.value = iteratorValue.stackSlot();
- next.done = iteratorDone.stackSlot();
- bytecodeGenerator->addInstruction(next);
- }
if (!e)
continue;
- RegisterScope scope(this);
- iterator.loadInAccumulator();
-
if (e->type == PatternElement::RestElement) {
- Reference::fromConst(this, Encode(true)).storeOnStack(iteratorDone.stackSlot());
+ Q_ASSERT(it == iteratorValues.constEnd());
+
+ // The rest element is guaranteed to exhaust the iterator
+ Reference::fromConst(this, Encode(false)).storeOnStack(needsClose.stackSlot());
+
+ iterator.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::DestructureRestElement());
- initializeAndDestructureBindingElement(e, Reference::fromAccumulator(this), isDefinition);
+ initializeAndDestructureBindingElement(
+ e, Reference::fromAccumulator(this), isDefinition);
} else {
- Instruction::IteratorNext next;
- next.value = iteratorValue.stackSlot();
- next.done = iteratorDone.stackSlot();
- bytecodeGenerator->addInstruction(next);
- initializeAndDestructureBindingElement(e, iteratorValue, isDefinition);
- if (hasError())
- return;
+ Q_ASSERT(it != iteratorValues.constEnd());
+ initializeAndDestructureBindingElement(e, *it++, isDefinition);
}
+
+ if (hasError())
+ return;
}
}
}
@@ -905,6 +933,11 @@ bool Codegen::visit(UiHeaderItemList *)
Q_UNREACHABLE_RETURN(false);
}
+bool Codegen::visit(UiPragmaValueList *)
+{
+ Q_UNREACHABLE_RETURN(false);
+}
+
bool Codegen::visit(UiPragma *)
{
Q_UNREACHABLE_RETURN(false);
@@ -1163,7 +1196,6 @@ bool Codegen::visit(ArrayPattern *ast)
RegisterScope scope(this);
Reference iterator = Reference::fromStackSlot(this);
- Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
Reference lhsValue = Reference::fromStackSlot(this);
// There should be a temporal block, so that variables declared in lhs shadow outside vars.
@@ -1183,24 +1215,23 @@ bool Codegen::visit(ArrayPattern *ast)
BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
{
- auto cleanup = [this, iterator, iteratorDone]() {
+ auto cleanup = [this, iterator, done]() {
iterator.loadInAccumulator();
Instruction::IteratorClose close;
- close.done = iteratorDone.stackSlot();
bytecodeGenerator->addInstruction(close);
+ done.link();
};
- ControlFlowLoop flow(this, &end, &in, cleanup);
+ ControlFlowLoop flow(this, &end, &in, std::move(cleanup));
in.link();
bytecodeGenerator->addLoopStart(in);
iterator.loadInAccumulator();
Instruction::IteratorNext next;
next.value = lhsValue.stackSlot();
- next.done = iteratorDone.stackSlot();
- bytecodeGenerator->addInstruction(next);
- bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end);
+ bytecodeGenerator->addJumpInstruction(next).link(done);
lhsValue.loadInAccumulator();
pushAccumulator();
@@ -1230,35 +1261,26 @@ bool Codegen::visit(ArrayPattern *ast)
bool Codegen::visit(ArrayMemberExpression *ast)
{
- auto label = traverseOptionalChain(ast);
- auto targetLabel = label.has_value() ? label.value() : Moth::BytecodeGenerator::Label();
-
if (hasError())
return false;
- if (ast->isOptional)
- Q_ASSERT(m_optionalChainLabels.contains(ast));
-
+ const bool isTailOfChain = traverseOptionalChain(ast);
TailCallBlocker blockTailCalls(this);
Reference base = expression(ast->base);
auto writeSkip = [&]() {
- auto acc = Reference::fromAccumulator(this).storeOnStack();
- base.loadInAccumulator();
- bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
- auto jumpFalse = bytecodeGenerator->jumpFalse();
- bytecodeGenerator->addInstruction(Instruction::LoadUndefined());
- bytecodeGenerator->jump().link(m_optionalChainLabels.take(ast));
- jumpFalse.link();
- acc.loadInAccumulator();
+ base.loadInAccumulator();
+ bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
+ auto jumpToUndefined = bytecodeGenerator->jumpTrue();
+ m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined));
};
if (hasError())
return false;
if (base.isSuper()) {
Reference index = expression(ast->expression).storeOnStack();
- setExprResult(Reference::fromSuperProperty(index));
+ optionalChainFinalizer(Reference::fromSuperProperty(index), isTailOfChain);
return false;
}
base = base.storeOnStack();
@@ -1268,11 +1290,11 @@ bool Codegen::visit(ArrayMemberExpression *ast)
QString s = str->value.toString();
uint arrayIndex = stringToArrayIndex(s);
if (arrayIndex == UINT_MAX) {
- auto jumpLabel = ast->isOptional ? m_optionalChainLabels.take(ast) : Moth::BytecodeGenerator::Label();
-
- setExprResult(Reference::fromMember(base, str->value.toString(),
- ast->expression->firstSourceLocation(), jumpLabel,
- targetLabel));
+ auto ref = Reference::fromMember(base, s, ast->expression->firstSourceLocation(),
+ ast->isOptional,
+ &m_optionalChainsStates.top().jumpsToPatch);
+ setExprResult(ref);
+ optionalChainFinalizer(ref, isTailOfChain);
return false;
}
@@ -1280,7 +1302,7 @@ bool Codegen::visit(ArrayMemberExpression *ast)
writeSkip();
Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex));
- setExprResult(Reference::fromSubscript(base, index, targetLabel));
+ optionalChainFinalizer(Reference::fromSubscript(base, index), isTailOfChain);
return false;
}
@@ -1293,8 +1315,7 @@ bool Codegen::visit(ArrayMemberExpression *ast)
if (hasError())
return false;
- setExprResult(Reference::fromSubscript(base, index, targetLabel));
-
+ optionalChainFinalizer(Reference::fromSubscript(base, index), isTailOfChain);
return false;
}
@@ -1394,26 +1415,20 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label iffalse = bytecodeGenerator->newLabel();
- Instruction::CmpNeNull cmp;
+ Instruction::CmpEqNull cmp;
left = left.storeOnStack();
left.loadInAccumulator();
bytecodeGenerator->addInstruction(cmp);
bytecodeGenerator->jumpTrue().link(iftrue);
- bytecodeGenerator->jumpFalse().link(iffalse);
blockTailCalls.unblock();
- iftrue.link();
-
left.loadInAccumulator();
-
BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
-
- iffalse.link();
+ iftrue.link();
Reference right = expression(ast->right);
right.loadInAccumulator();
@@ -1496,7 +1511,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError())
return false;
- binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
+ binopHelper(ast, baseOp(ast->op), tempLeft, right).loadInAccumulator();
setExprResult(left.storeRetainAccumulator());
break;
@@ -1509,7 +1524,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right = expression(ast->right);
if (hasError())
return false;
- setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
+ setExprResult(binopHelper(ast, static_cast<QSOperator::Op>(ast->op), right, left));
break;
}
Q_FALLTHROUGH();
@@ -1544,7 +1559,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError())
return false;
- setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
+ setExprResult(binopHelper(ast, static_cast<QSOperator::Op>(ast->op), left, right));
break;
}
@@ -1553,8 +1568,11 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
}
-Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Reference &right)
+Codegen::Reference Codegen::binopHelper(BinaryExpression *ast, QSOperator::Op oper, Reference &left,
+ Reference &right)
{
+ auto loc = combine(ast->left->firstSourceLocation(), ast->right->lastSourceLocation());
+ bytecodeGenerator->setLocation(loc);
switch (oper) {
case QSOperator::Add: {
left = left.storeOnStack();
@@ -1946,12 +1964,13 @@ bool Codegen::visit(CallExpression *ast)
if (hasError())
return false;
- auto label = traverseOptionalChain(ast);
+ const bool isTailOfChain = traverseOptionalChain(ast);
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
- Reference base = expression(ast->base);
+ Reference expr = expression(ast->base);
+ Reference base = expr;
if (hasError())
return false;
@@ -1975,21 +1994,21 @@ bool Codegen::visit(CallExpression *ast)
break;
}
+ if (expr.hasSavedCallBaseSlot) {
+ // Hack to preserve `this` context in optional chain calls. See optionalChainFinalizer().
+ base.hasSavedCallBaseSlot = true;
+ base.savedCallBaseSlot = expr.savedCallBaseSlot;
+ base.savedCallPropertyNameIndex = expr.savedCallPropertyNameIndex;
+ }
+
int thisObject = bytecodeGenerator->newRegister();
int functionObject = bytecodeGenerator->newRegister();
- if (ast->isOptional || (!base.optionalChainJumpLabel.isNull() && base.optionalChainJumpLabel->isValid())) {
- if (ast->isOptional)
- Q_ASSERT(m_optionalChainLabels.contains(ast));
-
- auto jumpLabel = ast->isOptional ? m_optionalChainLabels.take(ast) : *base.optionalChainJumpLabel.get();
-
+ if (ast->isOptional || m_optionalChainsStates.top().actuallyHasOptionals) {
base.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
- auto jumpFalse = bytecodeGenerator->jumpFalse();
- bytecodeGenerator->addInstruction(Instruction::LoadUndefined());
- bytecodeGenerator->jump().link(jumpLabel);
- jumpFalse.link();
+ auto jumpToUndefined = bytecodeGenerator->jumpTrue();
+ m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined));
}
auto calldata = pushArgs(ast->arguments);
@@ -2032,20 +2051,12 @@ bool Codegen::visit(CallExpression *ast)
bytecodeGenerator->addInstruction(call);
}
- setExprResult(Reference::fromAccumulator(this));
-
- if (label.has_value())
- label->link();
-
+ optionalChainFinalizer(Reference::fromAccumulator(this), isTailOfChain);
return false;
-
}
handleCall(base, calldata, functionObject, thisObject, ast->isOptional);
-
- if (label.has_value())
- label->link();
-
+ optionalChainFinalizer(Reference::fromAccumulator(this), isTailOfChain);
return false;
}
@@ -2060,19 +2071,30 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
bytecodeGenerator->setLocation(base.sourceLocation);
//### Do we really need all these call instructions? can's we load the callee in a temp?
- if (base.type == Reference::Member) {
- if (!disable_lookups && useFastLookups) {
+ if (base.type == Reference::Member || base.hasSavedCallBaseSlot) {
+ if (useFastLookups) {
Instruction::CallPropertyLookup call;
- call.base = base.propertyBase.stackSlot();
- call.lookupIndex = registerGetterLookup(
+ if (base.hasSavedCallBaseSlot) {
+ call.base = base.savedCallBaseSlot;
+ call.lookupIndex = registerGetterLookup(
+ base.savedCallPropertyNameIndex, JSUnitGenerator::LookupForCall);
+ } else {
+ call.base = base.propertyBase.stackSlot();
+ call.lookupIndex = registerGetterLookup(
base.propertyNameIndex, JSUnitGenerator::LookupForCall);
+ }
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
} else {
Instruction::CallProperty call;
- call.base = base.propertyBase.stackSlot();
- call.name = base.propertyNameIndex;
+ if (base.hasSavedCallBaseSlot) {
+ call.base = base.savedCallBaseSlot;
+ call.name = base.savedCallPropertyNameIndex;
+ } else {
+ call.base = base.propertyBase.stackSlot();
+ call.name = base.propertyNameIndex;
+ }
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
@@ -2090,7 +2112,7 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
- } else if (!disable_lookups && useFastLookups && base.global) {
+ } else if (useFastLookups && base.global) {
if (base.qmlGlobal) {
Instruction::CallQmlContextPropertyLookup call;
call.index = registerQmlContextPropertyGetterLookup(
@@ -2137,8 +2159,6 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
}
-
- setExprResult(Reference::fromAccumulator(this));
}
Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
@@ -2248,7 +2268,7 @@ bool Codegen::visit(DeleteExpression *ast)
if (hasError())
return false;
- auto label = traverseOptionalChain(ast);
+ const bool isTailOfChain = traverseOptionalChain(ast);
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
@@ -2256,8 +2276,8 @@ bool Codegen::visit(DeleteExpression *ast)
if (hasError())
return false;
- // If there is a label, there is a chain and that should only be possible with those two kinds of references
- if (label.has_value())
+ const bool chainActuallyHasOptionals = m_optionalChainsStates.top().actuallyHasOptionals;
+ if (chainActuallyHasOptionals)
Q_ASSERT(expr.type == Reference::Member || expr.type == Reference::Subscript);
switch (expr.type) {
@@ -2291,10 +2311,11 @@ bool Codegen::visit(DeleteExpression *ast)
//### maybe add a variant where the base can be in the accumulator?
expr = expr.asLValue();
- if (!expr.optionalChainJumpLabel.isNull() && expr.optionalChainJumpLabel->isValid()) {
+ if (chainActuallyHasOptionals) {
expr.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
- bytecodeGenerator->jumpTrue().link(*expr.optionalChainJumpLabel.get());
+ auto jumpToUndefined = bytecodeGenerator->jumpTrue();
+ m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined));
}
Instruction::LoadRuntimeString instr;
@@ -2306,42 +2327,29 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.propertyBase.stackSlot();
del.index = index.stackSlot();
bytecodeGenerator->addInstruction(del);
- setExprResult(Reference::fromAccumulator(this));
-
- if (label.has_value()) {
- auto jump = bytecodeGenerator->jump();
- label->link();
- Instruction::LoadTrue loadTrue;
- bytecodeGenerator->addInstruction(loadTrue);
- jump.link();
- }
+ auto ref = Reference::fromAccumulator(this);
+ optionalChainFinalizer(ref, isTailOfChain, true);
return false;
}
case Reference::Subscript: {
//### maybe add a variant where the index can be in the accumulator?
expr = expr.asLValue();
- if (!expr.optionalChainJumpLabel.isNull() && expr.optionalChainJumpLabel->isValid()) {
+ if (chainActuallyHasOptionals) {
expr.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
- bytecodeGenerator->jumpTrue().link(*expr.optionalChainJumpLabel.get());
+ auto jumpToUndefined = bytecodeGenerator->jumpTrue();
+ m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined));
}
Instruction::DeleteProperty del;
del.base = expr.elementBase;
del.index = expr.elementSubscript.stackSlot();
bytecodeGenerator->addInstruction(del);
- setExprResult(Reference::fromAccumulator(this));
-
- if (label.has_value()) {
- auto jump = bytecodeGenerator->jump();
- label->link();
- Instruction::LoadTrue loadTrue;
- bytecodeGenerator->addInstruction(loadTrue);
- jump.link();
- }
+ auto ref = Reference::fromAccumulator(this);
+ optionalChainFinalizer(ref, isTailOfChain, true);
return false;
}
default:
@@ -2374,72 +2382,104 @@ bool Codegen::visit(SuperLiteral *)
return false;
}
-std::optional<Moth::BytecodeGenerator::Label> Codegen::traverseOptionalChain(Node *node) {
+bool Codegen::traverseOptionalChain(Node *node)
+{
if (m_seenOptionalChainNodes.contains(node))
- return {};
-
- auto label = bytecodeGenerator->newLabel();
+ return false;
- auto isOptionalChainNode = [](const Node *node) {
+ const auto isOptionalChainableNode = [](const Node *node) {
return node->kind == Node::Kind_FieldMemberExpression ||
node->kind == Node::Kind_CallExpression ||
node->kind == Node::Kind_ArrayMemberExpression ||
node->kind == Node::Kind_DeleteExpression;
};
-
- bool labelUsed = false;
-
- while (isOptionalChainNode(node)) {
+ m_optionalChainsStates.emplace();
+ while (isOptionalChainableNode(node)) {
m_seenOptionalChainNodes.insert(node);
switch (node->kind) {
case Node::Kind_FieldMemberExpression: {
- auto *fme = AST::cast<FieldMemberExpression*>(node);
-
- if (fme->isOptional) {
- m_optionalChainLabels.insert(fme, label);
- labelUsed = true;
- }
-
+ auto *fme = AST::cast<FieldMemberExpression *>(node);
+ m_optionalChainsStates.top().actuallyHasOptionals |= fme->isOptional;
node = fme->base;
break;
}
case Node::Kind_CallExpression: {
- auto *ce = AST::cast<CallExpression*>(node);
-
- if (ce->isOptional) {
- m_optionalChainLabels.insert(ce, label);
- labelUsed = true;
- }
-
+ auto *ce = AST::cast<CallExpression *>(node);
+ m_optionalChainsStates.top().actuallyHasOptionals |= ce->isOptional;
node = ce->base;
break;
}
case Node::Kind_ArrayMemberExpression: {
- auto *ame = AST::cast<ArrayMemberExpression*>(node);
-
- if (ame->isOptional) {
- m_optionalChainLabels.insert(ame, label);
- labelUsed = true;
- }
-
+ auto *ame = AST::cast<ArrayMemberExpression *>(node);
+ m_optionalChainsStates.top().actuallyHasOptionals |= ame->isOptional;
node = ame->base;
break;
}
- case Node::Kind_DeleteExpression: {
- auto *de = AST::cast<DeleteExpression*>(node);
- node = de->expression;
+ case Node::Kind_DeleteExpression:
+ node = AST::cast<DeleteExpression *>(node)->expression;
break;
- }
+ default:
+ Q_UNREACHABLE();
}
}
- if (!labelUsed) {
- label.link(); // If we don't link the unused label here, we would hit an assert later.
- return {};
+ return true;
+}
+
+void Codegen::optionalChainFinalizer(const Reference &expressionResult, bool tailOfChain,
+ bool isDeleteExpression)
+{
+ auto &chainState = m_optionalChainsStates.top();
+ if (!tailOfChain) {
+ setExprResult(expressionResult);
+ return;
+ } else if (!chainState.actuallyHasOptionals) {
+ setExprResult(expressionResult);
+ m_optionalChainsStates.pop();
+ return;
}
- return label;
+ auto savedBaseSlot = -1;
+ if (expressionResult.type == Reference::Member)
+ savedBaseSlot = expressionResult.propertyBase.storeOnStack().stackSlot();
+ expressionResult.loadInAccumulator();
+
+ std::optional<Moth::BytecodeGenerator::Jump> jumpToDone;
+ if (!isDeleteExpression) // Delete expressions always return true, avoid the extra jump
+ jumpToDone.emplace(bytecodeGenerator->jump());
+
+ for (auto &jump : chainState.jumpsToPatch)
+ jump.link();
+
+ if (isDeleteExpression)
+ bytecodeGenerator->addInstruction(Instruction::LoadTrue());
+ else
+ bytecodeGenerator->addInstruction(Instruction::LoadUndefined());
+
+ if (jumpToDone.has_value())
+ jumpToDone.value().link();
+
+ auto ref = Reference::fromAccumulator(this);
+ if (expressionResult.type == Reference::Member) {
+ /* Because the whole optional chain is handled at once with a chain finalizer instead of
+ * instruction by instruction, the result of the chain (either undefined or the result of
+ * the optional operation) is stored in the accumulator. This works fine except for one
+ * edge case where the `this` context is required in a call
+ * (see tst_ecmascripttests: language/expressions/optional-chaining/optional-call-preserves-this.js).
+ *
+ * In order to preserve the `this` context in the call, the call base and the property name
+ * index need to be available as with a Member reference. However, since the result must be
+ * in the accumulator the resulting reference is of type Accumulator. Therefore, the call
+ * base and the property name index are `glued` to an accumulator reference to make it work
+ * when deciding which call instruction to use later on.
+ */
+ ref.hasSavedCallBaseSlot = true;
+ ref.savedCallBaseSlot = savedBaseSlot;
+ ref.savedCallPropertyNameIndex = expressionResult.propertyNameIndex;
+ }
+ setExprResult(ref);
+ m_optionalChainsStates.pop();
}
bool Codegen::visit(FieldMemberExpression *ast)
@@ -2447,9 +2487,10 @@ bool Codegen::visit(FieldMemberExpression *ast)
if (hasError())
return false;
- auto label = traverseOptionalChain(ast);
+ const bool isTailOfChain = traverseOptionalChain(ast);
TailCallBlocker blockTailCalls(this);
+
if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
if (id->name == QLatin1String("new")) {
// new.target
@@ -2460,27 +2501,17 @@ bool Codegen::visit(FieldMemberExpression *ast)
r.isReadonly = true;
setExprResult(r);
- if (label.has_value())
- label->link();
-
return false;
}
- Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
- setExprResult(r);
-
- if (label.has_value())
- label->link();
-
+ auto ref = Reference::fromStackSlot(this, CallData::NewTarget);
+ optionalChainFinalizer(ref, isTailOfChain);
return false;
}
}
Reference base = expression(ast->base);
- if (ast->isOptional)
- Q_ASSERT(m_optionalChainLabels.contains(ast));
-
if (hasError())
return false;
if (base.isSuper()) {
@@ -2488,19 +2519,15 @@ bool Codegen::visit(FieldMemberExpression *ast)
load.stringId = registerString(ast->name.toString());
bytecodeGenerator->addInstruction(load);
Reference property = Reference::fromAccumulator(this).storeOnStack();
- setExprResult(Reference::fromSuperProperty(property));
-
- if (label.has_value())
- label->link();
+ optionalChainFinalizer(Reference::fromSuperProperty(property), isTailOfChain);
return false;
}
- setExprResult(Reference::fromMember(
- base, ast->name.toString(), ast->lastSourceLocation(),
- ast->isOptional ? m_optionalChainLabels.take(ast) : Moth::BytecodeGenerator::Label(),
- label.has_value() ? label.value() : Moth::BytecodeGenerator::Label()));
+ auto ref = Reference::fromMember(base, ast->name.toString(), ast->lastSourceLocation(),
+ ast->isOptional, &m_optionalChainsStates.top().jumpsToPatch);
+ optionalChainFinalizer(ref, isTailOfChain);
return false;
}
@@ -2554,6 +2581,7 @@ bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast)
--calldata.argv;
handleCall(base, calldata, functionObject, thisObject);
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2612,7 +2640,7 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co
}
if (resolved.isInjected && accessLocation.isValid()) {
- qCWarning(lcQmlCompiler).nospace().noquote()
+ qCWarning(lcQmlInjectedParameter).nospace().noquote()
<< url().toString() << ":" << accessLocation.startLine
<< ":" << accessLocation.startColumn << " Parameter \"" << name
<< "\" is not declared."
@@ -3222,15 +3250,15 @@ bool Codegen::visit(YieldExpression *ast)
Instruction::IteratorNextForYieldStar next;
next.object = lhsValue.stackSlot();
next.iterator = iterator.stackSlot();
- bytecodeGenerator->addInstruction(next);
-
- BytecodeGenerator::Jump done = bytecodeGenerator->jumpTrue();
+ BytecodeGenerator::Jump done = bytecodeGenerator->addJumpInstruction(next);
bytecodeGenerator->jumpNotUndefined().link(loop);
+
lhsValue.loadInAccumulator();
emitReturn(acc);
done.link();
+ bytecodeGenerator->checkException();
lhsValue.loadInAccumulator();
setExprResult(acc);
@@ -3411,8 +3439,9 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalPara
if (showCode) {
qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict
<< "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue;
- QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(),
- _context->line, _context->lineAndStatementNumberMapping);
+ qDebug().noquote() << QV4::Moth::dumpBytecode(
+ _context->code, _context->locals.size(), _context->arguments.size(),
+ _context->line, _context->lineAndStatementNumberMapping);
qDebug();
}
}
@@ -3573,7 +3602,6 @@ bool Codegen::visit(ForEachStatement *ast)
TailCallBlocker blockTailCalls(this);
Reference iterator = Reference::fromStackSlot(this);
- Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
Reference lhsValue = Reference::fromStackSlot(this);
// There should be a temporal block, so that variables declared in lhs shadow outside vars.
@@ -3594,25 +3622,28 @@ bool Codegen::visit(ForEachStatement *ast)
BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done;
{
- auto cleanup = [ast, iterator, iteratorDone, this]() {
- if (ast->type == ForEachType::Of) {
+ std::function<void()> cleanup;
+ if (ast->type == ForEachType::Of) {
+ done = bytecodeGenerator->newLabel();
+ cleanup = [iterator, this, done]() {
iterator.loadInAccumulator();
Instruction::IteratorClose close;
- close.done = iteratorDone.stackSlot();
bytecodeGenerator->addInstruction(close);
- }
- };
- ControlFlowLoop flow(this, &end, &in, cleanup);
+ done.link();
+ };
+ } else {
+ done = end;
+ }
+ ControlFlowLoop flow(this, &end, &in, std::move(cleanup));
bytecodeGenerator->addLoopStart(in);
in.link();
iterator.loadInAccumulator();
Instruction::IteratorNext next;
next.value = lhsValue.stackSlot();
- next.done = iteratorDone.stackSlot();
- bytecodeGenerator->addInstruction(next);
- bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end);
+ bytecodeGenerator->addJumpInstruction(next).link(done);
// each iteration gets it's own context, as per spec
{
@@ -3928,7 +3959,8 @@ void Codegen::handleTryCatch(TryStatement *ast)
void Codegen::handleTryFinally(TryStatement *ast)
{
RegisterScope scope(this);
- ControlFlowFinally finally(this, ast->finallyExpression);
+ const bool hasCatchBlock = ast->catchExpression;
+ ControlFlowFinally finally(this, ast->finallyExpression, hasCatchBlock);
TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before finally is generated
if (ast->catchExpression) {
@@ -4100,14 +4132,16 @@ QQmlJS::DiagnosticMessage Codegen::error() const
return _error;
}
-QV4::CompiledData::CompilationUnit Codegen::generateCompilationUnit(
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Codegen::generateCompilationUnit(
bool generateUnitData)
{
- return QV4::CompiledData::CompilationUnit(
- generateUnitData ? jsUnitGenerator->generateUnit() : nullptr);
+ return QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
+ new QV4::CompiledData::CompilationUnit(
+ generateUnitData ? jsUnitGenerator->generateUnit() : nullptr),
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>::Adopt);
}
-CompiledData::CompilationUnit Codegen::compileModule(
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Codegen::compileModule(
bool debugMode, const QString &url, const QString &sourceCode,
const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics)
{
@@ -4122,7 +4156,7 @@ CompiledData::CompilationUnit Codegen::compileModule(
*diagnostics = parser.diagnosticMessages();
if (!parsed)
- return CompiledData::CompilationUnit();
+ return QQmlRefPointer<CompiledData::CompilationUnit>();
QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode());
if (!moduleNode) {
@@ -4143,7 +4177,7 @@ CompiledData::CompilationUnit Codegen::compileModule(
if (cg.hasError()) {
if (diagnostics)
*diagnostics << cg.error();
- return CompiledData::CompilationUnit();
+ return QQmlRefPointer<CompiledData::CompilationUnit>();
}
return cg.generateCompilationUnit();
@@ -4561,7 +4595,7 @@ void Codegen::Reference::storeAccumulator() const
}
} return;
case Member:
- if (!disable_lookups && codegen->useFastLookups) {
+ if (codegen->useFastLookups) {
Instruction::SetLookup store;
store.base = propertyBase.stackSlot();
store.index = codegen->registerSetterLookup(propertyNameIndex);
@@ -4621,7 +4655,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str
StaticValue p = StaticValue::fromReturnedValue(constant);
if (p.isNumber()) {
double d = p.asDouble();
- int i = static_cast<int>(d);
+ int i = QJSNumberCoercion::toInteger(d);
if (d == i && (d != 0 || !std::signbit(d))) {
if (!i) {
Instruction::LoadZero load;
@@ -4679,7 +4713,7 @@ QT_WARNING_POP
if (sourceLocation.isValid())
codegen->bytecodeGenerator->setLocation(sourceLocation);
- if (!disable_lookups && global) {
+ if (global) {
if (qmlGlobal) {
Instruction::LoadQmlContextPropertyLookup load;
load.index = codegen->registerQmlContextPropertyGetterLookup(
@@ -4704,13 +4738,12 @@ QT_WARNING_POP
if (sourceLocation.isValid())
codegen->bytecodeGenerator->setLocation(sourceLocation);
- if (!disable_lookups && codegen->useFastLookups) {
- if (optionalChainJumpLabel->isValid()) {
- // If we got a valid jump label, this means it's an optional lookup
+ if (codegen->useFastLookups) {
+ if (optionalChainJumpsToPatch && isOptional) {
auto jump = codegen->bytecodeGenerator->jumpOptionalLookup(
codegen->registerGetterLookup(
propertyNameIndex, JSUnitGenerator::LookupForStorage));
- jump.link(*optionalChainJumpLabel.get());
+ optionalChainJumpsToPatch->emplace_back(std::move(jump));
} else {
Instruction::GetLookup load;
load.index = codegen->registerGetterLookup(
@@ -4718,18 +4751,15 @@ QT_WARNING_POP
codegen->bytecodeGenerator->addInstruction(load);
}
} else {
- if (optionalChainJumpLabel->isValid()) {
+ if (optionalChainJumpsToPatch && isOptional) {
auto jump = codegen->bytecodeGenerator->jumpOptionalProperty(propertyNameIndex);
- jump.link(*optionalChainJumpLabel.get());
+ optionalChainJumpsToPatch->emplace_back(std::move(jump));
} else {
Instruction::LoadProperty load;
load.name = propertyNameIndex;
codegen->bytecodeGenerator->addInstruction(load);
}
}
- if (optionalChainTargetLabel->isValid()) {
- optionalChainTargetLabel->link();
- }
return;
case Import: {
Instruction::LoadImport load;
@@ -4744,10 +4774,6 @@ QT_WARNING_POP
Instruction::LoadElement load;
load.base = elementBase;
codegen->bytecodeGenerator->addInstruction(load);
-
- if (optionalChainTargetLabel->isValid()) {
- optionalChainTargetLabel->link();
- }
} return;
case Invalid:
break;
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index fddbcc7d31..3a27cb1487 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -25,6 +25,7 @@
#include <private/qv4calldata_p.h>
#include <QtCore/qsharedpointer.h>
+#include <stack>
QT_BEGIN_NAMESPACE
@@ -44,7 +45,7 @@ struct ControlFlow;
struct ControlFlowCatch;
struct ControlFlowFinally;
-class Q_QML_COMPILER_PRIVATE_EXPORT CodegenWarningInterface
+class Q_QML_COMPILER_EXPORT CodegenWarningInterface
{
public:
virtual void reportVarUsedBeforeDeclaration(const QString &name, const QString &fileName,
@@ -59,7 +60,7 @@ inline CodegenWarningInterface *defaultCodegenWarningInterface()
return &iface;
}
-class Q_QML_COMPILER_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor
+class Q_QML_COMPILER_EXPORT Codegen: protected QQmlJS::AST::Visitor
{
protected:
using BytecodeGenerator = QV4::Moth::BytecodeGenerator;
@@ -190,7 +191,9 @@ public:
global(false),
qmlGlobal(false),
throwsReferenceError(false),
- subscriptLoadedForCall(false)
+ subscriptLoadedForCall(false),
+ isOptional(false),
+ hasSavedCallBaseSlot(false)
{}
Reference(const Reference &) = default;
@@ -256,8 +259,8 @@ public:
static Reference
fromMember(const Reference &baseRef, const QString &name,
QQmlJS::SourceLocation sourceLocation = QQmlJS::SourceLocation(),
- Moth::BytecodeGenerator::Label jumpLabel = Moth::BytecodeGenerator::Label(),
- Moth::BytecodeGenerator::Label targetLabel = Moth::BytecodeGenerator::Label())
+ bool isOptional = false,
+ std::vector<Moth::BytecodeGenerator::Jump> *optionalChainJumpsToPatch = nullptr)
{
Q_ASSERT(baseRef.isValid());
Reference r(baseRef.codegen, Member);
@@ -265,8 +268,8 @@ public:
r.propertyNameIndex = r.codegen->registerString(name);
r.requiresTDZCheck = baseRef.requiresTDZCheck;
r.sourceLocation = sourceLocation;
- r.optionalChainJumpLabel.reset(new Moth::BytecodeGenerator::Label(jumpLabel));
- r.optionalChainTargetLabel.reset(new Moth::BytecodeGenerator::Label(targetLabel));
+ r.optionalChainJumpsToPatch = optionalChainJumpsToPatch;
+ r.isOptional = isOptional;
return r;
}
static Reference fromSuperProperty(const Reference &property) {
@@ -276,14 +279,13 @@ public:
r.subscriptRequiresTDZCheck = property.requiresTDZCheck;
return r;
}
- static Reference fromSubscript(const Reference &baseRef, const Reference &subscript, Moth::BytecodeGenerator::Label targetLabel = Moth::BytecodeGenerator::Label()) {
+ static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) {
Q_ASSERT(baseRef.isStackSlot());
Reference r(baseRef.codegen, Subscript);
r.elementBase = baseRef.stackSlot();
r.elementSubscript = subscript.asRValue();
r.requiresTDZCheck = baseRef.requiresTDZCheck;
r.subscriptRequiresTDZCheck = subscript.requiresTDZCheck;
- r.optionalChainTargetLabel.reset(new Moth::BytecodeGenerator::Label(targetLabel));
return r;
}
static Reference fromConst(Codegen *cg, QV4::ReturnedValue constant) {
@@ -373,9 +375,12 @@ public:
quint32 qmlGlobal:1;
quint32 throwsReferenceError:1;
quint32 subscriptLoadedForCall:1;
+ quint32 isOptional: 1;
+ quint32 hasSavedCallBaseSlot: 1;
QQmlJS::SourceLocation sourceLocation = QQmlJS::SourceLocation();
- QSharedPointer<Moth::BytecodeGenerator::Label> optionalChainJumpLabel;
- QSharedPointer<Moth::BytecodeGenerator::Label> optionalChainTargetLabel;
+ std::vector<Moth::BytecodeGenerator::Jump> *optionalChainJumpsToPatch = nullptr;
+ int savedCallBaseSlot = -1;
+ int savedCallPropertyNameIndex = -1;
private:
void storeAccumulator() const;
@@ -595,6 +600,7 @@ protected:
bool visit(QQmlJS::AST::UiArrayMemberList *ast) override;
bool visit(QQmlJS::AST::UiImport *ast) override;
bool visit(QQmlJS::AST::UiHeaderItemList *ast) override;
+ bool visit(QQmlJS::AST::UiPragmaValueList *ast) override;
bool visit(QQmlJS::AST::UiPragma *ast) override;
bool visit(QQmlJS::AST::UiObjectInitializer *ast) override;
bool visit(QQmlJS::AST::UiObjectMemberList *ast) override;
@@ -705,7 +711,8 @@ public:
QQmlJS::DiagnosticMessage error() const;
QUrl url() const;
- Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right);
+ Reference binopHelper(QQmlJS::AST::BinaryExpression *ast, QSOperator::Op oper, Reference &left,
+ Reference &right);
Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right);
struct Arguments { int argc; int argv; bool hasSpread; };
Arguments pushArgs(QQmlJS::AST::ArgumentList *args);
@@ -725,8 +732,9 @@ public:
const QString &name, bool lhs,
const QQmlJS::SourceLocation &accessLocation = QQmlJS::SourceLocation());
- QV4::CompiledData::CompilationUnit generateCompilationUnit(bool generateUnitData = true);
- static QV4::CompiledData::CompilationUnit compileModule(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(
+ bool generateUnitData = true);
+ static QQmlRefPointer<QV4::CompiledData::CompilationUnit> compileModule(
bool debugMode, const QString &url, const QString &sourceCode,
const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics);
@@ -800,8 +808,15 @@ protected:
bool _tailCallsAreAllowed = true;
bool storeSourceLocations = false;
QSet<QString> m_globalNames;
+
+ struct OptionalChainState
+ {
+ QQmlJS::AST::Node *tailNodeOfChain = nullptr;
+ std::vector<Moth::BytecodeGenerator::Jump> jumpsToPatch;
+ bool actuallyHasOptionals = false;
+ };
QSet<QQmlJS::AST::Node*> m_seenOptionalChainNodes;
- QHash<QQmlJS::AST::Node*, Moth::BytecodeGenerator::Label> m_optionalChainLabels;
+ std::stack<OptionalChainState> m_optionalChainsStates;
ControlFlow *controlFlow = nullptr;
@@ -835,11 +850,14 @@ protected:
};
private:
+ Q_DISABLE_COPY(Codegen)
VolatileMemoryLocations scanVolatileMemoryLocations(QQmlJS::AST::Node *ast);
void handleConstruct(const Reference &base, QQmlJS::AST::ArgumentList *args);
void throwError(ErrorType errorType, const QQmlJS::SourceLocation &loc,
const QString &detail);
- std::optional<Moth::BytecodeGenerator::Label> traverseOptionalChain(QQmlJS::AST::Node *node);
+ bool traverseOptionalChain(QQmlJS::AST::Node *node);
+ void optionalChainFinalizer(const Reference &expressionResult, bool tailOfChain,
+ bool isDeleteExpression = false);
Reference loadSubscriptForCall(const Reference &base);
void generateThrowException(const QString &type, const QString &text = QString());
};
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 051a87a6c7..7a7c8f621b 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -15,12 +15,17 @@
#include <QtEndian>
// Efficient implementation that takes advantage of powers of two.
+
+QT_BEGIN_NAMESPACE
+namespace QtPrivate { // Disambiguate from WTF::roundUpToMultipleOf
static inline size_t roundUpToMultipleOf(size_t divisor, size_t x)
{
Q_ASSERT(divisor && !(divisor & (divisor - 1)));
const size_t remainderMask = divisor - 1;
return (x + remainderMask) & ~remainderMask;
}
+}
+QT_END_NAMESPACE
QV4::Compiler::StringTableGenerator::StringTableGenerator()
{
@@ -66,7 +71,8 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
{
char *dataStart = reinterpret_cast<char *>(unit);
quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable);
- char *stringData = reinterpret_cast<char *>(stringTable) + roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint));
+ char *stringData = reinterpret_cast<char *>(stringTable)
+ + QtPrivate::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint));
for (int i = backingUnitTableSize ; i < strings.size(); ++i) {
const int index = i - backingUnitTableSize;
stringTable[index] = stringData - dataStart;
@@ -191,6 +197,10 @@ QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) const
return constants.at(idx);
}
+// The JSClass object and its members are stored contiguously in the jsClassData.
+// In order to get to the members you have to skip over the JSClass, therefore +1.
+static constexpr qsizetype jsClassMembersOffset = 1;
+
int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
{
// ### re-use existing class definitions.
@@ -203,7 +213,8 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
jsClass->nMembers = members.size();
- CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
+ CompiledData::JSClassMember *member
+ = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + jsClassMembersOffset);
for (const auto &name : members) {
member->set(registerString(name), false);
@@ -213,6 +224,25 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
return jsClassOffsets.size() - 1;
}
+int QV4::Compiler::JSUnitGenerator::jsClassSize(int jsClassId) const
+{
+ const CompiledData::JSClass *jsClass
+ = reinterpret_cast<const CompiledData::JSClass*>(
+ jsClassData.data() + jsClassOffsets[jsClassId]);
+ return jsClass->nMembers;
+}
+
+QString QV4::Compiler::JSUnitGenerator::jsClassMember(int jsClassId, int member) const
+{
+ const CompiledData::JSClass *jsClass = reinterpret_cast<const CompiledData::JSClass*>(
+ jsClassData.data() + jsClassOffsets[jsClassId]);
+ Q_ASSERT(member >= 0);
+ Q_ASSERT(uint(member) < jsClass->nMembers);
+ const CompiledData::JSClassMember *members
+ = reinterpret_cast<const CompiledData::JSClassMember*>(jsClass + jsClassMembersOffset);
+ return stringForIndex(members[member].nameOffset());
+}
+
int QV4::Compiler::JSUnitGenerator::registerTranslation(const QV4::CompiledData::TranslationData &translation)
{
translations.append(translation);
@@ -398,7 +428,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
{
QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
- quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*function)));
+ quint32 currentOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, sizeof(*function)));
function->nameIndex = getStringId(irFunction->name);
function->flags = 0;
@@ -573,7 +603,7 @@ void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context
{
QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
- quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*block)));
+ quint32 currentOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, sizeof(*block)));
block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone;
block->nLocals = irBlock->locals.size();
@@ -636,7 +666,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.constantTableSize = constants.size();
// Ensure we load constants from well-aligned addresses into for example SSE registers.
- nextOffset = static_cast<quint32>(roundUpToMultipleOf(16, nextOffset));
+ nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(16, nextOffset));
unit.offsetToConstantTable = nextOffset;
nextOffset += unit.constantTableSize * sizeof(ReturnedValue);
@@ -647,19 +677,23 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
*jsClassDataOffset = nextOffset;
nextOffset += jsClassData.size();
- nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
unit.translationTableSize = translations.size();
unit.offsetToTranslationTable = nextOffset;
nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData);
+ if (unit.translationTableSize != 0) {
+ constexpr auto spaceForTranslationContextId = sizeof(quint32_le);
+ nextOffset += spaceForTranslationContextId;
+ }
- nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) {
*tableSizePtr = count;
*offsetPtr = nextOffset;
nextOffset += count * sizeof(CompiledData::ExportEntry);
- nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
};
reserveExportTable(module->localExportEntries.size(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable);
@@ -669,12 +703,12 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.importEntryTableSize = module->importEntries.size();
unit.offsetToImportEntryTable = nextOffset;
nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry);
- nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
unit.moduleRequestTableSize = module->moduleRequests.size();
unit.offsetToModuleRequestTable = nextOffset;
nextOffset += unit.moduleRequestTableSize * sizeof(uint);
- nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
quint32 functionSize = 0;
for (int i = 0; i < module->functions.size(); ++i) {
@@ -715,7 +749,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
if (option == GenerateWithStringTable) {
unit.stringTableSize = stringTable.stringCount();
- nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset));
unit.offsetToStringTable = nextOffset;
nextOffset += stringTable.sizeOfTableAndData();
} else {
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 82afb7cf3a..bf2f5c8167 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -42,7 +42,7 @@ struct Module;
struct Class;
struct TemplateObject;
-struct Q_QML_COMPILER_PRIVATE_EXPORT StringTableGenerator {
+struct Q_QML_COMPILER_EXPORT StringTableGenerator {
StringTableGenerator();
int registerString(const QString &str);
@@ -69,7 +69,7 @@ private:
bool frozen = false;
};
-struct Q_QML_COMPILER_PRIVATE_EXPORT JSUnitGenerator {
+struct Q_QML_COMPILER_EXPORT JSUnitGenerator {
enum LookupMode { LookupForStorage, LookupForCall };
static void generateUnitChecksum(CompiledData::Unit *unit);
@@ -100,6 +100,8 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT JSUnitGenerator {
ReturnedValue constant(int idx) const;
int registerJSClass(const QStringList &members);
+ int jsClassSize(int jsClassId) const;
+ QString jsClassMember(int jsClassId, int member) const;
int registerTranslation(const CompiledData::TranslationData &translation);
@@ -116,6 +118,7 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT JSUnitGenerator {
StringTableGenerator stringTable;
QString codeGeneratorName;
+
private:
CompiledData::Unit generateHeader(GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset);
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
index 5fb383c4b6..b190b77410 100644
--- a/src/qml/compiler/qv4compilercontrolflow_p.h
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -167,8 +167,8 @@ struct ControlFlowUnwindCleanup : public ControlFlowUnwind
~ControlFlowUnwindCleanup() {
if (cleanup) {
unwindLabel.link();
- generator()->setUnwindHandler(parentUnwindHandler());
cleanup();
+ generator()->setUnwindHandler(parentUnwindHandler());
emitUnwindHandler();
}
}
@@ -338,12 +338,17 @@ struct ControlFlowFinally : public ControlFlowUnwind
QQmlJS::AST::Finally *finally;
bool insideFinally = false;
- ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally)
+ ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally, bool hasCatchBlock)
: ControlFlowUnwind(cg, Finally), finally(finally)
{
Q_ASSERT(finally != nullptr);
setupUnwindHandler();
- generator()->setUnwindHandler(&unwindLabel);
+
+ // No need to set the handler for the finally now if there is a catch block.
+ // In that case, a handler for the latter will be set immediately after this.
+ if (!hasCatchBlock) {
+ generator()->setUnwindHandler(&unwindLabel);
+ }
}
virtual bool requiresUnwind() override {
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index eae076012a..edba9d40b0 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -48,20 +48,18 @@ static QByteArray rawBytes(const char *data, int n)
}
#define ABSOLUTE_OFFSET() \
- (code - start + offset)
+ (code + beginOffset - start + offset)
#define MOTH_BEGIN_INSTR(instr) \
{ \
INSTR_##instr(MOTH_DECODE_WITH_BASE) \
- QDebug d = qDebug(); \
- d.noquote(); \
- d.nospace(); \
if (static_cast<int>(Instr::Type::instr) >= 0x100) \
--base_ptr; \
- d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \
+ s << alignedLineNumber(line) << alignedNumber(beginOffset + codeOffset).constData() << ": " \
<< rawBytes(base_ptr, int(code - base_ptr)) << #instr << " ";
#define MOTH_END_INSTR(instr) \
+ s << "\n"; \
continue; \
}
@@ -106,24 +104,37 @@ QString dumpArguments(int argc, int argv, int nFormals)
return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")");
}
-void dumpBytecode(
+QString dumpBytecode(
const char *code, int len, int nLocals, int nFormals, int /*startLine*/,
const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping)
{
+ return dumpBytecode(code, len, nLocals, nFormals, 0, len - 1, lineAndStatementNumberMapping);
+}
+
+QString dumpBytecode(
+ const char *code, int len, int nLocals, int nFormals, int beginOffset, int endOffset,
+ const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping)
+{
+ Q_ASSERT(beginOffset <= endOffset && 0 <= beginOffset && endOffset <= len);
+
MOTH_JUMP_TABLE;
auto findLine = [](const CompiledData::CodeOffsetToLineAndStatement &entry, uint offset) {
return entry.codeOffset < offset;
};
+ QString output;
+ QTextStream s{ &output };
+
int lastLine = -1;
+ code += beginOffset;
const char *start = code;
- const char *end = code + len;
+ const char *end = code + (endOffset - beginOffset) + 1;
while (code < end) {
const auto codeToLine = std::lower_bound(
lineAndStatementNumberMapping.constBegin(),
lineAndStatementNumberMapping.constEnd(),
- static_cast<uint>(code - start) + 1, findLine) - 1;
+ static_cast<uint>(code - start + beginOffset) + 1, findLine) - 1;
int line = int(codeToLine->line);
if (line != lastLine)
lastLine = line;
@@ -135,23 +146,23 @@ void dumpBytecode(
MOTH_DISPATCH()
MOTH_BEGIN_INSTR(LoadReg)
- d << dumpRegister(reg, nFormals);
+ s << dumpRegister(reg, nFormals);
MOTH_END_INSTR(LoadReg)
MOTH_BEGIN_INSTR(StoreReg)
- d << dumpRegister(reg, nFormals);
+ s << dumpRegister(reg, nFormals);
MOTH_END_INSTR(StoreReg)
MOTH_BEGIN_INSTR(MoveReg)
- d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals);
+ s << dumpRegister(srcReg, nFormals) << ", " << dumpRegister(destReg, nFormals);
MOTH_END_INSTR(MoveReg)
MOTH_BEGIN_INSTR(LoadImport)
- d << "i" << index;
+ s << "i" << index;
MOTH_END_INSTR(LoadImport)
MOTH_BEGIN_INSTR(LoadConst)
- d << "C" << index;
+ s << "C" << index;
MOTH_END_INSTR(LoadConst)
MOTH_BEGIN_INSTR(LoadNull)
@@ -170,111 +181,111 @@ void dumpBytecode(
MOTH_END_INSTR(LoadUndefined)
MOTH_BEGIN_INSTR(LoadInt)
- d << value;
+ s << value;
MOTH_END_INSTR(LoadInt)
MOTH_BEGIN_INSTR(MoveConst)
- d << dumpRegister(destTemp, nFormals) << ", C" << constIndex;
+ s << "C" << constIndex << ", " << dumpRegister(destTemp, nFormals);
MOTH_END_INSTR(MoveConst)
MOTH_BEGIN_INSTR(LoadLocal)
if (index < nLocals)
- d << "l" << index;
+ s << "l" << index;
else
- d << "a" << (index - nLocals);
+ s << "a" << (index - nLocals);
MOTH_END_INSTR(LoadLocal)
MOTH_BEGIN_INSTR(StoreLocal)
if (index < nLocals)
- d << "l" << index;
+ s << "l" << index;
else
- d << "a" << (index - nLocals);
+ s << "a" << (index - nLocals);
MOTH_END_INSTR(StoreLocal)
MOTH_BEGIN_INSTR(LoadScopedLocal)
if (index < nLocals)
- d << "l" << index << "@" << scope;
+ s << "l" << index << "@" << scope;
else
- d << "a" << (index - nLocals) << "@" << scope;
+ s << "a" << (index - nLocals) << "@" << scope;
MOTH_END_INSTR(LoadScopedLocal)
MOTH_BEGIN_INSTR(StoreScopedLocal)
if (index < nLocals)
- d << ", " << "l" << index << "@" << scope;
+ s << ", " << "l" << index << "@" << scope;
else
- d << ", " << "a" << (index - nLocals) << "@" << scope;
+ s << ", " << "a" << (index - nLocals) << "@" << scope;
MOTH_END_INSTR(StoreScopedLocal)
MOTH_BEGIN_INSTR(LoadRuntimeString)
- d << stringId;
+ s << stringId;
MOTH_END_INSTR(LoadRuntimeString)
MOTH_BEGIN_INSTR(MoveRegExp)
- d << dumpRegister(destReg, nFormals) << ", " <<regExpId;
+ s << regExpId << ", " << dumpRegister(destReg, nFormals);
MOTH_END_INSTR(MoveRegExp)
MOTH_BEGIN_INSTR(LoadClosure)
- d << value;
+ s << value;
MOTH_END_INSTR(LoadClosure)
MOTH_BEGIN_INSTR(LoadName)
- d << name;
+ s << name;
MOTH_END_INSTR(LoadName)
MOTH_BEGIN_INSTR(LoadGlobalLookup)
- d << index;
+ s << index;
MOTH_END_INSTR(LoadGlobalLookup)
MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
- d << index;
+ s << index;
MOTH_END_INSTR(LoadQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(StoreNameSloppy)
- d << name;
+ s << name;
MOTH_END_INSTR(StoreNameSloppy)
MOTH_BEGIN_INSTR(StoreNameStrict)
- d << name;
+ s << name;
MOTH_END_INSTR(StoreNameStrict)
MOTH_BEGIN_INSTR(LoadElement)
- d << dumpRegister(base, nFormals) << "[acc]";
+ s << dumpRegister(base, nFormals) << "[acc]";
MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(StoreElement)
- d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
+ s << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
- d << "acc[" << name << "]";
+ s << "acc[" << name << "]";
MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(LoadOptionalProperty)
- d << "acc[" << name << "], jump(" << ABSOLUTE_OFFSET() << ")";
+ s << "acc[" << name << "], jump(" << ABSOLUTE_OFFSET() << ")";
MOTH_END_INSTR(LoadOptionalProperty)
MOTH_BEGIN_INSTR(GetLookup)
- d << "acc(" << index << ")";
+ s << "acc(" << index << ")";
MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(GetOptionalLookup)
- d << "acc(" << index << "), jump(" << ABSOLUTE_OFFSET() << ")";
+ s << "acc(" << index << "), jump(" << ABSOLUTE_OFFSET() << ")";
MOTH_END_INSTR(GetOptionalLookup)
MOTH_BEGIN_INSTR(StoreProperty)
- d << dumpRegister(base, nFormals) << "[" << name<< "]";
+ s << dumpRegister(base, nFormals) << "[" << name<< "]";
MOTH_END_INSTR(StoreProperty)
MOTH_BEGIN_INSTR(SetLookup)
- d << dumpRegister(base, nFormals) << "(" << index << ")";
+ s << dumpRegister(base, nFormals) << "(" << index << ")";
MOTH_END_INSTR(SetLookup)
MOTH_BEGIN_INSTR(LoadSuperProperty)
- d << dumpRegister(property, nFormals);
+ s << dumpRegister(property, nFormals);
MOTH_END_INSTR(LoadSuperProperty)
MOTH_BEGIN_INSTR(StoreSuperProperty)
- d << dumpRegister(property, nFormals);
+ s << dumpRegister(property, nFormals);
MOTH_END_INSTR(StoreSuperProperty)
MOTH_BEGIN_INSTR(Yield)
@@ -284,73 +295,73 @@ void dumpBytecode(
MOTH_END_INSTR(YieldStar)
MOTH_BEGIN_INSTR(Resume)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(Resume)
MOTH_BEGIN_INSTR(CallValue)
- d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals);
+ s << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallWithReceiver)
- d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals)
+ s << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals)
<< dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallWithReceiver)
MOTH_BEGIN_INSTR(CallProperty)
- d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
+ s << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
;
MOTH_END_INSTR(CallProperty)
MOTH_BEGIN_INSTR(CallPropertyLookup)
- d << dumpRegister(base, nFormals) << "." << lookupIndex
+ s << dumpRegister(base, nFormals) << "." << lookupIndex
<< dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallName)
- d << name << dumpArguments(argc, argv, nFormals);
+ s << name << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallName)
MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
- d << dumpArguments(argc, argv, nFormals);
+ s << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallPossiblyDirectEval)
MOTH_BEGIN_INSTR(CallGlobalLookup)
- d << index << dumpArguments(argc, argv, nFormals);
+ s << index << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
- d << index << dumpArguments(argc, argv, nFormals);
+ s << index << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
- d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
+ s << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
<< dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallWithSpread)
MOTH_BEGIN_INSTR(Construct)
- d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ s << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(Construct)
MOTH_BEGIN_INSTR(ConstructWithSpread)
- d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ s << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(ConstructWithSpread)
MOTH_BEGIN_INSTR(SetUnwindHandler)
if (offset)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
else
- d << "<null>";
+ s << "<null>";
MOTH_END_INSTR(SetUnwindHandler)
MOTH_BEGIN_INSTR(UnwindDispatch)
MOTH_END_INSTR(UnwindDispatch)
MOTH_BEGIN_INSTR(UnwindToLabel)
- d << "(" << level << ") " << ABSOLUTE_OFFSET();
+ s << "(" << level << ") " << ABSOLUTE_OFFSET();
MOTH_END_INSTR(UnwindToLabel)
MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
- d << name;
+ s << name;
MOTH_END_INSTR(DeadTemporalZoneCheck)
MOTH_BEGIN_INSTR(ThrowException)
@@ -366,21 +377,21 @@ void dumpBytecode(
MOTH_END_INSTR(CreateCallContext)
MOTH_BEGIN_INSTR(PushCatchContext)
- d << index << ", " << name;
+ s << index << ", " << name;
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(PushWithContext)
MOTH_END_INSTR(PushWithContext)
MOTH_BEGIN_INSTR(PushBlockContext)
- d << index;
+ s << index;
MOTH_END_INSTR(PushBlockContext)
MOTH_BEGIN_INSTR(CloneBlockContext)
MOTH_END_INSTR(CloneBlockContext)
MOTH_BEGIN_INSTR(PushScriptContext)
- d << index;
+ s << index;
MOTH_END_INSTR(PushScriptContext)
MOTH_BEGIN_INSTR(PopScriptContext)
@@ -390,55 +401,55 @@ void dumpBytecode(
MOTH_END_INSTR(PopContext)
MOTH_BEGIN_INSTR(GetIterator)
- d << iterator;
+ s << iterator;
MOTH_END_INSTR(GetIterator)
MOTH_BEGIN_INSTR(IteratorNext)
- d << dumpRegister(value, nFormals) << ", " << dumpRegister(done, nFormals);
+ s << dumpRegister(value, nFormals) << ", " << ABSOLUTE_OFFSET();
MOTH_END_INSTR(IteratorNext)
MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
- d << dumpRegister(iterator, nFormals) << ", " << dumpRegister(object, nFormals);
+ s << dumpRegister(iterator, nFormals) << ", " << dumpRegister(object, nFormals)
+ << ABSOLUTE_OFFSET();
MOTH_END_INSTR(IteratorNextForYieldStar)
MOTH_BEGIN_INSTR(IteratorClose)
- d << dumpRegister(done, nFormals);
MOTH_END_INSTR(IteratorClose)
MOTH_BEGIN_INSTR(DestructureRestElement)
MOTH_END_INSTR(DestructureRestElement)
MOTH_BEGIN_INSTR(DeleteProperty)
- d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
+ s << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
- d << name;
+ s << name;
MOTH_END_INSTR(DeleteName)
MOTH_BEGIN_INSTR(TypeofName)
- d << name;
+ s << name;
MOTH_END_INSTR(TypeofName)
MOTH_BEGIN_INSTR(TypeofValue)
MOTH_END_INSTR(TypeofValue)
MOTH_BEGIN_INSTR(DeclareVar)
- d << isDeletable << ", " << varName;
+ s << isDeletable << ", " << varName;
MOTH_END_INSTR(DeclareVar)
MOTH_BEGIN_INSTR(DefineArray)
- d << dumpRegister(args, nFormals) << ", " << argc;
+ s << dumpRegister(args, nFormals) << ", " << argc;
MOTH_END_INSTR(DefineArray)
MOTH_BEGIN_INSTR(DefineObjectLiteral)
- d << internalClassId
+ s << internalClassId
<< ", " << argc
<< ", " << dumpRegister(args, nFormals);
MOTH_END_INSTR(DefineObjectLiteral)
MOTH_BEGIN_INSTR(CreateClass)
- d << classIndex
+ s << classIndex
<< ", " << dumpRegister(heritage, nFormals)
<< ", " << dumpRegister(computedNames, nFormals);
MOTH_END_INSTR(CreateClass)
@@ -450,7 +461,7 @@ void dumpBytecode(
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
MOTH_BEGIN_INSTR(CreateRestParameter)
- d << argIndex;
+ s << argIndex;
MOTH_END_INSTR(CreateRestParameter)
MOTH_BEGIN_INSTR(ConvertThisToObject)
@@ -463,23 +474,23 @@ void dumpBytecode(
MOTH_END_INSTR(ToObject)
MOTH_BEGIN_INSTR(Jump)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(Jump)
MOTH_BEGIN_INSTR(JumpTrue)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpTrue)
MOTH_BEGIN_INSTR(JumpFalse)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpFalse)
MOTH_BEGIN_INSTR(JumpNotUndefined)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpNotUndefined)
MOTH_BEGIN_INSTR(JumpNoException)
- d << ABSOLUTE_OFFSET();
+ s << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpNoException)
MOTH_BEGIN_INSTR(CheckException)
@@ -492,43 +503,43 @@ void dumpBytecode(
MOTH_END_INSTR(CmpNeNull)
MOTH_BEGIN_INSTR(CmpEqInt)
- d << lhs;
+ s << lhs;
MOTH_END_INSTR(CmpEq)
MOTH_BEGIN_INSTR(CmpNeInt)
- d << lhs;
+ s << lhs;
MOTH_END_INSTR(CmpNeInt)
MOTH_BEGIN_INSTR(CmpEq)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpEq)
MOTH_BEGIN_INSTR(CmpNe)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpNe)
MOTH_BEGIN_INSTR(CmpGt)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpGt)
MOTH_BEGIN_INSTR(CmpGe)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpGe)
MOTH_BEGIN_INSTR(CmpLt)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpLt)
MOTH_BEGIN_INSTR(CmpLe)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpLe)
MOTH_BEGIN_INSTR(CmpStrictEqual)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictEqual)
MOTH_BEGIN_INSTR(CmpStrictNotEqual)
- d << dumpRegister(lhs, nFormals);
+ s << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictNotEqual)
MOTH_BEGIN_INSTR(UNot)
@@ -550,87 +561,87 @@ void dumpBytecode(
MOTH_END_INSTR(Decrement)
MOTH_BEGIN_INSTR(Add)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Add)
MOTH_BEGIN_INSTR(BitAnd)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(BitAnd)
MOTH_BEGIN_INSTR(BitOr)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(BitOr)
MOTH_BEGIN_INSTR(BitXor)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(BitXor)
MOTH_BEGIN_INSTR(UShr)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(UShr)
MOTH_BEGIN_INSTR(Shr)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Shr)
MOTH_BEGIN_INSTR(Shl)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Shl)
MOTH_BEGIN_INSTR(BitAndConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(BitAndConst)
MOTH_BEGIN_INSTR(BitOrConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(BitOr)
MOTH_BEGIN_INSTR(BitXorConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(BitXor)
MOTH_BEGIN_INSTR(UShrConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(UShrConst)
MOTH_BEGIN_INSTR(ShrConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(ShrConst)
MOTH_BEGIN_INSTR(ShlConst)
- d << "acc, " << rhs;
+ s << "acc, " << rhs;
MOTH_END_INSTR(ShlConst)
MOTH_BEGIN_INSTR(Exp)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Exp)
MOTH_BEGIN_INSTR(Mul)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mul)
MOTH_BEGIN_INSTR(Div)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Div)
MOTH_BEGIN_INSTR(Mod)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mod)
MOTH_BEGIN_INSTR(Sub)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Sub)
MOTH_BEGIN_INSTR(As)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Sub)
MOTH_BEGIN_INSTR(CmpIn)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(CmpIn)
MOTH_BEGIN_INSTR(CmpInstanceOf)
- d << dumpRegister(lhs, nFormals) << ", acc";
+ s << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(CmpInstanceOf)
MOTH_BEGIN_INSTR(Ret)
@@ -640,20 +651,21 @@ void dumpBytecode(
MOTH_END_INSTR(Debug)
MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
- d << dumpRegister(firstReg, nFormals) << ", " << count;
+ s << dumpRegister(firstReg, nFormals) << ", " << count;
MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined)
MOTH_END_INSTR(ThrowOnNullOrUndefined)
MOTH_BEGIN_INSTR(GetTemplateObject)
- d << index;
+ s << index;
MOTH_END_INSTR(GetTemplateObject)
MOTH_BEGIN_INSTR(TailCall)
- d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
+ s << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(TailCall)
}
+ return output;
}
}
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index c61db9a6ce..4dde924379 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_Yield(op) INSTRUCTION(op, Yield, 0)
#define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0)
#define INSTR_Resume(op) INSTRUCTION(op, Resume, 1, offset)
-#define INSTR_IteratorNextForYieldStar(op) INSTRUCTION(op, IteratorNextForYieldStar, 2, iterator, object)
+#define INSTR_IteratorNextForYieldStar(op) INSTRUCTION(op, IteratorNextForYieldStar, 3, iterator, object, offset)
#define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base)
#define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base)
#define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property)
@@ -94,8 +94,8 @@ QT_BEGIN_NAMESPACE
#define INSTR_PopScriptContext(op) INSTRUCTION(op, PopScriptContext, 0)
#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 0)
#define INSTR_GetIterator(op) INSTRUCTION(op, GetIterator, 1, iterator)
-#define INSTR_IteratorNext(op) INSTRUCTION(op, IteratorNext, 2, value, done)
-#define INSTR_IteratorClose(op) INSTRUCTION(op, IteratorClose, 1, done)
+#define INSTR_IteratorNext(op) INSTRUCTION(op, IteratorNext, 2, value, offset)
+#define INSTR_IteratorClose(op) INSTRUCTION(op, IteratorClose, 0)
#define INSTR_DestructureRestElement(op) INSTRUCTION(op, DestructureRestElement, 0)
#define INSTR_DeleteProperty(op) INSTRUCTION(op, DeleteProperty, 2, base, index)
#define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name)
@@ -497,14 +497,22 @@ inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackS
// When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
-void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1,
- const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping
- = QVector<CompiledData::CodeOffsetToLineAndStatement>());
-inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1,
- const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping
- = QVector<CompiledData::CodeOffsetToLineAndStatement>()) {
- dumpBytecode(bytecode.constData(), bytecode.size(), nLocals, nFormals, startLine,
- lineAndStatementNumberMapping);
+Q_QML_EXPORT
+QString dumpBytecode(
+ const char *bytecode, int len, int nLocals, int nFormals, int beginOffset, int endOffset,
+ const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping =
+ QVector<CompiledData::CodeOffsetToLineAndStatement>());
+QString dumpBytecode(
+ const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1,
+ const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping =
+ QVector<CompiledData::CodeOffsetToLineAndStatement>());
+inline QString dumpBytecode(
+ const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1,
+ const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping =
+ QVector<CompiledData::CodeOffsetToLineAndStatement>())
+{
+ return dumpBytecode(bytecode.constData(), bytecode.size(), nLocals, nFormals, startLine,
+ lineAndStatementNumberMapping);
}
union Instr
diff --git a/src/qml/configure.cmake b/src/qml/configure.cmake
index 9f82bdecd9..1bc647ff29 100644
--- a/src/qml/configure.cmake
+++ b/src/qml/configure.cmake
@@ -14,45 +14,11 @@ qt_find_package(LTTngUST PROVIDED_TARGETS LTTng::UST MODULE_NAME qml QMAKE_LIB l
qt_find_package(Python REQUIRED)
if(Python_Interpreter_FOUND)
# Need to make it globally available to the project
- set(QT_INTERNAL_DECLARATIVE_PYTHON "${Python_EXECUTABLE}" CACHE STRING "")
+ set(QT_INTERNAL_DECLARATIVE_PYTHON "${Python_EXECUTABLE}" CACHE STRING "" FORCE)
endif()
#### Tests
-# pointer_32bit
-qt_config_compile_test(pointer_32bit
- LABEL "32bit pointers"
- CODE
-"
-
-
-int main(int argc, char **argv)
-{
- (void)argc; (void)argv;
- /* BEGIN TEST: */
-static_assert(sizeof(void *) == 4, \"fail\");
- /* END TEST: */
- return 0;
-}
-")
-
-# pointer_64bit
-qt_config_compile_test(pointer_64bit
- LABEL "64bit pointers"
- CODE
-"
-
-
-int main(int argc, char **argv)
-{
- (void)argc; (void)argv;
- /* BEGIN TEST: */
-static_assert(sizeof(void *) == 8, \"fail\");
- /* END TEST: */
- return 0;
-}
-")
-
# arm_thumb
qt_config_compile_test(arm_thumb
LABEL "THUMB mode on ARM"
@@ -111,13 +77,27 @@ qt_feature("qml-network" PUBLIC
PURPOSE "Provides network transparency."
CONDITION QT_FEATURE_network
)
-# On arm and arm64 we need a specialization of cacheFlush() for each OS to be enabeled. Therefore the config white list. Also Mind that e.g. x86_32 has arch.x86_64 but 32bit pointers. Therefore the checks for architecture and pointer size. Finally, ios and tvos can technically use the JIT but Apple does not allow it. Therefore, it's disabled by default.
+
+qt_feature("qml-ssl" PUBLIC
+ SECTION "QML"
+ LABEL "QML SSL support"
+ PURPOSE "Provides ssl support in QML."
+ CONDITION QT_FEATURE_qml_network AND QT_FEATURE_ssl
+)
+
+# On arm and arm64 we need a specialization of cacheFlush() for each OS to be
+# enabled. Therefore the config white list. Finally, ios and tvos can
+# technically use the JIT but Apple does not allow it. Therefore, it's disabled
+# by default.
qt_feature("qml-jit" PRIVATE
SECTION "QML"
LABEL "QML just-in-time compiler"
PURPOSE "Provides a JIT for QML and JavaScript"
AUTODETECT NOT IOS AND NOT TVOS
- CONDITION ( ( ( TEST_architecture_arch STREQUAL i386 ) AND TEST_pointer_32bit AND QT_FEATURE_sse2 ) OR ( ( TEST_architecture_arch STREQUAL x86_64 ) AND TEST_pointer_64bit AND QT_FEATURE_sse2 ) OR ( ( TEST_architecture_arch STREQUAL arm ) AND TEST_pointer_32bit AND TEST_arm_fp AND TEST_arm_thumb AND ( ANDROID OR LINUX OR IOS OR TVOS OR QNX ) ) OR ( ( TEST_architecture_arch STREQUAL arm64 ) AND TEST_pointer_64bit AND TEST_arm_fp AND ( ANDROID OR LINUX OR IOS OR TVOS OR QNX OR INTEGRITY ) ) )
+ CONDITION ( ( TEST_architecture_arch STREQUAL i386 AND QT_FEATURE_sse2 ) OR
+ ( TEST_architecture_arch STREQUAL x86_64 AND QT_FEATURE_sse2 ) OR
+ ( TEST_architecture_arch STREQUAL arm AND TEST_arm_fp AND TEST_arm_thumb AND ( ANDROID OR LINUX OR IOS OR TVOS OR QNX ) ) OR
+ ( TEST_architecture_arch STREQUAL arm64 AND TEST_arm_fp AND ( ANDROID OR LINUX OR IOS OR TVOS OR QNX OR INTEGRITY ) ) )
)
# special case begin
# When doing macOS universal builds, JIT needs to be disabled for the ARM slice.
@@ -152,7 +132,7 @@ qt_feature("qml-profiler" PRIVATE
SECTION "QML"
LABEL "Command line QML Profiler"
PURPOSE "Supports retrieving QML tracing data from an application."
- CONDITION ( QT_FEATURE_commandlineparser ) AND ( QT_FEATURE_qml_debug ) AND ( QT_FEATURE_qml_network AND QT_FEATURE_localserver ) AND ( QT_FEATURE_xmlstreamwriter )
+ CONDITION ( QT_FEATURE_commandlineparser ) AND ( QT_FEATURE_qml_debug ) AND ( QT_FEATURE_qml_network AND QT_FEATURE_localserver ) AND ( QT_FEATURE_xmlstreamwriter ) AND QT_FEATURE_process
)
qt_feature("qml-preview" PRIVATE
SECTION "QML"
@@ -160,11 +140,6 @@ qt_feature("qml-preview" PRIVATE
PURPOSE "Updates QML documents in your application live as you change them on disk"
CONDITION ( QT_FEATURE_commandlineparser ) AND ( QT_FEATURE_filesystemwatcher ) AND ( QT_FEATURE_qml_network AND QT_FEATURE_localserver ) AND ( QT_FEATURE_process ) AND ( QT_FEATURE_qml_debug )
)
-qt_feature("qml-devtools" PRIVATE
- SECTION "QML"
- LABEL "QML Development Tools"
- PURPOSE "Provides the QmlDevtools library and various utilities."
-)
qt_feature("qml-xml-http-request" PRIVATE
SECTION "QML"
LABEL "QML XML http request"
@@ -211,6 +186,7 @@ qt_configure_add_summary_entry(ARGS "qml-debug")
qt_configure_add_summary_entry(ARGS "qml-jit")
qt_configure_add_summary_entry(ARGS "qml-xml-http-request")
qt_configure_add_summary_entry(ARGS "qml-locale")
+qt_configure_add_summary_entry(ARGS "qml-ssl")
qt_configure_end_summary_section() # end of "Qt QML" section
qt_configure_add_report_entry(
TYPE ERROR
diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h
index bd59c41aa7..c95eee949f 100644
--- a/src/qml/debugger/qqmlabstractprofileradapter_p.h
+++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
QT_REQUIRE_CONFIG(qml_debug);
class QQmlProfilerService;
-class Q_QML_PRIVATE_EXPORT QQmlAbstractProfilerAdapter : public QObject, public QQmlProfilerDefinitions {
+class Q_QML_EXPORT QQmlAbstractProfilerAdapter : public QObject, public QQmlProfilerDefinitions {
Q_OBJECT
public:
@@ -71,7 +71,7 @@ private:
quint64 featuresEnabled;
};
-class Q_QML_PRIVATE_EXPORT QQmlAbstractProfilerAdapterFactory : public QObject
+class Q_QML_EXPORT QQmlAbstractProfilerAdapterFactory : public QObject
{
Q_OBJECT
public:
diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp
index 19fee0c315..b9d984db1d 100644
--- a/src/qml/debugger/qqmldebug.cpp
+++ b/src/qml/debugger/qqmldebug.cpp
@@ -175,7 +175,7 @@ quintptr Q_QML_EXPORT qtDeclarativeHookData[] = {
// TypeInformationVersion, an integral value, bumped whenever private
// object sizes or member offsets that are used in Qt Creator's
// data structure "pretty printing" change.
- 3,
+ 4,
// Version of the cache data.
QV4_DATA_STRUCTURE_VERSION
diff --git a/src/qml/debugger/qqmldebugconnector_p.h b/src/qml/debugger/qqmldebugconnector_p.h
index 39d042202d..d021020bf4 100644
--- a/src/qml/debugger/qqmldebugconnector_p.h
+++ b/src/qml/debugger/qqmldebugconnector_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
#if !QT_CONFIG(qml_debug)
-class Q_QML_PRIVATE_EXPORT QQmlDebugConnector
+class Q_QML_EXPORT QQmlDebugConnector
{
virtual ~QQmlDebugConnector() = default; // don't break 'override' on ~QQmlDebugServer
public:
@@ -50,7 +50,7 @@ public:
#else
class QQmlDebugService;
-class Q_QML_PRIVATE_EXPORT QQmlDebugConnector : public QObject
+class Q_QML_EXPORT QQmlDebugConnector : public QObject
{
Q_OBJECT
public:
@@ -87,7 +87,7 @@ protected:
static int s_dataStreamVersion;
};
-class Q_QML_PRIVATE_EXPORT QQmlDebugConnectorFactory : public QObject {
+class Q_QML_EXPORT QQmlDebugConnectorFactory : public QObject {
Q_OBJECT
public:
virtual QQmlDebugConnector *create(const QString &key) = 0;
diff --git a/src/qml/debugger/qqmldebugserver_p.h b/src/qml/debugger/qqmldebugserver_p.h
index cba4be82b0..04e8b95f67 100644
--- a/src/qml/debugger/qqmldebugserver_p.h
+++ b/src/qml/debugger/qqmldebugserver_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlDebugServer : public QQmlDebugConnector
+class Q_QML_EXPORT QQmlDebugServer : public QQmlDebugConnector
{
Q_OBJECT
public:
diff --git a/src/qml/debugger/qqmldebugserverconnection_p.h b/src/qml/debugger/qqmldebugserverconnection_p.h
index 4a12583d19..c7ec68727c 100644
--- a/src/qml/debugger/qqmldebugserverconnection_p.h
+++ b/src/qml/debugger/qqmldebugserverconnection_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
class QQmlDebugServer;
-class Q_QML_PRIVATE_EXPORT QQmlDebugServerConnection : public QObject
+class Q_QML_EXPORT QQmlDebugServerConnection : public QObject
{
Q_OBJECT
public:
@@ -37,7 +37,7 @@ public:
virtual void flush() = 0;
};
-class Q_QML_PRIVATE_EXPORT QQmlDebugServerConnectionFactory : public QObject
+class Q_QML_EXPORT QQmlDebugServerConnectionFactory : public QObject
{
Q_OBJECT
public:
diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp
index 034aa55ce7..56cffc169c 100644
--- a/src/qml/debugger/qqmldebugservice.cpp
+++ b/src/qml/debugger/qqmldebugservice.cpp
@@ -103,8 +103,8 @@ Q_GLOBAL_STATIC(ObjectReferenceHash, objectReferenceHash)
void ObjectReferenceHash::remove(QObject *obj)
{
- QHash<QObject *, int>::Iterator iter = objects.find(obj);
- if (iter != objects.end()) {
+ const auto iter = objects.constFind(obj);
+ if (iter != objects.cend()) {
ids.remove(iter.value());
objects.erase(iter);
}
@@ -120,9 +120,9 @@ int QQmlDebugService::idForObject(QObject *object)
return -1;
ObjectReferenceHash *hash = objectReferenceHash();
- QHash<QObject *, int>::Iterator iter = hash->objects.find(object);
+ auto iter = hash->objects.constFind(object);
- if (iter == hash->objects.end()) {
+ if (iter == hash->objects.cend()) {
int id = hash->nextId++;
hash->ids.insert(id, object);
iter = hash->objects.insert(object, id);
diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h
index b92c61146c..5970e89038 100644
--- a/src/qml/debugger/qqmldebugservice_p.h
+++ b/src/qml/debugger/qqmldebugservice_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
class QJSEngine;
class QQmlDebugServicePrivate;
-class Q_QML_PRIVATE_EXPORT QQmlDebugService : public QObject
+class Q_QML_EXPORT QQmlDebugService : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlDebugService)
diff --git a/src/qml/debugger/qqmldebugservicefactory_p.h b/src/qml/debugger/qqmldebugservicefactory_p.h
index 6a1e85dad6..e9e4069efb 100644
--- a/src/qml/debugger/qqmldebugservicefactory_p.h
+++ b/src/qml/debugger/qqmldebugservicefactory_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlDebugServiceFactory : public QObject
+class Q_QML_EXPORT QQmlDebugServiceFactory : public QObject
{
Q_OBJECT
public:
diff --git a/src/qml/debugger/qqmldebugserviceinterfaces.cpp b/src/qml/debugger/qqmldebugserviceinterfaces.cpp
index 9ef5058803..db1ec2db5e 100644
--- a/src/qml/debugger/qqmldebugserviceinterfaces.cpp
+++ b/src/qml/debugger/qqmldebugserviceinterfaces.cpp
@@ -54,7 +54,7 @@ const TranslationBindingInformation TranslationBindingInformation::create(
QQmlTranslation translation;
if (binding->type() == QV4::CompiledData::Binding::Type_TranslationById) {
const QV4::CompiledData::TranslationData data =
- compilationUnit->data->translations()[binding->value.translationDataIndex];
+ compilationUnit->unitData()->translations()[binding->value.translationDataIndex];
const QString id = compilationUnit->stringAt(data.stringIndex);
const int n = data.number;
@@ -63,17 +63,20 @@ const TranslationBindingInformation TranslationBindingInformation::create(
Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Translation);
const QV4::CompiledData::TranslationData data =
- compilationUnit->data->translations()[binding->value.translationDataIndex];
+ compilationUnit->unitData()->translations()[binding->value.translationDataIndex];
const QString text = compilationUnit->stringAt(data.stringIndex);
const QString comment = compilationUnit->stringAt(data.commentIndex);
- const QString context = compilationUnit->stringAt(data.contextIndex);
+ const bool hasContext
+ = data.contextIndex != QV4::CompiledData::TranslationData::NoContextIndex;
const int n = data.number;
translation = QQmlTranslation(
- QQmlTranslation::QsTrData(context.isEmpty()
- ? QQmlTranslation::contextFromQmlFilename(
- compilationUnit->fileName())
- : context, text, comment, n));
+ QQmlTranslation::QsTrData(
+ hasContext
+ ? compilationUnit->stringAt(data.contextIndex)
+ : QQmlTranslation::contextFromQmlFilename(
+ compilationUnit->fileName()),
+ text, comment, n));
}
return { compilationUnit,
diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h
index 7c0b736463..cd9022b497 100644
--- a/src/qml/debugger/qqmldebugserviceinterfaces_p.h
+++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h
@@ -78,7 +78,7 @@ public:
#else
-class Q_QML_PRIVATE_EXPORT QV4DebugService : public QQmlDebugService
+class Q_QML_EXPORT QV4DebugService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -96,7 +96,7 @@ protected:
};
class QQmlAbstractProfilerAdapter;
-class Q_QML_PRIVATE_EXPORT QQmlProfilerService : public QQmlDebugService
+class Q_QML_EXPORT QQmlProfilerService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -120,7 +120,7 @@ protected:
QQmlDebugService(s_key, version, parent) {}
};
-class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : public QQmlDebugService
+class Q_QML_EXPORT QQmlEngineDebugService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -160,7 +160,7 @@ struct TranslationBindingInformation
quint32 column;
};
-class Q_QML_PRIVATE_EXPORT QQmlDebugTranslationService : public QQmlDebugService
+class Q_QML_EXPORT QQmlDebugTranslationService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -178,7 +178,7 @@ protected:
};
#endif //QT_CONFIG(translation)
-class Q_QML_PRIVATE_EXPORT QQmlInspectorService : public QQmlDebugService
+class Q_QML_EXPORT QQmlInspectorService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -197,7 +197,7 @@ protected:
QQmlDebugService(s_key, version, parent) {}
};
-class Q_QML_PRIVATE_EXPORT QDebugMessageService : public QQmlDebugService
+class Q_QML_EXPORT QDebugMessageService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -214,7 +214,7 @@ protected:
QQmlDebugService(s_key, version, parent) {}
};
-class Q_QML_PRIVATE_EXPORT QQmlEngineControlService : public QQmlDebugService
+class Q_QML_EXPORT QQmlEngineControlService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -230,7 +230,7 @@ protected:
};
-class Q_QML_PRIVATE_EXPORT QQmlNativeDebugService : public QQmlDebugService
+class Q_QML_EXPORT QQmlNativeDebugService : public QQmlDebugService
{
Q_OBJECT
public:
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index 8ea5c459e4..2cf0e85760 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -112,7 +112,7 @@ struct Q_AUTOTEST_EXPORT QQmlProfilerData : public QQmlProfilerDefinitions
Q_DECLARE_TYPEINFO(QQmlProfilerData, Q_RELOCATABLE_TYPE);
-class Q_QML_PRIVATE_EXPORT QQmlProfiler : public QObject, public QQmlProfilerDefinitions {
+class Q_QML_EXPORT QQmlProfiler : public QObject, public QQmlProfilerDefinitions {
Q_OBJECT
public:
diff --git a/src/qml/doc/images/extending-qml-advanced-word-cloud.png b/src/qml/doc/images/extending-qml-advanced-word-cloud.png
new file mode 100644
index 0000000000..b479b4f793
--- /dev/null
+++ b/src/qml/doc/images/extending-qml-advanced-word-cloud.png
Binary files differ
diff --git a/src/qml/doc/qtqml.qdocconf b/src/qml/doc/qtqml.qdocconf
index 2a7bcc7f9e..9c1632e537 100644
--- a/src/qml/doc/qtqml.qdocconf
+++ b/src/qml/doc/qtqml.qdocconf
@@ -33,6 +33,7 @@ tagfile = ../../../doc/qtqml/qtqml.tags
depends += \
qtcore \
+ qtnetwork \
qtqmlmodels \
qtqmlworkerscript \
qtgui \
@@ -62,9 +63,12 @@ manifestmeta.thumbnail.names += "QtQml/Chapter 4*" \
navigation.landingpage = "Qt QML"
navigation.cppclassespage = "Qt QML C++ Classes"
navigation.qmltypespage = "Qt QML QML Types"
+# Auto-generate navigation linking based on the \list structures on the following:
+navigation.toctitles = "Qt Quick Compiler"
+navigation.toctitles.inclusive = true
# suppress qdoc warnings for \instantiates entries
spurious += "C\\+\\+ class .*\\\\instantiates .*"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt b/src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt
index e0286b3408..7ec50e425f 100644
--- a/src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt
+++ b/src/qml/doc/snippets/cmake/qt_target_qml_sources/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.19)
project(qt_target_qml_sources_snippet)
diff --git a/src/qml/doc/snippets/code/backend/main.qml b/src/qml/doc/snippets/code/backend/main.qml
index bae7e99904..7bad89374f 100644
--- a/src/qml/doc/snippets/code/backend/main.qml
+++ b/src/qml/doc/snippets/code/backend/main.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [main_qml]
-import QtQuick 2.6
-import QtQuick.Controls 2.0
+import QtQuick
+import QtQuick.Controls
//![import]
import io.qt.examples.backend 1.0
//![import]
diff --git a/src/qml/doc/snippets/code/doc_src_qtqml.cmake b/src/qml/doc/snippets/code/doc_src_qtqml.cmake
index e22aa53650..1d9dccc49a 100644
--- a/src/qml/doc/snippets/code/doc_src_qtqml.cmake
+++ b/src/qml/doc/snippets/code/doc_src_qtqml.cmake
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#! [0]
find_package(Qt6 REQUIRED COMPONENTS Qml)
diff --git a/src/qml/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp b/src/qml/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp
new file mode 100644
index 0000000000..b200261035
--- /dev/null
+++ b/src/qml/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [0]
+class CachingNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
+{
+public:
+
+ inline QNetworkAccessManager *create(QObject *parent) override
+ {
+ QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager(parent);
+ QNetworkDiskCache *diskCache = new QNetworkDiskCache(parent);
+ diskCache->setCacheDirectory("requestCache");
+ networkAccessManager->setCache(diskCache);
+
+ return networkAccessManager;
+ }
+};
+//! [0]
+
+//! [1]
+CachingNetworkAccessManagerFactory networkManagerFactory;
+engine->setNetworkAccessManagerFactory(&networkManagerFactory);
+//! [1]
diff --git a/src/qml/doc/snippets/qml/Button.qml b/src/qml/doc/snippets/qml/Button.qml
index 1aba2cd39f..aacce0c37a 100644
--- a/src/qml/doc/snippets/qml/Button.qml
+++ b/src/qml/doc/snippets/qml/Button.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
Rectangle {
diff --git a/src/qml/doc/snippets/qml/CMakeLists.txt b/src/qml/doc/snippets/qml/CMakeLists.txt
index 7c7ac116c3..717f153e7d 100644
--- a/src/qml/doc/snippets/qml/CMakeLists.txt
+++ b/src/qml/doc/snippets/qml/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
qt_add_library(extra_module STATIC)
qt_add_qml_module(extra_module
diff --git a/src/qml/doc/snippets/qml/DynamicText.qml b/src/qml/doc/snippets/qml/DynamicText.qml
index fd54fc6ced..93b614a628 100644
--- a/src/qml/doc/snippets/qml/DynamicText.qml
+++ b/src/qml/doc/snippets/qml/DynamicText.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Text {
id: textElement
diff --git a/src/qml/doc/snippets/qml/SelfDestroyingRect.qml b/src/qml/doc/snippets/qml/SelfDestroyingRect.qml
index e788a62642..b31c0857ba 100644
--- a/src/qml/doc/snippets/qml/SelfDestroyingRect.qml
+++ b/src/qml/doc/snippets/qml/SelfDestroyingRect.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
diff --git a/src/qml/doc/snippets/qml/Sprite.qml b/src/qml/doc/snippets/qml/Sprite.qml
index 5d3a5bf804..fc0032c4f7 100644
--- a/src/qml/doc/snippets/qml/Sprite.qml
+++ b/src/qml/doc/snippets/qml/Sprite.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle { width: 80; height: 50; color: "red" }
//![0]
diff --git a/src/qml/doc/snippets/qml/XHRForm.qml b/src/qml/doc/snippets/qml/XHRForm.qml
new file mode 100644
index 0000000000..90c918ddbb
--- /dev/null
+++ b/src/qml/doc/snippets/qml/XHRForm.qml
@@ -0,0 +1,83 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//![0]
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import "request.js" as XHR
+
+ApplicationWindow {
+ width: 640
+ height: 640
+ visible: true
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ RowLayout {
+ Layout.fillWidth: true
+
+ TextField {
+ id: urlTextField
+ text: "https://www.example.com/index.html"
+ Layout.fillWidth: true
+ }
+ Button {
+ text: qsTr("Send!")
+ onClicked: XHR.sendRequest(urlTextField.text, function(response) {
+ statusTextField.text = response.status;
+ let isPlainText = response.contentType.length === 0
+
+ contentTypeTextField.text = isPlainText ? "text" : response.contentType;
+
+ if (isPlainText)
+ contentTextArea.text = response.content;
+ });
+ }
+ }
+
+ GridLayout {
+ columns: 2
+
+ Layout.fillWidth: true
+
+ Label {
+ text: qsTr("Status code")
+
+ Layout.fillWidth: true
+ }
+ Label {
+ text: qsTr("Response type")
+
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: statusTextField
+
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: contentTypeTextField
+
+ Layout.fillWidth: true
+ }
+ }
+ Flickable {
+ clip: true
+ contentWidth: contentTextArea.width
+ contentHeight: contentTextArea.height
+ Text {
+ id: contentTextArea
+ }
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ ScrollBar.vertical: ScrollBar {}
+ ScrollBar.horizontal: ScrollBar {}
+ }
+ }
+}
+
+//![0]
+
diff --git a/src/qml/doc/snippets/qml/application.qml b/src/qml/doc/snippets/qml/application.qml
index 79563fab46..a7eb91c37c 100644
--- a/src/qml/doc/snippets/qml/application.qml
+++ b/src/qml/doc/snippets/qml/application.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 300; height: 55
diff --git a/src/qml/doc/snippets/qml/comments.qml b/src/qml/doc/snippets/qml/comments.qml
index 2cba0552b8..aa01bf110b 100644
--- a/src/qml/doc/snippets/qml/comments.qml
+++ b/src/qml/doc/snippets/qml/comments.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Text {
diff --git a/src/qml/doc/snippets/qml/component.qml b/src/qml/doc/snippets/qml/component.qml
index e6229aee3e..c166d87fa5 100644
--- a/src/qml/doc/snippets/qml/component.qml
+++ b/src/qml/doc/snippets/qml/component.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 100; height: 100
diff --git a/src/qml/doc/snippets/qml/component/MyItem.qml b/src/qml/doc/snippets/qml/component/MyItem.qml
index 6d49690cf1..7a3290ffa6 100644
--- a/src/qml/doc/snippets/qml/component/MyItem.qml
+++ b/src/qml/doc/snippets/qml/component/MyItem.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/qml/doc/snippets/qml/component/main.qml b/src/qml/doc/snippets/qml/component/main.qml
index b9bf933362..a76471dce2 100644
--- a/src/qml/doc/snippets/qml/component/main.qml
+++ b/src/qml/doc/snippets/qml/component/main.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
ListView {
diff --git a/src/qml/doc/snippets/qml/createComponent-simple.qml b/src/qml/doc/snippets/qml/createComponent-simple.qml
index 6e9c188734..2adc1c49f2 100644
--- a/src/qml/doc/snippets/qml/createComponent-simple.qml
+++ b/src/qml/doc/snippets/qml/createComponent-simple.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
id: container
diff --git a/src/qml/doc/snippets/qml/createComponent.qml b/src/qml/doc/snippets/qml/createComponent.qml
index 292f51f9eb..f71526fc70 100644
--- a/src/qml/doc/snippets/qml/createComponent.qml
+++ b/src/qml/doc/snippets/qml/createComponent.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
import "componentCreation.js" as MyScript
Rectangle {
diff --git a/src/qml/doc/snippets/qml/createQmlObject.qml b/src/qml/doc/snippets/qml/createQmlObject.qml
index 77137328c3..32d9e8cff2 100644
--- a/src/qml/doc/snippets/qml/createQmlObject.qml
+++ b/src/qml/doc/snippets/qml/createQmlObject.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: parentItem
@@ -12,7 +12,7 @@ Rectangle {
function createIt() {
//![0]
const newObject = Qt.createQmlObject(`
- import QtQuick 2.0
+ import QtQuick
Rectangle {
color: "red"
diff --git a/src/qml/doc/snippets/qml/dynamicObjects-destroy.qml b/src/qml/doc/snippets/qml/dynamicObjects-destroy.qml
index bac07b0178..91db58f7bb 100644
--- a/src/qml/doc/snippets/qml/dynamicObjects-destroy.qml
+++ b/src/qml/doc/snippets/qml/dynamicObjects-destroy.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
id: container
diff --git a/src/qml/doc/snippets/qml/events.qml b/src/qml/doc/snippets/qml/events.qml
index 81df6ded1b..3e5171102a 100644
--- a/src/qml/doc/snippets/qml/events.qml
+++ b/src/qml/doc/snippets/qml/events.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![document]
-import QtQuick 2.0
+import QtQuick
//![parent begin]
Rectangle {
diff --git a/src/qml/doc/snippets/qml/exposing-state/RequiredProperties.qml b/src/qml/doc/snippets/qml/exposing-state/RequiredProperties.qml
new file mode 100644
index 0000000000..18425930de
--- /dev/null
+++ b/src/qml/doc/snippets/qml/exposing-state/RequiredProperties.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+pragma ComponentBehavior: Bound
+
+import QtQuick
+
+Window {
+ id: root
+ visible: true
+
+ required property int thing
+
+ Text {
+ anchors.fill: parent
+ text: "The thing is " + root.thing
+ }
+
+ component Inner: QtObject {
+ objectName: "I can see " + root.thing + " because I'm bound."
+ }
+}
+//![0]
diff --git a/src/qml/doc/snippets/qml/exposing-state/createWithInitialProperties.cpp b/src/qml/doc/snippets/qml/exposing-state/createWithInitialProperties.cpp
new file mode 100644
index 0000000000..1e5f1859f9
--- /dev/null
+++ b/src/qml/doc/snippets/qml/exposing-state/createWithInitialProperties.cpp
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtGui/qguiapplication.h>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+//![0]
+ QQmlEngine engine;
+
+ QQmlComponent component(&engine, "MyModule", "RequiredProperties");
+ QScopedPointer<QObject> o(component.createWithInitialProperties({
+ {"thing", 11}
+ }));
+//![0]
+
+ return app.exec();
+}
diff --git a/src/qml/doc/snippets/qml/exposing-state/singleton.h b/src/qml/doc/snippets/qml/exposing-state/singleton.h
new file mode 100644
index 0000000000..e600531883
--- /dev/null
+++ b/src/qml/doc/snippets/qml/exposing-state/singleton.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SINGLETON_H
+#define SINGLETON_H
+
+#include <QtQml/qobject.h>
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlengine.h>
+
+//![0]
+// Singleton.h
+class Singleton : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int thing READ thing WRITE setThing NOTIFY thingChanged FINAL)
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ Singleton(QObject *parent = nullptr) : QObject(parent) {}
+
+ int thing() const { return m_value; }
+ void setThing(int v)
+ {
+ if (v != m_value) {
+ m_value = v;
+ emit thingChanged();
+ }
+ }
+
+signals:
+ void thingChanged();
+
+private:
+ int m_value = 12;
+};
+//![0]
+
+inline void setTheThing(QQmlEngine *engine)
+{
+//![1]
+ Singleton *singleton
+ = engine->singletonInstance<Singleton *>("MyModule", "Singleton");
+ singleton->setThing(77);
+//![1]
+}
+
+#endif
diff --git a/src/qml/doc/snippets/qml/exposing-state/useSingleton.qml b/src/qml/doc/snippets/qml/exposing-state/useSingleton.qml
new file mode 100644
index 0000000000..a9021a9241
--- /dev/null
+++ b/src/qml/doc/snippets/qml/exposing-state/useSingleton.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQml
+
+QtObject {
+ objectName: "The thing is " + Singleton.thing
+}
+//![0]
diff --git a/src/qml/doc/snippets/qml/imports/installed-module.qml b/src/qml/doc/snippets/qml/imports/installed-module.qml
index 6fa2e7d953..fa8534b4f8 100644
--- a/src/qml/doc/snippets/qml/imports/installed-module.qml
+++ b/src/qml/doc/snippets/qml/imports/installed-module.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [imports]
-import QtQuick 2.0
+import QtQuick
import com.nokia.qml.mymodule 1.0
//! [imports]
diff --git a/src/qml/doc/snippets/qml/imports/named-imports.qml b/src/qml/doc/snippets/qml/imports/named-imports.qml
deleted file mode 100644
index 075fd9b2f4..0000000000
--- a/src/qml/doc/snippets/qml/imports/named-imports.qml
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-//! [imports]
-import QtQuick 2.0 as QtLibrary
-import "../MyComponents" as MyComponents
-import com.nokia.qml.mymodule 1.0 as MyModule
-//! [imports]
-
-Item {
- //! [imported items]
- QtLibrary.Rectangle {
- // ...
- }
-
- MyComponents.Slider {
- // ...
- }
-
- MyModule.SomeComponent {
- // ...
- }
- //! [imported items]
-}
diff --git a/src/qml/doc/snippets/qml/imports/qtquick-1.0.qml b/src/qml/doc/snippets/qml/imports/qtquick-1.0.qml
index e1a4e0eb0a..7a1d3d85a7 100644
--- a/src/qml/doc/snippets/qml/imports/qtquick-1.0.qml
+++ b/src/qml/doc/snippets/qml/imports/qtquick-1.0.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [import]
-import QtQuick 2.0
+import QtQuick
//! [import]
Item {
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/connectjs.qml b/src/qml/doc/snippets/qml/integrating-javascript/connectjs.qml
index da44b63aef..a83f9466d8 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/connectjs.qml
+++ b/src/qml/doc/snippets/qml/integrating-javascript/connectjs.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
import "script.js" as MyScript
Item {
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml b/src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml
index 31558a5ee2..84c35c4900 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml
+++ b/src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
import "script.mjs" as MyScript
Item {
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFive.qml b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFive.qml
index 3cec18c87f..d72de68a23 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFive.qml
+++ b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFive.qml
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// exampleFive.qml
-import QtQuick 2.0
-import Qt.example 1.0
+import QtQuick
+import Qt.example
import "exampleFour.js" as ExampleFourJs // use factory from example four
QtObject {
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFour.qml b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFour.qml
index 80bdc35d3a..2002ed394b 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFour.qml
+++ b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleFour.qml
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// exampleFour.qml
-import QtQuick 2.0
-import Qt.example 1.0
+import QtQuick
+import Qt.example
import "exampleFour.js" as ExampleFourJs
QtObject {
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleOne.qml b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleOne.qml
index 9d52e0fe40..76b161fe26 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleOne.qml
+++ b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleOne.qml
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// exampleOne.qml
-import QtQuick 2.0
-import Qt.example 1.0
+import QtQuick
+import Qt.example
QtObject {
property AvatarExample a;
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleThree.qml b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleThree.qml
index daf1a1e432..286ff8aaef 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleThree.qml
+++ b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleThree.qml
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// exampleThree.qml
-import QtQuick 2.0
-import Qt.example 1.0
+import QtQuick
+import Qt.example
import "exampleThree.js" as ExampleThreeJs
QtObject {
diff --git a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleTwo.qml b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleTwo.qml
index 1aac883c1a..dcfe6e4cd0 100644
--- a/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleTwo.qml
+++ b/src/qml/doc/snippets/qml/integrating-javascript/scarceresources/exampleTwo.qml
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// exampleTwo.qml
-import QtQuick 2.0
-import Qt.example 1.0
+import QtQuick
+import Qt.example
QtObject {
property AvatarExample a;
diff --git a/src/qml/doc/snippets/qml/properties.qml b/src/qml/doc/snippets/qml/properties.qml
index 3134a4c2e5..39f508e424 100644
--- a/src/qml/doc/snippets/qml/properties.qml
+++ b/src/qml/doc/snippets/qml/properties.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
Rectangle {
@@ -229,20 +229,9 @@ Button {
//! [image alias]
Item {
-id: widget
-
-//! [alias complete]
-property alias widgetLabel: label
-
-//will generate an error
-//widgetLabel.text: "Initial text"
-
-//will generate an error
-//property alias widgetLabelText: widgetLabel.text
-
-Component.onCompleted: widgetLabel.text = "Alias completed Initialization"
-//! [alias complete]
+ id: widget
+ property alias widgetLabel: label
Text {id: label}
}
diff --git a/src/qml/doc/snippets/qml/qml-documents/A.qml b/src/qml/doc/snippets/qml/qml-documents/A.qml
index 924bea9639..de9894788b 100644
--- a/src/qml/doc/snippets/qml/qml-documents/A.qml
+++ b/src/qml/doc/snippets/qml/qml-documents/A.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
// A.qml
-import QtQuick 2.15
+import QtQuick
Item {
id: root
diff --git a/src/qml/doc/snippets/qml/qml-documents/B.qml b/src/qml/doc/snippets/qml/qml-documents/B.qml
index 5f354be5e1..27fce37d9f 100644
--- a/src/qml/doc/snippets/qml/qml-documents/B.qml
+++ b/src/qml/doc/snippets/qml/qml-documents/B.qml
@@ -3,7 +3,7 @@
//! [document]
// B.qml
-import QtQuick 2.15
+import QtQuick
Item {
A.MyInlineComponent {}
diff --git a/src/qml/doc/snippets/qml/qml-documents/Images.qml b/src/qml/doc/snippets/qml/qml-documents/Images.qml
index 77f83f46ec..613e223b34 100644
--- a/src/qml/doc/snippets/qml/qml-documents/Images.qml
+++ b/src/qml/doc/snippets/qml/qml-documents/Images.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
// Images.qml
-import QtQuick 2.15
+import QtQuick
Item {
component LabeledImage: Column {
diff --git a/src/qml/doc/snippets/qml/qml-documents/LabeledImageBox.qml b/src/qml/doc/snippets/qml/qml-documents/LabeledImageBox.qml
index 92bd55799e..bfc5a7b45e 100644
--- a/src/qml/doc/snippets/qml/qml-documents/LabeledImageBox.qml
+++ b/src/qml/doc/snippets/qml/qml-documents/LabeledImageBox.qml
@@ -3,7 +3,7 @@
//! [document]
// LabeledImageBox.qml
-import QtQuick 2.15
+import QtQuick
Rectangle {
property alias caption: image.caption
diff --git a/src/qml/doc/snippets/qml/qml-documents/inline-component.qml b/src/qml/doc/snippets/qml/qml-documents/inline-component.qml
index 72e7487e73..32286790a5 100644
--- a/src/qml/doc/snippets/qml/qml-documents/inline-component.qml
+++ b/src/qml/doc/snippets/qml/qml-documents/inline-component.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 240; height: 320;
diff --git a/src/qml/doc/snippets/qml/qml-documents/inline-text-component.qml b/src/qml/doc/snippets/qml/qml-documents/inline-text-component.qml
index 2cc440bc6d..096d060583 100644
--- a/src/qml/doc/snippets/qml/qml-documents/inline-text-component.qml
+++ b/src/qml/doc/snippets/qml/qml-documents/inline-text-component.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 240; height: 320;
diff --git a/src/qml/doc/snippets/qml/qml-documents/non-trivial.qml b/src/qml/doc/snippets/qml/qml-documents/non-trivial.qml
index 5f301130d9..9fccc3d286 100644
--- a/src/qml/doc/snippets/qml/qml-documents/non-trivial.qml
+++ b/src/qml/doc/snippets/qml/qml-documents/non-trivial.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 240; height: 320;
diff --git a/src/qml/doc/snippets/qml/qml-documents/qmldocuments.qml b/src/qml/doc/snippets/qml/qml-documents/qmldocuments.qml
index 1809cc7f99..b71ff6cad8 100644
--- a/src/qml/doc/snippets/qml/qml-documents/qmldocuments.qml
+++ b/src/qml/doc/snippets/qml/qml-documents/qmldocuments.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
property alias text: textItem.text
diff --git a/src/qml/doc/snippets/qml/qsTr.qml b/src/qml/doc/snippets/qml/qsTr.qml
index 4b1f358bd8..c1a48c9d32 100644
--- a/src/qml/doc/snippets/qml/qsTr.qml
+++ b/src/qml/doc/snippets/qml/qsTr.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Text { text: qsTr("hello") }
diff --git a/src/qml/doc/snippets/qml/qsTrId.1.qml b/src/qml/doc/snippets/qml/qsTrId.1.qml
index 8da6023c4d..4989adad25 100644
--- a/src/qml/doc/snippets/qml/qsTrId.1.qml
+++ b/src/qml/doc/snippets/qml/qsTrId.1.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Text {
diff --git a/src/qml/doc/snippets/qml/qsTrId.qml b/src/qml/doc/snippets/qml/qsTrId.qml
index b6fd569fd5..b407896943 100644
--- a/src/qml/doc/snippets/qml/qsTrId.qml
+++ b/src/qml/doc/snippets/qml/qsTrId.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Text { text: qsTrId("hello_id") }
diff --git a/src/qml/doc/snippets/qml/qsTranslate.qml b/src/qml/doc/snippets/qml/qsTranslate.qml
index 2634ec9834..fe0936b1b7 100644
--- a/src/qml/doc/snippets/qml/qsTranslate.qml
+++ b/src/qml/doc/snippets/qml/qsTranslate.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Text { text: qsTranslate("CustomContext", "hello") }
diff --git a/src/qml/doc/snippets/qml/qtBinding.1.qml b/src/qml/doc/snippets/qml/qtBinding.1.qml
index 24d71a847b..5ea94d36ab 100644
--- a/src/qml/doc/snippets/qml/qtBinding.1.qml
+++ b/src/qml/doc/snippets/qml/qtBinding.1.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/qml/doc/snippets/qml/qtBinding.2.qml b/src/qml/doc/snippets/qml/qtBinding.2.qml
index af76ee1dd6..57151f341f 100644
--- a/src/qml/doc/snippets/qml/qtBinding.2.qml
+++ b/src/qml/doc/snippets/qml/qtBinding.2.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/qml/doc/snippets/qml/qtBinding.3.qml b/src/qml/doc/snippets/qml/qtBinding.3.qml
index bb1964695f..8ec24a7c7f 100644
--- a/src/qml/doc/snippets/qml/qtBinding.3.qml
+++ b/src/qml/doc/snippets/qml/qtBinding.3.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/qml/doc/snippets/qml/qtBinding.4.qml b/src/qml/doc/snippets/qml/qtBinding.4.qml
index 640428573f..08f0953314 100644
--- a/src/qml/doc/snippets/qml/qtBinding.4.qml
+++ b/src/qml/doc/snippets/qml/qtBinding.4.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/qml/doc/snippets/qml/qtLater.qml b/src/qml/doc/snippets/qml/qtLater.qml
index 09970b10b1..cb999b09c1 100644
--- a/src/qml/doc/snippets/qml/qtLater.qml
+++ b/src/qml/doc/snippets/qml/qtLater.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 480
diff --git a/src/qml/doc/snippets/qml/qtTrIdNoOp.qml b/src/qml/doc/snippets/qml/qtTrIdNoOp.qml
index 1a8df6ca1e..14d802c776 100644
--- a/src/qml/doc/snippets/qml/qtTrIdNoOp.qml
+++ b/src/qml/doc/snippets/qml/qtTrIdNoOp.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/qml/doc/snippets/qml/qtTrNoOp.qml b/src/qml/doc/snippets/qml/qtTrNoOp.qml
index 266b76214c..550619afd7 100644
--- a/src/qml/doc/snippets/qml/qtTrNoOp.qml
+++ b/src/qml/doc/snippets/qml/qtTrNoOp.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/qml/doc/snippets/qml/qtTranslateNoOp.qml b/src/qml/doc/snippets/qml/qtTranslateNoOp.qml
index 28fdc86d55..46d5dc05da 100644
--- a/src/qml/doc/snippets/qml/qtTranslateNoOp.qml
+++ b/src/qml/doc/snippets/qml/qtTranslateNoOp.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/qml/doc/snippets/qml/qtbinding/context-advanced/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/context-advanced/MyItem.qml
index 0d44a69b14..486b3bba42 100644
--- a/src/qml/doc/snippets/qml/qtbinding/context-advanced/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/context-advanced/MyItem.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// MyItem.qml
-import QtQuick 2.0
+import QtQuick
Text { text: applicationData.getCurrentDateTime() }
//![0]
diff --git a/src/qml/doc/snippets/qml/qtbinding/context-advanced/connections.qml b/src/qml/doc/snippets/qml/qtbinding/context-advanced/connections.qml
index ac2575ade1..f769e3246c 100644
--- a/src/qml/doc/snippets/qml/qtbinding/context-advanced/connections.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/context-advanced/connections.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Text {
diff --git a/src/qml/doc/snippets/qml/qtbinding/context/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/context/MyItem.qml
index fd5f6e65f3..c03a5d26b7 100644
--- a/src/qml/doc/snippets/qml/qtbinding/context/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/context/MyItem.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// MyItem.qml
-import QtQuick 2.0
+import QtQuick
Text { text: currentDateTime }
//![0]
diff --git a/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml
index f66e8d582c..b26522f3f0 100644
--- a/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// MyItem.qml
-import QtQuick 2.0
+import QtQuick
Item {
function myQmlFunction(msg: string) : string {
diff --git a/src/qml/doc/snippets/qml/qtbinding/loading/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/loading/MyItem.qml
index eff81c8c0f..a2403ce9d2 100644
--- a/src/qml/doc/snippets/qml/qtbinding/loading/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/loading/MyItem.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![start]
-import QtQuick 2.0
+import QtQuick
Item {
width: 100; height: 100
diff --git a/src/qml/doc/snippets/qml/qtbinding/properties-qml/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/properties-qml/MyItem.qml
index f0e6fe77f5..ff626d3837 100644
--- a/src/qml/doc/snippets/qml/qtbinding/properties-qml/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/properties-qml/MyItem.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// MyItem.qml
-import QtQuick 2.0
+import QtQuick
Item {
property int someNumber: 100
diff --git a/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml
index 064dcd35d0..9d141be179 100644
--- a/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// MyItem.qml
-import QtQuick 2.0
+import QtQuick
Item {
id: item
diff --git a/src/qml/doc/snippets/qml/qtbinding/variantlistmap/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/variantlistmap/MyItem.qml
index 2379f43d93..e50ae519b0 100644
--- a/src/qml/doc/snippets/qml/qtbinding/variantlistmap/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/variantlistmap/MyItem.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
// MyItem.qml
diff --git a/src/qml/doc/snippets/qml/qtobject.qml b/src/qml/doc/snippets/qml/qtobject.qml
index 46bc1d6469..7707123511 100644
--- a/src/qml/doc/snippets/qml/qtobject.qml
+++ b/src/qml/doc/snippets/qml/qtobject.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
QtObject {
diff --git a/src/qml/doc/snippets/qml/reusablecomponents/Button.qml b/src/qml/doc/snippets/qml/reusablecomponents/Button.qml
index 254d41fc69..5554befa3e 100644
--- a/src/qml/doc/snippets/qml/reusablecomponents/Button.qml
+++ b/src/qml/doc/snippets/qml/reusablecomponents/Button.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
//contents of Button.qml
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
Rectangle {
diff --git a/src/qml/doc/snippets/qml/reusablecomponents/application.qml b/src/qml/doc/snippets/qml/reusablecomponents/application.qml
index 69d53cab8f..7c40bda2b6 100644
--- a/src/qml/doc/snippets/qml/reusablecomponents/application.qml
+++ b/src/qml/doc/snippets/qml/reusablecomponents/application.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 175; height: 350
diff --git a/src/qml/doc/snippets/qml/reusablecomponents/component.qml b/src/qml/doc/snippets/qml/reusablecomponents/component.qml
index 9a5662d4e0..ba70f0b9e2 100644
--- a/src/qml/doc/snippets/qml/reusablecomponents/component.qml
+++ b/src/qml/doc/snippets/qml/reusablecomponents/component.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
Rectangle {
diff --git a/src/qml/doc/snippets/qml/reusablecomponents/focusbutton.qml b/src/qml/doc/snippets/qml/reusablecomponents/focusbutton.qml
index 46af8879af..be1feabe67 100644
--- a/src/qml/doc/snippets/qml/reusablecomponents/focusbutton.qml
+++ b/src/qml/doc/snippets/qml/reusablecomponents/focusbutton.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
//contents of focusbutton.qml
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
FocusScope {
diff --git a/src/qml/doc/snippets/qml/xmlhttprequest.js b/src/qml/doc/snippets/qml/xmlhttprequest.js
new file mode 100644
index 0000000000..f395a15d8d
--- /dev/null
+++ b/src/qml/doc/snippets/qml/xmlhttprequest.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//![0]
+function sendRequest(url, callback)
+{
+ let request = new XMLHttpRequest();
+
+ request.onreadystatechange = function() {
+ if (request.readyState === XMLHttpRequest.DONE) {
+ let response = {
+ status : request.status,
+ headers : request.getAllResponseHeaders(),
+ contentType : request.responseType,
+ content : request.response
+ };
+
+ callback(response);
+ }
+ }
+
+ request.open("GET", url);
+ request.send();
+}
+//![0]
diff --git a/src/quick/doc/snippets/qmllint/config.ini b/src/qml/doc/snippets/qmllint/config.ini
index 29fa21438a..29fa21438a 100644
--- a/src/quick/doc/snippets/qmllint/config.ini
+++ b/src/qml/doc/snippets/qmllint/config.ini
diff --git a/src/qml/doc/snippets/qmltc/CMakeLists.txt b/src/qml/doc/snippets/qmltc/CMakeLists.txt
index 4be5a96528..e7b150a787 100644
--- a/src/qml/doc/snippets/qmltc/CMakeLists.txt
+++ b/src/qml/doc/snippets/qmltc/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
set(application_name tst_qmltc_examples)
#[[
@@ -42,11 +42,12 @@ target_compile_definitions(${application_name} PRIVATE
QMLTC_TESTS_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}"
)
+qt_policy(SET QTP0001 NEW)
+
#! [qmltc-add-qml-module]
# Make the application into a proper QML module:
qt6_add_qml_module(${application_name}
URI QmltcExample
- AUTO_RESOURCE_PREFIX
QML_FILES ${application_qml_files}
# Compile qml files (listed in QML_FILES) to C++ using qmltc and add these
diff --git a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
index eda9009bb7..0afbcbf0bf 100644
--- a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
+++ b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp
@@ -22,7 +22,7 @@ class HelloWorld : public QObject
Q_PROPERTY(QString hello WRITE setHello READ hello BINDABLE bindableHello)
public:
- HelloWorld(QQmlEngine* engine, QObject* parent = nullptr);
+ HelloWorld(QQmlEngine* engine, QObject* parent = nullptr, [[maybe_unused]] qxp::function_ref<void(PropertyInitializer&)> initializer = [](PropertyInitializer&){});
Q_SIGNALS:
void created();
diff --git a/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp b/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
index 7bda70f985..8c2706531b 100644
--- a/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
+++ b/src/qml/doc/snippets/qmltc/tst_qmltc_examples.cpp
@@ -53,7 +53,10 @@ void tst_qmltc_examples::app()
QQmlEngine e;
QQuickWindow window;
- QScopedPointer<QmltcExample::myApp> documentRoot(new QmltcExample::myApp(&e));
+ QScopedPointer<QmltcExample::myApp> documentRoot(
+ new QmltcExample::myApp(&e, nullptr, [](auto& component){
+ component.setWidth(800);
+ }));
documentRoot->setParentItem(window.contentItem());
window.setHeight(documentRoot->height());
diff --git a/src/qml/doc/src/cmake/cmake-properties.qdoc b/src/qml/doc/src/cmake/cmake-properties.qdoc
index 750dcd2d49..297a094582 100644
--- a/src/qml/doc/src/cmake/cmake-properties.qdoc
+++ b/src/qml/doc/src/cmake/cmake-properties.qdoc
@@ -17,7 +17,7 @@ global CMake properties:
\ingroup cmake-properties-qtqml
\ingroup cmake-global-properties-qtqml
-\title QT_QMLLLINTER_TARGETS_FOLDER
+\title QT_QMLLINTER_TARGETS_FOLDER
\brief Sets the FOLDER property for targets that belong to the QML linter.
@@ -76,6 +76,8 @@ A \c{.qml} file that provides a singleton type needs to have its \c QT_QML_SINGL
property set to \c TRUE to ensure that the singleton command is written into the
\l{Module Definition qmldir Files}{qmldir} file.
This must be done in addition to the QML file containing the \c {pragma Singleton} statement.
+The source property must be set before \l{qt_add_qml_module}{creating} the module the
+singleton belongs to.
See \l{qt_target_qml_sources_example}{qt_target_qml_sources()} for an example on
how to set the \c QT_QML_SINGLETON_TYPE property.
diff --git a/src/qml/doc/src/cmake/cmake-variables.qdoc b/src/qml/doc/src/cmake/cmake-variables.qdoc
index 6e35655b43..de984c88ef 100644
--- a/src/qml/doc/src/cmake/cmake-variables.qdoc
+++ b/src/qml/doc/src/cmake/cmake-variables.qdoc
@@ -2,6 +2,15 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
+\group cmake-variables-qtqml
+\title CMake Global Variables in Qt6 Qml
+
+\l{CMake Command Reference#Qt6::Qml}{CMake Commands} know about the following
+global CMake variables:
+
+*/
+
+/*!
\page cmake-variable-qt-qml-output-directory.html
\ingroup cmake-variables-qtqml
@@ -26,6 +35,41 @@ The \c QT_QML_OUTPUT_DIRECTORY will also be added to the import path of the
modules under the same base location. This allows the project to use a source
directory structure that doesn't exactly match the URI structure of the QML
modules, or to merge sets of QML modules under a common base point.
+*/
+
+/*!
+\page cmake-variable-qt-qml-generate-qmlls-ini.html
+\ingroup cmake-variables-qtqml
+
+\title QT_QML_GENERATE_QMLLS_INI
+
+\brief Enables autogeneration of .qmlls.ini files for \QMLLS.
+\cmakevariablesince 6.7
+
+\c QT_QML_GENERATE_QMLLS_INI is a boolean that describes whether
+\l{qt6_add_qml_module}{qt6_add_qml_module()} calls generate \c{.qmlls.ini} files inside
+the \b{source folder}, into each subdirectory with a CMakeLists.txt file creating a QML module.
+If \c{.qmlls.ini} files already exist there, then they are overwritten.
+
+\note Using \c QT_QML_GENERATE_QMLLS_INI requires a CMake version >= 3.19.
+
+These \c{.qmlls.ini} files contain the path to the last configured build directory,
+and is needed by \l{\QMLLS Reference}{\QMLLS} to find user defined modules. See also
+\l{\QMLLS Reference}{\QMLLS} about the other ways of passing build folders to \QMLLS.
+
+
+As this variable is used for IDE integration, it should normally not be set in a project itself, but
+passed to CMake via an IDE or manually by passing
+\badcode
+-DQT_QML_GENERATE_QMLLS_INI=ON
+\endcode
+to the cmake executable.
+
+\warning The files generated by \c QT_QML_GENERATE_QMLLS_INI are only valid for the current
+configuration and should be ignored by your version control system. For Git, add \tt{**\/.qmlls.ini}
+to your top-level project \c{.gitignore}, for example.
+The globbing is required because .qmlls.ini files are generated in \e{all source
+subdirectories} that define QML Modules.
*/
diff --git a/src/qml/doc/src/cmake/policy/qtp0001.qdoc b/src/qml/doc/src/cmake/policy/qtp0001.qdoc
index a5381647c9..a4e54ed1ae 100644
--- a/src/qml/doc/src/cmake/policy/qtp0001.qdoc
+++ b/src/qml/doc/src/cmake/policy/qtp0001.qdoc
@@ -14,23 +14,26 @@ This policy was introduced in Qt 6.5. It changes where
\l{qt_add_qml_module}{qt_add_qml_module()} stores QML resources in
the resource system.
-The \c OLD behavior of this policy is that, unless
-\c AUTO_RESOURCE_PREFIX is set, the \c RESOURCE_PREFIX argument for
+Enabling this policy ensures that your QML module is placed under
+a default \l {QML Import Path}{import path}, and its types can be
+found without manual calls to \l QQmlEngine::addImportPath.
+
+The \c OLD behavior of this policy is that, the \c RESOURCE_PREFIX argument for
\c{qt_add_qml_module()} defaults to \c{":/"}.
The \c NEW behavior of this policy is that the \c RESOURCE_PREFIX argument
-for \c{qt_add_qml_module()} defaults to \c{\":/qt/qml/"}. The new behavior
+for \c{qt_add_qml_module()} defaults to \c{":/qt/qml/"}. The new behavior
ensures that modules are put into the \l{QML Import Path} and can be
found without further setup.
Qt 6.5 issues warnings if you do not pass any of the following arguments to the
-\c qt_add_qml_module command: \c RESOURCE_PREFIX, \c AUTO_RESOURCE_PREFIX,
-\c NO_RESOURCE_TARGET_PATH. Use the \l qt_policy command to suppress
-the warning by explicitly setting the policy to \c OLD or \c NEW.
+\c qt_add_qml_module command: \c RESOURCE_PREFIX, \c NO_RESOURCE_TARGET_PATH.
+Use the \l qt_policy command to suppress the warning by explicitly setting
+the policy to \c OLD or \c NEW.
-\note The \c{OLD} behavior of a policy is deprecated, and may
-be removed in the future.
+\qtpolicydeprecatedbehavior
-\sa qt_policy, qt_cmake_policies, qt_add_qml_module
+\sa qt_policy, {qt6_standard_project_setup}{qt_standard_project_setup()},
+ qt_cmake_policies, qt_add_qml_module
*/
diff --git a/src/qml/doc/src/cmake/policy/qtp0004.qdoc b/src/qml/doc/src/cmake/policy/qtp0004.qdoc
new file mode 100644
index 0000000000..9d3428e52b
--- /dev/null
+++ b/src/qml/doc/src/cmake/policy/qtp0004.qdoc
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qt-cmake-policy-qtp0004.html
+\ingroup qt-cmake-policies
+
+\title QTP0004
+\keyword qt_cmake_policy_qtp0004
+
+\summary {Extra directories with QML files in a QML module need extra qmldir files.}
+
+This policy was introduced in Qt 6.8. It causes the build system to generate
+an extra qmldir file for each additional directory that contains QML files in
+a QML module.
+
+Enabling this policy ensures that the implicit import of each of the QML
+components in your module is the same as the module itself. This means that
+all the components can see each other without explicitly importing the module.
+
+The \c OLD behavior of this policy is that a qmldir file is only generated for
+the root directory of a module.
+
+The \c NEW behavior of this policy is that for each directory with QML files in
+a module a separate qmldir file is generated.
+
+Qt 6.8 issues warnings if you do not explicitly set the policy.
+
+\qtpolicydeprecatedbehavior
+
+\sa qt_policy, {qt6_standard_project_setup}{qt_standard_project_setup()},
+ qt_cmake_policies, qt_add_qml_module
+
+*/
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 857779af2d..4ca7635b9c 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -23,7 +23,6 @@ qt_add_qml_module(
[STATIC | SHARED]
[PLUGIN_TARGET plugin_target]
[OUTPUT_DIRECTORY output_dir]
- [AUTO_RESOURCE_PREFIX]
[RESOURCE_PREFIX resource_prefix]
[CLASS_NAME class_name]
[TYPEINFO typeinfo]
@@ -39,6 +38,7 @@ qt_add_qml_module(
[DESIGNER_SUPPORTED]
[FOLLOW_FOREIGN_VERSIONING]
[NAMESPACE namespace]
+ [NO_PLUGIN]
[NO_PLUGIN_OPTIONAL]
[NO_CREATE_PLUGIN_TARGET]
[NO_GENERATE_PLUGIN_SOURCE]
@@ -50,6 +50,9 @@ qt_add_qml_module(
[NO_IMPORT_SCAN]
[ENABLE_TYPE_COMPILER]
[TYPE_COMPILER_NAMESPACE namespace]
+ [QMLTC_EXPORT_DIRECTIVE export_macro]
+ [QMLTC_EXPORT_FILE_NAME header_defining_export_macro]
+
)
\endcode
@@ -59,6 +62,9 @@ qt_add_qml_module(
See \l {Building a QML application} and \l {Building a reusable QML module}
for examples that define QML modules.
+See \l {QT_QML_GENERATE_QMLLS_INI} for configuring your project such that information about
+QML modules is exposed to the \l{QML Language Server}.
+
\section1 Description
This command defines a QML module that can consist of C++ sources, \c{.qml}
@@ -112,9 +118,13 @@ For cases where the QML module needs a custom plugin class implementation, the
\l{NO_GENERATE_PLUGIN_SOURCE} and usually the \l{NO_PLUGIN_OPTIONAL} options
will be needed.
+The \c STATIC QML modules also generate the static QML plugins if
+\c NO_PLUGIN is not specified. Targets that import such \c STATIC QML modules
+also need to explicitly link to corresponding QML plugins.
+
\note
-When using static linking, it migt be necessary to use
-\c Q_IMPORT_QML_PLUGIN to ensure that the QML plugin is correctly linked.
+When using static linking, it might be necessary to use
+\l {Q_IMPORT_QML_PLUGIN} to ensure that the QML plugin is correctly linked.
\section3 Plugin target with no backing target
@@ -161,6 +171,12 @@ The \l OUTPUT_DIRECTORY argument determines where the \c qmldir and typeinfo
files will be written to. If the QML module has a plugin, that plugin will also
be created in the same directory as the \c qmldir file.
+If \l{QTP0004} policy is set to \c NEW, for each further directory that contains
+\c{.qml} files another \c qmldir file is generated. These extra \c qmldir files
+merely redirect to the module's base directory via a \c prefer directive. This
+is so that all the QML components in a module can access each other, no matter
+which directory they are stored in.
+
If using a statically built Qt, the backing target's \c{.qml} files will be
scanned during the CMake configure run to determine the imports used by the
module and to set up linking relationships (the \c{NO_IMPORT_SCAN} keyword
@@ -209,11 +225,10 @@ current source directory (\c CMAKE_CURRENT_SOURCE_DIR). This resource path is
appended to a prefix formed by concatenating the \l{RESOURCE_PREFIX} and
the target path (but see \l NO_RESOURCE_TARGET_PATH for an exception to this).
-Since Qt 6.5, you can specify \l{AUTO_RESOURCE_PREFIX}. If
-\l{AUTO_RESOURCE_PREFIX} is specified, \l{RESOURCE_PREFIX} is set to
-\c{/qt/qml}. This way, your modules are automatically
-placed in the default import path of the QML engine. If you don't do this, you
-should set up a \l{QML Import Path} to point to your resource prefix.
+If \l{QTP0001} policy is set to \c NEW, the \l{RESOURCE_PREFIX} defaults
+to \c{/qt/qml/} which is the default import path of the QML engine.
+This ensures that modules are put into the \l{QML Import Path} and can be
+found without further setup.
Ordinarily, the project should aim to place \c{.qml} files in
the same relative location as they would have in the resources. If the \c{.qml}
@@ -269,7 +284,7 @@ set_target_properties(someTarget PROPERTIES
\endcode
Another important argument is \c{--direct-calls}. You can use it to enable the
-direct mode of \l{The QML Script Compiler} in case the QtQuick Compiler
+direct mode of \l{The QML script compiler} in case the Qt Quick Compiler
Extensions are installed. If the extensions are not installed, the argument is
ignored. There is a shorthand called \c {QT_QMLCACHEGEN_DIRECT_CALLS} for it.
@@ -279,6 +294,23 @@ set_target_properties(someTarget PROPERTIES
)
\endcode
+Finally, the \c --verbose argument can be used to see diagnostic output from
+qmlcachegen:
+
+\badcode
+set_target_properties(someTarget PROPERTIES
+ QT_QMLCACHEGEN_ARGUMENTS "--verbose"
+)
+\endcode
+
+With this flag set, qmlcachegen will output warnings for each function it
+cannot compile to C++. Some of these warnings will point to problems in your
+QML code and some will tell you that certain features of the QML language are
+not implemented in the C++ code generator. In both cases, qmlcachegen will
+still generate byte code for such functions. If you want to see only the
+problems in your QML code, you should use qmllint and the targets generated
+for it instead.
+
\target qmllint-auto
\section2 Linting QML sources
@@ -296,9 +328,7 @@ 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.
+property \l QT_QML_SOURCE_TYPENAME to the desired type name.
\target qml-cmake-singletons
\section2 Singletons
@@ -308,15 +338,25 @@ need to have their \c QT_QML_SINGLETON_TYPE source property set to \c TRUE, to
ensure that the \c singleton command is written into the
\l{Module Definition qmldir Files}{qmldir} file. This must be done in addition
to the QML file containing the \c {pragma Singleton} statement.
+The source property must be set before creating the module the
+singleton belongs to.
See \l{qt_target_qml_sources_example}{qt_target_qml_sources()} for an example on
how to set the \c QT_QML_SINGLETON_TYPE property.
\target qmltc-cmake
-\section2 Compiling QML to C++ with QML Type Compiler
+\section2 Compiling QML to C++ with QML type compiler
+
+\note The \l{QML type compiler} \c{qmltc} does not guarantee that the generated
+C++ stays API-, source- or binary-compatible between past or future versions,
+even patch versions.
+Furthermore, qmltc-compiled apps using Qt's QML modules will require linking
+against private Qt API, see also
+\l{QML type compiler#compiling-qml-code-with-qmltc}{Compiling QML code with qmltc}.
+
If a QML module has \c{.qml} files, you can compile them to C++ using \l{QML
-Type Compiler}{qmltc}. Unlike \l{qmlcachegen-auto}{bytecode compilation}, you
+type compiler}{qmltc}. Unlike \l{qmlcachegen-auto}{bytecode compilation}, you
have to explicitly enable qmltc via \l{ENABLE_TYPE_COMPILER} argument. In which
case, \c{.qml} files specified under \c{QML_FILES} would be compiled. Files
ending with \c{.js} and \c{.mjs} are ignored as qmltc does not compile
@@ -447,6 +487,7 @@ under the \c RESOURCE_PREFIX instead. This can be achieved by specifying the
\c NO_RESOURCE_TARGET_PATH option, which may only be used if the backing target
is an executable.
+\target PAST_MAJOR_VERSIONS
\section2 Registering past major versions
\c PAST_MAJOR_VERSIONS contains a list of additional major version that the module
@@ -512,7 +553,7 @@ class MyItem: public QQuickItem { ... };
then one has to make sure that the module containing \c QQuickItem, called
\c Quick, is declared as a dependency via the \c DEPENDENCIES option. Not doing
so might result in errors during type compilation with
-\l{QML Type Compiler}{qmltc} or during binding and function compilation to C++
+\l{QML type compiler}{qmltc} or during binding and function compilation to C++
with \l{qmlcachegen-auto}{qmlcachegen}.
\note Adding the module to \c DEPENDENCIES is not necessary if the module
@@ -537,6 +578,7 @@ These additional targets are generated internally by \c{qt_add_qml_module()}
and are referenced by the backing target's linking requirements as part of
ensuring that resources are set up and loaded correctly.
+\target PLUGIN_TARGET
\section2 Targets and plugin targets
\c PLUGIN_TARGET specifies the plugin target associated with the QML module.
@@ -561,6 +603,7 @@ link directly to the backing target and do not need a plugin, it can be
disabled by adding the \c NO_PLUGIN option. Specifying both \c NO_PLUGIN and
\c PLUGIN_TARGET is an error.
+\target NO_CREATE_PLUGIN_TARGET
In certain situations, the project may want to delay creating the plugin target
until after the call. The \c NO_CREATE_PLUGIN_TARGET option can be given in
that situation. The project is then expected to call
@@ -568,6 +611,7 @@ that situation. The project is then expected to call
been created. When \c NO_CREATE_PLUGIN_TARGET is given, \c PLUGIN_TARGET must
also be provided to explicitly name the plugin target.
+\target CLASS_NAME
\target NO_GENERATE_PLUGIN_SOURCE
By default, \c{qt_add_qml_module()} will auto-generate a \c{.cpp} file that
implements the plugin class named by the \c CLASS_NAME argument. The generated
@@ -577,7 +621,21 @@ plugin class, the \c NO_GENERATE_PLUGIN_SOURCE option should be given. Where no
\c CLASS_NAME is provided, it defaults to the \c URI with dots replaced by
underscores, then \c Plugin appended. Unless the QML module has no plugin, the
class name will be recorded as a \c classname line in the generated
-\l{Module Definition qmldir Files}{qmldir} file.
+\l{Module Definition qmldir Files}{qmldir} file. You need to add any C++ files
+with custom plugin code to the plugin target. Since the plugin then likely
+contains functionality that goes beyond simply loading the backing library, you
+will probably want to add \l{NO_PLUGIN_OPTIONAL}, too. Otherwise the QML engine
+may skip loading the plugin if it detects that the backing library is already
+linked.
+
+\target NO_PLUGIN
+If the \c NO_PLUGIN keyword is given, then no plugin will be built. This
+keyword is thus incompatible with all the options that customize the plugin
+target, in particular \l{NO_GENERATE_PLUGIN_SOURCE}, \l{NO_PLUGIN_OPTIONAL},
+\l{PLUGIN_TARGET}, \l{NO_CREATE_PLUGIN_TARGET}, and \l{CLASS_NAME}. If you do
+not provide a plugin for your module, it will only be fully usable if its
+backing library has been linked into the executable. It is generally hard to
+guarantee that a linker preserves the linkage to a library it considers unused.
\target NO_PLUGIN_OPTIONAL
If the \c NO_PLUGIN_OPTIONAL keyword is given, then the plugin is recorded in
@@ -661,27 +719,23 @@ code will be generated into a C++ namespace of this name.
For static Qt builds, \c{qmlimportscanner} is run at configure time to scan the
\c{.qml} files of a QML module and identify the QML imports it uses (see
\l{qt6_import_qml_plugins}{qt_import_qml_plugins()}). For non-static Qt builds,
-if the target is an executable, a similar scan is performed at build time
-to provide the information needed by deployment scripts (see
-\l{qt_deploy_qml_imports()}). Both scans can be disabled by providing the
-\c{NO_IMPORT_SCAN} option. Doing so means the project takes on the
-responsibility of ensuring all required plugins are instantiated and linked for
-static builds. For non-static builds the project must manually work out
-and deploy all QML modules used by an executable target.
+if the target is an executable, a similar scan is performed at build time to
+provide the information needed by deployment scripts (see
+\l{qt6_deploy_qml_imports}{qt_deploy_qml_imports()}). Both scans can be
+disabled by providing the \c{NO_IMPORT_SCAN} option. Doing so means the project
+takes on the responsibility of ensuring all required plugins are instantiated
+and linked for static builds. For non-static builds the project must manually
+work out and deploy all QML modules used by an executable target.
\section2 Arguments for qmltc
\target ENABLE_TYPE_COMPILER
\c ENABLE_TYPE_COMPILER can be used to compile \c{.qml} files to C++ source code
-with \l{QML Type Compiler}{qmltc}. Files with the source property
+with \l{QML type compiler}{qmltc}. Files with the source property
\c{QT_QML_SKIP_TYPE_COMPILER} are not compiled to C++.
-\c TYPE_COMPILER_NAMESPACE argument defines a namespace, in which the generated
-C++ code resides. By default, no namespace is specified for user projects. The
-code generated from Qt's own sources is put under a QT_NAMESPACE namespace.
-
\c TYPE_COMPILER_NAMESPACE argument allows to override the namespace in which
-\l{QML Type Compiler}{qmltc} generates code.
+\l{QML type compiler}{qmltc} generates code.
By default, the namespace of the generated code follows the module
hierarchy as depicted in the URI,
e.g., \c MyModule for a module with URI \c MyModule or
@@ -691,4 +745,15 @@ can be put instead in a custom namespace, where different subnamespaces are to
be separated by a "::", e.g. "MyNamespace::MySubnamespace" for the namespace MySubnamespace that
is inside the MyNamespace. Apart from the "::", C++ namespace naming rules
apply.
+
+\c QMLTC_QMLTC_EXPORT_DIRECTIVE should be used with \c QMLTC_EXPORT_FILE_NAME when
+the classes generated by \l{QML type compiler}{qmltc} should be exported from
+the qml library. By default, classes generated by qmltc are not exported from
+their library.
+The header defining the export macro for the current library
+can be specified as an optional argument to \c QMLTC_EXPORT_FILE_NAME while the
+exporting macro name should be specified as an argument to
+\c QMLTC_QMLTC_EXPORT_DIRECTIVE. If no additional include is required or wanted,
+e.g. when the header of the export macro is already indirectly included by a base
+class, then the \c QMLTC_EXPORT_FILE_NAME option can be left out.
*/
diff --git a/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc b/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
index c92286f109..87125cd0bf 100644
--- a/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
+++ b/src/qml/doc/src/cmake/qt_deploy_qml_imports.qdoc
@@ -6,17 +6,17 @@
\ingroup cmake-commands-qtqml
\title qt_deploy_qml_imports
-\target qt6_deploy_qml_imports
+\keyword qt6_deploy_qml_imports
\summary {Deploy the runtime components of QML modules needed by an executable.}
\include cmake-find-package-qml.qdocinc
-Unlike most other CMake commands provided by Qt, \c{qt_deploy_qml_imports()}
-can only be called from a deployment script. It cannot be called directly by the
-project.
+Unlike most other CMake commands provided by Qt,
+\c{qt6_deploy_qml_imports} can only be called from a
+deployment script. It cannot be called directly by the project.
-\include cmake-qml-qt-finalize-target-warning.qdocinc
+\include cmake-qml-qt-finalize-target-warning.qdocinc warning
\section1 Synopsis
@@ -38,9 +38,9 @@ qt_deploy_qml_imports(
When installing an application that uses QML, it may be non-trivial to work out
which QML modules and which parts of those QML modules need to also be
installed. Because QML plugins are not linked directly to an application's
-executable, \l{qt_deploy_runtime_dependencies()} won't find these QML modules.
-The \c{qt_deploy_qml_imports()} command provides the necessary logic which
-complements \l{qt_deploy_runtime_dependencies()} and deploys the runtime parts
+executable, \l{qt6_deploy_runtime_dependencies}{qt_deploy_runtime_dependencies()} won't find these QML modules.
+The \c{qt6_deploy_qml_imports} command provides the necessary logic which
+complements \l{qt6_deploy_runtime_dependencies}{qt_deploy_runtime_dependencies()} and deploys the runtime parts
of all QML modules imported by the application.
The \c{TARGET} option is mandatory and should specify a \c{target} that is an
@@ -62,10 +62,11 @@ choice.
The command will store a list of all QML plugins it deploys in the variable
named by the \c{PLUGINS_FOUND} option, if given. This is often passed as the
\c{ADDITIONAL_MODULES} argument in a subsequent call to
-\l{qt_deploy_runtime_dependencies()}.
+\l{qt6_deploy_runtime_dependencies}{qt_deploy_runtime_dependencies()}.
\sa {qt6_generate_deploy_qml_app_script}{qt_generate_deploy_qml_app_script()},
- qt_deploy_runtime_dependencies(), QT_DEPLOY_QML_DIR
+ {qt6_deploy_runtime_dependencies}{qt_deploy_runtime_dependencies()},
+ QT_DEPLOY_QML_DIR
\section1 Example
@@ -86,7 +87,7 @@ qt_add_qml_module(MyApp
# The following script must only be executed at install time
set(deploy_script "${CMAKE_CURRENT_BINARY_DIR}/deploy_MyApp.cmake")
-file(GENERATE OUTPUT ${deploy_script} CONTENTS "
+file(GENERATE OUTPUT ${deploy_script} CONTENT "
include(\"${QT_DEPLOY_SUPPORT}\")
qt_deploy_qml_imports(
# Deploy QML modules used by MyApp
diff --git a/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc b/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
index f99f963770..0d5088e7e5 100644
--- a/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
+++ b/src/qml/doc/src/cmake/qt_generate_deploy_qml_app_script.qdoc
@@ -14,7 +14,7 @@
\cmakecommandsince 6.3
-\include cmake-qml-qt-finalize-target-warning.qdocinc
+\include cmake-qml-qt-finalize-target-warning.qdocinc warning
\section1 Synopsis
@@ -24,6 +24,8 @@ qt_generate_deploy_qml_app_script(
OUTPUT_SCRIPT <var>
[NO_UNSUPPORTED_PLATFORM_ERROR]
[NO_TRANSLATIONS]
+ [NO_COMPILER_RUNTIME]
+ [DEPLOY_TOOL_OPTIONS ...]
[DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM]
[MACOS_BUNDLE_POST_BUILD]
[PRE_INCLUDE_REGEXES regexes...]
@@ -68,9 +70,11 @@ is only written at CMake generate-time. It is intended to be used with the
\l{install(SCRIPT)} command, which should come after the application's target
has been installed using \l{install(TARGETS)}.
-The deployment script will call \l{qt_deploy_qml_imports()} with a suitable set
-of options for the standard install layout. For macOS app bundles and Windows
-targets, it will then also call \l{qt_deploy_runtime_dependencies()}, again
+The deployment script will call
+\l{qt6_deploy_qml_imports}{qt_deploy_qml_imports()} with a suitable set of
+options for the standard install layout. For macOS app bundles and Windows
+targets, it will then also call
+\l{qt6_deploy_runtime_dependencies}{qt_deploy_runtime_dependencies()}, again
with suitable options for the standard install layout.
Calling \c{qt_generate_deploy_qml_app_script()} for a platform that is not
@@ -94,14 +98,22 @@ other platforms.
On platforms other than macOS, Qt translations are automatically deployed. To
inhibit this behavior, specify \c{NO_TRANSLATIONS}. Use
-\l{qt6_deploy_translations}{qt_deploy_translations} to deploy translations in a
+\l{qt_deploy_translations}{qt_deploy_translations()} to deploy translations in a
customized way.
+For Windows desktop applications, the required runtime files for the compiler
+are also installed by default. To prevent this, specify \c{NO_COMPILER_RUNTIME}.
+
+Since Qt 6.7, you can use \c{DEPLOY_TOOL_OPTIONS} to pass additional options to
+the underlying deployment tool. This only has an effect if the underlying
+deployment tool is either macdeployqt or windeployqt.
+
The options \c{PRE_INCLUDE_REGEXES}, \c{PRE_EXCLUDE_REGEXES},
\c{POST_INCLUDE_REGEXES}, \c{POST_EXCLUDE_REGEXES}, \c{POST_INCLUDE_FILES}, and
\c{POST_EXCLUDE_FILES} can be specified to control the deployment of runtime
dependencies. These options do not apply to all platforms and are forwarded
-unmodified to \l{qt_deploy_runtime_dependencies()}.
+unmodified to
+\l{qt6_deploy_runtime_dependencies}{qt_deploy_runtime_dependencies()}.
For deploying a non-QML application, use
\l{qt6_generate_deploy_app_script}{qt_generate_deploy_app_script()}
@@ -114,6 +126,8 @@ same target.
\section1 Example
+The following example shows how to deploy a QtQuick app.
+
\badcode
cmake_minimum_required(VERSION 3.16...3.22)
project(MyThings)
@@ -142,4 +156,22 @@ qt_generate_deploy_qml_app_script(
)
install(SCRIPT ${deploy_script})
\endcode
+
+The following example shows how to pass additional options to the underlying
+deployment tool.
+
+\badcode
+set(deploy_tool_options_arg "")
+if(APPLE)
+ set(deploy_tool_options_arg --hardened-runtime)
+elseif(WIN32)
+ set(deploy_tool_options_arg --no-compiler-runtime)
+endif()
+
+qt_generate_deploy_qml_app_script(
+ ...
+ DEPLOY_TOOL_OPTIONS ${deploy_tool_options_arg}
+)
+install(SCRIPT ${deploy_script})
+\endcode
*/
diff --git a/src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc b/src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc
index 8206dc011b..22d72c101b 100644
--- a/src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc
+++ b/src/qml/doc/src/cmake/qt_generate_foreign_qml_types.qdoc
@@ -65,4 +65,13 @@ the \c QML_ELEMENT macro).
The effect is equivalent to using \c QML_FOREIGN with custom structs in the QML library to expose
the types.
+
+\note In order to implement custom behavior, such as exposing an existing
+singleton instance with its own life cycle to QML, you should add custom types
+to your QML library (mylib_declarative in the above example). In turn, you
+should omit the \l QML_ELEMENT and similar macros from the original C++ classes
+so that qt_generate_foreign_qml_types() does not generate more QML integration
+structs for them. The QML macros, as well as any singleton factory functions,
+can be added to the structs that contain the \l QML_FOREIGN.
+
*/
diff --git a/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc b/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
index 35854d4b95..336cd973f9 100644
--- a/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
+++ b/src/qml/doc/src/cmake/qt_target_qml_sources.qdoc
@@ -159,6 +159,8 @@ the type it provides is an internal one. The name of the type itself can also
be overridden using the \c QT_QML_SOURCE_TYPENAME property. All three of these
will be reflected in the file's type entries in the
\l{qmldir-autogeneration}{generated \c qmldir file}.
+The source properties must be set before \l{qt_add_qml_module}{creating} the
+module the singleton belongs to.
\target QT_RESOURCE_ALIAS
All files listed with \c QML_FILES or \c RESOURCES will be added to the
diff --git a/src/qml/doc/src/cppclasses/topic.qdoc b/src/qml/doc/src/cppclasses/topic.qdoc
index 0ef283fe16..78595fa13a 100644
--- a/src/qml/doc/src/cppclasses/topic.qdoc
+++ b/src/qml/doc/src/cppclasses/topic.qdoc
@@ -5,7 +5,7 @@
\title Important C++ Classes Provided By The Qt QML Module
\brief Overview of the C++ classes provided by the Qt QML module
-The \l{Qt QML} module provides C++ classes which implement the QML framework.
+The \l{Qt Qml} module provides C++ classes which implement the QML framework.
Clients can use these classes to interact with the QML run-time (for example,
by injecting data or invoking methods on objects), and to instantiate a
hierarchy of objects from a QML document. The Qt QML module provides more
diff --git a/src/qml/doc/src/cppintegration/contextproperties.qdoc b/src/qml/doc/src/cppintegration/contextproperties.qdoc
index af9f4719e8..1783c0cdc8 100644
--- a/src/qml/doc/src/cppintegration/contextproperties.qdoc
+++ b/src/qml/doc/src/cppintegration/contextproperties.qdoc
@@ -5,6 +5,23 @@
\title Embedding C++ Objects into QML with Context Properties
\brief Description of how to embed C++ data into QML using context properties
+\warning By using context properties in your QML code, you create a dependency from your QML code
+ to the specific context you have in mind when writing it. This limits re-usability of your
+ code since the context may be different in other places where it might be used.
+ Furthermore, the dependency is not declared. You never \c import the context or otherwise
+ state what you expect. Therefore, anyone trying to re-use your code will have difficulties
+ finding out whether the place of re-use has a context sufficient for your code.
+
+\warning Context properties are invisible to any tooling that processes QML code ahead of time,
+ before you load it into the QML engine. The \l{Qt Quick Compiler},
+ \l{qmllint Reference}{qmllint}, and the \l{\QMLLS Reference}{\QMLLS} do
+ not know anything about your context properties and will consider any access to context
+ properties as an \e{unqualified access}.
+
+\note Context properties can generally be replaced either by regular properties on the root object
+ of a component, or by singletons defined either in C++ using \l{QML_SINGLETON}{QML_SINGLETON}
+ or in QML using \l{Structure of a QML Document#Singleton}{pragma Singleton}.
+
When loading a QML object into a C++ application, it can be useful to directly embed some C++ data
that can be used from within the QML code. This makes it possible, for example, to invoke a C++
method on the embedded object, or use a C++ object instance as a data model for a QML view.
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc
index e564edb943..2822f4acee 100644
--- a/src/qml/doc/src/cppintegration/data.qdoc
+++ b/src/qml/doc/src/cppintegration/data.qdoc
@@ -92,7 +92,7 @@ when passed from C++ to QML and vice-versa:
\li QVector2D, QVector3D, QVector4D
\li \l vector2d, \l vector3d, \l vector4d
\row
- \li Enums declared with Q_ENUM() or Q_ENUMS()
+ \li Enums declared with Q_ENUM()
\li \l enumeration
\endtable
@@ -306,97 +306,15 @@ calling its \l {QDateTime::}{time()} method.
\section2 Sequence Type to JavaScript Array
-Certain C++ sequence types are supported transparently in QML to behave like
-JavaScript \c Array types.
-
-In particular, QML currently supports:
-\list
- \li \c {QList<int>}
- \li \c {QList<qreal>}
- \li \c {QList<bool>}
- \li \c {QList<QString>} and \c{QStringList}
- \li \c {QVector<QString>}
- \li \c {std::vector<QString>}
- \li \c {QList<QUrl>}
- \li \c {QVector<QUrl>}
- \li \c {std::vector<QUrl>}
- \li \c {QVector<int>}
- \li \c {QVector<qreal>}
- \li \c {QVector<bool>}
- \li \c {std::vector<int>}
- \li \c {std::vector<qreal>}
- \li \c {std::vector<bool>}
-\endlist
-
-and all registered QList, QVector, QQueue, QStack, QSet, std::list,
-std::vector that contain a type marked with \l Q_DECLARE_METATYPE.
-
-These sequence types are implemented directly in terms of the underlying C++
-sequence. There are two ways in which such sequences can be exposed to QML:
-as a Q_PROPERTY of the given sequence type; or as the return type of a
-Q_INVOKABLE method. There are some differences in the way these are
-implemented, which are important to note.
-
-If the sequence is exposed as a Q_PROPERTY, accessing any value in the
-sequence by index will cause the sequence data to be read from the QObject's
-property, then a read to occur. Similarly, modifying any value in the
-sequence will cause the sequence data to be read, and then the modification
-will be performed and the modified sequence will be written back to the
-QObject's property.
-
-If the sequence is returned from a Q_INVOKABLE function, access and mutation
-is much cheaper, as no QObject property read or write occurs; instead, the
-C++ sequence data is accessed and modified directly.
-
-In both the Q_PROPERTY and return from Q_INVOKABLE cases, the elements
-of a std::vector are copied. This copying may be an expensive operation,
-so std::vector should be used judiciously.
+See \l{QML Sequence Types} for a general description of sequence types. The
+\l{Qt QML QML Types}{QtQml module} contains a few sequence types
+you may want to use.
You can also create a list-like data structure by constructing a QJSValue using
QJSEngine::newArray(). Such a JavaScript array does not need any conversion
when passing it between QML and C++. See \l{QJSValue#Working With Arrays} for
details on how to manipulate JavaScript arrays from C++.
-Other sequence types are not supported transparently, and instead an
-instance of any other sequence type will be passed between QML and C++ as an
-opaque QVariantList.
-
-\b {Important Note:} There are some minor differences between the
-semantics of such sequence Array types and default JavaScript Array types
-which result from the use of a C++ storage type in the implementation. In
-particular, deleting an element from an Array will result in a
-default-constructed value replacing that element, rather than an Undefined
-value. Similarly, setting the length property of the Array to a value larger
-than its current value will result in the Array being padded out to the
-specified length with default-constructed elements rather than Undefined
-elements. Finally, the Qt container classes support signed (rather than
-unsigned) integer indexes; thus, attempting to access any index greater
-than INT_MAX will fail.
-
-The default-constructed values for each sequence type are as follows:
-\table
-\row \li QList<int> \li integer value 0
-\row \li QList<qreal> \li real value 0.0
-\row \li QList<bool> \li boolean value \c {false}
-\row \li QList<QString> and QStringList \li empty QString
-\row \li QVector<QString> \li empty QString
-\row \li std::vector<QString> \li empty QString
-\row \li QList<QUrl> \li empty QUrl
-\row \li QVector<QUrl> \li empty QUrl
-\row \li std::vector<QUrl> \li empty QUrl
-\row \li QVector<int> \li integer value 0
-\row \li QVector<qreal> \li real value 0.0
-\row \li QVector<bool> \li boolean value \c {false}
-\row \li std::vector<int> \li integer value 0
-\row \li std::vector<qreal> \li real value 0.0
-\row \li std::vector<bool> \li boolean value \c {false}
-\endtable
-
-If you wish to remove elements from a sequence rather than simply replace
-them with default constructed values, do not use the indexed delete operator
-("delete sequence[i]") but instead use the \c {splice} function
-("sequence.splice(startIndex, deleteCount)").
-
\section2 QByteArray to JavaScript ArrayBuffer
The QML engine provides automatic type conversion between QByteArray values and
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index e6a6dbfe36..feb9053632 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -16,7 +16,7 @@ as an instantiable \l{qtqml-typesystem-objecttypes.html}{QML object type} from
QML, or enabling a singleton instance of the class to be imported and used
from QML.
-Additionally, the \l {Qt QML} module provides mechanisms for implementing QML-specific
+Additionally, the \l {Qt Qml} module provides mechanisms for implementing QML-specific
features such as \e{attached properties} and \e{default properties} in C++.
(Note that a number of the important concepts covered in this document are
@@ -77,15 +77,18 @@ Types to QML} explains, the properties, methods and signals of any
QObject-derived class are accessible from QML code.
To register a QObject-derived class as an instantiable QML object type, add
-\c QML_ELEMENT or \c QML_NAMED_ELEMENT(<name>) to the class declaration and
+\c QML_ELEMENT or \c QML_NAMED_ELEMENT(<name>) to the class declaration. You
+also need to make adjustments in the build system. For qmake, add
\c {CONFIG += qmltypes}, a \c {QML_IMPORT_NAME}, and a
-\c QML_IMPORT_MAJOR_VERSION to your project file. This will register the class
-into the type namespace under the given major version, using either the class
-name or an explicitly given name as QML type name. The minor version(s) will
-be derived from any revisions attached to properties, methods, or signals. The
-default minor version is \c 0. You can explicitly restrict the type to be
-available only from specific minor versions by adding the
-\c QML_ADDED_IN_MINOR_VERSION() macro to the class declaration. Clients can
+\c QML_IMPORT_MAJOR_VERSION to your project file. For CMake, the file containing
+the class should be part of a target set-up with
+\l{qt_add_qml_module}{qt_add_qml_module()}.
+This will register the class into the type namespace under the given major version,
+using either the class name or an explicitly given name as QML type name. The
+minor version(s) will be derived from any revisions attached to properties,
+methods, or signals. The default minor version is \c 0. You can explicitly
+restrict the type to be available only from specific minor versions by adding
+the \c QML_ADDED_IN_VERSION() macro to the class declaration. Clients can
import suitable versions of the namespace in order to use the type.
For example, suppose there is a \c Message class with \c author and
@@ -107,26 +110,52 @@ This type can be registered by adding an appropriate type namespace and version
number to the project file. For example, to make the type available in the
\c com.mycompany.messaging namespace with version 1.0:
-\code
-CONFIG += qmltypes
-QML_IMPORT_NAME = com.mycompany.messaging
-QML_IMPORT_MAJOR_VERSION = 1
-\endcode
+\if defined(onlinedocs)
+ \tab {build-qt-app}{tab-cmake}{CMake}{selected}
+ \tab {build-qt-app}{tab-qmake}{qmake}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section3 Using CMake
+\endif
+ \badcode
+ qt_add_qml_module(messaging
+ URI com.mycompany.messaging
+ VERSION 1.0
+ SOURCES
+ message.cpp message.h
+ )
+ \endcode
+\if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+\else
+ \section3 Using QMake
+\endif
+ \code
+ CONFIG += qmltypes
+ QML_IMPORT_NAME = com.mycompany.messaging
+ QML_IMPORT_MAJOR_VERSION = 1
+ \endcode
+
+ If the header the class is declared in is not accessible from your project's
+ include path, you may have to amend the include path so that the generated
+ registration code can be compiled.
+
+ \code
+ INCLUDEPATH += com/mycompany/messaging
+ \endcode
+\if defined(onlinedocs)
+ \endtabcontent
+\endif
-If the header the class is declared in is not accessible from your project's
-include path, you may have to amend the include path so that the generated
-registration code can be compiled:
-\code
-INCLUDEPATH += com/mycompany/messaging
-\endcode
The type can be used in an \l{qtqml-syntax-basics.html#object-declarations}
{object declaration} from QML, and its properties can be read and written to,
as per the example below:
\qml
-import com.mycompany.messaging 1.0
+import com.mycompany.messaging
Message {
author: "Amelie"
@@ -137,7 +166,7 @@ Message {
\section2 Registering Value Types
Any type with a \l{Q_GADGET} macro can the registered as a
-\l{qtqml-typesystem-valuetypes.html}{QML value type}}. Once such a type is
+\l{qtqml-typesystem-valuetypes.html}{QML value type}. Once such a type is
registered with the QML type system it can be used as property type in QML
code. Such an instance can be manipulated from QML; as
\l{qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++
@@ -177,6 +206,60 @@ There are some further limitations on what you can do with value types:
and subject to future changes.
\endlist
+\section2 Value Types with Enumerations
+
+Exposing enumerations from a value type to QML requires some extra steps.
+
+Value types have lower case names in QML and types with lower case
+names are generally not addressable in JavaScript code (unless you specify
+\l{ValueTypeBehavior}{pragma ValueTypeBehavior: Addressable}). If you have
+a value type in C++ with an enumeration you want to expose to QML, you
+need to expose the enumeration separately.
+
+This can be solved by using \l{QML_FOREIGN_NAMESPACE}. First, derive from
+your value type to create a separate C++ type:
+
+\code
+class Person
+{
+ Q_GADGET
+ Q_PROPERTY(QString firstName READ firstName WRITE setFirstName)
+ Q_PROPERTY(QString lastName READ lastName WRITE setLastName)
+ QML_VALUE_TYPE(person)
+public:
+ enum TheEnum { A, B, C };
+ Q_ENUM(TheEnum)
+ //...
+};
+
+class PersonDerived: public Person
+{
+ Q_GADGET
+};
+\endcode
+
+Then expose the derived type as a foreign namespace:
+
+\code
+namespace PersonDerivedForeign
+{
+ Q_NAMESPACE
+ QML_NAMED_ELEMENT(Person)
+ QML_FOREIGN_NAMESPACE(PersonDerived)
+}
+\endcode
+
+This produces a \l{qtqml-typesystem-namespaces.html}{QML Namespace}
+called \c Person (upper case) with an enumeration called \c TheEnum and
+values \c{A}, \c{B}, and \c{C}. Then you can write the following in QML:
+
+\qml
+ someProperty: Person.A
+\endqml
+
+At the same time you can still use your value type called \c person
+(lower case) exactly as before.
+
\section2 Registering Non-Instantiable Types
Sometimes a QObject-derived class may need to be registered with the QML type
@@ -192,7 +275,7 @@ not be instantiable
should not be instantiable from QML
\endlist
-The \l {Qt QML} module provides several macros for registering non-instantiable
+The \l {Qt Qml} module provides several macros for registering non-instantiable
types:
\list
@@ -256,6 +339,7 @@ be aware that properties of such a singleton type cannot be bound to.
See \l{QML_SINGLETON} for more information on how implement and
register a new singleton type, and how to use an existing singleton type.
+See \l{Singletons in QML} for more in-depth information about singletons.
\note Enum values for registered types in QML should start with a capital.
@@ -331,7 +415,7 @@ available when \c MyTypes version 1.1 or higher is imported. Imports of
\c MyTypes version 1.0 remain unaffected.
For the same reason, new types introduced in later versions should be tagged
-with the QML_ADDED_IN_MINOR_VERSION macro.
+with the QML_ADDED_IN_VERSION macro.
This feature of the language allows for behavioural changes to be made
without breaking existing applications. Consequently QML module authors
@@ -361,7 +445,11 @@ type definition allows the programmer to supply an additional type, known as the
\e{extension type}, when registering the class. Its members are transparently
merged with the original target class when used from within QML. For example:
-\snippet referenceexamples/extended/example.qml 0
+\qml
+QLineEdit {
+ leftMargin: 20
+}
+\endqml
The \c leftMargin property is a new property added to an existing C++ type, \l
QLineEdit, without modifying its source code.
@@ -380,9 +468,6 @@ extended property is accessed. The extension class is created and the target
object is passed in as the parent. When the property on the original is
accessed, the corresponding property on the extension object is used instead.
-The \l{Extending QML - Extension Objects Example}{Extension Objects Example}
-demonstrates a usage of extension objects.
-
\section2 Registering Foreign Types
There may be C++ types that cannot be modified to hold the above mentioned
@@ -861,7 +946,7 @@ its properties have been set. For example, this may be the case if the
initialization is costly, or if the initialization should not be performed until
all property values have been initialized.
-The \l {Qt QML} module provides the QQmlParserStatus to be subclassed for these
+The \l {Qt Qml} module provides the QQmlParserStatus to be subclassed for these
purposes. It defines a number of virtual methods that are invoked at
various stages during component instantiation. To receive these notifications, a
C++ class should inherit QQmlParserStatus and also notify the Qt meta system
diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
index e3b3288a98..6031d0eebb 100644
--- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
+++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
@@ -39,6 +39,14 @@ Registration is required for Q_GADGET types, as they don't derive from a known
common base and can't be made available automatically. Without registration,
their properties and methods are inaccessible.
+You can make C++ types from a different module available in your own module by
+adding a dependency to your \l{qt_add_qml_module} call using the \e DEPENDENCIES
+option. You may, for example, want to depend on QtQuick so that your QML-exposed
+C++ types can use \l QColor as method arguments and return values. QtQuick
+exposes \l QColor as a \l {QML Value Types}{value type} \e color. Such
+dependencies may be automatically inferred at run time, but you should not rely
+on this.
+
Also note that a number of the important concepts covered in this document are
demonstrated in the \l{Writing QML Extensions with C++} tutorial.
@@ -83,7 +91,8 @@ Instead of:
\badcode
using FooEnum = Foo::Enum;
-class Bar : public QObject {
+class Bar : public QObject
+{
Q_OBJECT
Q_PROPERTY(FooEnum enum READ enum WRITE setEnum NOTIFY enumChanged)
};
@@ -92,37 +101,46 @@ class Bar : public QObject {
Refer to the type directly:
\code
-class Bar : public QObject {
+class Bar : public QObject
+{
Q_OBJECT
Q_PROPERTY(Foo::Enum enum READ enum WRITE setEnum NOTIFY enumChanged)
};
\endcode
+In order to make \c Message available you need to use \l{QML_ELEMENT} in C++
+and \l{qt_add_qml_module} in CMake.
+
\code
class Message : public QObject
{
Q_OBJECT
+ QML_ELEMENT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
- void setAuthor(const QString &a) {
+ void setAuthor(const QString &a)
+ {
if (a != m_author) {
m_author = a;
emit authorChanged();
}
}
- QString author() const {
+
+ QString author() const
+ {
return m_author;
}
+
signals:
void authorChanged();
+
private:
QString m_author;
};
\endcode
-If an instance of this class was \l{Embedding C++ Objects into QML with Context
-Properties}{set as a context property} when loading a file named \c MyItem.qml
-from C++:
+An instance of \c Message can be passed as required property to a file called
+\c MyItem.qml to make it available:
\code
int main(int argc, char *argv[]) {
@@ -130,7 +148,7 @@ from C++:
QQuickView view;
Message msg;
- view.engine()->rootContext()->setContextProperty("msg", &msg);
+ view.setInitialProperties({{"msg", &msg}});
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
@@ -142,9 +160,11 @@ Then, the \c author property could be read from \c MyItem.qml:
\qml
// MyItem.qml
-import QtQuick 2.0
+import QtQuick
Text {
+ required property Message msg
+
width: 100; height: 100
text: msg.author // invokes Message::author() to get this value
@@ -381,6 +401,8 @@ that is a public slot:
class MessageBoard : public QObject
{
Q_OBJECT
+ QML_ELEMENT
+
public:
Q_INVOKABLE bool postMessage(const QString &msg) {
qDebug() << "Called the C++ method with" << msg;
@@ -394,7 +416,7 @@ that is a public slot:
};
\endcode
-If an instance of \c MessageBoard was set as the context data for a file \c
+If an instance of \c MessageBoard was set as the required property for a file \c
MyItem.qml, then \c MyItem.qml could invoke the two methods as shown in the
examples below:
@@ -408,7 +430,7 @@ examples below:
MessageBoard msgBoard;
QQuickView view;
- view.engine()->rootContext()->setContextProperty("msgBoard", &msgBoard);
+ view.setInitialProperties({{"msgBoard", &msgBoard}});
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
@@ -423,6 +445,8 @@ examples below:
import QtQuick 2.0
Item {
+ required property MessageBoard msgBoard
+
width: 100; height: 100
MouseArea {
diff --git a/src/qml/doc/src/cppintegration/exposecppstate.qdoc b/src/qml/doc/src/cppintegration/exposecppstate.qdoc
new file mode 100644
index 0000000000..8abd498c49
--- /dev/null
+++ b/src/qml/doc/src/cppintegration/exposecppstate.qdoc
@@ -0,0 +1,66 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtqml-cppintegration-exposecppstate.html
+\title Exposing State from C++ to QML
+\brief Description of how to expose global state from C++ to QML
+
+It is often desirable to expose some properties from C++ to all QML elements in a
+particular component, all QML elements in a module, or even all QML elements
+overall. You can do this by introducing singletons or by adding properties to the
+root objects of select components.
+
+\section1 Using Singletons
+
+If you want to expose a number of global properties to all elements in a module
+or all elements overall, you can define a singleton in C++. To do this, add the
+\l{QML_ELEMENT} or \l{QML_NAMED_ELEMENT} macros and the \l{QML_SINGLETON} macro
+to a class containing the properties you want to expose as \l{Q_PROPERTY}
+declarations:
+
+\snippet qml/exposing-state/singleton.h 0
+
+Now you can access the \e thing property of the singleton from any QML code that
+imports this module:
+
+\snippet qml/exposing-state/useSingleton.qml 0
+
+If you have placed your QML files in the same directory as the module (which
+is highly recommended), the singleton is available from the implicit import
+within your module. You don't need to import anything explicitly. If not, or if
+you want to access the \e thing property from other modules, you do need to
+import the module the singleton belongs to.
+
+In order to set the value of the property from C++, you may need to retrieve the
+singleton instance. For this purpose you may use
+\l{QQmlEngine::singletonInstance}. The preferred way to do this is by giving a
+module and type name as parameters:
+
+\snippet qml/exposing-state/singleton.h 1
+
+\section1 Using Object Properties
+
+If you want to expose some properties to only the QML elements in a specific
+component, you can add them as regular properties to the root object of the
+component. In order to make sure they are actually set in all cases, you can
+make them \l{Required Properties}. You might write your QML component as
+follows:
+
+\snippet qml/exposing-state/RequiredProperties.qml 0
+
+We use an ID for the root element of the component and reference the property
+by ID and name from any inner objects. In order to safely make the ID of
+the root element available to any nested components, we use
+\l{ComponentBehavior}.
+
+Then, in C++, when you create an object from such a component, you need to make
+sure to call the \l{QQmlComponent::createWithInitialProperties},
+\l{QQmlApplicationEngine::setInitialProperties}, or
+\l{QQuickView::setInitialProperties} in order to initialize the properties. For
+example:
+
+\snippet qml/exposing-state/createWithInitialProperties.cpp 0
+
+This is assuming your module URI is \e MyModule and the module is available in
+the QML import path.
+*/
diff --git a/src/qml/doc/src/cppintegration/extending-tutorial-advanced.qdoc b/src/qml/doc/src/cppintegration/extending-tutorial-advanced.qdoc
new file mode 100644
index 0000000000..7e489276ac
--- /dev/null
+++ b/src/qml/doc/src/cppintegration/extending-tutorial-advanced.qdoc
@@ -0,0 +1,380 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tutorials-extending-qml-advanced-example.html
+\meta tags{qml,extensions,advanced}
+
+\title Writing advanced QML Extensions with C++
+\brief Tutorial about advanced extensions to QML with Qt C++.
+
+
+\section1 BirthdayParty Base Project
+\c extending-qml-advanced/advanced1-Base-project
+
+This tutorial uses the example of a birthday party to demonstrate some of
+the features of QML. The code for the various features explained below is
+based on this birthday party project and relies on some of the material in the
+first tutorial on \l {Writing QML Extensions with C++}{QML extensions}. This
+simple example is then expanded upon to illustrate the various QML extensions
+explained below. The complete code for each new extension to the code can be
+found in the tutorials at the location specified under each section's title or
+by following the link to the code at the very end of this page.
+
+\image extending-qml-advanced-word-cloud.png
+
+The base project defines the \c Person class and the \c BirthdayParty class,
+which model the attendees and the party itself respectively.
+\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/person.h
+ \skipto class
+ \printuntil QML_ELEMENT
+ \dots
+ \skipuntil private:
+ \printuntil /\};/
+
+\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h
+ \skipto class
+ \printuntil QML_ELEMENT
+ \dots
+ \skipto Person *m_host = nullptr;
+ \printuntil /\};/
+
+All the information about the party can then be stored in the corresponding QML
+file.
+\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml
+ \skipto BirthdayParty
+ \printuntil /^\}/
+
+The \c main.cpp file creates a simple shell application that displays whose
+birthday it is and who is invited to their party.
+\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/main.cpp
+ \skipto engine
+ \printuntil }
+
+The app outputs the following summary of the party.
+\badcode
+"Bob Jones" is having a birthday!
+They are inviting:
+ "Leo Hodges"
+ "Jack Smith"
+ "Anne Brown"
+\endcode
+
+The following sections go into how to add support for \c Boy and \c Girl
+attendees instead of just \c Person by using inheritance and coercion, how to
+make use of default properties to implicitly assign attendees of the party as
+guests, how to assign properties as groups instead of one by one, how to use
+attached objects to keep track of invited guests' reponses, how to use a
+property value source to display the lyrics of the happy birthday song over
+time, and how to expose third party objects to QML.
+
+
+
+\section1 Inheritance and Coercion
+\c extending-qml-advanced/advanced2-Inheritance-and-coercion
+
+Right now, each attendant is being modelled as a person. This is a bit too
+generic and it would be nice to be able to know more about the attendees. By
+specializing them as boys and girls, we can already get a better idea of who's
+coming.
+
+To do this, the \c Boy and \c Girl classes are introduced, both inheriting from
+\c Person.
+\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
+ \skipto Boy
+ \printuntil /^\};/
+
+\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
+ \skipto Girl
+ \printuntil /^\};/
+
+The \c Person class remains unaltered and the \c Boy and \c Girl C++ classes
+are trivial extensions of it. The types and their QML name are registered with
+the QML engine with \l QML_ELEMENT.
+
+Notice that the \c host and \c guests properties in \c BirthdayParty still take
+instances of \c Person.
+\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.h
+ \skipto BirthdayParty
+ \printuntil QML_ELEMENT
+ \dots
+ \skipto /^\};/
+ \printuntil /^\};/
+
+The implementation of the \c Person class itself has not been changed. However,
+as the \c Person class was repurposed as a common base for \c Boy and \c Girl,
+\c Person should no longer be instantiable from QML directly. An explicit
+\c Boy or \c Girl should be instantiated instead.
+\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
+ \skipto Person
+ \printto Q_OBJECT
+ \dots
+ \skipto QML_ELEMENT
+ \printuntil QML_UNCREATABLE
+ \dots
+ \skipto /^\};/
+ \printuntil /^\};/
+
+While we want to disallow instantiating \c Person from within QML, it still
+needs to be registered with the QML engine so that it can be used as a property
+type and other types can be coerced to it. This is what the QML_UNCREATABLE
+macro does. As all three types, \c Person, \c Boy and \c Girl, have been
+registered with the QML system, on assignment, QML automatically (and type-safely)
+converts the \c Boy and \c Girl objects into a \c Person.
+
+With these changes in place, we can now specify the birthday party with the
+extra information about the attendees as follows.
+\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml
+ \skipto BirthdayParty
+ \printuntil /^\}/
+
+
+
+\section1 Default Properties
+\c extending-qml-advanced/advanced3-Default-properties
+
+Currently, in the QML file, each property is assigned explicitly. For example,
+the \c host property is assigned a \c Boy and the \c guests property is
+assigned a list of \c Boy or \c Girl. This is easy but it can be made a bit
+simpler for this specific use case. Instead of assigning the \c guests property
+explicitly, we can add \c Boy and \c Girl objects inside the party directly
+and have them assigned to \c guests implicitly. It makes sense that all the
+attendees that we specify, and that are not the host, are guests. This change
+is purely syntactical but it can add a more natural feel in many situations.
+
+The \c guests property can be designated as the default property of
+\c BirthdayParty. Meaning that each object created inside of a \c BirthdayParty
+is implicitly appended to the default property \c guests. The resulting QML
+looks like this.
+\quotefromfile tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml
+ \skipto BirthdayParty
+ \printuntil /^\}/
+
+The only change required to enable this behavior is to add the \c DefaultProperty
+class info annotation to \c BirthdayParty to designate \c guests as its default
+property.
+\quotefromfile tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.h
+ \skipto class
+ \printuntil QML_ELEMENT
+ \dots
+ \skipto /^\};/
+ \printuntil /^\};/
+
+You may already be familiar with this mechanism. The default property for all
+descendants of \c Item in QML is the \c data property. All elements not
+explicitly added to a property of an \c Item will be added to \c data. This
+makes the structure clear and reduces unnecessary noise in the code.
+
+\sa {Specifying Default and Parent Properties for QML Object Types}
+
+
+
+\section1 Grouped Properties
+\c extending-qml-advanced/advanced4-Grouped-properties
+
+More information is needed about the shoes of the guests. Aside from their
+size, we also want to store the shoes' color, brand, and price. This
+information is stored in a \c ShoeDescription class.
+\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h
+ \skipto ShoeDescription
+ \printuntil price
+ \dots
+ \skipto /^\};/
+ \printuntil /^\};/
+
+Each person now has two properties, a \c name and a shoe description \c shoe.
+\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h
+ \skipto Person
+ \printuntil shoe
+ \dots
+ \skipto /^\};/
+ \printuntil /^\};/
+
+Specifying the values for each element of the shoe description works but is a
+bit repetitive.
+\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml
+ \skipto Girl
+ \printuntil }
+
+Grouped properties provide a more elegant way of assigning these properties.
+Instead of assigning the values to each property one-by-one, the individual
+values can be passed as a group to the \c shoe property making the code more
+readable. No changes are required to enable this feature as it is available by
+default for all of QML.
+\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml
+ \skipto host
+ \printuntil /^....}/
+
+\sa {Grouped Properties}
+
+
+
+\section1 Attached Properties
+\c extending-qml-advanced/advanced5-Attached-properties
+
+The time has come for the host to send out invitations. To keep track of which
+guests have responded to the invitation and when, we need somewhere to store
+that information. Storing it in the \c BirthdayParty object iself would not
+really fit. A better way would be to store the responses as attached objects to
+the party object.
+
+First, we declare the \c BirthdayPartyAttached class which holds the guest reponses.
+\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h
+ \skipto BirthdayPartyAttached
+ \printuntil QML_ANONYMOUS
+ \dots
+ \skipto /^\};/
+ \printuntil /^\};/
+
+And we attach it to the \c BirthdayParty class and define
+\c qmlAttachedProperties() to return the attached object.
+\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h
+ \skipto /BirthdayParty : public QObject/
+ \printuntil /^{/
+ \dots
+ \skipto QML_ATTACHED
+ \printuntil QML_ATTACHED
+ \dots
+ \skipto qmlAttachedProperties
+ \printuntil qmlAttachedProperties
+ \skipto /^\};/
+ \printuntil /^\};/
+
+Now, attached objects can be used in the QML to hold the rsvp information of the invited guests.
+\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml
+ \skipto BirthdayParty
+ \printuntil /^}/
+
+Finally, the information can be accessed in the following way.
+\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp
+ \skipto rsvpDate
+ \printuntil attached->property("rsvp").toDate();
+
+The program outputs the following summary of the party to come.
+\badcode
+"Jack Smith" is having a birthday!
+He is inviting:
+ "Robert Campbell" RSVP date: "Wed Mar 1 2023"
+ "Leo Hodges" RSVP date: "Mon Mar 6 2023"
+\endcode
+
+\sa {Providing Attached Properties}
+
+
+
+\section1 Property Value Source
+\c extending-qml-advanced/advanced6-Property-value-source
+
+During the party the guests have to sing for the host. It would be handy if the
+program could display the lyrics customized for the occasion to help the
+guests. To this end, a property value source is used to generate the verses of
+the song over time.
+\quotefromfile tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h
+ \skipto class
+ \printuntil Q_INTERFACES
+ \dots
+ \skipto setTarget
+ \printuntil setTarget
+ \skipto /^\};/
+ \printuntil /^\};/
+
+The class \c HappyBirthdaySong is added as a value source. It must inherit from
+\c QQmlPropertyValueSource and implement the QQmlPropertyValueSource interface
+with the Q_INTERFACES macro. The \c setTarget() function is used to define
+which property this source acts upon. In this case, the value source writes to
+the \c announcement property of the \c BirthdayParty to display the lyrics
+over time. It has an internal timer that causes the \c announcement
+property of the party to be set to the next line of the lyrics repeatedly.
+
+In QML, a \c HappyBirthdaySong is instantiated inside the \c BirthdayParty. The
+\c on keyword in its signature is used to specify the property that the value
+source targets, in this case \c announcement. The \c name property of the
+\c HappyBirthdaySong object is also \l {Property Binding}{bound} to the name of
+the host of the party.
+\quotefromfile tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml
+ \skipto BirthdayParty
+ \printuntil }
+ \dots
+ \skipto /^\}/
+ \printuntil /^\}/
+
+The program displays the time at which the party started using the
+\c partyStarted signal and then prints the following happy birthday verses
+over and over.
+\badcode
+Happy birthday to you,
+Happy birthday to you,
+Happy birthday dear Bob Jones,
+Happy birthday to you!
+\endcode
+
+\sa {Property Value Sources}
+
+
+
+\section1 Foreign objects integration
+\c extending-qml-advanced/advanced7-Foreign-objects-integration
+
+Instead of just printing the lyrics out to the console, the attendees would
+like to use a more fancy display with support for colors. They would like to
+integrate it in the project but currently it is not possible to configure the
+screen from QML because it comes from a third party library. To solve this, the
+necessary types need to be exposed to the QML engine so its properties are
+available for modification in QML directly.
+
+The display can be controlled by the \c ThirdPartyDisplay class. It has
+properties to define the content and the foreground and background colors of the text
+to display.
+\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h
+ \skipto ThirdPartyDisplay
+ \printuntil backgroundColor
+ \dots
+ \skipto };
+ \printuntil };
+
+To expose this type to QML, we can register it with the engine with
+QML_ELEMENT. However, since the class isn't accessible for modification,
+QML_ELEMENT cannot simply be added to it. To register the type with the engine,
+the type needs to be registered from the outside. This is what QML_FOREIGN is
+for. When used in a type in conjunction with other QML macros, the other macros
+apply not to the type they reside in but to the foreign type designated by
+QML_FOREIGN.
+\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h
+ \skipto ForeignDisplay
+ \printuntil };
+
+This way, the BirthdayParty now has a new property with the display.
+\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h
+ \skipuntil BirthdayPartyAttached
+ \skipto BirthdayParty
+ \printto Q_CLASSINFO
+ \dots
+ \skipto };
+ \printuntil };
+
+And, in QML, the colors of the text on the fancy third display can be set explicitly.
+\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml
+ \skipto BirthdayParty
+ \printuntil BirthdayParty
+ \skipto display:
+ \printuntil }
+ \dots
+ \skipto /^}/
+ \printuntil /^}/
+
+Setting the \c announcement property of the BirthdayParty now sends the
+message to the fancy display instead of printing it itself.
+\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp
+ \skipto setAnnouncement
+ \printuntil /^}/
+
+The output then looks like this over and over similar to the previous section.
+\badcode
+[Fancy ThirdPartyDisplay] Happy birthday to you,
+[Fancy ThirdPartyDisplay] Happy birthday to you,
+[Fancy ThirdPartyDisplay] Happy birthday dear Bob Jones,
+[Fancy ThirdPartyDisplay] Happy birthday to you!
+\endcode
+
+\sa {Registering Foreign Types}
+*/
diff --git a/src/qml/doc/src/cppintegration/extending-tutorial.qdoc b/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
index 66c133980d..156ad47089 100644
--- a/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
+++ b/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
@@ -2,11 +2,11 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
-\example tutorials/extending-qml
+\page qtqml-tutorials-extending-qml-example.html
\title Writing QML Extensions with C++
\brief Tutorial about extending QML with Qt C++.
-The \l {Qt QML} module provides a set of APIs for extending QML through
+The \l {Qt Qml} module provides a set of APIs for extending QML through
C++ extensions. You can write extensions to add your own QML types, extend existing
Qt types, or call C/C++ functions that are not accessible from ordinary QML code.
@@ -20,18 +20,19 @@ particular, you may be interested in the sub-topics
\l{qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++ Classes to QML}
and \l {qtqml-cppintegration-definetypes.html}{Defining QML Types from C++}.
-\section1 Running the Tutorial Examples
+\section1 Opening the Tutorial Sources
-The code in this tutorial is available as an example project with subprojects
-associated with each tutorial chapter. In \l{Qt Creator Manual}{Qt Creator}, open
-the \uicontrol Welcome mode and select the tutorial from \uicontrol Examples. In
-\uicontrol Edit mode, expand the \e extending-qml project, right-click on the
-subproject (chapter) you want to run and select \uicontrol Run.
+The code in this tutorial is available as part of the Qt sources.
+If you installed Qt with the \QOI, you can
+find the sources in the Qt installation directory under
+Examples/Qt-\QtVersion/qml/tutorials/extending-qml/.
-\section1 Creating Tutorial Project
+\section1 Creating Project from Scratch
-We create a new project using the \e {Qt Quick Application} template in Qt Creator,
-as instructed in \l {Qt Creator: Creating Qt Quick Projects}.
+Alternatively, you can follow the tutorial by creating the sources from scratch:
+For each chapter, create a new project using the \e {Qt Quick Application} template
+in Qt Creator, as instructed in \l {Qt Creator: Creating Qt Quick Projects}.
+Then follow along by adapting and extending the generated skeleton code.
\section1 Chapter 1: Creating a New Type
\c extending-qml/chapter1-basics
@@ -54,7 +55,7 @@ a version of 1.0.
We want this \c PieChart type to be usable from QML like this:
\qml
- import Charts 1.0
+ import Charts
PieChart {
width: 100; height: 100
@@ -89,7 +90,7 @@ operations with the QPainter API, we can just subclass QQuickItem.
The \c PieChart class defines the two properties, \c name and \c color, with the
Q_PROPERTY macro, and overrides QQuickPaintedItem::paint(). The \c PieChart
class is registered using the QML_ELEMENT macro, to allow it to be used from
-QML. If you don't register the class, \c app.qml won't be able to create a
+QML. If you don't register the class, \c App.qml won't be able to create a
\c PieChart.
\section2 qmake Setup
@@ -111,8 +112,7 @@ Similarly, for the registration to take effect when using CMake, use the
The class implementation in \c piechart.cpp simply sets and returns the
\c m_name and \c m_color values as appropriate, and implements \c paint() to
-draw a simple pie chart. It also turns off the QGraphicsItem::ItemHasNoContents
-flag to enable painting:
+draw a simple pie chart:
\snippet tutorials/extending-qml/chapter1-basics/piechart.cpp 0
\dots 0
@@ -121,10 +121,10 @@ flag to enable painting:
\section2 QML Usage
Now that we have defined the \c PieChart type, we will use it from QML. The \c
-app.qml file creates a \c PieChart item and displays the pie chart's details
+App.qml file creates a \c PieChart item and displays the pie chart's details
using a standard QML \l Text item:
-\snippet tutorials/extending-qml/chapter1-basics/app.qml 0
+\snippet tutorials/extending-qml/chapter1-basics/App.qml 0
Notice that although the color is specified as a string in QML, it is automatically
converted to a QColor object for the PieChart \c color property. Automatic conversions are
@@ -132,7 +132,7 @@ provided for various other \l {QML Value Types}{value types}. For example, a str
like "640x480" can be automatically converted to a QSize value.
We'll also create a C++ application that uses a QQuickView to run and
-display \c app.qml.
+display \c App.qml.
Here is the application \c main.cpp:
@@ -162,18 +162,14 @@ Now we can build and run the application:
cannot update the binding if the \c name value changes. This is addressed in
the following chapters.
-The source code from the following files are referred to in this chapter:
-\noautolist
-\generatelist examplefiles .*chapter1.*
-
\section1 Chapter 2: Connecting to C++ Methods and Signals
\c extending-qml/chapter2-methods
Suppose we want \c PieChart to have a "clearChart()" method that erases the
-chart and then emits a "chartCleared" signal. Our \c app.qml would be able
+chart and then emits a "chartCleared" signal. Our \c App.qml would be able
to call \c clearChart() and receive \c chartCleared() signals like this:
-\snippet tutorials/extending-qml/chapter2-methods/app.qml 0
+\snippet tutorials/extending-qml/chapter2-methods/App.qml 0
\image extending-tutorial-chapter2.png
@@ -205,8 +201,7 @@ disappears, and the application outputs:
qml: The chart has been cleared
\endcode
-The source code from the following files are referred to in this chapter:
-\generatelist examplefiles .*chapter2.*
+
\section1 Chapter 3: Adding Property Bindings
\c extending-qml/chapter3-bindings
@@ -218,7 +213,7 @@ other types' values when property values are changed.
Let's enable property bindings for the \c color property. That means
if we have code like this:
-\snippet tutorials/extending-qml/chapter3-bindings/app.qml 0
+\snippet tutorials/extending-qml/chapter3-bindings/App.qml 0
\image extending-tutorial-chapter3.png
@@ -256,8 +251,7 @@ automatically updated and cannot be used as flexibly in QML. Also, since
bindings are invoked so often and relied upon in QML usage, users of your
custom QML types may see unexpected behavior if bindings are not implemented.
-The source code from the following files are referred to in this chapter:
-\generatelist examplefiles .*chapter3.*
+
\section1 Chapter 4: Using Custom Property Types
@@ -301,7 +295,7 @@ For example, let's replace the use of the \c property with a type called
"PieSlice" that has a \c color property. Instead of assigning a color,
we assign an \c PieSlice value which itself contains a \c color:
-\snippet tutorials/extending-qml/chapter4-customPropertyTypes/app.qml 0
+\snippet tutorials/extending-qml/chapter4-customPropertyTypes/App.qml 0
Like \c PieChart, this new \c PieSlice type inherits from QQuickPaintedItem and declares
its properties with Q_PROPERTY():
@@ -346,8 +340,7 @@ Using CMake:
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt 1
\dots
-The source code from the following files are referred to in this chapter:
-\generatelist examplefiles .*chapter4.*
+
\section1 Chapter 5: Using List Property Types
\c extending-qml/chapter5-listproperties
@@ -356,7 +349,7 @@ Right now, a \c PieChart can only have one \c PieSlice. Ideally a chart would
have multiple slices, with different colors and sizes. To do this, we could
have a \c slices property that accepts a list of \c PieSlice items:
-\snippet tutorials/extending-qml/chapter5-listproperties/app.qml 0
+\snippet tutorials/extending-qml/chapter5-listproperties/App.qml 0
\image extending-tutorial-chapter5.png
@@ -391,14 +384,13 @@ The \c PieSlice class has also been modified to include \c fromAngle and \c angl
properties and to draw the slice according to these values. This is a straightforward
modification if you have read the previous pages in this tutorial, so the code is not shown here.
-The source code from the following files are referred to in this chapter:
-\generatelist examplefiles .*chapter5.*
+
\section1 Chapter 6: Writing an Extension Plugin
\c extending-qml/chapter6-plugins
-Currently the \c PieChart and \c PieSlice types are used by \c app.qml,
+Currently the \c PieChart and \c PieSlice types are used by \c App.qml,
which is displayed using a QQuickView in a C++ application. An alternative
way to use our QML extension is to create a plugin library to make it available
to the QML engine as a new QML import module. This allows the \c PieChart and
@@ -456,19 +448,18 @@ by the module:
Now we have a QML module that can be imported to any application, provided that the
QML engine knows where to find it. The example contains an executable that loads
-\c app.qml, which uses the \c {import Charts 1.0} statement. Alternatively, you can
+\c App.qml, which uses the \c {import Charts 1.0} statement. Alternatively, you can
load the QML file using the \l {Prototyping with the QML Runtime Tool}{qml tool},
setting the import path to the current directory so that it finds the \c qmldir file:
\code
- qml -I . app.qml
+ qml -I . App.qml
\endcode
The module "Charts" will be loaded by the QML engine, and the types provided by that
module will be available for use in any QML document which imports it.
-The source code from the following files are referred to in this chapter:
-\generatelist examplefiles .*chapter6.*
+
\section1 Chapter 7: Summary
@@ -507,4 +498,6 @@ Or randomly add and remove slices from time to time using \l{Property Value Sour
}
\endcode
+\note To continue learning about QML extensions and features follow the
+\l {Writing advanced QML Extensions with C++} tutorial.
*/
diff --git a/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc b/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc
index 242a6a2fbc..aac9c080d8 100644
--- a/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc
+++ b/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc
@@ -121,7 +121,8 @@ QJSManagedValue is similar to QJSValue, with a few differences:
\list
\li The constructors (except for the default and move constructor2) require
passing a QJSEngine pointer.
-\li Methods like \c deleteProperty and \l isSymbol are added.
+\li Methods like \l {QJSManagedValue::}{deleteProperty} and
+ \l {QJSManagedValue::}{isSymbol} are added.
\li If QJSManagedValue methods encounter an exception, they leave it intact.
\endlist
diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc
index 48911bc774..b14c54a24e 100644
--- a/src/qml/doc/src/cppintegration/topic.qdoc
+++ b/src/qml/doc/src/cppintegration/topic.qdoc
@@ -5,8 +5,9 @@
\page qtqml-cppintegration-overview.html
\title Overview - QML and C++ Integration
\brief Highlights important points about integrating C++ with QML.
+\ingroup explanations-programminglanguages
-QML is designed to be easily extensible through C++ code. The classes in the \l {Qt QML} module
+QML is designed to be easily extensible through C++ code. The classes in the \l {Qt Qml} module
enable QML objects to be loaded and manipulated from C++, and the nature of QML engine's
integration with Qt's \l{Meta Object System}{meta object system} enables C++ functionality to be
invoked directly from QML. This allows the development of hybrid applications which are implemented
@@ -20,7 +21,7 @@ with QML and JavaScript within \l{qtqml-documents-topic.html}{QML documents}, an
C++
\li Use and invoke some C++ functionality from QML (for example, to invoke your application logic,
use a data model implemented in C++, or call some functions in a third-party C++ library)
-\li Access functionality in the \l {Qt QML} or \l {Qt Quick} C++ API (for example, to dynamically generate
+\li Access functionality in the \l {Qt Qml} or \l {Qt Quick} C++ API (for example, to dynamically generate
images using QQuickImageProvider)
\li Implement your own \l{qtqml-typesystem-objecttypes.html}{QML object types} from C++
\unicode{0x2014} whether for use within your own specific application, or for distribution to others
@@ -48,11 +49,15 @@ methods and signals to be accessed from QML
These are the most common methods of accessing C++ functionality from QML code; for more options and
details, see the main documentation pages that are described in the sections further below.
-Additionally, aside from the ability to access C++ functionality from QML, the \l {Qt QML} module also
+Additionally, aside from the ability to access C++ functionality from QML, the \l {Qt Qml} module also
provides ways to do the reverse and manipulate QML objects from C++ code. See
\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++} for more
details.
+It is often desirable to expose some state as global properties to QML.
+\l{qtqml-cppintegration-exposecppstate.html}{Exposing State from C++ to QML}
+describes how to do this.
+
Finally, the C++ code may be integrated into either a C++ application or a C++ plugin depending on
whether it is to be distributed as a standalone application or a library. A plugin can be integrated
with a QML module that can then be imported and used by QML code in other applications; see
@@ -90,7 +95,7 @@ registered for other purposes: for example, it could be registered as a \e {Sing
single class instance to be imported by QML code, or it could be registered to enable the
enumeration values of a non-instantiable class to be accessible from QML.
-Additionally, the \l {Qt QML} module provides mechanisms to define QML types that integrate with QML
+Additionally, the \l {Qt Qml} module provides mechanisms to define QML types that integrate with QML
concepts like attached properties and default properties.
For more information on registering and creating custom QML types from C++, see the \l
@@ -101,7 +106,7 @@ For more information on registering and creating custom QML types from C++, see
C++ objects and values can be embedded directly into the context (or \e scope) of loaded QML objects
using \e {context properties} and \e {context objects}. This is achieved through the QQmlContext
-class provided by the \l {Qt QML} module, which exposes data to the context of a QML component, allowing
+class provided by the \l {Qt Qml} module, which exposes data to the context of a QML component, allowing
data to be injected from C++ into QML.
See \l{qtqml-cppintegration-contextproperties.html}{Embedding C++ Objects into QML with Context
@@ -118,7 +123,8 @@ dynamically load and introspect objects through the Qt meta object system.
\include warning.qdocinc
For more information on accessing QML objects from C++, see the documentation on
-\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++}.
+\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++},
+and the \l {Exposing Data from C++ to QML} section of the Best Practices page.
\section1 Data Type Conversion Between QML and C++
diff --git a/src/qml/doc/src/examples.qdoc b/src/qml/doc/src/examples.qdoc
deleted file mode 100644
index 4a4fdf0454..0000000000
--- a/src/qml/doc/src/examples.qdoc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
-\group qmlextendingexamples
-\title Qt QML Examples
-\brief List of Qt QML examples for reference.
-
-The list of examples demonstrating how to extend C++ to QML or the other way
-around.
-
-\noautolist
-
-\table
- \row
- \li \l {Extending QML - Adding Types Example}
- \li Exporting C++ Classes
- \row
- \li \l {Extending QML - Object and List Property Types Example}
- \li Exporting C++ Properties
- \row
- \li \l {Extending QML - Extension Objects Example}
- \li Extension Objects
- \row
- \li \l {Extending QML - Inheritance and Coercion Example}
- \li C++ Inheritance and Coercion
- \row
- \li \l {Extending QML - Methods Example}
- \li Methods Support
- \row
- \li \l {Extending QML - Attached Properties Example}
- \li Attached Properties
- \row
- \li \l {Extending QML - Signal Support Example}
- \li Signal Support
- \row
- \li \l {Extending QML - Property Value Source Example}
- \li Property Value Source
- \row
- \li \l {Extending QML - Default Property Example}
- \li Default Property
- \row
- \li \l {Extending QML - Grouped Properties Example}
- \li Grouped Properties
-\endtable
-
-*/
diff --git a/src/qml/doc/src/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc
index c120257247..ac22729d63 100644
--- a/src/qml/doc/src/external-resources.qdoc
+++ b/src/qml/doc/src/external-resources.qdoc
@@ -37,6 +37,10 @@
\title Qt Creator: QML Profiler
*/
/*!
+ \externalpage https://doc.qt.io/qtcreator/creator-live-preview-desktop.html
+ \title Qt Creator: QML Preview On Desktop
+*/
+/*!
\externalpage https://fontawesome.com/
\title Font Awesome
*/
@@ -53,3 +57,20 @@
\externalpage https://cmake.org/cmake/help/latest/command/install.html#files
\title install(FILES)
*/
+/*!
+ \externalpage https://developer.android.com/reference/android/view/View
+ \title Android: View
+*/
+/*!
+ \externalpage https://developer.android.com/reference/java/security/InvalidParameterException
+ \title Android: InvalidParameterException
+*/
+/*!
+ \externalpage https://developer.android.com/reference/java/lang/ClassCastException
+ \title Android: ClassCastException
+*/
+/*!
+ \externalpage https://developer.android.com/topic/libraries/view-binding
+ \title Android: View binding
+*/
+
diff --git a/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.qdocinc b/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.qdocinc
index 9152726398..0125a1c4c2 100644
--- a/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.qdocinc
+++ b/src/qml/doc/src/includes/cmake-qml-qt-finalize-target-warning.qdocinc
@@ -1,7 +1,9 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+//! [warning]
\warning If you are using a CMake version lower than 3.19, make sure that you
pass the \c MANUAL_FINALIZATION option to
\l{qt_add_executable}{qt6_add_executable()}, and then call
\l{qt_finalize_target}{qt6_finalize_target()} before calling this function.
+//! [warning]
diff --git a/src/qml/doc/src/includes/qqmlcomponent.qdoc b/src/qml/doc/src/includes/qqmlcomponent.qdoc
index 77d9fa90e9..fcc6fdc802 100644
--- a/src/qml/doc/src/includes/qqmlcomponent.qdoc
+++ b/src/qml/doc/src/includes/qqmlcomponent.qdoc
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
//! [url-note]
Ensure that the URL provided is full and correct, in particular, use
diff --git a/src/qml/doc/src/includes/qualified-class-name.qdocinc b/src/qml/doc/src/includes/qualified-class-name.qdocinc
new file mode 100644
index 0000000000..fce8b45cc0
--- /dev/null
+++ b/src/qml/doc/src/includes/qualified-class-name.qdocinc
@@ -0,0 +1,7 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+//! [class name must be qualified]
+\note The class name needs to be fully qualified, even if you're already
+ inside the namespace.
+//! [class name must be qualified]
diff --git a/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc b/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc
index 29a4a67460..f20111d154 100644
--- a/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc
+++ b/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc
@@ -11,10 +11,6 @@ useful to delay instantiation of objects until necessary, thereby improving
application startup time. It also allows visual objects to be dynamically
created and added to the scene in reaction to user input or other events.
-See the \l {QML Example - Dynamic Scene}{Dynamic Scene example} for a
-demonstration of the concepts discussed on this page.
-
-
\section1 Creating Objects Dynamically
There are two ways to create objects dynamically from JavaScript. You can
@@ -102,6 +98,12 @@ It is also possible to instantiate components without blocking via the
\section2 Creating an Object from a String of QML
+\warning Creating objects from a string of QML is extremely slow since the engine has to compile the
+passed QML string every time you do it. Furthermore, it's very easy to produce invalid QML when
+programmatically constructing QML code. It's much better to keep your QML components as separate
+files and add properties and methods to customize their behavior than to produce new components by
+string manipulation.
+
If the QML is not defined until runtime, you can create a QML object from
a string of QML using the \l{QtQml::Qt::createQmlObject()}{Qt.createQmlObject()}
function, as in the following example:
diff --git a/src/qml/doc/src/javascript/finetuning.qdoc b/src/qml/doc/src/javascript/finetuning.qdoc
index 14b4c9538d..0e8a913a2a 100644
--- a/src/qml/doc/src/javascript/finetuning.qdoc
+++ b/src/qml/doc/src/javascript/finetuning.qdoc
@@ -20,8 +20,11 @@ Running JavaScript code can be influenced by a few environment variables, partic
considered for JIT compilation. The default value is 3 times.
\row
\li \c{QV4_FORCE_INTERPRETER}
- \li Setting this environment variable disables the JIT and runs all
- functions through the interpreter, no matter how often they are called.
+ \li Setting this environment variable runs all functions and expressions through the
+ interpreter. The JIT is never used, no matter how often a function or expression is
+ called. Functions and expressions may still be compiled ahead of time using
+ \l{qmlcachegen} or \l{qmlsc}, but only the generated byte code is used at run time. Any
+ generated C++ code and the machine code resulting from it is ignored.
\row
\li \c{QV4_JS_MAX_STACK_SIZE}
\li The JavaScript engine reserves a special memory area as a stack to run JavaScript.
@@ -82,6 +85,19 @@ Running JavaScript code can be influenced by a few environment variables, partic
\li Outputs the IR bytecode generated by Qt to the console.
Has to be combined with \c{QML_DISABLE_DISK_CACHE} or already cached bytecode will not
be shown.
+ \row
+ \li \c{QV4_DUMP_BASIC_BLOCKS}
+ \li Outputs the basic blocks of each function compiled ahead of time. The details of the
+ blocks are printed to the console. Additionally, control flow graphs with the byte code
+ for each block are generated in the DOT format for each compiled function. The value of
+ \c {QV4_DUMP_BASIC_BLOCKS} is used as the path to the folder where the DOT files should
+ be generated. If the path is any of ["-", "1", "true"] or if files can't be opened,
+ the graphs are dumped to stdout instead.
+ \row
+ \li \c{QV4_VALIDATE_BASIC_BLOCKS}
+ \li Performs checks on the basic blocks of a function compiled ahead of time to validate
+ its structure and coherence. If the validation fails, an error message is printed to
+ the console.
\endtable
\l{The QML Disk Cache} accepts further environment variables that allow fine tuning its behavior.
diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc
index 04c55224ce..d656bc2234 100644
--- a/src/qml/doc/src/javascript/functionlist.qdoc
+++ b/src/qml/doc/src/javascript/functionlist.qdoc
@@ -209,9 +209,9 @@
\li [Symbol.iterator]()
\endlist
- Additionally, the QML engine adds the following functions to the \l String prototype:
+ Additionally, the QML engine adds the following functions to the \c String prototype:
\list
- \li \l {String::arg}{arg()}
+ \li \l {string}{string::arg()}
\endlist
diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc
index b7950d556e..a27fe48fbe 100644
--- a/src/qml/doc/src/javascript/hostenvironment.qdoc
+++ b/src/qml/doc/src/javascript/hostenvironment.qdoc
@@ -41,37 +41,12 @@ takes an \c int and a \c string parameter, and returns a \c QtObject:
function doThings(a: int, b: string) : QtObject { ... }
\endqml
-Type annotations help tools like \l{Qt Creator} and \l{qmllint} to make sense
+Type annotations help tools like \l{Qt Creator} and \l{qmllint Reference}{qmllint} to make sense
of the code and provide better diagnostics. Moreover, they make functions easier
to use from C++. See
\l {qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++}
for more information.
-By default, type annotations are ignored by the interpreter and the JIT
-compiler, but enforced by \l{qmlcachegen} and \l{qmlsc} when compiling to C++.
-This can lead to differences in behavior if you either pass values that
-are not actually of the declared type or if you modify instances of
-\l{QML Value Types} passed as typed arguments. Value types are passed by
-reference by the interpreter and JIT, but by value when compiled to C++.
-
-You can eliminate those differences by either forcing the interpreter and JIT
-to also respect type annotations or by having \l{qmlcachegen} and \l{qmlsc}
-ignore type annotations. The former has a performance cost when using the
-interpreter or JIT, the latter makes the compilation to C++ avoid any JavaScript
-functions, and any bindings and signal handlers that call JavaScript functions.
-Therefore less code will be compiled to C++.
-
-In order to always enforce type annotations, add the following to your QML
-document:
-\qml
-pragma FunctionSignatureBehavior: Enforced
-\endqml
-
-In order to always ignore type annotations, add the following instead:
-\qml
-pragma FunctionSignatureBehavior: Ignored
-\endqml
-
Type assertions (sometimes called \e as-casts) can also be used in order to cast an object to a
different object type. If the object is actually of the given type, then the type assertion returns
the same object. If not, it returns \c null. In the following snippet we assert that the \c parent
@@ -86,6 +61,17 @@ Item {
The optional chaining (\c{?.}) avoids throwing an exception if the parent is
actually not a rectangle. In that case "red" is chosen as \c parentColor.
+Since Qt 6.7 type annotations are always enforced when calling functions. Values
+are coerced to the required types as necessary. Previously, type annotations were
+ignored by the interpreter and the JIT compiler, but enforced by \l{qmlcachegen}
+and \l{qmlsc} when compiling to C++. This could lead to differences in behavior in
+some corner cases. In order to explicitly request the old behavior of the
+interpreter and JIT you can add the following to your QML document:
+
+\qml
+pragma FunctionSignatureBehavior: Ignored
+\endqml
+
\section1 QML Global Object
The QML JavaScript host environment implements a number of host objects and functions, as
@@ -103,7 +89,7 @@ QML engine can be found in the \l{List of JavaScript Objects and Functions}.
Note that QML makes the following modifications to native objects:
\list
-\li An \l {String::arg}{arg()} function is added to the \l String prototype.
+\li An \l {string}{arg()} function is added to the \c String prototype.
\li Locale-aware conversion functions are added to the \l Date and \l Number prototypes.
\endlist
diff --git a/src/qml/doc/src/javascript/memory.qdoc b/src/qml/doc/src/javascript/memory.qdoc
index c7dcc00d98..54f48f48df 100644
--- a/src/qml/doc/src/javascript/memory.qdoc
+++ b/src/qml/doc/src/javascript/memory.qdoc
@@ -163,7 +163,7 @@ is completely empty, it is decommitted, but the address space is retained
(see \l{Basic Principles} above). If the memory usage grows again, the same
address space is re-used.
-The garbage collector is triggered either manually by calling the gc() function
+The garbage collector is triggered either manually by calling the \l [QML] {Qt::}{gc()} function
or by a heuristic that takes the following aspects into account:
\list
diff --git a/src/qml/doc/src/javascript/number.qdoc b/src/qml/doc/src/javascript/number.qdoc
index 7676a7cd4f..a37fd8051b 100644
--- a/src/qml/doc/src/javascript/number.qdoc
+++ b/src/qml/doc/src/javascript/number.qdoc
@@ -34,21 +34,32 @@
If \a locale is not specified, the default locale will be used.
The following example shows a number formatted for the German locale:
- \code
+ \qml
import QtQuick 2.0
Text {
text: "The value is: " + Number(4742378.423).toLocaleString(Qt.locale("de_DE"))
}
- \endcode
+ \endqml
+
+ You can customize individual fields of the \a{locale} to tightly control
+ the output:
+ \qml
+ let locale = Qt.locale("de_DE");
+ let a = Number(1000).toLocaleString(locale)); // a == 1.000,00
+ locale.numberOptions = Locale.OmitGroupSeparator;
+ let b = Number(1000).toLocaleString(locale)); // b == 1000,00
+ \endqml
You can apply toLocaleString() directly to constants, provided the decimal
is included in the constant, e.g.
- \code
+ \qml
123.0.toLocaleString(Qt.locale("de_DE")) // OK
123..toLocaleString(Qt.locale("de_DE")) // OK
123.toLocaleString(Qt.locale("de_DE")) // fails
- \endcode
+ \endqml
+
+ \sa {QtQml::Locale}{Locale}
*/
/*!
@@ -69,12 +80,14 @@
If \a locale is not supplied the default locale will be used.
For example, using the German locale:
- \code
+ \qml
var german = Qt.locale("de_DE");
var d;
d = Number.fromLocaleString(german, "1234,56") // d == 1234.56
d = Number.fromLocaleString(german, "1.234,56") // d == 1234.56
d = Number.fromLocaleString(german, "1234.56") // throws exception
d = Number.fromLocaleString(german, "1.234") // d == 1234.0
- \endcode
+ \endqml
+
+ \sa {QtQml::Locale}{Locale}
*/
diff --git a/src/qml/doc/src/javascript/qmlglobalobject.qdoc b/src/qml/doc/src/javascript/qmlglobalobject.qdoc
index 0857cabbdc..15b9996ff3 100644
--- a/src/qml/doc/src/javascript/qmlglobalobject.qdoc
+++ b/src/qml/doc/src/javascript/qmlglobalobject.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtqml-javascript-qmlglobalobject.html
@@ -7,100 +7,27 @@
The QML JavaScript host environment implements the following host objects and functions.
-These are built in and can be used from any JavaScript code loaded in QML, without
+They are built-in, so you can use them from any JavaScript code loaded in QML, without
additional imports:
\list
-\li The \l{QmlGlobalQtObject}{Qt object}: This object is specific to QML, and provides helper methods
+\li The \l{QmlGlobalQtObject}{Qt object}: A QML object that offers helper methods
and properties specific to the QML environment.
-\li qsTr(), qsTranslate(), qsTrId(), QT_TR_NOOP(), QT_TRANSLATE_NOOP(), and QT_TRID_NOOP() functions:
- These functions are specific to QML, and provide \l{Overview of the Translation Process}{translation capabilities} to the QML environment.
-\li gc() function: This function is specific to QML, and provides a way to manually trigger garbage collection.
-\li print() function: This function is specific to QML, and provides a simple way to output information to the console.
-\li The \l{Console API}{console object}: This object implements a subset of the \l{http://getfirebug.com/wiki/index.php/Console_API}{FireBug Console API}.
-\li \l{XMLHttpRequest}, DOMException: These objects implement a subset of the \l{http://www.w3.org/TR/XMLHttpRequest/}{W3C XMLHttpRequest specification}.
-\endlist
-
-\note The \l {QJSEngine::}{globalObject()} function cannot be used to modify
-the global object of a \l QQmlEngine. For more information about this, see
+\li \l {Qt::}{qsTr()}, \l {Qt::}{qsTranslate()}, \l {Qt::}{qsTrId()}, \l {Qt::}{qsTrNoOp()},
+ \l {Qt::}{qsTranslateNoOp()}, \l {Qt::}{qsTrIdNoOp()} functions:
+ QML functions that let you translate \l{Mark Strings for Translation}
+ {strings} and \l{Mark Translatable Data Text Strings}{string literals} in the
+ QML environment.
+\li gc() function: A QML function that manually triggers garbage collection.
+\li print() function: A QML function that prints output to the console.
+\li The \l{Console API}{console object}: Implements a subset of the
+ \l{http://getfirebug.com/wiki/index.php/Console_API}{FireBug Console API}.
+\li \l{XMLHttpRequest}, DOMException: Implements a subset of the
+ \l{http://www.w3.org/TR/XMLHttpRequest/}{W3C XMLHttpRequest specification}.
+\endlist
+
+\note You cannot use the \l {QJSEngine::}{globalObject()} function to change
+the global object of a \l QQmlEngine. For more information, see
\l {JavaScript Environment Restrictions}.
-\target XMLHttpRequest
-\section1 XMLHttpRequest
-
-The XMLHttpRequest object, which can be used to asynchronously obtain
-data from over a network.
-
-The XMLHttpRequest API implements the same \l {http://www.w3.org/TR/XMLHttpRequest/}{W3C standard}
-as many popular web browsers with following exceptions:
-\list
-\li QML's XMLHttpRequest does not enforce the same origin policy.
-\endlist
-
-Additionally, the \c responseXML XML DOM tree currently supported by QML is a reduced subset
-of the \l {http://www.w3.org/TR/DOM-Level-3-Core/}{DOM Level 3 Core} API supported in a web
-browser. The following objects and properties are supported by the QML implementation:
-
-\table
-\header
-\li \b {Node}
-\li \b {Document}
-\li \b {Element}
-\li \b {Attr}
-\li \b {CharacterData}
-\li \b {Text}
-
-\row
-\li
-\list
-\li nodeName
-\li nodeValue
-\li nodeType
-\li parentNode
-\li childNodes
-\li firstChild
-\li lastChild
-\li previousSibling
-\li nextSibling
-\li attributes
-\endlist
-
-\li
-\list
-\li xmlVersion
-\li xmlEncoding
-\li xmlStandalone
-\li documentElement
-\endlist
-
-\li
-\list
-\li tagName
-\endlist
-
-\li
-\list
-\li name
-\li value
-\li ownerElement
-\endlist
-
-\li
-\list
-\li data
-\li length
-\endlist
-
-\li
-\list
-\li isElementContentWhitespace
-\li wholeText
-\endlist
-
-\endtable
-
-The \l{Qt Quick Examples - XMLHttpRequest}{XMLHttpRequest example} demonstrates
-how to use the XMLHttpRequest object to make a request and read the response
-headers.
-
*/
diff --git a/src/qml/doc/src/javascript/string.qdoc b/src/qml/doc/src/javascript/string.qdoc
deleted file mode 100644
index ade490d981..0000000000
--- a/src/qml/doc/src/javascript/string.qdoc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
- \qmltype String
- \inqmlmodule QtQml
- \brief The String object represents a string value.
-
- The QML String object extends the JS String object with
- the arg() function.
-
- \sa {ECMA-262}{ECMAScript Language Specification}
-*/
-
-/*!
- \qmlmethod string String::arg(value)
-
- Returns a copy of this string with the lowest numbered place marker replaced by \a value,
- i.e., %1, %2, ..., %99. The following example prints "There are 20 items":
-
- \code
- var message = "There are %1 items"
- var count = 20
- console.log(message.arg(count))
- \endcode
-*/
diff --git a/src/qml/doc/src/javascript/topic.qdoc b/src/qml/doc/src/javascript/topic.qdoc
index 27308cde3a..2302f90fbe 100644
--- a/src/qml/doc/src/javascript/topic.qdoc
+++ b/src/qml/doc/src/javascript/topic.qdoc
@@ -24,6 +24,25 @@ See the documentation page titled
\l{qtqml-javascript-expressions.html}{JavaScript Expressions in QML Documents}
for more information about using JavaScript expressions in QML.
+\section1 Dynamic QML Object Creation from JavaScript
+
+QML supports the dynamic creation of objects from within JavaScript. This is
+useful to delay instantiation of objects until necessary, thereby improving
+application startup time. It also allows visual objects to be dynamically
+created and added to the scene in reaction to user input or other events. This
+functionality can be used in two main ways.
+
+Object can be created dynamically from JavaScript in an imperative way using
+\l{qtqml-javascript-dynamicobjectcreation.html}{dynamic creation of objects}.
+This can be useful, for example, when QML is used as an application scripting
+language.
+
+\note When creating user interfaces, the preferred way of creating objects
+dynamically is to use declarative constructs as these integrate best with the
+QML engine and tooling. Various types exist to enable this functionality such
+as the \l{Loader}, \l{Instantiator}, \l{Repeater} types.
+
+
\section1 JavaScript Resources
Application logic defined in JavaScript functions may be separated into
diff --git a/src/qml/doc/src/javascript/xmlhttprequest.qdoc b/src/qml/doc/src/javascript/xmlhttprequest.qdoc
new file mode 100644
index 0000000000..b8624073c5
--- /dev/null
+++ b/src/qml/doc/src/javascript/xmlhttprequest.qdoc
@@ -0,0 +1,301 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \qmltype XMLHttpRequest
+ \inqmlmodule QtQml
+ \brief Object for sending requests to a server.
+
+ The \c XMLHttpRequest object allows scripts to perform HTTP client functionality,
+ such as submitting form data or loading data asynchronously from a server.
+
+ The \c XMLHttpRequest API is a partial implementation of the
+ \l {https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/}{W3C XHR Level 1 standard}
+ with the following exception:
+ \list
+ \li It does not enforce the same origin policy.
+ \endlist
+
+ \section1 Sending requests
+
+ Use the following steps to send a request using the \c XMLHttpRequest API:
+ \list 1
+ \li Create a \c XMLHttpRequest object.
+ \li Assign a callback function to the \l {XMLHttpRequest::onreadystatechange}{onreadystatechange} signal handler.
+ \li Call \l {XMLHttpRequest::open}{open()} with the appropriate HTTP method and URL to request.
+ \li Call \l {XMLHttpRequest::send}{send()}
+ \endlist
+
+ The callback function handles the HTTP response for a request.
+ It's a good practice to check if the \l{XMLHttpRequest::readyState}{readyState} is \c DONE in the handler,
+ before you use one of the following methods to read the response:
+ \list
+ \li \l {XMLHttpRequest::response}{response}
+ \li \l {XMLHttpRequest::responseText}{responseText}
+ \li \l {XMLHttpRequest::responseXML}{responseXML}
+ \endlist
+
+ The following example demonstrates how to send a request and read the response:
+
+ \snippet qml/XHRForm.qml 0
+
+ The earlier snippet connects the button's clicked signal to an external \c sendRequest function.
+ A resource URL is passed as the first argument, and a callback function to handle UI updates is passed as the second.
+ The \c sendRequest function, which exists in an external \c request.js file, can be implemented like this:
+
+ \snippet qml/xmlhttprequest.js 0
+
+ The earlier snippet follows the four simple steps mentioned at the beginning.
+ It instantiates the \c XMLHttpRequest object first, and assigns a callback function to handle the response.
+ It also calls \l {XMLHttpRequest::open}{open()} with an HTTP method and URL, before it sends the request to the server.
+ Notice that the second argument to \c sendRequest is called at the end of \l {XMLHttpRequest::onreadystatechange}{onreadystatechange},
+ in order to handle UI updates based on the HTTP response.
+
+ Set the \c QML_XHR_DUMP environment variable to \c 1 if you want to debug a request.
+ This will log the following information:
+ \list
+ \li Method type (GET or POST), URL, and body of sent requests.
+ \li URL and body of responses received.
+ \li Network error, if any.
+ \endlist
+
+ \section1 Accessing local files
+
+ By default, you cannot use the \c XMLHttpRequest object to read files from your local file system.
+ If you wish to use this feature to access local files, you can set the following environment variables to \c 1.
+ \list
+ \li QML_XHR_ALLOW_FILE_READ
+ \li QML_XHR_ALLOW_FILE_WRITE
+ \endlist
+ \warning Use this feature only if you know that the application runs trusted QML and JavaScript code.
+
+ \section1 responseXML document
+
+ The \c responseXML XML DOM tree currently supported by QML is a reduced subset of
+ the \l {http://www.w3.org/TR/DOM-Level-3-Core/}{DOM Level 3 Core} API supported in a web browser.
+ The following objects and properties are supported by the QML implementation:
+
+ \table
+ \header
+ \li \b {Node}
+ \li \b {Document}
+ \li \b {Element}
+ \li \b {Attr}
+ \li \b {CharacterData}
+ \li \b {Text}
+
+ \row
+ \li
+ \list
+ \li nodeName
+ \li nodeValue
+ \li nodeType
+ \li parentNode
+ \li childNodes
+ \li firstChild
+ \li lastChild
+ \li previousSibling
+ \li nextSibling
+ \li attributes
+ \endlist
+
+ \li
+ \list
+ \li xmlVersion
+ \li xmlEncoding
+ \li xmlStandalone
+ \li documentElement
+ \endlist
+
+ \li
+ \list
+ \li tagName
+ \endlist
+
+ \li
+ \list
+ \li name
+ \li value
+ \li ownerElement
+ \endlist
+
+ \li
+ \list
+ \li data
+ \li length
+ \endlist
+
+ \li
+ \list
+ \li isElementContentWhitespace
+ \li wholeText
+ \endlist
+
+ \endtable
+*/
+
+/*!
+ \qmlmethod void XMLHttpRequest::abort()
+
+ Cancels the current request.
+
+ It changes the \l {XMLHttpRequest::readyState}{readyState} property to be \c XMLHttpRequest.UNSENT and emits the \c readystatechange signal.
+*/
+
+/*!
+ \qmlmethod string XMLHttpRequest::getAllResponseHeaders()
+
+ Returns a \c String of headers received from the last response.
+
+ The following is an example response header:
+ \code
+ content-encoding: gzip
+ content-type: text/html; charset=UTF-8
+ date: Mon, 06 Feb 2023 09:00:08 GMT
+ expires: Mon, 13 Feb 2023 09:00:08 GMT
+ last-modified: Thu, 17 Oct 2019 07:18:26 GMT
+ \endcode
+
+ \sa {XMLHttpRequest::getResponseHeader}{getResponseHeader()}
+*/
+
+/*!
+ \qmlmethod string XMLHttpRequest::getResponseHeader(headerName)
+
+ Returns either the \a headerName value from the last response, or an empty \c String, if the \a headerName is missing.
+
+ \sa {XMLHttpRequest::getAllResponseHeaders}{getAllResponseHeaders()}
+*/
+
+/*!
+ \qmlmethod void XMLHttpRequest::open(method, url, async)
+
+ Specify the HTTP \a method you want the request to use, as well as the \a url you wish to request.
+ You should make sure to always call this function before \l {XMLHttpRequest::send}{send()}.
+ An optional third parameter \a async can be used to decide whether the request should be asynchronous or not.
+ The default value is \c true.
+
+ Emits the \c readystatechange signal, which calls the \l {XMLHttpRequest::onreadystatechange}{onreadystatechange} handler with
+ the \l {XMLHttpRequest::readyState}{readyState} property set to \c XMLHttpRequest.OPENED.
+*/
+
+/*!
+ \qmlmethod void XMLHttpRequest::send(data)
+
+ Sends the request to the server.
+ You can use the optional argument \a data to add extra data to the body of the request.
+ This can be useful for POST requests, where you typically want the request to contain extra data.
+
+ The \l {XMLHttpRequest::readyState}{readyState} property is updated once a response has been received from the server,
+ and while the response is being processed. It will first be set to \c HEADERS_RECEIVED, then to \c LOADING,
+ and finally \c DONE, once the response has been fully processed.
+ The \c readystatechange signal is emitted every time \l {XMLHttpRequest::readyState}{readyState} is updated.
+*/
+
+/*!
+ \qmlmethod void XMLHttpRequest::setRequestHeader(header, value)
+
+ Adds a new header to the next request you wish to send.
+ This is a key-value pair, which has the name \a header and the corresponding \a value.
+*/
+
+/*!
+ \qmlmethod void XMLHttpRequest::overrideMimeType(mime)
+ \since 6.6
+
+ Forces the \c XMLHttpRequest to interpret the data received in the next HTTP response,
+ as if it had the MIME type \a mime, rather than the one provided by the server.
+*/
+
+/*!
+ \qmlproperty function XMLHttpRequest::onreadystatechange
+
+ Choose a callback function you want to get invoked whenever the \l {XMLHttpRequest::readyState}{readyState} of the \c XMLHttpRequest object changes.
+
+ \sa {XMLHttpRequest::readyState}{readyState}
+*/
+
+/*!
+ \qmlproperty enumeration XMLHttpRequest::readyState
+ \readonly
+
+ Indicates the current state of the \c XMLHttpRequest object.
+
+ The value can be one of the following:
+ \value XMLHttpRequest.UNSENT The request is not initialized, which is the state before calling \l {XMLHttpRequest::open}{open()}.
+ \value XMLHttpRequest.OPENED The request is initialized, meaning \l {XMLHttpRequest::open}{open()} was previously called, but no further progress.
+ \value XMLHttpRequest.HEADERS_RECEIVED Received a reply from the sever, but the request is not fully processed yet.
+ \value XMLHttpRequest.LOADING Downloading data from the server.
+ \value XMLHttpRequest.DONE Finished processing the request.
+
+ \sa {XMLHttpRequest::onreadystatechange}{onreadystatechange}
+*/
+
+/*!
+ \qmlproperty string XMLHttpRequest::responseURL
+ \readonly
+ \since 6.6
+
+ Returns the url that was used to retrieve the response data, after any redirects have occurred.
+*/
+
+/*!
+ \qmlproperty string XMLHttpRequest::responseText
+ \readonly
+
+ Returns a \c String containing the data received from the last response.
+
+ \sa {XMLHttpRequest::responseXML}{responseXML}
+*/
+
+/*!
+ \qmlproperty var XMLHttpRequest::responseXML
+ \readonly
+
+ Returns either a \c Document, or \c null, if the response content cannot be parsed as XML or HTML.
+ See the \l {responseXML document}{responseXML document} section for more information.
+
+ \sa {XMLHttpRequest::responseText}{responseText}
+*/
+
+/*!
+ \qmlproperty var XMLHttpRequest::response
+ \readonly
+
+ Returns either a \c String, an \c ArrayBuffer, or a \c Document,
+ depending on the \l {XMLHttpRequest::responseType}{responseType} of the last request.
+
+ \sa {XMLHttpRequest::responseType}{responseType}, {XMLHttpRequest::responseText}{responseText}, {XMLHttpRequest::responseXML}{responseXML}
+*/
+
+/*!
+ \qmlproperty string XMLHttpRequest::responseType
+
+ Returns a \c String describing the content type of the last response received.
+ \list
+ \li If the response type is "text" or an empty \c String, the response content is a UTF-16 encoded \c String.
+ \li If the response type is "arraybuffer", it means that the response content is an \c ArrayBuffer containing binary data.
+ \li If the response type is "json", the response content should be a JSON \c Document.
+ \li If the response type is "document", it means that the response content is an XML \c Document, which can be safely read with the \l {XMLHttpRequest::responseXML}{responseXML} property.
+ \endlist
+
+ \sa {XMLHttpRequest::response}{response}
+*/
+
+/*!
+ \qmlproperty int XMLHttpRequest::status
+ \readonly
+
+ Returns the status code for the last response received.
+
+ \sa {XMLHttpRequest::statusText}{statusText}
+*/
+
+/*!
+ \qmlproperty string XMLHttpRequest::statusText
+ \readonly
+
+ Returns a \c String that has the status message associated with the status code for the last response received.
+
+ \sa {XMLHttpRequest::status}{status}
+*/
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index 5c5dc669f7..ecb13f160d 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -9,14 +9,24 @@
class or namespace name as the QML element name.
For example, this makes the C++ class \c Slider available as a QML type
- named \c Slider.
+ named \c Slider. All its properties, invokable methods and enums are exposed.
\code
class Slider : public QObject
{
Q_OBJECT
QML_ELEMENT
- ...
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged FINAL)
+ // ...
+ public:
+ enum Slippiness {
+ Dry, Wet, Icy
+ };
+ Q_ENUM(Slippiness)
+
+ Q_INVOKABLE void slide(Slippiness slippiness);
+
+ // ...
}
\endcode
@@ -46,19 +56,43 @@
import com.mycompany.qmlcomponents 1.0
Slider {
+ value: 12
+ Component.onCompleted: slide(Slider.Icy)
+
// ...
}
\endqml
You can also make namespaces tagged with Q_NAMESPACE available this way, in
- order to expose any enums tagged with Q_ENUM_NS they contain.
+ order to expose any enums tagged with Q_ENUM_NS they contain:
+
+ \code
+ namespace MyNamespace {
+ Q_NAMESPACE
+ QML_ELEMENT
+
+ enum MyEnum {
+ Key1,
+ Key2,
+ };
+ Q_ENUM_NS(MyEnum)
+ }
+ \endcode
+
+ In QML, you can then use the enums:
+
+ \qml
+ Component.onCompleted: console.log(MyNamespace.Key2)
+ \endqml
\b{NOTE:} When classes have the same name but are located in different namespaces using
\l QML_ELEMENT on both of them will cause a conflict.
Make sure to use \l QML_NAMED_ELEMENT() for one of them instead.
+ \include {qualified-class-name.qdocinc} {class name must be qualified}
+
\sa {Choosing the Correct Integration Method Between C++ and QML}, QML_NAMED_ELEMENT(),
- Q_REVISION(), QML_ADDED_IN_MINOR_VERSION()
+ Q_REVISION(), QML_ADDED_IN_VERSION()
*/
/*!
@@ -136,8 +170,8 @@
This macro tells Qt which QML \a interfaces the class implements.
This macro should only be used for interfacing with classes using \l QML_INTERFACE, use \l Q_INTERFACES otherwise.
- It's required in order for declarative registration via \l QML_ELEMENT to function properly.
-
+ It's required in order for declarative registration via \l QML_ELEMENT to
+ function properly.
\sa QML_INTERFACE, Q_INTERFACES
*/
@@ -153,10 +187,10 @@
Some QML types are implicitly uncreatable, in particular types exposed with
\l QML_ANONYMOUS or namespaces exposed with \l QML_ELEMENT or
- \l QML_NAMED_ELEMENT(). For such types, \l QML_UNCREATABLE() can be used to
- provide a custom error message.
+ \l QML_NAMED_ELEMENT().
- Since Qt 6.0 you can use "" instead of a reason to use a standard message instead.
+ Since Qt 6.0 you can use "" instead of a reason to use a standard message
+ instead.
\sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_ANONYMOUS
*/
@@ -173,7 +207,9 @@
\c{T *create(QQmlEngine *, QJSEngine *)} when the type is first accessed.
If both do exist and are accessible, the default constructor is preferred.
If there is no default constructor and no factory function the singleton is
- inaccessible.
+ inaccessible. The QML engine generally assumes ownership of the singleton and
+ will delete it when the engine itself is destroyed. You can prevent this by
+ calling QJSEngine::setObjectOwnership() on the singleton.
In order to declare a default-constructible class as singleton, all you have
to do is add \l QML_SINGLETON:
@@ -300,17 +336,37 @@
engine in order to assert on that.
\sa QML_ELEMENT, QML_NAMED_ELEMENT(),
- qmlRegisterSingletonInstance(), QQmlEngine::singletonInstance()
+ qmlRegisterSingletonInstance(), QQmlEngine::singletonInstance(), {Singletons in QML}
+*/
+
+/*!
+ \macro QML_ADDED_IN_VERSION(MAJOR, MINOR)
+ \relates QQmlEngine
+
+ Declares that the enclosing type or namespace was added in the specified
+ \a{MAJOR}.\a{MINOR} version. The version is assumed to be in line with any
+ revisions given by \l Q_REVISION() macros on methods, slots, or signals,
+ and any REVISION() attributes on properties declared with \l Q_PROPERTY().
+
+ \l QML_ADDED_IN_VERSION() only takes effect if the type or namespace is
+ available in QML, by having a \l QML_ELEMENT, \l QML_NAMED_ELEMENT(),
+ \l QML_ANONYMOUS, or \l QML_INTERFACE macro.
+
+ If the QML module the type belongs to is imported with a lower version than
+ the one determined this way, the QML type is invisible.
+
+ \sa QML_ELEMENT, QML_NAMED_ELEMENT
*/
/*!
\macro QML_ADDED_IN_MINOR_VERSION(VERSION)
\relates QQmlEngine
+ \deprecated [6.7] Use QML_ADDED_IN_VERSION and specify the full version
Declares that the enclosing type or namespace was added in the specified minor
\a VERSION, relative to the module major version. The minor version is assumed
to be in line with any revisions given by \l Q_REVISION() macros on methods,
- slots, or signals, and any REVISION tags on properties declared with
+ slots, or signals, and any REVISION() attributes on properties declared with
\l Q_PROPERTY().
\l QML_ADDED_IN_MINOR_VERSION() only takes effect if the type or namespace is
@@ -320,17 +376,37 @@
If the QML module the type belongs to is imported with a lower version than
the one determined this way, the QML type is invisible.
- \sa QML_ELEMENT, QML_NAMED_ELEMENT()
+ \sa QML_ADDED_IN_VERSION, QML_ELEMENT, QML_NAMED_ELEMENT
+*/
+
+/*!
+ \macro QML_REMOVED_IN_VERSION(MAJOR, MINOR)
+ \relates QQmlEngine
+
+ Declares that the enclosing type or namespace was removed in the specified
+ \a{MAJOR}.\a{MINOR} version. This is primarily useful when replacing the
+ implementation of a QML type. If a corresponding \l QML_ADDED_IN_VERSION()
+ is present on a different type or namespace of the same QML name, then the
+ removed type is used when importing versions of the module lower than
+ \a{MAJOR}.\a{MINOR}, and the added type is used when importing
+ versions of the module greater or equal \a{MAJOR}.\a{MINOR}.
+
+ \l QML_REMOVED_IN_VERSION() only takes effect if type or namespace is
+ available in QML, by having a \l QML_ELEMENT, \l QML_NAMED_ELEMENT(),
+ \l QML_ANONYMOUS, or \l QML_INTERFACE macro.
+
+ \sa QML_ELEMENT, QML_NAMED_ELEMENT
*/
/*!
\macro QML_REMOVED_IN_MINOR_VERSION(VERSION)
\relates QQmlEngine
+ \deprecated [6.7] Use QML_REMOVED_IN_VERSION and specify the full version
Declares that the enclosing type or namespace was removed in the specified
minor \a VERSION, relative to the module major version. This is primarily
useful when replacing the implementation of a QML type. If a corresponding
- \l QML_ADDED_IN_MINOR_VERSION() is present on a different type or namespace of
+ \l QML_ADDED_IN_VERSION() is present on a different type or namespace of
the same QML name, then the removed type is used when importing versions of
the module lower than \a VERSION, and the added type is used when importing
versions of the module greater or equal \a VERSION.
@@ -339,7 +415,7 @@
available in QML, by having a \l QML_ELEMENT, \l QML_NAMED_ELEMENT(),
\l QML_ANONYMOUS, or \l QML_INTERFACE macro.
- \sa QML_ELEMENT, QML_NAMED_ELEMENT()
+ \sa QML_REMOVED_IN_VERSION, QML_ELEMENT, QML_NAMED_ELEMENT
*/
/*!
@@ -364,7 +440,7 @@
\note Keeping multiple \l{PAST_MAJOR_VERSIONS} around is computationally
expensive.
- \sa QML_ELEMENT, QML_ADDED_IN_MINOR_VERSION
+ \sa QML_ELEMENT, QML_ADDED_IN_VERSION
*/
/*!
@@ -376,6 +452,8 @@
{attached property} to other types. This takes effect if the type
is exposed to QML using a \l QML_ELEMENT or \l QML_NAMED_ELEMENT() macro.
+ \include {qualified-class-name.qdocinc} {class name must be qualified}
+
\sa QML_ELEMENT, QML_NAMED_ELEMENT(), qmlAttachedPropertiesObject(),
{Providing Attached Properties}
*/
@@ -391,6 +469,8 @@
\warning Members of \a EXTENDED_TYPE are implicitly treated as FINAL.
+ \include {qualified-class-name.qdocinc} {class name must be qualified}
+
\sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_EXTENDED_NAMESPACE(),
{Registering Extension Objects}
*/
@@ -434,6 +514,8 @@
\note \a EXTENDED_NAMESPACE must have a metaobject; i.e. it must either be a namespace which
contains the Q_NAMESPACE macro or a QObject/QGadget.
+ \include {qualified-class-name.qdocinc} {class name must be qualified}
+
\sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_EXTENDED(),
{Registering Extension Objects}, Q_ENUM, Q_ENUM_NS
*/
@@ -444,8 +526,9 @@
Declares that any \l QML_ELEMENT, \l QML_NAMED_ELEMENT(), \l QML_ANONYMOUS,
\l QML_INTERFACE, \l QML_UNCREATABLE(), \l QML_SINGLETON,
+ \l QML_ADDED_IN_VERSION(), \l QML_REMOVED_IN_VERSION(),
\l QML_ADDED_IN_MINOR_VERSION(), \l QML_REMOVED_IN_MINOR_VERSION(),
- \l QML_ATTACHED(), \l QML_EXTENDED(), or \l QML_EXTENDED_NAMESPACE() macros
+ \l QML_EXTENDED(), or \l QML_EXTENDED_NAMESPACE() macros
in the enclosing C++ type do not apply to the enclosing type but instead to
\a FOREIGN_TYPE. The enclosing type still needs to be registered with the
\l {The Meta-Object System}{meta object system} using a \l Q_GADGET or
@@ -455,9 +538,15 @@
for example because they belong to 3rdparty libraries.
To register a namespace, see \l QML_FOREIGN_NAMESPACE().
- \b{NOTE:} You may want to use \l QML_NAMED_ELEMENT() instead of \l QML_ELEMENT due to the fact that
- the element will be named like the struct it is contained in, not the foreign type.
- See \l {Extending QML - Extension Objects Example} for an example.
+ \note You may want to use \l QML_NAMED_ELEMENT() instead of \l QML_ELEMENT.
+ With QML_ELEMENT, the element is named after the struct it is contained in,
+ not the foreign type. The \l {Foreign objects integration} chapter in
+ \l {Writing advanced QML Extensions with C++} demonstrates this.
+
+ \note QML_ATTACHED() can currently not be redirected like this. It has to be
+ specificed in the same type that implements qmlAttachedProperties().
+
+ \include {qualified-class-name.qdocinc} {class name must be qualified}
\sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_FOREIGN_NAMESPACE()
*/
@@ -468,6 +557,7 @@
Declares that any \l QML_ELEMENT, \l QML_NAMED_ELEMENT(), \l QML_ANONYMOUS,
\l QML_INTERFACE, \l QML_UNCREATABLE(), \l QML_SINGLETON,
+ \l QML_ADDED_IN_VERSION(), \l QML_REMOVED_IN_VERSION(),
\l QML_ADDED_IN_MINOR_VERSION(), or \l QML_REMOVED_IN_MINOR_VERSION()
macros in the enclosing C++ namespace do not apply to the enclosing type but
instead to \a FOREIGN_NAMESPACE. The enclosing namespace still needs to be
@@ -527,11 +617,14 @@
\l QML_UNAVAILABLE still results in a more specific error message than the usual
"is not a type" for completely unknown types.
+ \include {qualified-class-name.qdocinc} {class name must be qualified}
+
\sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_UNCREATABLE(), QML_FOREIGN()
*/
/*!
\macro QML_SEQUENTIAL_CONTAINER(VALUE_TYPE)
+ \relates QQmlEngine
This macro declares the enclosing or referenced type as a sequential container
managing a sequence of \a VALUE_TYPE elements. \a VALUE_TYPE can be an actual
@@ -543,39 +636,39 @@
containers like this:
\code
- class Person : public QObject
- {
- Q_OBJECT
- QML_ELEMENT
- [...]
- };
-
- class Thing
+ class IntDequeRegistration
{
Q_GADGET
- QML_VALUE_TYPE(thing)
- [...]
- };
-
- class PersonListRegistration
- {
- Q_GADGET
- QML_FOREIGN(QList<Person *>)
+ QML_FOREIGN(std::deque<int>)
QML_ANONYMOUS
- QML_SEQUENTIAL_CONTAINER(Person *)
+ QML_SEQUENTIAL_CONTAINER(int)
};
+ \endcode
- class ThingListRegistration
+ After this, you can use the container like a JavaScript array in QML.
+
+ \code
+ class Maze
{
- Q_GADGET
- QML_FOREIGN(std::vector<Thing>)
- QML_ANONYMOUS
- QML_SEQUENTIAL_CONTAINER(Thing)
- };
+ Q_OBJECT
+ Q_ELEMENT
+ // 0: North, 1: East, 2: South, 3: West
+ Q_PROPERTY(std::deque<int> solution READ solution CONSTANT FINAL)
+ [...]
+ }
\endcode
- After this, you can use the respective containers like JavaScript arrays
- in QML.
+ \code
+ Item {
+ Maze {
+ id: maze
+ }
+
+ function showSolution() {
+ maze.solution.forEach([...])
+ }
+ }
+ \endcode
\note For \l{QML Value Types} \l{QList} is automatically registered as
sequential container. For \l{QML Object Types} \l{QQmlListProperty} is.
@@ -586,6 +679,8 @@
sequential containers are available under the familiar \e{list<...>} names,
for example \e{list<QtObject>} or \e{list<font>}.
+ \include {qualified-class-name.qdocinc} {class name must be qualified}
+
\sa QML_ANONYMOUS, QML_FOREIGN()
*/
@@ -623,7 +718,7 @@
/*!
- \fn int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+ \fn template <typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
\relates QQmlEngine
This template function registers the C++ type in the QML system with
@@ -676,7 +771,7 @@
*/
/*!
- \fn int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)
+ \fn template<typename T, int metaObjectRevision> int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)
\relates QQmlEngine
This template function registers the specified revision of a C++ type in the QML system with
@@ -695,7 +790,7 @@
*/
/*!
- \fn int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message)
+ \fn template <typename T> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message)
\relates QQmlEngine
This template function registers the C++ type in the QML system with
@@ -714,7 +809,7 @@
*/
/*!
- \fn int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+ \fn template <typename T, typename E> int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
\relates QQmlEngine
This template function registers the C++ type and its extension object in the
@@ -729,7 +824,7 @@
/*!
- \fn int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
+ \fn template <typename T, typename E> int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
\relates QQmlEngine
This template function registers the C++ type and its extension
@@ -775,7 +870,7 @@
Key1,
Key2,
};
- Q_ENUMS(MyEnum)
+ Q_ENUM_NS(MyEnum)
}
//...
@@ -844,7 +939,7 @@
*/
/*!
- \fn int qmlRegisterAnonymousType(const char *uri, int versionMajor)
+ \fn template <typename T> int qmlRegisterAnonymousType(const char *uri, int versionMajor)
\relates QQmlEngine
This template function registers the C++ type in the QML system as an anonymous type. The
@@ -962,7 +1057,7 @@
*/
/*!
- \fn int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue (*callback)(QQmlEngine *, QJSEngine *))
+ \fn int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QJSValue(QQmlEngine *, QJSEngine *)> callback)
\relates QQmlEngine
This function may be used to register a singleton type provider \a callback in a particular \a uri
@@ -1036,7 +1131,7 @@
If \a create is true and type \e T is a valid attaching type, this creates and returns a new
attached object instance.
- Returns 0 if type \e T is not a valid attaching type, or if \a create is false and no
+ Returns \nullptr if type \e T is not a valid attaching type, or if \a create is false and no
attachment object instance has previously been created for \a attachee.
\sa QML_ATTACHED(), {Providing Attached Properties}
@@ -1053,7 +1148,7 @@
*/
/*!
- \fn int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(*callback)(QQmlEngine *, QJSEngine *))
+ \fn template <typename T> int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject*(QQmlEngine *, QJSEngine *)> callback)
\relates QQmlEngine
This function may be used to register a singleton type provider \a callback in a particular \a uri
@@ -1145,15 +1240,6 @@
\sa QML_SINGLETON, {Choosing the Correct Integration Method Between C++ and QML}
*/
-
-/*!
- \fn int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject*(QQmlEngine *, QJSEngine *)> callback)
- \relates QQmlEngine
-
- \overload qmlRegisterSingletonType
- \since 5.14
-*/
-
/*!
\fn int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
\relates QQmlEngine
@@ -1398,7 +1484,7 @@
/*!
\macro QML_CONSTRUCTIBLE_VALUE
- \internal
+ \since 6.5
\relates QQmlEngine
Marks the surrounding value type as constructible. That is, any \l Q_INVOKABLE
@@ -1413,6 +1499,7 @@
Q_GADGET
QML_VALUE_TYPE(myValueType)
QML_CONSTRUCTIBLE_VALUE
+ public:
Q_INVOKABLE MyValueType(double d);
// ...
@@ -1428,12 +1515,35 @@
}
\endqml
+ You can also construct lists of values this way:
+
+ \qml
+ QtObject {
+ property list<myValueType> v: [5.4, 4.5, 3.3]
+ }
+ \endqml
+
+ If you make value types \l{ValueTypeBehavior}{addressable}, you can
+ use such a type in a \l{Type annotations and assertions}{type assertion}
+ to explicitly construct it:
+
+ \qml
+ pragma ValueTypeBehavior: Addressable
+
+ QtObject {
+ function process(d: real) {
+ let v = d as myValueType;
+ // v is a myValueType now, not a number
+ }
+ }
+ \endqml
+
\sa QML_VALUE_TYPE
*/
/*!
\macro QML_STRUCTURED_VALUE
- \internal
+ \since 6.5
\relates QQmlEngine
Marks the surrounding value type as structured. Structured value types can
@@ -1468,5 +1578,31 @@
The extra parentheses are necessary to disambiguate the JavaScript object
from what might be interpreted as a JavaScript code block.
+ You can also construct lists of values this way:
+
+ \qml
+ QtObject {
+ property list<myValueType> v: [
+ {d: 4.4, e: "a string"},
+ {d: 7.1, e: "another string"}
+ ]
+ }
+ \endqml
+
+ If you make value types \l{ValueTypeBehavior}{addressable}, you can
+ use such a type in a \l{Type annotations and assertions}{type assertion}
+ to explicitly construct it:
+
+ \qml
+ pragma ValueTypeBehavior: Addressable
+
+ QtObject {
+ function process(d: real) {
+ let v = {d: d, e: objectName} as myValueType;
+ // v is a myValueType now
+ }
+ }
+ \endqml
+
\sa QML_VALUE_TYPE QML_CONSTRUCTIBLE_VALUE
*/
diff --git a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
index dde54e2af6..df609fb3b9 100644
--- a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
@@ -221,167 +221,4 @@ refer to the \l MouseArea child, and if it had an \c id of \c root rather than
\c squareButton, this would not conflict with the \c id of the same value for
the root object defined in \c SquareButton.qml as the two would be declared
within separate scopes.
-
-\section1 Pragmas
-
-You can prepend global instructions to a QML document using the \c pragma
-keyword. The following pragmas are supported:
-
-\section2 Singleton
-
-\c{pragma Singleton} declares the component defined in the QML document as
-singleton. Singletons are created only once per QML engine. In order to use
-a QML-declared singleton you also have to register it with its module. See
-\l{qt_target_qml_sources} for how to do this with CMake.
-
-\section2 ListPropertyAssignBehavior
-
-With this pragma you can define how assignments to list properties shall be
-handled in components defined in the QML document. By default, assigning to a
-list property appends to the list. You can explicitly request this behavior
-using the value \c{Append}. Alternatively, you can request the contents of list
-properties to always be replaced using \c{Replace}, or replaced if the property
-is not the default property using \c{ReplaceIfNotDefault}. For example:
-
-\qml
-pragma ListPropertyAssignBehavior: ReplaceIfNotDefault
-\endqml
-
-The same declaration can also be given for C++-defined types. See
-\l{QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND},
-\l{QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE}, and
-\l{QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT}
-
-\section2 ComponentBehavior
-
-With this pragma you can restrict components defined in this file to only
-create objects within their original context. This holds for inline
-components as well as Component elements explicitly or implicitly created
-as properties. If a component is bound to its context, you can safely
-use IDs from the rest of the file within the component. Otherwise, the
-engine and the QML tooling cannot know in advance what type, if any, such
-IDs will resolve to at run time.
-
-In order to bind the components to their context specify the \c{Bound}
-argument:
-
-\qml
-pragma ComponentBehavior: Bound
-\endqml
-
-The default is \c{Unbound}. You can also specify it explicitly. In a
-future version of Qt the default will change to \c{Bound}.
-
-Delegate components bound to their context don't receive their own
-private contexts on instantiation. This means that model data can only
-be passed via \l{Required Properties}{required properties} in this case.
-Passing model data via context properties will not work. This concerns
-delegates to e.g. \l{Instantiator}, \l{Repeater}, \l{ListView},
-\l{TableView}, \l{GridView}, \l{TreeView} and in general anything that
-uses \l{DelegateModel} internally.
-
-For example, the following will \e{not} work:
-
-\qml
-pragma ComponentBehavior: Bound
-import QtQuick
-
-ListView {
- delegate: Rectangle {
- color: model.myColor
- }
-}
-\endqml
-
-The \c{delegate} property of \l{ListView} is a component. Therefore, a
-\l{Component} is implicitly created around the \l{Rectangle} here. That
-component is bound to its context. It doesn't receive the context property
-\c{model} provided by \l{ListView}. To make it work, you'd have to write
-it this way:
-
-\qml
-pragma ComponentBehavior: Bound
-import QtQuick
-
-ListView {
- delegate: Rectangle {
- required property color myColor
- color: myColor
- }
-}
-\endqml
-
-You can nest components in a QML file. The pragma holds for all components in
-the file, no matter how deeply nested.
-
-
-\section2 FunctionSignatureBehavior
-
-With this pragma you can change the way type annotations on functions are
-handled. By default the interpreter and JIT ignore type annotations, but
-the \l{QML Script Compiler} enforces them when compiling to C++.
-
-Specifying \c{Enforce} as value makes sure the type annotations are always
-enforced. The resulting type coercions increase the overhead of calling
-typed JavaScript functions.
-
-Specifying \c{Ignore} as value makes the \l{QML Script Compiler} ignore
-any JavaScript functions when compiling the document to C++. This means less
-code is compiled to C++ ahead of time, and more code has to be interpreted or
-JIT-compiled.
-
-\sa {Type annotations and assertions}
-
-\section2 ValueTypeBehavior
-
-The behavior of \l{QML Value Types} and list types differs slightly
-depending on whether a QML document is compiled to C++ using the
-\l{QML Script Compiler} or interpreted at run time.
-
-With this pragma you can change the way value types and sequences are handled
-when retrieved as locals from properties. By default, the interpreter and JIT
-treat all value types and sequences as references. This means, if you change
-the local value, the original property is also changed. Furthermore, if you
-write the original property explicitly, the local value is also updated.
-
-When compiled to C++ using the \l{QML Script Compiler}, the local value is not
-updated when the property is written, and the property is only updated when
-written directly, without retrieving it as local value before.
-
-For example, the following code prints "1 1" when compiled to C++ and "5 5"
-when interpreted or JIT-compiled:
-
-\qml
-import QtQml
-
-QtObject {
- id: root
-
- property rect r: ({x: 1, y: 2, width: 3, height: 4})
- property list<double> numbers: [1, 2, 3, 4, 5]
-
- function manipulate() {
- root.r = {x: 5, y: 6, width: 7, height: 8};
- root.numbers = [5, 4, 3, 2, 1];
- }
-
- Component.onCompleted: {
- var numbers = root.numbers;
- var r = root.r;
- manipulate()
- console.log(r.x, numbers[0]);
- }
-}
-\endqml
-
-You may notice that the behavior when interpreted or JIT-compiled can be rather
-confusing.
-
-Specifying \c{Copy} as value to the pragma makes the interpreter and JIT behave
-like the generated C++ code. This is the recommended way to handle the problem.
-Specifying \c{Reference} makes the \l{QML Script Compiler} skip any functions
-that use value types or sequences when generating C++ code. Those functions are
-then left to be interpreted or JIT-compiled with the default behavior of the
-interpreter and JIT.
-
*/
diff --git a/src/qml/doc/src/qmllanguageref/documents/scope.qdoc b/src/qml/doc/src/qmllanguageref/documents/scope.qdoc
index 87cb191a19..0bbbcae178 100644
--- a/src/qml/doc/src/qmllanguageref/documents/scope.qdoc
+++ b/src/qml/doc/src/qmllanguageref/documents/scope.qdoc
@@ -4,6 +4,7 @@
\page qtqml-documents-scope.html
\title Scope and Naming Resolution
\brief overview of scope and naming resolution
+\ingroup explanations-programminglanguages
QML property bindings, inline functions, and imported JavaScript files all
run in a JavaScript scope. Scope controls which variables an expression can
diff --git a/src/qml/doc/src/qmllanguageref/documents/structure.qdoc b/src/qml/doc/src/qmllanguageref/documents/structure.qdoc
index 2c3f6fe7bd..5a43ae2028 100644
--- a/src/qml/doc/src/qmllanguageref/documents/structure.qdoc
+++ b/src/qml/doc/src/qmllanguageref/documents/structure.qdoc
@@ -6,9 +6,10 @@
\brief Description of the structure of QML documents
-A QML document is a self contained piece of QML source code that consists of two parts:
+A QML document is a self contained piece of QML source code that consists of three parts:
\list
+ \li An optional list of pragmas
\li Its \e import statements
\li A single root object declaration
\endlist
@@ -19,6 +20,294 @@ QML documents are always encoded in UTF-8 format.
+\section1 Pragmas
+
+Pragmas are instructions to the QML engine itself that can be used to specify
+certain characteristics of objects in the current file or to modify how the
+engine interprets code. The following pragmas are exaplained in details below.
+
+\list
+\li \c Singleton
+\li \c ListPropertyAssignBehavior
+\li \c ComponentBehavior
+\li \c FunctionSignatureBehavior
+\li \c NativeMethodBehavior
+\li \c ValueTypeBehavior
+\li \c Translator
+\endlist
+
+\section2 Singleton
+
+\c{pragma Singleton} declares the component defined in the QML document as
+singleton. Singletons are created only once per QML engine. In order to use
+a QML-declared singleton you also have to register it with its module. See
+\l{qt_target_qml_sources} for how to do this with CMake.
+
+\section2 ListPropertyAssignBehavior
+
+With this pragma you can define how assignments to list properties shall be
+handled in components defined in the QML document. By default, assigning to a
+list property appends to the list. You can explicitly request this behavior
+using the value \c{Append}. Alternatively, you can request the contents of list
+properties to always be replaced using \c{Replace}, or replaced if the property
+is not the default property using \c{ReplaceIfNotDefault}. For example:
+
+\qml
+pragma ListPropertyAssignBehavior: ReplaceIfNotDefault
+\endqml
+
+\note The same declaration can also be given for C++-defined types, by adding
+the \l{QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND},
+\l{QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE}, and
+\l{QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT} macros to the
+class declaration.
+
+\section2 ComponentBehavior
+
+You may have multiple components defined in the same QML file. The root
+scope of the QML file is a component, and you may additionally have
+elements of type \l QQmlComponent, explicitly or implicitly created
+as properties, or inline components. Those components are nested. Each
+of the inner components is within one specific outer component. Most of
+the time, IDs defined in an outer component are accessible within all
+its nested inner components. You can, however, create elements from a
+component in any a different context, with different IDs available.
+Doing so breaks the assumption that outer IDs are available. Therefore,
+the engine and the QML tooling cannot generally know in advance what
+type, if any, such IDs will resolve to at run time.
+
+With the ComponentBehavior pragma you can restrict all inner components
+defined in a file to only create objects within their original context.
+If a component is bound to its context, you can safely use IDs from
+outer components in the same file within the component. QML tooling will
+then assume the outer IDs with their specific types to be available.
+
+In order to bind the components to their context specify the \c{Bound}
+argument:
+
+\qml
+pragma ComponentBehavior: Bound
+\endqml
+
+This implies that, in case of name clashes, IDs defined outside a bound
+component override local properties of objects created from the
+component. Otherwise it wouldn't actually be safe to use the IDs since
+later versions of a module might add more properties to the component.
+If the component is not bound, local properties override IDs defined
+outside the component, but not IDs defined inside the component.
+
+The example below prints the \e r property of the ListView object with
+the id \e color, not the \e r property of the rectangle's color.
+
+\qml
+pragma ComponentBehavior: Bound
+import QtQuick
+
+ListView {
+ id: color
+ property int r: 12
+ model: 1
+
+ delegate: Rectangle {
+ Component.onCompleted: console.log(color.r)
+ }
+}
+\endqml
+
+The default value of \c ComponentBehavior is \c{Unbound}. You can also
+specify it explicitly. In a future version of Qt the default will change
+to \c{Bound}.
+
+Delegate components bound to their context don't receive their own
+private contexts on instantiation. This means that model data can only
+be passed via \l{Required Properties}{required properties} in this case.
+Passing model data via context properties will not work. This concerns
+delegates to e.g. \l{Instantiator}, \l{Repeater}, \l{ListView},
+\l{TableView}, \l{GridView}, \l{TreeView} and in general anything that
+uses \l{DelegateModel} internally.
+
+For example, the following will \e{not} work:
+
+\qml
+pragma ComponentBehavior: Bound
+import QtQuick
+
+ListView {
+ delegate: Rectangle {
+ color: model.myColor
+ }
+}
+\endqml
+
+The \c{delegate} property of \l{ListView} is a component. Therefore, a
+\l{Component} is implicitly created around the \l{Rectangle} here. That
+component is bound to its context. It doesn't receive the context property
+\c{model} provided by \l{ListView}. To make it work, you'd have to write
+it this way:
+
+\qml
+pragma ComponentBehavior: Bound
+import QtQuick
+
+ListView {
+ delegate: Rectangle {
+ required property color myColor
+ color: myColor
+ }
+}
+\endqml
+
+You can nest components in a QML file. The pragma holds for all components in
+the file, no matter how deeply nested.
+
+\section2 FunctionSignatureBehavior
+
+With this pragma you can change the way type annotations on functions
+are handled. Since Qt 6.7 type annotations are enforced when calling
+functions. Before, only the \l{QML script compiler} enforced the type
+annotations. The interpreter and JIT compiler ignored them. Always
+enforcing the type annotations is a behavior change in comparison to
+earlier versions since you could call functions with mismatched
+arguments before.
+
+Specifying \c{Ignored} as value makes the QML engine and the
+\l{QML script compiler} ignore any type annotations and therefore
+restores the pre-6.7 behavior of the interpreter and JIT. As a result
+less code is compiled to C++ ahead of time, and more code has to be
+interpreted or JIT-compiled.
+
+Specifying \c{Enforced} as value explicitly states the default: Type
+annotations are always enforced.
+
+\sa {Type annotations and assertions}
+
+\section2 NativeMethodBehavior
+
+Calling C++ methods with \c this objects different from the one they were
+retrieved from is broken, due to historical reasons. The original object is
+used as \c this object. You can allow the given \c this object to be used by
+setting \c {pragma NativeMethodBehavior: AcceptThisObject}. Specifying
+\c RejectThisObject keeps the historical behavior.
+
+An example of this can be found under \l {C++ methods and the 'this' object}.
+
+\section2 ValueTypeBehavior
+
+With this pragma you can change the way value types and sequences are handled.
+
+Usually lower case names cannot be type names in JavaScript code. This is a
+problem because value type names are lower case. You can specify \c{Addressable}
+as value for this pragma to change this. If \c{Addressable} is specified a
+JavaScript value can be explicitly coerced to a specific, named, value type. This is
+done using the \c as operator, like you would do with object types. Furthermore,
+you can also check for value types using the \c instanceof operator:
+
+\qml
+pragma ValueTypeBehavior: Addressable
+import QtQml
+
+QtObject {
+ property var a
+ property real b: (a as rect).x
+ property bool c: a instanceof rect
+
+ property var rect // inaccessible. "rect" is a type name.
+}
+\endqml
+
+If the type does not match, casting returns \c undefined. \c instanceof
+only checks for inheritance, not for all possible type coercions. So, for
+example, a \l{QRect} is not a \c rect value type since \c rect is \l{QRectF}
+in C++, and therefore not related by inheritance. With \c as you can cast
+to any type compatible via coercion.
+
+Since \c rect in the above example is now a type name, it will shadow any
+properties called \c{rect}.
+
+Explicitly casting to the desired type helps tooling. It can allow the
+\l{Qt Quick Compiler} generate efficient code where it otherwise would not be
+able to. You can use \l{qmllint Reference}{qmllint} to find such occurrences.
+
+There is also a \c{Inaddressable} value you can use to explicitly specify the
+default behavior.
+
+Value types and sequences are generally treated as references. This means, if
+you retrieve a value type instance from a property into a local value, and then
+change the local value, the original property is also changed. Furthermore,
+if you write the original property explicitly, the local value is also updated.
+This behavior is rather unintuitive in many places, and you should not rely on
+it. The \c{Copy} and \c{Reference} values for the \c{ValueTypeBehavior} pragma
+are experimental options to change this behavior. You should not use them.
+Specifying \c{Copy} causes all value types to be treated as actual copies.
+Specifying \c{Reference} explicitly states the default behavior.
+
+Rather than using \c{Copy} you should explicitly re-load references to value
+types and sequences any time they can have been affected by side effects. Side
+effects can happen whenever you call a function or imperatively set a property.
+\l qmllint provides guidance on this. For example, in the following code
+the variable \c f is affected by side effects after writing \c width. This is
+because there may be a binding in a derived type or in a \c Binding element
+that updates \c font when \c width is changed.
+
+\qml
+import QtQuick
+Text {
+ function a() : real {
+ var f = font;
+ width = f.pixelSize;
+ return f.pointSize;
+ }
+}
+\endqml
+
+In order to address this, you can avoid holding \c f across the write operation
+on \c width:
+
+\qml
+import QtQuick
+Text {
+ function a() : real {
+ var f = font;
+ width = f.pixelSize;
+ f = font;
+ return f.pointSize;
+ }
+}
+\endqml
+
+This, in turn can be shortened to:
+
+\qml
+import QtQuick
+Text {
+ function a() : real {
+ width = font.pixelSize;
+ return font.pointSize;
+ }
+}
+\endqml
+
+You might assume that re-retrieving the \c font property is costly, but actually
+the QML engine automatically refreshes value type references each time you read
+from them. So this is not more expensive than the first version, but a clearer
+way to express the same operations.
+
+\sa {Type annotations and assertions}
+
+\section2 Translator
+
+With this pragma you can set the context for the translations in the file.
+
+\qml
+pragma Translator: myTranslationContext
+\endqml
+
+\qml
+pragma Translator: "myTranslationContext"
+\endqml
+
+For more information on internationalization with QML, see \l{QML: Use qsTr()}{Use qsTr}.
+
\section1 Imports
A document must import the necessary modules or type namespaces to enable the
diff --git a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
index fff697dd8e..93461f1ed7 100644
--- a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
+++ b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
@@ -153,7 +153,6 @@ An identified module has several restrictions upon it:
\li the module must register its types into the module identifier type
namespace
\li the module may not register types into any other module's namespace
-\li clients must specify a version when importing the module
\endlist
For example, if an identified module is installed into
@@ -170,10 +169,9 @@ module com.example.CustomUi
\endcode
Clients will then be able to import the above module with the following import
-statement (assuming that the module registers types into version 1.0 of its
-namespace):
+statement:
\qml
-import com.example.CustomUi 1.0
+import com.example.CustomUi
\endqml
*/
diff --git a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
index fca702a278..771d520f8a 100644
--- a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
+++ b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc
@@ -62,85 +62,3 @@ that's part of the same binary as the registration:
volatile auto registration = &qml_register_types_my_module;
Q_UNUSED(registration);
\endcode
-
-\section1 TimeExample QML Extension Plugin
-
-Suppose there is a new \c TimeModel C++ class that should be made available
-as a new QML type. It provides the current time through \c hour and \c minute
-properties. It declares a QML type called \c Time via \l QML_NAMED_ELEMENT().
-
-\snippet qmlextensionplugins/timemodel.h 0
-\dots
-
-To make this type available, create a plugin class named \c QExampleQmlPlugin,
-which is a subclass of \l QQmlEngineExtensionPlugin. It uses the
-Q_PLUGIN_METADATA() macro in the class definition to register the plugin with
-the Qt meta object system using a unique identifier for the plugin.
-
-\snippet qmlextensionplugins/plugin.cpp plugin
-
-\section1 Build Settings for the Plugin
-
-The build file defines the project as a plugin library, specifies it should be
-built into the \c imports/TimeExample directory, and registers the plugin
-target name.
-
-\section2 Using CMake:
-
-\snippet qmlextensionplugins/CMakeLists.txt 0
-
-\snippet qmlextensionplugins/CMakeLists.txt 1
-
-\section2 Using qmake:
-
-\code
-TEMPLATE = lib
-CONFIG += qt plugin qmltypes
-QT += qml
-
-QML_IMPORT_NAME = TimeExample
-QML_IMPORT_MAJOR_VERSION = 1
-
-DESTDIR = imports/$$QML_IMPORT_NAME
-TARGET = qmlqtimeexampleplugin
-
-SOURCES += qexampleqmlplugin.cpp
-\endcode
-
-This registers the \c TimeModel class, with the import \c{TimeExample 1.0}, as
-a QML type called \c Time. The \l{Defining QML Types from C++} article has more
-information about registering C++ types for usage in QML.
-
-\section1 Plugin Definition in the qmldir
-
-Finally, a \l {Module Definition qmldir Files} {qmldir file} is required
-in the \c imports/TimeExample directory to describe the plugin and the types
-that it exports. The plugin includes a \c Clock.qml file along with the
-\c qmlqtimeexampleplugin that is built by the project.
-
-CMake will, by default, automatically generate this file. For more
-information, see \l {Auto-generating qmldir and typeinfo files}.
-
-When using qmake, specify the following in the \c qmldir file:
-
-\quotefile qmlextensionplugins/imports/TimeExample/qmldir
-
-To make things easier for this example, the TimeExample source directory is in
-\c{imports/TimeExample}, and we build
-\l{Source, Build, and Install Directories}{in-source}. However, the structure
-of the source directory is not important, as the \c qmldir file can specify
-paths to installed QML files.
-
-What is important is the name of the directory that the qmldir is installed
-into. When the user imports our module, the QML engine uses the
-\l{Contents of a Module Definition qmldir File}{module identifier}
-(\c TimeExample) to find the plugin, so the directory in which it is
-installed must match the module identifier.
-
-Once the project is built and installed, the new \c Time component is
-accessible by any QML component that imports the \c TimeExample
-module.
-
-\snippet qmlextensionplugins/plugins.qml 0
-
-The full source code is available in the \l {qmlextensionplugins}{plugins example}.
diff --git a/src/qml/doc/src/qmllanguageref/qmlreference.qdoc b/src/qml/doc/src/qmllanguageref/qmlreference.qdoc
index 4b873d9a0f..7f92129fcf 100644
--- a/src/qml/doc/src/qmllanguageref/qmlreference.qdoc
+++ b/src/qml/doc/src/qmllanguageref/qmlreference.qdoc
@@ -14,7 +14,7 @@ In addition, QML heavily uses Qt, which allows types and other Qt features to
be accessible directly from QML applications.
This reference guide describes the features of the QML language. Many of the
-QML types in the guide originate from the \l{Qt QML} or \l{Qt Quick}
+QML types in the guide originate from the \l{Qt Qml} or \l{Qt Quick}
modules.
\list
@@ -49,6 +49,7 @@ modules.
\li \l{qtqml-javascript-resources.html}{Defining JavaScript Resources In QML}
\li \l{qtqml-javascript-imports.html}{Importing JavaScript Resources In QML}
\li \l{qtqml-javascript-hostenvironment.html}{JavaScript Host Environment}
+ \li \l{qtqml-javascript-finetuning.html}{Configuring the JavaScript engine}
\endlist
\li \l{qtqml-typesystem-topic.html}{The QML Type System}
@@ -60,6 +61,8 @@ modules.
\li \l{qtqml-documents-definetypes.html}{Defining Object Types from QML}
\li \l{qtqml-cppintegration-definetypes.html}{Defining Object Types from C++}
\endlist
+ \li \l{qtqml-typesystem-sequencetypes.html}{QML Sequence Types}
+ \li \l{qtqml-typesystem-namespaces.html}{QML Namespaces}
\endlist
\li \l{qtqml-modules-topic.html}{QML Modules}
diff --git a/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc b/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc
index 809755ce16..ce2e48d41a 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc
@@ -24,7 +24,7 @@ A QML document may have one or more imports at the top of the file.
An import can be any one of:
\list
-\li a versioned namespace into which types have been registered (e.g., by a plugin)
+\li a QML Module
\li a relative directory which contains type-definitions as QML documents
\li a JavaScript file
\endlist
@@ -33,10 +33,9 @@ JavaScript file imports must be qualified when imported, so that the properties
The generic form of the various imports are as follows:
\list
-\li \tt{import Namespace VersionMajor.VersionMinor}
-\li \tt{import Namespace VersionMajor.VersionMinor as SingletonTypeIdentifier}
-\li \tt{import "directory"}
-\li \tt{import "file.js" as ScriptIdentifier}
+\li \tt{import <ModuleIdentifier> [<Version.Number>] [as <Qualifier>]}
+\li \tt{import "<Directory>"}
+\li \tt{import "<JavaScriptFile>" [as <ScriptIdentifier>]}
\endlist
Examples:
diff --git a/src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc b/src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc
index 06cc5e13e1..f653ba452b 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/directoryimports.qdoc
@@ -85,15 +85,29 @@ as an installed module is imported with a unique identifier string rather than
a file system path.
+\section1 The Implicit Import
+
+The directory a QML document resides in is automatically imported. You do
+not have to explicitly import \c{"."} or similar.
+
+\note You should make sure that the qmldir file that specifies the module a QML
+document belongs to resides in the same directory as the QML document itself.
+Otherwise the implicit import is different from the module the document belongs
+to. Then, for example, another QML document may be a singleton in the context of
+the module, but not a singleton in the context of the implicit import. This is a
+frequent source of mistakes.
+
+
\section1 Remotely Located Directories
A directory of QML files can also be imported from a remote location if the
directory contains a directory listing \c qmldir file.
-\note This also holds for the implicit import of the directory a QML document
-resides in. If your QML documents are loaded from a remote location, you need
-to add qmldir files even if they don't contain any explicit directory import
-statements. Otherwise your QML documents won't see each other.
+\note This also holds for the \l{The Implicit Import}{implicit import} of the
+directory a QML document resides in. If your QML documents are loaded from a
+remote location, you need to add qmldir files even if they don't contain any
+explicit directory import statements. Otherwise your QML documents won't see
+each other.
For example, if the \c myapp directory in the previous example was hosted at
"http://www.my-example-server.com", and the \c mycomponents directory
diff --git a/src/qml/doc/src/qmllanguageref/syntax/imports.qdoc b/src/qml/doc/src/qmllanguageref/syntax/imports.qdoc
index 27489445ae..c19ac3eeec 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/imports.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/imports.qdoc
@@ -12,12 +12,10 @@ resources and component directories are used within a QML document. The types
which may be used within a document depends on which modules, resources and
directories are imported by the document.
-\section2 Import Types
-
There are three different types of imports. Each import type has a slightly
different syntax, and different semantics apply to different import types.
-\section3 Module (Namespace) Imports
+\section2 Module (Namespace) Imports
The most common type of import is a module import. Clients can import
\l{qtqml-modules-identifiedmodules.html}{QML modules} which register QML object
@@ -112,7 +110,7 @@ Rectangle {
In this case, the engine will emit an error and refuse to load the file.
-\section4 C++ Module Imports
+\section3 C++ Module Imports
Usually, C++ types are declared using the QML_ELEMENT and QML_NAMED_ELEMENT()
macros and registered via the build system using QML_IMPORT_NAME and
@@ -122,7 +120,7 @@ module that can be imported to access the types.
This is most common in client applications which define their own QML object
types in C++.
-\section4 Importing into a Qualified Local Namespace
+\section3 Importing into a Qualified Local Namespace
The \c import statement may optionally use the \c as keyword to specify that
the types should be imported into a particular document-local namespace. If a
@@ -172,7 +170,7 @@ way that multiple modules can be imported into the global namespace. For example
\snippet qml/imports/merged-named-imports.qml imports
-\section3 Directory Imports
+\section2 Directory Imports
A directory which contains QML documents may also be imported directly in a
QML document. This provides a simple way for QML types to be segmented into
@@ -198,7 +196,7 @@ section about \l{Importing into a Qualified Local Namespace}.
For more information about directory imports, please see the in-depth
documentation about \l{qtqml-syntax-directoryimports.html}{directory imports}.
-\section3 JavaScript Resource Imports
+\section2 JavaScript Resource Imports
JavaScript resources may be imported directly in a QML document. Every
JavaScript resource must have an identifier by which it is accessed.
@@ -211,7 +209,7 @@ import "<JavaScriptFile>" as <Identifier>
Note that the \c <Identifier> must be unique within a QML document, unlike the
local namespace qualifier which can be applied to module imports.
-\section4 JavaScript Resources from Modules
+\section3 JavaScript Resources from Modules
Javascript files can be provided by modules, by adding identifier
definitions to the \c qmldir file which specifies the module.
@@ -254,7 +252,7 @@ Item {
}
\endqml
-\section4 Further Information
+\section3 Further Information
For more information about JavaScript resources, please see the documentation
about \l{qtqml-javascript-resources.html}
@@ -292,6 +290,11 @@ cannot specify resource paths or URLs in QML_IMPORT_PATH, as they contain
colons themselves. However, you can add resource paths and URLs by calling
QQmlEngine::addImportPath() programatically.
+\note It is recommended that applications and libraries put their modules
+under "qrc:/qt/qml". This happens by default when the module is created
+with \l{qt_add_qml_module}{qt_add_qml_module()} and \l{QTP0001} is
+enabled.
+
\section1 Debugging
diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
index 60e90217f4..2b1803042e 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -50,7 +50,7 @@ by referring to \c myTextInput.text. Now, both items will display the same
text:
\qml
-import QtQuick 2.0
+import QtQuick
Column {
width: 200; height: 200
@@ -200,7 +200,7 @@ definition becomes:
An example of property value initialization follows:
\qml
-import QtQuick 2.0
+import QtQuick
Rectangle {
color: "red"
@@ -222,7 +222,7 @@ assignment operator, as shown below:
An example of imperative value assignment follows:
\qml
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
@@ -262,7 +262,7 @@ also known as \l{Property Binding}{property bindings}.
Here is an example that shows both kinds of values being assigned to properties:
\qml
-import QtQuick 2.0
+import QtQuick
Rectangle {
// both of these are static value assignments on initialization
@@ -327,7 +327,7 @@ used to hold a list of \l State type objects. The code below initializes the
value of this property to a list of three \l State objects:
\qml
-import QtQuick 2.0
+import QtQuick
Item {
states: [
@@ -341,7 +341,7 @@ Item {
If the list contains a single item, the square brackets may be omitted:
\qml
-import QtQuick 2.0
+import QtQuick
Item {
states: State { name: "running" }
@@ -352,20 +352,20 @@ A \l list type property may be specified in an object declaration with the
following syntax:
\code
- [default] property list<<objectType>> propertyName
+ [default] property list<<ObjectType>> propertyName
\endcode
and, like other property declarations, a property initialization may be
combined with the property declaration with the following syntax:
\code
- [default] property list<<objectType>> propertyName: <value>
+ [default] property list<<ObjectType>> propertyName: <value>
\endcode
An example of list property declaration follows:
\qml
-import QtQuick 2.0
+import QtQuick
Rectangle {
// declaration without initialization
@@ -470,7 +470,7 @@ which is connected to the \c text object of the \l Text child:
\qml
// Button.qml
-import QtQuick 2.0
+import QtQuick
Rectangle {
property alias buttonText: textItem.text
@@ -498,16 +498,6 @@ the other way around.
\section4 Considerations for Property Aliases
-Aliases are only activated once a component has been fully initialized. An
-error is generated when an uninitialized alias is referenced. Likewise,
-aliasing an aliasing property will also result in an error.
-
-\snippet qml/properties.qml alias complete
-
-When importing a \l{QML Object Types}{QML object type} with a property alias in
-the root object, however, the property appear as a regular Qt property and
-consequently can be used in alias references.
-
It is possible for an aliasing property to have the same name as an existing
property, effectively overwriting the existing property. For example,
the following QML type has a \c color alias property, named the same as the
@@ -585,12 +575,12 @@ property \c someText:
\qml
// MyLabel.qml
-import QtQuick 2.0
+import QtQuick
Text {
default property var someText
- text: "Hello, " + someText.text
+ text: `Hello, ${someText.text}`
}
\endqml
@@ -621,10 +611,34 @@ This is because the default property of \l Item is its \c data property, and
any items added to this list for an \l Item are automatically added to its
list of \l {Item::children}{children}.
-Default properties can be useful for reassigning the children of an item. See
-the \l{TabWidget Example}, which uses a default property to
-automatically reassign children of the TabWidget as children of an inner
-ListView. See also \l {Extending QML}.
+Default properties can be useful for reassigning the children of an item.
+For example:
+
+\qml
+Item {
+ default property alias content: inner.children
+
+ Item {
+ id: inner
+ }
+}
+\endqml
+
+By setting the default property \e alias to \c {inner.children}, any object
+assigned as a child of the outer item is automatically reassigned as a child
+of the inner item.
+
+\warning Setting the values of a an element's default list property can be done implicitly or
+explicitly. Within a single element's definition, these two methods must not be mixed as that leads
+to undefined ordering of the elements in the list.
+
+\qml
+Item {
+ // Use either implicit or explicit assignement to the default list property but not both!
+ Rectangle { width: 40 } // implicit
+ data: [ Rectangle { width: 100 } ] // explicit
+}
+\endqml
\section3 Required Properties
@@ -671,12 +685,12 @@ An object declaration may define a read-only property using the \c readonly
keyword, with the following syntax:
\code
- readonly property <propertyType> <propertyName> : <initialValue>
+ readonly property <propertyType> <propertyName> : <value>
\endcode
-Read-only properties must be assigned a value on initialization. After a
-read-only property is initialized, it no longer possible to give it a value,
-whether from imperative code or otherwise.
+Read-only properties must be assigned a static value or a binding expression on
+initialization. After a read-only property is initialized, you cannot change
+its static value or binding expression anymore.
For example, the code in the \c Component.onCompleted block below is invalid:
@@ -684,7 +698,7 @@ For example, the code in the \c Component.onCompleted block below is invalid:
Item {
readonly property int someNumber: 10
- Component.onCompleted: someNumber = 20 // doesn't work, causes an error
+ Component.onCompleted: someNumber = 20 // TypeError: Cannot assign to read-only property
}
\endqml
@@ -743,7 +757,7 @@ For example, the \e onClicked signal handler below is declared within the
clicked, causing a console message to be printed:
\qml
-import QtQuick 2.0
+import QtQuick
Item {
width: 100; height: 100
@@ -776,7 +790,7 @@ may be hidden and become inaccessible.)
Here are three examples of signal declarations:
\qml
-import QtQuick 2.0
+import QtQuick
Item {
signal clicked
@@ -795,7 +809,7 @@ In order to be consistent with method declarations, you should prefer the
type declarations using colons.
If the signal has no parameters, the "()" brackets are optional. If parameters
-are used, the parameter types must be declared, as for the \c string and \c var
+are used, the parameter types must be declared, as for the \c string and \c int
arguments for the \c actionPerformed signal above. The allowed parameter types
are the same as those listed under \l {Defining Property Attributes} on this page.
@@ -840,7 +854,7 @@ Rectangle {
MouseArea {
anchors.fill: parent
onReleased: root.deactivated()
- onPressed: (mouse)=> root.activated(mouse.x, mouse.y)
+ onPressed: mouse => root.activated(mouse.x, mouse.y)
}
}
\endqml
@@ -853,7 +867,9 @@ provided by the client:
// myapplication.qml
SquareButton {
onDeactivated: console.log("Deactivated!")
- onActivated: (xPosition, yPosition)=> console.log("Activated at " + xPosition + "," + yPosition)
+ onActivated: (xPosition, yPosition) => {
+ console.log(`Activated at ${xPosition}, ${yPosition}`)
+ }
}
\endqml
@@ -875,12 +891,12 @@ implicitly available through the fact that \l TextInput has a
\c onTextChanged signal handler to be called whenever this property changes:
\qml
-import QtQuick 2.0
+import QtQuick
TextInput {
text: "Change this!"
- onTextChanged: console.log("Text has changed to:", text)
+ onTextChanged: console.log(`Text has changed to: ${text}`)
}
\endqml
@@ -920,11 +936,11 @@ Below is a \l Rectangle with a \c calculateHeight() method that is called when
assigning the \c height value:
\qml
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
- function calculateHeight() : real {
+ function calculateHeight(): real {
return rect.width / 2;
}
@@ -939,14 +955,14 @@ can then refer to the received \c newX and \c newY parameters to reposition the
text:
\qml
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 200
MouseArea {
anchors.fill: parent
- onClicked: (mouse)=> label.moveTo(mouse.x, mouse.y)
+ onClicked: mouse => label.moveTo(mouse.x, mouse.y)
}
Text {
@@ -991,7 +1007,7 @@ ListView. This can be used by each individual delegate object to determine
whether it is the currently selected item in the view:
\qml
-import QtQuick 2.0
+import QtQuick
ListView {
width: 240; height: 320
@@ -1015,15 +1031,16 @@ been fully created, its \c Component.onCompleted signal handler will
automatically be invoked to populate the model:
\qml
-import QtQuick 2.0
+import QtQuick
ListView {
width: 240; height: 320
model: ListModel {
id: listModel
Component.onCompleted: {
- for (var i = 0; i < 10; i++)
- listModel.append({"Name": "Item " + i})
+ for (let i = 0; i < 10; i++) {
+ append({ Name: `Item ${i}` })
+ }
}
}
delegate: Text { text: index }
@@ -1048,7 +1065,7 @@ attached properties. This time, the delegate is an \l Item and the colored
\l Rectangle is a child of that item:
\qml
-import QtQuick 2.0
+import QtQuick
ListView {
width: 240; height: 320
@@ -1058,7 +1075,7 @@ ListView {
Rectangle {
width: 100; height: 30
- color: ListView.isCurrentItem ? "red" : "yellow" // WRONG! This won't work.
+ color: ListView.isCurrentItem ? "red" : "yellow" // WRONG! This won't work.
}
}
}
@@ -1073,14 +1090,13 @@ it cannot access the \c isCurrentItem attached property as
\qml
ListView {
- //....
delegate: Item {
id: delegateItem
width: 100; height: 30
Rectangle {
width: 100; height: 30
- color: delegateItem.ListView.isCurrentItem ? "red" : "yellow" // correct
+ color: delegateItem.ListView.isCurrentItem ? "red" : "yellow" // correct
}
}
}
@@ -1117,8 +1133,8 @@ Text {
property int textType: MyText.TextType.Normal
- font.bold: textType == MyText.TextType.Heading
- font.pixelSize: textType == MyText.TextType.Heading ? 24 : 12
+ font.bold: textType === MyText.TextType.Heading
+ font.pixelSize: textType === MyText.TextType.Heading ? 24 : 12
}
\endqml
diff --git a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
index 92e2e81869..b40181b49c 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
@@ -54,6 +54,13 @@ Rectangle {
}
\endqml
+\note Even though signal handlers look a bit like JavaScript functions, you
+ should not call them directly. If you need to share code between signal
+ handlers and other functionality, refactor it into a separate function.
+ Otherwise always emit the signal if you want the signal handler to be
+ called. There can be multiple handlers, in different scopes, for the
+ same signal.
+
\section2 Property change signal handlers
A signal is automatically emitted when the value of a QML property changes.
@@ -97,6 +104,7 @@ import QtQuick
Item {
id: myitem
+
signal errorOccurred(message: string, line: int, column: int)
}
\endqml
@@ -113,7 +121,7 @@ signal.
If you do not need to handle all parameters, it is possible to omit trailing ones:
\qml
Status {
- onErrorOccurred: function (message) { console.log(message) }
+ onErrorOccurred: message => console.log(message)
}
\endqml
@@ -247,7 +255,7 @@ Now any objects of the \c SquareButton can connect to the \c activated signal us
\qml
// myapplication.qml
SquareButton {
- onActivated: (xPosition, yPosition)=> console.log("Activated at " + xPosition + "," + yPosition)
+ onActivated: (xPosition, yPosition) => console.log(`Activated at {xPosition}, ${yPosition}`)
}
\endqml
@@ -279,14 +287,14 @@ Rectangle {
relay.messageReceived("Tom", "Happy Birthday")
}
- function sendToPost(person, notice) {
- console.log("Sending to post: " + person + ", " + notice)
+ function sendToPost(person: string, notice: string) {
+ console.log(`Sending to post: ${person}, ${notice}`)
}
- function sendToTelegraph(person, notice) {
- console.log("Sending to telegraph: " + person + ", " + notice)
+ function sendToTelegraph(person: string, notice: string) {
+ console.log(`Sending to telegraph: ${person}, ${notice}`)
}
- function sendToEmail(person, notice) {
- console.log("Sending to email: " + person + ", " + notice)
+ function sendToEmail(person: string, notice: string) {
+ console.log(`Sending to email: ${person}, ${notice}`)
}
}
\endqml
@@ -347,4 +355,65 @@ output:
MouseArea clicked
Send clicked
\endcode
-*/
+
+\note Connections to function objects will stay alive as long as the sender of the signal is alive.
+This behavior is analogous to the 3-argument version of QObject::connect() in C++.
+
+\qml
+Window {
+ visible: true
+ width: 400
+ height: 400
+
+ Item {
+ id: item
+ property color globalColor: "red"
+
+ Button {
+ text: "Change global color"
+ onPressed: {
+ item.globalColor = item.globalColor === Qt.color("red") ? "green" : "red"
+ }
+ }
+
+ Button {
+ x: 150
+ text: "Clear rectangles"
+ onPressed: repeater.model = 0
+ }
+
+ Repeater {
+ id: repeater
+ model: 5
+ Rectangle {
+ id: rect
+ color: "red"
+ width: 50
+ height: 50
+ x: (width + 2) * index + 2
+ y: 100
+ Component.onCompleted: {
+ if (index % 2 === 0) {
+ item.globalColorChanged.connect(() => {
+ color = item.globalColor
+ })
+ }
+ }
+ }
+ }
+ }
+}
+\endqml
+
+In the contrived example above, the goal is to flip the color of every even rectangle to follow
+some global color. To achieve this, for every even rectangle, a connection is made between the
+globalColorChanged signal and a function to set the rectangle's color. This works as expected while
+the rectangles are alive. However, once the clear button is pressed, the rectangles are gone but
+the function handling the signal is still called every time the signal is emitted. This can be
+seen by the error messages thrown by the function trying to run in the background when changing
+the global color.
+
+In the current setup, the connections would only be destroyed once the item holding
+globalColor is destroyed. To prevent the connections from lingering on, they can be explicitly
+disconnected when the rectangles are being destroyed.
+ */
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/namespaces.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/namespaces.qdoc
new file mode 100644
index 0000000000..0635dbd026
--- /dev/null
+++ b/src/qml/doc/src/qmllanguageref/typesystem/namespaces.qdoc
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtqml-typesystem-namespaces.html
+\title QML Namespaces
+\brief Description of QML Namespaces
+
+A QML Namespace is a special kind of type that only exposes enumerations and cannot
+be instantiated. A namespace can only be declared in C++, using the \l QML_ELEMENT or
+\l QML_NAMED_ELEMENT macro inside a C++ namespace marked with \l{Q_NAMESPACE}.
+
+QML namespaces can be used to
+\l{qtqml-cppintegration-definetypes.html#value-types-with-enumerations}{extract enumerations}
+from other types.
+
+*/
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/references.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/references.qdoc
new file mode 100644
index 0000000000..5326759638
--- /dev/null
+++ b/src/qml/doc/src/qmllanguageref/typesystem/references.qdoc
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtqml-typesystem-references.html
+\title QML Value Type and Sequence References
+\brief Description of QML value type and sequence references
+
+\l{QML Value Types} and \l{QML Sequence Types} are necessarily passed by value.
+In contrast to \l{QML Object Types} they have no identity of themselves, but can
+only be accessed as properties of other objects or values, or as values returned
+from methods. Each such access implicitly creates a copy. Yet, in JavaScript
+everything is an object. There is no such concept as a value type in JavaScript.
+For example, if you execute \c{font.bold = true} in JavaScript, we expect the \c bold
+property of \c font to be set, no matter what \c font is. But consider the following
+code snippet:
+
+\qml
+import QtQuick
+Text {
+ onSomethingHappened: font.bold = true
+}
+\endqml
+
+In this case we know that \c font is a value type. Accessing it creates a local copy
+by calling the getter of a \l{Q_PROPERTY}. We can then set the \c bold property on it,
+but that would usually only affect the copy, not the original \l{Q_PROPERTY}.
+
+To overcome this problem, QML offers the concept of references. When you retrieve
+an instance of a value or sequence type from a property, the QML engine remembers
+the property along with the value itself. If the value is modified, it is written
+back to the property. This produces the illusion of an object with separate identity
+and makes the above case, along with many others, just work.
+
+This can be rather expensive, though. If a sequence is exposed as a Q_PROPERTY,
+accessing any value in the sequence by index will cause the whole sequence data
+to be read from the property. From this sequence data, a single element is then
+retrieved. Similarly, modifying any value in the sequence causes the
+sequence data to be read. Then the modification is performed and the modified
+sequence is be written back to the property. A read operation can be relatively
+cheap if the type in question is implicitly shared. A modification always incurs
+at least one deep copy.
+
+If you return an instance of a sequence or value type from a \l Q_INVOKABLE function
+you avoid such overhead. Return values are not attached to any property and won't be
+written back.
+
+Sequences of object types are passed as \l{QQmlListProperty} by default.
+\l{QQmlListProperty} is not an actual container, but only a view, or reference, to
+some sequential storage. Therefore, \{QQmlListProperty} is not affected by this
+effect. You can, however, register other sequence types for objects using
+\l{QML_SEQUENTIAL_CONTAINER}. Those will be affected.
+
+*/
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/sequencetypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/sequencetypes.qdoc
new file mode 100644
index 0000000000..ca10f8c23b
--- /dev/null
+++ b/src/qml/doc/src/qmllanguageref/typesystem/sequencetypes.qdoc
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\page qtqml-typesystem-sequencetypes.html
+\title QML Sequence Types
+\brief Description of QML sequence types
+
+For every \l{qtqml-typesystem-objecttypes.html}{object type} and
+\l{qtqml-typesystem-valuetypes.html}{value type} a sequence type for storing
+multiple instances of the type is automatically made available. You can use
+the \c list keyword to create properties of sequence types:
+
+\qml
+import QtQml
+
+QtObject {
+ property list<int> ints: [1, 2, 3, 4]
+ property list<Connection> connections: [
+ Connection {
+ // ...
+ },
+ Connection {
+ // ...
+ }
+ ]
+}
+\endqml
+
+Sequences of value types are implemented as \l{QList} and sequences of object
+types are implemented as \l{QQmlListProperty}.
+
+Sequences in QML generally behave like the JavaScript \c Array type, with some
+important differences which result from the use of a C++ storage type in the
+implementation:
+
+\list 1
+\li Deleting an element from a sequence will result in a default-constructed
+ value replacing that element, rather than an \c undefined value.
+\li Setting the \c length property of a sequence to a value larger
+ than its current value will result in the sequence being padded out to the
+ specified length with default-constructed elements rather than \c undefined
+ elements.
+\li The Qt container classes support signed (rather than
+ unsigned) integer indexes; thus, attempting to access any index greater
+ than the maximum number \l qsizetype can hold will fail.
+\endlist
+
+If you wish to remove elements from a sequence rather than simply replace
+them with default constructed values, do not use the indexed delete operator
+(\c{delete sequence[i]}) but instead use the \c {splice} function
+(\c{sequence.splice(startIndex, deleteCount)}).
+
+In general any container recognizable by \l QMetaSequence can be passed from
+C++ to QML via \l Q_PROPERTY or \l Q_INVOKABLE methods. This includes, but is
+not limited to, all registered QList, QQueue, QStack, QSet, std::list,
+std::vector that contain a type marked with \l Q_DECLARE_METATYPE.
+
+Using a sequence via \l QMetaSequence results in expensive data conversions.
+To avoid the conversions you can register your own anonymous sequence types
+using \l{QML_SEQUENTIAL_CONTAINER} from C++. Types registered this way behave
+like the pre-defined sequence types and are stored as-is. However, they have
+no QML names.
+
+\warning Sequences stored as a C++ container like \l QList or \c std::vector are
+subject to the effects caused by \l{QML Value Type and Sequence References} and
+should thus be handled with care. \l QQmlListProperty is not affected since
+it is only a view for an underlying container. C++ standard containers such as
+\c std::vector are not implicitly shared. Therefore, copying them always
+produces a deep copy. Since a sequence read from a property always has to be
+copied at least once, using such containers as QML sequences is rather
+expensive, even if you don't modify them from QML.
+
+The QtQml module contains a few \l [QML] {QtQml#Sequence Types}{sequence types}
+you may want to use.
+
+*/
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/topic.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/topic.qdoc
index 38196036d4..b1c5bce891 100644
--- a/src/qml/doc/src/qmllanguageref/typesystem/topic.qdoc
+++ b/src/qml/doc/src/qmllanguageref/typesystem/topic.qdoc
@@ -22,7 +22,7 @@ Wherever the type definitions come from, the engine will enforce type-safety
for properties and instances of those types.
-\section1 Value Types
+\section1 QML Value Types
The QML language has built-in support for various primitive types including
integers, double-precision floating point numbers, strings, and boolean values.
@@ -32,6 +32,35 @@ passed as arguments to methods of objects.
See the \l{qtqml-typesystem-valuetypes.html}{QML Value Types} documentation for
more information about value types.
+\section1 QML Object Types
+
+A QML object type is a type from which a QML object can be instantiated. QML
+object types are derived from \l QtObject, and are provided by QML modules.
+Applications can import these modules to use the object types they provide.
+The \c QtQuick module provides the most common object types needed to create
+user interfaces in QML.
+
+Finally, every QML document implicitly defines a QML object type, which can be
+re-used in other QML documents. See the documentation about
+\l{qtqml-typesystem-objecttypes.html}{object types in the QML type system} for
+in-depth information about object types.
+
+\section1 QML Sequence Types
+
+Sequence types can be used to store sequences of values or objects.
+
+See the documentation about
+\l{qtqml-typesystem-sequencetypes.html}{sequence types in the QML type system}
+for in-depth information about sequence types.
+
+\section1 QML Namespaces
+
+QML Namespaces can be used to expose enumerations from C++ namespaces.
+
+See the documentation about
+\l{qtqml-typesystem-namespaces.html}{namespaces in the QML type system}
+for in-depth information about namespaces.
+
\section1 JavaScript Types
JavaScript objects and arrays are supported by the QML engine. Any standard
@@ -40,7 +69,7 @@ JavaScript type can be created and stored using the generic \l var type.
For example, the standard \c Date and \c Array types are available, as below:
\qml
-import QtQuick 2.0
+import QtQuick
Item {
property var theArray: []
@@ -58,17 +87,4 @@ Item {
See \l {qtqml-javascript-expressions.html}{JavaScript Expressions in QML Documents} for more details.
-\section1 QML Object Types
-
-A QML object type is a type from which a QML object can be instantiated. QML
-object types are derived from \l QtObject, and are provided by QML modules.
-Applications can import these modules to use the object types they provide.
-The \c QtQuick module provides the most common object types needed to create
-user interfaces in QML.
-
-Finally, every QML document implicitly defines a QML object type, which can be
-re-used in other QML documents. See the documentation about
-\l{qtqml-typesystem-objecttypes.html}{object types in the QML type system} for
-in-depth information about object types.
-
*/
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
index dc5c99ea03..0bf849b155 100644
--- a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
@@ -7,16 +7,21 @@
QML supports built-in and custom value types.
-A \e{value type} is one that is passed by value rather than by reference, such
-as an \c int or a \c string. This contrasts with
+A \e{value type} is one that is conceptually passed by value rather than by
+reference, such as an \c int or a \c string. This contrasts with
\l{qtqml-typesystem-topic.html#qml-object-types}{QML Object Types}. Object types
are passed by reference. If you assign an instance of an object type to two
different properties, both properties carry the same value. Modifying the object
is reflected in both properties. If you assign an instance of a value type to
two different properties, the properties carry separate values. If you modify
-one of them, the other one stays the same. Unlike an object type, a value type
-cannot be used to declare QML objects: it is not possible, for example, to
-declare an \c int{} object or a \c size{} object.
+one of them, the other one stays the same. Value types are only conceptually
+passed by value since it must still be possible to interact with them as if they
+were JavaScript objects. To facilitate this, in reality they are passed as
+\l{QML Value Type and Sequence References}{Value Type References} when you access
+them from JavaScript code.
+
+Unlike an object type, a value type cannot be used to declare QML objects:
+it is not possible, for example, to declare an \c int{} object or a \c size{} object.
Value types can be used to refer to:
@@ -40,22 +45,30 @@ the client to import the module which provides them.
All of the value types listed below may be used as a \c property type in a QML
document, with the following exceptions:
\list
+ \li \c void, which marks the absence of a value
\li \c list must be used in conjunction with an object or value type as element
\li \c enumeration cannot be used directly as the enumeration must be defined by a registered QML object type
\endlist
\section2 Built-in Value Types Provided By The QML Language
-The built-in value types supported natively in the QML language are listed below:
+The built-in value types supported natively in the \l{The QML Reference}{QML language} are listed below:
\annotatedlist qmlvaluetypes
\section2 Value Types Provided By QML Modules
QML modules may extend the QML language with more value types.
-For example, the value types provided by the \c QtQuick module are listed below:
+
+For instance, the value types provided by the \c QtQml module are:
+\annotatedlist qtqmlvaluetypes
+
+The value types provided by the \c QtQuick module are:
\annotatedlist qtquickvaluetypes
-The \l{QtQml::Qt}{Qt} global object provides useful functions for manipulating values of value types.
+The \l{QtQml::Qt}{Qt} global object provides \l{globalqtobjecttypes}{useful functions} for manipulating values of value
+types for the \l{Qt Qml} and \l{Qt Quick} modules.
+
+Other Qt modules will document their value types on their respective module pages.
You may define your own value types as described in
\l{qtqml-cppintegration-definetypes.html}{Defining QML Types from C++}.
@@ -202,9 +215,10 @@ property is only invoked when the property is reassigned to a different object v
/*!
\qmlvaluetype string
\ingroup qmlvaluetypes
- \brief a free form text string.
+ \brief A free form text string.
- The \c string type refers to a free form text string in quotes, e.g. "Hello world!".
+ The \c string type refers to a free form text string in quotes, for example
+ "Hello world!". The QML language provides this value type by default.
Example:
\qml
@@ -213,19 +227,43 @@ property is only invoked when the property is reassigned to a different object v
Properties of type \c string are empty by default.
- Strings have a \c length attribute that holds the number of characters in the
- string.
+ Strings have a \c length attribute that holds the number of characters in
+ the string.
- QML extends the JavaScript String type with a \l {String::arg}{arg()} function
- to support value substitution.
+ The string value type is backed by the C++ type QString. It extends the
+ JavaScript String primitive type in that it provides much of the same API,
+ plus some extra methods. For example, the QML string value type method
+ \c {arg()} supports value substitution:
- When integrating with C++, note that any QString value
- \l{qtqml-cppintegration-data.html}{passed into QML from C++} is automatically
- converted into a \c string value, and vice-versa.
+ \qml
+ var message = "There are %1 items"
+ var count = 20
+ console.log(message.arg(count))
+ \endqml
- This value type is provided by the QML language.
+ The example above prints "There are 20 items".
- \sa {QML Value Types}
+ The QML string value type supports most of the ECMAScript string features,
+ such as template (string) literals, string interpolation, multi-line
+ strings, and looping over strings.
+
+ In general, QML string supports most JavaScript String methods, including
+ checking for inclusion using \c string.includes(), \c string.startsWith(),
+ and \c string.endsWith(); repeating a string using \c string.repeats(), and
+ slicing and splitting using \c string.slice() and \c string.split().
+
+ For more information about which version of ECMAScript QML supports, see
+ \l {JavaScript Host Environment}
+
+ For more information about JavaScript String methods, see
+ \l {https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String}
+ {mdn JavaScript String}
+
+ When integrating with C++, note that any QString value
+ \l{qtqml-cppintegration-data.html}{passed into QML from C++} is
+ automatically converted into a \c string value, and vice-versa.
+
+ \sa {QML Value Types}, {ECMA-262}{ECMAScript Language Specification}
*/
/*!
diff --git a/src/qml/doc/src/qmllint/access-singleton-via-object.qdoc b/src/qml/doc/src/qmllint/access-singleton-via-object.qdoc
new file mode 100644
index 0000000000..66a2f86b7c
--- /dev/null
+++ b/src/qml/doc/src/qmllint/access-singleton-via-object.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-access-singleton-via-object.html
+\ingroup qmllint-warnings-and-errors
+
+\title access-singleton-via-object
+\brief BRIEF
+
+\section1 access-singleton-via-object
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/alias-cycle.qdoc b/src/qml/doc/src/qmllint/alias-cycle.qdoc
new file mode 100644
index 0000000000..561cedf416
--- /dev/null
+++ b/src/qml/doc/src/qmllint/alias-cycle.qdoc
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-alias-cycle.html
+\ingroup qmllint-warnings-and-errors
+
+\title Alias Cycle
+\brief Alias property is part of an alias cycle.
+
+\section1 Alias Property Is Part Of An Alias Cycle
+
+\section2 What happened?
+A \l{QML Object Attributes#property-aliases}{property alias} resolves to itself or to another
+alias resolving to itself.
+
+Usually, \l{QML Object Attributes#property-aliases}{a property alias} should reference another
+property either directly, or indirectly by passing through another alias property.
+
+If a property alias directly or indirectly references itself, then it forms an alias cycle.
+The warning indicates that the current alias property is inside or references
+an alias cycle, see \l{#example}{Example}.
+
+\section2 Why is this bad?
+Instances of components with alias cycles will not be created at runtime: they will be null instead.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ id: someId
+ property alias myself: someId.myself // not ok: referring to itself
+
+ property alias cycle: someId.cycle2 // not ok: indirectly referring to itself
+ property alias cycle2: someId.cycle
+
+ property alias indirect: someId.cycle // not ok: referring to alias indirectly referring to itself
+}
+\endqml
+You can fix this warning by breaking up the alias cycles:
+\qml
+import QtQuick
+
+Item {
+ id: someId
+ Item {
+ id: anotherId
+ property string myself
+ property int cycle
+ }
+ property alias myself: anotherId.myself // ok: referring to a property
+
+ property alias cycle: someId.cycle2 // ok: does not refer to itself anymore
+ property alias cycle2: anotherId.cycle // ok: not a cycle anymore
+
+ property alias indirect: someId.cycle // ok: cycle does not form an alias cycle anymore
+}
+\endqml
+*/
diff --git a/src/qml/doc/src/qmllint/attached-property-reuse.qdoc b/src/qml/doc/src/qmllint/attached-property-reuse.qdoc
new file mode 100644
index 0000000000..15d81f6b94
--- /dev/null
+++ b/src/qml/doc/src/qmllint/attached-property-reuse.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-attached-property-reuse.html
+\ingroup qmllint-warnings-and-errors
+
+\title attached-property-reuse
+\brief BRIEF
+
+\section1 attached-property-reuse
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/compiler.qdoc b/src/qml/doc/src/qmllint/compiler.qdoc
new file mode 100644
index 0000000000..7b2eb22f41
--- /dev/null
+++ b/src/qml/doc/src/qmllint/compiler.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-compiler.html
+\ingroup qmllint-warnings-and-errors
+
+\title compiler
+\brief BRIEF
+
+\section1 compiler
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/deferred-property-id.qdoc b/src/qml/doc/src/qmllint/deferred-property-id.qdoc
new file mode 100644
index 0000000000..990322a88e
--- /dev/null
+++ b/src/qml/doc/src/qmllint/deferred-property-id.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-deferred-property-id.html
+\ingroup qmllint-warnings-and-errors
+
+\title deferred-property-id
+\brief BRIEF
+
+\section1 deferred-property-id
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/deprecated.qdoc b/src/qml/doc/src/qmllint/deprecated.qdoc
new file mode 100644
index 0000000000..311796871a
--- /dev/null
+++ b/src/qml/doc/src/qmllint/deprecated.qdoc
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-deprecated.html
+\ingroup qmllint-warnings-and-errors
+
+\title Deprecated
+\brief A deprecated property or type was used.
+
+\section1 Deprecated Binding or Type
+
+\section2 What happened?
+A deprecated type was instantiated, a deprecated property was used or a deprecated method was
+called.
+
+\section2 Why is this bad?
+Types, properties and methods can be deprecated for different reasons, please refer to the
+documentation of the deprecated item to find out why they were deprecated.
+
+You can fix the deprecation warning by following the advice on the deprecation notice in the
+documentation of the deprecated item.
+*/
diff --git a/src/qml/doc/src/qmllint/duplicate-property-binding.qdoc b/src/qml/doc/src/qmllint/duplicate-property-binding.qdoc
new file mode 100644
index 0000000000..4826a552ea
--- /dev/null
+++ b/src/qml/doc/src/qmllint/duplicate-property-binding.qdoc
@@ -0,0 +1,122 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-duplicate-property-binding.html
+\ingroup qmllint-warnings-and-errors
+
+\title Duplicate Bindings
+\brief A property was bound multiple times.
+
+This warning category has multiple warnings:
+\list
+ \li \l{Duplicate Interceptor On Property}
+ \li \l{Cannot Combine Value Source And Binding}
+ \li \l{Duplicate Value Source On Property}
+\endlist
+
+\section1 Duplicate Interceptor On Property
+
+\section2 What happened?
+One property has multiple \l{Property Modifier Types}{interceptors}.
+
+\section2 Why is this bad?
+Setting multiple interceptors on the same property is unsupported by the QML engine.
+
+\section2 Example
+
+Lets use \l{Behavior} as interceptor twice on the same property:
+\qml
+import QtQuick
+
+Rectangle {
+ Behavior on width {
+ NumberAnimation { duration: 1000 }
+ }
+ Behavior on width { // not ok: Duplicate interceptor on property "width" [duplicate-property-binding]
+ NumberAnimation { duration: 2000 }
+ }
+}
+\endqml
+You can fix this warning by removing all but one \l{Behavior}:
+\qml
+import QtQuick
+
+Rectangle {
+ Behavior on width {
+ NumberAnimation { duration: 2000 }
+ }
+}
+\endqml
+
+\b {See also} \l {Property Modifier Types}.
+
+\section1 Duplicate Value Source On Property
+
+\section2 What happened?
+One property has multiple \l{Property Value Sources}{value sources}.
+
+\section2 Why is this bad?
+The value sources will show unexpected behavior when combined. See \l{Example}{example} below.
+
+\section2 Example
+
+Lets use \l{NumberAnimation} as value source twice on the same property:
+\qml
+import QtQuick
+
+Rectangle {
+ NumberAnimation on x { to: 50; duration: 1000 }
+ NumberAnimation on x { to: 10; duration: 100 } // not ok: Duplicate value source on property "x" [duplicate-property-binding]
+
+ onXChanged: console.log(x)
+}
+\endqml
+
+If you check the output of that program, you will see that the two NumberAnimation will interleave
+each other, which is probably not the effect that was intended.
+You can fix this warning by removing all but one \l{NumberAnimation}:
+\qml
+import QtQuick
+
+Rectangle {
+ NumberAnimation on x { to: 50; duration: 1000 }
+}
+\endqml
+
+
+\section1 Cannot Combine Value Source And Binding
+
+\section2 What happened?
+One property has a \l{Property Value Sources}{value source} and a binding on the same property.
+
+\section2 Why is this bad?
+The binding will updated the property value before the value source starts updating this property.
+This may lead to unexpected behavior, and is also harder to read.
+
+\section2 Example
+
+Lets use \l{NumberAnimation} as value source on the same property:
+\qml
+import QtQuick
+
+Rectangle {
+ NumberAnimation on x { to: 50; duration: 1000 } // not ok: Cannot combine value source and binding on property "x" [duplicate-property-binding]
+ x: 55
+
+ onXChanged: console.log(x)
+}
+\endqml
+
+If you check the output of that program, you will see that the \l{NumberAnimation} will animate
+from 55 to 50, which would be easier to read with following code:
+\qml
+import QtQuick
+
+Rectangle {
+ NumberAnimation on x { from: 55; to: 50; duration: 1000 } // ok: intentions are clearer now!
+}
+\endqml
+
+*/
+
diff --git a/src/qml/doc/src/qmllint/duplicated-name.qdoc b/src/qml/doc/src/qmllint/duplicated-name.qdoc
new file mode 100644
index 0000000000..0cc4583cb4
--- /dev/null
+++ b/src/qml/doc/src/qmllint/duplicated-name.qdoc
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-duplicated-name.html
+\ingroup qmllint-warnings-and-errors
+
+\title Duplicated Name
+\brief Multiple signals or properties share the same name in the same Component.
+
+This warning category has multiple warnings:
+\list
+ \li \l{Duplicated Property Name}
+ \li \l{Duplicated Signal Name}
+\endlist
+
+\section1 Duplicated Property Name
+
+\section2 What happened?
+Multiple properties in the same QML component scope have the same name.
+
+\section2 Why is this bad?
+Components with duplicate property names will not be created at runtime: they will be null instead.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ property int helloWorld
+ property int helloWorld
+}
+\endqml
+You can fix this warning by removing the duplicate property or renaming it:
+\qml
+import QtQuick
+
+Item {
+ property int helloWorld
+}
+\endqml
+
+\section1 Duplicated Signal Name
+
+\section2 What happened?
+Multiple signals in the same QML component scope have the same name.
+
+\section2 Why is this bad?
+Components with duplicate signal names will not be created at runtime: they will be null instead.
+
+\section2 Example
+\qml
+import QtQuick
+
+Rectangle {
+ signal helloWorld
+ signal helloWorld
+}
+\endqml
+You can fix this warning by removing the duplicate signal or renaming it:
+\qml
+import QtQuick
+
+Rectangle {
+ signal helloWorld
+}
+
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/import.qdoc b/src/qml/doc/src/qmllint/import.qdoc
new file mode 100644
index 0000000000..c5adababc4
--- /dev/null
+++ b/src/qml/doc/src/qmllint/import.qdoc
@@ -0,0 +1,174 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-import.html
+\ingroup qmllint-warnings-and-errors
+
+\title Warnings Occurred While Importing
+\brief The imported module was not found.
+
+This warning category contains multiple warnings:
+\list
+\li \l{Failed To Import Module}
+\li \l{Component Was Not Found}
+\li \l{Import Qualifier Must Start With A Capital Letter}
+\li \l{Unknown Import Syntax}
+\endlist
+
+\section1 Failed To Import Module
+
+\section2 What happened?
+The module imported via \l{Import Statements}{import statement} was not found.
+
+This can be caused, for example, by
+\list
+ \li a typo in the import statement, or
+ \li a user-defined module that was not built, or
+ \li a wrong \l{Import Statements#qml-import-path}{import path}, or
+ \li a missing module
+\endlist
+
+\section2 Why is this bad?
+The application can't run because it can't find a module it relies on.
+
+\section2 Examples
+
+\section3 Typo In The Import Statement
+\qml
+import QtQuicky // not ok: typo in module name
+
+Item {
+}
+\endqml
+You can fix this warning by correcting the typo:
+\qml
+import QtQuick // ok: no typo in module name
+
+Item {
+}
+\endqml
+
+\section3 User-Defined Module That Was Not Built
+
+Some tooling like \l{\QMLLS Reference}{\QMLLS} or \l{qmllint Reference}{qmllint}
+can't find user-defined modules when they
+are not built. If your project defines the QML Module you are trying to import, then
+the QML tooling will not find it until you build it.
+
+\note If building the module does not help when using \l{\QMLLS Reference}{\QMLLS}, follow the
+instructions in
+\l{Setting up the \QMLLS in Your Editor}{\QMLLS setup instructions}
+and make sure that you communicate the correct build folder to \QMLLS.
+
+\section3 Wrong Import Path
+
+Please refer to \l{Import Statements#qml-import-path}{the QML import path documentation} and to
+\l{Debugging QML Applications#debugging-module-imports}{the debugging module import documentation}
+for more information about import paths.
+
+\section3 Missing Module
+
+If the previous sections did not help to find the imported module, it might be missing.
+This might be caused by a missing dependency. When using external libraries, verify that they are
+actually installed, and that their modules end up in an
+\l{Import Statements#qml-import-path}{import path}.
+
+\section1 Component Was Not Found
+
+\section2 What happened?
+Some component was not found.
+
+\section2 Why is this bad?
+The application can't run because it can't instantiate the non-found component.
+
+\section2 Examples
+
+\section3 Typo In The Component Name
+\qml
+import QtQuick
+
+Item {
+ Itemy {} // not ok: typo in name
+}
+\endqml
+You can fix this warning by correcting the typo:
+\qml
+import QtQuick
+
+Item {
+ Item {} // ok: no typo in name
+}
+\endqml
+
+\section3 Missing Import Statement
+
+\qml
+
+Item { // not ok: must be imported from QtQuick first
+}
+\endqml
+You can fix this warning by adding the missing module import:
+\qml
+import QtQuick
+
+Item { // ok: was imported from QtQuick
+}
+\endqml
+
+\section1 Import Qualifier must start with a capital letter
+
+\section2 What happened?
+Some imported module has an invalid qualifier.
+
+\section2 Why is this bad?
+The module imported with this invalid qualifier can't be used.
+
+\section2 Examples
+
+\qml
+import QtQuick as qq
+
+qq.Item {
+}
+\endqml
+You can fix this warning by making the import qualifier start with an upper case letter:
+\qml
+import QtQuick as Qq
+
+Qq.Item {
+}
+\endqml
+
+\section1 Unknown Import Syntax
+
+\section2 What happened?
+An import statement is using an invalid \l{Import Statements}{import syntax}.
+
+\section2 Why is this bad?
+The application can't run because it can't import a module it relies on.
+
+\section2 Examples
+
+\qml
+import "¯\(ツ)/¯:/path/to/Module"
+import QtQuick
+
+Item {
+}
+\endqml
+You can fix this warning by using URLs that have an allowed scheme:
+\qml
+import "qrc:/path/to/Module"
+import QtQuick
+
+Item {
+}
+\endqml
+
+\note This example assumes that you are not using \l{QQmlAbstractUrlInterceptor}{URL handlers}.
+
+\sa{Import Statements}
+
+*/
+
diff --git a/src/qml/doc/src/qmllint/incompatible-type.qdoc b/src/qml/doc/src/qmllint/incompatible-type.qdoc
new file mode 100644
index 0000000000..6946740b66
--- /dev/null
+++ b/src/qml/doc/src/qmllint/incompatible-type.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-incompatible-type.html
+\ingroup qmllint-warnings-and-errors
+
+\title incompatible-type
+\brief BRIEF
+
+\section1 incompatible-type
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/inheritance-cycle.qdoc b/src/qml/doc/src/qmllint/inheritance-cycle.qdoc
new file mode 100644
index 0000000000..c1be63ae78
--- /dev/null
+++ b/src/qml/doc/src/qmllint/inheritance-cycle.qdoc
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-inheritance-cycle.html
+\ingroup qmllint-warnings-and-errors
+
+\title Inheritance Cycle
+\brief A component inherits from itself.
+
+\section1 Component Is Part Of An Inheritance Cycle
+
+\section2 What happened?
+A component inherited directly or indirectly from itself.
+
+Usually, Components can inherit properties, methods, signals and enums from other components.
+
+If a component inherits itself directly or indirectly through another base component, then
+it forms an inheritance cycle. The warning indicates that the current component is inside an
+inheritance cycle, see \l{#example}{Example}.
+
+\section2 Why is this bad?
+Components with inheritance cycles will not be created at runtime: they will be null instead.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ component Cycle: Cycle {} // not ok: directly inherits from itself
+ component C: C2 {} // not ok: indirectly inherits from itself
+ component C2: C{}
+}
+\endqml
+You can fix this warning by breaking up the inheritance cycle
+\qml
+import QtQuick
+
+Item {
+ component Cycle: Item {} // ok: does not inherit from itself
+ component C: C2 {} // ok: does not indirectly inherits from itself anymore
+ component C2: Cycle{}
+}
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/invalid-lint-directive.qdoc b/src/qml/doc/src/qmllint/invalid-lint-directive.qdoc
new file mode 100644
index 0000000000..39488f291f
--- /dev/null
+++ b/src/qml/doc/src/qmllint/invalid-lint-directive.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-invalid-lint-directive.html
+\ingroup qmllint-warnings-and-errors
+
+\title invalid-lint-directive
+\brief BRIEF
+
+\section1 invalid-lint-directive
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/missing-enum-entry.qdoc b/src/qml/doc/src/qmllint/missing-enum-entry.qdoc
new file mode 100644
index 0000000000..433562f638
--- /dev/null
+++ b/src/qml/doc/src/qmllint/missing-enum-entry.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-missing-enum-entry.html
+\ingroup qmllint-warnings-and-errors
+
+\title missing-enum-entry
+\brief BRIEF
+
+\section1 missing-enum-entry
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/missing-property.qdoc b/src/qml/doc/src/qmllint/missing-property.qdoc
new file mode 100644
index 0000000000..4d1b9a02c3
--- /dev/null
+++ b/src/qml/doc/src/qmllint/missing-property.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-missing-property.html
+\ingroup qmllint-warnings-and-errors
+
+\title missing-property
+\brief BRIEF
+
+\section1 missing-property
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/missing-type.qdoc b/src/qml/doc/src/qmllint/missing-type.qdoc
new file mode 100644
index 0000000000..466370ef39
--- /dev/null
+++ b/src/qml/doc/src/qmllint/missing-type.qdoc
@@ -0,0 +1,240 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-missing-type.html
+\ingroup qmllint-warnings-and-errors
+
+\title Missing Type
+\brief A type used in a binding or alias was not found.
+
+This warning category has multiple warnings:
+\list
+ \li \l{Cannot Deduce Type of Alias}
+ \li \l{No Type Found For Property}
+\endlist
+
+\section1 Cannot Deduce Type of Alias
+
+\section2 What happened?
+An alias property points to a property with a C++ type whose QML counterpart was not found. This can
+be caused by importing a QML module which do not declare its QML dependencies on other modules.
+
+\note If you are importing QML modules with external dependencies, verify that they are
+actually installed, and that their modules end up in an
+\l{Import Statements#qml-import-path}{import path}.
+
+The warning might also indicate that the type of the property referenced by the alias does not have
+a QML counterpart. The referenced property type might be missing the
+\l{QQmlEngine::}{QML_ELEMENT} macro, for example. Refer to
+\l{Defining QML Types from C++} or \l{Overview - QML and C++ Integration} in this case.
+
+\section2 Why is this bad?
+QML tooling is not able to find the QML counterpart of the C++ type: the
+\l{Qt Quick Compiler}{compiler} can't compile this property alias to
+C++ and \l{qmllint Reference}{qmllint} as well as \l{\QMLLS Reference}{\QMLLS}
+can't analyze this property alias.
+
+\section2 Example
+Let our QML module have one C++ class with a property \c{myProperty}:
+
+\code
+#include <QQuickItem>
+#include <QtQml/qqmlregistration.h>
+#include <QObject>
+
+class MyCppObject : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ MyCppObject(QObject *parent = nullptr)
+ : QObject(parent)
+ {}
+
+ Q_PROPERTY(QQuickItem *myProperty READ myProperty WRITE setMyProperty NOTIFY notifyMyProperty)
+ QQuickItem *myProperty() { return m_myProperty; }
+ void setMyProperty(QQuickItem *item) { emit notifyMyProperty(); m_myProperty = item; }
+
+private:
+ QQuickItem *m_myProperty;
+
+signals:
+ void notifyMyProperty();
+};
+\endcode
+
+with following \c{CMakeLists.txt}:
+\badcode
+project(mymodule VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+find_package(Qt6 6.5 REQUIRED COMPONENTS Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(appmymodule
+ main.cpp
+)
+
+qt_add_qml_module(appmymodule
+ URI mymodule
+ VERSION 1.0
+ QML_FILES Main.qml HelloWorld.qml
+ SOURCES mycppobject.cpp mycppobject.h
+)
+
+target_link_libraries(appmymodule
+ PRIVATE Qt6::Quick
+)
+\endcode
+
+The C++ dependency \c{Quick} was declared, such that this class can compile and the QQuickItem
+include can be found. Also, \c{mymodule} does not have any dependency on QtQuick.
+
+Now, let's try to use \c{myProperty} in an alias in QML. The program will run but QML tooling like
+the \l{Qt Quick Compiler}{compiler}, for example, will complain about the usage of \c{myProperty}:
+\qml
+import mymodule
+
+MyCppObject {
+ id: root
+
+ property alias myAlias: root.myProperty // not ok: Cannot deduce type of alias [missing-type]
+}
+\endqml
+The reason for the warning message is that in the QML code, the type \c{QQuickItem} of
+\c{myProperty} and its QML counterpart \c{Item} are not known, even if you have \c{import QtQuick}
+in your QML file. This is because the same type can be exposed multiple times with different
+attributes in different modules: \c{mymodule} actually has to be precise about the QML type of
+\c{myProperty}.
+
+You can fix this warning by adding the dependency in the \c{CMakeLists.txt}:
+\badcode
+qt_add_qml_module(mymodule
+ URI mymodule
+ ...
+ # declarare QML dependency to QtQuick module
+ DEPENDENCIES QtQuick
+ ...
+)
+\endcode
+
+Now, the warning should be gone!
+
+\b {See also} \l {Declaring module dependencies}.
+
+\section1 No Type Found For Property
+
+\section2 What happened?
+A binding was set on a property whose QML type was not found. This can be caused by a QML module
+which does not declare its QML dependencies on other modules.
+
+\note If you are importing QML modules with external dependencies, verify that they are
+actually installed, and that their modules end up in an
+\l{Import Statements#qml-import-path}{import path}.
+
+The warning might also indicate that the type of the property does not have
+a QML counterpart. The property type might be missing the
+\l{QQmlEngine::}{QML_ELEMENT} macro, for example. Refer to
+\l{Defining QML Types from C++} or \l{Overview - QML and C++ Integration} in this case.
+
+\section2 Why is this bad?
+QML tooling is not able to find the QML counterpart of the C++ type: the
+\l{Qt Quick Compiler}{compiler} can't compile this property binding to
+C++ and \l{qmllint Reference}{qmllint} as well as \l{\QMLLS Reference}{\QMLLS} can't analyze this property binding.
+
+\section2 Example
+Let our QML module have a C++ class with two properties, \c{myProperty} and \c{myProperty2}:
+
+\code
+#include <QQuickItem>
+#include <QtQml/qqmlregistration.h>
+#include <QObject>
+
+class MyCppObject : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ MyCppObject(QObject *parent = nullptr)
+ : QObject(parent)
+ {}
+
+ Q_PROPERTY(QQuickItem *myProperty READ myProperty WRITE setMyProperty NOTIFY notifyMyProperty)
+ QQuickItem *myProperty() { return m_myProperty; }
+ void setMyProperty(QQuickItem *item) { emit notifyMyProperty(); m_myProperty = item; }
+
+ Q_PROPERTY(QQuickItem *myProperty2 READ myProperty2 WRITE setMyProperty2 NOTIFY notifyMyProperty2)
+ QQuickItem *myProperty2() { return m_myProperty2; }
+ void setMyProperty2(QQuickItem *item) { emit notifyMyProperty2(); m_myProperty2 = item; }
+
+private:
+ QQuickItem *m_myProperty;
+ QQuickItem *m_myProperty2;
+
+signals:
+ void notifyMyProperty();
+ void notifyMyProperty2();
+};
+\endcode
+
+with following \c{CMakeLists.txt}:
+\badcode
+project(mymodule VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+find_package(Qt6 6.5 REQUIRED COMPONENTS Quick)
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(appmymodule
+ main.cpp
+)
+
+qt_add_qml_module(appmymodule
+ URI mymodule
+ VERSION 1.0
+ QML_FILES Main.qml HelloWorld.qml
+ SOURCES mycppobject.cpp mycppobject.h
+)
+
+target_link_libraries(appmymodule
+ PRIVATE Qt6::Quick
+)
+\endcode
+
+The C++ dependency \c{Quick} was declared, such that this class can compile and the QQuickItem
+include can be found. Also, \c{mymodule} does not have any dependency on QtQuick.
+
+Now, let's try to bind \c{myProperty2} to \c{myProperty} in an alias in QML. The program will run
+but QML tooling like the \l{Qt Quick Compiler}{compiler}, for example, will complain about the
+usage of \c{myProperty}:
+
+\qml
+import mymodule
+
+MyCppObject {
+ id: root
+
+ myProperty: myProperty2 // not ok: No type found for property "myProperty". [missing-type]
+}
+\endqml
+The reason for the warning message is that in the QML code, the type \c{QQuickItem} of \c{myProperty}
+and its QML counterpart \c{Item} are not known: the dependency 'QtQuick' of mymodule was not
+declared in the \c{CMakeLists.txt}.
+
+You can fix this warning by adding the dependency in the \c{CMakeLists.txt}:
+\badcode
+qt_add_qml_module(mymodule
+ URI mymodule
+ ...
+ # declarare QML dependency to QtQuick module
+ DEPENDENCIES QtQuick
+ ...
+)
+\endcode
+
+Now, the warning should be gone!
+
+\b {See also} \l {Declaring module dependencies}.
+*/
+
diff --git a/src/qml/doc/src/qmllint/multiline-strings.qdoc b/src/qml/doc/src/qmllint/multiline-strings.qdoc
new file mode 100644
index 0000000000..4c1aef4e85
--- /dev/null
+++ b/src/qml/doc/src/qmllint/multiline-strings.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-multiline-strings.html
+\ingroup qmllint-warnings-and-errors
+
+\title multiline-strings
+\brief BRIEF
+
+\section1 multiline-strings
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/non-list-property.qdoc b/src/qml/doc/src/qmllint/non-list-property.qdoc
new file mode 100644
index 0000000000..2d778d87bc
--- /dev/null
+++ b/src/qml/doc/src/qmllint/non-list-property.qdoc
@@ -0,0 +1,85 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-non-list-property.html
+\ingroup qmllint-warnings-and-errors
+
+\title Non-List Property
+\brief Multiple values were assigned to a non-list property.
+
+\section1 Cannot Assign Multiple Objects To A Default Non-List Property
+
+\section2 What happened?
+A \l{Default Properties}{default property} has multiple bindings but the default
+property type is not a list type and only expects one binding.
+
+\section2 Why is this bad?
+All the bindings to the default property, except the last one, will be ignored. This most likely
+hints that the default property should instead be a list, or that there are too many bindings to
+the same property.
+
+\section2 Example
+
+Let's declare a component \c{MyComponent} that has one default non-list property, and then lets
+bind three items to that default property:
+\qml
+import QtQuick
+
+Item {
+ component MyComponent: QtObject {
+ default property Item helloWorld
+ }
+ MyComponent {
+ // first item bound to default property:
+ Item { objectName: "first" } // will warn: Cannot assign multiple objects to a default non-list property [non-list-property]
+ // second item bound to default property:
+ Item { objectName: "second" } // not ok: default property was bound already
+ // third item bound to default property:
+ Item { objectName: "third" } // not ok: default property was bound already
+
+ Component.onCompleted: console.log(helloWorld.objectName) // prints "third"
+ }
+}
+
+\endqml
+You can fix this warning by replacing the default property by a list:
+\qml
+import QtQuick
+
+Item {
+ component MyComponent: QtObject {
+ default property list<Item> helloWorld
+ }
+ MyComponent {
+ // first item bound to default property:
+ Item { objectName: "first" } // ok: binding a first item to the list
+ // second item bound to default property:
+ Item { objectName: "second" } // ok: binding a second item to the list
+ // third item bound to default property:
+ Item { objectName: "third" } // ok: binding a third item to the list
+ }
+}
+\endqml
+You can also fix this warning by removing all the unwanted bindings, in case the default property
+is not supposed to be a list:
+\qml
+import QtQuick
+
+Item {
+ component MyComponent: QtObject {
+ default property Item helloWorld
+ }
+ MyComponent {
+ Item { objectName: "first" } // ok: just one item bound to default property
+ }
+ MyComponent {
+ Item { objectName: "second" } // ok: just one item bound to default property
+ }
+ MyComponent {
+ Item { objectName: "third" } // ok: just one item bound to default property
+ }
+}
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/plugin.qdoc b/src/qml/doc/src/qmllint/plugin.qdoc
new file mode 100644
index 0000000000..b2b43ea5f3
--- /dev/null
+++ b/src/qml/doc/src/qmllint/plugin.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-plugin.html
+\ingroup qmllint-warnings-and-errors
+
+\title plugin
+\brief BRIEF
+
+\section1 plugin
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/prefixed-import-type.qdoc b/src/qml/doc/src/qmllint/prefixed-import-type.qdoc
new file mode 100644
index 0000000000..f2f53bcffb
--- /dev/null
+++ b/src/qml/doc/src/qmllint/prefixed-import-type.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-prefixed-import-type.html
+\ingroup qmllint-warnings-and-errors
+
+\title prefixed-import-type
+\brief BRIEF
+
+\section1 prefixed-import-type
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/qtqml-qmllint-warnings-and-errors.qdoc b/src/qml/doc/src/qmllint/qtqml-qmllint-warnings-and-errors.qdoc
new file mode 100644
index 0000000000..894f5f4306
--- /dev/null
+++ b/src/qml/doc/src/qmllint/qtqml-qmllint-warnings-and-errors.qdoc
@@ -0,0 +1,9 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\group qmllint-warnings-and-errors
+\title QML Lint Warning and Errors
+
+Here is an overview over all QML Lint warning and error messages.
+*/
diff --git a/src/qml/doc/src/qmllint/read-only-property.qdoc b/src/qml/doc/src/qmllint/read-only-property.qdoc
new file mode 100644
index 0000000000..7bc6b5fcc2
--- /dev/null
+++ b/src/qml/doc/src/qmllint/read-only-property.qdoc
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-read-only-property.html
+\ingroup qmllint-warnings-and-errors
+
+\title Readonly Property
+\brief A readonly property was written.
+
+\section1 Cannot Assign To Read-Only Property
+
+\section2 What happened?
+A \l{Read-Only Properties}{read-only property} was written.
+
+\section2 Why is this bad?
+The QML engine will throw a Type Error when it sees the write to a read-only property.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ id: root
+ readonly property int someNumber: 10
+
+ Component.onCompleted: {
+ someNumber = 20 // not ok: TypeError: Cannot assign to read-only property
+ }
+}
+\endqml
+You can fix this warning by removing the write to the read-only property, by writing to another
+non-read-only property, or by removing the readonly modifier if the property should no longer be
+considered constant.
+
+\sa{Read-Only Properties}
+*/
+
diff --git a/src/qml/doc/src/qmllint/recursion-depth-errors.qdoc b/src/qml/doc/src/qmllint/recursion-depth-errors.qdoc
new file mode 100644
index 0000000000..ed0d2c09b2
--- /dev/null
+++ b/src/qml/doc/src/qmllint/recursion-depth-errors.qdoc
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-recursion-depth-errors.html
+\ingroup qmllint-warnings-and-errors
+
+\title Recursion Depths Errors
+\brief Qml statement or expression is too deeply nested.
+
+\section1 Maximum Statement Or Expression Depth Exceeded
+\section2 What happened?
+A QML statement or expression was too deeply nested for the compiler. This usually only happens for
+generated code where statements or expressions can be very long, as the recursion limit is usually
+large enough for any sensible QML document.
+
+\section2 Why is this bad?
+The QML engine will not be able to run this code.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ function f() {
+ let x = 1 + 1 + .... + 1 // maximum depth exceeded: add too many ones together
+ return x
+ }
+
+ Item { Item { .... } } // maximum depth exceeded: too many nested Item's
+}
+\endqml
+
+You can fix this warning by auto-generating smaller code pieces. You could split deeply nested
+Components in multiple files or inline components, or split deeply nested expressions into multiple
+expressions:
+\qml
+import QtQuick
+
+Item {
+ function f() {
+ let x = 1 + 1 + .... + 1 // first half of the split
+ x += 1 + 1 + .... + 1 // second half of the split
+ return x
+ }
+
+ component NestedItem : Item { Item {... }} // first half of the nested Item
+ component DeeplyNestedItem: Item { ... NestedItem{} ... } // second half of the nested Items + NestedItem
+ DeeplyNestedItem {}
+}
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/required.qdoc b/src/qml/doc/src/qmllint/required.qdoc
new file mode 100644
index 0000000000..6252ddef2d
--- /dev/null
+++ b/src/qml/doc/src/qmllint/required.qdoc
@@ -0,0 +1,65 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-required.html
+\ingroup qmllint-warnings-and-errors
+
+\title Component is Missing a Required Property
+\brief A component's required property was not bound.
+
+\section1 Component is Missing a Required Property
+
+\section2 What happened?
+The \l{QML Object Attributes#required-properties}{required property} of a component was not set.
+
+
+\section2 Why is this bad?
+QML applications where components miss required properties will misbehave: they will not
+start at all if a missing required property is detected statically. Dynamically created components
+with missing required properties will not be created at runtime: they will be null instead.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ component RepeatMe: Item {
+ required property int index;
+ required property int helloWorld;
+ }
+
+ RepeatMe {} // not ok: required properties index and helloWorld not set
+
+ Repeater {
+ model: 10
+ RepeatMe {} // not ok: required property index set by Repeater, but not helloWorld
+ }
+}
+\endqml
+You can fix this warning by setting the required properties
+\qml
+import QtQuick
+
+Item {
+ component RepeatMe: Item {
+ required property int index;
+ required property int helloWorld;
+ }
+
+ RepeatMe {
+ index: 0
+ helloWorld: 42
+ } // ok: all required properties were set
+
+ Repeater {
+ model: 10
+ RepeatMe {
+ helloWorld: index * 2 + 1
+ } // ok: all required properties were set: index by the Repeater and helloWorld by the user
+ }
+}
+\endqml
+
+\sa {QML Coding Conventions#required-properties}{QML Coding Conventions - Required Properties}
+*/
diff --git a/src/qml/doc/src/qmllint/restricted-type.qdoc b/src/qml/doc/src/qmllint/restricted-type.qdoc
new file mode 100644
index 0000000000..d240f55e4a
--- /dev/null
+++ b/src/qml/doc/src/qmllint/restricted-type.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-restricted-type.html
+\ingroup qmllint-warnings-and-errors
+
+\title restricted-type
+\brief BRIEF
+
+\section1 restricted-type
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/signal-handler-parameters.qdoc b/src/qml/doc/src/qmllint/signal-handler-parameters.qdoc
new file mode 100644
index 0000000000..a6510b566b
--- /dev/null
+++ b/src/qml/doc/src/qmllint/signal-handler-parameters.qdoc
@@ -0,0 +1,261 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-signal-handler-parameters.html
+\ingroup qmllint-warnings-and-errors
+
+\title Signal Handler Parameters
+\brief The signal handler does not satisfy the signal types.
+
+This warning category has multiple warnings:
+\list
+ \li \l{Type Of Parameter In Signal Was Not Found}
+ \li \l{Type Of Parameter In Signal Cannot Be Used}
+ \li \l{The Signal Has A Parameter Of The Same Name}
+\endlist
+
+
+\section1 Type Of Parameter In Signal Was Not Found
+
+\section2 What happened?
+A signal handler tried to handle a signal with parameters of unknown QML types.
+
+Usually, this happens when handling C++ defined signals in QML when the module with the C++ defined
+signal does not properly declare its QML dependency to another QML module. If the module with the
+C++ defined signal compiles, then this is a sign that a dependency was only declared on the C++
+level and not on \l{qt_add_qml_module#declaring-module-dependencies}{the QML module level}.
+
+\note If you are importing QML modules with external dependencies, verify that they are
+actually installed, and that their modules end up in an
+\l{Import Statements#qml-import-path}{import path}.
+
+The warning might also indicate that the parameter type of the C++ defined signal does not have
+a QML counterpart. The parameter type might be missing the
+\l{QQmlEngine Class#QML_ELEMENT}{QML_ELEMENT} macro, for example. Refer to
+\l{Defining QML Types from C++} or \l{Overview - QML and C++ Integration} in this case.
+
+\section2 Why is this bad?
+In the first case, the module with the C++ signal has an undeclared dependency on the QML module
+level, which makes it hard to use the module, as users of the module need to guess the module's
+hidden dependencies.
+
+In both cases, QML tooling is not able to find the QML counterpart of the
+C++ type: the \l{Qt Quick Compiler}{compiler} can't compile this signal handler to
+C++ and \l{qmllint Reference}{qmllint} as well as \l{\QMLLS Reference}{\QMLLS}
+can't analyze this handler.
+
+\section2 Example
+
+Let our module have a C++ class with one \c{helloWorld} signal:
+\code
+#include <QQuickItem>
+#include <QtQml/qqmlregistration.h>
+#include <QObject>
+
+class MyCppObject : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ MyCppObject(QObject *parent = nullptr)
+ : QObject(parent)
+ {}
+
+signals:
+ void helloWorld(QQuickItem *i);
+
+};
+\endcode
+with following CMakeLists.txt:
+\badcode
+find_package(Qt6 6.5 REQUIRED COMPONENTS Quick QuickControls2)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(mymodule
+ main.cpp
+)
+
+qt_add_qml_module(mymodule
+ URI MyModule
+ VERSION 1.0
+ QML_FILES Main.qml
+ SOURCES mycppobject.cpp mycppobject.h
+)
+
+# declare C++ dependency to Quick
+target_link_libraries(appuntitled27
+ PRIVATE Qt6::Quick
+)
+\endcode
+The C++ dependency \c{Quick} was declared, such that this class can compile and the QQuickItem
+include can be found. Also, mymodule does not have any dependency on QtQuick.
+
+Now, lets try to handle this \c{helloWorld} signal in QML:
+\qml
+import MyModule // name of the module with MyCppObject
+
+MyCppObject {
+ onHelloWorld: function (x) { console.log(x); } // not ok: Type QQuickItem was not found!
+}
+\endqml
+
+The reason of the warning message is that in the QML code, \c{QQuickItem} and its QML counterpart
+\c{Item} are not known: the dependency 'QtQuick' of MyModule was not declared in the CMakeLists.txt!
+
+You can add it as following in the qt_add_qml_module() call:
+\badcode
+qt_add_qml_module(mymodule
+ URI MyModule
+ ...
+ # declare QML dependencies to QtQuick:
+ DEPENDENCIES QtQuick
+ ...
+)
+\endcode
+
+Now, the QML code should be fine again!
+
+\sa {qt_add_qml_module#declaring-module-dependencies}
+
+\omit
+TODO: QML Lint cannot detect if you pass signal parameters by value, reference or pointer!
+Therefore, it will never print that warning.
+\section1 Type Of Parameter In Signal Should Be Passed By Pointer
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+
+TODO: QML Lint cannot detect if you pass signal parameters by value, reference or pointer!
+Therefore, it will never print that warning.
+that warning
+\section1 Type Of Parameter In Signal Should Be Passed By Value Or Const Reference
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+
+\endomit
+
+\section1 Signal Handler Has More Formal Parameters Than The Signal It Handles
+\section2 What happened?
+A signal handler expects more parameters than what the signal will actually provide.
+
+\section2 Why is this bad?
+The extra parameters will be undefined.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ signal helloWorld(x: QtObject) // signal expects only one parameter
+
+ onHelloWorld: function (x,y,z) {} // not ok: signal handler handles three parameters
+}
+\endqml
+You can fix this warning by removing the extra parameters of the signal handler:
+\qml
+import QtQuick
+
+Item {
+ signal helloWorld(x: QtObject) // signal expects only one parameter
+
+ onHelloWorld: function (x) {} // ok: signal handler handles one parameter
+}
+\endqml
+
+It can also be fixed by adding the missing parameters to the signal's declaration:
+\qml
+import QtQuick
+
+Item {
+ signal helloWorld(x: QtObject, y: int, y: int) // signal expects three parameters
+
+ onHelloWorld: function (x,y,z) {} // ok: signal handler handles three parameters
+}
+\endqml
+
+\section1 The Signal Has A Parameter Of The Same Name
+\section2 What happened?
+The signal or signal handler might have swapped some of its arguments, or some arguments might be
+missing.
+
+\section2 Why is this bad?
+This is very probably a typo and not intended by the user.
+
+\section2 Example
+\section3 Missing Arguments
+\qml
+import QtQuick
+
+Item {
+ signal helloWorld(x: QtObject, y: int)
+
+ onHelloWorld: function (y) {} // not ok: it seems that x was forgotten
+}
+
+\endqml
+You can fix this warning by adding the missing parameters:
+\qml
+import QtQuick
+
+Item {
+ signal helloWorld(x: QtObject, y: int)
+
+ onHelloWorld: function (x, y) {} // ok: parameters have the same order as in helloWorld
+}
+\endqml
+or by renaming the first parameter:
+\qml
+import QtQuick
+
+Item {
+ signal helloWorld(x: QtObject, y: int)
+
+ onHelloWorld: function (x) {} // ok: parameters have the same order as in helloWorld, even if y is missing
+}
+\endqml
+
+\section3 Swapped Arguments
+\qml
+import QtQuick
+
+Item {
+ signal helloWorld(x: QtObject, y: int)
+
+ onHelloWorld: function (y, x) {} // not ok: helloWorld expects first 'x' then 'y'
+}
+
+\endqml
+You can fix this warning by reordering the parameters in the correct order
+\qml
+import QtQuick
+
+Item {
+ signal helloWorld(x: QtObject, y: int)
+
+ onHelloWorld: function (x, y) {} // ok: parameters have the same order as in helloWorld
+}
+
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/syntax.duplicate-ids.qdoc b/src/qml/doc/src/qmllint/syntax.duplicate-ids.qdoc
new file mode 100644
index 0000000000..e1f796c8fd
--- /dev/null
+++ b/src/qml/doc/src/qmllint/syntax.duplicate-ids.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-syntax.duplicate-ids.html
+\ingroup qmllint-warnings-and-errors
+
+\title syntax.duplicate-ids
+\brief BRIEF
+
+\section1 syntax.duplicate-ids
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/syntax.id-quotation.qdoc b/src/qml/doc/src/qmllint/syntax.id-quotation.qdoc
new file mode 100644
index 0000000000..b335bbc962
--- /dev/null
+++ b/src/qml/doc/src/qmllint/syntax.id-quotation.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-syntax.id-quotation.html
+\ingroup qmllint-warnings-and-errors
+
+\title syntax.id-quotation
+\brief BRIEF
+
+\section1 syntax.id-quotation
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/syntax.qdoc b/src/qml/doc/src/qmllint/syntax.qdoc
new file mode 100644
index 0000000000..e2e59e8dce
--- /dev/null
+++ b/src/qml/doc/src/qmllint/syntax.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-syntax.html
+\ingroup qmllint-warnings-and-errors
+
+\title syntax
+\brief BRIEF
+
+\section1 syntax
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/top-level-component.qdoc b/src/qml/doc/src/qmllint/top-level-component.qdoc
new file mode 100644
index 0000000000..111a1e4bf6
--- /dev/null
+++ b/src/qml/doc/src/qmllint/top-level-component.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-top-level-component.html
+\ingroup qmllint-warnings-and-errors
+
+\title top-level-component
+\brief BRIEF
+
+\section1 top-level-component
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/uncreatable-type.qdoc b/src/qml/doc/src/qmllint/uncreatable-type.qdoc
new file mode 100644
index 0000000000..05dcb53edc
--- /dev/null
+++ b/src/qml/doc/src/qmllint/uncreatable-type.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-uncreatable-type.html
+\ingroup qmllint-warnings-and-errors
+
+\title uncreatable-type
+\brief BRIEF
+
+\section1 uncreatable-type
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/unqualified.qdoc b/src/qml/doc/src/qmllint/unqualified.qdoc
new file mode 100644
index 0000000000..7f2e315efe
--- /dev/null
+++ b/src/qml/doc/src/qmllint/unqualified.qdoc
@@ -0,0 +1,52 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-unqualified.html
+\ingroup qmllint-warnings-and-errors
+
+\title Unqualified Access
+\brief Accessing an outer scope without its id.
+
+\section1 Unqualified Access
+
+\section2 What happened?
+
+A parent element was accessed without its \l{QML Object Attributes#the-id-attribute}{id}.
+
+\section2 Why is this bad?
+
+This makes the code harder to read and impedes performance.
+
+\section2 Example
+
+\qml
+import QtQuick
+
+Item {
+ property int helloWorld
+ Item {
+ property int unqualifiedAccess: helloWorld + 1 // not ok: Unqualified access here.
+ }
+}
+\endqml
+
+You can fix this warning by referring to the parent object by
+\l{QML Object Attributes#the-id-attribute}{id}.
+If the object currently has no \l{QML Object Attributes#the-id-attribute}{id}, you will need to add
+one first.
+
+\qml
+import QtQuick
+
+Item {
+ id: root
+ property int helloWorld
+ Item {
+ property int unqualifiedAccess: root.helloWorld + 1 // ok: this access is qualified now!
+ }
+}
+\endqml
+
+\sa {QML Coding Conventions#unqualified-access}{QML Coding Conventions - Unqualified Access}
+*/
diff --git a/src/qml/doc/src/qmllint/unresolved-alias.qdoc b/src/qml/doc/src/qmllint/unresolved-alias.qdoc
new file mode 100644
index 0000000000..63b02613dd
--- /dev/null
+++ b/src/qml/doc/src/qmllint/unresolved-alias.qdoc
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-unresolved-alias.html
+\ingroup qmllint-warnings-and-errors
+
+\title Unresolved Alias
+\brief Property of property alias was not found.
+
+\section1 Unresolved Alias
+
+\section2 What happened?
+A property alias should hold a reference to another property, see also
+\l{QML Object Attributes#property-aliases}{QML Object Attributes - Property Aliases}.
+In this case, it holds a reference to a property that was not found.
+
+\section2 Why is this bad?
+Instances of components with unresolved alias will not be created at runtime:
+they will be null instead.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ id: someId
+ property int helloWorld
+
+ property alias helloWorldAlias: helloWorld // not ok: aliases have to refer by id
+ property alias helloWorldAlias2: someId.helloWorlddd // not ok: no helloWorlddd in someId
+ property alias helloWorldAlias3: someIddd.helloWorld // not ok: someIddd does not exist
+}
+
+\endqml
+You can fix this warning by making sure that the id and the properties of the alias property
+really do exist:
+\qml
+import QtQuick
+
+Item {
+ id: someId
+ property int helloWorld
+
+ property alias helloWorldAlias: someId.helloWorld // ok: alias refers by id
+ property alias helloWorldAlias2: someId.helloWorld // ok: helloWorld does exist in someId
+ property alias helloWorldAlias3: someId.helloWorld // ok: someId does exist
+}
+\endqml
+*/
diff --git a/src/qml/doc/src/qmllint/unresolved-type.qdoc b/src/qml/doc/src/qmllint/unresolved-type.qdoc
new file mode 100644
index 0000000000..5b0a943fe8
--- /dev/null
+++ b/src/qml/doc/src/qmllint/unresolved-type.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-unresolved-type.html
+\ingroup qmllint-warnings-and-errors
+
+\title unresolved-type
+\brief BRIEF
+
+\section1 unresolved-type
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/unused-imports.qdoc b/src/qml/doc/src/qmllint/unused-imports.qdoc
new file mode 100644
index 0000000000..58821afb88
--- /dev/null
+++ b/src/qml/doc/src/qmllint/unused-imports.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-unused-imports.html
+\ingroup qmllint-warnings-and-errors
+
+\title unused-imports
+\brief BRIEF
+
+\section1 unused-imports
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/use-proper-function.qdoc b/src/qml/doc/src/qmllint/use-proper-function.qdoc
new file mode 100644
index 0000000000..d87667a6f9
--- /dev/null
+++ b/src/qml/doc/src/qmllint/use-proper-function.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-use-proper-function.html
+\ingroup qmllint-warnings-and-errors
+
+\title use-proper-function
+\brief BRIEF
+
+\section1 use-proper-function
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/var-used-before-declaration.qdoc b/src/qml/doc/src/qmllint/var-used-before-declaration.qdoc
new file mode 100644
index 0000000000..5089ecf276
--- /dev/null
+++ b/src/qml/doc/src/qmllint/var-used-before-declaration.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-var-used-before-declaration.html
+\ingroup qmllint-warnings-and-errors
+
+\title var-used-before-declaration
+\brief BRIEF
+
+\section1 var-used-before-declaration
+
+\section2 What happened?
+TODO
+
+\section2 Why is this bad?
+TODO
+
+\section2 Example
+\qml
+\endqml
+You can fix this warning by TODO
+\qml
+\endqml
+*/
+
diff --git a/src/qml/doc/src/qmllint/with.qdoc b/src/qml/doc/src/qmllint/with.qdoc
new file mode 100644
index 0000000000..bfdde2e86b
--- /dev/null
+++ b/src/qml/doc/src/qmllint/with.qdoc
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qmllint-warnings-and-errors-with.html
+\ingroup qmllint-warnings-and-errors
+
+\title With Statements
+\brief With statements are strongly discouraged in QML.
+
+\section1 With Statements
+
+\section2 What happened?
+The JavaScript \c{with} statement was used.
+
+\section2 Why is this bad?
+With statements might cause false positives when analysing unqualified identifiers. Also, \c{with}
+statements are
+\l{https://262.ecma-international.org/#sec-with-statement}{marked as deprecated by the latest JavaScript standard}.
+
+\section2 Example
+\qml
+import QtQuick
+
+Item {
+ function f() {
+ with (Math) {
+ return PI
+ }
+ }
+}
+\endqml
+You can fix this warning by replacing the \c{with} statement with a destructuring property,
+for example:
+\qml
+import QtQuick
+
+Item {
+ function f() {
+ const { PI } = Math;
+ return PI
+ }
+}
+
+\endqml
+
+\note You can find more replacement ideas
+\l{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with?retiredLocale=de#examples}{here}.
+*/
+
diff --git a/src/qml/doc/src/qmlsingletons.qdoc b/src/qml/doc/src/qmlsingletons.qdoc
new file mode 100644
index 0000000000..ad441eca85
--- /dev/null
+++ b/src/qml/doc/src/qmlsingletons.qdoc
@@ -0,0 +1,339 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qml-singleton.html
+\title Singletons in QML
+\brief A guide for using singletons in QML
+
+In QML, a singleton is an object which is created at most once per
+\l{QQmlEngine}{engine}. In this guide, we'll
+\l{How can singletons be created in QML}{explain how to create} singletons
+and \l{Accessing singletons}{how to use them}. We'll also provide some
+best practices for working with singletons.
+
+\section1 How can singletons be created in QML?
+
+There are two separate ways of creating singletons in QML. You can either define
+the singleton in a QML file, or register it from C++.
+
+\section2 Defining singletons in QML
+To define a singleton in QML, you first have to add
+\code
+pragma singleton
+\endcode
+to the top of your file.
+There's one more step: You will need to add an entry to the QML module's
+\l{Module Definition qmldir Files}{qmldir file}.
+
+\section3 Using qt_add_qml_module (CMake)
+When using CMake, the qmldir is automatically created by \l{qt_add_qml_module}.
+To indicate that the QML file should be turned into a singleton, you need to set
+the \c{QT_QML_SINGLETON_TYPE}
+file property on it:
+\code
+set_source_files_properties(MySingleton.qml
+ PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
+\endcode
+
+You can pass multiple files at once to \c{set_source_files_properties}:
+\code
+set(plain_qml_files
+ MyItem1.qml
+ MyItem2.qml
+ FancyButton.qml
+)
+set(qml_singletons
+ MySingleton.qml
+ MyOtherSingleton.qml
+)
+set_source_files_properties(${qml_singletons}
+ PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
+qt_add_qml_module(myapp
+ URI MyModule
+ QML_FILES ${plain_qml_files} ${qml_singletons}
+)
+\endcode
+
+\note set_source_files_properties needs to be called before \c{qt_add_qml_module}
+
+\section3 Without qt_add_qml_module
+If you aren't using \c{qt_add_qml_module}, you'll need to manually create a
+\l{Module Definition qmldir Files}{qmldir file}.
+There, you'll need to mark your singletons accordingly:
+\code
+module MyModule
+singleton MySingleton 1.0 MySingleton.qml
+singleton MyOtherSingleton 1.0 MyOtherSingleton.qml
+\endcode
+See also \l{Object Type Declaration} for more details.
+
+
+\section2 Defining singletons in C++
+
+There are multiple ways of exposing singletons to QML from C++. The main
+difference depends on whether a new instance of a class should be created when
+needed by the QML engine; or if some existing object needs to be exposed to a
+QML program.
+
+\section3 Registering a class to provide singletons
+
+The simplest way of defining a singleton is to have a default-constructible
+class, which derives from QObject and mark it with the \l{QML_SINGLETON} and
+\l{QML_ELEMENT} macros.
+\code
+class MySingleton : public QObject
+{
+ Q_OBJECT
+ QML_SINGLETON
+ QML_ELEMENT
+public:
+ MySingleton(QObject *parent = nullptr) : QObject(parent) {
+ // ...
+ }
+};
+\endcode
+This will register the \c{MySingleton} class under the name \c{MySingleton} in
+the QML module to which the file belongs.
+If you want to expose it under a different name, you can use \l{QML_NAMED_ELEMENT}
+instead.
+
+If the class can't be made default-constructible, or if you need access to
+the \l{QQmlEngine} in which the singleton is instantiated, it is possible to
+use a static create function instead. It must have the signature
+\c{MySingleton *create(QQmlEngine *, QJSEngine *)}, where \c{MySingleton} is
+the type of the class that gets registered.
+\code
+class MyNonDefaultConstructibleSingleton : public QObject
+{
+ Q_OBJECT
+ QML_SINGLETON
+ QML_NAMED_ELEMENT(MySingleton)
+public:
+ MyNonDefaultConstructibleSingleton(QJSValue id, QObject *parent = nullptr)
+ : QObject(parent)
+ , m_symbol(std::move(id))
+ {}
+
+ static MyNonDefaultConstructibleSingleton *create(QQmlEngine *qmlEngine, QJSEngine *)
+ {
+ return new MyNonDefaultConstructibleSingleton(qmlEngine->newSymbol(u"MySingleton"_s));
+ }
+
+private:
+ QJSValue m_symbol;
+};
+\endcode
+
+\note The create function takes both a \l{QJSEngine} and a \l{QQmlEngine} parameter. That is
+for historical reasons. They both point to the same object which is in fact a QQmlEngine.
+
+\section3 Exposing an existing object as a singleton
+
+Sometimes, you have an existing object that might have been created via
+some third-party API. Often, the right choice in this case is to have one
+singleton, which exposes those objects as its properties (see
+\l{Grouping together related data}).
+But if that is not the case, for example because there is only a single object that needs
+to be exposed, use the following approach to expose an instance of type
+\c{MySingleton} to the engine.
+We first expose the Singleton as a \l{QML_FOREIGN}{foreign type}:
+\code
+struct SingletonForeign
+{
+ Q_GADGET
+ QML_FOREIGN(MySingleton)
+ QML_SINGLETON
+ QML_NAMED_ELEMENT(MySingleton)
+public:
+
+ inline static MySingleton *s_singletonInstance = nullptr;
+
+ static MySingleton *create(QQmlEngine *, QJSEngine *engine)
+ {
+ // The instance has to exist before it is used. We cannot replace it.
+ Q_ASSERT(s_singletonInstance);
+
+ // The engine has to have the same thread affinity as the singleton.
+ Q_ASSERT(engine->thread() == s_singletonInstance->thread());
+
+ // There can only be one engine accessing the singleton.
+ if (s_engine)
+ Q_ASSERT(engine == s_engine);
+ else
+ s_engine = engine;
+
+ // Explicitly specify C++ ownership so that the engine doesn't delete
+ // the instance.
+ QJSEngine::setObjectOwnership(s_singletonInstance,
+ QJSEngine::CppOwnership);
+ return s_singletonInstance;
+ }
+
+private:
+ inline static QJSEngine *s_engine = nullptr;
+};
+\endcode
+Then we set \c{SingletonForeign::s_singletonInstance} before we start
+the first engine
+\code
+SingletonForeign::s_singletonInstance = getSingletonInstance();
+QQmlApplicationEngine engine;
+engine.loadFromModule("MyModule", "Main");
+\endcode
+
+\note It can be very tempting to simply use \l{qmlRegisterSingletonInstance} in
+this case. However, be wary of the pitfalls of imperative type registration
+listed in the next section.
+
+\section3 Imperative type registration
+Before Qt 5.15, all types, including singletons were registered via the
+\c{qmlRegisterType} API. Singletons specifically were registered via either
+\l{qmlRegisterSingletonType} or \l{qmlRegisterSingletonInstance}. Besides the
+minor annoyance of having to repeat the module name for each type and the forced
+decoupling of the class declaration and its registration, the major problem with
+that approach was that it is tooling unfriendly: It was not statically possible
+to extract all the necessary information about the types of a module at compile
+time. The declarative registration solved this issue.
+
+\note There is one remaining use case for the imperative \c{qmlRegisterType} API:
+It is a way to expose a singleton of non-QObject type as a \c{var} property via
+\l{qmlRegisterSingletonType}{the QJSValue based \c{qmlRegisterSingletonType} overload}
+. Prefer the alternative: Expose that value as the property of a (\c{QObject}) based
+singleton, so that type information will be available.
+
+\section2 Accessing singletons
+Singletons can be accessed both from QML as well as from C++. In QML, you need
+to import the containing module. Afterwards, you can access the singleton via its
+name. Reading its properties and writing to them inside JavaScript contexts is
+done in the same way as with normal objects:
+
+\code
+import QtQuick
+import MyModule
+
+Item {
+ x: MySingleton.posX
+ Component.onCompleted: MySingleton.ready = true;
+}
+\endcode
+
+Setting up bindings on a singletons properties is not possible; however, if it
+is needed, a \l{Binding} element can be used to achieve the same result:
+\code
+import QtQuick
+import MyModule
+
+Item {
+ id: root
+ Binding {
+ target: MySingleton
+ property: "posX"
+ value: root.x
+ }
+}
+\endcode
+
+\note Care must be taken when installing a binding on a singleton property: If
+done by more than one file, the results are not defined.
+
+\section1 Guidelines for (not) using singletons
+
+Singletons allow you to expose data which needs to be accessed in multiple places
+to the engine. That can be globally shared settings, like the spacing between
+elements, or data models which need to be displayed in multiple places.
+Compared to context properties which can solve a similar use case,
+they have the benefit of being typed, being supported by tooling like the
+\l{\QMLLS Reference}{\QMLLS}, and they are also generally faster at runtime.
+
+It is recommended not to register too many singletons in a module: Singletons,
+once created, stay alive until the engine itself gets destroyed
+and come with the drawbacks of shared state as they are part of the global state.
+Thus consider using the following techniques to reduce the amount of singletons
+in your application:
+
+\section2 Grouping together related data
+Adding one singleton for each object which you want to expose adds quite some boiler plate.
+Most of the time, it makes more sense to group data you want to expose together as properties
+of a single singleton. Assume for instance that you want to create an ebook reader
+where you need to expose three \l{QAbstractItemModel}{abstract item models}, one
+for local books, and two for remote sources. Instead of repeating the process
+for \l{Exposing an existing object as a singleton}{exposing existing objects}
+three times, you can instead create one singleton and set it up before starting
+the main application:
+\code
+class GlobalState : QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+ Q_PROPERTY(QAbstractItemModel* localBooks MEMBER localBooks)
+ Q_PROPERTY(QAbstractItemModel* digitalStoreFront MEMBER digitalStoreFront)
+ Q_PROPERTY(QAbstractItemModel* publicLibrary MEMBER publicLibrary)
+public:
+ QAbstractItemModel* localBooks;
+ QAbstractItemModel* digitalStoreFront;
+ QAbstractItemModel* publicLibrary
+};
+
+int main() {
+ QQmlApplicationEngine engine;
+ auto globalState = engine.singletonInstance<GlobalState *>("MyModule", "GlobalState");
+ globalState->localBooks = getLocalBooks();
+ globalState->digitalStoreFront = setupLoalStoreFront();
+ globalState->publicLibrary = accessPublicLibrary();
+ engine.loadFromModule("MyModule", "Main");
+}
+\endcode
+
+\section2 Use object instances
+In the last section, we had the example of exposing three models as members of a
+singleton. That can be useful when either the models need to be used in multiple
+places, or when they are provided by some external API over which we have no
+control. However, if we need the models only in a single place it might make
+more sense have them as an instantiable type. Coming back to the previous example,
+we can add an instantiable RemoteBookModel class, and then instantiate it inside
+the book browser QML file:
+
+
+\code
+// remotebookmodel.h
+class RemoteBookModel : public QAbstractItemModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
+ // ...
+};
+
+// bookbrowser.qml
+Row {
+ ListView {
+ model: RemoteBookModel { url: "www.public-lib.example"}
+ }
+ ListView {
+ model: RemoteBookModel { url: "www.store-front.example"}
+ }
+}
+
+\endcode
+
+\section2 Passing initial state
+
+While singletons can be used to pass state to QML, they are wasteful when the
+state is only needed for the initial setup of the application. In that case, it
+is often possible to use \l{QQmlApplicationEngine::setInitialProperties}.
+You might for instance want to set \l{Window::visibility} to fullscreen if
+a corresponding command line flag has been set:
+\code
+QQmlApplicationEngine engine;
+if (parser.isSet(fullScreenOption)) {
+ // assumes root item is ApplicationWindow
+ engine.setInitialProperties(
+ { "visibility", QVariant::fromValue(QWindow::FullScreen)}
+ );
+}
+engine.loadFromModule("MyModule, "Main");
+\endcode
+
+*/
diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc
index 4efa25c989..e6896addf6 100644
--- a/src/qml/doc/src/qmltypereference.qdoc
+++ b/src/qml/doc/src/qmltypereference.qdoc
@@ -7,9 +7,9 @@
\ingroup qmlmodules
\brief List of QML types provided by the Qt QML module
-The \l{Qt QML} module provides the definition and implementation of various
-convenience types which can be used with the QML language, including some
-elementary QML types which can provide the basis for further extensions to the
+The \l{Qt Qml} module provides the definition and implementation of various
+convenience types that can be used with the QML language. This includes
+elementary QML types, which can provide the basis for further extensions to the
QML language. The \l QtObject and \l Component object types are non-visual and
provide building-blocks for extensions to QML.
@@ -25,24 +25,29 @@ To use the module, import the \c QtQml module with the following statement:
import QtQml
\endqml
-Most clients will never need to use the \c QtQml import, as all of the types
-are also provided by the \c QtQuick namespace which may be imported as
-follows:
+Many clients will never need to use the \c QtQml module directly, but will rather
+import it indirectly via the \c QtQuick module as follows:
\qml
import QtQuick
\endqml
-See the \l{Qt Quick} module documentation for more information about the \c
-QtQuick namespace and what it provides to QML application developers.
+See the \l{Qt Quick} module documentation for more information about its types.
-The QML types for creating lists and models, such as \l ListModel and \l
-ListElement, are moved to a submodule, \c QtQml.Models. The \l{Qt QML Models QML
-Types}{Qt QML Models} page has more information.
+The QML types for creating lists and models, such as \l ListModel and
+\l ListElement, belong to a submodule, \l{Qt QML Models QML Types}{QtQml.Models}.
+The \l WorkerScript QML type belongs to the submodule
+\l{Qt QML WorkerScript QML Types}{QtQml.WorkerScript}.
-The documentation for the types below applies equally to the types of the same
-name provided by the \l{Qt Quick} module, as they are in fact identical.
+Both, \l{Qt QML Models QML Types}{QtQml.Models} and
+\l{Qt QML WorkerScript QML Types}{QtQml.WorkerScript} are automatically imported
+whenever you import \c QtQml. All their types are then available, too.
+
+The \l{Qt Quick} module automatically imports \c QtQml and, transitively,
+\l{Qt QML Models QML Types}{QtQml.Models} and
+\l{Qt QML WorkerScript QML Types}{QtQml.WorkerScript}, making all their types
+available whenever you import \c QtQuick.
\section1 Value Types
@@ -51,6 +56,21 @@ provided:
\annotatedlist qtqmlvaluetypes
+\section1 Sequence Types
+
+The following \l{QML Sequence Types}{QML sequence types} are provided by the Qt
+QML module in addition to the ones registered with each value type and object
+type:
+
+\list
+ \li \c {std::vector<QString>}
+ \li \c {std::vector<QUrl>}
+ \li \c {std::vector<bool>}
+ \li \c {std::vector<int>}
+ \li \c {std::vector<float>}
+ \li \c {std::vector<double>}
+\endlist
+
\section1 Object Types
The following \l{qtqml-typesystem-objecttypes.html}{QML object types} are
@@ -102,7 +122,6 @@ to a \l{QtQml::Date}{Date} object.
/*!
\qmlvaluetype point
\ingroup qtqmlvaluetypes
-\ingroup qtquickvaluetypes
\brief a value with x and y attributes.
The \c point type refers to a value with \c x and \c y attributes.
@@ -132,7 +151,6 @@ Properties of type \c point are \c {Qt.point(0, 0)} by default.
/*!
\qmlvaluetype size
\ingroup qtqmlvaluetypes
-\ingroup qtquickvaluetypes
\brief a value with width and height attributes.
The \c size type refers to a value with has \c width and \c height attributes.
@@ -172,7 +190,6 @@ is automatically converted into a QSizeF value.
/*!
\qmlvaluetype rect
\ingroup qtqmlvaluetypes
-\ingroup qtquickvaluetypes
\brief a value with x, y, width and height attributes.
The \c rect type refers to a value with \c x, \c y, \c width and \c height attributes.
diff --git a/src/qml/doc/src/qt6-changes.qdoc b/src/qml/doc/src/qt6-changes.qdoc
index 1dfee995f9..4f95103a4f 100644
--- a/src/qml/doc/src/qt6-changes.qdoc
+++ b/src/qml/doc/src/qt6-changes.qdoc
@@ -68,7 +68,7 @@
Implicit conversions were done for strings that could be parsed as
\list
- \li color (use Qt.color instead instead),
+ \li color (use Qt.color instead),
\li date (use the Date object instead),
\li rect (use Qt.rect instead) and
\li size (use Qt.size instead)
diff --git a/src/qml/doc/src/qtqml-cpp.qdoc b/src/qml/doc/src/qtqml-cpp.qdoc
index 9e140c5674..e337364489 100644
--- a/src/qml/doc/src/qtqml-cpp.qdoc
+++ b/src/qml/doc/src/qtqml-cpp.qdoc
@@ -9,5 +9,5 @@
\brief The C++ API provided by the Qt QML module.
For more information on the Qt QML module, see the
-\l{Qt QML} module documentation.
+\l{Qt Qml} module documentation.
*/
diff --git a/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc b/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc
index 293546255f..3631a85b5f 100644
--- a/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc
+++ b/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc
@@ -7,7 +7,7 @@
\title Qt Quick Compiler
\brief Overview of Qt Quick Compiler components
-Qt Quick Compiler enables you to process QML and JavaScript code at compile
+Qt Quick Compiler lets you process QML and JavaScript code at compile
time, rather than at run time. This allows for:
\list
@@ -17,17 +17,17 @@ time, rather than at run time. This allows for:
The Qt Quick Compiler consist of two components:
\list
- \li \l {QML Type Compiler}
- \li \l {QML Script Compiler}
+ \li \l {QML type compiler}
+ \li \l {QML script compiler}
\endlist
\note \e qmltc, \e qmlsc and \l qmlcachegen are internal build tools. If you
need to care about their invocation, you are either writing a build system, or
you are doing something wrong.
-\subtitle The QML Type Compiler
+\section1 The QML type compiler
-The \l{QML Type Compiler}, \e(qmltc) compiles QML types to C++ classes. These C++
+The \l{QML type compiler}, \e(qmltc) compiles QML types to C++ classes. These C++
classes are then added to your application and can be instantiated from other
C++ code. This way you can avoid much of the overhead of using \l{QQmlComponent}
to create instances of your QML types. In order to benefit from \l qmltc, you
@@ -38,12 +38,16 @@ structure. It will fail if an unsupported language feature is encountered.
It does not have to understand the JavaScript code in bindings and functions,
though.
-\subtitle The QML Script Compiler
+\section1 The QML script compiler
-The \l{QML Script Compiler}, (\e qmlsc and \e qmlcachegen) compiles bindings and
+The \l{QML script compiler}, (\e qmlsc and \e qmlcachegen) compiles bindings and
functions to both, an efficient byte code and C++ functions. This process
automatically happens behind the scenes if you are using \l{qt_add_qml_module}
-to specify your QML modules. At compile time, for each QML or JavaScript
+to specify your QML modules. For more information about available options to
+control different aspects of QML compilation, see
+\l {Caching compiled QML sources}.
+
+At compile time, for each QML or JavaScript
document a corresponding C++ file is created and built into the application. The
C++ file then contains a \e{QML compilation unit}, which consists of:
@@ -63,7 +67,7 @@ involved type analysis can be performed. Therefore, the generated C++ code
is generally more efficient than the result of the JIT compilation.
There are limitations on what JavaScript constructs can be compiled to C++.
-For more information see \l {Limitations when compiling JavaScript}.
+For more information, see \l {Limitations when compiling JavaScript to C++}.
\e{qmlsc} will be used instead of \e{qmlcachegen} if the Qt Quick Compiler
Extensions are installed. It has the following additional features over
@@ -89,7 +93,7 @@ Compilation of bindings and functions to C++ is omitted if cache files are
produced. Neither the CMake nor the qmake build system offered by Qt expose this
functionality.
-\subtitle Summary
+\section1 Summary
The following table summarizes the differences between \l{qmltc},
\l{qmlcachegen} and \l{qmlsc}:
diff --git a/src/qml/doc/src/qtqml-tool-qmlcachegen.qdoc b/src/qml/doc/src/qtqml-tool-qmlcachegen.qdoc
deleted file mode 100644
index 8a30e49717..0000000000
--- a/src/qml/doc/src/qtqml-tool-qmlcachegen.qdoc
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
-\page qtqml-tool-qmlcachegen.html
-\title qmlcachegen
-\brief A tool to compile QML documents ahead of time
-
-\e qmlcachegen is an internal build tool, invoked by the build system
-when using \l qt_add_qml_module in CMake or CONFIG+=qtquickcompiler in
-qmake. Users should not invoke it manually.
-*/
diff --git a/src/qml/doc/src/qtqml-tool-qmlsc.qdoc b/src/qml/doc/src/qtqml-tool-qmlsc.qdoc
deleted file mode 100644
index b1276449fc..0000000000
--- a/src/qml/doc/src/qtqml-tool-qmlsc.qdoc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
-\page qtqml-tool-qmlsc.html
-\title QML Script Compiler
-\brief A tool to compile functions and expressions in QML.
-\keyword qmlsc
-
-The QML Script Compiler, \c qmlsc 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-tool-qmltc.qdoc b/src/qml/doc/src/qtqml-tool-qmltc.qdoc
deleted file mode 100644
index a9e9256926..0000000000
--- a/src/qml/doc/src/qtqml-tool-qmltc.qdoc
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
-\page qtqml-tool-qmltc.html
-\title QML Type Compiler
-\brief A tool to compile QML types to C++ ahead of time.
-\keyword qmltc
-
-The QML Type Compiler, \c qmltc, is a tool shipped with Qt to translate QML
-types into C++ types that are \e{ahead-of-time} compiled as part of the user
-code. Using qmltc can lead to better run-time performance due to more
-optimization opportunities available to the compiler compared to a
-QQmlComponent-based object creation. The qmltc is part of the Qt Quick Compiler
-toolchain.
-
-By design, qmltc outputs user-facing code. That code is supposed to be utilized
-by the C++ application directly, otherwise you won't see any benefit. This
-generated code essentially replaces QQmlComponent and its APIs to create objects
-from QML documents. You can find more information under \l{Using qmltc in a QML
-application} and \l{Generated Output Basics}.
-
-In order to enable qmltc:
-
-\list
- \li Create a \l{qt_add_qml_module}{proper QML module} for your application.
-
- \li Invoke qmltc, for example, through the \l{qmltc-cmake}{CMake API}.
-
- \li \c{#include} the generated header file(s) in the application source
- code.
-
- \li Instantiate an object of the generated type.
-\endlist
-
-In this workflow qmltc usually runs during the build process. Thus, when qmltc
-rejects a QML document (whether due to errors or warnings, or because of
-constructs qmltc doesn't yet support), the build process will fail. This is
-similar to how you receive qmllint errors when you enable the automatic
-generation of linting targets during \l{qt_add_qml_module}{QML module creation}
-and then attempt to "build" them to run the qmllint.
-
-\warning qmltc is currently in a Tech Preview stage and might not compile an
-arbitrary QML program (see \l{Known Limitations} for more details). When qmltc
-fails, nothing is generated as your application cannot sensibly use the qmltc
-output. If your program contains errors (or unsolvable warnings), they should be
-fixed to enable the compilation. The general rule is to adhere to the best
-practices and follow \l{qmllint} advice.
-
-\section2 Using qmltc in a QML application
-
-From the build system perspective, adding qmltc compilation is not much
-different from adding qml cache generation. Naively, the build process could be
-described as:
-
-\image qmltc-compilation-scheme.png
-
-While the real compilation process is much trickier, this diagram captures the
-core components that qmltc uses: the QML files themselves and qmldir with
-qmltypes information. Simpler applications typically have rather primitive
-qmldir yet, in general, qmldir could be complex, providing essential, nicely
-packed type information that qmltc relies on to perform correct QML-to-C++
-translation.
-
-Nevertheless, adding an extra build step is not enough in qmltc case. The
-application code must also be modified to use qmltc-generated classes instead of
-QQmlComponent or its higher-level alternatives.
-
-\section3 Compiling QML code with qmltc
-
-Qt, starting from Qt 6, uses CMake to build its various components. User
-projects can - and are encouraged to - also use CMake to build their components
-using Qt. Adding out-of-the-box qmltc compilation support to your project would
-require a CMake-driven build flow as well since this flow is centered around
-proper QML modules and their infrastructure.
-
-The easy way to add qmltc compilation is by using the dedicated
-\l{qmltc-cmake}{CMake API} as part of a QML module creation for the application.
-Consider a simple application directory structure:
-
-\badcode
-.
-├── CMakeLists.txt
-├── myspecialtype.h // C++ type exposed to QML
-├── myspecialtype.cpp
-├── myApp.qml // main QML page
-├── MyButton.qml // custom UI button
-├── MySlider.qml // custom UI slider
-└── main.cpp // main C++ application file
-\endcode
-
-Then the CMake code would usually look similar to the following:
-
-\snippet qmltc/CMakeLists.txt qmltc-app-name
-\codeline
-\snippet qmltc/CMakeLists.txt qmltc-qml-files
-\codeline
-\snippet qmltc/CMakeLists.txt qmltc-add-qml-module
-\codeline
-\snippet qmltc/CMakeLists.txt qmltc-compile-to-cpp
-
-\section3 Using the Generated C++
-
-Unlike in the case of QQmlComponent instantiation, the output of qmltc, being
-C++ code, is used directly by the application. Generally, constructing a new
-object in C++ is equivalent to creating a new object through
-QQmlComponent::create(). Once created, the object could be manipulated from C++
-or, for example, combined with QQuickWindow to be drawn on screen. Given a
-\c{myApp.qml} file, the application code (in both cases) would typically look
-like this:
-
-\if defined(onlinedocs)
- \tab {generated-c++}{tab-qqmlcomponent}{Using QQmlComponent}{checked}
- \tab {generated-c++}{tab-qmltc}{Using qmltc-generated class}{}
- \tabcontent {tab-qqmlcomponent}
-\else
- \section4 Using QQmlComponent
-\endif
-\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-include
-\codeline
-\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-0
-\codeline
-\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-1
-\codeline
-\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-2
-\codeline
-\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
-\if defined(onlinedocs)
- \endtabcontent
- \tabcontent {tab-qmltc}
-\else
- \section4 Using qmltc-generated class
-\endif
-\snippet qmltc/tst_qmltc_examples.cpp qmltc-include
-\codeline
-\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-code
-\codeline
-\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
-\if defined(onlinedocs)
- \endtabcontent
-\endif
-
-\section2 QML engine
-
-The generated code uses QQmlEngine to interact with dynamic parts of a QML
-document - mainly the JavaScript code. For this to work, no special arrangements
-are needed. Any QQmlEngine instance passed to the constructor of a
-qmltc-generated class object should work correctly as does
-\c{QQmlComponent(engine)}. This also means that you can use
-\l{QQmlEngine}{QQmlEngine methods} that affect QML behavior. However, there are
-caveats. Unlike QQmlComponent-based object creation, qmltc itself \e{does not}
-rely on QQmlEngine when compiling the code to C++. For instance,
-\c{QQmlEngine::addImportPath("/foo/bar/")} - normally resulting in an additional
-import path to scan for - would be completely ignored by the ahead-of-time qmltc
-procedure.
-
-\note To add import paths to the qmltc compilation, consider using a relevant
-argument of the \l{qmltc-cmake}{CMake command} instead.
-
-Generally, you can think of it this way: QQmlEngine involves the application
-process to run, while qmltc does not as it operates \e{before} your application
-is even compiled. Since qmltc makes no attempt to introspect your application's
-C++ source code, there is no way for it to know about certain kinds of QML
-manipulations you, as a user, do. Instead of using QQmlEngine and related
-run-time routines to expose types to QML, adding import paths, etc. you are,
-practically, required to create \l{qt_add_qml_module}{well-behaving QML modules}
-and use \l{Defining QML Types from C++}{declarative QML type registration}.
-
-\warning Despite qmltc working closely with QQmlEngine and creating C++ code,
-the generated classes cannot be further exposed to QML and used through
-QQmlComponent.
-
-\section2 Generated Output Basics
-
-\c qmltc aims to be compatible with the existing QML execution model. This
-implies that the generated code is roughly equivalent to the internal
-QQmlComponent setup logic and thus you should be able to understand your QML
-type's behavior, semantics and API the same way you do currently - by visually
-inspecting the corresponding QML document.
-
-However, the generated code is still somewhat confusing, especially given that
-your application should use the qmltc output on the C++ side directly. There are
-two parts of the generated code: CMake build files structure and the generated
-C++ format. The former is covered in the \l{qmltc-cmake}{CMake API of qmltc} and
-the latter is covered here.
-
-Consider a simple HelloWorld type, that has a \c hello property, a function to
-print that property, and a signal emitted when the object of that type is
-created:
-
-\snippet qmltc/special/HelloWorld.qml qmltc-hello-world-qml
-
-When providing a C++ alternative of this QML type, the C++ class would need a
-\l{Overview - QML and C++ Integration}{QML-specific meta-object system macro},
-Q_PROPERTY decoration for the \c hello property, \c{Q_INVOKABLE} C++ printing
-function and a regular Qt signal definition. Similarly, qmltc would translate
-the given HelloWorld type into roughly the following:
-
-\snippet qmltc/special/HelloWorld.qml.cpp qmltc-hello-world-generated
-
-Even though specific details of the generated type could differ, the universal
-aspects remain. For instance:
-
-\list
- \li QML types within a document are translated into C++ types, according to
- the compiler-visible information.
- \li Properties are translated into C++ properties with Q_PROPERTY
- declarations.
- \li JavaScript functions become \c{Q_INVOKABLE} C++ functions.
- \li QML signals are transformed into C++ Qt signals.
- \li QML enumerations are converted into C++ enumerations with \c{Q_ENUM}
- declarations.
-\endlist
-
-\note \c qmltc does not guarantee that the generated C++ stays API-, source- or
-binary-compatible between releases.
-
-An additional detail is the way \c qmltc generates class names. A class name for
-a given QML type is automatically deduced from the QML document defining that
-type: the QML file name without extensions (up to and excluding the first \c{.},
-also known as the base name) becomes a class name. The file name case is
-preserved. Thus, \c{HelloWorld.qml} would result in a \c{class HelloWorld} and
-\c{helloWoRlD.qml} in a \c{class helloWoRlD}. Following the QML convention, if a
-QML document file name starts with a lower-case letter, the generated C++ class
-is assumed to be anonymous and marked with \l{QML_ANONYMOUS}.
-
-For now, although the generated code is ready to be used from the C++
-application side, you should generally limit calls to the generated APIs.
-Instead, prefer implementing the application logic in QML/JavaScript and
-hand-written C++ types exposed to QML, using the qmltc-created classes for
-simple object instantiation. While generated C++ gives you direct (and usually
-faster) access to QML-defined elements of the type, understanding such code
-could be a challenge.
-
-\section2 Known Limitations
-
-Despite covering many common QML features, qmltc is still in the early stage of
-development with many things yet to be supported. To name a few:
-
-\list
- \li Translation APIs (qsTr() and similar methods)
-
- \li Inline components
-
- \li Imported QML modules that consist of QML-defined types (such as
- \c{QtQuick.Controls}). At present, you can reliably use \c{QtQml} and
- \c{QtQuick} modules as well as any other QML module that \b{only} contains
- C++ classes exposed to QML
-
- \li Singleton QML types
-\endlist
-
-On top of this, there are some more fundamental peculiarities to consider:
-
-\list
- \li Qt's QML modules usually rely on C++ libraries to do the heavy lifting.
- Often enough, these libraries do not provide public C++ API (since their
- primary usage is through QML). For the users of qmltc, this means that their
- apps need to link against private Qt libraries.
-
- \li Due to the nature of qmltc code generation, QML plugins are unusable for
- compilation purposes. Instead, QML modules - that use a plugin - have to
- ensure that the plugin data is accessible at compile time. Such QML modules
- would then have \e optional plugins. In most cases, the compile-time
- information can be provided through a header file (with C++ declarations)
- and linkable library (with C++ definitions). The user code is responsible
- (usually through CMake) for including a path to the header file and linking
- against the QML module library.
-\endlist
-
-Given the tech preview status of the compiler, you might also encounter bugs in
-qmltc, in the generated code, or some other related part. We encourage you to
-\l{https://bugreports.qt.io/}{submit a bug report} in this case.
-
-*/
diff --git a/src/qml/doc/src/qtqml-tooling.qdoc b/src/qml/doc/src/qtqml-tooling.qdoc
new file mode 100644
index 0000000000..45e4528a6b
--- /dev/null
+++ b/src/qml/doc/src/qtqml-tooling.qdoc
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+\group qtqml-tooling
+\title Qt Qml Tooling
+\brief List of Qt Qml Tools and Utilities
+
+The Qt Qml module provides a range of tools and utilities that enhance
+the developer and designer experience and includes some internal tools
+used by Qt. The following sections offer a concise overview of these tools
+and utilities, along with links to additional information about each of them.
+
+\section1 Developer Tools
+
+A set of tools provided by the Qt Qml module that aim to facilitate a QML developer's life
+during various phases of development. These tools are:
+
+\list
+ \li \l {qmllint Reference}{qmllint}
+ \li \l {qmlformat}
+ \li \l {\QMLLS Reference}{qmlls}
+ \li \l {qmlprofiler}
+\endlist
+
+\section1 Designer Tools
+
+The Qt Qml module also provides user facing tools which can increase the productivity
+of designers by helping them with fast prototyping. These tools are:
+
+\list
+ \li \l{qml}
+ \li \l{qmlpreview}
+\endlist
+
+\section1 Internal Tools
+
+Certain utility tools are not intended for direct user interaction.
+Instead, they are all meant to be invoked by the build system,
+augmenting its functionality and capabilities.
+
+\list
+ \li \l {QML type compiler}
+ \li \l {QML script compiler}
+ \li \l {qmltyperegistrar}
+ \li \l {qmlimportscanner}
+\endlist
+
+*/
diff --git a/src/qml/doc/src/qtqml-writing-a-module.qdoc b/src/qml/doc/src/qtqml-writing-a-module.qdoc
index 68566f984a..c8a99f1a37 100644
--- a/src/qml/doc/src/qtqml-writing-a-module.qdoc
+++ b/src/qml/doc/src/qtqml-writing-a-module.qdoc
@@ -92,7 +92,7 @@ You can use the \l Q_IMPORT_QML_PLUGIN macro to create a reference to this symbo
Add the following code to the main.cpp:
\badcode
-#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/QQmlExtensionPlugin>
Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
\endcode
@@ -104,10 +104,201 @@ CMakeLists.txt, link the plugin, not the backing library, into the main program:
\skipto target_link_libraries
\printuntil )
-\section1 Exporting Multiple Major Versions from The Same Module
+\section1 Versions
+
+QML has a complex system to assign versions to components and modules. In most
+cases you should ignore all of it by:
+
+\list 1
+ \li Never adding a version to your import statements
+ \li Never specifying any versions in \l{qt_add_qml_module}
+ \li Never using \l{QML_ADDED_IN_VERSION} or \l{QT_QML_SOURCE_VERSIONS}
+ \li Never using \l{Q_REVISION} or the \c{REVISION()} attribute in \l{Q_PROPERTY}
+ \li Avoiding unqualified access
+ \li Generously using import namespaces
+\endlist
+
+Versioning is ideally handled outside the language itself. You may, for example,
+keep separate \l{QML Import Path}{import paths} for different sets of QML modules.
+Or you may use a versioning mechanism provided by your operating system to install
+or uninstall packages with QML modules.
+
+In some cases, Qt's own QML modules may show different behavior, depending on what
+version is imported. In particular, if a property is added to a QML component, and
+your code contains unqualified access to another property of the same name, your
+code will break. In the following example, the code will behave differently
+depending on the version of Qt, because the \l [QML] {Rectangle::}{topLeftRadius}
+property was added in Qt 6.7:
+
+\qml
+import QtQuick
+
+Item {
+ // property you want to use
+ property real topLeftRadius: 24
+
+ Rectangle {
+
+ // correct for Qt version < 6.7 but uses Rectangle's topLeftRadius in 6.7
+ objectName: "top left radius:" + topLeftRadius
+ }
+}
+\endqml
+
+The solution here is to avoid the unqualified access. \l{qmllint Reference}{qmllint} can be used to
+find such problems. The following example accesses the property you actually mean
+in a safe, qualified way:
+
+\qml
+import QtQuick
+
+Item {
+ id: root
+
+ // property you want to use
+ property real topLeftRadius: 24
+
+ Rectangle {
+
+ // never mixes up topLeftRadius with unrelated Rectangle's topLeftRadius
+ objectName: "top left radius:" + root.topLeftRadius
+ }
+}
+\endqml
+
+You can also avoid the incompatibility by importing a specific version of QtQuick:
+
+\qml
+// make sure Rectangle has no topLeftRadius property
+import QtQuick 6.6
+
+Item {
+ property real topLeftRadius: 24
+ Rectangle {
+ objectName: "top left radius:" + topLeftRadius
+ }
+}
+\endqml
+
+Another problem solved by versioning is the fact that QML components imported by
+different modules may shadow each other. In the following example, if \c{MyModule} were
+to introduce a component named \c{Rectangle} in a newer version, the \c{Rectangle}
+created by this document would not be a \c {QQuickRectangle} anymore, but rather the
+new \c{Rectangle} introduced by \c{MyModule}.
+
+\qml
+import QtQuick
+import MyModule
+
+Rectangle {
+ // MyModule's Rectangle, not QtQuick's
+}
+\endqml
+
+A good way to avoid the shadowing would be to import \c{QtQuick} and/or \c{MyModule}
+into type namespaces as follows:
+
+\qml
+import QtQuick as QQ
+import MyModule as MM
+
+QQ.Rectangle {
+ // QtQuick's Rectangle
+}
+\endqml
+
+Alternatively, if you import \c{MyModule} with a fixed version, and the new component
+receives a correct version tag via \l{QML_ADDED_IN_VERSION} or \l{QT_QML_SOURCE_VERSIONS},
+the shadowing is also avoided:
+
+\qml
+import QtQuick 6.6
+
+// Types introduced after 1.0 are not available, like Rectangle for example
+import MyModule 1.0
+
+Rectangle {
+ // QtQuick's Rectangle
+}
+\endqml
+
+For this to work, you need to use versions in \c{MyModule}. There are a few things
+to be aware of.
+
+\section2 If you add versions, add them everywhere
+
+You need to add a \c{VERSION} attribute to \l{qt_add_qml_module}. The version should
+be the most recent version provided by your module. Older minor versions of the same
+major version will automatically be registered. For older major versions, see
+\l{#Exporting Multiple Major Versions from The Same Module}{below}.
+
+You should add \l{QML_ADDED_IN_VERSION} or \l{QT_QML_SOURCE_VERSIONS} to every type
+that was \e not introduced in version \c{x.0} of your module, where \c{x} is the current
+major version.
+
+If you forget to add a version tag, the component will be available in all versions,
+making the versioning ineffective.
+
+However, there is no way to add versions to properties, methods, and signals defined
+in QML. The only way to version QML documents is to add a new document with separate
+\l{QT_QML_SOURCE_VERSIONS} for each change.
+
+\section2 Versions are not transitive
+
+If a component from your module \c{A} imports another module \c{B} and instantiates a type
+from that module as the root element, then the import version of \c{B} is relevant for the
+properties available from the resulting component, no matter what version of \c{A} is
+imported by a user.
+
+Consider a file \c{TypeFromA.qml} with version \c{2.6} in module \c{A}:
+
+\qml
+import B 2.7
+
+// Exposes TypeFromB 2.7, no matter what version of A is imported
+TypeFromB { }
+\endqml
+
+Now consider a user of \c{TypeFromA}:
+
+\qml
+import A 2.6
+
+// This is TypeFromB 2.7.
+TypeFromA { }
+\endqml
+
+The user hopes to see version \c{2.6} but actually gets version
+\c{2.7} of the base class \c{TypeFromB}.
+
+Therefore, in order to be safe, you not only have to duplicate your QML files and
+give them new versions when you add properties yourself, but also when you bump
+versions of modules you import.
+
+\section2 Qualified access does not honor versioning
+
+Versioning only affects unqualified access to members of a type or the type itself.
+In the example with \c{topLeftRadius}, if you write \c{this.topLeftRadius}, the
+property will be resolved if you're using Qt 6.7, even if you \c{import QtQuick 6.6}.
+
+\section2 Versions and revisions
+
+With \l{QML_ADDED_IN_VERSION}, and the two-argument variants of \l{Q_REVISION} and
+\l{Q_PROPERTY}'s \c{REVISION()}, you can only declare versions that are tightly coupled
+to the \l{QMetaObject}{metaobject's} revision as exposed in \l{QMetaMethod::revision}
+and \l{QMetaProperty::revision}. This means all the types in your type hierarchy have
+to follow the same versioning scheme. This includes any types provided by Qt itself
+that you inherit from.
+
+With \l{QQmlEngine::}{qmlRegisterType} and related functions you can register any mapping
+between metaobject revisions and type versions. You then need to use the one-argument forms
+of \l{Q_REVISION} and the \c{REVISION} attribute of \l{Q_PROPERTY}. However, this
+can become rather complex and confusing and is not recommended.
+
+\section2 Exporting multiple major versions from the same module
\l qt_add_qml_module by default considers the major version given in its
-URI argument, even if the individual types declare other versions in their
+VERSION argument, even if the individual types declare other versions in their
added specific version via \l QT_QML_SOURCE_VERSIONS or \l Q_REVISION.
If a module is available under more than one version, you also need to decide
what versions the individual QML files are available under. To declare further
@@ -143,7 +334,7 @@ In more complex projects, this convention can be too limiting. You might for
instance want to group all QML modules in one place to avoid polluting the
project's root directory. Or you want to reuse a single module in multiple
applications. For those cases, \c QT_QML_OUTPUT_DIRECTORY in combination with
-\c RESOURCE_PREFIX or \c AUTO_RESOURCE_PREFIX and \l IMPORT_PATH can be used.
+\c RESOURCE_PREFIX and \l IMPORT_PATH can be used.
To collect QML modules into a specific output directory, for example a
subdirectory "qml" in the build directory \l QT_QML_OUTPUT_DIRECTORY, set the
@@ -206,11 +397,11 @@ qt_add_qml_module(
If all QML modules are always loaded from the resource
file system, you can deploy the application as a single binary.
-Generally, you should pass the \c AUTO_RESOURCE_PREFIX without arguments to
-\l{qt_add_qml_module}. This way, your modules are placed in
-\c{:/qt/qml/} in the resource file system. Since Qt 6.5,
-this is part of the default \l{QML Import Path}, but not used by Qt itself. For
-modules to be used within your application, this is the right place.
+If \l{QTP0001} policy is set to \c NEW, the \c RESOURCE_PREFIX argument
+for \c{qt_add_qml_module()} defaults to \c{/qt/qml/}, therefore your
+modules are placed in \c{:/qt/qml/} in the resource file system.
+This is part of the default \l{QML Import Path}, but not used by Qt
+itself. For modules to be used within your application, this is the right place.
If you have instead specified a custom \c RESOURCE_PREFIX, you have to add the
custom resource prefix to the \l{QML Import Path}. You can also add multiple
diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc
index 2da6a8c9e0..51a638bf4d 100644
--- a/src/qml/doc/src/qtqml.qdoc
+++ b/src/qml/doc/src/qtqml.qdoc
@@ -1,25 +1,19 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtqml-index.html
-\title Qt QML
-\brief The Qt QML module defines and implements the QML language
+\title Qt Qml
+\brief The Qt Qml module implements the QML language and offers APIs to register
+ types for it.
-The Qt QML module provides a framework for developing applications and libraries
-with the \l{QML Applications}{QML language}. It defines and implements the
+The Qt Qml module provides a framework for developing applications and libraries
+with the \l{The QML Reference}{QML language}. It defines and implements the
language and engine infrastructure, and provides an API to enable application
-developers to extend the QML language with custom types and integrate QML code
-with JavaScript and C++. The Qt QML module provides both a \l{Qt QML QML Types}
-{QML API} and a \l{Qt QML C++ Classes}{C++ API}.
-
-Note that while the Qt QML module provides the language and infrastructure
-for QML applications, the \l{Qt Quick} module provides many visual components,
-model-view support, an animation framework, and much more for building user
-interfaces.
-
-If you're new to QML and Qt Quick, see \l{QML Applications} for an
-introduction to writing QML applications.
+developers to \l{Registering QML Types and QML Modules}{register} custom QML types
+and modules and integrate QML code with JavaScript and C++. The Qt Qml module
+provides both a \l{Qt QML QML Types}{QML API} and a
+\l{Qt Qml C++ Classes}{C++ API}.
\section1 Using the Module
@@ -27,10 +21,23 @@ introduction to writing QML applications.
\include {module-use.qdocinc} {using the qml api} {QtQml}
+The Qt QML module contains the QML framework and important QML types used in
+applications. The constructs of QML are described in the \l{The QML Reference}.
+
+The \l{Qt QML QML Types}{QML API} of the Qt QML module provides a number of
+\l{qtqml-typesystem-objecttypes.html}{QML Object Types},
+\l{qtqml-typesystem-valuetypes.html}{QML Value Types} and namespaces.
+
\section2 C++ API
\include {module-use.qdocinc} {using the c++ api}
+The C++ API contains some
+\l{Important C++ Classes Provided By The Qt QML Module}{important classes}
+you should get familiar with. It also provides
+\l{Integrating with JavaScript values from C++}{types} to hold JavaScript
+values.
+
\section3 Building with CMake
\include {module-use.qdocinc} {building with cmake} {Qml}
@@ -46,88 +53,40 @@ See \l{qt6_generate_foreign_qml_types} for details.
\include {module-use.qdocinc} {building_with_qmake} {qml}
-\section1 QML and QML Types
-
-The Qt QML module contains the QML framework and important QML types used in
-applications. The constructs of QML are described in the \l{The QML Reference}.
-
-In addition to the \l{QML Value Types}, the module comes with
-the following QML object types:
-\list
- \li \l Component
- \li \l QtObject
- \li \l Binding
- \li \l Connections
- \li \l Timer
-\endlist
-
-The \l{QtQml::Qt}{Qt} global object provides useful enums and functions
-for various QML types.
-
-\section2 Lists and Models
-
-New in Qt 5.1, the model types are moved to a submodule, \c QtQml.Models. The
-\l{Qt QML Models QML Types}{Qt QML Models} page has more information.
-
-\list
- \li \l DelegateModel
- \li \l DelegateModelGroup
- \li \l ListElement
- \li \l ListModel
- \li \l ObjectModel
-\endlist
-
-\section1 JavaScript Environment for QML Applications
+\section1 Registering QML Types and QML Modules
-JavaScript expressions allow QML code to contain application logic. Qt QML
-provides the framework for running JavaScript expressions in QML and from C++.
+In order to register types for usage with QML you first need to define a
+\l{Writing QML Modules}{QML module}, preferably using \l{qt_add_qml_module} in CMake.
+Then, you can add C++ headers to your new module, and
+\l{Defining QML Types from C++}{define types} to be
+\l{Exposing Attributes of C++ Types to QML}{exposed to QML} in them.
-These sections are from \l{The QML Reference}.
-\list
- \li \l {qtqml-javascript-topic.html} {Integrating QML and JavaScript}
- \li \l {qtqml-javascript-expressions.html}
- {Using JavaScript Expressions with QML}
- \li \l {qtqml-javascript-dynamicobjectcreation.html}
- {Dynamic QML Object Creation from JavaScript}
- \li \l {qtqml-javascript-resources.html}
- {Defining JavaScript Resources In QML}
- \li \l {qtqml-javascript-imports.html}
- {Importing JavaScript Resources In QML}
- \li \l {qtqml-javascript-hostenvironment.html}
- {JavaScript Host Environment}
-\endlist
+\section1 Tweaking the engine
-\section1 Integrating QML with C++ Applications
+There are a number of knobs you can turn to customize the behavior of the QML engine.
+The page on \l{Configuring the JavaScript Engine}{configuring the JavaScript engine}
+lists the environment variables you may use to this effect. The description of the
+\l{The QML Disk Cache}{QML Disk Cache} describes the options related to how your QML
+components are compiled and loaded.
-The module also provides the framework for running QML applications.
-The QML framework allows QML code to contain JavaScript expressions and for
-the QML code to interact with C++ code.
+\section1 Articles and Guides
-\section2 Articles and Guides
+These articles contain information about Qt Qml.
\list
- \li \l {Overview - QML and C++ Integration}
- \li \l {Data Type Conversion Between QML and C++}
- \li \l {Integrating with JavaScript values from C++}
- \li \l {Exposing Attributes of C++ Types to QML}
- \li \l {Defining QML Types from C++}
- \li \l {Writing QML Modules}
- \li \l {Important C++ Classes Provided By The Qt QML Module}
+ \li \l{The QML Reference}
+ \li \l{Qt Qml Tooling}
+ \li \l{Writing QML Modules}
+ \li \l{Singletons in QML}
\endlist
-\section2 Examples
+\section1 Reference
\list
- \li \l {Writing QML Extensions with C++}
+ \li \l {Qt QML C++ Classes} {C++ Classes}
+ \li \l {Qt QML QML Types} {QML Types}
\endlist
-\omit
- \section1 Additional Frameworks
- \list
- \li \l{The Declarative State Machine Framework}
- \endlist
-\endomit
-
\section1 Licenses and Attributions
Qt QML is available under commercial licenses from \l{The Qt Company}.
@@ -142,29 +101,4 @@ modules under following permissive licenses:
\generatelist{groupsbymodule attributions-qtqml}
-\section1 Related Articles and Guides
-
-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
- \li \l {Qt Quick}
- - a module which provides a set of QML types and C++ classes for
- building user interfaces and applications with QML
- \li \l {The QML Disk Cache}
- - how to fine tune if and where the QML engine caches compilation
- results
-\endlist
-
-\section1 Reference
-
-\list
- \li \l {Qt QML C++ Classes} {C++ Classes}
- \li \l {Qt QML QML Types} {QML Types}
- \li \l {Qt QML Examples} {Examples}
-\endlist
*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qml.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qml.qdoc
new file mode 100644
index 0000000000..6a0a335839
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-qml.qdoc
@@ -0,0 +1,107 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-qml.html
+\title qml
+\brief Overview of the qml command line utility.
+\ingroup qtqml-tooling
+
+\section1 The qml utility
+\c The qml utility tool loads QML documents and creates a window to show the scene
+if your QML document includes a visual item. You can also evaluate non-visual QML
+documents with it.
+It is mainly meant for testing your QML applications or components quickly
+as described in \l {Prototyping with the QML Runtime Tool}{here}.
+
+\table
+\header
+ \li Usage:
+\row
+ \li qml [\l{options}] files... [-- args...]
+\endtable
+
+\section2 options
+
+\table
+\header
+ \li Option
+ \li Description
+\row
+ \li -h, --help
+ \li Displays help on commandline options.
+\row
+ \li --help-all
+ \li Displays help, including generic Qt options.
+\row
+ \li -v, --version
+ \li Displays version information.
+\row
+ \li -a, --apptype <core|gui|widget>
+ \li Select which application class to use. Default is gui.
+\row
+ \li -I <path>
+ \li Prepend the given path to the import paths.
+\row
+ \li -f <file>
+ \li Load the given file as a QML file.
+\row
+ \li -c, --config <file>
+ \li Load the given built-in configuration or configuration file.
+\row
+ \li --list-conf
+ \li List the built-in configurations.
+\row
+ \li --translation <file>
+ \li Load the given file as the translations file.
+\row
+ \li --dummy-data <file>
+ \li Load QML files from the given directory as context properties. (deprecated)
+\row
+ \li --desktop
+ \li Force use of desktop OpenGL (AA_UseDesktopOpenGL).
+\row
+ \li --gles
+ \li Force use of GLES (AA_UseOpenGLES).
+\row
+ \li --software
+ \li Force use of software rendering (AA_UseSoftwareOpenGL).
+\row
+ \li --core-profile
+ \li Force use of OpenGL Core Profile.
+\row
+ \li --disable-context-sharing
+ \li Disable the use of a shared GL context for QtQuick Windows
+\row
+ \li --enable-shader-cache
+ \li Enable persistent caching of generated shaders
+\row
+ \li --transparent
+ \li Requests an alpha channel in order to enable semi-transparent windows.
+\row
+ \li --multisample
+ \li Requests 4x multisample antialiasing.
+\row
+ \li --quiet
+ \li Suppress all output.
+\row
+ \li --verbose
+ \li Print information about what qml is doing, like specific file URLs being loaded.
+\row
+ \li --slow-animations
+ \li Run all animations in slow motion.
+\row
+ \li --fixed-animations
+ \li Run animations off animation tick rather than wall time.
+\row
+ \li -r, --rhi <backend>
+ \li Set the backend for the Qt graphics abstraction (RHI). Backend is one of:
+ default, vulkan, metal, d3d11, gl
+\row
+ \li -S <selector>
+ \li Add selector to the list of QQmlFileSelectors.
+
+\endtable
+
+
+*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc
new file mode 100644
index 0000000000..b516104f66
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-qmlformat.qdoc
@@ -0,0 +1,146 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-qmlformat.html
+\title qmlformat
+\brief Overview of the qmlformat tool.
+\ingroup qtqml-tooling
+
+\section1 qmlformat
+\e qmlformat is a tool that automatically formats QML files in accordance
+with the \l{QML Coding Conventions}. \l{Details}{More...}
+
+\table
+\header
+ \li Usage:
+\row
+ \li qmlformat [\l{options}] \l{arguments}
+\endtable
+
+\section2 Options
+\target options
+The following options are available:
+
+\table
+\header
+ \li Option
+ \li Default Value
+ \li Description
+\row
+ \li -h, --help
+ \li
+ \li Displays help on commandline options.
+\row
+ \li --help-all
+ \li
+ \li Displays help, including generic Qt options.
+
+\row
+ \li -v, --version
+ \li
+ \li Displays version information.
+\row
+ \li -V, --verbose
+ \li
+ \li Verbose mode. Outputs more detailed information.
+\row
+ \li --write-defaults
+ \li
+ \li Writes defaults settings to .qmlformat.ini and exits
+ (Warning: This will overwrite any existing settings and comments!)
+\row
+ \li --ignore-settings
+ \li
+ \li Ignores all settings files and only takes command line options into consideration
+\row
+ \li -i, --inplace
+ \li
+ \li Edit file in-place instead of outputting to stdout.
+\row
+ \li -f, --force
+ \li
+ \li Continue even if an error has occurred.
+\row
+ \li -t, --tabs
+ \li
+ \li Use tabs instead of spaces.
+\row
+ \li -w, --indent-width <width>
+ \li 4
+ \li How many spaces are used when indenting.
+\row
+ \li -n, --normalize
+ \li
+ \li Reorders the attributes of the objects according to the QML Coding Guidelines.
+\row
+ \li -F, --files <file>
+ \li
+ \li Format all files listed in file, in-place
+\row
+ \li -l, --newline <newline>
+ \li
+ \li Override the new line format to use (native macos unix windows).
+\row
+ \li --objects-spacing
+ \li
+ \li Ensure spaces between objects (only works with normalize option).
+\row
+ \li --functions-spacing
+ \li
+ \li Ensure spaces between functions (only works with normalize option).
+
+\endtable
+
+\section2 Arguments
+\target arguments
+\table
+\header
+ \li Arguments:
+\row
+ \li filenames
+\endtable
+
+\section2 Details
+\e qmlformat is flexible and can be configured according to your needs.
+
+\section3 Output
+qmlformat writes the formatted version of the file to stdout.
+If you wish to have your file updated in-place specify the \c{-i} flag.
+
+\section3 Grouping Properties, Functions, and Signals Together
+With \c{-n} or \c{--normalize} flag, \e qmlformat groups all properties, functions,
+and signals together, instead of retaining the order you specified.
+
+\section3 Settings File
+You can configure \e qmlformat by including a settings file \c{.qmlformat.ini} in your
+project source or in the parent directories of your project source folder. A default
+settings file can be obtained by passing the \c{--write-defaults} flag. This generates the
+\c{.qmlformat.ini} file in the current working directory.
+
+\warning \c{--write-defaults} will overwrite any existing settings and comments!
+
+\section3 Formatting a List of Files
+While you can pass a list of files to be formatted as arguments, qmlformat provides
+\c {-F} option to format a set of files stored in a file. In this case, formatting will happen
+inplace.
+
+\code
+ // FileList.txt
+ main.qml
+ mycomponent.qml
+\endcode
+
+Then, use it like
+\code
+ qmlformat -F FileList.txt
+\endcode
+
+\note If the file contains an invalid entry, for example, a file path that
+doesn't exist or a valid file path but the content is an invalid qml document,
+then \c qmlformat will error out for that particular entry. It will still format
+the valid file entries in place.
+
+\warning If you provide -F option, qmlformat will ignore the positional arguments.
+
+*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qmlimportscanner.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qmlimportscanner.qdoc
new file mode 100644
index 0000000000..7a4047cf7d
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-qmlimportscanner.qdoc
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-qmlimportscanner.html
+\title qmlimportscanner
+\brief Overview of the qmlimportscanner utility.
+\ingroup qtqml-tooling
+
+\section1 qmlimportscanner
+\c qmlimportscanner is an internal tool shipped with Qt Qml to scan QML files
+inside the directory for QML import dependencies. It is meant to be invoked by
+the build system only, users shouldn't need to execute it directly.
+
+*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qmllint.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qmllint.qdoc
new file mode 100644
index 0000000000..b26fe1eff0
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-qmllint.qdoc
@@ -0,0 +1,141 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-qmllint.html
+\title qmllint Reference
+\ingroup qtqml-tooling
+\brief A tool for verifying the syntax of QML files and warning about
+anti-patterns.
+
+\e 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.
+
+By default, some issues will result in warnings that will be printed and result
+in a non-zero exit code.
+Minor issues however (such as unused imports) are just informational messages
+by default and will not affect the exit code.
+qmllint is very configurable and allows for disabling warnings or changing how
+they are treated.
+Users may freely turn any issue into a warning, informational message, or
+disable them outright.
+
+qmllint warns about:
+\list
+ \li Unqualified accesses of properties
+ \li Usage of signal handlers without a matching signal
+ \li Usage of with statements in QML
+ \li Issues related to compiling QML code
+ \li Unused imports
+ \li Deprecated components and properties
+ \li And many other things
+\endlist
+
+\note In order for qmllint to work properly, it requires type information.
+That information is provided by QML modules in the import paths.
+The current directory, as well as the import paths for Qt's built-in types,
+are used as import paths by default.
+To add more import paths not included in the default,
+add them via the \c{-I} flag.
+
+To get an overview and explanation of all available command line options, run \c{qmllint --help}.
+
+\section2 Compiler warnings
+
+qmllint can warn you about code that cannot be compiled by \l{qmlsc}.
+
+These warnings are not enabled by default. In order to enable them specify
+\c{--compiler warning} or adjust your settings file accordingly.
+
+\section2 Marking components and properties as deprecated
+
+qmllint allows you to mark both properties and components as deprecated:
+
+\code
+@Deprecated { reason: "Use NewCustomText instead" }
+Text {
+ @Deprecated { reason: "Use newProperty instead" }
+ property int oldProperty
+ property int newProperty
+ Component.onCompleted: console.log(oldProperty); // Warning: XY.qml:8:40: Property "oldProperty" is deprecated (Reason: Use newProperty instead)
+}
+\endcode
+
+Deprecation warnings for components will be shown every time the component is created.
+
+\section2 Disabling warnings inline
+
+You may at any point disable warnings temporarily in a file using \c{// qmllint
+disable}.
+
+You can do this at the end of a line when a single line produces warnings:
+
+\code
+Item {
+ property string foo
+ Item {
+ property string bar: foo // qmllint disable unqualified
+ }
+}
+\endcode
+
+Alternatively you can disable comments for a block of lines by putting the
+comment in a line only containing \c{// qmllint disable}, ending the block with
+\c{// qmllint enable}:
+
+\code
+Item {
+ property string foo
+ Item {
+ // qmllint disable unqualified
+ property string bar: foo
+ property string bar2: foo
+ // qmllint enable unqualified
+ }
+}
+\endcode
+
+qmllint interprets all single line comments starting with \c {qmllint} as
+directives. Thus you may not start a comment that way unless you wish to enable
+or disable warnings.
+
+\note As done in the examples above it is preferable to explicitly specify the
+warning or a list of warnings you want to disable instead of disabling all
+warnings. This can be done by simply listing warning categories after \c{qmllint disable} (the names are
+the same as the options listed in \c{--help}).
+
+\section2 Settings
+
+In addition to passing command-line options, you can also
+configure qmllint via a settings file.
+The command line \c{--write-defaults} will generate one for you.
+
+Setting files are named \c{.qmllint.ini} and look like this:
+
+\quotefile qmllint/config.ini
+
+Warning levels may be set to \c{info}, \c{warning} or \c{disable} just as with
+command line options.
+
+qmllint will automatically look for a settings file at the location of the qml
+file that is being linted.
+It also looks through all parent directories to find this file and
+automatically applies the settings therein. You can disable this behavior by
+using \c{--ignore-settings}.
+You may always override these defaults by specifying command line parameters
+that take precedence over the warning levels in settings.
+
+\section2 Scripting
+
+qmllint can write or output JSON via the \c{--json <file>} option which will return valid JSON
+with warning messages, file and line location of warnings, and their severity
+level. Use the special filename '-' to write to stdout instead of a file.
+This can be used to more easily integrate qmllint in your pre-commit hooks or
+CI testing.
+
+\sa {Type Description Files}
+\sa {Qt Quick Tools and Utilities}
+*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qmlls.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qmlls.qdoc
new file mode 100644
index 0000000000..e9c737c036
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-qmlls.qdoc
@@ -0,0 +1,168 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-qmlls.html
+\title \QMLLS Reference
+\brief Overview of \QMLLS (qmlls).
+\ingroup qtqml-tooling
+
+\QMLLS is a tool shipped with Qt that helps you write code
+in your favorite (LSP-supporting) editor.
+See \l{https://microsoft.github.io/language-server-protocol/}{Language Server Protocol}
+for more information.
+
+Currently, it enables your editor to:
+\list
+ \li Autocomplete your code
+ \li Display qmllint warnings
+ \li Navigate to definitions in QML files
+ \li Find usages of JavaScript variables and QML objects
+ \li Rename JavaScript variables and QML objects
+ \li Format QML files
+\endlist
+
+\note qmlls is currently in development, see \l{Known Limitations} for
+more details.
+
+\section1 Supported Features
+
+\section2 Linting
+
+\QMLLS can automatically lint opened QML files
+and display warnings or errors straight in the editor. See
+\l{qmllint Reference}{qmllint} for more information about the linting process.
+
+\section2 Formatting
+
+\QMLLS can format entire files from inside
+the editor. See \l{qmlformat} for more information about the
+formatting process.
+
+
+\section2 Finding Definitions
+
+\QMLLS can find definitions of JavaScript variables,
+functions, QML object id's and QML properties from their usages.
+
+\QMLLS can also find the definition of types used in
+type annotations for JavaScript functions, QML object properties,
+and QML object instantiation.
+
+\section2 Finding Usages
+
+\QMLLS can find usages of JavaScript variables,
+QML object properties, JavaScript functions, QML object methods,
+and QML object id's.
+
+\section2 Renaming
+
+\QMLLS can rename JavaScript variables and functions,
+as well as QML object properties, methods, and id's, as long as
+they are defined in a QML file.
+
+\section2 Suggesting Autocompletion Items
+
+\QMLLS provides autocompletion suggestions for
+JavaScript variables, expressions, and statements, as well as
+QML object properties, methods, and id's.
+
+\section2 Tracking Changes in C++ Files
+
+\QMLLS can track changes in C++ files defining QML
+types. It automatically rebuilds CMake QML modules to provide
+accurate and up-to-date warnings and completion items for C++
+defined QML types.
+
+You can
+\l{Disabling automatic CMake builds}{disable this feature}.
+
+\section1 Setting up the \QMLLS in Your Editor
+
+\note You can find the \QMLLS binary under
+\c{<Qt installation folder>/bin/qmlls} in installations of Qt
+made with \QOI.
+
+\section2 Setting up the Build Directory
+
+\QMLLS needs to know the location of your build
+folder. You can pass it the following ways:
+\list
+ \li The \c{--build-dir} command line option. In this case
+your editor should invoke \c{qmlls} as following:
+\badcode
+<path/to/qmlls> --build-dir <path/to/build-directory>
+\endcode
+ \li The \c{QMLLS_BUILD_DIRS} environment variable.
+ \li The \c{.qmlls.ini} settings file, see \l {Configuration File}.
+\endlist
+
+\note When the build directory is specified in multiple ways, the
+command line option takes preference over the environment variable
+that takes precedence over the setting file.
+
+\section2 Disabling Automatic CMake Builds
+
+\c{qmlls} will try to trigger a CMake rebuild when it detects that the
+source code of a C++ defined QML type has been modified.
+
+To disable this feature, use the following ways:
+\list
+ \li The \c{--no-cmake-calls} command line option. In this case
+your editor should invoke \c{qmlls} as follows:
+\badcode
+<path/to/qmlls> --build-dir <path/to/build-directory> --no-cmake-calls
+\endcode
+ \li The \c{QMLLS_NO_CMAKE_CALLS} environment variable.
+ \li The \c{.qmlls.ini} settings file, see \l {Configuration File}.
+\endlist
+
+\section1 Configuration File
+
+\QMLLS can be configured via a configuration file \c{.qmlls.ini}.
+This file should be in the root source directory of the project.
+It should be a text file in the ini-format.
+
+\note \c{.qmlls.ini} files can be generated automatically via
+\l{QT_QML_GENERATE_QMLLS_INI}.
+
+The configuration file should look like this:
+\code
+// .qmlls.ini
+[General]
+buildDir=<path/to/build-directory>
+no-cmake-calls=<true-or-false>
+\endcode
+
+Currently, the configuration file can be used to set the build
+directory of the current project and optionally disable the automatic
+CMake rebuild functionality for C++ defined QML types.
+
+\note \QMLLS can create default configuration files
+using the \c{--write-defaults} option. This will overwrite an
+already existing .qmlls.ini file in the current directory.
+
+\section1 Known Limitations
+
+\QMLLS might emit false positive warnings on projects
+that were not built, as it needs the build information to find
+QML modules defined in the same project, for example.
+
+Despite covering many common QML features,
+the \QMLLS is still in development with some features
+yet to be supported:
+
+\list
+ \li Renaming QML types.
+ \li Suggesting autocompletions on invalid QML files.
+ \li Navigating to definitions of objects defined in C++.
+ \li Supporting all QML and JavaScript language constructs for all features.
+\endlist
+
+The QML code model in the \QMLLS does not yet
+support all of the JavaScript language constructs, which means that
+some features like navigating to definition and finding usages might not
+work on these language constructs.
+
+
+*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qmlpreview.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qmlpreview.qdoc
new file mode 100644
index 0000000000..5e9da3d2f4
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-qmlpreview.qdoc
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-qmlpreview.html
+\title qmlpreview
+\brief Overview of the qmlpreview utility.
+\ingroup qtqml-tooling
+
+\section1 The Qml Preview
+The QML Preview tool watches QML and JavaScript files on disk and updates
+the application live with any changes. The application to be previewed
+has to have QML debugging enabled. \l {details}{More...}
+
+\table
+\header
+ \li Usage
+\row
+ \li qmlpreview [\l{options}] executable [parameters...]
+\endtable
+
+\section2 options
+
+\table
+\header
+ \li Option
+ \li Description
+\row
+ \li --verbose
+ \li Print debugging output.
+\row
+ \li -h, --help
+ \li Displays help on commandline options.
+\row
+ \li --help-all
+ \li Displays help, including generic Qt options.
+\row
+ \li -v, --version
+ \li Displays version information.
+\endtable
+
+\section2 Arguments
+
+\table
+ \header
+ \li Argument
+ \li Description
+ \row
+ \li executable
+ \li The path of the executable file that loads a QML document.
+ \row
+ \li parameters
+ \li Arguments of the executable
+
+\endtable
+
+\section2 Details
+\target details
+
+\section3 Enable QML Debugging
+To enable QML debugging, make sure you build your application with appropriate
+configuration parameters. When using qmake, you should add \c {CONFIG+=qml_debug}
+in the \c {.pro} file. If you use another build system, then \c {QT_QML_DEBUG}
+variable should be defined.
+
+\badcode
+ qt_add_executable(MyApp
+ ...
+ )
+
+ target_compile_definitions(MyApp PRIVATE QT_QML_DEBUG)
+\endcode
+
+*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qmlprofiler.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qmlprofiler.qdoc
new file mode 100644
index 0000000000..6bce0417b7
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-qmlprofiler.qdoc
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-qmlprofiler.html
+\title qmlprofiler
+\brief Overview of the qml profiler tool.
+\ingroup qtqml-tooling
+
+*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-qmltyperegistrar.qdoc b/src/qml/doc/src/tools/qtqml-tooling-qmltyperegistrar.qdoc
new file mode 100644
index 0000000000..1f2103d1b1
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-qmltyperegistrar.qdoc
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-qmltyperegistrar.html
+\title qmltyperegistrar
+\brief Overview of the qmltyperegistrar utility.
+\ingroup qtqml-tooling
+
+*/
diff --git a/src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc b/src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc
new file mode 100644
index 0000000000..836acc3f6a
--- /dev/null
+++ b/src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc
@@ -0,0 +1,92 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tooling-svgtoqml.html
+\title svgtoqml
+\brief The SVG to QML converter tool
+\ingroup qtqml-tooling
+
+\c svgtoqml is a command line tool shipped with Qt that converts an SVG document
+to a QML file. This QML file can then be used as a component in Qt Quick
+applications. The \l{Weather Forecast Example} includes multiple QML files that have been generated
+using this tool.
+
+\section1 Overview
+The \c svgtoqml will convert an SVG file to a QML file which uses Qt Quick primitives. Since
+Qt Quick supports scalable vector graphics, the resulting item will be smoothly transformable as far
+as this is possible. As a baseline, the tool supports most of the static features of the SVG Tiny 1.2
+profile. Certain additional features are supported, determined on a case-by-case basis. Interactive
+features and animations are not supported.
+
+\section1 Usage
+The basic usage of \c svgtoqml is to provide an input file and an output file:
+\c{svgtoqml input.svg output.qml}. This will read the \c{input.svg} file and convert it into the
+corresponding Qt Quick scene in \c{output.qml}, which can then be used as part of a Qt Quick
+application.
+
+In addition, it supports the following options:
+
+\table
+\header
+ \li Option
+ \li Description
+\row
+ \li --copyright-statement <string>
+ \li Adds <string> as a comment at the beginning of the generated file.
+\row
+ \li --curve-renderer
+ \li Enables the curve renderer backend for \l{Qt Quick Shapes}. This enables smooth, antialiased
+ shapes in the scene without multi-sampling, but at some extra cost.
+\row
+ \li --optimize-paths
+ \li Enables optimization of paths before committing them to the QML file, potentially making
+ them faster to load and render later.
+\row
+ \li --outline-stroke-mode
+ \li Stroke the outline (contour) of the filled shape instead of the original path.
+\row
+ \li -t, --type-name <string>
+ \li In place of \l{Shape}, the output will use the type name <string> instead. This is
+ enables using a custom item to override the default behavior of \l{Shape} items.
+\row
+ \li -v, --view
+ \li Display a preview of the Qt Quick item as it will be generated.
+\endtable
+
+\section1 Comparison to other options
+There are multiple options for including SVG content in Qt Quick. The following will give an
+overview of where \c svgtoqml fits into the story.
+
+\section2 Comparison to Qt Svg
+\l{Qt Svg} is a module which provides a parser and software renderer for SVG files. In addition, it
+includes an image loader plugin, so that SVG files can be loaded directly by the \l{Image} element
+in Qt Quick. The SVG will then be rasterized and cached at a specified size and redrawing it will
+be quite cheap. But zooming into the image without pixelation will involve reloading it at a
+different size, which in turn can be expensive.
+
+\c svgtoqml (and the \l{VectorImage} component) are alternative ways of rendering the same content.
+Once loaded into Qt Quick, transforms can be changed while retaining the geometry data needed to
+render the scene in GPU memory. Thus, the vector image can be redrawn at different scales with very
+little overhead.
+
+If the image size will not change during the life time of the application, however, loading the
+SVG as an \l{Image} will be more efficient. In this case, if the SVG is always rendered at a
+small subset of possible sizes, consider pre-rasterizing it to an image format which is more
+efficient to load, such as \c PNG.
+
+\section2 Comparison to VectorImage
+The \l{VectorImage} component provides the same basic functionality as \c svgtoqml, but instead of
+pregenerating the Qt Quick scene as a QML file, it creates the scene at runtime. This allows loading
+SVG files that are not provided at build time and thus allows for more flexibility. Pregenerating
+the scenes with \c svgtoqml allows optimizing the scene before it is loaded. Thus, for files that
+are available at build time, \c svgtoqml is the preferred option.
+
+\section2 Comparison to PathSvg
+The \l{PathSvg} component is part of the \l{Qt Quick Shapes} module. It provides a way to define
+paths with the syntax used by SVG, where the control points of a path are specified as a string. It
+does not support loading SVG files, so it is not a direct alternative to \c svgtoqml. If a complex
+SVG contains a specific shape needed by the application, then copying this path description into
+\l{PathSvg} may be more convenient than generating the full file.
+
+*/
diff --git a/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc b/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc
new file mode 100644
index 0000000000..302c71882a
--- /dev/null
+++ b/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc
@@ -0,0 +1,120 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-qml-script-compiler.html
+\title QML script compiler
+\brief A tool to compile functions and expressions in QML.
+\keyword qmlsc
+\ingroup qtqml-tooling
+
+The QML script compiler compiles functions and expressions in QML and JavaScript
+files to a byte code that can be interpreted or Just-in-time compiled by the
+QML engine.
+
+In addition, it compiles some functions and expressions in QML files into C++
+code, within limitations set by the nature of JavaScript. It generates C++ code
+for functions that can be exhaustively analyzed. The following flow chart
+explains the compilation workflow.
+
+\image qmlsc-compilation-scheme.png
+
+QML script compiler is available in two versions. One is \e qmlcachegen, which
+is a part of the \l{Qt Quick Compiler}. Another one is \e qmlsc, which is a part
+of the commercial-only add-on \e{Qt Quick Compiler Extensions}.
+
+\section1 qmlcachegen
+\e qmlcachegen uses the meta-object system and generates lookups and stores them in a
+central place, a compilation unit. The compilation unit contains a representation of
+document structure, compact byte code representation for each function and expression,
+and native code for functions and bindings that compiler fully understands.
+The byte code in a compilation unit can be used by the QML engine to avoid re-compilation
+and to speed up execution.
+
+\section1 qmlsc
+\e qmlsc, on the flip side, extends the base functionality of qmlcachegen by providing
+two extra modes.
+
+\list
+\li \l {static mode}
+\li \l {direct mode}
+\endlist
+
+\section2 static mode
+In static mode, qmlsc assumes that no properties of any types exposed to C++ can be
+shadowed by derived types. It eliminates the shadow checking mechanism and allows more
+JavaScript code to be compiled to C++ and eventually generates faster code.
+
+To enable static mode in qmlsc, you should pass \c{--static} via \c{QT_QMLCACHEGEN_ARGUMENTS} to \l{qt_add_qml_module}.
+\badcode
+ qt_add_qml_module(someTarget
+ ...
+ )
+
+ set_target_properties(someTarget PROPERTIES
+ QT_QMLCACHEGEN_ARGUMENTS "--static"
+ )
+\endcode
+
+\warning qmlsc static-mode generates invalid code if any properties are shadowed in
+the QML document.
+
+\section2 direct mode
+In direct mode, qmlsc assumes that all C++ types used in your QML code are available
+and can be included as C++ headers to the generated code. Then the generated code
+accesses or modifies properties by directly calling getters, setters and invokable
+functions in those headers which makes the execution even faster. This means you have to
+link to private Qt APIs in CMake.
+
+\warning Private Qt APIs change often. You will need to recompile Qt for each new version.
+
+\warning If a type is only defined in a plugin or has no header, you can’t use it in direct mode.
+
+To enable direct mode, you should consider the followings:
+
+\list
+ \li you should pass \c{--direct-calls} via \c{QT_QMLCACHEGEN_ARGUMENTS} to \l{qt_add_qml_module}.
+
+\badcode
+ qt_add_qml_module(someTarget
+ ...
+ )
+
+ set_target_properties(someTarget PROPERTIES
+ QT_QMLCACHEGEN_ARGUMENTS "--direct-calls"
+ )
+\endcode
+
+ \li Link all the relavant private Qt modules instead of their public counterparts.
+\badcode
+ qt_add_qml_module(someTarget
+ ...
+ )
+
+ target_link_libraries(someTarget PRIVATE
+ Qt::QmlPrivate
+ Qt::QuickPrivate
+ ...
+ )
+\endcode
+
+ \li Do not set the \c{PLUGIN_TARGET} to be the same as the backing library target.
+\badcode
+ # direct mode will not function in this setup.
+ qt_add_qml_module(someTarget
+ PLUGIN_TARGET someTarget
+ ...
+ )
+\endcode
+\endlist
+
+\section1 Limitations when compiling JavaScript to C++
+
+Many JavaScript constructs cannot be efficiently represented in C++. The QML
+script compiler skips the C++ code generation for functions that contain such
+constructs and only generates byte code to be interpreted or run through the
+Just-in-time compiler. 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.
+
+*/
diff --git a/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc b/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc
new file mode 100644
index 0000000000..38f8fe039b
--- /dev/null
+++ b/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-type-compiler.qdoc
@@ -0,0 +1,283 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-qml-type-compiler.html
+\title QML type compiler
+\brief A tool to compile QML types to C++ ahead of time.
+\keyword qmltc
+\ingroup qtqml-tooling
+
+The QML type compiler, \c qmltc, is a tool shipped with Qt to translate QML
+types into C++ types that are \e{ahead-of-time} compiled as part of the user
+code. Using qmltc can lead to better run-time performance due to more
+optimization opportunities available to the compiler compared to a
+QQmlComponent-based object creation. The qmltc is part of the \l{Qt Quick Compiler}
+toolchain.
+
+By design, qmltc outputs user-facing code. That code is supposed to be utilized
+by the C++ application directly, otherwise you won't see any benefit. This
+generated code essentially replaces QQmlComponent and its APIs to create objects
+from QML documents. You can find more information under \l{Using qmltc in a QML
+application} and \l{Generated Output Basics}.
+
+In order to enable qmltc:
+
+\list
+ \li Create a \l{qt_add_qml_module}{proper QML module} for your application.
+
+ \li Invoke qmltc, for example, through the \l{qmltc-cmake}{CMake API}.
+
+ \li \c{#include} the generated header file(s) in the application source
+ code.
+
+ \li Instantiate an object of the generated type.
+\endlist
+
+In this workflow qmltc usually runs during the build process. Thus, when qmltc
+rejects a QML document (whether due to errors or warnings, or because of
+constructs qmltc doesn't yet support), the build process will fail. This is
+similar to how you receive qmllint errors when you enable the automatic
+generation of linting targets during \l{qt_add_qml_module}{QML module creation}
+and then attempt to "build" them to run the qmllint.
+
+\warning qmltc is currently in a Tech Preview stage and might not compile an
+arbitrary QML program (see \l{Known Limitations} for more details). When qmltc
+fails, nothing is generated as your application cannot sensibly use the qmltc
+output. If your program contains errors (or unsolvable warnings), they should be
+fixed to enable the compilation. The general rule is to adhere to the best
+practices and follow \l{qmllint Reference}{qmllint} advice.
+
+\note \c qmltc does not guarantee that the generated C++ stays API-, source- or
+binary-compatible between past or future versions, even patch versions.
+Furthermore, qmltc-compiled apps using Qt's QML modules will require linking
+against private Qt API. This is because Qt's QML modules do not usually provide
+a public C++ API since their primary usage is through QML.
+
+
+\section2 Using qmltc in a QML application
+
+From the build system perspective, adding qmltc compilation is not much
+different from adding qml cache generation. Naively, the build process could be
+described as:
+
+\image qmltc-compilation-scheme.png
+
+While the real compilation process is much trickier, this diagram captures the
+core components that qmltc uses: the QML files themselves and qmldir with
+qmltypes information. Simpler applications typically have rather primitive
+qmldir yet, in general, qmldir could be complex, providing essential, nicely
+packed type information that qmltc relies on to perform correct QML-to-C++
+translation.
+
+Nevertheless, adding an extra build step is not enough in qmltc case. The
+application code must also be modified to use qmltc-generated classes instead of
+QQmlComponent or its higher-level alternatives.
+
+\section3 Compiling QML code with qmltc
+
+Qt, starting from Qt 6, uses CMake to build its various components. User
+projects can - and are encouraged to - also use CMake to build their components
+using Qt. Adding out-of-the-box qmltc compilation support to your project would
+require a CMake-driven build flow as well since this flow is centered around
+proper QML modules and their infrastructure.
+
+The easy way to add qmltc compilation is by using the dedicated
+\l{qmltc-cmake}{CMake API} as part of a QML module creation for the application.
+Consider a simple application directory structure:
+
+\badcode
+.
+├── CMakeLists.txt
+├── myspecialtype.h // C++ type exposed to QML
+├── myspecialtype.cpp
+├── myApp.qml // main QML page
+├── MyButton.qml // custom UI button
+├── MySlider.qml // custom UI slider
+└── main.cpp // main C++ application file
+\endcode
+
+Then the CMake code would usually look similar to the following:
+
+\snippet qmltc/CMakeLists.txt qmltc-app-name
+\codeline
+\snippet qmltc/CMakeLists.txt qmltc-qml-files
+\codeline
+\snippet qmltc/CMakeLists.txt qmltc-add-qml-module
+\codeline
+\snippet qmltc/CMakeLists.txt qmltc-compile-to-cpp
+
+\section3 Using the Generated C++
+
+Unlike in the case of QQmlComponent instantiation, the output of qmltc, being
+C++ code, is used directly by the application. Generally, constructing a new
+object in C++ is equivalent to creating a new object through
+QQmlComponent::create(). Once created, the object could be manipulated from C++
+or, for example, combined with QQuickWindow to be drawn on screen.
+
+If a compiled type exposes some required properties, `qmltc` will
+require an initial value for those properties in the constructor for
+the generated object.
+
+Additionally, the constructor for a qmltc object can be provided with
+with a callback to set up initial values for the component's
+properties.
+
+Given a \c{myApp.qml} file, the application code (in both cases) would
+typically look like this:
+
+\if defined(onlinedocs)
+ \tab {generated-c++}{tab-qqmlcomponent}{Using QQmlComponent}{checked}
+ \tab {generated-c++}{tab-qmltc}{Using qmltc-generated class}{}
+ \tabcontent {tab-qqmlcomponent}
+\else
+ \section4 Using QQmlComponent
+\endif
+\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-include
+\codeline
+\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-0
+\codeline
+\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-1
+\codeline
+\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-2
+\codeline
+\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
+\if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmltc}
+\else
+ \section4 Using qmltc-generated class
+\endif
+\snippet qmltc/tst_qmltc_examples.cpp qmltc-include
+\codeline
+\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-code
+\codeline
+\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
+\if defined(onlinedocs)
+ \endtabcontent
+\endif
+
+\section2 QML engine
+
+The generated code uses QQmlEngine to interact with dynamic parts of a QML
+document - mainly the JavaScript code. For this to work, no special arrangements
+are needed. Any QQmlEngine instance passed to the constructor of a
+qmltc-generated class object should work correctly as does
+\c{QQmlComponent(engine)}. This also means that you can use
+\l{QQmlEngine}{QQmlEngine methods} that affect QML behavior. However, there are
+caveats. Unlike QQmlComponent-based object creation, qmltc itself \e{does not}
+rely on QQmlEngine when compiling the code to C++. For instance,
+\c{QQmlEngine::addImportPath("/foo/bar/")} - normally resulting in an additional
+import path to scan for - would be completely ignored by the ahead-of-time qmltc
+procedure.
+
+\note To add import paths to the qmltc compilation, consider using a relevant
+argument of the \l{qmltc-cmake}{CMake command} instead.
+
+Generally, you can think of it this way: QQmlEngine involves the application
+process to run, while qmltc does not as it operates \e{before} your application
+is even compiled. Since qmltc makes no attempt to introspect your application's
+C++ source code, there is no way for it to know about certain kinds of QML
+manipulations you, as a user, do. Instead of using QQmlEngine and related
+run-time routines to expose types to QML, adding import paths, etc. you are,
+practically, required to create \l{qt_add_qml_module}{well-behaving QML modules}
+and use \l{Defining QML Types from C++}{declarative QML type registration}.
+
+\warning Despite qmltc working closely with QQmlEngine and creating C++ code,
+the generated classes cannot be further exposed to QML and used through
+QQmlComponent.
+
+\section2 Generated Output Basics
+
+\c qmltc aims to be compatible with the existing QML execution model. This
+implies that the generated code is roughly equivalent to the internal
+QQmlComponent setup logic and thus you should be able to understand your QML
+type's behavior, semantics and API the same way you do currently - by visually
+inspecting the corresponding QML document.
+
+However, the generated code is still somewhat confusing, especially given that
+your application should use the qmltc output on the C++ side directly. There are
+two parts of the generated code: CMake build files structure and the generated
+C++ format. The former is covered in the \l{qmltc-cmake}{CMake API of qmltc} and
+the latter is covered here.
+
+Consider a simple HelloWorld type, that has a \c hello property, a function to
+print that property, and a signal emitted when the object of that type is
+created:
+
+\snippet qmltc/special/HelloWorld.qml qmltc-hello-world-qml
+
+When providing a C++ alternative of this QML type, the C++ class would need a
+\l{Overview - QML and C++ Integration}{QML-specific meta-object system macro},
+Q_PROPERTY decoration for the \c hello property, \c{Q_INVOKABLE} C++ printing
+function and a regular Qt signal definition. Similarly, qmltc would translate
+the given HelloWorld type into roughly the following:
+
+\snippet qmltc/special/HelloWorld.qml.cpp qmltc-hello-world-generated
+
+Even though specific details of the generated type could differ, the universal
+aspects remain. For instance:
+
+\list
+ \li QML types within a document are translated into C++ types, according to
+ the compiler-visible information.
+ \li Properties are translated into C++ properties with Q_PROPERTY
+ declarations.
+ \li JavaScript functions become \c{Q_INVOKABLE} C++ functions.
+ \li QML signals are transformed into C++ Qt signals.
+ \li QML enumerations are converted into C++ enumerations with \c{Q_ENUM}
+ declarations.
+\endlist
+
+An additional detail is the way \c qmltc generates class names. A class name for
+a given QML type is automatically deduced from the QML document defining that
+type: the QML file name without extensions (up to and excluding the first \c{.},
+also known as the base name) becomes a class name. The file name case is
+preserved. Thus, \c{HelloWorld.qml} would result in a \c{class HelloWorld} and
+\c{helloWoRlD.qml} in a \c{class helloWoRlD}. Following the QML convention, if a
+QML document file name starts with a lower-case letter, the generated C++ class
+is assumed to be anonymous and marked with \l{QML_ANONYMOUS}.
+
+For now, although the generated code is ready to be used from the C++
+application side, you should generally limit calls to the generated APIs.
+Instead, prefer implementing the application logic in QML/JavaScript and
+hand-written C++ types exposed to QML, using the qmltc-created classes for
+simple object instantiation. While generated C++ gives you direct (and usually
+faster) access to QML-defined elements of the type, understanding such code
+could be a challenge.
+
+\section2 Known Limitations
+
+Despite covering many common QML features, qmltc is still in the early stage of
+development with some things yet to be supported.
+
+Imported QML modules that consist of QML-defined types (such as
+\c{QtQuick.Controls}) might not get compiled correctly, even if those QML-defined
+types were compiled by qmltc..
+At present, you can reliably use \c{QtQml} and \c{QtQuick} modules as well as any
+other QML module that \b{only} contains C++ classes exposed to QML.
+
+On top of this, there are some more fundamental peculiarities to consider:
+
+\list
+ \li Qt's QML modules usually rely on C++ libraries to do the heavy lifting.
+ Often enough, these libraries do not provide public C++ API (since their
+ primary usage is through QML). For the users of qmltc, this means that their
+ apps need to link against private Qt libraries.
+
+ \li Due to the nature of qmltc code generation, QML plugins are unusable for
+ compilation purposes. Instead, QML modules - that use a plugin - have to
+ ensure that the plugin data is accessible at compile time. Such QML modules
+ would then have \e optional plugins. In most cases, the compile-time
+ information can be provided through a header file (with C++ declarations)
+ and linkable library (with C++ definitions). The user code is responsible
+ (usually through CMake) for including a path to the header file and linking
+ against the QML module library.
+\endlist
+
+\note
+Given the tech preview status of the compiler, you might also encounter bugs in
+qmltc, in the generated code, or some other related part. We encourage you to
+\l{https://bugreports.qt.io/}{submit a bug report} in this case.
+
+*/
diff --git a/src/qml/doc/src/tools/qtquickcompiler/qtqml-tool-qmlcachegen.qdoc b/src/qml/doc/src/tools/qtquickcompiler/qtqml-tool-qmlcachegen.qdoc
new file mode 100644
index 0000000000..b7f6d56705
--- /dev/null
+++ b/src/qml/doc/src/tools/qtquickcompiler/qtqml-tool-qmlcachegen.qdoc
@@ -0,0 +1,13 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtqml-tool-qmlcachegen.html
+\title qmlcachegen
+\brief A tool to compile QML documents ahead of time
+\ingroup qtqml-tooling
+
+\e qmlcachegen is an internal build tool, invoked by the build system
+when using \l qt_add_qml_module in CMake or CONFIG+=qtquickcompiler in
+qmake. Users should not invoke it manually.
+*/
diff --git a/src/qml/inlinecomponentutils_p.h b/src/qml/inlinecomponentutils_p.h
index 50936a19f0..c7a93c870b 100644
--- a/src/qml/inlinecomponentutils_p.h
+++ b/src/qml/inlinecomponentutils_p.h
@@ -14,6 +14,7 @@
// We mean it.
//
+#include <private/qqmlmetatype_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qv4resolvedtypereference_p.h>
@@ -55,10 +56,19 @@ public:
IndexType index() const { return m_data.get<IndexField>(); }
};
+using NodeList = std::vector<Node>;
using AdjacencyList = std::vector<std::vector<Node*>>;
+inline bool containedInSameType(const QQmlType &a, const QQmlType &b)
+{
+ return QQmlMetaType::equalBaseUrls(a.sourceUrl(), b.sourceUrl());
+}
+
template<typename ObjectContainer, typename InlineComponent>
-void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, AdjacencyList &adjacencyList, std::vector<Node> &nodes, const std::vector<InlineComponent> &allICs) {
+void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer,
+ AdjacencyList &adjacencyList, NodeList &nodes,
+ const std::vector<InlineComponent> &allICs)
+{
using CompiledObject = typename ObjectContainer::CompiledObject;
// add an edge from A to B if A and B are inline components with the same containing type
// and A inherits from B (ignore indirect chains through external types for now)
@@ -71,9 +81,10 @@ void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, Adja
if (targetTypeRef) {
const auto targetType = targetTypeRef->type();
if (targetType.isInlineComponentType()
- && targetType.containingType() == currentICTypeRef->type().containingType()) {
+ && containedInSameType(targetType, currentICTypeRef->type())) {
auto icIt = std::find_if(allICs.cbegin(), allICs.cend(), [&](const QV4::CompiledData::InlineComponent &icSearched){
- return int(icSearched.objectIndex) == targetType.inlineComponentObjectId();
+ return objectContainer->stringAt(icSearched.nameIndex)
+ == targetType.elementName();
});
Q_ASSERT(icIt != allICs.cend());
Node& target = nodes[i];
@@ -102,7 +113,9 @@ void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, Adja
}
};
-inline void topoVisit(Node *node, AdjacencyList &adjacencyList, bool &hasCycle, std::vector<Node> &nodesSorted) {
+inline void topoVisit(Node *node, AdjacencyList &adjacencyList, bool &hasCycle,
+ NodeList &nodesSorted)
+{
if (node->hasPermanentMark())
return;
if (node->hasTemporaryMark()) {
@@ -121,8 +134,9 @@ inline void topoVisit(Node *node, AdjacencyList &adjacencyList, bool &hasCycle,
};
// Use DFS based topological sorting (https://en.wikipedia.org/wiki/Topological_sorting)
-inline std::vector<Node> topoSort(std::vector<Node> &nodes, AdjacencyList &adjacencyList, bool &hasCycle) {
- std::vector<Node> nodesSorted;
+inline NodeList topoSort(NodeList &nodes, AdjacencyList &adjacencyList, bool &hasCycle)
+{
+ NodeList nodesSorted;
nodesSorted.reserve(nodes.size());
hasCycle = false;
diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp
index 3938265019..2b33d0aa10 100644
--- a/src/qml/jit/qv4assemblercommon.cpp
+++ b/src/qml/jit/qv4assemblercommon.cpp
@@ -44,10 +44,20 @@ public:
void vprintf(const char* format, va_list argList) override WTF_ATTRIBUTE_PRINTF(2, 0)
{
- const int written = qvsnprintf(buf.data(), buf.size(), format, argList);
- if (written > 0)
- dest->write(buf.constData(), written);
- memset(buf.data(), 0, qMin(written, buf.size()));
+ const int printed = qvsnprintf(buf.data(), buf.size(), format, argList);
+ Q_ASSERT(printed <= buf.size());
+
+ qint64 written = 0;
+ while (written < printed) {
+ const qint64 result = dest->write(buf.constData() + written, printed - written);
+ if (result < 0)
+ break;
+ written += result;
+ }
+
+ Q_ASSERT(written <= buf.size());
+ Q_ASSERT(written >= 0);
+ memset(buf.data(), 0, size_t(written));
}
void flush() override
diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h
index cccd90fab8..e83608ae23 100644
--- a/src/qml/jit/qv4assemblercommon_p.h
+++ b/src/qml/jit/qv4assemblercommon_p.h
@@ -16,12 +16,15 @@
//
#include <private/qv4engine_p.h>
-#include <private/qv4global_p.h>
#include <private/qv4function_p.h>
-#include <QHash>
+#include <private/qv4global_p.h>
+#include <private/qv4stackframe_p.h>
+
#include <wtf/Vector.h>
#include <assembler/MacroAssembler.h>
+#include <QtCore/qhash.h>
+
#if QT_CONFIG(qml_jit)
QT_BEGIN_NAMESPACE
@@ -30,7 +33,7 @@ namespace QV4 {
namespace JIT {
#if defined(Q_PROCESSOR_X86_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN) || defined(Q_OS_SOLARIS)
+#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN) || defined(Q_OS_SOLARIS) || defined(Q_OS_VXWORKS)
class PlatformAssembler_X86_64_SysV : public JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
{
@@ -559,7 +562,7 @@ public:
Address loadConstAddress(int constIndex, RegisterID baseReg = ScratchRegister)
{
Address addr = loadCompilationUnitPtr(baseReg);
- addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, constants);
+ addr.offset = offsetof(QV4::CompilationUnitRuntimeData, constants);
loadPtr(addr, baseReg);
addr.offset = constIndex * int(sizeof(QV4::Value));
return addr;
@@ -568,7 +571,7 @@ public:
Address loadStringAddress(int stringId)
{
Address addr = loadCompilationUnitPtr(ScratchRegister);
- addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, runtimeStrings);
+ addr.offset = offsetof(QV4::CompilationUnitRuntimeData, runtimeStrings);
loadPtr(addr, ScratchRegister);
return Address(ScratchRegister, stringId * PointerSize);
}
diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index b97126945e..496624c752 100644
--- a/src/qml/jit/qv4baselineassembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -236,8 +236,7 @@ public:
void isNullOrUndefined()
{
move(AccumulatorRegister, ScratchRegister);
- move(TrustedImm64(Value::ManagedMask), ScratchRegister2);
- compare64(Equal, ScratchRegister, ScratchRegister2, AccumulatorRegister);
+ compare64(Equal, ScratchRegister, TrustedImm32(0), AccumulatorRegister);
Jump isUndef = branch32(NotEqual, TrustedImm32(0), AccumulatorRegister);
// not undefined
@@ -904,7 +903,7 @@ void BaselineAssembler::storeHeapObject(int reg)
void BaselineAssembler::loadImport(int index)
{
Address addr = pasm()->loadCompilationUnitPtr(PlatformAssembler::ScratchRegister);
- addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, imports);
+ addr.offset = offsetof(QV4::CompilationUnitRuntimeData, imports);
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
addr.offset = index * int(sizeof(QV4::Value*));
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index 14e183adb8..bd2e331cbc 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -173,6 +173,7 @@ void BaselineJIT::generate_LoadName(int name)
void BaselineJIT::generate_LoadGlobalLookup(int index)
{
+ STORE_IP();
as->prepareCallWithArgCount(3);
as->passInt32AsArg(index, 2);
as->passFunctionAsArg(1);
@@ -182,6 +183,7 @@ void BaselineJIT::generate_LoadGlobalLookup(int index)
void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index)
{
+ STORE_IP();
as->prepareCallWithArgCount(2);
as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
@@ -506,6 +508,8 @@ void BaselineJIT::generate_ThrowException()
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowException, CallResultDestination::Ignore);
as->gotoCatchException();
+
+ // LOAD_ACC(); <- not needed here since it would be unreachable.
}
void BaselineJIT::generate_GetException() { as->getException(); }
@@ -513,9 +517,11 @@ void BaselineJIT::generate_SetException() { as->setException(); }
void BaselineJIT::generate_CreateCallContext()
{
+ STORE_ACC();
as->prepareCallWithArgCount(1);
as->passCppFrameAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(PushCallContext, CallResultDestination::Ignore);
+ LOAD_ACC();
}
void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); }
@@ -580,7 +586,7 @@ void BaselineJIT::generate_GetIterator(int iterator)
BASELINEJIT_GENERATE_RUNTIME_CALL(GetIterator, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_IteratorNext(int value, int done)
+void BaselineJIT::generate_IteratorNext(int value, int offset)
{
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(3);
@@ -588,10 +594,10 @@ void BaselineJIT::generate_IteratorNext(int value, int done)
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorNext, CallResultDestination::InAccumulator);
- as->storeReg(done);
+ labels.insert(as->jumpTrue(absoluteOffset(offset)));
}
-void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object)
+void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object, int offset)
{
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(4);
@@ -600,13 +606,13 @@ void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object)
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorNextForYieldStar, CallResultDestination::InAccumulator);
+ labels.insert(as->jumpTrue(absoluteOffset(offset)));
}
-void BaselineJIT::generate_IteratorClose(int done)
+void BaselineJIT::generate_IteratorClose()
{
as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(3);
- as->passJSSlotAsArg(done, 2);
+ as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorClose, CallResultDestination::InAccumulator);
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index c502e4d405..40138ea700 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -106,9 +106,9 @@ public:
void generate_PopScriptContext() override;
void generate_PopContext() override;
void generate_GetIterator(int iterator) override;
- void generate_IteratorNext(int value, int done) override;
- void generate_IteratorNextForYieldStar(int iterator, int object) override;
- void generate_IteratorClose(int done) override;
+ void generate_IteratorNext(int value, int offset) override;
+ void generate_IteratorNextForYieldStar(int iterator, int object, int offset) override;
+ void generate_IteratorClose() override;
void generate_DestructureRestElement() override;
void generate_DeleteProperty(int base, int index) override;
void generate_DeleteName(int name) override;
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index d3a6ed7391..8346ef5d84 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -16,6 +16,7 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4qmetaobjectwrapper_p.h>
#include <private/qv4stackframe_p.h>
#include <private/qv4module_p.h>
#include <private/qv4symbol_p.h>
@@ -243,7 +244,7 @@ Q_DECLARE_METATYPE(QList<int>)
\section1 Extensions
QJSEngine provides a compliant ECMAScript implementation. By default,
- familiar utilities like logging are not available, but they can can be
+ familiar utilities like logging are not available, but they can be
installed via the \l installExtensions() function.
\sa QJSValue, {Making Applications Scriptable},
@@ -345,11 +346,8 @@ QJSEngine::QJSEngine()
*/
QJSEngine::QJSEngine(QObject *parent)
- : QObject(*new QJSEnginePrivate, parent)
- , m_v4Engine(new QV4::ExecutionEngine(this))
+ : QJSEngine(*new QJSEnginePrivate, parent)
{
- checkForApplicationInstance();
-
QJSEnginePrivate::addToDebugServer(this);
}
@@ -367,11 +365,12 @@ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent)
Destroys this QJSEngine.
Garbage is not collected from the persistent JS heap during QJSEngine
- destruction. If you need all memory freed, call collectGarbage manually
+ destruction. If you need all memory freed, call collectGarbage() manually
right before destroying the QJSEngine.
*/
QJSEngine::~QJSEngine()
{
+ m_v4Engine->inShutdown = true;
QJSEnginePrivate::removeFromDebugServer(this);
delete m_v4Engine;
}
@@ -391,7 +390,11 @@ QJSEngine::~QJSEngine()
when the QJSEngine decides that it's wise to do so (i.e. when a certain number of new objects
have been created). However, you can call this function to explicitly request that garbage
collection should be performed as soon as possible.
-*/
+
+
+ \sa {Garbage Collection}
+ \sa {Qt::}{gc()}
+ */
void QJSEngine::collectGarbage()
{
m_v4Engine->memoryManager->runGC();
@@ -474,6 +477,9 @@ static QUrl urlForFileName(const QString &fileName)
The script code will be evaluated in the context of the global object.
+ \note If you need to evaluate inside a QML context, use \l QQmlExpression
+ instead.
+
The evaluation of \a program can cause an \l{Script Exceptions}{exception} in the
engine; in this case the return value will be the exception
that was thrown (typically an \c{Error} object; see
@@ -504,6 +510,8 @@ static QUrl urlForFileName(const QString &fileName)
exception value will still be returned. Use \c exceptionStackTrace->isEmpty()
to distinguish whether the value was a normal or an exceptional return
value.
+
+ \sa QQmlExpression::evaluate
*/
QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber, QStringList *exceptionStackTrace)
{
@@ -528,9 +536,9 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
result = v4->catchException(&trace);
if (exceptionStackTrace) {
for (auto &&frame: trace)
- exceptionStackTrace->push_back(QString::fromLatin1("%1:%2:%3:%4").arg(
+ exceptionStackTrace->push_back(QLatin1StringView("%1:%2:%3:%4").arg(
frame.function,
- QString::number(frame.line),
+ QString::number(qAbs(frame.line)),
QString::number(frame.column),
frame.source)
);
@@ -573,7 +581,7 @@ QJSValue QJSEngine::importModule(const QString &fileName)
QV4::Scope scope(m_v4Engine);
if (const auto compiled = module.compiled) {
- QV4::Scoped<QV4::Module> moduleNamespace(scope, compiled->instantiate(m_v4Engine));
+ QV4::Scoped<QV4::Module> moduleNamespace(scope, compiled->instantiate());
if (m_v4Engine->hasException)
return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException());
compiled->evaluate();
@@ -872,6 +880,12 @@ bool QJSEngine::convertString(const QString &string, QMetaType metaType, void *p
case QMetaType::UInt:
*reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
return true;
+ case QMetaType::Long:
+ *reinterpret_cast<long*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::ULong:
+ *reinterpret_cast<ulong*>(ptr) = QV4::Value::toInteger(d);
+ return true;
case QMetaType::LongLong:
*reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
return true;
@@ -916,6 +930,7 @@ bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
if (const QString *string = QJSValuePrivate::asQString(&value))
return convertString(*string, metaType, ptr);
+ // Does not need scoping since QJSValue still holds on to the value.
return QV4::ExecutionEngine::metaTypeFromJS(QJSValuePrivate::asReturnedValue(&value), metaType, ptr);
}
@@ -923,14 +938,18 @@ bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *
{
// TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to
// duplicate much of metaTypeFromJS and some methods of QV4::Value itself here.
- return QV4::ExecutionEngine::metaTypeFromJS(handle()->fromVariant(value), metaType, ptr);
+ QV4::Scope scope(handle());
+ QV4::ScopedValue scoped(scope, scope.engine->fromVariant(value));
+ return QV4::ExecutionEngine::metaTypeFromJS(scoped, metaType, ptr);
}
bool QJSEngine::convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to)
{
// TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to
// duplicate much of metaTypeFromJS and some methods of QV4::Value itself here.
- return QV4::ExecutionEngine::metaTypeFromJS(handle()->fromData(fromType, from), toType, to);
+ QV4::Scope scope(handle());
+ QV4::ScopedValue scoped(scope, scope.engine->fromData(fromType, from));
+ return QV4::ExecutionEngine::metaTypeFromJS(scoped, toType, to);
}
QString QJSEngine::convertQObjectToString(QObject *object)
@@ -944,6 +963,11 @@ QString QJSEngine::convertDateTimeToString(const QDateTime &dateTime)
return QV4::DateObject::dateTimeToString(dateTime, handle());
}
+double QJSEngine::convertDateTimeToNumber(const QDateTime &dateTime)
+{
+ return QV4::DateObject::dateTimeToNumber(dateTime);
+}
+
QDate QJSEngine::convertDateTimeToDate(const QDateTime &dateTime)
{
return QV4::DateObject::dateTimeToDate(dateTime);
@@ -1007,7 +1031,7 @@ QDate QJSEngine::convertDateTimeToDate(const QDateTime &dateTime)
conversions between JavaScript-equivalent types that are not
performed by qvariant_cast by default.
- \sa coerceValue(), fromScriptValue(), qvariant_cast()
+ \sa coerceValue(), fromScriptValue(), {QVariant::}{qvariant_cast()}
*/
/*! \fn template <typename From, typename To> T QJSEngine::coerceValue(const From &from)
@@ -1019,7 +1043,7 @@ QDate QJSEngine::convertDateTimeToDate(const QDateTime &dateTime)
performed by qvariant_cast by default. This method is a generalization of
all the other conversion methods in this class.
- \sa fromVariant(), qvariant_cast(), fromScriptValue(), toScriptValue()
+ \sa fromVariant(), {QVariant::}{qvariant_cast()}, fromScriptValue(), toScriptValue()
*/
/*!
@@ -1031,7 +1055,7 @@ QDate QJSEngine::convertDateTimeToDate(const QDateTime &dateTime)
JavaScript function through QJSEngine.
When returning from C++, the engine will interrupt the normal flow of
- execution and call the the next pre-registered exception handler with
+ execution and call the next pre-registered exception handler with
an error object that contains the given \a message. The error object
will point to the location of the top-most context on the JavaScript
caller stack; specifically, it will have properties \c lineNumber,
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index c04e47d34e..b1c99db220 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -139,12 +139,21 @@ public:
return toPrimitiveValue(value);
if constexpr (std::is_same_v<T, QString>) {
- if (targetType.flags() & QMetaType::PointerToQObject) {
+ if (sourceType.flags() & QMetaType::PointerToQObject) {
return convertQObjectToString(
*reinterpret_cast<QObject *const *>(value.constData()));
}
}
+ if constexpr (std::is_same_v<QObject, std::remove_const_t<std::remove_pointer_t<T>>>) {
+ if (sourceType.flags() & QMetaType::PointerToQObject) {
+ return *static_cast<QObject *const *>(value.constData());
+
+ // We should not access source->metaObject() here since that may trigger some
+ // rather involved code. convertVariant() can do this using property caches.
+ }
+ }
+
if (sourceType == QMetaType::fromType<QJSValue>())
return fromScriptValue<T>(*reinterpret_cast<const QJSValue *>(value.constData()));
@@ -175,7 +184,7 @@ public:
template<typename From, typename To>
inline To coerceValue(const From &from)
{
- if constexpr (std::is_same_v<From, To>)
+ if constexpr (std::is_base_of_v<To, From>)
return from;
if constexpr (std::is_same_v<To, QJSValue>)
@@ -214,6 +223,8 @@ public:
return from.toLocalTime().time();
if constexpr (std::is_same_v<To, QString>)
return convertDateTimeToString(from.toLocalTime());
+ if constexpr (std::is_same_v<To, double>)
+ return convertDateTimeToNumber(from.toLocalTime());
}
if constexpr (std::is_same_v<From, QDate>) {
@@ -225,6 +236,8 @@ public:
}
if constexpr (std::is_same_v<To, QString>)
return convertDateTimeToString(coerceValue<QDate, QDateTime>(from));
+ if constexpr (std::is_same_v<To, double>)
+ return convertDateTimeToNumber(coerceValue<QDate, QDateTime>(from));
}
if constexpr (std::is_same_v<From, QTime>) {
@@ -237,6 +250,8 @@ public:
return QDateTime(coerceValue<QTime, QDate>(from), from, QTimeZone::LocalTime);
if constexpr (std::is_same_v<To, QString>)
return convertDateTimeToString(coerceValue<QTime, QDateTime>(from));
+ if constexpr (std::is_same_v<To, double>)
+ return convertDateTimeToNumber(coerceValue<QTime, QDateTime>(from));
}
if constexpr (std::is_same_v<To, std::remove_const_t<std::remove_pointer_t<To>> const *>) {
@@ -316,6 +331,7 @@ private:
QString convertQObjectToString(QObject *object);
QString convertDateTimeToString(const QDateTime &dateTime);
+ double convertDateTimeToNumber(const QDateTime &dateTime);
static QDate convertDateTimeToDate(const QDateTime &dateTime);
template<typename T>
@@ -343,10 +359,7 @@ T qjsvalue_cast(const QJSValue &value)
{
if (T t; QJSEngine::convertV2(value, QMetaType::fromType<T>(), &t))
return t;
- else if (value.isVariant())
- return qvariant_cast<T>(value.toVariant());
-
- return T();
+ return qvariant_cast<T>(value.toVariant());
}
template<typename T>
diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h
index bee2f9d79c..735dae8d81 100644
--- a/src/qml/jsapi/qjsengine_p.h
+++ b/src/qml/jsapi/qjsengine_p.h
@@ -30,7 +30,7 @@ namespace QV4 {
struct ExecutionEngine;
}
-class Q_QML_PRIVATE_EXPORT QJSEnginePrivate : public QObjectPrivate
+class Q_QML_EXPORT QJSEnginePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QJSEngine)
diff --git a/src/qml/jsapi/qjslist.cpp b/src/qml/jsapi/qjslist.cpp
new file mode 100644
index 0000000000..b3a4eed3c0
--- /dev/null
+++ b/src/qml/jsapi/qjslist.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtQml/qjslist.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ * \class QJSListIndexClamp
+ * \internal
+ */
+
+/*!
+ * \class QJSList
+ * \internal
+ */
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsapi/qjslist.h b/src/qml/jsapi/qjslist.h
new file mode 100644
index 0000000000..d604e266f2
--- /dev/null
+++ b/src/qml/jsapi/qjslist.h
@@ -0,0 +1,368 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QJSLIST_H
+#define QJSLIST_H
+
+#include <QtQml/qtqmlglobal.h>
+#include <QtQml/qqmllist.h>
+#include <QtQml/qjsengine.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+#include <algorithm>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version. It will be kept compatible with the intended usage by
+// code generated using qmlcachegen.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+struct QJSListIndexClamp
+{
+ static qsizetype clamp(qsizetype start, qsizetype max, qsizetype min = 0)
+ {
+ Q_ASSERT(min >= 0);
+ Q_ASSERT(min <= max);
+ return std::clamp(start < 0 ? max + qsizetype(start) : qsizetype(start), min, max);
+ }
+};
+
+template<typename List, typename Value = typename List::value_type>
+struct QJSList : private QJSListIndexClamp
+{
+ Q_DISABLE_COPY_MOVE(QJSList)
+
+ QJSList(List *list, QJSEngine *engine) : m_list(list), m_engine(engine) {}
+
+ Value at(qsizetype index) const
+ {
+ Q_ASSERT(index >= 0 && index < size());
+ return *(m_list->cbegin() + index);
+ }
+
+ qsizetype size() const { return m_list->size(); }
+
+ void resize(qsizetype size)
+ {
+ m_list->resize(size);
+ }
+
+ bool includes(const Value &value) const
+ {
+ return std::find(m_list->cbegin(), m_list->cend(), value) != m_list->cend();
+ }
+
+ bool includes(const Value &value, qsizetype start) const
+ {
+ return std::find(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(), value)
+ != m_list->cend();
+ }
+
+ QString join(const QString &separator = QStringLiteral(",")) const
+ {
+ QString result;
+ bool atBegin = true;
+ std::for_each(m_list->cbegin(), m_list->cend(), [&](const Value &value) {
+ if (atBegin)
+ atBegin = false;
+ else
+ result += separator;
+ result += m_engine->coerceValue<Value, QString>(value);
+ });
+ return result;
+ }
+
+ List slice() const
+ {
+ return *m_list;
+ }
+ List slice(qsizetype start) const
+ {
+ List result;
+ std::copy(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(),
+ std::back_inserter(result));
+ return result;
+ }
+ List slice(qsizetype start, qsizetype end) const
+ {
+ const qsizetype size = m_list->size();
+ const qsizetype clampedStart = clamp(start, size);
+ const qsizetype clampedEnd = clamp(end, size, clampedStart);
+
+ List result;
+ std::copy(m_list->cbegin() + clampedStart, m_list->cbegin() + clampedEnd,
+ std::back_inserter(result));
+ return result;
+ }
+
+ qsizetype indexOf(const Value &value) const
+ {
+ const auto begin = m_list->cbegin();
+ const auto end = m_list->cend();
+ const auto it = std::find(begin, end, value);
+ if (it == end)
+ return -1;
+ const qsizetype result = it - begin;
+ Q_ASSERT(result >= 0);
+ return result;
+ }
+ qsizetype indexOf(const Value &value, qsizetype start) const
+ {
+ const auto begin = m_list->cbegin();
+ const auto end = m_list->cend();
+ const auto it = std::find(begin + clamp(start, m_list->size()), end, value);
+ if (it == end)
+ return -1;
+ const qsizetype result = it - begin;
+ Q_ASSERT(result >= 0);
+ return result;
+ }
+
+ qsizetype lastIndexOf(const Value &value) const
+ {
+ const auto begin = std::make_reverse_iterator(m_list->cend());
+ const auto end = std::make_reverse_iterator(m_list->cbegin());
+ const auto it = std::find(begin, end, value);
+ return (end - it) - 1;
+ }
+ qsizetype lastIndexOf(const Value &value, qsizetype start) const
+ {
+ const qsizetype size = m_list->size();
+ if (size == 0)
+ return -1;
+
+ // Construct a one-past-end iterator as input.
+ const qsizetype clampedStart = std::min(clamp(start, size), size - 1);
+ const auto begin = std::make_reverse_iterator(m_list->cbegin() + clampedStart + 1);
+
+ const auto end = std::make_reverse_iterator(m_list->cbegin());
+ const auto it = std::find(begin, end, value);
+ return (end - it) - 1;
+ }
+
+ QString toString() const { return join(); }
+
+private:
+ List *m_list = nullptr;
+ QJSEngine *m_engine = nullptr;
+};
+
+template<>
+struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClamp
+{
+ Q_DISABLE_COPY_MOVE(QJSList)
+
+ QJSList(QQmlListProperty<QObject> *list, QJSEngine *engine) : m_list(list), m_engine(engine) {}
+
+ QObject *at(qsizetype index) const
+ {
+ Q_ASSERT(index >= 0 && index < size());
+ return m_list->at(m_list, index);
+ }
+
+ qsizetype size() const
+ {
+ return m_list->count(m_list);
+ }
+
+ void resize(qsizetype size)
+ {
+ qsizetype current = m_list->count(m_list);
+ if (current < size && m_list->append) {
+ do {
+ m_list->append(m_list, nullptr);
+ } while (++current < size);
+ } else if (current > size && m_list->removeLast) {
+ do {
+ m_list->removeLast(m_list);
+ } while (--current > size);
+ }
+ }
+
+ bool includes(const QObject *value) const
+ {
+ if (!m_list->count || !m_list->at)
+ return false;
+
+ const qsizetype size = m_list->count(m_list);
+ for (qsizetype i = 0; i < size; ++i) {
+ if (m_list->at(m_list, i) == value)
+ return true;
+ }
+
+ return false;
+ }
+ bool includes(const QObject *value, qsizetype start) const
+ {
+ if (!m_list->count || !m_list->at)
+ return false;
+
+ const qsizetype size = m_list->count(m_list);
+ for (qsizetype i = clamp(start, size); i < size; ++i) {
+ if (m_list->at(m_list, i) == value)
+ return true;
+ }
+
+ return false;
+ }
+
+ QString join(const QString &separator = QStringLiteral(",")) const
+ {
+ if (!m_list->count || !m_list->at)
+ return QString();
+
+ QString result;
+ for (qsizetype i = 0, end = m_list->count(m_list); i < end; ++i) {
+ if (i != 0)
+ result += separator;
+ result += m_engine->coerceValue<QObject *, QString>(m_list->at(m_list, i));
+ }
+
+ return result;
+ }
+
+ QObjectList slice() const
+ {
+ return m_list->toList<QObjectList>();
+ }
+ QObjectList slice(qsizetype start) const
+ {
+ if (!m_list->count || !m_list->at)
+ return QObjectList();
+
+ const qsizetype size = m_list->count(m_list);
+ const qsizetype clampedStart = clamp(start, size);
+ QObjectList result;
+ result.reserve(size - clampedStart);
+ for (qsizetype i = clampedStart; i < size; ++i)
+ result.append(m_list->at(m_list, i));
+ return result;
+ }
+ QObjectList slice(qsizetype start, qsizetype end) const
+ {
+ if (!m_list->count || !m_list->at)
+ return QObjectList();
+
+ const qsizetype size = m_list->count(m_list);
+ const qsizetype clampedStart = clamp(start, size);
+ const qsizetype clampedEnd = clamp(end, size, clampedStart);
+ QObjectList result;
+ result.reserve(clampedEnd - clampedStart);
+ for (qsizetype i = clampedStart; i < clampedEnd; ++i)
+ result.append(m_list->at(m_list, i));
+ return result;
+ }
+
+ qsizetype indexOf(const QObject *value) const
+ {
+ if (!m_list->count || !m_list->at)
+ return -1;
+
+ const qsizetype end = m_list->count(m_list);
+ for (qsizetype i = 0; i < end; ++i) {
+ if (m_list->at(m_list, i) == value)
+ return i;
+ }
+ return -1;
+ }
+ qsizetype indexOf(const QObject *value, qsizetype start) const
+ {
+ if (!m_list->count || !m_list->at)
+ return -1;
+
+ const qsizetype size = m_list->count(m_list);
+ for (qsizetype i = clamp(start, size); i < size; ++i) {
+ if (m_list->at(m_list, i) == value)
+ return i;
+ }
+ return -1;
+ }
+
+ qsizetype lastIndexOf(const QObject *value) const
+ {
+ if (!m_list->count || !m_list->at)
+ return -1;
+
+ for (qsizetype i = m_list->count(m_list) - 1; i >= 0; --i) {
+ if (m_list->at(m_list, i) == value)
+ return i;
+ }
+ return -1;
+ }
+ qsizetype lastIndexOf(const QObject *value, qsizetype start) const
+ {
+ if (!m_list->count || !m_list->at)
+ return -1;
+
+ const qsizetype size = m_list->count(m_list);
+ if (size == 0)
+ return -1;
+
+ qsizetype clampedStart = std::min(clamp(start, size), size - 1);
+ for (qsizetype i = clampedStart; i >= 0; --i) {
+ if (m_list->at(m_list, i) == value)
+ return i;
+ }
+ return -1;
+ }
+
+ QString toString() const { return join(); }
+
+private:
+ QQmlListProperty<QObject> *m_list = nullptr;
+ QJSEngine *m_engine = nullptr;
+};
+
+struct QJSListForInIterator
+{
+public:
+ using Ptr = QJSListForInIterator *;
+ template<typename List, typename Value>
+ void init(const QJSList<List, Value> &list)
+ {
+ m_index = 0;
+ m_size = list.size();
+ }
+
+ bool hasNext() const { return m_index < m_size; }
+ qsizetype next() { return m_index++; }
+
+private:
+ qsizetype m_index;
+ qsizetype m_size;
+};
+
+// QJSListForInIterator must not require initialization so that we can jump over it with goto.
+static_assert(std::is_trivial_v<QJSListForInIterator>);
+
+struct QJSListForOfIterator
+{
+public:
+ using Ptr = QJSListForOfIterator *;
+ void init() { m_index = 0; }
+
+ template<typename List, typename Value>
+ bool hasNext(const QJSList<List, Value> &list) const { return m_index < list.size(); }
+
+ template<typename List, typename Value>
+ Value next(const QJSList<List, Value> &list) { return list.at(m_index++); }
+
+private:
+ qsizetype m_index;
+};
+
+// QJSListForOfIterator must not require initialization so that we can jump over it with goto.
+static_assert(std::is_trivial_v<QJSListForOfIterator>);
+
+QT_END_NAMESPACE
+
+#endif // QJSLIST_H
diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp
index eeadf875de..452f991a26 100644
--- a/src/qml/jsapi/qjsmanagedvalue.cpp
+++ b/src/qml/jsapi/qjsmanagedvalue.cpp
@@ -13,6 +13,7 @@
#include <QtQml/private/qv4urlobject_p.h>
#include <QtQml/private/qv4variantobject_p.h>
#include <QtQml/private/qv4qobjectwrapper_p.h>
+#include <QtQml/private/qv4qmetaobjectwrapper_p.h>
#include <QtQml/private/qv4regexpobject_p.h>
#include <QtQml/private/qv4dateobject_p.h>
#include <QtQml/private/qv4errorobject_p.h>
@@ -1086,8 +1087,11 @@ QStringList QJSManagedValue::jsMetaMembers() const
const int size = heapClass->size;
QStringList result;
result.reserve(size);
- for (int i = 0; i < size; ++i)
- result.append(heapClass->keyAt(i));
+ QV4::Scope scope(c->engine());
+ for (int i = 0; i < size; ++i) {
+ QV4::ScopedValue key(scope, heapClass->keyAt(i));
+ result.append(key->toQString());
+ }
return result;
}
diff --git a/src/qml/jsapi/qjsprimitivevalue.cpp b/src/qml/jsapi/qjsprimitivevalue.cpp
index 772ae49db8..4bd418e082 100644
--- a/src/qml/jsapi/qjsprimitivevalue.cpp
+++ b/src/qml/jsapi/qjsprimitivevalue.cpp
@@ -146,6 +146,17 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(QMetaType type)
+ \since 6.6
+ \internal
+
+ Creates a QJSPrimitiveValue of type \a type, and initializes with a
+ default-constructed value if \a type can be stored in QJSPrimtiveValue.
+ If \a type cannot be stored this results in a QJSPrimitiveValue of type
+ Undefined.
+*/
+
+/*!
\fn QJSPrimitiveValue::QJSPrimitiveValue(const QVariant &value)
Creates a QJSPrimitiveValue from the contents of \a value if those contents
@@ -273,6 +284,40 @@ QT_BEGIN_NAMESPACE
result.
*/
+/*!
+ \fn QMetaType QJSPrimitiveValue::metaType() const
+ \since 6.6
+
+ Returns the QMetaType of the value stored in the QJSPrimitiveValue.
+ */
+
+/*!
+ \fn const void *QJSPrimitiveValue::constData() const
+ \fn const void *QJSPrimitiveValue::data() const
+ \since 6.6
+
+ Returns a pointer to the contained value as a generic void* that cannot be
+ written to.
+ */
+
+/*!
+ \fn const void *QJSPrimitiveValue::data()
+ \since 6.6
+
+ Returns a pointer to the contained data as a generic void* that can be
+ written to.
+*/
+
+/*!
+ \fn template<QJSPrimitiveValue::Type type> QJSPrimitiveValue QJSPrimitiveValue::to() const
+ \since 6.6
+
+ Coerces the value to the specified \e type and returns the result as a new
+ QJSPrimitiveValue.
+
+ \sa toBoolean(), toInteger(), toDouble(), toString()
+*/
+
QString QJSPrimitiveValue::toString(double d)
{
QString result;
diff --git a/src/qml/jsapi/qjsprimitivevalue.h b/src/qml/jsapi/qjsprimitivevalue.h
index 49a00ed180..4ba3fd7dc3 100644
--- a/src/qml/jsapi/qjsprimitivevalue.h
+++ b/src/qml/jsapi/qjsprimitivevalue.h
@@ -163,11 +163,61 @@ public:
}
}
+ explicit QJSPrimitiveValue(QMetaType type) noexcept
+ {
+ switch (type.id()) {
+ case QMetaType::UnknownType:
+ d = QJSPrimitiveUndefined();
+ break;
+ case QMetaType::Nullptr:
+ d = QJSPrimitiveNull();
+ break;
+ case QMetaType::Bool:
+ d = false;
+ break;
+ case QMetaType::Int:
+ d = 0;
+ break;
+ case QMetaType::Double:
+ d = 0.0;
+ break;
+ case QMetaType::QString:
+ d = QString();
+ break;
+ default:
+ // Unsupported. Remains undefined.
+ break;
+ }
+ }
+
explicit QJSPrimitiveValue(const QVariant &variant) noexcept
: QJSPrimitiveValue(variant.metaType(), variant.data())
{
}
+ constexpr QMetaType metaType() const { return d.metaType(); }
+ constexpr void *data() { return d.data(); }
+ constexpr const void *data() const { return d.data(); }
+ constexpr const void *constData() const { return d.data(); }
+
+ template<Type type>
+ QJSPrimitiveValue to() const {
+ if constexpr (type == Undefined)
+ return QJSPrimitiveUndefined();
+ if constexpr (type == Null)
+ return QJSPrimitiveNull();
+ if constexpr (type == Boolean)
+ return toBoolean();
+ if constexpr (type == Integer)
+ return toInteger();
+ if constexpr (type == Double)
+ return toDouble();
+ if constexpr (type == String)
+ return toString();
+
+ Q_UNREACHABLE_RETURN(QJSPrimitiveUndefined());
+ }
+
constexpr bool toBoolean() const
{
switch (type()) {
@@ -180,10 +230,14 @@ public:
return !QJSNumberCoercion::equals(v, 0) && !std::isnan(v);
}
case String: return !asString().isEmpty();
- default: Q_UNREACHABLE();
}
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(false);
+ #else
return false;
+ #endif
}
constexpr int toInteger() const
@@ -195,10 +249,14 @@ public:
case Integer: return asInteger();
case Double: return QJSNumberCoercion::toInteger(asDouble());
case String: return fromString(asString()).toInteger();
- default: Q_UNREACHABLE();
}
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(0);
+ #else
return 0;
+ #endif
}
constexpr double toDouble() const
@@ -210,10 +268,14 @@ public:
case Integer: return asInteger();
case Double: return asDouble();
case String: return fromString(asString()).toDouble();
- default: Q_UNREACHABLE();
}
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN({});
+ #else
return {};
+ #endif
}
QString toString() const
@@ -749,7 +811,82 @@ private:
else if constexpr (std::is_same_v<T, QString>)
return getString();
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
Q_UNREACHABLE_RETURN(T());
+ #else
+ return T();
+ #endif
+ }
+
+ constexpr QMetaType metaType() const noexcept {
+ switch (m_type) {
+ case Undefined:
+ return QMetaType();
+ case Null:
+ return QMetaType::fromType<std::nullptr_t>();
+ case Boolean:
+ return QMetaType::fromType<bool>();
+ case Integer:
+ return QMetaType::fromType<int>();
+ case Double:
+ return QMetaType::fromType<double>();
+ case String:
+ return QMetaType::fromType<QString>();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(QMetaType());
+ #else
+ return QMetaType();
+ #endif
+ }
+
+ constexpr void *data() noexcept {
+ switch (m_type) {
+ case Undefined:
+ case Null:
+ return nullptr;
+ case Boolean:
+ return &m_bool;
+ case Integer:
+ return &m_int;
+ case Double:
+ return &m_double;
+ case String:
+ return &m_string;
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(nullptr);
+ #else
+ return nullptr;
+ #endif
+ }
+
+ constexpr const void *data() const noexcept {
+ switch (m_type) {
+ case Undefined:
+ case Null:
+ return nullptr;
+ case Boolean:
+ return &m_bool;
+ case Integer:
+ return &m_int;
+ case Double:
+ return &m_double;
+ case String:
+ return &m_string;
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(nullptr);
+ #else
+ return nullptr;
+ #endif
}
private:
@@ -770,10 +907,14 @@ private:
return true;
case String:
return false;
- default:
- Q_UNREACHABLE();
}
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(false);
+ #else
return false;
+ #endif
}
union {
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 0aaa471e23..f6b97262c3 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -19,7 +19,9 @@
#include <private/qv4mm_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4qmetaobjectwrapper_p.h>
#include <private/qv4urlobject_p.h>
+#include <private/qqmlbuiltins_p.h>
/*!
\since 5.0
@@ -163,10 +165,11 @@
/*!
\enum QJSValue::ObjectConversionBehavior
- This enum is used to specify how JavaScript objects without an equivalent
+ This enum is used to specify how JavaScript objects and symbols without an equivalent
native Qt type should be treated when converting to QVariant.
\value ConvertJSObjects A best-effort, possibly lossy, conversion is attempted.
+ Symbols are converted to QString.
\value RetainJSObjects The value is retained as QJSValue wrapped in QVariant.
*/
@@ -371,8 +374,11 @@ bool QJSValue::isError() const
}
/*!
- Returns true if this QJSValue is an object of the URL class;
+ Returns true if this QJSValue is an object of the URL JavaScript class;
otherwise returns false.
+
+ \note For a QJSValue that contains a QUrl, this function returns false.
+ However, \c{toVariant().value<QUrl>()} works in both cases.
*/
bool QJSValue::isUrl() const
{
@@ -446,16 +452,32 @@ bool QJSValue::isCallable() const
return QJSValuePrivate::asManagedType<FunctionObject>(this);
}
+#if QT_DEPRECATED_SINCE(6, 9)
/*!
+ \deprecated [6.9]
Returns true if this QJSValue is a variant value;
otherwise returns false.
+ \warning This function is likely to give unexpected results.
+ A variant value is only constructed by the QJSEngine in a very
+ limited number of cases. This used to be different before Qt
+ 5.14, where \l{QJSEngine::toScriptValue} would have created
+ them for more types instead of corresponding ECMAScript types.
+ You can get a valid \l QVariant via \l toVariant for many values
+ for which \c{isVariant} returns false.
+
\sa toVariant()
*/
bool QJSValue::isVariant() const
{
- return QJSValuePrivate::asManagedType<QV4::VariantObject>(this);
+ if (QJSValuePrivate::asManagedType<QV4::VariantObject>(this))
+ return true;
+ if (auto vt = QJSValuePrivate::asManagedType<QV4::QQmlValueTypeWrapper>(this))
+ if (vt->metaObject() == &QQmlVarForeign::staticMetaObject)
+ return true;
+ return false;
}
+#endif
/*!
Returns the string value of this QJSValue, as defined in
@@ -634,8 +656,11 @@ QVariant QJSValue::toVariant(QJSValue::ObjectConversionBehavior behavior) const
if (val.isString())
return QVariant(val.toQString());
if (val.as<QV4::Managed>()) {
- return QV4::ExecutionEngine::toVariant(
- val, /*typeHint*/ QMetaType{}, behavior == RetainJSObjects);
+ if (behavior == RetainJSObjects)
+ return QV4::ExecutionEngine::toVariant(
+ val, /*typeHint*/ QMetaType{}, /*createJSValueForObjectsAndSymbols=*/ true);
+ else
+ return QV4::ExecutionEngine::toVariantLossy(val);
}
Q_ASSERT(false);
diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h
index d847048efc..065f73f666 100644
--- a/src/qml/jsapi/qjsvalue.h
+++ b/src/qml/jsapi/qjsvalue.h
@@ -80,7 +80,10 @@ public:
bool isNull() const;
bool isString() const;
bool isUndefined() const;
+#if QT_DEPRECATED_SINCE(6, 9)
+ QT_DEPRECATED_VERSION_X_6_9("This might return unexpected results; consult documentation for more information")
bool isVariant() const;
+#endif
bool isQObject() const;
bool isQMetaObject() const;
bool isObject() const;
diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h
index b95e76e76e..4624652c93 100644
--- a/src/qml/jsapi/qjsvalue_p.h
+++ b/src/qml/jsapi/qjsvalue_p.h
@@ -150,9 +150,17 @@ public:
case QV4::StaticValue::Integer_Type:
return encode(qv4Value.integerValue());
case QV4::StaticValue::Managed_Type: {
- QV4::Value *m = qv4Value.as<QV4::Managed>()->engine()
- ->memoryManager->m_persistentValues->allocate();
+ auto managed = qv4Value.as<QV4::Managed>();
+ auto engine = managed->engine();
+ auto mm = engine->memoryManager;
+ QV4::Value *m = mm->m_persistentValues->allocate();
Q_ASSERT(m);
+ // we create a new strong reference to the heap managed object
+ // to avoid having to rescan the persistent values, we mark it here
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack){
+ if constexpr (QV4::WriteBarrier::isInsertionBarrier)
+ managed->heapObject()->mark(stack);
+ });
*m = qv4Value;
return encodePointer(m, Kind::QV4ValuePtr);
}
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 52731ca221..74b79cb400 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -1,14 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <qv4argumentsobject_p.h>
-#include <qv4arrayobject_p.h>
-#include <qv4scopedvalue_p.h>
-#include <qv4string_p.h>
-#include <qv4function_p.h>
-#include <qv4jscall_p.h>
-#include <qv4symbol_p.h>
+
+#include "qv4argumentsobject_p.h"
#include <private/qv4alloca_p.h>
+#include <private/qv4arrayobject_p.h>
+#include <private/qv4function_p.h>
+#include <private/qv4jscall_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qv4stackframe_p.h>
+#include <private/qv4string_p.h>
+#include <private/qv4symbol_p.h>
using namespace QV4;
diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h
index b955618cbe..aafa3c6335 100644
--- a/src/qml/jsruntime/qv4arraybuffer_p.h
+++ b/src/qml/jsruntime/qv4arraybuffer_p.h
@@ -32,7 +32,7 @@ struct ArrayBufferCtor : SharedArrayBufferCtor {
void init(QV4::ExecutionContext *scope);
};
-struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object {
+struct Q_QML_EXPORT SharedArrayBuffer : Object {
void init(size_t length);
void init(const QByteArray& array);
void destroy();
@@ -68,7 +68,7 @@ private:
bool isShared;
};
-struct Q_QML_PRIVATE_EXPORT ArrayBuffer : SharedArrayBuffer {
+struct Q_QML_EXPORT ArrayBuffer : SharedArrayBuffer {
void init(size_t length) {
SharedArrayBuffer::init(length);
setSharedArrayBuffer(false);
@@ -98,7 +98,7 @@ struct ArrayBufferCtor : SharedArrayBufferCtor
static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
-struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object
+struct Q_QML_EXPORT SharedArrayBuffer : Object
{
V4_OBJECT2(SharedArrayBuffer, Object)
V4_NEEDS_DESTROY
@@ -115,7 +115,7 @@ struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object
bool isSharedArrayBuffer() const { return d()->isSharedArrayBuffer(); }
};
-struct Q_QML_PRIVATE_EXPORT ArrayBuffer : SharedArrayBuffer
+struct Q_QML_EXPORT ArrayBuffer : SharedArrayBuffer
{
V4_OBJECT2(ArrayBuffer, SharedArrayBuffer)
V4_NEEDS_DESTROY
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 6e01c50048..e1da807c21 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -711,8 +711,36 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
ArrayElementLessThan lessThan(engine, comparefn);
- Value *begin = thisObject->arrayData()->values.values;
- sortHelper(begin, begin + len, lessThan);
+ const auto thisArrayData = thisObject->arrayData();
+ uint startIndex = thisArrayData->mappedIndex(0);
+ uint endIndex = thisArrayData->mappedIndex(len - 1) + 1;
+ if (startIndex < endIndex) {
+ // Values are contiguous. Sort right away.
+ sortHelper(
+ thisArrayData->values.values + startIndex,
+ thisArrayData->values.values + endIndex,
+ lessThan);
+ } else {
+ // Values wrap around the end of the allocation. Close the gap to form a contiguous array.
+ // We're going to sort anyway. So we don't need to care about order.
+
+ // ArrayElementLessThan sorts empty and undefined to the end of the array anyway, but we
+ // probably shouldn't rely on the unused slots to be actually undefined or empty.
+
+ const uint gap = startIndex - endIndex;
+ const uint allocEnd = thisArrayData->values.alloc - 1;
+ for (uint i = 0; i < gap; ++i) {
+ const uint from = allocEnd - i;
+ const uint to = endIndex + i;
+ if (from < startIndex)
+ break;
+
+ std::swap(thisArrayData->values.values[from], thisArrayData->values.values[to]);
+ }
+
+ thisArrayData->offset = 0;
+ sortHelper(thisArrayData->values.values, thisArrayData->values.values + len, lessThan);
+ }
#ifdef CHECK_SPARSE_ARRAYS
thisObject->initSparseArray();
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index 05e73d0295..a32017210a 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -194,9 +194,8 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
// sets them into the created array.
forever {
if (k > (static_cast<qint64>(1) << 53) - 1) {
- ScopedValue falsey(scope, Encode(false));
ScopedValue error(scope, scope.engine->throwTypeError());
- return Runtime::IteratorClose::call(scope.engine, iterator, falsey);
+ return Runtime::IteratorClose::call(scope.engine, iterator);
}
// Retrieve the next value. If the iteration ends, we're done here.
@@ -218,7 +217,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
mapArguments[1] = Value::fromDouble(k);
mappedValue = mapfn->call(thisArg, mapArguments, 2);
if (scope.hasException())
- return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false));
+ return Runtime::IteratorClose::call(scope.engine, iterator);
} else {
mappedValue = *nextValue;
}
@@ -230,10 +229,8 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
}
- if (scope.hasException()) {
- ScopedValue falsey(scope, Encode(false));
- return Runtime::IteratorClose::call(scope.engine, iterator, falsey);
- }
+ if (scope.hasException())
+ return Runtime::IteratorClose::call(scope.engine, iterator);
k++;
}
diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp
index 0479b8b8f1..e9915c7d26 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp
@@ -4,7 +4,6 @@
#include "qv4compilationunitmapper_p.h"
#include <private/qv4compileddata_p.h>
-#include <private/qv4executablecompilationunit_p.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qmutex.h>
@@ -29,23 +28,24 @@ public:
s_staticUnits.insert(file, staticUnit);
}
+ void remove(const QString &file)
+ {
+ s_staticUnits.remove(file);
+ }
+
private:
QMutexLocker<QMutex> m_lock;
static QMutex s_mutex;
- // We can copy the mappers around because they're all static, that is the dtors are noops.
+ // We can copy the mappers around because they're all static.
+ // We never unmap the files.
static QHash<QString, CompilationUnitMapper> s_staticUnits;
};
QHash<QString, CompilationUnitMapper> StaticUnitCache::s_staticUnits;
QMutex StaticUnitCache::s_mutex;
-CompilationUnitMapper::~CompilationUnitMapper()
-{
- close();
-}
-
CompiledData::Unit *CompilationUnitMapper::get(
const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString)
{
@@ -54,7 +54,7 @@ CompiledData::Unit *CompilationUnitMapper::get(
CompilationUnitMapper mapper = cache.get(cacheFilePath);
if (mapper.dataPtr) {
auto *unit = reinterpret_cast<CompiledData::Unit *>(mapper.dataPtr);
- if (ExecutableCompilationUnit::verifyHeader(unit, sourceTimeStamp, errorString)) {
+ if (unit->verifyHeader(sourceTimeStamp, errorString)) {
*this = mapper;
return unit;
}
@@ -63,10 +63,19 @@ CompiledData::Unit *CompilationUnitMapper::get(
}
CompiledData::Unit *data = open(cacheFilePath, sourceTimeStamp, errorString);
- if (data && (data->flags & CompiledData::Unit::StaticData))
+ if (data && (data->flags & CompiledData::Unit::StaticData)) {
cache.set(cacheFilePath, *this);
+ return data;
+ } else {
+ close();
+ return nullptr;
+ }
+}
- return data;
+void CompilationUnitMapper::invalidate(const QString &cacheFilePath)
+{
+ StaticUnitCache cache;
+ cache.remove(cacheFilePath);
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h
index 0d3ec4eeee..c214141804 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper_p.h
+++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h
@@ -29,10 +29,9 @@ struct Unit;
class CompilationUnitMapper
{
public:
- ~CompilationUnitMapper();
-
CompiledData::Unit *get(
const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
+ static void invalidate(const QString &cacheFilePath);
private:
CompiledData::Unit *open(
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp
index d7364f8706..204e222121 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp
@@ -3,13 +3,14 @@
#include "qv4compilationunitmapper_p.h"
-#include <sys/mman.h>
-#include <functional>
#include <private/qcore_unix_p.h>
-#include <QScopeGuard>
-#include <QDateTime>
+#include <private/qv4compileddata_p.h>
-#include "qv4executablecompilationunit_p.h"
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qdatetime.h>
+
+#include <functional>
+#include <sys/mman.h>
QT_BEGIN_NAMESPACE
@@ -37,12 +38,22 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- if (!ExecutableCompilationUnit::verifyHeader(&header, sourceTimeStamp, errorString))
+ if (!header.verifyHeader(sourceTimeStamp, errorString))
return nullptr;
// Data structure and qt version matched, so now we can access the rest of the file safely.
length = static_cast<size_t>(lseek(fd, 0, SEEK_END));
+ /* Error out early on file corruption. We assume we can read header.unitSize bytes
+ later (even before verifying the checksum), potentially causing out-of-bound
+ reads
+ Also, no need to wait until checksum verification if we know beforehand
+ that the cached unit is bogus
+ */
+ if (length != header.unitSize) {
+ *errorString = QStringLiteral("Potential file corruption, file too small");
+ return nullptr;
+ }
void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0);
if (ptr == MAP_FAILED) {
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_win.cpp b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp
index 2ea54ce286..73096207b4 100644
--- a/src/qml/jsruntime/qv4compilationunitmapper_win.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp
@@ -3,10 +3,12 @@
#include "qv4compilationunitmapper_p.h"
-#include "qv4executablecompilationunit_p.h"
-#include <QScopeGuard>
-#include <QFileInfo>
-#include <QDateTime>
+#include <private/qv4compileddata_p.h>
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qscopeguard.h>
+
#include <qt_windows.h>
QT_BEGIN_NAMESPACE
@@ -45,11 +47,28 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- if (!ExecutableCompilationUnit::verifyHeader(&header, sourceTimeStamp, errorString))
+ if (!header.verifyHeader(sourceTimeStamp, errorString))
return nullptr;
// Data structure and qt version matched, so now we can access the rest of the file safely.
+ /* Error out early on file corruption. We assume we can read header.unitSize bytes
+ later (even before verifying the checksum), potentially causing out-of-bound
+ reads
+ Also, no need to wait until checksum verification if we know beforehand
+ that the cached unit is bogus
+ */
+ LARGE_INTEGER fileSize;
+ if (!GetFileSizeEx(handle, &fileSize)) {
+ *errorString = QStringLiteral("Could not determine file size");
+ return nullptr;
+ }
+ if (header.unitSize != fileSize.QuadPart) {
+ *errorString = QStringLiteral("Potential file corruption, file too small");
+ return nullptr;
+ }
+
+
HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0);
if (!fileMappingHandle) {
*errorString = qt_error_string(GetLastError());
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index b702dbe2db..3ae3e5d24c 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -299,9 +299,14 @@ ReturnedValue ExecutionContext::getProperty(String *name)
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- uint index = c->internalClass->indexOfValueOrGetter(id);
- if (index < UINT_MAX)
+ const uint index = c->internalClass->indexOfValueOrGetter(id);
+ if (index < c->locals.alloc)
return c->locals[index].asReturnedValue();
+
+ // TODO: We should look up the module imports here, but those are part of the CU:
+ // imports[index - c->locals.size];
+ // See QTBUG-118478
+
Q_FALLTHROUGH();
}
case Heap::ExecutionContext::Type_WithContext:
@@ -349,9 +354,14 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- uint index = c->internalClass->indexOfValueOrGetter(id);
- if (index < UINT_MAX)
+ const uint index = c->internalClass->indexOfValueOrGetter(id);
+ if (index < c->locals.alloc)
return c->locals[index].asReturnedValue();
+
+ // TODO: We should look up the module imports here, but those are part of the CU:
+ // imports[index - c->locals.size];
+ // See QTBUG-118478
+
Q_FALLTHROUGH();
}
case Heap::ExecutionContext::Type_GlobalContext: {
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index ed64493d9a..6b64be3abb 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -315,14 +315,14 @@ static inline double ParseString(const QString &s, double localTZA)
First, try the format defined in ECMA 262's "Date Time String Format";
only if that fails, fall back to QDateTime for parsing
- The defined string format is YYYY-MM-DDTHH:mm:ss.sssZ; the time (T and all
- after it) may be omitted; in each part, the second and later components
- are optional; and there's an extended syntax for negative and large
- positive years: +/-YYYYYY; the leading sign, even when +, isn't optional.
- If month or day is omitted, it is 01; if minute or second is omitted, it's
- 00; if milliseconds are omitted, they're 000.
-
- When the time zone offset is absent, date-only forms are interpreted as
+ The defined string format is yyyy-MM-ddTHH:mm:ss.zzzt; the time (T and all
+ after it) may be omitted. In each part, the second and later components
+ are optional. There's an extended syntax for negative and large positive
+ years: ±yyyyyy; the leading sign, even when +, isn't optional. If month
+ (MM) or day (dd) is omitted, it is 01; if minute (mm) or second (ss) is
+ omitted, it's 00; if milliseconds (zzz) are omitted, they're 000.
+
+ When the time zone offset (t) is absent, date-only forms are interpreted as
indicating a UTC time and date-time forms are interpreted in local time.
*/
@@ -719,11 +719,33 @@ QString DateObject::dateTimeToString(const QDateTime &dateTime, ExecutionEngine
return ToString(TimeClip(dateTime.toMSecsSinceEpoch()), engine->localTZA);
}
+double DateObject::dateTimeToNumber(const QDateTime &dateTime)
+{
+ if (!dateTime.isValid())
+ return qQNaN();
+ return TimeClip(dateTime.toMSecsSinceEpoch());
+}
+
QDateTime DateObject::stringToDateTime(const QString &string, ExecutionEngine *engine)
{
return ToDateTime(ParseString(string, engine->localTZA), QTimeZone::LocalTime);
}
+QDateTime DateObject::timestampToDateTime(double timestamp, QTimeZone zone)
+{
+ return ToDateTime(timestamp, zone);
+}
+
+double DateObject::componentsToTimestamp(
+ double year, double month, double day, double hours,
+ double mins, double secs, double ms, ExecutionEngine *v4)
+{
+ if (year >= 0 && year <= 99)
+ year += 1900;
+ const double t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms));
+ return UTC(t, v4->localTZA);
+}
+
QDate DateObject::dateTimeToDate(const QDateTime &dateTime)
{
// If the Date object was parse()d from a string with no time part
@@ -773,17 +795,14 @@ ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, con
}
else { // d.argc > 1
- double year = argv[0].toNumber();
- double month = argv[1].toNumber();
- double day = argc >= 3 ? argv[2].toNumber() : 1;
- double hours = argc >= 4 ? argv[3].toNumber() : 0;
- double mins = argc >= 5 ? argv[4].toNumber() : 0;
- double secs = argc >= 6 ? argv[5].toNumber() : 0;
- double ms = argc >= 7 ? argv[6].toNumber() : 0;
- if (year >= 0 && year <= 99)
- year += 1900;
- t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms));
- t = UTC(t, v4->localTZA);
+ const double year = argv[0].toNumber();
+ const double month = argv[1].toNumber();
+ const double day = argc >= 3 ? argv[2].toNumber() : 1;
+ const double hours = argc >= 4 ? argv[3].toNumber() : 0;
+ const double mins = argc >= 5 ? argv[4].toNumber() : 0;
+ const double secs = argc >= 6 ? argv[5].toNumber() : 0;
+ const double ms = argc >= 7 ? argv[6].toNumber() : 0;
+ t = DateObject::componentsToTimestamp(year, month, day, hours, mins, secs, ms, v4);
}
ReturnedValue o = Encode(v4->newDateObject(t));
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h
index ad5b51f063..7debcff4e9 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -202,12 +202,18 @@ struct DateObject: ReferenceObject {
void setDate(double date) { d()->setDate(date); }
double date() const { return d()->date(); }
- Q_QML_PRIVATE_EXPORT QDateTime toQDateTime() const;
+ Q_QML_EXPORT QDateTime toQDateTime() const;
QString toString() const;
static QString dateTimeToString(const QDateTime &dateTime, ExecutionEngine *engine);
+ static double dateTimeToNumber(const QDateTime &dateTime);
static QDate dateTimeToDate(const QDateTime &dateTime);
static QDateTime stringToDateTime(const QString &string, ExecutionEngine *engine);
+ static QDateTime timestampToDateTime(double timestamp, QTimeZone zone = QTimeZone::LocalTime);
+ static double componentsToTimestamp(
+ double year, double month, double day,
+ double hours, double mins, double secs, double ms,
+ ExecutionEngine *v4);
};
template<>
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 3d14d504e2..a2a2e99a01 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -61,6 +61,7 @@
#include "qv4variantobject_p.h"
#include "qv4sequenceobject_p.h"
#include "qv4qobjectwrapper_p.h"
+#include "qv4qmetaobjectwrapper_p.h"
#include "qv4memberdata_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4dataview_p.h"
@@ -92,8 +93,6 @@
#include <valgrind/memcheck.h>
#endif
-Q_DECLARE_METATYPE(QList<int>)
-
QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE);
@@ -372,6 +371,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
const size_t guardPages = 2 * WTF::pageSize();
memoryManager = new QV4::MemoryManager(this);
+ // we don't want to run the gc while the initial setup is not done; not even in aggressive mode
+ GCCriticalSection gcCriticalSection(this);
// reserve space for the JS stack
// we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues
// allocated outside of JIT'ed methods.
@@ -850,7 +851,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
ExecutionEngine::~ExecutionEngine()
{
- modules.clear();
for (auto val : nativeModules) {
PersistentValueStorage::free(val);
}
@@ -861,8 +861,12 @@ ExecutionEngine::~ExecutionEngine()
delete identifierTable;
delete memoryManager;
- while (!compilationUnits.isEmpty())
- (*compilationUnits.begin())->unlink();
+ for (const auto &cu : std::as_const(m_compilationUnits)) {
+ Q_ASSERT(cu->engine == this);
+ cu->clear();
+ cu->engine = nullptr;
+ }
+ m_compilationUnits.clear();
delete bumperPointerAllocator;
delete regExpCache;
@@ -896,7 +900,7 @@ void ExecutionEngine::setProfiler(Profiling::Profiler *profiler)
void ExecutionEngine::initRootContext()
{
Scope scope(this);
- Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>(sizeof(ExecutionContext::Data)));
+ Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>());
r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
r->d()->activation.set(this, globalObject->d());
jsObjects[RootContext] = r;
@@ -1227,8 +1231,6 @@ QQmlRefPointer<QQmlContextData> ExecutionEngine::callingQmlContext() const
StackTrace ExecutionEngine::stackTrace(int frameLimit) const
{
- Scope scope(const_cast<ExecutionEngine *>(this));
- ScopedString name(scope);
StackTrace stack;
CppStackFrame *f = currentStackFrame;
@@ -1236,8 +1238,8 @@ StackTrace ExecutionEngine::stackTrace(int frameLimit) const
QV4::StackFrame frame;
frame.source = f->source();
frame.function = f->function();
- frame.line = qAbs(f->lineNumber());
- frame.column = -1;
+ frame.line = f->lineNumber();
+
stack.append(frame);
if (f->isJSTypesFrame()) {
if (static_cast<JSTypesStackFrame *>(f)->isTailCalling()) {
@@ -1274,7 +1276,7 @@ static inline char *v4StackTrace(const ExecutionContext *context)
const QString fileName = url.isLocalFile() ? url.toLocalFile() : url.toString();
str << "frame={level=\"" << i << "\",func=\"" << stackTrace.at(i).function
<< "\",file=\"" << fileName << "\",fullname=\"" << fileName
- << "\",line=\"" << stackTrace.at(i).line << "\",language=\"js\"}";
+ << "\",line=\"" << qAbs(stackTrace.at(i).line) << "\",language=\"js\"}";
}
}
str << ']';
@@ -1326,7 +1328,7 @@ void ExecutionEngine::markObjects(MarkStack *markStack)
identifierTable->markObjects(markStack);
- for (auto compilationUnit: compilationUnits)
+ for (const auto &compilationUnit : std::as_const(m_compilationUnits))
compilationUnit->markObjects(markStack);
}
@@ -1469,7 +1471,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
if (!trace.isEmpty()) {
QV4::StackFrame frame = trace.constFirst();
error.setUrl(QUrl(frame.source));
- error.setLine(frame.line);
+ error.setLine(qAbs(frame.line));
error.setColumn(frame.column);
}
QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception);
@@ -1480,20 +1482,21 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
// Variant conversion code
typedef QSet<QV4::Heap::Object *> V4ObjectSet;
-static QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects);
+enum class JSToQVariantConversionBehavior {Never, Safish, Aggressive };
+static QVariant toVariant(
+ const QV4::Value &value, QMetaType typeHint, JSToQVariantConversionBehavior conversionBehavior,
+ V4ObjectSet *visitedObjects);
static QObject *qtObjectFromJS(const QV4::Value &value);
-static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr);
+static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr,
+ JSToQVariantConversionBehavior behavior = JSToQVariantConversionBehavior::Safish);
static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result);
-static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst);
-static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst);
static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap);
static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value)
{
return v4->metaTypeToJS(value.metaType(), value.constData());
}
-static QVariant toVariant(
- const QV4::Value &value, QMetaType metaType, bool createJSValueForObjects,
+static QVariant toVariant(const QV4::Value &value, QMetaType metaType, JSToQVariantConversionBehavior conversionBehavior,
V4ObjectSet *visitedObjects)
{
Q_ASSERT (!value.isEmpty());
@@ -1504,6 +1507,12 @@ static QVariant toVariant(
if (metaType == QMetaType::fromType<bool>())
return QVariant(value.toBoolean());
+ if (metaType == QMetaType::fromType<double>())
+ return QVariant(value.toNumber());
+
+ if (metaType == QMetaType::fromType<float>())
+ return QVariant(float(value.toNumber()));
+
if (metaType == QMetaType::fromType<QJsonValue>())
return QVariant::fromValue(QV4::JsonObject::toJsonValue(value));
@@ -1565,9 +1574,9 @@ static QVariant toVariant(
QV4::ScopedValue arrayValue(scope);
for (qint64 i = 0; i < length; ++i) {
arrayValue = a->get(i);
- QVariant asVariant(valueMetaType);
- if (QQmlValueTypeProvider::createValueType(
- arrayValue, valueMetaType, asVariant.data())) {
+ QVariant asVariant = QQmlValueTypeProvider::createValueType(
+ arrayValue, valueMetaType);
+ if (asVariant.isValid()) {
retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData());
continue;
}
@@ -1584,21 +1593,21 @@ static QVariant toVariant(
}
}
- asVariant = toVariant(arrayValue, valueMetaType, false, visitedObjects);
+ asVariant = toVariant(arrayValue, valueMetaType, JSToQVariantConversionBehavior::Never, visitedObjects);
if (valueMetaType == QMetaType::fromType<QVariant>()) {
retnAsIterable.metaContainer().addValue(retn.data(), &asVariant);
} else {
auto originalType = asVariant.metaType();
bool couldConvert = asVariant.convert(valueMetaType);
- if (!couldConvert) {
+ if (!couldConvert && originalType.isValid()) {
+ // If the original type was void, we're converting a "hole" in a sparse
+ // array. There is no point in warning about that.
qWarning().noquote()
<< QLatin1String("Could not convert array value "
"at position %1 from %2 to %3")
.arg(QString::number(i),
QString::fromUtf8(originalType.name()),
QString::fromUtf8(valueMetaType.name()));
- // create default constructed value
- asVariant = QVariant(valueMetaType, nullptr);
}
retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData());
}
@@ -1625,11 +1634,10 @@ static QVariant toVariant(
return str.at(0);
return str;
}
-#if QT_CONFIG(qml_locale)
- if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>())
- return *ld->d()->locale;
-#endif
if (const QV4::DateObject *d = value.as<DateObject>()) {
+ // NOTE: since we convert QTime to JS Date,
+ // round trip will change the variant type (to QDateTime)!
+
if (metaType == QMetaType::fromType<QDate>())
return DateObject::dateTimeToDate(d->toQDateTime());
@@ -1645,7 +1653,11 @@ static QVariant toVariant(
return d->toQUrl();
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
return d->asByteArray();
- // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
+ if (const Symbol *symbol = value.as<Symbol>()) {
+ return conversionBehavior == JSToQVariantConversionBehavior::Never
+ ? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue()))
+ : symbol->descriptiveString();
+ }
const QV4::Object *object = value.as<QV4::Object>();
Q_ASSERT(object);
@@ -1658,24 +1670,53 @@ static QVariant toVariant(
#endif
if (metaType.isValid() && !(metaType.flags() & QMetaType::PointerToQObject)) {
- QVariant result(metaType);
- if (QQmlValueTypeProvider::createValueType(value, metaType, result.data()))
+ const QVariant result = QQmlValueTypeProvider::createValueType(value, metaType);
+ if (result.isValid())
return result;
}
- if (createJSValueForObjects)
+ if (conversionBehavior == JSToQVariantConversionBehavior::Never)
return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
- return objectToVariant(o, visitedObjects);
+ return objectToVariant(o, visitedObjects, conversionBehavior);
}
+QVariant ExecutionEngine::toVariantLossy(const Value &value)
+{
+ return ::toVariant(value, QMetaType(), JSToQVariantConversionBehavior::Aggressive, nullptr);
+}
-QVariant ExecutionEngine::toVariant(const Value &value, QMetaType typeHint, bool createJSValueForObjects)
+QVariant ExecutionEngine::toVariant(
+ const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols)
{
- return ::toVariant(value, typeHint, createJSValueForObjects, nullptr);
+ auto behavior = createJSValueForObjectsAndSymbols ? JSToQVariantConversionBehavior::Never
+ : JSToQVariantConversionBehavior::Safish;
+ return ::toVariant(value, typeHint, behavior, nullptr);
+}
+
+static QVariantMap objectToVariantMap(const QV4::Object *o, V4ObjectSet *visitedObjects,
+ JSToQVariantConversionBehavior conversionBehvior)
+{
+ QVariantMap map;
+ QV4::Scope scope(o->engine());
+ QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
+ QV4::ScopedValue name(scope);
+ QV4::ScopedValue val(scope);
+ while (1) {
+ name = it.nextPropertyNameAsString(val);
+ if (name->isNull())
+ break;
+
+ QString key = name->toQStringNoThrow();
+ map.insert(key, ::toVariant(
+ val, /*type hint*/ QMetaType {},
+ conversionBehvior, visitedObjects));
+ }
+ return map;
}
-static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects)
+static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects,
+ JSToQVariantConversionBehavior conversionBehvior)
{
Q_ASSERT(o);
@@ -1703,31 +1744,23 @@ static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObject
int length = a->getLength();
for (int ii = 0; ii < length; ++ii) {
v = a->get(ii);
- list << ::toVariant(v, QMetaType {}, /*createJSValueForObjects*/false, visitedObjects);
+ list << ::toVariant(v, QMetaType {}, conversionBehvior,
+ visitedObjects);
}
result = list;
- } else if (const FunctionObject *f = o->as<FunctionObject>()) {
- // If it's a FunctionObject, we can only save it as QJSValue.
- result = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(f->asReturnedValue()));
+ } else if (o->getPrototypeOf() == o->engine()->objectPrototype()->d()
+ || (conversionBehvior == JSToQVariantConversionBehavior::Aggressive &&
+ !o->as<QV4::FunctionObject>())) {
+ /* FunctionObject is excluded for historical reasons, even though
+ objects with a custom prototype risk losing information
+ But the Aggressive path is used only in QJSValue::toVariant
+ which is documented to be lossy
+ */
+ result = objectToVariantMap(o, visitedObjects, conversionBehvior);
} else {
- QVariantMap map;
- QV4::Scope scope(o->engine());
- QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
- QV4::ScopedValue name(scope);
- QV4::ScopedValue val(scope);
- while (1) {
- name = it.nextPropertyNameAsString(val);
- if (name->isNull())
- break;
-
- QString key = name->toQStringNoThrow();
- map.insert(key, ::toVariant(
- val, /*type hint*/ QMetaType {},
- /*createJSValueForObjects*/false, visitedObjects));
- }
-
- result = map;
+ // If it's not a plain object, we can only save it as QJSValue.
+ result = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
}
visitedObjects->remove(o->d());
@@ -1743,6 +1776,18 @@ QV4::ReturnedValue ExecutionEngine::fromData(
QMetaType metaType, const void *ptr,
QV4::Heap::Object *container, int property, uint flags)
{
+ const auto createSequence = [&](const QMetaSequence metaSequence) {
+ QV4::Scope scope(this);
+ QV4::Scoped<Sequence> sequence(scope);
+ if (container) {
+ return QV4::SequencePrototype::newSequence(
+ this, metaType, metaSequence, ptr,
+ container, property, Heap::ReferenceObject::Flags(flags));
+ } else {
+ return QV4::SequencePrototype::fromData(this, metaType, metaSequence, ptr);
+ }
+ };
+
const int type = metaType.id();
if (type < QMetaType::User) {
switch (QMetaType::Type(type)) {
@@ -1758,6 +1803,10 @@ QV4::ReturnedValue ExecutionEngine::fromData(
return QV4::Encode(*reinterpret_cast<const int*>(ptr));
case QMetaType::UInt:
return QV4::Encode(*reinterpret_cast<const uint*>(ptr));
+ case QMetaType::Long:
+ return QV4::Encode((double)*reinterpret_cast<const long *>(ptr));
+ case QMetaType::ULong:
+ return QV4::Encode((double)*reinterpret_cast<const ulong *>(ptr));
case QMetaType::LongLong:
return QV4::Encode((double)*reinterpret_cast<const qlonglong*>(ptr));
case QMetaType::ULongLong:
@@ -1803,15 +1852,9 @@ QV4::ReturnedValue ExecutionEngine::fromData(
case QMetaType::QObjectStar:
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
case QMetaType::QStringList:
- {
- QV4::Scope scope(this);
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromData(this, metaType, ptr));
- if (!retn->isUndefined())
- return retn->asReturnedValue();
- return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr)));
- }
+ return createSequence(QMetaSequence::fromContainer<QStringList>());
case QMetaType::QVariantList:
- return variantListToJS(this, *reinterpret_cast<const QVariantList *>(ptr));
+ return createSequence(QMetaSequence::fromContainer<QVariantList>());
case QMetaType::QVariantMap:
return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(ptr));
case QMetaType::QJsonValue:
@@ -1820,10 +1863,6 @@ QV4::ReturnedValue ExecutionEngine::fromData(
return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr));
case QMetaType::QJsonArray:
return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr));
-#if QT_CONFIG(qml_locale)
- case QMetaType::QLocale:
- return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr));
-#endif
case QMetaType::QPixmap:
case QMetaType::QImage:
// Scarce value types
@@ -1831,106 +1870,95 @@ QV4::ReturnedValue ExecutionEngine::fromData(
default:
break;
}
+ }
- if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) {
- if (container) {
- return QV4::QQmlValueTypeWrapper::create(
- this, ptr, vtmo, metaType,
- container, property, Heap::ReferenceObject::Flags(flags));
- } else {
- return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType);
- }
- }
+ if (metaType.flags() & QMetaType::IsEnumeration)
+ return fromData(metaType.underlyingType(), ptr, container, property, flags);
- } else {
- QV4::Scope scope(this);
- if (metaType == QMetaType::fromType<QQmlListReference>()) {
- typedef QQmlListReferencePrivate QDLRP;
- QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr));
- if (p->object)
- return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType);
- else
- return QV4::Encode::null();
- } else if (auto flags = metaType.flags(); flags & QMetaType::IsQmlList) {
- // casting to QQmlListProperty<QObject> is slightly nasty, but it's the
- // same QQmlListReference does.
- const auto *p = static_cast<const QQmlListProperty<QObject> *>(ptr);
- if (p->object)
- return QV4::QmlListWrapper::create(scope.engine, *p, metaType);
- else
- return QV4::Encode::null();
- } else if (metaType == QMetaType::fromType<QJSValue>()) {
- return QJSValuePrivate::convertToReturnedValue(
- this, *reinterpret_cast<const QJSValue *>(ptr));
- } else if (metaType == QMetaType::fromType<QList<QObject *> >()) {
- // XXX Can this be made more by using Array as a prototype and implementing
- // directly against QList<QObject*>?
- const QList<QObject *> &list = *(const QList<QObject *>*)ptr;
- QV4::ScopedArrayObject a(scope, newArrayObject());
- a->arrayReserve(list.size());
- QV4::ScopedValue v(scope);
- for (int ii = 0; ii < list.size(); ++ii)
- a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii))));
- a->setArrayLengthUnchecked(list.size());
- return a.asReturnedValue();
- } else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) {
- if (flags.testFlag(QMetaType::IsConst))
- return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr));
- else
- return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
- } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) {
- const QJSPrimitiveValue *primitive = static_cast<const QJSPrimitiveValue *>(ptr);
- switch (primitive->type()) {
- case QJSPrimitiveValue::Boolean:
- return Encode(primitive->asBoolean());
- case QJSPrimitiveValue::Integer:
- return Encode(primitive->asInteger());
- case QJSPrimitiveValue::String:
- return newString(primitive->asString())->asReturnedValue();
- case QJSPrimitiveValue::Undefined:
- return Encode::undefined();
- case QJSPrimitiveValue::Null:
- return Encode::null();
- case QJSPrimitiveValue::Double:
- return Encode(primitive->asDouble());
- }
+ QV4::Scope scope(this);
+ if (metaType == QMetaType::fromType<QQmlListReference>()) {
+ typedef QQmlListReferencePrivate QDLRP;
+ QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr));
+ if (p->object)
+ return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType);
+ else
+ return QV4::Encode::null();
+ } else if (auto flags = metaType.flags(); flags & QMetaType::IsQmlList) {
+ // casting to QQmlListProperty<QObject> is slightly nasty, but it's the
+ // same QQmlListReference does.
+ const auto *p = static_cast<const QQmlListProperty<QObject> *>(ptr);
+ if (p->object)
+ return QV4::QmlListWrapper::create(scope.engine, *p, metaType);
+ else
+ return QV4::Encode::null();
+ } else if (metaType == QMetaType::fromType<QJSValue>()) {
+ return QJSValuePrivate::convertToReturnedValue(
+ this, *reinterpret_cast<const QJSValue *>(ptr));
+ } else if (metaType == QMetaType::fromType<QList<QObject *> >()) {
+ // XXX Can this be made more by using Array as a prototype and implementing
+ // directly against QList<QObject*>?
+ const QList<QObject *> &list = *(const QList<QObject *>*)ptr;
+ QV4::ScopedArrayObject a(scope, newArrayObject());
+ a->arrayReserve(list.size());
+ QV4::ScopedValue v(scope);
+ for (int ii = 0; ii < list.size(); ++ii)
+ a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii))));
+ a->setArrayLengthUnchecked(list.size());
+ return a.asReturnedValue();
+ } else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) {
+ if (flags.testFlag(QMetaType::IsConst))
+ return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr));
+ else
+ return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
+ } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) {
+ const QJSPrimitiveValue *primitive = static_cast<const QJSPrimitiveValue *>(ptr);
+ switch (primitive->type()) {
+ case QJSPrimitiveValue::Boolean:
+ return Encode(primitive->asBoolean());
+ case QJSPrimitiveValue::Integer:
+ return Encode(primitive->asInteger());
+ case QJSPrimitiveValue::String:
+ return newString(primitive->asString())->asReturnedValue();
+ case QJSPrimitiveValue::Undefined:
+ return Encode::undefined();
+ case QJSPrimitiveValue::Null:
+ return Encode::null();
+ case QJSPrimitiveValue::Double:
+ return Encode(primitive->asDouble());
}
+ }
- QV4::Scoped<Sequence> sequence(scope);
+ if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) {
if (container) {
- sequence = QV4::SequencePrototype::newSequence(
- this, metaType, ptr,
- container, property, Heap::ReferenceObject::Flags(flags));
+ return QV4::QQmlValueTypeWrapper::create(
+ this, ptr, vtmo, metaType,
+ container, property, Heap::ReferenceObject::Flags(flags));
} else {
- sequence = QV4::SequencePrototype::fromData(this, metaType, ptr);
+ return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType);
}
- if (!sequence->isUndefined())
- return sequence->asReturnedValue();
+ }
- if (QMetaType::canConvert(metaType, QMetaType::fromType<QSequentialIterable>())) {
- QSequentialIterable lst;
- QMetaType::convert(metaType, ptr, QMetaType::fromType<QSequentialIterable>(), &lst);
- return sequentialIterableToJS(this, lst);
- }
+ const QQmlType listType = QQmlMetaType::qmlListType(metaType);
+ if (listType.isSequentialContainer())
+ return createSequence(listType.listMetaSequence());
- if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) {
- if (container) {
- return QV4::QQmlValueTypeWrapper::create(
- this, ptr, vtmo, metaType,
- container, property, Heap::ReferenceObject::Flags(flags));
- } else {
- return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType);
- }
- }
- }
+ QSequentialIterable iterable;
+ if (QMetaType::convert(metaType, ptr, QMetaType::fromType<QSequentialIterable>(), &iterable)) {
- // XXX TODO: To be compatible, we still need to handle:
- // + QObjectList
- // + QList<int>
+ // If the resulting iterable is useful for anything, turn it into a QV4::Sequence.
+ const QMetaSequence sequence = iterable.metaContainer();
+ if (sequence.hasSize() && sequence.canGetValueAtIndex())
+ return createSequence(sequence);
- // Enumeration types can just be treated as integers for now
- if (metaType.flags() & QMetaType::IsEnumeration)
- return QV4::Encode(*reinterpret_cast<const int *>(ptr));
+ // As a last resort, try to read the contents of the container via an iterator
+ // and build a JS array from them.
+ if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) {
+ QV4::ScopedArrayObject a(scope, newArrayObject());
+ for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it)
+ a->push_back(fromVariant(*it));
+ return a.asReturnedValue();
+ }
+ }
return QV4::Encode(newVariantObject(metaType, ptr));
}
@@ -1948,40 +1976,10 @@ ReturnedValue ExecutionEngine::fromVariant(
QVariantMap ExecutionEngine::variantMapFromJS(const Object *o)
{
- return objectToVariant(o).toMap();
-}
-
-
-// Converts a QVariantList to JS.
-// The result is a new Array object with length equal to the length
-// of the QVariantList, and the elements being the QVariantList's
-// elements converted to JS, recursively.
-static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst)
-{
- QV4::Scope scope(v4);
- QV4::ScopedArrayObject a(scope, v4->newArrayObject());
- a->arrayReserve(lst.size());
- QV4::ScopedValue v(scope);
- for (int i = 0; i < lst.size(); i++)
- a->arrayPut(i, (v = variantToJS(v4, lst.at(i))));
- a->setArrayLengthUnchecked(lst.size());
- return a.asReturnedValue();
-}
-
-// Converts a QSequentialIterable to JS.
-// The result is a new Array object with length equal to the length
-// of the QSequentialIterable, and the elements being the QSequentialIterable's
-// elements converted to JS, recursively.
-static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst)
-{
- QV4::Scope scope(v4);
- QV4::ScopedArrayObject a(scope, v4->newArrayObject());
- a->arrayReserve(lst.size());
- QV4::ScopedValue v(scope);
- for (int i = 0; i < lst.size(); i++)
- a->arrayPut(i, (v = variantToJS(v4, lst.at(i))));
- a->setArrayLengthUnchecked(lst.size());
- return a.asReturnedValue();
+ Q_ASSERT(o);
+ V4ObjectSet visitedObjects;
+ visitedObjects.insert(o->d());
+ return objectToVariantMap(o, &visitedObjects, JSToQVariantConversionBehavior::Safish);
}
// Converts a QVariantMap to JS.
@@ -2071,10 +2069,10 @@ QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(const Q
: QQmlMetaType::RequireFullyTyped,
&cacheError)
: nullptr) {
- return ExecutableCompilationUnit::create(
- QV4::CompiledData::CompilationUnit(
- cachedUnit->qmlData, cachedUnit->aotCompiledFunctions,
- url.fileName(), url.toString()));
+ return executableCompilationUnit(
+ QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(
+ cachedUnit->qmlData, cachedUnit->aotCompiledFunctions, url.fileName(),
+ url.toString()));
}
QFile f(QQmlFile::urlToLocalFileOrQrc(url));
@@ -2108,21 +2106,58 @@ QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(
}
}
- return ExecutableCompilationUnit::create(std::move(unit));
+ return insertCompilationUnit(std::move(unit));
+}
+
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compilationUnitForUrl(const QUrl &url) const
+{
+ // Gives the _most recently inserted_ CU of that URL. That's what we want.
+ return m_compilationUnits.value(url);
+}
+
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::executableCompilationUnit(
+ QQmlRefPointer<CompiledData::CompilationUnit> &&unit)
+{
+ const QUrl url = unit->finalUrl();
+ auto [begin, end] = std::as_const(m_compilationUnits).equal_range(url);
+
+ for (auto it = begin; it != end; ++it) {
+ if ((*it)->baseCompilationUnit() == unit)
+ return *it;
+ }
+
+ auto executableUnit = m_compilationUnits.insert(
+ url, ExecutableCompilationUnit::create(std::move(unit), this));
+ // runtime data should not be initialized yet, so we don't need to mark the CU
+ Q_ASSERT(!(*executableUnit)->runtimeStrings);
+ return *executableUnit;
+}
+
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::insertCompilationUnit(QQmlRefPointer<CompiledData::CompilationUnit> &&unit) {
+ QUrl url = unit->finalUrl();
+ auto executableUnit = ExecutableCompilationUnit::create(std::move(unit), this);
+ /* Compilation Units stored in the engine are part of the gc roots,
+ so we don't trigger any write-barrier when they are added. Use
+ markCustom to make sure they are still marked when we insert them */
+ QV4::WriteBarrier::markCustom(this, [&executableUnit](QV4::MarkStack *ms) {
+ executableUnit->markObjects(ms);
+ });
+ return *m_compilationUnits.insert(std::move(url), std::move(executableUnit));
}
-void ExecutionEngine::injectCompiledModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit)
+void ExecutionEngine::trimCompilationUnits()
{
- // Injection can happen from the QML type loader thread for example, but instantiation and
- // evaluation must be limited to the ExecutionEngine's thread.
- QMutexLocker moduleGuard(&moduleMutex);
- modules.insert(moduleUnit->finalUrl(), moduleUnit);
+ for (auto it = m_compilationUnits.begin(); it != m_compilationUnits.end();) {
+ if ((*it)->count() == 1)
+ it = m_compilationUnits.erase(it);
+ else
+ ++it;
+ }
}
ExecutionEngine::Module ExecutionEngine::moduleForUrl(
const QUrl &url, const ExecutableCompilationUnit *referrer) const
{
- QMutexLocker moduleGuard(&moduleMutex);
const auto nativeModule = nativeModules.find(url);
if (nativeModule != nativeModules.end())
return Module { nullptr, *nativeModule };
@@ -2130,47 +2165,45 @@ ExecutionEngine::Module ExecutionEngine::moduleForUrl(
const QUrl resolved = referrer
? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url))
: QQmlTypeLoader::normalize(url);
- auto existingModule = modules.find(resolved);
- if (existingModule == modules.end())
+ auto existingModule = m_compilationUnits.find(resolved);
+ if (existingModule == m_compilationUnits.end())
return Module { nullptr, nullptr };
return Module { *existingModule, nullptr };
}
ExecutionEngine::Module ExecutionEngine::loadModule(const QUrl &url, const ExecutableCompilationUnit *referrer)
{
- QMutexLocker moduleGuard(&moduleMutex);
- const auto nativeModule = nativeModules.find(url);
- if (nativeModule != nativeModules.end())
+ const auto nativeModule = nativeModules.constFind(url);
+ if (nativeModule != nativeModules.cend())
return Module { nullptr, *nativeModule };
const QUrl resolved = referrer
? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url))
: QQmlTypeLoader::normalize(url);
- auto existingModule = modules.find(resolved);
- if (existingModule != modules.end())
+ auto existingModule = m_compilationUnits.constFind(resolved);
+ if (existingModule != m_compilationUnits.cend())
return Module { *existingModule, nullptr };
- moduleGuard.unlock();
-
auto newModule = compileModule(resolved);
- if (newModule) {
- moduleGuard.relock();
- modules.insert(resolved, newModule);
- }
+ Q_ASSERT(!newModule || m_compilationUnits.contains(resolved, newModule));
return Module { newModule, nullptr };
}
QV4::Value *ExecutionEngine::registerNativeModule(const QUrl &url, const QV4::Value &module)
{
- QMutexLocker moduleGuard(&moduleMutex);
- const auto existingModule = nativeModules.find(url);
- if (existingModule != nativeModules.end())
+ const auto existingModule = nativeModules.constFind(url);
+ if (existingModule != nativeModules.cend())
return nullptr;
QV4::Value *val = this->memoryManager->m_persistentValues->allocate();
*val = module.asReturnedValue();
nativeModules.insert(url, val);
+
+ // Make sure the type loader doesn't try to resolve the script anymore.
+ if (m_qmlEngine)
+ QQmlEnginePrivate::get(m_qmlEngine)->typeLoader.injectScript(url, *val);
+
return val;
}
@@ -2437,6 +2470,23 @@ void ExecutionEngine::setExtensionData(int index, Deletable *data)
m_extensionData[index] = data;
}
+template<typename Source>
+bool convertToIterable(QMetaType metaType, void *data, Source *sequence)
+{
+ QSequentialIterable iterable;
+ if (!QMetaType::view(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable))
+ return false;
+
+ const QMetaType elementMetaType = iterable.valueMetaType();
+ QVariant element(elementMetaType);
+ for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) {
+ if (!ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data()))
+ element = QVariant(elementMetaType);
+ iterable.addValue(element, QSequentialIterable::AtEnd);
+ }
+ return true;
+}
+
// Converts a JS value to a meta-type.
// data must point to a place that can store a value of the given type.
// Returns true if conversion succeeded, false otherwise.
@@ -2453,6 +2503,12 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
case QMetaType::UInt:
*reinterpret_cast<uint*>(data) = value.toUInt32();
return true;
+ case QMetaType::Long:
+ *reinterpret_cast<long*>(data) = long(value.toInteger());
+ return true;
+ case QMetaType::ULong:
+ *reinterpret_cast<ulong*>(data) = ulong(value.toInteger());
+ return true;
case QMetaType::LongLong:
*reinterpret_cast<qlonglong*>(data) = qlonglong(value.toInteger());
return true;
@@ -2471,12 +2527,24 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
*reinterpret_cast<QString*>(data) = value.toQString();
return true;
case QMetaType::QByteArray:
- if (const ArrayBuffer *ab = value.as<ArrayBuffer>())
+ if (const ArrayBuffer *ab = value.as<ArrayBuffer>()) {
*reinterpret_cast<QByteArray*>(data) = ab->asByteArray();
- else if (const String *string = value.as<String>())
+ } else if (const String *string = value.as<String>()) {
*reinterpret_cast<QByteArray*>(data) = string->toQString().toUtf8();
- else
+ } else if (const ArrayObject *ao = value.as<ArrayObject>()) {
+ // Since QByteArray is sequentially iterable, we have to construct it from a JS Array.
+ QByteArray result;
+ const qint64 length = ao->getLength();
+ result.reserve(length);
+ for (qint64 i = 0; i < length; ++i) {
+ char value = 0;
+ ExecutionEngine::metaTypeFromJS(ao->get(i), QMetaType::fromType<char>(), &value);
+ result.push_back(value);
+ }
+ *reinterpret_cast<QByteArray*>(data) = std::move(result);
+ } else {
*reinterpret_cast<QByteArray*>(data) = QByteArray();
+ }
return true;
case QMetaType::Float:
*reinterpret_cast<float*>(data) = value.toNumber();
@@ -2493,6 +2561,9 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
case QMetaType::UChar:
*reinterpret_cast<unsigned char*>(data) = (unsigned char)(value.toInt32());
return true;
+ case QMetaType::SChar:
+ *reinterpret_cast<signed char*>(data) = (signed char)(value.toInt32());
+ return true;
case QMetaType::QChar:
if (String *s = value.stringValue()) {
QString str = s->toQString();
@@ -2562,7 +2633,8 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
if (a) {
*reinterpret_cast<QVariantList *>(data) = ExecutionEngine::toVariant(
- *a, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false).toList();
+ *a, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false)
+ .toList();
return true;
}
break;
@@ -2578,7 +2650,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
case QMetaType::QVariant:
if (value.as<QV4::Managed>()) {
*reinterpret_cast<QVariant*>(data) = ExecutionEngine::toVariant(
- value, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false);
+ value, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false);
} else if (value.isNull()) {
*reinterpret_cast<QVariant*>(data) = QVariant::fromValue(nullptr);
} else if (value.isUndefined()) {
@@ -2606,15 +2678,6 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
}
break;
}
-#if QT_CONFIG(qml_locale)
- case QMetaType::QLocale: {
- if (const QV4::QQmlLocaleData *l = value.as<QQmlLocaleData>()) {
- *reinterpret_cast<QLocale *>(data) = *l->d()->locale;
- return true;
- }
- break;
- }
-#endif
default:
break;
}
@@ -2624,19 +2687,40 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
return true;
}
- if (metaType == QMetaType::fromType<QQmlListReference>()) {
- if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
+ if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
+ if (metaType == QMetaType::fromType<QQmlListReference>()) {
*reinterpret_cast<QQmlListReference *>(data) = wrapper->toListReference();
return true;
}
+
+ const auto wrapperPrivate = wrapper->d();
+ if (wrapperPrivate->propertyType() == metaType) {
+ *reinterpret_cast<QQmlListProperty<QObject> *>(data) = *wrapperPrivate->property();
+ return true;
+ }
}
if (const QQmlValueTypeWrapper *vtw = value.as<QQmlValueTypeWrapper>()) {
const QMetaType valueType = vtw->type();
if (valueType == metaType)
return vtw->toGadget(data);
- if (QMetaType::canConvert(valueType, metaType))
- return QMetaType::convert(valueType, vtw->d()->gadgetPtr(), metaType, data);
+
+ Heap::QQmlValueTypeWrapper *d = vtw->d();
+ if (d->isReference())
+ d->readReference();
+
+ if (void *gadgetPtr = d->gadgetPtr()) {
+ if (QQmlValueTypeProvider::populateValueType(metaType, data, valueType, gadgetPtr))
+ return true;
+ if (QMetaType::canConvert(valueType, metaType))
+ return QMetaType::convert(valueType, gadgetPtr, metaType, data);
+ } else {
+ QVariant empty(valueType);
+ if (QQmlValueTypeProvider::populateValueType(metaType, data, valueType, empty.data()))
+ return true;
+ if (QMetaType::canConvert(valueType, metaType))
+ return QMetaType::convert(valueType, empty.data(), metaType, data);
+ }
}
// Try to use magic; for compatibility with qjsvalue_cast.
@@ -2645,43 +2729,62 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
return true;
const bool isPointer = (metaType.flags() & QMetaType::IsPointer);
- if (value.as<QV4::VariantObject>() && isPointer) {
- const QByteArray pointedToTypeName = QByteArray(metaType.name()).chopped(1);
- const QMetaType valueType = QMetaType::fromName(pointedToTypeName);
- QVariant &var = value.as<QV4::VariantObject>()->d()->data();
- if (valueType == var.metaType()) {
- // We have T t, T* is requested, so return &t.
- *reinterpret_cast<void* *>(data) = var.data();
+ const QV4::VariantObject *variantObject = value.as<QV4::VariantObject>();
+ if (variantObject) {
+ // Actually a reference, because we're poking it for its data() below and we want
+ // the _original_ data, not some copy.
+ QVariant &var = variantObject->d()->data();
+
+ if (var.metaType() == metaType) {
+ metaType.destruct(data);
+ metaType.construct(data, var.data());
return true;
- } else if (Object *o = value.objectValue()) {
- // Look in the prototype chain.
- QV4::Scope scope(o->engine());
- QV4::ScopedObject proto(scope, o->getPrototypeOf());
- while (proto) {
- bool canCast = false;
- if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
- const QVariant &v = vo->d()->data();
- canCast = (metaType == v.metaType());
- }
- else if (proto->as<QV4::QObjectWrapper>()) {
- QV4::ScopedObject p(scope, proto.getPointer());
- if (QObject *qobject = qtObjectFromJS(p)) {
- if (const QMetaObject *metaObject = metaType.metaObject())
- canCast = metaObject->cast(qobject) != nullptr;
- else
- canCast = qobject->qt_metacast(pointedToTypeName);
+ }
+
+ if (isPointer) {
+ const QByteArray pointedToTypeName = QByteArray(metaType.name()).chopped(1);
+ const QMetaType valueType = QMetaType::fromName(pointedToTypeName);
+
+ if (valueType == var.metaType()) {
+ // ### Qt7: Remove this. Returning pointers to potentially gc'd data is crazy.
+ // We have T t, T* is requested, so return &t.
+ *reinterpret_cast<const void **>(data) = var.data();
+ return true;
+ } else if (Object *o = value.objectValue()) {
+ // Look in the prototype chain.
+ QV4::Scope scope(o->engine());
+ QV4::ScopedObject proto(scope, o->getPrototypeOf());
+ while (proto) {
+ bool canCast = false;
+ if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
+ const QVariant &v = vo->d()->data();
+ canCast = (metaType == v.metaType());
}
+ else if (proto->as<QV4::QObjectWrapper>()) {
+ QV4::ScopedObject p(scope, proto.getPointer());
+ if (QObject *qobject = qtObjectFromJS(p)) {
+ if (const QMetaObject *metaObject = metaType.metaObject())
+ canCast = metaObject->cast(qobject) != nullptr;
+ else
+ canCast = qobject->qt_metacast(pointedToTypeName);
+ }
+ }
+ if (canCast) {
+ const QMetaType varType = var.metaType();
+ if (varType.flags() & QMetaType::IsPointer) {
+ *reinterpret_cast<const void **>(data)
+ = *reinterpret_cast<void *const *>(var.data());
+ } else {
+ *reinterpret_cast<const void **>(data) = var.data();
+ }
+ return true;
+ }
+ proto = proto->getPrototypeOf();
}
- if (canCast) {
- const QMetaType varType = var.metaType();
- if (varType.flags() & QMetaType::IsPointer)
- *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data());
- else
- *reinterpret_cast<void* *>(data) = var.data();
- return true;
- }
- proto = proto->getPrototypeOf();
}
+ } else if (QQmlValueTypeProvider::populateValueType(
+ metaType, data, var.metaType(), var.data())) {
+ return true;
}
} else if (value.isNull() && isPointer) {
*reinterpret_cast<void* *>(data) = nullptr;
@@ -2693,7 +2796,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
*reinterpret_cast<QJSPrimitiveValue *>(data) = createPrimitive(&value);
return true;
} else if (!isPointer) {
- if (QQmlValueTypeProvider::createValueType(value, metaType, data))
+ if (QQmlValueTypeProvider::populateValueType(metaType, data, value))
return true;
}
@@ -2704,21 +2807,14 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
metaType.construct(data, result.constData());
return true;
}
+
+ if (convertToIterable(metaType, data, sequence))
+ return true;
}
if (const QV4::ArrayObject *array = value.as<ArrayObject>()) {
- QSequentialIterable iterable;
- if (QMetaType::view(
- metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable)) {
- const QMetaType elementMetaType = iterable.valueMetaType();
- QVariant element(elementMetaType);
- for (qsizetype i = 0, end = array->getLength(); i < end; ++i) {
- if (!metaTypeFromJS(array->get(i), elementMetaType, element.data()))
- element = QVariant(elementMetaType);
- iterable.addValue(element, QSequentialIterable::AtEnd);
- }
+ if (convertToIterable(metaType, data, array))
return true;
- }
}
return false;
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index ec1150120d..8e1bd24f6b 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -14,23 +14,22 @@
// We mean it.
//
-#include "qv4global_p.h"
-#include "qv4context_p.h"
-#include "qv4stackframe_p.h"
#include <private/qintrusivelist_p.h>
-#include "qv4enginebase_p.h"
-#include <private/qqmlrefcount_p.h>
#include <private/qqmldelayedcallqueue_p.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qset.h>
-#include <QtCore/qprocessordetection.h>
-
-#include "qv4function_p.h"
+#include <private/qqmlrefcount_p.h>
#include <private/qv4compileddata_p.h>
+#include <private/qv4context_p.h>
+#include <private/qv4enginebase_p.h>
#include <private/qv4executablecompilationunit_p.h>
+#include <private/qv4function_p.h>
+#include <private/qv4global_p.h>
#include <private/qv4stacklimits_p.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qprocessordetection.h>
+#include <QtCore/qset.h>
+
namespace WTF {
class BumpPointerAllocator;
class PageAllocation;
@@ -76,7 +75,7 @@ namespace QV4 { struct QObjectMethod; }
// class MyClass : public QObject {
// Q_OBJECT
// ...
-// Q_INVOKABLE void myMethod(QQmlV4Function*);
+// Q_INVOKABLE void myMethod(QQmlV4FunctionPtr);
// };
// The QQmlV8Function - and consequently the arguments and return value - only remains
// valid during the call. If the return value isn't set within myMethod(), the will return
@@ -110,6 +109,7 @@ class QQmlError;
class QJSEngine;
class QQmlEngine;
class QQmlContextData;
+class QQmlTypeLoader;
namespace QV4 {
namespace Debugging {
@@ -174,6 +174,14 @@ public:
QQmlEngine *qmlEngine() const { return m_qmlEngine; }
QJSEngine *publicEngine;
+ template<typename TypeLoader = QQmlTypeLoader>
+ TypeLoader *typeLoader()
+ {
+ if (m_qmlEngine)
+ return TypeLoader::get(m_qmlEngine);
+ return nullptr;
+ }
+
enum JSObjects {
RootContext,
ScriptContext,
@@ -488,8 +496,6 @@ public:
Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); }
Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); }
- QIntrusiveList<ExecutableCompilationUnit, &ExecutableCompilationUnit::nextCompilationUnit> compilationUnits;
-
quint32 m_engineId;
RegExpCache *regExpCache;
@@ -536,7 +542,14 @@ public:
void setProfiler(Profiling::Profiler *profiler);
#endif // QT_CONFIG(qml_debug)
- ExecutionContext *currentContext() const { return currentStackFrame->context(); }
+ // We don't want to #include <private/qv4stackframe_p.h> here, but we still want
+ // currentContext() to be inline. Therefore we shift the requirement to provide the
+ // complete type of CppStackFrame to the caller by making this a template.
+ template<typename StackFrame = CppStackFrame>
+ ExecutionContext *currentContext() const
+ {
+ return static_cast<const StackFrame *>(currentStackFrame)->context();
+ }
// ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast
quintptr newProtoId() { return (protoIdCount += 2); }
@@ -546,6 +559,7 @@ public:
Heap::Object *newObject();
Heap::Object *newObject(Heap::InternalClass *internalClass);
+ Heap::String *newString(char16_t c) { return newString(QChar(c)); }
Heap::String *newString(const QString &s = QString());
Heap::String *newIdentifier(const QString &text);
@@ -657,7 +671,8 @@ public:
// variant conversions
static QVariant toVariant(
- const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects = true);
+ const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols = true);
+ static QVariant toVariantLossy(const QV4::Value &value);
QV4::ReturnedValue fromVariant(const QVariant &);
QV4::ReturnedValue fromVariant(
const QVariant &variant, Heap::Object *parent, int property, uint flags);
@@ -738,7 +753,21 @@ public:
QQmlRefPointer<ExecutableCompilationUnit> compileModule(
const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp);
- void injectCompiledModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit);
+ QQmlRefPointer<ExecutableCompilationUnit> compilationUnitForUrl(const QUrl &url) const;
+
+ QQmlRefPointer<ExecutableCompilationUnit> executableCompilationUnit(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit);
+
+ QQmlRefPointer<ExecutableCompilationUnit> insertCompilationUnit(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit);
+
+ QMultiHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> compilationUnits() const
+ {
+ return m_compilationUnits;
+ }
+ void clearCompilationUnits() { m_compilationUnits.clear(); }
+ void trimCompilationUnits();
+
QV4::Value *registerNativeModule(const QUrl &url, const QV4::Value &module);
struct Module {
@@ -854,8 +883,7 @@ private:
QVector<Deletable *> m_extensionData;
- mutable QMutex moduleMutex;
- QHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> modules;
+ QMultiHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> m_compilationUnits;
// QV4::PersistentValue would be preferred, but using QHash will create copies,
// and QV4::PersistentValue doesn't like creating copies.
diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h
index 721a024efb..68e906baa1 100644
--- a/src/qml/jsruntime/qv4enginebase_p.h
+++ b/src/qml/jsruntime/qv4enginebase_p.h
@@ -45,7 +45,8 @@ struct Q_QML_EXPORT EngineBase {
quint8 isExecutingInRegExpJIT = false;
quint8 isInitialized = false;
- quint8 padding[2];
+ quint8 inShutdown = false;
+ quint8 isGCOngoing = false; // incremental gc is ongoing (but mutator might be running)
MemoryManager *memoryManager = nullptr;
union {
diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp
index 516c81864f..35b5952d38 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -56,7 +56,7 @@ void Heap::ErrorObject::init(const Value &message, ErrorType t)
e->d()->stackTrace = new StackTrace(scope.engine->stackTrace());
if (!e->d()->stackTrace->isEmpty()) {
setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source));
- setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(e->d()->stackTrace->at(0).line));
+ setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(qAbs(e->d()->stackTrace->at(0).line)));
}
if (!message.isUndefined())
@@ -84,7 +84,7 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int
Q_ASSERT(!e->d()->stackTrace->isEmpty());
setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source));
- setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(e->d()->stackTrace->at(0).line));
+ setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(qAbs(e->d()->stackTrace->at(0).line)));
if (!message.isUndefined())
setProperty(scope.engine, QV4::ErrorObject::Index_Message, message);
diff --git a/src/qml/jsruntime/qv4estable.cpp b/src/qml/jsruntime/qv4estable.cpp
index ebd62db1b5..fb36b10728 100644
--- a/src/qml/jsruntime/qv4estable.cpp
+++ b/src/qml/jsruntime/qv4estable.cpp
@@ -111,21 +111,18 @@ ReturnedValue ESTable::get(const Value &key, bool *hasValue) const
// Removes the given \a key from the table
bool ESTable::remove(const Value &key)
{
- bool found = false;
- uint idx = 0;
- for (; idx < m_size; ++idx) {
- if (m_keys[idx].sameValueZero(key)) {
- found = true;
- break;
+ for (uint index = 0; index < m_size; ++index) {
+ if (m_keys[index].sameValueZero(key)) {
+ // Remove the element at |index| by moving all elements to the right
+ // of |index| one place to the left.
+ size_t count = (m_size - (index + 1)) * sizeof(Value);
+ memmove(m_keys + index, m_keys + index + 1, count);
+ memmove(m_values + index, m_values + index + 1, count);
+ m_size--;
+ return true;
}
}
-
- if (found == true) {
- memmove(m_keys + idx, m_keys + idx + 1, (m_size - idx)*sizeof(Value));
- memmove(m_values + idx, m_values + idx + 1, (m_size - idx)*sizeof(Value));
- m_size--;
- }
- return found;
+ return false;
}
// Returns the size of the table. Note that the size may not match the underlying allocation.
diff --git a/src/qml/jsruntime/qv4estable_p.h b/src/qml/jsruntime/qv4estable_p.h
index 25fbf0c372..f0c5c7cb81 100644
--- a/src/qml/jsruntime/qv4estable_p.h
+++ b/src/qml/jsruntime/qv4estable_p.h
@@ -17,12 +17,13 @@
#include "qv4value_p.h"
+class tst_qv4estable;
+
QT_BEGIN_NAMESPACE
-namespace QV4
-{
+namespace QV4 {
-class ESTable
+class Q_AUTOTEST_EXPORT ESTable
{
public:
ESTable();
@@ -40,13 +41,15 @@ public:
void removeUnmarkedKeys();
private:
+ friend class ::tst_qv4estable;
+
Value *m_keys = nullptr;
Value *m_values = nullptr;
uint m_size = 0;
uint m_capacity = 0;
};
-}
+} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h
index 2f97c9ae6e..8181bf17ae 100644
--- a/src/qml/jsruntime/qv4executableallocator_p.h
+++ b/src/qml/jsruntime/qv4executableallocator_p.h
@@ -100,7 +100,7 @@ public:
private:
QMultiMap<size_t, Allocation*> freeAllocations;
QMap<quintptr, ChunkOfPages*> chunks;
- mutable QRecursiveMutex mutex;
+ mutable QMutex mutex;
};
}
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index 898f911334..34d737cdae 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -17,36 +17,14 @@
#include <private/qqmlscriptdata_p.h>
#include <private/qv4module_p.h>
#include <private/qv4compilationunitmapper_p.h>
-#include <private/qml_compile_hash_p.h>
#include <private/qqmltypewrapper_p.h>
-#include <private/inlinecomponentutils_p.h>
#include <private/qv4resolvedtypereference_p.h>
#include <private/qv4objectiterator_p.h>
-#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlpropertymap.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qstandardpaths.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qscopeguard.h>
#include <QtCore/qcryptographichash.h>
-#include <QtCore/QScopedValueRollback>
-
-static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
-
-#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
-# ifdef Q_OS_LINUX
-// Place on a separate section on Linux so it's easier to check from outside
-// what the hash version is.
-__attribute__((section(".qml_compile_hash")))
-# endif
-const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH;
-static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH,
- "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
-#else
-# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
-#endif
QT_BEGIN_NAMESPACE
@@ -55,28 +33,16 @@ namespace QV4 {
ExecutableCompilationUnit::ExecutableCompilationUnit() = default;
ExecutableCompilationUnit::ExecutableCompilationUnit(
- CompiledData::CompilationUnit &&compilationUnit)
- : CompiledData::CompilationUnit(std::move(compilationUnit))
-{}
-
-ExecutableCompilationUnit::~ExecutableCompilationUnit()
+ QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit)
+ : m_compilationUnit(std::move(compilationUnit))
{
- unlink();
+ constants = m_compilationUnit->constants;
}
-QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url)
+ExecutableCompilationUnit::~ExecutableCompilationUnit()
{
- static const QByteArray envCachePath = qgetenv("QML_DISK_CACHE_PATH");
-
- const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
- const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
- QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
- fileNameHash.addData(localSourcePath.toUtf8());
- QString directory = envCachePath.isEmpty()
- ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/")
- : QString::fromLocal8Bit(envCachePath) + QLatin1String("/");
- QDir::root().mkpath(directory);
- return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
+ if (engine)
+ clear();
}
static QString toString(QV4::ReturnedValue v)
@@ -104,25 +70,30 @@ static void dumpConstantTable(const StaticValue *constants, uint count)
}
}
-QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
+void ExecutableCompilationUnit::populate()
{
- this->engine = engine;
- engine->compilationUnits.insert(this);
+ /* In general, we should use QV4::Scope whenever we allocate heap objects, and employ write barriers
+ for member variables pointing to heap objects. However, ExecutableCompilationUnit is special, as it
+ is always part of the root set. So instead of using scopde allocations and write barriers, we use a
+ slightly different approach: We temporarily block the gc from running. Afterwards, at the end of the
+ function we check whether the gc was already running, and mark the ExecutableCompilationUnit. This
+ ensures that all the newly allocated objects of the compilation unit will be marked in turn.
+ If the gc was not running, we don't have to do anything, because everything will be marked when the
+ gc starts marking the root set at the start of a run.
+ */
+ const CompiledData::Unit *data = m_compilationUnit->data;
+ GCCriticalSection<ExecutableCompilationUnit> criticalSection(engine, this);
Q_ASSERT(!runtimeStrings);
+ Q_ASSERT(engine);
Q_ASSERT(data);
const quint32 stringCount = totalStringCount();
- runtimeStrings = (QV4::Heap::String **)malloc(stringCount * sizeof(QV4::Heap::String*));
- // memset the strings to 0 in case a GC run happens while we're within the loop below
- memset(runtimeStrings, 0, stringCount * sizeof(QV4::Heap::String*));
+ runtimeStrings = (QV4::Heap::String **)calloc(stringCount, sizeof(QV4::Heap::String*));
for (uint i = 0; i < stringCount; ++i)
runtimeStrings[i] = engine->newString(stringAt(i));
runtimeRegularExpressions
= new QV4::Value[data->regexpTableSize];
- // memset the regexps to 0 in case a GC run happens while we're within the loop below
- memset(runtimeRegularExpressions, 0,
- data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
uint f = re->flags();
@@ -155,11 +126,9 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
if (data->jsClassTableSize) {
runtimeClasses
- = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize
- * sizeof(QV4::Heap::InternalClass *));
- // memset the regexps to 0 in case a GC run happens while we're within the loop below
- memset(runtimeClasses, 0,
- data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
+ = (QV4::Heap::InternalClass **)calloc(data->jsClassTableSize,
+ sizeof(QV4::Heap::InternalClass *));
+
for (uint i = 0; i < data->jsClassTableSize; ++i) {
int memberCount = 0;
const CompiledData::JSClassMember *member
@@ -182,13 +151,13 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
= qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER")
|| !(engine->diskCacheOptions() & ExecutionEngine::DiskCache::AotNative);
- const QQmlPrivate::TypedFunction *aotFunction
- = ignoreAotCompiledFunctions ? nullptr : aotCompiledFunctions;
+ const QQmlPrivate::AOTCompiledFunction *aotFunction
+ = ignoreAotCompiledFunctions ? nullptr : m_compilationUnit->aotCompiledFunctions;
- auto advanceAotFunction = [&](int i) -> const QQmlPrivate::TypedFunction * {
+ auto advanceAotFunction = [&](int i) -> const QQmlPrivate::AOTCompiledFunction * {
if (aotFunction) {
if (aotFunction->functionPtr) {
- if (aotFunction->extraData == i)
+ if (aotFunction->functionIndex == i)
return aotFunction++;
} else {
aotFunction = nullptr;
@@ -234,15 +203,14 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
<< (data->indexOfRootFunction != -1
? data->indexOfRootFunction : 0);
}
-
- if (data->indexOfRootFunction != -1)
- return runtimeFunctions[data->indexOfRootFunction];
- else
- return nullptr;
}
Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const
{
+ const CompiledData::Unit *data = m_compilationUnit->data;
+ Q_ASSERT(data);
+ Q_ASSERT(engine);
+
Q_ASSERT(index < int(data->templateObjectTableSize));
if (!templateObjects.size())
templateObjects.resize(data->templateObjectTableSize);
@@ -271,33 +239,17 @@ Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const
return templateObjects.at(index);
}
-void ExecutableCompilationUnit::unlink()
+void ExecutableCompilationUnit::clear()
{
- if (engine)
- nextCompilationUnit.remove();
-
- if (isRegistered) {
- Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0));
- QQmlMetaType::unregisterInternalCompositeType(this);
- }
-
- propertyCaches.clear();
+ delete [] imports;
+ imports = nullptr;
if (runtimeLookups) {
- for (uint i = 0; i < data->lookupTableSize; ++i)
+ const uint lookupTableSize = unitData()->lookupTableSize;
+ for (uint i = 0; i < lookupTableSize; ++i)
runtimeLookups[i].releasePropertyCache();
}
- dependentScripts.clear();
-
- typeNameCache.reset();
-
- qDeleteAll(resolvedTypes);
- resolvedTypes.clear();
-
- engine = nullptr;
- qmlEngine = nullptr;
-
delete [] runtimeLookups;
runtimeLookups = nullptr;
@@ -313,8 +265,10 @@ void ExecutableCompilationUnit::unlink()
runtimeClasses = nullptr;
}
-void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack)
+void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) const
{
+ const CompiledData::Unit *data = m_compilationUnit->data;
+
if (runtimeStrings) {
for (uint i = 0, end = totalStringCount(); i < end; ++i)
if (runtimeStrings[i])
@@ -362,174 +316,29 @@ IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int com
return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
}
-void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine, CompositeMetaTypeIds types)
-{
- this->qmlEngine = qmlEngine;
-
- // Add to type registry of composites
- if (propertyCaches.needsVMEMetaObject(/*root object*/0)) {
- // typeIds is only valid for types that have references to themselves.
- if (!types.isValid())
- types = CompositeMetaTypeIds::fromCompositeName(rootPropertyCache()->className());
- typeIds = types;
- QQmlMetaType::registerInternalCompositeType(this);
-
- } else {
- const QV4::CompiledData::Object *obj = objectAt(/*root object*/0);
- auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
- if (const auto compilationUnit = typeRef->compilationUnit()) {
- typeIds = compilationUnit->typeIds;
- } else {
- const auto type = typeRef->type();
- typeIds = CompositeMetaTypeIds{ type.typeId(), type.qListTypeId() };
- }
- }
-
- // Collect some data for instantiation later.
- using namespace icutils;
- std::vector<QV4::CompiledData::InlineComponent> allICs {};
- for (int i=0; i != objectCount(); ++i) {
- const CompiledObject *obj = objectAt(i);
- for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
- allICs.push_back(*it);
- }
- }
- std::vector<Node> nodes;
- nodes.resize(allICs.size());
- std::iota(nodes.begin(), nodes.end(), 0);
- AdjacencyList adjacencyList;
- adjacencyList.resize(nodes.size());
- fillAdjacencyListForInlineComponents(this, adjacencyList, nodes, allICs);
- bool hasCycle = false;
- auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
- Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator
-
- // We need to first iterate over all inline components, as the containing component might create instances of them
- // and in that case we need to add its object count
- for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
- const auto &ic = allICs.at(nodeIt->index());
- int lastICRoot = ic.objectIndex;
- for (int i = ic.objectIndex; i<objectCount(); ++i) {
- const QV4::CompiledData::Object *obj = objectAt(i);
- bool leftCurrentInlineComponent
- = (i != lastICRoot
- && obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot))
- || !obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent);
- if (leftCurrentInlineComponent)
- break;
- inlineComponentData[lastICRoot].totalBindingCount += obj->nBindings;
-
- if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
- const auto type = typeRef->type();
- if (type.isValid() && type.parserStatusCast() != -1)
- ++inlineComponentData[lastICRoot].totalParserStatusCount;
-
- ++inlineComponentData[lastICRoot].totalObjectCount;
- if (const auto compilationUnit = typeRef->compilationUnit()) {
- // if the type is an inline component type, we have to extract the information from it
- // This requires that inline components are visited in the correct order
- auto icRoot = compilationUnit->icRoot;
- if (type.isInlineComponentType())
- icRoot = type.inlineComponentId();
- QScopedValueRollback<int> rollback {compilationUnit->icRoot, icRoot};
- inlineComponentData[lastICRoot].totalBindingCount += compilationUnit->totalBindingsCount();
- inlineComponentData[lastICRoot].totalParserStatusCount += compilationUnit->totalParserStatusCount();
- inlineComponentData[lastICRoot].totalObjectCount += compilationUnit->totalObjectCount();
- }
- }
- }
- }
- int bindingCount = 0;
- int parserStatusCount = 0;
- int objectCount = 0;
- for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
- const QV4::CompiledData::Object *obj = objectAt(i);
- if (obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent))
- continue;
-
- bindingCount += obj->nBindings;
- if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
- const auto type = typeRef->type();
- if (type.isValid() && type.parserStatusCast() != -1)
- ++parserStatusCount;
- ++objectCount;
- if (const auto compilationUnit = typeRef->compilationUnit()) {
- auto icRoot = compilationUnit->icRoot;
- if (type.isInlineComponentType())
- icRoot = type.inlineComponentId();
- QScopedValueRollback<int> rollback {compilationUnit->icRoot, icRoot};
- bindingCount += compilationUnit->totalBindingsCount();
- parserStatusCount += compilationUnit->totalParserStatusCount();
- objectCount += compilationUnit->totalObjectCount();
- }
- }
- }
-
- m_totalBindingsCount = bindingCount;
- m_totalParserStatusCount = parserStatusCount;
- m_totalObjectCount = objectCount;
-}
-
-int ExecutableCompilationUnit::totalBindingsCount() const {
- if (icRoot == -1)
- return m_totalBindingsCount;
- return inlineComponentData[icRoot].totalBindingCount;
-}
-
-int ExecutableCompilationUnit::totalObjectCount() const {
- if (icRoot == -1)
- return m_totalObjectCount;
- return inlineComponentData[icRoot].totalObjectCount;
-}
-
-int ExecutableCompilationUnit::totalParserStatusCount() const {
- if (icRoot == -1)
- return m_totalParserStatusCount;
- return inlineComponentData[icRoot].totalParserStatusCount;
-}
-
-bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const
+QQmlRefPointer<ExecutableCompilationUnit> ExecutableCompilationUnit::create(
+ QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit, ExecutionEngine *engine)
{
- if (!dependencyHasher) {
- for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
- if (data->dependencyMD5Checksum[i] != 0)
- return false;
- }
- return true;
- }
- const QByteArray checksum = dependencyHasher();
- return checksum.size() == sizeof(data->dependencyMD5Checksum)
- && memcmp(data->dependencyMD5Checksum, checksum.constData(),
- sizeof(data->dependencyMD5Checksum)) == 0;
-}
-
-CompositeMetaTypeIds ExecutableCompilationUnit::typeIdsForComponent(int objectid) const
-{
- if (objectid == 0)
- return typeIds;
- return inlineComponentData[objectid].typeIds;
+ auto result = QQmlRefPointer<ExecutableCompilationUnit>(
+ new ExecutableCompilationUnit(std::move(compilationUnit)),
+ QQmlRefPointer<ExecutableCompilationUnit>::Adopt);
+ result->engine = engine;
+ return result;
}
-QStringList ExecutableCompilationUnit::moduleRequests() const
+Heap::Module *ExecutableCompilationUnit::instantiate()
{
- QStringList requests;
- requests.reserve(data->moduleRequestTableSize);
- for (uint i = 0; i < data->moduleRequestTableSize; ++i)
- requests << stringAt(data->moduleRequestTable()[i]);
- return requests;
-}
+ const CompiledData::Unit *data = m_compilationUnit->data;
-Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
-{
if (isESModule() && module())
return module();
if (data->indexOfRootFunction < 0)
return nullptr;
- if (!this->engine)
- linkToEngine(engine);
+ Q_ASSERT(engine);
+ if (!runtimeStrings)
+ populate();
Scope scope(engine);
Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this));
@@ -537,13 +346,14 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
if (isESModule())
setModule(module->d());
- for (const QString &request: moduleRequests()) {
+ const QStringList moduleRequests = m_compilationUnit->moduleRequests();
+ for (const QString &request: moduleRequests) {
const QUrl url(request);
const auto dependentModuleUnit = engine->loadModule(url, this);
if (engine->hasException)
return nullptr;
if (dependentModuleUnit.compiled)
- dependentModuleUnit.compiled->instantiate(engine);
+ dependentModuleUnit.compiled->instantiate();
}
ScopedString importName(scope);
@@ -657,6 +467,11 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively(
if (exportName->toQString() == QLatin1String("*"))
return &module()->self;
+ const CompiledData::Unit *data = m_compilationUnit->data;
+
+ Q_ASSERT(data);
+ Q_ASSERT(engine);
+
Scope scope(engine);
if (auto localExport = lookupNameInExportTable(
@@ -767,6 +582,11 @@ void ExecutableCompilationUnit::getExportedNamesRecursively(
names->append(name);
};
+ const CompiledData::Unit *data = m_compilationUnit->data;
+
+ Q_ASSERT(data);
+ Q_ASSERT(engine);
+
for (uint i = 0; i < data->localExportEntryTableSize; ++i) {
const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i];
append(stringAt(entry.exportName));
@@ -799,6 +619,8 @@ void ExecutableCompilationUnit::getExportedNamesRecursively(
void ExecutableCompilationUnit::evaluate()
{
+ Q_ASSERT(engine);
+
QV4::Scope scope(engine);
QV4::Scoped<Module> mod(scope, module());
mod->evaluate();
@@ -806,7 +628,10 @@ void ExecutableCompilationUnit::evaluate()
void ExecutableCompilationUnit::evaluateModuleRequests()
{
- for (const QString &request: moduleRequests()) {
+ Q_ASSERT(engine);
+
+ const QStringList moduleRequests = m_compilationUnit->moduleRequests();
+ for (const QString &request: moduleRequests) {
auto dependentModule = engine->loadModule(QUrl(request), this);
if (dependentModule.native)
continue;
@@ -821,88 +646,6 @@ void ExecutableCompilationUnit::evaluateModuleRequests()
}
}
-bool ExecutableCompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
-{
- if (!QQmlFile::isLocalFile(url)) {
- *errorString = QStringLiteral("File has to be a local file.");
- return false;
- }
-
- const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
- auto cacheFile = std::make_unique<CompilationUnitMapper>();
-
- const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
- for (const QString &cachePath : cachePaths) {
- CompiledData::Unit *mappedUnit = cacheFile->get(cachePath, sourceTimeStamp, errorString);
- if (!mappedUnit)
- continue;
-
- const CompiledData::Unit * const oldDataPtr
- = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data
- : nullptr;
- const CompiledData::Unit *oldData = data;
- auto dataPtrRevert = qScopeGuard([this, oldData](){
- setUnitData(oldData);
- });
- setUnitData(mappedUnit);
-
- if (data->sourceFileIndex != 0
- && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
- *errorString = QStringLiteral("QML source file has moved to a different location.");
- continue;
- }
-
- dataPtrRevert.dismiss();
- free(const_cast<CompiledData::Unit*>(oldDataPtr));
- backingFile = std::move(cacheFile);
- return true;
- }
-
- return false;
-}
-
-bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
-{
- if (data->sourceTimeStamp == 0) {
- *errorString = QStringLiteral("Missing time stamp for source file");
- return false;
- }
-
- if (!QQmlFile::isLocalFile(unitUrl)) {
- *errorString = QStringLiteral("File has to be a local file.");
- return false;
- }
-
- return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>(
- [&unitUrl, errorString](const char *data, quint32 size) {
- return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data,
- size, errorString);
- });
-}
-
-/*!
- \internal
- This function creates a temporary key vector and sorts it to guarantuee a stable
- hash. This is used to calculate a check-sum on dependent meta-objects.
- */
-bool ResolvedTypeReferenceMap::addToHash(
- QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const
-{
- std::vector<int> keys (size());
- int i = 0;
- for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
- keys[i] = it.key();
- ++i;
- }
- std::sort(keys.begin(), keys.end());
- for (int key: keys) {
- if (!this->operator[](key)->addToHash(hash, checksums))
- return false;
- }
-
- return true;
-}
-
QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const
{
#if QT_CONFIG(translation)
@@ -919,7 +662,7 @@ QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Bind
break;
}
#endif
- return CompilationUnit::bindingValueAsString(binding);
+ return m_compilationUnit->bindingValueAsString(binding);
}
QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) const
@@ -927,7 +670,7 @@ QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) con
#if !QT_CONFIG(translation)
return QString();
#else
- const CompiledData::TranslationData &translation = data->translations()[index.index];
+ const CompiledData::TranslationData &translation = unitData()->translations()[index.index];
if (index.byId) {
QByteArray id = stringAt(translation.stringIndex).toUtf8();
@@ -945,62 +688,21 @@ QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) con
return context.toUtf8();
};
- QByteArray context = stringAt(translation.contextIndex).toUtf8();
- QByteArray comment = stringAt(translation.commentIndex).toUtf8();
- QByteArray text = stringAt(translation.stringIndex).toUtf8();
- return QCoreApplication::translate(
- context.isEmpty() ? fileContext() : context, text, comment, translation.number);
-#endif
-}
-
-bool ExecutableCompilationUnit::verifyHeader(
- const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString)
-{
- if (strncmp(unit->magic, CompiledData::magic_str, sizeof(unit->magic))) {
- *errorString = QStringLiteral("Magic bytes in the header do not match");
- return false;
- }
-
- if (unit->version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
- *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2")
- .arg(unit->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
- return false;
- }
-
- if (unit->qtVersion != quint32(QT_VERSION)) {
- *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2")
- .arg(unit->qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
- return false;
- }
-
- if (unit->sourceTimeStamp) {
- // Files from the resource system do not have any time stamps, so fall back to the application
- // executable.
- if (!expectedSourceTimeStamp.isValid())
- expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
-
- if (expectedSourceTimeStamp.isValid()
- && expectedSourceTimeStamp.toMSecsSinceEpoch() != unit->sourceTimeStamp) {
- *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
- return false;
- }
+ const bool hasContext
+ = translation.contextIndex != QV4::CompiledData::TranslationData::NoContextIndex;
+ QByteArray context;
+ if (hasContext) {
+ context = stringAt(translation.contextIndex).toUtf8();
+ } else {
+ auto pragmaTranslationContext = unitData()->translationContextIndex();
+ context = stringAt(*pragmaTranslationContext).toUtf8();
+ context = context.isEmpty() ? fileContext() : context;
}
-#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
- if (qstrncmp(qml_compile_hash, unit->libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) {
- *errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2")
- .arg(QString::fromLatin1(
- QByteArray(unit->libraryVersionHash, QML_COMPILE_HASH_LENGTH)
- .toPercentEncoding()),
- QString::fromLatin1(
- QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH)
- .toPercentEncoding()));
- return false;
- }
-#else
-#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
+ QByteArray comment = stringAt(translation.commentIndex).toUtf8();
+ QByteArray text = stringAt(translation.stringIndex).toUtf8();
+ return QCoreApplication::translate(context, text, comment, translation.number);
#endif
- return true;
}
} // namespace QV4
diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h
index e6472f3f83..3f3335ef4e 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit_p.h
+++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h
@@ -15,14 +15,15 @@
// We mean it.
//
-#include <private/qv4compileddata_p.h>
-#include <private/qv4identifierhash_p.h>
-#include <private/qqmlrefcount_p.h>
#include <private/qintrusivelist_p.h>
+#include <private/qqmlmetatype_p.h>
+#include <private/qqmlnullablevalue_p.h>
#include <private/qqmlpropertycachevector_p.h>
+#include <private/qqmlrefcount_p.h>
#include <private/qqmltype_p.h>
-#include <private/qqmlnullablevalue_p.h>
-#include <private/qqmlmetatype_p.h>
+#include <private/qqmltypenamecache_p.h>
+#include <private/qv4compileddata_p.h>
+#include <private/qv4identifierhash_p.h>
#include <memory>
@@ -31,210 +32,120 @@ QT_BEGIN_NAMESPACE
class QQmlScriptData;
class QQmlEnginePrivate;
-struct InlineComponentData {
-
- InlineComponentData() = default;
- InlineComponentData(const CompositeMetaTypeIds &typeIds, int objectIndex, int nameIndex, int totalObjectCount, int totalBindingCount, int totalParserStatusCount)
- : typeIds(typeIds)
- , objectIndex(objectIndex)
- , nameIndex(nameIndex)
- , totalObjectCount(totalObjectCount)
- , totalBindingCount(totalBindingCount)
- , totalParserStatusCount(totalParserStatusCount) {}
-
- CompositeMetaTypeIds typeIds;
- int objectIndex = -1;
- int nameIndex = -1;
- int totalObjectCount = 0;
- int totalBindingCount = 0;
- int totalParserStatusCount = 0;
-};
-
namespace QV4 {
-// index is per-object binding index
-typedef QVector<const QQmlPropertyData *> BindingPropertyData;
-
class CompilationUnitMapper;
-class ResolvedTypeReference;
-// map from name index
-struct ResolvedTypeReferenceMap: public QHash<int, ResolvedTypeReference*>
-{
- bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const;
-};
-class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit,
- public QQmlRefCount
+struct CompilationUnitRuntimeData
{
- Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit)
-public:
- friend class QQmlRefPointer<ExecutableCompilationUnit>;
-
- static QQmlRefPointer<ExecutableCompilationUnit> create(
- CompiledData::CompilationUnit &&compilationUnit)
- {
- return QQmlRefPointer<ExecutableCompilationUnit>(
- new ExecutableCompilationUnit(std::move(compilationUnit)),
- QQmlRefPointer<ExecutableCompilationUnit>::Adopt);
- }
+ Heap::String **runtimeStrings = nullptr; // Array
- static QQmlRefPointer<ExecutableCompilationUnit> create()
- {
- return QQmlRefPointer<ExecutableCompilationUnit>(
- new ExecutableCompilationUnit,
- QQmlRefPointer<ExecutableCompilationUnit>::Adopt);
- }
+ // pointers either to data->constants() or little-endian memory copy.
+ // We keep this member twice so that the JIT can access it via standard layout.
+ const StaticValue *constants = nullptr;
- QIntrusiveListNode nextCompilationUnit;
- ExecutionEngine *engine = nullptr;
- QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case.
-
- // url() and fileName() shall be used to load the actual QML/JS code or to show errors or
- // warnings about that code. They include any potential URL interceptions and thus represent the
- // "physical" location of the code.
- //
- // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code
- // They are _not_ intercepted and thus represent the "logical" name for the code.
-
- QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; }
- QUrl finalUrl() const
- {
- if (m_finalUrl.isNull)
- m_finalUrl = QUrl(finalUrlString());
- return m_finalUrl;
- }
+ QV4::StaticValue *runtimeRegularExpressions = nullptr;
+ Heap::InternalClass **runtimeClasses = nullptr;
+ const StaticValue **imports = nullptr;
QV4::Lookup *runtimeLookups = nullptr;
QVector<QV4::Function *> runtimeFunctions;
QVector<QV4::Heap::InternalClass *> runtimeBlocks;
mutable QVector<QV4::Heap::Object *> templateObjects;
- mutable QQmlNullableValue<QUrl> m_url;
- mutable QQmlNullableValue<QUrl> m_finalUrl;
-
- // QML specific fields
- QQmlPropertyCacheVector propertyCaches;
- QQmlPropertyCache::ConstPtr rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
-
- QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
-
- // index is object index. This allows fast access to the
- // property data when initializing bindings, avoiding expensive
- // lookups by string (property name).
- QVector<BindingPropertyData> bindingPropertyDataPerObject;
-
- // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
- // this is initialized on-demand by QQmlContextData
- QHash<int, IdentifierHash> namedObjectsPerComponentCache;
- inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
-
- void finalizeCompositeType(QQmlEnginePrivate *qmlEngine, CompositeMetaTypeIds typeIdsForComponent);
-
- int m_totalBindingsCount = 0; // Number of bindings used in this type
- int m_totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
- int m_totalObjectCount = 0; // Number of objects explicitly instantiated
- int icRoot = -1;
-
- int totalBindingsCount() const;
- int totalParserStatusCount() const;
- int totalObjectCount() const;
-
- QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
- ResolvedTypeReferenceMap resolvedTypes;
- ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); }
+};
- bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const;
+static_assert(std::is_standard_layout_v<CompilationUnitRuntimeData>);
+static_assert(offsetof(CompilationUnitRuntimeData, runtimeStrings) == 0);
+static_assert(offsetof(CompilationUnitRuntimeData, constants) == sizeof(QV4::Heap::String **));
+static_assert(offsetof(CompilationUnitRuntimeData, runtimeRegularExpressions) == offsetof(CompilationUnitRuntimeData, constants) + sizeof(const StaticValue *));
+static_assert(offsetof(CompilationUnitRuntimeData, runtimeClasses) == offsetof(CompilationUnitRuntimeData, runtimeRegularExpressions) + sizeof(const StaticValue *));
+static_assert(offsetof(CompilationUnitRuntimeData, imports) == offsetof(CompilationUnitRuntimeData, runtimeClasses) + sizeof(const StaticValue *));
- CompositeMetaTypeIds typeIdsForComponent(int objectid = 0) const;
+class Q_QML_EXPORT ExecutableCompilationUnit final
+ : public CompilationUnitRuntimeData,
+ public QQmlRefCounted<ExecutableCompilationUnit>
+{
+ Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit)
+public:
+ friend class QQmlRefCounted<ExecutableCompilationUnit>;
+ friend class QQmlRefPointer<ExecutableCompilationUnit>;
+ friend struct ExecutionEngine;
- CompositeMetaTypeIds typeIds;
- bool isRegistered = false;
+ ExecutionEngine *engine = nullptr;
- QHash<int, InlineComponentData> inlineComponentData;
+ QString finalUrlString() const { return m_compilationUnit->finalUrlString(); }
+ QString fileName() const { return m_compilationUnit->fileName(); }
- std::unique_ptr<CompilationUnitMapper> backingFile;
+ QUrl url() const { return m_compilationUnit->url(); }
+ QUrl finalUrl() const { return m_compilationUnit->finalUrl(); }
- // --- interface for QQmlPropertyCacheCreator
- using CompiledObject = CompiledData::Object;
- using CompiledFunction = CompiledData::Function;
- enum class ListPropertyAssignBehavior { Append, Replace, ReplaceIfNotDefault };
- ListPropertyAssignBehavior listPropertyAssignBehavior() const
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache() const
{
- if (data->flags & CompiledData::Unit::ListPropertyAssignReplace)
- return ListPropertyAssignBehavior::Replace;
- if (data->flags & CompiledData::Unit::ListPropertyAssignReplaceIfNotDefault)
- return ListPropertyAssignBehavior::ReplaceIfNotDefault;
- return ListPropertyAssignBehavior::Append;
+ return m_compilationUnit->typeNameCache;
}
- bool enforcesFunctionSignature() const
+ QQmlPropertyCacheVector *propertyCachesPtr()
{
- return data->flags & CompiledData::Unit::FunctionSignaturesEnforced;
+ return &m_compilationUnit->propertyCaches;
}
- bool nativeMethodsAcceptThisObjects() const
+ QQmlPropertyCache::ConstPtr rootPropertyCache() const
{
- return data->flags & CompiledData::Unit::NativeMethodsAcceptThisObject;
+ return m_compilationUnit->rootPropertyCache();
}
- bool valueTypesAreCopied() const
- {
- return data->flags & CompiledData::Unit::ValueTypesCopied;
- }
+ // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
+ // this is initialized on-demand by QQmlContextData
+ QHash<int, IdentifierHash> namedObjectsPerComponentCache;
+ inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
- int objectCount() const { return qmlData->nObjects; }
- const CompiledObject *objectAt(int index) const
+ int totalBindingsCount() const { return m_compilationUnit->totalBindingsCount(); }
+ int totalParserStatusCount() const { return m_compilationUnit->totalParserStatusCount(); }
+ int totalObjectCount() const { return m_compilationUnit->totalObjectCount(); }
+
+ ResolvedTypeReference *resolvedType(int id) const
{
- return qmlData->objectAt(index);
+ return m_compilationUnit->resolvedType(id);
}
- int importCount() const { return qmlData->nImports; }
- const CompiledData::Import *importAt(int index) const
+ QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const
{
- return qmlData->importAt(index);
+ return m_compilationUnit->qmlTypeForComponent(inlineComponentName);
}
- Heap::Object *templateObjectAt(int index) const;
-
- struct FunctionIterator
- {
- FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index)
- : unit(unit), object(object), index(index) {}
- const CompiledData::Unit *unit;
- const CompiledObject *object;
- int index;
-
- const CompiledFunction *operator->() const
- {
- return unit->functionAt(object->functionOffsetTable()[index]);
- }
-
- void operator++() { ++index; }
- bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; }
- bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; }
- };
+ QMetaType metaType() const { return m_compilationUnit->qmlType.typeId(); }
- FunctionIterator objectFunctionsBegin(const CompiledObject *object) const
+ int inlineComponentId(const QString &inlineComponentName) const
{
- return FunctionIterator(data, object, 0);
+ return m_compilationUnit->inlineComponentId(inlineComponentName);
}
- FunctionIterator objectFunctionsEnd(const CompiledObject *object) const
- {
- return FunctionIterator(data, object, object->nFunctions);
- }
+ // --- interface for QQmlPropertyCacheCreator
+ using CompiledObject = CompiledData::CompilationUnit::CompiledObject;
+ using CompiledFunction = CompiledData::CompilationUnit::CompiledFunction;
+ using CompiledBinding = CompiledData::CompilationUnit::CompiledBinding;
+ using IdToObjectMap = CompiledData::CompilationUnit::IdToObjectMap;
- bool isESModule() const
+ bool nativeMethodsAcceptThisObjects() const
{
- return data->flags & CompiledData::Unit::IsESModule;
+ return m_compilationUnit->nativeMethodsAcceptThisObjects();
}
- bool isSharedLibrary() const
+ bool ignoresFunctionSignature() const { return m_compilationUnit->ignoresFunctionSignature(); }
+ bool valueTypesAreCopied() const { return m_compilationUnit->valueTypesAreCopied(); }
+ bool valueTypesAreAddressable() const { return m_compilationUnit->valueTypesAreAddressable(); }
+ bool componentsAreBound() const { return m_compilationUnit->componentsAreBound(); }
+ bool isESModule() const { return m_compilationUnit->isESModule(); }
+
+ int objectCount() const { return m_compilationUnit->objectCount(); }
+ const CompiledObject *objectAt(int index) const
{
- return data->flags & CompiledData::Unit::IsSharedLibrary;
+ return m_compilationUnit->objectAt(index);
}
- QStringList moduleRequests() const;
- Heap::Module *instantiate(ExecutionEngine *engine);
+ Heap::Object *templateObjectAt(int index) const;
+
+ Heap::Module *instantiate();
const Value *resolveExport(QV4::String *exportName)
{
QVector<ResolveSetEntry> resolveSet;
@@ -255,17 +166,18 @@ public:
void evaluate();
void evaluateModuleRequests();
- QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
- void unlink();
-
- void markObjects(MarkStack *markStack);
-
- bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
-
- static QString localCacheFilePath(const QUrl &url);
- bool saveToDisk(const QUrl &unitUrl, QString *errorString);
+ void mark(MarkStack *markStack) const { markObjects(markStack); }
+ void markObjects(MarkStack *markStack) const;
QString bindingValueAsString(const CompiledData::Binding *binding) const;
+ double bindingValueAsNumber(const CompiledData::Binding *binding) const
+ {
+ return m_compilationUnit->bindingValueAsNumber(binding);
+ }
+ QString bindingValueAsScriptString(const CompiledData::Binding *binding) const
+ {
+ return m_compilationUnit->bindingValueAsScriptString(binding);
+ }
struct TranslationDataIndex
{
@@ -275,13 +187,53 @@ public:
QString translateFrom(TranslationDataIndex index) const;
- static bool verifyHeader(const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp,
- QString *errorString);
+ Heap::Module *module() const { return m_module; }
+ void setModule(Heap::Module *module) { m_module = module; }
+
+ const CompiledData::Unit *unitData() const { return m_compilationUnit->data; }
+
+ QString stringAt(uint index) const { return m_compilationUnit->stringAt(index); }
+
+ const QVector<QQmlRefPointer<QQmlScriptData>> *dependentScriptsPtr() const
+ {
+ return &m_compilationUnit->dependentScripts;
+ }
+
+ const CompiledData::BindingPropertyData *bindingPropertyDataPerObjectAt(
+ qsizetype objectIndex) const
+ {
+ return &m_compilationUnit->bindingPropertyDataPerObject.at(objectIndex);
+ }
+
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &baseCompilationUnit() const
+ {
+ return m_compilationUnit;
+ }
+
+ QV4::Function *rootFunction()
+ {
+ if (!runtimeStrings)
+ populate();
+
+ const auto *data = unitData();
+ return data->indexOfRootFunction != -1
+ ? runtimeFunctions[data->indexOfRootFunction]
+ : nullptr;
+ }
+
+ void populate();
+ void clear();
+
protected:
quint32 totalStringCount() const
- { return data->stringTableSize; }
+ { return unitData()->stringTableSize; }
private:
+ friend struct ExecutionEngine;
+
+ QQmlRefPointer<CompiledData::CompilationUnit> m_compilationUnit;
+ Heap::Module *m_module = nullptr;
+
struct ResolveSetEntry
{
ResolveSetEntry() {}
@@ -292,9 +244,13 @@ private:
};
ExecutableCompilationUnit();
- ExecutableCompilationUnit(CompiledData::CompilationUnit &&compilationUnit);
+ ExecutableCompilationUnit(QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit);
~ExecutableCompilationUnit();
+ static QQmlRefPointer<ExecutableCompilationUnit> create(
+ QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit,
+ ExecutionEngine *engine);
+
const Value *resolveExportRecursively(QV4::String *exportName,
QVector<ResolveSetEntry> *resolveSet);
@@ -312,8 +268,8 @@ private:
IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
{
- auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
- if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
+ auto it = namedObjectsPerComponentCache.constFind(componentObjectIndex);
+ if (Q_UNLIKELY(it == namedObjectsPerComponentCache.cend()))
return createNamedObjectsPerComponent(componentObjectIndex);
Q_ASSERT(!it->isEmpty());
return *it;
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 29e3d8d7ef..ae36b563e0 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -41,8 +41,8 @@ bool Function::call(QObject *thisObject, void **a, const QMetaType *types, int a
}
static ReturnedValue doCall(
- Function *self, const Value *thisObject, const Value *argv, int argc,
- ExecutionContext *context)
+ QV4::Function *self, const QV4::Value *thisObject, const QV4::Value *argv, int argc,
+ QV4::ExecutionContext *context)
{
ExecutionEngine *engine = context->engine();
JSTypesStackFrame frame;
@@ -61,15 +61,15 @@ ReturnedValue Function::call(
switch (kind) {
case AotCompiled:
return QV4::convertAndCall(
- context->engine(), typedFunction, thisObject, argv, argc,
+ context->engine(), &aotCompiledFunction, thisObject, argv, argc,
[this, context](
QObject *thisObject, void **a, const QMetaType *types, int argc) {
call(thisObject, a, types, argc, context);
});
case JsTyped:
return QV4::coerceAndCall(
- context->engine(), typedFunction, thisObject, argv, argc,
- [this, context](const Value *thisObject, const Value *argv, int argc) {
+ context->engine(), &jsTypedFunction, compiledFunction, argv, argc,
+ [this, context, thisObject](const Value *argv, int argc) {
return doCall(this, thisObject, argv, argc, context);
});
default:
@@ -81,7 +81,7 @@ ReturnedValue Function::call(
Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
const CompiledData::Function *function,
- const QQmlPrivate::TypedFunction *aotFunction)
+ const QQmlPrivate::AOTCompiledFunction *aotFunction)
{
return new Function(engine, unit, function, aotFunction);
}
@@ -91,16 +91,24 @@ void Function::destroy()
delete this;
}
+void Function::mark(MarkStack *ms)
+{
+ if (internalClass)
+ internalClass->mark(ms);
+}
+
+static bool isSpecificType(const CompiledData::ParameterType &type)
+{
+ return type.typeNameIndexOrCommonType()
+ != (type.indexIsCommonType() ? quint32(CompiledData::CommonType::Invalid) : 0);
+}
+
Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
const CompiledData::Function *function,
- const QQmlPrivate::TypedFunction *aotFunction)
- : FunctionData(unit)
+ const QQmlPrivate::AOTCompiledFunction *aotFunction)
+ : FunctionData(engine, unit)
, compiledFunction(function)
, codeData(function->code())
- , jittedCode(nullptr)
- , codeRef(nullptr)
- , typedFunction(aotFunction)
- , kind(aotFunction ? AotCompiled : JsUntyped)
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
@@ -111,74 +119,59 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable();
- const bool enforcesSignature = !aotFunction && unit->enforcesFunctionSignature();
- bool hasTypes = false;
+ bool enforceJsTypes = !unit->ignoresFunctionSignature();
+
for (quint32 i = 0; i < compiledFunction->nFormals; ++i) {
ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable);
- if (enforcesSignature
- && !hasTypes
- && formalsIndices[i].type.typeNameIndexOrBuiltinType()
- != quint32(QV4::CompiledData::BuiltinType::InvalidBuiltin)) {
- hasTypes = true;
- }
+ if (enforceJsTypes && !isSpecificType(formalsIndices[i].type))
+ enforceJsTypes = false;
}
- internalClass = ic->d();
+ internalClass.set(engine, ic->d());
nFormals = compiledFunction->nFormals;
- if (!enforcesSignature)
+ if (!enforceJsTypes)
return;
- if (!hasTypes
- && compiledFunction->returnType.typeNameIndexOrBuiltinType()
- == quint32(QV4::CompiledData::BuiltinType::InvalidBuiltin)) {
+ if (aotFunction) {
+ aotCompiledCode = aotFunction->functionPtr;
+ new (&aotCompiledFunction) AOTCompiledFunction;
+ kind = AotCompiled;
+ aotCompiledFunction.types.resize(aotFunction->numArguments + 1);
+ aotFunction->signature(unit, aotCompiledFunction.types.data());
return;
}
- QQmlPrivate::TypedFunction *synthesized = new QQmlPrivate::TypedFunction;
- QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine->qmlEngine());
-
- auto findMetaType = [&](const CompiledData::ParameterType &param) {
- const quint32 type = param.typeNameIndexOrBuiltinType();
- if (param.indexIsBuiltinType()) {
- if (param.isList()) {
- return QQmlPropertyCacheCreatorBase::listTypeForPropertyType(
- QV4::CompiledData::BuiltinType(type));
- }
- return QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(
- QV4::CompiledData::BuiltinType(type));
- }
-
- if (type == 0)
- return QMetaType();
-
- const QQmlType qmltype = unit->typeNameCache->query(unit->stringAt(type)).type;
- if (!qmltype.isValid())
- return QMetaType();
+ // If a function has any typed arguments, but an untyped return value, the return value is void.
+ // If it doesn't have any arguments at all and the return value is untyped, the function is
+ // untyped. Users can specifically set the return type to "void" to have it enforced.
+ if (nFormals == 0 && !isSpecificType(compiledFunction->returnType))
+ return;
- const QMetaType metaType = param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
- if (metaType.isValid())
- return metaType;
+ QQmlTypeLoader *typeLoader = engine->typeLoader();
- if (!qmltype.isComposite()) {
- if (!qmltype.isInlineComponentType())
- return QMetaType();
- const CompositeMetaTypeIds typeIds
- = unit->typeIdsForComponent(qmltype.inlineComponentId());
- return param.isList() ? typeIds.listId : typeIds.id;
+ auto findQmlType = [&](const CompiledData::ParameterType &param) {
+ const quint32 type = param.typeNameIndexOrCommonType();
+ if (param.indexIsCommonType()) {
+ return QQmlMetaType::qmlType(QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(
+ QV4::CompiledData::CommonType(type)));
}
- const CompositeMetaTypeIds typeIds = enginePrivate->typeLoader.getType(
- qmltype.sourceUrl())->compilationUnit()->typeIds;
- return param.isList() ? typeIds.listId : typeIds.id;
- };
+ if (type == 0 || !typeLoader)
+ return QQmlType();
- for (quint16 i = 0; i < nFormals; ++i)
- synthesized->argumentTypes.append(findMetaType(formalsIndices[i].type));
+ const auto &base = unit->baseCompilationUnit();
+ const QQmlType qmltype = QQmlTypePrivate::compositeQmlType(
+ base, typeLoader, base->stringAt(type));
+ return qmltype.typeId().isValid() ? qmltype : QQmlType();
+ };
- synthesized->returnType = findMetaType(compiledFunction->returnType);
- typedFunction = synthesized;
+ new (&jsTypedFunction) JSTypedFunction;
kind = JsTyped;
+ jsTypedFunction.types.reserve(nFormals + 1);
+ jsTypedFunction.types.append(findQmlType(compiledFunction->returnType));
+ for (quint16 i = 0; i < nFormals; ++i)
+ jsTypedFunction.types.append(findQmlType(formalsIndices[i].type));
}
Function::~Function()
@@ -187,8 +180,18 @@ Function::~Function()
destroyFunctionTable(this, codeRef);
delete codeRef;
}
- if (kind == JsTyped)
- delete typedFunction;
+
+ switch (kind) {
+ case JsTyped:
+ jsTypedFunction.~JSTypedFunction();
+ break;
+ case AotCompiled:
+ aotCompiledFunction.~AOTCompiledFunction();
+ break;
+ case JsUntyped:
+ case Eval:
+ break;
+ }
}
void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters)
@@ -219,22 +222,23 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
}
- internalClass = engine->internalClasses(EngineBase::Class_CallContext);
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i) {
- internalClass = internalClass->addMember(
+ ic = ic->addMember(
engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]),
Attr_NotConfigurable);
}
- Scope scope(engine);
ScopedString arg(scope);
for (const QString &parameterName : parameterNames) {
arg = engine->newIdentifier(parameterName);
- internalClass = internalClass->addMember(arg->propertyKey(), Attr_NotConfigurable);
+ ic = ic->addMember(arg->propertyKey(), Attr_NotConfigurable);
}
+ internalClass.set(engine, ic->d());
nFormals = parameters.size();
}
@@ -255,6 +259,11 @@ QQmlSourceLocation Function::sourceLocation() const
sourceFile(), compiledFunction->location.line(), compiledFunction->location.column());
}
+FunctionData::FunctionData(EngineBase *engine, ExecutableCompilationUnit *compilationUnit_)
+{
+ compilationUnit.set(engine, compilationUnit_);
+}
+
} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 181c88a1e2..7543dd3c4b 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -30,16 +30,15 @@ struct QQmlSourceLocation;
namespace QV4 {
-struct Q_QML_EXPORT FunctionData {
- CompiledData::CompilationUnitBase *compilationUnit;
+struct Q_QML_EXPORT FunctionData
+{
+ WriteBarrier::HeapObjectWrapper<CompilationUnitRuntimeData, 1> compilationUnit;
// Intentionally require an ExecutableCompilationUnit but save only a pointer to
// CompilationUnitBase. This is so that we can take advantage of the standard layout
// of CompilationUnitBase in the JIT. Furthermore we can safely static_cast to
// ExecutableCompilationUnit where we need it.
- FunctionData(ExecutableCompilationUnit *compilationUnit)
- : compilationUnit(compilationUnit)
- {}
+ FunctionData(EngineBase *engine, ExecutableCompilationUnit *compilationUnit_);
};
// Make sure this class can be accessed through offsetof (done by the assemblers):
Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value);
@@ -47,16 +46,22 @@ Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value);
struct Q_QML_EXPORT Function : public FunctionData {
protected:
Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
- const CompiledData::Function *function, const QQmlPrivate::TypedFunction *aotFunction);
+ const CompiledData::Function *function, const QQmlPrivate::AOTCompiledFunction *aotFunction);
~Function();
public:
- const CompiledData::Function *compiledFunction;
+ struct JSTypedFunction {
+ QVarLengthArray<QQmlType, 4> types;
+ };
+
+ struct AOTCompiledFunction {
+ QVarLengthArray<QMetaType, 4> types;
+ };
QV4::ExecutableCompilationUnit *executableCompilationUnit() const
{
// This is safe: We require an ExecutableCompilationUnit in the ctor.
- return static_cast<QV4::ExecutableCompilationUnit *>(compilationUnit);
+ return static_cast<QV4::ExecutableCompilationUnit *>(compilationUnit.get());
}
QV4::Heap::String *runtimeString(uint i) const
@@ -69,26 +74,39 @@ public:
ReturnedValue call(const Value *thisObject, const Value *argv, int argc,
ExecutionContext *context);
- const char *codeData;
+ const CompiledData::Function *compiledFunction = nullptr;
+ const char *codeData = nullptr;
+ JSC::MacroAssemblerCodeRef *codeRef = nullptr;
typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *);
- JittedCode jittedCode;
- JSC::MacroAssemblerCodeRef *codeRef;
- const QQmlPrivate::TypedFunction *typedFunction = nullptr;
+ typedef void (*AotCompiledCode)(const QQmlPrivate::AOTCompiledContext *context, void **argv);
+
+ union {
+ void *noFunction = nullptr;
+ JSTypedFunction jsTypedFunction;
+ AOTCompiledFunction aotCompiledFunction;
+ };
+
+ union {
+ JittedCode jittedCode = nullptr;
+ AotCompiledCode aotCompiledCode;
+ };
// first nArguments names in internalClass are the actual arguments
- Heap::InternalClass *internalClass;
+ QV4::WriteBarrier::Pointer<Heap::InternalClass> internalClass;
int interpreterCallCount = 0;
- quint16 nFormals;
+ quint16 nFormals = 0;
enum Kind : quint8 { JsUntyped, JsTyped, AotCompiled, Eval };
Kind kind = JsUntyped;
bool detectedInjectedParameters = false;
static Function *create(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
const CompiledData::Function *function,
- const QQmlPrivate::TypedFunction *aotFunction);
+ const QQmlPrivate::AOTCompiledFunction *aotFunction);
void destroy();
+ void mark(QV4::MarkStack *ms);
+
// used when dynamically assigning signal handlers (QQmlConnection)
void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters);
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 84ba0cfcfb..ab6a34435f 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -256,7 +256,7 @@ QQmlRefPointer<ExecutableCompilationUnit> FunctionCtor::parse(ExecutionEngine *e
if (engine->hasException)
return nullptr;
- return ExecutableCompilationUnit::create(cg.generateCompilationUnit());
+ return engine->insertCompilationUnit(cg.generateCompilationUnit());
}
ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
@@ -268,7 +268,7 @@ ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, co
if (engine->hasException)
return Encode::undefined();
- Function *vmf = compilationUnit->linkToEngine(engine);
+ Function *vmf = compilationUnit->rootFunction();
ExecutionContext *global = engine->scriptContext();
ReturnedValue o = Encode(FunctionObject::createScriptFunction(global, vmf));
@@ -428,8 +428,8 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu
}
ScopedContext ctx(scope, target->scope());
- Heap::BoundFunction *bound = BoundFunction::create(ctx, target, boundThis, boundArgs);
- bound->setFunction(target->function());
+ Scoped<BoundFunction> bound(scope, BoundFunction::create(ctx, target, boundThis, boundArgs));
+ bound->d()->setFunction(target->function());
return bound->asReturnedValue();
}
@@ -507,7 +507,8 @@ void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, QObject *
frame.pop(scope.engine);
}
-static ReturnedValue doCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
+static ReturnedValue qfoDoCall(const QV4::FunctionObject *fo, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc)
{
ExecutionEngine *engine = fo->engine();
JSTypesStackFrame frame;
@@ -531,27 +532,28 @@ static ReturnedValue doCall(const FunctionObject *fo, const Value *thisObject, c
return result;
}
-ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue ArrowFunction::virtualCall(const QV4::FunctionObject *fo, const Value *thisObject,
+ const QV4::Value *argv, int argc)
{
Function *function = fo->function();
switch (function->kind) {
case Function::AotCompiled:
return QV4::convertAndCall(
- fo->engine(), function->typedFunction, thisObject, argv, argc,
+ fo->engine(), &function->aotCompiledFunction, thisObject, argv, argc,
[fo](QObject *thisObject, void **a, const QMetaType *types, int argc) {
ArrowFunction::virtualCallWithMetaTypes(fo, thisObject, a, types, argc);
});
case Function::JsTyped:
return QV4::coerceAndCall(
- fo->engine(), function->typedFunction, thisObject, argv, argc,
- [fo](const Value *thisObject, const Value *argv, int argc) {
- return doCall(fo, thisObject, argv, argc);
+ fo->engine(), &function->jsTypedFunction, function->compiledFunction, argv, argc,
+ [fo, thisObject](const Value *argv, int argc) {
+ return qfoDoCall(fo, thisObject, argv, argc);
});
default:
break;
}
- return doCall(fo, thisObject, argv, argc);
+ return qfoDoCall(fo, thisObject, argv, argc);
}
void Heap::ArrowFunction::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n)
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index e32fce28ba..573848f62a 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -51,14 +51,14 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) {
return jsConstruct != nullptr;
}
- Q_QML_PRIVATE_EXPORT void init(
+ Q_QML_EXPORT void init(
QV4::ExecutionContext *scope, QV4::String *name,
VTable::Call call, VTable::CallWithMetaTypes callWithMetaTypes = nullptr);
- Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr);
- Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr);
- Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, const QString &name);
- Q_QML_PRIVATE_EXPORT void init();
- Q_QML_PRIVATE_EXPORT void destroy();
+ Q_QML_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr);
+ Q_QML_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr);
+ Q_QML_EXPORT void init(QV4::ExecutionContext *scope, const QString &name);
+ Q_QML_EXPORT void init();
+ Q_QML_EXPORT void destroy();
void setFunction(Function *f);
@@ -74,9 +74,11 @@ struct FunctionPrototype : FunctionObject {
void init();
};
+// A function object with an additional index into a list.
+// Used by Models to refer to property roles.
struct IndexedBuiltinFunction : FunctionObject {
- inline void init(QV4::ExecutionContext *scope, uint index, VTable::Call call);
- uint index;
+ inline void init(QV4::ExecutionContext *scope, qsizetype index, VTable::Call call);
+ qsizetype index;
};
struct ArrowFunction : FunctionObject {
@@ -230,12 +232,13 @@ struct FunctionPrototype: FunctionObject
static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
-struct Q_QML_PRIVATE_EXPORT IndexedBuiltinFunction : FunctionObject
+struct Q_QML_EXPORT IndexedBuiltinFunction : FunctionObject
{
V4_OBJECT2(IndexedBuiltinFunction, FunctionObject)
};
-void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index, VTable::Call call)
+void Heap::IndexedBuiltinFunction::init(
+ QV4::ExecutionContext *scope, qsizetype index, VTable::Call call)
{
Heap::FunctionObject::init(scope);
this->jsCall = call;
@@ -249,7 +252,8 @@ struct ArrowFunction : FunctionObject {
static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *thisObject,
void **a, const QMetaType *types, int argc);
- static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(const QV4::FunctionObject *f, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc);
};
struct ScriptFunction : ArrowFunction {
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
index 9f77d83ac3..e7a63ba185 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -26,7 +26,7 @@ ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObje
if (engine->hasException)
return Encode::undefined();
- Function *vmf = compilationUnit->linkToEngine(engine);
+ Function *vmf = compilationUnit->rootFunction();
ExecutionContext *global = engine->scriptContext();
ReturnedValue o = Encode(GeneratorFunction::create(global, vmf));
@@ -62,7 +62,7 @@ ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Valu
ExecutionEngine *engine = gf->engine();
Scope scope(gf);
- Scoped<GeneratorObject> g(scope, engine->memoryManager->allocManaged<GeneratorObject>(sizeof(GeneratorObject::Data), engine->classes[EngineBase::Class_GeneratorObject]));
+ Scoped<GeneratorObject> g(scope, engine->memoryManager->allocManaged<GeneratorObject>(engine->classes[EngineBase::Class_GeneratorObject]));
g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype())));
// We need to set up a separate JSFrame for the generator, as it's being re-entered
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 8279fa00f5..d15fb356c4 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -191,6 +191,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(PropertyFlags)
struct PropertyAttributes
{
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_MSVC(4201) // nonstandard extension used: nameless struct/union
union {
uchar m_all;
struct {
@@ -208,6 +210,7 @@ struct PropertyAttributes
uchar configurable_set : 1;
};
};
+ QT_WARNING_POP
enum Type {
Data = 0,
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index 32e93cedb4..37548ffc9f 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -2,25 +2,28 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qv4globalobject_p.h"
-#include <private/qv4mm_p.h>
-#include "qv4value_p.h"
-#include "qv4context_p.h"
-#include "qv4function_p.h"
-#include "qv4script_p.h"
-#include "qv4scopedvalue_p.h"
-#include "qv4string_p.h"
-#include <private/qv4codegen_p.h>
#include <private/qv4alloca_p.h>
-#include "private/qlocale_tools_p.h"
-#include "private/qtools_p.h"
-
-#include <QtCore/QDebug>
-#include <QtCore/QString>
-#include <iostream>
+#include <private/qv4codegen_p.h>
+#include <private/qv4context_p.h>
+#include <private/qv4function_p.h>
+#include <private/qv4mm_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qv4script_p.h>
+#include <private/qv4stackframe_p.h>
+#include <private/qv4string_p.h>
+#include <private/qv4value_p.h>
#include <wtf/MathExtras.h>
+#include <QtCore/private/qlocale_tools_p.h>
+#include <QtCore/private/qtools_p.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qstring.h>
+
+#include <iostream>
+
using namespace QV4;
using QtMiscUtils::toHexUpper;
using QtMiscUtils::fromHex;
diff --git a/src/qml/jsruntime/qv4identifierhash.cpp b/src/qml/jsruntime/qv4identifierhash.cpp
index 3cc8aa2194..48df2283f0 100644
--- a/src/qml/jsruntime/qv4identifierhash.cpp
+++ b/src/qml/jsruntime/qv4identifierhash.cpp
@@ -91,7 +91,7 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const
if (!d)
return nullptr;
- PropertyKey id = d->identifierTable->asPropertyKey(str);
+ PropertyKey id = d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId);
return lookup(id);
}
@@ -110,7 +110,7 @@ inline
const PropertyKey IdentifierHash::toIdentifier(const QString &str) const
{
Q_ASSERT(d);
- return d->identifierTable->asPropertyKey(str);
+ return d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId);
}
inline
diff --git a/src/qml/jsruntime/qv4identifierhash_p.h b/src/qml/jsruntime/qv4identifierhash_p.h
index ad3c179502..6c77a78f85 100644
--- a/src/qml/jsruntime/qv4identifierhash_p.h
+++ b/src/qml/jsruntime/qv4identifierhash_p.h
@@ -24,7 +24,7 @@ namespace QV4 {
struct IdentifierHashEntry;
struct IdentifierHashData;
-struct Q_QML_PRIVATE_EXPORT IdentifierHash
+struct Q_QML_EXPORT IdentifierHash
{
IdentifierHash() = default;
IdentifierHash(ExecutionEngine *engine);
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index 96aa54018b..4c915442f4 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -36,7 +36,7 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str)
if (str->subtype == Heap::String::StringType_ArrayIndex)
return;
- str->identifier = PropertyKey::fromStringOrSymbol(str);
+ str->identifier = PropertyKey::fromStringOrSymbol(engine, str);
bool grow = (alloc <= size*2);
@@ -165,6 +165,9 @@ PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str)
while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
if (e->stringHash == hash && e->toQString() == str->toQString()) {
str->identifier = e->identifier;
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) {
+ e->identifier.asStringOrSymbol()->mark(stack);
+ });
return e->identifier;
}
++idx;
@@ -249,24 +252,20 @@ void IdentifierTable::sweep()
size -= freed;
}
-PropertyKey IdentifierTable::asPropertyKey(const QString &s)
+PropertyKey IdentifierTable::asPropertyKey(const QString &s,
+ IdentifierTable::KeyConversionBehavior conversionBehvior)
{
uint subtype;
- const uint hash = String::createHashValue(s.constData(), s.size(), &subtype);
- if (subtype == Heap::String::StringType_ArrayIndex)
- return PropertyKey::fromArrayIndex(hash);
+ uint hash = String::createHashValue(s.constData(), s.size(), &subtype);
+ if (subtype == Heap::String::StringType_ArrayIndex) {
+ if (Q_UNLIKELY(conversionBehvior == ForceConversionToId))
+ hash = String::createHashValueDisallowingArrayIndex(s.constData(), s.size(), &subtype);
+ else
+ return PropertyKey::fromArrayIndex(hash);
+ }
return resolveStringEntry(s, hash, subtype)->identifier;
}
-PropertyKey IdentifierTable::asPropertyKey(const char *s, int len)
-{
- uint subtype;
- uint hash = String::createHashValue(s, len, &subtype);
- if (subtype == Heap::String::StringType_ArrayIndex)
- return PropertyKey::fromArrayIndex(hash);
- return resolveStringEntry(QString::fromLatin1(s, len), hash, subtype)->identifier;
-}
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h
index 09e07fe5b8..2ecd4a7294 100644
--- a/src/qml/jsruntime/qv4identifiertable_p.h
+++ b/src/qml/jsruntime/qv4identifiertable_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-struct Q_QML_PRIVATE_EXPORT IdentifierTable
+struct Q_QML_EXPORT IdentifierTable
{
ExecutionEngine *engine;
@@ -55,8 +55,8 @@ public:
return asPropertyKey(str->d());
}
- PropertyKey asPropertyKey(const QString &s);
- PropertyKey asPropertyKey(const char *s, int len);
+ enum KeyConversionBehavior { Default, ForceConversionToId };
+ PropertyKey asPropertyKey(const QString &s, KeyConversionBehavior conversionBehavior = Default);
PropertyKey asPropertyKeyImpl(const Heap::String *str);
diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp
index 67572b50a9..76545ba692 100644
--- a/src/qml/jsruntime/qv4include.cpp
+++ b/src/qml/jsruntime/qv4include.cpp
@@ -26,7 +26,7 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine,
: QObject(engine->jsEngine())
, v4(engine), m_url(url)
#if QT_CONFIG(qml_network)
- , m_redirectCount(0), m_network(nullptr) , m_reply(nullptr)
+ , m_network(nullptr) , m_reply(nullptr)
#endif
{
if (qmlContext)
@@ -104,27 +104,9 @@ QV4::ReturnedValue QV4Include::result()
return m_resultObject.value();
}
-#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15
void QV4Include::finished()
{
#if QT_CONFIG(qml_network)
- m_redirectCount++;
-
- if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) {
- QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- m_url = m_url.resolved(redirect.toUrl());
- delete m_reply;
-
- QNetworkRequest request;
- request.setUrl(m_url);
-
- m_reply = m_network->get(request);
- QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
- return;
- }
- }
-
QV4::Scope scope(v4);
QV4::ScopedObject resultObj(scope, m_resultObject.value());
QV4::ScopedString status(scope, v4->newString(QStringLiteral("status")));
diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h
index c5f6560b08..c6ac98c761 100644
--- a/src/qml/jsruntime/qv4include_p.h
+++ b/src/qml/jsruntime/qv4include_p.h
@@ -63,7 +63,6 @@ private:
QUrl m_url;
#if QT_CONFIG(qml_network)
- int m_redirectCount;
QNetworkAccessManager *m_network;
QPointer<QNetworkReply> m_reply;
#endif
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index c72effd406..228a6bcd36 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -67,12 +67,11 @@ void PropertyHash::detach(bool grow, int classSize)
SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyKey> &other)
: refcount(1),
- engine(other.engine),
- data(nullptr)
+ engine(other.engine)
{
if (other.alloc()) {
const uint s = other.size();
- data = MemberData::allocate(engine, other.alloc(), other.data);
+ data.set(engine, MemberData::allocate(engine, other.alloc(), other.data));
setSize(s);
}
}
@@ -82,7 +81,7 @@ SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(cons
: refcount(1),
engine(other.engine)
{
- data = MemberData::allocate(engine, other.alloc(), nullptr);
+ data.set(engine, MemberData::allocate(engine, other.alloc(), nullptr));
memcpy(data, other.data, sizeof(Heap::MemberData) - sizeof(Value) + pos*sizeof(Value));
data->values.size = pos + 1;
data->values.set(engine, pos, Value::fromReturnedValue(value.id()));
@@ -92,7 +91,7 @@ void SharedInternalClassDataPrivate<PropertyKey>::grow()
{
const uint a = alloc() * 2;
const uint s = size();
- data = MemberData::allocate(engine, a, data);
+ data.set(engine, MemberData::allocate(engine, a, data));
setSize(s);
Q_ASSERT(alloc() >= a);
}
@@ -122,6 +121,11 @@ PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) const
void SharedInternalClassDataPrivate<PropertyKey>::set(uint i, PropertyKey t)
{
Q_ASSERT(data && i < size());
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) {
+ if constexpr (QV4::WriteBarrier::isInsertionBarrier)
+ if (auto string = t.asStringOrSymbol())
+ string->mark(stack);
+ });
data->values.values[i].rawValueRef() = t.id();
}
@@ -227,7 +231,7 @@ void InternalClass::init(ExecutionEngine *engine)
new (&propertyTable) PropertyHash();
new (&nameMap) SharedInternalClassData<PropertyKey>(engine);
new (&propertyData) SharedInternalClassData<PropertyAttributes>(engine);
- new (&transitions) std::vector<Transition>();
+ new (&transitions) QVarLengthArray<Transition, 1>();
this->engine = engine;
vtable = QV4::InternalClass::staticVTable();
@@ -244,7 +248,7 @@ void InternalClass::init(Heap::InternalClass *other)
new (&propertyTable) PropertyHash(other->propertyTable);
new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap);
new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData);
- new (&transitions) std::vector<Transition>();
+ new (&transitions) QVarLengthArray<Transition, 1>();
engine = other->engine;
vtable = other->vtable;
@@ -256,6 +260,11 @@ void InternalClass::init(Heap::InternalClass *other)
protoId = engine->newProtoId();
internalClass.set(engine, other->internalClass);
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) {
+ if constexpr (QV4::WriteBarrier::isInsertionBarrier) {
+ other->mark(stack);
+ }
+ });
}
void InternalClass::destroy()
@@ -275,14 +284,20 @@ void InternalClass::destroy()
propertyTable.~PropertyHash();
nameMap.~SharedInternalClassData<PropertyKey>();
propertyData.~SharedInternalClassData<PropertyAttributes>();
- transitions.~vector<Transition>();
+ transitions.~QVarLengthArray<Transition, 1>();
engine = nullptr;
Base::destroy();
}
-QString InternalClass::keyAt(uint index) const
+ReturnedValue InternalClass::keyAt(uint index) const
{
- return nameMap.at(index).toQString();
+ PropertyKey key = nameMap.at(index);
+ if (!key.isValid())
+ return Encode::undefined();
+ if (key.isArrayIndex())
+ return Encode(key.asArrayIndex());
+ Q_ASSERT(key.isStringOrSymbol());
+ return key.asStringOrSymbol()->asReturnedValue();
}
void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry)
@@ -296,7 +311,7 @@ void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAt
InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalClassTransition &t)
{
- std::vector<Transition>::iterator it = std::lower_bound(transitions.begin(), transitions.end(), t);
+ QVarLengthArray<Transition, 1>::iterator it = std::lower_bound(transitions.begin(), transitions.end(), t);
if (it != transitions.end() && *it == t) {
return *it;
} else {
@@ -328,7 +343,7 @@ static Heap::InternalClass *cleanInternalClass(Heap::InternalClass *orig)
// We will generally add quite a few transitions here. We have 255 redundant ones.
// We can expect at least as many significant ones in addition.
- std::vector<InternalClassTransition> transitions;
+ QVarLengthArray<InternalClassTransition, 1> transitions;
Scope scope(orig->engine);
Scoped<QV4::InternalClass> child(scope, orig);
@@ -470,6 +485,10 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
// create a new class and add it to the tree
Heap::InternalClass *newClass = engine->newClass(this);
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) {
+ if (proto && QV4::WriteBarrier::isInsertionBarrier)
+ proto->mark(stack);
+ });
newClass->prototype = proto;
t.lookup = newClass;
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index df73ee6ccc..56ce787859 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -17,6 +17,7 @@
#include "qv4global_p.h"
#include <QHash>
+#include <QVarLengthArray>
#include <climits> // for UINT_MAX
#include <private/qv4propertykey_p.h>
#include <private/qv4heap_p.h>
@@ -132,6 +133,8 @@ struct SharedInternalClassDataPrivate<PropertyAttributes> {
void grow();
+ void markIfNecessary(const PropertyAttributes &) {}
+
uint alloc() const { return m_alloc; }
uint size() const { return m_size; }
void setSize(uint s) { m_size = s; }
@@ -174,11 +177,14 @@ private:
template<>
struct SharedInternalClassDataPrivate<PropertyKey> {
- SharedInternalClassDataPrivate(ExecutionEngine *e) : refcount(1), engine(e), data(nullptr) {}
+ SharedInternalClassDataPrivate(ExecutionEngine *e) : refcount(1), engine(e) {}
SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other);
SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, PropertyKey value);
~SharedInternalClassDataPrivate() {}
+ template<typename StringOrSymbol = Heap::StringOrSymbol>
+ void markIfNecessary(const PropertyKey &value);
+
void grow();
uint alloc() const;
uint size() const;
@@ -192,9 +198,20 @@ struct SharedInternalClassDataPrivate<PropertyKey> {
int refcount = 1;
private:
ExecutionEngine *engine;
- Heap::MemberData *data;
+ WriteBarrier::Pointer<Heap::MemberData> data;
};
+template<typename StringOrSymbol>
+void QV4::SharedInternalClassDataPrivate<PropertyKey>::markIfNecessary(const PropertyKey &value)
+{
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) {
+ if constexpr (QV4::WriteBarrier::isInsertionBarrier) {
+ if (auto s = value.asStringOrSymbol<StringOrSymbol>())
+ s->mark(stack);
+ }
+ });
+}
+
template <typename T>
struct SharedInternalClassData {
using Private = SharedInternalClassDataPrivate<T>;
@@ -222,6 +239,7 @@ struct SharedInternalClassData {
}
void add(uint pos, T value) {
+ d->markIfNecessary(value);
if (pos < d->size()) {
Q_ASSERT(d->refcount > 1);
// need to detach
@@ -243,6 +261,7 @@ struct SharedInternalClassData {
void set(uint pos, T value) {
Q_ASSERT(pos < d->size());
+ d->markIfNecessary(value);
if (d->refcount > 1) {
// need to detach
Private *dd = new Private(*d);
@@ -315,7 +334,7 @@ struct InternalClass : Base {
SharedInternalClassData<PropertyAttributes> propertyData;
typedef InternalClassTransition Transition;
- std::vector<Transition> transitions;
+ QVarLengthArray<Transition, 1> transitions;
InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t);
uint size;
@@ -332,7 +351,7 @@ struct InternalClass : Base {
void init(InternalClass *other);
void destroy();
- Q_QML_PRIVATE_EXPORT QString keyAt(uint index) const;
+ Q_QML_EXPORT ReturnedValue keyAt(uint index) const;
Q_REQUIRED_RESULT InternalClass *nonExtensible();
Q_REQUIRED_RESULT InternalClass *locked();
diff --git a/src/qml/jsruntime/qv4jscall.cpp b/src/qml/jsruntime/qv4jscall.cpp
index 350e0fe542..513ae59145 100644
--- a/src/qml/jsruntime/qv4jscall.cpp
+++ b/src/qml/jsruntime/qv4jscall.cpp
@@ -3,6 +3,8 @@
#include "qv4jscall_p.h"
+#include <QtQml/qqmlinfo.h>
+
#include <private/qqmlengine_p.h>
#include <private/qv4qobjectwrapper_p.h>
@@ -20,4 +22,25 @@ void QV4::populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall,
jsCall.args[ii] = v4->metaTypeToJS(types[ii], args[ii + 1]);
}
+void QV4::warnAboutCoercionToVoid(
+ ExecutionEngine *engine, const Value &value, CoercionProblem problem)
+{
+ auto log = qCritical().nospace().noquote();
+ if (const CppStackFrame *frame = engine->currentStackFrame)
+ log << frame->source() << ':' << frame->lineNumber() << ": ";
+ log << value.toQStringNoThrow()
+ << " should be coerced to void because";
+ switch (problem) {
+ case InsufficientAnnotation:
+ log << " the function called is insufficiently annotated.";
+ break;
+ case InvalidListType:
+ log << " the target type, a list of unknown elements, cannot be resolved.";
+ break;
+ }
+
+ log << " The original value is retained. This will change in a future version of Qt.";
+}
+
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index 4126a25758..ed1ca983ad 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -14,14 +14,26 @@
// We mean it.
//
-#include "qv4object_p.h"
-#include "qv4function_p.h"
-#include "qv4functionobject_p.h"
-#include "qv4context_p.h"
-#include "qv4scopedvalue_p.h"
-#include "qv4stackframe_p.h"
-#include "qv4qobjectwrapper_p.h"
+#include <private/qqmlengine_p.h>
+#include <private/qqmllistwrapper_p.h>
+#include <private/qqmlvaluetype_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qv4context_p.h>
+#include <private/qv4dateobject_p.h>
+#include <private/qv4function_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4object_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4regexpobject_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qv4stackframe_p.h>
+#include <private/qv4urlobject_p.h>
+#include <private/qv4variantobject_p.h>
+
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -99,47 +111,32 @@ ReturnedValue FunctionObject::call(const JSCallData &data) const
void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int argc,
void **args, const QMetaType *types);
-struct ScopedStackFrame
-{
- ScopedStackFrame(const Scope &scope, ExecutionContext *context)
- : engine(scope.engine)
- {
- if (auto currentFrame = engine->currentStackFrame) {
- frame.init(currentFrame->v4Function, nullptr, context, nullptr, nullptr, 0);
- frame.instructionPointer = currentFrame->instructionPointer;
- } else {
- frame.init(nullptr, nullptr, context, nullptr, nullptr, 0);
- }
- frame.push(engine);
- }
-
- ~ScopedStackFrame()
- {
- frame.pop(engine);
- }
-
-private:
- ExecutionEngine *engine = nullptr;
- MetaTypesStackFrame frame;
-};
-
template<typename Callable>
ReturnedValue convertAndCall(
- ExecutionEngine *engine, const QQmlPrivate::TypedFunction *aotFunction,
+ ExecutionEngine *engine, const Function::AOTCompiledFunction *aotFunction,
const Value *thisObject, const Value *argv, int argc, Callable call)
{
- const qsizetype numFunctionArguments = aotFunction->argumentTypes.size();
+ const qsizetype numFunctionArguments = aotFunction->types.length() - 1;
Q_ALLOCA_VAR(void *, values, (numFunctionArguments + 1) * sizeof(void *));
Q_ALLOCA_VAR(QMetaType, types, (numFunctionArguments + 1) * sizeof(QMetaType));
for (qsizetype i = 0; i < numFunctionArguments; ++i) {
- const QMetaType argumentType = aotFunction->argumentTypes[i];
+ const QMetaType argumentType = aotFunction->types[i + 1];
types[i + 1] = argumentType;
if (const qsizetype argumentSize = argumentType.sizeOf()) {
Q_ALLOCA_VAR(void, argument, argumentSize);
- argumentType.construct(argument);
- if (i < argc)
- ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument);
+ if (argumentType.flags() & QMetaType::NeedsConstruction) {
+ argumentType.construct(argument);
+ if (i < argc)
+ ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument);
+ } else if (i >= argc
+ || !ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument)) {
+ // If we can't convert the argument, we need to default-construct it even if it
+ // doesn't formally need construction.
+ // E.g. an int doesn't need construction, but we still want it to be 0.
+ argumentType.construct(argument);
+ }
+
values[i + 1] = argument;
} else {
values[i + 1] = nullptr;
@@ -147,29 +144,37 @@ ReturnedValue convertAndCall(
}
Q_ALLOCA_DECLARE(void, returnValue);
- types[0] = aotFunction->returnType;
+ types[0] = aotFunction->types[0];
if (const qsizetype returnSize = types[0].sizeOf()) {
Q_ALLOCA_ASSIGN(void, returnValue, returnSize);
values[0] = returnValue;
+ if (types[0].flags() & QMetaType::NeedsConstruction)
+ types[0].construct(returnValue);
} else {
values[0] = nullptr;
}
- if (const QV4::QObjectWrapper *cppThisObject = thisObject->as<QV4::QObjectWrapper>())
+ if (const QV4::QObjectWrapper *cppThisObject = thisObject
+ ? thisObject->as<QV4::QObjectWrapper>()
+ : nullptr) {
call(cppThisObject->object(), values, types, argc);
- else
+ } else {
call(nullptr, values, types, argc);
+ }
ReturnedValue result;
if (values[0]) {
result = engine->metaTypeToJS(types[0], values[0]);
- types[0].destruct(values[0]);
+ if (types[0].flags() & QMetaType::NeedsDestruction)
+ types[0].destruct(values[0]);
} else {
result = Encode::undefined();
}
- for (qsizetype i = 1, end = numFunctionArguments + 1; i < end; ++i)
- types[i].destruct(values[i]);
+ for (qsizetype i = 1, end = numFunctionArguments + 1; i < end; ++i) {
+ if (types[i].flags() & QMetaType::NeedsDestruction)
+ types[i].destruct(values[i]);
+ }
return result;
}
@@ -202,57 +207,380 @@ bool convertAndCall(ExecutionEngine *engine, QObject *thisObject,
const QMetaType resultType = types[0];
if (scope.hasException()) {
// Clear the return value
+ resultType.destruct(result);
resultType.construct(result);
- } else {
+ } else if (resultType == QMetaType::fromType<QVariant>()) {
// When the return type is QVariant, JS objects are to be returned as
// QJSValue wrapped in QVariant. metaTypeFromJS unwraps them, unfortunately.
- if (resultType == QMetaType::fromType<QVariant>()) {
- new (result) QVariant(ExecutionEngine::toVariant(jsResult, QMetaType {}));
- } else {
- resultType.construct(result);
- ExecutionEngine::metaTypeFromJS(jsResult, resultType, result);
- }
+ *static_cast<QVariant *>(result) = ExecutionEngine::toVariant(jsResult, QMetaType {});
+ } else if (!ExecutionEngine::metaTypeFromJS(jsResult, resultType, result)) {
+ // If we cannot convert, also clear the return value.
+ // The caller may have given us an uninitialized QObject*, expecting it to be overwritten.
+ resultType.destruct(result);
+ resultType.construct(result);
}
return !jsResult->isUndefined();
}
+inline ReturnedValue coerce(
+ ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList);
+
+inline QObject *coerceQObject(const Value &value, const QQmlType &qmlType)
+{
+ QObject *o;
+ if (const QV4::QObjectWrapper *wrapper = value.as<QV4::QObjectWrapper>())
+ o = wrapper->object();
+ else if (const QV4::QQmlTypeWrapper *wrapper = value.as<QQmlTypeWrapper>())
+ o = wrapper->object();
+ else
+ return nullptr;
+
+ return (o && qmlobject_can_qml_cast(o, qmlType)) ? o : nullptr;
+}
+
+enum CoercionProblem
+{
+ InsufficientAnnotation,
+ InvalidListType
+};
+
+Q_QML_EXPORT void warnAboutCoercionToVoid(
+ ExecutionEngine *engine, const Value &value, CoercionProblem problem);
+
+inline ReturnedValue coerceListType(
+ ExecutionEngine *engine, const Value &value, const QQmlType &qmlType)
+{
+ QMetaType type = qmlType.qListTypeId();
+ const auto metaSequence = [&]() {
+ // TODO: We should really add the metasequence to the same QQmlType that holds
+ // all the other type information. Then we can get rid of the extra
+ // QQmlMetaType::qmlListType() here.
+ return qmlType.isSequentialContainer()
+ ? qmlType.listMetaSequence()
+ : QQmlMetaType::qmlListType(type).listMetaSequence();
+ };
+
+ if (const QV4::Sequence *sequence = value.as<QV4::Sequence>()) {
+ if (sequence->d()->listType() == type)
+ return value.asReturnedValue();
+ }
+
+ if (const QmlListWrapper *list = value.as<QmlListWrapper>()) {
+ if (list->d()->propertyType() == type)
+ return value.asReturnedValue();
+ }
+
+ QMetaType listValueType = qmlType.typeId();
+ if (!listValueType.isValid()) {
+ warnAboutCoercionToVoid(engine, value, InvalidListType);
+ return value.asReturnedValue();
+ }
+
+ QV4::Scope scope(engine);
+
+ const ArrayObject *array = value.as<ArrayObject>();
+ if (!array) {
+ return (listValueType.flags() & QMetaType::PointerToQObject)
+ ? QmlListWrapper::create(engine, listValueType)
+ : SequencePrototype::fromData(engine, type, metaSequence(), nullptr);
+ }
+
+ if (listValueType.flags() & QMetaType::PointerToQObject) {
+ QV4::Scoped<QmlListWrapper> newList(scope, QmlListWrapper::create(engine, type));
+ QQmlListProperty<QObject> *listProperty = newList->d()->property();
+
+ const qsizetype length = array->getLength();
+ qsizetype i = 0;
+ for (; i < length; ++i) {
+ ScopedValue v(scope, array->get(i));
+ listProperty->append(listProperty, coerceQObject(v, qmlType));
+ }
+
+ return newList->asReturnedValue();
+ }
+
+ QV4::Scoped<Sequence> sequence(
+ scope, SequencePrototype::fromData(engine, type, metaSequence(), nullptr));
+ const qsizetype length = array->getLength();
+ for (qsizetype i = 0; i < length; ++i)
+ sequence->containerPutIndexed(i, array->get(i));
+ return sequence->asReturnedValue();
+}
+
+inline ReturnedValue coerce(
+ ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList)
+{
+ // These are all the named non-list, non-QObject builtins. Only those need special handling.
+ // Some of them may be wrapped in VariantObject because that is how they are stored in VME
+ // properties.
+ if (isList)
+ return coerceListType(engine, value, qmlType);
+
+ const QMetaType metaType = qmlType.typeId();
+ if (!metaType.isValid()) {
+ if (!value.isUndefined())
+ warnAboutCoercionToVoid(engine, value, InsufficientAnnotation);
+ return value.asReturnedValue();
+ }
+
+ switch (metaType.id()) {
+ case QMetaType::Void:
+ return Encode::undefined();
+ case QMetaType::QVariant:
+ return value.asReturnedValue();
+ case QMetaType::Int:
+ return Encode(value.toInt32());
+ case QMetaType::Double:
+ return value.convertedToNumber();
+ case QMetaType::QString:
+ return value.toString(engine)->asReturnedValue();
+ case QMetaType::Bool:
+ return Encode(value.toBoolean());
+ case QMetaType::QDateTime:
+ if (value.as<DateObject>())
+ return value.asReturnedValue();
+ if (const VariantObject *varObject = value.as<VariantObject>()) {
+ const QVariant &var = varObject->d()->data();
+ switch (var.metaType().id()) {
+ case QMetaType::QDateTime:
+ return engine->newDateObject(var.value<QDateTime>())->asReturnedValue();
+ case QMetaType::QTime:
+ return engine->newDateObject(var.value<QTime>(), nullptr, -1, 0)->asReturnedValue();
+ case QMetaType::QDate:
+ return engine->newDateObject(var.value<QDate>(), nullptr, -1, 0)->asReturnedValue();
+ default:
+ break;
+ }
+ }
+ return engine->newDateObject(QDateTime())->asReturnedValue();
+ case QMetaType::QUrl:
+ if (value.as<UrlObject>())
+ return value.asReturnedValue();
+ if (const VariantObject *varObject = value.as<VariantObject>()) {
+ const QVariant &var = varObject->d()->data();
+ return var.metaType() == QMetaType::fromType<QUrl>()
+ ? engine->newUrlObject(var.value<QUrl>())->asReturnedValue()
+ : engine->newUrlObject()->asReturnedValue();
+ }
+ // Since URL properties are stored as string, we need to support the string conversion here.
+ if (const String *string = value.stringValue())
+ return engine->newUrlObject(QUrl(string->toQString()))->asReturnedValue();
+ return engine->newUrlObject()->asReturnedValue();
+#if QT_CONFIG(regularexpression)
+ case QMetaType::QRegularExpression:
+ if (value.as<RegExpObject>())
+ return value.asReturnedValue();
+ if (const VariantObject *varObject = value.as<VariantObject>()) {
+ const QVariant &var = varObject->d()->data();
+ if (var.metaType() == QMetaType::fromType<QRegularExpression>())
+ return engine->newRegExpObject(var.value<QRegularExpression>())->asReturnedValue();
+ }
+ return engine->newRegExpObject(QString(), 0)->asReturnedValue();
+#endif
+ default:
+ break;
+ }
+
+ if (metaType.flags() & QMetaType::PointerToQObject) {
+ return coerceQObject(value, qmlType)
+ ? value.asReturnedValue()
+ : Encode::null();
+ }
+
+ if (const QQmlValueTypeWrapper *wrapper = value.as<QQmlValueTypeWrapper>()) {
+ if (wrapper->type() == metaType)
+ return value.asReturnedValue();
+ }
+
+ if (void *target = QQmlValueTypeProvider::heapCreateValueType(qmlType, value)) {
+ Heap::QQmlValueTypeWrapper *wrapper = engine->memoryManager->allocate<QQmlValueTypeWrapper>(
+ nullptr, metaType, qmlType.metaObjectForValueType(),
+ nullptr, -1, Heap::ReferenceObject::NoFlag);
+ Q_ASSERT(!wrapper->gadgetPtr());
+ wrapper->setGadgetPtr(target);
+ return wrapper->asReturnedValue();
+ }
+
+ return Encode::undefined();
+}
+
template<typename Callable>
ReturnedValue coerceAndCall(
- ExecutionEngine *engine, const QQmlPrivate::TypedFunction *typedFunction,
- const Value *thisObject, const Value *argv, int argc, Callable call)
+ ExecutionEngine *engine,
+ const Function::JSTypedFunction *typedFunction, const CompiledData::Function *compiledFunction,
+ const Value *argv, int argc, Callable call)
{
Scope scope(engine);
- QV4::JSCallArguments jsCallData(scope, argc);
- const qsizetype numFunctionArguments = typedFunction->argumentTypes.size();
- for (qsizetype i = 0; i < numFunctionArguments; ++i) {
- const QMetaType argumentType = typedFunction->argumentTypes[i];
- if (const qsizetype argumentSize = argumentType.sizeOf()) {
- Q_ALLOCA_VAR(void, argument, argumentSize);
- argumentType.construct(argument);
- if (i < argc)
- ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument);
- jsCallData.args[i] = engine->metaTypeToJS(argumentType, argument);
+ QV4::JSCallArguments jsCallData(scope, typedFunction->types.size() - 1);
+ const CompiledData::Parameter *formals = compiledFunction->formalsTable();
+ for (qsizetype i = 0; i < jsCallData.argc; ++i) {
+ jsCallData.args[i] = coerce(
+ engine, i < argc ? argv[i] : Encode::undefined(),
+ typedFunction->types[i + 1], formals[i].type.isList());
+ }
+
+ ScopedValue result(scope, call(jsCallData.args, jsCallData.argc));
+ return coerce(engine, result, typedFunction->types[0], compiledFunction->returnType.isList());
+}
+
+// Note: \a to is unininitialized here! This is in contrast to most other related functions.
+inline void coerce(
+ ExecutionEngine *engine, QMetaType fromType, const void *from, QMetaType toType, void *to)
+{
+ if ((fromType.flags() & QMetaType::PointerToQObject)
+ && (toType.flags() & QMetaType::PointerToQObject)) {
+ QObject *fromObj = *static_cast<QObject * const*>(from);
+ *static_cast<QObject **>(to)
+ = (fromObj && fromObj->metaObject()->inherits(toType.metaObject()))
+ ? fromObj
+ : nullptr;
+ return;
+ }
+
+ if (toType == QMetaType::fromType<QVariant>()) {
+ new (to) QVariant(fromType, from);
+ return;
+ }
+
+ if (toType == QMetaType::fromType<QJSPrimitiveValue>()) {
+ new (to) QJSPrimitiveValue(fromType, from);
+ return;
+ }
+
+ if (fromType == QMetaType::fromType<QVariant>()) {
+ const QVariant *fromVariant = static_cast<const QVariant *>(from);
+ if (fromVariant->metaType() == toType)
+ toType.construct(to, fromVariant->data());
+ else
+ coerce(engine, fromVariant->metaType(), fromVariant->data(), toType, to);
+ return;
+ }
+
+ if (fromType == QMetaType::fromType<QJSPrimitiveValue>()) {
+ const QJSPrimitiveValue *fromPrimitive = static_cast<const QJSPrimitiveValue *>(from);
+ if (fromPrimitive->metaType() == toType)
+ toType.construct(to, fromPrimitive->data());
+ else
+ coerce(engine, fromPrimitive->metaType(), fromPrimitive->data(), toType, to);
+ return;
+ }
+
+ // TODO: This is expensive. We might establish a direct C++-to-C++ type coercion, like we have
+ // for JS-to-JS. However, we shouldn't need this very often. Most of the time the compiler
+ // will generate code that passes the right arguments.
+ if (toType.flags() & QMetaType::NeedsConstruction)
+ toType.construct(to);
+ QV4::Scope scope(engine);
+ QV4::ScopedValue value(scope, engine->fromData(fromType, from));
+ if (!ExecutionEngine::metaTypeFromJS(value, toType, to))
+ QMetaType::convert(fromType, from, toType, to);
+}
+
+template<typename TypedFunction, typename Callable>
+void coerceAndCall(
+ ExecutionEngine *engine, const TypedFunction *typedFunction,
+ void **argv, const QMetaType *types, int argc, Callable call)
+{
+ const qsizetype numFunctionArguments = typedFunction->parameterCount();
+
+ Q_ALLOCA_DECLARE(void *, transformedArguments);
+ Q_ALLOCA_DECLARE(void, transformedResult);
+
+ const QMetaType returnType = typedFunction->returnMetaType();
+ const QMetaType frameReturn = types[0];
+ bool returnsQVariantWrapper = false;
+ if (argv[0] && returnType != frameReturn) {
+ Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *));
+ memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *));
+
+ if (frameReturn == QMetaType::fromType<QVariant>()) {
+ QVariant *returnValue = static_cast<QVariant *>(argv[0]);
+ *returnValue = QVariant(returnType);
+ transformedResult = transformedArguments[0] = returnValue->data();
+ returnsQVariantWrapper = true;
+ } else if (returnType.sizeOf() > 0) {
+ Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf());
+ transformedArguments[0] = transformedResult;
+ if (returnType.flags() & QMetaType::NeedsConstruction)
+ returnType.construct(transformedResult);
} else {
- jsCallData.args[i] = argv[i];
+ transformedResult = transformedArguments[0] = &argc; // Some non-null marker value
}
}
- ScopedValue result(scope, call(thisObject, jsCallData.args, argc));
- const QMetaType returnType = typedFunction->returnType;
- if (const qsizetype returnSize = returnType.sizeOf()) {
- Q_ALLOCA_VAR(void, returnValue, returnSize);
- if (scope.hasException()) {
- returnType.construct(returnValue);
- } else if (returnType == QMetaType::fromType<QVariant>()) {
- new (returnValue) QVariant(ExecutionEngine::toVariant(result, QMetaType {}));
- } else {
- returnType.construct(returnValue);
- ExecutionEngine::metaTypeFromJS(result, returnType, returnValue);
+ for (qsizetype i = 0; i < numFunctionArguments; ++i) {
+ const bool isValid = argc > i;
+ const QMetaType frameType = isValid ? types[i + 1] : QMetaType();
+
+ const QMetaType argumentType = typedFunction->parameterMetaType(i);
+ if (isValid && argumentType == frameType)
+ continue;
+
+ if (transformedArguments == nullptr) {
+ Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *));
+ memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *));
+ }
+
+ if (argumentType.sizeOf() == 0) {
+ transformedArguments[i + 1] = nullptr;
+ continue;
+ }
+
+ void *frameVal = isValid ? argv[i + 1] : nullptr;
+ if (isValid && frameType == QMetaType::fromType<QVariant>()) {
+ QVariant *variant = static_cast<QVariant *>(frameVal);
+
+ const QMetaType variantType = variant->metaType();
+ if (variantType == argumentType) {
+ // Slightly nasty, but we're allowed to do this.
+ // We don't want to destruct() the QVariant's data() below.
+ transformedArguments[i + 1] = argv[i + 1] = variant->data();
+ } else {
+ Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
+ coerce(engine, variantType, variant->constData(), argumentType, arg);
+ transformedArguments[i + 1] = arg;
+ }
+ continue;
+ }
+
+ Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
+
+ if (isValid)
+ coerce(engine, frameType, frameVal, argumentType, arg);
+ else
+ argumentType.construct(arg);
+
+ transformedArguments[i + 1] = arg;
+ }
+
+ if (!transformedArguments) {
+ call(argv, numFunctionArguments);
+ return;
+ }
+
+ call(transformedArguments, numFunctionArguments);
+
+ if (transformedResult && !returnsQVariantWrapper) {
+ if (frameReturn.sizeOf() > 0) {
+ if (frameReturn.flags() & QMetaType::NeedsDestruction)
+ frameReturn.destruct(argv[0]);
+ coerce(engine, returnType, transformedResult, frameReturn, argv[0]);
+ }
+ if (returnType.flags() & QMetaType::NeedsDestruction)
+ returnType.destruct(transformedResult);
+ }
+
+ for (qsizetype i = 0; i < numFunctionArguments; ++i) {
+ void *arg = transformedArguments[i + 1];
+ if (arg == nullptr)
+ continue;
+ if (i >= argc || arg != argv[i + 1]) {
+ const QMetaType argumentType = typedFunction->parameterMetaType(i);
+ if (argumentType.flags() & QMetaType::NeedsDestruction)
+ argumentType.destruct(arg);
}
- return engine->metaTypeToJS(returnType, returnValue);
}
- return result->asReturnedValue();
}
} // namespace QV4
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index 4f46e605af..d78d09113a 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -114,6 +114,7 @@ QChar JsonParser::nextToken()
case EndArray:
case EndObject:
eatSpace();
+ break;
case Quote:
break;
default:
@@ -695,12 +696,18 @@ QString Stringify::Str(const QString &key, const Value &v)
}
if (replacerFunction) {
- ScopedObject holder(scope, v4->newObject());
- holder->put(scope.engine->id_empty(), value);
JSCallArguments jsCallData(scope, 2);
jsCallData.args[0] = v4->newString(key);
jsCallData.args[1] = value;
- *jsCallData.thisObject = holder;
+
+ if (stack.isEmpty()) {
+ ScopedObject holder(scope, v4->newObject());
+ holder->put(scope.engine->id_empty(), v);
+ *jsCallData.thisObject = holder;
+ } else {
+ *jsCallData.thisObject = stack.top();
+ }
+
value = replacerFunction->call(jsCallData);
if (v4->hasException)
return QString();
@@ -981,12 +988,16 @@ QJsonValue JsonObject::toJsonValue(const Value &value, V4ObjectSet &visitedObjec
Q_ASSERT(value.isObject());
Scope scope(value.as<Object>()->engine());
- ScopedArrayObject a(scope, value);
- if (a)
+ if (ScopedArrayObject a{ scope, value }) {
return toJsonArray(a, visitedObjects);
- ScopedObject o(scope, value);
- if (o)
+ } else if (Scoped<QV4::Sequence> a{ scope, value }) {
+ return toJsonArray(a, visitedObjects);
+ } else if (Scoped<QmlListWrapper> lw{ scope, value }) {
+ return toJsonArray(lw, visitedObjects);
+ } else if (ScopedObject o{ scope, value }) {
return toJsonObject(o, visitedObjects);
+ }
+
return QJsonValue(value.toQString());
}
@@ -1051,7 +1062,7 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso
return a.asReturnedValue();
}
-QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects)
+QJsonArray JsonObject::toJsonArray(const Object *a, V4ObjectSet &visitedObjects)
{
QJsonArray result;
if (!a)
diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h
index abdfcf5d37..f6f63d7eb3 100644
--- a/src/qml/jsruntime/qv4jsonobject_p.h
+++ b/src/qml/jsruntime/qv4jsonobject_p.h
@@ -63,14 +63,13 @@ public:
{ V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); }
static inline QJsonObject toJsonObject(const QV4::Object *o)
{ V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); }
- static inline QJsonArray toJsonArray(const QV4::ArrayObject *a)
- { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); }
+ static inline QJsonArray toJsonArray(const QV4::Object *o)
+ { V4ObjectSet visitedObjects; return toJsonArray(o, visitedObjects); }
private:
static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects);
static QJsonObject toJsonObject(const Object *o, V4ObjectSet &visitedObjects);
- static QJsonArray toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects);
-
+ static QJsonArray toJsonArray(const Object *o, V4ObjectSet &visitedObjects);
};
class JsonParser
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index abf88f4283..654275a709 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -1,10 +1,12 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qv4lookup_p.h"
-#include "qv4functionobject_p.h"
+
+#include <private/qv4functionobject_p.h>
#include <private/qv4identifiertable_p.h>
-#include <QtQml/private/qv4runtime_p.h>
-#include <QtQml/private/qv4qobjectwrapper_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4runtime_p.h>
+#include <private/qv4stackframe_p.h>
QT_BEGIN_NAMESPACE
@@ -52,12 +54,12 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
return engine->throwTypeError(message);
}
case Value::Boolean_Type:
- primitiveLookup.proto = engine->booleanPrototype()->d();
+ primitiveLookup.proto.set(engine, engine->booleanPrototype()->d());
break;
case Value::Managed_Type: {
// ### Should move this over to the Object path, as strings also have an internalClass
Q_ASSERT(object.isStringOrSymbol());
- primitiveLookup.proto = static_cast<const Managed &>(object).internalClass()->prototype;
+ primitiveLookup.proto.set(engine, static_cast<const Managed &>(object).internalClass()->prototype);
Q_ASSERT(primitiveLookup.proto);
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
@@ -70,7 +72,7 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
}
case Value::Integer_Type:
default: // Number
- primitiveLookup.proto = engine->numberPrototype()->d();
+ primitiveLookup.proto.set(engine, engine->numberPrototype()->d());
}
PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
@@ -120,9 +122,10 @@ static inline void setupObjectLookupTwoClasses(Lookup *l, const Lookup &first, c
const uint offset1 = first.objectLookup.offset;
Heap::InternalClass *ic2 = second.objectLookup.ic;
const uint offset2 = second.objectLookup.offset;
+ auto engine = ic1->engine;
- l->objectLookupTwoClasses.ic = ic1;
- l->objectLookupTwoClasses.ic2 = ic2;
+ l->objectLookupTwoClasses.ic.set(engine, ic1);
+ l->objectLookupTwoClasses.ic2.set(engine, ic2);
l->objectLookupTwoClasses.offset = offset1;
l->objectLookupTwoClasses.offset2 = offset2;
}
@@ -201,6 +204,21 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V
return o->get(name);
}
+ReturnedValue Lookup::getterFallbackAsVariant(
+ Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ if (&Lookup::getterFallback == &Lookup::getterFallbackAsVariant) {
+ // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
+ // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
+ // decides how to retrieve the property.
+ qFatal("Your C++ compiler is broken.");
+ }
+
+ // This getter just marks the presence of a fallback lookup with variant conversion.
+ // It only does anything with it when running AOT-compiled code.
+ return getterFallback(l, engine, object);
+}
+
ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
{
// we can safely cast to a QV4::Object here. If object is actually a string,
@@ -401,6 +419,21 @@ ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, con
return QObjectWrapper::lookupPropertyGetterImpl(lookup, engine, object, flags, revertLookup);
}
+ReturnedValue Lookup::getterQObjectAsVariant(
+ Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ if (&Lookup::getterQObject == &Lookup::getterQObjectAsVariant) {
+ // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
+ // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
+ // decides how to retrieve the property.
+ qFatal("Your C++ compiler is broken.");
+ }
+
+ // This getter marks the presence of a qobjectlookup with variant conversion.
+ // It only does anything with it when running AOT-compiled code.
+ return getterQObject(lookup, engine, object);
+}
+
ReturnedValue Lookup::getterQObjectMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object)
{
const auto revertLookup = [lookup, engine, &object]() {
@@ -533,8 +566,9 @@ bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object,
}
if (l->setter == Lookup::setter0MemberData || l->setter == Lookup::setter0Inline) {
- l->objectLookupTwoClasses.ic = ic;
- l->objectLookupTwoClasses.ic2 = ic;
+ auto engine = ic->engine;
+ l->objectLookupTwoClasses.ic.set(engine, ic);
+ l->objectLookupTwoClasses.ic2.set(engine, ic);
l->objectLookupTwoClasses.offset = index;
l->objectLookupTwoClasses.offset2 = index;
l->setter = setter0setter0;
@@ -559,6 +593,21 @@ bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c
return o->put(name, value);
}
+bool Lookup::setterFallbackAsVariant(
+ Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+{
+ if (&Lookup::setterFallback == &Lookup::setterFallbackAsVariant) {
+ // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
+ // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
+ // decides how to retrieve the property.
+ qFatal("Your C++ compiler is broken.");
+ }
+
+ // This setter just marks the presence of a fallback lookup with QVariant conversion.
+ // It only does anything with it when running AOT-compiled code.
+ return setterFallback(l, engine, object, value);
+}
+
bool Lookup::setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
@@ -619,9 +668,25 @@ bool Lookup::setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, co
{
// This setter just marks the presence of a qobjectlookup. It only does anything with it when
// running AOT-compiled code, though.
- return QV4::Lookup::setterFallback(l, engine, object, v);
+ return setterFallback(l, engine, object, v);
}
+bool Lookup::setterQObjectAsVariant(
+ Lookup *l, ExecutionEngine *engine, Value &object, const Value &v)
+{
+ if (&Lookup::setterQObject == &Lookup::setterQObjectAsVariant) {
+ // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
+ // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
+ // decides how to retrieve the property.
+ qFatal("Your C++ compiler is broken.");
+ }
+
+ // This setter marks the presence of a qobjectlookup with QVariant conversion.
+ // It only does anything with it when running AOT-compiled code.
+ return setterQObject(l, engine, object, v);
+}
+
+
bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object, const Value &value)
{
Q_ASSERT(object.isObject() && static_cast<Object &>(object).isArrayObject());
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index bd481d366e..258184cd37 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -19,6 +19,7 @@
#include "qv4internalclass_p.h"
#include "qv4qmlcontext_p.h"
#include <private/qqmltypewrapper_p.h>
+#include <private/qv4mm_p.h>
QT_BEGIN_NAMESPACE
@@ -28,10 +29,13 @@ namespace Heap {
struct QObjectMethod;
}
+template <typename T, int PhantomTag>
+using HeapObjectWrapper = WriteBarrier::HeapObjectWrapper<T, PhantomTag>;
+
// Note: We cannot hide the copy ctor and assignment operator of this class because it needs to
// be trivially copyable. But you should never ever copy it. There are refcounted members
// in there.
-struct Q_QML_PRIVATE_EXPORT Lookup {
+struct Q_QML_EXPORT Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
@@ -48,7 +52,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
quintptr unused2;
} markDef;
struct {
- Heap::InternalClass *ic;
+ HeapObjectWrapper<Heap::InternalClass, 0> ic;
quintptr unused;
uint index;
uint offset;
@@ -59,8 +63,8 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
const Value *data;
} protoLookup;
struct {
- Heap::InternalClass *ic;
- Heap::InternalClass *ic2;
+ HeapObjectWrapper<Heap::InternalClass, 1> ic;
+ HeapObjectWrapper<Heap::InternalClass, 2> ic2;
uint offset;
uint offset2;
} objectLookupTwoClasses;
@@ -73,12 +77,12 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
struct {
// Make sure the next two values are in sync with protoLookup
quintptr protoId;
- Heap::Object *proto;
+ HeapObjectWrapper<Heap::Object, 3> proto;
const Value *data;
quintptr type;
} primitiveLookup;
struct {
- Heap::InternalClass *newClass;
+ HeapObjectWrapper<Heap::InternalClass, 4> newClass;
quintptr protoId;
uint offset;
uint unused;
@@ -90,14 +94,14 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
uint unused;
} indexedLookup;
struct {
- Heap::InternalClass *ic;
- Heap::InternalClass *qmlTypeIc; // only used when lookup goes through QQmlTypeWrapper
+ HeapObjectWrapper<Heap::InternalClass, 5> ic;
+ HeapObjectWrapper<Heap::InternalClass, 6> qmlTypeIc; // only used when lookup goes through QQmlTypeWrapper
const QQmlPropertyCache *propertyCache;
const QQmlPropertyData *propertyData;
} qobjectLookup;
struct {
- Heap::InternalClass *ic;
- Heap::QObjectMethod *method;
+ HeapObjectWrapper<Heap::InternalClass, 7> ic;
+ HeapObjectWrapper<Heap::QObjectMethod, 8> method;
const QQmlPropertyCache *propertyCache;
const QQmlPropertyData *propertyData;
} qobjectMethodLookup;
@@ -108,7 +112,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
int notifyIndex;
} qobjectFallbackLookup;
struct {
- Heap::InternalClass *ic;
+ HeapObjectWrapper<Heap::InternalClass, 9> ic;
quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
const QtPrivate::QMetaTypeInterface *metaType; // cannot use QMetaType; class must be trivial
quint16 coreIndex;
@@ -121,7 +125,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
int scriptIndex;
} qmlContextScriptLookup;
struct {
- Heap::Base *singletonObject;
+ HeapObjectWrapper<Heap::Base, 10> singletonObject;
quintptr unused2;
QV4::ReturnedValue singletonValue;
} qmlContextSingletonLookup;
@@ -138,17 +142,18 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine);
} qmlContextGlobalLookup;
struct {
- Heap::Base *qmlTypeWrapper;
+ HeapObjectWrapper<Heap::Base, 11> qmlTypeWrapper;
quintptr unused2;
} qmlTypeLookup;
struct {
- Heap::InternalClass *ic;
+ HeapObjectWrapper<Heap::InternalClass, 12> ic;
quintptr unused;
ReturnedValue encodedEnumValue;
+ const QtPrivate::QMetaTypeInterface *metaType;
} qmlEnumValueLookup;
struct {
- Heap::InternalClass *ic;
- Heap::Object *qmlScopedEnumWrapper;
+ HeapObjectWrapper<Heap::InternalClass, 13> ic;
+ HeapObjectWrapper<Heap::Object, 14> qmlScopedEnumWrapper;
} qmlScopedEnumWrapperLookup;
};
@@ -164,6 +169,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getterFallbackAsVariant(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -177,6 +183,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterQObject(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getterQObjectAsVariant(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterQObjectMethod(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -191,11 +198,13 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
Q_NEVER_INLINE static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterFallbackAsVariant(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterQObjectAsVariant(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
void markObjects(MarkStack *stack) {
@@ -205,17 +214,15 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
markDef.h2->mark(stack);
}
- void clear() {
- memset(&markDef, 0, sizeof(markDef));
- }
-
void releasePropertyCache()
{
if (getter == getterQObject
|| getter == QQmlTypeWrapper::lookupSingletonProperty
|| setter == setterQObject
|| qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty
- || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) {
+ || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty
+ || getter == getterQObjectAsVariant
+ || setter == setterQObjectAsVariant) {
if (const QQmlPropertyCache *pc = qobjectLookup.propertyCache)
pc->release();
} else if (getter == getterQObjectMethod
@@ -248,7 +255,7 @@ inline void setupQObjectLookup(
const Object *self)
{
setupQObjectLookup(lookup, ddata, propertyData);
- lookup->qobjectLookup.ic = self->internalClass();
+ lookup->qobjectLookup.ic.set(self->engine(), self->internalClass());
}
@@ -257,17 +264,20 @@ inline void setupQObjectLookup(
const Object *self, const Object *qmlType)
{
setupQObjectLookup(lookup, ddata, propertyData, self);
- lookup->qobjectLookup.qmlTypeIc = qmlType->internalClass();
+ lookup->qobjectLookup.qmlTypeIc.set(self->engine(), qmlType->internalClass());
}
+// template parameter is an ugly trick to avoid pulling in the QObjectMethod header here
+template<typename QObjectMethod = Heap::QObjectMethod>
inline void setupQObjectMethodLookup(
Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData,
- const Object *self, Heap::QObjectMethod *method)
+ const Object *self, QObjectMethod *method)
{
lookup->releasePropertyCache();
Q_ASSERT(!ddata->propertyCache.isNull());
- lookup->qobjectMethodLookup.method = method;
- lookup->qobjectMethodLookup.ic = self->internalClass();
+ auto engine = self->engine();
+ lookup->qobjectMethodLookup.method.set(engine, method);
+ lookup->qobjectMethodLookup.ic.set(engine, self->internalClass());
lookup->qobjectMethodLookup.propertyCache = ddata->propertyCache.data();
lookup->qobjectMethodLookup.propertyCache->addref();
lookup->qobjectMethodLookup.propertyData = propertyData;
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index 299d4b4196..c5f5fbff8e 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -69,7 +69,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
static Heap::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
{ return e->internalClasses(QV4::EngineBase::Class_##c); }
-struct Q_QML_PRIVATE_EXPORT Managed : Value, VTableBase
+struct Q_QML_EXPORT Managed : Value, VTableBase
{
V4_MANAGED_ITSELF(Base, Managed)
enum {
diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp
index 72e66e0e76..4bb9617b93 100644
--- a/src/qml/jsruntime/qv4mapobject.cpp
+++ b/src/qml/jsruntime/qv4mapobject.cpp
@@ -74,8 +74,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv,
if (scope.hasException())
break;
}
- ScopedValue falsey(scope, Encode(false));
- return Runtime::IteratorClose::call(scope.engine, iter, falsey);
+ return Runtime::IteratorClose::call(scope.engine, iter);
}
}
return a->asReturnedValue();
diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h
index 2692c2617b..b12990700d 100644
--- a/src/qml/jsruntime/qv4math_p.h
+++ b/src/qml/jsruntime/qv4math_p.h
@@ -34,7 +34,7 @@ namespace QV4 {
static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b)
{
int result;
- if (Q_UNLIKELY(add_overflow(a, b, &result)))
+ if (Q_UNLIKELY(qAddOverflow(a, b, &result)))
return StaticValue::fromDouble(static_cast<double>(a) + b).asReturnedValue();
return StaticValue::fromInt32(result).asReturnedValue();
}
@@ -42,7 +42,7 @@ static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b)
static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b)
{
int result;
- if (Q_UNLIKELY(sub_overflow(a, b, &result)))
+ if (Q_UNLIKELY(qSubOverflow(a, b, &result)))
return StaticValue::fromDouble(static_cast<double>(a) - b).asReturnedValue();
return StaticValue::fromInt32(result).asReturnedValue();
}
@@ -50,7 +50,7 @@ static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b)
static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b)
{
int result;
- if (Q_UNLIKELY(mul_overflow(a, b, &result)))
+ if (Q_UNLIKELY(qMulOverflow(a, b, &result)))
return StaticValue::fromDouble(static_cast<double>(a) * b).asReturnedValue();
// need to handle the case where one number is negative and the other 0 ==> -0
if (((a < 0) xor (b < 0)) && (result == 0))
diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp
index 6c0fc3a183..0231641609 100644
--- a/src/qml/jsruntime/qv4memberdata.cpp
+++ b/src/qml/jsruntime/qv4memberdata.cpp
@@ -46,7 +46,7 @@ Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberD
if (oldSize > alloc)
alloc = oldSize;
m = e->memoryManager->allocManaged<MemberData>(alloc);
- // no write barrier required here
+ // no write barrier required here, as m gets marked later when member data is set
memcpy(m, old, oldSize);
} else {
m = e->memoryManager->allocManaged<MemberData>(alloc);
diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp
index 1e1a059dfa..f701529096 100644
--- a/src/qml/jsruntime/qv4module.cpp
+++ b/src/qml/jsruntime/qv4module.cpp
@@ -4,13 +4,14 @@
#include "qv4module_p.h"
-#include <private/qv4mm_p.h>
-#include <private/qv4vme_moth_p.h>
#include <private/qv4context_p.h>
-#include <private/qv4symbol_p.h>
#include <private/qv4identifiertable_p.h>
+#include <private/qv4mm_p.h>
+#include <private/qv4stackframe_p.h>
+#include <private/qv4symbol_p.h>
+#include <private/qv4vme_moth_p.h>
-#include <QScopeGuard>
+#include <QtCore/qscopeguard.h>
using namespace QV4;
@@ -50,8 +51,8 @@ void Heap::Module::init(ExecutionEngine *engine, ExecutableCompilationUnit *modu
{
Scoped<QV4::InternalClass> ic(valueScope, scope->internalClass);
- for (uint i = 0; i < unit->data->importEntryTableSize; ++i) {
- const CompiledData::ImportEntry &import = unit->data->importEntryTable()[i];
+ for (uint i = 0; i < unit->unitData()->importEntryTableSize; ++i) {
+ const CompiledData::ImportEntry &import = unit->unitData()->importEntryTable()[i];
ic = ic->addMember(engine->identifierTable->asPropertyKey(unit->runtimeStrings[import.localName]), Attr_NotConfigurable);
}
scope->internalClass.set(engine, ic->d());
@@ -75,7 +76,7 @@ void Module::evaluate()
unit->evaluateModuleRequests();
ExecutionEngine *v4 = engine();
- Function *moduleFunction = unit->runtimeFunctions[unit->data->indexOfRootFunction];
+ Function *moduleFunction = unit->runtimeFunctions[unit->unitData()->indexOfRootFunction];
JSTypesStackFrame frame;
frame.init(moduleFunction, nullptr, 0);
frame.setupJSFrame(v4->jsStackTop, Value::undefinedValue(), d()->scope,
@@ -222,9 +223,12 @@ OwnPropertyKeyIterator *Module::virtualOwnPropertyKeys(const Object *o, Value *t
if (module->d()->unit->isESModule()) {
names = module->d()->unit->exportedNames();
} else {
- Heap::InternalClass *scopeClass = module->d()->scope->internalClass;
- for (uint i = 0; i < scopeClass->size; ++i)
- names << scopeClass->keyAt(i);
+ QV4::Scope scope(module->engine());
+ QV4::Scoped<InternalClass> scopeClass(scope, module->d()->scope->internalClass);
+ for (uint i = 0, end = scopeClass->d()->size; i < end; ++i) {
+ QV4::ScopedValue key(scope, scopeClass->d()->keyAt(i));
+ names << key->toQString();
+ }
}
return new ModuleNamespaceIterator(names);
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index d58a712147..1ad5e063e8 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -2,22 +2,25 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qv4object_p.h"
-#include "qv4stringobject_p.h"
-#include "qv4argumentsobject_p.h"
+
+#include <private/qv4argumentsobject_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4jscall_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4memberdata_p.h>
#include <private/qv4mm_p.h>
-#include "qv4lookup_p.h"
-#include "qv4scopedvalue_p.h"
-#include "qv4memberdata_p.h"
-#include "qv4identifiertable_p.h"
-#include "qv4jscall_p.h"
-#include "qv4symbol_p.h"
-#include "qv4proxy_p.h"
+#include <private/qv4proxy_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qv4stackframe_p.h>
+#include <private/qv4stringobject_p.h>
+#include <private/qv4symbol_p.h>
#include <QtCore/qloggingcategory.h>
#include <stdint.h>
using namespace QV4;
+using namespace Qt::Literals::StringLiterals;
Q_LOGGING_CATEGORY(lcJavaScriptGlobals, "qt.qml.js.globals")
@@ -39,8 +42,11 @@ void Object::setInternalClass(Heap::InternalClass *ic)
// Pick the members of the old IC that are still valid in the new IC.
// Order them by index in memberData (or inline data).
Scoped<MemberData> newMembers(scope, MemberData::allocate(scope.engine, ic->size));
- for (uint i = 0; i < ic->size; ++i)
- newMembers->set(scope.engine, i, get(ic->nameMap.at(i)));
+ for (uint i = 0; i < ic->size; ++i) {
+ // Note that some members might have been deleted. The key may be invalid.
+ const PropertyKey key = ic->nameMap.at(i);
+ newMembers->set(scope.engine, i, key.isValid() ? get(key) : Encode::undefined());
+ }
p->internalClass.set(scope.engine, ic);
const uint nInline = p->vtable()->nInlineProperties;
@@ -177,16 +183,16 @@ void Object::defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, V
QV4::Scope scope(v4);
ScopedProperty p(scope);
QString n = name->toQString();
- if (n.at(0) == QLatin1Char('@'))
- n = QChar::fromLatin1('[') + QStringView{n}.mid(1) + QChar::fromLatin1(']');
+ if (!n.isEmpty() && n.at(0) == '@'_L1)
+ n = '['_L1 + QStringView{n}.mid(1) + ']'_L1;
if (getter) {
- ScopedString getName(scope, v4->newString(QString::fromLatin1("get ") + n));
+ ScopedString getName(scope, v4->newString("get "_L1 + n));
p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, getName, getter, 0)));
} else {
p->setGetter(nullptr);
}
if (setter) {
- ScopedString setName(scope, v4->newString(QString::fromLatin1("set ") + n));
+ ScopedString setName(scope, v4->newString("set "_L1 + n));
p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, setName, setter, 0)));
} else {
p->setSetter(nullptr);
@@ -762,7 +768,7 @@ ReturnedValue Object::virtualResolveLookupGetter(const Object *object, Execution
} else {
lookup->getter = Lookup::getterAccessor;
}
- lookup->objectLookup.ic = obj->internalClass;
+ lookup->objectLookup.ic.set(engine, obj->internalClass.get());
lookup->objectLookup.offset = index.index;
return lookup->getter(lookup, engine, *object);
}
@@ -789,7 +795,7 @@ bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine,
lookup->setter = Lookup::arrayLengthSetter;
return lookup->setter(lookup, engine, *object, value);
} else if (idx.attrs.isData() && idx.attrs.isWritable()) {
- lookup->objectLookup.ic = object->internalClass();
+ lookup->objectLookup.ic.set(engine, object->internalClass());
lookup->objectLookup.index = idx.index;
const auto nInline = object->d()->vtable()->nInlineProperties;
if (idx.index < nInline) {
@@ -823,7 +829,7 @@ bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine,
lookup->setter = Lookup::setterFallback;
return false;
}
- lookup->insertionLookup.newClass = object->internalClass();
+ lookup->insertionLookup.newClass.set(engine, object->internalClass());
lookup->insertionLookup.offset = idx.index;
lookup->setter = Lookup::setterInsert;
return true;
@@ -1178,6 +1184,7 @@ QStringList ArrayObject::toQStringList() const
ScopedValue v(scope);
uint length = getLength();
+ result.reserve(length);
for (uint i = 0; i < length; ++i) {
v = const_cast<ArrayObject *>(this)->get(i);
result.append(v->toQStringNoThrow());
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index acfc7d3a47..55d18fad52 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -377,7 +377,7 @@ private:
friend struct ObjectPrototype;
};
-struct Q_QML_PRIVATE_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
+struct Q_QML_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
{
uint arrayIndex = 0;
uint memberIndex = 0;
diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h
index fa12f77a6a..d152bfd175 100644
--- a/src/qml/jsruntime/qv4objectproto_p.h
+++ b/src/qml/jsruntime/qv4objectproto_p.h
@@ -38,7 +38,7 @@ struct ObjectCtor: FunctionObject
static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc);
};
-struct Q_QML_PRIVATE_EXPORT ObjectPrototype: Object
+struct Q_QML_EXPORT ObjectPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp
index 75353935a1..4f11d0a2ad 100644
--- a/src/qml/jsruntime/qv4persistent.cpp
+++ b/src/qml/jsruntime/qv4persistent.cpp
@@ -142,6 +142,7 @@ PersistentValueStorage::PersistentValueStorage(ExecutionEngine *engine)
PersistentValueStorage::~PersistentValueStorage()
{
+ clearFreePageHint();
Page *p = static_cast<Page *>(firstPage);
while (p) {
for (int i = 0; i < kEntriesPerPage; ++i) {
@@ -159,7 +160,9 @@ PersistentValueStorage::~PersistentValueStorage()
Value *PersistentValueStorage::allocate()
{
- Page *p = static_cast<Page *>(firstPage);
+ Page *p = static_cast<Page *>(freePageHint);
+ if (p && p->header.freeList == -1)
+ p = static_cast<Page *>(firstPage);
while (p) {
if (p->header.freeList != -1)
break;
@@ -171,9 +174,15 @@ Value *PersistentValueStorage::allocate()
Value *v = p->values + p->header.freeList;
p->header.freeList = v->int_32();
- if (p->header.freeList != -1 && p != firstPage) {
- unlink(p);
- insertInFront(this, p);
+ if (p->header.freeList != -1 && p != freePageHint) {
+ if (auto oldHint = static_cast<Page *>(freePageHint)) {
+ oldHint->header.refCount--;
+ // no need to free - if the old page were unused,
+ // we would have used it to serve the allocation
+ Q_ASSERT(oldHint->header.refCount);
+ }
+ freePageHint = p;
+ p->header.refCount++;
}
++p->header.refCount;
@@ -207,6 +216,17 @@ void PersistentValueStorage::mark(MarkStack *markStack)
}
}
+void PersistentValueStorage::clearFreePageHint()
+{
+ if (!freePageHint)
+ return;
+ auto page = static_cast<Page *>(freePageHint);
+ if (!--page->header.refCount)
+ freePage(page);
+ freePageHint = nullptr;
+
+}
+
ExecutionEngine *PersistentValueStorage::getEngine(const Value *v)
{
return getPage(v)->header.engine;
@@ -223,22 +243,18 @@ void PersistentValueStorage::freePage(void *page)
PersistentValue::PersistentValue(const PersistentValue &other)
: val(nullptr)
{
- if (other.val) {
- val = other.engine()->memoryManager->m_persistentValues->allocate();
- *val = *other.val;
- }
+ if (other.val)
+ set(other.engine(), *other.val);
}
PersistentValue::PersistentValue(ExecutionEngine *engine, const Value &value)
{
- val = engine->memoryManager->m_persistentValues->allocate();
- *val = value;
+ set(engine, value);
}
PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue value)
{
- val = engine->memoryManager->m_persistentValues->allocate();
- *val = value;
+ set(engine, value);
}
PersistentValue::PersistentValue(ExecutionEngine *engine, Object *object)
@@ -246,9 +262,7 @@ PersistentValue::PersistentValue(ExecutionEngine *engine, Object *object)
{
if (!object)
return;
-
- val = engine->memoryManager->m_persistentValues->allocate();
- *val = object;
+ set(engine, *object);
}
PersistentValue &PersistentValue::operator=(const PersistentValue &other)
@@ -271,19 +285,16 @@ PersistentValue &PersistentValue::operator=(const PersistentValue &other)
PersistentValue &PersistentValue::operator=(const WeakValue &other)
{
- if (!val) {
- if (!other.valueRef())
- return *this;
- val = other.engine()->memoryManager->m_persistentValues->allocate();
- }
+ if (!val && !other.valueRef())
+ return *this;
if (!other.valueRef()) {
*val = Encode::undefined();
return *this;
}
- Q_ASSERT(engine() == other.engine());
+ Q_ASSERT(!engine() || engine() == other.engine());
- *val = *other.valueRef();
+ set(other.engine(), *other.valueRef());
return *this;
}
@@ -293,10 +304,7 @@ PersistentValue &PersistentValue::operator=(Object *object)
PersistentValueStorage::free(val);
return *this;
}
- if (!val)
- val = object->engine()->memoryManager->m_persistentValues->allocate();
-
- *val = object;
+ set(object->engine(), *object);
return *this;
}
@@ -304,6 +312,10 @@ void PersistentValue::set(ExecutionEngine *engine, const Value &value)
{
if (!val)
val = engine->memoryManager->m_persistentValues->allocate();
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack){
+ if (QV4::WriteBarrier::isInsertionBarrier && value.isManaged())
+ value.heapObject()->mark(stack);
+ });
*val = value;
}
@@ -311,6 +323,13 @@ void PersistentValue::set(ExecutionEngine *engine, ReturnedValue value)
{
if (!val)
val = engine->memoryManager->m_persistentValues->allocate();
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack){
+ if constexpr (!QV4::WriteBarrier::isInsertionBarrier)
+ return;
+ auto val = Value::fromReturnedValue(value);
+ if (val.isManaged())
+ val.heapObject()->mark(stack);
+ });
*val = value;
}
@@ -318,6 +337,11 @@ void PersistentValue::set(ExecutionEngine *engine, Heap::Base *obj)
{
if (!val)
val = engine->memoryManager->m_persistentValues->allocate();
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack){
+ if constexpr (QV4::WriteBarrier::isInsertionBarrier)
+ obj->mark(stack);
+ });
+
*val = obj;
}
@@ -359,6 +383,56 @@ WeakValue::~WeakValue()
free();
}
+/*
+ WeakValue::set shold normally not mark objects, after all a weak value
+ is not supposed to keep an object alive.
+ However, if we are past GCState::HandleQObjectWrappers, nothing will
+ reset weak values referencing unmarked values, but those values will
+ still be swept.
+ That lead to stale pointers, and potentially to crashes. To avoid this,
+ we mark the objects here (they might still get collected in the next gc
+ run).
+ This is especially important due to the way we handle QObjectWrappers.
+ */
+void WeakValue::set(ExecutionEngine *engine, const Value &value)
+{
+ if (!val)
+ allocVal(engine);
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) {
+ if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers)
+ return;
+ if (auto *h = value.heapObject())
+ h->mark(ms);
+ });
+ *val = value;
+}
+
+void WeakValue::set(ExecutionEngine *engine, ReturnedValue value)
+{
+ if (!val)
+ allocVal(engine);
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) {
+ if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers)
+ return;
+ if (auto *h = QV4::Value::fromReturnedValue(value).heapObject())
+ h->mark(ms);
+ });
+
+ *val = value;
+}
+
+void WeakValue::set(ExecutionEngine *engine, Heap::Base *obj)
+{
+ if (!val)
+ allocVal(engine);
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) {
+ if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers)
+ return;
+ obj->mark(ms);
+ });
+ *val = obj;
+}
+
void WeakValue::allocVal(ExecutionEngine *engine)
{
val = engine->memoryManager->m_weakValues->allocate();
diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h
index 7e208bd4fd..d0e29a166e 100644
--- a/src/qml/jsruntime/qv4persistent_p.h
+++ b/src/qml/jsruntime/qv4persistent_p.h
@@ -51,10 +51,13 @@ struct Q_QML_EXPORT PersistentValueStorage
Iterator begin() { return Iterator(firstPage, 0); }
Iterator end() { return Iterator(nullptr, 0); }
+ void clearFreePageHint();
+
static ExecutionEngine *getEngine(const Value *v);
ExecutionEngine *engine;
void *firstPage;
+ void *freePageHint = nullptr;
private:
static void freeUnchecked(Value *v);
static void freePage(void *page);
@@ -128,26 +131,11 @@ public:
WeakValue &operator=(const WeakValue &other);
~WeakValue();
- void set(ExecutionEngine *engine, const Value &value)
- {
- if (!val)
- allocVal(engine);
- *val = value;
- }
+ void set(ExecutionEngine *engine, const Value &value);
- void set(ExecutionEngine *engine, ReturnedValue value)
- {
- if (!val)
- allocVal(engine);
- *val = value;
- }
+ void set(ExecutionEngine *engine, ReturnedValue value);
- void set(ExecutionEngine *engine, Heap::Base *obj)
- {
- if (!val)
- allocVal(engine);
- *val = obj;
- }
+ void set(ExecutionEngine *engine, Heap::Base *obj);
ReturnedValue value() const {
return (val ? val->asReturnedValue() : Encode::undefined());
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index ed0dba1809..df63d4bdf7 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -88,10 +88,10 @@ void Profiler::startProfiling(quint64 features)
(qint64)m_engine->memoryManager->getLargeItemsMem(),
HeapPage};
m_memory_data.append(heap);
- MemoryAllocationProperties small = {timestamp,
+ MemoryAllocationProperties smallP = {timestamp,
(qint64)m_engine->memoryManager->getUsedMem(),
SmallItem};
- m_memory_data.append(small);
+ m_memory_data.append(smallP);
MemoryAllocationProperties large = {timestamp,
(qint64)m_engine->memoryManager->getLargeItemsMem(),
LargeItem};
diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h
index 6fdd457f9d..d1e8e34ba3 100644
--- a/src/qml/jsruntime/qv4profiling_p.h
+++ b/src/qml/jsruntime/qv4profiling_p.h
@@ -23,8 +23,8 @@
#if !QT_CONFIG(qml_debug)
-#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine)
-#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine)
+#define Q_V4_PROFILE_ALLOC(engine, size, type) Q_UNUSED(engine)
+#define Q_V4_PROFILE_DEALLOC(engine, size, type) Q_UNUSED(engine)
QT_BEGIN_NAMESPACE
diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp
index 5424545e79..16cffea124 100644
--- a/src/qml/jsruntime/qv4promiseobject.cpp
+++ b/src/qml/jsruntime/qv4promiseobject.cpp
@@ -575,7 +575,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
}
if (!doneValue->toBoolean())
- completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(e, iteratorObject);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -583,7 +583,9 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1)));
if (scope.hasException() || !nextPromise) {
- ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue));
+ ScopedValue completion(scope, doneValue->toBoolean()
+ ? Encode::undefined()
+ : Runtime::IteratorClose::call(e, iteratorObject));
if (scope.hasException()) {
completion = e->exceptionValue->asReturnedValue();
dropException(e);
@@ -605,7 +607,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
}
if (!doneValue->toBoolean())
- completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(scope.engine, iteratorObject);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -624,7 +626,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
dropException(e);
if (!doneValue->toBoolean())
- completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(scope.engine, iteratorObject);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -686,7 +688,9 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue));
if (scope.hasException()) {
- ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue));
+ ScopedValue completion(scope, doneValue->toBoolean()
+ ? Encode::undefined()
+ : Runtime::IteratorClose::call(e, iteratorObject));
if (scope.hasException()) {
completion = e->exceptionValue->asReturnedValue();
dropException(e);
@@ -721,7 +725,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
}
if (!doneValue->toBoolean())
- completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(e, iteratorObject);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -729,7 +733,9 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1)));
if (scope.hasException() || !nextPromise) {
- ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue));
+ ScopedValue completion(scope, doneValue->toBoolean()
+ ? Encode::undefined()
+ : Runtime::IteratorClose::call(e, iteratorObject));
if (scope.hasException()) {
completion = e->exceptionValue->asReturnedValue();
dropException(e);
@@ -749,7 +755,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
}
if (!doneValue->toBoolean())
- completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(e, iteratorObject);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -768,7 +774,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
dropException(e);
if (!doneValue->toBoolean())
- completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(e, iteratorObject);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
diff --git a/src/qml/jsruntime/qv4promiseobject_p.h b/src/qml/jsruntime/qv4promiseobject_p.h
index 1c11ebbfd6..ca31f00881 100644
--- a/src/qml/jsruntime/qv4promiseobject_p.h
+++ b/src/qml/jsruntime/qv4promiseobject_p.h
@@ -181,7 +181,7 @@ struct PromiseExecutionState : Object
V4_OBJECT2(PromiseExecutionState, Object)
};
-struct Q_QML_PRIVATE_EXPORT PromiseObject : Object
+struct Q_QML_EXPORT PromiseObject : Object
{
V4_OBJECT2(PromiseObject, Object)
V4_NEEDS_DESTROY
diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h
index 055c132fcd..e7ad6b58d6 100644
--- a/src/qml/jsruntime/qv4property_p.h
+++ b/src/qml/jsruntime/qv4property_p.h
@@ -173,8 +173,6 @@ struct PropertyIndex {
}
-Q_DECLARE_TYPEINFO(QV4::Property, Q_RELOCATABLE_TYPE);
-
QT_END_NAMESPACE
#endif
diff --git a/src/qml/jsruntime/qv4propertykey.cpp b/src/qml/jsruntime/qv4propertykey.cpp
index ac03e01a73..65dd7e7fc1 100644
--- a/src/qml/jsruntime/qv4propertykey.cpp
+++ b/src/qml/jsruntime/qv4propertykey.cpp
@@ -7,6 +7,9 @@
#include <qv4string_p.h>
#include <qv4engine_p.h>
#include <qv4scopedvalue_p.h>
+#include <private/qv4mm_p.h>
+
+using namespace Qt::Literals::StringLiterals;
QV4::Heap::StringOrSymbol *QV4::PropertyKey::toStringOrSymbol(QV4::ExecutionEngine *e)
{
@@ -56,9 +59,9 @@ QV4::Heap::String *QV4::PropertyKey::asFunctionName(ExecutionEngine *engine, Fun
{
QString n;
if (prefix == Getter)
- n = QStringLiteral("get ");
+ n += "get "_L1;
else if (prefix == Setter)
- n = QStringLiteral("set ");
+ n += "set "_L1;
if (isArrayIndex())
n += QString::number(asArrayIndex());
else {
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
index 0b8ad084d2..f3b05ee0d8 100644
--- a/src/qml/jsruntime/qv4propertykey_p.h
+++ b/src/qml/jsruntime/qv4propertykey_p.h
@@ -14,6 +14,7 @@
// We mean it.
//
+#include <private/qv4writebarrier_p.h>
#include <private/qv4global_p.h>
#include <private/qv4staticvalue_p.h>
#include <QtCore/qhashfunctions.h>
@@ -70,11 +71,18 @@ public:
// We cannot #include the declaration of Heap::StringOrSymbol here.
// Therefore we do some gymnastics to enforce the type safety.
- template<typename StringOrSymbol = Heap::StringOrSymbol>
- static PropertyKey fromStringOrSymbol(StringOrSymbol *b)
+ template<typename StringOrSymbol = Heap::StringOrSymbol, typename Engine = QV4::EngineBase>
+ static PropertyKey fromStringOrSymbol(Engine *engine, StringOrSymbol *b)
{
static_assert(std::is_base_of_v<Heap::StringOrSymbol, StringOrSymbol>);
PropertyKey key;
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) {
+ if constexpr (QV4::WriteBarrier::isInsertionBarrier) {
+ // treat this as an insertion - the StringOrSymbol becomes reachable
+ // via the propertykey, so we consequently need to mark it durnig gc
+ b->mark(stack);
+ }
+ });
key.val.setM(b);
Q_ASSERT(key.isManaged());
return key;
@@ -89,11 +97,11 @@ public:
return static_cast<StringOrSymbol *>(val.m());
}
- Q_QML_PRIVATE_EXPORT bool isString() const;
- Q_QML_PRIVATE_EXPORT bool isSymbol() const;
+ Q_QML_EXPORT bool isString() const;
+ Q_QML_EXPORT bool isSymbol() const;
bool isCanonicalNumericIndexString() const;
- Q_QML_PRIVATE_EXPORT QString toQString() const;
+ Q_QML_EXPORT QString toQString() const;
Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e);
quint64 id() const { return val._val; }
static PropertyKey fromId(quint64 id) {
diff --git a/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp b/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp
new file mode 100644
index 0000000000..13d93c7122
--- /dev/null
+++ b/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp
@@ -0,0 +1,126 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qv4qmetaobjectwrapper_p.h"
+
+#include <private/qqmlobjectorgadget_p.h>
+#include <private/qv4jscall_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QV4 {
+
+void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
+{
+ FunctionObject::init();
+ this->metaObject = metaObject;
+ constructors = nullptr;
+ constructorCount = 0;
+}
+
+void Heap::QMetaObjectWrapper::destroy()
+{
+ delete[] constructors;
+}
+
+void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
+
+ const int count = metaObject->constructorCount();
+ if (constructorCount != count) {
+ delete[] constructors;
+ constructorCount = count;
+ if (count == 0) {
+ constructors = nullptr;
+ return;
+ }
+ constructors = new QQmlPropertyData[count];
+
+ for (int i = 0; i < count; ++i) {
+ QMetaMethod method = metaObject->constructor(i);
+ QQmlPropertyData &d = constructors[i];
+ d.load(method);
+ d.setCoreIndex(i);
+ }
+ }
+}
+
+
+ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
+
+ Scope scope(engine);
+ Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QMetaObjectWrapper>(metaObject)->asReturnedValue());
+ mo->init(engine);
+ return mo->asReturnedValue();
+}
+
+void QMetaObjectWrapper::init(ExecutionEngine *) {
+ const QMetaObject & mo = *d()->metaObject;
+
+ for (int i = 0; i < mo.enumeratorCount(); i++) {
+ QMetaEnum Enum = mo.enumerator(i);
+ for (int k = 0; k < Enum.keyCount(); k++) {
+ const char* key = Enum.key(k);
+ const int value = Enum.value(k);
+ defineReadonlyProperty(QLatin1String(key), Value::fromInt32(value));
+ }
+ }
+}
+
+ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
+ return This->constructInternal(argv, argc);
+}
+
+ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const
+{
+
+ d()->ensureConstructorsCache();
+
+ ExecutionEngine *v4 = engine();
+ const QMetaObject* mo = d()->metaObject;
+ if (d()->constructorCount == 0) {
+ return v4->throwTypeError(QLatin1String(mo->className())
+ + QLatin1String(" has no invokable constructor"));
+ }
+
+ Scope scope(v4);
+ Scoped<QObjectWrapper> object(scope);
+ JSCallData cData(nullptr, argv, argc);
+ CallData *callData = cData.callData(scope);
+
+ const QQmlObjectOrGadget objectOrGadget(mo);
+
+ if (d()->constructorCount == 1) {
+ object = QObjectMethod::callPrecise(
+ objectOrGadget, d()->constructors[0], v4, callData, QMetaObject::CreateInstance);
+ } else if (const QQmlPropertyData *ctor = QObjectMethod::resolveOverloaded(
+ objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) {
+ object = QObjectMethod::callPrecise(
+ objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance);
+ }
+ if (object) {
+ Scoped<QMetaObjectWrapper> metaObject(scope, this);
+ object->defineDefaultProperty(v4->id_constructor(), metaObject);
+ object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
+ }
+
+ return object.asReturnedValue();
+}
+
+bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
+{
+ const QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
+ Q_ASSERT(aMetaObject);
+ const QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
+ return bMetaObject && aMetaObject->metaObject() == bMetaObject->metaObject();
+}
+
+DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
+
+} // namespace QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h b/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h
new file mode 100644
index 0000000000..063bc089e7
--- /dev/null
+++ b/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QV4QMETAOBJECTWRAPPER_P_H
+#define QV4QMETAOBJECTWRAPPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4functionobject_p.h>
+#include <private/qv4value_p.h>
+
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPropertyData;
+
+namespace QV4 {
+namespace Heap {
+
+struct QMetaObjectWrapper : FunctionObject {
+ const QMetaObject* metaObject;
+ QQmlPropertyData *constructors;
+ int constructorCount;
+
+ void init(const QMetaObject* metaObject);
+ void destroy();
+ void ensureConstructorsCache();
+};
+
+} // namespace Heap
+
+struct Q_QML_EXPORT QMetaObjectWrapper : public FunctionObject
+{
+ V4_OBJECT2(QMetaObjectWrapper, FunctionObject)
+ V4_NEEDS_DESTROY
+
+ static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
+ const QMetaObject *metaObject() const { return d()->metaObject; }
+
+protected:
+ static ReturnedValue virtualCallAsConstructor(
+ const FunctionObject *, const Value *argv, int argc, const Value *);
+ static bool virtualIsEqualTo(Managed *a, Managed *b);
+
+private:
+ void init(ExecutionEngine *engine);
+ ReturnedValue constructInternal(const Value *argv, int argc) const;
+};
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4QMETAOBJECTWRAPPER_P_H
+
+
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 1cabf7c6c2..53444cddb7 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -19,6 +19,7 @@
#include <private/qv4module_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4stackframe_p.h>
#include <private/qv4value_p.h>
#include <QtCore/qloggingcategory.h>
@@ -196,9 +197,11 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
return result->asReturnedValue();
}
- if (context->imports() && name->startsWithUpper()) {
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine());
+ if (context->imports() && (name->startsWithUpper() || context->valueTypesAreAddressable())) {
// Search for attached properties, enums and imported scripts
- QQmlTypeNameCache::Result r = context->imports()->query<QQmlImport::AllowRecursion>(name);
+ QQmlTypeNameCache::Result r = context->imports()->query<QQmlImport::AllowRecursion>(
+ name, QQmlTypeLoader::get(ep));
if (r.isValid()) {
if (hasProperty)
@@ -220,10 +223,10 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine());
if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) {
e->singletonInstance<QObject*>(r.type);
- lookup->qmlContextSingletonLookup.singletonObject =
+ lookup->qmlContextSingletonLookup.singletonObject.set(v4,
Value::fromReturnedValue(
QQmlTypeWrapper::create(v4, nullptr, r.type)
- ).heapObject();
+ ).heapObject());
} else {
QJSValue singleton = e->singletonInstance<QJSValue>(r.type);
@@ -232,7 +235,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
Q_ASSERT(!QJSValuePrivate::asQString(&singleton));
if (QV4::Value *val = QJSValuePrivate::takeManagedValue(&singleton)) {
- lookup->qmlContextSingletonLookup.singletonObject = val->heapObject();
+ lookup->qmlContextSingletonLookup.singletonObject.set(v4, val->heapObject());
} else {
lookup->qmlContextSingletonLookup.singletonValue = QJSValuePrivate::asReturnedValue(&singleton);
isValueSingleton = true;
@@ -248,7 +251,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
result = QQmlTypeWrapper::create(v4, scopeObject, context->imports(), r.importNamespace);
}
if (lookup) {
- lookup->qmlTypeLookup.qmlTypeWrapper = result->heapObject();
+ lookup->qmlTypeLookup.qmlTypeWrapper.set(v4, result->heapObject());
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupType;
}
return result->asReturnedValue();
@@ -257,7 +260,6 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
// Fall through
}
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine());
Lookup * const originalLookup = lookup;
decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty;
@@ -268,9 +270,33 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty;
}
+ QQmlRefPointer<QQmlContextData> outer = context;
while (context) {
- if (auto property = searchContextProperties(v4, context, name, hasProperty, base, lookup, originalLookup, ep))
- return *property;
+ if (outer == context) {
+ if (auto property = searchContextProperties(
+ v4, context, name, hasProperty, base, lookup, originalLookup, ep)) {
+ return *property;
+ }
+
+ outer = outer->parent();
+
+ if (const auto cu = context->typeCompilationUnit(); cu && cu->componentsAreBound()) {
+ // If components are bound in this CU, we can search the whole context hierarchy
+ // of the file. Bound components' contexts override their local properties.
+ // You also can't instantiate bound components outside of their creation
+ // context. Therefore this is safe.
+
+ for (;
+ outer && outer->typeCompilationUnit() == cu;
+ outer = outer->parent()) {
+ if (auto property = searchContextProperties(
+ v4, outer, name, hasProperty, base,
+ nullptr, originalLookup, ep)) {
+ return *property;
+ }
+ }
+ }
+ }
// Search scope object
if (scopeObject) {
@@ -475,8 +501,8 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *
{
Scope scope(engine);
auto *func = engine->currentStackFrame->v4Function;
- PropertyKey name =engine->identifierTable->asPropertyKey(
- func->compilationUnit->runtimeStrings[l->nameIndex]);
+ ScopedPropertyKey name(scope, engine->identifierTable->asPropertyKey(
+ func->compilationUnit->runtimeStrings[l->nameIndex]));
// Special hack for bounded signal expressions, where the parameters of signals are injected
// into the handler expression through the locals of the call context. So for onClicked: { ... }
@@ -490,7 +516,7 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *
const auto location = func->sourceLocation();
qCWarning(lcQmlContext).nospace().noquote()
<< location.sourceFile << ":" << location.line << ":" << location.column
- << " Parameter \"" << name.toQString() << "\" is not declared."
+ << " Parameter \"" << name->toQString() << "\" is not declared."
<< " Injection of parameters into signal handlers is deprecated."
<< " Use JavaScript functions with formal parameters instead.";
@@ -526,7 +552,7 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *
}
}
if (!hasProperty)
- return engine->throwReferenceError(name.toQString());
+ return engine->throwReferenceError(name->toQString());
return result->asReturnedValue();
}
@@ -789,7 +815,7 @@ ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine,
Heap::Base *heapObject = l->qmlTypeLookup.qmlTypeWrapper;
if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) {
- l->qmlTypeLookup.qmlTypeWrapper = nullptr;
+ l->qmlTypeLookup.qmlTypeWrapper.clear();
l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 694b7828b6..5f85aae89e 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -14,6 +14,9 @@
#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qqmllistwrapper_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
+#if QT_CONFIG(qml_locale)
+#include <private/qqmllocale_p.h>
+#endif
#include <private/qv4arraybuffer_p.h>
#include <private/qv4functionobject_p.h>
@@ -33,6 +36,8 @@
#include <private/qqmlscriptstring_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmlpropertybinding_p.h>
+#include <private/qqmlpropertycachemethodarguments_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <QtQml/qjsvalue.h>
#include <QtCore/qjsonarray.h>
@@ -47,6 +52,7 @@
#endif
#include <QtCore/qloggingcategory.h>
#include <QtCore/qqueue.h>
+#include <QtCore/qtypes.h>
#include <vector>
QT_BEGIN_NAMESPACE
@@ -55,6 +61,7 @@ Q_LOGGING_CATEGORY(lcBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
Q_LOGGING_CATEGORY(lcObjectConnect, "qt.qml.object.connect", QtWarningMsg)
Q_LOGGING_CATEGORY(lcOverloadResolution, "qt.qml.overloadresolution", QtWarningMsg)
Q_LOGGING_CATEGORY(lcMethodBehavior, "qt.qml.method.behavior")
+Q_LOGGING_CATEGORY(lcSignalHandler, "qt.qml.signalhandler")
// The code in this file does not violate strict aliasing, but GCC thinks it does
// so turn off the warnings for us to have a clean build
@@ -130,37 +137,136 @@ static ReturnedValue loadProperty(
if (property.isQList() && propMetaType.flags().testFlag(QMetaType::IsQmlList))
return QmlListWrapper::create(v4, object, property.coreIndex(), propMetaType);
- // TODO: Check all the builtin types here. See getGadgetProperty() in qqmlvaluetypewrapper.cpp
- switch (property.isEnum() ? QMetaType::Int : propMetaType.id()) {
- case QMetaType::Int: {
- int v = 0;
+ const auto encodeSimple = [&](auto v) {
property.readProperty(object, &v);
return Encode(v);
+ };
+
+ const auto encodeInt = [&](auto v) {
+ property.readProperty(object, &v);
+ return Encode(int(v));
+ };
+
+ const auto encodeDouble = [&](auto v) {
+ property.readProperty(object, &v);
+ return Encode(double(v));
+ };
+
+ const auto encodeDate = [&](auto v) {
+ property.readProperty(object, &v);
+ return Encode(v4->newDateObject(
+ v, wrapper, property.coreIndex(), referenceFlags(scope.engine, property)));
+ };
+
+ const auto encodeString = [&](auto v) {
+ property.readProperty(object, &v);
+ return v4->newString(v)->asReturnedValue();
+ };
+
+ const auto encodeSequence = [&](QMetaSequence metaSequence) {
+ // Pass nullptr as data. It's lazy-loaded.
+ return QV4::SequencePrototype::newSequence(
+ v4, propMetaType, metaSequence, nullptr,
+ wrapper, property.coreIndex(), referenceFlags(scope.engine, property));
+ };
+
+
+ switch (property.isEnum() ? propMetaType.underlyingType().id() : propMetaType.id()) {
+ case QMetaType::UnknownType:
+ case QMetaType::Void:
+ return Encode::undefined();
+ case QMetaType::Nullptr:
+ case QMetaType::VoidStar:
+ return Encode::null();
+ case QMetaType::Int:
+ return encodeSimple(int());
+ case QMetaType::Bool:
+ return encodeSimple(bool());
+ case QMetaType::QString:
+ return encodeString(QString());
+ case QMetaType::QByteArray: {
+ QByteArray v;
+ property.readProperty(object, &v);
+ return v4->newArrayBuffer(v)->asReturnedValue();
}
- case QMetaType::Bool: {
- bool v = false;
+ case QMetaType::QChar:
+ return encodeString(QChar());
+ case QMetaType::Char16:
+ return encodeString(char16_t());
+ case QMetaType::UInt:
+ return encodeSimple(uint());
+ case QMetaType::Float:
+ return encodeSimple(float());
+ case QMetaType::Double:
+ return encodeSimple(double());
+ case QMetaType::Short:
+ return encodeInt(short());
+ case QMetaType::UShort:
+ return encodeInt(ushort());
+ case QMetaType::Char:
+ return encodeInt(char());
+ case QMetaType::UChar:
+ return encodeInt(uchar());
+ case QMetaType::SChar:
+ return encodeInt(qint8());
+ case QMetaType::Long:
+ return encodeDouble(long());
+ case QMetaType::ULong:
+ return encodeDouble(ulong());
+ case QMetaType::LongLong:
+ return encodeDouble(qlonglong());
+ case QMetaType::ULongLong:
+ return encodeDouble(qulonglong());
+ case QMetaType::QDateTime:
+ return encodeDate(QDateTime());
+ case QMetaType::QDate:
+ return encodeDate(QDate());
+ case QMetaType::QTime:
+ return encodeDate(QTime());
+#if QT_CONFIG(regularexpression)
+ case QMetaType::QRegularExpression: {
+ QRegularExpression v;
property.readProperty(object, &v);
- return Encode(v);
+ return Encode(v4->newRegExpObject(v));
}
- case QMetaType::QString: {
- QString v;
+#endif
+ case QMetaType::QVariantMap: {
+ QVariantMap v;
property.readProperty(object, &v);
- return v4->newString(v)->asReturnedValue();
+ return scope.engine->fromData(
+ propMetaType, &v, wrapper, property.coreIndex(), referenceFlags(v4, property));
}
- case QMetaType::UInt: {
- uint v = 0;
+ case QMetaType::QJsonValue: {
+ QJsonValue v;
property.readProperty(object, &v);
- return Encode(v);
+ return QV4::JsonObject::fromJsonValue(v4, v);
}
- case QMetaType::Float: {
- float v = 0;
+ case QMetaType::QJsonObject: {
+ QJsonObject v;
property.readProperty(object, &v);
- return Encode(v);
+ return QV4::JsonObject::fromJsonObject(v4, v);
}
- case QMetaType::Double: {
- double v = 0;
+ case QMetaType::QJsonArray: {
+ QJsonArray v;
property.readProperty(object, &v);
- return Encode(v);
+ return QV4::JsonObject::fromJsonArray(v4, v);
+ }
+ case QMetaType::QStringList:
+ return encodeSequence(QMetaSequence::fromContainer<QStringList>());
+ case QMetaType::QVariantList:
+ return encodeSequence(QMetaSequence::fromContainer<QVariantList>());
+ case QMetaType::QUrl: {
+ // ### Qt7: We really want this to be a JS URL object, but that would break things.
+ QUrl v;
+ property.readProperty(object, &v);
+ return Encode(v4->newVariantObject(propMetaType, &v));
+ }
+ case QMetaType::QPixmap:
+ case QMetaType::QImage: {
+ // Scarce value types
+ QVariant v(propMetaType);
+ property.readProperty(object, v.data());
+ return Encode(v4->newVariantObject(propMetaType, v.constData()));
}
default:
break;
@@ -204,13 +310,9 @@ static ReturnedValue loadProperty(
}
// See if it's a sequence type.
- // Pass nullptr as data. It's lazy-loaded.
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(
- v4, propMetaType, nullptr,
- wrapper, property.coreIndex(),
- referenceFlags(scope.engine, property)));
- if (!retn->isUndefined())
- return retn->asReturnedValue();
+ const QQmlType qmlType = QQmlMetaType::qmlListType(propMetaType);
+ if (qmlType.isSequentialContainer())
+ return encodeSequence(qmlType.listMetaSequence());
QVariant v(propMetaType);
property.readProperty(object, v.data());
@@ -263,7 +365,7 @@ ReturnedValue QObjectWrapper::getProperty(
if (!global)
global = engine->rootContext();
return QObjectMethod::create(
- global, (flags & AttachMethods) ? object : nullptr, property->coreIndex());
+ global, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
return engine->memoryManager->allocate<QmlSignalHandler>(
@@ -271,7 +373,7 @@ ReturnedValue QObjectWrapper::getProperty(
} else {
ExecutionContext *global = engine->rootContext();
return QObjectMethod::create(
- global, (flags & AttachMethods) ? object : nullptr, property->coreIndex());
+ global, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
}
}
@@ -290,7 +392,8 @@ ReturnedValue QObjectWrapper::getProperty(
}
}
-static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
+static OptionalReturnedValue getDestroyOrToStringMethod(
+ ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
{
int index = 0;
if (name->equals(v4->id_destroy()))
@@ -313,24 +416,29 @@ static OptionalReturnedValue getPropertyFromImports(
if (!qmlContext || !qmlContext->imports())
return OptionalReturnedValue();
- QQmlTypeNameCache::Result r = qmlContext->imports()->query(name);
-
if (hasProperty)
*hasProperty = true;
- if (!r.isValid())
- return OptionalReturnedValue();
+ if (QQmlTypeLoader *typeLoader = v4->typeLoader()) {
+ QQmlTypeNameCache::Result r = qmlContext->imports()->query(name, typeLoader);
- if (r.scriptIndex != -1) {
- return OptionalReturnedValue(Encode::undefined());
- } else if (r.type.isValid()) {
- return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
- } else if (r.importNamespace) {
- return OptionalReturnedValue(QQmlTypeWrapper::create(
- v4, qobj, qmlContext->imports(), r.importNamespace,
- Heap::QQmlTypeWrapper::ExcludeEnums));
+ if (!r.isValid())
+ return OptionalReturnedValue();
+
+ if (r.scriptIndex != -1) {
+ return OptionalReturnedValue(Encode::undefined());
+ } else if (r.type.isValid()) {
+ return OptionalReturnedValue(
+ QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
+ } else if (r.importNamespace) {
+ return OptionalReturnedValue(QQmlTypeWrapper::create(
+ v4, qobj, qmlContext->imports(), r.importNamespace,
+ Heap::QQmlTypeWrapper::ExcludeEnums));
+ }
+ Q_UNREACHABLE_RETURN(OptionalReturnedValue());
+ } else {
+ return OptionalReturnedValue();
}
- Q_UNREACHABLE_RETURN(OptionalReturnedValue());
}
ReturnedValue QObjectWrapper::getQmlProperty(
@@ -347,7 +455,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
ExecutionEngine *v4 = engine();
- if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty))
return *methodValue;
QQmlPropertyData local;
@@ -390,7 +498,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
return Encode::null();
}
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, wrapper, hasProperty))
return *methodValue;
QQmlData *ddata = QQmlData::get(object, false);
@@ -465,6 +573,28 @@ bool QObjectWrapper::setQmlProperty(
return true;
}
+/*!
+ \internal
+ If an QObjectWrapper is created via wrap, then it needs to be stored somewhere.
+ Otherwise, the garbage collector will immediately collect it if it is already
+ past the "mark QObjectWrapper's" phase (note that QObjectWrapper are marked
+ by iterating over a list of all QObjectWrapper, and then checking if the
+ wrapper fulfills some conditions).
+ However, sometimes we don't really want to keep a reference to the wrapper,
+ but just want to make sure that it exists (and we know that the wrapper
+ already fulfills the conditions to be kept alive). Then ensureWrapper
+ can be used, which creates the wrapper and ensures that it is also
+ marked.
+ */
+void QObjectWrapper::ensureWrapper(ExecutionEngine *engine, QObject *object)
+{
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::QObjectWrapper> wrapper {scope, QV4::QObjectWrapper::wrap(engine, object)};
+ QV4::WriteBarrier::markCustom(engine, [&wrapper](QV4::MarkStack *ms) {
+ wrapper->mark(ms);
+ });
+}
+
void QObjectWrapper::setProperty(
ExecutionEngine *engine, QObject *object,
const QQmlPropertyData *property, const Value &value)
@@ -551,14 +681,26 @@ void QObjectWrapper::setProperty(
if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) {
- Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding);
- const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
const auto stackFrame = engine->currentStackFrame;
- qCInfo(lcBindingRemoval,
- "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
- object->metaObject()->className(), qPrintable(property->name(object)),
- qPrintable(stackFrame->source()), stackFrame->lineNumber(),
- qPrintable(qmlBinding->expressionIdentifier()));
+ switch (binding->kind()) {
+ case QQmlAbstractBinding::QmlBinding: {
+ const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
+ qCInfo(lcBindingRemoval,
+ "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
+ object->metaObject()->className(), qPrintable(property->name(object)),
+ qPrintable(stackFrame->source()), stackFrame->lineNumber(),
+ qPrintable(qmlBinding->expressionIdentifier()));
+ break;
+ }
+ case QQmlAbstractBinding::ValueTypeProxy:
+ case QQmlAbstractBinding::PropertyToPropertyBinding: {
+ qCInfo(lcBindingRemoval,
+ "Overwriting binding on %s::%s at %s:%d",
+ object->metaObject()->className(), qPrintable(property->name(object)),
+ qPrintable(stackFrame->source()), stackFrame->lineNumber());
+ break;
+ }
+ }
}
}
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
@@ -602,7 +744,7 @@ void QObjectWrapper::setProperty(
scope.engine->throwError(error);
return;
} else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
- PROPERTY_STORE(int, value.asDouble());
+ PROPERTY_STORE(int, value.toInt32());
} else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
PROPERTY_STORE(qreal, qreal(value.asDouble()));
} else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
@@ -724,15 +866,6 @@ ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObjec
return constWrapper.asReturnedValue();
}
-Heap::QObjectMethod *QObjectWrapper::cloneMethod(
- ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
- Heap::Object *wrapper, QObject *object)
-{
- Scope scope(engine);
- Scoped<QObjectMethod> method(scope, QObjectMethod::create(engine, cloneFrom, wrapper, object));
- return method ? method->d() : nullptr;
-}
-
void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
{
if (QQmlData::wasDeleted(object))
@@ -972,7 +1105,7 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
return Encode::undefined();
QQmlData *ddata = QQmlData::get(qobj, false);
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) {
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, This->d())) {
Scoped<QObjectMethod> method(scope, *methodValue);
setupQObjectMethodLookup(
lookup, ddata ? ddata : QQmlData::get(qobj, true), nullptr, This, method->d());
@@ -1004,7 +1137,8 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
&& !property->isVarProperty()
&& !property->isVMEFunction() // Handled by QObjectLookup
&& !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
- setupQObjectMethodLookup(lookup, ddata, property, This, nullptr);
+ QV4::Heap::QObjectMethod *method = nullptr;
+ setupQObjectMethodLookup(lookup, ddata, property, This, method);
lookup->getter = Lookup::getterQObjectMethod;
return lookup->getter(lookup, engine, *object);
}
@@ -1081,13 +1215,15 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
{
- Q_UNUSED(receiver);
switch (which) {
case Destroy: {
delete static_cast<QObjectSlotDispatcher*>(this_);
}
break;
case Call: {
+ if (QQmlData::wasDeleted(receiver))
+ break;
+
QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
ExecutionEngine *v4 = This->function.engine();
// Might be that we're still connected to a signal that's emitted long
@@ -1096,7 +1232,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
if (!v4)
break;
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
int argCount = storage.size();
@@ -1236,7 +1372,16 @@ ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Valu
}
QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect
- if (QObject *receiver = functionData.first) {
+ QObject *receiver = nullptr;
+
+ if (functionData.first)
+ receiver = functionData.first;
+ else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>())
+ receiver = qobjectWrapper->object();
+ else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>())
+ receiver = typeWrapper->object();
+
+ if (receiver) {
QObjectPrivate::connect(signalObject, signalIndex, receiver, slot, Qt::AutoConnection);
} else {
qCInfo(lcObjectConnect,
@@ -1294,7 +1439,16 @@ ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const V
&functionData.second
};
- if (QObject *receiver = functionData.first) {
+ QObject *receiver = nullptr;
+
+ if (functionData.first)
+ receiver = functionData.first;
+ else if (auto qobjectWrapper = functionThisValue->as<QV4::QObjectWrapper>())
+ receiver = qobjectWrapper->object();
+ else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>())
+ receiver = typeWrapper->object();
+
+ if (receiver) {
QObjectPrivate::disconnect(signalObject, signalIndex, receiver,
reinterpret_cast<void **>(&a));
} else {
@@ -1324,9 +1478,37 @@ void Heap::QObjectWrapper::markObjects(Heap::Base *that, MarkStack *markStack)
QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
if (QObject *o = This->object()) {
- QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
- if (vme)
- vme->mark(markStack);
+ if (QQmlData *ddata = QQmlData::get(o)) {
+ if (ddata->hasVMEMetaObject) {
+ if (QQmlVMEMetaObject *vme
+ = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
+ vme->mark(markStack);
+ }
+ }
+
+ if (ddata->hasConstWrapper) {
+ Scope scope(that->internalClass->engine);
+ Q_ASSERT(scope.engine->m_multiplyWrappedQObjects);
+
+ Scoped<QV4::QObjectWrapper> constWrapper(
+ scope,
+ scope.engine->m_multiplyWrappedQObjects->value(
+ static_cast<const QObject *>(o)));
+
+ Q_ASSERT(constWrapper);
+
+ if (This == constWrapper->d()) {
+ // We've got the const wrapper. Also mark the non-const one
+ if (ddata->jsEngineId == scope.engine->m_engineId)
+ ddata->jsWrapper.markOnce(markStack);
+ else
+ scope.engine->m_multiplyWrappedQObjects->mark(o, markStack);
+ } else {
+ // We've got the non-const wrapper. Also mark the const one.
+ constWrapper->mark(markStack);
+ }
+ }
+ }
// Children usually don't need to be marked, the gc keeps them alive.
// But in the rare case of a "floating" QObject without a parent that
@@ -1350,13 +1532,22 @@ void QObjectWrapper::destroyObject(bool lastCall)
if (!o->parent() && !ddata->indestructible) {
if (ddata && ddata->ownContext) {
Q_ASSERT(ddata->ownContext.data() == ddata->context);
- ddata->ownContext->emitDestruction();
+ ddata->ownContext->deepClearContextObject(o);
ddata->ownContext.reset();
ddata->context = nullptr;
}
- // This object is notionally destroyed now
+
+ // This object is notionally destroyed now. It might still live until the next
+ // event loop iteration, but it won't need its connections, CU, or deferredData
+ // anymore.
+
ddata->isQueuedForDeletion = true;
- ddata->disconnectNotifiers();
+ ddata->disconnectNotifiers(QQmlData::DeleteNotifyList::No);
+ ddata->compilationUnit.reset();
+
+ qDeleteAll(ddata->deferredData);
+ ddata->deferredData.clear();
+
if (lastCall)
delete o;
else
@@ -1476,7 +1667,7 @@ static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMe
const bool is_signal =
object.metaObject()->method(index).methodType() == QMetaMethod::Signal;
if (is_signal) {
- qWarning() << "Passing incomatible arguments to signals is not supported.";
+ qWarning() << "Passing incompatible arguments to signals is not supported.";
} else {
return engine->throwTypeError(
QLatin1String("Passing incompatible arguments to C++ functions from "
@@ -1512,6 +1703,26 @@ static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMe
}
}
+template<typename Retrieve>
+int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
+ if (conversionMetaType == QMetaType::fromType<QVariant>())
+ return 0;
+
+ const QMetaType type = retrieve();
+ if (type == conversionMetaType)
+ return 0;
+
+ if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
+ if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
+ return 1;
+ }
+
+ if (QMetaType::canConvert(type, conversionMetaType))
+ return 5;
+
+ return 10;
+};
+
/*
Returns the match score for converting \a actual to be of type \a conversionType. A
zero score means "perfect match" whereas a higher score is worse.
@@ -1625,22 +1836,48 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
}
}
} else if (const Object *obj = actual.as<Object>()) {
- if (obj->as<VariantObject>()) {
- if (conversionType == qMetaTypeId<QVariant>())
- return 0;
- if (ExecutionEngine::toVariant(actual, QMetaType {}).metaType() == conversionMetaType)
- return 0;
- else
- return 10;
+ if (const VariantObject *variantObject = obj->as<VariantObject>()) {
+ return MatchVariant(conversionMetaType, [variantObject]() {
+ return variantObject->d()->data().metaType();
+ });
}
- if (obj->as<QObjectWrapper>()) {
+ if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
switch (conversionType) {
case QMetaType::QObjectStar:
return 0;
default:
- return 10;
+ if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
+ QObject *wrapped = wrapper->object();
+ if (!wrapped)
+ return 0;
+ if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
+ return 0;
+ }
+ }
+ return 10;
+ }
+
+ if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
+ const QQmlType type = wrapper->d()->type();
+ if (type.isSingleton()) {
+ const QMetaType metaType = type.typeId();
+ if (metaType == conversionMetaType)
+ return 0;
+
+ if (conversionMetaType.flags() & QMetaType::PointerToQObject
+ && metaType.flags() & QMetaType::PointerToQObject
+ && type.metaObject()->inherits(conversionMetaType.metaObject())) {
+ return 0;
+ }
+ } else if (QObject *object = wrapper->object()) {
+ if (conversionMetaType.flags() & QMetaType::PointerToQObject
+ && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
+ return 0;
+ }
}
+
+ return 10;
}
if (const Sequence *sequence = obj->as<Sequence>()) {
@@ -1650,13 +1887,12 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
return 10;
}
- if (obj->as<QQmlValueTypeWrapper>()) {
- const QVariant v = ExecutionEngine::toVariant(actual, QMetaType {});
- if (v.userType() == conversionType)
- return 0;
- else if (v.canConvert(conversionMetaType))
- return 5;
- return 10;
+ if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
+ return MatchVariant(conversionMetaType, [wrapper]() {
+ return wrapper->d()->isVariant()
+ ? wrapper->toVariant().metaType()
+ : wrapper->type();
+ });
}
if (conversionType == QMetaType::QJsonObject)
@@ -1680,9 +1916,17 @@ static int numDefinedArguments(CallData *callArgs)
return numDefinedArguments;
}
-static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
- ExecutionEngine *engine, CallData *callArgs,
- QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
+static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
+{
+ const QMetaObject *metaObject = object.metaObject();
+ const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
+ return indexOfClassInfo != -1
+ && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
+}
+
+ReturnedValue QObjectMethod::callPrecise(
+ const QQmlObjectOrGadget &object, const QQmlPropertyData &data, ExecutionEngine *engine,
+ CallData *callArgs, QMetaObject::Call callType)
{
QByteArray unknownTypeError;
@@ -1694,11 +1938,7 @@ static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPro
}
auto handleTooManyArguments = [&](int expectedArguments) {
- const QMetaObject *metaObject = object.metaObject();
- const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
- if (indexOfClassInfo != -1
- && QString::fromUtf8(metaObject->classInfo(indexOfClassInfo).value())
- == QStringLiteral("true")) {
+ if (requiresStrictArguments(object)) {
engine->throwError(QStringLiteral("Too many arguments"));
return false;
}
@@ -1709,7 +1949,7 @@ static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPro
<< "When matching arguments for "
<< object.className() << "::" << data.name(object.metaObject()) << "():";
} else {
- const StackFrame frame = engine->stackTrace().first();
+ const StackFrame frame = stackTrace.first();
qWarning().noquote() << frame.function + QLatin1Char('@') + frame.source
+ (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
: QString());
@@ -1724,7 +1964,7 @@ static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPro
if (data.hasArguments()) {
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
bool ok = false;
if (data.isConstructor())
@@ -1771,7 +2011,7 @@ Resolve the overloaded method to call. The algorithm works conceptually like th
If two or more overloads have the same match score, return the last one. The match
score is constructed by adding the matchScore() result for each of the parameters.
*/
-static const QQmlPropertyData *ResolveOverloaded(
+const QQmlPropertyData *QObjectMethod::resolveOverloaded(
const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
ExecutionEngine *engine, CallData *callArgs)
{
@@ -1789,12 +2029,12 @@ static const QQmlPropertyData *ResolveOverloaded(
for (int i = 0; i < methodCount; ++i) {
const QQmlPropertyData *attempt = methods + i;
- if (lcOverloadResolution().isInfoEnabled()) {
+ if (lcOverloadResolution().isDebugEnabled()) {
const QQmlPropertyData &candidate = methods[i];
const QMetaMethod m = candidate.isConstructor()
? object.metaObject()->constructor(candidate.coreIndex())
: object.metaObject()->method(candidate.coreIndex());
- qCInfo(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
+ qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
}
// QQmlV4Function overrides anything that doesn't provide the exact number of arguments
@@ -1805,17 +2045,17 @@ static const QQmlPropertyData *ResolveOverloaded(
int sumMethodMatchScore = bestSumMatchScore;
if (!attempt->isV4Function()) {
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
int methodArgumentCount = 0;
if (attempt->hasArguments()) {
if (attempt->isConstructor()) {
if (!object.constructorParameterTypes(attempt->coreIndex(), &storage, nullptr)) {
- qCInfo(lcOverloadResolution, "rejected, could not get ctor argument types");
+ qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
continue;
}
} else {
if (!object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr)) {
- qCInfo(lcOverloadResolution, "rejected, could not get ctor argument types");
+ qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
continue;
}
}
@@ -1823,7 +2063,7 @@ static const QQmlPropertyData *ResolveOverloaded(
}
if (methodArgumentCount > argumentCount) {
- qCInfo(lcOverloadResolution, "rejected, insufficient arguments");
+ qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
continue; // We don't have sufficient arguments to call this method
}
@@ -1831,7 +2071,7 @@ static const QQmlPropertyData *ResolveOverloaded(
? 0
: (definedArgumentCount - methodArgumentCount + 1);
if (methodParameterScore > bestParameterScore) {
- qCInfo(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
+ qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
continue; // We already have a better option
}
@@ -1853,21 +2093,21 @@ static const QQmlPropertyData *ResolveOverloaded(
bestParameterScore = methodParameterScore;
bestMaxMatchScore = maxMethodMatchScore;
bestSumMatchScore = sumMethodMatchScore;
- qCInfo(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
- << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
- << "bestSumMatchScore" << bestSumMatchScore << "\n";
+ qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
+ << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
+ << "bestSumMatchScore" << bestSumMatchScore << "\n";
} else {
- qCInfo(lcOverloadResolution) << "did not update best\n"
- << "bestParameterScore" << bestParameterScore << "\t"
- << "methodParameterScore" << methodParameterScore << "\n"
- << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
- << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
- << "bestSumMatchScore" << bestSumMatchScore << "\t"
- << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
+ qCDebug(lcOverloadResolution) << "did not update best\n"
+ << "bestParameterScore" << bestParameterScore << "\t"
+ << "methodParameterScore" << methodParameterScore << "\n"
+ << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
+ << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
+ << "bestSumMatchScore" << bestSumMatchScore << "\t"
+ << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
}
if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
- qCInfo(lcOverloadResolution, "perfect match");
+ qCDebug(lcOverloadResolution, "perfect match");
break; // We can't get better than that
}
@@ -1878,13 +2118,11 @@ static const QQmlPropertyData *ResolveOverloaded(
} else {
QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
for (int i = 0; i < methodCount; ++i) {
- for (int i = 0; i < methodCount; ++i) {
- const QQmlPropertyData &candidate = methods[i];
- const QMetaMethod m = candidate.isConstructor()
- ? object.metaObject()->constructor(candidate.coreIndex())
- : object.metaObject()->method(candidate.coreIndex());
- error += u"\n " + QString::fromUtf8(m.methodSignature());
- }
+ const QQmlPropertyData &candidate = methods[i];
+ const QMetaMethod m = candidate.isConstructor()
+ ? object.metaObject()->constructor(candidate.coreIndex())
+ : object.metaObject()->method(candidate.coreIndex());
+ error += u"\n " + QString::fromUtf8(m.methodSignature());
}
engine->throwError(error);
@@ -1892,7 +2130,69 @@ static const QQmlPropertyData *ResolveOverloaded(
}
}
+static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
+{
+ if (required == QMetaType::fromType<QVariant>()
+ || required == QMetaType::fromType<QJSValue>()
+ || required == QMetaType::fromType<QJSManagedValue>()) {
+ return true;
+ }
+ if (data) {
+ if (passed == QMetaType::fromType<QVariant>())
+ passed = static_cast<const QVariant *>(data)->metaType();
+ else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
+ passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
+ }
+
+ if (passed == required)
+ return true;
+
+ if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
+ switch (passed.id()) {
+ case QMetaType::UnknownType:
+ case QMetaType::Nullptr:
+ case QMetaType::Bool:
+ case QMetaType::Int:
+ case QMetaType::Double:
+ case QMetaType::QString:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+const QQmlPropertyData *QObjectMethod::resolveOverloaded(
+ const QQmlPropertyData *methods, int methodCount,
+ void **argv, int argc, const QMetaType *types)
+{
+ // We only accept exact matches here. Everything else goes through the JavaScript conversion.
+ for (int i = 0; i < methodCount; ++i) {
+ const QQmlPropertyData *attempt = methods + i;
+ if (types[0].isValid() && !ExactMatch(attempt->propType(), types[0], nullptr))
+ continue;
+
+ const QMetaMethod method = attempt->metaMethod();
+ if (method.parameterCount() != argc)
+ continue;
+
+ bool valid = true;
+ for (int i = 0; i < argc; ++i) {
+ if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1])) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid)
+ return attempt;
+ }
+
+ return nullptr;
+}
void CallArgument::cleanup()
{
@@ -2057,6 +2357,10 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
else
qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
return true;
+ case QMetaType::QByteArray:
+ qbyteArrayPtr = new (&allocData) QByteArray();
+ ExecutionEngine::metaTypeFromJS(value, metaType, qbyteArrayPtr);
+ return true;
case QMetaType::QObjectStar:
if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
qobjectPtr = qobjectWrapper->object();
@@ -2087,8 +2391,8 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
return true;
case QMetaType::QJsonArray: {
Scope scope(engine);
- ScopedArrayObject a(scope, value);
- jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(a));
+ ScopedObject o(scope, value);
+ jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(o));
return true;
}
case QMetaType::QJsonObject: {
@@ -2135,6 +2439,11 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
return true;
}
+ if (const QmlListWrapper *listWrapper = value.as<QmlListWrapper>()) {
+ *qlistPtr = listWrapper->d()->property()->toList<QList<QObject *>>();
+ return true;
+ }
+
qlistPtr->append(nullptr);
return value.isNullOrUndefined();
}
@@ -2175,37 +2484,14 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
}
// Convert via QVariant through the QML engine.
- qvariantPtr = new (&allocData) QVariant();
+ qvariantPtr = new (&allocData) QVariant(metaType);
type = QVariantWrappedType;
- QVariant v = ExecutionEngine::toVariant(value, metaType);
-
- if (v.metaType() == metaType) {
- *qvariantPtr = std::move(v);
- return true;
- }
-
- if (v.canConvert(metaType)) {
- *qvariantPtr = std::move(v);
- qvariantPtr->convert(metaType);
- return true;
- }
-
- const QQmlMetaObject mo = QQmlMetaType::rawMetaObjectForType(metaType);
- if (!mo.isNull() && v.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
- QObject *obj = QQmlMetaType::toQObject(v);
-
- if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) {
- *qvariantPtr = QVariant(metaType, nullptr);
- return false;
- }
-
- *qvariantPtr = QVariant(metaType, &obj);
+ if (ExecutionEngine::metaTypeFromJS(value, metaType, qvariantPtr->data()))
return true;
- }
- *qvariantPtr = QVariant(metaType, (void *)nullptr);
- return false;
+ const QVariant v = ExecutionEngine::toVariant(value, metaType);
+ return QMetaType::convert(v.metaType(), v.constData(), metaType, qvariantPtr->data());
}
ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
@@ -2273,34 +2559,33 @@ ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
return Encode::undefined();
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
+ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::Object *wrapper, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->setObject(object);
-
- method->d()->index = index;
+ Scoped<QObjectMethod> method(
+ valueScope,
+ valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, wrapper, index));
return method.asReturnedValue();
}
ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->index = index;
- method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
+ Scoped<QObjectMethod> method(
+ valueScope,
+ valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, valueType, index));
return method.asReturnedValue();
}
ReturnedValue QObjectMethod::create(
ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
- Heap::Object *wrapper, QObject *object)
+ Heap::Object *wrapper, Heap::Object *object)
{
Scope valueScope(engine);
Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope);
- if (cloneFrom->valueTypeWrapper) {
- Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->valueTypeWrapper);
+ if (cloneFrom->wrapper) {
+ Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->wrapper);
if (ref) {
valueTypeWrapper = QQmlValueTypeWrapper::create(engine, ref->d(), wrapper);
} else {
@@ -2313,71 +2598,96 @@ ReturnedValue QObjectMethod::create(
Scoped<ExecutionContext> context(valueScope, cloneFrom->scope.get());
Scoped<QObjectMethod> method(
- valueScope, engine->memoryManager->allocate<QV4::QObjectMethod>(context));
+ valueScope,
+ engine->memoryManager->allocate<QV4::QObjectMethod>(
+ context, valueTypeWrapper ? valueTypeWrapper->d() : object, cloneFrom->index));
- method->d()->index = cloneFrom->index;
method->d()->methodCount = cloneFrom->methodCount;
- if (valueTypeWrapper)
- method->d()->valueTypeWrapper.set(engine, valueTypeWrapper->d());
- else
- method->d()->setObject(object);
-
- if (cloneFrom->methodCount == 1) {
+ Q_ASSERT(method->d()->methods == nullptr);
+ switch (cloneFrom->methodCount) {
+ case 0:
+ Q_ASSERT(cloneFrom->methods == nullptr);
+ break;
+ case 1:
+ Q_ASSERT(cloneFrom->methods
+ == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
*method->d()->methods = *cloneFrom->methods;
- } else {
+ break;
+ default:
+ Q_ASSERT(cloneFrom->methods != nullptr);
method->d()->methods = new QQmlPropertyData[cloneFrom->methodCount];
memcpy(method->d()->methods, cloneFrom->methods,
cloneFrom->methodCount * sizeof(QQmlPropertyData));
+ break;
}
+
return method.asReturnedValue();
}
-void Heap::QObjectMethod::init(QV4::ExecutionContext *scope)
+void Heap::QObjectMethod::init(QV4::ExecutionContext *scope, Object *object, int methodIndex)
{
Heap::FunctionObject::init(scope);
+ wrapper.set(internalClass->engine, object);
+ index = methodIndex;
}
const QMetaObject *Heap::QObjectMethod::metaObject() const
{
- if (valueTypeWrapper)
- return valueTypeWrapper->metaObject();
+ Scope scope(internalClass->engine);
+
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
+ return valueWrapper->metaObject();
if (QObject *self = object())
return self->metaObject();
+
+ return nullptr;
+}
+
+QObject *Heap::QObjectMethod::object() const
+{
+ Scope scope(internalClass->engine);
+
+ if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
+ return objectWrapper->object();
+ if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
+ return typeWrapper->object();
return nullptr;
}
bool Heap::QObjectMethod::isDetached() const
{
- if (qObj.isValid())
- return false;
+ if (!wrapper)
+ return true;
- if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get())
- return wrapper->object() == nullptr;
+ QV4::Scope scope(internalClass->engine);
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
+ return valueWrapper->d()->object() == nullptr;
- return true;
+ return false;
}
bool Heap::QObjectMethod::isAttachedTo(QObject *o) const
{
- if (qObj.isValid() && qObj != o)
- return false;
+ QV4::Scope scope(internalClass->engine);
+ if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
+ return objectWrapper->object() == o;
+ if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
+ return typeWrapper->object() == o;
- if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) {
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper) {
QV4::Scope scope(wrapper->internalClass->engine);
- QV4::Scoped<QV4::QObjectWrapper> qobject(scope, wrapper->object());
- if (qobject && qobject->object() == o)
- return true;
- QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, wrapper->object());
- if (type && type->object() == o)
- return true;
+ if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, valueWrapper->d()->object()); qobject)
+ return qobject->object() == o;
+ if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, valueWrapper->d()->object()); type)
+ return type->object() == o;
// Attached to some nested value type or sequence object
return false;
}
- return true;
+ return false;
}
Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
@@ -2387,7 +2697,7 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
if (!thisMeta) {
// You can only get a detached method via a lookup, and then you have a thisObject.
- Q_ASSERT(valueTypeWrapper || qObj);
+ Q_ASSERT(wrapper);
return Included;
}
@@ -2405,6 +2715,10 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
return Included;
}
+ // destroy() and toString() can be called on all QObjects, but not on gadgets.
+ if (index < 0)
+ return thisMeta->inherits(&QObject::staticMetaObject) ? Explicit : Invalid;
+
// Find the base type the method belongs to.
int methodOffset = included->methodOffset();
while (true) {
@@ -2414,19 +2728,16 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
if (methodOffset <= index)
return thisMeta->inherits(included) ? Explicit : Invalid;
- methodOffset -= QMetaObjectPrivate::get(included)->methodCount;
included = included->superClass();
Q_ASSERT(included);
+ methodOffset -= QMetaObjectPrivate::get(included)->methodCount;
};
Q_UNREACHABLE_RETURN(Invalid);
};
- if (QObject *o = object())
- return check(o->metaObject());
-
- if (valueTypeWrapper)
- return check(valueTypeWrapper->metaObject());
+ if (const QMetaObject *meta = metaObject())
+ return check(meta);
// If the QObjectMethod is detached, we can only have gotten here via a lookup.
// The lookup checks that the QQmlPropertyCache matches.
@@ -2456,8 +2767,10 @@ QString Heap::QObjectMethod::name() const
void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
{
- if (methods)
+ if (methods) {
+ Q_ASSERT(methodCount > 0);
return;
+ }
const QMetaObject *mo = metaObject();
@@ -2475,6 +2788,7 @@ void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
QQmlPropertyData dummy;
QMetaMethod method = mo->method(index);
dummy.load(method);
+ dummy.setMetaObject(mo);
resolvedMethods.append(dummy);
// Look for overloaded methods
QByteArray methodName = method.name();
@@ -2494,15 +2808,8 @@ void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
*methods = resolvedMethods.at(0);
methodCount = 1;
}
-}
-static QObject *qObject(const Value *v)
-{
- if (const QObjectWrapper *o = v->as<QObjectWrapper>())
- return o->object();
- if (const QQmlTypeWrapper *t = v->as<QQmlTypeWrapper>())
- return t->object();
- return nullptr;
+ Q_ASSERT(methodCount > 0);
}
ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, QObject *o) const
@@ -2533,32 +2840,44 @@ ReturnedValue QObjectMethod::method_destroy(
return Encode::undefined();
}
-ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue QObjectMethod::virtualCall(
+ const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
{
const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
return This->callInternal(thisObject, argv, argc);
}
+void QObjectMethod::virtualCallWithMetaTypes(
+ const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
+{
+ const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
+ This->callInternalWithMetaTypes(thisObject, argv, types, argc);
+}
+
ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const
{
ExecutionEngine *v4 = engine();
const QMetaObject *thisMeta = nullptr;
- QObject *o = qObject(thisObject);
- Heap::QQmlValueTypeWrapper *wrapper = nullptr;
- if (o) {
- thisMeta = o->metaObject();
- } else if (const QQmlValueTypeWrapper *value = thisObject->as<QQmlValueTypeWrapper>()) {
- wrapper = value->d();
- thisMeta = wrapper->metaObject();
+ QObject *o = nullptr;
+ Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
+ if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
+ thisMeta = w->metaObject();
+ o = w->object();
+ } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
+ thisMeta = w->metaObject();
+ o = w->object();
+ } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
+ thisMeta = w->metaObject();
+ valueWrapper = w->d();
}
Heap::QObjectMethod::ThisObjectMode mode = Heap::QObjectMethod::Invalid;
if (o && o == d()->object()) {
mode = Heap::QObjectMethod::Explicit;
// Nothing to do; objects are the same. This should be common
- } else if (wrapper && wrapper == d()->valueTypeWrapper) {
+ } else if (valueWrapper && valueWrapper == d()->wrapper) {
mode = Heap::QObjectMethod::Explicit;
// Nothing to do; gadgets are the same. This should be somewhat common
} else {
@@ -2572,20 +2891,24 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
QQmlObjectOrGadget object = [&](){
if (mode == Heap::QObjectMethod::Included) {
- if (QObject *qObject = d()->qObj)
- return QQmlObjectOrGadget(qObject);
-
- wrapper = d()->valueTypeWrapper;
- Q_ASSERT(wrapper);
- return QQmlObjectOrGadget(wrapper->metaObject(), wrapper->gadgetPtr());
+ QV4::Scope scope(v4);
+ if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, d()->wrapper); qobject)
+ return QQmlObjectOrGadget(qobject->object());
+ if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, d()->wrapper); type)
+ return QQmlObjectOrGadget(type->object());
+ if (QV4::Scoped<QV4::QQmlValueTypeWrapper> value(scope, d()->wrapper); value) {
+ valueWrapper = value->d();
+ return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr());
+ }
+ Q_UNREACHABLE();
} else {
if (o)
return QQmlObjectOrGadget(o);
- Q_ASSERT(wrapper);
- if (!wrapper->enforcesLocation())
- QV4::ReferenceObject::readReference(wrapper);
- return QQmlObjectOrGadget(thisMeta, wrapper->gadgetPtr());
+ Q_ASSERT(valueWrapper);
+ if (!valueWrapper->enforcesLocation())
+ QV4::ReferenceObject::readReference(valueWrapper);
+ return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
}
}();
@@ -2609,9 +2932,9 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
// The method might change the value.
const auto doCall = [&](const auto &call) {
if (!method->isConstant()) {
- if (wrapper && wrapper->isReference()) {
+ if (valueWrapper && valueWrapper->isReference()) {
ScopedValue rv(scope, call());
- d()->valueTypeWrapper->writeBack();
+ valueWrapper->writeBack();
return rv->asReturnedValue();
}
}
@@ -2620,7 +2943,8 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
};
if (d()->methodCount != 1) {
- method = ResolveOverloaded(object, d()->methods, d()->methodCount, v4, callData);
+ Q_ASSERT(d()->methodCount > 0);
+ method = resolveOverloaded(object, d()->methods, d()->methodCount, v4, callData);
if (method == nullptr)
return Encode::undefined();
}
@@ -2629,7 +2953,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
return doCall([&]() {
ScopedValue rv(scope, Value::undefinedValue());
QQmlV4Function func(callData, rv, v4);
- QQmlV4Function *funcptr = &func;
+ QQmlV4FunctionPtr funcptr = &func;
void *args[] = { nullptr, &funcptr };
object.metacall(QMetaObject::InvokeMetaMethod, method->coreIndex(), args);
@@ -2638,116 +2962,127 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
});
}
- return doCall([&]() { return CallPrecise(object, *method, v4, callData); });
+ return doCall([&]() { return callPrecise(object, *method, v4, callData); });
}
-DEFINE_OBJECT_VTABLE(QObjectMethod);
-
-
-void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
+struct ToStringMetaMethod
{
- FunctionObject::init();
- this->metaObject = metaObject;
- constructors = nullptr;
- constructorCount = 0;
-}
+ constexpr int parameterCount() const { return 0; }
+ constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
+ constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
+};
-void Heap::QMetaObjectWrapper::destroy()
+void QObjectMethod::callInternalWithMetaTypes(
+ QObject *thisObject, void **argv, const QMetaType *types, int argc) const
{
- delete[] constructors;
-}
-
-void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
+ ExecutionEngine *v4 = engine();
- const int count = metaObject->constructorCount();
- if (constructorCount != count) {
- delete[] constructors;
- constructorCount = count;
- if (count == 0) {
- constructors = nullptr;
- return;
- }
- constructors = new QQmlPropertyData[count];
+ const QMetaObject *thisMeta = nullptr;
+ Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
- for (int i = 0; i < count; ++i) {
- QMetaMethod method = metaObject->constructor(i);
- QQmlPropertyData &d = constructors[i];
- d.load(method);
- d.setCoreIndex(i);
- }
+ if (thisObject) {
+ thisMeta = thisObject->metaObject();
+ } else {
+ Q_ASSERT(Value::fromHeapObject(d()->wrapper).as<QQmlValueTypeWrapper>());
+ valueWrapper = d()->wrapper.cast<Heap::QQmlValueTypeWrapper>();
+ thisMeta = valueWrapper->metaObject();
}
-}
+ QQmlObjectOrGadget object = [&](){
+ if (thisObject)
+ return QQmlObjectOrGadget(thisObject);
-ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
+ Scope scope(v4);
+ Scoped<QQmlValueTypeWrapper> wrapper(scope, d()->wrapper);
+ Q_ASSERT(wrapper);
- Scope scope(engine);
- Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QMetaObjectWrapper>(metaObject)->asReturnedValue());
- mo->init(engine);
- return mo->asReturnedValue();
-}
+ Heap::QQmlValueTypeWrapper *valueWrapper = wrapper->d();
+ if (!valueWrapper->enforcesLocation())
+ QV4::ReferenceObject::readReference(valueWrapper);
+ return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
+ }();
-void QMetaObjectWrapper::init(ExecutionEngine *) {
- const QMetaObject & mo = *d()->metaObject;
+ if (object.isNull())
+ return;
- for (int i = 0; i < mo.enumeratorCount(); i++) {
- QMetaEnum Enum = mo.enumerator(i);
- for (int k = 0; k < Enum.keyCount(); k++) {
- const char* key = Enum.key(k);
- const int value = Enum.value(k);
- defineReadonlyProperty(QLatin1String(key), Value::fromInt32(value));
- }
+ if (d()->index == DestroyMethod) {
+ // method_destroy will use at most one argument
+ QV4::convertAndCall(
+ v4, thisObject, argv, types, std::min(argc, 1),
+ [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
+ Q_UNUSED(thisObject);
+ return method_destroy(v4, object.qObject(), argv, argc);
+ });
+ return;
}
-}
-ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
-{
- const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
- return This->constructInternal(argv, argc);
-}
-
-ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const
-{
+ if (d()->index == ToStringMethod) {
+ const ToStringMetaMethod metaMethod;
+ QV4::coerceAndCall(
+ v4, &metaMethod, argv, types, argc,
+ [v4, thisMeta, object](void **argv, int) {
+ *static_cast<QString *>(argv[0])
+ = QObjectWrapper::objectToString(v4, thisMeta, object.qObject());
+ });
+ return;
+ }
- d()->ensureConstructorsCache();
+ d()->ensureMethodsCache(thisMeta);
- ExecutionEngine *v4 = engine();
- const QMetaObject* mo = d()->metaObject;
- if (d()->constructorCount == 0) {
- return v4->throwTypeError(QLatin1String(mo->className())
- + QLatin1String(" has no invokable constructor"));
+ const QQmlPropertyData *method = d()->methods;
+ if (d()->methodCount != 1) {
+ Q_ASSERT(d()->methodCount > 0);
+ method = resolveOverloaded(d()->methods, d()->methodCount, argv, argc, types);
}
- Scope scope(v4);
- Scoped<QObjectWrapper> object(scope);
- JSCallData cData(nullptr, argv, argc);
- CallData *callData = cData.callData(scope);
+ if (!method || method->isV4Function()) {
+ QV4::convertAndCall(
+ v4, thisObject, argv, types, argc,
+ [this](const Value *thisObject, const Value *argv, int argc) {
+ return callInternal(thisObject, argv, argc);
+ });
+ } else {
+ const QMetaMethod metaMethod = method->metaMethod();
+ QV4::coerceAndCall(
+ v4, &metaMethod, argv, types, argc,
+ [v4, object, valueWrapper, method](void **argv, int argc) {
+ Q_UNUSED(argc);
+
+ // If we call the method, we have to write back any value type references afterwards.
+ // The method might change the value.
+ object.metacall(QMetaObject::InvokeMetaMethod, method->coreIndex(), argv);
+ if (!method->isConstant()) {
+ if (valueWrapper && valueWrapper->isReference())
+ valueWrapper->writeBack();
+ }
- const QQmlObjectOrGadget objectOrGadget(mo);
+ // If the method returns a QObject* we need to track it on the JS heap
+ // (if it's destructible).
+ QObject *qobjectPtr = nullptr;
+ const QMetaType resultType = method->propType();
+ if (argv[0]) {
+ if (resultType.flags() & QMetaType::PointerToQObject) {
+ qobjectPtr = *static_cast<QObject **>(argv[0]);
+ } else if (resultType == QMetaType::fromType<QVariant>()) {
+ const QVariant *result = static_cast<const QVariant *>(argv[0]);
+ const QMetaType variantType = result->metaType();
+ if (variantType.flags() & QMetaType::PointerToQObject)
+ qobjectPtr = *static_cast<QObject *const *>(result->data());
+ }
+ }
- if (d()->constructorCount == 1) {
- object = CallPrecise(objectOrGadget, d()->constructors[0], v4, callData, QMetaObject::CreateInstance);
- } else if (const QQmlPropertyData *ctor = ResolveOverloaded(
- objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) {
- object = CallPrecise(objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance);
+ if (qobjectPtr) {
+ QQmlData *ddata = QQmlData::get(qobjectPtr, true);
+ if (!ddata->explicitIndestructibleSet) {
+ ddata->indestructible = false;
+ QObjectWrapper::ensureWrapper(v4, qobjectPtr);
+ }
+ }
+ });
}
- Scoped<QMetaObjectWrapper> metaObject(scope, this);
- object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
- return object.asReturnedValue();
-
-}
-
-bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
-{
- const QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
- Q_ASSERT(aMetaObject);
- const QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
- return bMetaObject && aMetaObject->metaObject() == bMetaObject->metaObject();
}
-DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
-
+DEFINE_OBJECT_VTABLE(QObjectMethod);
void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
{
@@ -2758,6 +3093,26 @@ void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
DEFINE_OBJECT_VTABLE(QmlSignalHandler);
+ReturnedValue QmlSignalHandler::call(const Value *thisObject, const Value *argv, int argc) const
+{
+ const QString handlerName = QQmlSignalNames::signalNameToHandlerName(
+ object()->metaObject()->method(signalIndex()).name());
+ qCWarning(lcSignalHandler).noquote()
+ << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
+ "not call it directly. Make it a proper function and call "
+ "that or emit the signal.")
+ .arg(handlerName, thisObject->toQStringNoThrow());
+
+ Scope scope(engine());
+ Scoped<QObjectMethod> method(
+ scope, QObjectMethod::create(
+ scope.engine->rootContext(),
+ static_cast<Heap::QObjectWrapper *>(nullptr),
+ signalIndex()));
+
+ return method->call(thisObject, argv, argc);
+}
+
void QmlSignalHandler::initProto(ExecutionEngine *engine)
{
if (engine->signalHandlerPrototype()->d_unchecked())
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 3f94cc7798..1af8fc887f 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -15,16 +15,17 @@
// We mean it.
//
+#include <private/qbipointer_p.h>
+#include <private/qintrusivelist_p.h>
+#include <private/qqmldata_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4value_p.h>
+
#include <QtCore/qglobal.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qpair.h>
#include <QtCore/qhash.h>
-#include <private/qqmldata_p.h>
-#include <private/qintrusivelist_p.h>
-
-#include <private/qv4value_p.h>
-#include <private/qv4functionobject_p.h>
-#include <private/qv4lookup_p.h>
QT_BEGIN_NAMESPACE
@@ -32,6 +33,7 @@ class QObject;
class QQmlData;
class QQmlPropertyCache;
class QQmlPropertyData;
+class QQmlObjectOrGadget;
namespace QV4 {
struct QObjectSlotDispatcher;
@@ -60,23 +62,21 @@ private:
};
#define QObjectMethodMembers(class, Member) \
- Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper)
+ Member(class, Pointer, Object *, wrapper) \
-DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
+DECLARE_EXPORTED_HEAP_OBJECT(QObjectMethod, FunctionObject) {
DECLARE_MARKOBJECTS(QObjectMethod)
- QV4QPointer<QObject> qObj;
QQmlPropertyData *methods;
alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)];
int methodCount;
int index;
- void init(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope, Object *wrapper, int index);
void destroy()
{
if (methods != reinterpret_cast<const QQmlPropertyData *>(&_singleMethod))
delete[] methods;
- qObj.destroy();
FunctionObject::destroy();
}
@@ -84,8 +84,7 @@ DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
QString name() const;
const QMetaObject *metaObject() const;
- QObject *object() const { return qObj.data(); }
- void setObject(QObject *o) { qObj = o; }
+ QObject *object() const;
bool isDetached() const;
bool isAttachedTo(QObject *o) const;
@@ -99,16 +98,6 @@ DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
QV4::Heap::QObjectMethod::ThisObjectMode checkThisObject(const QMetaObject *thisMeta) const;
};
-struct QMetaObjectWrapper : FunctionObject {
- const QMetaObject* metaObject;
- QQmlPropertyData *constructors;
- int constructorCount;
-
- void init(const QMetaObject* metaObject);
- void destroy();
- void ensureConstructorsCache();
-};
-
struct QmlSignalHandler : Object {
void init(QObject *object, int signalIndex);
void destroy() {
@@ -143,6 +132,13 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
static void initializeBindings(ExecutionEngine *engine);
+ const QMetaObject *metaObject() const
+ {
+ if (QObject *o = object())
+ return o->metaObject();
+ return nullptr;
+ }
+
QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty(
@@ -158,8 +154,11 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
QObject *object, String *name, Flags flags, const Value &value);
+ Q_NODISCARD_X("Use ensureWrapper if you don't need the return value")
static ReturnedValue wrap(ExecutionEngine *engine, QObject *object);
+ Q_NODISCARD_X("Throwing the const wrapper away can cause it to be garbage collected")
static ReturnedValue wrapConst(ExecutionEngine *engine, QObject *object);
+ static void ensureWrapper(ExecutionEngine *engine, QObject *object);
static void markWrapper(QObject *object, MarkStack *markStack);
using Object::get;
@@ -219,10 +218,6 @@ protected:
private:
Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object);
Q_NEVER_INLINE static ReturnedValue wrapConst_slowPath(ExecutionEngine *engine, QObject *object);
-
- static Heap::QObjectMethod *cloneMethod(
- ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
- Heap::Object *wrapper, QObject *object);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QObjectWrapper::Flags)
@@ -328,14 +323,8 @@ inline ReturnedValue QObjectWrapper::lookupMethodGetterImpl(
}
if (Heap::QObjectMethod *method = lookup->qobjectMethodLookup.method) {
- if (lookup->forCall && !method->isDetached()) {
- method = lookup->qobjectMethodLookup.method
- = cloneMethod(engine, method, nullptr, nullptr);
- } else if (!lookup->forCall && !method->isAttachedTo(qobj)) {
- method = lookup->qobjectMethodLookup.method
- = cloneMethod(engine, method, This, qobj);
- }
- return method ? method->asReturnedValue() : revertLookup();
+ if (method->isDetached())
+ return method->asReturnedValue();
}
if (!property) // was toString() or destroy()
@@ -346,7 +335,7 @@ inline ReturnedValue QObjectWrapper::lookupMethodGetterImpl(
if (!v->as<QObjectMethod>())
return revertLookup();
- lookup->qobjectMethodLookup.method = static_cast<Heap::QObjectMethod *>(v->heapObject());
+ lookup->qobjectMethodLookup.method.set(engine, static_cast<Heap::QObjectMethod *>(v->heapObject()));
return v->asReturnedValue();
}
@@ -359,13 +348,11 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
enum { DestroyMethod = -1, ToStringMethod = -2 };
- static ReturnedValue create(
- QV4::ExecutionContext *scope, QObject *object, int index);
+ static ReturnedValue create(QV4::ExecutionContext *scope, Heap::Object *wrapper, int index);
static ReturnedValue create(
QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
- static ReturnedValue create(
- QV4::ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
- Heap::Object *wrapper, QObject *object);
+ static ReturnedValue create(QV4::ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
+ Heap::Object *wrapper, Heap::Object *object);
int methodIndex() const { return d()->index; }
QObject *object() const { return d()->object(); }
@@ -373,30 +360,38 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine, QObject *o) const;
QV4::ReturnedValue method_destroy(
QV4::ExecutionEngine *ctx, QObject *o, const Value *args, int argc) const;
+ void method_destroy(
+ QV4::ExecutionEngine *engine, QObject *o,
+ void **argv, const QMetaType *types, int argc) const;
- static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(
+ const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static void virtualCallWithMetaTypes(
+ const FunctionObject *m, QObject *thisObject,
+ void **argv, const QMetaType *types, int argc);
- ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const;
+ ReturnedValue callInternal(
+ const Value *thisObject, const Value *argv, int argc) const;
+ void callInternalWithMetaTypes(
+ QObject *thisObject, void **argv, const QMetaType *types, int argc) const;
static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function);
-};
-
-struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject
-{
- V4_OBJECT2(QMetaObjectWrapper, QV4::FunctionObject)
- V4_NEEDS_DESTROY
+private:
+ friend struct QMetaObjectWrapper;
- static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
- const QMetaObject *metaObject() const { return d()->metaObject; }
+ static const QQmlPropertyData *resolveOverloaded(
+ const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
+ ExecutionEngine *engine, CallData *callArgs);
-protected:
- static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
- static bool virtualIsEqualTo(Managed *a, Managed *b);
+ static const QQmlPropertyData *resolveOverloaded(
+ const QQmlPropertyData *methods, int methodCount,
+ void **argv, int argc, const QMetaType *types);
-private:
- void init(ExecutionEngine *engine);
- ReturnedValue constructInternal(const Value *argv, int argc) const;
+ static ReturnedValue callPrecise(
+ const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
+ ExecutionEngine *engine, CallData *callArgs,
+ QMetaObject::Call callType = QMetaObject::InvokeMetaMethod);
};
struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object
@@ -408,6 +403,8 @@ struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object
int signalIndex() const { return d()->signalIndex; }
QObject *object() const { return d()->object(); }
+ ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const;
+
static void initProto(ExecutionEngine *v4);
};
diff --git a/src/qml/jsruntime/qv4referenceobject_p.h b/src/qml/jsruntime/qv4referenceobject_p.h
index 5378a0c362..094749ce6e 100644
--- a/src/qml/jsruntime/qv4referenceobject_p.h
+++ b/src/qml/jsruntime/qv4referenceobject_p.h
@@ -16,6 +16,7 @@
//
#include <private/qv4object_p.h>
+#include <private/qv4stackframe_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp
index be7ff77603..9c48199157 100644
--- a/src/qml/jsruntime/qv4regexp.cpp
+++ b/src/qml/jsruntime/qv4regexp.cpp
@@ -9,19 +9,23 @@
using namespace QV4;
+#if ENABLE(YARR_JIT)
+static constexpr quint8 RegexpJitThreshold = 5;
+#endif
+
static JSC::RegExpFlags jscFlags(uint flags)
{
JSC::RegExpFlags jscFlags = JSC::NoFlags;
if (flags & CompiledData::RegExp::RegExp_Global)
- jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagGlobal);
+ jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagGlobal);
if (flags & CompiledData::RegExp::RegExp_IgnoreCase)
- jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagIgnoreCase);
+ jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagIgnoreCase);
if (flags & CompiledData::RegExp::RegExp_Multiline)
- jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagMultiline);
+ jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagMultiline);
if (flags & CompiledData::RegExp::RegExp_Unicode)
- jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagUnicode);
+ jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagUnicode);
if (flags & CompiledData::RegExp::RegExp_Sticky)
- jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagSticky);
+ jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagSticky);
return jscFlags;
}
@@ -40,12 +44,58 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets)
if (!isValid())
return JSC::Yarr::offsetNoMatch;
+#if ENABLE(YARR_JIT)
+ auto *priv = d();
+
+ auto regenerateByteCode = [](Heap::RegExp *regexp) {
+ JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError;
+ JSC::Yarr::YarrPattern yarrPattern(WTF::String(*regexp->pattern), jscFlags(regexp->flags),
+ error);
+
+ // As we successfully parsed the pattern before, we should still be able to.
+ Q_ASSERT(error == JSC::Yarr::ErrorCode::NoError);
+
+ regexp->byteCode = JSC::Yarr::byteCompile(
+ yarrPattern,
+ regexp->internalClass->engine->bumperPointerAllocator).release();
+ };
+
+ auto removeJitCode = [](Heap::RegExp *regexp) {
+ delete regexp->jitCode;
+ regexp->jitCode = nullptr;
+ regexp->jitFailed = true;
+ };
+
+ auto removeByteCode = [](Heap::RegExp *regexp) {
+ delete regexp->byteCode;
+ regexp->byteCode = nullptr;
+ };
+
+ if (!priv->jitCode && !priv->jitFailed && priv->internalClass->engine->canJIT()
+ && (string.length() > 1024 || priv->matchCount++ == RegexpJitThreshold)) {
+ removeByteCode(priv);
+
+ JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError;
+ JSC::Yarr::YarrPattern yarrPattern(
+ WTF::String(*priv->pattern), jscFlags(priv->flags), error);
+ if (!yarrPattern.m_containsBackreferences) {
+ priv->jitCode = new JSC::Yarr::YarrCodeBlock;
+ JSC::VM *vm = static_cast<JSC::VM *>(priv->internalClass->engine);
+ JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, vm, *priv->jitCode);
+ }
+
+ if (!priv->hasValidJITCode()) {
+ removeJitCode(priv);
+ regenerateByteCode(priv);
+ }
+ }
+#endif
+
WTF::String s(string);
#if ENABLE(YARR_JIT)
- static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1;
- auto *priv = d();
if (priv->hasValidJITCode()) {
+ static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1;
uint ret = JSC::Yarr::offsetNoMatch;
#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
char buffer[8192];
@@ -58,19 +108,10 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets)
if (ret != offsetJITFail)
return ret;
+ removeJitCode(priv);
// JIT failed. We need byteCode to run the interpreter.
- if (!priv->byteCode) {
- JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError;
- JSC::Yarr::YarrPattern yarrPattern(WTF::String(*priv->pattern), jscFlags(priv->flags),
- error);
-
- // As we successfully parsed the pattern before, we should still be able to.
- Q_ASSERT(error == JSC::Yarr::ErrorCode::NoError);
-
- priv->byteCode = JSC::Yarr::byteCompile(
- yarrPattern,
- priv->internalClass->engine->bumperPointerAllocator).release();
- }
+ Q_ASSERT(!priv->byteCode);
+ regenerateByteCode(priv);
}
#endif // ENABLE(YARR_JIT)
@@ -175,25 +216,15 @@ void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, uint fl
this->flags = flags;
valid = false;
+ jitFailed = false;
+ matchCount = 0;
JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError;
JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), jscFlags(flags), error);
if (error != JSC::Yarr::ErrorCode::NoError)
return;
subPatternCount = yarrPattern.m_numSubpatterns;
-#if ENABLE(YARR_JIT)
- if (!yarrPattern.m_containsBackreferences && engine->canJIT()) {
- jitCode = new JSC::Yarr::YarrCodeBlock;
- JSC::VM *vm = static_cast<JSC::VM *>(engine);
- JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, vm, *jitCode);
- }
-#else
Q_UNUSED(engine);
-#endif
- if (hasValidJITCode()) {
- valid = true;
- return;
- }
byteCode = JSC::Yarr::byteCompile(yarrPattern, internalClass->engine->bumperPointerAllocator).release();
if (byteCode)
valid = true;
diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h
index 1b70354caa..8a178dd2f6 100644
--- a/src/qml/jsruntime/qv4regexp_p.h
+++ b/src/qml/jsruntime/qv4regexp_p.h
@@ -66,6 +66,8 @@ struct RegExp : Base {
int subPatternCount;
uint flags;
bool valid;
+ bool jitFailed;
+ quint8 matchCount;
QString flagsAsString() const;
int captureCount() const { return subPatternCount + 1; }
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index 10702c48bd..8d52300013 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -14,17 +14,13 @@
// We mean it.
//
-#include "qv4engine_p.h"
-#include "qv4context_p.h"
-#include "qv4functionobject_p.h"
-#include "qv4managed_p.h"
-#include "qv4regexp_p.h"
-
-#include <QtCore/QString>
-#include <QtCore/QHash>
-#include <QtCore/QScopedPointer>
-#include <cstdio>
-#include <cassert>
+#include <private/qv4context_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4managed_p.h>
+
+#include <QtCore/qhash.h>
+#include <QtCore/qstring.h>
QT_BEGIN_NAMESPACE
@@ -60,7 +56,7 @@ DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) {
}
-struct Q_QML_PRIVATE_EXPORT RegExpObject: Object {
+struct Q_QML_EXPORT RegExpObject: Object {
V4_OBJECT2(RegExpObject, Object)
Q_MANAGED_TYPE(RegExpObject)
V4_INTERNALCLASS(RegExpObject)
@@ -110,11 +106,19 @@ struct Q_QML_PRIVATE_EXPORT RegExpObject: Object {
return s->toQString();
}
- Heap::RegExp *value() const { return d()->value; }
- uint flags() const { return d()->value->flags; }
- bool global() const { return d()->value->global(); }
- bool sticky() const { return d()->value->sticky(); }
- bool unicode() const { return d()->value->unicode(); }
+ // We cannot name Heap::RegExp here since we don't want to include qv4regexp_p.h but we still
+ // want to keep the methods inline. We shift the requirement to name the type to the caller by
+ // making it a template.
+ template<typename RegExp = Heap::RegExp>
+ RegExp *value() const { return d()->value; }
+ template<typename RegExp = Heap::RegExp>
+ uint flags() const { return value<RegExp>()->flags; }
+ template<typename RegExp = Heap::RegExp>
+ bool global() const { return value<RegExp>()->global(); }
+ template<typename RegExp = Heap::RegExp>
+ bool sticky() const { return value<RegExp>()->sticky(); }
+ template<typename RegExp = Heap::RegExp>
+ bool unicode() const { return value<RegExp>()->unicode(); }
ReturnedValue builtinExec(ExecutionEngine *engine, const String *s);
};
diff --git a/src/qml/jsruntime/qv4resolvedtypereference.cpp b/src/qml/jsruntime/qv4resolvedtypereference.cpp
index 0bf04c941a..7dcf2cd0b8 100644
--- a/src/qml/jsruntime/qv4resolvedtypereference.cpp
+++ b/src/qml/jsruntime/qv4resolvedtypereference.cpp
@@ -55,8 +55,8 @@ QQmlPropertyCache::ConstPtr ResolvedTypeReference::createPropertyCache()
const QMetaObject *metaObject = m_type.metaObject();
if (!metaObject) // value type of non-Q_GADGET base with extension
metaObject = m_type.extensionMetaObject();
- Q_ASSERT(metaObject);
- m_typePropertyCache = QQmlMetaType::propertyCache(metaObject, m_version);
+ if (metaObject)
+ m_typePropertyCache = QQmlMetaType::propertyCache(metaObject, m_version);
return m_typePropertyCache;
} else {
Q_ASSERT(m_compilationUnit);
@@ -67,15 +67,29 @@ QQmlPropertyCache::ConstPtr ResolvedTypeReference::createPropertyCache()
bool ResolvedTypeReference::addToHash(
QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums)
{
- if (m_type.isValid() && !m_type.isInlineComponentType()) {
+ if (m_type.isInlineComponentType()) {
+
+ // A reference to an inline component in the same file will have
+ // - no compilation unit since we cannot resolve the compilation unit before it's built.
+ // - a property cache since we've assigned one in buildMetaObjectsIncrementally().
+ // - a QQmlType that says it's an inline component.
+ // We don't have to add such a thing to the hash since if it changes, the QML document
+ // itself changes, leading to a new timestamp, which is checked before the checksum.
+ if (!m_compilationUnit)
+ return !m_typePropertyCache.isNull();
+
+ } else if (m_type.isValid()) {
bool ok = false;
- hash->addData(createPropertyCache()->checksum(checksums, &ok));
+ if (QQmlPropertyCache::ConstPtr propertyCache = createPropertyCache())
+ hash->addData(propertyCache->checksum(checksums, &ok));
+ else
+ Q_ASSERT(m_type.module() == QLatin1String("QML")); // a builtin without metaobject
return ok;
}
if (!m_compilationUnit)
return false;
- hash->addData({m_compilationUnit->data->md5Checksum,
- sizeof(m_compilationUnit->data->md5Checksum)});
+ hash->addData({m_compilationUnit->unitData()->md5Checksum,
+ sizeof(m_compilationUnit->unitData()->md5Checksum)});
return true;
}
diff --git a/src/qml/jsruntime/qv4resolvedtypereference_p.h b/src/qml/jsruntime/qv4resolvedtypereference_p.h
index 9720c90a12..4aaafdd71c 100644
--- a/src/qml/jsruntime/qv4resolvedtypereference_p.h
+++ b/src/qml/jsruntime/qv4resolvedtypereference_p.h
@@ -19,7 +19,7 @@
#include <QtQml/private/qqmlrefcount_p.h>
#include <QtQml/private/qqmlpropertycache_p.h>
#include <QtQml/private/qqmltype_p.h>
-#include <QtQml/private/qv4executablecompilationunit_p.h>
+#include <QtQml/private/qv4compileddata_p.h>
QT_BEGIN_NAMESPACE
@@ -46,8 +46,12 @@ public:
QQmlType type() const { return m_type; }
void setType(QQmlType type) { m_type = std::move(type); }
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() { return m_compilationUnit; }
- void setCompilationUnit(QQmlRefPointer<QV4::ExecutableCompilationUnit> unit)
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit()
+ {
+ return m_compilationUnit;
+ }
+
+ void setCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit)
{
if (m_compilationUnit == unit.data())
return;
@@ -93,7 +97,7 @@ public:
private:
QQmlType m_type;
QQmlPropertyCache::ConstPtr m_typePropertyCache;
- QV4::ExecutableCompilationUnit *m_compilationUnit = nullptr;
+ QV4::CompiledData::CompilationUnit *m_compilationUnit = nullptr;
QTypeRevision m_version = QTypeRevision::zero();
// Types such as QQmlPropertyMap can add properties dynamically at run-time and
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index aa4d5c875a..c51c94ffe4 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1,42 +1,44 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qv4global_p.h"
#include "qv4runtime_p.h"
-#include "qv4engine_p.h"
-#include "qv4object_p.h"
-#include "qv4globalobject_p.h"
-#include "qv4argumentsobject_p.h"
-#include "qv4lookup_p.h"
-#include "qv4function_p.h"
-#include "qv4regexp_p.h"
-#include "qv4regexpobject_p.h"
-#include "private/qlocale_tools_p.h"
-#include "qv4scopedvalue_p.h"
-#include "qv4jscall_p.h"
-#include <private/qv4qmlcontext_p.h>
-#include <private/qqmltypewrapper_p.h>
+
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <private/qqmljsast_p.h>
+#include <private/qqmltypewrapper_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
-#include "qv4qobjectwrapper_p.h"
-#include "qv4symbol_p.h"
-#include "qv4generatorobject_p.h"
-#include <QtQml/private/qv4math_p.h>
-
-#include <QtCore/QDebug>
-#include <cassert>
-#include <cstdio>
-#include <stdlib.h>
+#include <private/qv4argumentsobject_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4function_p.h>
+#include <private/qv4generatorobject_p.h>
+#include <private/qv4global_p.h>
+#include <private/qv4globalobject_p.h>
+#include <private/qv4jscall_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4math_p.h>
+#include <private/qv4object_p.h>
+#include <private/qv4qmlcontext_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4regexp_p.h>
+#include <private/qv4regexpobject_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qv4stackframe_p.h>
+#include <private/qv4symbol_p.h>
#include <wtf/MathExtras.h>
+#include <QtCore/private/qlocale_tools_p.h>
+#include <QtCore/qdebug.h>
+
#ifdef QV4_COUNT_RUNTIME_FUNCTIONS
-# include <QtCore/QBuffer>
-# include <QtCore/QDebug>
+# include <QtCore/qbuffer.h>
#endif // QV4_COUNT_RUNTIME_FUNCTIONS
+#include <cassert>
+#include <cstdio>
+#include <stdlib.h>
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -329,7 +331,7 @@ ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *funct
}
}
-QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval)
+static QV4::ReturnedValue doInstanceof(ExecutionEngine *engine, const Value &lval, const Value &rval)
{
// 11.8.6, 5: rval must be an Object
const Object *rhs = rval.as<Object>();
@@ -345,26 +347,44 @@ QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Valu
Scope scope(engine);
ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance()));
if (hasInstance->isUndefined())
- return rhs->instanceOf(lval);
+ return Encode(rhs->instanceOf(lval));
+
FunctionObject *fHasInstance = hasInstance->as<FunctionObject>();
if (!fHasInstance)
return engine->throwTypeError();
- ScopedValue result(scope, fHasInstance->call(&rval, &lval, 1));
+ return Encode(fHasInstance->call(&rval, &lval, 1));
+}
+
+QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval)
+{
+ Scope scope(engine);
+ ScopedValue result(scope, doInstanceof(engine, lval, rval));
return scope.hasException() ? Encode::undefined() : Encode(result->toBoolean());
}
QV4::ReturnedValue Runtime::As::call(ExecutionEngine *engine, const Value &lval, const Value &rval)
{
Scope scope(engine);
- ScopedValue result(scope, Runtime::Instanceof::call(engine, lval, rval));
+ ScopedValue result(scope, doInstanceof(engine, lval, rval));
- if (scope.hasException())
+ if (scope.hasException()) {
+ // "foo instanceof valueType" must not throw an exception.
+ // So this can only be an object type.
engine->catchException();
- else if (result->toBoolean())
+ return Encode::null();
+ }
+
+ if (result->toBoolean())
return lval.asReturnedValue();
+ else if (result->isBoolean())
+ return Encode::null();
+
+ // Try to convert the value type
+ if (Scoped<QQmlTypeWrapper> typeWrapper(scope, rval); typeWrapper)
+ return coerce(engine, lval, typeWrapper->d()->type(), false);
- return Encode::null();
+ return Encode::undefined();
}
QV4::ReturnedValue Runtime::In::call(ExecutionEngine *engine, const Value &left, const Value &right)
@@ -564,7 +584,7 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value val
goto redo;
}
case Value::Integer_Type:
- return RuntimeHelpers::stringFromNumber(engine, value.int_32());
+ return engine->newString(QString::number(value.int_32()));
default: // double
return RuntimeHelpers::stringFromNumber(engine, value.doubleValue());
} // switch
@@ -842,6 +862,8 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c
engine->hasException = false;
ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
+ if (engine->hasException)
+ return Encode(true);
if (ret->isUndefined()) {
// propagate return()
return Encode::undefined();
@@ -856,14 +878,13 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c
ScopedValue t(scope, static_cast<const Object &>(iterator).get(engine->id_throw()));
if (engine->hasException)
- return Encode::undefined();
+ return Encode(true);
if (t->isUndefined()) {
// no throw method on the iterator
- ScopedValue done(scope, Encode(false));
- IteratorClose::call(engine, iterator, done);
- if (engine->hasException)
- return Encode::undefined();
- return engine->throwTypeError();
+ IteratorClose::call(engine, iterator);
+ if (!engine->hasException)
+ engine->throwTypeError();
+ return Encode(true);
}
f = t->as<FunctionObject>();
arg = exceptionValue;
@@ -874,14 +895,18 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c
f = next->as<FunctionObject>();
}
- if (!f)
- return engine->throwTypeError();
+ if (!f) {
+ engine->throwTypeError();
+ return Encode(true);
+ }
ScopedObject o(scope, f->call(&iterator, arg, 1));
if (scope.hasException())
return Encode(true);
- if (!o)
- return engine->throwTypeError();
+ if (!o) {
+ engine->throwTypeError();
+ return Encode(true);
+ }
ScopedValue d(scope, o->get(engine->id_done()));
if (scope.hasException())
@@ -889,18 +914,15 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c
bool done = d->toBoolean();
if (done) {
*object = o->get(engine->id_value());
- return returnCalled ? Encode::undefined() : Encode(true);
+ return (returnCalled && !engine->hasException) ? Encode::undefined() : Encode(true);
}
*object = o;
return Encode(false);
}
-ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator, const Value &done)
+ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator)
{
Q_ASSERT(iterator.isObject());
- Q_ASSERT(done.isBoolean());
- if (done.booleanValue())
- return Encode::undefined();
Scope scope(engine);
ScopedValue e(scope);
@@ -1481,10 +1503,17 @@ ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const V
// ok to have the value on the stack here
Value f = Value::fromReturnedValue(l->getter(l, engine, base));
- if (!f.isFunctionObject())
- return engine->throwTypeError();
+ if (Q_LIKELY(f.isFunctionObject()))
+ return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc));
+
+ if (QmlSignalHandler *handler = f.as<QmlSignalHandler>())
+ return checkedResult(engine, handler->call(&base, argv, argc));
- return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc));
+ const QString message = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit
+ ->runtimeStrings[l->nameIndex]->toQString())
+ .arg(base.toQStringNoThrow());
+ return engine->throwTypeError(message);
}
ReturnedValue Runtime::CallValue::call(ExecutionEngine *engine, const Value &func, Value *argv, int argc)
@@ -1608,7 +1637,7 @@ ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine
return checkedResult(engine, fo.call(&thisObject, argv, argc));
}
- memcpy(frame->jsFrame->args, argv, argc * sizeof(Value));
+ memmove(frame->jsFrame->args, argv, argc * sizeof(Value));
frame->init(fo.function(), frame->jsFrame->argValues<Value>(), argc,
frame->callerCanHandleTailCall());
frame->setupJSFrame(frame->framePointer(), fo, fo.scope(), thisObject,
diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h
index 40994633f0..a012728cd9 100644
--- a/src/qml/jsruntime/qv4runtime_p.h
+++ b/src/qml/jsruntime/qv4runtime_p.h
@@ -58,7 +58,7 @@ enum TypeHint {
STRING_HINT
};
-struct Q_QML_PRIVATE_EXPORT RuntimeHelpers {
+struct Q_QML_EXPORT RuntimeHelpers {
static ReturnedValue objectDefaultValue(const Object *object, int typeHint);
static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint);
static ReturnedValue ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint);
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 5e11d15495..fdee6ac580 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -23,7 +23,7 @@ namespace QV4 {
typedef uint Bool;
-struct Q_QML_PRIVATE_EXPORT Runtime {
+struct Q_QML_EXPORT Runtime {
typedef ReturnedValue (*UnaryOperation)(const Value &value);
typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right);
typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *, const Value &left, const Value &right);
@@ -43,418 +43,418 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
static constexpr bool lastArgumentIsOutputValue = out == LastArgumentIsOutputValue::Yes;
};
using PureMethod = Method<Throws::No, ChangesContext::No, Pure::Yes>;
- using IteratorMethod = Method<Throws::Yes, ChangesContext::No, Pure::No,
+ using IteratorMethod = Method<Throws::No, ChangesContext::No, Pure::No,
LastArgumentIsOutputValue::Yes>;
/* call */
- struct Q_QML_PRIVATE_EXPORT CallGlobalLookup : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallGlobalLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallName : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, int, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CallProperty : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallProperty : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CallPropertyLookup : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallPropertyLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, uint, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CallValue : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallValue : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CallWithReceiver : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallWithReceiver : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CallPossiblyDirectEval : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallPossiblyDirectEval : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CallWithSpread : Method<Throws::Yes>
+ struct Q_QML_EXPORT CallWithSpread : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT TailCall : Method<Throws::Yes>
+ struct Q_QML_EXPORT TailCall : Method<Throws::Yes>
{
static ReturnedValue call(JSTypesStackFrame *, ExecutionEngine *engine);
};
/* construct */
- struct Q_QML_PRIVATE_EXPORT Construct : Method<Throws::Yes>
+ struct Q_QML_EXPORT Construct : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT ConstructWithSpread : Method<Throws::Yes>
+ struct Q_QML_EXPORT ConstructWithSpread : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
};
/* load & store */
- struct Q_QML_PRIVATE_EXPORT StoreNameStrict : Method<Throws::Yes>
+ struct Q_QML_EXPORT StoreNameStrict : Method<Throws::Yes>
{
static void call(ExecutionEngine *, int, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT StoreNameSloppy : Method<Throws::Yes>
+ struct Q_QML_EXPORT StoreNameSloppy : Method<Throws::Yes>
{
static void call(ExecutionEngine *, int, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT StoreProperty : Method<Throws::Yes>
+ struct Q_QML_EXPORT StoreProperty : Method<Throws::Yes>
{
static void call(ExecutionEngine *, const Value &, int, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT StoreElement : Method<Throws::Yes>
+ struct Q_QML_EXPORT StoreElement : Method<Throws::Yes>
{
static void call(ExecutionEngine *, const Value &, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT LoadProperty : Method<Throws::Yes>
+ struct Q_QML_EXPORT LoadProperty : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, int);
};
- struct Q_QML_PRIVATE_EXPORT LoadName : Method<Throws::Yes>
+ struct Q_QML_EXPORT LoadName : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, int);
};
- struct Q_QML_PRIVATE_EXPORT LoadElement : Method<Throws::Yes>
+ struct Q_QML_EXPORT LoadElement : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT LoadSuperProperty : Method<Throws::Yes>
+ struct Q_QML_EXPORT LoadSuperProperty : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT StoreSuperProperty : Method<Throws::Yes>
+ struct Q_QML_EXPORT StoreSuperProperty : Method<Throws::Yes>
{
static void call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT LoadSuperConstructor : Method<Throws::Yes>
+ struct Q_QML_EXPORT LoadSuperConstructor : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT LoadGlobalLookup : Method<Throws::Yes>
+ struct Q_QML_EXPORT LoadGlobalLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Function *, int);
};
- struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes>
+ struct Q_QML_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, uint);
};
- struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes>
+ struct Q_QML_EXPORT GetLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int);
};
- struct Q_QML_PRIVATE_EXPORT SetLookupStrict : Method<Throws::Yes>
+ struct Q_QML_EXPORT SetLookupStrict : Method<Throws::Yes>
{
static void call(Function *, const Value &, int, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT SetLookupSloppy : Method<Throws::Yes>
+ struct Q_QML_EXPORT SetLookupSloppy : Method<Throws::Yes>
{
static void call(Function *, const Value &, int, const Value &);
};
/* typeof */
- struct Q_QML_PRIVATE_EXPORT TypeofValue : PureMethod
+ struct Q_QML_EXPORT TypeofValue : PureMethod
{
static ReturnedValue call(ExecutionEngine *, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT TypeofName : Method<Throws::No>
+ struct Q_QML_EXPORT TypeofName : Method<Throws::No>
{
static ReturnedValue call(ExecutionEngine *, int);
};
/* delete */
- struct Q_QML_PRIVATE_EXPORT DeleteProperty_NoThrow : Method<Throws::No>
+ struct Q_QML_EXPORT DeleteProperty_NoThrow : Method<Throws::No>
{
static Bool call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT DeleteProperty : Method<Throws::Yes>
+ struct Q_QML_EXPORT DeleteProperty : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Function *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT DeleteName_NoThrow : Method<Throws::No>
+ struct Q_QML_EXPORT DeleteName_NoThrow : Method<Throws::No>
{
static Bool call(ExecutionEngine *, int);
};
- struct Q_QML_PRIVATE_EXPORT DeleteName : Method<Throws::Yes>
+ struct Q_QML_EXPORT DeleteName : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Function *, int);
};
/* exceptions & scopes */
- struct Q_QML_PRIVATE_EXPORT ThrowException : Method<Throws::Yes>
+ struct Q_QML_EXPORT ThrowException : Method<Throws::Yes>
{
static void call(ExecutionEngine *, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT PushCallContext : Method<Throws::No, ChangesContext::Yes>
+ struct Q_QML_EXPORT PushCallContext : Method<Throws::No, ChangesContext::Yes>
{
static void call(JSTypesStackFrame *);
};
- struct Q_QML_PRIVATE_EXPORT PushWithContext : Method<Throws::Yes, ChangesContext::Yes>
+ struct Q_QML_EXPORT PushWithContext : Method<Throws::Yes, ChangesContext::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT PushCatchContext : Method<Throws::No, ChangesContext::Yes>
+ struct Q_QML_EXPORT PushCatchContext : Method<Throws::No, ChangesContext::Yes>
{
static void call(ExecutionEngine *, int, int);
};
- struct Q_QML_PRIVATE_EXPORT PushBlockContext : Method<Throws::No, ChangesContext::Yes>
+ struct Q_QML_EXPORT PushBlockContext : Method<Throws::No, ChangesContext::Yes>
{
static void call(ExecutionEngine *, int);
};
- struct Q_QML_PRIVATE_EXPORT CloneBlockContext : Method<Throws::No, ChangesContext::Yes>
+ struct Q_QML_EXPORT CloneBlockContext : Method<Throws::No, ChangesContext::Yes>
{
static void call(ExecutionEngine *);
};
- struct Q_QML_PRIVATE_EXPORT PushScriptContext : Method<Throws::No, ChangesContext::Yes>
+ struct Q_QML_EXPORT PushScriptContext : Method<Throws::No, ChangesContext::Yes>
{
static void call(ExecutionEngine *, int);
};
- struct Q_QML_PRIVATE_EXPORT PopScriptContext : Method<Throws::No, ChangesContext::Yes>
+ struct Q_QML_EXPORT PopScriptContext : Method<Throws::No, ChangesContext::Yes>
{
static void call(ExecutionEngine *);
};
- struct Q_QML_PRIVATE_EXPORT ThrowReferenceError : Method<Throws::Yes>
+ struct Q_QML_EXPORT ThrowReferenceError : Method<Throws::Yes>
{
static void call(ExecutionEngine *, int);
};
- struct Q_QML_PRIVATE_EXPORT ThrowOnNullOrUndefined : Method<Throws::Yes>
+ struct Q_QML_EXPORT ThrowOnNullOrUndefined : Method<Throws::Yes>
{
static void call(ExecutionEngine *, const Value &);
};
/* closures */
- struct Q_QML_PRIVATE_EXPORT Closure : Method<Throws::No>
+ struct Q_QML_EXPORT Closure : Method<Throws::No>
{
static ReturnedValue call(ExecutionEngine *, int);
};
/* Function header */
- struct Q_QML_PRIVATE_EXPORT ConvertThisToObject : Method<Throws::Yes>
+ struct Q_QML_EXPORT ConvertThisToObject : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT DeclareVar : Method<Throws::Yes>
+ struct Q_QML_EXPORT DeclareVar : Method<Throws::Yes>
{
static void call(ExecutionEngine *, Bool, int);
};
- struct Q_QML_PRIVATE_EXPORT CreateMappedArgumentsObject : Method<Throws::No>
+ struct Q_QML_EXPORT CreateMappedArgumentsObject : Method<Throws::No>
{
static ReturnedValue call(ExecutionEngine *);
};
- struct Q_QML_PRIVATE_EXPORT CreateUnmappedArgumentsObject : Method<Throws::No>
+ struct Q_QML_EXPORT CreateUnmappedArgumentsObject : Method<Throws::No>
{
static ReturnedValue call(ExecutionEngine *);
};
- struct Q_QML_PRIVATE_EXPORT CreateRestParameter : PureMethod
+ struct Q_QML_EXPORT CreateRestParameter : PureMethod
{
static ReturnedValue call(ExecutionEngine *, int);
};
/* literals */
- struct Q_QML_PRIVATE_EXPORT ArrayLiteral : Method<Throws::Yes>
+ struct Q_QML_EXPORT ArrayLiteral : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Value[], uint);
};
- struct Q_QML_PRIVATE_EXPORT ObjectLiteral : Method<Throws::Yes>
+ struct Q_QML_EXPORT ObjectLiteral : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, int, Value[], int);
};
- struct Q_QML_PRIVATE_EXPORT CreateClass : Method<Throws::Yes>
+ struct Q_QML_EXPORT CreateClass : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, int, const Value &, Value[]);
};
/* for-in, for-of and array destructuring */
- struct Q_QML_PRIVATE_EXPORT GetIterator : Method<Throws::Yes>
+ struct Q_QML_EXPORT GetIterator : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, int);
};
- struct Q_QML_PRIVATE_EXPORT IteratorNext : IteratorMethod
+ struct Q_QML_EXPORT IteratorNext : IteratorMethod
{
static ReturnedValue call(ExecutionEngine *, const Value &, Value *);
};
- struct Q_QML_PRIVATE_EXPORT IteratorNextForYieldStar : IteratorMethod
+ struct Q_QML_EXPORT IteratorNextForYieldStar : IteratorMethod
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value *);
};
- struct Q_QML_PRIVATE_EXPORT IteratorClose : Method<Throws::Yes>
+ struct Q_QML_EXPORT IteratorClose : Method<Throws::No>
{
- static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
+ static ReturnedValue call(ExecutionEngine *, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT DestructureRestElement : Method<Throws::Yes>
+ struct Q_QML_EXPORT DestructureRestElement : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &);
};
/* conversions */
- struct Q_QML_PRIVATE_EXPORT ToObject : Method<Throws::Yes>
+ struct Q_QML_EXPORT ToObject : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT ToBoolean : PureMethod
+ struct Q_QML_EXPORT ToBoolean : PureMethod
{
static Bool call(const Value &);
};
- struct Q_QML_PRIVATE_EXPORT ToNumber : Method<Throws::Yes>
+ struct Q_QML_EXPORT ToNumber : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &);
};
/* unary operators */
- struct Q_QML_PRIVATE_EXPORT UMinus : Method<Throws::Yes>
+ struct Q_QML_EXPORT UMinus : Method<Throws::Yes>
{
static ReturnedValue call(const Value &);
};
/* binary operators */
- struct Q_QML_PRIVATE_EXPORT Instanceof : Method<Throws::Yes>
+ struct Q_QML_EXPORT Instanceof : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT As : Method<Throws::No>
+ struct Q_QML_EXPORT As : Method<Throws::No>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT In : Method<Throws::Yes>
+ struct Q_QML_EXPORT In : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Add : Method<Throws::Yes>
+ struct Q_QML_EXPORT Add : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Sub : Method<Throws::Yes>
+ struct Q_QML_EXPORT Sub : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Mul : Method<Throws::Yes>
+ struct Q_QML_EXPORT Mul : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Div : Method<Throws::Yes>
+ struct Q_QML_EXPORT Div : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Mod : Method<Throws::Yes>
+ struct Q_QML_EXPORT Mod : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Exp : Method<Throws::Yes>
+ struct Q_QML_EXPORT Exp : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT BitAnd : Method<Throws::Yes>
+ struct Q_QML_EXPORT BitAnd : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT BitOr : Method<Throws::Yes>
+ struct Q_QML_EXPORT BitOr : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT BitXor : Method<Throws::Yes>
+ struct Q_QML_EXPORT BitXor : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Shl : Method<Throws::Yes>
+ struct Q_QML_EXPORT Shl : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Shr : Method<Throws::Yes>
+ struct Q_QML_EXPORT Shr : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT UShr : Method<Throws::Yes>
+ struct Q_QML_EXPORT UShr : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT GreaterThan : Method<Throws::Yes>
+ struct Q_QML_EXPORT GreaterThan : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT LessThan : Method<Throws::Yes>
+ struct Q_QML_EXPORT LessThan : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT GreaterEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT GreaterEqual : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT LessEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT LessEqual : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT Equal : Method<Throws::Yes>
+ struct Q_QML_EXPORT Equal : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT NotEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT NotEqual : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT StrictEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT StrictEqual : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT StrictNotEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT StrictNotEqual : Method<Throws::Yes>
{
static ReturnedValue call(const Value &, const Value &);
};
/* comparisons */
- struct Q_QML_PRIVATE_EXPORT CompareGreaterThan : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareGreaterThan : Method<Throws::Yes>
{
static Bool call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareLessThan : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareLessThan : Method<Throws::Yes>
{
static Bool call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareGreaterEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareGreaterEqual : Method<Throws::Yes>
{
static Bool call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareLessEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareLessEqual : Method<Throws::Yes>
{
static Bool call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareEqual : Method<Throws::Yes>
{
static Bool call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareNotEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareNotEqual : Method<Throws::Yes>
{
static Bool call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareStrictEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareStrictEqual : Method<Throws::Yes>
{
static Bool call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareStrictNotEqual : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareStrictNotEqual : Method<Throws::Yes>
{
static Bool call(const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareInstanceof : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareInstanceof : Method<Throws::Yes>
{
static Bool call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT CompareIn : Method<Throws::Yes>
+ struct Q_QML_EXPORT CompareIn : Method<Throws::Yes>
{
static Bool call(ExecutionEngine *, const Value &, const Value &);
};
- struct Q_QML_PRIVATE_EXPORT RegexpLiteral : PureMethod
+ struct Q_QML_EXPORT RegexpLiteral : PureMethod
{
static ReturnedValue call(ExecutionEngine *, int);
};
- struct Q_QML_PRIVATE_EXPORT GetTemplateObject : PureMethod
+ struct Q_QML_EXPORT GetTemplateObject : PureMethod
{
static ReturnedValue call(Function *, int);
};
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 395409ad4b..894426ce3b 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -26,14 +26,15 @@ using namespace QQmlJS;
Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit)
: line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false)
- , compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true)
+ , compilationUnit(compilationUnit), parseAsBinding(true)
{
if (qml)
qmlContext.set(v4, *qml);
parsed = true;
- vmFunction = compilationUnit ? compilationUnit->linkToEngine(v4) : nullptr;
+ vmFunction.set(v4,
+ compilationUnit ? compilationUnit->rootFunction() : nullptr);
}
Script::~Script()
@@ -45,14 +46,12 @@ void Script::parse()
if (parsed)
return;
- using namespace QV4::Compiler;
-
parsed = true;
ExecutionEngine *v4 = context->engine();
Scope valueScope(v4);
- Module module(v4->debugger() != nullptr);
+ QV4::Compiler::Module module(v4->debugger() != nullptr);
if (sourceCode.startsWith(QLatin1String("function("))) {
static const int snippetLength = 70;
@@ -96,8 +95,8 @@ void Script::parse()
if (v4->hasException)
return;
- compilationUnit = QV4::ExecutableCompilationUnit::create(cg.generateCompilationUnit());
- vmFunction = compilationUnit->linkToEngine(v4);
+ compilationUnit = v4->insertCompilationUnit(cg.generateCompilationUnit());
+ vmFunction.set(v4, compilationUnit->rootFunction());
}
if (!vmFunction) {
@@ -135,7 +134,7 @@ Function *Script::function()
return vmFunction;
}
-QV4::CompiledData::CompilationUnit Script::precompile(
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(
QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine,
Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl,
const QString &source, QList<QQmlError> *reportedErrors,
@@ -199,8 +198,9 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo
&cacheError)
: nullptr) {
QQmlRefPointer<QV4::ExecutableCompilationUnit> jsUnit
- = QV4::ExecutableCompilationUnit::create(
- QV4::CompiledData::CompilationUnit(cachedUnit->qmlData, cachedUnit->aotCompiledFunctions));
+ = engine->insertCompilationUnit(
+ QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(
+ cachedUnit->qmlData, cachedUnit->aotCompiledFunctions));
return new QV4::Script(engine, qmlContext, jsUnit);
}
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index 26996ca2bc..7b3dcae486 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -36,11 +36,11 @@ struct Q_QML_EXPORT Script {
Script(ExecutionContext *scope, QV4::Compiler::ContextType mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
, context(scope), strictMode(false), inheritContext(false), parsed(false), contextType(mode)
- , vmFunction(nullptr), parseAsBinding(false) {}
+ , parseAsBinding(false) {}
Script(ExecutionEngine *engine, QmlContext *qml, bool parseAsBinding, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
, context(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false)
- , vmFunction(nullptr), parseAsBinding(parseAsBinding) {
+ , parseAsBinding(parseAsBinding) {
if (qml)
qmlContext.set(engine, *qml);
}
@@ -57,7 +57,7 @@ struct Q_QML_EXPORT Script {
QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval;
QV4::PersistentValue qmlContext;
QQmlRefPointer<ExecutableCompilationUnit> compilationUnit;
- Function *vmFunction;
+ QV4::WriteBarrier::Pointer<Function> vmFunction;
bool parseAsBinding;
void parse();
@@ -65,7 +65,7 @@ struct Q_QML_EXPORT Script {
Function *function();
- static QV4::CompiledData::CompilationUnit precompile(
+ static QQmlRefPointer<QV4::CompiledData::CompilationUnit> precompile(
QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine,
Compiler::JSUnitGenerator *unitGenerator, const QString &fileName,
const QString &finalUrl, const QString &source,
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 132084705c..cc899428c2 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -27,9 +27,9 @@ static ReturnedValue doGetIndexed(const Sequence *s, qsizetype index) {
Heap::ReferenceObject::Flags flags =
Heap::ReferenceObject::EnforcesLocation;
- if (s->d()->typePrivate()->extraData.ld->canSetValueAtIndex())
+ if (s->d()->metaSequence().canSetValueAtIndex())
flags |= Heap::ReferenceObject::CanWriteBack;
- if (Sequence::valueMetaType(s->d()) == QMetaType::fromType<QVariant>())
+ if (s->d()->valueMetaType() == QMetaType::fromType<QVariant>())
flags |= Heap::ReferenceObject::IsVariant;
QV4::ScopedValue v(scope, scope.engine->fromVariant(
@@ -42,18 +42,12 @@ static ReturnedValue doGetIndexed(const Sequence *s, qsizetype index) {
return v->asReturnedValue();
}
-static const QMetaSequence *metaSequence(const Heap::Sequence *p)
-{
- return p->typePrivate()->extraData.ld;
-}
-
template<typename Compare>
void sortSequence(Sequence *sequence, const Compare &compare)
{
- auto *p = sequence->d();
- const auto *m = metaSequence(p);
+ /* non-const */ Heap::Sequence *p = sequence->d();
- QSequentialIterable iterable(*m, p->typePrivate()->listId, p->storagePointer());
+ QSequentialIterable iterable(p->metaSequence(), p->listType(), p->storagePointer());
if (iterable.canRandomAccessIterate()) {
std::sort(QSequentialIterable::RandomAccessIterator(iterable.mutableBegin()),
QSequentialIterable::RandomAccessIterator(iterable.mutableEnd()),
@@ -148,37 +142,35 @@ struct SequenceDefaultCompareFunctor
}
};
-void Heap::Sequence::init(const QQmlType &qmlType, const void *container)
+void Heap::Sequence::initTypes(QMetaType listType, QMetaSequence metaSequence)
{
- ReferenceObject::init(nullptr, -1, NoFlag);
-
- Q_ASSERT(qmlType.isSequentialContainer());
- m_typePrivate = qmlType.priv();
- QQmlType::refHandle(m_typePrivate);
-
- m_container = m_typePrivate->listId.create(container);
-
+ m_listType = listType.iface();
+ Q_ASSERT(m_listType);
+ m_metaSequence = metaSequence.iface();
+ Q_ASSERT(m_metaSequence);
QV4::Scope scope(internalClass->engine);
QV4::Scoped<QV4::Sequence> o(scope, this);
o->setArrayType(Heap::ArrayData::Custom);
}
+void Heap::Sequence::init(QMetaType listType, QMetaSequence metaSequence, const void *container)
+{
+ ReferenceObject::init(nullptr, -1, NoFlag);
+ initTypes(listType, metaSequence);
+ m_container = listType.create(container);
+}
+
void Heap::Sequence::init(
- const QQmlType &qmlType, const void *container,
- Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags)
+ QMetaType listType, QMetaSequence metaSequence, const void *container,
+ Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags)
{
ReferenceObject::init(object, propertyIndex, flags);
+ initTypes(listType, metaSequence);
- Q_ASSERT(qmlType.isSequentialContainer());
- m_typePrivate = qmlType.priv();
- QQmlType::refHandle(m_typePrivate);
- QV4::Scope scope(internalClass->engine);
- QV4::Scoped<QV4::Sequence> o(scope, this);
- o->setArrayType(Heap::ArrayData::Custom);
- if (CppStackFrame *frame = scope.engine->currentStackFrame)
+ if (CppStackFrame *frame = internalClass->engine->currentStackFrame)
setLocation(frame->v4Function, frame->statementNumber());
if (container)
- m_container = QMetaType(m_typePrivate->listId).create(container);
+ m_container = listType.create(container);
else if (flags & EnforcesLocation)
QV4::ReferenceObject::readReference(this);
}
@@ -186,28 +178,27 @@ void Heap::Sequence::init(
Heap::Sequence *Heap::Sequence::detached() const
{
return internalClass->engine->memoryManager->allocate<QV4::Sequence>(
- QQmlType(m_typePrivate), m_container);
+ QMetaType(m_listType), QMetaSequence(m_metaSequence), m_container);
}
void Heap::Sequence::destroy()
{
if (m_container)
- m_typePrivate->listId.destroy(m_container);
- QQmlType::derefHandle(m_typePrivate);
+ listType().destroy(m_container);
ReferenceObject::destroy();
}
void *Heap::Sequence::storagePointer()
{
if (!m_container)
- m_container = m_typePrivate->listId.create();
+ m_container = listType().create();
return m_container;
}
bool Heap::Sequence::setVariant(const QVariant &variant)
{
const QMetaType variantReferenceType = variant.metaType();
- if (variantReferenceType != m_typePrivate->listId) {
+ if (variantReferenceType != listType()) {
// This is a stale reference. That is, the property has been
// overwritten with a different type in the meantime.
// We need to modify this reference to the updated type, if
@@ -215,11 +206,10 @@ bool Heap::Sequence::setVariant(const QVariant &variant)
const QQmlType newType = QQmlMetaType::qmlListType(variantReferenceType);
if (newType.isSequentialContainer()) {
if (m_container)
- m_typePrivate->listId.destroy(m_container);
- QQmlType::derefHandle(m_typePrivate);
- m_typePrivate = newType.priv();
- QQmlType::refHandle(m_typePrivate);
- m_container = m_typePrivate->listId.create(variant.constData());
+ listType().destroy(m_container);
+ m_listType = newType.qListTypeId().iface();
+ m_metaSequence = newType.listMetaSequence().iface();
+ m_container = listType().create(variant.constData());
return true;
} else {
return false;
@@ -235,32 +225,27 @@ bool Heap::Sequence::setVariant(const QVariant &variant)
}
QVariant Heap::Sequence::toVariant() const
{
- return QVariant(m_typePrivate->listId, m_container);
-}
-
-const QMetaType Sequence::valueMetaType(const Heap::Sequence *p)
-{
- return p->typePrivate()->typeId;
+ return QVariant(listType(), m_container);
}
qsizetype Sequence::size() const
{
const auto *p = d();
Q_ASSERT(p->storagePointer()); // Must readReference() before
- return metaSequence(p)->size(p->storagePointer());
+ return p->metaSequence().size(p->storagePointer());
}
QVariant Sequence::at(qsizetype index) const
{
const auto *p = d();
Q_ASSERT(p->storagePointer()); // Must readReference() before
- const QMetaType v = valueMetaType(p);
+ const QMetaType v = p->valueMetaType();
QVariant result;
if (v == QMetaType::fromType<QVariant>()) {
- metaSequence(p)->valueAtIndex(p->storagePointer(), index, &result);
+ p->metaSequence().valueAtIndex(p->storagePointer(), index, &result);
} else {
result = QVariant(v);
- metaSequence(p)->valueAtIndex(p->storagePointer(), index, result.data());
+ p->metaSequence().valueAtIndex(p->storagePointer(), index, result.data());
}
return result;
}
@@ -284,45 +269,45 @@ void convertAndDo(const QVariant &item, const QMetaType v, Action action)
void Sequence::append(const QVariant &item)
{
Heap::Sequence *p = d();
- convertAndDo(item, valueMetaType(p), [p](const void *data) {
- metaSequence(p)->addValueAtEnd(p->storagePointer(), data);
+ convertAndDo(item, p->valueMetaType(), [p](const void *data) {
+ p->metaSequence().addValueAtEnd(p->storagePointer(), data);
});
}
void Sequence::append(qsizetype num, const QVariant &item)
{
Heap::Sequence *p = d();
- convertAndDo(item, valueMetaType(p), [p, num](const void *data) {
- const QMetaSequence *m = metaSequence(p);
+ convertAndDo(item, p->valueMetaType(), [p, num](const void *data) {
+ const QMetaSequence m = p->metaSequence();
void *container = p->storagePointer();
for (qsizetype i = 0; i < num; ++i)
- m->addValueAtEnd(container, data);
+ m.addValueAtEnd(container, data);
});
}
void Sequence::replace(qsizetype index, const QVariant &item)
{
Heap::Sequence *p = d();
- convertAndDo(item, valueMetaType(p), [p, index](const void *data) {
- metaSequence(p)->setValueAtIndex(p->storagePointer(), index, data);
+ convertAndDo(item, p->valueMetaType(), [p, index](const void *data) {
+ p->metaSequence().setValueAtIndex(p->storagePointer(), index, data);
});
}
void Sequence::removeLast(qsizetype num)
{
auto *p = d();
- const auto *m = metaSequence(p);
-
- if (m->canEraseRangeAtIterator() && m->hasRandomAccessIterator() && num > 1) {
- void *i = m->end(p->storagePointer());
- m->advanceIterator(i, -num);
- void *j = m->end(p->storagePointer());
- m->eraseRangeAtIterator(p->storagePointer(), i, j);
- m->destroyIterator(i);
- m->destroyIterator(j);
+ const QMetaSequence m = p->metaSequence();
+
+ if (m.canEraseRangeAtIterator() && m.hasRandomAccessIterator() && num > 1) {
+ void *i = m.end(p->storagePointer());
+ m.advanceIterator(i, -num);
+ void *j = m.end(p->storagePointer());
+ m.eraseRangeAtIterator(p->storagePointer(), i, j);
+ m.destroyIterator(i);
+ m.destroyIterator(j);
} else {
for (int i = 0; i < num; ++i)
- m->removeValueAtEnd(p->storagePointer());
+ m.removeValueAtEnd(p->storagePointer());
}
}
@@ -355,7 +340,7 @@ bool Sequence::containerPutIndexed(qsizetype index, const Value &value)
return false;
const qsizetype count = size();
- const QMetaType valueType = valueMetaType(d());
+ const QMetaType valueType = d()->valueMetaType();
const QVariant element = ExecutionEngine::toVariant(value, valueType, false);
if (index < 0)
@@ -467,6 +452,14 @@ ReturnedValue Sequence::virtualGet(const Managed *that, PropertyKey id, const Va
return Object::virtualGet(that, id, receiver, hasProperty);
}
+qint64 Sequence::virtualGetLength(const Managed *m)
+{
+ const Sequence *s = static_cast<const Sequence *>(m);
+ if (s->d()->isReference() && !s->loadReference())
+ return 0;
+ return s->size();
+}
+
bool Sequence::virtualPut(Managed *that, PropertyKey id, const Value &value, Value *receiver)
{
if (id.isArrayIndex()) {
@@ -510,26 +503,27 @@ int Sequence::virtualMetacall(Object *object, QMetaObject::Call call, int index,
switch (call) {
case QMetaObject::ReadProperty: {
- const QMetaType valueType = valueMetaType(sequence->d());
- if (!sequence->loadReference())
+ const QMetaType valueType = sequence->d()->valueMetaType();
+ if (sequence->d()->isReference() && !sequence->loadReference())
return 0;
- const QMetaSequence *metaSequence = sequence->d()->typePrivate()->extraData.ld;
- if (metaSequence->valueMetaType() != valueType)
+ const QMetaSequence metaSequence = sequence->d()->metaSequence();
+ if (metaSequence.valueMetaType() != valueType)
return 0; // value metatype is not what the caller expects anymore.
const void *storagePointer = sequence->d()->storagePointer();
- if (index < 0 || index >= metaSequence->size(storagePointer))
+ if (index < 0 || index >= metaSequence.size(storagePointer))
return 0;
- metaSequence->valueAtIndex(storagePointer, index, a[0]);
+ metaSequence.valueAtIndex(storagePointer, index, a[0]);
break;
}
case QMetaObject::WriteProperty: {
void *storagePointer = sequence->d()->storagePointer();
- const QMetaSequence *metaSequence = sequence->d()->typePrivate()->extraData.ld;
- if (index < 0 || index >= metaSequence->size(storagePointer))
+ const QMetaSequence metaSequence = sequence->d()->metaSequence();
+ if (index < 0 || index >= metaSequence.size(storagePointer))
return 0;
- metaSequence->setValueAtIndex(storagePointer, index, a[0]);
- sequence->storeReference();
+ metaSequence.setValueAtIndex(storagePointer, index, a[0]);
+ if (sequence->d()->isReference())
+ sequence->storeReference();
break;
}
default:
@@ -584,7 +578,7 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value
if (newCount == count) {
RETURN_UNDEFINED();
} else if (newCount > count) {
- const QMetaType valueMetaType = metaSequence(This->d())->valueMetaType();
+ const QMetaType valueMetaType = This->d()->valueMetaType();
/* according to ECMA262r3 we need to insert */
/* undefined values increasing length to newLength. */
/* We cannot, so we insert default-values instead. */
@@ -634,40 +628,43 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu
}
ReturnedValue SequencePrototype::newSequence(
- QV4::ExecutionEngine *engine, QMetaType sequenceType, const void *data,
- Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags)
+ QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data,
+ Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags)
{
// This function is called when the property is a QObject Q_PROPERTY of
// the given sequence type. Internally we store a sequence
// (as well as object ptr + property index for updated-read and write-back)
// and so access/mutate avoids variant conversion.
- const QQmlType qmlType = QQmlMetaType::qmlListType(sequenceType);
- if (qmlType.isSequentialContainer()) {
- return engine->memoryManager->allocate<Sequence>(
- qmlType, data, object, propertyIndex, flags)->asReturnedValue();
- }
-
- return Encode::undefined();
+ return engine->memoryManager->allocate<Sequence>(
+ type, metaSequence, data, object, propertyIndex, flags)->asReturnedValue();
}
ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant &v)
{
- return fromData(engine, v.metaType(), v.constData());
+ const QMetaType type = v.metaType();
+ const QQmlType qmlType = QQmlMetaType::qmlListType(type);
+ if (qmlType.isSequentialContainer())
+ return fromData(engine, type, qmlType.listMetaSequence(), v.constData());
+
+ QSequentialIterable iterable;
+ if (QMetaType::convert(
+ type, v.constData(), QMetaType::fromType<QSequentialIterable>(), &iterable)) {
+ return fromData(engine, type, iterable.metaContainer(), v.constData());
+ }
+
+ return Encode::undefined();
}
-ReturnedValue SequencePrototype::fromData(ExecutionEngine *engine, QMetaType type, const void *data)
+ReturnedValue SequencePrototype::fromData(
+ ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data)
{
// This function is called when assigning a sequence value to a normal JS var
// in a JS block. Internally, we store a sequence of the specified type.
// Access and mutation is extremely fast since it will not need to modify any
// QObject property.
- const QQmlType qmlType = QQmlMetaType::qmlListType(type);
- if (qmlType.isSequentialContainer())
- return engine->memoryManager->allocate<Sequence>(qmlType, data)->asReturnedValue();
-
- return Encode::undefined();
+ return engine->memoryManager->allocate<Sequence>(type, metaSequence, data)->asReturnedValue();
}
QVariant SequencePrototype::toVariant(const Sequence *object)
@@ -684,7 +681,7 @@ QVariant SequencePrototype::toVariant(const Sequence *object)
if (!p->hasData())
return QVariant();
- return QVariant(p->typePrivate()->listId, p->storagePointer());
+ return QVariant(p->listType(), p->storagePointer());
}
QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHint)
@@ -698,7 +695,7 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
const QQmlType type = QQmlMetaType::qmlListType(typeHint);
if (type.isSequentialContainer()) {
const QQmlTypePrivate *priv = type.priv();
- const QMetaSequence *meta = priv->extraData.ld;
+ const QMetaSequence *meta = &priv->extraData.sequentialContainerTypeData;
const QMetaType containerMetaType(priv->listId);
QVariant result(containerMetaType);
qint64 length = a->getLength();
@@ -714,18 +711,19 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
} else {
const QMetaType originalType = variant.metaType();
if (originalType != valueMetaType) {
- QVariant converted(valueMetaType);
- if (QQmlValueTypeProvider::createValueType(
- variant, valueMetaType, converted.data())) {
+ const QVariant converted = QQmlValueTypeProvider::createValueType(
+ variant, valueMetaType);
+ if (converted.isValid()) {
variant = converted;
- } else if (!variant.convert(valueMetaType)) {
+ } else if (!variant.convert(valueMetaType) && originalType.isValid()) {
+ // If the original type was void, we're converting a "hole" in a sparse
+ // array. There is no point in warning about that.
qWarning().noquote()
<< QLatin1String("Could not convert array value "
"at position %1 from %2 to %3")
.arg(QString::number(i),
QString::fromUtf8(originalType.name()),
QString::fromUtf8(valueMetaType.name()));
- variant = converted;
}
}
meta->addValueAtEnd(result.data(), variant.constData());
@@ -739,14 +737,14 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
void *SequencePrototype::getRawContainerPtr(const Sequence *object, QMetaType typeHint)
{
- if (object->d()->typePrivate()->listId == typeHint)
+ if (object->d()->listType() == typeHint)
return object->getRawContainerPtr();
return nullptr;
}
QMetaType SequencePrototype::metaTypeForSequence(const Sequence *object)
{
- return object->d()->typePrivate()->listId;
+ return object->d()->listType();
}
} // namespace QV4
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index 282928e661..3d1baf6c77 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct Sequence;
-struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object
+struct Q_QML_EXPORT SequencePrototype : public QV4::Object
{
V4_PROTOTYPE(arrayPrototype)
void init();
@@ -37,10 +37,11 @@ struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object
static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue newSequence(
- QV4::ExecutionEngine *engine, QMetaType sequenceType, const void *data,
- Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags);
+ QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data,
+ Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags);
static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant &vd);
- static ReturnedValue fromData(QV4::ExecutionEngine *engine, QMetaType type, const void *data);
+ static ReturnedValue fromData(
+ QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data);
static QMetaType metaTypeForSequence(const Sequence *object);
static QVariant toVariant(const Sequence *object);
@@ -52,8 +53,8 @@ namespace Heap {
struct Sequence : ReferenceObject
{
- void init(const QQmlType &qmlType, const void *container);
- void init(const QQmlType &qmlType, const void *container,
+ void init(QMetaType listType, QMetaSequence metaSequence, const void *container);
+ void init(QMetaType listType, QMetaSequence metaSequence, const void *container,
Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags);
Sequence *detached() const;
@@ -68,25 +69,30 @@ struct Sequence : ReferenceObject
bool setVariant(const QVariant &variant);
QVariant toVariant() const;
- const QQmlTypePrivate *typePrivate() const { return m_typePrivate; }
+ QMetaType listType() const { return QMetaType(m_listType); }
+ QMetaType valueMetaType() const { return QMetaType(m_metaSequence->valueMetaType); }
+ QMetaSequence metaSequence() const { return QMetaSequence(m_metaSequence); }
private:
+ void initTypes(QMetaType listType, QMetaSequence metaSequence);
+
void *m_container;
- const QQmlTypePrivate *m_typePrivate;
+ const QtPrivate::QMetaTypeInterface *m_listType;
+ const QtMetaContainerPrivate::QMetaSequenceInterface *m_metaSequence;
};
}
-struct Q_QML_PRIVATE_EXPORT Sequence : public QV4::ReferenceObject
+struct Q_QML_EXPORT Sequence : public QV4::ReferenceObject
{
V4_OBJECT2(Sequence, QV4::ReferenceObject)
Q_MANAGED_TYPE(V4Sequence)
V4_PROTOTYPE(sequencePrototype)
V4_NEEDS_DESTROY
public:
- static const QMetaType valueMetaType(const Heap::Sequence *p);
static QV4::ReturnedValue virtualGet(
const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static qint64 virtualGetLength(const Managed *m);
static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver);
static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id);
static bool virtualIsEqualTo(Managed *that, Managed *other);
diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp
index 2a4cdc1944..a7589a40db 100644
--- a/src/qml/jsruntime/qv4setobject.cpp
+++ b/src/qml/jsruntime/qv4setobject.cpp
@@ -8,6 +8,7 @@
#include "qv4symbol_p.h"
using namespace QV4;
+using namespace Qt::Literals::StringLiterals;
DEFINE_OBJECT_VTABLE(SetCtor);
DEFINE_OBJECT_VTABLE(WeakSetCtor);
@@ -37,7 +38,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv,
if (argc > 0) {
ScopedValue iterable(scope, argv[0]);
if (!iterable->isUndefined() && !iterable->isNull()) {
- ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add")))));
+ ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(u"add"_s))));
if (!adder)
return scope.engine->throwTypeError();
ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true));
@@ -54,10 +55,8 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv,
return a.asReturnedValue();
adder->call(a, nextValue, 1);
- if (scope.hasException()) {
- ScopedValue falsey(scope, Encode(false));
- return Runtime::IteratorClose::call(scope.engine, iter, falsey);
- }
+ if (scope.hasException())
+ return Runtime::IteratorClose::call(scope.engine, iter);
}
}
}
diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h
index db23c0af75..7da42a4985 100644
--- a/src/qml/jsruntime/qv4sparsearray_p.h
+++ b/src/qml/jsruntime/qv4sparsearray_p.h
@@ -110,8 +110,8 @@ struct Q_QML_EXPORT SparseArray
{
SparseArray();
~SparseArray() {
- if (root())
- freeTree(header.left, alignof(SparseArrayNode));
+ if (SparseArrayNode *n = root())
+ freeTree(n, alignof(SparseArrayNode));
}
SparseArray(const SparseArray &other);
@@ -287,37 +287,45 @@ inline QList<int> SparseArray::keys() const
inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const
{
- const SparseArrayNode *lb = root()->lowerBound(akey);
- if (!lb)
- lb = end();
- return lb;
+ if (SparseArrayNode *n = root()) {
+ if (const SparseArrayNode *lb = n->lowerBound(akey))
+ return lb;
+ }
+
+ return end();
}
inline SparseArrayNode *SparseArray::lowerBound(uint akey)
{
- SparseArrayNode *lb = root()->lowerBound(akey);
- if (!lb)
- lb = end();
- return lb;
+ if (SparseArrayNode *n = root()) {
+ if (SparseArrayNode *lb = n->lowerBound(akey))
+ return lb;
+ }
+
+ return end();
}
inline const SparseArrayNode *SparseArray::upperBound(uint akey) const
{
- const SparseArrayNode *ub = root()->upperBound(akey);
- if (!ub)
- ub = end();
- return ub;
+ if (SparseArrayNode *n = root()) {
+ if (const SparseArrayNode *ub = n->upperBound(akey))
+ return ub;
+ }
+
+ return end();
}
inline SparseArrayNode *SparseArray::upperBound(uint akey)
{
- SparseArrayNode *ub = root()->upperBound(akey);
- if (!ub)
- ub = end();
- return ub;
+ if (SparseArrayNode *n = root()) {
+ if (SparseArrayNode *ub = n->upperBound(akey))
+ return ub;
+ }
+
+ return end();
}
}
diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp
index e8ff9a89bc..5117e745a0 100644
--- a/src/qml/jsruntime/qv4stackframe.cpp
+++ b/src/qml/jsruntime/qv4stackframe.cpp
@@ -40,7 +40,7 @@ int CppStackFrame::lineNumber() const
{
if (auto *line = lineAndStatement(this))
return line->line;
- return -1;
+ return missingLineNumber();
}
int CppStackFrame::statementNumber() const
@@ -50,6 +50,15 @@ int CppStackFrame::statementNumber() const
return -1;
}
+int CppStackFrame::missingLineNumber() const
+{
+ // Remove the first bit so that we can cast to positive int and negate.
+ // Remove the last bit so that it can't be -1.
+ const int result = -int(quintptr(this) & 0x7ffffffe);
+ Q_ASSERT(result < -1);
+ return result;
+}
+
ReturnedValue QV4::CppStackFrame::thisObject() const
{
if (isJSTypesFrame())
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
index 2777d79c31..f24b0b2433 100644
--- a/src/qml/jsruntime/qv4stackframe_p.h
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -14,6 +14,7 @@
// We mean it.
//
+#include <private/qv4scopedvalue_p.h>
#include <private/qv4context_p.h>
#include <private/qv4enginebase_p.h>
#include <private/qv4calldata_p.h>
@@ -26,7 +27,7 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct CppStackFrame;
-struct Q_QML_PRIVATE_EXPORT CppStackFrameBase
+struct Q_QML_EXPORT CppStackFrameBase
{
enum class Kind : quint8 { JS, Meta };
@@ -35,6 +36,8 @@ struct Q_QML_PRIVATE_EXPORT CppStackFrameBase
int originalArgumentsCount;
int instructionPointer;
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_MSVC(4201) // nonstandard extension used: nameless struct/union
union {
struct {
Value *savedStackTop;
@@ -57,11 +60,12 @@ struct Q_QML_PRIVATE_EXPORT CppStackFrameBase
bool returnValueIsUndefined;
};
};
+ QT_WARNING_POP
Kind kind;
};
-struct Q_QML_PRIVATE_EXPORT CppStackFrame : protected CppStackFrameBase
+struct Q_QML_EXPORT CppStackFrame : protected CppStackFrameBase
{
// We want to have those public but we can't declare them as public without making the struct
// non-standard layout. So we have this other struct with "using" in between.
@@ -83,6 +87,8 @@ struct Q_QML_PRIVATE_EXPORT CppStackFrame : protected CppStackFrameBase
int lineNumber() const;
int statementNumber() const;
+ int missingLineNumber() const;
+
CppStackFrame *parentFrame() const { return parent; }
void setParentFrame(CppStackFrame *parentFrame) { parent = parentFrame; }
@@ -116,7 +122,7 @@ protected:
}
};
-struct Q_QML_PRIVATE_EXPORT MetaTypesStackFrame : public CppStackFrame
+struct Q_QML_EXPORT MetaTypesStackFrame : public CppStackFrame
{
using CppStackFrame::push;
using CppStackFrame::pop;
@@ -141,6 +147,9 @@ struct Q_QML_PRIVATE_EXPORT MetaTypesStackFrame : public CppStackFrame
const QMetaType *argTypes() const { return metaTypes + 1; }
void **argv() const { return returnAndArgs + 1; }
+ const QMetaType *returnAndArgTypes() const { return metaTypes; }
+ void **returnAndArgValues() const { return returnAndArgs; }
+
QObject *thisObject() const { return CppStackFrameBase::thisObject; }
ExecutionContext *context() const { return CppStackFrameBase::context; }
@@ -152,7 +161,7 @@ struct Q_QML_PRIVATE_EXPORT MetaTypesStackFrame : public CppStackFrame
}
};
-struct Q_QML_PRIVATE_EXPORT JSTypesStackFrame : public CppStackFrame
+struct Q_QML_EXPORT JSTypesStackFrame : public CppStackFrame
{
using CppStackFrame::jsFrame;
@@ -285,6 +294,30 @@ inline ExecutionContext *CppStackFrame::context() const
return static_cast<const MetaTypesStackFrame *>(this)->context();
}
+struct ScopedStackFrame
+{
+ ScopedStackFrame(const Scope &scope, ExecutionContext *context)
+ : engine(scope.engine)
+ {
+ if (auto currentFrame = engine->currentStackFrame) {
+ frame.init(currentFrame->v4Function, nullptr, context, nullptr, nullptr, 0);
+ frame.instructionPointer = currentFrame->instructionPointer;
+ } else {
+ frame.init(nullptr, nullptr, context, nullptr, nullptr, 0);
+ }
+ frame.push(engine);
+ }
+
+ ~ScopedStackFrame()
+ {
+ frame.pop(engine);
+ }
+
+private:
+ ExecutionEngine *engine = nullptr;
+ MetaTypesStackFrame frame;
+};
+
Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(JSTypesStackFrame));
Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(MetaTypesStackFrame));
Q_STATIC_ASSERT(std::is_standard_layout_v<CppStackFrame>);
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index 118382d5dc..8b5594b43b 100644
--- a/src/qml/jsruntime/qv4string.cpp
+++ b/src/qml/jsruntime/qv4string.cpp
@@ -170,23 +170,38 @@ bool Heap::String::startsWithUpper() const
void Heap::String::append(const String *data, QChar *ch)
{
- std::vector<const String *> worklist;
+ // in-order visitation with explicit stack
+ // where leaf nodes are "real" strings that get appended to ch
+
+ enum StatusTag : bool { NotVisited, Visited };
+ using Pointer = QTaggedPointer<const String, StatusTag>;
+
+ std::vector<Pointer> worklist;
worklist.reserve(32);
- worklist.push_back(data);
+ worklist.push_back(Pointer(data));
while (!worklist.empty()) {
- const String *item = worklist.back();
- worklist.pop_back();
+ Pointer item = worklist.back();
+ if (item.tag() == Visited) {
+ Q_ASSERT(item->subtype == StringType_AddedString);
+ const ComplexString *cs = static_cast<const ComplexString *>(item.data());
+ worklist.pop_back();
+ worklist.push_back(Pointer(cs->right));
+ continue;
+ }
if (item->subtype == StringType_AddedString) {
- const ComplexString *cs = static_cast<const ComplexString *>(item);
- worklist.push_back(cs->right);
- worklist.push_back(cs->left);
+ // we need to keep the node in the worklist, as we still need to handle "right"
+ worklist.back().setTag(Visited);
+ const ComplexString *cs = static_cast<const ComplexString *>(item.data());
+ worklist.push_back(Pointer(cs->left));
} else if (item->subtype == StringType_SubString) {
- const ComplexString *cs = static_cast<const ComplexString *>(item);
+ worklist.pop_back();
+ const ComplexString *cs = static_cast<const ComplexString *>(item.data());
memcpy(ch, cs->left->toQString().constData() + cs->from, cs->len*sizeof(QChar));
ch += cs->len;
} else {
+ worklist.pop_back();
memcpy(static_cast<void *>(ch), item->text().data(), item->text().size * sizeof(QChar));
ch += item->text().size;
}
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 78727bdcc3..370baadc98 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -29,7 +29,7 @@ struct PropertyKey;
namespace Heap {
-struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base
+struct Q_QML_EXPORT StringOrSymbol : Base
{
enum StringType {
StringType_Symbol,
@@ -76,7 +76,7 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base
}
};
-struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol {
+struct Q_QML_EXPORT String : StringOrSymbol {
static void markObjects(Heap::Base *that, MarkStack *markStack);
const VTable *vtable() const {
@@ -136,7 +136,7 @@ int String::length() const {
}
-struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed {
+struct Q_QML_EXPORT StringOrSymbol : public Managed {
V4_MANAGED(StringOrSymbol, Managed)
V4_NEEDS_DESTROY
enum {
@@ -155,7 +155,7 @@ public:
}
};
-struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
+struct Q_QML_EXPORT String : public StringOrSymbol {
V4_MANAGED(String, StringOrSymbol)
Q_MANAGED_TYPE(String)
V4_INTERNALCLASS(String)
@@ -195,6 +195,12 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
return calculateHashValue(ch, end, subtype);
}
+ static uint createHashValueDisallowingArrayIndex(const QChar *ch, int length, uint *subtype)
+ {
+ const QChar *end = ch + length;
+ return calculateHashValue<String::DisallowArrayIndex>(ch, end, subtype);
+ }
+
static uint createHashValue(const char *ch, int length, uint *subtype)
{
const char *end = ch + length;
@@ -208,15 +214,19 @@ protected:
static qint64 virtualGetLength(const Managed *m);
public:
- template <typename T>
+ enum IndicesBehavior {Default, DisallowArrayIndex};
+ template <IndicesBehavior Behavior = Default, typename T>
static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype)
{
// array indices get their number as hash value
- uint h = stringToArrayIndex(ch, end);
- if (h != UINT_MAX) {
- if (subtype)
- *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
- return h;
+ uint h = UINT_MAX;
+ if constexpr (Behavior != DisallowArrayIndex) {
+ h = stringToArrayIndex(ch, end);
+ if (h != UINT_MAX) {
+ if (subtype)
+ *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
+ return h;
+ }
}
while (ch < end) {
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 98bb6347d3..ad3c39c7b9 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -30,6 +30,7 @@
#endif
using namespace QV4;
+using namespace Qt::Literals::StringLiterals;
DEFINE_OBJECT_VTABLE(StringObject);
@@ -599,7 +600,7 @@ ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Valu
double maxLen = argv[0].toInteger();
if (maxLen <= s->d()->length())
return s->asReturnedValue();
- QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
+ QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : u" "_s;
if (v4->hasException)
return Encode::undefined();
@@ -638,7 +639,7 @@ ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Va
double maxLen = argv[0].toInteger();
if (maxLen <= s->d()->length())
return s->asReturnedValue();
- QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
+ QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : u" "_s;
if (v4->hasException)
return Encode::undefined();
diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp
index 585038937e..5f7ec89fd2 100644
--- a/src/qml/jsruntime/qv4symbol.cpp
+++ b/src/qml/jsruntime/qv4symbol.cpp
@@ -16,7 +16,7 @@ void Heap::Symbol::init(const QString &s)
Q_ASSERT(s.at(0) == QLatin1Char('@'));
QString desc(s);
StringOrSymbol::init(desc.data_ptr());
- identifier = PropertyKey::fromStringOrSymbol(this);
+ identifier = PropertyKey::fromStringOrSymbol(internalClass->engine, this);
}
void Heap::SymbolCtor::init(QV4::ExecutionContext *scope)
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index 3027cdf94f..6c72eaba5f 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -373,7 +373,7 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
uint elementSize = operations[that->d()->type].bytesPerElement;
size_t bufferSize;
- if (mul_overflow(size_t(l), size_t(elementSize), &bufferSize))
+ if (qMulOverflow(size_t(l), size_t(elementSize), &bufferSize))
return scope.engine->throwRangeError(QLatin1String("new TypedArray: invalid length"));
Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(bufferSize));
if (scope.hasException())
@@ -1680,14 +1680,13 @@ ReturnedValue IntrinsicTypedArrayCtor::method_from(const FunctionObject *f, cons
forever {
// Here we calculate the length of the iterable range.
if (iterableLength > (static_cast<qint64>(1) << 53) - 1) {
- ScopedValue falsey(scope, Encode(false));
ScopedValue error(scope, scope.engine->throwTypeError());
- return Runtime::IteratorClose::call(scope.engine, lengthIterator, falsey);
+ return Runtime::IteratorClose::call(scope.engine, lengthIterator);
}
// Retrieve the next value. If the iteration ends, we're done here.
done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, lengthIterator, nextValue));
if (scope.hasException())
- return Runtime::IteratorClose::call(scope.engine, lengthIterator, Value::fromBoolean(false));
+ return Runtime::IteratorClose::call(scope.engine, lengthIterator);
if (done->toBoolean()) {
break;
}
@@ -1720,21 +1719,21 @@ ReturnedValue IntrinsicTypedArrayCtor::method_from(const FunctionObject *f, cons
for (qint64 k = 0; k < iterableLength; ++k) {
done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, iterator, nextValue));
if (scope.hasException())
- return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false));
+ return Runtime::IteratorClose::call(scope.engine, iterator);
if (mapfn) {
mapArguments[0] = *nextValue;
mapArguments[1] = Value::fromDouble(k);
mappedValue = mapfn->call(thisArg, mapArguments, 2);
if (scope.hasException())
- return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false));
+ return Runtime::IteratorClose::call(scope.engine, iterator);
} else {
mappedValue = *nextValue;
}
a->put(k, mappedValue);
if (scope.hasException())
- return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false));
+ return Runtime::IteratorClose::call(scope.engine, iterator);
}
return a.asReturnedValue();
} else {
diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h
index 0284dceb7b..9747eac411 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -106,7 +106,7 @@ struct TypedArrayPrototype : Object {
}
-struct Q_QML_PRIVATE_EXPORT TypedArray : Object
+struct Q_QML_EXPORT TypedArray : Object
{
V4_OBJECT2(TypedArray, Object)
diff --git a/src/qml/jsruntime/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp
index 8b6db439e2..4ece91a2a2 100644
--- a/src/qml/jsruntime/qv4urlobject.cpp
+++ b/src/qml/jsruntime/qv4urlobject.cpp
@@ -124,14 +124,14 @@ void UrlObject::setUrl(const QUrl &url)
{
d()->hash.set(engine(), engine()->newString(url.fragment()));
d()->hostname.set(engine(), engine()->newString(url.host()));
- d()->href.set(engine(), engine()->newString(url.toString()));
+ d()->href.set(engine(), engine()->newString(url.toString(QUrl::ComponentFormattingOptions(QUrl::ComponentFormattingOption::FullyEncoded))));
d()->password.set(engine(), engine()->newString(url.password()));
d()->pathname.set(engine(), engine()->newString(url.path()));
d()->port.set(engine(),
engine()->newString(url.port() == -1 ? QLatin1String("")
: QString::number(url.port())));
d()->protocol.set(engine(), engine()->newString(url.scheme() + QLatin1Char(':')));
- d()->search.set(engine(), engine()->newString(url.query()));
+ d()->search.set(engine(), engine()->newString(url.query(QUrl::ComponentFormattingOptions(QUrl::ComponentFormattingOption::FullyEncoded))));
d()->username.set(engine(), engine()->newString(url.userName()));
updateOrigin();
@@ -242,6 +242,18 @@ bool UrlObject::setUsername(QString username)
return true;
}
+QString UrlObject::search() const
+{
+ auto url = QUrl(href());
+ if (auto url = QUrl(href()); !url.hasQuery() || url.query().isEmpty())
+ return QLatin1String("");
+
+ constexpr auto options = QUrl::ComponentFormattingOption::EncodeSpaces
+ | QUrl::ComponentFormattingOption::EncodeUnicode
+ | QUrl::ComponentFormattingOption::EncodeReserved;
+ return u'?' + url.query(options);
+}
+
QUrl UrlObject::toQUrl() const
{
return QUrl(href());
@@ -670,6 +682,7 @@ ReturnedValue UrlPrototype::method_getSearchParams(const FunctionObject *b, cons
Scoped<UrlSearchParamsObject> usp(scope, v4->newUrlSearchParamsObject());
+ usp->setUrlObject(thisObject->as<UrlObject>());
usp->initializeParams(r->search());
return usp->asReturnedValue();
@@ -960,6 +973,11 @@ void UrlSearchParamsObject::setParams(QList<QStringList> params)
d()->values.set(engine(), values);
}
+void UrlSearchParamsObject::setUrlObject(const UrlObject *url)
+{
+ d()->url.set(engine(), url->d());
+}
+
void UrlSearchParamsObject::append(Heap::String *name, Heap::String *value)
{
Scope scope(engine());
@@ -1008,6 +1026,25 @@ QList<QStringList> UrlSearchParamsObject::params() const
return result;
}
+Heap::UrlObject *UrlSearchParamsObject::urlObject() const
+{
+ return d()->url.get();
+}
+
+QString UrlSearchParamsObject::searchString() const
+{
+ QString search = QLatin1String("");
+ auto params = this->params();
+ auto len = params.size();
+ for (int i = 0; i < len; ++i) {
+ const QStringList &param = params[i];
+ search += param[0] + QLatin1Char('=') + param[1];
+ if (i != len - 1)
+ search += QLatin1Char('&');
+ }
+ return search;
+}
+
int UrlSearchParamsObject::length() const
{
auto *arrayObject = d()->params.get();
@@ -1251,12 +1288,7 @@ ReturnedValue UrlSearchParamsPrototype::method_delete(const FunctionObject *b, c
return Encode::undefined();
QList<QStringList> params = o->params();
-
- auto to_remove = std::remove_if(params.begin(), params.end(), [&name](QStringList pair) {
- return pair[0] == name;
- });
-
- params.erase(to_remove, params.end());
+ params.removeIf([&name](const auto &pair) { return pair.at(0) == name; });
o->setParams(params);
@@ -1335,6 +1367,10 @@ ReturnedValue UrlSearchParamsPrototype::method_set(const FunctionObject *b, cons
o->setParams(params);
+ Scoped<UrlObject> scopedUrlObject(scope, o->d()->url.get());
+ if (scopedUrlObject)
+ scopedUrlObject->setSearch(o->searchString());
+
return Encode::undefined();
}
diff --git a/src/qml/jsruntime/qv4urlobject_p.h b/src/qml/jsruntime/qv4urlobject_p.h
index e4a3ba073b..d45b74fcaa 100644
--- a/src/qml/jsruntime/qv4urlobject_p.h
+++ b/src/qml/jsruntime/qv4urlobject_p.h
@@ -50,12 +50,13 @@ struct UrlCtor : FunctionObject
void init(QV4::ExecutionContext *scope);
};
-// clang-format on
+// clang-format off
#define UrlSearchParamsObjectMembers(class, Member) \
Member(class, Pointer, ArrayObject *, params) \
Member(class, Pointer, ArrayObject *, keys) \
- Member(class, Pointer, ArrayObject *, values)
-// clang-format off
+ Member(class, Pointer, ArrayObject *, values) \
+ Member(class, Pointer, UrlObject *, url)
+// clang-format on
DECLARE_HEAP_OBJECT(UrlSearchParamsObject, Object)
{
@@ -101,7 +102,7 @@ struct UrlObject : Object
QString protocol() const { return toQString(d()->protocol); }
bool setProtocol(QString protocol);
- QString search() const { return QLatin1String("?") + toQString(d()->search); }
+ Q_QML_AUTOTEST_EXPORT QString search() const;
bool setSearch(QString search);
QString username() const { return toQString(d()->username); }
@@ -212,6 +213,10 @@ struct UrlSearchParamsObject : Object
QList<QStringList> params() const;
void setParams(QList<QStringList> params);
+ Heap::UrlObject *urlObject() const;
+ void setUrlObject(const UrlObject *url);
+
+ QString searchString() const;
QString nameAt(int index) const;
Heap::String * nameAtRaw(int index) const;
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index 259334e12c..f2d01c56cd 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -34,7 +34,7 @@ namespace Heap {
struct Base;
}
-struct Q_QML_PRIVATE_EXPORT Value : public StaticValue
+struct Q_QML_EXPORT Value : public StaticValue
{
using ManagedPtr = Managed *;
@@ -442,19 +442,6 @@ struct ValueArray {
return values;
}
- void insertData(EngineBase *e, uint index, Value v) {
- for (uint i = size - 1; i > index; --i) {
- values[i] = values[i - 1];
- }
- set(e, index, v);
- }
- void removeData(EngineBase *e, uint index, int n = 1) {
- Q_UNUSED(e);
- for (uint i = index; i < size - n; ++i) {
- values[i] = values[i + n];
- }
- }
-
void mark(MarkStack *markStack) {
for (Value *v = values, *end = values + alloc; v < end; ++v)
v->mark(markStack);
diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp
index ccea9cac2a..62e21a120c 100644
--- a/src/qml/jsruntime/qv4variantobject.cpp
+++ b/src/qml/jsruntime/qv4variantobject.cpp
@@ -131,7 +131,8 @@ ReturnedValue VariantPrototype::method_valueOf(const FunctionObject *b, const Va
return Encode(v.toBool());
default:
if (QMetaType(v.metaType()).flags() & QMetaType::IsEnumeration)
- return Encode(v.toInt());
+ if (v.metaType().sizeOf() <= qsizetype(sizeof(int)))
+ return Encode(v.toInt());
if (v.canConvert<double>())
return Encode(v.toDouble());
if (v.canConvert<int>())
diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h
index 1a4e0eeaa7..f2394ce9a2 100644
--- a/src/qml/jsruntime/qv4variantobject_p.h
+++ b/src/qml/jsruntime/qv4variantobject_p.h
@@ -54,7 +54,7 @@ private:
}
-struct VariantObject : Object
+struct Q_QML_EXPORT VariantObject : Object
{
V4_OBJECT2(VariantObject, Object)
V4_PROTOTYPE(variantPrototype)
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 41fbc9c182..096b9a6299 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -35,6 +35,9 @@
#undef COUNT_INSTRUCTIONS
+Q_TRACE_POINT(qtqml, QQmlV4_function_call_entry, const QV4::ExecutionEngine *engine, const QString &function, const QString &fileName, int line, int column)
+Q_TRACE_POINT(qtqml, QQmlV4_function_call_exit)
+
enum { ShowWhenDeoptimiationHappens = 0 };
extern "C" {
@@ -392,6 +395,21 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
} \
} while (false)
+struct AOTCompiledMetaMethod
+{
+public:
+ AOTCompiledMetaMethod(const Function::AOTCompiledFunction *aotCompiledFunction)
+ : aotCompiledFunction(aotCompiledFunction)
+ {}
+
+ int parameterCount() const { return aotCompiledFunction->types.size() - 1; }
+ QMetaType returnMetaType() const { return aotCompiledFunction->types[0]; }
+ QMetaType parameterMetaType(int i) const { return aotCompiledFunction->types[i + 1]; }
+
+private:
+ const Function::AOTCompiledFunction *aotCompiledFunction = nullptr;
+};
+
void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
{
qt_v4ResolvePendingBreakpointsHook();
@@ -402,103 +420,31 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
ExecutionEngineCallDepthRecorder executionEngineCallDepthRecorder(engine);
Function *function = frame->v4Function;
- Q_ASSERT(function->typedFunction);
+ Q_ASSERT(function->aotCompiledCode);
Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
function->executableCompilationUnit()->fileName(),
function->compiledFunction->location.line(),
function->compiledFunction->location.column());
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
- const qsizetype numFunctionArguments = function->typedFunction->argumentTypes.size();
-
- Q_ALLOCA_DECLARE(void *, transformedArguments);
- for (qsizetype i = 0; i < numFunctionArguments; ++i) {
- const QMetaType argumentType = function->typedFunction->argumentTypes[i];
- if (frame->argc() > i && argumentType == frame->argTypes()[i])
- continue;
-
- if (transformedArguments == nullptr) {
- Q_ALLOCA_ASSIGN(void *, transformedArguments, numFunctionArguments * sizeof(void *));
- memcpy(transformedArguments, frame->argv(), frame->argc() * sizeof(void *));
- }
-
- if (argumentType.sizeOf() == 0) {
- transformedArguments[i] = nullptr;
- continue;
- }
-
- Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
-
- if (argumentType == QMetaType::fromType<QVariant>()) {
- if (frame->argc() > i)
- new (arg) QVariant(frame->argTypes()[i], frame->argv()[i]);
- else
- new (arg) QVariant();
- } else if (argumentType == QMetaType::fromType<QJSPrimitiveValue>()) {
- if (frame->argc() > i)
- new (arg) QJSPrimitiveValue(frame->argTypes()[i], frame->argv()[i]);
- else
- new (arg) QJSPrimitiveValue();
- } else {
- argumentType.construct(arg);
- if (frame->argc() > i)
- QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
- }
-
- transformedArguments[i] = arg;
- }
-
- const QMetaType returnType = function->typedFunction->returnType;
- const QMetaType frameReturn = frame->returnType();
- Q_ALLOCA_DECLARE(void, transformedResult);
- if (frame->returnValue() && returnType != frameReturn) {
- if (returnType.sizeOf() > 0)
- Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf());
- else
- transformedResult = frame; // Some non-null marker value
- }
-
- QQmlPrivate::AOTCompiledContext aotContext;
- if (auto context = QV4::ExecutionEngine::qmlContext(frame->context()->d())) {
- QV4::Heap::QQmlContextWrapper *wrapper = static_cast<Heap::QmlContext *>(context)->qml();
- aotContext.qmlScopeObject = wrapper->scopeObject;
- aotContext.qmlContext = wrapper->context;
- }
-
- aotContext.engine = engine->jsEngine();
- aotContext.compilationUnit = function->executableCompilationUnit();
-
- function->typedFunction->functionPtr(
- &aotContext, transformedResult ? transformedResult : frame->returnValue(),
- transformedArguments ? transformedArguments : frame->argv());
-
- if (transformedResult) {
- // Shortcut the common case of the AOT function returning a more generic QObject pointer
- // that we need to QObject-cast. No need to construct or destruct anything in that case.
- if ((frameReturn.flags() & QMetaType::PointerToQObject)
- && (returnType.flags() & QMetaType::PointerToQObject)) {
- QObject *resultObj = *static_cast<QObject **>(transformedResult);
- *static_cast<QObject **>(frame->returnValue())
- = (resultObj && resultObj->metaObject()->inherits(frameReturn.metaObject()))
- ? resultObj
- : nullptr;
- } else {
- // Convert needs a pre-constructed target.
- frameReturn.construct(frame->returnValue());
- QMetaType::convert(returnType, transformedResult, frameReturn, frame->returnValue());
- returnType.destruct(transformedResult);
+ const AOTCompiledMetaMethod method(&function->aotCompiledFunction);
+ QV4::coerceAndCall(
+ engine, &method, frame->returnAndArgValues(),
+ frame->returnAndArgTypes(), frame->argc(),
+ [frame, engine, function](void **argv, int argc) {
+ Q_UNUSED(argc);
+
+ QQmlPrivate::AOTCompiledContext aotContext;
+ if (auto context = QV4::ExecutionEngine::qmlContext(frame->context()->d())) {
+ QV4::Heap::QQmlContextWrapper *wrapper = static_cast<Heap::QmlContext *>(context)->qml();
+ aotContext.qmlScopeObject = wrapper->scopeObject;
+ aotContext.qmlContext = wrapper->context;
}
- }
- if (transformedArguments) {
- for (int i = 0; i < numFunctionArguments; ++i) {
- void *arg = transformedArguments[i];
- if (arg == nullptr)
- continue;
- if (i >= frame->argc() || arg != frame->argv()[i])
- function->typedFunction->argumentTypes[i].destruct(arg);
- }
- }
+ aotContext.engine = engine->jsEngine();
+ aotContext.compilationUnit = function->executableCompilationUnit();
+ function->aotCompiledCode(&aotContext, argv);
+ });
}
ReturnedValue VME::exec(JSTypesStackFrame *frame, ExecutionEngine *engine)
@@ -736,7 +682,6 @@ QV4::ReturnedValue VME::interpret(JSTypesStackFrame *frame, ExecutionEngine *eng
QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
if (accumulator.isNullOrUndefined()) {
- acc = Encode::undefined();
code += offset;
} else {
acc = l->getter(l, engine, accumulator);
@@ -801,7 +746,8 @@ QV4::ReturnedValue VME::interpret(JSTypesStackFrame *frame, ExecutionEngine *eng
MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
STORE_ACC();
acc = Runtime::IteratorNextForYieldStar::call(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object));
- CHECK_EXCEPTION;
+ if (ACC.toBoolean())
+ code += offset;
MOTH_END_INSTR(IteratorNextForYieldStar)
MOTH_BEGIN_INSTR(CallValue)
@@ -848,15 +794,19 @@ QV4::ReturnedValue VME::interpret(JSTypesStackFrame *frame, ExecutionEngine *eng
// ok to have the value on the stack here
Value f = Value::fromReturnedValue(l->getter(l, engine, STACK_VALUE(base)));
- if (Q_UNLIKELY(!f.isFunctionObject())) {
- QString message = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ if (Q_LIKELY(f.isFunctionObject())) {
+ acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
+ } else if (QmlSignalHandler *handler = f.as<QmlSignalHandler>()) {
+ acc = handler->call(stack + base, stack + argv, argc);
+ } else {
+ const QString message = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit
+ ->runtimeStrings[l->nameIndex]->toQString())
.arg(STACK_VALUE(base).toQStringNoThrow());
acc = engine->throwTypeError(message);
goto handleUnwind;
}
- acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallPropertyLookup)
@@ -1011,15 +961,14 @@ QV4::ReturnedValue VME::interpret(JSTypesStackFrame *frame, ExecutionEngine *eng
STORE_IP();
STORE_ACC();
acc = Runtime::IteratorNext::call(engine, accumulator, &STACK_VALUE(value));
- STACK_VALUE(done) = acc;
- CHECK_EXCEPTION;
+ if (ACC.toBoolean())
+ code += offset;
MOTH_END_INSTR(IteratorNext)
MOTH_BEGIN_INSTR(IteratorClose)
STORE_IP();
STORE_ACC();
- acc = Runtime::IteratorClose::call(engine, accumulator, STACK_VALUE(done));
- CHECK_EXCEPTION;
+ acc = Runtime::IteratorClose::call(engine, accumulator);
MOTH_END_INSTR(IteratorClose)
MOTH_BEGIN_INSTR(DestructureRestElement)
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
index 7db1e71704..8ec8c87a31 100644
--- a/src/qml/jsruntime/qv4vtable_p.h
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -24,7 +24,7 @@ namespace QV4 {
struct Lookup;
-struct Q_QML_PRIVATE_EXPORT OwnPropertyKeyIterator {
+struct Q_QML_EXPORT OwnPropertyKeyIterator {
virtual ~OwnPropertyKeyIterator() = 0;
virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
};
diff --git a/src/qml/memory/design.md b/src/qml/memory/design.md
new file mode 100644
index 0000000000..1b4eb8eac4
--- /dev/null
+++ b/src/qml/memory/design.md
@@ -0,0 +1,125 @@
+The V4 Garbage Collector
+========================
+
+ChangeLog
+---------
+- < 6.8: There was little documentation, and the gc was STW mark&sweep
+- 6.8: The gc became incremental (with a stop-the-world sweep phase)
+- 6.8: Sweep was made incremental, too
+
+
+Glossary:
+------------
+- V4: The ECMAScript engine used in Qt (qtdeclarative)
+- gc: abbreviation for garbage collector
+- mutator: the actual user application (in constrast to the collector)
+- roots: A set of pointers from which the gc process starts
+- fast roots: A subset of the roots which are not protected by a barrier
+- write barrier: A set of instructions executed on each write
+- stop the world: not concurrent
+- concurrent gc: gc and mutator can run "at the same time"; this can either mean
+ + incremental: collector and mutator run in the same thread, but in certain time intervals the mutator is paused, a chunk of the collector is executed, and then the mutator resumes. Repeats until the gc cycle is finished
+ + parallel: gc and mutator operations run in different threads
+- precise: a gc is precise if for every value it can know whether it's a pointer to a heap object (a non-precise gc can't in general distinguish pointers from pointer-sized numbers)
+- floating garbage: items that are not live, but nevertheless end up surviving the gc cycle
+- generational: generational refers to dividing objects into different "generations" based on how many collection cycles they survived. This technique is used in garbage collection to improve performance by focusing on collecting the most recently created objects first.
+- moving: A gc is moving if it can relocate objects in memory. Care must be taken to update pointers pointing to them.
+
+
+Overview:
+---------
+
+Since Qt 6.8, V4 uses an incremental, precise mark-and-sweep gc algorithm. It is neither generational nor moving.
+
+In the mark phase, each heap-item can be in one of three states:
+1. unvisited ("white"): The gc has not seen this item at all
+2. seen ("grey"): All grey items have been discovered by the gc, but items directly reachable from them have (potentially) not been visited.
+3. finished ("black"): Not only has the item been seen, but also all items directly reachable from it have been seen.
+
+Items are black if they have their corresponding bit set in the black-object bitmap. They are grey if they are stored at least once in the MarkStack, a stack data structure. Items are white if they are neither grey nor black. Note that black items must never be pushed to the MarkStack (otherwise we could easily end up with endless cycles), but items already _on_ the MarkStack can be black:
+If an item has been pushed multiple times before it has been popped, this can happen. It causes some additional work to revisit its fields, but that is safe, as after popping the item will be black, and thus we won't keep on repeatedly pushing the same item on the mark stack.
+
+The roots consist of
+- the engine-global objects (namely the internal classes for the JS globals)
+- all strings (and symbols) stored in the identifier table and
+- all actively linked compilation units.
+- Moreover, the values on the JS stack are also treated as roots; more precisely as fast roots.
+- Additionally, all persistent values (everything stored in a QJSValue as well as all bound functions of QQmlBindings) are added to the roots.
+- Lastly, all QObjectWrapper of QObjects with C++ ownership, or which are rooted in or parented to a QObject with C++ ownership are added to the root set.
+
+All roots are added to the MarkStack. Then, during mark phase, entries are:
+1. popped from the markstack
+2. All heap-objects reachable from them are added to the MarkStack (unless they are already black)
+
+To avoid that values that were on the heap during the start of the gc cycle, then moved to the stack before they could be visited and consequently freed even though they are still live, the stack is rescanned before the sweep phase.
+
+To avoid that unmarked heap-items are moved from one heap item (or the stack) to an already marked heap-item (and consequently end up hidden from the gc), we employ a Dijkstra style write barrier: Any item that becomes reachable from another heap-item is marked grey (unless already black).
+
+While a gc cycle is ongoing, allocations are black, meaning every allocated object is considered to be live (until the next gc cycle is started).
+This is currently required as compilation units linked to the engine while the gc is running are not protected by the write barrier or another mechanism. It also helps to reduce the amount of work to be done when rescanning the JS stack (as it helps to ensure that most items are already black at that point).
+
+
+The gc state machine
+--------------------
+
+To facilitate incremental garbage collection, the gc algorithm is divided into the following stages:
+
+1. markStart, the atomic initialization phase, in which the MarkStack is initialized, and a flag is set on the engine indicating that incremental gc is active
+2. markGlobalObject, an atomic phase in which the global object, the engine's identifier table and the currently linked compilation units are marked
+3. markJSStack, an atomic phase in which the JS stack is marked
+4. initMarkPersistentValues: Atomic phase. If there are persistent values, some setup is done for the next phase.
+5. markPersistentValues: An interruptible phase in which all persistent values are marked.
+6. initMarkWeakValues: Atomic phase. If there are weak values, some setup is done for the next phase
+7. markWeakValues: An interruptible phase which takes care of marking the QObjectWrappers
+8. markDrain: An interrupible phase. While the MarkStack is not empty, the marking algorithm runs.
+9. markReady: An atomic phase which currently does nothing, but could be used for e.g. logging statistics
+10. initCallDestroyObjects: An atomic phase, in which the stack is rescanned, the MarkStack is drained once more. This ensures that all live objects are really marked.
+ Afterwards, the iteration over all the QObjectWrappers is prepared.
+11. callDestroyObject: An interruptible phase, were we call destroyObject of all non-marked QObjectWrapper.
+12. freeWeakMaps: An atomic phase in which we remove references to dead objects from live weak maps.
+13. freeWeakSets: Same as the last phase, but for weak sets
+14: handleQObjectWrappers: An atomic phase in which pending references to QObjectWrappers are cleared
+15. multiple sweep phases: Atomic phases, in which do the actual sweeping to free up memory. Note that this will also call destroy on objects marked with `V4_NEEDS_DESTROY`. There is one phase for the various allocators (identifier table, block allocator, huge item allocator, IC allocator)
+16. updateMetaData: Updates the black bitmaps, the usage statistics, and marks the gc cycle as done.
+17. invalid, the "not-running" stage of the state machine.
+
+To avoid constantly having to query the timer, even interruptible phases run for a fixed amount of steps before checking whether there's a timemout.
+
+Most steps are straight-forward, only the persistent and weak value phases require some explanation as to why it's safe to interrupt the process: The important thing to note is that we never remove elements from the structure while we're undergoing gc, and that we only ever append at the end. So we will see any new values that might be added.
+
+Persistent Values
+-----------------
+As shown in the diagram above, the handling of persistent values is interruptible (both for "real" persistent values, and also for weak vaules which also are stored in a `PersistentValueStorage` data structure.
+This is done by storing the `PersistentValueStorage::Iterator` in the gc state machine. That in turn raises two questions: Is the iterator safe against invalidation? And do we actually keep track of newly added persistent values?
+
+The latter part is easy to answer: Any newly added weak value is marked when we are in a gc cycle, so the marking part is handled. Sweeping only cares about unmarked values, so that's safe too.
+To answer the question about iterator validity, we have to look at the `PersistentValueStorage` data structure. Conceptionally, it's a forward-list of `Page`s (arrays of `QV4::Value`). Pages are ref-counted, and only unliked from the list if the ref-count reaches zero. Moreover, iterators also increase the ref-count.
+Therefore, as long as we iterate over the list, we don't risk having the pointer point to a deleted `Page` – even if all values in it have been freed. Freeing values is unproblematic for the gc – it will simply encounter `undefined` values, something it is already prepared to handle.
+Pages are also kept in the list while they are not deleted, so iteration works as expected. The only adjustment we need to do is to disable an optimization: When searching for a Page with an empty slot, we have
+to (potentially) travese the whole `PersistentValueStorage`. To avoid that, the first Page with empty slots is normally moved to the front of the list. However, that would mean that we could potentially skip over it during the marking phase. We sidestep that issue by simply disabling the optimization. This area could
+easily be improved in the future by keeping track of the first page with free slots in a different way.
+
+Custom marking
+---------------
+
+Some parts of the engine have to deviate from the general scheme described in the overview, as they don't integrate with the normal WriteBarrier. They are wrapped in the callback of the `QV4::WriteBarrier::markCustom` function, so that they can easily be found via "Find references".
+
+1. `QJSValue::encode`. QJSValues act as roots, and don't act as normal heap-items. When the current value of a QJSValue is overwritten with another heap-item, we also mark the new object. That aligns nicely with the gc barrier.
+2. The same applies to `PersistentValue::set`.
+3. The identifier table is another root; if a new string is inserted there during gc, it is (conservatively) marked black.
+4. PropertyKeys should for all intents and purposes use a write barrier (and have a deleted operator=). But them being an ad-hoc union type of numbers, Strings and Symbols, which has the additional requirements of having to be trivial, it turned out to be easier to simply mark them in `SharedInternalClassDataPrivate<PropertyKey>::set` (for PropertyKeys that had already been allocated), and on the fact that we allocate black for newly created PropertyKeys.
+5. `QV4::Heap::InternalClass` also requires special handling, as it uses plain members to Heap objects, notably to the prototype and to the parent internal class. As the class is somewhat special in any case (due to the usage of `SharedInternalClassData` and especially due to the usage of `SharedInternalClassData<PropertyKey>`, see notes on PropertyKey above), we use some bespoke sections for now. This could probably be cleaned up.
+
+Motivation for using a Dijkstra style barrier:
+----------------------------------------------
+- Deletion barriers are hard to support with the current PropertyKey design
+- Steele style barriers cause more work (have to revisit more objects), and as long as we have black allocations it doesn't make much sense to optimize for a minimal amount of floating garbage.
+
+Sweep Phase and finalizers:
+---------------------------
+A story for another day
+
+Allocator design:
+-----------------
+Your explanation is in another castle.
+
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 5c3be27014..8f6a6503fc 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -25,6 +25,8 @@
#include "qv4mapobject_p.h"
#include "qv4setobject_p.h"
+#include <chrono>
+
//#define MM_STATS
#if !defined(MM_STATS) && !defined(QT_NO_DEBUG)
@@ -671,6 +673,229 @@ void HugeItemAllocator::freeAll()
}
}
+namespace {
+using ExtraData = GCStateInfo::ExtraData;
+GCState markStart(GCStateMachine *that, ExtraData &)
+{
+ //Initialize the mark stack
+ that->mm->m_markStack = std::make_unique<MarkStack>(that->mm->engine);
+ that->mm->engine->isGCOngoing = true;
+ return MarkGlobalObject;
+}
+
+GCState markGlobalObject(GCStateMachine *that, ExtraData &)
+{
+ that->mm->engine->markObjects(that->mm->m_markStack.get());
+ return MarkJSStack;
+}
+
+GCState markJSStack(GCStateMachine *that, ExtraData &)
+{
+ that->mm->collectFromJSStack(that->mm->markStack());
+ return InitMarkPersistentValues;
+}
+
+GCState initMarkPersistentValues(GCStateMachine *that, ExtraData &stateData)
+{
+ if (!that->mm->m_persistentValues)
+ return InitMarkWeakValues; // no persistent values to mark
+ stateData = GCIteratorStorage { that->mm->m_persistentValues->begin() };
+ return MarkPersistentValues;
+}
+
+static constexpr int markLoopIterationCount = 1024;
+
+bool wasDrainNecessary(MarkStack *ms, QDeadlineTimer deadline)
+{
+ if (ms->remainingBeforeSoftLimit() > markLoopIterationCount)
+ return false;
+ // drain
+ ms->drain(deadline);
+ return true;
+}
+
+GCState markPersistentValues(GCStateMachine *that, ExtraData &stateData) {
+ auto markStack = that->mm->markStack();
+ if (wasDrainNecessary(markStack, that->deadline) && that->deadline.hasExpired())
+ return MarkPersistentValues;
+ PersistentValueStorage::Iterator& it = get<GCIteratorStorage>(stateData).it;
+ // avoid repeatedly hitting the timer constantly by batching iterations
+ for (int i = 0; i < markLoopIterationCount; ++i) {
+ if (!it.p)
+ return InitMarkWeakValues;
+ if (Managed *m = (*it).as<Managed>())
+ m->mark(markStack);
+ ++it;
+ }
+ return MarkPersistentValues;
+}
+
+GCState initMarkWeakValues(GCStateMachine *that, ExtraData &stateData)
+{
+ stateData = GCIteratorStorage { that->mm->m_weakValues->begin() };
+ return MarkWeakValues;
+}
+
+GCState markWeakValues(GCStateMachine *that, ExtraData &stateData)
+{
+ auto markStack = that->mm->markStack();
+ if (wasDrainNecessary(markStack, that->deadline) && that->deadline.hasExpired())
+ return MarkWeakValues;
+ PersistentValueStorage::Iterator& it = get<GCIteratorStorage>(stateData).it;
+ // avoid repeatedly hitting the timer constantly by batching iterations
+ for (int i = 0; i < markLoopIterationCount; ++i) {
+ if (!it.p)
+ return MarkDrain;
+ QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>();
+ ++it;
+ if (!qobjectWrapper)
+ continue;
+ QObject *qobject = qobjectWrapper->object();
+ if (!qobject)
+ continue;
+ bool keepAlive = QQmlData::keepAliveDuringGarbageCollection(qobject);
+
+ if (!keepAlive) {
+ if (QObject *parent = qobject->parent()) {
+ while (parent->parent())
+ parent = parent->parent();
+ keepAlive = QQmlData::keepAliveDuringGarbageCollection(parent);
+ }
+ }
+
+ if (keepAlive)
+ qobjectWrapper->mark(that->mm->markStack());
+ }
+ return MarkWeakValues;
+}
+
+GCState markDrain(GCStateMachine *that, ExtraData &)
+{
+ if (that->deadline.isForever()) {
+ that->mm->markStack()->drain();
+ return MarkReady;
+ }
+ auto drainState = that->mm->m_markStack->drain(that->deadline);
+ return drainState == MarkStack::DrainState::Complete
+ ? MarkReady
+ : MarkDrain;
+}
+
+GCState markReady(GCStateMachine *, ExtraData &)
+{
+ //Possibility to do some clean up, stat printing, etc...
+ return InitCallDestroyObjects;
+}
+
+/** \!internal
+collects new references from the stack, then drains the mark stack again
+*/
+void redrain(GCStateMachine *that)
+{
+ that->mm->collectFromJSStack(that->mm->markStack());
+ that->mm->m_markStack->drain();
+}
+
+GCState initCallDestroyObjects(GCStateMachine *that, ExtraData &stateData)
+{
+ // as we don't have a deletion barrier, we need to rescan the stack
+ redrain(that);
+ if (!that->mm->m_weakValues)
+ return FreeWeakMaps; // no need to call destroy objects
+ stateData = GCIteratorStorage { that->mm->m_weakValues->begin() };
+ return CallDestroyObjects;
+}
+GCState callDestroyObject(GCStateMachine *that, ExtraData &stateData)
+{
+ PersistentValueStorage::Iterator& it = get<GCIteratorStorage>(stateData).it;
+ // destroyObject might call user code, which really shouldn't call back into the gc
+ auto oldState = std::exchange(that->mm->gcBlocked, QV4::MemoryManager::Blockness::InCriticalSection);
+ auto cleanup = qScopeGuard([&]() {
+ that->mm->gcBlocked = oldState;
+ });
+ // avoid repeatedly hitting the timer constantly by batching iterations
+ for (int i = 0; i < markLoopIterationCount; ++i) {
+ if (!it.p)
+ return FreeWeakMaps;
+ Managed *m = (*it).managed();
+ ++it;
+ if (!m || m->markBit())
+ continue;
+ // we need to call destroyObject on qobjectwrappers now, so that they can emit the destroyed
+ // signal before we start sweeping the heap
+ if (QObjectWrapper *qobjectWrapper = m->as<QObjectWrapper>())
+ qobjectWrapper->destroyObject(/*lastSweep =*/false);
+ }
+ return CallDestroyObjects;
+}
+
+void freeWeakMaps(MemoryManager *mm)
+{
+ for (auto [map, lastMap] = std::tuple {mm->weakMaps, &mm->weakMaps }; map; map = map->nextWeakMap) {
+ if (!map->isMarked())
+ continue;
+ map->removeUnmarkedKeys();
+ *lastMap = map;
+ lastMap = &map->nextWeakMap;
+ }
+}
+
+GCState freeWeakMaps(GCStateMachine *that, ExtraData &)
+{
+ freeWeakMaps(that->mm);
+ return FreeWeakSets;
+}
+
+void freeWeakSets(MemoryManager *mm)
+{
+ for (auto [set, lastSet] = std::tuple {mm->weakSets, &mm->weakSets}; set; set = set->nextWeakSet) {
+
+ if (!set->isMarked())
+ continue;
+ set->removeUnmarkedKeys();
+ *lastSet = set;
+ lastSet = &set->nextWeakSet;
+ }
+}
+
+GCState freeWeakSets(GCStateMachine *that, ExtraData &)
+{
+ freeWeakSets(that->mm);
+ return HandleQObjectWrappers;
+}
+
+GCState handleQObjectWrappers(GCStateMachine *that, ExtraData &)
+{
+ that->mm->cleanupDeletedQObjectWrappersInSweep();
+ return DoSweep;
+}
+
+GCState doSweep(GCStateMachine *that, ExtraData &)
+{
+ auto mm = that->mm;
+
+ mm->engine->identifierTable->sweep();
+ mm->blockAllocator.sweep();
+ mm->hugeItemAllocator.sweep(that->mm->gcCollectorStats ? increaseFreedCountForClass : nullptr);
+ mm->icAllocator.sweep();
+
+ // reset all black bits
+ mm->blockAllocator.resetBlackBits();
+ mm->hugeItemAllocator.resetBlackBits();
+ mm->icAllocator.resetBlackBits();
+
+ mm->usedSlotsAfterLastFullSweep = mm->blockAllocator.usedSlotsAfterLastSweep + mm->icAllocator.usedSlotsAfterLastSweep;
+ mm->gcBlocked = MemoryManager::Unblocked;
+ mm->m_markStack.reset();
+ mm->engine->isGCOngoing = false;
+
+ mm->updateUnmanagedHeapSizeGCLimit();
+
+ return Invalid;
+}
+
+}
+
MemoryManager::MemoryManager(ExecutionEngine *engine)
: engine(engine)
@@ -691,6 +916,70 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
memset(statistics.allocations, 0, sizeof(statistics.allocations));
if (gcStats)
blockAllocator.allocationStats = statistics.allocations;
+
+ gcStateMachine = std::make_unique<GCStateMachine>();
+ gcStateMachine->mm = this;
+
+ gcStateMachine->stateInfoMap[GCState::MarkStart] = {
+ markStart,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::MarkGlobalObject] = {
+ markGlobalObject,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::MarkJSStack] = {
+ markJSStack,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::InitMarkPersistentValues] = {
+ initMarkPersistentValues,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::MarkPersistentValues] = {
+ markPersistentValues,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::InitMarkWeakValues] = {
+ initMarkWeakValues,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::MarkWeakValues] = {
+ markWeakValues,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::MarkDrain] = {
+ markDrain,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::MarkReady] = {
+ markReady,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::InitCallDestroyObjects] = {
+ initCallDestroyObjects,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::CallDestroyObjects] = {
+ callDestroyObject,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::FreeWeakMaps] = {
+ freeWeakMaps,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::FreeWeakSets] = {
+ freeWeakSets,
+ true, // ensure that handleQObjectWrappers runs in isolation
+ };
+ gcStateMachine->stateInfoMap[GCState::HandleQObjectWrappers] = {
+ handleQObjectWrappers,
+ false,
+ };
+ gcStateMachine->stateInfoMap[GCState::DoSweep] = {
+ doSweep,
+ false,
+ };
}
Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
@@ -704,13 +993,6 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
HeapItem *m = allocate(&blockAllocator, stringSize);
memset(m, 0, stringSize);
- if (gcBlocked) {
- // If the gc is running right now, it will not have a chance to mark the newly created item
- // and may therefore sweep it right away.
- // Protect the new object from the current GC run to avoid this.
- m->as<Heap::Base>()->setMarkBit();
- }
-
return *m;
}
@@ -726,13 +1008,6 @@ Heap::Base *MemoryManager::allocData(std::size_t size)
HeapItem *m = allocate(&blockAllocator, size);
memset(m, 0, size);
- if (gcBlocked) {
- // If the gc is running right now, it will not have a chance to mark the newly created item
- // and may therefore sweep it right away.
- // Protect the new object from the current GC run to avoid this.
- m->as<Heap::Base>()->setMarkBit();
- }
-
return *m;
}
@@ -790,6 +1065,7 @@ MarkStack::MarkStack(ExecutionEngine *engine)
void MarkStack::drain()
{
+ // we're not calling drain(QDeadlineTimer::Forever) as that has higher overhead
while (m_top > m_base) {
Heap::Base *h = pop();
++markStackSize;
@@ -798,91 +1074,85 @@ void MarkStack::drain()
}
}
-void MemoryManager::collectRoots(MarkStack *markStack)
+MarkStack::DrainState MarkStack::drain(QDeadlineTimer deadline)
{
- engine->markObjects(markStack);
-
-// qDebug() << " mark stack after engine->mark" << (engine->jsStackTop - markBase);
-
- collectFromJSStack(markStack);
-
-// qDebug() << " mark stack after js stack collect" << (engine->jsStackTop - markBase);
- m_persistentValues->mark(markStack);
-
-// qDebug() << " mark stack after persistants" << (engine->jsStackTop - markBase);
-
- // Preserve QObject ownership rules within JavaScript: A parent with c++ ownership
- // keeps all of its children alive in JavaScript.
-
- // Do this _after_ collectFromStack to ensure that processing the weak
- // managed objects in the loop down there doesn't make then end up as leftovers
- // on the stack and thus always get collected.
- for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
- QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>();
- if (!qobjectWrapper)
- continue;
- QObject *qobject = qobjectWrapper->object();
- if (!qobject)
- continue;
- bool keepAlive = QQmlData::keepAliveDuringGarbageCollection(qobject);
-
- if (!keepAlive) {
- if (QObject *parent = qobject->parent()) {
- while (parent->parent())
- parent = parent->parent();
-
- keepAlive = QQmlData::keepAliveDuringGarbageCollection(parent);
- }
+ do {
+ for (int i = 0; i <= markLoopIterationCount * 10; ++i) {
+ if (m_top == m_base)
+ return DrainState::Complete;
+ Heap::Base *h = pop();
+ ++markStackSize;
+ Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen.
+ h->internalClass->vtable->markObjects(h, this);
}
+ } while (!deadline.hasExpired());
+ return DrainState::Ongoing;
+}
- if (keepAlive)
- qobjectWrapper->mark(markStack);
+void MemoryManager::onEventLoop()
+{
+ if (engine->inShutdown)
+ return;
+ if (gcBlocked == InCriticalSection) {
+ QMetaObject::invokeMethod(engine->publicEngine, [this]{
+ onEventLoop();
+ }, Qt::QueuedConnection);
+ return;
+ }
+ if (gcStateMachine->inProgress()) {
+ gcStateMachine->step();
}
}
-void MemoryManager::mark()
+
+void MemoryManager::setGCTimeLimit(int timeMs)
{
- markStackSize = 0;
- MarkStack markStack(engine);
- collectRoots(&markStack);
- // dtor of MarkStack drains
+ gcStateMachine->timeLimit = std::chrono::milliseconds(timeMs);
}
void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPtr)
{
+
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
Managed *m = (*it).managed();
if (!m || m->markBit())
continue;
// we need to call destroyObject on qobjectwrappers now, so that they can emit the destroyed
// signal before we start sweeping the heap
- if (QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>())
+ if (QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>()) {
qobjectWrapper->destroyObject(lastSweep);
- }
-
- // remove objects from weak maps and sets
- Heap::MapObject *map = weakMaps;
- Heap::MapObject **lastMap = &weakMaps;
- while (map) {
- if (map->isMarked()) {
- map->removeUnmarkedKeys();
- *lastMap = map;
- lastMap = &map->nextWeakMap;
}
- map = map->nextWeakMap;
}
- Heap::SetObject *set = weakSets;
- Heap::SetObject **lastSet = &weakSets;
- while (set) {
- if (set->isMarked()) {
- set->removeUnmarkedKeys();
- *lastSet = set;
- lastSet = &set->nextWeakSet;
- }
- set = set->nextWeakSet;
+ freeWeakMaps(this);
+ freeWeakSets(this);
+
+ cleanupDeletedQObjectWrappersInSweep();
+
+ if (!lastSweep) {
+ engine->identifierTable->sweep();
+ blockAllocator.sweep(/*classCountPtr*/);
+ hugeItemAllocator.sweep(classCountPtr);
+ icAllocator.sweep(/*classCountPtr*/);
}
+ // reset all black bits
+ blockAllocator.resetBlackBits();
+ hugeItemAllocator.resetBlackBits();
+ icAllocator.resetBlackBits();
+
+ usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep + icAllocator.usedSlotsAfterLastSweep;
+ updateUnmanagedHeapSizeGCLimit();
+ gcBlocked = MemoryManager::Unblocked;
+}
+
+/*
+ \internal
+ Helper function used in sweep to clean up the (to-be-freed) QObjectWrapper
+ Used both in MemoryManager::sweep, and the corresponding gc statemachine phase
+*/
+void MemoryManager::cleanupDeletedQObjectWrappersInSweep()
+{
// onDestruction handlers may have accessed other QObject wrappers and reset their value, so ensure
// that they are all set to undefined.
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
@@ -915,14 +1185,6 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
++it;
}
}
-
-
- if (!lastSweep) {
- engine->identifierTable->sweep();
- blockAllocator.sweep(/*classCountPtr*/);
- hugeItemAllocator.sweep(classCountPtr);
- icAllocator.sweep(/*classCountPtr*/);
- }
}
bool MemoryManager::shouldRunGC() const
@@ -962,15 +1224,44 @@ static size_t dumpBins(BlockAllocator *b, const char *title)
return totalSlotMem*Chunk::SlotSize;
}
+/*!
+ \internal
+ Precondition: Incremental garbage collection must be currently active
+ Finishes incremental garbage collection, unless in a critical section
+ Code entering a critical section is expected to check if we need to
+ force a gc completion, and to trigger the gc again if necessary
+ when exiting the critcial section.
+ Returns \c true if the gc cycle completed, false otherwise.
+ */
+bool MemoryManager::tryForceGCCompletion()
+{
+ if (gcBlocked == InCriticalSection)
+ return false;
+ const bool incrementalGCIsAlreadyRunning = m_markStack != nullptr;
+ Q_ASSERT(incrementalGCIsAlreadyRunning);
+ auto oldTimeLimit = std::exchange(gcStateMachine->timeLimit, std::chrono::microseconds::max());
+ while (gcStateMachine->inProgress()) {
+ gcStateMachine->step();
+ }
+ gcStateMachine->timeLimit = oldTimeLimit;
+ return true;
+}
+
+void MemoryManager::runFullGC()
+{
+ runGC();
+ const bool incrementalGCStillRunning = m_markStack != nullptr;
+ if (incrementalGCStillRunning)
+ tryForceGCCompletion();
+}
+
void MemoryManager::runGC()
{
- if (gcBlocked) {
-// qDebug() << "Not running GC.";
+ if (gcBlocked != Unblocked) {
return;
}
- QScopedValueRollback<bool> gcBlocker(gcBlocked, true);
-// qDebug() << "runGC";
+ gcBlocked = MemoryManager::NormalBlocked;
if (gcStats) {
statistics.maxReservedMem = qMax(statistics.maxReservedMem, getAllocatedMem());
@@ -978,8 +1269,7 @@ void MemoryManager::runGC()
}
if (!gcCollectorStats) {
- mark();
- sweep();
+ gcStateMachine->step();
} else {
bool triggeredByUnmanagedHeap = (unmanagedHeapSize > unmanagedHeapSizeGCLimit);
size_t oldUnmanagedSize = unmanagedHeapSize;
@@ -1003,13 +1293,11 @@ void MemoryManager::runGC()
QElapsedTimer t;
t.start();
- mark();
+ gcStateMachine->step();
qint64 markTime = t.nsecsElapsed()/1000;
t.restart();
- sweep(false, increaseFreedCountForClass);
const size_t usedAfter = getUsedMem();
const size_t largeItemsAfter = getLargeItemsMem();
- qint64 sweepTime = t.nsecsElapsed()/1000;
if (triggeredByUnmanagedHeap) {
qDebug(stats) << "triggered by unmanaged heap:";
@@ -1021,7 +1309,6 @@ void MemoryManager::runGC()
+ dumpBins(&icAllocator, "InternalClasss");
qDebug(stats) << "Marked object in" << markTime << "us.";
qDebug(stats) << " " << markStackSize << "objects marked";
- qDebug(stats) << "Sweeped object in" << sweepTime << "us.";
// sort our object types by number of freed instances
MMStatsHash freedObjectStats;
@@ -1059,21 +1346,6 @@ void MemoryManager::runGC()
if (gcStats)
statistics.maxUsedMem = qMax(statistics.maxUsedMem, getUsedMem() + getLargeItemsMem());
-
- if (aggressiveGC) {
- // ensure we don't 'loose' any memory
- Q_ASSERT(blockAllocator.allocatedMem()
- == blockAllocator.usedMem() + dumpBins(&blockAllocator, nullptr));
- Q_ASSERT(icAllocator.allocatedMem()
- == icAllocator.usedMem() + dumpBins(&icAllocator, nullptr));
- }
-
- usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep + icAllocator.usedSlotsAfterLastSweep;
-
- // reset all black bits
- blockAllocator.resetBlackBits();
- hugeItemAllocator.resetBlackBits();
- icAllocator.resetBlackBits();
}
size_t MemoryManager::getUsedMem() const
@@ -1091,6 +1363,29 @@ size_t MemoryManager::getLargeItemsMem() const
return hugeItemAllocator.usedMem();
}
+void MemoryManager::updateUnmanagedHeapSizeGCLimit()
+{
+ if (3*unmanagedHeapSizeGCLimit <= 4 * unmanagedHeapSize) {
+ // more than 75% full, raise limit
+ unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit,
+ unmanagedHeapSize) * 2;
+ } else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit) {
+ // less than 25% full, lower limit
+ unmanagedHeapSizeGCLimit = qMax(std::size_t(MinUnmanagedHeapSizeGCLimit),
+ unmanagedHeapSizeGCLimit/2);
+ }
+
+ if (aggressiveGC && !engine->inShutdown) {
+ // ensure we don't 'loose' any memory
+ // but not during shutdown, because than we skip parts of sweep
+ // and use freeAll instead
+ Q_ASSERT(blockAllocator.allocatedMem()
+ == blockAllocator.usedMem() + dumpBins(&blockAllocator, nullptr));
+ Q_ASSERT(icAllocator.allocatedMem()
+ == icAllocator.usedMem() + dumpBins(&icAllocator, nullptr));
+ }
+}
+
void MemoryManager::registerWeakMap(Heap::MapObject *map)
{
map->nextWeakMap = weakMaps;
@@ -1106,10 +1401,22 @@ void MemoryManager::registerWeakSet(Heap::SetObject *set)
MemoryManager::~MemoryManager()
{
delete m_persistentValues;
-
dumpStats();
+ // do one last non-incremental sweep to clean up C++ objects
+ // first, abort any on-going incremental gc operation
+ setGCTimeLimit(-1);
+ if (engine->isGCOngoing) {
+ engine->isGCOngoing = false;
+ m_markStack.reset();
+ gcStateMachine->state = GCState::Invalid;
+ blockAllocator.resetBlackBits();
+ hugeItemAllocator.resetBlackBits();
+ icAllocator.resetBlackBits();
+ }
+ // then sweep
sweep(/*lastSweep*/true);
+
blockAllocator.freeAll();
hugeItemAllocator.freeAll();
icAllocator.freeAll();
@@ -1153,6 +1460,49 @@ void MemoryManager::collectFromJSStack(MarkStack *markStack) const
}
}
+GCStateMachine::GCStateMachine()
+{
+ // base assumption: target 60fps, use at most 1/3 of time for gc
+ timeLimit = std::chrono::milliseconds { (1000 / 60) / 3 };
+}
+
+void GCStateMachine::transition() {
+ if (timeLimit.count() > 0) {
+ deadline = QDeadlineTimer(timeLimit);
+ bool deadlineExpired = false;
+ while (!(deadlineExpired = deadline.hasExpired()) && state != GCState::Invalid) {
+ if (state > GCState::InitCallDestroyObjects) {
+ /* initCallDestroyObjects is the last action which drains the mark
+ stack by default. But as our write-barrier might end up putting
+ objects on the markStack which still reference other objects.
+ Especially when we call user code triggered by Component.onDestruction,
+ but also when we run into a timeout.
+ We don't redrain before InitCallDestroyObjects, as that would
+ potentially lead to useless busy-work (e.g., if the last referencs
+ to objects are removed while the mark phase is running)
+ */
+ redrain(this);
+ }
+ GCStateInfo& stateInfo = stateInfoMap[int(state)];
+ state = stateInfo.execute(this, stateData);
+ if (stateInfo.breakAfter)
+ break;
+ }
+ if (deadlineExpired)
+ handleTimeout(state);
+ if (state != GCState::Invalid)
+ QMetaObject::invokeMethod(mm->engine->publicEngine, [this]{
+ mm->onEventLoop();
+ }, Qt::QueuedConnection);
+ } else {
+ deadline = QDeadlineTimer::Forever;
+ while (state != GCState::Invalid) {
+ GCStateInfo& stateInfo = stateInfoMap[int(state)];
+ state = stateInfo.execute(this, stateData);
+ }
+ }
+}
+
} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index 378b36369d..ef0cd0c36c 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -28,6 +28,73 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+enum GCState {
+ MarkStart = 0,
+ MarkGlobalObject,
+ MarkJSStack,
+ InitMarkPersistentValues,
+ MarkPersistentValues,
+ InitMarkWeakValues,
+ MarkWeakValues,
+ MarkDrain,
+ MarkReady,
+ InitCallDestroyObjects,
+ CallDestroyObjects,
+ FreeWeakMaps,
+ FreeWeakSets,
+ HandleQObjectWrappers,
+ DoSweep,
+ Invalid,
+ Count,
+};
+
+struct GCData { virtual ~GCData(){};};
+
+struct GCIteratorStorage {
+ PersistentValueStorage::Iterator it{nullptr, 0};
+};
+struct GCStateMachine;
+
+struct GCStateInfo {
+ using ExtraData = std::variant<std::monostate, GCIteratorStorage>;
+ GCState (*execute)(GCStateMachine *, ExtraData &) = nullptr; // Function to execute for this state, returns true if ready to transition
+ bool breakAfter{false};
+};
+
+struct GCStateMachine {
+ using ExtraData = GCStateInfo::ExtraData;
+ GCState state{GCState::Invalid};
+ std::chrono::microseconds timeLimit{};
+ QDeadlineTimer deadline;
+ std::array<GCStateInfo, GCState::Count> stateInfoMap;
+ MemoryManager *mm = nullptr;
+ ExtraData stateData; // extra date for specific states
+
+ GCStateMachine();
+
+ inline void step() {
+ if (!inProgress()) {
+ reset();
+ }
+ transition();
+ }
+
+ inline bool inProgress() {
+ return state != GCState::Invalid;
+ }
+
+ inline void reset() {
+ state = GCState::MarkStart;
+ }
+
+ Q_QML_EXPORT void transition();
+
+ inline void handleTimeout(GCState state) {
+ Q_UNUSED(state);
+ }
+};
+
+
struct ChunkAllocator;
struct MemorySegment;
@@ -112,11 +179,22 @@ public:
MemoryManager(ExecutionEngine *engine);
~MemoryManager();
+ template <typename ToBeMarked>
+ friend struct GCCriticalSection;
+
// TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries).
// Note: all occurrences of "16" in alloc/dealloc are also due to the alignment.
constexpr static inline std::size_t align(std::size_t size)
{ return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
+ /* NOTE: allocManaged comes in various overloads. If size is not passed explicitly
+ sizeof(ManagedType::Data) is used for size. However, there are quite a few cases
+ where we allocate more than sizeof(ManagedType::Data); that's generally the case
+ when the Object has a ValueArray member.
+ If no internal class pointer is provided, ManagedType::defaultInternalClass(engine)
+ will be used as the internal class.
+ */
+
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, Heap::InternalClass *ic)
{
@@ -130,12 +208,24 @@ public:
}
template<typename ManagedType>
+ inline typename ManagedType::Data *allocManaged(Heap::InternalClass *ic)
+ {
+ return allocManaged<ManagedType>(sizeof(typename ManagedType::Data), ic);
+ }
+
+ template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
{
return allocManaged<ManagedType>(size, ic->d());
}
template<typename ManagedType>
+ inline typename ManagedType::Data *allocManaged(InternalClass *ic)
+ {
+ return allocManaged<ManagedType>(sizeof(typename ManagedType::Data), ic);
+ }
+
+ template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size)
{
Scope scope(engine);
@@ -143,6 +233,15 @@ public:
return allocManaged<ManagedType>(size, ic);
}
+ template<typename ManagedType>
+ inline typename ManagedType::Data *allocManaged()
+ {
+ auto constexpr size = sizeof(typename ManagedType::Data);
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
+ return allocManaged<ManagedType>(size, ic);
+ }
+
template <typename ObjectType>
typename ObjectType::Data *allocateObject(Heap::InternalClass *ic)
{
@@ -208,12 +307,14 @@ public:
typename ManagedType::Data *alloc(Args&&... args)
{
Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
+ Scoped<ManagedType> t(scope, allocManaged<ManagedType>());
t->d_unchecked()->init(std::forward<Args>(args)...);
return t->d();
}
void runGC();
+ bool tryForceGCCompletion();
+ void runFullGC();
void dumpStats() const;
@@ -225,6 +326,9 @@ public:
// and InternalClassDataPrivate<PropertyAttributes>.
void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; }
+ // called at the end of a gc cycle
+ void updateUnmanagedHeapSizeGCLimit();
+
template<typename ManagedType>
typename ManagedType::Data *allocIC()
{
@@ -235,6 +339,12 @@ public:
void registerWeakMap(Heap::MapObject *map);
void registerWeakSet(Heap::SetObject *set);
+ void onEventLoop();
+
+ //GC related methods
+ void setGCTimeLimit(int timeMs);
+ MarkStack* markStack() { return m_markStack.get(); }
+
protected:
/// expects size to be aligned
Heap::Base *allocString(std::size_t unmanagedSize);
@@ -246,33 +356,34 @@ private:
MinUnmanagedHeapSizeGCLimit = 128 * 1024
};
+public:
void collectFromJSStack(MarkStack *markStack) const;
- void mark();
void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr);
+ void cleanupDeletedQObjectWrappersInSweep();
+ bool isAboveUnmanagedHeapLimit()
+ {
+ const bool incrementalGCIsAlreadyRunning = m_markStack != nullptr;
+ const bool aboveUnmanagedHeapLimit = incrementalGCIsAlreadyRunning
+ ? unmanagedHeapSize > 3 * unmanagedHeapSizeGCLimit / 2
+ : unmanagedHeapSize > unmanagedHeapSizeGCLimit;
+ return aboveUnmanagedHeapLimit;
+ }
+private:
bool shouldRunGC() const;
- void collectRoots(MarkStack *markStack);
HeapItem *allocate(BlockAllocator *allocator, std::size_t size)
{
+ const bool incrementalGCIsAlreadyRunning = m_markStack != nullptr;
+
bool didGCRun = false;
if (aggressiveGC) {
- runGC();
+ runFullGC();
didGCRun = true;
}
- if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) {
+ if (isAboveUnmanagedHeapLimit()) {
if (!didGCRun)
- runGC();
-
- if (3*unmanagedHeapSizeGCLimit <= 4 * unmanagedHeapSize) {
- // more than 75% full, raise limit
- unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit,
- unmanagedHeapSize) * 2;
- } else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit) {
- // less than 25% full, lower limit
- unmanagedHeapSizeGCLimit = qMax(std::size_t(MinUnmanagedHeapSizeGCLimit),
- unmanagedHeapSizeGCLimit/2);
- }
+ incrementalGCIsAlreadyRunning ? (void) tryForceGCCompletion() : runGC();
didGCRun = true;
}
@@ -300,11 +411,15 @@ public:
Heap::MapObject *weakMaps = nullptr;
Heap::SetObject *weakSets = nullptr;
+ std::unique_ptr<GCStateMachine> gcStateMachine{nullptr};
+ std::unique_ptr<MarkStack> m_markStack{nullptr};
+
std::size_t unmanagedHeapSize = 0; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items.
std::size_t unmanagedHeapSizeGCLimit;
std::size_t usedSlotsAfterLastFullSweep = 0;
- bool gcBlocked = false;
+ enum Blockness : quint8 {Unblocked, NormalBlocked, InCriticalSection };
+ Blockness gcBlocked = Unblocked;
bool aggressiveGC = false;
bool gcStats = false;
bool gcCollectorStats = false;
@@ -320,6 +435,51 @@ public:
} statistics;
};
+/*!
+ \internal
+ GCCriticalSection prevets the gc from running, until it is destructed.
+ In its dtor, it runs a check whether we've reached the unmanaegd heap limit,
+ and triggers a gc run if necessary.
+ Lastly, it can optionally mark an object passed to it before runnig the gc.
+ */
+template <typename ToBeMarked = void>
+struct GCCriticalSection {
+ Q_DISABLE_COPY_MOVE(GCCriticalSection)
+
+ Q_NODISCARD_CTOR GCCriticalSection(QV4::ExecutionEngine *engine, ToBeMarked *toBeMarked = nullptr)
+ : m_engine(engine)
+ , m_oldState(std::exchange(engine->memoryManager->gcBlocked, MemoryManager::InCriticalSection))
+ , m_toBeMarked(toBeMarked)
+ {
+ // disallow nested critical sections
+ Q_ASSERT(m_oldState != MemoryManager::InCriticalSection);
+ }
+ ~GCCriticalSection()
+ {
+ m_engine->memoryManager->gcBlocked = m_oldState;
+ if (m_oldState != MemoryManager::Unblocked)
+ if constexpr (!std::is_same_v<ToBeMarked, void>)
+ if (m_toBeMarked)
+ m_toBeMarked->markObjects(m_engine->memoryManager->markStack());
+ /* because we blocked the gc, we might be using too much memoryon the unmanaged heap
+ and did not run the normal fixup logic. So recheck again, and trigger a gc run
+ if necessary*/
+ if (!m_engine->memoryManager->isAboveUnmanagedHeapLimit())
+ return;
+ if (!m_engine->isGCOngoing) {
+ m_engine->memoryManager->runGC();
+ } else {
+ [[maybe_unused]] bool gcFinished = m_engine->memoryManager->tryForceGCCompletion();
+ Q_ASSERT(gcFinished);
+ }
+ }
+
+private:
+ QV4::ExecutionEngine *m_engine;
+ MemoryManager::Blockness m_oldState;
+ ToBeMarked *m_toBeMarked;
+};
+
}
QT_END_NAMESPACE
diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h
index a7fc7bb7cb..b77b615e6c 100644
--- a/src/qml/memory/qv4mmdefs_p.h
+++ b/src/qml/memory/qv4mmdefs_p.h
@@ -21,6 +21,8 @@
QT_BEGIN_NAMESPACE
+class QDeadlineTimer;
+
namespace QV4 {
struct MarkStack;
@@ -227,9 +229,9 @@ Q_STATIC_ASSERT(sizeof(HeapItem) == Chunk::SlotSize);
Q_STATIC_ASSERT(QT_POINTER_SIZE*8 == Chunk::Bits);
Q_STATIC_ASSERT((1 << Chunk::BitShift) == Chunk::Bits);
-struct Q_QML_PRIVATE_EXPORT MarkStack {
+struct Q_QML_EXPORT MarkStack {
MarkStack(ExecutionEngine *engine);
- ~MarkStack() { drain(); }
+ ~MarkStack() { /* we drain manually */ }
void push(Heap::Base *m) {
*(m_top++) = m;
@@ -250,17 +252,28 @@ struct Q_QML_PRIVATE_EXPORT MarkStack {
}
}
+ bool isEmpty() const { return m_top == m_base; }
+
+ qptrdiff remainingBeforeSoftLimit() const
+ {
+ return m_softLimit - m_top;
+ }
+
ExecutionEngine *engine() const { return m_engine; }
+ void drain();
+ enum class DrainState { Ongoing, Complete };
+ DrainState drain(QDeadlineTimer deadline);
private:
Heap::Base *pop() { return *(--m_top); }
- void drain();
Heap::Base **m_top = nullptr;
Heap::Base **m_base = nullptr;
Heap::Base **m_softLimit = nullptr;
Heap::Base **m_hardLimit = nullptr;
+
ExecutionEngine *m_engine = nullptr;
+
quintptr m_drainRecursion = 0;
};
diff --git a/src/qml/memory/qv4stacklimits.cpp b/src/qml/memory/qv4stacklimits.cpp
index 663ece09a2..288a6fd347 100644
--- a/src/qml/memory/qv4stacklimits.cpp
+++ b/src/qml/memory/qv4stacklimits.cpp
@@ -31,6 +31,8 @@
# include <unistd.h>
#elif defined(Q_OS_INTEGRITY)
# include <INTEGRITY.h>
+#elif defined(Q_OS_VXWORKS)
+# include <taskLib.h>
#elif defined(Q_OS_WASM)
# include <emscripten/stack.h>
#endif
@@ -147,12 +149,19 @@ StackProperties stackProperties()
static_assert(Q_STACK_GROWTH_DIRECTION < 0);
StackProperties stackProperties()
{
+ // MinGW complains about out of bounds array access in compiler headers
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_GCC("-Warray-bounds")
+
// Get the stack base.
# ifdef _WIN64
PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
# else
PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
# endif
+
+ QT_WARNING_POP
+
quint8 *stackBase = reinterpret_cast<quint8 *>(pTib->StackBase);
// Get the stack limit. tib->StackLimit is the size of the
@@ -226,6 +235,15 @@ StackProperties stackProperties()
return createStackProperties(reinterpret_cast<void *>(base), size);
}
+#elif defined(Q_OS_VXWORKS)
+
+StackProperties stackProperties()
+{
+ TASK_DESC taskDescription;
+ taskInfoGet(taskIdSelf(), &taskDescription);
+ return createStackProperties(taskDescription.td_pStackBase, taskDescription.td_stackSize);
+}
+
#else
StackProperties stackPropertiesGeneric(qsizetype stackSize = 0)
@@ -234,8 +252,8 @@ StackProperties stackPropertiesGeneric(qsizetype stackSize = 0)
pthread_t thread = pthread_self();
pthread_attr_t sattr;
- pthread_attr_init(&sattr);
# if defined(PTHREAD_NP_H) || defined(_PTHREAD_NP_H_) || defined(Q_OS_NETBSD)
+ pthread_attr_init(&sattr);
pthread_attr_get_np(thread, &sattr);
# else
pthread_getattr_np(thread, &sattr);
diff --git a/src/qml/memory/qv4writebarrier.cpp b/src/qml/memory/qv4writebarrier.cpp
new file mode 100644
index 0000000000..d7e56212ca
--- /dev/null
+++ b/src/qml/memory/qv4writebarrier.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include <private/qv4value_p.h>
+#include <private/qv4mm_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+ void markHeapBase(QV4::MarkStack* markStack, QV4::Heap::Base *base){
+ if (!base)
+ return;
+ base->mark(markStack);
+ }
+}
+namespace QV4 {
+
+void WriteBarrier::write_slowpath(EngineBase *engine, Heap::Base *base, ReturnedValue *slot, ReturnedValue value)
+{
+ Q_UNUSED(base);
+ Q_UNUSED(slot);
+ MarkStack * markStack = engine->memoryManager->markStack();
+ if constexpr (isInsertionBarrier)
+ markHeapBase(markStack, Value::fromReturnedValue(value).heapObject());
+}
+
+void WriteBarrier::write_slowpath(EngineBase *engine, Heap::Base *base, Heap::Base **slot, Heap::Base *value)
+{
+ Q_UNUSED(base);
+ Q_UNUSED(slot);
+ MarkStack * markStack = engine->memoryManager->markStack();
+ if constexpr (isInsertionBarrier)
+ markHeapBase(markStack, value);
+}
+
+}
+QT_END_NAMESPACE
diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h
index 813d360841..ddee183982 100644
--- a/src/qml/memory/qv4writebarrier_p.h
+++ b/src/qml/memory/qv4writebarrier_p.h
@@ -15,57 +15,115 @@
//
#include <private/qv4global_p.h>
+#include <private/qv4enginebase_p.h>
QT_BEGIN_NAMESPACE
-#define WRITEBARRIER_none 1
-
-#define WRITEBARRIER(x) (1/WRITEBARRIER_##x == 1)
-
namespace QV4 {
struct EngineBase;
-
-namespace WriteBarrier {
-
-enum Type {
- NoBarrier,
- Barrier
+typedef quint64 ReturnedValue;
+
+struct WriteBarrier {
+
+ static constexpr bool isInsertionBarrier = true;
+
+ Q_ALWAYS_INLINE static void write(EngineBase *engine, Heap::Base *base, ReturnedValue *slot, ReturnedValue value)
+ {
+ if (engine->isGCOngoing)
+ write_slowpath(engine, base, slot, value);
+ *slot = value;
+ }
+ Q_QML_EXPORT Q_NEVER_INLINE static void write_slowpath(
+ EngineBase *engine, Heap::Base *base,
+ ReturnedValue *slot, ReturnedValue value);
+
+ Q_ALWAYS_INLINE static void write(EngineBase *engine, Heap::Base *base, Heap::Base **slot, Heap::Base *value)
+ {
+ if (engine->isGCOngoing)
+ write_slowpath(engine, base, slot, value);
+ *slot = value;
+ }
+ Q_QML_EXPORT Q_NEVER_INLINE static void write_slowpath(
+ EngineBase *engine, Heap::Base *base,
+ Heap::Base **slot, Heap::Base *value);
+
+ // MemoryManager isn't a complete type here, so make Engine a template argument
+ // so that we can still call engine->memoryManager->markStack()
+ template<typename F, typename Engine = EngineBase>
+ static void markCustom(Engine *engine, F &&markFunction) {
+ if (engine->isGCOngoing)
+ (std::forward<F>(markFunction))(engine->memoryManager->markStack());
+ }
+
+ // HeapObjectWrapper(Base) are helper classes to ensure that
+ // we always use a WriteBarrier when setting heap-objects
+ // they are also trivial; if triviality is not required, use Pointer instead
+ struct HeapObjectWrapperBase
+ {
+ // enum class avoids accidental construction via brace-init
+ enum class PointerWrapper : quintptr {};
+ PointerWrapper wrapped;
+
+ void clear() { wrapped = PointerWrapper(quintptr(0)); }
+ };
+
+ template<typename HeapType>
+ struct HeapObjectWrapperCommon : HeapObjectWrapperBase
+ {
+ HeapType *get() const { return reinterpret_cast<HeapType *>(wrapped); }
+ operator HeapType *() const { return get(); }
+ HeapType * operator->() const { return get(); }
+
+ template <typename ConvertibleToHeapType>
+ void set(QV4::EngineBase *engine, ConvertibleToHeapType *heapObject)
+ {
+ WriteBarrier::markCustom(engine, [heapObject](QV4::MarkStack *ms){
+ if (heapObject)
+ heapObject->mark(ms);
+ });
+ wrapped = static_cast<HeapObjectWrapperBase::PointerWrapper>(quintptr(heapObject));
+ }
+ };
+
+ // all types are trivial; we however want to block copies bypassing the write barrier
+ // therefore, all members use a PhantomTag to reduce the likelihood
+ template<typename HeapType, int PhantomTag>
+ struct HeapObjectWrapper : HeapObjectWrapperCommon<HeapType> {};
+
+ /* similar Heap::Pointer, but without the Base conversion (and its inUse assert)
+ and for storing references in engine classes stored on the native heap
+ Stores a "non-owning" reference to a heap-item (in the C++ sense), but should
+ generally mark the heap-item; therefore set goes through a write-barrier
+ */
+ template<typename T>
+ struct Pointer
+ {
+ Pointer() = default;
+ ~Pointer() = default;
+ Q_DISABLE_COPY_MOVE(Pointer)
+ T* operator->() const { return get(); }
+ operator T* () const { return get(); }
+
+ void set(EngineBase *e, T *newVal) {
+ WriteBarrier::markCustom(e, [newVal](QV4::MarkStack *ms) {
+ if (newVal)
+ newVal->mark(ms);
+ });
+ ptr = newVal;
+ }
+
+ T* get() const { return ptr; }
+
+
+
+ private:
+ T *ptr = nullptr;
+ };
};
-enum NewValueType {
- Primitive,
- Object,
- Unknown
-};
-
-// ### this needs to be filled with a real memory fence once marking is concurrent
+ // ### this needs to be filled with a real memory fence once marking is concurrent
Q_ALWAYS_INLINE void fence() {}
-#if WRITEBARRIER(none)
-
-template <NewValueType type>
-static constexpr inline bool isRequired() {
- return false;
-}
-
-inline void write(EngineBase *engine, Heap::Base *base, ReturnedValue *slot, ReturnedValue value)
-{
- Q_UNUSED(engine);
- Q_UNUSED(base);
- *slot = value;
-}
-
-inline void write(EngineBase *engine, Heap::Base *base, Heap::Base **slot, Heap::Base *value)
-{
- Q_UNUSED(engine);
- Q_UNUSED(base);
- *slot = value;
-}
-
-#endif
-
-}
-
}
QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index 394edf0fed..32b609f5ff 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -212,11 +212,11 @@ public:
AST::ExportClause *ExportClause;
AST::ExportDeclaration *ExportDeclaration;
AST::TypeAnnotation *TypeAnnotation;
- AST::TypeArgument *TypeArgument;
AST::Type *Type;
AST::UiProgram *UiProgram;
AST::UiHeaderItemList *UiHeaderItemList;
+ AST::UiPragmaValueList *UiPragmaValueList;
AST::UiPragma *UiPragma;
AST::UiImport *UiImport;
AST::UiParameterList *UiParameterList;
@@ -302,6 +302,18 @@ public:
inline int errorColumnNumber() const
{ return diagnosticMessage().loc.startColumn; }
+ inline bool identifierInsertionEnabled() const
+ { return m_identifierInsertionEnabled; }
+
+ inline void setIdentifierInsertionEnabled(bool enable)
+ { m_identifierInsertionEnabled = enable; }
+
+ inline bool incompleteBindingsEnabled() const
+ { return m_incompleteBindingsEnabled; }
+
+ inline void setIncompleteBindingsEnabled(bool enable)
+ { m_incompleteBindingsEnabled = enable; }
+
protected:
bool parse(int startToken);
@@ -322,6 +334,7 @@ protected:
AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
void pushToken(int token);
+ void pushTokenWithEmptyLocation(int token);
int lookaheadToken(Lexer *lexer);
static DiagnosticMessage compileError(const SourceLocation &location,
@@ -373,6 +386,7 @@ protected:
QStringView yytokenraw;
SourceLocation yylloc;
SourceLocation yyprevlloc;
+ int yyprevtoken = -1;
SavedToken token_buffer[TOKEN_BUFFER_SIZE];
SavedToken *first_token = nullptr;
@@ -390,6 +404,8 @@ protected:
CoverExpressionType coverExpressionType = CE_Invalid;
QList<DiagnosticMessage> diagnostic_messages;
+ bool m_identifierInsertionEnabled = false;
+ bool m_incompleteBindingsEnabled = false;
};
} // end of namespace QQmlJS
@@ -465,11 +481,13 @@ AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
{
QVarLengthArray<QStringView, 4> nameIds;
QVarLengthArray<SourceLocation, 4> locations;
+ QVarLengthArray<SourceLocation, 4> dotLocations;
AST::ExpressionNode *it = expr;
while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) {
nameIds.append(m->name);
locations.append(m->identifierToken);
+ dotLocations.append(m->dotToken);
it = m->base;
}
@@ -481,6 +499,7 @@ AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
for (int i = nameIds.size() - 1; i != -1; --i) {
currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]);
currentId->identifierToken = locations[i];
+ currentId->dotToken = dotLocations[i];
}
return currentId->finish();
@@ -502,9 +521,19 @@ void Parser::pushToken(int token)
yytoken = token;
}
+void Parser::pushTokenWithEmptyLocation(int token)
+{
+ pushToken(token);
+ yylloc = yyprevlloc;
+ yylloc.offset += yylloc.length;
+ yylloc.startColumn += yylloc.length;
+ yylloc.length = 0;
+}
+
int Parser::lookaheadToken(Lexer *lexer)
{
if (yytoken < 0) {
+ yyprevtoken = yytoken;
yytoken = lexer->lex();
yylval = lexer->tokenValue();
yytokenspell = lexer->tokenSpell();
@@ -600,6 +629,7 @@ bool Parser::parse(int startToken)
#endif
if (action > 0) {
if (action != ACCEPT_STATE) {
+ yyprevtoken = yytoken;
yytoken = -1;
sym(1).dval = yylval;
stringRef(1) = yytokenspell;
@@ -717,25 +747,51 @@ UiHeaderItemList: UiHeaderItemList UiImport;
./
PragmaId: JsIdentifier;
+PragmaValue: JsIdentifier
+ | T_STRING_LITERAL;
Semicolon: T_AUTOMATIC_SEMICOLON;
Semicolon: T_SEMICOLON;
+UiPragmaValueList: PragmaValue;
+/.
+ case $rule_number: {
+ AST::UiPragmaValueList *list
+ = new (pool) AST::UiPragmaValueList(stringRef(1));
+ list->location = loc(1);
+ sym(1).Node = list;
+ } break;
+./
+
+UiPragmaValueList: UiPragmaValueList T_COMMA PragmaValue;
+/.
+ case $rule_number: {
+ AST::UiPragmaValueList *list
+ = new (pool) AST::UiPragmaValueList(sym(1).UiPragmaValueList, stringRef(3));
+ list->location = loc(3);
+ sym(1).Node = list;
+ } break;
+./
+
UiPragma: T_PRAGMA PragmaId Semicolon;
/.
case $rule_number: {
AST::UiPragma *pragma = new (pool) AST::UiPragma(stringRef(2));
pragma->pragmaToken = loc(1);
+ pragma->pragmaIdToken = loc(2);
pragma->semicolonToken = loc(3);
sym(1).Node = pragma;
} break;
./
-UiPragma: T_PRAGMA PragmaId T_COLON JsIdentifier Semicolon;
+UiPragma: T_PRAGMA PragmaId T_COLON UiPragmaValueList Semicolon;
/.
case $rule_number: {
- AST::UiPragma *pragma = new (pool) AST::UiPragma(stringRef(2), stringRef(4));
+ AST::UiPragma *pragma = new (pool) AST::UiPragma(
+ stringRef(2), sym(4).UiPragmaValueList->finish());
pragma->pragmaToken = loc(1);
+ pragma->pragmaIdToken = loc(2);
+ pragma->colonToken = loc(3);
pragma->semicolonToken = loc(5);
sym(1).Node = pragma;
} break;
@@ -1095,6 +1151,20 @@ case $rule_number:
} break;
./
+UiObjectMember: UiQualifiedId Semicolon;
+/.
+ case $rule_number: {
+ if (!m_incompleteBindingsEnabled) {
+ diagnostic_messages.append(compileError(loc(1), QLatin1String("Incomplete binding, expected token `:` or `{`")));
+ return false;
+ }
+ AST::EmptyStatement *statement = new (pool) AST::EmptyStatement;
+ statement->semicolonToken = loc(2);
+ AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(sym(1).UiQualifiedId, statement);
+ sym(1).Node = node;
+ } break;
+./
+
UiPropertyType: T_VAR;
/. case $rule_number: Q_FALLTHROUGH(); ./
UiPropertyType: T_RESERVED_WORD;
@@ -1359,6 +1429,7 @@ UiObjectMemberWithArray: UiPropertyAttributes T_IDENTIFIER T_LT UiPropertyType T
node->typeToken = loc(4);
node->identifierToken = loc(6);
node->semicolonToken = loc(7); // insert a fake ';' before ':'
+ node->colonToken = loc(7);
AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
propertyName->identifierToken = loc(6);
@@ -1388,6 +1459,7 @@ UiObjectMemberExpressionStatementLookahead: UiPropertyAttributes UiPropertyType
node->typeToken = loc(2);
node->identifierToken = loc(3);
node->semicolonToken = loc(4); // insert a fake ';' before ':'
+ node->colonToken = loc(4);
AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
propertyName->identifierToken = loc(3);
@@ -1472,6 +1544,7 @@ UiObjectMember: T_COMPONENT T_IDENTIFIER T_COLON UiObjectDefinition;
}
auto inlineComponent = new (pool) AST::UiInlineComponent(stringRef(2), sym(4).UiObjectDefinition);
inlineComponent->componentToken = loc(1);
+ inlineComponent->identifierToken = loc(2);
sym(1).Node = inlineComponent;
} break;
./
@@ -1983,7 +2056,6 @@ PropertyDefinition: IdentifierReference;
AST::IdentifierExpression *expr = new (pool) AST::IdentifierExpression(stringRef(1));
expr->identifierToken = loc(1);
AST::PatternProperty *node = new (pool) AST::PatternProperty(name, expr);
- node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
@@ -2006,7 +2078,6 @@ CoverInitializedName: IdentifierReference Initializer_In;
AST::BinaryExpression *assignment = new (pool) AST::BinaryExpression(left, QSOperator::Assign, sym(2).Expression);
assignment->operatorToken = loc(2);
AST::PatternProperty *node = new (pool) AST::PatternProperty(name, assignment);
- node->colonToken = loc(1);
sym(1).Node = node;
} break;
@@ -2119,7 +2190,9 @@ Initializer: T_EQ AssignmentExpression;
Initializer_In: T_EQ AssignmentExpression_In;
/.
case $rule_number: {
- sym(1) = sym(2);
+ auto node = new (pool) AST::InitializerExpression(sym(2).Expression);
+ node->equalToken = loc(1);
+ sym(1).Expression = node;
} break;
./
@@ -3309,14 +3382,15 @@ BindingElisionElement: ElisionOpt BindingElement;
BindingProperty: BindingIdentifier InitializerOpt_In;
/.
case $rule_number: {
- AST::StringLiteralPropertyName *name = new (pool) AST::StringLiteralPropertyName(stringRef(1));
+ AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1));
name->propertyNameToken = loc(1);
// if initializer is an anonymous function expression, we need to assign identifierref as it's name
if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
f->name = stringRef(1);
if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
c->name = stringRef(1);
- sym(1).Node = new (pool) AST::PatternProperty(name, stringRef(1), sym(2).Expression);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(name, stringRef(1), sym(2).Expression);
+ sym(1).Node = node;
} break;
./
@@ -3324,6 +3398,8 @@ BindingProperty: PropertyName T_COLON BindingIdentifier InitializerOpt_In;
/.
case $rule_number: {
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, stringRef(3), sym(4).Expression);
+ node->colonToken = loc(2);
+ node->identifierToken = loc(3);
sym(1).Node = node;
} break;
./
@@ -3332,6 +3408,7 @@ BindingProperty: PropertyName T_COLON BindingPattern InitializerOpt_In;
/.
case $rule_number: {
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Pattern, sym(4).Expression);
+ node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
@@ -3491,6 +3568,11 @@ IterationStatement: T_FOR T_LPAREN LexicalDeclaration T_SEMICOLON ExpressionOpt_
AST::ForStatement *node = new (pool) AST::ForStatement(
static_cast<AST::VariableStatement *>(sym(3).Node)->declarations, sym(5).Expression,
sym(7).Expression, sym(9).Statement);
+ if (node->declarations) {
+ AST::PatternElement *pe = node->declarations->declaration;
+ pe->isForDeclaration = true;
+ pe->declarationKindToken = loc(3);
+ }
node->forToken = loc(1);
node->lparenToken = loc(2);
node->firstSemicolonToken = loc(4);
@@ -3562,6 +3644,7 @@ ForDeclaration: Var BindingIdentifier TypeAnnotationOpt;
node->identifierToken = loc(2);
node->scope = sym(1).scope;
node->isForDeclaration = true;
+ node->declarationKindToken = loc(1);
sym(1).Node = node;
} break;
./
@@ -3574,6 +3657,7 @@ ForDeclaration: Var BindingPattern;
auto *node = new (pool) AST::PatternElement(sym(2).Pattern, nullptr);
node->scope = sym(1).scope;
node->isForDeclaration = true;
+ node->declarationKindToken = loc(1);
sym(1).Node = node;
} break;
./
@@ -4090,7 +4174,6 @@ MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_R
f->rbraceToken = loc(9);
f->isGenerator = true;
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Method);
- node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
@@ -4108,7 +4191,6 @@ MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN TypeAnnotationOpt Functio
f->lbraceToken = loc(6);
f->rbraceToken = loc(8);
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Getter);
- node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
@@ -4125,7 +4207,6 @@ MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN
f->lbraceToken = loc(7);
f->rbraceToken = loc(9);
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Setter);
- node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
@@ -4755,35 +4836,26 @@ ExportSpecifier: IdentifierName T_AS IdentifierName;
if (first_token == last_token) {
const int errorState = state_stack[tos];
+ // automatic insertion of missing identifiers after dots
+ if (yytoken != -1 && m_identifierInsertionEnabled && t_action(errorState, T_IDENTIFIER) && yyprevtoken == T_DOT) {
+#ifdef PARSER_DEBUG
+ qDebug() << "Inserting missing identifier between" << spell[yyprevtoken] << "and"
+ << spell[yytoken];
+#endif
+ pushTokenWithEmptyLocation(T_IDENTIFIER);
+ action = errorState;
+ goto _Lcheck_token;
+ }
+
+
// automatic insertion of `;'
if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken))
|| t_action(errorState, T_COMPATIBILITY_SEMICOLON))) {
#ifdef PARSER_DEBUG
qDebug() << "Inserting automatic semicolon.";
#endif
- SavedToken &tk = token_buffer[0];
- tk.token = yytoken;
- tk.dval = yylval;
- tk.spell = yytokenspell;
- tk.raw = yytokenraw;
- tk.loc = yylloc;
-
- yylloc = yyprevlloc;
- yylloc.offset += yylloc.length;
- yylloc.startColumn += yylloc.length;
- yylloc.length = 0;
-
- //const QString msg = QCoreApplication::translate("QQmlParser", "Missing `;'");
- //diagnostic_messages.append(compileError(yyloc, msg, QtWarningMsg));
-
- first_token = &token_buffer[0];
- last_token = &token_buffer[1];
-
- yytoken = T_SEMICOLON;
- yylval = 0;
-
+ pushTokenWithEmptyLocation(T_SEMICOLON);
action = errorState;
-
goto _Lcheck_token;
}
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 5408f29900..8f13ad193e 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -3,6 +3,7 @@
#include <QString>
#include <QLocale>
+#include "common/qqmljssourcelocation_p.h"
#include "qqmljsast_p.h"
#include "qqmljsastvisitor_p.h"
@@ -1015,13 +1016,9 @@ BoundNames FormalParameterList::formals() const
// change the name of the earlier argument to enforce the lookup semantics from the spec
formals[duplicateIndex].id += QLatin1String("#") + QString::number(i);
}
- formals += {
- name,
- it->element->typeAnnotation,
- it->element->isInjectedSignalParameter
- ? BoundName::Injected
- : BoundName::Declared
- };
+ formals += { name, it->element->firstSourceLocation(), it->element->typeAnnotation,
+ it->element->isInjectedSignalParameter ? BoundName::Injected
+ : BoundName::Declared };
}
++i;
}
@@ -1049,17 +1046,10 @@ void FormalParameterList::accept0(BaseVisitor *visitor)
}
}
-FormalParameterList *FormalParameterList::finish(QQmlJS::MemoryPool *pool)
+FormalParameterList *FormalParameterList::finish(QQmlJS::MemoryPool *)
{
FormalParameterList *front = next;
next = nullptr;
-
- int i = 0;
- for (const FormalParameterList *it = this; it; it = it->next) {
- if (it->element && it->element->bindingIdentifier.isEmpty())
- it->element->bindingIdentifier = pool->newString(QLatin1String("arg#") + QString::number(i));
- ++i;
- }
return front;
}
@@ -1168,8 +1158,8 @@ void ExportClause::accept0(BaseVisitor *visitor)
void ExportDeclaration::accept0(BaseVisitor *visitor)
{
if (visitor->visit(this)) {
- accept(fromClause, visitor);
accept(exportClause, visitor);
+ accept(fromClause, visitor);
accept(variableStatementOrDeclaration, visitor);
}
@@ -1336,6 +1326,15 @@ void UiImport::accept0(BaseVisitor *visitor)
visitor->endVisit(this);
}
+void UiPragmaValueList::accept0(BaseVisitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+
void UiPragma::accept0(BaseVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -1395,6 +1394,11 @@ void TaggedTemplate::accept0(BaseVisitor *visitor)
visitor->endVisit(this);
}
+void InitializerExpression::accept0(BaseVisitor *visitor)
+{
+ expression->accept0(visitor);
+}
+
void PatternElement::accept0(BaseVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -1414,8 +1418,8 @@ void PatternElement::boundNames(BoundNames *names)
else if (PatternPropertyList *p = propertyList())
p->boundNames(names);
} else {
- names->append({bindingIdentifier.toString(), typeAnnotation,
- isInjectedSignalParameter ? BoundName::Injected : BoundName::Declared});
+ names->append({ bindingIdentifier.toString(), firstSourceLocation(), typeAnnotation,
+ isInjectedSignalParameter ? BoundName::Injected : BoundName::Declared });
}
}
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 77af38aaca..bfeab7518c 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -149,6 +149,7 @@ public:
Kind_ClassDeclaration,
Kind_IdentifierExpression,
Kind_IdentifierPropertyName,
+ Kind_InitializerExpression,
Kind_ComputedPropertyName,
Kind_IfStatement,
Kind_LabelledStatement,
@@ -222,6 +223,7 @@ public:
Kind_UiObjectInitializer,
Kind_UiObjectMemberList,
Kind_UiArrayMemberList,
+ Kind_UiPragmaValueList,
Kind_UiPragma,
Kind_UiProgram,
Kind_UiParameterList,
@@ -327,7 +329,11 @@ public:
{ return identifierToken; }
SourceLocation lastSourceLocation() const override
- { return lastListElement(this)->identifierToken; }
+ {
+ return lastListElement(this)->lastOwnSourceLocation();
+ }
+
+ SourceLocation lastOwnSourceLocation() const { return identifierToken; }
QString toString() const
{
@@ -349,6 +355,7 @@ public:
UiQualifiedId *next;
QStringView name;
SourceLocation identifierToken;
+ SourceLocation dotToken;
};
class QML_PARSER_EXPORT Type: public Node
@@ -839,9 +846,11 @@ struct QML_PARSER_EXPORT BoundName
};
QString id;
+ QQmlJS::SourceLocation location;
QTaggedPointer<TypeAnnotation, Type> typeAnnotation;
- BoundName(const QString &id, TypeAnnotation *typeAnnotation, Type type = Declared)
- : id(id), typeAnnotation(typeAnnotation, type)
+ BoundName(const QString &id, const QQmlJS::SourceLocation &location,
+ TypeAnnotation *typeAnnotation, Type type = Declared)
+ : id(id), location(location), typeAnnotation(typeAnnotation, type)
{}
BoundName() = default;
@@ -865,6 +874,39 @@ struct BoundNames : public QVector<BoundName>
}
};
+/*!
+\internal
+This class is needed to pass the information about the equalToken in the parser, and is only needed
+during AST construction. It behaves exactly like the expression it contains: that avoids changing
+all the usages in qqmljs.g from ExpressionNode to InitializerExpression for every rule expecting a
+InitializerOpt_In or InitializerOpt.
+*/
+class QML_PARSER_EXPORT InitializerExpression : public ExpressionNode
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(InitializerExpression)
+
+ InitializerExpression(ExpressionNode *e) : expression(e) { kind = K; }
+
+ void accept0(BaseVisitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return equalToken; }
+
+ SourceLocation lastSourceLocation() const override { return expression->lastSourceLocation(); }
+
+ FunctionExpression *asFunctionDefinition() override
+ {
+ return expression->asFunctionDefinition();
+ }
+
+ ClassExpression *asClassDefinition() override { return expression->asClassDefinition(); }
+
+ // attributes
+ ExpressionNode *expression;
+ SourceLocation equalToken;
+};
+
class QML_PARSER_EXPORT PatternElement : public Node
{
public:
@@ -885,9 +927,28 @@ public:
Binding,
};
+private:
+ /*!
+ \internal
+ Hide InitializerExpression from the AST. InitializerExpression is only needed during parsing for
+ the AST construction, and it is not possible for the parser to directly embed the location of
+ equal tokens inside the PatternElement without the InitializerExpression.
+ */
+ void unwrapInitializer()
+ {
+ if (auto unwrapped = AST::cast<InitializerExpression *>(initializer)) {
+ equalToken = unwrapped->equalToken;
+ initializer = unwrapped->expression;
+ }
+ }
+public:
+
PatternElement(ExpressionNode *i = nullptr, Type t = Literal)
: initializer(i), type(t)
- { kind = K; }
+ {
+ kind = K;
+ unwrapInitializer();
+ }
PatternElement(QStringView n, TypeAnnotation *typeAnnotation = nullptr, ExpressionNode *i = nullptr, Type t = Binding)
: bindingIdentifier(n), initializer(i), type(t)
@@ -895,6 +956,7 @@ public:
{
Q_ASSERT(t >= RestElement);
kind = K;
+ unwrapInitializer();
}
PatternElement(Pattern *pattern, ExpressionNode *i = nullptr, Type t = Binding)
@@ -902,6 +964,7 @@ public:
{
Q_ASSERT(t >= RestElement);
kind = K;
+ unwrapInitializer();
}
void accept0(BaseVisitor *visitor) override;
@@ -925,12 +988,14 @@ public:
// attributes
SourceLocation identifierToken;
+ SourceLocation equalToken;
QStringView bindingIdentifier;
ExpressionNode *bindingTarget = nullptr;
ExpressionNode *initializer = nullptr;
Type type = Literal;
TypeAnnotation *typeAnnotation = nullptr;
// when used in a VariableDeclarationList
+ SourceLocation declarationKindToken;
VariableScope scope = VariableScope::NoScope;
bool isForDeclaration = false;
bool isInjectedSignalParameter = false;
@@ -3074,13 +3139,53 @@ public:
UiObjectMember *member;
};
+class QML_PARSER_EXPORT UiPragmaValueList: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(UiPragmaValueList)
+
+ UiPragmaValueList(QStringView value)
+ : value(value)
+ , next(this)
+ {
+ kind = K;
+ }
+
+ UiPragmaValueList(UiPragmaValueList *previous, QStringView value)
+ : value(value)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ void accept0(BaseVisitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return location; }
+
+ SourceLocation lastSourceLocation() const override
+ { return lastListElement(this)->location; }
+
+ UiPragmaValueList *finish()
+ {
+ UiPragmaValueList *head = next;
+ next = nullptr;
+ return head;
+ }
+
+ QStringView value;
+ UiPragmaValueList *next;
+ SourceLocation location;
+};
+
class QML_PARSER_EXPORT UiPragma: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(UiPragma)
- UiPragma(QStringView name, QStringView value = {})
- : name(name), value(value)
+ UiPragma(QStringView name, UiPragmaValueList *values = nullptr)
+ : name(name), values(values)
{ kind = K; }
void accept0(BaseVisitor *visitor) override;
@@ -3093,8 +3198,10 @@ public:
// attributes
QStringView name;
- QStringView value;
+ UiPragmaValueList *values;
SourceLocation pragmaToken;
+ SourceLocation pragmaIdToken;
+ SourceLocation colonToken;
SourceLocation semicolonToken;
};
@@ -3289,7 +3396,12 @@ public:
SourceLocation lastSourceLocation() const override
{
auto last = lastListElement(this);
- return (last->colonToken.isValid() ? last->propertyTypeToken : last->identifierToken);
+ return last->lastOwnSourceLocation();
+ }
+
+ SourceLocation lastOwnSourceLocation() const
+ {
+ return (colonToken.isValid() ? propertyTypeToken : identifierToken);
}
inline UiParameterList *finish ()
@@ -3486,6 +3598,7 @@ public:
QStringView name;
UiObjectDefinition* component;
SourceLocation componentToken;
+ SourceLocation identifierToken;
};
class QML_PARSER_EXPORT UiSourceElement: public UiObjectMember
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index e06da8b4c1..c87f67c67e 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -122,12 +122,12 @@ class NestedExpression;
class ClassExpression;
class ClassDeclaration;
class ClassElementList;
-class TypeArgument;
class Type;
class TypeAnnotation;
// ui elements
class UiProgram;
+class UiPragmaValueList;
class UiPragma;
class UiImport;
class UiPublicMember;
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index 450cfaad40..540d21d725 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -20,6 +20,129 @@
QT_BEGIN_NAMESPACE
+// as done in https://en.wikipedia.org/wiki/X_Macro
+
+#define QQmlJSASTUiClassListToVisit \
+ X(UiProgram) \
+ X(UiHeaderItemList) \
+ X(UiPragmaValueList) \
+ X(UiPragma) \
+ X(UiImport) \
+ X(UiPublicMember) \
+ X(UiSourceElement) \
+ X(UiObjectDefinition) \
+ X(UiObjectInitializer) \
+ X(UiObjectBinding) \
+ X(UiScriptBinding) \
+ X(UiArrayBinding) \
+ X(UiParameterList) \
+ X(UiObjectMemberList) \
+ X(UiArrayMemberList) \
+ X(UiQualifiedId) \
+ X(UiEnumDeclaration) \
+ X(UiEnumMemberList) \
+ X(UiVersionSpecifier) \
+ X(UiInlineComponent) \
+ X(UiAnnotation) \
+ X(UiAnnotationList) \
+ X(UiRequired)
+
+#define QQmlJSASTQQmlJSClassListToVisit \
+ X(TypeExpression) \
+ X(ThisExpression) \
+ X(IdentifierExpression) \
+ X(NullExpression) \
+ X(TrueLiteral) \
+ X(FalseLiteral) \
+ X(SuperLiteral) \
+ X(StringLiteral) \
+ X(TemplateLiteral) \
+ X(NumericLiteral) \
+ X(RegExpLiteral) \
+ X(ArrayPattern) \
+ X(ObjectPattern) \
+ X(PatternElementList) \
+ X(PatternPropertyList) \
+ X(PatternElement) \
+ X(PatternProperty) \
+ X(Elision) \
+ X(NestedExpression) \
+ X(IdentifierPropertyName) \
+ X(StringLiteralPropertyName) \
+ X(NumericLiteralPropertyName) \
+ X(ComputedPropertyName) \
+ X(ArrayMemberExpression) \
+ X(FieldMemberExpression) \
+ X(TaggedTemplate) \
+ X(NewMemberExpression) \
+ X(NewExpression) \
+ X(CallExpression) \
+ X(ArgumentList) \
+ X(PostIncrementExpression) \
+ X(PostDecrementExpression) \
+ X(DeleteExpression) \
+ X(VoidExpression) \
+ X(TypeOfExpression) \
+ X(PreIncrementExpression) \
+ X(PreDecrementExpression) \
+ X(UnaryPlusExpression) \
+ X(UnaryMinusExpression) \
+ X(TildeExpression) \
+ X(NotExpression) \
+ X(BinaryExpression) \
+ X(ConditionalExpression) \
+ X(Expression) \
+ X(Block) \
+ X(StatementList) \
+ X(VariableStatement) \
+ X(VariableDeclarationList) \
+ X(EmptyStatement) \
+ X(ExpressionStatement) \
+ X(IfStatement) \
+ X(DoWhileStatement) \
+ X(WhileStatement) \
+ X(ForStatement) \
+ X(ForEachStatement) \
+ X(ContinueStatement) \
+ X(BreakStatement) \
+ X(ReturnStatement) \
+ X(YieldExpression) \
+ X(WithStatement) \
+ X(SwitchStatement) \
+ X(CaseBlock) \
+ X(CaseClauses) \
+ X(CaseClause) \
+ X(DefaultClause) \
+ X(LabelledStatement) \
+ X(ThrowStatement) \
+ X(TryStatement) \
+ X(Catch) \
+ X(Finally) \
+ X(FunctionDeclaration) \
+ X(FunctionExpression) \
+ X(FormalParameterList) \
+ X(ClassExpression) \
+ X(ClassDeclaration) \
+ X(ClassElementList) \
+ X(Program) \
+ X(NameSpaceImport) \
+ X(ImportSpecifier) \
+ X(ImportsList) \
+ X(NamedImports) \
+ X(FromClause) \
+ X(ImportClause) \
+ X(ImportDeclaration) \
+ X(ExportSpecifier) \
+ X(ExportsList) \
+ X(ExportClause) \
+ X(ExportDeclaration) \
+ X(ESModule) \
+ X(DebuggerStatement) \
+ X(Type) \
+ X(TypeAnnotation)
+
+#define QQmlJSASTClassListToVisit QQmlJSASTUiClassListToVisit QQmlJSASTQQmlJSClassListToVisit
+
namespace QQmlJS { namespace AST {
class QML_PARSER_EXPORT BaseVisitor
@@ -57,332 +180,11 @@ public:
virtual bool preVisit(Node *) = 0;
virtual void postVisit(Node *) = 0;
- // Ui
- virtual bool visit(UiProgram *) = 0;
- virtual bool visit(UiHeaderItemList *) = 0;
- virtual bool visit(UiPragma *) = 0;
- virtual bool visit(UiImport *) = 0;
- virtual bool visit(UiPublicMember *) = 0;
- virtual bool visit(UiSourceElement *) = 0;
- virtual bool visit(UiObjectDefinition *) = 0;
- virtual bool visit(UiObjectInitializer *) = 0;
- virtual bool visit(UiObjectBinding *) = 0;
- virtual bool visit(UiScriptBinding *) = 0;
- virtual bool visit(UiArrayBinding *) = 0;
- virtual bool visit(UiParameterList *) = 0;
- virtual bool visit(UiObjectMemberList *) = 0;
- virtual bool visit(UiArrayMemberList *) = 0;
- virtual bool visit(UiQualifiedId *) = 0;
- virtual bool visit(UiEnumDeclaration *) = 0;
- virtual bool visit(UiEnumMemberList *) = 0;
- virtual bool visit(UiVersionSpecifier *) = 0;
- virtual bool visit(UiInlineComponent *) = 0;
- virtual bool visit(UiAnnotation *) = 0;
- virtual bool visit(UiAnnotationList *) = 0;
- virtual bool visit(UiRequired *) = 0;
-
- virtual void endVisit(UiProgram *) = 0;
- virtual void endVisit(UiImport *) = 0;
- virtual void endVisit(UiHeaderItemList *) = 0;
- virtual void endVisit(UiPragma *) = 0;
- virtual void endVisit(UiPublicMember *) = 0;
- virtual void endVisit(UiSourceElement *) = 0;
- virtual void endVisit(UiObjectDefinition *) = 0;
- virtual void endVisit(UiObjectInitializer *) = 0;
- virtual void endVisit(UiObjectBinding *) = 0;
- virtual void endVisit(UiScriptBinding *) = 0;
- virtual void endVisit(UiArrayBinding *) = 0;
- virtual void endVisit(UiParameterList *) = 0;
- virtual void endVisit(UiObjectMemberList *) = 0;
- virtual void endVisit(UiArrayMemberList *) = 0;
- virtual void endVisit(UiQualifiedId *) = 0;
- virtual void endVisit(UiEnumDeclaration *) = 0;
- virtual void endVisit(UiEnumMemberList *) = 0;
- virtual void endVisit(UiVersionSpecifier *) = 0;
- virtual void endVisit(UiInlineComponent *) = 0;
- virtual void endVisit(UiAnnotation *) = 0;
- virtual void endVisit(UiAnnotationList *) = 0;
- virtual void endVisit(UiRequired *) = 0;
-
- // QQmlJS
- virtual bool visit(TypeExpression *) = 0;
- virtual void endVisit(TypeExpression *) = 0;
-
- virtual bool visit(ThisExpression *) = 0;
- virtual void endVisit(ThisExpression *) = 0;
-
- virtual bool visit(IdentifierExpression *) = 0;
- virtual void endVisit(IdentifierExpression *) = 0;
-
- virtual bool visit(NullExpression *) = 0;
- virtual void endVisit(NullExpression *) = 0;
-
- virtual bool visit(TrueLiteral *) = 0;
- virtual void endVisit(TrueLiteral *) = 0;
-
- virtual bool visit(FalseLiteral *) = 0;
- virtual void endVisit(FalseLiteral *) = 0;
-
- virtual bool visit(SuperLiteral *) = 0;
- virtual void endVisit(SuperLiteral *) = 0;
-
- virtual bool visit(StringLiteral *) = 0;
- virtual void endVisit(StringLiteral *) = 0;
-
- virtual bool visit(TemplateLiteral *) = 0;
- virtual void endVisit(TemplateLiteral *) = 0;
-
- virtual bool visit(NumericLiteral *) = 0;
- virtual void endVisit(NumericLiteral *) = 0;
-
- virtual bool visit(RegExpLiteral *) = 0;
- virtual void endVisit(RegExpLiteral *) = 0;
-
- virtual bool visit(ArrayPattern *) = 0;
- virtual void endVisit(ArrayPattern *) = 0;
-
- virtual bool visit(ObjectPattern *) = 0;
- virtual void endVisit(ObjectPattern *) = 0;
-
- virtual bool visit(PatternElementList *) = 0;
- virtual void endVisit(PatternElementList *) = 0;
-
- virtual bool visit(PatternPropertyList *) = 0;
- virtual void endVisit(PatternPropertyList *) = 0;
-
- virtual bool visit(PatternElement *) = 0;
- virtual void endVisit(PatternElement *) = 0;
-
- virtual bool visit(PatternProperty *) = 0;
- virtual void endVisit(PatternProperty *) = 0;
-
- virtual bool visit(Elision *) = 0;
- virtual void endVisit(Elision *) = 0;
-
- virtual bool visit(NestedExpression *) = 0;
- virtual void endVisit(NestedExpression *) = 0;
-
- virtual bool visit(IdentifierPropertyName *) = 0;
- virtual void endVisit(IdentifierPropertyName *) = 0;
-
- virtual bool visit(StringLiteralPropertyName *) = 0;
- virtual void endVisit(StringLiteralPropertyName *) = 0;
-
- virtual bool visit(NumericLiteralPropertyName *) = 0;
- virtual void endVisit(NumericLiteralPropertyName *) = 0;
-
- virtual bool visit(ComputedPropertyName *) = 0;
- virtual void endVisit(ComputedPropertyName *) = 0;
-
- virtual bool visit(ArrayMemberExpression *) = 0;
- virtual void endVisit(ArrayMemberExpression *) = 0;
-
- virtual bool visit(FieldMemberExpression *) = 0;
- virtual void endVisit(FieldMemberExpression *) = 0;
-
- virtual bool visit(TaggedTemplate *) = 0;
- virtual void endVisit(TaggedTemplate *) = 0;
-
- virtual bool visit(NewMemberExpression *) = 0;
- virtual void endVisit(NewMemberExpression *) = 0;
-
- virtual bool visit(NewExpression *) = 0;
- virtual void endVisit(NewExpression *) = 0;
-
- virtual bool visit(CallExpression *) = 0;
- virtual void endVisit(CallExpression *) = 0;
-
- virtual bool visit(ArgumentList *) = 0;
- virtual void endVisit(ArgumentList *) = 0;
-
- virtual bool visit(PostIncrementExpression *) = 0;
- virtual void endVisit(PostIncrementExpression *) = 0;
-
- virtual bool visit(PostDecrementExpression *) = 0;
- virtual void endVisit(PostDecrementExpression *) = 0;
-
- virtual bool visit(DeleteExpression *) = 0;
- virtual void endVisit(DeleteExpression *) = 0;
-
- virtual bool visit(VoidExpression *) = 0;
- virtual void endVisit(VoidExpression *) = 0;
-
- virtual bool visit(TypeOfExpression *) = 0;
- virtual void endVisit(TypeOfExpression *) = 0;
-
- virtual bool visit(PreIncrementExpression *) = 0;
- virtual void endVisit(PreIncrementExpression *) = 0;
-
- virtual bool visit(PreDecrementExpression *) = 0;
- virtual void endVisit(PreDecrementExpression *) = 0;
-
- virtual bool visit(UnaryPlusExpression *) = 0;
- virtual void endVisit(UnaryPlusExpression *) = 0;
-
- virtual bool visit(UnaryMinusExpression *) = 0;
- virtual void endVisit(UnaryMinusExpression *) = 0;
-
- virtual bool visit(TildeExpression *) = 0;
- virtual void endVisit(TildeExpression *) = 0;
-
- virtual bool visit(NotExpression *) = 0;
- virtual void endVisit(NotExpression *) = 0;
-
- virtual bool visit(BinaryExpression *) = 0;
- virtual void endVisit(BinaryExpression *) = 0;
-
- virtual bool visit(ConditionalExpression *) = 0;
- virtual void endVisit(ConditionalExpression *) = 0;
-
- virtual bool visit(Expression *) = 0;
- virtual void endVisit(Expression *) = 0;
-
- virtual bool visit(Block *) = 0;
- virtual void endVisit(Block *) = 0;
-
- virtual bool visit(StatementList *) = 0;
- virtual void endVisit(StatementList *) = 0;
-
- virtual bool visit(VariableStatement *) = 0;
- virtual void endVisit(VariableStatement *) = 0;
-
- virtual bool visit(VariableDeclarationList *) = 0;
- virtual void endVisit(VariableDeclarationList *) = 0;
-
- virtual bool visit(EmptyStatement *) = 0;
- virtual void endVisit(EmptyStatement *) = 0;
-
- virtual bool visit(ExpressionStatement *) = 0;
- virtual void endVisit(ExpressionStatement *) = 0;
-
- virtual bool visit(IfStatement *) = 0;
- virtual void endVisit(IfStatement *) = 0;
-
- virtual bool visit(DoWhileStatement *) = 0;
- virtual void endVisit(DoWhileStatement *) = 0;
-
- virtual bool visit(WhileStatement *) = 0;
- virtual void endVisit(WhileStatement *) = 0;
-
- virtual bool visit(ForStatement *) = 0;
- virtual void endVisit(ForStatement *) = 0;
-
- virtual bool visit(ForEachStatement *) = 0;
- virtual void endVisit(ForEachStatement *) = 0;
-
- virtual bool visit(ContinueStatement *) = 0;
- virtual void endVisit(ContinueStatement *) = 0;
-
- virtual bool visit(BreakStatement *) = 0;
- virtual void endVisit(BreakStatement *) = 0;
-
- virtual bool visit(ReturnStatement *) = 0;
- virtual void endVisit(ReturnStatement *) = 0;
-
- virtual bool visit(YieldExpression *) = 0;
- virtual void endVisit(YieldExpression *) = 0;
-
- virtual bool visit(WithStatement *) = 0;
- virtual void endVisit(WithStatement *) = 0;
-
- virtual bool visit(SwitchStatement *) = 0;
- virtual void endVisit(SwitchStatement *) = 0;
-
- virtual bool visit(CaseBlock *) = 0;
- virtual void endVisit(CaseBlock *) = 0;
-
- virtual bool visit(CaseClauses *) = 0;
- virtual void endVisit(CaseClauses *) = 0;
-
- virtual bool visit(CaseClause *) = 0;
- virtual void endVisit(CaseClause *) = 0;
-
- virtual bool visit(DefaultClause *) = 0;
- virtual void endVisit(DefaultClause *) = 0;
-
- virtual bool visit(LabelledStatement *) = 0;
- virtual void endVisit(LabelledStatement *) = 0;
-
- virtual bool visit(ThrowStatement *) = 0;
- virtual void endVisit(ThrowStatement *) = 0;
-
- virtual bool visit(TryStatement *) = 0;
- virtual void endVisit(TryStatement *) = 0;
-
- virtual bool visit(Catch *) = 0;
- virtual void endVisit(Catch *) = 0;
-
- virtual bool visit(Finally *) = 0;
- virtual void endVisit(Finally *) = 0;
-
- virtual bool visit(FunctionDeclaration *) = 0;
- virtual void endVisit(FunctionDeclaration *) = 0;
-
- virtual bool visit(FunctionExpression *) = 0;
- virtual void endVisit(FunctionExpression *) = 0;
-
- virtual bool visit(FormalParameterList *) = 0;
- virtual void endVisit(FormalParameterList *) = 0;
-
- virtual bool visit(ClassExpression *) = 0;
- virtual void endVisit(ClassExpression *) = 0;
-
- virtual bool visit(ClassDeclaration *) = 0;
- virtual void endVisit(ClassDeclaration *) = 0;
-
- virtual bool visit(ClassElementList *) = 0;
- virtual void endVisit(ClassElementList *) = 0;
-
- virtual bool visit(Program *) = 0;
- virtual void endVisit(Program *) = 0;
-
- virtual bool visit(NameSpaceImport *) = 0;
- virtual void endVisit(NameSpaceImport *) = 0;
-
- virtual bool visit(ImportSpecifier *) = 0;
- virtual void endVisit(ImportSpecifier *) = 0;
-
- virtual bool visit(ImportsList *) = 0;
- virtual void endVisit(ImportsList *) = 0;
-
- virtual bool visit(NamedImports *) = 0;
- virtual void endVisit(NamedImports *) = 0;
-
- virtual bool visit(FromClause *) = 0;
- virtual void endVisit(FromClause *) = 0;
-
- virtual bool visit(ImportClause *) = 0;
- virtual void endVisit(ImportClause *) = 0;
-
- virtual bool visit(ImportDeclaration *) = 0;
- virtual void endVisit(ImportDeclaration *) = 0;
-
- virtual bool visit(ExportSpecifier *) = 0;
- virtual void endVisit(ExportSpecifier *) = 0;
-
- virtual bool visit(ExportsList *) = 0;
- virtual void endVisit(ExportsList *) = 0;
-
- virtual bool visit(ExportClause *) = 0;
- virtual void endVisit(ExportClause *) = 0;
-
- virtual bool visit(ExportDeclaration *) = 0;
- virtual void endVisit(ExportDeclaration *) = 0;
-
- virtual bool visit(ESModule *) = 0;
- virtual void endVisit(ESModule *) = 0;
-
- virtual bool visit(DebuggerStatement *) = 0;
- virtual void endVisit(DebuggerStatement *) = 0;
-
- virtual bool visit(Type *) = 0;
- virtual void endVisit(Type *) = 0;
-
- virtual bool visit(TypeArgument *) = 0;
- virtual void endVisit(TypeArgument *) = 0;
-
- virtual bool visit(TypeAnnotation *) = 0;
- virtual void endVisit(TypeAnnotation *) = 0;
+#define X(name) \
+ virtual bool visit(name *) = 0; \
+ virtual void endVisit(name *) = 0;
+ QQmlJSASTClassListToVisit
+#undef X
virtual void throwRecursionDepthError() = 0;
@@ -401,334 +203,37 @@ public:
bool preVisit(Node *) override { return true; }
void postVisit(Node *) override {}
- // Ui
- bool visit(UiProgram *) override { return true; }
- bool visit(UiHeaderItemList *) override { return true; }
- bool visit(UiPragma *) override { return true; }
- bool visit(UiImport *) override { return true; }
- bool visit(UiPublicMember *) override { return true; }
- bool visit(UiSourceElement *) override { return true; }
- bool visit(UiObjectDefinition *) override { return true; }
- bool visit(UiObjectInitializer *) override { return true; }
- bool visit(UiObjectBinding *) override { return true; }
- bool visit(UiScriptBinding *) override { return true; }
- bool visit(UiArrayBinding *) override { return true; }
- bool visit(UiParameterList *) override { return true; }
- bool visit(UiObjectMemberList *) override { return true; }
- bool visit(UiArrayMemberList *) override { return true; }
- bool visit(UiQualifiedId *) override { return true; }
- bool visit(UiEnumDeclaration *) override { return true; }
- bool visit(UiEnumMemberList *) override { return true; }
- bool visit(UiVersionSpecifier *) override { return true; }
- bool visit(UiInlineComponent *) override { return true; }
- bool visit(UiAnnotation *) override { return true; }
- bool visit(UiAnnotationList *) override { return true; }
- bool visit(UiRequired *) override { return true; }
-
- void endVisit(UiProgram *) override {}
- void endVisit(UiImport *) override {}
- void endVisit(UiHeaderItemList *) override {}
- void endVisit(UiPragma *) override {}
- void endVisit(UiPublicMember *) override {}
- void endVisit(UiSourceElement *) override {}
- void endVisit(UiObjectDefinition *) override {}
- void endVisit(UiObjectInitializer *) override {}
- void endVisit(UiObjectBinding *) override {}
- void endVisit(UiScriptBinding *) override {}
- void endVisit(UiArrayBinding *) override {}
- void endVisit(UiParameterList *) override {}
- void endVisit(UiObjectMemberList *) override {}
- void endVisit(UiArrayMemberList *) override {}
- void endVisit(UiQualifiedId *) override {}
- void endVisit(UiEnumDeclaration *) override {}
- void endVisit(UiEnumMemberList *) override {}
- void endVisit(UiVersionSpecifier *) override {}
- void endVisit(UiInlineComponent *) override {}
- void endVisit(UiAnnotation *) override {}
- void endVisit(UiAnnotationList *) override {}
- void endVisit(UiRequired *) override {}
-
- // QQmlJS
- bool visit(TypeExpression *) override { return true; }
- void endVisit(TypeExpression *) override {}
-
- bool visit(ThisExpression *) override { return true; }
- void endVisit(ThisExpression *) override {}
-
- bool visit(IdentifierExpression *) override { return true; }
- void endVisit(IdentifierExpression *) override {}
-
- bool visit(NullExpression *) override { return true; }
- void endVisit(NullExpression *) override {}
-
- bool visit(TrueLiteral *) override { return true; }
- void endVisit(TrueLiteral *) override {}
-
- bool visit(FalseLiteral *) override { return true; }
- void endVisit(FalseLiteral *) override {}
-
- bool visit(SuperLiteral *) override { return true; }
- void endVisit(SuperLiteral *) override {}
-
- bool visit(StringLiteral *) override { return true; }
- void endVisit(StringLiteral *) override {}
-
- bool visit(TemplateLiteral *) override { return true; }
- void endVisit(TemplateLiteral *) override {}
-
- bool visit(NumericLiteral *) override { return true; }
- void endVisit(NumericLiteral *) override {}
-
- bool visit(RegExpLiteral *) override { return true; }
- void endVisit(RegExpLiteral *) override {}
-
- bool visit(ArrayPattern *) override { return true; }
- void endVisit(ArrayPattern *) override {}
-
- bool visit(ObjectPattern *) override { return true; }
- void endVisit(ObjectPattern *) override {}
-
- bool visit(PatternElementList *) override { return true; }
- void endVisit(PatternElementList *) override {}
-
- bool visit(PatternPropertyList *) override { return true; }
- void endVisit(PatternPropertyList *) override {}
-
- bool visit(PatternElement *) override { return true; }
- void endVisit(PatternElement *) override {}
-
- bool visit(PatternProperty *) override { return true; }
- void endVisit(PatternProperty *) override {}
-
- bool visit(Elision *) override { return true; }
- void endVisit(Elision *) override {}
-
- bool visit(NestedExpression *) override { return true; }
- void endVisit(NestedExpression *) override {}
-
- bool visit(IdentifierPropertyName *) override { return true; }
- void endVisit(IdentifierPropertyName *) override {}
-
- bool visit(StringLiteralPropertyName *) override { return true; }
- void endVisit(StringLiteralPropertyName *) override {}
-
- bool visit(NumericLiteralPropertyName *) override { return true; }
- void endVisit(NumericLiteralPropertyName *) override {}
-
- bool visit(ComputedPropertyName *) override { return true; }
- void endVisit(ComputedPropertyName *) override {}
-
- bool visit(ArrayMemberExpression *) override { return true; }
- void endVisit(ArrayMemberExpression *) override {}
-
- bool visit(FieldMemberExpression *) override { return true; }
- void endVisit(FieldMemberExpression *) override {}
-
- bool visit(TaggedTemplate *) override { return true; }
- void endVisit(TaggedTemplate *) override {}
-
- bool visit(NewMemberExpression *) override { return true; }
- void endVisit(NewMemberExpression *) override {}
-
- bool visit(NewExpression *) override { return true; }
- void endVisit(NewExpression *) override {}
-
- bool visit(CallExpression *) override { return true; }
- void endVisit(CallExpression *) override {}
-
- bool visit(ArgumentList *) override { return true; }
- void endVisit(ArgumentList *) override {}
-
- bool visit(PostIncrementExpression *) override { return true; }
- void endVisit(PostIncrementExpression *) override {}
-
- bool visit(PostDecrementExpression *) override { return true; }
- void endVisit(PostDecrementExpression *) override {}
-
- bool visit(DeleteExpression *) override { return true; }
- void endVisit(DeleteExpression *) override {}
-
- bool visit(VoidExpression *) override { return true; }
- void endVisit(VoidExpression *) override {}
-
- bool visit(TypeOfExpression *) override { return true; }
- void endVisit(TypeOfExpression *) override {}
-
- bool visit(PreIncrementExpression *) override { return true; }
- void endVisit(PreIncrementExpression *) override {}
-
- bool visit(PreDecrementExpression *) override { return true; }
- void endVisit(PreDecrementExpression *) override {}
-
- bool visit(UnaryPlusExpression *) override { return true; }
- void endVisit(UnaryPlusExpression *) override {}
-
- bool visit(UnaryMinusExpression *) override { return true; }
- void endVisit(UnaryMinusExpression *) override {}
-
- bool visit(TildeExpression *) override { return true; }
- void endVisit(TildeExpression *) override {}
-
- bool visit(NotExpression *) override { return true; }
- void endVisit(NotExpression *) override {}
-
- bool visit(BinaryExpression *) override { return true; }
- void endVisit(BinaryExpression *) override {}
-
- bool visit(ConditionalExpression *) override { return true; }
- void endVisit(ConditionalExpression *) override {}
-
- bool visit(Expression *) override { return true; }
- void endVisit(Expression *) override {}
-
- bool visit(Block *) override { return true; }
- void endVisit(Block *) override {}
-
- bool visit(StatementList *) override { return true; }
- void endVisit(StatementList *) override {}
-
- bool visit(VariableStatement *) override { return true; }
- void endVisit(VariableStatement *) override {}
-
- bool visit(VariableDeclarationList *) override { return true; }
- void endVisit(VariableDeclarationList *) override {}
-
- bool visit(EmptyStatement *) override { return true; }
- void endVisit(EmptyStatement *) override {}
-
- bool visit(ExpressionStatement *) override { return true; }
- void endVisit(ExpressionStatement *) override {}
-
- bool visit(IfStatement *) override { return true; }
- void endVisit(IfStatement *) override {}
-
- bool visit(DoWhileStatement *) override { return true; }
- void endVisit(DoWhileStatement *) override {}
-
- bool visit(WhileStatement *) override { return true; }
- void endVisit(WhileStatement *) override {}
-
- bool visit(ForStatement *) override { return true; }
- void endVisit(ForStatement *) override {}
-
- bool visit(ForEachStatement *) override { return true; }
- void endVisit(ForEachStatement *) override {}
-
- bool visit(ContinueStatement *) override { return true; }
- void endVisit(ContinueStatement *) override {}
-
- bool visit(BreakStatement *) override { return true; }
- void endVisit(BreakStatement *) override {}
-
- bool visit(ReturnStatement *) override { return true; }
- void endVisit(ReturnStatement *) override {}
-
- bool visit(YieldExpression *) override { return true; }
- void endVisit(YieldExpression *) override {}
-
- bool visit(WithStatement *) override { return true; }
- void endVisit(WithStatement *) override {}
-
- bool visit(SwitchStatement *) override { return true; }
- void endVisit(SwitchStatement *) override {}
-
- bool visit(CaseBlock *) override { return true; }
- void endVisit(CaseBlock *) override {}
-
- bool visit(CaseClauses *) override { return true; }
- void endVisit(CaseClauses *) override {}
-
- bool visit(CaseClause *) override { return true; }
- void endVisit(CaseClause *) override {}
-
- bool visit(DefaultClause *) override { return true; }
- void endVisit(DefaultClause *) override {}
-
- bool visit(LabelledStatement *) override { return true; }
- void endVisit(LabelledStatement *) override {}
-
- bool visit(ThrowStatement *) override { return true; }
- void endVisit(ThrowStatement *) override {}
-
- bool visit(TryStatement *) override { return true; }
- void endVisit(TryStatement *) override {}
-
- bool visit(Catch *) override { return true; }
- void endVisit(Catch *) override {}
-
- bool visit(Finally *) override { return true; }
- void endVisit(Finally *) override {}
-
- bool visit(FunctionDeclaration *) override { return true; }
- void endVisit(FunctionDeclaration *) override {}
-
- bool visit(FunctionExpression *) override { return true; }
- void endVisit(FunctionExpression *) override {}
-
- bool visit(FormalParameterList *) override { return true; }
- void endVisit(FormalParameterList *) override {}
-
- bool visit(ClassExpression *) override { return true; }
- void endVisit(ClassExpression *) override {}
-
- bool visit(ClassDeclaration *) override { return true; }
- void endVisit(ClassDeclaration *) override {}
-
- bool visit(ClassElementList *) override { return true; }
- void endVisit(ClassElementList *) override {}
-
- bool visit(Program *) override { return true; }
- void endVisit(Program *) override {}
-
- bool visit(NameSpaceImport *) override { return true; }
- void endVisit(NameSpaceImport *) override {}
-
- bool visit(ImportSpecifier *) override { return true; }
- void endVisit(ImportSpecifier *) override {}
-
- bool visit(ImportsList *) override { return true; }
- void endVisit(ImportsList *) override {}
-
- bool visit(NamedImports *) override { return true; }
- void endVisit(NamedImports *) override {}
-
- bool visit(FromClause *) override { return true; }
- void endVisit(FromClause *) override {}
-
- bool visit(ImportClause *) override { return true; }
- void endVisit(ImportClause *) override {}
-
- bool visit(ImportDeclaration *) override { return true; }
- void endVisit(ImportDeclaration *) override {}
-
- bool visit(ExportSpecifier *) override { return true; }
- void endVisit(ExportSpecifier *) override {}
-
- bool visit(ExportsList *) override { return true; }
- void endVisit(ExportsList *) override {}
-
- bool visit(ExportClause *) override { return true; }
- void endVisit(ExportClause *) override {}
-
- bool visit(ExportDeclaration *) override { return true; }
- void endVisit(ExportDeclaration *) override {}
-
- bool visit(ESModule *) override { return true; }
- void endVisit(ESModule *) override {}
-
- bool visit(DebuggerStatement *) override { return true; }
- void endVisit(DebuggerStatement *) override {}
-
- bool visit(Type *) override { return true; }
- void endVisit(Type *) override {}
-
- bool visit(TypeArgument *) override { return true; }
- void endVisit(TypeArgument *) override {}
-
- bool visit(TypeAnnotation *) override { return true; }
- void endVisit(TypeAnnotation *) override {}
+#define X(name) \
+ bool visit(name *) override { return true; } \
+ void endVisit(name *) override { }
+ QQmlJSASTClassListToVisit
+#undef X
};
+class QML_PARSER_EXPORT JSVisitor : public BaseVisitor
+{
+public:
+ JSVisitor() = default;
+
+ bool preVisit(Node *) override { return true; }
+ void postVisit(Node *) override { }
+
+#define X(name) \
+ bool visit(name *) override { return true; } \
+ void endVisit(name *) override { }
+ QQmlJSASTQQmlJSClassListToVisit
+#undef X
+
+#define X(name) \
+ bool visit(name *) override \
+ { \
+ Q_ASSERT(false); \
+ return false; \
+ } \
+ void endVisit(name *) override { }
+ QQmlJSASTUiClassListToVisit
+#undef X
+}; // namespace AST
} } // namespace AST
QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h
index 568bf4d35b..37f78a30e1 100644
--- a/src/qml/parser/qqmljsengine_p.h
+++ b/src/qml/parser/qqmljsengine_p.h
@@ -71,8 +71,8 @@ public:
void addComment(int pos, int len, int line, int col)
{
- if (len > 0)
- _comments.append(QQmlJS::SourceLocation(pos, len, line, col));
+ Q_ASSERT(len >= 0);
+ _comments.append(QQmlJS::SourceLocation(pos, len, line, col));
}
QList<SourceLocation> comments() const { return _comments; }
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index e0a20b00d2..cdb3dde5c6 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -368,7 +368,8 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
{
Q_ASSERT(_state.currentChar == u'u');
scanChar(); // skip u
- if (_codePtr + 4 <= _endPtr && isHexDigit(_state.currentChar)) {
+ constexpr int distanceFromFirstHexToLastHex = 3;
+ if (_codePtr + distanceFromFirstHexToLastHex <= _endPtr && isHexDigit(_state.currentChar)) {
uint codePoint = 0;
for (int i = 0; i < 4; ++i) {
int digit = hexDigit(_state.currentChar);
@@ -1213,6 +1214,32 @@ int Lexer::scanString(ScanStringMode mode)
int Lexer::scanNumber(QChar ch)
{
+ auto scanOptionalNumericSeparator = [this](auto isNextCharacterValid){
+ if (_state.currentChar == u'_') {
+ if (peekChar() == u'_') {
+ _state.errorCode = IllegalNumber;
+ _errorMessage = QCoreApplication::translate(
+ "QQmlParser",
+ "There can be at most one numeric separator beetwen digits"
+ );
+ return false;
+ }
+
+ if (!isNextCharacterValid()) {
+ _state.errorCode = IllegalNumber;
+ _errorMessage = QCoreApplication::translate(
+ "QQmlParser",
+ "A trailing numeric separator is not allowed in numeric literals"
+ );
+ return false;
+ }
+
+ scanChar();
+ }
+
+ return true;
+ };
+
if (ch == u'0') {
if (_state.currentChar == u'x' || _state.currentChar == u'X') {
ch = _state.currentChar; // remember the x or X to use it in the error message below.
@@ -1237,6 +1264,9 @@ int Lexer::scanNumber(QChar ch)
d *= 16;
d += digit;
scanChar();
+
+ if (!scanOptionalNumericSeparator([this](){ return isHexDigit(peekChar()); }))
+ return T_ERROR;
}
_state.tokenValue = d;
@@ -1264,6 +1294,12 @@ int Lexer::scanNumber(QChar ch)
d *= 8;
d += digit;
scanChar();
+
+ if (!scanOptionalNumericSeparator([this](){
+ return isOctalDigit(peekChar().unicode());
+ })) {
+ return T_ERROR;
+ }
}
_state.tokenValue = d;
@@ -1293,6 +1329,12 @@ int Lexer::scanNumber(QChar ch)
d *= 2;
d += digit;
scanChar();
+
+ if (!scanOptionalNumericSeparator([this](){
+ return peekChar().unicode() == u'0' || peekChar().unicode() == u'1';
+ })) {
+ return T_ERROR;
+ }
}
_state.tokenValue = d;
@@ -1310,9 +1352,15 @@ int Lexer::scanNumber(QChar ch)
chars.append(ch.unicode());
if (ch != u'.') {
+ if (!scanOptionalNumericSeparator([this](){ return peekChar().isDigit(); }))
+ return T_ERROR;
+
while (_state.currentChar.isDigit()) {
chars.append(_state.currentChar.unicode());
scanChar(); // consume the digit
+
+ if (!scanOptionalNumericSeparator([this](){ return peekChar().isDigit(); }))
+ return T_ERROR;
}
if (_state.currentChar == u'.') {
@@ -1324,6 +1372,9 @@ int Lexer::scanNumber(QChar ch)
while (_state.currentChar.isDigit()) {
chars.append(_state.currentChar.unicode());
scanChar();
+
+ if (!scanOptionalNumericSeparator([this](){ return peekChar().isDigit(); }))
+ return T_ERROR;
}
if (_state.currentChar == u'e' || _state.currentChar == u'E') {
@@ -1341,6 +1392,9 @@ int Lexer::scanNumber(QChar ch)
while (_state.currentChar.isDigit()) {
chars.append(_state.currentChar.unicode());
scanChar();
+
+ if (!scanOptionalNumericSeparator([this](){ return peekChar().isDigit(); }))
+ return T_ERROR;
}
}
}
@@ -1802,6 +1856,17 @@ void Lexer::setState(const Lexer::State &state)
_state = state;
}
+int Lexer::parseModeFlags() const {
+ int flags = 0;
+ if (qmlMode())
+ flags |= QmlMode|StaticIsKeyword;
+ if (yieldIsKeyWord())
+ flags |= YieldIsKeyword;
+ if (_staticIsKeyword)
+ flags |= StaticIsKeyword;
+ return flags;
+}
+
namespace QQmlJS {
QDebug operator<<(QDebug dbg, const Lexer::State &s)
{
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index 242a6ec095..b6144e8894 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -102,17 +102,6 @@ public:
public:
Lexer(Engine *engine, LexMode lexMode = LexMode::WholeCode);
- int parseModeFlags() const {
- int flags = 0;
- if (qmlMode())
- flags |= QmlMode|StaticIsKeyword;
- if (yieldIsKeyWord())
- flags |= YieldIsKeyword;
- if (_staticIsKeyword)
- flags |= StaticIsKeyword;
- return flags;
- }
-
bool qmlMode() const;
bool yieldIsKeyWord() const { return _state.generatorLevel != 0; }
void setStaticIsKeyword(bool b) { _staticIsKeyword = b; }
@@ -144,8 +133,6 @@ public:
Error errorCode() const;
QString errorMessage() const;
- bool prevTerminator() const;
- bool followsClosingBrace() const;
bool canInsertAutomaticSemicolon(int token) const;
enum ParenthesesState {
@@ -245,6 +232,9 @@ protected:
static int classify(const QChar *s, int n, int parseModeFlags);
private:
+ int parseModeFlags() const;
+ bool prevTerminator() const;
+ bool followsClosingBrace() const;
inline void scanChar();
inline QChar peekChar();
int scanToken();
diff --git a/src/qml/qml/ftw/qbipointer_p.h b/src/qml/qml/ftw/qbipointer_p.h
index 4039d6b60d..1597b9e4fc 100644
--- a/src/qml/qml/ftw/qbipointer_p.h
+++ b/src/qml/qml/ftw/qbipointer_p.h
@@ -17,6 +17,8 @@
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qhashfunctions.h>
+
QT_BEGIN_NAMESPACE
namespace QtPrivate {
@@ -43,17 +45,17 @@ template <> struct QFlagPointerAlignment<void>
template<typename T, typename T2>
class QBiPointer {
public:
- constexpr QBiPointer() noexcept = default;
+ Q_NODISCARD_CTOR constexpr QBiPointer() noexcept = default;
~QBiPointer() noexcept = default;
- QBiPointer(const QBiPointer &o) noexcept = default;
- QBiPointer(QBiPointer &&o) noexcept = default;
+ Q_NODISCARD_CTOR QBiPointer(const QBiPointer &o) noexcept = default;
+ Q_NODISCARD_CTOR QBiPointer(QBiPointer &&o) noexcept = default;
QBiPointer<T, T2> &operator=(const QBiPointer<T, T2> &o) noexcept = default;
QBiPointer<T, T2> &operator=(QBiPointer<T, T2> &&o) noexcept = default;
void swap(QBiPointer &other) noexcept { std::swap(ptr_value, other.ptr_value); }
- inline QBiPointer(T *);
- inline QBiPointer(T2 *);
+ Q_NODISCARD_CTOR inline QBiPointer(T *);
+ Q_NODISCARD_CTOR inline QBiPointer(T2 *);
inline bool isNull() const;
inline bool isT1() const;
diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h
index 4de5524a10..78ce738f3b 100644
--- a/src/qml/qml/ftw/qhashedstring_p.h
+++ b/src/qml/qml/ftw/qhashedstring_p.h
@@ -26,7 +26,7 @@
QT_BEGIN_NAMESPACE
class QHashedStringRef;
-class Q_QML_PRIVATE_EXPORT QHashedString : public QString
+class Q_QML_EXPORT QHashedString : public QString
{
public:
inline QHashedString();
@@ -56,7 +56,7 @@ private:
};
class QHashedCStringRef;
-class Q_QML_PRIVATE_EXPORT QHashedStringRef
+class Q_QML_EXPORT QHashedStringRef
{
public:
inline QHashedStringRef();
diff --git a/src/qml/qml/ftw/qintrusivelist_p.h b/src/qml/qml/ftw/qintrusivelist_p.h
index 0d4e428fa0..1170370fae 100644
--- a/src/qml/qml/ftw/qintrusivelist_p.h
+++ b/src/qml/qml/ftw/qintrusivelist_p.h
@@ -19,221 +19,140 @@
QT_BEGIN_NAMESPACE
-class QIntrusiveListNode;
-template<class N, QIntrusiveListNode N::*member>
-class QIntrusiveList
+class QIntrusiveListNode
{
public:
- inline QIntrusiveList();
- inline ~QIntrusiveList();
-
- inline bool isEmpty() const;
- inline void insert(N *n);
- inline void remove(N *n);
- inline bool contains(N *) const;
-
- class iterator {
- public:
- inline iterator();
- inline iterator(N *value);
-
- inline N *operator*() const;
- inline N *operator->() const;
- inline bool operator==(const iterator &other) const;
- inline bool operator!=(const iterator &other) const;
- inline iterator &operator++();
-
- inline iterator &erase();
-
- private:
- N *_value;
- };
- typedef iterator Iterator;
-
- inline N *first() const;
- static inline N *next(N *current);
+ ~QIntrusiveListNode() { remove(); }
+
+ void remove()
+ {
+ if (_prev) *_prev = _next;
+ if (_next) _next->_prev = _prev;
+ _prev = nullptr;
+ _next = nullptr;
+ }
- inline iterator begin();
- inline iterator end();
+ bool isInList() const { return _prev != nullptr; }
private:
- static inline N *nodeToN(QIntrusiveListNode *node);
-
- QIntrusiveListNode *__first = nullptr;
-};
-
-class QIntrusiveListNode
-{
-public:
- inline QIntrusiveListNode();
- inline ~QIntrusiveListNode();
-
- inline void remove();
- inline bool isInList() const;
+ template<class N, QIntrusiveListNode N::*member>
+ friend class QIntrusiveList;
QIntrusiveListNode *_next = nullptr;
QIntrusiveListNode**_prev = nullptr;
};
template<class N, QIntrusiveListNode N::*member>
-QIntrusiveList<N, member>::iterator::iterator()
-: _value(nullptr)
-{
-}
-
-template<class N, QIntrusiveListNode N::*member>
-QIntrusiveList<N, member>::iterator::iterator(N *value)
-: _value(value)
-{
-}
-
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::iterator::operator*() const
-{
- return _value;
-}
-
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::iterator::operator->() const
-{
- return _value;
-}
-
-template<class N, QIntrusiveListNode N::*member>
-bool QIntrusiveList<N, member>::iterator::operator==(const iterator &other) const
+class QIntrusiveList
{
- return other._value == _value;
-}
+private:
+ template<typename O>
+ class iterator_impl {
+ public:
+ iterator_impl() = default;
+ iterator_impl(O value) : _value(value) {}
+
+ O operator*() const { return _value; }
+ O operator->() const { return _value; }
+ bool operator==(const iterator_impl &other) const { return other._value == _value; }
+ bool operator!=(const iterator_impl &other) const { return other._value != _value; }
+ iterator_impl &operator++()
+ {
+ _value = QIntrusiveList<N, member>::next(_value);
+ return *this;
+ }
+
+ protected:
+ O _value = nullptr;
+ };
-template<class N, QIntrusiveListNode N::*member>
-bool QIntrusiveList<N, member>::iterator::operator!=(const iterator &other) const
-{
- return other._value != _value;
-}
+public:
+ class iterator : public iterator_impl<N *>
+ {
+ public:
+ iterator() = default;
+ iterator(N *value) : iterator_impl<N *>(value) {}
+
+ iterator &erase()
+ {
+ N *old = this->_value;
+ this->_value = QIntrusiveList<N, member>::next(this->_value);
+ (old->*member).remove();
+ return *this;
+ }
+ };
-template<class N, QIntrusiveListNode N::*member>
-typename QIntrusiveList<N, member>::iterator &QIntrusiveList<N, member>::iterator::operator++()
-{
- _value = QIntrusiveList<N, member>::next(_value);
- return *this;
-}
+ using const_iterator = iterator_impl<const N *>;
-template<class N, QIntrusiveListNode N::*member>
-typename QIntrusiveList<N, member>::iterator &QIntrusiveList<N, member>::iterator::erase()
-{
- N *old = _value;
- _value = QIntrusiveList<N, member>::next(_value);
- (old->*member).remove();
- return *this;
-}
+ using Iterator = iterator;
+ using ConstIterator = const_iterator;
-template<class N, QIntrusiveListNode N::*member>
-QIntrusiveList<N, member>::QIntrusiveList()
+ ~QIntrusiveList() { while (__first) __first->remove(); }
-{
-}
+ bool isEmpty() const { return __first == nullptr; }
-template<class N, QIntrusiveListNode N::*member>
-QIntrusiveList<N, member>::~QIntrusiveList()
-{
- while (__first) __first->remove();
-}
+ void insert(N *n)
+ {
+ QIntrusiveListNode *nnode = &(n->*member);
+ nnode->remove();
-template<class N, QIntrusiveListNode N::*member>
-bool QIntrusiveList<N, member>::isEmpty() const
-{
- return __first == nullptr;
-}
+ nnode->_next = __first;
+ if (nnode->_next) nnode->_next->_prev = &nnode->_next;
+ __first = nnode;
+ nnode->_prev = &__first;
+ }
-template<class N, QIntrusiveListNode N::*member>
-void QIntrusiveList<N, member>::insert(N *n)
-{
- QIntrusiveListNode *nnode = &(n->*member);
- nnode->remove();
+ void remove(N *n)
+ {
+ QIntrusiveListNode *nnode = &(n->*member);
+ nnode->remove();
+ }
- nnode->_next = __first;
- if (nnode->_next) nnode->_next->_prev = &nnode->_next;
- __first = nnode;
- nnode->_prev = &__first;
-}
+ bool contains(const N *n) const
+ {
+ QIntrusiveListNode *nnode = __first;
+ while (nnode) {
+ if (nodeToN(nnode) == n)
+ return true;
+ nnode = nnode->_next;
+ }
+ return false;
+ }
-template<class N, QIntrusiveListNode N::*member>
-void QIntrusiveList<N, member>::remove(N *n)
-{
- QIntrusiveListNode *nnode = &(n->*member);
- nnode->remove();
-}
+ const N *first() const { return __first ? nodeToN(__first) : nullptr; }
+ N *first() { return __first ? nodeToN(__first) : nullptr; }
-template<class N, QIntrusiveListNode N::*member>
-bool QIntrusiveList<N, member>::contains(N *n) const
-{
- QIntrusiveListNode *nnode = __first;
- while (nnode) {
- if (nodeToN(nnode) == n)
- return true;
- nnode = nnode->_next;
+ template<typename O>
+ static O next(O current)
+ {
+ QIntrusiveListNode *nextnode = (current->*member)._next;
+ return nextnode ? nodeToN(nextnode) : nullptr;
}
- return false;
-}
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::first() const
-{
- return __first?nodeToN(__first):nullptr;
-}
+ iterator begin() { return __first ? iterator(nodeToN(__first)) : iterator(); }
+ iterator end() { return iterator(); }
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::next(N *current)
-{
- QIntrusiveListNode *nextnode = (current->*member)._next;
- N *nextstruct = nextnode?nodeToN(nextnode):nullptr;
- return nextstruct;
-}
+ const_iterator begin() const
+ {
+ return __first ? const_iterator(nodeToN(__first)) : const_iterator();
+ }
-template<class N, QIntrusiveListNode N::*member>
-typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::begin()
-{
- return __first?iterator(nodeToN(__first)):iterator();
-}
+ const_iterator end() const { return const_iterator(); }
-template<class N, QIntrusiveListNode N::*member>
-typename QIntrusiveList<N, member>::iterator QIntrusiveList<N, member>::end()
-{
- return iterator();
-}
+private:
-template<class N, QIntrusiveListNode N::*member>
-N *QIntrusiveList<N, member>::nodeToN(QIntrusiveListNode *node)
-{
- QT_WARNING_PUSH
+ static N *nodeToN(QIntrusiveListNode *node)
+ {
+ QT_WARNING_PUSH
#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 1300
- QT_WARNING_DISABLE_CLANG("-Wnull-pointer-subtraction")
+ QT_WARNING_DISABLE_CLANG("-Wnull-pointer-subtraction")
#endif
- return (N *)((char *)node - ((char *)&(((N *)nullptr)->*member) - (char *)nullptr));
- QT_WARNING_POP
-}
-
-QIntrusiveListNode::QIntrusiveListNode()
-{
-}
-
-QIntrusiveListNode::~QIntrusiveListNode()
-{
- remove();
-}
-
-void QIntrusiveListNode::remove()
-{
- if (_prev) *_prev = _next;
- if (_next) _next->_prev = _prev;
- _prev = nullptr;
- _next = nullptr;
-}
+ return (N *)((char *)node - ((char *)&(((N *)nullptr)->*member) - (char *)nullptr));
+ QT_WARNING_POP
+ }
-bool QIntrusiveListNode::isInList() const
-{
- return _prev != nullptr;
-}
+ QIntrusiveListNode *__first = nullptr;
+};
QT_END_NAMESPACE
diff --git a/src/qml/qml/ftw/qqmlnullablevalue_p.h b/src/qml/qml/ftw/qqmlnullablevalue_p.h
index 2c1b94b556..9a3f032b68 100644
--- a/src/qml/qml/ftw/qqmlnullablevalue_p.h
+++ b/src/qml/qml/ftw/qqmlnullablevalue_p.h
@@ -22,22 +22,69 @@ QT_BEGIN_NAMESPACE
template<typename T>
struct QQmlNullableValue
{
- QQmlNullableValue()
- : value(T()) {}
+ QQmlNullableValue() = default;
+
QQmlNullableValue(const QQmlNullableValue<T> &o)
- : isNull(o.isNull), value(o.value) {}
+ : m_value(o.m_value)
+ , m_isNull(o.m_isNull)
+ {}
+
+ QQmlNullableValue(QQmlNullableValue<T> &&o) noexcept
+ : m_value(std::move(o.value))
+ , m_isNull(std::exchange(o.m_isNull, true))
+ {}
+
QQmlNullableValue(const T &t)
- : isNull(false), value(t) {}
- QQmlNullableValue<T> &operator=(const T &t)
- { isNull = false; value = t; return *this; }
+ : m_value(t)
+ , m_isNull(false)
+ {}
+
+ QQmlNullableValue(T &&t) noexcept
+ : m_value(std::move(t))
+ , m_isNull(false)
+ {}
+
QQmlNullableValue<T> &operator=(const QQmlNullableValue<T> &o)
- { isNull = o.isNull; value = o.value; return *this; }
- operator T() const { return value; }
+ {
+ if (&o != this) {
+ m_value = o.m_value;
+ m_isNull = o.m_isNull;
+ }
+ return *this;
+ }
+
+ QQmlNullableValue<T> &operator=(QQmlNullableValue<T> &&o) noexcept
+ {
+ if (&o != this) {
+ m_value = std::move(o.m_value);
+ m_isNull = std::exchange(o.m_isNull, true);
+ }
+ return *this;
+ }
+
+ QQmlNullableValue<T> &operator=(const T &t)
+ {
+ m_value = t;
+ m_isNull = false;
+ return *this;
+ }
+
+ QQmlNullableValue<T> &operator=(T &&t) noexcept
+ {
+ m_value = std::move(t);
+ m_isNull = false;
+ return *this;
+ }
+
+ const T &value() const { return m_value; }
+ operator T() const { return m_value; }
+
+ void invalidate() { m_isNull = true; }
+ bool isValid() const { return !m_isNull; }
- void invalidate() { isNull = true; }
- bool isValid() const { return !isNull; }
- bool isNull = true;
- T value;
+private:
+ T m_value = T();
+ bool m_isNull = true;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h
index 8b4b5b1535..e7616915eb 100644
--- a/src/qml/qml/ftw/qqmlrefcount_p.h
+++ b/src/qml/qml/ftw/qqmlrefcount_p.h
@@ -21,23 +21,34 @@
QT_BEGIN_NAMESPACE
+template <typename T>
+class QQmlRefCounted;
-class Q_QML_PRIVATE_EXPORT QQmlRefCount
+class QQmlRefCount
{
Q_DISABLE_COPY_MOVE(QQmlRefCount)
public:
inline QQmlRefCount();
inline void addref() const;
- inline void release() const;
inline int count() const;
-protected:
- inline virtual ~QQmlRefCount();
+private:
+ inline ~QQmlRefCount();
+ template <typename T> friend class QQmlRefCounted;
private:
mutable QAtomicInt refCount;
};
+template <typename T>
+class QQmlRefCounted : public QQmlRefCount
+{
+public:
+ inline void release() const;
+protected:
+ inline ~QQmlRefCounted();
+};
+
template<class T>
class QQmlRefPointer
{
@@ -46,16 +57,16 @@ public:
AddRef,
Adopt
};
- inline QQmlRefPointer();
- inline QQmlRefPointer(T *, Mode m = AddRef);
- inline QQmlRefPointer(const QQmlRefPointer<T> &);
- inline QQmlRefPointer(QQmlRefPointer<T> &&);
+ Q_NODISCARD_CTOR inline QQmlRefPointer() noexcept;
+ Q_NODISCARD_CTOR inline QQmlRefPointer(T *, Mode m = AddRef);
+ Q_NODISCARD_CTOR inline QQmlRefPointer(const QQmlRefPointer &);
+ Q_NODISCARD_CTOR inline QQmlRefPointer(QQmlRefPointer &&) noexcept;
inline ~QQmlRefPointer();
void swap(QQmlRefPointer &other) noexcept { qt_ptr_swap(o, other.o); }
inline QQmlRefPointer<T> &operator=(const QQmlRefPointer<T> &o);
- inline QQmlRefPointer<T> &operator=(QQmlRefPointer<T> &&o);
+ inline QQmlRefPointer<T> &operator=(QQmlRefPointer<T> &&o) noexcept;
inline bool isNull() const { return !o; }
@@ -68,8 +79,20 @@ public:
inline T* take() { T *res = o; o = nullptr; return res; }
- friend bool operator==(const QQmlRefPointer &a, const QQmlRefPointer &b) { return a.o == b.o; }
- friend bool operator!=(const QQmlRefPointer &a, const QQmlRefPointer &b) { return !(a == b); }
+ friend bool operator==(const QQmlRefPointer &a, const QQmlRefPointer &b) noexcept
+ {
+ return a.o == b.o;
+ }
+
+ friend bool operator!=(const QQmlRefPointer &a, const QQmlRefPointer &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QQmlRefPointer &v, size_t seed = 0) noexcept
+ {
+ return qHash(v.o, seed);
+ }
void reset(T *t = nullptr)
{
@@ -90,7 +113,7 @@ namespace QQml {
/*!
\internal
Creates a QQmlRefPointer which takes ownership of a newly constructed T.
- T must derive from QQmlRefCount (as we rely on an initial refcount of _1_).
+ T must derive from QQmlRefCounted<T> (as we rely on an initial refcount of _1_).
T will be constructed by forwarding \a args to its constructor.
*/
template <typename T, typename ...Args>
@@ -120,11 +143,22 @@ void QQmlRefCount::addref() const
refCount.ref();
}
-void QQmlRefCount::release() const
+template <typename T>
+void QQmlRefCounted<T>::release() const
{
+ static_assert(std::is_base_of_v<QQmlRefCounted, T>,
+ "QQmlRefCounted<T> must be a base of T (CRTP)");
Q_ASSERT(refCount.loadRelaxed() > 0);
if (!refCount.deref())
- delete this;
+ delete static_cast<const T *>(this);
+}
+
+template <typename T>
+QQmlRefCounted<T>::~QQmlRefCounted()
+{
+ static_assert(std::is_final_v<T> || std::has_virtual_destructor_v<T>,
+ "T must either be marked final or have a virtual dtor, "
+ "lest release() runs into UB.");
}
int QQmlRefCount::count() const
@@ -133,7 +167,7 @@ int QQmlRefCount::count() const
}
template<class T>
-QQmlRefPointer<T>::QQmlRefPointer()
+QQmlRefPointer<T>::QQmlRefPointer() noexcept
: o(nullptr)
{
}
@@ -154,7 +188,7 @@ QQmlRefPointer<T>::QQmlRefPointer(const QQmlRefPointer<T> &other)
}
template <class T>
-QQmlRefPointer<T>::QQmlRefPointer(QQmlRefPointer<T> &&other)
+QQmlRefPointer<T>::QQmlRefPointer(QQmlRefPointer<T> &&other) noexcept
: o(other.take())
{
}
@@ -179,7 +213,7 @@ QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(const QQmlRefPointer<T> &other)
}
template <class T>
-QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(QQmlRefPointer<T> &&other)
+QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(QQmlRefPointer<T> &&other) noexcept
{
QQmlRefPointer<T> m(std::move(other));
swap(m);
diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp
index 8589402bff..57f91b6b4d 100644
--- a/src/qml/qml/ftw/qqmlthread.cpp
+++ b/src/qml/qml/ftw/qqmlthread.cpp
@@ -194,17 +194,6 @@ void QQmlThread::shutdown()
Q_ASSERT(!d->m_shutdown);
d->m_shutdown = true;
- for (;;) {
- if (d->mainSync || !d->mainList.isEmpty()) {
- d->unlock();
- d->mainEvent();
- d->lock();
- } else if (!d->threadList.isEmpty()) {
- d->wait();
- } else {
- break;
- }
- }
if (QCoreApplication::closingDown())
d->quit();
@@ -213,6 +202,10 @@ void QQmlThread::shutdown()
d->unlock();
d->QThread::wait();
+
+ // Discard all remaining messages.
+ // We don't need the lock anymore because the thread is dead.
+ discardMessages();
}
bool QQmlThread::isShutdown() const
@@ -399,5 +392,22 @@ void QQmlThread::waitForNextMessage()
d->m_mainThreadWaiting = false;
}
+/*!
+ \internal
+ \note This method must be called in the main thread
+ \warning This method requires that the lock is held!
+
+ Clear all pending events, for either thread.
+*/
+void QQmlThread::discardMessages()
+{
+ Q_ASSERT(!isThisThread());
+ if (Message *mainSync = std::exchange(d->mainSync, nullptr))
+ delete mainSync;
+ while (!d->mainList.isEmpty())
+ delete d->mainList.takeFirst();
+ while (!d->threadList.isEmpty())
+ delete d->threadList.takeFirst();
+}
QT_END_NAMESPACE
diff --git a/src/qml/qml/ftw/qqmlthread_p.h b/src/qml/qml/ftw/qqmlthread_p.h
index 4af94036b5..35f586f7e7 100644
--- a/src/qml/qml/ftw/qqmlthread_p.h
+++ b/src/qml/qml/ftw/qqmlthread_p.h
@@ -64,6 +64,7 @@ public:
void postMethodToMain(Method &&method, Args &&...args);
void waitForNextMessage();
+ void discardMessages();
private:
friend class QQmlThreadPrivate;
diff --git a/src/qml/qml/ftw/qrecyclepool_p.h b/src/qml/qml/ftw/qrecyclepool_p.h
index 6c2b22d93b..6661c88631 100644
--- a/src/qml/qml/ftw/qrecyclepool_p.h
+++ b/src/qml/qml/ftw/qrecyclepool_p.h
@@ -17,6 +17,8 @@
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/q20memory.h>
+
QT_BEGIN_NAMESPACE
#define QRECYCLEPOOLCOOKIE 0x33218ADF
@@ -67,11 +69,8 @@ public:
inline QRecyclePool();
inline ~QRecyclePool();
- inline T *New();
- template<typename T1>
- inline T *New(const T1 &);
- template<typename T1>
- inline T *New(T1 &);
+ template<typename...Args>
+ [[nodiscard]] inline T *New(Args&&...args);
static inline void Delete(T *);
@@ -93,29 +92,10 @@ QRecyclePool<T, Step>::~QRecyclePool()
}
template<typename T, int Step>
-T *QRecyclePool<T, Step>::New()
-{
- T *rv = d->allocate();
- new (rv) T;
- return rv;
-}
-
-template<typename T, int Step>
-template<typename T1>
-T *QRecyclePool<T, Step>::New(const T1 &a)
+template<typename...Args>
+T *QRecyclePool<T, Step>::New(Args&&...args)
{
- T *rv = d->allocate();
- new (rv) T(a);
- return rv;
-}
-
-template<typename T, int Step>
-template<typename T1>
-T *QRecyclePool<T, Step>::New(T1 &a)
-{
- T *rv = d->allocate();
- new (rv) T(a);
- return rv;
+ return q20::construct_at(d->allocate(), std::forward<Args>(args)...);
}
template<typename T, int Step>
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index c752adc5a0..eb716671b1 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -6,22 +6,22 @@
#include <QtQml/qqmlprivate.h>
#include <private/qjsvalue_p.h>
+#include <private/qqmlbuiltinfunctions_p.h>
+#include <private/qqmlcomponent_p.h>
#include <private/qqmlengine_p.h>
+#include <private/qqmlfinalizer_p.h>
+#include <private/qqmlloggingcategory_p.h>
#include <private/qqmlmetatype_p.h>
#include <private/qqmlmetatypedata_p.h>
#include <private/qqmltype_p_p.h>
#include <private/qqmltypemodule_p.h>
-#include <private/qqmltypenotavailable_p.h>
-#include <private/qqmlcomponent_p.h>
#include <private/qqmltypewrapper_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qv4dateobject_p.h>
+#include <private/qv4errorobject_p.h>
+#include <private/qv4identifiertable_p.h>
#include <private/qv4lookup_p.h>
#include <private/qv4qobjectwrapper_p.h>
-#include <private/qv4identifiertable_p.h>
-#include <private/qv4errorobject_p.h>
-#include <private/qqmlbuiltinfunctions_p.h>
-#include <private/qqmlfinalizer_p.h>
-#include <private/qqmlloggingcategory_p.h>
#include <QtCore/qmutex.h>
@@ -50,17 +50,26 @@ void qmlExecuteDeferred(QObject *object)
{
QQmlData *data = QQmlData::get(object);
- if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
+ if (!data
+ || !data->context
+ || !data->context->engine()
+ || data->deferredData.isEmpty()
+ || data->wasDeleted(object)) {
+ return;
+ }
- QQmlComponentPrivate::DeferredState state;
- QQmlComponentPrivate::beginDeferred(ep, object, &state);
+ if (!data->propertyCache)
+ data->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
- // Release the reference for the deferral action (we still have one from construction)
- data->releaseDeferredData();
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
- QQmlComponentPrivate::completeDeferred(ep, &state);
- }
+ QQmlComponentPrivate::DeferredState state;
+ QQmlComponentPrivate::beginDeferred(ep, object, &state);
+
+ // Release the reference for the deferral action (we still have one from construction)
+ data->releaseDeferredData();
+
+ QQmlComponentPrivate::completeDeferred(ep, &state);
}
QQmlContext *qmlContext(const QObject *obj)
@@ -146,16 +155,17 @@ void QQmlPrivate::qmlRegistrationWarning(
{
switch (warning) {
case UnconstructibleType:
- qWarning()
+ qWarning().nospace()
<< metaType.name()
- << "is neither a QObject, nor default- and copy-constructible, nor uncreatable."
+ << " is neither a default constructible QObject, nor a default- "
+ << "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
<< "You should not use it as a QML type.";
break;
case UnconstructibleSingleton:
qWarning()
<< "Singleton" << metaType.name()
- << "needs either a default constructor or, when adding a default"
- << "constructor is infeasible, a public static"
+ << "needs to be a concrete class with either a default constructor"
+ << "or, when adding a default constructor is infeasible, a public static"
<< "create(QQmlEngine *, QJSEngine *) method.";
break;
case NonQObjectWithAtached:
@@ -166,6 +176,22 @@ void QQmlPrivate::qmlRegistrationWarning(
}
}
+QMetaType QQmlPrivate::compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .typeId();
+}
+
+QMetaType QQmlPrivate::compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .qListTypeId();
+}
+
int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
const char *uri, int versionMajor,
int versionMinor, const char *qmlName,
@@ -261,7 +287,7 @@ static QTypeRevision resolveModuleVersion(int moduleMajor)
/*!
* \relates QQmlEngine
- * Registers an implicit import for module \a uri of major version \a moduleMajor.
+ * Registers a qmldir-import for module \a uri of major version \a moduleMajor.
*
* This has the same effect as an \c import statement in a qmldir file: Whenever
* \a uri of version \a moduleMajor is imported, \a import of version
@@ -470,13 +496,280 @@ static void uniqueRevisions(QVector<QTypeRevision> *revisions, QTypeRevision def
revisions->erase(it, revisions->end());
}
+static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(
+ const QQmlPrivate::RegisterSingletonType &type)
+{
+ QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
+ siinfo->scriptCallback = type.scriptApi;
+ siinfo->qobjectCallback = type.qObjectApi;
+ siinfo->typeName = type.typeName;
+ return QQmlType::SingletonInstanceInfo::ConstPtr(
+ siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
+}
+
+static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(
+ const QQmlPrivate::RegisterCompositeSingletonType &type)
+{
+ QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
+ siinfo->url = QQmlTypeLoader::normalize(type.url);
+ siinfo->typeName = type.typeName;
+ return QQmlType::SingletonInstanceInfo::ConstPtr(
+ siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
+}
+
+static int finalizeType(const QQmlType &dtype)
+{
+ if (!dtype.isValid())
+ return -1;
+
+ QQmlMetaType::registerUndeletableType(dtype);
+ return dtype.index();
+}
+
+using ElementNames = QVarLengthArray<const char *, 8>;
+static ElementNames classElementNames(const QMetaObject *metaObject)
+{
+ Q_ASSERT(metaObject);
+ const char *key = "QML.Element";
+
+ const int offset = metaObject->classInfoOffset();
+ const int start = metaObject->classInfoCount() + offset - 1;
+
+ ElementNames elementNames;
+
+ for (int i = start; i >= offset; --i) {
+ const QMetaClassInfo classInfo = metaObject->classInfo(i);
+ if (qstrcmp(key, classInfo.name()) == 0) {
+ const char *elementName = classInfo.value();
+
+ if (qstrcmp(elementName, "auto") == 0) {
+ const char *strippedClassName = metaObject->className();
+ for (const char *c = strippedClassName; *c != '\0'; c++) {
+ if (*c == ':')
+ strippedClassName = c + 1;
+ }
+ elementName = strippedClassName;
+ } else if (qstrcmp(elementName, "anonymous") == 0) {
+ if (elementNames.isEmpty())
+ elementNames.push_back(nullptr);
+ else if (elementNames[0] != nullptr)
+ qWarning() << metaObject->className() << "is both anonymous and named";
+ continue;
+ }
+
+ if (!elementNames.isEmpty() && elementNames[0] == nullptr) {
+ qWarning() << metaObject->className() << "is both anonymous and named";
+ elementNames[0] = elementName;
+ } else {
+ elementNames.push_back(elementName);
+ }
+ }
+ }
+
+ return elementNames;
+}
+
+struct AliasRegistrar
+{
+ AliasRegistrar(const ElementNames *elementNames) : elementNames(elementNames) {}
+
+ void registerAliases(int typeId)
+ {
+ if (elementNames) {
+ for (int i = 1, end = elementNames->length(); i < end; ++i)
+ otherNames.append(QString::fromUtf8(elementNames->at(i)));
+ elementNames = nullptr;
+ }
+
+ for (const QString &otherName : std::as_const(otherNames))
+ QQmlMetaType::registerTypeAlias(typeId, otherName);
+ }
+
+private:
+ const ElementNames *elementNames;
+ QVarLengthArray<QString, 8> otherNames;
+};
+
+
+static void doRegisterTypeAndRevisions(
+ const QQmlPrivate::RegisterTypeAndRevisions &type,
+ const ElementNames &elementNames)
+{
+ using namespace QQmlPrivate;
+
+ const bool isValueType = !(type.typeId.flags() & QMetaType::PointerToQObject);
+ const bool creatable = (elementNames[0] != nullptr || isValueType)
+ && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
+
+ QString noCreateReason;
+ ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
+
+ if (!creatable) {
+ noCreateReason = QString::fromUtf8(
+ classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
+ if (noCreateReason.isEmpty())
+ noCreateReason = QLatin1String("Type cannot be created in QML.");
+ } else if (isValueType) {
+ const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
+ if (qstrcmp(method, "structured") == 0)
+ creationMethod = ValueTypeCreationMethod::Structured;
+ else if (qstrcmp(method, "construct") == 0)
+ creationMethod = ValueTypeCreationMethod::Construct;
+ }
+
+ RegisterType typeRevision = {
+ QQmlPrivate::RegisterType::CurrentVersion,
+ type.typeId,
+ type.listId,
+ creatable ? type.objectSize : 0,
+ nullptr,
+ nullptr,
+ noCreateReason,
+ type.createValueType,
+ type.uri,
+ type.version,
+ nullptr,
+ type.metaObject,
+ type.attachedPropertiesFunction,
+ type.attachedPropertiesMetaObject,
+ type.parserStatusCast,
+ type.valueSourceCast,
+ type.valueInterceptorCast,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ nullptr,
+ QTypeRevision(),
+ type.structVersion > 0 ? type.finalizerCast : -1,
+ creationMethod
+ };
+
+ QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
+ 0,
+ type.uri,
+ type.version,
+ nullptr,
+ type.listId,
+ type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
+ QTypeRevision(),
+ };
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+ const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
+ "QML.ExtraVersion");
+
+ auto revisions = prepareRevisions(type.metaObject, added) + furtherRevisions;
+ if (type.attachedPropertiesMetaObject)
+ revisions += availableRevisions(type.attachedPropertiesMetaObject);
+ uniqueRevisions(&revisions, type.version, added);
+
+ AliasRegistrar aliasRegistrar(&elementNames);
+ for (QTypeRevision revision : revisions) {
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ assignVersions(&typeRevision, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (typeRevision.version < added
+ || (removed.isValid() && !(typeRevision.version < removed))) {
+ typeRevision.elementName = nullptr;
+ typeRevision.create = nullptr;
+ typeRevision.userdata = nullptr;
+ } else {
+ typeRevision.elementName = elementNames[0];
+ typeRevision.create = creatable ? type.create : nullptr;
+ typeRevision.userdata = type.userdata;
+ }
+
+ typeRevision.customParser = type.customParserFactory();
+ const int id = qmlregister(TypeRegistration, &typeRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+
+ if (typeRevision.elementName)
+ aliasRegistrar.registerAliases(id);
+
+ if (sequenceRevision.metaSequence != QMetaSequence()) {
+ sequenceRevision.version = typeRevision.version;
+ sequenceRevision.revision = typeRevision.revision;
+ const int id = QQmlPrivate::qmlregister(
+ QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+ }
+ }
+}
+
+static void doRegisterSingletonAndRevisions(
+ const QQmlPrivate::RegisterSingletonTypeAndRevisions &type,
+ const ElementNames &elementNames)
+{
+ using namespace QQmlPrivate;
+
+ RegisterSingletonType revisionRegistration = {
+ 0,
+ type.uri,
+ type.version,
+ elementNames[0],
+ nullptr,
+ type.qObjectApi,
+ type.instanceMetaObject,
+ type.typeId,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ QTypeRevision()
+ };
+ const QQmlType::SingletonInstanceInfo::ConstPtr siinfo
+ = singletonInstanceInfo(revisionRegistration);
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+ const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
+ "QML.ExtraVersion");
+
+ auto revisions = prepareRevisions(type.instanceMetaObject, added) + furtherRevisions;
+ uniqueRevisions(&revisions, type.version, added);
+
+ AliasRegistrar aliasRegistrar(&elementNames);
+ for (QTypeRevision revision : std::as_const(revisions)) {
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ assignVersions(&revisionRegistration, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (revisionRegistration.version < added
+ || (removed.isValid() && !(revisionRegistration.version < removed))) {
+ revisionRegistration.typeName = nullptr;
+ revisionRegistration.qObjectApi = nullptr;
+ } else {
+ revisionRegistration.typeName = elementNames[0];
+ revisionRegistration.qObjectApi = type.qObjectApi;
+ }
+
+ const int id = finalizeType(
+ QQmlMetaType::registerSingletonType(revisionRegistration, siinfo));
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+
+ if (revisionRegistration.typeName)
+ aliasRegistrar.registerAliases(id);
+ }
+}
+
/*
This method is "over generalized" to allow us to (potentially) register more types of things in
the future without adding exported symbols.
*/
int QQmlPrivate::qmlregister(RegistrationType type, void *data)
{
- QQmlType dtype;
switch (type) {
case AutoParentRegistration:
return QQmlMetaType::registerAutoParentFunction(
@@ -486,159 +779,29 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
*reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
case TypeAndRevisionsRegistration: {
const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
- const char *elementName = (type.structVersion > 1 && type.forceAnonymous)
- ? nullptr
- : classElementName(type.classInfoMetaObject);
- const bool creatable = (elementName != nullptr)
- && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
-
- QString noCreateReason;
- ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
-
- if (!creatable) {
- noCreateReason = QString::fromUtf8(
- classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
- if (noCreateReason.isEmpty())
- noCreateReason = QLatin1String("Type cannot be created in QML.");
- } else if (!(type.typeId.flags() & QMetaType::PointerToQObject)) {
- const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
- if (qstrcmp(method, "structured") == 0)
- creationMethod = ValueTypeCreationMethod::Structured;
- else if (qstrcmp(method, "construct") == 0)
- creationMethod = ValueTypeCreationMethod::Construct;
- }
-
- RegisterType typeRevision = {
- QQmlPrivate::RegisterType::CurrentVersion,
- type.typeId,
- type.listId,
- creatable ? type.objectSize : 0,
- nullptr,
- nullptr,
- noCreateReason,
- type.createValueType,
- type.uri,
- type.version,
- nullptr,
- type.metaObject,
- type.attachedPropertiesFunction,
- type.attachedPropertiesMetaObject,
- type.parserStatusCast,
- type.valueSourceCast,
- type.valueInterceptorCast,
- type.extensionObjectCreate,
- type.extensionMetaObject,
- nullptr,
- QTypeRevision(),
- type.structVersion > 0 ? type.finalizerCast : -1,
- creationMethod
- };
-
- QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
- 0,
- type.uri,
- type.version,
- nullptr,
- type.listId,
- type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
- QTypeRevision(),
- };
-
- const QTypeRevision added = revisionClassInfo(
- type.classInfoMetaObject, "QML.AddedInVersion",
- QTypeRevision::fromVersion(type.version.majorVersion(), 0));
- const QTypeRevision removed = revisionClassInfo(
- type.classInfoMetaObject, "QML.RemovedInVersion");
- const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
- "QML.ExtraVersion");
-
- auto revisions = prepareRevisions(type.metaObject, added) + furtherRevisions;
- if (type.attachedPropertiesMetaObject)
- revisions += availableRevisions(type.attachedPropertiesMetaObject);
- uniqueRevisions(&revisions, type.version, added);
-
- for (QTypeRevision revision : revisions) {
- if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
- break;
-
- assignVersions(&typeRevision, revision, type.version);
-
- // When removed or before added, we still add revisions, but anonymous ones
- if (typeRevision.version < added
- || (removed.isValid() && !(typeRevision.version < removed))) {
- typeRevision.elementName = nullptr;
- typeRevision.create = nullptr;
- typeRevision.userdata = nullptr;
+ if (type.structVersion > 1 && type.forceAnonymous) {
+ doRegisterTypeAndRevisions(type, {nullptr});
+ } else {
+ const ElementNames names = classElementNames(type.classInfoMetaObject);
+ if (names.isEmpty()) {
+ qWarning().nospace() << "Missing QML.Element class info for "
+ << type.classInfoMetaObject->className();
} else {
- typeRevision.elementName = elementName;
- typeRevision.create = creatable ? type.create : nullptr;
- typeRevision.userdata = type.userdata;
+ doRegisterTypeAndRevisions(type, names);
}
- typeRevision.customParser = type.customParserFactory();
- const int id = qmlregister(TypeRegistration, &typeRevision);
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
-
- if (sequenceRevision.metaSequence != QMetaSequence()) {
- sequenceRevision.version = typeRevision.version;
- sequenceRevision.revision = typeRevision.revision;
- const int id = QQmlPrivate::qmlregister(
- QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
- }
}
break;
}
case SingletonAndRevisionsRegistration: {
const RegisterSingletonTypeAndRevisions &type
= *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
- const char *elementName = classElementName(type.classInfoMetaObject);
- RegisterSingletonType revisionRegistration = {
- 0,
- type.uri,
- type.version,
- elementName,
- nullptr,
- type.qObjectApi,
- type.instanceMetaObject,
- type.typeId,
- type.extensionObjectCreate,
- type.extensionMetaObject,
- QTypeRevision()
- };
-
- const QTypeRevision added = revisionClassInfo(
- type.classInfoMetaObject, "QML.AddedInVersion",
- QTypeRevision::fromMinorVersion(0));
- const QTypeRevision removed = revisionClassInfo(
- type.classInfoMetaObject, "QML.RemovedInVersion");
- const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
- "QML.ExtraVersion");
-
- auto revisions = prepareRevisions(type.instanceMetaObject, added) + furtherRevisions;
- uniqueRevisions(&revisions, type.version, added);
-
- for (QTypeRevision revision : std::as_const(revisions)) {
- if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
- break;
-
- assignVersions(&revisionRegistration, revision, type.version);
-
- // When removed or before added, we still add revisions, but anonymous ones
- if (revisionRegistration.version < added
- || (removed.isValid() && !(revisionRegistration.version < removed))) {
- revisionRegistration.typeName = nullptr;
- revisionRegistration.qObjectApi = nullptr;
- } else {
- revisionRegistration.typeName = elementName;
- revisionRegistration.qObjectApi = type.qObjectApi;
- }
-
- const int id = qmlregister(SingletonRegistration, &revisionRegistration);
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
+ const ElementNames names = classElementNames(type.classInfoMetaObject);
+ if (names.isEmpty()) {
+ qWarning().nospace() << "Missing QML.Element class info for "
+ << type.classInfoMetaObject->className();
+ } else {
+ doRegisterSingletonAndRevisions(type, names);
}
break;
}
@@ -677,32 +840,30 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
break;
}
case TypeRegistration:
- dtype = QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data));
- break;
+ return finalizeType(
+ QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data)));
case InterfaceRegistration:
- dtype = QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data));
- break;
+ return finalizeType(
+ QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data)));
case SingletonRegistration:
- dtype = QQmlMetaType::registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerSingletonType(
+ *reinterpret_cast<RegisterSingletonType *>(data),
+ singletonInstanceInfo(*reinterpret_cast<RegisterSingletonType *>(data))));
case CompositeRegistration:
- dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerCompositeType(
+ *reinterpret_cast<RegisterCompositeType *>(data)));
case CompositeSingletonRegistration:
- dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerCompositeSingletonType(
+ *reinterpret_cast<RegisterCompositeSingletonType *>(data),
+ singletonInstanceInfo(*reinterpret_cast<RegisterCompositeSingletonType *>(data))));
case SequentialContainerRegistration:
- dtype = QQmlMetaType::registerSequentialContainer(*reinterpret_cast<RegisterSequentialContainer *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerSequentialContainer(
+ *reinterpret_cast<RegisterSequentialContainer *>(data)));
default:
return -1;
}
- if (!dtype.isValid())
- return -1;
-
- QQmlMetaType::registerUndeletableType(dtype);
- return dtype.index();
+ return -1;
}
void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
@@ -749,6 +910,13 @@ QList<QTypeRevision> QQmlPrivate::revisionClassInfos(const QMetaObject *metaObje
return revisions;
}
+int qmlRegisterTypeNotAvailable(
+ const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, const QString &message)
+{
+ return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(
+ uri, versionMajor, versionMinor, qmlName, message);
+}
namespace QQmlPrivate {
template<>
@@ -792,10 +960,21 @@ void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
qmlregister(TypeAndRevisionsRegistration, &type);
}
+QObject *AOTCompiledContext::thisObject() const
+{
+ return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame)
+ ->thisObject();
+}
QQmlEngine *AOTCompiledContext::qmlEngine() const
{
- return qmlContext ? qmlContext->engine() : nullptr;
+ return engine->handle()->qmlEngine();
+}
+
+static QQmlPropertyCapture *propertyCapture(const AOTCompiledContext *aotContext)
+{
+ QQmlEngine *engine = aotContext->qmlEngine();
+ return engine ? QQmlEnginePrivate::get(aotContext->qmlEngine())->propertyCapture : nullptr;
}
QJSValue AOTCompiledContext::jsMetaType(int index) const
@@ -818,37 +997,25 @@ void AOTCompiledContext::setReturnValueUndefined() const
}
}
-static QQmlPropertyCapture *propertyCapture(const QQmlContextData *qmlContext)
-{
- if (!qmlContext)
- return nullptr;
-
- QQmlEngine *engine = qmlContext->engine();
- Q_ASSERT(engine);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
- Q_ASSERT(ep);
- return ep->propertyCapture;
-}
-
static void captureFallbackProperty(
QObject *object, int coreIndex, int notifyIndex, bool isConstant,
- const QQmlContextData *qmlContext)
+ const AOTCompiledContext *aotContext)
{
if (isConstant)
return;
- if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
+ if (QQmlPropertyCapture *capture = propertyCapture(aotContext))
capture->captureProperty(object, coreIndex, notifyIndex);
}
static void captureObjectProperty(
QObject *object, const QQmlPropertyCache *propertyCache,
- const QQmlPropertyData *property, QQmlContextData *qmlContext)
+ const QQmlPropertyData *property, const AOTCompiledContext *aotContext)
{
if (property->isConstant())
return;
- if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
+ if (QQmlPropertyCapture *capture = propertyCapture(aotContext))
capture->captureProperty(object, propertyCache, property);
}
@@ -863,83 +1030,202 @@ static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCach
enum class ObjectPropertyResult { OK, NeedsInit, Deleted };
-static ObjectPropertyResult loadObjectProperty(
- QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
+struct ObjectPropertyQmlData
+{
+ QQmlData *qmlData;
+ ObjectPropertyResult result;
+};
+
+template<bool StrictType>
+ObjectPropertyQmlData findObjectPropertyQmlData(QV4::Lookup *l, QObject *object)
{
QQmlData *qmlData = QQmlData::get(object);
if (!qmlData)
- return ObjectPropertyResult::NeedsInit;
+ return {qmlData, ObjectPropertyResult::NeedsInit};
if (qmlData->isQueuedForDeletion)
- return ObjectPropertyResult::Deleted;
+ return {qmlData, ObjectPropertyResult::Deleted};
Q_ASSERT(!QQmlData::wasDeleted(object));
const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
- if (!inherits(qmlData->propertyCache.data(), propertyCache))
- return ObjectPropertyResult::NeedsInit;
- const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ if (StrictType) {
+ if (qmlData->propertyCache.data() != propertyCache)
+ return {qmlData, ObjectPropertyResult::NeedsInit};
+ } else if (!inherits(qmlData->propertyCache.data(), propertyCache)) {
+ return {qmlData, ObjectPropertyResult::NeedsInit};
+ }
+ return {qmlData, ObjectPropertyResult::OK};
+}
+
+template<bool StrictType = false>
+ObjectPropertyResult loadObjectProperty(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
- const int coreIndex = property->coreIndex();
- if (qmlData->hasPendingBindingBit(coreIndex))
- qmlData->flushPendingBinding(coreIndex);
+ const QQmlPropertyData *propertyData = l->qobjectLookup.propertyData;
+ const int coreIndex = propertyData->coreIndex();
+ if (data.qmlData->hasPendingBindingBit(coreIndex))
+ data.qmlData->flushPendingBinding(coreIndex);
- captureObjectProperty(object, propertyCache, property, qmlContext);
- property->readProperty(object, target);
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, propertyData, aotContext);
+ propertyData->readProperty(object, target);
return ObjectPropertyResult::OK;
}
-static ObjectPropertyResult loadFallbackProperty(
- QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
+template<bool StrictType = false>
+ObjectPropertyResult writeBackObjectProperty(QV4::Lookup *l, QObject *object, void *source)
+{
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ l->qobjectLookup.propertyData->writeProperty(object, source, {});
+ return ObjectPropertyResult::OK;
+}
+
+struct FallbackPropertyQmlData
+{
+ QQmlData *qmlData;
+ const QMetaObject *metaObject;
+ ObjectPropertyResult result;
+};
+
+static FallbackPropertyQmlData findFallbackPropertyQmlData(QV4::Lookup *l, QObject *object)
{
QQmlData *qmlData = QQmlData::get(object);
if (qmlData && qmlData->isQueuedForDeletion)
- return ObjectPropertyResult::Deleted;
+ return {qmlData, nullptr, ObjectPropertyResult::Deleted};
Q_ASSERT(!QQmlData::wasDeleted(object));
const QMetaObject *metaObject
= reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
if (!metaObject || metaObject != object->metaObject())
- return ObjectPropertyResult::NeedsInit;
+ return {qmlData, nullptr, ObjectPropertyResult::NeedsInit};
+
+ return {qmlData, metaObject, ObjectPropertyResult::OK};
+}
+
+static ObjectPropertyResult loadFallbackProperty(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
- if (qmlData && qmlData->hasPendingBindingBit(coreIndex))
- qmlData->flushPendingBinding(coreIndex);
+ if (data.qmlData && data.qmlData->hasPendingBindingBit(coreIndex))
+ data.qmlData->flushPendingBinding(coreIndex);
captureFallbackProperty(object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
- l->qobjectFallbackLookup.isConstant, qmlContext);
+ l->qobjectFallbackLookup.isConstant, aotContext);
void *a[] = { target, nullptr };
- metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
+ data.metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
return ObjectPropertyResult::OK;
}
-template<typename Op>
+static ObjectPropertyResult writeBackFallbackProperty(QV4::Lookup *l, QObject *object, void *source)
+{
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ void *a[] = { source, nullptr };
+ data.metaObject->metacall(
+ object, QMetaObject::WriteProperty, l->qobjectFallbackLookup.coreIndex, a);
+
+ return ObjectPropertyResult::OK;
+}
+
+ObjectPropertyResult loadObjectAsVariant(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ QVariant *variant = static_cast<QVariant *>(target);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return loadObjectProperty<true>(l, object, variant, aotContext);
+
+ *variant = QVariant(propType);
+ return loadObjectProperty<true>(l, object, variant->data(), aotContext);
+}
+
+ObjectPropertyResult writeBackObjectAsVariant(QV4::Lookup *l, QObject *object, void *source)
+{
+ QVariant *variant = static_cast<QVariant *>(source);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return writeBackObjectProperty<true>(l, object, variant);
+
+ Q_ASSERT(variant->metaType() == propType);
+ return writeBackObjectProperty<true>(l, object, variant->data());
+}
+
+ObjectPropertyResult loadFallbackAsVariant(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ QVariant *variant = static_cast<QVariant *>(target);
+ const QMetaType propType = metaObject->property(l->qobjectFallbackLookup.coreIndex).metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return loadFallbackProperty(l, object, variant, aotContext);
+
+ *variant = QVariant(propType);
+ return loadFallbackProperty(l, object, variant->data(), aotContext);
+}
+
+ObjectPropertyResult writeBackFallbackAsVariant(QV4::Lookup *l, QObject *object, void *source)
+{
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ QVariant *variant = static_cast<QVariant *>(source);
+ const QMetaType propType = metaObject->property(l->qobjectFallbackLookup.coreIndex).metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return writeBackFallbackProperty(l, object, variant);
+
+ Q_ASSERT(variant->metaType() == propType);
+ return writeBackFallbackProperty(l, object, variant->data());
+}
+
+template<bool StrictType, typename Op>
static ObjectPropertyResult changeObjectProperty(QV4::Lookup *l, QObject *object, Op op)
{
- const QQmlData *qmlData = QQmlData::get(object);
- if (!qmlData)
- return ObjectPropertyResult::NeedsInit;
- if (qmlData->isQueuedForDeletion)
- return ObjectPropertyResult::Deleted;
- Q_ASSERT(!QQmlData::wasDeleted(object));
- if (!inherits(qmlData->propertyCache.data(), l->qobjectLookup.propertyCache))
- return ObjectPropertyResult::NeedsInit;
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
const QQmlPropertyData *property = l->qobjectLookup.propertyData;
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
op(property);
return ObjectPropertyResult::OK;
}
-static ObjectPropertyResult resetObjectProperty(QV4::Lookup *l, QObject *object)
+template<bool StrictType = false>
+static ObjectPropertyResult resetObjectProperty(
+ QV4::Lookup *l, QObject *object, QV4::ExecutionEngine *v4)
{
- return changeObjectProperty(l, object, [&](const QQmlPropertyData *property) {
- property->resetProperty(object, {});
+ return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
+ if (property->isResettable()) {
+ property->resetProperty(object, {});
+ } else {
+ v4->throwError(
+ QLatin1String("Cannot assign [undefined] to ") +
+ QLatin1String(property->propType().name()));
+ }
});
}
+template<bool StrictType = false>
static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
{
- return changeObjectProperty(l, object, [&](const QQmlPropertyData *property) {
+ return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
property->writeProperty(object, value, {});
});
}
@@ -947,20 +1233,14 @@ static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object,
template<typename Op>
static ObjectPropertyResult changeFallbackProperty(QV4::Lookup *l, QObject *object, Op op)
{
- const QQmlData *qmlData = QQmlData::get(object);
- if (qmlData && qmlData->isQueuedForDeletion)
- return ObjectPropertyResult::Deleted;
- Q_ASSERT(!QQmlData::wasDeleted(object));
-
- const QMetaObject *metaObject
- = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
- if (!metaObject || metaObject != object->metaObject())
- return ObjectPropertyResult::NeedsInit;
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(coreIndex));
- op(metaObject, coreIndex);
+ op(data.metaObject, coreIndex);
return ObjectPropertyResult::OK;
}
@@ -972,11 +1252,18 @@ static ObjectPropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *objec
});
}
-static ObjectPropertyResult resetFallbackProperty(QV4::Lookup *l, QObject *object)
+static ObjectPropertyResult resetFallbackProperty(
+ QV4::Lookup *l, QObject *object, const QMetaProperty *property, QV4::ExecutionEngine *v4)
{
return changeFallbackProperty(l, object, [&](const QMetaObject *metaObject, int coreIndex) {
- void *args[] = { nullptr };
- metaObject->metacall(object, QMetaObject::ResetProperty, coreIndex, args);
+ if (property->isResettable()) {
+ void *args[] = { nullptr };
+ metaObject->metacall(object, QMetaObject::ResetProperty, coreIndex, args);
+ } else {
+ v4->throwError(
+ QLatin1String("Cannot assign [undefined] to ") +
+ QLatin1String(property->typeName()));
+ }
});
}
@@ -1003,19 +1290,97 @@ static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
if (!foundMetaObject)
return false;
- } else if (lookupType == QMetaType::fromType<int>()
- && propertyType.flags() & QMetaType::IsEnumeration) {
- return true;
+ } else if (propertyType.flags() & QMetaType::IsEnumeration) {
+ if (propertyType == lookupType)
+ return true;
+
+ // You can pass the underlying type of an enum.
+ // We don't want to check for the actual underlying type because
+ // moc and qmltyperegistrar are not very precise about it. Especially
+ // the long and longlong types can be ambiguous.
+
+ const bool isUnsigned = propertyType.flags() & QMetaType::IsUnsignedEnumeration;
+ switch (propertyType.sizeOf()) {
+ case 1:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<quint8>()
+ : lookupType == QMetaType::fromType<qint8>();
+ case 2:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<ushort>()
+ : lookupType == QMetaType::fromType<short>();
+ case 4:
+ // The default type, if moc doesn't know the actual enum type, is int.
+ // However, the compiler can still decide to encode the enum in uint.
+ // Therefore, we also accept int for uint enums.
+ // TODO: This is technically UB.
+ return isUnsigned
+ ? (lookupType == QMetaType::fromType<int>()
+ || lookupType == QMetaType::fromType<uint>())
+ : lookupType == QMetaType::fromType<int>();
+ case 8:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<qulonglong>()
+ : lookupType == QMetaType::fromType<qlonglong>();
+ }
+
+ return false;
} else if (propertyType != lookupType) {
return false;
}
return true;
}
+static ObjectPropertyResult storeObjectAsVariant(
+ QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
+{
+ QVariant *variant = static_cast<QVariant *>(value);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return storeObjectProperty<true>(l, object, variant);
+
+ if (!variant->isValid())
+ return resetObjectProperty<true>(l, object, v4);
+
+ if (isTypeCompatible(variant->metaType(), propType))
+ return storeObjectProperty<true>(l, object, variant->data());
+
+ QVariant converted(propType);
+ v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data());
+ return storeObjectProperty<true>(l, object, converted.data());
+}
+
+static ObjectPropertyResult storeFallbackAsVariant(
+ QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
+{
+ QVariant *variant = static_cast<QVariant *>(value);
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ const QMetaProperty property = metaObject->property(l->qobjectFallbackLookup.coreIndex);
+ const QMetaType propType = property.metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return storeFallbackProperty(l, object, variant);
+
+ if (!variant->isValid())
+ return resetFallbackProperty(l, object, &property, v4);
+
+ if (isTypeCompatible(variant->metaType(), propType))
+ return storeFallbackProperty(l, object, variant->data());
+
+ QVariant converted(propType);
+ v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data());
+ return storeFallbackProperty(l, object, converted.data());
+}
+
enum class ObjectLookupResult {
Failure,
Object,
- Fallback
+ Fallback,
+ ObjectAsVariant,
+ FallbackAsVariant,
};
static ObjectLookupResult initObjectLookup(
@@ -1045,6 +1410,7 @@ static ObjectLookupResult initObjectLookup(
name.getPointer(), object, aotContext->qmlContext);
}
+ const bool doVariantLookup = type == QMetaType::fromType<QVariant>();
if (!property) {
const QMetaObject *metaObject = object->metaObject();
if (!metaObject)
@@ -1056,7 +1422,7 @@ static ObjectLookupResult initObjectLookup(
return ObjectLookupResult::Failure;
const QMetaProperty property = metaObject->property(coreIndex);
- if (!isTypeCompatible(type, property.metaType()))
+ if (!doVariantLookup && !isTypeCompatible(type, property.metaType()))
return ObjectLookupResult::Failure;
l->releasePropertyCache();
@@ -1066,16 +1432,21 @@ static ObjectLookupResult initObjectLookup(
l->qobjectFallbackLookup.notifyIndex =
QMetaObjectPrivate::signalIndex(property.notifySignal());
l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
- return ObjectLookupResult::Fallback;
+ return doVariantLookup
+ ? ObjectLookupResult::FallbackAsVariant
+ : ObjectLookupResult::Fallback;
}
- if (!isTypeCompatible(type, property->propType()))
+ if (!doVariantLookup && !isTypeCompatible(type, property->propType()))
return ObjectLookupResult::Failure;
Q_ASSERT(ddata->propertyCache);
QV4::setupQObjectLookup(l, ddata, property);
- return ObjectLookupResult::Object;
+
+ return doVariantLookup
+ ? ObjectLookupResult::ObjectAsVariant
+ : ObjectLookupResult::Object;
}
static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
@@ -1095,13 +1466,25 @@ static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *comp
static void amendException(QV4::ExecutionEngine *engine)
{
+ const int missingLineNumber = engine->currentStackFrame->missingLineNumber();
const int lineNumber = engine->currentStackFrame->lineNumber();
- engine->exceptionStackTrace.front().line = lineNumber;
+ Q_ASSERT(missingLineNumber != lineNumber);
+
+ auto amendStackTrace = [&](QV4::StackTrace *stackTrace) {
+ for (auto it = stackTrace->begin(), end = stackTrace->end(); it != end; ++it) {
+ if (it->line == missingLineNumber) {
+ it->line = lineNumber;
+ break;
+ }
+ }
+ };
+
+ amendStackTrace(&engine->exceptionStackTrace);
QV4::Scope scope(engine);
QV4::Scoped<QV4::ErrorObject> error(scope, *engine->exceptionValue);
if (error) // else some other value was thrown
- error->d()->stackTrace->front().line = lineNumber;
+ amendStackTrace(error->d()->stackTrace);
}
@@ -1112,19 +1495,21 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
- || l->getter == QV4::Lookup::getterQObject) {
+ || l->getter == QV4::Lookup::getterQObject
+ || l->getter == QV4::Lookup::getterQObjectAsVariant) {
const QQmlPropertyData *property = l->qobjectLookup.propertyData;
QQmlData::flushPendingBinding(object, property->coreIndex());
- captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext);
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, property, this);
return true;
}
- if (l->getter == QV4::Lookup::getterFallback) {
+ if (l->getter == QV4::Lookup::getterFallback
+ || l->getter == QV4::Lookup::getterFallbackAsVariant) {
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
QQmlData::flushPendingBinding(object, coreIndex);
captureFallbackProperty(
object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
- l->qobjectFallbackLookup.isConstant, qmlContext);
+ l->qobjectFallbackLookup.isConstant, this);
return true;
}
@@ -1138,7 +1523,7 @@ bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
&& l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) {
const QQmlPropertyData *property = l->qobjectLookup.propertyData;
QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex());
- captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext);
+ captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, this);
return true;
}
@@ -1146,7 +1531,7 @@ bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
QQmlData::flushPendingBinding(qmlScopeObject, coreIndex);
captureFallbackProperty(qmlScopeObject, coreIndex, l->qobjectFallbackLookup.notifyIndex,
- l->qobjectFallbackLookup.isConstant, qmlContext);
+ l->qobjectFallbackLookup.isConstant, this);
return true;
}
@@ -1155,7 +1540,7 @@ bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
void AOTCompiledContext::captureTranslation() const
{
- if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
+ if (QQmlPropertyCapture *capture = propertyCapture(this))
capture->captureTranslation();
}
@@ -1175,7 +1560,9 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty
|| l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
|| l->getter == QV4::Lookup::getterQObject
- || l->setter == QV4::Lookup::setterQObject) {
+ || l->setter == QV4::Lookup::setterQObject
+ || l->getter == QV4::Lookup::getterQObjectAsVariant
+ || l->setter == QV4::Lookup::setterQObjectAsVariant) {
return l->qobjectLookup.propertyData->propType();
} else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
return QMetaType(l->qgadgetLookup.metaType);
@@ -1188,6 +1575,8 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
return QMetaType::fromType<QObject *>();
} else if (l->getter == QV4::Lookup::getterFallback
|| l->setter == QV4::Lookup::setterFallback
+ || l->getter == QV4::Lookup::getterFallbackAsVariant
+ || l->setter == QV4::Lookup::setterFallbackAsVariant
|| l->qmlContextPropertyGetter
== QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
const QMetaObject *metaObject
@@ -1218,38 +1607,42 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
// the property cache we store a value into the property.
QV4::Lookup l;
- l.clear();
+ memset(&l, 0, sizeof(QV4::Lookup));
l.nameIndex = nameIndex;
l.forCall = false;
ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
+ case ObjectLookupResult::ObjectAsVariant:
case ObjectLookupResult::Object: {
const QMetaType propType = l.qobjectLookup.propertyData->propType();
- if (type == propType) {
+ if (isTypeCompatible(type, propType)) {
storeResult = storeObjectProperty(&l, qmlScopeObject, value);
} else if (isUndefined(value, type)) {
- storeResult = resetObjectProperty(&l, qmlScopeObject);
+ storeResult = resetObjectProperty(&l, qmlScopeObject, engine->handle());
} else {
QVariant var(propType);
- propType.convert(type, value, propType, var.data());
+ QV4::ExecutionEngine *v4 = engine->handle();
+ v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
storeResult = storeObjectProperty(&l, qmlScopeObject, var.data());
}
l.qobjectLookup.propertyCache->release();
break;
}
+ case ObjectLookupResult::FallbackAsVariant:
case ObjectLookupResult::Fallback: {
const QMetaObject *metaObject
= reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
- const QMetaType propType
- = metaObject->property(l.qobjectFallbackLookup.coreIndex).metaType();
- if (type == propType) {
+ const QMetaProperty property = metaObject->property(l.qobjectFallbackLookup.coreIndex);
+ const QMetaType propType = property.metaType();
+ if (isTypeCompatible(type, propType)) {
storeResult = storeFallbackProperty(&l, qmlScopeObject, value);
} else if (isUndefined(value, type)) {
- storeResult = resetFallbackProperty(&l, qmlScopeObject);
+ storeResult = resetFallbackProperty(&l, qmlScopeObject, &property, engine->handle());
} else {
QVariant var(propType);
- propType.convert(type, value, propType, var.data());
+ QV4::ExecutionEngine *v4 = engine->handle();
+ v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data());
}
break;
@@ -1309,8 +1702,10 @@ void AOTCompiledContext::writeToConsole(
const QV4::CppStackFrame *frame = engine->handle()->currentStackFrame;
Q_ASSERT(frame);
- QMessageLogger logger(qUtf8Printable(frame->source()), frame->lineNumber(),
- qUtf8Printable(frame->function()), loggingCategory->categoryName());
+ const QByteArray source(frame->source().toUtf8());
+ const QByteArray function(frame->function().toUtf8());
+ QMessageLogger logger(source.constData(), frame->lineNumber(),
+ function.constData(), loggingCategory->categoryName());
switch (type) {
case QtDebugMsg:
@@ -1330,6 +1725,32 @@ void AOTCompiledContext::writeToConsole(
}
}
+QVariant AOTCompiledContext::constructValueType(
+ QMetaType resultMetaType, const QMetaObject *resultMetaObject,
+ int ctorIndex, void *ctorArg) const
+{
+ return QQmlValueTypeProvider::constructValueType(
+ resultMetaType, resultMetaObject, ctorIndex, ctorArg);
+}
+
+QDateTime AOTCompiledContext::constructDateTime(double timestamp) const
+{
+ return QV4::DateObject::timestampToDateTime(timestamp);
+}
+
+QDateTime AOTCompiledContext::constructDateTime(const QString &string) const
+{
+ return QV4::DateObject::stringToDateTime(string, engine->handle());
+}
+
+QDateTime AOTCompiledContext::constructDateTime(
+ double year, double month, double day, double hours,
+ double minutes, double seconds, double msecs) const
+{
+ return constructDateTime(QV4::DateObject::componentsToTimestamp(
+ year, month, day, hours, minutes, seconds, msecs, engine->handle()));
+}
+
bool AOTCompiledContext::callQmlContextPropertyLookup(
uint index, void **args, const QMetaType *types, int argc) const
{
@@ -1483,11 +1904,17 @@ bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target)
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (!qmlScopeObject) {
+ engine->handle()->throwReferenceError(
+ compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+ return false;
+ }
+
ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
- result = loadObjectProperty(l, qmlScopeObject, target, qmlContext);
+ result = loadObjectProperty(l, qmlScopeObject, target, this);
else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
- result = loadFallbackProperty(l, qmlScopeObject, target, qmlContext);
+ result = loadFallbackProperty(l, qmlScopeObject, target, this);
else
return false;
@@ -1506,6 +1933,29 @@ bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target)
Q_UNREACHABLE_RETURN(false);
}
+bool AOTCompiledContext::writeBackScopeObjectPropertyLookup(uint index, void *source) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+ result = writeBackObjectProperty(l, qmlScopeObject, source);
+ else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
+ result = writeBackFallbackProperty(l, qmlScopeObject, source);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::Deleted: // Silently omit the write back. Same as interpreter
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType type) const
{
QV4::ExecutionEngine *v4 = engine->handle();
@@ -1517,9 +1967,11 @@ void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType
}
switch (initObjectLookup(this, l, qmlScopeObject, type)) {
+ case ObjectLookupResult::ObjectAsVariant:
case ObjectLookupResult::Object:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
break;
+ case ObjectLookupResult::FallbackAsVariant:
case ObjectLookupResult::Fallback:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
break;
@@ -1558,17 +2010,21 @@ static void initTypeWrapperLookup(
if (importNamespace != AOTCompiledContext::InvalidStringId) {
QV4::Scope scope(context->engine->handle());
QV4::ScopedString import(scope, context->compilationUnit->runtimeStrings[importNamespace]);
+
+ QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
+ Q_ASSERT(typeLoader);
if (const QQmlImportRef *importRef
- = context->qmlContext->imports()->query(import).importNamespace) {
+ = context->qmlContext->imports()->query(import, typeLoader).importNamespace) {
+
QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
scope, QV4::QQmlTypeWrapper::create(
scope.engine, nullptr, context->qmlContext->imports(), importRef));
wrapper = l->qmlContextPropertyGetter(l, context->engine->handle(), wrapper);
l->qmlContextPropertyGetter = qmlContextPropertyGetter;
if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton)
- l->qmlContextSingletonLookup.singletonObject = wrapper->heapObject();
+ l->qmlContextSingletonLookup.singletonObject.set(scope.engine, wrapper->heapObject());
else if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType)
- l->qmlTypeLookup.qmlTypeWrapper = wrapper->heapObject();
+ l->qmlTypeLookup.qmlTypeWrapper.set(scope.engine, wrapper->heapObject());
return;
}
scope.engine->throwTypeError();
@@ -1618,12 +2074,16 @@ void AOTCompiledContext::initLoadAttachedLookup(
QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
QQmlType type;
+ QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
+ Q_ASSERT(typeLoader);
if (importNamespace != InvalidStringId) {
QV4::ScopedString import(scope, compilationUnit->runtimeStrings[importNamespace]);
- if (const QQmlImportRef *importRef = qmlContext->imports()->query(import).importNamespace)
- type = qmlContext->imports()->query(name, importRef).type;
+ if (const QQmlImportRef *importRef
+ = qmlContext->imports()->query(import, typeLoader).importNamespace) {
+ type = qmlContext->imports()->query(name, importRef, typeLoader).type;
+ }
} else {
- type = qmlContext->imports()->query<QQmlImport::AllowRecursion>(name).type;
+ type = qmlContext->imports()->query<QQmlImport::AllowRecursion>(name, typeLoader).type;
}
if (!type.isValid()) {
@@ -1635,7 +2095,7 @@ void AOTCompiledContext::initLoadAttachedLookup(
scope, QV4::QQmlTypeWrapper::create(scope.engine, object, type,
QV4::Heap::QQmlTypeWrapper::ExcludeEnums));
- l->qmlTypeLookup.qmlTypeWrapper = wrapper->d();
+ l->qmlTypeLookup.qmlTypeWrapper.set(scope.engine, wrapper->d());
l->getter = QV4::QObjectWrapper::lookupAttached;
}
@@ -1646,15 +2106,9 @@ bool AOTCompiledContext::loadTypeLookup(uint index, void *target) const
return false;
const QV4::Heap::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::Heap::QQmlTypeWrapper *>(
- l->qmlTypeLookup.qmlTypeWrapper);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine());
+ l->qmlTypeLookup.qmlTypeWrapper.get());
QMetaType metaType = typeWrapper->type().typeId();
- if (!metaType.isValid()) {
- metaType = ep->typeLoader.getType(typeWrapper->type().sourceUrl())
- ->compilationUnit()->typeIds.id;
- }
-
*static_cast<const QMetaObject **>(target)
= QQmlMetaType::metaObjectForType(metaType).metaObject();
return true;
@@ -1668,7 +2122,6 @@ void AOTCompiledContext::initLoadTypeLookup(uint index, uint importNamespace) co
bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *target) const
{
-
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
const auto doThrow = [&]() {
engine->handle()->throwTypeError(
@@ -1682,9 +2135,13 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
if (l->getter == QV4::Lookup::getterQObject)
- result = loadObjectProperty(l, object, target, qmlContext);
+ result = loadObjectProperty(l, object, target, this);
else if (l->getter == QV4::Lookup::getterFallback)
- result = loadFallbackProperty(l, object, target, qmlContext);
+ result = loadFallbackProperty(l, object, target, this);
+ else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
+ result = loadObjectAsVariant(l, object, target, this);
+ else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
+ result = loadFallbackAsVariant(l, object, target, this);
else
return false;
@@ -1700,6 +2157,35 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
Q_UNREACHABLE_RETURN(false);
}
+bool AOTCompiledContext::writeBackObjectLookup(uint index, QObject *object, void *source) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (!object)
+ return true;
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->getter == QV4::Lookup::getterQObject)
+ result = writeBackObjectProperty(l, object, source);
+ else if (l->getter == QV4::Lookup::getterFallback)
+ result = writeBackFallbackProperty(l, object, source);
+ else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
+ result = writeBackObjectAsVariant(l, object, source);
+ else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
+ result = writeBackFallbackAsVariant(l, object, source);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::Deleted: // Silently omit the write back
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaType type) const
{
QV4::ExecutionEngine *v4 = engine->handle();
@@ -1711,9 +2197,15 @@ void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaT
case ObjectLookupResult::Object:
l->getter = QV4::Lookup::getterQObject;
break;
+ case ObjectLookupResult::ObjectAsVariant:
+ l->getter = QV4::Lookup::getterQObjectAsVariant;
+ break;
case ObjectLookupResult::Fallback:
l->getter = QV4::Lookup::getterFallback;
break;
+ case ObjectLookupResult::FallbackAsVariant:
+ l->getter = QV4::Lookup::getterFallbackAsVariant;
+ break;
case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
break;
@@ -1740,6 +2232,25 @@ bool AOTCompiledContext::getValueLookup(uint index, void *value, void *target) c
return true;
}
+bool AOTCompiledContext::writeBackValueLookup(uint index, void *value, void *source) const
+{
+ Q_ASSERT(value);
+
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->getter != QV4::QQmlValueTypeWrapper::lookupGetter)
+ return false;
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ void *args[] = { source, nullptr };
+ metaObject->d.static_metacall(
+ reinterpret_cast<QObject*>(value), QMetaObject::WriteProperty,
+ l->qgadgetLookup.coreIndex, args);
+ return true;
+}
+
void AOTCompiledContext::initGetValueLookup(
uint index, const QMetaObject *metaObject, QMetaType type) const
{
@@ -1751,13 +2262,44 @@ void AOTCompiledContext::initGetValueLookup(
engine->handle()->throwTypeError();
}
-bool AOTCompiledContext::getEnumLookup(uint index, int *target) const
+bool AOTCompiledContext::getEnumLookup(uint index, void *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
if (l->getter != QV4::QQmlTypeWrapper::lookupEnumValue)
return false;
- *target = l->qmlEnumValueLookup.encodedEnumValue;
- return true;
+ const bool isUnsigned
+ = l->qmlEnumValueLookup.metaType->flags & QMetaType::IsUnsignedEnumeration;
+ const QV4::ReturnedValue encoded = l->qmlEnumValueLookup.encodedEnumValue;
+ switch (l->qmlEnumValueLookup.metaType->size) {
+ case 1:
+ if (isUnsigned)
+ *static_cast<quint8 *>(target) = encoded;
+ else
+ *static_cast<qint8 *>(target) = encoded;
+ return true;
+ case 2:
+ if (isUnsigned)
+ *static_cast<quint16 *>(target) = encoded;
+ else
+ *static_cast<qint16 *>(target) = encoded;
+ return true;
+ case 4:
+ if (isUnsigned)
+ *static_cast<quint32 *>(target) = encoded;
+ else
+ *static_cast<qint32 *>(target) = encoded;
+ return true;
+ case 8:
+ if (isUnsigned)
+ *static_cast<quint64 *>(target) = encoded;
+ else
+ *static_cast<qint64 *>(target) = encoded;
+ return true;
+ default:
+ break;
+ }
+
+ return false;
}
void AOTCompiledContext::initGetEnumLookup(
@@ -1766,10 +2308,16 @@ void AOTCompiledContext::initGetEnumLookup(
{
Q_ASSERT(!engine->hasError());
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- Q_ASSERT(metaObject);
+ if (!metaObject) {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Cannot read property '%1' of undefined")
+ .arg(QString::fromUtf8(enumValue)));
+ return;
+ }
const int enumIndex = metaObject->indexOfEnumerator(enumerator);
- const int value = metaObject->enumerator(enumIndex).keyToValue(enumValue);
- l->qmlEnumValueLookup.encodedEnumValue = value;
+ const QMetaEnum metaEnum = metaObject->enumerator(enumIndex);
+ l->qmlEnumValueLookup.encodedEnumValue = metaEnum.keyToValue(enumValue);
+ l->qmlEnumValueLookup.metaType = metaEnum.metaType().iface();
l->getter = QV4::QQmlTypeWrapper::lookupEnumValue;
}
@@ -1790,6 +2338,10 @@ bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *valu
result = storeObjectProperty(l, object, value);
else if (l->setter == QV4::Lookup::setterFallback)
result = storeFallbackProperty(l, object, value);
+ else if (l->setter == QV4::Lookup::setterQObjectAsVariant)
+ result = storeObjectAsVariant(engine->handle(), l, object, value);
+ else if (l->setter == QV4::Lookup::setterFallbackAsVariant)
+ result = storeFallbackAsVariant(engine->handle(), l, object, value);
else
return false;
@@ -1816,9 +2368,15 @@ void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaT
case ObjectLookupResult::Object:
l->setter = QV4::Lookup::setterQObject;
break;
+ case ObjectLookupResult::ObjectAsVariant:
+ l->setter = QV4::Lookup::setterQObjectAsVariant;
+ break;
case ObjectLookupResult::Fallback:
l->setter = QV4::Lookup::setterFallback;
break;
+ case ObjectLookupResult::FallbackAsVariant:
+ l->setter = QV4::Lookup::setterFallbackAsVariant;
+ break;
case ObjectLookupResult::Failure:
engine->handle()->throwTypeError();
break;
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 1695a78bbf..3e6441bfa7 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -133,8 +133,16 @@ void qmlRegisterAnonymousTypesAndRevisions(const char *uri, int versionMajor)
nullptr, true);
}
+class QQmlTypeNotAvailable : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(TypeNotAvailable)
+ QML_ADDED_IN_VERSION(2, 15)
+ QML_UNCREATABLE("Type not available.")
+};
+
int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor,
- const char *qmlName, const QString& message);
+ const char *qmlName, const QString &message);
template<typename T>
int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
@@ -662,37 +670,25 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true)
return qmlAttachedPropertiesObject(const_cast<QObject *>(obj), func, create);
}
-inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
- QJSValue (*callback)(QQmlEngine *, QJSEngine *))
-{
- QQmlPrivate::RegisterSingletonType api = {
- 0,
-
- uri, QTypeRevision::fromVersion(versionMajor, versionMinor), typeName,
-
- callback,
- nullptr, nullptr, QMetaType(),
- nullptr, nullptr,
- QTypeRevision::zero()
- };
-
- return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api);
-}
-
-template <typename T>
-inline int qmlRegisterSingletonType(
- const char *uri, int versionMajor, int versionMinor, const char *typeName,
- QObject *(*callback)(QQmlEngine *, QJSEngine *))
+#ifdef Q_QDOC
+int qmlRegisterSingletonType(
+ const char *uri, int versionMajor, int versionMinor, const char *typeName,
+ std::function<QJSValue(QQmlEngine *, QJSEngine *)> callback)
+#else
+template<typename F, typename std::enable_if<std::is_convertible<F, std::function<QJSValue(QQmlEngine *, QJSEngine *)>>::value, void>::type* = nullptr>
+int qmlRegisterSingletonType(
+ const char *uri, int versionMajor, int versionMinor, const char *typeName, F &&callback)
+#endif
{
QQmlPrivate::RegisterSingletonType api = {
0,
uri,
QTypeRevision::fromVersion(versionMajor, versionMinor),
typeName,
+ std::forward<F>(callback),
nullptr,
- callback,
- QQmlPrivate::StaticMetaObject<T>::staticMetaObject(),
- QQmlPrivate::QmlMetaType<T>::self(),
+ nullptr,
+ QMetaType(),
nullptr, nullptr,
QTypeRevision::zero()
};
@@ -702,12 +698,13 @@ inline int qmlRegisterSingletonType(
#ifdef Q_QDOC
template <typename T>
-int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject*(QQmlEngine *, QJSEngine *)> callback)
+int qmlRegisterSingletonType(
+ const char *uri, int versionMajor, int versionMinor, const char *typeName,
+ std::function<QObject *(QQmlEngine *, QJSEngine *)> callback)
#else
-template <typename T, typename F, typename std::enable_if<std::is_convertible<F, std::function<QObject *(QQmlEngine *, QJSEngine *)>>::value
- && !std::is_convertible<F, QObject *(*)(QQmlEngine *, QJSEngine *)>::value, void>::type* = nullptr>
-inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
- F&& callback)
+template<typename T, typename F, typename std::enable_if<std::is_convertible<F, std::function<QObject *(QQmlEngine *, QJSEngine *)>>::value, void>::type* = nullptr>
+int qmlRegisterSingletonType(
+ const char *uri, int versionMajor, int versionMinor, const char *typeName, F &&callback)
#endif
{
QQmlPrivate::RegisterSingletonType api = {
@@ -716,7 +713,7 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi
QTypeRevision::fromVersion(versionMajor, versionMinor),
typeName,
nullptr,
- callback,
+ std::forward<F>(callback),
QQmlPrivate::StaticMetaObject<T>::staticMetaObject(),
QQmlPrivate::QmlMetaType<T>::self(),
nullptr, nullptr,
@@ -818,8 +815,8 @@ struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, false, false, fals
}
#else
static_assert(QQmlPrivate::QmlMetaType<Resolved>::hasAcceptableCtors(),
- "This type is neither a QObject, nor default- and copy-constructible, nor"
- "uncreatable.\n"
+ "This type is neither a default constructible QObject, nor a default- "
+ "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
"You should not use it as a QML type.");
static_assert(std::is_base_of_v<QObject, Resolved>
|| !QQmlTypeInfo<Resolved>::hasAttachedProperties);
@@ -871,12 +868,14 @@ struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, true, false, false
{
#if QT_DEPRECATED_SINCE(6, 4)
// ### Qt7: Remove the warning, and leave only the static assert below.
- if constexpr (!QQmlPrivate::QmlMetaType<Resolved>::hasAcceptableSingletonCtors()) {
+ if constexpr (QQmlPrivate::singletonConstructionMode<Resolved, T>()
+ == QQmlPrivate::SingletonConstructionMode::None) {
QQmlPrivate::qmlRegistrationWarning(QQmlPrivate::UnconstructibleSingleton,
QMetaType::fromType<Resolved>());
}
#else
- static_assert(QQmlPrivate::QmlMetaType<Resolved>::hasAcceptableSingletonCtors(),
+ static_assert(QQmlPrivate::singletonConstructionMode<Resolved, T>()
+ != QQmlPrivate::SingletonConstructionMode::None,
"A singleton needs either a default constructor or, when adding a default "
"constructor is infeasible, a public static "
"create(QQmlEngine *, QJSEngine *) method");
@@ -920,7 +919,7 @@ void qmlRegisterTypesAndRevisions(const char *uri, int versionMajor, QList<int>
QQmlPrivate::QmlSingleton<T>::Value,
QQmlPrivate::QmlInterface<T>::Value,
QQmlPrivate::QmlSequence<T>::Value,
- QQmlPrivate::QmlUncreatable<T>::Value>
+ QQmlPrivate::QmlUncreatable<T>::Value || QQmlPrivate::QmlAnonymous<T>::Value>
::registerTypeAndRevisions(uri, versionMajor, qmlTypeIds,
QQmlPrivate::QmlExtendedNamespace<T>::metaObject()), ...);
}
diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h
index d4c00ea886..8230c6aa6b 100644
--- a/src/qml/qml/qqmlabstractbinding_p.h
+++ b/src/qml/qml/qqmlabstractbinding_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQmlObjectCreator;
class QQmlAnyBinding;
-class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding
+class Q_QML_EXPORT QQmlAbstractBinding
{
friend class QQmlAnyBinding;
protected:
diff --git a/src/qml/qml/qqmlanybinding_p.h b/src/qml/qml/qqmlanybinding_p.h
index fbe4c12350..f432d2abae 100644
--- a/src/qml/qml/qqmlanybinding_p.h
+++ b/src/qml/qml/qqmlanybinding_p.h
@@ -73,10 +73,10 @@ public:
{
QQmlAnyBinding binding;
Q_ASSERT(object);
- QQmlData *data = QQmlData::get(object, true);
auto coreIndex = index.coreIndex();
// we don't support bindable properties on value types so far
- if (!index.hasValueTypeIndex() && data->propertyCache->property(coreIndex)->isBindable()) {
+ if (!index.hasValueTypeIndex()
+ && QQmlData::ensurePropertyCache(object)->property(coreIndex)->isBindable()) {
auto metaProp = object->metaObject()->property(coreIndex);
QUntypedBindable bindable = metaProp.bindable(object);
binding = bindable.binding();
@@ -459,8 +459,7 @@ private:
delete qqmlptr;
} else if (d.isT2()) {
QPropertyBindingPrivate *priv = d.asT2();
- priv->ref--;
- if (!priv->ref)
+ if (!priv->deref())
QPropertyBindingPrivate::destroyAndFreeMemory(priv);
}
d = static_cast<QQmlAbstractBinding *>(nullptr);
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index 869291caad..82cc335c8e 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -7,10 +7,10 @@
#include <QQmlComponent>
#include "qqmlapplicationengine.h"
#include "qqmlapplicationengine_p.h"
+#include <QtQml/private/qqmlcomponent_p.h>
+#include <QtQml/private/qqmldirdata_p.h>
#include <QtQml/private/qqmlfileselector_p.h>
-#include <memory>
-
QT_BEGIN_NAMESPACE
QQmlApplicationEnginePrivate::QQmlApplicationEnginePrivate(QQmlEngine *e)
@@ -109,15 +109,36 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray &
ensureLoadingFinishes(c);
}
-void QQmlApplicationEnginePrivate::startLoad(QAnyStringView uri, QAnyStringView type)
+void QQmlApplicationEnginePrivate::startLoad(QAnyStringView uri, QAnyStringView typeName)
{
Q_Q(QQmlApplicationEngine);
- _q_loadTranslations(); //Translations must be loaded before the QML file is
QQmlComponent *c = new QQmlComponent(q, q);
ensureInitialized();
- c->loadFromModule(uri, type);
+
+ auto *componentPriv = QQmlComponentPrivate::get(c);
+ const auto [status, type] = componentPriv->prepareLoadFromModule(uri, typeName);
+
+ if (type.sourceUrl().isValid()) {
+ const auto qmlDirData = typeLoader.getQmldir(type.sourceUrl());
+ const QUrl url = qmlDirData->finalUrl();
+ if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc")) {
+ QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
+ translationsDirectory = fi.path() + QLatin1String("/i18n");
+ } else {
+ translationsDirectory.clear();
+ }
+ }
+
+ /* Translations must be loaded before the QML file. They require translationDirectory to
+ * already be resolved. But, in order to resolve the translationDirectory, the type of the
+ * module to load needs to be known. Therefore, loadFromModule is split into resolution and
+ * loading because the translation directory needs to be set in between.
+ */
+ _q_loadTranslations();
+ componentPriv->completeLoadFromModule(uri, typeName, type, status);
+
ensureLoadingFinishes(c);
}
@@ -236,10 +257,9 @@ void QQmlApplicationEnginePrivate::ensureLoadingFinishes(QQmlComponent *c)
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
- // quit on error
- QObject::connect(&app, QQmlApplicationEngine::objectCreationFailed,
- QCoreApplication::instance(), QCoreApplication::quit,
- Qt::QueuedConnection);
+ // exit on error
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
+ &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
engine.load(QUrl());
return app.exec();
\endcode
@@ -354,6 +374,11 @@ void QQmlApplicationEngine::load(const QString &filePath)
engine.loadFromModule("QtQuick", "Rectangle");
\endcode
+ \note The module identified by \a uri is searched in the
+ \l {QML Import Path}{import path}, in the same way as if
+ you were doing \c{import uri} inside a QML file. If the
+ module cannot be located there, this function will fail.
+
\since 6.5
\sa QQmlComponent::loadFromModule
*/
diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h
index 4cf21bba6d..df99783e0a 100644
--- a/src/qml/qml/qqmlapplicationengine_p.h
+++ b/src/qml/qml/qqmlapplicationengine_p.h
@@ -24,7 +24,7 @@
QT_BEGIN_NAMESPACE
class QFileSelector;
-class Q_QML_PRIVATE_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePrivate
+class Q_QML_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePrivate
{
Q_DECLARE_PUBLIC(QQmlApplicationEngine)
public:
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index cd4af5c839..47f8e5c429 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -29,6 +29,9 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtqml, QQmlBinding_entry, const QQmlEngine *engine, const QString &function, const QString &fileName, int line, int column)
+Q_TRACE_POINT(qtqml, QQmlBinding_exit)
+
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
{
QQmlBinding *b = newBinding(property);
@@ -254,7 +257,7 @@ protected:
break;
default:
if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
- if (vtw->d()->valueType()->metaType() == pd->propType()) {
+ if (vtw->d()->metaType() == pd->propType()) {
return vtw->write(m_target.data(), pd->coreIndex());
}
}
@@ -407,6 +410,8 @@ QQmlBinding *QQmlBinding::createTranslationBinding(
location.line, location.column });
}
+#else
+ Q_UNUSED(propertyName)
#endif
return b;
}
@@ -670,22 +675,23 @@ void QQmlBinding::doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::Write
auto canWrite = [&]() { return !watcher.wasDeleted() && isAddedToObject() && !hasError(); };
const QV4::Function *v4Function = function();
if (v4Function && v4Function->kind == QV4::Function::AotCompiled && !hasBoundFunction()) {
- const auto returnType = v4Function->typedFunction->returnType;
+ const auto returnType = v4Function->aotCompiledFunction.types[0];
if (returnType == QMetaType::fromType<QVariant>()) {
- // It expects uninitialized memory
- Q_ALLOCA_VAR(QVariant, result, sizeof(QVariant));
- const bool isUndefined = !evaluate(result, returnType);
+ QVariant result;
+ const bool isUndefined = !evaluate(&result, returnType);
if (canWrite())
- error = !write(result->data(), result->metaType(), isUndefined, flags);
- result->~QVariant();
+ error = !write(result.data(), result.metaType(), isUndefined, flags);
} else {
const auto size = returnType.sizeOf();
if (Q_LIKELY(size > 0)) {
Q_ALLOCA_VAR(void, result, size);
+ if (returnType.flags() & QMetaType::NeedsConstruction)
+ returnType.construct(result);
const bool isUndefined = !evaluate(result, returnType);
if (canWrite())
error = !write(result, returnType, isUndefined, flags);
- returnType.destruct(result);
+ if (returnType.flags() & QMetaType::NeedsDestruction)
+ returnType.destruct(result);
} else if (canWrite()) {
error = !write(QV4::Encode::undefined(), true, flags);
}
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index 00aa0d6f58..4031a2655e 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -29,7 +29,7 @@
QT_BEGIN_NAMESPACE
class QQmlContext;
-class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression,
+class Q_QML_EXPORT QQmlBinding : public QQmlJavaScriptExpression,
public QQmlAbstractBinding
{
friend class QQmlAbstractBinding;
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index da81244f18..3f9ce26764 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -24,6 +24,10 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtqml, QQmlHandlingSignal_entry, const QQmlEngine *engine, const QString &function,
+ const QString &fileName, int line, int column)
+Q_TRACE_POINT(qtqml, QQmlHandlingSignal_exit)
+
QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope,
const QString &expression, const QString &fileName, quint16 line, quint16 column,
const QString &handlerName, const QString &parameterString)
@@ -175,7 +179,7 @@ void QQmlBoundSignalExpression::evaluate(void **a)
QMetaObjectPrivate::signal(targetMeta, m_index).methodIndex());
int argCount = metaMethod.parameterCount();
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
storage.reserve(argCount + 1);
storage.append(QMetaType()); // We're not interested in the return value
for (int i = 0; i < argCount; ++i) {
@@ -183,7 +187,7 @@ void QQmlBoundSignalExpression::evaluate(void **a)
if (!type.isValid())
argCount = 0;
else if (type.flags().testFlag(QMetaType::IsEnumeration))
- storage.append(QMetaType::fromType<int>());
+ storage.append(type.underlyingType());
else
storage.append(type);
}
diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h
index 42612b1df7..9eab562f56 100644
--- a/src/qml/qml/qqmlboundsignal_p.h
+++ b/src/qml/qml/qqmlboundsignal_p.h
@@ -24,8 +24,11 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlJavaScriptExpression, public QQmlRefCount
+class Q_QML_EXPORT QQmlBoundSignalExpression final
+ : public QQmlJavaScriptExpression,
+ public QQmlRefCounted<QQmlBoundSignalExpression>
{
+ friend class QQmlRefCounted<QQmlBoundSignalExpression>;
public:
QQmlBoundSignalExpression(
const QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope,
@@ -59,7 +62,7 @@ private:
const QObject *m_target;
};
-class Q_QML_PRIVATE_EXPORT QQmlBoundSignal : public QQmlNotifierEndpoint
+class Q_QML_EXPORT QQmlBoundSignal : public QQmlNotifierEndpoint
{
public:
QQmlBoundSignal(QObject *target, int signal, QObject *owner, QQmlEngine *engine);
diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp
index 36111f32a6..de37cc18be 100644
--- a/src/qml/qml/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/qqmlbuiltinfunctions.cpp
@@ -87,6 +87,7 @@ the \l Qt::LeftButton and \l Qt::RightButton enumeration values as \c Qt.LeftBut
\section1 Types
+\target globalqtobjecttypes
The Qt object also contains helper functions for creating objects of specific
data types. This is primarily useful when setting the properties of an item
@@ -109,8 +110,6 @@ creating objects of specific data types are also available for clients to use:
\li \c matrix4x4 - use \l{Qt::matrix4x4()}{Qt.matrix4x4()}
\endlist
-There are also string based constructors for these types. See \l{qtqml-typesystem-valuetypes.html}{QML Value Types} for more information.
-
\section1 Date/Time Formatters
The Qt object contains several functions for formatting QDateTime, QDate and QTime values.
@@ -190,7 +189,7 @@ The following functions are also on the Qt object.
*/
/*!
- \qmlproperty object Qt::application
+ \qmlproperty Application Qt::application
\since 5.1
The \c application object provides access to global application state
@@ -213,7 +212,7 @@ The following functions are also on the Qt object.
*/
/*!
- \qmlproperty object Qt::inputMethod
+ \qmlproperty InputMethod Qt::inputMethod
\since 5.0
It is the same as the \l InputMethod singleton.
@@ -263,6 +262,9 @@ The \c status property will be updated as the operation progresses.
If provided, \a callback is invoked when the operation completes. The callback is passed
the same object as is returned from the Qt.include() call.
+
+\warning Using this function is strict mode does not actually put identifier into the
+current context.
*/
// Qt.include() is implemented in qv4include.cpp
@@ -475,8 +477,9 @@ QVariant QtObject::font(const QJSValue &fontSpecifier) const
}
{
- QVariant v((QMetaType(QMetaType::QFont)));
- if (QQmlValueTypeProvider::constructFromJSValue(fontSpecifier, v.metaType(), v.data()))
+ const QVariant v = QQmlValueTypeProvider::createValueType(
+ fontSpecifier, QMetaType(QMetaType::QFont));
+ if (v.isValid())
return v;
}
@@ -511,9 +514,8 @@ static QVariant constructFromJSValue(QJSEngine *e, QMetaType type, T... paramete
return QVariant();
QJSValue params = e->newArray(sizeof...(parameters));
addParameters(e, params, 0, parameters...);
- QVariant variant(type);
- QQmlValueTypeProvider::constructFromJSValue(params, type, variant.data());
- return variant;
+ const QVariant variant = QQmlValueTypeProvider::createValueType(params, type);
+ return variant.isValid() ? variant : QVariant(type);
}
/*!
@@ -563,10 +565,9 @@ QVariant QtObject::quaternion(double scalar, double x, double y, double z) const
*/
QVariant QtObject::matrix4x4() const
{
- QVariant variant((QMetaType(QMetaType::QMatrix4x4)));
- QQmlValueTypeProvider::constructFromJSValue(
- QJSValue(), variant.metaType(), variant.data());
- return variant;
+ const QMetaType metaType(QMetaType::QMatrix4x4);
+ const QVariant variant = QQmlValueTypeProvider::createValueType(QJSValue(), metaType);
+ return variant.isValid() ? variant : QVariant(metaType);
}
/*!
@@ -587,8 +588,9 @@ QVariant QtObject::matrix4x4() const
QVariant QtObject::matrix4x4(const QJSValue &value) const
{
if (value.isObject()) {
- QVariant v((QMetaType(QMetaType::QMatrix4x4)));
- if (QQmlValueTypeProvider::constructFromJSValue(value, v.metaType(), v.data()))
+ QVariant v = QQmlValueTypeProvider::createValueType(
+ value, QMetaType(QMetaType::QMatrix4x4));
+ if (v.isValid())
return v;
}
@@ -808,12 +810,12 @@ static std::optional<QDate> dateFromString(const QString &string, QV4::Execution
return std::nullopt;
}
-QString QtObject::formatDate(const QDate &date, const QString &format) const
+QString QtObject::formatDate(QDate date, const QString &format) const
{
return date.toString(format);
}
-QString QtObject::formatDate(const QDate &date, Qt::DateFormat format) const
+QString QtObject::formatDate(QDate date, Qt::DateFormat format) const
{
return formatDateTimeObjectUsingDateFormat(date, format);
}
@@ -845,7 +847,7 @@ QString QtObject::formatDate(const QString &string, Qt::DateFormat format) const
}
#if QT_CONFIG(qml_locale)
-QString QtObject::formatDate(const QDate &date, const QLocale &locale,
+QString QtObject::formatDate(QDate date, const QLocale &locale,
QLocale::FormatType formatType) const
{
return locale.toString(date, formatType);
@@ -913,7 +915,7 @@ static std::optional<QTime> timeFromString(const QString &string, QV4::Execution
return std::nullopt;
}
-QString QtObject::formatTime(const QTime &time, const QString &format) const
+QString QtObject::formatTime(QTime time, const QString &format) const
{
return time.toString(format);
}
@@ -932,7 +934,7 @@ QString QtObject::formatTime(const QString &time, const QString &format) const
return QString();
}
-QString QtObject::formatTime(const QTime &time, Qt::DateFormat format) const
+QString QtObject::formatTime(QTime time, Qt::DateFormat format) const
{
return formatDateTimeObjectUsingDateFormat(time, format);
}
@@ -951,7 +953,7 @@ QString QtObject::formatTime(const QString &time, Qt::DateFormat format) const
}
#if QT_CONFIG(qml_locale)
-QString QtObject::formatTime(const QTime &time, const QLocale &locale,
+QString QtObject::formatTime(QTime time, const QLocale &locale,
QLocale::FormatType formatType) const
{
return locale.toString(time, formatType);
@@ -1300,10 +1302,15 @@ Each object in this array has the members \c lineNumber, \c columnNumber, \c fil
For example, if the above snippet had misspelled color as 'colro' then the array would contain an object like the following:
{ "lineNumber" : 1, "columnNumber" : 32, "fileName" : "dynamicSnippet1", "message" : "Cannot assign to non-existent property \"colro\""}.
-Note that this function returns immediately, and therefore may not work if
+\note This function returns immediately, and therefore may not work if
the \a qml string loads new components (that is, external QML files that have not yet been loaded).
If this is the case, consider using \l{QtQml::Qt::createComponent()}{Qt.createComponent()} instead.
+\warning This function is extremely slow since it has to compile the passed QML string every time
+it is invoked. Furthermore, it's very easy to produce invalid QML when programmatically constructing
+QML code. It's much better to keep your QML components as separate files and add properties and
+methods to customize their behavior than to produce new components by string manipulation.
+
See \l {Dynamic QML Object Creation from JavaScript} for more information on using this function.
*/
QObject *QtObject::createQmlObject(const QString &qml, QObject *parent, const QUrl &url) const
@@ -1472,7 +1479,7 @@ Returns a \l Component object created for the type specified by \a moduleUri and
import QtQuick
QtObject {
id: root
- property Component myComponent: Qt.createComponent(Rectangle, root)
+ property Component myComponent: Qt.createComponent("QtQuick", "Rectangle", Component.Asynchronous, root)
}
\endqml
This overload mostly behaves as the \c url based version, but can be used
@@ -1684,7 +1691,7 @@ QJSValue QtObject::binding(const QJSValue &function) const
Encode(e->memoryManager->allocate<QQmlBindingFunction>(f)));
}
-void QtObject::callLater(QQmlV4Function *args)
+void QtObject::callLater(QQmlV4FunctionPtr args)
{
m_engine->delayedCallQueue()->addUniquelyAndExecuteLater(m_engine, args);
}
@@ -1749,21 +1756,22 @@ enum ConsoleLogTypes {
static QString jsStack(QV4::ExecutionEngine *engine) {
QString stack;
- QVector<QV4::StackFrame> stackTrace = engine->stackTrace(10);
-
- for (int i = 0; i < stackTrace.size(); i++) {
- const QV4::StackFrame &frame = stackTrace.at(i);
-
+ int i = 0;
+ for (CppStackFrame *f = engine->currentStackFrame; f && i < 10; f = f->parentFrame(), ++i) {
QString stackFrame;
- if (frame.column >= 0)
- stackFrame = QStringLiteral("%1 (%2:%3:%4)").arg(frame.function,
- frame.source,
- QString::number(frame.line),
- QString::number(frame.column));
- else
- stackFrame = QStringLiteral("%1 (%2:%3)").arg(frame.function,
- frame.source,
- QString::number(frame.line));
+
+ if (f->isJSTypesFrame() && static_cast<JSTypesStackFrame *>(f)->isTailCalling()) {
+ stackFrame = QStringLiteral("[elided tail calls]");
+ } else {
+ const int line = f->lineNumber();
+ if (line != f->missingLineNumber()) {
+ stackFrame = QStringLiteral("%1 (%2:%3)").arg(
+ f->function(), f->source(), QString::number(qAbs(line)));
+ } else {
+ stackFrame = QStringLiteral("%1 (%2)").arg(
+ f->function(), f->source());
+ }
+ }
if (i)
stack += QLatin1Char('\n');
@@ -1989,7 +1997,7 @@ ReturnedValue ConsoleObject::method_trace(const FunctionObject *b, const Value *
QV4::CppStackFrame *frame = v4->currentStackFrame;
QMessageLogger(frame->source().toUtf8().constData(), frame->lineNumber(),
frame->function().toUtf8().constData())
- .debug("%s", qPrintable(stack));
+ .debug(v4->qmlEngine() ? lcQml : lcJs, "%s", qPrintable(stack));
return QV4::Encode::undefined();
}
@@ -2170,8 +2178,12 @@ QString GlobalExtensions::currentTranslationContext(ExecutionEngine *engine)
// The first non-empty source URL in the call stack determines the translation context.
while (frame && context.isEmpty()) {
- if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) {
- const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit);
+ if (ExecutableCompilationUnit *unit = frame->v4Function->executableCompilationUnit()) {
+ auto translationContextIndex = unit->unitData()->translationContextIndex();
+ if (translationContextIndex)
+ context = unit->stringAt(*translationContextIndex);
+ if (!context.isEmpty())
+ break;
QString fileName = unit->fileName();
QUrl url(unit->fileName());
if (url.isValid() && url.isRelative()) {
@@ -2349,16 +2361,23 @@ ReturnedValue GlobalExtensions::method_qsTrIdNoOp(const FunctionObject *, const
}
#endif // translation
+/*!
+ \qmlmethod void Qt::gc()
+
+ Runs the garbage collector.
+ This is equivalent to calling QJSEngine::collectGarbage().
+
+ \sa {Garbage Collection}
+*/
ReturnedValue GlobalExtensions::method_gc(const FunctionObject *b, const Value *, const Value *, int)
{
- b->engine()->memoryManager->runGC();
+ auto mm = b->engine()->memoryManager;
+ mm->runFullGC();
return QV4::Encode::undefined();
}
-
-
ReturnedValue GlobalExtensions::method_string_arg(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
QV4::Scope scope(b);
diff --git a/src/qml/qml/qqmlbuiltinfunctions_p.h b/src/qml/qml/qqmlbuiltinfunctions_p.h
index 46c4f415e3..9ceedad28b 100644
--- a/src/qml/qml/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/qqmlbuiltinfunctions_p.h
@@ -85,17 +85,17 @@ public:
Q_INVOKABLE QVariant alpha(const QJSValue &baseColor, double value) const;
Q_INVOKABLE QVariant tint(const QJSValue &baseColor, const QJSValue &tintColor) const;
- Q_INVOKABLE QString formatDate(const QDate &date, const QString &format) const;
+ Q_INVOKABLE QString formatDate(QDate date, const QString &format) const;
Q_INVOKABLE QString formatDate(const QDateTime &dateTime, const QString &format) const;
Q_INVOKABLE QString formatDate(const QString &string, const QString &format) const;
- Q_INVOKABLE QString formatDate(const QDate &date, Qt::DateFormat format) const;
+ Q_INVOKABLE QString formatDate(QDate date, Qt::DateFormat format) const;
Q_INVOKABLE QString formatDate(const QDateTime &dateTime, Qt::DateFormat format) const;
Q_INVOKABLE QString formatDate(const QString &string, Qt::DateFormat format) const;
- Q_INVOKABLE QString formatTime(const QTime &time, const QString &format) const;
+ Q_INVOKABLE QString formatTime(QTime time, const QString &format) const;
Q_INVOKABLE QString formatTime(const QDateTime &dateTime, const QString &format) const;
Q_INVOKABLE QString formatTime(const QString &time, const QString &format) const;
- Q_INVOKABLE QString formatTime(const QTime &time, Qt::DateFormat format) const;
+ Q_INVOKABLE QString formatTime(QTime time, Qt::DateFormat format) const;
Q_INVOKABLE QString formatTime(const QDateTime &dateTime, Qt::DateFormat format) const;
Q_INVOKABLE QString formatTime(const QString &time, Qt::DateFormat format) const;
@@ -105,13 +105,13 @@ public:
Q_INVOKABLE QString formatDateTime(const QString &string, Qt::DateFormat format) const;
#if QT_CONFIG(qml_locale)
- Q_INVOKABLE QString formatDate(const QDate &date, const QLocale &locale = QLocale(),
+ Q_INVOKABLE QString formatDate(QDate date, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
Q_INVOKABLE QString formatDate(const QDateTime &dateTime, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
Q_INVOKABLE QString formatDate(const QString &string, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
- Q_INVOKABLE QString formatTime(const QTime &time, const QLocale &locale = QLocale(),
+ Q_INVOKABLE QString formatTime(QTime time, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
Q_INVOKABLE QString formatTime(const QDateTime &dateTime, const QLocale &locale = QLocale(),
QLocale::FormatType formatType = QLocale::ShortFormat) const;
@@ -154,7 +154,7 @@ public:
QObject *parent = nullptr) const;
Q_INVOKABLE QJSValue binding(const QJSValue &function) const;
- Q_INVOKABLE void callLater(QQmlV4Function *args);
+ Q_INVOKABLE void callLater(QQmlV4FunctionPtr args);
#if QT_CONFIG(translation)
QString uiLanguage() const;
@@ -226,7 +226,7 @@ struct ConsoleObject : Object
};
-struct Q_QML_PRIVATE_EXPORT GlobalExtensions {
+struct Q_QML_EXPORT GlobalExtensions {
static void init(Object *globalObject, QJSEngine::Extensions extensions);
#if QT_CONFIG(translation)
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 4ff47588ed..e063418de4 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -29,6 +29,9 @@
#include <QtCore/qloggingcategory.h>
#include <qqmlinfo.h>
+
+using namespace Qt::Literals::StringLiterals;
+
namespace {
Q_CONSTINIT thread_local int creationDepth = 0;
}
@@ -247,7 +250,7 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
}
\endqml
- \sa {Qt QML}
+ \sa {Qt Qml}
*/
/*!
@@ -297,7 +300,8 @@ void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data)
{
url = data->finalUrl();
- compilationUnit.reset(data->compilationUnit());
+ if (auto cu = data->compilationUnit())
+ compilationUnit = engine->handle()->executableCompilationUnit(std::move(cu));
if (!compilationUnit) {
Q_ASSERT(data->isError());
@@ -320,7 +324,7 @@ void QQmlComponentPrivate::clear()
compilationUnit.reset();
loadedType = {};
- isInlineComponent = false;
+ inlineComponentName.reset();
}
QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context)
@@ -335,6 +339,35 @@ QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *cont
return q->beginCreate(context);
}
+static void removePendingQPropertyBinding(
+ QV4::Value *object, const QString &propertyName, QQmlObjectCreator *creator)
+{
+ if (!creator)
+ return;
+
+ QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>();
+ if (!wrapper)
+ return;
+
+ QObject *o = wrapper->object();
+ if (!o)
+ return;
+
+ if (QQmlData *ddata = QQmlData::get(o)) {
+ const QQmlPropertyData *propData = ddata->propertyCache->property(
+ propertyName, o, ddata->outerContext);
+ if (propData && propData->isBindable())
+ creator->removePendingBinding(o, propData->coreIndex());
+ return;
+ }
+
+ const QMetaObject *meta = o->metaObject();
+ Q_ASSERT(meta);
+ const int index = meta->indexOfProperty(propertyName.toUtf8());
+ if (index != -1 && meta->property(index).isBindable())
+ creator->removePendingBinding(o, index);
+}
+
bool QQmlComponentPrivate::setInitialProperty(
QObject *base, const QString &name, const QVariant &value)
{
@@ -351,13 +384,16 @@ bool QQmlComponentPrivate::setInitialProperty(
if (scope.engine->hasException)
break;
}
- segment = scope.engine->newString(properties.last());
+ const QString lastProperty = properties.last();
+ segment = scope.engine->newString(lastProperty);
object->put(segment, scope.engine->metaTypeToJS(value.metaType(), value.constData()));
if (scope.engine->hasException) {
qmlWarning(base, scope.engine->catchExceptionAsQmlError());
scope.engine->hasException = false;
return false;
}
+
+ removePendingQPropertyBinding(object, lastProperty, state.creator());
return true;
}
@@ -369,7 +405,12 @@ bool QQmlComponentPrivate::setInitialProperty(
prop = QQmlProperty(base, name, engine);
QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop);
const bool isValid = prop.isValid();
- if (!isValid || !privProp->writeValueProperty(value, {})) {
+ if (isValid && privProp->writeValueProperty(value, {})) {
+ if (prop.isBindable()) {
+ if (QQmlObjectCreator *creator = state.creator())
+ creator->removePendingBinding(prop.object(), prop.index());
+ }
+ } else {
QQmlError error{};
error.setUrl(url);
if (isValid) {
@@ -428,13 +469,11 @@ QQmlComponent::~QQmlComponent()
This property holds the status of component loading. The status can be one of the
following:
- \list
- \li \c{Component.Null} - no data is available for the component
- \li \c{Component.Ready} - the component has been loaded, and can be used to create instances.
- \li \c{Component.Loading} - the component is currently being loaded
- \li \c{Component.Error} - an error occurred while loading the component.
+ \value Component.Null no data is available for the component
+ \value Component.Ready the component has been loaded, and can be used to create instances.
+ \value Component.Loading the component is currently being loaded
+ \value Component.Error an error occurred while loading the component.
Calling \l errorString() will provide a human-readable description of any errors.
- \endlist
*/
/*!
@@ -960,6 +999,15 @@ QObject *QQmlComponentPrivate::createWithProperties(QObject *parent, const QVari
The ownership of the returned object instance is transferred to the caller.
+ \note The categorization of bindings into constant values and actual
+ bindings is intentionally unspecified and may change between versions of Qt
+ and depending on whether and how you are using \l{qmlcachegen}. You should
+ not rely on any particular binding to be evaluated either before or after
+ beginCreate() returns. For example a constant expression like
+ \e{MyType.EnumValue} may be recognized as such at compile time or deferred
+ to be executed as binding. The same holds for constant expressions like
+ \e{-(5)} or \e{"a" + " constant string"}.
+
\sa completeCreate(), QQmlEngine::ObjectOwnership
*/
QObject *QQmlComponent::beginCreate(QQmlContext *context)
@@ -1001,11 +1049,7 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
// filter out temporary errors as they do not really affect component's
// state (they are not part of the document compilation)
- state.errors.erase(std::remove_if(state.errors.begin(), state.errors.end(),
- [](const QQmlComponentPrivate::AnnotatedQmlError &e) {
- return e.isTransient;
- }),
- state.errors.end());
+ state.errors.removeIf([](const auto &e) { return e.isTransient; });
state.clearRequiredProperties();
if (!q->isReady()) {
@@ -1031,13 +1075,31 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
if (!loadedType.isValid()) {
enginePriv->referenceScarceResources();
state.initCreator(std::move(context), compilationUnit, creationContext);
- rv = state.creator()->create(start, nullptr, nullptr, isInlineComponent ? QQmlObjectCreator::InlineComponent : QQmlObjectCreator::NormalObject);
+
+ QQmlObjectCreator::CreationFlags flags;
+ if (const QString *icName = inlineComponentName.get()) {
+ flags = QQmlObjectCreator::InlineComponent;
+ if (start == -1)
+ start = compilationUnit->inlineComponentId(*icName);
+ Q_ASSERT(start > 0);
+ } else {
+ flags = QQmlObjectCreator::NormalObject;
+ }
+
+ rv = state.creator()->create(start, nullptr, nullptr, flags);
if (!rv)
state.appendCreatorErrors();
enginePriv->dereferenceScarceResources();
} else {
+ // TODO: extract into function
rv = loadedType.createWithQQmlData();
QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(rv);
+ QQmlParserStatus *parserStatus = nullptr;
+ const int parserStatusCast = loadedType.parserStatusCast();
+ if (parserStatusCast != -1) {
+ parserStatus = reinterpret_cast<QQmlParserStatus*>(reinterpret_cast<char *>(rv) + parserStatusCast);
+ parserStatus->classBegin();
+ }
for (int i = 0, propertyCount = propertyCache->propertyCount(); i < propertyCount; ++i) {
if (const QQmlPropertyData *propertyData = propertyCache->property(i); propertyData->isRequired()) {
state.ensureRequiredPropertyStorage();
@@ -1046,6 +1108,12 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
state.addPendingRequiredProperty(rv, propertyData, info);
}
}
+ if (parserStatus)
+ parserStatus->componentComplete();
+ if (const int finalizerCast = loadedType.finalizerCast(); finalizerCast != -1) {
+ auto* hook = reinterpret_cast<QQmlFinalizerHook *>(reinterpret_cast<char *>(rv) + finalizerCast);
+ hook->componentFinalized();
+ }
}
if (rv) {
@@ -1154,8 +1222,8 @@ QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(
Q_ASSERT(data && data->propertyCache);
targetProp = data->propertyCache->property(targetProp->coreIndex());
}
- auto it = requiredProperties->find({createdComponent, targetProp});
- if (it != requiredProperties->end()) {
+ auto it = requiredProperties->constFind({createdComponent, targetProp});
+ if (it != requiredProperties->cend()) {
if (wasInRequiredProperties)
*wasInRequiredProperties = true;
requiredProperties->erase(it);
@@ -1266,56 +1334,90 @@ void QQmlComponent::loadFromModule(QAnyStringView uri, QAnyStringView typeName,
QQmlComponent::CompilationMode mode)
{
Q_D(QQmlComponent);
+ auto [status, type] = d->prepareLoadFromModule(uri, typeName);
+ d->completeLoadFromModule(uri, typeName, type, status, mode);
+}
- auto enginePriv = QQmlEnginePrivate::get(d->engine);
+LoadHelper::ResolveTypeResult QQmlComponentPrivate::prepareLoadFromModule(QAnyStringView uri,
+ QAnyStringView typeName)
+{
+ auto enginePriv = QQmlEnginePrivate::get(engine);
// LoadHelper must be on the Heap as it derives from QQmlRefCount
auto loadHelper = QQml::makeRefPointer<LoadHelper>(&enginePriv->typeLoader, uri);
- auto [moduleStatus, type] = loadHelper->resolveType(typeName);
+ return loadHelper->resolveType(typeName);
+}
+
+void QQmlComponentPrivate::completeLoadFromModule(QAnyStringView uri, QAnyStringView typeName, QQmlType type,
+ LoadHelper::ResolveTypeResult::Status moduleStatus,
+ QQmlComponent::CompilationMode mode)
+{
+ Q_Q(QQmlComponent);
+
+ // we always mimic the progressChanged behavior from loadUrl
auto reportError = [&](QString msg) {
QQmlError error;
error.setDescription(msg);
- d->state.errors.push_back(std::move(error));
- emit statusChanged(Error);
+ state.errors.push_back(std::move(error));
+ progress = 1;
+ emit q->progressChanged(1);
+ emit q->statusChanged(q->Error);
+ };
+ auto emitProgressReset = [&](){
+ if (progress != 0) {
+ progress = 0;
+ emit q->progressChanged(0);
+ }
};
+ auto emitComplete = [&]() {
+ progress = 1;
+ emit q->progressChanged(1);
+ emit q->statusChanged(q->status());
+ };
+ emitProgressReset();
if (moduleStatus == LoadHelper::ResolveTypeResult::NoSuchModule) {
- reportError(QLatin1String(R"(No module named "%1" found)")
- .arg(uri.toString()));
+ reportError(QLatin1String(R"(No module named "%1" found)").arg(uri.toString()));
} else if (!type.isValid()) {
reportError(QLatin1String(R"(Module "%1" contains no type named "%2")")
.arg(uri.toString(), typeName.toString()));
} else if (type.isCreatable()) {
- d->clear();
- // mimic the progressChanged behavior from loadUrl
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(0);
- }
- d->loadedType = type;
- d->progress = 1;
- emit progressChanged(1);
- emit statusChanged(status());
-
+ clear();
+ loadedType = type;
+ emitComplete();
} else if (type.isComposite()) {
+ // loadUrl takes care of signal emission
loadUrl(type.sourceUrl(), mode);
} else if (type.isInlineComponentType()) {
auto baseUrl = type.sourceUrl();
baseUrl.setFragment(QString());
- // if the outer type has not been resolved yet, we need to load the outer type synchronously
- // in order to get the correct object id
- mode = type.inlineComponentObjectId() > 0 ? mode : QQmlComponent::CompilationMode::PreferSynchronous;
- loadUrl(baseUrl, mode);
- if (!isError()) {
- d->isInlineComponent = true;
- d->start = type.inlineComponentObjectId();
- Q_ASSERT(d->start >= 0);
+ {
+ // we don't want to emit status changes from the "helper" loadUrl below
+ // because it would signal success to early
+ QSignalBlocker blockSignals(q);
+ // we really need to continue in a synchronous way, otherwise we can't check the CU
+ loadUrl(baseUrl, QQmlComponent::PreferSynchronous);
+ }
+ if (q->isError()) {
+ emitComplete();
+ return;
+ }
+ QString elementName = type.elementName();
+ if (compilationUnit->inlineComponentId(elementName) == -1) {
+ QString realTypeName = typeName.toString();
+ realTypeName.truncate(realTypeName.indexOf(u'.'));
+ QString errorMessage = R"(Type "%1" from module "%2" contains no inline component named "%3".)"_L1.arg(
+ realTypeName, uri.toString(), elementName);
+ if (elementName == u"qml")
+ errorMessage += " To load the type \"%1\", drop the \".qml\" extension."_L1.arg(realTypeName);
+ reportError(std::move(errorMessage));
+ } else {
+ inlineComponentName = std::make_unique<QString>(std::move(elementName));
+ emitComplete();
}
} else if (type.isSingleton() || type.isCompositeSingleton()) {
- reportError(QLatin1String(R"(%1 is a singleton, and cannot be loaded)")
- .arg(typeName.toString()));
+ reportError(QLatin1String(R"(%1 is a singleton, and cannot be loaded)").arg(typeName.toString()));
} else {
- reportError(QLatin1String("Could not load %1, as the type is uncreatable")
- .arg(typeName.toString()));
+ reportError(QLatin1String("Could not load %1, as the type is uncreatable").arg(typeName.toString()));
}
}
@@ -1426,6 +1528,13 @@ void QQmlComponentPrivate::incubateObject(
incubatorPriv->compilationUnit = componentPriv->compilationUnit;
incubatorPriv->enginePriv = enginePriv;
incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext));
+
+ if (start == -1) {
+ if (const QString *icName = componentPriv->inlineComponentName.get()) {
+ start = compilationUnit->inlineComponentId(*icName);
+ Q_ASSERT(start > 0);
+ }
+ }
incubatorPriv->subComponentToCreate = componentPriv->start;
enginePriv->incubate(*incubationTask, forContext);
@@ -1440,7 +1549,7 @@ namespace QV4 {
namespace Heap {
#define QmlIncubatorObjectMembers(class, Member) \
- Member(class, HeapValue, HeapValue, valuemap) \
+ Member(class, HeapValue, HeapValue, valuemapOrObject) \
Member(class, HeapValue, HeapValue, statusChanged) \
Member(class, Pointer, QmlContext *, qmlContext) \
Member(class, NoMark, QQmlComponentIncubator *, incubator) \
@@ -1565,7 +1674,10 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
*/
-void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent)
+void QQmlComponentPrivate::setInitialProperties(
+ QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o,
+ const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent,
+ QQmlObjectCreator *creator)
{
QV4::Scope scope(engine);
QV4::ScopedObject object(scope);
@@ -1605,7 +1717,8 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
qmlWarning(createdComponent, error);
continue;
}
- name = engine->newString(properties.last());
+ const QString lastProperty = properties.last();
+ name = engine->newString(lastProperty);
object->put(name, val);
if (engine->hasException) {
qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
@@ -1614,6 +1727,8 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
auto prop = removePropertyFromRequired(createdComponent, name->toQString(),
requiredProperties, engine->qmlEngine());
}
+
+ removePendingQPropertyBinding(object, lastProperty, creator);
}
engine->hasException = false;
@@ -1651,7 +1766,7 @@ QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredP
/*!
\internal
*/
-void QQmlComponent::createObject(QQmlV4Function *args)
+void QQmlComponent::createObject(QQmlV4FunctionPtr args)
{
Q_D(QQmlComponent);
Q_ASSERT(d->engine);
@@ -1699,8 +1814,9 @@ void QQmlComponent::createObject(QQmlV4Function *args)
if (!valuemap->isUndefined()) {
QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
- QQmlComponentPrivate::setInitialProperties(v4, qmlContext, object, valuemap,
- d->state.requiredProperties(), rv);
+ QQmlComponentPrivate::setInitialProperties(
+ v4, qmlContext, object, valuemap, d->state.requiredProperties(), rv,
+ d->state.creator());
}
if (d->state.hasUnsetRequiredProperties()) {
QList<QQmlError> errors;
@@ -1799,7 +1915,7 @@ QObject *QQmlComponent::createObject(QObject *parent, const QVariantMap &propert
/*!
\internal
*/
-void QQmlComponent::incubateObject(QQmlV4Function *args)
+void QQmlComponent::incubateObject(QQmlV4FunctionPtr args)
{
Q_D(QQmlComponent);
Q_ASSERT(d->engine);
@@ -1846,7 +1962,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
r->setPrototypeOf(p);
if (!valuemap->isUndefined())
- r->d()->valuemap.set(scope.engine, valuemap);
+ r->d()->valuemapOrObject.set(scope.engine, valuemap);
r->d()->qmlContext.set(scope.engine, v4->qmlContext());
r->d()->parent = parent;
@@ -1869,8 +1985,10 @@ void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext
QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4engine, toCreate));
Q_ASSERT(object->as<QV4::Object>());
- if (!valuemap.isUndefined())
- setInitialProperties(v4engine, qmlContext, object, valuemap, requiredProperties, toCreate);
+ if (!valuemap.isUndefined()) {
+ setInitialProperties(
+ v4engine, qmlContext, object, valuemap, requiredProperties, toCreate, state.creator());
+ }
}
QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
@@ -1947,7 +2065,7 @@ QQmlComponentExtension::~QQmlComponentExtension()
void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
{
Object::init();
- valuemap.set(internalClass->engine, QV4::Value::undefinedValue());
+ valuemapOrObject.set(internalClass->engine, QV4::Value::undefinedValue());
statusChanged.set(internalClass->engine, QV4::Value::undefinedValue());
parent.init();
qmlContext.set(internalClass->engine, nullptr);
@@ -1964,25 +2082,32 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties *re
{
QQmlComponent_setQmlParent(o, d()->parent);
- if (!d()->valuemap.isUndefined()) {
+ if (!d()->valuemapOrObject.isUndefined()) {
QV4::ExecutionEngine *v4 = engine();
QV4::Scope scope(v4);
QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o));
QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
- QQmlComponentPrivate::setInitialProperties(v4, qmlCtxt, obj, d()->valuemap, requiredProperties, o);
+ QQmlComponentPrivate::setInitialProperties(
+ v4, qmlCtxt, obj, d()->valuemapOrObject, requiredProperties, o,
+ QQmlIncubatorPrivate::get(d()->incubator)->creator.data());
}
}
void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
{
QV4::Scope scope(engine());
- // hold the incubated object in a scoped value to prevent it's destruction before this method returns
- QV4::ScopedObject incubatedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, d()->incubator->object()));
+
+ QObject *object = d()->incubator->object();
if (s == QQmlIncubator::Ready) {
- Q_ASSERT(QQmlData::get(d()->incubator->object()));
- QQmlData::get(d()->incubator->object())->explicitIndestructibleSet = false;
- QQmlData::get(d()->incubator->object())->indestructible = false;
+ // We don't need the arguments anymore, but we still want to hold on to the object so
+ // that it doesn't get gc'd
+ d()->valuemapOrObject.set(scope.engine, QV4::QObjectWrapper::wrap(scope.engine, object));
+
+ QQmlData *ddata = QQmlData::get(object);
+ Q_ASSERT(ddata);
+ ddata->explicitIndestructibleSet = false;
+ ddata->indestructible = false;
}
QV4::ScopedFunctionObject f(scope, d()->statusChanged);
diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h
index 0c7f7ce1ff..2d68c47c11 100644
--- a/src/qml/qml/qqmlcomponent.h
+++ b/src/qml/qml/qqmlcomponent.h
@@ -21,7 +21,6 @@ class QByteArray;
class QQmlEngine;
class QQmlComponent;
class QQmlIncubator;
-class QQmlV4Function;
class QQmlComponentPrivate;
class QQmlComponentAttached;
@@ -37,10 +36,6 @@ class Q_QML_EXPORT QQmlComponent : public QObject
Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(QUrl url READ url CONSTANT)
- QML_NAMED_ELEMENT(Component)
- QML_ADDED_IN_VERSION(2, 0)
- QML_ATTACHED(QQmlComponentAttached)
- Q_CLASSINFO("QML.OmitFromQmlTypes", "true")
public:
enum CompilationMode { PreferSynchronous, Asynchronous };
@@ -106,12 +101,12 @@ protected:
#if QT_DEPRECATED_SINCE(6, 3)
QT_DEPRECATED_X("Use the overload with proper arguments")
- Q_INVOKABLE void createObject(QQmlV4Function *);
+ Q_INVOKABLE void createObject(QQmlV4FunctionPtr);
#endif
Q_INVOKABLE QObject *createObject(
QObject *parent = nullptr, const QVariantMap &properties = {});
- Q_INVOKABLE void incubateObject(QQmlV4Function *);
+ Q_INVOKABLE void incubateObject(QQmlV4FunctionPtr);
private:
QQmlComponent(QQmlEngine *, QV4::ExecutableCompilationUnit *compilationUnit, int,
@@ -143,8 +138,6 @@ struct OverridableAttachedType<QQmlComponent, QQmlComponentAttached>
} // namespace QQmlPrivate
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlComponent)
#endif // QQMLCOMPONENT_H
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index d76e1c24a3..21fdee3f7a 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -36,7 +36,7 @@ class QQmlComponent;
class QQmlEngine;
class QQmlComponentAttached;
-class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback
+class Q_QML_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback
{
Q_DECLARE_PUBLIC(QQmlComponent)
@@ -49,7 +49,10 @@ public:
QObject *beginCreate(QQmlRefPointer<QQmlContextData>);
void completeCreate();
void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties *requiredProperties);
- static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent);
+ static void setInitialProperties(
+ QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o,
+ const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent,
+ QQmlObjectCreator *creator);
static QQmlError unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty);
virtual void incubateObject(
@@ -67,11 +70,12 @@ public:
QUrl url;
qreal progress;
+ std::unique_ptr<QString> inlineComponentName;
/* points to the sub-object in a QML file that should be instantiated
- used for inline components and to create instances of QtQml's Component type */
+ used create instances of QtQml's Component type and indirectly for inline components */
int start;
- bool isInlineComponent = false;
+
bool hadTopLevelRequiredProperties() const;
// TODO: merge compilation unit and type
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
@@ -163,9 +167,12 @@ public:
QObject *createWithProperties(QObject *parent, const QVariantMap &properties,
QQmlContext *context, CreateBehavior behavior = CreateDefault);
- bool isBound() const {
- return compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound;
- }
+ bool isBound() const { return compilationUnit && (compilationUnit->componentsAreBound()); }
+ LoadHelper::ResolveTypeResult prepareLoadFromModule(QAnyStringView uri,
+ QAnyStringView typeName);
+ void completeLoadFromModule(QAnyStringView uri, QAnyStringView typeName, QQmlType type,
+ LoadHelper::ResolveTypeResult::Status moduleStatus,
+ QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous);
};
QQmlComponentPrivate::ConstructionState::~ConstructionState()
diff --git a/src/qml/qml/qqmlcomponentandaliasresolver_p.h b/src/qml/qml/qqmlcomponentandaliasresolver_p.h
new file mode 100644
index 0000000000..a2758d385c
--- /dev/null
+++ b/src/qml/qml/qqmlcomponentandaliasresolver_p.h
@@ -0,0 +1,480 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QQMLCOMPONENTANDALIASRESOLVER_P_H
+#define QQMLCOMPONENTANDALIASRESOLVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlerror.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qhash.h>
+
+#include <private/qqmltypeloader_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcQmlTypeCompiler);
+
+// This class primarily resolves component boundaries in a document.
+// With the information about boundaries, it then goes on to resolve aliases and generalized
+// group properties. Both rely on IDs as first part of their expressions and the IDs have
+// to be located in surrounding components. That's why we have to do this with the component
+// boundaries in mind.
+
+template<typename ObjectContainer>
+class QQmlComponentAndAliasResolver
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlComponentAndAliasResolver)
+public:
+ using CompiledObject = typename ObjectContainer::CompiledObject;
+ using CompiledBinding = typename ObjectContainer::CompiledBinding;
+
+ QQmlComponentAndAliasResolver(
+ ObjectContainer *compiler,
+ QQmlEnginePrivate *enginePrivate,
+ QQmlPropertyCacheVector *propertyCaches);
+
+ [[nodiscard]] QQmlError resolve(int root = 0);
+
+private:
+ enum AliasResolutionResult {
+ NoAliasResolved,
+ SomeAliasesResolved,
+ AllAliasesResolved
+ };
+
+ // To be specialized for each container
+ void allocateNamedObjects(CompiledObject *object) const;
+ void setObjectId(int index) const;
+ [[nodiscard]] bool markAsComponent(int index) const;
+ [[nodiscard]] AliasResolutionResult resolveAliasesInObject(
+ const CompiledObject &component, int objectIndex, QQmlError *error);
+ void resolveGeneralizedGroupProperty(const CompiledObject &component, CompiledBinding *binding);
+ [[nodiscard]] bool wrapImplicitComponent(CompiledBinding *binding);
+
+ [[nodiscard]] QQmlError findAndRegisterImplicitComponents(
+ const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
+ [[nodiscard]] QQmlError collectIdsAndAliases(int objectIndex);
+ [[nodiscard]] QQmlError resolveAliases(int componentIndex);
+ void resolveGeneralizedGroupProperties(int componentIndex);
+ [[nodiscard]] QQmlError resolveComponentsInInlineComponentRoot(int root);
+
+ QString stringAt(int idx) const { return m_compiler->stringAt(idx); }
+ QV4::ResolvedTypeReference *resolvedType(int id) const { return m_compiler->resolvedType(id); }
+
+ [[nodiscard]] QQmlError error(
+ const QV4::CompiledData::Location &location,
+ const QString &description)
+ {
+ QQmlError error;
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
+ error.setDescription(description);
+ error.setUrl(m_compiler->url());
+ return error;
+ }
+
+ template<typename Token>
+ [[nodiscard]] QQmlError error(Token token, const QString &description)
+ {
+ return error(token->location, description);
+ }
+
+ static bool isUsableComponent(const QMetaObject *metaObject)
+ {
+ // The metaObject is a component we're interested in if it either is a QQmlComponent itself
+ // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
+ // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
+
+ if (metaObject == &QQmlComponent::staticMetaObject)
+ return true;
+
+ for (; metaObject; metaObject = metaObject->superClass()) {
+ if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
+ return true;
+ }
+
+ return false;
+ }
+
+ ObjectContainer *m_compiler = nullptr;
+ QQmlEnginePrivate *m_enginePrivate = nullptr;
+
+ // Implicit component insertion may have added objects and thus we also need
+ // to extend the symmetric propertyCaches. Therefore, non-const propertyCaches.
+ QQmlPropertyCacheVector *m_propertyCaches = nullptr;
+
+ // indices of the objects that are actually Component {}
+ QVector<quint32> m_componentRoots;
+ QVector<int> m_objectsWithAliases;
+ QVector<CompiledBinding *> m_generalizedGroupProperties;
+ typename ObjectContainer::IdToObjectMap m_idToObjectIndex;
+};
+
+template<typename ObjectContainer>
+QQmlComponentAndAliasResolver<ObjectContainer>::QQmlComponentAndAliasResolver(
+ ObjectContainer *compiler,
+ QQmlEnginePrivate *enginePrivate,
+ QQmlPropertyCacheVector *propertyCaches)
+ : m_compiler(compiler)
+ , m_enginePrivate(enginePrivate)
+ , m_propertyCaches(propertyCaches)
+{
+}
+
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::findAndRegisterImplicitComponents(
+ const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache)
+{
+ QQmlPropertyResolver propertyResolver(propertyCache);
+
+ const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1
+ ? propertyCache->parent()->defaultProperty()
+ : propertyCache->defaultProperty();
+
+ for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
+ if (binding->type() != QV4::CompiledData::Binding::Type_Object)
+ continue;
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject))
+ continue;
+
+ auto targetObject = m_compiler->objectAt(binding->value.objectIndex);
+ auto typeReference = resolvedType(targetObject->inheritedTypeNameIndex);
+ Q_ASSERT(typeReference);
+
+ const QMetaObject *firstMetaObject = nullptr;
+ const auto type = typeReference->type();
+ if (type.isValid())
+ firstMetaObject = type.metaObject();
+ else if (const auto compilationUnit = typeReference->compilationUnit())
+ firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ if (isUsableComponent(firstMetaObject))
+ continue;
+
+ // if here, not a QQmlComponent, so needs wrapping
+ const QQmlPropertyData *pd = nullptr;
+ if (binding->propertyNameIndex != quint32(0)) {
+ bool notInRevision = false;
+ pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
+ } else {
+ pd = defaultProperty;
+ }
+ if (!pd || !pd->isQObject())
+ continue;
+
+ // If the version is given, use it and look up by QQmlType.
+ // Otherwise, make sure we look up by metaobject.
+ // TODO: Is this correct?
+ QQmlPropertyCache::ConstPtr pc = pd->typeVersion().hasMinorVersion()
+ ? QQmlMetaType::rawPropertyCacheForType(pd->propType(), pd->typeVersion())
+ : QQmlMetaType::rawPropertyCacheForType(pd->propType());
+ const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
+ while (mo) {
+ if (mo == &QQmlComponent::staticMetaObject)
+ break;
+ mo = mo->superClass();
+ }
+
+ if (!mo)
+ continue;
+
+ if (!wrapImplicitComponent(binding))
+ return error(binding, tr("Cannot wrap implicit component"));
+ }
+
+ return QQmlError();
+}
+
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveComponentsInInlineComponentRoot(
+ int root)
+{
+ // Find implicit components in the inline component itself. Also warn about inline
+ // components being explicit components.
+
+ const auto rootObj = m_compiler->objectAt(root);
+ Q_ASSERT(rootObj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot));
+
+ if (const int typeName = rootObj->inheritedTypeNameIndex) {
+ const auto *tref = resolvedType(typeName);
+ Q_ASSERT(tref);
+ if (tref->type().metaObject() == &QQmlComponent::staticMetaObject) {
+ qCWarning(lcQmlTypeCompiler).nospace().noquote()
+ << m_compiler->url().toString() << ":" << rootObj->location.line() << ":"
+ << rootObj->location.column()
+ << ": Using a Component as the root of an inline component is deprecated: "
+ "inline components are "
+ "automatically wrapped into Components when needed.";
+ return QQmlError();
+ }
+ }
+
+ const QQmlPropertyCache::ConstPtr rootCache = m_propertyCaches->at(root);
+ Q_ASSERT(rootCache);
+
+ return findAndRegisterImplicitComponents(rootObj, rootCache);
+}
+
+// Resolve ignores everything relating to inline components, except for implicit components.
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolve(int root)
+{
+ // Detect real Component {} objects as well as implicitly defined components, such as
+ // someItemDelegate: Item {}
+ // In the implicit case Item is surrounded by a synthetic Component {} because the property
+ // on the left hand side is of QQmlComponent type.
+ const int objCountWithoutSynthesizedComponents = m_compiler->objectCount();
+
+ if (root != 0) {
+ const QQmlError error = resolveComponentsInInlineComponentRoot(root);
+ if (error.isValid())
+ return error;
+ }
+
+ // root+1, as ic root is handled at the end
+ const int startObjectIndex = root == 0 ? root : root+1;
+
+ for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
+ auto obj = m_compiler->objectAt(i);
+ const bool isInlineComponentRoot
+ = obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot);
+ const bool isPartOfInlineComponent
+ = obj->hasFlag(QV4::CompiledData::Object::IsPartOfInlineComponent);
+ QQmlPropertyCache::ConstPtr cache = m_propertyCaches->at(i);
+
+ if (root == 0) {
+ // normal component root, skip over anything inline component related
+ if (isInlineComponentRoot || isPartOfInlineComponent)
+ continue;
+ } else if (!isPartOfInlineComponent || isInlineComponentRoot) {
+ // When handling an inline component, stop where the inline component ends
+ // Note: We do not support nested inline components. Therefore, isInlineComponentRoot
+ // tells us that the element after the current inline component is again an
+ // inline component
+ break;
+ }
+
+ if (obj->inheritedTypeNameIndex == 0 && !cache)
+ continue;
+
+ bool isExplicitComponent = false;
+ if (obj->inheritedTypeNameIndex) {
+ auto *tref = resolvedType(obj->inheritedTypeNameIndex);
+ Q_ASSERT(tref);
+ if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
+ isExplicitComponent = true;
+ }
+
+ if (!isExplicitComponent) {
+ if (cache) {
+ const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
+ if (error.isValid())
+ return error;
+ }
+ continue;
+ }
+
+ if (!markAsComponent(i))
+ return error(obj, tr("Cannot mark object as component"));
+
+ // check if this object is the root
+ if (i == 0) {
+ if (isExplicitComponent)
+ qCWarning(lcQmlTypeCompiler).nospace().noquote()
+ << m_compiler->url().toString() << ":" << obj->location.line() << ":"
+ << obj->location.column()
+ << ": Using a Component as the root of a QML document is deprecated: types "
+ "defined in qml documents are "
+ "automatically wrapped into Components when needed.";
+ }
+
+ if (obj->functionCount() > 0)
+ return error(obj, tr("Component objects cannot declare new functions."));
+ if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
+ return error(obj, tr("Component objects cannot declare new properties."));
+ if (obj->signalCount() > 0)
+ return error(obj, tr("Component objects cannot declare new signals."));
+
+ if (obj->bindingCount() == 0)
+ return error(obj, tr("Cannot create empty component specification"));
+
+ const auto rootBinding = obj->bindingsBegin();
+ const auto bindingsEnd = obj->bindingsEnd();
+
+ // Produce the more specific "no properties" error rather than the "invalid body" error
+ // where possible.
+ for (auto b = rootBinding; b != bindingsEnd; ++b) {
+ if (b->propertyNameIndex == 0)
+ continue;
+
+ return error(b, tr("Component elements may not contain properties other than id"));
+ }
+
+ if (auto b = rootBinding;
+ b->type() != QV4::CompiledData::Binding::Type_Object || ++b != bindingsEnd) {
+ return error(obj, tr("Invalid component body specification"));
+ }
+
+ // For the root object, we are going to collect ids/aliases and resolve them for as a
+ // separate last pass.
+ if (i != 0)
+ m_componentRoots.append(i);
+ }
+
+ for (int i = 0; i < m_componentRoots.size(); ++i) {
+ CompiledObject *component = m_compiler->objectAt(m_componentRoots.at(i));
+ const auto rootBinding = component->bindingsBegin();
+
+ m_idToObjectIndex.clear();
+ m_objectsWithAliases.clear();
+ m_generalizedGroupProperties.clear();
+
+ if (const QQmlError error = collectIdsAndAliases(rootBinding->value.objectIndex);
+ error.isValid()) {
+ return error;
+ }
+
+ allocateNamedObjects(component);
+
+ if (const QQmlError error = resolveAliases(m_componentRoots.at(i)); error.isValid())
+ return error;
+
+ resolveGeneralizedGroupProperties(m_componentRoots.at(i));
+ }
+
+ // Collect ids and aliases for root
+ m_idToObjectIndex.clear();
+ m_objectsWithAliases.clear();
+ m_generalizedGroupProperties.clear();
+
+ if (const QQmlError error = collectIdsAndAliases(root); error.isValid())
+ return error;
+
+ allocateNamedObjects(m_compiler->objectAt(root));
+ if (const QQmlError error = resolveAliases(root); error.isValid())
+ return error;
+
+ resolveGeneralizedGroupProperties(root);
+ return QQmlError();
+}
+
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::collectIdsAndAliases(int objectIndex)
+{
+ auto obj = m_compiler->objectAt(objectIndex);
+
+ if (obj->idNameIndex != 0) {
+ if (m_idToObjectIndex.contains(obj->idNameIndex))
+ return error(obj->locationOfIdProperty, tr("id is not unique"));
+ setObjectId(objectIndex);
+ m_idToObjectIndex.insert(obj->idNameIndex, objectIndex);
+ }
+
+ if (obj->aliasCount() > 0)
+ m_objectsWithAliases.append(objectIndex);
+
+ // Stop at Component boundary
+ if (obj->hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
+ return QQmlError();
+
+ for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd();
+ binding != end; ++binding) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_GroupProperty: {
+ const auto *inner = m_compiler->objectAt(binding->value.objectIndex);
+ if (m_compiler->stringAt(inner->inheritedTypeNameIndex).isEmpty()) {
+ const auto cache = m_propertyCaches->at(objectIndex);
+ if (!cache || !cache->property(
+ m_compiler->stringAt(binding->propertyNameIndex), nullptr, nullptr)) {
+ m_generalizedGroupProperties.append(binding);
+ }
+ }
+ }
+ Q_FALLTHROUGH();
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ if (const QQmlError error = collectIdsAndAliases(binding->value.objectIndex);
+ error.isValid()) {
+ return error;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return QQmlError();
+}
+
+template<typename ObjectContainer>
+QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveAliases(int componentIndex)
+{
+ if (m_objectsWithAliases.isEmpty())
+ return QQmlError();
+
+ QQmlPropertyCacheAliasCreator<ObjectContainer> aliasCacheCreator(m_propertyCaches, m_compiler);
+
+ bool atLeastOneAliasResolved;
+ do {
+ atLeastOneAliasResolved = false;
+ QVector<int> pendingObjects;
+
+ for (int objectIndex: std::as_const(m_objectsWithAliases)) {
+
+ QQmlError error;
+ const auto &component = *m_compiler->objectAt(componentIndex);
+ const auto result = resolveAliasesInObject(component, objectIndex, &error);
+ if (error.isValid())
+ return error;
+
+ if (result == AllAliasesResolved) {
+ QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(
+ component, objectIndex, m_enginePrivate);
+ if (error.isValid())
+ return error;
+ atLeastOneAliasResolved = true;
+ } else if (result == SomeAliasesResolved) {
+ atLeastOneAliasResolved = true;
+ pendingObjects.append(objectIndex);
+ } else {
+ pendingObjects.append(objectIndex);
+ }
+ }
+ qSwap(m_objectsWithAliases, pendingObjects);
+ } while (!m_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
+
+ if (!atLeastOneAliasResolved && !m_objectsWithAliases.isEmpty()) {
+ const CompiledObject *obj = m_compiler->objectAt(m_objectsWithAliases.first());
+ for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
+ if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved))
+ return error(alias->location, tr("Circular alias reference detected"));
+ }
+ }
+
+ return QQmlError();
+}
+
+template<typename ObjectContainer>
+void QQmlComponentAndAliasResolver<ObjectContainer>::resolveGeneralizedGroupProperties(
+ int componentIndex)
+{
+ const auto &component = *m_compiler->objectAt(componentIndex);
+ for (CompiledBinding *binding : m_generalizedGroupProperties)
+ resolveGeneralizedGroupProperty(component, binding);
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLCOMPONENTANDALIASRESOLVER_P_H
diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h
index 14874ab9cb..303fea7b46 100644
--- a/src/qml/qml/qqmlcomponentattached_p.h
+++ b/src/qml/qml/qqmlcomponentattached_p.h
@@ -16,6 +16,7 @@
//
#include <QtQml/qqml.h>
+#include <QtQml/qqmlcomponent.h>
#include <private/qtqmlglobal_p.h>
#include <QtCore/QObject>
@@ -23,16 +24,9 @@ QT_BEGIN_NAMESPACE
// implemented in qqmlcomponent.cpp
-class Q_QML_PRIVATE_EXPORT QQmlComponentAttached : public QObject
+class Q_QML_EXPORT QQmlComponentAttached : public QObject
{
Q_OBJECT
-
- // Used as attached object for QQmlComponent. We want qqmlcomponentattached_p.h to be #include'd
- // when registering QQmlComponent, but we cannot #include it from qqmlcomponent.h. Therefore we
- // force an anonymous type registration here.
- QML_ANONYMOUS
- QML_ADDED_IN_VERSION(2, 0)
- Q_CLASSINFO("QML.OmitFromQmlTypes", "true")
public:
QQmlComponentAttached(QObject *parent = nullptr);
~QQmlComponentAttached();
@@ -68,4 +62,7 @@ private:
QT_END_NAMESPACE
+// TODO: We still need this because we cannot properly use QML_ATTACHED with QML_FOREIGN.
+QML_DECLARE_TYPEINFO(QQmlComponent, QML_HAS_ATTACHED_PROPERTIES)
+
#endif // QQMLCOMPONENTATTACHED_P_H
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index a43bded441..cf6736deb9 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -23,30 +23,44 @@ QT_BEGIN_NAMESPACE
\brief The QQmlContext class defines a context within a QML engine.
\inmodule QtQml
- Contexts allow data to be exposed to the QML components instantiated by the
- QML engine.
+ Contexts hold the objects identified by \e id in a QML document. You
+ can use \l{nameForObject()} and \l{objectForName()} to retrieve them.
- Each QQmlContext contains a set of properties, distinct from its QObject
- properties, that allow data to be explicitly bound to a context by name. The
- context properties are defined and updated by calling
- QQmlContext::setContextProperty(). The following example shows a Qt model
- being bound to a context and then accessed from a QML file.
+ \note It is the responsibility of the creator to delete any QQmlContext it
+ constructs. If a QQmlContext is no longer needed, it must be destroyed
+ explicitly. The simplest way to ensure this is to give the QQmlContext a
+ \l{QObject::setParent()}{parent}.
- \code
- QQmlEngine engine;
- QStringListModel modelData;
- QQmlContext *context = new QQmlContext(engine.rootContext());
- context->setContextProperty("myModel", &modelData);
+ \section2 The Context Hierarchy
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0; ListView { model: myModel }", QUrl());
- QObject *window = component.create(context);
- \endcode
+ Contexts form a hierarchy. The root of this hierarchy is the QML engine's
+ \l {QQmlEngine::rootContext()}{root context}. Each QML component creates its
+ own context when instantiated and some QML elements create extra contexts
+ for themselves.
- \note It is the responsibility of the creator to delete any QQmlContext it
- constructs. If the \c context object in the example is no longer needed when the
- \c window component instance is destroyed, the \c context must be destroyed explicitly.
- The simplest way to ensure this is to set \c window as the parent of \c context.
+ While QML objects instantiated in a context are not strictly owned by that
+ context, their bindings are. If a context is destroyed, the property bindings of
+ outstanding QML objects will stop evaluating.
+
+ \section2 Context Properties
+
+ Contexts also allow data to be exposed to the QML components instantiated
+ by the QML engine. Such data is invisible to any tooling, including the
+ \l{Qt Quick Compiler} and to future readers of the QML documents in
+ question. It will only be exposed if the QML component is instantiated in
+ the specific C++ context you are envisioning. In other places, different
+ context data may be exposed instead.
+
+ Instead of using the QML context to expose data to your QML components, you
+ should either create additional object properties to hold the data or use
+ \l{QML_SINGLETON}{singletons}. See
+ \l{qtqml-cppintegration-exposecppstate.html}{Exposing C++ State to QML} for
+ a detailed explanation.
+
+ Each QQmlContext contains a set of properties, distinct from its QObject
+ properties, that allow data to be explicitly bound to a context by name. The
+ context properties can be defined and updated by calling
+ QQmlContext::setContextProperty().
To simplify binding and maintaining larger data sets, a context object can be set
on a QQmlContext. All the properties of the context object are available
@@ -55,59 +69,17 @@ QT_BEGIN_NAMESPACE
detected through the property's notify signal. Setting a context object is both
faster and easier than manually adding and maintaining context property values.
- The following example has the same effect as the previous one, but it uses a context
- object.
-
- \code
- class MyDataSet : public QObject {
- // ...
- Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
- // ...
- };
-
- MyDataSet myDataSet;
- QQmlEngine engine;
- QQmlContext *context = new QQmlContext(engine.rootContext());
- context->setContextObject(&myDataSet);
-
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0; ListView { model: myModel }", QUrl());
- component.create(context);
- \endcode
-
All properties added explicitly by QQmlContext::setContextProperty() take
precedence over the context object's properties.
- \section2 The Context Hierarchy
-
- Contexts form a hierarchy. The root of this hierarchy is the QML engine's
- \l {QQmlEngine::rootContext()}{root context}. Child contexts inherit
- the context properties of their parents; if a child context sets a context property
- that already exists in its parent, the new context property overrides that of the
- parent.
-
- The following example defines two contexts - \c context1 and \c context2. The
- second context overrides the "b" context property inherited from the first with a
- new value.
-
- \code
- QQmlEngine engine;
- QQmlContext *context1 = new QQmlContext(engine.rootContext());
- QQmlContext *context2 = new QQmlContext(context1);
-
- context1->setContextProperty("a", 9001);
- context1->setContextProperty("b", 9001);
-
- context2->setContextProperty("b", 42);
- \endcode
-
- While QML objects instantiated in a context are not strictly owned by that
- context, their bindings are. If a context is destroyed, the property bindings of
- outstanding QML objects will stop evaluating.
+ Child contexts inherit the context properties of their parents; if a child
+ context sets a context property that already exists in its parent, the new
+ context property overrides that of the parent.
- \warning Setting the context object or adding new context properties after an object
- has been created in that context is an expensive operation (essentially forcing all bindings
- to reevaluate). Thus whenever possible you should complete "setup" of the context
+ \warning Setting the context object or adding new context properties after
+ an object has been created in that context is an expensive operation
+ (essentially forcing all bindings to re-evaluate). Thus, if you need to use
+ context properties, you should at least complete the "setup" of the context
before using it to create any objects.
\sa {qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++ Types to QML}
@@ -208,6 +180,9 @@ QObject *QQmlContext::contextObject() const
/*!
Set the context \a object.
+
+ \note You should not use context objects to inject values into your QML
+ components. Use singletons or regular object properties instead.
*/
void QQmlContext::setContextObject(QObject *object)
{
@@ -231,6 +206,9 @@ void QQmlContext::setContextObject(QObject *object)
/*!
Set a the \a value of the \a name property on this context.
+
+ \note You should not use context properties to inject values into your QML
+ components. Use singletons or regular object properties instead.
*/
void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
{
@@ -250,6 +228,11 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
return;
}
+ if (bool isNumber = false; name.toUInt(&isNumber), isNumber) {
+ qWarning("QQmlContext: Using numbers as context properties will be disallowed in a future Qt version.");
+ QT7_ONLY(return;)
+ }
+
int idx = data->propertyIndex(name);
if (idx == -1) {
data->addPropertyNameAndIndex(name, data->numIdValues() + d->numPropertyValues());
@@ -271,6 +254,9 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
Set the \a value of the \a name property on this context.
QQmlContext does \b not take ownership of \a value.
+
+ \note You should not use context properties to inject values into your QML
+ components. Use singletons or regular object properties instead.
*/
void QQmlContext::setContextProperty(const QString &name, QObject *value)
{
@@ -286,6 +272,9 @@ void QQmlContext::setContextProperty(const QString &name, QObject *value)
refreshing expressions, and is therefore recommended
instead of calling \l setContextProperty() for each individual property.
+ \note You should not use context properties to inject values into your QML
+ components. Use singletons or regular object properties instead.
+
\sa QQmlContext::setContextProperty()
*/
void QQmlContext::setContextProperties(const QList<PropertyPair> &properties)
@@ -464,7 +453,7 @@ QJSValue QQmlContext::importedScript(const QString &name) const
{
Q_D(const QQmlContext);
- QQmlTypeNameCache::Result r = d->m_data->imports()->query(name);
+ QQmlTypeNameCache::Result r = d->m_data->imports()->query(name, QQmlTypeLoader::get(engine()));
QV4::Scope scope(engine()->handle());
QV4::ScopedObject scripts(scope, d->m_data->importedScripts().valueRef());
return scripts ? QJSValuePrivate::fromReturnedValue(scripts->get(r.scriptIndex))
diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h
index eb6210d5b3..b02aefe1d6 100644
--- a/src/qml/qml/qqmlcontext.h
+++ b/src/qml/qml/qqmlcontext.h
@@ -17,7 +17,6 @@ QT_BEGIN_NAMESPACE
class QString;
class QQmlEngine;
-class QQmlRefCount;
class QQmlContextPrivate;
class QQmlCompositeTypeData;
class QQmlContextData;
diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h
index 99f2467dd9..c8e362ec8d 100644
--- a/src/qml/qml/qqmlcontextdata_p.h
+++ b/src/qml/qml/qqmlcontextdata_p.h
@@ -30,7 +30,7 @@ class QQmlGuardedContextData;
class QQmlJavaScriptExpression;
class QQmlIncubatorPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlContextData
+class Q_QML_EXPORT QQmlContextData
{
public:
static QQmlRefPointer<QQmlContextData> createRefCounted(
@@ -128,6 +128,28 @@ public:
QObject *contextObject() const { return m_contextObject; }
void setContextObject(QObject *contextObject) { m_contextObject = contextObject; }
+ template<typename HandleSelf, typename HandleLinked>
+ void deepClearContextObject(
+ QObject *contextObject, HandleSelf &&handleSelf, HandleLinked &&handleLinked) {
+ for (QQmlContextData *lc = m_linkedContext.data(); lc; lc = lc->m_linkedContext.data()) {
+ handleLinked(lc);
+ if (lc->m_contextObject == contextObject)
+ lc->m_contextObject = nullptr;
+ }
+
+ handleSelf(this);
+ if (m_contextObject == contextObject)
+ m_contextObject = nullptr;
+ }
+
+ void deepClearContextObject(QObject *contextObject)
+ {
+ deepClearContextObject(
+ contextObject,
+ [](QQmlContextData *self) { self->emitDestruction(); },
+ [](QQmlContextData *){});
+ }
+
QQmlEngine *engine() const { return m_engine; }
void setEngine(QQmlEngine *engine) { m_engine = engine; }
@@ -266,6 +288,10 @@ public:
void addExpression(QQmlJavaScriptExpression *expression);
+ bool valueTypesAreAddressable() const {
+ return m_typeCompilationUnit && m_typeCompilationUnit->valueTypesAreAddressable();
+ }
+
private:
friend class QQmlGuardedContextData;
friend class QQmlContextPrivate;
@@ -358,7 +384,7 @@ private:
quint32 m_ownedByParent:1;
quint32 m_ownedByPublicContext:1;
quint32 m_hasExtraObject:1; // used in QQmlDelegateModelItem::dataForObject to find the corresponding QQmlDelegateModelItem of an object
- quint32 m_dummy:23;
+ Q_DECL_UNUSED_MEMBER quint32 m_dummy:23;
QQmlContext *m_publicContext = nullptr;
union {
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index d3f43cb8c4..bd632d25a6 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -103,10 +103,13 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
const QString scope = script.left(dot);
if (scope != QLatin1String("Qt")) {
- if (imports.isNull())
+ if (!engine || imports.isNull())
return -1;
QQmlType type;
+ QQmlTypeLoader *typeLoader = QQmlTypeLoader::get(engine);
+ Q_ASSERT(typeLoader);
+
if (imports.isT1()) {
QQmlImportNamespace *ns = nullptr;
@@ -115,7 +118,7 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
bool recursionDetected = false;
if (!imports.asT1()->resolveType(
- scope, &type, nullptr, &ns, nullptr,
+ typeLoader, scope, &type, nullptr, &ns, nullptr,
QQmlType::AnyRegistrationType, &recursionDetected)) {
return -1;
}
@@ -123,7 +126,7 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
if (!type.isValid() && ns != nullptr) {
dot = nextDot(dot);
if (dot == -1 || !imports.asT1()->resolveType(
- script.left(dot), &type, nullptr, nullptr, nullptr,
+ typeLoader, script.left(dot), &type, nullptr, nullptr, nullptr,
QQmlType::AnyRegistrationType, &recursionDetected)) {
return -1;
}
@@ -131,13 +134,15 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
} else {
// Allow recursion so that we can find enums from the same document.
const QQmlTypeNameCache::Result result
- = imports.asT2()->query<QQmlImport::AllowRecursion>(scope);
+ = imports.asT2()->query<QQmlImport::AllowRecursion>(scope, typeLoader);
if (result.isValid()) {
type = result.type;
} else if (result.importNamespace) {
dot = nextDot(dot);
- if (dot != -1)
- type = imports.asT2()->query<QQmlImport::AllowRecursion>(script.left(dot)).type;
+ if (dot != -1) {
+ type = imports.asT2()->query<QQmlImport::AllowRecursion>(
+ script.left(dot), typeLoader).type;
+ }
}
}
@@ -196,10 +201,10 @@ int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const
*/
const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const
{
- if (!imports.isT1())
+ if (!engine || !imports.isT1())
return nullptr;
QQmlType qmltype;
- if (!imports.asT1()->resolveType(name, &qmltype, nullptr, nullptr, nullptr))
+ if (!imports.asT1()->resolveType(QQmlTypeLoader::get(engine), name, &qmltype, nullptr, nullptr))
return nullptr;
return qmltype.metaObject();
}
diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h
index fc0a814af6..4a3628fdb1 100644
--- a/src/qml/qml/qqmlcustomparser_p.h
+++ b/src/qml/qml/qqmlcustomparser_p.h
@@ -20,14 +20,13 @@
#include <private/qv4compileddata_p.h>
#include <QtCore/qbytearray.h>
-#include <QtCore/qxmlstream.h>
QT_BEGIN_NAMESPACE
class QQmlPropertyValidator;
class QQmlEnginePrivate;
-class Q_QML_PRIVATE_EXPORT QQmlCustomParser
+class Q_QML_EXPORT QQmlCustomParser
{
public:
enum Flag {
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 5b7c05c61e..7055ca94e6 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -52,10 +52,12 @@ struct Binding;
// default state for elemental object allocations. This is crucial in the
// workings of the QQmlInstruction::CreateSimpleObject instruction.
// Don't change anything here without first considering that case!
-class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData
+class Q_QML_EXPORT QQmlData : public QAbstractDeclarativeData
{
public:
- QQmlData();
+ enum Ownership { DoesNotOwnMemory, OwnsMemory };
+
+ QQmlData(Ownership ownership);
~QQmlData();
static inline void init() {
@@ -122,24 +124,24 @@ public:
};
struct NotifyList {
- quint64 connectionMask;
-
- quint16 maximumTodoIndex;
- quint16 notifiesSize;
-
- QQmlNotifierEndpoint *todo;
- QQmlNotifierEndpoint**notifies;
+ QAtomicInteger<quint64> connectionMask;
+ QQmlNotifierEndpoint *todo = nullptr;
+ QQmlNotifierEndpoint**notifies = nullptr;
+ quint16 maximumTodoIndex = 0;
+ quint16 notifiesSize = 0;
void layout();
private:
void layout(QQmlNotifierEndpoint*);
};
- NotifyList *notifyList;
+ QAtomicPointer<NotifyList> notifyList;
- inline QQmlNotifierEndpoint *notify(int index);
+ inline QQmlNotifierEndpoint *notify(int index) const;
void addNotify(int index, QQmlNotifierEndpoint *);
int endpointCount(int index);
bool signalHasEndpoint(int index) const;
- void disconnectNotifiers();
+
+ enum class DeleteNotifyList { Yes, No };
+ void disconnectNotifiers(DeleteNotifyList doDelete);
// The context that created the C++ object; not refcounted to prevent cycles
QQmlContextData *context = nullptr;
@@ -147,13 +149,13 @@ public:
QQmlContextData *outerContext = nullptr;
QQmlRefPointer<QQmlContextData> ownContext;
- QQmlAbstractBinding *bindings;
- QQmlBoundSignal *signalHandlers;
+ QQmlAbstractBinding *bindings = nullptr;
+ QQmlBoundSignal *signalHandlers = nullptr;
std::vector<QQmlPropertyObserver> propertyObservers;
// Linked list for QQmlContext::contextObjects
- QQmlData *nextContextObject;
- QQmlData**prevContextObject;
+ QQmlData *nextContextObject = nullptr;
+ QQmlData**prevContextObject = nullptr;
inline bool hasBindingBit(int) const;
inline void setBindingBit(QObject *obj, int);
@@ -163,10 +165,10 @@ public:
inline void setPendingBindingBit(QObject *obj, int);
inline void clearPendingBindingBit(int);
- quint16 lineNumber;
- quint16 columnNumber;
+ quint16 lineNumber = 0;
+ quint16 columnNumber = 0;
- quint32 jsEngineId; // id of the engine that created the jsWrapper
+ quint32 jsEngineId = 0; // id of the engine that created the jsWrapper
struct DeferredData {
DeferredData();
@@ -192,7 +194,7 @@ public:
QQmlPropertyCache::ConstPtr propertyCache;
- QQmlGuardImpl *guards;
+ QQmlGuardImpl *guards = nullptr;
static QQmlData *get(QObjectPrivate *priv, bool create) {
// If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
@@ -260,7 +262,7 @@ public:
private:
// For attachedProperties
- mutable QQmlDataExtended *extendedData;
+ mutable QQmlDataExtended *extendedData = nullptr;
Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object);
@@ -295,7 +297,7 @@ private:
Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
- Q_DISABLE_COPY(QQmlData);
+ Q_DISABLE_COPY_MOVE(QQmlData);
};
bool QQmlData::wasDeleted(const QObjectPrivate *priv)
@@ -316,23 +318,31 @@ bool QQmlData::wasDeleted(const QObject *object)
return QQmlData::wasDeleted(priv);
}
-QQmlNotifierEndpoint *QQmlData::notify(int index)
+inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
+{
+ return connectionMask & (1ULL << quint64(index % 64));
+}
+
+QQmlNotifierEndpoint *QQmlData::notify(int index) const
{
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
Q_ASSERT(index <= 0xFFFF);
- if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) {
+ NotifyList *list = notifyList.loadRelaxed();
+ if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
return nullptr;
- } else if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else if (index <= notifyList->maximumTodoIndex) {
- notifyList->layout();
- }
- if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else {
- return nullptr;
+ if (index < list->notifiesSize)
+ return list->notifies[index];
+
+ if (index <= list->maximumTodoIndex) {
+ list->layout();
+ if (index < list->notifiesSize)
+ return list->notifies[index];
}
+
+ return nullptr;
}
/*
@@ -341,7 +351,19 @@ QQmlNotifierEndpoint *QQmlData::notify(int index)
*/
inline bool QQmlData::signalHasEndpoint(int index) const
{
- return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64)));
+ // This can be called from any thread.
+ // We still use relaxed semantics. If we're on a thread different from the "home" thread
+ // of the QQmlData, two interesting things might happen:
+ //
+ // 1. The list might go away while we hold it. In that case we are dealing with an object whose
+ // QObject dtor is being executed concurrently. This is UB already without the notify lists.
+ // Therefore, we don't need to consider it.
+ // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
+ // we "misreport" the endpoint. Since ordering of events across threads is inherently
+ // nondeterministic, either result is correct in that case. We can accept it.
+
+ NotifyList *list = notifyList.loadRelaxed();
+ return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
}
bool QQmlData::hasBindingBit(int coreIndex) const
diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp
index 054317b5ad..6b354c337f 100644
--- a/src/qml/qml/qqmldatablob.cpp
+++ b/src/qml/qml/qqmldatablob.cpp
@@ -39,16 +39,14 @@ The QQmlTypeLoader invokes callbacks on the QQmlDataBlob as data becomes availab
This enum describes the status of the data blob.
-\list
-\li Null The blob has not yet been loaded by a QQmlTypeLoader
-\li Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been
- invoked or has not yet returned.
-\li WaitingForDependencies The blob is waiting for dependencies to be done before continuing.
- This status only occurs after the QQmlDataBlob::setData() callback has been made, and when the
- blob has outstanding dependencies.
-\li Complete The blob's data has been loaded and all dependencies are done.
-\li Error An error has been set on this blob.
-\endlist
+\value Null The blob has not yet been loaded by a QQmlTypeLoader
+\value Loading The blob is loading network data. The QQmlDataBlob::setData() callback has
+ not yet been invoked or has not yet returned.
+\value WaitingForDependencies The blob is waiting for dependencies to be done before continuing.
+ This status only occurs after the QQmlDataBlob::setData() callback has been made,
+ and when the blob has outstanding dependencies.
+\value Complete The blob's data has been loaded and all dependencies are done.
+\value Error An error has been set on this blob.
*/
/*!
@@ -56,11 +54,9 @@ This enum describes the status of the data blob.
This enum describes the type of the data blob.
-\list
-\li QmlFile This is a QQmlTypeData
-\li JavaScriptFile This is a QQmlScriptData
-\li QmldirFile This is a QQmlQmldirData
-\endlist
+\value QmlFile This is a QQmlTypeData
+\value JavaScriptFile This is a QQmlScriptData
+\value QmldirFile This is a QQmlQmldirData
*/
/*!
diff --git a/src/qml/qml/qqmldatablob_p.h b/src/qml/qml/qqmldatablob_p.h
index b54f71c79e..87a7e8e2e8 100644
--- a/src/qml/qml/qqmldatablob_p.h
+++ b/src/qml/qml/qqmldatablob_p.h
@@ -17,7 +17,6 @@
#include <private/qqmlrefcount_p.h>
#include <private/qqmljsdiagnosticmessage_p.h>
-#include <private/qv4compileddata_p.h>
#if QT_CONFIG(qml_network)
#include <QtNetwork/qnetworkreply.h>
@@ -35,7 +34,7 @@
QT_BEGIN_NAMESPACE
class QQmlTypeLoader;
-class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount
+class Q_QML_EXPORT QQmlDataBlob : public QQmlRefCounted<QQmlDataBlob>
{
public:
using Ptr = QQmlRefPointer<QQmlDataBlob>;
@@ -56,7 +55,7 @@ public:
};
QQmlDataBlob(const QUrl &, Type, QQmlTypeLoader* manager);
- ~QQmlDataBlob() override;
+ virtual ~QQmlDataBlob();
void startLoading();
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 96a5679599..efd8519a58 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -70,7 +70,7 @@ void QQmlDelayedCallQueue::init(QV4::ExecutionEngine* engine)
m_tickedMethod = metaObject.method(methodIndex);
}
-QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::ExecutionEngine *engine, QQmlV4Function *args)
+QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::ExecutionEngine *engine, QQmlV4FunctionPtr args)
{
QQmlDelayedCallQueue *self = engine->delayedCallQueue();
@@ -142,7 +142,7 @@ QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::Executi
return QV4::Encode::undefined();
}
-void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, QQmlV4Function *args, int offset, QV4::ExecutionEngine *engine)
+void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, QQmlV4FunctionPtr args, int offset, QV4::ExecutionEngine *engine)
{
const int length = args->length() - offset;
if (length == 0) {
diff --git a/src/qml/qml/qqmldelayedcallqueue_p.h b/src/qml/qml/qqmldelayedcallqueue_p.h
index 9b66e85a14..88f0c4d118 100644
--- a/src/qml/qml/qqmldelayedcallqueue_p.h
+++ b/src/qml/qml/qqmldelayedcallqueue_p.h
@@ -24,8 +24,6 @@
QT_BEGIN_NAMESPACE
-class QQmlV4Function;
-
class QQmlDelayedCallQueue : public QObject
{
Q_OBJECT
@@ -36,7 +34,7 @@ public:
void init(QV4::ExecutionEngine *);
static QV4::ReturnedValue addUniquelyAndExecuteLater(QV4::ExecutionEngine *engine,
- QQmlV4Function *args);
+ QQmlV4FunctionPtr args);
public Q_SLOTS:
void ticked();
@@ -56,7 +54,7 @@ private:
bool m_guarded;
};
- void storeAnyArguments(DelayedFunctionCall& dfc, QQmlV4Function *args, int offset, QV4::ExecutionEngine *engine);
+ void storeAnyArguments(DelayedFunctionCall& dfc, QQmlV4FunctionPtr args, int offset, QV4::ExecutionEngine *engine);
void executeAllExpired_Later();
QV4::ExecutionEngine *m_engine;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 7c218276c2..c7812059a1 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -62,6 +62,8 @@
QT_BEGIN_NAMESPACE
+void qml_register_types_QML();
+
/*!
\qmltype QtObject
\instantiates QObject
@@ -215,23 +217,16 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
{
QObjectPrivate *p = QObjectPrivate::get(o);
if (QQmlData *d = QQmlData::get(p)) {
+ const auto invalidate = [](QQmlContextData *c) {c->invalidate();};
if (d->ownContext) {
- for (QQmlRefPointer<QQmlContextData> lc = d->ownContext->linkedContext(); lc;
- lc = lc->linkedContext()) {
- lc->invalidate();
- if (lc->contextObject() == o)
- lc->setContextObject(nullptr);
- }
- d->ownContext->invalidate();
- if (d->ownContext->contextObject() == o)
- d->ownContext->setContextObject(nullptr);
+ d->ownContext->deepClearContextObject(o, invalidate, invalidate);
d->ownContext.reset();
d->context = nullptr;
+ Q_ASSERT(!d->outerContext || d->outerContext->contextObject() != o);
+ } else if (d->outerContext && d->outerContext->contextObject() == o) {
+ d->outerContext->deepClearContextObject(o, invalidate, invalidate);
}
- if (d->outerContext && d->outerContext->contextObject() == o)
- d->outerContext->setContextObject(nullptr);
-
if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) {
// This is somewhat dangerous because another thread might concurrently
// try to resolve the dynamic metaobject. In practice this will then
@@ -255,14 +250,11 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
}
}
-QQmlData::QQmlData()
- : ownMemory(true), indestructible(true), explicitIndestructibleSet(false),
+QQmlData::QQmlData(Ownership ownership)
+ : ownMemory(ownership == OwnsMemory), indestructible(true), explicitIndestructibleSet(false),
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
- hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false),
- bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr),
- bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr),
- lineNumber(0), columnNumber(0), jsEngineId(0),
- guards(nullptr), extendedData(nullptr)
+ hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false), dummy(0),
+ bindingBitsArraySize(InlineBindingArraySize)
{
memset(bindingBitsValue, 0, sizeof(bindingBitsValue));
init();
@@ -312,7 +304,10 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
// QQmlEngine to emit signals from a different thread. These signals are then automatically
// marshalled back onto the QObject's thread and handled by QML from there. This is tested
// by the qqmlecmascript::threadSignal() autotest.
- if (!ddata->notifyList)
+
+ // Relaxed semantics here. If we're on a different thread we might schedule a useless event,
+ // but that should be rare.
+ if (!ddata->notifyList.loadRelaxed())
return;
auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed();
@@ -386,11 +381,15 @@ int QQmlData::endpointCount(int index)
void QQmlData::markAsDeleted(QObject *o)
{
- QQmlData::setQueuedForDeletion(o);
-
- QObjectPrivate *p = QObjectPrivate::get(o);
- for (QList<QObject *>::const_iterator it = p->children.constBegin(), end = p->children.constEnd(); it != end; ++it) {
- QQmlData::markAsDeleted(*it);
+ QVarLengthArray<QObject *> workStack;
+ workStack.push_back(o);
+ while (!workStack.isEmpty()) {
+ auto currentObject = workStack.last();
+ workStack.pop_back();
+ QQmlData::setQueuedForDeletion(currentObject);
+ auto currentObjectPriv = QObjectPrivate::get(currentObject);
+ for (QObject *child: std::as_const(currentObjectPriv->children))
+ workStack.push_back(child);
}
}
@@ -400,9 +399,7 @@ void QQmlData::setQueuedForDeletion(QObject *object)
if (QQmlData *ddata = QQmlData::get(object)) {
if (ddata->ownContext) {
Q_ASSERT(ddata->ownContext.data() == ddata->context);
- ddata->context->emitDestruction();
- if (ddata->ownContext->contextObject() == object)
- ddata->ownContext->setContextObject(nullptr);
+ ddata->ownContext->deepClearContextObject(object);
ddata->ownContext.reset();
ddata->context = nullptr;
}
@@ -413,7 +410,7 @@ void QQmlData::setQueuedForDeletion(QObject *object)
// possible to get the metaobject anymore.
// Also, there is no point in evaluating bindings in order to set properties on
// half-deleted objects.
- ddata->disconnectNotifiers();
+ ddata->disconnectNotifiers(DeleteNotifyList::No);
}
}
}
@@ -437,38 +434,57 @@ void QQmlData::flushPendingBinding(int coreIndex)
QQmlData::DeferredData::DeferredData() = default;
QQmlData::DeferredData::~DeferredData() = default;
+template<>
+int qmlRegisterType<void>(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+{
+ QQmlPrivate::RegisterType type = {
+ QQmlPrivate::RegisterType::CurrentVersion,
+ QMetaType(),
+ QMetaType(),
+ 0, nullptr, nullptr,
+ QString(),
+ nullptr,
+ uri,
+ QTypeRevision::fromVersion(versionMajor, versionMinor),
+ qmlName,
+ nullptr,
+ nullptr,
+ nullptr,
+ -1,
+ -1,
+ -1,
+ nullptr,
+ nullptr,
+ nullptr,
+ QTypeRevision::zero(),
+ -1,
+ QQmlPrivate::ValueTypeCreationMethod::None,
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+}
+
bool QQmlEnginePrivate::baseModulesUninitialized = true;
void QQmlEnginePrivate::init()
{
Q_Q(QQmlEngine);
if (baseModulesUninitialized) {
-
- // required for the Compiler.
- qmlRegisterType<QObject>("QML", 1, 0, "QtObject");
- qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component");
- qmlRegisterAnonymousSequentialContainer<QList<QVariant>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<bool>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<int>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<float>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<double>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QString>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QUrl>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QDateTime>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QRegularExpression>>("QML", 1);
- qmlRegisterAnonymousSequentialContainer<QList<QByteArray>>("QML", 1);
+ // Register builtins
+ qml_register_types_QML();
// No need to specifically register those.
static_assert(std::is_same_v<QStringList, QList<QString>>);
static_assert(std::is_same_v<QVariantList, QList<QVariant>>);
- qRegisterMetaType<QVariant>();
qRegisterMetaType<QQmlScriptString>();
- qRegisterMetaType<QJSValue>();
qRegisterMetaType<QQmlComponent::Status>();
qRegisterMetaType<QList<QObject*> >();
qRegisterMetaType<QQmlBinding*>();
+ // Protect the module: We don't want any URL interceptor to mess with the builtins.
+ qmlProtectModule("QML", 1);
+
QQmlData::init();
baseModulesUninitialized = false;
}
@@ -484,29 +500,16 @@ void QQmlEnginePrivate::init()
\inmodule QtQml
\brief The QQmlEngine class provides an environment for instantiating QML components.
- Each QML component is instantiated in a QQmlContext.
- QQmlContext's are essential for passing data to QML
- components. In QML, contexts are arranged hierarchically and this
- hierarchy is managed by the QQmlEngine.
-
- Prior to creating any QML components, an application must have
- created a QQmlEngine to gain access to a QML context. The
- following example shows how to create a simple Text item.
-
- \code
- QQmlEngine engine;
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0\nText { text: \"Hello world!\" }", QUrl());
- QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
-
- //add item to view, etc
- ...
- \endcode
+ A QQmlEngine is used to manage \l{QQmlComponent}{components} and objects created from
+ them and execute their bindings and functions. QQmlEngine also inherits from
+ \l{QJSEngine} which allows seamless integration between your QML components and
+ JavaScript code.
- In this case, the Text item will be created in the engine's
- \l {QQmlEngine::rootContext()}{root context}.
+ Each QML component is instantiated in a QQmlContext. In QML, contexts are arranged
+ hierarchically and this hierarchy is managed by the QQmlEngine. By default,
+ components are instantiated in the \l {QQmlEngine::rootContext()}{root context}.
- \sa QQmlComponent, QQmlContext, {QML Global Object}
+ \sa QQmlComponent, QQmlContext, {QML Global Object}, QQmlApplicationEngine
*/
/*!
@@ -537,11 +540,12 @@ QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent)
invalidated, but not destroyed (unless they are parented to the
QQmlEngine object).
- See QJSEngine docs for details on cleaning up the JS engine.
+ See ~QJSEngine() for details on cleaning up the JS engine.
*/
QQmlEngine::~QQmlEngine()
{
Q_D(QQmlEngine);
+ handle()->inShutdown = true;
QJSEnginePrivate::removeFromDebugServer(this);
// Emit onDestruction signals for the root context before
@@ -610,9 +614,20 @@ QQmlEngine::~QQmlEngine()
void QQmlEngine::clearComponentCache()
{
Q_D(QQmlEngine);
+
+ // Contexts can hold on to CUs but live on the JS heap.
+ // Use a non-incremental GC run to get rid of those.
+ QV4::MemoryManager *mm = handle()->memoryManager;
+ auto oldLimit = mm->gcStateMachine->timeLimit;
+ mm->setGCTimeLimit(-1);
+ mm->runGC();
+ mm->gcStateMachine->timeLimit = std::move(oldLimit);
+
+ handle()->clearCompilationUnits();
d->typeLoader.lock();
d->typeLoader.clearCache();
d->typeLoader.unlock();
+ QQmlMetaType::freeUnusedTypesAndCaches();
}
/*!
@@ -630,6 +645,7 @@ void QQmlEngine::clearComponentCache()
void QQmlEngine::trimComponentCache()
{
Q_D(QQmlEngine);
+ handle()->trimCompilationUnits();
d->typeLoader.trimCache();
}
@@ -927,6 +943,45 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
d->outputWarningsToMsgLog = enabled;
}
+
+/*!
+ \since 6.6
+ If this method is called inside of a function that is part of
+ a binding in QML, the binding will be treated as a translation binding.
+
+ \code
+ class I18nAwareClass : public QObject {
+
+ //...
+
+ QString text() const
+ {
+ if (auto engine = qmlEngine(this))
+ engine->markCurrentFunctionAsTranslationBinding();
+ return tr("Hello, world!");
+ }
+ };
+ \endcode
+
+ \note This function is mostly useful if you wish to provide your
+ own alternative to the qsTr function. To ensure that properties
+ exposed from C++ classes are updated on language changes, it is
+ instead recommended to react to \c LanguageChange events. That
+ is a more general mechanism which also works when the class is
+ used in a non-QML context, and has slightly less overhead. However,
+ using \c markCurrentFunctionAsTranslationBinding can be acceptable
+ when the class is already closely tied to the QML engine.
+ For more details, see \l {Prepare for Dynamic Language Changes}
+
+ \sa QQmlEngine::retranslate
+*/
+void QQmlEngine::markCurrentFunctionAsTranslationBinding()
+{
+ Q_D(QQmlEngine);
+ if (auto propertyCapture = d->propertyCapture)
+ propertyCapture->captureTranslation();
+}
+
/*!
\internal
@@ -1194,11 +1249,12 @@ void QQmlData::deferData(
deferData->context = context;
const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(objectIndex);
- const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex);
+ const QV4::CompiledData::BindingPropertyData *propertyData
+ = compilationUnit->bindingPropertyDataPerObjectAt(objectIndex);
const QV4::CompiledData::Binding *binding = compiledObject->bindingTable();
for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) {
- const QQmlPropertyData *property = propertyData.at(i);
+ const QQmlPropertyData *property = propertyData->at(i);
if (binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding))
deferData->bindings.insert(property ? property->coreIndex() : -1, binding);
}
@@ -1222,49 +1278,73 @@ void QQmlData::releaseDeferredData()
void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint)
{
- if (!notifyList) {
- notifyList = (NotifyList *)malloc(sizeof(NotifyList));
- notifyList->connectionMask = 0;
- notifyList->maximumTodoIndex = 0;
- notifyList->notifiesSize = 0;
- notifyList->todo = nullptr;
- notifyList->notifies = nullptr;
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
+ NotifyList *list = notifyList.loadRelaxed();
+
+ if (!list) {
+ list = new NotifyList;
+ // We don't really care when this change takes effect on other threads. The notifyList can
+ // only become non-null once in the life time of a QQmlData. It becomes null again when the
+ // underlying QObject is deleted. At that point any interaction with the QQmlData is UB
+ // anyway. So, for all intents and purposese, the list becomes non-null once and then stays
+ // non-null "forever". We can apply relaxed semantics.
+ notifyList.storeRelaxed(list);
}
Q_ASSERT(!endpoint->isConnected());
index = qMin(index, 0xFFFF - 1);
- notifyList->connectionMask |= (1ULL << quint64(index % 64));
- if (index < notifyList->notifiesSize) {
+ // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other
+ // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying
+ // the conenctionMask in the presence of concurrent modification, any result is correct.
+ list->connectionMask.storeRelaxed(
+ list->connectionMask.loadRelaxed() | (1ULL << quint64(index % 64)));
- endpoint->next = notifyList->notifies[index];
+ if (index < list->notifiesSize) {
+ endpoint->next = list->notifies[index];
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->notifies[index];
- notifyList->notifies[index] = endpoint;
-
+ endpoint->prev = &list->notifies[index];
+ list->notifies[index] = endpoint;
} else {
- notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index);
+ list->maximumTodoIndex = qMax(int(list->maximumTodoIndex), index);
- endpoint->next = notifyList->todo;
+ endpoint->next = list->todo;
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->todo;
- notifyList->todo = endpoint;
+ endpoint->prev = &list->todo;
+ list->todo = endpoint;
}
}
-void QQmlData::disconnectNotifiers()
+void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete)
{
- if (notifyList) {
- while (notifyList->todo)
- notifyList->todo->disconnect();
- for (int ii = 0; ii < notifyList->notifiesSize; ++ii) {
- while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii])
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+ if (NotifyList *list = notifyList.loadRelaxed()) {
+ while (QQmlNotifierEndpoint *todo = list->todo)
+ todo->disconnect();
+ for (int ii = 0; ii < list->notifiesSize; ++ii) {
+ while (QQmlNotifierEndpoint *ep = list->notifies[ii])
ep->disconnect();
}
- free(notifyList->notifies);
- free(notifyList);
- notifyList = nullptr;
+ free(list->notifies);
+
+ if (doDelete == DeleteNotifyList::Yes) {
+ // We can only get here from QQmlData::destroyed(), and that can only come from the
+ // the QObject dtor. If you're still sending signals at that point you have UB already
+ // without any threads. Therefore, it's enough to apply relaxed semantics.
+ notifyList.storeRelaxed(nullptr);
+ delete list;
+ } else {
+ // We can use relaxed semantics here. The worst thing that can happen is that some
+ // signal is falsely reported as connected. Signal connectedness across threads
+ // is not quite deterministic anyway.
+ list->connectionMask.storeRelaxed(0);
+ list->maximumTodoIndex = 0;
+ list->notifiesSize = 0;
+ list->notifies = nullptr;
+
+ }
}
}
@@ -1349,7 +1429,7 @@ void QQmlData::destroyed(QObject *object)
guard->objectDestroyed(guard);
}
- disconnectNotifiers();
+ disconnectNotifiers(DeleteNotifyList::Yes);
if (extendedData)
delete extendedData;
@@ -1390,7 +1470,7 @@ QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv)
{
Q_ASSERT(priv);
Q_ASSERT(!priv->isDeletingChildren);
- priv->declarativeData = new QQmlData;
+ priv->declarativeData = new QQmlData(OwnsMemory);
return static_cast<QQmlData *>(priv->declarativeData);
}
@@ -1545,7 +1625,8 @@ void QQmlEnginePrivate::cleanupScarceResources()
The newly added \a path will be first in the importPathList().
- \sa setImportPathList(), {QML Modules}
+ \b {See also} \l setImportPathList(), \l {QML Modules},
+ and \l [QtQml] {QML Import Path}
*/
void QQmlEngine::addImportPath(const QString& path)
{
@@ -1563,9 +1644,8 @@ void QQmlEngine::addImportPath(const QString& path)
provided by that module. A \c qmldir file is required for defining the
type version mapping and possibly QML extensions plugins.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
\sa addImportPath(), setImportPathList()
*/
@@ -1579,9 +1659,11 @@ QStringList QQmlEngine::importPathList() const
Sets \a paths as the list of directories where the engine searches for
installed modules in a URL-based directory structure.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
+
+ \warning Calling setImportPathList does not preserve the default
+ import paths.
\sa importPathList(), addImportPath()
*/
@@ -1745,14 +1827,13 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
{
Q_Q(QQmlEngine);
- QJSValue value = singletonInstances.value(type);
- if (!value.isUndefined()) {
- return value;
- }
-
- QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
+ QQmlType::SingletonInstanceInfo::ConstPtr siinfo = type.singletonInstanceInfo();
Q_ASSERT(siinfo != nullptr);
+ QJSValue value = singletonInstances.value(siinfo);
+ if (!value.isUndefined())
+ return value;
+
if (siinfo->scriptCallback) {
value = siinfo->scriptCallback(q, q);
if (value.isQObject()) {
@@ -1761,7 +1842,7 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
// should behave identically to QML singleton types.
q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
}
- singletonInstances.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
} else if (siinfo->qobjectCallback) {
QObject *o = siinfo->qobjectCallback(q, q);
@@ -1790,7 +1871,7 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
}
value = q->newQObject(o);
- singletonInstances.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
} else if (!siinfo->url.isEmpty()) {
QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
if (component.isError()) {
@@ -1801,7 +1882,7 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
}
QObject *o = component.beginCreate(q->rootContext());
value = q->newQObject(o);
- singletonInstances.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
component.completeCreate();
}
@@ -1879,12 +1960,20 @@ void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationU
QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &url)
{
+ QV4::ExecutionEngine *v4 = v4engine();
+ if (auto unit = v4->compilationUnitForUrl(url)) {
+ if (!unit->runtimeStrings)
+ unit->populate();
+ return unit.data();
+ }
+
auto unit = typeLoader.getType(url)->compilationUnit();
if (!unit)
return nullptr;
- if (!unit->engine)
- unit->linkToEngine(v4engine());
- return unit;
+
+ auto executable = v4->executableCompilationUnit(std::move(unit));
+ executable->populate();
+ return executable.data();
}
QQmlRefPointer<QQmlContextData>
@@ -1897,21 +1986,21 @@ QQmlEnginePrivate::createInternalContext(const QQmlRefPointer<QV4::ExecutableCom
QQmlRefPointer<QQmlContextData> context;
context = QQmlContextData::createRefCounted(parentContext);
context->setInternal(true);
- context->setImports(unit->typeNameCache);
+ context->setImports(unit->typeNameCache());
context->initFromTypeCompilationUnit(unit, subComponentIndex);
- if (isComponentRoot && unit->dependentScripts.size()) {
+ const auto *dependentScripts = unit->dependentScriptsPtr();
+ const qsizetype dependentScriptsSize = dependentScripts->size();
+ if (isComponentRoot && dependentScriptsSize) {
QV4::ExecutionEngine *v4 = v4engine();
Q_ASSERT(v4);
QV4::Scope scope(v4);
- QV4::ScopedObject scripts(scope, v4->newArrayObject(unit->dependentScripts.size()));
+ QV4::ScopedObject scripts(scope, v4->newArrayObject(dependentScriptsSize));
context->setImportedScripts(QV4::PersistentValue(v4, scripts.asReturnedValue()));
QV4::ScopedValue v(scope);
- for (int i = 0; i < unit->dependentScripts.size(); ++i) {
- QQmlRefPointer<QQmlScriptData> s = unit->dependentScripts.at(i);
- scripts->put(i, (v = s->scriptValueForContext(context)));
- }
+ for (qsizetype i = 0; i < dependentScriptsSize; ++i)
+ scripts->put(i, (v = dependentScripts->at(i)->scriptValueForContext(context)));
}
return context;
@@ -2032,10 +2121,12 @@ LoadHelper::LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri)
{
auto import = std::make_shared<PendingImport>();
- import->uri = uri.toString();
+ import->uri = m_uri;
QList<QQmlError> errorList;
- if (!Blob::addImport(import, &errorList))
- m_uri = QString(); // reset m_uri to remember the failure
+ if (!Blob::addImport(import, &errorList)) {
+ qCDebug(lcQmlImport) << "LoadHelper: Errors loading " << m_uri << errorList;
+ m_uri.clear(); // reset m_uri to remember the failure
+ }
}
LoadHelper::ResolveTypeResult LoadHelper::resolveType(QAnyStringView typeName)
@@ -2054,9 +2145,8 @@ LoadHelper::ResolveTypeResult LoadHelper::resolveType(QAnyStringView typeName)
QTypeRevision versionReturn;
QList<QQmlError> errors;
QQmlImportNamespace *ns_return = nullptr;
- m_importCache->resolveType(typeName.toString(), &type, &versionReturn,
- &ns_return,
- &errors);
+ m_importCache->resolveType(
+ typeLoader(), typeName.toString(), &type, &versionReturn, &ns_return, &errors);
return {ResolveTypeResult::ModuleFound, type};
}
@@ -2072,7 +2162,4 @@ bool LoadHelper::couldFindModule() const
QT_END_NAMESPACE
-#include "moc_qqmlengine_p.cpp"
-
#include "moc_qqmlengine.cpp"
-
diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h
index a8b19e41c6..9c20090613 100644
--- a/src/qml/qml/qqmlengine.h
+++ b/src/qml/qml/qqmlengine.h
@@ -123,6 +123,8 @@ public:
bool outputWarningsToStandardError() const;
void setOutputWarningsToStandardError(bool);
+ void markCurrentFunctionAsTranslationBinding();
+
template<typename T>
T singletonInstance(int qmlTypeId);
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 26969467b8..7c820679ba 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -39,6 +39,7 @@
#include <QtCore/qmetaobject.h>
#include <QtCore/qmutex.h>
#include <QtCore/qpair.h>
+#include <QtCore/qpointer.h>
#include <QtCore/qproperty.h>
#include <QtCore/qstack.h>
#include <QtCore/qstring.h>
@@ -57,14 +58,6 @@ class QQmlObjectCreator;
class QQmlProfiler;
class QQmlPropertyCapture;
-struct QObjectForeign {
- Q_GADGET
- QML_FOREIGN(QObject)
- QML_NAMED_ELEMENT(QtObject)
- QML_ADDED_IN_VERSION(2, 0)
- Q_CLASSINFO("QML.OmitFromQmlTypes", "true")
-};
-
// This needs to be declared here so that the pool for it can live in QQmlEnginePrivate.
// The inline method definitions are in qqmljavascriptexpression_p.h
class QQmlJavaScriptExpressionGuard : public QQmlNotifierEndpoint
@@ -81,9 +74,16 @@ public:
};
struct QPropertyChangeTrigger : QPropertyObserver {
- QPropertyChangeTrigger(QQmlJavaScriptExpression *expression) : QPropertyObserver(&QPropertyChangeTrigger::trigger), m_expression(expression) {}
- QQmlJavaScriptExpression * m_expression;
- QObject *target = nullptr;
+ Q_DISABLE_COPY_MOVE(QPropertyChangeTrigger)
+
+ QPropertyChangeTrigger(QQmlJavaScriptExpression *expression)
+ : QPropertyObserver(&QPropertyChangeTrigger::trigger)
+ , m_expression(expression)
+ {
+ }
+
+ QPointer<QObject> target;
+ QQmlJavaScriptExpression *m_expression;
int propertyIndex = 0;
static void trigger(QPropertyObserver *, QUntypedPropertyData *);
@@ -97,7 +97,7 @@ struct TriggerList : QPropertyChangeTrigger {
TriggerList *next = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlEnginePrivate : public QJSEnginePrivate
+class Q_QML_EXPORT QQmlEnginePrivate : public QJSEnginePrivate
{
Q_DECLARE_PUBLIC(QQmlEngine)
public:
@@ -207,8 +207,8 @@ public:
QQmlGadgetPtrWrapper *valueTypeInstance(QMetaType type)
{
int typeIndex = type.id();
- auto it = cachedValueTypeInstances.find(typeIndex);
- if (it != cachedValueTypeInstances.end())
+ auto it = cachedValueTypeInstances.constFind(typeIndex);
+ if (it != cachedValueTypeInstances.cend())
return *it;
if (QQmlValueType *valueType = QQmlMetaType::valueType(type)) {
@@ -245,35 +245,48 @@ public:
}
private:
- class SingletonInstances : private QHash<QQmlType, QJSValue>
+ class SingletonInstances : private QHash<QQmlType::SingletonInstanceInfo::ConstPtr, QJSValue>
{
public:
- void convertAndInsert(QV4::ExecutionEngine *engine, const QQmlType &type, QJSValue *value)
+ void convertAndInsert(
+ QV4::ExecutionEngine *engine, const QQmlType::SingletonInstanceInfo::ConstPtr &type,
+ QJSValue *value)
{
QJSValuePrivate::manageStringOnV4Heap(engine, value);
insert(type, *value);
}
- void clear() {
- for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
- QObject *instance = it.value().toQObject();
+ void clear()
+ {
+ const auto canDelete = [](QObject *instance, const auto &siinfo) -> bool {
if (!instance)
- continue;
+ return false;
+
+ if (!siinfo->url.isEmpty())
+ return true;
+
+ const auto *ddata = QQmlData::get(instance, false);
+ return !(ddata && ddata->indestructible && ddata->explicitIndestructibleSet);
+ };
+
+ for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
+ auto *instance = it.value().toQObject();
+ if (canDelete(instance, it.key()))
+ QQmlData::markAsDeleted(instance);
+ }
- if (it.key().singletonInstanceInfo()->url.isEmpty()) {
- const QQmlData *ddata = QQmlData::get(instance, false);
- if (ddata && ddata->indestructible && ddata->explicitIndestructibleSet)
- continue;
- }
+ for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
+ QObject *instance = it.value().toQObject();
- delete instance;
+ if (canDelete(instance, it.key()))
+ delete instance;
}
- QHash<QQmlType, QJSValue>::clear();
+ QHash<QQmlType::SingletonInstanceInfo::ConstPtr, QJSValue>::clear();
}
- using QHash<QQmlType, QJSValue>::value;
- using QHash<QQmlType, QJSValue>::take;
+ using QHash<QQmlType::SingletonInstanceInfo::ConstPtr, QJSValue>::value;
+ using QHash<QQmlType::SingletonInstanceInfo::ConstPtr, QJSValue>::take;
};
SingletonInstances singletonInstances;
@@ -371,7 +384,7 @@ QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e)
}
template<>
-Q_QML_PRIVATE_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type);
+Q_QML_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type);
template<typename T>
T QQmlEnginePrivate::singletonInstance(const QQmlType &type) {
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index 7e4483a604..1cc734206e 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -105,6 +105,14 @@ QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt
return;
const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
+ if (!scriptPrivate) {
+ // A null QQmlScriptStringPrivate is an empty expression without context.
+ // We may still want the explicitly passed context, though.
+ if (ctxt)
+ d->init(QQmlContextData::get(ctxt), QString(), scope);
+ return;
+ }
+
if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
return;
diff --git a/src/qml/qml/qqmlexpression.h b/src/qml/qml/qqmlexpression.h
index 912ad9173f..c029c2069f 100644
--- a/src/qml/qml/qqmlexpression.h
+++ b/src/qml/qml/qqmlexpression.h
@@ -14,7 +14,6 @@ QT_BEGIN_NAMESPACE
class QString;
-class QQmlRefCount;
class QQmlEngine;
class QQmlContext;
class QQmlExpressionPrivate;
diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp
index 319accb768..5af51769ab 100644
--- a/src/qml/qml/qqmlextensionplugin.cpp
+++ b/src/qml/qml/qqmlextensionplugin.cpp
@@ -42,8 +42,8 @@ QT_BEGIN_NAMESPACE
\fn void QQmlExtensionPlugin::registerTypes(const char *uri)
Registers the QML types in the given \a uri. Subclasses should implement
- this to call qmlRegisterType() for all types which are provided by the extension
- plugin.
+ this to call \l {QQmlEngine::}{qmlRegisterType()} for all types which are
+ provided by the extension plugin.
The \a uri is an identifier for the plugin generated by the QML engine
based on the name and path of the extension's plugin library.
@@ -166,8 +166,20 @@ void QQmlEngineExtensionPlugin::initializeEngine(QQmlEngine *engine, const char
\since 6.2
\relates QQmlEngineExtensionPlugin
- Ensures the plugin whose metadata-declaring class is named \a PluginName
- is linked into static builds.
+ Ensures the plugin whose metadata-declaring plugin extension class is named
+ \a PluginName is linked into static builds. For the modules created using
+ \l qt_add_qml_module, the default plugin extension class name is computed
+ from the QML module URI by replacing dots with underscores, unless the
+ \c CLASS_NAME argument is specified.
+
+ For example:
+ \badcode
+ qt_add_qml_module(myplugin
+ # The plugin extension class name in this case is my_Company_QmlComponents.
+ URI my.Company.QmlComponents
+ ...
+ )
+ \endcode
\sa Q_IMPORT_PLUGIN
*/
diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp
index d616616ebd..bac70e69bb 100644
--- a/src/qml/qml/qqmlfile.cpp
+++ b/src/qml/qml/qqmlfile.cpp
@@ -10,18 +10,27 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
-/*!
-\class QQmlFile
-\brief The QQmlFile class gives access to local and remote files.
+QT_BEGIN_NAMESPACE
-\internal
+/*!
+ \class QQmlFile
+ \inmodule QtQml
+ \since 5.0
+ \brief The QQmlFile class provides static utility methods to categorize URLs.
-Supports file:// and qrc:/ uris and whatever QNetworkAccessManager supports.
+ QQmlFile provides some static utility methods to categorize URLs
+ and file names the way \l{QQmlEngine} does when loading content from them.
*/
-#define QQMLFILE_MAX_REDIRECT_RECURSION 16
+/*!
+ \internal
-QT_BEGIN_NAMESPACE
+ \enum QQmlFile::Status
+ \value Null
+ \value Ready
+ \value Error
+ \value Loading
+ */
static char qrc_string[] = "qrc";
static char file_string[] = "file";
@@ -64,7 +73,6 @@ private:
QQmlEngine *m_engine;
QQmlFilePrivate *m_p;
- int m_redirectCount;
QNetworkReply *m_reply;
};
#endif
@@ -99,7 +107,7 @@ int QQmlFileNetworkReply::replyFinishedIndex = -1;
int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p, const QUrl &url)
-: m_engine(e), m_p(p), m_redirectCount(0), m_reply(nullptr)
+: m_engine(e), m_p(p), m_reply(nullptr)
{
if (finishedIndex == -1) {
finishedIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::finished).methodIndex();
@@ -133,27 +141,6 @@ QQmlFileNetworkReply::~QQmlFileNetworkReply()
void QQmlFileNetworkReply::networkFinished()
{
- ++m_redirectCount;
- if (m_redirectCount < QQMLFILE_MAX_REDIRECT_RECURSION) {
- QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = m_reply->url().resolved(redirect.toUrl());
-
- QNetworkRequest req(url);
- req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
-
- m_reply->deleteLater();
- m_reply = m_engine->networkAccessManager()->get(req);
-
- QMetaObject::connect(m_reply, replyFinishedIndex,
- this, networkFinishedIndex);
- QMetaObject::connect(m_reply, replyDownloadProgressIndex,
- this, networkDownloadProgressIndex);
-
- return;
- }
- }
-
if (m_reply->error()) {
m_p->errorString = m_reply->errorString();
m_p->error = QQmlFilePrivate::Network;
@@ -183,22 +170,36 @@ QQmlFilePrivate::QQmlFilePrivate()
{
}
+/*!
+ \internal
+ */
QQmlFile::QQmlFile()
: d(new QQmlFilePrivate)
{
}
-QQmlFile::QQmlFile(QQmlEngine *e, const QUrl &url)
+/*!
+ \internal
+ Constructs a QQmlFile for content at \a url, using \a engine to retrieve it.
+ */
+QQmlFile::QQmlFile(QQmlEngine *engine, const QUrl &url)
: d(new QQmlFilePrivate)
{
- load(e, url);
+ load(engine, url);
}
-QQmlFile::QQmlFile(QQmlEngine *e, const QString &url)
- : QQmlFile(e, QUrl(url))
+/*!
+ \internal
+ Constructs a QQmlFile for content at \a url, using \a engine to retrieve it.
+ */
+QQmlFile::QQmlFile(QQmlEngine *engine, const QString &url)
+ : QQmlFile(engine, QUrl(url))
{
}
+/*!
+ \internal
+ */
QQmlFile::~QQmlFile()
{
#if QT_CONFIG(qml_network)
@@ -208,26 +209,41 @@ QQmlFile::~QQmlFile()
d = nullptr;
}
+/*!
+ \internal
+ */
bool QQmlFile::isNull() const
{
return status() == Null;
}
+/*!
+ \internal
+ */
bool QQmlFile::isReady() const
{
return status() == Ready;
}
+/*!
+ \internal
+ */
bool QQmlFile::isError() const
{
return status() == Error;
}
+/*!
+ \internal
+ */
bool QQmlFile::isLoading() const
{
return status() == Loading;
}
+/*!
+ \internal
+ */
QUrl QQmlFile::url() const
{
if (!d->urlString.isEmpty()) {
@@ -237,6 +253,9 @@ QUrl QQmlFile::url() const
return d->url;
}
+/*!
+ \internal
+ */
QQmlFile::Status QQmlFile::status() const
{
if (d->url.isEmpty() && d->urlString.isEmpty())
@@ -251,6 +270,9 @@ QQmlFile::Status QQmlFile::status() const
return Ready;
}
+/*!
+ \internal
+ */
QString QQmlFile::error() const
{
switch (d->error) {
@@ -264,21 +286,34 @@ QString QQmlFile::error() const
}
}
+/*!
+ \internal
+ */
qint64 QQmlFile::size() const
{
return d->data.size();
}
+/*!
+ \internal
+ */
const char *QQmlFile::data() const
{
return d->data.constData();
}
+/*!
+ \internal
+ */
QByteArray QQmlFile::dataByteArray() const
{
return d->data;
}
+/*!
+ \internal
+ Loads content at \a url using \a engine.
+ */
void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
{
Q_ASSERT(engine);
@@ -309,6 +344,10 @@ void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
}
}
+/*!
+ \internal
+ Loads content at \a url using \a engine.
+ */
void QQmlFile::load(QQmlEngine *engine, const QString &url)
{
Q_ASSERT(engine);
@@ -343,6 +382,9 @@ void QQmlFile::load(QQmlEngine *engine, const QString &url)
}
}
+/*!
+ \internal
+ */
void QQmlFile::clear()
{
d->url = QUrl();
@@ -351,12 +393,22 @@ void QQmlFile::clear()
d->error = QQmlFilePrivate::None;
}
-void QQmlFile::clear(QObject *)
+/*!
+ \internal
+ Redirects to the other clear() overload, ignoring \a object.
+ */
+void QQmlFile::clear(QObject *object)
{
+ Q_UNUSED(object);
clear();
}
#if QT_CONFIG(qml_network)
+
+/*!
+ \internal
+ Connects \a method of \a object to the internal \c{finished} signal.
+ */
bool QQmlFile::connectFinished(QObject *object, const char *method)
{
if (!d || !d->reply) {
@@ -364,10 +416,13 @@ bool QQmlFile::connectFinished(QObject *object, const char *method)
return false;
}
- return QObject::connect(d->reply, SIGNAL(finished()),
- object, method);
+ return QObject::connect(d->reply, SIGNAL(finished()), object, method);
}
+/*!
+ \internal
+ Connects \a method of \a object to the internal \c{finished} signal.
+ */
bool QQmlFile::connectFinished(QObject *object, int method)
{
if (!d || !d->reply) {
@@ -379,6 +434,10 @@ bool QQmlFile::connectFinished(QObject *object, int method)
object, method);
}
+/*!
+ \internal
+ Connects \a method of \a object to the internal \c{downloadProgress} signal.
+ */
bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
{
if (!d || !d->reply) {
@@ -390,6 +449,10 @@ bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
object, method);
}
+/*!
+ \internal
+ Connects \a method of \a object to the internal \c{downloadProgress} signal.
+ */
bool QQmlFile::connectDownloadProgress(QObject *object, int method)
{
if (!d || !d->reply) {
@@ -403,11 +466,14 @@ bool QQmlFile::connectDownloadProgress(QObject *object, int method)
#endif
/*!
-Returns true if QQmlFile will open \a url synchronously.
+ \internal
-Synchronous urls have a qrc:/ or file:// scheme.
+ Returns \c true if QQmlFile will open \a url synchronously.
+ Otherwise returns \c false. Synchronous urls have a \c{qrc:} or \c{file:}
+ scheme.
-\note On Android, urls with assets:/ scheme are also considered synchronous.
+ \note On Android, urls with \c{assets:} or \c{content:} scheme are also
+ considered synchronous.
*/
bool QQmlFile::isSynchronous(const QUrl &url)
{
@@ -430,11 +496,14 @@ bool QQmlFile::isSynchronous(const QUrl &url)
}
/*!
-Returns true if QQmlFile will open \a url synchronously.
+ \internal
-Synchronous urls have a qrc:/ or file:// scheme.
+ Returns \c true if QQmlFile will open \a url synchronously.
+ Otherwise returns \c false. Synchronous urls have a \c{qrc:} or \c{file:}
+ scheme.
-\note On Android, urls with assets:/ scheme are also considered synchronous.
+ \note On Android, urls with \c{assets:} or \c{content:} scheme are also
+ considered synchronous.
*/
bool QQmlFile::isSynchronous(const QString &url)
{
@@ -484,11 +553,12 @@ static bool hasLocalContentAuthority(const QUrl &url)
#endif
/*!
-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.
+ Returns \c true if \a url is a local file that can be opened with \l{QFile}.
+ Otherwise returns \c false. Local file urls have either a \c{qrc:} or
+ \c{file:} scheme.
-\note On Android, urls with assets:/ scheme are also considered local files.
+ \note On Android, urls with \c{assets:} or \c{content:} scheme are also
+ considered local files.
*/
bool QQmlFile::isLocalFile(const QUrl &url)
{
@@ -564,11 +634,12 @@ static bool hasLocalContentAuthority(const QString &url, qsizetype schemeLength)
#endif
/*!
-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.
+ Returns \c true if \a url is a local file that can be opened with \l{QFile}.
+ Otherwise returns \c false. Local file urls have either a \c{qrc:} or
+ \c{file:} scheme.
-\note On Android, urls with assets: or content: scheme are also considered local files.
+ \note On Android, urls with \c{assets:} or \c{content:} scheme are also considered
+ local files.
*/
bool QQmlFile::isLocalFile(const QString &url)
{
@@ -609,8 +680,10 @@ bool QQmlFile::isLocalFile(const QString &url)
}
/*!
-If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
-empty string.
+ If \a url is a local file returns a path suitable for passing to \l{QFile}.
+ Otherwise returns an empty string.
+
+ \sa isLocalFile
*/
QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
{
@@ -661,8 +734,10 @@ static bool isDoubleSlashed(const QString &url, qsizetype offset)
}
/*!
-If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
-empty string.
+ If \a url is a local file returns a path suitable for passing to \l{QFile}.
+ Otherwise returns an empty string.
+
+ \sa isLocalFile
*/
QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
{
diff --git a/src/qml/qml/qqmlfile.h b/src/qml/qml/qqmlfile.h
index 3d51132bfd..af90e671a9 100644
--- a/src/qml/qml/qqmlfile.h
+++ b/src/qml/qml/qqmlfile.h
@@ -18,8 +18,8 @@ class Q_QML_EXPORT QQmlFile
{
public:
QQmlFile();
- QQmlFile(QQmlEngine *, const QUrl &);
- QQmlFile(QQmlEngine *, const QString &);
+ QQmlFile(QQmlEngine *engine, const QUrl &url);
+ QQmlFile(QQmlEngine *engine, const QString &url);
~QQmlFile();
enum Status { Null, Ready, Error, Loading };
@@ -42,7 +42,7 @@ public:
void load(QQmlEngine *, const QString &);
void clear();
- void clear(QObject *);
+ void clear(QObject *object);
#if QT_CONFIG(qml_network)
bool connectFinished(QObject *, const char *);
diff --git a/src/qml/qml/qqmlfileselector_p.h b/src/qml/qml/qqmlfileselector_p.h
index 31f18cc27e..58696acef7 100644
--- a/src/qml/qml/qqmlfileselector_p.h
+++ b/src/qml/qml/qqmlfileselector_p.h
@@ -21,11 +21,13 @@
#include <private/qobject_p.h>
#include <private/qtqmlglobal_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QFileSelector;
class QQmlFileSelectorInterceptor;
-class Q_QML_PRIVATE_EXPORT QQmlFileSelectorPrivate : public QObjectPrivate
+class Q_QML_EXPORT QQmlFileSelectorPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQmlFileSelector)
public:
@@ -38,7 +40,7 @@ public:
QScopedPointer<QQmlFileSelectorInterceptor> myInstance;
};
-class Q_QML_PRIVATE_EXPORT QQmlFileSelectorInterceptor : public QQmlAbstractUrlInterceptor
+class Q_QML_EXPORT QQmlFileSelectorInterceptor : public QQmlAbstractUrlInterceptor
{
public:
QQmlFileSelectorInterceptor(QQmlFileSelectorPrivate* pd);
diff --git a/src/qml/qml/qqmlfinalizer_p.h b/src/qml/qml/qqmlfinalizer_p.h
index 57f5fdb61b..66fd85ddad 100644
--- a/src/qml/qml/qqmlfinalizer_p.h
+++ b/src/qml/qml/qqmlfinalizer_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlFinalizerHook
+class Q_QML_EXPORT QQmlFinalizerHook
{
public:
virtual ~QQmlFinalizerHook();
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 265b26b1e0..1273067187 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -1,15 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <private/qqmlglobal_p.h>
-#include <QtQml/private/qqmlmetatype_p.h>
#include <QtQml/private/qjsvalue_p.h>
-
+#include <QtQml/private/qqmlglobal_p.h>
+#include <QtQml/private/qqmlmetatype_p.h>
#include <QtQml/qqmlengine.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qstringlist.h>
+
+#include <QtCore/private/qvariant_p.h>
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
-#include <QtCore/QCoreApplication>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
@@ -70,43 +70,78 @@ static bool isConstructibleMetaType(const QMetaType metaType)
return true;
}
+static void *createVariantData(QMetaType type, QVariant *variant)
+{
+ const QtPrivate::QMetaTypeInterface *iface = type.iface();
+ QVariant::Private *d = &variant->data_ptr();
+ Q_ASSERT(d->is_null && !d->is_shared);
+ *d = QVariant::Private(iface);
+ if (QVariant::Private::canUseInternalSpace(iface))
+ return d->data.data;
+
+ // This is not exception safe.
+ // If your value type throws an exception from its ctor bad things will happen anyway.
+ d->data.shared = QVariant::PrivateShared::create(iface->size, iface->alignment);
+ d->is_shared = true;
+ return d->data.shared->data();
+}
+
static void callConstructor(
- const QMetaObject *mo, int i, void *parameter, QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, int i, void *source, void *target)
{
- // Unfortunately CreateInstance unconditionally creates the instance on the heap.
- void *gadget = nullptr;
- void *p[] = { &gadget, parameter };
- mo->static_metacall(QMetaObject::CreateInstance, i, p);
- Q_ASSERT(gadget);
- metaType.destruct(data);
- metaType.construct(data, gadget);
- metaType.destroy(gadget);
+ void *p[] = { target, source };
+ targetMetaObject->static_metacall(QMetaObject::ConstructInPlace, i, p);
}
+template<typename Allocate>
+static void fromVerifiedType(
+ const QMetaObject *targetMetaObject, int ctorIndex, void *source, Allocate &&allocate)
+{
+ const QMetaMethod ctor = targetMetaObject->constructor(ctorIndex);
+ Q_ASSERT_X(ctor.parameterCount() == 1, "fromVerifiedType",
+ "Value type constructor must take exactly one argument");
+ callConstructor(targetMetaObject, ctorIndex, source, allocate());
+}
+
+
+template<typename Allocate, typename Retrieve>
static bool fromMatchingType(
- const QMetaObject *mo, const QV4::Value &s, const QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, Allocate &&allocate, Retrieve &&retrieve)
{
- for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
- const QMetaMethod ctor = mo->constructor(i);
+ for (int i = 0, end = targetMetaObject->constructorCount(); i < end; ++i) {
+ const QMetaMethod ctor = targetMetaObject->constructor(i);
if (ctor.parameterCount() != 1)
continue;
const QMetaType parameterType = ctor.parameterMetaType(0);
- QVariant parameter = QV4::ExecutionEngine::toVariant(s, parameterType);
- if (parameter.metaType() == parameterType) {
- callConstructor(mo, i, parameter.data(), metaType, data);
- return true;
- }
- QVariant converted(parameterType);
- if (QQmlValueTypeProvider::createValueType(s, parameterType, converted.data())) {
- callConstructor(mo, i, converted.data(), metaType, data);
- return true;
- }
+ if (retrieve(parameterType, [&](QMetaType sourceMetaType, void *sourceData) {
+ if (sourceMetaType == parameterType) {
+ callConstructor(targetMetaObject, i, sourceData, allocate());
+ return true;
+ }
+
+ if (const QMetaObject *parameterMetaObject = parameterType.metaObject()) {
+ if (const QMetaObject *sourceMetaObject = sourceMetaType.metaObject();
+ sourceMetaObject && sourceMetaObject->inherits(parameterMetaObject)) {
+ // Allow construction from derived types.
+ callConstructor(targetMetaObject, i, sourceData, allocate());
+ return true;
+ }
+ }
+
+ // Do not recursively try to create parameters here. This may end up in infinite recursion.
+
+ // At this point, s should be a builtin type. For builtin types
+ // the QMetaType converters are good enough.
+ QVariant converted(parameterType);
+ if (QMetaType::convert(sourceMetaType, sourceData, parameterType, converted.data())) {
+ callConstructor(targetMetaObject, i, converted.data(), allocate());
+ return true;
+ }
- if (QMetaType::convert(parameter.metaType(), parameter.constData(),
- parameterType, converted.data())) {
- callConstructor(mo, i, converted.data(), metaType, data);
+ return false;
+ })) {
return true;
}
}
@@ -114,37 +149,38 @@ static bool fromMatchingType(
return false;
}
+template<typename Allocate>
static bool fromMatchingType(
- const QMetaObject *mo, QVariant s, const QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, const QV4::Value &source, Allocate &&allocate)
{
- const QMetaType sourceMetaType = s.metaType();
- if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
- QJSValue val = s.value<QJSValue>();
- return fromMatchingType(
- mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), metaType, data);
- }
+ return fromMatchingType(
+ targetMetaObject, std::forward<Allocate>(allocate),
+ [&](QMetaType parameterType, auto callback) {
+ QVariant variant = QV4::ExecutionEngine::toVariant(source, parameterType);
+ return callback(variant.metaType(), variant.data());
+ });
+}
+template<typename Allocate>
+static bool fromMatchingType(
+ const QMetaObject *targetMetaObject, QVariant source, Allocate &&allocate)
+{
+ return fromMatchingType(targetMetaObject, std::forward<Allocate>(allocate),
+ [&](QMetaType, auto callback) {
+ return callback(source.metaType(), source.data());
+ });
+}
+
+template<typename Allocate>
+static bool fromString(const QMetaObject *mo, QString s, Allocate &&allocate)
+{
for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
const QMetaMethod ctor = mo->constructor(i);
if (ctor.parameterCount() != 1)
continue;
- const QMetaType parameterType = ctor.parameterMetaType(0);
- if (sourceMetaType == parameterType) {
- callConstructor(mo, i, s.data(), metaType, data);
- return true;
- }
-
- QVariant parameter(parameterType);
- if (QQmlValueTypeProvider::createValueType(s, parameterType, parameter.data())) {
- callConstructor(mo, i, parameter.data(), metaType, data);
- return true;
- }
-
- // At this point, s should be a builtin type. For builtin types
- // the QMetaType converters are good enough.
- if (QMetaType::convert(sourceMetaType, s.constData(), parameterType, parameter.data())) {
- callConstructor(mo, i, parameter.data(), metaType, data);
+ if (ctor.parameterMetaType(0) == QMetaType::fromType<QString>()) {
+ callConstructor(mo, i, &s, allocate());
return true;
}
}
@@ -152,39 +188,42 @@ static bool fromMatchingType(
return false;
}
-static bool fromString(
- const QMetaObject *mo, QString s, const QMetaType metaType, void *data)
+template<typename Get, typename Convert>
+static bool doWriteProperty(const QMetaProperty &metaProperty, void *target,
+ Get &&get, Convert &&convert)
{
- for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
- const QMetaMethod ctor = mo->constructor(i);
- if (ctor.parameterCount() != 1)
- continue;
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = get(propertyType);
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ return true;
+ }
- if (ctor.parameterMetaType(0) == QMetaType::fromType<QString>()) {
- callConstructor(mo, i, &s, metaType, data);
- return true;
- }
+ QVariant converted = convert(propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ return true;
}
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ return true;
+ }
return false;
}
-static bool byProperties(
- const QMetaObject *mo, const QV4::Value &s, void *data)
+static void doWriteProperties(
+ const QMetaObject *targetMetaObject, void *target, const QV4::Value &source)
{
- if (!s.isObject())
- return false;
-
- if (!mo)
- return false;
-
- const QV4::Object *o = static_cast<const QV4::Object *>(&s);
+ const QV4::Object *o = static_cast<const QV4::Object *>(&source);
QV4::Scope scope(o->engine());
QV4::ScopedObject object(scope, o);
- for (int i = 0; i < mo->propertyCount(); ++i) {
- const QMetaProperty metaProperty = mo->property(i);
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
const QString propertyName = QString::fromUtf8(metaProperty.name());
QV4::ScopedString v4PropName(scope, scope.engine->newString(propertyName));
@@ -195,22 +234,31 @@ static bool byProperties(
if (v4PropValue->isUndefined())
continue;
+ if (doWriteProperty(metaProperty, target, [&](const QMetaType &propertyType) {
+ return QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
+ }, [&](const QMetaType &propertyType) {
+ return QQmlValueTypeProvider::createValueType(v4PropValue, propertyType);
+ })) {
+ continue;
+ }
+
const QMetaType propertyType = metaProperty.metaType();
QVariant property = QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
if (property.metaType() == propertyType) {
- metaProperty.writeOnGadget(data, property);
+ metaProperty.writeOnGadget(target, std::move(property));
continue;
}
- QVariant converted(propertyType);
- if (QQmlValueTypeProvider::createValueType(v4PropValue, propertyType, converted.data())) {
- metaProperty.writeOnGadget(data, converted);
+ QVariant converted = QQmlValueTypeProvider::createValueType(v4PropValue, propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
continue;
}
+ converted = QVariant(propertyType);
if (QMetaType::convert(property.metaType(), property.constData(),
propertyType, converted.data())) {
- metaProperty.writeOnGadget(data, converted);
+ metaProperty.writeOnGadget(target, std::move(converted));
continue;
}
@@ -219,102 +267,441 @@ static bool byProperties(
.arg(v4PropValue->toQStringNoThrow(), QString::fromUtf8(propertyType.name()),
propertyName);
}
- return true;
}
-static bool byProperties(
- const QMetaObject *mo, const QVariant &s, void *data)
+static QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType metaType, const QV4::Value &source)
{
- if (!mo)
- return false;
+ if (!source.isObject() || !targetMetaObject)
+ return QVariant();
+
+ QVariant result(metaType);
+ doWriteProperties(targetMetaObject, result.data(), source);
+ return result;
+}
+
+template<typename Read>
+static void doWriteProperties(
+ const QMetaObject *targetMetaObject, void *target,
+ const QMetaObject *sourceMetaObject, Read &&read)
+{
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
+
+ const int sourceProperty = sourceMetaObject->indexOfProperty(metaProperty.name());
+
+ // We assume that data is freshly constructed.
+ // There is no point in reset()'ing properties of a freshly created object.
+ if (sourceProperty == -1)
+ continue;
+
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = read(sourceMetaObject, sourceProperty);
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ continue;
+ }
+
+ QVariant converted = QQmlValueTypeProvider::createValueType(property, propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
+
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
- if (s.metaType() == QMetaType::fromType<QJSValue>()) {
- QJSValue val = s.value<QJSValue>();
- return byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), data);
+ qWarning().noquote()
+ << QLatin1String("Could not convert %1 to %2 for property %3")
+ .arg(property.toString(), QString::fromUtf8(propertyType.name()),
+ QString::fromUtf8(metaProperty.name()));
}
+}
- return false;
+
+static void doWriteProperties(const QMetaObject *targetMeta, void *target, QObject *source)
+{
+ doWriteProperties(
+ targetMeta, target, source->metaObject(),
+ [source](const QMetaObject *sourceMetaObject, int sourceProperty) {
+ return sourceMetaObject->property(sourceProperty).read(source);
+ });
}
-static bool fromJSValue(
- const QQmlType &type, const QJSValue &s, QMetaType metaType, void *data)
+static QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType targetMetaType, QObject *source)
{
- if (const auto valueTypeFunction = type.createValueTypeFunction()) {
- QVariant result = valueTypeFunction(s);
- if (result.metaType() == metaType) {
- metaType.destruct(data);
- metaType.construct(data, result.constData());
- return true;
+ if (!source || !targetMetaObject)
+ return QVariant();
+
+ QVariant result(targetMetaType);
+ doWriteProperties(targetMetaObject, result.data(), source);
+ return result;
+}
+
+static QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType targetMetaType,
+ const QMetaObject *sourceMetaObject, const void *source)
+{
+ if (!source || !sourceMetaObject || !targetMetaObject)
+ return QVariant();
+
+ QVariant result(targetMetaType);
+ doWriteProperties(
+ targetMetaObject, result.data(), sourceMetaObject,
+ [source](const QMetaObject *sourceMetaObject, int sourceProperty) {
+ return sourceMetaObject->property(sourceProperty).readOnGadget(source);
+ });
+ return result;
+}
+
+template<typename Map>
+void doWriteProperties(const QMetaObject *targetMetaObject, void *target, const Map &source)
+{
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
+
+ // We assume that data is freshly constructed.
+ // There is no point in reset()'ing properties of a freshly created object.
+ const auto it = source.constFind(QString::fromUtf8(metaProperty.name()));
+ if (it == source.constEnd())
+ continue;
+
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = *it;
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ continue;
+ }
+
+ QVariant converted = QQmlValueTypeProvider::createValueType(property, propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
}
+
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
+
+ qWarning().noquote()
+ << QLatin1String("Could not convert %1 to %2 for property %3")
+ .arg(property.toString(), QString::fromUtf8(propertyType.name()),
+ QString::fromUtf8(metaProperty.name()));
}
+}
- return false;
+template<typename Map>
+QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType targetMetaType, const Map &source)
+{
+ QVariant result(targetMetaType);
+ doWriteProperties(targetMetaObject, result.data(), source);
+ return result;
}
-bool QQmlValueTypeProvider::constructFromJSValue(
- const QJSValue &s, QMetaType metaType, void *data)
+static QVariant byProperties(
+ const QMetaObject *targetMetaObject, QMetaType targetMetaType, const QVariant &source)
{
- return isConstructibleMetaType(metaType)
- && fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType, data);
+ if (!targetMetaObject)
+ return QVariant();
+
+ if (source.metaType() == QMetaType::fromType<QJSValue>()) {
+ QJSValue val = source.value<QJSValue>();
+ return byProperties(
+ targetMetaObject, targetMetaType, QV4::Value(QJSValuePrivate::asReturnedValue(&val)));
+ }
+
+ if (source.metaType() == QMetaType::fromType<QVariantMap>()) {
+ return byProperties(
+ targetMetaObject, targetMetaType,
+ *static_cast<const QVariantMap *>(source.constData()));
+ }
+
+ if (source.metaType() == QMetaType::fromType<QVariantHash>()) {
+ return byProperties(
+ targetMetaObject, targetMetaType,
+ *static_cast<const QVariantHash *>(source.constData()));
+ }
+
+ if (source.metaType().flags() & QMetaType::PointerToQObject)
+ return byProperties(targetMetaObject, targetMetaType, source.value<QObject *>());
+
+ if (const QMetaObject *sourceMeta = QQmlMetaType::metaObjectForValueType(source.metaType()))
+ return byProperties(targetMetaObject, targetMetaType, sourceMeta, source.constData());
+
+ return QVariant();
}
-bool QQmlValueTypeProvider::createValueType(
- const QString &s, QMetaType metaType, void *data)
+template<typename Allocate, typename DefaultConstruct>
+bool createOrConstructValueType(
+ const QQmlType &targetType, const QV4::Value &source,
+ Allocate &&allocate, DefaultConstruct &&defaultConstruct)
{
- if (!isConstructibleMetaType(metaType))
- return false;
- const QQmlType type = QQmlMetaType::qmlType(metaType);
- const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type);
- if (mo && type.canConstructValueType()) {
- if (fromString(mo, s, metaType, data))
+ const auto warn = [&](const QMetaObject *targetMetaObject) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << targetMetaObject->className() << "to call with value"
+ << source.toQStringNoThrow();
+ };
+
+ if (targetType.canPopulateValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (source.isObject()) {
+ doWriteProperties(targetMetaObject, defaultConstruct(), source);
+ return true;
+ }
+ if (targetType.canConstructValueType()) {
+ if (fromMatchingType(targetMetaObject, source, allocate))
+ return true;
+ warn(targetMetaObject);
+ }
+ }
+ } else if (targetType.canConstructValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (fromMatchingType(targetMetaObject, source, allocate))
+ return true;
+ warn(targetMetaObject);
+ }
+ }
+
+ if (const auto valueTypeFunction = targetType.createValueTypeFunction()) {
+ const QVariant result
+ = valueTypeFunction(QJSValuePrivate::fromReturnedValue(source.asReturnedValue()));
+ const QMetaType resultType = result.metaType();
+ if (resultType == targetType.typeId()) {
+ resultType.construct(allocate(), result.constData());
return true;
+ }
}
- return fromJSValue(type, s, metaType, data);
+ return false;
}
-bool QQmlValueTypeProvider::createValueType(
- const QJSValue &s, QMetaType metaType, void *data)
+template<typename Allocate, typename DefaultConstruct>
+bool createOrConstructValueType(
+ const QQmlType &targetType, QMetaType sourceMetaType, void *source,
+ Allocate &&allocate, DefaultConstruct &&defaultConstruct)
{
- if (!isConstructibleMetaType(metaType))
- return false;
- const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType()
- && byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)), data)) {
+
+ const auto warn = [&](const QMetaObject *targetMetaObject) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << targetMetaObject->className() << "to call with value" << source;
+ };
+
+ if (targetType.canPopulateValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (const QMetaObject *sourceMetaObject
+ = QQmlMetaType::metaObjectForValueType(sourceMetaType)) {
+ doWriteProperties(
+ targetMetaObject, defaultConstruct(), sourceMetaObject,
+ [&source](const QMetaObject *sourceMetaObject, int sourceProperty) {
+ return sourceMetaObject->property(sourceProperty).readOnGadget(source);
+ });
return true;
+ }
+
+ if (sourceMetaType == QMetaType::fromType<QVariantMap>()) {
+ doWriteProperties(
+ targetMetaObject, defaultConstruct(),
+ *static_cast<const QVariantMap *>(source));
+ return true;
+ }
+
+ if (sourceMetaType == QMetaType::fromType<QVariantHash>()) {
+ doWriteProperties(
+ targetMetaObject, defaultConstruct(),
+ *static_cast<const QVariantHash *>(source));
+ return true;
+ }
+
+ if (sourceMetaType.flags() & QMetaType::PointerToQObject) {
+ doWriteProperties(
+ targetMetaObject, defaultConstruct(),
+ *static_cast<QObject *const *>(source));
+ return true;
+ }
}
+ }
- if (type.canConstructValueType()
- && fromMatchingType(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)),
- metaType, data)) {
+ if (targetType.canConstructValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (fromMatchingType(targetMetaObject, std::forward<Allocate>(allocate),
+ [&](QMetaType, auto callback) {
+ return callback(sourceMetaType, source);
+ })) {
return true;
+ }
+ warn(targetMetaObject);
}
}
- return fromJSValue(type, s, metaType, data);
+ return false;
}
-bool QQmlValueTypeProvider::createValueType(
- const QV4::Value &s, QMetaType metaType, void *data)
+/*!
+ * \internal
+ * Populate the value type in place at \a target, which is expected to be
+ * allocated and default-constructed, for example the result of a QVariant(QMetaType).
+ * This is efficient if we can do byProperties() since it can use the pre-constructed object.
+ * It also avoids the creation of a QVariant in most cases. It is not
+ * efficient if you're going to create a QVariant anyway.
+ */
+bool QQmlValueTypeProvider::populateValueType(
+ QMetaType targetMetaType, void *target, QMetaType sourceMetaType, void *source)
{
- if (!isConstructibleMetaType(metaType))
+ if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
+ const QJSValue *val = static_cast<const QJSValue *>(source);
+ return populateValueType(
+ targetMetaType, target, QV4::Value(QJSValuePrivate::asReturnedValue(val)));
+ }
+
+ if (!isConstructibleMetaType(targetMetaType))
return false;
+
+ return createOrConstructValueType(
+ QQmlMetaType::qmlType(targetMetaType), sourceMetaType, source,
+ [targetMetaType, target]() {
+ targetMetaType.destruct(target);
+ return target;
+ }, [target]() {
+ return target;
+ });
+}
+
+/*!
+ * \internal
+ * Populate the value type in place at \a target, which is expected to be
+ * allocated and default-constructed, for example the result of a QVariant(QMetaType).
+ * This is efficient if we can do byProperties() since it can use the pre-constructed object.
+ * It also avoids the creation of a QVariant in most cases. It is not
+ * efficient if you're going to create a QVariant anyway.
+ */
+bool QQmlValueTypeProvider::populateValueType(
+ QMetaType targetMetaType, void *target, const QV4::Value &source)
+{
+ if (!isConstructibleMetaType(targetMetaType))
+ return false;
+
+ return createOrConstructValueType(
+ QQmlMetaType::qmlType(targetMetaType), source, [targetMetaType, target]() {
+ targetMetaType.destruct(target);
+ return target;
+ }, [target]() {
+ return target;
+ });
+}
+
+/*!
+ * \internal
+ * Specialization that constructs the value type on the heap using new and returns a pointer to it.
+ */
+void *QQmlValueTypeProvider::heapCreateValueType(
+ const QQmlType &targetType, const QV4::Value &source)
+{
+ void *target = nullptr;
+ if (createOrConstructValueType(
+ targetType, source, [&]() {
+ const QMetaType metaType = targetType.typeId();
+ const ushort align = metaType.alignOf();
+ target = align > __STDCPP_DEFAULT_NEW_ALIGNMENT__
+ ? operator new(metaType.sizeOf(), std::align_val_t(align))
+ : operator new(metaType.sizeOf());
+ return target;
+ }, [&]() {
+ target = targetType.typeId().create();
+ return target;
+ })) {
+ Q_ASSERT(target != nullptr);
+ }
+
+ return target;
+}
+
+QVariant QQmlValueTypeProvider::constructValueType(
+ QMetaType targetMetaType, const QMetaObject *targetMetaObject,
+ int ctorIndex, void *ctorArg)
+{
+ QVariant result;
+ fromVerifiedType(targetMetaObject, ctorIndex, ctorArg,
+ [&]() { return createVariantData(targetMetaType, &result); });
+ return result;
+}
+
+static QVariant fromJSValue(const QQmlType &type, const QJSValue &s, QMetaType metaType)
+{
+ if (const auto valueTypeFunction = type.createValueTypeFunction()) {
+ const QVariant result = valueTypeFunction(s);
+ if (result.metaType() == metaType)
+ return result;
+ }
+
+ return QVariant();
+}
+
+QVariant QQmlValueTypeProvider::createValueType(const QJSValue &s, QMetaType metaType)
+{
+ if (!isConstructibleMetaType(metaType))
+ return QVariant();
+ return fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType);
+}
+
+QVariant QQmlValueTypeProvider::createValueType(const QString &s, QMetaType metaType)
+{
+ if (!isConstructibleMetaType(metaType))
+ return QVariant();
const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType() && byProperties(mo, s, data))
- return true;
- if (type.canConstructValueType()) {
- if (fromMatchingType(mo, s, metaType, data))
- return true;
- qWarning().noquote()
- << "Could not find any constructor for value type"
- << mo->className() << "to call with value" << s.toQStringNoThrow();
+ if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
+ QVariant result;
+ if (fromString(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
}
}
- return fromJSValue(
- type, QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType, data);
+ return fromJSValue(type, s, metaType);
+}
+
+QVariant QQmlValueTypeProvider::createValueType(const QV4::Value &s, QMetaType metaType)
+{
+ if (!isConstructibleMetaType(metaType))
+ return QVariant();
+ const QQmlType type = QQmlMetaType::qmlType(metaType);
+ const auto warn = [&](const QMetaObject *mo) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << mo->className() << "to call with value" << s.toQStringNoThrow();
+ };
+
+ if (type.canPopulateValueType()) {
+ if (const QMetaObject *mo = type.metaObject()) {
+ QVariant result = byProperties(mo, metaType, s);
+ if (result.isValid())
+ return result;
+ if (type.canConstructValueType()) {
+ if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ warn(mo);
+ }
+ }
+ } else if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObject()) {
+ QVariant result;
+ if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ warn(mo);
+ }
+ }
+
+ return fromJSValue(type, QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType);
}
@@ -322,25 +709,38 @@ bool QQmlValueTypeProvider::createValueType(
* \internal
* This should only be called with either builtin types or wrapped QJSValues as source.
*/
-bool QQmlValueTypeProvider::createValueType(
- const QVariant &s, QMetaType metaType, void *data)
+QVariant QQmlValueTypeProvider::createValueType(const QVariant &s, QMetaType metaType)
{
if (!isConstructibleMetaType(metaType))
- return false;
+ return QVariant();
const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType() && byProperties(mo, s, data))
- return true;
- if (type.canConstructValueType()) {
- if (fromMatchingType(mo, s, metaType, data))
- return true;
- qWarning().noquote()
- << "Could not find any constructor for value type"
- << mo->className() << "to call with value" << s;
+ const auto warn = [&](const QMetaObject *mo) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << mo->className() << "to call with value" << s;
+ };
+
+ if (type.canPopulateValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
+ QVariant result = byProperties(mo, metaType, s);
+ if (result.isValid())
+ return result;
+ if (type.canConstructValueType()) {
+ if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ warn(mo);
+ }
+ }
+ } else if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
+ QVariant result;
+ if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ warn(mo);
}
}
- return false;
+ return QVariant();
}
QQmlColorProvider::~QQmlColorProvider() {}
@@ -359,7 +759,7 @@ QVariant QQmlColorProvider::tint(const QVariant &, const QVariant &) { return QV
static QQmlColorProvider *colorProvider = nullptr;
-Q_QML_PRIVATE_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *newProvider)
+Q_QML_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *newProvider)
{
QQmlColorProvider *old = colorProvider;
colorProvider = newProvider;
@@ -413,7 +813,7 @@ QString QQmlGuiProvider::pluginName() const { return QString(); }
static QQmlGuiProvider *guiProvider = nullptr;
-Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newProvider)
+Q_QML_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newProvider)
{
QQmlGuiProvider *old = guiProvider;
guiProvider = newProvider;
@@ -438,18 +838,8 @@ Q_AUTOTEST_EXPORT QQmlGuiProvider *QQml_guiProvider(void)
//Docs in qqmlengine.cpp
QQmlApplication::QQmlApplication(QObject *parent)
- : QObject(*(new QQmlApplicationPrivate),parent)
+ : QQmlApplication(*(new QQmlApplicationPrivate), parent)
{
- connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
- this, SIGNAL(aboutToQuit()));
- connect(QCoreApplication::instance(), SIGNAL(applicationNameChanged()),
- this, SIGNAL(nameChanged()));
- connect(QCoreApplication::instance(), SIGNAL(applicationVersionChanged()),
- this, SIGNAL(versionChanged()));
- connect(QCoreApplication::instance(), SIGNAL(organizationNameChanged()),
- this, SIGNAL(organizationChanged()));
- connect(QCoreApplication::instance(), SIGNAL(organizationDomainChanged()),
- this, SIGNAL(domainChanged()));
}
QQmlApplication::QQmlApplication(QQmlApplicationPrivate &dd, QObject *parent)
@@ -517,6 +907,62 @@ void QQmlApplication::setDomain(const QString &arg)
QCoreApplication::instance()->setOrganizationDomain(arg);
}
+static const QQmlData *ddata_for_cast(QObject *object)
+{
+ Q_ASSERT(object);
+ auto ddata = QQmlData::get(object, false);
+ return (ddata && ddata->propertyCache) ? ddata : nullptr;
+}
+
+bool qmlobject_can_cpp_cast(QObject *object, const QMetaObject *mo)
+{
+ Q_ASSERT(mo);
+ if (const QQmlData *ddata = ddata_for_cast(object))
+ return ddata->propertyCache->firstCppMetaObject()->inherits(mo);
+ return object->metaObject()->inherits(mo);
+}
+
+bool qmlobject_can_qml_cast(QObject *object, const QQmlType &type)
+{
+ Q_ASSERT(type.isValid());
+
+ // A non-composite type will always have a metaobject.
+ const QMetaObject *typeMetaObject = type.metaObject();
+ const QQmlPropertyCache::ConstPtr typePropertyCache = typeMetaObject
+ ? QQmlPropertyCache::ConstPtr()
+ : QQmlMetaType::findPropertyCacheInCompositeTypes(type.typeId());
+
+ if (const QQmlData *ddata = ddata_for_cast(object)) {
+ for (const QQmlPropertyCache *propertyCache = ddata->propertyCache.data(); propertyCache;
+ propertyCache = propertyCache->parent().data()) {
+
+ if (typeMetaObject) {
+ // Prefer the metaobject inheritance mechanism, since it is more accurate.
+ //
+ // Assume the object can be casted to the type. Then, if we have a type metaobject,
+ // the object's property cache inheritance has to contain it. Otherwise we would
+ // end up with diverging metaobject hierarchies if we created the object's
+ // metaobject. This would be a disaster.
+ if (const QMetaObject *objectMetaObject = propertyCache->metaObject())
+ return objectMetaObject->inherits(typeMetaObject);
+ } else {
+ // This is a best effort attempt. There are a number of ways for the
+ // property caches to be unrelated but the types still convertible.
+ // Multiple property caches can hold the same metaobject, for example for
+ // versions of non-composite types.
+ if (propertyCache == typePropertyCache.data())
+ return true;
+ }
+ }
+ }
+
+ // If nothing else works, we have to create the metaobjects.
+
+ return object->metaObject()->inherits(typeMetaObject
+ ? typeMetaObject
+ : (typePropertyCache ? typePropertyCache->createMetaObject() : nullptr));
+}
+
QT_END_NAMESPACE
#include "moc_qqmlglobal_p.cpp"
diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h
index 918f95993f..98fe2e6a79 100644
--- a/src/qml/qml/qqmlglobal_p.h
+++ b/src/qml/qml/qqmlglobal_p.h
@@ -16,8 +16,9 @@
//
#include <private/qmetaobject_p.h>
-#include <private/qtqmlglobal_p.h>
#include <private/qqmlmetaobject_p.h>
+#include <private/qqmltype_p.h>
+#include <private/qtqmlglobal_p.h>
#include <QtQml/qqml.h>
#include <QtCore/qobject.h>
@@ -48,7 +49,7 @@ T qmlGetConfigOption(const char *var)
Connect \a Signal of \a Sender to \a Method of \a Receiver. \a Signal must be
of type \a SenderType and \a Receiver of type \a ReceiverType.
- Unlike QObject::connect(), this method caches the lookup of the signal and method
+ Unlike QObject::connect(), this macro caches the lookup of the signal and method
indexes. It also does not require lazy QMetaObjects to be built so should be
preferred in all QML code that might interact with QML built objects.
@@ -87,7 +88,7 @@ do { \
Disconnect \a Signal of \a Sender from \a Method of \a Receiver. \a Signal must be
of type \a SenderType and \a Receiver of type \a ReceiverType.
- Unlike QObject::disconnect(), this method caches the lookup of the signal and method
+ Unlike QObject::disconnect(), this macro caches the lookup of the signal and method
indexes. It also does not require lazy QMetaObjects to be built so should be
preferred in all QML code that might interact with QML built objects.
@@ -122,6 +123,9 @@ do { \
QMetaObject::disconnect(sender, signalIdx, receiver, methodIdx); \
} while (0)
+Q_QML_EXPORT bool qmlobject_can_cpp_cast(QObject *object, const QMetaObject *mo);
+Q_QML_EXPORT bool qmlobject_can_qml_cast(QObject *object, const QQmlType &type);
+
/*!
This method is identical to qobject_cast<T>() except that it does not require lazy
QMetaObjects to be built, so should be preferred in all QML code that might interact
@@ -137,12 +141,25 @@ do { \
template<class T>
T qmlobject_cast(QObject *object)
{
- if (object && QQmlMetaObject::canConvert(object, &reinterpret_cast<T>(object)->staticMetaObject))
+ if (!object)
+ return nullptr;
+ if (qmlobject_can_cpp_cast(object, &(std::remove_pointer_t<T>::staticMetaObject)))
return static_cast<T>(object);
else
return nullptr;
}
+class QQuickItem;
+template<>
+inline QQuickItem *qmlobject_cast<QQuickItem *>(QObject *object)
+{
+ if (!object || !object->isQuickItemType())
+ return nullptr;
+ // QQuickItem is incomplete here -> can't use static_cast
+ // but we don't need any pointer adjustment, so reinterpret is safe
+ return reinterpret_cast<QQuickItem *>(object);
+}
+
#define IS_SIGNAL_CONNECTED(Sender, SenderType, Name, Arguments) \
do { \
QObject *sender = (Sender); \
@@ -189,15 +206,24 @@ inline void QQml_setParent_noEvent(QObject *object, QObject *parent)
class QQmlValueTypeProvider
{
public:
- static bool constructFromJSValue(const QJSValue &, QMetaType, void *);
-
- static bool createValueType(const QString &, QMetaType, void *);
- static bool createValueType(const QJSValue &, QMetaType, void *);
- static bool createValueType(const QV4::Value &, QMetaType, void *);
- static bool createValueType(const QVariant &, QMetaType, void *);
+ static bool populateValueType(
+ QMetaType targetMetaType, void *target, const QV4::Value &source);
+ static bool populateValueType(
+ QMetaType targetMetaType, void *target, QMetaType sourceMetaType, void *source);
+
+ static Q_QML_EXPORT void *heapCreateValueType(
+ const QQmlType &targetType, const QV4::Value &source);
+ static QVariant constructValueType(
+ QMetaType targetMetaType, const QMetaObject *targetMetaObject,
+ int ctorIndex, void *ctorArg);
+
+ static QVariant createValueType(const QJSValue &, QMetaType);
+ static QVariant createValueType(const QString &, QMetaType);
+ static QVariant createValueType(const QV4::Value &, QMetaType);
+ static QVariant createValueType(const QVariant &, QMetaType);
};
-class Q_QML_PRIVATE_EXPORT QQmlColorProvider
+class Q_QML_EXPORT QQmlColorProvider
{
public:
virtual ~QQmlColorProvider();
@@ -213,11 +239,11 @@ public:
virtual QVariant tint(const QVariant &, const QVariant &);
};
-Q_QML_PRIVATE_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *);
-Q_QML_PRIVATE_EXPORT QQmlColorProvider *QQml_colorProvider();
+Q_QML_EXPORT QQmlColorProvider *QQml_setColorProvider(QQmlColorProvider *);
+Q_QML_EXPORT QQmlColorProvider *QQml_colorProvider();
class QQmlApplication;
-class Q_QML_PRIVATE_EXPORT QQmlGuiProvider
+class Q_QML_EXPORT QQmlGuiProvider
{
public:
virtual ~QQmlGuiProvider();
@@ -229,12 +255,12 @@ public:
virtual QString pluginName() const;
};
-Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *);
+Q_QML_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *);
Q_AUTOTEST_EXPORT QQmlGuiProvider *QQml_guiProvider();
class QQmlApplicationPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlApplication : public QObject
+class Q_QML_EXPORT QQmlApplication : public QObject
{
//Application level logic, subclassed by Qt Quick if available via QQmlGuiProvider
Q_OBJECT
diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h
index 3486b4f74f..685293868e 100644
--- a/src/qml/qml/qqmlguard_p.h
+++ b/src/qml/qml/qqmlguard_p.h
@@ -28,8 +28,10 @@ public:
inline QQmlGuardImpl();
inline QQmlGuardImpl(QObject *);
inline QQmlGuardImpl(const QQmlGuardImpl &);
+protected:
inline ~QQmlGuardImpl();
+public: // ### make so it can be private
QObject *o = nullptr;
QQmlGuardImpl *next = nullptr;
QQmlGuardImpl **prev = nullptr;
@@ -48,10 +50,10 @@ class QQmlGuard : protected QQmlGuardImpl
{
friend class QQmlData;
public:
- inline QQmlGuard();
- inline QQmlGuard(ObjectDestroyedFn objectDestroyed, T *);
- inline QQmlGuard(T *);
- inline QQmlGuard(const QQmlGuard<T> &);
+ Q_NODISCARD_CTOR inline QQmlGuard();
+ Q_NODISCARD_CTOR inline QQmlGuard(ObjectDestroyedFn objectDestroyed, T *);
+ Q_NODISCARD_CTOR inline QQmlGuard(T *);
+ Q_NODISCARD_CTOR inline QQmlGuard(const QQmlGuard<T> &);
inline QQmlGuard<T> &operator=(const QQmlGuard<T> &o);
inline QQmlGuard<T> &operator=(T *);
@@ -72,10 +74,10 @@ public:
* We save it in objectDestroyFn to save space
* (implemented in qqmlengine.cpp)
*/
-void Q_QML_PRIVATE_EXPORT hasJsOwnershipIndicator(QQmlGuardImpl *);
+void Q_QML_EXPORT hasJsOwnershipIndicator(QQmlGuardImpl *);
template <typename T>
-class QQmlStrongJSQObjectReference : protected QQmlGuardImpl
+class QQmlStrongJSQObjectReference final : protected QQmlGuardImpl
{
public:
T *object() const noexcept { return static_cast<T *>(o); }
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 98d9aee61b..86380294ba 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -85,12 +85,6 @@ QTypeRevision relevantVersion(const QString &uri, QTypeRevision version)
return QQmlMetaType::latestModuleVersion(uri).isValid() ? version : QTypeRevision();
}
-QTypeRevision validVersion(QTypeRevision version = QTypeRevision())
-{
- // If the given version is invalid, return a valid but useless version to signal "It's OK".
- return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
-}
-
QQmlError moduleNotFoundError(const QString &uri, QTypeRevision version)
{
QQmlError error;
@@ -203,6 +197,12 @@ bool isPathAbsolute(const QString &path)
\internal
*/
+QTypeRevision QQmlImports::validVersion(QTypeRevision version)
+{
+ // If the given version is invalid, return a valid but useless version to signal "It's OK".
+ return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
+}
+
/*!
Sets the base URL to be used for all relative file imports added.
*/
@@ -450,8 +450,8 @@ QString QQmlImports::versionString(QTypeRevision version, ImportVersion versionM
\sa addFileImport(), addLibraryImport
*/
bool QQmlImports::resolveType(
- const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return,
- QQmlImportNamespace **ns_return, QList<QQmlError> *errors,
+ QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return,
+ QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList<QQmlError> *errors,
QQmlType::RegistrationType registrationType, bool *typeRecursionDetected) const
{
QQmlImportNamespace *ns = findQualifiedNamespace(type);
@@ -461,7 +461,7 @@ bool QQmlImports::resolveType(
return true;
}
if (type_return) {
- if (resolveType(type, version_return, type_return, errors, registrationType,
+ if (resolveType(typeLoader, type, version_return, type_return, errors, registrationType,
typeRecursionDetected)) {
if (lcQmlImport().isDebugEnabled()) {
#define RESOLVE_TYPE_DEBUG qCDebug(lcQmlImport) \
@@ -489,18 +489,8 @@ bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl,
const QQmlTypeLoaderQmldirContent &qmldir,
QQmlImportNamespace *nameSpace, QList<QQmlError> *errors)
{
-
- const QString preferredPath = qmldir.preferredPath();
- if (preferredPath.isEmpty()) {
- Q_ASSERT(resolvedUrl.endsWith(Slash));
- url = resolvedUrl;
- } else {
- Q_ASSERT(preferredPath.endsWith(Slash));
- if (preferredPath.startsWith(u':'))
- url = QStringLiteral("qrc") + preferredPath;
- else
- url = QUrl::fromLocalFile(preferredPath).toString();
- }
+ Q_ASSERT(resolvedUrl.endsWith(Slash));
+ url = resolvedUrl;
qmlDirComponents = qmldir.components();
@@ -537,8 +527,8 @@ QQmlDirScripts QQmlImportInstance::getVersionedScripts(const QQmlDirScripts &qml
&& (!version.hasMinorVersion()
|| (sit->version.minorVersion() <= version.minorVersion()))) {
// Load the highest version that matches
- QMap<QString, QQmlDirParser::Script>::iterator vit = versioned.find(sit->nameSpace);
- if (vit == versioned.end()
+ const auto vit = versioned.constFind(sit->nameSpace);
+ if (vit == versioned.cend()
|| (vit->version.minorVersion() < sit->version.minorVersion())) {
versioned.insert(sit->nameSpace, *sit);
}
@@ -582,37 +572,14 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
bool ret = uri == typeStr;
if (ret) {
Q_ASSERT(!type_return->isValid());
- auto createICType = [&]() {
- auto typePriv = new QQmlTypePrivate {QQmlType::RegistrationType::InlineComponentType};
- bool ok = false;
- typePriv->extraData.id->objectId = QUrl(this->url).fragment().toInt(&ok);
- Q_ASSERT(ok);
- typePriv->extraData.id->url = QUrl(this->url);
- auto icType = QQmlType(typePriv);
- typePriv->release();
- return icType;
- };
- if (containingType.isValid()) {
- // we currently cannot reference a Singleton inside itself
- // in that case, containingType is still invalid
- if (int icID = containingType.lookupInlineComponentIdByName(typeStr); icID != -1) {
- *type_return = containingType.lookupInlineComponentById(icID);
- } else {
- auto icType = createICType();
- int placeholderId = containingType.generatePlaceHolderICId();
- const_cast<QQmlImportInstance*>(this)->containingType.associateInlineComponent(typeStr, placeholderId, CompositeMetaTypeIds {}, icType);
- *type_return = QQmlType(icType);
- }
- } else {
- *type_return = createICType();
- }
+ *type_return = QQmlMetaType::fetchOrCreateInlineComponentTypeForUrl(QUrl(url));
}
return ret;
}
QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
if (it != end) {
QString componentUrl;
- bool isCompositeSingleton = false;
+ QQmlMetaType::CompositeTypeLookupMode lookupMode = QQmlMetaType::NonSingleton;
QQmlDirComponents::ConstIterator candidate = end;
for ( ; it != end && it.key() == typeStr; ++it) {
const QQmlDirParser::Component &c = *it;
@@ -657,7 +624,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
// This is our best candidate so far
candidate = it;
- isCompositeSingleton = c.singleton;
+ lookupMode = c.singleton ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton;
}
}
}
@@ -665,7 +632,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
if (candidate != end) {
if (!base) // ensure we have a componentUrl
componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
- QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, isCompositeSingleton,
+ QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, lookupMode,
nullptr, candidate->version);
if (version_return)
*version_return = candidate->version;
@@ -714,7 +681,10 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
*typeRecursionDetected = recursion;
if (recursionRestriction == QQmlImport::AllowRecursion || !recursion) {
QQmlType returnType = QQmlMetaType::typeForUrl(
- qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors);
+ qmlUrl, type, registrationType == QQmlType::CompositeSingletonType
+ ? QQmlMetaType::Singleton
+ : QQmlMetaType::NonSingleton,
+ errors);
if (type_return)
*type_return = returnType;
return returnType.isValid();
@@ -726,16 +696,16 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
}
bool QQmlImports::resolveType(
- const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return,
- QList<QQmlError> *errors, QQmlType::RegistrationType registrationType,
- bool *typeRecursionDetected) const
+ QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return,
+ QQmlType *type_return, QList<QQmlError> *errors,
+ QQmlType::RegistrationType registrationType, bool *typeRecursionDetected) const
{
const QVector<QHashedStringRef> splitName = type.split(Dot);
auto resolveTypeInNamespace = [&](
QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace,
QList<QQmlError> *errors) -> bool {
if (nameSpace->resolveType(
- m_typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
+ typeLoader, unqualifiedtype, version_return, type_return, &m_base, errors,
registrationType, typeRecursionDetected))
return true;
if (nameSpace->imports.size() == 1
@@ -746,7 +716,7 @@ bool QQmlImports::resolveType(
*type_return = QQmlMetaType::typeForUrl(
resolveLocalUrl(nameSpace->imports.at(0)->url,
unqualifiedtype.toString() + QLatin1String(".qml")),
- type, false, errors);
+ type, QQmlMetaType::NonSingleton, errors);
return type_return->isValid();
}
return false;
@@ -765,23 +735,8 @@ bool QQmlImports::resolveType(
} else {
if (resolveTypeInNamespace(splitName.at(0), &m_unqualifiedset, nullptr)) {
// either simple type + inline component
- auto const icName = splitName.at(1).toString();
- auto objectIndex = type_return->lookupInlineComponentIdByName(icName);
- if (objectIndex != -1) {
- *type_return = type_return->lookupInlineComponentById(objectIndex);
- } else {
- auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
- icTypePriv->setContainingType(type_return);
- icTypePriv->extraData.id->url = type_return->sourceUrl();
- int placeholderId = type_return->generatePlaceHolderICId();
- icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId));
- auto icType = QQmlType(icTypePriv);
- icTypePriv->release();
- type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType);
- *type_return = icType;
- }
- Q_ASSERT(type_return->containingType().isValid());
- type_return->setPendingResolutionName(icName);
+ *type_return = QQmlMetaType::inlineComponentType(
+ *type_return, splitName.at(1).toString());
return true;
} else {
// or a failure
@@ -802,22 +757,8 @@ bool QQmlImports::resolveType(
error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(splitName.at(0).toString()));
} else {
if (resolveTypeInNamespace(splitName.at(1), s, nullptr)) {
- auto const icName = splitName.at(2).toString();
- auto objectIndex = type_return->lookupInlineComponentIdByName(icName);
- if (objectIndex != -1)
- *type_return = type_return->lookupInlineComponentById(objectIndex);
- else {
- auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
- icTypePriv->setContainingType(type_return);
- icTypePriv->extraData.id->url = type_return->sourceUrl();
- int placeholderId = type_return->generatePlaceHolderICId();
- icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId));
- auto icType = QQmlType(icTypePriv);
- icTypePriv->release();
- type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType);
- *type_return = icType;
- }
- type_return->setPendingResolutionName(icName);
+ *type_return = QQmlMetaType::inlineComponentType(
+ *type_return, splitName.at(2).toString());
return true;
} else {
error.setDescription(QQmlImportDatabase::tr("- %1 is not a type").arg(splitName.at(1).toString()));
@@ -950,7 +891,7 @@ QQmlImportNamespace *QQmlImports::findQualifiedNamespace(const QHashedStringRef
Import an extension defined by a qmldir file.
*/
QTypeRevision QQmlImports::importExtension(
- const QString &uri, QTypeRevision version, QQmlImportDatabase *database,
+ QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version,
const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
{
Q_ASSERT(qmldir->hasContent());
@@ -970,35 +911,49 @@ QTypeRevision QQmlImports::importExtension(
return QTypeRevision();
}
- if (qmldir->plugins().isEmpty())
+ if (qmldir->plugins().isEmpty()) {
+ // If the qmldir does not register a plugin, we might still have declaratively
+ // registered types (if we are dealing with an application instead of a library)
+ if (!QQmlMetaType::typeModule(uri, version))
+ QQmlMetaType::qmlRegisterModuleTypes(uri);
return validVersion(version);
+ }
- QQmlPluginImporter importer(uri, version, database, qmldir, m_typeLoader, errors);
+ QQmlPluginImporter importer(
+ uri, version, typeLoader->importDatabase(), qmldir, typeLoader, errors);
return importer.importPlugins();
}
-bool QQmlImports::getQmldirContent(const QString &qmldirIdentifier, const QString &uri,
- QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
+QString QQmlImports::redirectQmldirContent(
+ QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir)
+{
+ const QString preferredPath = qmldir->preferredPath();
+ const QString url = preferredPath.startsWith(u':')
+ ? QStringLiteral("qrc") + preferredPath
+ : QUrl::fromLocalFile(preferredPath).toString();
+
+ QQmlTypeLoaderQmldirContent redirected
+ = typeLoader->qmldirContent(url + QLatin1String("qmldir"));
+
+ // Ignore errors: If the qmldir doesn't exist, stick to the old one.
+ if (redirected.hasContent() && !redirected.hasError())
+ *qmldir = std::move(redirected);
+ return url;
+}
+
+bool QQmlImports::getQmldirContent(
+ QQmlTypeLoader *typeLoader, const QString &qmldirIdentifier, const QString &uri,
+ QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors)
{
Q_ASSERT(errors);
Q_ASSERT(qmldir);
- *qmldir = m_typeLoader->qmldirContent(qmldirIdentifier);
- if ((*qmldir).hasContent()) {
- // Ensure that parsing was successful
- if ((*qmldir).hasError()) {
- QUrl url = QUrl::fromLocalFile(qmldirIdentifier);
- const QList<QQmlError> qmldirErrors = (*qmldir).errors(uri);
- for (int i = 0; i < qmldirErrors.size(); ++i) {
- QQmlError error = qmldirErrors.at(i);
- error.setUrl(url);
- errors->append(error);
- }
- return false;
- }
- }
+ *qmldir = typeLoader->qmldirContent(qmldirIdentifier);
+ if (!qmldir->hasContent() || !qmldir->hasError())
+ return true;
- return true;
+ errors->append(qmldir->errors(uri, QUrl::fromLocalFile(qmldirIdentifier)));
+ return false;
}
QString QQmlImports::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
@@ -1036,26 +991,6 @@ QString QQmlImports::resolvedUri(const QString &dir_arg, QQmlImportDatabase *dat
return stableRelativePath;
}
-/* removes all file selector occurrences in path
- firstPlus is the position of the initial '+' in the path
- which we always have as we check for '+' to decide whether
- we need to do some work at all
-*/
-static QString pathWithoutFileSelectors(QString path, // we want a copy of path
- qsizetype firstPlus)
-{
- do {
- Q_ASSERT(path.at(firstPlus) == u'+');
- const auto eos = path.size();
- qsizetype terminatingSlashPos = firstPlus + 1;
- while (terminatingSlashPos != eos && path.at(terminatingSlashPos) != u'/')
- ++terminatingSlashPos;
- path.remove(firstPlus, terminatingSlashPos - firstPlus + 1);
- firstPlus = path.indexOf(u'+', firstPlus);
- } while (firstPlus != -1);
- return path;
-}
-
/*!
\internal
@@ -1102,47 +1037,17 @@ QTypeRevision QQmlImports::matchingQmldirVersion(
typedef QQmlDirComponents::const_iterator ConstIterator;
const QQmlDirComponents &components = qmldir.components();
- QMultiHash<QString, ConstIterator> baseFileName2ConflictingComponents;
-
ConstIterator cend = components.constEnd();
for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
for (ConstIterator cit2 = components.constBegin(); cit2 != cit; ++cit2) {
if (cit2->typeName == cit->typeName && cit2->version == cit->version) {
- // ugly heuristic to deal with file selectors
- const auto comp2PotentialFileSelectorPos = cit2->fileName.indexOf(u'+');
- const bool comp2MightHaveFileSelector = comp2PotentialFileSelectorPos != -1;
- /* If we detect conflicting paths, we check if they agree when we remove anything looking like a
- file selector.
- We need to create copies of the filenames, otherwise QString::replace would modify the
- existing file-names
- */
- QString compFileName1 = cit->fileName;
- QString compFileName2 = cit2->fileName;
- if (auto fileSelectorPos1 = compFileName1.indexOf(u'+'); fileSelectorPos1 != -1) {
- // existing entry was file selector entry, fix it up
- // it could also be the case that _both_ are using file selectors
- QString baseName = comp2MightHaveFileSelector ? pathWithoutFileSelectors(compFileName2,
- comp2PotentialFileSelectorPos)
- : compFileName2;
- if (pathWithoutFileSelectors(compFileName1, fileSelectorPos1) == baseName) {
- baseFileName2ConflictingComponents.insert(baseName, cit);
- baseFileName2ConflictingComponents.insert(baseName, cit2);
- continue;
- }
- // fall through to error case
- } else if (comp2MightHaveFileSelector) {
- // new entry contains file selector (and we now that cit did not)
- if (pathWithoutFileSelectors(compFileName2, comp2PotentialFileSelectorPos) == compFileName1) {
- baseFileName2ConflictingComponents.insert(compFileName1, cit2);
- continue;
- }
- // fall through to error case
- }
// This entry clashes with a predecessor
QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("\"%1\" version %2.%3 is defined more than once in module \"%4\"")
- .arg(cit->typeName).arg(cit->version.majorVersion())
- .arg(cit->version.minorVersion()).arg(uri));
+ error.setDescription(
+ QQmlImportDatabase::tr(
+ "\"%1\" version %2.%3 is defined more than once in module \"%4\"")
+ .arg(cit->typeName).arg(cit->version.majorVersion())
+ .arg(cit->version.minorVersion()).arg(uri));
errors->prepend(error);
return QTypeRevision();
}
@@ -1151,14 +1056,6 @@ QTypeRevision QQmlImports::matchingQmldirVersion(
addVersion(cit->version);
}
- // ensure that all components point to the actual base URL, and let the file selectors resolve them correctly during URL resolution
- for (auto keyIt = baseFileName2ConflictingComponents.keyBegin(); keyIt != baseFileName2ConflictingComponents.keyEnd(); ++keyIt) {
- const QString& baseFileName = *keyIt;
- const auto conflictingComponents = baseFileName2ConflictingComponents.values(baseFileName);
- for (ConstIterator component: conflictingComponents)
- component->fileName = baseFileName;
- }
-
typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
const QQmlDirScripts &scripts = qmldir.scripts();
@@ -1219,7 +1116,7 @@ QQmlImportNamespace *QQmlImports::importNamespace(const QString &prefix)
return nameSpace;
}
-QQmlImportInstance *QQmlImports::addImportToNamespace(
+static QQmlImportInstance *addImportToNamespace(
QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, QTypeRevision version,
QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, quint16 precedence)
{
@@ -1249,11 +1146,11 @@ QQmlImportInstance *QQmlImports::addImportToNamespace(
}
QTypeRevision QQmlImports::addLibraryImport(
- QQmlImportDatabase *database, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl,
ImportFlags flags, quint16 precedence, QList<QQmlError> *errors)
{
- Q_ASSERT(database);
+ Q_ASSERT(typeLoader);
Q_ASSERT(errors);
qCDebug(lcQmlImport)
@@ -1273,15 +1170,19 @@ QTypeRevision QQmlImports::addLibraryImport(
QQmlTypeLoaderQmldirContent qmldir;
if (!qmldirIdentifier.isEmpty()) {
- if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
+ if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
return QTypeRevision();
if (qmldir.hasContent()) {
- version = importExtension(uri, version, database, &qmldir, errors);
+ version = importExtension(typeLoader, uri, version, &qmldir, errors);
if (!version.isValid())
return QTypeRevision();
- if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors))
+ const QString resolvedUrl = qmldir.hasRedirection()
+ ? redirectQmldirContent(typeLoader, &qmldir)
+ : qmldirUrl;
+
+ if (!inserted->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors))
return QTypeRevision();
}
}
@@ -1333,11 +1234,11 @@ QTypeRevision QQmlImports::addLibraryImport(
In case of failure, the \a errors array will filled appropriately.
*/
QTypeRevision QQmlImports::addFileImport(
- QQmlImportDatabase *database, const QString &uri, const QString &prefix,
- QTypeRevision version, ImportFlags flags, quint16 precedence,
- QString *localQmldir, QList<QQmlError> *errors)
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
+ QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir,
+ QList<QQmlError> *errors)
{
- Q_ASSERT(database);
+ Q_ASSERT(typeLoader);
Q_ASSERT(errors);
qCDebug(lcQmlImport)
@@ -1367,7 +1268,7 @@ QTypeRevision QQmlImports::addFileImport(
QString qmldirUrl = resolveLocalUrl(m_base, importUri + (importUri.endsWith(Slash)
? String_qmldir
: Slash_qmldir));
- qmldirUrl = m_typeLoader->engine()->interceptUrl(
+ qmldirUrl = typeLoader->engine()->interceptUrl(
QUrl(qmldirUrl), QQmlAbstractUrlInterceptor::QmldirFile).toString();
QString qmldirIdentifier;
@@ -1377,7 +1278,7 @@ QTypeRevision QQmlImports::addFileImport(
Q_ASSERT(!localFileOrQrc.isEmpty());
const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
- if (!m_typeLoader->directoryExists(dir)) {
+ if (!typeLoader->directoryExists(dir)) {
if (precedence < QQmlImportInstance::Implicit) {
QQmlError error;
error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri));
@@ -1389,12 +1290,12 @@ QTypeRevision QQmlImports::addFileImport(
// Transforms the (possible relative) uri into our best guess relative to the
// import paths.
- importUri = resolvedUri(dir, database);
+ importUri = resolvedUri(dir, typeLoader->importDatabase());
if (importUri.endsWith(Slash))
importUri.chop(1);
- if (!m_typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
- qmldirIdentifier = localFileOrQrc;
+ if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
+ qmldirIdentifier = std::move(localFileOrQrc);
if (localQmldir)
*localQmldir = qmldirIdentifier;
}
@@ -1414,6 +1315,15 @@ QTypeRevision QQmlImports::addFileImport(
// The url for the path containing files for this import
QString url = resolveLocalUrl(m_base, uri);
+ if (url.isEmpty()) {
+ QQmlError error;
+ error.setDescription(
+ QQmlImportDatabase::tr("Cannot resolve URL for import \"%1\"").arg(uri));
+ error.setUrl(m_baseUrl);
+ errors->prepend(error);
+ return QTypeRevision();
+ }
+
if (!url.endsWith(Slash) && !url.endsWith(Backslash))
url += Slash;
@@ -1433,7 +1343,7 @@ QTypeRevision QQmlImports::addFileImport(
if (!(flags & QQmlImports::ImportIncomplete) && !qmldirIdentifier.isEmpty()) {
QQmlTypeLoaderQmldirContent qmldir;
- if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors))
+ if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors))
return QTypeRevision();
if (qmldir.hasContent()) {
@@ -1447,10 +1357,13 @@ QTypeRevision QQmlImports::addFileImport(
errors, precedence);
Q_ASSERT(inserted);
- version = importExtension(importUri, version, database, &qmldir, errors);
+ version = importExtension(typeLoader, importUri, version, &qmldir, errors);
if (!version.isValid())
return QTypeRevision();
+ if (qmldir.hasRedirection())
+ url = redirectQmldirContent(typeLoader, &qmldir);
+
if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors))
return QTypeRevision();
@@ -1466,10 +1379,10 @@ QTypeRevision QQmlImports::addFileImport(
}
QTypeRevision QQmlImports::updateQmldirContent(
- QQmlImportDatabase *database, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors)
{
- Q_ASSERT(database);
+ Q_ASSERT(typeLoader);
Q_ASSERT(errors);
qDebug(lcQmlImport)
@@ -1481,16 +1394,20 @@ QTypeRevision QQmlImports::updateQmldirContent(
if (QQmlImportInstance *import = nameSpace->findImport(uri)) {
QQmlTypeLoaderQmldirContent qmldir;
- if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors))
+ if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors))
return QTypeRevision();
if (qmldir.hasContent()) {
QTypeRevision version = importExtension(
- uri, import->version, database, &qmldir, errors);
+ typeLoader, uri, import->version, &qmldir, errors);
if (!version.isValid())
return QTypeRevision();
- if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) {
+ const QString resolvedUrl = qmldir.hasRedirection()
+ ? redirectQmldirContent(typeLoader, &qmldir)
+ : qmldirUrl;
+
+ if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) {
if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) {
// The implicit import qmldir can be empty, and plugins have no extra versions
if (uri != QLatin1String(".") && !QQmlMetaType::matchingModuleVersion(uri, version).isValid()) {
@@ -1518,7 +1435,7 @@ QTypeRevision QQmlImports::updateQmldirContent(
}
/*!
- \fn QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QString *localQmldir, QList<QQmlError> *errors)
+ \fn QQmlImports::addImplicitImport(QQmlTypeLoader *typeLoader, QString *localQmldir, QList<QQmlError> *errors)
\internal
Adds an implicit "." file import. This is equivalent to calling addFileImport(), but error
@@ -1531,13 +1448,12 @@ QTypeRevision QQmlImports::updateQmldirContent(
/*!
\internal
*/
-bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType)
+bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl)
{
importInstance->url = importUrl.toString();
importInstance->uri = name;
importInstance->isInlineComponent = true;
importInstance->version = QTypeRevision::zero();
- importInstance->containingType = containingType;
m_unqualifiedset.imports.push_back(importInstance);
m_unqualifiedset.setNeedsSorting(true);
return true;
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 6a9d207a6b..ef9b4b3422 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -15,7 +15,6 @@
#include <private/qqmldirparser_p.h>
#include <private/qqmltype_p.h>
#include <private/qstringhash_p.h>
-#include <private/qv4compileddata_p.h>
#include <private/qfieldlist_p.h>
//
@@ -38,6 +37,7 @@ class QQmlImportNamespace;
class QQmlImportDatabase;
class QQmlTypeLoader;
class QQmlTypeLoaderQmldirContent;
+class QTypeRevision;
const QLoggingCategory &lcQmlImport();
@@ -55,7 +55,6 @@ struct QQmlImportInstance
QString uri; // e.g. QtQuick
QString url; // the base path of the import
- QQmlType containingType; // points to the containing type for inline components
QTypeRevision version; // the version imported
bool isLibrary; // true means that this is not a file import
@@ -115,7 +114,7 @@ public:
}
};
-class Q_QML_PRIVATE_EXPORT QQmlImports : public QQmlRefCount
+class Q_QML_EXPORT QQmlImports final : public QQmlRefCounted<QQmlImports>
{
Q_DISABLE_COPY_MOVE(QQmlImports)
public:
@@ -127,7 +126,7 @@ public:
};
Q_DECLARE_FLAGS(ImportFlags, ImportFlag)
- QQmlImports(QQmlTypeLoader *loader) : m_typeLoader(loader) {}
+ QQmlImports() = default;
~QQmlImports()
{
while (QQmlImportNamespace *ns = m_qualifiedSets.takeFirst())
@@ -138,13 +137,14 @@ public:
QUrl baseUrl() const { return m_baseUrl; }
bool resolveType(
- const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return,
- QQmlImportNamespace **ns_return, QList<QQmlError> *errors = nullptr,
+ QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return,
+ QTypeRevision *version_return, QQmlImportNamespace **ns_return,
+ QList<QQmlError> *errors = nullptr,
QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType,
bool *typeRecursionDetected = nullptr) const;
QTypeRevision addImplicitImport(
- QQmlImportDatabase *importDb, QString *localQmldir, QList<QQmlError> *errors)
+ QQmlTypeLoader *typeLoader, QString *localQmldir, QList<QQmlError> *errors)
{
Q_ASSERT(errors);
qCDebug(lcQmlImport) << "addImplicitImport:" << qPrintable(baseUrl().toString());
@@ -152,26 +152,25 @@ public:
const ImportFlags flags =
ImportFlags(!isLocal(baseUrl()) ? ImportIncomplete : ImportNoFlag);
return addFileImport(
- importDb, QLatin1String("."), QString(), QTypeRevision(),
- flags, QQmlImportInstance::Implicit, localQmldir, errors);
+ typeLoader, QLatin1String("."), QString(), QTypeRevision(), flags,
+ QQmlImportInstance::Implicit, localQmldir, errors);
}
bool addInlineComponentImport(
- QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl,
- QQmlType containingType);
+ QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl);
QTypeRevision addFileImport(
- QQmlImportDatabase *importDb, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir,
QList<QQmlError> *errors);
QTypeRevision addLibraryImport(
- QQmlImportDatabase *importDb, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl,
ImportFlags flags, quint16 precedence, QList<QQmlError> *errors);
QTypeRevision updateQmldirContent(
- QQmlImportDatabase *importDb, const QString &uri, const QString &prefix,
+ QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix,
const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors);
void populateCache(QQmlTypeNameCache *cache) const;
@@ -213,14 +212,17 @@ public:
static void setDesignerSupportRequired(bool b);
+ static QTypeRevision validVersion(QTypeRevision version = QTypeRevision());
+
private:
friend class QQmlImportDatabase;
QQmlImportNamespace *importNamespace(const QString &prefix);
bool resolveType(
- const QHashedStringRef &type, QTypeRevision *version_return, QQmlType *type_return,
- QList<QQmlError> *errors, QQmlType::RegistrationType registrationType,
+ QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return,
+ QQmlType *type_return, QList<QQmlError> *errors,
+ QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected = nullptr) const;
QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const;
@@ -230,20 +232,17 @@ private:
QTypeRevision version, QList<QQmlError> *errors);
QTypeRevision importExtension(
- const QString &uri, QTypeRevision version, QQmlImportDatabase *database,
+ QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version,
const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors);
+ QString redirectQmldirContent(QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir);
+
bool getQmldirContent(
- const QString &qmldirIdentifier, const QString &uri, QQmlTypeLoaderQmldirContent *qmldir,
- QList<QQmlError> *errors);
+ QQmlTypeLoader *typeLoader, const QString &qmldirIdentifier, const QString &uri,
+ QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors);
QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
- QQmlImportInstance *addImportToNamespace(
- QQmlImportNamespace *nameSpace, const QString &uri, const QString &url,
- QTypeRevision version, QV4::CompiledData::Import::ImportType type,
- QList<QQmlError> *errors, quint16 precedence);
-
QUrl m_baseUrl;
QString m_base;
@@ -255,13 +254,11 @@ private:
// storage of data related to imports with a namespace
QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> m_qualifiedSets;
-
- QQmlTypeLoader *m_typeLoader = nullptr;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlImports::ImportFlags)
-class Q_QML_PRIVATE_EXPORT QQmlImportDatabase
+class Q_QML_EXPORT QQmlImportDatabase
{
Q_DECLARE_TR_FUNCTIONS(QQmlImportDatabase)
public:
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index 4ae1f20d6c..5c18230450 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -177,9 +177,15 @@ protected:
};
\endcode
-Although the previous example would work, it is not optimal. Real world incubation
-controllers should try and maximize the amount of idle time they consume - rather
-than a static amount like 5 milliseconds - while not disturbing the application.
+Although the example works, it is heavily simplified. Real world incubation controllers
+try and maximize the amount of idle time they consume while not disturbing the
+application. Using a static amount of 5 milliseconds like above may both leave idle
+time on the table in some frames and disturb the application in others.
+
+\l{QQuickWindow}, \l{QQuickView}, and \l{QQuickWidget} all pre-create an incubation
+controller that spaces out incubation over multiple frames using a more intelligent
+algorithm. You rarely have to write your own.
+
*/
/*!
@@ -457,25 +463,37 @@ synchronously which, depending on the complexity of the object, can cause notic
stutters in the application.
The use of QQmlIncubator gives more control over the creation of a QML object,
-including allowing it to be created asynchronously using application idle time. The following
+including allowing it to be created asynchronously using application idle time. The following
example shows a simple use of QQmlIncubator.
\code
+// Initialize the incubator
QQmlIncubator incubator;
component->create(incubator);
+\endcode
-while (!incubator.isReady()) {
- QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
-}
+Let the incubator run for a while (normally by returning control to the event loop),
+then poll it. There are a number of ways to get back to the incubator later. You may
+want to connect to one of the signals sent by \l{QQuickWindow}, or you may want to run
+a \l{QTimer} especially for that. You may also need the object for some specific
+purpose and poll the incubator when that purpose arises.
-QObject *object = incubator.object();
+\code
+// Poll the incubator
+if (incubator.isReady()) {
+ QObject *object = incubator.object();
+ // Use created object
+}
\endcode
-Asynchronous incubators are controlled by a QQmlIncubationController that is
-set on the QQmlEngine, which lets the engine know when the application is idle and
+Asynchronous incubators are controlled by a \l{QQmlIncubationController} that is
+set on the \l{QQmlEngine}, which lets the engine know when the application is idle and
incubating objects should be processed. If an incubation controller is not set on the
-QQmlEngine, QQmlIncubator creates objects synchronously regardless of the
-specified IncubationMode.
+\l{QQmlEngine}, \l{QQmlIncubator} creates objects synchronously regardless of the
+specified IncubationMode. By default, no incubation controller is set. However,
+\l{QQuickView}, \l{QQuickWindow} and \l{QQuickWidget} all set incubation controllers
+on their respective \l{QQmlEngine}s. These incubation controllers space out incubations
+across multiple frames while the view is being rendered.
QQmlIncubator supports three incubation modes:
\list
@@ -742,13 +760,23 @@ void QQmlIncubator::statusChanged(Status status)
}
/*!
-Called after the \a object is first created, but before property bindings are
-evaluated and, if applicable, QQmlParserStatus::componentComplete() is
-called. This is equivalent to the point between QQmlComponent::beginCreate()
+Called after the \a object is first created, but before complex property
+bindings are evaluated and, if applicable, QQmlParserStatus::componentComplete()
+is called. This is equivalent to the point between QQmlComponent::beginCreate()
and QQmlComponent::completeCreate(), and can be used to assign initial values
to the object's properties.
The default implementation does nothing.
+
+\note Simple bindings such as numeric literals are evaluated before
+setInitialState() is called. The categorization of bindings into simple and
+complex ones is intentionally unspecified and may change between versions of
+Qt and depending on whether and how you are using \l{qmlcachegen}. You should
+not rely on any particular binding to be evaluated either before or after
+setInitialState() is called. For example, a constant expression like
+\e{MyType.EnumValue} may be recognized as such at compile time or deferred
+to be executed as binding. The same holds for constant expressions like
+\e{-(5)} or \e{"a" + " constant string"}.
*/
void QQmlIncubator::setInitialState(QObject *object)
{
diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h
index d786cd9f52..f35679a8a1 100644
--- a/src/qml/qml/qqmlincubator_p.h
+++ b/src/qml/qml/qqmlincubator_p.h
@@ -12,6 +12,8 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlguardedcontextdata_p.h>
+#include <QtCore/qpointer.h>
+
//
// W A R N I N G
// -------------
@@ -28,7 +30,7 @@ QT_BEGIN_NAMESPACE
class RequiredProperties;
class QQmlIncubator;
-class Q_QML_PRIVATE_EXPORT QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator, public QSharedData
+class Q_QML_EXPORT QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator, public QSharedData
{
public:
QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m);
diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp
index 5137c4c1ab..b29ce185ef 100644
--- a/src/qml/qml/qqmlirloader.cpp
+++ b/src/qml/qml/qqmlirloader.cpp
@@ -57,7 +57,7 @@ void QQmlIRLoader::load()
const auto createValueTypePragma = [&](
Pragma::PragmaType type,
- Pragma::ValueTypeBehaviorValue value) {
+ Pragma::ValueTypeBehaviorValues value) {
createPragma(type)->valueTypeBehavior = value;
};
@@ -74,14 +74,19 @@ void QQmlIRLoader::load()
if (unit->flags & QV4::CompiledData::Unit::ComponentsBound)
createComponentPragma(Pragma::ComponentBehavior, Pragma::Bound);
- if (unit->flags & QV4::CompiledData::Unit::FunctionSignaturesEnforced)
- createFunctionSignaturePragma(Pragma::FunctionSignatureBehavior, Pragma::Enforced);
+ if (unit->flags & QV4::CompiledData::Unit::FunctionSignaturesIgnored)
+ createFunctionSignaturePragma(Pragma::FunctionSignatureBehavior, Pragma::Ignored);
if (unit->flags & QV4::CompiledData::Unit::NativeMethodsAcceptThisObject)
createNativeMethodPragma(Pragma::NativeMethodBehavior, Pragma::AcceptThisObject);
+ Pragma::ValueTypeBehaviorValues valueTypeBehavior = {};
if (unit->flags & QV4::CompiledData::Unit::ValueTypesCopied)
- createValueTypePragma(Pragma::ValueTypeBehavior, Pragma::Copy);
+ valueTypeBehavior |= Pragma::Copy;
+ if (unit->flags & QV4::CompiledData::Unit::ValueTypesAddressable)
+ valueTypeBehavior |= Pragma::Addressable;
+ if (valueTypeBehavior)
+ createValueTypePragma(Pragma::ValueTypeBehavior, valueTypeBehavior);
for (uint i = 0; i < qmlUnit->nObjects; ++i) {
const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i);
diff --git a/src/qml/qml/qqmlirloader_p.h b/src/qml/qml/qqmlirloader_p.h
index f4c1213223..8a14d6833e 100644
--- a/src/qml/qml/qqmlirloader_p.h
+++ b/src/qml/qml/qqmlirloader_p.h
@@ -26,7 +26,7 @@ struct Document;
struct Object;
}
-struct Q_QML_PRIVATE_EXPORT QQmlIRLoader {
+struct Q_QML_EXPORT QQmlIRLoader {
QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output);
void load();
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 304c5da29a..d7cf38984b 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -350,19 +350,35 @@ void QQmlPropertyCapture::captureProperty(
captureNonBindableProperty(o, propertyData->notifyIndex(), propertyData->coreIndex(), doNotify);
}
+bool QQmlJavaScriptExpression::needsPropertyChangeTrigger(QObject *target, int propertyIndex)
+{
+ TriggerList **prev = &qpropertyChangeTriggers;
+ TriggerList *current = qpropertyChangeTriggers;
+ while (current) {
+ if (!current->target) {
+ *prev = current->next;
+ QRecyclePool<TriggerList>::Delete(current);
+ current = *prev;
+ } else if (current->target == target && current->propertyIndex == propertyIndex) {
+ return false; // already installed
+ } else {
+ prev = &current->next;
+ current = current->next;
+ }
+ }
+
+ return true;
+}
+
void QQmlPropertyCapture::captureTranslation()
{
// use a unique invalid index to avoid needlessly querying the metaobject for
// the correct index of of the translationLanguage property
int const invalidIndex = -2;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger;
- trigger = trigger->next) {
- if (trigger->target == engine && trigger->propertyIndex == invalidIndex)
- return; // already installed
+ if (expression->needsPropertyChangeTrigger(engine, invalidIndex)) {
+ auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
+ trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
}
- auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
-
- trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
}
void QQmlPropertyCapture::captureBindableProperty(
@@ -372,16 +388,14 @@ void QQmlPropertyCapture::captureBindableProperty(
// the automatic capturing process already takes care of everything
if (!expression->mustCaptureBindableProperty())
return;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger;
- trigger = trigger->next) {
- if (trigger->target == o && trigger->propertyIndex == c)
- return; // already installed
+
+ if (expression->needsPropertyChangeTrigger(o, c)) {
+ auto trigger = expression->allocatePropertyChangeTrigger(o, c);
+ QUntypedBindable bindable;
+ void *argv[] = { &bindable };
+ metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
+ bindable.observe(trigger);
}
- auto trigger = expression->allocatePropertyChangeTrigger(o, c);
- QUntypedBindable bindable;
- void *argv[] = { &bindable };
- metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
- bindable.observe(trigger);
}
void QQmlPropertyCapture::captureNonBindableProperty(QObject *o, int n, int c, bool doNotify)
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index e8e4bb4990..80e2033627 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -62,7 +62,7 @@ private:
QQmlDelayedError **prevError;
};
-class Q_QML_PRIVATE_EXPORT QQmlJavaScriptExpression
+class Q_QML_EXPORT QQmlJavaScriptExpression
{
Q_DISABLE_COPY_MOVE(QQmlJavaScriptExpression)
public:
@@ -132,6 +132,8 @@ public:
QQmlEngine *engine() const { return m_context ? m_context->engine() : nullptr; }
bool hasUnresolvedNames() const { return m_context && m_context->hasUnresolvedNames(); }
+
+ bool needsPropertyChangeTrigger(QObject *target, int propertyIndex);
QPropertyChangeTrigger *allocatePropertyChangeTrigger(QObject *target, int propertyIndex);
protected:
@@ -183,7 +185,7 @@ protected:
TriggerList *qpropertyChangeTriggers = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlPropertyCapture
+class Q_QML_EXPORT QQmlPropertyCapture
{
public:
QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w)
diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp
index 5fd077c7c1..a166041070 100644
--- a/src/qml/qml/qqmllist.cpp
+++ b/src/qml/qml/qqmllist.cpp
@@ -447,7 +447,7 @@ Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit)
QML list properties are type-safe - in this case \c {Fruit} is a QObject type that
\c {Apple}, \c {Orange} and \c {Banana} all derive from.
-\sa {Extending QML - Object and List Property Types Example}
+\sa {Chapter 5: Using List Property Types}
*/
/*!
diff --git a/src/qml/qml/qqmllist.h b/src/qml/qml/qqmllist.h
index c54ebf0677..6f2c077764 100644
--- a/src/qml/qml/qqmllist.h
+++ b/src/qml/qml/qqmllist.h
@@ -5,9 +5,11 @@
#define QQMLLIST_H
#include <QtQml/qtqmlglobal.h>
+
+#include <QtCore/qcontainerinfo.h>
#include <QtCore/qlist.h>
+#include <QtCore/qmetatype.h>
#include <QtCore/qvariant.h>
-#include <QtCore/QMetaType>
QT_BEGIN_NAMESPACE
@@ -21,6 +23,8 @@ struct QMetaObject;
template<typename T>
class QQmlListProperty {
public:
+ using value_type = T*;
+
using AppendFunction = void (*)(QQmlListProperty<T> *, T *);
using CountFunction = qsizetype (*)(QQmlListProperty<T> *);
using AtFunction = T *(*)(QQmlListProperty<T> *, qsizetype);
@@ -84,24 +88,45 @@ public:
ReplaceFunction replace = nullptr;
RemoveLastFunction removeLast = nullptr;
+ template<typename List>
+ List toList()
+ {
+ if constexpr (std::is_same_v<List, QList<T *>>) {
+ if (append == qlist_append)
+ return *static_cast<QList<T *> *>(data);
+ }
+
+ const qsizetype size = count(this);
+
+ List result;
+ if constexpr (QContainerInfo::has_reserve_v<List>)
+ result.reserve(size);
+
+ static_assert(QContainerInfo::has_push_back_v<List>);
+ for (qsizetype i = 0; i < size; ++i)
+ result.push_back(at(this, i));
+
+ return result;
+ }
+
private:
static void qlist_append(QQmlListProperty *p, T *v) {
- reinterpret_cast<QList<T *> *>(p->data)->append(v);
+ static_cast<QList<T *> *>(p->data)->append(v);
}
static qsizetype qlist_count(QQmlListProperty *p) {
- return reinterpret_cast<QList<T *> *>(p->data)->size();
+ return static_cast<QList<T *> *>(p->data)->size();
}
static T *qlist_at(QQmlListProperty *p, qsizetype idx) {
- return reinterpret_cast<QList<T *> *>(p->data)->at(idx);
+ return static_cast<QList<T *> *>(p->data)->at(idx);
}
static void qlist_clear(QQmlListProperty *p) {
- return reinterpret_cast<QList<T *> *>(p->data)->clear();
+ return static_cast<QList<T *> *>(p->data)->clear();
}
static void qlist_replace(QQmlListProperty *p, qsizetype idx, T *v) {
- return reinterpret_cast<QList<T *> *>(p->data)->replace(idx, v);
+ return static_cast<QList<T *> *>(p->data)->replace(idx, v);
}
static void qlist_removeLast(QQmlListProperty *p) {
- return reinterpret_cast<QList<T *> *>(p->data)->removeLast();
+ return static_cast<QList<T *> *>(p->data)->removeLast();
}
static void qslow_replace(QQmlListProperty<T> *list, qsizetype idx, T *v)
diff --git a/src/qml/qml/qqmllist_p.h b/src/qml/qml/qqmllist_p.h
index 2a323fcd59..642b3c3db1 100644
--- a/src/qml/qml/qqmllist_p.h
+++ b/src/qml/qml/qqmllist_p.h
@@ -20,6 +20,8 @@
#include "qqmlmetatype_p.h"
#include <QtQml/private/qbipointer_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlListReferencePrivate
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index ba6058ff3d..8d5a585b62 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -3,6 +3,8 @@
#include "qqmllistwrapper_p.h"
+#include <QtQml/qqmlinfo.h>
+
#include <private/qqmllist_p.h>
#include <private/qv4arrayiterator_p.h>
@@ -20,50 +22,143 @@ using namespace Qt::StringLiterals;
DEFINE_OBJECT_VTABLE(QmlListWrapper);
-void Heap::QmlListWrapper::init()
+static void setArrayData(Heap::QmlListWrapper *d)
+{
+ QV4::Scope scope(d->internalClass->engine);
+ QV4::ScopedObject o(scope, d);
+ o->arrayCreate();
+}
+
+struct ListWrapperObject
+{
+ QV4::Scope scope;
+ QV4::ScopedObject object;
+
+ ListWrapperObject(QQmlListProperty<QObject> *p)
+ : scope(static_cast<Heap::QmlListWrapper *>(p->data)->internalClass->engine)
+ , object(scope, static_cast<Heap::QmlListWrapper *>(p->data))
+ {
+ Q_ASSERT(object);
+ Q_ASSERT(object->arrayData());
+ }
+
+ Heap::ArrayData *arrayData() const
+ {
+ return object->arrayData();
+ }
+};
+
+static void appendWrapped(QQmlListProperty<QObject> *p, QObject *o)
+{
+ ListWrapperObject object(p);
+ Heap::ArrayData *arrayData = object.arrayData();
+
+ const uint length = arrayData->length();
+ if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) {
+ object.scope.engine->throwRangeError(QLatin1String("Too many elements."));
+ return;
+ }
+
+ ArrayData::realloc(object.object, Heap::ArrayData::Simple, length + 1, false);
+ arrayData->vtable()->put(
+ object.object, length, QV4::QObjectWrapper::wrap(object.scope.engine, o));
+}
+
+static qsizetype countWrapped(QQmlListProperty<QObject> *p)
+{
+ ListWrapperObject object(p);
+ return object.arrayData()->length();
+}
+
+static QObject *atWrapped(QQmlListProperty<QObject> *p, qsizetype i)
+{
+ ListWrapperObject object(p);
+ QV4::Scoped<QObjectWrapper> result(object.scope, object.arrayData()->get(i));
+ return result ? result->object() : nullptr;
+}
+
+static void clearWrapped(QQmlListProperty<QObject> *p)
+{
+ ListWrapperObject object(p);
+ object.arrayData()->vtable()->truncate(object.object, 0);
+}
+
+static void replaceWrapped(QQmlListProperty<QObject> *p, qsizetype i, QObject *o)
+{
+ ListWrapperObject object(p);
+ object.arrayData()->vtable()->put(
+ object.object, i, QV4::QObjectWrapper::wrap(object.scope.engine, o));
+}
+
+static void removeLastWrapped(QQmlListProperty<QObject> *p)
+{
+ ListWrapperObject object(p);
+ Heap::ArrayData *arrayData = object.arrayData();
+ const uint length = arrayData->length();
+ if (length > 0)
+ arrayData->vtable()->truncate(object.object, length - 1);
+}
+
+void Heap::QmlListWrapper::init(QMetaType propertyType)
+{
+ Object::init();
+ m_object.init();
+ m_propertyType = propertyType.iface();
+ setArrayData(this);
+ *property() = QQmlListProperty<QObject>(
+ nullptr, this,
+ appendWrapped, countWrapped, atWrapped,
+ clearWrapped, replaceWrapped, removeLastWrapped);
+}
+
+void Heap::QmlListWrapper::init(QObject *object, int propertyId, QMetaType propertyType)
{
Object::init();
- object.init();
- QV4::Scope scope(internalClass->engine);
- QV4::ScopedObject o(scope, this);
- o->setArrayType(Heap::ArrayData::Custom);
+ m_object.init(object);
+ m_propertyType = propertyType.iface();
+ void *args[] = { property(), nullptr };
+ QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyId, args);
+}
+
+void Heap::QmlListWrapper::init(
+ QObject *object, const QQmlListProperty<QObject> &list, QMetaType propertyType)
+{
+ Object::init();
+ m_object.init(object);
+ m_propertyType = propertyType.iface();
+ *property() = list;
}
void Heap::QmlListWrapper::destroy()
{
- object.destroy();
+ m_object.destroy();
Object::destroy();
}
-ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, int propId, QMetaType propType)
+ReturnedValue QmlListWrapper::create(
+ ExecutionEngine *engine, QObject *object, int propId, QMetaType propType)
{
if (!object || propId == -1)
return Encode::null();
-
- Scope scope(engine);
-
- Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>());
- r->d()->object = object;
- r->d()->propertyType = propType.iface();
- void *args[] = { &r->d()->property(), nullptr };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, propId, args);
- return r.asReturnedValue();
+ return engine->memoryManager->allocate<QmlListWrapper>(object, propId, propType)
+ ->asReturnedValue();
}
-ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType)
+ReturnedValue QmlListWrapper::create(
+ ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType)
{
- Scope scope(engine);
+ return engine->memoryManager->allocate<QmlListWrapper>(prop.object, prop, propType)
+ ->asReturnedValue();
+}
- Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>());
- r->d()->object = prop.object;
- r->d()->property() = prop;
- r->d()->propertyType = propType.iface();
- return r.asReturnedValue();
+ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QMetaType propType)
+{
+ return engine->memoryManager->allocate<QmlListWrapper>(propType)->asReturnedValue();
}
QVariant QmlListWrapper::toVariant() const
{
- if (!d()->object)
+ if (!d()->object())
return QVariant();
return QVariant::fromValue(toListReference());
@@ -71,11 +166,10 @@ QVariant QmlListWrapper::toVariant() const
QQmlListReference QmlListWrapper::toListReference() const
{
- Heap::QmlListWrapper *wrapper = d();
- return QQmlListReferencePrivate::init(wrapper->property(), QMetaType(wrapper->propertyType));
+ const Heap::QmlListWrapper *wrapper = d();
+ return QQmlListReferencePrivate::init(*wrapper->property(), wrapper->propertyType());
}
-
ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QmlListWrapper>());
@@ -83,12 +177,14 @@ ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const
QV4::ExecutionEngine *v4 = w->engine();
if (id.isArrayIndex()) {
- uint index = id.asArrayIndex();
- quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- if (index < count && w->d()->property().at) {
+ const uint index = id.asArrayIndex();
+ const quint32 count = w->d()->property()->count
+ ? w->d()->property()->count(w->d()->property())
+ : 0;
+ if (index < count && w->d()->property()->at) {
if (hasProperty)
*hasProperty = true;
- return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index));
+ return QV4::QObjectWrapper::wrap(v4, w->d()->property()->at(w->d()->property(), index));
}
if (hasProperty)
@@ -99,6 +195,14 @@ ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const
return Object::virtualGet(m, id, receiver, hasProperty);
}
+qint64 QmlListWrapper::virtualGetLength(const Managed *m)
+{
+ Q_ASSERT(m->as<QmlListWrapper>());
+ QQmlListProperty<QObject> *property = static_cast<const QmlListWrapper *>(m)->d()->property();
+ Q_ASSERT(property);
+ return property->count ? property->count(property) : 0;
+}
+
bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QmlListWrapper>());
@@ -106,7 +210,7 @@ bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value,
const auto *w = static_cast<const QmlListWrapper *>(m);
QV4::ExecutionEngine *v4 = w->engine();
- QQmlListProperty<QObject> *prop = &(w->d()->property());
+ QQmlListProperty<QObject> *prop = w->d()->property();
if (id.isArrayIndex()) {
if (!prop->count || !prop->replace)
@@ -146,14 +250,16 @@ PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property
{
const QmlListWrapper *w = static_cast<const QmlListWrapper *>(o);
- quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
+ quint32 count = w->d()->property()->count ? w->d()->property()->count(w->d()->property()) : 0;
if (arrayIndex < count) {
uint index = arrayIndex;
++arrayIndex;
if (attrs)
*attrs = QV4::Attr_Data;
- if (pd)
- pd->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), index));
+ if (pd) {
+ pd->value = QV4::QObjectWrapper::wrap(
+ w->engine(), w->d()->property()->at(w->d()->property(), index));
+ }
return PropertyKey::fromArrayIndex(index);
} else if (memberIndex == 0) {
++memberIndex;
@@ -194,7 +300,7 @@ ReturnedValue PropertyListPrototype::method_pop(const FunctionObject *b, const V
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -224,7 +330,7 @@ ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->append)
return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
if (!property->count)
@@ -247,7 +353,11 @@ ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const
property->append(property, argv[i].as<QV4::QObjectWrapper>()->object());
}
- return Encode(uint(length + argc));
+ const auto actualLength = property->count(property);
+ if (actualLength != length + argc)
+ qmlWarning(property->object) << "List didn't append all objects";
+
+ return Encode(uint(actualLength));
}
ReturnedValue PropertyListPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -260,7 +370,7 @@ ReturnedValue PropertyListPrototype::method_shift(const FunctionObject *b, const
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -294,7 +404,7 @@ ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, cons
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -386,7 +496,7 @@ ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, con
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -408,12 +518,16 @@ ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, con
for (int i = 0; i < argc; ++i)
property->append(property, nullptr);
+ if (property->count(property) != argc + len)
+ return scope.engine->throwTypeError(u"List doesn't append null objects"_s);
for (qsizetype k = len; k > 0; --k)
property->replace(property, k + argc - 1, property->at(property, k - 1));
- for (int i = 0; i < argc; ++i)
- property->replace(property, i, argv[i].as<QObjectWrapper>()->object());
+ for (int i = 0; i < argc; ++i) {
+ const auto *wrapper = argv[i].as<QObjectWrapper>();
+ property->replace(property, i, wrapper ? wrapper->object() : nullptr);
+ }
return Encode(uint(len + argc));
}
@@ -446,7 +560,7 @@ ReturnedValue firstOrLastIndexOf(const FunctionObject *b, const Value *thisObjec
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -532,7 +646,7 @@ ReturnedValue PropertyListPrototype::method_sort(const FunctionObject *b, const
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -568,7 +682,7 @@ ReturnedValue PropertyListPrototype::method_get_length(const FunctionObject *b,
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
if (!property->count)
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
@@ -590,7 +704,7 @@ ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b,
if (!w)
RETURN_UNDEFINED();
- QQmlListProperty<QObject> *property = &w->d()->property();
+ QQmlListProperty<QObject> *property = w->d()->property();
bool ok = false;
const uint newLength = argc ? argv[0].asArrayLength(&ok) : 0;
@@ -620,11 +734,18 @@ ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b,
}
if (!property->append)
- return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
+ return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
for (uint i = count; i < newLength; ++i)
property->append(property, nullptr);
+ count = property->count(property);
+ if (!qIsAtMostUintLimit(count))
+ return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
+
+ if (uint(count) != newLength)
+ return scope.engine->throwTypeError(u"List doesn't append null objects"_s);
+
return true;
}
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index 21240d6b98..55100e9a07 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -29,20 +29,33 @@ namespace QV4 {
namespace Heap {
-struct QmlListWrapper : Object {
- void init();
+struct QmlListWrapper : Object
+{
+ void init(QMetaType propertyType);
+ void init(QObject *object, int propertyId, QMetaType propertyType);
+ void init(QObject *object, const QQmlListProperty<QObject> &list, QMetaType propertyType);
void destroy();
- QV4QPointer<QObject> object;
- QQmlListProperty<QObject> &property() {
- return *reinterpret_cast<QQmlListProperty<QObject>*>(propertyData);
+ QObject *object() const { return m_object.data(); }
+ QMetaType propertyType() const { return QMetaType(m_propertyType); }
+
+ const QQmlListProperty<QObject> *property() const
+ {
+ return reinterpret_cast<const QQmlListProperty<QObject>*>(m_propertyData);
}
- // interface instead of QMetaType to keep class a POD
- const QtPrivate::QMetaTypeInterface *propertyType;
+ QQmlListProperty<QObject> *property()
+ {
+ return reinterpret_cast<QQmlListProperty<QObject>*>(m_propertyData);
+ }
private:
- void *propertyData[sizeof(QQmlListProperty<QObject>)/sizeof(void*)];
+ void *m_propertyData[sizeof(QQmlListProperty<QObject>)/sizeof(void*)];
+
+ QV4QPointer<QObject> m_object;
+
+ // interface instead of QMetaType to keep class a POD
+ const QtPrivate::QMetaTypeInterface *m_propertyType;
};
}
@@ -56,11 +69,13 @@ struct Q_QML_EXPORT QmlListWrapper : Object
static ReturnedValue create(ExecutionEngine *engine, QObject *object, int propId, QMetaType propType);
static ReturnedValue create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType);
+ static ReturnedValue create(ExecutionEngine *engine, QMetaType propType);
QVariant toVariant() const;
QQmlListReference toListReference() const;
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static qint64 virtualGetLength(const Managed *m);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
};
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index a7eeb3b4d7..3249f5a6eb 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -13,13 +13,12 @@
#include <private/qv4dateobject_p.h>
#include <private/qv4numberobject_p.h>
#include <private/qv4stringobject_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
QT_BEGIN_NAMESPACE
using namespace QV4;
-DEFINE_OBJECT_VTABLE(QQmlLocaleData);
-
#define THROW_ERROR(string) \
do { \
return scope.engine->throwError(QString::fromUtf8(string)); \
@@ -27,13 +26,18 @@ DEFINE_OBJECT_VTABLE(QQmlLocaleData);
#define GET_LOCALE_DATA_RESOURCE(OBJECT) \
- QV4::Scoped<QQmlLocaleData> r(scope, OBJECT.as<QQmlLocaleData>()); \
+ QLocale *r = [&]() { \
+ QV4::Scoped<QQmlValueTypeWrapper> r(scope, OBJECT.as<QQmlValueTypeWrapper>()); \
+ return r ? r->cast<QLocale>() : nullptr; \
+ }(); \
if (!r) \
THROW_ERROR("Not a valid Locale object")
static bool isLocaleObject(const QV4::Value &val)
{
- return val.as<QQmlLocaleData>();
+ if (const QV4::QQmlValueTypeWrapper *wrapper = val.as<QQmlValueTypeWrapper>())
+ return wrapper->type() == QMetaType::fromType<QLocale>();
+ return false;
}
//--------------
@@ -78,16 +82,16 @@ ReturnedValue QQmlDateExtension::method_toLocaleString(const QV4::FunctionObject
if (argc == 2) {
if (String *s = argv[1].stringValue()) {
QString format = s->toQString();
- formattedDt = r->d()->locale->toString(dt, format);
+ formattedDt = r->toString(dt, format);
} else if (argv[1].isNumber()) {
quint32 intFormat = argv[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedDt = r->d()->locale->toString(dt, format);
+ formattedDt = r->toString(dt, format);
} else {
THROW_ERROR("Locale: Date.toLocaleString(): Invalid datetime format");
}
} else {
- formattedDt = r->d()->locale->toString(dt, enumFormat);
+ formattedDt = r->toString(dt, enumFormat);
}
RETURN_RESULT(scope.engine->newString(formattedDt));
@@ -122,16 +126,16 @@ ReturnedValue QQmlDateExtension::method_toLocaleTimeString(const QV4::FunctionOb
if (argc == 2) {
if (String *s = argv[1].stringValue()) {
QString format = s->toQString();
- formattedTime = r->d()->locale->toString(time, format);
+ formattedTime = r->toString(time, format);
} else if (argv[1].isNumber()) {
quint32 intFormat = argv[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedTime = r->d()->locale->toString(time, format);
+ formattedTime = r->toString(time, format);
} else {
THROW_ERROR("Locale: Date.toLocaleTimeString(): Invalid time format");
}
} else {
- formattedTime = r->d()->locale->toString(time, enumFormat);
+ formattedTime = r->toString(time, enumFormat);
}
RETURN_RESULT(scope.engine->newString(formattedTime));
@@ -166,16 +170,16 @@ ReturnedValue QQmlDateExtension::method_toLocaleDateString(const QV4::FunctionOb
if (argc == 2) {
if (String *s = argv[1].stringValue()) {
QString format = s->toQString();
- formattedDate = r->d()->locale->toString(date, format);
+ formattedDate = r->toString(date, format);
} else if (argv[1].isNumber()) {
quint32 intFormat = argv[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedDate = r->d()->locale->toString(date, format);
+ formattedDate = r->toString(date, format);
} else {
THROW_ERROR("Locale: Date.loLocaleDateString(): Invalid date format");
}
} else {
- formattedDate = r->d()->locale->toString(date, enumFormat);
+ formattedDate = r->toString(date, enumFormat);
}
RETURN_RESULT(scope.engine->newString(formattedDate));
@@ -205,16 +209,16 @@ ReturnedValue QQmlDateExtension::method_fromLocaleString(const QV4::FunctionObje
if (argc == 3) {
if (String *s = argv[2].stringValue()) {
QString format = s->toQString();
- dt = r->d()->locale->toDateTime(dateString, format);
+ dt = r->toDateTime(dateString, format);
} else if (argv[2].isNumber()) {
quint32 intFormat = argv[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- dt = r->d()->locale->toDateTime(dateString, format);
+ dt = r->toDateTime(dateString, format);
} else {
THROW_ERROR("Locale: Date.fromLocaleString(): Invalid datetime format");
}
} else {
- dt = r->d()->locale->toDateTime(dateString, enumFormat);
+ dt = r->toDateTime(dateString, enumFormat);
}
RETURN_RESULT(engine->newDateObject(dt));
@@ -247,16 +251,16 @@ ReturnedValue QQmlDateExtension::method_fromLocaleTimeString(const QV4::Function
if (argc == 3) {
if (String *s = argv[2].stringValue()) {
QString format = s->toQString();
- tm = r->d()->locale->toTime(dateString, format);
+ tm = r->toTime(dateString, format);
} else if (argv[2].isNumber()) {
quint32 intFormat = argv[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- tm = r->d()->locale->toTime(dateString, format);
+ tm = r->toTime(dateString, format);
} else {
THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid datetime format");
}
} else {
- tm = r->d()->locale->toTime(dateString, enumFormat);
+ tm = r->toTime(dateString, enumFormat);
}
QDateTime dt;
@@ -293,16 +297,16 @@ ReturnedValue QQmlDateExtension::method_fromLocaleDateString(const QV4::Function
if (argc == 3) {
if (String *s = argv[2].stringValue()) {
QString format = s->toQString();
- dt = r->d()->locale->toDate(dateString, format);
+ dt = r->toDate(dateString, format);
} else if (argv[2].isNumber()) {
quint32 intFormat = argv[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- dt = r->d()->locale->toDate(dateString, format);
+ dt = r->toDate(dateString, format);
} else {
THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid datetime format");
}
} else {
- dt = r->d()->locale->toDate(dateString, enumFormat);
+ dt = r->toDate(dateString, enumFormat);
}
RETURN_RESULT(engine->newDateObject(dt.startOfDay(QTimeZone::UTC)));
@@ -363,7 +367,7 @@ QV4::ReturnedValue QQmlNumberExtension::method_toLocaleString(const QV4::Functio
prec = argv[2].toInt32();
}
- RETURN_RESULT(scope.engine->newString(r->d()->locale->toString(number, (char)format, prec)));
+ RETURN_RESULT(scope.engine->newString(r->toString(number, (char)format, prec)));
}
ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
@@ -392,7 +396,7 @@ ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(const QV4::Func
symbol = argv[1].toQStringNoThrow();
}
- RETURN_RESULT(scope.engine->newString(r->d()->locale->toCurrencyString(number, symbol)));
+ RETURN_RESULT(scope.engine->newString(r->toCurrencyString(number, symbol)));
}
ReturnedValue QQmlNumberExtension::method_fromLocaleString(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *argv, int argc)
@@ -409,7 +413,7 @@ ReturnedValue QQmlNumberExtension::method_fromLocaleString(const QV4::FunctionOb
THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments");
GET_LOCALE_DATA_RESOURCE(argv[0]);
- locale = *r->d()->locale;
+ locale = *r;
numberIdx = 1;
}
@@ -430,382 +434,161 @@ ReturnedValue QQmlNumberExtension::method_fromLocaleString(const QV4::FunctionOb
//--------------
// Locale object
-ReturnedValue QQmlLocaleData::method_get_firstDayOfWeek(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+void QQmlLocaleValueType::formattedDataSize(QQmlV4FunctionPtr args) const
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
- int fdow = int(locale->firstDayOfWeek());
- if (fdow == 7)
- fdow = 0; // Qt::Sunday = 7, but Sunday is 0 in JS Date
- RETURN_RESULT(fdow);
-}
+ QV4::Scope scope(args->v4engine());
+ const auto doThrow = [&](const QString &message) {
+ args->setReturnValue(scope.engine->throwError(message));
+ };
-ReturnedValue QQmlLocaleData::method_get_numberOptions(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) {
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
- int numberOptions = int(locale->numberOptions());
- RETURN_RESULT(numberOptions);
-}
+ const int argc = args->length();
-ReturnedValue QQmlLocaleData::method_set_numberOptions(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) {
- QV4::Scope scope(b);
- QLocale *locale = getThisLocale(scope, thisObject);
- int const numberOptions = argc ? int(argv[0].toNumber()) : QLocale::DefaultNumberOptions;
- locale->setNumberOptions(QLocale::NumberOptions {numberOptions});
- return Encode::undefined();
-}
+ if (argc < 1 || argc > 3) {
+ doThrow(QString::fromLatin1(
+ "Locale: formattedDataSize(): Expected 1-3 arguments, but received %1")
+ .arg(argc));
+ return;
+ }
-ReturnedValue QQmlLocaleData::method_get_formattedDataSize(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
-{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
+ QV4::ScopedValue arg0(scope, (*args)[0]);
+ bool mismatched0 = false;
+ if (!arg0->isNumber()) {
+ // ### Qt7: Throw an exception here, so that we don't have to handle mismatched0 below.
+ qWarning() << "Locale: formattedDataSize(): Invalid argument ('bytes' should be a number)";
+ if (argc == 1) {
+ args->setReturnValue(
+ scope.engine->newString(locale.formattedDataSize(qint64(arg0->toInteger())))
+ ->asReturnedValue());
+ return;
+ }
- if (argc < 1 || argc > 3) {
- THROW_ERROR(QString::fromLatin1(
- "Locale: formattedDataSize(): Expected 1-3 arguments, but received %1").arg(argc).toLatin1());
+ mismatched0 = true;
}
- const qint64 bytes = static_cast<qint64>(argv[0].toInteger());
- if (argc == 1)
- RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes)));
+ // Anything can be coerced to a number, for better or worse ...
+ Q_ASSERT(argc >= 2);
- int precision = 0;
- if (argc >= 2) {
- if (!argv[1].isInteger())
- THROW_ERROR("Locale: formattedDataSize(): Invalid argument ('precision' must be an int)");
+ QV4::ScopedValue arg1(scope, (*args)[1]);
+ if (!arg1->isInteger()) {
+ doThrow(QLatin1String(
+ "Locale: formattedDataSize(): Invalid argument ('precision' must be an int)"));
+ return;
+ }
+
+ if (mismatched0) {
+ if (argc == 2) {
+ const QString result = locale.formattedDataSize(
+ qint64(arg0->toInteger()), arg1->integerValue());
+ args->setReturnValue(scope.engine->newString(result)->asReturnedValue());
+ return;
+ }
- precision = argv[1].toInt32();
- if (argc == 2)
- RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes, precision)));
+ QV4::ScopedValue arg2(scope, (*args)[2]);
+ if (arg2->isNumber()) {
+ const QString result = locale.formattedDataSize(
+ qint64(arg0->toInteger()), arg1->integerValue(),
+ QLocale::DataSizeFormats(arg2->integerValue()));
+ args->setReturnValue(scope.engine->newString(result)->asReturnedValue());
+ return;
+ }
}
- // argc >= 3
- if (!argv[2].isNumber())
- THROW_ERROR("Locale: formattedDataSize(): Invalid argument ('format' must be DataSizeFormat)");
+ Q_ASSERT(argc == 3);
+ Q_ASSERT(!QV4::ScopedValue(scope, (*args)[2])->isNumber());
- const quint32 intFormat = argv[2].toUInt32();
- const auto format = QLocale::DataSizeFormats(intFormat);
- RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes, precision, format)));
+ doThrow(QLatin1String(
+ "Locale: formattedDataSize(): Invalid argument ('format' must be DataSizeFormat)"));
}
-ReturnedValue QQmlLocaleData::method_get_measurementSystem(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+static QQmlLocale::DayOfWeek qtDayToQmlDay(Qt::DayOfWeek day)
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
- return QV4::Encode(locale->measurementSystem());
+ return day == Qt::Sunday ? QQmlLocale::DayOfWeek::Sunday : QQmlLocale::DayOfWeek(day);
}
-ReturnedValue QQmlLocaleData::method_get_textDirection(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+QQmlLocale::DayOfWeek QQmlLocaleValueType::firstDayOfWeek() const
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
-
- return QV4::Encode(locale->textDirection());
+ return qtDayToQmlDay(locale.firstDayOfWeek());
}
-ReturnedValue QQmlLocaleData::method_get_weekDays(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+QList<QQmlLocale::DayOfWeek> QQmlLocaleValueType::weekDays() const
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
-
- QList<Qt::DayOfWeek> days = locale->weekdays();
-
- QV4::ScopedArrayObject result(scope, scope.engine->newArrayObject());
- result->arrayReserve(days.size());
- for (int i = 0; i < days.size(); ++i) {
- int day = days.at(i);
- if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday)
- day = 0;
- result->arrayPut(i, QV4::Value::fromInt32(day));
- }
- result->setArrayLengthUnchecked(days.size());
-
- return result.asReturnedValue();
+ const QList<Qt::DayOfWeek> days = locale.weekdays();
+ QList<QQmlLocale::DayOfWeek> result;
+ result.reserve(days.size());
+ for (Qt::DayOfWeek day : days)
+ result.append(qtDayToQmlDay(day));
+ return result;
}
-ReturnedValue QQmlLocaleData::method_get_uiLanguages(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+void QQmlLocaleValueType::toString(QQmlV4FunctionPtr args) const
{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
+ Scope scope(args->v4engine());
+ const auto doThrow = [&](const QString &message) {
+ args->setReturnValue(scope.engine->throwError(message));
+ };
- QStringList langs = locale->uiLanguages();
- QV4::ScopedArrayObject result(scope, scope.engine->newArrayObject());
- result->arrayReserve(langs.size());
- QV4::ScopedValue v(scope);
- for (int i = 0; i < langs.size(); ++i)
- result->arrayPut(i, (v = scope.engine->newString(langs.at(i))));
+ const int argc = args->length();
- result->setArrayLengthUnchecked(langs.size());
-
- return result.asReturnedValue();
-}
+ // toString()
+ Q_ASSERT(argc > 0);
-ReturnedValue QQmlLocaleData::method_currencySymbol(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
-{
- QV4::Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
-
- if (argc > 1)
- THROW_ERROR("Locale: currencySymbol(): Invalid arguments");
-
- QLocale::CurrencySymbolFormat format = QLocale::CurrencySymbol;
- if (argc == 1) {
- quint32 intFormat = argv[0].toNumber();
- format = QLocale::CurrencySymbolFormat(intFormat);
+ if (argc > 3) {
+ doThrow(QString::fromLatin1("Locale: toString(): Expected 1-3 arguments, but received %1")
+ .arg(argc));
+ return;
}
- RETURN_RESULT(scope.engine->newString(locale->currencySymbol(format)));
-}
-
-#define LOCALE_FORMAT(FUNC) \
-ReturnedValue QQmlLocaleData::method_ ##FUNC (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) { \
- QV4::Scope scope(b); \
- const QLocale *locale = getThisLocale(scope, thisObject); \
- if (!locale) \
- return Encode::undefined(); \
- if (argc > 1) \
- THROW_ERROR("Locale: " #FUNC "(): Invalid arguments"); \
- QLocale::FormatType format = QLocale::LongFormat;\
- if (argc == 1) { \
- quint32 intFormat = argv[0].toUInt32(); \
- format = QLocale::FormatType(intFormat); \
- } \
- RETURN_RESULT(scope.engine->newString(locale-> FUNC (format))); \
-}
+ QV4::ScopedValue arg0(scope, (*args)[0]);
+ if (arg0->isNumber()) {
-LOCALE_FORMAT(dateTimeFormat)
-LOCALE_FORMAT(timeFormat)
-LOCALE_FORMAT(dateFormat)
-
-// +1 added to idx because JS is 0-based, whereas QLocale months begin at 1.
-#define LOCALE_FORMATTED_MONTHNAME(VARIABLE) \
-ReturnedValue QQmlLocaleData::method_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) {\
- Scope scope(b); \
- const QLocale *locale = getThisLocale(scope, thisObject); \
- if (!locale) \
- return Encode::undefined(); \
- if (argc < 1 || argc > 2) \
- THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \
- QLocale::FormatType enumFormat = QLocale::LongFormat; \
- int idx = argv[0].toInt32() + 1; \
- if (idx < 1 || idx > 12) \
- THROW_ERROR("Locale: Invalid month"); \
- QString name; \
- if (argc == 2) { \
- if (argv[1].isNumber()) { \
- quint32 intFormat = argv[1].toUInt32(); \
- QLocale::FormatType format = QLocale::FormatType(intFormat); \
- name = locale-> VARIABLE(idx, format); \
- } else { \
- THROW_ERROR("Locale: Invalid datetime format"); \
- } \
- } else { \
- name = locale-> VARIABLE(idx, enumFormat); \
- } \
- RETURN_RESULT(scope.engine->newString(name)); \
-}
+ // toString(int)
+ // toString(double)
+ Q_ASSERT(argc != 1);
-// 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date
-#define LOCALE_FORMATTED_DAYNAME(VARIABLE) \
-ReturnedValue QQmlLocaleData::method_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) {\
- Scope scope(b); \
- const QLocale *locale = getThisLocale(scope, thisObject); \
- if (!locale) \
- return Encode::undefined(); \
- if (argc < 1 || argc > 2) \
- THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \
- QLocale::FormatType enumFormat = QLocale::LongFormat; \
- int idx = argv[0].toInt32(); \
- if (idx < 0 || idx > 7) \
- THROW_ERROR("Locale: Invalid day"); \
- if (idx == 0) idx = 7; \
- QString name; \
- if (argc == 2) { \
- if (argv[1].isNumber()) { \
- quint32 intFormat = argv[1].toUInt32(); \
- QLocale::FormatType format = QLocale::FormatType(intFormat); \
- name = locale-> VARIABLE(idx, format); \
- } else { \
- THROW_ERROR("Locale: Invalid datetime format"); \
- } \
- } else { \
- name = locale-> VARIABLE(idx, enumFormat); \
- } \
- RETURN_RESULT(scope.engine->newString(name)); \
-}
+ QV4::ScopedValue arg1(scope, (*args)[1]);
+ if (!arg1->isString()) {
+ doThrow(QLatin1String("Locale: the second argument to the toString overload "
+ "whose first argument is a double should be a char"));
+ return;
+ }
-LOCALE_FORMATTED_MONTHNAME(monthName)
-LOCALE_FORMATTED_MONTHNAME(standaloneMonthName)
-LOCALE_FORMATTED_DAYNAME(dayName)
-LOCALE_FORMATTED_DAYNAME(standaloneDayName)
+ // toString(double, const QString &)
+ // toString(double, const QString &, int)
+ Q_ASSERT(argc == 3);
+ Q_ASSERT(!QV4::ScopedValue(scope, (*args)[2])->isInteger());
-ReturnedValue QQmlLocaleData::method_toString(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
-{
- Scope scope(b);
- const QLocale *locale = getThisLocale(scope, thisObject);
- if (!locale)
- return Encode::undefined();
-
- if (argc == 0) {
- // As a special (undocumented) case, when called with no arguments,
- // just forward to QDebug. This makes it consistent with other types
- // in JS that can be converted to a string via toString().
- RETURN_RESULT(scope.engine->newString(QDebug::toString(*locale)));
+ doThrow(QLatin1String("Locale: the third argument to the toString overload "
+ "whose first argument is a double should be an int"));
+ return;
}
- if (argc > 3) {
- return scope.engine->throwError(QString::fromLatin1(
- "Locale: toString(): Expected 1-3 arguments, but received %1").arg(argc));
- }
-
- if (argv[0].isNumber()) {
- if (argv[0].isInteger()) {
- // toString(int)
- RETURN_RESULT(scope.engine->newString(locale->toString(argv[0].toInt32())));
- } else {
- // toString(double[, char][, int])
- const double number = argv[0].toNumber();
- if (argc == 1)
- RETURN_RESULT(scope.engine->newString(locale->toString(number)));
-
- if (!argv[1].isString()) {
- THROW_ERROR("Locale: the second argument to the toString overload "
- "whose first argument is a double should be a char");
- }
- const char format = argv[1].toQString().at(0).toLatin1();
-
- switch (argc) {
- case 2:
- RETURN_RESULT(scope.engine->newString(locale->toString(number, format)));
- case 3:
- if (!argv[2].isInteger()) {
- THROW_ERROR("Locale: the third argument to the toString overload "
- "whose first argument is a double should be an int");
- }
-
- const int precision = argv[2].toInt32();
- RETURN_RESULT(scope.engine->newString(locale->toString(number, format, precision)));
- }
- }
- } else if (const DateObject *dateObject = argv[0].as<DateObject>()) {
- // toString(Date, string) or toString(Date[, FormatType])
+ if (arg0->as<DateObject>()) {
if (argc > 2) {
- return scope.engine->throwError(QString::fromLatin1(
- "Locale: the toString() overload that takes a Date as its first "
- "argument expects 1 or 2 arguments, but received %1").arg(argc));
- }
-
- if (argc == 2 && argv[1].isString()) {
- RETURN_RESULT(scope.engine->newString(locale->toString(
- dateObject->toQDateTime(), argv[1].toQString())));
- }
-
- if (argc == 2 && !argv[1].isNumber()) {
- THROW_ERROR("Locale: the second argument to the toString overloads whose "
- "first argument is a Date should be a string or FormatType");
+ doThrow(QString::fromLatin1(
+ "Locale: the toString() overload that takes a Date as its first "
+ "argument expects 1 or 2 arguments, but received %1").arg(argc));
+ return;
}
- const QLocale::FormatType format = argc == 2
- ? QLocale::FormatType(argv[1].toNumber()) : QLocale::LongFormat;
- RETURN_RESULT(scope.engine->newString(locale->toString(dateObject->toQDateTime(), format)));
- }
+ // toString(QDateTime)
+ Q_ASSERT(argc == 2);
+ QV4::ScopedValue arg1(scope, (*args)[1]);
- THROW_ERROR("Locale: toString() expects either an int, double, or Date as its first argument");
-}
+ // toString(QDateTime, QString)
+ Q_ASSERT(!arg1->isString());
-#define LOCALE_STRING_PROPERTY(VARIABLE) \
-ReturnedValue QQmlLocaleData::method_get_ ## VARIABLE (const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) \
-{ \
- Scope scope(b); \
- const QLocale *locale = getThisLocale(scope, thisObject); \
- if (!locale) \
- return Encode::undefined(); \
- RETURN_RESULT(scope.engine->newString(locale-> VARIABLE()));\
-}
-
-LOCALE_STRING_PROPERTY(name)
-LOCALE_STRING_PROPERTY(nativeLanguageName)
-QT_IGNORE_DEPRECATIONS(LOCALE_STRING_PROPERTY(nativeCountryName))
-LOCALE_STRING_PROPERTY(nativeTerritoryName)
-LOCALE_STRING_PROPERTY(decimalPoint)
-LOCALE_STRING_PROPERTY(groupSeparator)
-LOCALE_STRING_PROPERTY(percent)
-LOCALE_STRING_PROPERTY(zeroDigit)
-LOCALE_STRING_PROPERTY(negativeSign)
-LOCALE_STRING_PROPERTY(positiveSign)
-LOCALE_STRING_PROPERTY(exponential)
-LOCALE_STRING_PROPERTY(amText)
-LOCALE_STRING_PROPERTY(pmText)
-
-class QV4LocaleDataDeletable : public QV4::ExecutionEngine::Deletable
-{
-public:
- QV4LocaleDataDeletable(QV4::ExecutionEngine *engine);
- ~QV4LocaleDataDeletable();
+ // toString(QDateTime, QLocale::FormatType)
+ Q_ASSERT(!arg1->isNumber());
- QV4::PersistentValue prototype;
-};
+ doThrow(QLatin1String("Locale: the second argument to the toString overloads whose "
+ "first argument is a Date should be a string or FormatType"));
+ return;
+ }
-QV4LocaleDataDeletable::QV4LocaleDataDeletable(QV4::ExecutionEngine *engine)
-{
- QV4::Scope scope(engine);
- QV4::Scoped<QV4::Object> o(scope, engine->newObject());
-
- o->defineDefaultProperty(QStringLiteral("dateFormat"), QQmlLocaleData::method_dateFormat, 0);
- o->defineDefaultProperty(QStringLiteral("standaloneDayName"), QQmlLocaleData::method_standaloneDayName, 0);
- o->defineDefaultProperty(QStringLiteral("standaloneMonthName"), QQmlLocaleData::method_standaloneMonthName, 0);
- o->defineDefaultProperty(QStringLiteral("dayName"), QQmlLocaleData::method_dayName, 0);
- o->defineDefaultProperty(QStringLiteral("timeFormat"), QQmlLocaleData::method_timeFormat, 0);
- o->defineDefaultProperty(QStringLiteral("monthName"), QQmlLocaleData::method_monthName, 0);
- o->defineDefaultProperty(QStringLiteral("toString"), QQmlLocaleData::method_toString, 0);
- o->defineDefaultProperty(QStringLiteral("currencySymbol"), QQmlLocaleData::method_currencySymbol, 0);
- o->defineDefaultProperty(QStringLiteral("dateTimeFormat"), QQmlLocaleData::method_dateTimeFormat, 0);
- o->defineDefaultProperty(QStringLiteral("formattedDataSize"), QQmlLocaleData::method_get_formattedDataSize, 0);
- o->defineAccessorProperty(QStringLiteral("name"), QQmlLocaleData::method_get_name, nullptr);
- o->defineAccessorProperty(QStringLiteral("positiveSign"), QQmlLocaleData::method_get_positiveSign, nullptr);
- o->defineAccessorProperty(QStringLiteral("uiLanguages"), QQmlLocaleData::method_get_uiLanguages, nullptr);
- o->defineAccessorProperty(QStringLiteral("firstDayOfWeek"), QQmlLocaleData::method_get_firstDayOfWeek, nullptr);
- o->defineAccessorProperty(QStringLiteral("pmText"), QQmlLocaleData::method_get_pmText, nullptr);
- o->defineAccessorProperty(QStringLiteral("percent"), QQmlLocaleData::method_get_percent, nullptr);
- o->defineAccessorProperty(QStringLiteral("textDirection"), QQmlLocaleData::method_get_textDirection, nullptr);
- o->defineAccessorProperty(QStringLiteral("weekDays"), QQmlLocaleData::method_get_weekDays, nullptr);
- o->defineAccessorProperty(QStringLiteral("negativeSign"), QQmlLocaleData::method_get_negativeSign, nullptr);
- o->defineAccessorProperty(QStringLiteral("groupSeparator"), QQmlLocaleData::method_get_groupSeparator, nullptr);
- o->defineAccessorProperty(QStringLiteral("decimalPoint"), QQmlLocaleData::method_get_decimalPoint, nullptr);
- o->defineAccessorProperty(QStringLiteral("nativeLanguageName"), QQmlLocaleData::method_get_nativeLanguageName, nullptr);
- o->defineAccessorProperty(QStringLiteral("nativeCountryName"), QQmlLocaleData::method_get_nativeCountryName, nullptr);
- o->defineAccessorProperty(QStringLiteral("nativeTerritoryName"), QQmlLocaleData::method_get_nativeTerritoryName, nullptr);
- o->defineAccessorProperty(QStringLiteral("zeroDigit"), QQmlLocaleData::method_get_zeroDigit, nullptr);
- o->defineAccessorProperty(QStringLiteral("amText"), QQmlLocaleData::method_get_amText, nullptr);
- o->defineAccessorProperty(QStringLiteral("measurementSystem"), QQmlLocaleData::method_get_measurementSystem, nullptr);
- o->defineAccessorProperty(QStringLiteral("exponential"), QQmlLocaleData::method_get_exponential, nullptr);
- o->defineAccessorProperty(QStringLiteral("numberOptions"), QQmlLocaleData::method_get_numberOptions, QQmlLocaleData::method_set_numberOptions);
-
- prototype.set(engine, o);
+ doThrow(QLatin1String("Locale: toString() expects either an int, double, "
+ "or Date as its first argument"));
}
-QV4LocaleDataDeletable::~QV4LocaleDataDeletable()
-{
-}
-
-V4_DEFINE_EXTENSION(QV4LocaleDataDeletable, localeV4Data);
-
/*!
\qmltype Locale
//! \instantiates QQmlLocale
@@ -862,19 +645,17 @@ V4_DEFINE_EXTENSION(QV4LocaleDataDeletable, localeV4Data);
can use the following enumeration values to specify the formatting of
the string representation for a Date object.
- \list
- \li Locale.LongFormat The long version of day and month names; for
- example, returning "January" as a month name.
- \li Locale.ShortFormat The short version of day and month names; for
- example, returning "Jan" as a month name.
- \li Locale.NarrowFormat A special version of day and month names for
- use when space is limited; for example, returning "J" as a month
- name. Note that the narrow format might contain the same text for
- different months and days or it can even be an empty string if the
- locale doesn't support narrow names, so you should avoid using it
- for date formatting. Also, for the system locale this format is
- the same as ShortFormat.
- \endlist
+ \value Locale.LongFormat The long version of day and month names; for
+ example, returning "January" as a month name.
+ \value Locale.ShortFormat The short version of day and month names; for
+ example, returning "Jan" as a month name.
+ \value Locale.NarrowFormat A special version of day and month names for
+ use when space is limited; for example, returning "J" as a month
+ name. Note that the narrow format might contain the same text for
+ different months and days or it can even be an empty string if the
+ locale doesn't support narrow names, so you should avoid using it
+ for date formatting. Also, for the system locale this format is
+ the same as ShortFormat.
Additionally the double-to-string and string-to-double conversion functions are
@@ -902,21 +683,16 @@ V4_DEFINE_EXTENSION(QV4LocaleDataDeletable, localeV4Data);
QV4::ReturnedValue QQmlLocale::locale(ExecutionEngine *engine, const QString &localeName)
{
- QLocale qlocale;
- if (!localeName.isEmpty())
- qlocale = QLocale(localeName);
- return wrap(engine, qlocale);
-}
+ if (localeName.isEmpty()) {
+ return QQmlValueTypeWrapper::create(
+ engine, nullptr, &QQmlLocaleValueType::staticMetaObject,
+ QMetaType::fromType<QLocale>());
+ }
-QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale)
-{
- QV4::Scope scope(v4);
- QV4LocaleDataDeletable *d = localeV4Data(scope.engine);
- QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocate<QQmlLocaleData>());
- *wrapper->d()->locale = locale;
- QV4::ScopedObject p(scope, d->prototype.value());
- wrapper->setPrototypeOf(p);
- return wrapper.asReturnedValue();
+ QLocale qlocale(localeName);
+ return QQmlValueTypeWrapper::create(
+ engine, &qlocale, &QQmlLocaleValueType::staticMetaObject,
+ QMetaType::fromType<QLocale>());
}
void QQmlLocale::registerStringLocaleCompare(QV4::ExecutionEngine *engine)
@@ -941,10 +717,10 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
/*!
\qmlproperty string QtQml::Locale::name
- Holds the language and country of this locale as a
- string of the form "language_country", where
+ Holds the language and territory of this locale as a
+ string of the form "language_territory", where
language is a lowercase, two-letter ISO 639 language code,
- and country is an uppercase, two- or three-letter ISO 3166 country code.
+ and territory is an uppercase, two- or three-letter ISO 3166 territory code.
*/
/*!
@@ -1096,15 +872,13 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
Holds the first day of the week according to the current locale.
- \list
- \li Locale.Sunday = 0
- \li Locale.Monday = 1
- \li Locale.Tuesday = 2
- \li Locale.Wednesday = 3
- \li Locale.Thursday = 4
- \li Locale.Friday = 5
- \li Locale.Saturday = 6
- \endlist
+ \value Locale.Sunday 0
+ \value Locale.Monday 1
+ \value Locale.Tuesday 2
+ \value Locale.Wednesday 3
+ \value Locale.Thursday 4
+ \value Locale.Friday 5
+ \value Locale.Saturday 6
\note that these values match the JS Date API which is different
from the Qt C++ API where Qt::Sunday = 7.
@@ -1180,10 +954,9 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
\qmlproperty enumeration QtQml::Locale::textDirection
Holds the text direction of the language:
- \list
- \li Qt.LeftToRight
- \li Qt.RightToLeft
- \endlist
+
+ \value Qt.LeftToRight Text normally begins at the left side.
+ \value Qt.RightToLeft Text normally begins at the right side.
*/
/*!
@@ -1202,11 +975,11 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
\qmlmethod string QtQml::Locale::currencySymbol(format)
Returns the currency symbol for the specified \a format:
- \list
- \li Locale.CurrencyIsoCode a ISO-4217 code of the currency.
- \li Locale.CurrencySymbol a currency symbol.
- \li Locale.CurrencyDisplayName a user readable name of the currency.
- \endlist
+
+ \value Locale.CurrencyIsoCode a ISO-4217 code of the currency.
+ \value Locale.CurrencySymbol a currency symbol.
+ \value Locale.CurrencyDisplayName a user readable name of the currency.
+
\sa Number::toLocaleCurrencyString()
*/
@@ -1216,7 +989,7 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
Holds a native name of the language for the locale. For example
"Schwiizertüütsch" for Swiss-German locale.
- \sa nativeCountryName
+ \sa nativeTerritoryName
*/
/*!
@@ -1243,16 +1016,13 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
This property defines which units are used for measurement.
- \list
- \li Locale.MetricSystem This value indicates metric units, such as meters,
- centimeters and millimeters.
- \li Locale.ImperialUSSystem This value indicates imperial units, such as
- inches and miles as they are used in the United States.
- \li Locale.ImperialUKSystem This value indicates imperial units, such as
- inches and miles as they are used in the United Kingdom.
- \li Locale.ImperialSystem Provided for compatibility. The same as
- Locale.ImperialUSSystem.
- \endlist
+ \value Locale.MetricSystem This value indicates metric units, such as meters,
+ centimeters and millimeters.
+ \value Locale.ImperialUSSystem This value indicates imperial units, such as
+ inches and miles as they are used in the United States.
+ \value Locale.ImperialUKSystem This value indicates imperial units, such as
+ inches and miles as they are used in the United Kingdom.
+ \value Locale.ImperialSystem Provided for compatibility. The same as Locale.ImperialUSSystem.
*/
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h
index 7a497db523..5d26bf8a68 100644
--- a/src/qml/qml/qqmllocale_p.h
+++ b/src/qml/qml/qqmllocale_p.h
@@ -57,7 +57,7 @@ private:
namespace QQmlLocale
{
- Q_NAMESPACE_EXPORT(Q_QML_PRIVATE_EXPORT)
+ Q_NAMESPACE_EXPORT(Q_QML_EXPORT)
QML_NAMED_ELEMENT(Locale)
QML_ADDED_IN_VERSION(2, 2)
QML_NAMESPACE_EXTENDED(QLocale)
@@ -74,79 +74,172 @@ namespace QQmlLocale
};
Q_ENUM_NS(DayOfWeek)
- Q_QML_PRIVATE_EXPORT QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName);
- Q_QML_PRIVATE_EXPORT QV4::ReturnedValue wrap(QV4::ExecutionEngine *engine, const QLocale &locale);
- Q_QML_PRIVATE_EXPORT void registerStringLocaleCompare(QV4::ExecutionEngine *engine);
- Q_QML_PRIVATE_EXPORT QV4::ReturnedValue method_localeCompare(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ Q_QML_EXPORT QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName);
+ Q_QML_EXPORT void registerStringLocaleCompare(QV4::ExecutionEngine *engine);
+ Q_QML_EXPORT QV4::ReturnedValue method_localeCompare(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
-namespace QV4 {
+struct DayOfWeekList
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QList<QQmlLocale::DayOfWeek>)
+ QML_SEQUENTIAL_CONTAINER(QQmlLocale::DayOfWeek)
+};
+
+class QQmlLocaleValueType
+{
+ QLocale locale;
+
+ Q_PROPERTY(QQmlLocale::DayOfWeek firstDayOfWeek READ firstDayOfWeek CONSTANT)
+ Q_PROPERTY(QLocale::MeasurementSystem measurementSystem READ measurementSystem CONSTANT)
+ Q_PROPERTY(Qt::LayoutDirection textDirection READ textDirection CONSTANT)
+ Q_PROPERTY(QList<QQmlLocale::DayOfWeek> weekDays READ weekDays CONSTANT)
+ Q_PROPERTY(QStringList uiLanguages READ uiLanguages CONSTANT)
+
+ Q_PROPERTY(QString name READ name CONSTANT)
+ Q_PROPERTY(QString nativeLanguageName READ nativeLanguageName CONSTANT)
+#if QT_DEPRECATED_SINCE(6, 6)
+ Q_PROPERTY(QString nativeCountryName READ nativeCountryName CONSTANT)
+#endif
+ Q_PROPERTY(QString nativeTerritoryName READ nativeTerritoryName CONSTANT)
+ Q_PROPERTY(QString decimalPoint READ decimalPoint CONSTANT)
+ Q_PROPERTY(QString groupSeparator READ groupSeparator CONSTANT)
+ Q_PROPERTY(QString percent READ percent CONSTANT)
+ Q_PROPERTY(QString zeroDigit READ zeroDigit CONSTANT)
+ Q_PROPERTY(QString negativeSign READ negativeSign CONSTANT)
+ Q_PROPERTY(QString positiveSign READ positiveSign CONSTANT)
+ Q_PROPERTY(QString exponential READ exponential CONSTANT)
+ Q_PROPERTY(QString amText READ amText CONSTANT)
+ Q_PROPERTY(QString pmText READ pmText CONSTANT)
+
+ Q_PROPERTY(QLocale::NumberOptions numberOptions READ numberOptions WRITE setNumberOptions)
+
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QLocale)
+ QML_EXTENDED(QQmlLocaleValueType)
+ QML_CONSTRUCTIBLE_VALUE
-namespace Heap {
+public:
+ Q_INVOKABLE QQmlLocaleValueType(const QString &name) : locale(name) {}
-struct QQmlLocaleData : Object {
- inline void init() { locale = new QLocale; }
- void destroy() {
- delete locale;
- Object::destroy();
+ Q_INVOKABLE QString currencySymbol(
+ QLocale::CurrencySymbolFormat format = QLocale::CurrencySymbol) const
+ {
+ return locale.currencySymbol(format);
+ }
+
+ Q_INVOKABLE QString dateTimeFormat(QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ return locale.dateTimeFormat(format);
}
- QLocale *locale;
-};
-}
+ Q_INVOKABLE QString timeFormat(QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ return locale.timeFormat(format);
+ }
-struct QQmlLocaleData : public QV4::Object
-{
- V4_OBJECT2(QQmlLocaleData, Object)
- V4_NEEDS_DESTROY
-
- static QLocale *getThisLocale(QV4::Scope &scope, const QV4::Value *thisObject) {
- const QV4::Object *o = thisObject->as<Object>();
- const QQmlLocaleData *data = o ? o->as<QQmlLocaleData>() : nullptr;
- if (!data) {
- scope.engine->throwTypeError();
- return nullptr;
- }
- return data->d()->locale;
+ Q_INVOKABLE QString dateFormat(QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ return locale.dateFormat(format);
}
- static QV4::ReturnedValue method_currencySymbol(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_dateTimeFormat(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_timeFormat(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_dateFormat(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_monthName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_standaloneMonthName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_dayName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_standaloneDayName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_toString(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- static QV4::ReturnedValue method_get_firstDayOfWeek(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_measurementSystem(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_textDirection(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_weekDays(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_uiLanguages(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- static QV4::ReturnedValue method_get_name(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_nativeLanguageName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_nativeCountryName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_nativeTerritoryName(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_decimalPoint(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_groupSeparator(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_percent(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_zeroDigit(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_negativeSign(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_positiveSign(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_exponential(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_amText(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_get_pmText(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- static QV4::ReturnedValue method_get_numberOptions(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue method_set_numberOptions(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- static QV4::ReturnedValue method_get_formattedDataSize(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-};
+ Q_INVOKABLE QString monthName(int index, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ // +1 added to idx because JS is 0-based, whereas QLocale months begin at 1.
+ return locale.monthName(index + 1, format);
+ }
+
+ Q_INVOKABLE QString standaloneMonthName(
+ int index, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ // +1 added to idx because JS is 0-based, whereas QLocale months begin at 1.
+ return locale.standaloneMonthName(index + 1, format);
+ }
+
+ Q_INVOKABLE QString dayName(int index, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ // 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date
+ return locale.dayName(index == 0 ? 7 : index, format);
+ }
+
+ Q_INVOKABLE QString standaloneDayName(
+ int index, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ // 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date
+ return locale.standaloneDayName(index == 0 ? 7 : index, format);
+ }
+
+ Q_INVOKABLE void formattedDataSize(QQmlV4FunctionPtr args) const;
+ Q_INVOKABLE QString formattedDataSize(
+ double bytes, int precision = 2,
+ QLocale::DataSizeFormats format = QLocale::DataSizeIecFormat) const
+ {
+ return locale.formattedDataSize(
+ qint64(QV4::Value::toInteger(bytes)), precision, format);
+ }
+
+ Q_INVOKABLE void toString(QQmlV4FunctionPtr args) const;
+
+ // As a special (undocumented) case, when called with no arguments,
+ // just forward to QDebug. This makes it consistent with other types
+ // in JS that can be converted to a string via toString().
+ Q_INVOKABLE QString toString() const { return QDebug::toString(locale); }
+
+ Q_INVOKABLE QString toString(int i) const { return locale.toString(i); }
+ Q_INVOKABLE QString toString(double f) const
+ {
+ return QJSNumberCoercion::isInteger(f) ? toString(int(f)) : locale.toString(f);
+ }
+ Q_INVOKABLE QString toString(double f, const QString &format, int precision = 6) const
+ {
+ // Lacking a char type, we have to use QString here
+ return format.length() < 1
+ ? QString()
+ : locale.toString(f, format.at(0).toLatin1(), precision);
+ }
+ Q_INVOKABLE QString toString(const QDateTime &dateTime, const QString &format) const
+ {
+ return locale.toString(dateTime, format);
+ }
+ Q_INVOKABLE QString toString(
+ const QDateTime &dateTime, QLocale::FormatType format = QLocale::LongFormat) const
+ {
+ return locale.toString(dateTime, format);
+ }
-}
+ QQmlLocale::DayOfWeek firstDayOfWeek() const;
+ QLocale::MeasurementSystem measurementSystem() const { return locale.measurementSystem(); }
+ Qt::LayoutDirection textDirection() const { return locale.textDirection(); }
+ QList<QQmlLocale::DayOfWeek> weekDays() const;
+ QStringList uiLanguages() const { return locale.uiLanguages(); }
+
+ QString name() const { return locale.name(); }
+ QString nativeLanguageName() const { return locale.nativeLanguageName(); }
+#if QT_DEPRECATED_SINCE(6, 6)
+ QString nativeCountryName() const
+ {
+ QT_IGNORE_DEPRECATIONS(return locale.nativeCountryName();)
+ }
+#endif
+ QString nativeTerritoryName() const { return locale.nativeTerritoryName(); }
+ QString decimalPoint() const { return locale.decimalPoint(); }
+ QString groupSeparator() const { return locale.groupSeparator(); }
+ QString percent() const { return locale.percent(); }
+ QString zeroDigit() const { return locale.zeroDigit(); }
+ QString negativeSign() const { return locale.negativeSign(); }
+ QString positiveSign() const { return locale.positiveSign(); }
+ QString exponential() const { return locale.exponential(); }
+ QString amText() const { return locale.amText(); }
+ QString pmText() const { return locale.pmText(); }
+
+ QLocale::NumberOptions numberOptions() const { return locale.numberOptions(); }
+ void setNumberOptions(const QLocale::NumberOptions &numberOptions)
+ {
+ locale.setNumberOptions(numberOptions);
+ }
+};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp
index d735ecd716..8d7fd6c04d 100644
--- a/src/qml/qml/qqmlloggingcategory.cpp
+++ b/src/qml/qml/qqmlloggingcategory.cpp
@@ -15,11 +15,11 @@
\since 5.8
A logging category can be passed to console.log() and friends as the first argument.
- If supplied to to the logger the LoggingCategory's name will be used as Logging Category
- otherwise the default logging category will be used.
+ If supplied to the logger the LoggingCategory's name will be used as logging category.
+ Otherwise the default logging category will be used.
\qml
- import QtQuick 2.8
+ import QtQuick
Item {
LoggingCategory {
@@ -29,11 +29,18 @@
}
Component.onCompleted: {
- console.log(category, "message");
+ console.log(category, "log message");
+ console.warn(category, "warning message");
}
}
\endqml
+ By default this outputs only \c{com.qt.category: warning message}. The
+ \c{log message} is suppressed due to the \l{defaultLogLevel}. You can,
+ however, configure log levels for QML logging categories the same way
+ you can configure them for
+ \l{QLoggingCategory#configuring-categories}{QLoggingCategory}.
+
\note As the creation of objects is expensive, it is encouraged to put the needed
LoggingCategory definitions into a singleton and import this where needed.
@@ -58,8 +65,21 @@
Holds the default log level of the logging category. By default it is
created with the LoggingCategory.Debug log level.
+ The following enumeration values are available:
+ \list
+ \li LoggingCategory.Debug
+ \li LoggingCategory.Info
+ \li LoggingCategory.Warning
+ \li LoggingCategory.Critical
+ \li LoggingCategory.Fatal
+ \endlist
+
+ They mirror the values of the \l{QtMsgType} enumeration.
+
\note This property needs to be set when declaring the LoggingCategory
and cannot be changed later.
+
+ \sa QtMsgType
*/
QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent)
diff --git a/src/qml/qml/qqmlmetamoduleregistration.cpp b/src/qml/qml/qqmlmetamoduleregistration.cpp
index 280c7a7c8d..8e55b62b3a 100644
--- a/src/qml/qml/qqmlmetamoduleregistration.cpp
+++ b/src/qml/qml/qqmlmetamoduleregistration.cpp
@@ -12,7 +12,7 @@ QT_BEGIN_NAMESPACE
// rely on the plugin to be loaded.
// In CMakeLists.txt we've specified NO_GENERATE_QMLTYPES to prevent
// the generation of an extra type registration file.
-Q_QML_PRIVATE_EXPORT void qml_register_types_QtQml()
+Q_QML_EXPORT void qml_register_types_QtQml()
{
// ### Qt7: Handle version 6 like version 2.
qmlRegisterModule("QtQml", 2, 0);
diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp
index e84c5a366e..352db7bd69 100644
--- a/src/qml/qml/qqmlmetaobject.cpp
+++ b/src/qml/qml/qqmlmetaobject.cpp
@@ -8,28 +8,6 @@
QT_BEGIN_NAMESPACE
-// Returns true if \a from is assignable to a property of type \a to
-bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
-{
- Q_ASSERT(!from.isNull() && !to.isNull());
-
- auto equal = [] (const QMetaObject *lhs, const QMetaObject *rhs) -> bool {
- return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata);
- };
-
- const QMetaObject *tom = to.metaObject();
- if (tom == &QObject::staticMetaObject) return true;
-
- const QMetaObject *fromm = from.metaObject();
- while (fromm) {
- if (equal(fromm, tom))
- return true;
- fromm = fromm->superClass();
- }
-
- return false;
-}
-
void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index)
{
int offset;
@@ -70,7 +48,7 @@ QMetaType QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteAr
type = _m->method(data.coreIndex()).returnMetaType();
}
if (type.flags().testFlag(QMetaType::IsEnumeration))
- type = QMetaType::fromType<int>();
+ type = type.underlyingType();
if (type.isValid())
return type;
else if (unknownTypeError)
@@ -78,42 +56,4 @@ QMetaType QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteAr
return QMetaType();
}
-bool QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError) const
-{
- Q_ASSERT(_m && index >= 0);
-
- QMetaMethod m = _m->method(index);
- return methodParameterTypes(m, argStorage, unknownTypeError);
-}
-
-bool QQmlMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy,
- QByteArray *unknownTypeError) const
-{
- QMetaMethod m = _m->constructor(index);
- return methodParameterTypes(m, dummy, unknownTypeError);
-}
-
-bool QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError)
-{
- Q_ASSERT(argStorage);
-
- int argc = m.parameterCount();
- argStorage->resize(argc);
- for (int ii = 0; ii < argc; ++ii) {
- QMetaType type = m.parameterMetaType(ii);
- // we treat enumerations as int
- if (type.flags().testFlag(QMetaType::IsEnumeration))
- type = QMetaType::fromType<int>();
- if (!type.isValid()) {
- if (unknownTypeError)
- *unknownTypeError = m.parameterTypeName(ii);
- return false;
- }
- argStorage->operator[](ii) = type;
- }
- return true;
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetaobject_p.h b/src/qml/qml/qqmlmetaobject_p.h
index cc3cc6af79..498c27884e 100644
--- a/src/qml/qml/qqmlmetaobject_p.h
+++ b/src/qml/qml/qqmlmetaobject_p.h
@@ -34,7 +34,8 @@ class QQmlPropertyData;
class Q_QML_EXPORT QQmlMetaObject
{
public:
- typedef QVarLengthArray<QMetaType, 9> ArgTypeStorage;
+ template<qsizetype Prealloc>
+ using ArgTypeStorage = QVarLengthArray<QMetaType, Prealloc>;
inline QQmlMetaObject() = default;
inline QQmlMetaObject(const QObject *);
@@ -52,30 +53,120 @@ public:
inline const QMetaObject *metaObject() const;
QMetaType methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const;
+
/*!
\internal
Returns false if one of the types is unknown. Otherwise, fills \a argstorage with the
metatypes of the function.
*/
- bool methodParameterTypes(int index, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError) const;
+ template<typename ArgTypeStorage>
+ bool methodParameterTypes(
+ int index, ArgTypeStorage *argStorage, QByteArray *unknownTypeError) const
+ {
+ Q_ASSERT(_m && index >= 0);
+
+ QMetaMethod m = _m->method(index);
+ return methodParameterTypes(m, argStorage, unknownTypeError);
+ }
+
/*!
\internal
Returns false if one of the types is unknown. Otherwise, fills \a argstorage with the
metatypes of the function.
*/
- bool constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const;
+ template<typename ArgTypeStorage>
+ bool constructorParameterTypes(
+ int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const
+ {
+ QMetaMethod m = _m->constructor(index);
+ return methodParameterTypes(m, dummy, unknownTypeError);
+ }
- static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to);
+ static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
+ {
+ Q_ASSERT(!from.isNull() && !to.isNull());
+ return from.metaObject()->inherits(to.metaObject());
+ }
// static_metacall (on Gadgets) doesn't call the base implementation and therefore
// we need a helper to find the correct meta object and property/method index.
- static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index);
+ static void resolveGadgetMethodOrPropertyIndex(
+ QMetaObject::Call type, const QMetaObject **metaObject, int *index);
+
+ template<typename ArgTypeStorage>
+ static bool methodParameterTypes(
+ const QMetaMethod &method, ArgTypeStorage *argStorage, QByteArray *unknownTypeError)
+ {
+ Q_ASSERT(argStorage);
+
+ const int argc = method.parameterCount();
+ argStorage->resize(argc);
+ for (int ii = 0; ii < argc; ++ii) {
+ if (!parameterType(method, ii, unknownTypeError, [argStorage](int ii, QMetaType &&type) {
+ argStorage->operator[](ii) = std::forward<QMetaType>(type);
+ })) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template<typename ArgTypeStorage>
+ static bool methodReturnAndParameterTypes(
+ const QMetaMethod &method, ArgTypeStorage *argStorage, QByteArray *unknownTypeError)
+ {
+ Q_ASSERT(argStorage);
+
+ const int argc = method.parameterCount();
+ argStorage->resize(argc + 1);
+
+ QMetaType type = method.returnMetaType();
+ if (type.flags().testFlag(QMetaType::IsEnumeration))
+ type = type.underlyingType();
+
+ if (!type.isValid()) {
+ if (unknownTypeError)
+ *unknownTypeError = "return type";
+ return false;
+ }
+
+ argStorage->operator[](0) = type;
+
+ for (int ii = 0; ii < argc; ++ii) {
+ if (!parameterType(
+ method, ii, unknownTypeError, [argStorage](int ii, QMetaType &&type) {
+ argStorage->operator[](ii + 1) = std::forward<QMetaType>(type);
+ })) {
+ return false;
+ }
+ }
+
+ return true;
+ }
- static bool methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError);
protected:
+ template<typename Store>
+ static bool parameterType(
+ const QMetaMethod &method, int ii, QByteArray *unknownTypeError, const Store &store)
+ {
+ QMetaType type = method.parameterMetaType(ii);
+
+ // we treat enumerations as their underlying type
+ if (type.flags().testFlag(QMetaType::IsEnumeration))
+ type = type.underlyingType();
+
+ if (!type.isValid()) {
+ if (unknownTypeError)
+ *unknownTypeError = method.parameterTypeName(ii);
+ return false;
+ }
+
+ store(ii, std::move(type));
+ return true;
+ }
+
+
const QMetaObject *_m = nullptr;
};
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 054ad48617..1175bde3db 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -3,11 +3,12 @@
#include "qqmlmetatype_p.h"
+#include <private/qqmlextensionplugin_p.h>
#include <private/qqmlmetatypedata_p.h>
-#include <private/qqmltypemodule_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
#include <private/qqmltype_p_p.h>
#include <private/qqmltypeloader_p.h>
-#include <private/qqmlextensionplugin_p.h>
+#include <private/qqmltypemodule_p.h>
#include <private/qqmlvaluetype_p.h>
#include <private/qv4executablecompilationunit_p.h>
@@ -20,32 +21,6 @@ Q_LOGGING_CATEGORY(lcTypeRegistration, "qt.qml.typeregistration")
QT_BEGIN_NAMESPACE
-CompositeMetaTypeIds CompositeMetaTypeIds::fromCompositeName(const QByteArray &name)
-{
- auto ids = QQmlMetaType::registerInternalCompositeType(name);
- ids.refCount = new int;
- *ids.refCount = 1;
- return ids;
-}
-
-void CompositeMetaTypeIds::deref()
-{
- Q_ASSERT(refCount);
- --*refCount;
- if (!*refCount) {
- delete refCount;
- QQmlMetaType::unregisterInternalCompositeType(*this);
- refCount = nullptr;
- }
-}
-
-CompositeMetaTypeIds::~CompositeMetaTypeIds()
-{
- if (refCount)
- deref();
-}
-
-
struct LockedData : private QQmlMetaTypeData
{
friend class QQmlMetaTypeDataPtr;
@@ -86,18 +61,19 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data,
const QQmlPrivate::RegisterInterface &type)
{
auto *d = new QQmlTypePrivate(QQmlType::InterfaceType);
- d->iid = type.iid;
+ d->extraData.interfaceTypeData = type.iid;
d->typeId = type.typeId;
d->listId = type.listId;
- d->isSetup.storeRelease(true);
d->module = QString::fromUtf8(type.uri);
d->version = type.version;
data->registerType(d);
return d;
}
-static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
- const QQmlPrivate::RegisterSingletonType &type)
+static QQmlTypePrivate *createQQmlType(
+ QQmlMetaTypeData *data, const QString &elementName,
+ const QQmlPrivate::RegisterSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
{
auto *d = new QQmlTypePrivate(QQmlType::SingletonType);
data->registerType(d);
@@ -111,14 +87,9 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el
d->revision = type.revision;
}
- d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
- d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi;
- d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qObjectApi;
- d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName);
- d->extraData.sd->singletonInstanceInfo->instanceMetaObject
- = type.qObjectApi ? type.instanceMetaObject : nullptr;
- d->extraData.sd->extFunc = type.extensionObjectCreate;
- d->extraData.sd->extMetaObject = type.extensionMetaObject;
+ d->extraData.singletonTypeData->singletonInstanceInfo = siinfo;
+ d->extraData.singletonTypeData->extFunc = type.extensionObjectCreate;
+ d->extraData.singletonTypeData->extMetaObject = type.extensionMetaObject;
return d;
}
@@ -134,74 +105,98 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el
d->revision = type.revision;
d->typeId = type.typeId;
d->listId = type.listId;
- d->extraData.cd->allocationSize = type.objectSize;
- d->extraData.cd->userdata = type.userdata;
- d->extraData.cd->newFunc = type.create;
- d->extraData.cd->noCreationReason = type.noCreationReason;
- d->extraData.cd->createValueTypeFunc = type.createValueType;
+ d->extraData.cppTypeData->allocationSize = type.objectSize;
+ d->extraData.cppTypeData->userdata = type.userdata;
+ d->extraData.cppTypeData->newFunc = type.create;
+ d->extraData.cppTypeData->noCreationReason = type.noCreationReason;
+ d->extraData.cppTypeData->createValueTypeFunc = type.createValueType;
d->baseMetaObject = type.metaObject;
- d->extraData.cd->attachedPropertiesFunc = type.attachedPropertiesFunction;
- d->extraData.cd->attachedPropertiesType = type.attachedPropertiesMetaObject;
- d->extraData.cd->parserStatusCast = type.parserStatusCast;
- d->extraData.cd->propertyValueSourceCast = type.valueSourceCast;
- d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast;
- d->extraData.cd->finalizerCast = type.has(QQmlPrivate::RegisterType::FinalizerCast)
+ d->extraData.cppTypeData->attachedPropertiesFunc = type.attachedPropertiesFunction;
+ d->extraData.cppTypeData->attachedPropertiesType = type.attachedPropertiesMetaObject;
+ d->extraData.cppTypeData->parserStatusCast = type.parserStatusCast;
+ d->extraData.cppTypeData->propertyValueSourceCast = type.valueSourceCast;
+ d->extraData.cppTypeData->propertyValueInterceptorCast = type.valueInterceptorCast;
+ d->extraData.cppTypeData->finalizerCast = type.has(QQmlPrivate::RegisterType::FinalizerCast)
? type.finalizerCast
: -1;
- d->extraData.cd->extFunc = type.extensionObjectCreate;
- d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser);
- d->extraData.cd->registerEnumClassesUnscoped = true;
- d->extraData.cd->registerEnumsFromRelatedTypes = true;
- d->extraData.cd->constructValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
+ d->extraData.cppTypeData->extFunc = type.extensionObjectCreate;
+ d->extraData.cppTypeData->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser);
+ d->extraData.cppTypeData->registerEnumClassesUnscoped = true;
+ d->extraData.cppTypeData->registerEnumsFromRelatedTypes = true;
+ d->extraData.cppTypeData->constructValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
&& type.creationMethod != QQmlPrivate::ValueTypeCreationMethod::None;
- d->extraData.cd->populateValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
+ d->extraData.cppTypeData->populateValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
&& type.creationMethod == QQmlPrivate::ValueTypeCreationMethod::Structured;
if (type.extensionMetaObject)
- d->extraData.cd->extMetaObject = type.extensionMetaObject;
+ d->extraData.cppTypeData->extMetaObject = type.extensionMetaObject;
// Check if the user wants only scoped enum classes
if (d->baseMetaObject) {
auto indexOfUnscoped = d->baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped");
if (indexOfUnscoped != -1
&& qstrcmp(d->baseMetaObject->classInfo(indexOfUnscoped).value(), "false") == 0) {
- d->extraData.cd->registerEnumClassesUnscoped = false;
+ d->extraData.cppTypeData->registerEnumClassesUnscoped = false;
}
auto indexOfRelated = d->baseMetaObject->indexOfClassInfo("RegisterEnumsFromRelatedTypes");
if (indexOfRelated != -1
&& qstrcmp(d->baseMetaObject->classInfo(indexOfRelated).value(), "false") == 0) {
- d->extraData.cd->registerEnumsFromRelatedTypes = false;
+ d->extraData.cppTypeData->registerEnumsFromRelatedTypes = false;
}
}
return d;
}
+static void addQQmlMetaTypeInterfaces(QQmlTypePrivate *priv, const QByteArray &className)
+{
+ Q_ASSERT(!className.isEmpty());
+ QByteArray ptr = className + '*';
+ QByteArray lst = "QQmlListProperty<" + className + '>';
+
+ QMetaType ptr_type(new QQmlMetaTypeInterface(ptr));
+ QMetaType lst_type(new QQmlListMetaTypeInterface(lst, ptr_type.iface()));
+
+ // Retrieve the IDs once, so that the types are added to QMetaType's custom type registry.
+ ptr_type.id();
+ lst_type.id();
+
+ priv->typeId = ptr_type;
+ priv->listId = lst_type;
+}
+
static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
const QQmlPrivate::RegisterCompositeType &type)
{
+ // This is a procedurally registered composite type. It's evil. It doesn't get any metatypes
+ // because we never want to find it in the compositeTypes. Otherwise we might mix it up with an
+ // actually compiled version of the same type.
+
auto *d = new QQmlTypePrivate(QQmlType::CompositeType);
data->registerType(d);
d->setName(QString::fromUtf8(type.uri), elementName);
d->version = type.version;
-
- d->extraData.fd->url = QQmlTypeLoader::normalize(type.url);
+ d->extraData.compositeTypeData = QQmlTypeLoader::normalize(type.url);
return d;
}
-static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
- const QQmlPrivate::RegisterCompositeSingletonType &type)
+static QQmlTypePrivate *createQQmlType(
+ QQmlMetaTypeData *data, const QString &elementName,
+ const QQmlPrivate::RegisterCompositeSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
{
+ // This is a procedurally registered composite singleton. It's evil. It doesn't get any
+ // metatypes because we never want to find it in the compositeTypes. Otherwise we might mix it
+ // up with an actually compiled version of the same type.
+
auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType);
data->registerType(d);
d->setName(QString::fromUtf8(type.uri), elementName);
d->version = type.version;
- d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
- d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(type.url);
- d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName);
+ d->extraData.singletonTypeData->singletonInstanceInfo = siinfo;
return d;
}
@@ -262,7 +257,7 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
- // Clone Q_ENUMS
+ // Clone enums registered with the metatype system
for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) {
QMetaEnum enumerator = mo->enumerator(ii);
@@ -317,6 +312,20 @@ void QQmlMetaType::clearTypeRegistrations()
data->urlToNonFileImportType.clear();
data->metaObjectToType.clear();
data->undeletableTypes.clear();
+ data->propertyCaches.clear();
+ data->inlineComponentTypes.clear();
+
+ // Avoid deletion recursion (via QQmlTypePrivate dtor) by moving them out of the way first.
+ QQmlMetaTypeData::CompositeTypes emptyComposites;
+ emptyComposites.swap(data->compositeTypes);
+}
+
+void QQmlMetaType::registerTypeAlias(int typeIndex, const QString &name)
+{
+ QQmlMetaTypeDataPtr data;
+ const QQmlType type = data->types.value(typeIndex);
+ const QQmlTypePrivate *priv = type.priv();
+ data->nameToType.insert(name, priv);
}
int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function)
@@ -482,7 +491,9 @@ QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type)
return QQmlType(priv);
}
-QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingletonType &type)
+QQmlType QQmlMetaType::registerSingletonType(
+ const QQmlPrivate::RegisterSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
{
if (type.structVersion > 1)
qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
@@ -495,14 +506,16 @@ QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingleto
return QQmlType();
}
- QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
+ QQmlTypePrivate *priv = createQQmlType(data, typeName, type, siinfo);
addTypeToData(priv, data);
return QQmlType(priv);
}
-QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type)
+QQmlType QQmlMetaType::registerCompositeSingletonType(
+ const QQmlPrivate::RegisterCompositeSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
{
if (type.structVersion > 1)
qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
@@ -519,11 +532,11 @@ QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::Registe
return QQmlType();
}
- QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
+ QQmlTypePrivate *priv = createQQmlType(data, typeName, type, siinfo);
addTypeToData(priv, data);
QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType);
- files->insert(QQmlTypeLoader::normalize(type.url), priv);
+ files->insert(siinfo->url, priv);
return QQmlType(priv);
}
@@ -554,26 +567,172 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit
return QQmlType(priv);
}
-CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className)
+class QQmlMetaTypeRegistrationFailureRecorder
{
- QByteArray ptr = className + '*';
- QByteArray lst = "QQmlListProperty<" + className + '>';
+ Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder)
+public:
+ QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures)
+ : data(data)
+ {
+ data->setTypeRegistrationFailures(failures);
+ }
- QMetaType ptr_type(new QQmlMetaTypeInterface(ptr));
- QMetaType lst_type(new QQmlListMetaTypeInterface(lst, ptr_type.iface()));
+ ~QQmlMetaTypeRegistrationFailureRecorder()
+ {
+ data->setTypeRegistrationFailures(nullptr);
+ }
- // Retrieve the IDs once, so that the types are added to QMetaType's custom type registry.
- ptr_type.id();
- lst_type.id();
+ QQmlMetaTypeData *data = nullptr;
+};
+
+
+static QQmlType createTypeForUrl(
+ QQmlMetaTypeData *data, const QUrl &url, const QHashedStringRef &qualifiedType,
+ QQmlMetaType::CompositeTypeLookupMode mode, QList<QQmlError> *errors, QTypeRevision version)
+{
+ const int dot = qualifiedType.indexOf(QLatin1Char('.'));
+ const QString typeName = dot < 0
+ ? qualifiedType.toString()
+ : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1);
+
+ QStringList failures;
+ QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures);
+
+ // Register the type. Note that the URI parameters here are empty; for
+ // file type imports, we do not place them in a URI as we don't
+ // necessarily have a good and unique one (picture a library import,
+ // which may be found in multiple plugin locations on disk), but there
+ // are other reasons for this too.
+ //
+ // By not putting them in a URI, we prevent the types from being
+ // registered on a QQmlTypeModule; this is important, as once types are
+ // placed on there, they cannot be easily removed, meaning if the
+ // developer subsequently loads a different import (meaning different
+ // types) with the same URI (using, say, a different plugin path), it is
+ // very undesirable that we continue to associate the types from the
+ // "old" URI with that new module.
+ //
+ // Not having URIs also means that the types cannot be found by name
+ // etc, the only way to look them up is through QQmlImports -- for
+ // better or worse.
+ const QQmlType::RegistrationType registrationType = mode == QQmlMetaType::Singleton
+ ? QQmlType::CompositeSingletonType
+ : QQmlType::CompositeType;
+ if (checkRegistration(registrationType, data, nullptr, typeName, version, {})) {
+
+ // TODO: Ideally we should defer most of this work using some lazy/atomic mechanism
+ // that creates the details on first use. We must not observably modify
+ // QQmlTypePrivate after it has been created since it is supposed to be immutable
+ // and shared across threads.
+
+ auto *priv = new QQmlTypePrivate(registrationType);
+ addQQmlMetaTypeInterfaces(priv, QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url));
+
+ priv->setName(QString(), typeName);
+ priv->version = version;
+
+ if (mode == QQmlMetaType::Singleton) {
+ QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
+ siinfo->url = url;
+ siinfo->typeName = typeName.toUtf8();
+ priv->extraData.singletonTypeData->singletonInstanceInfo =
+ QQmlType::SingletonInstanceInfo::ConstPtr(
+ siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
+ } else {
+ priv->extraData.compositeTypeData = url;
+ }
+
+ data->registerType(priv);
+ addTypeToData(priv, data);
+ return QQmlType(priv);
+ }
+
+ // This means that the type couldn't be found by URL, but could not be
+ // registered either, meaning we most likely were passed some kind of bad
+ // data.
+ if (errors) {
+ QQmlError error;
+ error.setDescription(failures.join(u'\n'));
+ errors->prepend(error);
+ } else {
+ qWarning("%s", failures.join(u'\n').toLatin1().constData());
+ }
+ return QQmlType();
+}
+
+QQmlType QQmlMetaType::findCompositeType(
+ const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
+ CompositeTypeLookupMode mode)
+{
+ const QUrl normalized = QQmlTypeLoader::normalize(url);
+ QQmlMetaTypeDataPtr data;
+
+ bool urlExists = true;
+ auto found = data->urlToType.constFind(normalized);
+ if (found == data->urlToType.cend()) {
+ found = data->urlToNonFileImportType.constFind(normalized);
+ if (found == data->urlToNonFileImportType.cend())
+ urlExists = false;
+ }
+
+ if (const QtPrivate::QMetaTypeInterface *iface = urlExists
+ ? found.value()->typeId.iface()
+ : nullptr) {
+ if (compilationUnit.isNull())
+ return QQmlType(*found);
+
+ const auto composite = data->compositeTypes.constFind(iface);
+ if (composite == data->compositeTypes.constEnd() || composite.value() == compilationUnit)
+ return QQmlType(*found);
+ }
+
+ const QQmlType type = createTypeForUrl(
+ data, normalized, QHashedStringRef(), mode, nullptr, QTypeRevision());
+
+ if (!urlExists && type.isValid())
+ data->urlToType.insert(normalized, type.priv());
+
+ return type;
+}
+
+static QQmlType doRegisterInlineComponentType(QQmlMetaTypeData *data, const QUrl &url)
+{
+ QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::InlineComponentType);
+ priv->setName(QString(), url.fragment());
+
+ priv->extraData.inlineComponentTypeData = url;
+
+ const QByteArray className
+ = QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(url, url.fragment());
+
+ addQQmlMetaTypeInterfaces(priv, className);
+ const QQmlType result(priv);
+ priv->release();
+
+ data->inlineComponentTypes.insert(url, result);
- return {ptr_type, lst_type};
+ return result;
}
-void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds)
+QQmlType QQmlMetaType::findInlineComponentType(
+ const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
{
- QMetaType metaType(typeIds.id);
- QMetaType listMetaType(typeIds.listId);
+ QQmlMetaTypeDataPtr data;
+ // If there is an "unclaimed" inline component type, we can "claim" it now. Otherwise
+ // we have to create a new one.
+ const auto it = data->inlineComponentTypes.constFind(url);
+ if (it != data->inlineComponentTypes.constEnd()) {
+ const auto jt = data->compositeTypes.constFind(it->typeId().iface());
+ if (jt == data->compositeTypes.constEnd() || *jt == compilationUnit)
+ return *it;
+ }
+
+ return doRegisterInlineComponentType(data, url);
+}
+
+void QQmlMetaType::unregisterInternalCompositeType(QMetaType metaType, QMetaType listMetaType)
+{
// This may be called from delayed dtors on shutdown when the data is already gone.
QQmlMetaTypeDataPtr data;
if (data.isValid()) {
@@ -581,6 +740,10 @@ void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &t
delete vt;
if (QQmlValueType *vt = data->metaTypeToValueType.take(listMetaType.id()))
delete vt;
+
+ auto it = data->compositeTypes.constFind(metaType.iface());
+ if (it != data->compositeTypes.constEnd())
+ data->compositeTypes.erase(it);
}
QMetaType::unregisterMetaType(metaType);
@@ -621,7 +784,7 @@ QQmlType QQmlMetaType::registerSequentialContainer(
priv->revision = container.revision;
priv->typeId = container.metaSequence.valueMetaType();
priv->listId = container.typeId;
- *priv->extraData.ld = container.metaSequence;
+ priv->extraData.sequentialContainerTypeData = container.metaSequence;
addTypeToData(priv, data);
@@ -754,25 +917,6 @@ static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const Q
return false;
}
-class QQmlMetaTypeRegistrationFailureRecorder
-{
- Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder)
-public:
- QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures)
- : data(data)
- {
- data->setTypeRegistrationFailures(failures);
- }
-
- ~QQmlMetaTypeRegistrationFailureRecorder()
- {
- data->setTypeRegistrationFailures(nullptr);
- }
-
- QQmlMetaTypeData *data = nullptr;
-};
-
-
QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes(
QObject *instance, const QString &basePath, const QString &uri,
const QString &typeNamespace, QTypeRevision version, QList<QQmlError> *errors)
@@ -873,7 +1017,7 @@ QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes(
*/
QQmlType QQmlMetaType::typeForUrl(const QString &urlString,
const QHashedStringRef &qualifiedType,
- bool isCompositeSingleton, QList<QQmlError> *errors,
+ CompositeTypeLookupMode mode, QList<QQmlError> *errors,
QTypeRevision version)
{
// ### unfortunate (costly) conversion
@@ -891,69 +1035,10 @@ QQmlType QQmlMetaType::typeForUrl(const QString &urlString,
return ret;
}
- const int dot = qualifiedType.indexOf(QLatin1Char('.'));
- const QString typeName = dot < 0
- ? qualifiedType.toString()
- : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1);
-
- QStringList failures;
- QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures);
-
- // Register the type. Note that the URI parameters here are empty; for
- // file type imports, we do not place them in a URI as we don't
- // necessarily have a good and unique one (picture a library import,
- // which may be found in multiple plugin locations on disk), but there
- // are other reasons for this too.
- //
- // By not putting them in a URI, we prevent the types from being
- // registered on a QQmlTypeModule; this is important, as once types are
- // placed on there, they cannot be easily removed, meaning if the
- // developer subsequently loads a different import (meaning different
- // types) with the same URI (using, say, a different plugin path), it is
- // very undesirable that we continue to associate the types from the
- // "old" URI with that new module.
- //
- // Not having URIs also means that the types cannot be found by name
- // etc, the only way to look them up is through QQmlImports -- for
- // better or worse.
- const QQmlType::RegistrationType registrationType = isCompositeSingleton
- ? QQmlType::CompositeSingletonType
- : QQmlType::CompositeType;
- if (checkRegistration(registrationType, data, nullptr, typeName, version, {})) {
- auto *priv = new QQmlTypePrivate(registrationType);
- priv->setName(QString(), typeName);
- priv->version = version;
-
- if (isCompositeSingleton) {
- priv->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
- priv->extraData.sd->singletonInstanceInfo->url = url;
- priv->extraData.sd->singletonInstanceInfo->typeName = typeName;
- } else {
- priv->extraData.fd->url = url;
- }
-
- data->registerType(priv);
- addTypeToData(priv, data);
- data->urlToType.insert(url, priv);
- return QQmlType(priv);
- }
-
- // This means that the type couldn't be found by URL, but could not be
- // registered either, meaning we most likely were passed some kind of bad
- // data.
- if (errors) {
- QQmlError error;
- error.setDescription(failures.join(u'\n'));
- errors->prepend(error);
- } else {
- qWarning("%s", failures.join(u'\n').toLatin1().constData());
- }
- return QQmlType();
-}
-
-QRecursiveMutex *QQmlMetaType::typeRegistrationLock()
-{
- return metaTypeDataLock();
+ const QQmlType type = createTypeForUrl(
+ data, url, qualifiedType, mode, errors, version);
+ data->urlToType.insert(url, type.priv());
+ return type;
}
/*
@@ -1275,6 +1360,16 @@ QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileI
return QQmlType();
}
+QQmlType QQmlMetaType::fetchOrCreateInlineComponentTypeForUrl(const QUrl &url)
+{
+ QQmlMetaTypeDataPtr data;
+ const auto it = data->inlineComponentTypes.constFind(url);
+ if (it != data->inlineComponentTypes.constEnd())
+ return *it;
+
+ return doRegisterInlineComponentType(data, url);
+}
+
/*!
Returns a QQmlPropertyCache for \a obj if one is available.
@@ -1346,9 +1441,12 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCacheForType(QMetaType metaTyp
return composite;
const QQmlTypePrivate *type = data->idToType.value(metaType.id());
- return (type && type->typeId == metaType)
- ? data->propertyCache(QQmlType(type).metaObject(), type->version)
- : QQmlPropertyCache::ConstPtr();
+ if (type && type->typeId == metaType) {
+ if (const QMetaObject *mo = QQmlType(type).metaObject())
+ return data->propertyCache(mo, type->version);
+ }
+
+ return QQmlPropertyCache::ConstPtr();
}
/*!
@@ -1365,8 +1463,15 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(QMetaType meta
return composite;
const QQmlTypePrivate *type = data->idToType.value(metaType.id());
- return (type && type->typeId == metaType)
- ? data->propertyCache(type->baseMetaObject, QTypeRevision())
+ if (!type || type->typeId != metaType)
+ return QQmlPropertyCache::ConstPtr();
+
+ const QMetaObject *metaObject = type->isValueType()
+ ? type->metaObjectForValueType()
+ : type->baseMetaObject;
+
+ return metaObject
+ ? data->propertyCache(metaObject, QTypeRevision())
: QQmlPropertyCache::ConstPtr();
}
@@ -1388,8 +1493,11 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(
return QQmlPropertyCache::ConstPtr();
const QQmlType type(typePriv);
- if (type.containsRevisionedAttributes())
+ if (type.containsRevisionedAttributes()) {
+ // It can only have (revisioned) properties or methods if it has a metaobject
+ Q_ASSERT(type.metaObject());
return data->propertyCache(type, version);
+ }
if (const QMetaObject *metaObject = type.metaObject())
return data->propertyCache(metaObject, version);
@@ -1402,6 +1510,8 @@ void QQmlMetaType::unregisterType(int typeIndex)
QQmlMetaTypeDataPtr data;
const QQmlType type = data->types.value(typeIndex);
if (const QQmlTypePrivate *d = type.priv()) {
+ if (d->regType == QQmlType::CompositeType || d->regType == QQmlType::CompositeSingletonType)
+ removeFromInlineComponents(data->inlineComponentTypes, d);
removeQQmlTypePrivate(data->idToType, d);
removeQQmlTypePrivate(data->nameToType, d);
removeQQmlTypePrivate(data->urlToType, d);
@@ -1423,16 +1533,41 @@ void QQmlMetaType::registerMetaObjectForType(const QMetaObject *metaobject, QQml
data->metaObjectToType.insert(metaobject, type);
}
-static bool hasActiveInlineComponents(const QQmlTypePrivate *d)
+static bool hasActiveInlineComponents(const QQmlMetaTypeData *data, const QQmlTypePrivate *d)
{
- for (const QQmlType &ic : std::as_const(d->objectIdToICType)) {
- const QQmlTypePrivate *icPriv = ic.priv();
+ for (auto it = data->inlineComponentTypes.begin(), end = data->inlineComponentTypes.end();
+ it != end; ++it) {
+ if (!QQmlMetaType::equalBaseUrls(it.key(), d->sourceUrl()))
+ continue;
+
+ const QQmlTypePrivate *icPriv = it->priv();
if (icPriv && icPriv->count() > 1)
return true;
}
return false;
}
+static int doCountInternalCompositeTypeSelfReferences(
+ QQmlMetaTypeData *data,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
+{
+ int result = 0;
+ auto doCheck = [&](const QtPrivate::QMetaTypeInterface *iface) {
+ if (!iface)
+ return;
+
+ const auto it = data->compositeTypes.constFind(iface);
+ if (it != data->compositeTypes.constEnd() && *it == compilationUnit)
+ ++result;
+ };
+
+ doCheck(compilationUnit->metaType().iface());
+ for (auto &&inlineData: compilationUnit->inlineComponentData)
+ doCheck(inlineData.qmlType.typeId().iface());
+
+ return result;
+}
+
void QQmlMetaType::freeUnusedTypesAndCaches()
{
QQmlMetaTypeDataPtr data;
@@ -1441,15 +1576,33 @@ void QQmlMetaType::freeUnusedTypesAndCaches()
if (!data.isValid())
return;
+ bool droppedAtLeastOneComposite;
+ do {
+ droppedAtLeastOneComposite = false;
+ auto it = data->compositeTypes.begin();
+ while (it != data->compositeTypes.end()) {
+ if ((*it)->count() <= doCountInternalCompositeTypeSelfReferences(data, *it)) {
+ it = data->compositeTypes.erase(it);
+ droppedAtLeastOneComposite = true;
+ } else {
+ ++it;
+ }
+ }
+ } while (droppedAtLeastOneComposite);
+
bool deletedAtLeastOneType;
do {
deletedAtLeastOneType = false;
QList<QQmlType>::Iterator it = data->types.begin();
while (it != data->types.end()) {
const QQmlTypePrivate *d = (*it).priv();
- if (d && d->count() == 1 && !hasActiveInlineComponents(d)) {
+ if (d && d->count() == 1 && !hasActiveInlineComponents(data, d)) {
deletedAtLeastOneType = true;
+ if (d->regType == QQmlType::CompositeType
+ || d->regType == QQmlType::CompositeSingletonType) {
+ removeFromInlineComponents(data->inlineComponentTypes, d);
+ }
removeQQmlTypePrivate(data->idToType, d);
removeQQmlTypePrivate(data->nameToType, d);
removeQQmlTypePrivate(data->urlToType, d);
@@ -1509,7 +1662,7 @@ QList<QQmlType> QQmlMetaType::qmlTypes()
const QQmlMetaTypeDataPtr data;
QList<QQmlType> types;
- for (QQmlTypePrivate *t : data->nameToType)
+ for (const QQmlTypePrivate *t : data->nameToType)
types.append(QQmlType(t));
return types;
@@ -1543,7 +1696,7 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes()
static bool isFullyTyped(const QQmlPrivate::CachedQmlUnit *unit)
{
quint32 numTypedFunctions = 0;
- for (const QQmlPrivate::TypedFunction *function = unit->aotCompiledFunctions;
+ for (const QQmlPrivate::AOTCompiledFunction *function = unit->aotCompiledFunctions;
function; ++function) {
if (function->functionPtr)
++numTypedFunctions;
@@ -1562,7 +1715,7 @@ const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit(
for (const auto lookup : std::as_const(data->lookupCachedQmlUnit)) {
if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) {
QString error;
- if (!QV4::ExecutableCompilationUnit::verifyHeader(unit->qmlData, QDateTime(), &error)) {
+ if (!unit->qmlData->verifyHeader(QDateTime(), &error)) {
qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error;
if (status)
*status = CachedUnitLookupError::VersionMismatch;
@@ -1651,7 +1804,8 @@ QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject
QList<QQmlProxyMetaObject::ProxyData> metaObjects;
mo = mo->d.superdata;
- const QQmlMetaTypeDataPtr data;
+ if (!mo)
+ return metaObjects;
auto createProxyMetaObject = [&](QQmlTypePrivate *This,
const QMetaObject *superdataBaseMetaObject,
@@ -1674,19 +1828,25 @@ QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject
registerMetaObjectForType(mmo, This);
};
- while (mo) {
- QQmlTypePrivate *t = data->metaObjectToType.value(mo);
- if (t) {
+ for (const QQmlMetaTypeDataPtr data; mo; mo = mo->d.superdata) {
+ // TODO: There can in fact be multiple QQmlTypePrivate* for a single QMetaObject*.
+ // This algorithm only accounts for the most recently inserted one. That's pretty
+ // random. However, the availability of types depends on what documents you have
+ // loaded before. Just adding all possible extensions would also be pretty random.
+ // The right way to do this would be to take the relations between the QML modules
+ // into account. For this we would need proper module dependency information.
+ if (QQmlTypePrivate *t = data->metaObjectToType.value(mo)) {
if (t->regType == QQmlType::CppType) {
- createProxyMetaObject(t, t->baseMetaObject, t->extraData.cd->extMetaObject,
- t->extraData.cd->extFunc);
+ createProxyMetaObject(
+ t, t->baseMetaObject, t->extraData.cppTypeData->extMetaObject,
+ t->extraData.cppTypeData->extFunc);
} else if (t->regType == QQmlType::SingletonType) {
- createProxyMetaObject(t, t->baseMetaObject, t->extraData.sd->extMetaObject,
- t->extraData.sd->extFunc);
+ createProxyMetaObject(
+ t, t->baseMetaObject, t->extraData.singletonTypeData->extMetaObject,
+ t->extraData.singletonTypeData->extFunc);
}
}
- mo = mo->d.superdata;
- }
+ };
return metaObjects;
}
@@ -1745,8 +1905,12 @@ const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
// call QObject pointers value types. Explicitly registered types also override
// the implicit use of gadgets.
if (!(metaType.flags() & QMetaType::PointerToQObject)) {
- if (const QMetaObject *mo = metaObjectForValueType(QQmlMetaType::qmlType(metaType)))
- return mo;
+ const QQmlMetaTypeDataPtr data;
+ const QQmlTypePrivate *type = data->idToType.value(metaType.id());
+ if (type && type->regType == QQmlType::CppType && type->typeId == metaType) {
+ if (const QMetaObject *mo = type->metaObjectForValueType())
+ return mo;
+ }
}
// If it _is_ a gadget, we can just use it.
@@ -1775,33 +1939,76 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::findPropertyCacheInCompositeTypes(QMet
return data->findPropertyCacheInCompositeTypes(t);
}
-void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
+void QQmlMetaType::registerInternalCompositeType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
{
- compilationUnit->isRegistered = true;
-
QQmlMetaTypeDataPtr data;
- // The QQmlCompiledData is not referenced here, but it is removed from this
- // hash in the QQmlCompiledData destructor
- data->compositeTypes.insert(compilationUnit->typeIds.id.iface(), compilationUnit);
+ auto doInsert = [&data, &compilationUnit](const QtPrivate::QMetaTypeInterface *iface) {
+ Q_ASSERT(iface);
+ Q_ASSERT(compilationUnit);
+
+ // We can't assert on anything else here. We may get a completely new type as exposed
+ // by the qmldiskcache test that changes a QML file in place during the execution
+ // of the test.
+ data->compositeTypes.insert(iface, compilationUnit);
+ };
+
+ doInsert(compilationUnit->metaType().iface());
for (auto &&inlineData: compilationUnit->inlineComponentData)
- data->compositeTypes.insert(inlineData.typeIds.id.iface(), compilationUnit);
+ doInsert(inlineData.qmlType.typeId().iface());
}
-void QQmlMetaType::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
+void QQmlMetaType::unregisterInternalCompositeType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
{
- compilationUnit->isRegistered = false;
+ QQmlMetaTypeDataPtr data;
+
+ auto doRemove = [&](const QtPrivate::QMetaTypeInterface *iface) {
+ if (!iface)
+ return;
+
+ const auto it = data->compositeTypes.constFind(iface);
+ if (it != data->compositeTypes.constEnd() && *it == compilationUnit)
+ data->compositeTypes.erase(it);
+ };
+
+ doRemove(compilationUnit->metaType().iface());
+ for (auto &&inlineData: compilationUnit->inlineComponentData)
+ doRemove(inlineData.qmlType.typeId().iface());
+}
+int QQmlMetaType::countInternalCompositeTypeSelfReferences(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
+{
QQmlMetaTypeDataPtr data;
- data->compositeTypes.remove(compilationUnit->typeIds.id.iface());
- for (auto&& icDatum: compilationUnit->inlineComponentData)
- data->compositeTypes.remove(icDatum.typeIds.id.iface());
+ return doCountInternalCompositeTypeSelfReferences(data, compilationUnit);
}
-QV4::ExecutableCompilationUnit *QQmlMetaType::obtainExecutableCompilationUnit(QMetaType type)
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit(
+ QMetaType type)
{
const QQmlMetaTypeDataPtr data;
return data->compositeTypes.value(type.iface());
}
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit(
+ const QUrl &url)
+{
+ const QUrl normalized = QQmlTypeLoader::normalize(url);
+ QQmlMetaTypeDataPtr data;
+
+ auto found = data->urlToType.constFind(normalized);
+ if (found == data->urlToType.constEnd()) {
+ found = data->urlToNonFileImportType.constFind(normalized);
+ if (found == data->urlToNonFileImportType.constEnd())
+ return QQmlRefPointer<QV4::CompiledData::CompilationUnit>();
+ }
+
+ const auto composite = data->compositeTypes.constFind(found.value()->typeId.iface());
+ return composite == data->compositeTypes.constEnd()
+ ? QQmlRefPointer<QV4::CompiledData::CompilationUnit>()
+ : composite.value();
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index c19dd8aff7..f4870d9db1 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -28,88 +28,89 @@ class QRecursiveMutex;
class QQmlError;
class QQmlValueType;
-namespace QV4 { class ExecutableCompilationUnit; }
+namespace QV4 {
+namespace CompiledData {
+struct CompilationUnit;
+}
+}
-struct CompositeMetaTypeIds
+class Q_QML_EXPORT QQmlMetaType
{
-private:
- int *refCount = nullptr;
- void deref();
- void ref()
- {
- Q_ASSERT(refCount);
- ++*refCount;
- }
-public:
- CompositeMetaTypeIds() = default;
- CompositeMetaTypeIds(QMetaType id, QMetaType listId) : id(id), listId(listId) {}
- CompositeMetaTypeIds(const CompositeMetaTypeIds &other)
- : refCount(other.refCount), id(other.id), listId(other.listId)
- {
- if (refCount)
- ref();
- }
- CompositeMetaTypeIds(CompositeMetaTypeIds &&other)
- : refCount(other.refCount), id(other.id), listId(other.listId)
- {
- other.refCount = nullptr;
- }
- CompositeMetaTypeIds &operator=(const CompositeMetaTypeIds &other)
- {
- if (refCount)
- deref();
- refCount = other.refCount;
- id = other.id;
- listId = other.listId;
- if (refCount)
- ref();
- return *this;
- }
- CompositeMetaTypeIds &operator=(CompositeMetaTypeIds &&other)
- {
- if (refCount)
- deref();
- refCount = other.refCount;
- id = other.id;
- listId = other.listId;
- other.refCount = nullptr;
- return *this;
- }
- ~CompositeMetaTypeIds();
- static CompositeMetaTypeIds fromCompositeName(const QByteArray &name);
-public:
- QMetaType id;
- QMetaType listId;
- bool isValid() const { return id.isValid() && listId.isValid(); }
-};
-
-class Q_QML_PRIVATE_EXPORT QQmlMetaType
-{
- friend struct CompositeMetaTypeIds;
friend class QQmlDesignerMetaObject;
- static CompositeMetaTypeIds registerInternalCompositeType(const QByteArray &className);
- static void unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds);
-
public:
+
enum class RegistrationResult {
Success,
Failure,
NoRegistrationFunction
};
+ static QUrl inlineComponentUrl(const QUrl &baseUrl, const QString &name)
+ {
+ QUrl icUrl = baseUrl;
+ icUrl.setFragment(name);
+ return icUrl;
+ }
+
+ static bool equalBaseUrls(const QUrl &aUrl, const QUrl &bUrl)
+ {
+ // Everything but fragment has to match
+ return aUrl.port() == bUrl.port()
+ && aUrl.scheme() == bUrl.scheme()
+ && aUrl.userName() == bUrl.userName()
+ && aUrl.password() == bUrl.password()
+ && aUrl.host() == bUrl.host()
+ && aUrl.path() == bUrl.path()
+ && aUrl.query() == bUrl.query();
+ }
+
+ enum CompositeTypeLookupMode {
+ NonSingleton,
+ Singleton,
+ };
+
+ static QQmlType findCompositeType(
+ const QUrl &url,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
+ CompositeTypeLookupMode mode = NonSingleton);
+ static QQmlType findInlineComponentType(
+ const QUrl &url,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ static QQmlType findInlineComponentType(
+ const QUrl &baseUrl, const QString &name,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
+ {
+ return findInlineComponentType(inlineComponentUrl(baseUrl, name), compilationUnit);
+ }
+
+ static void unregisterInternalCompositeType(QMetaType metaType, QMetaType listMetaType);
static QQmlType registerType(const QQmlPrivate::RegisterType &type);
static QQmlType registerInterface(const QQmlPrivate::RegisterInterface &type);
- static QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type);
- static QQmlType registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type);
+ static QQmlType registerSingletonType(
+ const QQmlPrivate::RegisterSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo);
+ static QQmlType registerCompositeSingletonType(
+ const QQmlPrivate::RegisterCompositeSingletonType &type,
+ const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo);
static QQmlType registerCompositeType(const QQmlPrivate::RegisterCompositeType &type);
static RegistrationResult registerPluginTypes(QObject *instance, const QString &basePath,
const QString &uri, const QString &typeNamespace,
QTypeRevision version, QList<QQmlError> *errors);
+
static QQmlType typeForUrl(const QString &urlString, const QHashedStringRef& typeName,
- bool isCompositeSingleton, QList<QQmlError> *errors,
+ CompositeTypeLookupMode mode, QList<QQmlError> *errors,
QTypeRevision version = QTypeRevision());
+ static QQmlType fetchOrCreateInlineComponentTypeForUrl(const QUrl &url);
+ static QQmlType inlineComponentType(const QQmlType &outerType, const QString &name)
+ {
+ return outerType.isComposite()
+ ? fetchOrCreateInlineComponentTypeForUrl(
+ inlineComponentUrl(outerType.sourceUrl(), name))
+ : QQmlType();
+ }
+
static void unregisterType(int type);
static void registerMetaObjectForType(const QMetaObject *metaobject, QQmlTypePrivate *type);
@@ -197,8 +198,6 @@ public:
static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler);
static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler);
- static QRecursiveMutex *typeRegistrationLock();
-
static QString prettyTypeName(const QObject *object);
template <typename QQmlTypeContainer>
@@ -213,6 +212,21 @@ public:
}
}
+ template <typename InlineComponentContainer>
+ static void removeFromInlineComponents(
+ InlineComponentContainer &container, const QQmlTypePrivate *reference)
+ {
+ const QUrl referenceUrl = QQmlType(reference).sourceUrl();
+ for (auto it = container.begin(), end = container.end(); it != end;) {
+ if (equalBaseUrls(it.key(), referenceUrl))
+ it = container.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ static void registerTypeAlias(int typeId, const QString &name);
+
static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent);
static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function);
@@ -243,31 +257,18 @@ public:
static bool isValueType(QMetaType type);
static QQmlValueType *valueType(QMetaType metaType);
static const QMetaObject *metaObjectForValueType(QMetaType type);
- static const QMetaObject *metaObjectForValueType(const QQmlType &qmlType)
- {
- // Prefer the extension meta object, if any.
- // Extensions allow registration of non-gadget value types.
- if (const QMetaObject *extensionMetaObject = qmlType.extensionMetaObject()) {
- // This may be a namespace even if the original metaType isn't.
- // You can do such things with QML_FOREIGN declarations.
- if (extensionMetaObject->metaType().flags() & QMetaType::IsGadget)
- return extensionMetaObject;
- }
-
- if (const QMetaObject *qmlTypeMetaObject = qmlType.metaObject()) {
- // This may be a namespace even if the original metaType isn't.
- // You can do such things with QML_FOREIGN declarations.
- if (qmlTypeMetaObject->metaType().flags() & QMetaType::IsGadget)
- return qmlTypeMetaObject;
- }
-
- return nullptr;
- }
static QQmlPropertyCache::ConstPtr findPropertyCacheInCompositeTypes(QMetaType t);
- static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
- static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
- static QV4::ExecutableCompilationUnit *obtainExecutableCompilationUnit(QMetaType type);
+ static void registerInternalCompositeType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ static void unregisterInternalCompositeType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ static int countInternalCompositeTypeSelfReferences(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ static QQmlRefPointer<QV4::CompiledData::CompilationUnit> obtainCompilationUnit(
+ QMetaType type);
+ static QQmlRefPointer<QV4::CompiledData::CompilationUnit> obtainCompilationUnit(
+ const QUrl &url);
};
Q_DECLARE_TYPEINFO(QQmlMetaType, Q_RELOCATABLE_TYPE);
@@ -287,7 +288,7 @@ struct QQmlMetaTypeInterface : QtPrivate::QMetaTypeInterface
const QByteArray name;
QQmlMetaTypeInterface(const QByteArray &name)
: QMetaTypeInterface {
- /*.revision=*/ 0,
+ /*.revision=*/ QMetaTypeInterface::CurrentRevision,
/*.alignment=*/ alignof(QObject *),
/*.size=*/ sizeof(QObject *),
/*.flags=*/ QtPrivate::QMetaTypeTypeFlags<QObject *>::Flags,
@@ -322,7 +323,7 @@ struct QQmlListMetaTypeInterface : QtPrivate::QMetaTypeInterface
const QtPrivate::QMetaTypeInterface *valueType;
QQmlListMetaTypeInterface(const QByteArray &name, const QtPrivate::QMetaTypeInterface *valueType)
: QMetaTypeInterface {
- /*.revision=*/ 0,
+ /*.revision=*/ QMetaTypeInterface::CurrentRevision,
/*.alignment=*/ alignof(QQmlListProperty<QObject>),
/*.size=*/ sizeof(QQmlListProperty<QObject>),
/*.flags=*/ QtPrivate::QMetaTypeTypeFlags<QQmlListProperty<QObject>>::Flags,
diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp
index ab6054349a..bc7e762e53 100644
--- a/src/qml/qml/qqmlmetatypedata.cpp
+++ b/src/qml/qml/qqmlmetatypedata.cpp
@@ -15,8 +15,12 @@ QQmlMetaTypeData::QQmlMetaTypeData()
QQmlMetaTypeData::~QQmlMetaTypeData()
{
- for (auto iter = compositeTypes.cbegin(), end = compositeTypes.cend(); iter != end; ++iter)
- iter.value()->isRegistered = false;
+ {
+ // Unregister all remaining composite types.
+ // Avoid deletion recursion (via QQmlTypePrivate dtor) by moving them out of the way first.
+ CompositeTypes emptyComposites;
+ emptyComposites.swap(compositeTypes);
+ }
propertyCaches.clear();
// Do this before the attached properties disappear.
@@ -137,6 +141,7 @@ QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache(
quint8 maxMinorVersion = 0;
const QMetaObject *metaObject = type.metaObject();
+ Q_ASSERT(metaObject);
const QTypeRevision combinedVersion = version.hasMajorVersion()
? version
@@ -236,13 +241,11 @@ QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache(
}
static QQmlPropertyCache::ConstPtr propertyCacheForPotentialInlineComponentType(
- QMetaType t,
- const QHash<const QtPrivate::QMetaTypeInterface *,
- QV4::ExecutableCompilationUnit *>::const_iterator &iter) {
- if (t != (*iter)->typeIds.id) {
+ QMetaType t, const QQmlMetaTypeData::CompositeTypes::const_iterator &iter) {
+ if (t != (*iter)->metaType()) {
// this is an inline component, and what we have in the iterator is currently the parent compilation unit
for (auto &&icDatum: (*iter)->inlineComponentData)
- if (icDatum.typeIds.id == t)
+ if (icDatum.qmlType.typeId() == t)
return (*iter)->propertyCaches.at(icDatum.objectIndex);
}
return (*iter)->rootPropertyCache();
diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h
index 60b76137d1..8863bd1089 100644
--- a/src/qml/qml/qqmlmetatypedata_p.h
+++ b/src/qml/qml/qqmlmetatypedata_p.h
@@ -36,10 +36,10 @@ struct QQmlMetaTypeData
typedef QHash<int, QQmlTypePrivate *> Ids;
Ids idToType;
- using Names = QMultiHash<QHashedString, QQmlTypePrivate *>;
+ using Names = QMultiHash<QHashedString, const QQmlTypePrivate *>;
Names nameToType;
- typedef QHash<QUrl, QQmlTypePrivate *> Files; //For file imported composite types only
+ typedef QHash<QUrl, const QQmlTypePrivate *> Files; //For file imported composite types only
Files urlToType;
Files urlToNonFileImportType; // For non-file imported composite and composite
// singleton types. This way we can locate any
@@ -49,7 +49,11 @@ struct QQmlMetaTypeData
MetaObjects metaObjectToType;
QVector<QHash<QTypeRevision, QQmlPropertyCache::ConstPtr>> typePropertyCaches;
QHash<int, QQmlValueType *> metaTypeToValueType;
- QHash<const QtPrivate::QMetaTypeInterface *, QV4::ExecutableCompilationUnit *> compositeTypes;
+
+ using CompositeTypes = QHash<const QtPrivate::QMetaTypeInterface *,
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>>;
+ CompositeTypes compositeTypes;
+ QHash<QUrl, QQmlType> inlineComponentTypes;
struct VersionedUri {
VersionedUri() = default;
diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
index 05c9347fab..ed4fd34073 100644
--- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
+++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
@@ -18,9 +18,20 @@ QT_BEGIN_NAMESPACE
with custom QNetworkAccessManager instances with specialized caching,
proxy and cookies support.
+ \list
+ \li The QNetworkDiskCache can be used as a request cache with \l {QNetworkDiskCache}.
+ \li Using \l {QNetworkProxy}, traffic sent by the QNetworkAccessManager can be tunnelled through a proxy.
+ \li Cookies can be saved for future requests by adding a \l {QNetworkCookieJar}.
+ \endlist
+
To implement a factory, subclass QQmlNetworkAccessManagerFactory and
implement the virtual create() method, then assign it to the relevant QML
- engine using QQmlEngine::setNetworkAccessManagerFactory().
+ engine using QQmlEngine::setNetworkAccessManagerFactory(). For instance, the QNetworkAccessManager
+ objects created by the following snippet will cache requests.
+ \snippet code/src_network_access_qnetworkaccessmanager.cpp 0
+
+ The factory can then be passed to the QML engine so it can instantiate the QNetworkAccessManager with the custom behavior.
+ \snippet code/src_network_access_qnetworkaccessmanager.cpp 1
Note the QML engine may create QNetworkAccessManager instances
from multiple threads. Because of this, the implementation of the create()
@@ -44,7 +55,7 @@ QT_BEGIN_NAMESPACE
For more information about signals and threads, see
\l {Threads and QObjects} and \l {Signals and Slots Across Threads}.
- \sa {C++ Extensions: Network Access Manager Factory Example}{Network Access Manager Factory Example}
+ \sa QNetworkDiskCache
*/
/*!
diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h
index 844766a633..0ec7d5fef2 100644
--- a/src/qml/qml/qqmlnotifier_p.h
+++ b/src/qml/qml/qqmlnotifier_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQmlNotifierEndpoint;
class QQmlData;
-class Q_QML_PRIVATE_EXPORT QQmlNotifier
+class Q_QML_EXPORT QQmlNotifier
{
public:
inline QQmlNotifier();
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index c014c24d43..a9b9140390 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -38,6 +38,19 @@ Q_LOGGING_CATEGORY(lcQmlDefaultMethod, "qt.qml.defaultmethod")
QT_USE_NAMESPACE
+Q_TRACE_PREFIX(qtqml,
+"namespace QV4 {" \
+"struct ExecutionEngine;" \
+"class ExecutableCompilationUnit;" \
+"namespace CompiledData {" \
+"struct Object;" \
+"}}" \
+"class QQmlEngine;"
+)
+
+Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_entry, const QV4::ExecutableCompilationUnit *compilationUnit, const QV4::CompiledData::Object *object, const QUrl &url)
+Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_exit, const QString &typeName)
+
QQmlObjectCreator::QQmlObjectCreator(
QQmlRefPointer<QQmlContextData> parentContext,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
@@ -45,7 +58,7 @@ QQmlObjectCreator::QQmlObjectCreator(
QQmlIncubatorPrivate *incubator)
: phase(Startup)
, compilationUnit(compilationUnit)
- , propertyCaches(&compilationUnit->propertyCaches)
+ , propertyCaches(compilationUnit->propertyCachesPtr())
, sharedState(new QQmlObjectCreatorSharedState, QQmlRefPointer<QQmlObjectCreatorSharedState>::Adopt)
, topLevelCreator(true)
, isContextObject(true)
@@ -57,7 +70,7 @@ QQmlObjectCreator::QQmlObjectCreator(
sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount());
sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount());
sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount());
- sharedState->allJavaScriptObjects = nullptr;
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList();
sharedState->creationContext = creationContext;
sharedState->rootContext.reset();
sharedState->hadTopLevelRequiredProperties = false;
@@ -75,7 +88,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlRefPointer<QQmlContextData> parentConte
QQmlObjectCreatorSharedState *inheritedSharedState, bool isContextObject)
: phase(Startup)
, compilationUnit(compilationUnit)
- , propertyCaches(&compilationUnit->propertyCaches)
+ , propertyCaches(compilationUnit->propertyCachesPtr())
, sharedState(inheritedSharedState)
, topLevelCreator(false)
, isContextObject(isContextObject)
@@ -90,8 +103,10 @@ void QQmlObjectCreator::init(QQmlRefPointer<QQmlContextData> providedParentConte
engine = parentContext->engine();
v4 = engine->handle();
- if (compilationUnit && !compilationUnit->engine)
- compilationUnit->linkToEngine(v4);
+ Q_ASSERT(compilationUnit);
+ Q_ASSERT(compilationUnit->engine == v4);
+ if (!compilationUnit->runtimeStrings)
+ compilationUnit->populate();
qmlUnit = compilationUnit->unitData();
_qobject = nullptr;
@@ -141,7 +156,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
} else {
Q_ASSERT(subComponentIndex >= 0);
if (flags & CreationFlags::InlineComponent) {
- if (compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound
+ if (compilationUnit->componentsAreBound()
&& compilationUnit != parentContext->typeCompilationUnit()) {
recordError({}, tr("Cannot instantiate bound inline component in different file"));
phase = ObjectsCreated;
@@ -151,7 +166,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
isComponentRoot = true;
} else {
Q_ASSERT(flags & CreationFlags::NormalObject);
- if (compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound
+ if (compilationUnit->componentsAreBound()
&& sharedState->creationContext != parentContext) {
recordError({}, tr("Cannot instantiate bound component "
"outside its creation context"));
@@ -174,9 +189,9 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
QV4::Scope scope(v4);
- Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator);
+ Q_ASSERT(sharedState->allJavaScriptObjects.canTrack() || topLevelCreator);
if (topLevelCreator)
- sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount());
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList(scope, compilationUnit->totalObjectCount());
if (!isComponentRoot && sharedState->creationContext) {
// otherwise QQmlEnginePrivate::createInternalContext() handles it
@@ -191,7 +206,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
}
if (topLevelCreator)
- sharedState->allJavaScriptObjects = nullptr;
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList();
phase = CreatingObjectsPhase2;
@@ -220,10 +235,11 @@ void QQmlObjectCreator::beginPopulateDeferred(const QQmlRefPointer<QQmlContextDa
sharedState->rootContext = newContext;
Q_ASSERT(topLevelCreator);
- Q_ASSERT(!sharedState->allJavaScriptObjects);
+ Q_ASSERT(!sharedState->allJavaScriptObjects.canTrack());
+ // FIXME (QTBUG-122956): allocating from the short lived scope does not make any sense
QV4::Scope valueScope(v4);
- sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount());
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList(valueScope, compilationUnit->totalObjectCount());
}
void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
@@ -277,6 +293,16 @@ void QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty,
}
}
+void QQmlObjectCreator::populateDeferredInstance(
+ QObject *outerObject, int deferredIndex, int index, QObject *instance,
+ QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty,
+ const QV4::CompiledData::Binding *binding)
+{
+ doPopulateDeferred(outerObject, deferredIndex, [&]() {
+ populateInstance(index, instance, bindingTarget, valueTypeProperty, binding);
+ });
+}
+
void QQmlObjectCreator::finalizePopulateDeferred()
{
phase = ObjectsCreated;
@@ -290,8 +316,11 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
QMetaType propertyType = property->propType();
if (property->isEnum()) {
- if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) {
- propertyType = QMetaType::fromType<int>();
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum) ||
+ // TODO: For historical reasons you can assign any number to an enum property alias
+ // This can be fixed with an opt-out mechanism, for example a pragma.
+ (property->isAlias() && binding->isNumberBinding())) {
+ propertyType = property->propType().underlyingType();
} else {
// ### This should be resolved earlier at compile time and the binding value should be changed accordingly.
QVariant value = compilationUnit->bindingValueAsString(binding);
@@ -413,6 +442,49 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
}
break;
+ case QMetaType::SChar: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ qint8 value = qint8(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::UChar: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ quint8 value = quint8(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::Short: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ qint16 value = qint16(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::UShort: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ quint16 value = quint16(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::LongLong: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ qint64 value = qint64(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ case QMetaType::ULongLong: {
+ assertType(QV4::CompiledData::Binding::Type_Number);
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ quint64 value = quint64(d);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
+ break;
+ }
+ break;
case QMetaType::Float: {
assertType(QV4::CompiledData::Binding::Type_Number);
float value = float(compilationUnit->bindingValueAsNumber(binding));
@@ -426,9 +498,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
}
break;
case QMetaType::QColor: {
- QVariant data(propertyType);
- if (QQmlValueTypeProvider::createValueType(
- compilationUnit->bindingValueAsString(binding), propertyType, data.data())) {
+ QVariant data = QQmlValueTypeProvider::createValueType(
+ compilationUnit->bindingValueAsString(binding), propertyType);
+ if (data.isValid()) {
property->writeProperty(_qobject, data.data(), propertyWriteFlags);
}
}
@@ -509,12 +581,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
case QMetaType::QVector3D:
case QMetaType::QVector4D:
case QMetaType::QQuaternion: {
- QVariant result(propertyType);
- bool ok = QQmlValueTypeProvider::createValueType(
- compilationUnit->bindingValueAsString(binding),
- result.metaType(), result.data());
- assertOrNull(ok);
- Q_UNUSED(ok);
+ QVariant result = QQmlValueTypeProvider::createValueType(
+ compilationUnit->bindingValueAsString(binding), propertyType);
+ assertOrNull(result.isValid());
property->writeProperty(_qobject, result.data(), propertyWriteFlags);
break;
}
@@ -602,8 +671,8 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
}
- QVariant target(propertyType);
- if (QQmlValueTypeProvider::createValueType(source, propertyType, target.data())) {
+ QVariant target = QQmlValueTypeProvider::createValueType(source, propertyType);
+ if (target.isValid()) {
property->writeProperty(_qobject, target.data(), propertyWriteFlags);
break;
}
@@ -635,10 +704,11 @@ void QQmlObjectCreator::setupBindings(BindingSetupFlags mode)
QQmlListProperty<void> savedList;
qSwap(_currentList, savedList);
- const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex);
+ const QV4::CompiledData::BindingPropertyData *propertyData
+ = compilationUnit->bindingPropertyDataPerObjectAt(_compiledObjectIndex);
if (_compiledObject->idNameIndex) {
- const QQmlPropertyData *idProperty = propertyData.last();
+ const QQmlPropertyData *idProperty = propertyData->last();
Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id"));
if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType().id() == QMetaType::QString) {
QV4::CompiledData::Binding idBinding;
@@ -685,7 +755,7 @@ void QQmlObjectCreator::setupBindings(BindingSetupFlags mode)
const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable();
for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) {
- const QQmlPropertyData *const property = propertyData.at(i);
+ const QQmlPropertyData *const property = propertyData->at(i);
if (property) {
const QQmlPropertyData *targetProperty = property;
if (targetProperty->isAlias()) {
@@ -766,16 +836,17 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex);
Q_ASSERT(tr);
QQmlType attachedType = tr->type();
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
if (!attachedType.isValid()) {
QQmlTypeNameCache::Result res = context->imports()->query(
- stringAt(binding->propertyNameIndex));
+ stringAt(binding->propertyNameIndex), QQmlTypeLoader::get(enginePrivate));
if (res.isValid())
attachedType = res.type;
else
return false;
}
QObject *qmlObject = qmlAttachedPropertiesObject(
- _qobject, attachedType.attachedPropertiesFunction(QQmlEnginePrivate::get(engine)));
+ _qobject, attachedType.attachedPropertiesFunction(enginePrivate));
if (!qmlObject) {
recordError(binding->location,
QStringLiteral("Could not create attached properties object '%1'")
@@ -889,14 +960,13 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
&& !(bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
&& !_valueTypeProperty;
- if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && allowedToRemoveBinding) {
- QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
- } else if (bindingProperty->isBindable() && allowedToRemoveBinding) {
- QList<DeferredQPropertyBinding> &pendingBindings = sharedState.data()->allQPropertyBindings;
- auto it = std::remove_if(pendingBindings.begin(), pendingBindings.end(), [&](const DeferredQPropertyBinding &deferred) {
- return deferred.properyIndex == bindingProperty->coreIndex() && deferred.target == _bindingTarget;
- });
- pendingBindings.erase(it, pendingBindings.end());
+ if (allowedToRemoveBinding) {
+ if (bindingProperty->isBindable()) {
+ removePendingBinding(_bindingTarget, bindingProperty->coreIndex());
+ } else {
+ QQmlPropertyPrivate::removeBinding(
+ _bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
+ }
}
if (bindingType == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
@@ -942,6 +1012,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
qmlBinding = QQmlPropertyBinding::create(bindingProperty, runtimeFunction, _scopeObject, context, currentQmlContext(), _bindingTarget, index);
}
sharedState.data()->allQPropertyBindings.push_back(DeferredQPropertyBinding {_bindingTarget, bindingProperty->coreIndex(), qmlBinding });
+
+ QQmlData *data = QQmlData::get(_bindingTarget, true);
+ data->setBindingBit(_bindingTarget, bindingProperty->coreIndex());
} else {
// When writing bindings to grouped properties implemented as value types,
// such as point.x: { someExpression; }, then the binding is installed on
@@ -1067,9 +1140,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
recordError(binding->valueLocation, tr("Cannot assign object type %1 with no default method").arg(QString::fromLatin1(createdSubObject->metaObject()->className())));
return false;
}
- qCWarning(lcQmlDefaultMethod) << "Assigning an object to a signal handler is deprecated."
- "Instead, create the object, give it an id, and call the desired slot from the signal handler."
- ;
+ qCWarning(lcQmlDefaultMethod) << "Assigning an object to a signal handler is deprecated. "
+ "Instead, create the object, give it an id, and call the desired slot "
+ "from the signal handler. The object is:" << createdSubObject;
QMetaMethod signalMethod = _qobject->metaObject()->method(bindingProperty->coreIndex());
if (!QMetaObject::checkConnectArgs(signalMethod, method)) {
@@ -1250,7 +1323,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
sharedState->allCreatedObjects.push(instance);
} else {
- const auto compilationUnit = typeRef->compilationUnit();
+ auto compilationUnit = typeRef->compilationUnit();
Q_ASSERT(compilationUnit);
typeName = compilationUnit->fileName();
// compilation unit is shared between root type and its inline component types
@@ -1261,19 +1334,42 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
}
if (!type.isInlineComponentType()) {
- QQmlObjectCreator subCreator(context, compilationUnit, sharedState.data(),
- isContextObject);
+ QQmlObjectCreator subCreator(
+ context, engine->handle()->executableCompilationUnit(
+ std::move(compilationUnit)),
+ sharedState.data(), isContextObject);
instance = subCreator.create();
if (!instance) {
errors += subCreator.errors;
return nullptr;
}
} else {
- int subObjectId = type.inlineComponentId();
- QScopedValueRollback<int> rollback {compilationUnit->icRoot, subObjectId};
- QQmlObjectCreator subCreator(context, compilationUnit, sharedState.data(),
- isContextObject);
- instance = subCreator.create(subObjectId, nullptr, nullptr, CreationFlags::InlineComponent);
+ QString subObjectName;
+ if (QString *icRootName = compilationUnit->icRootName.get()) {
+ subObjectName = type.elementName();
+ std::swap(*icRootName, subObjectName);
+ } else {
+ compilationUnit->icRootName = std::make_unique<QString>(type.elementName());
+ }
+
+ const auto guard = qScopeGuard([&] {
+ if (subObjectName.isEmpty())
+ compilationUnit->icRootName.reset();
+ else
+ std::swap(*compilationUnit->icRootName, subObjectName);
+ });
+
+ const int inlineComponentId
+ = compilationUnit->inlineComponentId(*compilationUnit->icRootName);
+ QQmlObjectCreator subCreator(
+ context,
+ engine->handle()->executableCompilationUnit(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
+ compilationUnit)),
+ sharedState.data(),
+ isContextObject);
+ instance = subCreator.create(
+ inlineComponentId, nullptr, nullptr, CreationFlags::InlineComponent);
if (!instance) {
errors += subCreator.errors;
return nullptr;
@@ -1326,7 +1422,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (customParser && obj->hasFlag(QV4::CompiledData::Object::HasCustomParserBindings)) {
customParser->engine = QQmlEnginePrivate::get(engine);
- customParser->imports = compilationUnit->typeNameCache.data();
+ customParser->imports = compilationUnit->typeNameCache().data();
QList<const QV4::CompiledData::Binding *> bindings;
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index);
@@ -1354,9 +1450,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QObject *scopeObject = instance;
qSwap(_scopeObject, scopeObject);
- Q_ASSERT(sharedState->allJavaScriptObjects);
- *sharedState->allJavaScriptObjects = QV4::QObjectWrapper::wrap(v4, instance);
- ++sharedState->allJavaScriptObjects;
+ Q_ASSERT(sharedState->allJavaScriptObjects.canTrack());
+ sharedState->allJavaScriptObjects.trackObject(v4, instance);
QV4::Scope valueScope(v4);
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
@@ -1438,17 +1533,27 @@ bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt)
while (!sharedState->allQPropertyBindings.isEmpty()) {
auto& [target, index, qmlBinding] = sharedState->allQPropertyBindings.first();
+
+ QQmlData *data = QQmlData::get(target);
+ if (!data || !data->hasBindingBit(index)) {
+ // The target property has been overwritten since we stashed the binding.
+ sharedState->allQPropertyBindings.pop_front();
+ continue;
+ }
+
QUntypedBindable bindable;
void *argv[] = { &bindable };
// allow interception
target->metaObject()->metacall(target, QMetaObject::BindableProperty, index, argv);
const bool success = bindable.setBinding(qmlBinding);
+ const auto bindingPrivateRefCount = QPropertyBindingPrivate::get(qmlBinding)->refCount();
+
// Only pop_front after setting the binding as the bindings are refcounted.
sharedState->allQPropertyBindings.pop_front();
// If the binding was actually not set, it's deleted now.
- if (success) {
+ if (success && bindingPrivateRefCount > 1) {
if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
auto jsExpression = qmlBindingPriv->jsExpression();
@@ -1747,3 +1852,15 @@ QQmlObjectCreatorRecursionWatcher::QQmlObjectCreatorRecursionWatcher(QQmlObjectC
, watcher(creator->sharedState.data())
{
}
+
+void ObjectInCreationGCAnchorList::trackObject(QV4::ExecutionEngine *engine, QObject *instance)
+{
+ *allJavaScriptObjects = QV4::QObjectWrapper::wrap(engine, instance);
+ // we have to handle the case where the gc is already running, but the scope is discarded
+ // before the collector runs again. In that case, rescanning won't help us. Thus, mark the
+ // object.
+ QV4::WriteBarrier::markCustom(engine, [this](QV4::MarkStack *ms) {
+ allJavaScriptObjects->heapObject()->mark(ms);
+ });
+ ++allJavaScriptObjects;
+}
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index c951072dd5..8b1c251e2b 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -84,14 +84,28 @@ struct DeferredQPropertyBinding {
QUntypedPropertyBinding binding;
};
-struct QQmlObjectCreatorSharedState : QQmlRefCount
+class ObjectInCreationGCAnchorList {
+public:
+ // this is a non owning view, rule of zero applies
+ ObjectInCreationGCAnchorList() = default;
+ ObjectInCreationGCAnchorList(const QV4::Scope &scope, int totalObjectCount)
+ {
+ allJavaScriptObjects = scope.alloc(totalObjectCount);
+ }
+ void trackObject(QV4::ExecutionEngine *engine, QObject *instance);
+ bool canTrack() const { return allJavaScriptObjects; }
+private:
+ QV4::Value *allJavaScriptObjects = nullptr; // pointer to vector on JS stack to reference JS wrappers during creation phase.
+};
+
+struct QQmlObjectCreatorSharedState final : QQmlRefCounted<QQmlObjectCreatorSharedState>
{
QQmlRefPointer<QQmlContextData> rootContext;
QQmlRefPointer<QQmlContextData> creationContext;
QFiniteStack<QQmlAbstractBinding::Ptr> allCreatedBindings;
QFiniteStack<QQmlParserStatus*> allParserStatusCallbacks;
QFiniteStack<QQmlGuard<QObject> > allCreatedObjects;
- QV4::Value *allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase.
+ ObjectInCreationGCAnchorList allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase.
QQmlComponentAttached *componentAttached;
QList<QQmlFinalizerHook *> finalizeHooks;
QQmlVmeProfiler profiler;
@@ -101,7 +115,7 @@ struct QQmlObjectCreatorSharedState : QQmlRefCount
bool hadTopLevelRequiredProperties;
};
-class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
+class Q_QML_EXPORT QQmlObjectCreator
{
Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator)
public:
@@ -120,6 +134,10 @@ public:
void beginPopulateDeferred(const QQmlRefPointer<QQmlContextData> &context);
void populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex,
const QV4::CompiledData::Binding *binding);
+ void populateDeferredInstance(QObject *outerObject, int deferredIndex,
+ int index, QObject *instance, QObject *bindingTarget,
+ const QQmlPropertyData *valueTypeProperty,
+ const QV4::CompiledData::Binding *binding = nullptr);
void finalizePopulateDeferred();
bool finalize(QQmlInstantiationInterrupt &interrupt);
@@ -144,6 +162,14 @@ public:
int index, QObject *parent,
const QQmlRefPointer<QQmlContextData> &context);
+ void removePendingBinding(QObject *target, int propertyIndex)
+ {
+ QList<DeferredQPropertyBinding> &pendingBindings = sharedState.data()->allQPropertyBindings;
+ pendingBindings.removeIf([&](const DeferredQPropertyBinding &deferred) {
+ return deferred.properyIndex == propertyIndex && deferred.target == target;
+ });
+ }
+
private:
QQmlObjectCreator(QQmlRefPointer<QQmlContextData> contextData,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
@@ -231,6 +257,10 @@ private:
void doPopulateDeferred(QObject *instance, int deferredIndex, Functor f)
{
QQmlData *declarativeData = QQmlData::get(instance);
+
+ // We're in the process of creating the object. We sure hope it's still alive.
+ Q_ASSERT(declarativeData && declarativeData->propertyCache);
+
QObject *bindingTarget = instance;
QQmlPropertyCache::ConstPtr cache = declarativeData->propertyCache;
@@ -240,8 +270,9 @@ private:
qt_ptr_swap(_scopeObject, scopeObject);
QV4::Scope valueScope(v4);
- QScopedValueRollback<QV4::Value*> jsObjectGuard(sharedState->allJavaScriptObjects,
- valueScope.alloc(compilationUnit->totalObjectCount()));
+ QScopedValueRollback<ObjectInCreationGCAnchorList> jsObjectGuard(
+ sharedState->allJavaScriptObjects,
+ ObjectInCreationGCAnchorList(valueScope, compilationUnit->totalObjectCount()));
Q_ASSERT(topLevelCreator);
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index cf51286677..2d0a78c6cb 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -5,8 +5,10 @@
#include <private/qqmlpropertycache_p.h>
#include <private/qqmldata_p.h>
#include <private/qqmlmetatype_p.h>
+
#include <private/qmetaobjectbuilder_p.h>
#include <qdebug.h>
+#include <QtCore/qpointer.h>
#include <QtCore/qset.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h
index 7b09566710..35e595ec15 100644
--- a/src/qml/qml/qqmlopenmetaobject_p.h
+++ b/src/qml/qml/qqmlopenmetaobject_p.h
@@ -28,11 +28,12 @@ QT_BEGIN_NAMESPACE
class QQmlEngine;
class QMetaPropertyBuilder;
class QQmlOpenMetaObjectTypePrivate;
-class Q_QML_PRIVATE_EXPORT QQmlOpenMetaObjectType : public QQmlRefCount
+class Q_QML_EXPORT QQmlOpenMetaObjectType final
+ : public QQmlRefCounted<QQmlOpenMetaObjectType>
{
public:
QQmlOpenMetaObjectType(const QMetaObject *base);
- ~QQmlOpenMetaObjectType() override;
+ ~QQmlOpenMetaObjectType();
void createProperties(const QVector<QByteArray> &names);
int createProperty(const QByteArray &name);
@@ -53,7 +54,7 @@ private:
};
class QQmlOpenMetaObjectPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlOpenMetaObject : public QAbstractDynamicMetaObject
+class Q_QML_EXPORT QQmlOpenMetaObject : public QAbstractDynamicMetaObject
{
public:
QQmlOpenMetaObject(QObject *, const QMetaObject * = nullptr);
diff --git a/src/qml/qml/qqmlplatform_p.h b/src/qml/qml/qqmlplatform_p.h
index 567a2f0f64..b8d2167fd5 100644
--- a/src/qml/qml/qqmlplatform_p.h
+++ b/src/qml/qml/qqmlplatform_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlPlatform : public QObject
+class Q_QML_EXPORT QQmlPlatform : public QObject
{
Q_OBJECT
Q_PROPERTY(QString os READ os CONSTANT)
@@ -42,6 +42,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlPlatform)
-
#endif // QQMLPLATFORM_P_H
diff --git a/src/qml/qml/qqmlpluginimporter.cpp b/src/qml/qml/qqmlpluginimporter.cpp
index 8f33e11649..24f12891b9 100644
--- a/src/qml/qml/qqmlpluginimporter.cpp
+++ b/src/qml/qml/qqmlpluginimporter.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlpluginimporter_p.h"
+#include "qqmlimport_p.h"
#include <private/qqmlextensionplugin_p.h>
#include <private/qqmltypeloader_p.h>
@@ -59,12 +60,6 @@ private:
Q_GLOBAL_STATIC(PluginMap, qmlPluginsById); // stores the uri and the PluginLoaders
-static QTypeRevision validVersion(QTypeRevision version = QTypeRevision())
-{
- // If the given version is invalid, return a valid but useless version to signal "It's OK".
- return version.isValid() ? version : QTypeRevision::fromMinorVersion(0);
-}
-
static QVector<QStaticPlugin> makePlugins()
{
QVector<QStaticPlugin> plugins;
@@ -217,7 +212,7 @@ QTypeRevision QQmlPluginImporter::importStaticPlugin(QObject *instance, const QS
if (!database->initializedPlugins.contains(pluginId))
finalizePlugin(instance, pluginId);
- return validVersion(importVersion);
+ return QQmlImports::validVersion(importVersion);
}
QTypeRevision QQmlPluginImporter::importDynamicPlugin(
@@ -326,7 +321,7 @@ QTypeRevision QQmlPluginImporter::importDynamicPlugin(
if (!engineInitialized)
finalizePlugin(instance, pluginId);
- return validVersion(importVersion);
+ return QQmlImports::validVersion(importVersion);
}
/*!
@@ -611,7 +606,7 @@ QTypeRevision QQmlPluginImporter::importPlugins() {
database->modulesForWhichPluginsHaveBeenLoaded.insert(moduleId);
}
- return validVersion(importVersion);
+ return QQmlImports::validVersion(importVersion);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 7d5f99ca5c..92c9765509 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -15,25 +15,26 @@
// We mean it.
//
-#include <QtQml/qtqmlglobal.h>
-#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qjsprimitivevalue.h>
+#include <QtQml/qjsvalue.h>
#include <QtQml/qqmllist.h>
+#include <QtQml/qqmlparserstatus.h>
#include <QtQml/qqmlpropertyvaluesource.h>
-#include <QtQml/qjsvalue.h>
+#include <QtQml/qtqmlglobal.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
#include <QtCore/qglobal.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qurl.h>
+#include <QtCore/qmetacontainer.h>
+#include <QtCore/qmetaobject.h>
#include <QtCore/qpointer.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvariant.h>
#include <QtCore/qversionnumber.h>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qmetacontainer.h>
-#include <QtCore/qdebug.h>
-
#include <functional>
-#include <type_traits>
#include <limits>
+#include <type_traits>
QT_BEGIN_NAMESPACE
@@ -81,6 +82,10 @@ class QQmlEngine;
class QQmlCustomParser;
class QQmlTypeNotAvailable;
+class QQmlV4Function;
+using QQmlV4FunctionPtr = QQmlV4Function *;
+using QQmlV4ExecutionEnginePtr = QV4::ExecutionEngine *;
+
template<class T>
QQmlCustomParser *qmlCreateCustomParser()
{
@@ -114,7 +119,7 @@ namespace QQmlPrivate
#endif
};
- enum class ConstructionMode
+ enum class SingletonConstructionMode
{
None,
Constructor,
@@ -139,18 +144,18 @@ namespace QQmlPrivate
};
template<typename T, typename WrapperT>
- constexpr ConstructionMode constructionMode()
+ constexpr SingletonConstructionMode singletonConstructionMode()
{
if constexpr (!std::is_base_of<QObject, T>::value)
- return ConstructionMode::None;
+ return SingletonConstructionMode::None;
if constexpr (!std::is_same_v<T, WrapperT> && HasSingletonFactory<T, WrapperT>::value)
- return ConstructionMode::FactoryWrapper;
+ return SingletonConstructionMode::FactoryWrapper;
if constexpr (std::is_default_constructible<T>::value)
- return ConstructionMode::Constructor;
+ return SingletonConstructionMode::Constructor;
if constexpr (HasSingletonFactory<T>::value)
- return ConstructionMode::Factory;
+ return SingletonConstructionMode::Factory;
- return ConstructionMode::None;
+ return SingletonConstructionMode::None;
}
template<typename>
@@ -168,16 +173,16 @@ namespace QQmlPrivate
template<typename T>
void createInto(void *memory, void *) { new (memory) QQmlElement<T>; }
- template<typename T, typename WrapperT, ConstructionMode Mode>
+ template<typename T, typename WrapperT, SingletonConstructionMode Mode>
QObject *createSingletonInstance(QQmlEngine *q, QJSEngine *j)
{
Q_UNUSED(q);
Q_UNUSED(j);
- if constexpr (Mode == ConstructionMode::Constructor)
+ if constexpr (Mode == SingletonConstructionMode::Constructor)
return new T;
- else if constexpr (Mode == ConstructionMode::Factory)
+ else if constexpr (Mode == SingletonConstructionMode::Factory)
return T::create(q, j);
- else if constexpr (Mode == ConstructionMode::FactoryWrapper)
+ else if constexpr (Mode == SingletonConstructionMode::FactoryWrapper)
return WrapperT::create(q, j);
else
return nullptr;
@@ -191,39 +196,43 @@ namespace QQmlPrivate
using CreateParentFunction = QObject *(*)(QObject *);
using CreateValueTypeFunction = QVariant (*)(const QJSValue &);
- template<typename T, typename WrapperT = T, ConstructionMode Mode = constructionMode<T, WrapperT>()>
+ template<typename T, typename WrapperT = T,
+ SingletonConstructionMode Mode = singletonConstructionMode<T, WrapperT>()>
struct Constructors;
template<typename T, typename WrapperT>
- struct Constructors<T, WrapperT, ConstructionMode::Constructor>
+ struct Constructors<T, WrapperT, SingletonConstructionMode::Constructor>
{
static constexpr CreateIntoFunction createInto
= QQmlPrivate::createInto<T>;
static constexpr CreateSingletonFunction createSingletonInstance
- = QQmlPrivate::createSingletonInstance<T, WrapperT, ConstructionMode::Constructor>;
+ = QQmlPrivate::createSingletonInstance<
+ T, WrapperT, SingletonConstructionMode::Constructor>;
};
template<typename T, typename WrapperT>
- struct Constructors<T, WrapperT, ConstructionMode::None>
+ struct Constructors<T, WrapperT, SingletonConstructionMode::None>
{
static constexpr CreateIntoFunction createInto = nullptr;
static constexpr CreateSingletonFunction createSingletonInstance = nullptr;
};
template<typename T, typename WrapperT>
- struct Constructors<T, WrapperT, ConstructionMode::Factory>
+ struct Constructors<T, WrapperT, SingletonConstructionMode::Factory>
{
static constexpr CreateIntoFunction createInto = nullptr;
static constexpr CreateSingletonFunction createSingletonInstance
- = QQmlPrivate::createSingletonInstance<T, WrapperT, ConstructionMode::Factory>;
+ = QQmlPrivate::createSingletonInstance<
+ T, WrapperT, SingletonConstructionMode::Factory>;
};
template<typename T, typename WrapperT>
- struct Constructors<T, WrapperT, ConstructionMode::FactoryWrapper>
+ struct Constructors<T, WrapperT, SingletonConstructionMode::FactoryWrapper>
{
static constexpr CreateIntoFunction createInto = nullptr;
static constexpr CreateSingletonFunction createSingletonInstance
- = QQmlPrivate::createSingletonInstance<T, WrapperT, ConstructionMode::FactoryWrapper>;
+ = QQmlPrivate::createSingletonInstance<
+ T, WrapperT, SingletonConstructionMode::FactoryWrapper>;
};
template<typename T,
@@ -361,8 +370,8 @@ namespace QQmlPrivate
struct Properties<Parent, void>
{
using Func = QQmlAttachedPropertiesFunc<QObject>;
- static const QMetaObject *staticMetaObject() { return nullptr; };
- static Func attachedPropertiesFunc() { return nullptr; };
+ static const QMetaObject *staticMetaObject() { return nullptr; }
+ static Func attachedPropertiesFunc() { return nullptr; }
};
using Type = typename std::conditional<
@@ -619,6 +628,7 @@ namespace QQmlPrivate
qintptr extraData;
};
+ QObject *thisObject() const;
QQmlEngine *qmlEngine() const;
QJSValue jsMetaType(int index) const;
@@ -640,7 +650,23 @@ namespace QQmlPrivate
QtMsgType type, const QString &message,
const QLoggingCategory *loggingCategory) const;
- QString objectToString(QObject *object) const;
+ QVariant constructValueType(
+ QMetaType resultMetaType, const QMetaObject *resultMetaObject,
+ int ctorIndex, void *ctorArg) const;
+
+ // Those are explicit arguments to the Date() ctor, not implicit coercions.
+ QDateTime constructDateTime(double timestamp) const;
+ QDateTime constructDateTime(const QString &string) const;
+ QDateTime constructDateTime(const QJSPrimitiveValue &arg) const
+ {
+ return arg.type() == QJSPrimitiveValue::String
+ ? constructDateTime(arg.toString())
+ : constructDateTime(arg.toDouble());
+ }
+
+ QDateTime constructDateTime(
+ double year, double month, double day = 1,
+ double hours = 0, double minutes = 0, double seconds = 0, double msecs = 0) const;
// All of these lookup functions should be used as follows:
//
@@ -679,6 +705,7 @@ namespace QQmlPrivate
void initLoadGlobalLookup(uint index) const;
bool loadScopeObjectPropertyLookup(uint index, void *target) const;
+ bool writeBackScopeObjectPropertyLookup(uint index, void *source) const;
void initLoadScopeObjectPropertyLookup(uint index, QMetaType type) const;
bool loadSingletonLookup(uint index, void *target) const;
@@ -691,12 +718,17 @@ namespace QQmlPrivate
void initLoadTypeLookup(uint index, uint importNamespace) const;
bool getObjectLookup(uint index, QObject *object, void *target) const;
+ bool writeBackObjectLookup(uint index, QObject *object, void *source) const;
void initGetObjectLookup(uint index, QObject *object, QMetaType type) const;
bool getValueLookup(uint index, void *value, void *target) const;
+ bool writeBackValueLookup(uint index, void *value, void *source) const;
void initGetValueLookup(uint index, const QMetaObject *metaObject, QMetaType type) const;
+ bool getEnumLookup(uint index, void *target) const;
+#if QT_QML_REMOVED_SINCE(6, 6)
bool getEnumLookup(uint index, int *target) const;
+#endif
void initGetEnumLookup(uint index, const QMetaObject *metaObject,
const char *enumerator, const char *enumValue) const;
@@ -707,21 +739,21 @@ namespace QQmlPrivate
void initSetValueLookup(uint index, const QMetaObject *metaObject, QMetaType type) const;
};
- struct TypedFunction {
- qintptr extraData;
- QMetaType returnType;
- QList<QMetaType> argumentTypes;
- void (*functionPtr)(const AOTCompiledContext *context, void *resultPtr, void **arguments);
+ struct AOTCompiledFunction {
+ int functionIndex;
+ int numArguments;
+ void (*signature)(QV4::ExecutableCompilationUnit *unit, QMetaType *argTypes);
+ void (*functionPtr)(const AOTCompiledContext *context, void **argv);
};
-#if QT_DEPRECATED_SINCE(6, 5)
- QT_DEPRECATED_VERSION_X(6, 5, "Use TypedFunction instead")
- typedef TypedFunction AOTCompiledFunction;
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X(6, 6, "Use AOTCompiledFunction instead")
+ typedef AOTCompiledFunction TypedFunction;
#endif
struct CachedQmlUnit {
const QV4::CompiledData::Unit *qmlData;
- const TypedFunction *aotCompiledFunctions;
+ const AOTCompiledFunction *aotCompiledFunctions;
void *unused2;
};
@@ -810,29 +842,6 @@ namespace QQmlPrivate
return qstrcmp(metaObject->classInfo(index).value(), "true") == 0;
}
- inline const char *classElementName(const QMetaObject *metaObject)
- {
- const char *elementName = classInfo(metaObject, "QML.Element");
- if (qstrcmp(elementName, "auto") == 0) {
- const char *strippedClassName = metaObject->className();
- for (const char *c = strippedClassName; *c != '\0'; c++) {
- if (*c == ':')
- strippedClassName = c + 1;
- }
-
- return strippedClassName;
- }
- if (qstrcmp(elementName, "anonymous") == 0)
- return nullptr;
-
- if (!elementName) {
- qWarning().nospace() << "Missing QML.Element class info \"" << elementName << "\""
- << " for " << metaObject->className();
- }
-
- return elementName;
- }
-
template<class T, class = std::void_t<>>
struct QmlExtended
{
@@ -893,6 +902,20 @@ namespace QQmlPrivate
&& bool(T::QmlIsUncreatable::yes);
};
+ template<class T, class = std::void_t<>>
+ struct QmlAnonymous
+ {
+ static constexpr bool Value = false;
+ };
+
+ template<class T>
+ struct QmlAnonymous<T, std::void_t<typename T::QmlIsAnonymous>>
+ {
+ static constexpr bool Value =
+ QmlTypeHasMarker<T, decltype(&T::qt_qmlMarker_anonymous)>::value
+ && bool(T::QmlIsAnonymous::yes);
+ };
+
template<class T, class = std::void_t<>>
struct QmlSingleton
@@ -929,7 +952,7 @@ namespace QQmlPrivate
};
template<class T>
- struct QmlInterface<T, std::void_t<typename T::QmlIsInterface>>
+ struct QmlInterface<T, std::void_t<typename T::QmlIsInterface, decltype(qobject_interface_iid<T *>())>>
{
static constexpr bool Value = bool(T::QmlIsInterface::yes);
};
@@ -946,39 +969,9 @@ namespace QQmlPrivate
static const QMetaObject *staticMetaObject() { return &T::staticMetaObject; }
};
- /*! \internal
- For singletons: check that there is a static create method. It is needed by the engine to
- initialize a singleton object in case T has no default constructor.
-
- Using a default constructor is preferred over the static create method.
- */
- template<class T, class = std::void_t<>>
- struct HasStaticCreate
- {
- static constexpr bool Value = false;
- };
-
- // std::is_member_pointer<&X::Y> returns false when Y is a static member, and
- // sfinae takes care of the existence Y being a member of X
- template<class T>
- struct HasStaticCreate<
- T,
- typename std::enable_if<!std::is_member_pointer<decltype(&T::create)>::value
- && std::is_invocable_r_v<T *, decltype(&T::create),
- QQmlEngine *, QJSEngine *>>::type>
- {
- static constexpr bool Value = true;
- };
-
template<class T>
struct QmlMetaType
{
- static constexpr bool hasAcceptableSingletonCtors()
- {
- return std::is_default_constructible_v<T> || HasStaticCreate<T>::Value;
- }
-
-
static constexpr bool hasAcceptableCtors()
{
if constexpr (!std::is_default_constructible_v<T>)
@@ -989,7 +982,7 @@ namespace QQmlPrivate
return std::is_copy_constructible_v<T>;
}
- static QMetaType self()
+ static constexpr QMetaType self()
{
if constexpr (std::is_base_of_v<QObject, T>)
return QMetaType::fromType<T*>();
@@ -997,7 +990,7 @@ namespace QQmlPrivate
return QMetaType::fromType<T>();
}
- static QMetaType list()
+ static constexpr QMetaType list()
{
if constexpr (std::is_base_of_v<QObject, T>)
return QMetaType::fromType<QQmlListProperty<T>>();
@@ -1005,13 +998,28 @@ namespace QQmlPrivate
return QMetaType::fromType<QList<T>>();
}
- static QMetaSequence sequence()
+ static constexpr QMetaSequence sequence()
{
if constexpr (std::is_base_of_v<QObject, T>)
return QMetaSequence();
else
return QMetaSequence::fromContainer<QList<T>>();
}
+
+ static constexpr int size()
+ {
+ return sizeof(T);
+ }
+ };
+
+ template<>
+ struct QmlMetaType<void>
+ {
+ static constexpr bool hasAcceptableCtors() { return true; }
+ static constexpr QMetaType self() { return QMetaType(); }
+ static constexpr QMetaType list() { return QMetaType(); }
+ static constexpr QMetaSequence sequence() { return QMetaSequence(); }
+ static constexpr int size() { return 0; }
};
template<typename T, typename E, typename WrapperT = T>
@@ -1052,7 +1060,7 @@ namespace QQmlPrivate
3,
QmlMetaType<T>::self(),
QmlMetaType<T>::list(),
- int(sizeof(T)),
+ QmlMetaType<T>::size(),
Constructors<T>::createInto,
nullptr,
ValueType<T, E>::create,
@@ -1144,8 +1152,16 @@ namespace QQmlPrivate
Q_QML_EXPORT void qmlRegistrationWarning(QmlRegistrationWarning warning, QMetaType type);
+ Q_QML_EXPORT QMetaType compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName);
+ Q_QML_EXPORT QMetaType compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName);
+
} // namespace QQmlPrivate
QT_END_NAMESPACE
+Q_DECLARE_OPAQUE_POINTER(QQmlV4FunctionPtr)
+Q_DECLARE_OPAQUE_POINTER(QQmlV4ExecutionEnginePtr)
+
#endif // QQMLPRIVATE_H
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index ad08f06b05..7c78fbb984 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -2,40 +2,35 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlproperty.h"
-#include "qqmlproperty_p.h"
-
-#include "qqmlboundsignal_p.h"
-#include "qqmlcontext.h"
-#include "qqmlboundsignal_p.h"
-#include "qqmlengine.h"
-#include "qqmlengine_p.h"
-#include "qqmldata_p.h"
-#include "qqmlstringconverters_p.h"
-
-#include "qqmlvmemetaobject_p.h"
-#include "qqmlvaluetypeproxybinding_p.h"
+
#include <private/qjsvalue_p.h>
-#include <private/qv4functionobject_p.h>
-#include <private/qv4qobjectwrapper_p.h>
+#include <private/qmetaobject_p.h>
+#include <private/qproperty_p.h>
+#include <private/qqmlboundsignal_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
+#include <private/qqmldata_p.h>
+#include <private/qqmlengine_p.h>
#include <private/qqmlirbuilder_p.h>
-#include <QtQml/private/qqmllist_p.h>
-
-#include <QStringList>
-#include <QVector>
-#include <private/qmetaobject_p.h>
+#include <private/qqmllist_p.h>
+#include <private/qqmlproperty_p.h>
+#include <private/qqmlsignalnames_p.h>
+#include <private/qqmlstringconverters_p.h>
+#include <private/qqmlvaluetypeproxybinding_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlpropertymap.h>
+
#include <QtCore/qdebug.h>
-#include <cmath>
-#include <QtQml/QQmlPropertyMap>
-#include <QtCore/private/qproperty_p.h>
#include <QtCore/qsequentialiterable.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qvector.h>
-Q_DECLARE_METATYPE(QList<int>)
-Q_DECLARE_METATYPE(QList<qreal>)
-Q_DECLARE_METATYPE(QList<bool>)
-Q_DECLARE_METATYPE(QList<QString>)
-Q_DECLARE_METATYPE(QList<QUrl>)
+#include <cmath>
QT_BEGIN_NAMESPACE
@@ -252,10 +247,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
// Types must begin with an uppercase letter (see checkRegistration()
// in qqmlmetatype.cpp for the enforcement of this).
if (typeNameCache && !pathName.isEmpty() && pathName.at(0).isUpper()) {
- QQmlTypeNameCache::Result r = typeNameCache->query(pathName);
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
+ QQmlTypeLoader *typeLoader = QQmlTypeLoader::get(enginePrivate);
+ QQmlTypeNameCache::Result r = typeNameCache->query(pathName, typeLoader);
if (r.isValid()) {
if (r.type.isValid()) {
- QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate);
if (!func) return; // Not an attachable type
@@ -267,12 +263,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
// TODO: Do we really _not_ want to query the namespaced types here?
r = typeNameCache->query<QQmlTypeNameCache::QueryNamespaced::No>(
- path.at(ii), r.importNamespace);
+ path.at(ii), r.importNamespace, typeLoader);
if (!r.type.isValid())
return; // Invalid type in namespace
- QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate);
if (!func)
return; // Not an attachable type
@@ -370,10 +365,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
QQmlData *ddata = QQmlData::get(currentObject, false);
auto findChangeSignal = [&](QStringView signalName) {
- const QString changed = QStringLiteral("Changed");
- if (signalName.endsWith(changed)) {
- const QStringView propName = signalName.first(signalName.size() - changed.size());
- const QQmlPropertyData *d = ddata->propertyCache->property(propName, currentObject, context);
+ if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(signalName)) {
+ const QQmlPropertyData *d =
+ ddata->propertyCache->property(*propName, currentObject, context);
while (d && d->isFunction())
d = ddata->propertyCache->overrideData(d);
@@ -386,20 +380,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
return false;
};
- const QString terminalString = terminal.toString();
- if (QmlIR::IRBuilder::isSignalPropertyName(terminalString)) {
- QString signalName = terminalString.mid(2);
- int firstNon_;
- int length = signalName.size();
- for (firstNon_ = 0; firstNon_ < length; ++firstNon_)
- if (signalName.at(firstNon_) != u'_')
- break;
- signalName[firstNon_] = signalName.at(firstNon_).toLower();
-
+ const auto findSignal = [&](const QString &signalName) {
if (ddata && ddata->propertyCache) {
// Try method
- const QQmlPropertyData *d = ddata->propertyCache->property(
- signalName, currentObject, context);
+ const QQmlPropertyData *d
+ = ddata->propertyCache->property(signalName, currentObject, context);
// ### Qt7: This code treats methods as signals. It should use d->isSignal().
// That would be a change in behavior, though. Right now you can construct a
@@ -410,13 +395,30 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
if (d) {
object = currentObject;
core = *d;
- return;
+ return true;
}
- if (findChangeSignal(signalName))
- return;
- } else if (findSignalInMetaObject(signalName.toUtf8())) {
+ return findChangeSignal(signalName);
+ }
+
+ return findSignalInMetaObject(signalName.toUtf8());
+ };
+
+ auto signalName = QQmlSignalNames::handlerNameToSignalName(terminal);
+ if (signalName) {
+ if (findSignal(*signalName))
return;
+ } else {
+ signalName = QQmlSignalNames::badHandlerNameToSignalName(terminal);
+ if (signalName) {
+ if (findSignal(*signalName)) {
+ qWarning()
+ << terminal
+ << "is not a properly capitalized signal handler name."
+ << QQmlSignalNames::signalNameToHandlerName(*signalName)
+ << "would be correct.";
+ return;
+ }
}
}
@@ -429,7 +431,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
if (!property->isFunction()) {
object = currentObject;
core = *property;
- nameCache = terminalString;
+ nameCache = terminal.toString();
return;
}
property = ddata->propertyCache->overrideData(property);
@@ -750,15 +752,7 @@ QString QQmlProperty::name() const
d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName);
} else if (type() & SignalProperty) {
// ### Qt7: Return the original signal name here. Do not prepend "on"
- QString name = QStringLiteral("on") + d->core.name(d->object);
- for (int i = 2, end = name.size(); i != end; ++i) {
- const QChar c = name.at(i);
- if (c != u'_') {
- name[i] = c.toUpper();
- break;
- }
- }
- d->nameCache = name;
+ d->nameCache = QQmlSignalNames::signalNameToHandlerName(d->core.name(d->object));
} else {
d->nameCache = d->core.name(d->object);
}
@@ -1231,14 +1225,9 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx,
v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
if (!ok)
return false;
- } else if (v.userType() != QMetaType::Int && v.userType() != QMetaType::UInt) {
- int enumMetaTypeId = QMetaType::fromName(
- QByteArray(menum.scope() + QByteArray("::") + menum.name())).id();
- if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData())
- return false;
- v = QVariant(*reinterpret_cast<const int *>(v.constData()));
}
- v.convert(QMetaType(QMetaType::Int));
+ if (!v.convert(prop.metaType())) // ### TODO: underlyingType might be faster?
+ return false;
}
// the status variable is changed by qt_metacall to indicate what it did
@@ -1384,6 +1373,18 @@ static ConvertAndAssignResult tryConvertAndAssign(
return {false, false};
}
+ if (variantMetaType == QMetaType::fromType<QJSValue>()) {
+ // Handle Qt.binding bindings here to avoid mistaken conversion below
+ const QJSValue &jsValue = get<QJSValue>(value);
+ const QV4::FunctionObject *f
+ = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue);
+ if (f && f->isBinding()) {
+ QV4::QObjectWrapper::setProperty(
+ f->engine(), object, &property, f->asReturnedValue());
+ return {true, true};
+ }
+ }
+
// common cases:
switch (propertyMetaType.id()) {
case QMetaType::Bool:
@@ -1429,14 +1430,15 @@ static ConvertAndAssignResult tryConvertAndAssign(
}
}
- QVariant converted(propertyMetaType);
- if (QQmlValueTypeProvider::createValueType(value, propertyMetaType, converted.data())
- || QMetaType::convert(value.metaType(), value.constData(),
- propertyMetaType, converted.data())) {
- return {true, property.writeProperty(object, converted.data(), flags)};
+ QVariant converted = QQmlValueTypeProvider::createValueType(value, propertyMetaType);
+ if (!converted.isValid()) {
+ converted = QVariant(propertyMetaType);
+ if (!QMetaType::convert(value.metaType(), value.constData(),
+ propertyMetaType, converted.data())) {
+ return {false, false};
+ }
}
-
- return {false, false};
+ return {true, property.writeProperty(object, converted.data(), flags)};
};
template<typename Op>
@@ -1559,18 +1561,26 @@ bool QQmlPropertyPrivate::write(
if (valueMetaObject.isNull())
return false;
- QQmlListProperty<void> prop;
+ QQmlListProperty<QObject> prop;
property.readProperty(object, &prop);
- if (!prop.clear)
+ if (!prop.clear || !prop.append)
return false;
- prop.clear(&prop);
+ const bool useNonsignalingListOps = prop.clear == &QQmlVMEMetaObject::list_clear
+ && prop.append == &QQmlVMEMetaObject::list_append;
+
+ auto propClear =
+ useNonsignalingListOps ? &QQmlVMEMetaObject::list_clear_nosignal : prop.clear;
+ auto propAppend =
+ useNonsignalingListOps ? &QQmlVMEMetaObject::list_append_nosignal : prop.append;
+
+ propClear(&prop);
const auto doAppend = [&](QObject *o) {
if (o && !QQmlMetaObject::canConvert(o, valueMetaObject))
o = nullptr;
- prop.append(&prop, o);
+ propAppend(&prop, o);
};
if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
@@ -1581,9 +1591,18 @@ bool QQmlPropertyPrivate::write(
const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value);
for (qsizetype ii = 0; ii < list.size(); ++ii)
doAppend(list.at(ii));
+ } else if (variantMetaType == QMetaType::fromType<QList<QVariant>>()) {
+ const QList<QVariant> &list
+ = *static_cast<const QList<QVariant> *>(value.constData());
+ for (const QVariant &entry : list)
+ doAppend(QQmlMetaType::toQObject(entry));
} else if (!iterateQObjectContainer(variantMetaType, value.data(), doAppend)) {
doAppend(QQmlMetaType::toQObject(value));
}
+ if (useNonsignalingListOps) {
+ Q_ASSERT(QQmlVMEMetaObject::get(object));
+ QQmlVMEResolvedList(&prop).activateSignal();
+ }
} else if (variantMetaType == propertyMetaType) {
QVariant v = value;
property.writeProperty(object, v.data(), flags);
@@ -1595,16 +1614,6 @@ bool QQmlPropertyPrivate::write(
sequence.addValue(list.data(), value.data());
property.writeProperty(object, list.data(), flags);
}
- } else if (variantMetaType == QMetaType::fromType<QJSValue>()) {
- QJSValue jsValue = qvariant_cast<QJSValue>(value);
- const QV4::FunctionObject *f
- = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue);
- if (f && f->isBinding()) {
- QV4::QObjectWrapper::setProperty(
- f->engine(), object, &property, f->asReturnedValue());
- return true;
- }
- return false;
} else if (enginePriv && propertyMetaType == QMetaType::fromType<QJSValue>()) {
// We can convert everything into a QJSValue if we have an engine.
QJSValue jsValue = QJSValuePrivate::fromReturnedValue(
@@ -1696,13 +1705,13 @@ bool QQmlPropertyPrivate::write(
if (!ok && QQmlMetaType::isInterface(propertyMetaType)) {
auto valueAsQObject = qvariant_cast<QObject *>(value);
- if (void *interface = valueAsQObject
+ if (void *iface = valueAsQObject
? valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyMetaType))
: nullptr;
- interface) {
+ iface) {
// this case can occur when object has an interface type
// and the variant contains a type implementing the interface
- return property.writeProperty(object, &interface, flags);
+ return property.writeProperty(object, &iface, flags);
}
}
@@ -1947,9 +1956,8 @@ QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const Q
// If no signal is found, but the signal is of the form "onBlahChanged",
// return the notify signal for the property "Blah"
- if (name.endsWith("Changed")) {
- QByteArray propName = name.mid(0, name.size() - 7);
- int propIdx = mo->indexOfProperty(propName.constData());
+ if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
+ int propIdx = mo->indexOfProperty(propName->constData());
if (propIdx >= 0) {
QMetaProperty prop = mo->property(propIdx);
if (prop.hasNotifySignal())
diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h
index 06693f8d0a..fd07547d60 100644
--- a/src/qml/qml/qqmlproperty_p.h
+++ b/src/qml/qml/qqmlproperty_p.h
@@ -26,6 +26,8 @@
#include <QtQml/qqmlengine.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlContext;
@@ -35,7 +37,7 @@ class QQmlMetaObject;
class QQmlAbstractBinding;
class QQmlBoundSignalExpression;
-class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount
+class Q_QML_EXPORT QQmlPropertyPrivate final : public QQmlRefCounted<QQmlPropertyPrivate>
{
public:
enum class InitFlag {
diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp
index 5c8325a425..c8a7e6256a 100644
--- a/src/qml/qml/qqmlpropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertybinding.cpp
@@ -16,6 +16,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
Q_LOGGING_CATEGORY(lcQQPropertyBinding, "qt.qml.propertybinding");
QUntypedPropertyBinding QQmlPropertyBinding::create(const QQmlPropertyData *pd, QV4::Function *function,
@@ -230,10 +232,17 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void
setIsUndefined(true);
//suspend binding evaluation state for reset and subsequent read
auto state = QtPrivate::suspendCurrentBindingStatus();
- prop.reset();
+ prop.reset(); // May re-allocate the bindingData
QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
QtPrivate::restoreBindingStatus(state);
writeBackCurrentValue(std::move(currentValue));
+
+ // Re-fetch binding data
+ bindingData = storage->bindingData(propertyDataPtr);
+ if (!bindingData)
+ bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
+ bindingDataPointer = QPropertyBindingDataPointer {bindingData};
+
// reattach the binding (without causing a new notification)
if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
qCWarning(lcQQPropertyBinding) << "Resetting " << prop.name() << "due to the binding becoming undefined caused a new binding to be installed\n"
@@ -245,7 +254,7 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void
firstObserver = bindingDataPointer.firstObserver();
bindingData->d_ref() = reinterpret_cast<quintptr>(this) | QtPrivate::QPropertyBindingData::BindingBit;
if (firstObserver)
- bindingDataPointer.setObservers(firstObserver.ptr);
+ prependObserver(firstObserver);
} else {
QQmlError qmlError;
auto location = jsExpression()->sourceLocation();
@@ -272,7 +281,7 @@ QString QQmlPropertyBinding::createBindingLoopErrorDescription()
Q_ASSERT(propertyData);
Q_ASSERT(!targetIndex().hasValueTypeIndex());
QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData, nullptr);
- return QStringLiteral(R"(QML %1: Binding loop detected for property "%2")").arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
+ return R"(QML %1: Binding loop detected for property "%2")"_L1.arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
}
void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
diff --git a/src/qml/qml/qqmlpropertybinding_p.h b/src/qml/qml/qqmlpropertybinding_p.h
index 50688fa245..840239285e 100644
--- a/src/qml/qml/qqmlpropertybinding_p.h
+++ b/src/qml/qml/qqmlpropertybinding_p.h
@@ -33,7 +33,7 @@ namespace QV4 {
class QQmlPropertyBinding;
class QQmlScriptString;
-class Q_QML_PRIVATE_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpression
+class Q_QML_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpression
{
bool mustCaptureBindableProperty() const final {return false;}
@@ -47,14 +47,14 @@ class Q_QML_PRIVATE_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpressi
inline QQmlPropertyBinding const *asBinding() const;
};
-class Q_QML_PRIVATE_EXPORT QQmlPropertyBindingJSForBoundFunction : public QQmlPropertyBindingJS
+class Q_QML_EXPORT QQmlPropertyBindingJSForBoundFunction : public QQmlPropertyBindingJS
{
public:
QV4::ReturnedValue evaluate(bool *isUndefined);
QV4::PersistentValue m_boundFunction;
};
-class Q_QML_PRIVATE_EXPORT QQmlPropertyBinding : public QPropertyBindingPrivate
+class Q_QML_EXPORT QQmlPropertyBinding : public QPropertyBindingPrivate
{
friend class QQmlPropertyBindingJS;
@@ -220,10 +220,10 @@ inline const QtPrivate::BindingFunctionVTable *bindingFunctionVTableForQQmlPrope
class QQmlTranslationPropertyBinding
{
public:
- static QUntypedPropertyBinding Q_QML_PRIVATE_EXPORT create(const QQmlPropertyData *pd,
+ static QUntypedPropertyBinding Q_QML_EXPORT create(const QQmlPropertyData *pd,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
const QV4::CompiledData::Binding *binding);
- static QUntypedPropertyBinding Q_QML_PRIVATE_EXPORT
+ static QUntypedPropertyBinding Q_QML_EXPORT
create(const QMetaType &pd,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
const QQmlTranslation &translationData);
@@ -286,19 +286,27 @@ bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
if (!hasBoundFunction()) {
Q_ASSERT(metaType.sizeOf() > 0);
- // No need to construct here. evaluate() expects uninitialized memory.
- const auto size = [&]() -> qsizetype {
+ using Tuple = std::tuple<qsizetype, bool, bool>;
+ const auto [size, needsConstruction, needsDestruction] = [&]() -> Tuple {
switch (type) {
- case QMetaType::QObjectStar: return sizeof(QObject *);
- case QMetaType::Bool: return sizeof(bool);
- case QMetaType::Int: return (sizeof(int));
- case QMetaType::Double: return (sizeof(double));
- case QMetaType::Float: return (sizeof(float));
- case QMetaType::QString: return (sizeof(QString));
- default: return metaType.sizeOf();
+ case QMetaType::QObjectStar: return Tuple(sizeof(QObject *), false, false);
+ case QMetaType::Bool: return Tuple(sizeof(bool), false, false);
+ case QMetaType::Int: return Tuple(sizeof(int), false, false);
+ case QMetaType::Double: return Tuple(sizeof(double), false, false);
+ case QMetaType::Float: return Tuple(sizeof(float), false, false);
+ case QMetaType::QString: return Tuple(sizeof(QString), true, true);
+ default: {
+ const auto flags = metaType.flags();
+ return Tuple(
+ metaType.sizeOf(),
+ flags & QMetaType::NeedsConstruction,
+ flags & QMetaType::NeedsDestruction);
+ }
}
}();
Q_ALLOCA_VAR(void, result, size);
+ if (needsConstruction)
+ metaType.construct(result);
const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
if (!handleErrorAndUndefined(evaluatedToUndefined))
@@ -326,10 +334,12 @@ bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
const bool hasChanged = !metaType.equals(result, dataPtr);
if (hasChanged) {
- metaType.destruct(dataPtr);
+ if (needsDestruction)
+ metaType.destruct(dataPtr);
metaType.construct(dataPtr, result);
}
- metaType.destruct(result);
+ if (needsDestruction)
+ metaType.destruct(result);
return hasChanged;
}
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 4f14009cd1..a225f94a3f 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -10,6 +10,7 @@
#include <private/qmetaobject_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <private/qqmlpropertycachemethodarguments_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <private/qv4value_p.h>
@@ -53,17 +54,13 @@ QQmlPropertyData::flagsForProperty(const QMetaProperty &p)
const QMetaType metaType = p.metaType();
int propType = metaType.id();
if (p.isEnumType()) {
- flags.type = QQmlPropertyData::Flags::EnumType;
+ flags.setType(QQmlPropertyData::Flags::EnumType);
} else if (metaType.flags() & QMetaType::PointerToQObject) {
- flags.type = QQmlPropertyData::Flags::QObjectDerivedType;
+ flags.setType(QQmlPropertyData::Flags::QObjectDerivedType);
} else if (propType == QMetaType::QVariant) {
- flags.type = QQmlPropertyData::Flags::QVariantType;
- } else if (propType < static_cast<int>(QMetaType::User)) {
- // nothing to do
- } else if (propType == qMetaTypeId<QJSValue>()) {
- flags.type = QQmlPropertyData::Flags::QJSValueType;
+ flags.setType(QQmlPropertyData::Flags::QVariantType);
} else if (metaType.flags() & QMetaType::IsQmlList) {
- flags.type = QQmlPropertyData::Flags::QListType;
+ flags.setType(QQmlPropertyData::Flags::QListType);
}
return flags;
@@ -83,28 +80,44 @@ void QQmlPropertyData::load(const QMetaProperty &p)
void QQmlPropertyData::load(const QMetaMethod &m)
{
setCoreIndex(m.methodIndex());
- setArguments(nullptr);
+ m_flags.setType(Flags::FunctionType);
- setPropType(m.returnMetaType());
+ // We need to set the constructor, signal, constant, arguments, V4Function, cloned flags.
+ // These are specific to methods and change with each method.
+ // The same QQmlPropertyData may be loaded with multiple methods in sequence.
- m_flags.type = Flags::FunctionType;
- if (m.methodType() == QMetaMethod::Signal) {
+ switch (m.methodType()) {
+ case QMetaMethod::Signal:
m_flags.setIsSignal(true);
- } else if (m.methodType() == QMetaMethod::Constructor) {
+ m_flags.setIsConstructor(false);
+ setPropType(m.returnMetaType());
+ break;
+ case QMetaMethod::Constructor:
+ m_flags.setIsSignal(false);
m_flags.setIsConstructor(true);
setPropType(QMetaType::fromType<QObject *>());
+ break;
+ default:
+ m_flags.setIsSignal(false);
+ m_flags.setIsConstructor(false);
+ setPropType(m.returnMetaType());
+ break;
}
+
m_flags.setIsConstant(m.isConst());
const int paramCount = m.parameterCount();
if (paramCount) {
m_flags.setHasArguments(true);
- if ((paramCount == 1) && (m.parameterMetaType(0) == QMetaType::fromType<QQmlV4Function *>()))
- m_flags.setIsV4Function(true);
+ m_flags.setIsV4Function(
+ paramCount == 1 &&
+ m.parameterMetaType(0) == QMetaType::fromType<QQmlV4FunctionPtr>());
+ } else {
+ m_flags.setHasArguments(false);
+ m_flags.setIsV4Function(false);
}
- if (m.attributes() & QMetaMethod::Cloned)
- m_flags.setIsCloned(true);
+ m_flags.setIsCloned(m.attributes() & QMetaMethod::Cloned);
Q_ASSERT(m.revision() <= std::numeric_limits<quint16>::max());
setRevision(QTypeRevision::fromEncodedVersion(m.revision()));
@@ -249,8 +262,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
int signalHandlerIndex = signalHandlerIndexCache.size();
signalHandlerIndexCache.append(handler);
- QString handlerName = QLatin1String("on") + name;
- handlerName[2] = handlerName.at(2).toUpper();
+ const QString handlerName = QQmlSignalNames::signalNameToHandlerName(name);
setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex);
setNamedProperty(handlerName, signalHandlerIndex + signalOffset(),
@@ -352,6 +364,18 @@ QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject,
return rv;
}
+static QHashedString signalNameToHandlerName(const QHashedString &methodName)
+{
+ return QQmlSignalNames::signalNameToHandlerName(methodName);
+}
+
+static QHashedString signalNameToHandlerName(const QHashedCStringRef &methodName)
+{
+ return QQmlSignalNames::signalNameToHandlerName(
+ QLatin1StringView{ methodName.constData(), methodName.length() });
+}
+
+
void QQmlPropertyCache::append(const QMetaObject *metaObject,
QTypeRevision typeVersion,
QQmlPropertyData::Flags propertyFlags,
@@ -440,49 +464,29 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
QQmlPropertyData *old = nullptr;
- if (utf8) {
- QHashedString methodName(QString::fromUtf8(rawName, cptr - rawName));
+ const auto doSetNamedProperty = [&](const auto &methodName) {
if (StringCache::mapped_type *it = stringCache.value(methodName)) {
if (handleOverride(methodName, data, (old = it->second)) == InvalidOverride)
- continue;
+ return;
}
- setNamedProperty(methodName, ii, data);
- if (data->isSignal()) {
- QHashedString on(QLatin1String("on") % methodName.at(0).toUpper() % QStringView{methodName}.mid(1));
- setNamedProperty(on, ii, sigdata);
- ++signalHandlerIndex;
- }
- } else {
- QHashedCStringRef methodName(rawName, cptr - rawName);
- if (StringCache::mapped_type *it = stringCache.value(methodName)) {
- if (handleOverride(methodName, data, (old = it->second)) == InvalidOverride)
- continue;
- }
setNamedProperty(methodName, ii, data);
if (data->isSignal()) {
- int length = methodName.length();
-
- QVarLengthArray<char, 128> str(length+3);
- str[0] = 'o';
- str[1] = 'n';
- str[2] = QtMiscUtils::toAsciiUpper(rawName[0]);
- if (length > 1)
- memcpy(&str[3], &rawName[1], length - 1);
- str[length + 2] = '\0';
-
- QHashedString on(QString::fromLatin1(str.data()));
- setNamedProperty(on, ii, data);
+
+ // TODO: Remove this once we can. Signals should not be overridable.
+ if (!utf8)
+ data->m_flags.setIsOverridableSignal(true);
+
+ setNamedProperty(signalNameToHandlerName(methodName), ii, sigdata);
++signalHandlerIndex;
}
- }
+ };
- if (old) {
- // We only overload methods in the same class, exactly like C++
- if (old->isFunction() && old->coreIndex() >= methodOffset)
- data->m_flags.setIsOverload(true);
- }
+ if (utf8)
+ doSetNamedProperty(QHashedString(QString::fromUtf8(rawName, cptr - rawName)));
+ else
+ doSetNamedProperty(QHashedCStringRef(rawName, cptr - rawName));
}
int propCount = metaObject->propertyCount();
@@ -1186,7 +1190,7 @@ int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor)
} // anonymous namespace
-static_assert(QMetaObjectPrivate::OutputRevision == 11, "Check and adjust determineMetaObjectSizes");
+static_assert(QMetaObjectPrivate::OutputRevision == 12, "Check and adjust determineMetaObjectSizes");
bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount,
int *stringCount)
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index eb88cc43a2..7ff499460d 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -40,9 +40,9 @@ class QQmlVMEMetaObject;
class QQmlMetaObjectPointer
{
public:
- QQmlMetaObjectPointer() = default;
+ Q_NODISCARD_CTOR QQmlMetaObjectPointer() = default;
- QQmlMetaObjectPointer(const QMetaObject *staticMetaObject)
+ Q_NODISCARD_CTOR QQmlMetaObjectPointer(const QMetaObject *staticMetaObject)
: d(quintptr(staticMetaObject))
{
Q_ASSERT((d.loadRelaxed() & Shared) == 0);
@@ -57,7 +57,7 @@ public:
private:
friend class QQmlPropertyCache;
- QQmlMetaObjectPointer(const QQmlMetaObjectPointer &other)
+ Q_NODISCARD_CTOR QQmlMetaObjectPointer(const QQmlMetaObjectPointer &other)
: d(other.d.loadRelaxed())
{
// other has to survive until this ctor is done. So d cannot disappear before.
@@ -105,7 +105,7 @@ private:
Shared = 1
};
- struct SharedHolder : public QQmlRefCount
+ struct SharedHolder final : public QQmlRefCounted<SharedHolder>
{
Q_DISABLE_COPY_MOVE(SharedHolder)
SharedHolder(QMetaObject *shared) : metaObject(shared) {}
@@ -116,7 +116,8 @@ private:
mutable QBasicAtomicInteger<quintptr> d = 0;
};
-class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount
+class Q_QML_EXPORT QQmlPropertyCache final
+ : public QQmlRefCounted<QQmlPropertyCache>
{
public:
using Ptr = QQmlRefPointer<QQmlPropertyCache>;
@@ -135,7 +136,7 @@ public:
const QMetaObject *, QTypeRevision metaObjectRevision = QTypeRevision::zero());
QQmlPropertyCache() = default;
- ~QQmlPropertyCache() override;
+ ~QQmlPropertyCache();
void update(const QMetaObject *);
void invalidate(const QMetaObject *);
@@ -232,7 +233,7 @@ private:
friend class QQmlCompiler;
template <typename T> friend class QQmlPropertyCacheCreator;
template <typename T> friend class QQmlPropertyCacheAliasCreator;
- friend class QQmlComponentAndAliasResolver;
+ template <typename T> friend class QQmlComponentAndAliasResolver;
friend class QQmlMetaObject;
QQmlPropertyCache(const QQmlMetaObjectPointer &metaObject) : _metaObject(metaObject) {}
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index 4bc903c22e..06b405c7e4 100644
--- a/src/qml/qml/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -9,70 +9,54 @@ QT_BEGIN_NAMESPACE
QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0);
-
-QMetaType QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(QV4::CompiledData::BuiltinType type)
+template<typename BaseNameHandler, typename FailHandler>
+auto processUrlForClassName(
+ const QUrl &url, BaseNameHandler &&baseNameHandler, FailHandler &&failHandler)
{
- switch (type) {
- case QV4::CompiledData::BuiltinType::Var: return QMetaType::fromType<QVariant>();
- case QV4::CompiledData::BuiltinType::Int: return QMetaType::fromType<int>();
- case QV4::CompiledData::BuiltinType::Bool: return QMetaType::fromType<bool>();
- case QV4::CompiledData::BuiltinType::Real: return QMetaType::fromType<qreal>();
- case QV4::CompiledData::BuiltinType::String: return QMetaType::fromType<QString>();
- case QV4::CompiledData::BuiltinType::Url: return QMetaType::fromType<QUrl>();
- case QV4::CompiledData::BuiltinType::Time: return QMetaType::fromType<QTime>();
- case QV4::CompiledData::BuiltinType::Date: return QMetaType::fromType<QDate>();
- case QV4::CompiledData::BuiltinType::DateTime: return QMetaType::fromType<QDateTime>();
- case QV4::CompiledData::BuiltinType::Rect: return QMetaType::fromType<QRectF>();
- case QV4::CompiledData::BuiltinType::Point: return QMetaType::fromType<QPointF>();
- case QV4::CompiledData::BuiltinType::Size: return QMetaType::fromType<QSizeF>();
- case QV4::CompiledData::BuiltinType::InvalidBuiltin: break;
- };
- return QMetaType {};
+ const QString path = url.path();
+
+ // Not a reusable type if we don't have an absolute Url
+ const qsizetype lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ if (lastSlash <= -1)
+ return failHandler();
+
+ // ### this might not be correct for .ui.qml files
+ const QStringView baseName = QStringView{path}.mid(lastSlash + 1, path.size() - lastSlash - 5);
+
+ // Not a reusable type if it doesn't start with a upper case letter.
+ return (!baseName.isEmpty() && baseName.at(0).isUpper())
+ ? baseNameHandler(baseName)
+ : failHandler();
}
-QMetaType QQmlPropertyCacheCreatorBase::listTypeForPropertyType(QV4::CompiledData::BuiltinType type)
+bool QQmlPropertyCacheCreatorBase::canCreateClassNameTypeByUrl(const QUrl &url)
{
- switch (type) {
- case QV4::CompiledData::BuiltinType::Var: return QMetaType::fromType<QList<QVariant>>();
- case QV4::CompiledData::BuiltinType::Int: return QMetaType::fromType<QList<int>>();
- case QV4::CompiledData::BuiltinType::Bool: return QMetaType::fromType<QList<bool>>();
- case QV4::CompiledData::BuiltinType::Real: return QMetaType::fromType<QList<qreal>>();
- case QV4::CompiledData::BuiltinType::String: return QMetaType::fromType<QList<QString>>();
- case QV4::CompiledData::BuiltinType::Url: return QMetaType::fromType<QList<QUrl>>();
- case QV4::CompiledData::BuiltinType::Time: return QMetaType::fromType<QList<QTime>>();
- case QV4::CompiledData::BuiltinType::Date: return QMetaType::fromType<QList<QDate>>();
- case QV4::CompiledData::BuiltinType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
- case QV4::CompiledData::BuiltinType::Rect: return QMetaType::fromType<QList<QRectF>>();
- case QV4::CompiledData::BuiltinType::Point: return QMetaType::fromType<QList<QPointF>>();
- case QV4::CompiledData::BuiltinType::Size: return QMetaType::fromType<QList<QSizeF>>();
- case QV4::CompiledData::BuiltinType::InvalidBuiltin: break;
- };
- return QMetaType {};
+ return processUrlForClassName(url, [](QStringView) {
+ return true;
+ }, []() {
+ return false;
+ });
}
QByteArray QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(const QUrl &url)
{
- const QString path = url.path();
- int lastSlash = path.lastIndexOf(QLatin1Char('/'));
- // Not a reusable type if we don't have an absolute Url
- if (lastSlash <= -1)
- return QByteArray();
- // ### this might not be correct for .ui.qml files
- const QStringView nameBase = QStringView{path}.mid(lastSlash + 1, path.size() - lastSlash - 5);
- // Not a reusable type if it doesn't start with a upper case letter.
- if (nameBase.isEmpty() || !nameBase.at(0).isUpper())
- return QByteArray();
- return nameBase.toUtf8() + "_QMLTYPE_" +
- QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
+ return processUrlForClassName(url, [](QStringView nameBase) {
+ return nameBase.toUtf8() + QByteArray("_QMLTYPE_");
+ }, []() {
+ return QByteArray("ANON_QML_TYPE_");
+ }) + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
}
-QByteArray QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(const QUrl &baseUrl, int icId)
+QByteArray QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(
+ const QUrl &baseUrl, const QString &name)
{
- QByteArray baseName = createClassNameTypeByUrl(baseUrl);
- if (baseName.isEmpty())
- baseName = QByteArray("ANON_QML_IC_") + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
- baseName += "_" + QByteArray::number(icId);
- return baseName;
+ QByteArray baseName = processUrlForClassName(baseUrl, [](QStringView nameBase) {
+ return QByteArray(nameBase.toUtf8() + "_QMLTYPE_");
+ }, []() {
+ return QByteArray("ANON_QML_IC_");
+ });
+ return baseName + name.toUtf8() + '_'
+ + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
}
QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(
@@ -113,8 +97,19 @@ QQmlPropertyCache::ConstPtr QQmlBindingInstantiationContext::instantiatingProper
if (instantiatingProperty->isQObject()) {
// rawPropertyCacheForType assumes a given unspecified version means "any version".
// There is another overload that takes no version, which we shall not use here.
- return QQmlMetaType::rawPropertyCacheForType(instantiatingProperty->propType(),
+ auto result = QQmlMetaType::rawPropertyCacheForType(instantiatingProperty->propType(),
instantiatingProperty->typeVersion());
+ if (result)
+ return result;
+ /* We might end up here if there's a grouped property, and the type hasn't been registered.
+ Still try to get a property cache, as long as the type of the property is well-behaved
+ (i.e., not dynamic)*/
+ if (auto metaObject = instantiatingProperty->propType().metaObject(); metaObject) {
+ // we'll warn about dynamic meta-object later in the property validator
+ if (!(QMetaObjectPrivate::get(metaObject)->flags & DynamicMetaObject))
+ return QQmlMetaType::propertyCache(metaObject);
+ }
+ // fall through intentional
} else if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(instantiatingProperty->propType())) {
return QQmlMetaType::propertyCache(vtmo, instantiatingProperty->typeVersion());
}
@@ -131,12 +126,7 @@ void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(
if (propertyCaches->at(groupPropertyObjectIndex))
continue;
- if (pendingBinding.instantiatingPropertyName.isEmpty()) {
- // Generalized group property.
- auto cache = propertyCaches->at(pendingBinding.referencingObjectIndex);
- propertyCaches->set(groupPropertyObjectIndex, cache);
- continue;
- }
+ Q_ASSERT(!pendingBinding.instantiatingPropertyName.isEmpty());
if (!pendingBinding.referencingObjectPropertyCache) {
pendingBinding.referencingObjectPropertyCache
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index e58d8ece1b..4d49ca6ed4 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -21,8 +21,14 @@
#include <private/qqmltypedata_p.h>
#include <private/inlinecomponentutils_p.h>
#include <private/qqmlsourcecoordinate_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <QScopedValueRollback>
+
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
+
#include <vector>
QT_BEGIN_NAMESPACE
@@ -66,12 +72,62 @@ struct QQmlPropertyCacheCreatorBase
public:
static QAtomicInt Q_AUTOTEST_EXPORT classIndexCounter;
- static QMetaType metaTypeForPropertyType(QV4::CompiledData::BuiltinType type);
- static QMetaType listTypeForPropertyType(QV4::CompiledData::BuiltinType type);
+ static QMetaType metaTypeForPropertyType(QV4::CompiledData::CommonType type)
+ {
+ switch (type) {
+ case QV4::CompiledData::CommonType::Void: return QMetaType();
+ case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QVariant>();
+ case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<int>();
+ case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<bool>();
+ case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<qreal>();
+ case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QString>();
+ case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QUrl>();
+ case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QTime>();
+ case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QDate>();
+ case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QDateTime>();
+#if QT_CONFIG(regularexpression)
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QRegularExpression>();
+#else
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType();
+#endif
+ case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QRectF>();
+ case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QPointF>();
+ case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QSizeF>();
+ case QV4::CompiledData::CommonType::Invalid: break;
+ };
+ return QMetaType {};
+ }
+
+ static QMetaType listTypeForPropertyType(QV4::CompiledData::CommonType type)
+ {
+ switch (type) {
+ case QV4::CompiledData::CommonType::Void: return QMetaType();
+ case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QList<QVariant>>();
+ case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<QList<int>>();
+ case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<QList<bool>>();
+ case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<QList<qreal>>();
+ case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QList<QString>>();
+ case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QList<QUrl>>();
+ case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QList<QTime>>();
+ case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QList<QDate>>();
+ case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
+#if QT_CONFIG(regularexpression)
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QList<QRegularExpression>>();
+#else
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType();
+#endif
+ case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QList<QRectF>>();
+ case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QList<QPointF>>();
+ case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QList<QSizeF>>();
+ case QV4::CompiledData::CommonType::Invalid: break;
+ };
+ return QMetaType {};
+ }
+ static bool canCreateClassNameTypeByUrl(const QUrl &url);
static QByteArray createClassNameTypeByUrl(const QUrl &url);
- static QByteArray createClassNameForInlineComponent(const QUrl &baseUrl, int icId);
+ static QByteArray createClassNameForInlineComponent(const QUrl &baseUrl, const QString &name);
struct IncrementalResult {
// valid if and only if an error occurred
@@ -115,14 +171,6 @@ public:
*/
QQmlError verifyNoICCycle();
- /*!
- \internal
- Fills the property caches for the CompiledObjects by
- calling buildMetaObjectsIncrementally until it can no
- longer resume.
- */
- QQmlError buildMetaObjects();
-
enum class VMEMetaObjectIsRequired {
Maybe,
Always
@@ -165,7 +213,7 @@ inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlP
, typeClassName(typeClassName)
, currentRoot(-1)
{
- propertyCaches->resize(objectContainer->objectCount());
+ propertyCaches->resetAndResize(objectContainer->objectCount());
using namespace icutils;
@@ -179,7 +227,7 @@ inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlP
}
// create a graph on inline components referencing inline components
- std::vector<Node> nodes;
+ std::vector<icutils::Node> nodes;
nodes.resize(allICs.size());
std::iota(nodes.begin(), nodes.end(), 0);
AdjacencyList adjacencyList;
@@ -233,20 +281,6 @@ QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectsIncrementally()
}
template <typename ObjectContainer>
-inline QQmlError
-QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
-{
- QQmlError error = verifyNoICCycle();
- if (error.isValid())
- return error;
- QQmlPropertyCacheCreatorBase::IncrementalResult result;
- do {
- result = buildMetaObjectsIncrementally();
- } while (result.canResume);
- return result.error;
-}
-
-template <typename ObjectContainer>
inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired)
{
auto isAddressable = [](const QUrl &url) {
@@ -277,7 +311,10 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
QQmlPropertyCache::ConstPtr baseTypeCache = typeRef->createPropertyCache();
- QQmlError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
+ QQmlError error = baseTypeCache
+ ? createMetaObject(context.referencingObjectIndex, obj, baseTypeCache)
+ : qQmlCompileError(binding->location, QQmlPropertyCacheCreatorBase::tr(
+ "Type cannot be used for 'on' assignment"));
if (error.isValid())
return error;
}
@@ -367,7 +404,13 @@ inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::pr
}
}
- return typeRef->createPropertyCache();
+ if (QQmlPropertyCache::ConstPtr propertyCache = typeRef->createPropertyCache())
+ return propertyCache;
+ *error = qQmlCompileError(
+ obj->location,
+ QQmlPropertyCacheCreatorBase::tr("Type '%1' cannot declare new members.")
+ .arg(stringAt(obj->inheritedTypeNameIndex)));
+ return nullptr;
} else if (const QV4::CompiledData::Binding *binding = context.instantiatingBinding) {
if (binding->isAttachedProperty()) {
auto *typeRef = objectContainer->resolvedType(
@@ -375,8 +418,9 @@ inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::pr
Q_ASSERT(typeRef);
QQmlType qmltype = typeRef->type();
if (!qmltype.isValid()) {
- imports->resolveType(stringAt(binding->propertyNameIndex),
- &qmltype, nullptr, nullptr, nullptr);
+ imports->resolveType(
+ QQmlTypeLoader::get(enginePrivate), stringAt(binding->propertyNameIndex),
+ &qmltype, nullptr, nullptr);
}
const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
@@ -385,23 +429,6 @@ inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::pr
return nullptr;
}
return QQmlMetaType::propertyCache(attachedMo);
- } else if (binding->isGroupProperty()) {
- const auto *obj = objectContainer->objectAt(binding->value.objectIndex);
- if (!stringAt(obj->inheritedTypeNameIndex).isEmpty())
- return nullptr;
-
- for (int i = 0, end = objectContainer->objectCount(); i != end; ++i) {
- const auto *ext = objectContainer->objectAt(i);
- if (ext->idNameIndex != binding->propertyNameIndex)
- continue;
-
- if (ext->inheritedTypeNameIndex == 0)
- return nullptr;
-
- QQmlBindingInstantiationContext pendingContext(i, &(*binding), QString(), nullptr);
- pendingGroupPropertyBindings->append(pendingContext);
- return nullptr;
- }
}
}
return nullptr;
@@ -472,8 +499,13 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
// For property change signal override detection.
// We prepopulate a set of signal names which already exist in the object,
// and throw an error if there is a signal/method defined as an override.
- QSet<QString> seenSignals;
- seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
+ // TODO: Remove AllowOverride once we can. No override should be allowed.
+ enum class AllowOverride { No, Yes };
+ QHash<QString, AllowOverride> seenSignals {
+ { QStringLiteral("destroyed"), AllowOverride::No },
+ { QStringLiteral("parentChanged"), AllowOverride::No },
+ { QStringLiteral("objectNameChanged"), AllowOverride::No }
+ };
const QQmlPropertyCache *parentCache = cache.data();
while ((parentCache = parentCache->parent().data())) {
if (int pSigCount = parentCache->signalCount()) {
@@ -484,7 +516,15 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
iter != parentCache->stringCache.end(); ++iter) {
if (currPSig == (*iter).second) {
- seenSignals.insert(iter.key());
+ if (currPSig->isOverridableSignal()) {
+ const qsizetype oldSize = seenSignals.size();
+ AllowOverride &entry = seenSignals[iter.key()];
+ if (seenSignals.size() != oldSize)
+ entry = AllowOverride::Yes;
+ } else {
+ seenSignals[iter.key()] = AllowOverride::No;
+ }
+
break;
}
}
@@ -498,8 +538,9 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
for ( ; p != pend; ++p) {
auto flags = QQmlPropertyData::defaultSignalFlags();
- QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed");
- seenSignals.insert(changedSigName);
+ const QString changedSigName =
+ QQmlSignalNames::propertyNameToChangedSignalName(stringAt(p->nameIndex));
+ seenSignals[changedSigName] = AllowOverride::No;
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
}
@@ -509,8 +550,9 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
for ( ; a != aend; ++a) {
auto flags = QQmlPropertyData::defaultSignalFlags();
- QString changedSigName = stringAt(a->nameIndex()) + QLatin1String("Changed");
- seenSignals.insert(changedSigName);
+ const QString changedSigName =
+ QQmlSignalNames::propertyNameToChangedSignalName(stringAt(a->nameIndex()));
+ seenSignals[changedSigName] = AllowOverride::No;
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
}
@@ -562,10 +604,26 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
flags.setHasArguments(true);
QString signalName = stringAt(s->nameIndex);
- if (seenSignals.contains(signalName))
- return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
- seenSignals.insert(signalName);
-
+ const auto it = seenSignals.find(signalName);
+ if (it == seenSignals.end()) {
+ seenSignals[signalName] = AllowOverride::No;
+ } else {
+ // TODO: Remove the AllowOverride::Yes branch once we can.
+ QQmlError message = qQmlCompileError(
+ s->location,
+ QQmlPropertyCacheCreatorBase::tr(
+ "Duplicate signal name: "
+ "invalid override of property change signal or superclass signal"));
+ switch (*it) {
+ case AllowOverride::No:
+ return message;
+ case AllowOverride::Yes:
+ message.setUrl(objectContainer->url());
+ enginePrivate->warning(message);
+ *it = AllowOverride::No; // No further overriding allowed.
+ break;
+ }
+ }
cache->appendSignal(signalName, flags, effectiveMethodIndex++,
paramCount?paramTypes.constData():nullptr, names);
}
@@ -578,8 +636,23 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
auto flags = QQmlPropertyData::defaultSlotFlags();
const QString slotName = stringAt(function->nameIndex);
- if (seenSignals.contains(slotName))
- return qQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal"));
+ const auto it = seenSignals.constFind(slotName);
+ if (it != seenSignals.constEnd()) {
+ // TODO: Remove the AllowOverride::Yes branch once we can.
+ QQmlError message = qQmlCompileError(
+ function->location,
+ QQmlPropertyCacheCreatorBase::tr(
+ "Duplicate method name: "
+ "invalid override of property change signal or superclass signal"));
+ switch (*it) {
+ case AllowOverride::No:
+ return message;
+ case AllowOverride::Yes:
+ message.setUrl(objectContainer->url());
+ enginePrivate->warning(message);
+ break;
+ }
+ }
// Note: we don't append slotName to the seenSignals list, since we don't
// protect against overriding change signals or methods with properties.
@@ -614,58 +687,53 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
QTypeRevision propertyTypeVersion = QTypeRevision::zero();
QQmlPropertyData::Flags propertyFlags;
- const QV4::CompiledData::BuiltinType type = p->builtinType();
+ const QV4::CompiledData::CommonType type = p->commonType();
if (p->isList())
- propertyFlags.type = QQmlPropertyData::Flags::QListType;
- else if (type == QV4::CompiledData::BuiltinType::Var)
- propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
+ propertyFlags.setType(QQmlPropertyData::Flags::QListType);
+ else if (type == QV4::CompiledData::CommonType::Var)
+ propertyFlags.setType(QQmlPropertyData::Flags::VarPropertyType);
- if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) {
+ if (type != QV4::CompiledData::CommonType::Invalid) {
propertyType = p->isList()
? listTypeForPropertyType(type)
: metaTypeForPropertyType(type);
} else {
- Q_ASSERT(!p->isBuiltinType());
+ Q_ASSERT(!p->isCommonType());
QQmlType qmltype;
bool selfReference = false;
- if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex()), &qmltype, nullptr, nullptr,
- nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
+ if (!imports->resolveType(
+ QQmlTypeLoader::get(enginePrivate),
+ stringAt(p->commonTypeOrTypeNameIndex()), &qmltype, nullptr, nullptr,
+ nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
}
// inline components are not necessarily valid yet
- Q_ASSERT(qmltype.isValid() || qmltype.isInlineComponentType());
+ Q_ASSERT(qmltype.isValid());
if (qmltype.isComposite() || qmltype.isInlineComponentType()) {
- CompositeMetaTypeIds typeIds;
+ QQmlType compositeType;
if (qmltype.isInlineComponentType()) {
- auto objectId = qmltype.inlineComponentId();
- auto containingType = qmltype.containingType();
- if (containingType.isValid()) {
- auto icType = containingType.lookupInlineComponentById(objectId);
- typeIds = {icType.typeId(), icType.qListTypeId()};
- } else {
- typeIds = {};
- }
- if (!typeIds.isValid()) // type has not been registered yet, we must be in containing type
- typeIds = objectContainer->typeIdsForComponent(objectId);
- Q_ASSERT(typeIds.isValid());
+ compositeType = qmltype;
+ Q_ASSERT(compositeType.isValid());
} else if (selfReference) {
- typeIds = objectContainer->typeIdsForComponent();
+ compositeType = objectContainer->qmlTypeForComponent();
} else {
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ // compositeType may not be the same type as qmlType because multiple engines
+ // may load different types for the same document. Therefore we have to ask
+ // our engine's type loader here.
+ QQmlRefPointer<QQmlTypeData> tdata
+ = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
- typeIds = compilationUnit->typeIdsForComponent();
+ compositeType = tdata->compilationUnit()->qmlTypeForComponent();
}
if (p->isList()) {
- propertyType = typeIds.listId;
+ propertyType = compositeType.qListTypeId();
} else {
- propertyType = typeIds.id;
+ propertyType = compositeType.typeId();
}
} else {
if (p->isList())
@@ -676,11 +744,9 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
}
if (p->isList())
- propertyFlags.type = QQmlPropertyData::Flags::QListType;
+ propertyFlags.setType(QQmlPropertyData::Flags::QListType);
else if (propertyType.flags().testFlag(QMetaType::PointerToQObject))
- propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
- else
- propertyFlags.type = QQmlPropertyData::Flags::ValueType;
+ propertyFlags.setType(QQmlPropertyData::Flags::QObjectDerivedType);
}
if (!p->isReadOnly() && !propertyType.flags().testFlag(QMetaType::IsQmlList))
@@ -701,50 +767,56 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
}
template <typename ObjectContainer>
-inline QMetaType QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType &param,
- QString *customTypeName)
+inline QMetaType QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(
+ const QV4::CompiledData::ParameterType &param, QString *customTypeName)
{
- const quint32 typeId = param.typeNameIndexOrBuiltinType();
- if (param.indexIsBuiltinType()) {
+ const quint32 typeId = param.typeNameIndexOrCommonType();
+ if (param.indexIsCommonType()) {
// built-in type
if (param.isList())
- return listTypeForPropertyType(QV4::CompiledData::BuiltinType(typeId));
- return metaTypeForPropertyType(QV4::CompiledData::BuiltinType(typeId));
+ return listTypeForPropertyType(QV4::CompiledData::CommonType(typeId));
+ return metaTypeForPropertyType(QV4::CompiledData::CommonType(typeId));
}
// lazily resolved type
- const QString typeName = stringAt(param.typeNameIndexOrBuiltinType());
+ const QString typeName = stringAt(param.typeNameIndexOrCommonType());
if (customTypeName)
*customTypeName = typeName;
QQmlType qmltype;
bool selfReference = false;
- if (!imports->resolveType(typeName, &qmltype, nullptr, nullptr, nullptr,
- QQmlType::AnyRegistrationType, &selfReference))
+ if (!imports->resolveType(
+ &enginePrivate->typeLoader, typeName, &qmltype, nullptr, nullptr, nullptr,
+ QQmlType::AnyRegistrationType, &selfReference))
return QMetaType();
if (!qmltype.isComposite()) {
const QMetaType typeId = param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
if (!typeId.isValid() && qmltype.isInlineComponentType()) {
- const int objectId = qmltype.inlineComponentId();
- const auto typeIds = objectContainer->typeIdsForComponent(objectId);
- return param.isList() ? typeIds.listId : typeIds.id;
+ const QQmlType qmlType = objectContainer->qmlTypeForComponent(qmltype.elementName());
+ return param.isList() ? qmlType.qListTypeId() : qmlType.typeId();
} else {
return typeId;
}
}
if (selfReference) {
- const auto typeIds = objectContainer->typeIdsForComponent();
- return param.isList() ? typeIds.listId : typeIds.id;
+ const QQmlType qmlType = objectContainer->qmlTypeForComponent();
+ return param.isList() ? qmlType.qListTypeId() : qmlType.typeId();
}
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
+ return param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
+}
- return param.isList() ? compilationUnit->typeIds.listId : compilationUnit->typeIds.id;
+template <typename ObjectContainer, typename CompiledObject>
+int objectForId(const ObjectContainer *objectContainer, const CompiledObject &component, int id)
+{
+ for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
+ if (candidate.objectId() == id)
+ return candidateIndex;
+ }
+ return -1;
}
template <typename ObjectContainer>
@@ -753,126 +825,27 @@ class QQmlPropertyCacheAliasCreator
public:
typedef typename ObjectContainer::CompiledObject CompiledObject;
- QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
-
- void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv);
-
- QQmlError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
+ QQmlPropertyCacheAliasCreator(
+ QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
+ QQmlError appendAliasesToPropertyCache(
+ const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
private:
- void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv);
- QQmlError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type, QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv);
-
- void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
-
- int objectForId(const CompiledObject &component, int id) const;
+ QQmlError propertyDataForAlias(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
+ QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
+ QQmlEnginePrivate *enginePriv);
QQmlPropertyCacheVector *propertyCaches;
const ObjectContainer *objectContainer;
};
template <typename ObjectContainer>
-inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
+inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(
+ QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
: propertyCaches(propertyCaches)
, objectContainer(objectContainer)
{
-
-}
-
-template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv)
-{
- // skip the root object (index 0) as that one does not have a first object index originating
- // from a binding.
- for (int i = 1; i < objectContainer->objectCount(); ++i) {
- const CompiledObject &component = *objectContainer->objectAt(i);
- if (!component.hasFlag(QV4::CompiledData::Object::IsComponent))
- continue;
-
- const auto rootBinding = component.bindingsBegin();
- appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex, enginePriv);
- }
-
- const int rootObjectIndex = 0;
- appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex, enginePriv);
-}
-
-template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv)
-{
- QVector<int> objectsWithAliases;
- collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases);
- if (objectsWithAliases.isEmpty())
- return;
-
- const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) {
- auto alias = object.aliasesBegin();
- auto end = object.aliasesEnd();
- for ( ; alias != end; ++alias) {
- Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
-
- const int targetObjectIndex = objectForId(component, alias->targetObjectId());
- Q_ASSERT(targetObjectIndex >= 0);
-
- if (alias->isAliasToLocalAlias())
- continue;
-
- if (alias->encodedMetaPropertyIndex == -1)
- continue;
-
- const QQmlPropertyCache::ConstPtr targetCache
- = propertyCaches->at(targetObjectIndex);
- Q_ASSERT(targetCache);
-
- int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
- const QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
- if (!targetProperty)
- return false;
- }
- return true;
- };
-
- do {
- QVector<int> pendingObjects;
-
- for (int objectIndex: std::as_const(objectsWithAliases)) {
- const CompiledObject &object = *objectContainer->objectAt(objectIndex);
-
- if (allAliasTargetsExist(object)) {
- appendAliasesToPropertyCache(component, objectIndex, enginePriv);
- } else {
- pendingObjects.append(objectIndex);
- }
-
- }
- objectsWithAliases = std::move(pendingObjects);
- } while (!objectsWithAliases.isEmpty());
-}
-
-template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const
-{
- const CompiledObject &object = *objectContainer->objectAt(objectIndex);
- if (object.aliasCount() > 0)
- objectsWithAliases->append(objectIndex);
-
- // Stop at Component boundary
- if (object.hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
- return;
-
- auto binding = object.bindingsBegin();
- auto end = object.bindingsEnd();
- for (; binding != end; ++binding) {
- switch (binding->type()) {
- case QV4::CompiledData::Binding::Type_Object:
- case QV4::CompiledData::Binding::Type_AttachedProperty:
- case QV4::CompiledData::Binding::Type_GroupProperty:
- collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
- break;
- default:
- break;
- }
- }
}
template <typename ObjectContainer>
@@ -893,7 +866,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
do {
- const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId());
+ const int targetObjectIndex = objectForId(
+ objectContainer, component, lastAlias->targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
Q_ASSERT(targetObject);
@@ -912,10 +886,11 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
lastAlias = targetAlias;
} while (lastAlias->isAliasToLocalAlias());
- return propertyDataForAlias(component, *lastAlias, type, version, propertyFlags, enginePriv);
+ return propertyDataForAlias(
+ component, *lastAlias, type, version, propertyFlags, enginePriv);
}
- const int targetObjectIndex = objectForId(component, alias.targetObjectId());
+ const int targetObjectIndex = objectForId(objectContainer, component, alias.targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
@@ -934,20 +909,20 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
if (referencedType.isValid()) {
*type = referencedType.typeId();
if (!type->isValid() && referencedType.isInlineComponentType()) {
- int objectId = referencedType.inlineComponentId();
- *type = objectContainer->typeIdsForComponent(objectId).id;
+ *type = objectContainer->qmlTypeForComponent(referencedType.elementName()).typeId();
Q_ASSERT(type->isValid());
}
} else {
- *type = typeRef->compilationUnit()->typeIds.id;
+ *type = typeRef->compilationUnit()->metaType();
}
*version = typeRef->version();
- propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
+ propertyFlags->setType(QQmlPropertyData::Flags::QObjectDerivedType);
} else {
int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
- int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex();
+ int valueTypeIndex = QQmlPropertyIndex::fromEncoded(
+ alias.encodedMetaPropertyIndex).valueTypeIndex();
QQmlPropertyCache::ConstPtr targetCache = propertyCaches->at(targetObjectIndex);
Q_ASSERT(targetCache);
@@ -955,53 +930,67 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
const QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
Q_ASSERT(targetProperty);
+ const QMetaType targetPropType = targetProperty->propType();
+
+ const auto populateWithPropertyData = [&](const QQmlPropertyData *property) {
+ *type = property->propType();
+ writable = property->isWritable();
+ resettable = property->isResettable();
+ bindable = property->isBindable();
+
+ if (property->isVarProperty())
+ propertyFlags->setType(QQmlPropertyData::Flags::QVariantType);
+ else
+ propertyFlags->copyPropertyTypeFlags(property->flags());
+ };
+
// for deep aliases, valueTypeIndex is always set
- if (!QQmlMetaType::isValueType(targetProperty->propType()) && valueTypeIndex != -1) {
+ if (!QQmlMetaType::isValueType(targetPropType) && valueTypeIndex != -1) {
// deep alias property
- *type = targetProperty->propType();
- QQmlPropertyCache::ConstPtr typeCache = QQmlMetaType::propertyCacheForType(*type);
- Q_ASSERT(typeCache);
- const QQmlPropertyData *typeProperty = typeCache->property(valueTypeIndex);
- if (typeProperty == nullptr) {
- return qQmlCompileError(alias.referenceLocation,
- QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ QQmlPropertyCache::ConstPtr typeCache
+ = QQmlMetaType::propertyCacheForType(targetPropType);
+
+ if (!typeCache) {
+ // See if it's a half-resolved composite type
+ if (const QV4::ResolvedTypeReference *typeRef
+ = objectContainer->resolvedType(targetPropType)) {
+ typeCache = typeRef->typePropertyCache();
+ }
}
- *type = typeProperty->propType();
- writable = typeProperty->isWritable();
- resettable = typeProperty->isResettable();
- bindable = typeProperty->isBindable();
+ const QQmlPropertyData *typeProperty = typeCache
+ ? typeCache->property(valueTypeIndex)
+ : nullptr;
+ if (typeProperty == nullptr) {
+ return qQmlCompileError(
+ alias.referenceLocation,
+ QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ }
+ populateWithPropertyData(typeProperty);
} else {
// value type or primitive type or enum
- *type = targetProperty->propType();
-
- writable = targetProperty->isWritable();
- resettable = targetProperty->isResettable();
- bindable = targetProperty->isBindable();
+ populateWithPropertyData(targetProperty);
if (valueTypeIndex != -1) {
- const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(*type);
- if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
- *type = QMetaType::fromType<int>();
- else
- *type = valueTypeMetaObject->property(valueTypeIndex).metaType();
- } else {
- if (targetProperty->isEnum()) {
- *type = QMetaType::fromType<int>();
- } else {
- // Copy type flags
- propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
-
- if (targetProperty->isVarProperty())
- propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
- }
+ const QMetaObject *valueTypeMetaObject
+ = QQmlMetaType::metaObjectForValueType(*type);
+ const QMetaProperty valueTypeMetaProperty
+ = valueTypeMetaObject->property(valueTypeIndex);
+ *type = valueTypeMetaProperty.metaType();
+
+ // We can only write or reset the value type property if we can write
+ // the value type itself.
+ resettable = writable && valueTypeMetaProperty.isResettable();
+ writable = writable && valueTypeMetaProperty.isWritable();
+
+ bindable = valueTypeMetaProperty.isBindable();
}
}
}
- propertyFlags->setIsWritable(!(alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly))
- && writable);
+ propertyFlags->setIsWritable(
+ writable && !alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly));
propertyFlags->setIsResettable(resettable);
propertyFlags->setIsBindable(bindable);
return QQmlError();
@@ -1047,18 +1036,6 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesTo
return QQmlError();
}
-template <typename ObjectContainer>
-inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const
-{
- for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
- const int candidateIndex = component.namedObjectsInComponentTable()[i];
- const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
- if (candidate.objectId() == id)
- return candidateIndex;
- }
- return -1;
-}
-
QT_END_NAMESPACE
#endif // QQMLPROPERTYCACHECREATOR_P_H
diff --git a/src/qml/qml/qqmlpropertycachevector_p.h b/src/qml/qml/qqmlpropertycachevector_p.h
index a208b3fa6e..1cee914220 100644
--- a/src/qml/qml/qqmlpropertycachevector_p.h
+++ b/src/qml/qml/qqmlpropertycachevector_p.h
@@ -42,28 +42,30 @@ public:
}
void clear()
{
+ for (int i = 0; i < data.size(); ++i)
+ releaseElement(i);
+ data.clear();
+ }
+
+ void resetAndResize(int size)
+ {
for (int i = 0; i < data.size(); ++i) {
- const auto &cache = data.at(i);
- if (cache.isT2()) {
- if (QQmlPropertyCache *data = cache.asT2())
- data->release();
- } else if (const QQmlPropertyCache *data = cache.asT1()) {
- data->release();
- }
+ releaseElement(i);
+ data[i] = BiPointer();
}
- data.clear();
+ data.resize(size);
}
void append(const QQmlPropertyCache::ConstPtr &cache) {
cache->addref();
- data.append(QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>(cache.data()));
+ data.append(BiPointer(cache.data()));
Q_ASSERT(data.last().isT1());
Q_ASSERT(data.size() <= std::numeric_limits<int>::max());
}
void appendOwn(const QQmlPropertyCache::Ptr &cache) {
cache->addref();
- data.append(QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>(cache.data()));
+ data.append(BiPointer(cache.data()));
Q_ASSERT(data.last().isT2());
Q_ASSERT(data.size() <= std::numeric_limits<int>::max());
}
@@ -122,8 +124,20 @@ public:
}
private:
+ void releaseElement(int i)
+ {
+ const auto &cache = data.at(i);
+ if (cache.isT2()) {
+ if (QQmlPropertyCache *data = cache.asT2())
+ data->release();
+ } else if (const QQmlPropertyCache *data = cache.asT1()) {
+ data->release();
+ }
+ }
+
Q_DISABLE_COPY(QQmlPropertyCacheVector)
- QVector<QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>> data;
+ using BiPointer = QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>;
+ QVector<BiPointer> data;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index 19a045107d..0fa7984f05 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -37,20 +37,18 @@ public:
struct Flags {
friend class QQmlPropertyData;
- enum Types {
+ enum Type {
OtherType = 0,
FunctionType = 1, // Is an invokable
QObjectDerivedType = 2, // Property type is a QObject* derived type
EnumType = 3, // Property type is an enum
QListType = 4, // Property type is a QML list
- /*QmlBindingType = 5; was: Property type is a QQmlBinding*; now unused */
- QJSValueType = 6, // Property type is a QScriptValue
- // Gap, used to be V4HandleType
- VarPropertyType = 8, // Property type is a "var" property of VMEMO
- QVariantType = 9, // Property is a QVariant
- ValueType = 10 // Property type is a custom value type
+ VarPropertyType = 5, // Property type is a "var" property of VMEMO
+ QVariantType = 6, // Property is a QVariant
+ // One spot left for an extra type in the 3 bits used to store this.
};
+ private:
// The _otherBits (which "pad" the Flags struct to align it nicely) are used
// to store the relative property index. It will only get used when said index fits. See
// trySetStaticMetaCallFunction for details.
@@ -63,31 +61,32 @@ public:
// b when type equals FunctionType. For that reason, the semantic meaning of the bit is
// overloaded, and the accessor functions are used to get the correct value
//
- // Moreover, isSignalHandler, isOverload and isCloned make only sense
+ // Moreover, isSignalHandler, isOverridableSignal and isCloned make only sense
// for functions, too (and could at a later point be reused for flags that only make sense
// for non-functions)
//
// Lastly, isDirect and isOverridden apply to both functions and non-functions
- private:
unsigned isConst : 1; // Property: has CONST flag/Method: is const
unsigned isVMEFunction : 1; // Function was added by QML
unsigned isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments
unsigned isResettableORisSignal : 1; // Has RESET function OR Function is a signal
unsigned isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML
- unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args
+ unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4FunctionPtr args
unsigned isSignalHandler : 1; // Function is a signal handler
- unsigned isOverload : 1; // Function is an overload of another function
+
+ // TODO: Remove this once we can. Signals should not be overridable.
+ unsigned isOverridableSignal : 1; // Function is an overridable signal
+
unsigned isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned
unsigned isConstructorORisBindable : 1; // The function was marked is a constructor OR property is backed by QProperty<T>
unsigned isOverridden : 1; // Is overridden by a extension property
- public:
- unsigned type : 4; // stores an entry of Types
-
- // Apply only to IsFunctions
+ unsigned hasMetaObject : 1;
+ unsigned type : 3; // stores an entry of Types
// Internal QQmlPropertyCache flags
- unsigned overrideIndexIsProperty: 1;
+ unsigned overrideIndexIsProperty : 1;
+ public:
inline Flags();
inline bool operator==(const Flags &other) const;
inline void copyPropertyTypeFlags(Flags from);
@@ -157,9 +156,11 @@ public:
isSignalHandler = b;
}
- void setIsOverload(bool b) {
+ // TODO: Remove this once we can. Signals should not be overridable.
+ void setIsOverridableSignal(bool b) {
Q_ASSERT(type == FunctionType);
- isOverload = b;
+ Q_ASSERT(isResettableORisSignal);
+ isOverridableSignal = b;
}
void setIsCloned(bool b) {
@@ -172,6 +173,13 @@ public:
isConstructorORisBindable = b;
}
+ void setHasMetaObject(bool b) {
+ hasMetaObject = b;
+ }
+
+ void setType(Type newType) {
+ type = newType;
+ }
};
@@ -200,7 +208,6 @@ public:
bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; }
bool isEnum() const { return m_flags.type == Flags::EnumType; }
bool isQList() const { return m_flags.type == Flags::QListType; }
- bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; }
bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; }
bool isQVariant() const { return m_flags.type == Flags::QVariantType; }
bool isVMEFunction() const { return isFunction() && m_flags.isVMEFunction; }
@@ -209,8 +216,11 @@ public:
bool isVMESignal() const { return isFunction() && m_flags.isAliasORisVMESignal; }
bool isV4Function() const { return isFunction() && m_flags.isFinalORisV4Function; }
bool isSignalHandler() const { return m_flags.isSignalHandler; }
- bool isOverload() const { return m_flags.isOverload; }
- void setOverload(bool onoff) { m_flags.isOverload = onoff; }
+ bool hasMetaObject() const { return m_flags.hasMetaObject; }
+
+ // TODO: Remove this once we can. Signals should not be overridable.
+ bool isOverridableSignal() const { return m_flags.isOverridableSignal; }
+
bool isCloned() const { return isFunction() && m_flags.isRequiredORisCloned; }
bool isConstructor() const { return isFunction() && m_flags.isConstructorORisBindable; }
bool isBindable() const { return !isFunction() && m_flags.isConstructorORisBindable; }
@@ -275,8 +285,36 @@ public:
QTypeRevision typeVersion() const { return m_typeVersion; }
void setTypeVersion(QTypeRevision typeVersion) { m_typeVersion = typeVersion; }
- QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; }
- void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; }
+ QQmlPropertyCacheMethodArguments *arguments() const
+ {
+ Q_ASSERT(!hasMetaObject());
+ return m_arguments;
+ }
+ void setArguments(QQmlPropertyCacheMethodArguments *args)
+ {
+ Q_ASSERT(!hasMetaObject());
+ m_arguments = args;
+ }
+
+ const QMetaObject *metaObject() const
+ {
+ Q_ASSERT(hasMetaObject());
+ return m_metaObject;
+ }
+
+ void setMetaObject(const QMetaObject *metaObject)
+ {
+ Q_ASSERT(!hasArguments() || !m_arguments);
+ m_flags.setHasMetaObject(true);
+ m_metaObject = metaObject;
+ }
+
+ QMetaMethod metaMethod() const
+ {
+ Q_ASSERT(hasMetaObject());
+ Q_ASSERT(isFunction());
+ return m_metaObject->method(m_coreIndex);
+ }
int metaObjectOffset() const { return m_metaObjectOffset; }
void setMetaObjectOffset(int off)
@@ -353,7 +391,7 @@ public:
static Flags defaultSignalFlags()
{
Flags f;
- f.type = Flags::FunctionType;
+ f.setType(Flags::FunctionType);
f.setIsSignal(true);
f.setIsVMESignal(true);
return f;
@@ -362,7 +400,7 @@ public:
static Flags defaultSlotFlags()
{
Flags f;
- f.type = Flags::FunctionType;
+ f.setType(Flags::FunctionType);
f.setIsVMEFunction(true);
return f;
}
@@ -388,6 +426,7 @@ private:
union {
QQmlPropertyCacheMethodArguments *m_arguments = nullptr;
StaticMetaCallFunction m_staticMetaCallFunction;
+ const QMetaObject *m_metaObject;
};
};
@@ -397,6 +436,8 @@ private:
Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 32);
#endif
+static_assert(std::is_trivially_copyable<QQmlPropertyData>::value);
+
bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const
{
return flags() == other.flags() &&
@@ -415,13 +456,15 @@ QQmlPropertyData::Flags::Flags()
, isAliasORisVMESignal(false)
, isFinalORisV4Function(false)
, isSignalHandler(false)
- , isOverload(false)
+ , isOverridableSignal(false)
, isRequiredORisCloned(false)
, isConstructorORisBindable(false)
, isOverridden(false)
+ , hasMetaObject(false)
, type(OtherType)
, overrideIndexIsProperty(false)
-{}
+{
+}
bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const
{
@@ -434,6 +477,7 @@ bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) c
isOverridden == other.isOverridden &&
isSignalHandler == other.isSignalHandler &&
isRequiredORisCloned == other.isRequiredORisCloned &&
+ hasMetaObject == other.hasMetaObject &&
type == other.type &&
isConstructorORisBindable == other.isConstructorORisBindable &&
overrideIndexIsProperty == other.overrideIndexIsProperty;
@@ -445,7 +489,6 @@ void QQmlPropertyData::Flags::copyPropertyTypeFlags(QQmlPropertyData::Flags from
case QObjectDerivedType:
case EnumType:
case QListType:
- case QJSValueType:
case QVariantType:
type = from.type;
}
diff --git a/src/qml/qml/qqmlpropertyresolver.cpp b/src/qml/qml/qqmlpropertyresolver.cpp
index ff29c38997..0217f7b7b5 100644
--- a/src/qml/qml/qqmlpropertyresolver.cpp
+++ b/src/qml/qml/qqmlpropertyresolver.cpp
@@ -3,6 +3,7 @@
#include "qqmlpropertyresolver_p.h"
#include <private/qqmlcontextdata_p.h>
+#include <private/qqmlsignalnames_p.h>
QT_BEGIN_NAMESPACE
@@ -43,10 +44,8 @@ const QQmlPropertyData *QQmlPropertyResolver::signal(const QString &name, bool *
return d;
}
- if (name.endsWith(QLatin1String("Changed"))) {
- QString propName = name.mid(0, name.size() - static_cast<int>(strlen("Changed")));
-
- d = property(propName, notInRevision);
+ if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
+ d = property(*propName, notInRevision);
if (d)
return cache->signal(d->notifyIndex());
}
diff --git a/src/qml/qml/qqmlpropertytopropertybinding_p.h b/src/qml/qml/qqmlpropertytopropertybinding_p.h
index de6012ded9..d741c5a740 100644
--- a/src/qml/qml/qqmlpropertytopropertybinding_p.h
+++ b/src/qml/qml/qqmlpropertytopropertybinding_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlPropertyToPropertyBinding
+class Q_QML_EXPORT QQmlPropertyToPropertyBinding
: public QQmlAbstractBinding, public QQmlNotifierEndpoint
{
public:
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
index c6783842dc..ff22c3043a 100644
--- a/src/qml/qml/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -9,6 +9,7 @@
#include <private/qqmlpropertycachecreator_p.h>
#include <private/qqmlpropertyresolver_p.h>
#include <private/qqmlstringconverters_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <QtCore/qdatetime.h>
@@ -27,9 +28,8 @@ QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(HANDLE_PRIMITIVE);
}
}
-QQmlPropertyValidator::QQmlPropertyValidator(
- QQmlEnginePrivate *enginePrivate, const QQmlImports *imports,
- const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports *imports,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
: enginePrivate(enginePrivate)
, compilationUnit(compilationUnit)
, imports(imports)
@@ -87,6 +87,12 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
QQmlCustomParser *customParser = nullptr;
if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) {
+
+ // This binding instantiates a separate object. The separate object can have an ID and its
+ // own group properties even if it's then assigned to a value type, for example a 'var', or
+ // anything with an invokable ctor taking a QObject*.
+ populatingValueTypeGroupProperty = false;
+
const auto type = typeRef->type();
if (type.isValid())
customParser = type.customParser();
@@ -126,7 +132,7 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
defaultProperty = propertyCache->defaultProperty();
}
- QV4::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
+ QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
binding = obj->bindingTable();
for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
@@ -140,7 +146,7 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
customBindings << binding;
continue;
}
- } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
+ } else if (QQmlSignalNames::isHandlerName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
customBindings << binding;
continue;
@@ -194,7 +200,8 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
QQmlType type;
QQmlImportNamespace *typeNamespace = nullptr;
imports->resolveType(
- stringAt(binding->propertyNameIndex), &type, nullptr, &typeNamespace);
+ QQmlTypeLoader::get(enginePrivate), stringAt(binding->propertyNameIndex),
+ &type, nullptr, &typeNamespace);
if (typeNamespace)
return recordError(binding->location, tr("Invalid use of namespace"));
return recordError(binding->location, tr("Invalid attached object assignment"));
@@ -295,17 +302,25 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
return recordError(
binding->location,
tr("Invalid grouped property access: Property \"%1\" with primitive type \"%2\".")
- .arg(name)
- .arg(QString::fromUtf8(type.name()))
+ .arg(name, QString::fromUtf8(type.name()))
);
}
if (!QQmlMetaType::propertyCacheForType(type)) {
- return recordError(binding->location,
- tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type")
- .arg(name)
- .arg(QString::fromUtf8(type.name()))
- );
+ auto mo = type.metaObject();
+ if (!mo) {
+ return recordError(binding->location,
+ tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is neither a value nor an object type")
+ .arg(name, QString::fromUtf8(type.name()))
+ );
+ }
+ if (QMetaObjectPrivate::get(mo)->flags & DynamicMetaObject) {
+ return recordError(binding->location,
+ tr("Unsupported grouped property access: Property \"%1\" with type \"%2\" has a dynamic meta-object.")
+ .arg(name, QString::fromUtf8(type.name()))
+ );
+ }
+ // fall through, this is okay
}
}
}
@@ -335,7 +350,10 @@ QVector<QQmlError> QQmlPropertyValidator::validateObject(
customParser->validator = this;
customParser->engine = enginePrivate;
customParser->imports = imports;
- customParser->verifyBindings(compilationUnit, customBindings);
+ customParser->verifyBindings(
+ enginePrivate->v4engine()->executableCompilationUnit(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>(compilationUnit)),
+ customBindings);
customParser->validator = nullptr;
customParser->engine = nullptr;
customParser->imports = (QQmlImports*)nullptr;
@@ -364,6 +382,11 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum))
return noError;
+ // TODO: For historical reasons you can assign any number to an enum property alias
+ // This can be fixed with an opt-out mechanism, for example a pragma.
+ if (property->isAlias() && binding->isNumberBinding())
+ return noError;
+
QString value = compilationUnit->bindingValueAsString(binding);
QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
bool ok;
@@ -379,19 +402,6 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
}
auto warnOrError = [&](const QString &error) {
- if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
- QQmlError warning;
- warning.setUrl(compilationUnit->url());
- warning.setLine(qmlConvertSourceCoordinate<quint32, int>(
- binding->valueLocation.line()));
- warning.setColumn(qmlConvertSourceCoordinate<quint32, int>(
- binding->valueLocation.column()));
- warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML "
- "is deprecated. This will become a compile error in "
- "future versions of Qt."));
- enginePrivate->warning(warning);
- return noError;
- }
return qQmlCompileError(binding->valueLocation, error);
};
@@ -568,10 +578,10 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
default: return QString();
}
};
- QVariant result(property->propType());
- if (!QQmlValueTypeProvider::createValueType(
+ const QVariant result = QQmlValueTypeProvider::createValueType(
compilationUnit->bindingValueAsString(binding),
- result.metaType(), result.data())) {
+ property->propType());
+ if (!result.isValid()) {
return warnOrError(tr("Invalid property assignment: %1 expected")
.arg(typeName()));
}
@@ -622,7 +632,7 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
break;
}
- return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(property->propType().name())));
+ return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QLatin1StringView(property->propType().name())));
}
break;
}
@@ -643,7 +653,7 @@ bool QQmlPropertyValidator::canCoerce(QMetaType to, QQmlPropertyCache::ConstPtr
// only occurs after the whole file has been validated
// Therefore we need to check the ICs here
for (const auto& icDatum : compilationUnit->inlineComponentData) {
- if (icDatum.typeIds.id == to) {
+ if (icDatum.qmlType.typeId() == to) {
toMo = compilationUnit->propertyCaches.at(icDatum.objectIndex);
break;
}
@@ -685,13 +695,12 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(const QQmlPropertyData *p
const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex);
if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) {
QQmlPropertyCache::ConstPtr cache = typeRef->createPropertyCache();
- const QMetaObject *mo = cache->firstCppMetaObject();
+ const QMetaObject *mo = cache ? cache->firstCppMetaObject() : nullptr;
QQmlType qmlType;
while (mo && !qmlType.isValid()) {
qmlType = QQmlMetaType::qmlType(mo);
mo = mo->superClass();
}
- Q_ASSERT(qmlType.isValid());
isValueSource = qmlType.propertyValueSourceCast() != -1;
isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1;
@@ -754,8 +763,9 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(const QQmlPropertyData *p
// only occurs after the whole file has been validated
// Therefore we need to check the ICs here
for (const auto& icDatum: compilationUnit->inlineComponentData) {
- if (icDatum.typeIds.id == property->propType()) {
- propertyMetaObject = compilationUnit->propertyCaches.at(icDatum.objectIndex);
+ if (icDatum.qmlType.typeId() == property->propType()) {
+ propertyMetaObject
+ = compilationUnit->propertyCaches.at(icDatum.objectIndex);
break;
}
}
diff --git a/src/qml/qml/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h
index e1154347ec..75787fcf68 100644
--- a/src/qml/qml/qqmlpropertyvalidator_p.h
+++ b/src/qml/qml/qqmlpropertyvalidator_p.h
@@ -28,7 +28,9 @@ class QQmlPropertyValidator
{
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports *imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit);
+ QQmlPropertyValidator(
+ QQmlEnginePrivate *enginePrivate, const QQmlImports *imports,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
QVector<QQmlError> validate();
@@ -58,12 +60,12 @@ private:
}
QQmlEnginePrivate *enginePrivate;
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
const QQmlImports *imports;
const QV4::CompiledData::Unit *qmlUnit;
const QQmlPropertyCacheVector &propertyCaches;
- QVector<QV4::BindingPropertyData> * const bindingPropertyDataPerObject;
+ QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
index 90feabe565..9d1b0606cd 100644
--- a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
+++ b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
class QQmlProperty;
-class Q_QML_PRIVATE_EXPORT QQmlPropertyValueInterceptor
+class Q_QML_EXPORT QQmlPropertyValueInterceptor
{
public:
QQmlPropertyValueInterceptor();
diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp
index ad67856c02..6ec4009c37 100644
--- a/src/qml/qml/qqmlproxymetaobject.cpp
+++ b/src/qml/qml/qqmlproxymetaobject.cpp
@@ -6,8 +6,8 @@
QT_BEGIN_NAMESPACE
-QQmlProxyMetaObject::QQmlProxyMetaObject(QObject *obj, QList<ProxyData> *mList)
-: metaObjects(mList), proxies(nullptr), parent(nullptr), object(obj)
+QQmlProxyMetaObject::QQmlProxyMetaObject(QObject *obj, const QList<ProxyData> *mList)
+ : metaObjects(mList), proxies(nullptr), parent(nullptr), object(obj)
{
metaObject = metaObjects->constFirst().metaObject;
@@ -132,4 +132,12 @@ QMetaObject *QQmlProxyMetaObject::toDynamicMetaObject(QObject *)
return metaObject;
}
+void QQmlProxyMetaObject::objectDestroyed(QObject *object)
+{
+ if (parent)
+ parent->objectDestroyed(object);
+ else
+ QDynamicMetaObjectData::objectDestroyed(object);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlproxymetaobject_p.h b/src/qml/qml/qqmlproxymetaobject_p.h
index 6238dff4d1..18cf2d4bac 100644
--- a/src/qml/qml/qqmlproxymetaobject_p.h
+++ b/src/qml/qml/qqmlproxymetaobject_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlProxyMetaObject : public QDynamicMetaObjectData
+class Q_QML_EXPORT QQmlProxyMetaObject : public QDynamicMetaObjectData
{
public:
struct ProxyData {
@@ -36,7 +36,7 @@ public:
int methodOffset;
};
- QQmlProxyMetaObject(QObject *, QList<ProxyData> *);
+ QQmlProxyMetaObject(QObject *, const QList<ProxyData> *);
~QQmlProxyMetaObject();
static constexpr int extensionObjectId(int id) noexcept
@@ -49,11 +49,12 @@ public:
protected:
int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) override;
QMetaObject *toDynamicMetaObject(QObject *) override;
+ void objectDestroyed(QObject *object) override;
private:
QObject *getProxy(int index);
- QList<ProxyData> *metaObjects;
+ const QList<ProxyData> *metaObjects;
QObject **proxies;
QDynamicMetaObjectData *parent;
diff --git a/src/qml/qml/qqmlregistration.h b/src/qml/qml/qqmlregistration.h
index f40b39687c..562a79eaf1 100644
--- a/src/qml/qml/qqmlregistration.h
+++ b/src/qml/qml/qqmlregistration.h
@@ -10,19 +10,6 @@
// satisfy configure, which warns about public headers not using those
QT_BEGIN_NAMESPACE
-#define QML_FOREIGN(FOREIGN_TYPE) \
- Q_CLASSINFO("QML.Foreign", #FOREIGN_TYPE) \
- using QmlForeignType = FOREIGN_TYPE; \
- template<class, class> friend struct QML_PRIVATE_NAMESPACE::QmlResolved; \
- template<typename... Args> \
- friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
- inline constexpr void qt_qmlMarker_foreign() {}
-
-#define QML_FOREIGN_NAMESPACE(FOREIGN_NAMESPACE) \
- Q_CLASSINFO("QML.Foreign", #FOREIGN_NAMESPACE)
-
-#define QML_CUSTOMPARSER Q_CLASSINFO("QML.HasCustomParser", "true")
-
QT_END_NAMESPACE
#endif // QQMLREGISTRATION_H
diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp
index 72bc3a7c12..fa9a41e801 100644
--- a/src/qml/qml/qqmlscriptblob.cpp
+++ b/src/qml/qml/qqmlscriptblob.cpp
@@ -32,14 +32,18 @@ QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const
return m_scriptData;
}
+bool QQmlScriptBlob::isNative() const
+{
+ return m_scriptData && !m_scriptData->m_value.isEmpty();
+}
+
void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
{
if (readCacheFile()) {
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
- = QV4::ExecutableCompilationUnit::create();
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
QString error;
if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
- initializeFromCompilationUnit(unit);
+ initializeFromCompilationUnit(std::move(unit));
return;
} else {
qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error;
@@ -61,7 +65,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
return;
}
- QV4::CompiledData::CompilationUnit unit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
if (m_isModule) {
QList<QQmlJS::DiagnosticMessage> diagnostics;
@@ -96,28 +100,26 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
unit = std::move(irUnit.javaScriptCompilationUnit);
}
- auto executableUnit = QV4::ExecutableCompilationUnit::create(std::move(unit));
-
if (writeCacheFile()) {
QString errorString;
- if (executableUnit->saveToDisk(url(), &errorString)) {
+ if (unit->saveToDisk(url(), &errorString)) {
QString error;
- if (!executableUnit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
+ if (!unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
// ignore error, keep using the in-memory compilation unit.
}
} else {
qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of"
- << executableUnit->fileName() << "to disk:" << errorString;
+ << unit->fileName() << "to disk:" << errorString;
}
}
- initializeFromCompilationUnit(executableUnit);
+ initializeFromCompilationUnit(std::move(unit));
}
-void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
+void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *cachedUnit)
{
- initializeFromCompilationUnit(QV4::ExecutableCompilationUnit::create(
- QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions, urlString(), finalUrlString())));
+ initializeFromCompilationUnit(QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(
+ cachedUnit->qmlData, cachedUnit->aotCompiledFunctions, urlString(), finalUrlString()));
}
void QQmlScriptBlob::done()
@@ -182,9 +184,12 @@ void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob,
m_scripts << ref;
}
-void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit)
+void QQmlScriptBlob::initializeFromCompilationUnit(
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit)
{
Q_ASSERT(!m_scriptData);
+ Q_ASSERT(unit);
+
m_scriptData.adopt(new QQmlScriptData());
m_scriptData->url = finalUrl();
m_scriptData->urlString = finalUrlString();
@@ -192,12 +197,10 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Exe
m_importCache->setBaseUrl(finalUrl(), finalUrlString());
- QQmlRefPointer<QV4::ExecutableCompilationUnit> script = m_scriptData->m_precompiledScript;
-
if (!m_isModule) {
QList<QQmlError> errors;
- for (quint32 i = 0, count = script->importCount(); i < count; ++i) {
- const QV4::CompiledData::Import *import = script->importAt(i);
+ for (quint32 i = 0, count = unit->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = unit->importAt(i);
if (!addImport(import, {}, &errors)) {
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
@@ -211,22 +214,25 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Exe
}
}
- auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
-
- v4->injectCompiledModule(unit);
+ const QStringList moduleRequests = unit->moduleRequests();
+ for (const QString &request: moduleRequests) {
+ const QUrl relativeRequest = QUrl(request);
+ if (m_typeLoader->injectedScript(relativeRequest))
+ continue;
- for (const QString &request: unit->moduleRequests()) {
- const auto module = v4->moduleForUrl(QUrl(request), unit.data());
- if (module.compiled || module.native)
+ const QUrl absoluteRequest = unit->finalUrl().resolved(relativeRequest);
+ QQmlRefPointer<QQmlScriptBlob> absoluteBlob = typeLoader()->getScript(absoluteRequest);
+ if (absoluteBlob->m_scriptData && absoluteBlob->m_scriptData->m_precompiledScript)
continue;
- const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request));
- QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(absoluteRequest);
- addDependency(blob.data());
- scriptImported(blob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(), /*namespace*/QString());
+ addDependency(absoluteBlob.data());
+ scriptImported(
+ absoluteBlob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(),
+ /*namespace*/QString());
}
}
+
/*!
\internal
diff --git a/src/qml/qml/qqmlscriptblob_p.h b/src/qml/qml/qqmlscriptblob_p.h
index ad9c573400..59f969859b 100644
--- a/src/qml/qml/qqmlscriptblob_p.h
+++ b/src/qml/qml/qqmlscriptblob_p.h
@@ -39,6 +39,7 @@ public:
};
QQmlRefPointer<QQmlScriptData> scriptData() const;
+ bool isNative() const;
protected:
void dataReceived(const SourceCodeData &) override;
@@ -49,7 +50,7 @@ protected:
private:
void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
- void initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit);
+ void initializeFromCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&cu);
void initializeFromNative(const QV4::Value &value);
QList<ScriptReference> m_scripts;
diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp
index 1a6a8157d0..9337f25c6e 100644
--- a/src/qml/qml/qqmlscriptdata.cpp
+++ b/src/qml/qml/qqmlscriptdata.cpp
@@ -13,11 +13,6 @@
QT_BEGIN_NAMESPACE
-QQmlScriptData::QQmlScriptData()
- : m_loaded(false)
-{
-}
-
QQmlRefPointer<QQmlContextData> QQmlScriptData::qmlContextDataForContext(
const QQmlRefPointer<QQmlContextData> &parentQmlContextData)
{
@@ -86,7 +81,11 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(
/* scopeObject: */ nullptr);
}
- QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4));
+ QV4::Scoped<QV4::Module> module(
+ scope,
+ v4->executableCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
+ m_precompiledScript))->instantiate());
+
if (module) {
if (qmlExecutionContext) {
module->d()->scope->outer.set(v4, qmlExecutionContext->d());
diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h
index c7100861e2..ad5ffb3d07 100644
--- a/src/qml/qml/qqmlscriptdata_p.h
+++ b/src/qml/qml/qqmlscriptdata_p.h
@@ -19,7 +19,7 @@
#include <private/qqmlscriptblob_p.h>
#include <private/qv4value_p.h>
#include <private/qv4persistent_p.h>
-#include <private/qv4executablecompilationunit_p.h>
+#include <private/qv4compileddata_p.h>
#include <QtCore/qurl.h>
@@ -28,12 +28,12 @@ QT_BEGIN_NAMESPACE
class QQmlTypeNameCache;
class QQmlContextData;
-class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlRefCount
+class Q_AUTOTEST_EXPORT QQmlScriptData final : public QQmlRefCounted<QQmlScriptData>
{
private:
friend class QQmlTypeLoader;
- QQmlScriptData();
+ QQmlScriptData() = default;
public:
QUrl url;
@@ -43,7 +43,10 @@ public:
QV4::ReturnedValue scriptValueForContext(const QQmlRefPointer<QQmlContextData> &parentCtxt);
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; }
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit() const
+ {
+ return m_precompiledScript;
+ }
private:
friend class QQmlScriptBlob;
@@ -51,8 +54,8 @@ private:
QQmlRefPointer<QQmlContextData> qmlContextDataForContext(
const QQmlRefPointer<QQmlContextData> &parentQmlContextData);
- bool m_loaded;
- QQmlRefPointer<QV4::ExecutableCompilationUnit> m_precompiledScript;
+ bool m_loaded = false;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_precompiledScript;
QV4::PersistentValue m_value;
};
diff --git a/src/qml/qml/qqmlscriptstring.cpp b/src/qml/qml/qqmlscriptstring.cpp
index 370573b199..9f47841031 100644
--- a/src/qml/qml/qqmlscriptstring.cpp
+++ b/src/qml/qml/qqmlscriptstring.cpp
@@ -44,7 +44,7 @@ const QQmlScriptStringPrivate* QQmlScriptStringPrivate::get(const QQmlScriptStri
Constructs an empty instance.
*/
QQmlScriptString::QQmlScriptString()
-: d(new QQmlScriptStringPrivate)
+: d()
{
}
@@ -92,6 +92,8 @@ bool QQmlScriptString::operator==(const QQmlScriptString &other) const
{
if (d == other.d)
return true;
+ if (!d || !other.d)
+ return false;
if (d->isNumberLiteral || other.d->isNumberLiteral)
return d->isNumberLiteral && other.d->isNumberLiteral && d->numberValue == other.d->numberValue;
@@ -126,6 +128,8 @@ Returns whether the QQmlScriptString is empty.
*/
bool QQmlScriptString::isEmpty() const
{
+ if (!d)
+ return true;
if (!d->script.isEmpty())
return false;
return d->bindingId == -1;
@@ -136,7 +140,7 @@ Returns whether the content of the QQmlScriptString is the \c undefined literal.
*/
bool QQmlScriptString::isUndefinedLiteral() const
{
- return d->script == QLatin1String("undefined");
+ return d && d->script == QLatin1String("undefined");
}
/*!
@@ -144,7 +148,7 @@ Returns whether the content of the QQmlScriptString is the \c null literal.
*/
bool QQmlScriptString::isNullLiteral() const
{
- return d->script == QLatin1String("null");
+ return d && d->script == QLatin1String("null");
}
/*!
@@ -153,7 +157,7 @@ Otherwise returns a null QString.
*/
QString QQmlScriptString::stringLiteral() const
{
- if (d->isStringLiteral)
+ if (d && d->isStringLiteral)
return d->script.mid(1, d->script.size()-2);
return QString();
}
@@ -165,8 +169,8 @@ sets \a ok to true. Otherwise returns 0.0 and sets \a ok to false.
qreal QQmlScriptString::numberLiteral(bool *ok) const
{
if (ok)
- *ok = d->isNumberLiteral;
- return d->isNumberLiteral ? d->numberValue : 0.;
+ *ok = d && d->isNumberLiteral;
+ return (d && d->isNumberLiteral) ? d->numberValue : 0.;
}
/*!
@@ -175,8 +179,8 @@ sets \a ok to true. Otherwise returns false and sets \a ok to false.
*/
bool QQmlScriptString::booleanLiteral(bool *ok) const
{
- bool isTrue = d->script == QLatin1String("true");
- bool isFalse = !isTrue && d->script == QLatin1String("false");
+ bool isTrue = d && d->script == QLatin1String("true");
+ bool isFalse = !isTrue && d && d->script == QLatin1String("false");
if (ok)
*ok = isTrue || isFalse;
return isTrue ? true : false;
diff --git a/src/qml/qml/qqmlscriptstring.h b/src/qml/qml/qqmlscriptstring.h
index 51e86089e8..debc3d16c1 100644
--- a/src/qml/qml/qqmlscriptstring.h
+++ b/src/qml/qml/qqmlscriptstring.h
@@ -23,7 +23,6 @@ namespace QV4 {
class Q_QML_EXPORT QQmlScriptString
{
Q_GADGET
- QML_ANONYMOUS
public:
QQmlScriptString();
QQmlScriptString(const QQmlScriptString &);
diff --git a/src/qml/qml/qqmlstringconverters.cpp b/src/qml/qml/qqmlstringconverters.cpp
index c1625b6667..a1f8cce67c 100644
--- a/src/qml/qml/qqmlstringconverters.cpp
+++ b/src/qml/qml/qqmlstringconverters.cpp
@@ -40,8 +40,8 @@ QVariant QQmlStringConverters::variantFromString(const QString &s, QMetaType pre
case QMetaType::QRect:
return QVariant::fromValue(rectFFromString(s, ok).toRect());
default: {
- QVariant ret(preferredType);
- if (QQmlValueTypeProvider::createValueType(s, preferredType, ret.data())) {
+ const QVariant ret = QQmlValueTypeProvider::createValueType(s, preferredType);
+ if (ret.isValid()) {
if (ok)
*ok = true;
return ret;
@@ -89,77 +89,19 @@ QDateTime QQmlStringConverters::dateTimeFromString(const QString &s, bool *ok)
//expects input of "x,y"
QPointF QQmlStringConverters::pointFFromString(const QString &s, bool *ok)
{
- if (s.count(QLatin1Char(',')) != 1) {
- if (ok)
- *ok = false;
- return QPointF();
- }
-
- bool xGood, yGood;
- int index = s.indexOf(QLatin1Char(','));
- qreal xCoord = QStringView{s}.left(index).toDouble(&xGood);
- qreal yCoord = QStringView{s}.mid(index+1).toDouble(&yGood);
- if (!xGood || !yGood) {
- if (ok)
- *ok = false;
- return QPointF();
- }
-
- if (ok)
- *ok = true;
- return QPointF(xCoord, yCoord);
+ return valueTypeFromNumberString<QPointF, 2, u','>(s, ok);
}
//expects input of "widthxheight"
QSizeF QQmlStringConverters::sizeFFromString(const QString &s, bool *ok)
{
- if (s.count(QLatin1Char('x')) != 1) {
- if (ok)
- *ok = false;
- return QSizeF();
- }
-
- bool wGood, hGood;
- int index = s.indexOf(QLatin1Char('x'));
- qreal width = QStringView{s}.left(index).toDouble(&wGood);
- qreal height = QStringView{s}.mid(index+1).toDouble(&hGood);
- if (!wGood || !hGood) {
- if (ok)
- *ok = false;
- return QSizeF();
- }
-
- if (ok)
- *ok = true;
- return QSizeF(width, height);
+ return valueTypeFromNumberString<QSizeF, 2, u'x'>(s, ok);
}
//expects input of "x,y,widthxheight" //### use space instead of second comma?
QRectF QQmlStringConverters::rectFFromString(const QString &s, bool *ok)
{
- if (s.count(QLatin1Char(',')) != 2 || s.count(QLatin1Char('x')) != 1) {
- if (ok)
- *ok = false;
- return QRectF();
- }
-
- bool xGood, yGood, wGood, hGood;
- int index = s.indexOf(QLatin1Char(','));
- qreal x = QStringView{s}.left(index).toDouble(&xGood);
- int index2 = s.indexOf(QLatin1Char(','), index+1);
- qreal y = QStringView{s}.mid(index+1, index2-index-1).toDouble(&yGood);
- index = s.indexOf(QLatin1Char('x'), index2+1);
- qreal width = QStringView{s}.mid(index2+1, index-index2-1).toDouble(&wGood);
- qreal height = QStringView{s}.mid(index+1).toDouble(&hGood);
- if (!xGood || !yGood || !wGood || !hGood) {
- if (ok)
- *ok = false;
- return QRectF();
- }
-
- if (ok)
- *ok = true;
- return QRectF(x, y, width, height);
+ return valueTypeFromNumberString<QRectF, 4, u',', u',', u'x'>(s, ok);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlstringconverters_p.h b/src/qml/qml/qqmlstringconverters_p.h
index 5a07b947ca..0d220b11fc 100644
--- a/src/qml/qml/qqmlstringconverters_p.h
+++ b/src/qml/qml/qqmlstringconverters_p.h
@@ -30,19 +30,92 @@ class QByteArray;
namespace QQmlStringConverters
{
- Q_QML_PRIVATE_EXPORT QVariant variantFromString(const QString &, QMetaType preferredType, bool *ok = nullptr);
+ Q_QML_EXPORT QVariant variantFromString(const QString &, QMetaType preferredType, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QVariant colorFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT unsigned rgbaFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QVariant colorFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT unsigned rgbaFromString(const QString &, bool *ok = nullptr);
#if QT_CONFIG(datestring)
- Q_QML_PRIVATE_EXPORT QDate dateFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QTime timeFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QDateTime dateTimeFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QDate dateFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QTime timeFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QDateTime dateTimeFromString(const QString &, bool *ok = nullptr);
#endif
- Q_QML_PRIVATE_EXPORT QPointF pointFFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QSizeF sizeFFromString(const QString &, bool *ok = nullptr);
- Q_QML_PRIVATE_EXPORT QRectF rectFFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QPointF pointFFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QSizeF sizeFFromString(const QString &, bool *ok = nullptr);
+ Q_QML_EXPORT QRectF rectFFromString(const QString &, bool *ok = nullptr);
+
+ // checks if the string contains a list of doubles separated by separators, like "double1
+ // separators1 double2 separators2 ..." for example.
+ template<int NumParams, char16_t... separators>
+ bool isValidNumberString(const QString &s, std::array<double, NumParams> *numbers = nullptr)
+ {
+ Q_STATIC_ASSERT_X(
+ NumParams == 2 || NumParams == 3 || NumParams == 4 || NumParams == 16,
+ "Unsupported number of params; add an additional case below if necessary.");
+ constexpr std::array<char16_t, NumParams - 1> separatorArray{ separators... };
+ // complain about missing separators when first or last entry is initialized with 0
+ Q_STATIC_ASSERT_X(separatorArray[0] != 0,
+ "Did not specify any separators for isValidNumberString.");
+ Q_STATIC_ASSERT_X(separatorArray[NumParams - 2] != 0,
+ "Did not specify enough separators for isValidNumberString.");
+
+ bool floatOk = true;
+ QStringView view(s);
+ for (qsizetype i = 0; i < NumParams - 1; ++i) {
+ const qsizetype commaIndex = view.indexOf(separatorArray[i]);
+ if (commaIndex == -1)
+ return false;
+ const auto current = view.first(commaIndex).toDouble(&floatOk);
+ if (!floatOk)
+ return false;
+ if (numbers)
+ (*numbers)[i] = current;
+
+ view = view.sliced(commaIndex + 1);
+ }
+ const auto current = view.toDouble(&floatOk);
+ if (!floatOk)
+ return false;
+ if (numbers)
+ (*numbers)[NumParams - 1] = current;
+
+ return true;
+ }
+
+ // Constructs a value type T from the given string that contains NumParams double values
+ // separated by separators, like "double1 separators1 double2 separators2 ..." for example.
+ template<typename T, int NumParams, char16_t... separators>
+ T valueTypeFromNumberString(const QString &s, bool *ok = nullptr)
+ {
+ Q_STATIC_ASSERT_X(
+ NumParams == 2 || NumParams == 3 || NumParams == 4 || NumParams == 16,
+ "Unsupported number of params; add an additional case below if necessary.");
+
+ std::array<double, NumParams> parameters;
+ if (!isValidNumberString<NumParams, separators...>(s, &parameters)) {
+ if (ok)
+ *ok = false;
+ return T{};
+ }
+
+ if (ok)
+ *ok = true;
+
+ if constexpr (NumParams == 2) {
+ return T(parameters[0], parameters[1]);
+ } else if constexpr (NumParams == 3) {
+ return T(parameters[0], parameters[1], parameters[2]);
+ } else if constexpr (NumParams == 4) {
+ return T(parameters[0], parameters[1], parameters[2], parameters[3]);
+ } else if constexpr (NumParams == 16) {
+ return T(parameters[0], parameters[1], parameters[2], parameters[3], parameters[4],
+ parameters[5], parameters[6], parameters[7], parameters[8], parameters[9],
+ parameters[10], parameters[11], parameters[12], parameters[13], parameters[14],
+ parameters[15]);
+ }
+
+ Q_UNREACHABLE_RETURN(T{});
+ }
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index d01aa5ba11..533c3909ba 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -17,47 +17,44 @@
QT_BEGIN_NAMESPACE
QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
- : regType(type), iid(nullptr), revision(QTypeRevision::zero()),
- containsRevisionedAttributes(false), baseMetaObject(nullptr),
- index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false),
- haveSuperType(false)
+ : regType(type)
{
switch (type) {
case QQmlType::CppType:
- extraData.cd = new QQmlCppTypeData;
- extraData.cd->allocationSize = 0;
- extraData.cd->newFunc = nullptr;
- extraData.cd->createValueTypeFunc = nullptr;
- extraData.cd->parserStatusCast = -1;
- extraData.cd->extFunc = nullptr;
- extraData.cd->extMetaObject = nullptr;
- extraData.cd->customParser = nullptr;
- extraData.cd->attachedPropertiesFunc = nullptr;
- extraData.cd->attachedPropertiesType = nullptr;
- extraData.cd->propertyValueSourceCast = -1;
- extraData.cd->propertyValueInterceptorCast = -1;
- extraData.cd->finalizerCast = -1;
- extraData.cd->registerEnumClassesUnscoped = true;
- extraData.cd->registerEnumsFromRelatedTypes = true;
+ extraData.cppTypeData = new QQmlCppTypeData;
+ extraData.cppTypeData->allocationSize = 0;
+ extraData.cppTypeData->newFunc = nullptr;
+ extraData.cppTypeData->createValueTypeFunc = nullptr;
+ extraData.cppTypeData->parserStatusCast = -1;
+ extraData.cppTypeData->extFunc = nullptr;
+ extraData.cppTypeData->extMetaObject = nullptr;
+ extraData.cppTypeData->customParser = nullptr;
+ extraData.cppTypeData->attachedPropertiesFunc = nullptr;
+ extraData.cppTypeData->attachedPropertiesType = nullptr;
+ extraData.cppTypeData->propertyValueSourceCast = -1;
+ extraData.cppTypeData->propertyValueInterceptorCast = -1;
+ extraData.cppTypeData->finalizerCast = -1;
+ extraData.cppTypeData->registerEnumClassesUnscoped = true;
+ extraData.cppTypeData->registerEnumsFromRelatedTypes = true;
break;
case QQmlType::SingletonType:
case QQmlType::CompositeSingletonType:
- extraData.sd = new QQmlSingletonTypeData;
- extraData.sd->singletonInstanceInfo = nullptr;
- extraData.sd->extFunc = nullptr;
- extraData.sd->extMetaObject = nullptr;
+ extraData.singletonTypeData = new QQmlSingletonTypeData;
+ extraData.singletonTypeData->singletonInstanceInfo = nullptr;
+ extraData.singletonTypeData->extFunc = nullptr;
+ extraData.singletonTypeData->extMetaObject = nullptr;
break;
case QQmlType::InterfaceType:
- extraData.cd = nullptr;
+ extraData.interfaceTypeData = nullptr;
break;
case QQmlType::CompositeType:
- extraData.fd = new QQmlCompositeTypeData;
+ new (&extraData.compositeTypeData) QUrl();
break;
case QQmlType::InlineComponentType:
- extraData.id = new QQmlInlineTypeData;
+ new (&extraData.inlineComponentTypeData) QUrl();
break;
case QQmlType::SequentialContainerType:
- extraData.ld = new QQmlSequenceTypeData;
+ new (&extraData.sequentialContainerTypeData) QMetaSequence();
break;
default: qFatal("QQmlTypePrivate Internal Error.");
}
@@ -65,27 +62,32 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
QQmlTypePrivate::~QQmlTypePrivate()
{
- qDeleteAll(scopedEnums);
- for (const auto &metaObject : metaObjects)
- free(metaObject.metaObject);
+ delete enums.fetchAndStoreAcquire(nullptr);
+ delete proxyMetaObjects.fetchAndStoreAcquire(nullptr);
+
+ if (const QtPrivate::QMetaTypeInterface *iface = typeId.iface()) {
+ if (iface->metaObjectFn == &dynamicQmlMetaObject)
+ QQmlMetaType::unregisterInternalCompositeType(typeId, listId);
+ }
+
switch (regType) {
case QQmlType::CppType:
- delete extraData.cd->customParser;
- delete extraData.cd;
+ delete extraData.cppTypeData->customParser;
+ delete extraData.cppTypeData;
break;
case QQmlType::SingletonType:
case QQmlType::CompositeSingletonType:
- delete extraData.sd->singletonInstanceInfo;
- delete extraData.sd;
+ extraData.singletonTypeData->singletonInstanceInfo.reset();
+ delete extraData.singletonTypeData;
break;
case QQmlType::CompositeType:
- delete extraData.fd;
+ extraData.compositeTypeData.~QUrl();
break;
case QQmlType::InlineComponentType:
- delete extraData.id;
+ extraData.inlineComponentTypeData.~QUrl();
break;
case QQmlType::SequentialContainerType:
- delete extraData.ld;
+ extraData.sequentialContainerTypeData.~QMetaSequence();
break;
default: //Also InterfaceType, because it has no extra data
break;
@@ -144,7 +146,7 @@ QQmlType QQmlTypePrivate::resolveCompositeBaseType(QQmlEnginePrivate *engine) co
QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
return QQmlType();
- QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit();
+ QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject();
return QQmlMetaType::qmlType(mo);
}
@@ -159,7 +161,7 @@ QQmlPropertyCache::ConstPtr QQmlTypePrivate::compositePropertyCache(
QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
return nullptr;
- QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit();
+ QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
return compilationUnit->rootPropertyCache();
}
@@ -168,21 +170,29 @@ static bool isPropertyRevisioned(const QMetaObject *mo, int index)
return mo->property(index).revision();
}
-void QQmlTypePrivate::init() const
+const QQmlTypePrivate::ProxyMetaObjects *QQmlTypePrivate::init() const
{
- if (isSetup.loadAcquire())
- return;
+ if (const ProxyMetaObjects *result = proxyMetaObjects.loadRelaxed())
+ return result;
- QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
- if (isSetup.loadAcquire())
- return;
+ ProxyMetaObjects *proxies = new ProxyMetaObjects;
+ auto finalize = [this, proxies]() -> const ProxyMetaObjects *{
+ const ProxyMetaObjects *concurrentModification;
+ if (proxyMetaObjects.testAndSetOrdered(nullptr, proxies, concurrentModification))
+ return proxies;
+
+ delete proxies;
+ return concurrentModification;
+ };
const QMetaObject *mo = baseMetaObject;
if (!mo) {
// version 0 singleton type without metaobject information
- return;
+ return finalize();
}
+ QList<QQmlProxyMetaObject::ProxyData> metaObjects;
+
auto setupExtendedMetaObject = [&](const QMetaObject *extMetaObject,
QObject *(*extFunc)(QObject *)) {
if (!extMetaObject)
@@ -200,9 +210,9 @@ void QQmlTypePrivate::init() const
};
if (regType == QQmlType::SingletonType)
- setupExtendedMetaObject(extraData.sd->extMetaObject, extraData.sd->extFunc);
+ setupExtendedMetaObject(extraData.singletonTypeData->extMetaObject, extraData.singletonTypeData->extFunc);
else if (regType == QQmlType::CppType)
- setupExtendedMetaObject(extraData.cd->extMetaObject, extraData.cd->extFunc);
+ setupExtendedMetaObject(extraData.cppTypeData->extMetaObject, extraData.cppTypeData->extFunc);
metaObjects.append(QQmlMetaType::proxyData(
mo, baseMetaObject, metaObjects.isEmpty() ? nullptr
@@ -215,6 +225,8 @@ void QQmlTypePrivate::init() const
metaObjects.at(ii).metaObject->methodOffset();
}
+ bool containsRevisionedAttributes = false;
+
// Check for revisioned details
{
const QMetaObject *mo = nullptr;
@@ -234,46 +246,54 @@ void QQmlTypePrivate::init() const
}
}
- isSetup.storeRelease(true);
- lock.unlock();
+ proxies->data = std::move(metaObjects);
+ proxies->containsRevisionedAttributes = containsRevisionedAttributes;
+
+ return finalize();
}
-void QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const
+const QQmlTypePrivate::Enums *QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const
{
- QQmlPropertyCache::ConstPtr cache = (!isEnumFromCacheSetup.loadAcquire() && isComposite())
- ? compositePropertyCache(engine)
- : QQmlPropertyCache::ConstPtr();
+ if (const Enums *result = enums.loadRelaxed())
+ return result;
- // beware: It could be a singleton type without metaobject
- const QMetaObject *metaObject = !isEnumFromBaseSetup.loadAcquire()
- ? baseMetaObject
- : nullptr;
+ QQmlPropertyCache::ConstPtr cache;
+ if (isComposite()) {
+ cache = compositePropertyCache(engine);
+ if (!cache)
+ return nullptr; // Composite type not ready, yet.
+ }
- if (!cache && !metaObject)
- return;
+ Enums *newEnums = new Enums;
- init(); // init() can add to the metaObjects list. Therefore, check metaObjects only below
+ // beware: It could be a singleton type without metaobject
- QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
+ if (cache)
+ insertEnumsFromPropertyCache(newEnums, cache);
- if (cache) {
- insertEnumsFromPropertyCache(cache);
- isEnumFromCacheSetup.storeRelease(true);
+ if (baseMetaObject) {
+ // init() can add to the metaObjects list. Therefore, check proxies->data only below
+ const ProxyMetaObjects *proxies = init();
+ insertEnums(
+ newEnums,
+ proxies->data.isEmpty() ? baseMetaObject : proxies->data.constFirst().metaObject);
}
- if (metaObject) {
- insertEnums(metaObjects.isEmpty() ? baseMetaObject : metaObjects.constFirst().metaObject);
- isEnumFromBaseSetup.storeRelease(true);
- }
+ const Enums *concurrentModification;
+ if (enums.testAndSetOrdered(nullptr, newEnums, concurrentModification))
+ return newEnums;
+
+ delete newEnums;
+ return concurrentModification;
}
-void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
+void QQmlTypePrivate::insertEnums(Enums *enums, const QMetaObject *metaObject) const
{
// Add any enum values defined by 'related' classes
- if (regType != QQmlType::CppType || extraData.cd->registerEnumsFromRelatedTypes) {
+ if (regType != QQmlType::CppType || extraData.cppTypeData->registerEnumsFromRelatedTypes) {
if (const auto *related = metaObject->d.relatedMetaObjects) {
while (const QMetaObject *relatedMetaObject = *related) {
- insertEnums(relatedMetaObject);
+ insertEnums(enums, relatedMetaObject);
++related;
}
}
@@ -282,6 +302,19 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
QSet<QString> localEnums;
const QMetaObject *localMetaObject = nullptr;
+ // ### TODO (QTBUG-123294): track this at instance creation time
+ auto shouldSingletonAlsoRegisterUnscoped = [&](){
+ Q_ASSERT(regType == QQmlType::SingletonType);
+ if (!baseMetaObject)
+ return true;
+ int idx = baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped");
+ if (idx == -1)
+ return true;
+ if (qstrcmp(baseMetaObject->classInfo(idx).value(), "false") == 0)
+ return false;
+ return true;
+ };
+
// Add any enum values defined by this class, overwriting any inherited values
for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) {
QMetaEnum e = metaObject->enumerator(ii);
@@ -298,29 +331,33 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
localEnums.clear();
localMetaObject = e.enclosingMetaObject();
}
+ const bool shouldRegisterUnscoped = !isScoped
+ || (regType == QQmlType::CppType && extraData.cppTypeData->registerEnumClassesUnscoped)
+ || (regType == QQmlType::SingletonType && shouldSingletonAlsoRegisterUnscoped())
+ ;
for (int jj = 0; jj < e.keyCount(); ++jj) {
const QString key = QString::fromUtf8(e.key(jj));
const int value = e.value(jj);
- if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) {
+ if (shouldRegisterUnscoped) {
if (localEnums.contains(key)) {
- auto existingEntry = enums.find(key);
- if (existingEntry != enums.end() && existingEntry.value() != value) {
+ auto existingEntry = enums->enums.find(key);
+ if (existingEntry != enums->enums.end() && existingEntry.value() != value) {
qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData());
createEnumConflictReport(metaObject, key);
}
} else {
localEnums.insert(key);
}
- enums.insert(key, value);
+ enums->enums.insert(key, value);
}
if (isScoped)
scoped->insert(key, value);
}
if (isScoped) {
- scopedEnums << scoped;
- scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.size()-1);
+ enums->scopedEnums << scoped;
+ enums->scopedEnumIndex.insert(QString::fromUtf8(e.name()), enums->scopedEnums.size()-1);
}
}
}
@@ -379,7 +416,7 @@ void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, co
}
void QQmlTypePrivate::insertEnumsFromPropertyCache(
- const QQmlPropertyCache::ConstPtr &cache) const
+ Enums *enums, const QQmlPropertyCache::ConstPtr &cache) const
{
const QMetaObject *cppMetaObject = cache->firstCppMetaObject();
@@ -394,20 +431,14 @@ void QQmlTypePrivate::insertEnumsFromPropertyCache(
for (int jj = 0; jj < enumData->values.size(); ++jj) {
const QQmlEnumValue &value = enumData->values.at(jj);
- enums.insert(value.namedValue, value.value);
+ enums->enums.insert(value.namedValue, value.value);
scoped->insert(value.namedValue, value.value);
}
- scopedEnums << scoped;
- scopedEnumIndex.insert(enumData->name, scopedEnums.size()-1);
+ enums->scopedEnums << scoped;
+ enums->scopedEnumIndex.insert(enumData->name, enums->scopedEnums.size()-1);
}
}
- insertEnums(cppMetaObject);
-}
-
-void QQmlTypePrivate::setContainingType(QQmlType *containingType)
-{
- Q_ASSERT(regType == QQmlType::InlineComponentType);
- extraData.id->containingType = containingType->d.data();
+ insertEnums(enums, cppMetaObject);
}
void QQmlTypePrivate::setName(const QString &uri, const QString &element)
@@ -421,11 +452,9 @@ QByteArray QQmlType::typeName() const
{
if (d) {
if (d->regType == SingletonType || d->regType == CompositeSingletonType)
- return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8();
+ return d->extraData.singletonTypeData->singletonInstanceInfo->typeName;
else if (d->baseMetaObject)
return d->baseMetaObject->className();
- else if (d->regType == InlineComponentType)
- return d->extraData.id->inlineComponentName.toUtf8();
}
return QByteArray();
}
@@ -473,13 +502,11 @@ QObject *QQmlType::create(void **memory, size_t additionalMemory) const
if (!d || !isCreatable())
return nullptr;
- d->init();
-
- QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory);
- d->extraData.cd->newFunc(rv, d->extraData.cd->userdata);
+ QObject *rv = (QObject *)operator new(d->extraData.cppTypeData->allocationSize + additionalMemory);
+ d->extraData.cppTypeData->newFunc(rv, d->extraData.cppTypeData->userdata);
createProxy(rv);
- *memory = ((char *)rv) + d->extraData.cd->allocationSize;
+ *memory = ((char *)rv) + d->extraData.cppTypeData->allocationSize;
return rv;
}
@@ -494,21 +521,20 @@ QObject *QQmlType::createWithQQmlData() const
auto instance = create(&ddataMemory, sizeof(QQmlData));
if (!instance)
return nullptr;
- QQmlData *ddata = new (ddataMemory) QQmlData;
- ddata->ownMemory = false;
QObjectPrivate* p = QObjectPrivate::get(instance);
Q_ASSERT(!p->isDeletingChildren);
- p->declarativeData = ddata;
+ if (!p->declarativeData)
+ p->declarativeData = new (ddataMemory) QQmlData(QQmlData::DoesNotOwnMemory);
return instance;
}
-QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const
+QQmlType::SingletonInstanceInfo::ConstPtr QQmlType::singletonInstanceInfo() const
{
if (!d)
- return nullptr;
+ return {};
if (d->regType != SingletonType && d->regType != CompositeSingletonType)
- return nullptr;
- return d->extraData.sd->singletonInstanceInfo;
+ return {};
+ return d->extraData.singletonTypeData->singletonInstanceInfo;
}
QQmlCustomParser *QQmlType::customParser() const
@@ -517,47 +543,47 @@ QQmlCustomParser *QQmlType::customParser() const
return nullptr;
if (d->regType != CppType)
return nullptr;
- return d->extraData.cd->customParser;
+ return d->extraData.cppTypeData->customParser;
}
QQmlType::CreateValueTypeFunc QQmlType::createValueTypeFunction() const
{
if (!d || d->regType != CppType)
return nullptr;
- return d->extraData.cd->createValueTypeFunc;
+ return d->extraData.cppTypeData->createValueTypeFunc;
}
bool QQmlType::canConstructValueType() const
{
if (!d || d->regType != CppType)
return false;
- return d->extraData.cd->constructValueType;
+ return d->extraData.cppTypeData->constructValueType;
}
bool QQmlType::canPopulateValueType() const
{
if (!d || d->regType != CppType)
return false;
- return d->extraData.cd->populateValueType;
+ return d->extraData.cppTypeData->populateValueType;
}
QQmlType::CreateFunc QQmlType::createFunction() const
{
if (!d || d->regType != CppType)
return nullptr;
- return d->extraData.cd->newFunc;
+ return d->extraData.cppTypeData->newFunc;
}
QString QQmlType::noCreationReason() const
{
if (!d || d->regType != CppType)
return QString();
- return d->extraData.cd->noCreationReason;
+ return d->extraData.cppTypeData->noCreationReason;
}
bool QQmlType::isCreatable() const
{
- return d && d->regType == CppType && d->extraData.cd->newFunc;
+ return d && d->regType == CppType && d->extraData.cppTypeData->newFunc;
}
QQmlType::ExtensionFunc QQmlType::extensionFunction() const
@@ -567,9 +593,9 @@ QQmlType::ExtensionFunc QQmlType::extensionFunction() const
switch (d->regType) {
case CppType:
- return d->extraData.cd->extFunc;
+ return d->extraData.cppTypeData->extFunc;
case SingletonType:
- return d->extraData.sd->extFunc;
+ return d->extraData.singletonTypeData->extFunc;
default:
return nullptr;
}
@@ -582,9 +608,9 @@ const QMetaObject *QQmlType::extensionMetaObject() const
switch (d->regType) {
case CppType:
- return d->extraData.cd->extMetaObject;
+ return d->extraData.cppTypeData->extMetaObject;
case SingletonType:
- return d->extraData.sd->extMetaObject;
+ return d->extraData.singletonTypeData->extMetaObject;
default:
return nullptr;
}
@@ -592,11 +618,7 @@ const QMetaObject *QQmlType::extensionMetaObject() const
bool QQmlType::isExtendedType() const
{
- if (!d)
- return false;
- d->init();
-
- return !d->metaObjects.isEmpty();
+ return d && !d->init()->data.isEmpty();
}
bool QQmlType::isSingleton() const
@@ -625,12 +647,12 @@ bool QQmlType::isCompositeSingleton() const
bool QQmlType::isQObjectSingleton() const
{
- return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->qobjectCallback;
+ return d && d->regType == SingletonType && d->extraData.singletonTypeData->singletonInstanceInfo->qobjectCallback;
}
bool QQmlType::isQJSValueSingleton() const
{
- return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->scriptCallback;
+ return d && d->regType == SingletonType && d->extraData.singletonTypeData->singletonInstanceInfo->scriptCallback;
}
bool QQmlType::isSequentialContainer() const
@@ -638,6 +660,11 @@ bool QQmlType::isSequentialContainer() const
return d && d->regType == SequentialContainerType;
}
+bool QQmlType::isValueType() const
+{
+ return d && d->isValueType();
+}
+
QMetaType QQmlType::typeId() const
{
return d ? d->typeId : QMetaType{};
@@ -650,20 +677,18 @@ QMetaType QQmlType::qListTypeId() const
QMetaSequence QQmlType::listMetaSequence() const
{
- return isSequentialContainer() ? *d->extraData.ld : QMetaSequence();
+ return isSequentialContainer() ? d->extraData.sequentialContainerTypeData : QMetaSequence();
}
const QMetaObject *QQmlType::metaObject() const
{
- if (!d)
- return nullptr;
- d->init();
-
- if (d->metaObjects.isEmpty())
- return d->baseMetaObject;
- else
- return d->metaObjects.constFirst().metaObject;
+ return d ? d->metaObject() : nullptr;
+}
+const QMetaObject *QQmlType::metaObjectForValueType() const
+{
+ Q_ASSERT(d);
+ return d->metaObjectForValueType();
}
const QMetaObject *QQmlType::baseMetaObject() const
@@ -673,11 +698,7 @@ const QMetaObject *QQmlType::baseMetaObject() const
bool QQmlType::containsRevisionedAttributes() const
{
- if (!d)
- return false;
- d->init();
-
- return d->containsRevisionedAttributes;
+ return d && d->init()->containsRevisionedAttributes;
}
QTypeRevision QQmlType::metaObjectRevision() const
@@ -688,14 +709,14 @@ QTypeRevision QQmlType::metaObjectRevision() const
QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const
{
if (const QQmlTypePrivate *base = d ? d->attachedPropertiesBase(engine) : nullptr)
- return base->extraData.cd->attachedPropertiesFunc;
+ return base->extraData.cppTypeData->attachedPropertiesFunc;
return nullptr;
}
const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const
{
if (const QQmlTypePrivate *base = d ? d->attachedPropertiesBase(engine) : nullptr)
- return base->extraData.cd->attachedPropertiesType;
+ return base->extraData.cppTypeData->attachedPropertiesType;
return nullptr;
}
@@ -703,35 +724,35 @@ int QQmlType::parserStatusCast() const
{
if (!d || d->regType != CppType)
return -1;
- return d->extraData.cd->parserStatusCast;
+ return d->extraData.cppTypeData->parserStatusCast;
}
int QQmlType::propertyValueSourceCast() const
{
if (!d || d->regType != CppType)
return -1;
- return d->extraData.cd->propertyValueSourceCast;
+ return d->extraData.cppTypeData->propertyValueSourceCast;
}
int QQmlType::propertyValueInterceptorCast() const
{
if (!d || d->regType != CppType)
return -1;
- return d->extraData.cd->propertyValueInterceptorCast;
+ return d->extraData.cppTypeData->propertyValueInterceptorCast;
}
int QQmlType::finalizerCast() const
{
if (!d || d->regType != CppType)
return -1;
- return d->extraData.cd->finalizerCast;
+ return d->extraData.cppTypeData->finalizerCast;
}
const char *QQmlType::interfaceIId() const
{
if (!d || d->regType != InterfaceType)
return nullptr;
- return d->iid;
+ return d->extraData.interfaceTypeData;
}
int QQmlType::index() const
@@ -743,200 +764,49 @@ bool QQmlType::isInlineComponentType() const {
return d ? d->regType == QQmlType::InlineComponentType : false;
}
-int QQmlType::inlineComponentId() const {
- bool ok = false;
- if (d->regType == QQmlType::RegistrationType::InlineComponentType) {
- Q_ASSERT(d->extraData.id->objectId != -1);
- return d->extraData.id->objectId;
- }
- int subObjectId = sourceUrl().fragment().toInt(&ok);
- return ok ? subObjectId : -1;
-}
-
QUrl QQmlType::sourceUrl() const
{
- auto url = d ? d->sourceUrl() : QUrl();
- if (url.isValid() && d->regType == QQmlType::RegistrationType::InlineComponentType && d->extraData.id->objectId) {
- Q_ASSERT(url.hasFragment());
- url.setFragment(QString::number(inlineComponentId()));
- }
- return url;
+ return d ? d->sourceUrl() : QUrl();
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::enumValue(d, engine, name, ok);
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::enumValue(d, engine, name, ok);
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::enumValue(d, engine, name, ok);
}
int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->scopedEnumIndex.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::scopedEnumIndex(d, engine, name, ok);
}
int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->scopedEnumIndex.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::scopedEnumIndex(d, engine, name, ok);
}
int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const
{
- Q_UNUSED(engine);
- Q_ASSERT(ok);
- *ok = true;
-
- if (d) {
- Q_ASSERT(index > -1 && index < d->scopedEnums.size());
- int *rv = d->scopedEnums.at(index)->value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::scopedEnumValue(d, engine, index, name, ok);
}
int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const
{
- Q_UNUSED(engine);
- Q_ASSERT(ok);
- *ok = true;
-
- if (d) {
- Q_ASSERT(index > -1 && index < d->scopedEnums.size());
- int *rv = d->scopedEnums.at(index)->value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
+ return QQmlTypePrivate::scopedEnumValue(d, engine, index, name, ok);
}
-int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QHashedStringRef &scopedEnumName, const QHashedStringRef &name, bool *ok) const
{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.size()));
- if (rv) {
- int index = *rv;
- Q_ASSERT(index > -1 && index < d->scopedEnums.size());
- rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.size()));
- if (rv)
- return *rv;
- }
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, QStringView scopedEnumName, QStringView name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- *ok = true;
-
- d->initEnums(engine);
-
- int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName));
- if (rv) {
- int index = *rv;
- Q_ASSERT(index > -1 && index < d->scopedEnums.size());
- rv = d->scopedEnums.at(index)->value(QHashedStringRef(name));
- if (rv)
- return *rv;
- }
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::inlineComponentObjectId() const
-{
- if (!isInlineComponentType())
- return -1;
- return d->extraData.id->objectId;
-}
-
-void QQmlType::setInlineComponentObjectId(int id) const
-{
- Q_ASSERT(d && d->regType == QQmlType::InlineComponentType);
- d->extraData.id->objectId = id;
+ return QQmlTypePrivate::scopedEnumValue(d, engine, scopedEnumName, name, ok);
}
void QQmlType::refHandle(const QQmlTypePrivate *priv)
@@ -958,72 +828,11 @@ int QQmlType::refCount(const QQmlTypePrivate *priv)
return -1;
}
-int QQmlType::lookupInlineComponentIdByName(const QString &name) const
-{
- Q_ASSERT(d);
- return d->namesToInlineComponentObjectIndex.value(name, -1);
-}
-
-QQmlType QQmlType::containingType() const
-{
- Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
- auto ret = QQmlType {d->extraData.id->containingType};
- Q_ASSERT(!ret.isInlineComponentType());
- return ret;
-}
-
-QQmlType QQmlType::lookupInlineComponentById(int objectid) const
-{
- Q_ASSERT(d);
- return d->objectIdToICType.value(objectid, QQmlType(nullptr));
-}
-
-int QQmlType::generatePlaceHolderICId() const
-{
- Q_ASSERT(d);
- int id = -2;
- for (auto it = d->objectIdToICType.keyBegin(); it != d->objectIdToICType.keyEnd(); ++it)
- if (*it < id)
- id = *it;
- return id;
-}
-
-void QQmlType::associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType)
-{
- bool const reuseExistingType = existingType.isValid();
- auto priv = reuseExistingType ? const_cast<QQmlTypePrivate *>(existingType.d.data()) : new QQmlTypePrivate { RegistrationType::InlineComponentType } ;
- priv->setName( QString::fromUtf8(typeName()), name);
- auto icUrl = QUrl(sourceUrl());
- icUrl.setFragment(QString::number(objectID));
- priv->extraData.id->url = icUrl;
- priv->extraData.id->containingType = d.data();
- priv->extraData.id->objectId = objectID;
- priv->typeId = metaTypeIds.id;
- priv->listId = metaTypeIds.listId;
- d->namesToInlineComponentObjectIndex.insert(name, objectID);
- QQmlType icType(priv);
- d->objectIdToICType.insert(objectID, icType);
- if (!reuseExistingType)
- priv->release();
-}
-
-void QQmlType::setPendingResolutionName(const QString &name)
-{
- Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
- Q_ASSERT(d->extraData.id->inlineComponentName == name|| d->extraData.id->inlineComponentName.isEmpty());
- d->extraData.id->inlineComponentName = name;
-}
-
-QString QQmlType::pendingResolutionName() const
-{
- Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
- return d->extraData.id->inlineComponentName;
-}
-
void QQmlType::createProxy(QObject *instance) const
{
- if (!d->metaObjects.isEmpty())
- (void)new QQmlProxyMetaObject(instance, &d->metaObjects);
+ const QQmlTypePrivate::ProxyMetaObjects *proxies = d->init();
+ if (!proxies->data.isEmpty())
+ (void)new QQmlProxyMetaObject(instance, &proxies->data);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index c2c85c9c2e..622e91a4b2 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -39,9 +39,8 @@ class QQmlPropertyCache;
namespace QV4 {
struct String;
}
-struct CompositeMetaTypeIds;
-class Q_QML_PRIVATE_EXPORT QQmlType
+class Q_QML_EXPORT QQmlType
{
public:
QQmlType();
@@ -52,10 +51,6 @@ public:
explicit QQmlType(const QQmlTypePrivate *priv);
~QQmlType();
- bool operator ==(const QQmlType &other) const {
- return d.data() == other.d.data();
- }
-
bool isValid() const { return !d.isNull(); }
QByteArray typeName() const;
@@ -97,12 +92,17 @@ public:
bool isQObjectSingleton() const;
bool isQJSValueSingleton() const;
bool isSequentialContainer() const;
+ bool isValueType() const;
QMetaType typeId() const;
QMetaType qListTypeId() const;
QMetaSequence listMetaSequence() const;
const QMetaObject *metaObject() const;
+
+ // Precondition: The type is actually a value type!
+ const QMetaObject *metaObjectForValueType() const;
+
const QMetaObject *baseMetaObject() const;
QTypeRevision metaObjectRevision() const;
bool containsRevisionedAttributes() const;
@@ -119,17 +119,25 @@ public:
int index() const;
bool isInlineComponentType() const;
- int inlineComponentId() const;
- struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo
+ struct Q_QML_EXPORT SingletonInstanceInfo final
+ : public QQmlRefCounted<SingletonInstanceInfo>
{
+ using Ptr = QQmlRefPointer<SingletonInstanceInfo>;
+ using ConstPtr = QQmlRefPointer<const SingletonInstanceInfo>;
+
+ static Ptr create() { return Ptr(new SingletonInstanceInfo, Ptr::Adopt); }
+
std::function<QJSValue(QQmlEngine *, QJSEngine *)> scriptCallback = {};
std::function<QObject *(QQmlEngine *, QJSEngine *)> qobjectCallback = {};
- const QMetaObject *instanceMetaObject = nullptr;
- QString typeName;
+ QByteArray typeName;
QUrl url; // used by composite singletons
+
+ private:
+ Q_DISABLE_COPY_MOVE(SingletonInstanceInfo)
+ SingletonInstanceInfo() = default;
};
- SingletonInstanceInfo *singletonInstanceInfo() const;
+ SingletonInstanceInfo::ConstPtr singletonInstanceInfo() const;
QUrl sourceUrl() const;
@@ -141,10 +149,7 @@ public:
int scopedEnumIndex(QQmlEnginePrivate *engine, const QString &, bool *ok) const;
int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const;
int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const;
- int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const;
- int scopedEnumValue(QQmlEnginePrivate *engine, QStringView, QStringView, bool *ok) const;
- int inlineComponentObjectId() const;
- void setInlineComponentObjectId(int id) const; // TODO: const setters are BAD
+ int scopedEnumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, const QHashedStringRef &, bool *ok) const;
const QQmlTypePrivate *priv() const { return d.data(); }
static void refHandle(const QQmlTypePrivate *priv);
@@ -162,20 +167,20 @@ public:
AnyRegistrationType = 255
};
- QQmlType containingType() const;
- int lookupInlineComponentIdByName(const QString &name) const;
- QQmlType lookupInlineComponentById(int objectid) const;
- int generatePlaceHolderICId() const;
-
- void associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType);
- void setPendingResolutionName(const QString &name);
- QString pendingResolutionName() const;
-
void createProxy(QObject *instance) const;
private:
friend class QQmlTypePrivate;
friend size_t qHash(const QQmlType &t, size_t seed);
+ friend bool operator==(const QQmlType &a, const QQmlType &b) noexcept
+ {
+ return a.d.data() == b.d.data();
+ }
+ friend bool operator!=(const QQmlType &a, const QQmlType &b) noexcept
+ {
+ return !(a == b);
+ }
+
QQmlRefPointer<const QQmlTypePrivate> d;
};
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index d2b282955f..2bf83ddb8b 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -21,32 +21,52 @@
#include <private/qqmlrefcount_p.h>
#include <private/qqmlpropertycache_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qv4executablecompilationunit_p.h>
+#include <private/qv4engine_p.h>
#include <QAtomicInteger>
QT_BEGIN_NAMESPACE
-class QQmlTypePrivate : public QQmlRefCount
+class QQmlTypePrivate final : public QQmlRefCounted<QQmlTypePrivate>
{
Q_DISABLE_COPY_MOVE(QQmlTypePrivate)
public:
+ struct ProxyMetaObjects
+ {
+ ~ProxyMetaObjects()
+ {
+ for (const QQmlProxyMetaObject::ProxyData &metaObject : data)
+ free(metaObject.metaObject);
+ }
+
+ QList<QQmlProxyMetaObject::ProxyData> data;
+ bool containsRevisionedAttributes = false;
+ };
+
+ struct Enums
+ {
+ ~Enums() { qDeleteAll(scopedEnums); }
+
+ QStringHash<int> enums;
+ QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums
+ QList<QStringHash<int> *> scopedEnums;
+ };
+
QQmlTypePrivate(QQmlType::RegistrationType type);
- void init() const;
- void initEnums(QQmlEnginePrivate *engine) const;
- void insertEnums(const QMetaObject *metaObject) const;
- void insertEnumsFromPropertyCache(const QQmlPropertyCache::ConstPtr &cache) const;
- void setContainingType(QQmlType *containingType);
+ const ProxyMetaObjects *init() const;
QUrl sourceUrl() const
{
switch (regType) {
case QQmlType::CompositeType:
- return extraData.fd->url;
+ return extraData.compositeTypeData;
case QQmlType::CompositeSingletonType:
- return extraData.sd->singletonInstanceInfo->url;
+ return extraData.singletonTypeData->singletonInstanceInfo->url;
case QQmlType::InlineComponentType:
- return extraData.id->url;
+ return extraData.inlineComponentTypeData;
default:
return QUrl();
}
@@ -56,7 +76,7 @@ public:
{
for (const QQmlTypePrivate *d = this; d; d = d->resolveCompositeBaseType(engine).d.data()) {
if (d->regType == QQmlType::CppType)
- return d->extraData.cd->attachedPropertiesType ? d : nullptr;
+ return d->extraData.cppTypeData->attachedPropertiesType ? d : nullptr;
if (d->regType != QQmlType::CompositeType)
return nullptr;
@@ -69,11 +89,14 @@ public:
return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType;
}
+ bool isValueType() const
+ {
+ return regType == QQmlType::CppType && !(typeId.flags() & QMetaType::PointerToQObject);
+ }
+
QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const;
QQmlPropertyCache::ConstPtr compositePropertyCache(QQmlEnginePrivate *engine) const;
- QQmlType::RegistrationType regType;
-
struct QQmlCppTypeData
{
int allocationSize;
@@ -98,66 +121,146 @@ public:
struct QQmlSingletonTypeData
{
- QQmlType::SingletonInstanceInfo *singletonInstanceInfo;
+ QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo;
QObject *(*extFunc)(QObject *);
const QMetaObject *extMetaObject;
};
- struct QQmlCompositeTypeData
- {
- QUrl url;
- };
-
- struct QQmlInlineTypeData
- {
- QUrl url = QUrl();
- // The containing type stores a pointer to the inline component type
- // Using QQmlType here would create a reference cycle
- // As the inline component type cannot outlive the containing type
- // this should still be fine
- QQmlTypePrivate const * containingType = nullptr;
- QString inlineComponentName = QString();
- int objectId = -1;
- };
-
- using QQmlSequenceTypeData = QMetaSequence;
+ int index = -1;
union extraData {
- QQmlCppTypeData* cd;
- QQmlSingletonTypeData* sd;
- QQmlCompositeTypeData* fd;
- QQmlInlineTypeData* id;
- QQmlSequenceTypeData* ld;
+ extraData() {} // QQmlTypePrivate() does the actual construction.
+ ~extraData() {} // ~QQmlTypePrivate() does the actual destruction.
+
+ QQmlCppTypeData *cppTypeData;
+ QQmlSingletonTypeData *singletonTypeData;
+ QUrl compositeTypeData;
+ QUrl inlineComponentTypeData;
+ QMetaSequence sequentialContainerTypeData;
+ const char *interfaceTypeData;
} extraData;
+ static_assert(sizeof(extraData) == sizeof(void *));
- const char *iid;
QHashedString module;
QString name;
QString elementName;
QMetaType typeId;
QMetaType listId;
+ QQmlType::RegistrationType regType;
QTypeRevision version;
- QTypeRevision revision;
- mutable bool containsRevisionedAttributes;
- mutable QQmlType superType;
- const QMetaObject *baseMetaObject;
-
- int index;
- mutable QAtomicInteger<bool> isSetup;
- mutable QAtomicInteger<bool> isEnumFromCacheSetup;
- mutable QAtomicInteger<bool> isEnumFromBaseSetup;
- mutable bool haveSuperType;
- mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects;
- mutable QStringHash<int> enums;
- mutable QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums
- mutable QList<QStringHash<int>*> scopedEnums;
+ QTypeRevision revision = QTypeRevision::zero();
+ const QMetaObject *baseMetaObject = nullptr;
void setName(const QString &uri, const QString &element);
- mutable QHash<QString, int> namesToInlineComponentObjectIndex;
- mutable QHash<int, QQmlType> objectIdToICType;
+
+ template<typename String>
+ static int enumValue(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine,
+ const String &name, bool *ok)
+ {
+ return doGetEnumValue(d, engine, [&](const QQmlTypePrivate::Enums *enums) {
+ return enums->enums.value(name);
+ }, ok);
+ }
+
+ template<typename String>
+ static int scopedEnumIndex(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine,
+ const String &name, bool *ok)
+ {
+ return doGetEnumValue(d, engine, [&](const QQmlTypePrivate::Enums *enums) {
+ return enums->scopedEnumIndex.value(name);
+ }, ok);
+ }
+
+ template<typename String>
+ static int scopedEnumValue(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine, int index,
+ const String &name, bool *ok)
+ {
+ return doGetEnumValue(d, engine, [&](const QQmlTypePrivate::Enums *enums) {
+ Q_ASSERT(index > -1 && index < enums->scopedEnums.size());
+ return enums->scopedEnums.at(index)->value(name);
+ }, ok);
+ }
+
+ template<typename String1, typename String2>
+ static int scopedEnumValue(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine,
+ const String1 &scopedEnumName, const String2 &name, bool *ok)
+ {
+ return doGetEnumValue(d, engine, [&](const QQmlTypePrivate::Enums *enums) -> const int * {
+ const int *rv = enums->scopedEnumIndex.value(scopedEnumName);
+ if (!rv)
+ return nullptr;
+
+ const int index = *rv;
+ Q_ASSERT(index > -1 && index < enums->scopedEnums.size());
+ return enums->scopedEnums.at(index)->value(name);
+ }, ok);
+ }
+
+ const QMetaObject *metaObject() const
+ {
+ if (isValueType())
+ return metaObjectForValueType();
+
+ const QQmlTypePrivate::ProxyMetaObjects *proxies = init();
+ return proxies->data.isEmpty()
+ ? baseMetaObject
+ : proxies->data.constFirst().metaObject;
+ }
+
+ const QMetaObject *metaObjectForValueType() const
+ {
+ Q_ASSERT(isValueType());
+
+ // Prefer the extension meta object, if any.
+ // Extensions allow registration of non-gadget value types.
+ if (const QMetaObject *extensionMetaObject = extraData.cppTypeData->extMetaObject) {
+ // This may be a namespace even if the original metaType isn't.
+ // You can do such things with QML_FOREIGN declarations.
+ if (extensionMetaObject->metaType().flags() & QMetaType::IsGadget)
+ return extensionMetaObject;
+ }
+
+ if (baseMetaObject) {
+ // This may be a namespace even if the original metaType isn't.
+ // You can do such things with QML_FOREIGN declarations.
+ if (baseMetaObject->metaType().flags() & QMetaType::IsGadget)
+ return baseMetaObject;
+ }
+
+ return nullptr;
+ }
+
+ static QQmlType compositeQmlType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit,
+ QQmlTypeLoader *typeLoader, const QString &type)
+ {
+ Q_ASSERT(typeLoader);
+
+ const QQmlType qmltype
+ = unit->typeNameCache->query<QQmlImport::AllowRecursion>(type, typeLoader).type;
+ if (!qmltype.isValid())
+ return qmltype;
+
+ if (qmltype.isInlineComponentType()
+ && !QQmlMetaType::obtainCompilationUnit(qmltype.typeId())) {
+ // If it seems to be an IC type, make sure there is an actual
+ // compilation unit for it. We create inline component types speculatively.
+ return QQmlType();
+ }
+
+ return qmltype;
+ }
private:
- ~QQmlTypePrivate() override;
+ mutable QAtomicPointer<const ProxyMetaObjects> proxyMetaObjects;
+ mutable QAtomicPointer<const Enums> enums;
+
+ ~QQmlTypePrivate();
+ friend class QQmlRefCounted<QQmlTypePrivate>;
struct EnumInfo {
QStringList path;
@@ -168,6 +271,29 @@ private:
bool scoped;
};
+ template<typename Op>
+ static int doGetEnumValue(
+ const QQmlRefPointer<const QQmlTypePrivate> &d, QQmlEnginePrivate *engine,
+ Op &&op, bool *ok)
+ {
+ Q_ASSERT(ok);
+ if (d) {
+ if (const QQmlTypePrivate::Enums *enums = d->initEnums(engine)) {
+ if (const int *rv = op(enums)) {
+ *ok = true;
+ return *rv;
+ }
+ }
+ }
+
+ *ok = false;
+ return -1;
+ }
+
+ const Enums *initEnums(QQmlEnginePrivate *engine) const;
+ void insertEnums(Enums *enums, const QMetaObject *metaObject) const;
+ void insertEnumsFromPropertyCache(Enums *enums, const QQmlPropertyCache::ConstPtr &cache) const;
+
void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const;
void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const;
};
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index 203a004765..cdabd6d5bc 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -8,6 +8,8 @@
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlpropertyresolver_p.h>
+#include <private/qqmlcomponentandaliasresolver_p.h>
+#include <private/qqmlsignalnames_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -17,21 +19,24 @@
QT_BEGIN_NAMESPACE
+DEFINE_BOOL_CONFIG_OPTION(
+ disableInternalDeferredProperties, QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES);
+
Q_LOGGING_CATEGORY(lcQmlTypeCompiler, "qt.qml.typecompiler");
-QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
- QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+QQmlTypeCompiler::QQmlTypeCompiler(
+ QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
: resolvedTypes(resolvedTypeCache)
, engine(engine)
, dependencyHasher(dependencyHasher)
, document(parsedQML)
- , typeNameCache(typeNameCache)
, typeData(typeData)
{
}
-QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
{
// Build property caches and VME meta object data
@@ -63,9 +68,11 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
} else {
// Resolve component boundaries and aliases
- QQmlComponentAndAliasResolver resolver(this);
- if (!resolver.resolve(result.processedRoot))
+ QQmlComponentAndAliasResolver resolver(this, enginePrivate(), &m_propertyCaches);
+ if (QQmlError error = resolver.resolve(result.processedRoot); error.isValid()) {
+ recordError(error);
return nullptr;
+ }
pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_propertyCaches);
pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
}
@@ -105,7 +112,7 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
return nullptr;
}
- if (!document->javaScriptCompilationUnit.unitData()) {
+ if (!document->javaScriptCompilationUnit || !document->javaScriptCompilationUnit->unitData()) {
// Compile JS binding expressions and signal handlers if necessary
{
// We can compile script strings ahead of time, but they must be compiled
@@ -135,14 +142,7 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
if (!errors.isEmpty())
return nullptr;
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit
- = QV4::ExecutableCompilationUnit::create(std::move(
- document->javaScriptCompilationUnit));
- compilationUnit->typeNameCache = typeNameCache;
- compilationUnit->resolvedTypes = *resolvedTypes;
- compilationUnit->propertyCaches = std::move(m_propertyCaches);
- Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount()));
- return compilationUnit;
+ return std::move(document->javaScriptCompilationUnit);
}
void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
@@ -189,7 +189,7 @@ int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
{
- return document->javaScriptCompilationUnit.unitData();
+ return document->javaScriptCompilationUnit->unitData();
}
const QQmlImports *QQmlTypeCompiler::imports() const
@@ -202,10 +202,9 @@ QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const
return &document->objects;
}
-void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches)
+QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches()
{
- m_propertyCaches = std::move(caches);
- Q_ASSERT(m_propertyCaches.count() > 0);
+ return &m_propertyCaches;
}
const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
@@ -213,11 +212,6 @@ const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
return &m_propertyCaches;
}
-QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches()
-{
- return std::move(m_propertyCaches);
-}
-
QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool()
{
return document->jsParserEngine.pool();
@@ -259,9 +253,9 @@ void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier
document->imports.append(import);
}
-CompositeMetaTypeIds QQmlTypeCompiler::typeIdsForComponent(int objectId) const
+QQmlType QQmlTypeCompiler::qmlTypeForComponent(const QString &inlineComponentName) const
{
- return typeData->typeIds(objectId);
+ return typeData->qmlType(inlineComponentName);
}
QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
@@ -313,8 +307,11 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
auto *typeRef = resolvedType(binding->propertyNameIndex);
QQmlType type = typeRef ? typeRef->type() : QQmlType();
- if (!type.isValid())
- imports->resolveType(bindingPropertyName, &type, nullptr, nullptr, nullptr);
+ if (!type.isValid()) {
+ imports->resolveType(
+ QQmlTypeLoader::get(enginePrivate), bindingPropertyName, &type, nullptr,
+ nullptr);
+ }
const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate);
if (!attachedType)
@@ -325,18 +322,21 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(
continue;
}
- if (!QmlIR::IRBuilder::isSignalPropertyName(bindingPropertyName))
+ QString qPropertyName;
+ QString signalName;
+ if (auto propertyName =
+ QQmlSignalNames::changedHandlerNameToPropertyName(bindingPropertyName)) {
+ qPropertyName = *propertyName;
+ signalName = *QQmlSignalNames::changedHandlerNameToSignalName(bindingPropertyName);
+ } else {
+ signalName = QQmlSignalNames::handlerNameToSignalName(bindingPropertyName)
+ .value_or(QString());
+ }
+ if (signalName.isEmpty())
continue;
QQmlPropertyResolver resolver(propertyCache);
- const QString signalName = QmlIR::IRBuilder::signalNameFromSignalPropertyName(
- bindingPropertyName);
-
- QString qPropertyName;
- if (signalName.endsWith(QLatin1String("Changed")))
- qPropertyName = signalName.mid(0, signalName.size() - static_cast<int>(strlen("Changed")));
-
bool notInRevision = false;
const QQmlPropertyData * const signal = resolver.signal(signalName, &notInRevision);
const QQmlPropertyData * const signalPropertyData = resolver.property(signalName, /*notInRevision ptr*/nullptr);
@@ -545,7 +545,8 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(
return true;
}
QQmlType type;
- imports->resolveType(typeName, &type, nullptr, nullptr, nullptr);
+ imports->resolveType(
+ QQmlTypeLoader::get(compiler->enginePrivate()), typeName, &type, nullptr, nullptr);
if (!type.isValid() && !isQtObject)
return true;
@@ -607,7 +608,8 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, QStringView enumNam
if (scope != QLatin1String("Qt")) {
QQmlType type;
- imports->resolveType(scope, &type, nullptr, nullptr, nullptr);
+ imports->resolveType(
+ QQmlTypeLoader::get(compiler->enginePrivate()), scope, &type, nullptr, nullptr);
if (!type.isValid())
return -1;
if (!enumName.isEmpty())
@@ -730,357 +732,99 @@ void QQmlScriptStringScanner::scan()
}
}
-QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler)
- : QQmlCompilePass(typeCompiler)
- , enginePrivate(typeCompiler->enginePrivate())
- , pool(typeCompiler->memoryPool())
- , qmlObjects(typeCompiler->qmlObjects())
- , propertyCaches(std::move(typeCompiler->takePropertyCaches()))
+template<>
+void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::allocateNamedObjects(
+ QmlIR::Object *object) const
{
+ object->namedObjectsInComponent.allocate(m_compiler->memoryPool(), m_idToObjectIndex);
}
-static bool isUsableComponent(const QMetaObject *metaObject)
+template<>
+bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::markAsComponent(int index) const
{
- // The metaObject is a component we're interested in if it either is a QQmlComponent itself
- // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
- // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
-
- if (metaObject == &QQmlComponent::staticMetaObject)
- return true;
-
- for (; metaObject; metaObject = metaObject->superClass()) {
- if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
- return true;
- }
-
- return false;
+ m_compiler->qmlObjects()->at(index)->flags |= QV4::CompiledData::Object::IsComponent;
+ return true;
}
-void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(
- const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache)
+template<>
+void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::setObjectId(int index) const
{
- QQmlPropertyResolver propertyResolver(propertyCache);
-
- const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
-
- for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type() != QV4::CompiledData::Binding::Type_Object)
- continue;
- if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject))
- continue;
-
- const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
- auto *tr = resolvedType(targetObject->inheritedTypeNameIndex);
- Q_ASSERT(tr);
-
- const QMetaObject *firstMetaObject = nullptr;
- const auto type = tr->type();
- if (type.isValid())
- firstMetaObject = type.metaObject();
- else if (const auto compilationUnit = tr->compilationUnit())
- firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
- if (isUsableComponent(firstMetaObject))
- continue;
- // if here, not a QQmlComponent, so needs wrapping
-
- const QQmlPropertyData *pd = nullptr;
- if (binding->propertyNameIndex != quint32(0)) {
- bool notInRevision = false;
- pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
- } else {
- pd = defaultProperty;
- }
- if (!pd || !pd->isQObject())
- continue;
-
- // If the version is given, use it and look up by QQmlType.
- // Otherwise, make sure we look up by metaobject.
- // TODO: Is this correct?
- QQmlPropertyCache::ConstPtr pc = pd->typeVersion().hasMinorVersion()
- ? QQmlMetaType::rawPropertyCacheForType(pd->propType(), pd->typeVersion())
- : QQmlMetaType::rawPropertyCacheForType(pd->propType());
- const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
- while (mo) {
- if (mo == &QQmlComponent::staticMetaObject)
- break;
- mo = mo->superClass();
- }
-
- if (!mo)
- continue;
-
- // emulate "import QML 1.0" and then wrap the component in "QML.Component {}"
- QQmlType componentType = QQmlMetaType::qmlType(
- &QQmlComponent::staticMetaObject, QStringLiteral("QML"),
- QTypeRevision::fromVersion(1, 0));
- Q_ASSERT(componentType.isValid());
- const QString qualifier = QStringLiteral("QML");
-
- compiler->addImport(componentType.module(), qualifier, componentType.version());
-
- QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
- syntheticComponent->init(
- pool,
- compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()),
- compiler->registerString(QString()), binding->valueLocation);
- syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
-
- if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) {
- auto typeRef = new QV4::ResolvedTypeReference;
- typeRef->setType(componentType);
- typeRef->setVersion(componentType.version());
- insertResolvedType(syntheticComponent->inheritedTypeNameIndex, typeRef);
- }
-
- qmlObjects->append(syntheticComponent);
- const int componentIndex = qmlObjects->size() - 1;
- // Keep property caches symmetric
- QQmlPropertyCache::ConstPtr componentCache
- = QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject);
- propertyCaches.append(componentCache);
-
- QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
- *syntheticBinding = *binding;
-
- // The synthetic binding inside Component has no name. It's just "Component { Foo {} }".
- syntheticBinding->propertyNameIndex = 0;
-
- syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
- QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
- Q_ASSERT(error.isEmpty());
- Q_UNUSED(error);
-
- binding->value.objectIndex = componentIndex;
-
- componentRoots.append(componentIndex);
- }
+ m_compiler->qmlObjects()->at(index)->id = m_idToObjectIndex.size();
}
-// Resolve ignores everything relating to inline components, except for implicit components.
-bool QQmlComponentAndAliasResolver::resolve(int root)
+template<>
+bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::wrapImplicitComponent(QmlIR::Binding *binding)
{
- // Detect real Component {} objects as well as implicitly defined components, such as
- // someItemDelegate: Item {}
- // In the implicit case Item is surrounded by a synthetic Component {} because the property
- // on the left hand side is of QQmlComponent type.
- const int objCountWithoutSynthesizedComponents = qmlObjects->size();
- const int startObjectIndex = root == 0 ? root : root+1; // root+1, as ic root is handled at the end
- for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
- QmlIR::Object *obj = qmlObjects->at(i);
- const bool isInlineComponentRoot
- = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
- const bool isPartOfInlineComponent
- = obj->flags & QV4::CompiledData::Object::IsPartOfInlineComponent;
- QQmlPropertyCache::ConstPtr cache = propertyCaches.at(i);
-
- bool isExplicitComponent = false;
- if (obj->inheritedTypeNameIndex) {
- auto *tref = resolvedType(obj->inheritedTypeNameIndex);
- Q_ASSERT(tref);
- if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
- isExplicitComponent = true;
- }
-
- if (isInlineComponentRoot && isExplicitComponent) {
- qCWarning(lcQmlTypeCompiler).nospace().noquote()
- << compiler->url().toString() << ":" << obj->location.line() << ":"
- << obj->location.column()
- << ": Using a Component as the root of an inline component is deprecated: "
- "inline components are "
- "automatically wrapped into Components when needed.";
- }
-
- if (root == 0) {
- // normal component root, skip over anything inline component related
- if (isInlineComponentRoot || isPartOfInlineComponent)
- continue;
- } else if (!isPartOfInlineComponent || isInlineComponentRoot) {
- // We've left the current inline component (potentially entered a new one),
- // but we still need to resolve implicit components which are part of inline components.
- if (cache && !isExplicitComponent)
- findAndRegisterImplicitComponents(obj, cache);
- break;
- }
-
- if (obj->inheritedTypeNameIndex == 0 && !cache)
- continue;
-
- if (!isExplicitComponent) {
- if (cache)
- findAndRegisterImplicitComponents(obj, cache);
- continue;
- }
-
- obj->flags |= QV4::CompiledData::Object::IsComponent;
-
- // check if this object is the root
- if (i == 0) {
- if (isExplicitComponent)
- qCWarning(lcQmlTypeCompiler).nospace().noquote()
- << compiler->url().toString() << ":" << obj->location.line() << ":"
- << obj->location.column()
- << ": Using a Component as the root of a qmldocument is deprecated: types "
- "defined in qml documents are "
- "automatically wrapped into Components when needed.";
- }
-
- if (obj->functionCount() > 0)
- COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
- if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
- COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
- if (obj->signalCount() > 0)
- COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
-
- if (obj->bindingCount() == 0)
- COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
-
- const QmlIR::Binding *rootBinding = obj->firstBinding();
-
- for (const QmlIR::Binding *b = rootBinding; b; b = b->next) {
- if (b->propertyNameIndex != 0)
- COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id"));
- }
-
- if (rootBinding->next || rootBinding->type() != QV4::CompiledData::Binding::Type_Object)
- COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
-
- // For the root object, we are going to collect ids/aliases and resolve them for as a separate
- // last pass.
- if (i != 0)
- componentRoots.append(i);
-
+ QQmlJS::MemoryPool *pool = m_compiler->memoryPool();
+ QVector<QmlIR::Object *> *qmlObjects = m_compiler->qmlObjects();
+
+ // emulate "import QML 1.0" and then wrap the component in "QML.Component {}"
+ QQmlType componentType = QQmlMetaType::qmlType(
+ &QQmlComponent::staticMetaObject, QStringLiteral("QML"),
+ QTypeRevision::fromVersion(1, 0));
+ Q_ASSERT(componentType.isValid());
+ const QString qualifier = QStringLiteral("QML");
+
+ m_compiler->addImport(componentType.module(), qualifier, componentType.version());
+
+ QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
+ syntheticComponent->init(
+ pool,
+ m_compiler->registerString(
+ qualifier + QLatin1Char('.') + componentType.elementName()),
+ m_compiler->registerString(QString()), binding->valueLocation);
+ syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
+
+ if (!m_compiler->resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) {
+ auto typeRef = new QV4::ResolvedTypeReference;
+ typeRef->setType(componentType);
+ typeRef->setVersion(componentType.version());
+ m_compiler->resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef);
}
- for (int i = 0; i < componentRoots.size(); ++i) {
- QmlIR::Object *component = qmlObjects->at(componentRoots.at(i));
- const QmlIR::Binding *rootBinding = component->firstBinding();
+ qmlObjects->append(syntheticComponent);
+ const int componentIndex = qmlObjects->size() - 1;
+ // Keep property caches symmetric
+ QQmlPropertyCache::ConstPtr componentCache
+ = QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject);
+ m_propertyCaches->append(componentCache);
- _idToObjectIndex.clear();
+ QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
+ *syntheticBinding = *binding;
- _objectsWithAliases.clear();
+ // The synthetic binding inside Component has no name. It's just "Component { Foo {} }".
+ syntheticBinding->propertyNameIndex = 0;
- if (!collectIdsAndAliases(rootBinding->value.objectIndex))
- return false;
+ syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
+ QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
+ Q_ASSERT(error.isEmpty());
+ Q_UNUSED(error);
- component->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
-
- if (!resolveAliases(componentRoots.at(i)))
- return false;
- }
-
- // Collect ids and aliases for root
- _idToObjectIndex.clear();
- _objectsWithAliases.clear();
-
- collectIdsAndAliases(root);
-
- QmlIR::Object *rootComponent = qmlObjects->at(root);
- rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
-
- if (!resolveAliases(root))
- return false;
-
- // Implicit component insertion may have added objects and thus we also need
- // to extend the symmetric propertyCaches.
- compiler->setPropertyCaches(std::move(propertyCaches));
- compiler->setComponentRoots(componentRoots);
+ binding->value.objectIndex = componentIndex;
+ m_componentRoots.append(componentIndex);
return true;
}
-bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
+template<>
+void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveGeneralizedGroupProperty(
+ const CompiledObject &component, CompiledBinding *binding)
{
- QmlIR::Object *obj = qmlObjects->at(objectIndex);
-
- if (obj->idNameIndex != 0) {
- if (_idToObjectIndex.contains(obj->idNameIndex)) {
- recordError(obj->locationOfIdProperty, tr("id is not unique"));
- return false;
- }
- obj->id = _idToObjectIndex.size();
- _idToObjectIndex.insert(obj->idNameIndex, objectIndex);
- }
-
- if (obj->aliasCount() > 0)
- _objectsWithAliases.append(objectIndex);
-
- // Stop at Component boundary
- if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
- return true;
-
- for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- switch (binding->type()) {
- case QV4::CompiledData::Binding::Type_Object:
- case QV4::CompiledData::Binding::Type_AttachedProperty:
- case QV4::CompiledData::Binding::Type_GroupProperty:
- if (!collectIdsAndAliases(binding->value.objectIndex))
- return false;
- break;
- default:
- break;
- }
- }
-
- return true;
+ Q_UNUSED(component);
+ // We cannot make it fail here. It might be a custom-parsed property
+ const int targetObjectIndex = m_idToObjectIndex.value(binding->propertyNameIndex, -1);
+ if (targetObjectIndex != -1)
+ m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(targetObjectIndex));
}
-bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
+template<>
+typename QQmlComponentAndAliasResolver<QQmlTypeCompiler>::AliasResolutionResult
+QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
+ const CompiledObject &component, int objectIndex, QQmlError *error)
{
- if (_objectsWithAliases.isEmpty())
- return true;
-
- QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler);
-
- bool atLeastOneAliasResolved;
- do {
- atLeastOneAliasResolved = false;
- QVector<int> pendingObjects;
-
- for (int objectIndex: std::as_const(_objectsWithAliases)) {
-
- QQmlError error;
- const auto result = resolveAliasesInObject(objectIndex, &error);
-
- if (error.isValid()) {
- recordError(error);
- return false;
- }
-
- if (result == AllAliasesResolved) {
- QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate);
- if (error.isValid()) {
- recordError(error);
- return false;
- }
- atLeastOneAliasResolved = true;
- } else if (result == SomeAliasesResolved) {
- atLeastOneAliasResolved = true;
- pendingObjects.append(objectIndex);
- } else {
- pendingObjects.append(objectIndex);
- }
- }
- qSwap(_objectsWithAliases, pendingObjects);
- } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
-
- if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
- const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first());
- for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
- if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
- recordError(alias->location, tr("Circular alias reference detected"));
- return false;
- }
- }
- }
-
- return true;
-}
+ Q_UNUSED(component);
-QQmlComponentAndAliasResolver::AliasResolutionResult
-QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
- QQmlError *error)
-{
- const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
+ const QmlIR::Object * const obj = m_compiler->objectAt(objectIndex);
if (!obj->aliasCount())
return AllAliasesResolved;
@@ -1094,7 +838,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
seenUnresolvedAlias = true;
const int idIndex = alias->idIndex();
- const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
+ const int targetObjectIndex = m_idToObjectIndex.value(idIndex, -1);
if (targetObjectIndex == -1) {
*error = qQmlCompileError(
alias->referenceLocation,
@@ -1102,7 +846,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
break;
}
- const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
+ const QmlIR::Object *targetObject = m_compiler->objectAt(targetObjectIndex);
Q_ASSERT(targetObject->id >= 0);
alias->setTargetObjectId(targetObject->id);
alias->setIsAliasToLocalAlias(false);
@@ -1124,7 +868,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
if (property.isEmpty()) {
alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject);
} else {
- QQmlPropertyCache::ConstPtr targetCache = propertyCaches.at(targetObjectIndex);
+ QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
if (!targetCache) {
*error = qQmlCompileError(
alias->referenceLocation,
@@ -1180,8 +924,8 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
isDeepAlias = false;
for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) {
auto binding = *it;
- if (compiler->stringAt(binding.propertyNameIndex) == property) {
- resolver = QQmlPropertyResolver(propertyCaches.at(binding.value.objectIndex));
+ if (m_compiler->stringAt(binding.propertyNameIndex) == property) {
+ resolver = QQmlPropertyResolver(m_propertyCaches->at(binding.value.objectIndex));
const QQmlPropertyData *actualProperty = resolver.property(subProperty.toString());
if (actualProperty) {
propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex());
@@ -1316,7 +1060,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(
obj->flags |= Object::HasCustomParserBindings;
continue;
}
- } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
+ } else if (QQmlSignalNames::isHandlerName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
obj->flags |= Object::HasCustomParserBindings;
binding->setFlag(Binding::IsCustomParserBinding);
@@ -1384,10 +1128,13 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(
COMPILE_EXCEPTION(binding, tr("You cannot assign an id to an object assigned "
"to a deferred property."));
}
- isDeferred = true;
- } else if (!deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
- if (!seenSubObjectWithId && binding->type() != Binding::Type_GroupProperty)
+ if (isExternal || !disableInternalDeferredProperties())
isDeferred = true;
+ } else if (!deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
+ if (!seenSubObjectWithId && binding->type() != Binding::Type_GroupProperty) {
+ if (isExternal || !disableInternalDeferredProperties())
+ isDeferred = true;
+ }
}
if (binding->type() >= Binding::Type_Object) {
diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h
index 02edbb0c08..6d9e5a77ca 100644
--- a/src/qml/qml/qqmltypecompiler_p.h
+++ b/src/qml/qml/qqmltypecompiler_p.h
@@ -46,20 +46,24 @@ public:
QQmlTypeCompiler(QQmlEnginePrivate *engine,
QQmlTypeData *typeData,
QmlIR::Document *document,
- const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
// --- interface used by QQmlPropertyCacheCreator
typedef QmlIR::Object CompiledObject;
+ typedef QmlIR::Binding CompiledBinding;
using ListPropertyAssignBehavior = QmlIR::Pragma::ListPropertyAssignBehaviorValue;
+ // Deliberate choice of map over hash here to ensure stable generated output.
+ using IdToObjectMap = QMap<int, int>;
+
const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); }
+ QmlIR::Object *objectAt(int index) { return document->objects.at(index); }
int objectCount() const { return document->objects.size(); }
QString stringAt(int idx) const;
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); }
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); }
- QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr;
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes = nullptr;
ListPropertyAssignBehavior listPropertyAssignBehavior() const
{
for (const QmlIR::Pragma *pragma: document->pragmas) {
@@ -70,7 +74,7 @@ public:
}
// ---
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compile();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile();
QList<QQmlError> compilationErrors() const { return errors; }
void recordError(const QV4::CompiledData::Location &location, const QString &description);
@@ -86,11 +90,8 @@ public:
QQmlEnginePrivate *enginePrivate() const { return engine; }
const QQmlImports *imports() const;
QVector<QmlIR::Object *> *qmlObjects() const;
- void setPropertyCaches(QQmlPropertyCacheVector &&caches);
+ QQmlPropertyCacheVector *propertyCaches();
const QQmlPropertyCacheVector *propertyCaches() const;
- QQmlPropertyCacheVector &&takePropertyCaches();
- void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; }
- const QVector<quint32> &componentRoots() const { return m_componentRoots; }
QQmlJS::MemoryPool *memoryPool();
QStringView newStringRef(const QString &string);
const QV4::Compiler::StringTableGenerator *stringPool() const;
@@ -106,7 +107,16 @@ public:
return resolvedTypes->value(id);
}
- CompositeMetaTypeIds typeIdsForComponent(int objectId = 0) const;
+ QV4::ResolvedTypeReference *resolvedType(QMetaType type) const
+ {
+ for (QV4::ResolvedTypeReference *ref : std::as_const(*resolvedTypes)) {
+ if (ref->type().typeId() == type)
+ return ref;
+ }
+ return nullptr;
+ }
+
+ QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const;
private:
QList<QQmlError> errors;
@@ -117,10 +127,8 @@ private:
QHash<int, QQmlCustomParser*> customParsers;
// index in first hash is component index, vector inside contains object indices of objects with id property
- QVector<quint32> m_componentRoots;
QQmlPropertyCacheVector m_propertyCaches;
- QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
QQmlTypeData *typeData;
};
@@ -132,16 +140,9 @@ struct QQmlCompilePass
protected:
void recordError(const QV4::CompiledData::Location &location, const QString &description) const
{ compiler->recordError(location, description); }
- void recordError(const QQmlError &error)
- { compiler->recordError(error); }
QV4::ResolvedTypeReference *resolvedType(int id) const
{ return compiler->resolvedType(id); }
- bool containsResolvedType(int id) const
- { return compiler->resolvedTypes->contains(id); }
- QV4::ResolvedTypeReferenceMap::iterator insertResolvedType(
- int id, QV4::ResolvedTypeReference *value)
- { return compiler->resolvedTypes->insert(id, value); }
QQmlTypeCompiler *compiler;
};
@@ -235,44 +236,6 @@ private:
const QQmlPropertyCacheVector * const propertyCaches;
};
-class QQmlComponentAndAliasResolver : public QQmlCompilePass
-{
- Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver)
-public:
- QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler);
-
- bool resolve(int root = 0);
-
-protected:
- void findAndRegisterImplicitComponents(
- const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
- bool collectIdsAndAliases(int objectIndex);
- bool resolveAliases(int componentIndex);
- void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags);
-
- enum AliasResolutionResult {
- NoAliasResolved,
- SomeAliasesResolved,
- AllAliasesResolved
- };
-
- AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlError *error);
-
- QQmlEnginePrivate *enginePrivate;
- QQmlJS::MemoryPool *pool;
-
- QVector<QmlIR::Object*> *qmlObjects;
-
- // indices of the objects that are actually Component {}
- QVector<quint32> componentRoots;
-
- // Deliberate choice of map over hash here to ensure stable generated output.
- QMap<int, int> _idToObjectIndex;
- QVector<int> _objectsWithAliases;
-
- QQmlPropertyCacheVector propertyCaches;
-};
-
class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass
{
Q_DECLARE_TR_FUNCTIONS(QQmlDeferredAndCustomParserBindingScanner)
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 460a3440c7..9acd801672 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -1,15 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <private/qqmltypedata_p.h>
+#include <private/qqmlcomponentandaliasresolver_p.h>
#include <private/qqmlengine_p.h>
-#include <private/qqmlpropertycachecreator_p.h>
-#include <private/qqmlpropertyvalidator_p.h>
#include <private/qqmlirbuilder_p.h>
#include <private/qqmlirloader_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qqmlpropertyvalidator_p.h>
#include <private/qqmlscriptblob_p.h>
#include <private/qqmlscriptdata_p.h>
#include <private/qqmltypecompiler_p.h>
+#include <private/qqmltypedata_p.h>
#include <private/qqmltypeloaderqmldircontent_p.h>
#include <QtCore/qloggingcategory.h>
@@ -18,7 +19,7 @@
#include <memory>
Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
-Q_LOGGING_CATEGORY(lcCycle, "qt.qml.typeresolution.cycle")
+Q_LOGGING_CATEGORY(lcCycle, "qt.qml.typeresolution.cycle", QtWarningMsg)
QT_BEGIN_NAMESPACE
@@ -50,34 +51,11 @@ QQmlTypeData::~QQmlTypeData()
m_resolvedTypes.clear();
}
-const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
-{
- return m_scripts;
-}
-
-QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const
+QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const
{
return m_compiledData.data();
}
-QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnitForInlineComponent(unsigned int icObjectId) const
-{
- Q_ASSERT(m_document || m_compiledData);
- if (m_compiledData)
- return m_compiledData.data();
- for (auto it = m_document->objects.begin(); it != m_document->objects.end(); ++it) {
- auto object = *it;
- auto icIt = std::find_if(object->inlineComponentsBegin(), object->inlineComponentsEnd(), [&](const QV4::CompiledData::InlineComponent &ic) {
- return ic.objectIndex == icObjectId;
- });
- if (icIt != object->inlineComponentsEnd()) {
- Q_ASSERT(m_inlineComponentToCompiledData.contains(icIt->nameIndex));
- return m_inlineComponentToCompiledData[icIt->nameIndex].data();
- }
- }
- Q_UNREACHABLE_RETURN(nullptr);
-}
-
void QQmlTypeData::registerCallback(TypeDataCallback *callback)
{
Q_ASSERT(!m_callbacks.contains(callback));
@@ -91,11 +69,11 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
Q_ASSERT(!m_callbacks.contains(callback));
}
-CompositeMetaTypeIds QQmlTypeData::typeIds(int objectId) const
+QQmlType QQmlTypeData::qmlType(const QString &inlineComponentName) const
{
- if (objectId != 0)
- return m_inlineComponentData[objectId].typeIds;
- return m_typeIds;
+ if (inlineComponentName.isEmpty())
+ return m_qmlType;
+ return m_inlineComponentData[inlineComponentName].qmlType;
}
bool QQmlTypeData::tryLoadFromDiskCache()
@@ -103,11 +81,7 @@ bool QQmlTypeData::tryLoadFromDiskCache()
if (!readCacheFile())
return false;
- QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle();
- if (!v4)
- return false;
-
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create();
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
{
QString error;
if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
@@ -117,11 +91,11 @@ bool QQmlTypeData::tryLoadFromDiskCache()
}
if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
- restoreIR(std::move(*unit));
+ restoreIR(unit);
return true;
}
- m_compiledData = unit;
+ m_compiledData = std::move(unit);
QVector<QV4::CompiledData::InlineComponent> ics;
for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) {
@@ -183,42 +157,132 @@ bool QQmlTypeData::tryLoadFromDiskCache()
for (auto&& ic: ics) {
QString const nameString = m_compiledData->stringAt(ic.nameIndex);
auto importUrl = finalUrl();
- importUrl.setFragment(QString::number(ic.objectIndex));
+ importUrl.setFragment(nameString);
auto import = new QQmlImportInstance();
- m_importCache->addInlineComponentImport(import, nameString, importUrl, QQmlType());
+ m_importCache->addInlineComponentImport(import, nameString, importUrl);
}
return true;
}
-void QQmlTypeData::createTypeAndPropertyCaches(
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::allocateNamedObjects(
+ const QV4::CompiledData::Object *object) const
+{
+ Q_UNUSED(object);
+}
+
+template<>
+bool QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::markAsComponent(int index) const
+{
+ return m_compiler->objectAt(index)->hasFlag(QV4::CompiledData::Object::IsComponent);
+}
+
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::setObjectId(int index) const
+{
+ Q_UNUSED(index)
+ // we cannot sanity-check the index here because bindings are sorted in a different order
+ // in the CU vs the IR.
+}
+
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveGeneralizedGroupProperty(
+ const CompiledObject &component, CompiledBinding *binding)
+{
+ // We cannot make it fail here. It might be a custom-parsed property
+ for (int i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ if (m_compiler->objectAt(candidateIndex)->idNameIndex == binding->propertyNameIndex) {
+ m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(candidateIndex));
+ return;
+ }
+ }
+}
+
+template<>
+typename QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::AliasResolutionResult
+QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveAliasesInObject(
+ const CompiledObject &component, int objectIndex, QQmlError *error)
+{
+ const CompiledObject *obj = m_compiler->objectAt(objectIndex);
+ for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
+ if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
+ *error = qQmlCompileError( alias->referenceLocation, tr("Unresolved alias found"));
+ return NoAliasResolved;
+ }
+
+ if (alias->isAliasToLocalAlias() || alias->encodedMetaPropertyIndex == -1)
+ continue;
+
+ const int targetObjectIndex
+ = objectForId(m_compiler, component, alias->targetObjectId());
+ const int coreIndex
+ = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+
+ QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+
+ if (!targetCache->property(coreIndex))
+ return SomeAliasesResolved;
+ }
+
+ return AllAliasesResolved;
+}
+
+template<>
+bool QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::wrapImplicitComponent(
+ const QV4::CompiledData::Binding *binding)
+{
+ // This should have been done when creating the CU.
+ Q_UNUSED(binding);
+ return false;
+}
+
+QQmlError QQmlTypeData::createTypeAndPropertyCaches(
const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- const QV4::ResolvedTypeReferenceMap &resolvedTypeCache)
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
{
Q_ASSERT(m_compiledData);
m_compiledData->typeNameCache = typeNameCache;
m_compiledData->resolvedTypes = resolvedTypeCache;
+ m_compiledData->inlineComponentData = m_inlineComponentData;
QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
{
- QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator(
+ QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(
&m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine,
m_compiledData.data(), m_importCache.data(), typeClassName());
- QQmlError error = propertyCacheCreator.buildMetaObjects();
- if (error.isValid()) {
- setError(error);
- return;
- }
- QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator(
- &m_compiledData->propertyCaches, m_compiledData.data());
- aliasCreator.appendAliasPropertiesToMetaObjects(engine);
+ QQmlError error = propertyCacheCreator.verifyNoICCycle();
+ if (error.isValid())
+ return error;
+
+ QQmlPropertyCacheCreatorBase::IncrementalResult result;
+ do {
+ result = propertyCacheCreator.buildMetaObjectsIncrementally();
+ if (result.error.isValid()) {
+ return result.error;
+ } else {
+ QQmlComponentAndAliasResolver resolver(
+ m_compiledData.data(), engine, &m_compiledData->propertyCaches);
+ if (const QQmlError error = resolver.resolve(result.processedRoot);
+ error.isValid()) {
+ return error;
+ }
+ pendingGroupPropertyBindings.resolveMissingPropertyCaches(
+ &m_compiledData->propertyCaches);
+ pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
+ }
+
+ } while (result.canResume);
}
pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_compiledData->propertyCaches);
+ return QQmlError();
}
static bool addTypeReferenceChecksumsToHash(
@@ -229,8 +293,8 @@ static bool addTypeReferenceChecksumsToHash(
if (typeRef.typeData) {
const auto unit = typeRef.typeData->compilationUnit()->unitData();
hash->addData({unit->md5Checksum, sizeof(unit->md5Checksum)});
- } else if (typeRef.type.isValid()) {
- const auto propertyCache = QQmlMetaType::propertyCache(typeRef.type.metaObject());
+ } else if (const QMetaObject *mo = typeRef.type.metaObject()) {
+ const auto propertyCache = QQmlMetaType::propertyCache(mo);
bool ok = false;
hash->addData(propertyCache->checksum(checksums, &ok));
if (!ok)
@@ -242,15 +306,25 @@ static bool addTypeReferenceChecksumsToHash(
// local helper function for inline components
namespace {
+using InlineComponentData = QV4::CompiledData::InlineComponentData;
+
template<typename ObjectContainer>
-void setupICs(const ObjectContainer &container, QHash<int, InlineComponentData> *icData, const QUrl &finalUrl) {
+void setupICs(
+ const ObjectContainer &container, QHash<QString, InlineComponentData> *icData,
+ const QUrl &baseUrl,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) {
Q_ASSERT(icData->empty());
for (int i = 0; i != container->objectCount(); ++i) {
auto root = container->objectAt(i);
for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) {
- const QByteArray &className = QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(finalUrl, it->objectIndex);
- InlineComponentData icDatum(CompositeMetaTypeIds::fromCompositeName(className), int(it->objectIndex), int(it->nameIndex), 0, 0, 0);
- icData->insert(it->objectIndex, icDatum);
+ // We cannot re-use a previously finalized inline component type here. We need our own.
+ // We can and should re-use speculative type references, though.
+ InlineComponentData icDatum(
+ QQmlMetaType::findInlineComponentType(
+ baseUrl, container->stringAt(it->nameIndex), compilationUnit),
+ int(it->objectIndex), int(it->nameIndex), 0, 0, 0);
+
+ icData->insert(container->stringAt(it->nameIndex), icDatum);
}
}
};
@@ -324,16 +398,18 @@ void QQmlTypeData::done()
++it) {
const TypeReference &type = *it;
Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError() || type.type.isInlineComponentType());
- if (type.type.isInlineComponentType() && !type.type.pendingResolutionName().isEmpty()) {
- auto containingType = type.type.containingType();
- auto objectId = containingType.lookupInlineComponentIdByName(type.type.pendingResolutionName());
- if (objectId < 0) { // can be any negative number if we tentatively resolved it in QQmlImport but it actually was not an inline component
+
+ if (type.type.isInlineComponentType()) {
+ const QUrl url = type.type.sourceUrl();
+ if (!QQmlMetaType::equalBaseUrls(url, finalUrl())
+ && !QQmlMetaType::obtainCompilationUnit(type.type.typeId())) {
const QString &typeName = stringAt(it.key());
int lastDot = typeName.lastIndexOf(u'.');
- createError(type, QQmlTypeLoader::tr("Type %1 has no inline component type called %2").arg(QStringView{typeName}.left(lastDot), type.type.pendingResolutionName()));
+ createError(
+ type,
+ QQmlTypeLoader::tr("Type %1 has no inline component type called %2")
+ .arg(QStringView{typeName}.left(lastDot), type.type.elementName()));
return;
- } else {
- type.type.setInlineComponentObjectId(objectId);
}
}
if (type.typeData && type.typeData->isError()) {
@@ -355,17 +431,23 @@ void QQmlTypeData::done()
}
}
- m_typeClassName = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(finalUrl());
- if (!m_typeClassName.isEmpty())
- m_typeIds = CompositeMetaTypeIds::fromCompositeName(m_typeClassName);
-
- if (m_document) {
- setupICs(m_document, &m_inlineComponentData, finalUrl());
- } else {
- setupICs(m_compiledData, &m_inlineComponentData, finalUrl());
+ if (QQmlPropertyCacheCreatorBase::canCreateClassNameTypeByUrl(finalUrl())) {
+ const bool isSingleton = m_document
+ ? m_document.data()->isSingleton()
+ : (m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton);
+ m_qmlType = QQmlMetaType::findCompositeType(
+ finalUrl(), m_compiledData, isSingleton
+ ? QQmlMetaType::Singleton
+ : QQmlMetaType::NonSingleton);
+ m_typeClassName = QByteArray(m_qmlType.typeId().name()).chopped(1);
}
- QV4::ResolvedTypeReferenceMap resolvedTypeCache;
+ if (m_document)
+ setupICs(m_document, &m_inlineComponentData, finalUrl(), m_compiledData);
+ else
+ setupICs(m_compiledData, &m_inlineComponentData, finalUrl(), m_compiledData);
+
+ QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache;
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
{
QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
@@ -386,27 +468,57 @@ void QQmlTypeData::done()
};
// verify if any dependencies changed if we're using a cache
- if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) {
- qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName();
- if (!loadFromSource())
- return;
- m_compiledData.reset();
+ if (m_document.isNull()) {
+ const QQmlError error = createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
+ if (!error.isValid() && m_compiledData->verifyChecksum(dependencyHasher)) {
+ setCompileUnit(m_compiledData);
+ } else {
+
+ if (error.isValid()) {
+ qCDebug(DBG_DISK_CACHE)
+ << "Failed to create property caches for"
+ << m_compiledData->fileName()
+ << "because" << error.description();
+ } else {
+ qCDebug(DBG_DISK_CACHE)
+ << "Checksum mismatch for cached version of"
+ << m_compiledData->fileName();
+ }
+
+ if (!loadFromSource())
+ return;
+
+ // We want to keep our resolve types ...
+ m_compiledData->resolvedTypes.clear();
+ // ... but we don't want the property caches we've created for the broken CU.
+ for (QV4::ResolvedTypeReference *ref: std::as_const(resolvedTypeCache)) {
+ const auto compilationUnit = ref->compilationUnit();
+ if (compilationUnit.isNull()) {
+ // Inline component references without CU belong to the surrounding CU.
+ // We have to clear them. Inline component references to other documents
+ // have a CU.
+ if (!ref->type().isInlineComponentType())
+ continue;
+ } else if (compilationUnit != m_compiledData) {
+ continue;
+ }
+ ref->setTypePropertyCache(QQmlPropertyCache::ConstPtr());
+ ref->setCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit>());
+ }
+
+ m_compiledData.reset();
+ }
}
if (!m_document.isNull()) {
// Compile component
compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
- if (!isError())
+ if (isError())
+ return;
+ else
setCompileUnit(m_document);
- } else {
- createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
- if (!isError())
- setCompileUnit(m_compiledData);
}
- if (isError())
- return;
-
{
QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
m_compiledData->inlineComponentData = m_inlineComponentData;
@@ -420,7 +532,7 @@ void QQmlTypeData::done()
}
}
- m_compiledData->finalizeCompositeType(enginePrivate, typeIds());
+ m_compiledData->finalizeCompositeType(qmlType());
}
{
@@ -448,33 +560,6 @@ void QQmlTypeData::done()
}
}
- // associate inline components to root component
- {
- auto fileName = finalUrl().fileName();
- QStringView typeName = [&]() {
- // extract base name (QFileInfo::baseName would require constructing a QFileInfo)
- auto dotIndex = fileName.indexOf(u'.');
- if (dotIndex < 0)
- return QStringView();
- return QStringView(fileName).first(dotIndex);
- }();
- // typeName can be empty if a QQmlComponent was constructed with an empty QUrl parameter
- if (!typeName.isEmpty() && typeName.at(0).isUpper() && !m_inlineComponentData.isEmpty()) {
- QHashedStringRef const hashedStringRef { typeName };
- QList<QQmlError> errors;
- auto type = QQmlMetaType::typeForUrl(finalUrlString(), hashedStringRef, false, &errors);
- Q_ASSERT(errors.empty());
- if (type.isValid()) {
- for (auto const &icDatum : std::as_const(m_inlineComponentData)) {
- Q_ASSERT(icDatum.typeIds.isValid());
- QQmlType existingType = type.lookupInlineComponentById(type.lookupInlineComponentIdByName(m_compiledData->stringAt(icDatum.nameIndex)));
- type.associateInlineComponent(m_compiledData->stringAt(icDatum.nameIndex),
- icDatum.objectIndex, icDatum.typeIds, existingType);
- }
- }
- }
- }
-
{
// Collect imported scripts
m_compiledData->dependentScripts.reserve(m_scripts.size());
@@ -490,7 +575,8 @@ void QQmlTypeData::done()
qualifier = qualifier.mid(lastDotIndex+1);
}
- m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
+ m_compiledData->typeNameCache->add(
+ qualifier.toString(), scriptIndex, enclosingNamespace);
QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
m_compiledData->dependentScripts << scriptData;
}
@@ -512,13 +598,12 @@ bool QQmlTypeData::loadImplicitImport()
m_importCache->setBaseUrl(finalUrl(), finalUrlString());
- QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
// For local urls, add an implicit import "." as most overridden lookup.
// This will also trigger the loading of the qmldir and the import of any native
// types from available plugins.
QList<QQmlError> implicitImportErrors;
QString localQmldir;
- m_importCache->addImplicitImport(importDatabase, &localQmldir, &implicitImportErrors);
+ m_importCache->addImplicitImport(typeLoader(), &localQmldir, &implicitImportErrors);
// When loading with QQmlImports::ImportImplicit, the imports are _appended_ to the namespace
// in the order they are loaded. Therefore, the addImplicitImport above gets the highest
@@ -575,7 +660,10 @@ void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *un
loader.load();
m_document->jsModule.fileName = urlString();
m_document->jsModule.finalUrl = finalUrlString();
- m_document->javaScriptCompilationUnit = QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions);
+ m_document->javaScriptCompilationUnit
+ = QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
+ new QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions),
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>::Adopt);
continueLoadFromIR();
}
@@ -610,31 +698,26 @@ bool QQmlTypeData::loadFromSource()
return true;
}
-void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit)
+void QQmlTypeData::restoreIR(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
{
m_document.reset(new QmlIR::Document(isDebugging()));
- QQmlIRLoader loader(unit.unitData(), m_document.data());
+ QQmlIRLoader loader(unit->unitData(), m_document.data());
loader.load();
m_document->jsModule.fileName = urlString();
m_document->jsModule.finalUrl = finalUrlString();
- m_document->javaScriptCompilationUnit = std::move(unit);
+ m_document->javaScriptCompilationUnit = unit;
continueLoadFromIR();
}
void QQmlTypeData::continueLoadFromIR()
{
- QQmlType containingType;
- auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first();
- QTypeRevision version;
- QQmlImportNamespace *ns = nullptr;
- m_importCache->resolveType(containingTypeName, &containingType, &version, &ns);
for (auto const& object: m_document->objects) {
for (auto it = object->inlineComponentsBegin(); it != object->inlineComponentsEnd(); ++it) {
QString const nameString = m_document->stringAt(it->nameIndex);
auto importUrl = finalUrl();
- importUrl.setFragment(QString::number(it->objectIndex));
+ importUrl.setFragment(nameString);
auto import = new QQmlImportInstance(); // Note: The cache takes ownership of the QQmlImportInstance
- m_importCache->addInlineComponentImport(import, nameString, importUrl, containingType);
+ m_importCache->addInlineComponentImport(import, nameString, importUrl);
}
}
@@ -733,18 +816,22 @@ QString QQmlTypeData::stringAt(int index) const
}
void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
{
Q_ASSERT(m_compiledData.isNull());
- const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit.unitData()
- && (m_document->javaScriptCompilationUnit.unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation);
+ const bool typeRecompilation = m_document
+ && m_document->javaScriptCompilationUnit
+ && m_document->javaScriptCompilationUnit->unitData()
+ && (m_document->javaScriptCompilationUnit->unitData()->flags
+ & QV4::CompiledData::Unit::PendingTypeCompilation);
QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
- QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher);
- m_compiledData = compiler.compile();
- if (!m_compiledData) {
+ QQmlTypeCompiler compiler(
+ enginePrivate, this, m_document.data(), resolvedTypeCache, dependencyHasher);
+ auto compilationUnit = compiler.compile();
+ if (!compilationUnit) {
qDeleteAll(*resolvedTypeCache);
resolvedTypeCache->clear();
setError(compiler.compilationErrors());
@@ -754,19 +841,31 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach
const bool trySaveToDisk = writeCacheFile() && !typeRecompilation;
if (trySaveToDisk) {
QString errorString;
- if (m_compiledData->saveToDisk(url(), &errorString)) {
+ if (compilationUnit->saveToDisk(url(), &errorString)) {
QString error;
- if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
+ if (!compilationUnit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
// ignore error, keep using the in-memory compilation unit.
}
} else {
- qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->fileName() << "to disk:" << errorString;
+ qCDebug(DBG_DISK_CACHE) << "Error saving cached version of"
+ << compilationUnit->fileName() << "to disk:" << errorString;
}
}
+
+ m_compiledData = std::move(compilationUnit);
+ m_compiledData->typeNameCache = typeNameCache;
+ m_compiledData->resolvedTypes = *resolvedTypeCache;
+ m_compiledData->propertyCaches = std::move(*compiler.propertyCaches());
+ Q_ASSERT(m_compiledData->propertyCaches.count()
+ == static_cast<int>(m_compiledData->objectCount()));
}
void QQmlTypeData::resolveTypes()
{
+ // Load the implicit import since it may have additional scripts.
+ if (!m_implicitImportLoaded && !loadImplicitImport())
+ return;
+
// Add any imported scripts to our resolved set
const auto resolvedScripts = m_importCache->resolvedScripts();
for (const QQmlImports::ScriptReference &script : resolvedScripts) {
@@ -808,8 +907,8 @@ void QQmlTypeData::resolveTypes()
if (ref.type.isCompositeSingleton()) {
ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
if (ref.typeData->isWaiting() || m_waitingOnMe.contains(ref.typeData.data())) {
- qCWarning(lcCycle) << "Cyclic dependency detected between"
- << ref.typeData->urlString() << "and" << urlString();
+ qCDebug(lcCycle) << "Possible cyclic dependency detected between"
+ << ref.typeData->urlString() << "and" << urlString();
continue;
}
addDependency(ref.typeData.data());
@@ -842,11 +941,11 @@ void QQmlTypeData::resolveTypes()
addDependency(ref.typeData.data());
}
if (ref.type.isInlineComponentType()) {
- auto containingType = ref.type.containingType();
- if (containingType.isValid()) {
- auto const url = containingType.sourceUrl();
- if (url.isValid()) {
- auto typeData = typeLoader()->getType(url);
+ QUrl containingTypeUrl = ref.type.sourceUrl();
+ containingTypeUrl.setFragment(QString());
+ if (!containingTypeUrl.isEmpty()) {
+ auto typeData = typeLoader()->getType(containingTypeUrl);
+ if (typeData.data() != this) {
ref.typeData = typeData;
addDependency(typeData.data());
}
@@ -866,7 +965,7 @@ void QQmlTypeData::resolveTypes()
QQmlError QQmlTypeData::buildTypeResolutionCaches(
QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache) const
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache) const
{
typeNameCache->adopt(new QQmlTypeNameCache(m_importCache));
@@ -889,36 +988,31 @@ QQmlError QQmlTypeData::buildTypeResolutionCaches(
ref->setCompilationUnit(resolvedType->typeData->compilationUnit());
if (resolvedType->type.isInlineComponentType()) {
// Inline component which is part of an already resolved type
- int objectId = -1;
- if (qmlType.containingType().isValid()) {
- objectId = qmlType.containingType().lookupInlineComponentIdByName(QString::fromUtf8(qmlType.typeName()));
- qmlType.setInlineComponentObjectId(objectId);
- } else {
- objectId = resolvedType->type.inlineComponentId();
- }
- Q_ASSERT(objectId != -1);
- ref->setTypePropertyCache(resolvedType->typeData->compilationUnit()->propertyCaches.at(objectId));
- ref->setType(qmlType);
+ QString icName = qmlType.elementName();
+ Q_ASSERT(!icName.isEmpty());
+
+ const auto compilationUnit = resolvedType->typeData->compilationUnit();
+ ref->setTypePropertyCache(compilationUnit->propertyCaches.at(
+ compilationUnit->inlineComponentId(icName)));
+ ref->setType(std::move(qmlType));
Q_ASSERT(ref->type().isInlineComponentType());
}
} else if (resolvedType->type.isInlineComponentType()) {
- // Inline component, defined in the file we are currently compiling
- if (!m_inlineComponentToCompiledData.contains(resolvedType.key())) {
- ref->setType(qmlType);
- if (qmlType.isValid()) {
- // this is required for inline components in singletons
- auto type = qmlType.lookupInlineComponentById(qmlType.inlineComponentId()).typeId();
- auto exUnit = QQmlMetaType::obtainExecutableCompilationUnit(type);
- if (exUnit) {
- ref->setCompilationUnit(exUnit);
- ref->setTypePropertyCache(QQmlMetaType::propertyCacheForType(type));
- }
+ ref->setType(qmlType);
+
+ // Inline component
+ // If it's defined in the same file we're currently compiling, we don't want to use it.
+ // We're going to fill in the property caches later after all.
+ if (qmlType.isValid()
+ && !QQmlMetaType::equalBaseUrls(finalUrl(), qmlType.sourceUrl())) {
+
+ // this is required for inline components in singletons
+ const QMetaType type = qmlType.typeId();
+ if (auto unit = QQmlMetaType::obtainCompilationUnit(type)) {
+ ref->setCompilationUnit(std::move(unit));
+ ref->setTypePropertyCache(QQmlMetaType::propertyCacheForType(type));
}
- } else {
- ref->setCompilationUnit(m_inlineComponentToCompiledData[resolvedType.key()]);
- ref->setTypePropertyCache(m_inlineComponentToCompiledData[resolvedType.key()]->rootPropertyCache());
}
-
} else if (qmlType.isValid() && !resolvedType->selfReference) {
ref->setType(qmlType);
Q_ASSERT(ref->type().isValid());
@@ -930,8 +1024,12 @@ QQmlError QQmlTypeData::buildTypeResolutionCaches(
return qQmlCompileError(resolvedType->location, reason);
}
- if (qmlType.containsRevisionedAttributes())
- ref->setTypePropertyCache(QQmlMetaType::propertyCache(qmlType, resolvedType->version));
+ if (qmlType.containsRevisionedAttributes()) {
+ // It can only have (revisioned) properties or methods if it has a metaobject
+ Q_ASSERT(qmlType.metaObject());
+ ref->setTypePropertyCache(
+ QQmlMetaType::propertyCache(qmlType, resolvedType->version));
+ }
}
ref->setVersion(resolvedType->version);
ref->doDynamicTypeCheck();
@@ -949,17 +1047,17 @@ bool QQmlTypeData::resolveType(const QString &typeName, QTypeRevision &version,
QQmlImportNamespace *typeNamespace = nullptr;
QList<QQmlError> errors;
- bool typeFound = m_importCache->resolveType(typeName, &ref.type, &version,
- &typeNamespace, &errors, registrationType,
- typeRecursionDetected);
+ bool typeFound = m_importCache->resolveType(
+ typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors, registrationType,
+ typeRecursionDetected);
if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
// Lazy loading of implicit import
if (loadImplicitImport()) {
// Try again to find the type
errors.clear();
- typeFound = m_importCache->resolveType(typeName, &ref.type, &version,
- &typeNamespace, &errors, registrationType,
- typeRecursionDetected);
+ typeFound = m_importCache->resolveType(
+ typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors,
+ registrationType, typeRecursionDetected);
} else {
return false; //loadImplicitImport() hit an error, and called setError already
}
@@ -997,12 +1095,14 @@ bool QQmlTypeData::resolveType(const QString &typeName, QTypeRevision &version,
return true;
}
-void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
+void QQmlTypeData::scriptImported(
+ const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location,
+ const QString &nameSpace, const QString &qualifier)
{
ScriptReference ref;
ref.script = blob;
ref.location = location;
- ref.qualifier = qualifier;
+ ref.qualifier = qualifier.isEmpty() ? nameSpace : qualifier + QLatin1Char('.') + nameSpace;
m_scripts << ref;
}
diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h
index d5398b0b5f..97419b916b 100644
--- a/src/qml/qml/qqmltypedata_p.h
+++ b/src/qml/qml/qqmltypedata_p.h
@@ -55,10 +55,7 @@ private:
public:
~QQmlTypeData() override;
- const QList<ScriptReference> &resolvedScripts() const;
-
- QV4::ExecutableCompilationUnit *compilationUnit() const;
- QV4::ExecutableCompilationUnit *compilationUnitForInlineComponent(unsigned int icObjectId) const;
+ QV4::CompiledData::CompilationUnit *compilationUnit() const;
// Used by QQmlComponent to get notifications
struct TypeDataCallback {
@@ -69,7 +66,7 @@ public:
void registerCallback(TypeDataCallback *);
void unregisterCallback(TypeDataCallback *);
- CompositeMetaTypeIds typeIds(int objectId = 0) const;
+ QQmlType qmlType(const QString &inlineComponentName = QString()) const;
QByteArray typeClassName() const { return m_typeClassName; }
SourceCodeData backupSourceCode() const { return m_backupSourceCode; }
@@ -84,27 +81,32 @@ protected:
QString stringAt(int index) const override;
private:
+ using InlineComponentData = QV4::CompiledData::InlineComponentData;
+
bool tryLoadFromDiskCache();
bool loadFromSource();
- void restoreIR(QV4::CompiledData::CompilationUnit &&unit);
+ void restoreIR(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit);
void continueLoadFromIR();
void resolveTypes();
QQmlError buildTypeResolutionCaches(
QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
) const;
void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
- void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- const QV4::ResolvedTypeReferenceMap &resolvedTypeCache);
+ QQmlError createTypeAndPropertyCaches(
+ const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
bool resolveType(const QString &typeName, QTypeRevision &version,
TypeReference &ref, int lineNumber = -1, int columnNumber = -1,
bool reportErrors = true,
QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType,
bool *typeRecursionDetected = nullptr);
- void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
+ void scriptImported(
+ const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location,
+ const QString &nameSpace, const QString &qualifier) override;
SourceCodeData m_backupSourceCode; // used when cache verification fails.
QScopedPointer<QmlIR::Document> m_document;
@@ -121,16 +123,15 @@ private:
QMap<int, TypeReference> m_resolvedTypes;
bool m_typesResolved:1;
- // Used for self-referencing types, otherwise -1.
- CompositeMetaTypeIds m_typeIds;
+ // Used for self-referencing types, otherwise invalid.
+ QQmlType m_qmlType;
QByteArray m_typeClassName; // used for meta-object later
- using ExecutableCompilationUnitPtr = QQmlRefPointer<QV4::ExecutableCompilationUnit>;
+ using CompilationUnitPtr = QQmlRefPointer<QV4::CompiledData::CompilationUnit>;
- QHash<int, InlineComponentData> m_inlineComponentData;
+ QHash<QString, InlineComponentData> m_inlineComponentData;
- ExecutableCompilationUnitPtr m_compiledData;
- QHash<int, ExecutableCompilationUnitPtr> m_inlineComponentToCompiledData;
+ CompilationUnitPtr m_compiledData;
QList<TypeDataCallback *> m_callbacks;
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index a30d25c30a..a74397bd93 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -46,6 +46,9 @@ namespace {
};
}
+Q_TRACE_POINT(qtqml, QQmlCompiling_entry, const QUrl &url)
+Q_TRACE_POINT(qtqml, QQmlCompiling_exit)
+
/*!
\class QQmlTypeLoader
\brief The QQmlTypeLoader class abstracts loading files and their dependencies over the network.
@@ -452,7 +455,7 @@ QQmlTypeLoader::Blob::PendingImport::PendingImport(
QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader)
: QQmlDataBlob(url, type, loader)
- , m_importCache(new QQmlImports(loader), QQmlRefPointer<QQmlImports>::Adopt)
+ , m_importCache(new QQmlImports(), QQmlRefPointer<QQmlImports>::Adopt)
{
}
@@ -527,8 +530,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da
typeLoader()->setQmldirContent(qmldirIdentifier, data->content());
const QTypeRevision version = m_importCache->updateQmldirContent(
- typeLoader()->importDatabase(), import->uri, import->qualifier, qmldirIdentifier,
- qmldirUrl, errors);
+ typeLoader(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors);
if (!version.isValid())
return false;
@@ -551,15 +553,12 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da
bool QQmlTypeLoader::Blob::addScriptImport(const QQmlTypeLoader::Blob::PendingImportPtr &import)
{
const QUrl url(import->uri);
- const auto module = m_typeLoader->engine()->handle()->moduleForUrl(url);
- QQmlRefPointer<QQmlScriptBlob> blob;
- if (module.native) {
- blob.adopt(new QQmlScriptBlob(url, m_typeLoader));
- blob->initializeFromNative(*module.native);
- blob->tryDone();
- } else {
- blob = typeLoader()->getScript(finalUrl().resolved(url));
- }
+ QQmlTypeLoader *loader = typeLoader();
+ QQmlRefPointer<QQmlScriptBlob> blob = loader->injectedScript(url);
+ if (!blob)
+ blob = loader->getScript(finalUrl().resolved(url));
+ else
+ Q_ASSERT(blob->status() == QQmlDataBlob::Status::Complete);
addDependency(blob.data());
scriptImported(blob, import->location, import->qualifier, QString());
return true;
@@ -567,7 +566,6 @@ bool QQmlTypeLoader::Blob::addScriptImport(const QQmlTypeLoader::Blob::PendingIm
bool QQmlTypeLoader::Blob::addFileImport(const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors)
{
- QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
QQmlImports::ImportFlags flags;
QUrl importUrl(import->uri);
@@ -581,8 +579,8 @@ bool QQmlTypeLoader::Blob::addFileImport(const QQmlTypeLoader::Blob::PendingImpo
}
const QTypeRevision version = m_importCache->addFileImport(
- importDatabase, import->uri, import->qualifier, import->version, flags,
- import->precedence, nullptr, errors);
+ typeLoader(), import->uri, import->qualifier, import->version, flags,
+ import->precedence, nullptr, errors);
if (!version.isValid())
return false;
@@ -604,6 +602,28 @@ bool QQmlTypeLoader::Blob::addFileImport(const QQmlTypeLoader::Blob::PendingImpo
return true;
}
+static void addDependencyImportError(
+ const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors)
+{
+ QQmlError error;
+ QString reason = errors->front().description();
+ if (reason.size() > 512)
+ reason = reason.first(252) + QLatin1String("... ...") + reason.last(252);
+ if (import->version.hasMajorVersion()) {
+ error.setDescription(QQmlImportDatabase::tr(
+ "module \"%1\" version %2.%3 cannot be imported because:\n%4")
+ .arg(import->uri).arg(import->version.majorVersion())
+ .arg(import->version.hasMinorVersion()
+ ? QString::number(import->version.minorVersion())
+ : QLatin1String("x"))
+ .arg(reason));
+ } else {
+ error.setDescription(QQmlImportDatabase::tr("module \"%1\" cannot be imported because:\n%2")
+ .arg(import->uri, reason));
+ }
+ errors->prepend(error);
+}
+
bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors)
{
QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
@@ -619,9 +639,8 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
[&](const QString &qmldirFilePath, const QString &qmldirUrl) {
// This is a local library import
const QTypeRevision actualVersion = m_importCache->addLibraryImport(
- importDatabase, import->uri, import->qualifier,
- import->version, qmldirFilePath, qmldirUrl, import->flags, import->precedence,
- errors);
+ typeLoader(), import->uri, import->qualifier, import->version, qmldirFilePath,
+ qmldirUrl, import->flags, import->precedence, errors);
if (!actualVersion.isValid())
return false;
@@ -630,20 +649,7 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
import->version = actualVersion;
if (!loadImportDependencies(import, qmldirFilePath, import->flags, errors)) {
- QQmlError error;
- if (import->version.hasMajorVersion()) {
- error.setDescription(QQmlImportDatabase::tr(
- "module \"%1\" version %2.%3 cannot be imported because\n%4")
- .arg(import->uri).arg(import->version.majorVersion())
- .arg(import->version.hasMinorVersion()
- ? QString::number(import->version.minorVersion())
- : QLatin1String("x"))
- .arg(errors->front().description()));
- } else {
- error.setDescription(QQmlImportDatabase::tr("module \"%1\" cannot be imported because\n%2")
- .arg(import->uri, errors->front().description()));
- }
- errors->prepend(error);
+ addDependencyImportError(import, errors);
return false;
}
@@ -654,7 +660,13 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
switch (qmldirResult) {
case QQmlImportDatabase::QmldirFound:
return true;
- case QQmlImportDatabase::QmldirNotFound:
+ case QQmlImportDatabase::QmldirNotFound: {
+ if (!loadImportDependencies(import, QString(), import->flags, errors)) {
+ addDependencyImportError(import, errors);
+ return false;
+ }
+ break;
+ }
case QQmlImportDatabase::QmldirInterceptedToRemote:
break;
case QQmlImportDatabase::QmldirRejected:
@@ -676,8 +688,8 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
|| QQmlMetaType::latestModuleVersion(import->uri).isValid())) {
if (!m_importCache->addLibraryImport(
- importDatabase, import->uri, import->qualifier, import->version,
- QString(), QString(), import->flags, import->precedence, errors).isValid()) {
+ typeLoader(), import->uri, import->qualifier, import->version, QString(),
+ QString(), import->flags, import->precedence, errors).isValid()) {
return false;
}
} else {
@@ -696,9 +708,9 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
if (!remotePathList.isEmpty()) {
// Add this library and request the possible locations for it
const QTypeRevision version = m_importCache->addLibraryImport(
- importDatabase, import->uri, import->qualifier, import->version,
- QString(), QString(), import->flags | QQmlImports::ImportIncomplete,
- import->precedence, errors);
+ typeLoader(), import->uri, import->qualifier, import->version, QString(),
+ QString(), import->flags | QQmlImports::ImportIncomplete, import->precedence,
+ errors);
if (!version.isValid())
return false;
@@ -815,10 +827,10 @@ bool QQmlTypeLoader::Blob::loadImportDependencies(
const QQmlTypeLoader::Blob::PendingImportPtr &currentImport, const QString &qmldirUri,
QQmlImports::ImportFlags flags, QList<QQmlError> *errors)
{
- const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirUri);
- const QList<QQmlDirParser::Import> implicitImports
- = QQmlMetaType::moduleImports(currentImport->uri, currentImport->version)
- + qmldir.imports();
+ QList<QQmlDirParser::Import> implicitImports
+ = QQmlMetaType::moduleImports(currentImport->uri, currentImport->version);
+ if (!qmldirUri.isEmpty())
+ implicitImports += typeLoader()->qmldirContent(qmldirUri).imports();
// Prevent overflow from one category of import into the other.
switch (currentImport->precedence) {
@@ -826,7 +838,7 @@ bool QQmlTypeLoader::Blob::loadImportDependencies(
case QQmlImportInstance::Lowest: {
QQmlError error;
error.setDescription(
- QString::fromLatin1("Too many dependent imports")
+ QString::fromLatin1("Too many dependent imports for %1 %2.%3")
.arg(currentImport->uri)
.arg(currentImport->version.majorVersion())
.arg(currentImport->version.minorVersion()));
@@ -965,6 +977,7 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl
// this was started Asynchronous, but we need to force Synchronous
// completion now (if at all possible with this type of URL).
+#if QT_CONFIG(thread)
if (!m_thread->isThisThread()) {
// this only works when called directly from the UI thread, but not
// when recursively called on the QML thread via resolveTypes()
@@ -973,6 +986,7 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl
m_thread->waitForNextMessage();
}
}
+#endif
}
return typeData;
@@ -992,6 +1006,26 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QByteArray &data, con
return QQmlRefPointer<QQmlTypeData>(typeData, QQmlRefPointer<QQmlTypeData>::Adopt);
}
+void QQmlTypeLoader::injectScript(const QUrl &relativeUrl, const QV4::Value &value)
+{
+ LockHolder<QQmlTypeLoader> holder(this);
+
+ QQmlScriptBlob *blob = new QQmlScriptBlob(relativeUrl, this);
+ blob->initializeFromNative(value);
+ blob->m_isDone = true;
+ blob->m_data.setStatus(QQmlDataBlob::Complete);
+ m_scriptCache.insert(relativeUrl, blob);
+}
+
+QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::injectedScript(const QUrl &relativeUrl)
+{
+ LockHolder<QQmlTypeLoader> holder(this);
+ const auto it = m_scriptCache.constFind(relativeUrl);
+ return (it != m_scriptCache.constEnd() && (*it)->isNative())
+ ? *it
+ : QQmlRefPointer<QQmlScriptBlob>();
+}
+
/*!
Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached.
*/
@@ -1294,6 +1328,11 @@ and qmldir information.
*/
void QQmlTypeLoader::clearCache()
{
+ // Pending messages typically hold references to the blobs they want to be delivered to.
+ // We don't want them anymore.
+ if (m_thread)
+ m_thread->discardMessages();
+
for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter)
(*iter)->release();
for (ScriptCache::Iterator iter = m_scriptCache.begin(), end = m_scriptCache.end(); iter != end; ++iter)
@@ -1310,7 +1349,6 @@ void QQmlTypeLoader::clearCache()
m_importDirCache.clear();
m_importQmlDirCache.clear();
m_checksumCache.clear();
- QQmlMetaType::freeUnusedTypesAndCaches();
}
void QQmlTypeLoader::updateTypeCacheTrimThreshold()
@@ -1332,15 +1370,29 @@ void QQmlTypeLoader::trimCache()
// typeData->m_compiledData may be set early on in the proccess of loading a file, so
// it's important to check the general loading status of the typeData before making any
// other decisions.
- if (typeData->count() == 1 && (typeData->isError() || typeData->isComplete())
- && (!typeData->m_compiledData || typeData->m_compiledData->count() == 1)) {
- // There are no live objects of this type
- iter.value()->release();
- iter = m_typeCache.erase(iter);
- deletedOneType = true;
- } else {
+ if (typeData->count() != 1 || (!typeData->isError() && !typeData->isComplete())) {
++iter;
+ continue;
}
+
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit
+ = typeData->m_compiledData;
+ if (compilationUnit) {
+ if (compilationUnit->count()
+ > QQmlMetaType::countInternalCompositeTypeSelfReferences(
+ compilationUnit) + 1) {
+ ++iter;
+ continue;
+ }
+
+ QQmlMetaType::unregisterInternalCompositeType(compilationUnit);
+ Q_ASSERT(compilationUnit->count() == 1);
+ }
+
+ // There are no live objects of this type
+ iter.value()->release();
+ iter = m_typeCache.erase(iter);
+ deletedOneType = true;
}
if (!deletedOneType)
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 6c53437d86..e9c4559527 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -18,6 +18,7 @@
#include <private/qqmldatablob_p.h>
#include <private/qqmlimport_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qv4compileddata_p.h>
#include <QtQml/qtqmlglobal.h>
#include <QtQml/qqmlerror.h>
@@ -38,14 +39,14 @@ class QQmlProfiler;
class QQmlTypeLoaderThread;
class QQmlEngine;
-class Q_QML_PRIVATE_EXPORT QQmlTypeLoader
+class Q_QML_EXPORT QQmlTypeLoader
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader)
public:
using ChecksumCache = QHash<quintptr, QByteArray>;
enum Mode { PreferSynchronous, Asynchronous, Synchronous };
- class Q_QML_PRIVATE_EXPORT Blob : public QQmlDataBlob
+ class Q_QML_EXPORT Blob : public QQmlDataBlob
{
public:
Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader);
@@ -122,6 +123,23 @@ public:
QQmlTypeLoader(QQmlEngine *);
~QQmlTypeLoader();
+ template<
+ typename Engine,
+ typename EnginePrivate = QQmlEnginePrivate,
+ typename = std::enable_if_t<std::is_same_v<Engine, QQmlEngine>>>
+ static QQmlTypeLoader *get(Engine *engine)
+ {
+ return get(EnginePrivate::get(engine));
+ }
+
+ template<
+ typename Engine,
+ typename = std::enable_if_t<std::is_same_v<Engine, QQmlEnginePrivate>>>
+ static QQmlTypeLoader *get(Engine *engine)
+ {
+ return &engine->typeLoader;
+ }
+
QQmlImportDatabase *importDatabase() const;
ChecksumCache *checksumCache() { return &m_checksumCache; }
const ChecksumCache *checksumCache() const { return &m_checksumCache; }
@@ -131,6 +149,9 @@ public:
QQmlRefPointer<QQmlTypeData> getType(const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous);
QQmlRefPointer<QQmlTypeData> getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous);
+ void injectScript(const QUrl &relativeUrl, const QV4::Value &value);
+ QQmlRefPointer<QQmlScriptBlob> injectedScript(const QUrl &relativeUrl);
+
QQmlRefPointer<QQmlScriptBlob> getScript(const QUrl &unNormalizedUrl);
QQmlRefPointer<QQmlQmldirData> getQmldir(const QUrl &);
diff --git a/src/qml/qml/qqmltypeloaderqmldircontent.cpp b/src/qml/qml/qqmltypeloaderqmldircontent.cpp
index 57044fcdc3..5744cf2bb7 100644
--- a/src/qml/qml/qqmltypeloaderqmldircontent.cpp
+++ b/src/qml/qml/qqmltypeloaderqmldircontent.cpp
@@ -7,10 +7,9 @@
QT_BEGIN_NAMESPACE
-QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const
+QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri, const QUrl &url) const
{
QList<QQmlError> errors;
- const QUrl url(uri);
const auto parseErrors = m_parser.errors(uri);
for (const auto &parseError : parseErrors) {
QQmlError error;
@@ -29,6 +28,7 @@ void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QStr
m_hasContent = true;
m_location = location;
m_parser.parse(content);
+ m_parser.disambiguateFileSelectors();
}
void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error)
diff --git a/src/qml/qml/qqmltypeloaderqmldircontent_p.h b/src/qml/qml/qqmltypeloaderqmldircontent_p.h
index 5749e04eaf..421eb16da4 100644
--- a/src/qml/qml/qqmltypeloaderqmldircontent_p.h
+++ b/src/qml/qml/qqmltypeloaderqmldircontent_p.h
@@ -35,7 +35,7 @@ public:
bool hasContent() const { return m_hasContent; }
bool hasError() const { return m_parser.hasError(); }
- QList<QQmlError> errors(const QString &uri) const;
+ QList<QQmlError> errors(const QString &uri, const QUrl &url) const;
QString typeNamespace() const { return m_parser.typeNamespace(); }
@@ -47,6 +47,13 @@ public:
QString qmldirLocation() const { return m_location; }
QString preferredPath() const { return m_parser.preferredPath(); }
+ bool hasRedirection() const
+ {
+ const QString preferred = preferredPath();
+ return !preferred.isEmpty()
+ && preferred != QStringView(m_location).chopped(strlen("qmldir"));
+ }
+
bool designerSupported() const { return m_parser.designerSupported(); }
bool hasTypeInfo() const { return !m_parser.typeInfos().isEmpty(); }
diff --git a/src/qml/qml/qqmltypeloaderthread.cpp b/src/qml/qml/qqmltypeloaderthread.cpp
index 3d35962c08..18d1dbe7ce 100644
--- a/src/qml/qml/qqmltypeloaderthread.cpp
+++ b/src/qml/qml/qqmltypeloaderthread.cpp
@@ -72,22 +72,12 @@ void QQmlTypeLoaderThread::loadWithCachedUnitAsync(const QQmlDataBlob::Ptr &b, c
void QQmlTypeLoaderThread::callCompleted(const QQmlDataBlob::Ptr &b)
{
-#if !QT_CONFIG(thread)
- if (!isThisThread())
- postMethodToThread(&This::callCompletedMain, b);
-#else
postMethodToMain(&This::callCompletedMain, b);
-#endif
}
void QQmlTypeLoaderThread::callDownloadProgressChanged(const QQmlDataBlob::Ptr &b, qreal p)
{
-#if !QT_CONFIG(thread)
- if (!isThisThread())
- postMethodToThread(&This::callDownloadProgressChangedMain, b, p);
-#else
postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
-#endif
}
void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
diff --git a/src/qml/qml/qqmltypemodule_p.h b/src/qml/qml/qqmltypemodule_p.h
index acf21de595..717b07ec60 100644
--- a/src/qml/qml/qqmltypemodule_p.h
+++ b/src/qml/qml/qqmltypemodule_p.h
@@ -94,7 +94,7 @@ public:
void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const;
private:
- static Q_QML_PRIVATE_EXPORT QQmlType findType(
+ static Q_QML_EXPORT QQmlType findType(
const QList<QQmlTypePrivate *> *types, QTypeRevision version);
const QString m_module;
diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h
index 6b3f9094f1..1ec0a65fa0 100644
--- a/src/qml/qml/qqmltypenamecache_p.h
+++ b/src/qml/qml/qqmltypenamecache_p.h
@@ -45,7 +45,7 @@ struct QQmlImportRef {
class QQmlType;
class QQmlEngine;
-class Q_QML_PRIVATE_EXPORT QQmlTypeNameCache : public QQmlRefCount
+class Q_QML_EXPORT QQmlTypeNameCache final : public QQmlRefCounted<QQmlTypeNameCache>
{
public:
QQmlTypeNameCache(const QQmlRefPointer<QQmlImports> &imports) : m_imports(imports) {}
@@ -74,27 +74,29 @@ public:
// Restrict the types allowed for key. We don't want QV4::ScopedString, for example.
template<QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion>
- Result query(const QHashedStringRef &key) const
+ Result query(const QHashedStringRef &key, QQmlTypeLoader *typeLoader) const
{
- return doQuery<const QHashedStringRef &, recursionRestriction>(key);
+ return doQuery<const QHashedStringRef &, recursionRestriction>(key, typeLoader);
}
template<QueryNamespaced queryNamespaced = QueryNamespaced::Yes>
- Result query(const QHashedStringRef &key, const QQmlImportRef *importNamespace) const
+ Result query(const QHashedStringRef &key, const QQmlImportRef *importNamespace,
+ QQmlTypeLoader *typeLoader) const
{
- return doQuery<const QHashedStringRef &, queryNamespaced>(key, importNamespace);
+ return doQuery<const QHashedStringRef &, queryNamespaced>(key, importNamespace, typeLoader);
}
template<QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion>
- Result query(const QV4::String *key) const
+ Result query(const QV4::String *key, QQmlTypeLoader *typeLoader) const
{
- return doQuery<const QV4::String *, recursionRestriction>(key);
+ return doQuery<const QV4::String *, recursionRestriction>(key, typeLoader);
}
template<QueryNamespaced queryNamespaced = QueryNamespaced::Yes>
- Result query(const QV4::String *key, const QQmlImportRef *importNamespace) const
+ Result query(const QV4::String *key, const QQmlImportRef *importNamespace,
+ QQmlTypeLoader *typeLoader) const
{
- return doQuery<const QV4::String *, queryNamespaced>(key, importNamespace);
+ return doQuery<const QV4::String *, queryNamespaced>(key, importNamespace, typeLoader);
}
private:
@@ -122,7 +124,7 @@ private:
static QString toQString(const QV4::String *key) { return key->toQStringNoThrow(); }
template<typename Key, QQmlImport::RecursionRestriction recursionRestriction>
- Result doQuery(Key name) const
+ Result doQuery(Key name, QQmlTypeLoader *typeLoader) const
{
Result result = doQuery(m_namedImports, name);
@@ -141,9 +143,9 @@ private:
QQmlType t;
bool typeRecursionDetected = false;
const bool typeFound = m_imports->resolveType(
- toHashedStringRef(name), &t, nullptr, &typeNamespace, &errors,
- QQmlType::AnyRegistrationType,
- recursionRestriction == QQmlImport::AllowRecursion
+ typeLoader, toHashedStringRef(name), &t, nullptr, &typeNamespace, &errors,
+ QQmlType::AnyRegistrationType,
+ recursionRestriction == QQmlImport::AllowRecursion
? &typeRecursionDetected
: nullptr);
if (typeFound)
@@ -155,7 +157,7 @@ private:
}
template<typename Key, QueryNamespaced queryNamespaced>
- Result doQuery(Key name, const QQmlImportRef *importNamespace) const
+ Result doQuery(Key name, const QQmlImportRef *importNamespace, QQmlTypeLoader *typeLoader) const
{
Q_ASSERT(importNamespace && importNamespace->scriptIndex == -1);
@@ -183,7 +185,7 @@ private:
QList<QQmlError> errors;
QQmlType t;
bool typeFound = m_imports->resolveType(
- qualifiedTypeName, &t, nullptr, &typeNamespace, &errors);
+ typeLoader, qualifiedTypeName, &t, nullptr, &typeNamespace, &errors);
if (typeFound)
return Result(t);
}
diff --git a/src/qml/qml/qqmltypenotavailable.cpp b/src/qml/qml/qqmltypenotavailable.cpp
deleted file mode 100644
index b3d33eb706..0000000000
--- a/src/qml/qml/qqmltypenotavailable.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qqmltypenotavailable_p.h"
-
-QT_BEGIN_NAMESPACE
-
-int qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message)
-{
- return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(uri,versionMajor,versionMinor,qmlName,message);
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qqmltypenotavailable_p.cpp"
diff --git a/src/qml/qml/qqmltypenotavailable_p.h b/src/qml/qml/qqmltypenotavailable_p.h
deleted file mode 100644
index e198f3259a..0000000000
--- a/src/qml/qml/qqmltypenotavailable_p.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QQMLTYPENOTAVAILABLE_H
-#define QQMLTYPENOTAVAILABLE_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <qqml.h>
-#include <private/qglobal_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QQmlTypeNotAvailable : public QObject {
- Q_OBJECT
- QML_NAMED_ELEMENT(TypeNotAvailable)
- QML_ADDED_IN_VERSION(2, 15)
- QML_UNCREATABLE("Type not available.")
-};
-
-QT_END_NAMESPACE
-
-QML_DECLARE_TYPE(QQmlTypeNotAvailable)
-
-#endif // QQMLTYPENOTAVAILABLE_H
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 5a3127104c..0d8786a9df 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -23,15 +23,32 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(QQmlTypeWrapper);
DEFINE_OBJECT_VTABLE(QQmlScopedEnumWrapper);
-void Heap::QQmlTypeWrapper::init()
+
+void Heap::QQmlTypeWrapper::init(TypeNameMode m, QObject *o, const QQmlTypePrivate *type)
+{
+ Q_ASSERT(type);
+ Object::init();
+ mode = m;
+ object.init(o);
+ typePrivate = type;
+ QQmlType::refHandle(typePrivate);
+}
+
+void Heap::QQmlTypeWrapper::init(
+ TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import)
{
+ Q_ASSERT(type);
Object::init();
- mode = IncludeEnums;
- object.init();
+ mode = m;
+ object.init(o);
+ typeNamespace = type;
+ typeNamespace->addref();
+ importNamespace = import;
}
void Heap::QQmlTypeWrapper::destroy()
{
+ Q_ASSERT(typePrivate || typeNamespace);
QQmlType::derefHandle(typePrivate);
typePrivate = nullptr;
if (typeNamespace)
@@ -50,19 +67,48 @@ bool QQmlTypeWrapper::isSingleton() const
return d()->type().isSingleton();
}
+const QMetaObject *QQmlTypeWrapper::metaObject() const
+{
+ const QQmlType type = d()->type();
+ if (!type.isValid())
+ return nullptr;
+
+ if (type.isSingleton()) {
+ auto metaObjectCandidate = type.metaObject();
+ // if the candidate is the same as te baseMetaObject, we know that
+ // we don't have an extended singleton; in that case the
+ // actual instance might be subclass of type instead of type itself
+ // so we need to query the actual object for it's meta-object
+ if (metaObjectCandidate == type.baseMetaObject()) {
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine());
+ auto object = qmlEngine->singletonInstance<QObject *>(type);
+ if (object)
+ return object->metaObject();
+ }
+ /* if we instead have an extended singleton, the dynamic proxy
+ meta-object must alreday be set up correctly
+ ### TODO: it isn't, as QQmlTypePrivate::init has no way to
+ query the object
+ */
+ return metaObjectCandidate;
+ }
+
+ return type.attachedPropertiesType(QQmlEnginePrivate::get(engine()->qmlEngine()));
+}
+
QObject *QQmlTypeWrapper::object() const
{
const QQmlType type = d()->type();
if (!type.isValid())
return nullptr;
- QQmlEngine *qmlEngine = engine()->qmlEngine();
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine());
if (type.isSingleton())
- return QQmlEnginePrivate::get(qmlEngine)->singletonInstance<QObject *>(type);
+ return qmlEngine->singletonInstance<QObject *>(type);
return qmlAttachedPropertiesObject(
d()->object,
- type.attachedPropertiesFunction(QQmlEnginePrivate::get(qmlEngine)));
+ type.attachedPropertiesFunction(qmlEngine));
}
QObject* QQmlTypeWrapper::singletonObject() const
@@ -98,47 +144,31 @@ ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o,
Q_ASSERT(t.isValid());
Scope scope(engine);
- Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>());
- w->d()->mode = mode; w->d()->object = o;
- w->d()->typePrivate = t.priv();
- QQmlType::refHandle(w->d()->typePrivate);
+ Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>(
+ mode, o, t.priv()));
return w.asReturnedValue();
}
// Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a
// namespace.
-ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, const QQmlImportRef *importNamespace,
- Heap::QQmlTypeWrapper::TypeNameMode mode)
+ReturnedValue QQmlTypeWrapper::create(
+ QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t,
+ const QQmlImportRef *importNamespace, Heap::QQmlTypeWrapper::TypeNameMode mode)
{
Q_ASSERT(t);
Q_ASSERT(importNamespace);
Scope scope(engine);
- Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>());
- w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t.data(); w->d()->importNamespace = importNamespace;
- t->addref();
+ Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>(
+ mode, o, t.data(), importNamespace));
return w.asReturnedValue();
}
-static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qobjectSingleton,
- const QQmlType &type, bool *ok)
+static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, const QQmlType &type, bool *ok)
{
Q_ASSERT(ok != nullptr);
- int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, ok);
- if (*ok)
- return value;
-
- // ### Optimize
- QByteArray enumName = name->toQString().toUtf8();
- const QMetaObject *metaObject = qobjectSingleton->metaObject();
- for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) {
- QMetaEnum e = metaObject->enumerator(ii);
- value = e.keyToValue(enumName.constData(), ok);
- if (*ok)
- return value;
- }
- *ok = false;
- return -1;
+ const int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, ok);
+ return *ok ? value : -1;
}
ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
@@ -149,7 +179,7 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
if (!id.isString())
return Object::virtualGet(m, id, receiver, hasProperty);
- QV4::ExecutionEngine *v4 = static_cast<const QQmlTypeWrapper *>(m)->engine();
+ QV4::ExecutionEngine *v4 = m->engine();
QV4::Scope scope(v4);
ScopedString name(scope, id.asStringOrSymbol());
@@ -163,23 +193,24 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
QObject *object = w->d()->object;
QQmlType type = w->d()->type();
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(v4->qmlEngine());
if (type.isValid()) {
// singleton types are handled differently to other types.
if (type.isSingleton()) {
- QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine());
+
QJSValue scriptSingleton;
if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
- if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) {
+ if (QObject *qobjectSingleton = enginePrivate->singletonInstance<QObject*>(type)) {
// check for enum value
const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums;
if (includeEnums && name->startsWithUpper()) {
bool ok = false;
- int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok);
+ int value = enumForSingleton(v4, name, type, &ok);
if (ok)
return QV4::Value::fromInt32(value).asReturnedValue();
- value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
+ value = type.scopedEnumIndex(enginePrivate, name, &ok);
if (ok) {
Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
enumWrapper->d()->typePrivate = type.priv();
@@ -200,7 +231,7 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
return result;
}
} else if (type.isQJSValueSingleton()) {
- QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type);
+ QJSValue scriptSingleton = enginePrivate->singletonInstance<QJSValue>(type);
if (!scriptSingleton.isUndefined()) {
// NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable.
QV4::ScopedObject o(scope, QJSValuePrivate::asReturnedValue(&scriptSingleton));
@@ -215,11 +246,11 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
if (name->startsWithUpper()) {
bool ok = false;
- int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
+ int value = type.enumValue(enginePrivate, name, &ok);
if (ok)
return QV4::Value::fromInt32(value).asReturnedValue();
- value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
+ value = type.scopedEnumIndex(enginePrivate, name, &ok);
if (ok) {
Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
enumWrapper->d()->typePrivate = type.priv();
@@ -249,7 +280,8 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
} else if (w->d()->typeNamespace) {
Q_ASSERT(w->d()->importNamespace);
- QQmlTypeNameCache::Result r = w->d()->typeNamespace->query(name, w->d()->importNamespace);
+ QQmlTypeNameCache::Result r = w->d()->typeNamespace->query(
+ name, w->d()->importNamespace, QQmlTypeLoader::get(enginePrivate));
if (r.isValid()) {
if (r.type.isValid()) {
@@ -357,19 +389,11 @@ bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b)
return false;
}
-ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var)
+static ReturnedValue instanceOfQObject(const QV4::QQmlTypeWrapper *typeWrapper, const QObjectWrapper *objectWrapper)
{
- Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>());
- const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject);
-
- // can only compare a QObject* against a QML type
- const QObjectWrapper *wrapper = var.as<QObjectWrapper>();
- if (!wrapper)
- return QV4::Encode(false);
-
- QV4::ExecutionEngine *engine = typeObject->internalClass()->engine;
+ QV4::ExecutionEngine *engine = typeWrapper->internalClass()->engine;
// in case the wrapper outlived the QObject*
- const QObject *wrapperObject = wrapper->object();
+ const QObject *wrapperObject = objectWrapper->object();
if (!wrapperObject)
return engine->throwTypeError();
@@ -386,12 +410,14 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
QQmlEnginePrivate *qenginepriv = QQmlEnginePrivate::get(engine->qmlEngine());
QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
- if (ExecutableCompilationUnit *cu = td->compilationUnit())
- myQmlType = QQmlMetaType::metaObjectForType(cu->typeIds.id);
+ if (CompiledData::CompilationUnit *cu = td->compilationUnit())
+ myQmlType = QQmlMetaType::metaObjectForType(cu->metaType());
else
return Encode(false); // It seems myQmlType has some errors, so we could not compile it.
} else {
myQmlType = QQmlMetaType::metaObjectForType(myTypeId);
+ if (myQmlType.isNull())
+ return Encode(false);
}
const QMetaObject *theirType = wrapperObject->metaObject();
@@ -399,6 +425,29 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType));
}
+ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var)
+{
+ Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>());
+ const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject);
+
+ if (const QObjectWrapper *objectWrapper = var.as<QObjectWrapper>())
+ return instanceOfQObject(typeWrapper, objectWrapper);
+
+ const QQmlType type = typeWrapper->d()->type();
+ if (type.isValueType()) {
+ if (const QQmlValueTypeWrapper *valueWrapper = var.as<QQmlValueTypeWrapper>()) {
+ return QV4::Encode(QQmlMetaObject::canConvert(valueWrapper->metaObject(),
+ type.metaObjectForValueType()));
+ }
+
+ // We want "foo as valuetype" to return undefined if it doesn't match.
+ return Encode::undefined();
+ }
+
+ // If the target type is an object type we want null.
+ return Encode(false);
+}
+
ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
{
// Keep this code in sync with ::virtualGet
@@ -428,8 +477,9 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
if (property) {
ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
if (qualifiesForMethodLookup(property)) {
+ QV4::Heap::QObjectMethod *method = nullptr;
setupQObjectMethodLookup(
- lookup, ddata, property, val->objectValue(), nullptr);
+ lookup, ddata, property, val->objectValue(), method);
lookup->getter = QQmlTypeWrapper::lookupSingletonMethod;
} else {
setupQObjectLookup(
@@ -453,7 +503,7 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
bool ok = false;
int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok);
if (ok) {
- lookup->qmlEnumValueLookup.ic = This->internalClass();
+ lookup->qmlEnumValueLookup.ic.set(engine, This->internalClass());
lookup->qmlEnumValueLookup.encodedEnumValue
= QV4::Value::fromInt32(value).asReturnedValue();
lookup->getter = QQmlTypeWrapper::lookupEnumValue;
@@ -468,9 +518,9 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
QQmlType::refHandle(enumWrapper->d()->typePrivate);
enumWrapper->d()->scopeEnumIndex = value;
- lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass();
- lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper
- = static_cast<Heap::Object*>(enumWrapper->heapObject());
+ lookup->qmlScopedEnumWrapperLookup.ic.set(engine, This->internalClass());
+ lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.set(engine,
+ static_cast<Heap::Object*>(enumWrapper->heapObject()));
lookup->getter = QQmlTypeWrapper::lookupScopedEnum;
return enumWrapper.asReturnedValue();
}
@@ -603,12 +653,12 @@ ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engi
{
Scope scope(engine);
Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>(
- l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper));
+ l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.get()));
auto *o = static_cast<Heap::Object *>(base.heapObject());
if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) {
QQmlType::derefHandle(enumWrapper->d()->typePrivate);
- l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr;
+ l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.clear();
l->getter = Lookup::getterGeneric;
return Lookup::getterGeneric(l, engine, base);
}
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index ff2dda0a02..717efaf20e 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -38,7 +38,9 @@ struct QQmlTypeWrapper : Object {
ExcludeEnums
};
- void init();
+ void init(TypeNameMode m, QObject *o, const QQmlTypePrivate *type);
+ void init(TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import);
+
void destroy();
TypeNameMode mode;
QV4QPointer<QObject> object;
@@ -66,6 +68,7 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
V4_NEEDS_DESTROY
bool isSingleton() const;
+ const QMetaObject *metaObject() const;
QObject *object() const;
QObject *singletonObject() const;
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 428ce9b697..4088d6e6c4 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -55,16 +55,25 @@ void QQmlGadgetPtrWrapper::write(
QVariant QQmlGadgetPtrWrapper::value() const
{
Q_ASSERT(m_gadgetPtr);
- return QVariant(metaType(), m_gadgetPtr);
+
+ const QMetaType m = metaType();
+ return m == QMetaType::fromType<QVariant>()
+ ? *static_cast<const QVariant *>(m_gadgetPtr)
+ : QVariant(m, m_gadgetPtr);
}
void QQmlGadgetPtrWrapper::setValue(const QVariant &value)
{
Q_ASSERT(m_gadgetPtr);
- Q_ASSERT(metaType() == value.metaType());
- const QQmlValueType *type = valueType();
- type->destruct(m_gadgetPtr);
- type->construct(m_gadgetPtr, value.constData());
+
+ const QMetaType m = metaType();
+ m.destruct(m_gadgetPtr);
+ if (m == QMetaType::fromType<QVariant>()) {
+ m.construct(m_gadgetPtr, &value);
+ } else {
+ Q_ASSERT(m == value.metaType());
+ m.construct(m_gadgetPtr, value.constData());
+ }
}
int QQmlGadgetPtrWrapper::metaCall(QMetaObject::Call type, int id, void **argv)
diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h
index bc7a210cdf..8815c914ce 100644
--- a/src/qml/qml/qqmlvaluetype_p.h
+++ b/src/qml/qml/qqmlvaluetype_p.h
@@ -30,7 +30,7 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlValueType : public QDynamicMetaObjectData
+class Q_QML_EXPORT QQmlValueType : public QDynamicMetaObjectData
{
public:
QQmlValueType() = default;
@@ -60,7 +60,7 @@ private:
QMetaObject *m_dynamicMetaObject = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlGadgetPtrWrapper : public QObject
+class Q_QML_EXPORT QQmlGadgetPtrWrapper : public QObject
{
Q_OBJECT
public:
@@ -93,12 +93,17 @@ public:
property.writeOnGadget(m_gadgetPtr, value);
}
+ void writeOnGadget(const QMetaProperty &property, QVariant &&value)
+ {
+ property.writeOnGadget(m_gadgetPtr, std::move(value));
+ }
+
private:
const QQmlValueType *valueType() const;
void *m_gadgetPtr = nullptr;
};
-struct Q_QML_PRIVATE_EXPORT QQmlPointFValueType
+struct Q_QML_EXPORT QQmlPointFValueType
{
QPointF v;
Q_PROPERTY(qreal x READ x WRITE setX FINAL)
@@ -118,9 +123,11 @@ public:
qreal y() const;
void setX(qreal);
void setY(qreal);
+
+ operator QPointF() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlPointValueType
+struct Q_QML_EXPORT QQmlPointValueType
{
QPoint v;
Q_PROPERTY(int x READ x WRITE setX FINAL)
@@ -133,14 +140,18 @@ struct Q_QML_PRIVATE_EXPORT QQmlPointValueType
QML_STRUCTURED_VALUE
public:
+ QQmlPointValueType() = default;
+ Q_INVOKABLE QQmlPointValueType(const QPointF &point) : v(point.toPoint()) {}
Q_INVOKABLE QString toString() const;
int x() const;
int y() const;
void setX(int);
void setY(int);
+
+ operator QPoint() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlSizeFValueType
+struct Q_QML_EXPORT QQmlSizeFValueType
{
QSizeF v;
Q_PROPERTY(qreal width READ width WRITE setWidth FINAL)
@@ -160,9 +171,11 @@ public:
qreal height() const;
void setWidth(qreal);
void setHeight(qreal);
+
+ operator QSizeF() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlSizeValueType
+struct Q_QML_EXPORT QQmlSizeValueType
{
QSize v;
Q_PROPERTY(int width READ width WRITE setWidth FINAL)
@@ -175,14 +188,18 @@ struct Q_QML_PRIVATE_EXPORT QQmlSizeValueType
QML_STRUCTURED_VALUE
public:
+ QQmlSizeValueType() = default;
+ Q_INVOKABLE QQmlSizeValueType(const QSizeF &size) : v(size.toSize()) {}
Q_INVOKABLE QString toString() const;
int width() const;
int height() const;
void setWidth(int);
void setHeight(int);
+
+ operator QSize() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlRectFValueType
+struct Q_QML_EXPORT QQmlRectFValueType
{
QRectF v;
Q_PROPERTY(qreal x READ x WRITE setX FINAL)
@@ -218,9 +235,11 @@ public:
qreal right() const;
qreal top() const;
qreal bottom() const;
+
+ operator QRectF() const { return v; }
};
-struct Q_QML_PRIVATE_EXPORT QQmlRectValueType
+struct Q_QML_EXPORT QQmlRectValueType
{
QRect v;
Q_PROPERTY(int x READ x WRITE setX FINAL)
@@ -239,6 +258,8 @@ struct Q_QML_PRIVATE_EXPORT QQmlRectValueType
QML_STRUCTURED_VALUE
public:
+ QQmlRectValueType() = default;
+ Q_INVOKABLE QQmlRectValueType(const QRectF &rect) : v(rect.toRect()) {}
Q_INVOKABLE QString toString() const;
int x() const;
int y() const;
@@ -254,12 +275,14 @@ public:
int right() const;
int top() const;
int bottom() const;
+
+ operator QRect() const { return v; }
};
#if QT_CONFIG(easingcurve)
namespace QQmlEasingEnums
{
-Q_NAMESPACE_EXPORT(Q_QML_PRIVATE_EXPORT)
+Q_NAMESPACE_EXPORT(Q_QML_EXPORT)
QML_NAMED_ELEMENT(Easing)
QML_ADDED_IN_VERSION(2, 0)
@@ -294,7 +317,7 @@ enum Type {
Q_ENUM_NS(Type)
};
-struct Q_QML_PRIVATE_EXPORT QQmlEasingValueType
+struct Q_QML_EXPORT QQmlEasingValueType
{
QEasingCurve v;
Q_GADGET
@@ -321,9 +344,19 @@ public:
void setPeriod(qreal);
void setBezierCurve(const QVariantList &);
QVariantList bezierCurve() const;
+
+ operator QEasingCurve() const { return v; }
};
#endif
+struct QQmlV4ExecutionEnginePtrForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQmlV4ExecutionEnginePtr)
+ QML_EXTENDED(QQmlV4ExecutionEnginePtrForeign)
+};
+
QT_END_NAMESPACE
#endif // QQMLVALUETYPE_P_H
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 4ef757b8e1..7075d0f5f6 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -46,31 +46,22 @@ namespace QV4 {
Heap::QQmlValueTypeWrapper *Heap::QQmlValueTypeWrapper::detached() const
{
return internalClass->engine->memoryManager->allocate<QV4::QQmlValueTypeWrapper>(
- m_gadgetPtr, m_valueType, m_metaObject, nullptr, -1, NoFlag);
+ m_gadgetPtr, QMetaType(m_metaType), m_metaObject, nullptr, -1, NoFlag);
}
void Heap::QQmlValueTypeWrapper::destroy()
{
if (m_gadgetPtr) {
- m_valueType->metaType().destruct(m_gadgetPtr);
+ metaType().destruct(m_gadgetPtr);
::operator delete(m_gadgetPtr);
}
ReferenceObject::destroy();
}
-void Heap::QQmlValueTypeWrapper::setData(const void *data)
-{
- if (auto *gadget = gadgetPtr())
- valueType()->metaType().destruct(gadget);
- if (!gadgetPtr())
- setGadgetPtr(::operator new(valueType()->metaType().sizeOf()));
- valueType()->metaType().construct(gadgetPtr(), data);
-}
-
QVariant Heap::QQmlValueTypeWrapper::toVariant() const
{
Q_ASSERT(gadgetPtr());
- return QVariant(valueType()->metaType(), gadgetPtr());
+ return QVariant(metaType(), gadgetPtr());
}
bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
@@ -78,7 +69,7 @@ bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
Q_ASSERT(isVariant());
const QMetaType variantReferenceType = variant.metaType();
- if (variantReferenceType != valueType()->metaType()) {
+ if (variantReferenceType != metaType()) {
// This is a stale VariantReference. That is, the variant has been
// overwritten with a different type in the meantime.
// We need to modify this reference to the updated value type, if
@@ -86,12 +77,12 @@ bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
if (QQmlMetaType::isValueType(variantReferenceType)) {
const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(variantReferenceType);
if (gadgetPtr()) {
- valueType()->metaType().destruct(gadgetPtr());
+ metaType().destruct(gadgetPtr());
::operator delete(gadgetPtr());
}
setGadgetPtr(nullptr);
setMetaObject(mo);
- setValueType(QQmlMetaType::valueType(variantReferenceType));
+ setMetaType(variantReferenceType);
if (!mo)
return false;
} else {
@@ -106,8 +97,8 @@ bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
void *Heap::QQmlValueTypeWrapper::storagePointer()
{
if (!gadgetPtr()) {
- setGadgetPtr(::operator new(valueType()->metaType().sizeOf()));
- valueType()->metaType().construct(gadgetPtr(), nullptr);
+ setGadgetPtr(::operator new(metaType().sizeOf()));
+ metaType().construct(gadgetPtr(), nullptr);
}
return gadgetPtr();
}
@@ -132,7 +123,7 @@ ReturnedValue QQmlValueTypeWrapper::create(
// Either we're enforcing the location, then we have to read right away.
// Or we don't then we lazy-load. In neither case we pass any data.
Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
- nullptr, cloneFrom->valueType(), cloneFrom->metaObject(),
+ nullptr, cloneFrom->metaType(), cloneFrom->metaObject(),
object, cloneFrom->property(), cloneFrom->flags()));
r->d()->setLocation(cloneFrom->function(), cloneFrom->statementIndex());
if (cloneFrom->enforcesLocation())
@@ -202,15 +193,14 @@ ReturnedValue QQmlValueTypeWrapper::create(
Scope scope(engine);
initProto(engine);
- auto valueType = QQmlMetaType::valueType(type);
- if (!valueType) {
+ if (!type.isValid()) {
return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
.arg(QString::fromUtf8(type.name())));
}
// If data is given explicitly, we assume it has just been read from the property
Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
- data, valueType, metaObject, object, property, flags));
+ data, type, metaObject, object, property, flags));
if (CppStackFrame *frame = engine->currentStackFrame)
r->d()->setLocation(frame->v4Function, frame->statementNumber());
if (!data && r->d()->enforcesLocation())
@@ -224,15 +214,14 @@ ReturnedValue QQmlValueTypeWrapper::create(
Scope scope(engine);
initProto(engine);
- auto valueType = QQmlMetaType::valueType(type);
- if (!valueType) {
+ if (!type.isValid()) {
return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
.arg(QString::fromUtf8(type.name())));
}
Scoped<QQmlValueTypeWrapper> r(
scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
- data, valueType, metaObject, nullptr, -1, Heap::ReferenceObject::NoFlag));
+ data, type, metaObject, nullptr, -1, Heap::ReferenceObject::NoFlag));
return r->asReturnedValue();
}
@@ -247,7 +236,7 @@ bool QQmlValueTypeWrapper::toGadget(void *data) const
{
if (d()->isReference() && !readReferenceValue())
return false;
- const QMetaType type = d()->valueType()->metaType();
+ const QMetaType type = d()->metaType();
type.destruct(data);
type.construct(data, d()->gadgetPtr());
return true;
@@ -350,12 +339,6 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
time, valueTypeWrapper, index, referenceFlags(metaObject, index));
};
-#if QT_CONFIG(qml_locale)
- const auto wrapLocale = [engine](const QLocale &locale) {
- return QQmlLocale::wrap(engine, locale);
- };
-#endif
-
#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
case metatype: { \
cpptype v; \
@@ -368,7 +351,7 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
QMetaObject::ReadProperty, &metaObject, &index);
const int metaTypeId = isEnum
- ? QMetaType::Int
+ ? metaType.underlyingType().id()
: (metaType.flags() & QMetaType::PointerToQObject)
? QMetaType::QObjectStar
: metaType.id();
@@ -383,6 +366,8 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
VALUE_TYPE_LOAD(QMetaType::Int, int, int);
VALUE_TYPE_LOAD(QMetaType::UInt, uint, uint);
+ VALUE_TYPE_LOAD(QMetaType::Long, long, double);
+ VALUE_TYPE_LOAD(QMetaType::ULong, ulong, double);
VALUE_TYPE_LOAD(QMetaType::LongLong, qlonglong, double);
VALUE_TYPE_LOAD(QMetaType::ULongLong, qulonglong, double);
VALUE_TYPE_LOAD(QMetaType::Double, double, double);
@@ -406,9 +391,6 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
VALUE_TYPE_LOAD(QMetaType::QJsonValue, QJsonValue, wrapJsonValue);
VALUE_TYPE_LOAD(QMetaType::QJsonObject, QJsonObject, wrapJsonObject);
VALUE_TYPE_LOAD(QMetaType::QJsonArray, QJsonArray, wrapJsonArray);
-#if QT_CONFIG(qml_locale)
- VALUE_TYPE_LOAD(QMetaType::QLocale, QLocale, wrapLocale);
-#endif
case QMetaType::QPixmap:
case QMetaType::QImage: {
QVariant v(metaType);
@@ -557,12 +539,12 @@ bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
int QQmlValueTypeWrapper::typeId() const
{
- return d()->valueType()->metaType().id();
+ return d()->metaType().id();
}
QMetaType QQmlValueTypeWrapper::type() const
{
- return d()->valueType()->metaType();
+ return d()->metaType();
}
bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
@@ -571,9 +553,9 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
Q_ALLOCA_DECLARE(void, gadget);
if (d()->isReference()) {
if (!d()->gadgetPtr()) {
- Q_ALLOCA_ASSIGN(void, gadget, d()->valueType()->metaType().sizeOf());
+ Q_ALLOCA_ASSIGN(void, gadget, d()->metaType().sizeOf());
d()->setGadgetPtr(gadget);
- d()->valueType()->metaType().construct(d()->gadgetPtr(), nullptr);
+ d()->metaType().construct(d()->gadgetPtr(), nullptr);
destructGadgetOnExit = true;
}
if (!readReferenceValue())
@@ -586,7 +568,7 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a);
if (destructGadgetOnExit) {
- d()->valueType()->metaType().destruct(d()->gadgetPtr());
+ d()->metaType().destruct(d()->gadgetPtr());
d()->setGadgetPtr(nullptr);
}
return true;
@@ -623,9 +605,9 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
RETURN_UNDEFINED();
QString result;
- if (!QMetaType::convert(w->d()->valueType()->metaType(), w->d()->gadgetPtr(),
+ if (!QMetaType::convert(w->d()->metaType(), w->d()->gadgetPtr(),
QMetaType(QMetaType::QString), &result)) {
- result = QString::fromUtf8(w->d()->valueType()->metaType().name()) + QLatin1Char('(');
+ result = QString::fromUtf8(w->d()->metaType().name()) + QLatin1Char('(');
const QMetaObject *mo = w->d()->metaObject();
const int propCount = mo->propertyCount();
for (int i = 0; i < propCount; ++i) {
@@ -662,7 +644,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *obj
if (!result.isValid())
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
- lookup->qgadgetLookup.ic = r->internalClass();
+ lookup->qgadgetLookup.ic.set(engine, r->internalClass());
// & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
lookup->qgadgetLookup.metaObject = quintptr(r->d()->metaObject()) + 1;
lookup->qgadgetLookup.metaType = result.propType().iface();
@@ -833,6 +815,12 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
QMetaProperty property = metaObject->property(pd.coreIndex());
Q_ASSERT(property.isValid());
+ if (value.isUndefined() && pd.isResettable()) {
+ property.resetOnGadget(reinterpret_cast<QObject *>(r->d()->gadgetPtr()));
+ if (heapObject)
+ r->d()->writeBack(pd.coreIndex());
+ return true;
+ }
QVariant v = QV4::ExecutionEngine::toVariant(value, property.metaType());
@@ -840,7 +828,7 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
v = v.toInt();
void *gadget = r->d()->gadgetPtr();
- property.writeOnGadget(gadget, v);
+ property.writeOnGadget(gadget, std::move(v));
if (heapObject)
r->d()->writeBack(pd.coreIndex());
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index 0c4cbb7dd3..5b3894a07f 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -41,11 +41,12 @@ namespace Heap {
DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) {
DECLARE_MARKOBJECTS(QQmlValueTypeWrapper);
- void init(const void *data, QQmlValueType *valueType, const QMetaObject *metaObject,
- Object *object, int property, Flags flags)
+ void init(
+ const void *data, QMetaType metaType, const QMetaObject *metaObject,
+ Object *object, int property, Flags flags)
{
ReferenceObject::init(object, property, flags);
- setValueType(valueType);
+ setMetaType(metaType);
setMetaObject(metaObject);
if (data)
setData(data);
@@ -55,10 +56,10 @@ DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) {
void destroy();
- QQmlValueType *valueType() const
+ QMetaType metaType() const
{
- Q_ASSERT(m_valueType != nullptr);
- return m_valueType;
+ Q_ASSERT(m_metaType != nullptr);
+ return QMetaType(m_metaType);
}
void setGadgetPtr(void *gadgetPtr) { m_gadgetPtr = gadgetPtr; }
@@ -66,7 +67,19 @@ DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) {
const QMetaObject *metaObject() const { return m_metaObject; }
- void setData(const void *data);
+ void setData(const void *data)
+ {
+ const QMetaType type = metaType();
+ void *gadget = gadgetPtr();
+ if (gadget) {
+ type.destruct(gadget);
+ } else {
+ gadget = ::operator new(type.sizeOf());
+ setGadgetPtr(gadget);
+ }
+ type.construct(gadget, data);
+ }
+
QVariant toVariant() const;
void *storagePointer();
@@ -77,14 +90,14 @@ DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) {
private:
void setMetaObject(const QMetaObject *metaObject) { m_metaObject = metaObject; }
- void setValueType(QQmlValueType *valueType)
+ void setMetaType(QMetaType metaType)
{
- Q_ASSERT(valueType != nullptr);
- m_valueType = valueType;
+ Q_ASSERT(metaType.isValid());
+ m_metaType = metaType.iface();
}
void *m_gadgetPtr;
- QQmlValueType *m_valueType;
+ const QtPrivate::QMetaTypeInterface *m_metaType;
const QMetaObject *m_metaObject;
};
@@ -107,12 +120,24 @@ public:
ExecutionEngine *engine, const void *, const QMetaObject *metaObject, QMetaType type);
QVariant toVariant() const;
+
+ template<typename ValueType>
+ ValueType *cast()
+ {
+ if (QMetaType::fromType<ValueType>() != d()->metaType())
+ return nullptr;
+ if (d()->isReference() && !readReferenceValue())
+ return nullptr;
+ return static_cast<ValueType *>(d()->gadgetPtr());
+ }
+
bool toGadget(void *data) const;
bool isEqual(const QVariant& value) const;
int typeId() const;
QMetaType type() const;
bool write(QObject *target, int propertyIndex) const;
bool readReferenceValue() const { return d()->readReference(); }
+ const QMetaObject *metaObject() const { return d()->metaObject(); }
QQmlPropertyData dataForPropertyKey(PropertyKey id) const;
diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h
index b4aae29a93..b8dfc28050 100644
--- a/src/qml/qml/qqmlvme_p.h
+++ b/src/qml/qml/qqmlvme_p.h
@@ -48,7 +48,7 @@ private:
std::atomic<bool> *runWhile = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlVME
+class Q_QML_EXPORT QQmlVME
{
public:
static void enableComponentComplete();
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index c863fa6647..5f3b6975ca 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -21,105 +21,131 @@
#include <private/qqmlpropertycachemethodarguments_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <QtCore/qsequentialiterable.h>
+
#include <climits> // for CHAR_BIT
QT_BEGIN_NAMESPACE
-class ResolvedList
+QQmlVMEResolvedList::QQmlVMEResolvedList(QQmlListProperty<QObject> *prop)
{
- Q_DISABLE_COPY_MOVE(ResolvedList)
+ // see QQmlVMEMetaObject::metaCall for how this was constructed
+ auto encodedIndex = quintptr(prop->data);
+ constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
+ quintptr inheritanceDepth = encodedIndex >> (usableBits / 2);
+ m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1);
+
+ // walk up to the correct meta object if necessary
+ auto mo = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(prop->object)->metaObject);
+ while (inheritanceDepth--)
+ mo = mo->parentVMEMetaObject();
+ m_metaObject = mo;
+ Q_ASSERT(m_metaObject);
+ Q_ASSERT(::strstr(m_metaObject->toDynamicMetaObject(prop->object)
+ ->property(m_metaObject->propOffset() + m_id)
+ .typeName(),
+ "QQmlListProperty"));
+ Q_ASSERT(m_metaObject->object == prop->object);
+
+ // readPropertyAsList() with checks transformed into Q_ASSERT
+ // and without allocation.
+ if (m_metaObject->propertyAndMethodStorage.isUndefined()
+ && m_metaObject->propertyAndMethodStorage.valueRef()) {
+ return;
+ }
-public:
- ResolvedList(QQmlListProperty<QObject> *prop)
- {
- // see QQmlVMEMetaObject::metaCall for how this was constructed
- auto encodedIndex = quintptr(prop->data);
- constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
- quintptr inheritanceDepth = encodedIndex >> (usableBits / 2);
- m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1);
-
- // walk up to the correct meta object if necessary
- auto mo = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(prop->object)->metaObject);
- while (inheritanceDepth--)
- mo = mo->parentVMEMetaObject();
- m_metaObject = mo;
- Q_ASSERT(m_metaObject);
- Q_ASSERT(::strstr(m_metaObject->toDynamicMetaObject(prop->object)->property(
- m_metaObject->propOffset() + m_id).typeName(), "QQmlListProperty") );
- Q_ASSERT(m_metaObject->object == prop->object);
-
- // readPropertyAsList() with checks transformed into Q_ASSERT
- // and without allocation.
- if (m_metaObject->propertyAndMethodStorage.isUndefined() &&
- m_metaObject->propertyAndMethodStorage.valueRef()) {
- return;
- }
+ if (auto *md = static_cast<QV4::MemberData *>(
+ m_metaObject->propertyAndMethodStorage.asManaged())) {
+ const QV4::Value *v = md->data() + m_id;
+ Q_ASSERT(v->as<QV4::Object>());
+ m_list = static_cast<QV4::Heap::Object *>(v->heapObject());
+ Q_ASSERT(m_list);
+ }
+}
- if (auto *md = static_cast<QV4::MemberData *>(
- m_metaObject->propertyAndMethodStorage.asManaged())) {
- const auto *v = (md->data() + m_id)->as<QV4::VariantObject>();
- Q_ASSERT(v);
- Q_ASSERT(v->d());
- QVariant &data = v->d()->data();
- Q_ASSERT(data.userType() == qMetaTypeId<QVector<QQmlGuard<QObject>>>());
- m_list = static_cast<QVector<QQmlGuard<QObject>> *>(data.data());
- Q_ASSERT(m_list);
- }
+void QQmlVMEResolvedList::append(QObject *o) const
+{
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::Heap::ArrayData *arrayData = m_list->arrayData;
+
+ const uint length = arrayData->length();
+ if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) {
+ scope.engine->throwRangeError(QLatin1String("Too many elements."));
+ return;
}
- ~ResolvedList() = default;
+ QV4::ScopedObject object(scope, m_list);
+ QV4::ArrayData::realloc(object, QV4::Heap::ArrayData::Simple, length + 1, false);
+ arrayData->vtable()->put(
+ object, length, QV4::QObjectWrapper::wrap(scope.engine, o));
+}
+
+QObject *QQmlVMEResolvedList::at(qsizetype i) const
+{
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::Scoped<QV4::QObjectWrapper> result(scope, m_list->arrayData->get(i));
+ return result ? result->object() : nullptr;
+}
- QQmlVMEMetaObject *metaObject() const { return m_metaObject; }
- QVector<QQmlGuard<QObject>> *list() const { return m_list; }
- quintptr id() const { return m_id; }
+void QQmlVMEResolvedList::replace(qsizetype i, QObject *o) const
+{
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::ScopedObject object(scope, m_list);
+ m_list->arrayData->vtable()->put(object, i, QV4::QObjectWrapper::wrap(scope.engine, o));
+}
- void activateSignal() const
- {
- m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()),
- nullptr);
- }
+QQmlVMEResolvedList::~QQmlVMEResolvedList() = default;
-private:
- QQmlVMEMetaObject *m_metaObject = nullptr;
- QVector<QQmlGuard<QObject>> *m_list = nullptr;
- quintptr m_id = 0;
-};
+void QQmlVMEResolvedList::activateSignal() const
+{
+ m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()), nullptr);
+}
-static void list_append(QQmlListProperty<QObject> *prop, QObject *o)
+void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o)
{
- const ResolvedList resolved(prop);
- resolved.list()->append(o);
+ const QQmlVMEResolvedList resolved(prop);
+ resolved.append(o);
resolved.activateSignal();
}
+void QQmlVMEMetaObject::list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o)
+{
+ QQmlVMEResolvedList(prop).append(o);
+}
+
static qsizetype list_count(QQmlListProperty<QObject> *prop)
{
- return ResolvedList(prop).list()->size();
+ return QQmlVMEResolvedList(prop).size();
}
static QObject *list_at(QQmlListProperty<QObject> *prop, qsizetype index)
{
- return ResolvedList(prop).list()->at(index);
+ return QQmlVMEResolvedList(prop).at(index);
}
-static void list_clear(QQmlListProperty<QObject> *prop)
+void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop)
{
- const ResolvedList resolved(prop);
- resolved.list()->clear();
+ const QQmlVMEResolvedList resolved(prop);
+ resolved.clear();
resolved.activateSignal();
}
+void QQmlVMEMetaObject::list_clear_nosignal(QQmlListProperty<QObject> *prop)
+{
+ QQmlVMEResolvedList(prop).clear();
+}
+
static void list_replace(QQmlListProperty<QObject> *prop, qsizetype index, QObject *o)
{
- const ResolvedList resolved(prop);
- resolved.list()->replace(index, o);
+ const QQmlVMEResolvedList resolved(prop);
+ resolved.replace(index, o);
resolved.activateSignal();
}
static void list_removeLast(QQmlListProperty<QObject> *prop)
{
- const ResolvedList resolved(prop);
- resolved.list()->removeLast();
+ const QQmlVMEResolvedList resolved(prop);
+ resolved.removeLast();
resolved.activateSignal();
}
@@ -248,6 +274,15 @@ QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject()
void QQmlInterceptorMetaObject::registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor)
{
+ for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
+ if (Q_UNLIKELY(vi->m_propertyIndex.coreIndex() == index.coreIndex())) {
+ qWarning() << "Attempting to set another interceptor on "
+ << object->metaObject()->className() << "property"
+ << object->metaObject()->property(index.coreIndex()).name()
+ << "- unsupported";
+ }
+ }
+
interceptor->m_propertyIndex = index;
interceptor->m_next = interceptors;
interceptors = interceptor;
@@ -335,7 +370,7 @@ bool QQmlInterceptorMetaObject::doIntercept(QMetaObject::Call c, int id, void **
// change the value soon. Such an animation needs to be canceled if the
// current value is explicitly set.
// So, we cannot return here if prevComponentValue == newComponentValue.
- valueType->writeOnGadget(valueProp, prevComponentValue);
+ valueType->writeOnGadget(valueProp, std::move(prevComponentValue));
valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
vi->write(newComponentValue);
@@ -395,6 +430,12 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine,
uint size = compiledObject->nProperties + compiledObject->nFunctions;
if (size) {
QV4::Heap::MemberData *data = QV4::MemberData::allocate(engine, size);
+ // we only have a weak reference below; if the VMEMetaObject is already marked
+ // (triggered by the allocate call above)
+ // we therefore might never mark the member data; consequently, mark it now
+ QV4::WriteBarrier::markCustom(engine, [data](QV4::MarkStack *ms) {
+ data->mark(ms);
+ });
propertyAndMethodStorage.set(engine, data);
std::fill(data->values.values, data->values.values + data->values.size, QV4::Encode::undefined());
}
@@ -583,6 +624,22 @@ QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) const
return v->d()->data().value<QDateTime>();
}
+#if QT_CONFIG(regularexpression)
+QRegularExpression QQmlVMEMetaObject::readPropertyAsRegularExpression(int id) const
+{
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
+ if (!md)
+ return QRegularExpression();
+
+ QV4::Scope scope(engine);
+ QV4::ScopedValue sv(scope, *(md->data() + id));
+ const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
+ if (!v || v->d()->data().userType() != QMetaType::QRegularExpression)
+ return QRegularExpression();
+ return v->d()->data().value<QRegularExpression>();
+}
+#endif
+
QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) const
{
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
@@ -625,20 +682,19 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) const
return wrapper->object();
}
-QVector<QQmlGuard<QObject>> *QQmlVMEMetaObject::readPropertyAsList(int id) const
+void QQmlVMEMetaObject::initPropertyAsList(int id) const
{
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
- return nullptr;
+ return;
QV4::Scope scope(engine);
- QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id));
- if (!v || v->d()->data().metaType() != QMetaType::fromType<QVector<QQmlGuard<QObject>>>()) {
- const QVector<QQmlGuard<QObject>> guards;
- v = engine->newVariantObject(QMetaType::fromType<QVector<QQmlGuard<QObject>>>(), &guards);
+ QV4::ScopedObject v(scope, *(md->data() + id));
+ if (!v) {
+ v = engine->newObject();
+ v->arrayCreate();
md->set(engine, id, v);
}
- return static_cast<QVector<QQmlGuard<QObject>> *>(v->d()->data().data());
}
QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const
@@ -678,7 +734,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
// if we reach this point, propertyCount must have been > 0, and thus compiledObject != nullptr
Q_ASSERT(compiledObject);
const QV4::CompiledData::Property &property = compiledObject->propertyTable()[id];
- const QV4::CompiledData::BuiltinType t = property.builtinType();
+ const QV4::CompiledData::CommonType t = property.commonType();
// the context can be null if accessing var properties from cpp after re-parenting an item.
QQmlEnginePrivate *ep = (ctxt.isNull() || ctxt->engine() == nullptr)
@@ -719,7 +775,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
quintptr encodedIndex = (inheritanceDepth << idBits) + id;
- readPropertyAsList(id); // Initializes if necessary
+ initPropertyAsList(id);
*static_cast<QQmlListProperty<QObject> *>(a[0])
= QQmlListProperty<QObject>(
object, reinterpret_cast<void *>(quintptr(encodedIndex)),
@@ -739,40 +795,48 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
} else {
switch (t) {
- case QV4::CompiledData::BuiltinType::Int:
+ case QV4::CompiledData::CommonType::Void:
+ break;
+ case QV4::CompiledData::CommonType::Int:
*reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
break;
- case QV4::CompiledData::BuiltinType::Bool:
+ case QV4::CompiledData::CommonType::Bool:
*reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
break;
- case QV4::CompiledData::BuiltinType::Real:
+ case QV4::CompiledData::CommonType::Real:
*reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
break;
- case QV4::CompiledData::BuiltinType::String:
+ case QV4::CompiledData::CommonType::String:
*reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
break;
- case QV4::CompiledData::BuiltinType::Url:
+ case QV4::CompiledData::CommonType::Url:
*reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id);
break;
- case QV4::CompiledData::BuiltinType::Date:
+ case QV4::CompiledData::CommonType::Date:
*reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id);
break;
- case QV4::CompiledData::BuiltinType::DateTime:
+ case QV4::CompiledData::CommonType::DateTime:
*reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id);
break;
- case QV4::CompiledData::BuiltinType::Rect:
+ case QV4::CompiledData::CommonType::RegExp:
+#if QT_CONFIG(regularexpression)
+ *reinterpret_cast<QRegularExpression *>(a[0])
+ = readPropertyAsRegularExpression(id);
+#endif
+ break;
+ case QV4::CompiledData::CommonType::Rect:
*reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id);
break;
- case QV4::CompiledData::BuiltinType::Size:
+ case QV4::CompiledData::CommonType::Size:
*reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
break;
- case QV4::CompiledData::BuiltinType::Point:
+ case QV4::CompiledData::CommonType::Point:
*reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id);
break;
- case QV4::CompiledData::BuiltinType::Time:
+ case QV4::CompiledData::CommonType::Time:
*reinterpret_cast<QTime *>(a[0]) = readPropertyAsTime(id);
break;
- case QV4::CompiledData::BuiltinType::Var:
+ case QV4::CompiledData::CommonType::Var:
if (ep) {
*reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
} else {
@@ -781,7 +845,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
*reinterpret_cast<QVariant *>(a[0]) = QVariant();
}
break;
- case QV4::CompiledData::BuiltinType::InvalidBuiltin:
+ case QV4::CompiledData::CommonType::Invalid:
if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
@@ -835,8 +899,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
needActivate = true;
}
} else {
- QV4::ScopedValue sequence(scope, QV4::SequencePrototype::fromData(
- engine, propType, a[0]));
+ if (const QQmlType type = QQmlMetaType::qmlListType(propType);
+ type.isSequentialContainer()) {
+ sequence = QV4::SequencePrototype::fromData(
+ engine, propType, type.listMetaSequence(), a[0]);
+ } else if (QSequentialIterable iterable;
+ QMetaType::convert(
+ propType, a[0],
+ QMetaType::fromType<QSequentialIterable>(),
+ &iterable)) {
+ sequence = QV4::SequencePrototype::fromData(
+ engine, propType, iterable.metaContainer(), a[0]);
+ } else {
+ sequence = QV4::Encode::undefined();
+ }
md->set(engine, id, sequence);
if (sequence->isUndefined()) {
qmlWarning(object)
@@ -850,55 +926,64 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
} else {
switch (t) {
- case QV4::CompiledData::BuiltinType::Int:
+ case QV4::CompiledData::CommonType::Void:
+ break;
+ case QV4::CompiledData::CommonType::Int:
needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
writeProperty(id, *reinterpret_cast<int *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Bool:
+ case QV4::CompiledData::CommonType::Bool:
needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
writeProperty(id, *reinterpret_cast<bool *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Real:
+ case QV4::CompiledData::CommonType::Real:
needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
writeProperty(id, *reinterpret_cast<double *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::String:
+ case QV4::CompiledData::CommonType::String:
needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
writeProperty(id, *reinterpret_cast<QString *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Url:
+ case QV4::CompiledData::CommonType::Url:
needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id);
writeProperty(id, *reinterpret_cast<QUrl *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Date:
+ case QV4::CompiledData::CommonType::Date:
needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id);
writeProperty(id, *reinterpret_cast<QDate *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::DateTime:
+ case QV4::CompiledData::CommonType::DateTime:
needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id);
writeProperty(id, *reinterpret_cast<QDateTime *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Rect:
+ case QV4::CompiledData::CommonType::RegExp:
+#if QT_CONFIG(regularexpression)
+ needActivate = *reinterpret_cast<QRegularExpression *>(a[0])
+ != readPropertyAsRegularExpression(id);
+ writeProperty(id, *reinterpret_cast<QRegularExpression *>(a[0]));
+#endif
+ break;
+ case QV4::CompiledData::CommonType::Rect:
needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id);
writeProperty(id, *reinterpret_cast<QRectF *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Size:
+ case QV4::CompiledData::CommonType::Size:
needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Point:
+ case QV4::CompiledData::CommonType::Point:
needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id);
writeProperty(id, *reinterpret_cast<QPointF *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Time:
+ case QV4::CompiledData::CommonType::Time:
needActivate = *reinterpret_cast<QTime *>(a[0]) != readPropertyAsTime(id);
writeProperty(id, *reinterpret_cast<QTime *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::Var:
+ case QV4::CompiledData::CommonType::Var:
if (ep)
writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
break;
- case QV4::CompiledData::BuiltinType::InvalidBuiltin:
+ case QV4::CompiledData::CommonType::Invalid:
if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
@@ -980,15 +1065,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
int coreIndex = encodedIndex.coreIndex();
const int valueTypePropertyIndex = encodedIndex.valueTypeIndex();
- // Remove binding (if any) on write
- if(c == QMetaObject::WriteProperty) {
- int flags = *reinterpret_cast<int*>(a[3]);
- if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) {
- QQmlData *targetData = QQmlData::get(target);
- if (targetData && targetData->hasBindingBit(coreIndex))
- QQmlPropertyPrivate::removeBinding(target, encodedIndex);
+ const auto removePendingBinding
+ = [c, a](QObject *target, int coreIndex, QQmlPropertyIndex encodedIndex) {
+ // Remove binding (if any) on write
+ if (c == QMetaObject::WriteProperty) {
+ int flags = *reinterpret_cast<int*>(a[3]);
+ if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) {
+ QQmlData *targetData = QQmlData::get(target);
+ if (targetData && targetData->hasBindingBit(coreIndex)) {
+ QQmlPropertyPrivate::removeBinding(target, encodedIndex);
+ targetData->clearBindingBit(coreIndex);
+ }
+ }
}
- }
+ };
if (valueTypePropertyIndex != -1) {
if (!targetDData->propertyCache)
@@ -998,6 +1088,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
ctxt->engine(), pd->propType());
if (valueType) {
+ removePendingBinding(target, coreIndex, encodedIndex);
valueType->read(target, coreIndex);
int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
@@ -1010,10 +1101,14 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
// deep alias
void *argv[1] = { &target };
QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
+ removePendingBinding(
+ target, valueTypePropertyIndex,
+ QQmlPropertyIndex(valueTypePropertyIndex));
return QMetaObject::metacall(target, c, valueTypePropertyIndex, a);
}
} else {
+ removePendingBinding(target, coreIndex, encodedIndex);
return QMetaObject::metacall(target, c, coreIndex, a);
}
@@ -1067,15 +1162,11 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (arguments && arguments->names) {
const quint32 parameterCount = arguments->names->size();
Q_ASSERT(parameterCount == function->formalParameterCount());
- if (void *result = a[0])
- arguments->types[0].destruct(result);
- function->call(nullptr, a, arguments->types, parameterCount);
+ function->call(object, a, arguments->types, parameterCount);
} else {
Q_ASSERT(function->formalParameterCount() == 0);
const QMetaType returnType = methodData->propType();
- if (void *result = a[0])
- returnType.destruct(result);
- function->call(nullptr, a, &returnType, 0);
+ function->call(object, a, &returnType, 0);
}
if (scope.hasException()) {
@@ -1113,7 +1204,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::method(int index) const
QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) const
{
- Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var);
+ Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var);
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
@@ -1138,7 +1229,7 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const
void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
{
- Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var);
+ Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var);
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
@@ -1199,7 +1290,7 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value)
{
- if (compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var) {
+ if (compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var) {
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return;
@@ -1303,7 +1394,7 @@ void QQmlVMEMetaObject::setVMEProperty(int index, const QV4::Value &v)
void QQmlVMEMetaObject::ensureQObjectWrapper()
{
Q_ASSERT(cache);
- QV4::QObjectWrapper::wrap(engine, object);
+ QV4::QObjectWrapper::ensureWrapper(engine, object);
}
void QQmlVMEMetaObject::mark(QV4::MarkStack *markStack)
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 1b9a122b1f..d37c20a41b 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -16,27 +16,73 @@
// We mean it.
//
-#include <QtCore/QMetaObject>
-#include <QtCore/QBitArray>
-#include <QtCore/QPair>
-#include <QtCore/QDate>
-#include <QtCore/qlist.h>
-#include <QtCore/qdebug.h>
-
-#include <private/qobject_p.h>
-
-#include "qqmlguard_p.h"
-
-#include <private/qqmlguardedcontextdata_p.h>
#include <private/qbipointer_p.h>
-
+#include <private/qqmlguard_p.h>
+#include <private/qqmlguardedcontextdata_p.h>
+#include <private/qqmlpropertyvalueinterceptor_p.h>
#include <private/qv4object_p.h>
#include <private/qv4value_p.h>
-#include <private/qqmlpropertyvalueinterceptor_p.h>
+
+#include <QtCore/private/qobject_p.h>
+
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
+
+#include <QtCore/qbitarray.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qpair.h>
QT_BEGIN_NAMESPACE
class QQmlVMEMetaObject;
+class QQmlVMEResolvedList
+{
+ Q_DISABLE_COPY_MOVE(QQmlVMEResolvedList)
+
+public:
+ QQmlVMEResolvedList(QQmlListProperty<QObject> *prop);
+ ~QQmlVMEResolvedList();
+
+ QQmlVMEMetaObject *metaObject() const { return m_metaObject; }
+ QV4::Heap::Object *list() const { return m_list; }
+ quintptr id() const { return m_id; }
+
+ void append(QObject *o) const;
+ void replace(qsizetype i, QObject *o) const;
+ QObject *at(qsizetype i) const;
+
+ qsizetype size() const { return m_list->arrayData->length(); }
+
+ void clear() const
+ {
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::ScopedObject object(scope, m_list);
+ m_list->arrayData->vtable()->truncate(object, 0);
+ }
+
+ void removeLast() const
+ {
+ const uint length = m_list->arrayData->length();
+ if (length == 0)
+ return;
+
+ QV4::Scope scope(m_list->internalClass->engine);
+ QV4::ScopedObject object(scope, m_list);
+ m_list->arrayData->vtable()->truncate(object, length - 1);
+ }
+
+ void activateSignal() const;
+
+private:
+ QQmlVMEMetaObject *m_metaObject = nullptr;
+ QV4::Heap::Object *m_list = nullptr;
+ quintptr m_id = 0;
+};
+
class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject>
{
public:
@@ -52,7 +98,7 @@ private:
};
-class Q_QML_PRIVATE_EXPORT QQmlInterceptorMetaObject : public QDynamicMetaObjectData
+class Q_QML_EXPORT QQmlInterceptorMetaObject : public QDynamicMetaObjectData
{
public:
QQmlInterceptorMetaObject(QObject *obj, const QQmlPropertyCache::ConstPtr &cache);
@@ -127,7 +173,7 @@ inline QQmlInterceptorMetaObject *QQmlInterceptorMetaObject::get(QObject *obj)
}
class QQmlVMEMetaObjectEndpoint;
-class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
+class Q_QML_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
{
public:
QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj,
@@ -149,6 +195,11 @@ public:
static QQmlVMEMetaObject *getForMethod(QObject *o, int coreIndex);
static QQmlVMEMetaObject *getForSignal(QObject *o, int coreIndex);
+ static void list_append(QQmlListProperty<QObject> *prop, QObject *o);
+ static void list_clear(QQmlListProperty<QObject> *prop);
+ static void list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o);
+ static void list_clear_nosignal(QQmlListProperty<QObject> *prop);
+
protected:
int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) override;
@@ -176,9 +227,14 @@ public:
QDate readPropertyAsDate(int id) const;
QTime readPropertyAsTime(int id) const;
QDateTime readPropertyAsDateTime(int id) const;
+
+#if QT_CONFIG(regularexpression)
+ QRegularExpression readPropertyAsRegularExpression(int id) const;
+#endif
+
QRectF readPropertyAsRectF(int id) const;
QObject *readPropertyAsQObject(int id) const;
- QVector<QQmlGuard<QObject> > *readPropertyAsList(int id) const;
+ void initPropertyAsList(int id) const;
void writeProperty(int id, int v);
void writeProperty(int id, bool v);
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index cd4ef6e42d..c5d18860db 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -19,6 +19,8 @@
#include <QtQml/qjsengine.h>
#include <QtQml/qqmlfile.h>
#include <QtNetwork/qnetworkreply.h>
+
+#include <QtCore/qpointer.h>
#include <QtCore/qstringconverter.h>
#include <QtCore/qxmlstream.h>
#include <QtCore/qstack.h>
@@ -116,11 +118,12 @@ public:
QList<NodeImpl *> attributes;
};
-class DocumentImpl : public QQmlRefCount, public NodeImpl
+class DocumentImpl final : public QQmlRefCounted<DocumentImpl>, public NodeImpl
{
+ using Base1 = QQmlRefCounted<DocumentImpl>;
public:
DocumentImpl() : root(nullptr) { type = Document; }
- virtual ~DocumentImpl() {
+ ~DocumentImpl() override {
delete root;
}
@@ -130,8 +133,8 @@ public:
NodeImpl *root;
- void addref() { QQmlRefCount::addref(); }
- void release() { QQmlRefCount::release(); }
+ void addref() { Base1::addref(); }
+ void release() { Base1::release(); }
};
namespace Heap {
@@ -998,9 +1001,15 @@ public:
QString responseBody();
const QByteArray & rawResponseBody() const;
bool receivedXml() const;
+ QUrl url() const;
const QString & responseType() const;
void setResponseType(const QString &);
+ void setOverrideMimeType(QStringView mimeType) { m_overrideMime = mimeType.toUtf8(); }
+ void setOverrideCharset(QStringView charset) { m_overrideCharset = charset.toUtf8(); }
+
+ const QByteArray mimeType() const;
+ const QByteArray charset() const;
QV4::ReturnedValue jsonResponseBody(QV4::ExecutionEngine*);
QV4::ReturnedValue xmlResponseBody(QV4::ExecutionEngine*);
@@ -1029,6 +1038,9 @@ private:
bool m_gotXml;
QByteArray m_mime;
QByteArray m_charset;
+ QByteArray m_overrideMime;
+ QByteArray m_overrideCharset;
+
QStringDecoder findTextDecoder() const;
void readEncoding();
@@ -1057,8 +1069,6 @@ private:
QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4)
: m_state(Unsent), m_errorFlag(false), m_sendFlag(false)
, m_redirectCount(0), m_gotXml(false), m_network(nullptr), m_nam(manager)
- , m_responseType()
- , m_parsedDocument()
{
m_wasConstructedWithQmlContext = !v4->callingQmlContext().isNull();
}
@@ -1162,6 +1172,7 @@ void QQmlXMLHttpRequest::fillHeadersList()
void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
{
+ m_url = url;
QNetworkRequest request = m_request;
if (QQmlFile::isLocalFile(url)) {
@@ -1453,7 +1464,8 @@ void QQmlXMLHttpRequest::readEncoding()
}
}
- if (m_mime.isEmpty() || m_mime == "text/xml" || m_mime == "application/xml" || m_mime.endsWith("+xml"))
+ const auto mime = mimeType();
+ if (mime.isEmpty() || mime == "text/xml" || mime == "application/xml" || mime.endsWith("+xml"))
m_gotXml = true;
}
@@ -1462,6 +1474,25 @@ bool QQmlXMLHttpRequest::receivedXml() const
return m_gotXml;
}
+QUrl QQmlXMLHttpRequest::url() const
+{
+ return m_url;
+}
+
+const QByteArray QQmlXMLHttpRequest::mimeType() const
+{
+ // Final MIME type is the override MIME type unless that is null in which
+ // case it is the response MIME type.
+ return m_overrideMime.isEmpty() ? m_mime : m_overrideMime;
+}
+
+const QByteArray QQmlXMLHttpRequest::charset() const
+{
+ // Final charset is the override charset unless that is null in which case
+ // it is the response charset.
+ return m_overrideCharset.isEmpty() ? m_charset : m_overrideCharset;
+}
+
const QString & QQmlXMLHttpRequest::responseType() const
{
return m_responseType;
@@ -1503,8 +1534,8 @@ QStringDecoder QQmlXMLHttpRequest::findTextDecoder() const
{
QStringDecoder decoder;
- if (!m_charset.isEmpty())
- decoder = QStringDecoder(m_charset);
+ if (!charset().isEmpty())
+ decoder = QStringDecoder(charset());
if (!decoder.isValid() && m_gotXml) {
QXmlStreamReader reader(m_responseEntityBody);
@@ -1512,11 +1543,8 @@ QStringDecoder QQmlXMLHttpRequest::findTextDecoder() const
decoder = QStringDecoder(reader.documentEncoding().toString().toUtf8());
}
- if (!decoder.isValid() && m_mime == "text/html") {
- auto encoding = QStringConverter::encodingForHtml(m_responseEntityBody);
- if (encoding)
- decoder = QStringDecoder(*encoding);
- }
+ if (!decoder.isValid() && mimeType() == "text/html")
+ decoder = QStringDecoder::decoderForHtml(m_responseEntityBody);
if (!decoder.isValid()) {
auto encoding = QStringConverter::encodingForData(m_responseEntityBody);
@@ -1632,6 +1660,7 @@ struct QQmlXMLHttpRequestWrapper : public Object
V4_NEEDS_DESTROY
};
+// https://xhr.spec.whatwg.org/
struct QQmlXMLHttpRequestCtor : public FunctionObject
{
V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
@@ -1660,6 +1689,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
static ReturnedValue method_abort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_getAllResponseHeaders(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_readyState(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_status(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
@@ -1669,6 +1699,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
static ReturnedValue method_get_response(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
};
}
@@ -1709,6 +1740,7 @@ void QQmlXMLHttpRequestCtor::setupProto()
p->defineDefaultProperty(QStringLiteral("abort"), method_abort);
p->defineDefaultProperty(QStringLiteral("getResponseHeader"), method_getResponseHeader);
p->defineDefaultProperty(QStringLiteral("getAllResponseHeaders"), method_getAllResponseHeaders);
+ p->defineDefaultProperty(QStringLiteral("overrideMimeType"), method_overrideMimeType);
// Read-only properties
p->defineAccessorProperty(QStringLiteral("readyState"), method_get_readyState, nullptr);
@@ -1717,6 +1749,7 @@ void QQmlXMLHttpRequestCtor::setupProto()
p->defineAccessorProperty(QStringLiteral("responseText"),method_get_responseText, nullptr);
p->defineAccessorProperty(QStringLiteral("responseXML"),method_get_responseXML, nullptr);
p->defineAccessorProperty(QStringLiteral("response"),method_get_response, nullptr);
+ p->defineAccessorProperty(QStringLiteral("responseURL"),method_get_responseURL, nullptr);
// Read-write properties
p->defineAccessorProperty(QStringLiteral("responseType"), method_get_responseType, method_set_responseType);
@@ -2042,6 +2075,71 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(const FunctionObje
return Encode::undefined();
}
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
+ if (!w)
+ V4THROW_REFERENCE("Not an XMLHttpRequest object");
+ QQmlXMLHttpRequest *r = w->d()->request;
+
+ if (r->readyState() != QQmlXMLHttpRequest::Loading &&
+ r->readyState() != QQmlXMLHttpRequest::Done) {
+ return Encode(scope.engine->newString(QString()));
+ } else {
+ QUrl url = r->url();
+ url.setFragment(QString());
+ return Encode(scope.engine->newString(url.toString()));
+ }
+}
+
+ReturnedValue QQmlXMLHttpRequestCtor::method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
+ if (!w)
+ V4THROW_REFERENCE("Not an XMLHttpRequest object");
+ QQmlXMLHttpRequest *r = w->d()->request;
+
+ if (argc != 1)
+ THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
+
+ // If state is loading or done, throw an InvalidStateError exception.
+ if (r->readyState() == QQmlXMLHttpRequest::Loading ||
+ r->readyState() == QQmlXMLHttpRequest::Done)
+ THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
+
+ // Set override MIME type to `application/octet-stream`.
+ r->setOverrideMimeType(QStringLiteral("application/octet-stream"));
+ const auto parts = argv[0].toQStringNoThrow().split(QLatin1Char(';'));
+ const auto type = parts.at(0).trimmed();
+
+ const auto mimeInvalidCharacter = [](QChar uni) {
+ if (uni.unicode() > 127) // Only accept ASCII
+ return true;
+ const char ch = char(uni.unicode());
+ return !(ch == '-' || ch == '/' || isAsciiLetterOrNumber(ch));
+ };
+
+ // If mime is a parsable MIME type, ...
+ if (type.count(QLatin1Char('/')) == 1
+ && std::find_if(type.begin(), type.end(), mimeInvalidCharacter) == type.end()) {
+ // ... then set override MIME type to its MIME type portion.
+ r->setOverrideMimeType(type);
+ }
+ for (const auto &part : parts) {
+ const QLatin1String charset("charset=");
+ // If override MIME type has a `charset` parameter, ...
+ if (part.trimmed().startsWith(charset)) {
+ // ... then set override charset to its value.
+ const int offset(part.indexOf(charset) + charset.size());
+ r->setOverrideCharset(part.sliced(offset).trimmed());
+ }
+ }
+
+ return Encode::undefined();
+}
+
void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d)
{
QQmlXMLHttpRequestData *data = (QQmlXMLHttpRequestData *)d;
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index 112c7d12d0..e6a5691d3d 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -218,7 +218,7 @@ bool QQmlDirParser::parse(const QString &source)
continue;
} else {
reportError(lineNumber, 0,
- QStringLiteral("only optional imports can have a a defaultl, "
+ QStringLiteral("only optional imports can have a default, "
"not %1.")
.arg(sections[1]));
}
@@ -233,14 +233,28 @@ bool QQmlDirParser::parse(const QString &source)
_classNames.append(sections[1]);
} else if (sections[0] == QLatin1String("internal")) {
- if (sectionCount != 3) {
+ if (sectionCount == 3) {
+ Component entry(sections[1], sections[2], QTypeRevision());
+ entry.internal = true;
+ _components.insert(entry.typeName, entry);
+ } else if (sectionCount == 4) {
+ const QTypeRevision version = parseVersion(sections[2]);
+ if (version.isValid()) {
+ Component entry(sections[1], sections[3], version);
+ entry.internal = true;
+ _components.insert(entry.typeName, entry);
+ } else {
+ reportError(lineNumber, 0,
+ QStringLiteral("invalid version %1, expected <major>.<minor>")
+ .arg(sections[2]));
+ continue;
+ }
+ } else {
reportError(lineNumber, 0,
- QStringLiteral("internal types require 2 arguments, but %1 were provided").arg(sectionCount - 1));
+ QStringLiteral("internal types require 2 or 3 arguments, "
+ "but %1 were provided").arg(sectionCount - 1));
continue;
}
- Component entry(sections[1], sections[2], QTypeRevision());
- entry.internal = true;
- _components.insert(entry.typeName, entry);
} else if (sections[0] == QLatin1String("singleton")) {
if (sectionCount < 3 || sectionCount > 4) {
reportError(lineNumber, 0,
@@ -362,6 +376,129 @@ bool QQmlDirParser::parse(const QString &source)
return hasError();
}
+/* removes all file selector occurrences in path
+ firstPlus is the position of the initial '+' in the path
+ which we always have as we check for '+' to decide whether
+ we need to do some work at all
+*/
+static QString pathWithoutFileSelectors(QString path, // we want a copy of path
+ qsizetype firstPlus)
+{
+ do {
+ Q_ASSERT(path.at(firstPlus) == u'+');
+ const auto eos = path.size();
+ qsizetype terminatingSlashPos = firstPlus + 1;
+ while (terminatingSlashPos != eos && path.at(terminatingSlashPos) != u'/')
+ ++terminatingSlashPos;
+ path.remove(firstPlus, terminatingSlashPos - firstPlus + 1);
+ firstPlus = path.indexOf(u'+', firstPlus);
+ } while (firstPlus != -1);
+ return path;
+}
+
+static bool canDisambiguate(
+ const QString &fileName1, const QString &fileName2, QString *correctedFileName)
+{
+ // If the entries are exactly the same we can delete one without losing anything.
+ if (fileName1 == fileName2)
+ return true;
+
+ // If we detect conflicting paths, we check if they agree when we remove anything
+ // looking like a file selector.
+
+ // ugly heuristic to deal with file selectors
+ const qsizetype file2PotentialFileSelectorPos = fileName2.indexOf(u'+');
+ const bool file2MightHaveFileSelector = file2PotentialFileSelectorPos != -1;
+
+ if (const qsizetype fileSelectorPos1 = fileName1.indexOf(u'+'); fileSelectorPos1 != -1) {
+ // existing entry was file selector entry, fix it up
+ // it could also be the case that _both_ are using file selectors
+ const QString baseName = file2MightHaveFileSelector
+ ? pathWithoutFileSelectors(fileName2, file2PotentialFileSelectorPos)
+ : fileName2;
+
+ if (pathWithoutFileSelectors(fileName1, fileSelectorPos1) != baseName)
+ return false;
+
+ *correctedFileName = baseName;
+ return true;
+ }
+
+ // new entry contains file selector (and we know that fileName1 did not)
+ if (file2MightHaveFileSelector
+ && pathWithoutFileSelectors(fileName2, file2PotentialFileSelectorPos) == fileName1) {
+ *correctedFileName = fileName1;
+ return true;
+ }
+
+ return false;
+}
+
+static void disambiguateFileSelectedComponents(QQmlDirComponents *components)
+{
+ using ConstIterator = QQmlDirComponents::const_iterator;
+
+ // end iterator may get invalidated by the erasing below.
+ // Therefore, refetch it on each iteration.
+ for (ConstIterator cit = components->constBegin(); cit != components->constEnd();) {
+
+ // We can erase and re-assign cit if we immediately forget cit2.
+ // But we cannot erase cit2 without potentially invalidating cit.
+
+ bool doErase = false;
+ const ConstIterator cend = components->constEnd();
+ for (ConstIterator cit2 = ++ConstIterator(cit); cit2 != cend; ++cit2) {
+ if (cit2.key() != cit.key())
+ break;
+
+ Q_ASSERT(cit2->typeName == cit->typeName);
+
+ if (cit2->version != cit->version
+ || cit2->internal != cit->internal
+ || cit2->singleton != cit->singleton) {
+ continue;
+ }
+
+ // The two components may differ only by fileName now.
+
+ if (canDisambiguate(cit->fileName, cit2->fileName, &(cit2->fileName))) {
+ doErase = true;
+ break;
+ }
+ }
+
+ if (doErase)
+ cit = components->erase(cit);
+ else
+ ++cit;
+ }
+}
+
+static void disambiguateFileSelectedScripts(QQmlDirScripts *scripts)
+{
+ using Iterator = QQmlDirScripts::iterator;
+
+ Iterator send = scripts->end();
+
+ for (Iterator sit = scripts->begin(); sit != send; ++sit) {
+ send = std::remove_if(++Iterator(sit), send, [sit](const QQmlDirParser::Script &script2) {
+ if (sit->nameSpace != script2.nameSpace || sit->version != script2.version)
+ return false;
+
+ // The two scripts may differ only by fileName now.
+ return canDisambiguate(sit->fileName, script2.fileName, &(sit->fileName));
+ });
+ }
+
+ scripts->erase(send, scripts->end());
+}
+
+void QQmlDirParser::disambiguateFileSelectors()
+{
+ disambiguateFileSelectedComponents(&_components);
+ disambiguateFileSelectedScripts(&_scripts);
+}
+
void QQmlDirParser::reportError(quint16 line, quint16 column, const QString &description)
{
QQmlJS::DiagnosticMessage error;
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index 9a04997048..deef8f2dcf 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -25,11 +25,12 @@
QT_BEGIN_NAMESPACE
class QQmlEngine;
-class Q_QML_COMPILER_PRIVATE_EXPORT QQmlDirParser
+class Q_QML_COMPILER_EXPORT QQmlDirParser
{
public:
void clear();
bool parse(const QString &source);
+ void disambiguateFileSelectors();
bool hasError() const { return !_errors.isEmpty(); }
void setError(const QQmlJS::DiagnosticMessage &);
diff --git a/src/qml/qmldirparser/qqmlimportresolver_p.h b/src/qml/qmldirparser/qqmlimportresolver_p.h
index d44af11d90..1fa85e67b7 100644
--- a/src/qml/qmldirparser/qqmlimportresolver_p.h
+++ b/src/qml/qmldirparser/qqmlimportresolver_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
-Q_QML_COMPILER_PRIVATE_EXPORT QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths,
+Q_QML_COMPILER_EXPORT QStringList qQmlResolveImportPaths(QStringView uri, const QStringList &basePaths,
QTypeRevision version);
QT_END_NAMESPACE
diff --git a/src/qml/qmltc/qqmltcobjectcreationhelper.cpp b/src/qml/qmltc/qqmltcobjectcreationhelper.cpp
index db8517ca70..bab86de1b9 100644
--- a/src/qml/qmltc/qqmltcobjectcreationhelper.cpp
+++ b/src/qml/qmltc/qqmltcobjectcreationhelper.cpp
@@ -21,31 +21,31 @@ void qmltcCreateDynamicMetaObject(QObject *object, const QmltcTypeData &data)
// tie qmlTypePrivate destruction to objects's destruction. the type's
// content is not needed once the associated object is deleted
- QObject::connect(object, &QObject::destroyed,
- [qmlTypePrivate](QObject *) { qmlTypePrivate->release(); });
+ QObject::connect(object, &QObject::destroyed, object,
+ [qmlTypePrivate](QObject *) { qmlTypePrivate->release(); },
+ Qt::DirectConnection);
// initialize QQmlType::QQmlCppTypeData
Q_ASSERT(data.regType == QQmlType::CppType);
- qmlTypePrivate->extraData.cd->allocationSize = data.allocationSize;
- qmlTypePrivate->extraData.cd->newFunc = nullptr;
- qmlTypePrivate->extraData.cd->userdata = nullptr;
- qmlTypePrivate->extraData.cd->noCreationReason =
+ qmlTypePrivate->extraData.cppTypeData->allocationSize = data.allocationSize;
+ qmlTypePrivate->extraData.cppTypeData->newFunc = nullptr;
+ qmlTypePrivate->extraData.cppTypeData->userdata = nullptr;
+ qmlTypePrivate->extraData.cppTypeData->noCreationReason =
QStringLiteral("Qmltc-compiled type is not creatable via QQmlType");
- qmlTypePrivate->extraData.cd->createValueTypeFunc = nullptr;
- qmlTypePrivate->extraData.cd->parserStatusCast = -1;
- qmlTypePrivate->extraData.cd->extFunc = nullptr;
- qmlTypePrivate->extraData.cd->extMetaObject = nullptr;
- qmlTypePrivate->extraData.cd->customParser = nullptr;
- qmlTypePrivate->extraData.cd->attachedPropertiesFunc = nullptr;
- qmlTypePrivate->extraData.cd->attachedPropertiesType = nullptr;
- qmlTypePrivate->extraData.cd->propertyValueSourceCast = -1;
- qmlTypePrivate->extraData.cd->propertyValueInterceptorCast = -1;
- qmlTypePrivate->extraData.cd->finalizerCast = -1;
- qmlTypePrivate->extraData.cd->registerEnumClassesUnscoped = false;
- qmlTypePrivate->extraData.cd->registerEnumsFromRelatedTypes = false;
+ qmlTypePrivate->extraData.cppTypeData->createValueTypeFunc = nullptr;
+ qmlTypePrivate->extraData.cppTypeData->parserStatusCast = -1;
+ qmlTypePrivate->extraData.cppTypeData->extFunc = nullptr;
+ qmlTypePrivate->extraData.cppTypeData->extMetaObject = nullptr;
+ qmlTypePrivate->extraData.cppTypeData->customParser = nullptr;
+ qmlTypePrivate->extraData.cppTypeData->attachedPropertiesFunc = nullptr;
+ qmlTypePrivate->extraData.cppTypeData->attachedPropertiesType = nullptr;
+ qmlTypePrivate->extraData.cppTypeData->propertyValueSourceCast = -1;
+ qmlTypePrivate->extraData.cppTypeData->propertyValueInterceptorCast = -1;
+ qmlTypePrivate->extraData.cppTypeData->finalizerCast = -1;
+ qmlTypePrivate->extraData.cppTypeData->registerEnumClassesUnscoped = false;
+ qmlTypePrivate->extraData.cppTypeData->registerEnumsFromRelatedTypes = false;
qmlTypePrivate->baseMetaObject = data.metaObject;
- qmlTypePrivate->init();
QQmlType qmlType(qmlTypePrivate);
Q_ASSERT(qmlType.isValid());
diff --git a/src/qml/qmltc/qqmltcobjectcreationhelper_p.h b/src/qml/qmltc/qqmltcobjectcreationhelper_p.h
index ae4633ac43..fe0cdcfed2 100644
--- a/src/qml/qmltc/qqmltcobjectcreationhelper_p.h
+++ b/src/qml/qmltc/qqmltcobjectcreationhelper_p.h
@@ -118,7 +118,7 @@ struct QmltcTypeData
}
};
-Q_QML_PRIVATE_EXPORT void qmltcCreateDynamicMetaObject(QObject *object, const QmltcTypeData &data);
+Q_QML_EXPORT void qmltcCreateDynamicMetaObject(QObject *object, const QmltcTypeData &data);
QT_END_NAMESPACE
diff --git a/src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h b/src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h
index 0f2e6a4998..a1c60012bc 100644
--- a/src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h
+++ b/src/qml/qmltc/supportlibrary/qqmlcppbinding_p.h
@@ -32,7 +32,7 @@
QT_BEGIN_NAMESPACE
-struct Q_QML_PRIVATE_EXPORT QQmlCppBinding
+struct Q_QML_EXPORT QQmlCppBinding
{
// TODO: this might instead be put into the QQmlEngine or QQmlAnyBinding?
static QUntypedPropertyBinding
diff --git a/src/qml/qmltc/supportlibrary/qqmlcpponassignment_p.h b/src/qml/qmltc/supportlibrary/qqmlcpponassignment_p.h
index 9c854430dc..cd8014895f 100644
--- a/src/qml/qmltc/supportlibrary/qqmlcpponassignment_p.h
+++ b/src/qml/qmltc/supportlibrary/qqmlcpponassignment_p.h
@@ -30,7 +30,7 @@ QT_BEGIN_NAMESPACE
(unintentionally?). This API allows to avoid manual casts to base types as
the C++ compiler would implicitly cast derived classes in this case.
*/
-struct Q_QML_PRIVATE_EXPORT QQmlCppOnAssignmentHelper
+struct Q_QML_EXPORT QQmlCppOnAssignmentHelper
{
// TODO: in theory, this API might just accept QObject * and int that would
// give the QMetaProperty. using the meta property, one could create
diff --git a/src/qml/qqmlbuiltins_p.h b/src/qml/qqmlbuiltins_p.h
new file mode 100644
index 0000000000..187e11d5da
--- /dev/null
+++ b/src/qml/qqmlbuiltins_p.h
@@ -0,0 +1,407 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLBUILTINS_H
+#define QQMLBUILTINS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// QmlBuiltins does not link QtQml - rather the other way around. Still, we can use the QtQml
+// headers here. This works because we explicitly include the QtQml include directories in the
+// manual moc call.
+#include <private/qqmlcomponentattached_p.h>
+#include <QtQml/qjsvalue.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlscriptstring.h>
+
+#include <QtQmlIntegration/qqmlintegration.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qtmetamacros.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvariantmap.h>
+#include <QtCore/qtypes.h>
+#include <QtCore/qchar.h>
+#include <QtCore/qjsonobject.h>
+
+#include <climits>
+
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// moc doesn't do 64bit constants, so we have to determine the size of qsizetype indirectly.
+// We assume that qsizetype is always the same size as a pointer. I haven't seen a platform
+// where this is not the case.
+// Furthermore moc is wrong about pretty much everything on 64bit windows. We need to hardcode
+// the size there.
+// Likewise, we also have to determine the size of long and ulong indirectly.
+
+#if defined(Q_OS_WIN64)
+
+static_assert(sizeof(long) == 4);
+#define QML_LONG_IS_32BIT
+static_assert(sizeof(qsizetype) == 8);
+#define QML_SIZE_IS_64BIT
+
+#elif QT_POINTER_SIZE == 4
+
+static_assert(sizeof(long) == 4);
+#define QML_LONG_IS_32BIT
+static_assert(sizeof(qsizetype) == 4);
+#define QML_SIZE_IS_32BIT
+
+#else
+
+static_assert(sizeof(long) == 8);
+#define QML_LONG_IS_64BIT
+static_assert(sizeof(qsizetype) == 8);
+#define QML_SIZE_IS_64BIT
+
+#endif
+
+#define QML_EXTENDED_JAVASCRIPT(EXTENDED_TYPE) \
+ Q_CLASSINFO("QML.Extended", #EXTENDED_TYPE) \
+ Q_CLASSINFO("QML.ExtensionIsJavaScript", "true")
+
+template<typename A> struct QQmlPrimitiveAliasFriend {};
+
+#define QML_PRIMITIVE_ALIAS(PRIMITIVE_ALIAS) \
+ Q_CLASSINFO("QML.PrimitiveAlias", #PRIMITIVE_ALIAS) \
+ friend QQmlPrimitiveAliasFriend<PRIMITIVE_ALIAS>;
+
+struct QQmlVoidForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(void)
+ QML_EXTENDED_JAVASCRIPT(undefined)
+#if !QT_CONFIG(regularexpression)
+ QML_VALUE_TYPE(regexp)
+#endif
+ QML_FOREIGN(void)
+};
+
+struct QQmlVarForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(var)
+ QML_VALUE_TYPE(variant)
+ QML_FOREIGN(QVariant)
+ QML_EXTENDED(QQmlVarForeign)
+};
+
+struct QQmlQtObjectForeign
+{
+ Q_GADGET
+ QML_NAMED_ELEMENT(QtObject)
+ QML_EXTENDED_JAVASCRIPT(Object)
+ QML_FOREIGN(QObject)
+ Q_CLASSINFO("QML.Root", "true")
+};
+
+struct QQmlIntForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(int)
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(int)
+ QML_PRIMITIVE_ALIAS(qint32)
+ QML_PRIMITIVE_ALIAS(int32_t)
+#ifdef QML_SIZE_IS_32BIT
+ QML_PRIMITIVE_ALIAS(qsizetype)
+#endif
+#ifdef QML_LONG_IS_32BIT
+ QML_PRIMITIVE_ALIAS(long)
+#endif
+};
+
+struct QQmlDoubleForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(real)
+ QML_VALUE_TYPE(double)
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(double)
+#if !defined(QT_COORD_TYPE) || defined(QT_COORD_TYPE_IS_DOUBLE)
+ QML_PRIMITIVE_ALIAS(qreal)
+#endif
+};
+
+struct QQmlStringForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(string)
+ QML_EXTENDED_JAVASCRIPT(String)
+ QML_FOREIGN(QString)
+};
+
+struct QQmlAnyStringViewForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(String)
+ QML_FOREIGN(QAnyStringView)
+};
+
+struct QQmlBoolForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(bool)
+ QML_EXTENDED_JAVASCRIPT(Boolean)
+ QML_FOREIGN(bool)
+};
+
+struct QQmlDateForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(date)
+ QML_EXTENDED_JAVASCRIPT(Date)
+ QML_FOREIGN(QDateTime)
+};
+
+struct QQmlUrlForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(url)
+ QML_EXTENDED_JAVASCRIPT(URL)
+ QML_FOREIGN(QUrl)
+};
+
+#if QT_CONFIG(regularexpression)
+struct QQmlRegexpForeign
+{
+ Q_GADGET
+ QML_VALUE_TYPE(regexp)
+ QML_EXTENDED_JAVASCRIPT(RegExp)
+ QML_FOREIGN(QRegularExpression)
+};
+#endif
+
+struct QQmlNullForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(std::nullptr_t)
+ QML_EXTENDED(QQmlNullForeign)
+};
+
+struct QQmlQVariantMapForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QVariantMap)
+ QML_EXTENDED_JAVASCRIPT(Object)
+};
+
+struct QQmlQint8Foreign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(qint8)
+ QML_PRIMITIVE_ALIAS(int8_t)
+#if CHAR_MAX == SCHAR_MAX
+ QML_PRIMITIVE_ALIAS(char)
+#endif
+};
+
+struct QQmlQuint8Foreign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(quint8)
+ QML_PRIMITIVE_ALIAS(uint8_t)
+ QML_PRIMITIVE_ALIAS(uchar)
+#if CHAR_MAX == UCHAR_MAX
+ QML_PRIMITIVE_ALIAS(char)
+#endif
+};
+
+struct QQmlShortForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(short)
+ QML_PRIMITIVE_ALIAS(qint16)
+ QML_PRIMITIVE_ALIAS(int16_t)
+};
+
+struct QQmlUshortForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(ushort)
+ QML_PRIMITIVE_ALIAS(quint16)
+ QML_PRIMITIVE_ALIAS(uint16_t)
+};
+
+struct QQmlUintForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(uint)
+ QML_PRIMITIVE_ALIAS(quint32)
+ QML_PRIMITIVE_ALIAS(uint32_t)
+#ifdef QML_LONG_IS_32BIT
+ QML_PRIMITIVE_ALIAS(ulong)
+#endif
+};
+
+struct QQmlQlonglongForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(qlonglong)
+ QML_PRIMITIVE_ALIAS(qint64)
+ QML_PRIMITIVE_ALIAS(int64_t)
+#ifdef QML_LONG_IS_64BIT
+ QML_PRIMITIVE_ALIAS(long)
+#endif
+#ifdef QML_SIZE_IS_64BIT
+ QML_PRIMITIVE_ALIAS(qsizetype)
+#endif
+};
+
+struct QQmlQulonglongForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(qulonglong)
+ QML_PRIMITIVE_ALIAS(quint64)
+ QML_PRIMITIVE_ALIAS(uint64_t)
+#ifdef QML_LONG_IS_64BIT
+ QML_PRIMITIVE_ALIAS(ulong)
+#endif
+};
+
+struct QQmlFloatForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(Number)
+ QML_FOREIGN(float)
+#if defined(QT_COORD_TYPE) && defined(QT_COORD_TYPE_IS_FLOAT)
+ QML_PRIMITIVE_ALIAS(qreal)
+#endif
+};
+
+struct QQmlQCharForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QChar)
+ QML_EXTENDED_JAVASCRIPT(String)
+};
+
+struct QQmlQDateForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QDate)
+ QML_EXTENDED_JAVASCRIPT(Date)
+};
+
+struct QQmlQTimeForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QTime)
+ QML_EXTENDED_JAVASCRIPT(Date)
+};
+
+struct QQmlQByteArrayForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED_JAVASCRIPT(ArrayBuffer)
+ QML_FOREIGN(QByteArray)
+};
+
+struct QQmlQStringListForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QStringList)
+ QML_SEQUENTIAL_CONTAINER(QString)
+};
+
+struct QQmlQVariantListForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QVariantList)
+ QML_SEQUENTIAL_CONTAINER(QVariant)
+};
+
+struct QQmlQObjectListForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QObjectList)
+ QML_SEQUENTIAL_CONTAINER(QObject*)
+ QML_PRIMITIVE_ALIAS(QList<QObject*>)
+};
+
+struct QQmlQJSValueForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QJSValue)
+ QML_EXTENDED(QQmlQJSValueForeign)
+};
+
+struct QQmlComponentForeign
+{
+ Q_GADGET
+ QML_NAMED_ELEMENT(Component)
+ QML_FOREIGN(QQmlComponent)
+ QML_ATTACHED(QQmlComponentAttached)
+};
+
+struct QQmlScriptStringForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQmlScriptString)
+};
+
+struct QQmlV4FunctionPtrForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQmlV4FunctionPtr)
+ QML_EXTENDED(QQmlV4FunctionPtrForeign)
+};
+
+struct QQmlQJsonObjectForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QJsonObject)
+ QML_EXTENDED_JAVASCRIPT(Object)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLBUILTINS_H
diff --git a/src/qml/qtqml.tracepoints b/src/qml/qtqml.tracepoints
deleted file mode 100644
index 20fac87660..0000000000
--- a/src/qml/qtqml.tracepoints
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-namespace QV4 {
-struct ExecutionEngine;
-namespace CompiledData {
-struct CompilationUnit;
-struct Object;
-} // CompiledData
-} // QV4
-class QQmlEngine;
-}
-
-QQmlObjectCreator_createInstance_entry(const QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Object *object, const QUrl &url)
-QQmlObjectCreator_createInstance_exit(const QString &typeName)
-QQmlCompiling_entry(const QUrl &url)
-QQmlCompiling_exit()
-QQmlV4_function_call_entry(const QV4::ExecutionEngine *engine, const QString &function, const QString &fileName, int line, int column)
-QQmlV4_function_call_exit()
-QQmlBinding_entry(const QQmlEngine *engine, const QString &function, const QString &fileName, int line, int column)
-QQmlBinding_exit()
-QQmlHandlingSignal_entry(const QQmlEngine *engine, const QString &function, const QString &fileName, int line, int column)
-QQmlHandlingSignal_exit()
diff --git a/src/qml/qtqmlcompilerglobal_p.h b/src/qml/qtqmlcompilerglobal_p.h
index 4c06691ab5..f21c63d4a0 100644
--- a/src/qml/qtqmlcompilerglobal_p.h
+++ b/src/qml/qtqmlcompilerglobal_p.h
@@ -18,6 +18,4 @@
#include <QtCore/private/qglobal_p.h>
#include <qtqmlcompilerglobal.h>
-#define Q_QML_COMPILER_PRIVATE_EXPORT Q_QML_COMPILER_EXPORT
-
#endif // QTQMLCOMPILERGLOBAL_P_H
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index c29e09cbea..6c5c3967ed 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -18,7 +18,7 @@
#include <QtCore/private/qglobal_p.h>
#include <QtQml/qtqmlglobal.h>
#include <QtQml/private/qtqml-config_p.h>
-#include <QtQml/private/qtqmlexports_p.h>
+#include <QtQml/qtqmlexports.h>
#define Q_QML_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index a31e5ccd85..bc06d4ed51 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -1,30 +1,32 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlbind_p.h"
-#include <private/qqmlnullablevalue_p.h>
-#include <private/qqmlproperty_p.h>
+#include <private/qqmlanybinding_p.h>
#include <private/qqmlbinding_p.h>
+#include <private/qqmlcomponent_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qqmlnullablevalue_p.h>
+#include <private/qqmlproperty_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qv4persistent_p.h>
+#include <private/qv4qmlcontext_p.h>
+#include <private/qv4resolvedtypereference_p.h>
-#include <qqmlengine.h>
-#include <qqmlcontext.h>
-#include <qqmlproperty.h>
-#include <qqmlpropertymap.h>
-#include <qqmlinfo.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQml/qqmlproperty.h>
+#include <QtQml/qqmlpropertymap.h>
+
+#include <QtCore/private/qobject_p.h>
-#include <QtCore/qfile.h>
#include <QtCore/qdebug.h>
-#include <QtCore/qtimer.h>
+#include <QtCore/qfile.h>
#include <QtCore/qloggingcategory.h>
-#include <private/qqmlanybinding_p.h>
-#include <private/qv4qmlcontext_p.h>
-#include <private/qqmlcomponent_p.h>
-
-#include <private/qobject_p.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qtimer.h>
QT_BEGIN_NAMESPACE
@@ -280,7 +282,7 @@ public:
const QV4::CompiledData::Binding *binding,
QQmlComponentPrivate::ConstructionState *immediateState);
void createDelayedValues();
- void onDelayedValueChanged(const QString &delayedName);
+ void onDelayedValueChanged(QString delayedName);
void evalDelayed();
void buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState);
};
@@ -382,7 +384,7 @@ void QQmlBindPrivate::validate(QQmlBind *q) const
The Binding type restores any previously set direct bindings on the
property.
- \sa {Qt QML}
+ \sa {Qt Qml}
*/
QQmlBind::QQmlBind(QObject *parent)
: QObject(*(new QQmlBindPrivate), parent)
@@ -428,9 +430,9 @@ void QQmlBind::setWhen(bool v)
/*!
\qmlproperty QtObject QtQml::Binding::target
- The object to be updated. You only need to use this property if you can't
- supply the binding target declaratively. The following two pieces of code
- are equivalent.
+ The object to be updated. You need to use this property if the binding target
+ does not have an \c id attribute (for example, when the target is a singleton).
+ Otherwise, the following two pieces of code are equivalent:
\qml
Binding { contactName.text: name }
@@ -463,10 +465,26 @@ void QQmlBind::setObject(QObject *obj)
eval();
d->when = true;
}
+ /* if "when" and "target" depend on the same property, we might
+ end up here before we could have updated "when". So reevaluate
+ when manually here.
+ */
+ const QQmlProperty whenProp(this, QLatin1StringView("when"));
+ const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp);
+ if (auto abstractBinding = potentialWhenBinding.asAbstractBinding()) {
+ QQmlBinding *binding = static_cast<QQmlBinding *>(abstractBinding);
+ if (binding->hasValidContext()) {
+ const auto boolType = QMetaType::fromType<bool>();
+ bool when;
+ binding->evaluate(&when, boolType);
+ d->when = when;
+ }
+ }
d->obj = obj;
if (d->componentComplete) {
setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this)));
- d->validate(this);
+ if (d->when)
+ d->validate(this);
}
eval();
}
@@ -520,7 +538,8 @@ void QQmlBind::setProperty(const QString &p)
d->propName = p;
if (d->componentComplete) {
setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this)));
- d->validate(this);
+ if (d->when)
+ d->validate(this);
}
eval();
}
@@ -625,14 +644,13 @@ void QQmlBind::setDelayed(bool delayed)
be restored when the binding is disabled.
The possible values are:
- \list
- \li Binding.RestoreNone The original value is not restored at all
- \li Binding.RestoreBinding The original value is restored if it was another
- binding. In that case the old binding is in effect again.
- \li Binding.RestoreValue The original value is restored if it was a plain
- value rather than a binding.
- \li Binding.RestoreBindingOrValue The original value is always restored.
- \endlist
+
+ \value Binding.RestoreNone The original value is not restored at all
+ \value Binding.RestoreBinding The original value is restored if it was another binding.
+ In that case the old binding is in effect again.
+ \value Binding.RestoreValue The original value is restored if it was a plain
+ value rather than a binding.
+ \value Binding.RestoreBindingOrValue The original value is always restored.
The default value is \c Binding.RestoreBindingOrValue.
@@ -720,6 +738,20 @@ static QQmlAnyBinding createBinding(
return QQmlAnyBinding();
}
+static void initCreator(
+ QQmlData::DeferredData *deferredData,
+ const QQmlRefPointer<QQmlContextData> &contextData,
+ QQmlComponentPrivate::ConstructionState *immediateState)
+{
+ if (!immediateState->hasCreator()) {
+ immediateState->setCompletePending(true);
+ immediateState->initCreator(
+ deferredData->context->parent(), deferredData->compilationUnit,
+ contextData);
+ immediateState->creator()->beginPopulateDeferred(deferredData->context);
+ }
+}
+
void QQmlBindPrivate::decodeBinding(
QQmlBind *q, const QString &propertyPrefix,
QQmlData::DeferredData *deferredData,
@@ -728,12 +760,56 @@ void QQmlBindPrivate::decodeBinding(
{
const QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit
= deferredData->compilationUnit;
- const QString propertyName = propertyPrefix
- + compilationUnit->stringAt(binding->propertyNameIndex);
+ const QString propertySuffix = compilationUnit->stringAt(binding->propertyNameIndex);
+ const QString propertyName = propertyPrefix + propertySuffix;
switch (binding->type()) {
- case QV4::CompiledData::Binding::Type_GroupProperty:
- case QV4::CompiledData::Binding::Type_AttachedProperty: {
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ if (propertyPrefix.isEmpty()) {
+ // Top-level attached properties cannot be generalized grouped properties.
+ // Treat them as regular properties.
+ // ... unless we're not supposed to handle regular properties. Then ignore them.
+ if (!immediateState)
+ return;
+
+ Q_ASSERT(compilationUnit->stringAt(compilationUnit->objectAt(binding->value.objectIndex)
+ ->inheritedTypeNameIndex).isEmpty());
+
+ const QV4::ResolvedTypeReference *typeReference
+ = compilationUnit->resolvedType(binding->propertyNameIndex);
+ Q_ASSERT(typeReference);
+ QQmlType attachedType = typeReference->type();
+ if (!attachedType.isValid()) {
+ if (QQmlTypeLoader *typeLoader = compilationUnit->engine->typeLoader()) {
+ const QQmlTypeNameCache::Result result
+ = deferredData->context->imports()->query(propertySuffix, typeLoader);
+ if (!result.isValid()) {
+ qmlWarning(q).nospace()
+ << "Unknown name " << propertySuffix << ". The binding is ignored.";
+ return;
+ }
+ attachedType = result.type;
+ }
+ }
+
+ QQmlContext *context = qmlContext(q);
+ QObject *attachedObject = qmlAttachedPropertiesObject(
+ q, attachedType.attachedPropertiesFunction(
+ QQmlEnginePrivate::get(context->engine())));
+ if (!attachedObject) {
+ qmlWarning(q).nospace() <<"Could not create attached properties object '"
+ << attachedType.typeName() << "'";
+ return;
+ }
+
+ initCreator(deferredData, QQmlContextData::get(context), immediateState);
+ immediateState->creator()->populateDeferredInstance(
+ q, deferredData->deferredIdx, binding->value.objectIndex, attachedObject,
+ attachedObject, /*value type property*/ nullptr, binding);
+ return;
+ }
+ Q_FALLTHROUGH();
+ case QV4::CompiledData::Binding::Type_GroupProperty: {
const QString pre = propertyName + u'.';
const QV4::CompiledData::Object *subObj
= compilationUnit->objectAt(binding->value.objectIndex);
@@ -760,13 +836,7 @@ void QQmlBindPrivate::decodeBinding(
QQmlProperty property = QQmlPropertyPrivate::create(
q, propertyName, contextData, QQmlPropertyPrivate::InitFlag::AllowSignal);
if (property.isValid()) {
- if (!immediateState->hasCreator()) {
- immediateState->setCompletePending(true);
- immediateState->initCreator(
- deferredData->context->parent(), deferredData->compilationUnit,
- contextData);
- immediateState->creator()->beginPopulateDeferred(deferredData->context);
- }
+ initCreator(deferredData, contextData, immediateState);
immediateState->creator()->populateDeferredBinding(
property, deferredData->deferredIdx, binding);
} else {
@@ -831,14 +901,14 @@ void QQmlBindPrivate::createDelayedValues()
delayedValues = std::make_unique<QQmlPropertyMap>();
QObject::connect(
delayedValues.get(), &QQmlPropertyMap::valueChanged,
- delayedValues.get(), [this](const QString &delayedName, const QVariant &value) {
+ delayedValues.get(), [this](QString delayedName, const QVariant &value) {
Q_UNUSED(value);
- onDelayedValueChanged(delayedName);
+ onDelayedValueChanged(std::move(delayedName));
}
);
}
-void QQmlBindPrivate::onDelayedValueChanged(const QString &delayedName)
+void QQmlBindPrivate::onDelayedValueChanged(QString delayedName)
{
Q_ASSERT(delayed);
Q_ASSERT(delayedValues);
@@ -849,7 +919,7 @@ void QQmlBindPrivate::onDelayedValueChanged(const QString &delayedName)
else if (pending.contains(delayedName))
return;
- pending.append(delayedName);
+ pending.append(std::move(delayedName));
(*delayedValues)[pendingName].setValue(std::move(pending));
}
@@ -959,6 +1029,7 @@ void QQmlBind::eval()
break;
case QQmlBindEntryKind::V4Value:
if (d->restoreValue) {
+ QQmlAnyBinding::takeFrom(entry.prop); // we don't want to have a binding active
auto propPriv = QQmlPropertyPrivate::get(entry.prop);
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object);
Q_ASSERT(vmemo);
@@ -969,6 +1040,7 @@ void QQmlBind::eval()
break;
case QQmlBindEntryKind::Variant:
if (d->restoreValue) {
+ QQmlAnyBinding::takeFrom(entry.prop); // we don't want to have a binding active
entry.prop.write(entry.previous.variant);
entry.clearPrev();
}
diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h
index 31d1c53552..aaefbc0618 100644
--- a/src/qml/types/qqmlbind_p.h
+++ b/src/qml/types/qqmlbind_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
class QQmlBindPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus
+class Q_QML_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus
{
public:
enum RestorationMode {
@@ -89,6 +89,4 @@ private Q_SLOTS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlBind)
-
#endif
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
index 1527a2ea34..19363f9f76 100644
--- a/src/qml/types/qqmlconnections.cpp
+++ b/src/qml/types/qqmlconnections.cpp
@@ -3,16 +3,20 @@
#include "qqmlconnections_p.h"
-#include <private/qqmlexpression_p.h>
-#include <private/qqmlproperty_p.h>
#include <private/qqmlboundsignal_p.h>
-#include <qqmlcontext.h>
#include <private/qqmlcontext_p.h>
+#include <private/qqmlexpression_p.h>
+#include <private/qqmlproperty_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <private/qqmlvmemetaobject_p.h>
-#include <qqmlinfo.h>
+#include <private/qv4jscall_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlinfo.h>
-#include <QtCore/qloggingcategory.h>
#include <QtCore/qdebug.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qstringlist.h>
#include <private/qobject_p.h>
@@ -21,10 +25,110 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQmlConnections, "qt.qml.connections")
+// This is the equivalent of QQmlBoundSignal for C++ methods as as slots.
+// If a type derived from QQmlConnnections is compiled using qmltc, the
+// JavaScript functions it contains are turned into C++ methods and we cannot
+// use QQmlBoundSignal to connect to those.
+struct QQmlConnectionSlotDispatcher : public QtPrivate::QSlotObjectBase
+{
+ QV4::ExecutionEngine *v4 = nullptr;
+ QObject *receiver = nullptr;
+
+ // Signals rarely have more than one argument.
+ QQmlMetaObject::ArgTypeStorage<2> signalMetaTypes;
+ QQmlMetaObject::ArgTypeStorage<2> slotMetaTypes;
+
+ QMetaObject::Connection connection;
+
+ int slotIndex = -1;
+ bool enabled = true;
+
+ QQmlConnectionSlotDispatcher(
+ QV4::ExecutionEngine *v4, QObject *sender, int signalIndex,
+ QObject *receiver, int slotIndex, bool enabled)
+ : QtPrivate::QSlotObjectBase(&impl)
+ , v4(v4)
+ , receiver(receiver)
+ , slotIndex(slotIndex)
+ , enabled(enabled)
+ {
+ QMetaMethod signal = sender->metaObject()->method(signalIndex);
+ QQmlMetaObject::methodReturnAndParameterTypes(signal, &signalMetaTypes, nullptr);
+
+ QMetaMethod slot = receiver->metaObject()->method(slotIndex);
+ QQmlMetaObject::methodReturnAndParameterTypes(slot, &slotMetaTypes, nullptr);
+ }
+
+ template<typename ArgTypeStorage>
+ struct TypedFunction
+ {
+ Q_DISABLE_COPY_MOVE(TypedFunction)
+ public:
+ TypedFunction(const ArgTypeStorage *storage) : storage(storage) {}
+
+ QMetaType returnMetaType() const { return storage->at(0); }
+ qsizetype parameterCount() const { return storage->size() - 1; }
+ QMetaType parameterMetaType(qsizetype i) const { return storage->at(i + 1); }
+
+ private:
+ const ArgTypeStorage *storage;
+ };
+
+ static void impl(int which, QSlotObjectBase *base, QObject *, void **metaArgs, bool *ret)
+ {
+ switch (which) {
+ case Destroy: {
+ delete static_cast<QQmlConnectionSlotDispatcher *>(base);
+ break;
+ }
+ case Call: {
+ QQmlConnectionSlotDispatcher *self = static_cast<QQmlConnectionSlotDispatcher *>(base);
+ QV4::ExecutionEngine *v4 = self->v4;
+ if (!v4)
+ break;
+
+ if (!self->enabled)
+ break;
+
+ TypedFunction typedFunction(&self->slotMetaTypes);
+ QV4::coerceAndCall(
+ v4, &typedFunction, metaArgs,
+ self->signalMetaTypes.data(), self->signalMetaTypes.size() - 1,
+ [&](void **argv, int) {
+ self->receiver->metaObject()->metacall(
+ self->receiver, QMetaObject::InvokeMetaMethod,
+ self->slotIndex, argv);
+ });
+
+ if (v4->hasException) {
+ QQmlError error = v4->catchExceptionAsQmlError();
+ if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
+ QQmlEnginePrivate::get(qmlEngine)->warning(error);
+ } else {
+ QMessageLogger(
+ qPrintable(error.url().toString()), error.line(), nullptr)
+ .warning().noquote()
+ << error.toString();
+ }
+ }
+ break;
+ }
+ case Compare:
+ // We're not implementing the Compare protocol here. It's insane.
+ // QQmlConnectionSlotDispatcher compares false to anything. We use
+ // the regular QObject::disconnect with QMetaObject::Connection.
+ *ret = false;
+ break;
+ case NumOperations:
+ break;
+ }
+ };
+};
+
class QQmlConnectionsPrivate : public QObjectPrivate
{
public:
- QList<QQmlBoundSignal*> boundsignals;
+ QList<QBiPointer<QQmlBoundSignal, QQmlConnectionSlotDispatcher>> boundsignals;
QQmlGuard<QObject> target;
bool enabled = true;
@@ -98,13 +202,29 @@ public:
then all signal handlers specified as \c{function} in the same Connections
object are ignored.
- \sa {Qt QML}
+ \sa {Qt Qml}
*/
QQmlConnections::QQmlConnections(QObject *parent) :
QObject(*(new QQmlConnectionsPrivate), parent)
{
}
+QQmlConnections::~QQmlConnections()
+{
+ Q_D(QQmlConnections);
+
+ // The slot dispatchers hold cyclic references to their connections. Clear them.
+ for (const auto &bound : std::as_const(d->boundsignals)) {
+ if (QQmlConnectionSlotDispatcher *dispatcher = bound.isT2() ? bound.asT2() : nullptr) {
+ // No need to explicitly disconnect anymore since 'this' is the receiver.
+ // But to be safe, explicitly break any cyclic references between the connection
+ // and the slot object.
+ dispatcher->connection = {};
+ dispatcher->destroyIfLastRef();
+ }
+ }
+}
+
/*!
\qmlproperty QtObject QtQml::Connections::target
This property holds the object that sends the signal.
@@ -136,13 +256,19 @@ void QQmlConnections::setTarget(QObject *obj)
if (d->targetSet && d->target == obj)
return;
d->targetSet = true; // even if setting to 0, it is *set*
- for (QQmlBoundSignal *s : std::as_const(d->boundsignals)) {
+ for (const auto &bound : std::as_const(d->boundsignals)) {
// It is possible that target is being changed due to one of our signal
// handlers -> use deleteLater().
- if (s->isNotifying())
- (new QQmlBoundSignalDeleter(s))->deleteLater();
- else
- delete s;
+ if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() : nullptr) {
+ if (signal->isNotifying())
+ (new QQmlBoundSignalDeleter(signal))->deleteLater();
+ else
+ delete signal;
+ } else {
+ QQmlConnectionSlotDispatcher *dispatcher = bound.asT2();
+ QObject::disconnect(std::exchange(dispatcher->connection, {}));
+ dispatcher->destroyIfLastRef();
+ }
}
d->boundsignals.clear();
d->target = obj;
@@ -172,8 +298,12 @@ void QQmlConnections::setEnabled(bool enabled)
d->enabled = enabled;
- for (QQmlBoundSignal *s : std::as_const(d->boundsignals))
- s->setEnabled(d->enabled);
+ for (const auto &bound : std::as_const(d->boundsignals)) {
+ if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() : nullptr)
+ signal->setEnabled(d->enabled);
+ else
+ bound.asT2()->enabled = enabled;
+ }
emit enabledChanged();
}
@@ -205,9 +335,7 @@ void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableC
const QV4::CompiledData::Binding *binding = props.at(ii);
const QString &propName = compilationUnit->stringAt(binding->propertyNameIndex);
- const bool thirdCharacterIsValid = (propName.size() >= 2)
- && (propName.at(2).isUpper() || propName.at(2) == u'_');
- if (!propName.startsWith(QLatin1String("on")) || !thirdCharacterIsValid) {
+ if (!QQmlSignalNames::isHandlerName(propName)) {
error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
return;
}
@@ -246,12 +374,10 @@ void QQmlConnections::connectSignals()
if (d->bindings.isEmpty()) {
connectSignalsToMethods();
} else {
-#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
if (lcQmlConnections().isWarningEnabled()) {
qmlWarning(this) << tr("Implicitly defined onFoo properties in Connections are deprecated. "
"Use this syntax instead: function onFoo(<arguments>) { ... }");
}
-#endif
connectSignalsToBindings();
}
}
@@ -274,33 +400,41 @@ void QQmlConnections::connectSignalsToMethods()
++i) {
const QQmlPropertyData *handler = ddata->propertyCache->method(i);
- if (!handler || !handler->isVMEFunction())
+ if (!handler)
continue;
const QString propName = handler->name(this);
QQmlProperty prop(target, propName);
if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
- int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
- auto *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this));
- signal->setEnabled(d->enabled);
-
QV4::Scope scope(engine);
QV4::ScopedContext global(scope, engine->rootContext());
- QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(this);
- Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
-
- QV4::ScopedFunctionObject method(scope, vmeMetaObject->vmeMethod(handler->coreIndex()));
-
- QQmlBoundSignalExpression *expression =
- ctxtdata ? new QQmlBoundSignalExpression(
- target, signalIndex, ctxtdata, this,
- method->as<QV4::FunctionObject>()->function())
- : nullptr;
-
- signal->takeExpression(expression);
- d->boundsignals += signal;
+ if (QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(this)) {
+ int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
+ auto *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this));
+ signal->setEnabled(d->enabled);
+
+ QV4::ScopedFunctionObject method(
+ scope, vmeMetaObject->vmeMethod(handler->coreIndex()));
+
+ QQmlBoundSignalExpression *expression = ctxtdata
+ ? new QQmlBoundSignalExpression(
+ target, signalIndex, ctxtdata, this,
+ method->as<QV4::FunctionObject>()->function())
+ : nullptr;
+
+ signal->takeExpression(expression);
+ d->boundsignals += signal;
+ } else {
+ QQmlConnectionSlotDispatcher *slot = new QQmlConnectionSlotDispatcher(
+ scope.engine, target, prop.index(),
+ this, handler->coreIndex(), d->enabled);
+ slot->connection = QObjectPrivate::connect(
+ target, prop.index(), slot, Qt::AutoConnection);
+ slot->ref();
+ d->boundsignals += slot;
+ }
} else if (!d->ignoreUnknownSignals
&& propName.startsWith(QLatin1String("on")) && propName.size() > 2
&& propName.at(2).isUpper()) {
diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h
index a7f8c19d4e..e0100aa452 100644
--- a/src/qml/types/qqmlconnections_p.h
+++ b/src/qml/types/qqmlconnections_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQmlBoundSignal;
class QQmlContext;
class QQmlConnectionsPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlConnections : public QObject, public QQmlParserStatus
+class Q_QML_EXPORT QQmlConnections : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlConnections)
@@ -40,7 +40,8 @@ class Q_QML_PRIVATE_EXPORT QQmlConnections : public QObject, public QQmlParserSt
QML_CUSTOMPARSER
public:
- QQmlConnections(QObject *parent=nullptr);
+ QQmlConnections(QObject *parent = nullptr);
+ ~QQmlConnections();
QObject *target() const;
void setTarget(QObject *);
@@ -51,6 +52,10 @@ public:
bool ignoreUnknownSignals() const;
void setIgnoreUnknownSignals(bool ignore);
+protected:
+ void classBegin() override;
+ void componentComplete() override;
+
Q_SIGNALS:
void targetChanged();
Q_REVISION(2, 3) void enabledChanged();
@@ -59,9 +64,6 @@ private:
void connectSignals();
void connectSignalsToMethods();
void connectSignalsToBindings();
-
- void classBegin() override;
- void componentComplete() override;
};
// TODO: Drop this class as soon as we can
@@ -82,6 +84,4 @@ inline QQmlCustomParser *qmlCreateCustomParser<QQmlConnections>()
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlConnections)
-
#endif
diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h
index c5a7f2bd11..e75b8bbe9d 100644
--- a/src/qml/types/qqmltimer_p.h
+++ b/src/qml/types/qqmltimer_p.h
@@ -26,7 +26,7 @@ QT_REQUIRE_CONFIG(qml_animation);
QT_BEGIN_NAMESPACE
class QQmlTimerPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlTimer : public QObject, public QQmlParserStatus
+class Q_QML_EXPORT QQmlTimer : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlTimer)
@@ -82,6 +82,4 @@ private Q_SLOTS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlTimer)
-
#endif
diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt
index b3cd96e1d7..4e7d1cbf1c 100644
--- a/src/qmlcompiler/CMakeLists.txt
+++ b/src/qmlcompiler/CMakeLists.txt
@@ -5,11 +5,9 @@
## QmlCompilerPrivate Module:
#####################################################################
-qt_internal_add_module(QmlCompilerPrivate
- INTERNAL_MODULE
+qt_internal_add_module(QmlCompiler
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
- PLUGIN_TYPES qmllint
+ PLUGIN_TYPES qmllint
SOURCES
qcoloroutput_p.h qcoloroutput.cpp
qdeferredpointer_p.h
@@ -22,13 +20,17 @@ qt_internal_add_module(QmlCompilerPrivate
qqmljsimporter.cpp qqmljsimporter_p.h
qqmljsimportvisitor.cpp qqmljsimportvisitor_p.h
qqmljsliteralbindingcheck.cpp qqmljsliteralbindingcheck_p.h
+ qqmljsvaluetypefromstringcheck.cpp qqmljsvaluetypefromstringcheck_p.h
qqmljsloadergenerator.cpp qqmljsloadergenerator_p.h
qqmljslogger_p.h qqmljslogger.cpp
+ qqmljsloggingutils.h qqmljsloggingutils.cpp qqmljsloggingutils_p.h
qqmljsmetatypes_p.h qqmljsmetatypes.cpp
+ qqmljsoptimizations_p.h qqmljsoptimizations.cpp
qqmljsregistercontent.cpp qqmljsregistercontent_p.h
qqmljsresourcefilemapper.cpp qqmljsresourcefilemapper_p.h
qqmljsscope.cpp qqmljsscope_p.h
qqmljsscopesbyid_p.h
+ qqmljscontextualtypes_p.h
qqmljsshadowcheck.cpp qqmljsshadowcheck_p.h
qqmljsstoragegeneralizer.cpp qqmljsstoragegeneralizer_p.h
qqmljstypedescriptionreader.cpp qqmljstypedescriptionreader_p.h
@@ -39,18 +41,32 @@ qt_internal_add_module(QmlCompilerPrivate
qqmljsutils_p.h qqmljsutils.cpp
qqmljslinter_p.h qqmljslinter.cpp
qqmljslintercodegen_p.h qqmljslintercodegen.cpp
- qqmlsa_p.h qqmlsa.cpp
+ qqmlsa_p.h qqmlsa.h qqmlsa.cpp
+ qqmlsaconstants.h
+ qqmlsasourcelocation.h qqmlsasourcelocation.cpp qqmlsasourcelocation_p.h
+ NO_UNITY_BUILD_SOURCES
+ qqmljsoptimizations.cpp
PUBLIC_LIBRARIES
- Qt::CorePrivate
+ Qt::Core
+ Qt::Qml
+ LIBRARIES
Qt::QmlPrivate
+ PRIVATE_MODULE_INTERFACE
+ Qt::QmlPrivate
+
)
-qt_internal_add_resource(QmlCompilerPrivate "builtins"
+qt_path_join(qml_build_dir "${QT_BUILD_DIR}" "${INSTALL_QMLDIR}")
+qt_internal_add_resource(QmlCompiler "builtins"
PREFIX
"/qt-project.org/qml/builtins"
BASE
- "${CMAKE_CURRENT_SOURCE_DIR}/../imports/builtins/"
+ "${qml_build_dir}"
FILES
- "${CMAKE_CURRENT_SOURCE_DIR}/../imports/builtins/builtins.qmltypes"
- "${CMAKE_CURRENT_SOURCE_DIR}/../imports/builtins/jsroot.qmltypes"
+ "${qml_build_dir}/builtins.qmltypes"
+ "${qml_build_dir}/jsroot.qmltypes"
+)
+
+qt_internal_add_docs(QmlCompiler
+ doc/qtqmlcompiler.qdocconf
)
diff --git a/src/qmlcompiler/doc/qtqmlcompiler-index.qdoc b/src/qmlcompiler/doc/qtqmlcompiler-index.qdoc
new file mode 100644
index 0000000000..3de06d577e
--- /dev/null
+++ b/src/qmlcompiler/doc/qtqmlcompiler-index.qdoc
@@ -0,0 +1,81 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtqmlcompiler-index.html
+ \title Qt QML Compiler
+ \brief Provides tools for static analysis of QML code.
+
+ The Qt QML Compiler module contains shared functionality needed by QML
+ tooling like the \l{Qt Quick Compiler} and \l{qmllint Reference}{qmllint}.
+ It also provides the QQmlSA framework, which can be used to extend the
+ built-in analysis capabilities of the tools.
+
+ \section1 Using the Module
+
+ \include {module-use.qdocinc} {using the c++ api}
+
+ \section2 Building with CMake
+
+ \include {module-use.qdocinc} {building with cmake} {QmlCompiler}
+
+ \section2 Building with qmake
+
+ \include {module-use.qdocinc} {building_with_qmake} {QmlCompiler}
+
+ \section1 Using the QQmlSA framework
+
+ The Qt QML Compiler module offers the QQmlSA framework which provides tools
+ for static analysis of QML code. These tools can help ensure syntactic
+ validity and warn about QML anti-patterns.
+ Adding static analysis to a QML program is done by writing plugins. They
+ will run a collection of analysis passes over the elements and properties
+ of the QML code. The passes can be registered with a PassManager which
+ holds the passes and can be called to analyze an element and its children.
+ A pass is a check for a certain rule or condition evaluated on elements or
+ properties. If the condition is met, the pass can warn the user of an
+ indentified issue in the code and maybe even suggest a fix. It is called a
+ pass because the analysis performed on elements and properties happens by
+ running a collection of passes on them in succesion. Each pass should be
+ responsible for identifying one specific issue only. Combining a set of
+ passes can perform more complex analysis and, together, form a plugin.
+ Element passes are defined by two main components, namely \c shouldRun()
+ and \c run(). When performing the analysis, the pass manager will execute
+ the pass over every element it encounters while traversing the children of
+ the root element. For each element, if \c shouldRun() evaluated on that
+ element returns \c true then \c run() is executed on it.
+ Passes on properties trigger on three different events, namely when the
+ property is bound, when it is read, and when it is written to. These can be
+ implemented by overriding the \c onBinding(), \c onRead() and \c onWrite()
+ functions respectively.
+ As the code grows, so does the number of elements and properties.
+ Performing the static analysis passes on all of them can become expensive.
+ That's why it is good to be granular when deciding which elements and
+ properties to analyze. For elements, the \c shouldRun() is intended to be a
+ cheap check to determine if \c run(), which performs the real computation,
+ should be run. For properties, the selection is done when registering the
+ passes with the manager. The \c registerPropertyPass() function takes the
+ \c moduleName, \c typeName and \c propertyName strings as arguments. These
+ are used to filter down the set of properties affected by the registered
+ pass.
+
+
+ \section1 Examples
+
+ The \l{QML Static Analysis Tutorial} shows how to use the \c{QQmlSA}
+ framework to create a custom \l{qmllint Reference}{qmllint} pass.
+
+ \section1 Reference
+
+ \list
+ \li \l {Qt QML Compiler C++ Classes}
+ - the C++ API provided by the QmlCompiler module
+ \li QML tooling using the static analysis capabilities
+ \list
+ \li \l{QML script compiler}
+ \li \l{qmllint Reference}{qmllint}
+ \li \l{\QMLLS Reference}{\QMLLS}
+ \li \l{QML type compiler}
+ \endlist
+ \endlist
+ */
diff --git a/src/qmlcompiler/doc/qtqmlcompiler.qdocconf b/src/qmlcompiler/doc/qtqmlcompiler.qdocconf
new file mode 100644
index 0000000000..d5b45c2731
--- /dev/null
+++ b/src/qmlcompiler/doc/qtqmlcompiler.qdocconf
@@ -0,0 +1,49 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtdeclarative.qdocconf)
+
+project = QtQmlCompiler
+description = Qt QML Compiler Reference Documentation
+version = $QT_VERSION
+
+qhp.projects = QtQmlCompiler
+
+qhp.QtQmlCompiler.file = qtqmlcompiler.qhp
+qhp.QtQmlCompiler.namespace = org.qt-project.qtqmlcompiler.$QT_VERSION_TAG
+qhp.QtQmlCompiler.virtualFolder = qtqmlcompiler
+qhp.QtQmlCompiler.indexTitle = Qt QML Compiler
+qhp.QtQmlCompiler.indexRoot =
+
+qhp.QtQmlCompiler.subprojects = classes
+qhp.QtQmlCompiler.subprojects.classes.title = Classes
+qhp.QtQmlCompiler.subprojects.classes.indexTitle = Qt QML Compiler C++ Classes
+qhp.QtQmlCompiler.subprojects.classes.selectors = namespace class
+qhp.QtQmlCompiler.subprojects.classes.sortPages = true
+
+tagfile = qtqmlcompiler.tags
+
+depends += \
+ qtcore \
+ qtnetwork \
+ qtqmlmodels \
+ qtqmlworkerscript \
+ qtgui \
+ qtquick \
+ qtdoc \
+ qtlinguist \
+ qtwidgets \
+ qtquickcontrols \
+ qmake \
+ qtcmake \
+ qtqml
+
+headerdirs += ..
+
+sourcedirs += ..
+
+navigation.landingpage = "Qt QML Compiler"
+navigation.cppclassespage = "Qt QML Compiler C++ Classes"
+
+# Enforce zero documentation warnings
+warninglimit = 0
+exampledirs += \
+ ../../../examples/qmlcompiler
diff --git a/src/qmlcompiler/doc/src/qtqmlcompiler-module.qdoc b/src/qmlcompiler/doc/src/qtqmlcompiler-module.qdoc
new file mode 100644
index 0000000000..152fc94cd4
--- /dev/null
+++ b/src/qmlcompiler/doc/src/qtqmlcompiler-module.qdoc
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \module QtQmlCompiler
+ \modulestate Technical Preview
+ \title Qt QML Compiler C++ Classes
+ \qtcmakepackage QmlCompiler
+
+ For more information on the Qt QmlCompiler module, see the
+ \l{Qt QML Compiler} module documentation.\brief The Qt QML Compiler module provides tools
+ for static analysis of QML code.
+*/
diff --git a/src/qmlcompiler/doc/src/tutorial.qdoc b/src/qmlcompiler/doc/src/tutorial.qdoc
new file mode 100644
index 0000000000..ef96ee1e2c
--- /dev/null
+++ b/src/qmlcompiler/doc/src/tutorial.qdoc
@@ -0,0 +1,229 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qqmlsa-tutorial.html
+\title QML Static Analysis Tutorial
+\brief An introduction on writing your own qmllint checks
+\nextpage QML Static Analysis 1 - Basic Setup
+
+This tutorial gives an introduction to QQmlSA, the API to statically analyze QML code.
+
+Through the different steps of this tutorial we will learn about how to set up a basic qmllint
+plugin, we will create our own custom analysis pass, and we will add support for fixit-hints.
+
+Chapter one starts with a minimal plugin and the following chapters introduce new concepts.
+
+The tutorial's source code is located in the \c{examples/qmlcompiler/tutorials/helloworld} directory.
+
+Tutorial chapters:
+
+\list 1
+\li \l {QML Static Analysis 1 - Basic Setup}{Basic Setup}
+\li \l {QML Static Analysis 2 - Custom Pass}{Custom Pass}
+\li \l {QML Static Analysis 3 - Fixit Hints}{Fixit Hints}
+\endlist
+
+*/
+
+/*!
+\page qqmlsa-tutorial1.html
+\title QML Static Analysis 1 - Basic Setup
+\previouspage QML Static Analysis Tutorial
+\nextpage QML Static Analysis 2 - Custom Pass
+
+This chapter introduces the basic structure of a qmllint extension plugin,
+and how it can be used with qmllint.
+
+To create our plugin, we first need to make the QmlCompiler module available:
+\quotefromfile tutorials/helloworld/chapter1/CMakeLists.txt
+\skipto find_package
+\printline find_package
+
+We then create a plugin, and link it against the QmlCompiler module.
+
+\skipto qt_add_plugin(HelloWorldPlugin)
+\printuntil
+
+The implementation follows the pattern for \l{The High-Level API: Writing Qt Extensions}{extending
+Qt with a plugin}: We subclass the \l{QQmlSA::LintPlugin},
+\quotefromfile tutorials/helloworld/chapter1/helloplugin.h
+\skipto class HelloWorldPlugin
+\printuntil };
+
+The plugin references a \c{plugin.json} file, which contains some important information:
+\quotefile tutorials/helloworld/chapter1/plugin.json
+\c{name}, \c{author}, \c{version} and \c{description} are meta-data to describe the plugin.
+
+Each plugin can have one or more logging categories, which are used to thematically group
+warnings.
+\c{loggingCategories} takes an array of categories, each consisting of
+\list
+ \li \c{name}, which is used to identify the category, and as the flag name in qmllint,
+ \li \c{settingsName}, which is used to configure the category in qmllint.ini
+ \li \c{description}, which should describe what kind of warning messages are tagged with the category
+\endlist
+
+In our example, we have only one logging category, \c{hello-world}. For each category, we have to
+create a \l{QQmlSA::}{LoggerWarningId} object in our plugin.
+
+\quotefromfile tutorials/helloworld/chapter1/helloplugin.cpp
+\skipto static constexpr QQmlSA::LoggerWarningId
+\printline static constexpr QQmlSA::LoggerWarningId
+
+We construct it with a string literal which should have the format
+\c{Plugin.<plugin name>.<category name>}.
+
+Lastly, for our plugin to actually do anything useful, we have to implement the
+\c{registerPasses} function.
+
+\skipto void HelloWorldPlugin::registerPasses(
+\printuntil }
+
+We check whether our category is enabled, and print a diagnostic indicating its status
+via \l{qDebug}.
+Note that the plugin currently does not do anything useful, as we do not register any
+passes. This will done in the next part of this tutorial.
+
+We can however already verify that our plugin is correctly detected. We use the following command to
+check it:
+
+\badcode
+qmllint -P /path/to/the/directory/containing/the/plugin --Plugin.HelloWorld.hello-world info test.qml
+\endcode
+
+The \c{-P} options tells qmllint to look for plugins in the specified folder. To avoid conflicts
+with Qt internal categories, plugin categories are always prefixed with "Plugin", then followed by
+a dot, the plugin name, another dot and finally the category.
+Running the command should print \c{Hello World Plugin is enabled}.
+
+*/
+
+
+/*!
+\page qqmlsa-tutorial2.html
+\title QML Static Analysis 2 - Custom Pass
+\previouspage QML Static Analysis 1 - Basic Setup
+\nextpage QML Static Analysis 3 - Fixit Hints
+
+This chapter shows how custom analysis passes can be added to \l{qmllint Reference}{qmllint},
+by extending the plugin we've created in the last chapter.
+For demonstration purposes, we will create a plugin which checks whether
+\l{Text} elements have "Hello world!" assigned to their text property.
+
+To do this, we create a new class derived from
+\l{QQmlSA::ElementPass}{ElementPass}.
+
+\note There are two types of passes that
+plugins can register, \l{QQmlSA::ElementPass}{ElementPasses}, and \l{QQmlSA::PropertyPass}{PropertyPasses}.
+In this tutorial, we will only consider the simpler \c{ElementPass}.
+
+\quotefromfile tutorials/helloworld/chapter2/helloplugin.cpp
+\skipto class HelloWorldElementPass : public QQmlSA::ElementPass
+\printuntil };
+
+As our \c{HelloWorldElementPass} should analyze \c{Text} elements,
+we need a reference to the \c{Text} type. We can use the
+\l{QQmlSA::GenericPass::resolveType}{resolveType} function to obtain it.
+As we don't want to constantly re-resolve the type, we do this
+once in the constructor, and store the type in a member variable.
+
+\skipto HelloWorldElementPass::HelloWorldElementPass(
+\printuntil }
+
+The actual logic of our pass happens in two functions:
+\l{QQmlSA::ElementPass::shouldRun}{shouldRun} and
+\l{QQmlSA::ElementPass::run}{run}. They will run on
+all Elements in the file that gets analyzed by qmllint.
+
+In our \c{shouldRun} method, we check whether the current
+Element is derived from Text, and check whether it has
+a binding on the text property.
+\skipto bool HelloWorldElementPass::shouldRun
+\printuntil }
+
+Only elements passing the checks there will then be analyzed by our
+pass via its \c{run} method. It would be possible to do all checking
+inside of \c{run} itself, but it is generally preferable to have
+a separation of concerns – both for performance and to enhance
+code readability.
+
+In our \c{run} function, we retrieve the bindings to the text
+property. If the bound value is a string literal, we check
+if it's the greeting we expect.
+
+\skipto void HelloWorldElementPass::run
+\printuntil /^}/
+
+\note Most of the time, a property will only have one
+binding assigned to it. However, there might be for
+instance a literal binding and a \l{Behavior} assigned
+to the same property.
+
+Lastly, we need to create an instance of our pass, and
+register it with the \l{QQmlSA::PassManager}{PassManager}. This is done by
+adding
+\skipto manager->registerElementPass
+\printline manager->registerElementPass
+to the \c{registerPasses} functions of our plugin.
+
+We can test our plugin by invoking \l{qmllint Reference}{qmllint} on an example
+file via
+\badcode
+qmllint -P /path/to/the/directory/containing/the/plugin --Plugin.HelloWorld.hello-world info test.qml
+\endcode
+
+If \c{test.qml} looks like
+\quotefromfile{tutorials/helloworld/chapter2/test.qml}
+\skipto import
+\printuntil
+
+we will get
+\badcode
+Info: test.qml:22:26: Incorrect greeting [Plugin.HelloWorld.hello-world]
+ MyText { text: "Goodbye world!" }
+ ^^^^^^^^^^^^^^^^
+Info: test.qml:19:19: Incorrect greeting [Plugin.HelloWorld.hello-world]
+ Text { text: "Goodbye world!" }
+\endcode
+
+as the output. We can make a few observations here:
+\list
+\li The first \c{Text} does contain the expected greeting, so there's no warning
+\li The second \c{Text} would at runtime have the wrong warning (\c{"Hello"}
+ instead of \c{"Hello world"}. However, this cannot be detected by qmllint
+ (in general), as there's no literal binding, but a binding to another property.
+ As we only check literal bindings, we simply skip over this binding.
+\li For the literal binding in the third \c{Text} element, we correctly warn about
+ the wrong greeting.
+\li As \c{NotText} does not derive from \c{Text}, the analysis will skip it, as
+ the \c{inherits} check will discard it.
+\li The custom \c{MyText} element inherits from \c{Text}, and consequently we
+ see the expected warning.
+\endlist
+
+In summary, we've seen the steps necessary to extend \c{qmllint} with custom passes,
+and have also become aware of the limitations of static checks.
+
+*/
+
+/*!
+\page qqmlsa-tutorial3.html
+\title QML Static Analysis 3 - Fixit Hints
+\previouspage QML Static Analysis 2 - Custom Pass
+
+In this chapter we learn how to improve our custom warnings by amending them
+with fixit hints.
+
+So far, we only created warning messages. However, sometimes we also want to
+add a tip for the user how to fix the code. For that, we can pass an instance
+of \c FixSuggestion to \l{QQmlSA::GenericPass::emitWarning}{emitWarning}.
+A fix suggestion always consists of a description of what should be fixed, and
+the location where it should apply. It can also feature a replacement text.
+By default, the replacement text is only shown in the diagnostic message.
+By calling \c{setAutoApplicable(true)} on the \c{FixSuggestion}, the user
+can however apply the fix automatically via qmllint or the QML language
+server.
+It is important to only mark the suggestion as auto-applicable if the
+resulting code is valid.
+*/
diff --git a/src/qmlcompiler/qcoloroutput.cpp b/src/qmlcompiler/qcoloroutput.cpp
index b79fc86caf..8e0d57faf5 100644
--- a/src/qmlcompiler/qcoloroutput.cpp
+++ b/src/qmlcompiler/qcoloroutput.cpp
@@ -17,17 +17,17 @@ class QColorOutputPrivate
public:
QColorOutputPrivate()
{
- /* - QIODevice::Unbuffered because we want it to appear when the user actually calls,
- * performance is considered of lower priority.
- */
- m_out.open(stderr, QIODevice::WriteOnly | QIODevice::Unbuffered);
m_coloringEnabled = isColoringPossible();
}
static const char *const foregrounds[];
static const char *const backgrounds[];
- inline void write(const QString &msg) { m_out.write(msg.toLocal8Bit()); }
+ inline void write(const QString &msg)
+ {
+ const QByteArray encodedMsg = msg.toLocal8Bit();
+ fwrite(encodedMsg.constData(), size_t(1), size_t(encodedMsg.size()), stderr);
+ }
static QString escapeCode(const QString &in)
{
@@ -58,7 +58,7 @@ private:
bool m_coloringEnabled = false;
bool m_silent = false;
- /*!
+ /*
Returns true if it's suitable to send colored output to \c stderr.
*/
inline bool isColoringPossible() const
@@ -72,7 +72,7 @@ private:
/* We use QFile::handle() to get the file descriptor. It's a bit unsure
* whether it's 2 on all platforms and in all cases, so hopefully this layer
* of abstraction helps handle such cases. */
- return isatty(m_out.handle());
+ return isatty(fileno(stderr));
#endif
}
};
@@ -109,18 +109,18 @@ const char *const QColorOutputPrivate::backgrounds[] =
};
/*!
- \class ColorOutput
+ \class QColorOutput
\nonreentrant
\brief Outputs colored messages to \c stderr.
\internal
- ColorOutput is a convenience class for outputting messages to \c
- stderr using color escape codes, as mandated in ECMA-48. ColorOutput
+ QColorOutput is a convenience class for outputting messages to \c
+ stderr using color escape codes, as mandated in ECMA-48. QColorOutput
will only color output when it is detected to be suitable. For
instance, if \c stderr is detected to be attached to a file instead
of a TTY, no coloring will be done.
- ColorOutput does its best attempt. but it is generally undefined
+ QColorOutput does its best attempt. but it is generally undefined
what coloring or effect the various coloring flags has. It depends
strongly on what terminal software that is being used.
@@ -129,14 +129,14 @@ const char *const QColorOutputPrivate::backgrounds[] =
escaped the backslash. That's why we below use characters with
value 0x1B.
- It can be convenient to subclass ColorOutput with a private scope,
+ It can be convenient to subclass QColorOutput with a private scope,
such that the functions are directly available in the class using
it.
\section1 Usage
To output messages, call write() or writeUncolored(). write() takes
- as second argument an integer, which ColorOutput uses as a lookup
+ as second argument an integer, which QColorOutput uses as a lookup
key to find the color it should color the text in. The mapping from
keys to colors is done using insertMapping(). Typically this is used
by having enums for the various kinds of messages, which
@@ -149,9 +149,9 @@ const char *const QColorOutputPrivate::backgrounds[] =
Important
};
- ColorOutput output;
- output.insertMapping(Error, ColorOutput::RedForeground);
- output.insertMapping(Import, ColorOutput::BlueForeground);
+ QColorOutput output;
+ output.insertMapping(Error, QColorOutput::RedForeground);
+ output.insertMapping(Import, QColorOutput::BlueForeground);
output.write("This is important", Important);
output.write("Jack, I'm only the selected official!", Error);
@@ -165,7 +165,8 @@ const char *const QColorOutputPrivate::backgrounds[] =
*/
/*!
- \enum ColorOutput::ColorCodeComponent
+ \internal
+ \enum QColorOutput::ColorCodeComponent
\value BlackForeground
\value BlueForeground
\value GreenForeground
@@ -190,13 +191,14 @@ const char *const QColorOutputPrivate::backgrounds[] =
\value PurpleBackground
\value BrownBackground
- \value DefaultColor ColorOutput performs no coloring. This typically
+ \value DefaultColor QColorOutput performs no coloring. This typically
means black on white or white on black, depending
on the settings of the user's terminal.
*/
/*!
- Constructs a ColorOutput instance, ready for use.
+ \internal
+ Constructs a QColorOutput instance, ready for use.
*/
QColorOutput::QColorOutput() : d(new QColorOutputPrivate) {}
@@ -207,11 +209,12 @@ bool QColorOutput::isSilent() const { return d->isSilent(); }
void QColorOutput::setSilent(bool silent) { d->setSilent(silent); }
/*!
+ \internal
Sends \a message to \c stderr, using the color looked up in the color mapping using \a colorID.
If \a color isn't available in the color mapping, result and behavior is undefined.
- If \a colorID is 0, which is the default value, the previously used coloring is used. ColorOutput
+ If \a colorID is 0, which is the default value, the previously used coloring is used. QColorOutput
is initialized to not color at all.
If \a message is empty, effects are undefined.
@@ -241,10 +244,11 @@ void QColorOutput::writePrefixedMessage(const QString &message, QtMsgType type,
}
/*!
+ \internal
Writes \a message to \c stderr as if for instance
QTextStream would have been used, and adds a line ending at the end.
- This function can be practical to use such that one can use ColorOutput for all forms of writing.
+ This function can be practical to use such that one can use QColorOutput for all forms of writing.
*/
void QColorOutput::writeUncolored(const QString &message)
{
@@ -253,6 +257,7 @@ void QColorOutput::writeUncolored(const QString &message)
}
/*!
+ \internal
Treats \a message and \a colorID identically to write(), but instead of writing
\a message to \c stderr, it is prepared for being written to \c stderr, but is then
returned.
@@ -309,7 +314,8 @@ QString QColorOutput::colorify(const QStringView message, int colorID) const
}
/*!
- Adds a color mapping from \a colorID to \a colorCode, for this ColorOutput instance.
+ \internal
+ Adds a color mapping from \a colorID to \a colorCode, for this QColorOutput instance.
*/
void QColorOutput::insertMapping(int colorID, const ColorCode colorCode)
{
diff --git a/src/qmlcompiler/qcoloroutput_p.h b/src/qmlcompiler/qcoloroutput_p.h
index c53b666802..af80e13f7e 100644
--- a/src/qmlcompiler/qcoloroutput_p.h
+++ b/src/qmlcompiler/qcoloroutput_p.h
@@ -14,7 +14,7 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include <QtCore/private/qglobal_p.h>
#include <QtCore/qscopedpointer.h>
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QColorOutputPrivate;
-class Q_QMLCOMPILER_PRIVATE_EXPORT QColorOutput
+class Q_QMLCOMPILER_EXPORT QColorOutput
{
enum
{
diff --git a/src/qmlcompiler/qdeferredpointer_p.h b/src/qmlcompiler/qdeferredpointer_p.h
index 481c0e27ae..e4d5eb6df3 100644
--- a/src/qmlcompiler/qdeferredpointer_p.h
+++ b/src/qmlcompiler/qdeferredpointer_p.h
@@ -14,7 +14,7 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include <QtCore/private/qglobal_p.h>
#include <QtCore/qsharedpointer.h>
@@ -47,18 +47,18 @@ class QDeferredSharedPointer
public:
using Factory = QDeferredFactory<std::remove_const_t<T>>;
- QDeferredSharedPointer() = default;
+ Q_NODISCARD_CTOR QDeferredSharedPointer() = default;
- QDeferredSharedPointer(QSharedPointer<T> data)
- : m_data(data)
+ Q_NODISCARD_CTOR QDeferredSharedPointer(QSharedPointer<T> data)
+ : m_data(std::move(data))
{}
- QDeferredSharedPointer(QWeakPointer<T> data)
- : m_data(data)
+ Q_NODISCARD_CTOR QDeferredSharedPointer(QWeakPointer<T> data)
+ : m_data(std::move(data))
{}
- QDeferredSharedPointer(QSharedPointer<T> data, QSharedPointer<Factory> factory)
- : m_data(data), m_factory(factory)
+ Q_NODISCARD_CTOR QDeferredSharedPointer(QSharedPointer<T> data, QSharedPointer<Factory> factory)
+ : m_data(std::move(data)), m_factory(std::move(factory))
{
// You have to provide a valid pointer if you provide a factory. We cannot allocate the
// pointer for you because then two copies of the same QDeferredSharedPointer will diverge
@@ -66,7 +66,7 @@ public:
Q_ASSERT(!m_data.isNull() || m_factory.isNull());
}
- operator QSharedPointer<T>() const
+ [[nodiscard]] operator QSharedPointer<T>() const
{
lazyLoad();
return m_data;
@@ -74,8 +74,8 @@ public:
operator QDeferredSharedPointer<const T>() const { return { m_data, m_factory }; }
- T &operator*() const { return QSharedPointer<T>(*this).operator*(); }
- T *operator->() const { return QSharedPointer<T>(*this).operator->(); }
+ [[nodiscard]] T &operator*() const { return QSharedPointer<T>(*this).operator*(); }
+ [[nodiscard]] T *operator->() const { return QSharedPointer<T>(*this).operator->(); }
bool isNull() const
{
@@ -85,8 +85,8 @@ public:
explicit operator bool() const noexcept { return !isNull(); }
bool operator !() const noexcept { return isNull(); }
- T *data() const { return QSharedPointer<T>(*this).data(); }
- T *get() const { return data(); }
+ [[nodiscard]] T *data() const { return QSharedPointer<T>(*this).data(); }
+ [[nodiscard]] T *get() const { return data(); }
friend size_t qHash(const QDeferredSharedPointer &ptr, size_t seed = 0)
{
@@ -177,31 +177,31 @@ class QDeferredWeakPointer
public:
using Factory = QDeferredFactory<std::remove_const_t<T>>;
- QDeferredWeakPointer() = default;
+ Q_NODISCARD_CTOR QDeferredWeakPointer() = default;
- QDeferredWeakPointer(const QDeferredSharedPointer<T> &strong)
+ Q_NODISCARD_CTOR QDeferredWeakPointer(const QDeferredSharedPointer<T> &strong)
: m_data(strong.m_data), m_factory(strong.m_factory)
{
}
- QDeferredWeakPointer(QWeakPointer<T> data, QWeakPointer<Factory> factory)
+ Q_NODISCARD_CTOR QDeferredWeakPointer(QWeakPointer<T> data, QWeakPointer<Factory> factory)
: m_data(data), m_factory(factory)
{}
- operator QWeakPointer<T>() const
+ [[nodiscard]] operator QWeakPointer<T>() const
{
lazyLoad();
return m_data;
}
- operator QDeferredSharedPointer<T>() const
+ [[nodiscard]] operator QDeferredSharedPointer<T>() const
{
return QDeferredSharedPointer<T>(m_data.toStrongRef(), m_factory.toStrongRef());
}
operator QDeferredWeakPointer<const T>() const { return {m_data, m_factory}; }
- QSharedPointer<T> toStrongRef() const
+ [[nodiscard]] QSharedPointer<T> toStrongRef() const
{
return QWeakPointer<T>(*this).toStrongRef();
}
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp
index 3583aa2405..392a8b9ba7 100644
--- a/src/qmlcompiler/qqmljsbasicblocks.cpp
+++ b/src/qmlcompiler/qqmljsbasicblocks.cpp
@@ -2,48 +2,113 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qqmljsbasicblocks_p.h"
+#include "qqmljsutils_p.h"
+
+#include <QtQml/private/qv4instr_moth_p.h>
QT_BEGIN_NAMESPACE
-template<typename Container>
-void deduplicate(Container &container)
+using namespace Qt::Literals::StringLiterals;
+
+DEFINE_BOOL_CONFIG_OPTION(qv4DumpBasicBlocks, QV4_DUMP_BASIC_BLOCKS)
+DEFINE_BOOL_CONFIG_OPTION(qv4ValidateBasicBlocks, QV4_VALIDATE_BASIC_BLOCKS)
+
+void QQmlJSBasicBlocks::dumpBasicBlocks()
{
- std::sort(container.begin(), container.end());
- auto erase = std::unique(container.begin(), container.end());
- container.erase(erase, container.end());
+ qDebug().noquote() << "=== Basic Blocks for \"%1\""_L1.arg(m_context->name);
+ for (const auto &[blockOffset, block] : m_basicBlocks) {
+ QDebug debug = qDebug().nospace();
+ debug << "Block " << (blockOffset < 0 ? "Function prolog"_L1 : QString::number(blockOffset))
+ << ":\n";
+ debug << " jumpOrigins[" << block.jumpOrigins.size() << "]: ";
+ for (auto origin : block.jumpOrigins) {
+ debug << origin << ", ";
+ }
+ debug << "\n readRegisters[" << block.readRegisters.size() << "]: ";
+ for (auto reg : block.readRegisters) {
+ debug << reg << ", ";
+ }
+ debug << "\n readTypes[" << block.readTypes.size() << "]: ";
+ for (const auto &type : block.readTypes) {
+ debug << type->augmentedInternalName() << ", ";
+ }
+ debug << "\n jumpTarget: " << block.jumpTarget;
+ debug << "\n jumpIsUnConditional: " << block.jumpIsUnconditional;
+ debug << "\n isReturnBlock: " << block.isReturnBlock;
+ debug << "\n isThrowBlock: " << block.isThrowBlock;
+ }
+ qDebug() << "\n";
}
-static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
+void QQmlJSBasicBlocks::dumpDOTGraph()
{
- using Type = QV4::Moth::Instr::Type;
- switch (type) {
- case Type::PopContext:
- case Type::PopScriptContext:
- case Type::CreateCallContext:
- case Type::CreateCallContext_Wide:
- case Type::PushCatchContext:
- case Type::PushCatchContext_Wide:
- case Type::PushWithContext:
- case Type::PushWithContext_Wide:
- case Type::PushBlockContext:
- case Type::PushBlockContext_Wide:
- case Type::CloneBlockContext:
- case Type::CloneBlockContext_Wide:
- case Type::PushScriptContext:
- case Type::PushScriptContext_Wide:
- return true;
- default:
- break;
+ QString output;
+ QTextStream s{ &output };
+ s << "=== Basic Blocks Graph in DOT format for \"%1\" (spaces are encoded as"
+ " &#160; to preserve formatting)\n"_L1.arg(m_context->name);
+ s << "digraph BasicBlocks {\n"_L1;
+
+ QFlatMap<int, BasicBlock> blocks{ m_basicBlocks };
+ for (const auto &[blockOffset, block] : blocks) {
+ for (int originOffset : block.jumpOrigins) {
+ const auto originBlockIt = basicBlockForInstruction(blocks, originOffset);
+ const auto isBackEdge = originOffset > blockOffset && originBlockIt->second.jumpIsUnconditional;
+ s << " %1 -> %2%3\n"_L1.arg(QString::number(originBlockIt.key()))
+ .arg(QString::number(blockOffset))
+ .arg(isBackEdge ? " [color=blue]"_L1 : ""_L1);
+ }
+ }
+
+ for (const auto &[blockOffset, block] : blocks) {
+ if (blockOffset < 0) {
+ s << " %1 [shape=record, fontname=\"Monospace\", label=\"Function Prolog\"]\n"_L1
+ .arg(QString::number(blockOffset));
+ continue;
+ }
+
+ auto nextBlockIt = blocks.lower_bound(blockOffset + 1);
+ int nextBlockOffset = nextBlockIt == blocks.end() ? m_context->code.size() : nextBlockIt->first;
+ QString dump = QV4::Moth::dumpBytecode(
+ m_context->code.constData(), m_context->code.size(), m_context->locals.size(),
+ m_context->formals->length(), blockOffset, nextBlockOffset - 1,
+ m_context->lineAndStatementNumberMapping);
+ dump = dump.replace(" "_L1, "&#160;"_L1); // prevent collapse of extra whitespace for formatting
+ dump = dump.replace("\n"_L1, "\\l"_L1); // new line + left aligned
+ s << " %1 [shape=record, fontname=\"Monospace\", label=\"{Block %1: | %2}\"]\n"_L1
+ .arg(QString::number(blockOffset))
+ .arg(dump);
+ }
+ s << "}\n"_L1;
+
+ // Have unique names to prevent overwriting of functions with the same name (eg. anonymous functions).
+ static int functionCount = 0;
+ static const auto dumpFolderPath = qEnvironmentVariable("QV4_DUMP_BASIC_BLOCKS");
+
+ QString expressionName = m_context->name == ""_L1
+ ? "anonymous"_L1
+ : QString(m_context->name).replace(" "_L1, "_"_L1);
+ QString fileName = "function"_L1 + QString::number(functionCount++) + "_"_L1 + expressionName + ".gv"_L1;
+ QFile dumpFile(dumpFolderPath + (dumpFolderPath.endsWith("/"_L1) ? ""_L1 : "/"_L1) + fileName);
+
+ if (dumpFolderPath == "-"_L1 || dumpFolderPath == "1"_L1 || dumpFolderPath == "true"_L1) {
+ qDebug().noquote() << output;
+ } else {
+ if (!dumpFile.open(QIODeviceBase::Truncate | QIODevice::WriteOnly)) {
+ qDebug() << "Error: Could not open file to dump the basic blocks into";
+ } else {
+ dumpFile.write(("//"_L1 + output).toLatin1().toStdString().c_str());
+ dumpFile.close();
+ }
}
- return false;
}
-QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run(
- const Function *function,
- const InstructionAnnotations &annotations)
+QQmlJSCompilePass::BlocksAndAnnotations
+QQmlJSBasicBlocks::run(const Function *function, QQmlJSAotCompiler::Flags compileFlags,
+ bool &basicBlocksValidationFailed)
{
+ basicBlocksValidationFailed = false;
+
m_function = function;
- m_annotations = annotations;
for (int i = 0, end = function->argumentTypes.size(); i != end; ++i) {
InstructionAnnotation annotation;
@@ -59,7 +124,11 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run(
m_annotations[-annotation.changedRegisterIndex] = annotation;
}
+ // Insert the function prolog block followed by the first "real" block.
m_basicBlocks.insert_or_assign(m_annotations.begin().key(), BasicBlock());
+ BasicBlock zeroBlock;
+ zeroBlock.jumpOrigins.append(m_basicBlocks.begin().key());
+ m_basicBlocks.insert(0, zeroBlock);
const QByteArray byteCode = function->code;
decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
@@ -74,16 +143,27 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run(
it->second.jumpIsUnconditional = false;
}
+ m_skipUntilNextLabel = false;
+
reset();
decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it)
- deduplicate(it->second.jumpOrigins);
+ QQmlJSUtils::deduplicate(it->second.jumpOrigins);
}
- populateBasicBlocks();
- populateReaderLocations();
- adjustTypes();
- return std::move(m_annotations);
+ if (compileFlags.testFlag(QQmlJSAotCompiler::ValidateBasicBlocks) || qv4ValidateBasicBlocks()) {
+ if (auto validationResult = basicBlocksValidation(); !validationResult.success) {
+ qDebug() << "Basic blocks validation failed: %1."_L1.arg(validationResult.errorMessage);
+ basicBlocksValidationFailed = true;
+ }
+ }
+
+ if (qv4DumpBasicBlocks()) {
+ dumpBasicBlocks();
+ dumpDOTGraph();
+ }
+
+ return { std::move(m_basicBlocks), std::move(m_annotations) };
}
QV4::Moth::ByteCodeHandler::Verdict QQmlJSBasicBlocks::startInstruction(QV4::Moth::Instr::Type type)
@@ -132,22 +212,64 @@ void QQmlJSBasicBlocks::generate_JumpNotUndefined(int offset)
processJump(offset, Conditional);
}
+void QQmlJSBasicBlocks::generate_IteratorNext(int value, int offset)
+{
+ Q_UNUSED(value);
+ processJump(offset, Conditional);
+}
+
+void QQmlJSBasicBlocks::generate_GetOptionalLookup(int index, int offset)
+{
+ Q_UNUSED(index);
+ processJump(offset, Conditional);
+}
+
void QQmlJSBasicBlocks::generate_Ret()
{
+ auto currentBlock = basicBlockForInstruction(m_basicBlocks, currentInstructionOffset());
+ currentBlock.value().isReturnBlock = true;
m_skipUntilNextLabel = true;
}
void QQmlJSBasicBlocks::generate_ThrowException()
{
+ auto currentBlock = basicBlockForInstruction(m_basicBlocks, currentInstructionOffset());
+ currentBlock.value().isThrowBlock = true;
m_skipUntilNextLabel = true;
}
-void QQmlJSBasicBlocks::generate_DefineArray(int argc, int)
+void QQmlJSBasicBlocks::generate_DefineArray(int argc, int argv)
+{
+ if (argc == 0)
+ return; // empty array/list, nothing to do
+
+ m_objectAndArrayDefinitions.append({
+ currentInstructionOffset(), ObjectOrArrayDefinition::ArrayClassId, argc, argv
+ });
+}
+
+void QQmlJSBasicBlocks::generate_DefineObjectLiteral(int internalClassId, int argc, int argv)
{
if (argc == 0)
+ return;
+
+ m_objectAndArrayDefinitions.append({ currentInstructionOffset(), internalClassId, argc, argv });
+}
+
+void QQmlJSBasicBlocks::generate_Construct(int func, int argc, int argv)
+{
+ Q_UNUSED(func)
+ if (argc == 0)
return; // empty array/list, nothing to do
- m_arrayDefinitions.append(currentInstructionOffset());
+ m_objectAndArrayDefinitions.append({
+ currentInstructionOffset(),
+ argc == 1
+ ? ObjectOrArrayDefinition::ArrayConstruct1ArgId
+ : ObjectOrArrayDefinition::ArrayClassId,
+ argc,
+ argv
+ });
}
void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode)
@@ -156,400 +278,76 @@ void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode)
m_hadBackJumps = true;
const int jumpTarget = absoluteOffset(offset);
Q_ASSERT(!m_basicBlocks.isEmpty());
- auto currentBlock = m_basicBlocks.lower_bound(currentInstructionOffset());
- if (currentBlock == m_basicBlocks.end() || currentBlock->first != currentInstructionOffset())
- --currentBlock;
+ auto currentBlock = basicBlockForInstruction(m_basicBlocks, currentInstructionOffset());
currentBlock->second.jumpTarget = jumpTarget;
currentBlock->second.jumpIsUnconditional = (mode == Unconditional);
m_basicBlocks[jumpTarget].jumpOrigins.append(currentInstructionOffset());
if (mode == Unconditional)
m_skipUntilNextLabel = true;
else
- m_basicBlocks[nextInstructionOffset()].jumpOrigins.append(currentInstructionOffset());
+ m_basicBlocks.insert(nextInstructionOffset(), BasicBlock());
}
-template<typename ContainerA, typename ContainerB>
-static bool containsAny(const ContainerA &container, const ContainerB &elements)
+QQmlJSCompilePass::BasicBlocks::iterator QQmlJSBasicBlocks::basicBlockForInstruction(
+ QFlatMap<int, BasicBlock> &container, int instructionOffset)
{
- for (const auto &element : elements) {
- if (container.contains(element))
- return true;
- }
- return false;
+ auto block = container.lower_bound(instructionOffset);
+ if (block == container.end() || block->first != instructionOffset)
+ --block;
+ return block;
}
-template<typename ContainerA, typename ContainerB>
-static bool containsAll(const ContainerA &container, const ContainerB &elements)
+QQmlJSCompilePass::BasicBlocks::const_iterator QQmlJSBasicBlocks::basicBlockForInstruction(
+ const BasicBlocks &container, int instructionOffset)
{
- for (const auto &element : elements) {
- if (!container.contains(element))
- return false;
- }
- return true;
+ return basicBlockForInstruction(const_cast<BasicBlocks &>(container), instructionOffset);
}
-template<class Key, class T, class Compare = std::less<Key>,
- class KeyContainer = QList<Key>, class MappedContainer = QList<T>>
-class NewFlatMap
+QQmlJSBasicBlocks::BasicBlocksValidationResult QQmlJSBasicBlocks::basicBlocksValidation()
{
-public:
- using OriginalFlatMap = QFlatMap<Key, T, Compare, KeyContainer, MappedContainer>;
-
- void appendOrdered(const typename OriginalFlatMap::iterator &i) {
- keys.append(i.key());
- values.append(i.value());
- }
-
- OriginalFlatMap take() {
- OriginalFlatMap result(Qt::OrderedUniqueRange, std::move(keys), std::move(values));
- keys.clear();
- values.clear();
- return result;
- }
-
-private:
- typename OriginalFlatMap::key_container_type keys;
- typename OriginalFlatMap::mapped_container_type values;
-};
-
-struct PendingBlock
-{
- QList<int> conversions;
- int start = -1;
- bool registerActive = false;
-};
-
-void QQmlJSBasicBlocks::populateReaderLocations()
-{
- using NewInstructionAnnotations = NewFlatMap<int, InstructionAnnotation>;
-
- bool erasedReaders = false;
- auto eraseDeadStore = [&](const InstructionAnnotations::iterator &it) {
- auto reader = m_readerLocations.find(it.key());
- if (reader != m_readerLocations.end()
- && (reader->typeReaders.isEmpty()
- || reader->registerReadersAndConversions.isEmpty())) {
-
- if (it->second.isRename) {
- // If it's a rename, it doesn't "own" its output type. The type may
- // still be read elsewhere, even if this register isn't. However, we're
- // not interested in the variant or any other details of the register.
- // Therefore just delete it.
- it->second.changedRegisterIndex = InvalidRegister;
- it->second.changedRegister = QQmlJSRegisterContent();
- } else {
- // void the output, rather than deleting it. We still need its variant.
- m_typeResolver->adjustTrackedType(
- it->second.changedRegister.storedType(),
- m_typeResolver->voidType());
- m_typeResolver->adjustTrackedType(
- m_typeResolver->containedType(it->second.changedRegister),
- m_typeResolver->voidType());
- }
- m_readerLocations.erase(reader);
-
- // If it's not a label and has no side effects, we can drop the instruction.
- if (!it->second.hasSideEffects) {
- if (!it->second.readRegisters.isEmpty()) {
- it->second.readRegisters.clear();
- erasedReaders = true;
- }
- if (m_basicBlocks.find(it.key()) == m_basicBlocks.end())
- return true;
- }
- }
- return false;
- };
-
- NewInstructionAnnotations newAnnotations;
- for (auto writeIt = m_annotations.begin(), writeEnd = m_annotations.end();
- writeIt != writeEnd; ++writeIt) {
- const int writtenRegister = writeIt->second.changedRegisterIndex;
- if (writtenRegister == InvalidRegister) {
- newAnnotations.appendOrdered(writeIt);
- continue;
- }
-
- RegisterAccess &access = m_readerLocations[writeIt.key()];
- access.trackedRegister = writtenRegister;
- if (writeIt->second.changedRegister.isConversion()) {
- // If it's a conversion, we have to check for all readers of the conversion origins.
- // This happens at jump targets where different types are merged. A StoreReg or similar
- // instruction must be optimized out if none of the types it can hold is read anymore.
- access.trackedTypes = writeIt->second.changedRegister.conversionOrigins();
- } else {
- access.trackedTypes.append(
- m_typeResolver->trackedContainedType(writeIt->second.changedRegister));
- }
-
- auto blockIt = m_basicBlocks.lower_bound(writeIt.key());
- if (blockIt == m_basicBlocks.end() || blockIt->first != writeIt.key())
- --blockIt;
-
- QList<PendingBlock> blocks = { { {}, blockIt->first, true } };
- QHash<int, PendingBlock> processedBlocks;
- bool isFirstBlock = true;
-
- while (!blocks.isEmpty()) {
- const PendingBlock block = blocks.takeLast();
-
- // We can re-enter the first block from the beginning.
- // We will then find any reads before the write we're currently examining.
- if (!isFirstBlock)
- processedBlocks.insert(block.start, block);
-
- auto nextBlock = m_basicBlocks.find(block.start);
- auto currentBlock = nextBlock++;
- bool registerActive = block.registerActive;
- QList<int> conversions = block.conversions;
-
- const auto blockEnd = (nextBlock == m_basicBlocks.end())
- ? m_annotations.end()
- : m_annotations.find(nextBlock->first);
-
- auto blockInstr = isFirstBlock
- ? (writeIt + 1)
- : m_annotations.find(currentBlock->first);
- for (; blockInstr != blockEnd; ++blockInstr) {
- if (registerActive
- && blockInstr->second.typeConversions.contains(writtenRegister)) {
- conversions.append(blockInstr.key());
- }
-
- for (auto readIt = blockInstr->second.readRegisters.constBegin(),
- end = blockInstr->second.readRegisters.constEnd();
- readIt != end; ++readIt) {
- if (!blockInstr->second.isRename && containsAny(
- readIt->second.content.conversionOrigins(), access.trackedTypes)) {
- Q_ASSERT(readIt->second.content.isConversion());
- access.typeReaders[blockInstr.key()]
- = readIt->second.content.conversionResult();
- }
- if (registerActive && readIt->first == writtenRegister)
- access.registerReadersAndConversions[blockInstr.key()] = conversions;
- }
-
- if (blockInstr->second.changedRegisterIndex == writtenRegister) {
- conversions.clear();
- registerActive = false;
- }
- }
-
- auto scheduleBlock = [&](int blockStart) {
- // If we find that an already processed block has the register activated by this jump,
- // we need to re-evaluate it. We also need to propagate any newly found conversions.
- const auto processed = processedBlocks.find(blockStart);
- if (processed == processedBlocks.end())
- blocks.append({conversions, blockStart, registerActive});
- else if (registerActive && !processed->registerActive)
- blocks.append({conversions, blockStart, registerActive});
- else if (!containsAll(processed->conversions, conversions))
- blocks.append({processed->conversions + conversions, blockStart, registerActive});
- };
-
- if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end())
- scheduleBlock(nextBlock->first);
-
- const int jumpTarget = currentBlock->second.jumpTarget;
- if (jumpTarget != -1)
- scheduleBlock(jumpTarget);
-
- if (isFirstBlock)
- isFirstBlock = false;
- }
-
- if (!eraseDeadStore(writeIt))
- newAnnotations.appendOrdered(writeIt);
+ if (m_basicBlocks.empty())
+ return {};
+
+ const QFlatMap<int, BasicBlock> blocks{ m_basicBlocks };
+ QList<QFlatMap<int, BasicBlock>::const_iterator> returnOrThrowBlocks;
+ for (auto it = blocks.cbegin(); it != blocks.cend(); ++it) {
+ if (it.value().isReturnBlock || it.value().isThrowBlock)
+ returnOrThrowBlocks.append(it);
}
- m_annotations = newAnnotations.take();
-
- while (erasedReaders) {
- erasedReaders = false;
-
- for (auto it = m_annotations.begin(), end = m_annotations.end(); it != end; ++it) {
- InstructionAnnotation &instruction = it->second;
- if (instruction.changedRegisterIndex < InvalidRegister) {
- newAnnotations.appendOrdered(it);
- continue;
- }
-
- auto readers = m_readerLocations.find(it.key());
- if (readers != m_readerLocations.end()) {
- for (auto typeIt = readers->typeReaders.begin();
- typeIt != readers->typeReaders.end();) {
- if (m_annotations.contains(typeIt.key()))
- ++typeIt;
- else
- typeIt = readers->typeReaders.erase(typeIt);
- }
-
- for (auto registerIt = readers->registerReadersAndConversions.begin();
- registerIt != readers->registerReadersAndConversions.end();) {
- if (m_annotations.contains(registerIt.key()))
- ++registerIt;
- else
- registerIt = readers->registerReadersAndConversions.erase(registerIt);
- }
- }
-
- if (!eraseDeadStore(it))
- newAnnotations.appendOrdered(it);
- }
- m_annotations = newAnnotations.take();
+ // 1. Return blocks and throw blocks must not have a jump target
+ for (const auto &it : returnOrThrowBlocks) {
+ if (it.value().jumpTarget != -1)
+ return { false, "Return or throw block jumps to somewhere"_L1 };
}
-}
-
-bool QQmlJSBasicBlocks::canMove(int instructionOffset, const RegisterAccess &access) const
-{
- if (access.registerReadersAndConversions.size() != 1)
- return false;
- const auto basicBlockForInstruction = [this](int instruction) {
- auto block = m_basicBlocks.lower_bound(instruction);
- if (block == m_basicBlocks.end() || block.key() == instruction)
- return block;
- Q_ASSERT(block.key() > instruction);
- if (block == m_basicBlocks.begin())
- return m_basicBlocks.end();
- return --block;
- };
- return basicBlockForInstruction(instructionOffset)
- == basicBlockForInstruction(access.registerReadersAndConversions.begin().key());
-}
-void QQmlJSBasicBlocks::adjustTypes()
-{
- using NewVirtualRegisters = NewFlatMap<int, VirtualRegister>;
-
- QHash<int, QList<int>> liveConversions;
- QHash<int, QList<int>> movableReads;
-
- const auto handleRegisterReadersAndConversions
- = [&](QHash<int, RegisterAccess>::const_iterator it) {
- for (auto conversions = it->registerReadersAndConversions.constBegin(),
- end = it->registerReadersAndConversions.constEnd(); conversions != end;
- ++conversions) {
- if (conversions->isEmpty() && canMove(it.key(), it.value()))
- movableReads[conversions.key()].append(it->trackedRegister);
- for (int conversion : *conversions)
- liveConversions[conversion].append(it->trackedRegister);
+ // 2. The basic blocks graph must be connected.
+ QSet<int> visitedBlockOffsets;
+ QList<QFlatMap<int, BasicBlock>::const_iterator> toVisit;
+ toVisit.append(returnOrThrowBlocks);
+
+ while (!toVisit.empty()) {
+ const auto &[offset, block] = *toVisit.takeLast();
+ visitedBlockOffsets.insert(offset);
+ for (int originOffset : block.jumpOrigins) {
+ const auto originBlock = basicBlockForInstruction(blocks, originOffset);
+ if (visitedBlockOffsets.find(originBlock.key()) == visitedBlockOffsets.end()
+ && !toVisit.contains(originBlock))
+ toVisit.append(originBlock);
}
- };
-
- // Handle the array definitions first.
- // Changing the array type changes the expected element types.
- for (int instructionOffset : m_arrayDefinitions) {
- auto it = m_readerLocations.find(instructionOffset);
- if (it == m_readerLocations.end())
- continue;
-
- const InstructionAnnotation &annotation = m_annotations[instructionOffset];
-
- Q_ASSERT(it->trackedTypes.size() == 1);
- Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister));
- Q_ASSERT(!annotation.readRegisters.isEmpty());
-
- m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values());
-
- // Now we don't adjust the type we store, but rather the type we expect to read. We
- // can do this because we've tracked the read type when we defined the array in
- // QQmlJSTypePropagator.
- if (QQmlJSScope::ConstPtr valueType = it->trackedTypes[0]->valueType()) {
- m_typeResolver->adjustTrackedType(
- m_typeResolver->containedType(annotation.readRegisters.begin().value().content),
- valueType);
- }
-
- handleRegisterReadersAndConversions(it);
- m_readerLocations.erase(it);
}
- for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
- handleRegisterReadersAndConversions(it);
+ if (visitedBlockOffsets.size() != blocks.size())
+ return { false, "Basic blocks graph is not fully connected"_L1 };
- // There is always one first occurrence of any tracked type. Conversions don't change
- // the type.
- if (it->trackedTypes.size() != 1)
- continue;
-
- m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values());
+ // 3. A block's jump target must be the first offset of a block.
+ for (const auto &[blockOffset, block] : blocks) {
+ auto target = block.jumpTarget;
+ if (target != -1 && blocks.find(target) == blocks.end())
+ return { false, "Invalid jump; target is not the start of a block"_L1 };
}
- const auto transformRegister = [&](QQmlJSRegisterContent &content) {
- m_typeResolver->adjustTrackedType(
- content.storedType(),
- m_typeResolver->storedType(m_typeResolver->containedType(content)));
- };
-
- NewVirtualRegisters newRegisters;
- for (auto i = m_annotations.begin(), iEnd = m_annotations.end(); i != iEnd; ++i) {
- if (i->second.changedRegisterIndex != InvalidRegister)
- transformRegister(i->second.changedRegister);
-
- for (auto conversion = i->second.typeConversions.begin(),
- conversionEnd = i->second.typeConversions.end(); conversion != conversionEnd;
- ++conversion) {
- if (!liveConversions[i.key()].contains(conversion.key()))
- continue;
-
- QQmlJSScope::ConstPtr conversionResult = conversion->second.content.conversionResult();
- const auto conversionOrigins = conversion->second.content.conversionOrigins();
- QQmlJSScope::ConstPtr newResult;
- for (const auto &origin : conversionOrigins)
- newResult = m_typeResolver->merge(newResult, origin);
- m_typeResolver->adjustTrackedType(conversionResult, newResult);
- transformRegister(conversion->second.content);
- newRegisters.appendOrdered(conversion);
- }
- i->second.typeConversions = newRegisters.take();
-
- for (int movable : std::as_const(movableReads[i.key()]))
- i->second.readRegisters[movable].canMove = true;
- }
-}
-
-void QQmlJSBasicBlocks::populateBasicBlocks()
-{
- for (auto blockNext = m_basicBlocks.begin(), blockEnd = m_basicBlocks.end();
- blockNext != blockEnd;) {
-
- const auto blockIt = blockNext++;
- BasicBlock &block = blockIt->second;
- QList<QQmlJSScope::ConstPtr> writtenTypes;
- QList<int> writtenRegisters;
-
- const auto instrEnd = (blockNext == blockEnd)
- ? m_annotations.end()
- : m_annotations.find(blockNext->first);
- for (auto instrIt = m_annotations.find(blockIt->first); instrIt != instrEnd; ++instrIt) {
- const InstructionAnnotation &instruction = instrIt->second;
- for (auto it = instruction.readRegisters.begin(), end = instruction.readRegisters.end();
- it != end; ++it) {
- if (!instruction.isRename) {
- Q_ASSERT(it->second.content.isConversion());
- for (const QQmlJSScope::ConstPtr &origin :
- it->second.content.conversionOrigins()) {
- if (!writtenTypes.contains(origin))
- block.readTypes.append(origin);
- }
- }
- if (!writtenRegisters.contains(it->first))
- block.readRegisters.append(it->first);
- }
-
- // If it's just a renaming, the type has existed in a different register before.
- if (instruction.changedRegisterIndex != InvalidRegister) {
- if (!instruction.isRename) {
- writtenTypes.append(m_typeResolver->trackedContainedType(
- instruction.changedRegister));
- }
- writtenRegisters.append(instruction.changedRegisterIndex);
- }
- }
-
- deduplicate(block.readTypes);
- deduplicate(block.readRegisters);
- }
+ return {};
}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h
index ae95014dd1..a443d422ea 100644
--- a/src/qmlcompiler/qqmljsbasicblocks_p.h
+++ b/src/qmlcompiler/qqmljsbasicblocks_p.h
@@ -15,41 +15,39 @@
// We mean it.
-#include <private/qqmljscompilepass_p.h>
#include <private/qflatmap_p.h>
+#include <private/qqmljscompilepass_p.h>
+#include <private/qqmljscompiler_p.h>
QT_BEGIN_NAMESPACE
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSBasicBlocks : public QQmlJSCompilePass
+class Q_QMLCOMPILER_EXPORT QQmlJSBasicBlocks : public QQmlJSCompilePass
{
public:
- struct BasicBlock {
- QList<int> jumpOrigins;
- QList<int> readRegisters;
- QList<QQmlJSScope::ConstPtr> readTypes;
- int jumpTarget = -1;
- bool jumpIsUnconditional = false;
- };
-
- QQmlJSBasicBlocks(const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ QQmlJSBasicBlocks(const QV4::Compiler::Context *context,
+ const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
- : QQmlJSCompilePass(unitGenerator, typeResolver, logger)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger), m_context{ context }
{
}
~QQmlJSBasicBlocks() = default;
- InstructionAnnotations run(const Function *function, const InstructionAnnotations &annotations);
+ QQmlJSCompilePass::BlocksAndAnnotations run(const Function *function,
+ QQmlJSAotCompiler::Flags compileFlags,
+ bool &basicBlocksValidationFailed);
-private:
- struct RegisterAccess
- {
- QList<QQmlJSScope::ConstPtr> trackedTypes;
- QHash<int, QQmlJSScope::ConstPtr> typeReaders;
- QHash<int, QList<int>> registerReadersAndConversions;
- int trackedRegister;
- };
+ struct BasicBlocksValidationResult { bool success = true; QString errorMessage; };
+ BasicBlocksValidationResult basicBlocksValidation();
+
+ static BasicBlocks::iterator
+ basicBlockForInstruction(QFlatMap<int, BasicBlock> &container, int instructionOffset);
+ static BasicBlocks::const_iterator
+ basicBlockForInstruction(const QFlatMap<int, BasicBlock> &container, int instructionOffset);
+ QList<ObjectOrArrayDefinition> objectAndArrayDefinitions() const;
+
+private:
QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type type) override;
void endInstruction(QV4::Moth::Instr::Type type) override;
@@ -58,23 +56,24 @@ private:
void generate_JumpFalse(int offset) override;
void generate_JumpNoException(int offset) override;
void generate_JumpNotUndefined(int offset) override;
+ void generate_IteratorNext(int value, int offset) override;
+ void generate_GetOptionalLookup(int index, int offset) override;
void generate_Ret() override;
void generate_ThrowException() override;
void generate_DefineArray(int argc, int argv) override;
+ void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override;
+ void generate_Construct(int func, int argc, int argv) override;
enum JumpMode { Unconditional, Conditional };
void processJump(int offset, JumpMode mode);
- void populateBasicBlocks();
- void populateReaderLocations();
- void adjustTypes();
- bool canMove(int instructionOffset, const RegisterAccess &access) const;
-
- InstructionAnnotations m_annotations;
- QFlatMap<int, BasicBlock> m_basicBlocks;
- QHash<int, RegisterAccess> m_readerLocations;
- QList<int> m_arrayDefinitions;
+
+ void dumpBasicBlocks();
+ void dumpDOTGraph();
+
+ const QV4::Compiler::Context *m_context;
+ QList<ObjectOrArrayDefinition> m_objectAndArrayDefinitions;
bool m_skipUntilNextLabel = false;
bool m_hadBackJumps = false;
};
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index a1be6ae37a..9f609f39fb 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -44,7 +44,6 @@ static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope
{
return !type.isNull()
&& !resolver->equals(type, resolver->nullType())
- && !resolver->equals(type, resolver->emptyListType())
&& !resolver->equals(type, resolver->voidType());
}
@@ -54,11 +53,11 @@ QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) c
}
QQmlJSCodeGenerator::QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext,
- const QV4::Compiler::JSUnitGenerator *unitGenerator,
- const QQmlJSTypeResolver *typeResolver,
- QQmlJSLogger *logger, const QStringList &sourceCodeLines)
- : QQmlJSCompilePass(unitGenerator, typeResolver, logger)
- , m_sourceCodeLines(sourceCodeLines)
+ const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver,
+ QQmlJSLogger *logger, BasicBlocks basicBlocks,
+ InstructionAnnotations annotations)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations)
, m_context(compilerContext)
{}
@@ -74,54 +73,100 @@ QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type)
+ u"\"); return t; }()"_s;
}
+QString QQmlJSCodeGenerator::compositeListMetaType(const QString &elementName) const
+{
+ return u"[](auto *aotContext) { static const auto t = QQmlPrivate::compositeListMetaType("
+ "aotContext->compilationUnit, QStringLiteral(\""_s
+ + elementName
+ + u"\")); return t; }(aotContext)"_s;
+}
+
+QString QQmlJSCodeGenerator::compositeMetaType(const QString &elementName) const
+{
+ return u"[](auto *aotContext) { static const auto t = QQmlPrivate::compositeMetaType("
+ "aotContext->compilationUnit, QStringLiteral(\""_s
+ + elementName
+ + u"\")); return t; }(aotContext)"_s;
+}
+
QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
{
- if (!objectType->isComposite()) {
- if (objectType->internalName() == u"QObject"_s
- || objectType->internalName() == u"QQmlComponent"_s) {
- return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
+ if (objectType->isComposite()) {
+ const QString name = m_typeResolver->nameForType(objectType);
+ if (name.isEmpty()) {
+ reject(u"retrieving the metaObject of a composite type without an element name."_s);
+ return QString();
}
- return metaTypeFromName(objectType) + u".metaObject()"_s;
+ return compositeMetaType(name) + u".metaObject()"_s;
}
- reject(u"retrieving the metaObject of a composite type without using an instance."_s);
- return QString();
+ if (objectType->internalName() == u"QObject"_s
+ || objectType->internalName() == u"QQmlComponent"_s) {
+ return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
+ }
+ return metaTypeFromName(objectType) + u".metaObject()"_s;
}
-QQmlJSAotFunction QQmlJSCodeGenerator::run(
- const Function *function, const InstructionAnnotations *annotations,
- QQmlJS::DiagnosticMessage *error)
+QString QQmlJSCodeGenerator::metaType(const QQmlJSScope::ConstPtr &type)
+{
+ if (type->isComposite()) {
+ const QString name = m_typeResolver->nameForType(type);
+ if (!name.isEmpty())
+ return compositeMetaType(name);
+ }
+
+ if (type->isListProperty() && type->valueType()->isComposite()) {
+ const QString name = m_typeResolver->nameForType(type->valueType());
+ if (!name.isEmpty())
+ return compositeListMetaType(name);
+ }
+
+ return m_typeResolver->equals(m_typeResolver->genericType(type), type)
+ ? metaTypeFromType(type)
+ : metaTypeFromName(type);
+}
+
+QQmlJSAotFunction QQmlJSCodeGenerator::run(const Function *function,
+ QQmlJS::DiagnosticMessage *error,
+ bool basicBlocksValidationFailed)
{
- m_annotations = annotations;
m_function = function;
m_error = error;
- QHash<int, QHash<QQmlJSScope::ConstPtr, QString>> registerNames;
+ QHash<int, int> numRegisterVariablesPerIndex;
- auto addVariable = [&](int registerIndex, const QQmlJSScope::ConstPtr &seenType) {
+ const auto addVariable
+ = [&](int registerIndex, int lookupIndex, const QQmlJSScope::ConstPtr &seenType) {
// Don't generate any variables for registers that are initialized with undefined.
if (registerIndex == InvalidRegister || !isTypeStorable(m_typeResolver, seenType))
return;
- auto &typesForRegisters = m_registerVariables[registerIndex];
- if (!typesForRegisters.contains(seenType)) {
- auto &currentRegisterNames = registerNames[registerIndex];
- QString &name = currentRegisterNames[m_typeResolver->comparableType(seenType)];
- if (name.isEmpty())
- name = u"r%1_%2"_s.arg(registerIndex).arg(currentRegisterNames.size());
- typesForRegisters[seenType] = name;
+ const RegisterVariablesKey key = { seenType->internalName(), registerIndex, lookupIndex };
+
+
+ const auto oldSize = m_registerVariables.size();
+ auto &e = m_registerVariables[key];
+ if (m_registerVariables.size() != oldSize) {
+ e.variableName = u"r%1_%2"_s
+ .arg(registerIndex)
+ .arg(numRegisterVariablesPerIndex[registerIndex]++);
+ e.storedType = m_typeResolver->comparableType(seenType);
}
+ ++e.numTracked;
};
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
- for (const auto &annotation : *m_annotations) {
+ for (const auto &annotation : m_annotations) {
addVariable(annotation.second.changedRegisterIndex,
+ annotation.second.changedRegister.resultLookupIndex(),
annotation.second.changedRegister.storedType());
for (auto it = annotation.second.typeConversions.begin(),
end = annotation.second.typeConversions.end();
it != end; ++it) {
- addVariable(it.key(), it.value().content.storedType());
+ addVariable(
+ it.key(), it.value().content.resultLookupIndex(),
+ it.value().content.storedType());
}
}
QT_WARNING_POP
@@ -141,144 +186,201 @@ QT_WARNING_POP
QQmlJSAotFunction result;
result.includes.swap(m_includes);
- QDuplicateTracker<QString> generatedVariables;
+ if (basicBlocksValidationFailed) {
+ result.code += "// QV4_BASIC_BLOCK_VALIDATION_FAILED: This file failed compilation "_L1
+ "with basic blocks validation but compiled without it.\n"_L1;
+ }
+
+ result.code += u"// %1 at line %2, column %3\n"_s
+ .arg(m_context->name).arg(m_context->line).arg(m_context->column);
+
for (auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
registerIt != registerEnd; ++registerIt) {
- const auto &registerTypes = *registerIt;
- const int registerIndex = registerIt.key();
+ const int registerIndex = registerIt.key().registerIndex;
const bool registerIsArgument = isArgument(registerIndex);
- for (auto registerTypeIt = registerTypes.constBegin(), end = registerTypes.constEnd();
- registerTypeIt != end; ++registerTypeIt) {
+ result.code += registerIt.key().internalName;
- const QQmlJSScope::ConstPtr storedType = registerTypeIt.key();
+ const QQmlJSScope::ConstPtr storedType = registerIt->storedType;
+ const bool isPointer
+ = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
+ if (isPointer)
+ result.code += u" *"_s;
+ else
+ result.code += u' ';
+
+ if (!registerIsArgument
+ && registerIndex != Accumulator
+ && registerIndex != This
+ && !m_typeResolver->registerIsStoredIn(
+ function->registerTypes[registerIndex - firstRegisterIndex()],
+ m_typeResolver->voidType())) {
+ result.code += registerIt->variableName + u" = "_s;
+ result.code += convertStored(m_typeResolver->voidType(), storedType, QString());
+ } else if (registerIsArgument && m_typeResolver->registerIsStoredIn(
+ argumentType(registerIndex), storedType)) {
+ const int argumentIndex = registerIndex - FirstArgument;
+ const QQmlJSRegisterContent argument
+ = m_function->argumentTypes[argumentIndex];
+ const QQmlJSRegisterContent original
+ = m_typeResolver->original(argument);
+
+ const bool needsConversion = argument != original;
+ if (!isPointer && registerIt->numTracked == 1 && !needsConversion) {
+ // Not a pointer, never written to, and doesn't need any initial conversion.
+ // This is a readonly argument.
+ //
+ // We would like to make the variable a const ref if it's a readonly argument,
+ // but due to the various call interfaces accepting non-const values, we can't.
+ // We rely on those calls to still not modify their arguments in place.
+ result.code += u'&';
+ }
- if (generatedVariables.hasSeen(registerTypeIt.value()))
- continue;
+ result.code += registerIt->variableName + u" = "_s;
- result.code += storedType->internalName();
+ const QString originalValue
+ = u"(*static_cast<"_s + castTargetName(original.storedType())
+ + u"*>(argv["_s + QString::number(argumentIndex + 1) + u"]))"_s;
- const bool isPointer
- = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
- if (isPointer)
- result.code += u" *"_s;
+ if (needsConversion)
+ result.code += conversion(original, argument, originalValue);
else
- result.code += u' ';
-
- if (!registerIsArgument && registerIndex != Accumulator
- && !m_typeResolver->registerIsStoredIn(
- function->registerTypes[registerIndex - firstRegisterIndex()],
- m_typeResolver->voidType())) {
- result.code += registerTypeIt.value() + u" = "_s;
- result.code += conversion(m_typeResolver->voidType(), storedType, QString());
- } else if (registerIsArgument && m_typeResolver->registerIsStoredIn(
- argumentType(registerIndex), storedType)) {
- const int argumentIndex = registerIndex - FirstArgument;
- const QQmlJSScope::ConstPtr argument
- = m_function->argumentTypes[argumentIndex].storedType();
- const QQmlJSScope::ConstPtr original
- = m_typeResolver->originalType(argument);
-
- const bool needsConversion = argument != original;
- if (!isPointer && registerTypes.size() == 1 && !needsConversion) {
- // Not a pointer, never written to, and doesn't need any initial conversion.
- // This is a readonly argument.
- //
- // We would like to make the variable a const ref if it's a readonly argument,
- // but due to the various call interfaces accepting non-const values, we can't.
- // We rely on those calls to still not modify their arguments in place.
- result.code += u'&';
- }
-
- result.code += registerTypeIt.value() + u" = "_s;
-
- const QString originalValue = u"*static_cast<"_s + castTargetName(original)
- + u"*>(argumentsPtr["_s + QString::number(argumentIndex) + u"])"_s;
-
- if (needsConversion)
- result.code += conversion(original, argument, originalValue);
- else
- result.code += originalValue;
- } else {
- result.code += registerTypeIt.value();
- }
- result.code += u";\n"_s;
+ result.code += originalValue;
+ } else {
+ result.code += registerIt->variableName;
}
+ result.code += u";\n"_s;
}
result.code += m_body;
- for (const QQmlJSRegisterContent &argType : std::as_const(function->argumentTypes)) {
- if (argType.isValid()) {
- result.argumentTypes.append(
- m_typeResolver->originalType(argType.storedType())
- ->augmentedInternalName());
- } else {
- result.argumentTypes.append(u"void"_s);
- }
- }
- if (function->returnType) {
- result.returnType = function->returnType->internalName();
- if (function->returnType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- result.returnType += u'*';
+ QString signature
+ = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { unit };\n"
+ " const auto *aotContext = &c;\n"
+ " Q_UNUSED(aotContext);\n"_s;
+
+ if (function->returnType.isValid()) {
+ signature += u" argTypes[0] = %1;\n"_s.arg(
+ metaType(m_typeResolver->containedType(function->returnType)));
} else {
- result.returnType = u"void"_s;
+ signature += u" argTypes[0] = QMetaType();\n"_s;
+ }
+ result.numArguments = function->argumentTypes.length();
+ for (qsizetype i = 0; i != result.numArguments; ++i) {
+ signature += u" argTypes[%1] = %2;\n"_s.arg(
+ QString::number(i + 1),
+ metaType(m_typeResolver->originalContainedType(function->argumentTypes[i])));
}
+ result.signature = signature;
return result;
}
-QString QQmlJSCodeGenerator::errorReturnValue()
+void QQmlJSCodeGenerator::generateReturnError()
{
- if (auto ret = m_function->returnType) {
- return ret->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
- ? conversion(m_typeResolver->nullType(), ret, QString())
- : ret->internalName() + u"()"_s;
+ const auto finalizeReturn = qScopeGuard([this]() { m_body += u"return;\n"_s; });
+
+ m_body += u"aotContext->setReturnValueUndefined();\n"_s;
+ const auto ret = m_function->returnType;
+ if (!ret.isValid() || m_typeResolver->registerContains(ret, m_typeResolver->voidType()))
+ return;
+
+ m_body += u"if (argv[0]) {\n"_s;
+
+ const auto contained = m_typeResolver->containedType(ret);
+ const auto stored = ret.storedType();
+ if (contained->isReferenceType() && stored->isReferenceType()) {
+ m_body += u" *static_cast<"_s
+ + stored->augmentedInternalName()
+ + u" *>(argv[0]) = nullptr;\n"_s;
+ } else if (m_typeResolver->equals(contained, stored)) {
+ m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
+ + stored->internalName() + u"();\n"_s;
+ } else {
+ m_body += u" const QMetaType returnType = "_s
+ + metaType(m_typeResolver->containedType(ret)) + u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0]);\n "_s;
}
- return QString();
+
+ m_body += u"}\n"_s;
}
void QQmlJSCodeGenerator::generate_Ret()
{
INJECT_TRACE_INFO(generate_Ret);
- if (m_function->returnType) {
- const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
- if (!m_state.accumulatorVariableIn.isEmpty()) {
- const QString in = m_state.accumulatorVariableIn;
- if (m_typeResolver->registerIsStoredIn(
- m_state.accumulatorIn(), m_typeResolver->varType())) {
- m_body += u"if (!"_s + in + u".isValid())\n"_s;
- m_body += u" "_s + signalUndefined;
- } else if (m_typeResolver->registerIsStoredIn(
- m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) {
- m_body += u"if ("_s + in
- + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
- m_body += u" "_s + signalUndefined;
- } else if (m_typeResolver->registerIsStoredIn(
- m_state.accumulatorIn(), m_typeResolver->jsValueType())) {
- m_body += u"if ("_s + in + u".isUndefined())\n"_s;
- m_body += u" "_s + signalUndefined;
- }
- m_body += u"return "_s
- + conversion(m_state.accumulatorIn().storedType(), m_function->returnType, in);
- } else {
- if (m_typeResolver->equals(m_state.accumulatorIn().storedType(),
- m_typeResolver->voidType())) {
- m_body += signalUndefined;
- }
- m_body += u"return "_s + conversion(
- m_state.accumulatorIn().storedType(), m_function->returnType, QString());
+ const auto finalizeReturn = qScopeGuard([this]() {
+ m_body += u"return;\n"_s;
+ m_skipUntilNextLabel = true;
+ resetState();
+ });
+
+ if (!m_function->returnType.isValid())
+ return;
+
+ m_body += u"if (argv[0]) {\n"_s;
+
+ const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
+ const QString in = m_state.accumulatorVariableIn;
+
+ if (in.isEmpty()) {
+ if (m_typeResolver->equals(m_state.accumulatorIn().storedType(),
+ m_typeResolver->voidType())) {
+ m_body += signalUndefined;
+
}
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->varType())) {
+ m_body += u" if (!"_s + in + u".isValid())\n"_s;
+ m_body += u" "_s + signalUndefined;
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) {
+ m_body += u" if ("_s + in + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
+ m_body += u" "_s + signalUndefined;
+ } else if (m_typeResolver->registerIsStoredIn(
+ m_state.accumulatorIn(), m_typeResolver->jsValueType())) {
+ m_body += u" if ("_s + in + u".isUndefined())\n"_s;
+ m_body += u" "_s + signalUndefined;
+ }
+
+ if (m_typeResolver->registerContains(
+ m_function->returnType, m_typeResolver->voidType())) {
+ m_body += u"}\n"_s;
+ return;
+ }
+
+ const auto contained = m_typeResolver->containedType(m_function->returnType);
+ const auto stored = m_function->returnType.storedType();
+ if (m_typeResolver->equals(contained, stored)
+ || (contained->isReferenceType() && stored->isReferenceType())) {
+ m_body += u" *static_cast<"_s
+ + stored->augmentedInternalName()
+ + u" *>(argv[0]) = "_s
+ + conversion(m_state.accumulatorIn(), m_function->returnType,
+ consumedAccumulatorVariableIn())
+ + u";\n"_s;
+ } else if (m_typeResolver->registerContains(m_state.accumulatorIn(), contained)) {
+ m_body += u" const QMetaType returnType = "_s + contentType(m_state.accumulatorIn(), in)
+ + u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0], "_s
+ + contentPointer(m_state.accumulatorIn(), in) + u");\n"_s;
} else {
- m_body += u"return"_s;
+ m_body += u" const auto converted = "_s
+ + conversion(m_state.accumulatorIn(), m_function->returnType,
+ consumedAccumulatorVariableIn()) + u";\n"_s;
+ m_body += u" const QMetaType returnType = "_s
+ + contentType(m_function->returnType, u"converted"_s)
+ + u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0], "_s
+ + contentPointer(m_function->returnType, u"converted"_s) + u");\n"_s;
}
- m_body += u";\n"_s;
- m_skipUntilNextLabel = true;
- resetState();
+ m_body += u"}\n"_s;
}
void QQmlJSCodeGenerator::generate_Debug()
@@ -325,30 +427,29 @@ void QQmlJSCodeGenerator::generate_LoadConst(int index)
m_body += m_state.accumulatorVariableOut + u" = "_s;
if (type == m_typeResolver->realType()) {
m_body += conversion(
- type, m_state.accumulatorOut().storedType(),
+ type, m_state.accumulatorOut(),
toNumericString(value.doubleValue()));
- } else if (type == m_typeResolver->intType()) {
+ } else if (type == m_typeResolver->int32Type()) {
m_body += conversion(
- type, m_state.accumulatorOut().storedType(),
+ type, m_state.accumulatorOut(),
QString::number(value.integerValue()));
} else if (type == m_typeResolver->boolType()) {
m_body += conversion(
- type, m_state.accumulatorOut().storedType(),
+ type, m_state.accumulatorOut(),
value.booleanValue() ? u"true"_s : u"false"_s);
} else if (type == m_typeResolver->voidType()) {
m_body += conversion(
- type, m_state.accumulatorOut().storedType(),
+ type, m_state.accumulatorOut(),
QString());
} else if (type == m_typeResolver->nullType()) {
m_body += conversion(
- type, m_state.accumulatorOut().storedType(),
+ type, m_state.accumulatorOut(),
u"nullptr"_s);
} else {
reject(u"unsupported constant type"_s);
}
m_body += u";\n"_s;
- generateOutputVariantConversion(type);
}
void QQmlJSCodeGenerator::generate_LoadZero()
@@ -357,9 +458,8 @@ void QQmlJSCodeGenerator::generate_LoadZero()
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_s + conversion(
- m_typeResolver->intType(), m_state.accumulatorOut().storedType(), u"0"_s);
+ m_typeResolver->int32Type(), m_state.accumulatorOut(), u"0"_s);
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadTrue()
@@ -368,9 +468,8 @@ void QQmlJSCodeGenerator::generate_LoadTrue()
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_s + conversion(
- m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"true"_s);
+ m_typeResolver->boolType(), m_state.accumulatorOut(), u"true"_s);
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadFalse()
@@ -379,9 +478,8 @@ void QQmlJSCodeGenerator::generate_LoadFalse()
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_s + conversion(
- m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"false"_s);
+ m_typeResolver->boolType(), m_state.accumulatorOut(), u"false"_s);
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadNull()
@@ -389,10 +487,9 @@ void QQmlJSCodeGenerator::generate_LoadNull()
INJECT_TRACE_INFO(generate_LoadNull);
m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut().storedType(),
+ m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut(),
u"nullptr"_s);
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->nullType());
}
void QQmlJSCodeGenerator::generate_LoadUndefined()
@@ -400,10 +497,9 @@ void QQmlJSCodeGenerator::generate_LoadUndefined()
INJECT_TRACE_INFO(generate_LoadUndefined);
m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut().storedType(),
+ m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut(),
QString());
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->voidType());
}
void QQmlJSCodeGenerator::generate_LoadInt(int value)
@@ -412,10 +508,9 @@ void QQmlJSCodeGenerator::generate_LoadInt(int value)
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_s;
- m_body += conversion(m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
+ m_body += conversion(m_typeResolver->int32Type(), m_state.accumulatorOut(),
QString::number(value));
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
@@ -431,7 +526,7 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
const auto v4Value = QV4::StaticValue::fromReturnedValue(
m_jsUnitGenerator->constant(constIndex));
- const auto changed = m_state.changedRegister().storedType();
+ const auto changed = m_state.changedRegister();
QQmlJSScope::ConstPtr contained;
QString input;
@@ -444,16 +539,16 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
contained = m_typeResolver->boolType();
input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
} else if (v4Value.isInteger()) {
- contained = m_typeResolver->intType();
+ contained = m_typeResolver->int32Type();
input = QString::number(v4Value.int_32());
} else if (v4Value.isDouble()) {
contained = m_typeResolver->realType();
input = toNumericString(v4Value.doubleValue());
} else {
reject(u"unknown const type"_s);
+ return;
}
m_body += conversion(contained, changed, input) + u";\n"_s;
- generateOutputVariantConversion(contained);
}
void QQmlJSCodeGenerator::generate_LoadReg(int reg)
@@ -536,11 +631,9 @@ void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId)
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_s;
- m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut().storedType(),
+ m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut(),
QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
m_body += u";\n"_s;
-
- generateOutputVariantConversion(m_typeResolver->stringType());
}
void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
@@ -587,7 +680,7 @@ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index)
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
m_body += m_state.accumulatorVariableOut + u" = "_s
+ conversion(
- m_typeResolver->jsValueType(), m_state.accumulatorOut().storedType(),
+ m_typeResolver->original(m_state.accumulatorOut()), m_state.accumulatorOut(),
u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
+ u";\n"_s;
return;
@@ -633,9 +726,7 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
INJECT_TRACE_INFO(generate_StoreNameSloppy);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- const QQmlJSRegisterContent specific = m_typeResolver->scopedType(m_function->qmlScope, name);
- const QQmlJSRegisterContent type = specific.storedIn(
- m_typeResolver->genericType(specific.storedType()));
+ const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
Q_ASSERT(type.isProperty());
switch (type.variant()) {
@@ -672,37 +763,54 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
const QQmlJSRegisterContent baseType = registerType(base);
- if (!m_typeResolver->isNumeric(m_state.accumulatorIn())
- || (!baseType.isList()
- && !m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType()))) {
- reject(u"LoadElement with non-list base type or non-numeric arguments"_s);
+ if (!baseType.isList()
+ && !m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType())) {
+ reject(u"LoadElement with non-list base type "_s + baseType.descriptiveName());
return;
}
- AccumulatorConverter registers(this);
-
- const QString baseName = registerVariable(base);
- const QString indexName = m_state.accumulatorVariableIn;
-
const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
conversion(m_typeResolver->globalType(m_typeResolver->voidType()),
m_state.accumulatorOut(), QString()) + u";\n"_s;
- if (!m_typeResolver->isIntegral(m_state.accumulatorIn())) {
- m_body += u"if (!QJSNumberCoercion::isInteger("_s + indexName + u"))\n"_s
+ QString indexName = m_state.accumulatorVariableIn;
+ QQmlJSScope::ConstPtr indexType;
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ indexType = m_typeResolver->containedType(m_state.accumulatorIn());
+ } else if (m_state.accumulatorIn().isConversion()) {
+ const auto target = m_typeResolver->extractNonVoidFromOptionalType(m_state.accumulatorIn());
+ if (m_typeResolver->isNumeric(target)) {
+ indexType = target;
+ m_body += u"if (!" + indexName + u".metaType().isValid())\n"
+ + voidAssignment
+ + u"else ";
+ indexName = convertStored(
+ m_state.accumulatorIn().storedType(), indexType, indexName);
+ } else {
+ reject(u"LoadElement with non-numeric argument"_s);
+ return;
+ }
+ }
+
+ AccumulatorConverter registers(this);
+ const QString baseName = registerVariable(base);
+
+ if (!m_typeResolver->isNativeArrayIndex(indexType)) {
+ m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
+ + voidAssignment
+ + u"else "_s;
+ } else if (!m_typeResolver->isUnsignedInteger(indexType)) {
+ m_body += u"if ("_s + indexName + u" < 0)\n"_s
+ voidAssignment
+ u"else "_s;
}
if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
// Our QQmlListProperty only keeps plain QObject*.
- const auto valueType = m_typeResolver->valueType(baseType);
- const auto elementType = m_typeResolver->globalType(
- m_typeResolver->genericType(m_typeResolver->containedType(valueType)));
+ const auto elementType = m_typeResolver->globalType(m_typeResolver->qObjectType());
- m_body += u"if ("_s + indexName + u" >= 0 && "_s + indexName
- + u" < "_s + baseName + u".count(&"_s + baseName
- + u"))\n"_s;
+ m_body += u"if ("_s + indexName + u" < "_s + baseName
+ + u".count(&"_s + baseName + u"))\n"_s;
m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
conversion(elementType, m_state.accumulatorOut(),
baseName + u".at(&"_s + baseName + u", "_s
@@ -719,11 +827,12 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
// TODO: Once we get a char type in QML, use it here.
if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType()))
access = u"QString("_s + access + u")"_s;
- else if (!m_typeResolver->canUseValueTypes())
- reject(u"LoadElement in sequence type reference"_s);
+ else if (m_state.isRegisterAffectedBySideEffects(base))
+ reject(u"LoadElement on a sequence potentially affected by side effects"_s);
+ else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
+ reject(u"LoadElement on a sequence wrapped in a non-sequence type"_s);
- m_body += u"if ("_s + indexName + u" >= 0 && "_s + indexName
- + u" < "_s + baseName + u".size())\n"_s;
+ m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s;
m_body += u"else\n"_s
@@ -735,18 +844,15 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
INJECT_TRACE_INFO(generate_StoreElement);
const QQmlJSRegisterContent baseType = registerType(base);
- const QQmlJSRegisterContent indexType = registerType(index);
+ const QQmlJSScope::ConstPtr indexType = m_typeResolver->containedType(registerType(index));
- if (!m_typeResolver->isNumeric(registerType(index)) || !baseType.isList()) {
+ if (!m_typeResolver->isNumeric(indexType) || !baseType.isList()) {
reject(u"StoreElement with non-list base type or non-numeric arguments"_s);
return;
}
- if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
- if (m_typeResolver->canUseValueTypes())
- reject(u"indirect StoreElement"_s);
- else
- reject(u"StoreElement in sequence type reference"_s);
+ if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
+ reject(u"indirect StoreElement"_s);
return;
}
@@ -757,16 +863,37 @@ 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 ("_s;
- if (!m_typeResolver->isIntegral(indexType))
- m_body += u"QJSNumberCoercion::isInteger("_s + indexName + u") && "_s;
- m_body += indexName + u" >= 0 && "_s
- + indexName + u" < "_s + baseName + u".count(&"_s + baseName
- + u"))\n"_s;
- m_body += u" "_s + baseName + u".replace(&"_s + baseName
- + u", "_s + indexName + u", "_s;
+ addInclude(u"QtQml/qjslist.h"_s);
+ if (!m_typeResolver->isNativeArrayIndex(indexType))
+ m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s;
+ else if (!m_typeResolver->isUnsignedInteger(indexType))
+ m_body += u"if ("_s + indexName + u" >= 0) {\n"_s;
+ else
+ m_body += u"{\n"_s;
+
+ if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
+ m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
+ + u"))\n"_s;
+ m_body += u" "_s + baseName + u".replace(&"_s + baseName
+ + u", "_s + indexName + u", "_s;
+ m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
+ + u");\n"_s;
+ m_body += u"}\n"_s;
+ return;
+ }
+
+ if (m_state.isRegisterAffectedBySideEffects(base))
+ reject(u"LoadElement on a sequence potentially affected by side effects"_s);
+
+ m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
+ m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
+ + indexName + u" + 1);\n"_s;
+ m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
- + u");\n"_s;
+ + u";\n"_s;
+ m_body += u"}\n"_s;
+
+ generateWriteBack(base);
}
void QQmlJSCodeGenerator::generate_LoadProperty(int nameIndex)
@@ -786,9 +913,15 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
{
const QString enumMember = m_state.accumulatorOut().enumMember();
- // If we're referring to the type, there's nothing to do.
- if (enumMember.isEmpty())
+ if (enumMember.isEmpty()) {
+ // If we're referring to the type, there's nothing to do.
+ // However, we should not get here since no one can ever use the enum metatype.
+ // The lookup is dead code and should be optimized away.
+ // ... unless you are actually trying to store the metatype itself in a property.
+ // We cannot compile such code.
+ reject(u"Lookup of enum metatype"_s);
return;
+ }
// If the metaenum has the value, just use it and skip all the rest.
const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
@@ -805,6 +938,12 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
Q_ASSERT(!scopeType->isComposite());
const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
+ if (enumName.isEmpty()) {
+ if (metaEnum.isFlag() && !metaEnum.name().isEmpty())
+ reject(u"qmltypes misses name entry for flag; did you pass the enum type to Q_FLAG instead of the QFlag type?"
+ "\nType is %1, enum name is %2"_s.arg(scopeType->internalName(), metaEnum.name()));
+ reject(u"qmltypes misses name entry for enum"_s);
+ }
const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
+ u", &"_s + m_state.accumulatorVariableOut + u')';
const QString initialization = u"aotContext->initGetEnumLookup("_s
@@ -866,26 +1005,6 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index)
}
}
-void QQmlJSCodeGenerator::generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType)
-{
- if (changedRegisterVariable().isEmpty())
- return;
-
- const QQmlJSRegisterContent changed = m_state.changedRegister();
- if (!m_typeResolver->equals(changed.storedType(), m_typeResolver->varType()))
- return;
-
- const QQmlJSScope::ConstPtr target = m_typeResolver->containedType(changed);
- if (m_typeResolver->equals(target, containedType)
- || m_typeResolver->equals(target, m_typeResolver->varType())) {
- return;
- }
-
- // If we could store the type directly, we would not wrap it in a QVariant.
- // Therefore, our best bet here is metaTypeFromName().
- m_body += changedRegisterVariable() + u".convert("_s + metaTypeFromName(target) + u");\n"_s;
-}
-
void QQmlJSCodeGenerator::generateVariantEqualityComparison(
const QQmlJSRegisterContent &nonStorableContent, const QString &registerName, bool invert)
{
@@ -899,7 +1018,7 @@ void QQmlJSCodeGenerator::generateVariantEqualityComparison(
m_body += u"if ("_s + registerName
+ u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
+ m_state.accumulatorVariableOut + u" = "_s
- + conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
u"static_cast<const QJSPrimitiveValue *>("_s + registerName
+ u".constData())"_s + u"->type() "_s
+ (invert ? u"!="_s : u"=="_s)
@@ -909,7 +1028,7 @@ void QQmlJSCodeGenerator::generateVariantEqualityComparison(
+ u";\n} else if ("_s + registerName
+ u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
+ m_state.accumulatorVariableOut + u" = "_s
- + conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
(invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
+ registerName + u".constData())"_s + u"->"_s
+ (m_typeResolver->equals(comparedType, m_typeResolver->nullType())
@@ -923,21 +1042,180 @@ void QQmlJSCodeGenerator::generateVariantEqualityComparison(
m_body += u"else if ("_s + registerName
+ u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
+ m_state.accumulatorVariableOut + u" = "_s
- + conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
u"*static_cast<QObject *const *>("_s + registerName
+ u".constData())"_s + (invert ? u"!="_s : u"=="_s)
+ u" nullptr"_s)
+ u";\n} else if ("_s + registerName
+ u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
- + m_state.accumulatorVariableOut + u" = "_s + (invert ? u"false"_s : u"true"_s)
+ + m_state.accumulatorVariableOut + u" = "_s
+ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
+ (invert ? u"false"_s : u"true"_s))
+ u";\n}\n"_s;
}
// fallback case (if variant contains a different type, then it is not null or undefined)
m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
- + (invert ? (registerName + u".isValid() ? true : false"_s)
- : (registerName + u".isValid() ? false : true"_s))
- + u";}\n"_s;
+ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
+ (invert ? (registerName + u".isValid() ? true : false"_s)
+ : (registerName + u".isValid() ? false : true"_s)))
+ + u";\n}"_s;
+}
+
+void QQmlJSCodeGenerator::generateVariantEqualityComparison(
+ const QQmlJSRegisterContent &storableContent, const QString &typedRegisterName,
+ const QString &varRegisterName, bool invert)
+{
+ // enumerations are ===-equal to their underlying type and they are stored as such.
+ // Therefore, use the underlying type right away.
+ const auto contained = storableContent.isEnumeration()
+ ? storableContent.storedType()
+ : m_typeResolver->containedType(storableContent);
+
+ if (contained->isReferenceType()) {
+ const QQmlJSRegisterContent comparable
+ = m_typeResolver->builtinType(m_typeResolver->qObjectType());
+ m_body += m_state.accumulatorVariableOut + u" = "_s + (invert ? u"!"_s : QString()) + u"(("
+ + varRegisterName + u".metaType().flags() & QMetaType::PointerToQObject) "_s
+ + u" && "_s + conversion(storableContent, comparable, typedRegisterName) + u" == "_s
+ + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u");\n";
+ return;
+ }
+
+ if (m_typeResolver->isPrimitive(contained)) {
+ const QQmlJSRegisterContent comparable
+ = m_typeResolver->builtinType(m_typeResolver->jsPrimitiveType());
+ m_body += m_state.accumulatorVariableOut + u" = "_s + (invert ? u"!"_s : QString())
+ + conversion(storableContent, comparable, typedRegisterName)
+ + u".strictlyEquals("_s
+ + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u");\n"_s;
+ return;
+ }
+
+ reject(u"comparison of non-primitive, non-object type to var"_s);
+}
+
+void QQmlJSCodeGenerator::generateArrayInitializer(int argc, int argv)
+{
+ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
+ const QQmlJSScope::ConstPtr value = stored->valueType();
+ Q_ASSERT(value);
+
+ QStringList initializer;
+ for (int i = 0; i < argc; ++i) {
+ initializer += convertStored(
+ registerType(argv + i).storedType(), value,
+ consumedRegisterVariable(argv + i));
+ }
+
+ m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
+ m_body += initializer.join(u", "_s);
+ m_body += u"};\n";
+}
+
+void QQmlJSCodeGenerator::generateWriteBack(int registerIndex)
+{
+ QString writeBackRegister = registerVariable(registerIndex);
+ bool writeBackAffectedBySideEffects = m_state.isRegisterAffectedBySideEffects(registerIndex);
+
+ for (QQmlJSRegisterContent writeBack = registerType(registerIndex);
+ !writeBack.storedType()->isReferenceType();) {
+ if (writeBackAffectedBySideEffects)
+ reject(u"write-back of value affected by side effects"_s);
+
+ if (writeBack.isConversion())
+ reject(u"write-back of converted value"_s);
+
+ const int lookupIndex = writeBack.resultLookupIndex();
+ if (lookupIndex == -1) {
+ // This is essential for the soundness of the type system.
+ //
+ // If a value or a list is returned from a function, we cannot know
+ // whether it is a copy or a reference. Therefore, we cannot know whether
+ // we have to write it back and so we have to reject any write on it.
+ //
+ // Only if we are sure that the value is locally created we can be sure
+ // we don't have to write it back. In this latter case we could allow
+ // a modification that doesn't write back.
+ reject(u"write-back of non-lookup"_s);
+ break;
+ }
+
+ const QString writeBackIndexString = QString::number(lookupIndex);
+
+ const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
+ if (variant == QQmlJSRegisterContent::ScopeProperty
+ || variant == QQmlJSRegisterContent::ExtensionScopeProperty) {
+ const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
+ + writeBackIndexString
+ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
+ const QString initialization
+ = u"aotContext->initLoadScopeObjectPropertyLookup("_s
+ + writeBackIndexString
+ + u", "_s + contentType(writeBack, writeBackRegister) + u')';
+ generateLookup(lookup, initialization);
+ break;
+ }
+
+
+ QQmlJSRegisterContent outerContent;
+ QString outerRegister;
+ bool outerAffectedBySideEffects = false;
+ for (auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
+ it != end; ++it) {
+ if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
+ outerContent = it.value().content;
+ outerRegister = lookupVariable(outerContent.resultLookupIndex());
+ outerAffectedBySideEffects = it.value().affectedBySideEffects;
+ break;
+ }
+ }
+
+ if (!outerContent.isValid()) {
+ // If the lookup doesn't exist, it was killed by state merge.
+ reject(u"write-back of lookup across jumps or merges."_s);
+ break;
+ }
+
+ Q_ASSERT(!outerRegister.isEmpty());
+
+ switch (writeBack.variant()) {
+ case QQmlJSRegisterContent::ScopeProperty:
+ case QQmlJSRegisterContent::ExtensionScopeProperty:
+ Q_UNREACHABLE();
+ case QQmlJSRegisterContent::ObjectProperty:
+ case QQmlJSRegisterContent::ExtensionObjectProperty:
+ if (writeBack.scopeType()->isReferenceType()) {
+ const QString lookup = u"aotContext->writeBackObjectLookup("_s
+ + writeBackIndexString
+ + u", "_s + outerRegister
+ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
+ const QString initialization = u"aotContext->initGetObjectLookup("_s
+ + writeBackIndexString
+ + u", "_s + outerRegister
+ + u", "_s + contentType(writeBack, writeBackRegister) + u')';
+ generateLookup(lookup, initialization);
+ } else {
+ const QString valuePointer = contentPointer(outerContent, outerRegister);
+ const QString lookup = u"aotContext->writeBackValueLookup("_s
+ + writeBackIndexString
+ + u", "_s + valuePointer
+ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
+ const QString initialization = u"aotContext->initGetValueLookup("_s
+ + writeBackIndexString
+ + u", "_s + metaObject(writeBack.scopeType())
+ + u", "_s + contentType(writeBack, writeBackRegister) + u')';
+ generateLookup(lookup, initialization);
+ }
+ break;
+ default:
+ reject(u"SetLookup on value types (because of missing write-back)"_s);
+ }
+
+ writeBackRegister = outerRegister;
+ writeBack = outerContent;
+ writeBackAffectedBySideEffects = outerAffectedBySideEffects;
+ }
}
void QQmlJSCodeGenerator::rejectIfNonQObjectOut(const QString &error)
@@ -948,15 +1226,141 @@ void QQmlJSCodeGenerator::rejectIfNonQObjectOut(const QString &error)
}
}
+void QQmlJSCodeGenerator::rejectIfBadArray()
+{
+ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
+ if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
+ // This rejects any attempt to store the list into a QVariant.
+ // Therefore, we don't have to adjust the contained type below.
+
+ reject(u"storing an array in a non-sequence type"_s);
+ } else if (stored->isListProperty()) {
+ // We can, technically, generate code for this. But it's dangerous:
+ //
+ // const QString storage = m_state.accumulatorVariableOut + u"_storage"_s;
+ // m_body += stored->internalName() + u"::ListType " + storage
+ // + u" = {"_s + initializer.join(u", "_s) + u"};\n"_s;
+ // m_body += m_state.accumulatorVariableOut
+ // + u" = " + stored->internalName() + u"(nullptr, &"_s + storage + u");\n"_s;
+
+ reject(u"creating a QQmlListProperty not backed by a property"_s);
+
+ }
+}
+
+/*!
+ * \internal
+ *
+ * generates a check for the content pointer to be valid.
+ * Returns true if the content pointer needs to be retrieved from a QVariant, or
+ * false if the variable can be used as-is.
+ */
+bool QQmlJSCodeGenerator::generateContentPointerCheck(
+ const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
+ const QString &variable, const QString &errorMessage)
+{
+ const QQmlJSScope::ConstPtr scope = required;
+ const QQmlJSScope::ConstPtr input = m_typeResolver->containedType(actual);
+ if (QQmlJSUtils::searchBaseAndExtensionTypes(input,
+ [&](const QQmlJSScope::ConstPtr &base) {
+ return m_typeResolver->equals(base, scope);
+ })) {
+ return false;
+ }
+
+ if (!m_typeResolver->canHold(input, scope)) {
+ reject(u"lookup of members of %1 in %2"_s.arg(
+ scope->internalName(), input->internalName()));
+ }
+
+ bool needsVarContentConversion = false;
+ QString processedErrorMessage;
+ if (actual.storedType()->isReferenceType()) {
+ // Since we have verified the type in qqmljstypepropagator.cpp we now know
+ // that we can only have either null or the actual type here. Therefore,
+ // it's enough to check the pointer for null.
+ m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
+ processedErrorMessage = errorMessage.arg(u"null");
+ } else if (m_typeResolver->equals(actual.storedType(), m_typeResolver->varType())) {
+ // Since we have verified the type in qqmljstypepropagator.cpp we now know
+ // that we can only have either undefined or the actual type here. Therefore,
+ // it's enough to check the QVariant for isValid().
+ m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
+ needsVarContentConversion = true;
+ processedErrorMessage = errorMessage.arg(u"undefined");
+ } else {
+ reject(u"retrieving metatype from %1"_s.arg(actual.descriptiveName()));
+ }
+
+ generateSetInstructionPointer();
+ m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
+ m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
+ generateReturnError();
+ m_body += u"}\n"_s;
+ return needsVarContentConversion;
+}
+
+QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
+ const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
+ const QString &variable, const QString &errorMessage)
+{
+ if (generateContentPointerCheck(required, actual, variable, errorMessage))
+ return variable + u".data()"_s;
+ return contentPointer(actual, variable);
+}
+
+QString QQmlJSCodeGenerator::resolveQObjectPointer(
+ const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
+ const QString &variable, const QString &errorMessage)
+{
+ if (generateContentPointerCheck(required, actual, variable, errorMessage))
+ return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
+ return variable;
+}
+
void QQmlJSCodeGenerator::generate_GetLookup(int index)
{
INJECT_TRACE_INFO(generate_GetLookup);
+ generate_GetLookupHelper(index);
+}
+void QQmlJSCodeGenerator::generate_GetLookupHelper(int index)
+{
if (m_state.accumulatorOut().isMethod()) {
reject(u"lookup of function property."_s);
return;
}
+ if (m_typeResolver->equals(m_state.accumulatorOut().scopeType(), m_typeResolver->mathObject())) {
+ QString name = m_jsUnitGenerator->lookupName(index);
+
+ double value{};
+ if (name == u"E") {
+ value = std::exp(1.0);
+ } else if (name == u"LN10") {
+ value = log(10.0);
+ } else if (name == u"LN2") {
+ value = log(2.0);
+ } else if (name == u"LOG10E") {
+ value = log10(std::exp(1.0));
+ } else if (name == u"LOG2E") {
+ value = log2(std::exp(1.0));
+ } else if (name == u"PI") {
+ value = 3.14159265358979323846;
+ } else if (name == u"SQRT1_2") {
+ value = std::sqrt(0.5);
+ } else if (name == u"SQRT2") {
+ value = std::sqrt(2.0);
+ } else {
+ Q_UNREACHABLE();
+ }
+
+ m_body += m_state.accumulatorVariableOut + u" = "_s
+ + conversion(m_typeResolver->realType(), m_state.accumulatorOut(), toNumericString(value))
+ + u";\n"_s;
+ return;
+ }
+
if (m_state.accumulatorOut().isImportNamespace()) {
Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectModulePrefix);
// If we have an object module prefix, we need to pass through the original object.
@@ -981,8 +1385,8 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
? QString::number(m_state.accumulatorIn().importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
const auto accumulatorIn = m_state.accumulatorIn();
- const bool isReferenceType = (accumulatorIn.storedType()->accessSemantics()
- == QQmlJSScope::AccessSemantics::Reference);
+ const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
+ const bool isReferenceType = scope->isReferenceType();
switch (m_state.accumulatorOut().variant()) {
case QQmlJSRegisterContent::ObjectAttached: {
@@ -993,6 +1397,13 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
// that would be expensive.
reject(u"attached object for non-QObject type"_s);
}
+
+ if (!m_state.accumulatorIn().storedType()->isReferenceType()) {
+ // This can happen if we retroactively determine that the property might not be
+ // what we think it is (ie, it can be shadowed).
+ reject(u"attached object of potentially non-QObject base"_s);
+ }
+
rejectIfNonQObjectOut(u"non-QObject attached type"_s);
const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
@@ -1006,6 +1417,7 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
}
case QQmlJSRegisterContent::ScopeAttached:
case QQmlJSRegisterContent::Singleton:
+ case QQmlJSRegisterContent::Script:
case QQmlJSRegisterContent::MetaType: {
generateTypeLookup(index);
return;
@@ -1016,60 +1428,106 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
Q_ASSERT(m_state.accumulatorOut().isProperty());
- if (isReferenceType) {
+ if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) {
+ reject(u"lookup in QJSValue"_s);
+ } else if (isReferenceType) {
+ const QString inputPointer = resolveQObjectPointer(
+ scope, accumulatorIn, m_state.accumulatorVariableIn,
+ u"Cannot read property '%1' of %2"_s.arg(
+ m_jsUnitGenerator->lookupName(index)));
const QString lookup = u"aotContext->getObjectLookup("_s + indexString
- + u", "_s + m_state.accumulatorVariableIn + u", "_s
+ + u", "_s + inputPointer + u", "_s
+ contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString initialization = u"aotContext->initGetObjectLookup("_s
- + indexString + u", "_s + m_state.accumulatorVariableIn
+ + indexString + u", "_s + inputPointer
+ u", "_s + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
+ u')';
const QString preparation = getLookupPreparation(
m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- } else if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->listPropertyType())
+ } else if ((scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
+ || m_typeResolver->equals(scope, m_typeResolver->stringType()))
&& m_jsUnitGenerator->lookupName(index) == u"length"_s) {
+ const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
+ if (stored->isListProperty()) {
+ m_body += m_state.accumulatorVariableOut + u" = "_s;
+ m_body += conversion(
+ m_typeResolver->globalType(m_typeResolver->sizeType()),
+ m_state.accumulatorOut(),
+ m_state.accumulatorVariableIn + u".count("_s + u'&'
+ + m_state.accumulatorVariableIn + u')');
+ m_body += u";\n"_s;
+ } else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
+ || m_typeResolver->equals(stored, m_typeResolver->stringType())) {
+ m_body += m_state.accumulatorVariableOut + u" = "_s
+ + conversion(m_typeResolver->globalType(m_typeResolver->sizeType()),
+ m_state.accumulatorOut(),
+ m_state.accumulatorVariableIn + u".length()"_s)
+ + u";\n"_s;
+ } else {
+ reject(u"access to 'length' property of sequence wrapped in non-sequence"_s);
+ }
+ } else if (m_typeResolver->registerIsStoredIn(accumulatorIn,
+ m_typeResolver->variantMapType())) {
+ QString mapLookup = m_state.accumulatorVariableIn + u"["_s
+ + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(index)) + u"]"_s;
m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(
- m_typeResolver->globalType(m_typeResolver->intType()), m_state.accumulatorOut(),
- m_state.accumulatorVariableIn + u".count("_s + u'&'
- + m_state.accumulatorVariableIn + u')');
+ m_body += conversion(m_typeResolver->globalType(m_typeResolver->varType()),
+ m_state.accumulatorOut(), mapLookup);
m_body += u";\n"_s;
- } else if ((m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->stringType())
- || accumulatorIn.storedType()->accessSemantics()
- == QQmlJSScope::AccessSemantics::Sequence)
- && m_jsUnitGenerator->lookupName(index) == u"length"_s) {
- m_body += m_state.accumulatorVariableOut + u" = "_s
- + conversion(m_typeResolver->globalType(m_typeResolver->intType()),
- m_state.accumulatorOut(),
- m_state.accumulatorVariableIn + u".length()"_s)
- + u";\n"_s;
- } else if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) {
- reject(u"lookup in QJSValue"_s);
- } else if (m_typeResolver->canUseValueTypes()) {
+ } else {
+ if (m_state.isRegisterAffectedBySideEffects(Accumulator))
+ reject(u"reading from a value that's potentially affected by side effects"_s);
+
+ const QString inputContentPointer = resolveValueTypeContentPointer(
+ scope, accumulatorIn, m_state.accumulatorVariableIn,
+ u"Cannot read property '%1' of %2"_s.arg(
+ m_jsUnitGenerator->lookupName(index)));
+
const QString lookup = u"aotContext->getValueLookup("_s + indexString
- + u", "_s + contentPointer(m_state.accumulatorIn(),
- m_state.accumulatorVariableIn)
- + u", "_s + contentPointer(m_state.accumulatorOut(),
- m_state.accumulatorVariableOut)
+ + u", "_s + inputContentPointer
+ + u", "_s + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
+ u')';
const QString initialization = u"aotContext->initGetValueLookup("_s
+ indexString + u", "_s
- + metaObject(m_state.accumulatorOut().scopeType()) + u", "_s
+ + metaObject(scope) + u", "_s
+ contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
const QString preparation = getLookupPreparation(
m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- } else {
- reject(u"lookup in value type reference"_s);
}
}
void QQmlJSCodeGenerator::generate_GetOptionalLookup(int index, int offset)
{
- Q_UNUSED(index)
- Q_UNUSED(offset)
- BYTECODE_UNIMPLEMENTED();
+ INJECT_TRACE_INFO(generate_GetOptionalLookup);
+
+ const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
+ QString accumulatorVarIn = m_state.accumulatorVariableIn;
+
+ const auto &annotation = m_annotations[currentInstructionOffset()];
+ if (accumulatorIn.storedType()->isReferenceType()) {
+ m_body += u"if (!%1)\n"_s.arg(accumulatorVarIn);
+ generateJumpCodeWithTypeConversions(offset);
+ } else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->varType())) {
+ m_body += u"if (!%1.isValid() || ((%1.metaType().flags() & QMetaType::PointerToQObject) "
+ "&& %1.value<QObject *>() == nullptr))\n"_s.arg(accumulatorVarIn);
+ generateJumpCodeWithTypeConversions(offset);
+ } else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType())) {
+ m_body += u"if (%1.equals(QJSPrimitiveUndefined()) "
+ "|| %1.equals(QJSPrimitiveNull()))\n"_s.arg(accumulatorVarIn);
+ generateJumpCodeWithTypeConversions(offset);
+ } else if (annotation.changedRegisterIndex == Accumulator
+ && annotation.changedRegister.variant() == QQmlJSRegisterContent::ObjectEnum) {
+ // Nothing
+ } else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->jsValueType())) {
+ m_body += u"if (%1.isNull() || %1.isUndefined())\n"_s.arg(accumulatorVarIn);
+ generateJumpCodeWithTypeConversions(offset);
+ } else {
+ Q_UNREACHABLE(); // No other accumulatorIn stored types should be possible
+ }
+
+ generate_GetLookupHelper(index);
}
void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
@@ -1101,18 +1559,33 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
const QString indexString = QString::number(index);
const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
- const QQmlJSRegisterContent callBase = registerType(baseReg);
- const QQmlJSRegisterContent specific = m_typeResolver->memberType(
- callBase, m_jsUnitGenerator->lookupName(index));
+ const QQmlJSRegisterContent specific = m_state.readAccumulator();
+ Q_ASSERT(specific.isConversion());
+ const QQmlJSScope::ConstPtr originalScope
+ = m_typeResolver->originalType(specific.conversionResultScope());
+
if (specific.storedType().isNull()) {
reject(u"SetLookup. Could not find property "
- + callBase.storedType()->internalName()
- + u" on type "
- + m_jsUnitGenerator->lookupName(index));
+ + m_jsUnitGenerator->lookupName(index)
+ + u" on type "
+ + originalScope->internalName());
return;
}
- const QQmlJSRegisterContent property = specific.storedIn(
- m_typeResolver->genericType(specific.storedType()));
+
+ // Choose a container that can hold both, the "in" accumulator and what we actually want.
+ // If the types are all the same because we can all store them as verbatim C++ types,
+ // the container will also be that type.
+
+ QQmlJSRegisterContent property = specific;
+ if (!m_typeResolver->equals(specific.storedType(), valueType)) {
+ if (m_typeResolver->isPrimitive(specific.storedType())
+ && m_typeResolver->isPrimitive(valueType)) {
+ // Preferably store in QJSPrimitiveValue since we need the content pointer below.
+ property = property.storedIn(m_typeResolver->jsPrimitiveType());
+ } else {
+ property = property.storedIn(m_typeResolver->merge(specific.storedType(), valueType));
+ }
+ }
const QString object = registerVariable(baseReg);
m_body += u"{\n"_s;
@@ -1138,12 +1611,16 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
argType = variableInType;
}
- switch (callBase.storedType()->accessSemantics()) {
+ switch (originalScope->accessSemantics()) {
case QQmlJSScope::AccessSemantics::Reference: {
+ const QString basePointer = resolveQObjectPointer(
+ originalScope, registerType(baseReg), object,
+ u"TypeError: Value is %1 and could not be converted to an object"_s);
+
const QString lookup = u"aotContext->setObjectLookup("_s + indexString
- + u", "_s + object + u", "_s + variableIn + u')';
+ + u", "_s + basePointer + u", "_s + variableIn + u')';
const QString initialization = u"aotContext->initSetObjectLookup("_s
- + indexString + u", "_s + object + u", "_s + argType + u')';
+ + indexString + u", "_s + basePointer + u", "_s + argType + u')';
generateLookup(lookup, initialization, preparation);
break;
}
@@ -1154,11 +1631,8 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
break;
}
- if (!m_typeResolver->registerIsStoredIn(callBase, m_typeResolver->listPropertyType())) {
- if (m_typeResolver->canUseValueTypes())
- reject(u"resizing sequence types (because of missing write-back)"_s);
- else
- reject(u"resizing sequence type references"_s);
+ if (!originalScope->isListProperty()) {
+ reject(u"resizing sequence types (because of missing write-back)"_s);
break;
}
@@ -1175,24 +1649,21 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
break;
}
case QQmlJSScope::AccessSemantics::Value: {
- const QString propertyName = m_jsUnitGenerator->lookupName(index);
- const QQmlJSRegisterContent specific = m_typeResolver->memberType(callBase, propertyName);
- Q_ASSERT(specific.isProperty());
- const QQmlJSRegisterContent property = specific.storedIn(
- m_typeResolver->genericType(specific.storedType()));
+ const QQmlJSRegisterContent base = registerType(baseReg);
+ const QString baseContentPointer = resolveValueTypeContentPointer(
+ originalScope, base, object,
+ u"TypeError: Value is %1 and could not be converted to an object"_s);
const QString lookup = u"aotContext->setValueLookup("_s + indexString
- + u", "_s + contentPointer(registerType(baseReg), object)
+ + u", "_s + baseContentPointer
+ u", "_s + variableIn + u')';
const QString initialization = u"aotContext->initSetValueLookup("_s
- + indexString + u", "_s + metaObject(property.scopeType())
- + u", "_s + contentType(registerType(baseReg), object) + u')';
+ + indexString + u", "_s + metaObject(originalScope)
+ + u", "_s + argType + u')';
generateLookup(lookup, initialization, preparation);
- if (m_typeResolver->canUseValueTypes())
- reject(u"SetLookup on value types (because of missing write-back)"_s);
- else
- reject(u"SetLookup on value type references"_s);
+ generateWriteBack(baseReg);
+
break;
}
case QQmlJSScope::AccessSemantics::None:
@@ -1234,7 +1705,6 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
{
QString types;
QString args;
- QString conversions;
if (m_state.changedRegisterIndex() == InvalidRegister ||
m_typeResolver->registerContains(
@@ -1244,39 +1714,29 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
} else {
*outVar = u"callResult"_s;
const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
- m_body += outType->internalName();
- if (outType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- m_body += u" *"_s;
- else
- m_body += u' ';
- m_body += *outVar + u";\n";
+ m_body += outType->augmentedInternalName() + u' ' + *outVar;
+ if (!m_typeResolver->registerContains(m_state.accumulatorOut(), outType)) {
+ if (m_typeResolver->equals(outType, m_typeResolver->varType())
+ || m_typeResolver->equals(outType, m_typeResolver->jsPrimitiveType())) {
+ m_body += u'('
+ + metaType(m_typeResolver->containedType(m_state.accumulatorOut()))
+ + u')';
+ }
+ }
+ m_body += u";\n";
- types = metaTypeFromType(m_state.accumulatorOut().storedType());
- args = u'&' + *outVar;
+ args = contentPointer(m_state.accumulatorOut(), *outVar);
+ types = contentType(m_state.accumulatorOut(), *outVar);
}
for (int i = 0; i < argc; ++i) {
const QQmlJSRegisterContent content = registerType(argv + i);
- if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
- QString argName = u"arg"_s + QString::number(i);
- conversions += u"QVariant "_s + argName + u" = "_s
- + conversion(content.storedType(), m_typeResolver->varType(),
- consumedRegisterVariable(argv + i)) + u";\n"_s;
- args += u", "_s + argName + u".data()"_s;
- types += u", "_s + argName + u".metaType()"_s;
- } else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())
- && !m_typeResolver->registerContains(content, m_typeResolver->varType())) {
- const QString var = registerVariable(argv + i);
- args += u", "_s + var + u".data()"_s;
- types += u", "_s + var + u".metaType()"_s;
- } else {
- const QString var = registerVariable(argv + i);
- args += u", &"_s + var;
- types += u", "_s + metaTypeFromType(content.storedType());
- }
+ const QString var = registerVariable(argv + i);
+ args += u", "_s + contentPointer(content, var);
+ types += u", "_s + contentType(content, var);
}
- return conversions
- + u"void *args[] = { "_s + args + u" };\n"_s
+
+ return u"void *args[] = { "_s + args + u" };\n"_s
+ u"const QMetaType types[] = { "_s + types + u" };\n"_s;
}
@@ -1321,30 +1781,24 @@ bool QQmlJSCodeGenerator::inlineStringMethod(const QString &name, int base, int
return false;
const auto arg = [&](const QQmlJSScope::ConstPtr &type) {
- return conversion(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
+ return convertStored(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
};
const auto ret = [&](const QString &arg) {
- const QString expression = conversion(
+ const QString expression = convertStored(
registerType(base).storedType(), m_typeResolver->stringType(),
consumedRegisterVariable(base)) + u".arg("_s + arg + u')';
return conversion(
- m_typeResolver->stringType(), m_state.accumulatorOut().storedType(), expression);
+ m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
};
const QQmlJSRegisterContent input = m_state.readRegister(argv);
m_body += m_state.accumulatorVariableOut + u" = "_s;
- if (m_typeResolver->registerContains(input, m_typeResolver->intType()))
- m_body += ret(arg(m_typeResolver->intType()));
- else if (m_typeResolver->registerContains(input, m_typeResolver->uintType()))
- m_body += ret(arg(m_typeResolver->uintType()));
+ if (m_typeResolver->isNumeric(input))
+ m_body += ret(arg(m_typeResolver->containedType(input)));
else if (m_typeResolver->registerContains(input, m_typeResolver->boolType()))
m_body += ret(arg(m_typeResolver->boolType()));
- else if (m_typeResolver->registerContains(input, m_typeResolver->realType()))
- m_body += ret(arg(m_typeResolver->realType()));
- else if (m_typeResolver->registerContains(input, m_typeResolver->floatType()))
- m_body += ret(arg(m_typeResolver->floatType()));
else
m_body += ret(arg(m_typeResolver->stringType()));
m_body += u";\n"_s;
@@ -1353,12 +1807,12 @@ bool QQmlJSCodeGenerator::inlineStringMethod(const QString &name, int base, int
bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, int argv)
{
- addInclude(u"qcoreapplication.h"_s);
+ addInclude(u"QtCore/qcoreapplication.h"_s);
const auto arg = [&](int i, const QQmlJSScope::ConstPtr &type) {
Q_ASSERT(i < argc);
- return conversion(registerType(argv + i).storedType(), type,
- consumedRegisterVariable(argv + i));
+ return convertStored(registerType(argv + i).storedType(), type,
+ consumedRegisterVariable(argv + i));
};
const auto stringArg = [&](int i) {
@@ -1368,12 +1822,12 @@ bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, i
};
const auto intArg = [&](int i) {
- return i < argc ? arg(i, m_typeResolver->intType()) : u"-1"_s;
+ return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
};
const auto stringRet = [&](const QString &expression) {
return conversion(
- m_typeResolver->stringType(), m_state.accumulatorOut().storedType(), expression);
+ m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
};
const auto capture = [&]() {
@@ -1427,13 +1881,45 @@ bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, i
return false;
}
+static QString maxExpression(int argc)
+{
+ Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "max() expects at least two arguments.");
+
+ QString expression =
+ u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
+ for (int i = 2; i < argc; i++) {
+ expression +=
+ "\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
+ .arg("arg"_L1 + QString::number(i + 1));
+ }
+ expression += "return tmpMax;\n}()"_L1;
+
+ return expression;
+}
+
+static QString minExpression(int argc)
+{
+ Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "min() expects at least two arguments.");
+
+ QString expression =
+ u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
+ for (int i = 2; i < argc; i++) {
+ expression +=
+ "tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
+ .arg("arg"_L1 + QString::number(i + 1));
+ }
+ expression += "return tmpMin;\n}()"_L1;
+
+ return expression;
+}
+
bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int argv)
{
addInclude(u"cmath"_s);
addInclude(u"limits"_s);
- addInclude(u"qalgorithms.h"_s);
- addInclude(u"qrandom.h"_s);
- addInclude(u"qjsprimitivevalue.h"_s);
+ addInclude(u"QtCore/qalgorithms.h"_s);
+ addInclude(u"QtCore/qrandom.h"_s);
+ addInclude(u"QtQml/qjsprimitivevalue.h"_s);
// If the result is not stored, we don't need to generate any code. All the math methods are
// conceptually pure functions.
@@ -1442,7 +1928,7 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
m_body += u"{\n"_s;
for (int i = 0; i < argc; ++i) {
- m_body += u"const double arg%1 = "_s.arg(i + 1) + conversion(
+ m_body += u"const double arg%1 = "_s.arg(i + 1) + convertStored(
registerType(argv + i).storedType(),
m_typeResolver->realType(), consumedRegisterVariable(argv + i))
+ u";\n"_s;
@@ -1508,14 +1994,10 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(qNaN);
} else if (name == u"log2"_s && argc == 1) {
expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(qNaN);
- } else if (name == u"max"_s && argc == 2) {
- expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) "
- "? arg2 "
- ": ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1)"_s;
- } else if (name == u"min"_s && argc == 2) {
- expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) "
- "? arg2 "
- ": ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1)"_s;
+ } else if (name == u"max"_s && argc >= 2) {
+ expression = maxExpression(argc);
+ } else if (name == u"min"_s && argc >= 2) {
+ expression = minExpression(argc);
} else if (name == u"pow"_s) {
expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
} else if (name == u"random"_s && argc == 0) {
@@ -1548,8 +2030,7 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
return false;
}
- m_body += conversion(
- m_typeResolver->realType(), m_state.accumulatorOut().storedType(), expression);
+ m_body += conversion(m_typeResolver->realType(), m_state.accumulatorOut(), expression);
m_body += u";\n"_s;
m_body += u"}\n"_s;
@@ -1575,7 +2056,7 @@ bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int
if (type.isEmpty())
return false;
- addInclude(u"qloggingcategory.h"_s);
+ addInclude(u"QtCore/qloggingcategory.h"_s);
m_body += u"{\n";
m_body += u" bool firstArgIsCategory = false;\n";
@@ -1591,7 +2072,7 @@ bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int
if (firstArgIsReference) {
m_body += u" QObject *firstArg = ";
- m_body += conversion(
+ m_body += convertStored(
firstArg.storedType(),
m_typeResolver->genericType(firstArg.storedType()),
registerVariable(argv));
@@ -1604,18 +2085,35 @@ bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int
m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
m_body += u" const QString message = ";
+
+ const auto stringConversion = [&](int i) -> QString {
+ const QQmlJSScope::ConstPtr stored = m_state.readRegister(argv + i).storedType();
+ if (m_typeResolver->equals(stored, m_typeResolver->stringType())) {
+ return convertStored(
+ registerType(argv + i).storedType(),
+ m_typeResolver->stringType(), consumedRegisterVariable(argv + i));
+ } else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
+ addInclude(u"QtQml/qjslist.h"_s);
+ return u"u'[' + QJSList(&"_s + registerVariable(argv + i)
+ + u", aotContext->engine).toString() + u']'"_s;
+ } else {
+ reject(u"converting arguments for console method to string"_s);
+ return QString();
+ }
+ };
+
if (argc > 0) {
- const QString firstArgStringConversion = conversion(
+ if (firstArgIsReference) {
+ const QString firstArgStringConversion = convertStored(
registerType(argv).storedType(),
m_typeResolver->stringType(), registerVariable(argv));
- if (firstArgIsReference) {
m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
if (argc > 1)
m_body += u".append(QLatin1Char(' ')))).append(";
else
m_body += u"))";
} else {
- m_body += firstArgStringConversion;
+ m_body += stringConversion(0);
if (argc > 1)
m_body += u".append(QLatin1Char(' ')).append(";
}
@@ -1623,21 +2121,98 @@ bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int
for (int i = 1; i < argc; ++i) {
if (i > 1)
m_body += u".append(QLatin1Char(' ')).append("_s;
- m_body += conversion(
- registerType(argv + i).storedType(),
- m_typeResolver->stringType(), consumedRegisterVariable(argv + i)) + u')';
+ m_body += stringConversion(i) + u')';
}
} else {
m_body += u"QString()";
}
- m_body += u";\n";
-
+ m_body += u";\n ";
+ generateSetInstructionPointer();
m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
m_body += u" }\n";
m_body += u"}\n";
return true;
}
+bool QQmlJSCodeGenerator::inlineArrayMethod(const QString &name, int base, int argc, int argv)
+{
+ const auto intType = m_typeResolver->int32Type();
+ const auto valueType = registerType(base).storedType()->valueType();
+ const auto boolType = m_typeResolver->boolType();
+ const auto stringType = m_typeResolver->stringType();
+ const auto baseType = registerType(base);
+
+ const QString baseVar = registerVariable(base);
+ const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
+ + name + u"(";
+
+ addInclude(u"QtQml/qjslist.h"_s);
+
+ if (name == u"includes" && argc > 0 && argc < 3) {
+ QString call = qjsListMethod
+ + convertStored(registerType(argv).storedType(), valueType,
+ consumedRegisterVariable(argv));
+ if (argc == 2) {
+ call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
+ consumedRegisterVariable(argv + 1));
+ }
+ call += u")";
+
+ m_body += m_state.accumulatorVariableOut + u" = "_s
+ + conversion(boolType, m_state.accumulatorOut(), call) + u";\n"_s;
+ return true;
+ }
+
+ if (name == u"toString" || (name == u"join" && argc < 2)) {
+ QString call = qjsListMethod;
+ if (argc == 1) {
+ call += convertStored(registerType(argv).storedType(), stringType,
+ consumedRegisterVariable(argv));
+ }
+ call += u")";
+
+ m_body += m_state.accumulatorVariableOut + u" = "_s
+ + conversion(stringType, m_state.accumulatorOut(), call) + u";\n"_s;
+ return true;
+ }
+
+ if (name == u"slice" && argc < 3) {
+ QString call = qjsListMethod;
+ for (int i = 0; i < argc; ++i) {
+ if (i > 0)
+ call += u", ";
+ call += convertStored(registerType(argv + i).storedType(), intType,
+ consumedRegisterVariable(argv + i));
+ }
+ call += u")";
+
+ const auto outType = baseType.storedType()->isListProperty()
+ ? m_typeResolver->globalType(m_typeResolver->qObjectListType())
+ : baseType;
+
+ m_body += m_state.accumulatorVariableOut + u" = "_s
+ + conversion(outType, m_state.accumulatorOut(), call) + u";\n"_s;
+ return true;
+ }
+
+ if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
+ QString call = qjsListMethod
+ + convertStored(registerType(argv).storedType(), valueType,
+ consumedRegisterVariable(argv));
+ if (argc == 2) {
+ call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
+ consumedRegisterVariable(argv + 1));
+ }
+ call += u")";
+
+ m_body += m_state.accumulatorVariableOut + u" = "_s
+ + conversion(intType, m_state.accumulatorOut(), call) + u";\n"_s;
+ return true;
+ }
+
+ return false;
+}
+
void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int argc, int argv)
{
INJECT_TRACE_INFO(generate_CallPropertyLookup);
@@ -1645,38 +2220,36 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
reject(u"call to untyped JavaScript function"_s);
+ const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
+
AccumulatorConverter registers(this);
const QQmlJSRegisterContent baseType = registerType(base);
- if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
- const QString name = m_jsUnitGenerator->stringForIndex(
- m_jsUnitGenerator->lookupNameIndex(index));
- if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), mathObject())) {
- if (inlineMathMethod(name, argc, argv))
- return;
- }
-
- if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), consoleObject())) {
- if (inlineConsoleMethod(name, argc, argv))
- return;
- }
+ const QString name = m_jsUnitGenerator->lookupName(index);
- if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType),
- m_typeResolver->stringType())) {
- if (inlineStringMethod(name, base, argc, argv))
- return;
- }
+ if (m_typeResolver->equals(scope, m_typeResolver->mathObject())) {
+ if (inlineMathMethod(name, argc, argv))
+ return;
+ } else if (m_typeResolver->equals(scope, m_typeResolver->consoleObject())) {
+ if (inlineConsoleMethod(name, argc, argv))
+ return;
+ } else if (m_typeResolver->equals(scope, m_typeResolver->stringType())) {
+ if (inlineStringMethod(name, base, argc, argv))
+ return;
+ } else if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
+ if (inlineArrayMethod(name, base, argc, argv))
+ return;
+ }
- if (m_typeResolver->canUseValueTypes()) {
- // This is possible, once we establish the right kind of lookup for it
- reject(u"call to property '%1' of %2"_s.arg(name, baseType.descriptiveName()));
- } else {
- // This is not possible.
- reject(u"call to property '%1' of value type reference %2"_s
- .arg(name, baseType.descriptiveName()));
- }
+ if (!scope->isReferenceType()) {
+ // This is possible, once we establish the right kind of lookup for it
+ reject(u"call to property '%1' of %2"_s.arg(name, baseType.descriptiveName()));
}
+ const QString inputPointer = resolveQObjectPointer(
+ scope, baseType, registerVariable(base),
+ u"Cannot call method '%1' of %2"_s.arg(name));
+
const QString indexString = QString::number(index);
m_body += u"{\n"_s;
@@ -1684,7 +2257,7 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
QString outVar;
m_body += argumentsList(argc, argv, &outVar);
const QString lookup = u"aotContext->callObjectPropertyLookup("_s + indexString
- + u", "_s + registerVariable(base)
+ + u", "_s + inputPointer
+ u", args, types, "_s + QString::number(argc) + u')';
const QString initialization = u"aotContext->initCallObjectPropertyLookup("_s
+ indexString + u')';
@@ -1769,9 +2342,83 @@ void QQmlJSCodeGenerator::generate_TailCall(int func, int thisObject, int argc,
void QQmlJSCodeGenerator::generate_Construct(int func, int argc, int argv)
{
+ INJECT_TRACE_INFO(generate_Construct);
Q_UNUSED(func);
- Q_UNUSED(argc);
- Q_UNUSED(argv);
+
+ const auto original = m_typeResolver->original(m_state.accumulatorOut());
+
+ if (m_typeResolver->registerContains(original, m_typeResolver->dateTimeType())) {
+ m_body += m_state.accumulatorVariableOut + u" = ";
+ if (argc == 0) {
+ m_body += conversion(
+ m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
+ u"QDateTime::currentDateTime()"_s) + u";\n";
+ return;
+ }
+
+ if (argc == 1
+ && m_typeResolver->registerContains(
+ m_state.readRegister(argv), m_typeResolver->dateTimeType())) {
+ m_body += conversion(
+ registerType(argv), m_state.readRegister(argv), registerVariable(argv))
+ + u";\n";
+ return;
+ }
+
+ QString ctorArgs;
+ constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
+ for (int i = 0; i < std::min(argc, maxArgc); ++i) {
+ if (i > 0)
+ ctorArgs += u", ";
+ ctorArgs += conversion(
+ registerType(argv + i), m_state.readRegister(argv + i),
+ registerVariable(argv + i));
+ }
+ m_body += conversion(
+ m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
+ u"aotContext->constructDateTime("_s + ctorArgs + u')') + u";\n";
+ return;
+ }
+
+ if (m_typeResolver->registerContains(original, m_typeResolver->variantListType())) {
+ rejectIfBadArray();
+
+ if (argc == 1
+ && m_typeResolver->registerContains(
+ m_state.readRegister(argv), m_typeResolver->realType())) {
+ addInclude(u"QtQml/qjslist.h"_s);
+
+ const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
+ + u"QLatin1String(\"Invalid array length\"));\n"_s;
+
+ const QString indexName = registerVariable(argv);
+ const auto indexType = m_typeResolver->containedType(registerType(argv));
+ if (!m_typeResolver->isNativeArrayIndex(indexType)) {
+ m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
+ + error;
+ generateReturnError();
+ m_body += u"}\n"_s;
+ } else if (!m_typeResolver->isUnsignedInteger(indexType)) {
+ m_body += u"if ("_s + indexName + u" < 0) {\n"_s
+ + error;
+ generateReturnError();
+ m_body += u"}\n"_s;
+ }
+
+ m_body += m_state.accumulatorVariableOut + u" = "_s
+ + m_state.accumulatorOut().storedType()->internalName() + u"();\n"_s;
+ m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
+ + u", aotContext->engine).resize("_s
+ + convertStored(
+ registerType(argv).storedType(), m_typeResolver->sizeType(),
+ consumedRegisterVariable(argv))
+ + u");\n"_s;
+ } else if (!m_error->isValid()) {
+ generateArrayInitializer(argc, argv);
+ }
+ return;
+ }
+
reject(u"Construct"_s);
}
@@ -1804,7 +2451,9 @@ void QQmlJSCodeGenerator::generate_UnwindToLabel(int level, int offset)
void QQmlJSCodeGenerator::generate_DeadTemporalZoneCheck(int name)
{
Q_UNUSED(name)
- BYTECODE_UNIMPLEMENTED();
+ INJECT_TRACE_INFO(generate_DeadTemporalZoneCheck);
+ // Nothing to do here. If we have statically asserted the dtz check in the type propagator
+ // the value cannot be empty. Otherwise we can't get here.
}
void QQmlJSCodeGenerator::generate_ThrowException()
@@ -1816,7 +2465,7 @@ void QQmlJSCodeGenerator::generate_ThrowException()
+ conversion(m_state.accumulatorIn(), m_typeResolver->globalType(
m_typeResolver->jsValueType()),
m_state.accumulatorVariableIn) + u");\n"_s;
- m_body += u"return "_s + errorReturnValue() + u";\n"_s;
+ generateReturnError();
m_skipUntilNextLabel = true;
resetState();
}
@@ -1876,33 +2525,96 @@ void QQmlJSCodeGenerator::generate_PopContext()
{
INJECT_TRACE_INFO(generate_PopContext);
- // Add a semicolon before the closing brace, in case there was a bare label before it.
- m_body += u";}\n"_s;
+ // Add an empty block before the closing brace, in case there was a bare label before it.
+ m_body += u"{}\n}\n"_s;
}
void QQmlJSCodeGenerator::generate_GetIterator(int iterator)
{
- Q_UNUSED(iterator)
- BYTECODE_UNIMPLEMENTED();
+ INJECT_TRACE_INFO(generate_GetIterator);
+
+ addInclude(u"QtQml/qjslist.h"_s);
+ const QQmlJSRegisterContent listType = m_state.accumulatorIn();
+ if (!listType.isList())
+ reject(u"iterator on non-list type"_s);
+
+ const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut();
+ if (!iteratorType.isProperty()) {
+ reject(u"using non-iterator as iterator"_s);
+ return;
+ }
+
+ const QString identifier = QString::number(iteratorType.baseLookupIndex());
+ const QString iteratorName = m_state.accumulatorVariableOut + u"Iterator" + identifier;
+ const QString listName = m_state.accumulatorVariableOut + u"List" + identifier;
+
+ m_body += u"QJSListFor"_s
+ + (iterator == int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s)
+ + u"Iterator "_s + iteratorName + u";\n";
+ m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n";
+
+ m_body += m_state.accumulatorVariableOut + u"->init(";
+ if (iterator == int(QQmlJS::AST::ForEachType::In)) {
+ if (!m_typeResolver->equals(iteratorType.storedType(), m_typeResolver->forInIteratorPtr()))
+ reject(u"using non-iterator as iterator"_s);
+ m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)";
+ }
+ m_body += u");\n";
+
+ if (iterator == int(QQmlJS::AST::ForEachType::Of)) {
+ if (!m_typeResolver->equals(iteratorType.storedType(), m_typeResolver->forOfIteratorPtr()))
+ reject(u"using non-iterator as iterator"_s);
+ m_body += u"const auto &" // Rely on life time extension for const refs
+ + listName + u" = " + consumedAccumulatorVariableIn();
+ }
}
-void QQmlJSCodeGenerator::generate_IteratorNext(int value, int done)
+void QQmlJSCodeGenerator::generate_IteratorNext(int value, int offset)
{
- Q_UNUSED(value)
- Q_UNUSED(done)
- BYTECODE_UNIMPLEMENTED();
+ INJECT_TRACE_INFO(generate_IteratorNext);
+
+ Q_ASSERT(value == m_state.changedRegisterIndex());
+ const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn();
+ if (!iteratorContent.isProperty()) {
+ reject(u"using non-iterator as iterator"_s);
+ return;
+ }
+
+ const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType();
+ const QString iteratorTypeName = iteratorType->internalName();
+ const QString listName = m_state.accumulatorVariableIn
+ + u"List" + QString::number(iteratorContent.baseLookupIndex());
+ QString qjsList;
+ if (m_typeResolver->equals(iteratorType, m_typeResolver->forOfIteratorPtr()))
+ qjsList = u"QJSList(&" + listName + u", aotContext->engine)";
+ else if (!m_typeResolver->equals(iteratorType, m_typeResolver->forInIteratorPtr()))
+ reject(u"using non-iterator as iterator"_s);
+
+ m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n ";
+ m_body += changedRegisterVariable() + u" = "
+ + conversion(
+ m_typeResolver->valueType(iteratorContent),
+ m_state.changedRegister(),
+ m_state.accumulatorVariableIn + u"->next(" + qjsList + u')')
+ + u";\n";
+ m_body += u"} else {\n ";
+ m_body += changedRegisterVariable() + u" = "
+ + conversion(m_typeResolver->voidType(), m_state.changedRegister(), QString());
+ m_body += u";\n ";
+ generateJumpCodeWithTypeConversions(offset);
+ m_body += u"\n}"_s;
}
-void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(int iterator, int object)
+void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(int iterator, int object, int offset)
{
Q_UNUSED(iterator)
Q_UNUSED(object)
+ Q_UNUSED(offset)
BYTECODE_UNIMPLEMENTED();
}
-void QQmlJSCodeGenerator::generate_IteratorClose(int done)
+void QQmlJSCodeGenerator::generate_IteratorClose()
{
- Q_UNUSED(done)
BYTECODE_UNIMPLEMENTED();
}
@@ -1946,55 +2658,136 @@ void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args)
{
INJECT_TRACE_INFO(generate_DefineArray);
- const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
+ rejectIfBadArray();
+ if (!m_error->isValid())
+ generateArrayInitializer(argc, args);
+}
- if (argc == 0) {
- m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(m_typeResolver->emptyListType(), stored, QString());
- m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->emptyListType());
+void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
+{
+ INJECT_TRACE_INFO(generate_DefineObjectLiteral);
+
+ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
+ if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value) {
+ reject(u"storing an object literal in a non-value type"_s);
return;
}
- if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
- // This rejects any attempt to store the list into a QVariant.
- // Therefore, we don't have to adjust the contained type below.
- reject(u"storing an array in a non-sequence type"_s);
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(m_state.accumulatorOut());
+
+ const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
+ Q_ASSERT(argc >= classSize);
+
+ if (m_typeResolver->equals(contained, m_typeResolver->varType())
+ || m_typeResolver->equals(contained, m_typeResolver->variantMapType())) {
+
+ m_body += m_state.accumulatorVariableOut + u" = QVariantMap {\n";
+ const QQmlJSScope::ConstPtr propType = m_typeResolver->varType();
+ for (int i = 0; i < classSize; ++i) {
+ m_body += u"{ "_s
+ + QQmlJSUtils::toLiteral(m_jsUnitGenerator->jsClassMember(internalClassId, i))
+ + u", "_s;
+ const int currentArg = args + i;
+ const QQmlJSScope::ConstPtr argType = registerType(currentArg).storedType();
+ const QString consumedArg = consumedRegisterVariable(currentArg);
+ m_body += convertStored(argType, propType, consumedArg) + u" },\n";
+ }
+
+ for (int i = classSize; i < argc; i += 3) {
+ const int nameArg = args + i + 1;
+ m_body += u"{ "_s
+ + conversion(
+ registerType(nameArg),
+ m_typeResolver->globalType(m_typeResolver->stringType()),
+ consumedRegisterVariable(nameArg))
+ + u", "_s;
+
+ const int valueArg = args + i + 2;
+ m_body += convertStored(
+ registerType(valueArg).storedType(),
+ propType,
+ consumedRegisterVariable(valueArg))
+ + u" },\n";
+ }
+
+ m_body += u"};\n";
return;
}
- const QQmlJSScope::ConstPtr value = stored->valueType();
- Q_ASSERT(value);
+ m_body += m_state.accumulatorVariableOut + u" = "_s + stored->augmentedInternalName();
+ const bool isVariantOrPrimitive = m_typeResolver->equals(stored, m_typeResolver->varType())
+ || m_typeResolver->equals(stored, m_typeResolver->jsPrimitiveType());
- QStringList initializer;
- for (int i = 0; i < argc; ++i) {
- initializer += conversion(registerType(args + i).storedType(), value,
- consumedRegisterVariable(args + i));
+ if (m_typeResolver->registerContains(m_state.accumulatorOut(), stored)) {
+ m_body += u"()";
+ } else if (isVariantOrPrimitive) {
+ m_body += u'(' + metaType(m_typeResolver->containedType(m_state.accumulatorOut())) + u')';
+ } else {
+ reject(u"storing an object literal in an unsupported container %1"_s
+ .arg(stored->internalName()));
}
+ m_body += u";\n";
- if (stored->isListProperty()) {
- reject(u"creating a QQmlListProperty not backed by a property"_s);
+ if (argc == 0)
+ return;
- // We can, technically, generate code for this. But it's dangerous:
- //
- // const QString storage = m_state.accumulatorVariableOut + u"_storage"_s;
- // m_body += stored->internalName() + u"::ListType " + storage
- // + u" = {"_s + initializer.join(u", "_s) + u"};\n"_s;
- // m_body += m_state.accumulatorVariableOut
- // + u" = " + stored->internalName() + u"(nullptr, &"_s + storage + u");\n"_s;
- } else {
- m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
- m_body += initializer.join(u", "_s);
- m_body += u"};\n";
+ bool isExtension = false;
+ if (!m_typeResolver->canPopulate(contained, m_typeResolver->variantMapType(), &isExtension)) {
+ reject(u"storing an object literal in a non-structured value type"_s);
}
-}
-void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
-{
- Q_UNUSED(internalClassId)
- Q_UNUSED(argc)
- Q_UNUSED(args)
- reject(u"DefineObjectLiteral"_s);
+ const QQmlJSScope::ConstPtr accessor = isExtension
+ ? contained->extensionType().scope
+ : contained;
+
+ m_body += u"{\n";
+ m_body += u" const QMetaObject *meta = ";
+ if (!isExtension && isVariantOrPrimitive)
+ m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
+ else
+ m_body += metaObject(accessor);
+ m_body += u";\n";
+
+ for (int i = 0; i < classSize; ++i) {
+ m_body += u" {\n";
+ const QString propName = m_jsUnitGenerator->jsClassMember(internalClassId, i);
+ const int currentArg = args + i;
+ const QQmlJSRegisterContent propType = m_state.readRegister(currentArg);
+ const QQmlJSRegisterContent argType = registerType(currentArg);
+ const QQmlJSMetaProperty property = contained->property(propName);
+ const QString consumedArg = consumedRegisterVariable(currentArg);
+ QString argument = conversion(argType, propType, consumedArg);
+
+ if (argument == consumedArg) {
+ argument = registerVariable(currentArg);
+ } else {
+ m_body += u" auto arg = "_s + argument + u";\n";
+ argument = u"arg"_s;
+ }
+
+ int index = property.index();
+ if (index == -1)
+ continue;
+
+ m_body += u" void *argv[] = { %1, nullptr };\n"_s
+ .arg(contentPointer(propType, argument));
+ m_body += u" meta->d.static_metacall(reinterpret_cast<QObject *>(";
+ m_body += contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
+ m_body += u"), QMetaObject::WriteProperty, ";
+ m_body += QString::number(index) + u", argv);\n";
+ m_body += u" }\n";
+ }
+
+ // This is not implemented because we cannot statically determine the type of the value and we
+ // don't want to rely on QVariant::convert() since that may give different results than
+ // the JavaScript coercion. We might still make it work by querying the QMetaProperty
+ // for its type at run time and runtime coercing to that, but we don't know whether that
+ // still pays off.
+ if (argc > classSize)
+ reject(u"non-literal keys of object literals"_s);
+
+ m_body += u"}\n";
+
}
void QQmlJSCodeGenerator::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -2023,7 +2816,12 @@ void QQmlJSCodeGenerator::generate_CreateRestParameter(int argIndex)
void QQmlJSCodeGenerator::generate_ConvertThisToObject()
{
- BYTECODE_UNIMPLEMENTED();
+ INJECT_TRACE_INFO(generate_ConvertThisToObject);
+
+ m_body += changedRegisterVariable() + u" = "_s
+ + conversion(m_typeResolver->qObjectType(), m_state.changedRegister(),
+ u"aotContext->thisObject()"_s)
+ + u";\n"_s;
}
void QQmlJSCodeGenerator::generate_LoadSuperConstructor()
@@ -2041,7 +2839,6 @@ void QQmlJSCodeGenerator::generate_Jump(int offset)
INJECT_TRACE_INFO(generate_Jump);
generateJumpCodeWithTypeConversions(offset);
- m_body += u";\n"_s;
m_skipUntilNextLabel = true;
resetState();
}
@@ -2051,11 +2848,10 @@ void QQmlJSCodeGenerator::generate_JumpTrue(int offset)
INJECT_TRACE_INFO(generate_JumpTrue);
m_body += u"if ("_s;
- m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
- m_state.accumulatorVariableIn);
+ m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
+ m_state.accumulatorVariableIn);
m_body += u") "_s;
generateJumpCodeWithTypeConversions(offset);
- m_body += u";\n"_s;
}
void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
@@ -2063,11 +2859,10 @@ void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
INJECT_TRACE_INFO(generate_JumpFalse);
m_body += u"if (!"_s;
- m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
- m_state.accumulatorVariableIn);
+ m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
+ m_state.accumulatorVariableIn);
m_body += u") "_s;
generateJumpCodeWithTypeConversions(offset);
- m_body += u";\n"_s;
}
void QQmlJSCodeGenerator::generate_JumpNoException(int offset)
@@ -2076,7 +2871,6 @@ void QQmlJSCodeGenerator::generate_JumpNoException(int offset)
m_body += u"if (!context->engine->hasException()) "_s;
generateJumpCodeWithTypeConversions(offset);
- m_body += u";\n"_s;
}
void QQmlJSCodeGenerator::generate_JumpNotUndefined(int offset)
@@ -2094,63 +2888,16 @@ void QQmlJSCodeGenerator::generate_CheckException()
void QQmlJSCodeGenerator::generate_CmpEqNull()
{
- INJECT_TRACE_INFO(generate_CmlEqNull);
-
- m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(
- m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
- u"QJSPrimitiveValue(QJSPrimitiveNull()).equals("_s
- + conversion(
- m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
- consumedAccumulatorVariableIn()) + u')');
- m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->boolType());
+ INJECT_TRACE_INFO(generate_CmpEqNull);
+ generateEqualityOperation(
+ m_typeResolver->globalType(m_typeResolver->nullType()), QString(), u"equals"_s, false);
}
void QQmlJSCodeGenerator::generate_CmpNeNull()
{
INJECT_TRACE_INFO(generate_CmlNeNull);
-
- m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(
- m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
- u"!QJSPrimitiveValue(QJSPrimitiveNull()).equals("_s
- + conversion(
- m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
- consumedAccumulatorVariableIn()) + u')');
- m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->boolType());
-}
-
-QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
-{
- if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->intType())
- || m_typeResolver->registerIsStoredIn(
- m_state.accumulatorIn(), m_typeResolver->uintType())) {
- return QString::number(lhsConst) + u" == "_s + m_state.accumulatorVariableIn;
- }
-
- if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->boolType())) {
- return QString::number(lhsConst) + u" == "_s
- + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->intType(),
- consumedAccumulatorVariableIn());
- }
-
- if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
- return conversion(m_typeResolver->intType(), m_typeResolver->realType(),
- QString::number(lhsConst)) + u" == "_s
- + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->realType(),
- consumedAccumulatorVariableIn());
- }
-
- QString result;
- result += conversion(m_typeResolver->intType(), m_typeResolver->jsPrimitiveType(),
- QString::number(lhsConst));
- result += u".equals("_s;
- result += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
- consumedAccumulatorVariableIn());
- result += u')';
- return result;
+ generateEqualityOperation(
+ m_typeResolver->globalType(m_typeResolver->nullType()), QString(), u"equals"_s, true);
}
QString QQmlJSCodeGenerator::getLookupPreparation(
@@ -2173,47 +2920,49 @@ QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content
if (m_typeResolver->registerContains(content, stored))
return u'&' + var;
- if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType()))
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())
+ || m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
return var + u".data()"_s;
+ }
if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u'&' + var;
- if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->intType())
- && m_typeResolver->containedType(content)->scopeType() == QQmlJSScope::EnumScope) {
+ if (m_typeResolver->isNumeric(content.storedType())
+ && m_typeResolver->containedType(content)->scopeType() == QQmlSA::ScopeType::EnumScope) {
return u'&' + var;
}
if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
return u'&' + var;
- reject(u"content pointer of non-QVariant wrapper type "_s + content.descriptiveName());
+ reject(u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
return QString();
}
QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, const QString &var)
{
const QQmlJSScope::ConstPtr stored = content.storedType();
- const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType(
- m_typeResolver->containedType(content));
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
if (m_typeResolver->equals(contained, stored))
return metaTypeFromType(stored);
- if (m_typeResolver->equals(stored, m_typeResolver->varType()))
- return var + u".metaType()"_s; // We expect the QVariant to be initialized
+ if (m_typeResolver->equals(stored, m_typeResolver->varType())
+ || m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
+ return var + u".metaType()"_s; // We expect the container to be initialized
+ }
if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- return metaTypeFromName(contained);
+ return metaType(contained);
- if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->intType())
- && m_typeResolver->containedType(content)->scopeType() == QQmlJSScope::EnumScope) {
- return metaTypeFromType(m_typeResolver->intType());
- }
+ const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(contained);
+ if (m_typeResolver->isNumeric(stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
+ return metaTypeFromType(nonComposite->baseType());
- if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
- return metaTypeFromType(m_typeResolver->listPropertyType());
+ if (stored->isListProperty() && contained->isListProperty())
+ return metaType(contained);
- reject(u"content type of non-QVariant wrapper type "_s + content.descriptiveName());
+ reject(u"content type of unsupported wrapper type "_s + content.descriptiveName());
return QString();
}
@@ -2221,32 +2970,30 @@ void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpEqInt);
- m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
- eqIntExpression(lhsConst)) + u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->boolType());
+ generateEqualityOperation(
+ m_typeResolver->globalType(m_typeResolver->int32Type()), QString::number(lhsConst),
+ u"equals"_s, false);
}
void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpNeInt);
- m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
- u"!("_s + eqIntExpression(lhsConst) + u')') + u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->boolType());
+ generateEqualityOperation(
+ m_typeResolver->globalType(m_typeResolver->int32Type()), QString::number(lhsConst),
+ u"equals"_s, true);
}
void QQmlJSCodeGenerator::generate_CmpEq(int lhs)
{
INJECT_TRACE_INFO(generate_CmpEq);
- generateEqualityOperation(lhs, u"equals"_s, false);
+ generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s, false);
}
void QQmlJSCodeGenerator::generate_CmpNe(int lhs)
{
INJECT_TRACE_INFO(generate_CmpNe);
- generateEqualityOperation(lhs, u"equals"_s, true);
+ generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s, true);
}
void QQmlJSCodeGenerator::generate_CmpGt(int lhs)
@@ -2276,13 +3023,13 @@ void QQmlJSCodeGenerator::generate_CmpLe(int lhs)
void QQmlJSCodeGenerator::generate_CmpStrictEqual(int lhs)
{
INJECT_TRACE_INFO(generate_CmpStrictEqual);
- generateEqualityOperation(lhs, u"strictlyEquals"_s, false);
+ generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s, false);
}
void QQmlJSCodeGenerator::generate_CmpStrictNotEqual(int lhs)
{
INJECT_TRACE_INFO(generate_CmpStrictNotEqual);
- generateEqualityOperation(lhs, u"strictlyEquals"_s, true);
+ generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s, true);
}
void QQmlJSCodeGenerator::generate_CmpIn(int lhs)
@@ -2302,22 +3049,70 @@ void QQmlJSCodeGenerator::generate_As(int lhs)
INJECT_TRACE_INFO(generate_As);
const QString input = registerVariable(lhs);
- const QQmlJSScope::ConstPtr contained
- = m_typeResolver->containedType(m_state.readRegister(lhs));
+ const QQmlJSRegisterContent inputContent = m_state.readRegister(lhs);
+ const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
+
+ // If the original output is a conversion, we're supposed to check for the contained
+ // type and if it doesn't match, set the result to null or undefined.
+ const QQmlJSRegisterContent originalContent = m_typeResolver->original(outputContent);
+ const QQmlJSScope::ConstPtr target = originalContent.storedType()->isReferenceType()
+ ? m_typeResolver->containedType(originalContent)
+ : m_typeResolver->extractNonVoidFromOptionalType(originalContent);
+
+ if (!target) {
+ reject(u"type assertion to unknown type"_s);
+ return;
+ }
+
+ const bool isTrivial = m_typeResolver->inherits(
+ m_typeResolver->originalContainedType(inputContent), target);
m_body += m_state.accumulatorVariableOut + u" = "_s;
- if (m_typeResolver->equals(
- m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType())
- && contained->isComposite()) {
- m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
- m_state.accumulatorVariableIn + u"->cast("_s + input + u')');
- } else {
- m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
- u'(' + metaObject(contained) + u")->cast("_s + input + u')');
+
+ if (!isTrivial && target->isReferenceType()) {
+ const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(target);
+ const QString inputConversion = inputContent.storedType()->isReferenceType()
+ ? input
+ : convertStored(inputContent.storedType(), genericContained, input);
+
+ if (target->isComposite() && m_typeResolver->equals(
+ m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType())) {
+ m_body += conversion(
+ genericContained, outputContent,
+ m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
+ } else {
+ m_body += conversion(
+ genericContained, outputContent,
+ u'(' + metaObject(target) + u")->cast("_s + inputConversion + u')');
+ }
+ m_body += u";\n"_s;
+ return;
}
- m_body += u";\n"_s;
+
+ if (m_typeResolver->registerIsStoredIn(inputContent, m_typeResolver->varType())
+ || m_typeResolver->registerIsStoredIn(inputContent, m_typeResolver->jsPrimitiveType())) {
+
+ const auto source = m_typeResolver->extractNonVoidFromOptionalType(
+ m_typeResolver->original(inputContent));
+
+ if (source && m_typeResolver->equals(source, target)) {
+ m_body += input + u".metaType() == "_s + metaType(target)
+ + u" ? " + conversion(inputContent, outputContent, input)
+ + u" : " + conversion(
+ m_typeResolver->globalType(m_typeResolver->voidType()),
+ outputContent, QString());
+ m_body += u";\n"_s;
+ return;
+ }
+ }
+
+ if (isTrivial) {
+ // No actual conversion necessary. The 'as' is a no-op
+ m_body += conversion(inputContent, m_state.accumulatorOut(), input) + u";\n"_s;
+ return;
+ }
+
+ reject(u"non-trivial value type assertion"_s);
}
void QQmlJSCodeGenerator::generate_UNot()
@@ -2453,7 +3248,6 @@ void QQmlJSCodeGenerator::generate_Exp(int lhs)
originalOut, m_state.accumulatorOut(),
u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->containedType(originalOut));
}
void QQmlJSCodeGenerator::generate_Mul(int lhs)
@@ -2472,10 +3266,10 @@ void QQmlJSCodeGenerator::generate_Mod(int lhs)
{
INJECT_TRACE_INFO(generate_Mod);
- const auto lhsVar = conversion(
+ const auto lhsVar = convertStored(
registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
consumedRegisterVariable(lhs));
- const auto rhsVar = conversion(
+ const auto rhsVar = convertStored(
m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
consumedAccumulatorVariableIn());
Q_ASSERT(m_error->isValid() || !lhsVar.isEmpty());
@@ -2483,11 +3277,9 @@ void QQmlJSCodeGenerator::generate_Mod(int lhs)
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_s;
- m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut().storedType(),
+ m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut(),
u'(' + lhsVar + u" % "_s + rhsVar + u')');
m_body += u";\n"_s;
-
- generateOutputVariantConversion(m_typeResolver->jsPrimitiveType());
}
void QQmlJSCodeGenerator::generate_Sub(int lhs)
@@ -2514,40 +3306,19 @@ void QQmlJSCodeGenerator::generate_GetTemplateObject(int index)
BYTECODE_UNIMPLEMENTED();
}
-static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
-{
- using Type = QV4::Moth::Instr::Type;
- switch (type) {
- case Type::PopContext:
- case Type::PopScriptContext:
- case Type::CreateCallContext:
- case Type::CreateCallContext_Wide:
- case Type::PushCatchContext:
- case Type::PushCatchContext_Wide:
- case Type::PushWithContext:
- case Type::PushWithContext_Wide:
- case Type::PushBlockContext:
- case Type::PushBlockContext_Wide:
- case Type::CloneBlockContext:
- case Type::CloneBlockContext_Wide:
- case Type::PushScriptContext:
- case Type::PushScriptContext_Wide:
- return true;
- default:
- break;
- }
- return false;
-}
-
QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
QV4::Moth::Instr::Type type)
{
- m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations));
+ m_state.State::operator=(nextStateFromAnnotations(m_state, m_annotations));
const auto accumulatorIn = m_state.registers.find(Accumulator);
if (accumulatorIn != m_state.registers.end()
&& isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
- m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
- .value(accumulatorIn.value().content.storedType());
+ const QQmlJSRegisterContent &content = accumulatorIn.value().content;
+ m_state.accumulatorVariableIn = m_registerVariables.value(RegisterVariablesKey {
+ content.storedType()->internalName(),
+ Accumulator,
+ content.resultLookupIndex()
+ }).variableName;
Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
} else {
m_state.accumulatorVariableIn.clear();
@@ -2574,21 +3345,12 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
|| !m_state.accumulatorVariableOut.isEmpty()
|| !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
- const int currentLine = currentSourceLocation().startLine;
- if (currentLine != m_lastLineNumberUsed) {
- const int nextLine = nextJSLine(currentLine);
- for (auto line = currentLine - 1; line < nextLine - 1; ++line) {
- m_body += u"// "_s;
- m_body += m_sourceCodeLines.value(line).trimmed();
- m_body += u'\n';
- }
- m_lastLineNumberUsed = currentLine;
- }
-
// If the instruction has no side effects and doesn't write any register, it's dead.
// We might still need the label, though, and the source code comment.
- if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty())
+ if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty()) {
+ generateJumpCodeWithTypeConversions(0);
return SkipInstruction;
+ }
return ProcessInstruction;
}
@@ -2607,86 +3369,225 @@ void QQmlJSCodeGenerator::generateSetInstructionPointer()
void QQmlJSCodeGenerator::generateExceptionCheck()
{
- m_body += u"if (aotContext->engine->hasError())\n"_s;
- m_body += u" return "_s + errorReturnValue() + u";\n"_s;
+ m_body += u"if (aotContext->engine->hasError()) {\n"_s;
+ generateReturnError();
+ m_body += u"}\n"_s;
}
-void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &function, bool invert)
+void QQmlJSCodeGenerator::generateEqualityOperation(
+ const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent,
+ const QString &lhsName, const QString &rhsName, const QString &function, bool invert)
{
- const QQmlJSRegisterContent lhsContent = registerType(lhs);
- const bool strictlyComparableWithVar = function == "strictlyEquals"_L1
- && canStrictlyCompareWithVar(m_typeResolver, lhsContent, m_state.accumulatorIn());
+ const bool lhsIsOptional = m_typeResolver->isOptionalType(lhsContent);
+ const bool rhsIsOptional = m_typeResolver->isOptionalType(rhsContent);
+
+ const auto rhsContained = rhsIsOptional
+ ? m_typeResolver->extractNonVoidFromOptionalType(rhsContent)
+ : m_typeResolver->containedType(rhsContent);
+
+ const auto lhsContained = lhsIsOptional
+ ? m_typeResolver->extractNonVoidFromOptionalType(lhsContent)
+ : m_typeResolver->containedType(lhsContent);
+
+ const bool isStrict = function == "strictlyEquals"_L1;
+ const bool strictlyComparableWithVar
+ = isStrict && canStrictlyCompareWithVar(m_typeResolver, lhsContained, rhsContained);
auto isComparable = [&]() {
- if (m_typeResolver->isPrimitive(lhsContent)
- && m_typeResolver->isPrimitive(m_state.accumulatorIn())) {
+ if (m_typeResolver->isPrimitive(lhsContent) && m_typeResolver->isPrimitive(rhsContent))
return true;
- }
- if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn().isEnumeration())
+ if (m_typeResolver->isNumeric(lhsContent) && rhsContent.isEnumeration())
return true;
- if (m_typeResolver->isNumeric(m_state.accumulatorIn()) && lhsContent.isEnumeration())
+ if (m_typeResolver->isNumeric(rhsContent) && lhsContent.isEnumeration())
return true;
if (strictlyComparableWithVar)
return true;
- if (canCompareWithQObject(m_typeResolver, lhsContent, m_state.accumulatorIn()))
+ if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained))
+ return true;
+ if (canCompareWithQUrl(m_typeResolver, lhsContained, rhsContained))
return true;
return false;
};
+ const auto retrieveOriginal = [this](const QQmlJSRegisterContent &content) {
+ const auto contained = m_typeResolver->containedType(content);
+ const auto original = m_typeResolver->original(content);
+ const auto containedOriginal = m_typeResolver->containedType(original);
+
+ if (m_typeResolver->equals(
+ m_typeResolver->genericType(containedOriginal), original.storedType())) {
+ // The original type doesn't need any wrapping.
+ return original;
+ } else if (m_typeResolver->equals(contained, containedOriginal)) {
+ if (original.isConversion()) {
+ // The original conversion origins are more accurate
+ return original.storedIn(content.storedType());
+ }
+ } else if (m_typeResolver->canHold(contained, containedOriginal)) {
+ return original.storedIn(content.storedType());
+ }
+
+ return content;
+ };
+
if (!isComparable()) {
- reject(u"incomparable types %1 and %2"_s.arg(m_state.accumulatorIn().descriptiveName(),
- lhsContent.descriptiveName()));
+ QQmlJSRegisterContent lhsOriginal = retrieveOriginal(lhsContent);
+ QQmlJSRegisterContent rhsOriginal = retrieveOriginal(rhsContent);
+ if (lhsOriginal != lhsContent || rhsOriginal != rhsContent) {
+ // If either side is simply a wrapping of a specific type into a more general one, we
+ // can compare the original types instead. You can't nest wrappings after all.
+ generateEqualityOperation(lhsOriginal, rhsOriginal,
+ conversion(lhsContent.storedType(), lhsOriginal, lhsName),
+ conversion(rhsContent.storedType(), rhsOriginal, rhsName),
+ function, invert);
+ return;
+ }
+
+ reject(u"incomparable types %1 and %2"_s.arg(
+ rhsContent.descriptiveName(), lhsContent.descriptiveName()));
}
const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
- const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn().storedType();
+ const QQmlJSScope::ConstPtr rhsType = rhsContent.storedType();
- const auto primitive = m_typeResolver->jsPrimitiveType();
- if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) {
- m_body += m_state.accumulatorVariableOut + u" = "_s;
- if (isTypeStorable(m_typeResolver, lhsType)) {
- m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
- consumedRegisterVariable(lhs) + (invert ? u" != "_s : u" == "_s)
- + consumedAccumulatorVariableIn());
- } else if (m_typeResolver->equals(lhsType, m_typeResolver->emptyListType())) {
- // We cannot compare two empty lists, because we don't know whether it's
- // the same instance or not. "[] === []" is false, but "var a = []; a === a" is true;
- reject(u"comparison of two empty lists"_s);
- } else {
- // null === null and undefined === undefined
- m_body += invert ? u"false"_s : u"true"_s;
- }
- } else if (strictlyComparableWithVar) {
+ if (strictlyComparableWithVar) {
// Determine which side is holding a storable type
- if (const auto registerVariableName = registerVariable(lhs);
- !registerVariableName.isEmpty()) {
- // lhs register holds var type
- generateVariantEqualityComparison(m_state.accumulatorIn(), registerVariableName,
- invert);
- } else {
+ if (!lhsName.isEmpty() && rhsName.isEmpty()) {
+ // lhs register holds var type and rhs is not storable
+ generateVariantEqualityComparison(rhsContent, lhsName, invert);
+ return;
+ }
+
+ if (!rhsName.isEmpty() && lhsName.isEmpty()) {
// lhs content is not storable and rhs is var type
- generateVariantEqualityComparison(lhsContent, m_state.accumulatorVariableIn, invert);
+ generateVariantEqualityComparison(lhsContent, rhsName, invert);
+ return;
}
- } else if (canCompareWithQObject(m_typeResolver, lhsContent, m_state.accumulatorIn())) {
- m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += u'('
- + (isTypeStorable(m_typeResolver, lhsContent.storedType())
- ? registerVariable(lhs)
- : m_state.accumulatorVariableIn)
- + (invert ? u" != "_s : u" == "_s) + u"nullptr)"_s;
- } else {
- m_body += m_state.accumulatorVariableOut + u" = "_s;
- m_body += conversion(
- m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
- (invert ? u"!"_s : QString())
- + conversion(registerType(lhs).storedType(), primitive,
- consumedRegisterVariable(lhs))
- + u'.' + function + u'(' + conversion(
- m_state.accumulatorIn().storedType(), primitive,
- consumedAccumulatorVariableIn())
- + u')');
+
+ if (m_typeResolver->registerContains(lhsContent, m_typeResolver->varType())) {
+ generateVariantEqualityComparison(rhsContent, rhsName, lhsName, invert);
+ return;
+ }
+
+ if (m_typeResolver->registerContains(rhsContent, m_typeResolver->varType())) {
+ generateVariantEqualityComparison(lhsContent, lhsName, rhsName, invert);
+ return;
+ }
+
+ // It shouldn't be possible to get here because optional null should be stored in
+ // QJSPrimitiveValue, not in QVariant. But let's rather be safe than sorry.
+ reject(u"comparison of optional null"_s);
}
+
+ const auto comparison = [&]() -> QString {
+ const auto primitive = m_typeResolver->jsPrimitiveType();
+ const QString sign = invert ? u" != "_s : u" == "_s;
+
+ if (m_typeResolver->equals(lhsType, rhsType)
+ && !m_typeResolver->equals(lhsType, primitive)
+ && !m_typeResolver->equals(lhsType, m_typeResolver->varType())) {
+
+ // Straight forward comparison of equal types,
+ // except QJSPrimitiveValue which has two comparison functions.
+
+ if (isTypeStorable(m_typeResolver, lhsType))
+ return lhsName + sign + rhsName;
+
+ // null === null and undefined === undefined
+ return invert ? u"false"_s : u"true"_s;
+ }
+
+ if (canCompareWithQObject(m_typeResolver, lhsType, rhsType)) {
+ // Comparison of QObject-derived with nullptr or different QObject-derived.
+ return (isTypeStorable(m_typeResolver, lhsType) ? lhsName : u"nullptr"_s)
+ + sign
+ + (isTypeStorable(m_typeResolver, rhsType) ? rhsName : u"nullptr"_s);
+ }
+
+ if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained)) {
+ // Comparison of optional QObject-derived with nullptr or different QObject-derived.
+ // Mind that null == undefined but null !== undefined
+ // Therefore the isStrict dance.
+
+ QString result;
+ if (isStrict) {
+ if (lhsIsOptional) {
+ if (rhsIsOptional) {
+ // If both are invalid we're fine
+ result += u"(!"_s
+ + lhsName + u".isValid() && !"_s
+ + rhsName + u".isValid()) || "_s;
+ }
+
+ result += u'(' + lhsName + u".isValid() && "_s;
+ } else {
+ result += u'(';
+ }
+
+ if (rhsIsOptional) {
+ result += rhsName + u".isValid() && "_s;
+ }
+ } else {
+ result += u'(';
+ }
+
+ // We do not implement comparison with explicit undefined, yet. Only with null.
+ Q_ASSERT(!m_typeResolver->equals(lhsType, m_typeResolver->voidType()));
+ Q_ASSERT(!m_typeResolver->equals(rhsType, m_typeResolver->voidType()));
+
+ const auto resolvedName = [&](const QString name) -> QString {
+ // If isStrict we check validity already before.
+ const QString content = u"*static_cast<QObject **>("_s + name + u".data())"_s;
+ return isStrict
+ ? content
+ : u'(' + name + u".isValid() ? "_s + content + u" : nullptr)"_s;
+ };
+
+ const QString lhsResolved = lhsIsOptional ? resolvedName(lhsName) : lhsName;
+ const QString rhsResolved = rhsIsOptional ? resolvedName(rhsName) : rhsName;
+
+ return (invert ? u"!("_s : u"("_s) + result
+ + (isTypeStorable(m_typeResolver, lhsType) ? lhsResolved : u"nullptr"_s)
+ + u" == "_s
+ + (isTypeStorable(m_typeResolver, rhsType) ? rhsResolved : u"nullptr"_s)
+ + u"))"_s;
+ }
+
+ if ((m_typeResolver->isUnsignedInteger(rhsType)
+ && m_typeResolver->isUnsignedInteger(lhsType))
+ || (m_typeResolver->isSignedInteger(rhsType)
+ && m_typeResolver->isSignedInteger(lhsType))) {
+ // Both integers of same signedness: Let the C++ compiler perform the type promotion
+ return lhsName + sign + rhsName;
+ }
+
+ if (m_typeResolver->equals(rhsType, m_typeResolver->boolType())
+ && m_typeResolver->isIntegral(lhsType)) {
+ // Integral and bool: We can promote the bool to the integral type
+ return lhsName + sign + convertStored(rhsType, lhsType, rhsName);
+ }
+
+ if (m_typeResolver->equals(lhsType, m_typeResolver->boolType())
+ && m_typeResolver->isIntegral(rhsType)) {
+ // Integral and bool: We can promote the bool to the integral type
+ return convertStored(lhsType, rhsType, lhsName) + sign + rhsName;
+ }
+
+ if (m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(rhsType)) {
+ // Both numbers: promote them to double
+ return convertStored(lhsType, m_typeResolver->realType(), lhsName)
+ + sign
+ + convertStored(rhsType, m_typeResolver->realType(), rhsName);
+ }
+
+ // If none of the above matches, we have to use QJSPrimitiveValue
+ return (invert ? u"!"_s : QString())
+ + convertStored(lhsType, primitive, lhsName)
+ + u'.' + function + u'(' + convertStored(rhsType, primitive, rhsName) + u')';
+ };
+
+ m_body += m_state.accumulatorVariableOut + u" = "_s;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut(), comparison());
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
@@ -2700,14 +3601,13 @@ void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOp
: m_typeResolver->jsPrimitiveType();
m_body += conversion(
- m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
- conversion(registerType(lhs).storedType(), compareType,
- consumedRegisterVariable(lhs))
+ m_typeResolver->boolType(), m_state.accumulatorOut(),
+ convertStored(registerType(lhs).storedType(), compareType,
+ consumedRegisterVariable(lhs))
+ u' ' + cppOperator + u' '
- + conversion(m_state.accumulatorIn().storedType(), compareType,
- consumedAccumulatorVariableIn()));
+ + convertStored(m_state.accumulatorIn().storedType(), compareType,
+ consumedAccumulatorVariableIn()));
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
@@ -2743,7 +3643,6 @@ void QQmlJSCodeGenerator::generateArithmeticOperation(
originalOut, m_state.accumulatorOut(),
u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
m_body += u";\n"_s;
- generateOutputVariantConversion(m_typeResolver->containedType(originalOut));
}
void QQmlJSCodeGenerator::generateArithmeticConstOperation(int rhsConst, const QString &cppOperator)
@@ -2751,14 +3650,15 @@ void QQmlJSCodeGenerator::generateArithmeticConstOperation(int rhsConst, const Q
generateArithmeticOperation(
conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
consumedAccumulatorVariableIn()),
- conversion(m_typeResolver->globalType(m_typeResolver->intType()),
+ conversion(m_typeResolver->globalType(m_typeResolver->int32Type()),
m_state.readAccumulator(), QString::number(rhsConst)),
cppOperator);
}
void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
{
- const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
+ const auto var = conversion(m_state.accumulatorIn(),
+ m_typeResolver->original(m_state.readAccumulator()),
consumedAccumulatorVariableIn());
if (var == m_state.accumulatorVariableOut) {
@@ -2775,10 +3675,7 @@ void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
}
m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
- m_typeResolver->original(m_state.accumulatorOut()),
- m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
-
- generateOutputVariantConversion(m_typeResolver->containedType(original));
+ original, m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
}
void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
@@ -2806,19 +3703,26 @@ void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
m_body += u"{\n"_s;
m_body += u"auto converted = "_s + var + u";\n"_s;
m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
- m_typeResolver->original(m_state.accumulatorOut()),
- m_state.accumulatorOut(), u'(' + cppOperator + u"converted)"_s) + u";\n"_s;
+ original, m_state.accumulatorOut(), u'('
+ + cppOperator + u"converted)"_s) + u";\n"_s;
m_body += u"}\n"_s;
- generateOutputVariantConversion(m_typeResolver->containedType(original));
}
void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
const QString &resultPreparation)
{
+ m_body += u"#ifndef QT_NO_DEBUG\n"_s;
+ generateSetInstructionPointer();
+ m_body += u"#endif\n"_s;
+
if (!resultPreparation.isEmpty())
m_body += resultPreparation + u";\n"_s;
m_body += u"while (!"_s + lookup + u") {\n"_s;
+
+ m_body += u"#ifdef QT_NO_DEBUG\n"_s;
generateSetInstructionPointer();
+ m_body += u"#endif\n"_s;
+
m_body += initialization + u";\n"_s;
generateExceptionCheck();
if (!resultPreparation.isEmpty())
@@ -2830,43 +3734,51 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset
{
QString conversionCode;
const int absoluteOffset = nextInstructionOffset() + relativeOffset;
- const auto annotation = m_annotations->find(absoluteOffset);
- if (annotation != m_annotations->constEnd()) {
+ const auto annotation = m_annotations.find(absoluteOffset);
+ if (static_cast<InstructionAnnotations::const_iterator>(annotation) != m_annotations.constEnd()) {
const auto &conversions = annotation->second.typeConversions;
for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
regIt != regEnd; ++regIt) {
- int registerIndex = regIt.key();
const QQmlJSRegisterContent targetType = regIt.value().content;
- if (!targetType.isValid())
+ if (!targetType.isValid() || !isTypeStorable(m_typeResolver, targetType.storedType()))
+ continue;
+
+ const int registerIndex = regIt.key();
+ const auto variable = m_registerVariables.constFind(RegisterVariablesKey {
+ targetType.storedType()->internalName(),
+ registerIndex,
+ targetType.resultLookupIndex()
+ });
+
+ if (variable == m_registerVariables.constEnd())
continue;
QQmlJSRegisterContent currentType;
QString currentVariable;
if (registerIndex == m_state.changedRegisterIndex()) {
- currentType = m_state.changedRegister();
currentVariable = changedRegisterVariable();
+ if (variable->variableName == currentVariable)
+ continue;
+
+ currentType = m_state.changedRegister();
+ currentVariable = u"std::move("_s + currentVariable + u')';
} else {
- auto it = m_state.registers.find(registerIndex);
- if (it == m_state.registers.end())
+ const auto it = m_state.registers.find(registerIndex);
+ if (it == m_state.registers.end()
+ || variable->variableName == registerVariable(registerIndex)) {
continue;
+ }
+
currentType = it.value().content;
currentVariable = consumedRegisterVariable(registerIndex);
}
// Actually == here. We want the jump code also for equal types
- if (currentType == targetType
- || !isTypeStorable(m_typeResolver, targetType.storedType())) {
+ if (currentType == targetType)
continue;
- }
- Q_ASSERT(m_registerVariables.contains(registerIndex));
- const auto &currentRegisterVariables = m_registerVariables[registerIndex];
- const auto variable = currentRegisterVariables.constFind(targetType.storedType());
- if (variable == currentRegisterVariables.end() || *variable == currentVariable)
- continue;
-
- conversionCode += *variable;
+ conversionCode += variable->variableName;
conversionCode += u" = "_s;
conversionCode += conversion(currentType, targetType, currentVariable);
conversionCode += u";\n"_s;
@@ -2880,41 +3792,60 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset
conversionCode += u" goto "_s + *labelIt + u";\n"_s;
}
- if (!conversionCode.isEmpty())
- m_body += u"{\n"_s + conversionCode + u"}\n"_s;
+ m_body += u"{\n"_s + conversionCode + u"}\n"_s;
}
QString QQmlJSCodeGenerator::registerVariable(int index) const
{
- auto it = m_registerVariables.find(index);
- if (it != m_registerVariables.end()) {
- const QString variable = it->value(registerType(index).storedType());
- if (!variable.isEmpty())
- return variable;
- }
+ const QQmlJSRegisterContent &content = registerType(index);
+ const auto it = m_registerVariables.constFind(RegisterVariablesKey {
+ content.storedType()->internalName(),
+ index,
+ content.resultLookupIndex()
+ });
+ if (it != m_registerVariables.constEnd())
+ return it->variableName;
return QString();
}
+QString QQmlJSCodeGenerator::lookupVariable(int lookupIndex) const
+{
+ for (auto it = m_registerVariables.constBegin(), end = m_registerVariables.constEnd(); it != end; ++it) {
+ if (it.key().lookupIndex == lookupIndex)
+ return it->variableName;
+ }
+ return QString();
+}
+
QString QQmlJSCodeGenerator::consumedRegisterVariable(int index) const
{
const QString var = registerVariable(index);
- if (var.isEmpty() || !m_state.canMoveReadRegister(index))
+ if (var.isEmpty() || !shouldMoveRegister(index))
return var;
return u"std::move(" + var + u")";
}
QString QQmlJSCodeGenerator::consumedAccumulatorVariableIn() const
{
- return m_state.canMoveReadRegister(Accumulator)
+ return shouldMoveRegister(Accumulator)
? u"std::move(" + m_state.accumulatorVariableIn + u")"
: m_state.accumulatorVariableIn;
}
QString QQmlJSCodeGenerator::changedRegisterVariable() const
{
- return m_registerVariables.value(m_state.changedRegisterIndex()).value(
- m_state.changedRegister().storedType());
+ const QQmlJSRegisterContent &changedRegister = m_state.changedRegister();
+
+ const QQmlJSScope::ConstPtr storedType = changedRegister.storedType();
+ if (storedType.isNull())
+ return QString();
+
+ return m_registerVariables.value(RegisterVariablesKey {
+ storedType->internalName(),
+ m_state.changedRegisterIndex(),
+ changedRegister.resultLookupIndex()
+ }).variableName;
}
QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const
@@ -2926,9 +3857,71 @@ QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const
return QQmlJSRegisterContent();
}
-QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
- const QQmlJSScope::ConstPtr &to,
- const QString &variable)
+QQmlJSRegisterContent QQmlJSCodeGenerator::lookupType(int lookupIndex) const
+{
+ auto it = m_state.lookups.find(lookupIndex);
+ if (it != m_state.lookups.end())
+ return it.value().content;
+
+ return QQmlJSRegisterContent();
+}
+
+bool QQmlJSCodeGenerator::shouldMoveRegister(int index) const
+{
+ return m_state.canMoveReadRegister(index)
+ && !m_typeResolver->isTriviallyCopyable(m_state.readRegister(index).storedType());
+}
+
+QString QQmlJSCodeGenerator::conversion(
+ const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
+{
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(to);
+
+ // If from is QJSPrimitiveValue and to contains a primitive we coerce using QJSPrimitiveValue
+ if (m_typeResolver->registerIsStoredIn(from, m_typeResolver->jsPrimitiveType())
+ && m_typeResolver->isPrimitive(to)) {
+
+ QString primitive = [&]() {
+ if (m_typeResolver->equals(contained, m_typeResolver->jsPrimitiveType()))
+ return variable;
+
+ const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
+ if (m_typeResolver->equals(contained, m_typeResolver->boolType()))
+ return conversion.arg(u"Boolean"_s);
+ if (m_typeResolver->isIntegral(to))
+ return conversion.arg(u"Integer"_s);
+ if (m_typeResolver->isNumeric(to))
+ return conversion.arg(u"Double"_s);
+ if (m_typeResolver->equals(contained, m_typeResolver->stringType()))
+ return conversion.arg(u"String"_s);
+ reject(u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
+ return QString();
+ }();
+
+ if (primitive.isEmpty())
+ return primitive;
+
+ return convertStored(m_typeResolver->jsPrimitiveType(), to.storedType(), primitive);
+ }
+
+ if (m_typeResolver->registerIsStoredIn(to, contained)
+ || m_typeResolver->isNumeric(to.storedType())
+ || to.storedType()->isReferenceType()
+ || m_typeResolver->registerContains(from, contained)) {
+ // If:
+ // * the output is not actually wrapped at all, or
+ // * the output is stored in a numeric type (as there are no internals to a number), or
+ // * the output is a QObject pointer, or
+ // * we merely wrap the value into a new container,
+ // we can convert by stored type.
+ return convertStored(from.storedType(), to.storedType(), variable);
+ } else {
+ return convertContained(from, to, variable);
+ }
+}
+
+QString QQmlJSCodeGenerator::convertStored(
+ const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to, const QString &variable)
{
// TODO: most values can be moved, which is much more efficient with the common types.
// add a move(from, to, variable) function that implements the moves.
@@ -2939,32 +3932,36 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
const auto boolType = m_typeResolver->boolType();
- auto zeroBoolOrNumeric = [&](const QQmlJSScope::ConstPtr &to) {
+ auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) {
if (m_typeResolver->equals(to, boolType))
return u"false"_s;
- if (m_typeResolver->equals(to, m_typeResolver->intType()))
+ if (m_typeResolver->isSignedInteger(to))
return u"0"_s;
- if (m_typeResolver->equals(to, m_typeResolver->uintType()))
+ if (m_typeResolver->isUnsignedInteger(to))
return u"0u"_s;
- if (m_typeResolver->equals(to, m_typeResolver->floatType()))
- return u"0.0f"_s;
- if (m_typeResolver->equals(to, m_typeResolver->realType()))
- return u"0.0"_s;
return QString();
};
if (m_typeResolver->equals(from, m_typeResolver->voidType())) {
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
- const QString zero = zeroBoolOrNumeric(to);
+ const QString zero = zeroBoolOrInt(to);
if (!zero.isEmpty())
return zero;
+ if (m_typeResolver->equals(to, m_typeResolver->floatType()))
+ return u"std::numeric_limits<float>::quiet_NaN()"_s;
+ if (m_typeResolver->equals(to, m_typeResolver->realType()))
+ return u"std::numeric_limits<double>::quiet_NaN()"_s;
if (m_typeResolver->equals(to, m_typeResolver->stringType()))
return QQmlJSUtils::toLiteral(u"undefined"_s);
+ if (m_typeResolver->equals(to, m_typeResolver->varType()))
+ return u"QVariant()"_s;
+ if (m_typeResolver->equals(to, m_typeResolver->jsValueType()))
+ return u"QJSValue();"_s;
+ if (m_typeResolver->equals(to, m_typeResolver->jsPrimitiveType()))
+ return u"QJSPrimitiveValue()"_s;
if (m_typeResolver->equals(from, to))
return QString();
- // Anything else is just the default constructed type.
- return to->augmentedInternalName() + u"()"_s;
}
if (m_typeResolver->equals(from, m_typeResolver->nullType())) {
@@ -2976,9 +3973,13 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
if (m_typeResolver->equals(to, varType))
return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
- const QString zero = zeroBoolOrNumeric(to);
+ const QString zero = zeroBoolOrInt(to);
if (!zero.isEmpty())
return zero;
+ if (m_typeResolver->equals(to, m_typeResolver->floatType()))
+ return u"0.0f"_s;
+ if (m_typeResolver->equals(to, m_typeResolver->realType()))
+ return u"0.0"_s;
if (m_typeResolver->equals(to, m_typeResolver->stringType()))
return QQmlJSUtils::toLiteral(u"null"_s);
if (m_typeResolver->equals(from, to))
@@ -2986,16 +3987,6 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
reject(u"Conversion from null to %1"_s.arg(to->internalName()));
}
- if (m_typeResolver->equals(from, m_typeResolver->emptyListType())) {
- if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
- return castTargetName(to) + u"()"_s;
- if (m_typeResolver->equals(to, m_typeResolver->varType()))
- return u"QVariant(QVariantList())"_s;
- if (m_typeResolver->equals(from, to))
- return QString();
- reject(u"Conversion from empty list to %1"_s.arg(to->internalName()));
- }
-
if (m_typeResolver->equals(from, to))
return variable;
@@ -3029,17 +4020,17 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
return m_typeResolver->isNumeric(m_typeResolver->globalType(type))
|| m_typeResolver->equals(type, m_typeResolver->boolType())
- || type->scopeType() == QQmlJSScope::EnumScope;
+ || type->scopeType() == QQmlSA::ScopeType::EnumScope;
};
if (m_typeResolver->equals(from, m_typeResolver->realType())
|| m_typeResolver->equals(from, m_typeResolver->floatType())) {
- if (m_typeResolver->equals(to, m_typeResolver->intType()))
+ if (m_typeResolver->isSignedInteger(to))
return u"QJSNumberCoercion::toInteger("_s + variable + u')';
- if (m_typeResolver->equals(to, m_typeResolver->uintType()))
+ if (m_typeResolver->isUnsignedInteger(to))
return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
if (m_typeResolver->equals(to, m_typeResolver->boolType()))
- return u'(' + variable + u" && !std::isnan("_s + variable + u"))"_s;
+ return u"[](double moved){ return moved && !std::isnan(moved); }("_s + variable + u')';
}
if (isBoolOrNumber(from) && isBoolOrNumber(to))
@@ -3051,10 +4042,12 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
return variable + u".toDouble()"_s;
if (m_typeResolver->equals(to, boolType))
return variable + u".toBoolean()"_s;
- if (m_typeResolver->equals(to, m_typeResolver->intType()))
- return variable + u".toInteger()"_s;
- if (m_typeResolver->equals(to, m_typeResolver->uintType()))
- return u"uint("_s + variable + u".toInteger())"_s;
+ if (m_typeResolver->equals(to, m_typeResolver->int64Type())
+ || m_typeResolver->equals(to, m_typeResolver->uint64Type())) {
+ return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
+ }
+ if (m_typeResolver->isIntegral(to))
+ return u"%1(%2.toInteger())"_s.arg(to->internalName(), variable);
if (m_typeResolver->equals(to, m_typeResolver->stringType()))
return variable + u".toString()"_s;
if (m_typeResolver->equals(to, jsValueType))
@@ -3079,10 +4072,15 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->voidType()));
if (m_typeResolver->equals(from, m_typeResolver->boolType())
- || m_typeResolver->equals(from, m_typeResolver->intType())
+ || m_typeResolver->equals(from, m_typeResolver->int32Type())
|| m_typeResolver->equals(from, m_typeResolver->realType())
|| m_typeResolver->equals(from, m_typeResolver->stringType())) {
return u"QJSPrimitiveValue("_s + variable + u')';
+ } else if (m_typeResolver->equals(from, m_typeResolver->int16Type())
+ || m_typeResolver->equals(from, m_typeResolver->int8Type())
+ || m_typeResolver->equals(from, m_typeResolver->uint16Type())
+ || m_typeResolver->equals(from, m_typeResolver->uint8Type())) {
+ return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
} else if (m_typeResolver->isNumeric(from)) {
return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
}
@@ -3130,7 +4128,8 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
m_typeResolver->dateTimeType(),
m_typeResolver->dateType(),
m_typeResolver->timeType(),
- m_typeResolver->stringType()}) {
+ m_typeResolver->stringType(),
+ m_typeResolver->realType()}) {
if (m_typeResolver->equals(to, targetType)) {
return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
originType->internalName(), targetType->internalName(), variable);
@@ -3145,9 +4144,9 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
{
if (m_typeResolver->equals(type, m_typeResolver->boolType()))
return expression + u".toBoolean()"_s;
- if (m_typeResolver->equals(type, m_typeResolver->intType()))
+ if (m_typeResolver->isSignedInteger(type))
return expression + u".toInteger()"_s;
- if (m_typeResolver->equals(type, m_typeResolver->uintType()))
+ if (m_typeResolver->isUnsignedInteger(type))
return u"uint("_s + expression + u".toInteger())"_s;
if (m_typeResolver->equals(type, m_typeResolver->realType()))
return expression + u".toDouble()"_s;
@@ -3160,7 +4159,7 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
const QString retrieve = retrieveFromPrimitive(
- to, conversion(from, m_typeResolver->jsPrimitiveType(), variable));
+ to, convertStored(from, m_typeResolver->jsPrimitiveType(), variable));
if (!retrieve.isEmpty())
return retrieve;
}
@@ -3170,25 +4169,122 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
+ castTargetName(to) + u">("_s + variable + u')';
}
+ // Any value type is a non-null JS 'object' and therefore coerces to true.
+ if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
+ // All the interesting cases are already handled above:
+ Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->nullType()));
+ Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->voidType()));
+ Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
+ Q_ASSERT(!isBoolOrNumber(from));
+
+ return u"true"_s;
+ }
+
+ if (m_typeResolver->areEquivalentLists(from, to))
+ return variable;
+
+ if (from->isListProperty()
+ && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
+ && to->valueType()->isReferenceType()
+ && !to->isListProperty()) {
+ return variable + u".toList<"_s + to->internalName() + u">()"_s;
+ }
+
+ bool isExtension = false;
+ if (m_typeResolver->canPopulate(to, from, &isExtension)) {
+ reject(u"populating "_s + to->internalName() + u" from "_s + from->internalName());
+ } else if (const auto ctor = m_typeResolver->selectConstructor(to, from, &isExtension);
+ ctor.isValid()) {
+ const auto argumentTypes = ctor.parameters();
+ return (isExtension ? to->extensionType().scope->internalName() : to->internalName())
+ + u"("_s + convertStored(from, argumentTypes[0].type(), variable) + u")"_s;
+ }
+
+ if (m_typeResolver->equals(to, m_typeResolver->stringType())
+ && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
+ addInclude(u"QtQml/qjslist.h"_s);
+
+ // Extend the life time of whatever variable is across the call to toString().
+ // variable may be an rvalue.
+ return u"[&](auto &&l){ return QJSList(&l, aotContext->engine).toString(); }("_s
+ + variable + u')';
+ }
+
// TODO: add more conversions
reject(u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
return QString();
}
-int QQmlJSCodeGenerator::nextJSLine(uint line) const
+QString QQmlJSCodeGenerator::convertContained(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
{
- auto findLine = [](int line, const QV4::CompiledData::CodeOffsetToLineAndStatement &entry) {
- return entry.line > line;
- };
- const auto codeToLine
- = std::upper_bound(m_context->lineAndStatementNumberMapping.constBegin(),
- m_context->lineAndStatementNumberMapping.constEnd(),
- line,
- findLine);
- bool bNoNextLine = m_context->lineAndStatementNumberMapping.constEnd() == codeToLine;
+ const QQmlJSScope::ConstPtr containedFrom = m_typeResolver->containedType(from);
+ const QQmlJSScope::ConstPtr containedTo = m_typeResolver->containedType(to);
+
+ // Those should be handled before, by convertStored().
+ Q_ASSERT(!to.storedType()->isReferenceType());
+ Q_ASSERT(!m_typeResolver->registerIsStoredIn(to, containedTo));
+ Q_ASSERT(!m_typeResolver->isIntegral(from.storedType()));
+ Q_ASSERT(!m_typeResolver->equals(containedFrom, containedTo));
+
+ if (!m_typeResolver->registerIsStoredIn(to, m_typeResolver->varType()) &&
+ !m_typeResolver->registerIsStoredIn(to, m_typeResolver->jsPrimitiveType())) {
+ reject(u"internal conversion into unsupported wrapper type."_s);
+ return QString();
+ }
+
+ bool isExtension = false;
+ if (m_typeResolver->canPopulate(containedTo, containedFrom, &isExtension)) {
+ reject(u"populating "_s + containedTo->internalName()
+ + u" from "_s + containedFrom->internalName());
+ return QString();
+ } else if (const auto ctor = m_typeResolver->selectConstructor(
+ containedTo, containedFrom, &isExtension); ctor.isValid()) {
+ const auto argumentTypes = ctor.parameters();
+ const QQmlJSScope::ConstPtr argumentType = argumentTypes[0].type();
+
+ // We need to store the converted argument in a temporary
+ // because it might not be an lvalue.
+
+ QString input;
+ QString argPointer;
- return static_cast<int>(bNoNextLine ? -1 : codeToLine->line);
+ if (m_typeResolver->equals(argumentType, containedFrom)) {
+ input = variable;
+ argPointer = contentPointer(from, u"arg"_s);
+ } else {
+ const QQmlJSRegisterContent argument
+ = m_typeResolver->globalType(argumentType)
+ .storedIn(m_typeResolver->genericType(argumentType));
+ input = conversion(from, argument, variable);
+ argPointer = contentPointer(argument, u"arg"_s);
+ }
+
+ return u"[&](){ auto arg = " + input
+ + u"; return aotContext->constructValueType("_s + metaType(containedTo)
+ + u", "_s + metaObject(
+ isExtension ? containedTo->extensionType().scope : containedTo)
+ + u", "_s + QString::number(int(ctor.constructorIndex()))
+ + u", "_s + argPointer + u"); }()"_s;
+ }
+
+ const auto originalFrom = m_typeResolver->original(from);
+ const auto containedOriginalFrom = m_typeResolver->containedType(originalFrom);
+ if (!m_typeResolver->equals(containedFrom, containedOriginalFrom)
+ && m_typeResolver->canHold(containedFrom, containedOriginalFrom)) {
+ // If from is simply a wrapping of a specific type into a more general one, we can convert
+ // the original type instead. You can't nest wrappings after all.
+ return conversion(originalFrom.storedIn(from.storedType()), to, variable);
+ }
+
+ if (m_typeResolver->isPrimitive(containedFrom) && m_typeResolver->isPrimitive(containedTo)) {
+ const QQmlJSRegisterContent intermediate = from.storedIn(m_typeResolver->jsPrimitiveType());
+ return conversion(intermediate, to, conversion(from, intermediate, variable));
+ }
+
+ reject(u"internal conversion with incompatible or ambiguous types: %1 -> %2"_s
+ .arg(from.descriptiveName(), to.descriptiveName()));
+ return QString();
}
void QQmlJSCodeGenerator::reject(const QString &thing)
@@ -3244,13 +4340,10 @@ QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
generator->m_state.accumulatorOut(), accumulatorOut,
- generator->m_state.accumulatorVariableOut) + u";\n"_s;
- const auto contained = generator->m_typeResolver->containedType(
- generator->m_state.accumulatorOut());
+ u"std::move("_s + generator->m_state.accumulatorVariableOut + u')') + u";\n"_s;
generator->m_body += u"}\n"_s;
generator->m_state.setRegister(Accumulator, accumulatorOut);
generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
- generator->generateOutputVariantConversion(contained);
} else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
generator->m_body += u"}\n"_s;
generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index 4719764294..2edccf31ae 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -27,17 +27,17 @@
QT_BEGIN_NAMESPACE
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSCodeGenerator : public QQmlJSCompilePass
+class Q_QMLCOMPILER_EXPORT QQmlJSCodeGenerator : public QQmlJSCompilePass
{
public:
QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext,
- const QV4::Compiler::JSUnitGenerator *unitGenerator,
- const QQmlJSTypeResolver *typeResolver,
- QQmlJSLogger *logger, const QStringList &sourceCodeLines);
+ const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks, InstructionAnnotations annotations);
~QQmlJSCodeGenerator() = default;
- QQmlJSAotFunction run(const Function *function, const InstructionAnnotations *annotations,
- QQmlJS::DiagnosticMessage *error);
+ QQmlJSAotFunction run(const Function *function, QQmlJS::DiagnosticMessage *error,
+ bool basicBlocksValidationFailed);
protected:
struct CodegenState : public State
@@ -49,7 +49,7 @@ protected:
// This is an RAII helper we can use to automatically convert the result of "inflexible"
// operations to the desired type. For example GetLookup can only retrieve the type of
// the property we're looking up. If we want to store a different type, we need to convert.
- struct Q_QMLCOMPILER_PRIVATE_EXPORT AccumulatorConverter
+ struct Q_QMLCOMPILER_EXPORT AccumulatorConverter
{
Q_DISABLE_COPY_MOVE(AccumulatorConverter);
AccumulatorConverter(QQmlJSCodeGenerator *generator);
@@ -63,6 +63,7 @@ protected:
};
virtual QString metaObject(const QQmlJSScope::ConstPtr &objectType);
+ virtual QString metaType(const QQmlJSScope::ConstPtr &type);
void generate_Ret() override;
void generate_Debug() override;
@@ -132,9 +133,9 @@ protected:
void generate_PopScriptContext() override;
void generate_PopContext() override;
void generate_GetIterator(int iterator) override;
- void generate_IteratorNext(int value, int done) override;
- void generate_IteratorNextForYieldStar(int iterator, int object) override;
- void generate_IteratorClose(int done) override;
+ void generate_IteratorNext(int value, int offset) override;
+ void generate_IteratorNextForYieldStar(int iterator, int object, int offset) override;
+ void generate_IteratorClose() override;
void generate_DestructureRestElement() override;
void generate_DeleteProperty(int base, int index) override;
void generate_DeleteName(int name) override;
@@ -210,20 +211,45 @@ protected:
QString conversion(const QQmlJSRegisterContent &from,
const QQmlJSRegisterContent &to,
+ const QString &variable);
+
+ QString conversion(const QQmlJSScope::ConstPtr &from,
+ const QQmlJSRegisterContent &to,
const QString &variable)
{
- return conversion(from.storedType(), to.storedType(), variable);
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(to);
+ if (m_typeResolver->equals(to.storedType(), contained)
+ || m_typeResolver->isNumeric(to.storedType())
+ || to.storedType()->isReferenceType()
+ || m_typeResolver->equals(from, contained)) {
+ // If:
+ // * the output is not actually wrapped at all, or
+ // * the output is a number (as there are no internals to a number)
+ // * the output is a QObject pointer, or
+ // * we merely wrap the value into a new container,
+ // we can convert by stored type.
+ return convertStored(from, to.storedType(), variable);
+ } else {
+ return convertContained(m_typeResolver->globalType(from), to, variable);
+ }
}
- QString conversion(const QQmlJSScope::ConstPtr &from,
- const QQmlJSScope::ConstPtr &to,
- const QString &variable);
+ QString convertStored(const QQmlJSScope::ConstPtr &from,
+ const QQmlJSScope::ConstPtr &to,
+ const QString &variable);
+
+ QString convertContained(const QQmlJSRegisterContent &from,
+ const QQmlJSRegisterContent &to,
+ const QString &variable);
- QString errorReturnValue();
+ void generateReturnError();
void reject(const QString &thing);
QString metaTypeFromType(const QQmlJSScope::ConstPtr &type) const;
QString metaTypeFromName(const QQmlJSScope::ConstPtr &type) const;
+ QString compositeMetaType(const QString &elementName) const;
+ QString compositeListMetaType(const QString &elementName) const;
+
QString contentPointer(const QQmlJSRegisterContent &content, const QString &var);
QString contentType(const QQmlJSRegisterContent &content, const QString &var);
@@ -237,11 +263,14 @@ protected:
void generateEnumLookup(int index);
QString registerVariable(int index) const;
+ QString lookupVariable(int lookupIndex) const;
QString consumedRegisterVariable(int index) const;
QString consumedAccumulatorVariableIn() const;
QString changedRegisterVariable() const;
QQmlJSRegisterContent registerType(int index) const;
+ QQmlJSRegisterContent lookupType(int lookupIndex) const;
+ bool shouldMoveRegister(int index) const;
QString m_body;
CodegenState m_state;
@@ -250,7 +279,18 @@ protected:
private:
void generateExceptionCheck();
- void generateEqualityOperation(int lhs, const QString &function, bool invert);
+
+ void generateEqualityOperation(
+ const QQmlJSRegisterContent &lhsContent, const QString &lhsName,
+ const QString &function, bool invert) {
+ generateEqualityOperation(
+ lhsContent, m_state.accumulatorIn(), lhsName, m_state.accumulatorVariableIn,
+ function, invert);
+ }
+
+ void generateEqualityOperation(
+ const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent,
+ const QString &lhsName, const QString &rhsName, const QString &function, bool invert);
void generateCompareOperation(int lhs, const QString &cppOperator);
void generateArithmeticOperation(int lhs, const QString &cppOperator);
void generateShiftOperation(int lhs, const QString &cppOperator);
@@ -262,10 +302,16 @@ private:
void generateInPlaceOperation(const QString &cppOperator);
void generateMoveOutVar(const QString &outVar);
void generateTypeLookup(int index);
- void generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType);
- void generateVariantEqualityComparison(const QQmlJSRegisterContent &nonStorable,
- const QString &registerName, bool invert);
+ void generateVariantEqualityComparison(
+ const QQmlJSRegisterContent &nonStorable, const QString &registerName, bool invert);
+ void generateVariantEqualityComparison(
+ const QQmlJSRegisterContent &storableContent, const QString &typedRegisterName,
+ const QString &varRegisterName, bool invert);
+ void generateArrayInitializer(int argc, int argv);
+ void generateWriteBack(int registerIndex);
void rejectIfNonQObjectOut(const QString &error);
+ void rejectIfBadArray();
+
QString eqIntExpression(int lhsConst);
QString argumentsList(int argc, int argv, QString *outVar);
@@ -275,34 +321,64 @@ private:
bool inlineTranslateMethod(const QString &name, int argc, int argv);
bool inlineMathMethod(const QString &name, int argc, int argv);
bool inlineConsoleMethod(const QString &name, int argc, int argv);
+ bool inlineArrayMethod(const QString &name, int base, int argc, int argv);
- QQmlJSScope::ConstPtr mathObject() const
- {
- using namespace Qt::StringLiterals;
- return m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
- }
+ void generate_GetLookupHelper(int index);
- QQmlJSScope::ConstPtr consoleObject() const
- {
- using namespace Qt::StringLiterals;
- return m_typeResolver->jsGlobalObject()->property(u"console"_s).type();
- }
-
- int nextJSLine(uint line) const;
-
- QStringList m_sourceCodeLines;
+ QString resolveValueTypeContentPointer(
+ const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
+ const QString &variable, const QString &errorMessage);
+ QString resolveQObjectPointer(
+ const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
+ const QString &variable, const QString &errorMessage);
+ bool generateContentPointerCheck(
+ const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
+ const QString &variable, const QString &errorMessage);
// map from instruction offset to sequential label number
QHash<int, QString> m_labels;
const QV4::Compiler::Context *m_context = nullptr;
- const InstructionAnnotations *m_annotations = nullptr;
- int m_lastLineNumberUsed = -1;
bool m_skipUntilNextLabel = false;
QStringList m_includes;
- QHash<int, QHash<QQmlJSScope::ConstPtr, QString>> m_registerVariables;
+
+ struct RegisterVariablesKey
+ {
+ QString internalName;
+ int registerIndex = -1;
+ int lookupIndex = QQmlJSRegisterContent::InvalidLookupIndex;
+
+ private:
+ friend size_t qHash(const RegisterVariablesKey &key, size_t seed = 0) noexcept
+ {
+ return qHashMulti(seed, key.internalName, key.registerIndex, key.lookupIndex);
+ }
+
+ friend bool operator==(
+ const RegisterVariablesKey &lhs, const RegisterVariablesKey &rhs) noexcept
+ {
+ return lhs.registerIndex == rhs.registerIndex
+ && lhs.lookupIndex == rhs.lookupIndex
+ && lhs.internalName == rhs.internalName;
+ }
+
+ friend bool operator!=(
+ const RegisterVariablesKey &lhs, const RegisterVariablesKey &rhs) noexcept
+ {
+ return !(lhs == rhs);
+ }
+ };
+
+ struct RegisterVariablesValue
+ {
+ QString variableName;
+ QQmlJSScope::ConstPtr storedType;
+ int numTracked = 0;
+ };
+
+ QHash<RegisterVariablesKey, RegisterVariablesValue> m_registerVariables;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index 139e970750..a18b906d8d 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -32,6 +32,7 @@ public:
enum RegisterShortcuts {
InvalidRegister = -1,
Accumulator = QV4::CallData::Accumulator,
+ This = QV4::CallData::This,
FirstArgument = QV4::CallData::OffsetCount
};
@@ -41,17 +42,32 @@ public:
{
QQmlJSRegisterContent content;
bool canMove = false;
+ bool affectedBySideEffects = false;
private:
friend bool operator==(const VirtualRegister &a, const VirtualRegister &b)
{
- return a.content == b.content && a.canMove == b.canMove;
+ return a.content == b.content && a.canMove == b.canMove
+ && a.affectedBySideEffects == b.affectedBySideEffects;
}
};
// map from register index to expected type
using VirtualRegisters = QFlatMap<int, VirtualRegister>;
+ struct BasicBlock
+ {
+ QList<int> jumpOrigins;
+ QList<int> readRegisters;
+ QList<QQmlJSScope::ConstPtr> readTypes;
+ int jumpTarget = -1;
+ bool jumpIsUnconditional = false;
+ bool isReturnBlock = false;
+ bool isThrowBlock = false;
+ };
+
+ using BasicBlocks = QFlatMap<int, BasicBlock>;
+
struct InstructionAnnotation
{
// Registers explicit read as part of the instruction.
@@ -67,25 +83,56 @@ public:
};
using InstructionAnnotations = QFlatMap<int, InstructionAnnotation>;
+ struct BlocksAndAnnotations
+ {
+ BasicBlocks basicBlocks;
+ InstructionAnnotations annotations;
+ };
struct Function
{
QQmlJSScopesById addressableScopes;
QList<QQmlJSRegisterContent> argumentTypes;
QList<QQmlJSRegisterContent> registerTypes;
- QQmlJSScope::ConstPtr returnType;
+ QQmlJSRegisterContent returnType;
QQmlJSScope::ConstPtr qmlScope;
QByteArray code;
const SourceLocationTable *sourceLocations = nullptr;
bool isSignalHandler = false;
bool isQPropertyBinding = false;
bool isProperty = false;
+ bool isFullyTyped = false;
+ };
+
+ struct ObjectOrArrayDefinition
+ {
+ enum {
+ ArrayClassId = -1,
+ ArrayConstruct1ArgId = -2,
+ };
+
+ int instructionOffset = -1;
+ int internalClassId = ArrayClassId;
+ int argc = 0;
+ int argv = -1;
};
struct State
{
VirtualRegisters registers;
-
+ VirtualRegisters lookups;
+
+ /*!
+ \internal
+ \brief The accumulatorIn is the input register of the current instruction.
+
+ It holds a content, a type that content is acctually stored in, and an enclosing type
+ of the stored type called the scope. Note that passes after the original type
+ propagation may change the type of this register to a different type that the original
+ one can be coerced to. Therefore, when analyzing the same instruction in a later pass,
+ the type may differ from what was seen or requested ealier. See \l {readAccumulator()}.
+ The input type may then need to be converted to the expected type.
+ */
const QQmlJSRegisterContent &accumulatorIn() const
{
auto it = registers.find(Accumulator);
@@ -93,6 +140,10 @@ public:
return it.value().content;
};
+ /*!
+ \internal
+ \brief The accumulatorOut is the output register of the current instruction.
+ */
const QQmlJSRegisterContent &accumulatorOut() const
{
Q_ASSERT(m_changedRegisterIndex == Accumulator);
@@ -101,6 +152,10 @@ public:
void setRegister(int registerIndex, QQmlJSRegisterContent content)
{
+ const int lookupIndex = content.resultLookupIndex();
+ if (lookupIndex != QQmlJSRegisterContent::InvalidLookupIndex)
+ lookups[lookupIndex] = { content, false, false };
+
m_changedRegister = std::move(content);
m_changedRegisterIndex = registerIndex;
}
@@ -117,7 +172,11 @@ public:
void addReadRegister(int registerIndex, const QQmlJSRegisterContent &reg)
{
Q_ASSERT(isRename() || reg.isConversion());
- m_readRegisters[registerIndex].content = reg;
+ const VirtualRegister &source = registers[registerIndex];
+ VirtualRegister &target = m_readRegisters[registerIndex];
+ target.content = reg;
+ target.canMove = source.canMove;
+ target.affectedBySideEffects = source.affectedBySideEffects;
}
void addReadAccumulator(const QQmlJSRegisterContent &reg)
@@ -143,6 +202,19 @@ public:
return it != m_readRegisters.end() && it->second.canMove;
}
+ bool isRegisterAffectedBySideEffects(int registerIndex) const
+ {
+ auto it = m_readRegisters.find(registerIndex);
+ return it != m_readRegisters.end() && it->second.affectedBySideEffects;
+ }
+
+ /*!
+ \internal
+ \brief The readAccumulator is the register content expected by the current instruction.
+
+ It may differ from the actual input type of the accumulatorIn register and usage of the
+ value may require a conversion.
+ */
QQmlJSRegisterContent readAccumulator() const
{
return readRegister(Accumulator);
@@ -154,11 +226,35 @@ public:
}
bool hasSideEffects() const { return m_hasSideEffects; }
- void setHasSideEffects(bool hasSideEffects) { m_hasSideEffects = hasSideEffects; }
+
+ void markSideEffects(bool hasSideEffects) { m_hasSideEffects = hasSideEffects; }
+ void applySideEffects(bool hasSideEffects)
+ {
+ if (!hasSideEffects)
+ return;
+
+ for (auto it = registers.begin(), end = registers.end(); it != end; ++it)
+ it.value().affectedBySideEffects = true;
+
+ for (auto it = lookups.begin(), end = lookups.end(); it != end; ++it)
+ it.value().affectedBySideEffects = true;
+ }
+
+ void setHasSideEffects(bool hasSideEffects) {
+ markSideEffects(hasSideEffects);
+ applySideEffects(hasSideEffects);
+ }
bool isRename() const { return m_isRename; }
void setIsRename(bool isRename) { m_isRename = isRename; }
+ int renameSourceRegisterIndex() const
+ {
+ Q_ASSERT(m_isRename);
+ Q_ASSERT(m_readRegisters.size() == 1);
+ return m_readRegisters.begin().key();
+ }
+
private:
VirtualRegisters m_readRegisters;
QQmlJSRegisterContent m_changedRegister;
@@ -168,10 +264,13 @@ public:
};
QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
- const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks = {}, InstructionAnnotations annotations = {})
: m_jsUnitGenerator(jsUnitGenerator)
, m_typeResolver(typeResolver)
, m_logger(logger)
+ , m_basicBlocks(basicBlocks)
+ , m_annotations(annotations)
{}
protected:
@@ -180,6 +279,8 @@ protected:
QQmlJSLogger *m_logger = nullptr;
const Function *m_function = nullptr;
+ BasicBlocks m_basicBlocks;
+ InstructionAnnotations m_annotations;
QQmlJS::DiagnosticMessage *m_error = nullptr;
int firstRegisterIndex() const
@@ -218,17 +319,23 @@ protected:
const auto instruction = annotations.find(currentInstructionOffset());
newState.registers = oldState.registers;
+ newState.lookups = oldState.lookups;
// Usually the initial accumulator type is the output of the previous instruction, but ...
if (oldState.changedRegisterIndex() != InvalidRegister) {
+ newState.registers[oldState.changedRegisterIndex()].affectedBySideEffects = false;
newState.registers[oldState.changedRegisterIndex()].content
= oldState.changedRegister();
}
+ // Side effects are applied at the end of an instruction: An instruction with side
+ // effects can still read its registers before the side effects happen.
+ newState.applySideEffects(oldState.hasSideEffects());
+
if (instruction == annotations.constEnd())
return newState;
- newState.setHasSideEffects(instruction->second.hasSideEffects);
+ newState.markSideEffects(instruction->second.hasSideEffects);
newState.setReadRegisters(instruction->second.readRegisters);
newState.setIsRename(instruction->second.isRename);
@@ -277,6 +384,31 @@ protected:
setError(message, currentInstructionOffset());
}
+ static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
+ {
+ using Type = QV4::Moth::Instr::Type;
+ switch (type) {
+ case Type::PopContext:
+ case Type::PopScriptContext:
+ case Type::CreateCallContext:
+ case Type::CreateCallContext_Wide:
+ case Type::PushCatchContext:
+ case Type::PushCatchContext_Wide:
+ case Type::PushWithContext:
+ case Type::PushWithContext_Wide:
+ case Type::PushBlockContext:
+ case Type::PushBlockContext_Wide:
+ case Type::CloneBlockContext:
+ case Type::CloneBlockContext_Wide:
+ case Type::PushScriptContext:
+ case Type::PushScriptContext_Wide:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
// Stub out all the methods so that passes can choose to only implement part of them.
void generate_Add(int) override {}
void generate_As(int) override {}
@@ -337,9 +469,9 @@ protected:
void generate_GetTemplateObject(int) override {}
void generate_Increment() override {}
void generate_InitializeBlockDeadTemporalZone(int, int) override {}
- void generate_IteratorClose(int) override {}
+ void generate_IteratorClose() override {}
void generate_IteratorNext(int, int) override {}
- void generate_IteratorNextForYieldStar(int, int) override {}
+ void generate_IteratorNextForYieldStar(int, int, int) override {}
void generate_Jump(int) override {}
void generate_JumpFalse(int) override {}
void generate_JumpNoException(int) override {}
diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp
index 964c01c1a2..8ecc69d1c9 100644
--- a/src/qmlcompiler/qqmljscompiler.cpp
+++ b/src/qmlcompiler/qqmljscompiler.cpp
@@ -10,6 +10,7 @@
#include <private/qqmljsimportvisitor_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsloadergenerator_p.h>
+#include <private/qqmljsoptimizations_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsshadowcheck_p.h>
#include <private/qqmljsstoragegeneralizer_p.h>
@@ -19,6 +20,8 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qloggingcategory.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+
#include <limits>
QT_BEGIN_NAMESPACE
@@ -123,9 +126,7 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc,
if (binding->type() != QV4::CompiledData::Binding::Type_Script)
continue;
const QString propName = doc.stringAt(binding->propertyNameIndex);
- if (!propName.startsWith(QLatin1String("on"))
- || propName.size() < 3
- || !propName.at(2).isUpper())
+ if (!QQmlSignalNames::isHandlerName(propName))
continue;
auto compiledFunction = doc.jsModule.functions.value(object->runtimeFunctionIndices.at(binding->value.compiledScriptIndex));
if (!compiledFunction)
@@ -363,8 +364,8 @@ bool qCompileQmlFile(QmlIR::Document &irDocument, const QString &inputFileName,
const quint32 saveFlags
= QV4::CompiledData::Unit::StaticData
| QV4::CompiledData::Unit::PendingTypeCompilation;
- QV4::CompiledData::SaveableUnitPointer saveable(irDocument.javaScriptCompilationUnit.data,
- saveFlags);
+ QV4::CompiledData::SaveableUnitPointer saveable(
+ irDocument.javaScriptCompilationUnit->unitData(), saveFlags);
if (!saveFunction(saveable, aotFunctionsByIndex, &error->message))
return false;
}
@@ -373,7 +374,7 @@ bool qCompileQmlFile(QmlIR::Document &irDocument, const QString &inputFileName,
bool qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl, QQmlJSSaveFunction saveFunction, QQmlJSCompileError *error)
{
- QV4::CompiledData::CompilationUnit unit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
QString sourceCode;
{
@@ -397,7 +398,7 @@ bool qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl, Q
unit = QV4::Compiler::Codegen::compileModule(/*debugMode*/false, url, sourceCode,
QDateTime(), &diagnostics);
error->appendDiagnostics(inputFileName, diagnostics);
- if (!unit.unitData())
+ if (!unit || !unit->unitData())
return false;
} else {
QmlIR::Document irDocument(/*debugMode*/false);
@@ -455,32 +456,14 @@ bool qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl, Q
}
QQmlJSAotFunctionMap empty;
- return saveFunction(QV4::CompiledData::SaveableUnitPointer(unit.data), empty, &error->message);
+ return saveFunction(
+ QV4::CompiledData::SaveableUnitPointer(unit->unitData()), empty, &error->message);
}
-static const char *wrapCallCode = R"(
-template <typename Binding>
-void wrapCall(const QQmlPrivate::AOTCompiledContext *aotContext, void *dataPtr, void **argumentsPtr, Binding &&binding)
-{
- using return_type = std::invoke_result_t<Binding, const QQmlPrivate::AOTCompiledContext *, void **>;
- if constexpr (std::is_same_v<return_type, void>) {
- Q_UNUSED(dataPtr)
- binding(aotContext, argumentsPtr);
- } else {
- if (dataPtr) {
- new (dataPtr) return_type(binding(aotContext, argumentsPtr));
- } else {
- binding(aotContext, argumentsPtr);
- }
- }
-}
-)";
-
static const char *funcHeaderCode = R"(
- [](const QQmlPrivate::AOTCompiledContext *aotContext, void *dataPtr, void **argumentsPtr) {
- wrapCall(aotContext, dataPtr, argumentsPtr, [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argumentsPtr) {
+ [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argv) {
Q_UNUSED(aotContext)
-Q_UNUSED(argumentsPtr)
+Q_UNUSED(argv)
)";
bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFileName, const QV4::CompiledData::SaveableUnitPointer &unit, const QQmlJSAotFunctionMap &aotFunctions, QString *errorString)
@@ -576,14 +559,13 @@ bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFile
writeStr(aotFunctions[FileScopeCodeIndex].code.toUtf8().constData());
if (aotFunctions.size() <= 1) {
// FileScopeCodeIndex is always there, but it may be the only one.
- writeStr("extern const QQmlPrivate::TypedFunction aotBuiltFunctions[];\n"
- "extern const QQmlPrivate::TypedFunction aotBuiltFunctions[] = { { 0, QMetaType::fromType<void>(), {}, nullptr } };");
+ writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
+ "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
} else {
- writeStr(wrapCallCode);
- writeStr("extern const QQmlPrivate::TypedFunction aotBuiltFunctions[];\n"
- "extern const QQmlPrivate::TypedFunction aotBuiltFunctions[] = {\n");
+ writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
+ "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
- QString footer = QStringLiteral("});}\n");
+ QString footer = QStringLiteral("}\n");
for (QQmlJSAotFunctionMap::ConstIterator func = aotFunctions.constBegin(),
end = aotFunctions.constEnd();
@@ -592,25 +574,18 @@ bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFile
if (func.key() == FileScopeCodeIndex)
continue;
- QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
+ const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
- QString argumentTypes = func.value().argumentTypes.join(
- QStringLiteral(">(), QMetaType::fromType<"));
- if (!argumentTypes.isEmpty()) {
- argumentTypes = QStringLiteral("QMetaType::fromType<")
- + argumentTypes + QStringLiteral(">()");
- }
-
- writeStr(QStringLiteral("{ %1, QMetaType::fromType<%2>(), { %3 }, %4 },")
+ writeStr(QStringLiteral("{ %1, %2, [](QV4::ExecutableCompilationUnit *unit, "
+ "QMetaType *argTypes) {\n%3}, %4 },")
.arg(func.key())
- .arg(func.value().returnType)
- .arg(argumentTypes)
- .arg(function)
+ .arg(func->numArguments)
+ .arg(func->signature, function)
.toUtf8().constData());
}
// Conclude the list with a nullptr
- writeStr("{ 0, QMetaType::fromType<void>(), {}, nullptr }");
+ writeStr("{ 0, 0, nullptr, nullptr }");
writeStr("};\n");
}
@@ -650,7 +625,6 @@ void QQmlJSAotCompiler::setDocument(
m_logger->setFileName(resourcePathInfo.fileName());
m_logger->setCode(irDocument->code);
m_unitGenerator = &irDocument->jsGenerator;
- m_entireSourceCodeLines = irDocument->code.split(u'\n');
QQmlJSScope::Ptr target = QQmlJSScope::create();
QQmlJSImportVisitor visitor(target, m_importer, m_logger,
resourcePathInfo.canonicalPath() + u'/',
@@ -764,7 +738,6 @@ QQmlJSAotFunction QQmlJSAotCompiler::globalCode() const
return global;
}
-
QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
const QV4::Compiler::Context *context, QQmlJSCompilePass::Function *function,
QQmlJS::DiagnosticMessage *error)
@@ -775,30 +748,38 @@ QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
return QQmlJSAotFunction();
};
- QQmlJSTypePropagator propagator(m_unitGenerator, &m_typeResolver, m_logger);
- auto typePropagationResult = propagator.run(function, error);
if (error->isValid())
return compileError();
- QQmlJSBasicBlocks basicBlocks(m_unitGenerator, &m_typeResolver, m_logger);
- typePropagationResult = basicBlocks.run(function, typePropagationResult);
+ bool basicBlocksValidationFailed = false;
+ QQmlJSBasicBlocks basicBlocks(context, m_unitGenerator, &m_typeResolver, m_logger);
+ auto passResult = basicBlocks.run(function, m_flags, basicBlocksValidationFailed);
+ auto &[blocks, annotations] = passResult;
+
+ QQmlJSTypePropagator propagator(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
+ passResult = propagator.run(function, error);
+ if (error->isValid())
+ return compileError();
+
+ QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
+ passResult = shadowCheck.run(function, error);
+ if (error->isValid())
+ return compileError();
- QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger);
- shadowCheck.run(&typePropagationResult, function, error);
+ QQmlJSOptimizations optimizer(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations,
+ basicBlocks.objectAndArrayDefinitions());
+ passResult = optimizer.run(function, error);
if (error->isValid())
return compileError();
// Generalize all arguments, registers, and the return type.
- QQmlJSStorageGeneralizer generalizer(
- m_unitGenerator, &m_typeResolver, m_logger);
- typePropagationResult = generalizer.run(typePropagationResult, function, error);
+ QQmlJSStorageGeneralizer generalizer(m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
+ passResult = generalizer.run(function, error);
if (error->isValid())
return compileError();
- QQmlJSCodeGenerator codegen(
- context, m_unitGenerator, &m_typeResolver, m_logger,
- m_entireSourceCodeLines);
- QQmlJSAotFunction result = codegen.run(function, &typePropagationResult, error);
+ QQmlJSCodeGenerator codegen(context, m_unitGenerator, &m_typeResolver, m_logger, blocks, annotations);
+ QQmlJSAotFunction result = codegen.run(function, error, basicBlocksValidationFailed);
return error->isValid() ? compileError() : result;
}
diff --git a/src/qmlcompiler/qqmljscompiler_p.h b/src/qmlcompiler/qqmljscompiler_p.h
index 3483226a09..e358f76fef 100644
--- a/src/qmlcompiler/qqmljscompiler_p.h
+++ b/src/qmlcompiler/qqmljscompiler_p.h
@@ -14,7 +14,7 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
@@ -32,9 +32,9 @@
QT_BEGIN_NAMESPACE
-Q_QMLCOMPILER_PRIVATE_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcAotCompiler);
+Q_QMLCOMPILER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcAotCompiler);
-struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSCompileError
+struct Q_QMLCOMPILER_EXPORT QQmlJSCompileError
{
QString message;
void print();
@@ -45,17 +45,23 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSCompileError
const QQmlJS::DiagnosticMessage &diagnostic);
};
-struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSAotFunction
+struct Q_QMLCOMPILER_EXPORT QQmlJSAotFunction
{
QStringList includes;
- QStringList argumentTypes;
QString code;
- QString returnType;
+ QString signature;
+ int numArguments = 0;
};
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSAotCompiler
+class Q_QMLCOMPILER_EXPORT QQmlJSAotCompiler
{
public:
+ enum Flag {
+ NoFlags = 0x0,
+ ValidateBasicBlocks = 0x1,
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
QQmlJSAotCompiler(QQmlJSImporter *importer, const QString &resourcePath,
const QStringList &qmldirFiles, QQmlJSLogger *logger);
@@ -71,12 +77,13 @@ public:
virtual QQmlJSAotFunction globalCode() const;
+ Flags m_flags;
+
protected:
virtual QQmlJS::DiagnosticMessage diagnose(
const QString &message, QtMsgType type, const QQmlJS::SourceLocation &location) const;
QQmlJSTypeResolver m_typeResolver;
- QStringList m_entireSourceCodeLines;
const QString m_resourcePath;
const QStringList m_qmldirFiles;
@@ -95,31 +102,32 @@ private:
QQmlJS::DiagnosticMessage *error);
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlJSAotCompiler::Flags);
using QQmlJSAotFunctionMap = QMap<int, QQmlJSAotFunction>;
using QQmlJSSaveFunction
= std::function<bool(const QV4::CompiledData::SaveableUnitPointer &,
const QQmlJSAotFunctionMap &, QString *)>;
-bool Q_QMLCOMPILER_PRIVATE_EXPORT qCompileQmlFile(const QString &inputFileName,
+bool Q_QMLCOMPILER_EXPORT qCompileQmlFile(const QString &inputFileName,
QQmlJSSaveFunction saveFunction,
QQmlJSAotCompiler *aotCompiler, QQmlJSCompileError *error,
bool storeSourceLocation = false,
QV4::Compiler::CodegenWarningInterface *interface =
QV4::Compiler::defaultCodegenWarningInterface(),
const QString *fileContents = nullptr);
-bool Q_QMLCOMPILER_PRIVATE_EXPORT qCompileQmlFile(QmlIR::Document &irDocument, const QString &inputFileName,
+bool Q_QMLCOMPILER_EXPORT qCompileQmlFile(QmlIR::Document &irDocument, const QString &inputFileName,
QQmlJSSaveFunction saveFunction,
QQmlJSAotCompiler *aotCompiler, QQmlJSCompileError *error,
bool storeSourceLocation = false,
QV4::Compiler::CodegenWarningInterface *interface =
QV4::Compiler::defaultCodegenWarningInterface(),
const QString *fileContents = nullptr);
-bool Q_QMLCOMPILER_PRIVATE_EXPORT qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl,
+bool Q_QMLCOMPILER_EXPORT qCompileJSFile(const QString &inputFileName, const QString &inputFileUrl,
QQmlJSSaveFunction saveFunction,
QQmlJSCompileError *error);
-bool Q_QMLCOMPILER_PRIVATE_EXPORT qSaveQmlJSUnitAsCpp(const QString &inputFileName,
+bool Q_QMLCOMPILER_EXPORT qSaveQmlJSUnitAsCpp(const QString &inputFileName,
const QString &outputFileName,
const QV4::CompiledData::SaveableUnitPointer &unit,
const QQmlJSAotFunctionMap &aotFunctions,
diff --git a/src/qmlcompiler/qqmljscontextualtypes_p.h b/src/qmlcompiler/qqmljscontextualtypes_p.h
new file mode 100644
index 0000000000..cccd7fd1b0
--- /dev/null
+++ b/src/qmlcompiler/qqmljscontextualtypes_p.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLJSCONTEXTUALTYPES_P_H
+#define QQMLJSCONTEXTUALTYPES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include <QtCore/qstring.h>
+#include <QtCore/qhash.h>
+#include <private/qqmljsscope_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+/*! \internal
+ * Maps type names to types and the compile context of the types. The context can be
+ * INTERNAL (for c++ and synthetic jsrootgen types) or QML (for qml types).
+ */
+struct ContextualTypes
+{
+ enum CompileContext { INTERNAL, QML };
+
+ ContextualTypes(
+ CompileContext context,
+ const QHash<QString, ImportedScope<QQmlJSScope::ConstPtr>> types,
+ const QQmlJSScope::ConstPtr &arrayType)
+ : m_types(types)
+ , m_context(context)
+ , m_arrayType(arrayType)
+ {}
+
+ CompileContext context() const { return m_context; }
+ QQmlJSScope::ConstPtr arrayType() const { return m_arrayType; }
+
+ bool hasType(const QString &name) const { return m_types.contains(name); }
+
+ ImportedScope<QQmlJSScope::ConstPtr> type(const QString &name) const { return m_types[name]; }
+ QString name(const QQmlJSScope::ConstPtr &type) const { return m_names[type]; }
+
+ void setType(const QString &name, const ImportedScope<QQmlJSScope::ConstPtr> &type)
+ {
+ if (!name.startsWith(u'$'))
+ m_names.insert(type.scope, name);
+ m_types.insert(name, type);
+ }
+ void clearType(const QString &name)
+ {
+ auto &scope = m_types[name].scope;
+ auto it = m_names.constFind(scope);
+ while (it != m_names.constEnd() && it.key() == scope)
+ it = m_names.erase(it);
+ scope = QQmlJSScope::ConstPtr();
+ }
+
+ bool isNullType(const QString &name) const
+ {
+ const auto it = m_types.constFind(name);
+ return it != m_types.constEnd() && it->scope.isNull();
+ }
+
+ void addTypes(ContextualTypes &&types)
+ {
+ Q_ASSERT(types.m_context == m_context);
+ insertNames(types);
+ m_types.insert(std::move(types.m_types));
+ }
+
+ void addTypes(const ContextualTypes &types)
+ {
+ Q_ASSERT(types.m_context == m_context);
+ insertNames(types);
+ m_types.insert(types.m_types);
+ }
+
+ const QHash<QString, ImportedScope<QQmlJSScope::ConstPtr>> &types() const { return m_types; }
+
+ void clearTypes()
+ {
+ m_names.clear();
+ m_types.clear();
+ }
+
+private:
+ void insertNames(const ContextualTypes &types) {
+ for (auto it = types.m_types.constBegin(), end = types.m_types.constEnd();
+ it != end; ++it) {
+ const QString &name = it.key();
+ if (!name.startsWith(u'$'))
+ m_names.insert(it->scope, name);
+ }
+ }
+
+ QHash<QString, ImportedScope<QQmlJSScope::ConstPtr>> m_types;
+ QMultiHash<QQmlJSScope::ConstPtr, QString> m_names;
+ CompileContext m_context;
+
+ // For resolving sequence types
+ QQmlJSScope::ConstPtr m_arrayType;
+};
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSCONTEXTUALTYPES_P_H
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
index 997771489b..09928364b1 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp
+++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
@@ -8,6 +8,8 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qfileinfo.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -59,6 +61,7 @@ void QQmlJSFunctionInitializer::populateSignature(
error->type = QtWarningMsg;
error->loc = ast->firstSourceLocation();
error->message = message;
+ function->isFullyTyped = false;
};
if (!m_typeResolver->canCallJSFunctions()) {
@@ -71,6 +74,11 @@ void QQmlJSFunctionInitializer::populateSignature(
if (ast->formals)
arguments = ast->formals->formals();
+ // If the function has no arguments and no return type annotation we assume it's untyped.
+ // You can annotate it to return void to make it typed.
+ // Otherwise we first assume it's typed and reset the flag if we detect a problem.
+ function->isFullyTyped = !arguments.isEmpty() || ast->typeAnnotation;
+
if (function->argumentTypes.isEmpty()) {
for (const QQmlJS::AST::BoundName &argument : std::as_const(arguments)) {
if (argument.typeAnnotation) {
@@ -108,10 +116,11 @@ void QQmlJSFunctionInitializer::populateSignature(
}
}
- if (!function->returnType) {
+ if (!function->returnType.isValid()) {
if (ast->typeAnnotation) {
- function->returnType = m_typeResolver->typeFromAST(ast->typeAnnotation->type);
- if (!function->returnType)
+ function->returnType = m_typeResolver->globalType(
+ m_typeResolver->typeFromAST(ast->typeAnnotation->type));
+ if (!function->returnType.isValid())
signatureError(u"Cannot resolve return type %1"_s.arg(
QmlIR::IRBuilder::asString(ast->typeAnnotation->type->typeId)));
}
@@ -160,44 +169,45 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run(
}
function.isProperty = m_objectType->hasProperty(propertyName);
- if (!function.isProperty && QmlIR::IRBuilder::isSignalPropertyName(propertyName)) {
- const QString signalName = QmlIR::IRBuilder::signalNameFromSignalPropertyName(propertyName);
-
- if (signalName.endsWith(u"Changed"_s)
- && m_objectType->hasProperty(signalName.chopped(strlen("Changed")))) {
- function.isSignalHandler = true;
- } else {
- const auto methods = m_objectType->methods(signalName);
- for (const auto &method : methods) {
- if (method.isCloned())
- continue;
- if (method.methodType() == QQmlJSMetaMethod::Signal) {
- function.isSignalHandler = true;
- const auto arguments = method.parameters();
- for (qsizetype i = 0, end = arguments.size(); i < end; ++i) {
- const auto &type = arguments[i].type();
- if (type.isNull()) {
- diagnose(u"Cannot resolve the argument type %1."_s.arg(
- arguments[i].typeName()),
- QtDebugMsg, bindingLocation, error);
- function.argumentTypes.append(
+ if (!function.isProperty) {
+ if (QQmlSignalNames::isHandlerName(propertyName)) {
+ if (auto actualPropertyName =
+ QQmlSignalNames::changedHandlerNameToPropertyName(propertyName);
+ actualPropertyName && m_objectType->hasProperty(*actualPropertyName)) {
+ function.isSignalHandler = true;
+ } else {
+ auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
+ const auto methods = m_objectType->methods(*signalName);
+ for (const auto &method : methods) {
+ if (method.isCloned())
+ continue;
+ if (method.methodType() == QQmlJSMetaMethodType::Signal) {
+ function.isSignalHandler = true;
+ const auto arguments = method.parameters();
+ for (qsizetype i = 0, end = arguments.size(); i < end; ++i) {
+ const auto &type = arguments[i].type();
+ if (type.isNull()) {
+ diagnose(u"Cannot resolve the argument type %1."_s.arg(
+ arguments[i].typeName()),
+ QtDebugMsg, bindingLocation, error);
+ function.argumentTypes.append(
m_typeResolver->tracked(
- m_typeResolver->globalType(m_typeResolver->varType())));
- } else {
- function.argumentTypes.append(m_typeResolver->tracked(
- m_typeResolver->globalType(type)));
+ m_typeResolver->globalType(m_typeResolver->varType())));
+ } else {
+ function.argumentTypes.append(m_typeResolver->tracked(
+ m_typeResolver->globalType(type)));
+ }
}
+ break;
}
- break;
+ }
+ if (!function.isSignalHandler) {
+ diagnose(u"Could not compile signal handler for %1: The signal does not exist"_s.arg(
+ *signalName),
+ QtWarningMsg, bindingLocation, error);
}
}
}
-
- if (!function.isSignalHandler) {
- diagnose(u"Could not compile signal handler for %1: The signal does not exist"_s.arg(
- signalName),
- QtWarningMsg, bindingLocation, error);
- }
}
if (!function.isSignalHandler) {
@@ -209,13 +219,19 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run(
const auto property = m_objectType->property(propertyName);
if (const QQmlJSScope::ConstPtr propertyType = property.type()) {
- function.returnType = propertyType->isListProperty()
- ? m_typeResolver->qObjectListType()
- : propertyType;
+ function.returnType = m_typeResolver->globalType(propertyType->isListProperty()
+ ? m_typeResolver->qObjectListType()
+ : QQmlJSScope::ConstPtr(property.type()));
} else {
- diagnose(u"Cannot resolve property type %1 for binding on %2"_s.arg(
- property.typeName(), propertyName),
- QtWarningMsg, bindingLocation, error);
+ QString message = u"Cannot resolve property type %1 for binding on %2."_s
+ .arg(property.typeName(), propertyName);
+ if (m_objectType->isNameDeferred(propertyName)) {
+ // If the property doesn't exist but the name is deferred, then
+ // it's deferred via the presence of immediate names. Those are
+ // most commonly used to enable generalized grouped properties.
+ message += u" You may want use ID-based grouped properties here.";
+ }
+ diagnose(message, QtWarningMsg, bindingLocation, error);
}
if (!property.bindable().isEmpty() && !property.isPrivate())
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer_p.h b/src/qmlcompiler/qqmljsfunctioninitializer_p.h
index 9f191a4af8..424959a4fa 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer_p.h
+++ b/src/qmlcompiler/qqmljsfunctioninitializer_p.h
@@ -18,7 +18,7 @@
QT_BEGIN_NAMESPACE
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSFunctionInitializer
+class Q_QMLCOMPILER_EXPORT QQmlJSFunctionInitializer
{
Q_DISABLE_COPY_MOVE(QQmlJSFunctionInitializer)
public:
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp
index 2b42941039..bcc8e98434 100644
--- a/src/qmlcompiler/qqmljsimporter.cpp
+++ b/src/qmlcompiler/qqmljsimporter.cpp
@@ -16,8 +16,24 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-static const QLatin1String SlashQmldir = QLatin1String("/qmldir");
-static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes");
+static const QLatin1String SlashQmldir = QLatin1String("/qmldir");
+static const QLatin1String PluginsDotQmltypes = QLatin1String("plugins.qmltypes");
+
+
+QQmlJS::Import::Import(QString prefix, QString name, QTypeRevision version, bool isFile,
+ bool isDependency) :
+ m_prefix(std::move(prefix)),
+ m_name(std::move(name)),
+ m_version(version),
+ m_isFile(isFile),
+ m_isDependency(isDependency)
+{
+}
+
+bool QQmlJS::Import::isValid() const
+{
+ return !m_name.isEmpty();
+}
static const QString prefixedName(const QString &prefix, const QString &name)
{
@@ -25,12 +41,21 @@ static const QString prefixedName(const QString &prefix, const QString &name)
return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name);
}
-static QQmlDirParser createQmldirParserForFile(const QString &filename)
+QQmlDirParser QQmlJSImporter::createQmldirParserForFile(const QString &filename)
{
QFile f(filename);
- f.open(QFile::ReadOnly);
QQmlDirParser parser;
- parser.parse(QString::fromUtf8(f.readAll()));
+ if (f.open(QFile::ReadOnly)) {
+ parser.parse(QString::fromUtf8(f.readAll()));
+ } else {
+ m_warnings.append({
+ QStringLiteral("Could not open qmldir file: ")
+ + filename,
+ QtWarningMsg,
+ QQmlJS::SourceLocation()
+ });
+ }
+
return parser;
}
@@ -129,24 +154,79 @@ static bool isComposite(const QQmlJSScope::ConstPtr &scope)
return scope.factory() || scope->isComposite();
}
+static QStringList aliases(const QQmlJSScope::ConstPtr &scope)
+{
+ return isComposite(scope)
+ ? QStringList()
+ : scope->aliases();
+}
+
QQmlJSImporter::QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper,
bool useOptionalImports)
: m_importPaths(importPaths),
m_mapper(mapper),
m_useOptionalImports(useOptionalImports),
- m_createImportVisitor([](const QQmlJSScope::Ptr &target, QQmlJSImporter *importer,
- QQmlJSLogger *logger, const QString &implicitImportDirectory,
- const QStringList &qmldirFiles) {
- return new QQmlJSImportVisitor(target, importer, logger, implicitImportDirectory,
- qmldirFiles);
+ m_importVisitor([](QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
+ const ImportVisitorPrerequisites &p) {
+ auto visitor = std::unique_ptr<QQmlJS::AST::BaseVisitor>(new QQmlJSImportVisitor(
+ p.m_target, self, p.m_logger, p.m_implicitImportDirectory, p.m_qmldirFiles));
+ QQmlJS::AST::Node::accept(rootNode, visitor.get());
})
{
}
-QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path)
+static QString resolvePreferredPath(
+ const QString &qmldirPath, const QString &prefer, QQmlJSResourceFileMapper *mapper)
{
+ if (prefer.isEmpty())
+ return qmldirPath;
+
+ if (!prefer.endsWith(u'/')) {
+ qWarning() << "Ignoring invalid prefer path" << prefer << "(has to end with slash)";
+ return qmldirPath;
+ }
+
+ if (prefer.startsWith(u':')) {
+ // Resource path: Resolve via resource file mapper if possible.
+ if (!mapper)
+ return qmldirPath;
+
+ Q_ASSERT(prefer.endsWith(u'/'));
+ const auto entry = mapper->entry(
+ QQmlJSResourceFileMapper::resourceFileFilter(prefer.mid(1) + SlashQmldir.mid(1)));
+
+ // This can be empty if the .qrc files does not belong to this module.
+ // In that case we trust the given qmldir file.
+ return entry.filePath.endsWith(SlashQmldir)
+ ? entry.filePath
+ : qmldirPath;
+ }
+
+ // Host file system path. This should be rare. We don't generate it.
+ const QFileInfo f(prefer + SlashQmldir);
+ const QString canonical = f.canonicalFilePath();
+ if (canonical.isEmpty()) {
+ qWarning() << "No qmldir at" << prefer;
+ return qmldirPath;
+ }
+ return canonical;
+}
+
+QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &modulePath)
+{
+ const QString moduleQmldirPath = modulePath + SlashQmldir;
+ auto reader = createQmldirParserForFile(moduleQmldirPath);
+
+ const QString resolvedQmldirPath
+ = resolvePreferredPath(moduleQmldirPath, reader.preferredPath(), m_mapper);
+ if (resolvedQmldirPath != moduleQmldirPath)
+ reader = createQmldirParserForFile(resolvedQmldirPath);
+
+ // Leave the trailing slash
+ Q_ASSERT(resolvedQmldirPath.endsWith(SlashQmldir));
+ QStringView resolvedPath = QStringView(resolvedQmldirPath).chopped(SlashQmldir.size() - 1);
+
Import result;
- auto reader = createQmldirParserForFile(path + SlashQmldir);
result.name = reader.typeNamespace();
result.isStaticModule = reader.isStaticModule();
@@ -157,12 +237,13 @@ QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path)
const auto typeInfos = reader.typeInfos();
for (const auto &typeInfo : typeInfos) {
const QString typeInfoPath = QFileInfo(typeInfo).isRelative()
- ? path + u'/' + typeInfo : typeInfo;
+ ? resolvedPath + typeInfo
+ : typeInfo;
readQmltypes(typeInfoPath, &result.objects, &result.dependencies);
}
if (typeInfos.isEmpty() && !reader.plugins().isEmpty()) {
- const QString defaultTypeInfoPath = path + SlashPluginsDotQmltypes;
+ const QString defaultTypeInfoPath = resolvedPath + PluginsDotQmltypes;
if (QFile::exists(defaultTypeInfoPath)) {
m_warnings.append({
QStringLiteral("typeinfo not declared in qmldir file: ")
@@ -177,11 +258,11 @@ QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path)
QHash<QString, QQmlJSExportedScope> qmlComponents;
const auto components = reader.components();
for (auto it = components.begin(), end = components.end(); it != end; ++it) {
- const QString filePath = path + QLatin1Char('/') + it->fileName;
+ const QString filePath = resolvedPath + it->fileName;
if (!QFile::exists(filePath)) {
m_warnings.append({
it->fileName + QStringLiteral(" is listed as component in ")
- + path + SlashQmldir
+ + resolvedQmldirPath
+ QStringLiteral(" but does not exist.\n"),
QtWarningMsg,
QQmlJS::SourceLocation()
@@ -208,7 +289,7 @@ QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path)
const auto scripts = reader.scripts();
for (const auto &script : scripts) {
- const QString filePath = path + QLatin1Char('/') + script.fileName;
+ const QString filePath = resolvedPath + script.fileName;
auto mo = result.scripts.find(script.fileName);
if (mo == result.scripts.end())
mo = result.scripts.insert(script.fileName, { localFile2ScopeTree(filePath), {} });
@@ -308,7 +389,7 @@ void QQmlJSImporter::importDependencies(const QQmlJSImporter::Import &import,
}
static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry,
- const QQmlJSScope::Import &importDescription)
+ const QQmlJS::Import &importDescription)
{
const QTypeRevision importVersion = importDescription.version();
const QTypeRevision exportVersion = exportEntry.version();
@@ -320,7 +401,7 @@ static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry,
|| exportVersion.minorVersion() <= importVersion.minorVersion();
}
-void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription,
+void QQmlJSImporter::processImport(const QQmlJS::Import &importDescription,
const QQmlJSImporter::Import &import,
QQmlJSImporter::AvailableTypes *types)
{
@@ -333,6 +414,12 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription,
const QString modulePrefix = QStringLiteral("$module$");
QHash<QString, QList<QQmlJSScope::Export>> seenExports;
+ const auto insertAliases = [&](const QQmlJSScope::ConstPtr &scope, QTypeRevision revision) {
+ const QStringList cppAliases = aliases(scope);
+ for (const QString &alias : cppAliases)
+ types->cppNames.setType(alias, { scope, revision });
+ };
+
const auto insertExports = [&](const QQmlJSExportedScope &val, const QString &cppName) {
QQmlJSScope::Export bestExport;
@@ -405,6 +492,8 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription,
: QTypeRevision::zero();
types->cppNames.setType(cppName, { val.scope, bestRevision });
+ insertAliases(val.scope, bestRevision);
+
const QTypeRevision bestVersion = bestExport.isValid()
? bestExport.version()
: QTypeRevision::zero();
@@ -444,6 +533,7 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription,
types->qmlNames.setType(
prefixedName(internalPrefix, cppName), { val.scope, QTypeRevision() });
types->cppNames.setType(cppName, { val.scope, QTypeRevision() });
+ insertAliases(val.scope, QTypeRevision());
} else {
insertExports(val, cppName);
}
@@ -478,11 +568,10 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription,
// only happen when enumerations are involved, thus the strategy is to
// resolve enumerations (which can potentially create new child scopes)
// before resolving the type fully
- const QQmlJSScope::ConstPtr intType = tempTypes.cppNames.type(u"int"_s).scope;
const QQmlJSScope::ConstPtr arrayType = tempTypes.cppNames.type(u"Array"_s).scope;
for (auto it = import.objects.begin(); it != import.objects.end(); ++it) {
if (!it->scope.factory()) {
- QQmlJSScope::resolveEnums(it->scope, intType);
+ QQmlJSScope::resolveEnums(it->scope, tempTypes.cppNames);
QQmlJSScope::resolveList(it->scope, arrayType);
}
}
@@ -522,7 +611,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
if (m_builtins)
return *m_builtins;
- AvailableTypes builtins(ImportedTypes(ImportedTypes::INTERNAL, {}, {}, {}));
+ AvailableTypes builtins(ImportedTypes(ImportedTypes::INTERNAL, {}, {}));
Import result;
result.name = QStringLiteral("QML");
@@ -531,7 +620,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
QStringLiteral("jsroot.qmltypes") };
const auto importBuiltins = [&](const QStringList &imports) {
for (auto const &dir : imports) {
- QDirIterator it { dir, qmltypesFiles, QDir::NoFilter, QDirIterator::Subdirectories };
+ QDirIterator it { dir, qmltypesFiles, QDir::NoFilter };
while (it.hasNext() && !qmltypesFiles.isEmpty()) {
readQmltypes(it.next(), &result.objects, &result.dependencies);
qmltypesFiles.removeOne(it.fileName());
@@ -558,7 +647,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
// Process them together since there they have interdependencies that wouldn't get resolved
// otherwise
- const QQmlJSScope::Import builtinImport(
+ const QQmlJS::Import builtinImport(
QString(), QStringLiteral("QML"), QTypeRevision::fromVersion(1, 0), false, true);
QQmlJSScope::ConstPtr intType;
@@ -579,12 +668,10 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
Q_ASSERT(intType);
Q_ASSERT(arrayType);
- m_builtins = AvailableTypes(ImportedTypes(
- ImportedTypes::INTERNAL, builtins.cppNames.types(),
- intType, arrayType));
- m_builtins->qmlNames =
- ImportedTypes(ImportedTypes::QML, builtins.qmlNames.types(),
- intType, arrayType);
+ m_builtins = AvailableTypes(
+ ImportedTypes(ImportedTypes::INTERNAL, builtins.cppNames.types(), arrayType));
+ m_builtins->qmlNames
+ = ImportedTypes(ImportedTypes::QML, builtins.qmlNames.types(), arrayType);
processImport(builtinImport, result, &(*m_builtins));
@@ -601,6 +688,7 @@ void QQmlJSImporter::importQmldirs(const QStringList &qmldirFiles)
QString qmldirName;
if (file.endsWith(SlashQmldir)) {
result = readQmldir(file.chopped(SlashQmldir.size()));
+ setQualifiedNamesOn(result);
qmldirName = file;
} else {
m_warnings.append({
@@ -674,7 +762,7 @@ bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
if (isDependency)
Q_ASSERT(prefix.isEmpty());
- const QQmlJSScope::Import cacheKey(prefix, moduleCacheName, version, isFile, isDependency);
+ const QQmlJS::Import cacheKey(prefix, moduleCacheName, version, isFile, isDependency);
auto getTypesFromCache = [&]() -> bool {
if (!m_cachedImportTypes.contains(cacheKey))
@@ -703,9 +791,7 @@ bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
auto cacheTypes = QSharedPointer<QQmlJSImporter::AvailableTypes>(
new QQmlJSImporter::AvailableTypes(
- ImportedTypes(
- ImportedTypes::INTERNAL, {},
- types->cppNames.intType(), types->cppNames.arrayType())));
+ ImportedTypes(ImportedTypes::INTERNAL, {}, types->cppNames.arrayType())));
m_cachedImportTypes[cacheKey] = cacheTypes;
const QPair<QString, QTypeRevision> importId { module, version };
@@ -822,8 +908,7 @@ QQmlJSImporter::ImportedTypes QQmlJSImporter::importDirectory(
const AvailableTypes builtins = builtinImportHelper();
QQmlJSImporter::AvailableTypes types(
ImportedTypes(
- ImportedTypes::INTERNAL, {},
- builtins.cppNames.intType(), builtins.cppNames.arrayType()));
+ ImportedTypes::INTERNAL, {}, builtins.cppNames.arrayType()));
importHelper(directory, &types, prefix, QTypeRevision(), false, true);
return types.qmlNames;
}
@@ -857,27 +942,18 @@ void QQmlJSImporter::setQualifiedNamesOn(const Import &import)
for (auto &object : import.objects) {
if (object.exports.isEmpty())
continue;
- const QString qualifiedName = QQmlJSScope::qualifiedNameFrom(
- import.name, object.exports.first().type(),
- object.exports.first().revision(),
- object.exports.last().revision());
if (auto *factory = object.scope.factory()) {
- factory->setQualifiedName(qualifiedName);
factory->setModuleName(import.name);
} else {
- object.scope->setQualifiedName(qualifiedName);
- object.scope->setModuleName(import.name);
+ object.scope->setOwnModuleName(import.name);
}
}
}
-std::unique_ptr<QQmlJSImportVisitor>
-QQmlJSImporter::makeImportVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer,
- QQmlJSLogger *logger, const QString &implicitImportDirectory,
- const QStringList &qmldirFiles)
+void QQmlJSImporter::runImportVisitor(QQmlJS::AST::Node *rootNode,
+ const ImportVisitorPrerequisites &p)
{
- return std::unique_ptr<QQmlJSImportVisitor>(
- m_createImportVisitor(target, importer, logger, implicitImportDirectory, qmldirFiles));
+ m_importVisitor(rootNode, this, p);
}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsimporter_p.h b/src/qmlcompiler/qqmljsimporter_p.h
index 792e26a291..290ea71605 100644
--- a/src/qmlcompiler/qqmljsimporter_p.h
+++ b/src/qmlcompiler/qqmljsimporter_p.h
@@ -14,22 +14,60 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
+#include "qqmljscontextualtypes_p.h"
#include "qqmljsscope_p.h"
#include "qqmljsresourcefilemapper_p.h"
#include <QtQml/private/qqmldirparser_p.h>
+#include <QtQml/private/qqmljsast_p.h>
#include <memory>
QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+class Import
+{
+public:
+ Import() = default;
+ Import(QString prefix, QString name, QTypeRevision version, bool isFile, bool isDependency);
+
+ bool isValid() const;
+
+ QString prefix() const { return m_prefix; }
+ QString name() const { return m_name; }
+ QTypeRevision version() const { return m_version; }
+ bool isFile() const { return m_isFile; }
+ bool isDependency() const { return m_isDependency; }
+
+private:
+ QString m_prefix;
+ QString m_name;
+ QTypeRevision m_version;
+ bool m_isFile = false;
+ bool m_isDependency = false;
+
+ friend inline size_t qHash(const Import &key, size_t seed = 0) noexcept
+ {
+ return qHashMulti(seed, key.m_prefix, key.m_name, key.m_version,
+ key.m_isFile, key.m_isDependency);
+ }
+
+ friend inline bool operator==(const Import &a, const Import &b)
+ {
+ return a.m_prefix == b.m_prefix && a.m_name == b.m_name && a.m_version == b.m_version
+ && a.m_isFile == b.m_isFile && a.m_isDependency == b.m_isDependency;
+ }
+};
+}
+
class QQmlJSImportVisitor;
class QQmlJSLogger;
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSImporter
+class Q_QMLCOMPILER_EXPORT QQmlJSImporter
{
public:
- using ImportedTypes = QQmlJSScope::ContextualTypes;
+ using ImportedTypes = QQmlJS::ContextualTypes;
QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper,
bool useOptionalImports = false);
@@ -76,14 +114,38 @@ public:
QQmlJSScope::ConstPtr jsGlobalObject() const;
- std::unique_ptr<QQmlJSImportVisitor>
- makeImportVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer,
- QQmlJSLogger *logger, const QString &implicitImportDirectory,
- const QStringList &qmldirFiles = QStringList());
- using ImportVisitorCreator = QQmlJSImportVisitor *(*)(const QQmlJSScope::Ptr &,
- QQmlJSImporter *, QQmlJSLogger *,
- const QString &, const QStringList &);
- void setImportVisitorCreator(ImportVisitorCreator create) { m_createImportVisitor = create; }
+ struct ImportVisitorPrerequisites
+ {
+ ImportVisitorPrerequisites(QQmlJSScope::Ptr target, QQmlJSLogger *logger,
+ const QString &implicitImportDirectory = {},
+ const QStringList &qmldirFiles = {})
+ : m_target(target),
+ m_logger(logger),
+ m_implicitImportDirectory(implicitImportDirectory),
+ m_qmldirFiles(qmldirFiles)
+ {
+ Q_ASSERT(target && logger);
+ }
+
+ QQmlJSScope::Ptr m_target;
+ QQmlJSLogger *m_logger;
+ QString m_implicitImportDirectory;
+ QStringList m_qmldirFiles;
+ };
+ void runImportVisitor(QQmlJS::AST::Node *rootNode,
+ const ImportVisitorPrerequisites &prerequisites);
+
+ /*!
+ \internal
+ When a qml file gets lazily loaded, it will be lexed and parsed and finally be constructed
+ via an ImportVisitor. By default, this is done via the QQmlJSImportVisitor, but can also be done
+ via other import visitors like QmltcVisitor, which is used by qmltc to compile a QML file, or
+ QQmlDomAstCreatorWithQQmlJSScope, which is used to construct the Dom of lazily loaded QML files.
+ */
+ using ImportVisitor = std::function<void(QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
+ const ImportVisitorPrerequisites &prerequisites)>;
+
+ void setImportVisitor(ImportVisitor visitor) { m_importVisitor = visitor; }
private:
friend class QDeferredFactory<QQmlJSScope>;
@@ -92,8 +154,7 @@ private:
{
AvailableTypes(ImportedTypes builtins)
: cppNames(std::move(builtins))
- , qmlNames(QQmlJSScope::ContextualTypes::QML, {},
- cppNames.intType(), cppNames.arrayType())
+ , qmlNames(QQmlJS::ContextualTypes::QML, {}, cppNames.arrayType())
{
}
@@ -125,11 +186,12 @@ private:
bool importHelper(const QString &module, AvailableTypes *types,
const QString &prefix = QString(), QTypeRevision version = QTypeRevision(),
bool isDependency = false, bool isFile = false);
- void processImport(const QQmlJSScope::Import &importDescription, const Import &import,
+ void processImport(const QQmlJS::Import &importDescription, const Import &import,
AvailableTypes *types);
void importDependencies(const QQmlJSImporter::Import &import, AvailableTypes *types,
const QString &prefix = QString(),
QTypeRevision version = QTypeRevision(), bool isDependency = false);
+ QQmlDirParser createQmldirParserForFile(const QString &filename);
void readQmltypes(const QString &filename, QList<QQmlJSExportedScope> *objects,
QList<QQmlDirParser::Import> *dependencies);
Import readQmldir(const QString &dirname);
@@ -141,7 +203,7 @@ private:
QStringList m_importPaths;
QHash<QPair<QString, QTypeRevision>, QString> m_seenImports;
- QHash<QQmlJSScope::Import, QSharedPointer<AvailableTypes>> m_cachedImportTypes;
+ QHash<QQmlJS::Import, QSharedPointer<AvailableTypes>> m_cachedImportTypes;
QHash<QString, Import> m_seenQmldirFiles;
QHash<QString, QQmlJSScope::Ptr> m_importedFiles;
@@ -153,7 +215,7 @@ private:
QQmlJSResourceFileMapper *m_metaDataMapper = nullptr;
bool m_useOptionalImports;
- ImportVisitorCreator m_createImportVisitor = nullptr;
+ ImportVisitor m_importVisitor;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 48c4bcbeef..f048748b58 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qqmljsimportvisitor_p.h"
+#include "qqmljslogger_p.h"
#include "qqmljsmetatypes_p.h"
#include "qqmljsresourcefilemapper_p.h"
@@ -13,13 +14,18 @@
#include <QtCore/qrect.h>
#include <QtCore/qsize.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <QtQml/private/qv4codegen_p.h>
#include <QtQml/private/qqmlstringconverters_p.h>
#include <QtQml/private/qqmlirbuilder_p.h>
#include "qqmljsscope_p.h"
#include "qqmljsutils_p.h"
+#include "qqmljsloggingutils.h"
+#include "qqmlsaconstants.h"
#include <algorithm>
+#include <limits>
+#include <optional>
#include <variant>
QT_BEGIN_NAMESPACE
@@ -29,13 +35,56 @@ using namespace Qt::StringLiterals;
using namespace QQmlJS::AST;
/*!
+ \internal
+ Returns if assigning \a assignedType to \a property would require an
+ implicit component wrapping.
+ */
+static bool causesImplicitComponentWrapping(const QQmlJSMetaProperty &property,
+ const QQmlJSScope::ConstPtr &assignedType)
+{
+ // See QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents()
+ // for the logic in qqmltypecompiler
+
+ // Note: unlike findAndRegisterImplicitComponents() we do not check whether
+ // the property type is *derived* from QQmlComponent at some point because
+ // this is actually meaningless (and in the case of QQmlComponent::create()
+ // gets rejected in QQmlPropertyValidator): if the type is not a
+ // QQmlComponent, we have a type mismatch because of assigning a Component
+ // object to a non-Component property
+ const bool propertyVerdict = property.type()->internalName() == u"QQmlComponent";
+
+ const bool assignedTypeVerdict = [&assignedType]() {
+ // Note: nonCompositeBaseType covers the case when assignedType itself
+ // is non-composite
+ auto cppBase = QQmlJSScope::nonCompositeBaseType(assignedType);
+ Q_ASSERT(cppBase); // any QML type has (or must have) a C++ base type
+
+ // See isUsableComponent() in qqmltypecompiler.cpp: along with checking
+ // whether a type has a QQmlComponent static meta object (which we
+ // substitute here with checking the first non-composite base for being
+ // a QQmlComponent), it also excludes QQmlAbstractDelegateComponent
+ // subclasses from implicit wrapping
+ if (cppBase->internalName() == u"QQmlComponent")
+ return false;
+ for (; cppBase; cppBase = cppBase->baseType()) {
+ if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
+ return false;
+ }
+ return true;
+ }();
+
+ return propertyVerdict && assignedTypeVerdict;
+}
+
+/*!
\internal
Sets the name of \a scope to \a name based on \a type.
*/
inline void setScopeName(QQmlJSScope::Ptr &scope, QQmlJSScope::ScopeType type, const QString &name)
{
Q_ASSERT(scope);
- if (type == QQmlJSScope::GroupedPropertyScope || type == QQmlJSScope::AttachedPropertyScope)
+ if (type == QQmlSA::ScopeType::GroupedPropertyScope
+ || type == QQmlSA::ScopeType::AttachedPropertyScope)
scope->setInternalName(name);
else
scope->setBaseTypeName(name);
@@ -48,7 +97,8 @@ inline void setScopeName(QQmlJSScope::Ptr &scope, QQmlJSScope::ScopeType type, c
inline QString getScopeName(const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ScopeType type)
{
Q_ASSERT(scope);
- if (type == QQmlJSScope::GroupedPropertyScope || type == QQmlJSScope::AttachedPropertyScope)
+ if (type == QQmlSA::ScopeType::GroupedPropertyScope
+ || type == QQmlSA::ScopeType::AttachedPropertyScope)
return scope->internalName();
return scope->baseTypeName();
@@ -77,10 +127,9 @@ QQmlJSImportVisitor::QQmlJSImportVisitor(
m_logger(logger),
m_rootScopeImports(
QQmlJSImporter::ImportedTypes::QML, {},
- importer->builtinInternalNames().intType(),
importer->builtinInternalNames().arrayType())
{
- m_currentScope->setScopeType(QQmlJSScope::JSFunctionScope);
+ m_currentScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope);
Q_ASSERT(logger); // must be valid
m_globalScope = m_currentScope;
@@ -102,7 +151,8 @@ QQmlJSImportVisitor::QQmlJSImportVisitor(
};
QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
- QQmlJSScope::JavaScriptIdentifier::LexicalScoped, QQmlJS::SourceLocation(), true
+ QQmlJSScope::JavaScriptIdentifier::LexicalScoped, QQmlJS::SourceLocation(), std::nullopt,
+ true
};
for (const char **globalName = QV4::Compiler::Codegen::s_globalNames; *globalName != nullptr;
++globalName) {
@@ -145,8 +195,8 @@ bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
{
- Q_ASSERT(type == QQmlJSScope::GroupedPropertyScope
- || type == QQmlJSScope::AttachedPropertyScope);
+ Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope
+ || type == QQmlSA::ScopeType::AttachedPropertyScope);
const auto pred = [&](const QQmlJSScope::ConstPtr &s) {
// it's either attached or group property, so use internalName()
@@ -185,7 +235,7 @@ bool QQmlJSImportVisitor::isTypeResolved(const QQmlJSScope::ConstPtr &type)
static bool mayBeUnresolvedGeneralizedGroupedProperty(const QQmlJSScope::ConstPtr &scope)
{
- return scope->scopeType() == QQmlJSScope::GroupedPropertyScope && !scope->baseType();
+ return scope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope && !scope->baseType();
}
void QQmlJSImportVisitor::resolveAliasesAndIds()
@@ -263,7 +313,7 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
newProperty.setIsWritable(targetProperty.isWritable());
newProperty.setIsPointer(targetProperty.isPointer());
- if (!typeScope.isNull()) {
+ if (!typeScope.isNull() && !object->isPropertyLocallyRequired(property.propertyName())) {
object->setPropertyLocallyRequired(
newProperty.propertyName(),
typeScope->isPropertyRequired(targetProperty.propertyName()));
@@ -360,7 +410,7 @@ void QQmlJSImportVisitor::importBaseModules()
// Pulling in the modules and neighboring qml files of the qmltypes we're trying to lint is not
// something we need to do.
if (!m_logger->fileName().endsWith(u".qmltypes"_s)) {
- QQmlJSScope::ContextualTypes fromDirectory =
+ QQmlJS::ContextualTypes fromDirectory =
m_importer->importDirectory(m_implicitImportDirectory);
m_rootScopeImports.addTypes(std::move(fromDirectory));
@@ -415,6 +465,7 @@ void QQmlJSImportVisitor::endVisit(UiProgram *)
setAllBindings();
processDefaultProperties();
processPropertyTypes();
+ processMethodTypes();
processPropertyBindings();
processPropertyBindingObjects();
checkRequiredProperties();
@@ -578,8 +629,7 @@ void QQmlJSImportVisitor::processDefaultProperties()
if (!isTypeResolved(propType, handleUnresolvedDefaultProperty))
continue;
- const auto scopes = *it;
- for (const auto &scope : scopes) {
+ for (const QQmlJSScope::Ptr &scope : std::as_const(*it)) {
if (!isTypeResolved(scope))
continue;
@@ -587,7 +637,7 @@ void QQmlJSImportVisitor::processDefaultProperties()
// Check whether the property can be assigned the scope
if (propType->canAssign(scope)) {
scope->setIsWrappedInImplicitComponent(
- QQmlJSScope::causesImplicitComponentWrapping(defaultProp, scope));
+ causesImplicitComponentWrapping(defaultProp, scope));
continue;
}
@@ -616,6 +666,39 @@ void QQmlJSImportVisitor::processPropertyTypes()
}
}
+void QQmlJSImportVisitor::processMethodTypes()
+{
+ for (const auto &type : m_pendingMethodTypes) {
+
+ for (auto [it, end] = type.scope->mutableOwnMethodsRange(type.methodName); it != end;
+ ++it) {
+ if (const auto returnType =
+ QQmlJSScope::findType(it->returnTypeName(), m_rootScopeImports).scope) {
+ it->setReturnType({ returnType });
+ } else {
+ m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
+ it->returnTypeName(), it->methodName()),
+ qmlUnresolvedType, type.location);
+ }
+
+ for (auto [parameter, parameterEnd] = it->mutableParametersRange();
+ parameter != parameterEnd; ++parameter) {
+ if (const auto parameterType =
+ QQmlJSScope::findType(parameter->typeName(), m_rootScopeImports)
+ .scope) {
+ parameter->setType({ parameterType });
+ } else {
+ m_logger->log(
+ u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
+ .arg(parameter->typeName(), parameter->name(),
+ it->methodName()),
+ qmlUnresolvedType, type.location);
+ }
+ }
+ }
+ }
+}
+
void QQmlJSImportVisitor::processPropertyBindingObjects()
{
QSet<QPair<QQmlJSScope::Ptr, QString>> foundLiterals;
@@ -689,17 +772,17 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
"incompatible type \"%3\"")
.arg(propertyName)
.arg(property.typeName())
- .arg(getScopeName(childScope, QQmlJSScope::QMLScope)),
+ .arg(getScopeName(childScope, QQmlSA::ScopeType::QMLScope)),
qmlIncompatibleType, objectBinding.location);
continue;
}
objectBinding.childScope->setIsWrappedInImplicitComponent(
- QQmlJSScope::causesImplicitComponentWrapping(property, childScope));
+ causesImplicitComponentWrapping(property, childScope));
// unique because it's per-scope and per-property
const auto uniqueBindingId = qMakePair(objectBinding.scope, objectBinding.name);
- const QString typeName = getScopeName(childScope, QQmlJSScope::QMLScope);
+ const QString typeName = getScopeName(childScope, QQmlSA::ScopeType::QMLScope);
if (objectBinding.onToken) {
if (childScope->hasInterface(QStringLiteral("QQmlPropertyValueInterceptor"))) {
@@ -778,7 +861,7 @@ void QQmlJSImportVisitor::checkRequiredProperties()
!= scopesToSearch.constEnd();
if (!found) {
- const QString scopeId = m_scopesById.id(defScope);
+ const QString scopeId = m_scopesById.id(defScope, scope);
bool propertyUsedInRootAlias = false;
if (!scopeId.isEmpty()) {
for (const QQmlJSMetaProperty &property :
@@ -807,14 +890,14 @@ void QQmlJSImportVisitor::checkRequiredProperties()
: QQmlJSScope::ConstPtr();
const QString propertyScopeName = !propertyScope.isNull()
- ? getScopeName(propertyScope, QQmlJSScope::QMLScope)
+ ? getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope)
: u"here"_s;
const QString requiredScopeName = prevRequiredScope
- ? getScopeName(prevRequiredScope, QQmlJSScope::QMLScope)
+ ? getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
: u"here"_s;
- std::optional<FixSuggestion> suggestion;
+ std::optional<QQmlJSFixSuggestion> suggestion;
QString message =
QStringLiteral(
@@ -824,15 +907,15 @@ void QQmlJSImportVisitor::checkRequiredProperties()
if (requiredScope != scope) {
if (!prevRequiredScope.isNull()) {
auto sourceScope = prevRequiredScope->baseType();
- suggestion = FixSuggestion {
- { { u"%1:%2:%3: Property marked as required in %4"_s
- .arg(sourceScope->filePath())
- .arg(sourceScope->sourceLocation().startLine)
- .arg(sourceScope->sourceLocation().startColumn)
- .arg(requiredScopeName),
- sourceScope->sourceLocation(), QString(),
- sourceScope->filePath() } }
+ suggestion = QQmlJSFixSuggestion{
+ "%1:%2:%3: Property marked as required in %4."_L1
+ .arg(sourceScope->filePath())
+ .arg(sourceScope->sourceLocation().startLine)
+ .arg(sourceScope->sourceLocation().startColumn)
+ .arg(requiredScopeName),
+ sourceScope->sourceLocation()
};
+ suggestion->setFilename(sourceScope->filePath());
} else {
message += QStringLiteral(" (marked as required by %1)")
.arg(requiredScopeName);
@@ -863,7 +946,7 @@ void QQmlJSImportVisitor::processPropertyBindings()
continue;
// TODO: Can this be in a better suited category?
- std::optional<FixSuggestion> fixSuggestion;
+ std::optional<QQmlJSFixSuggestion> fixSuggestion;
for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
baseScope = baseScope->baseType()) {
@@ -916,12 +999,11 @@ void QQmlJSImportVisitor::checkSignal(
const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location,
const QString &handlerName, const QStringList &handlerParameters)
{
- const auto signal = QQmlJSUtils::signalName(handlerName);
+ const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
std::optional<QQmlJSMetaMethod> signalMethod;
- const auto setSignalMethod = [&](const QQmlJSScope::ConstPtr &scope,
- const QString &name) {
- const auto methods = scope->methods(name, QQmlJSMetaMethod::Signal);
+ const auto setSignalMethod = [&](const QQmlJSScope::ConstPtr &scope, const QString &name) {
+ const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
if (!methods.isEmpty())
signalMethod = methods[0];
};
@@ -929,8 +1011,7 @@ void QQmlJSImportVisitor::checkSignal(
if (signal.has_value()) {
if (signalScope->hasMethod(*signal)) {
setSignalMethod(signalScope, *signal);
- } else if (auto p = QQmlJSUtils::changeHandlerProperty(signalScope, *signal);
- p.has_value()) {
+ } else if (auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
// we have a change handler of the form "onXChanged" where 'X'
// is a property name
@@ -945,7 +1026,7 @@ void QQmlJSImportVisitor::checkSignal(
}
if (!signalMethod.has_value()) { // haven't found anything
- std::optional<FixSuggestion> fix;
+ std::optional<QQmlJSFixSuggestion> fix;
// There is a small chance of suggesting this fix for things that are not actually
// QtQml/Connections elements, but rather some other thing that is also called
@@ -957,15 +1038,13 @@ void QQmlJSImportVisitor::checkSignal(
const qsizetype newLength = m_logger->code().indexOf(u'\n', location.end())
- location.offset;
- fix = FixSuggestion { { FixSuggestion::Fix {
- QStringLiteral("Implicitly defining %1 as signal handler in "
- "Connections is deprecated. Create a function "
- "instead")
- .arg(handlerName),
- QQmlJS::SourceLocation(location.offset, newLength, location.startLine,
- location.startColumn),
- QStringLiteral("function %1(%2) { ... }")
- .arg(handlerName, handlerParameters.join(u", ")) } } };
+ fix = QQmlJSFixSuggestion{
+ "Implicitly defining %1 as signal handler in Connections is deprecated. "
+ "Create a function instead."_L1.arg(handlerName),
+ QQmlJS::SourceLocation(location.offset, newLength, location.startLine,
+ location.startColumn),
+ "function %1(%2) { ... }"_L1.arg(handlerName, handlerParameters.join(u", "))
+ };
}
m_logger->log(QStringLiteral("no matching signal found for handler \"%1\"")
@@ -1102,7 +1181,7 @@ void QQmlJSImportVisitor::addDefaultProperties()
// Note: in this specific code path, binding on default property
// means an object binding (we work with pending objects here)
QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
- binding.setObject(getScopeName(m_currentScope, QQmlJSScope::QMLScope),
+ binding.setObject(getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope),
QQmlJSScope::ConstPtr(m_currentScope));
m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() { return binding; },
QQmlJSScope::UnnamedPropertyTarget });
@@ -1181,17 +1260,18 @@ void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr sc
auto childScope = children.takeFirst();
const auto type = childScope->scopeType();
switch (type) {
- case QQmlJSScope::GroupedPropertyScope:
- case QQmlJSScope::AttachedPropertyScope:
+ case QQmlSA::ScopeType::GroupedPropertyScope:
+ case QQmlSA::ScopeType::AttachedPropertyScope:
if (!childScope->baseType()) {
m_logger->log(QStringLiteral("unknown %1 property scope %2.")
- .arg(type == QQmlJSScope::GroupedPropertyScope
+ .arg(type == QQmlSA::ScopeType::GroupedPropertyScope
? QStringLiteral("grouped")
: QStringLiteral("attached"),
childScope->internalName()),
qmlUnqualified, childScope->sourceLocation());
}
children.append(childScope->childScopes());
+ break;
default:
break;
}
@@ -1202,9 +1282,9 @@ void QQmlJSImportVisitor::flushPendingSignalParameters()
{
const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
for (const QString &parameter : handler.signalParameters) {
- m_currentScope->insertJSIdentifier(
- parameter,
- { QQmlJSScope::JavaScriptIdentifier::Injected, m_pendingSignalHandler, false });
+ m_currentScope->insertJSIdentifier(parameter,
+ { QQmlJSScope::JavaScriptIdentifier::Injected,
+ m_pendingSignalHandler, std::nullopt, false });
}
m_pendingSignalHandler = QQmlJS::SourceLocation();
}
@@ -1270,8 +1350,9 @@ int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices(
{
const auto suitableScope = [](const QQmlJSScope::Ptr &scope) {
const auto type = scope->scopeType();
- return type == QQmlJSScope::QMLScope || type == QQmlJSScope::GroupedPropertyScope
- || type == QQmlJSScope::AttachedPropertyScope;
+ return type == QQmlSA::ScopeType::QMLScope
+ || type == QQmlSA::ScopeType::GroupedPropertyScope
+ || type == QQmlSA::ScopeType::AttachedPropertyScope;
};
if (!suitableScope(scope))
@@ -1320,7 +1401,7 @@ void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument() const
bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
{
if (m_pendingSignalHandler.isValid()) {
- enterEnvironment(QQmlJSScope::JSFunctionScope, u"signalhandler"_s,
+ enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, u"signalhandler"_s,
ast->firstSourceLocation());
flushPendingSignalParameters();
}
@@ -1329,7 +1410,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
{
- if (m_currentScope->scopeType() == QQmlJSScope::JSFunctionScope
+ if (m_currentScope->scopeType() == QQmlSA::ScopeType::JSFunctionScope
&& m_currentScope->baseTypeName() == u"signalhandler"_s) {
leaveEnvironment();
}
@@ -1366,9 +1447,9 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::StringLiteral *sl)
templateString += c;
}
- const FixSuggestion suggestion = { { { u"Use a template literal instead"_s,
- sl->literalToken, u"`" % templateString % u"`",
- QString(), false } } };
+ QQmlJSFixSuggestion suggestion = { "Use a template literal instead."_L1, sl->literalToken,
+ u"`" % templateString % u"`" };
+ suggestion.setAutoApplicable();
m_logger->log(QStringLiteral("String contains unescaped line terminator which is "
"deprecated."),
qmlMultilineStrings, sl->literalToken, true, true, suggestion);
@@ -1381,17 +1462,41 @@ inline QQmlJSImportVisitor::UnfinishedBinding
createNonUniqueScopeBinding(QQmlJSScope::Ptr &scope, const QString &name,
const QQmlJS::SourceLocation &srcLocation);
+static void logLowerCaseImport(QStringView superType, QQmlJS::SourceLocation location,
+ QQmlJSLogger *logger)
+{
+ QStringView namespaceName{ superType };
+ namespaceName = namespaceName.first(namespaceName.indexOf(u'.'));
+ logger->log(u"Namespace '%1' of '%2' must start with an upper case letter."_s.arg(namespaceName)
+ .arg(superType),
+ qmlUncreatableType, location, true, true);
+}
+
bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
{
const QString superType = buildName(definition->qualifiedTypeNameId);
const bool isRoot = !rootScopeIsValid();
Q_ASSERT(!superType.isEmpty());
- if (superType.front().isUpper()) {
+
+ // we need to assume that it is a type based on its capitalization. Types defined in inline
+ // components, for example, can have their type definition after their type usages:
+ // Item { property IC myIC; component IC: Item{}; }
+ const qsizetype indexOfTypeName = superType.lastIndexOf(u'.');
+ const bool looksLikeGroupedProperty = superType.front().isLower();
+
+ if (indexOfTypeName != -1 && looksLikeGroupedProperty) {
+ logLowerCaseImport(superType, definition->qualifiedTypeNameId->identifierToken,
+ m_logger);
+ }
+
+ if (!looksLikeGroupedProperty) {
if (!isRoot) {
- enterEnvironment(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation());
+ enterEnvironment(QQmlSA::ScopeType::QMLScope, superType,
+ definition->firstSourceLocation());
} else {
- enterRootScope(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation());
+ enterRootScope(QQmlSA::ScopeType::QMLScope, superType,
+ definition->firstSourceLocation());
m_currentScope->setIsSingleton(m_rootIsSingleton);
}
@@ -1401,7 +1506,6 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
if (isRoot && base->internalName() == u"QQmlComponent") {
m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
definition->qualifiedTypeNameId->identifierToken, true, true);
- return false;
}
if (base->isSingleton() && m_currentScope->isComposite()) {
m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
@@ -1421,19 +1525,19 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
m_currentScope->setIsInlineComponent(true);
m_currentScope->setInlineComponentName(name);
+ m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
m_rootScopeImports.setType(name, { m_currentScope, revision });
m_nextIsInlineComponent = false;
}
addDefaultProperties();
- Q_ASSERT(m_currentScope->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope);
m_qmlTypes.append(m_currentScope);
m_objectDefinitionScopes << m_currentScope;
} else {
- enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, superType,
+ enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, superType,
definition->firstSourceLocation());
- Q_ASSERT(rootScopeIsValid());
m_bindings.append(createNonUniqueScopeBinding(m_currentScope, superType,
definition->firstSourceLocation()));
QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes);
@@ -1484,8 +1588,10 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
}
UiParameterList *param = publicMember->parameters;
QQmlJSMetaMethod method;
- method.setMethodType(QQmlJSMetaMethod::Signal);
+ method.setMethodType(QQmlJSMetaMethodType::Signal);
method.setMethodName(publicMember->name.toString());
+ method.setSourceLocation(combine(publicMember->firstSourceLocation(),
+ publicMember->lastSourceLocation()));
while (param) {
method.addParameter(
QQmlJSMetaParameter(
@@ -1504,6 +1610,10 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
publicMember->firstSourceLocation());
}
QString typeName = buildName(publicMember->memberType);
+ if (typeName.contains(u'.') && typeName.front().isLower()) {
+ logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
+ }
+
QString aliasExpr;
const bool isAlias = (typeName == u"alias"_s);
if (isAlias) {
@@ -1526,9 +1636,11 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
if (const auto idExpression = cast<IdentifierExpression *>(node)) {
aliasExpr.prepend(idExpression->name.toString());
} else {
+ // cast to expression might have failed above, so use publicMember->statement
+ // to obtain the source location
m_logger->log(QStringLiteral("Invalid alias expression. Only IDs and field "
"member expressions can be aliased."),
- qmlSyntax, expression->firstSourceLocation());
+ qmlSyntax, publicMember->statement->firstSourceLocation());
}
};
tryParseAlias();
@@ -1575,7 +1687,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
if (parseResult == BindingExpressionParseResult::Script) {
Q_ASSERT(!m_savedBindingOuterScope); // automatically true due to grammar
m_savedBindingOuterScope = m_currentScope;
- enterEnvironment(QQmlJSScope::JSFunctionScope, QStringLiteral("binding"),
+ enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral("binding"),
publicMember->statement->firstSourceLocation());
}
@@ -1611,9 +1723,11 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
{
using namespace QQmlJS::AST;
auto name = fexpr->name.toString();
+ bool pending = false;
if (!name.isEmpty()) {
QQmlJSMetaMethod method(name);
- method.setMethodType(QQmlJSMetaMethod::Method);
+ method.setMethodType(QQmlJSMetaMethodType::Method);
+ method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
if (!m_pendingMethodAnnotations.isEmpty()) {
method.setAnnotations(m_pendingMethodAnnotations);
@@ -1637,6 +1751,15 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
} else {
anyFormalTyped = true;
method.addParameter(QQmlJSMetaParameter(parameter.id, type));
+ if (!pending) {
+ m_pendingMethodTypes << PendingMethodType{
+ m_currentScope,
+ name,
+ combine(parameter.typeAnnotation->firstSourceLocation(),
+ parameter.typeAnnotation->lastSourceLocation())
+ };
+ pending = true;
+ }
}
}
}
@@ -1648,9 +1771,17 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
// Methods with only untyped arguments return an untyped value.
// Methods with at least one typed argument but no explicit return type return void.
// In order to make a function without arguments return void, you have to specify that.
- if (parseTypes && fexpr->typeAnnotation)
+ if (parseTypes && fexpr->typeAnnotation) {
method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
- else if (anyFormalTyped)
+ if (!pending) {
+ m_pendingMethodTypes << PendingMethodType{
+ m_currentScope, name,
+ combine(fexpr->typeAnnotation->firstSourceLocation(),
+ fexpr->typeAnnotation->lastSourceLocation())
+ };
+ pending = true;
+ }
+ } else if (anyFormalTyped)
method.setReturnTypeName(QStringLiteral("void"));
else
method.setReturnTypeName(QStringLiteral("var"));
@@ -1658,15 +1789,16 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
method.setJsFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
m_currentScope->addOwnMethod(method);
- if (m_currentScope->scopeType() != QQmlJSScope::QMLScope) {
+ if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
m_currentScope->insertJSIdentifier(name,
{ QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
- fexpr->firstSourceLocation(), false });
+ fexpr->firstSourceLocation(),
+ method.returnTypeName(), false });
}
- enterEnvironment(QQmlJSScope::JSFunctionScope, name, fexpr->firstSourceLocation());
+ enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
} else {
addFunctionOrExpression(m_currentScope, QStringLiteral("<anon>"));
- enterEnvironment(QQmlJSScope::JSFunctionScope, QStringLiteral("<anon>"),
+ enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral("<anon>"),
fexpr->firstSourceLocation());
}
}
@@ -1706,7 +1838,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast)
QQmlJSMetaProperty prop;
prop.setPropertyName(ast->name.toString());
m_currentScope->addOwnProperty(prop);
- enterEnvironment(QQmlJSScope::JSFunctionScope, ast->name.toString(),
+ enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
ast->firstSourceLocation());
return true;
}
@@ -1767,13 +1899,18 @@ QQmlJSImportVisitor::parseBindingExpression(const QString &name,
QQmlJSMetaPropertyBinding binding(location, name);
binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
- QQmlJSMetaPropertyBinding::Script_PropertyBinding);
- m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; } });
+ QQmlSA::ScriptBindingKind::PropertyBinding);
+ m_bindings.append(UnfinishedBinding {
+ m_currentScope,
+ [binding = std::move(binding)]() { return binding; }
+ });
return BindingExpressionParseResult::Script;
}
auto expr = exprStatement->expression;
- QQmlJSMetaPropertyBinding binding(expr->firstSourceLocation(), name);
+ QQmlJSMetaPropertyBinding binding(
+ combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
+ name);
bool isUndefinedBinding = false;
@@ -1809,7 +1946,11 @@ QQmlJSImportVisitor::parseBindingExpression(const QString &name,
binding.setStringLiteral(templateLit->value);
} else {
binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
- QQmlJSMetaPropertyBinding::Script_PropertyBinding);
+ QQmlSA::ScriptBindingKind::PropertyBinding);
+ for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
+ if (QQmlJS::AST::ExpressionNode *expression = l->expression)
+ expression->accept(this);
+ }
}
break;
}
@@ -1827,16 +1968,15 @@ QQmlJSImportVisitor::parseBindingExpression(const QString &name,
if (!binding.isValid()) {
// consider this to be a script binding (see IRBuilder::setBindingValue)
binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
- QQmlJSMetaPropertyBinding::Script_PropertyBinding,
- isUndefinedBinding
- ? QQmlJSMetaPropertyBinding::ScriptValue_Undefined
- : QQmlJSMetaPropertyBinding::ScriptValue_Unknown);
+ QQmlSA::ScriptBindingKind::PropertyBinding,
+ isUndefinedBinding ? ScriptBindingValueType::ScriptValue_Undefined
+ : ScriptBindingValueType::ScriptValue_Unknown);
}
m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; } });
// translations are neither literal bindings nor script bindings
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Translation
- || binding.bindingType() == QQmlJSMetaPropertyBinding::TranslationById) {
+ if (binding.bindingType() == QQmlSA::BindingType::Translation
+ || binding.bindingType() == QQmlSA::BindingType::TranslationById) {
return BindingExpressionParseResult::Translation;
}
if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
@@ -1872,7 +2012,6 @@ void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scri
m_logger->log(u"Failed to parse id"_s, qmlSyntax,
statement->expression->firstSourceLocation());
return QString();
-
}();
if (m_scopesById.existsAnywhereInDocument(name)) {
// ### TODO: find an alternative to breakInhertianceCycles here
@@ -1905,12 +2044,11 @@ createNonUniqueScopeBinding(QQmlJSScope::Ptr &scope, const QString &name,
{
const auto createBinding = [=]() {
const QQmlJSScope::ScopeType type = scope->scopeType();
- Q_ASSERT(type == QQmlJSScope::GroupedPropertyScope
- || type == QQmlJSScope::AttachedPropertyScope);
- const QQmlJSMetaPropertyBinding::BindingType bindingType =
- (type == QQmlJSScope::GroupedPropertyScope)
- ? QQmlJSMetaPropertyBinding::GroupProperty
- : QQmlJSMetaPropertyBinding::AttachedProperty;
+ Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope
+ || type == QQmlSA::ScopeType::AttachedPropertyScope);
+ const QQmlSA::BindingType bindingType = (type == QQmlSA::ScopeType::GroupedPropertyScope)
+ ? QQmlSA::BindingType::GroupProperty
+ : QQmlSA::BindingType::AttachedProperty;
const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
const bool alreadyHasBinding = std::any_of(propertyBindings.first, propertyBindings.second,
@@ -1921,7 +2059,7 @@ createNonUniqueScopeBinding(QQmlJSScope::Ptr &scope, const QString &name,
return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {});
QQmlJSMetaPropertyBinding binding(srcLocation, name);
- if (type == QQmlJSScope::GroupedPropertyScope)
+ if (type == QQmlSA::ScopeType::GroupedPropertyScope)
binding.setGroupBinding(static_cast<QSharedPointer<QQmlJSScope>>(scope));
else
binding.setAttachedBinding(static_cast<QSharedPointer<QQmlJSScope>>(scope));
@@ -1957,11 +2095,11 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
const bool isAttachedProperty = name.front().isUpper();
if (isAttachedProperty) {
// attached property
- enterEnvironmentNonUnique(QQmlJSScope::AttachedPropertyScope, prefix + name,
+ enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name,
group->firstSourceLocation());
} else {
// grouped property
- enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, prefix + name,
+ enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name,
group->firstSourceLocation());
}
m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name,
@@ -1970,11 +2108,11 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
prefix.clear();
}
- auto name = group->name.toString();
+ const auto name = group->name.toString();
// This is a preliminary check.
// Even if the name starts with "on", it might later turn out not to be a signal.
- const auto signal = QQmlJSUtils::signalName(name);
+ const auto signal = QQmlSignalNames::handlerNameToSignalName(name);
if (!signal.has_value() || m_currentScope->hasProperty(name)) {
m_propertyBindings[m_currentScope].append(
@@ -1994,7 +2132,7 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
}
QQmlJSMetaMethod scopeSignal;
- const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethod::Signal);
+ const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
if (!methods.isEmpty())
scopeSignal = methods[0];
@@ -2019,19 +2157,18 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
signalParameters]() {
// when encountering a signal handler, add it as a script binding
Q_ASSERT(scope->isFullyResolved());
- QQmlJSMetaPropertyBinding::ScriptBindingKind kind =
- QQmlJSMetaPropertyBinding::Script_Invalid;
- const auto methods = scope->methods(signalName, QQmlJSMetaMethod::Signal);
+ QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Invalid;
+ const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
if (!methods.isEmpty()) {
- kind = QQmlJSMetaPropertyBinding::Script_SignalHandler;
+ kind = QQmlSA::ScriptBindingKind::SignalHandler;
checkSignal(scope, groupLocation, name, signalParameters);
- } else if (QQmlJSUtils::changeHandlerProperty(scope, signalName).has_value()) {
- kind = QQmlJSMetaPropertyBinding::Script_ChangeHandler;
+ } else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
+ kind = QQmlSA::ScriptBindingKind::ChangeHandler;
checkSignal(scope, groupLocation, name, signalParameters);
} else if (scope->hasProperty(name)) {
// Not a signal handler after all.
// We can see this now because the type is fully resolved.
- kind = QQmlJSMetaPropertyBinding::Script_PropertyBinding;
+ kind = QQmlSA::ScriptBindingKind::PropertyBinding;
m_signalHandlers.remove(firstSourceLocation);
} else {
// We already know it's bad, but let's allow checkSignal() to do its thing.
@@ -2049,12 +2186,12 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
// TODO: before leaving the scopes, we must create the binding.
// Leave any group/attached scopes so that the binding scope doesn't see its properties.
- while (m_currentScope->scopeType() == QQmlJSScope::GroupedPropertyScope
- || m_currentScope->scopeType() == QQmlJSScope::AttachedPropertyScope) {
+ while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope
+ || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) {
leaveEnvironment();
}
- enterEnvironment(QQmlJSScope::JSFunctionScope, QStringLiteral("binding"),
+ enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral("binding"),
scriptBinding->statement->firstSourceLocation());
return true;
@@ -2079,7 +2216,7 @@ void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
{
- enterEnvironment(QQmlJSScope::QMLScope, buildName(arrayBinding->qualifiedId),
+ enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
arrayBinding->firstSourceLocation());
m_currentScope->setIsArrayScope(true);
@@ -2096,13 +2233,13 @@ void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
// other expressions involving lists (e.g. `var p: [1,2,3]`) are considered
// to be script bindings
const auto children = m_currentScope->childScopes();
- const auto propertyName = getScopeName(m_currentScope, QQmlJSScope::QMLScope);
+ const auto propertyName = getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope);
leaveEnvironment();
qsizetype i = 0;
for (auto element = arrayBinding->members; element; element = element->next, ++i) {
const auto &type = children[i];
- if ((type->scopeType() != QQmlJSScope::QMLScope)) {
+ if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
m_logger->log(u"Declaring an object which is not an Qml object"
" as a list member."_s, qmlSyntax, element->firstSourceLocation());
return;
@@ -2111,9 +2248,13 @@ void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
<< PendingPropertyObjectBinding { m_currentScope, type, propertyName,
element->firstSourceLocation(), false };
QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
- binding.setObject(getScopeName(type, QQmlJSScope::QMLScope), QQmlJSScope::ConstPtr(type));
- m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; },
- QQmlJSScope::ListPropertyTarget });
+ binding.setObject(getScopeName(type, QQmlSA::ScopeType::QMLScope),
+ QQmlJSScope::ConstPtr(type));
+ m_bindings.append(UnfinishedBinding {
+ m_currentScope,
+ [binding = std::move(binding)]() { return binding; },
+ QQmlJSScope::ListPropertyTarget
+ });
}
}
@@ -2124,6 +2265,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
uied->firstSourceLocation());
}
QQmlJSMetaEnum qmlEnum(uied->name.toString());
+ qmlEnum.setIsQml(true);
for (const auto *member = uied->members; member; member = member->next) {
qmlEnum.addKey(member->member.toString());
qmlEnum.addValue(int(member->value));
@@ -2146,63 +2288,97 @@ void QQmlJSImportVisitor::addImportWithLocation(const QString &name,
m_importLocations.insert(loc);
}
+void QQmlJSImportVisitor::importFromHost(const QString &path, const QString &prefix,
+ const QQmlJS::SourceLocation &location)
+{
+ QFileInfo fileInfo(path);
+ if (!fileInfo.exists()) {
+ m_logger->log("File or directory you are trying to import does not exist: %1."_L1.arg(path),
+ qmlImport, location);
+ return;
+ }
+
+ if (fileInfo.isFile()) {
+ const auto scope = m_importer->importFile(path);
+ const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
+ m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
+ addImportWithLocation(actualPrefix, location);
+ } else if (fileInfo.isDir()) {
+ const auto scopes = m_importer->importDirectory(path, prefix);
+ m_rootScopeImports.addTypes(scopes);
+ for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end; it++)
+ addImportWithLocation(*it, location);
+ } else {
+ m_logger->log(
+ "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
+ path),
+ qmlImport, location);
+ }
+}
+
+void QQmlJSImportVisitor::importFromQrc(const QString &path, const QString &prefix,
+ const QQmlJS::SourceLocation &location)
+{
+ if (const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper()) {
+ if (mapper->isFile(path)) {
+ const auto entry = m_importer->resourceFileMapper()->entry(
+ QQmlJSResourceFileMapper::resourceFileFilter(path));
+ const auto scope = m_importer->importFile(entry.filePath);
+ const QString actualPrefix =
+ prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
+ m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
+ addImportWithLocation(actualPrefix, location);
+ } else {
+ const auto scopes = m_importer->importDirectory(path, prefix);
+ m_rootScopeImports.addTypes(scopes);
+ for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end; it++)
+ addImportWithLocation(*it, location);
+ }
+ }
+}
+
bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
{
- auto addImportLocation = [this, import](const QString &name) {
- addImportWithLocation(name, import->firstSourceLocation());
- };
// construct path
QString prefix = QLatin1String("");
if (import->asToken.isValid()) {
prefix += import->importId;
+ if (!import->importId.isEmpty() && !import->importId.front().isUpper()) {
+ m_logger->log(u"Import qualifier '%1' must start with a capital letter."_s.arg(
+ import->importId),
+ qmlImport, import->importIdToken, true, true);
+ }
}
- auto filename = import->fileName.toString();
+
+ const QString filename = import->fileName.toString();
if (!filename.isEmpty()) {
- const QFileInfo file(filename);
- const QString absolute = file.isRelative()
- ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
- : filename;
-
- if (absolute.startsWith(u':')) {
- if (m_importer->resourceFileMapper()) {
- if (m_importer->resourceFileMapper()->isFile(absolute.mid(1))) {
- const auto entry = m_importer->resourceFileMapper()->entry(
- QQmlJSResourceFileMapper::resourceFileFilter(absolute.mid(1)));
- const auto scope = m_importer->importFile(entry.filePath);
- const QString actualPrefix = prefix.isEmpty()
- ? QFileInfo(entry.resourcePath).baseName()
- : prefix;
- m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
-
- addImportLocation(actualPrefix);
- } else {
- const auto scopes = m_importer->importDirectory(absolute, prefix);
- m_rootScopeImports.addTypes(scopes);
- for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end;
- it++)
- addImportLocation(*it);
- }
+ const QUrl url(filename);
+ const QString scheme = url.scheme();
+ const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
+ if (scheme == ""_L1) {
+ QFileInfo fileInfo(url.path());
+ QString absolute = fileInfo.isRelative()
+ ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
+ : filename;
+ if (absolute.startsWith(u':')) {
+ importFromQrc(absolute, prefix, importLocation);
+ } else {
+ importFromHost(absolute, prefix, importLocation);
}
-
- processImportWarnings(QStringLiteral("URL \"%1\"").arg(absolute), import->firstSourceLocation());
+ processImportWarnings("path \"%1\""_L1.arg(url.path()), importLocation);
return true;
+ } else if (scheme == "file"_L1) {
+ importFromHost(url.path(), prefix, importLocation);
+ processImportWarnings("URL \"%1\""_L1.arg(url.path()), importLocation);
+ return true;
+ } else if (scheme == "qrc"_L1) {
+ importFromQrc(":"_L1 + url.path(), prefix, importLocation);
+ processImportWarnings("URL \"%1\""_L1.arg(url.path()), importLocation);
+ return true;
+ } else {
+ m_logger->log("Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
+ qmlImport, import->firstSourceLocation());
}
-
- QFileInfo path(absolute);
- if (path.isDir()) {
- const auto scopes = m_importer->importDirectory(path.canonicalFilePath(), prefix);
- m_rootScopeImports.addTypes(scopes);
- for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end; it++)
- addImportLocation(*it);
- } else if (path.isFile()) {
- const auto scope = m_importer->importFile(path.canonicalFilePath());
- const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
- m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
- addImportLocation(actualPrefix);
- }
-
- processImportWarnings(QStringLiteral("path \"%1\"").arg(path.canonicalFilePath()), import->firstSourceLocation());
- return true;
}
const QString path = buildName(import->importUri);
@@ -2214,7 +2390,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
&staticModulesProvided);
m_rootScopeImports.addTypes(imported);
for (auto it = imported.types().keyBegin(), end = imported.types().keyEnd(); it != end; it++)
- addImportLocation(*it);
+ addImportWithLocation(*it, import->firstSourceLocation());
if (prefix.isEmpty()) {
for (const QString &staticModule : staticModulesProvided) {
@@ -2230,6 +2406,21 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
return true;
}
+#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
+template<typename F>
+void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
+{
+ for (const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
+ assign(v->value);
+}
+#else
+template<typename F>
+void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
+{
+ assign(pragma->value);
+}
+#endif
+
bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
{
if (pragma->name == u"Strict"_s) {
@@ -2245,37 +2436,43 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
} else if (pragma->name == u"Singleton") {
m_rootIsSingleton = true;
} else if (pragma->name == u"ComponentBehavior") {
- if (pragma->value == u"Bound") {
- m_scopesById.setComponentsAreBound(true);
- } else if (pragma->value == u"Unbound") {
- m_scopesById.setComponentsAreBound(false);
- } else {
- m_logger->log(
- u"Unkonwn argument \"%s\" to pragma ComponentBehavior"_s.arg(pragma->value),
- qmlSyntax, pragma->firstSourceLocation());
- }
+ handlePragmaValues(pragma, [this, pragma](QStringView value) {
+ if (value == u"Bound") {
+ m_scopesById.setComponentsAreBound(true);
+ } else if (value == u"Unbound") {
+ m_scopesById.setComponentsAreBound(false);
+ } else {
+ m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
+ qmlSyntax, pragma->firstSourceLocation());
+ }
+ });
} else if (pragma->name == u"FunctionSignatureBehavior") {
- if (pragma->value == u"Enforced") {
- m_scopesById.setSignaturesAreEnforced(true);
- } else if (pragma->value == u"Ignored") {
- m_scopesById.setSignaturesAreEnforced(false);
- } else {
- m_logger->log(
- u"Unkonwn argument \"%s\" to pragma FunctionSignatureBehavior"_s
- .arg(pragma->value),
- qmlSyntax, pragma->firstSourceLocation());
- }
+ handlePragmaValues(pragma, [this, pragma](QStringView value) {
+ if (value == u"Enforced") {
+ m_scopesById.setSignaturesAreEnforced(true);
+ } else if (value == u"Ignored") {
+ m_scopesById.setSignaturesAreEnforced(false);
+ } else {
+ m_logger->log(
+ u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
+ qmlSyntax, pragma->firstSourceLocation());
+ }
+ });
} else if (pragma->name == u"ValueTypeBehavior") {
- if (pragma->value == u"Copy") {
- m_scopesById.setValueTypesAreCopied(true);
- } else if (pragma->value == u"Reference") {
- m_scopesById.setValueTypesAreCopied(false);
- } else {
- m_logger->log(
- u"Unkonwn argument \"%s\" to pragma ValueTypeBehavior"_s
- .arg(pragma->value),
- qmlSyntax, pragma->firstSourceLocation());
- }
+ handlePragmaValues(pragma, [this, pragma](QStringView value) {
+ if (value == u"Copy") {
+ // Ignore
+ } else if (value == u"Reference") {
+ // Ignore
+ } else if (value == u"Addressable") {
+ m_scopesById.setValueTypesAreAddressable(true);
+ } else if (value == u"Inaddressable") {
+ m_scopesById.setValueTypesAreAddressable(false);
+ } else {
+ m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
+ qmlSyntax, pragma->firstSourceLocation());
+ }
+ });
}
return true;
@@ -2289,7 +2486,7 @@ void QQmlJSImportVisitor::throwRecursionDepthError()
bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
{
- enterEnvironment(QQmlJSScope::JSFunctionScope, ast->name.toString(),
+ enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
ast->firstSourceLocation());
return true;
}
@@ -2301,7 +2498,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast)
{
- enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("forloop"),
+ enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("forloop"),
ast->firstSourceLocation());
return true;
}
@@ -2313,7 +2510,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast)
{
- enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("foreachloop"),
+ enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("foreachloop"),
ast->firstSourceLocation());
return true;
}
@@ -2325,7 +2522,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast)
{
- enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("block"),
+ enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("block"),
ast->firstSourceLocation());
if (m_pendingSignalHandler.isValid())
@@ -2341,7 +2538,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast)
{
- enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("case"),
+ enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("case"),
ast->firstSourceLocation());
return true;
}
@@ -2353,12 +2550,12 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
{
- enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("catch"),
+ enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("catch"),
catchStatement->firstSourceLocation());
m_currentScope->insertJSIdentifier(
catchStatement->patternElement->bindingIdentifier.toString(),
{ QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
- catchStatement->patternElement->firstSourceLocation(),
+ catchStatement->patternElement->firstSourceLocation(), std::nullopt,
catchStatement->patternElement->scope == QQmlJS::AST::VariableScope::Const });
return true;
}
@@ -2370,7 +2567,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
{
- enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("with"),
+ enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("with"),
ast->firstSourceLocation());
m_logger->log(QStringLiteral("with statements are strongly discouraged in QML "
@@ -2389,12 +2586,17 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::WithStatement *)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
{
while (vdl) {
+ std::optional<QString> typeName;
+ if (TypeAnnotation *annotation = vdl->declaration->typeAnnotation)
+ if (Type *type = annotation->type)
+ typeName = type->toString();
+
m_currentScope->insertJSIdentifier(
vdl->declaration->bindingIdentifier.toString(),
{ (vdl->declaration->scope == QQmlJS::AST::VariableScope::Var)
? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
: QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
- vdl->declaration->firstSourceLocation(),
+ vdl->declaration->firstSourceLocation(), typeName,
vdl->declaration->scope == QQmlJS::AST::VariableScope::Const });
vdl = vdl->next;
}
@@ -2404,9 +2606,14 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
{
for (auto const &boundName : fpl->boundNames()) {
+
+ std::optional<QString> typeName;
+ if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
+ if (Type *type = annotation->type)
+ typeName = type->toString();
m_currentScope->insertJSIdentifier(boundName.id,
{ QQmlJSScope::JavaScriptIdentifier::Parameter,
- fpl->firstSourceLocation(), false });
+ boundName.location, typeName, false });
}
return true;
}
@@ -2420,6 +2627,11 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
bool needsResolution = false;
int scopesEnteredCounter = 0;
+ const QString typeName = buildName(uiob->qualifiedTypeNameId);
+ if (typeName.front().isLower() && typeName.contains(u'.')) {
+ logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
+ }
+
QString prefix;
for (auto group = uiob->qualifiedId; group->next; group = group->next) {
const QString idName = group->name.toString();
@@ -2432,8 +2644,8 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
continue;
}
- const auto scopeKind = idName.front().isUpper() ? QQmlJSScope::AttachedPropertyScope
- : QQmlJSScope::GroupedPropertyScope;
+ const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
+ : QQmlSA::ScopeType::GroupedPropertyScope;
bool exists =
enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
@@ -2455,7 +2667,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
if (needsResolution)
QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes);
- enterEnvironment(QQmlJSScope::QMLScope, buildName(uiob->qualifiedTypeNameId),
+ enterEnvironment(QQmlSA::ScopeType::QMLScope, typeName,
uiob->qualifiedTypeNameId->identifierToken);
QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes);
@@ -2486,8 +2698,8 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
continue;
}
- const auto scopeKind = idName.front().isUpper() ? QQmlJSScope::AttachedPropertyScope
- : QQmlJSScope::GroupedPropertyScope;
+ const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
+ : QQmlSA::ScopeType::GroupedPropertyScope;
// definitely exists
[[maybe_unused]] bool exists =
enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
@@ -2508,7 +2720,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
while (!childScopes.isEmpty()) {
const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
- if (!m_scopesById.id(scope).isEmpty()) {
+ if (!m_scopesById.id(scope, scope).isEmpty()) {
foundIds = true;
break;
}
@@ -2535,14 +2747,14 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
if (uiob->hasOnToken) {
if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
- binding.setInterceptor(getScopeName(childScope, QQmlJSScope::QMLScope),
+ binding.setInterceptor(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
QQmlJSScope::ConstPtr(childScope));
} else { // if (childScope->hasInterface(u"QQmlPropertyValueSource"_s))
- binding.setValueSource(getScopeName(childScope, QQmlJSScope::QMLScope),
+ binding.setValueSource(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
QQmlJSScope::ConstPtr(childScope));
}
} else {
- binding.setObject(getScopeName(childScope, QQmlJSScope::QMLScope),
+ binding.setObject(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
QQmlJSScope::ConstPtr(childScope));
}
m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; } });
@@ -2571,8 +2783,8 @@ void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
bool QQmlJSImportVisitor::visit(ESModule *module)
{
Q_ASSERT(!rootScopeIsValid());
- enterRootScope(
- QQmlJSScope::JSLexicalScope, QStringLiteral("module"), module->firstSourceLocation());
+ enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("module"),
+ module->firstSourceLocation());
m_currentScope->setIsScript(true);
importBaseModules();
leaveEnvironment();
@@ -2602,17 +2814,24 @@ void QQmlJSImportVisitor::endVisit(Program *)
void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember)
{
+ // This is a rather rough approximation of "used type" but the "unused import"
+ // info message doesn't have to be 100% accurate.
const QString name = fieldMember->name.toString();
if (m_importTypeLocationMap.contains(name)) {
- if (m_rootScopeImports.isNullType(name))
+ const QQmlJSImportedScope type = m_rootScopeImports.type(name);
+ if (type.scope.isNull()) {
+ if (m_rootScopeImports.hasType(name))
+ m_usedTypes.insert(name);
+ } else if (!type.scope->ownAttachedTypeName().isEmpty()) {
m_usedTypes.insert(name);
+ }
}
}
bool QQmlJSImportVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
{
const QString name = idexp->name.toString();
- if (name.front().isUpper() && m_importTypeLocationMap.contains(name)) {
+ if (m_importTypeLocationMap.contains(name)) {
m_usedTypes.insert(name);
}
@@ -2626,12 +2845,16 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
QQmlJS::AST::BoundNames names;
element->boundNames(&names);
for (const auto &name : names) {
+ std::optional<QString> typeName;
+ if (TypeAnnotation *annotation = name.typeAnnotation.data())
+ if (Type *type = annotation->type)
+ typeName = type->toString();
m_currentScope->insertJSIdentifier(
name.id,
{ (element->scope == QQmlJS::AST::VariableScope::Var)
? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
: QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
- element->firstSourceLocation(),
+ name.location, typeName,
element->scope == QQmlJS::AST::VariableScope::Const });
}
}
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index 1a587cbb85..b04b0a0777 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -14,7 +14,8 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <private/qqmljscontextualtypes_p.h>
+#include <qtqmlcompilerexports.h>
#include "qqmljsannotation_p.h"
#include "qqmljsimporter_p.h"
@@ -33,18 +34,29 @@
QT_BEGIN_NAMESPACE
+namespace QQmlJS::Dom {
+class QQmlDomAstCreatorWithQQmlJSScope;
+}
+
struct QQmlJSResourceFileMapper;
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor
+class Q_QMLCOMPILER_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor
{
public:
+ QQmlJSImportVisitor();
QQmlJSImportVisitor(const QQmlJSScope::Ptr &target,
QQmlJSImporter *importer, QQmlJSLogger *logger,
const QString &implicitImportDirectory,
const QStringList &qmldirFiles = QStringList());
~QQmlJSImportVisitor();
+ using QQmlJS::AST::Visitor::endVisit;
+ using QQmlJS::AST::Visitor::postVisit;
+ using QQmlJS::AST::Visitor::preVisit;
+ using QQmlJS::AST::Visitor::visit;
+
QQmlJSScope::Ptr result() const { return m_exportedRootScope; }
+ const QQmlJSLogger *logger() const { return m_logger; }
QQmlJSLogger *logger() { return m_logger; }
QQmlJSImporter::ImportedTypes imports() const { return m_rootScopeImports; }
@@ -63,7 +75,9 @@ public:
static QString implicitImportDirectory(
const QString &localFile, QQmlJSResourceFileMapper *mapper);
- QQmlJSImporter *importer() { return m_importer; } // ### should this be restricted?
+ // ### should this be restricted?
+ QQmlJSImporter *importer() { return m_importer; }
+ const QQmlJSImporter *importer() const { return m_importer; }
struct UnfinishedBinding
{
@@ -246,6 +260,7 @@ protected:
void processPropertyBindings();
void checkRequiredProperties();
void processPropertyTypes();
+ void processMethodTypes();
void processPropertyBindingObjects();
void flushPendingSignalParameters();
@@ -271,6 +286,13 @@ protected:
QQmlJS::SourceLocation location;
};
+ struct PendingMethodType
+ {
+ QQmlJSScope::Ptr scope;
+ QString methodName;
+ QQmlJS::SourceLocation location;
+ };
+
struct PendingPropertyObjectBinding
{
QQmlJSScope::Ptr scope;
@@ -308,6 +330,7 @@ protected:
QHash<QQmlJSScope::Ptr, QVector<QQmlJSScope::Ptr>> m_pendingDefaultProperties;
QVector<PendingPropertyType> m_pendingPropertyTypes;
+ QVector<PendingMethodType> m_pendingMethodTypes;
QVector<PendingPropertyObjectBinding> m_pendingPropertyObjectBindings;
QVector<RequiredProperty> m_requiredProperties;
QVector<QQmlJSScope::Ptr> m_objectBindingScopes;
@@ -336,6 +359,14 @@ private:
const QQmlJS::SourceLocation &location);
void enterRootScope(QQmlJSScope::ScopeType type, const QString &name,
const QQmlJS::SourceLocation &location);
+
+ void importFromHost(const QString &path, const QString &prefix,
+ const QQmlJS::SourceLocation &location);
+ void importFromQrc(const QString &path, const QString &prefix,
+ const QQmlJS::SourceLocation &location);
+
+public:
+ friend class QQmlJS::Dom::QQmlDomAstCreatorWithQQmlJSScope;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp
index e84b04b713..b5744b0c3d 100644
--- a/src/qmlcompiler/qqmljslinter.cpp
+++ b/src/qmlcompiler/qqmljslinter.cpp
@@ -19,6 +19,7 @@
#include <QtCore/qscopedpointer.h>
#include <QtQmlCompiler/private/qqmlsa_p.h>
+#include <QtQmlCompiler/private/qqmljsloggingutils_p.h>
#if QT_CONFIG(library)
# include <QtCore/qdiriterator.h>
@@ -169,7 +170,7 @@ bool QQmlJSLinter::Plugin::parseMetaData(const QJsonObject &metaData, QString pl
return false;
}
- QJsonObject object = value.toObject();
+ const QJsonObject object = value.toObject();
for (const QString &requiredKey : { u"name"_s, u"description"_s }) {
if (!object.contains(requiredKey)) {
@@ -179,10 +180,18 @@ bool QQmlJSLinter::Plugin::parseMetaData(const QJsonObject &metaData, QString pl
}
}
+ const auto it = object.find("enabled"_L1);
+ const bool ignored = (it != object.end() && !it->toBool());
+
const QString categoryId =
(m_isInternal ? u""_s : u"Plugin."_s) + m_name + u'.' + object[u"name"].toString();
- m_categories << QQmlJSLogger::Category { categoryId, categoryId,
- object[u"description"].toString(), QtWarningMsg };
+ const auto settingsNameIt = object.constFind(u"settingsName");
+ const QString settingsName = (settingsNameIt == object.constEnd())
+ ? categoryId
+ : settingsNameIt->toString(categoryId);
+ m_categories << QQmlJS::LoggerCategory{ categoryId, settingsName,
+ object["description"_L1].toString(), QtWarningMsg,
+ ignored };
}
return true;
@@ -190,7 +199,6 @@ bool QQmlJSLinter::Plugin::parseMetaData(const QJsonObject &metaData, QString pl
std::vector<QQmlJSLinter::Plugin> QQmlJSLinter::loadPlugins(QStringList paths)
{
-
std::vector<Plugin> plugins;
QDuplicateTracker<QString> seenPlugins;
@@ -236,7 +244,7 @@ std::vector<QQmlJSLinter::Plugin> QQmlJSLinter::loadPlugins(QStringList paths)
}
}
#endif
-
+ Q_UNUSED(paths)
return plugins;
}
@@ -265,7 +273,7 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger,
const QString category = words.at(i);
const auto categoryExists = std::any_of(
loggerCategories.cbegin(), loggerCategories.cend(),
- [&](const QQmlJSLogger::Category &cat) { return cat.id().name() == category; });
+ [&](const QQmlJS::LoggerCategory &cat) { return cat.id().name() == category; });
if (categoryExists)
categories << category;
@@ -323,7 +331,7 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger,
}
static void addJsonWarning(QJsonArray &warnings, const QQmlJS::DiagnosticMessage &message,
- QAnyStringView id, const std::optional<FixSuggestion> &suggestion = {})
+ QAnyStringView id, const std::optional<QQmlJSFixSuggestion> &suggestion = {})
{
QJsonObject jsonMessage;
@@ -362,19 +370,35 @@ static void addJsonWarning(QJsonArray &warnings, const QQmlJS::DiagnosticMessage
jsonMessage[u"message"_s] = message.message;
QJsonArray suggestions;
+ const auto convertLocation = [](const QQmlJS::SourceLocation &source, QJsonObject *target) {
+ target->insert("line"_L1, int(source.startLine));
+ target->insert("column"_L1, int(source.startColumn));
+ target->insert("charOffset"_L1, int(source.offset));
+ target->insert("length"_L1, int(source.length));
+ };
if (suggestion.has_value()) {
- for (const auto &fix : suggestion->fixes) {
- QJsonObject jsonFix;
- jsonFix[u"message"] = fix.message;
- jsonFix[u"line"_s] = static_cast<int>(fix.cutLocation.startLine);
- jsonFix[u"column"_s] = static_cast<int>(fix.cutLocation.startColumn);
- jsonFix[u"charOffset"_s] = static_cast<int>(fix.cutLocation.offset);
- jsonFix[u"length"_s] = static_cast<int>(fix.cutLocation.length);
- jsonFix[u"replacement"_s] = fix.replacementString;
- jsonFix[u"isHint"] = fix.isHint;
- if (!fix.fileName.isEmpty())
- jsonFix[u"fileName"] = fix.fileName;
- suggestions << jsonFix;
+ QJsonObject jsonFix {
+ { "message"_L1, suggestion->fixDescription() },
+ { "replacement"_L1, suggestion->replacement() },
+ { "isHint"_L1, !suggestion->isAutoApplicable() },
+ };
+ convertLocation(suggestion->location(), &jsonFix);
+ const QString filename = suggestion->filename();
+ if (!filename.isEmpty())
+ jsonFix.insert("fileName"_L1, filename);
+ suggestions << jsonFix;
+
+ const QString hint = suggestion->hint();
+ if (!hint.isEmpty()) {
+ // We need to keep compatibility with the JSON format.
+ // Therefore the overly verbose encoding of the hint.
+ QJsonObject jsonHint {
+ { "message"_L1, hint },
+ { "replacement"_L1, QString() },
+ { "isHint"_L1, true }
+ };
+ convertLocation(QQmlJS::SourceLocation(), &jsonHint);
+ suggestions << jsonHint;
}
}
jsonMessage[u"suggestions"] = suggestions;
@@ -398,7 +422,7 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
QJsonArray *json, const QStringList &qmlImportPaths,
const QStringList &qmldirFiles,
const QStringList &resourceFiles,
- const QList<QQmlJSLogger::Category> &categories)
+ const QList<QQmlJS::LoggerCategory> &categories)
{
// Make sure that we don't expose an old logger if we return before a new one is created.
m_logger.reset();
@@ -495,17 +519,17 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
if (m_enablePlugins) {
for (const Plugin &plugin : m_plugins) {
- for (const QQmlJSLogger::Category &category : plugin.categories())
+ for (const QQmlJS::LoggerCategory &category : plugin.categories())
m_logger->registerCategory(category);
}
}
for (auto it = categories.cbegin(); it != categories.cend(); ++it) {
- if (!it->changed)
+ if (auto logger = *it; !QQmlJS::LoggerCategoryPrivate::get(&logger)->hasChanged())
continue;
- m_logger->setCategoryIgnored(it->id(), it->ignored);
- m_logger->setCategoryLevel(it->id(), it->level);
+ m_logger->setCategoryIgnored(it->id(), it->isIgnored());
+ m_logger->setCategoryLevel(it->id(), it->level());
}
parseComments(m_logger.get(), engine.comments());
@@ -525,25 +549,26 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
typeResolver.init(&v, parser.rootNode());
- QQmlJSLiteralBindingCheck literalCheck;
- literalCheck.run(&v, &typeResolver);
-
- QScopedPointer<QQmlSA::PassManager> passMan;
+ using PassManagerPtr = std::unique_ptr<
+ QQmlSA::PassManager, decltype(&QQmlSA::PassManagerPrivate::deletePassManager)>;
+ PassManagerPtr passMan(QQmlSA::PassManagerPrivate::createPassManager(&v, &typeResolver),
+ &QQmlSA::PassManagerPrivate::deletePassManager);
+ passMan->registerPropertyPass(
+ std::make_unique<QQmlJSLiteralBindingCheck>(passMan.get()), QString(),
+ QString(), QString());
if (m_enablePlugins) {
- passMan.reset(new QQmlSA::PassManager(&v, &typeResolver));
-
for (const Plugin &plugin : m_plugins) {
if (!plugin.isValid() || !plugin.isEnabled())
continue;
QQmlSA::LintPlugin *instance = plugin.m_instance;
Q_ASSERT(instance);
- instance->registerPasses(passMan.get(), v.result());
+ instance->registerPasses(passMan.get(),
+ QQmlJSScope::createQQmlSAElement(v.result()));
}
-
- passMan->analyze(v.result());
}
+ passMan->analyze(QQmlJSScope::createQQmlSAElement(v.result()));
success = !m_logger->hasWarnings() && !m_logger->hasErrors();
@@ -553,16 +578,13 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
return;
}
- QQmlJSTypeInfo typeInfo;
-
const QStringList resourcePaths = mapper
? mapper->resourcePaths(QQmlJSResourceFileMapper::localFileFilter(filename))
: QStringList();
const QString resolvedPath =
(resourcePaths.size() == 1) ? u':' + resourcePaths.first() : filename;
- QQmlJSLinterCodegen codegen { &m_importer, resolvedPath, qmldirFiles, m_logger.get(),
- &typeInfo };
+ QQmlJSLinterCodegen codegen { &m_importer, resolvedPath, qmldirFiles, m_logger.get() };
codegen.setTypeResolver(std::move(typeResolver));
if (passMan)
codegen.setPassManager(passMan.get());
@@ -603,8 +625,9 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
return success ? LintSuccess : HasWarnings;
}
-QQmlJSLinter::LintResult QQmlJSLinter::lintModule(const QString &module, const bool silent,
- QJsonArray *json)
+QQmlJSLinter::LintResult QQmlJSLinter::lintModule(
+ const QString &module, const bool silent, QJsonArray *json,
+ const QStringList &qmlImportPaths, const QStringList &resourceFiles)
{
// Make sure that we don't expose an old logger if we return before a new one is created.
m_logger.reset();
@@ -612,6 +635,15 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintModule(const QString &module, const b
// We can't lint properly if a module has already been pre-cached
m_importer.clearCache();
+ if (m_importer.importPaths() != qmlImportPaths)
+ m_importer.setImportPaths(qmlImportPaths);
+
+ QQmlJSResourceFileMapper mapper(resourceFiles);
+ if (!resourceFiles.isEmpty())
+ m_importer.setResourceFileMapper(&mapper);
+ else
+ m_importer.setResourceFileMapper(nullptr);
+
QJsonArray warnings;
QJsonObject result;
@@ -768,7 +800,7 @@ QQmlJSLinter::FixResult QQmlJSLinter::applyFixes(QString *fixedCode, bool silent
QString code = m_fileContents;
- QList<FixSuggestion::Fix> fixesToApply;
+ QList<QQmlJSFixSuggestion> fixesToApply;
QFileInfo info(m_logger->fileName());
const QString currentFileAbsolutePath = info.absoluteFilePath();
@@ -782,34 +814,33 @@ QQmlJSLinter::FixResult QQmlJSLinter::applyFixes(QString *fixedCode, bool silent
for (const auto &messages : { m_logger->infos(), m_logger->warnings(), m_logger->errors() })
for (const Message &msg : messages) {
- if (!msg.fixSuggestion.has_value())
+ if (!msg.fixSuggestion.has_value() || !msg.fixSuggestion->isAutoApplicable())
continue;
- for (const auto &fix : msg.fixSuggestion->fixes) {
- if (fix.isHint)
- continue;
-
- // Ignore fix suggestions for other files
- if (!fix.fileName.isEmpty()
- && QFileInfo(fix.fileName).absoluteFilePath() != currentFileAbsolutePath) {
- continue;
- }
-
- fixesToApply << fix;
+ // Ignore fix suggestions for other files
+ const QString filename = msg.fixSuggestion->filename();
+ if (!filename.isEmpty()
+ && QFileInfo(filename).absoluteFilePath() != currentFileAbsolutePath) {
+ continue;
}
+
+ fixesToApply << msg.fixSuggestion.value();
}
if (fixesToApply.isEmpty())
return NothingToFix;
std::sort(fixesToApply.begin(), fixesToApply.end(),
- [](FixSuggestion::Fix &a, FixSuggestion::Fix &b) {
- return a.cutLocation.offset < b.cutLocation.offset;
+ [](const QQmlJSFixSuggestion &a, const QQmlJSFixSuggestion &b) {
+ return a.location().offset < b.location().offset;
});
+ const auto dupes = std::unique(fixesToApply.begin(), fixesToApply.end());
+ fixesToApply.erase(dupes, fixesToApply.end());
+
for (auto it = fixesToApply.begin(); it + 1 != fixesToApply.end(); it++) {
- QQmlJS::SourceLocation srcLocA = it->cutLocation;
- QQmlJS::SourceLocation srcLocB = (it + 1)->cutLocation;
+ const QQmlJS::SourceLocation srcLocA = it->location();
+ const QQmlJS::SourceLocation srcLocB = (it + 1)->location();
if (srcLocA.offset + srcLocA.length > srcLocB.offset) {
if (!silent)
qWarning() << "Fixes for two warnings are overlapping, aborting. Please file a bug "
@@ -821,12 +852,14 @@ QQmlJSLinter::FixResult QQmlJSLinter::applyFixes(QString *fixedCode, bool silent
int offsetChange = 0;
for (const auto &fix : fixesToApply) {
- qsizetype cutLocation = fix.cutLocation.offset + offsetChange;
- QString before = code.left(cutLocation);
- QString after = code.mid(cutLocation + fix.cutLocation.length);
-
- code = before + fix.replacementString + after;
- offsetChange += fix.replacementString.size() - fix.cutLocation.length;
+ const QQmlJS::SourceLocation fixLocation = fix.location();
+ qsizetype cutLocation = fixLocation.offset + offsetChange;
+ const QString before = code.left(cutLocation);
+ const QString after = code.mid(cutLocation + fixLocation.length);
+
+ const QString replacement = fix.replacement();
+ code = before + replacement + after;
+ offsetChange += replacement.size() - fixLocation.length;
}
QQmlJS::Engine engine;
diff --git a/src/qmlcompiler/qqmljslinter_p.h b/src/qmlcompiler/qqmljslinter_p.h
index 77a8fe701f..5f367df4c6 100644
--- a/src/qmlcompiler/qqmljslinter_p.h
+++ b/src/qmlcompiler/qqmljslinter_p.h
@@ -14,7 +14,7 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include <QtQmlCompiler/private/qqmljslogger_p.h>
#include <QtQmlCompiler/private/qqmljsimporter_p.h>
@@ -38,7 +38,7 @@ namespace QQmlSA {
class LintPlugin;
}
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSLinter
+class Q_QMLCOMPILER_EXPORT QQmlJSLinter
{
public:
QQmlJSLinter(const QStringList &importPaths,
@@ -48,7 +48,7 @@ public:
enum LintResult { FailedToOpen, FailedToParse, HasWarnings, LintSuccess };
enum FixResult { NothingToFix, FixError, FixSuccess };
- class Q_QMLCOMPILER_PRIVATE_EXPORT Plugin
+ class Q_QMLCOMPILER_EXPORT Plugin
{
Q_DISABLE_COPY(Plugin)
public:
@@ -65,7 +65,7 @@ public:
const QString &description() const { return m_description; }
const QString &version() const { return m_version; }
const QString &author() const { return m_author; }
- const QList<QQmlJSLogger::Category> categories() const
+ const QList<QQmlJS::LoggerCategory> categories() const
{
return m_categories;
}
@@ -95,10 +95,10 @@ public:
QString m_version;
QString m_author;
- QList<QQmlJSLogger::Category> m_categories;
+ QList<QQmlJS::LoggerCategory> m_categories;
QQmlSA::LintPlugin *m_instance;
std::unique_ptr<QPluginLoader> m_loader;
- bool m_isBuiltin;
+ bool m_isBuiltin = false;
bool m_isInternal =
false; // Internal plugins are those developed and maintained inside the Qt project
bool m_isValid = false;
@@ -111,9 +111,10 @@ public:
LintResult lintFile(const QString &filename, const QString *fileContents, const bool silent,
QJsonArray *json, const QStringList &qmlImportPaths,
const QStringList &qmldirFiles, const QStringList &resourceFiles,
- const QList<QQmlJSLogger::Category> &categories);
+ const QList<QQmlJS::LoggerCategory> &categories);
- LintResult lintModule(const QString &uri, const bool silent, QJsonArray *json);
+ LintResult lintModule(const QString &uri, const bool silent, QJsonArray *json,
+ const QStringList &qmlImportPaths, const QStringList &resourceFiles);
FixResult applyFixes(QString *fixedCode, bool silent);
diff --git a/src/qmlcompiler/qqmljslintercodegen.cpp b/src/qmlcompiler/qqmljslintercodegen.cpp
index c7cd5d0d00..175f7ee1e0 100644
--- a/src/qmlcompiler/qqmljslintercodegen.cpp
+++ b/src/qmlcompiler/qqmljslintercodegen.cpp
@@ -16,9 +16,8 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
QQmlJSLinterCodegen::QQmlJSLinterCodegen(QQmlJSImporter *importer, const QString &fileName,
- const QStringList &qmldirFiles, QQmlJSLogger *logger,
- QQmlJSTypeInfo *typeInfo)
- : QQmlJSAotCompiler(importer, fileName, qmldirFiles, logger), m_typeInfo(typeInfo)
+ const QStringList &qmldirFiles, QQmlJSLogger *logger)
+ : QQmlJSAotCompiler(importer, fileName, qmldirFiles, logger)
{
}
@@ -28,7 +27,6 @@ void QQmlJSLinterCodegen::setDocument(const QmlIR::JSCodeGen *codegen,
Q_UNUSED(codegen);
m_document = document;
m_unitGenerator = &document->jsGenerator;
- m_entireSourceCodeLines = document->code.split(u'\n');
}
std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage>
@@ -84,17 +82,19 @@ bool QQmlJSLinterCodegen::analyzeFunction(const QV4::Compiler::Context *context,
QQmlJSCompilePass::Function *function,
QQmlJS::DiagnosticMessage *error)
{
- QQmlJSTypePropagator propagator(m_unitGenerator, &m_typeResolver, m_logger, m_typeInfo,
- m_passManager);
- QQmlJSCompilePass::InstructionAnnotations annotations = propagator.run(function, error);
+ QQmlJSTypePropagator propagator(m_unitGenerator, &m_typeResolver, m_logger,
+ {}, {}, m_passManager);
+ auto [basicBlocks, annotations] = propagator.run(function, error);
if (!error->isValid()) {
- QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger);
- shadowCheck.run(&annotations, function, error);
+ QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger, basicBlocks,
+ annotations);
+ shadowCheck.run(function, error);
}
if (!error->isValid()) {
- QQmlJSStorageGeneralizer generalizer(m_unitGenerator, &m_typeResolver, m_logger);
- generalizer.run(annotations, function, error);
+ QQmlJSStorageGeneralizer generalizer(m_unitGenerator, &m_typeResolver, m_logger,
+ basicBlocks, annotations);
+ generalizer.run(function, error);
}
if (error->isValid()) {
diff --git a/src/qmlcompiler/qqmljslintercodegen_p.h b/src/qmlcompiler/qqmljslintercodegen_p.h
index 3a095f0091..5cddbea704 100644
--- a/src/qmlcompiler/qqmljslintercodegen_p.h
+++ b/src/qmlcompiler/qqmljslintercodegen_p.h
@@ -39,8 +39,7 @@ class QQmlJSLinterCodegen : public QQmlJSAotCompiler
{
public:
QQmlJSLinterCodegen(QQmlJSImporter *importer, const QString &fileName,
- const QStringList &qmldirFiles, QQmlJSLogger *logger,
- QQmlJSTypeInfo *typeInfo);
+ const QStringList &qmldirFiles, QQmlJSLogger *logger);
void setDocument(const QmlIR::JSCodeGen *codegen, const QmlIR::Document *document) override;
std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage>
@@ -62,7 +61,6 @@ public:
QQmlSA::PassManager *passManager() { return m_passManager; }
private:
- QQmlJSTypeInfo *m_typeInfo;
QQmlSA::PassManager *m_passManager = nullptr;
bool analyzeFunction(const QV4::Compiler::Context *context,
diff --git a/src/qmlcompiler/qqmljsliteralbindingcheck.cpp b/src/qmlcompiler/qqmljsliteralbindingcheck.cpp
index a2d5222b2b..af1fb9104f 100644
--- a/src/qmlcompiler/qqmljsliteralbindingcheck.cpp
+++ b/src/qmlcompiler/qqmljsliteralbindingcheck.cpp
@@ -6,15 +6,22 @@
#include <private/qqmljsimportvisitor_p.h>
#include <private/qqmljstyperesolver_p.h>
#include <private/qqmljsmetatypes_p.h>
+#include <private/qqmlsa_p.h>
+#include <private/qqmlsasourcelocation_p.h>
+#include <private/qqmlstringconverters_p.h>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
// This makes no sense, but we want to warn about things QQmlPropertyResolver complains about.
-static bool canConvertForLiteralBinding(
- QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &from,
- const QQmlJSScope::ConstPtr &to) {
+static bool canConvertForLiteralBinding(QQmlJSTypeResolver *resolver,
+ const QQmlSA::Element &fromElement,
+ const QQmlSA::Element &toElement)
+{
+ Q_ASSERT(resolver);
+ auto from = QQmlJSScope::scope(fromElement);
+ auto to = QQmlJSScope::scope(toElement);
if (resolver->equals(from, to))
return true;
@@ -39,39 +46,113 @@ static bool canConvertForLiteralBinding(
return true;
}
-void QQmlJSLiteralBindingCheck::run(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver)
+QQmlJSLiteralBindingCheck::QQmlJSLiteralBindingCheck(QQmlSA::PassManager *passManager)
+ : LiteralBindingCheckBase(passManager),
+ m_resolver(QQmlSA::PassManagerPrivate::resolver(*passManager))
{
- QQmlJSLogger *logger = visitor->logger();
- const auto literalScopes = visitor->literalScopesToCheck();
- for (const auto &scope : literalScopes) {
- const auto bindings = scope->ownPropertyBindings();
- for (const auto &binding : bindings) {
- if (!binding.hasLiteral())
- continue;
-
- const QString propertyName = binding.propertyName();
- const QQmlJSMetaProperty property = scope->property(propertyName);
- if (!property.isValid())
- continue;
-
- // If the property is defined in the same scope where it is set,
- // we are in fact allowed to set it, even if it's not writable.
- if (!property.isWritable() && !scope->hasOwnProperty(propertyName)) {
- logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
- qmlReadOnlyProperty, binding.sourceLocation());
- continue;
- }
+}
+
+QQmlJSStructuredTypeError QQmlJSLiteralBindingCheck::check(const QString &typeName,
+ const QString &value) const
+{
+ return QQmlJSValueTypeFromStringCheck::hasError(typeName, value);
+}
+
+static QString literalPrettyTypeName(QQmlSA::BindingType type)
+{
+ switch (type) {
+ case QQmlSA::BindingType::BoolLiteral:
+ return u"bool"_s;
+ case QQmlSA::BindingType::NumberLiteral:
+ return u"double"_s;
+ case QQmlSA::BindingType::StringLiteral:
+ return u"string"_s;
+ case QQmlSA::BindingType::RegExpLiteral:
+ return u"regexp"_s;
+ case QQmlSA::BindingType::Null:
+ return u"null"_s;
+ default:
+ return QString();
+ }
+ Q_UNREACHABLE_RETURN(QString());
+}
+
+QQmlSA::Property LiteralBindingCheckBase::getProperty(const QString &propertyName,
+ const QQmlSA::Binding &binding,
+ const QQmlSA::Element &bindingScope) const
+{
+ if (!QQmlSA::Binding::isLiteralBinding(binding.bindingType()))
+ return {};
- if (!canConvertForLiteralBinding(
- resolver, binding.literalType(resolver), property.type())) {
- logger->log(u"Cannot assign literal of type %1 to %2"_s.arg(
- QQmlJSScope::prettyName(binding.literalTypeName()),
- QQmlJSScope::prettyName(property.typeName())),
- qmlIncompatibleType, binding.sourceLocation());
- continue;
+ const QString unqualifiedPropertyName = [&propertyName]() -> QString {
+ if (auto idx = propertyName.lastIndexOf(u'.'); idx != -1 && idx != propertyName.size() - 1)
+ return propertyName.sliced(idx + 1);
+ return propertyName;
+ }();
+
+ return bindingScope.property(unqualifiedPropertyName);
+}
+
+
+void LiteralBindingCheckBase::onBinding(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Binding &binding,
+ const QQmlSA::Element &bindingScope,
+ const QQmlSA::Element &value)
+{
+ Q_UNUSED(value);
+
+ const auto property = getProperty(propertyName, binding, bindingScope);
+ if (!property.isValid())
+ return;
+
+ // If the property is defined in the same scope where it is set,
+ // we are in fact allowed to set it, even if it's not writable.
+ if (property.isReadonly() && !element.hasOwnProperty(propertyName)) {
+ emitWarning(u"Cannot assign to read-only property %1"_s.arg(propertyName),
+ qmlReadOnlyProperty, binding.sourceLocation());
+ return;
+ }
+ if (auto propertyType = property.type(); propertyType) {
+ auto construction = check(propertyType.internalId(), binding.stringValue());
+ if (construction.isValid()) {
+ const QString warningMessage =
+ u"Binding is not supported: Type %1 should be constructed using"
+ u" QML_STRUCTURED_VALUE's construction mechanism, instead of a "
+ u"string."_s.arg(propertyType.internalId());
+
+ if (!construction.code.isNull()) {
+ QQmlSA::FixSuggestion suggestion(
+ u"Replace string by structured value construction"_s,
+ binding.sourceLocation(), construction.code);
+ emitWarning(warningMessage, qmlIncompatibleType, binding.sourceLocation(), suggestion);
+ return;
}
+ emitWarning(warningMessage, qmlIncompatibleType, binding.sourceLocation());
+ return;
}
}
+
+}
+
+void QQmlJSLiteralBindingCheck::onBinding(const QQmlSA::Element &element,
+ const QString &propertyName,
+ const QQmlSA::Binding &binding,
+ const QQmlSA::Element &bindingScope,
+ const QQmlSA::Element &value)
+{
+ LiteralBindingCheckBase::onBinding(element, propertyName, binding, bindingScope, value);
+
+ const auto property = getProperty(propertyName, binding, bindingScope);
+ if (!property.isValid())
+ return;
+
+ if (!canConvertForLiteralBinding(m_resolver, resolveLiteralType(binding), property.type())) {
+ emitWarning(u"Cannot assign literal of type %1 to %2"_s.arg(
+ literalPrettyTypeName(binding.bindingType()),
+ QQmlJSScope::prettyName(property.typeName())),
+ qmlIncompatibleType, binding.sourceLocation());
+ return;
+ }
}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsliteralbindingcheck_p.h b/src/qmlcompiler/qqmljsliteralbindingcheck_p.h
index 92acd0555c..ca5c65e9b4 100644
--- a/src/qmlcompiler/qqmljsliteralbindingcheck_p.h
+++ b/src/qmlcompiler/qqmljsliteralbindingcheck_p.h
@@ -15,17 +15,46 @@
// We mean it.
#include <QtCore/qglobal.h>
-#include <private/qtqmlcompilerexports_p.h>
+#include <QtQmlCompiler/qqmlsa.h>
+
+#include <qtqmlcompilerexports.h>
+#include "qqmljsvaluetypefromstringcheck_p.h"
QT_BEGIN_NAMESPACE
class QQmlJSImportVisitor;
class QQmlJSTypeResolver;
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSLiteralBindingCheck
+class Q_QMLCOMPILER_EXPORT LiteralBindingCheckBase : public QQmlSA::PropertyPass
{
public:
- void run(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver);
+ using QQmlSA::PropertyPass::PropertyPass;
+
+ void onBinding(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope,
+ const QQmlSA::Element &value) override;
+
+protected:
+ virtual QQmlJSStructuredTypeError check(const QString &typeName, const QString &value) const = 0;
+
+ QQmlSA::Property getProperty(const QString &propertyName, const QQmlSA::Binding &binding,
+ const QQmlSA::Element &bindingScope) const;
+};
+
+class Q_QMLCOMPILER_EXPORT QQmlJSLiteralBindingCheck: public LiteralBindingCheckBase
+{
+public:
+ QQmlJSLiteralBindingCheck(QQmlSA::PassManager *manager);
+
+ void onBinding(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope,
+ const QQmlSA::Element &value) override;
+
+private:
+ QQmlJSTypeResolver *m_resolver;
+
+protected:
+ QQmlJSStructuredTypeError check(const QString &typeName, const QString &value) const override;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsloadergenerator.cpp b/src/qmlcompiler/qqmljsloadergenerator.cpp
index 6e9fabbf60..494ecdadd0 100644
--- a/src/qmlcompiler/qqmljsloadergenerator.cpp
+++ b/src/qmlcompiler/qqmljsloadergenerator.cpp
@@ -110,7 +110,7 @@ bool qQmlJSGenerateLoader(const QStringList &compiledFiles, const QString &outpu
const QString ns = qQmlJSSymbolNamespaceForPath(compiledFile);
stream << "namespace " << ns << " { \n";
stream << " extern const unsigned char qmlData[];\n";
- stream << " extern const QQmlPrivate::TypedFunction aotBuiltFunctions[];\n";
+ stream << " extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n";
stream << " const QQmlPrivate::CachedQmlUnit unit = {\n";
stream << " reinterpret_cast<const QV4::CompiledData::Unit*>(&qmlData), &aotBuiltFunctions[0], nullptr\n";
stream << " };\n";
diff --git a/src/qmlcompiler/qqmljsloadergenerator_p.h b/src/qmlcompiler/qqmljsloadergenerator_p.h
index fc207f075b..f76fdef17b 100644
--- a/src/qmlcompiler/qqmljsloadergenerator_p.h
+++ b/src/qmlcompiler/qqmljsloadergenerator_p.h
@@ -14,7 +14,7 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
@@ -22,11 +22,11 @@
QT_BEGIN_NAMESPACE
-bool Q_QMLCOMPILER_PRIVATE_EXPORT qQmlJSGenerateLoader(const QStringList &compiledFiles,
+bool Q_QMLCOMPILER_EXPORT qQmlJSGenerateLoader(const QStringList &compiledFiles,
const QString &outputFileName,
const QStringList &resourceFileMappings,
QString *errorString);
-QString Q_QMLCOMPILER_PRIVATE_EXPORT qQmlJSSymbolNamespaceForPath(const QString &relativePath);
+QString Q_QMLCOMPILER_EXPORT qQmlJSSymbolNamespaceForPath(const QString &relativePath);
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp
index 293069a2b6..7bf686018d 100644
--- a/src/qmlcompiler/qqmljslogger.cpp
+++ b/src/qmlcompiler/qqmljslogger.cpp
@@ -1,210 +1,225 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <qglobal.h>
-
+#include <QtCore/qcompilerdetection.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>
+#include <QtCore/qlist.h>
QT_WARNING_POP
-#include "qqmljslogger_p.h"
+#include <private/qqmljslogger_p.h>
+#include <private/qqmlsa_p.h>
+
+#include <QtQmlCompiler/qqmljsloggingutils.h>
+#include <QtCore/qglobal.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-const LoggerWarningId qmlRequired { "required" };
-const LoggerWarningId qmlAliasCycle { "alias-cycle" };
-const LoggerWarningId qmlUnresolvedAlias { "unresolved-alias" };
-const LoggerWarningId qmlImport { "import" };
-const LoggerWarningId qmlRecursionDepthErrors { "recursion-depth-errors" };
-const LoggerWarningId qmlWith { "with" };
-const LoggerWarningId qmlInheritanceCycle { "inheritance-cycle" };
-const LoggerWarningId qmlDeprecated { "deprecated" };
-const LoggerWarningId qmlSignalParameters { "signal-handler-parameters" };
-const LoggerWarningId qmlMissingType { "missing-type" };
-const LoggerWarningId qmlUnresolvedType { "unresolved-type" };
-const LoggerWarningId qmlRestrictedType { "restricted-type" };
-const LoggerWarningId qmlPrefixedImportType { "prefixed-import-type" };
-const LoggerWarningId qmlIncompatibleType { "incompatible-type" };
-const LoggerWarningId qmlMissingProperty { "missing-property" };
-const LoggerWarningId qmlNonListProperty { "non-list-property" };
-const LoggerWarningId qmlReadOnlyProperty { "read-only-property" };
-const LoggerWarningId qmlDuplicatePropertyBinding { "duplicate-property-binding" };
-const LoggerWarningId qmlDuplicatedName { "duplicated-name" };
-const LoggerWarningId qmlDeferredPropertyId { "deferred-property-id" };
-const LoggerWarningId qmlUnqualified { "unqualified" };
-const LoggerWarningId qmlUnusedImports { "unused-imports" };
-const LoggerWarningId qmlMultilineStrings { "multiline-strings" };
-const LoggerWarningId qmlSyntax { "syntax" };
-const LoggerWarningId qmlSyntaxIdQuotation { "syntax.id-quotation" };
-const LoggerWarningId qmlSyntaxDuplicateIds { "syntax.duplicate-ids" };
-const LoggerWarningId qmlCompiler { "compiler" };
-const LoggerWarningId qmlAttachedPropertyReuse { "attached-property-reuse" };
-const LoggerWarningId qmlPlugin { "plugin" };
-const LoggerWarningId qmlVarUsedBeforeDeclaration { "var-used-before-declaration" };
-const LoggerWarningId qmlInvalidLintDirective { "invalid-lint-directive" };
-const LoggerWarningId qmlUseProperFunction { "use-proper-function" };
-const LoggerWarningId qmlAccessSingleton { "access-singleton-via-object" };
-const LoggerWarningId qmlTopLevelComponent { "top-level-component" };
-const LoggerWarningId qmlUncreatableType { "uncreatable-type" };
-
-
-const QList<QQmlJSLogger::Category> &QQmlJSLogger::defaultCategories()
+const QQmlSA::LoggerWarningId qmlRequired{ "required" };
+const QQmlSA::LoggerWarningId qmlAliasCycle{ "alias-cycle" };
+const QQmlSA::LoggerWarningId qmlUnresolvedAlias{ "unresolved-alias" };
+const QQmlSA::LoggerWarningId qmlImport{ "import" };
+const QQmlSA::LoggerWarningId qmlRecursionDepthErrors{ "recursion-depth-errors" };
+const QQmlSA::LoggerWarningId qmlWith{ "with" };
+const QQmlSA::LoggerWarningId qmlInheritanceCycle{ "inheritance-cycle" };
+const QQmlSA::LoggerWarningId qmlDeprecated{ "deprecated" };
+const QQmlSA::LoggerWarningId qmlSignalParameters{ "signal-handler-parameters" };
+const QQmlSA::LoggerWarningId qmlMissingType{ "missing-type" };
+const QQmlSA::LoggerWarningId qmlUnresolvedType{ "unresolved-type" };
+const QQmlSA::LoggerWarningId qmlRestrictedType{ "restricted-type" };
+const QQmlSA::LoggerWarningId qmlPrefixedImportType{ "prefixed-import-type" };
+const QQmlSA::LoggerWarningId qmlIncompatibleType{ "incompatible-type" };
+const QQmlSA::LoggerWarningId qmlMissingProperty{ "missing-property" };
+const QQmlSA::LoggerWarningId qmlNonListProperty{ "non-list-property" };
+const QQmlSA::LoggerWarningId qmlReadOnlyProperty{ "read-only-property" };
+const QQmlSA::LoggerWarningId qmlDuplicatePropertyBinding{ "duplicate-property-binding" };
+const QQmlSA::LoggerWarningId qmlDuplicatedName{ "duplicated-name" };
+const QQmlSA::LoggerWarningId qmlDeferredPropertyId{ "deferred-property-id" };
+const QQmlSA::LoggerWarningId qmlUnqualified{ "unqualified" };
+const QQmlSA::LoggerWarningId qmlUnusedImports{ "unused-imports" };
+const QQmlSA::LoggerWarningId qmlMultilineStrings{ "multiline-strings" };
+const QQmlSA::LoggerWarningId qmlSyntax{ "syntax" };
+const QQmlSA::LoggerWarningId qmlSyntaxIdQuotation{ "syntax.id-quotation" };
+const QQmlSA::LoggerWarningId qmlSyntaxDuplicateIds{ "syntax.duplicate-ids" };
+const QQmlSA::LoggerWarningId qmlCompiler{ "compiler" };
+const QQmlSA::LoggerWarningId qmlAttachedPropertyReuse{ "attached-property-reuse" };
+const QQmlSA::LoggerWarningId qmlPlugin{ "plugin" };
+const QQmlSA::LoggerWarningId qmlVarUsedBeforeDeclaration{ "var-used-before-declaration" };
+const QQmlSA::LoggerWarningId qmlInvalidLintDirective{ "invalid-lint-directive" };
+const QQmlSA::LoggerWarningId qmlUseProperFunction{ "use-proper-function" };
+const QQmlSA::LoggerWarningId qmlAccessSingleton{ "access-singleton-via-object" };
+const QQmlSA::LoggerWarningId qmlTopLevelComponent{ "top-level-component" };
+const QQmlSA::LoggerWarningId qmlUncreatableType{ "uncreatable-type" };
+const QQmlSA::LoggerWarningId qmlMissingEnumEntry{ "missing-enum-entry" };
+
+QQmlJSLogger::QQmlJSLogger()
{
- static const QList<QQmlJSLogger::Category> cats = {
- QQmlJSLogger::Category { qmlRequired.name().toString(), QStringLiteral("RequiredProperty"),
- QStringLiteral("Warn about required properties"), QtWarningMsg },
- QQmlJSLogger::Category { qmlAliasCycle.name().toString(),
- QStringLiteral("PropertyAliasCycles"),
- QStringLiteral("Warn about alias cycles"), QtWarningMsg },
- QQmlJSLogger::Category { qmlUnresolvedAlias.name().toString(),
- QStringLiteral("PropertyAliasCycles"),
- QStringLiteral("Warn about unresolved aliases"), QtWarningMsg },
- QQmlJSLogger::Category {
+ static const QList<QQmlJS::LoggerCategory> cats = defaultCategories();
+
+ for (const QQmlJS::LoggerCategory &category : cats)
+ registerCategory(category);
+
+ // setup color output
+ m_output.insertMapping(QtCriticalMsg, QColorOutput::RedForeground);
+ m_output.insertMapping(QtWarningMsg, QColorOutput::PurpleForeground); // Yellow?
+ m_output.insertMapping(QtInfoMsg, QColorOutput::BlueForeground);
+ m_output.insertMapping(QtDebugMsg, QColorOutput::GreenForeground); // None?
+}
+
+const QList<QQmlJS::LoggerCategory> &QQmlJSLogger::defaultCategories()
+{
+ static const QList<QQmlJS::LoggerCategory> cats = {
+ QQmlJS::LoggerCategory{ qmlRequired.name().toString(), QStringLiteral("RequiredProperty"),
+ QStringLiteral("Warn about required properties"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlAliasCycle.name().toString(),
+ QStringLiteral("PropertyAliasCycles"),
+ QStringLiteral("Warn about alias cycles"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlUnresolvedAlias.name().toString(),
+ QStringLiteral("PropertyAliasCycles"),
+ QStringLiteral("Warn about unresolved aliases"), QtWarningMsg },
+ QQmlJS::LoggerCategory{
qmlImport.name().toString(), QStringLiteral("ImportFailure"),
QStringLiteral("Warn about failing imports and deprecated qmltypes"),
QtWarningMsg },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{
qmlRecursionDepthErrors.name().toString(), QStringLiteral("ImportFailure"),
QStringLiteral("Warn about failing imports and deprecated qmltypes"), QtWarningMsg,
false, true },
- QQmlJSLogger::Category {
- qmlWith.name().toString(), QStringLiteral("WithStatement"),
- QStringLiteral("Warn about with statements as they can cause false "
- "positives when checking for unqualified access"),
- QtWarningMsg },
- QQmlJSLogger::Category { qmlInheritanceCycle.name().toString(),
- QStringLiteral("InheritanceCycle"),
- QStringLiteral("Warn about inheritance cycles"), QtWarningMsg },
- QQmlJSLogger::Category { qmlDeprecated.name().toString(), QStringLiteral("Deprecated"),
- QStringLiteral("Warn about deprecated properties and types"),
- QtWarningMsg },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{ qmlWith.name().toString(), QStringLiteral("WithStatement"),
+ QStringLiteral("Warn about with statements as they can cause false "
+ "positives when checking for unqualified access"),
+ QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlInheritanceCycle.name().toString(),
+ QStringLiteral("InheritanceCycle"),
+ QStringLiteral("Warn about inheritance cycles"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlDeprecated.name().toString(), QStringLiteral("Deprecated"),
+ QStringLiteral("Warn about deprecated properties and types"),
+ QtWarningMsg },
+ QQmlJS::LoggerCategory{
qmlSignalParameters.name().toString(), QStringLiteral("BadSignalHandlerParameters"),
QStringLiteral("Warn about bad signal handler parameters"), QtWarningMsg },
- QQmlJSLogger::Category { qmlMissingType.name().toString(), QStringLiteral("MissingType"),
- QStringLiteral("Warn about missing types"), QtWarningMsg },
- QQmlJSLogger::Category { qmlUnresolvedType.name().toString(),
- QStringLiteral("UnresolvedType"),
- QStringLiteral("Warn about unresolved types"), QtWarningMsg },
- QQmlJSLogger::Category { qmlRestrictedType.name().toString(),
- QStringLiteral("RestrictedType"),
- QStringLiteral("Warn about restricted types"), QtWarningMsg },
- QQmlJSLogger::Category { qmlPrefixedImportType.name().toString(),
- QStringLiteral("PrefixedImportType"),
- QStringLiteral("Warn about prefixed import types"), QtWarningMsg },
- QQmlJSLogger::Category { qmlIncompatibleType.name().toString(),
- QStringLiteral("IncompatibleType"),
- QStringLiteral("Warn about missing types"), QtWarningMsg },
- QQmlJSLogger::Category { qmlMissingProperty.name().toString(),
- QStringLiteral("MissingProperty"),
- QStringLiteral("Warn about missing properties"), QtWarningMsg },
- QQmlJSLogger::Category { qmlNonListProperty.name().toString(),
- QStringLiteral("NonListProperty"),
- QStringLiteral("Warn about non-list properties"), QtWarningMsg },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{ qmlMissingType.name().toString(), QStringLiteral("MissingType"),
+ QStringLiteral("Warn about missing types"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlUnresolvedType.name().toString(),
+ QStringLiteral("UnresolvedType"),
+ QStringLiteral("Warn about unresolved types"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlRestrictedType.name().toString(),
+ QStringLiteral("RestrictedType"),
+ QStringLiteral("Warn about restricted types"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlPrefixedImportType.name().toString(),
+ QStringLiteral("PrefixedImportType"),
+ QStringLiteral("Warn about prefixed import types"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlIncompatibleType.name().toString(),
+ QStringLiteral("IncompatibleType"),
+ QStringLiteral("Warn about missing types"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlMissingProperty.name().toString(),
+ QStringLiteral("MissingProperty"),
+ QStringLiteral("Warn about missing properties"), QtWarningMsg },
+ QQmlJS::LoggerCategory{ qmlNonListProperty.name().toString(),
+ QStringLiteral("NonListProperty"),
+ QStringLiteral("Warn about non-list properties"), QtWarningMsg },
+ QQmlJS::LoggerCategory{
qmlReadOnlyProperty.name().toString(), QStringLiteral("ReadOnlyProperty"),
QStringLiteral("Warn about writing to read-only properties"), QtWarningMsg },
- QQmlJSLogger::Category { qmlDuplicatePropertyBinding.name().toString(),
- QStringLiteral("DuplicatePropertyBinding"),
- QStringLiteral("Warn about duplicate property bindings"),
- QtWarningMsg },
- QQmlJSLogger::Category { qmlDuplicatedName.name().toString(),
- QStringLiteral("DuplicatedName"),
- QStringLiteral("Warn about duplicated property/signal names"),
- QtWarningMsg },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{ qmlDuplicatePropertyBinding.name().toString(),
+ QStringLiteral("DuplicatePropertyBinding"),
+ QStringLiteral("Warn about duplicate property bindings"),
+ QtWarningMsg },
+ QQmlJS::LoggerCategory{
+ qmlDuplicatedName.name().toString(), QStringLiteral("DuplicatedName"),
+ QStringLiteral("Warn about duplicated property/signal names"), QtWarningMsg },
+ QQmlJS::LoggerCategory{
qmlDeferredPropertyId.name().toString(), QStringLiteral("DeferredPropertyId"),
QStringLiteral(
"Warn about making deferred properties immediate by giving them an id."),
QtInfoMsg, true, true },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{
qmlUnqualified.name().toString(), QStringLiteral("UnqualifiedAccess"),
QStringLiteral("Warn about unqualified identifiers and how to fix them"),
QtWarningMsg },
- QQmlJSLogger::Category { qmlUnusedImports.name().toString(),
- QStringLiteral("UnusedImports"),
- QStringLiteral("Warn about unused imports"), QtInfoMsg },
- QQmlJSLogger::Category { qmlMultilineStrings.name().toString(),
- QStringLiteral("MultilineStrings"),
- QStringLiteral("Warn about multiline strings"), QtInfoMsg },
- QQmlJSLogger::Category { qmlSyntax.name().toString(), QString(),
- QStringLiteral("Syntax errors"), QtWarningMsg, false, true },
- QQmlJSLogger::Category { qmlSyntaxIdQuotation.name().toString(), QString(),
- QStringLiteral("ID quotation"), QtWarningMsg, false, true },
- QQmlJSLogger::Category { qmlSyntaxDuplicateIds.name().toString(), QString(),
- QStringLiteral("ID duplication"), QtCriticalMsg, false, true },
- QQmlJSLogger::Category { qmlCompiler.name().toString(), QStringLiteral("CompilerWarnings"),
- QStringLiteral("Warn about compiler issues"), QtWarningMsg, true },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{ qmlUnusedImports.name().toString(), QStringLiteral("UnusedImports"),
+ QStringLiteral("Warn about unused imports"), QtInfoMsg },
+ QQmlJS::LoggerCategory{ qmlMultilineStrings.name().toString(),
+ QStringLiteral("MultilineStrings"),
+ QStringLiteral("Warn about multiline strings"), QtInfoMsg },
+ QQmlJS::LoggerCategory{ qmlSyntax.name().toString(), QString(),
+ QStringLiteral("Syntax errors"), QtWarningMsg, false, true },
+ QQmlJS::LoggerCategory{ qmlSyntaxIdQuotation.name().toString(), QString(),
+ QStringLiteral("ID quotation"), QtWarningMsg, false, true },
+ QQmlJS::LoggerCategory{ qmlSyntaxDuplicateIds.name().toString(), QString(),
+ QStringLiteral("ID duplication"), QtCriticalMsg, false, true },
+ QQmlJS::LoggerCategory{ qmlCompiler.name().toString(), QStringLiteral("CompilerWarnings"),
+ QStringLiteral("Warn about compiler issues"), QtWarningMsg, true },
+ QQmlJS::LoggerCategory{
qmlAttachedPropertyReuse.name().toString(), QStringLiteral("AttachedPropertyReuse"),
- QStringLiteral("Warn if attached types from parent components aren't reused"),
+ QStringLiteral("Warn if attached types from parent components "
+ "aren't reused. This is handled by the QtQuick "
+ "lint plugin. Use Quick.AttachedPropertyReuse instead."),
QtCriticalMsg, true },
- QQmlJSLogger::Category { qmlPlugin.name().toString(), QStringLiteral("LintPluginWarnings"),
- QStringLiteral("Warn if a qmllint plugin finds an issue"),
- QtWarningMsg, true },
- QQmlJSLogger::Category { qmlVarUsedBeforeDeclaration.name().toString(),
- QStringLiteral("VarUsedBeforeDeclaration"),
- QStringLiteral("Warn if a variable is used before declaration"),
- QtWarningMsg },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{ qmlPlugin.name().toString(), QStringLiteral("LintPluginWarnings"),
+ QStringLiteral("Warn if a qmllint plugin finds an issue"),
+ QtWarningMsg, true },
+ QQmlJS::LoggerCategory{ qmlVarUsedBeforeDeclaration.name().toString(),
+ QStringLiteral("VarUsedBeforeDeclaration"),
+ QStringLiteral("Warn if a variable is used before declaration"),
+ QtWarningMsg },
+ QQmlJS::LoggerCategory{
qmlInvalidLintDirective.name().toString(), QStringLiteral("InvalidLintDirective"),
QStringLiteral("Warn if an invalid qmllint comment is found"), QtWarningMsg },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{
qmlUseProperFunction.name().toString(), QStringLiteral("UseProperFunction"),
QStringLiteral("Warn if var is used for storing functions"), QtWarningMsg },
- QQmlJSLogger::Category {
+ QQmlJS::LoggerCategory{
qmlAccessSingleton.name().toString(), QStringLiteral("AccessSingletonViaObject"),
QStringLiteral("Warn if a singleton is accessed via an object"), QtWarningMsg },
- QQmlJSLogger::Category {
- qmlTopLevelComponent.name().toString(), QStringLiteral("TopLevelComponent"),
- QStringLiteral("Fail when a top level Component are encountered"), QtWarningMsg },
- QQmlJSLogger::Category {
- qmlUncreatableType.name().toString(), QStringLiteral("UncreatableType"),
- QStringLiteral("Warn if uncreatable types are created"), QtWarningMsg }
+ QQmlJS::LoggerCategory{
+ qmlTopLevelComponent.name().toString(), QStringLiteral("TopLevelComponent"),
+ QStringLiteral("Fail when a top level Component are encountered"), QtWarningMsg },
+ QQmlJS::LoggerCategory{
+ qmlUncreatableType.name().toString(), QStringLiteral("UncreatableType"),
+ QStringLiteral("Warn if uncreatable types are created"), QtWarningMsg }
};
return cats;
}
-const QList<QQmlJSLogger::Category> QQmlJSLogger::categories() const
+bool QQmlJSFixSuggestion::operator==(const QQmlJSFixSuggestion &other) const
{
- return m_categories.values();
+ return m_location == other.m_location && m_fixDescription == other.m_fixDescription
+ && m_replacement == other.m_replacement && m_filename == other.m_filename
+ && m_hint == other.m_hint && m_autoApplicable == other.m_autoApplicable;
}
-void QQmlJSLogger::registerCategory(const QQmlJSLogger::Category &category)
+bool QQmlJSFixSuggestion::operator!=(const QQmlJSFixSuggestion &other) const
{
- if (m_categories.contains(category.name)) {
- qWarning() << "Trying to re-register existing logger category" << category.name;
- return;
- }
-
- m_categoryLevels[category.name] = category.level;
- m_categoryIgnored[category.name] = category.ignored;
- m_categories.insert(category.name, category);
+ return !(*this == other);
}
-QQmlJSLogger::QQmlJSLogger()
+QList<QQmlJS::LoggerCategory> QQmlJSLogger::categories() const
{
- static const QList<QQmlJSLogger::Category> cats = defaultCategories();
+ return m_categories.values();
+}
- for (const QQmlJSLogger::Category &category : cats)
- registerCategory(category);
+void QQmlJSLogger::registerCategory(const QQmlJS::LoggerCategory &category)
+{
+ if (m_categories.contains(category.name())) {
+ qWarning() << "Trying to re-register existing logger category" << category.name();
+ return;
+ }
- // setup color output
- m_output.insertMapping(QtCriticalMsg, QColorOutput::RedForeground);
- m_output.insertMapping(QtWarningMsg, QColorOutput::PurpleForeground); // Yellow?
- m_output.insertMapping(QtInfoMsg, QColorOutput::BlueForeground);
- m_output.insertMapping(QtDebugMsg, QColorOutput::GreenForeground); // None?
+ m_categoryLevels[category.name()] = category.level();
+ m_categoryIgnored[category.name()] = category.isIgnored();
+ m_categories.insert(category.name(), category);
}
static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
@@ -217,9 +232,9 @@ static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
return level[a] < level[b];
}
-void QQmlJSLogger::log(const QString &message, LoggerWarningId id,
+void QQmlJSLogger::log(const QString &message, QQmlJS::LoggerWarningId id,
const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
- bool showFileName, const std::optional<FixSuggestion> &suggestion,
+ bool showFileName, const std::optional<QQmlJSFixSuggestion> &suggestion,
const QString overrideFileName)
{
Q_ASSERT(m_categoryLevels.contains(id.name().toString()));
@@ -275,7 +290,7 @@ void QQmlJSLogger::log(const QString &message, LoggerWarningId id,
}
void QQmlJSLogger::processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
- LoggerWarningId id)
+ QQmlJS::LoggerWarningId id)
{
if (messages.isEmpty() || isCategoryIgnored(id))
return;
@@ -324,59 +339,65 @@ void QQmlJSLogger::printContext(const QString &overrideFileName,
+ QString::fromLatin1("^").repeated(locationLength) + QLatin1Char('\n'));
}
-void QQmlJSLogger::printFix(const FixSuggestion &fix)
+void QQmlJSLogger::printFix(const QQmlJSFixSuggestion &fixItem)
{
const QString currentFileAbsPath = QFileInfo(m_fileName).absolutePath();
QString code = m_code;
QString currentFile;
- for (const auto &fixItem : fix.fixes) {
- m_output.writePrefixedMessage(fixItem.message, QtInfoMsg);
-
- if (!fixItem.cutLocation.isValid())
- continue;
-
- if (fixItem.fileName == currentFile) {
- // Nothing to do in this case, we've already read the code
- } else if (fixItem.fileName.isEmpty() || fixItem.fileName == currentFileAbsPath) {
- code = m_code;
- } else {
- QFile file(fixItem.fileName);
- const bool success = file.open(QFile::ReadOnly);
- Q_ASSERT(success);
- code = QString::fromUtf8(file.readAll());
- currentFile = fixItem.fileName;
- }
-
- IssueLocationWithContext issueLocationWithContext { code, fixItem.cutLocation };
-
- if (const QStringView beforeText = issueLocationWithContext.beforeText();
- !beforeText.isEmpty()) {
- m_output.write(beforeText);
- }
-
- // The replacement string can be empty if we're only pointing something out to the user
- QStringView replacementString = fixItem.replacementString.isEmpty()
- ? issueLocationWithContext.issueText()
- : fixItem.replacementString;
-
- // But if there's nothing to change it has to be a hint
- if (fixItem.replacementString.isEmpty())
- Q_ASSERT(fixItem.isHint);
-
- m_output.write(replacementString, QtDebugMsg);
- m_output.write(issueLocationWithContext.afterText().toString() + u'\n');
-
- int tabCount = issueLocationWithContext.beforeText().count(u'\t');
-
- // Do not draw location indicator for multiline replacement strings
- if (replacementString.contains(u'\n'))
- continue;
-
- m_output.write(u" "_s.repeated(
- issueLocationWithContext.beforeText().size() - tabCount)
- + u"\t"_s.repeated(tabCount)
- + u"^"_s.repeated(fixItem.replacementString.size()) + u'\n');
+ m_output.writePrefixedMessage(fixItem.fixDescription(), QtInfoMsg);
+
+ if (!fixItem.location().isValid())
+ return;
+
+ const QString filename = fixItem.filename();
+ if (filename == currentFile) {
+ // Nothing to do in this case, we've already read the code
+ } else if (filename.isEmpty() || filename == currentFileAbsPath) {
+ code = m_code;
+ } else {
+ QFile file(filename);
+ const bool success = file.open(QFile::ReadOnly);
+ Q_ASSERT(success);
+ code = QString::fromUtf8(file.readAll());
+ currentFile = filename;
+ }
+
+ IssueLocationWithContext issueLocationWithContext { code, fixItem.location() };
+
+ if (const QStringView beforeText = issueLocationWithContext.beforeText();
+ !beforeText.isEmpty()) {
+ m_output.write(beforeText);
}
+
+ // The replacement string can be empty if we're only pointing something out to the user
+ const QString replacement = fixItem.replacement();
+ QStringView replacementString = replacement.isEmpty()
+ ? issueLocationWithContext.issueText()
+ : replacement;
+
+ // But if there's nothing to change it cannot be auto-applied
+ Q_ASSERT(!replacement.isEmpty() || !fixItem.isAutoApplicable());
+
+ m_output.write(replacementString, QtDebugMsg);
+ m_output.write(issueLocationWithContext.afterText().toString() + u'\n');
+
+ int tabCount = issueLocationWithContext.beforeText().count(u'\t');
+
+ // Do not draw location indicator for multiline replacement strings
+ if (replacementString.contains(u'\n'))
+ return;
+
+ m_output.write(u" "_s.repeated(
+ issueLocationWithContext.beforeText().size() - tabCount)
+ + u"\t"_s.repeated(tabCount)
+ + u"^"_s.repeated(replacement.size()) + u'\n');
+}
+
+QQmlJSFixSuggestion::QQmlJSFixSuggestion(const QString &fixDescription,
+ const QQmlJS::SourceLocation &location,
+ const QString &replacement)
+ : m_location{ location }, m_fixDescription{ fixDescription }, m_replacement{ replacement }
+{
}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h
index ca76bb5f37..d7c600d311 100644
--- a/src/qmlcompiler/qqmljslogger_p.h
+++ b/src/qmlcompiler/qqmljslogger_p.h
@@ -15,9 +15,10 @@
// We mean it.
//
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include "qcoloroutput_p.h"
+#include "qqmljsloggingutils_p.h"
#include <private/qqmljsdiagnosticmessage_p.h>
@@ -34,9 +35,9 @@ QT_BEGIN_NAMESPACE
/*!
\internal
- Used to print the the line containing the location of a certain error
+ Used to print the line containing the location of a certain error
*/
-class Q_QMLCOMPILER_PRIVATE_EXPORT IssueLocationWithContext
+class Q_QMLCOMPILER_EXPORT IssueLocationWithContext
{
public:
/*!
@@ -70,134 +71,54 @@ private:
QStringView m_afterText;
};
-struct Q_QMLCOMPILER_PRIVATE_EXPORT FixSuggestion
-{
- struct Fix
- {
- QString message;
- QQmlJS::SourceLocation cutLocation = QQmlJS::SourceLocation();
- QString replacementString = QString();
- QString fileName = QString();
- // A Fix is a hint if it can not be automatically applied to fix an issue or only points out
- // its origin
- bool isHint = true;
- };
- QList<Fix> fixes;
-};
-
-class Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId
+class Q_QMLCOMPILER_EXPORT QQmlJSFixSuggestion
{
public:
- constexpr LoggerWarningId(QAnyStringView name) : m_name(name) { }
+ QQmlJSFixSuggestion() = default;
+ QQmlJSFixSuggestion(const QString &fixDescription, const QQmlJS::SourceLocation &location,
+ const QString &replacement = QString());
+
+ QString fixDescription() const { return m_fixDescription; }
+ QQmlJS::SourceLocation location() const { return m_location; }
+ QString replacement() const { return m_replacement; }
+
+ void setFilename(const QString &filename) { m_filename = filename; }
+ QString filename() const { return m_filename; }
- const QAnyStringView name() const { return m_name; }
+ void setHint(const QString &hint) { m_hint = hint; }
+ QString hint() const { return m_hint; }
+
+ void setAutoApplicable(bool autoApply = true) { m_autoApplicable = autoApply; }
+ bool isAutoApplicable() const { return m_autoApplicable; }
+
+ bool operator==(const QQmlJSFixSuggestion &) const;
+ bool operator!=(const QQmlJSFixSuggestion &) const;
private:
- const QAnyStringView m_name;
+ QQmlJS::SourceLocation m_location;
+ QString m_fixDescription;
+ QString m_replacement;
+ QString m_filename;
+ QString m_hint;
+ bool m_autoApplicable = false;
};
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlRequired;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUnresolvedAlias;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlAliasCycle;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlImport;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlRecursionDepthErrors;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlWith;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlInheritanceCycle;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlDeprecated;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlSignalParameters;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlMissingType;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUnresolvedType;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlIncompatibleType;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlMissingProperty;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlRestrictedType;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlPrefixedImportType;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlNonListProperty;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlReadOnlyProperty;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlDuplicatePropertyBinding;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlDuplicatedName;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlDeferredPropertyId;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUnqualified;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUnusedImports;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlMultilineStrings;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlSyntax;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlSyntaxIdQuotation;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlSyntaxDuplicateIds;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlCompiler;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlAttachedPropertyReuse;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlPlugin;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlVarUsedBeforeDeclaration;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlInvalidLintDirective;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUseProperFunction;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlAccessSingleton;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlTopLevelComponent;
-extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUncreatableType;
-
struct Message : public QQmlJS::DiagnosticMessage
{
// This doesn't need to be an owning-reference since the string is expected to outlive any
// Message object by virtue of coming from a LoggerWarningId.
QAnyStringView id;
- std::optional<FixSuggestion> fixSuggestion;
+ std::optional<QQmlJSFixSuggestion> fixSuggestion;
};
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSLogger
+class Q_QMLCOMPILER_EXPORT QQmlJSLogger
{
Q_DISABLE_COPY_MOVE(QQmlJSLogger)
public:
- struct Category
- {
- QString name;
- QString settingsName;
- QString description;
- QtMsgType level;
- bool ignored = false;
- bool isDefault = false; // Whether or not the category can be disabled
- bool changed = false;
-
- bool operator==(const LoggerWarningId warningId) const { return warningId.name() == name; }
-
- LoggerWarningId id() const { return LoggerWarningId(name); }
-
- QString levelToString() const {
- // TODO: this only makes sense to qmllint
- Q_ASSERT(ignored || level != QtCriticalMsg);
- if (ignored)
- return QStringLiteral("disable");
-
- switch (level) {
- case QtInfoMsg:
- return QStringLiteral("info");
- case QtWarningMsg:
- return QStringLiteral("warning");
- default:
- Q_UNREACHABLE();
- break;
- }
- }
-
- bool setLevel(const QString &level) {
- if (level == QStringLiteral("disable")) {
- this->level = QtCriticalMsg; // TODO: only so for consistency with previous logic
- this->ignored = true;
- } else if (level == QStringLiteral("info")) {
- this->level = QtInfoMsg;
- this->ignored = false;
- } else if (level == QStringLiteral("warning")) {
- this->level = QtWarningMsg;
- this->ignored = false;
- } else {
- return false;
- }
-
- this->changed = true;
- return true;
- }
- };
-
- const QList<Category> categories() const;
- static const QList<Category> &defaultCategories();
-
- void registerCategory(const Category &category);
+ QList<QQmlJS::LoggerCategory> categories() const;
+ static const QList<QQmlJS::LoggerCategory> &defaultCategories();
+
+ void registerCategory(const QQmlJS::LoggerCategory &category);
QQmlJSLogger();
~QQmlJSLogger() = default;
@@ -209,37 +130,37 @@ public:
const QList<Message> &warnings() const { return m_warnings; }
const QList<Message> &errors() const { return m_errors; }
- QtMsgType categoryLevel(LoggerWarningId id) const
+ QtMsgType categoryLevel(QQmlJS::LoggerWarningId id) const
{
return m_categoryLevels[id.name().toString()];
}
- void setCategoryLevel(LoggerWarningId id, QtMsgType level)
+ void setCategoryLevel(QQmlJS::LoggerWarningId id, QtMsgType level)
{
m_categoryLevels[id.name().toString()] = level;
m_categoryChanged[id.name().toString()] = true;
}
- bool isCategoryIgnored(LoggerWarningId id) const
+ bool isCategoryIgnored(QQmlJS::LoggerWarningId id) const
{
return m_categoryIgnored[id.name().toString()];
}
- void setCategoryIgnored(LoggerWarningId id, bool error)
+ void setCategoryIgnored(QQmlJS::LoggerWarningId id, bool error)
{
m_categoryIgnored[id.name().toString()] = error;
m_categoryChanged[id.name().toString()] = true;
}
- bool isCategoryFatal(LoggerWarningId id) const
+ bool isCategoryFatal(QQmlJS::LoggerWarningId id) const
{
return m_categoryFatal[id.name().toString()];
}
- void setCategoryFatal(LoggerWarningId id, bool error)
+ void setCategoryFatal(QQmlJS::LoggerWarningId id, bool error)
{
m_categoryFatal[id.name().toString()] = error;
m_categoryChanged[id.name().toString()] = true;
}
- bool wasCategoryChanged(LoggerWarningId id) const
+ bool wasCategoryChanged(QQmlJS::LoggerWarningId id) const
{
return m_categoryChanged[id.name().toString()];
}
@@ -251,9 +172,9 @@ public:
\sa setCategoryLevel
*/
- void log(const QString &message, LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation,
- bool showContext = true, bool showFileName = true,
- const std::optional<FixSuggestion> &suggestion = {},
+ void log(const QString &message, QQmlJS::LoggerWarningId id,
+ const QQmlJS::SourceLocation &srcLocation, bool showContext = true,
+ bool showFileName = true, const std::optional<QQmlJSFixSuggestion> &suggestion = {},
const QString overrideFileName = QString())
{
log(message, id, srcLocation, m_categoryLevels[id.name().toString()], showContext,
@@ -261,7 +182,7 @@ public:
}
void processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
- const LoggerWarningId id);
+ const QQmlJS::LoggerWarningId id);
void ignoreWarnings(uint32_t line, const QSet<QString> &categories)
{
@@ -278,14 +199,15 @@ public:
QString fileName() const { return m_fileName; }
private:
- QMap<QString, Category> m_categories;
+ QMap<QString, QQmlJS::LoggerCategory> m_categories;
void printContext(const QString &overrideFileName, const QQmlJS::SourceLocation &location);
- void printFix(const FixSuggestion &fix);
+ void printFix(const QQmlJSFixSuggestion &fix);
- void log(const QString &message, LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation,
- QtMsgType type, bool showContext, bool showFileName,
- const std::optional<FixSuggestion> &suggestion, const QString overrideFileName);
+ void log(const QString &message, QQmlJS::LoggerWarningId id,
+ const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
+ bool showFileName, const std::optional<QQmlJSFixSuggestion> &suggestion,
+ const QString overrideFileName);
QString m_fileName;
QString m_code;
diff --git a/src/qmlcompiler/qqmljsloggingutils.cpp b/src/qmlcompiler/qqmljsloggingutils.cpp
new file mode 100644
index 0000000000..caa2438ae5
--- /dev/null
+++ b/src/qmlcompiler/qqmljsloggingutils.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmljsloggingutils.h"
+#include "qqmljsloggingutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+LoggerCategory::LoggerCategory() : d_ptr{ new LoggerCategoryPrivate } { }
+
+LoggerCategory::LoggerCategory(QString name, QString settingsName, QString description,
+ QtMsgType level, bool ignored, bool isDefault)
+ : d_ptr{ new LoggerCategoryPrivate }
+{
+ Q_D(LoggerCategory);
+ d->m_name = name;
+ d->m_settingsName = settingsName;
+ d->m_description = description;
+ d->m_level = level;
+ d->m_ignored = ignored;
+ d->m_isDefault = isDefault;
+}
+
+LoggerCategory::LoggerCategory(const LoggerCategory &other)
+ : d_ptr{ new LoggerCategoryPrivate{ *other.d_func() } }
+{
+}
+
+LoggerCategory::LoggerCategory(LoggerCategory &&) noexcept = default;
+
+LoggerCategory &LoggerCategory::operator=(const LoggerCategory &other)
+{
+ *d_func() = *other.d_func();
+ return *this;
+}
+
+LoggerCategory &LoggerCategory::operator=(LoggerCategory &&) noexcept = default;
+
+LoggerCategory::~LoggerCategory() = default;
+
+QString LoggerCategory::name() const
+{
+ Q_D(const LoggerCategory);
+ return d->m_name;
+}
+
+QString LoggerCategory::settingsName() const
+{
+ Q_D(const LoggerCategory);
+ return d->m_settingsName;
+}
+
+QString LoggerCategory::description() const
+{
+ Q_D(const LoggerCategory);
+ return d->m_description;
+}
+
+QtMsgType LoggerCategory::level() const
+{
+ Q_D(const LoggerCategory);
+ return d->m_level;
+}
+
+bool LoggerCategory::isIgnored() const
+{
+ Q_D(const LoggerCategory);
+ return d->m_ignored;
+}
+
+bool LoggerCategory::isDefault() const
+{
+ Q_D(const LoggerCategory);
+ return d->m_isDefault;
+}
+
+LoggerWarningId LoggerCategory::id() const
+{
+ Q_D(const LoggerCategory);
+ return d->id();
+}
+
+void LoggerCategory::setLevel(QtMsgType type)
+{
+ Q_D(LoggerCategory);
+ d->setLevel(type);
+}
+
+void LoggerCategoryPrivate::setLevel(QtMsgType type)
+{
+ if (m_level == type)
+ return;
+
+ m_level = type;
+ m_changed = true;
+}
+
+void LoggerCategory::setIgnored(bool isIgnored)
+{
+ Q_D(LoggerCategory);
+ d->setIgnored(isIgnored);
+}
+
+void LoggerCategoryPrivate::setIgnored(bool isIgnored)
+{
+ if (m_ignored == isIgnored)
+ return;
+
+ m_ignored = isIgnored;
+ m_changed = true;
+}
+
+bool LoggerCategoryPrivate::hasChanged() const
+{
+ return m_changed;
+}
+
+LoggerCategoryPrivate *LoggerCategoryPrivate::get(LoggerCategory *loggerCategory)
+{
+ Q_ASSERT(loggerCategory);
+ return loggerCategory->d_func();
+}
+
+/*!
+ \class QQmlSA::LoggerWarningId
+ \inmodule QtQmlCompiler
+
+ \brief A wrapper around a string literal to uniquely identify
+ warning categories in the \c{QQmlSA} framework.
+*/
+
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsloggingutils.h b/src/qmlcompiler/qqmljsloggingutils.h
new file mode 100644
index 0000000000..5d48cd379b
--- /dev/null
+++ b/src/qmlcompiler/qqmljsloggingutils.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLJSLOGGER_H
+#define QQMLJSLOGGER_H
+
+#include <QtCore/qanystringview.h>
+#include <QtQmlCompiler/qtqmlcompilerexports.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlJSLoggerPrivate;
+class QQmlJSScope;
+
+namespace QQmlSA {
+class SourceLocation;
+}
+
+namespace QQmlSA {
+
+class Q_QMLCOMPILER_EXPORT LoggerWarningId
+{
+public:
+ constexpr LoggerWarningId(QAnyStringView name) : m_name(name) { }
+
+ QAnyStringView name() const { return m_name; }
+
+private:
+ friend bool operator==(const LoggerWarningId &a, const LoggerWarningId &b)
+ {
+ return a.m_name == b.m_name;
+ }
+
+ friend bool operator!=(const LoggerWarningId &a, const LoggerWarningId &b)
+ {
+ return a.m_name != b.m_name;
+ }
+ const QAnyStringView m_name;
+};
+
+} // namespace QQmlSA
+
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlRequired;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnresolvedAlias;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlAliasCycle;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlImport;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlRecursionDepthErrors;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlWith;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlInheritanceCycle;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlDeprecated;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlSignalParameters;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlMissingType;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnresolvedType;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlIncompatibleType;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlMissingProperty;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlRestrictedType;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlPrefixedImportType;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlNonListProperty;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlReadOnlyProperty;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlDuplicatePropertyBinding;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlDuplicatedName;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlDeferredPropertyId;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnqualified;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnusedImports;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlMultilineStrings;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlSyntax;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlSyntaxIdQuotation;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlSyntaxDuplicateIds;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlCompiler;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlAttachedPropertyReuse;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlPlugin;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlVarUsedBeforeDeclaration;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlInvalidLintDirective;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUseProperFunction;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlAccessSingleton;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlTopLevelComponent;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUncreatableType;
+extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlMissingEnumEntry;
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSLOGGER_H
diff --git a/src/qmlcompiler/qqmljsloggingutils_p.h b/src/qmlcompiler/qqmljsloggingutils_p.h
new file mode 100644
index 0000000000..9099a2c73c
--- /dev/null
+++ b/src/qmlcompiler/qqmljsloggingutils_p.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLJSLOGGINGUTILS_P_H
+#define QQMLJSLOGGINGUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+#include <qtqmlcompilerexports.h>
+
+#include "qqmljsloggingutils.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+using LoggerWarningId = QQmlSA::LoggerWarningId;
+
+class LoggerCategoryPrivate;
+
+class Q_QMLCOMPILER_EXPORT LoggerCategory
+{
+ Q_DECLARE_PRIVATE(LoggerCategory)
+
+public:
+ LoggerCategory();
+ LoggerCategory(QString name, QString settingsName, QString description, QtMsgType level,
+ bool ignored = false, bool isDefault = false);
+ LoggerCategory(const LoggerCategory &);
+ LoggerCategory(LoggerCategory &&) noexcept;
+ LoggerCategory &operator=(const LoggerCategory &);
+ LoggerCategory &operator=(LoggerCategory &&) noexcept;
+ ~LoggerCategory();
+
+ QString name() const;
+ QString settingsName() const;
+ QString description() const;
+ QtMsgType level() const;
+ bool isIgnored() const;
+ bool isDefault() const;
+
+ LoggerWarningId id() const;
+
+ void setLevel(QtMsgType);
+ void setIgnored(bool);
+
+private:
+ std::unique_ptr<QQmlJS::LoggerCategoryPrivate> d_ptr;
+};
+
+class LoggerCategoryPrivate
+{
+ friend class QT_PREPEND_NAMESPACE(QQmlJS::LoggerCategory);
+
+public:
+ LoggerWarningId id() const { return LoggerWarningId(m_name); }
+
+ void setLevel(QtMsgType);
+ void setIgnored(bool);
+
+ QString name() const;
+ QString settingsName() const;
+ QString description() const;
+ QtMsgType level() const;
+ bool isIgnored() const;
+ bool isDefault() const;
+ bool hasChanged() const;
+
+ static LoggerCategoryPrivate *get(LoggerCategory *);
+
+ friend bool operator==(const LoggerCategoryPrivate &lhs, const LoggerCategoryPrivate &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+ friend bool operator!=(const LoggerCategoryPrivate &lhs, const LoggerCategoryPrivate &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+ bool operator==(const LoggerWarningId warningId) const { return warningId.name() == m_name; }
+
+private:
+ static bool operatorEqualsImpl(const LoggerCategoryPrivate &, const LoggerCategoryPrivate &);
+
+ QString m_name;
+ QString m_settingsName;
+ QString m_description;
+ QtMsgType m_level = QtDebugMsg;
+ bool m_ignored = false;
+ bool m_isDefault = false; // Whether or not the category can be disabled
+ bool m_changed = false;
+};
+
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSLOGGINGUTILS_P_H
diff --git a/src/qmlcompiler/qqmljsmetatypes.cpp b/src/qmlcompiler/qqmljsmetatypes.cpp
index 9ba7d2ca54..4155e58728 100644
--- a/src/qmlcompiler/qqmljsmetatypes.cpp
+++ b/src/qmlcompiler/qqmljsmetatypes.cpp
@@ -13,21 +13,9 @@ QT_BEGIN_NAMESPACE
A binding is valid when it has both a target (m_propertyName is set)
and some content set (m_bindingType != Invalid).
*/
-bool QQmlJSMetaPropertyBinding::isValid() const { return !m_propertyName.isEmpty() && bindingType() != Invalid; }
-
-QString QQmlJSMetaPropertyBinding::literalTypeName() const
+bool QQmlJSMetaPropertyBinding::isValid() const
{
- if (std::holds_alternative<Content::BoolLiteral>(m_bindingContent))
- return QLatin1String("bool");
- else if (std::holds_alternative<Content::NumberLiteral>(m_bindingContent))
- return QLatin1String("double");
- else if (std::holds_alternative<Content::StringLiteral>(m_bindingContent))
- return QLatin1String("string");
- else if (std::holds_alternative<Content::RegexpLiteral>(m_bindingContent))
- return QLatin1String("regexp");
- else if (std::holds_alternative<Content::Null>(m_bindingContent))
- return QLatin1String("$internal$.std::nullptr_t");
- return {};
+ return !m_propertyName.isEmpty() && bindingType() != QQmlSA::BindingType::Invalid;
}
bool QQmlJSMetaPropertyBinding::boolValue() const
@@ -93,28 +81,30 @@ QSharedPointer<const QQmlJSScope> QQmlJSMetaPropertyBinding::literalType(const Q
{
Q_ASSERT(resolver);
switch (bindingType()) {
- case QQmlJSMetaPropertyBinding::BoolLiteral:
+ case BindingType::BoolLiteral:
return resolver->boolType();
- case QQmlJSMetaPropertyBinding::NumberLiteral:
+ case BindingType::NumberLiteral:
return resolver->typeForName(QLatin1String("double"));
- case QQmlJSMetaPropertyBinding::Translation: // translations are strings
- case QQmlJSMetaPropertyBinding::TranslationById:
- case QQmlJSMetaPropertyBinding::StringLiteral:
+ case BindingType::Translation: // translations are strings
+ case BindingType::TranslationById:
+ case BindingType::StringLiteral:
return resolver->stringType();
- case QQmlJSMetaPropertyBinding::RegExpLiteral:
+ case BindingType::RegExpLiteral:
return resolver->typeForName(QLatin1String("regexp"));
- case QQmlJSMetaPropertyBinding::Null:
+ case BindingType::Null:
return resolver->nullType();
- case QQmlJSMetaPropertyBinding::Invalid:
- case QQmlJSMetaPropertyBinding::Script:
- case QQmlJSMetaPropertyBinding::Object:
- case QQmlJSMetaPropertyBinding::Interceptor:
- case QQmlJSMetaPropertyBinding::ValueSource:
- case QQmlJSMetaPropertyBinding::AttachedProperty:
- case QQmlJSMetaPropertyBinding::GroupProperty:
+ case BindingType::Invalid:
+ case BindingType::Script:
+ case BindingType::Object:
+ case BindingType::Interceptor:
+ case BindingType::ValueSource:
+ case BindingType::AttachedProperty:
+ case BindingType::GroupProperty:
return {};
}
Q_UNREACHABLE_RETURN({});
}
+QQmlJSMetaPropertyBinding::QQmlJSMetaPropertyBinding() = default;
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index 47945558b7..0ea7020a2d 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -14,7 +14,7 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
@@ -23,9 +23,10 @@
#include <QtCore/qhash.h>
#include <QtQml/private/qqmljssourcelocation_p.h>
-
#include <QtQml/private/qqmltranslation_p.h>
+#include "qqmlsaconstants.h"
+#include "qqmlsa.h"
#include "qqmljsannotation_p.h"
// MetaMethod and MetaProperty have both type names and actual QQmlJSScope types.
@@ -40,6 +41,13 @@
QT_BEGIN_NAMESPACE
+enum ScriptBindingValueType : unsigned int {
+ ScriptValue_Unknown,
+ ScriptValue_Undefined // property int p: undefined
+};
+
+using QQmlJSMetaMethodType = QQmlSA::MethodType;
+
class QQmlJSTypeResolver;
class QQmlJSScope;
class QQmlJSMetaEnum
@@ -48,9 +56,11 @@ class QQmlJSMetaEnum
QList<int> m_values; // empty if values unknown.
QString m_name;
QString m_alias;
+ QString m_typeName;
QSharedPointer<const QQmlJSScope> m_type;
bool m_isFlag = false;
- bool m_scoped = true;
+ bool m_isScoped = false;
+ bool m_isQml = false;
public:
QQmlJSMetaEnum() = default;
@@ -67,8 +77,11 @@ public:
bool isFlag() const { return m_isFlag; }
void setIsFlag(bool isFlag) { m_isFlag = isFlag; }
- bool isScoped() const { return m_scoped; }
- void setScoped(bool v) { m_scoped = v; }
+ bool isScoped() const { return m_isScoped; }
+ void setIsScoped(bool v) { m_isScoped = v; }
+
+ bool isQml() const { return m_isQml; }
+ void setIsQml(bool v) { m_isQml = v; }
void addKey(const QString &key) { m_keys.append(key); }
QStringList keys() const { return m_keys; }
@@ -80,6 +93,9 @@ public:
int value(const QString &key) const { return m_values.value(m_keys.indexOf(key)); }
bool hasKey(const QString &key) const { return m_keys.indexOf(key) != -1; }
+ QString typeName() const { return m_typeName; }
+ void setTypeName(const QString &typeName) { m_typeName = typeName; }
+
QSharedPointer<const QQmlJSScope> type() const { return m_type; }
void setType(const QSharedPointer<const QQmlJSScope> &type) { m_type = type; }
@@ -90,7 +106,8 @@ public:
&& a.m_name == b.m_name
&& a.m_alias == b.m_alias
&& a.m_isFlag == b.m_isFlag
- && a.m_type == b.m_type;
+ && a.m_type == b.m_type
+ && a.m_isScoped == b.m_isScoped;
}
friend bool operator!=(const QQmlJSMetaEnum &a, const QQmlJSMetaEnum &b)
@@ -100,7 +117,8 @@ public:
friend size_t qHash(const QQmlJSMetaEnum &e, size_t seed = 0)
{
- return qHashMulti(seed, e.m_keys, e.m_values, e.m_name, e.m_alias, e.m_isFlag, e.m_type);
+ return qHashMulti(
+ seed, e.m_keys, e.m_values, e.m_name, e.m_alias, e.m_isFlag, e.m_type, e.m_isScoped);
}
};
@@ -118,10 +136,13 @@ public:
Const,
};
- QQmlJSMetaParameter(const QString &name, const QString &typeName,
+ QQmlJSMetaParameter(QString name = QString(), QString typeName = QString(),
Constness typeQualifier = NonConst,
QWeakPointer<const QQmlJSScope> type = {})
- : m_name(name), m_typeName(typeName), m_type(type), m_typeQualifier(typeQualifier)
+ : m_name(std::move(name)),
+ m_typeName(std::move(typeName)),
+ m_type(type),
+ m_typeQualifier(typeQualifier)
{
}
@@ -135,11 +156,13 @@ public:
void setTypeQualifier(Constness typeQualifier) { m_typeQualifier = typeQualifier; }
bool isPointer() const { return m_isPointer; }
void setIsPointer(bool isPointer) { m_isPointer = isPointer; }
+ bool isList() const { return m_isList; }
+ void setIsList(bool isList) { m_isList = isList; }
friend bool operator==(const QQmlJSMetaParameter &a, const QQmlJSMetaParameter &b)
{
return a.m_name == b.m_name && a.m_typeName == b.m_typeName
- && a.m_type.toStrongRef().data() == b.m_type.toStrongRef().data()
+ && a.m_type.owner_equal(b.m_type)
&& a.m_typeQualifier == b.m_typeQualifier;
}
@@ -150,7 +173,7 @@ public:
friend size_t qHash(const QQmlJSMetaParameter &e, size_t seed = 0)
{
- return qHashMulti(seed, e.m_name, e.m_typeName, e.m_type.toStrongRef().data(),
+ return qHashMulti(seed, e.m_name, e.m_typeName, e.m_type.owner_hash(),
e.m_typeQualifier);
}
@@ -160,14 +183,16 @@ private:
QWeakPointer<const QQmlJSScope> m_type;
Constness m_typeQualifier = NonConst;
bool m_isPointer = false;
+ bool m_isList = false;
};
+using QQmlJSMetaReturnType = QQmlJSMetaParameter;
+
class QQmlJSMetaMethod
{
public:
- enum Type { Signal, Slot, Method, StaticMethod };
-
enum Access { Private, Protected, Public };
+ using MethodType = QQmlJSMetaMethodType;
public:
/*! \internal
@@ -188,23 +213,30 @@ public:
QQmlJSMetaMethod() = default;
explicit QQmlJSMetaMethod(QString name, QString returnType = QString())
- : m_name(std::move(name))
- , m_returnTypeName(std::move(returnType))
- , m_methodType(Method)
+ : m_name(std::move(name)),
+ m_returnType(QString(), std::move(returnType)),
+ m_methodType(MethodType::Method)
{}
QString methodName() const { return m_name; }
void setMethodName(const QString &name) { m_name = name; }
- QString returnTypeName() const { return m_returnTypeName; }
- QSharedPointer<const QQmlJSScope> returnType() const { return m_returnType.toStrongRef(); }
- void setReturnTypeName(const QString &type) { m_returnTypeName = type; }
- void setReturnType(const QSharedPointer<const QQmlJSScope> &type)
- {
- m_returnType = type;
- }
+ QQmlJS::SourceLocation sourceLocation() const { return m_sourceLocation; }
+ void setSourceLocation(QQmlJS::SourceLocation location) { m_sourceLocation = location; }
+
+ QQmlJSMetaReturnType returnValue() const { return m_returnType; }
+ void setReturnValue(const QQmlJSMetaReturnType returnValue) { m_returnType = returnValue; }
+ QString returnTypeName() const { return m_returnType.typeName(); }
+ void setReturnTypeName(const QString &typeName) { m_returnType.setTypeName(typeName); }
+ QSharedPointer<const QQmlJSScope> returnType() const { return m_returnType.type(); }
+ void setReturnType(QWeakPointer<const QQmlJSScope> type) { m_returnType.setType(type); }
QList<QQmlJSMetaParameter> parameters() const { return m_parameters; }
+ QPair<QList<QQmlJSMetaParameter>::iterator, QList<QQmlJSMetaParameter>::iterator>
+ mutableParametersRange()
+ {
+ return { m_parameters.begin(), m_parameters.end() };
+ }
QStringList parameterNames() const
{
@@ -219,8 +251,8 @@ public:
void addParameter(const QQmlJSMetaParameter &p) { m_parameters.append(p); }
- int methodType() const { return m_methodType; }
- void setMethodType(Type methodType) { m_methodType = methodType; }
+ QQmlJSMetaMethodType methodType() const { return m_methodType; }
+ void setMethodType(MethodType methodType) { m_methodType = methodType; }
Access access() const { return m_methodAccess; }
@@ -250,16 +282,36 @@ public:
const QVector<QQmlJSAnnotation>& annotations() const { return m_annotations; }
void setAnnotations(QVector<QQmlJSAnnotation> annotations) { m_annotations = annotations; }
- void setJsFunctionIndex(RelativeFunctionIndex index) { m_jsFunctionIndex = index; }
- RelativeFunctionIndex jsFunctionIndex() const { return m_jsFunctionIndex; }
+ void setJsFunctionIndex(RelativeFunctionIndex index)
+ {
+ Q_ASSERT(!m_isConstructor);
+ m_relativeFunctionIndex = index;
+ }
+
+ RelativeFunctionIndex jsFunctionIndex() const
+ {
+ Q_ASSERT(!m_isConstructor);
+ return m_relativeFunctionIndex;
+ }
+
+ void setConstructorIndex(RelativeFunctionIndex index)
+ {
+ Q_ASSERT(m_isConstructor);
+ m_relativeFunctionIndex = index;
+ }
+
+ RelativeFunctionIndex constructorIndex() const
+ {
+ Q_ASSERT(m_isConstructor);
+ return m_relativeFunctionIndex;
+ }
friend bool operator==(const QQmlJSMetaMethod &a, const QQmlJSMetaMethod &b)
{
- return a.m_name == b.m_name && a.m_returnTypeName == b.m_returnTypeName
- && a.m_returnType == b.m_returnType && a.m_parameters == b.m_parameters
- && a.m_annotations == b.m_annotations && a.m_methodType == b.m_methodType
- && a.m_methodAccess == b.m_methodAccess && a.m_revision == b.m_revision
- && a.m_isConstructor == b.m_isConstructor;
+ return a.m_name == b.m_name && a.m_returnType == b.m_returnType
+ && a.m_parameters == b.m_parameters && a.m_annotations == b.m_annotations
+ && a.m_methodType == b.m_methodType && a.m_methodAccess == b.m_methodAccess
+ && a.m_revision == b.m_revision && a.m_isConstructor == b.m_isConstructor;
}
friend bool operator!=(const QQmlJSMetaMethod &a, const QQmlJSMetaMethod &b)
@@ -272,8 +324,7 @@ public:
QtPrivate::QHashCombine combine;
seed = combine(seed, method.m_name);
- seed = combine(seed, method.m_returnTypeName);
- seed = combine(seed, method.m_returnType.toStrongRef().data());
+ seed = combine(seed, method.m_returnType);
seed = combine(seed, method.m_annotations);
seed = combine(seed, method.m_methodType);
seed = combine(seed, method.m_methodAccess);
@@ -289,16 +340,17 @@ public:
private:
QString m_name;
- QString m_returnTypeName;
- QWeakPointer<const QQmlJSScope> m_returnType;
+ QQmlJS::SourceLocation m_sourceLocation;
+
+ QQmlJSMetaReturnType m_returnType;
QList<QQmlJSMetaParameter> m_parameters;
QList<QQmlJSAnnotation> m_annotations;
- Type m_methodType = Signal;
+ MethodType m_methodType = MethodType::Signal;
Access m_methodAccess = Public;
int m_revision = 0;
- RelativeFunctionIndex m_jsFunctionIndex = RelativeFunctionIndex::Invalid;
+ RelativeFunctionIndex m_relativeFunctionIndex = RelativeFunctionIndex::Invalid;
bool m_isCloned = false;
bool m_isConstructor = false;
bool m_isJavaScriptFunction = false;
@@ -391,7 +443,7 @@ public:
{
return a.m_index == b.m_index && a.m_propertyName == b.m_propertyName
&& a.m_typeName == b.m_typeName && a.m_bindable == b.m_bindable
- && a.m_type == b.m_type && a.m_isList == b.m_isList
+ && a.m_type.owner_equal(b.m_type) && a.m_isList == b.m_isList
&& a.m_isWritable == b.m_isWritable && a.m_isPointer == b.m_isPointer
&& a.m_aliasExpr == b.m_aliasExpr && a.m_revision == b.m_revision
&& a.m_isFinal == b.m_isFinal;
@@ -420,39 +472,10 @@ public:
create a new binding, you know all the details of it already, so you should
just set all the data at once.
*/
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSMetaPropertyBinding
+class Q_QMLCOMPILER_EXPORT QQmlJSMetaPropertyBinding
{
-public:
- enum BindingType : unsigned int {
- Invalid,
- BoolLiteral,
- NumberLiteral,
- StringLiteral,
- RegExpLiteral,
- Null,
- Translation,
- TranslationById,
- Script,
- Object,
- Interceptor,
- ValueSource,
- AttachedProperty,
- GroupProperty,
- };
-
- enum ScriptBindingKind : unsigned int {
- Script_Invalid,
- Script_PropertyBinding, // property int p: 1 + 1
- Script_SignalHandler, // onSignal: { ... }
- Script_ChangeHandler, // onXChanged: { ... }
- };
-
- enum ScriptBindingValueType : unsigned int {
- ScriptValue_Unknown,
- ScriptValue_Undefined // property int p: undefined
- };
-
-private:
+ using BindingType = QQmlSA::BindingType;
+ using ScriptBindingKind = QQmlSA::ScriptBindingKind;
// needs to be kept in sync with the BindingType enum
struct Content {
@@ -514,11 +537,11 @@ private:
friend bool operator!=(Script a, Script b) { return !(a == b); }
QQmlJSMetaMethod::RelativeFunctionIndex index =
QQmlJSMetaMethod::RelativeFunctionIndex::Invalid;
- ScriptBindingKind kind = Script_Invalid;
- ScriptBindingValueType valueType = ScriptValue_Unknown;
+ ScriptBindingKind kind = ScriptBindingKind::Invalid;
+ ScriptBindingValueType valueType = ScriptBindingValueType::ScriptValue_Unknown;
};
struct Object {
- friend bool operator==(Object a, Object b) { return a.value == b.value && a.typeName == b.typeName; }
+ friend bool operator==(Object a, Object b) { return a.value.owner_equal(b.value) && a.typeName == b.typeName; }
friend bool operator!=(Object a, Object b) { return !(a == b); }
QString typeName;
QWeakPointer<const QQmlJSScope> value;
@@ -526,7 +549,7 @@ private:
struct Interceptor {
friend bool operator==(Interceptor a, Interceptor b)
{
- return a.value == b.value && a.typeName == b.typeName;
+ return a.value.owner_equal(b.value) && a.typeName == b.typeName;
}
friend bool operator!=(Interceptor a, Interceptor b) { return !(a == b); }
QString typeName;
@@ -535,7 +558,7 @@ private:
struct ValueSource {
friend bool operator==(ValueSource a, ValueSource b)
{
- return a.value == b.value && a.typeName == b.typeName;
+ return a.value.owner_equal(b.value) && a.typeName == b.typeName;
}
friend bool operator!=(ValueSource a, ValueSource b) { return !(a == b); }
QString typeName;
@@ -564,7 +587,7 @@ private:
*/
friend bool operator==(AttachedProperty a, AttachedProperty b)
{
- return a.value == b.value;
+ return a.value.owner_equal(b.value);
}
friend bool operator!=(AttachedProperty a, AttachedProperty b) { return !(a == b); }
QWeakPointer<const QQmlJSScope> value;
@@ -586,7 +609,7 @@ private:
### TODO: Obtaining the effective binding result requires some resolving function
*/
QWeakPointer<const QQmlJSScope> groupScope;
- friend bool operator==(GroupProperty a, GroupProperty b) { return a.groupScope == b.groupScope; }
+ friend bool operator==(GroupProperty a, GroupProperty b) { return a.groupScope.owner_equal(b.groupScope); }
friend bool operator!=(GroupProperty a, GroupProperty b) { return !(a == b); }
};
using type = std::variant<Invalid, BoolLiteral, NumberLiteral, StringLiteral,
@@ -617,6 +640,7 @@ public:
|| type == BindingType::Null; // special. we record it as literal
}
+ QQmlJSMetaPropertyBinding();
QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation location) : m_sourceLocation(location) { }
explicit QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation location, const QString &propName)
: m_sourceLocation(location), m_propertyName(propName)
@@ -643,8 +667,9 @@ public:
m_bindingContent = Content::StringLiteral { value.toString() };
}
- void setScriptBinding(QQmlJSMetaMethod::RelativeFunctionIndex value, ScriptBindingKind kind,
- ScriptBindingValueType valueType = ScriptValue_Unknown)
+ void
+ setScriptBinding(QQmlJSMetaMethod::RelativeFunctionIndex value, ScriptBindingKind kind,
+ ScriptBindingValueType valueType = ScriptBindingValueType::ScriptValue_Unknown)
{
ensureSetBindingTypeOnce();
m_bindingContent = Content::Script { value, kind, valueType };
@@ -717,8 +742,6 @@ public:
m_bindingContent = Content::ValueSource { typeName, type };
}
- QString literalTypeName() const;
-
// ### TODO: here and below: Introduce an allowConversion parameter, if yes, enable conversions e.g. bool -> number?
bool boolValue() const;
@@ -745,7 +768,7 @@ public:
if (auto *script = std::get_if<Content::Script>(&m_bindingContent))
return script->kind;
// warn
- return ScriptBindingKind::Script_Invalid;
+ return ScriptBindingKind::Invalid;
}
ScriptBindingValueType scriptValueType() const
@@ -852,7 +875,7 @@ public:
}
};
-struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSMetaSignalHandler
+struct Q_QMLCOMPILER_EXPORT QQmlJSMetaSignalHandler
{
QStringList signalParameters;
bool isMultiline;
diff --git a/src/qmlcompiler/qqmljsoptimizations.cpp b/src/qmlcompiler/qqmljsoptimizations.cpp
new file mode 100644
index 0000000000..1d0a7cf415
--- /dev/null
+++ b/src/qmlcompiler/qqmljsoptimizations.cpp
@@ -0,0 +1,531 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmljsoptimizations_p.h"
+#include "qqmljsbasicblocks_p.h"
+#include "qqmljsutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::Literals::StringLiterals;
+
+QQmlJSCompilePass::BlocksAndAnnotations QQmlJSOptimizations::run(const Function *function,
+ QQmlJS::DiagnosticMessage *error)
+{
+ m_function = function;
+ m_error = error;
+
+ populateBasicBlocks();
+ populateReaderLocations();
+ adjustTypes();
+
+ return { std::move(m_basicBlocks), std::move(m_annotations) };
+}
+
+struct PendingBlock
+{
+ QQmlJSOptimizations::Conversions conversions;
+ int start = -1;
+ bool registerActive = false;
+};
+
+template<typename ContainerA, typename ContainerB>
+static bool containsAny(const ContainerA &container, const ContainerB &elements)
+{
+ for (const auto &element : elements) {
+ if (container.contains(element))
+ return true;
+ }
+ return false;
+}
+
+template<class Key, class T, class Compare = std::less<Key>,
+ class KeyContainer = QList<Key>, class MappedContainer = QList<T>>
+class NewFlatMap
+{
+public:
+ using OriginalFlatMap = QFlatMap<Key, T, Compare, KeyContainer, MappedContainer>;
+
+ void appendOrdered(const typename OriginalFlatMap::iterator &i)
+ {
+ keys.append(i.key());
+ values.append(i.value());
+ }
+
+ OriginalFlatMap take()
+ {
+ OriginalFlatMap result(Qt::OrderedUniqueRange, std::move(keys), std::move(values));
+ keys.clear();
+ values.clear();
+ return result;
+ }
+
+private:
+ typename OriginalFlatMap::key_container_type keys;
+ typename OriginalFlatMap::mapped_container_type values;
+};
+
+void QQmlJSOptimizations::populateReaderLocations()
+{
+ using NewInstructionAnnotations = NewFlatMap<int, InstructionAnnotation>;
+
+ bool erasedReaders = false;
+ auto eraseDeadStore = [&](const InstructionAnnotations::iterator &it) {
+ auto reader = m_readerLocations.find(it.key());
+ if (reader != m_readerLocations.end()
+ && (reader->typeReaders.isEmpty() || reader->registerReadersAndConversions.isEmpty())) {
+
+ if (it->second.isRename) {
+ // If it's a rename, it doesn't "own" its output type. The type may
+ // still be read elsewhere, even if this register isn't. However, we're
+ // not interested in the variant or any other details of the register.
+ // Therefore just delete it.
+ it->second.changedRegisterIndex = InvalidRegister;
+ it->second.changedRegister = QQmlJSRegisterContent();
+ } else {
+ // void the output, rather than deleting it. We still need its variant.
+ bool adjusted = m_typeResolver->adjustTrackedType(
+ it->second.changedRegister.storedType(), m_typeResolver->voidType());
+ Q_ASSERT(adjusted); // Can always convert to void
+
+ adjusted = m_typeResolver->adjustTrackedType(
+ m_typeResolver->containedType(it->second.changedRegister),
+ m_typeResolver->voidType());
+ Q_ASSERT(adjusted); // Can always convert to void
+ }
+ m_readerLocations.erase(reader);
+
+ // If it's not a label and has no side effects, we can drop the instruction.
+ if (!it->second.hasSideEffects) {
+ if (!it->second.readRegisters.isEmpty()) {
+ it->second.readRegisters.clear();
+ erasedReaders = true;
+ }
+ if (m_basicBlocks.find(it.key()) == m_basicBlocks.end())
+ return true;
+ }
+ }
+ return false;
+ };
+
+ NewInstructionAnnotations newAnnotations;
+ for (auto writeIt = m_annotations.begin(), writeEnd = m_annotations.end();
+ writeIt != writeEnd; ++writeIt) {
+ const int writtenRegister = writeIt->second.changedRegisterIndex;
+ if (writtenRegister == InvalidRegister) {
+ newAnnotations.appendOrdered(writeIt);
+ continue;
+ }
+
+ RegisterAccess &access = m_readerLocations[writeIt.key()];
+ access.trackedRegister = writtenRegister;
+ if (writeIt->second.changedRegister.isConversion()) {
+ // If it's a conversion, we have to check for all readers of the conversion origins.
+ // This happens at jump targets where different types are merged. A StoreReg or similar
+ // instruction must be optimized out if none of the types it can hold is read anymore.
+ access.trackedTypes = writeIt->second.changedRegister.conversionOrigins();
+ } else {
+ access.trackedTypes.append(
+ m_typeResolver->trackedContainedType(writeIt->second.changedRegister));
+ }
+
+ auto blockIt = QQmlJSBasicBlocks::basicBlockForInstruction(m_basicBlocks, writeIt.key());
+ QList<PendingBlock> blocks = { { {}, blockIt->first, true } };
+ QHash<int, PendingBlock> processedBlocks;
+ bool isFirstBlock = true;
+
+ while (!blocks.isEmpty()) {
+ const PendingBlock block = blocks.takeLast();
+
+ // We can re-enter the first block from the beginning.
+ // We will then find any reads before the write we're currently examining.
+ if (!isFirstBlock)
+ processedBlocks.insert(block.start, block);
+
+ auto nextBlock = m_basicBlocks.find(block.start);
+ auto currentBlock = nextBlock++;
+ bool registerActive = block.registerActive;
+ Conversions conversions = block.conversions;
+
+ const auto blockEnd = (nextBlock == m_basicBlocks.end())
+ ? m_annotations.end()
+ : m_annotations.find(nextBlock->first);
+
+ auto blockInstr = isFirstBlock
+ ? (writeIt + 1)
+ : m_annotations.find(currentBlock->first);
+ for (; blockInstr != blockEnd; ++blockInstr) {
+ if (registerActive
+ && blockInstr->second.typeConversions.contains(writtenRegister)) {
+ conversions.insert(blockInstr.key());
+ }
+
+ for (auto readIt = blockInstr->second.readRegisters.constBegin(),
+ end = blockInstr->second.readRegisters.constEnd();
+ readIt != end; ++readIt) {
+ if (!blockInstr->second.isRename && containsAny(
+ readIt->second.content.conversionOrigins(), access.trackedTypes)) {
+ Q_ASSERT(readIt->second.content.isConversion());
+ Q_ASSERT(readIt->second.content.conversionResult());
+ access.typeReaders[blockInstr.key()]
+ = readIt->second.content.conversionResult();
+ }
+ if (registerActive && readIt->first == writtenRegister)
+ access.registerReadersAndConversions[blockInstr.key()] = conversions;
+ }
+
+ if (blockInstr->second.changedRegisterIndex == writtenRegister) {
+ conversions.clear();
+ registerActive = false;
+ }
+ }
+
+ auto scheduleBlock = [&](int blockStart) {
+ // If we find that an already processed block has the register activated by this jump,
+ // we need to re-evaluate it. We also need to propagate any newly found conversions.
+ const auto processed = processedBlocks.find(blockStart);
+ if (processed == processedBlocks.end()) {
+ blocks.append({conversions, blockStart, registerActive});
+ } else if (registerActive && !processed->registerActive) {
+ blocks.append({conversions, blockStart, registerActive});
+ } else {
+
+ // TODO: Use unite() once it is fixed.
+ // We don't use unite() here since it would be more expensive. unite()
+ // effectively loops on only insert() and insert() does a number of checks
+ // each time. We trade those checks for calculating the hash twice on each
+ // iteration. Calculating the hash is very cheap for integers.
+ Conversions merged = processed->conversions;
+ for (const int conversion : std::as_const(conversions)) {
+ if (!merged.contains(conversion))
+ merged.insert(conversion);
+ }
+
+ if (merged.size() > processed->conversions.size())
+ blocks.append({std::move(merged), blockStart, registerActive});
+ }
+ };
+
+ if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end())
+ scheduleBlock(nextBlock->first);
+
+ const int jumpTarget = currentBlock->second.jumpTarget;
+ if (jumpTarget != -1)
+ scheduleBlock(jumpTarget);
+
+ if (isFirstBlock)
+ isFirstBlock = false;
+ }
+
+ if (!eraseDeadStore(writeIt))
+ newAnnotations.appendOrdered(writeIt);
+ }
+ m_annotations = newAnnotations.take();
+
+ while (erasedReaders) {
+ erasedReaders = false;
+
+ for (auto it = m_annotations.begin(), end = m_annotations.end(); it != end; ++it) {
+ InstructionAnnotation &instruction = it->second;
+ if (instruction.changedRegisterIndex < InvalidRegister) {
+ newAnnotations.appendOrdered(it);
+ continue;
+ }
+
+ auto readers = m_readerLocations.find(it.key());
+ if (readers != m_readerLocations.end()) {
+ for (auto typeIt = readers->typeReaders.begin();
+ typeIt != readers->typeReaders.end();) {
+ if (m_annotations.contains(typeIt.key()))
+ ++typeIt;
+ else
+ typeIt = readers->typeReaders.erase(typeIt);
+ }
+
+ for (auto registerIt = readers->registerReadersAndConversions.begin();
+ registerIt != readers->registerReadersAndConversions.end();) {
+ if (m_annotations.contains(registerIt.key()))
+ ++registerIt;
+ else
+ registerIt = readers->registerReadersAndConversions.erase(registerIt);
+ }
+ }
+
+ if (!eraseDeadStore(it))
+ newAnnotations.appendOrdered(it);
+ }
+
+ m_annotations = newAnnotations.take();
+ }
+}
+
+bool QQmlJSOptimizations::canMove(int instructionOffset,
+ const QQmlJSOptimizations::RegisterAccess &access) const
+{
+ if (access.registerReadersAndConversions.size() != 1)
+ return false;
+ return QQmlJSBasicBlocks::basicBlockForInstruction(m_basicBlocks, instructionOffset)
+ == QQmlJSBasicBlocks::basicBlockForInstruction(m_basicBlocks, access.registerReadersAndConversions.begin().key());
+}
+
+QList<QQmlJSCompilePass::ObjectOrArrayDefinition>
+QQmlJSBasicBlocks::objectAndArrayDefinitions() const
+{
+ return m_objectAndArrayDefinitions;
+}
+
+static QString adjustErrorMessage(
+ const QQmlJSScope::ConstPtr &origin, const QQmlJSScope::ConstPtr &conversion) {
+ return QLatin1String("Cannot convert from ")
+ + origin->internalName() + QLatin1String(" to ") + conversion->internalName();
+}
+
+static QString adjustErrorMessage(
+ const QQmlJSScope::ConstPtr &origin, const QList<QQmlJSScope::ConstPtr> &conversions) {
+ if (conversions.size() == 1)
+ return adjustErrorMessage(origin, conversions[0]);
+
+ QString types;
+ for (const QQmlJSScope::ConstPtr &type : conversions) {
+ if (!types.isEmpty())
+ types += QLatin1String(", ");
+ types += type->internalName();
+ }
+ return QLatin1String("Cannot convert from ")
+ + origin->internalName() + QLatin1String(" to union of ") + types;
+}
+
+void QQmlJSOptimizations::adjustTypes()
+{
+ using NewVirtualRegisters = NewFlatMap<int, VirtualRegister>;
+
+ QHash<int, QList<int>> liveConversions;
+ QHash<int, QList<int>> movableReads;
+
+ const auto handleRegisterReadersAndConversions
+ = [&](QHash<int, RegisterAccess>::const_iterator it) {
+ for (auto conversions = it->registerReadersAndConversions.constBegin(),
+ end = it->registerReadersAndConversions.constEnd(); conversions != end;
+ ++conversions) {
+ if (conversions->isEmpty() && canMove(it.key(), it.value()))
+ movableReads[conversions.key()].append(it->trackedRegister);
+ for (int conversion : *conversions)
+ liveConversions[conversion].append(it->trackedRegister);
+ }
+ };
+
+ const auto transformRegister = [&](const QQmlJSRegisterContent &content) {
+ const QQmlJSScope::ConstPtr conversion
+ = m_typeResolver->storedType(m_typeResolver->containedType(content));
+ if (!m_typeResolver->adjustTrackedType(content.storedType(), conversion))
+ setError(adjustErrorMessage(content.storedType(), conversion));
+ };
+
+ // Handle the array definitions first.
+ // Changing the array type changes the expected element types.
+ auto adjustArray = [&](int instructionOffset, int mode) {
+ auto it = m_readerLocations.find(instructionOffset);
+ if (it == m_readerLocations.end())
+ return;
+
+ const InstructionAnnotation &annotation = m_annotations[instructionOffset];
+ if (annotation.readRegisters.isEmpty())
+ return;
+
+ Q_ASSERT(it->trackedTypes.size() == 1);
+ Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister));
+
+ if (it->trackedTypes[0]->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
+ return; // Constructed something else.
+
+ if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
+ setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
+
+ // Now we don't adjust the type we store, but rather the type we expect to read. We
+ // can do this because we've tracked the read type when we defined the array in
+ // QQmlJSTypePropagator.
+ if (QQmlJSScope::ConstPtr valueType = it->trackedTypes[0]->valueType()) {
+ const QQmlJSRegisterContent content = annotation.readRegisters.begin().value().content;
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
+
+ // If it's the 1-arg Array ctor, and the argument is a number, that's special.
+ if (mode != ObjectOrArrayDefinition::ArrayConstruct1ArgId
+ || !m_typeResolver->equals(contained, m_typeResolver->realType())) {
+ if (!m_typeResolver->adjustTrackedType(contained, valueType))
+ setError(adjustErrorMessage(contained, valueType));
+
+ // We still need to adjust the stored type, too.
+ transformRegister(content);
+ }
+ }
+
+ handleRegisterReadersAndConversions(it);
+ m_readerLocations.erase(it);
+ };
+
+ // Handle the object definitions.
+ // Changing the object type changes the expected property types.
+ const auto adjustObject = [&](const ObjectOrArrayDefinition &object) {
+ auto it = m_readerLocations.find(object.instructionOffset);
+ if (it == m_readerLocations.end())
+ return;
+
+ const InstructionAnnotation &annotation = m_annotations[object.instructionOffset];
+
+ Q_ASSERT(it->trackedTypes.size() == 1);
+ QQmlJSScope::ConstPtr resultType = it->trackedTypes[0];
+
+ Q_ASSERT(resultType == m_typeResolver->containedType(annotation.changedRegister));
+ Q_ASSERT(!annotation.readRegisters.isEmpty());
+
+ if (!m_typeResolver->adjustTrackedType(resultType, it->typeReaders.values()))
+ setError(adjustErrorMessage(resultType, it->typeReaders.values()));
+
+ if (m_typeResolver->equals(resultType, m_typeResolver->varType())
+ || m_typeResolver->equals(resultType, m_typeResolver->variantMapType())) {
+ // It's all variant anyway
+ return;
+ }
+
+ const int classSize = m_jsUnitGenerator->jsClassSize(object.internalClassId);
+ Q_ASSERT(object.argc >= classSize);
+
+ for (int i = 0; i < classSize; ++i) {
+ // Now we don't adjust the type we store, but rather the types we expect to read. We
+ // can do this because we've tracked the read types when we defined the object in
+ // QQmlJSTypePropagator.
+
+ const QString propName = m_jsUnitGenerator->jsClassMember(object.internalClassId, i);
+ const QQmlJSMetaProperty property = resultType->property(propName);
+ if (!property.isValid()) {
+ setError(resultType->internalName() + QLatin1String(" has no property called ")
+ + propName);
+ continue;
+ }
+ const QQmlJSScope::ConstPtr propType = property.type();
+ if (propType.isNull()) {
+ setError(QLatin1String("Cannot resolve type of property ") + propName);
+ continue;
+ }
+ const QQmlJSRegisterContent content = annotation.readRegisters[object.argv + i].content;
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
+ if (!m_typeResolver->adjustTrackedType(contained, propType))
+ setError(adjustErrorMessage(contained, propType));
+
+ // We still need to adjust the stored type, too.
+ transformRegister(content);
+ }
+
+ // The others cannot be adjusted. We don't know their names, yet.
+ // But we might still be able to use the variants.
+ };
+
+ // Iterate in reverse so that we can have nested lists and objects and the types are propagated
+ // from the outer lists/objects to the inner ones.
+ for (auto it = m_objectAndArrayDefinitions.crbegin(), end = m_objectAndArrayDefinitions.crend();
+ it != end; ++it) {
+ switch (it->internalClassId) {
+ case ObjectOrArrayDefinition::ArrayClassId:
+ case ObjectOrArrayDefinition::ArrayConstruct1ArgId:
+ adjustArray(it->instructionOffset, it->internalClassId);
+ break;
+ default:
+ adjustObject(*it);
+ break;
+ }
+ }
+
+ for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) {
+ handleRegisterReadersAndConversions(it);
+
+ // There is always one first occurrence of any tracked type. Conversions don't change
+ // the type.
+ if (it->trackedTypes.size() != 1)
+ continue;
+
+ // Don't adjust renamed values. We only adjust the originals.
+ const int writeLocation = it.key();
+ if (writeLocation >= 0 && m_annotations[writeLocation].isRename)
+ continue;
+
+ if (!m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()))
+ setError(adjustErrorMessage(it->trackedTypes[0], it->typeReaders.values()));
+ }
+
+
+ NewVirtualRegisters newRegisters;
+ for (auto i = m_annotations.begin(), iEnd = m_annotations.end(); i != iEnd; ++i) {
+ if (i->second.changedRegisterIndex != InvalidRegister)
+ transformRegister(i->second.changedRegister);
+
+ for (auto conversion = i->second.typeConversions.begin(),
+ conversionEnd = i->second.typeConversions.end(); conversion != conversionEnd;
+ ++conversion) {
+ if (!liveConversions[i.key()].contains(conversion.key()))
+ continue;
+
+ QQmlJSScope::ConstPtr newResult;
+ const auto content = conversion->second.content;
+ if (content.isConversion()) {
+ QQmlJSScope::ConstPtr conversionResult = content.conversionResult();
+ const auto conversionOrigins = content.conversionOrigins();
+ for (const auto &origin : conversionOrigins)
+ newResult = m_typeResolver->merge(newResult, origin);
+ if (!m_typeResolver->adjustTrackedType(conversionResult, newResult))
+ setError(adjustErrorMessage(conversionResult, newResult));
+ }
+ transformRegister(content);
+ newRegisters.appendOrdered(conversion);
+ }
+ i->second.typeConversions = newRegisters.take();
+
+ for (int movable : std::as_const(movableReads[i.key()]))
+ i->second.readRegisters[movable].canMove = true;
+ }
+}
+
+void QQmlJSOptimizations::populateBasicBlocks()
+{
+ for (auto blockNext = m_basicBlocks.begin(), blockEnd = m_basicBlocks.end();
+ blockNext != blockEnd;) {
+
+ const auto blockIt = blockNext++;
+ BasicBlock &block = blockIt->second;
+ QList<QQmlJSScope::ConstPtr> writtenTypes;
+ QList<int> writtenRegisters;
+
+ const auto instrEnd = (blockNext == blockEnd) ? m_annotations.end()
+ : m_annotations.find(blockNext->first);
+ for (auto instrIt = m_annotations.find(blockIt->first); instrIt != instrEnd; ++instrIt) {
+ const InstructionAnnotation &instruction = instrIt->second;
+ for (auto it = instruction.readRegisters.begin(), end = instruction.readRegisters.end();
+ it != end; ++it) {
+ if (!instruction.isRename) {
+ Q_ASSERT(it->second.content.isConversion());
+ for (const QQmlJSScope::ConstPtr &origin :
+ it->second.content.conversionOrigins()) {
+ if (!writtenTypes.contains(origin))
+ block.readTypes.append(origin);
+ }
+ }
+ if (!writtenRegisters.contains(it->first))
+ block.readRegisters.append(it->first);
+ }
+
+ // If it's just a renaming, the type has existed in a different register before.
+ if (instruction.changedRegisterIndex != InvalidRegister) {
+ if (!instruction.isRename) {
+ writtenTypes.append(m_typeResolver->trackedContainedType(
+ instruction.changedRegister));
+ }
+ writtenRegisters.append(instruction.changedRegisterIndex);
+ }
+ }
+
+ QQmlJSUtils::deduplicate(block.readTypes);
+ QQmlJSUtils::deduplicate(block.readRegisters);
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsoptimizations_p.h b/src/qmlcompiler/qqmljsoptimizations_p.h
new file mode 100644
index 0000000000..0d7091ab49
--- /dev/null
+++ b/src/qmlcompiler/qqmljsoptimizations_p.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLJSOPTIMIZATIONS_P_H
+#define QQMLJSOPTIMIZATIONS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include <private/qqmljscompilepass_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QMLCOMPILER_EXPORT QQmlJSOptimizations : public QQmlJSCompilePass
+{
+public:
+ using Conversions = QSet<int>;
+
+ QQmlJSOptimizations(const QV4::Compiler::JSUnitGenerator *unitGenerator,
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks, InstructionAnnotations annotations,
+ QList<ObjectOrArrayDefinition> objectAndArrayDefinitions)
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations),
+ m_objectAndArrayDefinitions{ objectAndArrayDefinitions }
+ {
+ }
+
+ ~QQmlJSOptimizations() = default;
+
+ BlocksAndAnnotations run(const Function *function, QQmlJS::DiagnosticMessage *error);
+
+private:
+ struct RegisterAccess
+ {
+ QList<QQmlJSScope::ConstPtr> trackedTypes;
+ QHash<int, QQmlJSScope::ConstPtr> typeReaders;
+ QHash<int, Conversions> registerReadersAndConversions;
+ int trackedRegister;
+ };
+
+ QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type) override
+ {
+ return ProcessInstruction;
+ }
+ void endInstruction(QV4::Moth::Instr::Type) override { }
+
+ void populateBasicBlocks();
+ void populateReaderLocations();
+ void adjustTypes();
+ bool canMove(int instructionOffset, const RegisterAccess &access) const;
+
+ QHash<int, RegisterAccess> m_readerLocations;
+ QList<ObjectOrArrayDefinition> m_objectAndArrayDefinitions;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSOPTIMIZATIONS_P_H
diff --git a/src/qmlcompiler/qqmljsregistercontent.cpp b/src/qmlcompiler/qqmljsregistercontent.cpp
index 1573f889a1..2c5d562e5b 100644
--- a/src/qmlcompiler/qqmljsregistercontent.cpp
+++ b/src/qmlcompiler/qqmljsregistercontent.cpp
@@ -13,7 +13,6 @@ QString QQmlJSRegisterContent::descriptiveName() const
if (m_storedType.isNull())
return u"(invalid type)"_s;
- QString result = m_storedType->internalName() + u" of "_s;
const auto scope = [this]() -> QString {
if (m_scope.isNull())
return u"(invalid type)::"_s;
@@ -25,35 +24,46 @@ QString QQmlJSRegisterContent::descriptiveName() const
+ u"::"_s;
};
+ QString result;
switch (m_content.index()) {
- case Type:
- return result + std::get<QQmlJSScope::ConstPtr>(m_content)->internalName();
+ case Type: {
+ auto contained = std::get<std::pair<QQmlJSScope::ConstPtr, int>>(m_content).first;
+ result += contained->internalName();
+ if (m_storedType->internalName() != contained->internalName())
+ result += u" stored as "_s + m_storedType->internalName();
+ return result;
+ }
case Property: {
- const QQmlJSMetaProperty prop = std::get<QQmlJSMetaProperty>(m_content);
- return result + scope() + prop.propertyName() + u" with type "_s + prop.typeName();
+ const QQmlJSMetaProperty prop = std::get<PropertyLookup>(m_content).property;
+ result += scope() + prop.propertyName() + u" with type "_s + prop.typeName();
+ if (m_storedType->internalName() != prop.typeName())
+ result += u" (stored as "_s + m_storedType->internalName() + u")";
+ return result;
}
case Method: {
const auto methods = std::get<QList<QQmlJSMetaMethod>>(m_content);
if (methods.isEmpty())
- return result + scope() + u"(unknown method)"_s;
+ result = scope() + u"(unknown method)"_s;
else
- return result + scope() + methods[0].methodName() + u"(...)"_s;
+ result = scope() + methods[0].methodName() + u"(...)"_s;
+ return result + u" (stored as "_s + m_storedType->internalName() + u")";
}
case Enum: {
const auto e = std::get<std::pair<QQmlJSMetaEnum, QString>>(m_content);
if (e.second.isEmpty())
- return result + scope() + e.first.name();
+ result = scope() + e.first.name();
else
- return result + scope() + e.first.name() + u"::"_s + e.second;
+ result = scope() + e.first.name() + u"::"_s + e.second;
+ return result + u" (stored as "_s + m_storedType->internalName() + u")";
}
case ImportNamespace: {
return u"import namespace %1"_s.arg(std::get<uint>(m_content));
}
case Conversion: {
- return u"conversion to %1"_s.arg(
- std::get<ConvertedTypes>(m_content).result->internalName());
+ return u"conversion to %1"_s.arg(std::get<ConvertedTypes>(m_content).result->internalName());
}
}
+
Q_UNREACHABLE_RETURN(result + u"wat?"_s);
}
@@ -61,10 +71,10 @@ bool QQmlJSRegisterContent::isList() const
{
switch (m_content.index()) {
case Type:
- return std::get<QQmlJSScope::ConstPtr>(m_content)->accessSemantics()
+ return std::get<std::pair<QQmlJSScope::ConstPtr, int>>(m_content).first->accessSemantics()
== QQmlJSScope::AccessSemantics::Sequence;
case Property:
- return std::get<QQmlJSMetaProperty>(m_content).type()->accessSemantics()
+ return std::get<PropertyLookup>(m_content).property.type()->accessSemantics()
== QQmlJSScope::AccessSemantics::Sequence;
case Conversion:
return std::get<ConvertedTypes>(m_content).result->accessSemantics()
@@ -78,7 +88,7 @@ bool QQmlJSRegisterContent::isWritable() const
{
switch (m_content.index()) {
case Property:
- return std::get<QQmlJSMetaProperty>(m_content).isWritable();
+ return std::get<PropertyLookup>(m_content).property.isWritable();
// TODO: What can we actually write?
default:
@@ -90,21 +100,23 @@ bool QQmlJSRegisterContent::isWritable() const
QQmlJSRegisterContent QQmlJSRegisterContent::create(const QQmlJSScope::ConstPtr &storedType,
const QQmlJSScope::ConstPtr &type,
+ int resultLookupIndex,
QQmlJSRegisterContent::ContentVariant variant,
const QQmlJSScope::ConstPtr &scope)
{
QQmlJSRegisterContent result(storedType, scope, variant);
- result.m_content = type;
+ result.m_content = std::make_pair(type, resultLookupIndex);
return result;
}
QQmlJSRegisterContent QQmlJSRegisterContent::create(const QQmlJSScope::ConstPtr &storedType,
const QQmlJSMetaProperty &property,
+ int baseLookupIndex, int resultLookupIndex,
QQmlJSRegisterContent::ContentVariant variant,
const QQmlJSScope::ConstPtr &scope)
{
QQmlJSRegisterContent result(storedType, scope, variant);
- result.m_content = property;
+ result.m_content = PropertyLookup { property, baseLookupIndex, resultLookupIndex};
return result;
}
@@ -140,13 +152,14 @@ QQmlJSRegisterContent QQmlJSRegisterContent::create(const QQmlJSScope::ConstPtr
}
QQmlJSRegisterContent QQmlJSRegisterContent::create(const QQmlJSScope::ConstPtr &storedType,
- const QList<QQmlJSScope::ConstPtr> origins,
+ const QList<QQmlJSScope::ConstPtr> &origins,
const QQmlJSScope::ConstPtr &conversion,
+ const QQmlJSScope::ConstPtr &conversionScope,
ContentVariant variant,
const QQmlJSScope::ConstPtr &scope)
{
QQmlJSRegisterContent result(storedType, scope, variant);
- result.m_content = ConvertedTypes { origins, conversion };
+ result.m_content = ConvertedTypes { origins, conversion, conversionScope };
return result;
}
diff --git a/src/qmlcompiler/qqmljsregistercontent_p.h b/src/qmlcompiler/qqmljsregistercontent_p.h
index 1ca44fd0c3..bbf5008399 100644
--- a/src/qmlcompiler/qqmljsregistercontent_p.h
+++ b/src/qmlcompiler/qqmljsregistercontent_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSRegisterContent
+class Q_QMLCOMPILER_EXPORT QQmlJSRegisterContent
{
public:
enum ContentVariant {
@@ -34,7 +34,7 @@ public:
JavaScriptGlobal,
JavaScriptObject,
JavaScriptScopeProperty,
- JavaScriptObjectProperty,
+ GenericObjectProperty, // Can be JSObject property or QVariantMap
ScopeProperty,
ScopeMethod,
@@ -56,10 +56,13 @@ public:
JavaScriptReturnValue,
ListValue,
+ ListIterator,
Builtin,
Unknown,
};
+ enum { InvalidLookupIndex = -1 };
+
QQmlJSRegisterContent() = default;
bool isValid() const { return !m_storedType.isNull(); }
@@ -89,8 +92,29 @@ public:
QQmlJSScope::ConstPtr storedType() const { return m_storedType; }
QQmlJSScope::ConstPtr scopeType() const { return m_scope; }
- QQmlJSScope::ConstPtr type() const { return std::get<QQmlJSScope::ConstPtr>(m_content); }
- QQmlJSMetaProperty property() const { return std::get<QQmlJSMetaProperty>(m_content); }
+ QQmlJSScope::ConstPtr type() const
+ {
+ return std::get<std::pair<QQmlJSScope::ConstPtr, int>>(m_content).first;
+ }
+ QQmlJSMetaProperty property() const
+ {
+ return std::get<PropertyLookup>(m_content).property;
+ }
+ int baseLookupIndex() const
+ {
+ return std::get<PropertyLookup>(m_content).baseLookupIndex;
+ }
+ int resultLookupIndex() const
+ {
+ switch (m_content.index()) {
+ case Type:
+ return std::get<std::pair<QQmlJSScope::ConstPtr, int>>(m_content).second;
+ case Property:
+ return std::get<PropertyLookup>(m_content).resultLookupIndex;
+ default:
+ return InvalidLookupIndex;
+ }
+ }
QQmlJSMetaEnum enumeration() const
{
return std::get<std::pair<QQmlJSMetaEnum, QString>>(m_content).first;
@@ -107,6 +131,11 @@ public:
return std::get<ConvertedTypes>(m_content).result;
}
+ QQmlJSScope::ConstPtr conversionResultScope() const
+ {
+ return std::get<ConvertedTypes>(m_content).resultScope;
+ }
+
QList<QQmlJSScope::ConstPtr> conversionOrigins() const
{
return std::get<ConvertedTypes>(m_content).origins;
@@ -120,9 +149,10 @@ public:
registerContent.m_scope, registerContent.m_variant);
switch (registerContent.m_content.index()) {
case Type:
- return qHash(std::get<QQmlJSScope::ConstPtr>(registerContent.m_content), seed);
+ return qHash(std::get<std::pair<QQmlJSScope::ConstPtr, int>>(registerContent.m_content),
+ seed);
case Property:
- return qHash(std::get<QQmlJSMetaProperty>(registerContent.m_content), seed);
+ return qHash(std::get<PropertyLookup>(registerContent.m_content), seed);
case Enum:
return qHash(std::get<std::pair<QQmlJSMetaEnum, QString>>(registerContent.m_content),
seed);
@@ -138,11 +168,14 @@ public:
}
static QQmlJSRegisterContent create(const QQmlJSScope::ConstPtr &storedType,
- const QQmlJSScope::ConstPtr &type, ContentVariant variant,
+ const QQmlJSScope::ConstPtr &type,
+ int resultLookupIndex, ContentVariant variant,
const QQmlJSScope::ConstPtr &scope = {});
static QQmlJSRegisterContent create(const QQmlJSScope::ConstPtr &storedType,
- const QQmlJSMetaProperty &property, ContentVariant variant,
+ const QQmlJSMetaProperty &property,
+ int baseLookupIndex, int resultLookupIndex,
+ ContentVariant variant,
const QQmlJSScope::ConstPtr &scope);
static QQmlJSRegisterContent create(const QQmlJSScope::ConstPtr &storedType,
@@ -160,8 +193,9 @@ public:
const QQmlJSScope::ConstPtr &scope = {});
static QQmlJSRegisterContent create(const QQmlJSScope::ConstPtr &storedType,
- const QList<QQmlJSScope::ConstPtr> origins,
+ const QList<QQmlJSScope::ConstPtr> &origins,
const QQmlJSScope::ConstPtr &conversion,
+ const QQmlJSScope::ConstPtr &conversionScope,
ContentVariant variant,
const QQmlJSScope::ConstPtr &scope = {});
@@ -172,6 +206,14 @@ public:
return result;
}
+ QQmlJSRegisterContent castTo(const QQmlJSScope::ConstPtr &newContainedType) const
+ {
+ // This is not a conversion but a run time cast. It may result in null or undefined.
+ QQmlJSRegisterContent result = *this;
+ result.m_content = std::make_pair(newContainedType, result.resultLookupIndex());
+ return result;
+ }
+
private:
enum ContentKind { Type, Property, Enum, Method, ImportNamespace, Conversion };
@@ -179,15 +221,16 @@ private:
{
QList<QQmlJSScope::ConstPtr> origins;
QQmlJSScope::ConstPtr result;
+ QQmlJSScope::ConstPtr resultScope;
friend size_t qHash(const ConvertedTypes &types, size_t seed = 0)
{
- return qHashMulti(seed, types.origins, types.result);
+ return qHashMulti(seed, types.origins, types.result, types.resultScope);
}
friend bool operator==(const ConvertedTypes &a, const ConvertedTypes &b)
{
- return a.origins == b.origins && a.result == b.result;
+ return a.origins == b.origins && a.result == b.result && a.resultScope == b.resultScope;
}
friend bool operator!=(const ConvertedTypes &a, const ConvertedTypes &b)
@@ -196,9 +239,34 @@ private:
}
};
+ struct PropertyLookup
+ {
+ QQmlJSMetaProperty property;
+ int baseLookupIndex = InvalidLookupIndex;
+ int resultLookupIndex = InvalidLookupIndex;
+
+ friend size_t qHash(const PropertyLookup &property, size_t seed = 0)
+ {
+ return qHashMulti(
+ seed, property.property, property.baseLookupIndex, property.resultLookupIndex);
+ }
+
+ friend bool operator==(const PropertyLookup &a, const PropertyLookup &b)
+ {
+ return a.baseLookupIndex == b.baseLookupIndex
+ && a.resultLookupIndex == b.resultLookupIndex
+ && a.property == b.property;
+ }
+
+ friend bool operator!=(const PropertyLookup &a, const PropertyLookup &b)
+ {
+ return !(a == b);
+ }
+ };
+
using Content = std::variant<
- QQmlJSScope::ConstPtr,
- QQmlJSMetaProperty,
+ std::pair<QQmlJSScope::ConstPtr, int>,
+ PropertyLookup,
std::pair<QQmlJSMetaEnum, QString>,
QList<QQmlJSMetaMethod>,
uint,
diff --git a/src/qmlcompiler/qqmljsresourcefilemapper_p.h b/src/qmlcompiler/qqmljsresourcefilemapper_p.h
index 618cc2edec..16fb99e82e 100644
--- a/src/qmlcompiler/qqmljsresourcefilemapper_p.h
+++ b/src/qmlcompiler/qqmljsresourcefilemapper_p.h
@@ -13,7 +13,7 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include <QStringList>
#include <QHash>
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSResourceFileMapper
+struct Q_QMLCOMPILER_EXPORT QQmlJSResourceFileMapper
{
struct Entry
{
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index e3d8bafeb3..b32f86226e 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -1,10 +1,13 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "qqmljscontextualtypes_p.h"
#include "qqmljsscope_p.h"
#include "qqmljstypereader_p.h"
#include "qqmljsimporter_p.h"
#include "qqmljsutils_p.h"
+#include "qqmlsa.h"
+#include "qqmlsa_p.h"
#include <QtCore/qqueue.h>
#include <QtCore/qsharedpointer.h>
@@ -26,13 +29,20 @@ QT_BEGIN_NAMESPACE
Multiple QQmlJSScope objects might be created for the same conceptual type, except when reused
due to extensive caching. Two QQmlJSScope objects are considered equal when they are backed
by the same implementation, that is, they have the same internalName.
- The qualifiedName of the QQmlJSScope for a type imported from multiple modules will contain the
- name of one of the modules that imported it, which is not unique and might change depending
- on the caching in .
*/
using namespace Qt::StringLiterals;
+QQmlJSScope::QQmlJSScope(const QString &internalName) : QQmlJSScope{}
+{
+ m_internalName = internalName;
+}
+
+QQmlJSScope::Ptr QQmlJSScope::create(const QString &internalName)
+{
+ return QSharedPointer<QQmlJSScope>(new QQmlJSScope(internalName));
+}
+
void QQmlJSScope::reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope)
{
if (const QQmlJSScope::Ptr parent = childScope->m_parentScope.toStrongRef())
@@ -53,16 +63,25 @@ QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin)
return cloned;
}
+/*!
+\internal
+Return all the JavaScript identifiers defined in the current scope.
+*/
+QHash<QString, QQmlJSScope::JavaScriptIdentifier> QQmlJSScope::ownJSIdentifiers() const
+{
+ return m_jsIdentifiers;
+}
+
void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
{
- Q_ASSERT(m_scopeType != QQmlJSScope::QMLScope);
+ Q_ASSERT(m_scopeType != QQmlSA::ScopeType::QMLScope);
if (identifier.kind == JavaScriptIdentifier::LexicalScoped
- || identifier.kind == JavaScriptIdentifier::Injected
- || m_scopeType == QQmlJSScope::JSFunctionScope) {
+ || identifier.kind == JavaScriptIdentifier::Injected
+ || m_scopeType == QQmlSA::ScopeType::JSFunctionScope) {
m_jsIdentifiers.insert(name, identifier);
} else {
auto targetScope = parentScope();
- while (targetScope->m_scopeType != QQmlJSScope::JSFunctionScope)
+ while (targetScope->m_scopeType != QQmlSA::ScopeType::JSFunctionScope)
targetScope = targetScope->parentScope();
targetScope->m_jsIdentifiers.insert(name, identifier);
}
@@ -71,17 +90,13 @@ void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdenti
void QQmlJSScope::insertPropertyIdentifier(const QQmlJSMetaProperty &property)
{
addOwnProperty(property);
- QQmlJSMetaMethod method(property.propertyName() + u"Changed"_s, u"void"_s);
- method.setMethodType(QQmlJSMetaMethod::Signal);
+ QQmlJSMetaMethod method(
+ QQmlSignalNames::propertyNameToChangedSignalName(property.propertyName()), u"void"_s);
+ method.setMethodType(QQmlJSMetaMethodType::Signal);
method.setIsImplicitQmlPropertyChangeSignal(true);
addOwnMethod(method);
}
-bool QQmlJSScope::isIdInCurrentScope(const QString &id) const
-{
- return isIdInCurrentQmlScopes(id) || isIdInCurrentJSScopes(id);
-}
-
bool QQmlJSScope::hasMethod(const QString &name) const
{
return QQmlJSUtils::searchBaseAndExtensionTypes(
@@ -133,7 +148,7 @@ QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name) const
return results;
}
-QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name, QQmlJSMetaMethod::Type type) const
+QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name, QQmlJSMetaMethodType type) const
{
QList<QQmlJSMetaMethod> results;
@@ -157,15 +172,19 @@ bool QQmlJSScope::hasEnumeration(const QString &name) const
this, [&](const QQmlJSScope *scope) { return scope->m_enumerations.contains(name); });
}
+bool QQmlJSScope::hasOwnEnumerationKey(const QString &name) const
+{
+ for (const auto &e : m_enumerations) {
+ if (e.keys().contains(name))
+ return true;
+ }
+ return false;
+}
+
bool QQmlJSScope::hasEnumerationKey(const QString &name) const
{
- return QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
- for (const auto &e : scope->m_enumerations) {
- if (e.keys().contains(name))
- return true;
- }
- return false;
- });
+ return QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope) { return scope->hasOwnEnumerationKey(name); });
}
QQmlJSMetaEnum QQmlJSScope::enumeration(const QString &name) const
@@ -199,6 +218,36 @@ QHash<QString, QQmlJSMetaEnum> QQmlJSScope::enumerations() const
return results;
}
+QString QQmlJSScope::augmentedInternalName() const
+{
+ using namespace Qt::StringLiterals;
+
+ switch (m_semantics) {
+ case AccessSemantics::Reference:
+ return m_internalName + " *"_L1;
+ case AccessSemantics::Value:
+ case AccessSemantics::Sequence:
+ break;
+ case AccessSemantics::None:
+ // If we got a namespace, it might still be a regular type, exposed as namespace.
+ // We may need to travel the inheritance chain all the way up to QObject to
+ // figure this out, since all other types may be exposed the same way.
+ for (QQmlJSScope::ConstPtr base = baseType(); base; base = base->baseType()) {
+ switch (base->accessSemantics()) {
+ case AccessSemantics::Reference:
+ return m_internalName + " *"_L1;
+ case AccessSemantics::Value:
+ case AccessSemantics::Sequence:
+ return m_internalName;
+ case AccessSemantics::None:
+ break;
+ }
+ }
+ break;
+ }
+ return m_internalName;
+}
+
QString QQmlJSScope::prettyName(QAnyStringView name)
{
const auto internal = "$internal$."_L1;
@@ -221,47 +270,6 @@ QString QQmlJSScope::prettyName(QAnyStringView name)
}
/*!
- Returns if assigning \a assignedType to \a property would require an
- implicit component wrapping.
- */
-bool QQmlJSScope::causesImplicitComponentWrapping(const QQmlJSMetaProperty &property,
- const QQmlJSScope::ConstPtr &assignedType)
-{
- // See QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents()
- // for the logic in qqmltypecompiler
-
- // Note: unlike findAndRegisterImplicitComponents() we do not check whether
- // the property type is *derived* from QQmlComponent at some point because
- // this is actually meaningless (and in the case of QQmlComponent::create()
- // gets rejected in QQmlPropertyValidator): if the type is not a
- // QQmlComponent, we have a type mismatch because of assigning a Component
- // object to a non-Component property
- const bool propertyVerdict = property.type()->internalName() == u"QQmlComponent";
-
- const bool assignedTypeVerdict = [&assignedType]() {
- // Note: nonCompositeBaseType covers the case when assignedType itself
- // is non-composite
- auto cppBase = nonCompositeBaseType(assignedType);
- Q_ASSERT(cppBase); // any QML type has (or must have) a C++ base type
-
- // See isUsableComponent() in qqmltypecompiler.cpp: along with checking
- // whether a type has a QQmlComponent static meta object (which we
- // substitute here with checking the first non-composite base for being
- // a QQmlComponent), it also excludes QQmlAbstractDelegateComponent
- // subclasses from implicit wrapping
- if (cppBase->internalName() == u"QQmlComponent")
- return false;
- for (; cppBase; cppBase = cppBase->baseType()) {
- if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
- return false;
- }
- return true;
- }();
-
- return propertyVerdict && assignedTypeVerdict;
-}
-
-/*!
\internal
Returns true if the scope is the outermost element of a separate Component
Either because it has been implicitly wrapped, e.g. due to an assignment to
@@ -281,42 +289,12 @@ bool QQmlJSScope::isComponentRootElement() const {
return base->internalName() == u"QQmlComponent";
}
-bool QQmlJSScope::isIdInCurrentQmlScopes(const QString &id) const
-{
- if (m_scopeType == QQmlJSScope::QMLScope)
- return m_properties.contains(id) || m_methods.contains(id) || m_enumerations.contains(id);
-
- const auto qmlScope = findCurrentQMLScope(parentScope());
- return qmlScope->m_properties.contains(id)
- || qmlScope->m_methods.contains(id)
- || qmlScope->m_enumerations.contains(id);
-}
-
-bool QQmlJSScope::isIdInCurrentJSScopes(const QString &id) const
-{
- if (m_scopeType != QQmlJSScope::QMLScope && m_jsIdentifiers.contains(id))
- return true;
-
- for (auto jsScope = parentScope(); jsScope; jsScope = jsScope->parentScope()) {
- if (jsScope->m_scopeType != QQmlJSScope::QMLScope && jsScope->m_jsIdentifiers.contains(id))
- return true;
- }
-
- return false;
-}
-
-bool QQmlJSScope::isIdInjectedFromSignal(const QString &id) const
-{
- const auto found = findJSIdentifier(id);
- return found.has_value() && found->kind == JavaScriptIdentifier::Injected;
-}
-
std::optional<QQmlJSScope::JavaScriptIdentifier>
-QQmlJSScope::findJSIdentifier(const QString &id) const
+QQmlJSScope::jsIdentifier(const QString &id) const
{
for (const auto *scope = this; scope; scope = scope->parentScope().data()) {
- if (scope->m_scopeType == QQmlJSScope::JSFunctionScope
- || scope->m_scopeType == QQmlJSScope::JSLexicalScope) {
+ if (scope->m_scopeType == QQmlSA::ScopeType::JSFunctionScope
+ || scope->m_scopeType == QQmlSA::ScopeType::JSLexicalScope) {
auto it = scope->m_jsIdentifiers.find(id);
if (it != scope->m_jsIdentifiers.end())
return *it;
@@ -326,8 +304,17 @@ QQmlJSScope::findJSIdentifier(const QString &id) const
return std::optional<JavaScriptIdentifier>{};
}
+std::optional<QQmlJSScope::JavaScriptIdentifier> QQmlJSScope::ownJSIdentifier(const QString &id) const
+{
+ auto it = m_jsIdentifiers.find(id);
+ if (it != m_jsIdentifiers.end())
+ return *it;
+
+ return std::optional<JavaScriptIdentifier>{};
+}
+
static QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr>
-qFindInlineComponents(QStringView typeName, const QQmlJSScope::ContextualTypes &contextualTypes)
+qFindInlineComponents(QStringView typeName, const QQmlJS::ContextualTypes &contextualTypes)
{
const int separatorIndex = typeName.lastIndexOf(u'.');
// do not crash in typeName.sliced() when it starts or ends with an '.'.
@@ -363,8 +350,16 @@ qFindInlineComponents(QStringView typeName, const QQmlJSScope::ContextualTypes &
return {};
}
+/*! \internal
+ * Finds a type in contextualTypes with given name.
+ * If a type is found, then its name is inserted into usedTypes (when provided).
+ * If contextualTypes has mode INTERNAl, then namespace resolution for enums is
+ * done (eg for Qt::Alignment).
+ * If contextualTypes has mode QML, then inline component resolution is done
+ * ("qmlFileName.IC" is correctly resolved from qmlFileName).
+ */
QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr> QQmlJSScope::findType(
- const QString &name, const QQmlJSScope::ContextualTypes &contextualTypes,
+ const QString &name, const QQmlJS::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes)
{
const auto useType = [&]() {
@@ -397,7 +392,7 @@ QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr> QQmlJSScope::findType(
};
switch (contextualTypes.context()) {
- case ContextualTypes::INTERNAL: {
+ case QQmlJS::ContextualTypes::INTERNAL: {
if (const auto listType = findListType(u"QList<"_s, u">"_s);
listType.scope && !listType.scope->isReferenceType()) {
return listType;
@@ -427,7 +422,7 @@ QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr> QQmlJSScope::findType(
break;
}
- case ContextualTypes::QML: {
+ case QQmlJS::ContextualTypes::QML: {
// look after inline components
const auto inlineComponent = qFindInlineComponents(name, contextualTypes);
if (inlineComponent.scope) {
@@ -445,7 +440,7 @@ QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr> QQmlJSScope::findType(
}
QTypeRevision QQmlJSScope::resolveType(
- const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &context,
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &context,
QSet<QString> *usedTypes)
{
if (self->accessSemantics() == AccessSemantics::Sequence
@@ -469,6 +464,7 @@ QTypeRevision QQmlJSScope::resolveType(
if (self->accessSemantics() == AccessSemantics::Sequence) {
// All sequence types are implicitly extended by JS Array.
self->setExtensionTypeName(u"Array"_s);
+ self->setExtensionIsJavaScript(true);
self->m_extensionType = context.arrayType();
}
} else {
@@ -495,37 +491,47 @@ QTypeRevision QQmlJSScope::resolveType(
}
}
- for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) {
- const QString returnTypeName = it->returnTypeName();
- if (!it->returnType() && !returnTypeName.isEmpty()) {
- const auto returnType = findType(returnTypeName, context, usedTypes);
- it->setReturnType(returnType.scope);
- }
-
- auto parameters = it->parameters();
- for (int i = 0, length = parameters.size(); i < length; ++i) {
- auto &parameter = parameters[i];
- if (const QString typeName = parameter.typeName();
- !parameter.type() && !typeName.isEmpty()) {
- const auto type = findType(typeName, context, usedTypes);
- if (type.scope && type.scope->isReferenceType())
- parameter.setIsPointer(true);
- parameter.setType({ type.scope });
+ const auto resolveParameter = [&](QQmlJSMetaParameter &parameter) {
+ if (const QString typeName = parameter.typeName();
+ !parameter.type() && !typeName.isEmpty()) {
+ auto type = findType(typeName, context, usedTypes);
+ if (type.scope && parameter.isList()) {
+ type.scope = type.scope->listType();
+ parameter.setIsList(false);
+ parameter.setIsPointer(false);
+ parameter.setTypeName(type.scope ? type.scope->internalName() : QString());
+ } else if (type.scope && type.scope->isReferenceType()) {
+ parameter.setIsPointer(true);
}
+ parameter.setType({ type.scope });
}
+ };
+ for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) {
+ auto returnValue = it->returnValue();
+ resolveParameter(returnValue);
+ it->setReturnValue(returnValue);
+
+ auto parameters = it->parameters();
+ for (int i = 0, length = parameters.size(); i < length; ++i)
+ resolveParameter(parameters[i]);
it->setParameters(parameters);
}
+ for (auto it = self->m_jsIdentifiers.begin(); it != self->m_jsIdentifiers.end(); ++it) {
+ if (it->typeName)
+ it->scope = findType(it->typeName.value(), context, usedTypes).scope;
+ }
+
return baseType.revision;
}
void QQmlJSScope::updateChildScope(
const QQmlJSScope::Ptr &childScope, const QQmlJSScope::Ptr &self,
- const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
+ const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
{
switch (childScope->scopeType()) {
- case QQmlJSScope::GroupedPropertyScope:
+ case QQmlSA::ScopeType::GroupedPropertyScope:
QQmlJSUtils::searchBaseAndExtensionTypes(
self.data(), [&](const QQmlJSScope *type, QQmlJSScope::ExtensionKind mode) {
if (mode == QQmlJSScope::ExtensionNamespace)
@@ -541,7 +547,7 @@ void QQmlJSScope::updateChildScope(
return false;
});
break;
- case QQmlJSScope::AttachedPropertyScope:
+ case QQmlSA::ScopeType::AttachedPropertyScope:
if (const auto attachedBase = findType(
childScope->internalName(), contextualTypes, usedTypes).scope) {
childScope->m_baseType.scope = attachedBase->attachedType();
@@ -556,7 +562,7 @@ void QQmlJSScope::updateChildScope(
template<typename Resolver, typename ChildScopeUpdater>
static QTypeRevision resolveTypesInternal(
Resolver resolve, ChildScopeUpdater update, const QQmlJSScope::Ptr &self,
- const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
+ const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
{
const QTypeRevision revision = resolve(self, contextualTypes, usedTypes);
// NB: constness ensures no detach
@@ -570,13 +576,13 @@ static QTypeRevision resolveTypesInternal(
}
QTypeRevision QQmlJSScope::resolveTypes(
- const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes)
{
const auto resolveAll = [](const QQmlJSScope::Ptr &self,
- const QQmlJSScope::ContextualTypes &contextualTypes,
+ const QQmlJS::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes) {
- resolveEnums(self, contextualTypes.intType());
+ resolveEnums(self, contextualTypes, usedTypes);
resolveList(self, contextualTypes.arrayType());
return resolveType(self, contextualTypes, usedTypes);
};
@@ -584,12 +590,35 @@ QTypeRevision QQmlJSScope::resolveTypes(
}
void QQmlJSScope::resolveNonEnumTypes(
- const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes)
{
resolveTypesInternal(resolveType, updateChildScope, self, contextualTypes, usedTypes);
}
+static QString flagStorage(const QString &underlyingType)
+{
+ // All numeric types are builtins. Therefore we can exhaustively check the internal names.
+
+ if (underlyingType == u"uint"
+ || underlyingType == u"quint8"
+ || underlyingType == u"ushort"
+ || underlyingType == u"ulonglong") {
+ return u"uint"_s;
+ }
+
+ if (underlyingType == u"int"
+ || underlyingType == u"qint8"
+ || underlyingType == u"short"
+ || underlyingType == u"longlong") {
+ return u"int"_s;
+ }
+
+ // Will fail to resolve and produce an error on usage.
+ // It's harmless if you never use the enum.
+ return QString();
+}
+
/*!
\internal
Resolves all enums of self.
@@ -601,20 +630,28 @@ void QQmlJSScope::resolveNonEnumTypes(
resolveEnums() will create a QQmlJSMetaEnum copy for the alias in case the 'self'-scope already
does not have an enum called like the alias.
*/
-void QQmlJSScope::resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &intType)
+void QQmlJSScope::resolveEnums(
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
+ QSet<QString> *usedTypes)
{
// temporary hash to avoid messing up m_enumerations while iterators are active on it
QHash<QString, QQmlJSMetaEnum> toBeAppended;
- for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end;
- ++it) {
+ for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end; ++it) {
if (it->type())
continue;
- Q_ASSERT(intType); // We need an "int" type to resolve enums
QQmlJSScope::Ptr enumScope = QQmlJSScope::create();
reparent(self, enumScope);
- enumScope->m_scopeType = EnumScope;
- enumScope->setBaseTypeName(QStringLiteral("int"));
- enumScope->m_baseType.scope = intType;
+ enumScope->m_scopeType = QQmlSA::ScopeType::EnumScope;
+
+ QString typeName = it->typeName();
+ if (typeName.isEmpty())
+ typeName = QStringLiteral("int");
+ else if (it->isFlag())
+ typeName = flagStorage(typeName);
+ enumScope->setBaseTypeName(typeName);
+ const auto type = findType(typeName, contextualTypes, usedTypes);
+ enumScope->m_baseType = { type.scope, type.revision };
+
enumScope->m_semantics = AccessSemantics::Value;
enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name();
if (QString alias = it->alias(); !alias.isEmpty()
@@ -656,9 +693,9 @@ void QQmlJSScope::resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::C
const QQmlJSImportedScope element = {self, QTypeRevision()};
const QQmlJSImportedScope array = {arrayType, QTypeRevision()};
- QQmlJSScope::ContextualTypes contextualTypes(
- QQmlJSScope::ContextualTypes::INTERNAL, { { self->internalName(), element }, },
- QQmlJSScope::ConstPtr(), arrayType);
+ QQmlJS::ContextualTypes contextualTypes(
+ QQmlJS::ContextualTypes::INTERNAL, { { self->internalName(), element }, },
+ arrayType);
QQmlJSScope::resolveTypes(listType, contextualTypes);
Q_ASSERT(listType->valueType() == self);
@@ -667,7 +704,7 @@ void QQmlJSScope::resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::C
void QQmlJSScope::resolveGeneralizedGroup(
const Ptr &self, const ConstPtr &baseType,
- const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
+ const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
{
Q_ASSERT(baseType);
// Generalized group properties are always composite,
@@ -682,7 +719,7 @@ void QQmlJSScope::resolveGeneralizedGroup(
QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
{
auto qmlScope = scope;
- while (qmlScope && qmlScope->m_scopeType != QQmlJSScope::QMLScope)
+ while (qmlScope && qmlScope->m_scopeType != QQmlSA::ScopeType::QMLScope)
qmlScope = qmlScope->parentScope();
return qmlScope;
}
@@ -790,8 +827,21 @@ bool QQmlJSScope::isPropertyLocallyRequired(const QString &name) const
return m_requiredPropertyNames.contains(name);
}
-static_assert(QTypeInfo<QQmlJSScope::QmlIRCompatibilityBindingData>::isRelocatable,
- "We really want T to be relocatable as it improves QList<T> performance");
+void QQmlJSScope::addOwnPropertyBinding(const QQmlJSMetaPropertyBinding &binding, BindingTargetSpecifier specifier)
+{
+ Q_ASSERT(binding.sourceLocation().isValid());
+ m_propertyBindings.insert(binding.propertyName(), binding);
+
+ // NB: insert() prepends \a binding to the list of bindings, but we need
+ // append, so rotate
+ using iter = typename QMultiHash<QString, QQmlJSMetaPropertyBinding>::iterator;
+ QPair<iter, iter> r = m_propertyBindings.equal_range(binding.propertyName());
+ std::rotate(r.first, std::next(r.first), r.second);
+
+ // additionally store bindings in the QmlIR compatible order
+ addOwnPropertyBindingInQmlIROrder(binding, specifier);
+ Q_ASSERT(m_propertyBindings.size() == m_propertyBindingsArray.size());
+}
void QQmlJSScope::addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBinding &binding,
BindingTargetSpecifier specifier)
@@ -803,6 +853,9 @@ void QQmlJSScope::addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBind
// * bindings to default properties (which are not explicitly mentioned in
// binding expression) are inserted by source location's offset
+ static_assert(QTypeInfo<QQmlJSScope::QmlIRCompatibilityBindingData>::isRelocatable,
+ "We really want T to be relocatable as it improves QList<T> performance");
+
switch (specifier) {
case BindingTargetSpecifier::SimplePropertyTarget: {
m_propertyBindingsArray.emplaceFront(binding.propertyName(),
@@ -941,22 +994,6 @@ bool QQmlJSScope::isNameDeferred(const QString &name) const
return isDeferred;
}
-QString QQmlJSScope::qualifiedNameFrom(const QString &moduleName, const QString &typeName,
- const QTypeRevision &firstRevision,
- const QTypeRevision &lastRevision)
-{
- QString qualifiedName =
- u"%1/%2 %3.%4"_s.arg(moduleName, typeName)
- .arg(firstRevision.hasMajorVersion() ? firstRevision.majorVersion() : 0)
- .arg(firstRevision.hasMinorVersion() ? firstRevision.minorVersion() : 0);
- if (firstRevision != lastRevision) {
- qualifiedName += u"-%1.%2"_s
- .arg(lastRevision.hasMajorVersion() ? lastRevision.majorVersion() : 0)
- .arg(lastRevision.hasMinorVersion() ? lastRevision.minorVersion() : 0);
- }
- return qualifiedName;
-}
-
void QQmlJSScope::setBaseTypeName(const QString &baseTypeName)
{
m_flags.setFlag(HasBaseTypeError, false);
@@ -974,6 +1011,22 @@ void QQmlJSScope::setBaseTypeError(const QString &baseTypeError)
m_baseTypeNameOrError = baseTypeError;
}
+/*!
+\internal
+The name of the module is only saved in the QmlComponent. Iterate through the parent scopes until
+the QmlComponent or the root is reached to find out the module name of the component in which `this`
+resides.
+*/
+QString QQmlJSScope::moduleName() const
+{
+ for (const QQmlJSScope *it = this; it; it = it->parentScope().get()) {
+ const QString name = it->ownModuleName();
+ if (!name.isEmpty())
+ return name;
+ }
+ return {};
+}
+
QString QQmlJSScope::baseTypeError() const
{
return m_flags.testFlag(HasBaseTypeError) ? m_baseTypeNameOrError : QString();
@@ -1011,6 +1064,22 @@ QQmlJSScope::ConstPtr QQmlJSScope::attachedType() const
return ptr;
}
+QQmlJSScope::AnnotatedScope QQmlJSScope::extensionType() const
+{
+ if (!m_extensionType)
+ return { m_extensionType, NotExtension };
+ if (m_flags & ExtensionIsJavaScript)
+ return { m_extensionType, ExtensionJavaScript };
+ if (m_flags & ExtensionIsNamespace)
+ return { m_extensionType, ExtensionNamespace };
+ return { m_extensionType, ExtensionType };
+}
+
+void QQmlJSScope::addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index)
+{
+ m_runtimeFunctionIndices.emplaceBack(index);
+}
+
bool QQmlJSScope::isResolved() const
{
const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope
@@ -1060,21 +1129,6 @@ bool QQmlJSScope::isFullyResolved() const
return baseResolved;
}
-QQmlJSScope::Import::Import(QString prefix, QString name, QTypeRevision version, bool isFile,
- bool isDependency) :
- m_prefix(std::move(prefix)),
- m_name(std::move(name)),
- m_version(version),
- m_isFile(isFile),
- m_isDependency(isDependency)
-{
-}
-
-bool QQmlJSScope::Import::isValid() const
-{
- return !m_name.isEmpty();
-}
-
QQmlJSScope::Export::Export(
QString package, QString type, QTypeRevision version, QTypeRevision revision)
: m_package(std::move(package))
@@ -1091,13 +1145,12 @@ bool QQmlJSScope::Export::isValid() const
void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const
{
- scope->setQualifiedName(m_qualifiedName);
- scope->setModuleName(m_moduleName);
+ scope->setOwnModuleName(m_moduleName);
QQmlJSTypeReader typeReader(m_importer, m_filePath);
typeReader(scope);
m_importer->m_globalWarnings.append(typeReader.errors());
scope->setInternalName(internalName());
- QQmlJSScope::resolveEnums(scope, m_importer->builtinInternalNames().intType());
+ QQmlJSScope::resolveEnums(scope, m_importer->builtinInternalNames());
QQmlJSScope::resolveList(scope, m_importer->builtinInternalNames().arrayType());
if (m_isSingleton && !scope->isSingleton()) {
@@ -1117,6 +1170,15 @@ void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &
}
}
+/*!
+ \internal
+ Checks whether \a derived type can be assigned to this type. Returns \c
+ true if the type hierarchy of \a derived contains a type equal to this.
+
+ \note Assigning \a derived to "QVariant" or "QJSValue" is always possible and
+ the function returns \c true in this case. In addition any "QObject" based \a derived type
+ can be assigned to a this type if that type is derived from "QQmlComponent".
+ */
bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const
{
if (!derived)
@@ -1156,6 +1218,10 @@ bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const
return isListProperty() && valueType()->canAssign(derived);
}
+/*!
+ \internal
+ Checks whether this type or its parents have a custom parser.
+*/
bool QQmlJSScope::isInCustomParserParent() const
{
for (const auto *scope = this; scope; scope = scope->parentScope().get()) {
@@ -1191,13 +1257,22 @@ QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineCompo
return RootDocumentNameType();
}
+QVector<QQmlJSScope::ConstPtr> QQmlJSScope::childScopes() const
+{
+ QVector<QQmlJSScope::ConstPtr> result;
+ result.reserve(m_childScopes.size());
+ for (const auto &child : m_childScopes)
+ result.append(child);
+ return result;
+}
+
/*!
\internal
Returns true if the current type is creatable by checking all the required base classes.
"Uncreatability" is only inherited from base types for composite types (in qml) and not for non-composite types (c++).
- For the exact definition:
- A type is uncreatable if and only if one of its composite base type or its first non-composite base type matches
+For the exact definition:
+A type is uncreatable if and only if one of its composite base type or its first non-composite base type matches
following criteria:
\list
\li the base type is a singleton, or
@@ -1209,7 +1284,8 @@ QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineCompo
bool QQmlJSScope::isCreatable() const
{
auto isCreatableNonRecursive = [](const QQmlJSScope *scope) {
- return scope->hasCreatableFlag() && !scope->isSingleton() && scope->scopeType() == QMLScope;
+ return scope->hasCreatableFlag() && !scope->isSingleton()
+ && scope->scopeType() == QQmlSA::ScopeType::QMLScope;
};
for (const QQmlJSScope* scope = this; scope; scope = scope->baseType().get()) {
@@ -1225,4 +1301,68 @@ bool QQmlJSScope::isCreatable() const
// no uncreatable bases found
return false;
}
+
+bool QQmlJSScope::isStructured() const
+{
+ for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
+ if (!scope->isComposite())
+ return scope->hasStructuredFlag();
+ }
+ return false;
+}
+
+QQmlSA::Element QQmlJSScope::createQQmlSAElement(const ConstPtr &ptr)
+{
+ QQmlSA::Element element;
+ *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = ptr;
+ return element;
+}
+
+QQmlSA::Element QQmlJSScope::createQQmlSAElement(ConstPtr &&ptr)
+{
+ QQmlSA::Element element;
+ *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = std::move(ptr);
+ return element;
+}
+
+const QQmlJSScope::ConstPtr &QQmlJSScope::scope(const QQmlSA::Element &element)
+{
+ return *reinterpret_cast<const QQmlJSScope::ConstPtr *>(element.m_data);
+}
+
+QTypeRevision
+QQmlJSScope::nonCompositeBaseRevision(const ImportedScope<QQmlJSScope::ConstPtr> &scope)
+{
+ for (auto base = scope; base.scope;
+ base = { base.scope->m_baseType.scope, base.scope->m_baseType.revision }) {
+ if (!base.scope->isComposite())
+ return base.revision;
+ }
+ return {};
+}
+
+/*!
+ \internal
+ Checks whether \a otherScope is the same type as this.
+
+ In addition to checking whether the scopes are identical, we also cover duplicate scopes with
+ the same internal name.
+ */
+bool QQmlJSScope::isSameType(const ConstPtr &otherScope) const
+{
+ return this == otherScope.get()
+ || (!this->internalName().isEmpty()
+ && this->internalName() == otherScope->internalName());
+}
+
+bool QQmlJSScope::inherits(const ConstPtr &base) const
+{
+ for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
+ if (scope->isSameType(base))
+ return true;
+ }
+ return false;
+}
+
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index aab9b472b8..97ec6cc004 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -14,11 +14,13 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include "qqmljsmetatypes_p.h"
#include "qdeferredpointer_p.h"
#include "qqmljsannotation_p.h"
+#include "qqmlsaconstants.h"
+#include "qqmlsa_p.h"
#include <QtQml/private/qqmljssourcelocation_p.h>
@@ -27,6 +29,7 @@
#include <QtCore/qset.h>
#include <QtCore/qstring.h>
#include <QtCore/qversionnumber.h>
+#include "qqmlsaconstants.h"
#include <optional>
@@ -34,9 +37,102 @@ QT_BEGIN_NAMESPACE
class QQmlJSImporter;
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSScope
+namespace QQmlJS {
+
+class ConstPtrWrapperIterator
{
public:
+ using Ptr = QDeferredSharedPointer<QQmlJSScope>;
+ using ConstPtr = QDeferredSharedPointer<const QQmlJSScope>;
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = ConstPtr;
+ using pointer = value_type *;
+ using reference = value_type &;
+
+ ConstPtrWrapperIterator(QList<Ptr>::const_iterator iterator) : m_iterator(iterator) { }
+
+ friend bool operator==(const ConstPtrWrapperIterator &a, const ConstPtrWrapperIterator &b)
+ {
+ return a.m_iterator == b.m_iterator;
+ }
+ friend bool operator!=(const ConstPtrWrapperIterator &a, const ConstPtrWrapperIterator &b)
+ {
+ return a.m_iterator != b.m_iterator;
+ }
+
+ reference operator*()
+ {
+ if (!m_pointer)
+ m_pointer = *m_iterator;
+ return m_pointer;
+ }
+ pointer operator->()
+ {
+ if (!m_pointer)
+ m_pointer = *m_iterator;
+ return &m_pointer;
+ }
+
+ ConstPtrWrapperIterator &operator++()
+ {
+ m_iterator++;
+ m_pointer = {};
+ return *this;
+ }
+ ConstPtrWrapperIterator operator++(int)
+ {
+ auto before = *this;
+ ++(*this);
+ return before;
+ }
+
+private:
+ QList<Ptr>::const_iterator m_iterator;
+ ConstPtr m_pointer;
+};
+
+class Export {
+public:
+ Export() = default;
+ Export(QString package, QString type, QTypeRevision version, QTypeRevision revision);
+
+ bool isValid() const;
+
+ QString package() const { return m_package; }
+ QString type() const { return m_type; }
+ QTypeRevision version() const { return m_version; }
+ QTypeRevision revision() const { return m_revision; }
+
+private:
+ QString m_package;
+ QString m_type;
+ QTypeRevision m_version;
+ QTypeRevision m_revision;
+};
+
+template<typename Pointer>
+struct ExportedScope {
+ Pointer scope;
+ QList<Export> exports;
+};
+
+template<typename Pointer>
+struct ImportedScope {
+ Pointer scope;
+ QTypeRevision revision;
+};
+
+struct ContextualTypes;
+
+} // namespace QQmlJS
+
+class Q_QMLCOMPILER_EXPORT QQmlJSScope
+{
+ friend QQmlSA::Element;
+
+public:
+ explicit QQmlJSScope(const QString &internalName);
QQmlJSScope(QQmlJSScope &&) = default;
QQmlJSScope &operator=(QQmlJSScope &&) = default;
@@ -45,6 +141,9 @@ public:
using ConstPtr = QDeferredSharedPointer<const QQmlJSScope>;
using WeakConstPtr = QDeferredWeakPointer<const QQmlJSScope>;
+ using AccessSemantics = QQmlSA::AccessSemantics;
+ using ScopeType = QQmlSA::ScopeType;
+
using InlineComponentNameType = QString;
using RootDocumentNameType = std::monostate; // an empty type that has std::hash
/*!
@@ -53,23 +152,6 @@ public:
using InlineComponentOrDocumentRootName =
std::variant<InlineComponentNameType, RootDocumentNameType>;
- enum ScopeType
- {
- JSFunctionScope,
- JSLexicalScope,
- QMLScope,
- GroupedPropertyScope,
- AttachedPropertyScope,
- EnumScope
- };
-
- enum class AccessSemantics {
- Reference,
- Value,
- None,
- Sequence
- };
-
enum Flag {
Creatable = 0x1,
Composite = 0x2,
@@ -80,197 +162,19 @@ public:
InlineComponent = 0x40,
WrappedInImplicitComponent = 0x80,
HasBaseTypeError = 0x100,
- HasExtensionNamespace = 0x200,
+ ExtensionIsNamespace = 0x200,
IsListProperty = 0x400,
+ Structured = 0x800,
+ ExtensionIsJavaScript = 0x1000,
};
Q_DECLARE_FLAGS(Flags, Flag)
Q_FLAGS(Flags);
- class ConstPtrWrapperIterator
- {
- public:
- using iterator_category = std::forward_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using value_type = ConstPtr;
- using pointer = value_type *;
- using reference = value_type &;
-
- ConstPtrWrapperIterator(QList<QQmlJSScope::Ptr>::const_iterator iterator)
- : m_iterator(iterator)
- {
- }
-
- friend bool operator==(const ConstPtrWrapperIterator &a, const ConstPtrWrapperIterator &b)
- {
- return a.m_iterator == b.m_iterator;
- }
- friend bool operator!=(const ConstPtrWrapperIterator &a, const ConstPtrWrapperIterator &b)
- {
- return a.m_iterator != b.m_iterator;
- }
-
- reference operator*()
- {
- if (!m_pointer)
- m_pointer = *m_iterator;
- return m_pointer;
- }
- pointer operator->()
- {
- if (!m_pointer)
- m_pointer = *m_iterator;
- return &m_pointer;
- }
-
- ConstPtrWrapperIterator &operator++()
- {
- m_iterator++;
- m_pointer = {};
- return *this;
- }
- ConstPtrWrapperIterator operator++(int)
- {
- auto before = *this;
- ++(*this);
- return before;
- }
-
- private:
- QList<QQmlJSScope::Ptr>::const_iterator m_iterator;
- QQmlJSScope::ConstPtr m_pointer;
- };
-
- class Import
- {
- public:
- Import() = default;
- Import(QString prefix, QString name, QTypeRevision version, bool isFile, bool isDependency);
-
- bool isValid() const;
-
- QString prefix() const { return m_prefix; }
- QString name() const { return m_name; }
- QTypeRevision version() const { return m_version; }
- bool isFile() const { return m_isFile; }
- bool isDependency() const { return m_isDependency; }
-
- private:
- QString m_prefix;
- QString m_name;
- QTypeRevision m_version;
- bool m_isFile = false;
- bool m_isDependency = false;
-
- friend inline size_t qHash(const Import &key, size_t seed = 0) noexcept
- {
- return qHashMulti(seed, key.m_prefix, key.m_name, key.m_version,
- key.m_isFile, key.m_isDependency);
- }
-
- friend inline bool operator==(const Import &a, const Import &b)
- {
- return a.m_prefix == b.m_prefix && a.m_name == b.m_name && a.m_version == b.m_version
- && a.m_isFile == b.m_isFile && a.m_isDependency == b.m_isDependency;
- }
- };
-
- class Export {
- public:
- Export() = default;
- Export(QString package, QString type, QTypeRevision version, QTypeRevision revision);
-
- bool isValid() const;
-
- QString package() const { return m_package; }
- QString type() const { return m_type; }
- QTypeRevision version() const { return m_version; }
- QTypeRevision revision() const { return m_revision; }
-
- private:
- QString m_package;
- QString m_type;
- QTypeRevision m_version;
- QTypeRevision m_revision;
- };
-
- template<typename Pointer>
- struct ExportedScope {
- Pointer scope;
- QList<QQmlJSScope::Export> exports;
- };
-
- template<typename Pointer>
- struct ImportedScope {
- Pointer scope;
- QTypeRevision revision;
- };
-
- /*! \internal
- * Maps type names to types and the compile context of the types. The context can be
- * INTERNAl (for c++ and synthetic jsrootgen types) or QML (for qml types).
- */
- struct ContextualTypes
- {
- enum CompileContext { INTERNAL, QML };
-
- ContextualTypes(
- CompileContext context,
- const QHash<QString, ImportedScope<ConstPtr>> types,
- const QQmlJSScope::ConstPtr &intType,
- const QQmlJSScope::ConstPtr &arrayType)
- : m_types(types)
- , m_context(context)
- , m_intType(intType)
- , m_arrayType(arrayType)
- {}
-
- CompileContext context() const { return m_context; }
- ConstPtr intType() const { return m_intType; }
- ConstPtr arrayType() const { return m_arrayType; }
-
- bool hasType(const QString &name) const { return m_types.contains(name); }
- ImportedScope<ConstPtr> type(const QString &name) const { return m_types[name]; }
- void setType(const QString &name, const ImportedScope<ConstPtr> &type)
- {
- m_types.insert(name, type);
- }
- void clearType(const QString &name)
- {
- m_types[name].scope = QQmlJSScope::ConstPtr();
- }
-
- bool isNullType(const QString &name) const
- {
- const auto it = m_types.constFind(name);
- return it != m_types.constEnd() && it->scope.isNull();
- }
-
- void addTypes(ContextualTypes &&types)
- {
- Q_ASSERT(types.m_context == m_context);
- m_types.insert(std::move(types.m_types));
- }
-
- void addTypes(const ContextualTypes &types)
- {
- Q_ASSERT(types.m_context == m_context);
- m_types.insert(types.m_types);
- }
-
- const QHash<QString, ImportedScope<ConstPtr>> &types() const { return m_types; }
-
- void clearTypes() { m_types.clear(); }
-
- private:
- QHash<QString, ImportedScope<ConstPtr>> m_types;
- CompileContext m_context;
-
- // For resolving enums
- QQmlJSScope::ConstPtr m_intType;
-
- // For resolving sequence types
- QQmlJSScope::ConstPtr m_arrayType;
- };
+ using Export = QQmlJS::Export;
+ template <typename Pointer>
+ using ImportedScope = QQmlJS::ImportedScope<Pointer>;
+ template <typename Pointer>
+ using ExportedScope = QQmlJS::ExportedScope<Pointer>;
struct JavaScriptIdentifier
{
@@ -283,7 +187,9 @@ public:
Kind kind = FunctionScoped;
QQmlJS::SourceLocation location;
- bool isConst;
+ std::optional<QString> typeName;
+ bool isConst = false;
+ QQmlJSScope::WeakConstPtr scope = {};
};
enum BindingTargetSpecifier {
@@ -292,38 +198,32 @@ public:
UnnamedPropertyTarget // default property bindings, where property name is unspecified
};
+ template <typename Key, typename Value>
+ using QMultiHashRange = QPair<typename QMultiHash<Key, Value>::iterator,
+ typename QMultiHash<Key, Value>::iterator>;
+
static QQmlJSScope::Ptr create() { return QSharedPointer<QQmlJSScope>(new QQmlJSScope); }
+ static QQmlJSScope::Ptr create(const QString &internalName);
static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin);
- static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope);
-
- QQmlJSScope::Ptr parentScope()
- {
- return m_parentScope.toStrongRef();
- }
- QQmlJSScope::ConstPtr parentScope() const
- {
-QT_WARNING_PUSH
-#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU < 1400 && Q_CC_GNU >= 1200
- QT_WARNING_DISABLE_GCC("-Wuse-after-free")
-#endif
- return QQmlJSScope::WeakConstPtr(m_parentScope).toStrongRef();
-QT_WARNING_POP
- }
+ static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope);
+ QQmlJSScope::Ptr parentScope();
+ QQmlJSScope::ConstPtr parentScope() const;
static void reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope);
void insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier);
-
- // inserts property as qml identifier as well as the corresponding
+ QHash<QString, JavaScriptIdentifier> ownJSIdentifiers() const;
void insertPropertyIdentifier(const QQmlJSMetaProperty &prop);
- bool isIdInCurrentScope(const QString &id) const;
-
ScopeType scopeType() const { return m_scopeType; }
void setScopeType(ScopeType type) { m_scopeType = type; }
void addOwnMethod(const QQmlJSMetaMethod &method) { m_methods.insert(method.methodName(), method); }
+ QMultiHashRange<QString, QQmlJSMetaMethod> mutableOwnMethodsRange(const QString &name)
+ {
+ return m_methods.equal_range(name);
+ }
QMultiHash<QString, QQmlJSMetaMethod> ownMethods() const { return m_methods; }
QList<QQmlJSMetaMethod> ownMethods(const QString &name) const { return m_methods.values(name); }
bool hasOwnMethod(const QString &name) const { return m_methods.contains(name); }
@@ -331,7 +231,7 @@ QT_WARNING_POP
bool hasMethod(const QString &name) const;
QHash<QString, QQmlJSMetaMethod> methods() const;
QList<QQmlJSMetaMethod> methods(const QString &name) const;
- QList<QQmlJSMetaMethod> methods(const QString &name, QQmlJSMetaMethod::Type type) const;
+ QList<QQmlJSMetaMethod> methods(const QString &name, QQmlJSMetaMethodType type) const;
void addOwnEnumeration(const QQmlJSMetaEnum &enumeration) { m_enumerations.insert(enumeration.name(), enumeration); }
QHash<QString, QQmlJSMetaEnum> ownEnumerations() const { return m_enumerations; }
@@ -340,6 +240,7 @@ QT_WARNING_POP
bool hasEnumeration(const QString &name) const;
bool hasEnumerationKey(const QString &name) const;
+ bool hasOwnEnumerationKey(const QString &name) const;
QQmlJSMetaEnum enumeration(const QString &name) const;
QHash<QString, QQmlJSMetaEnum> enumerations() const;
@@ -353,43 +254,16 @@ QT_WARNING_POP
// QML file. isComposite tells us if this is a C++ or a QML name.
QString internalName() const { return m_internalName; }
void setInternalName(const QString &internalName) { m_internalName = internalName; }
- QString augmentedInternalName() const
- {
- using namespace Qt::StringLiterals;
-
- switch (m_semantics) {
- case AccessSemantics::Reference:
- return m_internalName + " *"_L1;
- case AccessSemantics::Value:
- case AccessSemantics::Sequence:
- break;
- case AccessSemantics::None:
- // If we got a namespace, it might still be a regular type, exposed as namespace.
- // We may need to travel the inheritance chain all the way up to QObject to
- // figure this out, since all other types may be exposed the same way.
- for (QQmlJSScope::ConstPtr base = baseType(); base; base = base->baseType()) {
- switch (base->accessSemantics()) {
- case AccessSemantics::Reference:
- return m_internalName + " *"_L1;
- case AccessSemantics::Value:
- case AccessSemantics::Sequence:
- return m_internalName;
- case AccessSemantics::None:
- break;
- }
- }
- break;
- }
- return m_internalName;
- }
+ QString augmentedInternalName() const;
// This returns a more user readable version of internalName / baseTypeName
static QString prettyName(QAnyStringView name);
- static bool causesImplicitComponentWrapping(const QQmlJSMetaProperty &property,
- const QQmlJSScope::ConstPtr &assignedType);
bool isComponentRootElement() const;
+ void setAliases(const QStringList &aliases) { m_aliases = aliases; }
+ QStringList aliases() const { return m_aliases; }
+
void setInterfaceNames(const QStringList& interfaces) { m_interfaceNames = interfaces; }
QStringList interfaceNames() const { return m_interfaceNames; }
@@ -411,13 +285,9 @@ QT_WARNING_POP
QQmlJSScope::ConstPtr baseType() const { return m_baseType.scope; }
QTypeRevision baseTypeRevision() const { return m_baseType.revision; }
- QString qualifiedName() const { return m_qualifiedName; }
- void setQualifiedName(const QString &qualifiedName) { m_qualifiedName = qualifiedName; };
- static QString qualifiedNameFrom(const QString &moduleName, const QString &typeName,
- const QTypeRevision &firstRevision,
- const QTypeRevision &lastRevision);
- QString moduleName() const { return m_moduleName; }
- void setModuleName(const QString &moduleName) { m_moduleName = moduleName; }
+ QString moduleName() const;
+ QString ownModuleName() const { return m_moduleName; }
+ void setOwnModuleName(const QString &moduleName) { m_moduleName = moduleName; }
void clearBaseType() { m_baseType = {}; }
void setBaseTypeError(const QString &baseTypeError);
@@ -438,36 +308,13 @@ QT_WARNING_POP
void addOwnPropertyBinding(
const QQmlJSMetaPropertyBinding &binding,
- BindingTargetSpecifier specifier = BindingTargetSpecifier::SimplePropertyTarget)
- {
- Q_ASSERT(binding.sourceLocation().isValid());
- m_propertyBindings.insert(binding.propertyName(), binding);
-
- // NB: insert() prepends \a binding to the list of bindings, but we need
- // append, so rotate
- using iter = typename QMultiHash<QString, QQmlJSMetaPropertyBinding>::iterator;
- QPair<iter, iter> r = m_propertyBindings.equal_range(binding.propertyName());
- std::rotate(r.first, std::next(r.first), r.second);
-
- // additionally store bindings in the QmlIR compatible order
- addOwnPropertyBindingInQmlIROrder(binding, specifier);
- Q_ASSERT(m_propertyBindings.size() == m_propertyBindingsArray.size());
- }
- QMultiHash<QString, QQmlJSMetaPropertyBinding> ownPropertyBindings() const
- {
- return m_propertyBindings;
- }
+ BindingTargetSpecifier specifier = BindingTargetSpecifier::SimplePropertyTarget);
+ QMultiHash<QString, QQmlJSMetaPropertyBinding> ownPropertyBindings() const;
QPair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator,
QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator>
- ownPropertyBindings(const QString &name) const
- {
- return m_propertyBindings.equal_range(name);
- }
+ ownPropertyBindings(const QString &name) const;
QList<QQmlJSMetaPropertyBinding> ownPropertyBindingsInQmlIROrder() const;
- bool hasOwnPropertyBindings(const QString &name) const
- {
- return m_propertyBindings.contains(name);
- }
+ bool hasOwnPropertyBindings(const QString &name) const;
bool hasPropertyBindings(const QString &name) const;
QList<QQmlJSMetaPropertyBinding> propertyBindings(const QString &name) const;
@@ -498,6 +345,7 @@ QT_WARNING_POP
enum ExtensionKind {
NotExtension,
ExtensionType,
+ ExtensionJavaScript,
ExtensionNamespace,
};
struct AnnotatedScope
@@ -505,13 +353,7 @@ QT_WARNING_POP
QQmlJSScope::ConstPtr scope;
ExtensionKind extensionSpecifier = NotExtension;
};
- AnnotatedScope extensionType() const
- {
- if (!m_extensionType)
- return { m_extensionType, NotExtension };
- return { m_extensionType,
- (m_flags & HasExtensionNamespace) ? ExtensionNamespace : ExtensionType };
- }
+ AnnotatedScope extensionType() const;
QString valueTypeName() const { return m_valueTypeName; }
void setValueTypeName(const QString &name) { m_valueTypeName = name; }
@@ -519,22 +361,11 @@ QT_WARNING_POP
QQmlJSScope::ConstPtr listType() const { return m_listType; }
QQmlJSScope::Ptr listType() { return m_listType; }
- void addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index)
- {
- m_runtimeFunctionIndices.emplaceBack(index);
- }
+ void addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index);
QQmlJSMetaMethod::AbsoluteFunctionIndex
- ownRuntimeFunctionIndex(QQmlJSMetaMethod::RelativeFunctionIndex index) const
- {
- const int i = static_cast<int>(index);
- Q_ASSERT(i >= 0);
- Q_ASSERT(i < int(m_runtimeFunctionIndices.size()));
- return m_runtimeFunctionIndices[i];
- }
+ ownRuntimeFunctionIndex(QQmlJSMetaMethod::RelativeFunctionIndex index) const;
+
- bool isSingleton() const { return m_flags & Singleton; }
- bool isCreatable() const;
- bool hasCreatableFlag() const { return m_flags & Creatable; }
/*!
* \internal
*
@@ -546,148 +377,94 @@ QT_WARNING_POP
bool isArrayScope() const { return m_flags & Array; }
bool isInlineComponent() const { return m_flags & InlineComponent; }
bool isWrappedInImplicitComponent() const { return m_flags & WrappedInImplicitComponent; }
- bool extensionIsNamespace() const { return m_flags & HasExtensionNamespace; }
+ bool extensionIsJavaScript() const { return m_flags & ExtensionIsJavaScript; }
+ bool extensionIsNamespace() const { return m_flags & ExtensionIsNamespace; }
+ bool isListProperty() const { return m_flags.testFlag(IsListProperty); }
+ void setIsListProperty(bool v) { m_flags.setFlag(IsListProperty, v); }
+ bool isSingleton() const { return m_flags & Singleton; }
+ bool isCreatable() const;
+ bool isStructured() const;
+ bool isReferenceType() const { return m_semantics == QQmlJSScope::AccessSemantics::Reference; }
+ bool isValueType() const { return m_semantics == QQmlJSScope::AccessSemantics::Value; }
+
void setIsSingleton(bool v) { m_flags.setFlag(Singleton, v); }
void setCreatableFlag(bool v) { m_flags.setFlag(Creatable, v); }
+ void setStructuredFlag(bool v) { m_flags.setFlag(Structured, v); }
void setIsComposite(bool v) { m_flags.setFlag(Composite, v); }
void setIsScript(bool v) { m_flags.setFlag(Script, v); }
- void setHasCustomParser(bool v)
- {
- m_flags.setFlag(CustomParser, v);;
- }
+ void setHasCustomParser(bool v);
void setIsArrayScope(bool v) { m_flags.setFlag(Array, v); }
void setIsInlineComponent(bool v) { m_flags.setFlag(InlineComponent, v); }
void setIsWrappedInImplicitComponent(bool v) { m_flags.setFlag(WrappedInImplicitComponent, v); }
- void setExtensionIsNamespace(bool v) { m_flags.setFlag(HasExtensionNamespace, v); }
+ void setExtensionIsJavaScript(bool v) { m_flags.setFlag(ExtensionIsJavaScript, v); }
+ void setExtensionIsNamespace(bool v) { m_flags.setFlag(ExtensionIsNamespace, v); }
- bool isListProperty() const { return m_flags.testFlag(IsListProperty); }
- void setIsListProperty(bool v) { m_flags.setFlag(IsListProperty, v); }
void setAccessSemantics(AccessSemantics semantics) { m_semantics = semantics; }
AccessSemantics accessSemantics() const { return m_semantics; }
- bool isReferenceType() const { return m_semantics == QQmlJSScope::AccessSemantics::Reference; }
- bool isValueType() const { return m_semantics == QQmlJSScope::AccessSemantics::Value; }
-
- bool isIdInCurrentQmlScopes(const QString &id) const;
- bool isIdInCurrentJSScopes(const QString &id) const;
- bool isIdInjectedFromSignal(const QString &id) const;
- std::optional<JavaScriptIdentifier> findJSIdentifier(const QString &id) const;
+ std::optional<JavaScriptIdentifier> jsIdentifier(const QString &id) const;
+ std::optional<JavaScriptIdentifier> ownJSIdentifier(const QString &id) const;
- ConstPtrWrapperIterator childScopesBegin() const { return m_childScopes.constBegin(); }
- ConstPtrWrapperIterator childScopesEnd() const { return m_childScopes.constEnd(); }
+ QQmlJS::ConstPtrWrapperIterator childScopesBegin() const { return m_childScopes.constBegin(); }
+ QQmlJS::ConstPtrWrapperIterator childScopesEnd() const { return m_childScopes.constEnd(); }
- void setInlineComponentName(const QString &inlineComponentName)
- {
- Q_ASSERT(isInlineComponent());
- m_inlineComponentName = inlineComponentName;
- }
+ void setInlineComponentName(const QString &inlineComponentName);
std::optional<QString> inlineComponentName() const;
InlineComponentOrDocumentRootName enclosingInlineComponentName() const;
- QVector<QQmlJSScope::Ptr> childScopes()
- {
- return m_childScopes;
- }
+ QVector<QQmlJSScope::Ptr> childScopes();
- QVector<QQmlJSScope::ConstPtr> childScopes() const
- {
- QVector<QQmlJSScope::ConstPtr> result;
- result.reserve(m_childScopes.size());
- for (const auto &child : m_childScopes)
- result.append(child);
- return result;
- }
+ QVector<QQmlJSScope::ConstPtr> childScopes() const;
static QTypeRevision resolveTypes(
- const Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
+ const Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes = nullptr);
static void resolveNonEnumTypes(
- const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes = nullptr);
static void resolveEnums(
- const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &intType);
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
+ QSet<QString> *usedTypes = nullptr);
static void resolveList(
const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType);
static void resolveGeneralizedGroup(
const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &baseType,
- const QQmlJSScope::ContextualTypes &contextualTypes,
+ const QQmlJS::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes = nullptr);
- void setSourceLocation(const QQmlJS::SourceLocation &sourceLocation)
- {
- m_sourceLocation = sourceLocation;
- }
-
- QQmlJS::SourceLocation sourceLocation() const
- {
- return m_sourceLocation;
- }
-
- static QQmlJSScope::ConstPtr nonCompositeBaseType(const QQmlJSScope::ConstPtr &type)
- {
- for (QQmlJSScope::ConstPtr base = type; base; base = base->baseType()) {
- if (!base->isComposite())
- return base;
- }
- return {};
- }
+ void setSourceLocation(const QQmlJS::SourceLocation &sourceLocation);
+ QQmlJS::SourceLocation sourceLocation() const;
- static QTypeRevision nonCompositeBaseRevision(const ImportedScope<QQmlJSScope::ConstPtr> &scope)
- {
- for (auto base = scope; base.scope;
- base = { base.scope->m_baseType.scope, base.scope->m_baseType.revision }) {
- if (!base.scope->isComposite())
- return base.revision;
- }
- return {};
- }
+ static QQmlJSScope::ConstPtr nonCompositeBaseType(const QQmlJSScope::ConstPtr &type);
- /*!
- \internal
- Checks whether \a otherScope is the same type as this.
+ static QTypeRevision
+ nonCompositeBaseRevision(const ImportedScope<QQmlJSScope::ConstPtr> &scope);
- In addition to checking whether the scopes are identical, we also cover duplicate scopes with
- the same internal name.
- */
- bool isSameType(const QQmlJSScope::ConstPtr &otherScope) const
- {
- return this == otherScope.get()
- || (!this->internalName().isEmpty()
- && this->internalName() == otherScope->internalName());
- }
+ bool isSameType(const QQmlJSScope::ConstPtr &otherScope) const;
+ bool inherits(const QQmlJSScope::ConstPtr &base) const;
+ bool canAssign(const QQmlJSScope::ConstPtr &derived) const;
- bool inherits(const QQmlJSScope::ConstPtr &base) const
- {
- for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
- if (scope->isSameType(base))
- return true;
- }
- return false;
- }
+ bool isInCustomParserParent() const;
- /*!
- \internal
- Checks whether \a derived type can be assigned to this type. Returns \c
- true if the type hierarchy of \a derived contains a type equal to this.
- \note Assigning \a derived to "QVariant" or "QJSValue" is always possible and
- the function returns \c true in this case. In addition any "QObject" based \a derived type
- can be assigned to a this type if that type is derived from "QQmlComponent".
- */
- bool canAssign(const QQmlJSScope::ConstPtr &derived) const;
+ static ImportedScope<QQmlJSScope::ConstPtr> findType(const QString &name,
+ const QQmlJS::ContextualTypes &contextualTypes,
+ QSet<QString> *usedTypes = nullptr);
- /*!
- \internal
- Checks whether this type or its parents have a custom parser.
- */
- bool isInCustomParserParent() const;
+ static QQmlSA::Element createQQmlSAElement(const ConstPtr &);
+ static QQmlSA::Element createQQmlSAElement(ConstPtr &&);
+ static const QQmlJSScope::ConstPtr &scope(const QQmlSA::Element &);
+ static constexpr qsizetype sizeofQQmlSAElement() { return QQmlSA::Element::sizeofElement; }
+private:
/*! \internal
- Minimal information about a QQmlJSMetaPropertyBinding that allows it to
- be manipulated similarly to QmlIR::Binding.
- */
+ Minimal information about a QQmlJSMetaPropertyBinding that allows it to
+ be manipulated similarly to QmlIR::Binding.
+ */
+ template <typename T>
+ friend class QTypeInfo; // so that we can Q_DECLARE_TYPEINFO QmlIRCompatibilityBindingData
struct QmlIRCompatibilityBindingData
{
QmlIRCompatibilityBindingData() = default;
@@ -699,31 +476,20 @@ QT_WARNING_POP
quint32 sourceLocationOffset = 0; // binding's source location offset
};
- /*! \internal
- * Finds a type in contextualTypes with given name.
- * If a type is found, then its name is inserted into usedTypes (when provided).
- * If contextualTypes has mode INTERNAl, then namespace resolution for enums is
- * done (eg for Qt::Alignment).
- * If contextualTypes has mode QML, then inline component resolution is done
- * ("qmlFileName.IC" is correctly resolved from qmlFileName).
- */
- static ImportedScope<QQmlJSScope::ConstPtr> findType(const QString &name,
- const ContextualTypes &contextualTypes,
- QSet<QString> *usedTypes = nullptr);
-
-private:
QQmlJSScope() = default;
QQmlJSScope(const QQmlJSScope &) = default;
QQmlJSScope &operator=(const QQmlJSScope &) = default;
static QTypeRevision resolveType(
- const QQmlJSScope::Ptr &self, const ContextualTypes &contextualTypes,
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes);
static void updateChildScope(
const QQmlJSScope::Ptr &childScope, const QQmlJSScope::Ptr &self,
- const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes);
+ const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes);
void addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBinding &binding,
BindingTargetSpecifier specifier);
+ bool hasCreatableFlag() const { return m_flags & Creatable; }
+ bool hasStructuredFlag() const { return m_flags & Structured; }
QHash<QString, JavaScriptIdentifier> m_jsIdentifiers;
@@ -752,7 +518,8 @@ private:
// the only relation between two types where the revisions matter.
ImportedScope<QQmlJSScope::WeakConstPtr> m_baseType;
- ScopeType m_scopeType = QMLScope;
+ ScopeType m_scopeType = ScopeType::QMLScope;
+ QStringList m_aliases;
QStringList m_interfaceNames;
QStringList m_ownDeferredNames;
QStringList m_ownImmediateNames;
@@ -790,15 +557,88 @@ private:
QQmlJS::SourceLocation m_sourceLocation;
- QString m_qualifiedName;
QString m_moduleName;
std::optional<QString> m_inlineComponentName;
};
+
+inline QQmlJSScope::Ptr QQmlJSScope::parentScope()
+{
+ return m_parentScope.toStrongRef();
+}
+
+inline QQmlJSScope::ConstPtr QQmlJSScope::parentScope() const
+{
+ QT_WARNING_PUSH
+#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU < 1400 && Q_CC_GNU >= 1200
+ QT_WARNING_DISABLE_GCC("-Wuse-after-free")
+#endif
+ return QQmlJSScope::WeakConstPtr(m_parentScope).toStrongRef();
+ QT_WARNING_POP
+}
+
+inline QMultiHash<QString, QQmlJSMetaPropertyBinding> QQmlJSScope::ownPropertyBindings() const
+{
+ return m_propertyBindings;
+}
+
+inline QPair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator, QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator> QQmlJSScope::ownPropertyBindings(const QString &name) const
+{
+ return m_propertyBindings.equal_range(name);
+}
+
+inline bool QQmlJSScope::hasOwnPropertyBindings(const QString &name) const
+{
+ return m_propertyBindings.contains(name);
+}
+
+inline QQmlJSMetaMethod::AbsoluteFunctionIndex QQmlJSScope::ownRuntimeFunctionIndex(QQmlJSMetaMethod::RelativeFunctionIndex index) const
+{
+ const int i = static_cast<int>(index);
+ Q_ASSERT(i >= 0);
+ Q_ASSERT(i < int(m_runtimeFunctionIndices.size()));
+ return m_runtimeFunctionIndices[i];
+}
+
+inline void QQmlJSScope::setHasCustomParser(bool v)
+{
+ m_flags.setFlag(CustomParser, v);;
+}
+
+inline void QQmlJSScope::setInlineComponentName(const QString &inlineComponentName)
+{
+ Q_ASSERT(isInlineComponent());
+ m_inlineComponentName = inlineComponentName;
+}
+
+inline QVector<QQmlJSScope::Ptr> QQmlJSScope::childScopes()
+{
+ return m_childScopes;
+}
+
+inline void QQmlJSScope::setSourceLocation(const QQmlJS::SourceLocation &sourceLocation)
+{
+ m_sourceLocation = sourceLocation;
+}
+
+inline QQmlJS::SourceLocation QQmlJSScope::sourceLocation() const
+{
+ return m_sourceLocation;
+}
+
+inline QQmlJSScope::ConstPtr QQmlJSScope::nonCompositeBaseType(const ConstPtr &type)
+{
+ for (QQmlJSScope::ConstPtr base = type; base; base = base->baseType()) {
+ if (!base->isComposite())
+ return base;
+ }
+ return {};
+}
+
Q_DECLARE_TYPEINFO(QQmlJSScope::QmlIRCompatibilityBindingData, Q_RELOCATABLE_TYPE);
template<>
-class Q_QMLCOMPILER_PRIVATE_EXPORT QDeferredFactory<QQmlJSScope>
+class Q_QMLCOMPILER_EXPORT QDeferredFactory<QQmlJSScope>
{
public:
QDeferredFactory() = default;
@@ -822,7 +662,6 @@ public:
m_isSingleton = isSingleton;
}
- void setQualifiedName(const QString &qualifiedName) { m_qualifiedName = qualifiedName; }
void setModuleName(const QString &moduleName) { m_moduleName = moduleName; }
private:
@@ -837,18 +676,12 @@ private:
QString m_filePath;
QQmlJSImporter *m_importer = nullptr;
bool m_isSingleton = false;
- QString m_qualifiedName;
QString m_moduleName;
};
using QQmlJSExportedScope = QQmlJSScope::ExportedScope<QQmlJSScope::Ptr>;
using QQmlJSImportedScope = QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr>;
-struct QQmlJSTypeInfo
-{
- QMultiHash<QQmlJSScope::ConstPtr, QQmlJSScope::ConstPtr> usedAttachedTypes;
-};
-
QT_END_NAMESPACE
#endif // QQMLJSSCOPE_P_H
diff --git a/src/qmlcompiler/qqmljsscopesbyid_p.h b/src/qmlcompiler/qqmljsscopesbyid_p.h
index 77ad1947ab..c9dca9ae34 100644
--- a/src/qmlcompiler/qqmljsscopesbyid_p.h
+++ b/src/qmlcompiler/qqmljsscopesbyid_p.h
@@ -22,6 +22,12 @@
QT_BEGIN_NAMESPACE
+enum QQmlJSScopesByIdOption: char {
+ Default = 0,
+ AssumeComponentsAreBound = 1,
+};
+Q_DECLARE_FLAGS(QQmlJSScopesByIdOptions, QQmlJSScopesByIdOption);
+
class QQmlJSScopesById
{
public:
@@ -31,13 +37,15 @@ public:
void setSignaturesAreEnforced(bool enforced) { m_signaturesAreEnforced = enforced; }
bool signaturesAreEnforced() const { return m_signaturesAreEnforced; }
- void setValueTypesAreCopied(bool copied) { m_valueTypesAreCopied = copied; }
- bool valueTypesAreCopied() const { return m_valueTypesAreCopied; }
+ void setValueTypesAreAddressable(bool addressable) { m_valueTypesAreAddressable = addressable; }
+ bool valueTypesAreAddressable() const { return m_valueTypesAreAddressable; }
- QString id(const QQmlJSScope::ConstPtr &scope) const
+ QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer,
+ QQmlJSScopesByIdOptions options = Default) const
{
+ const QQmlJSScope::ConstPtr referrerRoot = componentRoot(referrer);
for (auto it = m_scopesById.begin(), end = m_scopesById.end(); it != end; ++it) {
- if (*it == scope)
+ if (*it == scope && isComponentVisible(componentRoot(*it), referrerRoot, options))
return it.key();
}
return QString();
@@ -48,7 +56,8 @@ public:
Returns the scope that has id \a id in the component to which \a referrer belongs to.
If no such scope exists, a null scope is returned.
*/
- QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer) const
+ QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer,
+ QQmlJSScopesByIdOptions options = Default) const
{
Q_ASSERT(!id.isEmpty());
const auto range = m_scopesById.equal_range(id);
@@ -57,7 +66,7 @@ public:
const QQmlJSScope::ConstPtr referrerRoot = componentRoot(referrer);
for (auto it = range.first; it != range.second; ++it) {
- if (isComponentVisible(componentRoot(*it), referrerRoot))
+ if (isComponentVisible(componentRoot(*it), referrerRoot, options))
return *it;
}
@@ -94,10 +103,11 @@ private:
return scope;
}
- bool isComponentVisible(
- const QQmlJSScope::ConstPtr &observed, const QQmlJSScope::ConstPtr &observer) const
+ bool isComponentVisible(const QQmlJSScope::ConstPtr &observed,
+ const QQmlJSScope::ConstPtr &observer,
+ QQmlJSScopesByIdOptions options) const
{
- if (!m_componentsAreBound)
+ if (!m_componentsAreBound && !options.testAnyFlag(AssumeComponentsAreBound))
return observed == observer;
for (QQmlJSScope::ConstPtr scope = observer; scope; scope = scope->parentScope()) {
@@ -111,7 +121,7 @@ private:
QMultiHash<QString, QQmlJSScope::ConstPtr> m_scopesById;
bool m_componentsAreBound = false;
bool m_signaturesAreEnforced = true;
- bool m_valueTypesAreCopied = true;
+ bool m_valueTypesAreAddressable = false;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp
index 7ccef5e7bb..d542767dce 100644
--- a/src/qmlcompiler/qqmljsshadowcheck.cpp
+++ b/src/qmlcompiler/qqmljsshadowcheck.cpp
@@ -26,17 +26,32 @@ using namespace Qt::StringLiterals;
* 3. The property is declared final.
* 4. The object we are retrieving the property from is a value type. Value
* types cannot be used polymorphically.
+ *
+ * If the property is potentially shadowed, we can still retrieve it, but we
+ * don't know its type. We should assume "var" then.
+ *
+ * All of the above also holds for methods. There we have to transform the
+ * arguments and return types into "var".
*/
-void QQmlJSShadowCheck::run(
- const InstructionAnnotations *annotations, const Function *function,
- QQmlJS::DiagnosticMessage *error)
+QQmlJSCompilePass::BlocksAndAnnotations QQmlJSShadowCheck::run(const Function *function,
+ QQmlJS::DiagnosticMessage *error)
{
- m_annotations = annotations;
m_function = function;
m_error = error;
m_state = initialState(function);
decode(m_function->code.constData(), static_cast<uint>(m_function->code.size()));
+
+ for (const auto &store : m_resettableStores)
+ checkResettable(store.accumulatorIn, store.instructionOffset);
+
+ // Re-check all base types. We may have made them var after detecting them.
+ for (const auto &base : m_baseTypes) {
+ if (checkBaseType(base) == Shadowable)
+ break;
+ }
+
+ return { std::move(m_basicBlocks), std::move(m_annotations) };
}
void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex)
@@ -45,8 +60,11 @@ void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex)
return; // enum lookup cannot be shadowed.
auto accumulatorIn = m_state.registers.find(Accumulator);
- if (accumulatorIn != m_state.registers.end())
- checkShadowing(accumulatorIn.value().content, m_jsUnitGenerator->stringForIndex(nameIndex));
+ if (accumulatorIn != m_state.registers.end()) {
+ checkShadowing(
+ accumulatorIn.value().content, m_jsUnitGenerator->stringForIndex(nameIndex),
+ Accumulator);
+ }
}
void QQmlJSShadowCheck::generate_GetLookup(int index)
@@ -55,23 +73,70 @@ void QQmlJSShadowCheck::generate_GetLookup(int index)
return; // enum lookup cannot be shadowed.
auto accumulatorIn = m_state.registers.find(Accumulator);
- if (accumulatorIn != m_state.registers.end())
- checkShadowing(accumulatorIn.value().content, m_jsUnitGenerator->lookupName(index));
+ if (accumulatorIn != m_state.registers.end()) {
+ checkShadowing(
+ accumulatorIn.value().content, m_jsUnitGenerator->lookupName(index), Accumulator);
+ }
+}
+
+void QQmlJSShadowCheck::generate_GetOptionalLookup(int index, int offset)
+{
+ Q_UNUSED(offset);
+ generate_GetLookup(index);
+}
+
+void QQmlJSShadowCheck::handleStore(int base, const QString &memberName)
+{
+ const int instructionOffset = currentInstructionOffset();
+ const QQmlJSRegisterContent &readAccumulator
+ = m_annotations[instructionOffset].readRegisters[Accumulator].content;
+ const auto baseType = m_state.registers[base].content;
+
+ // If the accumulator is already read as var, we don't have to do anything.
+ if (m_typeResolver->registerContains(readAccumulator, m_typeResolver->varType())) {
+ if (checkBaseType(baseType) == NotShadowable)
+ m_baseTypes.append(baseType);
+ return;
+ }
+
+ if (checkShadowing(baseType, memberName, base) == Shadowable)
+ return;
+
+ // If the property isn't shadowable, we have to turn the read register into
+ // var if the accumulator can hold undefined. This has to be done in a second pass
+ // because the accumulator may still turn into var due to its own shadowing.
+ const QQmlJSRegisterContent member = m_typeResolver->memberType(baseType, memberName);
+ if (member.isProperty())
+ m_resettableStores.append({m_state.accumulatorIn(), instructionOffset});
}
void QQmlJSShadowCheck::generate_StoreProperty(int nameIndex, int base)
{
- checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->stringForIndex(nameIndex));
+ handleStore(base, m_jsUnitGenerator->stringForIndex(nameIndex));
}
void QQmlJSShadowCheck::generate_SetLookup(int index, int base)
{
- checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(index));
+ handleStore(base, m_jsUnitGenerator->lookupName(index));
+}
+
+void QQmlJSShadowCheck::generate_CallProperty(int nameIndex, int base, int argc, int argv)
+{
+ Q_UNUSED(argc);
+ Q_UNUSED(argv);
+ checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(nameIndex), base);
+}
+
+void QQmlJSShadowCheck::generate_CallPropertyLookup(int nameIndex, int base, int argc, int argv)
+{
+ Q_UNUSED(argc);
+ Q_UNUSED(argv);
+ checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(nameIndex), base);
}
QV4::Moth::ByteCodeHandler::Verdict QQmlJSShadowCheck::startInstruction(QV4::Moth::Instr::Type)
{
- m_state = nextStateFromAnnotations(m_state, *m_annotations);
+ m_state = nextStateFromAnnotations(m_state, m_annotations);
return (m_state.hasSideEffects() || m_state.changedRegisterIndex() != InvalidRegister)
? ProcessInstruction
: SkipInstruction;
@@ -81,17 +146,24 @@ void QQmlJSShadowCheck::endInstruction(QV4::Moth::Instr::Type)
{
}
-void QQmlJSShadowCheck::checkShadowing(
- const QQmlJSRegisterContent &baseType, const QString &memberName)
+QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkShadowing(
+ const QQmlJSRegisterContent &baseType, const QString &memberName, int baseRegister)
{
+ if (checkBaseType(baseType) == Shadowable)
+ return Shadowable;
+ else
+ m_baseTypes.append(baseType);
+
if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference)
- return;
+ return NotShadowable;
switch (baseType.variant()) {
- case QQmlJSRegisterContent::ObjectProperty:
case QQmlJSRegisterContent::ExtensionObjectProperty:
+ case QQmlJSRegisterContent::ExtensionScopeProperty:
+ case QQmlJSRegisterContent::MethodReturnValue:
+ case QQmlJSRegisterContent::ObjectProperty:
case QQmlJSRegisterContent::ScopeProperty:
- case QQmlJSRegisterContent::ExtensionScopeProperty: {
+ case QQmlJSRegisterContent::Unknown: {
const QQmlJSRegisterContent member = m_typeResolver->memberType(baseType, memberName);
// You can have something like parent.QtQuick.Screen.pixelDensity
@@ -100,26 +172,75 @@ void QQmlJSShadowCheck::checkShadowing(
// those are not shadowable.
if (!member.isValid()) {
Q_ASSERT(m_typeResolver->isPrefix(memberName));
- return;
+ return NotShadowable;
}
if (member.isProperty()) {
if (member.property().isFinal())
- return; // final properties can't be shadowed
+ return NotShadowable; // final properties can't be shadowed
} else if (!member.isMethod()) {
- return; // Only properties and methods can be shadowed
+ return NotShadowable; // Only properties and methods can be shadowed
}
- setError(u"Member %1 of %2 can be shadowed"_s
- .arg(memberName, m_state.accumulatorIn().descriptiveName()));
- return;
+ m_logger->log(
+ u"Member %1 of %2 can be shadowed"_s.arg(memberName, baseType.descriptiveName()),
+ qmlCompiler, currentSourceLocation());
+
+ // Make it "var". We don't know what it is.
+ const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
+ const QQmlJSRegisterContent varContent = m_typeResolver->globalType(varType);
+ InstructionAnnotation &currentAnnotation = m_annotations[currentInstructionOffset()];
+
+ if (currentAnnotation.changedRegisterIndex != InvalidRegister) {
+ m_typeResolver->adjustOriginalType(
+ currentAnnotation.changedRegister.storedType(), varType);
+ m_typeResolver->adjustOriginalType(
+ m_typeResolver->containedType(currentAnnotation.changedRegister), varType);
+ m_adjustedTypes.insert(currentAnnotation.changedRegister);
+ }
+
+ for (auto it = currentAnnotation.readRegisters.begin(),
+ end = currentAnnotation.readRegisters.end();
+ it != end; ++it) {
+ if (it.key() != baseRegister)
+ it->second.content = m_typeResolver->convert(it->second.content, varContent);
+ }
+ return Shadowable;
}
default:
// In particular ObjectById is fine as that cannot change into something else
// Singleton should also be fine, unless the factory function creates an object
// with different property types than the declared class.
- return;
+ return NotShadowable;
}
}
+void QQmlJSShadowCheck::checkResettable(
+ const QQmlJSRegisterContent &accumulatorIn, int instructionOffset)
+{
+ const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
+
+ // The stored type is not necessarily updated by the shadow check, but it
+ // will be in the basic blocks pass. For the purpose of adjusting newly
+ // shadowable types we can ignore it. We only want to know if any of the
+ // contents can hold undefined.
+ if (!m_typeResolver->canHoldUndefined(accumulatorIn.storedIn(varType)))
+ return;
+
+ const QQmlJSRegisterContent varContent = m_typeResolver->globalType(varType);
+
+ QQmlJSRegisterContent &readAccumulator
+ = m_annotations[instructionOffset].readRegisters[Accumulator].content;
+ readAccumulator = m_typeResolver->convert(readAccumulator, varContent);
+}
+
+QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkBaseType(
+ const QQmlJSRegisterContent &baseType)
+{
+ if (!m_adjustedTypes.contains(baseType))
+ return NotShadowable;
+ setError(u"Cannot use shadowable base type for further lookups: %1"_s.arg(baseType.descriptiveName()));
+ return Shadowable;
+}
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsshadowcheck_p.h b/src/qmlcompiler/qqmljsshadowcheck_p.h
index 92990a9eeb..dfa00134cb 100644
--- a/src/qmlcompiler/qqmljsshadowcheck_p.h
+++ b/src/qmlcompiler/qqmljsshadowcheck_p.h
@@ -18,31 +18,50 @@
QT_BEGIN_NAMESPACE
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSShadowCheck : public QQmlJSCompilePass
+class Q_QMLCOMPILER_EXPORT QQmlJSShadowCheck : public QQmlJSCompilePass
{
public:
QQmlJSShadowCheck(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
- const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
- : QQmlJSCompilePass(jsUnitGenerator, typeResolver, logger)
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks, InstructionAnnotations annotations)
+ : QQmlJSCompilePass(jsUnitGenerator, typeResolver, logger, basicBlocks, annotations)
{}
~QQmlJSShadowCheck() = default;
- void run(const InstructionAnnotations *annotations, const Function *function,
- QQmlJS::DiagnosticMessage *error);
+ BlocksAndAnnotations run(const Function *function, QQmlJS::DiagnosticMessage *error);
private:
+ struct ResettableStore {
+ QQmlJSRegisterContent accumulatorIn;
+ int instructionOffset = -1;
+ };
+
+ void handleStore(int base, const QString &memberName);
+
void generate_LoadProperty(int nameIndex) override;
void generate_GetLookup(int index) override;
+ void generate_GetOptionalLookup(int index, int offset) override;
void generate_StoreProperty(int nameIndex, int base) override;
void generate_SetLookup(int index, int base) override;
+ void generate_CallProperty(int nameIndex, int base, int argc, int argv) override;
+ void generate_CallPropertyLookup(int nameIndex, int base, int argc, int argv) override;
QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type) override;
void endInstruction(QV4::Moth::Instr::Type) override;
- void checkShadowing(const QQmlJSRegisterContent &baseType, const QString &propertyName);
+ enum Shadowability { NotShadowable, Shadowable };
+ Shadowability checkShadowing(
+ const QQmlJSRegisterContent &baseType, const QString &propertyName, int baseRegister);
+
+ void checkResettable(const QQmlJSRegisterContent &accumulatorIn, int instructionOffset);
+
+ Shadowability checkBaseType(const QQmlJSRegisterContent &baseType);
+
+ QList<ResettableStore> m_resettableStores;
+ QList<QQmlJSRegisterContent> m_baseTypes;
+ QSet<QQmlJSRegisterContent> m_adjustedTypes;
- const InstructionAnnotations *m_annotations = nullptr;
State m_state;
};
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
index 543dec9ff6..937c35ddcd 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
@@ -19,20 +19,19 @@ QT_BEGIN_NAMESPACE
* operates only on the annotations and the function description.
*/
-QQmlJSCompilePass::InstructionAnnotations QQmlJSStorageGeneralizer::run(
- InstructionAnnotations annotations, Function *function,
- QQmlJS::DiagnosticMessage *error)
+QQmlJSCompilePass::BlocksAndAnnotations
+QQmlJSStorageGeneralizer::run(Function *function, QQmlJS::DiagnosticMessage *error)
{
m_error = error;
- if (QQmlJSScope::ConstPtr &returnType = function->returnType) {
+ if (QQmlJSRegisterContent &returnType = function->returnType; returnType.isValid()) {
if (QQmlJSScope::ConstPtr stored = m_typeResolver->genericType(
- returnType, QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
- returnType = stored;
+ returnType.storedType(), QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
+ returnType = returnType.storedIn(stored);
} else {
setError(QStringLiteral("Cannot store the return type %1.")
- .arg(returnType->internalName(), 0));
- return InstructionAnnotations();
+ .arg(returnType.storedType()->internalName()));
+ return {};
}
}
@@ -53,12 +52,12 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSStorageGeneralizer::run(
transformRegister(argument);
}
- for (auto i = annotations.begin(), iEnd = annotations.end(); i != iEnd; ++i) {
+ for (auto i = m_annotations.begin(), iEnd = m_annotations.end(); i != iEnd; ++i) {
transformRegister(i->second.changedRegister);
transformRegisters(i->second.typeConversions);
}
- return annotations;
+ return { std::move(m_basicBlocks), std::move(m_annotations) };
}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer_p.h b/src/qmlcompiler/qqmljsstoragegeneralizer_p.h
index d04e5ce1dd..9ef4699fca 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer_p.h
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer_p.h
@@ -18,16 +18,16 @@
QT_BEGIN_NAMESPACE
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSStorageGeneralizer : public QQmlJSCompilePass
+class Q_QMLCOMPILER_EXPORT QQmlJSStorageGeneralizer : public QQmlJSCompilePass
{
public:
QQmlJSStorageGeneralizer(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
- const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
- : QQmlJSCompilePass(jsUnitGenerator, typeResolver, logger)
+ const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
+ BasicBlocks basicBlocks, InstructionAnnotations annotations)
+ : QQmlJSCompilePass(jsUnitGenerator, typeResolver, logger, basicBlocks, annotations)
{}
- InstructionAnnotations run(InstructionAnnotations annotations, Function *function,
- QQmlJS::DiagnosticMessage *error);
+ BlocksAndAnnotations run(Function *function, QQmlJS::DiagnosticMessage *error);
protected:
// We don't have to use the byte code here. We only transform the instruction annotations.
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index 8528d39f0f..bebc01a8ef 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -166,6 +166,7 @@ void QQmlJSTypeDescriptionReader::readDependencies(UiScriptBinding *ast)
void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
{
+ m_currentCtorIndex = 0;
QQmlJSScope::Ptr scope = QQmlJSScope::create();
QList<QQmlJSScope::Export> exports;
@@ -200,6 +201,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
scope->setOwnParentPropertyName(readStringBinding(script));
} else if (name == QLatin1String("exports")) {
exports = readExports(script);
+ } else if (name == QLatin1String("aliases")) {
+ readAliases(script, scope);
} else if (name == QLatin1String("interfaces")) {
readInterfaces(script, scope);
} else if (name == QLatin1String("exportMetaObjectRevisions")) {
@@ -212,6 +215,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
scope->setIsSingleton(readBoolBinding(script));
} else if (name == QLatin1String("isCreatable")) {
scope->setCreatableFlag(readBoolBinding(script));
+ } else if (name == QLatin1String("isStructured")) {
+ scope->setStructuredFlag(readBoolBinding(script));
} else if (name == QLatin1String("isComposite")) {
scope->setIsComposite(readBoolBinding(script));
} else if (name == QLatin1String("hasCustomParser")) {
@@ -232,6 +237,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
}
} else if (name == QLatin1String("extension")) {
scope->setExtensionTypeName(readStringBinding(script));
+ } else if (name == QLatin1String("extensionIsJavaScript")) {
+ scope->setExtensionIsJavaScript(readBoolBinding(script));
} else if (name == QLatin1String("extensionIsNamespace")) {
scope->setExtensionIsNamespace(readBoolBinding(script));
} else if (name == QLatin1String("deferredNames")) {
@@ -242,8 +249,9 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
addWarning(script->firstSourceLocation(),
tr("Expected only name, prototype, defaultProperty, attachedType, "
"valueType, exports, interfaces, isSingleton, isCreatable, "
- "isComposite, hasCustomParser, exportMetaObjectRevisions, "
- "deferredNames, and immediateNames in script bindings, not \"%1\".")
+ "isStructured, isComposite, hasCustomParser, aliases, "
+ "exportMetaObjectRevisions, deferredNames, and immediateNames "
+ "in script bindings, not \"%1\".")
.arg(name));
}
} else {
@@ -262,15 +270,15 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
m_objects->append({scope, exports});
}
-void QQmlJSTypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod,
- const QQmlJSScope::Ptr &scope)
+void QQmlJSTypeDescriptionReader::readSignalOrMethod(
+ UiObjectDefinition *ast, bool isMethod, const QQmlJSScope::Ptr &scope)
{
QQmlJSMetaMethod metaMethod;
// ### confusion between Method and Slot. Method should be removed.
if (isMethod)
- metaMethod.setMethodType(QQmlJSMetaMethod::Slot);
+ metaMethod.setMethodType(QQmlJSMetaMethodType::Slot);
else
- metaMethod.setMethodType(QQmlJSMetaMethod::Signal);
+ metaMethod.setMethodType(QQmlJSMetaMethodType::Signal);
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
@@ -293,22 +301,40 @@ void QQmlJSTypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bo
} else if (name == QLatin1String("revision")) {
metaMethod.setRevision(readIntBinding(script));
} else if (name == QLatin1String("isCloned")) {
- metaMethod.setIsCloned(true);
+ metaMethod.setIsCloned(readBoolBinding(script));
} else if (name == QLatin1String("isConstructor")) {
- metaMethod.setIsConstructor(true);
+ // The constructors in the moc json output are ordered the same
+ // way as the ones in the metaobject. qmltyperegistrar moves them into
+ // the same list as the other members, but maintains their order.
+ if (readBoolBinding(script)) {
+ metaMethod.setIsConstructor(true);
+ metaMethod.setConstructorIndex(
+ QQmlJSMetaMethod::RelativeFunctionIndex(m_currentCtorIndex++));
+ }
} else if (name == QLatin1String("isJavaScriptFunction")) {
- metaMethod.setIsJavaScriptFunction(true);
+ metaMethod.setIsJavaScriptFunction(readBoolBinding(script));
} else if (name == QLatin1String("isList")) {
- // TODO: Theoretically this can happen. QQmlJSMetaMethod should store it.
+ auto metaReturnType = metaMethod.returnValue();
+ metaReturnType.setIsList(readBoolBinding(script));
+ metaMethod.setReturnValue(metaReturnType);
} else if (name == QLatin1String("isPointer")) {
// TODO: We don't need this information. We can probably drop all isPointer members
// once we make sure that the type information is always complete. The
// description of the type being referenced has access semantics after all.
+ auto metaReturnType = metaMethod.returnValue();
+ metaReturnType.setIsPointer(readBoolBinding(script));
+ metaMethod.setReturnValue(metaReturnType);
+ } else if (name == QLatin1String("isConstant")) {
+ auto metaReturnType = metaMethod.returnValue();
+ metaReturnType.setTypeQualifier(readBoolBinding(script)
+ ? QQmlJSMetaParameter::Const
+ : QQmlJSMetaParameter::NonConst);
+ metaMethod.setReturnValue(metaReturnType);
} else {
addWarning(script->firstSourceLocation(),
- tr("Expected only name, type, revision, isPointer, isList, "
- "isCloned, isConstructor, and "
- "isJavaScriptFunction in script bindings."));
+ tr("Expected only name, type, revision, isPointer, isConstant, "
+ "isList, isCloned, isConstructor, and isJavaScriptFunction "
+ "in script bindings."));
}
} else {
addWarning(member->firstSourceLocation(),
@@ -412,11 +438,13 @@ void QQmlJSTypeDescriptionReader::readEnum(UiObjectDefinition *ast, const QQmlJS
metaEnum.setIsFlag(readBoolBinding(script));
} else if (name == QLatin1String("values")) {
readEnumValues(script, &metaEnum);
- } else if (name == QLatin1String("scoped")) {
- metaEnum.setScoped(readBoolBinding(script));
+ } else if (name == QLatin1String("isScoped")) {
+ metaEnum.setIsScoped(readBoolBinding(script));
+ } else if (name == QLatin1String("type")) {
+ metaEnum.setTypeName(readStringBinding(script));
} else {
addWarning(script->firstSourceLocation(),
- tr("Expected only name and values script bindings."));
+ tr("Expected only name, alias, isFlag, values, isScoped, or type."));
}
}
@@ -429,6 +457,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM
QString type;
bool isConstant = false;
bool isPointer = false;
+ bool isList = false;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
@@ -450,16 +479,18 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM
} else if (id == QLatin1String("isReadonly")) {
// ### unhandled
} else if (id == QLatin1String("isList")) {
- // ### unhandled
+ isList = readBoolBinding(script);
} else {
addWarning(script->firstSourceLocation(),
- tr("Expected only name and type script bindings."));
+ tr("Expected only name, type, isPointer, isConstant, isReadonly, "
+ "or IsList script bindings."));
}
}
QQmlJSMetaParameter p(name, type);
p.setTypeQualifier(isConstant ? QQmlJSMetaParameter::Const : QQmlJSMetaParameter::NonConst);
p.setIsPointer(isPointer);
+ p.setIsList(isList);
metaMethod->addParameter(std::move(p));
}
@@ -657,6 +688,12 @@ QList<QQmlJSScope::Export> QQmlJSTypeDescriptionReader::readExports(UiScriptBind
return exports;
}
+void QQmlJSTypeDescriptionReader::readAliases(
+ QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope)
+{
+ scope->setAliases(readStringList(ast));
+}
+
void QQmlJSTypeDescriptionReader::readInterfaces(UiScriptBinding *ast, const QQmlJSScope::Ptr &scope)
{
auto *arrayLit = getArray(ast);
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader_p.h b/src/qmlcompiler/qqmljstypedescriptionreader_p.h
index 37dd388308..2bbae61fd6 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader_p.h
+++ b/src/qmlcompiler/qqmljstypedescriptionreader_p.h
@@ -4,7 +4,7 @@
#ifndef QQMLJSTYPEDESCRIPTIONREADER_P_H
#define QQMLJSTYPEDESCRIPTIONREADER_P_H
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
//
// W A R N I N G
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSTypeDescriptionReader
+class Q_QMLCOMPILER_EXPORT QQmlJSTypeDescriptionReader
{
Q_DECLARE_TR_FUNCTIONS(QQmlJSTypeDescriptionReader)
public:
@@ -55,6 +55,7 @@ private:
QTypeRevision readNumericVersionBinding(QQmlJS::AST::UiScriptBinding *ast);
int readIntBinding(QQmlJS::AST::UiScriptBinding *ast);
QList<QQmlJSScope::Export> readExports(QQmlJS::AST::UiScriptBinding *ast);
+ void readAliases(QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope);
void readInterfaces(QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope);
void checkMetaObjectRevisions(
QQmlJS::AST::UiScriptBinding *ast, QList<QQmlJSScope::Export> *exports);
@@ -75,6 +76,7 @@ private:
QString m_warningMessage;
QList<QQmlJSExportedScope> *m_objects = nullptr;
QStringList *m_dependencies = nullptr;
+ int m_currentCtorIndex = 0;
};
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 380042e2fe..d7a7d68d9f 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -9,6 +9,8 @@
#include <private/qv4compilerscanfunctions_p.h>
+#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -26,20 +28,20 @@ using namespace Qt::StringLiterals;
QQmlJSTypePropagator::QQmlJSTypePropagator(const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver,
- QQmlJSLogger *logger, QQmlJSTypeInfo *typeInfo,
+ QQmlJSLogger *logger, BasicBlocks basicBlocks,
+ InstructionAnnotations annotations,
QQmlSA::PassManager *passManager)
- : QQmlJSCompilePass(unitGenerator, typeResolver, logger),
- m_typeInfo(typeInfo),
+ : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations),
m_passManager(passManager)
{
}
-QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
+QQmlJSCompilePass::BlocksAndAnnotations QQmlJSTypePropagator::run(
const Function *function, QQmlJS::DiagnosticMessage *error)
{
m_function = function;
m_error = error;
- m_returnType = m_typeResolver->globalType(m_function->returnType);
+ m_returnType = m_function->returnType;
do {
// Reset the error if we need to do another pass
@@ -48,6 +50,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
m_prevStateAnnotations = m_state.annotations;
m_state = PassState();
+ m_state.annotations = m_annotations;
m_state.State::operator=(initialState(m_function));
reset();
@@ -58,34 +61,49 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
// This means that we won't start over for the same reason again.
} while (m_state.needsMorePasses);
- return m_state.annotations;
+ return { std::move(m_basicBlocks), std::move(m_state.annotations) };
}
-#define INSTR_PROLOGUE_NOT_IMPLEMENTED() \
- setError(u"Instruction \"%1\" not implemented"_s \
- .arg(QString::fromUtf8(__func__))); \
- return;
+#define INSTR_PROLOGUE_NOT_IMPLEMENTED() \
+ setError(u"Instruction \"%1\" not implemented"_s.arg(QString::fromUtf8(__func__))); \
+ return;
#define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE() \
m_logger->log(u"Instruction \"%1\" not implemented"_s.arg(QString::fromUtf8(__func__)), \
qmlCompiler, QQmlJS::SourceLocation()); \
return;
+void QQmlJSTypePropagator::generate_ret_SAcheck()
+{
+ if (!m_function->isProperty)
+ return;
+ QQmlSA::PassManagerPrivate::get(m_passManager)
+ ->analyzeBinding(QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
+ QQmlJSScope::createQQmlSAElement(
+ m_typeResolver->containedType(m_state.accumulatorIn())),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
void QQmlJSTypePropagator::generate_Ret()
{
- if (m_passManager != nullptr && m_function->isProperty) {
- m_passManager->analyzeBinding(m_function->qmlScope,
- m_typeResolver->containedType(m_state.accumulatorIn()),
- getCurrentBindingSourceLocation());
- }
+ if (m_passManager != nullptr)
+ generate_ret_SAcheck();
if (m_function->isSignalHandler) {
// Signal handlers cannot return anything.
- } else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()
- && !m_typeResolver->registerContains(
- m_state.accumulatorIn(), m_typeResolver->voidType())) {
- setError(u"function without type annotation returns %1"_s
- .arg(m_state.accumulatorIn().descriptiveName()));
+ } else if (m_typeResolver->registerContains(
+ m_state.accumulatorIn(), m_typeResolver->voidType())) {
+ // You can always return undefined.
+ } else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()) {
+ setError(u"function without return type annotation returns %1. This may prevent proper "_s
+ u"compilation to Cpp."_s.arg(m_state.accumulatorIn().descriptiveName()));
+
+ if (m_function->isFullyTyped) {
+ // Do not complain if the function didn't have a valid annotation in the first place.
+ m_logger->log(u"Function without return type annotation returns %1"_s.arg(
+ m_typeResolver->containedTypeName(m_state.accumulatorIn(), true)),
+ qmlIncompatibleType, getCurrentBindingSourceLocation());
+ }
return;
} else if (!canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
setError(u"cannot convert from %1 to %2"_s
@@ -124,7 +142,7 @@ void QQmlJSTypePropagator::generate_LoadConst(int index)
void QQmlJSTypePropagator::generate_LoadZero()
{
- setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->int32Type()));
}
void QQmlJSTypePropagator::generate_LoadTrue()
@@ -149,7 +167,7 @@ void QQmlJSTypePropagator::generate_LoadUndefined()
void QQmlJSTypePropagator::generate_LoadInt(int)
{
- setAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->int32Type()));
}
void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp)
@@ -292,7 +310,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM
return;
}
- std::optional<FixSuggestion> suggestion;
+ std::optional<QQmlJSFixSuggestion> suggestion;
auto childScopes = m_function->qmlScope->childScopes();
for (qsizetype i = 0; i < m_function->qmlScope->childScopes().size(); i++) {
@@ -304,12 +322,9 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM
if (scope->childScopes().size() == 0)
continue;
- const auto jsId = scope->childScopes().first()->findJSIdentifier(name);
+ const auto jsId = scope->childScopes().first()->jsIdentifier(name);
if (jsId.has_value() && jsId->kind == QQmlJSScope::JavaScriptIdentifier::Injected) {
-
- suggestion = FixSuggestion {};
-
const QQmlJSScope::JavaScriptIdentifier id = jsId.value();
QQmlJS::SourceLocation fixLocation = id.location;
@@ -328,15 +343,15 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM
fixString += handler.isMultiline ? u") "_s : u") => "_s;
- suggestion->fixes << FixSuggestion::Fix {
- name
- + QString::fromLatin1(" is accessible in this scope because "
- "you are handling a signal at %1:%2. Use a "
- "function instead.\n")
- .arg(id.location.startLine)
- .arg(id.location.startColumn),
- fixLocation, fixString, QString(), false
+ suggestion = QQmlJSFixSuggestion {
+ name + u" is accessible in this scope because you are handling a signal"
+ " at %1:%2. Use a function instead.\n"_s
+ .arg(id.location.startLine)
+ .arg(id.location.startColumn),
+ fixLocation,
+ fixString
};
+ suggestion->setAutoApplicable();
}
break;
}
@@ -354,11 +369,10 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM
if (!it->hasObject())
continue;
if (it->objectType() == m_function->qmlScope) {
- suggestion = FixSuggestion {};
-
- suggestion->fixes << FixSuggestion::Fix {
- name + u" is implicitly injected into this delegate. Add a required property instead."_s,
- m_function->qmlScope->sourceLocation(), QString(), QString(), true
+ suggestion = QQmlJSFixSuggestion {
+ name + " is implicitly injected into this delegate."
+ " Add a required property instead."_L1,
+ m_function->qmlScope->sourceLocation()
};
};
@@ -371,38 +385,36 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM
for (QQmlJSScope::ConstPtr scope = m_function->qmlScope; !scope.isNull();
scope = scope->parentScope()) {
if (scope->hasProperty(name)) {
- const QString id = m_function->addressableScopes.id(scope);
-
- suggestion = FixSuggestion {};
+ const QString id = m_function->addressableScopes.id(scope, m_function->qmlScope);
QQmlJS::SourceLocation fixLocation = location;
fixLocation.length = 0;
- suggestion->fixes << FixSuggestion::Fix {
- name + QLatin1String(" is a member of a parent element\n")
- + QLatin1String(" You can qualify the access with its id "
- "to avoid this warning:\n"),
- fixLocation, (id.isEmpty() ? u"<id>."_s : (id + u'.')), QString(), id.isEmpty()
+ suggestion = QQmlJSFixSuggestion{
+ name
+ + " is a member of a parent element.\n You can qualify the access "
+ "with its id to avoid this warning.\n"_L1,
+ fixLocation, (id.isEmpty() ? u"<id>."_s : (id + u'.'))
};
- if (id.isEmpty()) {
- suggestion->fixes << FixSuggestion::Fix {
- u"You first have to give the element an id"_s, QQmlJS::SourceLocation {}, {}
- };
- }
+ if (id.isEmpty())
+ suggestion->setHint("You first have to give the element an id"_L1);
+ else
+ suggestion->setAutoApplicable();
}
}
}
if (!suggestion.has_value() && !m_function->addressableScopes.componentsAreBound()
&& m_function->addressableScopes.existsAnywhereInDocument(name)) {
- FixSuggestion::Fix bindComponents;
const QLatin1String replacement = "pragma ComponentBehavior: Bound"_L1;
- bindComponents.replacementString = replacement + '\n'_L1;
- bindComponents.message = "Set \"%1\" in order to use IDs "
- "from outer components in nested components."_L1.arg(replacement);
- bindComponents.cutLocation = QQmlJS::SourceLocation(0, 0, 1, 1);
- bindComponents.isHint = false;
- suggestion = FixSuggestion {{ bindComponents }};
+ QQmlJSFixSuggestion bindComponents {
+ "Set \"%1\" in order to use IDs from outer components in nested components."_L1
+ .arg(replacement),
+ QQmlJS::SourceLocation(0, 0, 1, 1),
+ replacement + '\n'_L1
+ };
+ bindComponents.setAutoApplicable();
+ suggestion = bindComponents;
}
if (!suggestion.has_value()) {
@@ -468,52 +480,6 @@ void QQmlJSTypePropagator::checkDeprecated(QQmlJSScope::ConstPtr scope, const QS
m_logger->log(message, qmlDeprecated, getCurrentSourceLocation());
}
-bool QQmlJSTypePropagator::isRestricted(const QString &propertyName) const
-{
- QString restrictedKind;
-
- const auto accumulatorIn = m_state.registers.find(Accumulator);
- if (accumulatorIn == m_state.registers.end())
- return false;
-
- if (accumulatorIn.value().content.isList() && propertyName != u"length") {
- restrictedKind = u"a list"_s;
- } else if (accumulatorIn.value().content.isEnumeration()) {
- const auto metaEn = accumulatorIn.value().content.enumeration();
- if (metaEn.isScoped()) {
- if (!metaEn.hasKey(propertyName))
- restrictedKind = u"an enum"_s;
- } else {
- restrictedKind = u"an unscoped enum"_s;
- }
- } else if (accumulatorIn.value().content.isMethod()) {
- auto overloadSet = accumulatorIn.value().content.method();
- auto potentiallyJSMethod = std::any_of(
- overloadSet.cbegin(), overloadSet.cend(),
- [](const QQmlJSMetaMethod &overload){
- return overload.isJavaScriptFunction();
- });
- if (potentiallyJSMethod) {
- /* JS global constructors like Number get detected as methods
- However, they still have properties that can be accessed
- e.g. Number.EPSILON. This also isn't restricted to constructor
- functions, so use isJavaScriptFunction as an overapproximation.
- That catches also QQmlV4Function, but we're purging uses of it
- anyway.
- */
- return false;
- }
- restrictedKind = u"a method"_s;
- }
-
- if (!restrictedKind.isEmpty())
- m_logger->log(u"Type is %1. You cannot access \"%2\" from here."_s.arg(restrictedKind,
- propertyName),
- qmlRestrictedType, getCurrentSourceLocation());
-
- return !restrictedKind.isEmpty();
-}
-
// Only to be called once a lookup has already failed
QQmlJSTypePropagator::PropertyResolution QQmlJSTypePropagator::propertyResolution(
QQmlJSScope::ConstPtr scope, const QString &propertyName) const
@@ -554,15 +520,17 @@ bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const
if (!methods.isEmpty()) {
errorType = u"shadowed by a property."_s;
switch (methods.first().methodType()) {
- case QQmlJSMetaMethod::Signal:
+ case QQmlJSMetaMethodType::Signal:
propertyType = u"Signal"_s;
break;
- case QQmlJSMetaMethod::Slot:
+ case QQmlJSMetaMethodType::Slot:
propertyType = u"Slot"_s;
break;
- case QQmlJSMetaMethod::Method:
+ case QQmlJSMetaMethodType::Method:
propertyType = u"Method"_s;
break;
+ default:
+ Q_UNREACHABLE();
}
} else if (m_typeResolver->equals(property.type(), m_typeResolver->varType())) {
errorType =
@@ -580,6 +548,17 @@ bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const
return true;
}
+
+void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup_SAcheck(const QString &name)
+{
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope), name,
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
+
+
void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
{
// LoadQmlContextPropertyLookup does not use accumulatorIn. It always refers to the scope.
@@ -588,7 +567,7 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
- setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name));
+ setAccumulator(m_typeResolver->scopedType(m_function->qmlScope, name, index));
if (!m_state.accumulatorOut().isValid() && m_typeResolver->isPrefix(name)) {
const QQmlJSRegisterContent inType = m_typeResolver->globalType(m_function->qmlScope);
@@ -603,36 +582,77 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
if (!m_state.accumulatorOut().isValid()) {
setError(u"Cannot access value for name "_s + name);
handleUnqualifiedAccess(name, false);
- } else if (m_typeResolver->genericType(m_state.accumulatorOut().storedType()).isNull()) {
+ return;
+ }
+
+ const QQmlJSScope::ConstPtr outStored
+ = m_typeResolver->genericType(m_state.accumulatorOut().storedType());
+
+ if (outStored.isNull()) {
// It should really be valid.
// We get the generic type from aotContext->loadQmlContextPropertyIdLookup().
setError(u"Cannot determine generic type for "_s + name);
- } else if (m_passManager != nullptr) {
- m_passManager->analyzeRead(m_function->qmlScope, name, m_function->qmlScope,
- getCurrentSourceLocation());
+ return;
+ }
+
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById
+ && !outStored->isReferenceType()) {
+ setError(u"Cannot retrieve a non-object type by ID: "_s + name);
+ return;
}
+ if (m_passManager != nullptr)
+ generate_LoadQmlContextPropertyLookup_SAcheck(name);
+
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ScopeAttached)
m_attachedContext = QQmlJSScope::ConstPtr();
}
-void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
+void QQmlJSTypePropagator::generate_StoreNameCommon_SAcheck(const QQmlJSRegisterContent &in, const QString &name)
+{
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope), name,
+ QQmlJSScope::createQQmlSAElement(m_typeResolver->containedType(in)),
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
+
+/*!
+ \internal
+ As far as type propagation is involved, StoreNameSloppy and
+ StoreNameStrict are completely the same
+ StoreNameStrict is rejecting a few writes (where the variable was not
+ defined before) that would work in a sloppy context in JS, but the
+ compiler would always reject this. And for type propagation, this does
+ not matter at all.
+ \a nameIndex is the index in the string table corresponding to
+ the name which we are storing
+ */
+void QQmlJSTypePropagator::generate_StoreNameCommon(int nameIndex)
{
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
const QQmlJSRegisterContent in = m_state.accumulatorIn();
if (!type.isValid()) {
+ handleUnqualifiedAccess(name, false);
setError(u"Cannot find name "_s + name);
return;
}
if (!type.isProperty()) {
+ QString message = type.isMethod() ? u"Cannot assign to method %1"_s
+ : u"Cannot assign to non-property %1"_s;
+ // The interpreter treats methods as read-only properties in its error messages
+ // and we lack a better fitting category. We might want to revisit this later.
+ m_logger->log(message.arg(name), qmlReadOnlyProperty,
+ getCurrentSourceLocation());
setError(u"Cannot assign to non-property "_s + name);
return;
}
- if (!type.isWritable() && !m_function->qmlScope->hasOwnProperty(name)) {
+ if (!type.isWritable()) {
setError(u"Can't assign to read-only property %1"_s.arg(name));
m_logger->log(u"Cannot assign to read-only property %1"_s.arg(name), qmlReadOnlyProperty,
@@ -646,53 +666,95 @@ void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
.arg(in.descriptiveName(), type.descriptiveName()));
}
- if (m_passManager != nullptr) {
- m_passManager->analyzeWrite(m_function->qmlScope, name,
- m_typeResolver->containedType(in),
- m_function->qmlScope, getCurrentSourceLocation());
- }
+ if (m_passManager != nullptr)
+ generate_StoreNameCommon_SAcheck(in, name);
- m_state.setHasSideEffects(true);
if (m_typeResolver->canHoldUndefined(in) && !m_typeResolver->canHoldUndefined(type)) {
- if (type.property().reset().isEmpty())
- setError(u"Cannot assign potential undefined to %1"_s.arg(type.descriptiveName()));
- else if (m_typeResolver->registerIsStoredIn(in, m_typeResolver->voidType()))
+ if (m_typeResolver->registerIsStoredIn(in, m_typeResolver->voidType()))
addReadAccumulator(m_typeResolver->globalType(m_typeResolver->varType()));
else
addReadAccumulator(in);
} else {
addReadAccumulator(type);
}
+
+ m_state.setHasSideEffects(true);
+}
+
+void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex)
+{
+ return generate_StoreNameCommon(nameIndex);
}
void QQmlJSTypePropagator::generate_StoreNameStrict(int name)
{
- m_state.setHasSideEffects(true);
- Q_UNUSED(name)
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ return generate_StoreNameCommon(name);
}
-void QQmlJSTypePropagator::generate_LoadElement(int base)
+bool QQmlJSTypePropagator::checkForEnumProblems(
+ const QQmlJSRegisterContent &base, const QString &propertyName)
{
- const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
+ if (base.isEnumeration()) {
+ const auto metaEn = base.enumeration();
+ if (!metaEn.hasKey(propertyName)) {
+ auto fixSuggestion = QQmlJSUtils::didYouMean(propertyName, metaEn.keys(),
+ getCurrentSourceLocation());
+ const QString error = u"\"%1\" is not an entry of enum \"%2\"."_s
+ .arg(propertyName, metaEn.name());
+ setError(error);
+ m_logger->log(
+ error, qmlMissingEnumEntry, getCurrentSourceLocation(), true, true,
+ fixSuggestion);
+ return true;
+ }
+ } else if (base.variant() == QQmlJSRegisterContent::MetaType) {
+ const QQmlJSMetaEnum metaEn = base.scopeType()->enumeration(propertyName);
+ if (metaEn.isValid() && !metaEn.isScoped() && !metaEn.isQml()) {
+ const QString error
+ = u"You cannot access unscoped enum \"%1\" from here."_s.arg(propertyName);
+ setError(error);
+ m_logger->log(error, qmlRestrictedType, getCurrentSourceLocation());
+ return true;
+ }
+ }
- if ((baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
- && !m_typeResolver->registerIsStoredIn(baseRegister, m_typeResolver->stringType()))
- || !m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ return false;
+}
+
+void QQmlJSTypePropagator::generate_LoadElement(int base)
+{
+ const auto fallback = [&]() {
const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
addReadAccumulator(jsValue);
addReadRegister(base, jsValue);
setAccumulator(jsValue);
+ };
+
+ const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
+ if (!baseRegister.isList()
+ && !m_typeResolver->registerContains(baseRegister, m_typeResolver->stringType())) {
+ fallback();
return;
}
+ addReadRegister(base, baseRegister);
- if (m_typeResolver->isIntegral(m_state.accumulatorIn()))
- addReadAccumulator(m_typeResolver->globalType(m_typeResolver->intType()));
- else
- addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType()));
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
+ const auto contained = m_typeResolver->containedType(m_state.accumulatorIn());
+ if (m_typeResolver->isSignedInteger(contained))
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->sizeType()));
+ else if (m_typeResolver->isUnsignedInteger(contained))
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->uint32Type()));
+ else
+ addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType()));
+ } else if (m_typeResolver->isNumeric(m_typeResolver->extractNonVoidFromOptionalType(
+ m_state.accumulatorIn()))) {
+ addReadAccumulator(m_state.accumulatorIn());
+ } else {
+ fallback();
+ return;
+ }
- addReadRegister(base, baseRegister);
// We can end up with undefined.
setAccumulator(m_typeResolver->merge(
m_typeResolver->valueType(baseRegister),
@@ -704,7 +766,7 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
const QQmlJSRegisterContent baseRegister = m_state.registers[base].content;
const QQmlJSRegisterContent indexRegister = checkedInputRegister(index);
- if (baseRegister.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
+ if (!baseRegister.isList()
|| !m_typeResolver->isNumeric(indexRegister)) {
const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
addReadAccumulator(jsValue);
@@ -717,8 +779,11 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
return;
}
- if (m_typeResolver->isIntegral(indexRegister))
- addReadRegister(index, m_typeResolver->globalType(m_typeResolver->intType()));
+ const auto contained = m_typeResolver->containedType(indexRegister);
+ if (m_typeResolver->isSignedInteger(contained))
+ addReadRegister(index, m_typeResolver->globalType(m_typeResolver->int32Type()));
+ else if (m_typeResolver->isUnsignedInteger(contained))
+ addReadRegister(index, m_typeResolver->globalType(m_typeResolver->uint32Type()));
else
addReadRegister(index, m_typeResolver->globalType(m_typeResolver->realType()));
@@ -733,7 +798,22 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index)
m_state.setHasSideEffects(true);
}
-void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
+void QQmlJSTypePropagator::propagatePropertyLookup_SAcheck(const QString &propertyName)
+{
+ const bool isAttached =
+ m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectAttached;
+
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
+ QQmlJSScope::createQQmlSAElement(
+ m_typeResolver->containedType(m_state.accumulatorIn())),
+ propertyName,
+ QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
+ : m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
+
+void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName, int lookupIndex)
{
setAccumulator(
m_typeResolver->memberType(
@@ -741,52 +821,7 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
m_state.accumulatorIn().isImportNamespace()
? m_jsUnitGenerator->stringForIndex(m_state.accumulatorIn().importNamespace())
+ u'.' + propertyName
- : propertyName));
-
- if (m_typeInfo != nullptr
- && m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ScopeAttached) {
- QQmlJSScope::ConstPtr attachedType = m_typeResolver->originalType(
- m_state.accumulatorIn().scopeType());
-
- for (QQmlJSScope::ConstPtr scope = m_function->qmlScope->parentScope(); !scope.isNull();
- scope = scope->parentScope()) {
- if (m_typeInfo->usedAttachedTypes.values(scope).contains(attachedType)) {
-
- // Ignore enum accesses, as these will not cause the attached object to be created
- if (m_state.accumulatorOut().isValid() && m_state.accumulatorOut().isEnumeration())
- continue;
-
- const QString id = m_function->addressableScopes.id(scope);
-
- FixSuggestion suggestion;
-
- QQmlJS::SourceLocation fixLocation = getCurrentSourceLocation();
- fixLocation.length = 0;
-
- suggestion.fixes << FixSuggestion::Fix { u"Reference it by id instead:"_s,
- fixLocation,
- id.isEmpty() ? u"<id>."_s : (id + u'.'),
- QString(), id.isEmpty() };
-
- fixLocation = scope->sourceLocation();
- fixLocation.length = 0;
-
- if (id.isEmpty()) {
- suggestion.fixes
- << FixSuggestion::Fix { u"You first have to give the element an id"_s,
- QQmlJS::SourceLocation {},
- {} };
- }
-
- m_logger->log(
- u"Using attached type %1 already initialized in a parent scope."_s.arg(
- m_state.accumulatorIn().scopeType()->internalName()),
- qmlAttachedPropertyReuse, getCurrentSourceLocation(), true, true,
- suggestion);
- }
- }
- m_typeInfo->usedAttachedTypes.insert(m_function->qmlScope, attachedType);
- }
+ : propertyName, lookupIndex));
if (!m_state.accumulatorOut().isValid()) {
if (m_typeResolver->isPrefix(propertyName)) {
@@ -808,17 +843,29 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
u"Cannot access singleton as a property of an object. Did you want to access an attached object?"_s,
qmlAccessSingleton, getCurrentSourceLocation());
setAccumulator(QQmlJSRegisterContent());
+ } else if (m_state.accumulatorOut().isEnumeration()) {
+ switch (m_state.accumulatorIn().variant()) {
+ case QQmlJSRegisterContent::ExtensionObjectEnum:
+ case QQmlJSRegisterContent::MetaType:
+ case QQmlJSRegisterContent::ObjectAttached:
+ case QQmlJSRegisterContent::ObjectEnum:
+ case QQmlJSRegisterContent::ObjectModulePrefix:
+ case QQmlJSRegisterContent::ScopeAttached:
+ case QQmlJSRegisterContent::ScopeModulePrefix:
+ case QQmlJSRegisterContent::Singleton:
+ break; // OK, can look up enums on that thing
+ default:
+ setAccumulator(QQmlJSRegisterContent());
+ }
}
- const bool isRestrictedProperty = isRestricted(propertyName);
-
if (!m_state.accumulatorOut().isValid()) {
+ if (checkForEnumProblems(m_state.accumulatorIn(), propertyName))
+ return;
+
setError(u"Cannot load property %1 from %2."_s
.arg(propertyName, m_state.accumulatorIn().descriptiveName()));
- if (isRestrictedProperty)
- return;
-
const QString typeName = m_typeResolver->containedTypeName(m_state.accumulatorIn(), true);
if (typeName == u"QVariant")
@@ -832,7 +879,10 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
if (propertyResolution(baseType, propertyName) != PropertyMissing)
return;
- std::optional<FixSuggestion> fixSuggestion;
+ if (baseType->isScript())
+ return;
+
+ std::optional<QQmlJSFixSuggestion> fixSuggestion;
if (auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->properties().keys(),
getCurrentSourceLocation());
@@ -854,7 +904,7 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
}
}
- m_logger->log(u"Property \"%1\" not found on type \"%2\""_s.arg(propertyName).arg(typeName),
+ m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(propertyName).arg(typeName),
qmlMissingProperty, getCurrentSourceLocation(), true, true, fixSuggestion);
return;
}
@@ -865,6 +915,22 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
}
if (m_state.accumulatorOut().isProperty()) {
+ const QQmlJSScope::ConstPtr mathObject
+ = m_typeResolver->jsGlobalObject()->property(u"Math"_s).type();
+ if (m_typeResolver->registerContains(m_state.accumulatorIn(), mathObject)) {
+ QQmlJSMetaProperty prop;
+ prop.setPropertyName(propertyName);
+ prop.setTypeName(u"double"_s);
+ prop.setType(m_typeResolver->realType());
+ setAccumulator(
+ QQmlJSRegisterContent::create(
+ m_typeResolver->realType(), prop, m_state.accumulatorIn().resultLookupIndex(), lookupIndex,
+ QQmlJSRegisterContent::GenericObjectProperty, mathObject)
+ );
+
+ return;
+ }
+
if (m_typeResolver->registerContains(
m_state.accumulatorOut(), m_typeResolver->voidType())) {
setError(u"Type %1 does not have a property %2 for reading"_s
@@ -879,14 +945,8 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
}
}
- if (m_passManager != nullptr) {
- const bool isAttached =
- m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectAttached;
-
- m_passManager->analyzeRead(
- m_typeResolver->containedType(m_state.accumulatorIn()), propertyName,
- isAttached ? m_attachedContext : m_function->qmlScope, getCurrentSourceLocation());
- }
+ if (m_passManager != nullptr)
+ propagatePropertyLookup_SAcheck(propertyName);
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectAttached)
m_attachedContext = m_typeResolver->containedType(m_state.accumulatorIn());
@@ -920,14 +980,29 @@ void QQmlJSTypePropagator::generate_LoadOptionalProperty(int name, int offset)
void QQmlJSTypePropagator::generate_GetLookup(int index)
{
- propagatePropertyLookup(m_jsUnitGenerator->lookupName(index));
+ propagatePropertyLookup(m_jsUnitGenerator->lookupName(index), index);
}
void QQmlJSTypePropagator::generate_GetOptionalLookup(int index, int offset)
{
- Q_UNUSED(index);
Q_UNUSED(offset);
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ saveRegisterStateForJump(offset);
+ propagatePropertyLookup(m_jsUnitGenerator->lookupName(index), index);
+}
+
+void QQmlJSTypePropagator::generate_StoreProperty_SAcheck(const QString propertyName, const QQmlJSRegisterContent &callBase)
+{
+ const bool isAttached = callBase.variant() == QQmlJSRegisterContent::ObjectAttached;
+
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
+ QQmlJSScope::createQQmlSAElement(m_typeResolver->containedType(callBase)),
+ propertyName,
+ QQmlJSScope::createQQmlSAElement(
+ m_typeResolver->containedType(m_state.accumulatorIn())),
+ QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext
+ : m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
}
void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
@@ -942,7 +1017,13 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
return;
}
- if (!property.isWritable()) {
+ if (property.storedType().isNull()) {
+ setError(u"Cannot determine type for property %1 of type %2"_s.arg(
+ propertyName, callBase.descriptiveName()));
+ return;
+ }
+
+ if (!property.isWritable() && !property.storedType()->isListProperty()) {
setError(u"Can't assign to read-only property %1"_s.arg(propertyName));
m_logger->log(u"Cannot assign to read-only property %1"_s.arg(propertyName),
@@ -957,18 +1038,25 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base)
return;
}
- if (m_passManager != nullptr) {
- const bool isAttached = callBase.variant() == QQmlJSRegisterContent::ObjectAttached;
+ if (m_passManager != nullptr)
+ generate_StoreProperty_SAcheck(propertyName, callBase);
- m_passManager->analyzeWrite(m_typeResolver->containedType(callBase), propertyName,
- m_typeResolver->containedType(m_state.accumulatorIn()),
- isAttached ? m_attachedContext : m_function->qmlScope,
- getCurrentSourceLocation());
- }
+ // If the input can hold undefined we must not coerce it to the property type
+ // as that might eliminate an undefined value. For example, undefined -> string
+ // becomes "undefined".
+ // We need the undefined value for either resetting the property if that is supported
+ // or generating an exception otherwise. Therefore we explicitly require the value to
+ // be given as QVariant. This triggers the QVariant fallback path that's also used for
+ // shadowable properties. QVariant can hold undefined and the lookup functions will
+ // handle that appropriately.
- m_state.setHasSideEffects(true);
- addReadAccumulator(property);
+ const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
+ const QQmlJSRegisterContent readType = m_typeResolver->canHoldUndefined(m_state.accumulatorIn())
+ ? property.storedIn(varType).castTo(varType)
+ : std::move(property);
+ addReadAccumulator(readType);
addReadRegister(base, callBase);
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_SetLookup(int index, int base)
@@ -1028,88 +1116,123 @@ static bool isLoggingMethod(const QString &consoleMethod)
|| consoleMethod == u"warn" || consoleMethod == u"error";
}
-void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
+void QQmlJSTypePropagator::generate_CallProperty_SCMath(int base, int argc, int argv)
{
- Q_ASSERT(m_state.registers.contains(base));
- const auto callBase = m_state.registers[base].content;
- const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
+ // If we call a method on the Math object we don't need the actual Math object. We do need
+ // to transfer the type information to the code generator so that it knows that this is the
+ // Math object. Read the base register as void. void isn't stored, and the place where it's
+ // created will be optimized out if there are no other readers. The code generator can
+ // retrieve the original type and determine that it was the Math object.
- if (m_typeResolver->registerContains(
- callBase, m_typeResolver->jsGlobalObject()->property(u"Math"_s).type())) {
+ addReadRegister(base, m_typeResolver->globalType(m_typeResolver->voidType()));
- // If we call a method on the Math object we don't need the actual Math object. We do need
- // to transfer the type information to the code generator so that it knows that this is the
- // Math object. Read the base register as void. void isn't stored, and the place where it's
- // created will be optimized out if there are no other readers. The code generator can
- // retrieve the original type and determine that it was the Math object.
- addReadRegister(base, m_typeResolver->globalType(m_typeResolver->voidType()));
+ QQmlJSRegisterContent realType = m_typeResolver->returnType(
+ m_typeResolver->realType(), QQmlJSRegisterContent::MethodReturnValue,
+ m_typeResolver->mathObject());
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, realType);
+ setAccumulator(realType);
+}
- QQmlJSRegisterContent realType = m_typeResolver->globalType(m_typeResolver->realType());
- for (int i = 0; i < argc; ++i)
- addReadRegister(argv + i, realType);
- setAccumulator(realType);
- return;
- }
+void QQmlJSTypePropagator::generate_CallProperty_SCconsole(int base, int argc, int argv)
+{
+ const QQmlJSRegisterContent voidType
+ = m_typeResolver->globalType(m_typeResolver->voidType());
- if (m_typeResolver->registerContains(
- callBase, m_typeResolver->jsGlobalObject()->property(u"console"_s).type())
- && isLoggingMethod(propertyName)) {
+ // If we call a method on the console object we don't need the console object.
+ addReadRegister(base, voidType);
- const QQmlJSRegisterContent voidType
- = m_typeResolver->globalType(m_typeResolver->voidType());
+ const QQmlJSRegisterContent stringType
+ = m_typeResolver->globalType(m_typeResolver->stringType());
- // If we call a method on the console object we don't need the console object.
- addReadRegister(base, voidType);
+ if (argc > 0) {
+ const QQmlJSRegisterContent firstContent = m_state.registers[argv].content;
+ const QQmlJSScope::ConstPtr firstArg = m_typeResolver->containedType(firstContent);
+ switch (firstArg->accessSemantics()) {
+ case QQmlJSScope::AccessSemantics::Reference:
+ // We cannot know whether this will be a logging category at run time.
+ // Therefore we always pass any object types as special last argument.
+ addReadRegister(argv, m_typeResolver->globalType(
+ m_typeResolver->genericType(firstArg)));
+ break;
+ case QQmlJSScope::AccessSemantics::Sequence:
+ addReadRegister(argv, firstContent);
+ break;
+ default:
+ addReadRegister(argv, stringType);
+ break;
+ }
+ }
- const QQmlJSRegisterContent stringType
- = m_typeResolver->globalType(m_typeResolver->stringType());
+ for (int i = 1; i < argc; ++i) {
+ const QQmlJSRegisterContent argContent = m_state.registers[argv + i].content;
+ const QQmlJSScope::ConstPtr arg = m_typeResolver->containedType(argContent);
+ addReadRegister(
+ argv + i,
+ arg->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
+ ? argContent
+ : stringType);
+ }
- if (argc > 0) {
- const QQmlJSScope::ConstPtr firstArg
- = m_typeResolver->containedType(m_state.registers[argv].content);
- if (firstArg->isReferenceType()) {
- // We cannot know whether this will be a logging category at run time.
- // Therefore we always pass any object types as special last argument.
- addReadRegister(argv, m_typeResolver->globalType(
- m_typeResolver->genericType(firstArg)));
- } else {
- addReadRegister(argv, stringType);
- }
- }
+ m_state.setHasSideEffects(true);
+ setAccumulator(m_typeResolver->returnType(
+ m_typeResolver->voidType(), QQmlJSRegisterContent::MethodReturnValue,
+ m_typeResolver->consoleObject()));
+}
- for (int i = 1; i < argc; ++i)
- addReadRegister(argv + i, stringType);
+void QQmlJSTypePropagator::generate_callProperty_SAcheck(const QString propertyName, const QQmlJSScope::ConstPtr &baseType)
+{
+ // TODO: Should there be an analyzeCall() in the future? (w. corresponding onCall in Pass)
+ QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
+ QQmlJSScope::createQQmlSAElement(baseType), propertyName,
+ QQmlJSScope::createQQmlSAElement(m_function->qmlScope),
+ QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ getCurrentBindingSourceLocation()));
+}
- m_state.setHasSideEffects(true);
- setAccumulator(voidType);
+void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int argc, int argv)
+{
+ Q_ASSERT(m_state.registers.contains(base));
+ const auto callBase = m_state.registers[base].content;
+ const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
+
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->mathObject())) {
+ generate_CallProperty_SCMath(base, argc, argv);
return;
}
- if (m_typeResolver->registerContains(callBase, m_typeResolver->jsValueType())
- || m_typeResolver->registerContains(callBase, m_typeResolver->varType())) {
- const auto jsValueType = m_typeResolver->globalType(m_typeResolver->jsValueType());
- addReadRegister(base, jsValueType);
- for (int i = 0; i < argc; ++i)
- addReadRegister(argv + i, jsValueType);
- setAccumulator(jsValueType);
- m_state.setHasSideEffects(true);
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->consoleObject()) && isLoggingMethod(propertyName)) {
+ generate_CallProperty_SCconsole(base, argc, argv);
return;
}
+ const auto baseType = m_typeResolver->containedType(callBase);
const auto member = m_typeResolver->memberType(callBase, propertyName);
+
if (!member.isMethod()) {
+ if (m_typeResolver->registerContains(callBase, m_typeResolver->jsValueType())
+ || m_typeResolver->registerContains(callBase, m_typeResolver->varType())) {
+ const auto jsValueType = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadRegister(base, jsValueType);
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, jsValueType);
+ m_state.setHasSideEffects(true);
+ setAccumulator(m_typeResolver->returnType(
+ m_typeResolver->jsValueType(), QQmlJSRegisterContent::JavaScriptReturnValue,
+ m_typeResolver->jsValueType()));
+ return;
+ }
+
setError(u"Type %1 does not have a property %2 for calling"_s
.arg(callBase.descriptiveName(), propertyName));
if (callBase.isType() && isCallingProperty(callBase.type(), propertyName))
return;
- if (isRestricted(propertyName))
+ if (checkForEnumProblems(callBase, propertyName))
return;
- std::optional<FixSuggestion> fixSuggestion;
-
- const auto baseType = m_typeResolver->containedType(callBase);
+ std::optional<QQmlJSFixSuggestion> fixSuggestion;
if (auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
getCurrentSourceLocation());
@@ -1117,20 +1240,16 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
fixSuggestion = suggestion;
}
- m_logger->log(u"Property \"%1\" not found on type \"%2\""_s.arg(
+ m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(
propertyName, m_typeResolver->containedTypeName(callBase, true)),
qmlMissingProperty, getCurrentSourceLocation(), true, true, fixSuggestion);
return;
}
- checkDeprecated(m_typeResolver->containedType(callBase), propertyName, true);
+ checkDeprecated(baseType, propertyName, true);
- if (m_passManager != nullptr) {
- // TODO: Should there be an analyzeCall() in the future? (w. corresponding onCall in Pass)
- m_passManager->analyzeRead(
- m_typeResolver->containedType(callBase),
- propertyName, m_function->qmlScope, getCurrentSourceLocation());
- }
+ if (m_passManager != nullptr)
+ generate_callProperty_SAcheck(propertyName, baseType);
addReadRegister(base, callBase);
@@ -1141,6 +1260,12 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
}
}
+ if (baseType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
+ && m_typeResolver->equals(member.scopeType(), m_typeResolver->arrayPrototype())
+ && propagateArrayMethod(propertyName, argc, argv, callBase)) {
+ return;
+ }
+
propagateCall(member.method(), argc, argv, member.scopeType());
}
@@ -1148,10 +1273,13 @@ QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(const QList<QQmlJSMetaMe
int argc, int argv, QStringList *errors)
{
QQmlJSMetaMethod javascriptFunction;
+ QQmlJSMetaMethod candidate;
+ bool hasMultipleCandidates = false;
+
for (const auto &method : methods) {
// If we encounter a JavaScript function, use this as a fallback if no other method matches
- if (method.isJavaScriptFunction())
+ if (method.isJavaScriptFunction() && !javascriptFunction.isValid())
javascriptFunction = method;
if (method.returnType().isNull() && !method.returnTypeName().isEmpty()) {
@@ -1168,33 +1296,55 @@ QQmlJSMetaMethod QQmlJSTypePropagator::bestMatchForCall(const QList<QQmlJSMetaMe
continue;
}
- bool matches = true;
+ bool fuzzyMatch = true;
+ bool exactMatch = true;
for (int i = 0; i < argc; ++i) {
const auto argumentType = arguments[i].type();
if (argumentType.isNull()) {
errors->append(
u"type %1 for argument %2 cannot be resolved"_s.arg(arguments[i].typeName())
.arg(i));
- matches = false;
+ exactMatch = false;
+ fuzzyMatch = false;
break;
}
- if (canConvertFromTo(m_state.registers[argv + i].content,
- m_typeResolver->globalType(argumentType))) {
+ const auto content = m_state.registers[argv + i].content;
+ if (m_typeResolver->registerContains(content, argumentType))
+ continue;
+
+ exactMatch = false;
+ if (canConvertFromTo(content, m_typeResolver->globalType(argumentType)))
+ continue;
+
+ // We can try to call a method that expects a derived type.
+ if (argumentType->isReferenceType()
+ && m_typeResolver->inherits(
+ argumentType->baseType(), m_typeResolver->containedType(content))) {
continue;
}
errors->append(
u"argument %1 contains %2 but is expected to contain the type %3"_s.arg(i).arg(
- m_state.registers[argv + i].content.descriptiveName(),
- arguments[i].typeName()));
- matches = false;
+ content.descriptiveName(), arguments[i].typeName()));
+ fuzzyMatch = false;
break;
}
- if (matches)
+
+ if (exactMatch) {
return method;
+ } else if (fuzzyMatch) {
+ if (!candidate.isValid())
+ candidate = method;
+ else
+ hasMultipleCandidates = true;
+ }
}
- return javascriptFunction;
+
+ if (hasMultipleCandidates)
+ return QQmlJSMetaMethod();
+
+ return candidate.isValid() ? candidate : javascriptFunction;
}
void QQmlJSTypePropagator::setAccumulator(const QQmlJSRegisterContent &content)
@@ -1221,9 +1371,14 @@ void QQmlJSTypePropagator::mergeRegister(
int index, const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b)
{
auto merged = m_typeResolver->merge(a, b);
-
Q_ASSERT(merged.isValid());
- Q_ASSERT(merged.isConversion());
+
+ if (!merged.isConversion()) {
+ // The registers were the same. We're already tracking them.
+ m_state.annotations[currentInstructionOffset()].typeConversions[index].content = merged;
+ m_state.registers[index].content = merged;
+ return;
+ }
auto tryPrevStateConversion = [this](int index, const QQmlJSRegisterContent &merged) -> bool {
auto it = m_prevStateAnnotations.find(currentInstructionOffset());
@@ -1234,19 +1389,20 @@ void QQmlJSTypePropagator::mergeRegister(
if (conversion == it->second.typeConversions.end())
return false;
- const QQmlJSRegisterContent &lastTry = conversion.value().content;
+ const VirtualRegister &lastTry = conversion.value();
- Q_ASSERT(lastTry.isValid());
- Q_ASSERT(lastTry.isConversion());
+ Q_ASSERT(lastTry.content.isValid());
+ if (!lastTry.content.isConversion())
+ return false;
- if (!m_typeResolver->equals(lastTry.conversionResult(), merged.conversionResult())
- || lastTry.conversionOrigins() != merged.conversionOrigins()) {
+ if (!m_typeResolver->equals(lastTry.content.conversionResult(), merged.conversionResult())
+ || lastTry.content.conversionOrigins() != merged.conversionOrigins()) {
return false;
}
// We don't need to track it again if we've come to the same conclusion before.
- m_state.annotations[currentInstructionOffset()].typeConversions[index].content = lastTry;
- m_state.registers[index].content = lastTry;
+ m_state.annotations[currentInstructionOffset()].typeConversions[index] = lastTry;
+ m_state.registers[index] = lastTry;
return true;
};
@@ -1272,11 +1428,15 @@ void QQmlJSTypePropagator::propagateCall(
const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
if (!match.isValid()) {
- Q_ASSERT(errors.size() == methods.size());
- if (methods.size() == 1)
+ if (methods.size() == 1) {
+ // Cannot have multiple fuzzy matches if there is only one method
+ Q_ASSERT(errors.size() == 1);
setError(errors.first());
- else
+ } else if (errors.size() < methods.size()) {
+ setError(u"Multiple matching overrides found. Cannot determine the right one."_s);
+ } else {
setError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n'));
+ }
return;
}
@@ -1292,7 +1452,6 @@ void QQmlJSTypePropagator::propagateCall(
if (!m_state.accumulatorOut().isValid())
setError(u"Cannot store return type of method %1()."_s.arg(match.methodName()));
- m_state.setHasSideEffects(true);
const auto types = match.parameters();
for (int i = 0; i < argc; ++i) {
if (i < types.size()) {
@@ -1306,6 +1465,7 @@ void QQmlJSTypePropagator::propagateCall(
}
addReadRegister(argv + i, m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
+ m_state.setHasSideEffects(true);
}
bool QQmlJSTypePropagator::propagateTranslationMethod(
@@ -1316,7 +1476,7 @@ bool QQmlJSTypePropagator::propagateTranslationMethod(
const QQmlJSMetaMethod method = methods.front();
const QQmlJSRegisterContent intType
- = m_typeResolver->globalType(m_typeResolver->intType());
+ = m_typeResolver->globalType(m_typeResolver->int32Type());
const QQmlJSRegisterContent stringType
= m_typeResolver->globalType(m_typeResolver->stringType());
const QQmlJSRegisterContent returnType
@@ -1425,22 +1585,195 @@ void QQmlJSTypePropagator::propagateStringArgCall(int argv)
const QQmlJSScope::ConstPtr input = m_typeResolver->containedType(
m_state.registers[argv].content);
- for (QQmlJSScope::ConstPtr targetType : {
- m_typeResolver->intType(),
- m_typeResolver->uintType(),
- m_typeResolver->realType(),
- m_typeResolver->floatType(),
- m_typeResolver->boolType(),
- }) {
- if (m_typeResolver->equals(input, targetType)) {
- addReadRegister(argv, m_typeResolver->globalType(targetType));
- return;
- }
+
+ if (m_typeResolver->equals(input, m_typeResolver->uint32Type())) {
+ addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
+ return;
+ }
+
+ if (m_typeResolver->isIntegral(input)) {
+ addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->int32Type()));
+ return;
+ }
+
+ if (m_typeResolver->isNumeric(input)) {
+ addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
+ return;
+ }
+
+ if (m_typeResolver->equals(input, m_typeResolver->boolType())) {
+ addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->boolType()));
+ return;
}
addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->stringType()));
}
+bool QQmlJSTypePropagator::propagateArrayMethod(
+ const QString &name, int argc, int argv, const QQmlJSRegisterContent &baseType)
+{
+ // TODO:
+ // * For concat() we need to decide what kind of array to return and what kinds of arguments to
+ // accept.
+ // * For entries(), keys(), and values() we need iterators.
+ // * For find(), findIndex(), sort(), every(), some(), forEach(), map(), filter(), reduce(),
+ // and reduceRight() we need typed function pointers.
+
+ const auto intType = m_typeResolver->globalType(m_typeResolver->int32Type());
+ const auto stringType = m_typeResolver->globalType(m_typeResolver->stringType());
+ const auto baseContained = m_typeResolver->containedType(baseType);
+ const auto valueContained = baseContained->valueType();
+ const auto valueType = m_typeResolver->globalType(valueContained);
+
+ const bool canHaveSideEffects = (baseType.isProperty() && baseType.isWritable())
+ || baseContained->isListProperty()
+ || baseType.isConversion();
+
+ const auto setReturnType = [&](const QQmlJSScope::ConstPtr type) {
+ setAccumulator(m_typeResolver->returnType(
+ type, QQmlJSRegisterContent::MethodReturnValue, baseContained));
+ };
+
+ if (name == u"copyWithin" && argc > 0 && argc < 4) {
+ for (int i = 0; i < argc; ++i) {
+ if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
+ return false;
+ }
+
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, intType);
+
+ m_state.setHasSideEffects(canHaveSideEffects);
+ setReturnType(baseContained);
+ return true;
+ }
+
+ if (name == u"fill" && argc > 0 && argc < 4) {
+ if (!canConvertFromTo(m_state.registers[argv].content, valueType))
+ return false;
+
+ for (int i = 1; i < argc; ++i) {
+ if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
+ return false;
+ }
+
+ addReadRegister(argv, valueType);
+
+ for (int i = 1; i < argc; ++i)
+ addReadRegister(argv + i, intType);
+
+ m_state.setHasSideEffects(canHaveSideEffects);
+ setReturnType(baseContained);
+ return true;
+ }
+
+ if (name == u"includes" && argc > 0 && argc < 3) {
+ if (!canConvertFromTo(m_state.registers[argv].content, valueType))
+ return false;
+
+ if (argc == 2) {
+ if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
+ return false;
+ addReadRegister(argv + 1, intType);
+ }
+
+ addReadRegister(argv, valueType);
+ setReturnType(m_typeResolver->boolType());
+ return true;
+ }
+
+ if (name == u"toString" || (name == u"join" && argc < 2)) {
+ if (argc == 1) {
+ if (!canConvertFromTo(m_state.registers[argv].content, stringType))
+ return false;
+ addReadRegister(argv, stringType);
+ }
+
+ setReturnType(m_typeResolver->stringType());
+ return true;
+ }
+
+ if ((name == u"pop" || name == u"shift") && argc == 0) {
+ m_state.setHasSideEffects(canHaveSideEffects);
+ setReturnType(valueContained);
+ return true;
+ }
+
+ if (name == u"push" || name == u"unshift") {
+ for (int i = 0; i < argc; ++i) {
+ if (!canConvertFromTo(m_state.registers[argv + i].content, valueType))
+ return false;
+ }
+
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, valueType);
+
+ m_state.setHasSideEffects(canHaveSideEffects);
+ setReturnType(m_typeResolver->int32Type());
+ return true;
+ }
+
+ if (name == u"reverse" && argc == 0) {
+ m_state.setHasSideEffects(canHaveSideEffects);
+ setReturnType(baseContained);
+ return true;
+ }
+
+ if (name == u"slice" && argc < 3) {
+ for (int i = 0; i < argc; ++i) {
+ if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
+ return false;
+ }
+
+ for (int i = 0; i < argc; ++i)
+ addReadRegister(argv + i, intType);
+
+ setReturnType(baseType.storedType()->isListProperty()
+ ? m_typeResolver->qObjectListType()
+ : baseContained);
+ return true;
+ }
+
+ if (name == u"splice" && argc > 0) {
+ for (int i = 0; i < 2; ++i) {
+ if (!canConvertFromTo(m_state.registers[argv + i].content, intType))
+ return false;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ if (!canConvertFromTo(m_state.registers[argv + i].content, valueType))
+ return false;
+ }
+
+ for (int i = 0; i < 2; ++i)
+ addReadRegister(argv + i, intType);
+
+ for (int i = 2; i < argc; ++i)
+ addReadRegister(argv + i, valueType);
+
+ m_state.setHasSideEffects(canHaveSideEffects);
+ setReturnType(baseContained);
+ return true;
+ }
+
+ if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
+ if (!canConvertFromTo(m_state.registers[argv].content, valueType))
+ return false;
+
+ if (argc == 2) {
+ if (!canConvertFromTo(m_state.registers[argv + 1].content, intType))
+ return false;
+ addReadRegister(argv + 1, intType);
+ }
+
+ addReadRegister(argv, valueType);
+ setReturnType(m_typeResolver->int32Type());
+ return true;
+ }
+
+ return false;
+}
+
void QQmlJSTypePropagator::generate_CallPropertyLookup(int lookupIndex, int base, int argc,
int argv)
{
@@ -1517,14 +1850,70 @@ void QQmlJSTypePropagator::generate_TailCall(int func, int thisObject, int argc,
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
+void QQmlJSTypePropagator::generate_Construct_SCDate(int argc, int argv)
+{
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->dateTimeType()));
+
+ if (argc == 1) {
+ const QQmlJSRegisterContent argType = m_state.registers[argv].content;
+ if (m_typeResolver->isNumeric(argType)) {
+ addReadRegister(
+ argv, m_typeResolver->globalType(m_typeResolver->realType()));
+ } else if (m_typeResolver->registerContains(argType, m_typeResolver->stringType())) {
+ addReadRegister(
+ argv, m_typeResolver->globalType(m_typeResolver->stringType()));
+ } else if (m_typeResolver->registerContains(argType, m_typeResolver->dateTimeType())
+ || m_typeResolver->registerContains(argType, m_typeResolver->dateType())
+ || m_typeResolver->registerContains(argType, m_typeResolver->timeType())) {
+ addReadRegister(
+ argv, m_typeResolver->globalType(m_typeResolver->dateTimeType()));
+ } else {
+ addReadRegister(
+ argv, m_typeResolver->globalType(m_typeResolver->jsPrimitiveType()));
+ }
+ } else {
+ constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
+ for (int i = 0; i < std::min(argc, maxArgc); ++i) {
+ addReadRegister(
+ argv + i, m_typeResolver->globalType(m_typeResolver->realType()));
+ }
+ }
+}
+
+void QQmlJSTypePropagator::generate_Construct_SCArray(int argc, int argv)
+{
+ if (argc == 1) {
+ if (m_typeResolver->isNumeric(m_state.registers[argv].content)) {
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->variantListType()));
+ addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType()));
+ } else {
+ generate_DefineArray(argc, argv);
+ }
+ } else {
+ generate_DefineArray(argc, argv);
+ }
+}
void QQmlJSTypePropagator::generate_Construct(int func, int argc, int argv)
{
- m_state.setHasSideEffects(true);
- Q_UNUSED(func)
- Q_UNUSED(argv)
+ const QQmlJSRegisterContent type = m_state.registers[func].content;
+ if (!type.isMethod()) {
+ m_state.setHasSideEffects(true);
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ return;
+ }
- Q_UNUSED(argc)
+ if (type.method() == m_typeResolver->jsGlobalObject()->methods(u"Date"_s)) {
+ generate_Construct_SCDate(argc, argv);
+ return;
+ }
+
+ if (type.method() == m_typeResolver->jsGlobalObject()->methods(u"Array"_s)) {
+ generate_Construct_SCArray(argc, argv);
+
+ return;
+ }
+ m_state.setHasSideEffects(true);
setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
}
@@ -1560,8 +1949,22 @@ void QQmlJSTypePropagator::generate_UnwindToLabel(int level, int offset)
void QQmlJSTypePropagator::generate_DeadTemporalZoneCheck(int name)
{
- Q_UNUSED(name)
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ const auto fail = [this, name]() {
+ setError(u"Cannot statically assert the dead temporal zone check for %1"_s.arg(
+ name ? m_jsUnitGenerator->stringForIndex(name) : u"the anonymous accumulator"_s));
+ };
+
+ const QQmlJSRegisterContent in = m_state.accumulatorIn();
+ if (in.isConversion()) {
+ for (const QQmlJSScope::ConstPtr &origin : in.conversionOrigins()) {
+ if (!m_typeResolver->equals(origin, m_typeResolver->emptyType()))
+ continue;
+ fail();
+ break;
+ }
+ } else if (m_typeResolver->registerContains(in, m_typeResolver->emptyType())) {
+ fail();
+ }
}
void QQmlJSTypePropagator::generate_ThrowException()
@@ -1634,28 +2037,41 @@ void QQmlJSTypePropagator::generate_PopContext()
void QQmlJSTypePropagator::generate_GetIterator(int iterator)
{
- Q_UNUSED(iterator)
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ const QQmlJSRegisterContent listType = m_state.accumulatorIn();
+ if (!listType.isList()) {
+ const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType());
+ addReadAccumulator(jsValue);
+ setAccumulator(jsValue);
+ return;
+ }
+
+ addReadAccumulator(listType);
+ setAccumulator(m_typeResolver->iteratorPointer(
+ listType, QQmlJS::AST::ForEachType(iterator), currentInstructionOffset()));
}
-void QQmlJSTypePropagator::generate_IteratorNext(int value, int done)
+void QQmlJSTypePropagator::generate_IteratorNext(int value, int offset)
{
- Q_UNUSED(value)
- Q_UNUSED(done)
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ const QQmlJSRegisterContent iteratorType = m_state.accumulatorIn();
+ addReadAccumulator(iteratorType);
+ setRegister(value, m_typeResolver->merge(
+ m_typeResolver->valueType(iteratorType),
+ m_typeResolver->globalType(m_typeResolver->voidType())));
+ saveRegisterStateForJump(offset);
+ m_state.setHasSideEffects(true);
}
-void QQmlJSTypePropagator::generate_IteratorNextForYieldStar(int iterator, int object)
+void QQmlJSTypePropagator::generate_IteratorNextForYieldStar(int iterator, int object, int offset)
{
Q_UNUSED(iterator)
Q_UNUSED(object)
+ Q_UNUSED(offset)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
-void QQmlJSTypePropagator::generate_IteratorClose(int done)
+void QQmlJSTypePropagator::generate_IteratorClose()
{
- Q_UNUSED(done)
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ // Noop
}
void QQmlJSTypePropagator::generate_DestructureRestElement()
@@ -1696,9 +2112,7 @@ void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSTypePropagator::generate_DefineArray(int argc, int args)
{
- setAccumulator(m_typeResolver->globalType(argc == 0
- ? m_typeResolver->emptyListType()
- : m_typeResolver->variantListType()));
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->variantListType()));
// Track all arguments as the same type.
const QQmlJSRegisterContent elementType
@@ -1709,12 +2123,34 @@ void QQmlJSTypePropagator::generate_DefineArray(int argc, int args)
void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
{
- // TODO: computed property names, getters, and setters are unsupported. How do we catch them?
+ const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
+ Q_ASSERT(argc >= classSize);
- Q_UNUSED(internalClassId)
- Q_UNUSED(argc)
- Q_UNUSED(args)
- setAccumulator(m_typeResolver->globalType(m_typeResolver->jsValueType()));
+ // Track each element as separate type
+ for (int i = 0; i < classSize; ++i) {
+ addReadRegister(
+ args + i,
+ m_typeResolver->tracked(m_typeResolver->globalType(m_typeResolver->varType())));
+ }
+
+ for (int i = classSize; i < argc; i += 3) {
+ // layout for remaining members is:
+ // 0: ObjectLiteralArgument - Value|Method|Getter|Setter
+ // We cannot do anything useful with this. Any code that would call a getter/setter/method
+ // could not be compiled to C++. Ignore it.
+
+ // 1: name of argument
+ addReadRegister(
+ args + i + 1,
+ m_typeResolver->tracked(m_typeResolver->globalType(m_typeResolver->stringType())));
+
+ // 2: value of argument
+ addReadRegister(
+ args + i + 2,
+ m_typeResolver->tracked(m_typeResolver->globalType(m_typeResolver->varType())));
+ }
+
+ setAccumulator(m_typeResolver->globalType(m_typeResolver->variantMapType()));
}
void QQmlJSTypePropagator::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -1743,7 +2179,7 @@ void QQmlJSTypePropagator::generate_CreateRestParameter(int argIndex)
void QQmlJSTypePropagator::generate_ConvertThisToObject()
{
- INSTR_PROLOGUE_NOT_IMPLEMENTED();
+ setRegister(This, m_typeResolver->globalType(m_typeResolver->qObjectType()));
}
void QQmlJSTypePropagator::generate_LoadSuperConstructor()
@@ -1772,8 +2208,8 @@ void QQmlJSTypePropagator::generate_JumpTrue(int offset)
return;
}
saveRegisterStateForJump(offset);
- m_state.setHasSideEffects(true);
addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_JumpFalse(int offset)
@@ -1785,8 +2221,8 @@ void QQmlJSTypePropagator::generate_JumpFalse(int offset)
return;
}
saveRegisterStateForJump(offset);
- m_state.setHasSideEffects(true);
addReadAccumulator(m_typeResolver->globalType(m_typeResolver->boolType()));
+ m_state.setHasSideEffects(true);
}
void QQmlJSTypePropagator::generate_JumpNoException(int offset)
@@ -1833,42 +2269,33 @@ void QQmlJSTypePropagator::recordEqualsType(int lhs)
return content.isEnumeration() || m_typeResolver->isNumeric(content);
};
- const auto isIntCompatible = [this](const QQmlJSRegisterContent &content) {
- const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
- return contained->scopeType() == QQmlJSScope::EnumScope
- || m_typeResolver->equals(contained, m_typeResolver->intType())
- || m_typeResolver->equals(contained, m_typeResolver->uintType());
- };
-
const auto accumulatorIn = m_state.accumulatorIn();
const auto lhsRegister = m_state.registers[lhs].content;
// If the types are primitive, we compare directly ...
if (m_typeResolver->isPrimitive(accumulatorIn) || accumulatorIn.isEnumeration()) {
if (m_typeResolver->registerContains(
- accumulatorIn, m_typeResolver->containedType(lhsRegister))) {
- addReadRegister(lhs, accumulatorIn);
+ accumulatorIn, m_typeResolver->containedType(lhsRegister))
+ || (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister))
+ || m_typeResolver->isPrimitive(lhsRegister)) {
+ addReadRegister(lhs, lhsRegister);
addReadAccumulator(accumulatorIn);
return;
- } else if (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister)) {
- const auto targetType = isIntCompatible(accumulatorIn) && isIntCompatible(lhsRegister)
- ? m_typeResolver->globalType(m_typeResolver->intType())
- : m_typeResolver->globalType(m_typeResolver->realType());
- addReadRegister(lhs, targetType);
- addReadAccumulator(targetType);
- return;
- } else if (m_typeResolver->isPrimitive(lhsRegister)) {
- const QQmlJSRegisterContent primitive = m_typeResolver->globalType(
- m_typeResolver->jsPrimitiveType());
- addReadRegister(lhs, primitive);
- addReadAccumulator(primitive);
- return;
}
}
- // We don't modify types if the types are comparable with QObject or var
- if (canStrictlyCompareWithVar(m_typeResolver, lhsRegister, accumulatorIn)
- || canCompareWithQObject(m_typeResolver, lhsRegister, accumulatorIn)) {
+ const auto containedAccumulatorIn = m_typeResolver->isOptionalType(accumulatorIn)
+ ? m_typeResolver->extractNonVoidFromOptionalType(accumulatorIn)
+ : m_typeResolver->containedType(accumulatorIn);
+
+ const auto containedLhs = m_typeResolver->isOptionalType(lhsRegister)
+ ? m_typeResolver->extractNonVoidFromOptionalType(lhsRegister)
+ : m_typeResolver->containedType(lhsRegister);
+
+ // We don't modify types if the types are comparable with QObject, QUrl or var types
+ if (canStrictlyCompareWithVar(m_typeResolver, containedLhs, containedAccumulatorIn)
+ || canCompareWithQObject(m_typeResolver, containedLhs, containedAccumulatorIn)
+ || canCompareWithQUrl(m_typeResolver, containedLhs, containedAccumulatorIn)) {
addReadRegister(lhs, lhsRegister);
addReadAccumulator(accumulatorIn);
return;
@@ -1912,7 +2339,7 @@ void QQmlJSTypePropagator::generate_CmpEqInt(int lhsConst)
recordEqualsIntType();
Q_UNUSED(lhsConst)
setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->intType()),
+ QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->int32Type()),
m_state.accumulatorIn())));
}
@@ -1921,7 +2348,7 @@ void QQmlJSTypePropagator::generate_CmpNeInt(int lhsConst)
recordEqualsIntType();
Q_UNUSED(lhsConst)
setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation(
- QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->intType()),
+ QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->int32Type()),
m_state.accumulatorIn())));
}
@@ -1994,32 +2421,53 @@ void QQmlJSTypePropagator::generate_CmpInstanceOf(int lhs)
void QQmlJSTypePropagator::generate_As(int lhs)
{
const QQmlJSRegisterContent input = checkedInputRegister(lhs);
- QQmlJSScope::ConstPtr contained;
+ const QQmlJSScope::ConstPtr inContained = m_typeResolver->containedType(input);
+
+ QQmlJSScope::ConstPtr outContained;
switch (m_state.accumulatorIn().variant()) {
case QQmlJSRegisterContent::ScopeAttached:
- contained = m_state.accumulatorIn().scopeType();
+ outContained = m_state.accumulatorIn().scopeType();
break;
case QQmlJSRegisterContent::MetaType:
- contained = m_state.accumulatorIn().scopeType();
- if (contained->isComposite()) // Otherwise we don't need it
+ outContained = m_state.accumulatorIn().scopeType();
+ if (outContained->isComposite()) // Otherwise we don't need it
addReadAccumulator(m_typeResolver->globalType(m_typeResolver->metaObjectType()));
break;
default:
- contained = m_typeResolver->containedType(m_state.accumulatorIn());
+ outContained = m_typeResolver->containedType(m_state.accumulatorIn());
break;
}
- addReadRegister(lhs, m_typeResolver->globalType(contained));
+ QQmlJSRegisterContent output;
+
+ if (outContained->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
+ // A referece type cast can result in either the type or null.
+ // Reference types can hold null. We don't need to special case that.
- if (m_typeResolver->containedType(input)->accessSemantics()
- != QQmlJSScope::AccessSemantics::Reference
- || contained->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
+ if (m_typeResolver->inherits(inContained, outContained))
+ output = input;
+ else
+ output = m_typeResolver->cast(input, outContained);
+ } else if (!m_typeResolver->canAddressValueTypes()) {
setError(u"invalid cast from %1 to %2. You can only cast object types."_s
- .arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
+ .arg(input.descriptiveName(), m_state.accumulatorIn().descriptiveName()));
+ return;
} else {
- setAccumulator(m_typeResolver->globalType(contained));
+ if (m_typeResolver->inherits(inContained, outContained)) {
+ // A "slicing" cannot result in void
+ output = m_typeResolver->cast(input, outContained);
+ } else {
+ // A value type cast can result in either the type or undefined.
+ // Using convert() retains the variant of the input type.
+ output = m_typeResolver->merge(
+ m_typeResolver->cast(input, outContained),
+ m_typeResolver->cast(input, m_typeResolver->voidType()));
+ }
}
+
+ addReadRegister(lhs, input);
+ setAccumulator(output);
}
void QQmlJSTypePropagator::checkConversion(
@@ -2085,7 +2533,7 @@ void QQmlJSTypePropagator::generateBinaryConstArithmeticOperation(QSOperator::Op
{
const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation(
op, m_state.accumulatorIn(),
- m_typeResolver->builtinType(m_typeResolver->intType()));
+ m_typeResolver->builtinType(m_typeResolver->int32Type()));
checkConversion(m_state.accumulatorIn(), type);
addReadAccumulator(type);
@@ -2190,9 +2638,8 @@ void QQmlJSTypePropagator::generate_Sub(int lhs)
void QQmlJSTypePropagator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
{
- Q_UNUSED(firstReg)
- Q_UNUSED(count)
- // Ignore. We reject uninitialized values anyway.
+ for (int reg = firstReg, end = firstReg + count; reg < end; ++reg)
+ setRegister(reg, m_typeResolver->globalType(m_typeResolver->emptyType()));
}
void QQmlJSTypePropagator::generate_ThrowOnNullOrUndefined()
@@ -2206,31 +2653,6 @@ void QQmlJSTypePropagator::generate_GetTemplateObject(int index)
INSTR_PROLOGUE_NOT_IMPLEMENTED();
}
-static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
-{
- using Type = QV4::Moth::Instr::Type;
- switch (type) {
- case Type::PopContext:
- case Type::PopScriptContext:
- case Type::CreateCallContext:
- case Type::CreateCallContext_Wide:
- case Type::PushCatchContext:
- case Type::PushCatchContext_Wide:
- case Type::PushWithContext:
- case Type::PushWithContext_Wide:
- case Type::PushBlockContext:
- case Type::PushBlockContext_Wide:
- case Type::CloneBlockContext:
- case Type::CloneBlockContext_Wide:
- case Type::PushScriptContext:
- case Type::PushScriptContext_Wide:
- return true;
- default:
- break;
- }
- return false;
-}
-
QV4::Moth::ByteCodeHandler::Verdict
QQmlJSTypePropagator::startInstruction(QV4::Moth::Instr::Type type)
{
@@ -2298,9 +2720,6 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
currentInstruction.readRegisters = m_state.takeReadRegisters();
currentInstruction.hasSideEffects = m_state.hasSideEffects();
currentInstruction.isRename = m_state.isRename();
- m_state.setHasSideEffects(false);
- m_state.setIsRename(false);
- m_state.setReadRegisters(VirtualRegisters());
switch (instr) {
// the following instructions are not expected to produce output in the accumulator
@@ -2324,6 +2743,10 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
case QV4::Moth::Instr::Type::PushCatchContext:
case QV4::Moth::Instr::Type::UnwindDispatch:
case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone:
+ case QV4::Moth::Instr::Type::ConvertThisToObject:
+ case QV4::Moth::Instr::Type::DeadTemporalZoneCheck:
+ case QV4::Moth::Instr::Type::IteratorNext:
+ case QV4::Moth::Instr::Type::IteratorNextForYieldStar:
if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) {
setError(u"Instruction is not expected to populate the accumulator"_s);
return;
@@ -2339,11 +2762,26 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr)
}
}
+ if (!(m_error->isValid() && m_error->isError())
+ && instr != QV4::Moth::Instr::Type::DeadTemporalZoneCheck) {
+ // An instruction needs to have side effects or write to another register otherwise it's a
+ // noop. DeadTemporalZoneCheck is not needed by the compiler and is ignored.
+ Q_ASSERT(m_state.hasSideEffects() || m_state.changedRegisterIndex() != -1);
+ }
+
if (m_state.changedRegisterIndex() != InvalidRegister) {
Q_ASSERT(m_error->isValid() || m_state.changedRegister().isValid());
- m_state.registers[m_state.changedRegisterIndex()].content = m_state.changedRegister();
+ VirtualRegister &r = m_state.registers[m_state.changedRegisterIndex()];
+ r.content = m_state.changedRegister();
+ r.canMove = false;
+ r.affectedBySideEffects = m_state.isRename()
+ && m_state.isRegisterAffectedBySideEffects(m_state.renameSourceRegisterIndex());
m_state.clearChangedRegister();
}
+
+ m_state.setHasSideEffects(false);
+ m_state.setIsRename(false);
+ m_state.setReadRegisters(VirtualRegisters());
}
QQmlJSRegisterContent QQmlJSTypePropagator::propagateBinaryOperation(QSOperator::Op op, int lhs)
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index 550048767e..c9bbeb27cc 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -24,14 +24,14 @@ namespace QQmlSA {
class PassManager;
};
-struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSTypePropagator : public QQmlJSCompilePass
+struct Q_QMLCOMPILER_EXPORT QQmlJSTypePropagator : public QQmlJSCompilePass
{
QQmlJSTypePropagator(const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
- QQmlJSTypeInfo *typeInfo = nullptr,
+ BasicBlocks basicBlocks = {}, InstructionAnnotations annotations = {},
QQmlSA::PassManager *passManager = nullptr);
- InstructionAnnotations run(const Function *m_function, QQmlJS::DiagnosticMessage *error);
+ BlocksAndAnnotations run(const Function *m_function, QQmlJS::DiagnosticMessage *error);
void generate_Ret() override;
void generate_Debug() override;
@@ -57,6 +57,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSTypePropagator : public QQmlJSCompileP
void generate_LoadName(int nameIndex) override;
void generate_LoadGlobalLookup(int index) override;
void generate_LoadQmlContextPropertyLookup(int index) override;
+ void generate_StoreNameCommon(int nameIndex);
void generate_StoreNameSloppy(int nameIndex) override;
void generate_StoreNameStrict(int name) override;
void generate_LoadElement(int base) override;
@@ -101,9 +102,9 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSTypePropagator : public QQmlJSCompileP
void generate_PopScriptContext() override;
void generate_PopContext() override;
void generate_GetIterator(int iterator) override;
- void generate_IteratorNext(int value, int done) override;
- void generate_IteratorNextForYieldStar(int iterator, int object) override;
- void generate_IteratorClose(int done) override;
+ void generate_IteratorNext(int value, int offset) override;
+ void generate_IteratorNextForYieldStar(int iterator, int object, int offset) override;
+ void generate_IteratorClose() override;
void generate_DestructureRestElement() override;
void generate_DeleteProperty(int base, int index) override;
void generate_DeleteName(int name) override;
@@ -168,6 +169,8 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSTypePropagator : public QQmlJSCompileP
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;
+ bool checkForEnumProblems(const QQmlJSRegisterContent &base, const QString &propertyName);
+
Verdict startInstruction(QV4::Moth::Instr::Type instr) override;
void endInstruction(QV4::Moth::Instr::Type instr) override;
@@ -188,7 +191,6 @@ private:
void handleUnqualifiedAccess(const QString &name, bool isMethod) const;
void checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name, bool isMethod) const;
- bool isRestricted(const QString &propertyName) const;
bool isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const;
enum PropertyResolution {
@@ -213,7 +215,9 @@ private:
const QQmlJSScope::ConstPtr &scope);
bool propagateTranslationMethod(const QList<QQmlJSMetaMethod> &methods, int argc, int argv);
void propagateStringArgCall(int argv);
- void propagatePropertyLookup(const QString &name);
+ bool propagateArrayMethod(const QString &name, int argc, int argv, const QQmlJSRegisterContent &valueType);
+ void propagatePropertyLookup(
+ const QString &name, int lookupIndex = QQmlJSRegisterContent::InvalidLookupIndex);
void propagateScopeLookupCall(const QString &functionName, int argc, int argv);
void saveRegisterStateForJump(int offset);
bool canConvertFromTo(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to);
@@ -239,8 +243,22 @@ private:
void recordEqualsType(int lhs);
void recordCompareType(int lhs);
+ // helper functions to deal with special cases in generate_ methods
+ void generate_CallProperty_SCMath(int base, int arcg, int argv);
+ void generate_CallProperty_SCconsole(int base, int argc, int argv);
+ void generate_Construct_SCDate(int argc, int argv);
+ void generate_Construct_SCArray(int argc, int argv);
+
+ // helper functions to perform QQmlSA checks
+ void generate_ret_SAcheck();
+ void generate_LoadQmlContextPropertyLookup_SAcheck(const QString &name);
+ void generate_StoreNameCommon_SAcheck(const QQmlJSRegisterContent &in, const QString &name);
+ void propagatePropertyLookup_SAcheck(const QString &propertyName);
+ void generate_StoreProperty_SAcheck(const QString propertyName, const QQmlJSRegisterContent &callBase);
+ void generate_callProperty_SAcheck(const QString propertyName, const QQmlJSScope::ConstPtr &baseType);
+
+
QQmlJSRegisterContent m_returnType;
- QQmlJSTypeInfo *m_typeInfo = nullptr;
QQmlSA::PassManager *m_passManager = nullptr;
QQmlJSScope::ConstPtr m_attachedContext;
diff --git a/src/qmlcompiler/qqmljstypereader.cpp b/src/qmlcompiler/qqmljstypereader.cpp
index 86d547d714..6e2e119e42 100644
--- a/src/qmlcompiler/qqmljstypereader.cpp
+++ b/src/qmlcompiler/qqmljstypereader.cpp
@@ -44,23 +44,21 @@ bool QQmlJSTypeReader::operator ()(const QSharedPointer<QQmlJSScope> &scope)
const bool success = isJavaScript ? (isESModule ? parser.parseModule()
: parser.parseProgram())
: parser.parse();
- if (!success)
- return false;
QQmlJS::AST::Node *rootNode = parser.rootNode();
- if (!rootNode)
- return false;
QQmlJSLogger logger;
logger.setFileName(m_file);
logger.setCode(code);
logger.setSilent(true);
- auto membersVisitor = m_importer->makeImportVisitor(
- scope, m_importer, &logger,
- QQmlJSImportVisitor::implicitImportDirectory(m_file, m_importer->resourceFileMapper()));
- rootNode->accept(membersVisitor.get());
- return true;
+ m_importer->runImportVisitor(rootNode,
+ { scope,
+ &logger,
+ QQmlJSImportVisitor::implicitImportDirectory(
+ m_file, m_importer->resourceFileMapper()),
+ {} });
+ return success && rootNode;
}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 901eccf387..93f9c7f7f4 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -19,42 +19,124 @@ using namespace Qt::StringLiterals;
Q_LOGGING_CATEGORY(lcTypeResolver, "qt.qml.compiler.typeresolver", QtInfoMsg);
+static inline void assertExtension(const QQmlJSScope::ConstPtr &type, QLatin1String extension)
+{
+ Q_ASSERT(type);
+ Q_ASSERT(type->extensionType().scope->internalName() == extension);
+ Q_ASSERT(type->extensionIsJavaScript());
+}
+
QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
- : m_imports(importer->builtinInternalNames())
- , m_trackedTypes(std::make_unique<QHash<QQmlJSScope::ConstPtr, TrackedType>>())
+ : m_imports(importer->builtinInternalNames()),
+ m_trackedTypes(std::make_unique<QHash<QQmlJSScope::ConstPtr, TrackedType>>())
{
const QQmlJSImporter::ImportedTypes &builtinTypes = m_imports;
+
m_voidType = builtinTypes.type(u"void"_s).scope;
+ assertExtension(m_voidType, "undefined"_L1);
+
m_nullType = builtinTypes.type(u"std::nullptr_t"_s).scope;
+ Q_ASSERT(m_nullType);
+
m_realType = builtinTypes.type(u"double"_s).scope;
+ assertExtension(m_realType, "Number"_L1);
+
m_floatType = builtinTypes.type(u"float"_s).scope;
- m_intType = builtinTypes.type(u"int"_s).scope;
- m_uintType = builtinTypes.type(u"uint"_s).scope;
+ assertExtension(m_floatType, "Number"_L1);
+
+ m_int8Type = builtinTypes.type(u"qint8"_s).scope;
+ assertExtension(m_int8Type, "Number"_L1);
+
+ m_uint8Type = builtinTypes.type(u"quint8"_s).scope;
+ assertExtension(m_uint8Type, "Number"_L1);
+
+ m_int16Type = builtinTypes.type(u"short"_s).scope;
+ assertExtension(m_int16Type, "Number"_L1);
+
+ m_uint16Type = builtinTypes.type(u"ushort"_s).scope;
+ assertExtension(m_uint16Type, "Number"_L1);
+
+ m_int32Type = builtinTypes.type(u"int"_s).scope;
+ assertExtension(m_int32Type, "Number"_L1);
+
+ m_uint32Type = builtinTypes.type(u"uint"_s).scope;
+ assertExtension(m_uint32Type, "Number"_L1);
+
+ m_int64Type = builtinTypes.type(u"qlonglong"_s).scope;
+ Q_ASSERT(m_int64Type);
+
+ m_uint64Type = builtinTypes.type(u"qulonglong"_s).scope;
+ Q_ASSERT(m_uint64Type);
+
+ m_sizeType = builtinTypes.type(u"qsizetype"_s).scope;
+ assertExtension(m_sizeType, "Number"_L1);
+
+ // qsizetype is either a 32bit or a 64bit signed integer. We don't want to special-case it.
+ Q_ASSERT(m_sizeType == m_int32Type || m_sizeType == m_int64Type);
+
m_boolType = builtinTypes.type(u"bool"_s).scope;
+ assertExtension(m_boolType, "Boolean"_L1);
+
m_stringType = builtinTypes.type(u"QString"_s).scope;
+ assertExtension(m_stringType, "String"_L1);
+
m_stringListType = builtinTypes.type(u"QStringList"_s).scope;
+ assertExtension(m_stringListType, "Array"_L1);
+
m_byteArrayType = builtinTypes.type(u"QByteArray"_s).scope;
+ assertExtension(m_byteArrayType, "ArrayBuffer"_L1);
+
m_urlType = builtinTypes.type(u"QUrl"_s).scope;
+ assertExtension(m_urlType, "URL"_L1);
+
m_dateTimeType = builtinTypes.type(u"QDateTime"_s).scope;
+ assertExtension(m_dateTimeType, "Date"_L1);
+
m_dateType = builtinTypes.type(u"QDate"_s).scope;
+ Q_ASSERT(m_dateType);
+
m_timeType = builtinTypes.type(u"QTime"_s).scope;
+ Q_ASSERT(m_timeType);
+
m_variantListType = builtinTypes.type(u"QVariantList"_s).scope;
+ assertExtension(m_variantListType, "Array"_L1);
+
+ m_variantMapType = builtinTypes.type(u"QVariantMap"_s).scope;
+ Q_ASSERT(m_variantMapType);
m_varType = builtinTypes.type(u"QVariant"_s).scope;
+ Q_ASSERT(m_varType);
+
m_jsValueType = builtinTypes.type(u"QJSValue"_s).scope;
- m_listPropertyType = builtinTypes.type(u"QQmlListProperty<QObject>"_s).scope;
+ Q_ASSERT(m_jsValueType);
+
+ m_qObjectType = builtinTypes.type(u"QObject"_s).scope;
+ assertExtension(m_qObjectType, "Object"_L1);
+
m_qObjectListType = builtinTypes.type(u"QObjectList"_s).scope;
+ assertExtension(m_qObjectListType, "Array"_L1);
+
+ m_qQmlScriptStringType = builtinTypes.type(u"QQmlScriptString"_s).scope;
+ Q_ASSERT(m_qQmlScriptStringType);
+
+ m_functionType = builtinTypes.type(u"function"_s).scope;
+ Q_ASSERT(m_functionType);
+
+ m_numberPrototype = builtinTypes.type(u"NumberPrototype"_s).scope;
+ Q_ASSERT(m_numberPrototype);
+
+ m_arrayPrototype = builtinTypes.type(u"ArrayPrototype"_s).scope;
+ Q_ASSERT(m_arrayPrototype);
+
+ m_listPropertyType = m_qObjectType->listType();
+ Q_ASSERT(m_listPropertyType->internalName() == u"QQmlListProperty<QObject>"_s);
+ Q_ASSERT(m_listPropertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence);
+ Q_ASSERT(m_listPropertyType->valueTypeName() == u"QObject"_s);
+ assertExtension(m_listPropertyType, "Array"_L1);
QQmlJSScope::Ptr emptyType = QQmlJSScope::create();
emptyType->setAccessSemantics(QQmlJSScope::AccessSemantics::None);
m_emptyType = emptyType;
- QQmlJSScope::Ptr emptyListType = QQmlJSScope::create();
- emptyListType->setInternalName(u"void*"_s);
- emptyListType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
- QQmlJSScope::resolveTypes(emptyListType, builtinTypes);
- Q_ASSERT(!emptyListType->extensionType().scope.isNull());
- m_emptyListType = emptyListType;
-
QQmlJSScope::Ptr jsPrimitiveType = QQmlJSScope::create();
jsPrimitiveType->setInternalName(u"QJSPrimitiveValue"_s);
jsPrimitiveType->setFilePath(u"qjsprimitivevalue.h"_s);
@@ -67,29 +149,26 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
metaObjectType->setAccessSemantics(QQmlJSScope::AccessSemantics::Reference);
m_metaObjectType = metaObjectType;
- QQmlJSScope::Ptr functionType = QQmlJSScope::create();
- functionType->setInternalName(u"function"_s);
- functionType->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
- m_functionType = functionType;
-
m_jsGlobalObject = importer->jsGlobalObject();
- auto numberMethods = m_jsGlobalObject->methods(u"Number"_s);
- Q_ASSERT(numberMethods.size() == 1);
- m_numberPrototype = numberMethods[0].returnType()->baseType();
- Q_ASSERT(m_numberPrototype);
- Q_ASSERT(m_numberPrototype->internalName() == u"NumberPrototype"_s);
- auto arrayMethods = m_jsGlobalObject->methods(u"Array"_s);
- Q_ASSERT(arrayMethods.size() == 1);
- m_arrayType = arrayMethods[0].returnType();
- Q_ASSERT(m_arrayType);
+ QQmlJSScope::Ptr forInIteratorPtr = QQmlJSScope::create();
+ forInIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
+ forInIteratorPtr->setFilePath(u"qjslist.h"_s);
+ forInIteratorPtr->setInternalName(u"QJSListForInIterator::Ptr"_s);
+ m_forInIteratorPtr = forInIteratorPtr;
+
+ QQmlJSScope::Ptr forOfIteratorPtr = QQmlJSScope::create();
+ forOfIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
+ forOfIteratorPtr->setFilePath(u"qjslist.h"_s);
+ forOfIteratorPtr->setInternalName(u"QJSListForOfIterator::Ptr"_s);
+ m_forOfIteratorPtr = forOfIteratorPtr;
}
/*!
\internal
Initializes the type resolver. As part of that initialization, makes \a
- visitor traverse the program.
+ visitor traverse the program when given.
*/
void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program)
{
@@ -100,7 +179,8 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
m_imports.clearTypes();
m_signalHandlers.clear();
- program->accept(visitor);
+ if (program)
+ program->accept(visitor);
m_objectsById = visitor->addressableScopes();
m_objectsByLocation = visitor->scopesBylocation();
@@ -108,6 +188,16 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
m_imports = visitor->imports();
}
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::mathObject() const
+{
+ return jsGlobalObject()->property(u"Math"_s).type();
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::consoleObject() const
+{
+ return jsGlobalObject()->property(u"console"_s).type();
+}
+
QQmlJSScope::ConstPtr
QQmlJSTypeResolver::scopeForLocation(const QV4::CompiledData::Location &location) const
{
@@ -117,19 +207,15 @@ QQmlJSTypeResolver::scopeForLocation(const QV4::CompiledData::Location &location
return m_objectsByLocation[location];
}
-QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopeForId(
- const QString &id, const QQmlJSScope::ConstPtr &referrer) const
-{
- return m_objectsById.scope(id, referrer);
-}
-
QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeFromAST(QQmlJS::AST::Type *type) const
{
const QString typeId = QmlIR::IRBuilder::asString(type->typeId);
if (!type->typeArgument)
return m_imports.type(typeId).scope;
- if (typeId == u"list"_s)
- return typeForName(type->typeArgument->toString())->listType();
+ if (typeId == u"list"_s) {
+ if (const QQmlJSScope::ConstPtr typeArgument = typeForName(type->typeArgument->toString()))
+ return typeArgument->listType();
+ }
return QQmlJSScope::ConstPtr();
}
@@ -140,7 +226,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeForConst(QV4::ReturnedValue rv) co
return voidType();
if (value.isInt32())
- return intType();
+ return int32Type();
if (value.isBoolean())
return boolType();
@@ -180,9 +266,9 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
case QSOperator::Op::BitXor:
case QSOperator::Op::LShift:
case QSOperator::Op::RShift:
- return builtinType(intType());
+ return builtinType(int32Type());
case QSOperator::Op::URShift:
- return builtinType(uintType());
+ return builtinType(uint32Type());
case QSOperator::Op::Add: {
const auto leftContents = containedType(left);
const auto rightContents = containedType(right);
@@ -191,7 +277,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
const QQmlJSScope::ConstPtr result = merge(leftContents, rightContents);
if (equals(result, boolType()))
- return builtinType(intType());
+ return builtinType(int32Type());
if (isNumeric(result))
return builtinType(realType());
@@ -201,7 +287,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi
case QSOperator::Op::Mul:
case QSOperator::Op::Exp: {
const QQmlJSScope::ConstPtr result = merge(containedType(left), containedType(right));
- return builtinType(equals(result, boolType()) ? intType() : realType());
+ return builtinType(equals(result, boolType()) ? int32Type() : realType());
}
case QSOperator::Op::Div:
case QSOperator::Op::Mod:
@@ -222,14 +308,14 @@ QQmlJSRegisterContent QQmlJSTypeResolver::typeForArithmeticUnaryOperation(
case UnaryOperator::Not:
return builtinType(boolType());
case UnaryOperator::Complement:
- return builtinType(intType());
+ return builtinType(int32Type());
case UnaryOperator::Plus:
if (isIntegral(operand))
return operand;
Q_FALLTHROUGH();
default:
if (equals(containedType(operand), boolType()))
- return builtinType(intType());
+ return builtinType(int32Type());
break;
}
@@ -248,13 +334,18 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const
bool QQmlJSTypeResolver::isIntegral(const QQmlJSRegisterContent &type) const
{
- return equals(containedType(type), m_intType) || equals(containedType(type), m_uintType);
+ return isIntegral(containedType(type));
+}
+
+bool QQmlJSTypeResolver::isIntegral(const QQmlJSScope::ConstPtr &type) const
+{
+ // Only types of length <= 32bit count as integral
+ return isSignedInteger(type) || isUnsignedInteger(type);
}
bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
{
- return equals(type, m_intType) || equals(type, m_uintType)
- || equals(type, m_realType) || equals(type, m_floatType)
+ return isNumeric(type)
|| equals(type, m_boolType) || equals(type, m_voidType) || equals(type, m_nullType)
|| equals(type, m_stringType) || equals(type, m_jsPrimitiveType);
}
@@ -266,7 +357,33 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
if (mode == QQmlJSScope::ExtensionNamespace)
return false;
return equals(scope, m_numberPrototype);
- });
+ });
+}
+
+bool QQmlJSTypeResolver::isSignedInteger(const QQmlJSScope::ConstPtr &type) const
+{
+ return equals(type, m_int8Type)
+ || equals(type, m_int16Type)
+ || equals(type, m_int32Type)
+ || equals(type, m_int64Type);
+}
+
+bool QQmlJSTypeResolver::isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const
+{
+ return equals(type, m_uint8Type)
+ || equals(type, m_uint16Type)
+ || equals(type, m_uint32Type)
+ || equals(type, m_uint64Type);
+}
+
+bool QQmlJSTypeResolver::isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const
+{
+ return (equals(type, m_uint8Type)
+ || equals(type, m_int8Type)
+ || equals(type, m_uint16Type)
+ || equals(type, m_int16Type)
+ || equals(type, m_uint32Type)
+ || equals(type, m_int32Type));
}
QQmlJSScope::ConstPtr
@@ -317,15 +434,15 @@ QQmlJSRegisterContent QQmlJSTypeResolver::transformed(
if (origin.isType()) {
return QQmlJSRegisterContent::create(
(this->*op)(origin.storedType()), (this->*op)(origin.type()),
- origin.variant(), (this->*op)(origin.scopeType()));
+ origin.resultLookupIndex(), origin.variant(), (this->*op)(origin.scopeType()));
}
if (origin.isProperty()) {
QQmlJSMetaProperty prop = origin.property();
prop.setType((this->*op)(prop.type()));
return QQmlJSRegisterContent::create(
- (this->*op)(origin.storedType()), prop,
- origin.variant(), (this->*op)(origin.scopeType()));
+ (this->*op)(origin.storedType()), prop, origin.baseLookupIndex(),
+ origin.resultLookupIndex(), origin.variant(), (this->*op)(origin.scopeType()));
}
if (origin.isEnumeration()) {
@@ -349,16 +466,31 @@ QQmlJSRegisterContent QQmlJSTypeResolver::transformed(
}
if (origin.isConversion()) {
+ // When retrieving the originals we want a deep retrieval.
+ // When tracking a new type, we don't want to re-track its originals, though.
+
+ const QList<QQmlJSScope::ConstPtr> origins = origin.conversionOrigins();
+ QList<QQmlJSScope::ConstPtr> transformedOrigins;
+ if (op == &QQmlJSTypeResolver::trackedType) {
+ transformedOrigins = origins;
+ } else {
+ transformedOrigins.reserve(origins.length());
+ for (const QQmlJSScope::ConstPtr &origin: origins)
+ transformedOrigins.append((this->*op)(origin));
+ }
+
return QQmlJSRegisterContent::create(
- (this->*op)(origin.storedType()), origin.conversionOrigins(),
+ (this->*op)(origin.storedType()),
+ transformedOrigins,
(this->*op)(origin.conversionResult()),
+ (this->*op)(origin.conversionResultScope()),
origin.variant(), (this->*op)(origin.scopeType()));
}
Q_UNREACHABLE_RETURN({});
}
-QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName(
+QQmlJSRegisterContent QQmlJSTypeResolver::registerContentForName(
const QString &name, const QQmlJSScope::ConstPtr &scopeType,
bool hasObjectModulePrefix) const
{
@@ -366,13 +498,17 @@ QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName(
if (!type)
return QQmlJSRegisterContent();
- if (type->isSingleton())
- return QQmlJSRegisterContent::create(storedType(type), type,
- QQmlJSRegisterContent::Singleton, scopeType);
+ if (type->isSingleton()) {
+ return QQmlJSRegisterContent::create(
+ storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::Singleton, scopeType);
+ }
- if (type->isScript())
- return QQmlJSRegisterContent::create(storedType(type), type,
- QQmlJSRegisterContent::Script, scopeType);
+ if (type->isScript()) {
+ return QQmlJSRegisterContent::create(
+ storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::Script, scopeType);
+ }
if (const auto attached = type->attachedType()) {
if (!genericType(attached)) {
@@ -390,7 +526,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName(
// mode, we will figure this out using the scope type and access any enums of the
// plain type directly. In indirect mode, we can use enum lookups.
return QQmlJSRegisterContent::create(
- storedType(attached), attached,
+ storedType(attached), attached, QQmlJSRegisterContent::InvalidLookupIndex,
hasObjectModulePrefix
? QQmlJSRegisterContent::ObjectAttached
: QQmlJSRegisterContent::ScopeAttached, type);
@@ -404,11 +540,17 @@ QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName(
// We may still need the plain type reference for enum lookups,
// Store it as QMetaObject.
// This only works with namespaces and object types.
- return QQmlJSRegisterContent::create(metaObjectType(), metaObjectType(),
- QQmlJSRegisterContent::MetaType, type);
+ return QQmlJSRegisterContent::create(
+ metaObjectType(), metaObjectType(), QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::MetaType, type);
case QQmlJSScope::AccessSemantics::Sequence:
case QQmlJSScope::AccessSemantics::Value:
- // This is not actually a type reference. You cannot get the metaobject
+ if (canAddressValueTypes()) {
+ return QQmlJSRegisterContent::create(
+ metaObjectType(), metaObjectType(), QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::MetaType, type);
+ }
+ // Else this is not actually a type reference. You cannot get the metaobject
// of a value type in QML and sequences don't even have metaobjects.
break;
}
@@ -439,23 +581,33 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalContainedType(
return originalType(containedType(container));
}
-void QQmlJSTypeResolver::adjustTrackedType(
+bool QQmlJSTypeResolver::adjustTrackedType(
const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
{
if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
- return;
+ return true;
const auto it = m_trackedTypes->find(tracked);
Q_ASSERT(it != m_trackedTypes->end());
+
+ // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
+ // we better not change the type.
+ if (!canPrimitivelyConvertFromTo(tracked, conversion)
+ && !canPopulate(conversion, tracked, nullptr)
+ && !selectConstructor(conversion, tracked, nullptr).isValid()) {
+ return false;
+ }
+
it->replacement = comparableType(conversion);
*it->clone = std::move(*QQmlJSScope::clone(conversion));
+ return true;
}
-void QQmlJSTypeResolver::adjustTrackedType(
+bool QQmlJSTypeResolver::adjustTrackedType(
const QQmlJSScope::ConstPtr &tracked, const QList<QQmlJSScope::ConstPtr> &conversions) const
{
if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
- return;
+ return true;
const auto it = m_trackedTypes->find(tracked);
Q_ASSERT(it != m_trackedTypes->end());
@@ -466,10 +618,28 @@ void QQmlJSTypeResolver::adjustTrackedType(
// If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
// we better not change the type.
- if (canPrimitivelyConvertFromTo(tracked, result)) {
- it->replacement = comparableType(result);
- *mutableTracked = std::move(*QQmlJSScope::clone(result));
+ if (!canPrimitivelyConvertFromTo(tracked, result)
+ && !canPopulate(result, tracked, nullptr)
+ && !selectConstructor(result, tracked, nullptr).isValid()) {
+ return false;
}
+
+ it->replacement = comparableType(result);
+ *mutableTracked = std::move(*QQmlJSScope::clone(result));
+ return true;
+}
+
+void QQmlJSTypeResolver::adjustOriginalType(
+ const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
+{
+ if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
+ return;
+
+ const auto it = m_trackedTypes->find(tracked);
+ Q_ASSERT(it != m_trackedTypes->end());
+
+ it->original = conversion;
+ *it->clone = std::move(*QQmlJSScope::clone(conversion));
}
void QQmlJSTypeResolver::generalizeType(const QQmlJSScope::ConstPtr &type) const
@@ -512,8 +682,11 @@ QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &conta
bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to) const
{
- if (canPrimitivelyConvertFromTo(from, to))
+ if (canPrimitivelyConvertFromTo(from, to)
+ || canPopulate(to, from, nullptr)
+ || selectConstructor(to, from, nullptr).isValid()) {
return true;
+ }
// ### need a generic solution for custom cpp types:
// if (from->m_hasBoolOverload && equals(to, boolType))
@@ -525,11 +698,9 @@ bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
// in QQmlJSCodeGenerator::conversion().
if (equals(from, m_stringType) && !to.isNull()) {
const QString toTypeName = to->internalName();
- if (toTypeName == u"QTime"_s || toTypeName == u"QDate"_s
- || toTypeName == u"QPoint"_s || toTypeName == u"QPointF"_s
+ if (toTypeName == u"QPoint"_s || toTypeName == u"QPointF"_s
|| toTypeName == u"QSize"_s || toTypeName == u"QSizeF"_s
- || toTypeName == u"QRect"_s || toTypeName == u"QRectF"_s
- || toTypeName == u"QColor"_s) {
+ || toTypeName == u"QRect"_s || toTypeName == u"QRectF"_s) {
return true;
}
}
@@ -552,16 +723,28 @@ static QQmlJSRegisterContent::ContentVariant mergeVariants(QQmlJSRegisterContent
QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
const QQmlJSRegisterContent &b) const
{
+ if (a == b)
+ return a;
+
QList<QQmlJSScope::ConstPtr> origins;
- if (a.isConversion())
+
+ QQmlJSScope::ConstPtr aResultScope;
+ if (a.isConversion()) {
origins.append(a.conversionOrigins());
- else
+ aResultScope = a.conversionResultScope();
+ } else {
origins.append(containedType(a));
+ aResultScope = a.scopeType();
+ }
- if (b.isConversion())
+ QQmlJSScope::ConstPtr bResultScope;
+ if (b.isConversion()) {
origins.append(b.conversionOrigins());
- else
+ bResultScope = b.conversionResultScope();
+ } else {
origins.append(containedType(b));
+ bResultScope = b.scopeType();
+ }
std::sort(origins.begin(), origins.end());
const auto erase = std::unique(origins.begin(), origins.end());
@@ -571,6 +754,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
merge(a.storedType(), b.storedType()),
origins,
merge(containedType(a), containedType(b)),
+ merge(aResultScope, bResultScope),
mergeVariants(a.variant(), b.variant()),
merge(a.scopeType(), b.scopeType()));
}
@@ -605,36 +789,81 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
if (equals(b, jsValueType()) || equals(b, varType()))
return b;
- auto canConvert = [&](const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) {
- return (equals(a, from) && equals(b, to)) || (equals(b, from) && equals(a, to));
+ const auto isInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
+ return (isIntegral(type) && !equals(type, uint32Type())) || equals(type, boolType());
+ };
+
+ if (isInt32Compatible(a) && isInt32Compatible(b))
+ return int32Type();
+
+ const auto isUInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
+ return isUnsignedInteger(type) || equals(type, boolType());
};
+ if (isUInt32Compatible(a) && isUInt32Compatible(b))
+ return uint32Type();
+
if (isNumeric(a) && isNumeric(b))
return realType();
- if (canConvert(boolType(), intType()))
- return intType();
- if (canConvert(boolType(), uintType()))
- return uintType();
- if (canConvert(intType(), stringType()))
- return stringType();
- if (canConvert(uintType(), stringType()))
- return stringType();
if (isPrimitive(a) && isPrimitive(b))
return jsPrimitiveType();
if (auto commonBase = commonBaseType(a, b))
return commonBase;
- if (equals(a, nullType()) && b->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if ((equals(a, nullType()) || equals(a, boolType())) && b->isReferenceType())
return b;
- if (equals(b, nullType()) && a->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ if ((equals(b, nullType()) || equals(b, boolType())) && a->isReferenceType())
return a;
return varType();
}
+bool QQmlJSTypeResolver::canHold(
+ const QQmlJSScope::ConstPtr &container, const QQmlJSScope::ConstPtr &contained) const
+{
+ if (equals(container, contained)
+ || equals(container, m_varType)
+ || equals(container, m_jsValueType)) {
+ return true;
+ }
+
+ if (equals(container, m_jsPrimitiveType))
+ return isPrimitive(contained);
+
+ if (equals(container, m_variantListType))
+ return contained->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
+
+ if (equals(container, m_qObjectListType) || equals(container, m_listPropertyType)) {
+ if (contained->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
+ return false;
+ if (QQmlJSScope::ConstPtr value = contained->valueType())
+ return value->isReferenceType();
+ return false;
+ }
+
+ if (QQmlJSUtils::searchBaseAndExtensionTypes(
+ container, [&](const QQmlJSScope::ConstPtr &base) {
+ return equals(base, contained);
+ })) {
+ return true;
+ }
+
+ if (container->isReferenceType()) {
+ if (QQmlJSUtils::searchBaseAndExtensionTypes(
+ contained, [&](const QQmlJSScope::ConstPtr &base) {
+ return equals(base, container);
+ })) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
bool QQmlJSTypeResolver::canHoldUndefined(const QQmlJSRegisterContent &content) const
{
const auto canBeUndefined = [this](const QQmlJSScope::ConstPtr &type) {
@@ -657,6 +886,30 @@ bool QQmlJSTypeResolver::canHoldUndefined(const QQmlJSRegisterContent &content)
return false;
}
+bool QQmlJSTypeResolver::isOptionalType(const QQmlJSRegisterContent &content) const
+{
+ if (!content.isConversion())
+ return false;
+
+ const auto origins = content.conversionOrigins();
+ if (origins.length() != 2)
+ return false;
+
+ return equals(origins[0], m_voidType) || equals(origins[1], m_voidType);
+}
+
+QQmlJSScope::ConstPtr QQmlJSTypeResolver::extractNonVoidFromOptionalType(
+ const QQmlJSRegisterContent &content) const
+{
+ if (!isOptionalType(content))
+ return QQmlJSScope::ConstPtr();
+
+ const auto origins = content.conversionOrigins();
+ const QQmlJSScope::ConstPtr result = equals(origins[0], m_voidType) ? origins[1] : origins[0];
+ Q_ASSERT(!equals(result, m_voidType));
+ return result;
+}
+
QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
const QQmlJSScope::ConstPtr &type,
ComponentIsGeneric allowComponent) const
@@ -704,33 +957,21 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
if (type->isListProperty())
return m_listPropertyType;
- if (isPrimitive(type) || equals(type, m_jsValueType)
- || equals(type, m_urlType) || equals(type, m_dateTimeType)
- || equals(type, m_dateType) || equals(type, m_timeType)
- || equals(type, m_variantListType) || equals(type, m_varType)
- || equals(type, m_stringListType) || equals(type, m_emptyListType)
- || equals(type, m_byteArrayType)) {
- return type;
- }
-
- if (type->scopeType() == QQmlJSScope::EnumScope)
- return m_intType;
+ if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
+ return type->baseType();
- if (isNumeric(type))
- return m_realType;
+ if (isPrimitive(type))
+ return type;
- if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
- if (const QQmlJSScope::ConstPtr valueType = type->valueType()) {
- switch (valueType->accessSemantics()) {
- case QQmlJSScope::AccessSemantics::Value:
- return genericType(valueType)->listType();
- case QQmlJSScope::AccessSemantics::Reference:
- return m_qObjectListType;
- default:
- break;
- }
- }
- return m_variantListType;
+ for (const QQmlJSScope::ConstPtr &builtin : {
+ m_realType, m_floatType, m_int8Type, m_uint8Type, m_int16Type, m_uint16Type,
+ m_int32Type, m_uint32Type, m_int64Type, m_uint64Type, m_boolType, m_stringType,
+ m_stringListType, m_byteArrayType, m_urlType, m_dateTimeType, m_dateType,
+ m_timeType, m_variantListType, m_variantMapType, m_varType, m_jsValueType,
+ m_jsPrimitiveType, m_listPropertyType, m_qObjectType, m_qObjectListType,
+ m_metaObjectType, m_forInIteratorPtr, m_forOfIteratorPtr }) {
+ if (equals(type, builtin) || equals(type, builtin->listType()))
+ return type;
}
return m_varType;
@@ -739,12 +980,15 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
QQmlJSRegisterContent QQmlJSTypeResolver::builtinType(const QQmlJSScope::ConstPtr &type) const
{
Q_ASSERT(storedType(type) == type);
- return QQmlJSRegisterContent::create(type, type, QQmlJSRegisterContent::Builtin);
+ return QQmlJSRegisterContent::create(
+ type, type, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Builtin);
}
QQmlJSRegisterContent QQmlJSTypeResolver::globalType(const QQmlJSScope::ConstPtr &type) const
{
- return QQmlJSRegisterContent::create(storedType(type), type, QQmlJSRegisterContent::Unknown);
+ return QQmlJSRegisterContent::create(
+ storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::Unknown);
}
static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::ExtensionKind mode,
@@ -754,6 +998,7 @@ static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::Ex
case QQmlJSScope::NotExtension:
return isMethod ? QQmlJSRegisterContent::ScopeMethod : QQmlJSRegisterContent::ScopeProperty;
case QQmlJSScope::ExtensionType:
+ case QQmlJSScope::ExtensionJavaScript:
return isMethod ? QQmlJSRegisterContent::ExtensionScopeMethod
: QQmlJSRegisterContent::ExtensionScopeProperty;
case QQmlJSScope::ExtensionNamespace:
@@ -780,7 +1025,8 @@ static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &s
}
QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr &scope,
- const QString &name) const
+ const QString &name, int lookupIndex,
+ QQmlJSScopesByIdOptions options) const
{
const auto isAssignedToDefaultProperty = [this](const QQmlJSScope::ConstPtr &parent,
const QQmlJSScope::ConstPtr &child) {
@@ -791,7 +1037,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
const QList<QQmlJSMetaPropertyBinding> defaultPropBindings =
parent->propertyBindings(defaultPropertyName);
for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object
+ if (binding.bindingType() == QQmlSA::BindingType::Object
&& equals(binding.objectType(), child)) {
return true;
}
@@ -799,8 +1045,8 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
return false;
};
- if (QQmlJSScope::ConstPtr identified = scopeForId(name, scope)) {
- return QQmlJSRegisterContent::create(storedType(identified), identified,
+ if (QQmlJSScope::ConstPtr identified = m_objectsById.scope(name, scope, options)) {
+ return QQmlJSRegisterContent::create(storedType(identified), identified, lookupIndex,
QQmlJSRegisterContent::ObjectById, scope);
}
@@ -823,8 +1069,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
}
}
result = QQmlJSRegisterContent::create(
- storedType(prop.type()),
- prop, scopeContentVariant(mode, false), scope);
+ storedType(prop.type()), prop,
+ QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
+ scopeContentVariant(mode, false), scope);
return true;
}
@@ -851,14 +1098,16 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr
}
}
- QQmlJSRegisterContent result = referenceTypeForName(name);
+ QQmlJSRegisterContent result = registerContentForName(name);
+
if (result.isValid())
return result;
if (m_jsGlobalObject->hasProperty(name)) {
- return QQmlJSRegisterContent::create(jsValueType(), m_jsGlobalObject->property(name),
- QQmlJSRegisterContent::JavaScriptGlobal,
- m_jsGlobalObject);
+ return QQmlJSRegisterContent::create(
+ jsValueType(), m_jsGlobalObject->property(name),
+ QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
+ QQmlJSRegisterContent::JavaScriptGlobal, m_jsGlobalObject);
} else if (m_jsGlobalObject->hasMethod(name)) {
return QQmlJSRegisterContent::create(jsValueType(), m_jsGlobalObject->methods(name),
QQmlJSRegisterContent::JavaScriptGlobal,
@@ -876,23 +1125,22 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS
if (name.isEmpty() || !name.at(0).isUpper())
return false;
- const bool inExtension =
- (mode == QQmlJSScope::ExtensionType) || (mode == QQmlJSScope::ExtensionNamespace);
+ const bool inExtension = (mode != QQmlJSScope::NotExtension);
const auto enums = scope->ownEnumerations();
for (const auto &enumeration : enums) {
- if (enumeration.name() == name) {
+ if ((enumeration.isScoped() || enumeration.isQml()) && enumeration.name() == name) {
*result = QQmlJSRegisterContent::create(
- storedType(intType()), enumeration, QString(),
+ storedType(enumeration.type()), enumeration, QString(),
inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum
: QQmlJSRegisterContent::ObjectEnum,
scope);
return true;
}
- if (enumeration.hasKey(name)) {
+ if (!enumeration.isScoped() && enumeration.hasKey(name)) {
*result = QQmlJSRegisterContent::create(
- storedType(intType()), enumeration, name,
+ storedType(enumeration.type()), enumeration, name,
inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum
: QQmlJSRegisterContent::ObjectEnum,
scope);
@@ -903,6 +1151,143 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS
return false;
}
+bool QQmlJSTypeResolver::canPopulate(
+ const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
+ bool *isExtension) const
+{
+ // TODO: We could allow QVariantMap and QVariantHash to be populated, but that needs extra
+ // code in the code generator.
+
+ if (type.isNull()
+ || canHold(passedArgumentType, type)
+ || isPrimitive(passedArgumentType)
+ || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
+ || !type->isStructured()) {
+ return false;
+ }
+
+ if (isExtension)
+ *isExtension = !type->extensionType().scope.isNull();
+
+ return true;
+}
+
+QQmlJSMetaMethod QQmlJSTypeResolver::selectConstructor(
+ const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
+ bool *isExtension) const
+{
+ // If the "from" type can hold the target type, we should not try to coerce
+ // it to any constructor argument.
+ if (type.isNull()
+ || canHold(passedArgumentType, type)
+ || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
+ || !type->isCreatable()) {
+ return QQmlJSMetaMethod();
+ }
+
+ auto doSelectConstructor = [&](const QQmlJSScope::ConstPtr &type) {
+ QQmlJSMetaMethod candidate;
+
+ const auto ownMethods = type->ownMethods();
+ for (const QQmlJSMetaMethod &method : ownMethods) {
+ if (!method.isConstructor())
+ continue;
+
+ const auto index = method.constructorIndex();
+ Q_ASSERT(index != QQmlJSMetaMethod::RelativeFunctionIndex::Invalid);
+
+ const auto methodArguments = method.parameters();
+ if (methodArguments.size() != 1)
+ continue;
+
+ const QQmlJSScope::ConstPtr methodArgumentType = methodArguments[0].type();
+
+ if (equals(passedArgumentType, methodArgumentType))
+ return method;
+
+ // Do not select further ctors here. We don't want to do multi-step construction as that
+ // is confusing and easily leads to infinite recursion.
+ if (!candidate.isValid()
+ && canPrimitivelyConvertFromTo(passedArgumentType, methodArgumentType)) {
+ candidate = method;
+ }
+ }
+
+ return candidate;
+ };
+
+ if (QQmlJSScope::ConstPtr extension = type->extensionType().scope) {
+ const QQmlJSMetaMethod ctor = doSelectConstructor(extension);
+ if (ctor.isValid()) {
+ if (isExtension)
+ *isExtension = true;
+ return ctor;
+ }
+ }
+
+ if (isExtension)
+ *isExtension = false;
+
+ return doSelectConstructor(type);
+}
+
+bool QQmlJSTypeResolver::areEquivalentLists(
+ const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
+{
+ const QQmlJSScope::ConstPtr equivalentLists[2][2] = {
+ { m_stringListType, m_stringType->listType() },
+ { m_variantListType, m_varType->listType() }
+ };
+
+ for (const auto eq : equivalentLists) {
+ if ((equals(a, eq[0]) && equals(b, eq[1])) || (equals(a, eq[1]) && equals(b, eq[0])))
+ return true;
+ }
+
+ return false;
+}
+
+bool QQmlJSTypeResolver::isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const
+{
+ // pointers are trivially copyable
+ if (type->isReferenceType())
+ return true;
+
+ // Enum values are trivially copyable
+ if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
+ return true;
+
+ for (const QQmlJSScope::ConstPtr &trivial : {
+ m_nullType, m_voidType,
+ m_boolType, m_metaObjectType,
+ m_realType, m_floatType,
+ m_int8Type, m_uint8Type,
+ m_int16Type, m_uint16Type,
+ m_int32Type, m_uint32Type,
+ m_int64Type, m_uint64Type }) {
+ if (equals(type, trivial))
+ return true;
+ }
+
+ return false;
+}
+
+bool QQmlJSTypeResolver::inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) const
+{
+ const bool matchByName = !base->isComposite();
+ for (QQmlJSScope::ConstPtr derivedBase = derived; derivedBase;
+ derivedBase = derivedBase->baseType()) {
+ if (equals(derivedBase, base))
+ return true;
+ if (matchByName
+ && !derivedBase->isComposite()
+ && derivedBase->internalName() == base->internalName()) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
{
@@ -912,12 +1297,14 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
return true;
if (equals(from, m_jsValueType) || equals(to, m_jsValueType))
return true;
+ if (equals(to, m_qQmlScriptStringType))
+ return true;
if (isNumeric(from) && isNumeric(to))
return true;
if (isNumeric(from) && equals(to, m_boolType))
return true;
if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
- && equals(to, m_boolType)) {
+ && (equals(to, m_boolType) || equals(to, m_stringType))) {
return true;
}
@@ -925,6 +1312,10 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (isNumeric(from) && equals(to, m_stringType))
return true;
+ // We can convert strings to numbers, but not to enums
+ if (equals(from, m_stringType) && isNumeric(to))
+ return to->scopeType() != QQmlJSScope::ScopeType::EnumScope;
+
// We can always convert between strings and urls.
if ((equals(from, m_stringType) && equals(to, m_urlType))
|| (equals(from, m_urlType) && equals(to, m_stringType))) {
@@ -937,25 +1328,26 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
return true;
}
- if (equals(from, m_voidType) || equals(to, m_voidType))
+ if (equals(to, m_voidType))
return true;
if (to.isNull())
- return false;
-
- if (equals(from, m_stringType) && equals(to, m_dateTimeType))
- return true;
+ return equals(from, m_voidType);
- for (const auto &originType : {m_dateTimeType, m_dateType, m_timeType}) {
+ const auto types = { m_dateTimeType, m_dateType, m_timeType, m_stringType };
+ for (const auto &originType : types) {
if (!equals(from, originType))
continue;
- for (const auto &targetType : {m_dateTimeType, m_dateType, m_timeType, m_stringType}) {
+ for (const auto &targetType : types) {
if (equals(to, targetType))
return true;
}
- break;;
+ if (equals(to, m_realType))
+ return true;
+
+ break;
}
if (equals(from, m_nullType)
@@ -971,7 +1363,7 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (equals(to, m_jsPrimitiveType))
return isPrimitive(from);
- if (equals(from, m_emptyListType) || equals(from, m_variantListType))
+ if (equals(from, m_variantListType))
return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
const bool matchByName = !to->isComposite();
@@ -987,6 +1379,24 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (canConvertFromTo(from, m_jsPrimitiveType) && canConvertFromTo(m_jsPrimitiveType, to))
return true;
+ // We can convert everything to bool.
+ if (equals(to, m_boolType))
+ return true;
+
+ if (areEquivalentLists(from, to))
+ return true;
+
+ if (from->isListProperty()
+ && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
+ && canConvertFromTo(from->valueType(), to->valueType())) {
+ return true;
+ }
+
+ if (equals(to, m_stringType)
+ && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
+ return canConvertFromTo(from->valueType(), m_stringType);
+ }
+
return false;
}
@@ -995,14 +1405,17 @@ QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
{
QQmlJSMetaProperty prop;
prop.setPropertyName(u"length"_s);
- prop.setTypeName(u"int"_s);
- prop.setType(intType());
+ prop.setTypeName(u"qsizetype"_s);
+ prop.setType(sizeType());
prop.setIsWritable(isWritable);
- return QQmlJSRegisterContent::create(intType(), prop, QQmlJSRegisterContent::Builtin, scope);
+ return QQmlJSRegisterContent::create(
+ sizeType(), prop, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Builtin, scope);
}
-QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr &type,
- const QString &name) const
+QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
+ const QQmlJSScope::ConstPtr &type, const QString &name, int baseLookupIndex,
+ int resultLookupIndex) const
{
QQmlJSRegisterContent result;
@@ -1010,14 +1423,26 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
if (equals(type, metaObjectType()))
return {};
+ if (equals(type, variantMapType())) {
+ QQmlJSMetaProperty prop;
+ prop.setPropertyName(name);
+ prop.setTypeName(u"QVariant"_s);
+ prop.setType(varType());
+ prop.setIsWritable(true);
+ return QQmlJSRegisterContent::create(
+ varType(), prop, baseLookupIndex, resultLookupIndex,
+ QQmlJSRegisterContent::GenericObjectProperty, type);
+ }
+
if (equals(type, jsValueType())) {
QQmlJSMetaProperty prop;
prop.setPropertyName(name);
prop.setTypeName(u"QJSValue"_s);
prop.setType(jsValueType());
prop.setIsWritable(true);
- return QQmlJSRegisterContent::create(jsValueType(), prop,
- QQmlJSRegisterContent::JavaScriptObjectProperty, type);
+ return QQmlJSRegisterContent::create(
+ jsValueType(), prop, baseLookupIndex, resultLookupIndex,
+ QQmlJSRegisterContent::GenericObjectProperty, type);
}
if ((equals(type, stringType())
@@ -1032,7 +1457,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
const auto prop = scope->ownProperty(name);
result = QQmlJSRegisterContent::create(
storedType(prop.type()),
- prop,
+ prop, baseLookupIndex, resultLookupIndex,
mode == QQmlJSScope::NotExtension
? QQmlJSRegisterContent::ObjectProperty
: QQmlJSRegisterContent::ExtensionObjectProperty,
@@ -1050,20 +1475,6 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
scope);
return true;
}
-
- if (std::optional<QQmlJSScope::JavaScriptIdentifier> identifier =
- scope->findJSIdentifier(name);
- identifier.has_value()) {
- QQmlJSMetaProperty prop;
- prop.setPropertyName(name);
- prop.setTypeName(u"QJSValue"_s);
- prop.setType(jsValueType());
- prop.setIsWritable(!identifier->isConst);
-
- result = QQmlJSRegisterContent::create(
- jsValueType(), prop, QQmlJSRegisterContent::JavaScriptObject, type);
- return true;
- }
}
return checkEnums(scope, name, &result, mode);
@@ -1072,6 +1483,23 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
if (QQmlJSUtils::searchBaseAndExtensionTypes(type, check))
return result;
+ for (auto scope = type;
+ scope && (scope->scopeType() == QQmlSA::ScopeType::JSFunctionScope
+ || scope->scopeType() == QQmlSA::ScopeType::JSLexicalScope);
+ scope = scope->parentScope()) {
+ if (auto ownIdentifier = scope->ownJSIdentifier(name)) {
+ QQmlJSMetaProperty prop;
+ prop.setPropertyName(name);
+ prop.setTypeName(u"QJSValue"_s);
+ prop.setType(jsValueType());
+ prop.setIsWritable(!(ownIdentifier.value().isConst));
+
+ return QQmlJSRegisterContent::create(jsValueType(), prop, baseLookupIndex,
+ resultLookupIndex,
+ QQmlJSRegisterContent::JavaScriptObject, scope);
+ }
+ }
+
if (QQmlJSScope::ConstPtr attachedBase = typeForName(name)) {
if (QQmlJSScope::ConstPtr attached = attachedBase->attachedType()) {
if (!genericType(attached)) {
@@ -1085,9 +1513,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr
qmlCompiler, type->sourceLocation());
return {};
} else {
- return QQmlJSRegisterContent::create(storedType(attached), attached,
- QQmlJSRegisterContent::ObjectAttached,
- attachedBase);
+ return QQmlJSRegisterContent::create(
+ storedType(attached), attached, resultLookupIndex,
+ QQmlJSRegisterContent::ObjectAttached, attachedBase);
}
}
}
@@ -1110,12 +1538,12 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberEnumType(const QQmlJSScope::Cons
return {};
}
-QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent &type,
- const QString &name) const
+QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
+ const QQmlJSRegisterContent &type, const QString &name, int lookupIndex) const
{
if (type.isType()) {
const auto content = type.type();
- const auto result = memberType(content, name);
+ const auto result = memberType(content, name, type.resultLookupIndex(), lookupIndex);
if (result.isValid())
return result;
@@ -1124,12 +1552,12 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
return memberEnumType(type.scopeType(), name);
}
if (type.isProperty())
- return memberType(type.property().type(), name);
+ return memberType(type.property().type(), name, type.resultLookupIndex(), lookupIndex);
if (type.isEnumeration()) {
const auto enumeration = type.enumeration();
if (!type.enumMember().isEmpty() || !enumeration.hasKey(name))
return {};
- return QQmlJSRegisterContent::create(storedType(intType()), enumeration, name,
+ return QQmlJSRegisterContent::create(storedType(enumeration.type()), enumeration, name,
QQmlJSRegisterContent::ObjectEnum, type.scopeType());
}
if (type.isMethod()) {
@@ -1138,9 +1566,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
prop.setPropertyName(name);
prop.setType(jsValueType());
prop.setIsWritable(true);
- return QQmlJSRegisterContent::create(jsValueType(), prop,
- QQmlJSRegisterContent::JavaScriptObjectProperty,
- jsValueType());
+ return QQmlJSRegisterContent::create(
+ jsValueType(), prop, QQmlJSRegisterContent::InvalidLookupIndex, lookupIndex,
+ QQmlJSRegisterContent::GenericObjectProperty, jsValueType());
}
if (type.isImportNamespace()) {
if (type.scopeType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
@@ -1150,13 +1578,35 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent
return {};
}
- return referenceTypeForName(
+ return registerContentForName(
name, type.scopeType(),
type.variant() == QQmlJSRegisterContent::ObjectModulePrefix);
}
if (type.isConversion()) {
- const auto result = memberType(type.conversionResult(), name);
- return result.isValid() ? result : memberEnumType(type.scopeType(), name);
+ if (const auto result = memberType(
+ type.conversionResult(), name, type.resultLookupIndex(), lookupIndex);
+ result.isValid()) {
+ return result;
+ }
+
+ if (const auto result = memberEnumType(type.scopeType(), name); result.isValid())
+ return result;
+
+ // If the conversion consists of only undefined and one actual type,
+ // we can produce the members of that one type.
+ // If the value is then actually undefined, the result is an exception.
+
+ auto origins = type.conversionOrigins();
+ const auto begin = origins.begin();
+ const auto end = std::remove_if(begin, origins.end(),
+ [this](const QQmlJSScope::ConstPtr &origin) {
+ return equals(origin, m_voidType);
+ });
+
+ // If the conversion cannot hold the original type, it loses information.
+ return (end - begin == 1 && canHold(type.conversionResult(), *begin))
+ ? memberType(*begin, name, type.resultLookupIndex(), lookupIndex)
+ : QQmlJSRegisterContent();
}
Q_UNREACHABLE_RETURN({});
@@ -1167,13 +1617,22 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
QQmlJSScope::ConstPtr scope;
QQmlJSScope::ConstPtr value;
- auto valueType = [this](const QQmlJSScope::ConstPtr &scope) {
+ auto valueType = [&](const QQmlJSScope::ConstPtr &scope) {
if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
return scope->valueType();
- else if (equals(scope, m_jsValueType) || equals(scope, m_varType))
+
+ if (equals(scope, m_forInIteratorPtr))
+ return m_sizeType;
+
+ if (equals(scope, m_forOfIteratorPtr))
+ return list.scopeType()->valueType();
+
+ if (equals(scope, m_jsValueType) || equals(scope, m_varType))
return m_jsValueType;
- else if (equals(scope, m_stringType))
+
+ if (equals(scope, m_stringType))
return m_stringType;
+
return QQmlJSScope::ConstPtr();
};
@@ -1197,7 +1656,8 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent
property.setType(value);
return QQmlJSRegisterContent::create(
- storedType(value), property, QQmlJSRegisterContent::ListValue, scope);
+ storedType(value), property, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListValue, scope);
}
QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
@@ -1206,7 +1666,32 @@ QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
{
Q_ASSERT(variant == QQmlJSRegisterContent::MethodReturnValue
|| variant == QQmlJSRegisterContent::JavaScriptReturnValue);
- return QQmlJSRegisterContent::create(storedType(type), type, variant, scope);
+ return QQmlJSRegisterContent::create(
+ storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex, variant, scope);
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::iteratorPointer(
+ const QQmlJSRegisterContent &listType, QQmlJS::AST::ForEachType type,
+ int lookupIndex) const
+{
+ const QQmlJSScope::ConstPtr value = (type == QQmlJS::AST::ForEachType::In)
+ ? m_int32Type
+ : containedType(valueType(listType));
+
+ QQmlJSScope::ConstPtr iteratorPointer = type == QQmlJS::AST::ForEachType::In
+ ? m_forInIteratorPtr
+ : m_forOfIteratorPtr;
+
+ const QQmlJSScope::ConstPtr listContained = containedType(listType);
+
+ QQmlJSMetaProperty prop;
+ prop.setPropertyName(u"<>"_s);
+ prop.setTypeName(iteratorPointer->internalName());
+ prop.setType(iteratorPointer);
+ return QQmlJSRegisterContent::create(
+ storedType(iteratorPointer), prop, lookupIndex,
+ QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListIterator,
+ listContained);
}
bool QQmlJSTypeResolver::registerIsStoredIn(
@@ -1275,13 +1760,20 @@ QQmlJSRegisterContent QQmlJSTypeResolver::convert(
{
if (from.isConversion()) {
return QQmlJSRegisterContent::create(
- to.storedType(), from.conversionOrigins(), containedType(to), from.variant(),
- from.scopeType());
+ to.storedType(), from.conversionOrigins(), containedType(to),
+ to.scopeType() ? to.scopeType() : from.conversionResultScope(),
+ from.variant(), from.scopeType());
}
return QQmlJSRegisterContent::create(
- to.storedType(), QList<QQmlJSScope::ConstPtr>{containedType(from)},
- containedType(to), from.variant(), from.scopeType());
+ to.storedType(), QList<QQmlJSScope::ConstPtr>{containedType(from)},
+ containedType(to), to.scopeType(), from.variant(), from.scopeType());
+}
+
+QQmlJSRegisterContent QQmlJSTypeResolver::cast(
+ const QQmlJSRegisterContent &from, const QQmlJSScope::ConstPtr &to) const
+{
+ return from.castTo(to).storedIn(storedType(to));
}
QQmlJSScope::ConstPtr QQmlJSTypeResolver::comparableType(const QQmlJSScope::ConstPtr &type) const
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index 6c3583ca73..9961c24842 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -14,20 +14,22 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <memory>
+#include <qtqmlcompilerexports.h>
#include <private/qqmlirbuilder_p.h>
#include <private/qqmljsast_p.h>
#include "qqmljsimporter_p.h"
#include "qqmljslogger_p.h"
#include "qqmljsregistercontent_p.h"
+#include "qqmljsresourcefilemapper_p.h"
#include "qqmljsscope_p.h"
#include "qqmljsscopesbyid_p.h"
QT_BEGIN_NAMESPACE
class QQmlJSImportVisitor;
-class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSTypeResolver
+class Q_QMLCOMPILER_EXPORT QQmlJSTypeResolver
{
public:
enum ParentMode { UseDocumentParent, UseParentProperty };
@@ -41,12 +43,18 @@ public:
QQmlJSScope::ConstPtr voidType() const { return m_voidType; }
QQmlJSScope::ConstPtr emptyType() const { return m_emptyType; }
- QQmlJSScope::ConstPtr emptyListType() const { return m_emptyListType; }
QQmlJSScope::ConstPtr nullType() const { return m_nullType; }
QQmlJSScope::ConstPtr realType() const { return m_realType; }
QQmlJSScope::ConstPtr floatType() const { return m_floatType; }
- QQmlJSScope::ConstPtr intType() const { return m_intType; }
- QQmlJSScope::ConstPtr uintType() const { return m_uintType; }
+ QQmlJSScope::ConstPtr int8Type() const { return m_int8Type; }
+ QQmlJSScope::ConstPtr uint8Type() const { return m_uint8Type; }
+ QQmlJSScope::ConstPtr int16Type() const { return m_int16Type; }
+ QQmlJSScope::ConstPtr uint16Type() const { return m_uint16Type; }
+ QQmlJSScope::ConstPtr int32Type() const { return m_int32Type; }
+ QQmlJSScope::ConstPtr uint32Type() const { return m_uint32Type; }
+ QQmlJSScope::ConstPtr int64Type() const { return m_int64Type; }
+ QQmlJSScope::ConstPtr uint64Type() const { return m_uint64Type; }
+ QQmlJSScope::ConstPtr sizeType() const { return m_sizeType; }
QQmlJSScope::ConstPtr boolType() const { return m_boolType; }
QQmlJSScope::ConstPtr stringType() const { return m_stringType; }
QQmlJSScope::ConstPtr stringListType() const { return m_stringListType; }
@@ -56,6 +64,7 @@ public:
QQmlJSScope::ConstPtr dateType() const { return m_dateType; }
QQmlJSScope::ConstPtr timeType() const { return m_timeType; }
QQmlJSScope::ConstPtr variantListType() const { return m_variantListType; }
+ QQmlJSScope::ConstPtr variantMapType() const { return m_variantMapType; }
QQmlJSScope::ConstPtr varType() const { return m_varType; }
QQmlJSScope::ConstPtr jsValueType() const { return m_jsValueType; }
QQmlJSScope::ConstPtr jsPrimitiveType() const { return m_jsPrimitiveType; }
@@ -63,21 +72,35 @@ public:
QQmlJSScope::ConstPtr metaObjectType() const { return m_metaObjectType; }
QQmlJSScope::ConstPtr functionType() const { return m_functionType; }
QQmlJSScope::ConstPtr jsGlobalObject() const { return m_jsGlobalObject; }
+ QQmlJSScope::ConstPtr qObjectType() const { return m_qObjectType; }
QQmlJSScope::ConstPtr qObjectListType() const { return m_qObjectListType; }
+ QQmlJSScope::ConstPtr arrayPrototype() const { return m_arrayPrototype; }
+ QQmlJSScope::ConstPtr forInIteratorPtr() const { return m_forInIteratorPtr; }
+ QQmlJSScope::ConstPtr forOfIteratorPtr() const { return m_forOfIteratorPtr; }
+
+ QQmlJSScope::ConstPtr mathObject() const;
+ QQmlJSScope::ConstPtr consoleObject() const;
QQmlJSScope::ConstPtr scopeForLocation(const QV4::CompiledData::Location &location) const;
- QQmlJSScope::ConstPtr scopeForId(
- const QString &id, const QQmlJSScope::ConstPtr &referrer) const;
bool isPrefix(const QString &name) const
{
return m_imports.hasType(name) && !m_imports.type(name).scope;
}
+ const QHash<QString, QQmlJS::ImportedScope<QQmlJSScope::ConstPtr>> &importedTypes() const
+ {
+ return m_imports.types();
+ }
QQmlJSScope::ConstPtr typeForName(const QString &name) const
{
return m_imports.type(name).scope;
}
+ QString nameForType(const QQmlJSScope::ConstPtr &type) const
+ {
+ return m_imports.name(originalType(type));
+ }
+
QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const;
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const;
QQmlJSRegisterContent typeForBinaryOperation(QSOperator::Op oper,
@@ -89,6 +112,8 @@ public:
UnaryOperator op, const QQmlJSRegisterContent &operand) const;
bool isPrimitive(const QQmlJSRegisterContent &type) const;
+ bool isPrimitive(const QQmlJSScope::ConstPtr &type) const;
+
bool isNumeric(const QQmlJSRegisterContent &type) const;
bool isIntegral(const QQmlJSRegisterContent &type) const;
@@ -104,13 +129,21 @@ public:
QQmlJSRegisterContent builtinType(const QQmlJSScope::ConstPtr &type) const;
QQmlJSRegisterContent globalType(const QQmlJSScope::ConstPtr &type) const;
- QQmlJSRegisterContent scopedType(const QQmlJSScope::ConstPtr &scope, const QString &name) const;
- QQmlJSRegisterContent memberType(const QQmlJSRegisterContent &type, const QString &name) const;
+ QQmlJSRegisterContent scopedType(const QQmlJSScope::ConstPtr &scope, const QString &name,
+ int lookupIndex = QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSScopesByIdOptions options = Default) const;
+ QQmlJSRegisterContent memberType(
+ const QQmlJSRegisterContent &type, const QString &name,
+ int lookupIndex = QQmlJSRegisterContent::InvalidLookupIndex) const;
QQmlJSRegisterContent valueType(const QQmlJSRegisterContent &list) const;
QQmlJSRegisterContent returnType(
const QQmlJSScope::ConstPtr &type, QQmlJSRegisterContent::ContentVariant variant,
const QQmlJSScope::ConstPtr &scope) const;
+ QQmlJSRegisterContent iteratorPointer(
+ const QQmlJSRegisterContent &listType, QQmlJS::AST::ForEachType type,
+ int lookupIndex) const;
+
bool registerIsStoredIn(const QQmlJSRegisterContent &reg,
const QQmlJSScope::ConstPtr &type) const;
bool registerContains(const QQmlJSRegisterContent &reg,
@@ -125,10 +158,13 @@ public:
QQmlJSScope::ConstPtr trackedContainedType(const QQmlJSRegisterContent &container) const;
QQmlJSScope::ConstPtr originalContainedType(const QQmlJSRegisterContent &container) const;
- void adjustTrackedType(const QQmlJSScope::ConstPtr &tracked,
- const QQmlJSScope::ConstPtr &conversion) const;
- void adjustTrackedType(const QQmlJSScope::ConstPtr &tracked,
- const QList<QQmlJSScope::ConstPtr> &conversions) const;
+ [[nodiscard]] bool adjustTrackedType(
+ const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const;
+ [[nodiscard]] bool adjustTrackedType(
+ const QQmlJSScope::ConstPtr &tracked,
+ const QList<QQmlJSScope::ConstPtr> &conversions) const;
+ void adjustOriginalType(
+ const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const;
void generalizeType(const QQmlJSScope::ConstPtr &type) const;
void setParentMode(ParentMode mode) { m_parentMode = mode; }
@@ -144,7 +180,7 @@ public:
const QQmlJSScopesById &objectsById() const { return m_objectsById; }
bool canCallJSFunctions() const { return m_objectsById.signaturesAreEnforced(); }
- bool canUseValueTypes() const { return m_objectsById.valueTypesAreCopied(); }
+ bool canAddressValueTypes() const { return m_objectsById.valueTypesAreAddressable(); }
const QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> &signalHandlers() const
{
@@ -155,19 +191,47 @@ public:
QQmlJSRegisterContent convert(
const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const;
+ QQmlJSRegisterContent cast(
+ const QQmlJSRegisterContent &from, const QQmlJSScope::ConstPtr &to) const;
QQmlJSScope::ConstPtr merge(const QQmlJSScope::ConstPtr &a,
const QQmlJSScope::ConstPtr &b) const;
bool canHoldUndefined(const QQmlJSRegisterContent &content) const;
+ bool isOptionalType(const QQmlJSRegisterContent &content) const;
+ QQmlJSScope::ConstPtr extractNonVoidFromOptionalType(
+ const QQmlJSRegisterContent &content) const;
+
bool isNumeric(const QQmlJSScope::ConstPtr &type) const;
+ bool isIntegral(const QQmlJSScope::ConstPtr &type) const;
+ bool isSignedInteger(const QQmlJSScope::ConstPtr &type) const;
+ bool isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const;
+ bool isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const;
+
+ bool canHold(const QQmlJSScope::ConstPtr &container,
+ const QQmlJSScope::ConstPtr &contained) const;
+
+ bool canPopulate(
+ const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &argument,
+ bool *isExtension) const;
+
+ QQmlJSMetaMethod selectConstructor(
+ const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &argument,
+ bool *isExtension) const;
+
+ bool areEquivalentLists(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const;
+
+ bool isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const;
+
+ bool inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) const;
protected:
- QQmlJSRegisterContent memberType(const QQmlJSScope::ConstPtr &type, const QString &name) const;
+ QQmlJSRegisterContent memberType(
+ const QQmlJSScope::ConstPtr &type, const QString &name,
+ int baseLookupIndex, int resultLookupIndex) const;
QQmlJSRegisterContent memberEnumType(const QQmlJSScope::ConstPtr &type,
const QString &name) const;
- bool isPrimitive(const QQmlJSScope::ConstPtr &type) const;
bool checkEnums(const QQmlJSScope::ConstPtr &scope, const QString &name,
QQmlJSRegisterContent *result, QQmlJSScope::ExtensionKind mode) const;
bool canPrimitivelyConvertFromTo(
@@ -177,21 +241,28 @@ protected:
const QQmlJSRegisterContent &origin,
QQmlJSScope::ConstPtr (QQmlJSTypeResolver::*op)(const QQmlJSScope::ConstPtr &) const) const;
- QQmlJSRegisterContent referenceTypeForName(
+ QQmlJSRegisterContent registerContentForName(
const QString &name,
const QQmlJSScope::ConstPtr &scopeType = QQmlJSScope::ConstPtr(),
bool hasObjectModuelPrefix = false) const;
+
QQmlJSScope::ConstPtr m_voidType;
- QQmlJSScope::ConstPtr m_emptyListType;
QQmlJSScope::ConstPtr m_emptyType;
QQmlJSScope::ConstPtr m_nullType;
QQmlJSScope::ConstPtr m_numberPrototype;
- QQmlJSScope::ConstPtr m_arrayType;
+ QQmlJSScope::ConstPtr m_arrayPrototype;
QQmlJSScope::ConstPtr m_realType;
QQmlJSScope::ConstPtr m_floatType;
- QQmlJSScope::ConstPtr m_intType;
- QQmlJSScope::ConstPtr m_uintType;
+ QQmlJSScope::ConstPtr m_int8Type;
+ QQmlJSScope::ConstPtr m_uint8Type;
+ QQmlJSScope::ConstPtr m_int16Type;
+ QQmlJSScope::ConstPtr m_uint16Type;
+ QQmlJSScope::ConstPtr m_int32Type;
+ QQmlJSScope::ConstPtr m_uint32Type;
+ QQmlJSScope::ConstPtr m_int64Type;
+ QQmlJSScope::ConstPtr m_uint64Type;
+ QQmlJSScope::ConstPtr m_sizeType;
QQmlJSScope::ConstPtr m_boolType;
QQmlJSScope::ConstPtr m_stringType;
QQmlJSScope::ConstPtr m_stringListType;
@@ -201,14 +272,19 @@ protected:
QQmlJSScope::ConstPtr m_dateType;
QQmlJSScope::ConstPtr m_timeType;
QQmlJSScope::ConstPtr m_variantListType;
+ QQmlJSScope::ConstPtr m_variantMapType;
QQmlJSScope::ConstPtr m_varType;
QQmlJSScope::ConstPtr m_jsValueType;
QQmlJSScope::ConstPtr m_jsPrimitiveType;
QQmlJSScope::ConstPtr m_listPropertyType;
+ QQmlJSScope::ConstPtr m_qObjectType;
QQmlJSScope::ConstPtr m_qObjectListType;
+ QQmlJSScope::ConstPtr m_qQmlScriptStringType;
QQmlJSScope::ConstPtr m_metaObjectType;
QQmlJSScope::ConstPtr m_functionType;
QQmlJSScope::ConstPtr m_jsGlobalObject;
+ QQmlJSScope::ConstPtr m_forInIteratorPtr;
+ QQmlJSScope::ConstPtr m_forOfIteratorPtr;
QQmlJSScopesById m_objectsById;
QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> m_objectsByLocation;
@@ -235,6 +311,19 @@ protected:
std::unique_ptr<QHash<QQmlJSScope::ConstPtr, TrackedType>> m_trackedTypes;
};
+/*!
+\internal
+
+QQmlJSTypeResolver expects to be outlived by its importer and mapper. It crashes when its importer
+or mapper gets destructed. Therefore, you can use this struct to extend the lifetime of its
+dependencies in case you need to store the resolver as a class member.
+*/
+struct QQmlJSTypeResolverDependencies
+{
+ std::shared_ptr<QQmlJSImporter> importer;
+ std::shared_ptr<QQmlJSResourceFileMapper> mapper;
+};
+
QT_END_NAMESPACE
#endif // QQMLJSTYPERESOLVER_P_H
diff --git a/src/qmlcompiler/qqmljsutils.cpp b/src/qmlcompiler/qqmljsutils.cpp
index 410f6b0d14..c6eb09b313 100644
--- a/src/qmlcompiler/qqmljsutils.cpp
+++ b/src/qmlcompiler/qqmljsutils.cpp
@@ -80,7 +80,10 @@ QQmlJSUtils::ResolvedAlias QQmlJSUtils::resolveAlias(const QQmlJSTypeResolver *t
{
return ::resolveAlias(
[&](const QString &id, const QQmlJSScope::ConstPtr &referrer) {
- return typeResolver->scopeForId(id, referrer);
+ const QQmlJSRegisterContent content = typeResolver->scopedType(referrer, id);
+ if (content.variant() == QQmlJSRegisterContent::ObjectById)
+ return content.type();
+ return QQmlJSScope::ConstPtr();
},
property, owner, visitor);
}
@@ -97,7 +100,7 @@ QQmlJSUtils::ResolvedAlias QQmlJSUtils::resolveAlias(const QQmlJSScopesById &idS
property, owner, visitor);
}
-std::optional<FixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput,
+std::optional<QQmlJSFixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput,
QStringList candidates,
QQmlJS::SourceLocation location)
{
@@ -144,10 +147,12 @@ std::optional<FixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput,
}
if (shortestDistance
- < std::min(std::max(userInput.size() / 2, qsizetype(3)), userInput.size())) {
- return FixSuggestion { { FixSuggestion::Fix {
- u"Did you mean \"%1\"?"_s.arg(shortestDistanceWord), location,
- shortestDistanceWord } } };
+ < std::min(std::max(userInput.size() / 2, qsizetype(3)), userInput.size())) {
+ return QQmlJSFixSuggestion {
+ u"Did you mean \"%1\"?"_s.arg(shortestDistanceWord),
+ location,
+ shortestDistanceWord
+ };
} else {
return {};
}
@@ -202,24 +207,16 @@ QQmlJSUtils::sourceDirectoryPath(const QQmlJSImporter *importer, const QString &
Utility method that checks if one of the registers is var, and the other can be
efficiently compared to it
*/
-bool canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver,
- const QQmlJSRegisterContent &lhsContent,
- const QQmlJSRegisterContent &rhsContent)
+bool canStrictlyCompareWithVar(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType)
{
Q_ASSERT(typeResolver);
- const auto varType = typeResolver->varType();
- const auto nullType = typeResolver->nullType();
- const auto voidType = typeResolver->voidType();
-
- // Use containedType() because nullptr is not a stored type.
- const auto lhsType = typeResolver->containedType(lhsContent);
- const auto rhsType = typeResolver->containedType(rhsContent);
-
- return (typeResolver->equals(lhsType, varType)
- && (typeResolver->equals(rhsType, nullType) || typeResolver->equals(rhsType, voidType)))
- || (typeResolver->equals(rhsType, varType)
- && (typeResolver->equals(lhsType, nullType)
- || typeResolver->equals(lhsType, voidType)));
+
+ const QQmlJSScope::ConstPtr varType = typeResolver->varType();
+ const bool leftIsVar = typeResolver->equals(lhsType, varType);
+ const bool righttIsVar = typeResolver->equals(rhsType, varType);
+ return leftIsVar != righttIsVar;
}
/*! \internal
@@ -227,13 +224,11 @@ bool canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver,
Utility method that checks if one of the registers is qobject, and the other can be
efficiently compared to it
*/
-bool canCompareWithQObject(const QQmlJSTypeResolver *typeResolver,
- const QQmlJSRegisterContent &lhsContent,
- const QQmlJSRegisterContent &rhsContent)
+bool canCompareWithQObject(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType)
{
Q_ASSERT(typeResolver);
- const auto lhsType = typeResolver->containedType(lhsContent);
- const auto rhsType = typeResolver->containedType(rhsContent);
return (lhsType->isReferenceType()
&& (rhsType->isReferenceType()
|| typeResolver->equals(rhsType, typeResolver->nullType())))
@@ -242,4 +237,18 @@ bool canCompareWithQObject(const QQmlJSTypeResolver *typeResolver,
|| typeResolver->equals(lhsType, typeResolver->nullType())));
}
+/*! \internal
+
+ Utility method that checks if both sides are QUrl type. In future, that might be extended to
+ support comparison with other types i.e QUrl vs string
+*/
+bool canCompareWithQUrl(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType)
+{
+ Q_ASSERT(typeResolver);
+ return typeResolver->equals(lhsType, typeResolver->urlType())
+ && typeResolver->equals(rhsType, typeResolver->urlType());
+}
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsutils_p.h b/src/qmlcompiler/qqmljsutils_p.h
index 15425d0cab..eaa834bec9 100644
--- a/src/qmlcompiler/qqmljsutils_p.h
+++ b/src/qmlcompiler/qqmljsutils_p.h
@@ -14,7 +14,7 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include "qqmljslogger_p.h"
#include "qqmljsregistercontent_p.h"
@@ -25,6 +25,7 @@
#include <QtCore/qstring.h>
#include <QtCore/qstringview.h>
#include <QtCore/qstringbuilder.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <private/qduplicatetracker_p.h>
#include <optional>
@@ -64,7 +65,7 @@ static auto getQQmlJSScopeFromSmartPtr(const From &p) -> decltype(p.get())
class QQmlJSTypeResolver;
class QQmlJSScopesById;
-struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSUtils
+struct Q_QMLCOMPILER_EXPORT QQmlJSUtils
{
/*! \internal
Returns escaped version of \a s. This function is mostly useful for code
@@ -73,7 +74,10 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSUtils
static QString escapeString(QString s)
{
using namespace Qt::StringLiterals;
- return s.replace(u'\\', u"\\\\"_s).replace(u'"', u"\\\""_s).replace(u'\n', u"\\n"_s);
+ return s.replace('\\'_L1, "\\\\"_L1)
+ .replace('"'_L1, "\\\""_L1)
+ .replace('\n'_L1, "\\n"_L1)
+ .replace('?'_L1, "\\?"_L1);
}
/*! \internal
@@ -99,26 +103,6 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSUtils
return type;
}
- /*! \internal
- Returns a signal name from \a handlerName string.
- */
- static std::optional<QString> signalName(QStringView handlerName)
- {
- if (handlerName.startsWith(u"on") && handlerName.size() > 2) {
- QString signal = handlerName.mid(2).toString();
- for (int i = 0; i < signal.size(); ++i) {
- QChar &ch = signal[i];
- if (ch.isLower())
- return {};
- if (ch.isUpper()) {
- ch = ch.toLower();
- return signal;
- }
- }
- }
- return {};
- }
-
static std::optional<QQmlJSMetaProperty>
changeHandlerProperty(const QQmlJSScope::ConstPtr &scope, QStringView signalName)
{
@@ -134,6 +118,21 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSUtils
return {};
}
+ static std::optional<QQmlJSMetaProperty>
+ propertyFromChangedHandler(const QQmlJSScope::ConstPtr &scope, QStringView changedHandler)
+ {
+ auto signalName = QQmlSignalNames::changedHandlerNameToPropertyName(changedHandler);
+ if (!signalName)
+ return {};
+
+ auto p = scope->property(*signalName);
+ const bool isBindable = !p.bindable().isEmpty();
+ const bool canNotify = !p.notify().isEmpty();
+ if (p.isValid() && (isBindable || canNotify))
+ return p;
+ return {};
+ }
+
static bool hasCompositeBase(const QQmlJSScope::ConstPtr &scope)
{
if (!scope)
@@ -141,7 +140,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSUtils
const auto base = scope->baseType();
if (!base)
return false;
- return base->isComposite() && base->scopeType() == QQmlJSScope::QMLScope;
+ return base->isComposite() && base->scopeType() == QQmlSA::ScopeType::QMLScope;
}
enum PropertyAccessor {
@@ -359,21 +358,33 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSUtils
act(begin->scope, begin->extensionSpecifier);
}
- static std::optional<FixSuggestion> didYouMean(const QString &userInput,
+ static std::optional<QQmlJSFixSuggestion> didYouMean(const QString &userInput,
QStringList candidates,
QQmlJS::SourceLocation location);
static std::variant<QString, QQmlJS::DiagnosticMessage>
sourceDirectoryPath(const QQmlJSImporter *importer, const QString &buildDirectoryPath);
+
+ template <typename Container>
+ static void deduplicate(Container &container)
+ {
+ std::sort(container.begin(), container.end());
+ auto erase = std::unique(container.begin(), container.end());
+ container.erase(erase, container.end());
+ }
};
-bool Q_QMLCOMPILER_PRIVATE_EXPORT canStrictlyCompareWithVar(
- const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent,
- const QQmlJSRegisterContent &rhsContent);
+bool Q_QMLCOMPILER_EXPORT canStrictlyCompareWithVar(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType);
+
+bool Q_QMLCOMPILER_EXPORT canCompareWithQObject(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType);
-bool Q_QMLCOMPILER_PRIVATE_EXPORT canCompareWithQObject(const QQmlJSTypeResolver *typeResolver,
- const QQmlJSRegisterContent &lhsContent,
- const QQmlJSRegisterContent &rhsContent);
+bool Q_QMLCOMPILER_EXPORT canCompareWithQUrl(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType);
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsvaluetypefromstringcheck.cpp b/src/qmlcompiler/qqmljsvaluetypefromstringcheck.cpp
new file mode 100644
index 0000000000..40ca97e579
--- /dev/null
+++ b/src/qmlcompiler/qqmljsvaluetypefromstringcheck.cpp
@@ -0,0 +1,63 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmljsvaluetypefromstringcheck_p.h"
+#include <private/qqmlstringconverters_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+/*!
+\internal
+\class ValueTypeFromStringError
+
+Helps to differentiate between three states:
+\list
+\li a value type was constructed by a string in a deprecated way, e.g. "50x70" for a QSizeF
+\li a value type was constructed by an invalid string in a deprecated way, e.g. "50,70" for a QSizeF
+\li a value type was constructed by a string in a non-deprecated way.
+\endlist
+*/
+
+/*!
+\internal
+Checks if known value types are being constructed in a deprecated way, and constructs the code for
+the fix-it.
+*/
+QQmlJSStructuredTypeError QQmlJSValueTypeFromStringCheck::hasError(const QString &typeName,
+ const QString &value)
+{
+ if (typeName == u"QPointF" || typeName == u"QPoint") {
+ std::array<double, 2> numbers;
+ if (!QQmlStringConverters::isValidNumberString<2, u','>(value, &numbers))
+ return QQmlJSStructuredTypeError::withInvalidString();
+
+ const auto result = QQmlJSStructuredTypeError::fromSuggestedString(
+ u"({ x: %1, y: %2 })"_s.arg(numbers[0]).arg(numbers[1]));
+ return result;
+ } else if (typeName == u"QSizeF" || typeName == u"QSize") {
+ std::array<double, 2> numbers;
+ if (!QQmlStringConverters::isValidNumberString<2, u'x'>(value, &numbers))
+ return QQmlJSStructuredTypeError::withInvalidString();
+
+ const auto result = QQmlJSStructuredTypeError::fromSuggestedString(
+ u"({ width: %1, height: %2 })"_s.arg(numbers[0]).arg(numbers[1]));
+ return result;
+ } else if (typeName == u"QRectF" || typeName == u"QRect") {
+ std::array<double, 4> numbers;
+ if (!QQmlStringConverters::isValidNumberString<4, u',', u',', u'x'>(value, &numbers))
+ return QQmlJSStructuredTypeError::withInvalidString();
+
+ const auto result = QQmlJSStructuredTypeError::fromSuggestedString(
+ u"({ x: %1, y: %2, width: %3, height: %4 })"_s.arg(numbers[0])
+ .arg(numbers[1])
+ .arg(numbers[2])
+ .arg(numbers[3]));
+ return result;
+ }
+
+ return QQmlJSStructuredTypeError::withValidString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsvaluetypefromstringcheck_p.h b/src/qmlcompiler/qqmljsvaluetypefromstringcheck_p.h
new file mode 100644
index 0000000000..227a75003f
--- /dev/null
+++ b/src/qmlcompiler/qqmljsvaluetypefromstringcheck_p.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLJSVALUETYPEFROMSTRINGCHECK_H
+#define QQMLJSVALUETYPEFROMSTRINGCHECK_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+
+#include <qtqmlcompilerexports.h>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_QMLCOMPILER_EXPORT QQmlJSStructuredTypeError
+{
+ QString code;
+ bool constructedFromInvalidString = false;
+
+ bool isValid() const { return !code.isEmpty() || constructedFromInvalidString; }
+ static QQmlJSStructuredTypeError withInvalidString() { return { QString(), true }; }
+ static QQmlJSStructuredTypeError withValidString() { return { QString(), false }; }
+ static QQmlJSStructuredTypeError fromSuggestedString(const QString &enhancedString)
+ {
+ return { enhancedString, false };
+ }
+};
+
+
+class Q_QMLCOMPILER_EXPORT QQmlJSValueTypeFromStringCheck
+{
+public:
+ static QQmlJSStructuredTypeError hasError(const QString &typeName, const QString &value);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSVALUETYPEFROMSTRINGCHECK_H
diff --git a/src/qmlcompiler/qqmlsa.cpp b/src/qmlcompiler/qqmlsa.cpp
index 0b6e88ef7c..d2d0e6a74e 100644
--- a/src/qmlcompiler/qqmlsa.cpp
+++ b/src/qmlcompiler/qqmlsa.cpp
@@ -1,15 +1,21 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "qqmlsa.h"
#include "qqmlsa_p.h"
+#include "qqmlsasourcelocation.h"
#include "qqmljsscope_p.h"
#include "qqmljslogger_p.h"
#include "qqmljstyperesolver_p.h"
#include "qqmljsimportvisitor_p.h"
#include "qqmljsutils_p.h"
+#include "qdeferredpointer_p.h"
+
+#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h>
#include <memory>
+#include <new>
QT_BEGIN_NAMESPACE
@@ -17,44 +23,1043 @@ using namespace Qt::StringLiterals;
namespace QQmlSA {
+static_assert(QQmlJSScope::sizeofQQmlSAElement() == sizeof(Element));
+
+/*!
+ \namespace QQmlSA
+ \inmodule QtQmlCompiler
+
+ \brief Provides tools for static analysis on QML programs.
+ */
+
+/*!
+ \class QQmlSA::Binding::Bindings
+ \inmodule QtQmlCompiler
+
+ \brief Holds multiple property name to property binding associations.
+ */
+
+Binding::Bindings::Bindings() : d_ptr{ new BindingsPrivate{ this } } { }
+
+BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface) : q_ptr{ interface } { }
+
+Binding::Bindings::Bindings(const Bindings &other)
+ : d_ptr{ new BindingsPrivate{ this, *other.d_func() } }
+{
+}
+
+Binding::Bindings::~Bindings() = default;
+
+BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, const BindingsPrivate &other)
+ : m_bindings{ other.m_bindings.begin(), other.m_bindings.end() }, q_ptr{ interface }
+{
+}
+
+BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, BindingsPrivate &&other)
+ : m_bindings{ std::move(other.m_bindings) }, q_ptr{ interface }
+{
+}
+
+/*!
+ Returns an iterator to the beginning of the bindings.
+ */
+QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constBegin() const
+{
+ Q_D(const Bindings);
+ return d->constBegin();
+}
+
+QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constBegin() const
+{
+ return m_bindings.constBegin();
+}
+
+/*!
+ Returns an iterator to the end of the bindings.
+ */
+QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constEnd() const
+{
+ Q_D(const Bindings);
+ return d->constEnd();
+}
+
+QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constEnd() const
+{
+ return m_bindings.constEnd();
+}
+
+/*!
+ \class QQmlSA::Binding
+ \inmodule QtQmlCompiler
+
+ \brief Represents a single QML property binding for a specific type.
+ */
+
+Binding::Binding() : d_ptr{ new BindingPrivate{ this } } { }
+
+BindingPrivate::BindingPrivate(Binding *interface) : q_ptr{ interface } { }
+
+Binding::Binding(const Binding &other) : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
+
+Binding::Binding(Binding &&other) noexcept
+ : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { }
+
+Binding &Binding::operator=(const Binding &other)
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_binding = other.d_func()->m_binding;
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Binding &Binding::operator=(Binding &&other) noexcept
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_binding = std::move(other.d_func()->m_binding);
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Binding::~Binding() = default;
+
+bool Binding::operatorEqualsImpl(const Binding &lhs, const Binding &rhs)
+{
+ return lhs.d_func()->m_binding == rhs.d_func()->m_binding;
+}
+
+BindingPrivate::BindingPrivate(Binding *interface, const BindingPrivate &other)
+ : m_binding{ other.m_binding }, q_ptr{ interface }
+{
+}
+
+QQmlSA::Binding BindingPrivate::createBinding(const QQmlJSMetaPropertyBinding &binding)
+{
+ QQmlSA::Binding saBinding;
+ saBinding.d_func()->m_binding = binding;
+ return saBinding;
+}
+
+QQmlJSMetaPropertyBinding BindingPrivate::binding(QQmlSA::Binding &binding)
+{
+ return binding.d_func()->m_binding;
+}
+
+const QQmlJSMetaPropertyBinding BindingPrivate::binding(const QQmlSA::Binding &binding)
+{
+ return binding.d_func()->m_binding;
+}
+
+/*!
+ Returns the type of the property of this binding if it is a group property,
+ otherwise returns an invalid Element.
+ */
+Element Binding::groupType() const
+{
+ return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).groupType());
+}
+
+QQmlSA::BindingType Binding::bindingType() const
+{
+ return BindingPrivate::binding(*this).bindingType();
+}
+
+/*!
+ Returns the associated string literal if the content type of this binding is
+ StringLiteral, otherwise returns an empty string.
+ */
+QString Binding::stringValue() const
+{
+ return BindingPrivate::binding(*this).stringValue();
+}
+
+/*!
+ Returns the name of the property bound with this binding.
+ */
+QString Binding::propertyName() const
+{
+ return BindingPrivate::binding(*this).propertyName();
+}
+
+/*!
+ Returns the attached type if the content type of this binding is
+ AttachedProperty, otherwise returns an invalid Element.
+ */
+Element Binding::attachingType() const
+{
+ return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).attachingType());
+}
+
+/*!
+ Returns the location in the QML code where this binding is defined.
+ */
+QQmlSA::SourceLocation Binding::sourceLocation() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ BindingPrivate::binding(*this).sourceLocation());
+}
+
+/*!
+ Returns the associated number if the content type of this binding is
+ NumberLiteral, otherwise returns 0.
+ */
+double Binding::numberValue() const
+{
+ return BindingPrivate::binding(*this).numberValue();
+}
+
+/*!
+ Returns the kind of the associated script if the content type of this
+ binding is Script, otherwise returns Script_Invalid.
+ */
+QQmlSA::ScriptBindingKind Binding::scriptKind() const
+{
+ return BindingPrivate::binding(*this).scriptKind();
+}
+
+/*!
+ Returns \c true if this binding has an objects, otherwise returns \c false.
+ */
+bool Binding::hasObject() const
+{
+ return BindingPrivate::binding(*this).hasObject();
+}
+
+/*!
+ Returns the type of the associated object if the content type of this
+ binding is Object, otherwise returns an invalid Element.
+ */
+QQmlSA::Element Binding::objectType() const
+{
+ return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).objectType());
+}
+
+bool Binding::hasUndefinedScriptValue() const
+{
+ const auto &jsBinding = BindingPrivate::binding(*this);
+ return jsBinding.bindingType() == BindingType::Script
+ && jsBinding.scriptValueType() == ScriptValue_Undefined;
+}
+
+/*!
+ Returns \c true if \a bindingType is a literal type, and \c false
+ otherwise. Literal types include strings, booleans, numbers, regular
+ expressions.
+ */
+bool QQmlSA::Binding::isLiteralBinding(QQmlSA::BindingType bindingType)
+{
+ return QQmlJSMetaPropertyBinding::isLiteralBinding(bindingType);
+}
+
+QQmlSA::Method::Methods::Methods() : d_ptr{ new MethodsPrivate{ this } } { }
+
+QQmlSA::Method::Methods::Methods(const Methods &other)
+ : d_ptr{ new MethodsPrivate{ this, *other.d_func() } }
+{
+}
+
+QQmlSA::Method::Methods::~Methods() = default;
+
+/*!
+ Returns an iterator to the beginning of the methods.
+ */
+QMultiHash<QString, Method>::const_iterator Method::Methods::constBegin() const
+{
+ Q_D(const Methods);
+ return d->constBegin();
+}
+
+QMultiHash<QString, Method>::const_iterator MethodsPrivate::constBegin() const
+{
+ return m_methods.constBegin();
+}
+
+/*!
+ Returns an iterator to the end of the methods.
+ */
+QMultiHash<QString, Method>::const_iterator Method::Methods::constEnd() const
+{
+ Q_D(const Methods);
+ return d->constEnd();
+}
+QMultiHash<QString, Method>::const_iterator MethodsPrivate::constEnd() const
+{
+ return m_methods.constEnd();
+}
+
+MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface) : q_ptr{ interface } { }
+
+MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, const MethodsPrivate &other)
+ : m_methods{ other.m_methods }, q_ptr{ interface }
+{
+}
+
+MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, MethodsPrivate &&other)
+ : m_methods{ std::move(other.m_methods) }, q_ptr{ interface }
+{
+}
+
+MethodPrivate::MethodPrivate(Method *interface) : q_ptr{ interface } { }
+
+MethodPrivate::MethodPrivate(Method *interface, const MethodPrivate &other)
+ : m_method{ other.m_method }, q_ptr{ interface }
+{
+}
+
+QString MethodPrivate::methodName() const
+{
+ return m_method.methodName();
+}
+
+QQmlSA::SourceLocation MethodPrivate::sourceLocation() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(m_method.sourceLocation());
+}
+
+MethodType MethodPrivate::methodType() const
+{
+ return m_method.methodType();
+}
+
+/*!
+ \class QQmlSA::Method
+ \inmodule QtQmlCompiler
+
+ \brief Represents a QML method.
+ */
+
+Method::Method() : d_ptr{ new MethodPrivate{ this } } { }
+
+Method::Method(const Method &other) : d_ptr{ new MethodPrivate{ this, *other.d_func() } } { }
+
+Method::Method(Method &&other) noexcept
+ : d_ptr{ new MethodPrivate{ this, std::move(*other.d_func()) } }
+{
+}
+
+Method &Method::operator=(const Method &other)
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_method = other.d_func()->m_method;
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Method &Method::operator=(Method &&other) noexcept
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_method = std::move(other.d_func()->m_method);
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Method::~Method() = default;
+
+/*!
+ Returns the name of the this method.
+ */
+QString Method::methodName() const
+{
+ Q_D(const Method);
+ return d->methodName();
+}
+
+/*!
+ Returns the type of this method. For example, Signal, Slot, Method or
+ StaticMethod.
+ */
+MethodType Method::methodType() const
+{
+ Q_D(const Method);
+ return d->methodType();
+}
+
+/*!
+ Returns the location in the QML code where this method is defined.
+ */
+QQmlSA::SourceLocation Method::sourceLocation() const
+{
+ Q_D(const Method);
+ return d->sourceLocation();
+}
+
+bool Method::operatorEqualsImpl(const Method &lhs, const Method &rhs)
+{
+ return lhs.d_func()->m_method == rhs.d_func()->m_method;
+}
+
+QQmlSA::Method MethodPrivate::createMethod(const QQmlJSMetaMethod &jsMethod)
+{
+ QQmlSA::Method saMethod;
+ auto &wrappedMethod = saMethod.d_func()->m_method;
+ wrappedMethod = jsMethod;
+ return saMethod;
+}
+
+QQmlSA::Method::Methods
+MethodsPrivate::createMethods(const QMultiHash<QString, QQmlJSMetaMethod> &hash)
+{
+ QMultiHash<QString, QQmlSA::Method> saMethods;
+ for (const auto &[key, value] : hash.asKeyValueRange()) {
+ saMethods.insert(key, MethodPrivate::createMethod(value));
+ }
+
+ QQmlSA::Method::Methods methods;
+ methods.d_func()->m_methods = std::move(saMethods);
+ return methods;
+}
+
+QQmlJSMetaMethod MethodPrivate::method(const QQmlSA::Method &method)
+{
+ return method.d_func()->m_method;
+}
+
+PropertyPrivate::PropertyPrivate(Property *interface) : q_ptr{ interface } { }
+
+PropertyPrivate::PropertyPrivate(Property *interface, const PropertyPrivate &other)
+ : m_property{ other.m_property }, q_ptr{ interface }
+{
+}
+
+PropertyPrivate::PropertyPrivate(Property *interface, PropertyPrivate &&other)
+ : m_property{ std::move(other.m_property) }, q_ptr{ interface }
+{
+}
+
+QString PropertyPrivate::typeName() const
+{
+ return m_property.typeName();
+}
+
+bool PropertyPrivate::isValid() const
+{
+ return m_property.isValid();
+}
+
+/*!
+ Returns whether this property is readonly. Properties defined in QML are readonly when their
+ definition has the 'readonly' keyword. Properties defined in C++ are readonly when they do not
+ have a WRITE accessor function.
+ */
+bool PropertyPrivate::isReadonly() const
+{
+ return !m_property.isWritable();
+}
+
+/*!
+ Returns the type that this property was defined with.
+ */
+QQmlSA::Element PropertyPrivate::type() const
+{
+ return QQmlJSScope::createQQmlSAElement(m_property.type());
+}
+
+QQmlJSMetaProperty PropertyPrivate::property(const QQmlSA::Property &property)
+{
+ return property.d_func()->m_property;
+}
+
+QQmlSA::Property PropertyPrivate::createProperty(const QQmlJSMetaProperty &property)
+{
+ QQmlSA::Property saProperty;
+ auto &wrappedProperty = saProperty.d_func()->m_property;
+ wrappedProperty = property;
+ return saProperty;
+}
+
+/*!
+ \class QQmlSA::Property
+ \inmodule QtQmlCompiler
+
+ \brief Represents a QML property.
+ */
+
+Property::Property() : d_ptr{ new PropertyPrivate{ this } } { }
+
+Property::Property(const Property &other)
+ : d_ptr{ new PropertyPrivate{ this, *other.d_func() } } { }
+
+Property::Property(Property &&other) noexcept
+ : d_ptr{ new PropertyPrivate{ this, std::move(*other.d_func()) } }
+{
+}
+
+Property &Property::operator=(const Property &other)
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_property = other.d_func()->m_property;
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Property &Property::operator=(Property &&other) noexcept
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_property = std::move(other.d_func()->m_property);
+ d_func()->q_ptr = this;
+ return *this;
+}
+
+Property::~Property() = default;
+
+/*!
+ Returns the name of the type of this property.
+ */
+QString Property::typeName() const
+{
+ Q_D(const Property);
+ return d->typeName();
+}
+
+bool Property::isValid() const
+{
+ Q_D(const Property);
+ return d->isValid();
+}
+
+bool Property::isReadonly() const
+{
+ Q_D(const Property);
+ return d->isReadonly();
+}
+
+QQmlSA::Element Property::type() const
+{
+ Q_D(const Property);
+ return d->type();
+}
+
+
+bool Property::operatorEqualsImpl(const Property &lhs, const Property &rhs)
+{
+ return lhs.d_func()->m_property == rhs.d_func()->m_property;
+}
+
+/*!
+ \class QQmlSA::Element
+ \inmodule QtQmlCompiler
+
+ \brief Represents a QML type.
+ */
+
+Element::Element()
+{
+ new (m_data) QQmlJSScope::ConstPtr();
+}
+
+Element::Element(const Element &other)
+{
+ new (m_data) QQmlJSScope::ConstPtr(QQmlJSScope::scope(other));
+}
+
+Element &Element::operator=(const Element &other)
+{
+ if (this == &other)
+ return *this;
+
+ *reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data) = QQmlJSScope::scope(other);
+ return *this;
+}
+
+Element::~Element()
+{
+ (*reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data)).QQmlJSScope::ConstPtr::~ConstPtr();
+}
+
+/*!
+ Returns the type of Element's scope.
+ */
+QQmlJSScope::ScopeType Element::scopeType() const
+{
+ return QQmlJSScope::scope(*this)->scopeType();
+}
+
+/*!
+ Returns the Element this Element derives from.
+ */
+Element Element::baseType() const
+{
+ return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->baseType());
+}
+
+/*!
+ Returns the name of the Element this Element derives from.
+ */
+QString Element::baseTypeName() const
+{
+ return QQmlJSScope::prettyName(QQmlJSScope::scope(*this)->baseTypeName());
+}
+
+/*!
+ Returns the Element that encloses this Element.
+ */
+Element Element::parentScope() const
+{
+ return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->parentScope());
+}
+
+/*!
+ Returns whether this Element inherits from \a element.
+ */
+bool Element::inherits(const Element &element) const
+{
+ return QQmlJSScope::scope(*this)->inherits(QQmlJSScope::scope(element));
+}
+
+bool Element::isNull() const
+{
+ return QQmlJSScope::scope(*this).isNull();
+}
+
+/*!
+ \internal
+ */
+QString Element::internalId() const
+{
+ return QQmlJSScope::scope(*this)->internalName();
+}
+
+/*!
+ Returns the access semantics of this Element. For example, Reference,
+ Value or Sequence.
+ */
+AccessSemantics Element::accessSemantics() const
+{
+ return QQmlJSScope::scope(*this)->accessSemantics();
+}
+
+/*!
+ Returns true for objects defined from Qml, and false for objects declared from C++.
+ */
+bool QQmlSA::Element::isComposite() const
+{
+ return QQmlJSScope::scope(*this)->isComposite();
+}
+
+/*!
+ Returns whether this Element has a property with the name \a propertyName.
+ */
+bool Element::hasProperty(const QString &propertyName) const
+{
+ return QQmlJSScope::scope(*this)->hasProperty(propertyName);
+}
+
+/*!
+ Returns whether this Element defines a property with the name \a propertyName
+ which is not defined on its base or extension objects.
+ */
+bool Element::hasOwnProperty(const QString &propertyName) const
+{
+ return QQmlJSScope::scope(*this)->hasOwnProperty(propertyName);
+}
+
+/*!
+ Returns the property with the name \a propertyName if it is found in this
+ Element or its base and extension objects, otherwise returns an invalid property.
+ */
+QQmlSA::Property Element::property(const QString &propertyName) const
+{
+ return PropertyPrivate::createProperty(QQmlJSScope::scope(*this)->property(propertyName));
+}
+
+/*!
+ Returns whether the property with the name \a propertyName resolved on this
+ Element is required. Returns false if the the property couldn't be found.
+ */
+bool Element::isPropertyRequired(const QString &propertyName) const
+{
+ return QQmlJSScope::scope(*this)->isPropertyRequired(propertyName);
+}
+
+/*!
+ Returns the name of the default property of this Element. If it doesn't
+ have one, returns an empty string.
+ */
+QString Element::defaultPropertyName() const
+{
+ return QQmlJSScope::scope(*this)->defaultPropertyName();
+}
+
+/*!
+ Returns whether this Element has a method with the name \a methodName.
+ */
+bool Element::hasMethod(const QString &methodName) const
+{
+ return QQmlJSScope::scope(*this)->hasMethod(methodName);
+}
+
+/*!
+ \class QQmlSA::Method::Methods
+ \inmodule QtQmlCompiler
+
+ \brief Holds multiple method name to method associations.
+ */
+
+/*!
+ Returns this Elements's methods, which are not defined on its base or
+ extension objects.
+ */
+Method::Methods Element::ownMethods() const
+{
+ return MethodsPrivate::createMethods(QQmlJSScope::scope(*this)->ownMethods());
+}
+
+/*!
+ Returns the location in the QML code where this Element is defined.
+ */
+QQmlSA::SourceLocation Element::sourceLocation() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ QQmlJSScope::scope(*this)->sourceLocation());
+}
+
+/*!
+ Returns the file path of the QML code that defines this Element.
+ */
+QString Element::filePath() const
+{
+ return QQmlJSScope::scope(*this)->filePath();
+}
+
+/*!
+ Returns whether this Element has a property binding with the name \a name.
+ */
+bool Element::hasPropertyBindings(const QString &name) const
+{
+ return QQmlJSScope::scope(*this)->hasPropertyBindings(name);
+}
+
+/*!
+ Returns whether this Element has property bindings which are not defined in
+ its base or extension objects and that have name \a propertyName.
+ */
+bool Element::hasOwnPropertyBindings(const QString &propertyName) const
+{
+ return QQmlJSScope::scope(*this)->hasOwnPropertyBindings(propertyName);
+}
+
+/*!
+ Returns this Element's property bindings which are not defined on its base
+ or extension objects.
+ */
+Binding::Bindings Element::ownPropertyBindings() const
+{
+ return BindingsPrivate::createBindings(QQmlJSScope::scope(*this)->ownPropertyBindings());
+}
+
+/*!
+ Returns this Element's property bindings which are not defined on its base
+ or extension objects and that have the name \a propertyName.
+ */
+Binding::Bindings Element::ownPropertyBindings(const QString &propertyName) const
+{
+ return BindingsPrivate::createBindings(
+ QQmlJSScope::scope(*this)->ownPropertyBindings(propertyName));
+}
+
+/*!
+ Returns this Element's property bindings that have the name \a propertyName.
+ */
+QList<Binding> Element::propertyBindings(const QString &propertyName) const
+{
+ const auto &bindings = QQmlJSScope::scope(*this)->propertyBindings(propertyName);
+
+ QList<Binding> saBindings;
+ for (const auto &jsBinding : bindings) {
+ saBindings.push_back(BindingPrivate::createBinding(jsBinding));
+ }
+ return saBindings;
+}
+
+QQmlSA::Binding::Bindings
+BindingsPrivate::createBindings(const QMultiHash<QString, QQmlJSMetaPropertyBinding> &hash)
+{
+ QMultiHash<QString, QQmlSA::Binding> saBindings;
+ for (const auto &[key, value] : hash.asKeyValueRange()) {
+ saBindings.insert(key, BindingPrivate::createBinding(value));
+ }
+
+ QQmlSA::Binding::Bindings bindings;
+ bindings.d_func()->m_bindings = std::move(saBindings);
+ return bindings;
+}
+
+QQmlSA::Binding::Bindings BindingsPrivate::createBindings(
+ QPair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator,
+ QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator> iterators)
+{
+ QMultiHash<QString, QQmlSA::Binding> saBindings;
+ for (auto it = iterators.first; it != iterators.second; ++it) {
+ saBindings.insert(it.key(), BindingPrivate::createBinding(it.value()));
+ }
+
+ QQmlSA::Binding::Bindings bindings;
+ bindings.d_func()->m_bindings = std::move(saBindings);
+ return bindings;
+}
+
+Element::operator bool() const
+{
+ return bool(QQmlJSScope::scope(*this));
+}
+
+bool Element::operator!() const
+{
+ return !QQmlJSScope::scope(*this);
+}
+
+/*!
+ Returns the name of this Element.
+ */
+QString Element::name() const
+{
+ if (isNull())
+ return {};
+ return QQmlJSScope::prettyName(QQmlJSScope::scope(*this)->internalName());
+}
+
+bool Element::operatorEqualsImpl(const Element &lhs, const Element &rhs)
+{
+ return QQmlJSScope::scope(lhs) == QQmlJSScope::scope(rhs);
+}
+
+qsizetype Element::qHashImpl(const Element &key, qsizetype seed) noexcept
+{
+ return qHash(QQmlJSScope::scope(key), seed);
+}
+
+/*!
+ \class QQmlSA::GenericPass
+ \inmodule QtQmlCompiler
+
+ \brief The base class for static analysis passes.
+
+ This class contains common functionality used by more specific passses.
+ Custom passes should not directly derive from it, but rather from one of
+ its subclasses.
+ \sa ElementPass, PropertyPass
+ */
+
class GenericPassPrivate {
+ Q_DECLARE_PUBLIC(GenericPass);
+
public:
- const PassManager *manager;
+ GenericPassPrivate(GenericPass *interface, PassManager *manager)
+ : m_manager{ manager }, q_ptr{ interface }
+ {
+ Q_ASSERT(manager);
+ }
+
+private:
+ PassManager *m_manager;
+
+ GenericPass *q_ptr;
};
GenericPass::~GenericPass() = default;
+/*!
+ Creates a generic pass.
+ */
GenericPass::GenericPass(PassManager *manager)
+ : d_ptr{ new GenericPassPrivate{ this, manager } } { }
+
+/*!
+ Emits a warning message \a diagnostic about an issue of type \a id.
+ */
+void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id)
{
- Q_ASSERT(manager);
- d = std::make_unique<GenericPassPrivate>();
- d->manager = manager;
+ emitWarning(diagnostic, id, QQmlSA::SourceLocation{});
}
-void GenericPass::emitWarning(QAnyStringView message, LoggerWarningId id,
- QQmlJS::SourceLocation srcLocation)
+/*!
+ Emits warning message \a diagnostic about an issue of type \a id located at
+ \a srcLocation.
+ */
+void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
+ QQmlSA::SourceLocation srcLocation)
+{
+ Q_D(const GenericPass);
+ PassManagerPrivate::visitor(*d->m_manager)
+ ->logger()
+ ->log(diagnostic.toString(), id,
+ QQmlSA::SourceLocationPrivate::sourceLocation(srcLocation));
+}
+
+/*!
+ Emits a warning message \a diagnostic about an issue of type \a id located at
+ \a srcLocation and with suggested fix \a fix.
+ */
+void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
+ QQmlSA::SourceLocation srcLocation, const QQmlSA::FixSuggestion &fix)
+{
+ Q_D(const GenericPass);
+ PassManagerPrivate::visitor(*d->m_manager)
+ ->logger()
+ ->log(diagnostic.toString(), id,
+ QQmlSA::SourceLocationPrivate::sourceLocation(srcLocation), true, true,
+ FixSuggestionPrivate::fixSuggestion(fix));
+}
+
+/*!
+ Returns the type corresponding to \a typeName inside the
+ currently analysed file.
+ */
+Element GenericPass::resolveTypeInFileScope(QAnyStringView typeName)
{
- d->manager->m_visitor->logger()->log(message.toString(), id, srcLocation);
+ Q_D(const GenericPass);
+ const auto scope =
+ PassManagerPrivate::visitor(*d->m_manager)->imports().type(typeName.toString()).scope;
+ return QQmlJSScope::createQQmlSAElement(scope);
}
+/*!
+ Returns the attached type corresponding to \a typeName used inside
+ the currently analysed file.
+ */
+Element GenericPass::resolveAttachedInFileScope(QAnyStringView typeName)
+{
+ const auto type = resolveTypeInFileScope(typeName);
+ const auto scope = QQmlJSScope::scope(type);
+
+ if (scope.isNull())
+ return QQmlJSScope::createQQmlSAElement(QQmlJSScope::ConstPtr(nullptr));
+
+ return QQmlJSScope::createQQmlSAElement(scope->attachedType());
+}
+
+/*!
+ Returns the type of \a typeName defined in module \a moduleName.
+ If an attached type and a non-attached type share the same name
+ (for example, \c ListView), the \l Element corresponding to the
+ non-attached type is returned.
+ To obtain the attached type, use \l resolveAttached.
+ */
Element GenericPass::resolveType(QAnyStringView moduleName, QAnyStringView typeName)
{
- auto typeImporter = d->manager->m_visitor->importer();
- auto module = typeImporter->importModule(moduleName.toString());
- return module.type(typeName.toString()).scope;
+ Q_D(const GenericPass);
+ QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer();
+ const auto module = typeImporter->importModule(moduleName.toString());
+ const auto scope = module.type(typeName.toString()).scope;
+ return QQmlJSScope::createQQmlSAElement(scope);
+}
+
+/*!
+ Returns the type of the built-in type identified by \a typeName.
+ Built-in types encompass \c{C++} types which the QML engine can handle
+ without any imports (e.g. \l QDateTime and \l QString), global EcmaScript
+ objects like \c Number, as well as the \l {QML Global Object}
+ {global Qt object}.
+ */
+Element GenericPass::resolveBuiltinType(QAnyStringView typeName) const
+{
+ Q_D(const GenericPass);
+ QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer();
+ auto typeNameString = typeName.toString();
+ // we have to check both cpp names
+ auto scope = typeImporter->builtinInternalNames().type(typeNameString).scope;
+ if (!scope) {
+ // and qml names (e.g. for bool) - builtinImportHelper is private, so we can't do it in one call
+ auto builtins = typeImporter->importBuiltins();
+ scope = builtins.type(typeNameString).scope;
+ }
+ return QQmlJSScope::createQQmlSAElement(scope);
+}
+
+/*!
+ Returns the attached type of \a typeName defined in module \a moduleName.
+ */
+Element GenericPass::resolveAttached(QAnyStringView moduleName, QAnyStringView typeName)
+{
+ const auto &resolvedType = resolveType(moduleName, typeName);
+ return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(resolvedType)->attachedType());
}
-Element GenericPass::resolveLiteralType(const QQmlJSMetaPropertyBinding &binding)
+/*!
+ Returns the element representing the type of literal in \a binding. If the
+ binding does not contain a literal value, a null Element is returned.
+ */
+Element GenericPass::resolveLiteralType(const QQmlSA::Binding &binding)
{
- return binding.literalType(d->manager->m_typeResolver);
+ Q_D(const GenericPass);
+
+ return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(binding).literalType(
+ PassManagerPrivate::resolver(*d->m_manager)));
}
/*!
- * \brief PassManager::registerElementPass registers ElementPass
+ Returns the element in \a context that has id \a id.
+ */
+Element GenericPass::resolveIdToElement(QAnyStringView id, const Element &context)
+{
+ Q_D(const GenericPass);
+ const auto scope = PassManagerPrivate::visitor(*d->m_manager)
+ ->addressableScopes()
+ .scope(id.toString(), QQmlJSScope::scope(context));
+ return QQmlJSScope::createQQmlSAElement(scope);
+}
+
+/*!
+ Returns the id of \a element in a given \a context.
+ */
+QString GenericPass::resolveElementToId(const Element &element, const Element &context)
+{
+ Q_D(const GenericPass);
+ return PassManagerPrivate::visitor(*d->m_manager)
+ ->addressableScopes()
+ .id(QQmlJSScope::scope(element), QQmlJSScope::scope(context));
+}
+
+/*!
+ Returns the source code located within \a location.
+ */
+QString GenericPass::sourceCode(QQmlSA::SourceLocation location)
+{
+ Q_D(const GenericPass);
+ return PassManagerPrivate::visitor(*d->m_manager)
+ ->logger()
+ ->code()
+ .mid(location.offset(), location.length());
+}
+
+/*!
+ \class QQmlSA::PassManager
+ \inmodule QtQmlCompiler
+
+ \brief Can analyze an element and its children with static analysis passes.
+ */
+
+// explicitly defaulted out-of-line for PIMPL
+PassManager::PassManager() = default;
+PassManager::~PassManager() = default;
+
+/*!
+ Registers a static analysis \a pass to be run on all elements.
+ */
+void PassManager::registerElementPass(std::unique_ptr<ElementPass> pass)
+{
+ Q_D(PassManager);
+ d->registerElementPass(std::move(pass));
+}
+
+/*!
+ \internal
+ \brief PassManager::registerElementPass registers ElementPass
with the pass manager.
\param pass The registered pass. Ownership is transferred to the pass manager.
*/
-void PassManager::registerElementPass(std::unique_ptr<ElementPass> pass)
+void PassManagerPrivate::registerElementPass(std::unique_ptr<ElementPass> pass)
{
m_elementPasses.push_back(std::move(pass));
}
@@ -63,76 +1068,138 @@ enum LookupMode { Register, Lookup };
static QString lookupName(const QQmlSA::Element &element, LookupMode mode = Lookup)
{
QString name;
- if (element.isNull() || element->internalName().isEmpty()) {
+ if (element.isNull() || QQmlJSScope::scope(element)->internalName().isEmpty()) {
// Bail out with an invalid name, this type is so screwed up we can't do anything reasonable
// with it We should have warned about it in another plac
- if (element.isNull() || element->baseType().isNull())
+ if (element.isNull() || element.baseType().isNull())
return u"$INVALID$"_s;
- name = element->baseType()->internalName();
+ name = QQmlJSScope::scope(element.baseType())->internalName();
} else {
- name = element->internalName();
+ name = QQmlJSScope::scope(element)->internalName();
}
const QString filePath =
- (mode == Register || !element->baseType() ? element : element->baseType())->filePath();
+ (mode == Register || !element.baseType() ? element : element.baseType()).filePath();
- if (element->isComposite() && !filePath.endsWith(u".h"))
+ if (QQmlJSScope::scope(element)->isComposite() && !filePath.endsWith(u".h"))
name += u'@' + filePath;
return name;
}
+/*!
+ Registers a static analysis pass for properties. The \a pass will be run on
+ every property matching the \a moduleName, \a typeName and \a propertyName.
+
+ Omitting the \a propertyName will register this pass for all properties
+ matching the \a typeName and \a moduleName.
+
+ Setting \a allowInheritance to \c true means that the filtering on the type
+ also accepts types deriving from \a typeName.
+
+ \a pass is passed as a \c{std::shared_ptr} to allow reusing the same pass
+ on multiple elements:
+ \code
+ auto titleValiadorPass = std::make_shared<TitleValidatorPass>(manager);
+ manager->registerPropertyPass(titleValidatorPass,
+ "QtQuick", "Window", "title");
+ manager->registerPropertyPass(titleValidatorPass,
+ "QtQuick.Controls", "Dialog", "title");
+ \endcode
+
+ \note Running analysis passes on too many items can be expensive. This is
+ why it is generally good to filter down the set of properties of a pass
+ using the \a moduleName, \a typeName and \a propertyName.
+
+ Returns \c true if the pass was successfully added, \c false otherwise.
+ Adding a pass fails when the \l{QQmlSA::Element}{Element} specified by
+ \a moduleName and \a typeName does not exist.
+
+ \sa PropertyPass
+*/
bool PassManager::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
QAnyStringView moduleName, QAnyStringView typeName,
QAnyStringView propertyName, bool allowInheritance)
{
+ Q_D(PassManager);
+ return d->registerPropertyPass(pass, moduleName, typeName, propertyName, allowInheritance);
+}
+
+bool PassManagerPrivate::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
+ QAnyStringView moduleName, QAnyStringView typeName,
+ QAnyStringView propertyName, bool allowInheritance)
+{
+ if (moduleName.isEmpty() != typeName.isEmpty()) {
+ qWarning() << "Both the moduleName and the typeName must be specified "
+ "for the pass to be registered for a specific element.";
+ }
+
QString name;
if (!moduleName.isEmpty() && !typeName.isEmpty()) {
auto typeImporter = m_visitor->importer();
auto module = typeImporter->importModule(moduleName.toString());
- auto element = module.type(typeName.toString()).scope;
+ auto element = QQmlJSScope::createQQmlSAElement(module.type(typeName.toString()).scope);
if (element.isNull())
return false;
name = lookupName(element, Register);
}
- const PassManager::PropertyPassInfo passInfo {
- propertyName.isEmpty() ? QStringList {} : QStringList { propertyName.toString() },
- std::move(pass), allowInheritance
- };
+ const QQmlSA::PropertyPassInfo passInfo{ propertyName.isEmpty()
+ ? QStringList{}
+ : QStringList{ propertyName.toString() },
+ std::move(pass), allowInheritance };
m_propertyPasses.insert({ name, passInfo });
return true;
}
-void PassManager::addBindingSourceLocations(const Element &element, const Element &scope,
- const QString prefix, bool isAttached)
+void PassManagerPrivate::addBindingSourceLocations(const Element &element, const Element &scope,
+ const QString prefix, bool isAttached)
{
const Element &currentScope = scope.isNull() ? element : scope;
- const auto ownBindings = currentScope->ownPropertyBindings();
- for (const auto &binding : ownBindings.values()) {
+ const auto ownBindings = currentScope.ownPropertyBindings();
+ for (const auto &binding : ownBindings) {
switch (binding.bindingType()) {
- case QQmlJSMetaPropertyBinding::GroupProperty:
- addBindingSourceLocations(element, binding.groupType(),
+ case QQmlSA::BindingType::GroupProperty:
+ addBindingSourceLocations(element, Element{ binding.groupType() },
prefix + binding.propertyName() + u'.');
break;
- case QQmlJSMetaPropertyBinding::AttachedProperty:
- addBindingSourceLocations(element, binding.attachingType(),
+ case QQmlSA::BindingType::AttachedProperty:
+ addBindingSourceLocations(element, Element{ binding.attachingType() },
prefix + binding.propertyName() + u'.', true);
break;
default:
- m_bindingsByLocation.insert({ binding.sourceLocation().offset,
- BindingInfo { prefix + binding.propertyName(), binding,
- currentScope, isAttached } });
+ m_bindingsByLocation.insert({ binding.sourceLocation().offset(),
+ BindingInfo{ prefix + binding.propertyName(), binding,
+ currentScope, isAttached } });
- if (binding.bindingType() != QQmlJSMetaPropertyBinding::Script)
+ if (binding.bindingType() != QQmlSA::BindingType::Script)
analyzeBinding(element, QQmlSA::Element(), binding.sourceLocation());
}
}
}
+/*!
+ Runs the element passes over \a root and all its children.
+ */
void PassManager::analyze(const Element &root)
{
+ Q_D(PassManager);
+ d->analyze(root);
+}
+
+static QQmlJS::ConstPtrWrapperIterator childScopesBegin(const Element &element)
+{
+ return QQmlJSScope::scope(element)->childScopesBegin();
+}
+
+static QQmlJS::ConstPtrWrapperIterator childScopesEnd(const Element &element)
+{
+ return QQmlJSScope::scope(element)->childScopesEnd();
+}
+
+void PassManagerPrivate::analyze(const Element &root)
+{
QList<Element> runStack;
runStack.push_back(root);
while (!runStack.isEmpty()) {
@@ -141,33 +1208,33 @@ void PassManager::analyze(const Element &root)
for (auto &elementPass : m_elementPasses)
if (elementPass->shouldRun(element))
elementPass->run(element);
- const auto ownPropertyBindings = element->ownPropertyBindings();
- for (auto it = element->childScopesBegin(); it != element->childScopesEnd(); ++it) {
- if ((*it)->scopeType() == QQmlJSScope::QMLScope)
- runStack.push_back(*it);
+ for (auto it = childScopesBegin(element), end = childScopesEnd(element); it != end; ++it) {
+ if ((*it)->scopeType() == QQmlSA::ScopeType::QMLScope)
+ runStack.push_back(QQmlJSScope::createQQmlSAElement(*it));
}
}
}
-void PassManager::analyzeWrite(const Element &element, QString propertyName, const Element &value,
- const Element &writeScope, QQmlJS::SourceLocation location)
+void PassManagerPrivate::analyzeWrite(const Element &element, QString propertyName,
+ const Element &value, const Element &writeScope,
+ QQmlSA::SourceLocation location)
{
for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
pass->onWrite(element, propertyName, value, writeScope, location);
}
-void PassManager::analyzeRead(const Element &element, QString propertyName,
- const Element &readScope, QQmlJS::SourceLocation location)
+void PassManagerPrivate::analyzeRead(const Element &element, QString propertyName,
+ const Element &readScope, QQmlSA::SourceLocation location)
{
for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
pass->onRead(element, propertyName, readScope, location);
}
-void PassManager::analyzeBinding(const Element &element, const QQmlSA::Element &value,
- QQmlJS::SourceLocation location)
+void PassManagerPrivate::analyzeBinding(const Element &element, const QQmlSA::Element &value,
+ QQmlSA::SourceLocation location)
{
- const auto info = m_bindingsByLocation.find(location.offset);
+ const auto info = m_bindingsByLocation.find(location.offset());
// If there's no matching binding that means we're in a nested Ret somewhere inside an
// expression
@@ -175,33 +1242,68 @@ void PassManager::analyzeBinding(const Element &element, const QQmlSA::Element &
return;
const QQmlSA::Element &bindingScope = info->second.bindingScope;
- const QQmlJSMetaPropertyBinding &binding = info->second.binding;
+ const QQmlSA::Binding &binding = info->second.binding;
const QString &propertyName = info->second.fullPropertyName;
for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
pass->onBinding(element, propertyName, binding, bindingScope, value);
- if (!info->second.isAttached || bindingScope->baseType().isNull())
+ if (!info->second.isAttached || bindingScope.baseType().isNull())
return;
- for (PropertyPass *pass : findPropertyUsePasses(bindingScope->baseType(), propertyName))
+ for (PropertyPass *pass : findPropertyUsePasses(bindingScope.baseType(), propertyName))
pass->onBinding(element, propertyName, binding, bindingScope, value);
}
+/*!
+ Returns \c true if the module named \a module has been imported by the
+ QML to be analyzed, \c false otherwise.
+
+ This can be used to skip registering a pass which is specific to a specific
+ module.
+
+ \code
+ if (passManager->hasImportedModule("QtPositioning"))
+ passManager->registerElementPass(
+ std::make_unique<PositioningPass>(passManager)
+ );
+ \endcode
+
+ \sa registerPropertyPass(), registerElementPass()
+ */
bool PassManager::hasImportedModule(QAnyStringView module) const
{
- return m_visitor->imports().hasType(u"$module$." + module.toString());
+ return PassManagerPrivate::visitor(*this)->imports().hasType(u"$module$." + module.toString());
+}
+
+/*!
+ Returns \c true if warnings of \a category are enabled, \c false otherwise.
+ */
+bool PassManager::isCategoryEnabled(LoggerWarningId category) const
+{
+ return !PassManagerPrivate::visitor(*this)->logger()->isCategoryIgnored(category);
+}
+
+QQmlJSImportVisitor *QQmlSA::PassManagerPrivate::visitor(const QQmlSA::PassManager &manager)
+{
+ return manager.d_func()->m_visitor;
+}
+
+QQmlJSTypeResolver *QQmlSA::PassManagerPrivate::resolver(const QQmlSA::PassManager &manager)
+{
+ return manager.d_func()->m_typeResolver;
}
-QSet<PropertyPass *> PassManager::findPropertyUsePasses(const QQmlSA::Element &element,
- const QString &propertyName)
+QSet<PropertyPass *> PassManagerPrivate::findPropertyUsePasses(const QQmlSA::Element &element,
+ const QString &propertyName)
{
QStringList typeNames { lookupName(element) };
QQmlJSUtils::searchBaseAndExtensionTypes(
- element, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
+ QQmlJSScope::scope(element),
+ [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
Q_UNUSED(mode);
- typeNames.append(lookupName(scope));
+ typeNames.append(lookupName(QQmlJSScope::createQQmlSAElement(scope)));
return false;
});
@@ -227,26 +1329,118 @@ QSet<PropertyPass *> PassManager::findPropertyUsePasses(const QQmlSA::Element &e
}
void DebugElementPass::run(const Element &element) {
- emitWarning(u"Type: " + element->baseTypeName(), qmlPlugin);
- if (auto bindings = element->propertyBindings(u"objectName"_s); !bindings.isEmpty()) {
+ emitWarning(u"Type: " + element.baseTypeName(), qmlPlugin);
+ if (auto bindings = element.propertyBindings(u"objectName"_s); !bindings.isEmpty()) {
emitWarning(u"is named: " + bindings.first().stringValue(), qmlPlugin);
}
- if (auto defPropName = element->defaultPropertyName(); !defPropName.isEmpty()) {
- emitWarning(u"binding " + QString::number(element->propertyBindings(defPropName).size())
+ if (auto defPropName = element.defaultPropertyName(); !defPropName.isEmpty()) {
+ emitWarning(u"binding " + QString::number(element.propertyBindings(defPropName).size())
+ u" elements to property "_s + defPropName,
qmlPlugin);
}
}
-bool ElementPass::shouldRun(const Element &)
+/*!
+ \class QQmlSA::LintPlugin
+ \inmodule QtQmlCompiler
+
+ \brief Base class for all static analysis plugins.
+ */
+
+/*!
+ \fn void QQmlSA::LintPlugin::registerPasses(PassManager *manager, const Element &rootElement)
+
+ Adds a pass \a manager that will be executed on \a rootElement.
+ */
+
+/*!
+ \class QQmlSA::ElementPass
+ \inmodule QtQmlCompiler
+
+ \brief Base class for all static analysis passes on elements.
+
+ ElementPass is the simpler of the two analysis passes. It will consider every element in
+ a file. The \l shouldRun() method can be used to filter out irrelevant elements, and the
+ \l run() method is doing the initial work.
+
+ Common tasks suitable for an ElementPass are
+ \list
+ \li checking that properties of an Element are not combined in a nonsensical way
+ \li validating property values (e.g. that a property takes only certain enum values)
+ \li checking behavior dependent on an Element's parent (e.g. not using \l {Item::width}
+ when the parent element is a \c Layout).
+ \endlist
+
+ As shown in the snippet below, it is recommended to do necessary type resolution in the
+ constructor of the ElementPass and cache it in local members, and to implement some
+ filtering via \l shouldRun() to keep the static analysis performant.
+
+ \code
+ using namespace QQmlSA;
+ class MyElementPass : public ElementPass
+ {
+ Element myType;
+ public:
+ MyElementPass(QQmlSA::PassManager *manager)
+ : myType(resolveType("MyModule", "MyType")) {}
+
+ bool shouldRun(const Element &element) override
+ {
+ return element.inherits(myType);
+ }
+ void run(const Element &element) override
+ {
+ // actual pass logic
+ }
+ }
+ \endcode
+
+ ElementPasses have limited insight into how an element's properties are used. If you need
+ that information, consider using a \l PropertyPass instead.
+
+ \note ElementPass will only ever consider instantiable types. Therefore, it is unsuitable
+ to analyze attached types and singletons. Those need to be handled via a PropertyPass.
+ */
+
+/*!
+ \fn void QQmlSA::ElementPass::run(const Element &element)
+
+ Executes if \c shouldRun() returns \c true. Performs the real computation
+ of the pass on \a element.
+ This method is meant to be overridden. Calling the base method is not
+ necessary.
+ */
+
+/*!
+ Controls whether the \c run() function should be executed on the given \a element.
+ Subclasses can override this method to improve performance of the analysis by
+ filtering out elements which are not relevant.
+
+ The default implementation unconditionally returns \c true.
+ */
+bool ElementPass::shouldRun(const Element &element)
{
+ (void)element;
return true;
}
+/*!
+ \class QQmlSA::PropertyPass
+ \inmodule QtQmlCompiler
+
+ \brief Base class for all static analysis passes on properties.
+ */
+
+
PropertyPass::PropertyPass(PassManager *manager) : GenericPass(manager) { }
+/*!
+ Executes whenever a property gets bound to a value.
+ The property \a propertyName of \a element is bound to the \a value within
+ \a bindingScope with \a binding.
+ */
void PropertyPass::onBinding(const Element &element, const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding, const Element &bindingScope,
+ const QQmlSA::Binding &binding, const Element &bindingScope,
const Element &value)
{
Q_UNUSED(element);
@@ -256,8 +1450,14 @@ void PropertyPass::onBinding(const Element &element, const QString &propertyName
Q_UNUSED(value);
}
+/*!
+ Executes whenever a property is read.
+
+ The property \a propertyName of \a element is read by an instruction within
+ \a readScope defined at \a location.
+ */
void PropertyPass::onRead(const Element &element, const QString &propertyName,
- const Element &readScope, QQmlJS::SourceLocation location)
+ const Element &readScope, QQmlSA::SourceLocation location)
{
Q_UNUSED(element);
Q_UNUSED(propertyName);
@@ -265,14 +1465,21 @@ void PropertyPass::onRead(const Element &element, const QString &propertyName,
Q_UNUSED(location);
}
+/*!
+ Executes whenever a property is written to.
+
+ The property \a propertyName of \a element is written to by an instruction
+ within \a writeScope defined at \a location. The type of the expression
+ written to \a propertyName is \a expressionType.
+ */
void PropertyPass::onWrite(const Element &element, const QString &propertyName,
- const Element &value, const Element &writeScope,
- QQmlJS::SourceLocation location)
+ const Element &expressionType, const Element &writeScope,
+ QQmlSA::SourceLocation location)
{
Q_UNUSED(element);
Q_UNUSED(propertyName);
Q_UNUSED(writeScope);
- Q_UNUSED(value);
+ Q_UNUSED(expressionType);
Q_UNUSED(location);
}
@@ -281,48 +1488,280 @@ DebugPropertyPass::DebugPropertyPass(QQmlSA::PassManager *manager) : QQmlSA::Pro
}
void DebugPropertyPass::onRead(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlSA::Element &readScope, QQmlJS::SourceLocation location)
+ const QQmlSA::Element &readScope, QQmlSA::SourceLocation location)
{
emitWarning(u"onRead "_s
- + (element->internalName().isEmpty() ? element->baseTypeName()
- : element->internalName())
- + u' ' + propertyName + u' ' + readScope->internalName() + u' '
- + QString::number(location.startLine) + u':'
- + QString::number(location.startColumn),
+ + (QQmlJSScope::scope(element)->internalName().isEmpty()
+ ? element.baseTypeName()
+ : QQmlJSScope::scope(element)->internalName())
+ + u' ' + propertyName + u' ' + QQmlJSScope::scope(readScope)->internalName()
+ + u' ' + QString::number(location.startLine()) + u':'
+ + QString::number(location.startColumn()),
qmlPlugin, location);
}
void DebugPropertyPass::onBinding(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding,
+ const QQmlSA::Binding &binding,
const QQmlSA::Element &bindingScope, const QQmlSA::Element &value)
{
- const auto location = binding.sourceLocation();
+ const auto location = QQmlSA::SourceLocation{ binding.sourceLocation() };
emitWarning(u"onBinding element: '"_s
- + (element->internalName().isEmpty() ? element->baseTypeName()
- : element->internalName())
+ + (QQmlJSScope::scope(element)->internalName().isEmpty()
+ ? element.baseTypeName()
+ : QQmlJSScope::scope(element)->internalName())
+ u"' property: '"_s + propertyName + u"' value: '"_s
- + (value.isNull()
- ? u"NULL"_s
- : (value->internalName().isNull() ? value->baseTypeName()
- : value->internalName()))
+ + (value.isNull() ? u"NULL"_s
+ : (QQmlJSScope::scope(value)->internalName().isNull()
+ ? value.baseTypeName()
+ : QQmlJSScope::scope(value)->internalName()))
+ u"' binding_scope: '"_s
- + (bindingScope->internalName().isEmpty() ? bindingScope->baseTypeName()
- : bindingScope->internalName())
- + u"' "_s + QString::number(location.startLine) + u':'
- + QString::number(location.startColumn),
+ + (QQmlJSScope::scope(bindingScope)->internalName().isEmpty()
+ ? bindingScope.baseTypeName()
+ : QQmlJSScope::scope(bindingScope)->internalName())
+ + u"' "_s + QString::number(location.startLine()) + u':'
+ + QString::number(location.startColumn()),
qmlPlugin, location);
}
void DebugPropertyPass::onWrite(const QQmlSA::Element &element, const QString &propertyName,
const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
- QQmlJS::SourceLocation location)
+ QQmlSA::SourceLocation location)
{
- emitWarning(u"onWrite "_s + element->baseTypeName() + u' ' + propertyName + u' '
- + value->internalName() + u' ' + writeScope->internalName() + u' '
- + QString::number(location.startLine) + u':'
- + QString::number(location.startColumn),
+ emitWarning(u"onWrite "_s + element.baseTypeName() + u' ' + propertyName + u' '
+ + QQmlJSScope::scope(value)->internalName() + u' '
+ + QQmlJSScope::scope(writeScope)->internalName() + u' '
+ + QString::number(location.startLine()) + u':'
+ + QString::number(location.startColumn()),
qmlPlugin, location);
}
+
+/*!
+ Returns the list of element passes.
+ */
+std::vector<std::shared_ptr<ElementPass>> PassManager::elementPasses() const
+{
+ Q_D(const PassManager);
+ return d->m_elementPasses;
+}
+
+/*!
+ Returns the list of property passes.
+ */
+std::multimap<QString, PropertyPassInfo> PassManager::propertyPasses() const
+{
+ Q_D(const PassManager);
+ return d->m_propertyPasses;
+}
+
+/*!
+ Returns bindings by their source location.
+ */
+std::unordered_map<quint32, BindingInfo> PassManager::bindingsByLocation() const
+{
+ Q_D(const PassManager);
+ return d->m_bindingsByLocation;
+}
+
+FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface) : q_ptr{ interface } { }
+
+FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, const QString &fixDescription,
+ const QQmlSA::SourceLocation &location,
+ const QString &replacement)
+ : m_fixSuggestion{ fixDescription, QQmlSA::SourceLocationPrivate::sourceLocation(location),
+ replacement },
+ q_ptr{ interface }
+{
+}
+
+FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface,
+ const FixSuggestionPrivate &other)
+ : m_fixSuggestion{ other.m_fixSuggestion }, q_ptr{ interface }
+{
+}
+
+FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, FixSuggestionPrivate &&other)
+ : m_fixSuggestion{ std::move(other.m_fixSuggestion) }, q_ptr{ interface }
+{
+}
+
+QString FixSuggestionPrivate::fixDescription() const
+{
+ return m_fixSuggestion.fixDescription();
+}
+
+QQmlSA::SourceLocation FixSuggestionPrivate::location() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(m_fixSuggestion.location());
+}
+
+QString FixSuggestionPrivate::replacement() const
+{
+ return m_fixSuggestion.replacement();
+}
+
+void FixSuggestionPrivate::setFileName(const QString &fileName)
+{
+ m_fixSuggestion.setFilename(fileName);
+}
+
+QString FixSuggestionPrivate::fileName() const
+{
+ return m_fixSuggestion.filename();
+}
+
+void FixSuggestionPrivate::setHint(const QString &hint)
+{
+ m_fixSuggestion.setHint(hint);
+}
+
+QString FixSuggestionPrivate::hint() const
+{
+ return m_fixSuggestion.hint();
+}
+
+void FixSuggestionPrivate::setAutoApplicable(bool autoApplicable)
+{
+ m_fixSuggestion.setAutoApplicable(autoApplicable);
+}
+
+bool FixSuggestionPrivate::isAutoApplicable() const
+{
+ return m_fixSuggestion.isAutoApplicable();
+}
+
+QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(FixSuggestion &saFixSuggestion)
+{
+ return saFixSuggestion.d_func()->m_fixSuggestion;
+}
+
+const QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(const FixSuggestion &saFixSuggestion)
+{
+ return saFixSuggestion.d_func()->m_fixSuggestion;
+}
+
+/*!
+ \class QQmlSA::FixSuggestion
+ \inmodule QtQmlCompiler
+
+ \brief Represents a suggested fix for an issue in the source code.
+ */
+
+
+FixSuggestion::FixSuggestion(const QString &fixDescription, const QQmlSA::SourceLocation &location,
+ const QString &replacement)
+ : d_ptr{ new FixSuggestionPrivate{ this, fixDescription, location, replacement } }
+{
+}
+
+FixSuggestion::FixSuggestion(const FixSuggestion &other)
+ : d_ptr{ new FixSuggestionPrivate{ this, *other.d_func() } }
+{
+}
+
+FixSuggestion::FixSuggestion(FixSuggestion &&other) noexcept
+ : d_ptr{ new FixSuggestionPrivate{ this, std::move(*other.d_func()) } }
+{
}
+FixSuggestion &FixSuggestion::operator=(const FixSuggestion &other)
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_fixSuggestion = other.d_func()->m_fixSuggestion;
+ return *this;
+}
+
+FixSuggestion &FixSuggestion::operator=(FixSuggestion &&other) noexcept
+{
+ if (*this == other)
+ return *this;
+
+ d_func()->m_fixSuggestion = std::move(other.d_func()->m_fixSuggestion);
+ return *this;
+}
+
+FixSuggestion::~FixSuggestion() = default;
+
+/*!
+ Returns the description of the fix.
+ */
+QString QQmlSA::FixSuggestion::fixDescription() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).fixDescription();
+}
+
+/*!
+ Returns the location where the fix would be applied.
+ */
+QQmlSA::SourceLocation FixSuggestion::location() const
+{
+ return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
+ FixSuggestionPrivate::fixSuggestion(*this).location());
+}
+
+/*!
+ Returns the fix that will replace the problematic source code.
+ */
+QString FixSuggestion::replacement() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).replacement();
+}
+
+/*!
+ Sets \a fileName as the name of the file where this fix suggestion applies.
+ */
+void FixSuggestion::setFileName(const QString &fileName)
+{
+ FixSuggestionPrivate::fixSuggestion(*this).setFilename(fileName);
+}
+
+/*!
+ Returns the name of the file where this fix suggestion applies.
+ */
+QString FixSuggestion::fileName() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).filename();
+}
+
+/*!
+ Sets \a hint as the hint for this fix suggestion.
+ */
+void FixSuggestion::setHint(const QString &hint)
+{
+ FixSuggestionPrivate::fixSuggestion(*this).setHint(hint);
+}
+
+/*!
+ Returns the hint for this fix suggestion.
+ */
+QString FixSuggestion::hint() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).hint();
+}
+
+/*!
+ Sets \a autoApplicable to determine whether this suggested fix can be
+ applied automatically.
+ */
+void FixSuggestion::setAutoApplicable(bool autoApplicable)
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).setAutoApplicable(autoApplicable);
+}
+
+/*!
+ Returns whether this suggested fix can be applied automatically.
+ */
+bool QQmlSA::FixSuggestion::isAutoApplicable() const
+{
+ return FixSuggestionPrivate::fixSuggestion(*this).isAutoApplicable();
+}
+
+bool FixSuggestion::operatorEqualsImpl(const FixSuggestion &lhs, const FixSuggestion &rhs)
+{
+ return lhs.d_func()->m_fixSuggestion == rhs.d_func()->m_fixSuggestion;
+}
+
+} // namespace QQmlSA
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmlsa.h b/src/qmlcompiler/qqmlsa.h
new file mode 100644
index 0000000000..e63f9ea5ab
--- /dev/null
+++ b/src/qmlcompiler/qqmlsa.h
@@ -0,0 +1,435 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLSA_H
+#define QQMLSA_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the qmllint plugin API, with limited compatibility guarantees.
+// Usage of this API may make your code source and binary incompatible with
+// future versions of Qt.
+//
+
+#include <QtQmlCompiler/qqmlsaconstants.h>
+#include <QtQmlCompiler/qqmljsloggingutils.h>
+
+#include <QtQmlCompiler/qtqmlcompilerexports.h>
+
+#include <QtCore/qhash.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qplugin.h>
+#include <QtQmlCompiler/qqmlsasourcelocation.h>
+
+#include <unordered_map>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlSA {
+
+class BindingPrivate;
+class BindingsPrivate;
+class Element;
+class ElementPass;
+class FixSuggestion;
+class FixSuggestionPrivate;
+class GenericPassPrivate;
+class MethodPrivate;
+class MethodsPrivate;
+class PassManager;
+class PassManagerPrivate;
+class PropertyPass;
+class PropertyPrivate;
+enum class AccessSemantics;
+struct BindingInfo;
+struct PropertyPassInfo;
+
+enum class MethodType { Signal, Slot, Method, StaticMethod };
+
+class Q_QMLCOMPILER_EXPORT Binding
+{
+ Q_DECLARE_PRIVATE(Binding)
+
+public:
+ class Q_QMLCOMPILER_EXPORT Bindings
+ {
+ Q_DECLARE_PRIVATE(Bindings)
+
+ public:
+ Bindings();
+ Bindings(const Bindings &);
+ ~Bindings();
+
+ QMultiHash<QString, Binding>::const_iterator begin() const { return constBegin(); }
+ QMultiHash<QString, Binding>::const_iterator end() const { return constEnd(); }
+ QMultiHash<QString, Binding>::const_iterator constBegin() const;
+ QMultiHash<QString, Binding>::const_iterator constEnd() const;
+
+ private:
+ std::unique_ptr<BindingsPrivate> d_ptr;
+ };
+
+ Binding();
+ Binding(const Binding &);
+ Binding(Binding &&) noexcept;
+ Binding &operator=(const Binding &);
+ Binding &operator=(Binding &&) noexcept;
+ ~Binding();
+
+ Element groupType() const;
+ BindingType bindingType() const;
+ QString stringValue() const;
+ QString propertyName() const;
+ Element attachingType() const;
+ QQmlSA::SourceLocation sourceLocation() const;
+ double numberValue() const;
+ ScriptBindingKind scriptKind() const;
+ bool hasObject() const;
+ Element objectType() const;
+ bool hasUndefinedScriptValue() const;
+
+ friend bool operator==(const Binding &lhs, const Binding &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+ friend bool operator!=(const Binding &lhs, const Binding &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+ static bool isLiteralBinding(BindingType);
+
+private:
+ static bool operatorEqualsImpl(const Binding &, const Binding &);
+
+ std::unique_ptr<BindingPrivate> d_ptr;
+};
+
+class Q_QMLCOMPILER_EXPORT Method
+{
+ Q_DECLARE_PRIVATE(Method)
+
+public:
+ class Q_QMLCOMPILER_EXPORT Methods
+ {
+ Q_DECLARE_PRIVATE(Methods)
+
+ public:
+ Methods();
+ Methods(const Methods &);
+ ~Methods();
+
+ QMultiHash<QString, Method>::const_iterator begin() const { return constBegin(); }
+ QMultiHash<QString, Method>::const_iterator end() const { return constEnd(); }
+ QMultiHash<QString, Method>::const_iterator constBegin() const;
+ QMultiHash<QString, Method>::const_iterator constEnd() const;
+
+ private:
+ std::unique_ptr<MethodsPrivate> d_ptr;
+ };
+
+ Method();
+ Method(const Method &);
+ Method(Method &&) noexcept;
+ Method &operator=(const Method &);
+ Method &operator=(Method &&) noexcept;
+ ~Method();
+
+ QString methodName() const;
+ QQmlSA::SourceLocation sourceLocation() const;
+ MethodType methodType() const;
+
+ friend bool operator==(const Method &lhs, const Method &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+ friend bool operator!=(const Method &lhs, const Method &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+private:
+ static bool operatorEqualsImpl(const Method &, const Method &);
+
+ std::unique_ptr<MethodPrivate> d_ptr;
+};
+
+class Q_QMLCOMPILER_EXPORT Property
+{
+ Q_DECLARE_PRIVATE(Property)
+
+public:
+ Property();
+ Property(const Property &);
+ Property(Property &&) noexcept;
+ Property &operator=(const Property &);
+ Property &operator=(Property &&) noexcept;
+ ~Property();
+
+ QString typeName() const;
+ bool isValid() const;
+ bool isReadonly() const;
+ QQmlSA::Element type() const;
+
+ friend bool operator==(const Property &lhs, const Property &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+
+ friend bool operator!=(const Property &lhs, const Property &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+private:
+ static bool operatorEqualsImpl(const Property &, const Property &);
+
+ std::unique_ptr<PropertyPrivate> d_ptr;
+};
+
+class Q_QMLCOMPILER_EXPORT Element
+{
+ friend class QT_PREPEND_NAMESPACE(QQmlJSScope);
+
+public:
+ Element();
+ Element(const Element &);
+ Element(Element &&other) noexcept
+ {
+ memcpy(m_data, other.m_data, sizeofElement);
+ memset(other.m_data, 0, sizeofElement);
+ }
+ Element &operator=(const Element &);
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(Element)
+ ~Element();
+
+ ScopeType scopeType() const;
+ Element baseType() const;
+ QString baseTypeName() const;
+ Element parentScope() const;
+ bool inherits(const Element &) const;
+
+ bool isNull() const;
+ QString internalId() const;
+ AccessSemantics accessSemantics() const;
+ bool isComposite() const;
+
+ bool hasProperty(const QString &propertyName) const;
+ bool hasOwnProperty(const QString &propertyName) const;
+ Property property(const QString &propertyName) const;
+ bool isPropertyRequired(const QString &propertyName) const;
+ QString defaultPropertyName() const;
+
+ bool hasMethod(const QString &methodName) const;
+ Method::Methods ownMethods() const;
+
+ QQmlSA::SourceLocation sourceLocation() const;
+ QString filePath() const;
+
+ bool hasPropertyBindings(const QString &name) const;
+ bool hasOwnPropertyBindings(const QString &propertyName) const;
+
+ Binding::Bindings ownPropertyBindings() const;
+ Binding::Bindings ownPropertyBindings(const QString &propertyName) const;
+ QList<Binding> propertyBindings(const QString &propertyName) const;
+
+ explicit operator bool() const;
+ bool operator!() const;
+
+ QString name() const;
+
+ friend inline bool operator==(const QQmlSA::Element &lhs, const QQmlSA::Element &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+ friend inline bool operator!=(const Element &lhs, const Element &rhs) { return !(lhs == rhs); }
+
+ friend inline qsizetype qHash(const Element &key, qsizetype seed = 0) noexcept
+ {
+ return qHashImpl(key, seed);
+ }
+
+private:
+ static bool operatorEqualsImpl(const Element &, const Element &);
+ static qsizetype qHashImpl(const Element &key, qsizetype seed) noexcept;
+
+ static constexpr qsizetype sizeofElement = 2 * sizeof(QSharedPointer<int>);
+ alignas(QSharedPointer<int>) char m_data[sizeofElement];
+
+ void swap(Element &other) noexcept
+ {
+ char t[sizeofElement];
+ memcpy(t, m_data, sizeofElement);
+ memcpy(m_data, other.m_data, sizeofElement);
+ memcpy(other.m_data, t, sizeofElement);
+ }
+ friend void swap(Element &lhs, Element &rhs) noexcept { lhs.swap(rhs); }
+};
+
+class Q_QMLCOMPILER_EXPORT GenericPass
+{
+ Q_DECLARE_PRIVATE(GenericPass)
+ Q_DISABLE_COPY_MOVE(GenericPass)
+
+public:
+ GenericPass(PassManager *manager);
+ virtual ~GenericPass();
+
+ void emitWarning(QAnyStringView diagnostic, LoggerWarningId id);
+ void emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
+ QQmlSA::SourceLocation srcLocation);
+ void emitWarning(QAnyStringView diagnostic, LoggerWarningId id,
+ QQmlSA::SourceLocation srcLocation, const QQmlSA::FixSuggestion &fix);
+
+ Element resolveTypeInFileScope(QAnyStringView typeName);
+ Element resolveAttachedInFileScope(QAnyStringView typeName);
+ Element resolveType(QAnyStringView moduleName, QAnyStringView typeName); // #### TODO: revisions
+ Element resolveBuiltinType(QAnyStringView typeName) const;
+ Element resolveAttached(QAnyStringView moduleName, QAnyStringView typeName);
+ Element resolveLiteralType(const Binding &binding);
+
+ Element resolveIdToElement(QAnyStringView id, const Element &context);
+ QString resolveElementToId(const Element &element, const Element &context);
+
+ QString sourceCode(QQmlSA::SourceLocation location);
+
+private:
+ std::unique_ptr<GenericPassPrivate> d_ptr;
+};
+
+class Q_QMLCOMPILER_EXPORT PassManager
+{
+ Q_DISABLE_COPY_MOVE(PassManager)
+ Q_DECLARE_PRIVATE(PassManager)
+
+public:
+ void registerElementPass(std::unique_ptr<ElementPass> pass);
+ bool registerPropertyPass(std::shared_ptr<PropertyPass> pass, QAnyStringView moduleName,
+ QAnyStringView typeName,
+ QAnyStringView propertyName = QAnyStringView(),
+ bool allowInheritance = true);
+ void analyze(const Element &root);
+
+ bool hasImportedModule(QAnyStringView name) const;
+
+ bool isCategoryEnabled(LoggerWarningId category) const;
+
+ std::vector<std::shared_ptr<ElementPass>> elementPasses() const;
+ std::multimap<QString, PropertyPassInfo> propertyPasses() const;
+ std::unordered_map<quint32, BindingInfo> bindingsByLocation() const;
+
+private:
+ PassManager();
+ ~PassManager();
+
+ std::unique_ptr<PassManagerPrivate> d_ptr;
+};
+
+class Q_QMLCOMPILER_EXPORT LintPlugin
+{
+public:
+ LintPlugin() = default;
+ virtual ~LintPlugin() = default;
+
+ Q_DISABLE_COPY_MOVE(LintPlugin)
+
+ virtual void registerPasses(PassManager *manager, const Element &rootElement) = 0;
+};
+
+class Q_QMLCOMPILER_EXPORT PropertyPass : public GenericPass
+{
+public:
+ PropertyPass(PassManager *manager);
+
+ virtual void onBinding(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope,
+ const QQmlSA::Element &value);
+ virtual void onRead(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &readScope, QQmlSA::SourceLocation location);
+ virtual void onWrite(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
+ QQmlSA::SourceLocation location);
+};
+
+class Q_QMLCOMPILER_EXPORT ElementPass : public GenericPass
+{
+public:
+ ElementPass(PassManager *manager) : GenericPass(manager) { }
+
+ virtual bool shouldRun(const Element &element);
+ virtual void run(const Element &element) = 0;
+};
+
+class Q_QMLCOMPILER_EXPORT DebugElementPass : public ElementPass
+{
+ void run(const Element &element) override;
+};
+
+class Q_QMLCOMPILER_EXPORT DebugPropertyPass : public QQmlSA::PropertyPass
+{
+public:
+ DebugPropertyPass(QQmlSA::PassManager *manager);
+
+ void onRead(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override;
+ void onBinding(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope,
+ const QQmlSA::Element &value) override;
+ void onWrite(const QQmlSA::Element &element, const QString &propertyName,
+ const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
+ QQmlSA::SourceLocation location) override;
+};
+
+class Q_QMLCOMPILER_EXPORT FixSuggestion
+{
+ Q_DECLARE_PRIVATE(FixSuggestion)
+
+public:
+ FixSuggestion(const QString &fixDescription, const QQmlSA::SourceLocation &location,
+ const QString &replacement = QString());
+ FixSuggestion(const FixSuggestion &);
+ FixSuggestion(FixSuggestion &&) noexcept;
+ FixSuggestion &operator=(const FixSuggestion &);
+ FixSuggestion &operator=(FixSuggestion &&) noexcept;
+ ~FixSuggestion();
+
+ QString fixDescription() const;
+ QQmlSA::SourceLocation location() const;
+ QString replacement() const;
+
+ void setFileName(const QString &);
+ QString fileName() const;
+
+ void setHint(const QString &);
+ QString hint() const;
+
+ void setAutoApplicable(bool autoApplicable = true);
+ bool isAutoApplicable() const;
+
+ friend bool operator==(const FixSuggestion &lhs, const FixSuggestion &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+
+ friend bool operator!=(const FixSuggestion &lhs, const FixSuggestion &rhs)
+ {
+ return !operatorEqualsImpl(lhs, rhs);
+ }
+
+private:
+ static bool operatorEqualsImpl(const FixSuggestion &, const FixSuggestion &);
+
+ std::unique_ptr<FixSuggestionPrivate> d_ptr;
+};
+
+} // namespace QQmlSA
+
+#define QmlLintPluginInterface_iid "org.qt-project.Qt.Qml.SA.LintPlugin/1.0"
+
+Q_DECLARE_INTERFACE(QQmlSA::LintPlugin, QmlLintPluginInterface_iid)
+
+QT_END_NAMESPACE
+
+#endif // QQMLSA_H
diff --git a/src/qmlcompiler/qqmlsa_p.h b/src/qmlcompiler/qqmlsa_p.h
index 67b6995b54..9c9f557e22 100644
--- a/src/qmlcompiler/qqmlsa_p.h
+++ b/src/qmlcompiler/qqmlsa_p.h
@@ -16,9 +16,9 @@
#include <qtqmlcompilerexports.h>
-#include <private/qqmljsscope_p.h>
#include <private/qqmljslogger_p.h>
#include <QtCore/qset.h>
+#include "qqmljsmetatypes_p.h"
#include <map>
#include <unordered_map>
@@ -33,75 +33,173 @@ class QQmlJSImportVisitor;
namespace QQmlSA {
-// ### FIXME: Replace with a proper PIMPL'd type
-using Element = QQmlJSScope::ConstPtr;
-
+class Bindings;
class GenericPassPrivate;
class PassManager;
-class Q_QMLCOMPILER_EXPORT GenericPass
+enum class AccessSemantics { Reference, Value, None, Sequence };
+
+enum class Flag {
+ Creatable = 0x1,
+ Composite = 0x2,
+ Singleton = 0x4,
+ Script = 0x8,
+ CustomParser = 0x10,
+ Array = 0x20,
+ InlineComponent = 0x40,
+ WrappedInImplicitComponent = 0x80,
+ HasBaseTypeError = 0x100,
+ HasExtensionNamespace = 0x200,
+ IsListProperty = 0x400,
+};
+
+struct BindingInfo
+{
+ QString fullPropertyName;
+ QQmlSA::Binding binding;
+ QQmlSA::Element bindingScope;
+ bool isAttached;
+};
+
+struct PropertyPassInfo
+{
+ QStringList properties;
+ std::shared_ptr<QQmlSA::PropertyPass> pass;
+ bool allowInheritance = true;
+};
+
+class BindingsPrivate
+{
+ friend class QT_PREPEND_NAMESPACE(QQmlJSMetaPropertyBinding);
+ Q_DECLARE_PUBLIC(QQmlSA::Binding::Bindings)
+
+public:
+ explicit BindingsPrivate(QQmlSA::Binding::Bindings *);
+ BindingsPrivate(QQmlSA::Binding::Bindings *, const BindingsPrivate &);
+ BindingsPrivate(QQmlSA::Binding::Bindings *, BindingsPrivate &&);
+ ~BindingsPrivate() = default;
+
+ QMultiHash<QString, Binding>::const_iterator constBegin() const;
+ QMultiHash<QString, Binding>::const_iterator constEnd() const;
+
+ static QQmlSA::Binding::Bindings
+ createBindings(const QMultiHash<QString, QQmlJSMetaPropertyBinding> &);
+ static QQmlSA::Binding::Bindings
+ createBindings(QPair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator,
+ QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator>);
+
+private:
+ QMultiHash<QString, Binding> m_bindings;
+ QQmlSA::Binding::Bindings *q_ptr;
+};
+
+class BindingPrivate
{
+ friend class QT_PREPEND_NAMESPACE(QQmlJSMetaPropertyBinding);
+ Q_DECLARE_PUBLIC(Binding)
+
public:
- Q_DISABLE_COPY_MOVE(GenericPass)
- GenericPass(PassManager *manager);
- virtual ~GenericPass();
+ explicit BindingPrivate(Binding *);
+ BindingPrivate(Binding *, const BindingPrivate &);
- void emitWarning(QAnyStringView message, LoggerWarningId id,
- QQmlJS::SourceLocation srcLocation = QQmlJS::SourceLocation());
- Element resolveType(QAnyStringView moduleName, QAnyStringView typeName); // #### TODO: revisions
- Element resolveLiteralType(const QQmlJSMetaPropertyBinding &binding);
+ static QQmlSA::Binding createBinding(const QQmlJSMetaPropertyBinding &);
+ static QQmlJSMetaPropertyBinding binding(QQmlSA::Binding &binding);
+ static const QQmlJSMetaPropertyBinding binding(const QQmlSA::Binding &binding);
private:
- std::unique_ptr<GenericPassPrivate> d; // PIMPL might be overkill
+ QQmlJSMetaPropertyBinding m_binding;
+ Binding *q_ptr;
};
-class Q_QMLCOMPILER_EXPORT ElementPass : public GenericPass
+class MethodPrivate
{
+ friend class QT_PREPEND_NAMESPACE(QQmlJSMetaMethod);
+ Q_DECLARE_PUBLIC(Method)
+
public:
- ElementPass(PassManager *manager) : GenericPass(manager) { }
+ explicit MethodPrivate(Method *);
+ MethodPrivate(Method *, const MethodPrivate &);
- virtual bool shouldRun(const Element &element);
- virtual void run(const Element &element) = 0;
+ QString methodName() const;
+ QQmlSA::SourceLocation sourceLocation() const;
+ MethodType methodType() const;
+
+ static QQmlSA::Method createMethod(const QQmlJSMetaMethod &);
+ static QQmlJSMetaMethod method(const QQmlSA::Method &);
+
+private:
+ QQmlJSMetaMethod m_method;
+ Method *q_ptr;
};
-class Q_QMLCOMPILER_EXPORT PropertyPass : public GenericPass
+class MethodsPrivate
{
+ friend class QT_PREPEND_NAMESPACE(QQmlJSMetaMethod);
+ Q_DECLARE_PUBLIC(QQmlSA::Method::Methods)
+
public:
- PropertyPass(PassManager *manager);
-
- virtual void onBinding(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding,
- const QQmlSA::Element &bindingScope, const QQmlSA::Element &value);
- virtual void onRead(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlSA::Element &readScope, QQmlJS::SourceLocation location);
- virtual void onWrite(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
- QQmlJS::SourceLocation location);
+ explicit MethodsPrivate(QQmlSA::Method::Methods *);
+ MethodsPrivate(QQmlSA::Method::Methods *, const MethodsPrivate &);
+ MethodsPrivate(QQmlSA::Method::Methods *, MethodsPrivate &&);
+ ~MethodsPrivate() = default;
+
+ QMultiHash<QString, Method>::const_iterator constBegin() const;
+ QMultiHash<QString, Method>::const_iterator constEnd() const;
+
+ static QQmlSA::Method::Methods createMethods(const QMultiHash<QString, QQmlJSMetaMethod> &);
+
+private:
+ QMultiHash<QString, Method> m_methods;
+ QQmlSA::Method::Methods *q_ptr;
};
-class Q_QMLCOMPILER_EXPORT LintPlugin
+class PropertyPrivate
{
+ friend class QT_PREPEND_NAMESPACE(QQmlJSMetaProperty);
+ Q_DECLARE_PUBLIC(QQmlSA::Property)
+
public:
- LintPlugin() = default;
- virtual ~LintPlugin() = default;
+ explicit PropertyPrivate(Property *);
+ PropertyPrivate(Property *, const PropertyPrivate &);
+ PropertyPrivate(Property *, PropertyPrivate &&);
+ ~PropertyPrivate() = default;
+
+ QString typeName() const;
+ bool isValid() const;
+ bool isReadonly() const;
+ QQmlSA::Element type() const;
- Q_DISABLE_COPY_MOVE(LintPlugin)
+ static QQmlJSMetaProperty property(const QQmlSA::Property &property);
+ static QQmlSA::Property createProperty(const QQmlJSMetaProperty &);
- virtual void registerPasses(PassManager *manager, const Element &rootElement) = 0;
+private:
+ QQmlJSMetaProperty m_property;
+ QQmlSA::Property *q_ptr;
};
-// ### FIXME: Make this (at least partially) private again as soon as possible
-class Q_QMLCOMPILER_EXPORT PassManager
+class Q_QMLCOMPILER_EXPORT PassManagerPrivate
{
+ friend class QT_PREPEND_NAMESPACE(QQmlJSScope);
+
public:
- Q_DISABLE_COPY_MOVE(PassManager)
+ Q_DISABLE_COPY_MOVE(PassManagerPrivate)
friend class GenericPass;
- PassManager(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver)
+ PassManagerPrivate(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver)
: m_visitor(visitor), m_typeResolver(resolver)
{
- Q_UNUSED(m_typeResolver);
}
+
+ static PassManagerPrivate *get(PassManager *manager) { return manager->d_func(); }
+ static const PassManagerPrivate *get(const PassManager *manager) { return manager->d_func(); }
+ static PassManager *createPassManager(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver)
+ {
+ PassManager *result = new PassManager();
+ result->d_ptr = std::make_unique<PassManagerPrivate>(visitor, resolver);
+ return result;
+ }
+ static void deletePassManager(PassManager *q) { delete q; }
+
void registerElementPass(std::unique_ptr<ElementPass> pass);
bool registerPropertyPass(std::shared_ptr<PropertyPass> pass, QAnyStringView moduleName,
QAnyStringView typeName,
@@ -111,70 +209,67 @@ public:
bool hasImportedModule(QAnyStringView name) const;
-private:
- friend struct ::QQmlJSTypePropagator;
+ static QQmlJSImportVisitor *visitor(const QQmlSA::PassManager &);
+ static QQmlJSTypeResolver *resolver(const QQmlSA::PassManager &);
+
QSet<PropertyPass *> findPropertyUsePasses(const QQmlSA::Element &element,
const QString &propertyName);
void analyzeWrite(const QQmlSA::Element &element, QString propertyName,
const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
- QQmlJS::SourceLocation location);
+ QQmlSA::SourceLocation location);
void analyzeRead(const QQmlSA::Element &element, QString propertyName,
- const QQmlSA::Element &readScope, QQmlJS::SourceLocation location);
+ const QQmlSA::Element &readScope, QQmlSA::SourceLocation location);
void analyzeBinding(const QQmlSA::Element &element, const QQmlSA::Element &value,
- QQmlJS::SourceLocation location);
-
- struct BindingInfo
- {
- QString fullPropertyName;
- QQmlJSMetaPropertyBinding binding;
- QQmlSA::Element bindingScope;
- bool isAttached;
- };
-
- struct PropertyPassInfo
- {
- QStringList properties;
- std::shared_ptr<PropertyPass> pass;
- bool allowInheritance = true;
- };
+ QQmlSA::SourceLocation location);
void addBindingSourceLocations(const QQmlSA::Element &element,
const QQmlSA::Element &scope = QQmlSA::Element(),
const QString prefix = QString(), bool isAttached = false);
- std::vector<std::unique_ptr<ElementPass>> m_elementPasses;
+ std::vector<std::shared_ptr<ElementPass>> m_elementPasses;
std::multimap<QString, PropertyPassInfo> m_propertyPasses;
std::unordered_map<quint32, BindingInfo> m_bindingsByLocation;
QQmlJSImportVisitor *m_visitor;
QQmlJSTypeResolver *m_typeResolver;
};
-class Q_QMLCOMPILER_EXPORT DebugElementPass : public ElementPass
+class FixSuggestionPrivate
{
- void run(const Element &element) override;
-};
+ Q_DECLARE_PUBLIC(FixSuggestion)
+ friend class QT_PREPEND_NAMESPACE(QQmlJSFixSuggestion);
-class Q_QMLCOMPILER_EXPORT DebugPropertyPass : public QQmlSA::PropertyPass
-{
public:
- DebugPropertyPass(QQmlSA::PassManager *manager);
-
- void onRead(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override;
- void onBinding(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope,
- const QQmlSA::Element &value) override;
- void onWrite(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
- QQmlJS::SourceLocation location) override;
-};
-}
+ explicit FixSuggestionPrivate(FixSuggestion *);
+ FixSuggestionPrivate(FixSuggestion *, const QString &fixDescription,
+ const QQmlSA::SourceLocation &location, const QString &replacement);
+ FixSuggestionPrivate(FixSuggestion *, const FixSuggestionPrivate &);
+ FixSuggestionPrivate(FixSuggestion *, FixSuggestionPrivate &&);
+ ~FixSuggestionPrivate() = default;
+
+ QString fixDescription() const;
+ QQmlSA::SourceLocation location() const;
+ QString replacement() const;
-#define QmlLintPluginInterface_iid "org.qt-project.Qt.Qml.SA.LintPlugin/1.0"
+ void setFileName(const QString &);
+ QString fileName() const;
+
+ void setHint(const QString &);
+ QString hint() const;
+
+ void setAutoApplicable(bool autoApplicable = true);
+ bool isAutoApplicable() const;
+
+ static QQmlJSFixSuggestion &fixSuggestion(QQmlSA::FixSuggestion &);
+ static const QQmlJSFixSuggestion &fixSuggestion(const QQmlSA::FixSuggestion &);
+
+private:
+ QQmlJSFixSuggestion m_fixSuggestion;
+ QQmlSA::FixSuggestion *q_ptr;
+};
-Q_DECLARE_INTERFACE(QQmlSA::LintPlugin, QmlLintPluginInterface_iid)
+} // namespace QQmlSA
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmlsaconstants.h b/src/qmlcompiler/qqmlsaconstants.h
new file mode 100644
index 0000000000..019040d259
--- /dev/null
+++ b/src/qmlcompiler/qqmlsaconstants.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLSACONSTANTS_H
+#define QQMLSACONSTANTS_H
+
+#include <QtCore/qtconfigmacros.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlSA {
+
+enum class BindingType : unsigned int {
+ Invalid,
+ BoolLiteral,
+ NumberLiteral,
+ StringLiteral,
+ RegExpLiteral,
+ Null,
+ Translation,
+ TranslationById,
+ Script,
+ Object,
+ Interceptor,
+ ValueSource,
+ AttachedProperty,
+ GroupProperty,
+};
+
+enum class ScriptBindingKind : unsigned int {
+ Invalid,
+ PropertyBinding, // property int p: 1 + 1
+ SignalHandler, // onSignal: { ... }
+ ChangeHandler, // onXChanged: { ... }
+};
+
+enum class ScopeType {
+ JSFunctionScope,
+ JSLexicalScope,
+ QMLScope,
+ GroupedPropertyScope,
+ AttachedPropertyScope,
+ EnumScope
+};
+
+} // namespace QQmlSA
+
+QT_END_NAMESPACE
+
+#endif // QQMLSACONSTANTS_H
diff --git a/src/qmlcompiler/qqmlsasourcelocation.cpp b/src/qmlcompiler/qqmlsasourcelocation.cpp
new file mode 100644
index 0000000000..63b61cce5b
--- /dev/null
+++ b/src/qmlcompiler/qqmlsasourcelocation.cpp
@@ -0,0 +1,125 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmlsasourcelocation.h"
+#include "qqmlsasourcelocation_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QQmlSA {
+
+static_assert(SourceLocationPrivate::sizeOfSourceLocation() == sizeof(SourceLocation));
+
+/*!
+ \class QQmlSA::SourceLocation
+ \inmodule QtQmlCompiler
+
+ \brief Represents a location or region in the source code.
+ */
+QQmlSA::SourceLocation::SourceLocation(quint32 offset, quint32 length, quint32 line, quint32 column)
+{
+ new (m_data) QQmlJS::SourceLocation{ offset, length, line, column };
+}
+
+// explicitly defaulted out-of-line for PIMPL
+QQmlSA::SourceLocation::SourceLocation(const SourceLocation &other) = default;
+QQmlSA::SourceLocation & QQmlSA::SourceLocation::operator=(const QQmlSA::SourceLocation &other) = default;
+SourceLocation::~SourceLocation() = default;
+
+bool QQmlSA::SourceLocation::isValid() const
+{
+ return QQmlSA::SourceLocationPrivate::sourceLocation(*this).isValid();
+}
+
+/*!
+ Returns the offset of the beginning of this source location.
+ */
+quint32 QQmlSA::SourceLocation::begin() const
+{
+ return QQmlSA::SourceLocationPrivate::sourceLocation(*this).begin();
+}
+
+/*!
+ Returns the offset of the end of this source location.
+ */
+quint32 QQmlSA::SourceLocation::end() const
+{
+ return QQmlSA::SourceLocationPrivate::sourceLocation(*this).end();
+}
+
+/*!
+ Returns the offset of the beginning of this source location.
+ */
+quint32 QQmlSA::SourceLocation::offset() const
+{
+ return QQmlSA::SourceLocationPrivate::sourceLocation(*this).offset;
+}
+
+/*!
+ Returns the length of this source location.
+ */
+quint32 QQmlSA::SourceLocation::length() const
+{
+ return QQmlSA::SourceLocationPrivate::sourceLocation(*this).length;
+}
+
+/*!
+ Returns the line number containing the beginning of this source location.
+ */
+quint32 QQmlSA::SourceLocation::startLine() const
+{
+ return QQmlSA::SourceLocationPrivate::sourceLocation(*this).startLine;
+}
+
+/*!
+ Returns the column number containing the beginning of this source location.
+ */
+quint32 QQmlSA::SourceLocation::startColumn() const
+{
+ return QQmlSA::SourceLocationPrivate::sourceLocation(*this).startColumn;
+}
+
+/*!
+ Returns a source location of lenth zero pointing to the beginning of this
+ source location.
+ */
+QQmlSA::SourceLocation QQmlSA::SourceLocation::startZeroLengthLocation() const
+{
+ QQmlSA::SourceLocation saLocation;
+ auto &wrappedLocation = reinterpret_cast<QQmlJS::SourceLocation &>(saLocation.m_data);
+ wrappedLocation =
+ QQmlSA::SourceLocationPrivate::sourceLocation(*this).startZeroLengthLocation();
+
+ return saLocation;
+}
+
+/*!
+ Returns a source location of lenth zero pointing to the end of this source
+ location pointing to \a text.
+ */
+QQmlSA::SourceLocation QQmlSA::SourceLocation::endZeroLengthLocation(QStringView text) const
+{
+ QQmlSA::SourceLocation saLocation;
+ auto &wrappedLocation = reinterpret_cast<QQmlJS::SourceLocation &>(saLocation.m_data);
+ wrappedLocation = wrappedLocation.endZeroLengthLocation(text);
+
+ return saLocation;
+}
+
+qsizetype QQmlSA::SourceLocation::qHashImpl(const SourceLocation &location, qsizetype seed)
+{
+ return qHash(QQmlSA::SourceLocationPrivate::sourceLocation(location), seed);
+}
+
+bool QQmlSA::SourceLocation::operatorEqualsImpl(const SourceLocation &lhs,
+ const SourceLocation &rhs)
+{
+ return QQmlSA::SourceLocationPrivate::sourceLocation(lhs)
+ == QQmlSA::SourceLocationPrivate::sourceLocation(rhs);
+}
+
+} // namespace QQmlSA
+
+QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmlsasourcelocation.h b/src/qmlcompiler/qqmlsasourcelocation.h
new file mode 100644
index 0000000000..7009d4faef
--- /dev/null
+++ b/src/qmlcompiler/qqmlsasourcelocation.h
@@ -0,0 +1,83 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLSASOURCELOCATION_H
+#define QQMLSASOURCELOCATION_H
+
+#include <QtQmlCompiler/qtqmlcompilerexports.h>
+
+#include <QtCore/qstringview.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+class SourceLocation;
+} // namespace QQmlJS
+
+namespace QQmlSA {
+
+class SourceLocationPrivate;
+
+class Q_QMLCOMPILER_EXPORT SourceLocation
+{
+ friend class QT_PREPEND_NAMESPACE(QQmlSA::SourceLocationPrivate);
+
+public:
+ explicit SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0,
+ quint32 column = 0);
+ SourceLocation(const SourceLocation &);
+ SourceLocation(SourceLocation &&other) noexcept
+ {
+ memcpy(m_data, other.m_data, sizeofSourceLocation);
+ memset(other.m_data, 0, sizeofSourceLocation);
+ }
+ SourceLocation &operator=(const SourceLocation &);
+ SourceLocation &operator=(SourceLocation &&other) noexcept
+ {
+ memcpy(m_data, other.m_data, sizeofSourceLocation);
+ memset(other.m_data, 0, sizeofSourceLocation);
+ return *this;
+ }
+ ~SourceLocation();
+
+ bool isValid() const;
+
+ quint32 begin() const;
+ quint32 end() const;
+
+ quint32 offset() const;
+ quint32 length() const;
+ quint32 startLine() const;
+ quint32 startColumn() const;
+
+ SourceLocation startZeroLengthLocation() const;
+ SourceLocation endZeroLengthLocation(QStringView text) const;
+
+ friend qsizetype qHash(const SourceLocation &location, qsizetype seed = 0)
+ {
+ return qHashImpl(location, seed);
+ }
+
+ friend bool operator==(const SourceLocation &lhs, const SourceLocation &rhs)
+ {
+ return operatorEqualsImpl(lhs, rhs);
+ }
+
+ friend bool operator!=(const SourceLocation &lhs, const SourceLocation &rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+private:
+ static qsizetype qHashImpl(const SourceLocation &location, qsizetype seed);
+ static bool operatorEqualsImpl(const SourceLocation &, const SourceLocation &);
+
+ static constexpr qsizetype sizeofSourceLocation = 4 * sizeof(quint32);
+ alignas(int) char m_data[sizeofSourceLocation] = {};
+};
+
+} // namespace QQmlSA
+
+QT_END_NAMESPACE
+
+#endif // QQMLSASOURCELOCATION_H
diff --git a/src/qmlcompiler/qqmlsasourcelocation_p.h b/src/qmlcompiler/qqmlsasourcelocation_p.h
new file mode 100644
index 0000000000..fa78512c46
--- /dev/null
+++ b/src/qmlcompiler/qqmlsasourcelocation_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLSASOURCELOCATION_P_H
+#define QQMLSASOURCELOCATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include "qqmlsasourcelocation.h"
+
+#include <QtQml/private/qqmljssourcelocation_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlSA {
+
+class SourceLocationPrivate
+{
+public:
+ static const QQmlJS::SourceLocation &
+ sourceLocation(const QQmlSA::SourceLocation &sourceLocation)
+ {
+ return reinterpret_cast<const QQmlJS::SourceLocation &>(sourceLocation.m_data);
+ }
+
+ static QQmlSA::SourceLocation
+ createQQmlSASourceLocation(const QQmlJS::SourceLocation &jsLocation)
+ {
+ QQmlSA::SourceLocation saLocation;
+ auto &internal = reinterpret_cast<QQmlJS::SourceLocation &>(saLocation.m_data);
+ internal = jsLocation;
+ return saLocation;
+ }
+
+ static constexpr qsizetype sizeOfSourceLocation()
+ {
+ return SourceLocation::sizeofSourceLocation;
+ }
+};
+
+} // namespace QQmlSA
+
+QT_END_NAMESPACE
+
+#endif // QQMLSASOURCELOCATION_P_H
diff --git a/src/qmlcompiler/qresourcerelocater.cpp b/src/qmlcompiler/qresourcerelocater.cpp
index 05ad059586..3b2eb67156 100644
--- a/src/qmlcompiler/qresourcerelocater.cpp
+++ b/src/qmlcompiler/qresourcerelocater.cpp
@@ -10,6 +10,7 @@
QT_BEGIN_NAMESPACE
/*!
+ \internal
Changes all the paths in resource file \a input so that they are relative to
location \a output and writes the result to resource file \a output.
*/
diff --git a/src/qmlcompiler/qresourcerelocater_p.h b/src/qmlcompiler/qresourcerelocater_p.h
index b0c2471147..4b06a30041 100644
--- a/src/qmlcompiler/qresourcerelocater_p.h
+++ b/src/qmlcompiler/qresourcerelocater_p.h
@@ -14,14 +14,14 @@
//
// We mean it.
-#include <private/qtqmlcompilerexports_p.h>
+#include <qtqmlcompilerexports.h>
#include <QtCore/qstring.h>
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
-int Q_QMLCOMPILER_PRIVATE_EXPORT qRelocateResourceFile(const QString &input, const QString &output);
+int Q_QMLCOMPILER_EXPORT qRelocateResourceFile(const QString &input, const QString &output);
QT_END_NAMESPACE
diff --git a/src/qmldebug/CMakeLists.txt b/src/qmldebug/CMakeLists.txt
index 084e90cc19..033a492cce 100644
--- a/src/qmldebug/CMakeLists.txt
+++ b/src/qmldebug/CMakeLists.txt
@@ -39,4 +39,5 @@ qt_internal_add_module(QmlDebugPrivate
Qt::Network
Qt::PacketProtocolPrivate
Qt::QmlPrivate
+ NO_GENERATE_CPP_EXPORTS
)
diff --git a/src/qmldebug/qqmldebugclient_p_p.h b/src/qmldebug/qqmldebugclient_p_p.h
index 23ddae1ab1..003b513db2 100644
--- a/src/qmldebug/qqmldebugclient_p_p.h
+++ b/src/qmldebug/qqmldebugclient_p_p.h
@@ -5,7 +5,9 @@
#define QQMLDEBUGCLIENT_P_P_H
#include "qqmldebugclient_p.h"
+
#include <private/qobject_p.h>
+#include <QtCore/qpointer.h>
//
// W A R N I N G
diff --git a/src/qmldebug/qqmlenginedebugclient.cpp b/src/qmldebug/qqmlenginedebugclient.cpp
index eab1621cea..910cc6ca67 100644
--- a/src/qmldebug/qqmlenginedebugclient.cpp
+++ b/src/qmldebug/qqmlenginedebugclient.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlenginedebugclient_p_p.h"
#include <private/qqmldebugconnection_p.h>
diff --git a/src/qmldebug/qqmlenginedebugclient_p.h b/src/qmldebug/qqmlenginedebugclient_p.h
index 48bc62f076..356c640f96 100644
--- a/src/qmldebug/qqmlenginedebugclient_p.h
+++ b/src/qmldebug/qqmlenginedebugclient_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQMLENGINEDEBUGCLIENT_H
#define QQMLENGINEDEBUGCLIENT_H
diff --git a/src/qmldebug/qv4debugclient.cpp b/src/qmldebug/qv4debugclient.cpp
index 83c6660a94..cb05a015a7 100644
--- a/src/qmldebug/qv4debugclient.cpp
+++ b/src/qmldebug/qv4debugclient.cpp
@@ -526,7 +526,7 @@ void QV4DebugClientPrivate::sendMessage(const QByteArray &command, const QJsonOb
void QV4DebugClientPrivate::flushSendBuffer()
{
- foreach (const QByteArray &msg, sendBuffer)
+ for (const QByteArray &msg : std::as_const(sendBuffer))
sendMessage(msg);
sendBuffer.clear();
}
diff --git a/src/qmldom/CMakeLists.txt b/src/qmldom/CMakeLists.txt
index 3edb917836..f0fffea605 100644
--- a/src/qmldom/CMakeLists.txt
+++ b/src/qmldom/CMakeLists.txt
@@ -14,6 +14,7 @@ qt_internal_add_module(QmlDomPrivate
SOURCES
qqmldom_fwd_p.h
qqmldom_global.h
+ qqmldom_utils_p.h qqmldom_utils.cpp
qqmldomastcreator.cpp qqmldomastcreator_p.h
qqmldomastdumper.cpp qqmldomastdumper_p.h
qqmldomattachedinfo.cpp qqmldomattachedinfo_p.h
@@ -39,11 +40,14 @@ qt_internal_add_module(QmlDomPrivate
qqmldomscanner.cpp qqmldomscanner_p.h
qqmldomtop.cpp qqmldomtop_p.h
qqmldomtypesreader.cpp qqmldomtypesreader_p.h
+ qqmldomscriptelements_p.h qqmldomscriptelements.cpp
DEFINES
QMLDOM_LIBRARY
PUBLIC_LIBRARIES
Qt::QmlPrivate
Qt::QmlCompilerPrivate
+ NO_UNITY_BUILD
+ NO_GENERATE_CPP_EXPORTS
)
#### Keys ignored in scope 1:.:.:qmldom.pro:<TRUE>:
diff --git a/src/qmldom/qqmldom_fwd_p.h b/src/qmldom/qqmldom_fwd_p.h
index 25cba4f8dd..9b8603b33e 100644
--- a/src/qmldom/qqmldom_fwd_p.h
+++ b/src/qmldom/qqmldom_fwd_p.h
@@ -30,6 +30,7 @@ class Comment;
class CommentedElement;
class ConstantData;
class DomBase;
+enum DomCreationOption : char;
class DomEnvironment;
class DomItem;
class DomTop;
@@ -41,6 +42,7 @@ class ExternalItemInfoBase;
class ExternalItemPairBase;
class ExternalOwningItem;
class FileLocations;
+enum FileLocationRegion : int;
class FileWriter;
class GlobalComponent;
class GlobalScope;
@@ -65,7 +67,7 @@ class Path;
class Pragma;
class PropertyDefinition;
class PropertyInfo;
-class QmlDomAstCreator;
+class QQmlDomAstCreator;
class QmlComponent;
class QmlDirectory;
class QmldirFile;
@@ -80,6 +82,22 @@ class Source;
class TestDomItem;
class Version;
+namespace ScriptElements {
+class BlockStatement;
+class IdentifierExpression;
+class Literal;
+class ForStatement;
+class IfStatement;
+class BinaryExpression;
+class VariableDeclaration;
+class VariableDeclarationEntry;
+class GenericScriptElement;
+// TODO: add new script classes here, as qqmldomitem_p.h cannot include qqmldomscriptelements_p.h
+// without creating circular dependencies
+class ReturnStatement;
+
+} // end namespace ScriptElements
+
} // end namespace Dom
} // end namespace QQmlJS
QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldom_utils.cpp b/src/qmldom/qqmldom_utils.cpp
new file mode 100644
index 0000000000..a7c985644a
--- /dev/null
+++ b/src/qmldom/qqmldom_utils.cpp
@@ -0,0 +1,63 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmldom_utils_p.h"
+#include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qcbormap.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QQmlJSDomImporting, "qt.qqmljsdom.importing")
+
+namespace QQmlJS {
+namespace Dom {
+
+using namespace Qt::StringLiterals;
+
+QStringList resourceFilesFromBuildFolders(const QStringList &buildFolders)
+{
+ QStringList result;
+ for (const QString &path : buildFolders) {
+ QDir dir(path);
+ if (!dir.cd(u".rcc"_s))
+ continue;
+
+ QDirIterator it(dir.canonicalPath(), QStringList{ u"*.qrc"_s }, QDir::Files,
+ QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ result.append(it.next());
+ }
+ }
+ return result;
+}
+
+static QMetaEnum regionEnum = QMetaEnum::fromType<FileLocationRegion>();
+
+QString fileLocationRegionName(FileLocationRegion region)
+{
+ return QString::fromLatin1(regionEnum.key(region));
+}
+
+FileLocationRegion fileLocationRegionValue(QStringView region)
+{
+ return static_cast<FileLocationRegion>(regionEnum.keyToValue(region.toLatin1()));
+}
+
+QCborValue sourceLocationToQCborValue(QQmlJS::SourceLocation loc)
+{
+ QCborMap res({
+ {QStringLiteral(u"offset"), loc.offset},
+ {QStringLiteral(u"length"), loc.length},
+ {QStringLiteral(u"startLine"), loc.startLine},
+ {QStringLiteral(u"startColumn"), loc.startColumn}
+ });
+ return res;
+}
+
+} // namespace Dom
+}; // namespace QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldom_utils_p.h b/src/qmldom/qqmldom_utils_p.h
new file mode 100644
index 0000000000..6fcdb5fe10
--- /dev/null
+++ b/src/qmldom/qqmldom_utils_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLDOM_UTILS_P_H
+#define QQMLDOM_UTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include "qqmldom_fwd_p.h"
+#include "qqmldomconstants_p.h"
+#include <QtQml/private/qqmljssourcelocation_p.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qcborvalue.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QQmlJSDomImporting);
+
+template<class... Ts>
+struct qOverloadedVisitor : Ts...
+{
+ using Ts::operator()...;
+};
+template<class... Ts>
+qOverloadedVisitor(Ts...) -> qOverloadedVisitor<Ts...>;
+
+namespace QQmlJS {
+namespace Dom {
+
+QStringList resourceFilesFromBuildFolders(const QStringList &buildFolders);
+
+QString fileLocationRegionName(FileLocationRegion region);
+FileLocationRegion fileLocationRegionValue(QStringView region);
+
+QCborValue sourceLocationToQCborValue(SourceLocation loc);
+
+} // namespace Dom
+}; // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QQMLDOM_UTILS_P_H
diff --git a/src/qmldom/qqmldomastcreator.cpp b/src/qmldom/qqmldomastcreator.cpp
index 40665845fc..67e3acff1c 100644
--- a/src/qmldom/qqmldomastcreator.cpp
+++ b/src/qmldom/qqmldomastcreator.cpp
@@ -1,12 +1,21 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmldomastcreator_p.h"
+#include "qqmldomconstants_p.h"
#include "qqmldomelements_p.h"
+#include "qqmldomitem_p.h"
+#include "qqmldompath_p.h"
+#include "qqmldomscriptelements_p.h"
#include "qqmldomtop_p.h"
#include "qqmldomerrormessage_p.h"
#include "qqmldomastdumper_p.h"
#include "qqmldomattachedinfo_p.h"
+#include "qqmldomastcreator_p.h"
+#include "qqmldom_utils_p.h"
#include <QtQml/private/qqmljsast_p.h>
+#include <QtQmlCompiler/private/qqmljsutils_p.h>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
@@ -14,10 +23,32 @@
#include <QtCore/QLoggingCategory>
#include <memory>
+#include <optional>
+#include <type_traits>
#include <variant>
+#include <vector>
static Q_LOGGING_CATEGORY(creatorLog, "qt.qmldom.astcreator", QtWarningMsg);
+/*
+ Avoid crashing on files with JS-elements that are not implemented yet.
+ Might be removed (definition + usages) once all script elements are implemented.
+*/
+#define Q_SCRIPTELEMENT_DISABLE() \
+ do { \
+ qDebug() << "Could not construct the JS DOM at" << __FILE__ << ":" << __LINE__ \
+ << ", skipping JS elements..."; \
+ disableScriptElements(); \
+ } while (false)
+
+#define Q_SCRIPTELEMENT_EXIT_IF(check) \
+ do { \
+ if (m_enableScriptExpressions && (check)) { \
+ Q_SCRIPTELEMENT_DISABLE(); \
+ return; \
+ } \
+ } while (false)
+
QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
@@ -46,7 +77,7 @@ V *valueFromMultimap(QMultiMap<K, V> &mmap, const K &key, index_type idx)
return &(*it);
}
-static ErrorGroups myParseErrors()
+static ErrorGroups astParseErrors()
{
static ErrorGroups errs = { { NewErrorGroup("Dom"), NewErrorGroup("QmlFile"),
NewErrorGroup("Parsing") } };
@@ -88,879 +119,2934 @@ SourceLocation combineLocations(Node *n)
return combineLocations(n->firstSourceLocation(), n->lastSourceLocation());
}
-class DomValue
+static ScriptElementVariant wrapIntoFieldMemberExpression(const ScriptElementVariant &left,
+ const SourceLocation &dotToken,
+ const ScriptElementVariant &right)
{
-public:
- template<typename T>
- DomValue(const T &obj) : kind(T::kindValue), value(obj)
- {
- }
- DomType kind;
- std::variant<QmlObject, MethodInfo, QmlComponent, PropertyDefinition, Binding, EnumDecl,
- EnumItem, ConstantData, Id>
- value;
+ SourceLocation s1, s2;
+ left.visitConst([&s1](auto &&el) { s1 = el->mainRegionLocation(); });
+ right.visitConst([&s2](auto &&el) { s2 = el->mainRegionLocation(); });
+
+ auto result = std::make_shared<ScriptElements::BinaryExpression>(s1, s2);
+ result->addLocation(OperatorTokenRegion, dotToken);
+ result->setOp(ScriptElements::BinaryExpression::FieldMemberAccess);
+ result->setLeft(left);
+ result->setRight(right);
+ return ScriptElementVariant::fromElement(result);
};
-class StackEl
+/*!
+ \internal
+ Creates a FieldMemberExpression if the qualified id has dots.
+*/
+static ScriptElementVariant
+fieldMemberExpressionForQualifiedId(const AST::UiQualifiedId *qualifiedId)
{
-public:
- Path path;
- DomValue item;
- FileLocations::Tree fileLocations;
-};
+ ScriptElementVariant bindable;
+ bool first = true;
+ for (auto exp = qualifiedId; exp; exp = exp->next) {
+ const SourceLocation identifierLoc = exp->identifierToken;
+ auto id = std::make_shared<ScriptElements::IdentifierExpression>(identifierLoc);
+ id->setName(exp->name);
+ if (first) {
+ first = false;
+ bindable = ScriptElementVariant::fromElement(id);
+ continue;
+ }
+ bindable = wrapIntoFieldMemberExpression(bindable, exp->dotToken,
+ ScriptElementVariant::fromElement(id));
+ }
+
+ return bindable;
+}
-class QmlDomAstCreator final : public AST::Visitor
+QQmlDomAstCreator::QmlStackElement &QQmlDomAstCreator::currentQmlObjectOrComponentEl(int idx)
{
- Q_DECLARE_TR_FUNCTIONS(QmlDomAstCreator)
+ Q_ASSERT_X(idx < nodeStack.size() && idx >= 0, "currentQmlObjectOrComponentEl",
+ "Stack does not contain enough elements!");
+ int i = nodeStack.size() - idx;
+ while (i-- > 0) {
+ DomType k = nodeStack.at(i).item.kind;
+ if (k == DomType::QmlObject || k == DomType::QmlComponent)
+ return nodeStack[i];
+ }
+ Q_ASSERT_X(false, "currentQmlObjectEl", "No QmlObject or component in stack");
+ return nodeStack.last();
+}
- static constexpr const auto className = "QmlDomAstCreator";
+QQmlDomAstCreator::QmlStackElement &QQmlDomAstCreator::currentNodeEl(int i)
+{
+ Q_ASSERT_X(i < nodeStack.size() && i >= 0, "currentNode", "Stack does not contain element!");
+ return nodeStack[nodeStack.size() - i - 1];
+}
- MutableDomItem qmlFile;
- std::shared_ptr<QmlFile> qmlFilePtr;
- QVector<StackEl> nodeStack;
- QVector<int> arrayBindingLevels;
- FileLocations::Tree rootMap;
+QQmlDomAstCreator::ScriptStackElement &QQmlDomAstCreator::currentScriptNodeEl(int i)
+{
+ Q_ASSERT_X(i < scriptNodeStack.size() && i >= 0, "currentNode",
+ "Stack does not contain element!");
+ return scriptNodeStack[scriptNodeStack.size() - i - 1];
+}
- template<typename T>
- StackEl &currentEl(int idx = 0)
- {
- Q_ASSERT_X(idx < nodeStack.size() && idx >= 0, "currentQmlObjectOrComponentEl",
- "Stack does not contain enough elements!");
- int i = nodeStack.size() - idx;
- while (i-- > 0) {
- DomType k = nodeStack.at(i).item.kind;
- if (k == T::kindValue)
- return nodeStack[i];
+QQmlDomAstCreator::DomValue &QQmlDomAstCreator::currentNode(int i)
+{
+ Q_ASSERT_X(i < nodeStack.size() && i >= 0, "currentNode",
+ "Stack does not contain element!");
+ return nodeStack[nodeStack.size() - i - 1].item;
+}
+
+void QQmlDomAstCreator::removeCurrentNode(std::optional<DomType> expectedType)
+{
+ Q_ASSERT_X(!nodeStack.isEmpty(), className, "popCurrentNode() without any node");
+ if (expectedType)
+ Q_ASSERT(nodeStack.last().item.kind == *expectedType);
+ nodeStack.removeLast();
+}
+
+void QQmlDomAstCreator::removeCurrentScriptNode(std::optional<DomType> expectedType)
+{
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ Q_ASSERT_X(!scriptNodeStack.isEmpty(), className,
+ "popCurrentScriptNode() without any node");
+ if (expectedType)
+ Q_ASSERT(scriptNodeStack.last().kind == *expectedType);
+ scriptNodeStack.removeLast();
+}
+
+/*!
+ \internal
+ Prepares a script element DOM representation such that it can be used inside a QML DOM element.
+ This recursively sets the pathFromOwner and creates the FileLocations::Tree for all children of
+ element.
+
+ Beware that pathFromOwner is appended to ownerFileLocations when creating the FileLocations!
+
+ Make sure to add, for each of its use, a test in tst_qmldomitem:finalizeScriptExpressions, as
+ using a wrong pathFromOwner and/or a wrong base might lead to bugs hard to debug and spurious
+ crashes.
+ */
+const ScriptElementVariant &
+QQmlDomAstCreator::finalizeScriptExpression(const ScriptElementVariant &element, const Path &pathFromOwner,
+ const FileLocations::Tree &ownerFileLocations)
+{
+ auto e = element.base();
+ Q_ASSERT(e);
+
+ qCDebug(creatorLog) << "Finalizing script expression with path:"
+ << ownerFileLocations->canonicalPathForTesting().append(
+ pathFromOwner.toString());
+ e->updatePathFromOwner(pathFromOwner);
+ e->createFileLocations(ownerFileLocations);
+ return element;
+}
+
+FileLocations::Tree QQmlDomAstCreator::createMap(const FileLocations::Tree &base, const Path &p, AST::Node *n)
+{
+ FileLocations::Tree res = FileLocations::ensure(base, p, AttachedInfo::PathType::Relative);
+ if (n)
+ FileLocations::addRegion(res, MainRegion, combineLocations(n));
+ return res;
+}
+
+FileLocations::Tree QQmlDomAstCreator::createMap(DomType k, const Path &p, AST::Node *n)
+{
+ Path relative;
+ FileLocations::Tree base;
+ switch (k) {
+ case DomType::QmlObject:
+ switch (currentNode().kind) {
+ case DomType::QmlObject:
+ case DomType::QmlComponent:
+ case DomType::PropertyDefinition:
+ case DomType::Binding:
+ case DomType::Id:
+ case DomType::MethodInfo:
+ break;
+ default:
+ qCWarning(domLog) << "unexpected type" << domTypeToString(currentNode().kind);
+ Q_UNREACHABLE();
}
- Q_ASSERT_X(false, "currentEl", "Stack does not contan object of type ");
- return nodeStack.last();
- }
+ base = currentNodeEl().fileLocations;
+ if (p.length() > 2) {
+ Path p2 = p[p.length() - 2];
+ if (p2.headKind() == Path::Kind::Field
+ && (p2.checkHeadName(Fields::children) || p2.checkHeadName(Fields::objects)
+ || p2.checkHeadName(Fields::value) || p2.checkHeadName(Fields::annotations)
+ || p2.checkHeadName(Fields::children)))
+ relative = p.mid(p.length() - 2, 2);
+ else if (p.last().checkHeadName(Fields::value)
+ && p.last().headKind() == Path::Kind::Field)
+ relative = p.last();
+ else {
+ qCWarning(domLog) << "unexpected path to QmlObject in createMap" << p;
+ Q_UNREACHABLE();
+ }
+ } else {
+ qCWarning(domLog) << "unexpected path to QmlObject in createMap" << p;
+ Q_UNREACHABLE();
+ }
+ break;
+ case DomType::EnumItem:
+ relative = p;
+ base = currentNodeEl().fileLocations;
+ break;
+ case DomType::QmlComponent:
+ case DomType::Pragma:
+ case DomType::Import:
+ case DomType::Id:
+ case DomType::EnumDecl:
+ relative = p;
+ base = rootMap;
+ break;
+ case DomType::Binding:
+ case DomType::PropertyDefinition:
+ case DomType::MethodInfo:
+ base = currentEl<QmlObject>().fileLocations;
+ if (p.length() > 3)
+ relative = p.mid(p.length() - 3, 3);
+ else
+ relative = p;
+ break;
- template<typename T>
- T &current(int idx = 0)
- {
- return std::get<T>(currentEl<T>(idx).item.value);
+ default:
+ qCWarning(domLog) << "Unexpected type in createMap:" << domTypeToString(k);
+ Q_UNREACHABLE();
+ break;
}
+ return createMap(base, relative, n);
+}
- index_type currentIndex() { return currentNodeEl().path.last().headIndex(); }
+QQmlDomAstCreator::QQmlDomAstCreator(const MutableDomItem &qmlFile)
+ : qmlFile(qmlFile),
+ qmlFilePtr(qmlFile.ownerAs<QmlFile>()),
+ rootMap(qmlFilePtr->fileLocationsTree())
+{
+}
- StackEl &currentQmlObjectOrComponentEl(int idx = 0)
- {
- Q_ASSERT_X(idx < nodeStack.size() && idx >= 0, "currentQmlObjectOrComponentEl",
- "Stack does not contain enough elements!");
- int i = nodeStack.size() - idx;
- while (i-- > 0) {
- DomType k = nodeStack.at(i).item.kind;
- if (k == DomType::QmlObject || k == DomType::QmlComponent)
- return nodeStack[i];
+bool QQmlDomAstCreator::visit(UiProgram *program)
+{
+ QFileInfo fInfo(qmlFile.canonicalFilePath());
+ QString componentName = fInfo.baseName();
+ QmlComponent *cPtr;
+ Path p = qmlFilePtr->addComponent(QmlComponent(componentName), AddOption::KeepExisting,
+ &cPtr);
+ MutableDomItem newC(qmlFile.item(), p);
+ Q_ASSERT_X(newC.item(), className, "could not recover component added with addComponent");
+ // QmlFile region == Component region == program span
+ // we hide the component span because the component s written after the imports
+ FileLocations::addRegion(rootMap, MainRegion, combineLocations(program));
+ pushEl(p, *cPtr, program);
+
+ auto envPtr = qmlFile.environment().ownerAs<DomEnvironment>();
+ const bool loadDependencies =
+ !envPtr->options().testFlag(DomEnvironment::Option::NoDependencies);
+ // add implicit directory import and load them in the Dom
+ if (!fInfo.canonicalPath().isEmpty()) {
+ Import selfDirImport(QmlUri::fromDirectoryString(fInfo.canonicalPath()));
+ selfDirImport.implicit = true;
+ qmlFilePtr->addImport(selfDirImport);
+
+ if (loadDependencies) {
+ const QString currentFileDir =
+ QFileInfo(qmlFile.canonicalFilePath()).dir().canonicalPath();
+ envPtr->loadFile(FileToLoad::fromFileSystem(
+ envPtr, selfDirImport.uri.absoluteLocalPath(currentFileDir)),
+ DomItem::Callback(), DomType::QmlDirectory);
}
- Q_ASSERT_X(false, "currentQmlObjectEl", "No QmlObject or component in stack");
- return nodeStack.last();
}
+ // add implicit imports from the environment (QML, QtQml for example) and load them in the Dom
+ for (Import i : qmlFile.environment().ownerAs<DomEnvironment>()->implicitImports()) {
+ i.implicit = true;
+ qmlFilePtr->addImport(i);
- StackEl &currentNodeEl(int i = 0)
- {
- Q_ASSERT_X(i < nodeStack.size() && i >= 0, "currentNode",
- "Stack does not contain element!");
- return nodeStack[nodeStack.size() - i - 1];
+ if (loadDependencies)
+ envPtr->loadModuleDependency(i.uri.moduleUri(), i.version, DomItem::Callback());
}
-
- DomValue &currentNode(int i = 0)
- {
- Q_ASSERT_X(i < nodeStack.size() && i >= 0, "currentNode",
- "Stack does not contain element!");
- return nodeStack[nodeStack.size() - i - 1].item;
+ if (m_loadFileLazily && loadDependencies) {
+ envPtr->loadPendingDependencies();
+ envPtr->commitToBase(qmlFile.environment().item());
}
- void removeCurrentNode(std::optional<DomType> expectedType)
- {
- Q_ASSERT_X(!nodeStack.isEmpty(), className, "popCurrentNode() without any node");
- if (expectedType)
- Q_ASSERT(nodeStack.last().item.kind == *expectedType);
- nodeStack.removeLast();
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiProgram *)
+{
+ MutableDomItem newC = qmlFile.path(currentNodeEl().path);
+ QmlComponent &comp = current<QmlComponent>();
+ for (const Pragma &p : qmlFilePtr->pragmas()) {
+ if (p.name.compare(u"singleton", Qt::CaseInsensitive) == 0) {
+ comp.setIsSingleton(true);
+ comp.setIsCreatable(false); // correct?
+ }
}
+ *newC.mutableAs<QmlComponent>() = comp;
+ removeCurrentNode(DomType::QmlComponent);
+ Q_ASSERT_X(nodeStack.isEmpty(), className, "ui program did not finish node stack");
+}
- void pushEl(Path p, DomValue it, AST::Node *n)
- {
- nodeStack.append({ p, it, createMap(it.kind, p, n) });
+bool QQmlDomAstCreator::visit(UiPragma *el)
+{
+ QStringList valueList;
+ for (auto t = el->values; t; t = t->next)
+ valueList << t->value.toString();
+
+ auto fileLocation = createMap(
+ DomType::Pragma, qmlFilePtr->addPragma(Pragma(el->name.toString(), valueList)), el);
+ FileLocations::addRegion(fileLocation, PragmaKeywordRegion, el->pragmaToken);
+ FileLocations::addRegion(fileLocation, IdentifierRegion, el->pragmaIdToken);
+ if (el->colonToken.isValid()) {
+ FileLocations::addRegion(fileLocation, ColonTokenRegion, el->colonToken);
}
+ int i = 0;
+ for (auto t = el->values; t; t = t->next) {
+ auto subMap = createMap(fileLocation, Path().field(Fields::values).index(i), t);
+ FileLocations::addRegion(subMap, PragmaValuesRegion, t->location);
+ ++i;
+ }
+
+ return true;
+}
- FileLocations::Tree createMap(FileLocations::Tree base, Path p, AST::Node *n)
- {
- FileLocations::Tree res = FileLocations::ensure(base, p, AttachedInfo::PathType::Relative);
- if (n)
- FileLocations::addRegion(res, QString(), combineLocations(n));
- return res;
+bool QQmlDomAstCreator::visit(UiImport *el)
+{
+ Version v(Version::Latest, Version::Latest);
+ if (el->version && el->version->version.hasMajorVersion())
+ v.majorVersion = el->version->version.majorVersion();
+ if (el->version && el->version->version.hasMinorVersion())
+ v.minorVersion = el->version->version.minorVersion();
+
+ auto envPtr = qmlFile.environment().ownerAs<DomEnvironment>();
+ const bool loadDependencies =
+ !envPtr->options().testFlag(DomEnvironment::Option::NoDependencies);
+ FileLocations::Tree fileLocation;
+ if (el->importUri != nullptr) {
+ const Import import =
+ Import::fromUriString(toString(el->importUri), v, el->importId.toString());
+ fileLocation = createMap(DomType::Import, qmlFilePtr->addImport(import), el);
+
+ if (loadDependencies) {
+ envPtr->loadModuleDependency(import.uri.moduleUri(), import.version,
+ DomItem::Callback());
+ }
+ FileLocations::addRegion(fileLocation, ImportUriRegion, combineLocations(el->importUri));
+ } else {
+ const Import import =
+ Import::fromFileString(el->fileName.toString(), el->importId.toString());
+ fileLocation = createMap(DomType::Import, qmlFilePtr->addImport(import), el);
+
+ if (loadDependencies) {
+ const QString currentFileDir =
+ QFileInfo(qmlFile.canonicalFilePath()).dir().canonicalPath();
+ envPtr->loadFile(FileToLoad::fromFileSystem(
+ envPtr, import.uri.absoluteLocalPath(currentFileDir)),
+ DomItem::Callback(), DomType::QmlDirectory);
+ }
+ FileLocations::addRegion(fileLocation, ImportUriRegion, el->fileNameToken);
+ }
+ if (m_loadFileLazily && loadDependencies) {
+ envPtr->loadPendingDependencies();
+ envPtr->commitToBase(qmlFile.environment().item());
}
- FileLocations::Tree createMap(DomType k, Path p, AST::Node *n)
- {
- FileLocations::Tree base;
- switch (k) {
- case DomType::QmlObject:
- switch (currentNode().kind) {
- case DomType::QmlObject:
- case DomType::QmlComponent:
- case DomType::PropertyDefinition:
- case DomType::Binding:
- case DomType::Id:
- case DomType::MethodInfo:
- break;
- default:
- qCWarning(domLog) << "unexpected type" << domTypeToString(currentNode().kind);
- Q_UNREACHABLE();
- }
- base = currentNodeEl().fileLocations;
- if (p.length() > 2) {
- Path p2 = p[p.length() - 2];
- if (p2.headKind() == Path::Kind::Field
- && (p2.checkHeadName(Fields::children) || p2.checkHeadName(Fields::objects)
- || p2.checkHeadName(Fields::value) || p2.checkHeadName(Fields::annotations)
- || p2.checkHeadName(Fields::children)))
- p = p.mid(p.length() - 2, 2);
- else if (p.last().checkHeadName(Fields::value)
- && p.last().headKind() == Path::Kind::Field)
- p = p.last();
- else {
- qCWarning(domLog) << "unexpected path to QmlObject in createMap" << p;
- Q_UNREACHABLE();
- }
- } else {
- qCWarning(domLog) << "unexpected path to QmlObject in createMap" << p;
- Q_UNREACHABLE();
- }
- break;
- case DomType::EnumItem:
- base = currentNodeEl().fileLocations;
- break;
- case DomType::QmlComponent:
- case DomType::Pragma:
- case DomType::Import:
- case DomType::Id:
- case DomType::EnumDecl:
- base = rootMap;
- break;
- case DomType::Binding:
- case DomType::PropertyDefinition:
- case DomType::MethodInfo:
- base = currentEl<QmlObject>().fileLocations;
- if (p.length() > 3)
- p = p.mid(p.length() - 3, 3);
- break;
- default:
- qCWarning(domLog) << "Unexpected type in createMap:" << domTypeToString(k);
- Q_UNREACHABLE();
- break;
+ if (el->importToken.isValid())
+ FileLocations::addRegion(fileLocation, ImportTokenRegion, el->importToken);
+
+ if (el->asToken.isValid())
+ FileLocations::addRegion(fileLocation, AsTokenRegion, el->asToken);
+
+ if (el->importIdToken.isValid())
+ FileLocations::addRegion(fileLocation, IdNameRegion, el->importIdToken);
+
+ if (el->version)
+ FileLocations::addRegion(fileLocation, VersionRegion, combineLocations(el->version));
+
+
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::UiPublicMember *el)
+{
+ switch (el->type) {
+ case AST::UiPublicMember::Signal: {
+ MethodInfo m;
+ m.name = el->name.toString();
+ m.typeName = toString(el->memberType);
+ m.isReadonly = el->isReadonly();
+ m.access = MethodInfo::Public;
+ m.methodType = MethodInfo::Signal;
+ m.isList = el->typeModifier == QLatin1String("list");
+ MethodInfo *mPtr;
+ Path p = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
+ pushEl(p, *mPtr, el);
+ FileLocations::addRegion(nodeStack.last().fileLocations, SignalKeywordRegion,
+ el->propertyToken());
+ FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion,
+ el->identifierToken);
+ MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
+ AST::UiParameterList *args = el->parameters;
+ while (args) {
+ MethodParameter param;
+ param.name = args->name.toString();
+ param.typeName = args->type ? args->type->toString() : QString();
+ index_type idx = index_type(mInfo.parameters.size());
+ mInfo.parameters.append(param);
+ auto argLocs = FileLocations::ensure(nodeStack.last().fileLocations,
+ Path::Field(Fields::parameters).index(idx),
+ AttachedInfo::PathType::Relative);
+ FileLocations::addRegion(argLocs, MainRegion, combineLocations(args));
+ FileLocations::addRegion(argLocs, IdentifierRegion, args->identifierToken);
+ if (args->type)
+ FileLocations::addRegion(argLocs, TypeIdentifierRegion, args->propertyTypeToken);
+ args = args->next;
}
- return createMap(base, p, n);
- }
-
-public:
- QmlDomAstCreator(MutableDomItem qmlFile)
- : qmlFile(qmlFile),
- qmlFilePtr(qmlFile.ownerAs<QmlFile>()),
- rootMap(qmlFilePtr->fileLocationsTree())
- {
- }
-
- bool visit(UiProgram *program) override
- {
- QFileInfo fInfo(qmlFile.canonicalFilePath());
- QString componentName = fInfo.baseName();
- QmlComponent *cPtr;
- Path p = qmlFilePtr->addComponent(QmlComponent(componentName), AddOption::KeepExisting,
- &cPtr);
- MutableDomItem newC(qmlFile.item(), p);
- Q_ASSERT_X(newC.item(), className, "could not recover component added with addComponent");
- // QmlFile region == Component region == program span
- // we hide the component span because the component s written after the imports
- FileLocations::addRegion(rootMap, QString(), combineLocations(program));
- pushEl(p, *cPtr, program);
- // implicit imports
- // add implicit directory import
- if (!fInfo.canonicalPath().isEmpty()) {
- Import selfDirImport(QmlUri::fromDirectoryString(fInfo.canonicalPath()));
- selfDirImport.implicit = true;
- qmlFilePtr->addImport(selfDirImport);
- }
- for (Import i : qmlFile.environment().ownerAs<DomEnvironment>()->implicitImports()) {
- i.implicit = true;
- qmlFilePtr->addImport(i);
- }
- return true;
- }
-
- void endVisit(AST::UiProgram *) override
- {
- MutableDomItem newC = qmlFile.path(currentNodeEl().path);
- QmlComponent &comp = current<QmlComponent>();
- for (const Pragma &p : qmlFilePtr->pragmas()) {
- if (p.name.compare(u"singleton", Qt::CaseInsensitive) == 0) {
- comp.setIsSingleton(true);
- comp.setIsCreatable(false); // correct?
- }
+ break;
+ }
+ case AST::UiPublicMember::Property: {
+ PropertyDefinition p;
+ p.name = el->name.toString();
+ p.typeName = toString(el->memberType);
+ p.isReadonly = el->isReadonly();
+ p.isDefaultMember = el->isDefaultMember();
+ p.isRequired = el->isRequired();
+ p.isList = el->typeModifier == QLatin1String("list");
+ if (!el->typeModifier.isEmpty())
+ p.typeName = el->typeModifier.toString() + QChar(u'<') + p.typeName + QChar(u'>');
+ PropertyDefinition *pPtr;
+ Path pPathFromOwner =
+ current<QmlObject>().addPropertyDef(p, AddOption::KeepExisting, &pPtr);
+ if (m_enableScriptExpressions) {
+ auto qmlObjectType = makeGenericScriptElement(el->memberType, DomType::ScriptType);
+ qmlObjectType->insertChild(Fields::typeName,
+ fieldMemberExpressionForQualifiedId(el->memberType));
+ pPtr->setNameIdentifiers(finalizeScriptExpression(
+ ScriptElementVariant::fromElement(qmlObjectType),
+ pPathFromOwner.field(Fields::nameIdentifiers), rootMap));
+ // skip binding identifiers of the binding inside the property definition, if there is
+ // one
+ m_skipBindingIdentifiers = el->binding;
}
- *newC.mutableAs<QmlComponent>() = comp;
- removeCurrentNode(DomType::QmlComponent);
- Q_ASSERT_X(nodeStack.isEmpty(), className, "ui program did not finish node stack");
- }
-
- bool visit(UiPragma *el) override
- {
- createMap(DomType::Pragma, qmlFilePtr->addPragma(Pragma(el->name.toString())), el);
- return true;
- }
-
- bool visit(UiImport *el) override
- {
- Version v(Version::Latest, Version::Latest);
- if (el->version && el->version->version.hasMajorVersion())
- v.majorVersion = el->version->version.majorVersion();
- if (el->version && el->version->version.hasMinorVersion())
- v.minorVersion = el->version->version.minorVersion();
- if (el->importUri != nullptr)
- createMap(DomType::Import,
- qmlFilePtr->addImport(Import::fromUriString(toString(el->importUri), v,
- el->importId.toString())),
- el);
- else
- createMap(DomType::Import,
- qmlFilePtr->addImport(Import::fromFileString(
- el->fileName.toString(),
- el->importId.toString())),
- el);
- return true;
- }
-
- bool visit(AST::UiPublicMember *el) override
- {
- switch (el->type) {
- case AST::UiPublicMember::Signal: {
- MethodInfo m;
- m.name = el->name.toString();
- m.typeName = toString(el->memberType);
- m.isReadonly = el->isReadonly();
- m.access = MethodInfo::Public;
- m.methodType = MethodInfo::Signal;
- m.isList = el->typeModifier == QLatin1String("list");
- MethodInfo *mPtr;
- Path p = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
- pushEl(p, *mPtr, el);
- FileLocations::addRegion(nodeStack.last().fileLocations, u"signal", el->propertyToken());
- MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
- AST::UiParameterList *args = el->parameters;
- while (args) {
- MethodParameter param;
- param.name = args->name.toString();
- param.typeName = args->type ? args->type->toString() : QString();
- index_type idx = index_type(mInfo.parameters.size());
- mInfo.parameters.append(param);
- auto argLocs = FileLocations::ensure(nodeStack.last().fileLocations,
- Path::Field(Fields::parameters).index(idx),
- AttachedInfo::PathType::Relative);
- FileLocations::addRegion(argLocs, QString(), combineLocations(args));
- args = args->next;
- }
- break;
+ pushEl(pPathFromOwner, *pPtr, el);
+ FileLocations::addRegion(nodeStack.last().fileLocations, PropertyKeywordRegion,
+ el->propertyToken());
+ FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion,
+ el->identifierToken);
+ FileLocations::addRegion(nodeStack.last().fileLocations, TypeIdentifierRegion,
+ el->typeToken);
+ FileLocations::addRegion(nodeStack.last().fileLocations, ColonTokenRegion, el->colonToken);
+ if (p.name == u"id")
+ qmlFile.addError(std::move(astParseErrors()
+ .warning(tr("id is a special attribute, that should not be "
+ "used as property name"))
+ .withPath(currentNodeEl().path)));
+ if (p.isDefaultMember) {
+ FileLocations::addRegion(nodeStack.last().fileLocations, DefaultKeywordRegion,
+ el->defaultToken());
}
- case AST::UiPublicMember::Property: {
- PropertyDefinition p;
- p.name = el->name.toString();
- p.typeName = toString(el->memberType);
- p.isReadonly = el->isReadonly();
- p.isDefaultMember = el->isDefaultMember();
- p.isRequired = el->isRequired();
- p.isList = el->typeModifier == QLatin1String("list");
- if (!el->typeModifier.isEmpty())
- p.typeName = el->typeModifier.toString() + QChar(u'<') + p.typeName + QChar(u'>');
- PropertyDefinition *pPtr;
- Path pPathFromOwner =
- current<QmlObject>().addPropertyDef(p, AddOption::KeepExisting, &pPtr);
- pushEl(pPathFromOwner, *pPtr, el);
- FileLocations::addRegion(nodeStack.last().fileLocations, u"property",
- el->propertyToken());
- if (p.name == u"id")
- qmlFile.addError(
- myParseErrors()
- .warning(tr("id is a special attribute, that should not be "
- "used as property name"))
- .withPath(currentNodeEl().path));
- if (p.isDefaultMember)
- FileLocations::addRegion(nodeStack.last().fileLocations, u"default",
- el->defaultToken());
- if (p.isRequired)
- FileLocations::addRegion(nodeStack.last().fileLocations, u"required",
- el->requiredToken());
- if (el->statement) {
- BindingType bType = BindingType::Normal;
- SourceLocation loc = combineLocations(el->statement);
- QStringView code = qmlFilePtr->code();
-
- auto script = std::make_shared<ScriptExpression>(
- code.mid(loc.offset, loc.length), qmlFilePtr->engine(), el->statement,
- qmlFilePtr->astComments(),
- ScriptExpression::ExpressionType::BindingExpression, loc);
- Binding *bPtr;
- Path bPathFromOwner = current<QmlObject>().addBinding(
- Binding(p.name, script, bType), AddOption::KeepExisting, &bPtr);
- FileLocations::Tree bLoc = createMap(DomType::Binding, bPathFromOwner, el);
- FileLocations::addRegion(bLoc, u"colon", el->colonToken);
- FileLocations::Tree valueLoc = FileLocations::ensure(
- bLoc, Path::Field(Fields::value), AttachedInfo::PathType::Relative);
- FileLocations::addRegion(valueLoc, QString(), combineLocations(el->statement));
- }
- break;
+ if (p.isRequired) {
+ FileLocations::addRegion(nodeStack.last().fileLocations, RequiredKeywordRegion,
+ el->requiredToken());
}
+ if (p.isReadonly) {
+ FileLocations::addRegion(nodeStack.last().fileLocations, ReadonlyKeywordRegion,
+ el->readonlyToken());
}
- return true;
- }
-
- void endVisit(AST::UiPublicMember *el) override
- {
- Node::accept(el->parameters, this);
- loadAnnotations(el);
- if ((el->binding || el->statement)
- && nodeStack.last().item.kind == DomType::PropertyDefinition) {
- PropertyDefinition &pDef = std::get<PropertyDefinition>(nodeStack.last().item.value);
- if (!pDef.annotations.isEmpty()) {
- QmlObject duplicate;
- duplicate.setName(QLatin1String("duplicate"));
- QmlObject &obj = current<QmlObject>();
- auto it = obj.m_bindings.find(pDef.name);
- if (it != obj.m_bindings.end()) {
- for (QmlObject ann : pDef.annotations) {
- ann.addAnnotation(duplicate);
- it->addAnnotation(
- currentEl<QmlObject>()
- .path.field(Fields::bindings)
- .key(pDef.name)
- .index(obj.m_bindings.values(pDef.name).size() - 1),
- ann);
- }
- }
- }
+ if (el->statement) {
+ BindingType bType = BindingType::Normal;
+ SourceLocation loc = combineLocations(el->statement);
+ QStringView code = qmlFilePtr->code();
+
+ auto script = std::make_shared<ScriptExpression>(
+ code.mid(loc.offset, loc.length), qmlFilePtr->engine(), el->statement,
+ qmlFilePtr->astComments(), ScriptExpression::ExpressionType::BindingExpression,
+ loc);
+ Binding *bPtr;
+ Path bPathFromOwner = current<QmlObject>().addBinding(Binding(p.name, script, bType),
+ AddOption::KeepExisting, &bPtr);
+ FileLocations::Tree bLoc = createMap(DomType::Binding, bPathFromOwner, el);
+ FileLocations::addRegion(bLoc, ColonTokenRegion, el->colonToken);
+ FileLocations::Tree valueLoc = FileLocations::ensure(bLoc, Path::Field(Fields::value),
+ AttachedInfo::PathType::Relative);
+ FileLocations::addRegion(valueLoc, MainRegion, combineLocations(el->statement));
+ // push it also: its needed in endVisit to add the scriptNode to it
+ // do not use pushEl to avoid recreating the already created "bLoc" Map
+ nodeStack.append({ bPathFromOwner, *bPtr, bLoc });
}
- QmlObject &obj = current<QmlObject>();
- StackEl &sEl = nodeStack.last();
- switch (sEl.item.kind) {
- case DomType::PropertyDefinition: {
- PropertyDefinition pDef = std::get<PropertyDefinition>(sEl.item.value);
- PropertyDefinition *pDefPtr =
- valueFromMultimap(obj.m_propertyDefs, pDef.name, sEl.path.last().headIndex());
- Q_ASSERT(pDefPtr);
- *pDefPtr = pDef;
- } break;
- case DomType::MethodInfo: {
- MethodInfo m = std::get<MethodInfo>(sEl.item.value);
- MethodInfo *mPtr =
- valueFromMultimap(obj.m_methods, m.name, sEl.path.last().headIndex());
- Q_ASSERT(mPtr);
- *mPtr = m;
- } break;
- default:
- Q_UNREACHABLE();
+ break;
+ }
+ }
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiPublicMember *el)
+{
+ if (auto &lastEl = currentNode(); lastEl.kind == DomType::Binding) {
+ Binding &b = std::get<Binding>(lastEl.value);
+ if (m_enableScriptExpressions
+ && (scriptNodeStack.size() != 1 || scriptNodeStack.last().isList())) {
+ Q_SCRIPTELEMENT_DISABLE();
+ }
+ if (m_enableScriptExpressions) {
+ b.scriptExpressionValue()->setScriptElement(finalizeScriptExpression(
+ currentScriptNodeEl().takeVariant(), Path().field(Fields::scriptElement),
+ FileLocations::ensure(currentNodeEl().fileLocations,
+ Path().field(Fields::value))));
+ removeCurrentScriptNode({});
}
+
+ QmlObject &containingObject = current<QmlObject>();
+ Binding *bPtr =
+ valueFromMultimap(containingObject.m_bindings, b.name(), currentIndex());
+ Q_ASSERT(bPtr);
removeCurrentNode({});
}
-
- bool visit(AST::UiSourceElement *el) override
- {
- QStringView code(qmlFilePtr->code());
- if (FunctionDeclaration *fDef = cast<FunctionDeclaration *>(el->sourceElement)) {
- MethodInfo m;
- m.name = fDef->name.toString();
- if (AST::TypeAnnotation *tAnn = fDef->typeAnnotation) {
- if (AST::Type *t = tAnn->type)
- m.typeName = typeToString(t);
- }
- m.access = MethodInfo::Public;
- m.methodType = MethodInfo::Method;
- if (fDef->body) {
- SourceLocation bodyLoc = combineLocations(fDef->body);
- SourceLocation methodLoc = combineLocations(el);
- QStringView preCode =
- code.mid(methodLoc.begin(), bodyLoc.begin() - methodLoc.begin());
- QStringView postCode = code.mid(bodyLoc.end(), methodLoc.end() - bodyLoc.end());
- m.body = std::make_shared<ScriptExpression>(
- code.mid(bodyLoc.offset, bodyLoc.length), qmlFilePtr->engine(), fDef->body,
- qmlFilePtr->astComments(), ScriptExpression::ExpressionType::FunctionBody,
- bodyLoc, 0, preCode, postCode);
- }
- MethodInfo *mPtr;
- Path mPathFromOwner = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
- pushEl(mPathFromOwner, *mPtr,
- fDef); // add at the start and use the normal recursive visit?
- FileLocations::Tree &fLoc = nodeStack.last().fileLocations;
- if (fDef->lparenToken.length != 0)
- FileLocations::addRegion(fLoc, u"leftParen", fDef->lparenToken);
- if (fDef->rparenToken.length != 0)
- FileLocations::addRegion(fLoc, u"rightParen", fDef->rparenToken);
- if (fDef->lbraceToken.length != 0)
- FileLocations::addRegion(fLoc, u"leftBrace", fDef->lbraceToken);
- if (fDef->rbraceToken.length != 0)
- FileLocations::addRegion(fLoc, u"rightBrace", fDef->rbraceToken);
- loadAnnotations(el);
- MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
- AST::FormalParameterList *args = fDef->formals;
- while (args) {
- MethodParameter param;
- param.name = args->element->bindingIdentifier.toString();
- if (AST::TypeAnnotation *tAnn = args->element->typeAnnotation) {
- if (AST::Type *t = tAnn->type)
- param.typeName = typeToString(t);
- }
- if (args->element->initializer) {
- SourceLocation loc = combineLocations(args->element->initializer);
- auto script = std::make_shared<ScriptExpression>(
- code.mid(loc.offset, loc.length), qmlFilePtr->engine(),
- args->element->initializer, qmlFilePtr->astComments(),
- ScriptExpression::ExpressionType::ArgInitializer, loc);
- param.defaultValue = script;
+ Node::accept(el->parameters, this);
+ loadAnnotations(el);
+ if ((el->binding || el->statement)
+ && nodeStack.last().item.kind == DomType::PropertyDefinition) {
+ PropertyDefinition &pDef = std::get<PropertyDefinition>(nodeStack.last().item.value);
+ if (!pDef.annotations.isEmpty()) {
+ QmlObject duplicate;
+ duplicate.setName(QLatin1String("duplicate"));
+ QmlObject &obj = current<QmlObject>();
+ auto it = obj.m_bindings.find(pDef.name);
+ if (it != obj.m_bindings.end()) {
+ for (QmlObject ann : pDef.annotations) {
+ ann.addAnnotation(duplicate);
+ it->addAnnotation(currentEl<QmlObject>()
+ .path.field(Fields::bindings)
+ .key(pDef.name)
+ .index(obj.m_bindings.values(pDef.name).size() - 1),
+ ann);
}
- index_type idx = index_type(mInfo.parameters.size());
- mInfo.parameters.append(param);
- auto argLocs = FileLocations::ensure(nodeStack.last().fileLocations,
- Path::Field(Fields::parameters).index(idx),
- AttachedInfo::PathType::Relative);
- FileLocations::addRegion(argLocs, QString(), combineLocations(args));
- args = args->next;
}
- return false;
- } else {
- qCWarning(creatorLog) << "unhandled source el:" << static_cast<AST::Node *>(el);
- Q_UNREACHABLE();
}
- return true;
}
-
- void endVisit(AST::UiSourceElement *) override
- {
- MethodInfo &m = std::get<MethodInfo>(currentNode().value);
- QmlObject &obj = current<QmlObject>();
- MethodInfo *mPtr =
- valueFromMultimap(obj.m_methods, m.name, nodeStack.last().path.last().headIndex());
+ QmlObject &obj = current<QmlObject>();
+ QmlStackElement &sEl = nodeStack.last();
+ switch (sEl.item.kind) {
+ case DomType::PropertyDefinition: {
+ PropertyDefinition pDef = std::get<PropertyDefinition>(sEl.item.value);
+ PropertyDefinition *pDefPtr =
+ valueFromMultimap(obj.m_propertyDefs, pDef.name, sEl.path.last().headIndex());
+ Q_ASSERT(pDefPtr);
+ *pDefPtr = pDef;
+ } break;
+ case DomType::MethodInfo: {
+ MethodInfo m = std::get<MethodInfo>(sEl.item.value);
+ MethodInfo *mPtr = valueFromMultimap(obj.m_methods, m.name, sEl.path.last().headIndex());
Q_ASSERT(mPtr);
*mPtr = m;
- removeCurrentNode(DomType::MethodInfo);
- }
-
- void loadAnnotations(UiObjectMember *el) { Node::accept(el->annotations, this); }
-
- bool visit(AST::UiObjectDefinition *el) override
- {
- QmlObject scope;
- scope.setName(toString(el->qualifiedTypeNameId));
- scope.addPrototypePath(Paths::lookupTypePath(scope.name()));
- QmlObject *sPtr = nullptr;
- Path sPathFromOwner;
- if (!arrayBindingLevels.isEmpty() && nodeStack.size() == arrayBindingLevels.last()) {
- if (currentNode().kind == DomType::Binding) {
- QList<QmlObject> *vals = std::get<Binding>(currentNode().value).arrayValue();
- if (vals) {
- int idx = vals->size();
- vals->append(scope);
- sPathFromOwner = currentNodeEl().path.field(Fields::value).index(idx);
- sPtr = &((*vals)[idx]);
- sPtr->updatePathFromOwner(sPathFromOwner);
- } else {
- Q_ASSERT_X(false, className,
- "expected an array binding with a valid QList<QmlScope> as value");
- }
- } else {
- Q_ASSERT_X(false, className, "expected an array binding as last node on the stack");
- }
+ } break;
+ default:
+ Q_UNREACHABLE();
+ }
+ removeCurrentNode({});
+}
+
+bool QQmlDomAstCreator::visit(AST::FunctionDeclaration *fDef)
+{
+ const QStringView code(qmlFilePtr->code());
+ MethodInfo m;
+ m.name = fDef->name.toString();
+ if (AST::TypeAnnotation *tAnn = fDef->typeAnnotation) {
+ if (AST::Type *t = tAnn->type)
+ m.typeName = typeToString(t);
+ }
+ m.access = MethodInfo::Public;
+ m.methodType = MethodInfo::Method;
+
+ SourceLocation bodyLoc = fDef->body ? combineLocations(fDef->body)
+ : combineLocations(fDef->lbraceToken, fDef->rbraceToken);
+ SourceLocation methodLoc = combineLocations(fDef);
+ QStringView preCode = code.mid(methodLoc.begin(), bodyLoc.begin() - methodLoc.begin());
+ QStringView postCode = code.mid(bodyLoc.end(), methodLoc.end() - bodyLoc.end());
+ m.body = std::make_shared<ScriptExpression>(
+ code.mid(bodyLoc.offset, bodyLoc.length), qmlFilePtr->engine(), fDef->body,
+ qmlFilePtr->astComments(), ScriptExpression::ExpressionType::FunctionBody, bodyLoc, 0,
+ preCode, postCode);
+
+ if (fDef->typeAnnotation) {
+ SourceLocation typeLoc = combineLocations(fDef->typeAnnotation);
+ m.returnType = std::make_shared<ScriptExpression>(
+ code.mid(typeLoc.offset, typeLoc.length), qmlFilePtr->engine(),
+ fDef->typeAnnotation, qmlFilePtr->astComments(),
+ ScriptExpression::ExpressionType::ReturnType, typeLoc, 0, u"", u"");
+ }
+
+ MethodInfo *mPtr;
+ Path mPathFromOwner = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
+ pushEl(mPathFromOwner, *mPtr,
+ fDef); // add at the start and use the normal recursive visit?
+ FileLocations::Tree &fLoc = nodeStack.last().fileLocations;
+ if (fDef->identifierToken.isValid())
+ FileLocations::addRegion(fLoc, IdentifierRegion, fDef->identifierToken);
+ auto bodyTree = FileLocations::ensure(fLoc, Path::Field(Fields::body),
+ AttachedInfo::PathType::Relative);
+ FileLocations::addRegion(bodyTree, MainRegion, bodyLoc);
+ if (fDef->functionToken.isValid())
+ FileLocations::addRegion(fLoc, FunctionKeywordRegion, fDef->functionToken);
+ if (fDef->lparenToken.length != 0)
+ FileLocations::addRegion(fLoc, LeftParenthesisRegion, fDef->lparenToken);
+ if (fDef->rparenToken.length != 0)
+ FileLocations::addRegion(fLoc, RightParenthesisRegion, fDef->rparenToken);
+ if (fDef->lbraceToken.length != 0)
+ FileLocations::addRegion(fLoc, LeftBraceRegion, fDef->lbraceToken);
+ if (fDef->rbraceToken.length != 0)
+ FileLocations::addRegion(fLoc, RightBraceRegion, fDef->rbraceToken);
+ if (fDef->typeAnnotation)
+ FileLocations::addRegion(fLoc, TypeIdentifierRegion, combineLocations(fDef->typeAnnotation->type));
+ MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
+ AST::FormalParameterList *args = fDef->formals;
+ while (args) {
+ MethodParameter param;
+ param.name = args->element->bindingIdentifier.toString();
+ if (AST::TypeAnnotation *tAnn = args->element->typeAnnotation) {
+ if (AST::Type *t = tAnn->type)
+ param.typeName = typeToString(t);
+ }
+ if (args->element->initializer) {
+ SourceLocation loc = combineLocations(args->element->initializer);
+ auto script = std::make_shared<ScriptExpression>(
+ code.mid(loc.offset, loc.length), qmlFilePtr->engine(),
+ args->element->initializer, qmlFilePtr->astComments(),
+ ScriptExpression::ExpressionType::ArgInitializer, loc);
+ param.defaultValue = script;
+ }
+ if (args->element->type == AST::PatternElement::SpreadElement)
+ param.isRestElement = true;
+ SourceLocation parameterLoc = combineLocations(args->element);
+ param.value = std::make_shared<ScriptExpression>(
+ code.mid(parameterLoc.offset, parameterLoc.length), qmlFilePtr->engine(),
+ args->element, qmlFilePtr->astComments(),
+ ScriptExpression::ExpressionType::ArgumentStructure, parameterLoc);
+
+ index_type idx = index_type(mInfo.parameters.size());
+ mInfo.parameters.append(param);
+ auto argLocs = FileLocations::ensure(nodeStack.last().fileLocations,
+ Path::Field(Fields::parameters).index(idx),
+ AttachedInfo::PathType::Relative);
+ FileLocations::addRegion(argLocs, MainRegion, combineLocations(args));
+ if (args->element->identifierToken.isValid())
+ FileLocations::addRegion(argLocs, IdentifierRegion, args->element->identifierToken);
+ if (args->element->typeAnnotation)
+ FileLocations::addRegion(argLocs, TypeIdentifierRegion, combineLocations(args->element->typeAnnotation->type));
+ args = args->next;
+ }
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::UiSourceElement *el)
+{
+ if (!cast<FunctionDeclaration *>(el->sourceElement)) {
+ qCWarning(creatorLog) << "unhandled source el:" << static_cast<AST::Node *>(el);
+ Q_UNREACHABLE();
+ }
+ return true;
+}
+
+static void setFormalParameterKind(ScriptElementVariant &variant)
+{
+ if (auto data = variant.data()) {
+ if (auto genericElement =
+ std::get_if<std::shared_ptr<ScriptElements::GenericScriptElement>>(&*data)) {
+ (*genericElement)->setKind(DomType::ScriptFormalParameter);
+ }
+ }
+}
+
+void QQmlDomAstCreator::endVisit(AST::FunctionDeclaration *fDef)
+{
+ MethodInfo &m = std::get<MethodInfo>(currentNode().value);
+ const FileLocations::Tree bodyTree =
+ FileLocations::ensure(currentNodeEl().fileLocations, Path().field(Fields::body));
+ const Path bodyPath = Path().field(Fields::scriptElement);
+
+ if (!m_enableScriptExpressions)
+ return;
+
+ if (fDef->body) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ if (currentScriptNodeEl().isList()) {
+ // It is more intuitive to have functions with a block as a body instead of a
+ // list.
+ auto body = std::make_shared<ScriptElements::BlockStatement>(
+ combineLocations(fDef->lbraceToken, fDef->rbraceToken));
+ body->setStatements(currentScriptNodeEl().takeList());
+ if (auto semanticScope = body->statements().semanticScope())
+ body->setSemanticScope(semanticScope);
+ m.body->setScriptElement(finalizeScriptExpression(
+ ScriptElementVariant::fromElement(body), bodyPath, bodyTree));
} else {
- DomValue &containingObject = currentQmlObjectOrComponentEl().item;
- switch (containingObject.kind) {
- case DomType::QmlComponent:
- sPathFromOwner =
- std::get<QmlComponent>(containingObject.value).addObject(scope, &sPtr);
- break;
- case DomType::QmlObject:
- sPathFromOwner = std::get<QmlObject>(containingObject.value).addChild(scope, &sPtr);
- break;
- default:
- Q_UNREACHABLE();
- }
+ m.body->setScriptElement(finalizeScriptExpression(
+ currentScriptNodeEl().takeVariant(), bodyPath, bodyTree));
}
- Q_ASSERT_X(sPtr, className, "could not recover new scope");
- pushEl(sPathFromOwner, *sPtr, el);
- loadAnnotations(el);
- return true;
- }
-
- void endVisit(AST::UiObjectDefinition *) override
- {
- QmlObject &obj = current<QmlObject>();
- int idx = currentIndex();
- if (!arrayBindingLevels.isEmpty() && nodeStack.size() == arrayBindingLevels.last() + 1) {
- if (currentNode(1).kind == DomType::Binding) {
- Binding &b = std::get<Binding>(currentNode(1).value);
- QList<QmlObject> *vals = b.arrayValue();
- Q_ASSERT_X(vals, className,
- "expected an array binding with a valid QList<QmlScope> as value");
- (*vals)[idx] = obj;
+ removeCurrentScriptNode({});
+ } else {
+ // for convenience purposes: insert an empty BlockStatement
+ auto body = std::make_shared<ScriptElements::BlockStatement>(
+ combineLocations(fDef->lbraceToken, fDef->rbraceToken));
+ m.body->setScriptElement(finalizeScriptExpression(ScriptElementVariant::fromElement(body),
+ bodyPath, bodyTree));
+ }
+
+ if (fDef->typeAnnotation) {
+ auto argLoc = FileLocations::ensure(nodeStack.last().fileLocations,
+ Path().field(Fields::returnType),
+ AttachedInfo::PathType::Relative);
+ const Path pathToReturnType = Path().field(Fields::scriptElement);
+
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ ScriptElementVariant variant = currentScriptNodeEl().takeVariant();
+ finalizeScriptExpression(variant, pathToReturnType, argLoc);
+ m.returnType->setScriptElement(variant);
+ removeCurrentScriptNode({});
+ }
+ std::vector<FormalParameterList *> reversedInitializerExpressions;
+ for (auto it = fDef->formals; it; it = it->next) {
+ reversedInitializerExpressions.push_back(it);
+ }
+ const size_t size = reversedInitializerExpressions.size();
+ for (size_t idx = size - 1; idx < size; --idx) {
+ auto argLoc = FileLocations::ensure(
+ nodeStack.last().fileLocations,
+ Path().field(Fields::parameters).index(idx).field(Fields::value),
+ AttachedInfo::PathType::Relative);
+ const Path pathToArgument = Path().field(Fields::scriptElement);
+
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ ScriptElementVariant variant = currentScriptNodeEl().takeVariant();
+ setFormalParameterKind(variant);
+ finalizeScriptExpression(variant, pathToArgument, argLoc);
+ m.parameters[idx].value->setScriptElement(variant);
+ removeCurrentScriptNode({});
+ }
+
+ // there should be no more uncollected script elements
+ if (m_enableScriptExpressions && !scriptNodeStack.empty()) {
+ Q_SCRIPTELEMENT_DISABLE();
+ }
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiSourceElement *el)
+{
+ MethodInfo &m = std::get<MethodInfo>(currentNode().value);
+ loadAnnotations(el);
+ QmlObject &obj = current<QmlObject>();
+ MethodInfo *mPtr =
+ valueFromMultimap(obj.m_methods, m.name, nodeStack.last().path.last().headIndex());
+ Q_ASSERT(mPtr);
+ *mPtr = m;
+ removeCurrentNode(DomType::MethodInfo);
+}
+
+bool QQmlDomAstCreator::visit(AST::UiObjectDefinition *el)
+{
+ QmlObject scope;
+ scope.setName(toString(el->qualifiedTypeNameId));
+ scope.addPrototypePath(Paths::lookupTypePath(scope.name()));
+ QmlObject *sPtr = nullptr;
+ Path sPathFromOwner;
+ if (!arrayBindingLevels.isEmpty() && nodeStack.size() == arrayBindingLevels.last()) {
+ if (currentNode().kind == DomType::Binding) {
+ QList<QmlObject> *vals = std::get<Binding>(currentNode().value).arrayValue();
+ if (vals) {
+ int idx = vals->size();
+ vals->append(scope);
+ sPathFromOwner = currentNodeEl().path.field(Fields::value).index(idx);
+ sPtr = &((*vals)[idx]);
+ sPtr->updatePathFromOwner(sPathFromOwner);
} else {
- Q_ASSERT_X(false, className, "expected an array binding as last node on the stack");
+ Q_ASSERT_X(false, className,
+ "expected an array binding with a valid QList<QmlScope> as value");
}
} else {
- DomValue &containingObject = currentNodeEl(1).item;
- Path p = currentNodeEl().path;
- switch (containingObject.kind) {
- case DomType::QmlComponent:
- if (p[p.length() - 2] == Path::Field(Fields::objects))
- std::get<QmlComponent>(containingObject.value).m_objects[idx] = obj;
- else
- Q_UNREACHABLE();
- break;
- case DomType::QmlObject:
- if (p[p.length() - 2] == Path::Field(Fields::children))
- std::get<QmlObject>(containingObject.value).m_children[idx] = obj;
- else
- Q_UNREACHABLE();
- break;
- default:
+ Q_ASSERT_X(false, className, "expected an array binding as last node on the stack");
+ }
+ } else {
+ DomValue &containingObject = currentQmlObjectOrComponentEl().item;
+ switch (containingObject.kind) {
+ case DomType::QmlComponent:
+ sPathFromOwner = std::get<QmlComponent>(containingObject.value).addObject(scope, &sPtr);
+ break;
+ case DomType::QmlObject:
+ sPathFromOwner = std::get<QmlObject>(containingObject.value).addChild(scope, &sPtr);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ Path pathFromContainingObject = sPathFromOwner.mid(currentNodeEl().path.length());
+ FileLocations::Tree fLoc =
+ FileLocations::ensure(currentNodeEl().fileLocations, pathFromContainingObject,
+ AttachedInfo::PathType::Relative);
+ FileLocations::addRegion(fLoc, IdentifierRegion,
+ el->qualifiedTypeNameId->identifierToken);
+ }
+ Q_ASSERT_X(sPtr, className, "could not recover new scope");
+
+ if (m_enableScriptExpressions) {
+ auto qmlObjectType = makeGenericScriptElement(el->qualifiedTypeNameId, DomType::ScriptType);
+ qmlObjectType->insertChild(Fields::typeName,
+ fieldMemberExpressionForQualifiedId(el->qualifiedTypeNameId));
+ sPtr->setNameIdentifiers(
+ finalizeScriptExpression(ScriptElementVariant::fromElement(qmlObjectType),
+ sPathFromOwner.field(Fields::nameIdentifiers), rootMap));
+ }
+ pushEl(sPathFromOwner, *sPtr, el);
+
+ if (m_enableScriptExpressions && el->initializer) {
+ FileLocations::addRegion(nodeStack.last().fileLocations, LeftBraceRegion,
+ el->initializer->lbraceToken);
+ FileLocations::addRegion(nodeStack.last().fileLocations, RightBraceRegion,
+ el->initializer->rbraceToken);
+ }
+ loadAnnotations(el);
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiObjectDefinition *)
+{
+ QmlObject &obj = current<QmlObject>();
+ int idx = currentIndex();
+ if (!arrayBindingLevels.isEmpty() && nodeStack.size() == arrayBindingLevels.last() + 1) {
+ if (currentNode(1).kind == DomType::Binding) {
+ Binding &b = std::get<Binding>(currentNode(1).value);
+ QList<QmlObject> *vals = b.arrayValue();
+ Q_ASSERT_X(vals, className,
+ "expected an array binding with a valid QList<QmlScope> as value");
+ (*vals)[idx] = obj;
+ } else {
+ Q_ASSERT_X(false, className, "expected an array binding as last node on the stack");
+ }
+ } else {
+ DomValue &containingObject = currentNodeEl(1).item;
+ Path p = currentNodeEl().path;
+ switch (containingObject.kind) {
+ case DomType::QmlComponent:
+ if (p[p.length() - 2] == Path::Field(Fields::objects))
+ std::get<QmlComponent>(containingObject.value).m_objects[idx] = obj;
+ else
Q_UNREACHABLE();
- }
+ break;
+ case DomType::QmlObject:
+ if (p[p.length() - 2] == Path::Field(Fields::children))
+ std::get<QmlObject>(containingObject.value).m_children[idx] = obj;
+ else
+ Q_UNREACHABLE();
+ break;
+ default:
+ Q_UNREACHABLE();
}
- removeCurrentNode(DomType::QmlObject);
- }
-
- bool visit(AST::UiObjectBinding *el) override
- {
- BindingType bType = (el->hasOnToken ? BindingType::OnBinding : BindingType::Normal);
- QmlObject value;
- value.setName(toString(el->qualifiedTypeNameId));
- Binding *bPtr;
- Path bPathFromOwner = current<QmlObject>().addBinding(
- Binding(toString(el->qualifiedId), value, bType), AddOption::KeepExisting, &bPtr);
- if (bPtr->name() == u"id")
- qmlFile.addError(myParseErrors()
- .warning(tr("id attributes should only be a lower case letter "
- "followed by letters, numbers or underscore, "
- "assuming they refer to an id property"))
- .withPath(bPathFromOwner));
- pushEl(bPathFromOwner, *bPtr, el);
- FileLocations::addRegion(nodeStack.last().fileLocations, u"colon", el->colonToken);
- loadAnnotations(el);
- QmlObject *objValue = bPtr->objectValue();
- Q_ASSERT_X(objValue, className, "could not recover objectValue");
- pushEl(bPathFromOwner.field(Fields::value), *objValue, el->initializer);
- return true;
- }
-
- void endVisit(AST::UiObjectBinding *) override
- {
- QmlObject &objValue = current<QmlObject>();
- QmlObject &containingObj = current<QmlObject>(1);
- Binding &b = std::get<Binding>(currentNode(1).value);
- QmlObject *objPtr = b.objectValue();
- Q_ASSERT(objPtr);
- *objPtr = objValue;
- index_type idx = currentNodeEl(1).path.last().headIndex();
- Binding *bPtr = valueFromMultimap(containingObj.m_bindings, b.name(), idx);
- Q_ASSERT(bPtr);
- *bPtr = b;
- removeCurrentNode(DomType::QmlObject);
- removeCurrentNode(DomType::Binding);
- }
-
- bool visit(AST::UiScriptBinding *el) override
- {
- QStringView code = qmlFilePtr->code();
- SourceLocation loc = combineLocations(el->statement);
- auto script = std::make_shared<ScriptExpression>(
- code.mid(loc.offset, loc.length), qmlFilePtr->engine(),
- el->statement, qmlFilePtr->astComments(),
- ScriptExpression::ExpressionType::BindingExpression, loc);
- Binding bindingV(toString(el->qualifiedId), script, BindingType::Normal);
- Binding *bindingPtr = nullptr;
- Id *idPtr = nullptr;
- Path pathFromOwner;
- if (bindingV.name() == u"id") {
- Node *exp = script->ast();
- if (ExpressionStatement *eStat = cast<ExpressionStatement *>(script->ast()))
- exp = eStat->expression;
- if (IdentifierExpression *iExp = cast<IdentifierExpression *>(exp)) {
- StackEl &containingObjectEl = currentEl<QmlObject>();
- QmlObject &containingObject = std::get<QmlObject>(containingObjectEl.item.value);
- QString idName = iExp->name.toString();
- Id idVal(idName, qmlFile.canonicalPath().path(containingObject.pathFromOwner()));
- containingObject.setIdStr(idName);
- FileLocations::addRegion(containingObjectEl.fileLocations, u"idToken",
- combineLocations(el->qualifiedId));
- FileLocations::addRegion(containingObjectEl.fileLocations, u"idColon",
- el->colonToken);
- FileLocations::addRegion(containingObjectEl.fileLocations, u"id",
- combineLocations(el->statement));
- QmlComponent &comp = current<QmlComponent>();
- pathFromOwner = comp.addId(idVal, AddOption::KeepExisting, &idPtr);
- QRegularExpression idRe(QRegularExpression::anchoredPattern(
- QStringLiteral(uR"([[:lower:]][[:lower:][:upper:]0-9_]*)")));
- auto m = idRe.matchView(iExp->name);
- if (!m.hasMatch()) {
- qmlFile.addError(
- myParseErrors()
- .warning(
- tr("id attributes should only be a lower case letter "
- "followed by letters, numbers or underscore, not %1")
- .arg(iExp->name))
- .withPath(pathFromOwner));
- }
- } else {
- pathFromOwner = current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting,
- &bindingPtr);
- Q_ASSERT_X(bindingPtr, className, "binding could not be retrieved");
- qmlFile.addError(
- myParseErrors()
+ }
+ removeCurrentNode(DomType::QmlObject);
+}
+
+void QQmlDomAstCreator::setBindingIdentifiers(const Path &pathFromOwner,
+ const UiQualifiedId *identifiers, Binding *bindingPtr)
+{
+ const bool skipBindingIdentifiers = std::exchange(m_skipBindingIdentifiers, false);
+ if (!m_enableScriptExpressions || skipBindingIdentifiers)
+ return;
+
+ ScriptElementVariant bindable = fieldMemberExpressionForQualifiedId(identifiers);
+ bindingPtr->setBindingIdentifiers(finalizeScriptExpression(
+ bindable, pathFromOwner.field(Fields::bindingIdentifiers), rootMap));
+}
+
+bool QQmlDomAstCreator::visit(AST::UiObjectBinding *el)
+{
+ BindingType bType = (el->hasOnToken ? BindingType::OnBinding : BindingType::Normal);
+ QmlObject value;
+ value.setName(toString(el->qualifiedTypeNameId));
+ Binding *bPtr;
+ Path bPathFromOwner = current<QmlObject>().addBinding(
+ Binding(toString(el->qualifiedId), value, bType), AddOption::KeepExisting, &bPtr);
+ if (bPtr->name() == u"id")
+ qmlFile.addError(std::move(astParseErrors()
+ .warning(tr("id attributes should only be a lower case letter "
+ "followed by letters, numbers or underscore, "
+ "assuming they refer to an id property"))
+ .withPath(bPathFromOwner)));
+ setBindingIdentifiers(bPathFromOwner, el->qualifiedId, bPtr);
+
+ pushEl(bPathFromOwner, *bPtr, el);
+ if (el->hasOnToken)
+ FileLocations::addRegion(nodeStack.last().fileLocations, OnTokenRegion, el->colonToken);
+ else
+ FileLocations::addRegion(nodeStack.last().fileLocations, ColonTokenRegion, el->colonToken);
+ FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion, combineLocations(el->qualifiedId));
+ loadAnnotations(el);
+ QmlObject *objValue = bPtr->objectValue();
+ Q_ASSERT_X(objValue, className, "could not recover objectValue");
+ objValue->setName(toString(el->qualifiedTypeNameId));
+
+ if (m_enableScriptExpressions) {
+ auto qmlObjectType = makeGenericScriptElement(el->qualifiedTypeNameId, DomType::ScriptType);
+ qmlObjectType->insertChild(Fields::typeName,
+ fieldMemberExpressionForQualifiedId(el->qualifiedTypeNameId));
+ objValue->setNameIdentifiers(finalizeScriptExpression(
+ ScriptElementVariant::fromElement(qmlObjectType),
+ bPathFromOwner.field(Fields::value).field(Fields::nameIdentifiers), rootMap));
+ }
+
+ objValue->addPrototypePath(Paths::lookupTypePath(objValue->name()));
+ pushEl(bPathFromOwner.field(Fields::value), *objValue, el->initializer);
+ if (m_enableScriptExpressions && el->initializer) {
+ FileLocations::addRegion(nodeStack.last().fileLocations, LeftBraceRegion,
+ el->initializer->lbraceToken);
+ FileLocations::addRegion(nodeStack.last().fileLocations, RightBraceRegion,
+ el->initializer->rbraceToken);
+ }
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiObjectBinding *)
+{
+ QmlObject &objValue = current<QmlObject>();
+ QmlObject &containingObj = current<QmlObject>(1);
+ Binding &b = std::get<Binding>(currentNode(1).value);
+ QmlObject *objPtr = b.objectValue();
+ Q_ASSERT(objPtr);
+ *objPtr = objValue;
+ index_type idx = currentNodeEl(1).path.last().headIndex();
+ Binding *bPtr = valueFromMultimap(containingObj.m_bindings, b.name(), idx);
+ Q_ASSERT(bPtr);
+ *bPtr = b;
+ removeCurrentNode(DomType::QmlObject);
+ removeCurrentNode(DomType::Binding);
+}
+
+bool QQmlDomAstCreator::visit(AST::UiScriptBinding *el)
+{
+ QStringView code = qmlFilePtr->code();
+ SourceLocation loc = combineLocations(el->statement);
+ auto script = std::make_shared<ScriptExpression>(
+ code.mid(loc.offset, loc.length), qmlFilePtr->engine(), el->statement,
+ qmlFilePtr->astComments(), ScriptExpression::ExpressionType::BindingExpression, loc);
+ Binding bindingV(toString(el->qualifiedId), script, BindingType::Normal);
+ Binding *bindingPtr = nullptr;
+ Id *idPtr = nullptr;
+ Path pathFromOwner;
+ if (bindingV.name() == u"id") {
+ Node *exp = script->ast();
+ if (ExpressionStatement *eStat = cast<ExpressionStatement *>(script->ast()))
+ exp = eStat->expression;
+ if (IdentifierExpression *iExp = cast<IdentifierExpression *>(exp)) {
+ QmlStackElement &containingObjectEl = currentEl<QmlObject>();
+ QmlObject &containingObject = std::get<QmlObject>(containingObjectEl.item.value);
+ QString idName = iExp->name.toString();
+ Id idVal(idName, qmlFile.canonicalPath().path(containingObject.pathFromOwner()));
+ idVal.value = script;
+ containingObject.setIdStr(idName);
+ FileLocations::addRegion(containingObjectEl.fileLocations, IdTokenRegion,
+ combineLocations(el->qualifiedId));
+ FileLocations::addRegion(containingObjectEl.fileLocations, IdColonTokenRegion,
+ el->colonToken);
+ FileLocations::addRegion(containingObjectEl.fileLocations, IdNameRegion,
+ combineLocations(el->statement));
+ QmlComponent &comp = current<QmlComponent>();
+ pathFromOwner = comp.addId(idVal, AddOption::KeepExisting, &idPtr);
+ QRegularExpression idRe(QRegularExpression::anchoredPattern(
+ QStringLiteral(uR"([[:lower:]][[:lower:][:upper:]0-9_]*)")));
+ auto m = idRe.matchView(iExp->name);
+ if (!m.hasMatch()) {
+ qmlFile.addError(std::move(
+ astParseErrors()
.warning(tr("id attributes should only be a lower case letter "
- "followed by letters, numbers or underscore, not %1 "
- "%2, assuming they refer to a property")
- .arg(script->code(), script->astRelocatableDump()))
- .withPath(pathFromOwner));
+ "followed by letters, numbers or underscore, not %1")
+ .arg(iExp->name))
+ .withPath(pathFromOwner)));
}
} else {
pathFromOwner =
current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
Q_ASSERT_X(bindingPtr, className, "binding could not be retrieved");
+ qmlFile.addError(std::move(
+ astParseErrors()
+ .warning(tr("id attributes should only be a lower case letter "
+ "followed by letters, numbers or underscore, not %1 "
+ "%2, assuming they refer to a property")
+ .arg(script->code(), script->astRelocatableDump()))
+ .withPath(pathFromOwner)));
}
- if (bindingPtr)
- pushEl(pathFromOwner, *bindingPtr, el);
- else if (idPtr)
- pushEl(pathFromOwner, *idPtr, el);
- else
- Q_UNREACHABLE();
- loadAnnotations(el);
- // avoid duplicate colon location for id?
- FileLocations::addRegion(nodeStack.last().fileLocations, u"colon", el->colonToken);
- return false;
- }
-
- void endVisit(AST::UiScriptBinding *) override
- {
- DomValue &lastEl = currentNode();
- index_type idx = currentIndex();
- if (lastEl.kind == DomType::Binding) {
- Binding &b = std::get<Binding>(lastEl.value);
- QmlObject &containingObject = current<QmlObject>();
- Binding *bPtr = valueFromMultimap(containingObject.m_bindings, b.name(), idx);
- Q_ASSERT(bPtr);
- *bPtr = b;
- } else if (lastEl.kind == DomType::Id) {
- Id &id = std::get<Id>(lastEl.value);
- QmlComponent &comp = current<QmlComponent>();
- Id *idPtr = valueFromMultimap(comp.m_ids, id.name, idx);
- *idPtr = id;
- } else {
- Q_UNREACHABLE();
- }
- removeCurrentNode({});
+ } else {
+ pathFromOwner =
+ current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
+ QmlStackElement &containingObjectEl = currentEl<QmlObject>();
+ // remove the containingObjectEl.path prefix from pathFromOwner
+ Path pathFromContainingObject = pathFromOwner.mid(containingObjectEl.path.length());
+ auto bindingFileLocation =
+ FileLocations::ensure(containingObjectEl.fileLocations, pathFromContainingObject);
+ FileLocations::addRegion(bindingFileLocation, IdentifierRegion,
+ el->qualifiedId->identifierToken);
+ FileLocations::addRegion(bindingFileLocation, ColonTokenRegion, el->colonToken);
+
+ setBindingIdentifiers(pathFromOwner, el->qualifiedId, bindingPtr);
+
+ Q_ASSERT_X(bindingPtr, className, "binding could not be retrieved");
}
+ if (bindingPtr)
+ pushEl(pathFromOwner, *bindingPtr, el);
+ else if (idPtr)
+ pushEl(pathFromOwner, *idPtr, el);
+ else
+ Q_UNREACHABLE();
+ loadAnnotations(el);
+ // avoid duplicate colon location for id?
+ FileLocations::addRegion(nodeStack.last().fileLocations, ColonTokenRegion, el->colonToken);
+ return true;
+}
- bool visit(AST::UiArrayBinding *el) override
- {
- QList<QmlObject> value;
- Binding bindingV(toString(el->qualifiedId), value, BindingType::Normal);
- Binding *bindingPtr;
- Path bindingPathFromOwner =
- current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
- if (bindingV.name() == u"id")
- qmlFile.addError(
- myParseErrors()
- .error(tr("id attributes should have only simple strings as values"))
- .withPath(bindingPathFromOwner));
- pushEl(bindingPathFromOwner, *bindingPtr, el);
- FileLocations::addRegion(currentNodeEl().fileLocations, u"colon", el->colonToken);
- loadAnnotations(el);
- FileLocations::Tree arrayList =
- createMap(currentNodeEl().fileLocations, Path::Field(Fields::value), nullptr);
- FileLocations::addRegion(arrayList, u"leftSquareBrace", el->lbracketToken);
- FileLocations::addRegion(arrayList, u"rightSquareBrace", el->lbracketToken);
- arrayBindingLevels.append(nodeStack.size());
- return true;
- }
-
- void endVisit(AST::UiArrayBinding *) override
- {
- index_type idx = currentIndex();
- Binding &b = std::get<Binding>(currentNode().value);
- Binding *bPtr = valueFromMultimap(current<QmlObject>().m_bindings, b.name(), idx);
+void QQmlDomAstCreator::setScriptExpression (const std::shared_ptr<ScriptExpression>& value)
+{
+ if (m_enableScriptExpressions
+ && (scriptNodeStack.size() != 1 || currentScriptNodeEl().isList()))
+ Q_SCRIPTELEMENT_DISABLE();
+ if (m_enableScriptExpressions) {
+ FileLocations::Tree valueLoc = FileLocations::ensure(currentNodeEl().fileLocations,
+ Path().field(Fields::value));
+ value->setScriptElement(finalizeScriptExpression(currentScriptNodeEl().takeVariant(),
+ Path().field(Fields::scriptElement),
+ valueLoc));
+ removeCurrentScriptNode({});
+ }
+};
+
+void QQmlDomAstCreator::endVisit(AST::UiScriptBinding *)
+{
+ DomValue &lastEl = currentNode();
+ index_type idx = currentIndex();
+ if (lastEl.kind == DomType::Binding) {
+ Binding &b = std::get<Binding>(lastEl.value);
+
+ setScriptExpression(b.scriptExpressionValue());
+
+ QmlObject &containingObject = current<QmlObject>();
+ Binding *bPtr = valueFromMultimap(containingObject.m_bindings, b.name(), idx);
+ Q_ASSERT(bPtr);
*bPtr = b;
- arrayBindingLevels.removeLast();
- removeCurrentNode(DomType::Binding);
+ } else if (lastEl.kind == DomType::Id) {
+ Id &id = std::get<Id>(lastEl.value);
+
+ setScriptExpression(id.value);
+
+ QmlComponent &comp = current<QmlComponent>();
+ Id *idPtr = valueFromMultimap(comp.m_ids, id.name, idx);
+ *idPtr = id;
+ } else {
+ Q_UNREACHABLE();
}
- bool visit(AST::UiParameterList *el) override
- { // currently not used...
- MethodParameter p {
- el->name.toString(),
- el->type ? el->type->toString() : QString(),
- false, false, false, {}, {}, {}
- };
- return true;
- }
- void endVisit(AST::UiParameterList *el) override
- {
- Node::accept(el->next, this); // put other args at the same level as this one...
- }
-
- bool visit(AST::UiQualifiedId *) override { return false; }
-
- bool visit(AST::UiEnumDeclaration *el) override
- {
- EnumDecl eDecl;
- eDecl.setName(el->name.toString());
- EnumDecl *ePtr;
- Path enumPathFromOwner =
- current<QmlComponent>().addEnumeration(eDecl, AddOption::KeepExisting, &ePtr);
- pushEl(enumPathFromOwner, *ePtr, el);
- loadAnnotations(el);
- return true;
- }
-
- void endVisit(AST::UiEnumDeclaration *) override
- {
- EnumDecl &e = std::get<EnumDecl>(currentNode().value);
- EnumDecl *ePtr =
- valueFromMultimap(current<QmlComponent>().m_enumerations, e.name(), currentIndex());
- Q_ASSERT(ePtr);
- *ePtr = e;
- removeCurrentNode(DomType::EnumDecl);
- }
-
- bool visit(AST::UiEnumMemberList *el) override
- {
- EnumItem it(el->member.toString(), el->value);
- EnumDecl &eDecl = std::get<EnumDecl>(currentNode().value);
- Path itPathFromDecl = eDecl.addValue(it);
- FileLocations::addRegion(createMap(DomType::EnumItem, itPathFromDecl, nullptr), QString(),
- combine(el->memberToken, el->valueToken));
- return true;
- }
-
- void endVisit(AST::UiEnumMemberList *el) override
- {
- Node::accept(el->next, this); // put other enum members at the same level as this one...
- }
-
- bool visit(AST::UiInlineComponent *el) override
- {
- QStringList els = current<QmlComponent>().name().split(QLatin1Char('.'));
- els.append(el->name.toString());
- QString cName = els.join(QLatin1Char('.'));
- QmlComponent *compPtr;
- Path p = qmlFilePtr->addComponent(QmlComponent(cName), AddOption::KeepExisting, &compPtr);
- pushEl(p, *compPtr, el);
- FileLocations::addRegion(nodeStack.last().fileLocations, u"component", el->componentToken);
- loadAnnotations(el);
- return true;
- }
-
- void endVisit(AST::UiInlineComponent *) override
- {
- QmlComponent &component = std::get<QmlComponent>(currentNode().value);
- QStringList nameEls = component.name().split(QChar::fromLatin1('.'));
- QString key = nameEls.mid(1).join(QChar::fromLatin1('.'));
- QmlComponent *cPtr = valueFromMultimap(qmlFilePtr->m_components, key, currentIndex());
- Q_ASSERT(cPtr);
- *cPtr = component;
- removeCurrentNode(DomType::QmlComponent);
- }
-
- bool visit(UiRequired *el) override
- {
- PropertyDefinition pDef;
- pDef.name = el->name.toString();
- pDef.isRequired = true;
- PropertyDefinition *pDefPtr;
- Path pathFromOwner =
- current<QmlObject>().addPropertyDef(pDef, AddOption::KeepExisting, &pDefPtr);
- createMap(DomType::PropertyDefinition, pathFromOwner, el);
- return false;
- }
-
- bool visit(AST::UiAnnotation *el) override
- {
- QmlObject a;
- a.setName(QStringLiteral(u"@") + toString(el->qualifiedTypeNameId));
- // add annotation prototype?
- DomValue &containingElement = currentNode();
- Path pathFromOwner;
- QmlObject *aPtr = nullptr;
- switch (containingElement.kind) {
- case DomType::QmlObject:
- pathFromOwner = std::get<QmlObject>(containingElement.value).addAnnotation(a, &aPtr);
- break;
- case DomType::Binding:
- pathFromOwner = std::get<Binding>(containingElement.value)
- .addAnnotation(currentNodeEl().path, a, &aPtr);
- break;
- case DomType::Id:
- pathFromOwner = std::get<Id>(containingElement.value)
- .addAnnotation(currentNodeEl().path, a, &aPtr);
- break;
- case DomType::PropertyDefinition:
- pathFromOwner = std::get<PropertyDefinition>(containingElement.value)
- .addAnnotation(currentNodeEl().path, a, &aPtr);
- break;
- case DomType::MethodInfo:
- pathFromOwner = std::get<MethodInfo>(containingElement.value)
- .addAnnotation(currentNodeEl().path, a, &aPtr);
- break;
- default:
- qCWarning(domLog) << "Unexpected container object for annotation:"
- << domTypeToString(containingElement.kind);
- Q_UNREACHABLE();
+ // there should be no more uncollected script elements
+ if (m_enableScriptExpressions && !scriptNodeStack.empty()) {
+ Q_SCRIPTELEMENT_DISABLE();
+ }
+ removeCurrentNode({});
+}
+
+bool QQmlDomAstCreator::visit(AST::UiArrayBinding *el)
+{
+ QList<QmlObject> value;
+ Binding bindingV(toString(el->qualifiedId), value, BindingType::Normal);
+ Binding *bindingPtr;
+ Path bindingPathFromOwner =
+ current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
+ if (bindingV.name() == u"id")
+ qmlFile.addError(std::move(
+ astParseErrors()
+ .error(tr("id attributes should have only simple strings as values"))
+ .withPath(bindingPathFromOwner)));
+
+ setBindingIdentifiers(bindingPathFromOwner, el->qualifiedId, bindingPtr);
+
+ pushEl(bindingPathFromOwner, *bindingPtr, el);
+ FileLocations::addRegion(currentNodeEl().fileLocations, ColonTokenRegion, el->colonToken);
+ loadAnnotations(el);
+ FileLocations::Tree arrayList =
+ createMap(currentNodeEl().fileLocations, Path::Field(Fields::value), nullptr);
+ FileLocations::addRegion(arrayList, LeftBracketRegion, el->lbracketToken);
+ FileLocations::addRegion(arrayList, RightBracketRegion, el->rbracketToken);
+ arrayBindingLevels.append(nodeStack.size());
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiArrayBinding *)
+{
+ index_type idx = currentIndex();
+ Binding &b = std::get<Binding>(currentNode().value);
+ Binding *bPtr = valueFromMultimap(current<QmlObject>().m_bindings, b.name(), idx);
+ *bPtr = b;
+ arrayBindingLevels.removeLast();
+ removeCurrentNode(DomType::Binding);
+}
+
+bool QQmlDomAstCreator::visit(AST::ArgumentList *list)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto currentList = makeScriptList(list);
+
+ for (auto it = list; it; it = it->next) {
+ Node::accept(it->expression, this);
+ if (!m_enableScriptExpressions)
+ return false;
+
+ if (scriptNodeStack.empty() || scriptNodeStack.last().isList()) {
+ Q_SCRIPTELEMENT_DISABLE();
+ return false;
}
- pushEl(pathFromOwner, *aPtr, el);
- return true;
+ currentList.append(scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
}
- void endVisit(AST::UiAnnotation *) override
- {
- DomValue &containingElement = currentNode(1);
- Path pathFromOwner;
- QmlObject &a = std::get<QmlObject>(currentNode().value);
- switch (containingElement.kind) {
- case DomType::QmlObject:
- std::get<QmlObject>(containingElement.value).m_annotations[currentIndex()] = a;
- break;
- case DomType::Binding:
- std::get<Binding>(containingElement.value).m_annotations[currentIndex()] = a;
- break;
- case DomType::Id:
- std::get<Id>(containingElement.value).annotations[currentIndex()] = a;
- break;
- case DomType::PropertyDefinition:
- std::get<PropertyDefinition>(containingElement.value).annotations[currentIndex()] = a;
- break;
- case DomType::MethodInfo:
- std::get<MethodInfo>(containingElement.value).annotations[currentIndex()] = a;
- break;
- default:
- Q_UNREACHABLE();
+ pushScriptElement(currentList);
+
+ return false; // return false because we already iterated over the children using the custom
+ // iteration above
+}
+
+bool QQmlDomAstCreator::visit(AST::UiParameterList *)
+{
+ return false; // do not create script node for Ui stuff
+}
+
+bool QQmlDomAstCreator::visit(AST::PatternElementList *list)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto currentList = makeScriptList(list);
+
+ for (auto it = list; it; it = it->next) {
+ if (it->elision) {
+ Node::accept(it->elision, this);
+ if (scriptNodeStack.empty() || !scriptNodeStack.last().isList()) {
+ Q_SCRIPTELEMENT_DISABLE();
+ return false;
+ }
+ currentList.append(scriptNodeStack.last().takeList());
+ scriptNodeStack.removeLast();
+ }
+ if (it->element) {
+ Node::accept(it->element, this);
+ if (scriptNodeStack.empty() || scriptNodeStack.last().isList()) {
+ Q_SCRIPTELEMENT_DISABLE();
+ return false;
+ }
+ currentList.append(scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
}
- removeCurrentNode(DomType::QmlObject);
}
- void throwRecursionDepthError() override
- {
- qmlFile.addError(myParseErrors().error(
- tr("Maximum statement or expression depth exceeded in QmlDomAstCreator")));
+ pushScriptElement(currentList);
+
+ return false; // return false because we already iterated over the children using the custom
+ // iteration above
+}
+
+bool QQmlDomAstCreator::visit(AST::PatternPropertyList *list)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto currentList = makeScriptList(list);
+
+ for (auto it = list; it; it = it->next) {
+ if (it->property) {
+ Node::accept(it->property, this);
+ if (!m_enableScriptExpressions)
+ return false;
+ if (scriptNodeStack.empty() || scriptNodeStack.last().isList()) {
+ Q_SCRIPTELEMENT_DISABLE();
+ return false;
+ }
+ currentList.append(scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
+ }
}
-};
-void createDom(MutableDomItem qmlFile)
+ pushScriptElement(currentList);
+
+ return false; // return false because we already iterated over the children using the custom
+ // iteration above
+}
+
+/*!
+ \internal
+ Implementing the logic of this method in \c QQmlDomAstCreator::visit(AST::UiQualifiedId *)
+ would create scriptelements at places where there are not needed. This is mainly because
+ UiQualifiedId's appears inside and outside of script parts.
+*/
+ScriptElementVariant QQmlDomAstCreator::scriptElementForQualifiedId(AST::UiQualifiedId *expression)
+{
+ auto id = std::make_shared<ScriptElements::IdentifierExpression>(
+ expression->firstSourceLocation(), expression->lastSourceLocation());
+ id->setName(expression->toString());
+
+ return ScriptElementVariant::fromElement(id);
+}
+
+bool QQmlDomAstCreator::visit(AST::UiQualifiedId *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return false;
+}
+
+bool QQmlDomAstCreator::visit(AST::UiEnumDeclaration *el)
+{
+ EnumDecl eDecl;
+ eDecl.setName(el->name.toString());
+ EnumDecl *ePtr;
+ Path enumPathFromOwner =
+ current<QmlComponent>().addEnumeration(eDecl, AddOption::KeepExisting, &ePtr);
+ pushEl(enumPathFromOwner, *ePtr, el);
+ FileLocations::addRegion(nodeStack.last().fileLocations, EnumKeywordRegion, el->enumToken);
+ FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion, el->identifierToken);
+ loadAnnotations(el);
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiEnumDeclaration *)
+{
+ EnumDecl &e = std::get<EnumDecl>(currentNode().value);
+ EnumDecl *ePtr =
+ valueFromMultimap(current<QmlComponent>().m_enumerations, e.name(), currentIndex());
+ Q_ASSERT(ePtr);
+ *ePtr = e;
+ removeCurrentNode(DomType::EnumDecl);
+}
+
+bool QQmlDomAstCreator::visit(AST::UiEnumMemberList *el)
+{
+ EnumItem it(el->member.toString(), el->value);
+ EnumDecl &eDecl = std::get<EnumDecl>(currentNode().value);
+ Path itPathFromDecl = eDecl.addValue(it);
+ const auto map = createMap(DomType::EnumItem, itPathFromDecl, nullptr);
+ FileLocations::addRegion(map, MainRegion, combine(el->memberToken, el->valueToken));
+ if (el->memberToken.isValid())
+ FileLocations::addRegion(map, IdentifierRegion, el->memberToken);
+ if (el->valueToken.isValid())
+ FileLocations::addRegion(map, EnumValueRegion, el->valueToken);
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiEnumMemberList *el)
+{
+ Node::accept(el->next, this); // put other enum members at the same level as this one...
+}
+
+bool QQmlDomAstCreator::visit(AST::UiInlineComponent *el)
+{
+ QStringList els = current<QmlComponent>().name().split(QLatin1Char('.'));
+ els.append(el->name.toString());
+ QString cName = els.join(QLatin1Char('.'));
+ QmlComponent *compPtr;
+ Path p = qmlFilePtr->addComponent(QmlComponent(cName), AddOption::KeepExisting, &compPtr);
+
+ if (m_enableScriptExpressions) {
+ auto inlineComponentType =
+ makeGenericScriptElement(el->identifierToken, DomType::ScriptType);
+
+ auto typeName = std::make_shared<ScriptElements::IdentifierExpression>(el->identifierToken);
+ typeName->setName(el->name);
+ inlineComponentType->insertChild(Fields::typeName,
+ ScriptElementVariant::fromElement(typeName));
+ compPtr->setNameIdentifiers(
+ finalizeScriptExpression(ScriptElementVariant::fromElement(inlineComponentType),
+ p.field(Fields::nameIdentifiers), rootMap));
+ }
+
+ pushEl(p, *compPtr, el);
+ FileLocations::addRegion(nodeStack.last().fileLocations, ComponentKeywordRegion,
+ el->componentToken);
+ FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion, el->identifierToken);
+ loadAnnotations(el);
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiInlineComponent *)
+{
+ QmlComponent &component = std::get<QmlComponent>(currentNode().value);
+ QStringList nameEls = component.name().split(QChar::fromLatin1('.'));
+ QString key = nameEls.mid(1).join(QChar::fromLatin1('.'));
+ QmlComponent *cPtr = valueFromMultimap(qmlFilePtr->lazyMembers().m_components, key, currentIndex());
+ Q_ASSERT(cPtr);
+ *cPtr = component;
+ removeCurrentNode(DomType::QmlComponent);
+}
+
+bool QQmlDomAstCreator::visit(UiRequired *el)
+{
+ PropertyDefinition pDef;
+ pDef.name = el->name.toString();
+ pDef.isRequired = true;
+ PropertyDefinition *pDefPtr;
+ Path pathFromOwner =
+ current<QmlObject>().addPropertyDef(pDef, AddOption::KeepExisting, &pDefPtr);
+ createMap(DomType::PropertyDefinition, pathFromOwner, el);
+ return false;
+}
+
+bool QQmlDomAstCreator::visit(AST::UiAnnotation *el)
+{
+ QmlObject a;
+ a.setName(QStringLiteral(u"@") + toString(el->qualifiedTypeNameId));
+ // add annotation prototype?
+ DomValue &containingElement = currentNode();
+ Path pathFromOwner;
+ QmlObject *aPtr = nullptr;
+ switch (containingElement.kind) {
+ case DomType::QmlObject:
+ pathFromOwner = std::get<QmlObject>(containingElement.value).addAnnotation(a, &aPtr);
+ break;
+ case DomType::Binding:
+ pathFromOwner = std::get<Binding>(containingElement.value)
+ .addAnnotation(currentNodeEl().path, a, &aPtr);
+ break;
+ case DomType::Id:
+ pathFromOwner =
+ std::get<Id>(containingElement.value).addAnnotation(currentNodeEl().path, a, &aPtr);
+ break;
+ case DomType::PropertyDefinition:
+ pathFromOwner = std::get<PropertyDefinition>(containingElement.value)
+ .addAnnotation(currentNodeEl().path, a, &aPtr);
+ break;
+ case DomType::MethodInfo:
+ pathFromOwner = std::get<MethodInfo>(containingElement.value)
+ .addAnnotation(currentNodeEl().path, a, &aPtr);
+ break;
+ default:
+ qCWarning(domLog) << "Unexpected container object for annotation:"
+ << domTypeToString(containingElement.kind);
+ Q_UNREACHABLE();
+ }
+ pushEl(pathFromOwner, *aPtr, el);
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UiAnnotation *)
+{
+ DomValue &containingElement = currentNode(1);
+ Path pathFromOwner;
+ QmlObject &a = std::get<QmlObject>(currentNode().value);
+ switch (containingElement.kind) {
+ case DomType::QmlObject:
+ std::get<QmlObject>(containingElement.value).m_annotations[currentIndex()] = a;
+ break;
+ case DomType::Binding:
+ std::get<Binding>(containingElement.value).m_annotations[currentIndex()] = a;
+ break;
+ case DomType::Id:
+ std::get<Id>(containingElement.value).annotations[currentIndex()] = a;
+ break;
+ case DomType::PropertyDefinition:
+ std::get<PropertyDefinition>(containingElement.value).annotations[currentIndex()] = a;
+ break;
+ case DomType::MethodInfo:
+ std::get<MethodInfo>(containingElement.value).annotations[currentIndex()] = a;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ removeCurrentNode(DomType::QmlObject);
+}
+
+void QQmlDomAstCreator::throwRecursionDepthError()
+{
+ qmlFile.addError(astParseErrors().error(
+ tr("Maximum statement or expression depth exceeded in QmlDomAstCreator")));
+}
+
+bool QQmlDomAstCreator::visit(AST::StatementList *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::StatementList *list)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptList(list);
+
+ for (auto it = list; it; it = it->next) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current.append(scriptNodeStack.takeLast().takeVariant());
+ }
+
+ current.reverse();
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::BinaryExpression *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::BinaryExpression *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptElement<ScriptElements::BinaryExpression>(exp);
+ current->addLocation(OperatorTokenRegion, exp->operatorToken);
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setRight(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setLeft(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::Block *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::Block *block)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptElement<ScriptElements::BlockStatement>(block);
+
+ if (block->statements) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ current->setStatements(currentScriptNodeEl().takeList());
+ removeCurrentScriptNode(DomType::List);
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::ForStatement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ForStatement *forStatement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptElement<ScriptElements::ForStatement>(forStatement);
+ current->addLocation(FileLocationRegion::ForKeywordRegion, forStatement->forToken);
+ current->addLocation(FileLocationRegion::LeftParenthesisRegion, forStatement->lparenToken);
+ current->addLocation(FileLocationRegion::FirstSemicolonTokenRegion,
+ forStatement->firstSemicolonToken);
+ current->addLocation(FileLocationRegion::SecondSemicolonRegion,
+ forStatement->secondSemicolonToken);
+ current->addLocation(FileLocationRegion::RightParenthesisRegion, forStatement->rparenToken);
+
+ if (forStatement->statement) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setBody(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode(std::nullopt);
+ }
+
+ if (forStatement->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setExpression(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode(std::nullopt);
+ }
+
+ if (forStatement->condition) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setCondition(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode(std::nullopt);
+ }
+
+ if (forStatement->declarations) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ auto variableDeclaration = makeGenericScriptElement(forStatement->declarations,
+ DomType::ScriptVariableDeclaration);
+
+ ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
+ list.replaceKindForGenericChildren(DomType::ScriptPattern,
+ DomType::ScriptVariableDeclarationEntry);
+ variableDeclaration->insertChild(Fields::declarations, std::move(list));
+ removeCurrentScriptNode({});
+
+ current->setDeclarations(ScriptElementVariant::fromElement(variableDeclaration));
+
+ if (auto pe = forStatement->declarations->declaration;
+ pe && pe->declarationKindToken.isValid()) {
+ current->addLocation(FileLocationRegion::TypeIdentifierRegion,
+ pe->declarationKindToken);
+ }
+ }
+
+ if (forStatement->initialiser) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setInitializer(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode(std::nullopt);
+ }
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::IdentifierExpression *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto current = makeScriptElement<ScriptElements::IdentifierExpression>(expression);
+ current->setName(expression->name);
+ pushScriptElement(current);
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::NumericLiteral *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto current = makeScriptElement<ScriptElements::Literal>(expression);
+ current->setLiteralValue(expression->value);
+ pushScriptElement(current);
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::StringLiteral *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ pushScriptElement(makeStringLiteral(expression->value, expression));
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::NullExpression *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto current = makeScriptElement<ScriptElements::Literal>(expression);
+ current->setLiteralValue(nullptr);
+ pushScriptElement(current);
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::TrueLiteral *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto current = makeScriptElement<ScriptElements::Literal>(expression);
+ current->setLiteralValue(true);
+ pushScriptElement(current);
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::FalseLiteral *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto current = makeScriptElement<ScriptElements::Literal>(expression);
+ current->setLiteralValue(false);
+ pushScriptElement(current);
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::IdentifierPropertyName *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto current = makeScriptElement<ScriptElements::IdentifierExpression>(expression);
+ current->setName(expression->id);
+ pushScriptElement(current);
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::StringLiteralPropertyName *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ pushScriptElement(makeStringLiteral(expression->id, expression));
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::TypeAnnotation *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ // do nothing: the work is done in (end)visit(AST::Type*).
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::NumericLiteralPropertyName *expression)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto current = makeScriptElement<ScriptElements::Literal>(expression);
+ current->setLiteralValue(expression->id);
+ pushScriptElement(current);
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::ComputedPropertyName *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ // nothing to do, just forward the underlying expression without changing/wrapping it
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::VariableDeclarationList *list)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto currentList = makeScriptList(list);
+
+ for (auto it = list; it; it = it->next) {
+ if (it->declaration) {
+ Node::accept(it->declaration, this);
+ if (!m_enableScriptExpressions)
+ return false;
+ if (scriptNodeStack.empty() || scriptNodeStack.last().isList()) {
+ Q_SCRIPTELEMENT_DISABLE();
+ return false;
+ }
+ currentList.append(scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
+ }
+ }
+ pushScriptElement(currentList);
+
+ return false; // return false because we already iterated over the children using the custom
+ // iteration above
+}
+
+bool QQmlDomAstCreator::visit(AST::Elision *list)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ auto currentList = makeScriptList(list);
+
+ for (auto it = list; it; it = it->next) {
+ auto current = makeGenericScriptElement(it->commaToken, DomType::ScriptElision);
+ currentList.append(ScriptElementVariant::fromElement(current));
+ }
+ pushScriptElement(currentList);
+
+ return false; // return false because we already iterated over the children using the custom
+ // iteration above
+}
+
+bool QQmlDomAstCreator::visit(AST::PatternElement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+/*!
+ \internal
+ Avoid code-duplication, reuse this code when doing endVisit on types inheriting from
+ AST::PatternElement.
+*/
+void QQmlDomAstCreator::endVisitHelper(
+ AST::PatternElement *pe,
+ const std::shared_ptr<ScriptElements::GenericScriptElement> &current)
+{
+ if (pe->equalToken.isValid())
+ current->addLocation(FileLocationRegion::EqualTokenRegion, pe->equalToken);
+
+ if (pe->identifierToken.isValid() && !pe->bindingIdentifier.isEmpty()) {
+ auto identifier =
+ std::make_shared<ScriptElements::IdentifierExpression>(pe->identifierToken);
+ identifier->setName(pe->bindingIdentifier);
+ current->insertChild(Fields::identifier, ScriptElementVariant::fromElement(identifier));
+ }
+ if (pe->initializer) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::initializer, scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
+ }
+ if (pe->typeAnnotation) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::type, scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
+ }
+ if (pe->bindingTarget) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::bindingElement, scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
+ }
+}
+
+void QQmlDomAstCreator::endVisit(AST::PatternElement *pe)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto element = makeGenericScriptElement(pe, DomType::ScriptPattern);
+ endVisitHelper(pe, element);
+ // check if helper disabled scriptexpressions
+ if (!m_enableScriptExpressions)
+ return;
+
+ pushScriptElement(element);
+}
+
+bool QQmlDomAstCreator::visit(AST::IfStatement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::IfStatement *ifStatement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptElement<ScriptElements::IfStatement>(ifStatement);
+ current->addLocation(LeftParenthesisRegion, ifStatement->lparenToken);
+ current->addLocation(RightParenthesisRegion, ifStatement->rparenToken);
+ current->addLocation(ElseKeywordRegion, ifStatement->elseToken);
+ current->addLocation(IfKeywordRegion, ifStatement->ifToken);
+
+ if (ifStatement->ko) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setAlternative(scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
+ }
+
+ if (ifStatement->ok) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setConsequence(scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
+ }
+ if (ifStatement->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setCondition(scriptNodeStack.last().takeVariant());
+ scriptNodeStack.removeLast();
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::ReturnStatement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ReturnStatement *returnStatement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptElement<ScriptElements::ReturnStatement>(returnStatement);
+ current->addLocation(ReturnKeywordRegion, returnStatement->returnToken);
+
+ if (returnStatement->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setExpression(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::FieldMemberExpression *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::FieldMemberExpression *expression)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptElement<ScriptElements::BinaryExpression>(expression);
+ current->setOp(ScriptElements::BinaryExpression::FieldMemberAccess);
+ current->addLocation(FileLocationRegion::OperatorTokenRegion, expression->dotToken);
+
+ if (expression->base) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setLeft(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ auto scriptIdentifier =
+ std::make_shared<ScriptElements::IdentifierExpression>(expression->identifierToken);
+ scriptIdentifier->setName(expression->name);
+ current->setRight(ScriptElementVariant::fromElement(scriptIdentifier));
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::ArrayMemberExpression *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ArrayMemberExpression *expression)
{
- if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
- QmlDomAstCreator componentCreator(qmlFile);
- AST::Node::accept(qmlFilePtr->ast(), &componentCreator);
- AstComments::collectComments(qmlFile);
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptElement<ScriptElements::BinaryExpression>(expression);
+ current->setOp(ScriptElements::BinaryExpression::ArrayMemberAccess);
+ current->addLocation(FileLocationRegion::OperatorTokenRegion, expression->lbracketToken);
+
+ if (expression->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ // if scriptNodeStack.last() is fieldmember expression, add expression to it instead of
+ // creating new one
+ current->setRight(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (expression->base) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setLeft(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::CallExpression *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::CallExpression *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptCallExpression);
+ current->addLocation(LeftParenthesisRegion, exp->lparenToken);
+ current->addLocation(RightParenthesisRegion, exp->rparenToken);
+
+ if (exp->arguments) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ current->insertChild(Fields::arguments, currentScriptNodeEl().takeList());
+ removeCurrentScriptNode({});
} else {
- qCWarning(creatorLog) << "createDom called on non qmlFile";
+ // insert empty list
+ current->insertChild(Fields::arguments,
+ ScriptElements::ScriptList(exp->lparenToken, exp->rparenToken));
+ }
+
+ if (exp->base) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::callee, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
}
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::ArrayPattern *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ArrayPattern *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptArray);
+
+ if (exp->elements) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
+ list.replaceKindForGenericChildren(DomType::ScriptPattern, DomType::ScriptArrayEntry);
+ current->insertChild(Fields::elements, std::move(list));
+
+ removeCurrentScriptNode({});
+ } else {
+ // insert empty list
+ current->insertChild(Fields::elements,
+ ScriptElements::ScriptList(exp->lbracketToken, exp->rbracketToken));
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::ObjectPattern *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ObjectPattern *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptObject);
+
+ if (exp->properties) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ current->insertChild(Fields::properties, currentScriptNodeEl().takeList());
+ removeCurrentScriptNode({});
+ } else {
+ // insert empty list
+ current->insertChild(Fields::properties,
+ ScriptElements::ScriptList(exp->lbraceToken, exp->rbraceToken));
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::PatternProperty *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::PatternProperty *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptProperty);
+
+ // handle the stuff from PatternProperty's base class PatternElement
+ endVisitHelper(static_cast<PatternElement *>(exp), current);
+
+ // check if helper disabled scriptexpressions
+ if (!m_enableScriptExpressions)
+ return;
+
+ if (exp->name) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::name, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::VariableStatement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::VariableStatement *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(statement, DomType::ScriptVariableDeclaration);
+ current->addLocation(FileLocationRegion::TypeIdentifierRegion, statement->declarationKindToken);
+
+ if (statement->declarations) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+
+ ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
+ list.replaceKindForGenericChildren(DomType::ScriptPattern,
+ DomType::ScriptVariableDeclarationEntry);
+ current->insertChild(Fields::declarations, std::move(list));
+
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::Type *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::Type *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptType);
+
+ if (exp->typeArgument) {
+ current->insertChild(Fields::typeArgumentName,
+ fieldMemberExpressionForQualifiedId(exp->typeArgument));
+ current->addLocation(FileLocationRegion::IdentifierRegion, combineLocations(exp->typeArgument));
+ }
+
+ if (exp->typeId) {
+ current->insertChild(Fields::typeName, fieldMemberExpressionForQualifiedId(exp->typeId));
+ current->addLocation(FileLocationRegion::TypeIdentifierRegion, combineLocations(exp->typeId));
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::DefaultClause *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::DefaultClause *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptDefaultClause);
+ current->addLocation(DefaultKeywordRegion, exp->defaultToken);
+ current->addLocation(ColonTokenRegion, exp->colonToken);
+
+ if (exp->statements) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ current->insertChild(Fields::statements, currentScriptNodeEl().takeList());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::CaseClause *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::CaseClause *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptCaseClause);
+ current->addLocation(FileLocationRegion::CaseKeywordRegion, exp->caseToken);
+ current->addLocation(FileLocationRegion::ColonTokenRegion, exp->colonToken);
+
+ if (exp->statements) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ current->insertChild(Fields::statements, currentScriptNodeEl().takeList());
+ removeCurrentScriptNode({});
+ }
+
+ if (exp->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::CaseClauses *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::CaseClauses *list)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptList(list);
+
+ for (auto it = list; it; it = it->next) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current.append(scriptNodeStack.takeLast().takeVariant());
+ }
+
+ current.reverse();
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::CaseBlock *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::CaseBlock *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptCaseBlock);
+ current->addLocation(FileLocationRegion::LeftBraceRegion, exp->lbraceToken);
+ current->addLocation(FileLocationRegion::RightBraceRegion, exp->rbraceToken);
+
+ if (exp->moreClauses) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ current->insertChild(Fields::moreCaseClauses, currentScriptNodeEl().takeList());
+ removeCurrentScriptNode({});
+ }
+
+ if (exp->defaultClause) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::defaultClause, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (exp->clauses) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
+ current->insertChild(Fields::caseClauses, currentScriptNodeEl().takeList());
+ removeCurrentScriptNode({});
+ }
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::SwitchStatement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::SwitchStatement *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptSwitchStatement);
+ current->addLocation(FileLocationRegion::SwitchKeywordRegion, exp->switchToken);
+ current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
+ current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
+
+ if (exp->block) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::caseBlock, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+ if (exp->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::WhileStatement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::WhileStatement *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptWhileStatement);
+ current->addLocation(FileLocationRegion::WhileKeywordRegion, exp->whileToken);
+ current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
+ current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
+
+ if (exp->statement) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (exp->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::DoWhileStatement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::DoWhileStatement *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptDoWhileStatement);
+ current->addLocation(FileLocationRegion::DoKeywordRegion, exp->doToken);
+ current->addLocation(FileLocationRegion::WhileKeywordRegion, exp->whileToken);
+ current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
+ current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
+
+ if (exp->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (exp->statement) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::ForEachStatement *)
+{
+ if (!m_enableScriptExpressions)
+ return false;
+
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ForEachStatement *exp)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(exp, DomType::ScriptForEachStatement);
+ current->addLocation(FileLocationRegion::ForKeywordRegion, exp->forToken);
+ current->addLocation(FileLocationRegion::InOfTokenRegion, exp->inOfToken);
+ current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
+ current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
+
+ if (exp->statement) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+ if (exp->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (exp->lhs) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::bindingElement, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+
+ if (auto pe = AST::cast<PatternElement *>(exp->lhs);
+ pe && pe->declarationKindToken.isValid()) {
+ current->addLocation(FileLocationRegion::TypeIdentifierRegion,
+ pe->declarationKindToken);
+ }
+ }
+
+ pushScriptElement(current);
+}
+
+
+bool QQmlDomAstCreator::visit(AST::ClassExpression *)
+{
+ // TODO: Add support for js expressions in classes
+ // For now, turning off explicitly to avoid unwanted problems
+ if (m_enableScriptExpressions)
+ Q_SCRIPTELEMENT_DISABLE();
+ return true;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ClassExpression *)
+{
+}
+
+bool QQmlDomAstCreator::visit(AST::TemplateLiteral *)
+{
+ // TODO: Add support for template literals
+ // For now, turning off explicitly to avoid unwanted problems
+ if (m_enableScriptExpressions)
+ Q_SCRIPTELEMENT_DISABLE();
+ return true;
+}
+
+bool QQmlDomAstCreator::visit(AST::TryStatement *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::TryStatement *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(statement, DomType::ScriptTryCatchStatement);
+ current->addLocation(FileLocationRegion::TryKeywordRegion, statement->tryToken);
+
+ if (auto exp = statement->finallyExpression) {
+ current->addLocation(FileLocationRegion::FinallyKeywordRegion, exp->finallyToken);
+
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::finallyBlock, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (auto exp = statement->catchExpression) {
+ current->addLocation(FileLocationRegion::CatchKeywordRegion, exp->catchToken);
+ current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
+ current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
+
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::catchBlock, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::catchParameter, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (statement->statement) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::block, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::Catch *)
+{
+ // handled in visit(AST::TryStatement* )
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::Catch *)
+{
+ // handled in endVisit(AST::TryStatement* )
+}
+
+bool QQmlDomAstCreator::visit(AST::Finally *)
+{
+ // handled in visit(AST::TryStatement* )
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::Finally *)
+{
+ // handled in endVisit(AST::TryStatement* )
+}
+
+bool QQmlDomAstCreator::visit(AST::ThrowStatement *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ThrowStatement *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(statement, DomType::ScriptThrowStatement);
+ current->addLocation(FileLocationRegion::ThrowKeywordRegion, statement->throwToken);
+
+ if (statement->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::LabelledStatement *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::LabelledStatement *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(statement, DomType::ScriptLabelledStatement);
+ current->addLocation(FileLocationRegion::ColonTokenRegion, statement->colonToken);
+
+ auto label = std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
+ label->setName(statement->label);
+ current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
+
+
+ if (statement->statement) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::statement, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::BreakStatement *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::BreakStatement *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(statement, DomType::ScriptBreakStatement);
+ current->addLocation(FileLocationRegion::BreakKeywordRegion, statement->breakToken);
+
+ if (!statement->label.isEmpty()) {
+ auto label =
+ std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
+ label->setName(statement->label);
+ current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
+ }
+
+ pushScriptElement(current);
+}
+
+// note: thats for comma expressions
+bool QQmlDomAstCreator::visit(AST::Expression *)
+{
+ return m_enableScriptExpressions;
+}
+
+// note: thats for comma expressions
+void QQmlDomAstCreator::endVisit(AST::Expression *commaExpression)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeScriptElement<ScriptElements::BinaryExpression>(commaExpression);
+ current->addLocation(OperatorTokenRegion, commaExpression->commaToken);
+
+ if (commaExpression->right) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setRight(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (commaExpression->left) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->setLeft(currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::ConditionalExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ConditionalExpression *expression)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(expression, DomType::ScriptConditionalExpression);
+ current->addLocation(FileLocationRegion::QuestionMarkTokenRegion, expression->questionToken);
+ current->addLocation(FileLocationRegion::ColonTokenRegion, expression->colonToken);
+
+ if (expression->ko) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::alternative, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (expression->ok) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::consequence, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ if (expression->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::condition, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::ContinueStatement *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::ContinueStatement *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(statement, DomType::ScriptContinueStatement);
+ current->addLocation(FileLocationRegion::ContinueKeywordRegion, statement->continueToken);
+
+ if (!statement->label.isEmpty()) {
+ auto label =
+ std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
+ label->setName(statement->label);
+ current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
+ }
+
+ pushScriptElement(current);
+}
+
+/*!
+ \internal
+ Helper to create unary expressions from AST nodes.
+ \sa makeGenericScriptElement
+ */
+std::shared_ptr<ScriptElements::GenericScriptElement>
+QQmlDomAstCreator::makeUnaryExpression(AST::Node *expression, QQmlJS::SourceLocation operatorToken,
+ bool hasExpression, UnaryExpressionKind kind)
+{
+ const DomType type = [&kind]() {
+ switch (kind) {
+ case Prefix:
+ return DomType::ScriptUnaryExpression;
+ case Postfix:
+ return DomType::ScriptPostExpression;
+ }
+ Q_UNREACHABLE_RETURN(DomType::ScriptUnaryExpression);
+ }();
+
+ auto current = makeGenericScriptElement(expression, type);
+ current->addLocation(FileLocationRegion::OperatorTokenRegion, operatorToken);
+
+ if (hasExpression) {
+ if (scriptNodeStack.isEmpty() || scriptNodeStack.last().isList()) {
+ Q_SCRIPTELEMENT_DISABLE();
+ return {};
+ }
+ current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ return current;
+}
+
+bool QQmlDomAstCreator::visit(AST::UnaryMinusExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UnaryMinusExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->minusToken, statement->expression, Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::UnaryPlusExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::UnaryPlusExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->plusToken, statement->expression, Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::TildeExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::TildeExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->tildeToken, statement->expression, Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::NotExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::NotExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->notToken, statement->expression, Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::TypeOfExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::TypeOfExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->typeofToken, statement->expression, Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::DeleteExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::DeleteExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->deleteToken, statement->expression, Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::VoidExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::VoidExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->voidToken, statement->expression, Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::PostDecrementExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::PostDecrementExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->decrementToken, statement->base, Postfix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::PostIncrementExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::PostIncrementExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current =
+ makeUnaryExpression(statement, statement->incrementToken, statement->base, Postfix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::PreIncrementExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::PreIncrementExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeUnaryExpression(statement, statement->incrementToken, statement->expression,
+ Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::EmptyStatement *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::EmptyStatement *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(statement, DomType::ScriptEmptyStatement);
+ current->addLocation(FileLocationRegion::SemicolonTokenRegion, statement->semicolonToken);
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::NestedExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::NestedExpression *expression)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeGenericScriptElement(expression, DomType::ScriptParenthesizedExpression);
+ current->addLocation(FileLocationRegion::LeftParenthesisRegion, expression->lparenToken);
+ current->addLocation(FileLocationRegion::RightParenthesisRegion, expression->rparenToken);
+
+ if (expression->expression) {
+ Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() || scriptNodeStack.last().isList());
+ current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
+ removeCurrentScriptNode({});
+ }
+
+ pushScriptElement(current);
+}
+
+bool QQmlDomAstCreator::visit(AST::PreDecrementExpression *)
+{
+ return m_enableScriptExpressions;
+}
+
+void QQmlDomAstCreator::endVisit(AST::PreDecrementExpression *statement)
+{
+ if (!m_enableScriptExpressions)
+ return;
+
+ auto current = makeUnaryExpression(statement, statement->decrementToken, statement->expression,
+ Prefix);
+ if (!current)
+ return;
+
+ pushScriptElement(current);
+}
+
+static const DomEnvironment *environmentFrom(MutableDomItem &qmlFile)
+{
+ auto top = qmlFile.top();
+ if (!top) {
+ return {};
+ }
+ auto domEnvironment = top.as<DomEnvironment>();
+ if (!domEnvironment) {
+ return {};
+ }
+ return domEnvironment;
+}
+
+static QStringList qmldirFilesFrom(MutableDomItem &qmlFile)
+{
+ if (auto env = environmentFrom(qmlFile))
+ return env->qmldirFiles();
+
+ return {};
+}
+
+QQmlDomAstCreatorWithQQmlJSScope::QQmlDomAstCreatorWithQQmlJSScope(const QQmlJSScope::Ptr &current,
+ MutableDomItem &qmlFile,
+ QQmlJSLogger *logger,
+ QQmlJSImporter *importer)
+ : m_root(current),
+ m_logger(logger),
+ m_importer(importer),
+ m_implicitImportDirectory(QQmlJSImportVisitor::implicitImportDirectory(
+ m_logger->fileName(), m_importer->resourceFileMapper())),
+ m_scopeCreator(m_root, m_importer, m_logger, m_implicitImportDirectory,
+ qmldirFilesFrom(qmlFile)),
+ m_domCreator(qmlFile)
+{
+}
+
+#define X(name) \
+ bool QQmlDomAstCreatorWithQQmlJSScope::visit(name *node) \
+ { \
+ return visitT(node); \
+ } \
+ void QQmlDomAstCreatorWithQQmlJSScope::endVisit(name *node) \
+ { \
+ endVisitT(node); \
+ }
+QQmlJSASTClassListToVisit
+#undef X
+
+void QQmlDomAstCreatorWithQQmlJSScope::setScopeInDomAfterEndvisit()
+{
+ const QQmlJSScope::ConstPtr scope = m_scopeCreator.m_currentScope;
+ if (!m_domCreator.scriptNodeStack.isEmpty()) {
+ auto topOfStack = m_domCreator.currentScriptNodeEl();
+ switch (topOfStack.kind) {
+ case DomType::ScriptBlockStatement:
+ case DomType::ScriptForStatement:
+ case DomType::ScriptForEachStatement:
+ case DomType::ScriptDoWhileStatement:
+ case DomType::ScriptWhileStatement:
+ case DomType::List:
+ m_domCreator.currentScriptNodeEl().setSemanticScope(scope);
+ break;
+ // TODO: find which script elements also have a scope and implement them here
+ default:
+ break;
+ };
+ } else if (!m_domCreator.nodeStack.isEmpty()) {
+ std::visit(
+ [&scope](auto &&e) {
+ using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
+ // TODO: find which dom elements also have a scope and implement them here
+ if constexpr (std::is_same_v<U, QmlObject>) {
+ e.setSemanticScope(scope);
+ } else if constexpr (std::is_same_v<U, QmlComponent>) {
+ e.setSemanticScope(scope);
+ } else if constexpr (std::is_same_v<U, MethodInfo>) {
+ if (e.body) {
+ if (auto scriptElement = e.body->scriptElement()) {
+ scriptElement.base()->setSemanticScope(scope);
+ }
+ }
+ e.setSemanticScope(scope);
+ }
+ },
+ m_domCreator.currentNodeEl().item.value);
+ }
+}
+
+void QQmlDomAstCreatorWithQQmlJSScope::setScopeInDomBeforeEndvisit()
+{
+ const QQmlJSScope::ConstPtr scope = m_scopeCreator.m_currentScope;
+
+ // depending whether the property definition has a binding, the property definition might be
+ // either at the last position in the stack or at the position before the last position.
+ if (m_domCreator.nodeStack.size() > 1
+ && m_domCreator.nodeStack.last().item.kind == DomType::Binding) {
+ std::visit(
+ [&scope](auto &&e) {
+ using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
+ if constexpr (std::is_same_v<U, PropertyDefinition>) {
+ // Make sure to use the property definition scope instead of the binding
+ // scope. If the current scope is a binding scope (this happens when the
+ // property definition has a binding, like `property int i: 45` for
+ // example), then the property definition scope is the parent of the current
+ // scope.
+ e.setSemanticScope(scope->scopeType() == QQmlSA::ScopeType::JSFunctionScope
+ ? scope->parentScope()
+ : scope);
+ Q_ASSERT(e.semanticScope()
+ && e.semanticScope()->scopeType() == QQmlSA::ScopeType::QMLScope);
+ }
+ },
+ m_domCreator.currentNodeEl(1).item.value);
+ }
+ if (m_domCreator.nodeStack.size() > 0) {
+ std::visit(
+ [&scope](auto &&e) {
+ using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
+ if constexpr (std::is_same_v<U, PropertyDefinition>) {
+ e.setSemanticScope(scope);
+ Q_ASSERT(e.semanticScope());
+ } else if constexpr (std::is_same_v<U, MethodInfo>) {
+ if (e.methodType == MethodInfo::Signal) {
+ e.setSemanticScope(scope);
+ }
+ }
+ },
+ m_domCreator.currentNodeEl().item.value);
+ }
+}
+
+void QQmlDomAstCreatorWithQQmlJSScope::throwRecursionDepthError()
+{
}
} // end namespace Dom
} // end namespace QQmlJS
+
+#undef Q_SCRIPTELEMENT_DISABLE
+#undef Q_SCRIPTELEMENT_EXIT_IF
+
QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomastcreator_p.h b/src/qmldom/qqmldomastcreator_p.h
index 528304b83f..8176721d7e 100644
--- a/src/qmldom/qqmldomastcreator_p.h
+++ b/src/qmldom/qqmldomastcreator_p.h
@@ -15,22 +15,693 @@
// We mean it.
//
-#include "qqmldom_global.h"
+#include "qqmldomelements_p.h"
#include "qqmldomitem_p.h"
-#include "qqmldomastcreator_p.h"
-#include "qqmldomcomments_p.h"
+#include "qqmldompath_p.h"
+#include "qqmldomscriptelements_p.h"
+
+#include <QtQmlCompiler/private/qqmljsimportvisitor_p.h>
#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <memory>
+#include <type_traits>
+#include <variant>
QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-SourceLocation combineLocations(SourceLocation s1, SourceLocation s2);
-SourceLocation combineLocations(AST::Node *n);
+class QQmlDomAstCreator final : public AST::Visitor
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlDomAstCreator)
+ using AST::Visitor::endVisit;
+ using AST::Visitor::visit;
+
+ static constexpr const auto className = "QmlDomAstCreator";
+
+ class DomValue
+ {
+ public:
+ template<typename T>
+ DomValue(const T &obj) : kind(T::kindValue), value(obj)
+ {
+ }
+ DomType kind;
+ std::variant<QmlObject, MethodInfo, QmlComponent, PropertyDefinition, Binding, EnumDecl,
+ EnumItem, ConstantData, Id>
+ value;
+ };
+
+ class QmlStackElement
+ {
+ public:
+ Path path;
+ DomValue item;
+ FileLocations::Tree fileLocations;
+ };
+
+ /*!
+ \internal
+ Contains a ScriptElementVariant, that can be used everywhere in the DOM representation, or a
+ List that should always be inside of something else, e.g., that cannot be the root of the
+ script element DOM representation.
+
+ Also, it makes sure you do not mistreat a list as a regular script element and vice versa.
+
+ The reason for this is that Lists can get pretty unintuitive, as a List could be a Block of
+ statements or a list of variable declarations (let i = 3, j = 4, ...) or something completely
+ different. Instead, always put lists inside named construct (BlockStatement,
+ VariableDeclaration, ...).
+ */
+ class ScriptStackElement
+ {
+ public:
+ template<typename T>
+ static ScriptStackElement from(const T &obj)
+ {
+ if constexpr (std::is_same_v<T, ScriptElements::ScriptList>) {
+ ScriptStackElement s{ ScriptElements::ScriptList::kindValue, obj };
+ return s;
+ } else {
+ ScriptStackElement s{ obj->kind(), ScriptElementVariant::fromElement(obj) };
+ return s;
+ }
+ Q_UNREACHABLE();
+ }
+
+ DomType kind;
+ using Variant = std::variant<ScriptElementVariant, ScriptElements::ScriptList>;
+ Variant value;
+
+ ScriptElementVariant takeVariant()
+ {
+ Q_ASSERT_X(std::holds_alternative<ScriptElementVariant>(value), "takeVariant",
+ "Should be a variant, did the parser change?");
+ return std::get<ScriptElementVariant>(std::move(value));
+ }
+
+ bool isList() const { return std::holds_alternative<ScriptElements::ScriptList>(value); };
+
+ ScriptElements::ScriptList takeList()
+ {
+ Q_ASSERT_X(std::holds_alternative<ScriptElements::ScriptList>(value), "takeList",
+ "Should be a List, did the parser change?");
+ return std::get<ScriptElements::ScriptList>(std::move(value));
+ }
+
+ void setSemanticScope(const QQmlJSScope::ConstPtr &scope)
+ {
+ if (auto x = std::get_if<ScriptElementVariant>(&value)) {
+ x->base()->setSemanticScope(scope);
+ return;
+ } else if (auto x = std::get_if<ScriptElements::ScriptList>(&value)) {
+ x->setSemanticScope(scope);
+ return;
+ }
+ Q_UNREACHABLE();
+ }
+ };
+
+public:
+ void enableScriptExpressions(bool enable = true) { m_enableScriptExpressions = enable; }
+ void enableLoadFileLazily(bool enable = true) { m_loadFileLazily = enable; }
+
+private:
+
+ MutableDomItem qmlFile;
+ std::shared_ptr<QmlFile> qmlFilePtr;
+ QVector<QmlStackElement> nodeStack;
+ QList<ScriptStackElement> scriptNodeStack;
+ QVector<int> arrayBindingLevels;
+ FileLocations::Tree rootMap;
+ bool m_enableScriptExpressions = false;
+ bool m_loadFileLazily = false;
+
+ // A Binding inside a UiPublicMember (= a Property definition) will shadow the
+ // propertydefinition's binding identifiers with its own binding identifiers. Therefore, disable
+ // bindingIdentifiers for the Binding inside a Property definition by using this flag.
+ bool m_skipBindingIdentifiers = false;
+
+ void setBindingIdentifiers(const Path &pathFromOwner, const AST::UiQualifiedId *identifiers,
+ Binding *bindingPtr);
+ template<typename T>
+ QmlStackElement &currentEl(int idx = 0)
+ {
+ Q_ASSERT_X(idx < nodeStack.size() && idx >= 0, "currentQmlObjectOrComponentEl",
+ "Stack does not contain enough elements!");
+ int i = nodeStack.size() - idx;
+ while (i-- > 0) {
+ DomType k = nodeStack.at(i).item.kind;
+ if (k == T::kindValue)
+ return nodeStack[i];
+ }
+ Q_ASSERT_X(false, "currentEl", "Stack does not contan object of type ");
+ return nodeStack.last();
+ }
+
+ template<typename T>
+ ScriptStackElement &currentScriptEl(int idx = 0)
+ {
+ Q_ASSERT_X(m_enableScriptExpressions, "currentScriptEl",
+ "Cannot access script elements when they are disabled!");
+
+ Q_ASSERT_X(idx < scriptNodeStack.size() && idx >= 0, "currentQmlObjectOrComponentEl",
+ "Stack does not contain enough elements!");
+ int i = scriptNodeStack.size() - idx;
+ while (i-- > 0) {
+ DomType k = scriptNodeStack.at(i).kind;
+ if (k == T::element_type::kindValue)
+ return scriptNodeStack[i];
+ }
+ Q_ASSERT_X(false, "currentEl", "Stack does not contain object of type ");
+ return scriptNodeStack.last();
+ }
+
+ template<typename T>
+ T &current(int idx = 0)
+ {
+ return std::get<T>(currentEl<T>(idx).item.value);
+ }
+
+ index_type currentIndex() { return currentNodeEl().path.last().headIndex(); }
+
+ QmlStackElement &currentQmlObjectOrComponentEl(int idx = 0);
+
+ QmlStackElement &currentNodeEl(int i = 0);
+ ScriptStackElement &currentScriptNodeEl(int i = 0);
+
+ DomValue &currentNode(int i = 0);
+
+ void removeCurrentNode(std::optional<DomType> expectedType);
+ void removeCurrentScriptNode(std::optional<DomType> expectedType);
+
+ void pushEl(const Path &p, const DomValue &it, AST::Node *n)
+ {
+ nodeStack.append({ p, it, createMap(it.kind, p, n) });
+ }
+
+ FileLocations::Tree createMap(const FileLocations::Tree &base, const Path &p, AST::Node *n);
+
+ FileLocations::Tree createMap(DomType k, const Path &p, AST::Node *n);
+
+ const ScriptElementVariant &
+ finalizeScriptExpression(const ScriptElementVariant &element, const Path &pathFromOwner,
+ const FileLocations::Tree &ownerFileLocations);
+
+ void setScriptExpression (const std::shared_ptr<ScriptExpression>& value);
+
+ Path pathOfLastScriptNode() const;
+
+ /*!
+ \internal
+ Helper to create string literals from AST nodes.
+ */
+ template<typename AstNodeT>
+ static std::shared_ptr<ScriptElements::Literal> makeStringLiteral(QStringView value,
+ AstNodeT *ast)
+ {
+ auto myExp = std::make_shared<ScriptElements::Literal>(ast->firstSourceLocation(),
+ ast->lastSourceLocation());
+ myExp->setLiteralValue(value.toString());
+ return myExp;
+ }
+
+ static std::shared_ptr<ScriptElements::Literal> makeStringLiteral(QStringView value,
+ QQmlJS::SourceLocation loc)
+ {
+ auto myExp = std::make_shared<ScriptElements::Literal>(loc);
+ myExp->setLiteralValue(value.toString());
+ return myExp;
+ }
+
+ /*!
+ \internal
+ Helper to create script elements from AST nodes, as the DOM classes should be completely
+ dependency-free from AST and parser classes. Using the AST classes in qqmldomastcreator is
+ fine because it needs them for the construction/visit. \sa makeScriptList
+ */
+ template<typename ScriptElementT, typename AstNodeT,
+ typename Enable =
+ std::enable_if_t<!std::is_same_v<ScriptElementT, ScriptElements::ScriptList>>>
+ static decltype(auto) makeScriptElement(AstNodeT *ast)
+ {
+ auto myExp = std::make_shared<ScriptElementT>(ast->firstSourceLocation(),
+ ast->lastSourceLocation());
+ return myExp;
+ }
+
+ /*!
+ \internal
+ Helper to create generic script elements from AST nodes.
+ \sa makeScriptElement
+ */
+ template<typename AstNodeT>
+ static std::shared_ptr<ScriptElements::GenericScriptElement>
+ makeGenericScriptElement(AstNodeT *ast, DomType kind)
+ {
+ auto myExp = std::make_shared<ScriptElements::GenericScriptElement>(
+ ast->firstSourceLocation(), ast->lastSourceLocation());
+ myExp->setKind(kind);
+ return myExp;
+ }
+
+ enum UnaryExpressionKind { Prefix, Postfix };
+ std::shared_ptr<ScriptElements::GenericScriptElement>
+ makeUnaryExpression(AST::Node *expression, QQmlJS::SourceLocation operatorToken,
+ bool hasExpression, UnaryExpressionKind type);
+
+ static std::shared_ptr<ScriptElements::GenericScriptElement>
+ makeGenericScriptElement(SourceLocation location, DomType kind)
+ {
+ auto myExp = std::make_shared<ScriptElements::GenericScriptElement>(location);
+ myExp->setKind(kind);
+ return myExp;
+ }
+
+ /*!
+ \internal
+ Helper to create script lists from AST nodes.
+ \sa makeScriptElement
+ */
+ template<typename AstNodeT>
+ static decltype(auto) makeScriptList(AstNodeT *ast)
+ {
+ auto myExp =
+ ScriptElements::ScriptList(ast->firstSourceLocation(), ast->lastSourceLocation());
+ return myExp;
+ }
+
+ template<typename ScriptElementT>
+ void pushScriptElement(const ScriptElementT &element)
+ {
+ Q_ASSERT_X(m_enableScriptExpressions, "pushScriptElement",
+ "Cannot create script elements when they are disabled!");
+ scriptNodeStack.append(ScriptStackElement::from(element));
+ }
+
+ void disableScriptElements()
+ {
+ m_enableScriptExpressions = false;
+ scriptNodeStack.clear();
+ }
+
+ ScriptElementVariant scriptElementForQualifiedId(AST::UiQualifiedId *expression);
+
+public:
+ explicit QQmlDomAstCreator(const MutableDomItem &qmlFile);
+
+ bool visit(AST::UiProgram *program) override;
+ void endVisit(AST::UiProgram *) override;
+
+ bool visit(AST::UiPragma *el) override;
+
+ bool visit(AST::UiImport *el) override;
+
+ bool visit(AST::UiPublicMember *el) override;
+ void endVisit(AST::UiPublicMember *el) override;
+
+ bool visit(AST::FunctionDeclaration *el) override;
+ void endVisit(AST::FunctionDeclaration *) override;
+
+ bool visit(AST::UiSourceElement *el) override;
+ void endVisit(AST::UiSourceElement *) override;
+
+ void loadAnnotations(AST::UiObjectMember *el) { AST::Node::accept(el->annotations, this); }
+
+ bool visit(AST::UiObjectDefinition *el) override;
+ void endVisit(AST::UiObjectDefinition *) override;
+
+ bool visit(AST::UiObjectBinding *el) override;
+ void endVisit(AST::UiObjectBinding *) override;
+
+ bool visit(AST::UiScriptBinding *el) override;
+ void endVisit(AST::UiScriptBinding *) override;
+
+ bool visit(AST::UiArrayBinding *el) override;
+ void endVisit(AST::UiArrayBinding *) override;
+
+ bool visit(AST::UiQualifiedId *) override;
+
+ bool visit(AST::UiEnumDeclaration *el) override;
+ void endVisit(AST::UiEnumDeclaration *) override;
+
+ bool visit(AST::UiEnumMemberList *el) override;
+ void endVisit(AST::UiEnumMemberList *el) override;
+
+ bool visit(AST::UiInlineComponent *el) override;
+ void endVisit(AST::UiInlineComponent *) override;
+
+ bool visit(AST::UiRequired *el) override;
+
+ bool visit(AST::UiAnnotation *el) override;
+ void endVisit(AST::UiAnnotation *) override;
+
+ // for Script elements:
+ bool visit(AST::BinaryExpression *exp) override;
+ void endVisit(AST::BinaryExpression *exp) override;
+
+ bool visit(AST::Block *block) override;
+ void endVisit(AST::Block *) override;
+
+ bool visit(AST::ReturnStatement *block) override;
+ void endVisit(AST::ReturnStatement *) override;
+
+ bool visit(AST::ForStatement *forStatement) override;
+ void endVisit(AST::ForStatement *forStatement) override;
+
+ bool visit(AST::PatternElement *pe) override;
+ void endVisit(AST::PatternElement *pe) override;
+ void endVisitHelper(AST::PatternElement *pe,
+ const std::shared_ptr<ScriptElements::GenericScriptElement> &element);
+
+ bool visit(AST::IfStatement *) override;
+ void endVisit(AST::IfStatement *) override;
+
+ bool visit(AST::FieldMemberExpression *) override;
+ void endVisit(AST::FieldMemberExpression *) override;
+
+ bool visit(AST::ArrayMemberExpression *) override;
+ void endVisit(AST::ArrayMemberExpression *) override;
+
+ bool visit(AST::CallExpression *) override;
+ void endVisit(AST::CallExpression *) override;
+
+ bool visit(AST::ArrayPattern *) override;
+ void endVisit(AST::ArrayPattern *) override;
+
+ bool visit(AST::ObjectPattern *) override;
+ void endVisit(AST::ObjectPattern *) override;
+
+ bool visit(AST::PatternProperty *) override;
+ void endVisit(AST::PatternProperty *) override;
+
+ bool visit(AST::VariableStatement *) override;
+ void endVisit(AST::VariableStatement *) override;
+
+ bool visit(AST::Type *expression) override;
+ void endVisit(AST::Type *expression) override;
+
+ bool visit(AST::DefaultClause *) override;
+ void endVisit(AST::DefaultClause *) override;
+
+ bool visit(AST::CaseClause *) override;
+ void endVisit(AST::CaseClause *) override;
+
+ bool visit(AST::CaseClauses *) override;
+ void endVisit(AST::CaseClauses *) override;
+
+ bool visit(AST::CaseBlock *) override;
+ void endVisit(AST::CaseBlock *) override;
+
+ bool visit(AST::SwitchStatement *) override;
+ void endVisit(AST::SwitchStatement *) override;
+
+ bool visit(AST::WhileStatement *) override;
+ void endVisit(AST::WhileStatement *) override;
+
+ bool visit(AST::DoWhileStatement *) override;
+ void endVisit(AST::DoWhileStatement *) override;
+
+ bool visit(AST::ForEachStatement *) override;
+ void endVisit(AST::ForEachStatement *) override;
+
+ bool visit(AST::ClassExpression *) override;
+ void endVisit(AST::ClassExpression *) override;
+
+ bool visit(AST::TemplateLiteral *) override;
+
+ bool visit(AST::TryStatement *) override;
+ void endVisit(AST::TryStatement *) override;
+
+ bool visit(AST::Catch *) override;
+ void endVisit(AST::Catch *) override;
+
+ bool visit(AST::Finally *) override;
+ void endVisit(AST::Finally *) override;
+
+ bool visit(AST::ThrowStatement *) override;
+ void endVisit(AST::ThrowStatement *) override;
+
+ bool visit(AST::LabelledStatement *) override;
+ void endVisit(AST::LabelledStatement *) override;
+
+ bool visit(AST::ContinueStatement *) override;
+ void endVisit(AST::ContinueStatement *) override;
+
+ bool visit(AST::BreakStatement *) override;
+ void endVisit(AST::BreakStatement *) override;
+
+ bool visit(AST::Expression *) override;
+ void endVisit(AST::Expression *) override;
+
+ bool visit(AST::ConditionalExpression *) override;
+ void endVisit(AST::ConditionalExpression *) override;
+
+ bool visit(AST::UnaryMinusExpression *) override;
+ void endVisit(AST::UnaryMinusExpression *) override;
+
+ bool visit(AST::UnaryPlusExpression *) override;
+ void endVisit(AST::UnaryPlusExpression *) override;
+
+ bool visit(AST::TildeExpression *) override;
+ void endVisit(AST::TildeExpression *) override;
+
+ bool visit(AST::NotExpression *) override;
+ void endVisit(AST::NotExpression *) override;
+
+ bool visit(AST::TypeOfExpression *) override;
+ void endVisit(AST::TypeOfExpression *) override;
+
+ bool visit(AST::DeleteExpression *) override;
+ void endVisit(AST::DeleteExpression *) override;
+
+ bool visit(AST::VoidExpression *) override;
+ void endVisit(AST::VoidExpression *) override;
+
+ bool visit(AST::PostDecrementExpression *) override;
+ void endVisit(AST::PostDecrementExpression *) override;
+
+ bool visit(AST::PostIncrementExpression *) override;
+ void endVisit(AST::PostIncrementExpression *) override;
+
+ bool visit(AST::PreDecrementExpression *) override;
+ void endVisit(AST::PreDecrementExpression *) override;
+
+ bool visit(AST::PreIncrementExpression *) override;
+ void endVisit(AST::PreIncrementExpression *) override;
+
+ bool visit(AST::EmptyStatement *) override;
+ void endVisit(AST::EmptyStatement *) override;
+
+ bool visit(AST::NestedExpression *) override;
+ void endVisit(AST::NestedExpression *) override;
+
+ // lists of stuff whose children do not need a qqmljsscope: visitation order can be custom
+ bool visit(AST::ArgumentList *) override;
+ bool visit(AST::UiParameterList *) override;
+ bool visit(AST::PatternElementList *) override;
+ bool visit(AST::PatternPropertyList *) override;
+ bool visit(AST::VariableDeclarationList *vdl) override;
+ bool visit(AST::Elision *elision) override;
+
+ // lists of stuff whose children need a qqmljsscope: visitation order cannot be custom
+ bool visit(AST::StatementList *list) override;
+ void endVisit(AST::StatementList *list) override;
+
+ // literals and ids
+ bool visit(AST::IdentifierExpression *expression) override;
+ bool visit(AST::NumericLiteral *expression) override;
+ bool visit(AST::StringLiteral *expression) override;
+ bool visit(AST::NullExpression *expression) override;
+ bool visit(AST::TrueLiteral *expression) override;
+ bool visit(AST::FalseLiteral *expression) override;
+ bool visit(AST::ComputedPropertyName *expression) override;
+ bool visit(AST::IdentifierPropertyName *expression) override;
+ bool visit(AST::NumericLiteralPropertyName *expression) override;
+ bool visit(AST::StringLiteralPropertyName *expression) override;
+ bool visit(AST::TypeAnnotation *expression) override;
+
+ void throwRecursionDepthError() override;
+
+public:
+ friend class QQmlDomAstCreatorWithQQmlJSScope;
+};
+
+class QQmlDomAstCreatorWithQQmlJSScope : public AST::Visitor
+{
+public:
+ QQmlDomAstCreatorWithQQmlJSScope(const QQmlJSScope::Ptr &current, MutableDomItem &qmlFile,
+ QQmlJSLogger *logger, QQmlJSImporter *importer);
+
+#define X(name) \
+ bool visit(AST::name *) override; \
+ void endVisit(AST::name *) override;
+ QQmlJSASTClassListToVisit
+#undef X
+
+ virtual void throwRecursionDepthError() override;
+ /*!
+ \internal
+ Disable the DOM for scriptexpressions, as not yet unimplemented script elements might crash
+ the construction.
+ */
+ void enableScriptExpressions(bool enable = true)
+ {
+ m_enableScriptExpressions = enable;
+ m_domCreator.enableScriptExpressions(enable);
+ }
+
+ void enableLoadFileLazily(bool enable = true)
+ {
+ m_loadFileLazily = enable;
+ m_domCreator.enableLoadFileLazily(enable);
+ }
+
+ QQmlJSImportVisitor &scopeCreator() { return m_scopeCreator; }
+
+private:
+ void setScopeInDomAfterEndvisit();
+ void setScopeInDomBeforeEndvisit();
+
+ template<typename U, typename... V>
+ using IsInList = std::disjunction<std::is_same<U, V>...>;
+ template<typename U>
+ using RequiresCustomIteration = IsInList<U, AST::PatternElementList, AST::PatternPropertyList,
+ AST::FormalParameterList>;
+
+ enum VisitorKind : bool { DomCreator, ScopeCreator };
+ /*! \internal
+ \brief Holds the information to reactivate a visitor
+ This struct tracks a visitor during its inactive phases
+ and holds the information needed to reactivate the visitor.
+ */
+ struct InactiveVisitorMarker
+ {
+ qsizetype count;
+ AST::Node::Kind nodeKind;
+ VisitorKind inactiveVisitorKind;
+
+ VisitorKind stillActiveVisitorKind() const
+ {
+ return inactiveVisitorKind == DomCreator ? ScopeCreator : DomCreator;
+ }
+ };
+
+ template<typename T>
+ void customListIteration(T *t)
+ {
+ static_assert(RequiresCustomIteration<T>::value);
+ for (auto it = t; it; it = it->next) {
+ if constexpr (std::is_same_v<T, AST::PatternElementList>) {
+ AST::Node::accept(it->elision, this);
+ AST::Node::accept(it->element, this);
+ } else if constexpr (std::is_same_v<T, AST::PatternPropertyList>) {
+ AST::Node::accept(it->property, this);
+ } else if constexpr (std::is_same_v<T, AST::FormalParameterList>) {
+ AST::Node::accept(it->element, this);
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+ }
+
+ static void initMarkerForActiveVisitor(std::optional<InactiveVisitorMarker> &inactiveVisitorMarker,
+ AST::Node::Kind nodeKind, bool continueForDom)
+ {
+ inactiveVisitorMarker.emplace();
+ inactiveVisitorMarker->inactiveVisitorKind = continueForDom ? ScopeCreator : DomCreator;
+ inactiveVisitorMarker->count = 1;
+ inactiveVisitorMarker->nodeKind = nodeKind;
+ };
+
+ template<typename T>
+ bool performListIterationIfRequired(T *t)
+ {
+ if constexpr (RequiresCustomIteration<T>::value) {
+ customListIteration(t);
+ return false;
+ }
+ Q_UNUSED(t);
+ return true;
+ }
+
+ template<typename T>
+ bool visitT(T *t)
+ {
+ const auto handleVisitResult = [this, t](const bool continueVisit) {
+ if (m_inactiveVisitorMarker && m_inactiveVisitorMarker->nodeKind == t->kind)
+ m_inactiveVisitorMarker->count += 1;
+
+ if (continueVisit)
+ return performListIterationIfRequired(t);
+ return continueVisit;
+ };
+
+ // first case: no marker, both can visit
+ if (!m_inactiveVisitorMarker) {
+ bool continueForDom = m_domCreator.visit(t);
+ bool continueForScope = m_scopeCreator.visit(t);
+ if (!continueForDom && !continueForScope)
+ return false;
+ else if (continueForDom ^ continueForScope) {
+ initMarkerForActiveVisitor(m_inactiveVisitorMarker, AST::Node::Kind(t->kind),
+ continueForDom);
+ return performListIterationIfRequired(t);
+ } else {
+ Q_ASSERT(continueForDom && continueForScope);
+ return performListIterationIfRequired(t);
+ }
+ Q_UNREACHABLE();
+ }
+
+ // second case: a marker, just one visit
+ switch (m_inactiveVisitorMarker->stillActiveVisitorKind()) {
+ case DomCreator:
+ return handleVisitResult(m_domCreator.visit(t));
+ case ScopeCreator:
+ return handleVisitResult(m_scopeCreator.visit(t));
+ };
+ Q_UNREACHABLE();
+ }
+
+ template<typename T>
+ void endVisitT(T *t)
+ {
+ if (m_inactiveVisitorMarker && m_inactiveVisitorMarker->nodeKind == t->kind) {
+ m_inactiveVisitorMarker->count -= 1;
+ if (m_inactiveVisitorMarker->count == 0)
+ m_inactiveVisitorMarker.reset();
+ }
+ if (m_inactiveVisitorMarker) {
+ switch (m_inactiveVisitorMarker->stillActiveVisitorKind()) {
+ case DomCreator:
+ m_domCreator.endVisit(t);
+ return;
+ case ScopeCreator:
+ m_scopeCreator.endVisit(t);
+ return;
+ };
+ Q_UNREACHABLE();
+ }
+
+ setScopeInDomBeforeEndvisit();
+ m_domCreator.endVisit(t);
+ setScopeInDomAfterEndvisit();
+ m_scopeCreator.endVisit(t);
+ }
+
+ QQmlJSScope::Ptr m_root;
+ QQmlJSLogger *m_logger = nullptr;
+ QQmlJSImporter *m_importer = nullptr;
+ QString m_implicitImportDirectory;
+ QQmlJSImportVisitor m_scopeCreator;
+ QQmlDomAstCreator m_domCreator;
-void createDom(MutableDomItem qmlFile);
+ std::optional<InactiveVisitorMarker> m_inactiveVisitorMarker;
+ bool m_enableScriptExpressions = false;
+ bool m_loadFileLazily = false;
+};
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomastdumper.cpp b/src/qmldom/qqmldomastdumper.cpp
index ffb776cf2d..b4986f7a71 100644
--- a/src/qmldom/qqmldomastdumper.cpp
+++ b/src/qmldom/qqmldomastdumper.cpp
@@ -132,6 +132,14 @@ public:
bool visit(UiHeaderItemList *) override { start(u"UiHeaderItemList"); return true; }
void endVisit(AST::UiHeaderItemList *) override { stop(u"UiHeaderItemList"); }
+#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
+ bool visit(UiPragmaValueList *el) override {
+ start(QLatin1String("UiPragmaValueList value=%1").arg(el->value));
+ return true;
+ }
+ void endVisit(AST::UiPragmaValueList *) override { stop(u"UiPragmaValueList"); }
+#endif
+
bool visit(UiPragma *el) override {
start(QLatin1String("UiPragma name=%1 pragmaToken=%2%3")
.arg(quotedString(el->name), loc(el->pragmaToken),
@@ -330,7 +338,7 @@ public:
void endVisit(AST::ThisExpression *) override { stop(u"ThisExpression"); }
bool visit(AST::IdentifierExpression *el) override {
- start(QLatin1String("IdentifierExpression name=%1 identiferToken=%2")
+ start(QLatin1String("IdentifierExpression name=%1 identifierToken=%2")
.arg(quotedString(el->name), loc(el->identifierToken)));
return true;
}
@@ -982,12 +990,6 @@ public:
}
void endVisit(AST::Type *) override { stop(u"Type"); }
- bool visit(AST::TypeArgument *) override {
- start(u"TypeArgument");
- return true;
- }
- void endVisit(AST::TypeArgument *) override { stop(u"TypeArgument"); }
-
bool visit(AST::TypeAnnotation *el) override {
start(QLatin1String("TypeAnnotation colonToken=%1")
.arg(loc(el->colonToken)));
@@ -1082,7 +1084,7 @@ QString astNodeDiff(AST::Node *n1, AST::Node *n2, int nContext, AstDumperOptions
return lineDiff(s1, s2, nContext);
}
-void astNodeDumper(Sink s, Node *n, AstDumperOptions opt, int indent, int baseIndent,
+void astNodeDumper(const Sink &s, Node *n, AstDumperOptions opt, int indent, int baseIndent,
function_ref<QStringView(SourceLocation)>loc2str)
{
AstDumper visitor=AstDumper(s, opt, indent, baseIndent, loc2str);
@@ -1092,7 +1094,10 @@ void astNodeDumper(Sink s, Node *n, AstDumperOptions opt, int indent, int baseIn
QString astNodeDump(Node *n, AstDumperOptions opt, int indent, int baseIndent,
function_ref<QStringView(SourceLocation)>loc2str)
{
- return dumperToString([n, opt, indent, baseIndent, loc2str](Sink s) { astNodeDumper(s, n, opt, indent, baseIndent, loc2str); });
+ return dumperToString(
+ [n, opt, indent, baseIndent, loc2str = std::move(loc2str)](const Sink &s) {
+ astNodeDumper(s, n, opt, indent, baseIndent, std::move(loc2str));
+ });
}
} // end namespace Dom
diff --git a/src/qmldom/qqmldomastdumper_p.h b/src/qmldom/qqmldomastdumper_p.h
index c942f53a0e..542f81b1dd 100644
--- a/src/qmldom/qqmldomastdumper_p.h
+++ b/src/qmldom/qqmldomastdumper_p.h
@@ -39,7 +39,7 @@ QMLDOM_EXPORT QString astNodeDiff(AST::Node *n1, AST::Node *n2, int nContext = 3
AstDumperOptions opt = AstDumperOption::None, int indent = 0,
function_ref<QStringView(SourceLocation)> loc2str1 = noStr,
function_ref<QStringView(SourceLocation)> loc2str2 = noStr);
-QMLDOM_EXPORT void astNodeDumper(Sink s, AST::Node *n, AstDumperOptions opt = AstDumperOption::None,
+QMLDOM_EXPORT void astNodeDumper(const Sink &s, AST::Node *n, AstDumperOptions opt = AstDumperOption::None,
int indent = 1, int baseIndent = 0,
function_ref<QStringView(SourceLocation)> loc2str = noStr);
QMLDOM_EXPORT QString astNodeDump(AST::Node *n, AstDumperOptions opt = AstDumperOption::None,
diff --git a/src/qmldom/qqmldomattachedinfo.cpp b/src/qmldom/qqmldomattachedinfo.cpp
index 838b373048..e86a2782a6 100644
--- a/src/qmldom/qqmldomattachedinfo.cpp
+++ b/src/qmldom/qqmldomattachedinfo.cpp
@@ -1,12 +1,17 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qqmldom_fwd_p.h"
#include "qqmldomlinewriter_p.h"
#include "qqmldomelements_p.h"
+#include "qqmldompath_p.h"
QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
+using namespace Qt::StringLiterals;
+
+
/*!
\internal
\class QQmlJS::Dom::FileLocations
@@ -26,120 +31,120 @@ Attributes:
\sa QQmlJs::Dom::AttachedInfo
*/
-bool FileLocations::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool FileLocations::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
-#ifdef QmlDomAddCodeStr
- bool hasCode = false;
- QString codeStr = self.fileObject().field(Fields::code).value().toString();
- auto loc2str = [&self, &codeStr](SourceLocation loc) {
- if (loc.offset < codeStr.length() && loc.end() <= codeStr.length())
- return QStringView(codeStr).mid(loc.offset, loc.length);
- return QStringView();
- };
-#else
- auto loc2str = [](SourceLocation) { return QStringView(); };
-#endif
cont = cont && self.dvValueLazyField(visitor, Fields::fullRegion, [this]() {
- return locationToData(fullRegion);
+ return sourceLocationToQCborValue(fullRegion);
});
- cont = cont && self.dvItemField(visitor, Fields::regions, [this, &self, &loc2str]() {
- return self.subMapItem(Map::fromMapRef<SourceLocation>(
- self.pathFromOwner().field(Fields::regions), regions,
- [&loc2str](DomItem &map, const PathEls::PathComponent &key, SourceLocation &el) {
- return map.subLocationItem(key, el, loc2str(el));
- }));
+ cont = cont && self.dvItemField(visitor, Fields::regions, [this, &self]() -> DomItem {
+ const Path pathFromOwner = self.pathFromOwner().field(Fields::regions);
+ auto map = Map::fromFileRegionMap(pathFromOwner, regions);
+ return self.subMapItem(map);
});
cont = cont
- && self.dvItemField(visitor, Fields::preCommentLocations, [this, &self, &loc2str]() {
- return self.subMapItem(Map::fromMapRef<QList<SourceLocation>>(
- self.pathFromOwner().field(Fields::preCommentLocations),
- preCommentLocations,
- [&loc2str](DomItem &map, const PathEls::PathComponent &key,
- QList<SourceLocation> &el) {
- return map.subListItem(List::fromQListRef<SourceLocation>(
- map.pathFromOwner().appendComponent(key), el,
- [&loc2str](DomItem &list, const PathEls::PathComponent &idx,
- SourceLocation &el) {
- return list.subLocationItem(idx, el, loc2str(el));
- }));
- }));
+ && self.dvItemField(visitor, Fields::preCommentLocations, [this, &self]() -> DomItem {
+ const Path pathFromOwner =
+ self.pathFromOwner().field(Fields::preCommentLocations);
+ auto map = Map::fromFileRegionListMap(pathFromOwner, preCommentLocations);
+ return self.subMapItem(map);
});
cont = cont
- && self.dvItemField(visitor, Fields::postCommentLocations, [this, &self, &loc2str]() {
- return self.subMapItem(Map::fromMapRef<QList<SourceLocation>>(
- self.pathFromOwner().field(Fields::postCommentLocations),
- postCommentLocations,
- [&loc2str](DomItem &map, const PathEls::PathComponent &key,
- QList<SourceLocation> &el) {
- return map.subListItem(List::fromQListRef<SourceLocation>(
- map.pathFromOwner().appendComponent(key), el,
- [&loc2str](DomItem &list, const PathEls::PathComponent &idx,
- SourceLocation &el) {
- return list.subLocationItem(idx, el, loc2str(el));
- }));
- }));
+ && self.dvItemField(visitor, Fields::postCommentLocations, [this, &self]() -> DomItem {
+ const Path pathFromOwner =
+ self.pathFromOwner().field(Fields::postCommentLocations);
+ auto map = Map::fromFileRegionListMap(pathFromOwner, postCommentLocations);
+ return self.subMapItem(map);
});
return cont;
}
-void FileLocations::ensureCommentLocations(QList<QString> keys)
-{
- for (auto k : keys) {
- preCommentLocations[k];
- postCommentLocations[k];
- }
-}
-
-FileLocations::Tree FileLocations::createTree(Path basePath){
+FileLocations::Tree FileLocations::createTree(const Path &basePath){
return AttachedInfoT<FileLocations>::createTree(basePath);
}
-FileLocations::Tree FileLocations::ensure(FileLocations::Tree base, Path basePath, AttachedInfo::PathType pType){
+FileLocations::Tree FileLocations::ensure(
+ const FileLocations::Tree &base, const Path &basePath, AttachedInfo::PathType pType)
+{
return AttachedInfoT<FileLocations>::ensure(base, basePath, pType);
}
+/*!
+\internal
+Allows to query information about the FileLocations::Tree obtained from item, such as path of
+the Tree root in the Dom, the path of this item's Tree in the Dom, and so on.
+
+\note You can use \c{qDebug() << item.path(FileLocations::findAttachedInfo(item).foundTreePath)} or
+\c{item.path(FileLocations::findAttachedInfo(item).foundTreePath).toString()} to print out the Tree
+of item, for example, as Tree's cannot be printed when outside the Dom.
+*/
AttachedInfoLookupResult<FileLocations::Tree>
-FileLocations::findAttachedInfo(DomItem &item, AttachedInfo::FindOptions options)
+FileLocations::findAttachedInfo(const DomItem &item)
{
- return AttachedInfoT<FileLocations>::findAttachedInfo(item, Fields::fileLocationsTree, options);
+ return AttachedInfoT<FileLocations>::findAttachedInfo(item, Fields::fileLocationsTree);
}
-FileLocations::Tree FileLocations::treePtr(DomItem &item)
+/*!
+ \internal
+ Returns the tree corresponding to a DomItem.
+ */
+FileLocations::Tree FileLocations::treeOf(const DomItem &item)
{
- return AttachedInfoT<FileLocations>::treePtr(item, Fields::fileLocationsTree);
+ return findAttachedInfo(item).foundTree;
}
-const FileLocations *FileLocations::fileLocationsPtr(DomItem &item)
+/*!
+ \internal
+ Returns the filelocation Info corresponding to a DomItem.
+ */
+const FileLocations *FileLocations::fileLocationsOf(const DomItem &item)
{
- if (FileLocations::Tree t = treePtr(item))
+ if (const FileLocations::Tree &t = treeOf(item))
return &(t->info());
return nullptr;
}
-void FileLocations::updateFullLocation(FileLocations::Tree fLoc, SourceLocation loc) {
+void FileLocations::updateFullLocation(const FileLocations::Tree &fLoc, SourceLocation loc)
+{
Q_ASSERT(fLoc);
if (loc != SourceLocation()) {
FileLocations::Tree p = fLoc;
while (p) {
SourceLocation &l = p->info().fullRegion;
- if (loc.begin() < l.begin() || loc.end() > l.end())
+ if (loc.begin() < l.begin() || loc.end() > l.end()) {
l = combine(l, loc);
- else
+ p->info().regions[MainRegion] = l;
+ } else {
break;
+ }
p = p->parent();
}
}
}
-void FileLocations::addRegion(FileLocations::Tree fLoc, QString locName, SourceLocation loc) {
+// Adding a new region to file location regions might break down qmlformat because
+// comments might be linked to new region undesirably. We might need to add an
+// exception to AstRangesVisitor::shouldSkipRegion when confronted those cases.
+void FileLocations::addRegion(const FileLocations::Tree &fLoc, FileLocationRegion region,
+ SourceLocation loc)
+{
Q_ASSERT(fLoc);
- fLoc->info().regions[locName] = loc;
+ fLoc->info().regions[region] = loc;
updateFullLocation(fLoc, loc);
}
-void FileLocations::addRegion(FileLocations::Tree fLoc, QStringView locName, SourceLocation loc) {
- addRegion(fLoc, locName.toString(), loc);
+SourceLocation FileLocations::region(const FileLocations::Tree &fLoc, FileLocationRegion region)
+{
+ Q_ASSERT(fLoc);
+ const auto &regions = fLoc->info().regions;
+ if (auto it = regions.constFind(region); it != regions.constEnd() && it->isValid()) {
+ return *it;
+ }
+
+ if (region == MainRegion)
+ return fLoc->info().fullRegion;
+
+ return SourceLocation{};
}
/*!
@@ -160,7 +165,7 @@ Attributes:
\sa QQmlJs::Dom::AttachedInfo
*/
-bool AttachedInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool AttachedInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
if (Ptr p = parent())
@@ -172,13 +177,13 @@ bool AttachedInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::subItems, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::subItems),
- [this](DomItem &map, QString key) {
+ [this](const DomItem &map, const QString &key) {
Path p = Path::fromString(key);
return map.copy(m_subItems.value(p), map.canonicalPath().key(key));
},
- [this](DomItem &) {
+ [this](const DomItem &) {
QSet<QString> res;
- for (auto p : m_subItems.keys())
+ for (const auto &p : m_subItems.keys())
res.insert(p.toString());
return res;
},
@@ -202,7 +207,9 @@ AttachedInfo::AttachedInfo(const AttachedInfo &o):
The path might be either a relative path or a canonical path, as specified by the PathType
*/
-AttachedInfo::Ptr AttachedInfo::ensure(AttachedInfo::Ptr self, Path path, AttachedInfo::PathType pType){
+AttachedInfo::Ptr AttachedInfo::ensure(
+ const AttachedInfo::Ptr &self, const Path &path, AttachedInfo::PathType pType){
+ Path relative;
switch (pType) {
case PathType::Canonical: {
if (!path)
@@ -210,14 +217,15 @@ AttachedInfo::Ptr AttachedInfo::ensure(AttachedInfo::Ptr self, Path path, Attach
Q_ASSERT(self);
Path removed = path.mid(0, self->path().length());
Q_ASSERT(removed == self->path());
- path = path.mid(self->path().length());
+ relative = path.mid(self->path().length());
} break;
case PathType::Relative:
Q_ASSERT(self);
+ relative = path;
break;
}
Ptr res = self;
- for (auto p : path) {
+ for (const auto &p : std::as_const(relative)) {
if (AttachedInfo::Ptr subEl = res->m_subItems.value(p)) {
res = subEl;
} else {
@@ -229,15 +237,21 @@ AttachedInfo::Ptr AttachedInfo::ensure(AttachedInfo::Ptr self, Path path, Attach
return res;
}
-AttachedInfo::Ptr AttachedInfo::find(AttachedInfo::Ptr self, Path p, AttachedInfo::PathType pType){
+AttachedInfo::Ptr AttachedInfo::find(
+ const AttachedInfo::Ptr &self, const Path &p, AttachedInfo::PathType pType)
+{
+ Path rest;
if (pType == PathType::Canonical) {
if (!self) return nullptr;
Path removed = p.mid(0, self->path().length());
if (removed != self->path())
return nullptr;
+ rest = p.dropFront(self->path().length());
+ } else {
+ rest = p;
}
+
AttachedInfo::Ptr res = self;
- Path rest = p;
while (rest) {
if (!res)
break;
@@ -248,8 +262,7 @@ AttachedInfo::Ptr AttachedInfo::find(AttachedInfo::Ptr self, Path p, AttachedInf
}
AttachedInfoLookupResult<AttachedInfo::Ptr>
-AttachedInfo::findAttachedInfo(DomItem &item, QStringView fieldName,
- AttachedInfo::FindOptions options)
+AttachedInfo::findAttachedInfo(const DomItem &item, QStringView fieldName)
{
Path p;
DomItem fLoc = item.field(fieldName);
@@ -272,60 +285,55 @@ AttachedInfo::findAttachedInfo(DomItem &item, QStringView fieldName,
if (AttachedInfo::Ptr foundTree =
AttachedInfo::find(fLocPtr, p, AttachedInfo::PathType::Relative))
res.foundTree = foundTree;
- if (options & (FindOption::SetRootTreePath | FindOption::SetFoundTreePath))
- res.rootTreePath = fLoc.canonicalPath();
- if (options & FindOption::SetFoundTreePath) {
- Path foundTreePath = res.rootTreePath.value();
- if (res.lookupPath) {
- foundTreePath = foundTreePath.key(res.lookupPath.head().toString());
- for (Path pEl : res.lookupPath.mid(1))
- foundTreePath = foundTreePath.field(Fields::subItems).key(pEl.toString());
- }
- res.foundTreePath = foundTreePath;
- }
+ res.rootTreePath = fLoc.canonicalPath();
+
+ res.foundTreePath = res.rootTreePath;
+ for (const Path &pEl : res.lookupPath)
+ res.foundTreePath = res.foundTreePath.field(Fields::subItems).key(pEl.toString());
return res;
}
-bool UpdatedScriptExpression::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool UpdatedScriptExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvWrapField(visitor, Fields::expr, expr);
return cont;
}
-UpdatedScriptExpression::Tree UpdatedScriptExpression::createTree(Path basePath)
+UpdatedScriptExpression::Tree UpdatedScriptExpression::createTree(const Path &basePath)
{
return AttachedInfoT<UpdatedScriptExpression>::createTree(basePath);
}
-UpdatedScriptExpression::Tree UpdatedScriptExpression::ensure(UpdatedScriptExpression::Tree base,
- Path basePath,
- AttachedInfo::PathType pType)
+UpdatedScriptExpression::Tree UpdatedScriptExpression::ensure(
+ const UpdatedScriptExpression::Tree &base, const Path &basePath,
+ AttachedInfo::PathType pType)
{
return AttachedInfoT<UpdatedScriptExpression>::ensure(base, basePath, pType);
}
AttachedInfoLookupResult<UpdatedScriptExpression::Tree>
-UpdatedScriptExpression::findAttachedInfo(DomItem &item, AttachedInfo::FindOptions options)
+UpdatedScriptExpression::findAttachedInfo(const DomItem &item)
{
return AttachedInfoT<UpdatedScriptExpression>::findAttachedInfo(
- item, Fields::updatedScriptExpressions, options);
+ item, Fields::updatedScriptExpressions);
}
-UpdatedScriptExpression::Tree UpdatedScriptExpression::treePtr(DomItem &item)
+UpdatedScriptExpression::Tree UpdatedScriptExpression::treePtr(const DomItem &item)
{
return AttachedInfoT<UpdatedScriptExpression>::treePtr(item, Fields::updatedScriptExpressions);
}
-const UpdatedScriptExpression *UpdatedScriptExpression::exprPtr(DomItem &item)
+const UpdatedScriptExpression *UpdatedScriptExpression::exprPtr(const DomItem &item)
{
if (UpdatedScriptExpression::Tree t = treePtr(item))
return &(t->info());
return nullptr;
}
-bool UpdatedScriptExpression::visitTree(Tree base, function_ref<bool(Path, Tree)> visitor,
- Path basePath)
+bool UpdatedScriptExpression::visitTree(
+ const Tree &base, function_ref<bool(const Path &, const Tree &)> visitor,
+ const Path &basePath)
{
return AttachedInfoT<UpdatedScriptExpression>::visitTree(base, visitor, basePath);
}
diff --git a/src/qmldom/qqmldomattachedinfo_p.h b/src/qmldom/qqmldomattachedinfo_p.h
index 808d0db467..8412c3ab9b 100644
--- a/src/qmldom/qqmldomattachedinfo_p.h
+++ b/src/qmldom/qqmldomattachedinfo_p.h
@@ -25,22 +25,25 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
+struct AttachedInfoLookupResultBase
+{
+ Path lookupPath;
+ Path rootTreePath;
+ Path foundTreePath;
+};
template<typename TreePtr>
-class AttachedInfoLookupResult
+class AttachedInfoLookupResult: public AttachedInfoLookupResultBase
{
public:
TreePtr foundTree;
- Path lookupPath; // relative path used to reach result
- std::optional<Path> rootTreePath; // path of the root TreePath
- std::optional<Path> foundTreePath;
+
operator bool() { return bool(foundTree); }
template<typename T>
AttachedInfoLookupResult<std::shared_ptr<T>> as() const
{
AttachedInfoLookupResult<std::shared_ptr<T>> res;
+ res.AttachedInfoLookupResultBase::operator=(*this);
res.foundTree = std::static_pointer_cast<T>(foundTree);
- res.lookupPath = lookupPath;
- res.rootTreePath = rootTreePath;
return res;
}
};
@@ -53,51 +56,44 @@ public:
Canonical
};
Q_ENUM(PathType)
- enum class FindOption {
- None = 0,
- SetRootTreePath = 0x1,
- SetFoundTreePath = 0x2,
- Default = 0x3
- };
- Q_DECLARE_FLAGS(FindOptions, FindOption)
- Q_FLAG(FindOptions)
constexpr static DomType kindValue = DomType::AttachedInfo;
using Ptr = std::shared_ptr<AttachedInfo>;
DomType kind() const override { return kindValue; }
- Path canonicalPath(DomItem &self) const override { return self.m_ownerPath; }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+ Path canonicalPath(const DomItem &self) const override { return self.m_ownerPath; }
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
- AttachedInfo::Ptr makeCopy(DomItem &self) const
+ AttachedInfo::Ptr makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<AttachedInfo>(doCopy(self));
}
Ptr parent() const { return m_parent.lock(); }
Path path() const { return m_path; }
- void setPath(Path p) { m_path = p; }
+ void setPath(const Path &p) { m_path = p; }
+
+ AttachedInfo(const Ptr &parent = nullptr, const Path &p = Path())
+ : m_path(p), m_parent(parent)
+ {}
- AttachedInfo(Ptr parent = nullptr, Path p = Path()) : m_path(p), m_parent(parent) {}
AttachedInfo(const AttachedInfo &o);
- static Ptr ensure(Ptr self, Path path, PathType pType = PathType::Relative);
- static Ptr find(Ptr self, Path p, PathType pType = PathType::Relative);
- static AttachedInfoLookupResult<Ptr>
- findAttachedInfo(DomItem &item, QStringView treeFieldName,
- FindOptions options = AttachedInfo::FindOption::None);
- static Ptr treePtr(DomItem &item, QStringView fieldName)
+ static Ptr ensure(const Ptr &self, const Path &path, PathType pType = PathType::Relative);
+ static Ptr find(const Ptr &self, const Path &p, PathType pType = PathType::Relative);
+ static AttachedInfoLookupResult<Ptr> findAttachedInfo(const DomItem &item,
+ QStringView treeFieldName);
+ static Ptr treePtr(const DomItem &item, QStringView fieldName)
{
- return findAttachedInfo(item, fieldName, FindOption::None).foundTree;
+ return findAttachedInfo(item, fieldName).foundTree;
}
- DomItem itemAtPath(DomItem &self, Path p, PathType pType = PathType::Relative) const
+ DomItem itemAtPath(const DomItem &self, const Path &p, PathType pType = PathType::Relative) const
{
if (Ptr resPtr = find(self.ownerAs<AttachedInfo>(), p, pType)) {
- if (pType == PathType::Canonical)
- p = p.mid(m_path.length());
+ const Path relative = (pType == PathType::Canonical) ? p.mid(m_path.length()) : p;
Path resPath = self.canonicalPath();
- for (Path pEl : p) {
+ for (const Path &pEl : relative) {
resPath = resPath.field(Fields::subItems).key(pEl.toString());
}
return self.copy(resPtr, resPath);
@@ -105,19 +101,18 @@ public:
return DomItem();
}
- DomItem infoAtPath(DomItem &self, Path p, PathType pType = PathType::Relative) const
+ DomItem infoAtPath(const DomItem &self, const Path &p, PathType pType = PathType::Relative) const
{
return itemAtPath(self, p, pType).field(Fields::infoItem);
}
- MutableDomItem ensureItemAtPath(MutableDomItem &self, Path p,
+ MutableDomItem ensureItemAtPath(MutableDomItem &self, const Path &p,
PathType pType = PathType::Relative)
{
if (Ptr resPtr = ensure(self.ownerAs<AttachedInfo>(), p, pType)) {
- if (pType == PathType::Canonical)
- p = p.mid(m_path.length());
+ const Path relative = (pType == PathType::Canonical) ? p.mid(m_path.length()) : p;
Path resPath = self.canonicalPath();
- for (Path pEl : p) {
+ for (const Path &pEl : relative) {
resPath = resPath.field(Fields::subItems).key(pEl.toString());
}
return MutableDomItem(self.item().copy(resPtr, resPath));
@@ -125,18 +120,15 @@ public:
return MutableDomItem();
}
- MutableDomItem ensureInfoAtPath(MutableDomItem &self, Path p,
+ MutableDomItem ensureInfoAtPath(MutableDomItem &self, const Path &p,
PathType pType = PathType::Relative)
{
return ensureItemAtPath(self, p, pType).field(Fields::infoItem);
}
- virtual AttachedInfo::Ptr instantiate(AttachedInfo::Ptr parent, Path p = Path()) const = 0;
- virtual DomItem infoItem(DomItem &self) = 0;
- DomItem infoItem(DomItem &self) const
- {
- return const_cast<AttachedInfo *>(this)->infoItem(self);
- }
+ virtual AttachedInfo::Ptr instantiate(
+ const AttachedInfo::Ptr &parent, const Path &p = Path()) const = 0;
+ virtual DomItem infoItem(const DomItem &self) const = 0;
QMap<Path, Ptr> subItems() const {
return m_subItems;
}
@@ -148,7 +140,6 @@ protected:
std::weak_ptr<AttachedInfo> m_parent;
QMap<Path, Ptr> m_subItems;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(AttachedInfo::FindOptions)
template<typename Info>
class QMLDOM_EXPORT AttachedInfoT final : public AttachedInfo
@@ -158,7 +149,7 @@ public:
using Ptr = std::shared_ptr<AttachedInfoT>;
using InfoType = Info;
- AttachedInfoT(Ptr parent = nullptr, Path p = Path()) : AttachedInfo(parent, p) {}
+ AttachedInfoT(const Ptr &parent = nullptr, const Path &p = Path()) : AttachedInfo(parent, p) {}
AttachedInfoT(const AttachedInfoT &o):
AttachedInfo(o),
m_info(o.m_info)
@@ -171,29 +162,32 @@ public:
}
}
- static Ptr createTree(Path p = Path()) {
+ static Ptr createTree(const Path &p = Path()) {
return Ptr(new AttachedInfoT(nullptr, p));
}
- static Ptr ensure(Ptr self, Path path, PathType pType = PathType::Relative){
+ static Ptr ensure(const Ptr &self, const Path &path, PathType pType = PathType::Relative)
+ {
return std::static_pointer_cast<AttachedInfoT>(AttachedInfo::ensure(self, path, pType));
}
- static Ptr find(Ptr self, Path p, PathType pType = PathType::Relative){
+ static Ptr find(const Ptr &self, const Path &p, PathType pType = PathType::Relative)
+ {
return std::static_pointer_cast<AttachedInfoT>(AttachedInfo::find(self, p, pType));
}
- static AttachedInfoLookupResult<Ptr> findAttachedInfo(DomItem &item, QStringView fieldName,
- AttachedInfo::FindOptions options)
+ static AttachedInfoLookupResult<Ptr> findAttachedInfo(const DomItem &item,
+ QStringView fieldName)
{
- return AttachedInfo::findAttachedInfo(item, fieldName, options)
- .template as<AttachedInfoT>();
+ return AttachedInfo::findAttachedInfo(item, fieldName).template as<AttachedInfoT>();
}
- static Ptr treePtr(DomItem &item, QStringView fieldName)
+ static Ptr treePtr(const DomItem &item, QStringView fieldName)
{
return std::static_pointer_cast<AttachedInfoT>(AttachedInfo::treePtr(item, fieldName));
}
- static bool visitTree(Ptr base, function_ref<bool(Path, Ptr)>visitor, Path basePath = Path()) {
+ static bool visitTree(
+ const Ptr &base, function_ref<bool(const Path &, const Ptr &)> visitor,
+ const Path &basePath = Path()) {
if (base) {
Path pNow = basePath.path(base->path());
if (visitor(pNow, base)) {
@@ -211,12 +205,15 @@ public:
return true;
}
- AttachedInfo::Ptr instantiate(AttachedInfo::Ptr parent, Path p = Path()) const override {
+ AttachedInfo::Ptr instantiate(
+ const AttachedInfo::Ptr &parent, const Path &p = Path()) const override
+ {
return Ptr(new AttachedInfoT(std::static_pointer_cast<AttachedInfoT>(parent), p));
}
- DomItem infoItem(DomItem &self) override { return self.wrapField(Fields::infoItem, m_info); }
- Ptr makeCopy(DomItem &self) const
+ DomItem infoItem(const DomItem &self) const override { return self.wrapField(Fields::infoItem, m_info); }
+
+ Ptr makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<AttachedInfoT>(doCopy(self));
}
@@ -225,8 +222,18 @@ public:
const Info &info() const { return m_info; }
Info &info() { return m_info; }
+
+ QString canonicalPathForTesting() const
+ {
+ QString result;
+ for (auto *it = this; it; it = it->parent().get()) {
+ result.prepend(it->path().toString());
+ }
+ return result;
+ }
+
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
return Ptr(new AttachedInfoT(*this));
}
@@ -240,34 +247,34 @@ public:
using Tree = std::shared_ptr<AttachedInfoT<FileLocations>>;
constexpr static DomType kindValue = DomType::FileLocations;
DomType kind() const { return kindValue; }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
- void ensureCommentLocations(QList<QString> keys);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const;
- static Tree createTree(Path basePath);
- static Tree ensure(Tree base, Path basePath, AttachedInfo::PathType pType);
- static Tree find(Tree self, Path p,
+ static Tree createTree(const Path &basePath);
+ static Tree ensure(const Tree &base, const Path &basePath,
+ AttachedInfo::PathType pType = AttachedInfo::PathType::Relative);
+ static Tree find(const Tree &self, const Path &p,
AttachedInfo::PathType pType = AttachedInfo::PathType::Relative)
{
return AttachedInfoT<FileLocations>::find(self, p, pType);
}
// returns the path looked up and the found tree when looking for the info attached to item
- static AttachedInfoLookupResult<Tree>
- findAttachedInfo(DomItem &item,
- AttachedInfo::FindOptions options = AttachedInfo::FindOption::Default);
- // convenience: find FileLocations::Tree attached to the given item
- static FileLocations::Tree treePtr(DomItem &);
- // convenience: find FileLocations* attached to the given item (if there is one)
- static const FileLocations *fileLocationsPtr(DomItem &);
+ static AttachedInfoLookupResult<Tree> findAttachedInfo(const DomItem &item);
+ static FileLocations::Tree treeOf(const DomItem &);
+ static const FileLocations *fileLocationsOf(const DomItem &);
- static void updateFullLocation(Tree fLoc, SourceLocation loc);
- static void addRegion(Tree fLoc, QString locName, SourceLocation loc);
- static void addRegion(Tree fLoc, QStringView locName, SourceLocation loc);
+ static void updateFullLocation(const Tree &fLoc, SourceLocation loc);
+ static void addRegion(const Tree &fLoc, FileLocationRegion region, SourceLocation loc);
+ static QQmlJS::SourceLocation region(const Tree &fLoc, FileLocationRegion region);
+private:
+ static QMetaEnum regionEnum;
+
+public:
SourceLocation fullRegion;
- QMap<QString, SourceLocation> regions;
- QMap<QString, QList<SourceLocation>> preCommentLocations;
- QMap<QString, QList<SourceLocation>> postCommentLocations;
+ QMap<FileLocationRegion, SourceLocation> regions;
+ QMap<FileLocationRegion, QList<SourceLocation>> preCommentLocations;
+ QMap<FileLocationRegion, QList<SourceLocation>> postCommentLocations;
};
class QMLDOM_EXPORT UpdatedScriptExpression
@@ -277,22 +284,22 @@ public:
using Tree = std::shared_ptr<AttachedInfoT<UpdatedScriptExpression>>;
constexpr static DomType kindValue = DomType::UpdatedScriptExpression;
DomType kind() const { return kindValue; }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const;
- static Tree createTree(Path basePath);
- static Tree ensure(Tree base, Path basePath, AttachedInfo::PathType pType);
+ static Tree createTree(const Path &basePath);
+ static Tree ensure(const Tree &base, const Path &basePath, AttachedInfo::PathType pType);
// returns the path looked up and the found tree when looking for the info attached to item
static AttachedInfoLookupResult<Tree>
- findAttachedInfo(DomItem &item,
- AttachedInfo::FindOptions options = AttachedInfo::FindOption::Default);
+ findAttachedInfo(const DomItem &item);
// convenience: find FileLocations::Tree attached to the given item
- static Tree treePtr(DomItem &);
+ static Tree treePtr(const DomItem &);
// convenience: find FileLocations* attached to the given item (if there is one)
- static const UpdatedScriptExpression *exprPtr(DomItem &);
+ static const UpdatedScriptExpression *exprPtr(const DomItem &);
- static bool visitTree(Tree base, function_ref<bool(Path, Tree)> visitor,
- Path basePath = Path());
+ static bool visitTree(
+ const Tree &base, function_ref<bool(const Path &, const Tree &)> visitor,
+ const Path &basePath = Path());
std::shared_ptr<ScriptExpression> expr;
};
diff --git a/src/qmldom/qqmldomcodeformatter.cpp b/src/qmldom/qqmldomcodeformatter.cpp
index 9997905a5e..5660025fd8 100644
--- a/src/qmldom/qqmldomcodeformatter.cpp
+++ b/src/qmldom/qqmldomcodeformatter.cpp
@@ -224,9 +224,8 @@ void FormatPartialStatus::dump() const
{
qCDebug(formatterLog) << "Current token index" << tokenIndex;
qCDebug(formatterLog) << "Current state:";
- foreach (const State &s, currentStatus.states) {
+ for (const State &s : currentStatus.states)
qCDebug(formatterLog) << FormatTextStatus::stateToString(s.type) << s.savedIndentDepth;
- }
qCDebug(formatterLog) << "Current lexerState:" << currentStatus.lexerState.state;
qCDebug(formatterLog) << "Current indent:" << currentIndent;
}
diff --git a/src/qmldom/qqmldomcomments.cpp b/src/qmldom/qqmldomcomments.cpp
index 3eee7adb28..308467b99b 100644
--- a/src/qmldom/qqmldomcomments.cpp
+++ b/src/qmldom/qqmldomcomments.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmldomcomments_p.h"
#include "qqmldomoutwriter_p.h"
@@ -65,7 +65,8 @@ Comments store a string (rawComment) with comment characters (//,..) and spaces.
Sometime one wants just the comment, the commentcharacters, the space before the comment,....
CommentInfo gets such a raw comment string and makes the various pieces available
*/
-CommentInfo::CommentInfo(QStringView rawComment) : rawComment(rawComment)
+CommentInfo::CommentInfo(QStringView rawComment, QQmlJS::SourceLocation loc)
+ : rawComment(rawComment), commentLocation(loc)
{
commentBegin = 0;
while (commentBegin < quint32(rawComment.size()) && rawComment.at(commentBegin).isSpace()) {
@@ -97,10 +98,9 @@ CommentInfo::CommentInfo(QStringView rawComment) : rawComment(rawComment)
warnings.append(tr("Unexpected comment start %1").arg(commentStartStr));
break;
}
+
commentEnd = commentBegin + commentStartStr.size();
quint32 rawEnd = quint32(rawComment.size());
- while (commentEnd < rawEnd && rawComment.at(commentEnd).isSpace())
- ++commentEnd;
commentContentEnd = commentContentBegin = commentEnd;
QChar e1 = ((expectedEnd.isEmpty()) ? QChar::fromLatin1(0) : expectedEnd.at(0));
while (commentEnd < rawEnd) {
@@ -115,7 +115,8 @@ CommentInfo::CommentInfo(QStringView rawComment) : rawComment(rawComment)
commentContentEnd = commentEnd;
}
} else {
- commentEndStr = rawComment.mid(++commentEnd - 1, 1);
+ // Comment ends with \n, treat as it is not part of the comment but post whitespace
+ commentEndStr = rawComment.mid(commentEnd - 1, 1);
break;
}
} else if (!c.isSpace()) {
@@ -156,6 +157,11 @@ CommentInfo::CommentInfo(QStringView rawComment) : rawComment(rawComment)
.arg(i));
}
}
+
+ // Post process comment source location
+ commentLocation.offset -= commentStartStr.size();
+ commentLocation.startColumn -= commentStartStr.size();
+ commentLocation.length = commentEnd - commentBegin;
}
/*!
@@ -191,7 +197,7 @@ A comment has methods to write it out again (write) and expose it to the Dom
/*!
\brief Expose attributes to the Dom
*/
-bool Comment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool Comment::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::rawComment, rawComment());
@@ -236,55 +242,39 @@ Every region has a name, and should be written out using the OutWriter.writeRegi
startRegion/ EndRegion). Region comments keeps a mapping containing them.
*/
-bool CommentedElement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool CommentedElement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
- cont = cont && self.dvWrapField(visitor, Fields::preComments, preComments);
- cont = cont && self.dvWrapField(visitor, Fields::postComments, postComments);
+ cont = cont && self.dvWrapField(visitor, Fields::preComments, m_preComments);
+ cont = cont && self.dvWrapField(visitor, Fields::postComments, m_postComments);
return cont;
}
void CommentedElement::writePre(OutWriter &lw, QList<SourceLocation> *locs) const
{
if (locs)
- locs->resize(preComments.size());
+ locs->resize(m_preComments.size());
int i = 0;
- for (const Comment &c : preComments)
+ for (const Comment &c : m_preComments)
c.write(lw, (locs ? &((*locs)[i++]) : nullptr));
}
void CommentedElement::writePost(OutWriter &lw, QList<SourceLocation> *locs) const
{
if (locs)
- locs->resize(postComments.size());
+ locs->resize(m_postComments.size());
int i = 0;
- for (const Comment &c : postComments)
+ for (const Comment &c : m_postComments)
c.write(lw, (locs ? &((*locs)[i++]) : nullptr));
}
-/*!
-\brief Given the SourceLocation of the current element returns the comments associated with the
-start and end of item
-
-The map uses an index that is based on 2*the location. Thus for every location l it is possible
-to have two indexes: 2*l (just before) and 2*l+1 (just after).
-This allows to attach comments to indexes representing either just before or after any location
-*/
-QMultiMap<quint32, const QList<Comment> *>
-CommentedElement::commentGroups(SourceLocation elLocation) const
-{
- return QMultiMap<quint32, const QList<Comment> *>(
- { { elLocation.begin() * 2, &preComments },
- { elLocation.end() * 2 + 1, &postComments } });
-}
-
using namespace QQmlJS::AST;
class RegionRef
{
public:
Path path; // store the MutableDomItem instead?
- QString regionName;
+ FileLocationRegion regionName;
};
// internal class to keep a reference either to an AST::Node* or a region of a DomItem and the
@@ -293,8 +283,8 @@ class ElementRef
{
public:
ElementRef(AST::Node *node, quint32 size) : element(node), size(size) { }
- ElementRef(Path path, QString region, quint32 size)
- : element(RegionRef { path, region }), size(size)
+ ElementRef(const Path &path, FileLocationRegion region, quint32 size)
+ : element(RegionRef{ path, region }), size(size)
{
}
operator bool() const
@@ -331,6 +321,9 @@ QSet<int> VisitAll::uiKinds()
AST::Node::Kind_UiArrayBinding, AST::Node::Kind_UiImport,
AST::Node::Kind_UiObjectBinding, AST::Node::Kind_UiObjectDefinition,
AST::Node::Kind_UiInlineComponent, AST::Node::Kind_UiObjectInitializer,
+#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
+ AST::Node::Kind_UiPragmaValueList,
+#endif
AST::Node::Kind_UiPragma, AST::Node::Kind_UiProgram,
AST::Node::Kind_UiPublicMember, AST::Node::Kind_UiQualifiedId,
AST::Node::Kind_UiScriptBinding, AST::Node::Kind_UiSourceElement,
@@ -346,11 +339,13 @@ public:
AstRangesVisitor() = default;
void addNodeRanges(AST::Node *rootNode);
- void addItemRanges(DomItem item, FileLocations::Tree itemLocations, Path currentP);
+ void addItemRanges(
+ const DomItem &item, const FileLocations::Tree &itemLocations, const Path &currentP);
void throwRecursionDepthError() override { }
static const QSet<int> kindsToSkip();
+ static bool shouldSkipRegion(const DomItem &item, FileLocationRegion region);
bool preVisit(Node *n) override
{
@@ -365,8 +360,6 @@ public:
return true;
}
- QQmlJS::Engine *engine;
- FileLocations::Tree rootItemLocations;
QMap<quint32, ElementRef> starts;
QMap<quint32, ElementRef> ends;
};
@@ -376,7 +369,8 @@ void AstRangesVisitor::addNodeRanges(AST::Node *rootNode)
AST::Node::accept(rootNode, this);
}
-void AstRangesVisitor::addItemRanges(DomItem item, FileLocations::Tree itemLocations, Path currentP)
+void AstRangesVisitor::addItemRanges(
+ const DomItem &item, const FileLocations::Tree &itemLocations, const Path &currentP)
{
if (!itemLocations) {
if (item)
@@ -389,10 +383,13 @@ void AstRangesVisitor::addItemRanges(DomItem item, FileLocations::Tree itemLocat
for (auto it = regs.cbegin(), end = regs.cend(); it != end; ++it) {
quint32 startI = it.value().begin();
quint32 endI = it.value().end();
- if (!starts.contains(startI))
- starts.insert(startI, { currentP, it.key(), quint32(endI - startI) });
- if (!ends.contains(endI))
- ends.insert(endI, { currentP, it.key(), endI - startI });
+
+ if (!shouldSkipRegion(item, it.key())) {
+ if (!starts.contains(startI))
+ starts.insert(startI, { currentP, it.key(), quint32(endI - startI) });
+ if (!ends.contains(endI))
+ ends.insert(endI, { currentP, it.key(), endI - startI });
+ }
}
}
{
@@ -425,88 +422,99 @@ const QSet<int> AstRangesVisitor::kindsToSkip()
return res;
}
-/*!
-\class QQmlJS::Dom::AstComments
-\brief Stores the comments associated with javascript AST::Node pointers
+/*! \internal
+ \brief returns true if comments should skip attaching to this region
*/
-
-bool AstComments::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool AstRangesVisitor::shouldSkipRegion(const DomItem &item, FileLocationRegion region)
{
- bool cont = self.dvItemField(visitor, Fields::commentedElements, [this, &self]() {
- return self.subMapItem(Map(
- self.pathFromOwner().field(Fields::commentedElements),
- [this](DomItem &map, QString key) {
- bool ok;
- // we expose the comments as map just for debugging purposes,
- // as key we use the address hex value as key (keys must be strings)
- quintptr v = key.split(QLatin1Char('_')).last().toULong(&ok, 16);
- // recover the actual key, and check if it is in the map
- AST::Node *n = reinterpret_cast<AST::Node *>(v);
- if (ok && m_commentedElements.contains(n))
- return map.wrap(PathEls::Key(key), m_commentedElements[n]);
- return DomItem();
- },
- [this](DomItem &) {
- QSet<QString> res;
- for (AST::Node *n : m_commentedElements.keys()) {
- QString name;
- if (n)
- name = QString::number(n->kind); // we should add mapping to
- // string for this
- res.insert(name + QStringLiteral(u"_") + QString::number(quintptr(n), 16));
- }
- return res;
- },
- QLatin1String("CommentedElements")));
- });
- return cont;
+ switch (item.internalKind()) {
+ case DomType::EnumDecl: {
+ return (region == FileLocationRegion::IdentifierRegion)
+ || (region == FileLocationRegion::EnumKeywordRegion);
+ }
+ case DomType::EnumItem: {
+ return (region == FileLocationRegion::IdentifierRegion)
+ || (region == FileLocationRegion::EnumValueRegion);
+ }
+ case DomType::QmlObject: {
+ return (region == FileLocationRegion::RightBraceRegion
+ || region == FileLocationRegion::LeftBraceRegion);
+ }
+ case DomType::Import:
+ case DomType::ImportScope:
+ return region == FileLocationRegion::IdentifierRegion;
+ default:
+ return false;
+ }
+ Q_UNREACHABLE_RETURN(false);
}
-void AstComments::collectComments(MutableDomItem &item)
+class CommentLinker
{
- if (std::shared_ptr<ScriptExpression> scriptPtr = item.ownerAs<ScriptExpression>()) {
- DomItem itemItem = item.item();
- return collectComments(scriptPtr->engine(), scriptPtr->ast(), scriptPtr->astComments(),
- item, FileLocations::treePtr(itemItem));
- } else if (std::shared_ptr<QmlFile> qmlFilePtr = item.ownerAs<QmlFile>()) {
- return collectComments(qmlFilePtr->engine(), qmlFilePtr->ast(), qmlFilePtr->astComments(),
- item, qmlFilePtr->fileLocationsTree());
- } else {
- qCWarning(commentsLog)
- << "collectComments works with QmlFile and ScriptExpression, not with"
- << item.internalKindStr();
+public:
+ CommentLinker(QStringView code, ElementRef &commentedElement, const AstRangesVisitor &ranges, quint32 &lastPostCommentPostEnd,
+ const SourceLocation &commentLocation)
+ : m_code{ code },
+ m_commentedElement{ commentedElement },
+ m_lastPostCommentPostEnd{ lastPostCommentPostEnd },
+ m_ranges{ ranges },
+ m_commentLocation { commentLocation },
+ m_startElement{ m_ranges.starts.lowerBound(commentLocation.begin()) },
+ m_endElement{ m_ranges.ends.lowerBound(commentLocation.end()) },
+ m_spaces{findSpacesAroundComment()}
+ {
}
-}
-/*!
-\brief
-Collects and associates comments with javascript AST::Node pointers and MutableDomItem in
-rootItem
-*/
-void AstComments::collectComments(std::shared_ptr<Engine> engine, AST::Node *n,
- std::shared_ptr<AstComments> ccomm, MutableDomItem rootItem,
- FileLocations::Tree rootItemLocations)
-{
- if (!n)
- return;
- AstRangesVisitor ranges;
- ranges.addItemRanges(rootItem.item(), rootItemLocations, Path());
- ranges.addNodeRanges(n);
- QStringView code = engine->code();
- QHash<AST::Node *, CommentedElement> &commentedElements = ccomm->m_commentedElements;
- quint32 lastPostCommentPostEnd = 0;
- for (SourceLocation cLoc : engine->comments()) {
- // collect whitespace before and after cLoc -> iPre..iPost contains whitespace,
- // do not add newline before, but add the one after
- quint32 iPre = cLoc.begin();
+ void linkCommentWithElement()
+ {
+ if (m_spaces.preNewline < 1) {
+ checkElementBeforeComment();
+ checkElementAfterComment();
+ } else {
+ checkElementAfterComment();
+ checkElementBeforeComment();
+ }
+ if (!m_commentedElement)
+ checkElementInside();
+ }
+
+ [[nodiscard]] Comment createComment() const
+ {
+ const auto [preSpacesIndex, postSpacesIndex, preNewlineCount] = m_spaces;
+ return Comment{ m_code.mid(preSpacesIndex, quint32(postSpacesIndex) - preSpacesIndex),
+ m_commentLocation,
+ static_cast<int>(preNewlineCount),
+ m_commentType};
+ }
+
+private:
+ struct SpaceTrace
+ {
+ quint32 iPre;
+ qsizetype iPost;
+ int preNewline;
+ };
+
+ /*! \internal
+ \brief Returns a Comment data
+ Comment starts from the first non-newline and non-space character preceding
+ the comment start characters. For example, "\n\n // A comment \n\n\n", we
+ hold the prenewlines count (2). PostNewlines are part of the Comment structure
+ but they are not regarded while writing since they could be a part of prenewlines
+ of a following comment.
+ */
+ [[nodiscard]] SpaceTrace findSpacesAroundComment() const
+ {
+ quint32 iPre = m_commentLocation.begin();
int preNewline = 0;
+ int postNewline = 0;
QStringView commentStartStr;
while (iPre > 0) {
- QChar c = code.at(iPre - 1);
+ QChar c = m_code.at(iPre - 1);
if (!c.isSpace()) {
if (commentStartStr.isEmpty() && (c == QLatin1Char('*') || c == QLatin1Char('/'))
- && iPre - 1 > 0 && code.at(iPre - 2) == QLatin1Char('/')) {
- commentStartStr = code.mid(iPre - 2, 2);
+ && iPre - 1 > 0 && m_code.at(iPre - 2) == QLatin1Char('/')) {
+ commentStartStr = m_code.mid(iPre - 2, 2);
--iPre;
} else {
break;
@@ -515,10 +523,10 @@ void AstComments::collectComments(std::shared_ptr<Engine> engine, AST::Node *n,
preNewline = 1;
// possibly add an empty line if it was there (but never more than one)
int i = iPre - 1;
- if (c == QLatin1Char('\n') && i > 0 && code.at(i - 1) == QLatin1Char('\r'))
+ if (c == QLatin1Char('\n') && i > 0 && m_code.at(i - 1) == QLatin1Char('\r'))
--i;
- while (i > 0 && code.at(--i).isSpace()) {
- c = code.at(i);
+ while (i > 0 && m_code.at(--i).isSpace()) {
+ c = m_code.at(i);
if (c == QLatin1Char('\n') || c == QLatin1Char('\r')) {
++preNewline;
break;
@@ -530,194 +538,233 @@ void AstComments::collectComments(std::shared_ptr<Engine> engine, AST::Node *n,
}
if (iPre == 0)
preNewline = 1;
- qsizetype iPost = cLoc.end();
- while (iPost < code.size()) {
- QChar c = code.at(iPost);
+ qsizetype iPost = m_commentLocation.end();
+ while (iPost < m_code.size()) {
+ QChar c = m_code.at(iPost);
if (!c.isSpace()) {
if (!commentStartStr.isEmpty() && commentStartStr.at(1) == QLatin1Char('*')
- && c == QLatin1Char('*') && iPost + 1 < code.size()
- && code.at(iPost + 1) == QLatin1Char('/')) {
+ && c == QLatin1Char('*') && iPost + 1 < m_code.size()
+ && m_code.at(iPost + 1) == QLatin1Char('/')) {
commentStartStr = QStringView();
++iPost;
} else {
break;
}
+ } else {
+ if (c == QLatin1Char('\n')) {
+ ++postNewline;
+ if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char('\n')) {
+ ++iPost;
+ ++postNewline;
+ }
+ } else if (c == QLatin1Char('\r')) {
+ if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char('\n')) {
+ ++iPost;
+ ++postNewline;
+ }
+ }
}
++iPost;
- if (c == QLatin1Char('\n'))
- break;
- if (c == QLatin1Char('\r')) {
- if (iPost < code.size() && code.at(iPost) == QLatin1Char('\n'))
- ++iPost;
+ if (postNewline > 1)
break;
- }
}
- ElementRef commentEl;
- bool pre = true;
- auto iStart = ranges.starts.lowerBound(cLoc.begin());
- auto iEnd = ranges.ends.lowerBound(cLoc.begin());
- Q_ASSERT(!ranges.ends.isEmpty() && !ranges.starts.isEmpty());
-
- auto checkElementBefore = [&]() {
- if (commentEl)
- return;
- // prefer post comment attached to preceding element
- auto preEnd = iEnd;
- auto preStart = iStart;
- if (preEnd != ranges.ends.begin()) {
- --preEnd;
- if (iStart == ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
- // iStart == begin should never happen
- // check that we do not have operators (or in general other things) between
- // preEnd and this because inserting a newline too ealy might invalidate the
- // expression (think a + //comment\n b ==> a // comment\n + b), in this
- // case attaching as preComment of iStart (b in the example) should be
- // preferred as it is safe
- quint32 i = iPre;
- while (i != 0 && code.at(--i).isSpace())
- ;
- if (i <= preEnd.key() || i < lastPostCommentPostEnd
- || iEnd == ranges.ends.end()) {
- commentEl = preEnd.value();
- pre = false;
- lastPostCommentPostEnd = iPost + 1; // ensure the previous check works
- // with multiple post comments
- }
- }
- }
- };
- auto checkElementAfter = [&]() {
- if (commentEl)
- return;
- if (iStart != ranges.starts.end()) {
- // try to add a pre comment of following element
- if (iEnd == ranges.ends.end() || iEnd.key() > iStart.key()) {
- // there is no end of element before iStart begins
- // associate the comment as preComment of iStart
- // (btw iEnd == end should never happen here)
- commentEl = iStart.value();
- return;
+
+ return {iPre, iPost, preNewline};
+ }
+
+ // tries to associate comment as a postComment to currentElement
+ void checkElementBeforeComment()
+ {
+ if (m_commentedElement)
+ return;
+ // prefer post comment attached to preceding element
+ auto preEnd = m_endElement;
+ auto preStart = m_startElement;
+ if (preEnd != m_ranges.ends.begin()) {
+ --preEnd;
+ if (m_startElement == m_ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
+ // iStart == begin should never happen
+ // check that we do not have operators (or in general other things) between
+ // preEnd and this because inserting a newline too ealy might invalidate the
+ // expression (think a + //comment\n b ==> a // comment\n + b), in this
+ // case attaching as preComment of iStart (b in the example) should be
+ // preferred as it is safe
+ quint32 i = m_spaces.iPre;
+ while (i != 0 && m_code.at(--i).isSpace())
+ ;
+ if (i <= preEnd.key() || i < m_lastPostCommentPostEnd
+ || m_endElement == m_ranges.ends.end()) {
+ m_commentedElement = preEnd.value();
+ m_commentType = Comment::Post;
+ m_lastPostCommentPostEnd = m_spaces.iPost + 1; // ensure the previous check works
+ // with multiple post comments
}
}
- if (iStart == ranges.starts.begin()) {
- Q_ASSERT(iStart != ranges.starts.end());
- // we are before the first node (should be handled already by previous case)
- commentEl = iStart.value();
- }
- };
- auto checkInsideEl = [&]() {
- if (commentEl)
- return;
- auto preIStart = iStart;
- if (iStart == ranges.starts.begin()) {
- commentEl = iStart.value(); // checkElementAfter should have handled this
+ }
+ }
+ // tries to associate comment as a preComment to currentElement
+ void checkElementAfterComment()
+ {
+ if (m_commentedElement)
+ return;
+ if (m_startElement != m_ranges.starts.end()) {
+ // try to add a pre comment of following element
+ if (m_endElement == m_ranges.ends.end() || m_endElement.key() > m_startElement.key()) {
+ // there is no end of element before iStart begins
+ // associate the comment as preComment of iStart
+ // (btw iEnd == end should never happen here)
+ m_commentedElement = m_startElement.value();
return;
- } else {
- --preIStart;
}
- // we are inside a node, actually inside both n1 and n2 (which might be the same)
- // add to pre of the smallest between n1 and n2.
- // This is needed because if there are multiple nodes starting/ending at the same
- // place we store only the first (i.e. largest)
- ElementRef n1 = preIStart.value();
- ElementRef n2 = iEnd.value();
- if (n1.size > n2.size)
- commentEl = n2;
- else
- commentEl = n1;
- };
- if (!preNewline) {
- checkElementBefore();
- checkElementAfter();
- } else {
- checkElementAfter();
- checkElementBefore();
}
- if (!commentEl)
- checkInsideEl();
- if (!commentEl) {
- qCWarning(commentsLog) << "Could not assign comment at" << locationToData(cLoc)
- << "adding before root node";
- if (rootItem && (rootItemLocations || !n)) {
- commentEl.element = RegionRef { Path(), QString() };
- commentEl.size =
- rootItemLocations->info()
- .regions.value(QString(), rootItemLocations->info().fullRegion)
- .length;
- // attach to rootItem
- } else if (n) {
- commentEl.element = n;
- commentEl.size = n->lastSourceLocation().end() - n->firstSourceLocation().begin();
- }
+ if (m_startElement == m_ranges.starts.begin()) {
+ Q_ASSERT(m_startElement != m_ranges.starts.end());
+ // we are before the first node (should be handled already by previous case)
+ m_commentedElement = m_startElement.value();
}
- Comment comment(code.mid(iPre, iPost - iPre), preNewline);
- if (commentEl.element.index() == 0 && std::get<0>(commentEl.element)) {
- CommentedElement &cEl = commentedElements[std::get<0>(commentEl.element)];
- if (pre)
- cEl.preComments.append(comment);
- else
- cEl.postComments.append(comment);
- } else if (commentEl.element.index() == 1) {
- DomItem rComments = rootItem.item()
- .path(std::get<1>(commentEl.element).path)
- .field(Fields::comments);
- if (RegionComments *rCommentsPtr = rComments.mutableAs<RegionComments>()) {
- if (pre)
- rCommentsPtr->addPreComment(comment, std::get<1>(commentEl.element).regionName);
- else
- rCommentsPtr->addPostComment(comment,
- std::get<1>(commentEl.element).regionName);
- } else {
- Q_ASSERT(false);
- }
+ }
+ void checkElementInside()
+ {
+ if (m_commentedElement)
+ return;
+ auto preStart = m_startElement;
+ if (m_startElement == m_ranges.starts.begin()) {
+ m_commentedElement = m_startElement.value(); // checkElementAfter should have handled this
+ return;
} else {
- qCWarning(commentsLog)
- << "Failed: no item or node to attach comment" << comment.rawComment();
+ --preStart;
}
+ // we are inside a node, actually inside both n1 and n2 (which might be the same)
+ // add to pre of the smallest between n1 and n2.
+ // This is needed because if there are multiple nodes starting/ending at the same
+ // place we store only the first (i.e. largest)
+ ElementRef n1 = preStart.value();
+ ElementRef n2 = m_endElement.value();
+ if (n1.size > n2.size)
+ m_commentedElement = n2;
+ else
+ m_commentedElement = n1;
}
-}
+private:
+ QStringView m_code;
+ ElementRef &m_commentedElement;
+ quint32 &m_lastPostCommentPostEnd;
+ Comment::CommentType m_commentType = Comment::Pre;
+ const AstRangesVisitor &m_ranges;
+ const SourceLocation &m_commentLocation;
+
+ using RangesIterator = decltype(m_ranges.starts.begin());
+ const RangesIterator m_startElement;
+ const RangesIterator m_endElement;
+ SpaceTrace m_spaces;
+};
-// internal class to collect all comments in a node or its subnodes
-class CommentCollectorVisitor : protected VisitAll
+/*!
+\class QQmlJS::Dom::AstComments
+\brief Stores the comments associated with javascript AST::Node pointers
+*/
+bool AstComments::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
-public:
- CommentCollectorVisitor(AstComments *comments, AST::Node *n) : comments(comments)
- {
- AST::Node::accept(n, this);
+ // TODO: QTBUG-123645
+ // Revert this commit to reproduce crash with tst_qmldomitem::doNotCrashAtAstComments
+ QList<Comment> pre;
+ QList<Comment> post;
+ for (const auto &commentedElement : commentedElements().values()) {
+ pre.append(commentedElement.preComments());
+ post.append(commentedElement.postComments());
}
+ if (!pre.isEmpty())
+ self.dvWrapField(visitor, Fields::preComments, pre);
+ if (!post.isEmpty())
+ self.dvWrapField(visitor, Fields::postComments, post);
- void throwRecursionDepthError() override { }
-
- bool preVisit(Node *n) override
- {
- auto &cEls = comments->commentedElements();
- if (cEls.contains(n))
- nodeComments += cEls[n].commentGroups(
- combine(n->firstSourceLocation(), n->lastSourceLocation()));
- return true;
- }
+ return false;
+}
- AstComments *comments;
- QMultiMap<quint32, const QList<Comment> *> nodeComments;
-};
+CommentCollector::CommentCollector(MutableDomItem item)
+ : m_rootItem{ std::move(item) },
+ m_fileLocations{ FileLocations::treeOf(m_rootItem.item()) }
+{
+}
-/*!
-\brief low level method returns all comments in a node (including its subnodes)
+void CommentCollector::collectComments()
+{
+ if (std::shared_ptr<ScriptExpression> scriptPtr = m_rootItem.ownerAs<ScriptExpression>()) {
+ return collectComments(scriptPtr->engine(), scriptPtr->ast(), scriptPtr->astComments());
+ } else if (std::shared_ptr<QmlFile> qmlFilePtr = m_rootItem.ownerAs<QmlFile>()) {
+ return collectComments(qmlFilePtr->engine(), qmlFilePtr->ast(), qmlFilePtr->astComments());
+ } else {
+ qCWarning(commentsLog)
+ << "collectComments works with QmlFile and ScriptExpression, not with"
+ << m_rootItem.item().internalKindStr();
+ }
+}
-The comments are roughly ordered in the order they appear in the file.
-Multiple values are in reverse order if the index is even.
+/*! \internal
+ \brief Collects and associates comments with javascript AST::Node pointers
+ or with MutableDomItem
*/
-QMultiMap<quint32, const QList<Comment> *> AstComments::allCommentsInNode(AST::Node *n)
+void CommentCollector::collectComments(
+ const std::shared_ptr<Engine> &engine, AST::Node *rootNode,
+ const std::shared_ptr<AstComments> &astComments)
{
- CommentCollectorVisitor v(this, n);
- return v.nodeComments;
+ if (!rootNode)
+ return;
+ AstRangesVisitor ranges;
+ ranges.addItemRanges(m_rootItem.item(), m_fileLocations, Path());
+ ranges.addNodeRanges(rootNode);
+ QStringView code = engine->code();
+ quint32 lastPostCommentPostEnd = 0;
+ for (const SourceLocation &commentLocation : engine->comments()) {
+ // collect whitespace before and after cLoc -> iPre..iPost contains whitespace,
+ // do not add newline before, but add the one after
+ ElementRef elementToBeLinked;
+ CommentLinker linker(code, elementToBeLinked, ranges, lastPostCommentPostEnd, commentLocation);
+ linker.linkCommentWithElement();
+ const auto comment = linker.createComment();
+
+ if (!elementToBeLinked) {
+ qCWarning(commentsLog) << "Could not assign comment at" << sourceLocationToQCborValue(commentLocation)
+ << "adding before root node";
+ if (m_rootItem && (m_fileLocations || !rootNode)) {
+ elementToBeLinked.element = RegionRef{ Path(), MainRegion };
+ elementToBeLinked.size = FileLocations::region(m_fileLocations, MainRegion).length;
+ } else if (rootNode) {
+ elementToBeLinked.element = rootNode;
+ elementToBeLinked.size = rootNode->lastSourceLocation().end() - rootNode->firstSourceLocation().begin();
+ }
+ }
+
+ if (const auto *const commentNode = std::get_if<AST::Node *>(&elementToBeLinked.element)) {
+ auto &commentedElement = astComments->commentedElements()[*commentNode];
+ commentedElement.addComment(comment);
+ } else if (const auto * const regionRef = std::get_if<RegionRef>(&elementToBeLinked.element)) {
+ MutableDomItem regionComments = m_rootItem.item()
+ .path(regionRef->path)
+ .field(Fields::comments);
+ if (auto *regionCommentsPtr = regionComments.mutableAs<RegionComments>())
+ regionCommentsPtr->addComment(comment, regionRef->regionName);
+ else
+ Q_ASSERT(false && "Cannot attach to region comments");
+ } else {
+ qCWarning(commentsLog)
+ << "Failed: no item or node to attach comment" << comment.rawComment();
+ }
+ }
}
-bool RegionComments::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool RegionComments::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
- if (!regionComments.isEmpty())
- cont = cont && self.dvWrapField(visitor, Fields::regionComments, regionComments);
+ if (!m_regionComments.isEmpty()) {
+ cont = cont
+ && self.dvItemField(visitor, Fields::regionComments, [this, &self]() -> DomItem {
+ const Path pathFromOwner =
+ self.pathFromOwner().field(Fields::regionComments);
+ auto map = Map::fromFileRegionMap(pathFromOwner, m_regionComments);
+ return self.subMapItem(map);
+ });
+ }
return cont;
}
diff --git a/src/qmldom/qqmldomcomments_p.h b/src/qmldom/qqmldomcomments_p.h
index e4f85fa3e3..f8fe46c098 100644
--- a/src/qmldom/qqmldomcomments_p.h
+++ b/src/qmldom/qqmldomcomments_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQMLDOMCOMMENTS_P_H
#define QQMLDOMCOMMENTS_P_H
@@ -17,7 +17,6 @@
#include "qqmldom_fwd_p.h"
#include "qqmldomconstants_p.h"
-#include "qqmldomfunctionref_p.h"
#include "qqmldomitem_p.h"
#include "qqmldomattachedinfo_p.h"
@@ -39,7 +38,7 @@ class QMLDOM_EXPORT CommentInfo
{
Q_DECLARE_TR_FUNCTIONS(CommentInfo)
public:
- CommentInfo(QStringView);
+ CommentInfo(QStringView, QQmlJS::SourceLocation loc);
QStringView preWhitespace() const { return rawComment.mid(0, commentBegin); }
@@ -55,17 +54,22 @@ public:
return rawComment.mid(commentEnd, rawComment.size() - commentEnd);
}
- quint32 commentBegin;
- quint32 commentEnd;
- quint32 commentContentBegin;
- quint32 commentContentEnd;
+ // Comment source location populated during lexing doesn't include start strings // or /*
+ // Returns the location starting from // or /*
+ QQmlJS::SourceLocation sourceLocation() const { return commentLocation; }
+
+ quint32 commentBegin = 0;
+ quint32 commentEnd = 0;
+ quint32 commentContentBegin = 0;
+ quint32 commentContentEnd = 0;
QStringView commentStartStr;
QStringView commentEndStr;
bool hasStartNewline = false;
bool hasEndNewline = false;
- int nContentNewlines;
+ int nContentNewlines = 0;
QStringView rawComment;
QStringList warnings;
+ QQmlJS::SourceLocation commentLocation;
};
class QMLDOM_EXPORT Comment
@@ -74,21 +78,28 @@ public:
constexpr static DomType kindValue = DomType::Comment;
DomType kind() const { return kindValue; }
- Comment(QString c, int newlinesBefore = 1)
- : m_commentStr(c), m_comment(m_commentStr), m_newlinesBefore(newlinesBefore)
+ enum CommentType {Pre, Post};
+
+ Comment(const QString &c, const QQmlJS::SourceLocation &loc, int newlinesBefore = 1,
+ CommentType type = Pre)
+ : m_comment(c), m_location(loc), m_newlinesBefore(newlinesBefore), m_type(type)
{
}
- Comment(QStringView c, int newlinesBefore = 1) : m_comment(c), m_newlinesBefore(newlinesBefore)
+ Comment(QStringView c, const QQmlJS::SourceLocation &loc, int newlinesBefore = 1,
+ CommentType type = Pre)
+ : m_comment(c), m_location(loc), m_newlinesBefore(newlinesBefore), m_type(type)
{
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
int newlinesBefore() const { return m_newlinesBefore; }
void setNewlinesBefore(int n) { m_newlinesBefore = n; }
QStringView rawComment() const { return m_comment; }
- CommentInfo info() const { return CommentInfo(m_comment); }
+ CommentInfo info() const { return CommentInfo(m_comment, m_location); }
void write(OutWriter &lw, SourceLocation *commentLocation = nullptr) const;
+ CommentType type() const { return m_type; }
+
friend bool operator==(const Comment &c1, const Comment &c2)
{
return c1.m_newlinesBefore == c2.m_newlinesBefore && c1.m_comment == c2.m_comment;
@@ -96,9 +107,10 @@ public:
friend bool operator!=(const Comment &c1, const Comment &c2) { return !(c1 == c2); }
private:
- QString m_commentStr;
QStringView m_comment;
+ QQmlJS::SourceLocation m_location;
int m_newlinesBefore;
+ CommentType m_type;
};
class QMLDOM_EXPORT CommentedElement
@@ -107,22 +119,33 @@ public:
constexpr static DomType kindValue = DomType::CommentedElement;
DomType kind() const { return kindValue; }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
void writePre(OutWriter &lw, QList<SourceLocation> *locations = nullptr) const;
void writePost(OutWriter &lw, QList<SourceLocation> *locations = nullptr) const;
- QMultiMap<quint32, const QList<Comment> *> commentGroups(SourceLocation elLocation) const;
friend bool operator==(const CommentedElement &c1, const CommentedElement &c2)
{
- return c1.preComments == c2.preComments && c1.postComments == c2.postComments;
+ return c1.m_preComments == c2.m_preComments && c1.m_postComments == c2.m_postComments;
}
friend bool operator!=(const CommentedElement &c1, const CommentedElement &c2)
{
return !(c1 == c2);
}
- QList<Comment> preComments;
- QList<Comment> postComments;
+ void addComment(const Comment &comment)
+ {
+ if (comment.type() == Comment::CommentType::Pre)
+ m_preComments.append(comment);
+ else
+ m_postComments.append(comment);
+ }
+
+ const QList<Comment> &preComments() const { return m_preComments;}
+ const QList<Comment> &postComments() const { return m_postComments;}
+
+private:
+ QList<Comment> m_preComments;
+ QList<Comment> m_postComments;
};
class QMLDOM_EXPORT RegionComments
@@ -131,46 +154,56 @@ public:
constexpr static DomType kindValue = DomType::RegionComments;
DomType kind() const { return kindValue; }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
friend bool operator==(const RegionComments &c1, const RegionComments &c2)
{
- return c1.regionComments == c2.regionComments;
+ return c1.m_regionComments == c2.m_regionComments;
}
friend bool operator!=(const RegionComments &c1, const RegionComments &c2)
{
return !(c1 == c2);
}
- Path addPreComment(const Comment &comment, QString regionName)
+ const QMap<FileLocationRegion, CommentedElement> &regionComments() const { return m_regionComments;}
+ Path addComment(const Comment &comment, FileLocationRegion region)
+ {
+ if (comment.type() == Comment::CommentType::Pre)
+ return addPreComment(comment, region);
+ else
+ return addPostComment(comment, region);
+ }
+
+private:
+ Path addPreComment(const Comment &comment, FileLocationRegion region)
{
- auto &preList = regionComments[regionName].preComments;
+ auto &preList = m_regionComments[region].preComments();
index_type idx = preList.size();
- preList.append(comment);
+ m_regionComments[region].addComment(comment);
return Path::Field(Fields::regionComments)
- .key(regionName)
+ .key(fileLocationRegionName(region))
.field(Fields::preComments)
.index(idx);
}
- Path addPostComment(const Comment &comment, QString regionName)
+ Path addPostComment(const Comment &comment, FileLocationRegion region)
{
- auto &postList = regionComments[regionName].postComments;
+ auto &postList = m_regionComments[region].postComments();
index_type idx = postList.size();
- postList.append(comment);
+ m_regionComments[region].addComment(comment);
return Path::Field(Fields::regionComments)
- .key(regionName)
+ .key(fileLocationRegionName(region))
.field(Fields::postComments)
.index(idx);
}
- QMap<QString, CommentedElement> regionComments;
+ QMap<FileLocationRegion, CommentedElement> m_regionComments;
};
class QMLDOM_EXPORT AstComments final : public OwningItem
{
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
return std::make_shared<AstComments>(*this);
}
@@ -178,18 +211,14 @@ protected:
public:
constexpr static DomType kindValue = DomType::AstComments;
DomType kind() const override { return kindValue; }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- std::shared_ptr<AstComments> makeCopy(DomItem &self) const
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ std::shared_ptr<AstComments> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<AstComments>(doCopy(self));
}
- Path canonicalPath(DomItem &self) const override { return self.m_ownerPath; }
- static void collectComments(MutableDomItem &item);
- static void collectComments(std::shared_ptr<Engine> engine, AST::Node *n,
- std::shared_ptr<AstComments> collectComments,
- MutableDomItem rootItem, FileLocations::Tree rootItemLocations);
- AstComments(std::shared_ptr<Engine> e) : m_engine(e) { }
+ Path canonicalPath(const DomItem &self) const override { return self.m_ownerPath; }
+ AstComments(const std::shared_ptr<Engine> &e) : m_engine(e) { }
AstComments(const AstComments &o)
: OwningItem(o), m_engine(o.m_engine), m_commentedElements(o.m_commentedElements)
{
@@ -199,6 +228,12 @@ public:
{
return m_commentedElements;
}
+
+ QHash<AST::Node *, CommentedElement> &commentedElements()
+ {
+ return m_commentedElements;
+ }
+
CommentedElement *commentForNode(AST::Node *n)
{
if (m_commentedElements.contains(n))
@@ -212,6 +247,20 @@ private:
QHash<AST::Node *, CommentedElement> m_commentedElements;
};
+class CommentCollector
+{
+public:
+ CommentCollector() = default;
+ CommentCollector(MutableDomItem item);
+ void collectComments();
+ void collectComments(const std::shared_ptr<Engine> &engine, AST::Node *rootNode,
+ const std::shared_ptr<AstComments> &astComments);
+
+private:
+ MutableDomItem m_rootItem;
+ FileLocations::Tree m_fileLocations;
+};
+
class VisitAll : public AST::Visitor
{
public:
diff --git a/src/qmldom/qqmldomcompare.cpp b/src/qmldom/qqmldomcompare.cpp
index 92e18944c0..65b357cc75 100644
--- a/src/qmldom/qqmldomcompare.cpp
+++ b/src/qmldom/qqmldomcompare.cpp
@@ -8,8 +8,8 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-bool domCompare(DomItem &i1, DomItem &i2, function_ref<bool(Path, DomItem &, DomItem &)> change,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter,
+bool domCompare(const DomItem &i1, const DomItem &i2, function_ref<bool(Path, const DomItem &, const DomItem &)> change,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter,
Path basePath)
{
DomKind k1 = i1.domKind();
@@ -144,21 +144,26 @@ bool domCompare(DomItem &i1, DomItem &i2, function_ref<bool(Path, DomItem &, Dom
if (v1 != v2)
return change(basePath, i1, i2);
} break;
+ case DomKind::ScriptElement: {
+ // TODO: implement me
+ return false;
+
+ } break;
}
}
return true;
}
QStringList
-domCompareStrList(DomItem &i1, DomItem &i2,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter,
+domCompareStrList(const DomItem &i1, const DomItem &i2,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &) const> filter,
DomCompareStrList stopAtFirstDiff)
{
QStringList res;
bool hasDiff = false;
domCompare(
i1, i2,
- [&res, &hasDiff, stopAtFirstDiff](Path p, DomItem &j1, DomItem &j2) {
+ [&res, &hasDiff, stopAtFirstDiff](const Path &p, const DomItem &j1, const DomItem &j2) {
hasDiff = true;
if (!j1) {
res.append(QStringLiteral("- %1\n").arg(p.toString()));
@@ -206,6 +211,10 @@ domCompareStrList(DomItem &i1, DomItem &i2,
.arg(j2.toString()));
}
} break;
+ case DomKind::ScriptElement: {
+ // implement me
+ break;
+ }
}
}
}
diff --git a/src/qmldom/qqmldomcompare_p.h b/src/qmldom/qqmldomcompare_p.h
index b15fd562fe..651486d7a9 100644
--- a/src/qmldom/qqmldomcompare_p.h
+++ b/src/qmldom/qqmldomcompare_p.h
@@ -26,20 +26,20 @@ namespace QQmlJS {
namespace Dom {
bool domCompare(
- DomItem &i1, DomItem &i2, function_ref<bool(Path, DomItem &, DomItem &)> change,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter,
+ const DomItem &i1, const DomItem &i2, function_ref<bool(Path, const DomItem &, const DomItem &)> change,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter,
Path p = Path());
enum DomCompareStrList { FirstDiff, AllDiffs };
QMLDOM_EXPORT QStringList domCompareStrList(
- DomItem &i1, DomItem &i2,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter,
+ const DomItem &i1, const DomItem &i2,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &) const> filter = noFilter,
DomCompareStrList stopAtFirstDiff = DomCompareStrList::FirstDiff);
inline QStringList domCompareStrList(
- MutableDomItem &i1, DomItem &i2,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter,
+ MutableDomItem &i1, const DomItem &i2,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &) const> filter = noFilter,
DomCompareStrList stopAtFirstDiff = DomCompareStrList::FirstDiff)
{
DomItem ii1 = i1.item();
@@ -47,8 +47,8 @@ inline QStringList domCompareStrList(
}
inline QStringList domCompareStrList(
- DomItem &i1, MutableDomItem &i2,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter,
+ const DomItem &i1, MutableDomItem &i2,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &) const> filter = noFilter,
DomCompareStrList stopAtFirstDiff = DomCompareStrList::FirstDiff)
{
DomItem ii2 = i2.item();
@@ -57,7 +57,7 @@ inline QStringList domCompareStrList(
inline QStringList domCompareStrList(
MutableDomItem &i1, MutableDomItem &i2,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &) const> filter = noFilter,
DomCompareStrList stopAtFirstDiff = DomCompareStrList::FirstDiff)
{
DomItem ii1 = i1.item();
diff --git a/src/qmldom/qqmldomconstants_p.h b/src/qmldom/qqmldomconstants_p.h
index a648379551..469facd4b2 100644
--- a/src/qmldom/qqmldomconstants_p.h
+++ b/src/qmldom/qqmldomconstants_p.h
@@ -101,13 +101,7 @@ Q_ENUM_NS(VisitPrototypesOption)
Q_DECLARE_FLAGS(VisitPrototypesOptions, VisitPrototypesOption)
Q_DECLARE_OPERATORS_FOR_FLAGS(VisitPrototypesOptions)
-enum class DomKind {
- Empty,
- Object,
- List,
- Map,
- Value
-};
+enum class DomKind { Empty, Object, List, Map, Value, ScriptElement };
Q_ENUM_NS(DomKind)
enum class DomType {
@@ -130,7 +124,8 @@ enum class DomType {
// types
EnumDecl, // A in above example
JsResource, // QML file contains QML object, JSFile contains JsResource
- QmltypesComponent, // Component inside a qmltypes fles; compared to component it has exported meta-object revisions; singleton flag; can export multiple names
+ QmltypesComponent, // Component inside a qmltypes fles; compared to component it has exported
+ // meta-object revisions; singleton flag; can export multiple names
QmlComponent, // "normal" QML file based Component; also can represent inline components
GlobalComponent, // component of global object ### REVISIT, try to replace with one of the above
@@ -150,23 +145,29 @@ enum class DomType {
ConstantData, // the 2 in "property int i: 2"; can be any generic data in a QML document
SimpleObjectWrap, // internal wrapping to give uniform DOMItem access; ### research more
ScriptExpression, // wraps an AST script expression as a DOMItem
- Reference, // reference to another DOMItem; e.g. asking for a type of an object returns a Reference
- PropertyDefinition, // _just_ the property definition; without the binding, even if it's one line
+ Reference, // reference to another DOMItem; e.g. asking for a type of an object returns a
+ // Reference
+ PropertyDefinition, // _just_ the property definition; without the binding, even if it's one
+ // line
Binding, // the part after the ":"
MethodParameter,
MethodInfo, // container of MethodParameter
Version, // wrapped
Comment,
CommentedElement, // attached to AST if they have pre-/post-comments?
- RegionComments, // DomItems have attached RegionComments; can attach comments to fine grained "regions" in a DomItem; like the default keyword of a property definition
+ RegionComments, // DomItems have attached RegionComments; can attach comments to fine grained
+ // "regions" in a DomItem; like the default keyword of a property definition
AstComments, // hash-table from AST node to commented element
- FileLocations, // mapping from DomItem to file location ### REVISIT: try to move out of hierarchy?
- UpdatedScriptExpression, // used in writeOut method when formatting changes ### Revisit: try to move out of DOM hierarchy
+ FileLocations, // mapping from DomItem to file location ### REVISIT: try to move out of
+ // hierarchy?
+ UpdatedScriptExpression, // used in writeOut method when formatting changes ### Revisit: try to
+ // move out of DOM hierarchy
// convenience collecting types
PropertyInfo, // not a DOM Item, just a convenience class
- // Moc objects, mainly for testing ### Try to remove them; replace their usage in tests with "real" instances
+ // Moc objects, mainly for testing ### Try to remove them; replace their usage in tests with
+ // "real" instances
MockObject,
MockOwner,
@@ -182,7 +183,52 @@ enum class DomType {
// Dom top level
DomEnvironment, // a consistent view of modules, types, files, etc.
- DomUniverse // a cache of what can be found in the DomEnvironment, contains the latest valid version for every file/type, etc. + latest overall
+ DomUniverse, // a cache of what can be found in the DomEnvironment, contains the latest valid
+ // version for every file/type, etc. + latest overall
+
+ // Dom Script elements
+ // TODO
+ ScriptElementWrap, // internal wrapping to give uniform access of script elements (e.g. for
+ // statement lists)
+ ScriptElementStart, // marker to check if a DomType is a scriptelement or not
+ ScriptBlockStatement = ScriptElementStart,
+ ScriptIdentifierExpression,
+ ScriptLiteral,
+ ScriptForStatement,
+ ScriptIfStatement,
+ ScriptPostExpression,
+ ScriptUnaryExpression,
+ ScriptBinaryExpression,
+ ScriptVariableDeclaration,
+ ScriptVariableDeclarationEntry,
+ ScriptReturnStatement,
+ ScriptGenericElement,
+ ScriptCallExpression,
+ ScriptFormalParameter,
+ ScriptArray,
+ ScriptObject,
+ ScriptProperty,
+ ScriptType,
+ ScriptElision,
+ ScriptArrayEntry,
+ ScriptPattern,
+ ScriptSwitchStatement,
+ ScriptCaseBlock,
+ ScriptCaseClause,
+ ScriptDefaultClause,
+ ScriptWhileStatement,
+ ScriptDoWhileStatement,
+ ScriptForEachStatement,
+ ScriptTryCatchStatement,
+ ScriptThrowStatement,
+ ScriptLabelledStatement,
+ ScriptBreakStatement,
+ ScriptContinueStatement,
+ ScriptConditionalExpression,
+ ScriptEmptyStatement,
+ ScriptParenthesizedExpression,
+
+ ScriptElementStop, // marker to check if a DomType is a scriptelement or not
};
Q_ENUM_NS(DomType)
@@ -203,14 +249,6 @@ enum class ListOptions {
};
Q_ENUM_NS(ListOptions)
-enum class LoadOption {
- DefaultLoad = 0x0,
- ForceLoad = 0x1,
-};
-Q_ENUM_NS(LoadOption)
-Q_DECLARE_FLAGS(LoadOptions, LoadOption)
-Q_DECLARE_OPERATORS_FOR_FLAGS(LoadOptions)
-
enum class EscapeOptions{
OuterQuotes,
NoOuterQuotes
@@ -246,6 +284,14 @@ Q_ENUM_NS(GoTo)
enum class AddOption { KeepExisting, Overwrite };
Q_ENUM_NS(AddOption)
+/*!
+\internal
+FilterUpOptions decide in which direction the filtering is done.
+ReturnInner starts the search at top(), and work its way down to the current
+element.
+ReturnOuter and ReturnOuterNoSelf starts the search at the current element and
+works their way up to to top().
+*/
enum class FilterUpOptions { ReturnOuter, ReturnOuterNoSelf, ReturnInner };
Q_ENUM_NS(FilterUpOptions)
@@ -266,22 +312,100 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(WriteOutChecks)
enum class LocalSymbolsType {
None = 0x0,
- QmlTypes = 0x1,
- Types = 0x3,
- Signals = 0x4,
- Methods = 0xC,
- Attributes = 0x10,
- Ids = 0x20,
- Components = 0x40,
- Namespaces = 0x80,
- Globals = 0x100,
- MethodParameters = 0x200,
- All = 0x3FF
+ ObjectType = 0x1,
+ ValueType = 0x2,
+ Signal = 0x4,
+ Method = 0x8,
+ Attribute = 0x10,
+ Id = 0x20,
+ Namespace = 0x40,
+ Global = 0x80,
+ MethodParameter = 0x100,
+ Singleton = 0x200,
+ AttachedType = 0x400,
};
Q_ENUM_NS(LocalSymbolsType)
Q_DECLARE_FLAGS(LocalSymbolsTypes, LocalSymbolsType)
Q_DECLARE_OPERATORS_FOR_FLAGS(LocalSymbolsTypes)
+/*!
+\internal
+The FileLocationRegion allows to map the different FileLocation subregions to their position in
+the actual code. For example, \c{ColonTokenRegion} denotes the position of the ':' token in a
+binding like `myProperty: something()`, or the ':' token in a pragma like `pragma Hello: World`.
+
+These are used for formatting in qmlformat and autocompletion in qmlls.
+
+MainRegion denotes the entire FileLocation region.
+
+\sa{OutWriter::regionToString}, {FileLocations::regionName}
+*/
+enum FileLocationRegion : int {
+ AsTokenRegion,
+ BreakKeywordRegion,
+ DoKeywordRegion,
+ CaseKeywordRegion,
+ CatchKeywordRegion,
+ ColonTokenRegion,
+ CommaTokenRegion,
+ ComponentKeywordRegion,
+ ContinueKeywordRegion,
+ DefaultKeywordRegion,
+ EllipsisTokenRegion,
+ ElseKeywordRegion,
+ EnumKeywordRegion,
+ EnumValueRegion,
+ EqualTokenRegion,
+ ForKeywordRegion,
+ FinallyKeywordRegion,
+ FirstSemicolonTokenRegion,
+ FunctionKeywordRegion,
+ IdColonTokenRegion,
+ IdNameRegion,
+ IdTokenRegion,
+ IdentifierRegion,
+ IfKeywordRegion,
+ ImportTokenRegion,
+ ImportUriRegion,
+ InOfTokenRegion,
+ LeftBraceRegion,
+ LeftBracketRegion,
+ LeftParenthesisRegion,
+ MainRegion,
+ OperatorTokenRegion,
+ OnTargetRegion,
+ OnTokenRegion,
+ PragmaKeywordRegion,
+ PragmaValuesRegion,
+ PropertyKeywordRegion,
+ QuestionMarkTokenRegion,
+ ReadonlyKeywordRegion,
+ RequiredKeywordRegion,
+ ReturnKeywordRegion,
+ RightBraceRegion,
+ RightBracketRegion,
+ RightParenthesisRegion,
+ SecondSemicolonRegion,
+ SemicolonTokenRegion,
+ SignalKeywordRegion,
+ SwitchKeywordRegion,
+ ThrowKeywordRegion,
+ TryKeywordRegion,
+ TypeIdentifierRegion,
+ VersionRegion,
+ WhileKeywordRegion,
+};
+Q_ENUM_NS(FileLocationRegion);
+
+enum DomCreationOption : char {
+ None = 0,
+ WithSemanticAnalysis = 1,
+ WithScriptExpressions = 2,
+ WithRecovery = 4
+};
+
+Q_DECLARE_FLAGS(DomCreationOptions, DomCreationOption);
+
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomelements.cpp b/src/qmldom/qqmldomelements.cpp
index 74963aef3b..0fe5c3eb36 100644
--- a/src/qmldom/qqmldomelements.cpp
+++ b/src/qmldom/qqmldomelements.cpp
@@ -5,6 +5,8 @@
// but in this type of warning, it often isn't.
//#if defined(Q_CC_GNU) && Q_CC_GNU >= 1100
//QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
+#include "qqmldomconstants_p.h"
+#include "qqmldompath_p.h"
#if defined(__GNUC__) && __GNUC__ >= 11
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
@@ -43,7 +45,7 @@ namespace Dom {
namespace Paths {
-Path moduleIndexPath(QString uri, int majorVersion, ErrorHandler errorHandler)
+Path moduleIndexPath(const QString &uri, int majorVersion, const ErrorHandler &errorHandler)
{
QString version = QString::number(majorVersion);
if (majorVersion == Version::Latest)
@@ -59,7 +61,7 @@ Path moduleIndexPath(QString uri, int majorVersion, ErrorHandler errorHandler)
return Path::Root(PathRoot::Env).field(Fields::moduleIndexWithUri).key(uri).key(version);
}
-Path moduleScopePath(QString uri, Version version, ErrorHandler)
+Path moduleScopePath(const QString &uri, Version version, const ErrorHandler &)
{
return Path::Root(PathRoot::Env)
.field(Fields::moduleIndexWithUri)
@@ -69,7 +71,7 @@ Path moduleScopePath(QString uri, Version version, ErrorHandler)
.key(version.minorString());
}
-Path moduleScopePath(QString uri, QString version, ErrorHandler errorHandler)
+Path moduleScopePath(const QString &uri, const QString &version, const ErrorHandler &errorHandler)
{
Version v = Version::fromString(version);
if (!version.isEmpty() && !(v.isValid() || v.isLatest()))
@@ -85,25 +87,25 @@ static ErrorGroups domParsingErrors()
return res;
}
-bool CommentableDomElement::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool CommentableDomElement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
return cont;
}
-void Component::updatePathFromOwner(Path newPath)
+void Component::updatePathFromOwner(const Path &newPath)
{
DomElement::updatePathFromOwner(newPath);
updatePathFromOwnerMultiMap(m_enumerations, newPath.field(Fields::enumerations));
updatePathFromOwnerQList(m_objects, newPath.field(Fields::objects));
}
-Component::Component(QString name) : CommentableDomElement(Path()), m_name(name) { }
+Component::Component(const QString &name) : CommentableDomElement(Path()), m_name(name) { }
-Component::Component(Path pathFromOwner) : CommentableDomElement(pathFromOwner) { }
+Component::Component(const Path &pathFromOwner) : CommentableDomElement(pathFromOwner) { }
-bool Component::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool Component::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::name, name());
@@ -117,20 +119,13 @@ bool Component::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-DomItem Component::field(DomItem &self, QStringView name)
+DomItem Component::field(const DomItem &self, QStringView name) const
{
- switch (name.size()) {
- case 4:
- if (name == Fields::name)
- return self.wrapField(Fields::name, m_name);
- break;
- case 7:
- if (name == Fields::objects)
- return self.wrapField(Fields::objects, m_objects);
- break;
- default:
- break;
- }
+ if (name == Fields::name)
+ return self.wrapField(Fields::name, m_name);
+ if (name == Fields::objects)
+ return self.wrapField(Fields::objects, m_objects);
+
return DomBase::field(self, name);
}
@@ -140,45 +135,50 @@ Path Component::addObject(const QmlObject &object, QmlObject **oPtr)
oPtr);
}
-bool QmlComponent::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool QmlComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = Component::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvWrapField(visitor, Fields::ids, m_ids);
cont = cont && self.dvValueLazyField(visitor, Fields::subComponents, [this, &self]() {
return this->subComponents(self);
});
+ if (m_nameIdentifiers) {
+ cont = cont && self.dvItemField(visitor, Fields::nameIdentifiers, [this, &self]() {
+ return self.subScriptElementWrapperItem(m_nameIdentifiers);
+ });
+ }
return cont;
}
-void QmlComponent::updatePathFromOwner(Path newPath)
+void QmlComponent::updatePathFromOwner(const Path &newPath)
{
Component::updatePathFromOwner(newPath);
updatePathFromOwnerMultiMap(m_ids, newPath.field(Fields::annotations));
}
-void QmlComponent::writeOut(DomItem &self, OutWriter &lw) const
+void QmlComponent::writeOut(const DomItem &self, OutWriter &lw) const
{
if (name().contains(QLatin1Char('.'))) {
// inline component
lw.ensureNewline()
- .writeRegion(u"component")
+ .writeRegion(ComponentKeywordRegion)
.space()
- .writeRegion(u"componentName", name().split(QLatin1Char('.')).last())
- .writeRegion(u"colon", u":")
+ .writeRegion(IdentifierRegion, name().split(QLatin1Char('.')).last())
+ .writeRegion(ColonTokenRegion)
.space();
}
self.field(Fields::objects).index(0).writeOut(lw);
}
-QList<QString> QmlComponent::subComponentsNames(DomItem &self) const
+QList<QString> QmlComponent::subComponentsNames(const DomItem &self) const
{
DomItem components = self.owner().field(Fields::components);
- QSet<QString> cNames = components.keys();
+ const QSet<QString> cNames = components.keys();
QString myNameDot = self.pathFromOwner()[1].headName();
if (!myNameDot.isEmpty())
myNameDot += QLatin1Char('.');
QList<QString> subNames;
- for (QString cName : cNames)
+ for (const QString &cName : cNames)
if (cName.startsWith(myNameDot)
&& !QStringView(cName).mid(myNameDot.size()).contains(QLatin1Char('.'))
&& !cName.isEmpty())
@@ -187,12 +187,12 @@ QList<QString> QmlComponent::subComponentsNames(DomItem &self) const
return subNames;
}
-QList<DomItem> QmlComponent::subComponents(DomItem &self) const
+QList<DomItem> QmlComponent::subComponents(const DomItem &self) const
{
DomItem components = self.owner().field(Fields::components);
QList<DomItem> res;
- for (QString cName : subComponentsNames(self))
- for (DomItem comp : components.key(cName).values())
+ for (const QString &cName : subComponentsNames(self))
+ for (const DomItem &comp : components.key(cName).values())
res.append(comp);
return res;
}
@@ -244,7 +244,7 @@ QString Version::stringValue() const
return QString::number(majorVersion) + QChar::fromLatin1('.') + QString::number(minorVersion);
}
-bool Version::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool Version::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvWrapField(visitor, Fields::majorVersion, majorVersion);
@@ -264,7 +264,8 @@ QRegularExpression Import::importRe()
return res;
}
-Import Import::fromUriString(QString importStr, Version v, QString importId, ErrorHandler handler)
+Import Import::fromUriString(
+ const QString &importStr, Version v, const QString &importId, const ErrorHandler &handler)
{
auto m = importRe().match(importStr);
if (m.hasMatch()) {
@@ -276,15 +277,21 @@ Import Import::fromUriString(QString importStr, Version v, QString importId, Err
"version %3")
.arg(m.captured(2), importStr, v.stringValue()))
.handle(handler);
- if (importId.isEmpty())
- importId = m.captured(u"importId");
- else if (!m.captured(u"importId").isEmpty())
- domParsingErrors()
- .warning(tr("namespace %1 in import string '%2' overridden by explicit "
- "importId %3")
- .arg(m.captured(u"importId"), importStr, importId))
- .handle(handler);
- return Import(QmlUri::fromUriString(m.captured(u"uri").trimmed()), v, importId);
+ QString resolvedImportId;
+ if (importId.isEmpty()) {
+ resolvedImportId = m.captured(u"importId");
+ } else {
+ if (!m.captured(u"importId").isEmpty()) {
+ domParsingErrors()
+ .warning(tr("namespace %1 in import string '%2' overridden by explicit "
+ "importId %3")
+ .arg(m.captured(u"importId"), importStr, importId))
+ .handle(handler);
+ }
+ resolvedImportId = importId;
+ }
+
+ return Import(QmlUri::fromUriString(m.captured(u"uri").trimmed()), v, resolvedImportId);
}
domParsingErrors()
.error(tr("Unexpected URI format in import '%1'").arg(importStr))
@@ -292,12 +299,13 @@ Import Import::fromUriString(QString importStr, Version v, QString importId, Err
return Import();
}
-Import Import::fromFileString(QString importStr, QString importId, ErrorHandler)
+Import Import::fromFileString(
+ const QString &importStr, const QString &importId, const ErrorHandler &)
{
return Import(QmlUri::fromDirectoryString(importStr), Version(), importId);
}
-bool Import::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool Import::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::uri, uri.toString());
@@ -310,48 +318,74 @@ bool Import::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-void Import::writeOut(DomItem &, OutWriter &ow) const
+void Import::writeOut(const DomItem &self, OutWriter &ow) const
{
if (implicit)
return;
- ow.ensureNewline();
- ow.writeRegion(u"import").space();
- ow.writeRegion(u"uri", uri.toString());
+
+ QString code;
+ const DomItem owner = self.owner();
+ if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
+ code = qmlFilePtr->code();
+
+ // check for an empty line before the import, and preserve it
+ int preNewlines = 0;
+
+ const FileLocations::Tree elLoc = FileLocations::findAttachedInfo(self).foundTree;
+
+ quint32 start = elLoc->info().fullRegion.offset;
+ if (size_t(code.size()) >= start) {
+ while (start != 0) {
+ QChar c = code.at(--start);
+ if (c == u'\n') {
+ if (++preNewlines == 2)
+ break;
+ } else if (!c.isSpace())
+ break;
+ }
+ }
+ if (preNewlines == 0)
+ ++preNewlines;
+
+ ow.ensureNewline(preNewlines);
+ ow.writeRegion(ImportTokenRegion).space();
+ ow.writeRegion(ImportUriRegion, uri.toString());
if (uri.isModule()) {
QString vString = version.stringValue();
if (!vString.isEmpty())
ow.space().write(vString);
}
if (!importId.isEmpty())
- ow.space().writeRegion(u"as").space().writeRegion(u"id", importId);
+ ow.space().writeRegion(AsTokenRegion).space().writeRegion(IdNameRegion, importId);
}
-Id::Id(QString idName, Path referredObject) : name(idName), referredObjectPath(referredObject) { }
+Id::Id(const QString &idName, const Path &referredObject) : name(idName), referredObjectPath(referredObject) { }
-bool Id::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool Id::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::name, name);
cont = cont && self.dvReferenceField(visitor, Fields::referredObject, referredObjectPath);
cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
+ cont = cont && self.dvWrapField(visitor, Fields::value, value);
return cont;
}
-void Id::updatePathFromOwner(Path newPath)
+void Id::updatePathFromOwner(const Path &newPath)
{
updatePathFromOwnerQList(annotations, newPath.field(Fields::annotations));
}
-Path Id::addAnnotation(Path selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
+Path Id::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
{
return appendUpdatableElementInQList(selfPathFromOwner.field(Fields::annotations), annotations,
annotation, aPtr);
}
-QmlObject::QmlObject(Path pathFromOwner) : CommentableDomElement(pathFromOwner) { }
+QmlObject::QmlObject(const Path &pathFromOwner) : CommentableDomElement(pathFromOwner) { }
-bool QmlObject::iterateBaseDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool QmlObject::iterateBaseDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
if (!idStr().isEmpty())
@@ -369,13 +403,18 @@ bool QmlObject::iterateBaseDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::propertyInfos, [this, &self]() {
return self.subMapItem(Map(
pathFromOwner().field(Fields::propertyInfos),
- [&self](DomItem &map, QString k) {
+ [&self](const DomItem &map, const QString &k) {
auto pInfo = self.propertyInfoWithName(k);
return map.wrap(PathEls::Key(k), pInfo);
},
- [&self](DomItem &) { return self.propertyInfoNames(); },
+ [&self](const DomItem &) { return self.propertyInfoNames(); },
QLatin1String("PropertyInfo")));
});
+ if (m_nameIdentifiers) {
+ cont = cont && self.dvItemField(visitor, Fields::nameIdentifiers, [this, &self]() {
+ return self.subScriptElementWrapperItem(m_nameIdentifiers);
+ });
+ }
return cont;
}
@@ -391,7 +430,7 @@ QList<QString> QmlObject::fields() const
return myFields;
}
-bool QmlObject::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool QmlObject::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = iterateBaseDirectSubpaths(self, visitor);
cont = cont && self.dvValueLazyField(visitor, Fields::defaultPropertyName, [this, &self]() {
@@ -400,91 +439,65 @@ bool QmlObject::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-DomItem QmlObject::field(DomItem &self, QStringView name)
+DomItem QmlObject::field(const DomItem &self, QStringView name) const
{
- switch (name.size()) {
- case 4:
- if (name == Fields::name)
- return self.subDataItem(PathEls::Field(Fields::name), this->name());
- break;
- case 5:
- if (name == Fields::idStr) {
- if (idStr().isEmpty())
- return DomItem();
- return self.subDataItem(PathEls::Field(Fields::idStr), idStr());
- }
- break;
- case 7:
- if (name == Fields::methods)
- return self.wrapField(Fields::methods, m_methods);
- break;
- case 8:
- switch (name.at(1).unicode()) {
- case u'i':
- if (name == Fields::bindings)
- return self.wrapField(Fields::bindings, m_bindings);
- break;
- case u'o':
- if (name == Fields::comments)
- return CommentableDomElement::field(self, name);
- break;
- case u'h':
- if (name == Fields::children)
- return self.wrapField(Fields::children, m_children);
- break;
- default:
- break;
- }
- break;
- case 9:
- if (name == Fields::nextScope) {
- if (nextScopePath())
- return self.subReferenceItem(PathEls::Field(Fields::nextScope), nextScopePath());
- else
- return DomItem();
- }
- break;
- case 10:
- if (name == Fields::prototypes) {
- if (prototypePaths().isEmpty())
- return DomItem();
- return self.subReferencesItem(PathEls::Field(Fields::prototypes), m_prototypePaths);
- }
- break;
- case 11:
- if (name == Fields::annotations)
- return self.wrapField(Fields::annotations, m_annotations);
- break;
- case 12:
+ if (name == Fields::name)
+ return self.subDataItem(PathEls::Field(Fields::name), this->name());
+ if (name == Fields::idStr) {
+ if (idStr().isEmpty())
+ return DomItem();
+ return self.subDataItem(PathEls::Field(Fields::idStr), idStr());
+ }
+ if (name == Fields::methods)
+ return self.wrapField(Fields::methods, m_methods);
+ if (name == Fields::bindings)
+ return self.wrapField(Fields::bindings, m_bindings);
+ if (name == Fields::comments)
+ return CommentableDomElement::field(self, name);
+ if (name == Fields::children)
+ return self.wrapField(Fields::children, m_children);
+
+ if (name == Fields::nextScope) {
+ if (nextScopePath())
+ return self.subReferenceItem(PathEls::Field(Fields::nextScope), nextScopePath());
+ else
+ return DomItem();
+ }
+ if (name == Fields::prototypes) {
+ if (prototypePaths().isEmpty())
+ return DomItem();
+ return self.subReferencesItem(PathEls::Field(Fields::prototypes), m_prototypePaths);
+ }
+ if (name == Fields::annotations)
+ return self.wrapField(Fields::annotations, m_annotations);
+ if (name == Fields::propertyDefs)
return self.wrapField(Fields::propertyDefs, m_propertyDefs);
- break;
- case 13:
- if (name == Fields::propertyInfos)
- return self.subMapItem(Map(
- pathFromOwner().field(Fields::propertyInfos),
- [self](DomItem &map, QString k) mutable {
- auto pInfo = self.propertyInfoWithName(k);
- return map.wrap(PathEls::Key(k), pInfo);
- },
- [self](DomItem &) mutable { return self.propertyInfoNames(); },
- QLatin1String("PropertyInfo")));
- break;
- case 19:
- if (name == Fields::defaultPropertyName)
- return self.subDataItem(PathEls::Field(Fields::defaultPropertyName),
- defaultPropertyName(self));
- break;
- default:
- break;
+ if (name == Fields::propertyInfos) {
+ // Need to explicitly copy self here since we might store this and call it later.
+ return self.subMapItem(Map(
+ pathFromOwner().field(Fields::propertyInfos),
+ [copiedSelf = self](const DomItem &map, const QString &k) {
+ return map.wrap(PathEls::Key(k), copiedSelf.propertyInfoWithName(k));
+ },
+ [copiedSelf = self](const DomItem &) { return copiedSelf.propertyInfoNames(); },
+ QLatin1String("PropertyInfo")));
+ }
+ if (name == Fields::nameIdentifiers && m_nameIdentifiers) {
+ return self.subScriptElementWrapperItem(m_nameIdentifiers);
+ }
+ if (name == Fields::defaultPropertyName) {
+ return self.subDataItem(PathEls::Field(Fields::defaultPropertyName),
+ defaultPropertyName(self));
}
static QStringList knownLookups({ QString::fromUtf16(Fields::fileLocationsTree) });
- if (!knownLookups.contains(name))
+ if (!knownLookups.contains(name)) {
qCWarning(domLog()) << "Asked non existing field " << name << " in QmlObject "
<< pathFromOwner();
+ }
return DomItem();
}
-void QmlObject::updatePathFromOwner(Path newPath)
+void QmlObject::updatePathFromOwner(const Path &newPath)
{
DomElement::updatePathFromOwner(newPath);
updatePathFromOwnerMultiMap(m_propertyDefs, newPath.field(Fields::propertyDefs));
@@ -504,14 +517,14 @@ QString QmlObject::localDefaultPropertyName() const
return QString();
}
-QString QmlObject::defaultPropertyName(DomItem &self) const
+QString QmlObject::defaultPropertyName(const DomItem &self) const
{
QString dProp = localDefaultPropertyName();
if (!dProp.isEmpty())
return dProp;
QString res = QStringLiteral(u"data");
self.visitPrototypeChain(
- [&res](DomItem &obj) {
+ [&res](const DomItem &obj) {
if (const QmlObject *objPtr = obj.as<QmlObject>()) {
QString dProp = objPtr->localDefaultPropertyName();
if (!dProp.isEmpty()) {
@@ -525,10 +538,10 @@ QString QmlObject::defaultPropertyName(DomItem &self) const
return res;
}
-bool QmlObject::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &)> visitor) const
+bool QmlObject::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &)> visitor) const
{
- bool cont = self.field(Fields::bindings).visitKeys([visitor](QString, DomItem &bs) {
- return bs.visitIndexes([visitor](DomItem &b) {
+ bool cont = self.field(Fields::bindings).visitKeys([visitor](const QString &, const DomItem &bs) {
+ return bs.visitIndexes([visitor](const DomItem &b) {
DomItem v = b.field(Fields::value);
if (std::shared_ptr<ScriptExpression> vPtr = v.ownerAs<ScriptExpression>()) {
if (!visitor(v))
@@ -538,7 +551,7 @@ bool QmlObject::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &)> vi
return true;
});
});
- cont = cont && self.field(Fields::children).visitIndexes([visitor](DomItem &qmlObj) {
+ cont = cont && self.field(Fields::children).visitIndexes([visitor](const DomItem &qmlObj) {
if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>()) {
return qmlObjPtr->iterateSubOwners(qmlObj, visitor);
}
@@ -548,7 +561,7 @@ bool QmlObject::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &)> vi
return cont;
}
-static QStringList dotExpressionToList(std::shared_ptr<ScriptExpression> expr)
+static QStringList dotExpressionToList(const std::shared_ptr<ScriptExpression> &expr)
{
QStringList res;
AST::Node *node = (expr ? expr->ast() : nullptr);
@@ -574,14 +587,14 @@ static QStringList dotExpressionToList(std::shared_ptr<ScriptExpression> expr)
return res;
}
-LocallyResolvedAlias QmlObject::resolveAlias(DomItem &self,
+LocallyResolvedAlias QmlObject::resolveAlias(const DomItem &self,
std::shared_ptr<ScriptExpression> accessSequence) const
{
QStringList accessSequenceList = dotExpressionToList(accessSequence);
return resolveAlias(self, accessSequenceList);
}
-LocallyResolvedAlias QmlObject::resolveAlias(DomItem &self, const QStringList &accessSequence) const
+LocallyResolvedAlias QmlObject::resolveAlias(const DomItem &self, const QStringList &accessSequence) const
{
LocallyResolvedAlias res;
QSet<QString> visitedAlias;
@@ -669,8 +682,8 @@ LocallyResolvedAlias QmlObject::resolveAlias(DomItem &self, const QStringList &a
return res;
}
-MutableDomItem QmlObject::addPropertyDef(MutableDomItem &self, PropertyDefinition propertyDef,
- AddOption option)
+MutableDomItem QmlObject::addPropertyDef(
+ MutableDomItem &self, const PropertyDefinition &propertyDef, AddOption option)
{
Path p = addPropertyDef(propertyDef, option);
if (p.last().headIndex(0) > 1)
@@ -688,7 +701,8 @@ MutableDomItem QmlObject::addBinding(MutableDomItem &self, Binding binding, AddO
return self.owner().path(p);
}
-MutableDomItem QmlObject::addMethod(MutableDomItem &self, MethodInfo functionDef, AddOption option)
+MutableDomItem QmlObject::addMethod(
+ MutableDomItem &self, const MethodInfo &functionDef, AddOption option)
{
Path p = addMethod(functionDef, option);
if (p.last().headIndex(0) > 1)
@@ -697,7 +711,7 @@ MutableDomItem QmlObject::addMethod(MutableDomItem &self, MethodInfo functionDef
return self.owner().path(p);
}
-void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
+void QmlObject::writeOut(const DomItem &self, OutWriter &ow, const QString &onTarget) const
{
const quint32 posOfNewElements = std::numeric_limits<quint32>::max();
bool isRootObject = pathFromOwner().length() == 5
@@ -707,10 +721,10 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
DomItem owner = self.owner();
if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
code = qmlFilePtr->code();
- ow.writeRegion(u"name", name());
+ ow.writeRegion(IdentifierRegion, name());
if (!onTarget.isEmpty())
- ow.space().writeRegion(u"on", u"on").space().writeRegion(u"onTarget", onTarget).space();
- ow.writeRegion(u"leftBrace", u" {").newline();
+ ow.space().writeRegion(OnTokenRegion).space().writeRegion(OnTargetRegion, onTarget);
+ ow.writeRegion(LeftBraceRegion, u" {");
int baseIndent = ow.increaseIndent();
int spacerId = 0;
if (!idStr().isEmpty()) { // *always* put id first
@@ -718,22 +732,24 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
if (myId)
myId.writeOutPre(ow);
ow.ensureNewline()
- .writeRegion(u"idToken", u"id")
- .writeRegion(u"idColon", u":")
+ .writeRegion(IdTokenRegion)
+ .writeRegion(IdColonTokenRegion)
.space()
- .writeRegion(u"id", idStr());
+ .writeRegion(IdNameRegion, idStr());
if (ow.lineWriter.options().attributesSequence
== LineWriterOptions::AttributesSequence::Normalize) {
ow.ensureNewline(2);
}
- if (myId)
+ if (myId) {
myId.writeOutPost(ow);
+ ow.ensureNewline(1);
+ }
}
quint32 counter = ow.counter();
DomItem component;
if (isRootObject)
component = self.containingObject();
- auto startLoc = [&](FileLocations::Tree l) {
+ auto startLoc = [&](const FileLocations::Tree &l) {
if (l)
return l->info().fullRegion;
return SourceLocation(posOfNewElements, 0, 0, 0);
@@ -746,13 +762,16 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
FileLocations::Tree componentLoc;
if (isRootObject && objLoc.foundTree)
componentLoc = objLoc.foundTree->parent()->parent();
- auto addMMap = [&attribs, &startLoc](DomItem &base, FileLocations::Tree baseLoc) {
+ auto addMMap
+ = [&attribs, &startLoc](const DomItem &base, const FileLocations::Tree &baseLoc) {
if (!base)
return;
- for (auto els : base.values()) {
+ const auto values = base.values();
+ for (const auto &els : values) {
FileLocations::Tree elsLoc =
FileLocations::find(baseLoc, els.pathFromOwner().last());
- for (auto el : els.values()) {
+ const auto elsValues = els.values();
+ for (const auto &el : elsValues) {
FileLocations::Tree elLoc =
FileLocations::find(elsLoc, el.pathFromOwner().last());
attribs.append(std::make_pair(startLoc(elLoc), el));
@@ -763,10 +782,12 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
DomItem base = this->field(self, fieldName);
addMMap(base, FileLocations::find(objLoc.foundTree, base.pathFromOwner().last()));
};
- auto addSingleLevel = [&attribs, &startLoc](DomItem &base, FileLocations::Tree baseLoc) {
+ auto addSingleLevel
+ = [&attribs, &startLoc](const DomItem &base, const FileLocations::Tree &baseLoc) {
if (!base)
return;
- for (auto el : base.values()) {
+ const auto baseValues = base.values();
+ for (const auto &el : baseValues) {
FileLocations::Tree elLoc = FileLocations::find(baseLoc, el.pathFromOwner().last());
attribs.append(std::make_pair(startLoc(elLoc), el));
}
@@ -783,7 +804,7 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
FileLocations::find(objLoc.foundTree, children.pathFromOwner().last()));
if (isRootObject) {
DomItem subCs = component.field(Fields::subComponents);
- for (DomItem &c : subCs.values()) {
+ for (const DomItem &c : subCs.values()) {
AttachedInfoLookupResult<FileLocations::Tree> subLoc =
FileLocations::findAttachedInfo(c);
Q_ASSERT(subLoc.foundTree);
@@ -839,16 +860,17 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
else {
qWarning() << "Internal error casting binding to Binding in"
<< b.canonicalPath();
- ow.writeRegion(u"leftBrace", u"{").writeRegion(u"rightBrace", u"}");
+ ow.writeRegion(LeftBraceRegion).writeRegion(RightBraceRegion);
}
b.writeOutPost(ow);
}
} else {
el.second.writeOut(ow);
}
+ ow.ensureNewline();
}
ow.decreaseIndent(1, baseIndent);
- ow.ensureNewline().write(u"}");
+ ow.writeRegion(RightBraceRegion);
return;
}
@@ -856,8 +878,10 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
DomItem propertyDefs = field(self, Fields::propertyDefs);
if (isRootObject) {
- for (auto enumDescs : component.field(Fields::enumerations).values()) {
- for (auto enumDesc : enumDescs.values()) {
+ const auto descs = component.field(Fields::enumerations).values();
+ for (const auto &enumDescs : descs) {
+ const auto values = enumDescs.values();
+ for (const auto &enumDesc : values) {
ow.ensureNewline(1);
enumDesc.writeOut(ow);
ow.ensureNewline(1);
@@ -868,14 +892,14 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
spacerId = ow.addNewlinesAutospacerCallback(2);
QSet<QString> mergedDefBinding;
for (const QString &defName : propertyDefs.sortedKeys()) {
- auto pDefs = propertyDefs.key(defName).values();
- for (auto pDef : pDefs) {
+ const auto pDefs = propertyDefs.key(defName).values();
+ for (const auto &pDef : pDefs) {
const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>();
Q_ASSERT(pDefPtr);
DomItem b;
bool uniqueDeclarationWithThisName = pDefs.size() == 1;
if (uniqueDeclarationWithThisName && !pDefPtr->isRequired)
- bindings.key(pDef.name()).visitIndexes([&b, pDefPtr](DomItem &el) {
+ bindings.key(pDef.name()).visitIndexes([&b, pDefPtr](const DomItem &el) {
const Binding *elPtr = el.as<Binding>();
if (elPtr && elPtr->bindingType() == BindingType::Normal) {
switch (elPtr->valueKind()) {
@@ -911,7 +935,7 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
else {
qWarning() << "Internal error casting binding to Binding in"
<< b.canonicalPath();
- ow.writeRegion(u"leftBrace", u"{").writeRegion(u"rightBrace", u"}");
+ ow.writeRegion(LeftBraceRegion).writeRegion(RightBraceRegion);
}
b.writeOutPost(ow);
}
@@ -919,8 +943,10 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
}
ow.removeTextAddCallback(spacerId);
QList<DomItem> signalList, methodList;
- for (auto ms : field(self, Fields::methods).values()) {
- for (auto m : ms.values()) {
+ const auto fields = field(self, Fields::methods).values();
+ for (const auto &ms : fields) {
+ const auto values = ms.values();
+ for (const auto &m : values) {
const MethodInfo *mPtr = m.as<MethodInfo>();
if (mPtr && mPtr->methodType == MethodInfo::MethodType::Signal)
signalList.append(m);
@@ -930,7 +956,7 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
}
if (counter != ow.counter())
spacerId = ow.addNewlinesAutospacerCallback(2);
- for (auto &sig : signalList) {
+ for (const auto &sig : std::as_const(signalList)) {
ow.ensureNewline();
sig.writeOut(ow);
ow.ensureNewline();
@@ -939,7 +965,7 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
if (counter != ow.counter())
spacerId = ow.addNewlinesAutospacerCallback(2);
bool first = true;
- for (auto &method : methodList) {
+ for (const auto &method : std::as_const(methodList)) {
if (!first && ow.lineWriter.options().functionsSpacing) {
ow.newline();
}
@@ -950,9 +976,10 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
}
ow.removeTextAddCallback(spacerId);
QList<DomItem> normalBindings, signalHandlers, delayedBindings;
- for (auto bName : bindings.sortedKeys()) {
+ for (const auto &bName : bindings.sortedKeys()) {
bool skipFirstNormal = mergedDefBinding.contains(bName);
- for (auto b : bindings.key(bName).values()) {
+ const auto values = bindings.key(bName).values();
+ for (const auto &b : values) {
const Binding *bPtr = b.as<Binding>();
if (skipFirstNormal) {
if (bPtr && bPtr->bindingType() == BindingType::Normal) {
@@ -971,23 +998,24 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
}
if (counter != ow.counter())
spacerId = ow.addNewlinesAutospacerCallback(2);
- for (auto &b : normalBindings)
+ for (const auto &b : std::as_const(normalBindings))
b.writeOut(ow);
ow.removeTextAddCallback(spacerId);
if (counter != ow.counter())
spacerId = ow.addNewlinesAutospacerCallback(2);
- for (auto &b : delayedBindings)
+ for (const auto &b : std::as_const(delayedBindings))
b.writeOut(ow);
ow.removeTextAddCallback(spacerId);
if (counter != ow.counter())
spacerId = ow.addNewlinesAutospacerCallback(2);
- for (auto &b : signalHandlers)
+ for (const auto &b : std::as_const(signalHandlers))
b.writeOut(ow);
ow.removeTextAddCallback(spacerId);
if (counter != ow.counter())
spacerId = ow.addNewlinesAutospacerCallback(2);
first = true;
- for (auto c : field(self, Fields::children).values()) {
+ const auto values = field(self, Fields::children).values();
+ for (const auto &c : values) {
if (!first && ow.lineWriter.options().objectsSpacing) {
ow.newline().newline();
}
@@ -1001,27 +1029,30 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const
DomItem subComps = component.field(Fields::subComponents);
if (counter != ow.counter())
spacerId = ow.addNewlinesAutospacerCallback(2);
- for (auto subC : subComps.values()) {
+ const auto values = subComps.values();
+ for (const auto &subC : values) {
ow.ensureNewline();
subC.writeOut(ow);
}
ow.removeTextAddCallback(spacerId);
}
ow.decreaseIndent(1, baseIndent);
- ow.ensureNewline().write(u"}");
+ ow.ensureNewline().writeRegion(RightBraceRegion);
}
-Binding::Binding(QString name, std::unique_ptr<BindingValue> value, BindingType bindingType)
+Binding::Binding(const QString &name, std::unique_ptr<BindingValue> value, BindingType bindingType)
: m_bindingType(bindingType), m_name(name), m_value(std::move(value))
{
}
-Binding::Binding(QString name, std::shared_ptr<ScriptExpression> value, BindingType bindingType)
+Binding::Binding(
+ const QString &name, const std::shared_ptr<ScriptExpression> &value,
+ BindingType bindingType)
: Binding(name, std::make_unique<BindingValue>(value), bindingType)
{
}
-Binding::Binding(QString name, QString scriptCode, BindingType bindingType)
+Binding::Binding(const QString &name, const QString &scriptCode, BindingType bindingType)
: Binding(name,
std::make_unique<BindingValue>(std::make_shared<ScriptExpression>(
scriptCode, ScriptExpression::ExpressionType::BindingExpression, 0,
@@ -1030,12 +1061,12 @@ Binding::Binding(QString name, QString scriptCode, BindingType bindingType)
{
}
-Binding::Binding(QString name, QmlObject value, BindingType bindingType)
+Binding::Binding(const QString &name, const QmlObject &value, BindingType bindingType)
: Binding(name, std::make_unique<BindingValue>(value), bindingType)
{
}
-Binding::Binding(QString name, QList<QmlObject> value, BindingType bindingType)
+Binding::Binding(const QString &name, const QList<QmlObject> &value, BindingType bindingType)
: Binding(name, std::make_unique<BindingValue>(value), bindingType)
{
}
@@ -1044,7 +1075,8 @@ Binding::Binding(const Binding &o)
: m_bindingType(o.m_bindingType),
m_name(o.m_name),
m_annotations(o.m_annotations),
- m_comments(o.m_comments)
+ m_comments(o.m_comments),
+ m_bindingIdentifiers(o.m_bindingIdentifiers)
{
if (o.m_value) {
m_value = std::make_unique<BindingValue>(*o.m_value);
@@ -1059,6 +1091,7 @@ Binding &Binding::operator=(const Binding &o)
m_bindingType = o.m_bindingType;
m_annotations = o.m_annotations;
m_comments = o.m_comments;
+ m_bindingIdentifiers = o.m_bindingIdentifiers;
if (o.m_value) {
if (!m_value)
m_value = std::make_unique<BindingValue>(*o.m_value);
@@ -1070,7 +1103,7 @@ Binding &Binding::operator=(const Binding &o)
return *this;
}
-bool Binding::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool Binding::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::name, m_name);
@@ -1089,11 +1122,16 @@ bool Binding::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvValueLazyField(visitor, Fields::postCode, [this]() {
return this->postCode();
});
+ if (m_bindingIdentifiers) {
+ cont = cont && self.dvItemField(visitor, Fields::bindingIdentifiers, [this, &self]() {
+ return self.subScriptElementWrapperItem(m_bindingIdentifiers);
+ });
+ }
cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
return cont;
}
-DomItem Binding::valueItem(DomItem &self) const
+DomItem Binding::valueItem(const DomItem &self) const
{
if (!m_value)
return DomItem();
@@ -1149,13 +1187,13 @@ std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue()
return nullptr;
}
-Path Binding::addAnnotation(Path selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
+Path Binding::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
{
return appendUpdatableElementInQList(selfPathFromOwner.field(Fields::annotations),
m_annotations, annotation, aPtr);
}
-void Binding::updatePathFromOwner(Path newPath)
+void Binding::updatePathFromOwner(const Path &newPath)
{
Path base = newPath.field(Fields::annotations);
if (m_value)
@@ -1163,12 +1201,12 @@ void Binding::updatePathFromOwner(Path newPath)
updatePathFromOwnerQList(m_annotations, newPath.field(Fields::annotations));
}
-void Binding::writeOut(DomItem &self, OutWriter &lw) const
+void Binding::writeOut(const DomItem &self, OutWriter &lw) const
{
lw.ensureNewline();
if (m_bindingType == BindingType::Normal) {
- lw.writeRegion(u"name", name());
- lw.writeRegion(u"colon", u":").space();
+ lw.writeRegion(IdentifierRegion, name());
+ lw.writeRegion(ColonTokenRegion).space();
writeOutValue(self, lw);
} else {
DomItem v = valueItem(self);
@@ -1183,7 +1221,7 @@ void Binding::writeOut(DomItem &self, OutWriter &lw) const
}
}
-void Binding::writeOutValue(DomItem &self, OutWriter &lw) const
+void Binding::writeOutValue(const DomItem &self, OutWriter &lw) const
{
DomItem v = valueItem(self);
switch (valueKind()) {
@@ -1205,7 +1243,7 @@ void Binding::writeOutValue(DomItem &self, OutWriter &lw) const
}
}
-bool QmltypesComponent::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool QmltypesComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = Component::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
@@ -1220,7 +1258,8 @@ bool QmltypesComponent::iterateDirectSubpaths(DomItem &self, DirectVisitor visit
return cont;
}
-Export Export::fromString(Path source, QStringView exp, Path typePath, ErrorHandler h)
+Export Export::fromString(
+ const Path &source, QStringView exp, const Path &typePath, const ErrorHandler &h)
{
Export res;
res.exportSourcePath = source;
@@ -1237,14 +1276,13 @@ Export Export::fromString(Path source, QStringView exp, Path typePath, ErrorHand
"or 'Name major.minor' not '%1'.")
.arg(exp))
.handle(h);
- QString package;
if (slashIdx != -1)
res.uri = exp.left(slashIdx).toString();
res.typeName = exp.mid(slashIdx + 1, spaceIdx - (slashIdx + 1)).toString();
return res;
}
-bool AttributeInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool AttributeInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::name, name);
@@ -1257,20 +1295,20 @@ bool AttributeInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-Path AttributeInfo::addAnnotation(Path selfPathFromOwner, const QmlObject &annotation,
+Path AttributeInfo::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation,
QmlObject **aPtr)
{
return appendUpdatableElementInQList(selfPathFromOwner.field(Fields::annotations), annotations,
annotation, aPtr);
}
-void AttributeInfo::updatePathFromOwner(Path newPath)
+void AttributeInfo::updatePathFromOwner(const Path &newPath)
{
Path base = newPath.field(Fields::annotations);
updatePathFromOwnerQList(annotations, newPath.field(Fields::annotations));
}
-bool EnumDecl::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool EnumDecl::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::name, name());
@@ -1279,13 +1317,13 @@ bool EnumDecl::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-void EnumDecl::updatePathFromOwner(Path newPath)
+void EnumDecl::updatePathFromOwner(const Path &newPath)
{
DomElement::updatePathFromOwner(newPath);
updatePathFromOwnerQList(m_annotations, newPath.field(Fields::annotations));
}
-void EnumDecl::setAnnotations(QList<QmlObject> annotations)
+void EnumDecl::setAnnotations(const QList<QmlObject> &annotations)
{
m_annotations = annotations;
}
@@ -1296,23 +1334,24 @@ Path EnumDecl::addAnnotation(const QmlObject &annotation, QmlObject **aPtr)
annotation, aPtr);
}
-void EnumDecl::writeOut(DomItem &self, OutWriter &ow) const
+void EnumDecl::writeOut(const DomItem &self, OutWriter &ow) const
{
- ow.writeRegion(u"enum", u"enum")
+ ow.writeRegion(EnumKeywordRegion)
.space()
- .writeRegion(u"name", name())
+ .writeRegion(IdentifierRegion, name())
.space()
- .writeRegion(u"lbrace", u"{");
+ .writeRegion(LeftBraceRegion);
int iLevel = ow.increaseIndent(1);
- for (auto value : self.field(Fields::values).values()) {
+ const auto values = self.field(Fields::values).values();
+ for (const auto &value : values) {
ow.ensureNewline();
value.writeOut(ow);
}
ow.decreaseIndent(1, iLevel);
- ow.ensureNewline().writeRegion(u"rbrace", u"}");
+ ow.ensureNewline().writeRegion(RightBraceRegion);
}
-QList<Path> ImportScope::allSources(DomItem &self) const
+QList<Path> ImportScope::allSources(const DomItem &self) const
{
DomItem top = self.top();
DomItem env = top.environment();
@@ -1330,7 +1369,7 @@ QList<Path> ImportScope::allSources(DomItem &self) const
knownPaths.insert(pNow);
res.append(pNow);
DomItem sourceBase = top.path(pNow);
- for (DomItem autoExp : sourceBase.field(Fields::autoExports).values()) {
+ for (const DomItem &autoExp : sourceBase.field(Fields::autoExports).values()) {
if (const ModuleAutoExport *autoExpPtr = autoExp.as<ModuleAutoExport>()) {
Path newSource;
if (autoExpPtr->inheritVersion) {
@@ -1364,14 +1403,14 @@ QList<Path> ImportScope::allSources(DomItem &self) const
return res;
}
-bool ImportScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ImportScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvReferencesField(visitor, Fields::importSources, m_importSourcePaths);
cont = cont && self.dvItemField(visitor, Fields::allSources, [this, &self]() -> DomItem {
return self.subListItem(List::fromQList<Path>(
self.pathFromOwner().field(Fields::allSources), allSources(self),
- [](DomItem &list, const PathEls::PathComponent &p, const Path &el) {
+ [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
return list.subDataItem(p, el.toString());
}));
});
@@ -1379,20 +1418,20 @@ bool ImportScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::imported, [this, &self]() -> DomItem {
return self.subMapItem(Map(
self.pathFromOwner().field(Fields::imported),
- [this, &self](DomItem &map, QString key) {
+ [this, &self](const DomItem &map, const QString &key) {
return map.subListItem(List::fromQList<DomItem>(
map.pathFromOwner().key(key), importedItemsWithName(self, key),
- [](DomItem &, const PathEls::PathComponent &, DomItem &el) {
+ [](const DomItem &, const PathEls::PathComponent &, const DomItem &el) {
return el;
}));
},
- [this, &self](DomItem &) { return this->importedNames(self); },
+ [this, &self](const DomItem &) { return this->importedNames(self); },
QLatin1String("List<Export>")));
});
return cont;
}
-bool PropertyInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool PropertyInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::propertyDefs, propertyDefs);
@@ -1407,7 +1446,7 @@ BindingValue::BindingValue(const QmlObject &o) : kind(BindingValueKind::Object)
new (&object) QmlObject(o);
}
-BindingValue::BindingValue(std::shared_ptr<ScriptExpression> o)
+BindingValue::BindingValue(const std::shared_ptr<ScriptExpression> &o)
: kind(BindingValueKind::ScriptExpression)
{
new (&scriptExpression) std::shared_ptr<ScriptExpression>(o);
@@ -1458,7 +1497,7 @@ BindingValue &BindingValue::operator=(const BindingValue &o)
return *this;
}
-DomItem BindingValue::value(DomItem &binding)
+DomItem BindingValue::value(const DomItem &binding) const
{
switch (kind) {
case BindingValueKind::Empty:
@@ -1470,14 +1509,14 @@ DomItem BindingValue::value(DomItem &binding)
case BindingValueKind::Array:
return binding.subListItem(List::fromQListRef<QmlObject>(
binding.pathFromOwner().field(u"value"), array,
- [binding](DomItem &self, const PathEls::PathComponent &, QmlObject &obj) {
+ [](const DomItem &self, const PathEls::PathComponent &, const QmlObject &obj) {
return self.copy(&obj);
}));
}
return DomItem();
}
-void BindingValue::updatePathFromOwner(Path newPath)
+void BindingValue::updatePathFromOwner(const Path &newPath)
{
switch (kind) {
case BindingValueKind::Empty:
@@ -1511,10 +1550,10 @@ void BindingValue::clearValue()
kind = BindingValueKind::Empty;
}
-ScriptExpression::ScriptExpression(QStringView code, std::shared_ptr<QQmlJS::Engine> engine,
- AST::Node *ast, std::shared_ptr<AstComments> comments,
- ExpressionType expressionType, SourceLocation localOffset,
- int derivedFrom, QStringView preCode, QStringView postCode)
+ScriptExpression::ScriptExpression(
+ QStringView code, const std::shared_ptr<QQmlJS::Engine> &engine, AST::Node *ast,
+ const std::shared_ptr<AstComments> &comments, ExpressionType expressionType,
+ SourceLocation localOffset, int derivedFrom, QStringView preCode, QStringView postCode)
: OwningItem(derivedFrom),
m_expressionType(expressionType),
m_code(code),
@@ -1547,8 +1586,8 @@ ScriptExpression::ScriptExpression(const ScriptExpression &e) : OwningItem(e)
m_astComments = e.m_astComments;
}
-std::shared_ptr<ScriptExpression> ScriptExpression::copyWithUpdatedCode(DomItem &self,
- QString code) const
+std::shared_ptr<ScriptExpression> ScriptExpression::copyWithUpdatedCode(
+ const DomItem &self, const QString &code) const
{
std::shared_ptr<ScriptExpression> copy = makeCopy(self);
DomItem container = self.containingObject();
@@ -1558,7 +1597,7 @@ std::shared_ptr<ScriptExpression> ScriptExpression::copyWithUpdatedCode(DomItem
return copy;
}
-bool ScriptExpression::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ScriptExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::code, code());
@@ -1573,12 +1612,17 @@ bool ScriptExpression::iterateDirectSubpaths(DomItem &self, DirectVisitor visito
cont = cont
&& self.dvValueLazyField(
visitor, Fields::localOffset,
- [this]() { return locationToData(localOffset()); },
+ [this]() { return sourceLocationToQCborValue(localOffset()); },
ConstantData::Options::MapIsMap);
cont = cont && self.dvValueLazyField(visitor, Fields::astRelocatableDump, [this]() {
return astRelocatableDump();
});
cont = cont && self.dvValueField(visitor, Fields::expressionType, int(expressionType()));
+ if (m_element) {
+ cont = cont && self.dvItemField(visitor, Fields::scriptElement, [this, &self]() {
+ return self.subScriptElementWrapperItem(m_element);
+ });
+ }
return cont;
}
@@ -1613,19 +1657,24 @@ AST::Node *firstNodeInRange(AST::Node *n, quint32 minStart = 0, quint32 maxEnd =
return visitor.firstNodeInRange;
}
-void ScriptExpression::setCode(QString code, QString preCode, QString postCode)
+void ScriptExpression::setCode(const QString &code, const QString &preCode, const QString &postCode)
{
+ // TODO QTBUG-121933
m_codeStr = code;
- const bool qmlMode = (m_expressionType == ExpressionType::BindingExpression);
- if (qmlMode && preCode.isEmpty()) {
- preCode = Binding::preCodeForName(u"binding");
- postCode = Binding::postCodeForName(u"binding");
- }
- if (!preCode.isEmpty() || !postCode.isEmpty())
- m_codeStr = preCode + code + postCode;
- m_code = QStringView(m_codeStr).mid(preCode.size(), code.size());
- m_preCode = QStringView(m_codeStr).mid(0, preCode.size());
- m_postCode = QStringView(m_codeStr).mid(preCode.size() + code.size(), postCode.size());
+ QString resolvedPreCode, resolvedPostCode;
+ if (m_expressionType == ExpressionType::BindingExpression && preCode.isEmpty()) {
+ resolvedPreCode = Binding::preCodeForName(u"binding");
+ resolvedPostCode = Binding::postCodeForName(u"binding");
+ } else {
+ resolvedPreCode = preCode;
+ resolvedPostCode = postCode;
+ }
+ if (!resolvedPreCode.isEmpty() || !resolvedPostCode.isEmpty())
+ m_codeStr = resolvedPreCode + code + resolvedPostCode;
+ m_code = QStringView(m_codeStr).mid(resolvedPreCode.size(), code.size());
+ m_preCode = QStringView(m_codeStr).mid(0, resolvedPreCode.size());
+ m_postCode = QStringView(m_codeStr).mid(
+ resolvedPreCode.size() + code.size(), resolvedPostCode.size());
m_engine = nullptr;
m_ast = nullptr;
m_localOffset = SourceLocation();
@@ -1637,26 +1686,17 @@ void ScriptExpression::setCode(QString code, QString preCode, QString postCode)
m_localOffset.startLine = preChange.nNewlines;
m_engine = std::make_shared<QQmlJS::Engine>();
m_astComments = std::make_shared<AstComments>(m_engine);
- QQmlJS::Lexer lexer(m_engine.get());
- lexer.setCode(m_codeStr, /*lineno = */ 1, /*qmlMode=*/true);
- QQmlJS::Parser parser(m_engine.get());
- if ((qmlMode && !parser.parse()) || (!qmlMode && !parser.parseScript()))
- addErrorLocal(domParsingErrors().error(tr("Parsing of code failed")));
- for (DiagnosticMessage msg : parser.diagnosticMessages()) {
- ErrorMessage err = domParsingErrors().errorMessage(msg);
- err.location.offset -= m_localOffset.offset;
- err.location.startLine -= m_localOffset.startLine;
- if (err.location.startLine == 1)
- err.location.startColumn -= m_localOffset.startColumn;
- addErrorLocal(err);
- }
- m_ast = parser.rootNode();
+ m_ast = parse(resolveParseMode());
+
if (AST::Program *programPtr = AST::cast<AST::Program *>(m_ast)) {
m_ast = programPtr->statements;
}
if (!m_preCode.isEmpty())
m_ast = firstNodeInRange(m_ast, m_preCode.size(),
m_preCode.size() + m_code.size());
+ if (auto *sList = AST::cast<AST::FormalParameterList *>(m_ast)) {
+ m_ast = sList->element;
+ }
if (m_expressionType != ExpressionType::FunctionBody) {
if (AST::StatementList *sList = AST::cast<AST::StatementList *>(m_ast)) {
if (!sList->next)
@@ -1666,11 +1706,45 @@ void ScriptExpression::setCode(QString code, QString preCode, QString postCode)
if (m_expressionType == ExpressionType::BindingExpression)
if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(m_ast))
m_ast = exp->expression;
- AstComments::collectComments(m_engine, m_ast, m_astComments, MutableDomItem(), nullptr);
+
+ CommentCollector collector;
+ collector.collectComments(m_engine, m_ast, m_astComments);
+ }
+}
+
+AST::Node *ScriptExpression::parse(const ParseMode mode)
+{
+ QQmlJS::Lexer lexer(m_engine.get());
+ lexer.setCode(m_codeStr, /*lineno = */ 1, /*qmlMode=*/mode == ParseMode::QML);
+ QQmlJS::Parser parser(m_engine.get());
+ const bool parserSucceeded = [mode, &parser]() {
+ switch (mode) {
+ case ParseMode::QML:
+ return parser.parse();
+ case ParseMode::JS:
+ return parser.parseScript();
+ case ParseMode::ESM:
+ return parser.parseModule();
+ default:
+ Q_UNREACHABLE_RETURN(false);
+ }
+ }();
+ if (!parserSucceeded) {
+ addErrorLocal(domParsingErrors().error(tr("Parsing of code failed")));
+ }
+ const auto messages = parser.diagnosticMessages();
+ for (const DiagnosticMessage &msg : messages) {
+ ErrorMessage err = domParsingErrors().errorMessage(msg);
+ err.location.offset -= m_localOffset.offset;
+ err.location.startLine -= m_localOffset.startLine;
+ if (err.location.startLine == 1)
+ err.location.startColumn -= m_localOffset.startColumn;
+ addErrorLocal(std::move(err));
}
+ return parser.rootNode();
}
-void ScriptExpression::astDumper(Sink s, AstDumperOptions options) const
+void ScriptExpression::astDumper(const Sink &s, AstDumperOptions options) const
{
astNodeDumper(s, ast(), options, 1, 0, [this](SourceLocation astL) {
SourceLocation l = this->locationToLocal(astL);
@@ -1680,12 +1754,12 @@ void ScriptExpression::astDumper(Sink s, AstDumperOptions options) const
QString ScriptExpression::astRelocatableDump() const
{
- return dumperToString([this](Sink s) {
+ return dumperToString([this](const Sink &s) {
this->astDumper(s, AstDumperOption::NoLocations | AstDumperOption::SloppyCompare);
});
}
-void ScriptExpression::writeOut(DomItem &self, OutWriter &lw) const
+void ScriptExpression::writeOut(const DomItem &self, OutWriter &lw) const
{
OutWriter *ow = &lw;
@@ -1695,6 +1769,14 @@ void ScriptExpression::writeOut(DomItem &self, OutWriter &lw) const
QStringView reformattedCode =
QStringView(ow->writtenStr).mid(myLoc.offset, myLoc.length);
if (reformattedCode != code()) {
+ // If some reformatting of the expression took place,
+ // it will be saved as an intermediate step.
+ // then it will be used to restore writtenOut fileItem
+ // in the OutWriter::restoreWrittenFile
+
+ //Interestingly enough, this copyWithUpdatedCode will
+ //instantiate Engine and Parser and will parse "reformattedCode"
+ //because it calls ScriptExpression::setCode function
std::shared_ptr<ScriptExpression> copy =
copyWithUpdatedCode(self, reformattedCode.toString());
ow->addReformattedScriptExpression(self.canonicalPath(), copy);
@@ -1711,10 +1793,10 @@ void ScriptExpression::writeOut(DomItem &self, OutWriter &lw) const
lw.lineWriter.endSourceLocation(*codeLoc);
}
-SourceLocation ScriptExpression::globalLocation(DomItem &self) const
+SourceLocation ScriptExpression::globalLocation(const DomItem &self) const
{
- if (const FileLocations *fLocPtr = FileLocations::fileLocationsPtr(self)) {
- return fLocPtr->regions.value(QString(), fLocPtr->fullRegion);
+ if (const FileLocations::Tree tree = FileLocations::treeOf(self)) {
+ return FileLocations::region(tree, MainRegion);
}
return SourceLocation();
}
@@ -1724,23 +1806,23 @@ bool PropertyDefinition::isParametricType() const
return typeName.contains(QChar(u'<'));
}
-void PropertyDefinition::writeOut(DomItem &, OutWriter &lw) const
+void PropertyDefinition::writeOut(const DomItem &, OutWriter &lw) const
{
lw.ensureNewline();
if (isDefaultMember)
- lw.writeRegion(u"default").space();
+ lw.writeRegion(DefaultKeywordRegion).space();
if (isRequired)
- lw.writeRegion(u"required").space();
+ lw.writeRegion(RequiredKeywordRegion).space();
if (isReadonly)
- lw.writeRegion(u"readonly").space();
+ lw.writeRegion(ReadonlyKeywordRegion).space();
if (!typeName.isEmpty()) {
- lw.writeRegion(u"property").space();
- lw.writeRegion(u"type", typeName).space();
+ lw.writeRegion(PropertyKeywordRegion).space();
+ lw.writeRegion(TypeIdentifierRegion, typeName).space();
}
- lw.writeRegion(u"name", name);
+ lw.writeRegion(IdentifierRegion, name);
}
-bool MethodInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool MethodInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvWrapField(visitor, Fields::parameters, parameters);
@@ -1752,12 +1834,18 @@ bool MethodInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvValueField(visitor, Fields::postCode, postCode(self));
cont = cont && self.dvValueField(visitor, Fields::isConstructor, isConstructor);
}
+ if (returnType)
+ cont = cont && self.dvItemField(visitor, Fields::returnType, [this, &self]() {
+ return self.subOwnerItem(PathEls::Field(Fields::returnType), returnType);
+ });
if (body)
- cont = cont && self.dvWrapField(visitor, Fields::body, body);
+ cont = cont && self.dvItemField(visitor, Fields::body, [this, &self]() {
+ return self.subOwnerItem(PathEls::Field(Fields::body), body);
+ });
return cont;
}
-QString MethodInfo::preCode(DomItem &self) const
+QString MethodInfo::preCode(const DomItem &self) const
{
QString res;
LineWriter lw([&res](QStringView s) { res.append(s); }, QLatin1String("*preCode*"));
@@ -1767,41 +1855,41 @@ QString MethodInfo::preCode(DomItem &self) const
MockObject standinObj(self.pathFromOwner());
DomItem standin = self.copy(&standinObj);
ow.itemStart(standin);
- ow.writeRegion(u"function").space().writeRegion(u"name", name);
+ ow.writeRegion(FunctionKeywordRegion).space().writeRegion(IdentifierRegion, name);
bool first = true;
- ow.writeRegion(u"leftParen", u"(");
+ ow.writeRegion(LeftParenthesisRegion);
for (const MethodParameter &mp : parameters) {
if (first)
first = false;
else
ow.write(u", ");
- ow.write(mp.name);
+ ow.write(mp.value->code());
}
- ow.writeRegion(u"rightParen", u")");
- ow.ensureSpace().writeRegion(u"leftBrace", u"{");
+ ow.writeRegion(RightParenthesisRegion);
+ ow.ensureSpace().writeRegion(LeftBraceRegion);
ow.itemEnd(standin);
ow.eof();
return res;
}
-QString MethodInfo::postCode(DomItem &) const
+QString MethodInfo::postCode(const DomItem &) const
{
return QLatin1String("\n}\n");
}
-void MethodInfo::writeOut(DomItem &self, OutWriter &ow) const
+void MethodInfo::writeOut(const DomItem &self, OutWriter &ow) const
{
switch (methodType) {
case MethodType::Signal: {
if (body)
qCWarning(domLog) << "signal should not have a body in" << self.canonicalPath();
- ow.writeRegion(u"signal").space().writeRegion(u"name", name);
+ ow.writeRegion(SignalKeywordRegion).space().writeRegion(IdentifierRegion, name);
if (parameters.isEmpty())
return;
bool first = true;
- ow.writeRegion(u"leftParen", u"(");
+ ow.writeRegion(LeftParenthesisRegion);
int baseIndent = ow.increaseIndent();
- for (DomItem arg : self.field(Fields::parameters).values()) {
+ for (const DomItem &arg : self.field(Fields::parameters).values()) {
if (first)
first = false;
else
@@ -1811,86 +1899,112 @@ void MethodInfo::writeOut(DomItem &self, OutWriter &ow) const
else
qCWarning(domLog) << "failed to cast to MethodParameter";
}
- ow.writeRegion(u"rightParen", u")");
+ ow.writeRegion(RightParenthesisRegion);
ow.decreaseIndent(1, baseIndent);
return;
} break;
case MethodType::Method: {
- ow.writeRegion(u"function").space().writeRegion(u"name", name);
+ ow.writeRegion(FunctionKeywordRegion).space().writeRegion(IdentifierRegion, name);
bool first = true;
- ow.writeRegion(u"leftParen", u"(");
- for (DomItem arg : self.field(Fields::parameters).values()) {
+ ow.writeRegion(LeftParenthesisRegion);
+ for (const DomItem &arg : self.field(Fields::parameters).values()) {
if (first)
first = false;
else
ow.write(u", ");
arg.writeOut(ow);
}
- ow.writeRegion(u"rightParen", u")");
+ ow.writeRegion(RightParenthesisRegion);
if (!typeName.isEmpty()) {
- ow.writeRegion(u"colon", u":");
+ ow.writeRegion(ColonTokenRegion);
ow.space();
- ow.writeRegion(u"returnType", typeName);
+ ow.writeRegion(TypeIdentifierRegion, typeName);
}
- ow.ensureSpace().writeRegion(u"leftBrace", u"{");
+ ow.ensureSpace().writeRegion(LeftBraceRegion);
int baseIndent = ow.increaseIndent();
if (DomItem b = self.field(Fields::body)) {
ow.ensureNewline();
b.writeOut(ow);
}
ow.decreaseIndent(1, baseIndent);
- ow.ensureNewline().writeRegion(u"rightBrace", u"}");
+ ow.ensureNewline().writeRegion(RightBraceRegion);
} break;
}
}
-bool MethodParameter::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool MethodParameter::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::name, name);
if (!typeName.isEmpty()) {
cont = cont
- && self.dvReferenceField(visitor, Fields::type, Paths::lookupCppTypePath(typeName));
+ && self.dvReferenceField(visitor, Fields::type, Paths::lookupTypePath(typeName));
cont = cont && self.dvValueField(visitor, Fields::typeName, typeName);
}
cont = cont && self.dvValueField(visitor, Fields::isPointer, isPointer);
cont = cont && self.dvValueField(visitor, Fields::isReadonly, isReadonly);
cont = cont && self.dvValueField(visitor, Fields::isList, isList);
cont = cont && self.dvWrapField(visitor, Fields::defaultValue, defaultValue);
+ cont = cont && self.dvWrapField(visitor, Fields::value, value);
+
+ cont = cont && self.dvValueField(visitor, Fields::preCode, u"function f("_s);
+ cont = cont && self.dvValueField(visitor, Fields::postCode, u") {}"_s);
+
if (!annotations.isEmpty())
cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
return cont;
}
-void MethodParameter::writeOut(DomItem &self, OutWriter &ow) const
+void MethodParameter::writeOut(const DomItem &self, OutWriter &ow) const
{
- ow.writeRegion(u"name", name);
- if (!typeName.isEmpty())
- ow.writeRegion(u"colon", u":").space().writeRegion(u"type", typeName);
- if (defaultValue) {
- ow.space().writeRegion(u"equal", u"=").space();
- self.subOwnerItem(PathEls::Field(Fields::defaultValue), defaultValue).writeOut(ow);
+ if (!name.isEmpty()) {
+ if (isRestElement)
+ ow.writeRegion(EllipsisTokenRegion);
+ ow.writeRegion(IdentifierRegion, name);
+ if (!typeName.isEmpty())
+ ow.writeRegion(ColonTokenRegion).space().writeRegion(TypeIdentifierRegion, typeName);
+ if (defaultValue) {
+ ow.space().writeRegion(EqualTokenRegion).space();
+ self.subOwnerItem(PathEls::Field(Fields::defaultValue), defaultValue).writeOut(ow);
+ }
+ } else {
+ if (value) {
+ self.subOwnerItem(PathEls::Field(Fields::value), value).writeOut(ow);
+ }
}
}
-void MethodParameter::writeOutSignal(DomItem &self, OutWriter &ow) const
+void MethodParameter::writeOutSignal(const DomItem &self, OutWriter &ow) const
{
self.writeOutPre(ow);
if (!typeName.isEmpty())
- ow.writeRegion(u"type", typeName).space();
- ow.writeRegion(u"name", name);
+ ow.writeRegion(TypeIdentifierRegion, typeName).space();
+ ow.writeRegion(IdentifierRegion, name);
self.writeOutPost(ow);
}
-void Pragma::writeOut(DomItem &, OutWriter &ow) const
+void Pragma::writeOut(const DomItem &, OutWriter &ow) const
{
ow.ensureNewline();
- ow.writeRegion(u"pragma").space().writeRegion(u"name", name);
+ ow.writeRegion(PragmaKeywordRegion).space().writeRegion(IdentifierRegion, name);
+
+ bool isFirst = true;
+ for (const auto &value : values) {
+ if (isFirst) {
+ isFirst = false;
+ ow.writeRegion(ColonTokenRegion).space();
+ ow.writeRegion(PragmaValuesRegion, value);
+ continue;
+ }
+
+ ow.writeRegion(CommaTokenRegion).space();
+ ow.writeRegion(PragmaValuesRegion, value);
+ }
ow.ensureNewline();
}
-bool EnumItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool EnumItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::name, name());
@@ -1899,10 +2013,10 @@ bool EnumItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-void EnumItem::writeOut(DomItem &self, OutWriter &ow) const
+void EnumItem::writeOut(const DomItem &self, OutWriter &ow) const
{
ow.ensureNewline();
- ow.writeRegion(u"name", name());
+ ow.writeRegion(IdentifierRegion, name());
bool hasDefaultValue = false;
index_type myIndex = self.pathFromOwner().last().headIndex();
if (myIndex == 0)
@@ -1919,10 +2033,10 @@ void EnumItem::writeOut(DomItem &self, OutWriter &ow) const
QString v = QString::number(value(), 'f', 0);
if (abs(value() - v.toDouble()) > 1.e-10)
v = QString::number(value());
- ow.space().writeRegion(u"equal", u"=").space().writeRegion(u"value", v);
+ ow.space().writeRegion(EqualTokenRegion).space().writeRegion(PragmaValuesRegion, v);
}
if (myIndex >= 0 && self.container().indexes() != myIndex + 1)
- ow.writeRegion(u"comma", u",");
+ ow.writeRegion(CommaTokenRegion);
}
QmlUri QmlUri::fromString(const QString &str)
@@ -2069,6 +2183,11 @@ QmlUri::Kind QmlUri::kind() const
return m_kind;
}
+void ScriptExpression::setScriptElement(const ScriptElementVariant &p)
+{
+ m_element = p;
+}
+
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomelements_p.h b/src/qmldom/qqmldomelements_p.h
index cbf22a0018..db57da7bb2 100644
--- a/src/qmldom/qqmldomelements_p.h
+++ b/src/qmldom/qqmldomelements_p.h
@@ -22,17 +22,15 @@
#include <QtQml/private/qqmljsast_p.h>
#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <QtCore/QCborValue>
#include <QtCore/QCborMap>
#include <QtCore/QMutexLocker>
#include <QtCore/QPair>
-#ifdef QMLDOM_STANDALONE
-# include "qmlcompiler/qqmljsscope_p.h"
-#else
-# include <private/qqmljsscope_p.h>
-#endif
+#include <memory>
+#include <private/qqmljsscope_p.h>
#include <functional>
#include <limits>
@@ -45,38 +43,42 @@ namespace Dom {
// namespace for utility methods building specific paths
// using a namespace one can reopen it and add more methods in other places
namespace Paths {
-Path moduleIndexPath(QString uri, int majorVersion, ErrorHandler errorHandler = nullptr);
-Path moduleScopePath(QString uri, Version version, ErrorHandler errorHandler = nullptr);
-Path moduleScopePath(QString uri, QString version, ErrorHandler errorHandler = nullptr);
-inline Path moduleScopePath(QString uri, ErrorHandler errorHandler = nullptr)
+Path moduleIndexPath(
+ const QString &uri, int majorVersion, const ErrorHandler &errorHandler = nullptr);
+Path moduleScopePath(
+ const QString &uri, Version version, const ErrorHandler &errorHandler = nullptr);
+Path moduleScopePath(
+ const QString &uri, const QString &version, const ErrorHandler &errorHandler = nullptr);
+inline Path moduleScopePath(
+ const QString &uri, const ErrorHandler &errorHandler = nullptr)
{
return moduleScopePath(uri, QString(), errorHandler);
}
-inline Path qmlDirInfoPath(QString path)
+inline Path qmlDirInfoPath(const QString &path)
{
return Path::Root(PathRoot::Top).field(Fields::qmldirWithPath).key(path);
}
-inline Path qmlDirPath(QString path)
+inline Path qmlDirPath(const QString &path)
{
return qmlDirInfoPath(path).field(Fields::currentItem);
}
-inline Path qmldirFileInfoPath(QString path)
+inline Path qmldirFileInfoPath(const QString &path)
{
return Path::Root(PathRoot::Top).field(Fields::qmldirFileWithPath).key(path);
}
-inline Path qmldirFilePath(QString path)
+inline Path qmldirFilePath(const QString &path)
{
return qmldirFileInfoPath(path).field(Fields::currentItem);
}
-inline Path qmlFileInfoPath(QString canonicalFilePath)
+inline Path qmlFileInfoPath(const QString &canonicalFilePath)
{
return Path::Root(PathRoot::Top).field(Fields::qmlFileWithPath).key(canonicalFilePath);
}
-inline Path qmlFilePath(QString canonicalFilePath)
+inline Path qmlFilePath(const QString &canonicalFilePath)
{
return qmlFileInfoPath(canonicalFilePath).field(Fields::currentItem);
}
-inline Path qmlFileObjectPath(QString canonicalFilePath)
+inline Path qmlFileObjectPath(const QString &canonicalFilePath)
{
return qmlFilePath(canonicalFilePath)
.field(Fields::components)
@@ -85,55 +87,55 @@ inline Path qmlFileObjectPath(QString canonicalFilePath)
.field(Fields::objects)
.index(0);
}
-inline Path qmltypesFileInfoPath(QString path)
+inline Path qmltypesFileInfoPath(const QString &path)
{
return Path::Root(PathRoot::Top).field(Fields::qmltypesFileWithPath).key(path);
}
-inline Path qmltypesFilePath(QString path)
+inline Path qmltypesFilePath(const QString &path)
{
return qmltypesFileInfoPath(path).field(Fields::currentItem);
}
-inline Path jsFileInfoPath(QString path)
+inline Path jsFileInfoPath(const QString &path)
{
return Path::Root(PathRoot::Top).field(Fields::jsFileWithPath).key(path);
}
-inline Path jsFilePath(QString path)
+inline Path jsFilePath(const QString &path)
{
return jsFileInfoPath(path).field(Fields::currentItem);
}
-inline Path qmlDirectoryInfoPath(QString path)
+inline Path qmlDirectoryInfoPath(const QString &path)
{
return Path::Root(PathRoot::Top).field(Fields::qmlDirectoryWithPath).key(path);
}
-inline Path qmlDirectoryPath(QString path)
+inline Path qmlDirectoryPath(const QString &path)
{
return qmlDirectoryInfoPath(path).field(Fields::currentItem);
}
-inline Path globalScopeInfoPath(QString name)
+inline Path globalScopeInfoPath(const QString &name)
{
return Path::Root(PathRoot::Top).field(Fields::globalScopeWithName).key(name);
}
-inline Path globalScopePath(QString name)
+inline Path globalScopePath(const QString &name)
{
return globalScopeInfoPath(name).field(Fields::currentItem);
}
-inline Path lookupCppTypePath(QString name)
+inline Path lookupCppTypePath(const QString &name)
{
return Path::Current(PathCurrent::Lookup).field(Fields::cppType).key(name);
}
-inline Path lookupPropertyPath(QString name)
+inline Path lookupPropertyPath(const QString &name)
{
return Path::Current(PathCurrent::Lookup).field(Fields::propertyDef).key(name);
}
-inline Path lookupSymbolPath(QString name)
+inline Path lookupSymbolPath(const QString &name)
{
return Path::Current(PathCurrent::Lookup).field(Fields::symbol).key(name);
}
-inline Path lookupTypePath(QString name)
+inline Path lookupTypePath(const QString &name)
{
return Path::Current(PathCurrent::Lookup).field(Fields::type).key(name);
}
-inline Path loadInfoPath(Path el)
+inline Path loadInfoPath(const Path &el)
{
return Path::Root(PathRoot::Env).field(Fields::loadInfo).key(el.toString());
}
@@ -142,12 +144,12 @@ inline Path loadInfoPath(Path el)
class QMLDOM_EXPORT CommentableDomElement : public DomElement
{
public:
- CommentableDomElement(Path pathFromOwner = Path()) : DomElement(pathFromOwner) { }
+ CommentableDomElement(const Path &pathFromOwner = Path()) : DomElement(pathFromOwner) { }
CommentableDomElement(const CommentableDomElement &o) : DomElement(o), m_comments(o.m_comments)
{
}
CommentableDomElement &operator=(const CommentableDomElement &o) = default;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
RegionComments &comments() { return m_comments; }
const RegionComments &comments() const { return m_comments; }
@@ -165,7 +167,7 @@ public:
Version(qint32 majorVersion = Undefined, qint32 minorVersion = Undefined);
static Version fromString(QStringView v);
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const;
bool isLatest() const;
bool isValid() const;
@@ -264,17 +266,20 @@ class QMLDOM_EXPORT Import
public:
constexpr static DomType kindValue = DomType::Import;
- static Import fromUriString(QString importStr, Version v = Version(),
- QString importId = QString(), ErrorHandler handler = nullptr);
- static Import fromFileString(QString importStr, QString importId = QString(),
- ErrorHandler handler = nullptr);
+ static Import fromUriString(
+ const QString &importStr, Version v = Version(), const QString &importId = QString(),
+ const ErrorHandler &handler = nullptr);
+ static Import fromFileString(
+ const QString &importStr, const QString &importId = QString(),
+ const ErrorHandler &handler = nullptr);
- Import(QmlUri uri = QmlUri(), Version version = Version(), QString importId = QString())
+ Import(const QmlUri &uri = QmlUri(), Version version = Version(),
+ const QString &importId = QString())
: uri(uri), version(version), importId(importId)
{
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const;
Path importedPath() const
{
if (uri.isDirectory()) {
@@ -298,7 +303,7 @@ public:
}
friend bool operator!=(const Import &i1, const Import &i2) { return !(i1 == i2); }
- void writeOut(DomItem &self, OutWriter &ow) const;
+ void writeOut(const DomItem &self, OutWriter &ow) const;
static QRegularExpression importRe();
@@ -314,7 +319,7 @@ class QMLDOM_EXPORT ModuleAutoExport
public:
constexpr static DomType kindValue = DomType::ModuleAutoExport;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvWrapField(visitor, Fields::import, import);
@@ -340,18 +345,23 @@ class QMLDOM_EXPORT Pragma
public:
constexpr static DomType kindValue = DomType::Pragma;
- Pragma(QString pragmaName = QString()) : name(pragmaName) { }
+ Pragma(const QString &pragmaName = QString(), const QStringList &pragmaValues = {})
+ : name(pragmaName), values{ pragmaValues }
+ {
+ }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = self.dvValueField(visitor, Fields::name, name);
+ cont = cont && self.dvValueField(visitor, Fields::values, values);
cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
return cont;
}
- void writeOut(DomItem &self, OutWriter &ow) const;
+ void writeOut(const DomItem &self, OutWriter &ow) const;
QString name;
+ QStringList values;
RegionComments comments;
};
@@ -360,34 +370,45 @@ class QMLDOM_EXPORT Id
public:
constexpr static DomType kindValue = DomType::Id;
- Id(QString idName = QString(), Path referredObject = Path());
+ Id(const QString &idName = QString(), const Path &referredObject = Path());
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
- void updatePathFromOwner(Path pathFromOwner);
- Path addAnnotation(Path selfPathFromOwner, const QmlObject &ann, QmlObject **aPtr = nullptr);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const;
+ void updatePathFromOwner(const Path &pathFromOwner);
+ Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &ann, QmlObject **aPtr = nullptr);
QString name;
Path referredObjectPath;
RegionComments comments;
QList<QmlObject> annotations;
+ std::shared_ptr<ScriptExpression> value;
};
+// TODO: rename? it may contain statements and stuff, not only expressions
+// TODO QTBUG-121933
class QMLDOM_EXPORT ScriptExpression final : public OwningItem
{
Q_GADGET
Q_DECLARE_TR_FUNCTIONS(ScriptExpression)
public:
- enum class ExpressionType { BindingExpression, FunctionBody, ArgInitializer };
+ enum class ExpressionType {
+ BindingExpression,
+ FunctionBody,
+ ArgInitializer,
+ ArgumentStructure,
+ ReturnType,
+ JSCode, // Used for storing the content of the whole .js file as "one" Expression
+ ESMCode, // Used for storing the content of the whole ECMAScript module (.mjs) as "one"
+ // Expression
+ };
Q_ENUM(ExpressionType);
constexpr static DomType kindValue = DomType::ScriptExpression;
DomType kind() const override { return kindValue; }
- explicit ScriptExpression(QStringView code, std::shared_ptr<QQmlJS::Engine> engine,
- AST::Node *ast, std::shared_ptr<AstComments> comments,
- ExpressionType expressionType,
- SourceLocation localOffset = SourceLocation(), int derivedFrom = 0,
- QStringView preCode = QStringView(),
- QStringView postCode = QStringView());
+ explicit ScriptExpression(
+ QStringView code, const std::shared_ptr<QQmlJS::Engine> &engine, AST::Node *ast,
+ const std::shared_ptr<AstComments> &comments, ExpressionType expressionType,
+ SourceLocation localOffset = SourceLocation(), int derivedFrom = 0,
+ QStringView preCode = QStringView(), QStringView postCode = QStringView());
ScriptExpression()
: ScriptExpression(QStringView(), std::shared_ptr<QQmlJS::Engine>(), nullptr,
@@ -396,8 +417,9 @@ public:
{
}
- explicit ScriptExpression(QString code, ExpressionType expressionType, int derivedFrom = 0,
- QString preCode = QString(), QString postCode = QString())
+ explicit ScriptExpression(
+ const QString &code, ExpressionType expressionType, int derivedFrom = 0,
+ const QString &preCode = QString(), const QString &postCode = QString())
: OwningItem(derivedFrom), m_expressionType(expressionType)
{
setCode(code, preCode, postCode);
@@ -405,20 +427,20 @@ public:
ScriptExpression(const ScriptExpression &e);
- std::shared_ptr<ScriptExpression> makeCopy(DomItem &self) const
+ std::shared_ptr<ScriptExpression> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<ScriptExpression>(doCopy(self));
}
- std::shared_ptr<ScriptExpression> copyWithUpdatedCode(DomItem &self, QString code) const;
+ std::shared_ptr<ScriptExpression> copyWithUpdatedCode(const DomItem &self, const QString &code) const;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
- Path canonicalPath(DomItem &self) const override { return self.m_ownerPath; }
+ Path canonicalPath(const DomItem &self) const override { return self.m_ownerPath; }
// parsed and created if not available
AST::Node *ast() const { return m_ast; }
// dump of the ast (without locations)
- void astDumper(Sink s, AstDumperOptions options) const;
+ void astDumper(const Sink &s, AstDumperOptions options) const;
QString astRelocatableDump() const;
// definedSymbols name, value, from
@@ -446,19 +468,21 @@ public:
return m_engine;
}
std::shared_ptr<AstComments> astComments() const { return m_astComments; }
- void writeOut(DomItem &self, OutWriter &lw) const override;
- SourceLocation globalLocation(DomItem &self) const;
+ void writeOut(const DomItem &self, OutWriter &lw) const override;
+ SourceLocation globalLocation(const DomItem &self) const;
SourceLocation localOffset() const { return m_localOffset; }
QStringView preCode() const { return m_preCode; }
QStringView postCode() const { return m_postCode; }
+ void setScriptElement(const ScriptElementVariant &p);
+ ScriptElementVariant scriptElement() { return m_element; }
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
return std::make_shared<ScriptExpression>(*this);
}
- std::function<SourceLocation(SourceLocation)> locationToGlobalF(DomItem &self) const
+ std::function<SourceLocation(SourceLocation)> locationToGlobalF(const DomItem &self) const
{
SourceLocation loc = globalLocation(self);
return [loc, this](SourceLocation x) {
@@ -479,13 +503,34 @@ protected:
: x.startColumn)); // are line and column 1 based? then we should + 1
}
- std::function<SourceLocation(SourceLocation)> locationToLocalF(DomItem &) const
+ std::function<SourceLocation(SourceLocation)> locationToLocalF(const DomItem &) const
{
return [this](SourceLocation x) { return locationToLocal(x); };
}
private:
- void setCode(QString code, QString preCode, QString postCode);
+ enum class ParseMode {
+ QML,
+ JS,
+ ESM, // ECMAScript module
+ };
+
+ inline ParseMode resolveParseMode()
+ {
+ switch (m_expressionType) {
+ case ExpressionType::BindingExpression:
+ // unfortunately there are no documentation explaining this resolution
+ // this was just moved from the original implementation
+ return ParseMode::QML;
+ case ExpressionType::ESMCode:
+ return ParseMode::ESM;
+ default:
+ return ParseMode::JS;
+ }
+ }
+ void setCode(const QString &code, const QString &preCode, const QString &postCode);
+ [[nodiscard]] AST::Node *parse(ParseMode mode);
+
ExpressionType m_expressionType;
QString m_codeStr;
QStringView m_code;
@@ -495,6 +540,7 @@ private:
mutable AST::Node *m_ast;
std::shared_ptr<AstComments> m_astComments;
SourceLocation m_localOffset;
+ ScriptElementVariant m_element;
};
class BindingValue;
@@ -504,22 +550,25 @@ class QMLDOM_EXPORT Binding
public:
constexpr static DomType kindValue = DomType::Binding;
- Binding(QString m_name = QString(),
+ Binding(const QString &m_name = QString(),
std::unique_ptr<BindingValue> value = std::unique_ptr<BindingValue>(),
BindingType bindingType = BindingType::Normal);
- Binding(QString m_name, std::shared_ptr<ScriptExpression> value,
+ Binding(const QString &m_name, const std::shared_ptr<ScriptExpression> &value,
+ BindingType bindingType = BindingType::Normal);
+ Binding(const QString &m_name, const QString &scriptCode,
+ BindingType bindingType = BindingType::Normal);
+ Binding(const QString &m_name, const QmlObject &value,
+ BindingType bindingType = BindingType::Normal);
+ Binding(const QString &m_name, const QList<QmlObject> &value,
BindingType bindingType = BindingType::Normal);
- Binding(QString m_name, QString scriptCode, BindingType bindingType = BindingType::Normal);
- Binding(QString m_name, QmlObject value, BindingType bindingType = BindingType::Normal);
- Binding(QString m_name, QList<QmlObject> value, BindingType bindingType = BindingType::Normal);
Binding(const Binding &o);
Binding(Binding &&o) = default;
~Binding();
Binding &operator=(const Binding &);
Binding &operator=(Binding &&) = default;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor);
- DomItem valueItem(DomItem &self) const; // ### REVISIT: consider replacing return value with variant
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const;
+ DomItem valueItem(const DomItem &self) const; // ### REVISIT: consider replacing return value with variant
BindingValueKind valueKind() const;
QString name() const { return m_name; }
BindingType bindingType() const { return m_bindingType; }
@@ -530,20 +579,18 @@ public:
QList<QmlObject> *arrayValue();
std::shared_ptr<ScriptExpression> scriptExpressionValue();
QList<QmlObject> annotations() const { return m_annotations; }
- void setAnnotations(QList<QmlObject> annotations) { m_annotations = annotations; }
+ void setAnnotations(const QList<QmlObject> &annotations) { m_annotations = annotations; }
void setValue(std::unique_ptr<BindingValue> &&value) { m_value = std::move(value); }
- Path addAnnotation(Path selfPathFromOwner, const QmlObject &a, QmlObject **aPtr = nullptr);
+ Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &a, QmlObject **aPtr = nullptr);
const RegionComments &comments() const { return m_comments; }
RegionComments &comments() { return m_comments; }
- void updatePathFromOwner(Path newPath);
- void writeOut(DomItem &self, OutWriter &lw) const;
- void writeOutValue(DomItem &self, OutWriter &lw) const;
+ void updatePathFromOwner(const Path &newPath);
+ void writeOut(const DomItem &self, OutWriter &lw) const;
+ void writeOutValue(const DomItem &self, OutWriter &lw) const;
bool isSignalHandler() const
{
QString baseName = m_name.split(QLatin1Char('.')).last();
- if (baseName.startsWith(u"on") && baseName.size() > 2 && baseName.at(2).isUpper())
- return true;
- return false;
+ return QQmlSignalNames::isHandlerName(baseName);
}
static QString preCodeForName(QStringView n)
{
@@ -553,13 +600,17 @@ public:
QString preCode() const { return preCodeForName(m_name); }
QString postCode() const { return postCodeForName(m_name); }
+ ScriptElementVariant bindingIdentifiers() const { return m_bindingIdentifiers; }
+ void setBindingIdentifiers(const ScriptElementVariant &bindingIdentifiers) { m_bindingIdentifiers = bindingIdentifiers; }
+
private:
- friend class QmlDomAstCreator;
+ friend class QQmlDomAstCreator;
BindingType m_bindingType;
QString m_name;
std::unique_ptr<BindingValue> m_value;
QList<QmlObject> m_annotations;
RegionComments m_comments;
+ ScriptElementVariant m_bindingIdentifiers;
};
class QMLDOM_EXPORT AttributeInfo
@@ -567,11 +618,14 @@ class QMLDOM_EXPORT AttributeInfo
public:
enum Access { Private, Protected, Public };
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
- Path addAnnotation(Path selfPathFromOwner, const QmlObject &annotation,
+ Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation,
QmlObject **aPtr = nullptr);
- void updatePathFromOwner(Path newPath);
+ void updatePathFromOwner(const Path &newPath);
+
+ QQmlJSScope::ConstPtr semanticScope() const { return m_semanticScope; }
+ void setSemanticScope(const QQmlJSScope::ConstPtr &scope) { m_semanticScope = scope; }
QString name;
Access access = Access::Public;
@@ -580,6 +634,7 @@ public:
bool isList = false;
QList<QmlObject> annotations;
RegionComments comments;
+ QQmlJSScope::ConstPtr m_semanticScope;
};
struct QMLDOM_EXPORT LocallyResolvedAlias
@@ -608,7 +663,7 @@ class QMLDOM_EXPORT PropertyDefinition : public AttributeInfo
public:
constexpr static DomType kindValue = DomType::PropertyDefinition;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::isPointer, isPointer);
@@ -621,6 +676,11 @@ public:
cont = cont && self.dvValueField(visitor, Fields::bindable, bindable);
cont = cont && self.dvValueField(visitor, Fields::notify, notify);
cont = cont && self.dvReferenceField(visitor, Fields::type, typePath());
+ if (m_nameIdentifiers) {
+ cont = cont && self.dvItemField(visitor, Fields::nameIdentifiers, [this, &self]() {
+ return self.subScriptElementWrapperItem(m_nameIdentifiers);
+ });
+ }
return cont;
}
@@ -628,7 +688,9 @@ public:
bool isAlias() const { return typeName == u"alias"; }
bool isParametricType() const;
- void writeOut(DomItem &self, OutWriter &lw) const;
+ void writeOut(const DomItem &self, OutWriter &lw) const;
+ ScriptElementVariant nameIdentifiers() const { return m_nameIdentifiers; }
+ void setNameIdentifiers(const ScriptElementVariant &name) { m_nameIdentifiers = name; }
QString read;
QString write;
@@ -638,6 +700,7 @@ public:
bool isPointer = false;
bool isDefaultMember = false;
bool isRequired = false;
+ ScriptElementVariant m_nameIdentifiers;
};
class QMLDOM_EXPORT PropertyInfo
@@ -645,7 +708,7 @@ class QMLDOM_EXPORT PropertyInfo
public:
constexpr static DomType kindValue = DomType::PropertyInfo; // used to get the correct kind in ObjectWrapper
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
QList<DomItem> propertyDefs;
QList<DomItem> bindings;
@@ -656,17 +719,24 @@ class QMLDOM_EXPORT MethodParameter
public:
constexpr static DomType kindValue = DomType::MethodParameter;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
- void writeOut(DomItem &self, OutWriter &ow) const;
- void writeOutSignal(DomItem &self, OutWriter &ow) const;
+ void writeOut(const DomItem &self, OutWriter &ow) const;
+ void writeOutSignal(const DomItem &self, OutWriter &ow) const;
QString name;
QString typeName;
bool isPointer = false;
bool isReadonly = false;
bool isList = false;
+ bool isRestElement = false;
std::shared_ptr<ScriptExpression> defaultValue;
+ /*!
+ \internal
+ Contains the scriptElement representing this argument, inclusive default value,
+ deconstruction, etc.
+ */
+ std::shared_ptr<ScriptExpression> value;
QList<QmlObject> annotations;
RegionComments comments;
};
@@ -680,28 +750,29 @@ public:
constexpr static DomType kindValue = DomType::MethodInfo;
- Path typePath(DomItem &) const
+ Path typePath(const DomItem &) const
{
return (typeName.isEmpty() ? Path() : Paths::lookupTypePath(typeName));
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
- QString preCode(DomItem &) const; // ### REVISIT, might be simplified by using different toplevel production rules at usage site
- QString postCode(DomItem &) const;
- void writePre(DomItem &self, OutWriter &ow) const;
- void writeOut(DomItem &self, OutWriter &ow) const;
- void setCode(QString code)
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
+ QString preCode(const DomItem &) const; // ### REVISIT, might be simplified by using different toplevel production rules at usage site
+ QString postCode(const DomItem &) const;
+ void writePre(const DomItem &self, OutWriter &ow) const;
+ void writeOut(const DomItem &self, OutWriter &ow) const;
+ void setCode(const QString &code)
{
body = std::make_shared<ScriptExpression>(
code, ScriptExpression::ExpressionType::FunctionBody, 0,
QLatin1String("function foo(){\n"), QLatin1String("\n}\n"));
}
-
MethodInfo() = default;
+ // TODO: make private + add getters/setters
QList<MethodParameter> parameters;
MethodType methodType = Method;
std::shared_ptr<ScriptExpression> body;
+ std::shared_ptr<ScriptExpression> returnType;
bool isConstructor = false;
};
@@ -710,15 +781,15 @@ class QMLDOM_EXPORT EnumItem
public:
constexpr static DomType kindValue = DomType::EnumItem;
- EnumItem(QString name = QString(), int value = 0) : m_name(name), m_value(value) { }
+ EnumItem(const QString &name = QString(), int value = 0) : m_name(name), m_value(value) { }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
QString name() const { return m_name; }
double value() const { return m_value; }
RegionComments &comments() { return m_comments; }
const RegionComments &comments() const { return m_comments; }
- void writeOut(DomItem &self, OutWriter &lw) const;
+ void writeOut(const DomItem &self, OutWriter &lw) const;
private:
QString m_name;
@@ -732,33 +803,33 @@ public:
constexpr static DomType kindValue = DomType::EnumDecl;
DomType kind() const override { return kindValue; }
- EnumDecl(QString name = QString(), QList<EnumItem> values = QList<EnumItem>(),
+ EnumDecl(const QString &name = QString(), QList<EnumItem> values = QList<EnumItem>(),
Path pathFromOwner = Path())
: CommentableDomElement(pathFromOwner), m_name(name), m_values(values)
{
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
QString name() const { return m_name; }
- void setName(QString name) { m_name = name; }
+ void setName(const QString &name) { m_name = name; }
const QList<EnumItem> &values() const & { return m_values; }
bool isFlag() const { return m_isFlag; }
void setIsFlag(bool flag) { m_isFlag = flag; }
QString alias() const { return m_alias; }
- void setAlias(QString aliasName) { m_alias = aliasName; }
+ void setAlias(const QString &aliasName) { m_alias = aliasName; }
void setValues(QList<EnumItem> values) { m_values = values; }
Path addValue(EnumItem value)
{
m_values.append(value);
return Path::Field(Fields::values).index(index_type(m_values.size() - 1));
}
- void updatePathFromOwner(Path newP) override;
+ void updatePathFromOwner(const Path &newP) override;
const QList<QmlObject> &annotations() const & { return m_annotations; }
- void setAnnotations(QList<QmlObject> annotations);
+ void setAnnotations(const QList<QmlObject> &annotations);
Path addAnnotation(const QmlObject &child, QmlObject **cPtr = nullptr);
- void writeOut(DomItem &self, OutWriter &lw) const override;
+ void writeOut(const DomItem &self, OutWriter &lw) const override;
private:
QString m_name;
@@ -775,20 +846,16 @@ public:
constexpr static DomType kindValue = DomType::QmlObject;
DomType kind() const override { return kindValue; }
- QmlObject(Path pathFromOwner = Path());
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- bool iterateBaseDirectSubpaths(DomItem &self, DirectVisitor);
+ QmlObject(const Path &pathFromOwner = Path());
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ bool iterateBaseDirectSubpaths(const DomItem &self, DirectVisitor) const;
QList<QString> fields() const;
- QList<QString> fields(DomItem &) const override { return fields(); }
- DomItem field(DomItem &self, QStringView name);
- DomItem field(DomItem &self, QStringView name) const override
- {
- return const_cast<QmlObject *>(this)->field(self, name);
- }
- void updatePathFromOwner(Path newPath) override;
+ QList<QString> fields(const DomItem &) const override { return fields(); }
+ DomItem field(const DomItem &self, QStringView name) const override;
+ void updatePathFromOwner(const Path &newPath) override;
QString localDefaultPropertyName() const;
- QString defaultPropertyName(DomItem &self) const;
- virtual bool iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor) const;
+ QString defaultPropertyName(const DomItem &self) const;
+ virtual bool iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor) const;
QString idStr() const { return m_idStr; }
QString name() const { return m_name; }
@@ -800,11 +867,11 @@ public:
QList<QmlObject> children() const { return m_children; }
QList<QmlObject> annotations() const { return m_annotations; }
- void setIdStr(QString id) { m_idStr = id; }
- void setName(QString name) { m_name = name; }
- void setDefaultPropertyName(QString name) { m_defaultPropertyName = name; }
+ void setIdStr(const QString &id) { m_idStr = id; }
+ void setName(const QString &name) { m_name = name; }
+ void setDefaultPropertyName(const QString &name) { m_defaultPropertyName = name; }
void setPrototypePaths(QList<Path> prototypePaths) { m_prototypePaths = prototypePaths; }
- Path addPrototypePath(Path prototypePath)
+ Path addPrototypePath(const Path &prototypePath)
{
index_type idx = index_type(m_prototypePaths.indexOf(prototypePath));
if (idx == -1) {
@@ -813,33 +880,33 @@ public:
}
return Path::Field(Fields::prototypes).index(idx);
}
- void setNextScopePath(Path nextScopePath) { m_nextScopePath = nextScopePath; }
+ void setNextScopePath(const Path &nextScopePath) { m_nextScopePath = nextScopePath; }
void setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs)
{
m_propertyDefs = propertyDefs;
}
void setBindings(QMultiMap<QString, Binding> bindings) { m_bindings = bindings; }
void setMethods(QMultiMap<QString, MethodInfo> functionDefs) { m_methods = functionDefs; }
- void setChildren(QList<QmlObject> children)
+ void setChildren(const QList<QmlObject> &children)
{
m_children = children;
if (pathFromOwner())
updatePathFromOwner(pathFromOwner());
}
- void setAnnotations(QList<QmlObject> annotations)
+ void setAnnotations(const QList<QmlObject> &annotations)
{
m_annotations = annotations;
if (pathFromOwner())
updatePathFromOwner(pathFromOwner());
}
- Path addPropertyDef(PropertyDefinition propertyDef, AddOption option,
+ Path addPropertyDef(const PropertyDefinition &propertyDef, AddOption option,
PropertyDefinition **pDef = nullptr)
{
return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::propertyDefs),
m_propertyDefs, propertyDef.name, propertyDef,
option, pDef);
}
- MutableDomItem addPropertyDef(MutableDomItem &self, PropertyDefinition propertyDef,
+ MutableDomItem addPropertyDef(MutableDomItem &self, const PropertyDefinition &propertyDef,
AddOption option);
Path addBinding(Binding binding, AddOption option, Binding **bPtr = nullptr)
@@ -848,12 +915,12 @@ public:
binding.name(), binding, option, bPtr);
}
MutableDomItem addBinding(MutableDomItem &self, Binding binding, AddOption option);
- Path addMethod(MethodInfo functionDef, AddOption option, MethodInfo **mPtr = nullptr)
+ Path addMethod(const MethodInfo &functionDef, AddOption option, MethodInfo **mPtr = nullptr)
{
return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::methods), m_methods,
functionDef.name, functionDef, option, mPtr);
}
- MutableDomItem addMethod(MutableDomItem &self, MethodInfo functionDef, AddOption option);
+ MutableDomItem addMethod(MutableDomItem &self, const MethodInfo &functionDef, AddOption option);
Path addChild(QmlObject child, QmlObject **cPtr = nullptr)
{
return appendUpdatableElementInQList(pathFromOwner().field(Fields::children), m_children,
@@ -869,15 +936,21 @@ public:
return appendUpdatableElementInQList(pathFromOwner().field(Fields::annotations),
m_annotations, annotation, aPtr);
}
- void writeOut(DomItem &self, OutWriter &ow, QString onTarget) const;
- void writeOut(DomItem &self, OutWriter &lw) const override { writeOut(self, lw, QString()); }
+ void writeOut(const DomItem &self, OutWriter &ow, const QString &onTarget) const;
+ void writeOut(const DomItem &self, OutWriter &lw) const override { writeOut(self, lw, QString()); }
- LocallyResolvedAlias resolveAlias(DomItem &self,
+ LocallyResolvedAlias resolveAlias(const DomItem &self,
std::shared_ptr<ScriptExpression> accessSequence) const;
- LocallyResolvedAlias resolveAlias(DomItem &self, const QStringList &accessSequence) const;
+ LocallyResolvedAlias resolveAlias(const DomItem &self, const QStringList &accessSequence) const;
+
+ QQmlJSScope::ConstPtr semanticScope() const { return m_scope; }
+ void setSemanticScope(const QQmlJSScope::ConstPtr &scope) { m_scope = scope; }
+
+ ScriptElementVariant nameIdentifiers() const { return m_nameIdentifiers; }
+ void setNameIdentifiers(const ScriptElementVariant &name) { m_nameIdentifiers = name; }
private:
- friend class QmlDomAstCreator;
+ friend class QQmlDomAstCreator;
QString m_idStr;
QString m_name;
QList<Path> m_prototypePaths;
@@ -888,6 +961,8 @@ private:
QMultiMap<QString, MethodInfo> m_methods;
QList<QmlObject> m_children;
QList<QmlObject> m_annotations;
+ QQmlJSScope::ConstPtr m_scope;
+ ScriptElementVariant m_nameIdentifiers;
};
class Export
@@ -895,8 +970,9 @@ class Export
Q_DECLARE_TR_FUNCTIONS(Export)
public:
constexpr static DomType kindValue = DomType::Export;
- static Export fromString(Path source, QStringView exp, Path typePath, ErrorHandler h);
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+ static Export fromString(
+ const Path &source, QStringView exp, const Path &typePath, const ErrorHandler &h);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::uri, uri);
@@ -923,18 +999,14 @@ public:
class QMLDOM_EXPORT Component : public CommentableDomElement
{
public:
- Component(QString name);
- Component(Path pathFromOwner = Path());
+ Component(const QString &name);
+ Component(const Path &pathFromOwner = Path());
Component(const Component &o) = default;
Component &operator=(const Component &) = default;
- bool iterateDirectSubpaths(DomItem &, DirectVisitor) override;
- void updatePathFromOwner(Path newPath) override;
- DomItem field(DomItem &self, QStringView name) const override
- {
- return const_cast<Component *>(this)->field(self, name);
- }
- DomItem field(DomItem &self, QStringView name);
+ bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override;
+ void updatePathFromOwner(const Path &newPath) override;
+ DomItem field(const DomItem &self, QStringView name) const override;
QString name() const { return m_name; }
const QMultiMap<QString, EnumDecl> &enumerations() const & { return m_enumerations; }
@@ -943,9 +1015,9 @@ public:
bool isCreatable() const { return m_isCreatable; }
bool isComposite() const { return m_isComposite; }
QString attachedTypeName() const { return m_attachedTypeName; }
- Path attachedTypePath(DomItem &) const { return m_attachedTypePath; }
+ Path attachedTypePath(const DomItem &) const { return m_attachedTypePath; }
- void setName(QString name) { m_name = name; }
+ void setName(const QString &name) { m_name = name; }
void setEnumerations(QMultiMap<QString, EnumDecl> enumerations)
{
m_enumerations = enumerations;
@@ -957,16 +1029,16 @@ public:
m_enumerations, enumeration.name(), enumeration,
option, ePtr);
}
- void setObjects(QList<QmlObject> objects) { m_objects = objects; }
+ void setObjects(const QList<QmlObject> &objects) { m_objects = objects; }
Path addObject(const QmlObject &object, QmlObject **oPtr = nullptr);
void setIsSingleton(bool isSingleton) { m_isSingleton = isSingleton; }
void setIsCreatable(bool isCreatable) { m_isCreatable = isCreatable; }
void setIsComposite(bool isComposite) { m_isComposite = isComposite; }
- void setAttachedTypeName(QString name) { m_attachedTypeName = name; }
- void setAttachedTypePath(Path p) { m_attachedTypePath = p; }
+ void setAttachedTypeName(const QString &name) { m_attachedTypeName = name; }
+ void setAttachedTypePath(const Path &p) { m_attachedTypePath = p; }
private:
- friend class QmlDomAstCreator;
+ friend class QQmlDomAstCreator;
QString m_name;
QMultiMap<QString, EnumDecl> m_enumerations;
QList<QmlObject> m_objects;
@@ -983,8 +1055,8 @@ public:
constexpr static DomType kindValue = DomType::JsResource;
DomType kind() const override { return kindValue; }
- JsResource(Path pathFromOwner = Path()) : Component(pathFromOwner) { }
- bool iterateDirectSubpaths(DomItem &, DirectVisitor) override
+ JsResource(const Path &pathFromOwner = Path()) : Component(pathFromOwner) { }
+ bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override
{ // to do: complete
return true;
}
@@ -997,13 +1069,13 @@ public:
constexpr static DomType kindValue = DomType::QmltypesComponent;
DomType kind() const override { return kindValue; }
- QmltypesComponent(Path pathFromOwner = Path()) : Component(pathFromOwner) { }
- bool iterateDirectSubpaths(DomItem &, DirectVisitor) override;
+ QmltypesComponent(const Path &pathFromOwner = Path()) : Component(pathFromOwner) { }
+ bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override;
const QList<Export> &exports() const & { return m_exports; }
QString fileName() const { return m_fileName; }
void setExports(QList<Export> exports) { m_exports = exports; }
void addExport(const Export &exportedEntry) { m_exports.append(exportedEntry); }
- void setFileName(QString fileName) { m_fileName = fileName; }
+ void setFileName(const QString &fileName) { m_fileName = fileName; }
const QList<int> &metaRevisions() const & { return m_metaRevisions; }
void setMetaRevisions(QList<int> metaRevisions) { m_metaRevisions = metaRevisions; }
void setInterfaceNames(const QStringList& interfaces) { m_interfaceNames = interfaces; }
@@ -1014,20 +1086,28 @@ public:
void setValueTypeName(const QString &name) { m_valueTypeName = name; }
bool hasCustomParser() const { return m_hasCustomParser; }
void setHasCustomParser(bool v) { m_hasCustomParser = v; }
+ bool extensionIsJavaScript() const { return m_extensionIsJavaScript; }
+ void setExtensionIsJavaScript(bool v) { m_extensionIsJavaScript = v; }
bool extensionIsNamespace() const { return m_extensionIsNamespace; }
void setExtensionIsNamespace(bool v) { m_extensionIsNamespace = v; }
QQmlJSScope::AccessSemantics accessSemantics() const { return m_accessSemantics; }
void setAccessSemantics(QQmlJSScope::AccessSemantics v) { m_accessSemantics = v; }
+
+ void setSemanticScope(const QQmlJSScope::ConstPtr &scope) { m_semanticScope = scope; }
+ QQmlJSScope::ConstPtr semanticScope() const { return m_semanticScope; }
+
private:
QList<Export> m_exports;
QList<int> m_metaRevisions;
QString m_fileName; // remove?
QStringList m_interfaceNames;
bool m_hasCustomParser = false;
+ bool m_extensionIsJavaScript = false;
bool m_extensionIsNamespace = false;
QString m_valueTypeName;
QString m_extensionTypeName;
- QQmlJSScope::AccessSemantics m_accessSemantics;
+ QQmlJSScope::AccessSemantics m_accessSemantics = QQmlJSScope::AccessSemantics::None;
+ QQmlJSScope::ConstPtr m_semanticScope;
};
class QMLDOM_EXPORT QmlComponent final : public Component
@@ -1036,39 +1116,42 @@ public:
constexpr static DomType kindValue = DomType::QmlComponent;
DomType kind() const override { return kindValue; }
- QmlComponent(QString name = QString()) : Component(name)
+ QmlComponent(const QString &name = QString()) : Component(name)
{
setIsComposite(true);
setIsCreatable(true);
}
- QmlComponent(const QmlComponent &o)
- : Component(o), m_nextComponentPath(o.m_nextComponentPath), m_ids(o.m_ids)
- {
- }
- QmlComponent &operator=(const QmlComponent &) = default;
-
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
const QMultiMap<QString, Id> &ids() const & { return m_ids; }
Path nextComponentPath() const { return m_nextComponentPath; }
void setIds(QMultiMap<QString, Id> ids) { m_ids = ids; }
- void setNextComponentPath(Path p) { m_nextComponentPath = p; }
- void updatePathFromOwner(Path newPath) override;
+ void setNextComponentPath(const Path &p) { m_nextComponentPath = p; }
+ void updatePathFromOwner(const Path &newPath) override;
Path addId(const Id &id, AddOption option = AddOption::Overwrite, Id **idPtr = nullptr)
{
// warning does nor remove old idStr when overwriting...
return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::ids), m_ids, id.name,
id, option, idPtr);
}
- void writeOut(DomItem &self, OutWriter &) const override;
- QList<QString> subComponentsNames(DomItem &self) const;
- QList<DomItem> subComponents(DomItem &self) const;
+ void writeOut(const DomItem &self, OutWriter &) const override;
+ QList<QString> subComponentsNames(const DomItem &self) const;
+ QList<DomItem> subComponents(const DomItem &self) const;
+
+ void setSemanticScope(const QQmlJSScope::ConstPtr &scope) { m_semanticScope = scope; }
+ QQmlJSScope::ConstPtr semanticScope() const { return m_semanticScope; }
+ ScriptElementVariant nameIdentifiers() const { return m_nameIdentifiers; }
+ void setNameIdentifiers(const ScriptElementVariant &name) { m_nameIdentifiers = name; }
private:
- friend class QmlDomAstCreator;
+ friend class QQmlDomAstCreator;
Path m_nextComponentPath;
QMultiMap<QString, Id> m_ids;
+ QQmlJSScope::ConstPtr m_semanticScope;
+ // m_nameIdentifiers contains the name of the component as FieldMemberExpression, and therefore
+ // only exists in inline components!
+ ScriptElementVariant m_nameIdentifiers;
};
class QMLDOM_EXPORT GlobalComponent final : public Component
@@ -1077,7 +1160,7 @@ public:
constexpr static DomType kindValue = DomType::GlobalComponent;
DomType kind() const override { return kindValue; }
- GlobalComponent(Path pathFromOwner = Path()) : Component(pathFromOwner) { }
+ GlobalComponent(const Path &pathFromOwner = Path()) : Component(pathFromOwner) { }
};
static ErrorGroups importErrors = { { DomItem::domErrorGroup, NewErrorGroup("importError") } };
@@ -1095,22 +1178,24 @@ public:
const QMap<QString, ImportScope> &subImports() const & { return m_subImports; }
- QList<Path> allSources(DomItem &self) const;
+ QList<Path> allSources(const DomItem &self) const;
- QSet<QString> importedNames(DomItem &self) const
+ QSet<QString> importedNames(const DomItem &self) const
{
QSet<QString> res;
- for (Path p : allSources(self)) {
+ const auto sources = allSources(self);
+ for (const Path &p : sources) {
QSet<QString> ks = self.path(p.field(Fields::exports), self.errorHandler()).keys();
res += ks;
}
return res;
}
- QList<DomItem> importedItemsWithName(DomItem &self, QString name) const
+ QList<DomItem> importedItemsWithName(const DomItem &self, const QString &name) const
{
QList<DomItem> res;
- for (Path p : allSources(self)) {
+ const auto sources = allSources(self);
+ for (const Path &p : sources) {
DomItem source = self.path(p.field(Fields::exports), self.errorHandler());
DomItem els = source.key(name);
int nEls = els.indexes();
@@ -1125,10 +1210,10 @@ public:
return res;
}
- QList<Export> importedExportsWithName(DomItem &self, QString name) const
+ QList<Export> importedExportsWithName(const DomItem &self, const QString &name) const
{
QList<Export> res;
- for (DomItem &i : importedItemsWithName(self, name))
+ for (const DomItem &i : importedItemsWithName(self, name))
if (const Export *e = i.as<Export>())
res.append(*e);
else
@@ -1137,13 +1222,13 @@ public:
return res;
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
- void addImport(QStringList p, Path targetExports)
+ void addImport(QStringList p, const Path &targetExports)
{
if (!p.isEmpty()) {
- QString current = p.takeFirst();
- m_subImports[current].addImport(p, targetExports);
+ const QString current = p.takeFirst();
+ m_subImports[current].addImport(std::move(p), targetExports);
} else if (!m_importSourcePaths.contains(targetExports)) {
m_importSourcePaths.append(targetExports);
}
@@ -1159,14 +1244,14 @@ class BindingValue
public:
BindingValue();
BindingValue(const QmlObject &o);
- BindingValue(std::shared_ptr<ScriptExpression> o);
+ BindingValue(const std::shared_ptr<ScriptExpression> &o);
BindingValue(const QList<QmlObject> &l);
~BindingValue();
BindingValue(const BindingValue &o);
BindingValue &operator=(const BindingValue &o);
- DomItem value(DomItem &binding);
- void updatePathFromOwner(Path newPath);
+ DomItem value(const DomItem &binding) const;
+ void updatePathFromOwner(const Path &newPath);
private:
friend class Binding;
diff --git a/src/qmldom/qqmldomerrormessage.cpp b/src/qmldom/qqmldomerrormessage.cpp
index d4b95850e2..b48702822f 100644
--- a/src/qmldom/qqmldomerrormessage.cpp
+++ b/src/qmldom/qqmldomerrormessage.cpp
@@ -37,14 +37,14 @@ Every group has a unique string identifying it (the \l{groupId}), and it should
be translated to get the local name. The best way to acheive this is to create new groups using
the NewErrorGroup macro.
*/
-void ErrorGroup::dump(Sink sink) const
+void ErrorGroup::dump(const Sink &sink) const
{
sink(u"[");
sink(groupName());
sink(u"]");
}
-void ErrorGroup::dumpId(Sink sink) const
+void ErrorGroup::dumpId(const Sink &sink) const
{
sink(u"[");
sink(QString(groupId()));
@@ -70,13 +70,13 @@ The simplest way to create new ErrorMessages is to have an ErrorGroups instance,
and use it to create new ErrorMessages using its debug, warning, error,... methods
*/
-void ErrorGroups::dump(Sink sink) const
+void ErrorGroups::dump(const Sink &sink) const
{
for (int i = 0; i < groups.size(); ++i)
groups.at(i).dump(sink);
}
-void ErrorGroups::dumpId(Sink sink) const
+void ErrorGroups::dumpId(const Sink &sink) const
{
for (int i = 0; i < groups.size(); ++i)
groups.at(i).dumpId(sink);
@@ -138,14 +138,16 @@ errorHandler(ErrorMessage::load(QLatin1String("my.company.error1")));
The \l{withItem} method can be used to set the path file and location if not aready set.
*/
-ErrorMessage ErrorGroups::errorMessage(Dumper msg, ErrorLevel level, Path element, QString canonicalFilePath, SourceLocation location) const
+ErrorMessage ErrorGroups::errorMessage(
+ const Dumper &msg, ErrorLevel level, const Path &element, const QString &canonicalFilePath,
+ SourceLocation location) const
{
if (level == ErrorLevel::Fatal)
fatal(msg, element, canonicalFilePath, location);
return ErrorMessage(dumperToString(msg), *this, level, element, canonicalFilePath, location);
}
-ErrorMessage ErrorGroups::errorMessage(const DiagnosticMessage &msg, Path element, QString canonicalFilePath) const
+ErrorMessage ErrorGroups::errorMessage(const DiagnosticMessage &msg, const Path &element, const QString &canonicalFilePath) const
{
ErrorMessage res(*this, msg, element, canonicalFilePath);
if (res.location == SourceLocation()
@@ -156,7 +158,9 @@ ErrorMessage ErrorGroups::errorMessage(const DiagnosticMessage &msg, Path elemen
return res;
}
-void ErrorGroups::fatal(Dumper msg, Path element, QStringView canonicalFilePath, SourceLocation location) const
+void ErrorGroups::fatal(
+ const Dumper &msg, const Path &element, QStringView canonicalFilePath,
+ SourceLocation location) const
{
enum { FatalMsgMaxLen = 1023 };
char buf[FatalMsgMaxLen+1];
@@ -192,42 +196,42 @@ void ErrorGroups::fatal(Dumper msg, Path element, QStringView canonicalFilePath,
qFatal("%s", buf);
}
-ErrorMessage ErrorGroups::debug(QString message) const
+ErrorMessage ErrorGroups::debug(const QString &message) const
{
return ErrorMessage(message, *this, ErrorLevel::Debug);
}
-ErrorMessage ErrorGroups::debug(Dumper message) const
+ErrorMessage ErrorGroups::debug(const Dumper &message) const
{
return ErrorMessage(dumperToString(message), *this, ErrorLevel::Debug);
}
-ErrorMessage ErrorGroups::info(QString message) const
+ErrorMessage ErrorGroups::info(const QString &message) const
{
return ErrorMessage(message, *this, ErrorLevel::Info);
}
-ErrorMessage ErrorGroups::info(Dumper message) const
+ErrorMessage ErrorGroups::info(const Dumper &message) const
{
return ErrorMessage(dumperToString(message), *this, ErrorLevel::Info);
}
-ErrorMessage ErrorGroups::warning(QString message) const
+ErrorMessage ErrorGroups::warning(const QString &message) const
{
return ErrorMessage(message, *this, ErrorLevel::Warning);
}
-ErrorMessage ErrorGroups::warning(Dumper message) const
+ErrorMessage ErrorGroups::warning(const Dumper &message) const
{
return ErrorMessage(dumperToString(message), *this, ErrorLevel::Warning);
}
-ErrorMessage ErrorGroups::error(QString message) const
+ErrorMessage ErrorGroups::error(const QString &message) const
{
return ErrorMessage(message, *this, ErrorLevel::Error);
}
-ErrorMessage ErrorGroups::error(Dumper message) const
+ErrorMessage ErrorGroups::error(const Dumper &message) const
{
return ErrorMessage(dumperToString(message), *this, ErrorLevel::Error);
}
@@ -248,17 +252,31 @@ int ErrorGroups::cmp(const ErrorGroups &o1, const ErrorGroups &o2)
return 0;
}
-ErrorMessage::ErrorMessage(QString msg, ErrorGroups errorGroups, Level level, Path element, QString canonicalFilePath, SourceLocation location, QLatin1String errorId):
- errorId(errorId), message(msg), errorGroups(errorGroups), level(level), path(element), file(canonicalFilePath), location(location)
+ErrorMessage::ErrorMessage(
+ const QString &msg, const ErrorGroups &errorGroups, Level level, const Path &element,
+ const QString &canonicalFilePath, SourceLocation location, QLatin1String errorId)
+ : errorId(errorId)
+ , message(msg)
+ , errorGroups(errorGroups)
+ , level(level)
+ , path(element)
+ , file(canonicalFilePath)
+ , location(location)
{
if (level == Level::Fatal) // we should not end up here, it should have been handled at a higher level already
errorGroups.fatal(msg, element, canonicalFilePath, location);
}
-ErrorMessage::ErrorMessage(ErrorGroups errorGroups, const DiagnosticMessage &msg, Path element,
- QString canonicalFilePath, QLatin1String errorId):
- errorId(errorId), message(msg.message), errorGroups(errorGroups),
- level(errorLevelFromQtMsgType(msg.type)), path(element), file(canonicalFilePath), location(msg.loc)
+ErrorMessage::ErrorMessage(
+ const ErrorGroups &errorGroups, const DiagnosticMessage &msg, const Path &element,
+ const QString &canonicalFilePath, QLatin1String errorId)
+ : errorId(errorId)
+ , message(msg.message)
+ , errorGroups(errorGroups)
+ , level(errorLevelFromQtMsgType(msg.type))
+ , path(element)
+ , file(canonicalFilePath)
+ , location(msg.loc)
{
if (level == Level::Fatal) // we should not end up here, it should have been handled at a higher level already
errorGroups.fatal(msg.message, element, canonicalFilePath, location);
@@ -287,6 +305,10 @@ struct StorableMsg {
msg(e)
{}
+ StorableMsg(ErrorMessage &&e):
+ msg(std::move(e))
+ {}
+
ErrorMessage msg;
};
@@ -296,12 +318,12 @@ static QHash<QLatin1String, StorableMsg> &registry()
return r;
}
-QLatin1String ErrorMessage::msg(const char *errorId, ErrorMessage err)
+QLatin1String ErrorMessage::msg(const char *errorId, ErrorMessage &&err)
{
- return msg(QLatin1String(errorId), err);
+ return msg(QLatin1String(errorId), std::move(err));
}
-QLatin1String ErrorMessage::msg(QLatin1String errorId, ErrorMessage err)
+QLatin1String ErrorMessage::msg(QLatin1String errorId, ErrorMessage &&err)
{
bool doubleRegister = false;
ErrorMessage old = myErrors().debug(u"dummy");
@@ -312,14 +334,14 @@ QLatin1String ErrorMessage::msg(QLatin1String errorId, ErrorMessage err)
old = r[err.errorId].msg;
doubleRegister = true;
}
- r[errorId] = StorableMsg{err.withErrorId(errorId)};
+ r[errorId] = StorableMsg{std::move(err.withErrorId(errorId))};
}
if (doubleRegister)
defaultErrorHandler(myErrors().warning(tr("Double registration of error %1: (%2) vs (%3)").arg(errorId, err.withErrorId(errorId).toString(), old.toString())));
return errorId;
}
-void ErrorMessage::visitRegisteredMessages(function_ref<bool (ErrorMessage)> visitor)
+void ErrorMessage::visitRegisteredMessages(function_ref<bool(const ErrorMessage &)> visitor)
{
QHash<QLatin1String, StorableMsg> r;
{
@@ -336,7 +358,7 @@ void ErrorMessage::visitRegisteredMessages(function_ref<bool (ErrorMessage)> vis
ErrorMessage ErrorMessage::load(QLatin1String errorId)
{
- ErrorMessage res = myErrors().error([errorId](Sink s){
+ ErrorMessage res = myErrors().error([errorId](const Sink &s){
s(u"Unregistered error ");
s(QString(errorId)); });
{
@@ -363,7 +385,7 @@ ErrorMessage &ErrorMessage::withPath(const Path &path)
return *this;
}
-ErrorMessage &ErrorMessage::withFile(QString f)
+ErrorMessage &ErrorMessage::withFile(const QString &f)
{
file=f;
return *this;
@@ -381,15 +403,15 @@ ErrorMessage &ErrorMessage::withLocation(SourceLocation loc)
return *this;
}
-ErrorMessage &ErrorMessage::withItem(DomItem el)
+ErrorMessage &ErrorMessage::withItem(const DomItem &el)
{
if (path.length() == 0)
path = el.canonicalPath();
if (file.isEmpty())
file = el.canonicalFilePath();
if (location == SourceLocation()) {
- if (const FileLocations *fLocPtr = FileLocations::fileLocationsPtr(el)) {
- location = fLocPtr->regions.value(QString(), fLocPtr->fullRegion);
+ if (const FileLocations::Tree tree = FileLocations::treeOf(el)) {
+ location = FileLocations::region(tree, MainRegion);
}
}
return *this;
@@ -404,7 +426,7 @@ ErrorMessage ErrorMessage::handle(const ErrorHandler &errorHandler)
return *this;
}
-void ErrorMessage::dump(Sink sink) const
+void ErrorMessage::dump(const Sink &sink) const
{
if (!file.isEmpty()) {
sink(file);
@@ -436,7 +458,7 @@ void ErrorMessage::dump(Sink sink) const
QString ErrorMessage::toString() const
{
- return dumperToString([this](Sink sink){ this->dump(sink); });
+ return dumperToString([this](const Sink &sink){ this->dump(sink); });
}
QCborMap ErrorMessage::toCbor() const
@@ -463,7 +485,7 @@ QCborMap ErrorMessage::toCbor() const
*/
void errorToQDebug(const ErrorMessage &error)
{
- dumperToQDebug([&error](Sink s){ error.dump(s); }, error.level);
+ dumperToQDebug([&error](const Sink &s){ error.dump(s); }, error.level);
}
/*!
@@ -474,7 +496,7 @@ void silentError(const ErrorMessage &)
{
}
-void errorHandlerHandler(const ErrorMessage &msg, ErrorHandler *h = nullptr)
+void errorHandlerHandler(const ErrorMessage &msg, const ErrorHandler *h = nullptr)
{
static ErrorHandler handler = &errorToQDebug;
if (h) {
@@ -497,7 +519,7 @@ void defaultErrorHandler(const ErrorMessage &error)
* \internal
* \brief Sets the default error handler
*/
-void setDefaultErrorHandler(ErrorHandler h)
+void setDefaultErrorHandler(const ErrorHandler &h)
{
errorHandlerHandler(ErrorMessage(QString(), ErrorGroups({})), &h);
}
diff --git a/src/qmldom/qqmldomerrormessage_p.h b/src/qmldom/qqmldomerrormessage_p.h
index ad9c1d56ec..20e2d817e0 100644
--- a/src/qmldom/qqmldomerrormessage_p.h
+++ b/src/qmldom/qqmldomerrormessage_p.h
@@ -51,8 +51,8 @@ public:
{}
- void dump(Sink sink) const;
- void dumpId(Sink sink) const;
+ void dump(const Sink &sink) const;
+ void dumpId(const Sink &sink) const;
QLatin1String groupId() const;
QString groupName() const;
@@ -63,23 +63,28 @@ public:
class QMLDOM_EXPORT ErrorGroups{
Q_GADGET
public:
- void dump(Sink sink) const;
- void dumpId(Sink sink) const;
+ void dump(const Sink &sink) const;
+ void dumpId(const Sink &sink) const;
QCborArray toCbor() const;
- [[nodiscard]] ErrorMessage errorMessage(Dumper msg, ErrorLevel level, Path element = Path(), QString canonicalFilePath = QString(), SourceLocation location = SourceLocation()) const;
- [[nodiscard]] ErrorMessage errorMessage(const DiagnosticMessage &msg, Path element = Path(), QString canonicalFilePath = QString()) const;
-
- void fatal(Dumper msg, Path element = Path(), QStringView canonicalFilePath = u"", SourceLocation location = SourceLocation()) const;
-
- [[nodiscard]] ErrorMessage debug(QString message) const;
- [[nodiscard]] ErrorMessage debug(Dumper message) const;
- [[nodiscard]] ErrorMessage info(QString message) const;
- [[nodiscard]] ErrorMessage info(Dumper message) const;
- [[nodiscard]] ErrorMessage warning(QString message) const;
- [[nodiscard]] ErrorMessage warning(Dumper message) const;
- [[nodiscard]] ErrorMessage error(QString message) const;
- [[nodiscard]] ErrorMessage error(Dumper message) const;
+ [[nodiscard]] ErrorMessage errorMessage(
+ const Dumper &msg, ErrorLevel level, const Path &element = Path(),
+ const QString &canonicalFilePath = QString(), SourceLocation location = SourceLocation()) const;
+ [[nodiscard]] ErrorMessage errorMessage(
+ const DiagnosticMessage &msg, const Path &element = Path(),
+ const QString &canonicalFilePath = QString()) const;
+
+ void fatal(const Dumper &msg, const Path &element = Path(), QStringView canonicalFilePath = u"",
+ SourceLocation location = SourceLocation()) const;
+
+ [[nodiscard]] ErrorMessage debug(const QString &message) const;
+ [[nodiscard]] ErrorMessage debug(const Dumper &message) const;
+ [[nodiscard]] ErrorMessage info(const QString &message) const;
+ [[nodiscard]] ErrorMessage info(const Dumper &message) const;
+ [[nodiscard]] ErrorMessage warning(const QString &message) const;
+ [[nodiscard]] ErrorMessage warning(const Dumper &message) const;
+ [[nodiscard]] ErrorMessage error(const QString &message) const;
+ [[nodiscard]] ErrorMessage error(const Dumper &message) const;
static int cmp(const ErrorGroups &g1, const ErrorGroups &g2);
@@ -99,9 +104,9 @@ class QMLDOM_EXPORT ErrorMessage { // reuse Some of the other DiagnosticMessages
public:
using Level = ErrorLevel;
// error registry (usage is optional)
- static QLatin1String msg(const char *errorId, ErrorMessage err);
- static QLatin1String msg(QLatin1String errorId, ErrorMessage err);
- static void visitRegisteredMessages(function_ref<bool(ErrorMessage)> visitor);
+ static QLatin1String msg(const char *errorId, ErrorMessage &&err);
+ static QLatin1String msg(QLatin1String errorId, ErrorMessage &&err);
+ static void visitRegisteredMessages(function_ref<bool (const ErrorMessage &)> visitor);
[[nodiscard]] static ErrorMessage load(QLatin1String errorId);
[[nodiscard]] static ErrorMessage load(const char *errorId);
template<typename... T>
@@ -111,19 +116,24 @@ public:
return res;
}
- ErrorMessage(QString message, ErrorGroups errorGroups, Level level = Level::Warning, Path path = Path(), QString file = QString(), SourceLocation location = SourceLocation(), QLatin1String errorId = QLatin1String(""));
- ErrorMessage(ErrorGroups errorGroups, const DiagnosticMessage &msg, Path path = Path(), QString file = QString(), QLatin1String errorId = QLatin1String(""));
+ ErrorMessage(
+ const QString &message, const ErrorGroups &errorGroups, Level level = Level::Warning,
+ const Path &path = Path(), const QString &file = QString(),
+ SourceLocation location = SourceLocation(), QLatin1String errorId = QLatin1String(""));
+ ErrorMessage(
+ const ErrorGroups &errorGroups, const DiagnosticMessage &msg, const Path &path = Path(),
+ const QString &file = QString(), QLatin1String errorId = QLatin1String(""));
[[nodiscard]] ErrorMessage &withErrorId(QLatin1String errorId);
[[nodiscard]] ErrorMessage &withPath(const Path &);
- [[nodiscard]] ErrorMessage &withFile(QString);
+ [[nodiscard]] ErrorMessage &withFile(const QString &);
[[nodiscard]] ErrorMessage &withFile(QStringView);
[[nodiscard]] ErrorMessage &withLocation(SourceLocation);
- [[nodiscard]] ErrorMessage &withItem(DomItem);
+ [[nodiscard]] ErrorMessage &withItem(const DomItem &);
ErrorMessage handle(const ErrorHandler &errorHandler=nullptr);
- void dump(Sink s) const;
+ void dump(const Sink &s) const;
QString toString() const;
QCborMap toCbor() const;
friend int compare(const ErrorMessage &msg1, const ErrorMessage &msg2)
@@ -203,7 +213,7 @@ QMLDOM_EXPORT void silentError(const ErrorMessage &);
QMLDOM_EXPORT void errorToQDebug(const ErrorMessage &);
QMLDOM_EXPORT void defaultErrorHandler(const ErrorMessage &);
-QMLDOM_EXPORT void setDefaultErrorHandler(ErrorHandler h);
+QMLDOM_EXPORT void setDefaultErrorHandler(const ErrorHandler &h);
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomexternalitems.cpp b/src/qmldom/qqmldomexternalitems.cpp
index d46c258f94..6f48aa19e3 100644
--- a/src/qmldom/qqmldomexternalitems.cpp
+++ b/src/qmldom/qqmldomexternalitems.cpp
@@ -6,6 +6,7 @@
#include "qqmldomcomments_p.h"
#include "qqmldommock_p.h"
#include "qqmldomelements_p.h"
+#include "qqmldom_utils_p.h"
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
@@ -15,7 +16,6 @@
#include <QtCore/QDir>
#include <QtCore/QScopeGuard>
#include <QtCore/QFileInfo>
-#include <QtCore/QRegularExpression>
#include <QtCore/QRegularExpressionMatch>
#include <algorithm>
@@ -27,15 +27,16 @@ using namespace Qt::StringLiterals;
namespace QQmlJS {
namespace Dom {
-ExternalOwningItem::ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path path,
- int derivedFrom, QString code)
+ExternalOwningItem::ExternalOwningItem(
+ const QString &filePath, const QDateTime &lastDataUpdateAt, const Path &path,
+ int derivedFrom, const QString &code)
: OwningItem(derivedFrom, lastDataUpdateAt),
m_canonicalFilePath(filePath),
m_code(code),
m_path(path)
{}
-QString ExternalOwningItem::canonicalFilePath(DomItem &) const
+QString ExternalOwningItem::canonicalFilePath(const DomItem &) const
{
return m_canonicalFilePath;
}
@@ -45,7 +46,7 @@ QString ExternalOwningItem::canonicalFilePath() const
return m_canonicalFilePath;
}
-Path ExternalOwningItem::canonicalPath(DomItem &) const
+Path ExternalOwningItem::canonicalPath(const DomItem &) const
{
return m_path;
}
@@ -62,7 +63,7 @@ ErrorGroups QmldirFile::myParsingErrors()
return res;
}
-std::shared_ptr<QmldirFile> QmldirFile::fromPathAndCode(QString path, QString code)
+std::shared_ptr<QmldirFile> QmldirFile::fromPathAndCode(const QString &path, const QString &code)
{
QString canonicalFilePath = QFileInfo(path).canonicalFilePath();
@@ -158,8 +159,11 @@ void QmldirFile::setFromQmldir()
}
for (QQmlDirParser::Import const &imp : m_qmldir.dependencies()) {
QString uri = imp.module;
- if (imp.flags & QQmlDirParser::Import::Auto)
- qWarning() << "qmldir contains dependency with auto keyword";
+ if (imp.flags & QQmlDirParser::Import::Auto) {
+ qCDebug(QQmlJSDomImporting) << "QmldirFile::setFromQmlDir: ignoring initial version"
+ " 'auto' in depends command, using latest version"
+ " instead.";
+ }
Version v = Version(
(imp.version.hasMajorVersion() ? imp.version.majorVersion() : int(Version::Latest)),
(imp.version.hasMinorVersion() ? imp.version.minorVersion()
@@ -192,9 +196,9 @@ void QmldirFile::setFromQmldir()
bool hasErrors = false;
for (auto const &el : m_qmldir.errors(uri().toString())) {
ErrorMessage msg = myParsingErrors().errorMessage(el);
- addErrorLocal(msg);
if (msg.level == ErrorLevel::Error || msg.level == ErrorLevel::Fatal)
hasErrors = true;
+ addErrorLocal(std::move(msg));
}
setIsValid(!hasErrors); // consider it valid also with errors?
m_plugins = m_qmldir.plugins();
@@ -210,7 +214,7 @@ void QmldirFile::setAutoExports(const QList<ModuleAutoExport> &autoExport)
m_autoExports = autoExport;
}
-void QmldirFile::ensureInModuleIndex(DomItem &self, QString uri)
+void QmldirFile::ensureInModuleIndex(const DomItem &self, const QString &uri) const
{
// ModuleIndex keeps the various sources of types from a given module uri import
// this method ensures that all major versions that are contained in this qmldir
@@ -226,17 +230,17 @@ void QmldirFile::ensureInModuleIndex(DomItem &self, QString uri)
}
}
-QCborValue pluginData(QQmlDirParser::Plugin &pl, QStringList cNames)
+QCborValue pluginData(const QQmlDirParser::Plugin &pl, const QStringList &cNames)
{
QCborArray names;
- for (QString n : cNames)
+ for (const QString &n : cNames)
names.append(n);
return QCborMap({ { QCborValue(QStringView(Fields::name)), pl.name },
{ QStringView(Fields::path), pl.path },
{ QStringView(Fields::classNames), names } });
}
-bool QmldirFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool QmldirFile::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::uri, uri().toString());
@@ -248,8 +252,8 @@ bool QmldirFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
QStringList cNames = classNames();
return self.subListItem(List::fromQListRef<QQmlDirParser::Plugin>(
self.pathFromOwner().field(Fields::plugins), m_plugins,
- [cNames](DomItem &list, const PathEls::PathComponent &p,
- QQmlDirParser::Plugin &plugin) {
+ [cNames](const DomItem &list, const PathEls::PathComponent &p,
+ const QQmlDirParser::Plugin &plugin) {
return list.subDataItem(p, pluginData(plugin, cNames));
}));
});
@@ -259,7 +263,7 @@ bool QmldirFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
const QMap<QString, QString> typeFileMap = qmlFiles();
return self.subMapItem(Map(
self.pathFromOwner().field(Fields::qmlFiles),
- [typeFileMap](DomItem &map, QString typeV) {
+ [typeFileMap](const DomItem &map, const QString &typeV) {
QString path = typeFileMap.value(typeV);
if (path.isEmpty())
return DomItem();
@@ -268,7 +272,7 @@ bool QmldirFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
PathEls::Key(typeV),
QList<Path>({ Paths::qmlFileObjectPath(path) }));
},
- [typeFileMap](DomItem &) {
+ [typeFileMap](const DomItem &) {
return QSet<QString>(typeFileMap.keyBegin(), typeFileMap.keyEnd());
},
QStringLiteral(u"QList<Reference>")));
@@ -289,41 +293,172 @@ QMap<QString, QString> QmldirFile::qmlFiles() const
return res;
}
-std::shared_ptr<OwningItem> QmlFile::doCopy(DomItem &) const
+JsFile::JsFile(
+ const QString &filePath, const QString &code, const QDateTime &lastDataUpdateAt,
+ int derivedFrom)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlFilePath(filePath), derivedFrom,
+ code)
{
- auto res = std::make_shared<QmlFile>(*this);
+ m_engine = std::make_shared<QQmlJS::Engine>();
+ LegacyDirectivesCollector directivesCollector(*this);
+ m_engine->setDirectives(&directivesCollector);
+
+ QQmlJS::Lexer lexer(m_engine.get());
+ lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/false);
+ QQmlJS::Parser parser(m_engine.get());
+
+ bool isESM = filePath.endsWith(u".mjs", Qt::CaseInsensitive);
+ bool isValid = isESM ? parser.parseModule() : parser.parseProgram();
+ setIsValid(isValid);
+
+ const auto diagnostics = parser.diagnosticMessages();
+ for (const DiagnosticMessage &msg : diagnostics) {
+ addErrorLocal(
+ std::move(myParsingErrors().errorMessage(msg).withFile(filePath).withPath(m_path)));
+ }
+
+ auto astComments = std::make_shared<AstComments>(m_engine);
+
+ CommentCollector collector;
+ collector.collectComments(m_engine, parser.rootNode(), astComments);
+ m_script = std::make_shared<ScriptExpression>(code, m_engine, parser.rootNode(), astComments,
+ isESM ? ScriptExpression::ExpressionType::ESMCode
+ : ScriptExpression::ExpressionType::JSCode);
+}
+
+ErrorGroups JsFile::myParsingErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("JsFile"),
+ NewErrorGroup("Parsing") } };
return res;
}
-QmlFile::QmlFile(const QmlFile &o)
- : ExternalOwningItem(o),
- m_engine(o.m_engine),
- m_ast(o.m_ast),
- m_astComments(o.m_astComments),
- m_comments(o.m_comments),
- m_fileLocationsTree(o.m_fileLocationsTree),
- m_components(o.m_components),
- m_pragmas(o.m_pragmas),
- m_imports(o.m_imports),
- m_importScope(o.m_importScope)
+bool JsFile::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
- if (m_astComments)
- m_astComments = std::make_shared<AstComments>(*m_astComments);
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::fileLocationsTree, m_fileLocationsTree);
+ if (m_script)
+ cont = cont && self.dvItemField(visitor, Fields::expression, [this, &self]() {
+ return self.subOwnerItem(PathEls::Field(Fields::expression), m_script);
+ });
+ return cont;
+}
+
+void JsFile::writeOut(const DomItem &self, OutWriter &ow) const
+{
+ writeOutDirectives(ow);
+ ow.ensureNewline(2);
+ if (DomItem script = self.field(Fields::expression)) {
+ ow.ensureNewline();
+ script.writeOut(ow);
+ }
}
-QmlFile::QmlFile(QString filePath, QString code, QDateTime lastDataUpdateAt, int derivedFrom)
+void JsFile::addFileImport(const QString &jsfile, const QString &module)
+{
+ LegacyImport import;
+ import.fileName = jsfile;
+ import.asIdentifier = module;
+ m_imports.append(std::move(import));
+}
+
+void JsFile::addModuleImport(const QString &uri, const QString &version, const QString &module)
+{
+ LegacyImport import;
+ import.uri = uri;
+ import.version = version;
+ import.asIdentifier = module;
+ m_imports.append(std::move(import));
+}
+
+void JsFile::LegacyPragmaLibrary::writeOut(OutWriter &lw) const
+{
+ lw.write(u".pragma").space().write(u"library").ensureNewline();
+}
+
+void JsFile::LegacyImport::writeOut(OutWriter &lw) const
+{
+ // either filename or module uri must be present
+ Q_ASSERT(!fileName.isEmpty() || !uri.isEmpty());
+
+ lw.write(u".import").space();
+ if (!uri.isEmpty()) {
+ lw.write(uri).space();
+ if (!version.isEmpty()) {
+ lw.write(version).space();
+ }
+ } else {
+ lw.write(u"\"").write(fileName).write(u"\"").space();
+ }
+ lw.writeRegion(AsTokenRegion).space().write(asIdentifier);
+
+ lw.ensureNewline();
+}
+
+/*!
+ * \internal JsFile::writeOutDirectives
+ * \brief Performs writeOut of the .js Directives (.import, .pragma)
+ *
+ * Watch out!
+ * Currently directives in .js files do not have representative AST::Node-s (see QTBUG-119770),
+ * which makes it hard to preserve attached comments during the WriteOut process,
+ * because currently they are being attached to the first AST::Node.
+ * In case when the first AST::Node is absent, they are not collected, hence lost.
+ */
+void JsFile::writeOutDirectives(OutWriter &ow) const
+{
+ if (m_pragmaLibrary.has_value()) {
+ m_pragmaLibrary->writeOut(ow);
+ }
+ for (const auto &import : m_imports) {
+ import.writeOut(ow);
+ }
+}
+
+std::shared_ptr<OwningItem> QmlFile::doCopy(const DomItem &) const
+{
+ auto res = std::make_shared<QmlFile>(*this);
+ return res;
+}
+
+/*!
+ \class QmlFile
+
+ A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,
+ will be lazily constructed. That means that its member m_lazyMembers is uninitialized, and will
+ only be populated when it is accessed (through a getter, a setter or the DomItem interface).
+
+ The reason for the laziness is that the qqmljsscopes are created lazily and at the same time as
+ the Dom QmlFile representations. So instead of eagerly generating all qqmljsscopes when
+ constructing the Dom, the QmlFile itself becomes lazy and will only be populated on demand at
+ the same time as the corresponding qqmljsscopes.
+
+ The QDeferredFactory<QQmlJSScope> will, when the qqmljsscope is populated, take care of
+ populating all fields of the QmlFile.
+ Therefore, population of the QmlFile is done by populating the qqmljsscope.
+
+*/
+
+QmlFile::QmlFile(
+ const QString &filePath, const QString &code, const QDateTime &lastDataUpdateAt,
+ int derivedFrom, RecoveryOption option)
: ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlFilePath(filePath), derivedFrom,
code),
- m_engine(new QQmlJS::Engine),
- m_astComments(new AstComments(m_engine)),
- m_fileLocationsTree(FileLocations::createTree(canonicalPath()))
+ m_engine(new QQmlJS::Engine)
{
QQmlJS::Lexer lexer(m_engine.get());
lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/true);
QQmlJS::Parser parser(m_engine.get());
+ if (option == EnableParserRecovery) {
+ parser.setIdentifierInsertionEnabled(true);
+ parser.setIncompleteBindingsEnabled(true);
+ }
m_isValid = parser.parse();
- for (DiagnosticMessage msg : parser.diagnosticMessages())
- addErrorLocal(myParsingErrors().errorMessage(msg).withFile(filePath).withPath(m_path));
+ const auto diagnostics = parser.diagnosticMessages();
+ for (const DiagnosticMessage &msg : diagnostics) {
+ addErrorLocal(
+ std::move(myParsingErrors().errorMessage(msg).withFile(filePath).withPath(m_path)));
+ }
m_ast = parser.ast();
}
@@ -334,34 +469,38 @@ ErrorGroups QmlFile::myParsingErrors()
return res;
}
-bool QmlFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool QmlFile::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
+ auto &members = lazyMembers();
bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
- cont = cont && self.dvWrapField(visitor, Fields::components, m_components);
- cont = cont && self.dvWrapField(visitor, Fields::pragmas, m_pragmas);
- cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
- cont = cont && self.dvWrapField(visitor, Fields::importScope, m_importScope);
- cont = cont && self.dvWrapField(visitor, Fields::fileLocationsTree, m_fileLocationsTree);
- cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
- cont = cont && self.dvWrapField(visitor, Fields::astComments, m_astComments);
+ cont = cont && self.dvWrapField(visitor, Fields::components, members.m_components);
+ cont = cont && self.dvWrapField(visitor, Fields::pragmas, members.m_pragmas);
+ cont = cont && self.dvWrapField(visitor, Fields::imports, members.m_imports);
+ cont = cont && self.dvWrapField(visitor, Fields::importScope, members.m_importScope);
+ cont = cont
+ && self.dvWrapField(visitor, Fields::fileLocationsTree, members.m_fileLocationsTree);
+ cont = cont && self.dvWrapField(visitor, Fields::comments, members.m_comments);
+ cont = cont && self.dvWrapField(visitor, Fields::astComments, members.m_astComments);
return cont;
}
-DomItem QmlFile::field(DomItem &self, QStringView name)
+DomItem QmlFile::field(const DomItem &self, QStringView name) const
{
+ ensurePopulated();
if (name == Fields::components)
- return self.wrapField(Fields::components, m_components);
+ return self.wrapField(Fields::components, lazyMembers().m_components);
return DomBase::field(self, name);
}
-void QmlFile::addError(DomItem &self, ErrorMessage msg)
+void QmlFile::addError(const DomItem &self, ErrorMessage &&msg)
{
- self.containingObject().addError(msg);
+ self.containingObject().addError(std::move(msg));
}
-void QmlFile::writeOut(DomItem &self, OutWriter &ow) const
+void QmlFile::writeOut(const DomItem &self, OutWriter &ow) const
{
- for (DomItem &p : self.field(Fields::pragmas).values()) {
+ ensurePopulated();
+ for (const DomItem &p : self.field(Fields::pragmas).values()) {
p.writeOut(ow);
}
for (auto i : self.field(Fields::imports).values()) {
@@ -372,20 +511,20 @@ void QmlFile::writeOut(DomItem &self, OutWriter &ow) const
mainC.writeOut(ow);
}
-std::shared_ptr<OwningItem> GlobalScope::doCopy(DomItem &self) const
+std::shared_ptr<OwningItem> GlobalScope::doCopy(const DomItem &self) const
{
auto res = std::make_shared<GlobalScope>(
canonicalFilePath(self), lastDataUpdateAt(), revision());
return res;
}
-bool GlobalScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool GlobalScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
return cont;
}
-void QmltypesFile::ensureInModuleIndex(DomItem &self)
+void QmltypesFile::ensureInModuleIndex(const DomItem &self) const
{
auto it = m_uris.begin();
auto end = m_uris.end();
@@ -403,7 +542,7 @@ void QmltypesFile::ensureInModuleIndex(DomItem &self)
}
}
-bool QmltypesFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool QmltypesFile::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvWrapField(visitor, Fields::components, m_components);
@@ -411,30 +550,31 @@ bool QmltypesFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::uris, [this, &self]() {
return self.subMapItem(Map::fromMapRef<QSet<int>>(
self.pathFromOwner().field(Fields::uris), m_uris,
- [](DomItem &map, const PathEls::PathComponent &p, QSet<int> &el) {
+ [](const DomItem &map, const PathEls::PathComponent &p, const QSet<int> &el) {
QList<int> l(el.cbegin(), el.cend());
std::sort(l.begin(), l.end());
return map.subListItem(
List::fromQList<int>(map.pathFromOwner().appendComponent(p), l,
- [](DomItem &list, const PathEls::PathComponent &p,
- int &el) { return list.subDataItem(p, el); }));
+ [](const DomItem &list, const PathEls::PathComponent &p,
+ int el) { return list.subDataItem(p, el); }));
}));
});
cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
return cont;
}
-QmlDirectory::QmlDirectory(QString filePath, QStringList dirList, QDateTime lastDataUpdateAt,
- int derivedFrom)
+QmlDirectory::QmlDirectory(
+ const QString &filePath, const QStringList &dirList, const QDateTime &lastDataUpdateAt,
+ int derivedFrom)
: ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlDirectoryPath(filePath), derivedFrom,
dirList.join(QLatin1Char('\n')))
{
- for (QString f : dirList) {
+ for (const QString &f : dirList) {
addQmlFilePath(f);
}
}
-bool QmlDirectory::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool QmlDirectory::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
@@ -442,7 +582,7 @@ bool QmlDirectory::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
QDir baseDir(canonicalFilePath());
return self.subMapItem(Map(
self.pathFromOwner().field(Fields::qmlFiles),
- [this, baseDir](DomItem &map, QString key) -> DomItem {
+ [this, baseDir](const DomItem &map, const QString &key) -> DomItem {
QList<Path> res;
auto it = m_qmlFiles.find(key);
while (it != m_qmlFiles.end() && it.key() == key) {
@@ -452,7 +592,7 @@ bool QmlDirectory::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
}
return map.subReferencesItem(PathEls::Key(key), res);
},
- [this](DomItem &) {
+ [this](const DomItem &) {
auto keys = m_qmlFiles.keys();
return QSet<QString>(keys.begin(), keys.end());
},
@@ -461,11 +601,13 @@ bool QmlDirectory::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-bool QmlDirectory::addQmlFilePath(QString relativePath)
+bool QmlDirectory::addQmlFilePath(const QString &relativePath)
{
- QRegularExpression qmlFileRe(QRegularExpression::anchoredPattern(
- uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation))"));
- QRegularExpressionMatch m = qmlFileRe.match(relativePath);
+ static const QRegularExpression qmlFileRegularExpression{
+ QRegularExpression::anchoredPattern(
+ uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation|ui\.qml))")
+ };
+ QRegularExpressionMatch m = qmlFileRegularExpression.match(relativePath);
if (m.hasMatch() && !m_qmlFiles.values(m.captured(u"compName")).contains(relativePath)) {
m_qmlFiles.insert(m.captured(u"compName"), relativePath);
Export e;
diff --git a/src/qmldom/qqmldomexternalitems_p.h b/src/qmldom/qqmldomexternalitems_p.h
index 8072d2fa8a..1aa9d765cb 100644
--- a/src/qmldom/qqmldomexternalitems_p.h
+++ b/src/qmldom/qqmldomexternalitems_p.h
@@ -23,9 +23,12 @@
#include <QtQml/private/qqmljsast_p.h>
#include <QtQml/private/qqmljsengine_p.h>
#include <QtQml/private/qqmldirparser_p.h>
+#include <QtQmlCompiler/private/qqmljstyperesolver_p.h>
#include <QtCore/QMetaType>
+#include <QtCore/qregularexpression.h>
#include <limits>
+#include <memory>
Q_DECLARE_METATYPE(QQmlDirParser::Plugin)
@@ -46,14 +49,15 @@ Every owning item has a file or directory it refers to.
*/
class QMLDOM_EXPORT ExternalOwningItem: public OwningItem {
public:
- ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path pathFromTop,
- int derivedFrom = 0, QString code = QString());
+ ExternalOwningItem(
+ const QString &filePath, const QDateTime &lastDataUpdateAt, const Path &pathFromTop,
+ int derivedFrom = 0, const QString &code = QString());
ExternalOwningItem(const ExternalOwningItem &o) = default;
- QString canonicalFilePath(DomItem &) const override;
+ QString canonicalFilePath(const DomItem &) const override;
QString canonicalFilePath() const;
- Path canonicalPath(DomItem &) const override;
+ Path canonicalPath(const DomItem &) const override;
Path canonicalPath() const;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
{
bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueLazyField(visitor, Fields::canonicalFilePath, [this]() {
@@ -67,12 +71,12 @@ public:
return cont;
}
- bool iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor) override
+ bool iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor) override
{
bool cont = OwningItem::iterateSubOwners(self, visitor);
- cont = cont && self.field(Fields::components).visitKeys([visitor](QString, DomItem &comps) {
- return comps.visitIndexes([visitor](DomItem &comp) {
- return comp.field(Fields::objects).visitIndexes([visitor](DomItem &qmlObj) {
+ cont = cont && self.field(Fields::components).visitKeys([visitor](const QString &, const DomItem &comps) {
+ return comps.visitIndexes([visitor](const DomItem &comp) {
+ return comp.field(Fields::objects).visitIndexes([visitor](const DomItem &qmlObj) {
if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>())
return qmlObjPtr->iterateSubOwners(qmlObj, visitor);
Q_ASSERT(false);
@@ -104,7 +108,7 @@ protected:
class QMLDOM_EXPORT QmlDirectory final : public ExternalOwningItem
{
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
return std::make_shared<QmlDirectory>(*this);
}
@@ -112,23 +116,24 @@ protected:
public:
constexpr static DomType kindValue = DomType::QmlDirectory;
DomType kind() const override { return kindValue; }
- QmlDirectory(QString filePath = QString(), QStringList dirList = QStringList(),
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0);
+ QmlDirectory(
+ const QString &filePath = QString(), const QStringList &dirList = QStringList(),
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0);
QmlDirectory(const QmlDirectory &o) = default;
- std::shared_ptr<QmlDirectory> makeCopy(DomItem &self) const
+ std::shared_ptr<QmlDirectory> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<QmlDirectory>(doCopy(self));
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
const QMultiMap<QString, Export> &exports() const & { return m_exports; }
const QMultiMap<QString, QString> &qmlFiles() const & { return m_qmlFiles; }
- bool addQmlFilePath(QString relativePath);
+ bool addQmlFilePath(const QString &relativePath);
private:
QMultiMap<QString, Export> m_exports;
@@ -139,7 +144,7 @@ class QMLDOM_EXPORT QmldirFile final : public ExternalOwningItem
{
Q_DECLARE_TR_FUNCTIONS(QmldirFile)
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
auto copy = std::make_shared<QmldirFile>(*this);
return copy;
@@ -151,23 +156,24 @@ public:
static ErrorGroups myParsingErrors();
- QmldirFile(QString filePath = QString(), QString code = QString(),
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0)
+ QmldirFile(
+ const QString &filePath = QString(), const QString &code = QString(),
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0)
: ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmldirFilePath(filePath),
derivedFrom, code)
{
}
QmldirFile(const QmldirFile &o) = default;
- static std::shared_ptr<QmldirFile> fromPathAndCode(QString path, QString code);
+ static std::shared_ptr<QmldirFile> fromPathAndCode(const QString &path, const QString &code);
- std::shared_ptr<QmldirFile> makeCopy(DomItem &self) const
+ std::shared_ptr<QmldirFile> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<QmldirFile>(doCopy(self));
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
QmlUri uri() const { return m_uri; }
@@ -188,7 +194,7 @@ public:
QList<ModuleAutoExport> autoExports() const;
void setAutoExports(const QList<ModuleAutoExport> &autoExport);
- void ensureInModuleIndex(DomItem &self, QString uri);
+ void ensureInModuleIndex(const DomItem &self, const QString &uri) const;
private:
void parse();
@@ -207,7 +213,7 @@ private:
class QMLDOM_EXPORT JsFile final : public ExternalOwningItem
{
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
auto copy = std::make_shared<JsFile>(*this);
return copy;
@@ -216,129 +222,267 @@ protected:
public:
constexpr static DomType kindValue = DomType::JsFile;
DomType kind() const override { return kindValue; }
- JsFile(QString filePath = QString(),
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- Path pathFromTop = Path(), int derivedFrom = 0)
+ JsFile(const QString &filePath = QString(),
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ const Path &pathFromTop = Path(), int derivedFrom = 0)
: ExternalOwningItem(filePath, lastDataUpdateAt, pathFromTop, derivedFrom)
{
}
+ JsFile(const QString &filePath = QString(), const QString &code = QString(),
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0);
JsFile(const JsFile &o) = default;
- std::shared_ptr<JsFile> makeCopy(DomItem &self) const
+ std::shared_ptr<JsFile> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<JsFile>(doCopy(self));
}
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const
+ override; // iterates the *direct* subpaths, returns false if a quick end was requested
+
std::shared_ptr<QQmlJS::Engine> engine() const { return m_engine; }
JsResource rootComponent() const { return m_rootComponent; }
+ void setFileLocationsTree(const FileLocations::Tree &v) { m_fileLocationsTree = std::move(v); }
+
+ static ErrorGroups myParsingErrors();
+
+ void writeOut(const DomItem &self, OutWriter &lw) const override;
+ void setExpression(const std::shared_ptr<ScriptExpression> &script) { m_script = script; }
+
+ void initPragmaLibrary() { m_pragmaLibrary = LegacyPragmaLibrary{}; };
+ void addFileImport(const QString &jsfile, const QString &module);
+ void addModuleImport(const QString &uri, const QString &version, const QString &module);
+
+private:
+ void writeOutDirectives(OutWriter &lw) const;
+
+ /*
+ Entities with Legacy prefix are here to support formatting of the discouraged
+ .import, .pragma directives in .js files.
+ Taking into account that usage of these directives is discouraged and
+ the fact that current usecase is limited to the formatting of .js, it's arguably should not
+ be exposed and kept private.
+
+ LegacyPragma corresponds to the only one existing .pragma library
+
+ LegacyImport is capable of representing the following import statements:
+ .import T_STRING_LITERAL as T_IDENTIFIER
+ .import T_IDENTIFIER (. T_IDENTIFIER)* (T_VERSION_NUMBER (. T_VERSION_NUMBER)?)? as T_IDENTIFIER
+
+ LegacyDirectivesCollector is a workaround for collecting those directives.
+ At the moment of writing .import, .pragma in .js files do not have corresponding
+ representative AST::Node-s. Collecting of those is happening during the lexing
+ */
+
+ struct LegacyPragmaLibrary
+ {
+ void writeOut(OutWriter &lw) const;
+ };
+
+ struct LegacyImport
+ {
+ QString fileName; // file import
+ QString uri; // module import
+ QString version; // used for module import
+ QString asIdentifier; // .import ... as T_Identifier
+
+ void writeOut(OutWriter &lw) const;
+ };
+
+ class LegacyDirectivesCollector : public QQmlJS::Directives
+ {
+ public:
+ LegacyDirectivesCollector(JsFile &file) : m_file(file){};
+
+ void pragmaLibrary() override { m_file.initPragmaLibrary(); };
+ void importFile(const QString &jsfile, const QString &module, int, int) override
+ {
+ m_file.addFileImport(jsfile, module);
+ };
+ void importModule(const QString &uri, const QString &version, const QString &module, int,
+ int) override
+ {
+ m_file.addModuleImport(uri, version, module);
+ };
+
+ private:
+ JsFile &m_file;
+ };
private:
std::shared_ptr<QQmlJS::Engine> m_engine;
+ std::optional<LegacyPragmaLibrary> m_pragmaLibrary = std::nullopt;
+ QList<LegacyImport> m_imports;
+ std::shared_ptr<ScriptExpression> m_script;
JsResource m_rootComponent;
+ FileLocations::Tree m_fileLocationsTree;
};
class QMLDOM_EXPORT QmlFile final : public ExternalOwningItem
{
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
public:
constexpr static DomType kindValue = DomType::QmlFile;
DomType kind() const override { return kindValue; }
- QmlFile(const QmlFile &o);
- QmlFile(QString filePath = QString(), QString code = QString(),
- QDateTime lastDataUpdate = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0);
+ enum RecoveryOption { DisableParserRecovery, EnableParserRecovery };
+
+ QmlFile(const QString &filePath = QString(), const QString &code = QString(),
+ const QDateTime &lastDataUpdate = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0, RecoveryOption option = DisableParserRecovery);
static ErrorGroups myParsingErrors();
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor)
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const
override; // iterates the *direct* subpaths, returns false if a quick end was requested
- DomItem field(DomItem &self, QStringView name) const override
- {
- return const_cast<QmlFile *>(this)->field(self, name);
- }
- DomItem field(DomItem &self, QStringView name);
- std::shared_ptr<QmlFile> makeCopy(DomItem &self) const
+ DomItem field(const DomItem &self, QStringView name) const override;
+ std::shared_ptr<QmlFile> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<QmlFile>(doCopy(self));
}
- void addError(DomItem &self, ErrorMessage msg) override;
+ void addError(const DomItem &self, ErrorMessage &&msg) override;
- const QMultiMap<QString, QmlComponent> &components() const & { return m_components; }
+ const QMultiMap<QString, QmlComponent> &components() const &
+ {
+ return lazyMembers().m_components;
+ }
void setComponents(const QMultiMap<QString, QmlComponent> &components)
{
- m_components = components;
+ lazyMembers().m_components = components;
}
Path addComponent(const QmlComponent &component, AddOption option = AddOption::Overwrite,
QmlComponent **cPtr = nullptr)
{
QStringList nameEls = component.name().split(QChar::fromLatin1('.'));
QString key = nameEls.mid(1).join(QChar::fromLatin1('.'));
- return insertUpdatableElementInMultiMap(Path::Field(Fields::components), m_components, key,
- component, option, cPtr);
+ return insertUpdatableElementInMultiMap(Path::Field(Fields::components), lazyMembers().m_components,
+ key, component, option, cPtr);
}
- void writeOut(DomItem &self, OutWriter &lw) const override;
+ void writeOut(const DomItem &self, OutWriter &lw) const override;
AST::UiProgram *ast() const
{
return m_ast; // avoid making it public? would make moving away from it easier
}
- const QList<Import> &imports() const & { return m_imports; }
- void setImports(const QList<Import> &imports) { m_imports = imports; }
+ const QList<Import> &imports() const &
+ {
+ return lazyMembers().m_imports;
+ }
+ void setImports(const QList<Import> &imports) { lazyMembers().m_imports = imports; }
Path addImport(const Import &i)
{
- index_type idx = index_type(m_imports.size());
- m_imports.append(i);
+ auto &members = lazyMembers();
+ index_type idx = index_type(members.m_imports.size());
+ members.m_imports.append(i);
if (i.uri.isModule()) {
- m_importScope.addImport((i.importId.isEmpty()
- ? QStringList()
- : i.importId.split(QChar::fromLatin1('.'))),
- i.importedPath());
+ members.m_importScope.addImport((i.importId.isEmpty()
+ ? QStringList()
+ : i.importId.split(QChar::fromLatin1('.'))),
+ i.importedPath());
} else {
QString path = i.uri.absoluteLocalPath(canonicalFilePath());
if (!path.isEmpty())
- m_importScope.addImport((i.importId.isEmpty()
- ? QStringList()
- : i.importId.split(QChar::fromLatin1('.'))),
- Paths::qmlDirPath(path));
+ members.m_importScope.addImport(
+ (i.importId.isEmpty() ? QStringList()
+ : i.importId.split(QChar::fromLatin1('.'))),
+ Paths::qmlDirPath(path));
}
return Path::Field(Fields::imports).index(idx);
}
std::shared_ptr<QQmlJS::Engine> engine() const { return m_engine; }
- RegionComments &comments() { return m_comments; }
- std::shared_ptr<AstComments> astComments() const { return m_astComments; }
- void setAstComments(std::shared_ptr<AstComments> comm) { m_astComments = comm; }
- FileLocations::Tree fileLocationsTree() const { return m_fileLocationsTree; }
- void setFileLocationsTree(FileLocations::Tree v) { m_fileLocationsTree = v; }
- const QList<Pragma> &pragmas() const & { return m_pragmas; }
- void setPragmas(QList<Pragma> pragmas) { m_pragmas = pragmas; }
+ RegionComments &comments() { return lazyMembers().m_comments; }
+ std::shared_ptr<AstComments> astComments() const { return lazyMembers().m_astComments; }
+ void setAstComments(const std::shared_ptr<AstComments> &comm) { lazyMembers().m_astComments = comm; }
+ FileLocations::Tree fileLocationsTree() const { return lazyMembers().m_fileLocationsTree; }
+ void setFileLocationsTree(const FileLocations::Tree &v) { lazyMembers().m_fileLocationsTree = v; }
+ const QList<Pragma> &pragmas() const & { return lazyMembers().m_pragmas; }
+ void setPragmas(QList<Pragma> pragmas) { lazyMembers().m_pragmas = pragmas; }
Path addPragma(const Pragma &pragma)
{
- int idx = m_pragmas.size();
- m_pragmas.append(pragma);
+ auto &members = lazyMembers();
+ int idx = members.m_pragmas.size();
+ members.m_pragmas.append(pragma);
return Path::Field(Fields::pragmas).index(idx);
}
- ImportScope &importScope() { return m_importScope; }
- const ImportScope &importScope() const { return m_importScope; }
+ ImportScope &importScope() { return lazyMembers().m_importScope; }
+ const ImportScope &importScope() const { return lazyMembers().m_importScope; }
+
+ std::shared_ptr<QQmlJSTypeResolver> typeResolver() const
+ {
+ return lazyMembers().m_typeResolver;
+ }
+ void setTypeResolverWithDependencies(const std::shared_ptr<QQmlJSTypeResolver> &typeResolver,
+ const QQmlJSTypeResolverDependencies &dependencies)
+ {
+ auto &members = lazyMembers();
+ members.m_typeResolver = typeResolver;
+ members.m_typeResolverDependencies = dependencies;
+ }
+
+ DomCreationOptions creationOptions() const { return lazyMembers().m_creationOptions; }
+
+ void setHandleForPopulation(const QQmlJSScope::ConstPtr &scope)
+ {
+ m_handleForPopulation = scope;
+ }
+
private:
- friend class QmlDomAstCreator;
- std::shared_ptr<Engine> m_engine;
+ // The lazy parts of QmlFile are inside of QmlFileLazy.
+ struct QmlFileLazy
+ {
+ QmlFileLazy(FileLocations::Tree fileLocationsTree, AstComments *astComments)
+ : m_fileLocationsTree(fileLocationsTree), m_astComments(astComments)
+ {
+ }
+ RegionComments m_comments;
+ QMultiMap<QString, QmlComponent> m_components;
+ QList<Pragma> m_pragmas;
+ QList<Import> m_imports;
+ ImportScope m_importScope;
+ FileLocations::Tree m_fileLocationsTree;
+ std::shared_ptr<AstComments> m_astComments;
+ DomCreationOptions m_creationOptions;
+ std::shared_ptr<QQmlJSTypeResolver> m_typeResolver;
+ QQmlJSTypeResolverDependencies m_typeResolverDependencies;
+ };
+ friend class QQmlDomAstCreator;
AST::UiProgram *m_ast; // avoid? would make moving away from it easier
- std::shared_ptr<AstComments> m_astComments;
- RegionComments m_comments;
- FileLocations::Tree m_fileLocationsTree;
- QMultiMap<QString, QmlComponent> m_components;
- QList<Pragma> m_pragmas;
- QList<Import> m_imports;
- ImportScope m_importScope;
+ std::shared_ptr<Engine> m_engine;
+ QQmlJSScope::ConstPtr m_handleForPopulation;
+ mutable std::optional<QmlFileLazy> m_lazyMembers;
+
+ void ensurePopulated() const
+ {
+ if (m_lazyMembers)
+ return;
+
+ m_lazyMembers.emplace(FileLocations::createTree(canonicalPath()), new AstComments(m_engine));
+
+ // populate via the QQmlJSScope by accessing the (lazy) pointer
+ if (m_handleForPopulation.factory()) {
+ // silence no-discard attribute:
+ Q_UNUSED(m_handleForPopulation.data());
+ }
+ }
+ const QmlFileLazy &lazyMembers() const
+ {
+ ensurePopulated();
+ return *m_lazyMembers;
+ }
+ QmlFileLazy &lazyMembers()
+ {
+ ensurePopulated();
+ return *m_lazyMembers;
+ }
};
class QMLDOM_EXPORT QmltypesFile final : public ExternalOwningItem
{
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
auto res = std::make_shared<QmltypesFile>(*this);
return res;
@@ -348,9 +492,10 @@ public:
constexpr static DomType kindValue = DomType::QmltypesFile;
DomType kind() const override { return kindValue; }
- QmltypesFile(QString filePath = QString(), QString code = QString(),
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0)
+ QmltypesFile(
+ const QString &filePath = QString(), const QString &code = QString(),
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0)
: ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmltypesFilePath(filePath),
derivedFrom, code)
{
@@ -358,10 +503,10 @@ public:
QmltypesFile(const QmltypesFile &o) = default;
- void ensureInModuleIndex(DomItem &self);
+ void ensureInModuleIndex(const DomItem &self) const;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- std::shared_ptr<QmltypesFile> makeCopy(DomItem &self) const
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ std::shared_ptr<QmltypesFile> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<QmltypesFile>(doCopy(self));
}
@@ -392,7 +537,7 @@ public:
}
const QMap<QString, QSet<int>> &uris() const & { return m_uris; }
- void addUri(QString uri, int majorVersion)
+ void addUri(const QString &uri, int majorVersion)
{
QSet<int> &v = m_uris[uri];
if (!v.contains(majorVersion)) {
@@ -410,30 +555,31 @@ private:
class QMLDOM_EXPORT GlobalScope final : public ExternalOwningItem
{
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override;
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override;
public:
constexpr static DomType kindValue = DomType::GlobalScope;
DomType kind() const override { return kindValue; }
- GlobalScope(QString filePath = QString(),
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0)
+ GlobalScope(
+ const QString &filePath = QString(),
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0)
: ExternalOwningItem(filePath, lastDataUpdateAt, Paths::globalScopePath(filePath),
derivedFrom)
{
setIsValid(true);
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
- std::shared_ptr<GlobalScope> makeCopy(DomItem &self) const
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ std::shared_ptr<GlobalScope> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<GlobalScope>(doCopy(self));
}
QString name() const { return m_name; }
Language language() const { return m_language; }
GlobalComponent rootComponent() const { return m_rootComponent; }
- void setName(QString name) { m_name = name; }
+ void setName(const QString &name) { m_name = name; }
void setLanguage(Language language) { m_language = language; }
void setRootComponent(const GlobalComponent &ob)
{
diff --git a/src/qmldom/qqmldomfieldfilter.cpp b/src/qmldom/qqmldomfieldfilter.cpp
index 17529de4e9..67b33bbb7e 100644
--- a/src/qmldom/qqmldomfieldfilter.cpp
+++ b/src/qmldom/qqmldomfieldfilter.cpp
@@ -1,7 +1,9 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#include "qqmldomfieldfilter_p.h"
#include "qqmldompath_p.h"
+#include "qqmldomitem_p.h"
#include "QtCore/qglobal.h"
QT_BEGIN_NAMESPACE
@@ -61,7 +63,7 @@ QString FieldFilter::describeFieldsFilter() const
return fieldFilterStr;
}
-bool FieldFilter::operator()(DomItem &obj, Path p, DomItem &i) const
+bool FieldFilter::operator()(const DomItem &obj, const Path &p, const DomItem &i) const
{
if (p)
return this->operator()(obj, p.component(0), i);
@@ -69,7 +71,7 @@ bool FieldFilter::operator()(DomItem &obj, Path p, DomItem &i) const
return this->operator()(obj, PathEls::Empty(), i);
}
-bool FieldFilter::operator()(DomItem &base, const PathEls::PathComponent &c, DomItem &obj) const
+bool FieldFilter::operator()(const DomItem &base, const PathEls::PathComponent &c, const DomItem &obj) const
{
DomType baseK = base.internalKind();
if (c.kind() == Path::Kind::Field) {
@@ -95,7 +97,7 @@ bool FieldFilter::operator()(DomItem &base, const PathEls::PathComponent &c, Dom
return true;
}
-bool FieldFilter::addFilter(QString fFields)
+bool FieldFilter::addFilter(const QString &fFields)
{
// parses a base filter of the form <op><typeName>:<fieldName> or <op><fieldName>
// as described in this class documentation
@@ -119,6 +121,11 @@ bool FieldFilter::addFilter(QString fFields)
return true;
}
+FieldFilter FieldFilter::noFilter()
+{
+ return FieldFilter{ {}, {} };
+}
+
FieldFilter FieldFilter::defaultFilter()
{
QMultiMap<QString, QString> fieldFilterAdd { { QLatin1String("ScriptExpression"),
diff --git a/src/qmldom/qqmldomfieldfilter_p.h b/src/qmldom/qqmldomfieldfilter_p.h
index e40ce4459e..8ee5caa2c4 100644
--- a/src/qmldom/qqmldomfieldfilter_p.h
+++ b/src/qmldom/qqmldomfieldfilter_p.h
@@ -15,11 +15,13 @@
// We mean it.
//
+#include "qqmldom_fwd_p.h"
#include "qqmldom_global.h"
-#include "qqmldomitem_p.h"
-#include "qqmldomastcreator_p.h"
-#include "qqmldomcomments_p.h"
+#include "qqmldompath_p.h"
+#include <QtCore/qobject.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
#include <QtQml/private/qqmljsastvisitor_p.h>
QT_BEGIN_NAMESPACE
@@ -32,9 +34,10 @@ class QMLDOM_EXPORT FieldFilter
Q_GADGET
public:
QString describeFieldsFilter() const;
- bool addFilter(QString f);
- bool operator()(DomItem &, Path, DomItem &) const;
- bool operator()(DomItem &, const PathEls::PathComponent &c, DomItem &) const;
+ bool addFilter(const QString &f);
+ bool operator()(const DomItem &, const Path &, const DomItem &) const;
+ bool operator()(const DomItem &, const PathEls::PathComponent &c, const DomItem &) const;
+ static FieldFilter noFilter();
static FieldFilter defaultFilter();
static FieldFilter noLocationFilter();
static FieldFilter compareFilter();
diff --git a/src/qmldom/qqmldomfilewriter.cpp b/src/qmldom/qqmldomfilewriter.cpp
index f6fd650c13..82f2aca496 100644
--- a/src/qmldom/qqmldomfilewriter.cpp
+++ b/src/qmldom/qqmldomfilewriter.cpp
@@ -9,7 +9,7 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-FileWriter::Status FileWriter::write(QString tFile, function_ref<bool(QTextStream &)> write,
+FileWriter::Status FileWriter::write(const QString &tFile, function_ref<bool(QTextStream &)> write,
int nBk)
{
if (shouldRemoveTempFile)
diff --git a/src/qmldom/qqmldomfilewriter_p.h b/src/qmldom/qqmldomfilewriter_p.h
index 08a148ef5d..d9987f3f7a 100644
--- a/src/qmldom/qqmldomfilewriter_p.h
+++ b/src/qmldom/qqmldomfilewriter_p.h
@@ -37,14 +37,15 @@ public:
~FileWriter()
{
- if (!silentWarnings)
- for (QString w : warnings)
+ if (!silentWarnings) {
+ for (const QString &w : std::as_const(warnings))
qWarning() << w;
+ }
if (shouldRemoveTempFile)
tempFile.remove();
}
- Status write(QString targetFile, function_ref<bool(QTextStream &)> write, int nBk = 2);
+ Status write(const QString &targetFile, function_ref<bool(QTextStream &)> write, int nBk = 2);
bool shouldRemoveTempFile = false;
bool silentWarnings = false;
diff --git a/src/qmldom/qqmldomfunctionref_p.h b/src/qmldom/qqmldomfunctionref_p.h
index 65ef513ec2..525178e841 100644
--- a/src/qmldom/qqmldomfunctionref_p.h
+++ b/src/qmldom/qqmldomfunctionref_p.h
@@ -17,32 +17,44 @@
#include <QtCore/private/qglobal_p.h>
-#include <functional>
-// function_ref has been proposed for the C++20 standard, see
-// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0792r2.html
-// uses it if available, replace it with a const ref to std::function otherwise
-
-#ifndef __cpp_lib_function_ref
+#if !defined(Q_CC_MSVC) || Q_CC_MSVC >= 1930
+#include <QtCore/qxpfunctional.h>
QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
template <typename T>
-using function_ref = const std::function<T> &;
+using function_ref = qxp::function_ref<T>;
} // namespace Dom
} // namespace QQmlJS
QT_END_NAMESPACE
#else
+#include <functional>
+
QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-using std::function_ref;
+namespace _detail {
+template <typename T>
+struct function_ref_helper { using type = std::function<T>; };
+// std::function doesn't grok the const in <int(int) const>, so remove:
+template <typename R, typename...Args>
+struct function_ref_helper<R(Args...) const> : function_ref_helper<R(Args...)> {};
+// std::function doesn't grok the noexcept in <int(int) noexcept>, so remove:
+template <typename R, typename...Args>
+struct function_ref_helper<R(Args...) noexcept> : function_ref_helper<R(Args...)> {};
+// and both together:
+template <typename R, typename...Args>
+struct function_ref_helper<R(Args...) const noexcept> : function_ref_helper<R(Args...)> {};
+} // namespace _detail
+template <typename T>
+using function_ref = const typename _detail::function_ref_helper<T>::type &;
} // namespace Dom
} // namespace QQmlJS
QT_END_NAMESPACE
-#endif // __cpp_lib_function_ref
+#endif
#endif // QQMLDOMFUNCTIONREF_P_H
diff --git a/src/qmldom/qqmldomindentinglinewriter.cpp b/src/qmldom/qqmldomindentinglinewriter.cpp
index 734a56943f..99f81c1717 100644
--- a/src/qmldom/qqmldomindentinglinewriter.cpp
+++ b/src/qmldom/qqmldomindentinglinewriter.cpp
@@ -24,7 +24,7 @@ void IndentingLineWriter::willCommit()
m_preCachedStatus = fStatus().currentStatus;
}
-void IndentingLineWriter::reindentAndSplit(QString eol, bool eof)
+void IndentingLineWriter::reindentAndSplit(const QString &eol, bool eof)
{
bool shouldReindent = m_reindent;
indentAgain:
diff --git a/src/qmldom/qqmldomindentinglinewriter_p.h b/src/qmldom/qqmldomindentinglinewriter_p.h
index f066d370c9..63ec3fc432 100644
--- a/src/qmldom/qqmldomindentinglinewriter_p.h
+++ b/src/qmldom/qqmldomindentinglinewriter_p.h
@@ -31,7 +31,7 @@ QMLDOM_EXPORT class IndentingLineWriter : public LineWriter
{
Q_GADGET
public:
- IndentingLineWriter(SinkF innerSink, QString fileName,
+ IndentingLineWriter(const SinkF &innerSink, const QString &fileName,
const LineWriterOptions &options = LineWriterOptions(),
const FormatTextStatus &initialStatus = FormatTextStatus::initialStatus(),
int lineNr = 0, int columnNr = 0, int utf16Offset = 0,
@@ -40,7 +40,7 @@ public:
m_preCachedStatus(initialStatus)
{
}
- void reindentAndSplit(QString eol, bool eof = false) override;
+ void reindentAndSplit(const QString &eol, bool eof = false) override;
FormatPartialStatus &fStatus();
void lineChanged() override { m_fStatusValid = false; }
diff --git a/src/qmldom/qqmldomitem.cpp b/src/qmldom/qqmldomitem.cpp
index e3f871acf0..b885422c1d 100644
--- a/src/qmldom/qqmldomitem.cpp
+++ b/src/qmldom/qqmldomitem.cpp
@@ -1,5 +1,9 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qqmldomattachedinfo_p.h"
+#include "qqmldomconstants_p.h"
+#include "qqmldomitem_p.h"
+#include "qqmldompath_p.h"
#include "qqmldomtop_p.h"
#include "qqmldomelements_p.h"
#include "qqmldomexternalitems_p.h"
@@ -11,6 +15,8 @@
#include "qqmldomcompare_p.h"
#include "qqmldomastdumper_p.h"
#include "qqmldomlinewriter_p.h"
+#include "qqmldom_utils_p.h"
+#include "qqmldomscriptelements_p.h"
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
@@ -32,6 +38,8 @@
#include <QtCore/QScopeGuard>
#include <QtCore/QtGlobal>
#include <QtCore/QTimeZone>
+#include <optional>
+#include <type_traits>
QT_BEGIN_NAMESPACE
@@ -41,6 +49,37 @@ namespace Dom {
Q_LOGGING_CATEGORY(writeOutLog, "qt.qmldom.writeOut", QtWarningMsg);
static Q_LOGGING_CATEGORY(refLog, "qt.qmldom.ref", QtWarningMsg);
+template<class... TypeList>
+struct CheckDomElementT;
+
+template<class... Ts>
+struct CheckDomElementT<std::variant<Ts...>> : std::conjunction<IsInlineDom<Ts>...>
+{
+};
+
+/*!
+ \internal
+ \class QQmljs::Dom::ElementT
+
+ \brief A variant that contains all the Dom elements that an DomItem can contain.
+
+ Types in this variant are divided in two categories: normal Dom elements and internal Dom
+ elements.
+ The first ones are inheriting directly or indirectly from DomBase, and are the usual elements
+ that a DomItem can wrap around, like a QmlFile or an QmlObject. They should all appear in
+ ElementT as pointers, e.g. QmlFile*.
+ The internal Dom elements are a little bit special. They appear in ElementT without pointer, do
+ not inherit from DomBase \b{but} should behave like a smart DomBase-pointer. That is, they should
+ dereference as if they were a DomBase* pointing to a normal DomElement by implementing
+ operator->() and operator*().
+ Adding types here that are neither inheriting from DomBase nor implementing a smartpointer to
+ DomBase will throw compilation errors in the std::visit()-calls on this type.
+*/
+static_assert(CheckDomElementT<ElementT>::value,
+ "Types in ElementT must either be a pointer to a class inheriting "
+ "from DomBase or (for internal Dom structures) implement a smart "
+ "pointer pointing to a class inheriting from DomBase");
+
using std::shared_ptr;
/*!
\internal
@@ -59,17 +98,36 @@ The subclass *must* have a
\endcode
entry with its kind to enable casting usng the DomItem::as DomItem::ownerAs templates.
-The minimal overload set to be usable is:
+The minimal overload set to be usable consists of following methods:
+\list
+\li \c{kind()} returns the kind of the current element:
+\code
+ Kind kind() const override { return kindValue; }
+\endcode
+
+\li \c{pathFromOwner()} returns the path from the owner to the current element
\code
- Kind kind() const override { return kindValue; } // returns the kind of the current element
- Path pathFromOwner(DomItem &self) const override; // returns the path from the owner to the
-current element Path canonicalPath(DomItem &self) const override; // returns the path from virtual
-bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem)>) const = 0; // iterates
-the *direct* subpaths, returns false if a quick end was requested \endcode But you probably want to
-subclass either DomElement of OwningItem for your element. DomElement stores its pathFromOwner, and
-computes the canonicalPath from it and its owner. OwningItem is the unit for updates to the Dom
-model, exposed changes always change at least one OwningItem. They have their lifetime handled with
-shared_ptr and own (i.e. are responsible of freeing) other items in them.
+ Path pathFromOwner(const DomItem &self) const override;
+\endcode
+
+\li \c{canonicalPath()} returns the path
+\code
+ Path canonicalPath(const DomItem &self) const override;
+\endcode
+
+\li \c{iterateDirectSubpaths} iterates the *direct* subpaths/children and returns false if a quick
+end was requested:
+\code
+bool iterateDirectSubpaths(const DomItem &self, function_ref<bool(Path, DomItem)>) const = 0;
+\endcode
+
+\endlist
+
+But you probably want to subclass either \c DomElement or \c OwningItem for your element. \c
+DomElement stores its \c pathFromOwner, and computes the \c canonicalPath from it and its owner. \c
+OwningItem is the unit for updates to the Dom model, exposed changes always change at least one \c
+OwningItem. They have their lifetime handled with \c shared_ptr and own (i.e. are responsible of
+freeing) other items in them.
\sa QQml::Dom::DomItem, QQml::Dom::DomElement, QQml::Dom::OwningItem
*/
@@ -170,20 +228,7 @@ bool domTypeIsScope(DomType k)
}
}
-QCborValue locationToData(SourceLocation loc, QStringView strValue)
-{
- QCborMap res({
- {QStringLiteral(u"offset"), loc.offset},
- {QStringLiteral(u"length"), loc.length},
- {QStringLiteral(u"startLine"), loc.startLine},
- {QStringLiteral(u"startColumn"), loc.startColumn}
- });
- if (!strValue.isEmpty())
- res.insert(QStringLiteral(u"strValue"), QCborValue(strValue));
- return res;
-}
-
-QString DomBase::canonicalFilePath(DomItem &self) const
+QString DomBase::canonicalFilePath(const DomItem &self) const
{
auto parent = containingObject(self);
if (parent)
@@ -191,21 +236,21 @@ QString DomBase::canonicalFilePath(DomItem &self) const
return QString();
}
-void DomBase::writeOut(DomItem &self, OutWriter &) const
+void DomBase::writeOut(const DomItem &self, OutWriter &) const
{
qCWarning(writeOutLog) << "Ignoring unsupported writeOut for " << domTypeToString(kind()) << ":"
<< self.canonicalPath();
}
-ConstantData::ConstantData(Path pathFromOwner, QCborValue value, Options options)
+ConstantData::ConstantData(const Path &pathFromOwner, const QCborValue &value, Options options)
: DomElement(pathFromOwner), m_value(value), m_options(options)
{}
-bool ConstantData::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ConstantData::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
static QHash<QString, QString> knownFields;
static QBasicMutex m;
- auto toField = [](QString f) -> QStringView {
+ auto toField = [](const QString &f) -> QStringView {
QMutexLocker l(&m);
if (!knownFields.contains(f))
knownFields[f] = f;
@@ -302,6 +347,41 @@ It does not keep any pointers to internal elements, but rather the path to them,
it every time it needs.
*/
+FileToLoad::FileToLoad(const std::weak_ptr<DomEnvironment> &environment,
+ const QString &canonicalPath, const QString &logicalPath,
+ const std::optional<InMemoryContents> &content)
+ : m_environment(environment),
+ m_canonicalPath(canonicalPath),
+ m_logicalPath(logicalPath),
+ m_content(content)
+{
+}
+
+FileToLoad FileToLoad::fromMemory(const std::weak_ptr<DomEnvironment> &environment,
+ const QString &path, const QString &code)
+{
+ const QString canonicalPath = QFileInfo(path).canonicalFilePath();
+ return {
+ environment,
+ canonicalPath,
+ path,
+ InMemoryContents{ code },
+ };
+}
+
+FileToLoad FileToLoad::fromFileSystem(const std::weak_ptr<DomEnvironment> &environment,
+ const QString &path)
+{
+ // make the path canonical so the file content can be loaded from it later
+ const QString canonicalPath = QFileInfo(path).canonicalFilePath();
+ return {
+ environment,
+ canonicalPath,
+ path,
+ std::nullopt,
+ };
+}
+
ErrorGroup DomItem::domErrorGroup = NewErrorGroup("Dom");
DomItem DomItem::empty = DomItem();
@@ -317,7 +397,7 @@ ErrorGroups DomItem::myResolveErrors()
return res;
}
-Path DomItem::canonicalPath()
+Path DomItem::canonicalPath() const
{
Path res = visitEl([this](auto &&el) { return el->canonicalPath(*this); });
if (!(!res || res.headKind() == Path::Kind::Root)) {
@@ -328,14 +408,20 @@ Path DomItem::canonicalPath()
}
-DomItem DomItem::containingObject()
+DomItem DomItem::containingObject() const
{
return visitEl([this](auto &&el) { return el->containingObject(*this); });
}
-DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions)
+/*!
+ \internal
+ \brief Returns the QmlObject that this belongs to.
+
+ qmlObject() might also return the object of a component if GoTo:MostLikely is used.
+ */
+DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions) const
{
- if (DomItem res = filterUp([](DomType k, DomItem &) { return k == DomType::QmlObject; },
+ if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlObject; },
filterOptions))
return res;
if (options == GoTo::MostLikely) {
@@ -345,7 +431,7 @@ DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions)
return DomItem();
}
-DomItem DomItem::fileObject(GoTo options)
+DomItem DomItem::fileObject(GoTo options) const
{
DomItem res = *this;
DomType k = res.internalKind();
@@ -368,19 +454,12 @@ DomItem DomItem::fileObject(GoTo options)
return res;
}
-DomItem DomItem::rootQmlObject(GoTo options)
+DomItem DomItem::rootQmlObject(GoTo options) const
{
- if (DomItem res = filterUp([](DomType k, DomItem &) { return k == DomType::QmlObject; },
- FilterUpOptions::ReturnInner))
- return res;
- if (options == GoTo::MostLikely) {
- if (DomItem comp = component(options))
- return comp.field(Fields::objects).index(0);
- }
- return DomItem();
+ return qmlObject(options, FilterUpOptions::ReturnInner);
}
-DomItem DomItem::container()
+DomItem DomItem::container() const
{
Path path = pathFromOwner();
if (!path)
@@ -391,7 +470,7 @@ DomItem DomItem::container()
return containingObject();
}
-DomItem DomItem::globalScope()
+DomItem DomItem::globalScope() const
{
if (internalKind() == DomType::GlobalScope)
return *this;
@@ -403,23 +482,35 @@ DomItem DomItem::globalScope()
return DomItem();
}
-DomItem DomItem::owner()
+/*!
+ \internal
+ \brief The owner of an element, for an qmlObject this is the containing qml file.
+ */
+DomItem DomItem::owner() const
{
if (domTypeIsOwningItem(m_kind) || m_kind == DomType::Empty)
return *this;
- return std::visit(
- [this](auto &&el) { return DomItem(this->m_top, el, this->m_ownerPath, el.get()); },
- *m_owner);
+ return std::visit([this](auto &&el) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
+ return DomItem();
+ else
+ return DomItem(this->m_top, el, this->m_ownerPath, el.get());
+ }, m_owner);
}
-DomItem DomItem::top()
+DomItem DomItem::top() const
{
if (domTypeIsTopItem(m_kind) || m_kind == DomType::Empty)
return *this;
- return std::visit([](auto &&el) { return DomItem(el, el, Path(), el.get()); }, *m_top);
+ return std::visit([](auto &&el) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
+ return DomItem();
+ else
+ return DomItem(el, el, Path(), el.get());
+ }, m_top);
}
-DomItem DomItem::environment()
+DomItem DomItem::environment() const
{
DomItem res = top();
if (res.internalKind() == DomType::DomEnvironment)
@@ -427,7 +518,7 @@ DomItem DomItem::environment()
return DomItem(); // we are in the universe, and cannot go back to the environment...
}
-DomItem DomItem::universe()
+DomItem DomItem::universe() const
{
DomItem res = top();
if (res.internalKind() == DomType::DomUniverse)
@@ -437,91 +528,169 @@ DomItem DomItem::universe()
return DomItem(); // we should be in an empty DomItem already...
}
-DomItem DomItem::filterUp(function_ref<bool(DomType k, DomItem &)> filter, FilterUpOptions options)
+/*!
+ \internal
+ Shorthand to obtain the ScriptExpression DomItem, in which this DomItem is defined.
+ Returns an empty DomItem if the item is not defined inside a ScriptExpression.
+ \sa goToFile()
+ */
+DomItem DomItem::containingScriptExpression() const
+{
+ if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::ScriptExpression; },
+ FilterUpOptions::ReturnOuter))
+ return res;
+ return DomItem();
+}
+
+/*!
+ \internal
+ Shorthand to obtain the QmlFile DomItem, in which this DomItem is defined.
+ Returns an empty DomItem if the item is not defined in a QML file.
+ \sa goToFile()
+ */
+DomItem DomItem::containingFile() const
+{
+ if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlFile; },
+ FilterUpOptions::ReturnOuter))
+ return res;
+ return DomItem();
+}
+
+/*!
+ \internal
+ Shorthand to obtain the QmlFile DomItem from a canonicalPath.
+ \sa containingFile()
+ */
+DomItem DomItem::goToFile(const QString &canonicalPath) const
+{
+ Q_UNUSED(canonicalPath);
+ DomItem file =
+ top().field(Fields::qmlFileWithPath).key(canonicalPath).field(Fields::currentItem);
+ return file;
+}
+
+/*!
+ \internal
+ In the DomItem hierarchy, go \c n levels up.
+ */
+DomItem DomItem::goUp(int n) const
+{
+ Path path = canonicalPath();
+ // first entry of path is usually top(), and you cannot go up from top().
+ if (path.length() < n + 1)
+ return DomItem();
+
+ DomItem parent = top().path(path.dropTail(n));
+ return parent;
+}
+
+/*!
+ \internal
+ In the DomItem hierarchy, go 1 level up to get the direct parent.
+ */
+DomItem DomItem::directParent() const
{
- DomItem it = *this;
- DomType k = it.internalKind();
+ return goUp(1);
+}
+
+/*!
+\internal
+Finds the first element in the DomItem hierarchy that satisfies filter.
+Use options to set the search direction, see also \l{FilterUpOptions}.
+*/
+DomItem DomItem::filterUp(function_ref<bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const
+{
+ if (options == FilterUpOptions::ReturnOuter && filter(internalKind(), *this)) {
+ return *this;
+ }
+
switch (options) {
case FilterUpOptions::ReturnOuter:
case FilterUpOptions::ReturnOuterNoSelf: {
- bool checkTop = (options == FilterUpOptions::ReturnOuter);
- while (k != DomType::Empty) {
- if (checkTop && filter(k, it))
- return it;
- checkTop = true;
- if (!domTypeIsOwningItem(k)) {
- DomItem el = it.owner();
- DomItem res;
- k = DomType::Empty;
- Path pp = it.pathFromOwner();
- DomType k2 = el.internalKind();
- if (filter(k2, el)) {
- k = k2;
- res = el;
- }
- for (Path p : pp.mid(0, pp.length() - 1)) {
- el = el.path(p);
- DomType k2 = el.internalKind();
- if (filter(k2, el)) {
- k = k2;
- res = el;
- }
- }
- if (k != DomType::Empty)
- return res;
- it = it.owner();
+ for (DomItem current = *this, previous = DomItem(); current;
+ previous = current, current = current.directParent()) {
+ if (filter(current.internalKind(), current)) {
+ if (options != FilterUpOptions::ReturnOuterNoSelf || current != *this)
+ return current;
}
- it = it.containingObject();
- k = it.internalKind();
}
- } break;
+ break;
+ }
case FilterUpOptions::ReturnInner:
- while (k != DomType::Empty) {
- if (!domTypeIsOwningItem(k)) {
- DomItem el = owner();
- Path pp = pathFromOwner();
- for (Path p : pp) {
- DomItem child = el.path(p);
- DomType k2 = child.internalKind();
- if (filter(k2, child))
- return child;
- el = child;
- }
- it = it.owner();
- }
- it = it.containingObject();
- k = it.internalKind();
+ DomItem current = top();
+ for (const Path &currentPath : canonicalPath()) {
+ current = current.path(currentPath);
+ if (filter(current.internalKind(), current))
+ return current;
}
break;
}
+
return DomItem();
}
-DomItem DomItem::scope(FilterUpOptions options)
+DomItem DomItem::scope(FilterUpOptions options) const
{
- DomItem res = filterUp([](DomType, DomItem &el) { return el.isScope(); }, options);
+ DomItem res = filterUp([](DomType, const DomItem &el) { return el.isScope(); }, options);
return res;
}
-DomItem DomItem::get(ErrorHandler h, QList<Path> *visitedRefs)
+QQmlJSScope::ConstPtr DomItem::nearestSemanticScope() const
+{
+ QQmlJSScope::ConstPtr scope;
+ visitUp([&scope](const DomItem &item) {
+ scope = item.semanticScope();
+ return !scope; // stop when scope was true
+ });
+ return scope;
+}
+
+QQmlJSScope::ConstPtr DomItem::semanticScope() const
+{
+ QQmlJSScope::ConstPtr scope = std::visit(
+ [](auto &&e) -> QQmlJSScope::ConstPtr {
+ using T = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
+ if constexpr (std::is_same_v<T, const QmlObject *>) {
+ return e->semanticScope();
+ } else if constexpr (std::is_same_v<T, const QmlComponent *>) {
+ return e->semanticScope();
+ } else if constexpr (std::is_same_v<T, const QmltypesComponent *>) {
+ return e->semanticScope();
+ } else if constexpr (std::is_same_v<T, SimpleObjectWrap>) {
+ if (const MethodInfo *mi = e->template as<MethodInfo>()) {
+ return mi->semanticScope();
+ }
+ if (const auto *propertyDefinition = e->template as<PropertyDefinition>()) {
+ return propertyDefinition->semanticScope();
+ }
+ } else if constexpr (std::is_same_v<T, ScriptElementDomWrapper>) {
+ return e.element().base()->semanticScope();
+ }
+ return {};
+ },
+ m_element);
+ return scope;
+}
+
+DomItem DomItem::get(const ErrorHandler &h, QList<Path> *visitedRefs) const
{
if (const Reference *refPtr = as<Reference>())
return refPtr->get(*this, h, visitedRefs);
return DomItem();
}
-QList<DomItem> DomItem::getAll(ErrorHandler h, QList<Path> *visitedRefs)
+QList<DomItem> DomItem::getAll(const ErrorHandler &h, QList<Path> *visitedRefs) const
{
if (const Reference *refPtr = as<Reference>())
return refPtr->getAll(*this, h, visitedRefs);
return {};
}
-PropertyInfo DomItem::propertyInfoWithName(QString name)
+PropertyInfo DomItem::propertyInfoWithName(const QString &name) const
{
PropertyInfo pInfo;
- visitPrototypeChain([&pInfo, name](DomItem &obj) {
- return obj.visitLocalSymbolsNamed(name, [&pInfo, name](DomItem &el) {
+ visitPrototypeChain([&pInfo, name](const DomItem &obj) {
+ return obj.visitLocalSymbolsNamed(name, [&pInfo, name](const DomItem &el) {
switch (el.internalKind()) {
case DomType::Binding:
pInfo.bindings.append(el);
@@ -538,10 +707,10 @@ PropertyInfo DomItem::propertyInfoWithName(QString name)
return pInfo;
}
-QSet<QString> DomItem::propertyInfoNames()
+QSet<QString> DomItem::propertyInfoNames() const
{
QSet<QString> res;
- visitPrototypeChain([&res](DomItem &obj) {
+ visitPrototypeChain([&res](const DomItem &obj) {
res += obj.propertyDefs().keys();
res += obj.bindings().keys();
return true;
@@ -549,10 +718,10 @@ QSet<QString> DomItem::propertyInfoNames()
return res;
}
-DomItem DomItem::component(GoTo options)
+DomItem DomItem::component(GoTo options) const
{
if (DomItem res = filterUp(
- [](DomType kind, DomItem &) {
+ [](DomType kind, const DomItem &) {
return kind == DomType::QmlComponent || kind == DomType::QmltypesComponent
|| kind == DomType::GlobalComponent;
},
@@ -597,8 +766,8 @@ static QMap<LookupType, QString> lookupTypeToStringMap()
return map;
}
-bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHandler,
- ResolveOptions options, Path fullPath, QList<Path> *visitedRefs)
+bool DomItem::resolve(const Path &path, DomItem::Visitor visitor, const ErrorHandler &errorHandler,
+ ResolveOptions options, const Path &fullPath, QList<Path> *visitedRefs) const
{
QList<Path> vRefs;
Path fPath = fullPath;
@@ -635,13 +804,12 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
myResolveErrors().error(tr("Root context %1 is not known").arg(path.headName())).handle(errorHandler);
return false;
}
- toDos[0] = {root, 1};
+ toDos[0] = {std::move(root), 1};
} else {
toDos[0] = {*this, 0};
}
while (!toDos.isEmpty()) {
- auto toDo = toDos.last();
- toDos.removeLast();
+ const ResolveToDo toDo = toDos.takeLast();
{
auto idNow = toDo.item.id();
if (idNow == quintptr(0) && toDo.item == *this)
@@ -678,7 +846,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
}
if (visitedRefs->contains(refRef)) {
myResolveErrors()
- .error([visitedRefs, refRef](Sink sink) {
+ .error([visitedRefs, refRef](const Sink &sink) {
const QString msg = tr("Circular reference:") + QLatin1Char('\n');
sink(QStringView{msg});
for (const Path &vPath : *visitedRefs) {
@@ -695,7 +863,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
DomItem resolveRes;
it.resolve(
toResolve,
- [&resolveRes](Path, DomItem &r) {
+ [&resolveRes](Path, const DomItem &r) {
resolveRes = r;
return false;
},
@@ -732,11 +900,11 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
if (!branchExhausted)
visitTree(
Path(),
- [toFind, &toDos, iPath](Path, DomItem &item, bool) {
+ [&toFind, &toDos, iPath](Path, const DomItem &item, bool) {
// avoid non directly attached?
DomItem newItem = item[toFind];
if (newItem)
- toDos.append({ newItem, iPath });
+ toDos.append({ std::move(newItem), iPath });
return true;
},
VisitOption::VisitSelf | VisitOption::Recurse
@@ -760,7 +928,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
break;
case PathCurrent::ObjChain: {
bool cont = it.visitPrototypeChain(
- [&toDos, iPath](DomItem &subEl) {
+ [&toDos, iPath](const DomItem &subEl) {
toDos.append({ subEl, iPath });
return true;
},
@@ -773,7 +941,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
}
case PathCurrent::ScopeChain: {
bool cont = it.visitScopeChain(
- [&toDos, iPath](DomItem &subEl) {
+ [&toDos, iPath](const DomItem &subEl) {
toDos.append({ subEl, iPath });
return true;
},
@@ -903,7 +1071,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
}
it.visitLookup(
target,
- [&toDos, iPath](DomItem &subEl) {
+ [&toDos, iPath](const DomItem &subEl) {
toDos.append({ subEl, iPath });
return true;
},
@@ -917,7 +1085,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
case Path::Kind::Any:
visitTree(
Path(),
- [&toDos, iPath](Path, DomItem &item, bool) {
+ [&toDos, iPath](Path, const DomItem &item, bool) {
toDos.append({ item, iPath });
return true;
},
@@ -937,49 +1105,49 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan
return true;
}
-DomItem DomItem::path(Path p, ErrorHandler errorHandler)
+DomItem DomItem::path(const Path &p, const ErrorHandler &errorHandler) const
{
if (!p)
return *this;
DomItem res;
- resolve(p, [&res](Path, DomItem it) {
+ resolve(p, [&res](const Path &, const DomItem &it) {
res = it;
return false;
}, errorHandler);
return res;
}
-DomItem DomItem::path(QString p, ErrorHandler errorHandler)
+DomItem DomItem::path(const QString &p, const ErrorHandler &errorHandler) const
{
return path(Path::fromString(p, errorHandler));
}
-DomItem DomItem::path(QStringView p, ErrorHandler errorHandler)
+DomItem DomItem::path(QStringView p, const ErrorHandler &errorHandler) const
{
return path(Path::fromString(p, errorHandler));
}
-QList<QString> DomItem::fields()
+QList<QString> DomItem::fields() const
{
return visitEl([this](auto &&el) { return el->fields(*this); });
}
-DomItem DomItem::field(QStringView name)
+DomItem DomItem::field(QStringView name) const
{
return visitEl([this, name](auto &&el) { return el->field(*this, name); });
}
-index_type DomItem::indexes()
+index_type DomItem::indexes() const
{
return visitEl([this](auto &&el) { return el->indexes(*this); });
}
-DomItem DomItem::index(index_type i)
+DomItem DomItem::index(index_type i) const
{
return visitEl([this, i](auto &&el) { return el->index(*this, i); });
}
-bool DomItem::visitIndexes(function_ref<bool(DomItem &)> visitor)
+bool DomItem::visitIndexes(function_ref<bool(const DomItem &)> visitor) const
{
// use iterateDirectSubpathsConst instead?
int nIndexes = indexes();
@@ -991,12 +1159,12 @@ bool DomItem::visitIndexes(function_ref<bool(DomItem &)> visitor)
return true;
}
-QSet<QString> DomItem::keys()
+QSet<QString> DomItem::keys() const
{
return visitEl([this](auto &&el) { return el->keys(*this); });
}
-QStringList DomItem::sortedKeys()
+QStringList DomItem::sortedKeys() const
{
QSet<QString> ks = keys();
QStringList sortedKs(ks.begin(), ks.end());
@@ -1004,15 +1172,16 @@ QStringList DomItem::sortedKeys()
return sortedKs;
}
-DomItem DomItem::key(QString name)
+DomItem DomItem::key(const QString &name) const
{
return visitEl([this, name](auto &&el) { return el->key(*this, name); });
}
-bool DomItem::visitKeys(function_ref<bool(QString, DomItem &)> visitor)
+bool DomItem::visitKeys(function_ref<bool(const QString &, const DomItem &)> visitor) const
{
// use iterateDirectSubpathsConst instead?
- for (auto k : sortedKeys()) {
+ const QStringList keys = sortedKeys();
+ for (const QString &k : keys) {
DomItem v = key(k);
if (!visitor(k, v))
return false;
@@ -1020,10 +1189,10 @@ bool DomItem::visitKeys(function_ref<bool(QString, DomItem &)> visitor)
return true;
}
-QList<DomItem> DomItem::values()
+QList<DomItem> DomItem::values() const
{
QList<DomItem> res;
- visitEl([this, &res](auto &&el) {
+ visitEl([this, &res](const auto &el) {
return el->iterateDirectSubpathsConst(
*this, [&res](const PathEls::PathComponent &, function_ref<DomItem()> item) {
res.append(item());
@@ -1033,11 +1202,11 @@ QList<DomItem> DomItem::values()
return res;
}
-void DomItem::writeOutPre(OutWriter &ow)
+void DomItem::writeOutPre(OutWriter &ow) const
{
if (hasAnnotations()) {
DomItem anns = field(Fields::annotations);
- for (auto ann : anns.values()) {
+ for (const auto &ann : anns.values()) {
if (ann.annotations().indexes() == 0) {
ow.ensureNewline();
ann.writeOut(ow);
@@ -1052,172 +1221,198 @@ void DomItem::writeOutPre(OutWriter &ow)
ow.itemStart(*this);
}
-void DomItem::writeOut(OutWriter &ow)
+void DomItem::writeOut(OutWriter &ow) const
{
writeOutPre(ow);
visitEl([this, &ow](auto &&el) { el->writeOut(*this, ow); });
writeOutPost(ow);
}
-void DomItem::writeOutPost(OutWriter &ow)
+void DomItem::writeOutPost(OutWriter &ow) const
{
ow.itemEnd(*this);
}
-DomItem DomItem::writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks)
+DomItem::WriteOutCheckResult DomItem::performWriteOutChecks(const DomItem &original, const DomItem &reformatted,
+ OutWriter &ow,
+ WriteOutChecks extraChecks) const
+{
+ QStringList dumped;
+ auto maybeDump = [&ow, extraChecks, &dumped](const DomItem &obj, QStringView objName) {
+ QString objDumpPath;
+ if (extraChecks & WriteOutCheck::DumpOnFailure) {
+ objDumpPath = QDir(QDir::tempPath())
+ .filePath(objName.toString()
+ + QFileInfo(ow.lineWriter.fileName()).baseName()
+ + QLatin1String(".dump.json"));
+ obj.dump(objDumpPath);
+ dumped.append(objDumpPath);
+ }
+ return objDumpPath;
+ };
+ auto dumpedDumper = [&dumped](const Sink &s) {
+ if (dumped.isEmpty())
+ return;
+ s(u"\ndump: ");
+ for (const auto &dumpPath : dumped) {
+ s(u" ");
+ sinkEscaped(s, dumpPath);
+ }
+ };
+ auto compare = [&maybeDump, &dumpedDumper, this](const DomItem &obj1, QStringView obj1Name,
+ const DomItem &obj2, QStringView obj2Name,
+ const FieldFilter &filter) {
+ const auto diffList = domCompareStrList(obj1, obj2, filter, DomCompareStrList::AllDiffs);
+ if (!diffList.isEmpty()) {
+ maybeDump(obj1, obj1Name);
+ maybeDump(obj2, obj2Name);
+ qCWarning(writeOutLog).noquote().nospace()
+ << obj2Name << " writeOut of " << this->canonicalFilePath() << " has changes:\n"
+ << diffList.join(QString()) << dumpedDumper;
+ return false;
+ }
+ return true;
+ };
+ auto checkStability = [&maybeDump, &dumpedDumper, &dumped, &ow,
+ this](const QString &expected, const DomItem &obj, QStringView objName) {
+ LineWriter lw2([](QStringView) {}, ow.lineWriter.fileName(), ow.lineWriter.options());
+ OutWriter ow2(lw2);
+ ow2.indentNextlines = true;
+ obj.writeOut(ow2);
+ ow2.eof();
+ if (ow2.writtenStr != expected) {
+ DomItem fObj = this->fileObject();
+ maybeDump(fObj, u"initial");
+ maybeDump(obj, objName);
+ qCWarning(writeOutLog).noquote().nospace()
+ << objName << " non stable writeOut of " << this->canonicalFilePath() << ":"
+ << lineDiff(ow2.writtenStr, expected, 2) << dumpedDumper;
+ dumped.clear();
+ return false;
+ }
+ return true;
+ };
+
+ if ((extraChecks & WriteOutCheck::UpdatedDomCompare)
+ && !compare(original, u"initial", reformatted, u"reformatted",
+ FieldFilter::noLocationFilter()))
+ return WriteOutCheckResult::Failed;
+
+ if (extraChecks & WriteOutCheck::UpdatedDomStable) {
+ checkStability(ow.writtenStr, reformatted, u"reformatted");
+ }
+
+ if (extraChecks
+ & (WriteOutCheck::Reparse | WriteOutCheck::ReparseCompare | WriteOutCheck::ReparseStable)) {
+ DomItem newEnv = environment().makeCopy().item();
+ std::shared_ptr<DomEnvironment> newEnvPtr = newEnv.ownerAs<DomEnvironment>();
+ if (!newEnvPtr)
+ return WriteOutCheckResult::Failed;
+
+ auto newFilePtr = std::make_shared<QmlFile>(canonicalFilePath(), ow.writtenStr);
+ if (!newFilePtr)
+ return WriteOutCheckResult::Failed;
+ newEnvPtr->addQmlFile(newFilePtr, AddOption::Overwrite);
+
+ DomItem newFile = newEnv.copy(newFilePtr, Path());
+ if (newFilePtr->isValid()) {
+ if (extraChecks & (WriteOutCheck::ReparseCompare | WriteOutCheck::ReparseStable)) {
+ newEnvPtr->populateFromQmlFile(newFile);
+ if ((extraChecks & WriteOutCheck::ReparseCompare)
+ && !compare(reformatted, u"reformatted", newFile, u"reparsed",
+ FieldFilter::compareNoCommentsFilter()))
+ return WriteOutCheckResult::Failed;
+ if ((extraChecks & WriteOutCheck::ReparseStable))
+ checkStability(ow.writtenStr, newFile, u"reparsed");
+ }
+ } else {
+ const auto iterateErrors = [&newFile](const Sink &s) {
+ newFile.iterateErrors(
+ [s](const DomItem &, const ErrorMessage &msg) {
+ s(u"\n ");
+ msg.dump(s);
+ return true;
+ },
+ true);
+ s(u"\n"); // extra empty line at the end...
+ };
+ qCWarning(writeOutLog).noquote().nospace()
+ << "writeOut of " << canonicalFilePath()
+ << " created invalid code:\n----------\n"
+ << ow.writtenStr << "\n----------" << iterateErrors;
+ return WriteOutCheckResult::Failed;
+ }
+ }
+ return WriteOutCheckResult::Success;
+}
+
+/*!
+ \internal
+ Performes WriteOut of the FileItem and verifies the consistency of the DOM structure.
+
+ OutWriter is essentially a visitor traversing the DOM structure, starting from
+ the current item representing a FileItem.
+ While traversing it might be saving some intermediate information, used later for restoring
+ written out item. Restoration is needed to validate that the DOM structure of the written item
+ has not changed.
+*/
+bool DomItem::writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const
{
ow.indentNextlines = true;
writeOut(ow);
ow.eof();
- DomItem fObj = fileObject();
- DomItem copy = ow.updatedFile(fObj);
- if (extraChecks & WriteOutCheck::All) {
- QStringList dumped;
- auto maybeDump = [&ow, extraChecks, &dumped](DomItem &obj, QStringView objName) {
- QString objDumpPath;
- if (extraChecks & WriteOutCheck::DumpOnFailure) {
- objDumpPath = QDir(QDir::tempPath())
- .filePath(objName.toString()
- + QFileInfo(ow.lineWriter.fileName()).baseName()
- + QLatin1String(".dump.json"));
- obj.dump(objDumpPath);
- dumped.append(objDumpPath);
- }
- return objDumpPath;
- };
- auto dumpedDumper = [&dumped](Sink s) {
- if (dumped.isEmpty())
- return;
- s(u"\ndump: ");
- for (auto dumpPath : dumped) {
- s(u" ");
- sinkEscaped(s, dumpPath);
- }
- };
- auto compare = [&maybeDump, &dumpedDumper, this](DomItem &obj1, QStringView obj1Name,
- DomItem &obj2, QStringView obj2Name,
- const FieldFilter &filter) {
- if (!domCompareStrList(obj1, obj2, filter).isEmpty()) {
- maybeDump(obj1, obj1Name);
- maybeDump(obj2, obj2Name);
- qCWarning(writeOutLog).noquote().nospace()
- << obj2Name << " writeOut of " << this->canonicalFilePath()
- << " has changes:\n"
- << domCompareStrList(obj1, obj2, filter, DomCompareStrList::AllDiffs)
- .join(QString())
- << dumpedDumper;
- return false;
- }
- return true;
- };
- auto checkStability = [&maybeDump, &dumpedDumper, &dumped, &ow,
- this](QString expected, DomItem &obj, QStringView objName) {
- LineWriter lw2([](QStringView) {}, ow.lineWriter.fileName(), ow.lineWriter.options());
- OutWriter ow2(lw2);
- ow2.indentNextlines = true;
- obj.writeOut(ow2);
- ow2.eof();
- if (ow2.writtenStr != expected) {
- DomItem fObj = this->fileObject();
- maybeDump(fObj, u"initial");
- maybeDump(obj, objName);
- qCWarning(writeOutLog).noquote().nospace()
- << objName << " non stable writeOut of " << this->canonicalFilePath() << ":"
- << lineDiff(ow2.writtenStr, expected, 2) << dumpedDumper;
- dumped.clear();
- return false;
- }
- return true;
- };
- if ((extraChecks & WriteOutCheck::UpdatedDomCompare)
- && !compare(fObj, u"initial", copy, u"reformatted", FieldFilter::noLocationFilter()))
- return DomItem();
- if (extraChecks & WriteOutCheck::UpdatedDomStable)
- checkStability(ow.writtenStr, copy, u"reformatted");
- if (extraChecks
- & (WriteOutCheck::Reparse | WriteOutCheck::ReparseCompare
- | WriteOutCheck::ReparseStable)) {
- DomItem newEnv = environment().makeCopy().item();
- if (std::shared_ptr<DomEnvironment> newEnvPtr = newEnv.ownerAs<DomEnvironment>()) {
- auto newFilePtr = std::make_shared<QmlFile>(
- canonicalFilePath(), ow.writtenStr);
- newEnvPtr->addQmlFile(newFilePtr, AddOption::Overwrite);
- DomItem newFile = newEnv.copy(newFilePtr, Path());
- if (newFilePtr->isValid()) {
- if (extraChecks
- & (WriteOutCheck::ReparseCompare | WriteOutCheck::ReparseStable)) {
- MutableDomItem newFileMutable(newFile);
- createDom(newFileMutable);
- if ((extraChecks & WriteOutCheck::ReparseCompare)
- && !compare(copy, u"reformatted", newFile, u"reparsed",
- FieldFilter::compareNoCommentsFilter()))
- return DomItem();
- if ((extraChecks & WriteOutCheck::ReparseStable))
- checkStability(ow.writtenStr, newFile, u"reparsed");
- }
- } else {
- qCWarning(writeOutLog).noquote().nospace()
- << "writeOut of " << canonicalFilePath()
- << " created invalid code:\n----------\n"
- << ow.writtenStr << "\n----------" << [&newFile](Sink s) {
- newFile.iterateErrors(
- [s](DomItem, ErrorMessage msg) {
- s(u"\n ");
- msg.dump(s);
- return true;
- },
- true);
- s(u"\n"); // extra empty line at the end...
- };
- return DomItem();
- }
- }
- }
- }
- return copy;
+
+ auto currentFileItem = fileObject();
+ auto writtenFileItem = ow.restoreWrittenFileItem(currentFileItem);
+ WriteOutCheckResult result = WriteOutCheckResult::Success;
+ if (extraChecks & WriteOutCheck::All)
+ result = performWriteOutChecks(currentFileItem, writtenFileItem, ow, extraChecks);
+ return result == WriteOutCheckResult::Success ? bool(writtenFileItem) : false;
}
-DomItem DomItem::writeOut(QString path, int nBackups, const LineWriterOptions &options,
- FileWriter *fw, WriteOutChecks extraChecks)
+bool DomItem::writeOut(const QString &path, int nBackups, const LineWriterOptions &options,
+ FileWriter *fw, WriteOutChecks extraChecks) const
{
- DomItem res = *this;
- DomItem copy;
FileWriter localFw;
if (!fw)
fw = &localFw;
- switch (fw->write(
+ auto status = fw->write(
path,
- [this, path, &copy, &options, extraChecks](QTextStream &ts) {
+ [this, path, &options, extraChecks](QTextStream &ts) {
LineWriter lw([&ts](QStringView s) { ts << s; }, path, options);
OutWriter ow(lw);
- copy = writeOutForFile(ow, extraChecks);
- return bool(copy);
+ return writeOutForFile(ow, extraChecks);
},
- nBackups)) {
+ nBackups);
+ switch (status) {
+ case FileWriter::Status::DidWrite:
+ case FileWriter::Status::SkippedEqual:
+ return true;
case FileWriter::Status::ShouldWrite:
case FileWriter::Status::SkippedDueToFailure:
qCWarning(writeOutLog) << "failure reformatting " << path;
- break;
- case FileWriter::Status::DidWrite:
- case FileWriter::Status::SkippedEqual:
- res = copy;
- break;
+ return false;
+ default:
+ qCWarning(writeOutLog) << "Unknown FileWriter::Status ";
+ Q_ASSERT(false);
+ return false;
}
- return res;
}
-bool DomItem::isCanonicalChild(DomItem &item)
+bool DomItem::isCanonicalChild(const DomItem &item) const
{
+ bool isChild = false;
if (item.isOwningItem()) {
- return canonicalPath() == item.canonicalPath().dropTail();
+ isChild = canonicalPath() == item.canonicalPath().dropTail();
} else {
DomItem itemOw = item.owner();
DomItem selfOw = owner();
- return itemOw == selfOw && item.pathFromOwner().dropTail() == pathFromOwner();
+ isChild = itemOw == selfOw && item.pathFromOwner().dropTail() == pathFromOwner();
}
+ return isChild;
}
-bool DomItem::hasAnnotations()
+bool DomItem::hasAnnotations() const
{
bool hasAnnotations = false;
DomType iKind = internalKind();
@@ -1248,28 +1443,56 @@ bool DomItem::hasAnnotations()
return hasAnnotations;
}
-bool DomItem::visitTree(Path basePath, DomItem::ChildrenVisitor visitor, VisitOptions options,
- DomItem::ChildrenVisitor openingVisitor,
- DomItem::ChildrenVisitor closingVisitor)
+/*!
+ \internal
+ \brief Visits recursively all the children of this item using the given visitors.
+
+ First, the visitor is called and can continue or exit the visit by returning true or false.
+
+ Second, the openingVisitor is called and controls if the children of the current item needs to
+ be visited or not by returning true or false. In either case, the visitation of all the other
+ siblings is not affected. If both visitor and openingVisitor returned true, then the childrens of
+ the current item will be recursively visited.
+
+ Finally, after all the children were visited by visitor and openingVisitor, the closingVisitor
+ is called. Its return value is currently ignored.
+
+ Compared to the AST::Visitor*, openingVisitor and closingVisitor are called in the same order as
+ the visit() and endVisit()-calls.
+
+ Filtering allows to not visit certain part of the trees, and is checked before(!) the lazy child
+ is instantiated via its lambda. For example, visiting propertyInfos or defaultPropertyname takes
+ a lot of time because it resolves and collects all properties inherited from base types, and
+ might not even be relevant for the visitors.
+ */
+bool DomItem::visitTree(const Path &basePath, DomItem::ChildrenVisitor visitor,
+ VisitOptions options, DomItem::ChildrenVisitor openingVisitor,
+ DomItem::ChildrenVisitor closingVisitor, const FieldFilter &filter) const
{
if (!*this)
return true;
if (options & VisitOption::VisitSelf && !visitor(basePath, *this, true))
return false;
- if (!openingVisitor(basePath, *this, true))
+ if (options & VisitOption::VisitSelf && !openingVisitor(basePath, *this, true))
return true;
- auto atEnd = qScopeGuard(
- [closingVisitor, basePath, this]() { closingVisitor(basePath, *this, true); });
- return visitEl([this, basePath, visitor, openingVisitor, closingVisitor, options](auto &&el) {
+ auto atEnd = qScopeGuard([closingVisitor, basePath, this, options]() {
+ if (options & VisitOption::VisitSelf) {
+ closingVisitor(basePath, *this, true);
+ }
+ });
+ return visitEl([this, basePath, visitor, openingVisitor, closingVisitor, options,
+ &filter](auto &&el) {
return el->iterateDirectSubpathsConst(
*this,
- [this, basePath, visitor, openingVisitor, closingVisitor,
- options](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) {
+ [this, basePath, visitor, openingVisitor, closingVisitor, options,
+ &filter](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) {
Path pNow;
if (!(options & VisitOption::NoPath)) {
pNow = basePath;
pNow = pNow.appendComponent(c);
}
+ if (!filter(*this, c, DomItem{}))
+ return true;
DomItem item = itemF();
bool directChild = isCanonicalChild(item);
if (!directChild && !(options & VisitOption::VisitAdopted))
@@ -1285,16 +1508,81 @@ bool DomItem::visitTree(Path basePath, DomItem::ChildrenVisitor visitor, VisitOp
closingVisitor(pNow, item, directChild);
} else {
return item.visitTree(pNow, visitor, options | VisitOption::VisitSelf,
- openingVisitor, closingVisitor);
+ openingVisitor, closingVisitor, filter);
}
return true;
});
});
}
+static bool visitPrototypeIndex(QList<DomItem> &toDo, const DomItem &current,
+ const DomItem &derivedFromPrototype, const ErrorHandler &h,
+ QList<Path> *visitedRefs, VisitPrototypesOptions options,
+ const DomItem &prototype)
+{
+ Path elId = prototype.canonicalPath();
+ if (visitedRefs->contains(elId))
+ return true;
+ else
+ visitedRefs->append(elId);
+ QList<DomItem> protos = prototype.getAll(h, visitedRefs);
+ if (protos.isEmpty()) {
+ if (std::shared_ptr<DomEnvironment> envPtr =
+ derivedFromPrototype.environment().ownerAs<DomEnvironment>())
+ if (!(envPtr->options() & DomEnvironment::Option::NoDependencies))
+ derivedFromPrototype.myErrors()
+ .warning(derivedFromPrototype.tr("could not resolve prototype %1 (%2)")
+ .arg(current.canonicalPath().toString(),
+ prototype.field(Fields::referredObjectPath)
+ .value()
+ .toString()))
+ .withItem(derivedFromPrototype)
+ .handle(h);
+ } else {
+ if (protos.size() > 1) {
+ QStringList protoPaths;
+ for (const DomItem &p : protos)
+ protoPaths.append(p.canonicalPath().toString());
+ derivedFromPrototype.myErrors()
+ .warning(derivedFromPrototype
+ .tr("Multiple definitions found, using first only, resolving "
+ "prototype %1 (%2): %3")
+ .arg(current.canonicalPath().toString(),
+ prototype.field(Fields::referredObjectPath)
+ .value()
+ .toString(),
+ protoPaths.join(QLatin1String(", "))))
+ .withItem(derivedFromPrototype)
+ .handle(h);
+ }
+ int nProtos = 1; // change to protos.length() to use all prototypes
+ // (sloppier)
+ for (int i = nProtos; i != 0;) {
+ DomItem proto = protos.at(--i);
+ if (proto.internalKind() == DomType::Export) {
+ if (!(options & VisitPrototypesOption::ManualProceedToScope))
+ proto = proto.proceedToScope(h, visitedRefs);
+ toDo.append(proto);
+ } else if (proto.internalKind() == DomType::QmlObject
+ || proto.internalKind() == DomType::QmlComponent) {
+ toDo.append(proto);
+ } else {
+ derivedFromPrototype.myErrors()
+ .warning(derivedFromPrototype.tr("Unexpected prototype type %1 (%2)")
+ .arg(current.canonicalPath().toString(),
+ prototype.field(Fields::referredObjectPath)
+ .value()
+ .toString()))
+ .withItem(derivedFromPrototype)
+ .handle(h);
+ }
+ }
+ }
+ return true;
+}
-bool DomItem::visitPrototypeChain(function_ref<bool(DomItem &)> visitor,
- VisitPrototypesOptions options, ErrorHandler h,
- QSet<quintptr> *visited, QList<Path> *visitedRefs)
+bool DomItem::visitPrototypeChain(function_ref<bool(const DomItem &)> visitor,
+ VisitPrototypesOptions options, const ErrorHandler &h,
+ QSet<quintptr> *visited, QList<Path> *visitedRefs) const
{
QSet<quintptr> visitedLocal;
if (!visited)
@@ -1330,72 +1618,16 @@ bool DomItem::visitPrototypeChain(function_ref<bool(DomItem &)> visitor,
return false;
shouldVisit = true;
current.field(Fields::prototypes)
- .visitIndexes([&toDo, &current, this, &h, visitedRefs, options](DomItem &el) {
- Path elId = el.canonicalPath();
- if (visitedRefs->contains(elId))
- return true;
- else
- visitedRefs->append(elId);
- QList<DomItem> protos = el.getAll(h, visitedRefs);
- if (protos.isEmpty()) {
- if (std::shared_ptr<DomEnvironment> envPtr =
- environment().ownerAs<DomEnvironment>())
- if (!(envPtr->options() & DomEnvironment::Option::NoDependencies))
- myErrors()
- .warning(tr("could not resolve prototype %1 (%2)")
- .arg(current.canonicalPath().toString(),
- el.field(Fields::referredObjectPath)
- .value()
- .toString()))
- .withItem(*this)
- .handle(h);
- } else {
- if (protos.size() > 1) {
- QStringList protoPaths;
- for (DomItem &p : protos)
- protoPaths.append(p.canonicalPath().toString());
- myErrors()
- .warning(tr("Multiple definitions found, using first only, "
- "resolving prototype %1 (%2): %3")
- .arg(current.canonicalPath().toString(),
- el.field(Fields::referredObjectPath)
- .value()
- .toString(),
- protoPaths.join(QLatin1String(", "))))
- .withItem(*this)
- .handle(h);
- }
- int nProtos = 1; // change to protos.length() to us all prototypes found
- // (sloppier)
- for (int i = nProtos; i != 0;) {
- DomItem proto = protos.at(--i);
- if (proto.internalKind() == DomType::Export) {
- if (!(options & VisitPrototypesOption::ManualProceedToScope))
- proto = proto.proceedToScope(h, visitedRefs);
- toDo.append(proto);
- } else if (proto.internalKind() == DomType::QmlObject) {
- toDo.append(proto);
- } else {
- myErrors()
- .warning(tr("Unexpected prototype type %1 (%2)")
- .arg(current.canonicalPath().toString(),
- el.field(Fields::referredObjectPath)
- .value()
- .toString()))
- .withItem(*this)
- .handle(h);
- }
- }
- }
- return true;
+ .visitIndexes([&toDo, &current, this, &h, visitedRefs, options](const DomItem &el) {
+ return visitPrototypeIndex(toDo, current, *this, h, visitedRefs, options, el);
});
}
return true;
}
-bool DomItem::visitDirectAccessibleScopes(function_ref<bool(DomItem &)> visitor,
- VisitPrototypesOptions options, ErrorHandler h,
- QSet<quintptr> *visited, QList<Path> *visitedRefs)
+bool DomItem::visitDirectAccessibleScopes(
+ function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options,
+ const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
{
// these are the scopes one can access with the . operator from the current location
// but currently not the attached types, which we should
@@ -1440,9 +1672,9 @@ bool DomItem::visitDirectAccessibleScopes(function_ref<bool(DomItem &)> visitor,
* visit the values JS reaches accessing a type directly: the values if it is a singleton or the
* attached type
*/
-bool DomItem::visitStaticTypePrototypeChains(function_ref<bool(DomItem &)> visitor,
- VisitPrototypesOptions options, ErrorHandler h,
- QSet<quintptr> *visited, QList<Path> *visitedRefs)
+bool DomItem::visitStaticTypePrototypeChains(
+ function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options,
+ const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
{
QSet<quintptr> visitedLocal;
if (!visited)
@@ -1460,8 +1692,27 @@ bool DomItem::visitStaticTypePrototypeChains(function_ref<bool(DomItem &)> visit
return true;
}
-bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptions options,
- ErrorHandler h, QSet<quintptr> *visited, QList<Path> *visitedRefs)
+/*!
+ \brief Let the visitor visit the Dom Tree hierarchy of this DomItem.
+ */
+bool DomItem::visitUp(function_ref<bool(const DomItem &)> visitor) const
+{
+ Path p = canonicalPath();
+ while (p.length() > 0) {
+ DomItem current = top().path(p);
+ if (!visitor(current))
+ return false;
+ p = p.dropTail();
+ }
+ return true;
+}
+
+/*!
+ \brief Let the visitor visit the QML scope hierarchy of this DomItem.
+ */
+bool DomItem::visitScopeChain(
+ function_ref<bool(const DomItem &)> visitor, LookupOptions options, const ErrorHandler &h,
+ QSet<quintptr> *visited, QList<Path> *visitedRefs) const
{
QSet<quintptr> visitedLocal;
if (!visited)
@@ -1478,6 +1729,7 @@ bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptio
bool visitFirst = !(options & LookupOption::SkipFirstScope);
bool visitCurrent = visitFirst;
bool first = true;
+ QSet<quintptr> alreadyAddedComponentMaps;
while (!toDo.isEmpty()) {
DomItem current = toDo.takeLast();
if (visited->contains(current.id()))
@@ -1505,7 +1757,7 @@ bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptio
if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf))
toDo.append(next);
break;
- case DomType::QmlComponent: // ids/attached type
+ case DomType::QmlComponent: { // ids/attached type
if ((options & LookupOption::Strict) == 0) {
if (DomItem comp = current.field(Fields::nextComponent))
toDo.append(comp);
@@ -1517,8 +1769,25 @@ bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptio
}
if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf))
toDo.append(next);
+
+ DomItem owner = current.owner();
+ Path pathToComponentMap = current.pathFromOwner().dropTail(2);
+ DomItem componentMap = owner.path(pathToComponentMap);
+ if (alreadyAddedComponentMaps.contains(componentMap.id()))
+ break;
+ alreadyAddedComponentMaps.insert(componentMap.id());
+ const auto keys = componentMap.keys();
+ for (const QString &x : keys) {
+ DomItem componentList = componentMap.key(x);
+ for (int i = 0; i < componentList.indexes(); ++i) {
+ DomItem component = componentList.index(i);
+ if (component != current && !visited->contains(component.id()))
+ toDo.append(component);
+ }
+ }
first = false;
break;
+ }
case DomType::QmlFile: // subComponents, imported types
if (DomItem iScope =
current.field(Fields::importScope)) // treat file as a separate scope?
@@ -1558,118 +1827,14 @@ bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptio
return true;
}
-QSet<QString> DomItem::localSymbolNames(LocalSymbolsTypes typeFilter)
+bool DomItem::visitLookup1(
+ const QString &symbolName, function_ref<bool(const DomItem &)> visitor, LookupOptions opts,
+ const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
{
- QSet<QString> res;
- if (typeFilter == LocalSymbolsType::None)
- return res;
- switch (internalKind()) {
- case DomType::QmlObject:
- if (typeFilter & LocalSymbolsType::Attributes) {
- res += propertyDefs().keys();
- res += bindings().keys();
- }
- if (typeFilter & LocalSymbolsType::Methods) {
- if ((typeFilter & LocalSymbolsType::Methods) == LocalSymbolsType::Methods) {
- res += methods().keys();
- } else {
- bool shouldAddSignals = bool(typeFilter & LocalSymbolsType::Signals);
- if (const QmlObject *objPtr = as<QmlObject>()) {
- auto methods = objPtr->methods();
- for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
- if (bool(it.value().methodType == MethodInfo::MethodType::Signal)
- == shouldAddSignals)
- res += it.key();
- }
- }
- }
- }
- break;
- case DomType::ScriptExpression:
- // to do
- break;
- case DomType::QmlComponent:
- if (typeFilter & LocalSymbolsType::Ids)
- res += ids().keys();
- break;
- case DomType::QmlFile: // subComponents, imported types
- if (typeFilter & LocalSymbolsType::Components) {
- DomItem comps = field(Fields::components);
- for (auto k : comps.keys())
- if (!k.isEmpty())
- res.insert(k);
- }
- break;
- case DomType::ImportScope: {
- const ImportScope *currentPtr = as<ImportScope>();
- if (typeFilter & LocalSymbolsType::Types) {
- if ((typeFilter & LocalSymbolsType::Types) == LocalSymbolsType::Types) {
- res += currentPtr->importedNames(*this);
- } else {
- bool qmlTypes = bool(typeFilter & LocalSymbolsType::QmlTypes);
- for (const QString &typeName : currentPtr->importedNames(*this)) {
- if ((!typeName.isEmpty() && typeName.at(0).isUpper()) == qmlTypes)
- res += typeName;
- }
- }
- }
- if (typeFilter & LocalSymbolsType::Namespaces) {
- for (const auto &k : currentPtr->subImports().keys())
- res.insert(k);
- }
- break;
- }
- case DomType::QmltypesComponent:
- case DomType::JsResource:
- case DomType::GlobalComponent:
- if (typeFilter & LocalSymbolsType::Globals)
- res += enumerations().keys();
- break;
- case DomType::MethodInfo: {
- if (typeFilter & LocalSymbolsType::MethodParameters) {
- DomItem params = field(Fields::parameters);
- params.visitIndexes([&res](DomItem &p) {
- const MethodParameter *pPtr = p.as<MethodParameter>();
- res.insert(pPtr->name);
- return true;
- });
- }
- break;
- }
- default:
- break;
- }
- return res;
-}
-
-bool DomItem::visitLookup1(QString symbolName, function_ref<bool(DomItem &)> visitor,
- LookupOptions opts, ErrorHandler h, QSet<quintptr> *visited,
- QList<Path> *visitedRefs)
-{
- bool typeLookupInQmlFile = symbolName.size() > 1 && symbolName.at(0).isUpper()
- && fileObject().internalKind() == DomType::QmlFile;
- if (typeLookupInQmlFile) {
- // shortcut to lookup types (scope chain would find them too, but after looking
- // the prototype chain)
- DomItem importScope = fileObject().field(Fields::importScope);
- if (const ImportScope *importScopePtr = importScope.as<ImportScope>()) {
- if (importScopePtr->subImports().contains(symbolName)) {
- DomItem subItem = importScope.field(Fields::qualifiedImports).key(symbolName);
- if (!visitor(subItem))
- return false;
- }
- QList<DomItem> types = importScopePtr->importedItemsWithName(importScope, symbolName);
- for (DomItem &t : types) {
- if (!visitor(t))
- return false;
- }
- }
- return true;
- }
return visitScopeChain(
- [symbolName, visitor](DomItem &obj) {
+ [symbolName, visitor](const DomItem &obj) {
return obj.visitLocalSymbolsNamed(symbolName,
- [visitor](DomItem &el) { return visitor(el); });
+ [visitor](const DomItem &el) { return visitor(el); });
},
opts, h, visited, visitedRefs);
}
@@ -1680,7 +1845,7 @@ class CppTypeInfo
public:
CppTypeInfo() = default;
- static CppTypeInfo fromString(QStringView target, ErrorHandler h = nullptr)
+ static CppTypeInfo fromString(QStringView target, const ErrorHandler &h = nullptr)
{
CppTypeInfo res;
QRegularExpression reTarget = QRegularExpression(QRegularExpression::anchoredPattern(
@@ -1719,9 +1884,95 @@ public:
bool isList = false;
};
-bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor,
- LookupType lookupType, LookupOptions opts, ErrorHandler errorHandler,
- QSet<quintptr> *visited, QList<Path> *visitedRefs)
+static bool visitForLookupType(const DomItem &el, LookupType lookupType,
+ function_ref<bool(const DomItem &)> visitor)
+{
+ bool correctType = false;
+ DomType iType = el.internalKind();
+ switch (lookupType) {
+ case LookupType::Binding:
+ correctType = (iType == DomType::Binding);
+ break;
+ case LookupType::Method:
+ correctType = (iType == DomType::MethodInfo);
+ break;
+ case LookupType::Property:
+ correctType = (iType == DomType::PropertyDefinition || iType == DomType::Binding);
+ break;
+ case LookupType::PropertyDef:
+ correctType = (iType == DomType::PropertyDefinition);
+ break;
+ case LookupType::Type:
+ correctType = (iType == DomType::Export); // accept direct QmlObject ref?
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ if (correctType)
+ return visitor(el);
+ return true;
+}
+
+static bool visitQualifiedNameLookup(
+ const DomItem &newIt, const QStringList &subpath,
+ function_ref<bool(const DomItem &)> visitor, LookupType lookupType,
+ const ErrorHandler &errorHandler, QList<Path> *visitedRefs)
+{
+ QVector<ResolveToDo> lookupToDos(
+ { ResolveToDo{ newIt, 1 } }); // invariant: always increase pathIndex to guarantee
+ // end even with only partial visited match
+ QList<QSet<quintptr>> lookupVisited(subpath.size() + 1);
+ while (!lookupToDos.isEmpty()) {
+ ResolveToDo tNow = lookupToDos.takeFirst();
+ auto vNow = qMakePair(tNow.item.id(), tNow.pathIndex);
+ DomItem subNow = tNow.item;
+ int iSubPath = tNow.pathIndex;
+ Q_ASSERT(iSubPath < subpath.size());
+ QString subPathNow = subpath[iSubPath++];
+ DomItem scope = subNow.proceedToScope();
+ if (iSubPath < subpath.size()) {
+ if (vNow.first != 0) {
+ if (lookupVisited[vNow.second].contains(vNow.first))
+ continue;
+ else
+ lookupVisited[vNow.second].insert(vNow.first);
+ }
+ if (scope.internalKind() == DomType::QmlObject)
+ scope.visitDirectAccessibleScopes(
+ [&lookupToDos, &subPathNow, iSubPath](const DomItem &el) {
+ return el.visitLocalSymbolsNamed(
+ subPathNow, [&lookupToDos, iSubPath](const DomItem &subEl) {
+ lookupToDos.append({ subEl, iSubPath });
+ return true;
+ });
+ },
+ VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]),
+ visitedRefs);
+ } else {
+ bool cont = scope.visitDirectAccessibleScopes(
+ [&visitor, &subPathNow, lookupType](const DomItem &el) -> bool {
+ if (lookupType == LookupType::Symbol)
+ return el.visitLocalSymbolsNamed(subPathNow, visitor);
+ else
+ return el.visitLocalSymbolsNamed(
+ subPathNow, [lookupType, &visitor](const DomItem &el) -> bool {
+ return visitForLookupType(el, lookupType, visitor);
+ });
+ },
+ VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]),
+ visitedRefs);
+ if (!cont)
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DomItem::visitLookup(
+ const QString &target, function_ref<bool(const DomItem &)> visitor, LookupType lookupType,
+ LookupOptions opts, const ErrorHandler &errorHandler, QSet<quintptr> *visited,
+ QList<Path> *visitedRefs) const
{
if (target.isEmpty())
return true;
@@ -1738,101 +1989,10 @@ bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor,
} else {
return visitLookup1(
subpath.at(0),
- [&subpath, visitor, lookupType, &errorHandler, visitedRefs](DomItem &newIt) {
- QVector<ResolveToDo> lookupToDos({ ResolveToDo {
- newIt, 1 } }); // invariant: always increase pathIndex to guarantee
- // end even with only partial visited match
- QList<QSet<quintptr>> lookupVisited(subpath.size() + 1);
- while (!lookupToDos.isEmpty()) {
- ResolveToDo tNow = lookupToDos.takeFirst();
- auto vNow = qMakePair(tNow.item.id(), tNow.pathIndex);
- DomItem subNow = tNow.item;
- int iSubPath = tNow.pathIndex;
- Q_ASSERT(iSubPath < subpath.size());
- QString subPathNow = subpath[iSubPath++];
- DomItem scope = subNow.proceedToScope();
- if (iSubPath < subpath.size()) {
- if (vNow.first != 0) {
- if (lookupVisited[vNow.second].contains(vNow.first))
- continue;
- else
- lookupVisited[vNow.second].insert(vNow.first);
- }
- if (scope.internalKind() == DomType::QmlObject)
- scope.visitDirectAccessibleScopes(
- [&lookupToDos, subPathNow, iSubPath](DomItem &el) {
- return el.visitLocalSymbolsNamed(
- subPathNow,
- [&lookupToDos, iSubPath](DomItem &subEl) {
- lookupToDos.append({ subEl, iSubPath });
- return true;
- });
- },
- VisitPrototypesOption::Normal, errorHandler,
- &(lookupVisited[vNow.second]), visitedRefs);
- } else {
- bool cont = scope.visitDirectAccessibleScopes(
- [&visitor, subPathNow, lookupType](DomItem &el) -> bool {
- if (lookupType == LookupType::Symbol)
- return el.visitLocalSymbolsNamed(subPathNow,
- visitor);
- else
- return el.visitLocalSymbolsNamed(
- subPathNow,
- [lookupType,
- &visitor](DomItem &el) -> bool {
- bool correctType = false;
- DomType iType = el.internalKind();
- switch (lookupType) {
- case LookupType::Binding:
- correctType =
- (iType == DomType::Binding);
- break;
- case LookupType::Method:
- correctType =
- (iType
- == DomType::MethodInfo);
- break;
- case LookupType::Property:
- correctType =
- (iType
- == DomType::
- PropertyDefinition
- || iType
- == DomType::
- Binding);
- break;
- case LookupType::PropertyDef:
- correctType =
- (iType
- == DomType::
- PropertyDefinition);
- break;
- case LookupType::Type:
- correctType =
- (iType
- == DomType::
- Export); // accept
- // direct
- // QmlObject
- // ref?
- break;
- default:
- Q_ASSERT(false);
- break;
- }
- if (correctType)
- return visitor(el);
- return true;
- });
- },
- VisitPrototypesOption::Normal, errorHandler,
- &(lookupVisited[vNow.second]), visitedRefs);
- if (!cont)
- return false;
- }
- }
- return true;
+ [&subpath, visitor, lookupType, &errorHandler,
+ visitedRefs](const DomItem &newIt) -> bool {
+ return visitQualifiedNameLookup(newIt, subpath, visitor, lookupType,
+ errorHandler, visitedRefs);
},
opts, errorHandler, visited, visitedRefs);
}
@@ -1847,8 +2007,8 @@ bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor,
}
if (localQmltypes) {
if (DomItem localTypes = localQmltypes.field(Fields::components).key(baseTarget)) {
- bool cont = localTypes.visitIndexes([&visitor](DomItem &els) {
- return els.visitIndexes([&visitor](DomItem &el) {
+ bool cont = localTypes.visitIndexes([&visitor](const DomItem &els) {
+ return els.visitIndexes([&visitor](const DomItem &el) {
if (DomItem obj = el.field(Fields::objects).index(0))
return visitor(obj);
return true;
@@ -1859,10 +2019,10 @@ bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor,
}
}
DomItem qmltypes = environment().field(Fields::qmltypesFileWithPath);
- return qmltypes.visitKeys([baseTarget, &visitor](QString, DomItem &els) {
+ return qmltypes.visitKeys([baseTarget, &visitor](const QString &, const DomItem &els) {
DomItem comps =
els.field(Fields::currentItem).field(Fields::components).key(baseTarget);
- return comps.visitIndexes([&visitor](DomItem &el) {
+ return comps.visitIndexes([&visitor](const DomItem &el) {
if (DomItem obj = el.field(Fields::objects).index(0))
return visitor(obj);
return true;
@@ -1875,7 +2035,15 @@ bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor,
return true;
}
-DomItem DomItem::proceedToScope(ErrorHandler h, QList<Path> *visitedRefs)
+/*!
+ \internal
+ \brief Dereference DomItems pointing to other DomItems.
+
+ Dereferences DomItems with internalKind being References, Export and Id.
+ Also does multiple rounds of resolving for nested DomItems.
+ Prefer this over \l {DomItem::get}.
+ */
+DomItem DomItem::proceedToScope(const ErrorHandler &h, QList<Path> *visitedRefs) const
{
// follow references, resolve exports
DomItem current = *this;
@@ -1900,13 +2068,13 @@ DomItem DomItem::proceedToScope(ErrorHandler h, QList<Path> *visitedRefs)
return DomItem();
}
-QList<DomItem> DomItem::lookup(QString symbolName, LookupType type, LookupOptions opts,
- ErrorHandler errorHandler)
+QList<DomItem> DomItem::lookup(const QString &symbolName, LookupType type, LookupOptions opts,
+ const ErrorHandler &errorHandler) const
{
QList<DomItem> res;
visitLookup(
symbolName,
- [&res](DomItem &el) {
+ [&res](const DomItem &el) {
res.append(el);
return true;
},
@@ -1914,13 +2082,13 @@ QList<DomItem> DomItem::lookup(QString symbolName, LookupType type, LookupOption
return res;
}
-DomItem DomItem::lookupFirst(QString symbolName, LookupType type, LookupOptions opts,
- ErrorHandler errorHandler)
+DomItem DomItem::lookupFirst(const QString &symbolName, LookupType type, LookupOptions opts,
+ const ErrorHandler &errorHandler) const
{
DomItem res;
visitLookup(
symbolName,
- [&res](DomItem &el) {
+ [&res](const DomItem &el) {
res = el;
return false;
},
@@ -1928,75 +2096,81 @@ DomItem DomItem::lookupFirst(QString symbolName, LookupType type, LookupOptions
return res;
}
-quintptr DomItem::id()
+quintptr DomItem::id() const
{
return visitEl([](auto &&b) { return b->id(); });
}
-Path DomItem::pathFromOwner()
+Path DomItem::pathFromOwner() const
{
return visitEl([this](auto &&e) { return e->pathFromOwner(*this); });
}
-QString DomItem::canonicalFilePath()
+QString DomItem::canonicalFilePath() const
{
return visitEl([this](auto &&e) { return e->canonicalFilePath(*this); });
}
-DomItem DomItem::fileLocationsTree()
+DomItem DomItem::fileLocationsTree() const
{
if (DomItem l = field(Fields::fileLocationsTree))
return l;
- auto res = FileLocations::findAttachedInfo(*this, AttachedInfo::FindOption::SetFoundTreePath);
- if (res && res.foundTreePath.value()) {
- return copy(res.foundTree, res.foundTreePath.value());
+ auto res = FileLocations::findAttachedInfo(*this);
+ if (res && res.foundTreePath) {
+ return copy(res.foundTree, res.foundTreePath);
}
return DomItem();
}
-DomItem DomItem::fileLocations()
+DomItem DomItem::fileLocations() const
{
return fileLocationsTree().field(Fields::infoItem);
}
-MutableDomItem DomItem::makeCopy(DomItem::CopyOption option)
+MutableDomItem DomItem::makeCopy(DomItem::CopyOption option) const
{
if (m_kind == DomType::Empty)
return MutableDomItem();
DomItem o = owner();
if (option == CopyOption::EnvDisconnected) {
- DomItem newItem = std::visit(
- [this, &o](auto &&el) {
- auto copyPtr = el->makeCopy(o);
- return DomItem(m_top, copyPtr, m_ownerPath, copyPtr.get());
- },
- *m_owner);
+ DomItem newItem = std::visit([this, &o](auto &&el) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) {
+ return DomItem();
+ } else {
+ auto copyPtr = el->makeCopy(o);
+ return DomItem(m_top, copyPtr, m_ownerPath, copyPtr.get());
+ }
+ }, m_owner);
return MutableDomItem(newItem.path(pathFromOwner()));
}
DomItem env = environment();
std::shared_ptr<DomEnvironment> newEnvPtr;
if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
- newEnvPtr = std::make_shared<DomEnvironment>(
- envPtr, envPtr->loadPaths(), envPtr->options());
+ newEnvPtr = std::make_shared<DomEnvironment>(envPtr, envPtr->loadPaths(), envPtr->options(),
+ envPtr->domCreationOptions());
DomBase *eBase = envPtr.get();
- if (std::holds_alternative<DomEnvironment *>(m_element) && eBase
- && std::get<DomEnvironment *>(m_element) == eBase)
+ if (std::holds_alternative<const DomEnvironment *>(m_element) && eBase
+ && std::get<const DomEnvironment *>(m_element) == eBase)
return MutableDomItem(DomItem(newEnvPtr));
} else if (std::shared_ptr<DomUniverse> univPtr = top().ownerAs<DomUniverse>()) {
newEnvPtr = std::make_shared<DomEnvironment>(
QStringList(),
DomEnvironment::Option::SingleThreaded | DomEnvironment::Option::NoDependencies,
- univPtr);
+ DomCreationOption::None, univPtr);
} else {
Q_ASSERT(false);
return {};
}
DomItem newItem = std::visit(
[this, newEnvPtr, &o](auto &&el) {
- auto copyPtr = el->makeCopy(o);
- return DomItem(newEnvPtr, copyPtr, m_ownerPath, copyPtr.get());
+ if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) {
+ return DomItem();
+ } else {
+ auto copyPtr = el->makeCopy(o);
+ return DomItem(newEnvPtr, copyPtr, m_ownerPath, copyPtr.get());
+ }
},
- *m_owner);
+ m_owner);
switch (o.internalKind()) {
case DomType::QmlDirectory:
@@ -2037,7 +2211,7 @@ MutableDomItem DomItem::makeCopy(DomItem::CopyOption option)
return MutableDomItem(newItem.path(pathFromOwner()));
}
-bool DomItem::commitToBase(std::shared_ptr<DomEnvironment> validEnvPtr)
+bool DomItem::commitToBase(const std::shared_ptr<DomEnvironment> &validEnvPtr) const
{
DomItem env = environment();
if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
@@ -2046,7 +2220,7 @@ bool DomItem::commitToBase(std::shared_ptr<DomEnvironment> validEnvPtr)
return false;
}
-bool DomItem::visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)> visitor)
+bool DomItem::visitLocalSymbolsNamed(const QString &name, function_ref<bool(const DomItem &)> visitor) const
{
if (name.isEmpty()) // no empty symbol
return true;
@@ -2089,7 +2263,7 @@ bool DomItem::visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)>
break;
case DomType::MethodInfo: {
DomItem params = field(Fields::parameters);
- if (!params.visitIndexes([name, visitor](DomItem &p) {
+ if (!params.visitIndexes([name, visitor](const DomItem &p) {
const MethodParameter *pPtr = p.as<MethodParameter>();
if (pPtr->name == name && !visitor(p))
return false;
@@ -2123,33 +2297,31 @@ bool DomItem::visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)>
return true;
}
-DomItem DomItem::operator[](const QString &cName)
+DomItem DomItem::operator[](const QString &cName) const
{
if (internalKind() == DomType::Map)
return key(cName);
return field(cName);
}
-DomItem DomItem::operator[](QStringView cName)
+DomItem DomItem::operator[](QStringView cName) const
{
if (internalKind() == DomType::Map)
return key(cName.toString());
return field(cName);
}
-DomItem DomItem::operator[](Path p)
+DomItem DomItem::operator[](const Path &p) const
{
return path(p);
}
-QCborValue DomItem::value()
+QCborValue DomItem::value() const
{
- if (internalKind() == DomType::ConstantData)
- return std::get<ConstantData>(m_element).value();
- return QCborValue();
+ return base()->value();
}
-void DomItem::dumpPtr(Sink sink)
+void DomItem::dumpPtr(const Sink &sink) const
{
sink(u"DomItem{ topPtr:");
sink(QString::number((quintptr)topPtr().get(), 16));
@@ -2162,16 +2334,17 @@ void DomItem::dumpPtr(Sink sink)
sink(u"}");
}
-void DomItem::dump(Sink s, int indent,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter)
+void DomItem::dump(
+ const Sink &s, int indent,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
{
visitEl([this, s, indent, filter](auto &&e) { e->dump(*this, s, indent, filter); });
}
FileWriter::Status
-DomItem::dump(QString path,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter,
- int nBackups, int indent, FileWriter *fw)
+DomItem::dump(const QString &path,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter,
+ int nBackups, int indent, FileWriter *fw) const
{
FileWriter localFw;
if (!fw)
@@ -2194,122 +2367,140 @@ DomItem::dump(QString path,
return fw->status;
}
-QString DomItem::toString()
+QString DomItem::toString() const
{
- return dumperToString([this](Sink s){ dump(s); });
+ return dumperToString([this](const Sink &s){ dump(s); });
}
-int DomItem::derivedFrom()
+int DomItem::derivedFrom() const
{
- if (m_owner)
- return std::visit([](auto &&ow) { return ow->derivedFrom(); }, *m_owner);
- return 0;
+ return std::visit([](auto &&ow) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
+ return 0;
+ else
+ return ow->derivedFrom();
+ }, m_owner);
}
-int DomItem::revision()
+int DomItem::revision() const
{
- if (m_owner)
- return std::visit([](auto &&ow) { return ow->revision(); }, *m_owner);
- else
- return -1;
+ return std::visit([](auto &&ow) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
+ return -1;
+ else
+ return ow->revision();
+ }, m_owner);
}
-QDateTime DomItem::createdAt()
+QDateTime DomItem::createdAt() const
{
- if (m_owner)
- return std::visit([](auto &&ow) { return ow->createdAt(); }, *m_owner);
- else
- return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
+ return std::visit([](auto &&ow) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
+ return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
+ else
+ return ow->createdAt();
+ }, m_owner);
}
-QDateTime DomItem::frozenAt()
+QDateTime DomItem::frozenAt() const
{
- if (m_owner)
- return std::visit([](auto &&ow) { return ow->frozenAt(); }, *m_owner);
- else
- return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
+ return std::visit([](auto &&ow) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
+ return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
+ else
+ return ow->frozenAt();
+ }, m_owner);
}
-QDateTime DomItem::lastDataUpdateAt()
+QDateTime DomItem::lastDataUpdateAt() const
{
- if (m_owner)
- return std::visit([](auto &&ow) { return ow->lastDataUpdateAt(); }, *m_owner);
- else
- return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
+ return std::visit([](auto &&ow) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
+ return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
+ else
+ return ow->lastDataUpdateAt();
+ }, m_owner);
}
-void DomItem::addError(ErrorMessage msg)
+void DomItem::addError(ErrorMessage &&msg) const
{
- if (m_owner) {
- DomItem myOwner = owner();
- std::visit(
- [this, &myOwner, &msg](auto &&ow) { ow->addError(myOwner, msg.withItem(*this)); },
- *m_owner);
- } else
- defaultErrorHandler(msg.withItem(*this));
+ std::visit([this, &msg](auto &&ow) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
+ defaultErrorHandler(msg.withItem(*this));
+ else
+ ow->addError(owner(), std::move(msg.withItem(*this)));
+ }, m_owner);
}
-ErrorHandler DomItem::errorHandler()
+ErrorHandler DomItem::errorHandler() const
{
- DomItem self = *this;
- return [self](ErrorMessage m) mutable { self.addError(m); };
+ // We need a copy here. Error handlers may be called when this is gone.
+ return [self = *this](const ErrorMessage &m) { self.addError(ErrorMessage(m)); };
}
-void DomItem::clearErrors(ErrorGroups groups, bool iterate)
+void DomItem::clearErrors(const ErrorGroups &groups, bool iterate) const
{
- if (m_owner) {
- std::visit([&groups](auto &&ow) { ow->clearErrors(groups); }, *m_owner);
- if (iterate)
- iterateSubOwners([groups](DomItem i){
- i.clearErrors(groups, true);
- return true;
- });
+ std::visit([&groups](auto &&ow) {
+ if constexpr (!std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
+ ow->clearErrors(groups);
+ }, m_owner);
+
+ if (iterate) {
+ iterateSubOwners([groups](const DomItem &i){
+ i.clearErrors(groups, true);
+ return true;
+ });
}
}
-bool DomItem::iterateErrors(function_ref<bool(DomItem, ErrorMessage)> visitor, bool iterate,
- Path inPath)
+bool DomItem::iterateErrors(
+ function_ref<bool(const DomItem &, const ErrorMessage &)> visitor, bool iterate,
+ Path inPath) const
{
- if (m_owner) {
- DomItem ow = owner();
- if (!std::visit([&ow, visitor,
- inPath](auto &&el) { return el->iterateErrors(ow, visitor, inPath); },
- *m_owner))
- return false;
- if (iterate && !iterateSubOwners([inPath, visitor](DomItem &i) {
- return i.iterateErrors(visitor, true, inPath);
- }))
- return false;
+ if (!std::visit([this, visitor, inPath](auto &&el) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
+ return true;
+ else
+ return el->iterateErrors(owner(), visitor, inPath);
+ }, m_owner)) {
+ return false;
+ }
+
+ if (iterate && !iterateSubOwners([inPath, visitor](const DomItem &i) {
+ return i.iterateErrors(visitor, true, inPath);
+ })) {
+ return false;
}
+
return true;
}
-bool DomItem::iterateSubOwners(function_ref<bool(DomItem &)> visitor)
+bool DomItem::iterateSubOwners(function_ref<bool(const DomItem &)> visitor) const
{
- if (m_owner) {
- DomItem ow = owner();
- return std::visit([&ow, visitor](auto &&o) { return o->iterateSubOwners(ow, visitor); },
- *m_owner);
- }
- return true;
+ return std::visit([this, visitor](auto &&o) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(o)>, std::monostate>)
+ return true;
+ else
+ return o->iterateSubOwners(owner(), visitor);
+ }, m_owner);
}
-bool DomItem::iterateDirectSubpaths(DirectVisitor v)
+bool DomItem::iterateDirectSubpaths(DirectVisitor v) const
{
- return visitMutableEl(
- [this, v](auto &&el) mutable { return el->iterateDirectSubpaths(*this, v); });
+ return visitEl(
+ [this, v](auto &&el) { return el->iterateDirectSubpaths(*this, v); });
}
-DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, QList<Path> paths)
+DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, const QList<Path> &paths) const
{
return subListItem(
List::fromQList<Path>(pathFromOwner().appendComponent(c), paths,
- [](DomItem &list, const PathEls::PathComponent &p, Path &el) {
+ [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
return list.subReferenceItem(p, el);
}));
}
-DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, Path referencedObject)
+DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const
{
if (domTypeIsOwningItem(internalKind())) {
return DomItem(m_top, m_owner, m_ownerPath, Reference(referencedObject, Path(c)));
@@ -2319,126 +2510,45 @@ DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, Path referenc
}
}
-shared_ptr<DomTop> DomItem::topPtr()
-{
- if (m_top)
- return std::visit([](auto &&el) -> shared_ptr<DomTop> { return el; }, *m_top);
- return {};
-}
-
-shared_ptr<OwningItem> DomItem::owningItemPtr()
+shared_ptr<DomTop> DomItem::topPtr() const
{
- if (m_owner)
- return std::visit([](auto &&el) -> shared_ptr<OwningItem> { return el; }, *m_owner);
- return {};
+ return std::visit([](auto &&el) -> shared_ptr<DomTop> {
+ if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
+ return {};
+ else
+ return el;
+ }, m_top);
}
-const DomBase *DomItem::base()
+shared_ptr<OwningItem> DomItem::owningItemPtr() const
{
- return visitEl([](auto &&el) { return static_cast<const DomBase *>(&(*el)); });
+ return std::visit([](auto &&el) -> shared_ptr<OwningItem> {
+ if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
+ return {};
+ else
+ return el;
+ }, m_owner);
}
-DomBase *DomItem::mutableBase()
+/*!
+ \internal
+ Returns a pointer to the virtual base pointer to a DomBase.
+*/
+const DomBase *DomItem::base() const
{
- return visitMutableEl([](auto &&el) { return static_cast<DomBase *>(&(*el)); });
+ return visitEl([](auto &&el) -> const DomBase * { return el->domBase(); });
}
-DomItem::DomItem(std::shared_ptr<DomEnvironment> envPtr):
+DomItem::DomItem(const std::shared_ptr<DomEnvironment> &envPtr):
DomItem(envPtr, envPtr, Path(), envPtr.get())
{
}
-DomItem::DomItem(std::shared_ptr<DomUniverse> universePtr):
+DomItem::DomItem(const std::shared_ptr<DomUniverse> &universePtr):
DomItem(universePtr, universePtr, Path(), universePtr.get())
{
}
-void DomItem::loadFile(QString canonicalFilePath, QString logicalPath, QString code,
- QDateTime codeDate, DomTop::Callback callback, LoadOptions loadOptions,
- std::optional<DomType> fileType)
-{
- DomItem topEl = top();
- if (topEl.internalKind() == DomType::DomEnvironment
- || topEl.internalKind() == DomType::DomUniverse) {
- if (auto univ = topEl.ownerAs<DomUniverse>())
- univ->loadFile(*this, canonicalFilePath, logicalPath, code, codeDate, callback,
- loadOptions, fileType);
- else if (auto env = topEl.ownerAs<DomEnvironment>()) {
- if (env->options() & DomEnvironment::Option::NoDependencies)
- env->loadFile(topEl, canonicalFilePath, logicalPath, code, codeDate, callback,
- DomTop::Callback(), DomTop::Callback(), loadOptions, fileType);
- else
- env->loadFile(topEl, canonicalFilePath, logicalPath, code, codeDate,
- DomTop::Callback(), DomTop::Callback(), callback, loadOptions,
- fileType);
- } else
- Q_ASSERT(false && "expected either DomUniverse or DomEnvironment cast to succeed");
- } else {
- addError(myErrors().warning(tr("loadFile called without DomEnvironment or DomUniverse.")));
- callback(Paths::qmlFileInfoPath(canonicalFilePath), DomItem::empty, DomItem::empty);
- }
-}
-
-void DomItem::loadFile(QString filePath, QString logicalPath, DomTop::Callback callback,
- LoadOptions loadOptions, std::optional<DomType> fileType)
-{
- DomItem topEl = top();
- if (topEl.internalKind() == DomType::DomEnvironment
- || topEl.internalKind() == DomType::DomUniverse) {
- if (auto univ = topEl.ownerAs<DomUniverse>())
- univ->loadFile(*this, filePath, logicalPath, callback, loadOptions);
- else if (auto env = topEl.ownerAs<DomEnvironment>()) {
- if (env->options() & DomEnvironment::Option::NoDependencies)
- env->loadFile(topEl, filePath, logicalPath, callback, DomTop::Callback(),
- DomTop::Callback(), loadOptions, fileType);
- else
- env->loadFile(topEl, filePath, logicalPath, DomTop::Callback(), DomTop::Callback(),
- callback, loadOptions, fileType);
- } else
- Q_ASSERT(false && "expected either DomUniverse or DomEnvironment cast to succeed");
- } else {
- addError(myErrors().warning(tr("loadFile called without DomEnvironment or DomUniverse.")));
- callback(Paths::qmlFileInfoPath(filePath), DomItem::empty, DomItem::empty);
- }
-}
-
-void DomItem::loadModuleDependency(QString uri, Version version,
- std::function<void(Path, DomItem &, DomItem &)> callback,
- ErrorHandler errorHandler)
-{
- DomItem topEl = top();
- if (topEl.internalKind() == DomType::DomEnvironment) {
- if (auto envPtr = topEl.ownerAs<DomEnvironment>()) {
- if (envPtr->options() & DomEnvironment::Option::NoDependencies)
- envPtr->loadModuleDependency(topEl, uri, version, callback, nullptr, errorHandler);
- else
- envPtr->loadModuleDependency(topEl, uri, version, nullptr, callback, errorHandler);
- } else
- Q_ASSERT(false && "loadDependency expected the DomEnvironment cast to succeed");
- } else {
- addError(myErrors().warning(tr("loadModuleDependency called without DomEnvironment.")));
- callback(Paths::moduleScopePath(uri, version), DomItem::empty, DomItem::empty);
- }
-}
-
-void DomItem::loadBuiltins(std::function<void(Path, DomItem &, DomItem &)> callback, ErrorHandler h)
-{
- DomItem env = environment();
- if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>())
- envPtr->loadBuiltins(env, callback, h);
- else
- myErrors().error(tr("Cannot load builtins without DomEnvironment")).handle(h);
-}
-
-void DomItem::loadPendingDependencies()
-{
- DomItem env = environment();
- if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>())
- envPtr->loadPendingDependencies(env);
- else
- myErrors().error(tr("Called loadPendingDependencies without environment")).handle();
-}
-
/*!
\brief Creates a new document with the given code
@@ -2447,54 +2557,57 @@ The fileType should normally be QmlFile, but you might want to load a qmltypes f
example and interpret it as qmltypes file (not plain Qml), or as JsFile. In those case
set the file type accordingly.
*/
-DomItem DomItem::fromCode(QString code, DomType fileType)
+DomItem DomItem::fromCode(const QString &code, DomType fileType)
{
if (code.isEmpty())
return DomItem();
- DomItem env =
+ auto env =
DomEnvironment::create(QStringList(),
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
DomItem tFile;
- env.loadFile(
- QString(), QString(), code, QDateTime::currentDateTimeUtc(),
+
+ env->loadFile(
+ FileToLoad::fromMemory(env, QString(), code),
[&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad, fileType);
- env.loadPendingDependencies();
+ std::make_optional(fileType));
+ env->loadPendingDependencies();
return tFile.fileObject();
}
Empty::Empty()
{}
-Path Empty::pathFromOwner(DomItem &) const
+Path Empty::pathFromOwner(const DomItem &) const
{
return Path();
}
-Path Empty::canonicalPath(DomItem &) const
+Path Empty::canonicalPath(const DomItem &) const
{
return Path();
}
-bool Empty::iterateDirectSubpaths(DomItem &, DirectVisitor)
+bool Empty::iterateDirectSubpaths(const DomItem &, DirectVisitor) const
{
return true;
}
-DomItem Empty::containingObject(DomItem &self) const
+DomItem Empty::containingObject(const DomItem &self) const
{
return self;
}
-void Empty::dump(DomItem &, Sink s, int,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)>) const
+void Empty::dump(
+ const DomItem &, const Sink &s, int,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>) const
{
s(u"null");
}
-Map::Map(Path pathFromOwner, Map::LookupFunction lookup, Keys keys, QString targetType)
+Map::Map(const Path &pathFromOwner, const Map::LookupFunction &lookup,
+ const Keys &keys, const QString &targetType)
: DomElement(pathFromOwner), m_lookup(lookup), m_keys(keys), m_targetType(targetType)
{}
@@ -2503,31 +2616,31 @@ quintptr Map::id() const
return quintptr(0);
}
-bool Map::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool Map::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
QSet<QString> ksSet = keys(self);
QStringList ksList = QStringList(ksSet.begin(), ksSet.end());
std::sort(ksList.begin(), ksList.end());
- for (QString k : ksList) {
+ for (const QString &k : std::as_const(ksList)) {
if (!visitor(PathEls::Key(k), [&self, this, k]() { return key(self, k); }))
return false;
}
return true;
}
-const QSet<QString> Map::keys(DomItem &self) const
+const QSet<QString> Map::keys(const DomItem &self) const
{
return m_keys(self);
}
-DomItem Map::key(DomItem &self, QString name) const
+DomItem Map::key(const DomItem &self, const QString &name) const
{
return m_lookup(self, name);
}
void DomBase::dump(
- DomItem &self, Sink sink, int indent,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) const
+ const DomItem &self, const Sink &sink, int indent,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
{
bool comma = false;
DomKind dK = self.domKind();
@@ -2566,6 +2679,9 @@ void DomBase::dump(
case DomKind::Map:
sink(u"{");
break;
+ case DomKind::ScriptElement:
+ // nothing to print
+ break;
}
auto closeParens = qScopeGuard(
[dK, sink, indent]{
@@ -2586,6 +2702,9 @@ void DomBase::dump(
sinkNewline(sink, indent);
sink(u"}");
break;
+ case DomKind::ScriptElement:
+ // nothing to print
+ break;
}
});
index_type idx = 0;
@@ -2639,8 +2758,9 @@ void DomBase::dump(
});
}
-List::List(Path pathFromOwner, List::LookupFunction lookup, List::Length length,
- List::IteratorFunction iterator, QString elType):
+List::List(const Path &pathFromOwner, const List::LookupFunction &lookup,
+ const List::Length &length, const List::IteratorFunction &iterator,
+ const QString &elType):
DomElement(pathFromOwner), m_lookup(lookup), m_length(length), m_iterator(iterator),
m_elType(elType)
{}
@@ -2651,12 +2771,12 @@ quintptr List::id() const
}
void List::dump(
- DomItem &self, Sink sink, int indent,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) const
+ const DomItem &self, const Sink &sink, int indent,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
{
bool first = true;
sink(u"[");
- const_cast<List *>(this)->iterateDirectSubpaths(
+ iterateDirectSubpaths(
self,
[&self, indent, &first, sink, filter](const PathEls::PathComponent &c,
function_ref<DomItem()> itemF) {
@@ -2674,7 +2794,7 @@ void List::dump(
sink(u"]");
}
-bool List::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool List::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
if (m_iterator) {
return m_iterator(self, [visitor](index_type i, function_ref<DomItem()> itemF) {
@@ -2689,22 +2809,22 @@ bool List::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return true;
}
-index_type List::indexes(DomItem &self) const
+index_type List::indexes(const DomItem &self) const
{
return m_length(self);
}
-DomItem List::index(DomItem &self, index_type index) const
+DomItem List::index(const DomItem &self, index_type index) const
{
return m_lookup(self, index);
}
-void List::writeOut(DomItem &self, OutWriter &ow, bool compact) const
+void List::writeOut(const DomItem &self, OutWriter &ow, bool compact) const
{
- ow.writeRegion(u"leftSquareBrace", u"[");
+ ow.writeRegion(LeftBracketRegion);
int baseIndent = ow.increaseIndent(1);
bool first = true;
- const_cast<List *>(this)->iterateDirectSubpaths(
+ iterateDirectSubpaths(
self,
[&ow, &first, compact](const PathEls::PathComponent &, function_ref<DomItem()> elF) {
if (first)
@@ -2720,30 +2840,30 @@ void List::writeOut(DomItem &self, OutWriter &ow, bool compact) const
if (!compact && !first)
ow.newline();
ow.decreaseIndent(1, baseIndent);
- ow.writeRegion(u"rightSquareBrace", u"]");
+ ow.writeRegion(RightBracketRegion);
}
-DomElement::DomElement(Path pathFromOwner) : m_pathFromOwner(pathFromOwner) { }
+DomElement::DomElement(const Path &pathFromOwner) : m_pathFromOwner(pathFromOwner) { }
-Path DomElement::pathFromOwner(DomItem &) const
+Path DomElement::pathFromOwner(const DomItem &) const
{
Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
return m_pathFromOwner;
}
-Path DomElement::canonicalPath(DomItem &self) const
+Path DomElement::canonicalPath(const DomItem &self) const
{
Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
return self.owner().canonicalPath().path(m_pathFromOwner);
}
-DomItem DomElement::containingObject(DomItem &self) const
+DomItem DomElement::containingObject(const DomItem &self) const
{
Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
return DomBase::containingObject(self);
}
-void DomElement::updatePathFromOwner(Path newPath)
+void DomElement::updatePathFromOwner(const Path &newPath)
{
//if (!domTypeCanBeInline(kind()))
m_pathFromOwner = newPath;
@@ -2751,7 +2871,7 @@ void DomElement::updatePathFromOwner(Path newPath)
bool Reference::shouldCache() const
{
- for (Path p : referredObjectPath) {
+ for (const Path &p : referredObjectPath) {
switch (p.headKind()) {
case Path::Kind::Current:
switch (p.headCurrent()) {
@@ -2776,7 +2896,7 @@ bool Reference::shouldCache() const
return false;
}
-Reference::Reference(Path referredObject, Path pathFromOwner, const SourceLocation &)
+Reference::Reference(const Path &referredObject, const Path &pathFromOwner, const SourceLocation &)
: DomElement(pathFromOwner), referredObjectPath(referredObject)
{
}
@@ -2786,7 +2906,7 @@ quintptr Reference::id() const
return quintptr(0);
}
-bool Reference::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool Reference::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueLazyField(visitor, Fields::referredObjectPath, [this]() {
@@ -2797,7 +2917,7 @@ bool Reference::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-DomItem Reference::field(DomItem &self, QStringView name) const
+DomItem Reference::field(const DomItem &self, QStringView name) const
{
if (Fields::referredObjectPath == name)
return self.subDataItemField(Fields::referredObjectPath, referredObjectPath.toString());
@@ -2806,22 +2926,22 @@ DomItem Reference::field(DomItem &self, QStringView name) const
return DomItem();
}
-QList<QString> Reference::fields(DomItem &) const
+QList<QString> Reference::fields(const DomItem &) const
{
return QList<QString>({QString::fromUtf16(Fields::referredObjectPath), QString::fromUtf16(Fields::get)});
}
-DomItem Reference::index(DomItem &, index_type) const
+DomItem Reference::index(const DomItem &, index_type) const
{
return DomItem();
}
-DomItem Reference::key(DomItem &, QString) const
+DomItem Reference::key(const DomItem &, const QString &) const
{
return DomItem();
}
-DomItem Reference::get(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs) const
+DomItem Reference::get(const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const
{
DomItem res;
if (referredObjectPath) {
@@ -2857,7 +2977,7 @@ DomItem Reference::get(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs)
QList<Path> visitedRefsLocal;
self.resolve(
referredObjectPath,
- [&res](Path, DomItem &el) {
+ [&res](Path, const DomItem &el) {
res = el;
return false;
},
@@ -2870,7 +2990,8 @@ DomItem Reference::get(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs)
return res;
}
-QList<DomItem> Reference::getAll(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs) const
+QList<DomItem> Reference::getAll(
+ const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const
{
QList<DomItem> res;
if (referredObjectPath) {
@@ -2893,7 +3014,7 @@ QList<DomItem> Reference::getAll(DomItem &self, ErrorHandler h, QList<Path> *vis
}
if (!cachedPaths.isEmpty()) {
bool outdated = false;
- for (Path p : cachedPaths) {
+ for (const Path &p : cachedPaths) {
DomItem newEl = env.path(p);
if (!newEl) {
outdated = true;
@@ -2912,22 +3033,23 @@ QList<DomItem> Reference::getAll(DomItem &self, ErrorHandler h, QList<Path> *vis
}
self.resolve(
referredObjectPath,
- [&res](Path, DomItem &el) {
+ [&res](Path, const DomItem &el) {
res.append(el);
return true;
},
h, ResolveOption::None, referredObjectPath, visitedRefs);
if (env) {
QList<Path> canonicalPaths;
- for (DomItem i : res) {
+ for (const DomItem &i : res) {
if (i)
canonicalPaths.append(i.canonicalPath());
else
qCWarning(refLog)
<< "getAll of reference at " << selfPath << " visits empty items.";
}
- RefCacheEntry::addForPath(env, selfPath,
- RefCacheEntry { RefCacheEntry::Cached::All, canonicalPaths });
+ RefCacheEntry::addForPath(
+ env, selfPath,
+ RefCacheEntry { RefCacheEntry::Cached::All, std::move(canonicalPaths) });
}
}
return res;
@@ -2958,7 +3080,7 @@ OwningItem::OwningItem(int derivedFrom)
m_frozenAt(QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
{}
-OwningItem::OwningItem(int derivedFrom, QDateTime lastDataUpdateAt)
+OwningItem::OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt)
: m_derivedFrom(derivedFrom),
m_revision(nextRevision()),
m_createdAt(QDateTime::currentDateTimeUtc()),
@@ -2992,14 +3114,14 @@ int OwningItem::nextRevision()
return ++nextRev;
}
-bool OwningItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool OwningItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvItemField(visitor, Fields::errors, [&self, this]() {
QMultiMap<Path, ErrorMessage> myErrors = localErrors();
return self.subMapItem(Map(
self.pathFromOwner().field(Fields::errors),
- [myErrors](DomItem &map, QString key) {
+ [myErrors](const DomItem &map, const QString &key) {
auto it = myErrors.find(Path::fromString(key));
if (it != myErrors.end())
return map.subDataItem(PathEls::Key(key), it->toCbor(),
@@ -3007,7 +3129,7 @@ bool OwningItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
else
return DomItem();
},
- [myErrors](DomItem &) {
+ [myErrors](const DomItem &) {
QSet<QString> res;
auto it = myErrors.keyBegin();
auto end = myErrors.keyEnd();
@@ -3020,7 +3142,7 @@ bool OwningItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-DomItem OwningItem::containingObject(DomItem &self) const
+DomItem OwningItem::containingObject(const DomItem &self) const
{
Source s = self.canonicalPath().split();
if (s.pathFromSource) {
@@ -3078,12 +3200,12 @@ void OwningItem::refreshedDataAt(QDateTime tNew)
m_lastDataUpdateAt = tNew;
}
-void OwningItem::addError(DomItem &, ErrorMessage msg)
+void OwningItem::addError(const DomItem &, ErrorMessage &&msg)
{
- addErrorLocal(msg);
+ addErrorLocal(std::move(msg));
}
-void OwningItem::addErrorLocal(ErrorMessage msg)
+void OwningItem::addErrorLocal(ErrorMessage &&msg)
{
QMutexLocker l(mutex());
quint32 &c = m_errorsCounts[msg];
@@ -3092,7 +3214,7 @@ void OwningItem::addErrorLocal(ErrorMessage msg)
m_errors.insert(msg.path, msg);
}
-void OwningItem::clearErrors(ErrorGroups groups)
+void OwningItem::clearErrors(const ErrorGroups &groups)
{
QMutexLocker l(mutex());
auto it = m_errors.begin();
@@ -3104,8 +3226,9 @@ void OwningItem::clearErrors(ErrorGroups groups)
}
}
-bool OwningItem::iterateErrors(DomItem &self, function_ref<bool(DomItem, ErrorMessage)> visitor,
- Path inPath)
+bool OwningItem::iterateErrors(
+ const DomItem &self, function_ref<bool(const DomItem &, const ErrorMessage &)> visitor,
+ const Path &inPath)
{
QMultiMap<Path, ErrorMessage> myErrors;
{
@@ -3121,7 +3244,7 @@ bool OwningItem::iterateErrors(DomItem &self, function_ref<bool(DomItem, ErrorMe
return true;
}
-bool OwningItem::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor)
+bool OwningItem::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor)
{
return self.iterateDirectSubpaths(
[&self, visitor](const PathEls::PathComponent &, function_ref<DomItem()> iF) {
@@ -3135,13 +3258,11 @@ bool OwningItem::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owne
});
}
-bool operator==(const DomItem &o1c, const DomItem &o2c)
+bool operator==(const DomItem &o1, const DomItem &o2)
{
- DomItem &o1 = *const_cast<DomItem *>(&o1c);
- DomItem &o2 = *const_cast<DomItem *>(&o2c);
if (o1.m_kind != o2.m_kind)
return false;
- return o1.visitMutableEl([&o1, &o2](auto &&el1) {
+ return o1.visitEl([&o1, &o2](auto &&el1) {
auto &&el2 = std::get<std::decay_t<decltype(el1)>>(o2.m_element);
auto id1 = el1->id();
auto id2 = el2->id();
@@ -3162,10 +3283,10 @@ bool operator==(const DomItem &o1c, const DomItem &o2c)
ErrorHandler MutableDomItem::errorHandler()
{
MutableDomItem self;
- return [&self](ErrorMessage m) { self.addError(m); };
+ return [&self](const ErrorMessage &m) { self.addError(ErrorMessage(m)); };
}
-MutableDomItem MutableDomItem::addPrototypePath(Path prototypePath)
+MutableDomItem MutableDomItem::addPrototypePath(const Path &prototypePath)
{
if (QmlObject *el = mutableAs<QmlObject>()) {
return path(el->addPrototypePath(prototypePath));
@@ -3175,7 +3296,7 @@ MutableDomItem MutableDomItem::addPrototypePath(Path prototypePath)
}
}
-MutableDomItem MutableDomItem::setNextScopePath(Path nextScopePath)
+MutableDomItem MutableDomItem::setNextScopePath(const Path &nextScopePath)
{
if (QmlObject *el = mutableAs<QmlObject>()) {
el->setNextScopePath(nextScopePath);
@@ -3217,7 +3338,7 @@ MutableDomItem MutableDomItem::setMethods(QMultiMap<QString, MethodInfo> functio
return {};
}
-MutableDomItem MutableDomItem::setChildren(QList<QmlObject> children)
+MutableDomItem MutableDomItem::setChildren(const QList<QmlObject> &children)
{
if (QmlObject *el = mutableAs<QmlObject>()) {
el->setChildren(children);
@@ -3227,7 +3348,7 @@ MutableDomItem MutableDomItem::setChildren(QList<QmlObject> children)
return {};
}
-MutableDomItem MutableDomItem::setAnnotations(QList<QmlObject> annotations)
+MutableDomItem MutableDomItem::setAnnotations(const QList<QmlObject> &annotations)
{
if (QmlObject *el = mutableAs<QmlObject>())
el->setAnnotations(annotations);
@@ -3248,7 +3369,7 @@ MutableDomItem MutableDomItem::setAnnotations(QList<QmlObject> annotations)
}
return field(Fields::annotations);
}
-MutableDomItem MutableDomItem::setScript(std::shared_ptr<ScriptExpression> exp)
+MutableDomItem MutableDomItem::setScript(const std::shared_ptr<ScriptExpression> &exp)
{
switch (internalKind()) {
case DomType::Binding:
@@ -3265,8 +3386,14 @@ MutableDomItem MutableDomItem::setScript(std::shared_ptr<ScriptExpression> exp)
break;
case DomType::MethodParameter:
if (MethodParameter *p = mutableAs<MethodParameter>()) {
- p->defaultValue = exp;
- return field(Fields::body);
+ if (exp->expressionType() == ScriptExpression::ExpressionType::ArgInitializer) {
+ p->defaultValue = exp;
+ return field(Fields::defaultValue);
+ }
+ if (exp->expressionType() == ScriptExpression::ExpressionType::ArgumentStructure) {
+ p->value = exp;
+ return field(Fields::value);
+ }
}
break;
case DomType::ScriptExpression:
@@ -3280,7 +3407,7 @@ MutableDomItem MutableDomItem::setScript(std::shared_ptr<ScriptExpression> exp)
return MutableDomItem();
}
-MutableDomItem MutableDomItem::setCode(QString code)
+MutableDomItem MutableDomItem::setCode(const QString &code)
{
DomItem it = item();
switch (it.internalKind()) {
@@ -3323,7 +3450,8 @@ MutableDomItem MutableDomItem::setCode(QString code)
return MutableDomItem();
}
-MutableDomItem MutableDomItem::addPropertyDef(PropertyDefinition propertyDef, AddOption option)
+MutableDomItem MutableDomItem::addPropertyDef(
+ const PropertyDefinition &propertyDef, AddOption option)
{
if (QmlObject *el = mutableAs<QmlObject>())
return el->addPropertyDef(*this, propertyDef, option);
@@ -3341,7 +3469,7 @@ MutableDomItem MutableDomItem::addBinding(Binding binding, AddOption option)
return MutableDomItem();
}
-MutableDomItem MutableDomItem::addMethod(MethodInfo functionDef, AddOption option)
+MutableDomItem MutableDomItem::addMethod(const MethodInfo &functionDef, AddOption option)
{
if (QmlObject *el = mutableAs<QmlObject>())
return el->addMethod(*this, functionDef, option);
@@ -3394,17 +3522,17 @@ MutableDomItem MutableDomItem::addAnnotation(QmlObject annotation)
return MutableDomItem(owner().item(), res);
}
-MutableDomItem MutableDomItem::addPreComment(const Comment &comment, QString regionName)
+MutableDomItem MutableDomItem::addPreComment(const Comment &comment, FileLocationRegion region)
{
index_type idx;
MutableDomItem rC = field(Fields::comments);
if (auto rcPtr = rC.mutableAs<RegionComments>()) {
- auto &preList = rcPtr->regionComments[regionName].preComments;
- idx = preList.size();
- preList.append(comment);
+ auto commentedElement = rcPtr->regionComments()[region];
+ idx = commentedElement.preComments().size();
+ commentedElement.addComment(comment);
MutableDomItem res = path(Path::Field(Fields::comments)
.field(Fields::regionComments)
- .key(regionName)
+ .key(fileLocationRegionName(region))
.field(Fields::preComments)
.index(idx));
Q_ASSERT(res);
@@ -3413,17 +3541,17 @@ MutableDomItem MutableDomItem::addPreComment(const Comment &comment, QString reg
return MutableDomItem();
}
-MutableDomItem MutableDomItem::addPostComment(const Comment &comment, QString regionName)
+MutableDomItem MutableDomItem::addPostComment(const Comment &comment, FileLocationRegion region)
{
index_type idx;
MutableDomItem rC = field(Fields::comments);
if (auto rcPtr = rC.mutableAs<RegionComments>()) {
- auto &postList = rcPtr->regionComments[regionName].postComments;
- idx = postList.size();
- postList.append(comment);
+ auto commentedElement = rcPtr->regionComments()[region];
+ idx = commentedElement.postComments().size();
+ commentedElement.addComment(comment);
MutableDomItem res = path(Path::Field(Fields::comments)
.field(Fields::regionComments)
- .key(regionName)
+ .key(fileLocationRegionName(region))
.field(Fields::postComments)
.index(idx));
Q_ASSERT(res);
@@ -3434,7 +3562,7 @@ MutableDomItem MutableDomItem::addPostComment(const Comment &comment, QString re
QDebug operator<<(QDebug debug, const DomItem &c)
{
- dumperToQDebug([&c](Sink s) { const_cast<DomItem *>(&c)->dump(s); }, debug);
+ dumperToQDebug([&c](const Sink &s) { c.dump(s); }, debug);
return debug;
}
@@ -3445,7 +3573,7 @@ QDebug operator<<(QDebug debug, const MutableDomItem &c)
<< ", " << cc.canonicalPath().toString() << ")";
}
-bool ListPBase::iterateDirectSubpaths(DomItem &self, DirectVisitor v)
+bool ListPBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const
{
index_type len = index_type(m_pList.size());
for (index_type i = 0; i < len; ++i) {
@@ -3455,9 +3583,9 @@ bool ListPBase::iterateDirectSubpaths(DomItem &self, DirectVisitor v)
return true;
}
-void ListPBase::writeOut(DomItem &self, OutWriter &ow, bool compact) const
+void ListPBase::writeOut(const DomItem &self, OutWriter &ow, bool compact) const
{
- ow.writeRegion(u"leftSquareBrace", u"[");
+ ow.writeRegion(LeftBracketRegion);
int baseIndent = ow.increaseIndent(1);
bool first = true;
index_type len = index_type(m_pList.size());
@@ -3474,7 +3602,36 @@ void ListPBase::writeOut(DomItem &self, OutWriter &ow, bool compact) const
if (!compact && !first)
ow.newline();
ow.decreaseIndent(1, baseIndent);
- ow.writeRegion(u"rightSquareBrace", u"]");
+ ow.writeRegion(RightBracketRegion);
+}
+
+QQmlJSScope::ConstPtr ScriptElement::semanticScope()
+{
+ return m_scope;
+}
+void ScriptElement::setSemanticScope(const QQmlJSScope::ConstPtr &scope)
+{
+ m_scope = scope;
+}
+
+/*!
+ \internal
+ \brief Returns a pointer to the virtual base for virtual method calls.
+
+ A helper to call virtual methods without having to call std::visit(...).
+ */
+ScriptElement::PointerType<ScriptElement> ScriptElementVariant::base() const
+{
+ if (!m_data)
+ return nullptr;
+
+ return std::visit(
+ [](auto &&e) {
+ // std::reinterpret_pointer_cast does not exist on qnx it seems...
+ return std::shared_ptr<ScriptElement>(
+ e, static_cast<ScriptElement *>(e.get()));
+ },
+ *m_data);
}
} // end namespace Dom
diff --git a/src/qmldom/qqmldomitem_p.h b/src/qmldom/qqmldomitem_p.h
index 631a0ff335..50775a1db2 100644
--- a/src/qmldom/qqmldomitem_p.h
+++ b/src/qmldom/qqmldomitem_p.h
@@ -17,6 +17,7 @@
#include "qqmldom_global.h"
#include "qqmldom_fwd_p.h"
+#include "qqmldom_utils_p.h"
#include "qqmldomconstants_p.h"
#include "qqmldomstringdumper_p.h"
#include "qqmldompath_p.h"
@@ -24,6 +25,7 @@
#include "qqmldomfunctionref_p.h"
#include "qqmldomfilewriter_p.h"
#include "qqmldomlinewriter_p.h"
+#include "qqmldomfieldfilter_p.h"
#include <QtCore/QMap>
#include <QtCore/QMultiMap>
@@ -36,6 +38,7 @@
#include <QtCore/QCborValue>
#include <QtCore/QTimeZone>
#include <QtQml/private/qqmljssourcelocation_p.h>
+#include <QtQmlCompiler/private/qqmljsscope_p.h>
#include <memory>
#include <typeinfo>
@@ -60,6 +63,7 @@ constexpr bool domTypeIsValueWrap(DomType k);
constexpr bool domTypeIsDomElement(DomType);
constexpr bool domTypeIsOwningItem(DomType);
constexpr bool domTypeIsUnattachedOwningItem(DomType);
+constexpr bool domTypeIsScriptElement(DomType);
QMLDOM_EXPORT bool domTypeIsExternalItem(DomType k);
QMLDOM_EXPORT bool domTypeIsTopItem(DomType k);
QMLDOM_EXPORT bool domTypeIsContainer(DomType k);
@@ -72,6 +76,7 @@ constexpr bool domTypeCanBeInline(DomType k)
case DomType::ListP:
case DomType::ConstantData:
case DomType::SimpleObjectWrap:
+ case DomType::ScriptElementWrap:
case DomType::Reference:
return true;
default:
@@ -85,15 +90,13 @@ QMLDOM_EXPORT QString domTypeToString(DomType k);
QMLDOM_EXPORT QMap<DomKind, QString> domKindToStringMap();
QMLDOM_EXPORT QString domKindToString(DomKind k);
-QMLDOM_EXPORT QCborValue locationToData(SourceLocation loc, QStringView strValue=u"");
-
-inline bool noFilter(DomItem &, const PathEls::PathComponent &, DomItem &)
+inline bool noFilter(const DomItem &, const PathEls::PathComponent &, const DomItem &)
{
return true;
}
using DirectVisitor = function_ref<bool(const PathEls::PathComponent &, function_ref<DomItem()>)>;
-// using DirectVisitor = function_ref<bool(Path, DomItem &)>;
+// using DirectVisitor = function_ref<bool(Path, const DomItem &)>;
namespace {
template<typename T>
@@ -176,8 +179,11 @@ template<typename T>
union SubclassStorage {
int i;
T lp;
+
+ // TODO: these are extremely nasty. What is this int doing in here?
T *data() { return reinterpret_cast<T *>(this); }
const T *data() const { return reinterpret_cast<const T *>(this); }
+
SubclassStorage() { }
SubclassStorage(T &&el) { el.moveTo(data()); }
SubclassStorage(const T *el) { el->copyTo(data()); }
@@ -192,42 +198,47 @@ union SubclassStorage {
~SubclassStorage() { data()->~T(); }
};
-class QMLDOM_EXPORT DomBase{
+class QMLDOM_EXPORT DomBase
+{
public:
+ using FilterT = function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>;
+
virtual ~DomBase() = default;
+ DomBase *domBase() { return this; }
+ const DomBase *domBase() const { return this; }
+
// minimal overload set:
virtual DomType kind() const = 0;
virtual DomKind domKind() const;
- virtual Path pathFromOwner(DomItem &self) const = 0;
- virtual Path canonicalPath(DomItem &self) const = 0;
+ virtual Path pathFromOwner(const DomItem &self) const = 0;
+ virtual Path canonicalPath(const DomItem &self) const = 0;
virtual bool
- iterateDirectSubpaths(DomItem &self,
- DirectVisitor visitor) = 0; // iterates the *direct* subpaths, returns
- // false if a quick end was requested
- bool iterateDirectSubpathsConst(DomItem &self, DirectVisitor)
+ iterateDirectSubpaths(const DomItem &self,
+ DirectVisitor visitor) const = 0; // iterates the *direct* subpaths, returns
+ // false if a quick end was requested
+
+ bool iterateDirectSubpathsConst(const DomItem &self, DirectVisitor)
const; // iterates the *direct* subpaths, returns false if a quick end was requested
virtual DomItem containingObject(
- DomItem &self) const; // the DomItem corresponding to the canonicalSource source
- virtual void
- dump(DomItem &, Sink sink, int indent,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) const;
+ const DomItem &self) const; // the DomItem corresponding to the canonicalSource source
+ virtual void dump(const DomItem &, const Sink &sink, int indent, FilterT filter) const;
virtual quintptr id() const;
QString typeName() const;
- virtual QList<QString> fields(DomItem &self) const;
- virtual DomItem field(DomItem &self, QStringView name) const;
+ virtual QList<QString> fields(const DomItem &self) const;
+ virtual DomItem field(const DomItem &self, QStringView name) const;
- virtual index_type indexes(DomItem &self) const;
- virtual DomItem index(DomItem &self, index_type index) const;
+ virtual index_type indexes(const DomItem &self) const;
+ virtual DomItem index(const DomItem &self, index_type index) const;
- virtual QSet<QString> const keys(DomItem &self) const;
- virtual DomItem key(DomItem &self, QString name) const;
+ virtual QSet<QString> const keys(const DomItem &self) const;
+ virtual DomItem key(const DomItem &self, const QString &name) const;
- virtual QString canonicalFilePath(DomItem &self) const;
+ virtual QString canonicalFilePath(const DomItem &self) const;
- virtual void writeOut(DomItem &self, OutWriter &lw) const;
+ virtual void writeOut(const DomItem &self, OutWriter &lw) const;
virtual QCborValue value() const {
return QCborValue();
@@ -264,12 +275,12 @@ public:
Empty();
quintptr id() const override { return ~quintptr(0); }
- Path pathFromOwner(DomItem &self) const override;
- Path canonicalPath(DomItem &self) const override;
- DomItem containingObject(DomItem &self) const override;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- void dump(DomItem &, Sink s, int indent,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter)
+ Path pathFromOwner(const DomItem &self) const override;
+ Path canonicalPath(const DomItem &self) const override;
+ DomItem containingObject(const DomItem &self) const override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ void dump(const DomItem &, const Sink &s, int indent,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter)
const override;
};
@@ -277,13 +288,14 @@ class QMLDOM_EXPORT DomElement: public DomBase {
protected:
DomElement& operator=(const DomElement&) = default;
public:
- DomElement(Path pathFromOwner = Path());
+ DomElement(const Path &pathFromOwner = Path());
DomElement(const DomElement &o) = default;
- Path pathFromOwner(DomItem &self) const override;
+ Path pathFromOwner(const DomItem &self) const override;
Path pathFromOwner() const { return m_pathFromOwner; }
- Path canonicalPath(DomItem &self) const override;
- DomItem containingObject(DomItem &self) const override;
- virtual void updatePathFromOwner(Path newPath);
+ Path canonicalPath(const DomItem &self) const override;
+ DomItem containingObject(const DomItem &self) const override;
+ virtual void updatePathFromOwner(const Path &newPath);
+
private:
Path m_pathFromOwner;
};
@@ -299,22 +311,35 @@ public:
Map &operator*() { return *this; }
const Map &operator*() const { return *this; }
- using LookupFunction = std::function<DomItem(DomItem &, QString)>;
- using Keys = std::function<QSet<QString>(DomItem &)>;
- Map(Path pathFromOwner, LookupFunction lookup, Keys keys, QString targetType);
+ using LookupFunction = std::function<DomItem(const DomItem &, QString)>;
+ using Keys = std::function<QSet<QString>(const DomItem &)>;
+ Map(const Path &pathFromOwner, const LookupFunction &lookup,
+ const Keys &keys, const QString &targetType);
quintptr id() const override;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- QSet<QString> const keys(DomItem &self) const override;
- DomItem key(DomItem &self, QString name) const override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ QSet<QString> const keys(const DomItem &self) const override;
+ DomItem key(const DomItem &self, const QString &name) const override;
template<typename T>
- static Map fromMultiMapRef(Path pathFromOwner, QMultiMap<QString, T> &mmap);
+ static Map fromMultiMapRef(const Path &pathFromOwner, const QMultiMap<QString, T> &mmap);
+ template<typename T>
+ static Map fromMultiMap(const Path &pathFromOwner, const QMultiMap<QString, T> &mmap);
template<typename T>
static Map
- fromMapRef(Path pathFromOwner, QMap<QString, T> &mmap,
- std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper);
+ fromMapRef(
+ const Path &pathFromOwner, const QMap<QString, T> &mmap,
+ const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper);
+
+ template<typename T>
+ static Map fromFileRegionMap(
+ const Path &pathFromOwner, const QMap<FileLocationRegion, T> &map);
+ template<typename T>
+ static Map fromFileRegionListMap(
+ const Path &pathFromOwner, const QMap<FileLocationRegion, QList<T>> &map);
private:
+ template<typename MapT>
+ static QSet<QString> fileRegionKeysFromMap(const MapT &map);
LookupFunction m_lookup;
Keys m_keys;
QString m_targetType;
@@ -331,32 +356,33 @@ public:
List &operator*() { return *this; }
const List &operator*() const { return *this; }
- using LookupFunction = std::function<DomItem(DomItem &, index_type)>;
- using Length = std::function<index_type(DomItem &)>;
+ using LookupFunction = std::function<DomItem(const DomItem &, index_type)>;
+ using Length = std::function<index_type(const DomItem &)>;
using IteratorFunction =
- std::function<bool(DomItem &, function_ref<bool(index_type, function_ref<DomItem()>)>)>;
+ std::function<bool(const DomItem &, function_ref<bool(index_type, function_ref<DomItem()>)>)>;
- List(Path pathFromOwner, LookupFunction lookup, Length length, IteratorFunction iterator, QString elType);
+ List(const Path &pathFromOwner, const LookupFunction &lookup, const Length &length,
+ const IteratorFunction &iterator, const QString &elType);
quintptr id() const override;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
void
- dump(DomItem &, Sink s, int indent,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)>) const override;
- index_type indexes(DomItem &self) const override;
- DomItem index(DomItem &self, index_type index) const override;
+ dump(const DomItem &, const Sink &s, int indent,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>) const override;
+ index_type indexes(const DomItem &self) const override;
+ DomItem index(const DomItem &self, index_type index) const override;
template<typename T>
static List
- fromQList(Path pathFromOwner, QList<T> list,
- std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper,
+ fromQList(const Path &pathFromOwner, const QList<T> &list,
+ const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
ListOptions options = ListOptions::Normal);
template<typename T>
static List
- fromQListRef(Path pathFromOwner, QList<T> &list,
- std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper,
+ fromQListRef(const Path &pathFromOwner, const QList<T> &list,
+ const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
ListOptions options = ListOptions::Normal);
- void writeOut(DomItem &self, OutWriter &ow, bool compact) const;
- void writeOut(DomItem &self, OutWriter &ow) const override { writeOut(self, ow, true); }
+ void writeOut(const DomItem &self, OutWriter &ow, bool compact) const;
+ void writeOut(const DomItem &self, OutWriter &ow) const override { writeOut(self, ow, true); }
private:
LookupFunction m_lookup;
@@ -371,20 +397,20 @@ public:
constexpr static DomType kindValue = DomType::ListP;
DomType kind() const override { return kindValue; }
- ListPBase(Path pathFromOwner, const QList<void *> &pList, QString elType)
+ ListPBase(const Path &pathFromOwner, const QList<const void *> &pList, const QString &elType)
: DomElement(pathFromOwner), m_pList(pList), m_elType(elType)
{
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor v) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const override;
virtual void copyTo(ListPBase *) const { Q_ASSERT(false); };
virtual void moveTo(ListPBase *) const { Q_ASSERT(false); };
quintptr id() const override { return quintptr(0); }
- index_type indexes(DomItem &) const override { return index_type(m_pList.size()); }
- void writeOut(DomItem &self, OutWriter &ow, bool compact) const;
- void writeOut(DomItem &self, OutWriter &ow) const override { writeOut(self, ow, true); }
+ index_type indexes(const DomItem &) const override { return index_type(m_pList.size()); }
+ void writeOut(const DomItem &self, OutWriter &ow, bool compact) const;
+ void writeOut(const DomItem &self, OutWriter &ow) const override { writeOut(self, ow, true); }
protected:
- QList<void *> m_pList;
+ QList<const void *> m_pList;
QString m_elType;
};
@@ -394,7 +420,7 @@ class ListPT final : public ListPBase
public:
constexpr static DomType kindValue = DomType::ListP;
- ListPT(Path pathFromOwner, QList<T *> pList, QString elType = QString(),
+ ListPT(const Path &pathFromOwner, const QList<T *> &pList, const QString &elType = QString(),
ListOptions options = ListOptions::Normal)
: ListPBase(pathFromOwner, {},
(elType.isEmpty() ? QLatin1String(typeid(T).name()) : elType))
@@ -405,7 +431,7 @@ public:
"ListPT does not have the same size as ListPBase");
m_pList.reserve(pList.size());
if (options == ListOptions::Normal) {
- for (void *p : pList)
+ for (const void *p : pList)
m_pList.append(p);
} else if (options == ListOptions::Reverse) {
for (qsizetype i = pList.size(); i-- != 0;)
@@ -417,9 +443,9 @@ public:
}
void copyTo(ListPBase *t) const override { new (t) ListPT(*this); }
void moveTo(ListPBase *t) const override { new (t) ListPT(std::move(*this)); }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor v) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const override;
- DomItem index(DomItem &self, index_type index) const override;
+ DomItem index(const DomItem &self, index_type index) const override;
};
class QMLDOM_EXPORT ListP
@@ -427,7 +453,7 @@ class QMLDOM_EXPORT ListP
public:
constexpr static DomType kindValue = DomType::ListP;
template<typename T>
- ListP(Path pathFromOwner, QList<T *> pList, QString elType = QString(),
+ ListP(const Path &pathFromOwner, const QList<T *> &pList, const QString &elType = QString(),
ListOptions options = ListOptions::Normal)
: list(ListPT<T>(pathFromOwner, pList, elType, options))
{
@@ -459,8 +485,9 @@ public:
ConstantData &operator*() { return *this; }
const ConstantData &operator*() const { return *this; }
- ConstantData(Path pathFromOwner, QCborValue value, Options options = Options::MapIsMap);
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ ConstantData(const Path &pathFromOwner, const QCborValue &value,
+ Options options = Options::MapIsMap);
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
quintptr id() const override;
DomKind domKind() const override;
QCborValue value() const override { return m_value; }
@@ -484,27 +511,17 @@ public:
{
if (m_options & SimpleWrapOption::ValueType) {
if (m_value.metaType() == QMetaType::fromType<T>())
- return reinterpret_cast<const T *>(m_value.constData());
- return nullptr;
- } else {
- return m_value.value<T *>();
- }
- }
- template <typename T>
- T *mutableAs()
- {
- if (m_options & SimpleWrapOption::ValueType) {
- if (m_value.metaType() == QMetaType::fromType<T>())
- return reinterpret_cast<T *>(m_value.data());
+ return static_cast<const T *>(m_value.constData());
return nullptr;
} else {
- return m_value.value<T *>();
+ return m_value.value<const T *>();
}
}
+
SimpleObjectWrapBase() = delete;
virtual void copyTo(SimpleObjectWrapBase *) const { Q_ASSERT(false); }
virtual void moveTo(SimpleObjectWrapBase *) const { Q_ASSERT(false); }
- bool iterateDirectSubpaths(DomItem &, DirectVisitor) override
+ bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override
{
Q_ASSERT(false);
return true;
@@ -512,7 +529,7 @@ public:
protected:
friend class TestDomItem;
- SimpleObjectWrapBase(Path pathFromOwner, QVariant value, quintptr idValue,
+ SimpleObjectWrapBase(const Path &pathFromOwner, const QVariant &value, quintptr idValue,
DomType kind = kindValue,
SimpleWrapOptions options = SimpleWrapOption::None)
: DomElement(pathFromOwner),
@@ -537,21 +554,21 @@ class SimpleObjectWrapT final : public SimpleObjectWrapBase
public:
constexpr static DomType kindValue = DomType::SimpleObjectWrap;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
{
- return mutableAsT()->iterateDirectSubpaths(self, visitor);
+ return asT()->iterateDirectSubpaths(self, visitor);
}
- void writeOut(DomItem &self, OutWriter &lw) const override;
+ void writeOut(const DomItem &self, OutWriter &lw) const override;
- T const *asT() const
+ const T *asT() const
{
if constexpr (domTypeIsValueWrap(T::kindValue)) {
if (m_value.metaType() == QMetaType::fromType<T>())
- return reinterpret_cast<const T *>(m_value.constData());
+ return static_cast<const T *>(m_value.constData());
return nullptr;
} else if constexpr (domTypeIsObjWrap(T::kindValue)) {
- return m_value.value<T *>();
+ return m_value.value<const T *>();
} else {
// need dependent static assert to not unconditially trigger
static_assert(!std::is_same_v<T, T>, "wrapping of unexpected type");
@@ -559,20 +576,6 @@ public:
}
}
- T *mutableAsT()
- {
- if (domTypeIsValueWrap(T::kindValue)) {
- if (m_value.metaType() == QMetaType::fromType<T>())
- return reinterpret_cast<T *>(m_value.data());
- return nullptr;
- } else if constexpr (domTypeIsObjWrap(T::kindValue)) {
- return m_value.value<T *>();
- } else {
- Q_ASSERT_X(false, "SimpleObjectWrap", "wrapping of unexpected type");
- return nullptr;
- }
- }
-
void copyTo(SimpleObjectWrapBase *target) const override
{
static_assert(sizeof(SimpleObjectWrapBase) == sizeof(SimpleObjectWrapT),
@@ -591,7 +594,8 @@ public:
new (target) SimpleObjectWrapT(std::move(*this));
}
- SimpleObjectWrapT(Path pathFromOwner, QVariant v, quintptr idValue, SimpleWrapOptions o)
+ SimpleObjectWrapT(const Path &pathFromOwner, const QVariant &v,
+ quintptr idValue, SimpleWrapOptions o)
: SimpleObjectWrapBase(pathFromOwner, v, idValue, T::kindValue, o)
{
Q_ASSERT(domTypeIsValueWrap(T::kindValue) == bool(o & SimpleWrapOption::ValueType));
@@ -609,7 +613,7 @@ public:
const SimpleObjectWrapBase &operator*() const { return *wrap.data(); }
template<typename T>
- static SimpleObjectWrap fromObjectRef(Path pathFromOwner, T &value)
+ static SimpleObjectWrap fromObjectRef(const Path &pathFromOwner, T &value)
{
return SimpleObjectWrap(pathFromOwner, value);
}
@@ -617,7 +621,7 @@ public:
private:
template<typename T>
- SimpleObjectWrap(Path pathFromOwner, T &value)
+ SimpleObjectWrap(const Path &pathFromOwner, T &value)
{
using BaseT = std::decay_t<T>;
if constexpr (domTypeIsObjWrap(BaseT::kindValue)) {
@@ -651,57 +655,248 @@ public:
const Reference &operator*() const { return *this; }
bool shouldCache() const;
- Reference(Path referredObject = Path(), Path pathFromOwner = Path(), const SourceLocation & loc = SourceLocation());
+ Reference(const Path &referredObject = Path(), const Path &pathFromOwner = Path(),
+ const SourceLocation &loc = SourceLocation());
quintptr id() const override;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- DomItem field(DomItem &self, QStringView name) const override;
- QList<QString> fields(DomItem &self) const override;
- index_type indexes(DomItem &) const override { return 0; }
- DomItem index(DomItem &, index_type) const override;
- QSet<QString> const keys(DomItem &) const override { return {}; }
- DomItem key(DomItem &, QString) const override;
-
- DomItem get(DomItem &self, ErrorHandler h = nullptr, QList<Path> *visitedRefs = nullptr) const;
- QList<DomItem> getAll(DomItem &self, ErrorHandler h = nullptr,
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ DomItem field(const DomItem &self, QStringView name) const override;
+ QList<QString> fields(const DomItem &self) const override;
+ index_type indexes(const DomItem &) const override { return 0; }
+ DomItem index(const DomItem &, index_type) const override;
+ QSet<QString> const keys(const DomItem &) const override { return {}; }
+ DomItem key(const DomItem &, const QString &) const override;
+
+ DomItem get(const DomItem &self, const ErrorHandler &h = nullptr,
+ QList<Path> *visitedRefs = nullptr) const;
+ QList<DomItem> getAll(const DomItem &self, const ErrorHandler &h = nullptr,
QList<Path> *visitedRefs = nullptr) const;
Path referredObjectPath;
};
-using ElementT = std::variant<
- Empty, Map, List, ListP, ConstantData, SimpleObjectWrap, Reference, GlobalComponent *,
- JsResource *, QmlComponent *, QmltypesComponent *, EnumDecl *, MockObject *, ModuleScope *,
- AstComments *, AttachedInfo *, DomEnvironment *, DomUniverse *, ExternalItemInfoBase *,
- ExternalItemPairBase *, GlobalScope *, JsFile *, QmlDirectory *, QmlFile *, QmldirFile *,
- QmlObject *, QmltypesFile *, LoadInfo *, MockOwner *, ModuleIndex *, ScriptExpression *>;
+template<typename Info>
+class AttachedInfoT;
+class FileLocations;
+
+/*!
+ \internal
+ \brief A common base class for all the script elements.
+
+ This marker class allows to use all the script elements as a ScriptElement*, using virtual
+ dispatch. For now, it does not add any extra functionality, compared to a DomElement, but allows
+ to forbid DomElement* at the places where only script elements are required.
+ */
+// TODO: do we need another marker struct like this one to differentiate expressions from
+// statements? This would allow to avoid mismatchs between script expressions and script statements,
+// using type-safety.
+struct ScriptElement : public DomElement
+{
+ template<typename T>
+ using PointerType = std::shared_ptr<T>;
+
+ using DomElement::DomElement;
+ virtual void createFileLocations(
+ const std::shared_ptr<AttachedInfoT<FileLocations>> &fileLocationOfOwner) = 0;
+
+ QQmlJSScope::ConstPtr semanticScope();
+ void setSemanticScope(const QQmlJSScope::ConstPtr &scope);
+
+private:
+ QQmlJSScope::ConstPtr m_scope;
+};
+
+/*!
+ \internal
+ \brief Use this to contain any script element.
+ */
+class ScriptElementVariant
+{
+private:
+ template<typename... T>
+ using VariantOfPointer = std::variant<ScriptElement::PointerType<T>...>;
+
+ template<typename T, typename Variant>
+ struct TypeIsInVariant;
+
+ template<typename T, typename... Ts>
+ struct TypeIsInVariant<T, std::variant<Ts...>> : public std::disjunction<std::is_same<T, Ts>...>
+ {
+ };
+
+public:
+ using ScriptElementT =
+ VariantOfPointer<ScriptElements::BlockStatement, ScriptElements::IdentifierExpression,
+ ScriptElements::ForStatement, ScriptElements::BinaryExpression,
+ ScriptElements::VariableDeclarationEntry, ScriptElements::Literal,
+ ScriptElements::IfStatement, ScriptElements::GenericScriptElement,
+ ScriptElements::VariableDeclaration, ScriptElements::ReturnStatement>;
+
+ template<typename T>
+ static ScriptElementVariant fromElement(const T &element)
+ {
+ static_assert(TypeIsInVariant<T, ScriptElementT>::value,
+ "Cannot construct ScriptElementVariant from T, as it is missing from the "
+ "ScriptElementT.");
+ ScriptElementVariant p;
+ p.m_data = element;
+ return p;
+ }
+
+ ScriptElement::PointerType<ScriptElement> base() const;
+
+ operator bool() const { return m_data.has_value(); }
+
+ template<typename F>
+ void visitConst(F &&visitor) const
+ {
+ if (m_data)
+ std::visit(std::forward<F>(visitor), *m_data);
+ }
+
+ template<typename F>
+ void visit(F &&visitor)
+ {
+ if (m_data)
+ std::visit(std::forward<F>(visitor), *m_data);
+ }
+ std::optional<ScriptElementT> data() { return m_data; }
+ void setData(const ScriptElementT &data) { m_data = data; }
+
+private:
+ std::optional<ScriptElementT> m_data;
+};
+
+/*!
+ \internal
+
+ To avoid cluttering the already unwieldy \l ElementT type below with all the types that the
+ different script elements can have, wrap them in an extra class. It will behave like an internal
+ Dom structure (e.g. like a List or a Map) and contain a pointer the the script element.
+ */
+class ScriptElementDomWrapper
+{
+public:
+ ScriptElementDomWrapper(const ScriptElementVariant &element) : m_element(element) { }
+
+ static constexpr DomType kindValue = DomType::ScriptElementWrap;
-using TopT = std::variant<std::shared_ptr<DomEnvironment>, std::shared_ptr<DomUniverse>>;
+ DomBase *operator->() { return m_element.base().get(); }
+ const DomBase *operator->() const { return m_element.base().get(); }
+ DomBase &operator*() { return *m_element.base(); }
+ const DomBase &operator*() const { return *m_element.base(); }
-using OwnerT =
- std::variant<std::shared_ptr<ModuleIndex>, std::shared_ptr<MockOwner>,
- std::shared_ptr<ExternalItemInfoBase>, std::shared_ptr<ExternalItemPairBase>,
- std::shared_ptr<QmlDirectory>, std::shared_ptr<QmldirFile>,
- std::shared_ptr<JsFile>, std::shared_ptr<QmlFile>,
- std::shared_ptr<QmltypesFile>, std::shared_ptr<GlobalScope>,
- std::shared_ptr<ScriptExpression>, std::shared_ptr<AstComments>,
- std::shared_ptr<LoadInfo>, std::shared_ptr<AttachedInfo>,
- std::shared_ptr<DomEnvironment>, std::shared_ptr<DomUniverse>>;
+ ScriptElementVariant element() const { return m_element; }
-inline bool emptyChildrenVisitor(Path, DomItem &, bool)
+private:
+ ScriptElementVariant m_element;
+};
+
+// TODO: create more "groups" to simplify this variant? Maybe into Internal, ScriptExpression, ???
+using ElementT =
+ std::variant<
+ ConstantData,
+ Empty,
+ List,
+ ListP,
+ Map,
+ Reference,
+ ScriptElementDomWrapper,
+ SimpleObjectWrap,
+ const AstComments *,
+ const AttachedInfo *,
+ const DomEnvironment *,
+ const DomUniverse *,
+ const EnumDecl *,
+ const ExternalItemInfoBase *,
+ const ExternalItemPairBase *,
+ const GlobalComponent *,
+ const GlobalScope *,
+ const JsFile *,
+ const JsResource *,
+ const LoadInfo *,
+ const MockObject *,
+ const MockOwner *,
+ const ModuleIndex *,
+ const ModuleScope *,
+ const QmlComponent *,
+ const QmlDirectory *,
+ const QmlFile *,
+ const QmlObject *,
+ const QmldirFile *,
+ const QmltypesComponent *,
+ const QmltypesFile *,
+ const ScriptExpression *
+ >;
+
+using TopT = std::variant<
+ std::monostate,
+ std::shared_ptr<DomEnvironment>,
+ std::shared_ptr<DomUniverse>>;
+
+using OwnerT = std::variant<
+ std::monostate,
+ std::shared_ptr<ModuleIndex>,
+ std::shared_ptr<MockOwner>,
+ std::shared_ptr<ExternalItemInfoBase>,
+ std::shared_ptr<ExternalItemPairBase>,
+ std::shared_ptr<QmlDirectory>,
+ std::shared_ptr<QmldirFile>,
+ std::shared_ptr<JsFile>,
+ std::shared_ptr<QmlFile>,
+ std::shared_ptr<QmltypesFile>,
+ std::shared_ptr<GlobalScope>,
+ std::shared_ptr<ScriptExpression>,
+ std::shared_ptr<AstComments>,
+ std::shared_ptr<LoadInfo>,
+ std::shared_ptr<AttachedInfo>,
+ std::shared_ptr<DomEnvironment>,
+ std::shared_ptr<DomUniverse>>;
+
+inline bool emptyChildrenVisitor(Path, const DomItem &, bool)
{
return true;
}
class MutableDomItem;
+class FileToLoad
+{
+public:
+ struct InMemoryContents
+ {
+ QString data;
+ QDateTime date = QDateTime::currentDateTimeUtc();
+ };
+
+ FileToLoad(const std::weak_ptr<DomEnvironment> &environment, const QString &canonicalPath,
+ const QString &logicalPath, const std::optional<InMemoryContents> &content);
+ FileToLoad() = default;
+
+ static FileToLoad fromMemory(const std::weak_ptr<DomEnvironment> &environment,
+ const QString &path, const QString &data);
+ static FileToLoad fromFileSystem(const std::weak_ptr<DomEnvironment> &environment,
+ const QString &canonicalPath);
+
+ std::weak_ptr<DomEnvironment> environment() const { return m_environment; }
+ QString canonicalPath() const { return m_canonicalPath; }
+ QString logicalPath() const { return m_logicalPath; }
+ std::optional<InMemoryContents> content() const { return m_content; }
+
+private:
+ std::weak_ptr<DomEnvironment> m_environment;
+ QString m_canonicalPath;
+ QString m_logicalPath;
+ std::optional<InMemoryContents> m_content;
+};
+
class QMLDOM_EXPORT DomItem {
Q_DECLARE_TR_FUNCTIONS(DomItem);
public:
- using Callback = function<void(Path, DomItem &, DomItem &)>;
+ using Callback = function<void(const Path &, const DomItem &, const DomItem &)>;
using InternalKind = DomType;
- using Visitor = function_ref<bool(Path, DomItem &)>;
- using ChildrenVisitor = function_ref<bool(Path, DomItem &, bool)>;
+ using Visitor = function_ref<bool(const Path &, const DomItem &)>;
+ using ChildrenVisitor = function_ref<bool(const Path &, const DomItem &, bool)>;
static ErrorGroup domErrorGroup;
static ErrorGroups myErrors();
@@ -711,12 +906,7 @@ public:
enum class CopyOption { EnvConnected, EnvDisconnected };
template<typename F>
- auto visitMutableEl(F f)
- {
- return std::visit(f, this->m_element);
- }
- template<typename F>
- auto visitEl(F f)
+ auto visitEl(F f) const
{
return std::visit(f, this->m_element);
}
@@ -734,48 +924,55 @@ public:
return kind2domKind(m_kind);
}
- Path canonicalPath();
+ Path canonicalPath() const;
- DomItem filterUp(function_ref<bool(DomType k, DomItem &)> filter, FilterUpOptions options);
- DomItem containingObject();
- DomItem container();
- DomItem owner();
- DomItem top();
- DomItem environment();
- DomItem universe();
+ DomItem filterUp(function_ref<bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const;
+ DomItem containingObject() const;
+ DomItem container() const;
+ DomItem owner() const;
+ DomItem top() const;
+ DomItem environment() const;
+ DomItem universe() const;
+ DomItem containingFile() const;
+ DomItem containingScriptExpression() const;
+ DomItem goToFile(const QString &filePath) const;
+ DomItem goUp(int) const;
+ DomItem directParent() const;
DomItem qmlObject(GoTo option = GoTo::Strict,
- FilterUpOptions options = FilterUpOptions::ReturnOuter);
- DomItem fileObject(GoTo option = GoTo::Strict);
- DomItem rootQmlObject(GoTo option = GoTo::Strict);
- DomItem globalScope();
- DomItem component(GoTo option = GoTo::Strict);
- DomItem scope(FilterUpOptions options = FilterUpOptions::ReturnOuter);
+ FilterUpOptions options = FilterUpOptions::ReturnOuter) const;
+ DomItem fileObject(GoTo option = GoTo::Strict) const;
+ DomItem rootQmlObject(GoTo option = GoTo::Strict) const;
+ DomItem globalScope() const;
+ DomItem component(GoTo option = GoTo::Strict) const;
+ DomItem scope(FilterUpOptions options = FilterUpOptions::ReturnOuter) const;
+ QQmlJSScope::ConstPtr nearestSemanticScope() const;
+ QQmlJSScope::ConstPtr semanticScope() const;
// convenience getters
- DomItem get(ErrorHandler h = nullptr, QList<Path> *visitedRefs = nullptr);
- QList<DomItem> getAll(ErrorHandler h = nullptr, QList<Path> *visitedRefs = nullptr);
- bool isOwningItem() { return domTypeIsOwningItem(internalKind()); }
- bool isExternalItem() { return domTypeIsExternalItem(internalKind()); }
- bool isTopItem() { return domTypeIsTopItem(internalKind()); }
- bool isContainer() { return domTypeIsContainer(internalKind()); }
- bool isScope() { return domTypeIsScope(internalKind()); }
- bool isCanonicalChild(DomItem &child);
- bool hasAnnotations();
- QString name() { return field(Fields::name).value().toString(); }
- DomItem pragmas() { return field(Fields::pragmas); }
- DomItem ids() { return field(Fields::ids); }
- QString idStr() { return field(Fields::idStr).value().toString(); }
- DomItem propertyInfos() { return field(Fields::propertyInfos); }
- PropertyInfo propertyInfoWithName(QString name);
- QSet<QString> propertyInfoNames();
- DomItem propertyDefs() { return field(Fields::propertyDefs); }
- DomItem bindings() { return field(Fields::bindings); }
- DomItem methods() { return field(Fields::methods); }
- DomItem enumerations() { return field(Fields::enumerations); }
- DomItem children() { return field(Fields::children); }
- DomItem child(index_type i) { return field(Fields::children).index(i); }
- DomItem annotations()
+ DomItem get(const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
+ QList<DomItem> getAll(const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
+ bool isOwningItem() const { return domTypeIsOwningItem(internalKind()); }
+ bool isExternalItem() const { return domTypeIsExternalItem(internalKind()); }
+ bool isTopItem() const { return domTypeIsTopItem(internalKind()); }
+ bool isContainer() const { return domTypeIsContainer(internalKind()); }
+ bool isScope() const { return domTypeIsScope(internalKind()); }
+ bool isCanonicalChild(const DomItem &child) const;
+ bool hasAnnotations() const;
+ QString name() const { return field(Fields::name).value().toString(); }
+ DomItem pragmas() const { return field(Fields::pragmas); }
+ DomItem ids() const { return field(Fields::ids); }
+ QString idStr() const { return field(Fields::idStr).value().toString(); }
+ DomItem propertyInfos() const { return field(Fields::propertyInfos); }
+ PropertyInfo propertyInfoWithName(const QString &name) const;
+ QSet<QString> propertyInfoNames() const;
+ DomItem propertyDefs() const { return field(Fields::propertyDefs); }
+ DomItem bindings() const { return field(Fields::bindings); }
+ DomItem methods() const { return field(Fields::methods); }
+ DomItem enumerations() const { return field(Fields::enumerations); }
+ DomItem children() const { return field(Fields::children); }
+ DomItem child(index_type i) const { return field(Fields::children).index(i); }
+ DomItem annotations() const
{
if (hasAnnotations())
return field(Fields::annotations);
@@ -783,200 +980,214 @@ public:
return DomItem();
}
- bool resolve(Path path, Visitor visitor, ErrorHandler errorHandler,
- ResolveOptions options = ResolveOption::None, Path fullPath = Path(),
- QList<Path> *visitedRefs = nullptr);
+ bool resolve(const Path &path, Visitor visitor, const ErrorHandler &errorHandler,
+ ResolveOptions options = ResolveOption::None, const Path &fullPath = Path(),
+ QList<Path> *visitedRefs = nullptr) const;
- DomItem operator[](Path path);
- DomItem operator[](QStringView component);
- DomItem operator[](const QString &component);
- DomItem operator[](const char16_t *component)
+ DomItem operator[](const Path &path) const;
+ DomItem operator[](QStringView component) const;
+ DomItem operator[](const QString &component) const;
+ DomItem operator[](const char16_t *component) const
{
return (*this)[QStringView(component)];
} // to avoid clash with stupid builtin ptrdiff_t[DomItem&], coming from C
- DomItem operator[](index_type i) { return index(i); }
- DomItem operator[](int i) { return index(i); }
- index_type size() { return indexes() + keys().size(); }
- index_type length() { return size(); }
-
- DomItem path(Path p, ErrorHandler h = &defaultErrorHandler);
- DomItem path(QString p, ErrorHandler h = &defaultErrorHandler);
- DomItem path(QStringView p, ErrorHandler h = &defaultErrorHandler);
-
- QList<QString> fields();
- DomItem field(QStringView name);
-
- index_type indexes();
- DomItem index(index_type);
- bool visitIndexes(function_ref<bool(DomItem &)> visitor);
-
- QSet<QString> keys();
- QStringList sortedKeys();
- DomItem key(QString name);
- DomItem key(QStringView name) { return key(name.toString()); }
- bool visitKeys(function_ref<bool(QString, DomItem &)> visitor);
-
- QList<DomItem> values();
- void writeOutPre(OutWriter &lw);
- void writeOut(OutWriter &lw);
- void writeOutPost(OutWriter &lw);
- DomItem writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks);
- DomItem writeOut(QString path, int nBackups = 2,
- const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr,
- WriteOutChecks extraChecks = WriteOutCheck::Default);
-
- bool visitTree(Path basePath, ChildrenVisitor visitor,
+ DomItem operator[](index_type i) const { return index(i); }
+ DomItem operator[](int i) const { return index(i); }
+ index_type size() const { return indexes() + keys().size(); }
+ index_type length() const { return size(); }
+
+ DomItem path(const Path &p, const ErrorHandler &h = &defaultErrorHandler) const;
+ DomItem path(const QString &p, const ErrorHandler &h = &defaultErrorHandler) const;
+ DomItem path(QStringView p, const ErrorHandler &h = &defaultErrorHandler) const;
+
+ QList<QString> fields() const;
+ DomItem field(QStringView name) const;
+
+ index_type indexes() const;
+ DomItem index(index_type) const;
+ bool visitIndexes(function_ref<bool(const DomItem &)> visitor) const;
+
+ QSet<QString> keys() const;
+ QStringList sortedKeys() const;
+ DomItem key(const QString &name) const;
+ DomItem key(QStringView name) const { return key(name.toString()); }
+ bool visitKeys(function_ref<bool(const QString &, const DomItem &)> visitor) const;
+
+ QList<DomItem> values() const;
+ void writeOutPre(OutWriter &lw) const;
+ void writeOut(OutWriter &lw) const;
+ void writeOutPost(OutWriter &lw) const;
+ bool writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const;
+ bool writeOut(const QString &path, int nBackups = 2,
+ const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr,
+ WriteOutChecks extraChecks = WriteOutCheck::Default) const;
+
+ bool visitTree(const Path &basePath, ChildrenVisitor visitor,
VisitOptions options = VisitOption::Default,
ChildrenVisitor openingVisitor = emptyChildrenVisitor,
- ChildrenVisitor closingVisitor = emptyChildrenVisitor);
- bool visitPrototypeChain(function_ref<bool(DomItem &)> visitor,
+ ChildrenVisitor closingVisitor = emptyChildrenVisitor,
+ const FieldFilter &filter = FieldFilter::noFilter()) const;
+ bool visitPrototypeChain(function_ref<bool(const DomItem &)> visitor,
VisitPrototypesOptions options = VisitPrototypesOption::Normal,
- ErrorHandler h = nullptr, QSet<quintptr> *visited = nullptr,
- QList<Path> *visitedRefs = nullptr);
- bool visitDirectAccessibleScopes(function_ref<bool(DomItem &)> visitor,
+ const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr) const;
+ bool visitDirectAccessibleScopes(function_ref<bool(const DomItem &)> visitor,
VisitPrototypesOptions options = VisitPrototypesOption::Normal,
- ErrorHandler h = nullptr, QSet<quintptr> *visited = nullptr,
- QList<Path> *visitedRefs = nullptr);
+ const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr) const;
bool
- visitStaticTypePrototypeChains(function_ref<bool(DomItem &)> visitor,
+ visitStaticTypePrototypeChains(function_ref<bool(const DomItem &)> visitor,
VisitPrototypesOptions options = VisitPrototypesOption::Normal,
- ErrorHandler h = nullptr, QSet<quintptr> *visited = nullptr,
- QList<Path> *visitedRefs = nullptr);
- bool visitScopeChain(function_ref<bool(DomItem &)> visitor,
- LookupOptions = LookupOption::Normal, ErrorHandler h = nullptr,
- QSet<quintptr> *visited = nullptr, QList<Path> *visitedRefs = nullptr);
- bool visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)> visitor);
- QSet<QString> localSymbolNames(LocalSymbolsTypes lTypes = LocalSymbolsType::All);
- bool visitLookup1(QString symbolName, function_ref<bool(DomItem &)> visitor,
- LookupOptions = LookupOption::Normal, ErrorHandler h = nullptr,
- QSet<quintptr> *visited = nullptr, QList<Path> *visitedRefs = nullptr);
- bool visitLookup(QString symbolName, function_ref<bool(DomItem &)> visitor,
- LookupType type = LookupType::Symbol, LookupOptions = LookupOption::Normal,
- ErrorHandler errorHandler = nullptr, QSet<quintptr> *visited = nullptr,
- QList<Path> *visitedRefs = nullptr);
- bool visitSubSymbolsNamed(QString name, function_ref<bool(DomItem &)> visitor);
- DomItem proceedToScope(ErrorHandler h = nullptr, QList<Path> *visitedRefs = nullptr);
- QList<DomItem> lookup(QString symbolName, LookupType type = LookupType::Symbol,
- LookupOptions = LookupOption::Normal,
- ErrorHandler errorHandler = nullptr);
- DomItem lookupFirst(QString symbolName, LookupType type = LookupType::Symbol,
- LookupOptions = LookupOption::Normal, ErrorHandler errorHandler = nullptr);
-
- quintptr id();
- Path pathFromOwner();
- QString canonicalFilePath();
- DomItem fileLocationsTree();
- DomItem fileLocations();
- MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected);
- bool commitToBase(std::shared_ptr<DomEnvironment> validPtr = nullptr);
- DomItem refreshed() { return top().path(canonicalPath()); }
- QCborValue value();
-
- void dumpPtr(Sink sink);
- void dump(Sink, int indent = 0,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter =
- noFilter);
+ const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr) const;
+
+ bool visitUp(function_ref<bool(const DomItem &)> visitor) const;
+ bool visitScopeChain(
+ function_ref<bool(const DomItem &)> visitor, LookupOptions = LookupOption::Normal,
+ const ErrorHandler &h = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr) const;
+ bool visitLocalSymbolsNamed(
+ const QString &name, function_ref<bool(const DomItem &)> visitor) const;
+ bool visitLookup1(
+ const QString &symbolName, function_ref<bool(const DomItem &)> visitor,
+ LookupOptions = LookupOption::Normal, const ErrorHandler &h = nullptr,
+ QSet<quintptr> *visited = nullptr, QList<Path> *visitedRefs = nullptr) const;
+ bool visitLookup(
+ const QString &symbolName, function_ref<bool(const DomItem &)> visitor,
+ LookupType type = LookupType::Symbol, LookupOptions = LookupOption::Normal,
+ const ErrorHandler &errorHandler = nullptr, QSet<quintptr> *visited = nullptr,
+ QList<Path> *visitedRefs = nullptr) const;
+ bool visitSubSymbolsNamed(
+ const QString &name, function_ref<bool(const DomItem &)> visitor) const;
+ DomItem proceedToScope(
+ const ErrorHandler &h = nullptr, QList<Path> *visitedRefs = nullptr) const;
+ QList<DomItem> lookup(
+ const QString &symbolName, LookupType type = LookupType::Symbol,
+ LookupOptions = LookupOption::Normal, const ErrorHandler &errorHandler = nullptr) const;
+ DomItem lookupFirst(
+ const QString &symbolName, LookupType type = LookupType::Symbol,
+ LookupOptions = LookupOption::Normal, const ErrorHandler &errorHandler = nullptr) const;
+
+ quintptr id() const;
+ Path pathFromOwner() const;
+ QString canonicalFilePath() const;
+ DomItem fileLocationsTree() const;
+ DomItem fileLocations() const;
+ MutableDomItem makeCopy(CopyOption option = CopyOption::EnvConnected) const;
+ bool commitToBase(const std::shared_ptr<DomEnvironment> &validPtr = nullptr) const;
+ DomItem refreshed() const { return top().path(canonicalPath()); }
+ QCborValue value() const;
+
+ void dumpPtr(const Sink &sink) const;
+ void dump(const Sink &, int indent = 0,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter =
+ noFilter) const;
FileWriter::Status
- dump(QString path,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter,
- int nBackups = 2, int indent = 0, FileWriter *fw = nullptr);
- QString toString();
- QString toString() const
- {
- DomItem self = *this;
- return self.toString();
- }
+ dump(const QString &path,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter,
+ int nBackups = 2, int indent = 0, FileWriter *fw = nullptr) const;
+ QString toString() const;
// OwnigItem elements
- int derivedFrom();
- int revision();
- QDateTime createdAt();
- QDateTime frozenAt();
- QDateTime lastDataUpdateAt();
+ int derivedFrom() const;
+ int revision() const;
+ QDateTime createdAt() const;
+ QDateTime frozenAt() const;
+ QDateTime lastDataUpdateAt() const;
- void addError(ErrorMessage msg);
- ErrorHandler errorHandler();
- void clearErrors(ErrorGroups groups = ErrorGroups({}), bool iterate = true);
+ void addError(ErrorMessage &&msg) const;
+ ErrorHandler errorHandler() const;
+ void clearErrors(const ErrorGroups &groups = ErrorGroups({}), bool iterate = true) const;
// return false if a quick exit was requested
- bool iterateErrors(function_ref<bool(DomItem source, ErrorMessage msg)> visitor, bool iterate,
- Path inPath = Path());
+ bool iterateErrors(
+ function_ref<bool (const DomItem &, const ErrorMessage &)> visitor, bool iterate,
+ Path inPath = Path()) const;
- bool iterateSubOwners(function_ref<bool(DomItem &owner)> visitor);
- bool iterateDirectSubpaths(DirectVisitor v);
+ bool iterateSubOwners(function_ref<bool(const DomItem &owner)> visitor) const;
+ bool iterateDirectSubpaths(DirectVisitor v) const;
template<typename T>
- DomItem subDataItem(const PathEls::PathComponent &c, T value,
- ConstantData::Options options = ConstantData::Options::MapIsMap);
+ DomItem subDataItem(const PathEls::PathComponent &c, const T &value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap) const;
template<typename T>
- DomItem subDataItemField(QStringView f, T value,
- ConstantData::Options options = ConstantData::Options::MapIsMap)
+ DomItem subDataItemField(QStringView f, const T &value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap) const
{
return subDataItem(PathEls::Field(f), value, options);
}
template<typename T>
- DomItem subValueItem(const PathEls::PathComponent &c, T value,
- ConstantData::Options options = ConstantData::Options::MapIsMap);
+ DomItem subValueItem(const PathEls::PathComponent &c, const T &value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap) const;
template<typename T>
- bool dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, T value,
- ConstantData::Options options = ConstantData::Options::MapIsMap);
+ bool dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, const T &value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap) const;
template<typename T>
- bool dvValueField(DirectVisitor visitor, QStringView f, T value,
- ConstantData::Options options = ConstantData::Options::MapIsMap)
+ bool dvValueField(DirectVisitor visitor, QStringView f, const T &value,
+ ConstantData::Options options = ConstantData::Options::MapIsMap) const
{
- return this->dvValue<T>(visitor, PathEls::Field(f), value, options);
+ return this->dvValue<T>(std::move(visitor), PathEls::Field(f), value, options);
}
template<typename F>
bool dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF,
- ConstantData::Options options = ConstantData::Options::MapIsMap);
+ ConstantData::Options options = ConstantData::Options::MapIsMap) const;
template<typename F>
bool dvValueLazyField(DirectVisitor visitor, QStringView f, F valueF,
- ConstantData::Options options = ConstantData::Options::MapIsMap)
+ ConstantData::Options options = ConstantData::Options::MapIsMap) const
{
- return this->dvValueLazy(visitor, PathEls::Field(f), valueF, options);
+ return this->dvValueLazy(std::move(visitor), PathEls::Field(f), valueF, options);
}
- DomItem subLocationItem(const PathEls::PathComponent &c, SourceLocation loc,
- QStringView code = QStringView())
+ DomItem subLocationItem(const PathEls::PathComponent &c, SourceLocation loc) const
{
- return this->subDataItem(c, locationToData(loc, code));
+ return this->subDataItem(c, sourceLocationToQCborValue(loc));
}
// bool dvSubReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path
// referencedObject);
- DomItem subReferencesItem(const PathEls::PathComponent &c, QList<Path> paths);
- DomItem subReferenceItem(const PathEls::PathComponent &c, Path referencedObject);
- bool dvReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path referencedObject)
+ DomItem subReferencesItem(const PathEls::PathComponent &c, const QList<Path> &paths) const;
+ DomItem subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const;
+ bool dvReference(DirectVisitor visitor, const PathEls::PathComponent &c, const Path &referencedObject) const
{
- return dvItem(visitor, c, [c, this, referencedObject]() {
+ return dvItem(std::move(visitor), c, [c, this, referencedObject]() {
return this->subReferenceItem(c, referencedObject);
});
}
- bool dvReferences(DirectVisitor visitor, const PathEls::PathComponent &c, QList<Path> paths)
+ bool dvReferences(
+ DirectVisitor visitor, const PathEls::PathComponent &c, const QList<Path> &paths) const
{
- return dvItem(visitor, c, [c, this, paths]() { return this->subReferencesItem(c, paths); });
+ return dvItem(std::move(visitor), c, [c, this, paths]() {
+ return this->subReferencesItem(c, paths);
+ });
}
- bool dvReferenceField(DirectVisitor visitor, QStringView f, Path referencedObject)
+ bool dvReferenceField(DirectVisitor visitor, QStringView f, const Path &referencedObject) const
{
- return dvReference(visitor, PathEls::Field(f), referencedObject);
+ return dvReference(std::move(visitor), PathEls::Field(f), referencedObject);
}
- bool dvReferencesField(DirectVisitor visitor, QStringView f, QList<Path> paths)
+ bool dvReferencesField(DirectVisitor visitor, QStringView f, const QList<Path> &paths) const
{
- return dvReferences(visitor, PathEls::Field(f), paths);
+ return dvReferences(std::move(visitor), PathEls::Field(f), paths);
}
- bool dvItem(DirectVisitor visitor, const PathEls::PathComponent &c, function_ref<DomItem()> it)
+ bool dvItem(DirectVisitor visitor, const PathEls::PathComponent &c, function_ref<DomItem()> it) const
{
return visitor(c, it);
}
- bool dvItemField(DirectVisitor visitor, QStringView f, function_ref<DomItem()> it)
+ bool dvItemField(DirectVisitor visitor, QStringView f, function_ref<DomItem()> it) const
{
- return dvItem(visitor, PathEls::Field(f), it);
+ return dvItem(std::move(visitor), PathEls::Field(f), it);
}
- DomItem subListItem(const List &list);
- DomItem subMapItem(const Map &map);
- DomItem subObjectWrapItem(SimpleObjectWrap obj)
+ DomItem subListItem(const List &list) const;
+ DomItem subMapItem(const Map &map) const;
+ DomItem subObjectWrapItem(SimpleObjectWrap obj) const
{
return DomItem(m_top, m_owner, m_ownerPath, obj);
}
+
+ DomItem subScriptElementWrapperItem(const ScriptElementVariant &obj) const
+ {
+ Q_ASSERT(obj);
+ return DomItem(m_top, m_owner, m_ownerPath, ScriptElementDomWrapper(obj));
+ }
+
template<typename Owner>
- DomItem subOwnerItem(const PathEls::PathComponent &c, Owner o)
+ DomItem subOwnerItem(const PathEls::PathComponent &c, Owner o) const
{
if constexpr (domTypeIsUnattachedOwningItem(Owner::element_type::kindValue))
return DomItem(m_top, o, canonicalPath().appendComponent(c), o.get());
@@ -984,46 +1195,35 @@ public:
return DomItem(m_top, o, Path(), o.get());
}
template<typename T>
- DomItem wrap(const PathEls::PathComponent &c, T &obj);
+ DomItem wrap(const PathEls::PathComponent &c, const T &obj) const;
template<typename T>
- DomItem wrapField(QStringView f, T &obj)
+ DomItem wrapField(QStringView f, const T &obj) const
{
return wrap<T>(PathEls::Field(f), obj);
}
template<typename T>
- bool dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj);
+ bool dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj) const;
template<typename T>
- bool dvWrapField(DirectVisitor visitor, QStringView f, T &obj)
+ bool dvWrapField(DirectVisitor visitor, QStringView f, T &obj) const
{
- return dvWrap<T>(visitor, PathEls::Field(f), obj);
+ return dvWrap<T>(std::move(visitor), PathEls::Field(f), obj);
}
DomItem() = default;
- DomItem(std::shared_ptr<DomEnvironment>);
- DomItem(std::shared_ptr<DomUniverse>);
-
- static DomItem fromCode(QString code, DomType fileType = DomType::QmlFile);
- void loadFile(QString filePath, QString logicalPath,
- std::function<void(Path, DomItem &, DomItem &)> callback, LoadOptions loadOptions,
- std::optional<DomType> fileType = std::optional<DomType>());
- void loadFile(QString canonicalFilePath, QString logicalPath, QString code, QDateTime codeDate,
- std::function<void(Path, DomItem &, DomItem &)> callback, LoadOptions loadOptions,
- std::optional<DomType> fileType = std::optional<DomType>());
- void loadModuleDependency(QString uri, Version v,
- std::function<void(Path, DomItem &, DomItem &)> callback = nullptr,
- ErrorHandler = nullptr);
- void loadBuiltins(std::function<void(Path, DomItem &, DomItem &)> callback = nullptr,
- ErrorHandler = nullptr);
- void loadPendingDependencies();
+ DomItem(const std::shared_ptr<DomEnvironment> &);
+ DomItem(const std::shared_ptr<DomUniverse> &);
+
+ // TODO move to DomEnvironment?
+ static DomItem fromCode(const QString &code, DomType fileType = DomType::QmlFile);
// --- start of potentially dangerous stuff, make private? ---
- std::shared_ptr<DomTop> topPtr();
- std::shared_ptr<OwningItem> owningItemPtr();
+ std::shared_ptr<DomTop> topPtr() const;
+ std::shared_ptr<OwningItem> owningItemPtr() const;
// keep the DomItem around to ensure that it doesn't get deleted
template<typename T, typename std::enable_if<std::is_base_of_v<DomBase, T>, bool>::type = true>
- T const *as()
+ T const *as() const
{
if (m_kind == T::kindValue) {
if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
@@ -1035,7 +1235,7 @@ public:
}
template<typename T, typename std::enable_if<!std::is_base_of_v<DomBase, T>, bool>::type = true>
- T const *as()
+ T const *as() const
{
if (m_kind == T::kindValue) {
Q_ASSERT(domTypeIsObjWrap(m_kind) || domTypeIsValueWrap(m_kind));
@@ -1045,67 +1245,54 @@ public:
}
template<typename T>
- std::shared_ptr<T> ownerAs();
+ std::shared_ptr<T> ownerAs() const;
template<typename Owner, typename T>
- DomItem copy(Owner owner, Path ownerPath, T base)
+ DomItem copy(const Owner &owner, const Path &ownerPath, const T &base) const
{
- Q_ASSERT(m_top);
+ Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
static_assert(IsInlineDom<std::decay_t<T>>::value, "Expected an inline item or pointer");
return DomItem(m_top, owner, ownerPath, base);
}
template<typename Owner>
- DomItem copy(Owner owner, Path ownerPath)
+ DomItem copy(const Owner &owner, const Path &ownerPath) const
{
- Q_ASSERT(m_top);
+ Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
return DomItem(m_top, owner, ownerPath, owner.get());
}
template<typename T>
- DomItem copy(T base)
+ DomItem copy(const T &base) const
{
- Q_ASSERT(m_top);
+ Q_ASSERT(!std::holds_alternative<std::monostate>(m_top));
using BaseT = std::decay_t<T>;
static_assert(!std::is_same_v<BaseT, ElementT>,
"variant not supported, pass in the stored types");
- static_assert(IsInlineDom<BaseT>::value, "expected either a pointer or an inline item");
- if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
+ static_assert(IsInlineDom<BaseT>::value || std::is_same_v<BaseT, std::monostate>,
+ "expected either a pointer or an inline item");
+
+ if constexpr (IsSharedPointerToDomObject<BaseT>::value)
return DomItem(m_top, base, Path(), base.get());
- } else {
+ else if constexpr (IsInlineDom<BaseT>::value)
return DomItem(m_top, m_owner, m_ownerPath, base);
- }
+
+ Q_UNREACHABLE_RETURN(DomItem(m_top, m_owner, m_ownerPath, nullptr));
}
private:
- DomBase const *base();
- template <typename T, typename std::enable_if<std::is_base_of<DomBase, T>::value, bool>::type = true>
- T *mutableAs() {
- if (m_kind == T::kindValue) {
- if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
- return static_cast<SimpleObjectWrapBase *>(mutableBase())->mutableAs<T>();
- else
- return static_cast<T *>(mutableBase());
- }
- return nullptr;
- }
+ enum class WriteOutCheckResult { Success, Failed };
+ WriteOutCheckResult performWriteOutChecks(const DomItem &, const DomItem &, OutWriter &, WriteOutChecks) const;
+ const DomBase *base() const;
- template <typename T, typename std::enable_if<!std::is_base_of<DomBase, T>::value, bool>::type = true>
- T *mutableAs() {
- if (m_kind == T::kindValue) {
- Q_ASSERT(domTypeIsObjWrap(m_kind) || domTypeIsValueWrap(m_kind));
- return static_cast<SimpleObjectWrapBase *>(mutableBase())->mutableAs<T>();
- }
- return nullptr;
- }
- DomBase *mutableBase();
template<typename Env, typename Owner>
DomItem(Env, Owner, Path, std::nullptr_t) : DomItem()
{
}
+
template<typename Env, typename Owner, typename T,
typename = std::enable_if_t<IsInlineDom<std::decay_t<T>>::value>>
- DomItem(Env env, Owner owner, Path ownerPath, T el)
+ DomItem(Env env, Owner owner, const Path &ownerPath, const T &el)
: m_top(env), m_owner(owner), m_ownerPath(ownerPath), m_element(el)
{
using BaseT = std::decay_t<T>;
@@ -1113,8 +1300,8 @@ private:
if (!el || el->kind() == DomType::Empty) { // avoid null ptr, and allow only a
// single kind of Empty
m_kind = DomType::Empty;
- m_top.reset();
- m_owner.reset();
+ m_top = std::monostate();
+ m_owner = std::monostate();
m_ownerPath = Path();
m_element = Empty();
} else {
@@ -1144,8 +1331,8 @@ private:
friend class TestDomItem;
friend QMLDOM_EXPORT bool operator==(const DomItem &, const DomItem &);
DomType m_kind = DomType::Empty;
- std::optional<TopT> m_top;
- std::optional<OwnerT> m_owner;
+ TopT m_top;
+ OwnerT m_owner;
Path m_ownerPath;
ElementT m_element = Empty();
};
@@ -1158,123 +1345,179 @@ inline bool operator!=(const DomItem &o1, const DomItem &o2)
}
template<typename T>
-Map Map::fromMultiMapRef(Path pathFromOwner, QMultiMap<QString, T> &mmap)
+static DomItem keyMultiMapHelper(const DomItem &self, const QString &key,
+ const QMultiMap<QString, T> &mmap)
+{
+ auto it = mmap.find(key);
+ auto end = mmap.cend();
+ if (it == end)
+ return DomItem();
+ else {
+ // special case single element (++it == end || it.key() != key)?
+ QList<const T *> values;
+ while (it != end && it.key() == key)
+ values.append(&(*it++));
+ ListP ll(self.pathFromOwner().appendComponent(PathEls::Key(key)), values, QString(),
+ ListOptions::Reverse);
+ return self.copy(ll);
+ }
+}
+
+template<typename T>
+Map Map::fromMultiMapRef(const Path &pathFromOwner, const QMultiMap<QString, T> &mmap)
{
return Map(
pathFromOwner,
- [&mmap](DomItem &self, QString key) {
- auto it = mmap.find(key);
- auto end = mmap.cend();
- if (it == end)
- return DomItem();
- else {
- // special case single element (++it == end || it.key() != key)?
- QList<T *> values;
- while (it != end && it.key() == key)
- values.append(&(*it++));
- ListP ll(self.pathFromOwner().appendComponent(PathEls::Key(key)), values,
- QString(), ListOptions::Reverse);
- return self.copy(ll);
- }
+ [&mmap](const DomItem &self, const QString &key) {
+ return keyMultiMapHelper(self, key, mmap);
},
- [&mmap](DomItem &) { return QSet<QString>(mmap.keyBegin(), mmap.keyEnd()); },
+ [&mmap](const DomItem &) { return QSet<QString>(mmap.keyBegin(), mmap.keyEnd()); },
QLatin1String(typeid(T).name()));
}
template<typename T>
Map Map::fromMapRef(
- Path pathFromOwner, QMap<QString, T> &map,
- std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper)
+ const Path &pathFromOwner, const QMap<QString, T> &map,
+ const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper)
{
return Map(
pathFromOwner,
- [&map, elWrapper](DomItem &self, QString key) {
- if (!map.contains(key))
+ [&map, elWrapper](const DomItem &self, const QString &key) {
+ const auto it = map.constFind(key);
+ if (it == map.constEnd())
return DomItem();
- else {
- return elWrapper(self, PathEls::Key(key), map[key]);
- }
+ return elWrapper(self, PathEls::Key(key), it.value());
},
- [&map](DomItem &) { return QSet<QString>(map.keyBegin(), map.keyEnd()); },
+ [&map](const DomItem &) { return QSet<QString>(map.keyBegin(), map.keyEnd()); },
QLatin1String(typeid(T).name()));
}
+template<typename MapT>
+QSet<QString> Map::fileRegionKeysFromMap(const MapT &map)
+{
+ QSet<QString> keys;
+ std::transform(map.keyBegin(), map.keyEnd(), std::inserter(keys, keys.begin()), fileLocationRegionName);
+ return keys;
+}
+
+template<typename T>
+Map Map::fromFileRegionMap(const Path &pathFromOwner, const QMap<FileLocationRegion, T> &map)
+{
+ auto result = Map(
+ pathFromOwner,
+ [&map](const DomItem &mapItem, const QString &key) -> DomItem {
+ auto it = map.constFind(fileLocationRegionValue(key));
+ if (it == map.constEnd())
+ return {};
+
+ return mapItem.wrap(PathEls::Key(key), *it);
+ },
+ [&map](const DomItem &) { return fileRegionKeysFromMap(map); },
+ QString::fromLatin1(typeid(T).name()));
+ return result;
+}
+
+template<typename T>
+Map Map::fromFileRegionListMap(const Path &pathFromOwner,
+ const QMap<FileLocationRegion, QList<T>> &map)
+{
+ using namespace Qt::StringLiterals;
+ auto result = Map(
+ pathFromOwner,
+ [&map](const DomItem &mapItem, const QString &key) -> DomItem {
+ const QList<SourceLocation> locations = map.value(fileLocationRegionValue(key));
+ if (locations.empty())
+ return {};
+
+ auto list = List::fromQList<SourceLocation>(
+ mapItem.pathFromOwner(), locations,
+ [](const DomItem &self, const PathEls::PathComponent &path,
+ const SourceLocation &location) {
+ return self.subLocationItem(path, location);
+ });
+ return mapItem.subListItem(list);
+ },
+ [&map](const DomItem &) { return fileRegionKeysFromMap(map); },
+ u"QList<%1>"_s.arg(QString::fromLatin1(typeid(T).name())));
+ return result;
+}
+
template<typename T>
List List::fromQList(
- Path pathFromOwner, QList<T> list,
- std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper,
+ const Path &pathFromOwner, const QList<T> &list,
+ const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
ListOptions options)
{
index_type len = list.size();
if (options == ListOptions::Reverse) {
return List(
pathFromOwner,
- [list, elWrapper](DomItem &self, index_type i) mutable {
+ [list, elWrapper](const DomItem &self, index_type i) mutable {
if (i < 0 || i >= list.size())
return DomItem();
return elWrapper(self, PathEls::Index(i), list[list.size() - i - 1]);
},
- [len](DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
+ [len](const DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
} else {
return List(
pathFromOwner,
- [list, elWrapper](DomItem &self, index_type i) mutable {
+ [list, elWrapper](const DomItem &self, index_type i) mutable {
if (i < 0 || i >= list.size())
return DomItem();
return elWrapper(self, PathEls::Index(i), list[i]);
},
- [len](DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
+ [len](const DomItem &) { return len; }, nullptr, QLatin1String(typeid(T).name()));
}
}
template<typename T>
List List::fromQListRef(
- Path pathFromOwner, QList<T> &list,
- std::function<DomItem(DomItem &, const PathEls::PathComponent &, T &)> elWrapper,
+ const Path &pathFromOwner, const QList<T> &list,
+ const std::function<DomItem(const DomItem &, const PathEls::PathComponent &, const T &)> &elWrapper,
ListOptions options)
{
if (options == ListOptions::Reverse) {
return List(
pathFromOwner,
- [&list, elWrapper](DomItem &self, index_type i) {
+ [&list, elWrapper](const DomItem &self, index_type i) {
if (i < 0 || i >= list.size())
return DomItem();
return elWrapper(self, PathEls::Index(i), list[list.size() - i - 1]);
},
- [&list](DomItem &) { return list.size(); }, nullptr,
+ [&list](const DomItem &) { return list.size(); }, nullptr,
QLatin1String(typeid(T).name()));
} else {
return List(
pathFromOwner,
- [&list, elWrapper](DomItem &self, index_type i) {
+ [&list, elWrapper](const DomItem &self, index_type i) {
if (i < 0 || i >= list.size())
return DomItem();
return elWrapper(self, PathEls::Index(i), list[i]);
},
- [&list](DomItem &) { return list.size(); }, nullptr,
+ [&list](const DomItem &) { return list.size(); }, nullptr,
QLatin1String(typeid(T).name()));
}
}
class QMLDOM_EXPORT OwningItem: public DomBase {
protected:
- virtual std::shared_ptr<OwningItem> doCopy(DomItem &self) const = 0;
+ virtual std::shared_ptr<OwningItem> doCopy(const DomItem &self) const = 0;
public:
OwningItem(const OwningItem &o);
OwningItem(int derivedFrom=0);
- OwningItem(int derivedFrom, QDateTime lastDataUpdateAt);
+ OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt);
OwningItem(const OwningItem &&) = delete;
OwningItem &operator=(const OwningItem &&) = delete;
static int nextRevision();
- Path canonicalPath(DomItem &self) const override = 0;
+ Path canonicalPath(const DomItem &self) const override = 0;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- std::shared_ptr<OwningItem> makeCopy(DomItem &self) const { return doCopy(self); }
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ std::shared_ptr<OwningItem> makeCopy(const DomItem &self) const { return doCopy(self); }
Path pathFromOwner() const { return Path(); }
- Path pathFromOwner(DomItem &) const override final { return Path(); }
- DomItem containingObject(DomItem &self) const override;
+ Path pathFromOwner(const DomItem &) const override final { return Path(); }
+ DomItem containingObject(const DomItem &self) const override;
int derivedFrom() const;
virtual int revision() const;
@@ -1287,18 +1530,20 @@ public:
virtual bool freeze();
QDateTime frozenAt() const;
- virtual void addError(DomItem &self, ErrorMessage msg);
- void addErrorLocal(ErrorMessage msg);
- void clearErrors(ErrorGroups groups = ErrorGroups({}));
+ virtual void addError(const DomItem &self, ErrorMessage &&msg);
+ void addErrorLocal(ErrorMessage &&msg);
+ void clearErrors(const ErrorGroups &groups = ErrorGroups({}));
// return false if a quick exit was requested
- bool iterateErrors(DomItem &self, function_ref<bool(DomItem source, ErrorMessage msg)> visitor,
- Path inPath = Path());
+ bool iterateErrors(
+ const DomItem &self,
+ function_ref<bool(const DomItem &source, const ErrorMessage &msg)> visitor,
+ const Path &inPath = Path());
QMultiMap<Path, ErrorMessage> localErrors() const {
QMutexLocker l(mutex());
return m_errors;
}
- virtual bool iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor);
+ virtual bool iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor);
QBasicMutex *mutex() const { return &m_mutex; }
private:
@@ -1313,25 +1558,25 @@ private:
};
template<typename T>
-std::shared_ptr<T> DomItem::ownerAs()
+std::shared_ptr<T> DomItem::ownerAs() const
{
if constexpr (domTypeIsOwningItem(T::kindValue)) {
- if (m_owner) {
+ if (!std::holds_alternative<std::monostate>(m_owner)) {
if constexpr (T::kindValue == DomType::AttachedInfo) {
- if (std::holds_alternative<std::shared_ptr<AttachedInfo>>(*m_owner))
+ if (std::holds_alternative<std::shared_ptr<AttachedInfo>>(m_owner))
return std::static_pointer_cast<T>(
- std::get<std::shared_ptr<AttachedInfo>>(*m_owner));
+ std::get<std::shared_ptr<AttachedInfo>>(m_owner));
} else if constexpr (T::kindValue == DomType::ExternalItemInfo) {
- if (std::holds_alternative<std::shared_ptr<ExternalItemInfoBase>>(*m_owner))
+ if (std::holds_alternative<std::shared_ptr<ExternalItemInfoBase>>(m_owner))
return std::static_pointer_cast<T>(
- std::get<std::shared_ptr<ExternalItemInfoBase>>(*m_owner));
+ std::get<std::shared_ptr<ExternalItemInfoBase>>(m_owner));
} else if constexpr (T::kindValue == DomType::ExternalItemPair) {
- if (std::holds_alternative<std::shared_ptr<ExternalItemPairBase>>(*m_owner))
+ if (std::holds_alternative<std::shared_ptr<ExternalItemPairBase>>(m_owner))
return std::static_pointer_cast<T>(
- std::get<std::shared_ptr<ExternalItemPairBase>>(*m_owner));
+ std::get<std::shared_ptr<ExternalItemPairBase>>(m_owner));
} else {
- if (std::holds_alternative<std::shared_ptr<T>>(*m_owner)) {
- return std::get<std::shared_ptr<T>>(*m_owner);
+ if (std::holds_alternative<std::shared_ptr<T>>(m_owner)) {
+ return std::get<std::shared_ptr<T>>(m_owner);
}
}
}
@@ -1352,26 +1597,26 @@ struct rank<0>
};
template<typename T>
-auto writeOutWrap(const T &t, DomItem &self, OutWriter &lw, rank<1>)
+auto writeOutWrap(const T &t, const DomItem &self, OutWriter &lw, rank<1>)
-> decltype(t.writeOut(self, lw))
{
t.writeOut(self, lw);
}
template<typename T>
-auto writeOutWrap(const T &, DomItem &, OutWriter &, rank<0>) -> void
+auto writeOutWrap(const T &, const DomItem &, OutWriter &, rank<0>) -> void
{
qCWarning(writeOutLog) << "Ignoring writeout to wrapped object not supporting it ("
<< typeid(T).name();
}
template<typename T>
-auto writeOutWrap(const T &t, DomItem &self, OutWriter &lw) -> void
+auto writeOutWrap(const T &t, const DomItem &self, OutWriter &lw) -> void
{
writeOutWrap(t, self, lw, rank<1>());
}
template<typename T>
-void SimpleObjectWrapT<T>::writeOut(DomItem &self, OutWriter &lw) const
+void SimpleObjectWrapT<T>::writeOut(const DomItem &self, OutWriter &lw) const
{
writeOutWrap<T>(*asT(), self, lw);
}
@@ -1390,7 +1635,7 @@ public:
QString internalKindStr() { return domTypeToString(internalKind()); }
DomKind domKind() { return kind2domKind(internalKind()); }
- Path canonicalPath() { return m_owner.canonicalPath().path(m_pathFromOwner); }
+ Path canonicalPath() const { return m_owner.canonicalPath().path(m_pathFromOwner); }
MutableDomItem containingObject()
{
if (m_pathFromOwner)
@@ -1458,28 +1703,27 @@ public:
MutableDomItem index(index_type i) { return MutableDomItem(item().index(i)); }
QSet<QString> const keys() { return item().keys(); }
- MutableDomItem key(QString name) { return MutableDomItem(item().key(name)); }
+ MutableDomItem key(const QString &name) { return MutableDomItem(item().key(name)); }
MutableDomItem key(QStringView name) { return key(name.toString()); }
void
- dump(Sink s, int indent = 0,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter)
+ dump(const Sink &s, int indent = 0,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter)
{
item().dump(s, indent, filter);
}
FileWriter::Status
- dump(QString path,
- function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter = noFilter,
+ dump(const QString &path,
+ function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter = noFilter,
int nBackups = 2, int indent = 0, FileWriter *fw = nullptr)
{
return item().dump(path, filter, nBackups, indent, fw);
}
void writeOut(OutWriter &lw) { return item().writeOut(lw); }
- MutableDomItem writeOut(QString path, int nBackups = 2,
- const LineWriterOptions &opt = LineWriterOptions(),
- FileWriter *fw = nullptr)
+ bool writeOut(const QString &path, int nBackups = 2,
+ const LineWriterOptions &opt = LineWriterOptions(), FileWriter *fw = nullptr)
{
- return MutableDomItem(item().writeOut(path, nBackups, opt, fw));
+ return item().writeOut(path, nBackups, opt, fw);
}
MutableDomItem fileLocations() { return MutableDomItem(item().fileLocations()); }
@@ -1487,11 +1731,11 @@ public:
{
return item().makeCopy(option);
}
- bool commitToBase(std::shared_ptr<DomEnvironment> validEnvPtr = nullptr)
+ bool commitToBase(const std::shared_ptr<DomEnvironment> &validEnvPtr = nullptr)
{
return item().commitToBase(validEnvPtr);
}
- QString canonicalFilePath() { return item().canonicalFilePath(); }
+ QString canonicalFilePath() const { return item().canonicalFilePath(); }
MutableDomItem refreshed() { return MutableDomItem(item().refreshed()); }
@@ -1518,41 +1762,36 @@ public:
QDateTime frozenAt() { return m_owner.frozenAt(); }
QDateTime lastDataUpdateAt() { return m_owner.lastDataUpdateAt(); }
- void addError(ErrorMessage msg) { item().addError(msg); }
+ void addError(ErrorMessage &&msg) { item().addError(std::move(msg)); }
ErrorHandler errorHandler();
// convenience setters
- MutableDomItem addPrototypePath(Path prototypePath);
- MutableDomItem setNextScopePath(Path nextScopePath);
+ MutableDomItem addPrototypePath(const Path &prototypePath);
+ MutableDomItem setNextScopePath(const Path &nextScopePath);
MutableDomItem setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs);
MutableDomItem setBindings(QMultiMap<QString, Binding> bindings);
MutableDomItem setMethods(QMultiMap<QString, MethodInfo> functionDefs);
- MutableDomItem setChildren(QList<QmlObject> children);
- MutableDomItem setAnnotations(QList<QmlObject> annotations);
- MutableDomItem setScript(std::shared_ptr<ScriptExpression> exp);
- MutableDomItem setCode(QString code);
- MutableDomItem addPropertyDef(PropertyDefinition propertyDef,
+ MutableDomItem setChildren(const QList<QmlObject> &children);
+ MutableDomItem setAnnotations(const QList<QmlObject> &annotations);
+ MutableDomItem setScript(const std::shared_ptr<ScriptExpression> &exp);
+ MutableDomItem setCode(const QString &code);
+ MutableDomItem addPropertyDef(const PropertyDefinition &propertyDef,
AddOption option = AddOption::Overwrite);
MutableDomItem addBinding(Binding binding, AddOption option = AddOption::Overwrite);
- MutableDomItem addMethod(MethodInfo functionDef, AddOption option = AddOption::Overwrite);
+ MutableDomItem addMethod(
+ const MethodInfo &functionDef, AddOption option = AddOption::Overwrite);
MutableDomItem addChild(QmlObject child);
MutableDomItem addAnnotation(QmlObject child);
- MutableDomItem addPreComment(const Comment &comment, QString regionName = QString());
- MutableDomItem addPreComment(const Comment &comment, QStringView regionName)
- {
- return addPreComment(comment, regionName.toString());
- }
- MutableDomItem addPostComment(const Comment &comment, QString regionName = QString());
- MutableDomItem addPostComment(const Comment &comment, QStringView regionName)
- {
- return addPostComment(comment, regionName.toString());
- }
+ MutableDomItem addPreComment(const Comment &comment, FileLocationRegion region);
+ MutableDomItem addPostComment(const Comment &comment, FileLocationRegion region);
+ QQmlJSScope::ConstPtr semanticScope();
+ void setSemanticScope(const QQmlJSScope::ConstPtr &scope);
MutableDomItem() = default;
- MutableDomItem(DomItem owner, Path pathFromOwner):
+ MutableDomItem(const DomItem &owner, const Path &pathFromOwner):
m_owner(owner), m_pathFromOwner(pathFromOwner)
{}
- MutableDomItem(DomItem item):
+ MutableDomItem(const DomItem &item):
m_owner(item.owner()), m_pathFromOwner(item.pathFromOwner())
{}
@@ -1568,18 +1807,32 @@ public:
template <typename T>
T *mutableAs() {
Q_ASSERT(!m_owner || !m_owner.owningItemPtr()->frozen());
- return item().mutableAs<T>();
+
+ DomItem self = item();
+ if (self.m_kind != T::kindValue)
+ return nullptr;
+
+ const T *t = nullptr;
+ if constexpr (domTypeIsObjWrap(T::kindValue) || domTypeIsValueWrap(T::kindValue))
+ t = static_cast<const SimpleObjectWrapBase *>(self.base())->as<T>();
+ else if constexpr (std::is_base_of<DomBase, T>::value)
+ t = static_cast<const T *>(self.base());
+ else
+ Q_UNREACHABLE_RETURN(nullptr);
+
+ // Nasty. But since ElementT has to store the const pointers, we allow it in this one place.
+ return const_cast<T *>(t);
}
template<typename T>
- std::shared_ptr<T> ownerAs()
+ std::shared_ptr<T> ownerAs() const
{
return m_owner.ownerAs<T>();
}
// it is dangerous to assume it stays valid when updates are preformed...
- DomItem item() { return m_owner.path(m_pathFromOwner); }
+ DomItem item() const { return m_owner.path(m_pathFromOwner); }
- friend bool operator==(const MutableDomItem o1, const MutableDomItem &o2)
+ friend bool operator==(const MutableDomItem &o1, const MutableDomItem &o2)
{
return o1.m_owner == o2.m_owner && o1.m_pathFromOwner == o2.m_pathFromOwner;
}
@@ -1596,7 +1849,7 @@ private:
QMLDOM_EXPORT QDebug operator<<(QDebug debug, const MutableDomItem &c);
template<typename K, typename T>
-Path insertUpdatableElementInMultiMap(Path mapPathFromOwner, QMultiMap<K, T> &mmap, K key,
+Path insertUpdatableElementInMultiMap(const Path &mapPathFromOwner, QMultiMap<K, T> &mmap, K key,
const T &value, AddOption option = AddOption::KeepExisting,
T **valuePtr = nullptr)
{
@@ -1633,7 +1886,7 @@ Path insertUpdatableElementInMultiMap(Path mapPathFromOwner, QMultiMap<K, T> &mm
}
template<typename T>
-Path appendUpdatableElementInQList(Path listPathFromOwner, QList<T> &list, const T &value,
+Path appendUpdatableElementInQList(const Path &listPathFromOwner, QList<T> &list, const T &value,
T **vPtr = nullptr)
{
int idx = list.size();
@@ -1647,7 +1900,7 @@ Path appendUpdatableElementInQList(Path listPathFromOwner, QList<T> &list, const
}
template <typename T, typename K = QString>
-void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, Path newPath)
+void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, const Path &newPath)
{
auto it = mmap.begin();
auto end = mmap.end();
@@ -1676,7 +1929,7 @@ void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, Path newPath)
}
template <typename T>
-void updatePathFromOwnerQList(QList<T> &list, Path newPath)
+void updatePathFromOwnerQList(QList<T> &list, const Path &newPath)
{
auto it = list.begin();
auto end = list.end();
@@ -1788,9 +2041,14 @@ constexpr bool domTypeIsUnattachedOwningItem(DomType k)
}
}
+constexpr bool domTypeIsScriptElement(DomType k)
+{
+ return DomType::ScriptElementStart <= k && k <= DomType::ScriptElementStop;
+}
+
template<typename T>
-DomItem DomItem::subValueItem(const PathEls::PathComponent &c, T value,
- ConstantData::Options options)
+DomItem DomItem::subValueItem(const PathEls::PathComponent &c, const T &value,
+ ConstantData::Options options) const
{
using BaseT = std::remove_cv_t<std::remove_reference_t<T>>;
if constexpr (
@@ -1805,8 +2063,8 @@ DomItem DomItem::subValueItem(const PathEls::PathComponent &c, T value,
} else if constexpr (IsList<T>::value && !std::is_convertible_v<BaseT, QStringView>) {
return subListItem(List::fromQList<typename BaseT::value_type>(
pathFromOwner().appendComponent(c), value,
- [options](DomItem &list, const PathEls::PathComponent &p,
- typename T::value_type &v) { return list.subValueItem(p, v, options); }));
+ [options](const DomItem &list, const PathEls::PathComponent &p,
+ const typename T::value_type &v) { return list.subValueItem(p, v, options); }));
} else if constexpr (IsSharedPointerToDomObject<BaseT>::value) {
Q_UNUSED(options);
return subOwnerItem(c, value);
@@ -1816,8 +2074,8 @@ DomItem DomItem::subValueItem(const PathEls::PathComponent &c, T value,
}
template<typename T>
-DomItem DomItem::subDataItem(const PathEls::PathComponent &c, T value,
- ConstantData::Options options)
+DomItem DomItem::subDataItem(const PathEls::PathComponent &c, const T &value,
+ ConstantData::Options options) const
{
using BaseT = std::remove_cv_t<std::remove_reference_t<T>>;
if constexpr (std::is_same_v<BaseT, ConstantData>) {
@@ -1833,8 +2091,8 @@ DomItem DomItem::subDataItem(const PathEls::PathComponent &c, T value,
}
template<typename T>
-bool DomItem::dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, T value,
- ConstantData::Options options)
+bool DomItem::dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, const T &value,
+ ConstantData::Options options) const
{
auto lazyWrap = [this, &c, &value, options]() {
return this->subValueItem<T>(c, value, options);
@@ -1844,7 +2102,7 @@ bool DomItem::dvValue(DirectVisitor visitor, const PathEls::PathComponent &c, T
template<typename F>
bool DomItem::dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c, F valueF,
- ConstantData::Options options)
+ ConstantData::Options options) const
{
auto lazyWrap = [this, &c, &valueF, options]() {
return this->subValueItem<decltype(valueF())>(c, valueF(), options);
@@ -1853,7 +2111,7 @@ bool DomItem::dvValueLazy(DirectVisitor visitor, const PathEls::PathComponent &c
}
template<typename T>
-DomItem DomItem::wrap(const PathEls::PathComponent &c, T &obj)
+DomItem DomItem::wrap(const PathEls::PathComponent &c, const T &obj) const
{
using BaseT = std::decay_t<T>;
if constexpr (std::is_same_v<QString, BaseT> || std::is_arithmetic_v<BaseT>) {
@@ -1904,8 +2162,8 @@ DomItem DomItem::wrap(const PathEls::PathComponent &c, T &obj)
if constexpr (std::is_same_v<typename BaseT::key_type, QString>) {
return subMapItem(Map::fromMapRef<typename BaseT::mapped_type>(
pathFromOwner().appendComponent(c), obj,
- [](DomItem &map, const PathEls::PathComponent &p,
- typename BaseT::mapped_type &el) { return map.wrap(p, el); }));
+ [](const DomItem &map, const PathEls::PathComponent &p,
+ const typename BaseT::mapped_type &el) { return map.wrap(p, el); }));
} else {
Q_ASSERT_X(false, "DomItem::wrap", "non string keys not supported (try .toString()?)");
}
@@ -1913,8 +2171,8 @@ DomItem DomItem::wrap(const PathEls::PathComponent &c, T &obj)
if constexpr (IsDomObject<typename BaseT::value_type>::value) {
return subListItem(List::fromQListRef<typename BaseT::value_type>(
pathFromOwner().appendComponent(c), obj,
- [](DomItem &list, const PathEls::PathComponent &p,
- typename BaseT::value_type &el) { return list.wrap(p, el); }));
+ [](const DomItem &list, const PathEls::PathComponent &p,
+ const typename BaseT::value_type &el) { return list.wrap(p, el); }));
} else {
Q_ASSERT_X(false, "DomItem::wrap", "Unsupported list type T");
return DomItem();
@@ -1927,14 +2185,14 @@ DomItem DomItem::wrap(const PathEls::PathComponent &c, T &obj)
}
template<typename T>
-bool DomItem::dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj)
+bool DomItem::dvWrap(DirectVisitor visitor, const PathEls::PathComponent &c, T &obj) const
{
auto lazyWrap = [this, &c, &obj]() { return this->wrap<T>(c, obj); };
return visitor(c, lazyWrap);
}
template<typename T>
-bool ListPT<T>::iterateDirectSubpaths(DomItem &self, DirectVisitor v)
+bool ListPT<T>::iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const
{
index_type len = index_type(m_pList.size());
for (index_type i = 0; i < len; ++i) {
@@ -1945,10 +2203,10 @@ bool ListPT<T>::iterateDirectSubpaths(DomItem &self, DirectVisitor v)
}
template<typename T>
-DomItem ListPT<T>::index(DomItem &self, index_type index) const
+DomItem ListPT<T>::index(const DomItem &self, index_type index) const
{
if (index >= 0 && index < m_pList.size())
- return self.wrap(PathEls::Index(index), *reinterpret_cast<T *>(m_pList.value(index)));
+ return self.wrap(PathEls::Index(index), *static_cast<const T *>(m_pList.value(index)));
return DomItem();
}
@@ -1958,13 +2216,13 @@ inline DomKind DomBase::domKind() const
return kind2domKind(kind());
}
-inline bool DomBase::iterateDirectSubpathsConst(DomItem &self, DirectVisitor visitor) const
+inline bool DomBase::iterateDirectSubpathsConst(const DomItem &self, DirectVisitor visitor) const
{
Q_ASSERT(self.base() == this);
- return self.iterateDirectSubpaths(visitor);
+ return self.iterateDirectSubpaths(std::move(visitor));
}
-inline DomItem DomBase::containingObject(DomItem &self) const
+inline DomItem DomBase::containingObject(const DomItem &self) const
{
Path path = pathFromOwner(self);
DomItem base = self.owner();
@@ -1986,7 +2244,7 @@ inline QString DomBase::typeName() const
return domTypeToString(kind());
}
-inline QList<QString> DomBase::fields(DomItem &self) const
+inline QList<QString> DomBase::fields(const DomItem &self) const
{
QList<QString> res;
self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
@@ -1997,7 +2255,7 @@ inline QList<QString> DomBase::fields(DomItem &self) const
return res;
}
-inline DomItem DomBase::field(DomItem &self, QStringView name) const
+inline DomItem DomBase::field(const DomItem &self, QStringView name) const
{
DomItem res;
self.iterateDirectSubpaths(
@@ -2011,7 +2269,7 @@ inline DomItem DomBase::field(DomItem &self, QStringView name) const
return res;
}
-inline index_type DomBase::indexes(DomItem &self) const
+inline index_type DomBase::indexes(const DomItem &self) const
{
index_type res = 0;
self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
@@ -2025,7 +2283,7 @@ inline index_type DomBase::indexes(DomItem &self) const
return res;
}
-inline DomItem DomBase::index(DomItem &self, qint64 index) const
+inline DomItem DomBase::index(const DomItem &self, qint64 index) const
{
DomItem res;
self.iterateDirectSubpaths(
@@ -2039,7 +2297,7 @@ inline DomItem DomBase::index(DomItem &self, qint64 index) const
return res;
}
-inline QSet<QString> const DomBase::keys(DomItem &self) const
+inline QSet<QString> const DomBase::keys(const DomItem &self) const
{
QSet<QString> res;
self.iterateDirectSubpaths([&res](const PathEls::PathComponent &c, function_ref<DomItem()>) {
@@ -2050,7 +2308,7 @@ inline QSet<QString> const DomBase::keys(DomItem &self) const
return res;
}
-inline DomItem DomBase::key(DomItem &self, QString name) const
+inline DomItem DomBase::key(const DomItem &self, const QString &name) const
{
DomItem res;
self.iterateDirectSubpaths(
@@ -2064,12 +2322,12 @@ inline DomItem DomBase::key(DomItem &self, QString name) const
return res;
}
-inline DomItem DomItem::subListItem(const List &list)
+inline DomItem DomItem::subListItem(const List &list) const
{
return DomItem(m_top, m_owner, m_ownerPath, list);
}
-inline DomItem DomItem::subMapItem(const Map &map)
+inline DomItem DomItem::subMapItem(const Map &map) const
{
return DomItem(m_top, m_owner, m_ownerPath, map);
}
diff --git a/src/qmldom/qqmldomlinewriter.cpp b/src/qmldom/qqmldomlinewriter.cpp
index 3200f4f9c5..33326cb01a 100644
--- a/src/qmldom/qqmldomlinewriter.cpp
+++ b/src/qmldom/qqmldomlinewriter.cpp
@@ -50,8 +50,9 @@ void PendingSourceLocation::commit()
updater(value);
}
-LineWriter::LineWriter(SinkF innerSink, QString fileName, const LineWriterOptions &options,
- int lineNr, int columnNr, int utf16Offset, QString currentLine)
+LineWriter::LineWriter(
+ const SinkF &innerSink, const QString &fileName, const LineWriterOptions &options,
+ int lineNr, int columnNr, int utf16Offset, const QString &currentLine)
: m_innerSinks({ innerSink }),
m_fileName(fileName),
m_lineNr(lineNr),
@@ -320,7 +321,7 @@ void LineWriter::handleTrailingSpace(LineWriterOptions::TrailingSpace trailingSp
}
}
-void LineWriter::reindentAndSplit(QString eol, bool eof)
+void LineWriter::reindentAndSplit(const QString &eol, bool eof)
{
// maybe write out
if (!eol.isEmpty() || eof) {
@@ -370,7 +371,7 @@ void LineWriter::textAddCallback(LineWriter::TextAddType t)
}
}
-void LineWriter::commitLine(QString eol, TextAddType tType, int untilChar)
+void LineWriter::commitLine(const QString &eol, TextAddType tType, int untilChar)
{
if (untilChar == -1)
untilChar = m_currentLine.size();
diff --git a/src/qmldom/qqmldomlinewriter_p.h b/src/qmldom/qqmldomlinewriter_p.h
index eb858f5b7d..86da8d6f54 100644
--- a/src/qmldom/qqmldomlinewriter_p.h
+++ b/src/qmldom/qqmldomlinewriter_p.h
@@ -88,7 +88,11 @@ public:
int maxLineLength = -1;
int strongMaxLineExtra = 20;
int minContentLength = 10;
+#if defined (Q_OS_WIN)
+ LineEndings lineEndings = LineEndings::Windows;
+#else
LineEndings lineEndings = LineEndings::Unix;
+#endif
TrailingSpace codeTrailingSpace = TrailingSpace::Remove;
TrailingSpace commentTrailingSpace = TrailingSpace::Remove;
TrailingSpace stringTrailingSpace = TrailingSpace::Preserve;
@@ -100,7 +104,8 @@ public:
};
Q_DECLARE_OPERATORS_FOR_FLAGS(LineWriterOptions::Updates)
-using PendingSourceLocationId = QAtomicInt;
+using PendingSourceLocationId = int;
+using PendingSourceLocationIdAtomic = QAtomicInt;
class LineWriter;
class QMLDOM_EXPORT PendingSourceLocation
@@ -132,9 +137,9 @@ public:
Eof
};
- LineWriter(SinkF innerSink, QString fileName,
+ LineWriter(const SinkF &innerSink, const QString &fileName,
const LineWriterOptions &options = LineWriterOptions(), int lineNr = 0,
- int columnNr = 0, int utf16Offset = 0, QString currentLine = QString());
+ int columnNr = 0, int utf16Offset = 0, const QString &currentLine = QString());
std::function<void(QStringView)> sink()
{
return [this](QStringView s) { this->write(s); };
@@ -143,7 +148,7 @@ public:
virtual ~LineWriter() { }
QList<SinkF> innerSinks() { return m_innerSinks; }
- void addInnerSink(SinkF s) { m_innerSinks.append(s); }
+ void addInnerSink(const SinkF &s) { m_innerSinks.append(s); }
LineWriter &ensureNewline(int nNewlines = 1, TextAddType t = TextAddType::Extra);
LineWriter &ensureSpace(TextAddType t = TextAddType::Extra);
LineWriter &ensureSpace(QStringView space, TextAddType t = TextAddType::Extra);
@@ -166,7 +171,7 @@ public:
endSourceLocation(pLoc);
return *this;
}
- void commitLine(QString eol, TextAddType t = TextAddType::Normal, int untilChar = -1);
+ void commitLine(const QString &eol, TextAddType t = TextAddType::Normal, int untilChar = -1);
void flush();
void eof(bool ensureNewline = true);
SourceLocation committedLocation() const;
@@ -183,7 +188,7 @@ public:
const QString &currentLine() const { return m_currentLine; }
const LineWriterOptions &options() const { return m_options; }
virtual void lineChanged() { }
- virtual void reindentAndSplit(QString eol, bool eof = false);
+ virtual void reindentAndSplit(const QString &eol, bool eof = false);
virtual void willCommit() { }
private:
@@ -205,7 +210,7 @@ protected:
int m_utf16Offset = 0; // utf16 offset since start for committed data
QString m_currentLine;
LineWriterOptions m_options;
- PendingSourceLocationId m_lastSourceLocationId;
+ PendingSourceLocationIdAtomic m_lastSourceLocationId;
QMap<PendingSourceLocationId, PendingSourceLocation> m_pendingSourceLocations;
QAtomicInt m_lastCallbackId;
QMap<int, std::function<bool(LineWriter &, TextAddType)>> m_textAddCallbacks;
diff --git a/src/qmldom/qqmldommock.cpp b/src/qmldom/qqmldommock.cpp
index 84f9452f9d..df49b9baaf 100644
--- a/src/qmldom/qqmldommock.cpp
+++ b/src/qmldom/qqmldommock.cpp
@@ -32,11 +32,11 @@ std::pair<QString, MockObject> MockObject::asStringPair() const
return std::make_pair(pathFromOwner().last().headName(), *this);
}
-bool MockObject::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool MockObject::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
static QHash<QString, QString> knownFields;
static QBasicMutex m;
- auto toField = [](QString f) -> QStringView {
+ auto toField = [](const QString &f) -> QStringView {
QMutexLocker l(&m);
if (!knownFields.contains(f))
knownFields[f] = f;
@@ -60,7 +60,7 @@ bool MockObject::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-std::shared_ptr<OwningItem> MockOwner::doCopy(DomItem &) const
+std::shared_ptr<OwningItem> MockOwner::doCopy(const DomItem &) const
{
return std::make_shared<MockOwner>(*this);
}
@@ -77,21 +77,21 @@ MockOwner::MockOwner(const MockOwner &o)
}
}
-std::shared_ptr<MockOwner> MockOwner::makeCopy(DomItem &self) const
+std::shared_ptr<MockOwner> MockOwner::makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<MockOwner>(doCopy(self));
}
-Path MockOwner::canonicalPath(DomItem &) const
+Path MockOwner::canonicalPath(const DomItem &) const
{
return pathFromTop;
}
-bool MockOwner::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool MockOwner::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
static QHash<QString, QString> knownFields;
static QBasicMutex m;
- auto toField = [](QString f) -> QStringView {
+ auto toField = [](const QString &f) -> QStringView {
QMutexLocker l(&m);
if (!knownFields.contains(f))
knownFields[f] = f;
diff --git a/src/qmldom/qqmldommock_p.h b/src/qmldom/qqmldommock_p.h
index b35d4d2e1c..97504cc631 100644
--- a/src/qmldom/qqmldommock_p.h
+++ b/src/qmldom/qqmldommock_p.h
@@ -43,7 +43,7 @@ public:
constexpr static DomType kindValue = DomType::MockObject;
DomType kind() const override { return kindValue; }
- MockObject(Path pathFromOwner = Path(), QMap<QString, MockObject> subObjects = {},
+ MockObject(const Path &pathFromOwner = Path(), QMap<QString, MockObject> subObjects = {},
QMap<QString, QCborValue> subValues = {})
: CommentableDomElement(pathFromOwner), subObjects(subObjects), subValues(subValues)
{
@@ -52,7 +52,7 @@ public:
MockObject copy() const;
std::pair<QString, MockObject> asStringPair() const;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
QMap<QString, MockObject> subObjects;
QMap<QString, QCborValue> subValues;
@@ -62,13 +62,13 @@ public:
class MockOwner final : public OwningItem
{
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
public:
constexpr static DomType kindValue = DomType::MockOwner;
DomType kind() const override { return kindValue; }
- MockOwner(Path pathFromTop = Path(), int derivedFrom = 0,
+ MockOwner(const Path &pathFromTop = Path(), int derivedFrom = 0,
QMap<QString, MockObject> subObjects = {}, QMap<QString, QCborValue> subValues = {},
QMap<QString, QMap<QString, MockObject>> subMaps = {},
QMap<QString, QMultiMap<QString, MockObject>> subMultiMaps = {},
@@ -83,7 +83,7 @@ public:
{
}
- MockOwner(Path pathFromTop, int derivedFrom, QDateTime dataRefreshedAt,
+ MockOwner(const Path &pathFromTop, int derivedFrom, QDateTime dataRefreshedAt,
QMap<QString, MockObject> subObjects = {}, QMap<QString, QCborValue> subValues = {})
: OwningItem(derivedFrom, dataRefreshedAt),
pathFromTop(pathFromTop),
@@ -94,10 +94,10 @@ public:
MockOwner(const MockOwner &o);
- std::shared_ptr<MockOwner> makeCopy(DomItem &self) const;
- Path canonicalPath(DomItem &self) const override;
+ std::shared_ptr<MockOwner> makeCopy(const DomItem &self) const;
+ Path canonicalPath(const DomItem &self) const override;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
Path pathFromTop;
QMap<QString, MockObject> subObjects;
diff --git a/src/qmldom/qqmldommoduleindex.cpp b/src/qmldom/qqmldommoduleindex.cpp
index 3ba7814189..d44c9ae003 100644
--- a/src/qmldom/qqmldommoduleindex.cpp
+++ b/src/qmldom/qqmldommoduleindex.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmldomtop_p.h"
#include "qqmldomelements_p.h"
+#include "qqmldom_utils_p.h"
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
@@ -26,7 +27,7 @@ static ErrorGroups myExportErrors()
return res;
}
-bool ModuleScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ModuleScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.dvValueField(visitor, Fields::uri, uri);
@@ -35,19 +36,19 @@ bool ModuleScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
int minorVersion = version.minorVersion;
return self.subMapItem(Map(
self.pathFromOwner().field(Fields::exports),
- [minorVersion](DomItem &mapExp, QString name) -> DomItem {
+ [minorVersion](const DomItem &mapExp, const QString &name) -> DomItem {
DomItem mapExpOw = mapExp.owner();
QList<DomItem> exports =
mapExp.ownerAs<ModuleIndex>()->exportsWithNameAndMinorVersion(
mapExpOw, name, minorVersion);
return mapExp.subListItem(List::fromQList<DomItem>(
mapExp.pathFromOwner().key(name), exports,
- [](DomItem &, const PathEls::PathComponent &, DomItem &el) {
+ [](const DomItem &, const PathEls::PathComponent &, const DomItem &el) {
return el;
},
ListOptions::Normal));
},
- [](DomItem &mapExp) {
+ [](const DomItem &mapExp) {
DomItem mapExpOw = mapExp.owner();
return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
},
@@ -57,11 +58,11 @@ bool ModuleScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
Path basePath = Path::Current(PathCurrent::Obj).field(Fields::exports);
return self.subMapItem(Map(
self.pathFromOwner().field(Fields::symbols),
- [basePath](DomItem &mapExp, QString name) -> DomItem {
+ [basePath](const DomItem &mapExp, const QString &name) -> DomItem {
QList<Path> symb({ basePath.key(name) });
return mapExp.subReferencesItem(PathEls::Key(name), symb);
},
- [](DomItem &mapExp) {
+ [](const DomItem &mapExp) {
DomItem mapExpOw = mapExp.owner();
return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
},
@@ -73,7 +74,7 @@ bool ModuleScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-std::shared_ptr<OwningItem> ModuleIndex::doCopy(DomItem &) const
+std::shared_ptr<OwningItem> ModuleIndex::doCopy(const DomItem &) const
{
return std::make_shared<ModuleIndex>(*this);
}
@@ -113,14 +114,14 @@ ModuleIndex::~ModuleIndex()
}
}
-bool ModuleIndex::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ModuleIndex::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = self.dvValueField(visitor, Fields::uri, uri());
cont = cont && self.dvValueField(visitor, Fields::majorVersion, majorVersion());
cont = cont && self.dvItemField(visitor, Fields::moduleScope, [this, &self]() {
return self.subMapItem(Map(
pathFromOwner(self).field(Fields::moduleScope),
- [](DomItem &map, QString minorVersionStr) {
+ [](const DomItem &map, const QString &minorVersionStr) {
bool ok;
int minorVersion = minorVersionStr.toInt(&ok);
if (minorVersionStr.isEmpty()
@@ -130,7 +131,7 @@ bool ModuleIndex::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return DomItem();
return map.copy(map.ownerAs<ModuleIndex>()->ensureMinorVersion(minorVersion));
},
- [this](DomItem &) {
+ [this](const DomItem &) {
QSet<QString> res;
for (int el : minorVersions())
if (el >= 0)
@@ -150,7 +151,7 @@ bool ModuleIndex::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-QSet<QString> ModuleIndex::exportNames(DomItem &self) const
+QSet<QString> ModuleIndex::exportNames(const DomItem &self) const
{
QSet<QString> res;
QList<Path> mySources = sources();
@@ -161,7 +162,7 @@ QSet<QString> ModuleIndex::exportNames(DomItem &self) const
return res;
}
-QList<DomItem> ModuleIndex::autoExports(DomItem &self) const
+QList<DomItem> ModuleIndex::autoExports(const DomItem &self) const
{
QList<DomItem> res;
Path selfPath = canonicalPath(self).field(Fields::autoExports);
@@ -179,7 +180,7 @@ QList<DomItem> ModuleIndex::autoExports(DomItem &self) const
DomItem env = self.environment();
if (!cachedPaths.isEmpty()) {
bool outdated = false;
- for (Path p : cachedPaths) {
+ for (const Path &p : cachedPaths) {
DomItem newEl = env.path(p);
if (!newEl) {
outdated = true;
@@ -199,9 +200,9 @@ QList<DomItem> ModuleIndex::autoExports(DomItem &self) const
QList<Path> mySources = sources();
QSet<QString> knownAutoImportUris;
QList<ModuleAutoExport> knownExports;
- for (Path p : mySources) {
+ for (const Path &p : mySources) {
DomItem autoExports = self.path(p).field(Fields::autoExports);
- for (DomItem i : autoExports.values()) {
+ for (const DomItem &i : autoExports.values()) {
if (const ModuleAutoExport *iPtr = i.as<ModuleAutoExport>()) {
if (!knownAutoImportUris.contains(iPtr->import.uri.toString())
|| !knownExports.contains(*iPtr)) {
@@ -218,7 +219,7 @@ QList<DomItem> ModuleIndex::autoExports(DomItem &self) const
return res;
}
-QList<DomItem> ModuleIndex::exportsWithNameAndMinorVersion(DomItem &self, QString name,
+QList<DomItem> ModuleIndex::exportsWithNameAndMinorVersion(const DomItem &self, const QString &name,
int minorVersion) const
{
Path myPath = Paths::moduleScopePath(uri(), Version(majorVersion(), minorVersion))
@@ -245,12 +246,12 @@ QList<DomItem> ModuleIndex::exportsWithNameAndMinorVersion(DomItem &self, QStrin
undef.append(exportItem);
} else {
if (majorVersion() < 0)
- self.addError(myVersioningErrors()
+ self.addError(std::move(myVersioningErrors()
.error(tr("Module %1 (unversioned) has versioned entries "
"for '%2' from %3")
.arg(uri(), name,
source.canonicalPath().toString()))
- .withPath(myPath));
+ .withPath(myPath)));
if ((versionPtr->majorVersion == majorVersion()
|| versionPtr->majorVersion == Version::Undefined)
&& versionPtr->minorVersion >= vNow
@@ -265,11 +266,11 @@ QList<DomItem> ModuleIndex::exportsWithNameAndMinorVersion(DomItem &self, QStrin
}
if (!undef.isEmpty()) {
if (!res.isEmpty()) {
- self.addError(myVersioningErrors()
+ self.addError(std::move(myVersioningErrors()
.error(tr("Module %1 (major version %2) has versioned and "
"unversioned entries for '%3'")
.arg(uri(), QString::number(majorVersion()), name))
- .withPath(myPath));
+ .withPath(myPath)));
return res + undef;
} else {
return undef;
@@ -296,8 +297,8 @@ ModuleScope *ModuleIndex::ensureMinorVersion(int minorVersion)
minorVersion = Version::Latest;
{
QMutexLocker l(mutex());
- auto it = m_moduleScope.find(minorVersion);
- if (it != m_moduleScope.end())
+ auto it = m_moduleScope.constFind(minorVersion);
+ if (it != m_moduleScope.cend())
return *it;
}
ModuleScope *res = nullptr;
@@ -305,8 +306,8 @@ ModuleScope *ModuleIndex::ensureMinorVersion(int minorVersion)
auto cleanup = qScopeGuard([&newScope] { delete newScope; });
{
QMutexLocker l(mutex());
- auto it = m_moduleScope.find(minorVersion);
- if (it != m_moduleScope.end()) {
+ auto it = m_moduleScope.constFind(minorVersion);
+ if (it != m_moduleScope.cend()) {
res = *it;
} else {
res = newScope;
@@ -317,7 +318,7 @@ ModuleScope *ModuleIndex::ensureMinorVersion(int minorVersion)
return res;
}
-void ModuleIndex::mergeWith(std::shared_ptr<ModuleIndex> o)
+void ModuleIndex::mergeWith(const std::shared_ptr<ModuleIndex> &o)
{
if (o) {
QList<Path> qmltypesPaths;
@@ -329,7 +330,7 @@ void ModuleIndex::mergeWith(std::shared_ptr<ModuleIndex> o)
}
{
QMutexLocker l(mutex());
- for (Path qttPath : qmltypesPaths) {
+ for (const Path &qttPath : qmltypesPaths) {
if (!m_qmltypesFilesPaths.contains((qttPath)))
m_qmltypesFilesPaths.append(qttPath);
}
@@ -343,7 +344,7 @@ void ModuleIndex::mergeWith(std::shared_ptr<ModuleIndex> o)
}
}
-QList<Path> ModuleIndex::qmldirsToLoad(DomItem &self)
+QList<Path> ModuleIndex::qmldirsToLoad(const DomItem &self)
{
// this always checks the filesystem to the qmldir file to load
DomItem env = self.environment();
@@ -355,10 +356,15 @@ QList<Path> ModuleIndex::qmldirsToLoad(DomItem &self)
+ QLatin1String("/qmldir");
QString dirPath;
if (majorVersion() >= 0) {
- for (QString path : envPtr->loadPaths()) {
+ qCDebug(QQmlJSDomImporting)
+ << "ModuleIndex::qmldirsToLoad: Searching versioned module" << subPath
+ << majorVersion() << "in" << envPtr->loadPaths().join(u", ");
+ for (const QString &path : envPtr->loadPaths()) {
QDir dir(path);
QFileInfo fInfo(dir.filePath(subPathV));
if (fInfo.isFile()) {
+ qCDebug(QQmlJSDomImporting)
+ << "Found versioned module in " << fInfo.canonicalFilePath();
logicalPath = subPathV;
dirPath = fInfo.canonicalFilePath();
break;
@@ -366,10 +372,14 @@ QList<Path> ModuleIndex::qmldirsToLoad(DomItem &self)
}
}
if (dirPath.isEmpty()) {
- for (QString path : envPtr->loadPaths()) {
+ qCDebug(QQmlJSDomImporting) << "ModuleIndex::qmldirsToLoad: Searching unversioned module"
+ << subPath << "in" << envPtr->loadPaths().join(u", ");
+ for (const QString &path : envPtr->loadPaths()) {
QDir dir(path);
QFileInfo fInfo(dir.filePath(subPath + QLatin1String("/qmldir")));
if (fInfo.isFile()) {
+ qCDebug(QQmlJSDomImporting)
+ << "Found unversioned module in " << fInfo.canonicalFilePath();
logicalPath = subPath + QLatin1String("/qmldir");
dirPath = fInfo.canonicalFilePath();
break;
@@ -380,10 +390,14 @@ QList<Path> ModuleIndex::qmldirsToLoad(DomItem &self)
QMutexLocker l(mutex());
m_qmldirPaths = QList<Path>({ Paths::qmldirFilePath(dirPath) });
} else if (uri() != u"QML") {
- addErrorLocal(myExportErrors()
- .warning(tr("Failed to find main qmldir file for %1 %2")
- .arg(uri(), QString::number(majorVersion())))
- .handle());
+ const QString loadPaths = envPtr->loadPaths().join(u", "_s);
+ qCDebug(QQmlJSDomImporting) << "ModuleIndex::qmldirsToLoad: qmldir at"
+ << (uri() + u"/qmldir"_s) << " was not found in " << loadPaths;
+ addErrorLocal(
+ myExportErrors()
+ .warning(tr("Failed to find main qmldir file for %1 %2 in %3.")
+ .arg(uri(), QString::number(majorVersion()), loadPaths))
+ .handle());
}
return qmldirPaths();
}
diff --git a/src/qmldom/qqmldommoduleindex_p.h b/src/qmldom/qqmldommoduleindex_p.h
index 1d4d9be1df..f2b2731624 100644
--- a/src/qmldom/qqmldommoduleindex_p.h
+++ b/src/qmldom/qqmldommoduleindex_p.h
@@ -28,7 +28,7 @@ public:
constexpr static DomType kindValue = DomType::ModuleScope;
DomType kind() const override { return kindValue; }
- ModuleScope(QString uri = QString(), const Version &version = Version())
+ ModuleScope(const QString &uri = QString(), const Version &version = Version())
: uri(uri), version(version)
{
}
@@ -38,12 +38,12 @@ public:
return Path::Field(Fields::moduleScope)
.key(version.isValid() ? QString::number(version.minorVersion) : QString());
}
- Path pathFromOwner(DomItem &) const override { return pathFromOwner(); }
- Path canonicalPath(DomItem &self) const override
+ Path pathFromOwner(const DomItem &) const override { return pathFromOwner(); }
+ Path canonicalPath(const DomItem &self) const override
{
return self.owner().canonicalPath().path(pathFromOwner());
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
QString uri;
Version version;
@@ -54,15 +54,16 @@ class QMLDOM_EXPORT ModuleIndex final : public OwningItem
Q_DECLARE_TR_FUNCTIONS(ModuleIndex);
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
public:
enum class Status { NotLoaded, Loading, Loaded };
constexpr static DomType kindValue = DomType::ModuleIndex;
DomType kind() const override { return kindValue; }
- ModuleIndex(QString uri, int majorVersion, int derivedFrom = 0,
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
+ ModuleIndex(
+ const QString &uri, int majorVersion, int derivedFrom = 0,
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
: OwningItem(derivedFrom, lastDataUpdateAt), m_uri(uri), m_majorVersion(majorVersion)
{
}
@@ -71,21 +72,21 @@ public:
~ModuleIndex();
- std::shared_ptr<ModuleIndex> makeCopy(DomItem &self) const
+ std::shared_ptr<ModuleIndex> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<ModuleIndex>(doCopy(self));
}
- Path canonicalPath(DomItem &) const override
+ Path canonicalPath(const DomItem &) const override
{
return Paths::moduleIndexPath(uri(), majorVersion());
}
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
- QSet<QString> exportNames(DomItem &self) const;
+ QSet<QString> exportNames(const DomItem &self) const;
- QList<DomItem> exportsWithNameAndMinorVersion(DomItem &self, QString name,
+ QList<DomItem> exportsWithNameAndMinorVersion(const DomItem &self, const QString &name,
int minorVersion) const;
QString uri() const { return m_uri; }
@@ -98,15 +99,15 @@ public:
return m_moduleScope.keys();
}
ModuleScope *ensureMinorVersion(int minorVersion);
- void mergeWith(std::shared_ptr<ModuleIndex> o);
- void addQmltypeFilePath(Path p)
+ void mergeWith(const std::shared_ptr<ModuleIndex> &o);
+ void addQmltypeFilePath(const Path &p)
{
QMutexLocker l(mutex());
if (!m_qmltypesFilesPaths.contains(p))
m_qmltypesFilesPaths.append(p);
}
- QList<Path> qmldirsToLoad(DomItem &self);
+ QList<Path> qmldirsToLoad(const DomItem &self);
QList<Path> qmltypesFilesPaths() const
{
QMutexLocker l(mutex());
@@ -122,7 +123,7 @@ public:
QMutexLocker l(mutex());
return m_directoryPaths;
}
- QList<DomItem> autoExports(DomItem &self) const;
+ QList<DomItem> autoExports(const DomItem &self) const;
private:
QString m_uri;
diff --git a/src/qmldom/qqmldomoutwriter.cpp b/src/qmldom/qqmldomoutwriter.cpp
index 7bdbf505e2..7f4f219cbf 100644
--- a/src/qmldom/qqmldomoutwriter.cpp
+++ b/src/qmldom/qqmldomoutwriter.cpp
@@ -15,14 +15,13 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-OutWriterState::OutWriterState(Path itCanonicalPath, DomItem &it, FileLocations::Tree fLoc)
+OutWriterState::OutWriterState(
+ const Path &itCanonicalPath, const DomItem &it, const FileLocations::Tree &fLoc)
: itemCanonicalPath(itCanonicalPath), item(it), currentMap(fLoc)
{
DomItem cRegions = it.field(Fields::comments);
- if (const RegionComments *cRegionsPtr = cRegions.as<RegionComments>()) {
- pendingComments = cRegionsPtr->regionComments;
- fLoc->info().ensureCommentLocations(pendingComments.keys());
- }
+ if (const RegionComments *cRegionsPtr = cRegions.as<RegionComments>())
+ pendingComments = cRegionsPtr->regionComments();
}
void OutWriterState::closeState(OutWriter &w)
@@ -50,7 +49,7 @@ OutWriterState &OutWriter::state(int i)
return states[states.size() - 1 - i];
}
-void OutWriter::itemStart(DomItem &it)
+void OutWriter::itemStart(const DomItem &it)
{
if (!topLocation->path())
topLocation->setPath(it.canonicalPath());
@@ -74,135 +73,301 @@ void OutWriter::itemStart(DomItem &it)
if (updateLocs)
state().fullRegionId = lineWriter.startSourceLocation(
[newFLoc](SourceLocation l) { FileLocations::updateFullLocation(newFLoc, l); });
- regionStart(QString());
+ regionStart(MainRegion);
}
-void OutWriter::itemEnd(DomItem &it)
+void OutWriter::itemEnd(const DomItem &it)
{
Q_ASSERT(states.size() > 0);
Q_ASSERT(state().item == it);
- regionEnd(QString());
+ regionEnd(MainRegion);
state().closeState(*this);
states.removeLast();
}
-void OutWriter::regionStart(QString rName)
+void OutWriter::regionStart(FileLocationRegion region)
{
- Q_ASSERT(!state().pendingRegions.contains(rName));
+ Q_ASSERT(!state().pendingRegions.contains(region));
FileLocations::Tree fMap = state().currentMap;
- if (!skipComments && state().pendingComments.contains(rName)) {
+ if (!skipComments && state().pendingComments.contains(region)) {
bool updateLocs = lineWriter.options().updateOptions & LineWriterOptions::Update::Locations;
QList<SourceLocation> *cLocs =
- (updateLocs ? &(fMap->info().preCommentLocations[rName]) : nullptr);
- state().pendingComments[rName].writePre(*this, cLocs);
+ (updateLocs ? &(fMap->info().preCommentLocations[region]) : nullptr);
+ state().pendingComments[region].writePre(*this, cLocs);
}
- state().pendingRegions[rName] = lineWriter.startSourceLocation(
- [rName, fMap](SourceLocation l) { FileLocations::addRegion(fMap, rName, l); });
+ state().pendingRegions[region] = lineWriter.startSourceLocation(
+ [region, fMap](SourceLocation l) { FileLocations::addRegion(fMap, region, l); });
}
-void OutWriter::regionEnd(QString rName)
+void OutWriter::regionEnd(FileLocationRegion region)
{
- Q_ASSERT(state().pendingRegions.contains(rName));
+ Q_ASSERT(state().pendingRegions.contains(region));
FileLocations::Tree fMap = state().currentMap;
- lineWriter.endSourceLocation(state().pendingRegions.value(rName));
- state().pendingRegions.remove(rName);
- if (state().pendingComments.contains(rName)) {
+ lineWriter.endSourceLocation(state().pendingRegions.value(region));
+ state().pendingRegions.remove(region);
+ if (state().pendingComments.contains(region)) {
if (!skipComments) {
bool updateLocs =
lineWriter.options().updateOptions & LineWriterOptions::Update::Locations;
QList<SourceLocation> *cLocs =
- (updateLocs ? &(fMap->info().postCommentLocations[rName]) : nullptr);
- state().pendingComments[rName].writePost(*this, cLocs);
+ (updateLocs ? &(fMap->info().postCommentLocations[region]) : nullptr);
+ state().pendingComments[region].writePost(*this, cLocs);
}
- state().pendingComments.remove(rName);
+ state().pendingComments.remove(region);
}
}
-OutWriter &OutWriter::writeRegion(QString rName, QStringView toWrite)
+/*!
+\internal
+Helper method for writeRegion(FileLocationRegion region) that allows to use
+\c{writeRegion(ColonTokenRegion);} instead of having to write out the more error-prone
+\c{writeRegion(ColonTokenRegion, ":");} for tokens and keywords.
+*/
+OutWriter &OutWriter::writeRegion(FileLocationRegion region)
{
- regionStart(rName);
+ QString codeForRegion;
+ switch (region) {
+ case ComponentKeywordRegion:
+ codeForRegion = u"component"_s;
+ break;
+ case IdColonTokenRegion:
+ case ColonTokenRegion:
+ codeForRegion = u":"_s;
+ break;
+ case ImportTokenRegion:
+ codeForRegion = u"import"_s;
+ break;
+ case AsTokenRegion:
+ codeForRegion = u"as"_s;
+ break;
+ case OnTokenRegion:
+ codeForRegion = u"on"_s;
+ break;
+ case IdTokenRegion:
+ codeForRegion = u"id"_s;
+ break;
+ case LeftBraceRegion:
+ codeForRegion = u"{"_s;
+ break;
+ case RightBraceRegion:
+ codeForRegion = u"}"_s;
+ break;
+ case LeftBracketRegion:
+ codeForRegion = u"["_s;
+ break;
+ case RightBracketRegion:
+ codeForRegion = u"]"_s;
+ break;
+ case LeftParenthesisRegion:
+ codeForRegion = u"("_s;
+ break;
+ case RightParenthesisRegion:
+ codeForRegion = u")"_s;
+ break;
+ case EnumKeywordRegion:
+ codeForRegion = u"enum"_s;
+ break;
+ case DefaultKeywordRegion:
+ codeForRegion = u"default"_s;
+ break;
+ case RequiredKeywordRegion:
+ codeForRegion = u"required"_s;
+ break;
+ case ReadonlyKeywordRegion:
+ codeForRegion = u"readonly"_s;
+ break;
+ case PropertyKeywordRegion:
+ codeForRegion = u"property"_s;
+ break;
+ case FunctionKeywordRegion:
+ codeForRegion = u"function"_s;
+ break;
+ case SignalKeywordRegion:
+ codeForRegion = u"signal"_s;
+ break;
+ case ReturnKeywordRegion:
+ codeForRegion = u"return"_s;
+ break;
+ case EllipsisTokenRegion:
+ codeForRegion = u"..."_s;
+ break;
+ case EqualTokenRegion:
+ codeForRegion = u"="_s;
+ break;
+ case PragmaKeywordRegion:
+ codeForRegion = u"pragma"_s;
+ break;
+ case CommaTokenRegion:
+ codeForRegion = u","_s;
+ break;
+ case ForKeywordRegion:
+ codeForRegion = u"for"_s;
+ break;
+ case ElseKeywordRegion:
+ codeForRegion = u"else"_s;
+ break;
+ case DoKeywordRegion:
+ codeForRegion = u"do"_s;
+ break;
+ case WhileKeywordRegion:
+ codeForRegion = u"while"_s;
+ break;
+ case TryKeywordRegion:
+ codeForRegion = u"try"_s;
+ break;
+ case CatchKeywordRegion:
+ codeForRegion = u"catch"_s;
+ break;
+ case FinallyKeywordRegion:
+ codeForRegion = u"finally"_s;
+ break;
+ case CaseKeywordRegion:
+ codeForRegion = u"case"_s;
+ break;
+ case ThrowKeywordRegion:
+ codeForRegion = u"throw"_s;
+ break;
+ case ContinueKeywordRegion:
+ codeForRegion = u"continue"_s;
+ break;
+ case BreakKeywordRegion:
+ codeForRegion = u"break"_s;
+ break;
+ case QuestionMarkTokenRegion:
+ codeForRegion = u"?"_s;
+ break;
+ case SemicolonTokenRegion:
+ codeForRegion = u";"_s;
+ break;
+ case IfKeywordRegion:
+ codeForRegion = u"if"_s;
+ break;
+ case SwitchKeywordRegion:
+ codeForRegion = u"switch"_s;
+ break;
+ // not keywords:
+ case ImportUriRegion:
+ case IdNameRegion:
+ case IdentifierRegion:
+ case PragmaValuesRegion:
+ case MainRegion:
+ case OnTargetRegion:
+ case TypeIdentifierRegion:
+ case FirstSemicolonTokenRegion:
+ case SecondSemicolonRegion:
+ case InOfTokenRegion:
+ case OperatorTokenRegion:
+ case VersionRegion:
+ case EnumValueRegion:
+ Q_ASSERT_X(false, "regionToString", "Using regionToString on a value or an identifier!");
+ return *this;
+ }
+
+ return writeRegion(region, codeForRegion);
+}
+
+OutWriter &OutWriter::writeRegion(FileLocationRegion region, QStringView toWrite)
+{
+ regionStart(region);
lineWriter.write(toWrite);
- regionEnd(rName);
+ regionEnd(region);
return *this;
}
+/*!
+ \internal
+ Restores written out FileItem using intermediate information saved during DOM traversal.
+ It enables verifying DOM consistency of the written item later.
-DomItem OutWriter::updatedFile(DomItem &qmlFile)
+ At the moment of writing, intermediate information consisting only of UpdatedScriptExpression,
+ however this is subject for change. The process of restoration is the following:
+ 1. Creating copy of the initial fileItem
+ 2. Updating relevant data/subitems modified during the WriteOut
+ 3. Returning an item containing updates.
+ */
+DomItem OutWriter::restoreWrittenFileItem(const DomItem &fileItem)
{
- Q_ASSERT(qmlFile.internalKind() == DomType::QmlFile);
- if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
- std::shared_ptr<QmlFile> copyPtr = qmlFilePtr->makeCopy(qmlFile);
- DomItem env = qmlFile.environment();
- std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
- Q_ASSERT(envPtr);
- auto newEnvPtr = std::make_shared<DomEnvironment>(
- envPtr, envPtr->loadPaths(), envPtr->options());
- newEnvPtr->addQmlFile(copyPtr);
- MutableDomItem copy = MutableDomItem(DomItem(newEnvPtr).copy(copyPtr));
- FileLocations::Tree newLoc = topLocation;
- Path qmlFilePath = qmlFile.canonicalPath();
- if (newLoc->path() != qmlFilePath) {
- if (newLoc->path()) {
- if (newLoc->path().length() > qmlFilePath.length()
- && newLoc->path().mid(0, qmlFilePath.length()) == qmlFilePath) {
- newLoc = FileLocations::createTree(qmlFilePath);
- FileLocations::Tree loc =
- FileLocations::ensure(newLoc, newLoc->path().mid(qmlFilePath.length()),
- AttachedInfo::PathType::Relative);
- loc->setSubItems(topLocation->subItems());
- } else {
- qCWarning(writeOutLog)
- << "failed to base fileLocations in OutWriter (" << newLoc->path()
- << ") to current file (" << qmlFilePath << ")";
- }
- } else {
- newLoc = FileLocations::createTree(qmlFilePath);
- Q_ASSERT(newLoc->subItems().isEmpty() && newLoc->info().regions.isEmpty());
+ switch (fileItem.internalKind()) {
+ case DomType::QmlFile:
+ return writtenQmlFileItem(fileItem, fileItem.canonicalPath());
+ case DomType::JsFile:
+ return writtenJsFileItem(fileItem, fileItem.canonicalPath());
+ default:
+ qCWarning(writeOutLog) << fileItem.internalKind() << " is not supported";
+ return DomItem{};
+ }
+}
+
+DomItem OutWriter::writtenQmlFileItem(const DomItem &fileItem, const Path &filePath)
+{
+ Q_ASSERT(fileItem.internalKind() == DomType::QmlFile);
+ auto mutableFile = fileItem.makeCopy(DomItem::CopyOption::EnvDisconnected);
+ // QmlFile specific visitor for reformattedScriptExpressions tree
+ // lambda function responsible for the update of the initial expression by the formatted one
+ auto exprUpdater = [&mutableFile, filePath](
+ const Path &p, const UpdatedScriptExpression::Tree &t) {
+ if (std::shared_ptr<ScriptExpression> formattedExpr = t->info().expr) {
+ Q_ASSERT(p.mid(0, filePath.length()) == filePath);
+ MutableDomItem originalExprItem = mutableFile.path(p.mid(filePath.length()));
+ if (!originalExprItem)
+ qCWarning(writeOutLog) << "failed to get" << p.mid(filePath.length()) << "from"
+ << mutableFile.canonicalPath();
+ // Verifying originalExprItem.as<ScriptExpression>() == false is handy
+ // because we can't call setScript on the ScriptExpression itself and it needs to
+ // be called on the container / parent item. See setScript for details
+ else if (formattedExpr->ast()
+ || (!originalExprItem.as<ScriptExpression>()
+ || !originalExprItem.as<ScriptExpression>()->ast()))
+ originalExprItem.setScript(formattedExpr);
+ else {
+ logScriptExprUpdateSkipped(originalExprItem.item(),
+ originalExprItem.canonicalPath(), formattedExpr);
}
}
- copyPtr->setFileLocationsTree(newLoc);
- UpdatedScriptExpression::visitTree(
- reformattedScriptExpressions,
- [&copy, qmlFilePath](Path p, UpdatedScriptExpression::Tree t) {
- if (std::shared_ptr<ScriptExpression> exprPtr = t->info().expr) {
- Q_ASSERT(p.mid(0, qmlFilePath.length()) == qmlFilePath);
- MutableDomItem targetExpr = copy.path(p.mid(qmlFilePath.length()));
- if (!targetExpr)
- qCWarning(writeOutLog) << "failed to get" << p.mid(qmlFilePath.length())
- << "from" << copy.canonicalPath();
- else if (exprPtr->ast()
- || (!targetExpr.as<ScriptExpression>()
- || !targetExpr.as<ScriptExpression>()->ast()))
- targetExpr.setScript(exprPtr);
- else {
- qCWarning(writeOutLog).noquote()
- << "Skipped update of reformatted ScriptExpression with "
- "code:\n---------------\n"
- << exprPtr->code() << "\n---------------\n preCode:" <<
- [exprPtr](Sink s) { sinkEscaped(s, exprPtr->preCode()); }
- << "\n postCode: " <<
- [exprPtr](Sink s) { sinkEscaped(s, exprPtr->postCode()); }
- << "\n as it failed standalone reparse with errors:" <<
- [&targetExpr, exprPtr](Sink s) {
- targetExpr.item()
- .copy(exprPtr, targetExpr.canonicalPath())
- .iterateErrors(
- [s](DomItem, ErrorMessage msg) {
- s(u"\n ");
- msg.dump(s);
- return true;
- },
- true);
- }
- << "\n";
- }
- }
- return true;
- });
- return copy.item();
- }
- return DomItem();
+ return true;
+ };
+ // update relevant formatted expressions
+ UpdatedScriptExpression::visitTree(reformattedScriptExpressions, exprUpdater);
+ return mutableFile.item();
}
+DomItem OutWriter::writtenJsFileItem(const DomItem &fileItem, const Path &filePath)
+{
+ Q_ASSERT(fileItem.internalKind() == DomType::JsFile);
+ auto mutableFile = fileItem.makeCopy(DomItem::CopyOption::EnvDisconnected);
+ UpdatedScriptExpression::visitTree(
+ reformattedScriptExpressions,
+ [&mutableFile, filePath](const Path &p, const UpdatedScriptExpression::Tree &t) {
+ if (std::shared_ptr<ScriptExpression> formattedExpr = t->info().expr) {
+ Q_ASSERT(p.mid(0, filePath.length()) == filePath);
+ mutableFile.mutableAs<JsFile>()->setExpression(formattedExpr);
+ }
+ return true;
+ });
+ return mutableFile.item();
+}
+
+void OutWriter::logScriptExprUpdateSkipped(
+ const DomItem &exprItem, const Path &exprPath,
+ const std::shared_ptr<ScriptExpression> &formattedExpr)
+{
+ qCWarning(writeOutLog).noquote() << "Skipped update of reformatted ScriptExpression with "
+ "code:\n---------------\n"
+ << formattedExpr->code() << "\n---------------\n preCode:" <<
+ [&formattedExpr](Sink s) { sinkEscaped(s, formattedExpr->preCode()); }
+ << "\n postCode: " <<
+ [&formattedExpr](Sink s) { sinkEscaped(s, formattedExpr->postCode()); }
+ << "\n as it failed standalone reparse with errors:" <<
+ [&exprItem, &exprPath, &formattedExpr](Sink s) {
+ exprItem.copy(formattedExpr, exprPath)
+ .iterateErrors(
+ [s](const DomItem &, const ErrorMessage &msg) {
+ s(u"\n ");
+ msg.dump(s);
+ return true;
+ },
+ true);
+ } << "\n";
+}
} // namespace Dom
} // namespace QQmlJS
QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomoutwriter_p.h b/src/qmldom/qqmldomoutwriter_p.h
index 81251e60be..8b00223ea2 100644
--- a/src/qmldom/qqmldomoutwriter_p.h
+++ b/src/qmldom/qqmldomoutwriter_p.h
@@ -27,16 +27,10 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-#define QMLDOM_USTRING(s) u##s
-#define QMLDOM_REGION(name) constexpr const auto name = QMLDOM_USTRING(#name)
-// namespace, so it cam be reopened to add more entries
-namespace Regions {
-} // namespace Regions
-
class QMLDOM_EXPORT OutWriterState
{
public:
- OutWriterState(Path itPath, DomItem &it, FileLocations::Tree fLoc);
+ OutWriterState(const Path &itPath, const DomItem &it, const FileLocations::Tree &fLoc);
void closeState(OutWriter &);
@@ -44,8 +38,8 @@ public:
DomItem item;
PendingSourceLocationId fullRegionId;
FileLocations::Tree currentMap;
- QMap<QString, PendingSourceLocationId> pendingRegions;
- QMap<QString, CommentedElement> pendingComments;
+ QMap<FileLocationRegion, PendingSourceLocationId> pendingRegions;
+ QMap<FileLocationRegion, CommentedElement> pendingComments;
};
class QMLDOM_EXPORT OutWriter
@@ -92,21 +86,14 @@ public:
return indent;
}
- void itemStart(DomItem &it);
- void itemEnd(DomItem &it);
- void regionStart(QString rName);
- void regionStart(QStringView rName) { regionStart(rName.toString()); }
- void regionEnd(QString rName);
- void regionEnd(QStringView rName) { regionEnd(rName.toString()); }
+ void itemStart(const DomItem &it);
+ void itemEnd(const DomItem &it);
+ void regionStart(FileLocationRegion region);
+ void regionEnd(FileLocationRegion regino);
quint32 counter() const { return lineWriter.counter(); }
- OutWriter &writeRegion(QString rName, QStringView toWrite);
- OutWriter &writeRegion(QStringView rName, QStringView toWrite)
- {
- return writeRegion(rName.toString(), toWrite);
- }
- OutWriter &writeRegion(QString t) { return writeRegion(t, t); }
- OutWriter &writeRegion(QStringView t) { return writeRegion(t.toString(), t); }
+ OutWriter &writeRegion(FileLocationRegion region, QStringView toWrite);
+ OutWriter &writeRegion(FileLocationRegion region);
OutWriter &ensureNewline(int nNewlines = 1)
{
lineWriter.ensureNewline(nNewlines);
@@ -153,14 +140,21 @@ public:
return lineWriter.addTextAddCallback(callback);
}
bool removeTextAddCallback(int i) { return lineWriter.removeTextAddCallback(i); }
- void addReformattedScriptExpression(Path p, std::shared_ptr<ScriptExpression> exp)
+ void addReformattedScriptExpression(const Path &p, const std::shared_ptr<ScriptExpression> &exp)
{
if (auto updExp = UpdatedScriptExpression::ensure(reformattedScriptExpressions, p,
AttachedInfo::PathType::Canonical)) {
updExp->info().expr = exp;
}
}
- DomItem updatedFile(DomItem &qmlFile);
+ DomItem restoreWrittenFileItem(const DomItem &fileItem);
+
+private:
+ DomItem writtenQmlFileItem(const DomItem &fileItem, const Path &filePath);
+ DomItem writtenJsFileItem(const DomItem &fileItem, const Path &filePath);
+ static void logScriptExprUpdateSkipped(
+ const DomItem &exprItem, const Path &exprPath,
+ const std::shared_ptr<ScriptExpression> &formattedExpr);
};
} // end namespace Dom
diff --git a/src/qmldom/qqmldompath.cpp b/src/qmldom/qqmldompath.cpp
index 24eb9768eb..d2b4582bc0 100644
--- a/src/qmldom/qqmldompath.cpp
+++ b/src/qmldom/qqmldompath.cpp
@@ -74,15 +74,16 @@ The current contexts are:
\endlist
*/
-void Base::dump(Sink sink) const {
- if (hasSquareBrackets())
+void Base::dump(const Sink &sink, const QString &name, bool hasSquareBrackets) const {
+ if (hasSquareBrackets)
sink(u"[");
- sink(name());
- if (hasSquareBrackets())
+ sink(name);
+ if (hasSquareBrackets)
sink(u"]");
}
-Filter::Filter(function<bool(DomItem)> f, QStringView filterDescription): filterFunction(f), filterDescription(filterDescription) {}
+Filter::Filter(const function<bool(const DomItem &)> &f, QStringView filterDescription)
+ : filterFunction(f), filterDescription(filterDescription) {}
QString Filter::name() const {
return QLatin1String("?(%1)").arg(filterDescription); }
@@ -100,9 +101,6 @@ enum class ParserState{
End
};
-PathComponent::~PathComponent(){
-}
-
int PathComponent::cmp(const PathComponent &p1, const PathComponent &p2)
{
int k1 = static_cast<int>(p1.kind());
@@ -115,19 +113,19 @@ int PathComponent::cmp(const PathComponent &p1, const PathComponent &p2)
case Kind::Empty:
return 0;
case Kind::Field:
- return p1.data.field.fieldName.compare(p2.data.field.fieldName);
+ return std::get<Field>(p1.m_data).fieldName.compare(std::get<Field>(p2.m_data).fieldName);
case Kind::Index:
- if (p1.data.index.indexValue < p2.data.index.indexValue)
+ if (std::get<Index>(p1.m_data).indexValue < std::get<Index>(p2.m_data).indexValue)
return -1;
- if (p1.data.index.indexValue > p2.data.index.indexValue)
+ if (std::get<Index>(p1.m_data).indexValue > std::get<Index>(p2.m_data).indexValue)
return 1;
return 0;
case Kind::Key:
- return p1.data.key.keyValue.compare(p2.data.key.keyValue);
+ return std::get<Key>(p1.m_data).keyValue.compare(std::get<Key>(p2.m_data).keyValue);
case Kind::Root:
{
- PathRoot k1 = p1.data.root.contextKind;
- PathRoot k2 = p2.data.root.contextKind;
+ PathRoot k1 = std::get<Root>(p1.m_data).contextKind;
+ PathRoot k2 = std::get<Root>(p2.m_data).contextKind;
if (k1 == PathRoot::Env || k1 == PathRoot::Universe)
k1 = PathRoot::Top;
if (k2 == PathRoot::Env || k2 == PathRoot::Universe)
@@ -135,23 +133,26 @@ int PathComponent::cmp(const PathComponent &p1, const PathComponent &p2)
int c = int(k1) - int(k2);
if (c != 0)
return c;
- return p1.data.root.contextName.compare(p2.data.root.contextName);
+ return std::get<Root>(p1.m_data).contextName.compare(std::get<Root>(p2.m_data).contextName);
}
case Kind::Current:
{
- int c = int(p1.data.current.contextKind) - int(p2.data.current.contextKind);
+ int c = int(std::get<Current>(p1.m_data).contextKind)
+ - int(std::get<Current>(p2.m_data).contextKind);
if (c != 0)
return c;
- return p1.data.current.contextName.compare(p2.data.current.contextName);
+ return std::get<Current>(p1.m_data).contextName
+ .compare(std::get<Current>(p2.m_data).contextName);
}
case Kind::Any:
return 0;
case Kind::Filter:
{
- int c = p1.data.filter.filterDescription.compare(p2.data.filter.filterDescription);
+ int c = std::get<Filter>(p1.m_data).filterDescription
+ .compare(std::get<Filter>(p2.m_data).filterDescription);
if (c != 0)
return c;
- if (p1.data.filter.filterDescription.startsWith(u"<")) {
+ if (std::get<Filter>(p1.m_data).filterDescription.startsWith(u"<")) {
// assuming non comparable native code (target comparison is not portable)
auto pp1 = &p1;
auto pp2 = &p2;
@@ -213,7 +214,7 @@ PathIterator Path::end() const
PathRoot Path::headRoot() const
{
auto &comp = component(0);
- if (PathEls::Root const * r = comp.base()->asRoot())
+ if (PathEls::Root const * r = comp.asRoot())
return r->contextKind;
return PathRoot::Other;
}
@@ -221,7 +222,7 @@ PathRoot Path::headRoot() const
PathCurrent Path::headCurrent() const
{
auto comp = component(0);
- if (PathEls::Current const * c = comp.base()->asCurrent())
+ if (PathEls::Current const * c = comp.asCurrent())
return c->contextKind;
return PathCurrent::Other;
}
@@ -248,10 +249,10 @@ index_type Path::headIndex(index_type defaultValue) const
return component(0).index(defaultValue);
}
-function<bool (DomItem)> Path::headFilter() const
+function<bool(const DomItem &)> Path::headFilter() const
{
auto &comp = component(0);
- if (PathEls::Filter const * f = comp.base()->asFilter()) {
+ if (PathEls::Filter const * f = comp.asFilter()) {
return f->filterFunction;
}
return {};
@@ -279,7 +280,7 @@ Source Path::split() const
return Source{Path(), *this};
}
-bool inQString(QStringView el, QString base)
+bool inQString(QStringView el, const QString &base)
{
if (quintptr(base.constData()) > quintptr(el.begin())
|| quintptr(base.constData() + base.size()) < quintptr(el.begin()))
@@ -288,7 +289,7 @@ bool inQString(QStringView el, QString base)
return diff >= 0 && diff < base.size();
}
-bool inQString(QString el, QString base)
+bool inQString(const QString &el, const QString &base)
{
if (quintptr(base.constData()) > quintptr(el.constData())
|| quintptr(base.constData() + base.size()) < quintptr(el.constData()))
@@ -297,7 +298,7 @@ bool inQString(QString el, QString base)
return diff >= 0 && diff < base.size() && diff + el.size() < base.size();
}
-Path Path::fromString(QStringView s, ErrorHandler errorHandler)
+Path Path::fromString(QStringView s, const ErrorHandler &errorHandler)
{
if (s.isEmpty())
return Path();
@@ -525,7 +526,7 @@ Path Path::Root(PathRoot s)
QStringList(), QVector<Component>(1,Component(PathEls::Root(s)))));
}
-Path Path::Root(QString s)
+Path Path::Root(const QString &s)
{
return Path(0,1,std::make_shared<PathEls::PathData>(
QStringList(s), QVector<Component>(1,Component(PathEls::Root(s)))));
@@ -550,7 +551,7 @@ Path Path::Field(QStringView s)
QStringList(), QVector<Component>(1,Component(PathEls::Field(s)))));
}
-Path Path::Field(QString s)
+Path Path::Field(const QString &s)
{
return Path(0,1,std::make_shared<PathEls::PathData>(
QStringList(s), QVector<Component>(1,Component(PathEls::Field(s)))));
@@ -564,7 +565,7 @@ Path Path::Key(QStringView s)
QStringList(), QVector<Component>(1, Component(PathEls::Key(s.toString())))));
}
-Path Path::Key(QString s)
+Path Path::Key(const QString &s)
{
return Path(0, 1,
std::make_shared<PathEls::PathData>(
@@ -577,7 +578,7 @@ Path Path::Current(PathCurrent s)
QStringList(), QVector<Component>(1,Component(PathEls::Current(s)))));
}
-Path Path::Current(QString s)
+Path Path::Current(const QString &s)
{
return Path(0,1,std::make_shared<PathEls::PathData>(
QStringList(s), QVector<Component>(1,Component(PathEls::Current(s)))));
@@ -602,7 +603,7 @@ Path Path::empty() const
QStringList(), QVector<Component>(1,Component()), m_data));
}
-Path Path::field(QString name) const
+Path Path::field(const QString &name) const
{
auto res = field(QStringView(name));
res.m_data->strData.append(name);
@@ -617,7 +618,7 @@ Path Path::field(QStringView name) const
QStringList(), QVector<Component>(1,Component(PathEls::Field(name))), m_data));
}
-Path Path::key(QString name) const
+Path Path::key(const QString &name) const
{
if (m_endOffset != 0)
return noEndOffset().key(name);
@@ -646,14 +647,14 @@ Path Path::any() const
QStringList(), QVector<Component>(1,Component(PathEls::Any())), m_data));
}
-Path Path::filter(function<bool (DomItem)> filterF, QString desc) const
+Path Path::filter(const function<bool(const DomItem &)> &filterF, const QString &desc) const
{
auto res = filter(filterF, QStringView(desc));
res.m_data->strData.append(desc);
return res;
}
-Path Path::filter(function<bool (DomItem)> filter, QStringView desc) const
+Path Path::filter(const function<bool(const DomItem &)> &filter, QStringView desc) const
{
if (m_endOffset != 0)
return noEndOffset().filter(filter, desc);
@@ -667,7 +668,7 @@ Path Path::current(PathCurrent s) const
QStringList(), QVector<Component>(1,Component(PathEls::Current(s))), m_data));
}
-Path Path::current(QString s) const
+Path Path::current(const QString &s) const
{
auto res = current(QStringView(s));
res.m_data->strData.append(s);
@@ -682,7 +683,7 @@ Path Path::current(QStringView s) const
QStringList(), QVector<Component>(1,Component(PathEls::Current(s))), m_data));
}
-Path Path::path(Path toAdd, bool avoidToAddAsBase) const
+Path Path::path(const Path &toAdd, bool avoidToAddAsBase) const
{
if (toAdd.length() == 0)
return *this;
@@ -808,7 +809,7 @@ int Path::cmp(const Path &p1, const Path &p2)
return 0;
}
-Path::Path(quint16 endOffset, quint16 length, std::shared_ptr<PathEls::PathData> data)
+Path::Path(quint16 endOffset, quint16 length, const std::shared_ptr<PathEls::PathData> &data)
:m_endOffset(endOffset), m_length(length), m_data(data)
{
}
@@ -869,10 +870,10 @@ Path Path::appendComponent(const PathEls::PathComponent &c)
}
break;
case PathEls::Kind::Filter:
- if (!c.base()->asFilter()->filterDescription.isEmpty()) {
- my_data->strData.append(c.base()->asFilter()->filterDescription.toString());
+ if (!c.asFilter()->filterDescription.isEmpty()) {
+ my_data->strData.append(c.asFilter()->filterDescription.toString());
my_data->components.append(
- PathEls::Filter(c.base()->asFilter()->filterFunction, my_data->strData.last()));
+ PathEls::Filter(c.asFilter()->filterFunction, my_data->strData.last()));
} else {
my_data->components.append(c);
}
@@ -900,7 +901,7 @@ ErrorGroups Path::myErrors()
return res;
}
-void Path::dump(Sink sink) const
+void Path::dump(const Sink &sink) const
{
bool first = true;
for (int i = 0; i < m_length; ++i) {
@@ -951,7 +952,7 @@ Path Path::mid(int offset) const
return mid(offset, m_length - offset);
}
-Path Path::fromString(QString s, ErrorHandler errorHandler)
+Path Path::fromString(const QString &s, const ErrorHandler &errorHandler)
{
Path res = fromString(QStringView(s), errorHandler);
if (res.m_data)
diff --git a/src/qmldom/qqmldompath_p.h b/src/qmldom/qqmldompath_p.h
index 859e23d1b0..1a5af85e8e 100644
--- a/src/qmldom/qqmldompath_p.h
+++ b/src/qmldom/qqmldompath_p.h
@@ -69,35 +69,21 @@ class Filter;
class Base {
public:
- virtual ~Base() = default;
- virtual Kind kind() const = 0;
- virtual QString name() const = 0;
- virtual bool checkName(QStringView s) const = 0;
- virtual QStringView stringView() const { return QStringView(); }
- virtual index_type index(index_type defaultValue=-1) const { return defaultValue; }
-
- virtual void dump(Sink sink) const;
- virtual bool hasSquareBrackets() const { return false; }
-
- // casting, could use optional, but that is c++17...
- virtual const Empty *asEmpty() const { return nullptr; }
- virtual const Field *asField() const { return nullptr; }
- virtual const Index *asIndex() const { return nullptr; }
- virtual const Key *asKey() const { return nullptr; }
- virtual const Root *asRoot() const { return nullptr; }
- virtual const Current *asCurrent() const { return nullptr; }
- virtual const Any *asAny() const { return nullptr; }
- virtual const Filter *asFilter() const { return nullptr; }
+ QStringView stringView() const { return QStringView(); }
+ index_type index(index_type defaultValue = -1) const { return defaultValue; }
+ bool hasSquareBrackets() const { return false; }
+
+protected:
+ void dump(const Sink &sink, const QString &name, bool hasSquareBrackets) const;
};
class Empty final : public Base
{
public:
Empty() = default;
- Kind kind() const override { return Kind::Empty; }
- QString name() const override { return QString(); }
- bool checkName(QStringView s) const override { return s.isEmpty(); }
- const Empty * asEmpty() const override { return this; }
+ QString name() const { return QString(); }
+ bool checkName(QStringView s) const { return s.isEmpty(); }
+ void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); }
};
class Field final : public Base
@@ -105,12 +91,10 @@ class Field final : public Base
public:
Field() = default;
Field(QStringView n): fieldName(n) {}
- Kind kind() const override { return Kind::Field; }
- QString name() const override { return fieldName.toString(); }
- bool checkName(QStringView s) const override { return s == fieldName; }
- QStringView stringView() const override { return fieldName; }
- const Field * asField() const override { return this; }
- void dump(Sink sink) const override { sink(fieldName); }
+ QString name() const { return fieldName.toString(); }
+ bool checkName(QStringView s) const { return s == fieldName; }
+ QStringView stringView() const { return fieldName; }
+ void dump(const Sink &sink) const { sink(fieldName); }
QStringView fieldName;
};
@@ -120,12 +104,11 @@ class Index final : public Base
public:
Index() = default;
Index(index_type i): indexValue(i) {}
- Kind kind() const override { return Kind::Index; }
- QString name() const override { return QString::number(indexValue); }
- bool checkName(QStringView s) const override { return s == name(); }
- index_type index(index_type = -1) const override { return indexValue; }
- bool hasSquareBrackets() const override { return true; }
- const Index * asIndex() const override { return this; }
+ QString name() const { return QString::number(indexValue); }
+ bool checkName(QStringView s) const { return s == name(); }
+ index_type index(index_type = -1) const { return indexValue; }
+ void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); }
+ bool hasSquareBrackets() const { return true; }
index_type indexValue = -1;
};
@@ -134,18 +117,16 @@ class Key final : public Base
{
public:
Key() = default;
- Key(QString n) : keyValue(n) { }
- Kind kind() const override { return Kind::Key; }
- QString name() const override { return keyValue; }
- bool checkName(QStringView s) const override { return s == keyValue; }
- QStringView stringView() const override { return keyValue; }
- void dump(Sink sink) const override {
+ Key(const QString &n) : keyValue(n) { }
+ QString name() const { return keyValue; }
+ bool checkName(QStringView s) const { return s == keyValue; }
+ QStringView stringView() const { return keyValue; }
+ void dump(const Sink &sink) const {
sink(u"[");
sinkEscaped(sink, keyValue);
sink(u"]");
}
- bool hasSquareBrackets() const override { return true; }
- const Key * asKey() const override { return this; }
+ bool hasSquareBrackets() const { return true; }
QString keyValue;
};
@@ -164,8 +145,7 @@ public:
if (contextKind == PathRoot::Other)
contextName = n;
}
- Kind kind() const override { return Kind::Root; }
- QString name() const override {
+ QString name() const {
switch (contextKind) {
case PathRoot::Modules:
return QStringLiteral(u"$modules");
@@ -185,16 +165,13 @@ public:
Q_ASSERT(false && "Unexpected contextKind in name");
return QString();
}
- bool checkName(QStringView s) const override {
+ bool checkName(QStringView s) const {
if (contextKind != PathRoot::Other)
return s.compare(name(), Qt::CaseInsensitive) == 0;
return s.startsWith(QChar::fromLatin1('$')) && s.mid(1) == contextName;
}
- QStringView stringView() const override { return contextName; }
- void dump(Sink sink) const override {
- sink(name());
- }
- const Root *asRoot() const override { return this; }
+ QStringView stringView() const { return contextName; }
+ void dump(const Sink &sink) const { sink(name()); }
PathRoot contextKind = PathRoot::Other;
QStringView contextName;
@@ -214,8 +191,7 @@ public:
if (contextKind == PathCurrent::Other)
contextName = n;
}
- Kind kind() const override { return Kind::Current; }
- QString name() const override {
+ QString name() const {
switch (contextKind) {
case PathCurrent::Other:
return QString::fromUtf8("@").append(contextName.toString());
@@ -243,13 +219,13 @@ public:
Q_ASSERT(false && "Unexpected contextKind in Current::name");
return QString();
}
- bool checkName(QStringView s) const override {
+ bool checkName(QStringView s) const {
if (contextKind != PathCurrent::Other)
return s.compare(name(), Qt::CaseInsensitive) == 0;
return s.startsWith(QChar::fromLatin1('@')) && s.mid(1) == contextName;
}
- QStringView stringView() const override { return contextName; }
- const Current *asCurrent() const override { return this; }
+ QStringView stringView() const { return contextName; }
+ void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); }
PathCurrent contextKind = PathCurrent::Other;
QStringView contextName;
@@ -259,160 +235,110 @@ class Any final : public Base
{
public:
Any() = default;
- Kind kind() const override { return Kind::Any; }
- QString name() const override { return QLatin1String("*"); }
- bool checkName(QStringView s) const override { return s == u"*"; }
- bool hasSquareBrackets() const override { return true; }
- const Any *asAny() const override { return this; }
+ QString name() const { return QLatin1String("*"); }
+ bool checkName(QStringView s) const { return s == u"*"; }
+ void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); }
+ bool hasSquareBrackets() const { return true; }
};
class QMLDOM_EXPORT Filter final : public Base
{
public:
Filter() = default;
- Filter(std::function<bool(DomItem)> f, QStringView filterDescription = u"<native code filter>");
- Kind kind() const override { return Kind::Filter; }
- QString name() const override;
- bool checkName(QStringView s) const override;
- QStringView stringView() const override { return filterDescription; }
- bool hasSquareBrackets() const override { return true; }
- const Filter *asFilter() const override { return this; }
-
- std::function<bool(DomItem)> filterFunction;
+ Filter(const std::function<bool(const DomItem &)> &f,
+ QStringView filterDescription = u"<native code filter>");
+ QString name() const;
+ bool checkName(QStringView s) const;
+ QStringView stringView() const { return filterDescription; }
+ void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); }
+ bool hasSquareBrackets() const { return true; }
+
+ std::function<bool(const DomItem &)> filterFunction;
QStringView filterDescription;
};
class QMLDOM_EXPORT PathComponent {
public:
- PathComponent(): data() {}
- ~PathComponent();
-
- Kind kind() const { return base()->kind(); }
- QString name() const { return base()->name(); };
- bool checkName(QStringView s) const { return base()->checkName(s); }
- QStringView stringView() const { return base()->stringView(); };
- index_type index(index_type defaultValue=-1) const { return base()->index(defaultValue); }
- void dump(Sink sink) const { base()->dump(sink); }
- bool hasSquareBrackets() const { return base()->hasSquareBrackets(); }
-
- const Empty *asEmpty() const { return base()->asEmpty(); }
- const Field *asField() const { return base()->asField(); }
- const Index *asIndex() const { return base()->asIndex(); }
- const Key *asKey() const { return base()->asKey(); }
- const Root *asRoot() const { return base()->asRoot(); }
- const Current *asCurrent() const { return base()->asCurrent(); }
- const Any *asAny() const { return base()->asAny(); }
+ PathComponent() = default;
+ PathComponent(const PathComponent &) = default;
+ PathComponent(PathComponent &&) = default;
+ PathComponent &operator=(const PathComponent &) = default;
+ PathComponent &operator=(PathComponent &&) = default;
+ ~PathComponent() = default;
+
+ Kind kind() const { return Kind(m_data.index()); }
+
+ QString name() const
+ {
+ return std::visit([](auto &&d) { return d.name(); }, m_data);
+ }
+
+ bool checkName(QStringView s) const
+ {
+ return std::visit([s](auto &&d) { return d.checkName(s); }, m_data);
+ }
+
+ QStringView stringView() const
+ {
+ return std::visit([](auto &&d) { return d.stringView(); }, m_data);
+ }
+
+ index_type index(index_type defaultValue=-1) const
+ {
+ return std::visit([defaultValue](auto &&d) { return d.index(defaultValue); }, m_data);
+ }
+
+ void dump(const Sink &sink) const
+ {
+ return std::visit([sink](auto &&d) { return d.dump(sink); }, m_data);
+ }
+
+ bool hasSquareBrackets() const
+ {
+ return std::visit([](auto &&d) { return d.hasSquareBrackets(); }, m_data);
+ }
+
+ const Empty *asEmpty() const { return std::get_if<Empty>(&m_data); }
+ const Field *asField() const { return std::get_if<Field>(&m_data); }
+ const Index *asIndex() const { return std::get_if<Index>(&m_data); }
+ const Key *asKey() const { return std::get_if<Key>(&m_data); }
+ const Root *asRoot() const { return std::get_if<Root>(&m_data); }
+ const Current *asCurrent() const { return std::get_if<Current>(&m_data); }
+ const Any *asAny() const { return std::get_if<Any>(&m_data); }
+ const Filter *asFilter() const { return std::get_if<Filter>(&m_data); }
+
static int cmp(const PathComponent &p1, const PathComponent &p2);
- PathComponent(const Empty &o): data(o) {}
- PathComponent(const Field &o): data(o) {}
- PathComponent(const Index &o): data(o) {}
- PathComponent(const Key &o): data(o) {}
- PathComponent(const Root &o): data(o) {}
- PathComponent(const Current &o): data(o) {}
- PathComponent(const Any &o): data(o) {}
- PathComponent(const Filter &o): data(o) {}
+ PathComponent(Empty &&o): m_data(std::move(o)) {}
+ PathComponent(Field &&o): m_data(std::move(o)) {}
+ PathComponent(Index &&o): m_data(std::move(o)) {}
+ PathComponent(Key &&o): m_data(std::move(o)) {}
+ PathComponent(Root &&o): m_data(std::move(o)) {}
+ PathComponent(Current &&o): m_data(std::move(o)) {}
+ PathComponent(Any &&o): m_data(std::move(o)) {}
+ PathComponent(Filter &&o): m_data(std::move(o)) {}
+
private:
friend class QQmlJS::Dom::Path;
friend class QQmlJS::Dom::PathEls::TestPaths;
- Base *base() {
- return reinterpret_cast<Base*>(&data);
- }
- const Base *base() const {
- return reinterpret_cast<const Base*>(&data);
- }
- union Data {
- Data(): empty() { }
- Data(const Data &d) {
- switch (d.kind()){
- case Kind::Empty:
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&empty) && "non C++11 compliant compiler");
- new (&empty) Empty(d.empty);
- break;
- case Kind::Field:
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&field) && "non C++11 compliant compiler");
- new (&field) Field(d.field);
- break;
- case Kind::Index:
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&index) && "non C++11 compliant compiler");
- new (&index) Index(d.index);
- break;
- case Kind::Key:
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&key) && "non C++11 compliant compiler");
- new (&key) Key(d.key);
- break;
- case Kind::Root:
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&root) && "non C++11 compliant compiler");
- new (&root) Root(d.root);
- break;
- case Kind::Current:
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&current) && "non C++11 compliant compiler");
- new (&current) Current(d.current);
- break;
- case Kind::Any:
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&any) && "non C++11 compliant compiler");
- new (&any) Any(d.any);
- break;
- case Kind::Filter:
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&filter) && "non C++11 compliant compiler");
- new (&filter) Filter(d.filter);
- break;
- }
- }
- Data(const Empty &o) {
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&empty) && "non C++11 compliant compiler");
- new (&empty) Empty(o);
- }
- Data(const Field &o) {
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&field) && "non C++11 compliant compiler");
- new (&field) Field(o);
- }
- Data(const Index &o){
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&index) && "non C++11 compliant compiler");
- new (&index) Index(o);
- }
- Data(const Key &o) {
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&key) && "non C++11 compliant compiler");
- new (&key) Key(o);
- }
- Data(const Root &o) {
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&root) && "non C++11 compliant compiler");
- new (&root) Root(o);
- }
- Data(const Current &o) {
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&current) && "non C++11 compliant compiler");
- new (&current) Current(o);
- }
- Data(const Any &o) {
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&any) && "non C++11 compliant compiler");
- new (&any) Any(o);
- }
- Data(const Filter &o) {
- Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&filter) && "non C++11 compliant compiler");
- new (&filter) Filter(o);
- }
- Data &operator=(const Data &d) {
- Q_ASSERT(this != &d);
- this->~Data(); // destruct & construct new...
- new (this)Data(d);
- return *this;
- }
- Kind kind() const {
- return reinterpret_cast<const Base*>(this)->kind();
- }
- ~Data() {
- reinterpret_cast<const Base*>(this)->~Base();
- }
- Empty empty;
- Field field;
- Index index;
- Key key;
- Root root;
- Current current;
- Any any;
- Filter filter;
- } data;
+ using Variant = std::variant<Empty, Field, Index, Key, Root, Current, Any, Filter>;
+
+ template<typename T, Kind K>
+ static constexpr bool variantTypeMatches
+ = std::is_same_v<std::variant_alternative_t<size_t(K), Variant>, T>;
+
+ static_assert(size_t(Kind::Empty) == 0);
+ static_assert(variantTypeMatches<Empty, Kind::Empty>);
+ static_assert(variantTypeMatches<Field, Kind::Field>);
+ static_assert(variantTypeMatches<Key, Kind::Key>);
+ static_assert(variantTypeMatches<Root, Kind::Root>);
+ static_assert(variantTypeMatches<Current, Kind::Current>);
+ static_assert(variantTypeMatches<Any, Kind::Any>);
+ static_assert(variantTypeMatches<Filter, Kind::Filter>);
+ static_assert(std::variant_size_v<Variant> == size_t(Kind::Filter) + 1);
+
+ Variant m_data;
};
inline bool operator==(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) == 0; }
@@ -424,9 +350,13 @@ inline bool operator>=(const PathComponent& lhs, const PathComponent& rhs){ retu
class PathData {
public:
- PathData(QStringList strData, QVector<PathComponent> components): strData(strData), components(components) {}
- PathData(QStringList strData, QVector<PathComponent> components, std::shared_ptr<PathData> parent):
- strData(strData), components(components), parent(parent) {}
+ PathData(const QStringList &strData, const QVector<PathComponent> &components)
+ : strData(strData), components(components)
+ {}
+ PathData(const QStringList &strData, const QVector<PathComponent> &components,
+ const std::shared_ptr<PathData> &parent)
+ : strData(strData), components(components), parent(parent)
+ {}
QStringList strData;
QVector<PathComponent> components;
@@ -437,30 +367,51 @@ public:
#define QMLDOM_USTRING(s) u##s
#define QMLDOM_FIELD(name) inline constexpr const auto name = QMLDOM_USTRING(#name)
+/*!
+ \internal
+ In an ideal world, the Fields namespace would be an enum, not strings.
+ Use FieldType whenever you expect a static String from the Fields namespace instead of an
+ arbitrary QStringView.
+ */
+using FieldType = QStringView;
// namespace, so it cam be reopened to add more entries
namespace Fields{
QMLDOM_FIELD(access);
QMLDOM_FIELD(accessSemantics);
QMLDOM_FIELD(allSources);
+QMLDOM_FIELD(alternative);
QMLDOM_FIELD(annotations);
+QMLDOM_FIELD(arguments);
QMLDOM_FIELD(astComments);
QMLDOM_FIELD(astRelocatableDump);
QMLDOM_FIELD(attachedType);
QMLDOM_FIELD(attachedTypeName);
QMLDOM_FIELD(autoExports);
QMLDOM_FIELD(base);
+QMLDOM_FIELD(binaryExpression);
QMLDOM_FIELD(bindable);
+QMLDOM_FIELD(bindingElement);
+QMLDOM_FIELD(bindingIdentifiers);
QMLDOM_FIELD(bindingType);
QMLDOM_FIELD(bindings);
+QMLDOM_FIELD(block);
QMLDOM_FIELD(body);
+QMLDOM_FIELD(callee);
QMLDOM_FIELD(canonicalFilePath);
QMLDOM_FIELD(canonicalPath);
+QMLDOM_FIELD(caseBlock);
+QMLDOM_FIELD(caseClause);
+QMLDOM_FIELD(caseClauses);
+QMLDOM_FIELD(catchBlock);
+QMLDOM_FIELD(catchParameter);
QMLDOM_FIELD(children);
QMLDOM_FIELD(classNames);
QMLDOM_FIELD(code);
QMLDOM_FIELD(commentedElements);
QMLDOM_FIELD(comments);
QMLDOM_FIELD(components);
+QMLDOM_FIELD(condition);
+QMLDOM_FIELD(consequence);
QMLDOM_FIELD(contents);
QMLDOM_FIELD(contentsDate);
QMLDOM_FIELD(cppType);
@@ -468,20 +419,26 @@ QMLDOM_FIELD(currentExposedAt);
QMLDOM_FIELD(currentIsValid);
QMLDOM_FIELD(currentItem);
QMLDOM_FIELD(currentRevision);
+QMLDOM_FIELD(declarations);
+QMLDOM_FIELD(defaultClause);
QMLDOM_FIELD(defaultPropertyName);
QMLDOM_FIELD(defaultValue);
QMLDOM_FIELD(designerSupported);
QMLDOM_FIELD(elLocation);
+QMLDOM_FIELD(elements);
QMLDOM_FIELD(elementCanonicalPath);
QMLDOM_FIELD(enumerations);
QMLDOM_FIELD(errors);
QMLDOM_FIELD(exportSource);
QMLDOM_FIELD(exports);
QMLDOM_FIELD(expr);
+QMLDOM_FIELD(expression);
QMLDOM_FIELD(expressionType);
QMLDOM_FIELD(extensionTypeName);
QMLDOM_FIELD(fileLocationsTree);
QMLDOM_FIELD(fileName);
+QMLDOM_FIELD(finallyBlock);
+QMLDOM_FIELD(forStatement);
QMLDOM_FIELD(fullRegion);
QMLDOM_FIELD(get);
QMLDOM_FIELD(globalScopeName);
@@ -489,6 +446,7 @@ QMLDOM_FIELD(globalScopeWithName);
QMLDOM_FIELD(hasCallback);
QMLDOM_FIELD(hasCustomParser);
QMLDOM_FIELD(idStr);
+QMLDOM_FIELD(identifier);
QMLDOM_FIELD(ids);
QMLDOM_FIELD(implicit);
QMLDOM_FIELD(import);
@@ -500,6 +458,7 @@ QMLDOM_FIELD(imports);
QMLDOM_FIELD(inProgress);
QMLDOM_FIELD(infoItem);
QMLDOM_FIELD(inheritVersion);
+QMLDOM_FIELD(initializer);
QMLDOM_FIELD(interfaceNames);
QMLDOM_FIELD(isAlias);
QMLDOM_FIELD(isComposite);
@@ -519,7 +478,9 @@ QMLDOM_FIELD(isValid);
QMLDOM_FIELD(jsFileWithPath);
QMLDOM_FIELD(kind);
QMLDOM_FIELD(lastRevision);
+QMLDOM_FIELD(label);
QMLDOM_FIELD(lastValidRevision);
+QMLDOM_FIELD(left);
QMLDOM_FIELD(loadInfo);
QMLDOM_FIELD(loadOptions);
QMLDOM_FIELD(loadPaths);
@@ -535,17 +496,20 @@ QMLDOM_FIELD(minorVersion);
QMLDOM_FIELD(moduleIndex);
QMLDOM_FIELD(moduleIndexWithUri);
QMLDOM_FIELD(moduleScope);
+QMLDOM_FIELD(moreCaseClauses);
QMLDOM_FIELD(nAllLoadedCallbacks);
QMLDOM_FIELD(nCallbacks);
QMLDOM_FIELD(nLoaded);
QMLDOM_FIELD(nNotdone);
QMLDOM_FIELD(name);
+QMLDOM_FIELD(nameIdentifiers);
QMLDOM_FIELD(newlinesBefore);
QMLDOM_FIELD(nextComponent);
QMLDOM_FIELD(nextScope);
QMLDOM_FIELD(notify);
QMLDOM_FIELD(objects);
QMLDOM_FIELD(onAttachedObject);
+QMLDOM_FIELD(operation);
QMLDOM_FIELD(options);
QMLDOM_FIELD(parameters);
QMLDOM_FIELD(parent);
@@ -560,6 +524,7 @@ QMLDOM_FIELD(pragmas);
QMLDOM_FIELD(preCode);
QMLDOM_FIELD(preCommentLocations);
QMLDOM_FIELD(preComments);
+QMLDOM_FIELD(properties);
QMLDOM_FIELD(propertyDef);
QMLDOM_FIELD(propertyDefRef);
QMLDOM_FIELD(propertyDefs);
@@ -574,7 +539,6 @@ QMLDOM_FIELD(qmldirWithPath);
QMLDOM_FIELD(qmltypesFileWithPath);
QMLDOM_FIELD(qmltypesFiles);
QMLDOM_FIELD(qualifiedImports);
-QMLDOM_FIELD(queue);
QMLDOM_FIELD(rawComment);
QMLDOM_FIELD(read);
QMLDOM_FIELD(referredObject);
@@ -585,8 +549,13 @@ QMLDOM_FIELD(requestedAt);
QMLDOM_FIELD(requestingUniverse);
QMLDOM_FIELD(returnType);
QMLDOM_FIELD(returnTypeName);
+QMLDOM_FIELD(right);
QMLDOM_FIELD(rootComponent);
+QMLDOM_FIELD(scopeType);
+QMLDOM_FIELD(scriptElement);
QMLDOM_FIELD(sources);
+QMLDOM_FIELD(statement);
+QMLDOM_FIELD(statements);
QMLDOM_FIELD(status);
QMLDOM_FIELD(stringValue);
QMLDOM_FIELD(subComponents);
@@ -598,6 +567,8 @@ QMLDOM_FIELD(target);
QMLDOM_FIELD(targetPropertyName);
QMLDOM_FIELD(text);
QMLDOM_FIELD(type);
+QMLDOM_FIELD(typeArgument);
+QMLDOM_FIELD(typeArgumentName);
QMLDOM_FIELD(typeName);
QMLDOM_FIELD(types);
QMLDOM_FIELD(universe);
@@ -612,7 +583,7 @@ QMLDOM_FIELD(values);
QMLDOM_FIELD(version);
QMLDOM_FIELD(when);
QMLDOM_FIELD(write);
-}
+} // namespace Fields
class Source;
size_t qHash(const Path &, size_t);
@@ -646,12 +617,12 @@ public:
QString headName() const;
bool checkHeadName(QStringView name) const;
index_type headIndex(index_type defaultValue=-1) const;
- std::function<bool(DomItem)> headFilter() const;
+ std::function<bool(const DomItem &)> headFilter() const;
Path head() const;
Path last() const;
Source split() const;
- void dump(Sink sink) const;
+ void dump(const Sink &sink) const;
QString toString() const;
Path dropFront(int n = 1) const;
Path dropTail(int n = 1) const;
@@ -660,34 +631,35 @@ public:
Path appendComponent(const PathEls::PathComponent &c);
// # Path construction
- static Path fromString(QString s, ErrorHandler errorHandler=nullptr);
- static Path fromString(QStringView s, ErrorHandler errorHandler=nullptr);
+ static Path fromString(const QString &s, const ErrorHandler &errorHandler = nullptr);
+ static Path fromString(QStringView s, const ErrorHandler &errorHandler = nullptr);
static Path Root(PathRoot r);
static Path Root(QStringView s=u"");
- static Path Root(QString s);
+ static Path Root(const QString &s);
static Path Index(index_type i);
static Path Field(QStringView s=u"");
- static Path Field(QString s);
+ static Path Field(const QString &s);
static Path Key(QStringView s=u"");
- static Path Key(QString s);
+ static Path Key(const QString &s);
static Path Current(PathCurrent c);
static Path Current(QStringView s=u"");
- static Path Current(QString s);
+ static Path Current(const QString &s);
static Path Empty();
// add
Path empty() const;
- Path field(QString name) const;
+ Path field(const QString &name) const;
Path field(QStringView name) const;
- Path key(QString name) const;
+ Path key(const QString &name) const;
Path key(QStringView name) const;
Path index(index_type i) const;
Path any() const;
- Path filter(std::function<bool(DomItem)>, QString) const;
- Path filter(std::function<bool(DomItem)>, QStringView desc=u"<native code filter>") const;
+ Path filter(const std::function<bool(const DomItem &)> &, const QString &) const;
+ Path filter(const std::function<bool(const DomItem &)> &,
+ QStringView desc=u"<native code filter>") const;
Path current(PathCurrent s) const;
- Path current(QString s) const;
+ Path current(const QString &s) const;
Path current(QStringView s=u"") const;
- Path path(Path toAdd, bool avoidToAddAsBase = false) const;
+ Path path(const Path &toAdd, bool avoidToAddAsBase = false) const;
Path expandFront() const;
Path expandBack() const;
@@ -706,7 +678,8 @@ public:
private:
const Component &component(int i) const;
- explicit Path(quint16 endOffset, quint16 length, std::shared_ptr<PathEls::PathData> data);
+ explicit Path(quint16 endOffset, quint16 length,
+ const std::shared_ptr<PathEls::PathData> &data);
friend class QQmlJS::Dom::PathEls::TestPaths;
friend class FieldFilter;
friend size_t qHash(const Path &, size_t);
@@ -776,6 +749,9 @@ inline size_t qHash(const Path &path, size_t seed)
*it++ = qHash(p.component(0).stringView(), seed)^size_t(p.headRoot())^size_t(p.headCurrent());
}
}
+
+ // TODO: Get rid of the reinterpret_cast.
+ // Rather hash the path components in a more structured way.
return qHash(QByteArray::fromRawData(reinterpret_cast<char *>(&buf[0]), (it - &buf[0])*sizeof(size_t)), seed);
}
diff --git a/src/qmldom/qqmldomreformatter.cpp b/src/qmldom/qqmldomreformatter.cpp
index 9114f39c3e..95533b61ac 100644
--- a/src/qmldom/qqmldomreformatter.cpp
+++ b/src/qmldom/qqmldomreformatter.cpp
@@ -11,6 +11,7 @@
#include <QString>
+#include <algorithm>
#include <limits>
QT_BEGIN_NAMESPACE
@@ -19,1170 +20,1103 @@ namespace Dom {
using namespace AST;
-class Rewriter : protected BaseVisitor
+bool ScriptFormatter::preVisit(Node *n)
{
- OutWriter &lw;
- std::shared_ptr<AstComments> comments;
- std::function<QStringView(SourceLocation)> loc2Str;
- QHash<Node *, QList<std::function<void()>>> postOps;
- int expressionDepth = 0;
-
- bool addSemicolons() const { return expressionDepth > 0; }
-
-public:
- Rewriter(OutWriter &lw, std::shared_ptr<AstComments> comments,
- std::function<QStringView(SourceLocation)> loc2Str, Node *node)
- : lw(lw), comments(comments), loc2Str(loc2Str)
- {
- accept(node);
+ if (CommentedElement *c = comments->commentForNode(n)) {
+ c->writePre(lw);
+ postOps[n].append([c, this]() { c->writePost(lw); });
}
-
-protected:
- bool preVisit(Node *n) override
- {
- if (CommentedElement *c = comments->commentForNode(n)) {
- c->writePre(lw);
- postOps[n].append([c, this]() { c->writePost(lw); });
- }
- return true;
- }
- void postVisit(Node *n) override
- {
- for (auto &op : postOps[n]) {
- op();
- }
- postOps.remove(n);
+ return true;
+}
+void ScriptFormatter::postVisit(Node *n)
+{
+ for (auto &op : postOps[n]) {
+ op();
}
+ postOps.remove(n);
+}
- void accept(Node *node) { Node::accept(node, this); }
+void ScriptFormatter::lnAcceptIndented(Node *node)
+{
+ int indent = lw.increaseIndent(1);
+ lw.ensureNewline();
+ accept(node);
+ lw.decreaseIndent(1, indent);
+}
- void lnAcceptIndented(Node *node)
- {
- int indent = lw.increaseIndent(1);
- lw.ensureNewline();
- accept(node);
- lw.decreaseIndent(1, indent);
+bool ScriptFormatter::acceptBlockOrIndented(Node *ast, bool finishWithSpaceOrNewline)
+{
+ if (cast<Block *>(ast)) {
+ out(" ");
+ accept(ast);
+ if (finishWithSpaceOrNewline)
+ out(" ");
+ return true;
+ } else {
+ if (finishWithSpaceOrNewline)
+ postOps[ast].append([this]() { this->newLine(); });
+ lnAcceptIndented(ast);
+ return false;
}
+}
- void out(const char *str) { lw.write(QString::fromLatin1(str)); }
-
- void out(QStringView str) { lw.write(str); }
+bool ScriptFormatter::visit(ThisExpression *ast)
+{
+ out(ast->thisToken);
+ return true;
+}
- void out(const SourceLocation &loc)
- {
- if (loc.length != 0)
- out(loc2Str(loc));
- }
+bool ScriptFormatter::visit(NullExpression *ast)
+{
+ out(ast->nullToken);
+ return true;
+}
+bool ScriptFormatter::visit(TrueLiteral *ast)
+{
+ out(ast->trueToken);
+ return true;
+}
+bool ScriptFormatter::visit(FalseLiteral *ast)
+{
+ out(ast->falseToken);
+ return true;
+}
- void newLine() { lw.ensureNewline(); }
+bool ScriptFormatter::visit(IdentifierExpression *ast)
+{
+ out(ast->identifierToken);
+ return true;
+}
+bool ScriptFormatter::visit(StringLiteral *ast)
+{
+ // correctly handle multiline literals
+ if (ast->literalToken.length == 0)
+ return true;
+ QStringView str = loc2Str(ast->literalToken);
+ if (lw.indentNextlines && str.contains(QLatin1Char('\n'))) {
+ out(str.mid(0, 1));
+ lw.indentNextlines = false;
+ out(str.mid(1));
+ lw.indentNextlines = true;
+ } else {
+ out(str);
+ }
+ return true;
+}
+bool ScriptFormatter::visit(NumericLiteral *ast)
+{
+ out(ast->literalToken);
+ return true;
+}
+bool ScriptFormatter::visit(RegExpLiteral *ast)
+{
+ out(ast->literalToken);
+ return true;
+}
- bool acceptBlockOrIndented(Node *ast, bool finishWithSpaceOrNewline = false)
- {
- if (cast<Block *>(ast)) {
- out(" ");
- accept(ast);
- if (finishWithSpaceOrNewline)
- out(" ");
- return true;
- } else {
- if (finishWithSpaceOrNewline)
- postOps[ast].append([this]() { this->newLine(); });
- lnAcceptIndented(ast);
- return false;
+bool ScriptFormatter::visit(ArrayPattern *ast)
+{
+ out(ast->lbracketToken);
+ int baseIndent = lw.increaseIndent(1);
+ if (ast->elements) {
+ accept(ast->elements);
+ out(ast->commaToken);
+ auto lastElement = lastListElement(ast->elements);
+ if (lastElement->element && cast<ObjectPattern *>(lastElement->element->initializer)) {
+ newLine();
}
+ } else {
+ out(ast->commaToken);
}
+ lw.decreaseIndent(1, baseIndent);
+ out(ast->rbracketToken);
+ return false;
+}
- // we are not supposed to handle the ui
- bool visit(UiPragma *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiEnumDeclaration *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiEnumMemberList *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiImport *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiObjectDefinition *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiObjectInitializer *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiParameterList *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiPublicMember *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiObjectBinding *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiScriptBinding *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiArrayBinding *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiHeaderItemList *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiObjectMemberList *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiArrayMemberList *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiQualifiedId *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiProgram *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiSourceElement *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiVersionSpecifier *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiInlineComponent *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiAnnotation *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiAnnotationList *) override
- {
- Q_ASSERT(false);
- return false;
- }
- bool visit(UiRequired *) override
- {
- Q_ASSERT(false);
- return false;
+bool ScriptFormatter::visit(ObjectPattern *ast)
+{
+ out(ast->lbraceToken);
+ ++expressionDepth;
+ if (ast->properties) {
+ lnAcceptIndented(ast->properties);
+ newLine();
}
+ --expressionDepth;
+ out(ast->rbraceToken);
+ return false;
+}
- bool visit(ThisExpression *ast) override
- {
- out(ast->thisToken);
- return true;
- }
- bool visit(NullExpression *ast) override
- {
- out(ast->nullToken);
- return true;
- }
- bool visit(TrueLiteral *ast) override
- {
- out(ast->trueToken);
- return true;
- }
- bool visit(FalseLiteral *ast) override
- {
- out(ast->falseToken);
- return true;
- }
- bool visit(IdentifierExpression *ast) override
- {
- out(ast->identifierToken);
- return true;
- }
- bool visit(StringLiteral *ast) override
- {
- // correctly handle multiline literals
- if (ast->literalToken.length == 0)
- return true;
- QStringView str = loc2Str(ast->literalToken);
- if (lw.indentNextlines && str.contains(QLatin1Char('\n'))) {
- out(str.mid(0, 1));
- lw.indentNextlines = false;
- out(str.mid(1));
- lw.indentNextlines = true;
- } else {
- out(str);
- }
- return true;
- }
- bool visit(NumericLiteral *ast) override
- {
- out(ast->literalToken);
- return true;
- }
- bool visit(RegExpLiteral *ast) override
- {
- out(ast->literalToken);
- return true;
- }
+bool ScriptFormatter::visit(PatternElementList *ast)
+{
+ for (PatternElementList *it = ast; it; it = it->next) {
+ const bool isObjectInitializer =
+ it->element && cast<ObjectPattern *>(it->element->initializer);
+ if (isObjectInitializer)
+ newLine();
- bool visit(ArrayPattern *ast) override
- {
- out(ast->lbracketToken);
- int baseIndent = lw.increaseIndent(1);
- if (ast->elements)
- accept(ast->elements);
- out(ast->commaToken);
- lw.decreaseIndent(1, baseIndent);
- out(ast->rbracketToken);
- return false;
+ if (it->elision)
+ accept(it->elision);
+ if (it->elision && it->element)
+ out(", ");
+ if (it->element)
+ accept(it->element);
+ if (it->next) {
+ out(", ");
+ if (isObjectInitializer)
+ newLine();
+ }
}
+ return false;
+}
- bool visit(ObjectPattern *ast) override
- {
- out(ast->lbraceToken);
- ++expressionDepth;
- if (ast->properties) {
- lnAcceptIndented(ast->properties);
+bool ScriptFormatter::visit(PatternPropertyList *ast)
+{
+ for (PatternPropertyList *it = ast; it; it = it->next) {
+ accept(it->property);
+ if (it->next) {
+ out(",");
newLine();
}
- --expressionDepth;
- out(ast->rbraceToken);
- return false;
}
+ return false;
+}
- bool visit(PatternElementList *ast) override
- {
- for (PatternElementList *it = ast; it; it = it->next) {
- if (it->elision)
- accept(it->elision);
- if (it->elision && it->element)
- out(", ");
- if (it->element)
- accept(it->element);
- if (it->next)
- out(", ");
+// https://262.ecma-international.org/7.0/#prod-PropertyDefinition
+bool ScriptFormatter::visit(AST::PatternProperty *property)
+{
+ if (property->type == PatternElement::Getter || property->type == PatternElement::Setter
+ || property->type == PatternElement::Method) {
+ // note that MethodDefinitions and FunctionDeclarations have different syntax
+ // https://262.ecma-international.org/7.0/#prod-MethodDefinition
+ // https://262.ecma-international.org/7.0/#prod-FunctionDeclaration
+ // hence visit(FunctionDeclaration*) is not quite appropriate here
+ if (property->type == PatternProperty::Getter)
+ out("get ");
+ else if (property->type == PatternProperty::Setter)
+ out("set ");
+ FunctionExpression *f = AST::cast<FunctionExpression *>(property->initializer);
+ if (f->isGenerator) {
+ out("*");
}
- return false;
- }
-
- bool visit(PatternPropertyList *ast) override
- {
- for (PatternPropertyList *it = ast; it; it = it->next) {
- PatternProperty *assignment = AST::cast<PatternProperty *>(it->property);
- if (assignment) {
- preVisit(assignment);
- bool isStringLike = AST::cast<StringLiteralPropertyName *>(assignment->name)
- || cast<IdentifierPropertyName *>(assignment->name);
- if (isStringLike)
- out("\"");
- accept(assignment->name);
- if (isStringLike)
- out("\"");
- out(": "); // assignment->colonToken
- if (it->next)
- postOps[assignment->initializer].append([this] {
- out(","); // always invalid?
- });
- accept(assignment->initializer);
- if (it->next)
- newLine();
- postVisit(assignment);
- continue;
- }
- PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next);
- if (getterSetter->property) {
- switch (getterSetter->property->type) {
- case PatternElement::Getter:
- out("get");
- break;
- case PatternElement::Setter:
- out("set");
- break;
- default:
- break;
- }
-
- accept(getterSetter->property->name);
- out("(");
- // accept(getterSetter->formals); // TODO
- out(")");
- out(" {");
- // accept(getterSetter->functionBody); // TODO
- out(" }");
+ accept(property->name);
+ out(f->lparenToken);
+ accept(f->formals);
+ out(f->rparenToken);
+ out(f->lbraceToken);
+ const bool scoped = f->lbraceToken.isValid();
+ if (scoped)
+ ++expressionDepth;
+ if (f->body) {
+ if (f->body->next || scoped) {
+ lnAcceptIndented(f->body);
+ lw.newline();
+ } else {
+ auto baseIndent = lw.increaseIndent(1);
+ accept(f->body);
+ lw.decreaseIndent(1, baseIndent);
}
}
+ if (scoped)
+ --expressionDepth;
+ out(f->rbraceToken);
return false;
}
- bool visit(NestedExpression *ast) override
- {
- out(ast->lparenToken);
- int baseIndent = lw.increaseIndent(1);
- accept(ast->expression);
- lw.decreaseIndent(1, baseIndent);
- out(ast->rparenToken);
- return false;
+ // IdentifierReference[?Yield]
+ accept(property->name);
+ bool useInitializer = false;
+ const bool bindingIdentifierExist = !property->bindingIdentifier.isEmpty();
+ if (property->colonToken.isValid()) {
+ // PropertyName[?Yield] : AssignmentExpression[In, ?Yield]
+ out(": ");
+ useInitializer = true;
+ if (bindingIdentifierExist)
+ out(property->bindingIdentifier);
+ if (property->bindingTarget)
+ accept(property->bindingTarget);
}
- bool visit(IdentifierPropertyName *ast) override
- {
- out(ast->id.toString());
- return true;
- }
- bool visit(StringLiteralPropertyName *ast) override
- {
- out(ast->id.toString());
- return true;
- }
- bool visit(NumericLiteralPropertyName *ast) override
- {
- out(QString::number(ast->id));
- return true;
+ if (property->initializer) {
+ // CoverInitializedName[?Yield]
+ if (bindingIdentifierExist) {
+ out(" = ");
+ useInitializer = true;
+ }
+ if (useInitializer)
+ accept(property->initializer);
}
+ return false;
+}
- bool visit(TemplateLiteral *ast) override
- {
- // correctly handle multiline literals
- if (ast->literalToken.length != 0) {
- QStringView str = loc2Str(ast->literalToken);
- if (lw.indentNextlines && str.contains(QLatin1Char('\n'))) {
- out(str.mid(0, 1));
- lw.indentNextlines = false;
- out(str.mid(1));
- lw.indentNextlines = true;
- } else {
- out(str);
- }
+bool ScriptFormatter::visit(NestedExpression *ast)
+{
+ out(ast->lparenToken);
+ int baseIndent = lw.increaseIndent(1);
+ accept(ast->expression);
+ lw.decreaseIndent(1, baseIndent);
+ out(ast->rparenToken);
+ return false;
+}
+
+bool ScriptFormatter::visit(IdentifierPropertyName *ast)
+{
+ out(ast->id.toString());
+ return true;
+}
+bool ScriptFormatter::visit(StringLiteralPropertyName *ast)
+{
+ out(ast->propertyNameToken);
+ return true;
+}
+bool ScriptFormatter::visit(NumericLiteralPropertyName *ast)
+{
+ out(QString::number(ast->id));
+ return true;
+}
+
+bool ScriptFormatter::visit(TemplateLiteral *ast)
+{
+ // correctly handle multiline literals
+ if (ast->literalToken.length != 0) {
+ QStringView str = loc2Str(ast->literalToken);
+ if (lw.indentNextlines && str.contains(QLatin1Char('\n'))) {
+ out(str.mid(0, 1));
+ lw.indentNextlines = false;
+ out(str.mid(1));
+ lw.indentNextlines = true;
+ } else {
+ out(str);
}
- accept(ast->expression);
- return true;
}
+ accept(ast->expression);
+ return true;
+}
- bool visit(ArrayMemberExpression *ast) override
- {
- accept(ast->base);
- out(ast->lbracketToken);
- int indent = lw.increaseIndent(1);
- accept(ast->expression);
- lw.decreaseIndent(1, indent);
- out(ast->rbracketToken);
- return false;
- }
+bool ScriptFormatter::visit(ArrayMemberExpression *ast)
+{
+ accept(ast->base);
+ out(ast->lbracketToken);
+ int indent = lw.increaseIndent(1);
+ accept(ast->expression);
+ lw.decreaseIndent(1, indent);
+ out(ast->rbracketToken);
+ return false;
+}
- bool visit(FieldMemberExpression *ast) override
- {
- accept(ast->base);
- out(ast->dotToken);
- out(ast->identifierToken);
- return false;
- }
+bool ScriptFormatter::visit(FieldMemberExpression *ast)
+{
+ accept(ast->base);
+ out(ast->dotToken);
+ out(ast->identifierToken);
+ return false;
+}
- bool visit(NewMemberExpression *ast) override
- {
- out("new "); // ast->newToken
- accept(ast->base);
- out(ast->lparenToken);
- accept(ast->arguments);
- out(ast->rparenToken);
- return false;
- }
+bool ScriptFormatter::visit(NewMemberExpression *ast)
+{
+ out("new "); // ast->newToken
+ accept(ast->base);
+ out(ast->lparenToken);
+ accept(ast->arguments);
+ out(ast->rparenToken);
+ return false;
+}
- bool visit(NewExpression *ast) override
- {
- out("new "); // ast->newToken
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(NewExpression *ast)
+{
+ out("new "); // ast->newToken
+ accept(ast->expression);
+ return false;
+}
- bool visit(CallExpression *ast) override
- {
- accept(ast->base);
- out(ast->lparenToken);
- int baseIndent = lw.increaseIndent(1);
- accept(ast->arguments);
- lw.decreaseIndent(1, baseIndent);
- out(ast->rparenToken);
- return false;
- }
+bool ScriptFormatter::visit(CallExpression *ast)
+{
+ accept(ast->base);
+ out(ast->lparenToken);
+ accept(ast->arguments);
+ out(ast->rparenToken);
+ return false;
+}
- bool visit(PostIncrementExpression *ast) override
- {
- accept(ast->base);
- out(ast->incrementToken);
- return false;
- }
+bool ScriptFormatter::visit(PostIncrementExpression *ast)
+{
+ accept(ast->base);
+ out(ast->incrementToken);
+ return false;
+}
- bool visit(PostDecrementExpression *ast) override
- {
- accept(ast->base);
- out(ast->decrementToken);
- return false;
- }
+bool ScriptFormatter::visit(PostDecrementExpression *ast)
+{
+ accept(ast->base);
+ out(ast->decrementToken);
+ return false;
+}
- bool visit(PreIncrementExpression *ast) override
- {
- out(ast->incrementToken);
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(PreIncrementExpression *ast)
+{
+ out(ast->incrementToken);
+ accept(ast->expression);
+ return false;
+}
- bool visit(PreDecrementExpression *ast) override
- {
- out(ast->decrementToken);
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(PreDecrementExpression *ast)
+{
+ out(ast->decrementToken);
+ accept(ast->expression);
+ return false;
+}
- bool visit(DeleteExpression *ast) override
- {
- out("delete "); // ast->deleteToken
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(DeleteExpression *ast)
+{
+ out("delete "); // ast->deleteToken
+ accept(ast->expression);
+ return false;
+}
- bool visit(VoidExpression *ast) override
- {
- out("void "); // ast->voidToken
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(VoidExpression *ast)
+{
+ out("void "); // ast->voidToken
+ accept(ast->expression);
+ return false;
+}
- bool visit(TypeOfExpression *ast) override
- {
- out("typeof "); // ast->typeofToken
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(TypeOfExpression *ast)
+{
+ out("typeof "); // ast->typeofToken
+ accept(ast->expression);
+ return false;
+}
- bool visit(UnaryPlusExpression *ast) override
- {
- out(ast->plusToken);
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(UnaryPlusExpression *ast)
+{
+ out(ast->plusToken);
+ accept(ast->expression);
+ return false;
+}
- bool visit(UnaryMinusExpression *ast) override
- {
- out(ast->minusToken);
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(UnaryMinusExpression *ast)
+{
+ out(ast->minusToken);
+ accept(ast->expression);
+ return false;
+}
- bool visit(TildeExpression *ast) override
- {
- out(ast->tildeToken);
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(TildeExpression *ast)
+{
+ out(ast->tildeToken);
+ accept(ast->expression);
+ return false;
+}
- bool visit(NotExpression *ast) override
- {
- out(ast->notToken);
- accept(ast->expression);
- return false;
- }
+bool ScriptFormatter::visit(NotExpression *ast)
+{
+ out(ast->notToken);
+ accept(ast->expression);
+ return false;
+}
- bool visit(BinaryExpression *ast) override
- {
- accept(ast->left);
- out(" ");
- out(ast->operatorToken);
- out(" ");
- accept(ast->right);
- return false;
- }
+bool ScriptFormatter::visit(BinaryExpression *ast)
+{
+ accept(ast->left);
+ out(" ");
+ out(ast->operatorToken);
+ out(" ");
+ accept(ast->right);
+ return false;
+}
- bool visit(ConditionalExpression *ast) override
- {
- accept(ast->expression);
- out(" ? "); // ast->questionToken
- accept(ast->ok);
- out(" : "); // ast->colonToken
- accept(ast->ko);
- return false;
- }
+bool ScriptFormatter::visit(ConditionalExpression *ast)
+{
+ accept(ast->expression);
+ out(" ? "); // ast->questionToken
+ accept(ast->ok);
+ out(" : "); // ast->colonToken
+ accept(ast->ko);
+ return false;
+}
- bool visit(Block *ast) override
- {
- out(ast->lbraceToken);
+bool ScriptFormatter::visit(Block *ast)
+{
+ out(ast->lbraceToken);
+ if (ast->statements) {
++expressionDepth;
lnAcceptIndented(ast->statements);
newLine();
--expressionDepth;
- out(ast->rbraceToken);
- return false;
}
+ out(ast->rbraceToken);
+ return false;
+}
- bool visit(VariableStatement *ast) override
- {
- out(ast->declarationKindToken);
- out(" ");
- accept(ast->declarations);
- if (addSemicolons())
- out(";");
- return false;
+bool ScriptFormatter::visit(VariableStatement *ast)
+{
+ out(ast->declarationKindToken);
+ out(" ");
+ accept(ast->declarations);
+ if (addSemicolons())
+ out(";");
+ return false;
+}
+
+bool ScriptFormatter::visit(PatternElement *ast)
+{
+ switch (ast->type) {
+ case PatternElement::Literal:
+ case PatternElement::Method:
+ case PatternElement::Binding:
+ break;
+ case PatternElement::Getter:
+ out("get ");
+ break;
+ case PatternElement::Setter:
+ out("set ");
+ break;
+ case PatternElement::SpreadElement:
+ out("...");
+ break;
+ }
+
+ accept(ast->bindingTarget);
+ if (!ast->destructuringPattern())
+ out(ast->identifierToken);
+ if (ast->initializer) {
+ if (ast->isVariableDeclaration() || ast->type == AST::PatternElement::Binding)
+ out(" = ");
+ accept(ast->initializer);
}
+ return false;
+}
+bool ScriptFormatter::visit(EmptyStatement *ast)
+{
+ out(ast->semicolonToken);
+ return false;
+}
- void outputScope(VariableScope scope) {
- switch (scope) {
- case VariableScope::Const:
- out("const ");
- break;
- case VariableScope::Let:
- out("let ");
- break;
- case VariableScope::Var:
- out("var ");
- break;
- default:
- break;
+bool ScriptFormatter::visit(IfStatement *ast)
+{
+ out(ast->ifToken);
+ out(" ");
+ out(ast->lparenToken);
+ preVisit(ast->expression);
+ ast->expression->accept0(this);
+ out(ast->rparenToken);
+ postVisit(ast->expression);
+ acceptBlockOrIndented(ast->ok, ast->ko);
+ if (ast->ko) {
+ out(ast->elseToken);
+ if (cast<Block *>(ast->ko) || cast<IfStatement *>(ast->ko)) {
+ out(" ");
+ accept(ast->ko);
+ } else {
+ lnAcceptIndented(ast->ko);
}
}
+ return false;
+}
- bool visit(PatternElement *ast) override
- {
- if (ast->isForDeclaration) {
- outputScope(ast->scope);
- }
- accept(ast->bindingTarget);
- switch (ast->type) {
- case PatternElement::Literal:
- case PatternElement::Method:
- case PatternElement::Binding:
- break;
- case PatternElement::Getter:
- out("get ");
- break;
- case PatternElement::Setter:
- out("set ");
- break;
- case PatternElement::SpreadElement:
- out("...");
- break;
+bool ScriptFormatter::visit(DoWhileStatement *ast)
+{
+ out(ast->doToken);
+ acceptBlockOrIndented(ast->statement, true);
+ out(ast->whileToken);
+ out(" ");
+ out(ast->lparenToken);
+ accept(ast->expression);
+ out(ast->rparenToken);
+ return false;
+}
+
+bool ScriptFormatter::visit(WhileStatement *ast)
+{
+ out(ast->whileToken);
+ out(" ");
+ out(ast->lparenToken);
+ accept(ast->expression);
+ out(ast->rparenToken);
+ acceptBlockOrIndented(ast->statement);
+ return false;
+}
+
+bool ScriptFormatter::visit(ForStatement *ast)
+{
+ out(ast->forToken);
+ out(" ");
+ out(ast->lparenToken);
+ if (ast->initialiser) {
+ accept(ast->initialiser);
+ } else if (ast->declarations) {
+ if (auto pe = ast->declarations->declaration) {
+ out(pe->declarationKindToken);
+ out(" ");
}
- out(ast->identifierToken);
- if (ast->initializer) {
- if (ast->isVariableDeclaration())
- out(" = ");
- accept(ast->initializer);
+ for (VariableDeclarationList *it = ast->declarations; it; it = it->next) {
+ accept(it->declaration);
}
- return false;
}
+ out("; "); // ast->firstSemicolonToken
+ accept(ast->condition);
+ out("; "); // ast->secondSemicolonToken
+ accept(ast->expression);
+ out(ast->rparenToken);
+ acceptBlockOrIndented(ast->statement);
+ return false;
+}
- bool visit(EmptyStatement *ast) override
- {
- out(ast->semicolonToken);
- return false;
+bool ScriptFormatter::visit(ForEachStatement *ast)
+{
+ out(ast->forToken);
+ out(" ");
+ out(ast->lparenToken);
+ if (auto pe = AST::cast<PatternElement *>(ast->lhs)) {
+ out(pe->declarationKindToken);
+ out(" ");
}
+ accept(ast->lhs);
+ out(" ");
+ out(ast->inOfToken);
+ out(" ");
+ accept(ast->expression);
+ out(ast->rparenToken);
+ acceptBlockOrIndented(ast->statement);
+ return false;
+}
- bool visit(IfStatement *ast) override
- {
- out(ast->ifToken);
+bool ScriptFormatter::visit(ContinueStatement *ast)
+{
+ out(ast->continueToken);
+ if (!ast->label.isNull()) {
out(" ");
- out(ast->lparenToken);
- preVisit(ast->expression);
- ast->expression->accept0(this);
- out(ast->rparenToken);
- postVisit(ast->expression);
- acceptBlockOrIndented(ast->ok, ast->ko);
- if (ast->ko) {
- out(ast->elseToken);
- if (cast<Block *>(ast->ko) || cast<IfStatement *>(ast->ko)) {
- out(" ");
- accept(ast->ko);
- } else {
- lnAcceptIndented(ast->ko);
- }
- }
- return false;
+ out(ast->identifierToken);
}
+ if (addSemicolons())
+ out(";");
+ return false;
+}
- bool visit(DoWhileStatement *ast) override
- {
- out(ast->doToken);
- acceptBlockOrIndented(ast->statement, true);
- out(ast->whileToken);
+bool ScriptFormatter::visit(BreakStatement *ast)
+{
+ out(ast->breakToken);
+ if (!ast->label.isNull()) {
out(" ");
- out(ast->lparenToken);
- accept(ast->expression);
- out(ast->rparenToken);
- return false;
+ out(ast->identifierToken);
}
+ if (addSemicolons())
+ out(";");
+ return false;
+}
- bool visit(WhileStatement *ast) override
- {
- out(ast->whileToken);
- out(" ");
- out(ast->lparenToken);
+bool ScriptFormatter::visit(ReturnStatement *ast)
+{
+ out(ast->returnToken);
+ if (ast->expression) {
+ if (ast->returnToken.length != 0)
+ out(" ");
accept(ast->expression);
- out(ast->rparenToken);
- acceptBlockOrIndented(ast->statement);
- return false;
}
+ if (ast->returnToken.length > 0 && addSemicolons())
+ out(";");
+ return false;
+}
- bool visit(ForStatement *ast) override
- {
- out(ast->forToken);
+bool ScriptFormatter::visit(ThrowStatement *ast)
+{
+ out(ast->throwToken);
+ if (ast->expression) {
out(" ");
- out(ast->lparenToken);
- if (ast->initialiser) {
- accept(ast->initialiser);
- } else if (ast->declarations) {
- outputScope(ast->declarations->declaration->scope);
- accept(ast->declarations);
- }
- out("; "); // ast->firstSemicolonToken
- accept(ast->condition);
- out("; "); // ast->secondSemicolonToken
accept(ast->expression);
- out(ast->rparenToken);
- acceptBlockOrIndented(ast->statement);
- return false;
}
+ if (addSemicolons())
+ out(";");
+ return false;
+}
- bool visit(ForEachStatement *ast) override
- {
- out(ast->forToken);
- out(" ");
- out(ast->lparenToken);
- accept(ast->lhs);
+bool ScriptFormatter::visit(WithStatement *ast)
+{
+ out(ast->withToken);
+ out(" ");
+ out(ast->lparenToken);
+ accept(ast->expression);
+ out(ast->rparenToken);
+ acceptBlockOrIndented(ast->statement);
+ return false;
+}
+
+bool ScriptFormatter::visit(SwitchStatement *ast)
+{
+ out(ast->switchToken);
+ out(" ");
+ out(ast->lparenToken);
+ accept(ast->expression);
+ out(ast->rparenToken);
+ out(" ");
+ accept(ast->block);
+ return false;
+}
+
+bool ScriptFormatter::visit(CaseBlock *ast)
+{
+ out(ast->lbraceToken);
+ ++expressionDepth;
+ newLine();
+ accept(ast->clauses);
+ if (ast->clauses && ast->defaultClause)
+ newLine();
+ accept(ast->defaultClause);
+ if (ast->moreClauses)
+ newLine();
+ accept(ast->moreClauses);
+ newLine();
+ --expressionDepth;
+ out(ast->rbraceToken);
+ return false;
+}
+
+bool ScriptFormatter::visit(CaseClause *ast)
+{
+ out("case "); // ast->caseToken
+ accept(ast->expression);
+ out(ast->colonToken);
+ if (ast->statements)
+ lnAcceptIndented(ast->statements);
+ return false;
+}
+
+bool ScriptFormatter::visit(DefaultClause *ast)
+{
+ out(ast->defaultToken);
+ out(ast->colonToken);
+ lnAcceptIndented(ast->statements);
+ return false;
+}
+
+bool ScriptFormatter::visit(LabelledStatement *ast)
+{
+ out(ast->identifierToken);
+ out(": "); // ast->colonToken
+ accept(ast->statement);
+ return false;
+}
+
+bool ScriptFormatter::visit(TryStatement *ast)
+{
+ out("try "); // ast->tryToken
+ accept(ast->statement);
+ if (ast->catchExpression) {
out(" ");
- out(ast->inOfToken);
+ accept(ast->catchExpression);
+ }
+ if (ast->finallyExpression) {
out(" ");
- accept(ast->expression);
- out(ast->rparenToken);
- acceptBlockOrIndented(ast->statement);
- return false;
+ accept(ast->finallyExpression);
}
+ return false;
+}
- bool visit(ContinueStatement *ast) override
- {
- out(ast->continueToken);
- if (!ast->label.isNull()) {
- out(" ");
+bool ScriptFormatter::visit(Catch *ast)
+{
+ out(ast->catchToken);
+ out(" ");
+ out(ast->lparenToken);
+ out(ast->identifierToken);
+ out(") "); // ast->rparenToken
+ accept(ast->statement);
+ return false;
+}
+
+bool ScriptFormatter::visit(Finally *ast)
+{
+ out("finally "); // ast->finallyToken
+ accept(ast->statement);
+ return false;
+}
+
+bool ScriptFormatter::visit(FunctionDeclaration *ast)
+{
+ return ScriptFormatter::visit(static_cast<FunctionExpression *>(ast));
+}
+
+bool ScriptFormatter::visit(FunctionExpression *ast)
+{
+ if (!ast->isArrowFunction) {
+ if (ast->isGenerator) {
+ out("function* ");
+ } else {
+ out("function ");
+ }
+ if (!ast->name.isNull())
out(ast->identifierToken);
+ }
+ out(ast->lparenToken);
+ const bool needParentheses = ast->formals
+ && (ast->formals->next
+ || (ast->formals->element && ast->formals->element->bindingTarget));
+ if (ast->isArrowFunction && needParentheses)
+ out("(");
+ int baseIndent = lw.increaseIndent(1);
+ accept(ast->formals);
+ lw.decreaseIndent(1, baseIndent);
+ if (ast->isArrowFunction && needParentheses)
+ out(")");
+ out(ast->rparenToken);
+ if (ast->isArrowFunction && !ast->formals)
+ out("()");
+ out(" ");
+ if (ast->isArrowFunction)
+ out("=> ");
+ out(ast->lbraceToken);
+ if (ast->lbraceToken.length != 0)
+ ++expressionDepth;
+ if (ast->body) {
+ if (ast->body->next || ast->lbraceToken.length != 0) {
+ lnAcceptIndented(ast->body);
+ newLine();
+ } else {
+ // print a single statement in one line. E.g. x => x * 2
+ baseIndent = lw.increaseIndent(1);
+ accept(ast->body);
+ lw.decreaseIndent(1, baseIndent);
}
- if (addSemicolons())
- out(";");
- return false;
}
+ if (ast->lbraceToken.length != 0)
+ --expressionDepth;
+ out(ast->rbraceToken);
+ return false;
+}
- bool visit(BreakStatement *ast) override
- {
- out(ast->breakToken);
- if (!ast->label.isNull()) {
- out(" ");
- out(ast->identifierToken);
- }
- if (addSemicolons())
- out(";");
- return false;
+bool ScriptFormatter::visit(Elision *ast)
+{
+ for (Elision *it = ast; it; it = it->next) {
+ if (it->next)
+ out(", "); // ast->commaToken
}
+ return false;
+}
- bool visit(ReturnStatement *ast) override
- {
- out(ast->returnToken);
- if (ast->expression) {
- if (ast->returnToken.length != 0)
- out(" ");
- accept(ast->expression);
+bool ScriptFormatter::visit(ArgumentList *ast)
+{
+ for (ArgumentList *it = ast; it; it = it->next) {
+ if (it->isSpreadElement)
+ out("...");
+ accept(it->expression);
+ if (it->next) {
+ out(", "); // it->commaToken
}
- if (ast->returnToken.length > 0 && addSemicolons())
- out(";");
- return false;
}
+ return false;
+}
- bool visit(ThrowStatement *ast) override
- {
- out(ast->throwToken);
- if (ast->expression) {
- out(" ");
- accept(ast->expression);
+bool ScriptFormatter::visit(StatementList *ast)
+{
+ ++expressionDepth;
+ for (StatementList *it = ast; it; it = it->next) {
+ // ### work around parser bug: skip empty statements with wrong tokens
+ if (EmptyStatement *emptyStatement = cast<EmptyStatement *>(it->statement)) {
+ if (loc2Str(emptyStatement->semicolonToken) != QLatin1String(";"))
+ continue;
}
- if (addSemicolons())
- out(";");
- return false;
- }
- bool visit(WithStatement *ast) override
- {
- out(ast->withToken);
- out(" ");
- out(ast->lparenToken);
- accept(ast->expression);
- out(ast->rparenToken);
- acceptBlockOrIndented(ast->statement);
- return false;
+ accept(it->statement);
+ if (it->next) {
+ // There might be a post-comment attached to the current
+ // statement or a pre-comment attached to the next
+ // statmente or both.
+ // If any of those are present they will take care of
+ // handling the spacing between the statements so we
+ // don't need to push any newline.
+ auto *commentForCurrentStatement = comments->commentForNode(it->statement);
+ auto *commentForNextStatement = comments->commentForNode(it->next->statement);
+
+ if (
+ (commentForCurrentStatement && !commentForCurrentStatement->postComments().empty())
+ || (commentForNextStatement && !commentForNextStatement->preComments().empty())
+ ) continue;
+
+ quint32 lineDelta = it->next->firstSourceLocation().startLine
+ - it->statement->lastSourceLocation().startLine;
+ lineDelta = std::clamp(lineDelta, quint32{ 1 }, quint32{ 2 });
+
+ newLine(lineDelta);
+ }
}
+ --expressionDepth;
+ return false;
+}
- bool visit(SwitchStatement *ast) override
- {
- out(ast->switchToken);
- out(" ");
- out(ast->lparenToken);
- accept(ast->expression);
- out(ast->rparenToken);
- out(" ");
- accept(ast->block);
- return false;
+bool ScriptFormatter::visit(VariableDeclarationList *ast)
+{
+ for (VariableDeclarationList *it = ast; it; it = it->next) {
+ accept(it->declaration);
+ if (it->next)
+ out(", "); // it->commaToken
}
+ return false;
+}
- bool visit(CaseBlock *ast) override
- {
- out(ast->lbraceToken);
- ++expressionDepth;
- newLine();
- accept(ast->clauses);
- if (ast->clauses && ast->defaultClause)
- newLine();
- accept(ast->defaultClause);
- if (ast->moreClauses)
+bool ScriptFormatter::visit(CaseClauses *ast)
+{
+ for (CaseClauses *it = ast; it; it = it->next) {
+ accept(it->clause);
+ if (it->next)
newLine();
- accept(ast->moreClauses);
- newLine();
- --expressionDepth;
- out(ast->rbraceToken);
- return false;
}
+ return false;
+}
- bool visit(CaseClause *ast) override
- {
- out("case "); // ast->caseToken
- accept(ast->expression);
- out(ast->colonToken);
- if (ast->statements)
- lnAcceptIndented(ast->statements);
- return false;
- }
+bool ScriptFormatter::visit(FormalParameterList *ast)
+{
+ for (FormalParameterList *it = ast; it; it = it->next) {
+ // compare FormalParameterList::finish
+ if (auto id = it->element->bindingIdentifier.toString(); !id.isEmpty())
+ out(id);
+ if (it->element->bindingTarget)
+ accept(it->element->bindingTarget);
+ if (it->next)
+ out(", ");
+ }
+ return false;
+}
- bool visit(DefaultClause *ast) override
- {
- out(ast->defaultToken);
- out(ast->colonToken);
- lnAcceptIndented(ast->statements);
- return false;
- }
+// to check
+bool ScriptFormatter::visit(SuperLiteral *)
+{
+ out("super");
+ return true;
+}
+bool ScriptFormatter::visit(ComputedPropertyName *)
+{
+ out("[");
+ return true;
+}
+bool ScriptFormatter::visit(Expression *el)
+{
+ accept(el->left);
+ out(", ");
+ accept(el->right);
+ return false;
+}
+bool ScriptFormatter::visit(ExpressionStatement *el)
+{
+ if (addSemicolons())
+ postOps[el->expression].append([this]() { out(";"); });
+ return true;
+}
- bool visit(LabelledStatement *ast) override
- {
- out(ast->identifierToken);
- out(": "); // ast->colonToken
- accept(ast->statement);
- return false;
- }
+// Return false because we want to omit default function calls in accept0 implementation.
+bool ScriptFormatter::visit(ClassDeclaration *ast)
+{
+ preVisit(ast);
+ out(ast->classToken);
+ out(" ");
+ out(ast->name);
+ if (ast->heritage) {
+ out(" extends ");
+ accept(ast->heritage);
+ }
+ out(" {");
+ int baseIndent = lw.increaseIndent();
+ for (ClassElementList *it = ast->elements; it; it = it->next) {
+ lw.newline();
+ if (it->isStatic)
+ out("static ");
+ accept(it->property);
+ lw.newline();
+ }
+ lw.decreaseIndent(1, baseIndent);
+ out("}");
+ postVisit(ast);
+ return false;
+}
- bool visit(TryStatement *ast) override
- {
- out("try "); // ast->tryToken
- accept(ast->statement);
- if (ast->catchExpression) {
- out(" ");
- accept(ast->catchExpression);
- }
- if (ast->finallyExpression) {
- out(" ");
- accept(ast->finallyExpression);
- }
- return false;
+bool ScriptFormatter::visit(AST::ImportDeclaration *ast)
+{
+ out(ast->importToken);
+ lw.space();
+ if (!ast->moduleSpecifier.isNull()) {
+ out(ast->moduleSpecifierToken);
}
+ return true;
+}
- bool visit(Catch *ast) override
- {
- out(ast->catchToken);
- out(" ");
- out(ast->lparenToken);
+bool ScriptFormatter::visit(AST::ImportSpecifier *ast)
+{
+ if (!ast->identifier.isNull()) {
out(ast->identifierToken);
- out(") "); // ast->rparenToken
- accept(ast->statement);
- return false;
+ lw.space();
+ out("as");
+ lw.space();
}
+ out(ast->importedBindingToken);
+ return true;
+}
- bool visit(Finally *ast) override
- {
- out("finally "); // ast->finallyToken
- accept(ast->statement);
- return false;
- }
+bool ScriptFormatter::visit(AST::NameSpaceImport *ast)
+{
+ out(ast->starToken);
+ lw.space();
+ out("as");
+ lw.space();
+ out(ast->importedBindingToken);
+ return true;
+}
- bool visit(FunctionDeclaration *ast) override
- {
- return visit(static_cast<FunctionExpression *>(ast));
+bool ScriptFormatter::visit(AST::ImportsList *ast)
+{
+ for (ImportsList *it = ast; it; it = it->next) {
+ accept(it->importSpecifier);
+ if (it->next) {
+ out(",");
+ lw.space();
+ }
+ }
+ return false;
+}
+bool ScriptFormatter::visit(AST::NamedImports *ast)
+{
+ out(ast->leftBraceToken);
+ if (ast->importsList) {
+ lw.space();
}
+ return true;
+}
- bool visit(FunctionExpression *ast) override
- {
- if (!ast->isArrowFunction) {
- out("function "); // ast->functionToken
- if (!ast->name.isNull())
- out(ast->identifierToken);
- }
- out(ast->lparenToken);
- if (ast->isArrowFunction && ast->formals && ast->formals->next)
- out("(");
- int baseIndent = lw.increaseIndent(1);
- accept(ast->formals);
- lw.decreaseIndent(1, baseIndent);
- if (ast->isArrowFunction && ast->formals && ast->formals->next)
- out(")");
- out(ast->rparenToken);
- if (ast->isArrowFunction && !ast->formals)
- out("()");
- out(" ");
- if (ast->isArrowFunction)
- out("=> ");
- out(ast->lbraceToken);
- if (ast->lbraceToken.length != 0)
- ++expressionDepth;
- if (ast->body) {
- if (ast->body->next || ast->lbraceToken.length != 0) {
- lnAcceptIndented(ast->body);
- newLine();
- } else {
- // print a single statement in one line. E.g. x => x * 2
- baseIndent = lw.increaseIndent(1);
- accept(ast->body);
- lw.decreaseIndent(1, baseIndent);
- }
+bool ScriptFormatter::visit(AST::ImportClause *ast)
+{
+ if (!ast->importedDefaultBinding.isNull()) {
+ out(ast->importedDefaultBindingToken);
+ if (ast->nameSpaceImport || ast->namedImports) {
+ out(",");
+ lw.space();
}
- if (ast->lbraceToken.length != 0)
- --expressionDepth;
- out(ast->rbraceToken);
- return false;
}
+ return true;
+}
- bool visit(Elision *ast) override
- {
- for (Elision *it = ast; it; it = it->next) {
- if (it->next)
- out(", "); // ast->commaToken
- }
- return false;
+bool ScriptFormatter::visit(AST::ExportDeclaration *ast)
+{
+ out(ast->exportToken);
+ lw.space();
+ if (ast->exportDefault) {
+ out("default");
+ lw.space();
+ }
+ if (ast->exportsAll()) {
+ out("*");
}
+ return true;
+}
- bool visit(ArgumentList *ast) override
- {
- for (ArgumentList *it = ast; it; it = it->next) {
- if (it->isSpreadElement)
- out("...");
- accept(it->expression);
- if (it->next) {
- out(", "); // it->commaToken
- }
- }
- return false;
+bool ScriptFormatter::visit(AST::ExportClause *ast)
+{
+ out(ast->leftBraceToken);
+ if (ast->exportsList) {
+ lw.space();
}
+ return true;
+}
- bool visit(StatementList *ast) override
- {
- ++expressionDepth;
- for (StatementList *it = ast; it; it = it->next) {
- // ### work around parser bug: skip empty statements with wrong tokens
- if (EmptyStatement *emptyStatement = cast<EmptyStatement *>(it->statement)) {
- if (loc2Str(emptyStatement->semicolonToken) != QLatin1String(";"))
- continue;
- }
+bool ScriptFormatter::visit(AST::ExportSpecifier *ast)
+{
+ out(ast->identifier);
+ if (ast->exportedIdentifierToken.isValid()) {
+ lw.space();
+ out("as");
+ lw.space();
+ out(ast->exportedIdentifier);
+ }
+ return true;
+}
- accept(it->statement);
- if (it->next)
- newLine();
+bool ScriptFormatter::visit(AST::ExportsList *ast)
+{
+ for (ExportsList *it = ast; it; it = it->next) {
+ accept(it->exportSpecifier);
+ if (it->next) {
+ out(",");
+ lw.space();
}
- --expressionDepth;
- return false;
}
+ return false;
+}
- bool visit(VariableDeclarationList *ast) override
- {
- for (VariableDeclarationList *it = ast; it; it = it->next) {
- accept(it->declaration);
- if (it->next)
- out(", "); // it->commaToken
- }
- return false;
- }
+bool ScriptFormatter::visit(AST::FromClause *ast)
+{
+ lw.space();
+ out(ast->fromToken);
+ lw.space();
+ out(ast->moduleSpecifierToken);
+ return true;
+}
- bool visit(CaseClauses *ast) override
- {
- for (CaseClauses *it = ast; it; it = it->next) {
- accept(it->clause);
- if (it->next)
- newLine();
- }
- return false;
- }
+void ScriptFormatter::endVisit(ComputedPropertyName *)
+{
+ out("]");
+}
- bool visit(FormalParameterList *ast) override
- {
- for (FormalParameterList *it = ast; it; it = it->next) {
- out(it->element->bindingIdentifier.toString()); // TODO
- if (it->next)
- out(", ");
- }
- return false;
+void ScriptFormatter::endVisit(AST::ExportDeclaration *ast)
+{
+ // add a semicolon at the end of the following expressions
+ // export * FromClause ;
+ // export ExportClause FromClause ;
+ if (ast->fromClause) {
+ out(";");
}
- // to check
- bool visit(TypeExpression *) override { return true; }
- bool visit(SuperLiteral *) override
- {
- out("super");
- return true;
- }
- bool visit(PatternProperty *) override { return true; }
- bool visit(ComputedPropertyName *) override
- {
- out("[");
- return true;
- }
- bool visit(TaggedTemplate *) override { return true; }
- bool visit(Expression *el) override
- {
- accept(el->left);
- out(", ");
- accept(el->right);
- return false;
- }
- bool visit(ExpressionStatement *el) override
- {
- if (addSemicolons())
- postOps[el->expression].append([this]() { out(";"); });
- return true;
+ // add a semicolon at the end of the following expressions
+ // export ExportClause ;
+ if (ast->exportClause && !ast->fromClause) {
+ out(";");
}
- bool visit(YieldExpression *) override { return true; }
- bool visit(ClassExpression *) override { return true; }
-
- // Return false because we want to omit default function calls in accept0 implementation.
- bool visit(ClassDeclaration *ast) override
- {
- preVisit(ast);
- out(ast->classToken);
- out(" ");
- out(ast->name);
- if (ast->heritage) {
- out(" extends ");
- accept(ast->heritage);
+
+ // add a semicolon at the end of the following expressions
+ // export default [lookahead ∉ { function, class }] AssignmentExpression;
+ if (ast->exportDefault && ast->variableStatementOrDeclaration) {
+ // lookahead ∉ { function, class }
+ if (!(ast->variableStatementOrDeclaration->kind == Node::Kind_FunctionDeclaration
+ || ast->variableStatementOrDeclaration->kind == Node::Kind_ClassDeclaration)) {
+ out(";");
}
- out(" {");
- int baseIndent = lw.increaseIndent();
- for (ClassElementList *it = ast->elements; it; it = it->next) {
- PatternProperty *property = it->property;
- lw.newline();
- preVisit(property);
- if (it->isStatic)
- out("static ");
- if (property->type == PatternProperty::Getter)
- out("get ");
- else if (property->type == PatternProperty::Setter)
- out("set ");
- FunctionExpression *f = AST::cast<FunctionExpression *>(property->initializer);
- const bool scoped = f->lbraceToken.length != 0;
- out(f->functionToken);
- out(f->lparenToken);
- accept(f->formals);
- out(f->rparenToken);
- out(f->lbraceToken);
- if (scoped)
- ++expressionDepth;
- if (f->body) {
- if (f->body->next || scoped) {
- lnAcceptIndented(f->body);
- lw.newline();
- } else {
- baseIndent = lw.increaseIndent(1);
- accept(f->body);
- lw.decreaseIndent(1, baseIndent);
- }
- }
- if (scoped)
- --expressionDepth;
- out(f->rbraceToken);
- lw.newline();
- postVisit(property);
+ // ArrowFunction in QQmlJS::AST is handled with the help of FunctionDeclaration
+ // and not as part of AssignmentExpression (as per ECMA
+ // https://262.ecma-international.org/7.0/#prod-AssignmentExpression)
+ if (ast->variableStatementOrDeclaration->kind == Node::Kind_FunctionDeclaration
+ && static_cast<AST::FunctionDeclaration *>(ast->variableStatementOrDeclaration)
+ ->isArrowFunction) {
+ out(";");
}
- lw.decreaseIndent(1, baseIndent);
- out("}");
- postVisit(ast);
- return false;
}
+}
+
+void ScriptFormatter::endVisit(AST::ExportClause *ast)
+{
+ if (ast->exportsList) {
+ lw.space();
+ }
+ out(ast->rightBraceToken);
+}
- bool visit(ClassElementList *) override { return true; }
- bool visit(Program *) override { return true; }
- bool visit(NameSpaceImport *) override { return true; }
- bool visit(ImportSpecifier *) override { return true; }
- bool visit(ImportsList *) override { return true; }
- bool visit(NamedImports *) override { return true; }
- bool visit(FromClause *) override { return true; }
- bool visit(ImportClause *) override { return true; }
- bool visit(ImportDeclaration *) override { return true; }
- bool visit(ExportSpecifier *) override { return true; }
- bool visit(ExportsList *) override { return true; }
- bool visit(ExportClause *) override { return true; }
- bool visit(ExportDeclaration *) override { return true; }
- bool visit(ESModule *) override { return true; }
- bool visit(DebuggerStatement *) override { return true; }
- bool visit(Type *) override { return true; }
- bool visit(TypeArgument *) override { return true; }
- bool visit(TypeAnnotation *) override { return true; }
-
- // overridden to use BasicVisitor (and ensure warnings about new added AST)
- void endVisit(UiProgram *) override { }
- void endVisit(UiImport *) override { }
- void endVisit(UiHeaderItemList *) override { }
- void endVisit(UiPragma *) override { }
- void endVisit(UiPublicMember *) override { }
- void endVisit(UiSourceElement *) override { }
- void endVisit(UiObjectDefinition *) override { }
- void endVisit(UiObjectInitializer *) override { }
- void endVisit(UiObjectBinding *) override { }
- void endVisit(UiScriptBinding *) override { }
- void endVisit(UiArrayBinding *) override { }
- void endVisit(UiParameterList *) override { }
- void endVisit(UiObjectMemberList *) override { }
- void endVisit(UiArrayMemberList *) override { }
- void endVisit(UiQualifiedId *) override { }
- void endVisit(UiEnumDeclaration *) override { }
- void endVisit(UiEnumMemberList *) override { }
- void endVisit(UiVersionSpecifier *) override { }
- void endVisit(UiInlineComponent *) override { }
- void endVisit(UiAnnotation *) override { }
- void endVisit(UiAnnotationList *) override { }
- void endVisit(UiRequired *) override { }
- void endVisit(TypeExpression *) override { }
- void endVisit(ThisExpression *) override { }
- void endVisit(IdentifierExpression *) override { }
- void endVisit(NullExpression *) override { }
- void endVisit(TrueLiteral *) override { }
- void endVisit(FalseLiteral *) override { }
- void endVisit(SuperLiteral *) override { }
- void endVisit(StringLiteral *) override { }
- void endVisit(TemplateLiteral *) override { }
- void endVisit(NumericLiteral *) override { }
- void endVisit(RegExpLiteral *) override { }
- void endVisit(ArrayPattern *) override { }
- void endVisit(ObjectPattern *) override { }
- void endVisit(PatternElementList *) override { }
- void endVisit(PatternPropertyList *) override { }
- void endVisit(PatternElement *) override { }
- void endVisit(PatternProperty *) override { }
- void endVisit(Elision *) override { }
- void endVisit(NestedExpression *) override { }
- void endVisit(IdentifierPropertyName *) override { }
- void endVisit(StringLiteralPropertyName *) override { }
- void endVisit(NumericLiteralPropertyName *) override { }
- void endVisit(ComputedPropertyName *) override { out("]"); }
- void endVisit(ArrayMemberExpression *) override { }
- void endVisit(FieldMemberExpression *) override { }
- void endVisit(TaggedTemplate *) override { }
- void endVisit(NewMemberExpression *) override { }
- void endVisit(NewExpression *) override { }
- void endVisit(CallExpression *) override { }
- void endVisit(ArgumentList *) override { }
- void endVisit(PostIncrementExpression *) override { }
- void endVisit(PostDecrementExpression *) override { }
- void endVisit(DeleteExpression *) override { }
- void endVisit(VoidExpression *) override { }
- void endVisit(TypeOfExpression *) override { }
- void endVisit(PreIncrementExpression *) override { }
- void endVisit(PreDecrementExpression *) override { }
- void endVisit(UnaryPlusExpression *) override { }
- void endVisit(UnaryMinusExpression *) override { }
- void endVisit(TildeExpression *) override { }
- void endVisit(NotExpression *) override { }
- void endVisit(BinaryExpression *) override { }
- void endVisit(ConditionalExpression *) override { }
- void endVisit(Expression *) override { }
- void endVisit(Block *) override { }
- void endVisit(StatementList *) override { }
- void endVisit(VariableStatement *) override { }
- void endVisit(VariableDeclarationList *) override { }
- void endVisit(EmptyStatement *) override { }
- void endVisit(ExpressionStatement *) override { }
- void endVisit(IfStatement *) override { }
- void endVisit(DoWhileStatement *) override { }
- void endVisit(WhileStatement *) override { }
- void endVisit(ForStatement *) override { }
- void endVisit(ForEachStatement *) override { }
- void endVisit(ContinueStatement *) override { }
- void endVisit(BreakStatement *) override { }
- void endVisit(ReturnStatement *) override { }
- void endVisit(YieldExpression *) override { }
- void endVisit(WithStatement *) override { }
- void endVisit(SwitchStatement *) override { }
- void endVisit(CaseBlock *) override { }
- void endVisit(CaseClauses *) override { }
- void endVisit(CaseClause *) override { }
- void endVisit(DefaultClause *) override { }
- void endVisit(LabelledStatement *) override { }
- void endVisit(ThrowStatement *) override { }
- void endVisit(TryStatement *) override { }
- void endVisit(Catch *) override { }
- void endVisit(Finally *) override { }
- void endVisit(FunctionDeclaration *) override { }
- void endVisit(FunctionExpression *) override { }
- void endVisit(FormalParameterList *) override { }
- void endVisit(ClassExpression *) override { }
- void endVisit(ClassDeclaration *) override { }
- void endVisit(ClassElementList *) override { }
- void endVisit(Program *) override { }
- void endVisit(NameSpaceImport *) override { }
- void endVisit(ImportSpecifier *) override { }
- void endVisit(ImportsList *) override { }
- void endVisit(NamedImports *) override { }
- void endVisit(FromClause *) override { }
- void endVisit(ImportClause *) override { }
- void endVisit(ImportDeclaration *) override { }
- void endVisit(ExportSpecifier *) override { }
- void endVisit(ExportsList *) override { }
- void endVisit(ExportClause *) override { }
- void endVisit(ExportDeclaration *) override { }
- void endVisit(ESModule *) override { }
- void endVisit(DebuggerStatement *) override { }
- void endVisit(Type *) override { }
- void endVisit(TypeArgument *) override { }
- void endVisit(TypeAnnotation *) override { }
-
- void throwRecursionDepthError() override
- {
- out("/* ERROR: Hit recursion limit visiting AST, rewrite failed */");
+void ScriptFormatter::endVisit(AST::NamedImports *ast)
+{
+ if (ast->importsList) {
+ lw.space();
}
-};
+ out(ast->rightBraceToken);
+}
+
+void ScriptFormatter::endVisit(AST::ImportDeclaration *)
+{
+ out(";");
+}
+
+void ScriptFormatter::throwRecursionDepthError()
+{
+ out("/* ERROR: Hit recursion limit ScriptFormatter::visiting AST, rewrite failed */");
+}
-void reformatAst(OutWriter &lw, std::shared_ptr<AstComments> comments,
- const std::function<QStringView(SourceLocation)> loc2Str, AST::Node *n)
+void reformatAst(OutWriter &lw, const std::shared_ptr<AstComments> &comments,
+ const std::function<QStringView(SourceLocation)> &loc2Str, AST::Node *n)
{
if (n) {
- Rewriter rewriter(lw, comments, loc2Str, n);
+ ScriptFormatter formatter(lw, comments, loc2Str, n);
}
}
diff --git a/src/qmldom/qqmldomreformatter_p.h b/src/qmldom/qqmldomreformatter_p.h
index 4ff0d39ee0..e498e14b44 100644
--- a/src/qmldom/qqmldomreformatter_p.h
+++ b/src/qmldom/qqmldomreformatter_p.h
@@ -27,9 +27,192 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-QMLDOM_EXPORT void reformatAst(OutWriter &lw, std::shared_ptr<AstComments> comments,
- const std::function<QStringView(SourceLocation)> loc2Str,
- AST::Node *n);
+class ScriptFormatter final : protected AST::JSVisitor
+{
+public:
+ // TODO QTBUG-121988
+ ScriptFormatter(OutWriter &lw, const std::shared_ptr<AstComments> &comments,
+ const std::function<QStringView(SourceLocation)> &loc2Str, AST::Node *node)
+ : lw(lw), comments(comments), loc2Str(loc2Str)
+ {
+ accept(node);
+ }
+
+protected:
+ inline void out(const char *str) { lw.write(QString::fromLatin1(str)); }
+ inline void out(QStringView str) { lw.write(str); }
+ inline void out(const SourceLocation &loc)
+ {
+ if (loc.length != 0)
+ out(loc2Str(loc));
+ }
+ inline void newLine(quint32 count = 1) { lw.ensureNewline(count); }
+
+ inline void accept(AST::Node *node) { AST::Node::accept(node, this); }
+ void lnAcceptIndented(AST::Node *node);
+ bool acceptBlockOrIndented(AST::Node *ast, bool finishWithSpaceOrNewline = false);
+
+ bool preVisit(AST::Node *n) override;
+ void postVisit(AST::Node *n) override;
+
+ bool visit(AST::ThisExpression *ast) override;
+ bool visit(AST::NullExpression *ast) override;
+ bool visit(AST::TrueLiteral *ast) override;
+ bool visit(AST::FalseLiteral *ast) override;
+ bool visit(AST::IdentifierExpression *ast) override;
+ bool visit(AST::StringLiteral *ast) override;
+ bool visit(AST::NumericLiteral *ast) override;
+ bool visit(AST::RegExpLiteral *ast) override;
+
+ bool visit(AST::ArrayPattern *ast) override;
+
+ bool visit(AST::ObjectPattern *ast) override;
+
+ bool visit(AST::PatternElementList *ast) override;
+
+ bool visit(AST::PatternPropertyList *ast) override;
+ bool visit(AST::PatternProperty *property) override;
+
+ bool visit(AST::NestedExpression *ast) override;
+ bool visit(AST::IdentifierPropertyName *ast) override;
+ bool visit(AST::StringLiteralPropertyName *ast) override;
+ bool visit(AST::NumericLiteralPropertyName *ast) override;
+
+ bool visit(AST::TemplateLiteral *ast) override;
+ bool visit(AST::ArrayMemberExpression *ast) override;
+
+ bool visit(AST::FieldMemberExpression *ast) override;
+
+ bool visit(AST::NewMemberExpression *ast) override;
+
+ bool visit(AST::NewExpression *ast) override;
+
+ bool visit(AST::CallExpression *ast) override;
+
+ bool visit(AST::PostIncrementExpression *ast) override;
+
+ bool visit(AST::PostDecrementExpression *ast) override;
+ bool visit(AST::PreIncrementExpression *ast) override;
+
+ bool visit(AST::PreDecrementExpression *ast) override;
+
+ bool visit(AST::DeleteExpression *ast) override;
+
+ bool visit(AST::VoidExpression *ast) override;
+ bool visit(AST::TypeOfExpression *ast) override;
+
+ bool visit(AST::UnaryPlusExpression *ast) override;
+
+ bool visit(AST::UnaryMinusExpression *ast) override;
+
+ bool visit(AST::TildeExpression *ast) override;
+
+ bool visit(AST::NotExpression *ast) override;
+
+ bool visit(AST::BinaryExpression *ast) override;
+
+ bool visit(AST::ConditionalExpression *ast) override;
+
+ bool visit(AST::Block *ast) override;
+
+ bool visit(AST::VariableStatement *ast) override;
+
+ bool visit(AST::PatternElement *ast) override;
+
+ bool visit(AST::EmptyStatement *ast) override;
+
+ bool visit(AST::IfStatement *ast) override;
+ bool visit(AST::DoWhileStatement *ast) override;
+
+ bool visit(AST::WhileStatement *ast) override;
+
+ bool visit(AST::ForStatement *ast) override;
+
+ bool visit(AST::ForEachStatement *ast) override;
+
+ bool visit(AST::ContinueStatement *ast) override;
+ bool visit(AST::BreakStatement *ast) override;
+
+ bool visit(AST::ReturnStatement *ast) override;
+ bool visit(AST::ThrowStatement *ast) override;
+ bool visit(AST::WithStatement *ast) override;
+
+ bool visit(AST::SwitchStatement *ast) override;
+
+ bool visit(AST::CaseBlock *ast) override;
+
+ bool visit(AST::CaseClause *ast) override;
+
+ bool visit(AST::DefaultClause *ast) override;
+
+ bool visit(AST::LabelledStatement *ast) override;
+
+ bool visit(AST::TryStatement *ast) override;
+
+ bool visit(AST::Catch *ast) override;
+
+ bool visit(AST::Finally *ast) override;
+
+ bool visit(AST::FunctionDeclaration *ast) override;
+
+ bool visit(AST::FunctionExpression *ast) override;
+
+ bool visit(AST::Elision *ast) override;
+
+ bool visit(AST::ArgumentList *ast) override;
+
+ bool visit(AST::StatementList *ast) override;
+
+ bool visit(AST::VariableDeclarationList *ast) override;
+
+ bool visit(AST::CaseClauses *ast) override;
+
+ bool visit(AST::FormalParameterList *ast) override;
+
+ bool visit(AST::SuperLiteral *) override;
+ bool visit(AST::ComputedPropertyName *) override;
+ bool visit(AST::Expression *el) override;
+ bool visit(AST::ExpressionStatement *el) override;
+
+ bool visit(AST::ClassDeclaration *ast) override;
+
+ bool visit(AST::ImportDeclaration *ast) override;
+ bool visit(AST::ImportSpecifier *ast) override;
+ bool visit(AST::NameSpaceImport *ast) override;
+ bool visit(AST::ImportsList *ast) override;
+ bool visit(AST::NamedImports *ast) override;
+ bool visit(AST::ImportClause *ast) override;
+
+ bool visit(AST::ExportDeclaration *ast) override;
+ bool visit(AST::ExportClause *ast) override;
+ bool visit(AST::ExportSpecifier *ast) override;
+ bool visit(AST::ExportsList *ast) override;
+
+ bool visit(AST::FromClause *ast) override;
+
+ void endVisit(AST::ComputedPropertyName *) override;
+
+ void endVisit(AST::ExportDeclaration *ast) override;
+ void endVisit(AST::ExportClause *ast) override;
+
+ void endVisit(AST::ImportDeclaration *ast) override;
+ void endVisit(AST::NamedImports *ast) override;
+
+ void throwRecursionDepthError() override;
+
+private:
+ bool addSemicolons() const { return expressionDepth > 0; }
+
+ OutWriter &lw;
+ std::shared_ptr<AstComments> comments;
+ std::function<QStringView(SourceLocation)> loc2Str;
+ QHash<AST::Node *, QList<std::function<void()>>> postOps;
+ int expressionDepth = 0;
+};
+
+QMLDOM_EXPORT void reformatAst(
+ OutWriter &lw, const std::shared_ptr<AstComments> &comments,
+ const std::function<QStringView(SourceLocation)> &loc2Str, AST::Node *n);
} // namespace Dom
} // namespace QQmlJS
diff --git a/src/qmldom/qqmldomscanner.cpp b/src/qmldom/qqmldomscanner.cpp
index f73a1b1a5a..84cd591fb0 100644
--- a/src/qmldom/qqmldomscanner.cpp
+++ b/src/qmldom/qqmldomscanner.cpp
@@ -375,7 +375,7 @@ bool Token::lexKindIsInvalid(int kind)
return false;
}
-void Token::dump(Sink s, QStringView line) const
+void Token::dump(const Sink &s, QStringView line) const
{
s(u"{");
sinkInt(s, offset);
diff --git a/src/qmldom/qqmldomscanner_p.h b/src/qmldom/qqmldomscanner_p.h
index 45141033b4..5ff1c5b767 100644
--- a/src/qmldom/qqmldomscanner_p.h
+++ b/src/qmldom/qqmldomscanner_p.h
@@ -44,10 +44,10 @@ public:
inline Token(int o, int l, int lexKind) : offset(o), length(l), lexKind(lexKind) { }
inline int begin() const { return offset; }
inline int end() const { return offset + length; }
- void dump(Sink s, QStringView line = QStringView()) const;
+ void dump(const Sink &s, QStringView line = QStringView()) const;
QString toString(QStringView line = QStringView()) const
{
- return dumperToString([line, this](Sink s) { this->dump(s, line); });
+ return dumperToString([line, this](const Sink &s) { this->dump(s, line); });
}
static int compare(const Token &t1, const Token &t2)
diff --git a/src/qmldom/qqmldomscriptelements.cpp b/src/qmldom/qqmldomscriptelements.cpp
new file mode 100644
index 0000000000..4bde2abd09
--- /dev/null
+++ b/src/qmldom/qqmldomscriptelements.cpp
@@ -0,0 +1,355 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmldom_utils_p.h"
+#include "qqmldomitem_p.h"
+#include "qqmldompath_p.h"
+#include "qqmldomscriptelements_p.h"
+#include <memory>
+#include <utility>
+#include <variant>
+
+using namespace QQmlJS::Dom::ScriptElements;
+using QQmlJS::Dom::DomType;
+using QQmlJS::Dom::ScriptElement;
+using QQmlJS::Dom::ScriptElementVariant;
+
+/*!
+ \internal
+ \class ScriptElementBase
+
+ The base class for all script elements.
+
+ Derived classes should implement createFileLocations, DomElement::updatePathFromOwner and
+ DomBase::iterateDirectSubpaths. Furthermore, they need their own DomType enum.
+
+ updatePathFromOwner and createFileLocations should be called on the script element root node
+ after it was constructed for the DomItem-wrapping to work correctly. Without it, methods like
+ iterateDirectSubpaths and all the stuff in DomItem will not work.
+
+ createFileLocations does not work without having the pathFromOwner set
+ first via updatePathFromOwner.
+
+ In derived classes, the updatePathFromOwner-implementation should call the base implementation
+ and also call recursively updatePathFromOwner on the derived class's children.
+
+ See \l ScriptElementBase::createFileLocations for the createFileLocations implementation in
+ derived classes.
+
+ Derived classes need to implement iterateDirectSubpaths to comply with the DomItem interface.
+*/
+
+/*!
+ \internal
+ \fn ScriptElementBase::createFileLocations
+
+ Usually, all the visits/recursive calls to DOM elements can be done using the DomItem interface,
+ once all the DOM has been constructed.
+
+ During construction, createFileLocations can be used to annotate the DOM representation with the
+ corresponding source locations, which are needed, e.g., to find the corresponding DOM element
+ from a certain text position. When called, createFileLocations sets an entry for itself in the
+ FileLocationsTree.
+
+ Derived classes should call the base implemenatation and recursively call createFileLocations on
+ all their children.
+
+ Usually, only the root of the script DOM element requires one createFileLocations call after
+ construction \b{and} after a pathFromOwner was set using updatePathFromOwner.
+
+*/
+
+/*!
+ \internal
+ \class ScriptList
+
+ A Helper class for writing script elements that contain lists, helps for implementing the
+ recursive calls of iterateDirectSubpaths, updatePathFromOwner and createFileLocations.
+*/
+
+/*!
+ \internal
+ Helper for fields with elements in iterateDirectSubpaths.
+ */
+static bool wrap(const QQmlJS::Dom::DomItem &self, QQmlJS::Dom::DirectVisitor visitor, QStringView field,
+ const ScriptElementVariant &value)
+{
+ if (!value)
+ return true;
+
+ const bool b =
+ self.dvItemField(visitor, field, [&self, field, &value]() -> QQmlJS::Dom::DomItem {
+ const QQmlJS::Dom::Path pathFromOwner{ self.pathFromOwner().field(field) };
+ return self.subScriptElementWrapperItem(value);
+ });
+ return b;
+}
+
+/*!
+ \internal
+ Helper for fields with lists in iterateDirectSubpaths.
+ */
+static bool wrap(const QQmlJS::Dom::DomItem &self, QQmlJS::Dom::DirectVisitor visitor, QStringView field,
+ const ScriptList &value)
+{
+ const bool b =
+ self.dvItemField(visitor, field, [&self, field, &value]() -> QQmlJS::Dom::DomItem {
+ const QQmlJS::Dom::Path pathFromOwner{ self.pathFromOwner().field(field) };
+ return self.subListItem(value.asList(pathFromOwner));
+ });
+ return b;
+}
+
+bool GenericScriptElement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ for (auto it = m_children.begin(); it != m_children.end(); ++it) {
+ cont &= std::visit(
+ [&self, &visitor, &it](auto &&e) { return wrap(self, visitor, it->first, e); },
+ it->second);
+ }
+ return cont;
+}
+
+void GenericScriptElement::updatePathFromOwner(const Path &p)
+{
+ BaseT::updatePathFromOwner(p);
+ for (auto it = m_children.begin(); it != m_children.end(); ++it) {
+ std::visit(qOverloadedVisitor{ [&p, &it](ScriptElementVariant &e) {
+ e.base()->updatePathFromOwner(p.field(it->first));
+ },
+ [&p, &it](ScriptList &list) {
+ list.updatePathFromOwner(p.field(it->first));
+ } },
+ it->second);
+ }
+}
+
+void GenericScriptElement::createFileLocations(const FileLocations::Tree &base)
+{
+ BaseT::createFileLocations(base);
+ for (auto it = m_children.begin(); it != m_children.end(); ++it) {
+ std::visit(
+ qOverloadedVisitor{
+ [&base](ScriptElementVariant &e) { e.base()->createFileLocations(base); },
+ [&base](ScriptList &list) { list.createFileLocations(base); } },
+ it->second);
+ }
+}
+
+bool BlockStatement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ // TODO: test me
+ bool cont = true;
+ cont &= wrap(self, visitor, Fields::statements, m_statements);
+ return cont;
+}
+
+void BlockStatement::updatePathFromOwner(const Path &p)
+{
+ BaseT::updatePathFromOwner(p);
+ m_statements.updatePathFromOwner(p.field(Fields::statements));
+}
+
+void BlockStatement::createFileLocations(const FileLocations::Tree &base)
+{
+ BaseT::createFileLocations(base);
+ m_statements.createFileLocations(base);
+}
+
+bool IdentifierExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ cont &= self.dvValueField(visitor, Fields::identifier, m_name);
+ return cont;
+}
+
+bool Literal::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ std::visit([&cont, &visitor,
+ &self](auto &&e) { cont &= self.dvValueField(visitor, Fields::value, e); },
+ m_value);
+ return cont;
+}
+
+bool IfStatement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ // TODO: test me
+ bool cont = true;
+ cont &= wrap(self, visitor, Fields::condition, m_condition);
+ cont &= wrap(self, visitor, Fields::consequence, m_consequence);
+ cont &= wrap(self, visitor, Fields::alternative, m_alternative);
+ return cont;
+}
+
+void IfStatement::updatePathFromOwner(const Path &p)
+{
+ BaseT::updatePathFromOwner(p);
+ if (auto ptr = m_condition.base())
+ ptr->updatePathFromOwner(p.field(Fields::condition));
+ if (auto ptr = m_consequence.base())
+ ptr->updatePathFromOwner(p.field(Fields::consequence));
+ if (auto ptr = m_alternative.base())
+ ptr->updatePathFromOwner(p.field(Fields::alternative));
+}
+
+void IfStatement::createFileLocations(const FileLocations::Tree &base)
+{
+ BaseT::createFileLocations(base);
+ if (auto ptr = m_condition.base())
+ ptr->createFileLocations(base);
+ if (auto ptr = m_consequence.base())
+ ptr->createFileLocations(base);
+ if (auto ptr = m_alternative.base())
+ ptr->createFileLocations(base);
+}
+
+bool ForStatement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ cont &= wrap(self, visitor, Fields::initializer, m_initializer);
+ cont &= wrap(self, visitor, Fields::declarations, m_declarations);
+ cont &= wrap(self, visitor, Fields::condition, m_condition);
+ cont &= wrap(self, visitor, Fields::expression, m_expression);
+ cont &= wrap(self, visitor, Fields::body, m_body);
+ return cont;
+}
+
+void ForStatement::updatePathFromOwner(const Path &p)
+{
+ BaseT::updatePathFromOwner(p);
+ if (auto ptr = m_initializer.base())
+ ptr->updatePathFromOwner(p.field(Fields::initializer));
+ if (auto ptr = m_declarations.base())
+ ptr->updatePathFromOwner(p.field(Fields::declarations));
+ if (auto ptr = m_condition.base())
+ ptr->updatePathFromOwner(p.field(Fields::condition));
+ if (auto ptr = m_expression.base())
+ ptr->updatePathFromOwner(p.field(Fields::expression));
+ if (auto ptr = m_body.base())
+ ptr->updatePathFromOwner(p.field(Fields::body));
+}
+
+void ForStatement::createFileLocations(const FileLocations::Tree &base)
+{
+ BaseT::createFileLocations(base);
+ if (auto ptr = m_initializer.base())
+ ptr->createFileLocations(base);
+ if (auto ptr = m_declarations.base())
+ ptr->createFileLocations(base);
+ if (auto ptr = m_condition.base())
+ ptr->createFileLocations(base);
+ if (auto ptr = m_expression.base())
+ ptr->createFileLocations(base);
+ if (auto ptr = m_body.base())
+ ptr->createFileLocations(base);
+}
+
+bool BinaryExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ cont &= wrap(self, visitor, Fields::left, m_left);
+ cont &= self.dvValueField(visitor, Fields::operation, m_operator);
+ cont &= wrap(self, visitor, Fields::right, m_right);
+ return cont;
+}
+
+void BinaryExpression::updatePathFromOwner(const Path &p)
+{
+ BaseT::updatePathFromOwner(p);
+ if (auto ptr = m_left.base())
+ ptr->updatePathFromOwner(p.field(Fields::left));
+ if (auto ptr = m_right.base())
+ ptr->updatePathFromOwner(p.field(Fields::right));
+}
+
+void BinaryExpression::createFileLocations(const FileLocations::Tree &base)
+{
+ BaseT::createFileLocations(base);
+ if (auto ptr = m_left.base())
+ ptr->createFileLocations(base);
+ if (auto ptr = m_right.base())
+ ptr->createFileLocations(base);
+}
+
+bool VariableDeclarationEntry::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ cont &= self.dvValueField(visitor, Fields::scopeType, m_scopeType);
+ cont &= wrap(self, visitor, Fields::identifier, m_identifier);
+ cont &= wrap(self, visitor, Fields::initializer, m_initializer);
+ return cont;
+}
+
+void VariableDeclarationEntry::updatePathFromOwner(const Path &p)
+{
+ BaseT::updatePathFromOwner(p);
+ if (auto ptr = m_identifier.base())
+ ptr->updatePathFromOwner(p.field(Fields::identifier));
+ if (auto ptr = m_initializer.base())
+ ptr->updatePathFromOwner(p.field(Fields::initializer));
+}
+
+void VariableDeclarationEntry::createFileLocations(const FileLocations::Tree &base)
+{
+ BaseT::createFileLocations(base);
+ if (auto ptr = m_identifier.base())
+ ptr->createFileLocations(base);
+ if (auto ptr = m_initializer.base())
+ ptr->createFileLocations(base);
+}
+
+bool VariableDeclaration::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ cont &= wrap(self, visitor, Fields::declarations, m_declarations);
+ return cont;
+}
+
+void VariableDeclaration::updatePathFromOwner(const Path &p)
+{
+ BaseT::updatePathFromOwner(p);
+ m_declarations.updatePathFromOwner(p.field(Fields::declarations));
+}
+
+void VariableDeclaration::createFileLocations(const FileLocations::Tree &base)
+{
+ BaseT::createFileLocations(base);
+ m_declarations.createFileLocations(base);
+}
+
+bool ReturnStatement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ cont &= wrap(self, visitor, Fields::expression, m_expression);
+ return cont;
+}
+
+void ReturnStatement::updatePathFromOwner(const Path &p)
+{
+ BaseT::updatePathFromOwner(p);
+ if (auto ptr = m_expression.base())
+ ptr->updatePathFromOwner(p.field(Fields::expression));
+}
+
+void ReturnStatement::createFileLocations(const FileLocations::Tree &base)
+{
+ BaseT::createFileLocations(base);
+ if (auto ptr = m_expression.base())
+ ptr->createFileLocations(base);
+}
+
+void ScriptList::replaceKindForGenericChildren(DomType oldType, DomType newType)
+{
+ for (auto &it : m_list) {
+ if (auto current = it.data()) {
+ if (auto genericElement =
+ std::get_if<std::shared_ptr<ScriptElements::GenericScriptElement>>(
+ &*current)) {
+ if ((*genericElement)->kind() == oldType)
+ (*genericElement)->setKind(newType);
+ }
+ }
+ }
+}
diff --git a/src/qmldom/qqmldomscriptelements_p.h b/src/qmldom/qqmldomscriptelements_p.h
new file mode 100644
index 0000000000..b319e7e88f
--- /dev/null
+++ b/src/qmldom/qqmldomscriptelements_p.h
@@ -0,0 +1,405 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLDOMSCRIPTELEMENTS_P_H
+#define QQMLDOMSCRIPTELEMENTS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldomitem_p.h"
+#include "qqmldomattachedinfo_p.h"
+#include "qqmldompath_p.h"
+#include <algorithm>
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+namespace ScriptElements {
+
+template<DomType type>
+class ScriptElementBase : public ScriptElement
+{
+public:
+ using BaseT = ScriptElementBase<type>;
+ static constexpr DomType kindValue = type;
+ static constexpr DomKind domKindValue = DomKind::ScriptElement;
+
+ ScriptElementBase(QQmlJS::SourceLocation combinedLocation = QQmlJS::SourceLocation{})
+ : ScriptElement(), m_locations({ { FileLocationRegion::MainRegion, combinedLocation } })
+ {
+ }
+ ScriptElementBase(QQmlJS::SourceLocation first, QQmlJS::SourceLocation last)
+ : ScriptElementBase(combine(first, last))
+ {
+ }
+ DomType kind() const override { return type; }
+ DomKind domKind() const override { return domKindValue; }
+
+ void createFileLocations(const FileLocations::Tree &base) override
+ {
+ FileLocations::Tree res =
+ FileLocations::ensure(base, pathFromOwner(), AttachedInfo::PathType::Relative);
+ for (auto location: m_locations) {
+ FileLocations::addRegion(res, location.first, location.second);
+ }
+ }
+
+ /*
+ Pretty prints the current DomItem. Currently, for script elements, this is done entirely on
+ the parser representation (via the AST classes), but it could be moved here if needed.
+ */
+ // void writeOut(const DomItem &self, OutWriter &lw) const override;
+
+ /*!
+ All of the following overloads are only required for optimization purposes.
+ The base implementation will work fine, but might be slightly slower.
+ You can override dump(), fields(), field(), indexes(), index(), keys() or key() if the
+ performance of the base class becomes problematic.
+ */
+
+ // // needed for debug
+ // void dump(const DomItem &, const Sink &sink, int indent, FilterT filter) const override;
+
+ // // just required for optimization if iterateDirectSubpaths is slow
+ // QList<QString> fields(const DomItem &self) const override;
+ // DomItem field(const DomItem &self, QStringView name) const override;
+
+ // index_type indexes(const DomItem &self) const override;
+ // DomItem index(const DomItem &self, index_type index) const override;
+
+ // QSet<QString> const keys(const DomItem &self) const override;
+ // DomItem key(const DomItem &self, const QString &name) const override;
+
+ QQmlJS::SourceLocation mainRegionLocation() const
+ {
+ Q_ASSERT(m_locations.size() > 0);
+ Q_ASSERT(m_locations.front().first == FileLocationRegion::MainRegion);
+
+ auto current = m_locations.front();
+ return current.second;
+ }
+ void setMainRegionLocation(const QQmlJS::SourceLocation &location)
+ {
+ Q_ASSERT(m_locations.size() > 0);
+ Q_ASSERT(m_locations.front().first == FileLocationRegion::MainRegion);
+
+ m_locations.front().second = location;
+ }
+ void addLocation(FileLocationRegion region, QQmlJS::SourceLocation location)
+ {
+ Q_ASSERT_X(region != FileLocationRegion::MainRegion, "ScriptElementBase::addLocation",
+ "use the setCombinedLocation instead!");
+ m_locations.emplace_back(region, location);
+ }
+
+protected:
+ std::vector<std::pair<FileLocationRegion, QQmlJS::SourceLocation>> m_locations;
+};
+
+class ScriptList : public ScriptElementBase<DomType::List>
+{
+public:
+ using typename ScriptElementBase<DomType::List>::BaseT;
+
+ using BaseT::BaseT;
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
+ {
+ bool cont =
+ asList(self.pathFromOwner().key(QString())).iterateDirectSubpaths(self, visitor);
+ return cont;
+ }
+ void updatePathFromOwner(const Path &p) override
+ {
+ BaseT::updatePathFromOwner(p);
+ for (int i = 0; i < m_list.size(); ++i) {
+ Q_ASSERT(m_list[i].base());
+ m_list[i].base()->updatePathFromOwner(p.index(i));
+ }
+ }
+ void createFileLocations(const FileLocations::Tree &base) override
+ {
+ BaseT::createFileLocations(base);
+
+ for (int i = 0; i < m_list.size(); ++i) {
+ Q_ASSERT(m_list[i].base());
+ m_list[i].base()->createFileLocations(base);
+ }
+ }
+
+ List asList(const Path &path) const
+ {
+ auto asList = List::fromQList<ScriptElementVariant>(
+ path, m_list,
+ [](const DomItem &list, const PathEls::PathComponent &, const ScriptElementVariant &wrapped)
+ -> DomItem { return list.subScriptElementWrapperItem(wrapped); });
+
+ return asList;
+ }
+
+ void append(const ScriptElementVariant &statement) { m_list.push_back(statement); }
+ void append(const ScriptList &list) { m_list.append(list.m_list); }
+ void reverse() { std::reverse(m_list.begin(), m_list.end()); }
+ void replaceKindForGenericChildren(DomType oldType, DomType newType);
+ const QList<ScriptElementVariant> &qList() { return std::as_const(m_list); };
+
+private:
+ QList<ScriptElementVariant> m_list;
+};
+
+class GenericScriptElement : public ScriptElementBase<DomType::ScriptGenericElement>
+{
+public:
+ using BaseT::BaseT;
+ using VariantT = std::variant<ScriptElementVariant, ScriptList>;
+
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ void updatePathFromOwner(const Path &p) override;
+ void createFileLocations(const FileLocations::Tree &base) override;
+
+ DomType kind() const override { return m_kind; }
+ void setKind(DomType kind) { m_kind = kind; }
+
+ decltype(auto) insertChild(QStringView name, VariantT v)
+ {
+ return m_children.insert(std::make_pair(name, v));
+ }
+
+private:
+ /*!
+ \internal
+ The DomItem interface will use iterateDirectSubpaths for all kinds of operations on the
+ GenericScriptElement. Therefore, to avoid bad surprises when using the DomItem interface, use
+ a sorted map to always iterate the children in the same order.
+ */
+ std::map<QQmlJS::Dom::FieldType, VariantT> m_children;
+ DomType m_kind = DomType::Empty;
+};
+
+class BlockStatement : public ScriptElementBase<DomType::ScriptBlockStatement>
+{
+public:
+ using BaseT::BaseT;
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ void updatePathFromOwner(const Path &p) override;
+ void createFileLocations(const FileLocations::Tree &base) override;
+
+ ScriptList statements() const { return m_statements; }
+ void setStatements(const ScriptList &statements) { m_statements = statements; }
+
+private:
+ ScriptList m_statements;
+};
+
+class IdentifierExpression : public ScriptElementBase<DomType::ScriptIdentifierExpression>
+{
+public:
+ using BaseT::BaseT;
+ void setName(QStringView name) { m_name = name.toString(); }
+ QString name() { return m_name; }
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+
+ QCborValue value() const override { return QCborValue(m_name); }
+
+private:
+ QString m_name;
+};
+
+class Literal : public ScriptElementBase<DomType::ScriptLiteral>
+{
+public:
+ using BaseT::BaseT;
+
+ using VariantT = std::variant<QString, double, bool, std::nullptr_t>;
+
+ void setLiteralValue(VariantT value) { m_value = value; }
+ VariantT literalValue() const { return m_value; }
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+
+ QCborValue value() const override
+ {
+ return std::visit([](auto &&e) -> QCborValue { return e; }, m_value);
+ }
+
+private:
+ VariantT m_value;
+};
+
+// TODO: test this method + implement foreach etc
+class ForStatement : public ScriptElementBase<DomType::ScriptForStatement>
+{
+public:
+ using BaseT::BaseT;
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ void updatePathFromOwner(const Path &p) override;
+ void createFileLocations(const FileLocations::Tree &base) override;
+
+ ScriptElementVariant initializer() const { return m_initializer; }
+ void setInitializer(const ScriptElementVariant &newInitializer)
+ {
+ m_initializer = newInitializer;
+ }
+
+ ScriptElementVariant declarations() const { return m_declarations; }
+ void setDeclarations(const ScriptElementVariant &newDeclaration)
+ {
+ m_declarations = newDeclaration;
+ }
+ ScriptElementVariant condition() const { return m_condition; }
+ void setCondition(const ScriptElementVariant &newCondition) { m_condition = newCondition; }
+ ScriptElementVariant expression() const { return m_expression; }
+ void setExpression(const ScriptElementVariant &newExpression) { m_expression = newExpression; }
+ ScriptElementVariant body() const { return m_body; }
+ void setBody(const ScriptElementVariant &newBody) { m_body = newBody; }
+
+private:
+ ScriptElementVariant m_initializer;
+ ScriptElementVariant m_declarations;
+ ScriptElementVariant m_condition;
+ ScriptElementVariant m_expression;
+ ScriptElementVariant m_body;
+};
+
+class IfStatement : public ScriptElementBase<DomType::ScriptIfStatement>
+{
+public:
+ using BaseT::BaseT;
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ void updatePathFromOwner(const Path &p) override;
+ void createFileLocations(const FileLocations::Tree &base) override;
+
+ ScriptElementVariant condition() const { return m_condition; }
+ void setCondition(const ScriptElementVariant &condition) { m_condition = condition; }
+ ScriptElementVariant consequence() { return m_consequence; }
+ void setConsequence(const ScriptElementVariant &consequence) { m_consequence = consequence; }
+ ScriptElementVariant alternative() { return m_alternative; }
+ void setAlternative(const ScriptElementVariant &alternative) { m_alternative = alternative; }
+
+private:
+ ScriptElementVariant m_condition;
+ ScriptElementVariant m_consequence;
+ ScriptElementVariant m_alternative;
+};
+
+class ReturnStatement : public ScriptElementBase<DomType::ScriptReturnStatement>
+{
+public:
+ using BaseT::BaseT;
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ void updatePathFromOwner(const Path &p) override;
+ void createFileLocations(const FileLocations::Tree &base) override;
+
+ ScriptElementVariant expression() const { return m_expression; }
+ void setExpression(ScriptElementVariant expression) { m_expression = expression; }
+
+private:
+ ScriptElementVariant m_expression;
+};
+
+class BinaryExpression : public ScriptElementBase<DomType::ScriptBinaryExpression>
+{
+public:
+ using BaseT::BaseT;
+
+ enum Operator : char {
+ FieldMemberAccess,
+ ArrayMemberAccess,
+ TO_BE_IMPLEMENTED = std::numeric_limits<char>::max(), // not required by qmlls
+ };
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ void updatePathFromOwner(const Path &p) override;
+ void createFileLocations(const FileLocations::Tree &base) override;
+
+ ScriptElementVariant left() const { return m_left; }
+ void setLeft(const ScriptElementVariant &newLeft) { m_left = newLeft; }
+ ScriptElementVariant right() const { return m_right; }
+ void setRight(const ScriptElementVariant &newRight) { m_right = newRight; }
+ int op() const { return m_operator; }
+ void setOp(Operator op) { m_operator = op; }
+
+private:
+ ScriptElementVariant m_left;
+ ScriptElementVariant m_right;
+ Operator m_operator = TO_BE_IMPLEMENTED;
+};
+
+class VariableDeclarationEntry : public ScriptElementBase<DomType::ScriptVariableDeclarationEntry>
+{
+public:
+ using BaseT::BaseT;
+
+ enum ScopeType { Var, Let, Const };
+
+ ScopeType scopeType() const { return m_scopeType; }
+ void setScopeType(ScopeType scopeType) { m_scopeType = scopeType; }
+
+ ScriptElementVariant identifier() const { return m_identifier; }
+ void setIdentifier(const ScriptElementVariant &identifier) { m_identifier = identifier; }
+
+ ScriptElementVariant initializer() const { return m_initializer; }
+ void setInitializer(const ScriptElementVariant &initializer) { m_initializer = initializer; }
+
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ void updatePathFromOwner(const Path &p) override;
+ void createFileLocations(const FileLocations::Tree &base) override;
+
+private:
+ ScopeType m_scopeType;
+ ScriptElementVariant m_identifier;
+ ScriptElementVariant m_initializer;
+};
+
+class VariableDeclaration : public ScriptElementBase<DomType::ScriptVariableDeclaration>
+{
+public:
+ using BaseT::BaseT;
+
+ // minimal required overload for this to be wrapped as DomItem:
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override;
+ void updatePathFromOwner(const Path &p) override;
+ void createFileLocations(const FileLocations::Tree &base) override;
+
+ void setDeclarations(const ScriptList &list) { m_declarations = list; }
+ ScriptList declarations() { return m_declarations; }
+
+private:
+ ScriptList m_declarations;
+};
+
+} // namespace ScriptElements
+} // end namespace Dom
+} // end namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QQMLDOMSCRIPTELEMENTS_P_H
diff --git a/src/qmldom/qqmldomstringdumper.cpp b/src/qmldom/qqmldomstringdumper.cpp
index 081a6abf81..4859614f77 100644
--- a/src/qmldom/qqmldomstringdumper.cpp
+++ b/src/qmldom/qqmldomstringdumper.cpp
@@ -43,7 +43,7 @@ namespace Dom {
* \brief Converts a dumper to a string
* \param writer The dumper convert to a string
*/
-QString dumperToString(Dumper writer)
+QString dumperToString(const Dumper &writer)
{
QString s;
QTextStream d(&s);
@@ -59,7 +59,7 @@ QString dumperToString(Dumper writer)
* \param s The string to sink
* \param options If quotes should be outputted around the string (defaults to yes)
*/
-void sinkEscaped(Sink sink, QStringView s, EscapeOptions options) {
+void sinkEscaped(const Sink &sink, QStringView s, EscapeOptions options) {
if (options == EscapeOptions::OuterQuotes)
sink(u"\"");
int it0=0;
@@ -95,7 +95,7 @@ void sinkEscaped(Sink sink, QStringView s, EscapeOptions options) {
* \param s the sink to write to
* \param level the level to describe
*/
-void dumpErrorLevel(Sink s, ErrorLevel level)
+void dumpErrorLevel(const Sink &s, ErrorLevel level)
{
switch (level) {
case ErrorLevel::Debug:
@@ -117,7 +117,7 @@ void dumpErrorLevel(Sink s, ErrorLevel level)
}
-void dumperToQDebug(Dumper dumper, QDebug debug)
+void dumperToQDebug(const Dumper &dumper, QDebug debug)
{
QDebug & d = debug.noquote().nospace();
dumper([&d](QStringView s){
@@ -131,7 +131,7 @@ void dumperToQDebug(Dumper dumper, QDebug debug)
* \param level the error level of the message
* \param dumper the dumper that writes a message
*/
-void dumperToQDebug(Dumper dumper, ErrorLevel level)
+void dumperToQDebug(const Dumper &dumper, ErrorLevel level)
{
QDebug d = qDebug().noquote().nospace();
switch (level) {
@@ -157,7 +157,7 @@ void dumperToQDebug(Dumper dumper, ErrorLevel level)
* \internal
* \brief sinks the requested amount of spaces
*/
-void sinkIndent(Sink s, int indent)
+void sinkIndent(const Sink &s, int indent)
{
if (indent > 0) {
QStringView spaces = u" ";
@@ -173,7 +173,7 @@ void sinkIndent(Sink s, int indent)
* \internal
* \brief sinks a neline and indents by the given amount
*/
-void sinkNewline(Sink s, int indent)
+void sinkNewline(const Sink &s, int indent)
{
s(u"\n");
if (indent > 0)
@@ -186,7 +186,7 @@ void sinkNewline(Sink s, int indent)
* \brief A sink that ignores whatever it receives
*/
-QDebug operator<<(QDebug d, Dumper dumper)
+QDebug operator<<(QDebug d, const Dumper &dumper)
{
QDebug dd = d.noquote().nospace();
dumper([&dd](QStringView s) { dd << s; });
diff --git a/src/qmldom/qqmldomstringdumper_p.h b/src/qmldom/qqmldomstringdumper_p.h
index 5f1a633809..cf5c8e6483 100644
--- a/src/qmldom/qqmldomstringdumper_p.h
+++ b/src/qmldom/qqmldomstringdumper_p.h
@@ -32,7 +32,7 @@ namespace Dom {
using Sink = function_ref<void(QStringView)>;
using SinkF = std::function<void(QStringView)>;
-using DumperFunction = std::function<void(Sink)>;
+using DumperFunction = std::function<void(const Sink &)>;
class Dumper{
public:
@@ -43,7 +43,7 @@ private:
// would be the second user defined conversion.
// For a similar reason we have a template to accept function_ref<void(Sink)> .
// The end result is that void f(Dumper) can be called nicely, and avoid overloads:
- // f(u"bla"), f(QLatin1String("bla")), f(QString()), f([](Sink s){...}),...
+ // f(u"bla"), f(QLatin1String("bla")), f(QString()), f([](const Sink &s){...}),...
template <typename T>
using if_compatible_dumper = typename
std::enable_if<std::is_convertible<T, DumperFunction>::value, bool>::type;
@@ -54,7 +54,7 @@ private:
public:
Dumper(QStringView s):
- dumper([s](Sink sink){ sink(s); }) {}
+ dumper([s](const Sink &sink){ sink(s); }) {}
Dumper(std::nullptr_t): Dumper(QStringView(nullptr)) {}
@@ -63,13 +63,13 @@ public:
Dumper(QStringView(string)) {}
template <typename U, if_compatible_dumper<U> = true>
- Dumper(U f): dumper(f) {}
+ Dumper(U f): dumper(std::move(f)) {}
- void operator()(Sink s) { dumper(s); }
+ void operator()(const Sink &s) const { dumper(s); }
};
template <typename T>
-void sinkInt(Sink s, T i) {
+void sinkInt(const Sink &s, T i) {
const int BUFSIZE = 42; // safe up to 128 bits
QChar buf[BUFSIZE];
int ibuf = BUFSIZE;
@@ -96,24 +96,24 @@ void sinkInt(Sink s, T i) {
s(QStringView(&buf[ibuf], BUFSIZE - ibuf -1));
}
-QMLDOM_EXPORT QString dumperToString(Dumper writer);
+QMLDOM_EXPORT QString dumperToString(const Dumper &writer);
-QMLDOM_EXPORT void sinkEscaped(Sink sink, QStringView s,
+QMLDOM_EXPORT void sinkEscaped(const Sink &sink, QStringView s,
EscapeOptions options = EscapeOptions::OuterQuotes);
inline void devNull(QStringView) {}
-QMLDOM_EXPORT void sinkIndent(Sink s, int indent);
+QMLDOM_EXPORT void sinkIndent(const Sink &s, int indent);
-QMLDOM_EXPORT void sinkNewline(Sink s, int indent = 0);
+QMLDOM_EXPORT void sinkNewline(const Sink &s, int indent = 0);
-QMLDOM_EXPORT void dumpErrorLevel(Sink s, ErrorLevel level);
+QMLDOM_EXPORT void dumpErrorLevel(const Sink &s, ErrorLevel level);
-QMLDOM_EXPORT void dumperToQDebug(Dumper dumper, QDebug debug);
+QMLDOM_EXPORT void dumperToQDebug(const Dumper &dumper, QDebug debug);
-QMLDOM_EXPORT void dumperToQDebug(Dumper dumper, ErrorLevel level = ErrorLevel::Debug);
+QMLDOM_EXPORT void dumperToQDebug(const Dumper &dumper, ErrorLevel level = ErrorLevel::Debug);
-QMLDOM_EXPORT QDebug operator<<(QDebug d, Dumper dumper);
+QMLDOM_EXPORT QDebug operator<<(QDebug d, const Dumper &dumper);
} // end namespace Dom
} // end namespace QQmlJS
diff --git a/src/qmldom/qqmldomtop.cpp b/src/qmldom/qqmldomtop.cpp
index 4e82a01933..8ae836b0a2 100644
--- a/src/qmldom/qqmldomtop.cpp
+++ b/src/qmldom/qqmldomtop.cpp
@@ -1,6 +1,7 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qqmldomitem_p.h"
#include "qqmldomtop_p.h"
#include "qqmldomexternalitems_p.h"
#include "qqmldommock_p.h"
@@ -8,6 +9,7 @@
#include "qqmldomastcreator_p.h"
#include "qqmldommoduleindex_p.h"
#include "qqmldomtypesreader_p.h"
+#include "qqmldom_utils_p.h"
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
@@ -53,21 +55,21 @@ using std::shared_ptr;
if force is true the file is always read
*/
-Path DomTop::canonicalPath(DomItem &) const
+Path DomTop::canonicalPath(const DomItem &) const
{
return canonicalPath();
}
-DomItem DomTop::containingObject(DomItem &) const
+DomItem DomTop::containingObject(const DomItem &) const
{
return DomItem();
}
-bool DomTop::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool DomTop::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
static QHash<QString, QString> knownFields;
static QBasicMutex m;
- auto toField = [](QString f) mutable -> QStringView {
+ auto toField = [](const QString &f) mutable -> QStringView {
QMutexLocker l(&m);
if (!knownFields.contains(f))
knownFields[f] = f;
@@ -119,11 +121,10 @@ ErrorGroups DomUniverse::myErrors()
return groups;
}
-DomUniverse::DomUniverse(QString universeName, Options options):
- m_name(universeName), m_options(options)
-{}
+DomUniverse::DomUniverse(const QString &universeName) : m_name(universeName) { }
-std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(std::shared_ptr<DomUniverse> univ)
+std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(
+ const std::shared_ptr<DomUniverse> &univ)
{
const auto next = [] {
Q_CONSTINIT static std::atomic<int> counter(0);
@@ -136,9 +137,9 @@ std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(std::shared_ptr<DomU
QLatin1String("universe") + QString::number(next()));
}
-DomItem DomUniverse::create(QString universeName, Options options)
+DomItem DomUniverse::create(const QString &universeName)
{
- auto res = std::make_shared<DomUniverse>(universeName, options);
+ auto res = std::make_shared<DomUniverse>(universeName);
return DomItem(res);
}
@@ -147,66 +148,51 @@ Path DomUniverse::canonicalPath() const
return Path::Root(u"universe");
}
-bool DomUniverse::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool DomUniverse::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::name, name());
- cont = cont && self.dvValueField(visitor, Fields::options, int(options()));
cont = cont && self.dvItemField(visitor, Fields::globalScopeWithName, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::globalScopeWithName),
- [this](DomItem &map, QString key) { return map.copy(globalScopeWithName(key)); },
- [this](DomItem &) { return globalScopeNames(); }, QLatin1String("GlobalScope")));
+ [this](const DomItem &map, const QString &key) { return map.copy(globalScopeWithName(key)); },
+ [this](const DomItem &) { return globalScopeNames(); }, QLatin1String("GlobalScope")));
});
cont = cont && self.dvItemField(visitor, Fields::qmlDirectoryWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmlDirectoryWithPath),
- [this](DomItem &map, QString key) { return map.copy(qmlDirectoryWithPath(key)); },
- [this](DomItem &) { return qmlDirectoryPaths(); }, QLatin1String("QmlDirectory")));
+ [this](const DomItem &map, const QString &key) { return map.copy(qmlDirectoryWithPath(key)); },
+ [this](const DomItem &) { return qmlDirectoryPaths(); }, QLatin1String("QmlDirectory")));
});
cont = cont && self.dvItemField(visitor, Fields::qmldirFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmldirFileWithPath),
- [this](DomItem &map, QString key) { return map.copy(qmldirFileWithPath(key)); },
- [this](DomItem &) { return qmldirFilePaths(); }, QLatin1String("QmldirFile")));
+ [this](const DomItem &map, const QString &key) { return map.copy(qmldirFileWithPath(key)); },
+ [this](const DomItem &) { return qmldirFilePaths(); }, QLatin1String("QmldirFile")));
});
cont = cont && self.dvItemField(visitor, Fields::qmlFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmlFileWithPath),
- [this](DomItem &map, QString key) { return map.copy(qmlFileWithPath(key)); },
- [this](DomItem &) { return qmlFilePaths(); }, QLatin1String("QmlFile")));
+ [this](const DomItem &map, const QString &key) { return map.copy(qmlFileWithPath(key)); },
+ [this](const DomItem &) { return qmlFilePaths(); }, QLatin1String("QmlFile")));
});
cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::jsFileWithPath),
- [this](DomItem &map, QString key) { return map.copy(jsFileWithPath(key)); },
- [this](DomItem &) { return jsFilePaths(); }, QLatin1String("JsFile")));
+ [this](const DomItem &map, const QString &key) { return map.copy(jsFileWithPath(key)); },
+ [this](const DomItem &) { return jsFilePaths(); }, QLatin1String("JsFile")));
});
cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmltypesFileWithPath),
- [this](DomItem &map, QString key) { return map.copy(qmltypesFileWithPath(key)); },
- [this](DomItem &) { return qmltypesFilePaths(); }, QLatin1String("QmltypesFile")));
- });
- cont = cont && self.dvItemField(visitor, Fields::queue, [this, &self]() {
- QQueue<ParsingTask> q = queue();
- return self.subListItem(List(
- Path::Field(Fields::queue),
- [q](DomItem &list, index_type i) {
- if (i >= 0 && i < q.size())
- return list.subDataItem(PathEls::Index(i), q.at(i).toCbor(),
- ConstantData::Options::FirstMapIsFields);
- else
- return DomItem();
- },
- [q](DomItem &) { return index_type(q.size()); }, nullptr,
- QLatin1String("ParsingTask")));
+ [this](const DomItem &map, const QString &key) { return map.copy(qmltypesFileWithPath(key)); },
+ [this](const DomItem &) { return qmltypesFilePaths(); }, QLatin1String("QmltypesFile")));
});
return cont;
}
-std::shared_ptr<OwningItem> DomUniverse::doCopy(DomItem &) const
+std::shared_ptr<OwningItem> DomUniverse::doCopy(const DomItem &) const
{
QRegularExpression r(QRegularExpression::anchoredPattern(QLatin1String(R"(.*Copy([0-9]*)$)")));
auto m = r.match(m_name);
@@ -219,14 +205,7 @@ std::shared_ptr<OwningItem> DomUniverse::doCopy(DomItem &) const
return res;
}
-void DomUniverse::loadFile(DomItem &self, QString filePath, QString logicalPath, Callback callback,
- LoadOptions loadOptions, std::optional<DomType> fileType)
-{
- loadFile(self, filePath, logicalPath, QString(), QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- callback, loadOptions, fileType);
-}
-
-static DomType fileTypeForPath(DomItem &self, QString canonicalFilePath)
+static DomType fileTypeForPath(const DomItem &self, const QString &canonicalFilePath)
{
if (canonicalFilePath.endsWith(u".qml", Qt::CaseInsensitive)
|| canonicalFilePath.endsWith(u".qmlannotation", Qt::CaseInsensitive)) {
@@ -236,12 +215,16 @@ static DomType fileTypeForPath(DomItem &self, QString canonicalFilePath)
} else if (QStringView(u"qmldir").compare(QFileInfo(canonicalFilePath).fileName(),
Qt::CaseInsensitive)
== 0) {
- return DomType::QmltypesFile;
+ return DomType::QmldirFile;
} else if (QFileInfo(canonicalFilePath).isDir()) {
return DomType::QmlDirectory;
- } else {
+ } else if (canonicalFilePath.endsWith(u".js", Qt::CaseInsensitive)
+ || canonicalFilePath.endsWith(u".mjs", Qt::CaseInsensitive)) {
+ return DomType::JsFile;
+ }
+ else {
self.addError(DomUniverse::myErrors()
- .error(QCoreApplication::translate("Dom::filteTypeForPath",
+ .error(QCoreApplication::translate("Dom::fileTypeForPath",
"Could not detect type of file %1")
.arg(canonicalFilePath))
.handle());
@@ -249,245 +232,120 @@ static DomType fileTypeForPath(DomItem &self, QString canonicalFilePath)
return DomType::Empty;
}
-void DomUniverse::loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath,
- QString code, QDateTime codeDate, Callback callback,
- LoadOptions loadOptions, std::optional<DomType> fileType)
+DomUniverse::LoadResult DomUniverse::loadFile(const FileToLoad &file, DomType fileType,
+ DomCreationOptions creationOptions)
{
- DomType fType = (bool(fileType) ? (*fileType) : fileTypeForPath(self, canonicalFilePath));
- switch (fType) {
+ DomItem univ(shared_from_this());
+ switch (fileType) {
case DomType::QmlFile:
case DomType::QmltypesFile:
case DomType::QmldirFile:
- case DomType::QmlDirectory: {
- // Protect the queue from concurrent access.
- QMutexLocker l(mutex());
- m_queue.enqueue(ParsingTask{ QDateTime::currentDateTimeUtc(), loadOptions, fType,
- canonicalFilePath, logicalPath, code, codeDate,
- self.ownerAs<DomUniverse>(), callback });
- break;
+ case DomType::QmlDirectory:
+ case DomType::JsFile: {
+ LoadResult loadRes;
+ const auto &preLoadResult = preload(univ, file, fileType);
+ if (std::holds_alternative<LoadResult>(preLoadResult)) {
+ // universe already has the most recent version of the file
+ return std::get<LoadResult>(preLoadResult);
+ } else {
+ // content of the file needs to be parsed and value inside Universe needs to be updated
+ return load(std::get<ContentWithDate>(preLoadResult), file, fileType, creationOptions);
+ }
}
default:
- self.addError(myErrors()
+ univ.addError(myErrors()
.error(tr("Ignoring request to load file %1 of unexpected type %2, "
"calling callback immediately")
- .arg(canonicalFilePath, domTypeToString(fType)))
+ .arg(file.canonicalPath(), domTypeToString(fileType)))
.handle());
Q_ASSERT(false && "loading non supported file type");
- callback(Path(), DomItem::empty, DomItem::empty);
- return;
+ return {};
}
- if (m_options & Option::SingleThreaded)
- execQueue(); // immediate execution in the same thread
}
-template<typename T>
-QPair<std::shared_ptr<ExternalItemPair<T>>, std::shared_ptr<ExternalItemPair<T>>>
-updateEntry(DomItem &univ, std::shared_ptr<T> newItem,
- QMap<QString, std::shared_ptr<ExternalItemPair<T>>> &map, QBasicMutex *mutex)
-{
- std::shared_ptr<ExternalItemPair<T>> oldValue;
- std::shared_ptr<ExternalItemPair<T>> newValue;
- QString canonicalPath = newItem->canonicalFilePath();
- QDateTime now = QDateTime::currentDateTimeUtc();
- {
- QMutexLocker l(mutex);
- auto it = map.find(canonicalPath);
- if (it != map.cend() && (*it) && (*it)->current) {
- oldValue = *it;
- QString oldCode = oldValue->current->code();
- QString newCode = newItem->code();
- if (!oldCode.isNull() && !newCode.isNull() && oldCode == newCode) {
- newValue = oldValue;
- if (newValue->current->lastDataUpdateAt() < newItem->lastDataUpdateAt())
- newValue->current->refreshedDataAt(newItem->lastDataUpdateAt());
- } else if (oldValue->current->lastDataUpdateAt() > newItem->lastDataUpdateAt()) {
- newValue = oldValue;
- } else {
- DomItem oldValueObj = univ.copy(oldValue);
- newValue = oldValue->makeCopy(oldValueObj);
- newValue->current = newItem;
- newValue->currentExposedAt = now;
- if (newItem->isValid()) {
- newValue->valid = newItem;
- newValue->validExposedAt = now;
- }
- it = map.insert(it, canonicalPath, newValue);
- }
- } else {
- newValue = std::make_shared<ExternalItemPair<T>>(
- (newItem->isValid() ? newItem : std::shared_ptr<T>()), newItem, now, now);
- map.insert(canonicalPath, newValue);
- }
+DomUniverse::LoadResult DomUniverse::load(const ContentWithDate &codeWithDate,
+ const FileToLoad &file, DomType fType,
+ DomCreationOptions creationOptions)
+{
+ QString canonicalPath = file.canonicalPath();
+
+ DomItem oldValue; // old ExternalItemPair (might be empty, or equal to newValue)
+ DomItem newValue; // current ExternalItemPair
+ DomItem univ = DomItem(shared_from_this());
+
+ if (fType == DomType::QmlFile) {
+ auto qmlFile = parseQmlFile(codeWithDate.content, file, codeWithDate.date, creationOptions);
+ return insertOrUpdateExternalItem(std::move(qmlFile));
+ } else if (fType == DomType::QmltypesFile) {
+ auto qmltypesFile = std::make_shared<QmltypesFile>(canonicalPath, codeWithDate.content,
+ codeWithDate.date);
+ QmltypesReader reader(univ.copy(qmltypesFile));
+ reader.parse();
+ return insertOrUpdateExternalItem(std::move(qmltypesFile));
+ } else if (fType == DomType::QmldirFile) {
+ shared_ptr<QmldirFile> qmldirFile =
+ QmldirFile::fromPathAndCode(canonicalPath, codeWithDate.content);
+ return insertOrUpdateExternalItem(std::move(qmldirFile));
+ } else if (fType == DomType::QmlDirectory) {
+ auto qmlDirectory = std::make_shared<QmlDirectory>(
+ canonicalPath, codeWithDate.content.split(QLatin1Char('\n')), codeWithDate.date);
+ return insertOrUpdateExternalItem(std::move(qmlDirectory));
+ } else if (fType == DomType::JsFile) {
+ auto jsFile = parseJsFile(codeWithDate.content, file, codeWithDate.date);
+ return insertOrUpdateExternalItem(std::move(jsFile));
+ } else {
+ Q_ASSERT(false);
}
- return qMakePair(oldValue, newValue);
+ return { std::move(oldValue), std::move(newValue) };
}
-void DomUniverse::execQueue()
+/*!
+ \internal
+ This function is somewhat coupled and does the following:
+ 1. If a content of the file is provided it checks whether the item with the same content
+ already exists inside the Universe. If so, returns it as a result of the load
+ 2. If a content is not provided, it first tries to check whether Universe has the most
+ recent item. If yes, it returns it as a result of the load. Otherwise does step 1.
+ */
+DomUniverse::PreloadResult DomUniverse::preload(const DomItem &univ, const FileToLoad &file,
+ DomType fType) const
{
- ParsingTask t;
- {
- // Protect the queue from concurrent access.
- QMutexLocker l(mutex());
- if (m_queue.isEmpty())
- return;
- t = m_queue.dequeue();
- }
- shared_ptr<DomUniverse> topPtr = t.requestingUniverse.lock();
- if (!topPtr) {
- myErrors().error(tr("Ignoring callback for loading of %1: universe is not valid anymore").arg(t.canonicalPath)).handle();
- }
- QString canonicalPath = t.canonicalPath;
- QString code = t.contents;
- QDateTime contentDate = t.contentsDate;
- bool skipParse = false;
- DomItem oldValue; // old ExternalItemPair (might be empty, or equal to newValue)
- DomItem newValue; // current ExternalItemPair
- DomItem univ = DomItem(topPtr);
- QFileInfo path(canonicalPath);
- QVector<ErrorMessage> messages;
-
- if (t.kind == DomType::QmlFile || t.kind == DomType::QmltypesFile
- || t.kind == DomType::QmldirFile || t.kind == DomType::QmlDirectory) {
- auto getValue = [&t, this, &canonicalPath]() -> std::shared_ptr<ExternalItemPairBase> {
- if (t.kind == DomType::QmlFile)
- return m_qmlFileWithPath.value(canonicalPath);
- else if (t.kind == DomType::QmltypesFile)
- return m_qmlFileWithPath.value(canonicalPath);
- else if (t.kind == DomType::QmldirFile)
- return m_qmlFileWithPath.value(canonicalPath);
- else if (t.kind == DomType::QmlDirectory)
- return m_qmlDirectoryWithPath.value(canonicalPath);
- else
- Q_ASSERT(false);
- return {};
- };
- if (code.isEmpty()) {
- QFile file(canonicalPath);
- canonicalPath = path.canonicalFilePath();
- if (canonicalPath.isEmpty()) {
- messages.append(myErrors().error(tr("Non existing path %1").arg(t.canonicalPath)));
- canonicalPath = t.canonicalPath;
- }
- {
- QMutexLocker l(mutex());
- auto value = getValue();
- if (!(t.loadOptions & LoadOption::ForceLoad) && value) {
- if (value && value->currentItem()
- && path.lastModified() < value->currentItem()->lastDataUpdateAt()) {
- oldValue = newValue = univ.copy(value);
- skipParse = true;
- }
- }
- }
- if (!skipParse) {
- contentDate = QDateTime::currentDateTimeUtc();
- if (QFileInfo(canonicalPath).isDir()) {
- code = QDir(canonicalPath)
- .entryList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name)
- .join(QLatin1Char('\n'));
- } else if (!file.open(QIODevice::ReadOnly)) {
- code = QStringLiteral(u"");
- messages.append(myErrors().error(tr("Error opening path %1: %2 %3")
- .arg(canonicalPath,
- QString::number(file.error()),
- file.errorString())));
- } else {
- code = QString::fromUtf8(file.readAll());
- file.close();
- }
- }
- }
- if (!skipParse) {
- QMutexLocker l(mutex());
- if (auto value = getValue()) {
- QString oldCode = value->currentItem()->code();
- if (value && value->currentItem() && !oldCode.isNull() && oldCode == code) {
- skipParse = true;
- newValue = oldValue = univ.copy(value);
- if (value->currentItem()->lastDataUpdateAt() < contentDate)
- value->currentItem()->refreshedDataAt(contentDate);
- }
- }
- }
- if (!skipParse) {
- QDateTime now(QDateTime::currentDateTimeUtc());
- if (t.kind == DomType::QmlFile) {
- auto qmlFile = std::make_shared<QmlFile>(canonicalPath, code, contentDate);
- auto envPtr = std::make_shared<DomEnvironment>(
- QStringList(), DomEnvironment::Option::NoDependencies, topPtr);
- envPtr->addQmlFile(qmlFile);
- DomItem env(envPtr);
- if (qmlFile->isValid()) {
- MutableDomItem qmlFileObj(env.copy(qmlFile));
- createDom(qmlFileObj);
- } else {
- QString errs;
- DomItem qmlFileObj = env.copy(qmlFile);
- qmlFile->iterateErrors(qmlFileObj, [&errs](DomItem, ErrorMessage m) {
- errs += m.toString();
- errs += u"\n";
- return true;
- });
- qCWarning(domLog).noquote().nospace()
- << "Parsed invalid file " << canonicalPath << errs;
- }
- auto change = updateEntry<QmlFile>(univ, qmlFile, m_qmlFileWithPath, mutex());
- oldValue = univ.copy(change.first);
- newValue = univ.copy(change.second);
- } else if (t.kind == DomType::QmltypesFile) {
- auto qmltypesFile = std::make_shared<QmltypesFile>(
- canonicalPath, code, contentDate);
- QmltypesReader reader(univ.copy(qmltypesFile));
- reader.parse();
- auto change = updateEntry<QmltypesFile>(univ, qmltypesFile, m_qmltypesFileWithPath,
- mutex());
- oldValue = univ.copy(change.first);
- newValue = univ.copy(change.second);
- } else if (t.kind == DomType::QmldirFile) {
- shared_ptr<QmldirFile> qmldirFile =
- QmldirFile::fromPathAndCode(canonicalPath, code);
- auto change =
- updateEntry<QmldirFile>(univ, qmldirFile, m_qmldirFileWithPath, mutex());
- oldValue = univ.copy(change.first);
- newValue = univ.copy(change.second);
- } else if (t.kind == DomType::QmlDirectory) {
- auto qmlDirectory = std::make_shared<QmlDirectory>(
- canonicalPath, code.split(QLatin1Char('\n')), contentDate);
- auto change = updateEntry<QmlDirectory>(univ, qmlDirectory, m_qmlDirectoryWithPath,
- mutex());
- oldValue = univ.copy(change.first);
- newValue = univ.copy(change.second);
- } else {
- Q_ASSERT(false);
- }
+ QString canonicalPath = file.canonicalPath();
+ ContentWithDate codeWithDate;
+
+ if (file.content().has_value()) {
+ codeWithDate = { file.content()->data, file.content()->date };
+ } else {
+ // When content is empty, Universe attempts to read it from the File.
+ // However if it already has the most recent version of that File it just returns it
+ const auto &curValueItem = getItemIfMostRecent(univ, fType, canonicalPath);
+ if (curValueItem.has_value()) {
+ return LoadResult{ curValueItem.value(), curValueItem.value() };
}
- for (const ErrorMessage &m : messages)
- newValue.addError(m);
- // to do: tell observers?
- // execute callback
- if (t.callback) {
- Path p;
- if (t.kind == DomType::QmlFile)
- p = Paths::qmlFileInfoPath(canonicalPath);
- else if (t.kind == DomType::QmltypesFile)
- p = Paths::qmltypesFileInfoPath(canonicalPath);
- else if (t.kind == DomType::QmldirFile)
- p = Paths::qmldirFileInfoPath(canonicalPath);
- else if (t.kind == DomType::QmlDirectory)
- p = Paths::qmlDirectoryInfoPath(canonicalPath);
- else
- Q_ASSERT(false);
- t.callback(p, oldValue, newValue);
+ // otherwise tries to read the content from the path
+ auto readResult = readFileContent(canonicalPath);
+ if (std::holds_alternative<ErrorMessage>(readResult)) {
+ DomItem newValue;
+ newValue.addError(std::move(std::get<ErrorMessage>(readResult)));
+ return LoadResult{ DomItem(), std::move(newValue) }; // read failed, nothing to parse
+ } else {
+ codeWithDate = std::get<ContentWithDate>(readResult);
}
- } else {
- Q_ASSERT(false && "Unhandled kind in queue");
}
+
+ // Once the code is provided Universe verifies if it already has an up-to-date code
+ const auto &curValueItem = getItemIfHasSameCode(univ, fType, canonicalPath, codeWithDate);
+ if (curValueItem.has_value()) {
+ return LoadResult{ curValueItem.value(), curValueItem.value() };
+ }
+ // otherwise code needs to be parsed
+ return codeWithDate;
}
void DomUniverse::removePath(const QString &path)
{
QMutexLocker l(mutex());
- auto toDelete = [path](auto it) {
+ const auto toDelete = [path](const auto &it) {
QString p = it.key();
return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
};
@@ -498,7 +356,191 @@ void DomUniverse::removePath(const QString &path)
m_qmltypesFileWithPath.removeIf(toDelete);
}
-std::shared_ptr<OwningItem> LoadInfo::doCopy(DomItem &self) const
+DomUniverse::ReadResult DomUniverse::readFileContent(const QString &canonicalPath) const
+{
+ if (canonicalPath.isEmpty()) {
+ return myErrors().error(tr("Non existing path %1").arg(canonicalPath));
+ }
+ QFile file(canonicalPath);
+ QFileInfo fileInfo(canonicalPath);
+ if (fileInfo.isDir()) {
+ return ContentWithDate{ QDir(canonicalPath)
+ .entryList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name)
+ .join(QLatin1Char('\n')),
+ QDateTime::currentDateTimeUtc() };
+ }
+ if (!file.open(QIODevice::ReadOnly)) {
+ return myErrors().error(
+ tr("Error opening path %1: %2 %3")
+ .arg(canonicalPath, QString::number(file.error()), file.errorString()));
+ }
+ auto content = QString::fromUtf8(file.readAll());
+ file.close();
+ return ContentWithDate{ std::move(content), QDateTime::currentDateTimeUtc() };
+}
+
+std::shared_ptr<QmlFile> DomUniverse::parseQmlFile(const QString &code, const FileToLoad &file,
+ const QDateTime &contentDate,
+ DomCreationOptions creationOptions)
+{
+ auto qmlFile = std::make_shared<QmlFile>(file.canonicalPath(), code, contentDate, 0,
+ creationOptions.testFlag(WithRecovery)
+ ? QmlFile::EnableParserRecovery
+ : QmlFile::DisableParserRecovery);
+ std::shared_ptr<DomEnvironment> envPtr;
+ if (auto ptr = file.environment().lock())
+ envPtr = std::move(ptr);
+ else
+ envPtr = std::make_shared<DomEnvironment>(QStringList(),
+ DomEnvironment::Option::NoDependencies,
+ creationOptions, shared_from_this());
+ envPtr->addQmlFile(qmlFile);
+ DomItem env(envPtr);
+ if (qmlFile->isValid()) {
+ // do not call populateQmlFile twice on lazy qml files if the importer already does it!
+ if (!creationOptions.testFlag(DomCreationOption::WithSemanticAnalysis))
+ envPtr->populateFromQmlFile(MutableDomItem(env.copy(qmlFile)));
+ } else {
+ QString errs;
+ DomItem qmlFileObj = env.copy(qmlFile);
+ qmlFile->iterateErrors(qmlFileObj, [&errs](const DomItem &, const ErrorMessage &m) {
+ errs += m.toString();
+ errs += u"\n";
+ return true;
+ });
+ qCWarning(domLog).noquote().nospace()
+ << "Parsed invalid file " << file.canonicalPath() << errs;
+ }
+ return qmlFile;
+}
+
+std::shared_ptr<JsFile> DomUniverse::parseJsFile(const QString &code, const FileToLoad &file,
+ const QDateTime &contentDate)
+{
+ // WATCH OUT!
+ // DOM construction for plain JS files is not yet supported
+ // Only parsing of the file
+ // and adding ExternalItem to the Environment will happen here
+ auto jsFile = std::make_shared<JsFile>(file.canonicalPath(), code, contentDate);
+ std::shared_ptr<DomEnvironment> envPtr;
+ if (auto ptr = file.environment().lock())
+ envPtr = std::move(ptr);
+ else
+ envPtr = std::make_shared<DomEnvironment>(QStringList(),
+ DomEnvironment::Option::NoDependencies,
+ DomCreationOption::None, shared_from_this());
+ envPtr->addJsFile(jsFile);
+ DomItem env(envPtr);
+ if (!jsFile->isValid()) {
+ QString errs;
+ DomItem qmlFileObj = env.copy(jsFile);
+ jsFile->iterateErrors(qmlFileObj, [&errs](const DomItem &, const ErrorMessage &m) {
+ errs += m.toString();
+ errs += u"\n";
+ return true;
+ });
+ qCWarning(domLog).noquote().nospace()
+ << "Parsed invalid file " << file.canonicalPath() << errs;
+ }
+ return jsFile;
+}
+
+/*!
+ \internal
+ Queries the corresponding path map attempting to get the value
+ *WARNING* Usage of this function should be protected by the read lock
+ */
+std::shared_ptr<ExternalItemPairBase> DomUniverse::getPathValueOrNull(DomType fType,
+ const QString &path) const
+{
+ switch (fType) {
+ case DomType::QmlFile:
+ return m_qmlFileWithPath.value(path);
+ case DomType::QmltypesFile:
+ return m_qmltypesFileWithPath.value(path);
+ case DomType::QmldirFile:
+ return m_qmldirFileWithPath.value(path);
+ case DomType::QmlDirectory:
+ return m_qmlDirectoryWithPath.value(path);
+ case DomType::JsFile:
+ return m_jsFileWithPath.value(path);
+ default:
+ Q_ASSERT(false);
+ }
+ return nullptr;
+}
+
+std::optional<DomItem> DomUniverse::getItemIfMostRecent(const DomItem &univ, DomType fType,
+ const QString &canonicalPath) const
+{
+ QFileInfo fInfo(canonicalPath);
+ bool valueItemIsMostRecent = false;
+ std::shared_ptr<ExternalItemPairBase> value = nullptr;
+ {
+ // Mutex is to sync access to the Value and Value->CurrentItem, which can be modified
+ // through updateEnty method and currentItem->refreshedDataAt
+ QMutexLocker l(mutex());
+ value = getPathValueOrNull(fType, canonicalPath);
+ valueItemIsMostRecent = valueHasMostRecentItem(value.get(), fInfo.lastModified());
+ }
+ if (valueItemIsMostRecent) {
+ return univ.copy(value);
+ }
+ return std::nullopt;
+}
+
+std::optional<DomItem> DomUniverse::getItemIfHasSameCode(const DomItem &univ, DomType fType,
+ const QString &canonicalPath,
+ const ContentWithDate &codeWithDate) const
+{
+ std::shared_ptr<ExternalItemPairBase> value = nullptr;
+ bool valueItemHasSameCode = false;
+ {
+ // Mutex is to sync access to the Value and Value->CurrentItem, which can be modified
+ // through updateEnty method and currentItem->refreshedDataAt
+ QMutexLocker l(mutex());
+ value = getPathValueOrNull(fType, canonicalPath);
+ if (valueHasSameContent(value.get(), codeWithDate.content)) {
+ valueItemHasSameCode = true;
+ if (value->currentItem()->lastDataUpdateAt() < codeWithDate.date)
+ value->currentItem()->refreshedDataAt(codeWithDate.date);
+ }
+ }
+ if (valueItemHasSameCode) {
+ return univ.copy(value);
+ }
+ return std::nullopt;
+}
+
+/*!
+ \internal
+ Checks if value has current Item and if it was not modified since last seen
+ *WARNING* Usage of this function should be protected by the read lock
+ */
+bool DomUniverse::valueHasMostRecentItem(const ExternalItemPairBase *value,
+ const QDateTime &lastModified)
+{
+ if (!value || !value->currentItem()) {
+ return false;
+ }
+ return lastModified < value->currentItem()->lastDataUpdateAt();
+}
+
+/*!
+ \internal
+ Checks if value has current Item and if it has same content
+ *WARNING* Usage of this function should be protected by the read lock
+ */
+bool DomUniverse::valueHasSameContent(const ExternalItemPairBase *value, const QString &content)
+{
+ if (!value || !value->currentItem()) {
+ return false;
+ }
+ QString curContent = value->currentItem()->code();
+ return !curContent.isNull() && curContent == content;
+}
+
+std::shared_ptr<OwningItem> LoadInfo::doCopy(const DomItem &self) const
{
auto res = std::make_shared<LoadInfo>(*this);
if (res->status() != Status::Done) {
@@ -506,7 +548,7 @@ std::shared_ptr<OwningItem> LoadInfo::doCopy(DomItem &self) const
u"This is a copy of a LoadInfo still in progress, artificially ending it, if you "
u"use this you will *not* resume loading"));
DomEnvironment::myErrors()
- .warning([&self](Sink sink) {
+ .warning([&self](const Sink &sink) {
sink(u"Copying an in progress LoadInfo, which is most likely an error (");
self.dump(sink);
sink(u")");
@@ -521,12 +563,12 @@ std::shared_ptr<OwningItem> LoadInfo::doCopy(DomItem &self) const
return res;
}
-Path LoadInfo::canonicalPath(DomItem &) const
+Path LoadInfo::canonicalPath(const DomItem &) const
{
return Path::Root(PathRoot::Env).field(Fields::loadInfo).key(elementCanonicalPath().toString());
}
-bool LoadInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool LoadInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::status, int(status()));
@@ -539,8 +581,8 @@ bool LoadInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-void LoadInfo::addEndCallback(DomItem &self,
- std::function<void(Path, DomItem &, DomItem &)> callback)
+void LoadInfo::addEndCallback(const DomItem &self,
+ std::function<void(Path, const DomItem &, const DomItem &)> callback)
{
if (!callback)
return;
@@ -562,7 +604,7 @@ void LoadInfo::addEndCallback(DomItem &self,
callback(p, el, el);
}
-void LoadInfo::advanceLoad(DomItem &self)
+void LoadInfo::advanceLoad(const DomItem &self)
{
Status myStatus;
Dependency dep;
@@ -607,26 +649,27 @@ void LoadInfo::advanceLoad(DomItem &self)
case Status::InProgress:
if (depValid) {
refreshedDataAt(QDateTime::currentDateTimeUtc());
+ auto envPtr = self.environment().ownerAs<DomEnvironment>();
+ Q_ASSERT(envPtr && "missing environment");
if (!dep.uri.isEmpty()) {
- self.loadModuleDependency(
+ envPtr->loadModuleDependency(
dep.uri, dep.version,
- [this, self, dep](Path, DomItem &, DomItem &) mutable {
- finishedLoadingDep(self, dep);
+ [this, copiedSelf = self, dep](Path, const DomItem &, const DomItem &) {
+ // Need to explicitly copy self here since we might store this and
+ // call it later.
+ finishedLoadingDep(copiedSelf, dep);
},
self.errorHandler());
Q_ASSERT(dep.filePath.isEmpty() && "dependency with both uri and file");
} else if (!dep.filePath.isEmpty()) {
- DomItem env = self.environment();
- if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>())
- envPtr->loadFile(
- env, dep.filePath, QString(),
- [this, self, dep](Path, DomItem &, DomItem &) mutable {
- finishedLoadingDep(self, dep);
- },
- nullptr, nullptr, LoadOption::DefaultLoad, dep.fileType,
- self.errorHandler());
- else
- Q_ASSERT(false && "missing environment");
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, dep.filePath),
+ [this, copiedSelf = self, dep](Path, const DomItem &, const DomItem &) {
+ // Need to explicitly copy self here since we might store this and
+ // call it later.
+ finishedLoadingDep(copiedSelf, dep);
+ },
+ dep.fileType, self.errorHandler());
} else {
Q_ASSERT(false && "dependency without uri and filePath");
}
@@ -643,7 +686,7 @@ void LoadInfo::advanceLoad(DomItem &self)
}
}
-void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
+void LoadInfo::finishedLoadingDep(const DomItem &self, const Dependency &d)
{
bool didRemove = false;
bool unexpectedState = false;
@@ -668,7 +711,7 @@ void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
}
}
if (!didRemove) {
- addErrorLocal(DomEnvironment::myErrors().error([&self](Sink sink) {
+ addErrorLocal(DomEnvironment::myErrors().error([&self](const Sink &sink) {
sink(u"LoadInfo::finishedLoadingDep did not find its dependency in those inProgress "
u"()");
self.dump(sink);
@@ -678,7 +721,7 @@ void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
&& "LoadInfo::finishedLoadingDep did not find its dependency in those inProgress");
}
if (unexpectedState) {
- addErrorLocal(DomEnvironment::myErrors().error([&self](Sink sink) {
+ addErrorLocal(DomEnvironment::myErrors().error([&self](const Sink &sink) {
sink(u"LoadInfo::finishedLoadingDep found an unexpected state (");
self.dump(sink);
sink(u")");
@@ -689,9 +732,9 @@ void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
execEnd(self);
}
-void LoadInfo::execEnd(DomItem &self)
+void LoadInfo::execEnd(const DomItem &self)
{
- QList<std::function<void(Path, DomItem &, DomItem &)>> endCallbacks;
+ QList<std::function<void(Path, const DomItem &, const DomItem &)>> endCallbacks;
bool unexpectedState = false;
{
QMutexLocker l(mutex());
@@ -704,7 +747,7 @@ void LoadInfo::execEnd(DomItem &self)
DomItem el = self.path(p);
{
auto cleanup = qScopeGuard([this, p, &el] {
- QList<std::function<void(Path, DomItem &, DomItem &)>> otherCallbacks;
+ QList<std::function<void(Path, const DomItem &, const DomItem &)>> otherCallbacks;
bool unexpectedState2 = false;
{
QMutexLocker l(mutex());
@@ -726,7 +769,7 @@ void LoadInfo::execEnd(DomItem &self)
}
}
-void LoadInfo::doAddDependencies(DomItem &self)
+void LoadInfo::doAddDependencies(const DomItem &self)
{
if (!elementCanonicalPath()) {
DomEnvironment::myErrors()
@@ -739,55 +782,36 @@ void LoadInfo::doAddDependencies(DomItem &self)
DomItem el = self.path(elementCanonicalPath());
if (el.internalKind() == DomType::ExternalItemInfo) {
DomItem currentFile = el.field(Fields::currentItem);
- DomItem currentImports = currentFile.field(Fields::imports);
QString currentFilePath = currentFile.canonicalFilePath();
- int iEnd = currentImports.indexes();
- for (int i = 0; i < iEnd; ++i) {
- DomItem import = currentImports.index(i);
- if (const Import *importPtr = import.as<Import>()) {
- if (importPtr->uri.isDirectory()) {
- QString path = importPtr->uri.absoluteLocalPath(currentFilePath);
- if (!path.isEmpty()) {
- addDependency(self,
- Dependency { QString(), importPtr->version, path,
- DomType::QmlDirectory });
- } else {
- self.addError(DomEnvironment::myErrors().error(
- tr("Ignoring dependencies for non resolved path import %1")
- .arg(importPtr->uri.toString())));
- }
- } else {
- addDependency(self,
- Dependency { importPtr->uri.moduleUri(), importPtr->version,
- QString(), DomType::ModuleIndex });
- }
- }
- }
- DomItem currentQmltypesFiles = currentFile.field(Fields::qmltypesFiles);
- int qEnd = currentQmltypesFiles.indexes();
- for (int i = 0; i < qEnd; ++i) {
- DomItem qmltypesRef = currentQmltypesFiles.index(i);
- if (const Reference *ref = qmltypesRef.as<Reference>()) {
- Path canonicalPath = ref->referredObjectPath[2];
- if (canonicalPath && !canonicalPath.headName().isEmpty())
- addDependency(self,
- Dependency { QString(), Version(), canonicalPath.headName(),
- DomType::QmltypesFile });
- }
- }
- DomItem currentQmlFiles = currentFile.field(Fields::qmlFiles);
- currentQmlFiles.visitKeys([this, &self](QString, DomItem &els) {
- return els.visitIndexes([this, &self](DomItem &el) {
- if (const Reference *ref = el.as<Reference>()) {
+ // do not mess with QmlFile's lazy-loading
+ if (currentFile.internalKind() != DomType::QmlFile) {
+ DomItem currentQmltypesFiles = currentFile.field(Fields::qmltypesFiles);
+ int qEnd = currentQmltypesFiles.indexes();
+ for (int i = 0; i < qEnd; ++i) {
+ DomItem qmltypesRef = currentQmltypesFiles.index(i);
+ if (const Reference *ref = qmltypesRef.as<Reference>()) {
Path canonicalPath = ref->referredObjectPath[2];
if (canonicalPath && !canonicalPath.headName().isEmpty())
- addDependency(self,
- Dependency { QString(), Version(), canonicalPath.headName(),
- DomType::QmlFile });
+ addDependency(
+ self,
+ Dependency{ QString(), Version(), canonicalPath.headName(),
+ DomType::QmltypesFile });
}
- return true;
+ }
+ DomItem currentQmlFiles = currentFile.field(Fields::qmlFiles);
+ currentQmlFiles.visitKeys([this, &self](const QString &, const DomItem &els) {
+ return els.visitIndexes([this, &self](const DomItem &el) {
+ if (const Reference *ref = el.as<Reference>()) {
+ Path canonicalPath = ref->referredObjectPath[2];
+ if (canonicalPath && !canonicalPath.headName().isEmpty())
+ addDependency(self,
+ Dependency{ QString(), Version(),
+ canonicalPath.headName(), DomType::QmlFile });
+ }
+ return true;
+ });
});
- });
+ }
} else if (shared_ptr<ModuleIndex> elPtr = el.ownerAs<ModuleIndex>()) {
const auto qmldirs = elPtr->qmldirsToLoad(el);
for (const Path &qmldirPath : qmldirs) {
@@ -798,7 +822,7 @@ void LoadInfo::doAddDependencies(DomItem &self)
DomType::QmldirFile });
}
QString uri = elPtr->uri();
- addEndCallback(self, [uri, qmldirs](Path, DomItem &, DomItem &newV) {
+ addEndCallback(self, [uri, qmldirs](Path, const DomItem &, const DomItem &newV) {
for (const Path &p : qmldirs) {
DomItem qmldir = newV.path(p);
if (std::shared_ptr<QmldirFile> qmldirFilePtr = qmldir.ownerAs<QmldirFile>()) {
@@ -818,7 +842,7 @@ void LoadInfo::doAddDependencies(DomItem &self)
}
}
-void LoadInfo::addDependency(DomItem &self, const Dependency &dep)
+void LoadInfo::addDependency(const DomItem &self, const Dependency &dep)
{
bool unexpectedState = false;
{
@@ -835,105 +859,14 @@ void LoadInfo::addDependency(DomItem &self, const Dependency &dep)
\class QQmlJS::Dom::DomEnvironment
\brief Represents a consistent set of types organized in modules, it is the top level of the DOM
- */
-template<typename T>
-DomTop::Callback envCallbackForFile(
- DomItem &self, QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> DomEnvironment::*map,
- std::shared_ptr<ExternalItemInfo<T>> (DomEnvironment::*lookupF)(DomItem &, QString,
- EnvLookup) const,
- DomTop::Callback loadCallback, DomTop::Callback allDirectDepsCallback,
- DomTop::Callback endCallback)
-{
- std::shared_ptr<DomEnvironment> ePtr = self.ownerAs<DomEnvironment>();
- std::weak_ptr<DomEnvironment> selfPtr = ePtr;
- std::shared_ptr<DomEnvironment> basePtr = ePtr->base();
- return [selfPtr, basePtr, map, lookupF, loadCallback, allDirectDepsCallback,
- endCallback](Path, DomItem &, DomItem &newItem) {
- shared_ptr<DomEnvironment> envPtr = selfPtr.lock();
- if (!envPtr)
- return;
- DomItem env = DomItem(envPtr);
- shared_ptr<ExternalItemInfo<T>> oldValue;
- shared_ptr<ExternalItemInfo<T>> newValue;
- shared_ptr<T> newItemPtr;
- if (envPtr->options() & DomEnvironment::Option::KeepValid)
- newItemPtr = newItem.field(Fields::validItem).ownerAs<T>();
- if (!newItemPtr)
- newItemPtr = newItem.field(Fields::currentItem).ownerAs<T>();
- Q_ASSERT(newItemPtr && "callbackForQmlFile reached without current qmlFile");
- {
- QMutexLocker l(envPtr->mutex());
- oldValue = ((*envPtr).*map).value(newItem.canonicalFilePath());
- }
- if (oldValue) {
- // we do not change locally loaded files (avoid loading a file more than once)
- newValue = oldValue;
- } else {
- if (basePtr) {
- DomItem baseObj(basePtr);
- oldValue = ((*basePtr).*lookupF)(baseObj, newItem.canonicalFilePath(),
- EnvLookup::BaseOnly);
- }
- if (oldValue) {
- DomItem oldValueObj = env.copy(oldValue);
- newValue = oldValue->makeCopy(oldValueObj);
- if (newValue->current != newItemPtr) {
- newValue->current = newItemPtr;
- newValue->setCurrentExposedAt(QDateTime::currentDateTimeUtc());
- }
- } else {
- newValue = std::make_shared<ExternalItemInfo<T>>(
- newItemPtr, QDateTime::currentDateTimeUtc());
- }
- {
- QMutexLocker l(envPtr->mutex());
- auto value = ((*envPtr).*map).value(newItem.canonicalFilePath());
- if (value) {
- oldValue = newValue = value;
- } else {
- ((*envPtr).*map).insert(newItem.canonicalFilePath(), newValue);
- }
- }
- }
- Path p = env.copy(newValue).canonicalPath();
- {
- auto depLoad = qScopeGuard([p, &env, envPtr, allDirectDepsCallback, endCallback] {
- if (!(envPtr->options() & DomEnvironment::Option::NoDependencies)) {
- auto loadInfo = std::make_shared<LoadInfo>(p);
- if (!p)
- Q_ASSERT(false);
- DomItem loadInfoObj = env.copy(loadInfo);
- loadInfo->addEndCallback(loadInfoObj, allDirectDepsCallback);
- envPtr->addLoadInfo(env, loadInfo);
- }
- if (endCallback)
- envPtr->addAllLoadedCallback(env,
- [p, endCallback](Path, DomItem &, DomItem &env) {
- DomItem el = env.path(p);
- endCallback(p, el, el);
- });
- });
- if (loadCallback) {
- DomItem oldValueObj = env.copy(oldValue);
- DomItem newValueObj = env.copy(newValue);
- loadCallback(p, oldValueObj, newValueObj);
- }
- if ((envPtr->options() & DomEnvironment::Option::NoDependencies)
- && allDirectDepsCallback) {
- DomItem oldValueObj = env.copy(oldValue);
- DomItem newValueObj = env.copy(newValue);
- env.addError(DomEnvironment::myErrors().warning(
- QLatin1String("calling allDirectDepsCallback immediately for load with "
- "NoDependencies of %1")
- .arg(newItem.canonicalFilePath())));
- allDirectDepsCallback(p, oldValueObj, newValueObj);
- }
- }
- };
-}
+The DomEnvironment keeps a pointer m_lastValidBase to the last used valid DomEnvironment in the
+commitToBase() method. This allows the qqmldomastcreator to commit lazily loaded dependencies to the
+valid environment used by qmlls.
+ */
-ErrorGroups DomEnvironment::myErrors() {
+ErrorGroups DomEnvironment::myErrors()
+{
static ErrorGroups res = {{NewErrorGroup("Dom")}};
return res;
}
@@ -948,7 +881,7 @@ Path DomEnvironment::canonicalPath() const
return Path::Root(u"env");
}
-bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool DomEnvironment::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
@@ -962,54 +895,54 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::globalScopeWithName, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::globalScopeWithName),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(globalScopeWithName(self, key));
},
- [&self, this](DomItem &) { return globalScopeNames(self); },
+ [&self, this](const DomItem &) { return globalScopeNames(self); },
QLatin1String("GlobalScope")));
});
cont = cont && self.dvItemField(visitor, Fields::qmlDirectoryWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmlDirectoryWithPath),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(qmlDirectoryWithPath(self, key));
},
- [&self, this](DomItem &) { return qmlDirectoryPaths(self); },
+ [&self, this](const DomItem &) { return qmlDirectoryPaths(self); },
QLatin1String("QmlDirectory")));
});
cont = cont && self.dvItemField(visitor, Fields::qmldirFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmldirFileWithPath),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(qmldirFileWithPath(self, key));
},
- [&self, this](DomItem &) { return qmldirFilePaths(self); },
+ [&self, this](const DomItem &) { return qmldirFilePaths(self); },
QLatin1String("QmldirFile")));
});
cont = cont && self.dvItemField(visitor, Fields::qmldirWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmldirWithPath),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(qmlDirWithPath(self, key));
},
- [&self, this](DomItem &) { return qmlDirPaths(self); }, QLatin1String("Qmldir")));
+ [&self, this](const DomItem &) { return qmlDirPaths(self); }, QLatin1String("Qmldir")));
});
cont = cont && self.dvItemField(visitor, Fields::qmlFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmlFileWithPath),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(qmlFileWithPath(self, key));
},
- [&self, this](DomItem &) { return qmlFilePaths(self); }, QLatin1String("QmlFile")));
+ [&self, this](const DomItem &) { return qmlFilePaths(self); }, QLatin1String("QmlFile")));
});
cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::jsFileWithPath),
- [this](DomItem &map, QString key) {
+ [this](const DomItem &map, const QString &key) {
DomItem mapOw(map.owner());
return map.copy(jsFileWithPath(mapOw, key));
},
- [this](DomItem &map) {
+ [this](const DomItem &map) {
DomItem mapOw = map.owner();
return jsFilePaths(mapOw);
},
@@ -1018,11 +951,11 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::qmltypesFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmltypesFileWithPath),
- [this](DomItem &map, QString key) {
+ [this](const DomItem &map, const QString &key) {
DomItem mapOw = map.owner();
return map.copy(qmltypesFileWithPath(mapOw, key));
},
- [this](DomItem &map) {
+ [this](const DomItem &map) {
DomItem mapOw = map.owner();
return qmltypesFilePaths(mapOw);
},
@@ -1031,10 +964,10 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::moduleIndexWithUri, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::moduleIndexWithUri),
- [this](DomItem &map, QString key) {
+ [this](const DomItem &map, const QString &key) {
return map.subMapItem(Map(
map.pathFromOwner().key(key),
- [this, key](DomItem &submap, QString subKey) {
+ [this, key](const DomItem &submap, const QString &subKey) {
bool ok;
int i = subKey.toInt(&ok);
if (!ok) {
@@ -1050,7 +983,7 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
moduleIndexWithUri(subMapOw, key, i);
return submap.copy(mIndex);
},
- [this, key](DomItem &subMap) {
+ [this, key](const DomItem &subMap) {
QSet<QString> res;
DomItem subMapOw = subMap.owner();
for (int mVersion :
@@ -1065,7 +998,7 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
},
QLatin1String("ModuleIndex")));
},
- [this](DomItem &map) {
+ [this](const DomItem &map) {
DomItem mapOw = map.owner();
return moduleIndexUris(mapOw);
},
@@ -1090,14 +1023,14 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
ensureInfo();
return self.subListItem(List(
Path::Field(Fields::loadsWithWork),
- [loadsWithWork](DomItem &list, index_type i) {
+ [loadsWithWork](const DomItem &list, index_type i) {
if (i >= 0 && i < loadsWithWork.size())
return list.subDataItem(PathEls::Index(i),
loadsWithWork.at(i).toString());
else
return DomItem();
},
- [loadsWithWork](DomItem &) {
+ [loadsWithWork](const DomItem &) {
return index_type(loadsWithWork.size());
},
nullptr, QLatin1String("Path")));
@@ -1107,22 +1040,22 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
ensureInfo();
return self.subListItem(List(
Path::Field(Fields::inProgress),
- [inProgress](DomItem &list, index_type i) {
+ [inProgress](const DomItem &list, index_type i) {
if (i >= 0 && i < inProgress.size())
return list.subDataItem(PathEls::Index(i),
inProgress.at(i).toString());
else
return DomItem();
},
- [inProgress](DomItem &) { return index_type(inProgress.size()); },
+ [inProgress](const DomItem &) { return index_type(inProgress.size()); },
nullptr, QLatin1String("Path")));
});
cont = cont && self.dvItemField(visitor, Fields::loadInfo, [&self, this]() {
return self.subMapItem(Map(
Path::Field(Fields::loadInfo),
- [this](DomItem &map, QString pStr) {
+ [this](const DomItem &map, const QString &pStr) {
bool hasErrors = false;
- Path p = Path::fromString(pStr, [&hasErrors](ErrorMessage m) {
+ Path p = Path::fromString(pStr, [&hasErrors](const ErrorMessage &m) {
switch (m.level) {
case ErrorLevel::Debug:
case ErrorLevel::Info:
@@ -1138,7 +1071,7 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return map.copy(loadInfo(p));
return DomItem();
},
- [this](DomItem &) {
+ [this](const DomItem &) {
QSet<QString> res;
const auto infoPaths = loadInfoPaths();
for (const Path &p : infoPaths)
@@ -1157,189 +1090,136 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-DomItem DomEnvironment::field(DomItem &self, QStringView name) const
+DomItem DomEnvironment::field(const DomItem &self, QStringView name) const
{
return DomTop::field(self, name);
}
-std::shared_ptr<DomEnvironment> DomEnvironment::makeCopy(DomItem &self) const
+std::shared_ptr<DomEnvironment> DomEnvironment::makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<DomEnvironment>(doCopy(self));
}
-void DomEnvironment::loadFile(DomItem &self, QString filePath, QString logicalPath,
- DomTop::Callback loadCallback, DomTop::Callback directDepsCallback,
- DomTop::Callback endCallback, LoadOptions loadOptions,
- std::optional<DomType> fileType, ErrorHandler h)
-{
- loadFile(self, filePath, logicalPath, QString(), QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- loadCallback, directDepsCallback, endCallback, loadOptions, fileType, h);
-}
-
-std::shared_ptr<OwningItem> DomEnvironment::doCopy(DomItem &) const
+std::shared_ptr<OwningItem> DomEnvironment::doCopy(const DomItem &) const
{
shared_ptr<DomEnvironment> res;
if (m_base)
- res = std::make_shared<DomEnvironment>(m_base, m_loadPaths, m_options);
+ res = std::make_shared<DomEnvironment>(m_base, m_loadPaths, m_options,
+ m_domCreationOptions);
else
- res = std::make_shared<DomEnvironment>(
- m_loadPaths, m_options, m_universe);
+ res = std::make_shared<DomEnvironment>(m_loadPaths, m_options, m_domCreationOptions,
+ m_universe);
return res;
}
-void DomEnvironment::loadFile(DomItem &self, QString filePath, QString logicalPath, QString code,
- QDateTime codeDate, Callback loadCallback,
- Callback directDepsCallback, Callback endCallback,
- LoadOptions loadOptions, std::optional<DomType> fileType,
- ErrorHandler h)
+void DomEnvironment::loadFile(const FileToLoad &file, const Callback &callback,
+ std::optional<DomType> fileType, const ErrorHandler &h)
{
- QFileInfo fileInfo(filePath);
- QString canonicalFilePath = fileInfo.canonicalFilePath();
- if (canonicalFilePath.isEmpty()) {
- if (code.isNull()) {
- myErrors().error(tr("Non existing path to load: '%1'").arg(filePath)).handle(h);
+ if (options() & DomEnvironment::Option::NoDependencies)
+ loadFile(file, callback, DomTop::Callback(), fileType, h);
+ else {
+ // When the file is required to be loaded with dependencies, those dependencies
+ // will be added to the "pending" queue through envCallbackForFile
+ // then those should not be forgotten to be loaded.
+ loadFile(file, DomTop::Callback(), callback, fileType, h);
+ }
+}
+
+/*!
+ \internal
+ Depending on the options, the function will be called either with loadCallback OR endCallback
+
+ Before loading the file, envCallbackForFile will be created and passed as an argument to
+ universe().loadFile(...).
+ This is a callback which will be called after the load of the file is finished. More
+ specifically when File is required to be loaded without Dependencies only loadCallback is being
+ used. Otherwise, the callback is passed as endCallback. What endCallback means is that this
+ callback will be called only at the very end, once all necessary dependencies are being loaded.
+ Management and handing of this is happening through the m_loadsWithWork.
+*/
+// TODO(QTBUG-119550) refactor this
+void DomEnvironment::loadFile(const FileToLoad &file, const Callback &loadCallback,
+ const Callback &endCallback, std::optional<DomType> fileType,
+ const ErrorHandler &h)
+{
+ DomItem self(shared_from_this());
+ if (file.canonicalPath().isEmpty()) {
+ if (!file.content() || file.content()->data.isNull()) {
+ // file's content inavailable and no path to retrieve it
+ myErrors()
+ .error(tr("Non existing path to load: '%1'").arg(file.logicalPath()))
+ .handle(h);
if (loadCallback)
loadCallback(Path(), DomItem::empty, DomItem::empty);
- if (directDepsCallback)
- directDepsCallback(Path(), DomItem::empty, DomItem::empty);
if (endCallback)
- addAllLoadedCallback(self, [endCallback](Path, DomItem &, DomItem &) {
+ addAllLoadedCallback(self, [endCallback](Path, const DomItem &, const DomItem &) {
endCallback(Path(), DomItem::empty, DomItem::empty);
});
return;
} else {
- canonicalFilePath = filePath;
+ // fallback: path invalid but file's content is already available.
+ file.canonicalPath() = file.logicalPath();
}
}
+
shared_ptr<ExternalItemInfoBase> oldValue, newValue;
- DomType fType = (bool(fileType) ? (*fileType) : fileTypeForPath(self, canonicalFilePath));
+ const DomType fType =
+ (bool(fileType) ? (*fileType) : fileTypeForPath(self, file.canonicalPath()));
switch (fType) {
case DomType::QmlDirectory: {
- {
- QMutexLocker l(mutex());
- auto it = m_qmlDirectoryWithPath.find(canonicalFilePath);
- if (it != m_qmlDirectoryWithPath.end())
- oldValue = newValue = *it;
- }
- if (!newValue && (options() & Option::NoReload) && m_base) {
- if (auto v = m_base->qmlDirectoryWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
- oldValue = v;
- QDateTime now = QDateTime::currentDateTimeUtc();
- auto newV = std::make_shared<ExternalItemInfo<QmlDirectory>>(
- v->current, now, v->revision(), v->lastDataUpdateAt());
- newValue = newV;
- QMutexLocker l(mutex());
- auto it = m_qmlDirectoryWithPath.find(canonicalFilePath);
- if (it != m_qmlDirectoryWithPath.end())
- oldValue = newValue = *it;
- else
- m_qmlDirectoryWithPath.insert(canonicalFilePath, newV);
- }
- }
+ const auto &fetchResult = fetchFileFromEnvs<QmlDirectory>(file);
+ oldValue = fetchResult.first;
+ newValue = fetchResult.second;
if (!newValue) {
- self.universe().loadFile(
- canonicalFilePath, logicalPath, code, codeDate,
- callbackForQmlDirectory(self, loadCallback, directDepsCallback, endCallback),
- loadOptions, fType);
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<QmlDirectory>(loadRes.currentItem,
+ getLoadCallbackFor(fType, loadCallback), endCallback);
return;
}
} break;
case DomType::QmlFile: {
- {
- QMutexLocker l(mutex());
- auto it = m_qmlFileWithPath.find(canonicalFilePath);
- if (it != m_qmlFileWithPath.end())
- oldValue = newValue = *it;
- }
- if (!newValue && (options() & Option::NoReload) && m_base) {
- if (auto v = m_base->qmlFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
- oldValue = v;
- QDateTime now = QDateTime::currentDateTimeUtc();
- auto newV = std::make_shared<ExternalItemInfo<QmlFile>>(
- v->current, now, v->revision(), v->lastDataUpdateAt());
- newValue = newV;
- QMutexLocker l(mutex());
- auto it = m_qmlFileWithPath.find(canonicalFilePath);
- if (it != m_qmlFileWithPath.end())
- oldValue = newValue = *it;
- else
- m_qmlFileWithPath.insert(canonicalFilePath, newV);
- }
- }
+ const auto &fetchResult = fetchFileFromEnvs<QmlFile>(file);
+ oldValue = fetchResult.first;
+ newValue = fetchResult.second;
if (!newValue) {
- self.universe().loadFile(
- canonicalFilePath, logicalPath, code, codeDate,
- callbackForQmlFile(self, loadCallback, directDepsCallback, endCallback),
- loadOptions, fType);
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<QmlFile>(loadRes.currentItem,
+ getLoadCallbackFor(fType, loadCallback), endCallback);
return;
}
} break;
case DomType::QmltypesFile: {
- {
- QMutexLocker l(mutex());
- auto it = m_qmltypesFileWithPath.find(canonicalFilePath);
- if (it != m_qmltypesFileWithPath.end())
- oldValue = newValue = *it;
- }
- if (!newValue && (options() & Option::NoReload) && m_base) {
- if (auto v = m_base->qmltypesFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
- oldValue = v;
- QDateTime now = QDateTime::currentDateTimeUtc();
- auto newV = std::make_shared<ExternalItemInfo<QmltypesFile>>(
- v->current, now, v->revision(), v->lastDataUpdateAt());
- newValue = newV;
- QMutexLocker l(mutex());
- auto it = m_qmltypesFileWithPath.find(canonicalFilePath);
- if (it != m_qmltypesFileWithPath.end())
- oldValue = newValue = *it;
- else
- m_qmltypesFileWithPath.insert(canonicalFilePath, newV);
- }
- }
+ const auto &fetchResult = fetchFileFromEnvs<QmltypesFile>(file);
+ oldValue = fetchResult.first;
+ newValue = fetchResult.second;
if (!newValue) {
- self.universe().loadFile(
- canonicalFilePath, logicalPath, code, codeDate,
- callbackForQmltypesFile(self, loadCallback, directDepsCallback, endCallback),
- loadOptions, fType);
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<QmltypesFile>(loadRes.currentItem,
+ getLoadCallbackFor(fType, loadCallback), endCallback);
return;
}
} break;
case DomType::QmldirFile: {
- {
- QMutexLocker l(mutex());
- auto it = m_qmldirFileWithPath.find(canonicalFilePath);
- if (it != m_qmldirFileWithPath.end())
- oldValue = newValue = *it;
- }
- if (!newValue && (options() & Option::NoReload) && m_base) {
- if (auto v = m_base->qmldirFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
- oldValue = v;
- QDateTime now = QDateTime::currentDateTimeUtc();
- auto newV = std::make_shared<ExternalItemInfo<QmldirFile>>(
- v->current, now, v->revision(), v->lastDataUpdateAt());
- newValue = newV;
- QMutexLocker l(mutex());
- auto it = m_qmldirFileWithPath.find(canonicalFilePath);
- if (it != m_qmldirFileWithPath.end())
- oldValue = newValue = *it;
- else
- m_qmldirFileWithPath.insert(canonicalFilePath, newV);
- }
- }
+ const auto &fetchResult = fetchFileFromEnvs<QmldirFile>(file);
+ oldValue = fetchResult.first;
+ newValue = fetchResult.second;
if (!newValue) {
- self.universe().loadFile(
- canonicalFilePath, logicalPath, code, codeDate,
- callbackForQmldirFile(self, loadCallback, directDepsCallback, endCallback),
- loadOptions, fType);
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<QmldirFile>(loadRes.currentItem,
+ getLoadCallbackFor(fType, loadCallback), endCallback);
return;
}
} break;
+ case DomType::JsFile: {
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<JsFile>(loadRes.currentItem, getLoadCallbackFor(fType, loadCallback),
+ endCallback);
+ return;
+ } break;
default: {
- myErrors().error(tr("Unexpected file to load: '%1'").arg(filePath)).handle(h);
+ myErrors().error(tr("Unexpected file to load: '%1'").arg(file.canonicalPath())).handle(h);
if (loadCallback)
loadCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
- if (directDepsCallback)
- directDepsCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
if (endCallback)
endCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
return;
@@ -1353,27 +1233,34 @@ void DomEnvironment::loadFile(DomItem &self, QString filePath, QString logicalPa
DomItem newValueObj = self.copy(newValue);
loadCallback(p, oldValueObj, newValueObj);
}
- if (directDepsCallback) {
- DomItem lInfoObj = self.copy(lInfo);
- lInfo->addEndCallback(lInfoObj, directDepsCallback);
- }
} else {
self.addError(myErrors().error(tr("missing load info in ")));
if (loadCallback)
loadCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
- if (directDepsCallback)
- directDepsCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
}
if (endCallback)
- addAllLoadedCallback(self, [p, endCallback](Path, DomItem &, DomItem &env) {
+ addAllLoadedCallback(self, [p = std::move(p), endCallback](
+ const Path &, const DomItem &, const DomItem &env) {
DomItem el = env.path(p);
endCallback(p, el, el);
});
}
-void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
+void DomEnvironment::loadModuleDependency(
+ const QString &uri, Version version,
+ const std::function<void(const Path &, const DomItem &, const DomItem &)> &callback,
+ const ErrorHandler &errorHandler)
+{
+ DomItem envItem(shared_from_this());
+ if (options() & DomEnvironment::Option::NoDependencies)
+ loadModuleDependency(envItem, uri, version, callback, nullptr, errorHandler);
+ else
+ loadModuleDependency(envItem, uri, version, nullptr, callback, errorHandler);
+}
+
+void DomEnvironment::loadModuleDependency(const DomItem &self, const QString &uri, Version v,
Callback loadCallback, Callback endCallback,
- ErrorHandler errorHandler)
+ const ErrorHandler &errorHandler)
{
Q_ASSERT(!uri.contains(u'/'));
Path p = Paths::moduleIndexPath(uri, v.majorVersion);
@@ -1388,6 +1275,9 @@ void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
QRegularExpression vRe(QRegularExpression::anchoredPattern(
QRegularExpression::escape(lastComponent) + QStringLiteral(u"\\.([0-9]*)")));
const auto lPaths = loadPaths();
+ qCDebug(QQmlJSDomImporting) << "DomEnvironment::loadModuleDependency: Searching module with"
+ " uri"
+ << uri;
for (const QString &path : lPaths) {
QDir dir(path + (subPathV.isEmpty() ? QStringLiteral(u"") : QStringLiteral(u"/"))
+ subPathV);
@@ -1399,25 +1289,39 @@ void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
if (majorV > maxV) {
QFileInfo fInfo(dir.canonicalPath() + QChar(u'/') + dirNow
+ QStringLiteral(u"/qmldir"));
- if (fInfo.isFile())
+ if (fInfo.isFile()) {
+ qCDebug(QQmlJSDomImporting)
+ << "Found qmldir in " << fInfo.canonicalFilePath();
maxV = majorV;
+ }
}
}
if (!commonV && dirNow == lastComponent) {
QFileInfo fInfo(dir.canonicalPath() + QChar(u'/') + dirNow
+ QStringLiteral(u"/qmldir"));
- if (fInfo.isFile())
+ if (fInfo.isFile()) {
+ qCDebug(QQmlJSDomImporting)
+ << "Found qmldir in " << fInfo.canonicalFilePath();
commonV = true;
+ }
}
}
}
- QAtomicInt toLoad((commonV ? 1 : 0) + ((maxV >= 0) ? 1 : 0));
- auto loadCallback2 = (loadCallback ? [p, loadCallback, toLoad](Path, DomItem &, DomItem &elV) mutable {
- if (--toLoad == 0) {
- DomItem el = elV.path(p);
- loadCallback(p, el, el);
- }
- }: Callback());
+
+ // This decrements _separately_ for each copy of the lambda. So, what we get here is not a
+ // limit on the total number of calls but a limit on the number of calls per caller
+ // location. It gets even funnier if the callback is first called and then copied further.
+ // TODO: Is this the intended behavior?
+ int toLoad = (commonV ? 1 : 0) + ((maxV >= 0) ? 1 : 0);
+ const auto loadCallback2 = loadCallback
+ ? [p, loadCallback, toLoad](Path, const DomItem &, const DomItem &elV) mutable {
+ if (--toLoad == 0) {
+ DomItem el = elV.path(p);
+ loadCallback(p, el, el);
+ }
+ }
+ : Callback();
+
if (maxV >= 0)
loadModuleDependency(self, uri, Version(maxV, v.minorVersion), loadCallback2, nullptr);
if (commonV)
@@ -1425,10 +1329,15 @@ void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
loadCallback2, nullptr);
else if (maxV < 0) {
if (uri != u"QML") {
- addErrorLocal(myErrors()
- .warning(tr("Failed to find main qmldir file for %1 %2")
- .arg(uri, v.stringValue()))
- .handle());
+ const QString loadPaths = lPaths.join(u", "_s);
+ qCDebug(QQmlJSDomImporting)
+ << "DomEnvironment::loadModuleDependency: qmldir at" << (uri + u"/qmldir"_s)
+ << "was not found in " << loadPaths;
+ addErrorLocal(
+ myErrors()
+ .warning(tr("Failed to find main qmldir file for %1 %2 in %3.")
+ .arg(uri, v.stringValue(), loadPaths))
+ .handle());
}
if (loadCallback)
loadCallback(p, DomItem::empty, DomItem::empty);
@@ -1447,14 +1356,16 @@ void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
loadCallback(p, DomItem::empty, DomItem::empty);
}
}
- if (endCallback)
- addAllLoadedCallback(self, [p, endCallback](Path, DomItem &, DomItem &env) {
+ if (endCallback) {
+ addAllLoadedCallback(self, [p = std::move(p), endCallback = std::move(endCallback)](
+ Path, const DomItem &, const DomItem &env) {
DomItem el = env.path(p);
endCallback(p, el, el);
});
+ }
}
-void DomEnvironment::loadBuiltins(DomItem &self, Callback callback, ErrorHandler h)
+void DomEnvironment::loadBuiltins(const Callback &callback, const ErrorHandler &h)
{
QString builtinsName = QLatin1String("builtins.qmltypes");
const auto lPaths = loadPaths();
@@ -1462,7 +1373,8 @@ void DomEnvironment::loadBuiltins(DomItem &self, Callback callback, ErrorHandler
QDir dir(path);
QFileInfo fInfo(dir.filePath(builtinsName));
if (fInfo.isFile()) {
- self.loadFile(fInfo.canonicalFilePath(), QString(), callback, LoadOption::DefaultLoad);
+ loadFile(FileToLoad::fromFileSystem(shared_from_this(), fInfo.canonicalFilePath()),
+ callback);
return;
}
}
@@ -1517,7 +1429,7 @@ QSet<QString> DomEnvironment::getStrings(function_ref<QSet<QString>()> getBase,
return res;
}
-QSet<QString> DomEnvironment::moduleIndexUris(DomItem &, EnvLookup lookup) const
+QSet<QString> DomEnvironment::moduleIndexUris(const DomItem &, EnvLookup lookup) const
{
DomItem baseObj = DomItem(m_base);
return this->getStrings<QMap<int, std::shared_ptr<ModuleIndex>>>(
@@ -1525,7 +1437,7 @@ QSet<QString> DomEnvironment::moduleIndexUris(DomItem &, EnvLookup lookup) const
m_moduleIndexWithUri, lookup);
}
-QSet<int> DomEnvironment::moduleIndexMajorVersions(DomItem &, QString uri, EnvLookup lookup) const
+QSet<int> DomEnvironment::moduleIndexMajorVersions(const DomItem &, const QString &uri, EnvLookup lookup) const
{
QSet<int> res;
if (lookup != EnvLookup::NoBase && m_base) {
@@ -1562,7 +1474,7 @@ std::shared_ptr<ModuleIndex> DomEnvironment::lookupModuleInEnv(const QString &ur
return it->value(majorVersion); // null shared_ptr is fine if no match
}
-DomEnvironment::ModuleLookupResult DomEnvironment::moduleIndexWithUriHelper(DomItem &self, QString uri, int majorVersion, EnvLookup options) const
+DomEnvironment::ModuleLookupResult DomEnvironment::moduleIndexWithUriHelper(const DomItem &self, const QString &uri, int majorVersion, EnvLookup options) const
{
std::shared_ptr<ModuleIndex> res;
if (options != EnvLookup::BaseOnly)
@@ -1594,10 +1506,9 @@ DomEnvironment::ModuleLookupResult DomEnvironment::moduleIndexWithUriHelper(DomI
}
}
-std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, QString uri,
- int majorVersion, EnvLookup options,
- Changeable changeable,
- ErrorHandler errorHandler)
+std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(
+ const DomItem &self, const QString &uri, int majorVersion, EnvLookup options,
+ Changeable changeable, const ErrorHandler &errorHandler)
{
// sanity checks
Q_ASSERT((changeable == Changeable::ReadOnly
@@ -1636,7 +1547,7 @@ std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, Q
auto &modsNow = m_moduleIndexWithUri[uri];
// As we do not hold the lock for the whole operation, some other thread
// might have created the module already
- if (auto it = modsNow.find(majorVersion); it != modsNow.end())
+ if (auto it = modsNow.constFind(majorVersion); it != modsNow.cend())
return *it;
modsNow.insert(majorVersion, newModulePtr);
}
@@ -1654,30 +1565,20 @@ std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, Q
return newModulePtr;
}
-std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, QString uri,
+std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(const DomItem &self, const QString &uri,
int majorVersion,
EnvLookup options) const
{
return moduleIndexWithUriHelper(self, uri, majorVersion, options).module;
}
-
-
std::shared_ptr<ExternalItemInfo<QmlDirectory>>
-DomEnvironment::qmlDirectoryWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::qmlDirectoryWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- if (m_qmlDirectoryWithPath.contains(path))
- return m_qmlDirectoryWithPath.value(path);
- }
- if (options != EnvLookup::NoBase && m_base) {
- return m_base->qmlDirectoryWithPath(self, path, options);
- }
- return {};
+ return lookup<QmlDirectory>(path, options);
}
-QSet<QString> DomEnvironment::qmlDirectoryPaths(DomItem &, EnvLookup options) const
+QSet<QString> DomEnvironment::qmlDirectoryPaths(const DomItem &, EnvLookup options) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<QmlDirectory>>>(
[this] {
@@ -1688,20 +1589,12 @@ QSet<QString> DomEnvironment::qmlDirectoryPaths(DomItem &, EnvLookup options) co
}
std::shared_ptr<ExternalItemInfo<QmldirFile>>
-DomEnvironment::qmldirFileWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::qmldirFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- auto it = m_qmldirFileWithPath.find(path);
- if (it != m_qmldirFileWithPath.end())
- return *it;
- }
- if (options != EnvLookup::NoBase && m_base)
- return m_base->qmldirFileWithPath(self, path, options);
- return {};
+ return lookup<QmldirFile>(path, options);
}
-QSet<QString> DomEnvironment::qmldirFilePaths(DomItem &, EnvLookup lOptions) const
+QSet<QString> DomEnvironment::qmldirFilePaths(const DomItem &, EnvLookup lOptions) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<QmldirFile>>>(
[this] {
@@ -1711,7 +1604,7 @@ QSet<QString> DomEnvironment::qmldirFilePaths(DomItem &, EnvLookup lOptions) con
m_qmldirFileWithPath, lOptions);
}
-std::shared_ptr<ExternalItemInfoBase> DomEnvironment::qmlDirWithPath(DomItem &self, QString path,
+std::shared_ptr<ExternalItemInfoBase> DomEnvironment::qmlDirWithPath(const DomItem &self, const QString &path,
EnvLookup options) const
{
if (auto qmldirFile = qmldirFileWithPath(self, path + QLatin1String("/qmldir"), options))
@@ -1719,7 +1612,7 @@ std::shared_ptr<ExternalItemInfoBase> DomEnvironment::qmlDirWithPath(DomItem &se
return qmlDirectoryWithPath(self, path, options);
}
-QSet<QString> DomEnvironment::qmlDirPaths(DomItem &self, EnvLookup options) const
+QSet<QString> DomEnvironment::qmlDirPaths(const DomItem &self, EnvLookup options) const
{
QSet<QString> res = qmlDirectoryPaths(self, options);
const auto qmldirFiles = qmldirFilePaths(self, options);
@@ -1737,20 +1630,12 @@ QSet<QString> DomEnvironment::qmlDirPaths(DomItem &self, EnvLookup options) cons
}
std::shared_ptr<ExternalItemInfo<QmlFile>>
-DomEnvironment::qmlFileWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::qmlFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- auto it = m_qmlFileWithPath.find(path);
- if (it != m_qmlFileWithPath.end())
- return *it;
- }
- if (options != EnvLookup::NoBase && m_base)
- return m_base->qmlFileWithPath(self, path, options);
- return {};
+ return lookup<QmlFile>(path, options);
}
-QSet<QString> DomEnvironment::qmlFilePaths(DomItem &, EnvLookup lookup) const
+QSet<QString> DomEnvironment::qmlFilePaths(const DomItem &, EnvLookup lookup) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<QmlFile>>>(
[this] {
@@ -1761,19 +1646,12 @@ QSet<QString> DomEnvironment::qmlFilePaths(DomItem &, EnvLookup lookup) const
}
std::shared_ptr<ExternalItemInfo<JsFile>>
-DomEnvironment::jsFileWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::jsFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- if (m_jsFileWithPath.contains(path))
- return m_jsFileWithPath.value(path);
- }
- if (options != EnvLookup::NoBase && m_base)
- return m_base->jsFileWithPath(self, path, EnvLookup::Normal);
- return {};
+ return lookup<JsFile>(path, options);
}
-QSet<QString> DomEnvironment::jsFilePaths(DomItem &, EnvLookup lookup) const
+QSet<QString> DomEnvironment::jsFilePaths(const DomItem &, EnvLookup lookup) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<JsFile>>>(
[this] {
@@ -1784,19 +1662,12 @@ QSet<QString> DomEnvironment::jsFilePaths(DomItem &, EnvLookup lookup) const
}
std::shared_ptr<ExternalItemInfo<QmltypesFile>>
-DomEnvironment::qmltypesFileWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::qmltypesFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- if (m_qmltypesFileWithPath.contains(path))
- return m_qmltypesFileWithPath.value(path);
- }
- if (options != EnvLookup::NoBase && m_base)
- return m_base->qmltypesFileWithPath(self, path, EnvLookup::Normal);
- return {};
+ return lookup<QmltypesFile>(path, options);
}
-QSet<QString> DomEnvironment::qmltypesFilePaths(DomItem &, EnvLookup lookup) const
+QSet<QString> DomEnvironment::qmltypesFilePaths(const DomItem &, EnvLookup lookup) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<QmltypesFile>>>(
[this] {
@@ -1807,21 +1678,14 @@ QSet<QString> DomEnvironment::qmltypesFilePaths(DomItem &, EnvLookup lookup) con
}
std::shared_ptr<ExternalItemInfo<GlobalScope>>
-DomEnvironment::globalScopeWithName(DomItem &self, QString name, EnvLookup lookupOptions) const
+DomEnvironment::globalScopeWithName(const DomItem &, const QString &name,
+ EnvLookup lookupOptions) const
{
- if (lookupOptions != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- auto id = m_globalScopeWithName.find(name);
- if (id != m_globalScopeWithName.end())
- return *id;
- }
- if (lookupOptions != EnvLookup::NoBase && m_base)
- return m_base->globalScopeWithName(self, name, lookupOptions);
- return {};
+ return lookup<GlobalScope>(name, lookupOptions);
}
std::shared_ptr<ExternalItemInfo<GlobalScope>>
-DomEnvironment::ensureGlobalScopeWithName(DomItem &self, QString name, EnvLookup lookupOptions)
+DomEnvironment::ensureGlobalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookupOptions)
{
if (auto current = globalScopeWithName(self, name, lookupOptions))
return current;
@@ -1844,7 +1708,7 @@ DomEnvironment::ensureGlobalScopeWithName(DomItem &self, QString name, EnvLookup
return {};
}
-QSet<QString> DomEnvironment::globalScopeNames(DomItem &, EnvLookup lookupOptions) const
+QSet<QString> DomEnvironment::globalScopeNames(const DomItem &, EnvLookup lookupOptions) const
{
QSet<QString> res;
if (lookupOptions != EnvLookup::NoBase && m_base) {
@@ -1869,7 +1733,26 @@ QSet<QString> DomEnvironment::globalScopeNames(DomItem &, EnvLookup lookupOption
return res;
}
-void DomEnvironment::addLoadInfo(DomItem &self, std::shared_ptr<LoadInfo> loadInfo)
+/*!
+ \internal
+ Depending on the creation options, this function adds LoadInfo of the provided path
+*/
+void DomEnvironment::addDependenciesToLoad(const Path &path)
+{
+ if (options() & Option::NoDependencies) {
+ return;
+ }
+ Q_ASSERT(path);
+ const auto loadInfo = std::make_shared<LoadInfo>(path);
+ return addLoadInfo(DomItem(shared_from_this()), loadInfo);
+}
+
+/*!
+ \internal
+ Enqueues path to the m_loadsWithWork (queue of the pending "load" jobs).
+ In simpler words, schedule the load of the dependencies of the path from loadInfo.
+*/
+void DomEnvironment::addLoadInfo(const DomItem &self, const std::shared_ptr<LoadInfo> &loadInfo)
{
if (!loadInfo)
return;
@@ -1891,7 +1774,7 @@ void DomEnvironment::addLoadInfo(DomItem &self, std::shared_ptr<LoadInfo> loadIn
}
}
-std::shared_ptr<LoadInfo> DomEnvironment::loadInfo(Path path) const
+std::shared_ptr<LoadInfo> DomEnvironment::loadInfo(const Path &path) const
{
QMutexLocker l(mutex());
return m_loadInfos.value(path);
@@ -1909,145 +1792,150 @@ QList<Path> DomEnvironment::loadInfoPaths() const
return lInfos.keys();
}
-DomItem::Callback DomEnvironment::callbackForQmlDirectory(DomItem &self, Callback loadCallback,
- Callback allDirectDepsCallback,
- Callback endCallback)
+DomItem::Callback DomEnvironment::getLoadCallbackFor(DomType fileType, const Callback &loadCallback)
{
- return envCallbackForFile<QmlDirectory>(self, &DomEnvironment::m_qmlDirectoryWithPath,
- &DomEnvironment::qmlDirectoryWithPath, loadCallback,
- allDirectDepsCallback, endCallback);
+ if (fileType == DomType::QmltypesFile) {
+ return [loadCallback](const Path &p, const DomItem &oldV, const DomItem &newV) {
+ DomItem newFile = newV.field(Fields::currentItem);
+ if (std::shared_ptr<QmltypesFile> newFilePtr = newFile.ownerAs<QmltypesFile>())
+ newFilePtr->ensureInModuleIndex(newFile);
+ if (loadCallback)
+ loadCallback(p, oldV, newV);
+ };
+ }
+ return loadCallback;
}
-DomItem::Callback DomEnvironment::callbackForQmlFile(DomItem &self, Callback loadCallback,
- Callback allDirectDepsCallback,
- Callback endCallback)
+DomEnvironment::DomEnvironment(const QStringList &loadPaths, Options options,
+ DomCreationOptions domCreationOptions,
+ const shared_ptr<DomUniverse> &universe)
+ : m_options(options),
+ m_universe(DomUniverse::guaranteeUniverse(universe)),
+ m_loadPaths(loadPaths),
+ m_implicitImports(defaultImplicitImports()),
+ m_domCreationOptions(domCreationOptions)
+
{
- return envCallbackForFile<QmlFile>(self, &DomEnvironment::m_qmlFileWithPath,
- &DomEnvironment::qmlFileWithPath, loadCallback,
- allDirectDepsCallback, endCallback);
}
-DomTop::Callback DomEnvironment::callbackForQmltypesFile(DomItem &self,
- DomTop::Callback loadCallback,
- Callback allDirectDepsCallback,
- DomTop::Callback endCallback)
-{
- return envCallbackForFile<QmltypesFile>(
- self, &DomEnvironment::m_qmltypesFileWithPath, &DomEnvironment::qmltypesFileWithPath,
- [loadCallback](Path p, DomItem &oldV, DomItem &newV) {
- DomItem newFile = newV.field(Fields::currentItem);
- if (std::shared_ptr<QmltypesFile> newFilePtr = newFile.ownerAs<QmltypesFile>())
- newFilePtr->ensureInModuleIndex(newFile);
- if (loadCallback)
- loadCallback(p, oldV, newV);
- },
- allDirectDepsCallback, endCallback);
+/*!
+\internal
+Do not call this method inside of DomEnvironment's constructor! It requires weak_from_this() that
+only works after the constructor call finished.
+*/
+DomEnvironment::SemanticAnalysis &DomEnvironment::semanticAnalysis()
+{
+ if (m_semanticAnalysis)
+ return *m_semanticAnalysis;
+
+ Q_ASSERT(domCreationOptions().testFlag(DomCreationOption::WithSemanticAnalysis));
+
+ m_semanticAnalysis = SemanticAnalysis(m_loadPaths);
+ const auto &importer = m_semanticAnalysis->m_importer;
+
+ importer->setImportVisitor([self = weak_from_this(), base = m_base](
+ QQmlJS::AST::Node *rootNode, QQmlJSImporter *importer,
+ const QQmlJSImporter::ImportVisitorPrerequisites &p) {
+ Q_UNUSED(rootNode);
+ Q_UNUSED(importer);
+
+ // support the "commitToBase" workflow, which does changes in a temporary
+ // DomEnvironment. if the current DomEnvironment is temporary (e.g. the weak pointer is
+ // null), then we assume that the current DomEnvironment was committed to base and then
+ // destructed. Use the base DomEnvironment instead.
+ std::shared_ptr<DomEnvironment> envPtr;
+ if (auto ptr = self.lock()) {
+ envPtr = ptr;
+ } else {
+ envPtr = base;
+ }
+ // populate QML File if from implicit import directory
+ // use the version in DomEnvironment and do *not* load from disk.
+ auto it = envPtr->m_qmlFileWithPath.constFind(p.m_logger->fileName());
+ if (it == envPtr->m_qmlFileWithPath.constEnd()) {
+ qCDebug(domLog) << "Import visitor tried to lazily load file \""
+ << p.m_logger->fileName()
+ << "\", but that file was not found in the DomEnvironment. Was this "
+ "file not discovered by the Dom's dependency loading mechanism?";
+ return;
+ }
+ const DomItem qmlFile = it.value()->currentItem(DomItem(envPtr));
+ envPtr->populateFromQmlFile(MutableDomItem(qmlFile));
+ });
+
+ return *m_semanticAnalysis;
}
-DomTop::Callback DomEnvironment::callbackForQmldirFile(DomItem &self, DomTop::Callback loadCallback,
- Callback allDirectDepsCallback,
- DomTop::Callback endCallback)
+DomEnvironment::SemanticAnalysis::SemanticAnalysis(const QStringList &loadPaths)
+ : m_mapper(
+ std::make_shared<QQmlJSResourceFileMapper>(resourceFilesFromBuildFolders(loadPaths))),
+ m_importer(std::make_shared<QQmlJSImporter>(loadPaths, m_mapper.get(), true))
{
- return envCallbackForFile<QmldirFile>(self, &DomEnvironment::m_qmldirFileWithPath,
- &DomEnvironment::qmldirFileWithPath, loadCallback,
- allDirectDepsCallback, endCallback);
}
-DomEnvironment::DomEnvironment(QStringList loadPaths, Options options,
- shared_ptr<DomUniverse> universe)
- : m_options(options),
- m_universe(DomUniverse::guaranteeUniverse(universe)),
- m_loadPaths(loadPaths),
- m_implicitImports(defaultImplicitImports())
-{}
+void DomEnvironment::SemanticAnalysis::setLoadPaths(const QStringList &loadPaths)
+{
+ // TODO: maybe also update the build paths in m_mapper?
+ m_importer->setImportPaths(loadPaths);
+}
-DomItem DomEnvironment::create(QStringList loadPaths, Options options, DomItem &universe)
+std::shared_ptr<DomEnvironment> DomEnvironment::create(const QStringList &loadPaths,
+ Options options,
+ DomCreationOptions domCreationOptions,
+ const DomItem &universe)
{
std::shared_ptr<DomUniverse> universePtr = universe.ownerAs<DomUniverse>();
- auto envPtr = std::make_shared<DomEnvironment>(loadPaths, options, universePtr);
- return DomItem(envPtr);
+ return std::make_shared<DomEnvironment>(loadPaths, options, domCreationOptions, universePtr);
}
-DomEnvironment::DomEnvironment(shared_ptr<DomEnvironment> parent, QStringList loadPaths,
- Options options)
+DomEnvironment::DomEnvironment(const shared_ptr<DomEnvironment> &parent,
+ const QStringList &loadPaths, Options options,
+ DomCreationOptions domCreationOptions)
: m_options(options),
m_base(parent),
m_loadPaths(loadPaths),
- m_implicitImports(defaultImplicitImports())
-{}
-
-template<typename T>
-std::shared_ptr<ExternalItemInfo<T>>
-addExternalItem(std::shared_ptr<T> file, QString key,
- QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &map, AddOption option,
- QBasicMutex *mutex)
+ m_implicitImports(defaultImplicitImports()),
+ m_domCreationOptions(domCreationOptions)
{
- if (!file)
- return {};
- auto eInfo = std::make_shared<ExternalItemInfo<T>>(
- file, QDateTime::currentDateTimeUtc());
- {
- QMutexLocker l(mutex);
- auto it = map.find(key);
- if (it != map.end()) {
- switch (option) {
- case AddOption::KeepExisting:
- eInfo = *it;
- break;
- case AddOption::Overwrite:
- map.insert(key, eInfo);
- break;
- }
- } else {
- map.insert(key, eInfo);
- }
- }
- return eInfo;
}
-std::shared_ptr<ExternalItemInfo<QmlFile>> DomEnvironment::addQmlFile(std::shared_ptr<QmlFile> file,
- AddOption options)
+void DomEnvironment::addQmlFile(const std::shared_ptr<QmlFile> &file, AddOption options)
{
- return addExternalItem<QmlFile>(file, file->canonicalFilePath(), m_qmlFileWithPath, options,
- mutex());
+ if (domCreationOptions().testFlag(DomCreationOption::WithSemanticAnalysis)) {
+ const QQmlJSScope::Ptr &handle =
+ semanticAnalysis().m_importer->importFile(file->canonicalFilePath());
+ file->setHandleForPopulation(handle);
+ }
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<QmlDirectory>>
-DomEnvironment::addQmlDirectory(std::shared_ptr<QmlDirectory> file, AddOption options)
+void DomEnvironment::addQmlDirectory(const std::shared_ptr<QmlDirectory> &file, AddOption options)
{
- return addExternalItem<QmlDirectory>(file, file->canonicalFilePath(), m_qmlDirectoryWithPath,
- options, mutex());
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<QmldirFile>>
-DomEnvironment::addQmldirFile(std::shared_ptr<QmldirFile> file, AddOption options)
+void DomEnvironment::addQmldirFile(const std::shared_ptr<QmldirFile> &file, AddOption options)
{
- return addExternalItem<QmldirFile>(file, file->canonicalFilePath(), m_qmldirFileWithPath,
- options, mutex());
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<QmltypesFile>>
-DomEnvironment::addQmltypesFile(std::shared_ptr<QmltypesFile> file, AddOption options)
+void DomEnvironment::addQmltypesFile(const std::shared_ptr<QmltypesFile> &file, AddOption options)
{
- return addExternalItem<QmltypesFile>(file, file->canonicalFilePath(), m_qmltypesFileWithPath,
- options, mutex());
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<JsFile>> DomEnvironment::addJsFile(std::shared_ptr<JsFile> file,
- AddOption options)
+void DomEnvironment::addJsFile(const std::shared_ptr<JsFile> &file, AddOption options)
{
- return addExternalItem<JsFile>(file, file->canonicalFilePath(), m_jsFileWithPath, options,
- mutex());
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<GlobalScope>>
-DomEnvironment::addGlobalScope(std::shared_ptr<GlobalScope> scope, AddOption options)
+void DomEnvironment::addGlobalScope(const std::shared_ptr<GlobalScope> &scope, AddOption options)
{
- return addExternalItem<GlobalScope>(scope, scope->name(), m_globalScopeWithName, options,
- mutex());
+ addExternalItem(scope, scope->name(), options);
}
-bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> validEnvPtr)
+bool DomEnvironment::commitToBase(
+ const DomItem &self, const shared_ptr<DomEnvironment> &validEnvPtr)
{
if (!base())
return false;
@@ -2072,6 +1960,7 @@ bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> vali
}
{
QMutexLocker lBase(base()->mutex()); // be more careful about makeCopy calls with lock?
+ m_base->m_semanticAnalysis = m_semanticAnalysis;
m_base->m_globalScopeWithName.insert(my_globalScopeWithName);
m_base->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
m_base->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
@@ -2099,28 +1988,31 @@ bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> vali
}
}
}
- if (validEnvPtr) {
+ if (validEnvPtr)
+ m_lastValidBase = validEnvPtr;
+ if (m_lastValidBase) {
QMutexLocker lValid(
- validEnvPtr->mutex()); // be more careful about makeCopy calls with lock?
- validEnvPtr->m_globalScopeWithName.insert(my_globalScopeWithName);
- validEnvPtr->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
- validEnvPtr->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
+ m_lastValidBase->mutex()); // be more careful about makeCopy calls with lock?
+ m_base->m_semanticAnalysis = m_semanticAnalysis;
+ m_lastValidBase->m_globalScopeWithName.insert(my_globalScopeWithName);
+ m_lastValidBase->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
+ m_lastValidBase->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
for (auto it = my_qmlFileWithPath.cbegin(), end = my_qmlFileWithPath.cend(); it != end;
++it) {
if (it.value() && it.value()->current && it.value()->current->isValid())
- validEnvPtr->m_qmlFileWithPath.insert(it.key(), it.value());
+ m_lastValidBase->m_qmlFileWithPath.insert(it.key(), it.value());
}
for (auto it = my_jsFileWithPath.cbegin(), end = my_jsFileWithPath.cend(); it != end;
++it) {
if (it.value() && it.value()->current && it.value()->current->isValid())
- validEnvPtr->m_jsFileWithPath.insert(it.key(), it.value());
+ m_lastValidBase->m_jsFileWithPath.insert(it.key(), it.value());
}
- validEnvPtr->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
- validEnvPtr->m_loadInfos.insert(my_loadInfos);
+ m_lastValidBase->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
+ m_lastValidBase->m_loadInfos.insert(my_loadInfos);
for (auto it = my_moduleIndexWithUri.cbegin(), end = my_moduleIndexWithUri.cend();
it != end; ++it) {
QMap<int, shared_ptr<ModuleIndex>> &myVersions =
- validEnvPtr->m_moduleIndexWithUri[it.key()];
+ m_lastValidBase->m_moduleIndexWithUri[it.key()];
for (auto it2 = it.value().cbegin(), end2 = it.value().cend(); it2 != end2; ++it2) {
auto oldV = myVersions.value(it2.key());
DomItem it2Obj = self.copy(it2.value());
@@ -2133,8 +2025,9 @@ bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> vali
return true;
}
-void DomEnvironment::loadPendingDependencies(DomItem &self)
+void DomEnvironment::loadPendingDependencies()
{
+ DomItem self(shared_from_this());
while (true) {
Path elToDo;
std::shared_ptr<LoadInfo> loadInfo;
@@ -2147,7 +2040,7 @@ void DomEnvironment::loadPendingDependencies(DomItem &self)
loadInfo = m_loadInfos.value(elToDo);
}
if (loadInfo) {
- auto cleanup = qScopeGuard([this, elToDo, &self] {
+ auto cleanup = qScopeGuard([this, &elToDo, &self] {
QList<Callback> endCallbacks;
{
QMutexLocker l(mutex());
@@ -2176,12 +2069,12 @@ void DomEnvironment::loadPendingDependencies(DomItem &self)
}
}
-bool DomEnvironment::finishLoadingDependencies(DomItem &self, int waitMSec)
+bool DomEnvironment::finishLoadingDependencies(int waitMSec)
{
bool hasPendingLoads = true;
QDateTime endTime = QDateTime::currentDateTimeUtc().addMSecs(waitMSec);
for (int i = 0; i < waitMSec / 10 + 2; ++i) {
- loadPendingDependencies(self);
+ loadPendingDependencies();
auto lInfos = loadInfos();
auto it = lInfos.cbegin();
auto end = lInfos.cend();
@@ -2204,7 +2097,7 @@ bool DomEnvironment::finishLoadingDependencies(DomItem &self, int waitMSec)
return !hasPendingLoads;
}
-void DomEnvironment::addWorkForLoadInfo(Path elementCanonicalPath)
+void DomEnvironment::addWorkForLoadInfo(const Path &elementCanonicalPath)
{
QMutexLocker l(mutex());
m_loadsWithWork.enqueue(elementCanonicalPath);
@@ -2224,6 +2117,9 @@ void DomEnvironment::setLoadPaths(const QStringList &v)
{
QMutexLocker l(mutex());
m_loadPaths = v;
+
+ if (m_semanticAnalysis)
+ m_semanticAnalysis->setLoadPaths(v);
}
QStringList DomEnvironment::loadPaths() const
@@ -2232,6 +2128,12 @@ QStringList DomEnvironment::loadPaths() const
return m_loadPaths;
}
+QStringList DomEnvironment::qmldirFiles() const
+{
+ QMutexLocker l(mutex());
+ return m_qmldirFileWithPath.keys();
+}
+
QString DomEnvironment::globalScopeName() const
{
return m_globalScopeName;
@@ -2248,7 +2150,7 @@ QList<Import> DomEnvironment::implicitImports() const
return m_implicitImports;
}
-void DomEnvironment::addAllLoadedCallback(DomItem &self, DomTop::Callback c)
+void DomEnvironment::addAllLoadedCallback(const DomItem &self, DomTop::Callback c)
{
if (c) {
bool immediate = false;
@@ -2269,14 +2171,57 @@ void DomEnvironment::clearReferenceCache()
m_referenceCache.clear();
}
-QString ExternalItemInfoBase::canonicalFilePath(DomItem &self) const
+void DomEnvironment::populateFromQmlFile(MutableDomItem &&qmlFile)
+{
+ if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
+ QQmlJSLogger logger; // TODO
+ // the logger filename is used to populate the QQmlJSScope filepath.
+ logger.setFileName(qmlFile.canonicalFilePath());
+
+ auto setupFile = [&qmlFilePtr, &qmlFile, this](auto &&visitor) {
+ Q_UNUSED(this); // note: integrity requires "this" to be in the capture list, while
+ // other compilers complain about "this" being unused in the lambda
+ AST::Node::accept(qmlFilePtr->ast(), visitor);
+ CommentCollector collector(qmlFile);
+ collector.collectComments();
+ };
+
+ if (m_domCreationOptions.testFlag(DomCreationOption::WithSemanticAnalysis)) {
+ auto &analysis = semanticAnalysis();
+ auto scope = analysis.m_importer->importFile(qmlFile.canonicalFilePath());
+ auto v = std::make_unique<QQmlDomAstCreatorWithQQmlJSScope>(scope, qmlFile, &logger,
+ analysis.m_importer.get());
+ v->enableLoadFileLazily(true);
+ v->enableScriptExpressions(m_domCreationOptions.testFlag(DomCreationOption::WithScriptExpressions));
+
+ setupFile(v.get());
+
+ auto typeResolver =
+ std::make_shared<QQmlJSTypeResolver>(analysis.m_importer.get());
+ typeResolver->init(&v->scopeCreator(), nullptr);
+ qmlFilePtr->setTypeResolverWithDependencies(typeResolver,
+ { analysis.m_importer, analysis.m_mapper });
+ } else {
+ auto v = std::make_unique<QQmlDomAstCreator>(qmlFile);
+ v->enableScriptExpressions(
+ m_domCreationOptions.testFlag(DomCreationOption::WithScriptExpressions));
+
+ setupFile(v.get());
+ }
+ } else {
+ qCWarning(domLog) << "populateQmlFile called on non qmlFile";
+ return;
+ }
+}
+
+QString ExternalItemInfoBase::canonicalFilePath(const DomItem &self) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
DomItem currentObj = currentItem(self);
return current->canonicalFilePath(currentObj);
}
-bool ExternalItemInfoBase::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ExternalItemInfoBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
if (!self.dvValueLazyField(visitor, Fields::currentRevision,
[this, &self]() { return currentRevision(self); }))
@@ -2296,38 +2241,38 @@ bool ExternalItemInfoBase::iterateDirectSubpaths(DomItem &self, DirectVisitor vi
return true;
}
-int ExternalItemInfoBase::currentRevision(DomItem &) const
+int ExternalItemInfoBase::currentRevision(const DomItem &) const
{
return currentItem()->revision();
}
-int ExternalItemInfoBase::lastRevision(DomItem &self) const
+int ExternalItemInfoBase::lastRevision(const DomItem &self) const
{
Path p = currentItem()->canonicalPath();
DomItem lastValue = self.universe()[p.mid(1, p.length() - 1)].field(u"revision");
return static_cast<int>(lastValue.value().toInteger(0));
}
-int ExternalItemInfoBase::lastValidRevision(DomItem &self) const
+int ExternalItemInfoBase::lastValidRevision(const DomItem &self) const
{
Path p = currentItem()->canonicalPath();
DomItem lastValidValue = self.universe()[p.mid(1, p.length() - 2)].field(u"validItem").field(u"revision");
return static_cast<int>(lastValidValue.value().toInteger(0));
}
-QString ExternalItemPairBase::canonicalFilePath(DomItem &) const
+QString ExternalItemPairBase::canonicalFilePath(const DomItem &) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
return current->canonicalFilePath();
}
-Path ExternalItemPairBase::canonicalPath(DomItem &) const
+Path ExternalItemPairBase::canonicalPath(const DomItem &) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
return current->canonicalPath().dropTail();
}
-bool ExternalItemPairBase::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ExternalItemPairBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
if (!self.dvValueLazyField(visitor, Fields::currentIsValid,
[this]() { return currentIsValid(); }))
@@ -2349,7 +2294,7 @@ bool ExternalItemPairBase::currentIsValid() const
return currentItem() == validItem();
}
-RefCacheEntry RefCacheEntry::forPath(DomItem &el, Path canonicalPath)
+RefCacheEntry RefCacheEntry::forPath(const DomItem &el, const Path &canonicalPath)
{
DomItem env = el.environment();
std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
@@ -2365,7 +2310,7 @@ RefCacheEntry RefCacheEntry::forPath(DomItem &el, Path canonicalPath)
return cached;
}
-bool RefCacheEntry::addForPath(DomItem &el, Path canonicalPath, const RefCacheEntry &entry,
+bool RefCacheEntry::addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry,
AddOption addOption)
{
DomItem env = el.environment();
diff --git a/src/qmldom/qqmldomtop_p.h b/src/qmldom/qqmldomtop_p.h
index f73a973906..33a921af93 100644
--- a/src/qmldom/qqmldomtop_p.h
+++ b/src/qmldom/qqmldomtop_p.h
@@ -31,43 +31,21 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
namespace QQmlJS {
namespace Dom {
-class QMLDOM_EXPORT ParsingTask {
-public:
- QCborMap toCbor() const {
- return QCborMap(
- {{ QString::fromUtf16(Fields::requestedAt), QCborValue(requestedAt)},
- { QString::fromUtf16(Fields::loadOptions), int(loadOptions)},
- { QString::fromUtf16(Fields::kind), int(kind)},
- { QString::fromUtf16(Fields::canonicalPath), canonicalPath},
- { QString::fromUtf16(Fields::logicalPath), logicalPath},
- { QString::fromUtf16(Fields::contents), contents},
- { QString::fromUtf16(Fields::contentsDate), QCborValue(contentsDate)},
- { QString::fromUtf16(Fields::hasCallback), bool(callback)}});
- }
-
- QDateTime requestedAt;
- LoadOptions loadOptions;
- DomType kind;
- QString canonicalPath;
- QString logicalPath;
- QString contents;
- QDateTime contentsDate;
- std::weak_ptr<DomUniverse> requestingUniverse; // make it a shared_ptr?
- function<void(Path, DomItem &, DomItem &)> callback;
-};
-
class QMLDOM_EXPORT ExternalItemPairBase: public OwningItem { // all access should have the lock of the DomUniverse containing this
Q_DECLARE_TR_FUNCTIONS(ExternalItemPairBase);
public:
constexpr static DomType kindValue = DomType::ExternalItemPair;
DomType kind() const final override { return kindValue; }
- ExternalItemPairBase(QDateTime validExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0,
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
+ ExternalItemPairBase(
+ const QDateTime &validExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ const QDateTime &currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0,
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
: OwningItem(derivedFrom, lastDataUpdateAt),
validExposedAt(validExposedAt),
currentExposedAt(currentExposedAt)
@@ -76,21 +54,21 @@ public:
OwningItem(o), validExposedAt(o.validExposedAt), currentExposedAt(o.currentExposedAt)
{}
virtual std::shared_ptr<ExternalOwningItem> validItem() const = 0;
- virtual DomItem validItem(DomItem &self) const = 0;
+ virtual DomItem validItem(const DomItem &self) const = 0;
virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0;
- virtual DomItem currentItem(DomItem &self) const = 0;
+ virtual DomItem currentItem(const DomItem &self) const = 0;
- QString canonicalFilePath(DomItem &) const final override;
- Path canonicalPath(DomItem &self) const final override;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) final override;
- DomItem field(DomItem &self, QStringView name) const final override
+ QString canonicalFilePath(const DomItem &) const final override;
+ Path canonicalPath(const DomItem &self) const final override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const final override;
+ DomItem field(const DomItem &self, QStringView name) const final override
{
return OwningItem::field(self, name);
}
bool currentIsValid() const;
- std::shared_ptr<ExternalItemPairBase> makeCopy(DomItem &self) const
+ std::shared_ptr<ExternalItemPairBase> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<ExternalItemPairBase>(doCopy(self));
}
@@ -104,9 +82,9 @@ public:
void refreshedDataAt(QDateTime tNew) final override
{
- return OwningItem::refreshedDataAt(tNew);
if (currentItem())
currentItem()->refreshedDataAt(tNew);
+ return OwningItem::refreshedDataAt(tNew);
}
friend class DomUniverse;
@@ -119,7 +97,7 @@ template<class T>
class QMLDOM_EXPORT ExternalItemPair final : public ExternalItemPairBase
{ // all access should have the lock of the DomUniverse containing this
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
return std::make_shared<ExternalItemPair>(*this);
}
@@ -127,11 +105,12 @@ protected:
public:
constexpr static DomType kindValue = DomType::ExternalItemPair;
friend class DomUniverse;
- ExternalItemPair(std::shared_ptr<T> valid = {}, std::shared_ptr<T> current = {},
- QDateTime validExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0,
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
+ ExternalItemPair(
+ const std::shared_ptr<T> &valid = {}, const std::shared_ptr<T> &current = {},
+ const QDateTime &validExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ const QDateTime &currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0,
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
: ExternalItemPairBase(validExposedAt, currentExposedAt, derivedFrom, lastDataUpdateAt),
valid(valid),
current(current)
@@ -141,10 +120,10 @@ public:
{
}
std::shared_ptr<ExternalOwningItem> validItem() const override { return valid; }
- DomItem validItem(DomItem &self) const override { return self.copy(valid); }
+ DomItem validItem(const DomItem &self) const override { return self.copy(valid); }
std::shared_ptr<ExternalOwningItem> currentItem() const override { return current; }
- DomItem currentItem(DomItem &self) const override { return self.copy(current); }
- std::shared_ptr<ExternalItemPair> makeCopy(DomItem &self) const
+ DomItem currentItem(const DomItem &self) const override { return self.copy(current); }
+ std::shared_ptr<ExternalItemPair> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<ExternalItemPair>(doCopy(self));
}
@@ -171,11 +150,11 @@ public:
virtual Path canonicalPath() const = 0;
- Path canonicalPath(DomItem &) const override;
- DomItem containingObject(DomItem &) const override;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
+ Path canonicalPath(const DomItem &) const override;
+ DomItem containingObject(const DomItem &) const override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
template<typename T>
- void setExtraOwningItem(QString fieldName, std::shared_ptr<T> item)
+ void setExtraOwningItem(const QString &fieldName, const std::shared_ptr<T> &item)
{
QMutexLocker l(mutex());
if (!item)
@@ -191,55 +170,55 @@ private:
QMap<QString, OwnerT> m_extraOwningItems;
};
-class QMLDOM_EXPORT DomUniverse final : public DomTop
+class QMLDOM_EXPORT DomUniverse final : public DomTop,
+ public std::enable_shared_from_this<DomUniverse>
{
Q_GADGET
Q_DECLARE_TR_FUNCTIONS(DomUniverse);
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
public:
- enum class Option{
- Default,
- SingleThreaded
- };
- Q_ENUM(Option)
- Q_DECLARE_FLAGS(Options, Option);
constexpr static DomType kindValue = DomType::DomUniverse;
DomType kind() const override { return kindValue; }
static ErrorGroups myErrors();
- DomUniverse(QString universeName, Options options = Option::SingleThreaded);
+ DomUniverse(const QString &universeName);
DomUniverse(const DomUniverse &) = delete;
- static std::shared_ptr<DomUniverse> guaranteeUniverse(std::shared_ptr<DomUniverse> univ);
- static DomItem create(QString universeName, Options options = Option::SingleThreaded);
+ static std::shared_ptr<DomUniverse> guaranteeUniverse(const std::shared_ptr<DomUniverse> &univ);
+ static DomItem create(const QString &universeName);
Path canonicalPath() const override;
using DomTop::canonicalPath;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- std::shared_ptr<DomUniverse> makeCopy(DomItem &self) const
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ std::shared_ptr<DomUniverse> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<DomUniverse>(doCopy(self));
}
- void loadFile(DomItem &self, QString filePath, QString logicalPath, Callback callback,
- LoadOptions loadOptions,
- std::optional<DomType> fileType = std::optional<DomType>());
- void loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath, QString code,
- QDateTime codeDate, Callback callback, LoadOptions loadOptions,
- std::optional<DomType> fileType = std::optional<DomType>());
- void execQueue();
+ // Helper structure reflecting the change in the map once loading && parsing is completed
+ // formerItem - DomItem representing value (ExternalItemPair) existing in the map before the
+ // loading && parsing. Might be empty (if didn't exist / failure) or equal to currentItem
+ // currentItem - DomItem representing current map value
+ struct LoadResult
+ {
+ DomItem formerItem;
+ DomItem currentItem;
+ };
+
+ LoadResult loadFile(const FileToLoad &file, DomType fileType,
+ DomCreationOptions creationOptions = {});
void removePath(const QString &dir);
- std::shared_ptr<ExternalItemPair<GlobalScope>> globalScopeWithName(QString name) const
+ std::shared_ptr<ExternalItemPair<GlobalScope>> globalScopeWithName(const QString &name) const
{
QMutexLocker l(mutex());
return m_globalScopeWithName.value(name);
}
- std::shared_ptr<ExternalItemPair<GlobalScope>> ensureGlobalScopeWithName(QString name)
+ std::shared_ptr<ExternalItemPair<GlobalScope>> ensureGlobalScopeWithName(const QString &name)
{
if (auto current = globalScopeWithName(name))
return current;
@@ -263,7 +242,7 @@ public:
return QSet<QString>(map.keyBegin(), map.keyEnd());
}
- std::shared_ptr<ExternalItemPair<QmlDirectory>> qmlDirectoryWithPath(QString path) const
+ std::shared_ptr<ExternalItemPair<QmlDirectory>> qmlDirectoryWithPath(const QString &path) const
{
QMutexLocker l(mutex());
return m_qmlDirectoryWithPath.value(path);
@@ -278,7 +257,7 @@ public:
return QSet<QString>(map.keyBegin(), map.keyEnd());
}
- std::shared_ptr<ExternalItemPair<QmldirFile>> qmldirFileWithPath(QString path) const
+ std::shared_ptr<ExternalItemPair<QmldirFile>> qmldirFileWithPath(const QString &path) const
{
QMutexLocker l(mutex());
return m_qmldirFileWithPath.value(path);
@@ -293,7 +272,7 @@ public:
return QSet<QString>(map.keyBegin(), map.keyEnd());
}
- std::shared_ptr<ExternalItemPair<QmlFile>> qmlFileWithPath(QString path) const
+ std::shared_ptr<ExternalItemPair<QmlFile>> qmlFileWithPath(const QString &path) const
{
QMutexLocker l(mutex());
return m_qmlFileWithPath.value(path);
@@ -308,7 +287,7 @@ public:
return QSet<QString>(map.keyBegin(), map.keyEnd());
}
- std::shared_ptr<ExternalItemPair<JsFile>> jsFileWithPath(QString path) const
+ std::shared_ptr<ExternalItemPair<JsFile>> jsFileWithPath(const QString &path) const
{
QMutexLocker l(mutex());
return m_jsFileWithPath.value(path);
@@ -323,7 +302,7 @@ public:
return QSet<QString>(map.keyBegin(), map.keyEnd());
}
- std::shared_ptr<ExternalItemPair<QmltypesFile>> qmltypesFileWithPath(QString path) const
+ std::shared_ptr<ExternalItemPair<QmltypesFile>> qmltypesFileWithPath(const QString &path) const
{
QMutexLocker l(mutex());
return m_qmltypesFileWithPath.value(path);
@@ -341,37 +320,145 @@ public:
QString name() const {
return m_name;
}
- Options options() const {
- return m_options;
+
+private:
+ struct ContentWithDate
+ {
+ QString content;
+ QDateTime date;
+ };
+ // contains either Content with the timestamp when it was read or an Error
+ using ReadResult = std::variant<ContentWithDate, ErrorMessage>;
+ ReadResult readFileContent(const QString &canonicalPath) const;
+
+ LoadResult load(const ContentWithDate &codeWithDate, const FileToLoad &file, DomType fType,
+ DomCreationOptions creationOptions = {});
+
+ // contains either Content to be parsed or LoadResult if loading / parsing is not needed
+ using PreloadResult = std::variant<ContentWithDate, LoadResult>;
+ PreloadResult preload(const DomItem &univ, const FileToLoad &file, DomType fType) const;
+
+ std::shared_ptr<QmlFile> parseQmlFile(const QString &code, const FileToLoad &file,
+ const QDateTime &contentDate,
+ DomCreationOptions creationOptions);
+ std::shared_ptr<JsFile> parseJsFile(const QString &code, const FileToLoad &file,
+ const QDateTime &contentDate);
+ std::shared_ptr<ExternalItemPairBase> getPathValueOrNull(DomType fType,
+ const QString &path) const;
+ std::optional<DomItem> getItemIfMostRecent(const DomItem &univ, DomType fType,
+ const QString &path) const;
+ std::optional<DomItem> getItemIfHasSameCode(const DomItem &univ, DomType fType,
+ const QString &canonicalPath,
+ const ContentWithDate &codeWithDate) const;
+ static bool valueHasMostRecentItem(const ExternalItemPairBase *value,
+ const QDateTime &lastModified);
+ static bool valueHasSameContent(const ExternalItemPairBase *value, const QString &content);
+
+ // TODO better name / consider proper public get/set
+ template <typename T>
+ QMap<QString, std::shared_ptr<ExternalItemPair<T>>> &getMutableRefToMap()
+ {
+ Q_ASSERT(!mutex()->tryLock());
+ if constexpr (std::is_same_v<T, QmlDirectory>) {
+ return m_qmlDirectoryWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmldirFile>) {
+ return m_qmldirFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmlFile>) {
+ return m_qmlFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, JsFile>) {
+ return m_jsFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmltypesFile>) {
+ return m_qmltypesFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, GlobalScope>) {
+ return m_globalScopeWithName;
+ }
+ Q_UNREACHABLE();
}
- QQueue<ParsingTask> queue() const {
- QMutexLocker l(mutex());
- return m_queue;
+
+ // Inserts or updates an entry reflecting ExternalItem in the corresponding map
+ // Returns a pair of:
+ // - current ExternalItemPair, current value in the map (might be empty, or equal to curValue)
+ // - new current ExternalItemPair, value in the map after after the execution of this function
+ template <typename T>
+ QPair<std::shared_ptr<ExternalItemPair<T>>, std::shared_ptr<ExternalItemPair<T>>>
+ insertOrUpdateEntry(std::shared_ptr<T> newItem)
+ {
+ std::shared_ptr<ExternalItemPair<T>> curValue;
+ std::shared_ptr<ExternalItemPair<T>> newCurValue;
+ QString canonicalPath = newItem->canonicalFilePath();
+ QDateTime now = QDateTime::currentDateTimeUtc();
+ {
+ QMutexLocker l(mutex());
+ auto &map = getMutableRefToMap<T>();
+ auto it = map.find(canonicalPath);
+ if (it != map.cend() && (*it) && (*it)->current) {
+ curValue = *it;
+ if (valueHasSameContent(curValue.get(), newItem->code())) {
+ // value in the map has same content as newItem, a.k.a. most recent
+ newCurValue = curValue;
+ if (newCurValue->current->lastDataUpdateAt() < newItem->lastDataUpdateAt()) {
+ // update timestamp in the current, as if its content was refreshed by
+ // NewItem
+ newCurValue->current->refreshedDataAt(newItem->lastDataUpdateAt());
+ }
+ } else if (curValue->current->lastDataUpdateAt() > newItem->lastDataUpdateAt()) {
+ // value in the map is more recent than newItem, nothing to update
+ newCurValue = curValue;
+ } else {
+ // perform update with newItem
+ curValue->current = std::move(newItem);
+ curValue->currentExposedAt = now;
+ if (curValue->current->isValid()) {
+ curValue->valid = curValue->current;
+ curValue->validExposedAt = std::move(now);
+ }
+ newCurValue = curValue;
+ }
+ } else {
+ // not found / invalid, just insert
+ newCurValue = std::make_shared<ExternalItemPair<T>>(
+ (newItem->isValid() ? newItem : std::shared_ptr<T>()), newItem, now, now);
+ map.insert(canonicalPath, newCurValue);
+ }
+ }
+ return qMakePair(curValue, newCurValue);
+ }
+
+ // Inserts or updates an entry reflecting ExternalItem in the corresponding map
+ // returns LoadResult reflecting the change made to the map
+ template <typename T>
+ LoadResult insertOrUpdateExternalItem(std::shared_ptr<T> extItem)
+ {
+ auto change = insertOrUpdateEntry<T>(std::move(extItem));
+ DomItem univ(shared_from_this());
+ return { univ.copy(change.first), univ.copy(change.second) };
}
private:
QString m_name;
- Options m_options;
QMap<QString, std::shared_ptr<ExternalItemPair<GlobalScope>>> m_globalScopeWithName;
QMap<QString, std::shared_ptr<ExternalItemPair<QmlDirectory>>> m_qmlDirectoryWithPath;
QMap<QString, std::shared_ptr<ExternalItemPair<QmldirFile>>> m_qmldirFileWithPath;
QMap<QString, std::shared_ptr<ExternalItemPair<QmlFile>>> m_qmlFileWithPath;
QMap<QString, std::shared_ptr<ExternalItemPair<JsFile>>> m_jsFileWithPath;
QMap<QString, std::shared_ptr<ExternalItemPair<QmltypesFile>>> m_qmltypesFileWithPath;
- QQueue<ParsingTask> m_queue;
};
- Q_DECLARE_OPERATORS_FOR_FLAGS(DomUniverse::Options)
-
class QMLDOM_EXPORT ExternalItemInfoBase: public OwningItem {
Q_DECLARE_TR_FUNCTIONS(ExternalItemInfoBase);
public:
constexpr static DomType kindValue = DomType::ExternalItemInfo;
DomType kind() const final override { return kindValue; }
- ExternalItemInfoBase(Path canonicalPath,
- QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0,
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
+ ExternalItemInfoBase(
+ const Path &canonicalPath,
+ const QDateTime &currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0,
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
: OwningItem(derivedFrom, lastDataUpdateAt),
m_canonicalPath(canonicalPath),
m_currentExposedAt(currentExposedAt)
@@ -379,22 +466,22 @@ public:
ExternalItemInfoBase(const ExternalItemInfoBase &o) = default;
virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0;
- virtual DomItem currentItem(DomItem &) const = 0;
+ virtual DomItem currentItem(const DomItem &) const = 0;
- QString canonicalFilePath(DomItem &) const final override;
+ QString canonicalFilePath(const DomItem &) const final override;
Path canonicalPath() const { return m_canonicalPath; }
- Path canonicalPath(DomItem &) const final override { return canonicalPath(); }
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) final override;
- DomItem field(DomItem &self, QStringView name) const final override
+ Path canonicalPath(const DomItem &) const final override { return canonicalPath(); }
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const final override;
+ DomItem field(const DomItem &self, QStringView name) const final override
{
return OwningItem::field(self, name);
}
- int currentRevision(DomItem &self) const;
- int lastRevision(DomItem &self) const;
- int lastValidRevision(DomItem &self) const;
+ int currentRevision(const DomItem &self) const;
+ int lastRevision(const DomItem &self) const;
+ int lastValidRevision(const DomItem &self) const;
- std::shared_ptr<ExternalItemInfoBase> makeCopy(DomItem &self) const
+ std::shared_ptr<ExternalItemInfoBase> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<ExternalItemInfoBase>(doCopy(self));
}
@@ -408,12 +495,12 @@ public:
void refreshedDataAt(QDateTime tNew) final override
{
- return OwningItem::refreshedDataAt(tNew);
if (currentItem())
currentItem()->refreshedDataAt(tNew);
+ return OwningItem::refreshedDataAt(tNew);
}
- void ensureLogicalFilePath(QString path) {
+ void ensureLogicalFilePath(const QString &path) {
QMutexLocker l(mutex());
if (!m_logicalFilePaths.contains(path))
m_logicalFilePaths.append(path);
@@ -446,28 +533,29 @@ template<typename T>
class ExternalItemInfo final : public ExternalItemInfoBase
{
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &) const override
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
{
return std::make_shared<ExternalItemInfo>(*this);
}
public:
constexpr static DomType kindValue = DomType::ExternalItemInfo;
- ExternalItemInfo(std::shared_ptr<T> current = std::shared_ptr<T>(),
- QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- int derivedFrom = 0,
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
+ ExternalItemInfo(
+ const std::shared_ptr<T> &current = std::shared_ptr<T>(),
+ const QDateTime &currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
+ int derivedFrom = 0,
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
: ExternalItemInfoBase(current->canonicalPath().dropTail(), currentExposedAt, derivedFrom,
lastDataUpdateAt),
current(current)
{}
- ExternalItemInfo(QString canonicalPath) : current(new T(canonicalPath)) { }
+ ExternalItemInfo(const QString &canonicalPath) : current(new T(canonicalPath)) { }
ExternalItemInfo(const ExternalItemInfo &o):
ExternalItemInfoBase(o), current(o.current)
{
}
- std::shared_ptr<ExternalItemInfo> makeCopy(DomItem &self) const
+ std::shared_ptr<ExternalItemInfo> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<ExternalItemInfo>(doCopy(self));
}
@@ -475,7 +563,7 @@ public:
std::shared_ptr<ExternalOwningItem> currentItem() const override {
return current;
}
- DomItem currentItem(DomItem &self) const override { return self.copy(current); }
+ DomItem currentItem(const DomItem &self) const override { return self.copy(current); }
std::shared_ptr<T> current;
};
@@ -499,7 +587,7 @@ class QMLDOM_EXPORT LoadInfo final : public OwningItem
Q_DECLARE_TR_FUNCTIONS(LoadInfo);
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
public:
constexpr static DomType kindValue = DomType::LoadInfo;
@@ -513,9 +601,9 @@ public:
Done // fully loaded
};
- LoadInfo(Path elPath = Path(), Status status = Status::NotStarted, int nLoaded = 0,
+ LoadInfo(const Path &elPath = Path(), Status status = Status::NotStarted, int nLoaded = 0,
int derivedFrom = 0,
- QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
+ const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC))
: OwningItem(derivedFrom, lastDataUpdateAt),
m_elementCanonicalPath(elPath),
m_status(status),
@@ -534,23 +622,23 @@ public:
}
}
- Path canonicalPath(DomItem &self) const override;
+ Path canonicalPath(const DomItem &self) const override;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- std::shared_ptr<LoadInfo> makeCopy(DomItem &self) const
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ std::shared_ptr<LoadInfo> makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<LoadInfo>(doCopy(self));
}
- void addError(DomItem &self, ErrorMessage msg) override
+ void addError(const DomItem &self, ErrorMessage &&msg) override
{
- self.path(elementCanonicalPath()).addError(msg);
+ self.path(elementCanonicalPath()).addError(std::move(msg));
}
- void addEndCallback(DomItem &self, std::function<void(Path, DomItem &, DomItem &)> callback);
+ void addEndCallback(const DomItem &self, std::function<void(Path, const DomItem &, const DomItem &)> callback);
- void advanceLoad(DomItem &self);
- void finishedLoadingDep(DomItem &self, const Dependency &d);
- void execEnd(DomItem &self);
+ void advanceLoad(const DomItem &self);
+ void finishedLoadingDep(const DomItem &self, const Dependency &d);
+ void execEnd(const DomItem &self);
Status status() const
{
@@ -595,15 +683,15 @@ public:
}
private:
- void doAddDependencies(DomItem &self);
- void addDependency(DomItem &self, const Dependency &dep);
+ void doAddDependencies(const DomItem &self);
+ void addDependency(const DomItem &self, const Dependency &dep);
Path m_elementCanonicalPath;
Status m_status;
int m_nLoaded;
QQueue<Dependency> m_toDo;
QList<Dependency> m_inProgress;
- QList<std::function<void(Path, DomItem &, DomItem &)>> m_endCallbacks;
+ QList<std::function<void(Path, const DomItem &, const DomItem &)>> m_endCallbacks;
};
enum class EnvLookup { Normal, NoBase, BaseOnly };
@@ -617,20 +705,21 @@ public:
enum class Cached { None, First, All };
Q_ENUM(Cached)
- static RefCacheEntry forPath(DomItem &el, Path canonicalPath);
- static bool addForPath(DomItem &el, Path canonicalPath, const RefCacheEntry &entry,
+ static RefCacheEntry forPath(const DomItem &el, const Path &canonicalPath);
+ static bool addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry,
AddOption addOption = AddOption::KeepExisting);
Cached cached = Cached::None;
QList<Path> canonicalPaths;
};
-class QMLDOM_EXPORT DomEnvironment final : public DomTop
+class QMLDOM_EXPORT DomEnvironment final : public DomTop,
+ public std::enable_shared_from_this<DomEnvironment>
{
Q_GADGET
Q_DECLARE_TR_FUNCTIONS(DomEnvironment);
protected:
- std::shared_ptr<OwningItem> doCopy(DomItem &self) const override;
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override;
public:
enum class Option {
@@ -651,121 +740,336 @@ public:
Path canonicalPath() const override;
using DomTop::canonicalPath;
- bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override;
- DomItem field(DomItem &self, QStringView name) const final override;
+ bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
+ DomItem field(const DomItem &self, QStringView name) const final override;
- std::shared_ptr<DomEnvironment> makeCopy(DomItem &self) const;
+ std::shared_ptr<DomEnvironment> makeCopy(const DomItem &self) const;
- void loadFile(DomItem &self, QString filePath, QString logicalPath, Callback loadCallback,
- Callback directDepsCallback, Callback endCallback, LoadOptions loadOptions,
- std::optional<DomType> fileType = std::optional<DomType>(),
- ErrorHandler h = nullptr);
- void loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath, QString code,
- QDateTime codeDate, Callback loadCallback, Callback directDepsCallback,
- Callback endCallback, LoadOptions loadOptions,
+ void loadFile(const FileToLoad &file, const Callback &callback,
std::optional<DomType> fileType = std::optional<DomType>(),
- ErrorHandler h = nullptr);
- void loadModuleDependency(DomItem &self, QString uri, Version v,
- Callback loadCallback = nullptr, Callback endCallback = nullptr,
- ErrorHandler = nullptr);
- void loadBuiltins(DomItem &self, Callback callback = nullptr, ErrorHandler h = nullptr);
+ const ErrorHandler &h = nullptr /* used only in loadPendingDependencies*/);
+ void loadBuiltins(const Callback &callback = nullptr, const ErrorHandler &h = nullptr);
+ void loadModuleDependency(const QString &uri, Version v, const Callback &callback = nullptr,
+ const ErrorHandler & = nullptr);
+
void removePath(const QString &path);
std::shared_ptr<DomUniverse> universe() const;
- QSet<QString> moduleIndexUris(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
- QSet<int> moduleIndexMajorVersions(DomItem &self, QString uri,
+ QSet<QString> moduleIndexUris(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+ QSet<int> moduleIndexMajorVersions(const DomItem &self, const QString &uri,
EnvLookup lookup = EnvLookup::Normal) const;
- std::shared_ptr<ModuleIndex> moduleIndexWithUri(DomItem &self, QString uri, int majorVersion,
+ std::shared_ptr<ModuleIndex> moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion,
EnvLookup lookup, Changeable changeable,
- ErrorHandler errorHandler = nullptr);
- std::shared_ptr<ModuleIndex> moduleIndexWithUri(DomItem &self, QString uri, int majorVersion,
+ const ErrorHandler &errorHandler = nullptr);
+ std::shared_ptr<ModuleIndex> moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion,
EnvLookup lookup = EnvLookup::Normal) const;
std::shared_ptr<ExternalItemInfo<QmlDirectory>>
- qmlDirectoryWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
- QSet<QString> qmlDirectoryPaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const;
+ qmlDirectoryWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmlDirectoryPaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
std::shared_ptr<ExternalItemInfo<QmldirFile>>
- qmldirFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
- QSet<QString> qmldirFilePaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const;
+ qmldirFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmldirFilePaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
std::shared_ptr<ExternalItemInfoBase>
- qmlDirWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
- QSet<QString> qmlDirPaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const;
+ qmlDirWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmlDirPaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const;
std::shared_ptr<ExternalItemInfo<QmlFile>>
- qmlFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
- QSet<QString> qmlFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+ qmlFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmlFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
std::shared_ptr<ExternalItemInfo<JsFile>>
- jsFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
- QSet<QString> jsFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+ jsFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> jsFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
std::shared_ptr<ExternalItemInfo<QmltypesFile>>
- qmltypesFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const;
- QSet<QString> qmltypesFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+ qmltypesFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const;
+ QSet<QString> qmltypesFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
std::shared_ptr<ExternalItemInfo<GlobalScope>>
- globalScopeWithName(DomItem &self, QString name, EnvLookup lookup = EnvLookup::Normal) const;
+ globalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup = EnvLookup::Normal) const;
std::shared_ptr<ExternalItemInfo<GlobalScope>>
- ensureGlobalScopeWithName(DomItem &self, QString name, EnvLookup lookup = EnvLookup::Normal);
- QSet<QString> globalScopeNames(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
-
- explicit DomEnvironment(QStringList loadPaths, Options options = Option::SingleThreaded,
- std::shared_ptr<DomUniverse> universe = nullptr);
- explicit DomEnvironment(std::shared_ptr<DomEnvironment> parent, QStringList loadPaths,
- Options options = Option::SingleThreaded);
+ ensureGlobalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup = EnvLookup::Normal);
+ QSet<QString> globalScopeNames(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const;
+
+ explicit DomEnvironment(const QStringList &loadPaths, Options options = Option::SingleThreaded,
+ DomCreationOptions domCreationOptions = None,
+ const std::shared_ptr<DomUniverse> &universe = nullptr);
+ explicit DomEnvironment(const std::shared_ptr<DomEnvironment> &parent,
+ const QStringList &loadPaths, Options options = Option::SingleThreaded,
+ DomCreationOptions domCreationOptions = None);
DomEnvironment(const DomEnvironment &o) = delete;
- static DomItem create(QStringList loadPaths, Options options = Option::SingleThreaded,
- DomItem &universe = DomItem::empty);
-
- std::shared_ptr<ExternalItemInfo<QmlFile>>
- addQmlFile(std::shared_ptr<QmlFile> file, AddOption option = AddOption::KeepExisting);
- std::shared_ptr<ExternalItemInfo<QmlDirectory>>
- addQmlDirectory(std::shared_ptr<QmlDirectory> file, AddOption option = AddOption::KeepExisting);
- std::shared_ptr<ExternalItemInfo<QmldirFile>>
- addQmldirFile(std::shared_ptr<QmldirFile> file, AddOption option = AddOption::KeepExisting);
- std::shared_ptr<ExternalItemInfo<QmltypesFile>>
- addQmltypesFile(std::shared_ptr<QmltypesFile> file, AddOption option = AddOption::KeepExisting);
- std::shared_ptr<ExternalItemInfo<JsFile>> addJsFile(std::shared_ptr<JsFile> file,
- AddOption option = AddOption::KeepExisting);
- std::shared_ptr<ExternalItemInfo<GlobalScope>>
- addGlobalScope(std::shared_ptr<GlobalScope> file, AddOption option = AddOption::KeepExisting);
-
- bool commitToBase(DomItem &self, std::shared_ptr<DomEnvironment> validEnv = nullptr);
-
- void addLoadInfo(DomItem &self, std::shared_ptr<LoadInfo> loadInfo);
- std::shared_ptr<LoadInfo> loadInfo(Path path) const;
+ static std::shared_ptr<DomEnvironment>
+ create(const QStringList &loadPaths, Options options = Option::SingleThreaded,
+ DomCreationOptions creationOptions = DomCreationOption::None,
+ const DomItem &universe = DomItem::empty);
+
+ // TODO AddOption can easily be removed later. KeepExisting option only used in one
+ // place which will be removed in https://codereview.qt-project.org/c/qt/qtdeclarative/+/523217
+ void addQmlFile(const std::shared_ptr<QmlFile> &file,
+ AddOption option = AddOption::KeepExisting);
+ void addQmlDirectory(const std::shared_ptr<QmlDirectory> &file,
+ AddOption option = AddOption::KeepExisting);
+ void addQmldirFile(const std::shared_ptr<QmldirFile> &file,
+ AddOption option = AddOption::KeepExisting);
+ void addQmltypesFile(const std::shared_ptr<QmltypesFile> &file,
+ AddOption option = AddOption::KeepExisting);
+ void addJsFile(const std::shared_ptr<JsFile> &file, AddOption option = AddOption::KeepExisting);
+ void addGlobalScope(const std::shared_ptr<GlobalScope> &file,
+ AddOption option = AddOption::KeepExisting);
+
+ bool commitToBase(
+ const DomItem &self, const std::shared_ptr<DomEnvironment> &validEnv = nullptr);
+
+ void addDependenciesToLoad(const Path &path);
+ void addLoadInfo(
+ const DomItem &self, const std::shared_ptr<LoadInfo> &loadInfo);
+ std::shared_ptr<LoadInfo> loadInfo(const Path &path) const;
QList<Path> loadInfoPaths() const;
QHash<Path, std::shared_ptr<LoadInfo>> loadInfos() const;
- void loadPendingDependencies(DomItem &self);
- bool finishLoadingDependencies(DomItem &self, int waitMSec = 30000);
- void addWorkForLoadInfo(Path elementCanonicalPath);
+ void loadPendingDependencies();
+ bool finishLoadingDependencies(int waitMSec = 30000);
+ void addWorkForLoadInfo(const Path &elementCanonicalPath);
Options options() const;
std::shared_ptr<DomEnvironment> base() const;
QStringList loadPaths() const;
+ QStringList qmldirFiles() const;
QString globalScopeName() const;
static QList<Import> defaultImplicitImports();
QList<Import> implicitImports() const;
- void addAllLoadedCallback(DomItem &self, Callback c);
+ void addAllLoadedCallback(const DomItem &self, Callback c);
void clearReferenceCache();
void setLoadPaths(const QStringList &v);
+ // Helper structure reflecting the change in the map once loading / fetching is completed
+ // formerItem - DomItem representing value (ExternalItemInfo) existing in the map before the
+ // loading && parsing. Might be empty (if didn't exist / failure) or equal to currentItem
+ // currentItem - DomItem representing current map value
+ struct LoadResult
+ {
+ DomItem formerItem;
+ DomItem currentItem;
+ };
+ // TODO(QTBUG-121171)
+ template <typename T>
+ LoadResult insertOrUpdateExternalItemInfo(const QString &path, std::shared_ptr<T> extItem)
+ {
+ // maybe in the next revision this all can be just substituted by the addExternalItem
+ DomItem env(shared_from_this());
+ // try to fetch from the current env.
+ if (auto curValue = lookup<T>(path, EnvLookup::NoBase)) {
+ // found in the "initial" env
+ return { env.copy(curValue), env.copy(curValue) };
+ }
+ std::shared_ptr<ExternalItemInfo<T>> newCurValue;
+ // try to fetch from the base env
+ auto valueInBase = lookup<T>(path, EnvLookup::BaseOnly);
+ if (!valueInBase) {
+ // Nothing found. Just create an externalItemInfo which will be inserted
+ newCurValue = std::make_shared<ExternalItemInfo<T>>(std::move(extItem),
+ QDateTime::currentDateTimeUtc());
+ } else {
+ // prepare updated value as a copy of the value from the Base to be inserted
+ newCurValue = valueInBase->makeCopy(env);
+ if (newCurValue->current != extItem) {
+ newCurValue->current = std::move(extItem);
+ newCurValue->setCurrentExposedAt(QDateTime::currentDateTimeUtc());
+ }
+ }
+ // Before inserting new or updated value, check one more time, if ItemInfo is already
+ // present
+ // lookup<> can't be used here because of the data-race
+ {
+ QMutexLocker l(mutex());
+ auto &map = getMutableRefToMap<T>();
+ const auto &it = map.find(path);
+ if (it != map.end())
+ return { env.copy(*it), env.copy(*it) };
+ // otherwise insert
+ map.insert(path, newCurValue);
+ }
+ return { env.copy(valueInBase), env.copy(newCurValue) };
+ }
+
+ template <typename T>
+ void addExternalItemInfo(const DomItem &newExtItem, const Callback &loadCallback,
+ const Callback &endCallback)
+ {
+ // get either Valid "file" from the ExternalItemPair or the current (wip) "file"
+ std::shared_ptr<T> newItemPtr;
+ if (options() & DomEnvironment::Option::KeepValid)
+ newItemPtr = newExtItem.field(Fields::validItem).ownerAs<T>();
+ if (!newItemPtr)
+ newItemPtr = newExtItem.field(Fields::currentItem).ownerAs<T>();
+ Q_ASSERT(newItemPtr && "envCallbackForFile reached without current file");
+
+ auto loadResult = insertOrUpdateExternalItemInfo(newExtItem.canonicalFilePath(),
+ std::move(newItemPtr));
+ Path p = loadResult.currentItem.canonicalPath();
+ {
+ auto depLoad = qScopeGuard([p, this, endCallback] {
+ addDependenciesToLoad(p);
+ // add EndCallback to the queue, which should be called once all dependencies are
+ // loaded
+ if (endCallback) {
+ DomItem env = DomItem(shared_from_this());
+ addAllLoadedCallback(
+ env, [p, endCallback](Path, const DomItem &, const DomItem &env) {
+ DomItem el = env.path(p);
+ endCallback(p, el, el);
+ });
+ }
+ });
+ // call loadCallback
+ if (loadCallback) {
+ loadCallback(p, loadResult.formerItem, loadResult.currentItem);
+ }
+ }
+ }
+ void populateFromQmlFile(MutableDomItem &&qmlFile);
+ DomCreationOptions domCreationOptions() const { return m_domCreationOptions; }
+
private:
friend class RefCacheEntry;
- template<typename T>
+
+ void loadFile(const FileToLoad &file, const Callback &loadCallback, const Callback &endCallback,
+ std::optional<DomType> fileType = std::optional<DomType>(),
+ const ErrorHandler &h = nullptr);
+
+ void loadModuleDependency(const DomItem &self, const QString &uri, Version v,
+ Callback loadCallback = nullptr, Callback endCallback = nullptr,
+ const ErrorHandler & = nullptr);
+
+ template <typename T>
QSet<QString> getStrings(function_ref<QSet<QString>()> getBase, const QMap<QString, T> &selfMap,
EnvLookup lookup) const;
- Callback callbackForQmlDirectory(DomItem &self, Callback loadCallback,
- Callback directDepsCallback, Callback endCallback);
- Callback callbackForQmlFile(DomItem &self, Callback loadCallback, Callback directDepsCallback,
- Callback endCallback);
- Callback callbackForQmltypesFile(DomItem &self, Callback loadCallback,
- Callback directDepsCallback, Callback endCallback);
- Callback callbackForQmldirFile(DomItem &self, Callback loadCallback,
- Callback directDepsCallback, Callback endCallback);
+ template <typename T>
+ const QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &getConstRefToMap() const
+ {
+ Q_ASSERT(!mutex()->tryLock());
+ if constexpr (std::is_same_v<T, GlobalScope>) {
+ return m_globalScopeWithName;
+ }
+ if constexpr (std::is_same_v<T, QmlDirectory>) {
+ return m_qmlDirectoryWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmldirFile>) {
+ return m_qmldirFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmlFile>) {
+ return m_qmlFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, JsFile>) {
+ return m_jsFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmltypesFile>) {
+ return m_qmltypesFileWithPath;
+ }
+ Q_UNREACHABLE();
+ }
+
+ template <typename T>
+ std::shared_ptr<ExternalItemInfo<T>> lookup(const QString &path, EnvLookup options) const
+ {
+ if (options != EnvLookup::BaseOnly) {
+ QMutexLocker l(mutex());
+ const auto &map = getConstRefToMap<T>();
+ const auto &it = map.find(path);
+ if (it != map.end())
+ return *it;
+ }
+ if (options != EnvLookup::NoBase && m_base)
+ return m_base->lookup<T>(path, options);
+ return {};
+ }
+
+ template <typename T>
+ QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &getMutableRefToMap()
+ {
+ Q_ASSERT(!mutex()->tryLock());
+ if constexpr (std::is_same_v<T, QmlDirectory>) {
+ return m_qmlDirectoryWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmldirFile>) {
+ return m_qmldirFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmlFile>) {
+ return m_qmlFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, JsFile>) {
+ return m_jsFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, QmltypesFile>) {
+ return m_qmltypesFileWithPath;
+ }
+ if constexpr (std::is_same_v<T, GlobalScope>) {
+ return m_globalScopeWithName;
+ }
+ Q_UNREACHABLE();
+ }
+
+ template <typename T>
+ void addExternalItem(std::shared_ptr<T> file, QString key, AddOption option)
+ {
+ if (!file)
+ return;
+
+ auto eInfo = std::make_shared<ExternalItemInfo<T>>(file, QDateTime::currentDateTimeUtc());
+ // Lookup helper can't be used here, because it introduces data-race otherwise
+ // (other modifications might happen between the lookup and the insert)
+ QMutexLocker l(mutex());
+ auto &map = getMutableRefToMap<T>();
+ const auto &it = map.find(key);
+ if (it != map.end() && option == AddOption::KeepExisting)
+ return;
+ map.insert(key, eInfo);
+ }
+
+ using FetchResult =
+ QPair<std::shared_ptr<ExternalItemInfoBase>, std::shared_ptr<ExternalItemInfoBase>>;
+ // This function tries to get an Info object about the ExternalItem from the current env
+ // and depending on the result and options tries to fetch it from the Parent env,
+ // saving a copy with an updated timestamp
+ template <typename T>
+ FetchResult fetchFileFromEnvs(const FileToLoad &file)
+ {
+ const auto &path = file.canonicalPath();
+ // lookup only in the current env
+ if (auto value = lookup<T>(path, EnvLookup::NoBase)) {
+ return qMakePair(value, value);
+ }
+ // try to find the file in the base(parent) Env and insert if found
+ if (options() & Option::NoReload) {
+ if (auto baseV = lookup<T>(path, EnvLookup::BaseOnly)) {
+ // Watch out! QTBUG-121171
+ // It's possible between the lookup and creation of curVal, baseV && baseV->current
+ // might have changed
+ // Prepare a value to be inserted as copy of the value from Base
+ auto curV = std::make_shared<ExternalItemInfo<T>>(
+ baseV->current, QDateTime::currentDateTimeUtc(), baseV->revision(),
+ baseV->lastDataUpdateAt());
+ // Lookup one more time if the value was already inserted to the current env
+ // Lookup can't be used here because of the data-race
+ {
+ QMutexLocker l(mutex());
+ auto &map = getMutableRefToMap<T>();
+ const auto &it = map.find(path);
+ if (it != map.end())
+ return qMakePair(*it, *it);
+ // otherwise insert
+ map.insert(path, curV);
+ }
+ return qMakePair(baseV, curV);
+ }
+ }
+ return qMakePair(nullptr, nullptr);
+ }
+
+ Callback getLoadCallbackFor(DomType fileType, const Callback &loadCallback);
std::shared_ptr<ModuleIndex> lookupModuleInEnv(const QString &uri, int majorVersion) const;
// ModuleLookupResult contains the ModuleIndex pointer, and an indicator whether it was found
@@ -776,11 +1080,12 @@ private:
Origin fromBase = FromGlobal;
};
// helper function used by the moduleIndexWithUri methods
- ModuleLookupResult moduleIndexWithUriHelper(DomItem &self, QString uri, int majorVersion,
+ ModuleLookupResult moduleIndexWithUriHelper(const DomItem &self, const QString &uri, int majorVersion,
EnvLookup lookup = EnvLookup::Normal) const;
const Options m_options;
const std::shared_ptr<DomEnvironment> m_base;
+ std::shared_ptr<DomEnvironment> m_lastValidBase;
const std::shared_ptr<DomUniverse> m_universe;
QStringList m_loadPaths; // paths for qml
QString m_globalScopeName;
@@ -797,6 +1102,18 @@ private:
QList<Import> m_implicitImports;
QList<Callback> m_allLoadedCallback;
QHash<Path, RefCacheEntry> m_referenceCache;
+ DomCreationOptions m_domCreationOptions;
+
+ struct SemanticAnalysis
+ {
+ SemanticAnalysis(const QStringList &loadPaths);
+ void setLoadPaths(const QStringList &loadPaths);
+
+ std::shared_ptr<QQmlJSResourceFileMapper> m_mapper;
+ std::shared_ptr<QQmlJSImporter> m_importer;
+ };
+ std::optional<SemanticAnalysis> m_semanticAnalysis;
+ SemanticAnalysis &semanticAnalysis();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(DomEnvironment::Options)
diff --git a/src/qmldom/qqmldomtypesreader.cpp b/src/qmldom/qqmldomtypesreader.cpp
index 3f83130234..ae1ec7fb94 100644
--- a/src/qmldom/qqmldomtypesreader.cpp
+++ b/src/qmldom/qqmldomtypesreader.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmldomtypesreader_p.h"
#include "qqmldomelements_p.h"
@@ -9,11 +9,7 @@
#include <QtQml/private/qqmljsparser_p.h>
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsengine_p.h>
-#ifdef QMLDOM_STANDALONE
-# include "qmlcompiler/qqmljstypedescriptionreader_p.h"
-#else
-# include <private/qqmljstypedescriptionreader_p.h>
-#endif
+#include <private/qqmljstypedescriptionreader_p.h>
#include <QtCore/qdir.h>
@@ -24,15 +20,16 @@ namespace Dom {
using namespace QQmlJS::AST;
-static ErrorGroups myParseErrors()
+static ErrorGroups readerParseErrors()
{
static ErrorGroups errs = { { NewErrorGroup("Dom"), NewErrorGroup("QmltypesFile"),
NewErrorGroup("Parsing") } };
return errs;
}
-void QmltypesReader::insertProperty(QQmlJSScope::Ptr jsScope, const QQmlJSMetaProperty &property,
- QMap<int, QmlObject> &objs)
+void QmltypesReader::insertProperty(
+ const QQmlJSScope::ConstPtr &jsScope, const QQmlJSMetaProperty &property,
+ QMap<int, QmlObject> &objs)
{
PropertyDefinition prop;
prop.name = property.propertyName();
@@ -49,7 +46,7 @@ void QmltypesReader::insertProperty(QQmlJSScope::Ptr jsScope, const QQmlJSMetaPr
prop.notify = property.notify();
if (prop.name.isEmpty() || prop.typeName.isEmpty()) {
- addError(myParseErrors()
+ addError(readerParseErrors()
.warning(tr("Property object is missing a name or type script binding."))
.handle());
return;
@@ -63,11 +60,11 @@ void QmltypesReader::insertSignalOrMethod(const QQmlJSMetaMethod &metaMethod,
MethodInfo methodInfo;
// ### confusion between Method and Slot. Method should be removed.
switch (metaMethod.methodType()) {
- case QQmlJSMetaMethod::Method:
- case QQmlJSMetaMethod::Slot:
+ case QQmlJSMetaMethodType::Method:
+ case QQmlJSMetaMethodType::Slot:
methodInfo.methodType = MethodInfo::MethodType::Method;
break;
- case QQmlJSMetaMethod::Signal:
+ case QQmlJSMetaMethodType::Signal:
methodInfo.methodType = MethodInfo::MethodType::Signal;
break;
default:
@@ -86,7 +83,7 @@ void QmltypesReader::insertSignalOrMethod(const QQmlJSMetaMethod &metaMethod,
int revision = metaMethod.revision();
methodInfo.isConstructor = metaMethod.isConstructor();
if (methodInfo.name.isEmpty()) {
- addError(myParseErrors().error(tr("Method or signal is missing a name.")).handle());
+ addError(readerParseErrors().error(tr("Method or signal is missing a name.")).handle());
return;
}
@@ -112,20 +109,26 @@ EnumDecl QmltypesReader::enumFromMetaEnum(const QQmlJSMetaEnum &metaEnum)
return res;
}
-void QmltypesReader::insertComponent(const QQmlJSScope::Ptr &jsScope,
+void QmltypesReader::insertComponent(const QQmlJSScope::ConstPtr &jsScope,
const QList<QQmlJSScope::Export> &exportsList)
{
QmltypesComponent comp;
+ comp.setSemanticScope(jsScope);
QMap<int, QmlObject> objects;
{
bool hasExports = false;
for (const QQmlJSScope::Export &jsE : exportsList) {
int metaRev = jsE.version().toEncodedVersion<int>();
hasExports = true;
- objects.insert(metaRev, QmlObject());
+ QmlObject object;
+ object.setSemanticScope(jsScope);
+ objects.insert(metaRev, object);
+ }
+ if (!hasExports) {
+ QmlObject object;
+ object.setSemanticScope(jsScope);
+ objects.insert(0, object);
}
- if (!hasExports)
- objects.insert(0, QmlObject());
}
bool incrementedPath = false;
QString prototype;
@@ -176,6 +179,7 @@ void QmltypesReader::insertComponent(const QQmlJSScope::Ptr &jsScope,
comp.setValueTypeName(jsScope->valueTypeName());
comp.setAccessSemantics(jsScope->accessSemantics());
comp.setExtensionTypeName(jsScope->extensionTypeName());
+ comp.setExtensionIsJavaScript(jsScope->extensionIsJavaScript());
comp.setExtensionIsNamespace(jsScope->extensionIsNamespace());
Path exportSourcePath = qmltypesFile().canonicalPath();
QMap<int, Path> revToPath;
@@ -193,7 +197,7 @@ void QmltypesReader::insertComponent(const QQmlJSScope::Ptr &jsScope,
while (it != begin) {
--it;
if (it.key() < 0) {
- addError(myParseErrors().error(
+ addError(readerParseErrors().error(
tr("negative meta revision %1 not supported").arg(it.key())));
}
revToPath.insert(it.key(), compPath.field(Fields::objects).index(objectIndex));
@@ -219,6 +223,7 @@ void QmltypesReader::insertComponent(const QQmlJSScope::Ptr &jsScope,
Export e;
e.uri = jsE.package();
e.typeName = jsE.type();
+ e.isSingleton = jsScope->isSingleton();
e.version = Version((v.hasMajorVersion() ? v.majorVersion() : Version::Latest),
(v.hasMinorVersion() ? v.minorVersion() : Version::Latest));
e.typePath = revToPath.value(metaRev);
@@ -230,7 +235,7 @@ void QmltypesReader::insertComponent(const QQmlJSScope::Ptr &jsScope,
}
if (comp.name().isEmpty()) {
- addError(myParseErrors()
+ addError(readerParseErrors()
.error(tr("Component definition is missing a name binding."))
.handle());
return;
@@ -246,14 +251,14 @@ bool QmltypesReader::parse()
qmltypesFilePtr()->code());
QStringList dependencies;
QList<QQmlJSExportedScope> objects;
- m_isValid = reader(&objects, &dependencies);
+ const bool isValid = reader(&objects, &dependencies);
for (const auto &obj : std::as_const(objects))
insertComponent(obj.scope, obj.exports);
- qmltypesFilePtr()->setIsValid(m_isValid);
- return m_isValid;
+ qmltypesFilePtr()->setIsValid(isValid);
+ return isValid;
}
-void QmltypesReader::addError(ErrorMessage message)
+void QmltypesReader::addError(ErrorMessage &&message)
{
if (message.file.isEmpty())
message.file = qmltypesFile().canonicalFilePath();
diff --git a/src/qmldom/qqmldomtypesreader_p.h b/src/qmldom/qqmldomtypesreader_p.h
index 7794ed16ff..dda0b8f5b3 100644
--- a/src/qmldom/qqmldomtypesreader_p.h
+++ b/src/qmldom/qqmldomtypesreader_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQMLDOMTYPESREADER_H
#define QQMLDOMTYPESREADER_H
@@ -20,13 +20,9 @@
// for Q_DECLARE_TR_FUNCTIONS
#include <QtCore/qcoreapplication.h>
-#ifdef QMLDOM_STANDALONE
-# include "qmlcompiler/qqmljsmetatypes_p.h"
-# include "qmlcompiler/qqmljsscope_p.h"
-#else
-# include <private/qqmljsmetatypes_p.h>
-# include <private/qqmljsscope_p.h>
-#endif
+#include <private/qqmljsmetatypes_p.h>
+#include <private/qqmljsscope_p.h>
+
QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -36,7 +32,7 @@ class QmltypesReader
{
Q_DECLARE_TR_FUNCTIONS(TypeDescriptionReader)
public:
- explicit QmltypesReader(DomItem qmltypesFile)
+ explicit QmltypesReader(const DomItem &qmltypesFile)
: m_qmltypesFilePtr(qmltypesFile.ownerAs<QmltypesFile>()), m_qmltypesFile(qmltypesFile)
{
}
@@ -44,12 +40,12 @@ public:
bool parse();
// static void read
private:
- void addError(ErrorMessage message);
+ void addError(ErrorMessage &&message);
- void insertProperty(QQmlJSScope::Ptr jsScope, const QQmlJSMetaProperty &property,
+ void insertProperty(const QQmlJSScope::ConstPtr &jsScope, const QQmlJSMetaProperty &property,
QMap<int, QmlObject> &objs);
void insertSignalOrMethod(const QQmlJSMetaMethod &metaMethod, QMap<int, QmlObject> &objs);
- void insertComponent(const QQmlJSScope::Ptr &jsScope,
+ void insertComponent(const QQmlJSScope::ConstPtr &jsScope,
const QList<QQmlJSScope::Export> &exportsList);
EnumDecl enumFromMetaEnum(const QQmlJSMetaEnum &metaEnum);
@@ -57,11 +53,10 @@ private:
DomItem &qmltypesFile() { return m_qmltypesFile; }
ErrorHandler handler()
{
- return [this](ErrorMessage m) { this->addError(m); };
+ return [this](const ErrorMessage &m) { this->addError(ErrorMessage(m)); };
}
private:
- bool m_isValid;
std::shared_ptr<QmltypesFile> m_qmltypesFilePtr;
DomItem m_qmltypesFile;
Path m_currentPath;
diff --git a/src/qmldom/standalone/CMakeLists.txt b/src/qmldom/standalone/CMakeLists.txt
deleted file mode 100644
index 68def86a20..0000000000
--- a/src/qmldom/standalone/CMakeLists.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-
-include(../../../.cmake.conf)
-project(standalone_qmldom LANGUAGES CXX VERSION "${QT_REPO_MODULE_VERSION}")
-option(QMLDOM_EXTERNAL_BUILD "If the build is against an external Qt, and not tested inside a build of this Qt" ON)
-
-if (QMLDOM_EXTERNAL_BUILD)
-find_package(Qt6 COMPONENTS Core Qml)
-set(CMAKE_AUTOMOC ON)
-endif()
-
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-if(MSVC)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
-elseif (MINGW)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
-endif()
-
-
-add_library(qmldomlib
- STATIC
- private/qtqmlcompilerexports_p.h
- ../../qmlcompiler/qcoloroutput.cpp ../../qmlcompiler/qcoloroutput_p.h
- ../../qmlcompiler/qdeferredpointer_p.h
- ../../qmlcompiler/qqmljsannotation.cpp ../../qmlcompiler/qqmljsannotation_p.h
- ../../qmlcompiler/qqmljsimporter.cpp ../../qmlcompiler/qqmljsimporter_p.h
- ../../qmlcompiler/qqmljsimportvisitor.cpp ../../qmlcompiler/qqmljsimportvisitor_p.h
- ../../qmlcompiler/qqmljslogger.cpp ../../qmlcompiler/qqmljslogger_p.h
- ../../qmlcompiler/qqmljsmetatypes.cpp ../../qmlcompiler/qqmljsmetatypes_p.h
- ../../qmlcompiler/qqmljsresourcefilemapper.cpp ../../qmlcompiler/qqmljsresourcefilemapper_p.h
- ../../qmlcompiler/qqmljsscope.cpp ../../qmlcompiler/qqmljsscope_p.h
- ../../qmlcompiler/qqmljsscopesbyid_p.h
- ../../qmlcompiler/qqmljsutils.cpp ../../qmlcompiler/qqmljsutils_p.h
- ../../qmlcompiler/qqmljstypedescriptionreader.cpp ../../qmlcompiler/qqmljstypedescriptionreader_p.h
- ../../qmlcompiler/qqmljstypereader.cpp ../../qmlcompiler/qqmljstypereader_p.h
- ../../qmlcompiler/qqmljstyperesolver.cpp ../../qmlcompiler/qqmljstyperesolver_p.h
- ../../qmlcompiler/qqmljsregistercontent.cpp ../../qmlcompiler/qqmljsregistercontent_p.h
- ../qqmldom_fwd_p.h
- ../qqmldom_global.h
- ../qqmldomastcreator.cpp ../qqmldomastcreator_p.h
- ../qqmldomastdumper.cpp ../qqmldomastdumper_p.h
- ../qqmldomattachedinfo.cpp ../qqmldomattachedinfo_p.h
- ../qqmldomcodeformatter.cpp ../qqmldomcodeformatter_p.h
- ../qqmldomcomments.cpp ../qqmldomcomments_p.h
- ../qqmldomcompare.cpp ../qqmldomcompare_p.h
- ../qqmldomconstants_p.h
- ../qqmldomelements.cpp ../qqmldomelements_p.h
- ../qqmldomerrormessage.cpp ../qqmldomerrormessage_p.h
- ../qqmldomexternalitems.cpp ../qqmldomexternalitems_p.h
- ../qqmldomfieldfilter.cpp ../qqmldomfieldfilter_p.h
- ../qqmldomfilewriter.cpp ../qqmldomfilewriter_p.h
- ../qqmldomfunctionref_p.h
- ../qqmldomitem.cpp ../qqmldomitem_p.h
- ../qqmldomlinewriter.cpp ../qqmldomlinewriter_p.h
- ../qqmldommock.cpp ../qqmldommock_p.h
- ../qqmldommoduleindex.cpp ../qqmldommoduleindex_p.h
- ../qqmldomoutwriter.cpp ../qqmldomoutwriter_p.h
- ../qqmldompath.cpp ../qqmldompath_p.h
- ../qqmldomreformatter.cpp ../qqmldomreformatter_p.h
- ../qqmldomscanner.cpp ../qqmldomscanner_p.h
- ../qqmldomstringdumper.cpp ../qqmldomstringdumper_p.h
- ../qqmldomtop.cpp ../qqmldomtop_p.h
- ../qqmldomtypesreader.cpp ../qqmldomtypesreader_p.h
-)
-if(NOT QMLDOM_EXTERNAL_BUILD)
- qt_autogen_tools_initial_setup(qmldomlib)
-endif()
-
-target_compile_definitions(qmldomlib PUBLIC QMLDOM_STANDALONE)
-
-target_include_directories(qmldomlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../..)
-target_link_libraries(qmldomlib
- PUBLIC
- Qt::CorePrivate
- Qt::QmlPrivate
-)
diff --git a/src/qmldom/standalone/private/qtqmlcompilerexports_p.h b/src/qmldom/standalone/private/qtqmlcompilerexports_p.h
deleted file mode 100644
index a8b6d86456..0000000000
--- a/src/qmldom/standalone/private/qtqmlcompilerexports_p.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "../qqmldom_global.h"
-#ifndef QTQMLCOMPILEREXPORTS_P_H
-#define QTQMLCOMPILEREXPORTS_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#define Q_QMLCOMPILER_PRIVATE_EXPORT QMLDOM_EXPORT
-#endif
diff --git a/src/qmlintegration/CMakeLists.txt b/src/qmlintegration/CMakeLists.txt
index 0dd54f53bd..d0f63d25b6 100644
--- a/src/qmlintegration/CMakeLists.txt
+++ b/src/qmlintegration/CMakeLists.txt
@@ -7,4 +7,5 @@ qt_internal_add_module(QmlIntegration
qqmlintegration.h
LIBRARIES
Qt::Core
+ NO_GENERATE_CPP_EXPORTS
)
diff --git a/src/qmlintegration/qqmlintegration.h b/src/qmlintegration/qqmlintegration.h
index d7c9f9968d..f1a990a79c 100644
--- a/src/qmlintegration/qqmlintegration.h
+++ b/src/qmlintegration/qqmlintegration.h
@@ -18,6 +18,12 @@ namespace QQmlPrivate {
struct QmlExtendedNamespace;
template<class, class>
struct QmlUncreatable;
+ template<class, class>
+ struct QmlAnonymous;
+ template<class, class>
+ struct QmlSequence;
+ template<class, class>
+ struct QmlResolved;
}
template <typename T> class QList;
@@ -39,13 +45,17 @@ QT_END_NAMESPACE
#define QML_ELEMENT \
Q_CLASSINFO("QML.Element", "auto")
+
#define QML_ANONYMOUS \
Q_CLASSINFO("QML.Element", "anonymous") \
- enum class QmlIsUncreatable {yes = true}; \
- template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlUncreatable; \
+ enum class QmlIsAnonymous{yes = true}; \
+ template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlAnonymous; \
+ QT_WARNING_PUSH \
+ QT_WARNING_DISABLE_GCC("-Wredundant-decls") \
template<typename... Args> \
friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
- inline constexpr void qt_qmlMarker_uncreatable() {}
+ QT_WARNING_POP \
+ inline constexpr void qt_qmlMarker_anonymous() {}
#define QML_NAMED_ELEMENT(NAME) \
Q_CLASSINFO("QML.Element", #NAME)
@@ -55,8 +65,11 @@ QT_END_NAMESPACE
Q_CLASSINFO("QML.UncreatableReason", REASON) \
enum class QmlIsUncreatable {yes = true}; \
template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlUncreatable; \
+ QT_WARNING_PUSH \
+ QT_WARNING_DISABLE_GCC("-Wredundant-decls") \
template<typename... Args> \
friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
+ QT_WARNING_POP \
inline constexpr void qt_qmlMarker_uncreatable() {}
#define QML_VALUE_TYPE(NAME) \
@@ -74,8 +87,11 @@ QT_END_NAMESPACE
Q_CLASSINFO("QML.Singleton", "true") \
enum class QmlIsSingleton {yes = true}; \
template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlSingleton; \
+ QT_WARNING_PUSH \
+ QT_WARNING_DISABLE_GCC("-Wredundant-decls") \
template<typename... Args> \
friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
+ QT_WARNING_POP \
inline constexpr void qt_qmlMarker_singleton() {}
#define QML_ADDED_IN_MINOR_VERSION(VERSION) \
@@ -104,8 +120,11 @@ QT_END_NAMESPACE
Q_CLASSINFO("QML.Extended", #EXTENDED_TYPE) \
using QmlExtendedType = EXTENDED_TYPE; \
template<class, class> friend struct QML_PRIVATE_NAMESPACE::QmlExtended; \
+ QT_WARNING_PUSH \
+ QT_WARNING_DISABLE_GCC("-Wredundant-decls") \
template<typename... Args> \
friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
+ QT_WARNING_POP \
inline constexpr void qt_qmlMarker_extended() {}
#define QML_EXTENDED_NAMESPACE(EXTENDED_NAMESPACE) \
@@ -113,8 +132,11 @@ QT_END_NAMESPACE
Q_CLASSINFO("QML.ExtensionIsNamespace", "true") \
static constexpr const QMetaObject *qmlExtendedNamespace() { return &EXTENDED_NAMESPACE::staticMetaObject; } \
template<class, class> friend struct QML_PRIVATE_NAMESPACE::QmlExtendedNamespace; \
+ QT_WARNING_PUSH \
+ QT_WARNING_DISABLE_GCC("-Wredundant-decls") \
template<typename... Args> \
friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
+ QT_WARNING_POP \
inline constexpr void qt_qmlMarker_extendedNamespace() {}
#define QML_NAMESPACE_EXTENDED(EXTENDED_NAMESPACE) \
@@ -124,8 +146,11 @@ QT_END_NAMESPACE
Q_CLASSINFO("QML.Element", "anonymous") \
enum class QmlIsInterface {yes = true}; \
template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlInterface; \
+ QT_WARNING_PUSH \
+ QT_WARNING_DISABLE_GCC("-Wredundant-decls") \
template<typename... Args> \
friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
+ QT_WARNING_POP \
inline constexpr void qt_qmlMarker_interface() {}
#define QML_IMPLEMENTS_INTERFACES(INTERFACES) \
@@ -138,11 +163,33 @@ QT_END_NAMESPACE
using QmlSequenceValueType = VALUE_TYPE; \
enum class QmlIsSequence {yes = true}; \
template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlSequence; \
+ QT_WARNING_PUSH \
+ QT_WARNING_DISABLE_GCC("-Wredundant-decls") \
template<typename... Args> \
friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
+ QT_WARNING_POP \
inline constexpr void qt_qmlMarker_sequence() {}
#define QML_UNAVAILABLE \
QML_FOREIGN(QQmlTypeNotAvailable)
+#define QML_FOREIGN(FOREIGN_TYPE) \
+ Q_CLASSINFO("QML.Foreign", #FOREIGN_TYPE) \
+ using QmlForeignType = FOREIGN_TYPE; \
+ template<class, class> friend struct QML_PRIVATE_NAMESPACE::QmlResolved; \
+ QT_WARNING_PUSH \
+ QT_WARNING_DISABLE_GCC("-Wredundant-decls") \
+ template<typename... Args> \
+ friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *); \
+ QT_WARNING_POP \
+ inline constexpr void qt_qmlMarker_foreign() {}
+
+#define QML_FOREIGN_NAMESPACE(FOREIGN_NAMESPACE) \
+ Q_CLASSINFO("QML.Foreign", #FOREIGN_NAMESPACE) \
+ Q_CLASSINFO("QML.ForeignIsNamespace", "true") \
+ inline constexpr void qt_qmlMarker_foreign() {}
+
+#define QML_CUSTOMPARSER \
+ Q_CLASSINFO("QML.HasCustomParser", "true")
+
#endif
diff --git a/src/qmllocalstorage/CMakeLists.txt b/src/qmllocalstorage/CMakeLists.txt
index 63ecdcec29..8a67f14766 100644
--- a/src/qmllocalstorage/CMakeLists.txt
+++ b/src/qmllocalstorage/CMakeLists.txt
@@ -16,5 +16,4 @@ qt_internal_add_qml_module(QmlLocalStorage
Qt::QmlPrivate
Qt::Sql
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
diff --git a/src/qmllocalstorage/qqmllocalstorage.cpp b/src/qmllocalstorage/qqmllocalstorage.cpp
index e3b0159c09..bed86c141e 100644
--- a/src/qmllocalstorage/qqmllocalstorage.cpp
+++ b/src/qmllocalstorage/qqmllocalstorage.cpp
@@ -682,7 +682,7 @@ Returns the created database object.
*/
-void QQmlLocalStorage::openDatabaseSync(QQmlV4Function *args)
+void QQmlLocalStorage::openDatabaseSync(QQmlV4FunctionPtr args)
{
#if QT_CONFIG(settings)
QV4::Scope scope(args->v4engine());
@@ -735,8 +735,8 @@ void QQmlLocalStorage::openDatabaseSync(QQmlV4Function *args)
database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
database.setDatabaseName(basename+QLatin1String(".sqlite"));
}
- if (!database.isOpen())
- database.open();
+ if (!database.isOpen() && !database.open())
+ V4THROW_SQL2(SQLEXCEPTION_DATABASE_ERR, QQmlEngine::tr("SQL: Cannot open database"));
}
QV4::Scoped<QQmlSqlDatabaseWrapper> db(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
diff --git a/src/qmllocalstorage/qqmllocalstorage_p.h b/src/qmllocalstorage/qqmllocalstorage_p.h
index 94d46d5fa6..510ee78dbe 100644
--- a/src/qmllocalstorage/qqmllocalstorage_p.h
+++ b/src/qmllocalstorage/qqmllocalstorage_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
-class Q_QMLLOCALSTORAGE_PRIVATE_EXPORT QQmlLocalStorage : public QObject
+class Q_QMLLOCALSTORAGE_EXPORT QQmlLocalStorage : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(LocalStorage)
@@ -34,7 +34,7 @@ public:
QQmlLocalStorage(QObject *parent = nullptr) : QObject(parent) {}
~QQmlLocalStorage() override = default;
- Q_INVOKABLE void openDatabaseSync(QQmlV4Function* args);
+ Q_INVOKABLE void openDatabaseSync(QQmlV4FunctionPtr args);
};
QT_END_NAMESPACE
diff --git a/src/qmllocalstorage/qqmllocalstorageglobal_p.h b/src/qmllocalstorage/qqmllocalstorageglobal_p.h
index 0362f9955c..9f36a1f367 100644
--- a/src/qmllocalstorage/qqmllocalstorageglobal_p.h
+++ b/src/qmllocalstorage/qqmllocalstorageglobal_p.h
@@ -16,6 +16,6 @@
//
#include <QtCore/qglobal.h>
-#include <QtQmlLocalStorage/private/qtqmllocalstorageexports_p.h>
+#include <QtQmlLocalStorage/qtqmllocalstorageexports.h>
#endif // QQMLLOCALSTORAGEGLOBAL_P_H
diff --git a/src/qmlls/CMakeLists.txt b/src/qmlls/CMakeLists.txt
new file mode 100644
index 0000000000..9476bb847e
--- /dev/null
+++ b/src/qmlls/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_module(QmlLSPrivate
+ STATIC
+ INTERNAL_MODULE
+ PLUGIN_TYPES qmlls
+ SOURCES
+ qlspcustomtypes_p.h
+ qlanguageserver_p.h qlanguageserver_p.h qlanguageserver.cpp
+ qqmllanguageserver_p.h qqmllanguageserver.cpp
+ qworkspace.cpp qworkspace_p.h
+ qtextblock_p.h qtextblock.cpp
+ qtextcursor_p.h qtextcursor.cpp
+ qtextdocument.cpp qtextdocument_p.h
+ qqmllintsuggestions_p.h qqmllintsuggestions.cpp
+ qtextsynchronization.cpp qtextsynchronization_p.h
+ qqmlcompletionsupport_p.h qqmlcompletionsupport.cpp
+ qqmlcodemodel_p.h qqmlcodemodel.cpp
+ qqmlbasemodule_p.h
+ qqmlgototypedefinitionsupport_p.h qqmlgototypedefinitionsupport.cpp
+ qqmlformatting_p.h qqmlformatting.cpp
+ qqmlrangeformatting_p.h qqmlrangeformatting.cpp
+ qqmllsutils_p.h qqmllsutils.cpp
+ qqmlfindusagessupport_p.h qqmlfindusagessupport.cpp
+ qqmlgotodefinitionsupport.cpp qqmlgotodefinitionsupport_p.h
+ qqmlrenamesymbolsupport_p.h qqmlrenamesymbolsupport.cpp
+ qqmlcompletioncontextstrings_p.h qqmlcompletioncontextstrings.cpp
+ qqmlhover_p.h qqmlhover.cpp
+ qqmllsplugin_p.h
+ qqmllscompletion.cpp qqmllscompletion_p.h
+ qqmllscompletionplugin.cpp qqmllscompletionplugin_p.h
+ qdochtmlparser_p.h qdochtmlparser.cpp
+ qqmlhighlightsupport_p.h qqmlhighlightsupport.cpp
+ qqmlsemantictokens_p.h qqmlsemantictokens.cpp
+
+ PUBLIC_LIBRARIES
+ Qt::LanguageServerPrivate
+ Qt::CorePrivate
+ Qt::QmlDomPrivate
+ Qt::QmlCompilerPrivate
+ Qt::QmlToolingSettingsPrivate
+ Qt::LanguageServerPrivate
+ GENERATE_CPP_EXPORTS
+ )
diff --git a/src/qmlls/qdochtmlparser.cpp b/src/qmlls/qdochtmlparser.cpp
new file mode 100644
index 0000000000..bba18facb5
--- /dev/null
+++ b/src/qmlls/qdochtmlparser.cpp
@@ -0,0 +1,214 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qdochtmlparser_p.h>
+#include <QtCore/qregularexpression.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace { //anonymous
+
+// An emprical value to avoid too much content
+static constexpr qsizetype firstIndexOfParagraphTag = 400;
+
+// A paragraph can start with <p><i>, or <p><tt>
+// We need smallest value to use QString::indexOf
+static constexpr auto lengthOfSmallestOpeningTag = qsizetype(std::char_traits<char>::length("<p><i>"));
+static constexpr auto lengthOfStartParagraphTag = qsizetype(std::char_traits<char>::length("<p>"));
+static constexpr auto lengthOfEndParagraphTag = qsizetype(std::char_traits<char>::length("</p>"));
+static constexpr auto lengthOfPeriod = qsizetype(std::char_traits<char>::length("."));
+
+QString getContentsByMarks(const QString &html, QString startMark, QString endMark)
+{
+ startMark.prepend("$$$"_L1);
+ endMark.prepend("<!-- @@@"_L1);
+
+ QString contents;
+ qsizetype start = html.indexOf(startMark);
+ if (start != -1) {
+ start = html.indexOf("-->"_L1, start);
+ if (start != -1) {
+ qsizetype end = html.indexOf(endMark, start);
+ if (end != -1) {
+ start += qsizetype(std::char_traits<char>::length("-->"));
+ contents = html.mid(start, end - start);
+ }
+ }
+ }
+ return contents;
+}
+
+
+void stripAllHtml(QString *html)
+{
+ Q_ASSERT(html);
+ html->remove(QRegularExpression("<.*?>"_L1));
+}
+
+/*! \internal
+ \brief Process the string obtained from start mark to end mark.
+ This is duplicated from QtC's Utils::HtmlExtractor, modified on top of it.
+*/
+void processOutput(QString *html)
+{
+ Q_ASSERT(html);
+ if (html->isEmpty())
+ return;
+
+ // Do not write the first paragraph in case it has extra tags below.
+ // <p><i>This is only used on the Maemo platform.</i></p>
+ // or: <p><tt>This is used on Windows only.</tt></p>
+ // or: <p>[Conditional]</p>
+ const auto skipFirstParagraphIfNeeded = [html](qsizetype &index){
+ const bool shouldSkipFirstParagraph = html->indexOf(QLatin1String("<p><i>")) == index ||
+ html->indexOf(QLatin1String("<p><tt>")) == index ||
+ html->indexOf(QLatin1String("<p>[Conditional]</p>")) == index;
+
+ if (shouldSkipFirstParagraph)
+ index = html->indexOf(QLatin1String("<p>"), index + lengthOfSmallestOpeningTag);
+ };
+
+ // Try to get the entire first paragraph, but if one is not found or if its opening
+ // tag is not in the very beginning (using an empirical value as the limit)
+ // the html is cleared out to avoid too much content.
+ qsizetype index = html->indexOf(QLatin1String("<p>"));
+ if (index != -1 && index < firstIndexOfParagraphTag) {
+ skipFirstParagraphIfNeeded(index);
+ index = html->indexOf(QLatin1String("</p>"), index + lengthOfStartParagraphTag);
+ if (index != -1) {
+ // Most paragraphs end with a period, but there are cases without punctuation
+ // and cases like this: <p>This is a description. Example:</p>
+ const auto period = html->lastIndexOf(QLatin1Char('.'), index);
+ if (period != -1) {
+ html->truncate(period + lengthOfPeriod);
+ html->append(QLatin1String("</p>"));
+ } else {
+ html->truncate(index + lengthOfEndParagraphTag);
+ }
+ } else {
+ html->clear();
+ }
+ } else {
+ html->clear();
+ }
+}
+
+}
+
+QDocHtmlExtractor::QDocHtmlExtractor(const QString &code) : m_code{ code }
+{
+}
+
+QString QDocHtmlExtractor::extract(const QDocHtmlExtractor::Element &element, ExtractionMode mode)
+{
+ QString result;
+ switch (element.type) {
+ case ElementType::QmlType:
+ result = parseForQmlType(element.name, mode);
+ break;
+
+ case ElementType::QmlProperty:
+ result = parseForQmlProperty(element.name, mode);
+ break;
+ case ElementType::QmlMethod:
+ case ElementType::QmlSignal:
+ result = parseForQmlMethodOrSignal(element.name, mode);
+ break;
+ default:
+ return {};
+ }
+
+ stripAllHtml(&result);
+
+ // Also remove leading and trailing whitespaces
+ return result.trimmed();
+}
+
+QString QDocHtmlExtractor::parseForQmlType(const QString &element, ExtractionMode mode)
+{
+ QString result;
+ // Get brief description
+ if (mode == QDocHtmlExtractor::ExtractionMode::Simplified) {
+ result = getContentsByMarks(m_code, element + "-brief"_L1 , element);
+ // Remove More...
+ if (!result.isEmpty()) {
+ const auto tailToRemove = "More..."_L1;
+ const auto lastIndex = result.lastIndexOf(tailToRemove);
+ if (lastIndex != -1)
+ result.remove(lastIndex, tailToRemove.length());
+ }
+ } else {
+ result = getContentsByMarks(m_code, element + "-description"_L1, element);
+ // Remove header
+ if (!result.isEmpty()) {
+ const auto headerToRemove = "Detailed Description"_L1;
+ const auto firstIndex = result.indexOf(headerToRemove);
+ if (firstIndex != -1)
+ result.remove(firstIndex, headerToRemove.length());
+ }
+ }
+
+ return result;
+}
+
+QString QDocHtmlExtractor::parseForQmlProperty(const QString &element, ExtractionMode mode)
+{
+ // Qt 5.15 way of finding properties in doc
+ QString startMark = QString::fromLatin1("<a name=\"%1-prop\">").arg(element);
+ qsizetype startIndex = m_code.indexOf(startMark);
+ if (startIndex == -1) {
+ // if not found, try Qt6
+ startMark = QString::fromLatin1(
+ "<td class=\"tblQmlPropNode\"><p>\n<span class=\"name\">%1</span>")
+ .arg(element);
+ startIndex = m_code.indexOf(startMark);
+ if (startIndex == -1)
+ return {};
+ }
+
+ QString contents = m_code.mid(startIndex + startMark.size());
+ startIndex = contents.indexOf(QLatin1String("<div class=\"qmldoc\"><p>"));
+ if (startIndex == -1)
+ return {};
+
+ contents = contents.mid(startIndex);
+ if (mode == ExtractionMode::Simplified)
+ processOutput(&contents);
+ return contents;
+}
+
+QString QDocHtmlExtractor::parseForQmlMethodOrSignal(const QString &functionName, ExtractionMode mode)
+{
+ // the case with <!-- $$$childAt[overload1]$$$childAtrealreal -->
+ QString mark = QString::fromLatin1("$$$%1[overload1]$$$%1").arg(functionName);
+ qsizetype startIndex = m_code.indexOf(mark);
+ if (startIndex != -1) {
+ startIndex = m_code.indexOf("-->"_L1, startIndex + mark.length());
+ if (startIndex == -1)
+ return {};
+ } else {
+ // it could be part of the method list
+ mark = QString::fromLatin1("<span class=\"name\">%1</span>")
+ .arg(functionName);
+ startIndex = m_code.indexOf(mark);
+ if (startIndex != -1)
+ startIndex += mark.length();
+ else
+ return {};
+ }
+
+ startIndex = m_code.indexOf(QLatin1String("<div class=\"qmldoc\"><p>"), startIndex);
+ if (startIndex == -1)
+ return {};
+
+ QString endMark = QString::fromLatin1("<!-- @@@");
+ qsizetype endIndex = m_code.indexOf(endMark, startIndex);
+ QString contents = m_code.mid(startIndex, endIndex);
+ if (mode == ExtractionMode::Simplified)
+ processOutput(&contents);
+ return contents;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qdochtmlparser_p.h b/src/qmlls/qdochtmlparser_p.h
new file mode 100644
index 0000000000..f66fb0e56b
--- /dev/null
+++ b/src/qmlls/qdochtmlparser_p.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QDOCHTMLEXTRACTOR_P_H
+#define QDOCHTMLEXTRACTOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+class QDocHtmlExtractor
+{
+public:
+ enum class ExtractionMode : char { Simplified, Extended };
+ enum class ElementType : char {
+ QmlType,
+ QmlProperty,
+ QmlMethod,
+ QmlSignal
+ };
+
+ struct Element {
+ QString name;
+ ElementType type;
+ };
+
+ QDocHtmlExtractor(const QString &code);
+ QString extract(const Element &element, ExtractionMode extractionMode);
+
+private:
+ QString parseForQmlType(const QString &element, ExtractionMode mode);
+ QString parseForQmlProperty(const QString &element, ExtractionMode mode = ExtractionMode::Simplified);
+ QString parseForQmlMethodOrSignal(const QString &functionName, ExtractionMode mode);
+
+ const QString &m_code;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDOCHTMLEXTRACTOR_P_H
diff --git a/src/qmlls/qlanguageserver.cpp b/src/qmlls/qlanguageserver.cpp
new file mode 100644
index 0000000000..b65873ef5d
--- /dev/null
+++ b/src/qmlls/qlanguageserver.cpp
@@ -0,0 +1,378 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qlanguageserver_p_p.h"
+
+#include <QtLanguageServer/private/qlspnotifysignals_p.h>
+#include <QtJsonRpc/private/qjsonrpcprotocol_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QLspSpecification;
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lspServerLog, "qt.languageserver.server")
+
+QLanguageServerPrivate::QLanguageServerPrivate(const QJsonRpcTransport::DataHandler &h)
+ : protocol(h)
+{
+}
+
+/*!
+\internal
+\class QLanguageServer
+\brief Implements a server for the language server protocol
+
+QLanguageServer is a class that uses the QLanguageServerProtocol to
+provide a server implementation.
+It handles the lifecycle management, and can be extended via
+QLanguageServerModule subclasses.
+
+The language server keeps a strictly monotonically increasing runState that can be queried
+from any thread (and is thus mutex gated), the normal run state is DidInitialize.
+
+The language server also keeps track of the task canceled by the client (or implicitly when
+shutting down, and isRequestCanceled can be called from any thread.
+*/
+
+QLanguageServer::QLanguageServer(const QJsonRpcTransport::DataHandler &h, QObject *parent)
+ : QObject(*new QLanguageServerPrivate(h), parent)
+{
+ Q_D(QLanguageServer);
+ registerMethods(*d->protocol.typedRpc());
+ d->notifySignals.registerHandlers(&d->protocol);
+}
+
+QLanguageServerProtocol *QLanguageServer::protocol()
+{
+ Q_D(QLanguageServer);
+ return &d->protocol;
+}
+
+QLanguageServer::RunStatus QLanguageServer::runStatus() const
+{
+ const Q_D(QLanguageServer);
+ QMutexLocker l(&d->mutex);
+ return d->runStatus;
+}
+
+void QLanguageServer::finishSetup()
+{
+ Q_D(QLanguageServer);
+ RunStatus rStatus;
+ {
+ QMutexLocker l(&d->mutex);
+ rStatus = d->runStatus;
+ if (rStatus == RunStatus::NotSetup)
+ d->runStatus = RunStatus::SettingUp;
+ }
+ if (rStatus != RunStatus::NotSetup) {
+ emit lifecycleError();
+ return;
+ }
+ emit runStatusChanged(RunStatus::SettingUp);
+
+ registerHandlers(&d->protocol);
+ for (auto module : d->modules)
+ module->registerHandlers(this, &d->protocol);
+
+ {
+ QMutexLocker l(&d->mutex);
+ rStatus = d->runStatus;
+ if (rStatus == RunStatus::SettingUp)
+ d->runStatus = RunStatus::DidSetup;
+ }
+ if (rStatus != RunStatus::SettingUp) {
+ emit lifecycleError();
+ return;
+ }
+ emit runStatusChanged(RunStatus::DidSetup);
+}
+
+void QLanguageServer::addServerModule(QLanguageServerModule *serverModule)
+{
+ Q_D(QLanguageServer);
+ Q_ASSERT(serverModule);
+ RunStatus rStatus;
+ {
+ QMutexLocker l(&d->mutex);
+ rStatus = d->runStatus;
+ if (rStatus == RunStatus::NotSetup) {
+ if (d->modules.contains(serverModule->name())) {
+ d->modules.insert(serverModule->name(), serverModule);
+ qCWarning(lspServerLog) << "Duplicate add of QLanguageServerModule named"
+ << serverModule->name() << ", overwriting.";
+ } else {
+ d->modules.insert(serverModule->name(), serverModule);
+ }
+ }
+ }
+ if (rStatus != RunStatus::NotSetup) {
+ qCWarning(lspServerLog) << "Called QLanguageServer::addServerModule after setup";
+ emit lifecycleError();
+ return;
+ }
+}
+
+QLanguageServerModule *QLanguageServer::moduleByName(const QString &n) const
+{
+ const Q_D(QLanguageServer);
+ QMutexLocker l(&d->mutex);
+ return d->modules.value(n);
+}
+
+QLspNotifySignals *QLanguageServer::notifySignals()
+{
+ Q_D(QLanguageServer);
+ return &d->notifySignals;
+}
+
+void QLanguageServer::registerMethods(QJsonRpc::TypedRpc &typedRpc)
+{
+ typedRpc.installMessagePreprocessor(
+ [this](const QJsonDocument &doc, const QJsonParseError &err,
+ const QJsonRpcProtocol::Handler<QJsonRpcProtocol::Response> &responder) {
+ Q_D(QLanguageServer);
+ if (!doc.isObject()) {
+ qCWarning(lspServerLog)
+ << "non object jsonrpc message" << doc << err.errorString();
+ return QJsonRpcProtocol::Processing::Stop;
+ }
+ bool sendErrorResponse = false;
+ RunStatus rState;
+ QJsonValue id = doc.object()[u"id"];
+ {
+ QMutexLocker l(&d->mutex);
+ // the normal case is d->runStatus == RunStatus::DidInitialize
+ if (d->runStatus != RunStatus::DidInitialize) {
+ if (d->runStatus == RunStatus::DidSetup && !doc.isNull()
+ && doc.object()[u"method"].toString()
+ == QString::fromUtf8(
+ QLspSpecification::Requests::InitializeMethod)) {
+ return QJsonRpcProtocol::Processing::Continue;
+ } else if (!doc.isNull()
+ && doc.object()[u"method"].toString()
+ == QString::fromUtf8(
+ QLspSpecification::Notifications::ExitMethod)) {
+ return QJsonRpcProtocol::Processing::Continue;
+ }
+ if (id.isString() || id.isDouble()) {
+ sendErrorResponse = true;
+ rState = d->runStatus;
+ } else {
+ return QJsonRpcProtocol::Processing::Stop;
+ }
+ }
+ }
+ if (!sendErrorResponse) {
+ if (id.isString() || id.isDouble()) {
+ QMutexLocker l(&d->mutex);
+ d->requestsInProgress.insert(id, QRequestInProgress {});
+ }
+ return QJsonRpcProtocol::Processing::Continue;
+ }
+ if (rState == RunStatus::NotSetup || rState == RunStatus::DidSetup)
+ responder(QJsonRpcProtocol::MessageHandler::error(
+ int(QLspSpecification::ErrorCodes::ServerNotInitialized),
+ u"Request on non initialized Language Server (runStatus %1): %2"_s
+ .arg(int(rState))
+ .arg(QString::fromUtf8(doc.toJson()))));
+ else
+ responder(QJsonRpcProtocol::MessageHandler::error(
+ int(QLspSpecification::ErrorCodes::InvalidRequest),
+ u"Method called on stopping Language Server (runStatus %1)"_s.arg(
+ int(rState))));
+ return QJsonRpcProtocol::Processing::Stop;
+ });
+ typedRpc.installOnCloseAction([this](QJsonRpc::TypedResponse::Status,
+ const QJsonRpc::IdType &id, QJsonRpc::TypedRpc &) {
+ Q_D(QLanguageServer);
+ QJsonValue idValue = QTypedJson::toJsonValue(id);
+ bool lastReq;
+ {
+ QMutexLocker l(&d->mutex);
+ d->requestsInProgress.remove(idValue);
+ lastReq = d->runStatus == RunStatus::WaitPending && d->requestsInProgress.size() <= 1;
+ if (lastReq)
+ d->runStatus = RunStatus::Stopping;
+ }
+ if (lastReq)
+ executeShutdown();
+ });
+}
+
+void QLanguageServer::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &serverInfo)
+{
+ Q_D(QLanguageServer);
+ for (auto module : std::as_const(d->modules))
+ module->setupCapabilities(clientInfo, serverInfo);
+}
+
+const QLspSpecification::InitializeParams &QLanguageServer::clientInfo() const
+{
+ const Q_D(QLanguageServer);
+
+ if (int(runStatus()) < int(RunStatus::DidInitialize))
+ qCWarning(lspServerLog) << "asked for Language Server clientInfo before initialization";
+ return d->clientInfo;
+}
+
+const QLspSpecification::InitializeResult &QLanguageServer::serverInfo() const
+{
+ const Q_D(QLanguageServer);
+ if (int(runStatus()) < int(RunStatus::DidInitialize))
+ qCWarning(lspServerLog) << "asked for Language Server serverInfo before initialization";
+ return d->serverInfo;
+}
+
+void QLanguageServer::receiveData(const QByteArray &d)
+{
+ protocol()->receiveData(d);
+}
+
+void QLanguageServer::registerHandlers(QLanguageServerProtocol *protocol)
+{
+ QObject::connect(notifySignals(), &QLspNotifySignals::receivedCancelNotification, this,
+ [this](const QLspSpecification::Notifications::CancelParamsType &params) {
+ Q_D(QLanguageServer);
+ QJsonValue id = QTypedJson::toJsonValue(params.id);
+ QMutexLocker l(&d->mutex);
+ if (d->requestsInProgress.contains(id))
+ d->requestsInProgress[id].canceled = true;
+ else
+ qCWarning(lspServerLog)
+ << "Ignoring cancellation of non in progress request" << id;
+ });
+
+ protocol->registerInitializeRequestHandler(
+ [this](const QByteArray &,
+ const QLspSpecification::Requests::InitializeParamsType &params,
+ QLspSpecification::Responses::InitializeResponseType &&response) {
+ qCDebug(lspServerLog) << "init";
+ Q_D(QLanguageServer);
+ RunStatus rStatus;
+ {
+ QMutexLocker l(&d->mutex);
+ rStatus = d->runStatus;
+ if (rStatus == RunStatus::DidSetup)
+ d->runStatus = RunStatus::Initializing;
+ }
+ if (rStatus != RunStatus::DidSetup) {
+ if (rStatus == RunStatus::NotSetup || rStatus == RunStatus::SettingUp)
+ response.sendErrorResponse(
+ int(QLspSpecification::ErrorCodes::InvalidRequest),
+ u"Initialization request received on non setup language server"_s
+ .toUtf8());
+ else
+ response.sendErrorResponse(
+ int(QLspSpecification::ErrorCodes::InvalidRequest),
+ u"Received multiple initialization requests"_s.toUtf8());
+ emit lifecycleError();
+ return;
+ }
+ emit runStatusChanged(RunStatus::Initializing);
+ d->clientInfo = params;
+ setupCapabilities(d->clientInfo, d->serverInfo);
+ {
+ QMutexLocker l(&d->mutex);
+ d->runStatus = RunStatus::DidInitialize;
+ }
+ emit runStatusChanged(RunStatus::DidInitialize);
+ response.sendResponse(d->serverInfo);
+ });
+
+ QObject::connect(notifySignals(), &QLspNotifySignals::receivedInitializedNotification, this,
+ [this](const QLspSpecification::Notifications::InitializedParamsType &) {
+ Q_D(QLanguageServer);
+ {
+ QMutexLocker l(&d->mutex);
+ d->clientInitialized = true;
+ }
+ emit clientInitialized(this);
+ });
+
+ protocol->registerShutdownRequestHandler(
+ [this](const QByteArray &, const QLspSpecification::Requests::ShutdownParamsType &,
+ QLspSpecification::Responses::ShutdownResponseType &&response) {
+ Q_D(QLanguageServer);
+ RunStatus rStatus;
+ bool shouldExecuteShutdown = false;
+ {
+ QMutexLocker l(&d->mutex);
+ rStatus = d->runStatus;
+ if (rStatus == RunStatus::DidInitialize) {
+ d->shutdownResponse = std::move(response);
+ if (d->requestsInProgress.size() <= 1) {
+ d->runStatus = RunStatus::Stopping;
+ shouldExecuteShutdown = true;
+ } else {
+ d->runStatus = RunStatus::WaitPending;
+ }
+ }
+ }
+ if (rStatus != RunStatus::DidInitialize)
+ emit lifecycleError();
+ else if (shouldExecuteShutdown)
+ executeShutdown();
+ });
+
+ QObject::connect(notifySignals(), &QLspNotifySignals::receivedExitNotification, this,
+ [this](const QLspSpecification::Notifications::ExitParamsType &) {
+ if (runStatus() != RunStatus::Stopped)
+ emit lifecycleError();
+ else
+ emit exit();
+ });
+}
+
+void QLanguageServer::executeShutdown()
+{
+ RunStatus rStatus = runStatus();
+ if (rStatus != RunStatus::Stopping) {
+ emit lifecycleError();
+ return;
+ }
+ emit shutdown();
+ QLspSpecification::Responses::ShutdownResponseType shutdownResponse;
+ {
+ Q_D(QLanguageServer);
+ QMutexLocker l(&d->mutex);
+ rStatus = d->runStatus;
+ if (rStatus == RunStatus::Stopping) {
+ shutdownResponse = std::move(d->shutdownResponse);
+ d->runStatus = RunStatus::Stopped;
+ }
+ }
+ if (rStatus != RunStatus::Stopping)
+ emit lifecycleError();
+ else
+ shutdownResponse.sendResponse(nullptr);
+}
+
+bool QLanguageServer::isRequestCanceled(const QJsonRpc::IdType &id) const
+{
+ const Q_D(QLanguageServer);
+ QJsonValue idVal = QTypedJson::toJsonValue(id);
+ QMutexLocker l(&d->mutex);
+ return d->requestsInProgress.value(idVal).canceled || d->runStatus != RunStatus::DidInitialize;
+}
+
+bool QLanguageServer::isInitialized() const
+{
+ switch (runStatus()) {
+ case RunStatus::NotSetup:
+ case RunStatus::SettingUp:
+ case RunStatus::DidSetup:
+ case RunStatus::Initializing:
+ return false;
+ case RunStatus::DidInitialize:
+ case RunStatus::WaitPending:
+ case RunStatus::Stopping:
+ case RunStatus::Stopped:
+ break;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qlanguageserver_p.h b/src/qmlls/qlanguageserver_p.h
new file mode 100644
index 0000000000..019a059823
--- /dev/null
+++ b/src/qmlls/qlanguageserver_p.h
@@ -0,0 +1,92 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QLANGUAGESERVER_P_H
+#define QLANGUAGESERVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLanguageServer/private/qlanguageserverspec_p.h>
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtLanguageServer/private/qlspnotifysignals_p.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+class QLanguageServer;
+class QLanguageServerPrivate;
+Q_DECLARE_LOGGING_CATEGORY(lspServerLog)
+
+class QLanguageServerModule : public QObject
+{
+ Q_OBJECT
+public:
+ QLanguageServerModule(QObject *parent = nullptr) : QObject(parent) { }
+ virtual QString name() const = 0;
+ virtual void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) = 0;
+ virtual void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) = 0;
+};
+
+class QLanguageServer : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(RunStatus runStatus READ runStatus NOTIFY runStatusChanged)
+ Q_PROPERTY(bool isInitialized READ isInitialized)
+public:
+ QLanguageServer(const QJsonRpcTransport::DataHandler &h, QObject *parent = nullptr);
+ enum class RunStatus {
+ NotSetup,
+ SettingUp,
+ DidSetup,
+ Initializing,
+ DidInitialize, // normal state of execution
+ WaitPending,
+ Stopping,
+ Stopped
+ };
+ Q_ENUM(RunStatus)
+
+ QLanguageServerProtocol *protocol();
+ void finishSetup();
+ void registerHandlers(QLanguageServerProtocol *protocol);
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &serverInfo);
+ void addServerModule(QLanguageServerModule *serverModule);
+ QLanguageServerModule *moduleByName(const QString &n) const;
+ QLspNotifySignals *notifySignals();
+
+ // API
+ RunStatus runStatus() const;
+ bool isInitialized() const;
+ bool isRequestCanceled(const QJsonRpc::IdType &id) const;
+ const QLspSpecification::InitializeParams &clientInfo() const;
+ const QLspSpecification::InitializeResult &serverInfo() const;
+
+public Q_SLOTS:
+ void receiveData(const QByteArray &d);
+Q_SIGNALS:
+ void runStatusChanged(RunStatus);
+ void clientInitialized(QLanguageServer *server);
+ void shutdown();
+ void exit();
+ void lifecycleError();
+
+private:
+ void registerMethods(QJsonRpc::TypedRpc &typedRpc);
+ void executeShutdown();
+ Q_DECLARE_PRIVATE(QLanguageServer)
+};
+
+QT_END_NAMESPACE
+
+#endif // QLANGUAGESERVER_P_H
diff --git a/src/qmlls/qlanguageserver_p_p.h b/src/qmlls/qlanguageserver_p_p.h
new file mode 100644
index 0000000000..40e7a17432
--- /dev/null
+++ b/src/qmlls/qlanguageserver_p_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QLANGUAGESERVER_P_P_H
+#define QLANGUAGESERVER_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtCore/QMutex>
+#include <QtCore/QHash>
+#include <QtCore/private/qobject_p.h>
+#include <QtLanguageServer/private/qlspnotifysignals_p.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QRequestInProgress
+{
+public:
+ bool canceled = false;
+};
+
+class QLanguageServerPrivate : public QObjectPrivate
+{
+public:
+ QLanguageServerPrivate(const QJsonRpcTransport::DataHandler &h);
+ mutable QMutex mutex;
+ // mutex gated, monotonically increasing
+ QLanguageServer::RunStatus runStatus = QLanguageServer::RunStatus::NotSetup;
+ QHash<QJsonValue, QRequestInProgress> requestsInProgress; // mutex gated
+ bool clientInitialized = false; // mutex gated
+ QLspSpecification::InitializeParams clientInfo; // immutable after runStatus > DidInitialize
+ QLspSpecification::InitializeResult serverInfo; // immutable after runStatus > DidInitialize
+ QLspSpecification::Responses::ShutdownResponseType shutdownResponse;
+ QHash<QString, QLanguageServerModule *> modules;
+ QLanguageServerProtocol protocol;
+ QLspNotifySignals notifySignals;
+};
+
+QT_END_NAMESPACE
+#endif // QLANGUAGESERVER_P_P_H
diff --git a/src/qmlls/qlspcustomtypes_p.h b/src/qmlls/qlspcustomtypes_p.h
new file mode 100644
index 0000000000..e299388b79
--- /dev/null
+++ b/src/qmlls/qlspcustomtypes_p.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QLSPCUSTOMTYPES_P_H
+#define QLSPCUSTOMTYPES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLanguageServer/private/qlanguageserverspec_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QLspSpecification {
+
+class UriToBuildDirs
+{
+public:
+ QByteArray baseUri = {};
+ QList<QByteArray> buildDirs = {};
+
+ template<typename W>
+ void walk(W &w)
+ {
+ field(w, "baseUri", baseUri);
+ field(w, "buildDirs", buildDirs);
+ }
+};
+
+namespace Notifications {
+constexpr auto AddBuildDirsMethod = "$/addBuildDirs";
+
+class AddBuildDirsParams
+{
+public:
+ QList<UriToBuildDirs> buildDirsToSet = {};
+
+ template<typename W>
+ void walk(W &w)
+ {
+ field(w, "buildDirsToSet", buildDirsToSet);
+ }
+};
+} // namespace Notifications
+} // namespace QLspSpecification
+
+QT_END_NAMESPACE
+
+#endif // QLSPCUSTOMTYPES_P_H
diff --git a/src/qmlls/qqmlbasemodule_p.h b/src/qmlls/qqmlbasemodule_p.h
new file mode 100644
index 0000000000..47596914cb
--- /dev/null
+++ b/src/qmlls/qqmlbasemodule_p.h
@@ -0,0 +1,267 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLBASEMODULE_P_H
+#define QQMLBASEMODULE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlcodemodel_p.h"
+#include "qqmllsutils_p.h"
+#include <QtQmlDom/private/qqmldom_utils_p.h>
+
+#include <QObject>
+#include <type_traits>
+#include <unordered_map>
+
+template<typename ParametersT, typename ResponseT>
+struct BaseRequest
+{
+ // allow using Parameters and Response type aliases in the
+ // implementations of the different requests.
+ using Parameters = ParametersT;
+ using Response = ResponseT;
+
+ // The version of the code on which the typedefinition request was made.
+ // Request is received: mark it with the current version of the textDocument.
+ // Then, wait for the codemodel to finish creating a snapshot version that is newer or equal to
+ // the textDocument version at request-received-time.
+ int m_minVersion;
+ Parameters m_parameters;
+ Response m_response;
+
+ bool fillFrom(QmlLsp::OpenDocument doc, const Parameters &params, Response &&response);
+};
+
+/*!
+\internal
+\brief This class sends a result or an error when going out of scope.
+
+It has a helper method \c setErrorFrom that sets an error from variant and optionals.
+*/
+
+template<typename Result, typename ResponseCallback>
+struct ResponseScopeGuard
+{
+ Q_DISABLE_COPY_MOVE(ResponseScopeGuard)
+
+ std::variant<Result *, QQmlLSUtilsErrorMessage> m_response;
+ ResponseCallback &m_callback;
+
+ ResponseScopeGuard(Result &results, ResponseCallback &callback)
+ : m_response(&results), m_callback(callback)
+ {
+ }
+
+ // note: discards the current result or error message, if there is any
+ void setError(const QQmlLSUtilsErrorMessage &error) { m_response = error; }
+
+ template<typename... T>
+ bool setErrorFrom(const std::variant<T...> &variant)
+ {
+ static_assert(std::disjunction_v<std::is_same<T, QQmlLSUtilsErrorMessage>...>,
+ "ResponseScopeGuard::setErrorFrom was passed a variant that never contains"
+ " an error message.");
+ if (auto x = std::get_if<QQmlLSUtilsErrorMessage>(&variant)) {
+ setError(*x);
+ return true;
+ }
+ return false;
+ }
+
+ /*!
+ \internal
+ Note: use it as follows:
+ \badcode
+ if (scopeGuard.setErrorFrom(xxx)) {
+ // do early exit
+ }
+ // xxx was not an error, continue
+ \endcode
+ */
+ bool setErrorFrom(const std::optional<QQmlLSUtilsErrorMessage> &error)
+ {
+ if (error) {
+ setError(*error);
+ return true;
+ }
+ return false;
+ }
+
+ ~ResponseScopeGuard()
+ {
+ std::visit(qOverloadedVisitor{ [this](Result *result) { m_callback.sendResponse(*result); },
+ [this](const QQmlLSUtilsErrorMessage &error) {
+ m_callback.sendErrorResponse(error.code,
+ error.message.toUtf8());
+ } },
+ m_response);
+ }
+};
+
+template<typename RequestType>
+struct QQmlBaseModule : public QLanguageServerModule
+{
+ using RequestParameters = typename RequestType::Parameters;
+ using RequestResponse = typename RequestType::Response;
+ using RequestPointer = std::unique_ptr<RequestType>;
+ using RequestPointerArgument = RequestPointer &&;
+ using BaseT = QQmlBaseModule<RequestType>;
+
+ QQmlBaseModule(QmlLsp::QQmlCodeModel *codeModel);
+ ~QQmlBaseModule();
+
+ void requestHandler(const RequestParameters &parameters, RequestResponse &&response);
+ decltype(auto) getRequestHandler();
+ // processes a request in a different thread.
+ virtual void process(RequestPointerArgument toBeProcessed) = 0;
+ std::variant<QList<QQmlLSUtilsItemLocation>, QQmlLSUtilsErrorMessage>
+ itemsForRequest(const RequestPointer &request);
+
+public Q_SLOTS:
+ void updatedSnapshot(const QByteArray &uri);
+
+protected:
+ QMutex m_pending_mutex;
+ std::unordered_multimap<QString, RequestPointer> m_pending;
+ QmlLsp::QQmlCodeModel *m_codeModel;
+};
+
+template<typename Parameters, typename Response>
+bool BaseRequest<Parameters, Response>::fillFrom(QmlLsp::OpenDocument doc, const Parameters &params,
+ Response &&response)
+{
+ Q_UNUSED(doc);
+ m_parameters = params;
+ m_response = std::move(response);
+
+ if (!doc.textDocument) {
+ qDebug() << "Cannot find document in qmlls's codemodel, did you open it before accessing "
+ "it?";
+ return false;
+ }
+
+ {
+ QMutexLocker l(doc.textDocument->mutex());
+ m_minVersion = doc.textDocument->version().value_or(0);
+ }
+ return true;
+}
+
+template<typename RequestType>
+QQmlBaseModule<RequestType>::QQmlBaseModule(QmlLsp::QQmlCodeModel *codeModel)
+ : m_codeModel(codeModel)
+{
+ QObject::connect(m_codeModel, &QmlLsp::QQmlCodeModel::updatedSnapshot, this,
+ &QQmlBaseModule<RequestType>::updatedSnapshot);
+}
+
+template<typename RequestType>
+QQmlBaseModule<RequestType>::~QQmlBaseModule()
+{
+ QMutexLocker l(&m_pending_mutex);
+ m_pending.clear(); // empty the m_pending while the mutex is hold
+}
+
+template<typename RequestType>
+decltype(auto) QQmlBaseModule<RequestType>::getRequestHandler()
+{
+ auto handler = [this](const QByteArray &, const RequestParameters &parameters,
+ RequestResponse &&response) {
+ requestHandler(parameters, std::move(response));
+ };
+ return handler;
+}
+
+template<typename RequestType>
+void QQmlBaseModule<RequestType>::requestHandler(const RequestParameters &parameters,
+ RequestResponse &&response)
+{
+ auto req = std::make_unique<RequestType>();
+ QmlLsp::OpenDocument doc = m_codeModel->openDocumentByUrl(
+ QQmlLSUtils::lspUriToQmlUrl(parameters.textDocument.uri));
+
+ if (!req->fillFrom(doc, parameters, std::move(response))) {
+ req->m_response.sendErrorResponse(0, "Received invalid request", parameters);
+ return;
+ }
+ const int minVersion = req->m_minVersion;
+ {
+ QMutexLocker l(&m_pending_mutex);
+ m_pending.insert({ QString::fromUtf8(req->m_parameters.textDocument.uri), std::move(req) });
+ }
+
+ if (doc.snapshot.docVersion && *doc.snapshot.docVersion >= minVersion)
+ updatedSnapshot(QQmlLSUtils::lspUriToQmlUrl(parameters.textDocument.uri));
+}
+
+template<typename RequestType>
+void QQmlBaseModule<RequestType>::updatedSnapshot(const QByteArray &url)
+{
+ QmlLsp::OpenDocumentSnapshot doc = m_codeModel->snapshotByUrl(url);
+ std::vector<RequestPointer> toCompl;
+ {
+ QMutexLocker l(&m_pending_mutex);
+ for (auto [it, end] = m_pending.equal_range(QString::fromUtf8(url)); it != end;) {
+ if (auto &[key, value] = *it;
+ doc.docVersion && value->m_minVersion <= *doc.docVersion) {
+ toCompl.push_back(std::move(value));
+ it = m_pending.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ for (auto it = toCompl.rbegin(), end = toCompl.rend(); it != end; ++it) {
+ process(std::move(*it));
+ }
+}
+
+template<typename RequestType>
+std::variant<QList<QQmlLSUtilsItemLocation>, QQmlLSUtilsErrorMessage>
+QQmlBaseModule<RequestType>::itemsForRequest(const RequestPointer &request)
+{
+
+ QmlLsp::OpenDocument doc = m_codeModel->openDocumentByUrl(
+ QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri));
+
+ if (!doc.snapshot.validDocVersion || doc.snapshot.validDocVersion != doc.snapshot.docVersion) {
+ return QQmlLSUtilsErrorMessage{ 0,
+ u"Cannot proceed: current QML document is invalid! Fix"
+ u" all the errors in your QML code and try again."_s };
+ }
+
+ QQmlJS::Dom::DomItem file = doc.snapshot.validDoc.fileObject(QQmlJS::Dom::GoTo::MostLikely);
+ // clear reference cache to resolve latest versions (use a local env instead?)
+ if (auto envPtr = file.environment().ownerAs<QQmlJS::Dom::DomEnvironment>())
+ envPtr->clearReferenceCache();
+ if (!file) {
+ return QQmlLSUtilsErrorMessage{
+ 0,
+ u"Could not find file %1 in project."_s.arg(doc.snapshot.doc.toString()),
+ };
+ }
+
+ auto itemsFound = QQmlLSUtils::itemsFromTextLocation(file, request->m_parameters.position.line,
+ request->m_parameters.position.character);
+
+ if (itemsFound.isEmpty()) {
+ return QQmlLSUtilsErrorMessage{
+ 0,
+ u"Could not find any items at given text location."_s,
+ };
+ }
+ return itemsFound;
+}
+
+#endif // QQMLBASEMODULE_P_H
diff --git a/src/qmlls/qqmlcodemodel.cpp b/src/qmlls/qqmlcodemodel.cpp
new file mode 100644
index 0000000000..85d08f89b6
--- /dev/null
+++ b/src/qmlls/qqmlcodemodel.cpp
@@ -0,0 +1,906 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmlcodemodel_p.h"
+#include "qqmllsplugin_p.h"
+#include "qtextdocument_p.h"
+#include "qqmllsutils_p.h"
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qthreadpool.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qdiriterator.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+#include <memory>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+namespace QmlLsp {
+
+Q_LOGGING_CATEGORY(codeModelLog, "qt.languageserver.codemodel")
+
+using namespace QQmlJS::Dom;
+using namespace Qt::StringLiterals;
+
+/*!
+\internal
+\class QQmlCodeModel
+
+The code model offers a view of the current state of the current files, and traks open files.
+All methods are threadsafe, and generally return immutable or threadsafe objects that can be
+worked on from any thread (unless otherwise noted).
+The idea is the let all other operations be as lock free as possible, concentrating all tricky
+synchronization here.
+
+\section2 Global views
+\list
+\li currentEnv() offers a view that contains the latest version of all the loaded files
+\li validEnv() is just like current env but stores only the valid (meaning correctly parsed,
+ not necessarily without errors) version of a file, it is normally a better choice to load the
+ dependencies/symbol information from
+\endlist
+
+\section2 OpenFiles
+\list
+\li snapshotByUrl() returns an OpenDocumentSnapshot of an open document. From it you can get the
+ document, its latest valid version, scope, all connected to a specific version of the document
+ and immutable. The signal updatedSnapshot() is called every time a snapshot changes (also for
+ every partial change: document change, validDocument change, scope change).
+\li openDocumentByUrl() is a lower level and more intrusive access to OpenDocument objects. These
+ contains the current snapshot, and shared pointer to a Utils::TextDocument. This is *always* the
+ current version of the document, and has line by line support.
+ Working on it is more delicate and intrusive, because you have to explicitly acquire its mutex()
+ before *any* read or write/modification to it.
+ It has a version nuber which is supposed to always change and increase.
+ It is mainly used for highlighting/indenting, and is immediately updated when the user edits a
+ document. Its use should be avoided if possible, preferring the snapshots.
+\endlist
+
+\section2 Parallelism/Theading
+Most operations are not parallel and usually take place in the main thread (but are still thread
+safe).
+There are two main task that are executed in parallel: Indexing, and OpenDocumentUpdate.
+Indexing is meant to keep the global view up to date.
+OpenDocumentUpdate keeps the snapshots of the open documents up to date.
+
+There is always a tension between being responsive, using all threads available, and avoid to hog
+too many resources. One can choose different parallelization strategies, we went with a flexiable
+approach.
+We have (private) functions that execute part of the work: indexSome() and openUpdateSome(). These
+do all locking needed, get some work, do it without locks, and at the end update the state of the
+code model. If there is more work, then they return true. Thus while (xxxSome()); works until there
+is no work left.
+
+addDirectoriesToIndex(), the internal addDirectory() and addOpenToUpdate() add more work to do.
+
+indexNeedsUpdate() and openNeedUpdate(), check if there is work to do, and if yes ensure that a
+worker thread (or more) that work on it exist.
+*/
+
+QQmlCodeModel::QQmlCodeModel(QObject *parent, QQmlToolingSettings *settings)
+ : QObject { parent },
+ m_importPaths(QLibraryInfo::path(QLibraryInfo::QmlImportsPath)),
+ m_currentEnv(std::make_shared<DomEnvironment>(
+ m_importPaths, DomEnvironment::Option::SingleThreaded,
+ DomCreationOptions{} | DomCreationOption::WithRecovery
+ | DomCreationOption::WithScriptExpressions
+ | DomCreationOption::WithSemanticAnalysis)),
+ m_validEnv(std::make_shared<DomEnvironment>(
+ m_importPaths, DomEnvironment::Option::SingleThreaded,
+ DomCreationOptions{} | DomCreationOption::WithRecovery
+ | DomCreationOption::WithScriptExpressions
+ | DomCreationOption::WithSemanticAnalysis)),
+ m_settings(settings),
+ m_pluginLoader(QmlLSPluginInterface_iid, u"/qmlls"_s)
+{
+}
+
+/*!
+\internal
+Disable the functionality that uses CMake, and remove the already watched paths if there are some.
+*/
+void QQmlCodeModel::disableCMakeCalls()
+{
+ m_cmakeStatus = DoesNotHaveCMake;
+ m_cppFileWatcher.removePaths(m_cppFileWatcher.files());
+ QObject::disconnect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, nullptr, nullptr);
+}
+
+QQmlCodeModel::~QQmlCodeModel()
+{
+ QObject::disconnect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, nullptr, nullptr);
+ while (true) {
+ bool shouldWait;
+ {
+ QMutexLocker l(&m_mutex);
+ m_state = State::Stopping;
+ m_openDocumentsToUpdate.clear();
+ shouldWait = m_nIndexInProgress != 0 || m_nUpdateInProgress != 0;
+ }
+ if (!shouldWait)
+ break;
+ QThread::yieldCurrentThread();
+ }
+}
+
+OpenDocumentSnapshot QQmlCodeModel::snapshotByUrl(const QByteArray &url)
+{
+ return openDocumentByUrl(url).snapshot;
+}
+
+int QQmlCodeModel::indexEvalProgress() const
+{
+ Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
+ const int dirCost = 10;
+ int costToDo = 1;
+ for (const ToIndex &el : std::as_const(m_toIndex))
+ costToDo += dirCost * el.leftDepth;
+ costToDo += m_indexInProgressCost;
+ return m_indexDoneCost * 100 / (costToDo + m_indexDoneCost);
+}
+
+void QQmlCodeModel::indexStart()
+{
+ Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
+ qCDebug(codeModelLog) << "indexStart";
+}
+
+void QQmlCodeModel::indexEnd()
+{
+ Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
+ qCDebug(codeModelLog) << "indexEnd";
+ m_lastIndexProgress = 0;
+ m_nIndexInProgress = 0;
+ m_toIndex.clear();
+ m_indexInProgressCost = 0;
+ m_indexDoneCost = 0;
+}
+
+void QQmlCodeModel::indexSendProgress(int progress)
+{
+ if (progress <= m_lastIndexProgress)
+ return;
+ m_lastIndexProgress = progress;
+ // ### actually send progress
+}
+
+bool QQmlCodeModel::indexCancelled()
+{
+ QMutexLocker l(&m_mutex);
+ if (m_state == State::Stopping)
+ return true;
+ return false;
+}
+
+void QQmlCodeModel::indexDirectory(const QString &path, int depthLeft)
+{
+ if (indexCancelled())
+ return;
+ QDir dir(path);
+ if (depthLeft > 1) {
+ const QStringList dirs =
+ dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
+ for (const QString &child : dirs)
+ addDirectory(dir.filePath(child), --depthLeft);
+ }
+ const QStringList qmljs =
+ dir.entryList(QStringList({ u"*.qml"_s, u"*.js"_s, u"*.mjs"_s }), QDir::Files);
+ int progress = 0;
+ {
+ QMutexLocker l(&m_mutex);
+ m_indexInProgressCost += qmljs.size();
+ progress = indexEvalProgress();
+ }
+ indexSendProgress(progress);
+ if (qmljs.isEmpty())
+ return;
+ DomItem newCurrent = m_currentEnv.makeCopy(DomItem::CopyOption::EnvConnected).item();
+ for (const QString &file : qmljs) {
+ if (indexCancelled())
+ return;
+ QString fPath = dir.filePath(file);
+ auto newCurrentPtr = newCurrent.ownerAs<DomEnvironment>();
+ FileToLoad fileToLoad = FileToLoad::fromFileSystem(newCurrentPtr, fPath);
+ if (!fileToLoad.canonicalPath().isEmpty()) {
+ newCurrentPtr->loadBuiltins();
+ newCurrentPtr->loadFile(fileToLoad, [](Path, const DomItem &, const DomItem &) {});
+ newCurrentPtr->loadPendingDependencies();
+ newCurrent.commitToBase(m_validEnv.ownerAs<DomEnvironment>());
+ }
+ {
+ QMutexLocker l(&m_mutex);
+ ++m_indexDoneCost;
+ --m_indexInProgressCost;
+ progress = indexEvalProgress();
+ }
+ indexSendProgress(progress);
+ }
+}
+
+void QQmlCodeModel::addDirectoriesToIndex(const QStringList &paths, QLanguageServer *server)
+{
+ Q_UNUSED(server);
+ // ### create progress, &scan in a separate instance
+ const int maxDepth = 5;
+ for (const auto &path : paths)
+ addDirectory(path, maxDepth);
+ indexNeedsUpdate();
+}
+
+void QQmlCodeModel::addDirectory(const QString &path, int depthLeft)
+{
+ if (depthLeft < 1)
+ return;
+ {
+ QMutexLocker l(&m_mutex);
+ for (auto it = m_toIndex.begin(); it != m_toIndex.end();) {
+ if (it->path.startsWith(path)) {
+ if (it->path.size() == path.size())
+ return;
+ if (it->path.at(path.size()) == u'/') {
+ it = m_toIndex.erase(it);
+ continue;
+ }
+ } else if (path.startsWith(it->path) && path.at(it->path.size()) == u'/')
+ return;
+ ++it;
+ }
+ m_toIndex.append({ path, depthLeft });
+ }
+}
+
+void QQmlCodeModel::removeDirectory(const QString &path)
+{
+ {
+ QMutexLocker l(&m_mutex);
+ auto toRemove = [path](const QString &p) {
+ return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
+ };
+ auto it = m_toIndex.begin();
+ auto end = m_toIndex.end();
+ while (it != end) {
+ if (toRemove(it->path))
+ it = m_toIndex.erase(it);
+ else
+ ++it;
+ }
+ }
+ if (auto validEnvPtr = m_validEnv.ownerAs<DomEnvironment>())
+ validEnvPtr->removePath(path);
+ if (auto currentEnvPtr = m_currentEnv.ownerAs<DomEnvironment>())
+ currentEnvPtr->removePath(path);
+}
+
+QString QQmlCodeModel::url2Path(const QByteArray &url, UrlLookup options)
+{
+ QString res;
+ {
+ QMutexLocker l(&m_mutex);
+ res = m_url2path.value(url);
+ }
+ if (!res.isEmpty() && options == UrlLookup::Caching)
+ return res;
+ QUrl qurl(QString::fromUtf8(url));
+ QFileInfo f(qurl.toLocalFile());
+ QString cPath = f.canonicalFilePath();
+ if (cPath.isEmpty())
+ cPath = f.filePath();
+ {
+ QMutexLocker l(&m_mutex);
+ if (!res.isEmpty() && res != cPath)
+ m_path2url.remove(res);
+ m_url2path.insert(url, cPath);
+ m_path2url.insert(cPath, url);
+ }
+ return cPath;
+}
+
+void QQmlCodeModel::newOpenFile(const QByteArray &url, int version, const QString &docText)
+{
+ {
+ QMutexLocker l(&m_mutex);
+ auto &openDoc = m_openDocuments[url];
+ if (!openDoc.textDocument)
+ openDoc.textDocument = std::make_shared<Utils::TextDocument>();
+ QMutexLocker l2(openDoc.textDocument->mutex());
+ openDoc.textDocument->setVersion(version);
+ openDoc.textDocument->setPlainText(docText);
+ }
+ addOpenToUpdate(url);
+ openNeedUpdate();
+}
+
+OpenDocument QQmlCodeModel::openDocumentByUrl(const QByteArray &url)
+{
+ QMutexLocker l(&m_mutex);
+ return m_openDocuments.value(url);
+}
+
+RegisteredSemanticTokens &QQmlCodeModel::registeredTokens()
+{
+ QMutexLocker l(&m_mutex);
+ return m_tokens;
+}
+
+const RegisteredSemanticTokens &QQmlCodeModel::registeredTokens() const
+{
+ QMutexLocker l(&m_mutex);
+ return m_tokens;
+}
+
+void QQmlCodeModel::indexNeedsUpdate()
+{
+ const int maxIndexThreads = 1;
+ {
+ QMutexLocker l(&m_mutex);
+ if (m_toIndex.isEmpty() || m_nIndexInProgress >= maxIndexThreads)
+ return;
+ if (++m_nIndexInProgress == 1)
+ indexStart();
+ }
+ QThreadPool::globalInstance()->start([this]() {
+ while (indexSome()) { }
+ });
+}
+
+bool QQmlCodeModel::indexSome()
+{
+ qCDebug(codeModelLog) << "indexSome";
+ ToIndex toIndex;
+ {
+ QMutexLocker l(&m_mutex);
+ if (m_toIndex.isEmpty()) {
+ if (--m_nIndexInProgress == 0)
+ indexEnd();
+ return false;
+ }
+ toIndex = m_toIndex.last();
+ m_toIndex.removeLast();
+ }
+ bool hasMore = false;
+ {
+ auto guard = qScopeGuard([this, &hasMore]() {
+ QMutexLocker l(&m_mutex);
+ if (m_toIndex.isEmpty()) {
+ if (--m_nIndexInProgress == 0)
+ indexEnd();
+ hasMore = false;
+ } else {
+ hasMore = true;
+ }
+ });
+ indexDirectory(toIndex.path, toIndex.leftDepth);
+ }
+ return hasMore;
+}
+
+void QQmlCodeModel::openNeedUpdate()
+{
+ qCDebug(codeModelLog) << "openNeedUpdate";
+ const int maxIndexThreads = 1;
+ {
+ QMutexLocker l(&m_mutex);
+ if (m_openDocumentsToUpdate.isEmpty() || m_nUpdateInProgress >= maxIndexThreads)
+ return;
+ if (++m_nUpdateInProgress == 1)
+ openUpdateStart();
+ }
+ QThreadPool::globalInstance()->start([this]() {
+ while (openUpdateSome()) { }
+ });
+}
+
+bool QQmlCodeModel::openUpdateSome()
+{
+ qCDebug(codeModelLog) << "openUpdateSome start";
+ QByteArray toUpdate;
+ {
+ QMutexLocker l(&m_mutex);
+ if (m_openDocumentsToUpdate.isEmpty()) {
+ if (--m_nUpdateInProgress == 0)
+ openUpdateEnd();
+ return false;
+ }
+ auto it = m_openDocumentsToUpdate.find(m_lastOpenDocumentUpdated);
+ auto end = m_openDocumentsToUpdate.end();
+ if (it == end)
+ it = m_openDocumentsToUpdate.begin();
+ else if (++it == end)
+ it = m_openDocumentsToUpdate.begin();
+ toUpdate = *it;
+ m_openDocumentsToUpdate.erase(it);
+ }
+ bool hasMore = false;
+ {
+ auto guard = qScopeGuard([this, &hasMore]() {
+ QMutexLocker l(&m_mutex);
+ if (m_openDocumentsToUpdate.isEmpty()) {
+ if (--m_nUpdateInProgress == 0)
+ openUpdateEnd();
+ hasMore = false;
+ } else {
+ hasMore = true;
+ }
+ });
+ openUpdate(toUpdate);
+ }
+ return hasMore;
+}
+
+void QQmlCodeModel::openUpdateStart()
+{
+ qCDebug(codeModelLog) << "openUpdateStart";
+}
+
+void QQmlCodeModel::openUpdateEnd()
+{
+ qCDebug(codeModelLog) << "openUpdateEnd";
+}
+
+/*!
+\internal
+Performs initialization for m_cmakeStatus, including testing for CMake on the current system.
+*/
+void QQmlCodeModel::initializeCMakeStatus(const QString &pathForSettings)
+{
+ if (m_settings) {
+ const QString cmakeCalls = u"no-cmake-calls"_s;
+ m_settings->search(pathForSettings);
+ if (m_settings->isSet(cmakeCalls) && m_settings->value(cmakeCalls).toBool()) {
+ qWarning() << "Disabling CMake calls via .qmlls.ini setting.";
+ m_cmakeStatus = DoesNotHaveCMake;
+ return;
+ }
+ }
+
+ QProcess process;
+ process.setProgram(u"cmake"_s);
+ process.setArguments({ u"--version"_s });
+ process.start();
+ process.waitForFinished();
+ m_cmakeStatus = process.exitCode() == 0 ? HasCMake : DoesNotHaveCMake;
+
+ if (m_cmakeStatus == DoesNotHaveCMake) {
+ qWarning() << "Disabling CMake calls because CMake was not found.";
+ return;
+ }
+
+ QObject::connect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, this,
+ &QQmlCodeModel::onCppFileChanged);
+}
+
+/*!
+\internal
+For each build path that is a also a CMake build path, call CMake with \l cmakeBuildCommand to
+generate/update the .qmltypes, qmldir and .qrc files.
+It is assumed here that the number of build folders is usually no more than one, so execute the
+CMake builds one at a time.
+
+If CMake cannot be executed, false is returned. This may happen when CMake does not exist on the
+current system, when the target executed by CMake does not exist (for example when something else
+than qt_add_qml_module is used to setup the module in CMake), or the when the CMake build itself
+fails.
+*/
+bool QQmlCodeModel::callCMakeBuild(const QStringList &buildPaths)
+{
+ bool success = true;
+ for (const auto &path : buildPaths) {
+ if (!QFileInfo::exists(path + u"/.cmake"_s))
+ continue;
+
+ QProcess process;
+ const auto command = QQmlLSUtils::cmakeBuildCommand(path);
+ process.setProgram(command.first);
+ process.setArguments(command.second);
+ qCDebug(codeModelLog) << "Running" << process.program() << process.arguments();
+ process.start();
+
+ // TODO: run process concurrently instead of blocking qmlls
+ success &= process.waitForFinished();
+ success &= (process.exitCode() == 0);
+ qCDebug(codeModelLog) << process.program() << process.arguments() << "terminated with"
+ << process.exitCode();
+ }
+ return success;
+}
+
+/*!
+\internal
+Iterate the entire source directory to find all C++ files that have their names in fileNames, and
+return all the found file paths.
+
+This is an overapproximation and might find unrelated files with the same name.
+*/
+QStringList QQmlCodeModel::findFilePathsFromFileNames(const QStringList &fileNames) const
+{
+ QStringList result;
+ for (const auto &rootUrl : m_rootUrls) {
+ const QString rootDir = QUrl(QString::fromUtf8(rootUrl)).toLocalFile();
+
+ if (rootDir.isEmpty())
+ continue;
+
+ qCDebug(codeModelLog) << "Searching for files to watch in workspace folder" << rootDir;
+ QDirIterator it(rootDir, fileNames, QDir::Files, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QFileInfo info = it.nextFileInfo();
+ result << info.absoluteFilePath();
+ }
+ }
+ return result;
+}
+
+/*!
+\internal
+Find all C++ file names (not path, for file paths call \l findFilePathsFromFileNames on the result
+of this method) that this qmlFile relies on.
+*/
+QStringList QQmlCodeModel::fileNamesToWatch(const DomItem &qmlFile)
+{
+ const QmlFile *file = qmlFile.as<QmlFile>();
+ if (!file)
+ return {};
+
+ auto resolver = file->typeResolver();
+ if (!resolver)
+ return {};
+
+ auto types = resolver->importedTypes();
+
+ QStringList result;
+ for (const auto &type : types) {
+ if (!type.scope)
+ continue;
+ // note: the factory only loads composite types
+ const bool isComposite = type.scope.factory() || type.scope->isComposite();
+ if (isComposite)
+ continue;
+
+ const QString filePath = QFileInfo(type.scope->filePath()).fileName();
+ result << filePath;
+ }
+
+ return result;
+}
+
+/*!
+\internal
+Add watches for all C++ files that this qmlFile relies on, so a rebuild can be triggered when they
+are modified.
+*/
+void QQmlCodeModel::addFileWatches(const DomItem &qmlFile)
+{
+ const auto filesToWatch = fileNamesToWatch(qmlFile);
+ const QStringList filepathsToWatch = findFilePathsFromFileNames(filesToWatch);
+ const auto unwatchedPaths = m_cppFileWatcher.addPaths(filepathsToWatch);
+ if (!unwatchedPaths.isEmpty()) {
+ qCDebug(codeModelLog) << "Cannot watch paths" << unwatchedPaths << "from requested"
+ << filepathsToWatch;
+ }
+}
+
+void QQmlCodeModel::onCppFileChanged(const QString &)
+{
+ m_rebuildRequired = true;
+}
+
+void QQmlCodeModel::newDocForOpenFile(const QByteArray &url, int version, const QString &docText)
+{
+ qCDebug(codeModelLog) << "updating doc" << url << "to version" << version << "("
+ << docText.size() << "chars)";
+
+ const QString fPath = url2Path(url, UrlLookup::ForceLookup);
+ if (m_cmakeStatus == RequiresInitialization)
+ initializeCMakeStatus(fPath);
+
+ DomItem newCurrent = m_currentEnv.makeCopy(DomItem::CopyOption::EnvConnected).item();
+ QStringList loadPaths = buildPathsForFileUrl(url);
+
+ if (m_cmakeStatus == HasCMake && !loadPaths.isEmpty() && m_rebuildRequired) {
+ callCMakeBuild(loadPaths);
+ m_rebuildRequired = false;
+ }
+
+ loadPaths.append(m_importPaths);
+ if (std::shared_ptr<DomEnvironment> newCurrentPtr = newCurrent.ownerAs<DomEnvironment>()) {
+ newCurrentPtr->setLoadPaths(loadPaths);
+ }
+ Path p;
+ auto newCurrentPtr = newCurrent.ownerAs<DomEnvironment>();
+ newCurrentPtr->loadFile(FileToLoad::fromMemory(newCurrentPtr, fPath, docText),
+ [&p, this](Path, const DomItem &, const DomItem &newValue) {
+ const DomItem file = newValue.fileObject();
+ p = file.canonicalPath();
+ if (m_cmakeStatus == HasCMake)
+ addFileWatches(file);
+ });
+ newCurrentPtr->loadPendingDependencies();
+ if (p) {
+ newCurrent.commitToBase(m_validEnv.ownerAs<DomEnvironment>());
+ DomItem item = m_currentEnv.path(p);
+ {
+ QMutexLocker l(&m_mutex);
+ OpenDocument &doc = m_openDocuments[url];
+ if (!doc.textDocument) {
+ qCWarning(lspServerLog)
+ << "ignoring update to closed document" << QString::fromUtf8(url);
+ return;
+ } else {
+ QMutexLocker l(doc.textDocument->mutex());
+ if (doc.textDocument->version() && *doc.textDocument->version() > version) {
+ qCWarning(lspServerLog)
+ << "docUpdate: version" << version << "of document"
+ << QString::fromUtf8(url) << "is not the latest anymore";
+ return;
+ }
+ }
+ if (!doc.snapshot.docVersion || *doc.snapshot.docVersion < version) {
+ doc.snapshot.docVersion = version;
+ doc.snapshot.doc = item;
+ } else {
+ qCWarning(lspServerLog) << "skipping update of current doc to obsolete version"
+ << version << "of document" << QString::fromUtf8(url);
+ }
+ if (item.field(Fields::isValid).value().toBool(false)) {
+ if (!doc.snapshot.validDocVersion || *doc.snapshot.validDocVersion < version) {
+ DomItem vDoc = m_validEnv.path(p);
+ doc.snapshot.validDocVersion = version;
+ doc.snapshot.validDoc = vDoc;
+ } else {
+ qCWarning(lspServerLog) << "skippig update of valid doc to obsolete version"
+ << version << "of document" << QString::fromUtf8(url);
+ }
+ } else {
+ qCWarning(lspServerLog)
+ << "avoid update of validDoc to " << version << "of document"
+ << QString::fromUtf8(url) << "as it is invalid";
+ }
+ }
+ }
+ if (codeModelLog().isDebugEnabled()) {
+ qCDebug(codeModelLog) << "finished update doc of " << url << "to version" << version;
+ snapshotByUrl(url).dump(qDebug() << "postSnapshot",
+ OpenDocumentSnapshot::DumpOption::AllCode);
+ }
+ // we should update the scope in the future thus call addOpen(url)
+ emit updatedSnapshot(url);
+}
+
+void QQmlCodeModel::closeOpenFile(const QByteArray &url)
+{
+ QMutexLocker l(&m_mutex);
+ m_openDocuments.remove(url);
+}
+
+void QQmlCodeModel::setRootUrls(const QList<QByteArray> &urls)
+{
+ QMutexLocker l(&m_mutex);
+ m_rootUrls = urls;
+}
+
+void QQmlCodeModel::addRootUrls(const QList<QByteArray> &urls)
+{
+ QMutexLocker l(&m_mutex);
+ for (const QByteArray &url : urls) {
+ if (!m_rootUrls.contains(url))
+ m_rootUrls.append(url);
+ }
+}
+
+void QQmlCodeModel::removeRootUrls(const QList<QByteArray> &urls)
+{
+ QMutexLocker l(&m_mutex);
+ for (const QByteArray &url : urls)
+ m_rootUrls.removeOne(url);
+}
+
+QList<QByteArray> QQmlCodeModel::rootUrls() const
+{
+ QMutexLocker l(&m_mutex);
+ return m_rootUrls;
+}
+
+QStringList QQmlCodeModel::buildPathsForRootUrl(const QByteArray &url)
+{
+ QMutexLocker l(&m_mutex);
+ return m_buildPathsForRootUrl.value(url);
+}
+
+static bool isNotSeparator(char c)
+{
+ return c != '/';
+}
+
+QStringList QQmlCodeModel::buildPathsForFileUrl(const QByteArray &url)
+{
+ QList<QByteArray> roots;
+ {
+ QMutexLocker l(&m_mutex);
+ roots = m_buildPathsForRootUrl.keys();
+ }
+ // we want to longest match to be first, as it should override shorter matches
+ std::sort(roots.begin(), roots.end(), [](const QByteArray &el1, const QByteArray &el2) {
+ if (el1.size() > el2.size())
+ return true;
+ if (el1.size() < el2.size())
+ return false;
+ return el1 < el2;
+ });
+ QStringList buildPaths;
+ QStringList defaultValues;
+ if (!roots.isEmpty() && roots.last().isEmpty())
+ roots.removeLast();
+ QByteArray urlSlash(url);
+ if (!urlSlash.isEmpty() && isNotSeparator(urlSlash.at(urlSlash.size() - 1)))
+ urlSlash.append('/');
+ // look if the file has a know prefix path
+ for (const QByteArray &root : roots) {
+ if (urlSlash.startsWith(root)) {
+ buildPaths += buildPathsForRootUrl(root);
+ break;
+ }
+ }
+ QString path = url2Path(url);
+
+ // fallback to the empty root, if is has an entry.
+ // This is the buildPath that is passed to qmlls via --build-dir.
+ if (buildPaths.isEmpty()) {
+ buildPaths += buildPathsForRootUrl(QByteArray());
+ }
+
+ // look in the QMLLS_BUILD_DIRS environment variable
+ if (buildPaths.isEmpty()) {
+ QStringList envPaths = qEnvironmentVariable("QMLLS_BUILD_DIRS")
+ .split(QDir::listSeparator(), Qt::SkipEmptyParts);
+ buildPaths += envPaths;
+ }
+
+ // look in the settings.
+ // This is the one that is passed via the .qmlls.ini file.
+ if (buildPaths.isEmpty() && m_settings) {
+ m_settings->search(path);
+ QString buildDir = QStringLiteral(u"buildDir");
+ if (m_settings->isSet(buildDir))
+ buildPaths += m_settings->value(buildDir).toString().split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
+ }
+
+ // heuristic to find build directory
+ if (buildPaths.isEmpty()) {
+ QDir d(path);
+ d.setNameFilters(QStringList({ u"build*"_s }));
+ const int maxDirDepth = 8;
+ int iDir = maxDirDepth;
+ QString dirName = d.dirName();
+ QDateTime lastModified;
+ while (d.cdUp() && --iDir > 0) {
+ for (const QFileInfo &fInfo : d.entryInfoList(QDir::Dirs)) {
+ if (fInfo.completeBaseName() == u"build"
+ || fInfo.completeBaseName().startsWith(u"build-%1"_s.arg(dirName))) {
+ if (iDir > 1)
+ iDir = 1;
+ if (!lastModified.isValid() || lastModified < fInfo.lastModified()) {
+ buildPaths.clear();
+ buildPaths.append(fInfo.absoluteFilePath());
+ }
+ }
+ }
+ }
+ }
+ // add dependent build directories
+ QStringList res;
+ std::reverse(buildPaths.begin(), buildPaths.end());
+ const int maxDeps = 4;
+ while (!buildPaths.isEmpty()) {
+ QString bPath = buildPaths.last();
+ buildPaths.removeLast();
+ res += bPath;
+ if (QFile::exists(bPath + u"/_deps") && bPath.split(u"/_deps/"_s).size() < maxDeps) {
+ QDir d(bPath + u"/_deps");
+ for (const QFileInfo &fInfo : d.entryInfoList(QDir::Dirs))
+ buildPaths.append(fInfo.absoluteFilePath());
+ }
+ }
+ return res;
+}
+
+void QQmlCodeModel::setBuildPathsForRootUrl(QByteArray url, const QStringList &paths)
+{
+ QMutexLocker l(&m_mutex);
+ if (!url.isEmpty() && isNotSeparator(url.at(url.size() - 1)))
+ url.append('/');
+ if (paths.isEmpty())
+ m_buildPathsForRootUrl.remove(url);
+ else
+ m_buildPathsForRootUrl.insert(url, paths);
+}
+
+void QQmlCodeModel::openUpdate(const QByteArray &url)
+{
+ bool updateDoc = false;
+ bool updateScope = false;
+ std::optional<int> rNow = 0;
+ QString docText;
+ DomItem validDoc;
+ std::shared_ptr<Utils::TextDocument> document;
+ {
+ QMutexLocker l(&m_mutex);
+ OpenDocument &doc = m_openDocuments[url];
+ document = doc.textDocument;
+ if (!document)
+ return;
+ {
+ QMutexLocker l2(document->mutex());
+ rNow = document->version();
+ }
+ if (rNow && (!doc.snapshot.docVersion || *doc.snapshot.docVersion != *rNow))
+ updateDoc = true;
+ else if (doc.snapshot.validDocVersion
+ && (!doc.snapshot.scopeVersion
+ || *doc.snapshot.scopeVersion != *doc.snapshot.validDocVersion))
+ updateScope = true;
+ else
+ return;
+ if (updateDoc) {
+ QMutexLocker l2(doc.textDocument->mutex());
+ rNow = doc.textDocument->version();
+ docText = doc.textDocument->toPlainText();
+ } else {
+ validDoc = doc.snapshot.validDoc;
+ rNow = doc.snapshot.validDocVersion;
+ }
+ }
+ if (updateDoc) {
+ newDocForOpenFile(url, *rNow, docText);
+ }
+ if (updateScope) {
+ // to do
+ }
+}
+
+void QQmlCodeModel::addOpenToUpdate(const QByteArray &url)
+{
+ QMutexLocker l(&m_mutex);
+ m_openDocumentsToUpdate.insert(url);
+}
+
+QDebug OpenDocumentSnapshot::dump(QDebug dbg, DumpOptions options)
+{
+ dbg.noquote().nospace() << "{";
+ dbg << " url:" << QString::fromUtf8(url) << "\n";
+ dbg << " docVersion:" << (docVersion ? QString::number(*docVersion) : u"*none*"_s) << "\n";
+ if (options & DumpOption::LatestCode) {
+ dbg << " doc: ------------\n"
+ << doc.field(Fields::code).value().toString() << "\n==========\n";
+ } else {
+ dbg << u" doc:"
+ << (doc ? u"%1chars"_s.arg(doc.field(Fields::code).value().toString().size())
+ : u"*none*"_s)
+ << "\n";
+ }
+ dbg << " validDocVersion:"
+ << (validDocVersion ? QString::number(*validDocVersion) : u"*none*"_s) << "\n";
+ if (options & DumpOption::ValidCode) {
+ dbg << " validDoc: ------------\n"
+ << validDoc.field(Fields::code).value().toString() << "\n==========\n";
+ } else {
+ dbg << u" validDoc:"
+ << (validDoc ? u"%1chars"_s.arg(validDoc.field(Fields::code).value().toString().size())
+ : u"*none*"_s)
+ << "\n";
+ }
+ dbg << " scopeVersion:" << (scopeVersion ? QString::number(*scopeVersion) : u"*none*"_s)
+ << "\n";
+ dbg << " scopeDependenciesLoadTime:" << scopeDependenciesLoadTime << "\n";
+ dbg << " scopeDependenciesChanged" << scopeDependenciesChanged << "\n";
+ dbg << "}";
+ return dbg;
+}
+
+} // namespace QmlLsp
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlcodemodel_p.h b/src/qmlls/qqmlcodemodel_p.h
new file mode 100644
index 0000000000..0009a7e48b
--- /dev/null
+++ b/src/qmlls/qqmlcodemodel_p.h
@@ -0,0 +1,173 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLCODEMODEL_P_H
+#define QQMLCODEMODEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qtextdocument_p.h"
+
+#include <QObject>
+#include <QHash>
+#include <QtCore/qfilesystemwatcher.h>
+#include <QtCore/private/qfactoryloader_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlCompiler/private/qqmljsscope_p.h>
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+
+#include <functional>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+class TextSynchronization;
+namespace QmlLsp {
+
+class OpenDocumentSnapshot
+{
+public:
+ enum class DumpOption {
+ NoCode = 0,
+ LatestCode = 0x1,
+ ValidCode = 0x2,
+ AllCode = LatestCode | ValidCode
+ };
+ Q_DECLARE_FLAGS(DumpOptions, DumpOption)
+ QStringList searchPath;
+ QByteArray url;
+ std::optional<int> docVersion;
+ QQmlJS::Dom::DomItem doc;
+ std::optional<int> validDocVersion;
+ QQmlJS::Dom::DomItem validDoc;
+ std::optional<int> scopeVersion;
+ QDateTime scopeDependenciesLoadTime;
+ bool scopeDependenciesChanged = false;
+ QQmlJSScope::ConstPtr scope;
+ QDebug dump(QDebug dbg, DumpOptions dump = DumpOption::NoCode);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(OpenDocumentSnapshot::DumpOptions)
+
+class OpenDocument
+{
+public:
+ OpenDocumentSnapshot snapshot;
+ std::shared_ptr<Utils::TextDocument> textDocument;
+};
+
+struct ToIndex
+{
+ QString path;
+ int leftDepth;
+};
+
+struct RegisteredSemanticTokens
+{
+ QByteArray resultId = "0";
+ QList<int> lastTokens;
+};
+
+class QQmlCodeModel : public QObject
+{
+ Q_OBJECT
+public:
+ enum class UrlLookup { Caching, ForceLookup };
+ enum class State { Running, Stopping };
+
+ explicit QQmlCodeModel(QObject *parent = nullptr, QQmlToolingSettings *settings = nullptr);
+ ~QQmlCodeModel();
+ QQmlJS::Dom::DomItem currentEnv();
+ QQmlJS::Dom::DomItem validEnv();
+ OpenDocumentSnapshot snapshotByUrl(const QByteArray &url);
+ OpenDocument openDocumentByUrl(const QByteArray &url);
+
+ void openNeedUpdate();
+ void indexNeedsUpdate();
+ void addDirectoriesToIndex(const QStringList &paths, QLanguageServer *server);
+ void addOpenToUpdate(const QByteArray &);
+ void removeDirectory(const QString &path);
+ // void updateDocument(const OpenDocument &doc);
+ QString url2Path(const QByteArray &url, UrlLookup options = UrlLookup::Caching);
+ void newOpenFile(const QByteArray &url, int version, const QString &docText);
+ void newDocForOpenFile(const QByteArray &url, int version, const QString &docText);
+ void closeOpenFile(const QByteArray &url);
+ void setRootUrls(const QList<QByteArray> &urls);
+ QList<QByteArray> rootUrls() const;
+ void addRootUrls(const QList<QByteArray> &urls);
+ QStringList buildPathsForRootUrl(const QByteArray &url);
+ QStringList buildPathsForFileUrl(const QByteArray &url);
+ void setBuildPathsForRootUrl(QByteArray url, const QStringList &paths);
+ QStringList importPaths() const { return m_importPaths; };
+ void setImportPaths(const QStringList &paths) { m_importPaths = paths; };
+ void removeRootUrls(const QList<QByteArray> &urls);
+ QQmlToolingSettings *settings();
+ QStringList findFilePathsFromFileNames(const QStringList &fileNames) const;
+ static QStringList fileNamesToWatch(const QQmlJS::Dom::DomItem &qmlFile);
+ void disableCMakeCalls();
+ const QFactoryLoader &pluginLoader() const { return m_pluginLoader; }
+
+ RegisteredSemanticTokens &registeredTokens();
+ const RegisteredSemanticTokens &registeredTokens() const;
+
+Q_SIGNALS:
+ void updatedSnapshot(const QByteArray &url);
+private:
+ void indexDirectory(const QString &path, int depthLeft);
+ int indexEvalProgress() const; // to be called in the mutex
+ void indexStart(); // to be called in the mutex
+ void indexEnd(); // to be called in the mutex
+ void indexSendProgress(int progress);
+ bool indexCancelled();
+ bool indexSome();
+ void addDirectory(const QString &path, int leftDepth);
+ bool openUpdateSome();
+ void openUpdateStart();
+ void openUpdateEnd();
+ void openUpdate(const QByteArray &);
+
+ static bool callCMakeBuild(const QStringList &buildPaths);
+ void addFileWatches(const QQmlJS::Dom::DomItem &qmlFile);
+ enum CMakeStatus { RequiresInitialization, HasCMake, DoesNotHaveCMake };
+ void initializeCMakeStatus(const QString &);
+
+ mutable QMutex m_mutex;
+ State m_state = State::Running;
+ int m_lastIndexProgress = 0;
+ int m_nIndexInProgress = 0;
+ QList<ToIndex> m_toIndex;
+ int m_indexInProgressCost = 0;
+ int m_indexDoneCost = 0;
+ int m_nUpdateInProgress = 0;
+ QStringList m_importPaths;
+ QQmlJS::Dom::DomItem m_currentEnv;
+ QQmlJS::Dom::DomItem m_validEnv;
+ QByteArray m_lastOpenDocumentUpdated;
+ QSet<QByteArray> m_openDocumentsToUpdate;
+ QHash<QByteArray, QStringList> m_buildPathsForRootUrl;
+ QList<QByteArray> m_rootUrls;
+ QHash<QByteArray, QString> m_url2path;
+ QHash<QString, QByteArray> m_path2url;
+ QHash<QByteArray, OpenDocument> m_openDocuments;
+ QQmlToolingSettings *m_settings;
+ QFileSystemWatcher m_cppFileWatcher;
+ QFactoryLoader m_pluginLoader;
+ bool m_rebuildRequired = true; // always trigger a rebuild on start
+ CMakeStatus m_cmakeStatus = RequiresInitialization;
+ RegisteredSemanticTokens m_tokens;
+private slots:
+ void onCppFileChanged(const QString &);
+};
+
+} // namespace QmlLsp
+QT_END_NAMESPACE
+#endif // QQMLCODEMODEL_P_H
diff --git a/src/qmlls/qqmlcompletioncontextstrings.cpp b/src/qmlls/qqmlcompletioncontextstrings.cpp
new file mode 100644
index 0000000000..5fc2006661
--- /dev/null
+++ b/src/qmlls/qqmlcompletioncontextstrings.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmlcompletioncontextstrings_p.h"
+
+CompletionContextStrings::CompletionContextStrings(QString code, qsizetype pos)
+ : m_code(code), m_pos(pos)
+{
+ // computes the context just before pos in code.
+ // After this code all the values of all the attributes should be correct (see above)
+ // handle also letter or numbers represented a surrogate pairs?
+ m_filterStart = m_pos;
+ while (m_filterStart != 0) {
+ QChar c = code.at(m_filterStart - 1);
+ if (!c.isLetterOrNumber() && c != u'_')
+ break;
+ else
+ --m_filterStart;
+ }
+ // handle spaces?
+ m_baseStart = m_filterStart;
+ while (m_baseStart != 0) {
+ QChar c = code.at(m_baseStart - 1);
+ if (c != u'.' || m_baseStart == 1)
+ break;
+ c = code.at(m_baseStart - 2);
+ if (!c.isLetterOrNumber() && c != u'_')
+ break;
+ qsizetype baseEnd = --m_baseStart;
+ while (m_baseStart != 0) {
+ QChar c = code.at(m_baseStart - 1);
+ if (!c.isLetterOrNumber() && c != u'_')
+ break;
+ else
+ --m_baseStart;
+ }
+ if (m_baseStart == baseEnd)
+ break;
+ }
+ m_atLineStart = true;
+ m_lineStart = m_baseStart;
+ while (m_lineStart != 0) {
+ QChar c = code.at(m_lineStart - 1);
+ if (c == u'\n' || c == u'\r')
+ break;
+ if (!c.isSpace())
+ m_atLineStart = false;
+ --m_lineStart;
+ }
+}
diff --git a/src/qmlls/qqmlcompletioncontextstrings_p.h b/src/qmlls/qqmlcompletioncontextstrings_p.h
new file mode 100644
index 0000000000..78cf2b1553
--- /dev/null
+++ b/src/qmlls/qqmlcompletioncontextstrings_p.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLLSCOMPLETIONCONTEXTSTRINGS_H
+#define QQMLLSCOMPLETIONCONTEXTSTRINGS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qtconfigmacros.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringview.h>
+
+QT_BEGIN_NAMESPACE
+
+// finds the filter string, the base (for fully qualified accesses) and the whole string
+// just before pos in code
+struct CompletionContextStrings
+{
+ CompletionContextStrings(QString code, qsizetype pos);
+
+public:
+ // line up until pos
+ QStringView preLine() const
+ {
+ return QStringView(m_code).mid(m_lineStart, m_pos - m_lineStart);
+ }
+ // the part used to filter the completion (normally actual filtering is left to the client)
+ QStringView filterChars() const
+ {
+ return QStringView(m_code).mid(m_filterStart, m_pos - m_filterStart);
+ }
+ // the base part (qualified access)
+ QStringView base() const
+ {
+ return QStringView(m_code).mid(m_baseStart, m_filterStart - m_baseStart);
+ }
+ // if we are at line start
+ bool atLineStart() const { return m_atLineStart; }
+
+ qsizetype offset() const { return m_pos; }
+
+private:
+ QString m_code; // the current code
+ qsizetype m_pos = {}; // current position of the cursor
+ qsizetype m_filterStart = {}; // start of the characters that are used to filter the suggestions
+ qsizetype m_lineStart = {}; // start of the current line
+ qsizetype m_baseStart = {}; // start of the dotted expression that ends at the cursor position
+ bool m_atLineStart = {}; // if there are only spaces before base
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLLSCOMPLETIONCONTEXTSTRINGS_H
diff --git a/src/qmlls/qqmlcompletionsupport.cpp b/src/qmlls/qqmlcompletionsupport.cpp
new file mode 100644
index 0000000000..371a5eb447
--- /dev/null
+++ b/src/qmlls/qqmlcompletionsupport.cpp
@@ -0,0 +1,195 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmlcompletionsupport_p.h"
+#include "qqmllsutils_p.h"
+
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+#include <QtCore/qthreadpool.h>
+#include <QtCore/private/qduplicatetracker_p.h>
+#include <QtCore/QRegularExpression>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+
+QT_BEGIN_NAMESPACE
+using namespace QLspSpecification;
+using namespace QQmlJS::Dom;
+using namespace Qt::StringLiterals;
+
+bool CompletionRequest::fillFrom(QmlLsp::OpenDocument doc, const Parameters &params,
+ Response &&response)
+{
+ // do not call BaseRequest::fillFrom() to avoid taking the Mutex twice and getting an
+ // inconsistent state.
+ m_parameters = params;
+ m_response = std::move(response);
+
+ if (!doc.textDocument)
+ return false;
+
+ std::optional<int> targetVersion;
+ {
+ QMutexLocker l(doc.textDocument->mutex());
+ targetVersion = doc.textDocument->version();
+ code = doc.textDocument->toPlainText();
+ }
+ m_minVersion = (targetVersion ? *targetVersion : 0);
+
+ return true;
+}
+
+QmlCompletionSupport::QmlCompletionSupport(QmlLsp::QQmlCodeModel *codeModel)
+ : BaseT(codeModel), m_completionEngine(codeModel->pluginLoader())
+{
+}
+
+void QmlCompletionSupport::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerCompletionRequestHandler(getRequestHandler());
+ protocol->registerCompletionItemResolveRequestHandler(
+ [](const QByteArray &, const CompletionItem &cParams,
+ LSPResponse<CompletionItem> &&response) { response.sendResponse(cParams); });
+}
+
+QString QmlCompletionSupport::name() const
+{
+ return u"QmlCompletionSupport"_s;
+}
+
+void QmlCompletionSupport::setupCapabilities(
+ const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ QLspSpecification::CompletionOptions cOptions;
+ if (serverCapabilities.capabilities.completionProvider)
+ cOptions = *serverCapabilities.capabilities.completionProvider;
+ cOptions.resolveProvider = false;
+ cOptions.triggerCharacters = QList<QByteArray>({ QByteArray(".") });
+ serverCapabilities.capabilities.completionProvider = cOptions;
+}
+
+void QmlCompletionSupport::process(RequestPointerArgument req)
+{
+ QmlLsp::OpenDocumentSnapshot doc =
+ m_codeModel->snapshotByUrl(req->m_parameters.textDocument.uri);
+ req->sendCompletions(req->completions(doc, m_completionEngine));
+}
+
+QString CompletionRequest::urlAndPos() const
+{
+ return QString::fromUtf8(m_parameters.textDocument.uri) + u":"
+ + QString::number(m_parameters.position.line) + u":"
+ + QString::number(m_parameters.position.character);
+}
+
+void CompletionRequest::sendCompletions(const QList<CompletionItem> &completions)
+{
+ m_response.sendResponse(completions);
+}
+
+static bool positionIsFollowedBySpaces(qsizetype position, const QString &code)
+{
+ if (position >= code.size())
+ return false;
+
+ auto newline =
+ std::find_if(std::next(code.cbegin(), position), code.cend(),
+ [](const QChar &c) { return c == u'\n' || c == u'\r' || !c.isSpace(); });
+
+ return newline == code.cend() || newline->isSpace();
+}
+
+/*!
+\internal
+
+\note Remove this method and all its usages once the new fault-tolerant parser from QTBUG-118053 is
+introduced!!!
+
+Tries to make the document valid for the parser, to be able to provide completions after dots.
+The created DomItem is not in the qqmlcodemodel which mean it cannot be seen and cannot bother
+other modules: it would be bad to have the linting module complain about code that was modified
+here, but cannot be seen by the user.
+*/
+DomItem CompletionRequest::patchInvalidFileForParser(const DomItem &file, qsizetype position) const
+{
+ // automatic semicolon insertion after dots, if there is nothing behind the dot!
+ if (position > 0 && code[position - 1] == u'.' && positionIsFollowedBySpaces(position, code)) {
+ qCWarning(QQmlLSCompletionLog)
+ << "Patching invalid document: adding a semicolon after '.' for "
+ << QString::fromUtf8(m_parameters.textDocument.uri);
+
+ const QString patchedCode =
+ code.first(position).append(u"_dummyIdentifier;").append(code.sliced(position));
+
+ // create a new (local) Dom only for the completions.
+ // This avoids weird behaviors, like the linting module complaining about the inserted
+ // semicolon that the user cannot see, for example.
+ DomItem newCurrent = file.environment().makeCopy(DomItem::CopyOption::EnvConnected).item();
+
+ DomItem result;
+ auto newCurrentPtr = newCurrent.ownerAs<DomEnvironment>();
+ newCurrentPtr->loadFile(
+ FileToLoad::fromMemory(newCurrentPtr, file.canonicalFilePath(), patchedCode),
+ [&result](Path, const DomItem &, const DomItem &newValue) {
+ result = newValue.fileObject();
+ });
+ newCurrentPtr->loadPendingDependencies();
+ return result;
+ }
+
+ qCWarning(QQmlLSCompletionLog) << "No valid document for completions for "
+ << QString::fromUtf8(m_parameters.textDocument.uri);
+
+ return file;
+}
+
+QList<CompletionItem> CompletionRequest::completions(QmlLsp::OpenDocumentSnapshot &doc,
+ const QQmlLSCompletion &completionEngine) const
+{
+ QList<CompletionItem> res;
+
+
+ const qsizetype pos = QQmlLSUtils::textOffsetFrom(code, m_parameters.position.line,
+ m_parameters.position.character);
+
+ const bool useValidDoc =
+ doc.validDoc && doc.validDocVersion && *doc.validDocVersion >= m_minVersion;
+
+ const DomItem file = useValidDoc
+ ? doc.validDoc.fileObject(QQmlJS::Dom::GoTo::MostLikely)
+ : patchInvalidFileForParser(doc.doc.fileObject(QQmlJS::Dom::GoTo::MostLikely), pos);
+
+ // clear reference cache to resolve latest versions (use a local env instead?)
+ if (std::shared_ptr<DomEnvironment> envPtr = file.environment().ownerAs<DomEnvironment>())
+ envPtr->clearReferenceCache();
+
+
+ CompletionContextStrings ctx(code, pos);
+ auto itemsFound = QQmlLSUtils::itemsFromTextLocation(file, m_parameters.position.line,
+ m_parameters.position.character
+ - ctx.filterChars().size());
+ if (itemsFound.size() > 1) {
+ QStringList paths;
+ for (auto &it : itemsFound)
+ paths.append(it.domItem.canonicalPath().toString());
+ qCWarning(QQmlLSCompletionLog) << "Multiple elements of " << urlAndPos()
+ << " at the same depth:" << paths << "(using first)";
+ }
+ DomItem currentItem;
+ if (!itemsFound.isEmpty())
+ currentItem = itemsFound.first().domItem;
+ else
+ qCDebug(QQmlLSCompletionLog) << "No items found for completions at" << urlAndPos();
+ qCDebug(QQmlLSCompletionLog) << "Completion at " << urlAndPos() << " "
+ << m_parameters.position.line << ":"
+ << m_parameters.position.character << "offset:" << pos
+ << "base:" << ctx.base() << "filter:" << ctx.filterChars()
+ << "lastVersion:" << (doc.docVersion ? (*doc.docVersion) : -1)
+ << "validVersion:"
+ << (doc.validDocVersion ? (*doc.validDocVersion) : -1) << "in"
+ << currentItem.internalKindStr() << currentItem.canonicalPath();
+ auto result = completionEngine.completions(currentItem, ctx);
+ return result;
+}
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlcompletionsupport_p.h b/src/qmlls/qqmlcompletionsupport_p.h
new file mode 100644
index 0000000000..fa0c1ca51a
--- /dev/null
+++ b/src/qmlls/qqmlcompletionsupport_p.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLCOMPLETIONSUPPORT_P_H
+#define QQMLCOMPLETIONSUPPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlbasemodule_p.h"
+#include "qqmlcodemodel_p.h"
+
+#include <QtCore/qmutex.h>
+#include <QtCore/qhash.h>
+#include <QtQmlLS/private/qqmllscompletion_p.h>
+
+QT_BEGIN_NAMESPACE
+struct CompletionRequest
+ : BaseRequest<QLspSpecification::CompletionParams,
+ QLspSpecification::LSPPartialResponse<
+ std::variant<QList<QLspSpecification::CompletionItem>,
+ QLspSpecification::CompletionList, std::nullptr_t>,
+ std::variant<QLspSpecification::CompletionList,
+ QList<QLspSpecification::CompletionItem>>>>
+{
+ QString code;
+
+ bool fillFrom(QmlLsp::OpenDocument doc, const Parameters &params, Response &&response);
+ void sendCompletions(const QList<QLspSpecification::CompletionItem> &completions);
+ QString urlAndPos() const;
+ QList<QLspSpecification::CompletionItem>
+ completions(QmlLsp::OpenDocumentSnapshot &doc, const QQmlLSCompletion &completionEngine) const;
+ DomItem patchInvalidFileForParser(const DomItem& file, qsizetype position) const;
+};
+
+class QmlCompletionSupport : public QQmlBaseModule<CompletionRequest>
+{
+ Q_OBJECT
+public:
+ QmlCompletionSupport(QmlLsp::QQmlCodeModel *codeModel);
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+ void process(RequestPointerArgument req) override;
+
+ QQmlLSCompletion m_completionEngine;
+};
+QT_END_NAMESPACE
+
+#endif // QMLCOMPLETIONSUPPORT_P_H
diff --git a/src/qmlls/qqmlfindusagessupport.cpp b/src/qmlls/qqmlfindusagessupport.cpp
new file mode 100644
index 0000000000..8a3dca7e3a
--- /dev/null
+++ b/src/qmlls/qqmlfindusagessupport.cpp
@@ -0,0 +1,79 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmlfindusagessupport_p.h"
+#include "qqmllsutils_p.h"
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+QQmlFindUsagesSupport::QQmlFindUsagesSupport(QmlLsp::QQmlCodeModel *codeModel)
+ : BaseT(codeModel) { }
+
+QString QQmlFindUsagesSupport::name() const
+{
+ return u"QmlFindUsagesSupport"_s;
+}
+
+void QQmlFindUsagesSupport::setupCapabilities(
+ const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ // just assume serverCapabilities.capabilities.typeDefinitionProvider is a bool for now
+ // handle the ReferenceOptions later if needed (it adds the possibility to communicate the
+ // current progress).
+ serverCapabilities.capabilities.referencesProvider = true;
+}
+
+void QQmlFindUsagesSupport::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerReferenceRequestHandler(getRequestHandler());
+}
+
+void QQmlFindUsagesSupport::process(QQmlFindUsagesSupport::RequestPointerArgument request)
+{
+ QList<QLspSpecification::Location> results;
+ ResponseScopeGuard guard(results, request->m_response);
+
+ auto itemsFound = itemsForRequest(request);
+ if (guard.setErrorFrom(itemsFound))
+ return;
+
+ QQmlLSUtilsItemLocation &front = std::get<QList<QQmlLSUtilsItemLocation>>(itemsFound).front();
+
+ auto usages = QQmlLSUtils::findUsagesOf(front.domItem);
+
+ QQmlJS::Dom::DomItem files = front.domItem.top().field(QQmlJS::Dom::Fields::qmlFileWithPath);
+
+ QHash<QString, QString> codeCache;
+
+ for (const auto &usage : usages) {
+ QLspSpecification::Location location;
+ location.uri = QUrl::fromLocalFile(usage.filename).toEncoded();
+
+ auto cacheEntry = codeCache.find(usage.filename);
+ if (cacheEntry == codeCache.end()) {
+ auto file = files.key(usage.filename)
+ .field(QQmlJS::Dom::Fields::currentItem)
+ .ownerAs<QQmlJS::Dom::QmlFile>();
+ if (!file) {
+ qDebug() << "File" << usage.filename << "not found in DOM! Available files are"
+ << files.keys();
+ continue;
+ }
+ cacheEntry = codeCache.insert(usage.filename, file->code());
+ }
+
+ location.range =
+ QQmlLSUtils::qmlLocationToLspLocation(cacheEntry.value(), usage.sourceLocation);
+
+ results.append(location);
+ }
+}
+QT_END_NAMESPACE
+
diff --git a/src/qmlls/qqmlfindusagessupport_p.h b/src/qmlls/qqmlfindusagessupport_p.h
new file mode 100644
index 0000000000..ee4803ea93
--- /dev/null
+++ b/src/qmlls/qqmlfindusagessupport_p.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMLFINDUSAGESUPPORT_P_H
+#define QMLFINDUSAGESUPPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlcodemodel_p.h"
+#include "qqmlbasemodule_p.h"
+
+QT_BEGIN_NAMESPACE
+struct ReferencesRequest : public BaseRequest<QLspSpecification::ReferenceParams,
+ QLspSpecification::Responses::ReferenceResponseType>
+{
+};
+
+class QQmlFindUsagesSupport : public QQmlBaseModule<ReferencesRequest>
+{
+ Q_OBJECT
+public:
+ QQmlFindUsagesSupport(QmlLsp::QQmlCodeModel *codeModel);
+
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+
+ void process(RequestPointerArgument request) override;
+
+ void typeDefinitionRequestHandler(const QByteArray &,
+ const QLspSpecification::TypeDefinitionParams &params,
+ ReferencesRequest::Response &&response);
+};
+QT_END_NAMESPACE
+
+#endif // QMLFINDUSAGESUPPORT_P_H
diff --git a/src/qmlls/qqmlformatting.cpp b/src/qmlls/qqmlformatting.cpp
new file mode 100644
index 0000000000..82313f1c65
--- /dev/null
+++ b/src/qmlls/qqmlformatting.cpp
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qqmlformatting_p.h>
+#include <qqmlcodemodel_p.h>
+#include <qqmllsutils_p.h>
+
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomindentinglinewriter_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(formatLog, "qt.languageserver.formatting")
+
+QQmlDocumentFormatting::QQmlDocumentFormatting(QmlLsp::QQmlCodeModel *codeModel)
+ : QQmlBaseModule(codeModel)
+{
+}
+
+QString QQmlDocumentFormatting::name() const
+{
+ return u"QQmlDocumentFormatting"_s;
+}
+
+void QQmlDocumentFormatting::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerDocumentFormattingRequestHandler(getRequestHandler());
+}
+
+void QQmlDocumentFormatting::setupCapabilities(
+ const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ // TODO: Allow customized formatting in future
+ serverCapabilities.capabilities.documentFormattingProvider = true;
+}
+
+void QQmlDocumentFormatting::process(RequestPointerArgument request)
+{
+ QList<QLspSpecification::TextEdit> result;
+ ResponseScopeGuard guard(result, request->m_response);
+
+ using namespace QQmlJS::Dom;
+ QmlLsp::OpenDocument doc = m_codeModel->openDocumentByUrl(
+ QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri));
+
+ DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely);
+ if (!file) {
+ guard.setError(QQmlLSUtilsErrorMessage{
+ 0, u"Could not find the file %1"_s.arg(doc.snapshot.doc.canonicalFilePath()) });
+ return;
+ }
+ if (!file.field(Fields::isValid).value().toBool(false)) {
+ guard.setError(QQmlLSUtilsErrorMessage{ 0, u"Cannot format invalid documents!"_s });
+ return;
+ }
+ if (auto envPtr = file.environment().ownerAs<DomEnvironment>())
+ envPtr->clearReferenceCache();
+
+ auto qmlFile = file.ownerAs<QmlFile>();
+ if (!qmlFile || !qmlFile->isValid()) {
+ file.iterateErrors(
+ [](const DomItem &, const ErrorMessage &msg) {
+ errorToQDebug(msg);
+ return true;
+ },
+ true);
+ guard.setError(QQmlLSUtilsErrorMessage{
+ 0, u"Failed to parse %1"_s.arg(file.canonicalFilePath()) });
+ return;
+ }
+
+ // TODO: implement formatting options
+ // For now, qmlformat's default options.
+ LineWriterOptions options;
+ options.updateOptions = LineWriterOptions::Update::None;
+ options.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+
+ QLspSpecification::TextEdit formattedText;
+ LineWriter lw([&formattedText](QStringView s) {formattedText.newText += s.toUtf8(); }, QString(), options);
+ OutWriter ow(lw);
+ file.writeOutForFile(ow, WriteOutCheck::None);
+ ow.flush();
+ const auto &code = qmlFile->code();
+ const auto [endLine, endColumn] = QQmlLSUtils::textRowAndColumnFrom(code, code.length());
+
+ Q_UNUSED(endColumn);
+ formattedText.range = QLspSpecification::Range{ QLspSpecification::Position{ 0, 0 },
+ QLspSpecification::Position{ endLine + 1, 0 } };
+
+ result.append(formattedText);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlformatting_p.h b/src/qmlls/qqmlformatting_p.h
new file mode 100644
index 0000000000..465a32e31d
--- /dev/null
+++ b/src/qmlls/qqmlformatting_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLFORMATTING_P_H
+#define QQMLFORMATTING_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlbasemodule_p.h"
+#include "qqmlcodemodel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct DocumentFormattingRequest
+ : public BaseRequest<QLspSpecification::DocumentFormattingParams,
+ QLspSpecification::Responses::DocumentFormattingResponseType>
+{
+};
+
+class QQmlDocumentFormatting : public QQmlBaseModule<DocumentFormattingRequest>
+{
+ Q_OBJECT
+public:
+ QQmlDocumentFormatting(QmlLsp::QQmlCodeModel *codeModel);
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+ void process(RequestPointerArgument req) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLFORMATTING_P_H
diff --git a/src/qmlls/qqmlgotodefinitionsupport.cpp b/src/qmlls/qqmlgotodefinitionsupport.cpp
new file mode 100644
index 0000000000..0a5579a057
--- /dev/null
+++ b/src/qmlls/qqmlgotodefinitionsupport.cpp
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmlgotodefinitionsupport_p.h"
+#include "qqmllsutils_p.h"
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+QmlGoToDefinitionSupport::QmlGoToDefinitionSupport(QmlLsp::QQmlCodeModel *codeModel)
+ : BaseT(codeModel)
+{
+}
+
+QString QmlGoToDefinitionSupport::name() const
+{
+ return u"QmlDefinitionSupport"_s;
+}
+
+void QmlGoToDefinitionSupport::setupCapabilities(
+ const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ // just assume serverCapabilities.capabilities.typeDefinitionProvider is a bool for now
+ // handle the TypeDefinitionOptions and TypeDefinitionRegistrationOptions cases later on, if
+ // needed (as they just allow more fancy go-to-type-definition action).
+ serverCapabilities.capabilities.definitionProvider = true;
+}
+
+void QmlGoToDefinitionSupport::registerHandlers(QLanguageServer *,
+ QLanguageServerProtocol *protocol)
+{
+ protocol->registerDefinitionRequestHandler(getRequestHandler());
+}
+
+void QmlGoToDefinitionSupport::process(RequestPointerArgument request)
+{
+ QList<QLspSpecification::Location> results;
+ ResponseScopeGuard guard(results, request->m_response);
+
+ auto itemsFound = itemsForRequest(request);
+
+ if (guard.setErrorFrom(itemsFound))
+ return;
+
+ QQmlLSUtilsItemLocation &front = std::get<QList<QQmlLSUtilsItemLocation>>(itemsFound).front();
+
+ auto location = QQmlLSUtils::findDefinitionOf(front.domItem);
+ if (!location)
+ return;
+
+ QLspSpecification::Location l;
+ l.uri = QUrl::fromLocalFile(location->filename).toEncoded();
+
+ QQmlJS::Dom::DomItem file = front.domItem.goToFile(location->filename);
+ auto fileOfBasePtr = file.ownerAs<QQmlJS::Dom::QmlFile>();
+ if (!fileOfBasePtr) {
+ qDebug() << "Could not find file" << location->filename << "in the dom!";
+ return;
+ }
+ const QString qmlCode = fileOfBasePtr->code();
+ l.range = QQmlLSUtils::qmlLocationToLspLocation(qmlCode, location->sourceLocation);
+
+ results.append(l);
+}
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlgotodefinitionsupport_p.h b/src/qmlls/qqmlgotodefinitionsupport_p.h
new file mode 100644
index 0000000000..d4553e3b67
--- /dev/null
+++ b/src/qmlls/qqmlgotodefinitionsupport_p.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLGOTODEFINITIONSUPPORT_P_H
+#define QQMLGOTODEFINITIONSUPPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlcodemodel_p.h"
+#include "qqmlbasemodule_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct DefinitionRequest : public BaseRequest<QLspSpecification::DefinitionParams,
+ QLspSpecification::Responses::DefinitionResponseType>
+{
+};
+
+class QmlGoToDefinitionSupport : public QQmlBaseModule<DefinitionRequest>
+{
+ Q_OBJECT
+public:
+ QmlGoToDefinitionSupport(QmlLsp::QQmlCodeModel *codeModel);
+
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+
+ void process(RequestPointerArgument request) override;
+
+ void typeDefinitionRequestHandler(const QByteArray &,
+ const QLspSpecification::DefinitionParams &params,
+ RequestPointerArgument response);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLGOTODEFINITIONSUPPORT_P_H
diff --git a/src/qmlls/qqmlgototypedefinitionsupport.cpp b/src/qmlls/qqmlgototypedefinitionsupport.cpp
new file mode 100644
index 0000000000..d8a0277a62
--- /dev/null
+++ b/src/qmlls/qqmlgototypedefinitionsupport.cpp
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmlgototypedefinitionsupport_p.h"
+#include "qqmllsutils_p.h"
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+QmlGoToTypeDefinitionSupport::QmlGoToTypeDefinitionSupport(QmlLsp::QQmlCodeModel *codeModel)
+ : BaseT(codeModel)
+{
+}
+
+QString QmlGoToTypeDefinitionSupport::name() const
+{
+ return u"QmlNavigationSupport"_s;
+}
+
+void QmlGoToTypeDefinitionSupport::setupCapabilities(
+ const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ // just assume serverCapabilities.capabilities.typeDefinitionProvider is a bool for now
+ // handle the TypeDefinitionOptions and TypeDefinitionRegistrationOptions cases later on, if
+ // needed (as they just allow more fancy go-to-type-definition action).
+ serverCapabilities.capabilities.typeDefinitionProvider = true;
+}
+
+void QmlGoToTypeDefinitionSupport::registerHandlers(QLanguageServer *,
+ QLanguageServerProtocol *protocol)
+{
+ protocol->registerTypeDefinitionRequestHandler(getRequestHandler());
+}
+
+void QmlGoToTypeDefinitionSupport::process(RequestPointerArgument request)
+{
+ QList<QLspSpecification::Location> results;
+ ResponseScopeGuard guard(results, request->m_response);
+
+ auto itemsFound = itemsForRequest(request);
+ if (guard.setErrorFrom(itemsFound))
+ return;
+
+ QQmlLSUtilsItemLocation &front = std::get<QList<QQmlLSUtilsItemLocation>>(itemsFound).front();
+
+ auto base = QQmlLSUtils::findTypeDefinitionOf(front.domItem);
+
+ if (!base) {
+ qDebug() << u"Could not obtain the base from the item"_s;
+ return;
+ }
+
+ QQmlJS::Dom::DomItem fileOfBase = front.domItem.goToFile(base->filename);
+ auto fileOfBasePtr = fileOfBase.ownerAs<QQmlJS::Dom::QmlFile>();
+ if (!fileOfBasePtr) {
+ qDebug() << u"Could not obtain the file of the base."_s;
+ return;
+ }
+
+ QLspSpecification::Location l;
+ l.uri = QUrl::fromLocalFile(fileOfBasePtr->canonicalFilePath()).toEncoded();
+
+ const QString qmlCode = fileOfBasePtr->code();
+ l.range = QQmlLSUtils::qmlLocationToLspLocation(qmlCode, base->sourceLocation);
+
+ results.append(l);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlgototypedefinitionsupport_p.h b/src/qmlls/qqmlgototypedefinitionsupport_p.h
new file mode 100644
index 0000000000..a8af07fd91
--- /dev/null
+++ b/src/qmlls/qqmlgototypedefinitionsupport_p.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMLGOTOTYPEDEFINITIONSUPPORT_P_H
+#define QMLGOTOTYPEDEFINITIONSUPPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlcodemodel_p.h"
+#include "qqmlbasemodule_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct TypeDefinitionRequest
+ : public BaseRequest<QLspSpecification::TypeDefinitionParams,
+ QLspSpecification::Responses::TypeDefinitionResponseType>
+{
+};
+
+class QmlGoToTypeDefinitionSupport : public QQmlBaseModule<TypeDefinitionRequest>
+{
+ Q_OBJECT
+public:
+ QmlGoToTypeDefinitionSupport(QmlLsp::QQmlCodeModel *codeModel);
+
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+
+ void process(RequestPointerArgument request) override;
+
+ void typeDefinitionRequestHandler(const QByteArray &,
+ const QLspSpecification::TypeDefinitionParams &params,
+ TypeDefinitionRequest::Response &&response);
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLGOTOTYPEDEFINITIONSUPPORT_P_H
diff --git a/src/qmlls/qqmlhighlightsupport.cpp b/src/qmlls/qqmlhighlightsupport.cpp
new file mode 100644
index 0000000000..55002804a0
--- /dev/null
+++ b/src/qmlls/qqmlhighlightsupport.cpp
@@ -0,0 +1,212 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qqmlhighlightsupport_p.h>
+#include <qqmlsemantictokens_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+using namespace QLspSpecification;
+using namespace QQmlJS::Dom;
+
+/*!
+\internal
+Make a list of enum names to register the supported token
+types and modifiers. It is case-sensitive in the protocol
+thus we need to lower the first characters of the enum names.
+*/
+template <typename EnumType>
+static QList<QByteArray> enumToByteArray()
+{
+ QList<QByteArray> result;
+ QMetaEnum metaEnum = QMetaEnum::fromType<EnumType>();
+ for (auto i = 0; i < metaEnum.keyCount(); ++i) {
+ auto &&enumName = QByteArray(metaEnum.key(i));
+ enumName.front() = std::tolower(enumName.front());
+ result.emplace_back(std::move(enumName));
+ }
+
+ return result;
+}
+
+static QList<QByteArray> tokenTypesList()
+{
+ return enumToByteArray<SemanticTokenTypes>();
+}
+
+static QList<QByteArray> tokenModifiersList()
+{
+ return enumToByteArray<SemanticTokenModifiers>();
+}
+
+/*!
+\internal
+A wrapper class that handles the semantic tokens request for a whole file as described in
+https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#semanticTokens_fullRequest
+Sends a QLspSpecification::SemanticTokens data as response that is generated for the entire file.
+*/
+SemanticTokenFullHandler::SemanticTokenFullHandler(QmlLsp::QQmlCodeModel *codeModel)
+ : QQmlBaseModule(codeModel)
+{
+}
+
+void SemanticTokenFullHandler::process(
+ QQmlBaseModule<SemanticTokensRequest>::RequestPointerArgument request)
+{
+ Responses::SemanticTokensResultType result;
+ ResponseScopeGuard guard(result, request->m_response);
+
+ if (!request) {
+ qCWarning(semanticTokens) << "No semantic token request is available!";
+ return;
+ }
+
+ const auto doc = m_codeModel->openDocumentByUrl(
+ QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri));
+ DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely);
+ Highlights highlights;
+ auto &&encoded = highlights.collectTokens(file, std::nullopt);
+ auto &registeredTokens = m_codeModel->registeredTokens();
+ if (!encoded.isEmpty()) {
+ HighlightingUtils::updateResultID(registeredTokens.resultId);
+ result = SemanticTokens{ registeredTokens.resultId, encoded };
+ registeredTokens.lastTokens = std::move(encoded);
+ } else {
+ result = nullptr;
+ }
+}
+
+void SemanticTokenFullHandler::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerSemanticTokensRequestHandler(getRequestHandler());
+}
+
+/*!
+\internal
+A wrapper class that handles the semantic tokens delta request for a file
+https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#semanticTokens_deltaRequest
+Sends either SemanticTokens or SemanticTokensDelta data as response.
+This is generally requested when the text document is edited after receiving full highlighting data.
+*/
+SemanticTokenDeltaHandler::SemanticTokenDeltaHandler(QmlLsp::QQmlCodeModel *codeModel)
+ : QQmlBaseModule(codeModel)
+{
+}
+
+void SemanticTokenDeltaHandler::process(
+ QQmlBaseModule<SemanticTokensDeltaRequest>::RequestPointerArgument request)
+{
+ Responses::SemanticTokensDeltaResultType result;
+ ResponseScopeGuard guard(result, request->m_response);
+
+ if (!request) {
+ qCWarning(semanticTokens) << "No semantic token request is available!";
+ return;
+ }
+ const auto doc = m_codeModel->openDocumentByUrl(
+ QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri));
+ DomItem file = doc.snapshot.validDoc.fileObject(GoTo::MostLikely);
+ Highlights highlights;
+ auto newEncoded = highlights.collectTokens(file, std::nullopt);
+ auto &registeredTokens = m_codeModel->registeredTokens();
+ const auto lastResultId = registeredTokens.resultId;
+ HighlightingUtils::updateResultID(registeredTokens.resultId);
+
+ // Return full token list if result ids not align
+ // otherwise compute the delta.
+ if (lastResultId == request->m_parameters.previousResultId) {
+ auto &&oldEncoded = registeredTokens.lastTokens;
+ QList<SemanticTokensEdit> edits = HighlightingUtils::computeDiff(oldEncoded, newEncoded);
+ result = QLspSpecification::SemanticTokensDelta{ registeredTokens.resultId, edits };
+ } else if (!newEncoded.isEmpty()){
+ result = QLspSpecification::SemanticTokens{ registeredTokens.resultId, newEncoded };
+ } else {
+ result = nullptr;
+ }
+ registeredTokens.lastTokens = newEncoded;
+}
+
+void SemanticTokenDeltaHandler::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerSemanticTokensDeltaRequestHandler(getRequestHandler());
+}
+
+/*!
+\internal
+A wrapper class that handles the semantic tokens range request for a file
+https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#semanticTokens_rangeRequest
+Sends a QLspSpecification::SemanticTokens data as response that is generated for a range of file.
+*/
+SemanticTokenRangeHandler::SemanticTokenRangeHandler(QmlLsp::QQmlCodeModel *codeModel)
+ : QQmlBaseModule(codeModel)
+{
+}
+
+void SemanticTokenRangeHandler::process(
+ QQmlBaseModule<SemanticTokensRangeRequest>::RequestPointerArgument request)
+{
+ Responses::SemanticTokensRangeResultType result;
+ ResponseScopeGuard guard(result, request->m_response);
+
+ if (!request) {
+ qCWarning(semanticTokens) << "No semantic token request is available!";
+ return;
+ }
+ const auto doc = m_codeModel->openDocumentByUrl(
+ QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri));
+ DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely);
+ const auto qmlFile = file.as<QmlFile>();
+ if (!qmlFile)
+ return;
+ const auto code = qmlFile->code();
+ const auto range = request->m_parameters.range;
+ int startOffset = int(QQmlLSUtils::textOffsetFrom(code, range.start.line, range.end.character));
+ int endOffset = int(QQmlLSUtils::textOffsetFrom(code, range.end.line, range.end.character));
+ Highlights highlights;
+ auto &&encoded = highlights.collectTokens(file, HighlightsRange{startOffset, endOffset});
+ auto &registeredTokens = m_codeModel->registeredTokens();
+ if (!encoded.isEmpty()) {
+ HighlightingUtils::updateResultID(registeredTokens.resultId);
+ result = SemanticTokens{ registeredTokens.resultId, encoded };
+ } else {
+ result = nullptr;
+ }
+}
+
+void SemanticTokenRangeHandler::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerSemanticTokensRangeRequestHandler(getRequestHandler());
+}
+
+QQmlHighlightSupport::QQmlHighlightSupport(QmlLsp::QQmlCodeModel *codeModel)
+ : m_full(codeModel), m_delta(codeModel), m_range(codeModel)
+{
+}
+
+QString QQmlHighlightSupport::name() const
+{
+ return "QQmlHighlightSupport"_L1;
+}
+
+void QQmlHighlightSupport::registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol)
+{
+ m_full.registerHandlers(server, protocol);
+ m_delta.registerHandlers(server, protocol);
+ m_range.registerHandlers(server, protocol);
+}
+
+void QQmlHighlightSupport::setupCapabilities(
+ const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ QLspSpecification::SemanticTokensOptions options;
+ options.range = true;
+ options.full = QJsonObject({ { u"delta"_s, true } });
+ options.legend.tokenTypes = tokenTypesList();
+ options.legend.tokenModifiers = tokenModifiersList();
+
+ serverCapabilities.capabilities.semanticTokensProvider = options;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlhighlightsupport_p.h b/src/qmlls/qqmlhighlightsupport_p.h
new file mode 100644
index 0000000000..ef84b94ef3
--- /dev/null
+++ b/src/qmlls/qqmlhighlightsupport_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLHIGHLIGHTSUPPORT_P_H
+#define QQMLHIGHLIGHTSUPPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlbasemodule_p.h"
+#include "qqmlcodemodel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// We don't need these overrides as we register the request handlers in a single
+// module QQmlHighlightSupport. This is an unusual pattern because QQmlBaseModule
+// and QLanguageServerModule abstractions are designed to handle a single module
+// which has a single request handlers. That is not the case for the semanticTokens
+// module which has a one server module but also has three different handlers.
+#define HIDE_UNUSED_OVERRIDES \
+ private: \
+ QString name() const override \
+ { \
+ return {}; \
+ } \
+ void setupCapabilities(const QLspSpecification::InitializeParams &, \
+ QLspSpecification::InitializeResult &) override \
+ { \
+ }
+
+using SemanticTokensRequest = BaseRequest<QLspSpecification::SemanticTokensParams,
+ QLspSpecification::Responses::SemanticTokensResponseType>;
+
+using SemanticTokensDeltaRequest =
+ BaseRequest<QLspSpecification::SemanticTokensDeltaParams,
+ QLspSpecification::Responses::SemanticTokensDeltaResponseType>;
+
+using SemanticTokensRangeRequest =
+ BaseRequest<QLspSpecification::SemanticTokensRangeParams,
+ QLspSpecification::Responses::SemanticTokensRangeResponseType>;
+
+class SemanticTokenFullHandler : public QQmlBaseModule<SemanticTokensRequest>
+{
+public:
+ SemanticTokenFullHandler(QmlLsp::QQmlCodeModel *codeModel);
+ void process(QQmlBaseModule<SemanticTokensRequest>::RequestPointerArgument req) override;
+ void registerHandlers(QLanguageServer *, QLanguageServerProtocol *) override;
+ HIDE_UNUSED_OVERRIDES
+};
+
+class SemanticTokenDeltaHandler : public QQmlBaseModule<SemanticTokensDeltaRequest>
+{
+public:
+ SemanticTokenDeltaHandler(QmlLsp::QQmlCodeModel *codeModel);
+ void process(QQmlBaseModule<SemanticTokensDeltaRequest>::RequestPointerArgument req) override;
+ void registerHandlers(QLanguageServer *, QLanguageServerProtocol *) override;
+ HIDE_UNUSED_OVERRIDES
+};
+
+class SemanticTokenRangeHandler : public QQmlBaseModule<SemanticTokensRangeRequest>
+{
+public:
+ SemanticTokenRangeHandler(QmlLsp::QQmlCodeModel *codeModel);
+ void process(QQmlBaseModule<SemanticTokensRangeRequest>::RequestPointerArgument req) override;
+ void registerHandlers(QLanguageServer *, QLanguageServerProtocol *) override;
+ HIDE_UNUSED_OVERRIDES
+};
+
+class QQmlHighlightSupport : public QLanguageServerModule
+{
+public:
+ QQmlHighlightSupport(QmlLsp::QQmlCodeModel *codeModel);
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+private:
+ SemanticTokenFullHandler m_full;
+ SemanticTokenDeltaHandler m_delta;
+ SemanticTokenRangeHandler m_range;
+};
+
+#undef HIDE_UNUSED_OVERRIDES
+
+QT_END_NAMESPACE
+
+#endif // QQMLHIGHLIGHTSUPPORT_P_H
diff --git a/src/qmlls/qqmlhover.cpp b/src/qmlls/qqmlhover.cpp
new file mode 100644
index 0000000000..4f604558a3
--- /dev/null
+++ b/src/qmlls/qqmlhover.cpp
@@ -0,0 +1,81 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qqmlhover_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(hoverLog, "qt.languageserver.hover")
+
+QQmlHover::QQmlHover(QmlLsp::QQmlCodeModel *codeModel)
+ : QQmlBaseModule(codeModel)
+{
+}
+
+QString QQmlHover::name() const
+{
+ return u"QQmlHover"_s;
+}
+
+void QQmlHover::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerHoverRequestHandler(getRequestHandler());
+}
+
+void QQmlHover::setupCapabilities(
+ const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ serverCapabilities.capabilities.hoverProvider = true;
+}
+
+void QQmlHover::process(RequestPointerArgument request)
+{
+ using namespace QQmlJS::Dom;
+ QLspSpecification::Hover result;
+ ResponseScopeGuard guard(result, request->m_response);
+
+ if (!request) {
+ qCWarning(hoverLog) << "No hover information is available!";
+ return;
+ }
+
+ const auto textDocument = request->m_parameters.textDocument;
+ const auto [hoveredLine, hoveredCharacter] = request->m_parameters.position;
+ qCDebug(hoverLog) << QStringLiteral("Hovered (line, col): (%1,%2)").arg(hoveredLine).arg(hoveredCharacter);
+
+ const auto doc = m_codeModel->openDocumentByUrl(
+ QQmlLSUtils::lspUriToQmlUrl(textDocument.uri));
+
+ DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely);
+ if (!file) {
+ guard.setError(QQmlLSUtilsErrorMessage{
+ 0, u"Could not find the file %1"_s.arg(doc.snapshot.doc.canonicalFilePath()) });
+ return;
+ }
+
+ // TODO: Fetch the actual documentation or other possible infos to be shown when hovered.
+ // Early return if hovered element is not identifier kind (for example, don't perform anything on paranthesis)
+
+ const auto documentation = QQmlLSUtils::getDocumentationFromLocation(
+ file, { hoveredLine + 1, hoveredCharacter + 1 });
+ if (documentation.isEmpty()) {
+ qCDebug(hoverLog)
+ << QStringLiteral(
+ "No documentation hints found for the item at (line, col): (%1,%2)")
+ .arg(hoveredLine)
+ .arg(hoveredCharacter);
+ return;
+ }
+
+ QLspSpecification::MarkupContent content;
+
+ // TODO: This should eventually be a Markdown kind.
+ // We will do post-formatting what we fetch from documentation.
+ content.kind = QLspSpecification::MarkupKind::PlainText;
+ content.value = documentation;
+
+ result.contents = content;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlhover_p.h b/src/qmlls/qqmlhover_p.h
new file mode 100644
index 0000000000..6d3fa59c62
--- /dev/null
+++ b/src/qmlls/qqmlhover_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLHOVER_P_H
+#define QQMLHOVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlbasemodule_p.h"
+#include "qqmlcodemodel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct HoverRequest
+ : public BaseRequest<QLspSpecification::HoverParams,
+ QLspSpecification::Responses::HoverResponseType>
+{
+};
+
+class QQmlHover : public QQmlBaseModule<HoverRequest>
+{
+ Q_OBJECT
+public:
+ QQmlHover(QmlLsp::QQmlCodeModel *codeModel);
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+ void process(RequestPointerArgument req) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLHOVER_P_H
diff --git a/src/qmlls/qqmllanguageserver.cpp b/src/qmlls/qqmllanguageserver.cpp
new file mode 100644
index 0000000000..1ef142b7b5
--- /dev/null
+++ b/src/qmlls/qqmllanguageserver.cpp
@@ -0,0 +1,185 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmllanguageserver_p.h"
+#include "qtextsynchronization_p.h"
+#include "qlanguageserver_p.h"
+#include "qlspcustomtypes_p.h"
+
+#include <QtCore/qdir.h>
+
+#include <iostream>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+namespace QmlLsp {
+
+using namespace QLspSpecification;
+using namespace Qt::StringLiterals;
+/*!
+\internal
+\class QmlLsp::QQmlLanguageServer
+\brief Sets up a QmlLanguageServer.
+
+This class sets up a QML language server.
+
+Use the following function to send replies:
+
+\code
+std::function<void(const QByteArray &)> sendData
+\endcode
+
+And, feed the data that the function receives to the \c {server()->receive()}
+method.
+
+Call this method only from a single thread, and do not block. To achieve this,
+avoid direct calls, and connect the method as a slot, while reading from another
+thread.
+
+The various tasks of the language server are divided between
+QLanguageServerModule instances. Each instance is responsible for handling a
+certain subset of client requests. For example, one instance handles completion
+requests, another one updates the code in the code model when the client sends a
+new file version, and so on. The QLanguageServerModule instances are
+constructed and registered with QLanguageServer in the constructor of
+this class.
+
+Generally, do all operations in the object thread and always call handlers from
+it. However, the operations can delegate the response to another thread, as the
+response handler is thread safe. All the methods of the \c server() object are
+also thread safe.
+
+The code model starts other threads to update its state. See its documentation
+for more information.
+*/
+QQmlLanguageServer::QQmlLanguageServer(std::function<void(const QByteArray &)> sendData,
+ QQmlToolingSettings *settings)
+ : m_codeModel(nullptr, settings),
+ m_server(sendData),
+ m_textSynchronization(&m_codeModel),
+ m_lint(&m_server, &m_codeModel),
+ m_workspace(&m_codeModel),
+ m_completionSupport(&m_codeModel),
+ m_navigationSupport(&m_codeModel),
+ m_definitionSupport(&m_codeModel),
+ m_referencesSupport(&m_codeModel),
+ m_documentFormatting(&m_codeModel),
+ m_renameSupport(&m_codeModel),
+ m_rangeFormatting(&m_codeModel),
+ m_hover(&m_codeModel),
+ m_highlightSupport(&m_codeModel)
+{
+ m_server.addServerModule(this);
+ m_server.addServerModule(&m_textSynchronization);
+ m_server.addServerModule(&m_lint);
+ m_server.addServerModule(&m_workspace);
+ m_server.addServerModule(&m_completionSupport);
+ m_server.addServerModule(&m_navigationSupport);
+ m_server.addServerModule(&m_definitionSupport);
+ m_server.addServerModule(&m_referencesSupport);
+ m_server.addServerModule(&m_documentFormatting);
+ m_server.addServerModule(&m_renameSupport);
+ m_server.addServerModule(&m_rangeFormatting);
+ m_server.addServerModule(&m_hover);
+ m_server.addServerModule(&m_highlightSupport);
+ m_server.finishSetup();
+ qCWarning(lspServerLog) << "Did Setup";
+}
+
+void QQmlLanguageServer::registerHandlers(QLanguageServer *server,
+ QLanguageServerProtocol *protocol)
+{
+ Q_UNUSED(protocol);
+ QObject::connect(server, &QLanguageServer::lifecycleError, this,
+ &QQmlLanguageServer::errorExit);
+ QObject::connect(server, &QLanguageServer::exit, this, &QQmlLanguageServer::exit);
+ QObject::connect(server, &QLanguageServer::runStatusChanged, this, [](QLanguageServer::RunStatus r) {
+ qCDebug(lspServerLog) << "runStatus" << int(r);
+ });
+ protocol->typedRpc()->registerNotificationHandler<Notifications::AddBuildDirsParams>(
+ QByteArray(Notifications::AddBuildDirsMethod),
+ [this](const QByteArray &, const Notifications::AddBuildDirsParams &params) {
+ for (const auto &buildDirs : params.buildDirsToSet) {
+ QStringList dirPaths;
+ dirPaths.resize(buildDirs.buildDirs.size());
+ std::transform(buildDirs.buildDirs.begin(), buildDirs.buildDirs.end(),
+ dirPaths.begin(), [](const QByteArray &utf8Str) {
+ return QString::fromUtf8(utf8Str);
+ });
+ m_codeModel.setBuildPathsForRootUrl(buildDirs.baseUri, dirPaths);
+ }
+ });
+}
+
+void QQmlLanguageServer::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &serverInfo)
+{
+ QJsonObject expCap;
+ if (serverInfo.capabilities.experimental.has_value() && serverInfo.capabilities.experimental->isObject())
+ expCap = serverInfo.capabilities.experimental->toObject();
+ expCap.insert(u"addBuildDirs"_s, QJsonObject({ { u"supported"_s, true } }));
+ serverInfo.capabilities.experimental = expCap;
+
+ if (clientInfo.workspaceFolders) {
+ if (auto workspaceList =
+ std::get_if<QList<WorkspaceFolder>>(&*clientInfo.workspaceFolders)) {
+ QList<QByteArray> workspaceUris;
+ std::transform(workspaceList->cbegin(), workspaceList->cend(),
+ std::back_inserter(workspaceUris),
+ [](const auto &workspaceFolder) { return workspaceFolder.uri; });
+ m_codeModel.setRootUrls(workspaceUris);
+ }
+ }
+}
+
+QString QQmlLanguageServer::name() const
+{
+ return u"QQmlLanguageServer"_s;
+}
+
+void QQmlLanguageServer::errorExit()
+{
+ qCWarning(lspServerLog) << "Error exit";
+ fclose(stdin);
+}
+
+void QQmlLanguageServer::exit()
+{
+ m_returnValue = 0;
+ fclose(stdin);
+}
+
+int QQmlLanguageServer::returnValue() const
+{
+ return m_returnValue;
+}
+
+QQmlCodeModel *QQmlLanguageServer::codeModel()
+{
+ return &m_codeModel;
+}
+
+QLanguageServer *QQmlLanguageServer::server()
+{
+ return &m_server;
+}
+
+TextSynchronization *QQmlLanguageServer::textSynchronization()
+{
+ return &m_textSynchronization;
+}
+
+QmlLintSuggestions *QQmlLanguageServer::lint()
+{
+ return &m_lint;
+}
+
+WorkspaceHandlers *QQmlLanguageServer::worspace()
+{
+ return &m_workspace;
+}
+
+} // namespace QmlLsp
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmllanguageserver_p.h b/src/qmlls/qqmllanguageserver_p.h
new file mode 100644
index 0000000000..3347acd670
--- /dev/null
+++ b/src/qmlls/qqmllanguageserver_p.h
@@ -0,0 +1,83 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLLANGUAGESERVER_P_H
+#define QQMLLANGUAGESERVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlcodemodel_p.h"
+#include "qqmlfindusagessupport_p.h"
+#include "qtextsynchronization_p.h"
+#include "qqmllintsuggestions_p.h"
+#include "qworkspace_p.h"
+#include "qqmlcompletionsupport_p.h"
+#include "qqmlgototypedefinitionsupport_p.h"
+#include "qqmlformatting_p.h"
+#include "qqmlrangeformatting_p.h"
+#include "qqmlgotodefinitionsupport_p.h"
+#include "qqmlrenamesymbolsupport_p.h"
+#include "qqmlhover_p.h"
+#include "qqmlhighlightsupport_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQmlToolingSettings;
+
+namespace QmlLsp {
+
+class QQmlLanguageServer : public QLanguageServerModule
+{
+ Q_OBJECT
+public:
+ QQmlLanguageServer(std::function<void(const QByteArray &)> sendData,
+ QQmlToolingSettings *settings = nullptr);
+
+ QString name() const final;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) final;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &serverInfo) final;
+
+ int returnValue() const;
+
+ QQmlCodeModel *codeModel();
+ QLanguageServer *server();
+ TextSynchronization *textSynchronization();
+ QmlLintSuggestions *lint();
+ WorkspaceHandlers *worspace();
+
+public Q_SLOTS:
+ void exit();
+ void errorExit();
+
+private:
+ QQmlCodeModel m_codeModel;
+ QLanguageServer m_server;
+ TextSynchronization m_textSynchronization;
+ QmlLintSuggestions m_lint;
+ WorkspaceHandlers m_workspace;
+ QmlCompletionSupport m_completionSupport;
+ QmlGoToTypeDefinitionSupport m_navigationSupport;
+ QmlGoToDefinitionSupport m_definitionSupport;
+ QQmlFindUsagesSupport m_referencesSupport;
+ QQmlDocumentFormatting m_documentFormatting;
+ QQmlRenameSymbolSupport m_renameSupport;
+ QQmlRangeFormatting m_rangeFormatting;
+ QQmlHover m_hover;
+ QQmlHighlightSupport m_highlightSupport;
+ int m_returnValue = 1;
+};
+
+} // namespace QmlLsp
+QT_END_NAMESPACE
+#endif // QQMLLANGUAGESERVER_P_H
diff --git a/src/qmlls/qqmllintsuggestions.cpp b/src/qmlls/qqmllintsuggestions.cpp
new file mode 100644
index 0000000000..ab12195b4d
--- /dev/null
+++ b/src/qmlls/qqmllintsuggestions.cpp
@@ -0,0 +1,378 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmllintsuggestions_p.h"
+
+#include <QtLanguageServer/private/qlanguageserverspec_p.h>
+#include <QtQmlCompiler/private/qqmljslinter_p.h>
+#include <QtQmlCompiler/private/qqmljslogger_p.h>
+#include <QtQmlDom/private/qqmldom_utils_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qxpfunctional.h>
+#include <chrono>
+
+using namespace QLspSpecification;
+using namespace QQmlJS::Dom;
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lintLog, "qt.languageserver.lint")
+
+QT_BEGIN_NAMESPACE
+namespace QmlLsp {
+
+static DiagnosticSeverity severityFromMsgType(QtMsgType t)
+{
+ switch (t) {
+ case QtDebugMsg:
+ return DiagnosticSeverity::Hint;
+ case QtInfoMsg:
+ return DiagnosticSeverity::Information;
+ case QtWarningMsg:
+ return DiagnosticSeverity::Warning;
+ case QtCriticalMsg:
+ case QtFatalMsg:
+ break;
+ }
+ return DiagnosticSeverity::Error;
+}
+
+static void codeActionHandler(
+ const QByteArray &, const CodeActionParams &params,
+ LSPPartialResponse<std::variant<QList<std::variant<Command, CodeAction>>, std::nullptr_t>,
+ QList<std::variant<Command, CodeAction>>> &&response)
+{
+ QList<std::variant<Command, CodeAction>> responseData;
+
+ for (const Diagnostic &diagnostic : params.context.diagnostics) {
+ if (!diagnostic.data.has_value())
+ continue;
+
+ const auto &data = diagnostic.data.value();
+
+ int version = data[u"version"].toInt();
+ QJsonArray suggestions = data[u"suggestions"].toArray();
+
+ QList<WorkspaceEdit::DocumentChange> edits;
+ QString message;
+ for (const QJsonValue &suggestion : suggestions) {
+ QString replacement = suggestion[u"replacement"].toString();
+ message += suggestion[u"message"].toString() + u"\n";
+
+ TextEdit textEdit;
+ textEdit.range = { Position { suggestion[u"lspBeginLine"].toInt(),
+ suggestion[u"lspBeginCharacter"].toInt() },
+ Position { suggestion[u"lspEndLine"].toInt(),
+ suggestion[u"lspEndCharacter"].toInt() } };
+ textEdit.newText = replacement.toUtf8();
+
+ TextDocumentEdit textDocEdit;
+ textDocEdit.textDocument = { params.textDocument, version };
+ textDocEdit.edits.append(textEdit);
+
+ edits.append(textDocEdit);
+ }
+ message.chop(1);
+ WorkspaceEdit edit;
+ edit.documentChanges = edits;
+
+ CodeAction action;
+ // VS Code and QtC ignore everything that is not a 'quickfix'.
+ action.kind = u"quickfix"_s.toUtf8();
+ action.edit = edit;
+ action.title = message.toUtf8();
+
+ responseData.append(action);
+ }
+
+ response.sendResponse(responseData);
+}
+
+void QmlLintSuggestions::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerCodeActionRequestHandler(&codeActionHandler);
+}
+
+void QmlLintSuggestions::setupCapabilities(const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverInfo)
+{
+ serverInfo.capabilities.codeActionProvider = true;
+}
+
+QmlLintSuggestions::QmlLintSuggestions(QLanguageServer *server, QmlLsp::QQmlCodeModel *codeModel)
+ : m_server(server), m_codeModel(codeModel)
+{
+ QObject::connect(m_codeModel, &QmlLsp::QQmlCodeModel::updatedSnapshot, this,
+ &QmlLintSuggestions::diagnose, Qt::DirectConnection);
+}
+
+static void advancePositionPastLocation_helper(const QString &fileContents, const QQmlJS::SourceLocation &location, Position &position) {
+ const int startOffset = location.offset;
+ const int length = location.length;
+ int i = startOffset;
+ int iEnd = i + length;
+ if (iEnd > int(fileContents.size()))
+ iEnd = fileContents.size();
+ while (i < iEnd) {
+ if (fileContents.at(i) == u'\n') {
+ ++position.line;
+ position.character = 0;
+ if (i + 1 < iEnd && fileContents.at(i) == u'\r')
+ ++i;
+ } else {
+ ++position.character;
+ }
+ ++i;
+ }
+};
+
+static Diagnostic createMissingBuildDirDiagnostic()
+{
+ Diagnostic diagnostic;
+ diagnostic.severity = DiagnosticSeverity::Warning;
+ Range &range = diagnostic.range;
+ Position &position = range.start;
+ position.line = 0;
+ position.character = 0;
+ Position &positionEnd = range.end;
+ positionEnd.line = 1;
+ diagnostic.message =
+ "qmlls could not find a build directory, without a build directory "
+ "containing a current build there could be spurious warnings, you might "
+ "want to pass the --build-dir <buildDir> option to qmlls, or set the "
+ "environment variable QMLLS_BUILD_DIRS.";
+ diagnostic.source = QByteArray("qmllint");
+ return diagnostic;
+}
+
+using AdvanceFunc = qxp::function_ref<void(const QQmlJS::SourceLocation &, Position &)>;
+static Diagnostic messageToDiagnostic_helper(AdvanceFunc advancePositionPastLocation,
+ std::optional<int> version, const Message &message)
+{
+ Diagnostic diagnostic;
+ diagnostic.severity = severityFromMsgType(message.type);
+ Range &range = diagnostic.range;
+ Position &position = range.start;
+
+ QQmlJS::SourceLocation srcLoc = message.loc;
+
+ if (srcLoc.isValid()) {
+ position.line = srcLoc.startLine - 1;
+ position.character = srcLoc.startColumn - 1;
+ range.end = position;
+ advancePositionPastLocation(message.loc, range.end);
+ }
+
+ if (message.fixSuggestion && !message.fixSuggestion->fixDescription().isEmpty()) {
+ diagnostic.message = QString(message.message)
+ .append(u": "_s)
+ .append(message.fixSuggestion->fixDescription())
+ .simplified()
+ .toUtf8();
+ } else {
+ diagnostic.message = message.message.toUtf8();
+ }
+
+ diagnostic.source = QByteArray("qmllint");
+
+ auto suggestion = message.fixSuggestion;
+ if (!suggestion.has_value())
+ return diagnostic;
+
+ // We need to interject the information about where the fix suggestions end
+ // here since we don't have access to the textDocument to calculate it later.
+ const QQmlJS::SourceLocation cut = suggestion->location();
+
+ const int line = cut.isValid() ? cut.startLine - 1 : 0;
+ const int column = cut.isValid() ? cut.startColumn - 1 : 0;
+
+ QJsonObject object;
+ object.insert("lspBeginLine"_L1, line);
+ object.insert("lspBeginCharacter"_L1, column);
+
+ Position end = { line, column };
+
+ if (srcLoc.isValid())
+ advancePositionPastLocation(cut, end);
+ object.insert("lspEndLine"_L1, end.line);
+ object.insert("lspEndCharacter"_L1, end.character);
+
+ object.insert("message"_L1, suggestion->fixDescription());
+ object.insert("replacement"_L1, suggestion->replacement());
+
+ QJsonArray fixedSuggestions;
+ fixedSuggestions.append(object);
+ QJsonObject data;
+ data[u"suggestions"] = fixedSuggestions;
+
+ Q_ASSERT(version.has_value());
+ data[u"version"] = version.value();
+
+ diagnostic.data = data;
+
+ return diagnostic;
+};
+
+static bool isSnapshotNew(std::optional<int> snapshotVersion, std::optional<int> processedVersion)
+{
+ if (!snapshotVersion)
+ return false;
+ if (!processedVersion || *snapshotVersion > *processedVersion)
+ return true;
+ return false;
+}
+
+using namespace std::chrono_literals;
+
+QmlLintSuggestions::VersionToDiagnose
+QmlLintSuggestions::chooseVersionToDiagnoseHelper(const QByteArray &url)
+{
+ const std::chrono::milliseconds maxInvalidTime = 400ms;
+ QmlLsp::OpenDocumentSnapshot snapshot = m_codeModel->snapshotByUrl(url);
+
+ LastLintUpdate &lastUpdate = m_lastUpdate[url];
+
+ // ignore updates when already processed
+ if (lastUpdate.version && *lastUpdate.version == snapshot.docVersion) {
+ qCDebug(lspServerLog) << "skipped update of " << url << "unchanged valid doc";
+ return NoDocumentAvailable{};
+ }
+
+ // try out a valid version, if there is one
+ if (isSnapshotNew(snapshot.validDocVersion, lastUpdate.version))
+ return VersionedDocument{ snapshot.validDocVersion, snapshot.validDoc };
+
+ // try out an invalid version, if there is one
+ if (isSnapshotNew(snapshot.docVersion, lastUpdate.version)) {
+ if (auto since = lastUpdate.invalidUpdatesSince) {
+ // did we wait enough to get a valid document?
+ if (std::chrono::steady_clock::now() - *since > maxInvalidTime) {
+ return VersionedDocument{ snapshot.docVersion, snapshot.doc };
+ }
+ } else {
+ // first time hitting the invalid document:
+ lastUpdate.invalidUpdatesSince = std::chrono::steady_clock::now();
+ }
+
+ // wait some time for extra keystrokes before diagnose
+ return TryAgainLater{ maxInvalidTime };
+ }
+ return NoDocumentAvailable{};
+}
+
+QmlLintSuggestions::VersionToDiagnose
+QmlLintSuggestions::chooseVersionToDiagnose(const QByteArray &url)
+{
+ QMutexLocker l(&m_mutex);
+ auto versionToDiagnose = chooseVersionToDiagnoseHelper(url);
+ if (auto versionedDocument = std::get_if<VersionedDocument>(&versionToDiagnose)) {
+ // update immediately, and do not keep track of sent version, thus in extreme cases sent
+ // updates could be out of sync
+ LastLintUpdate &lastUpdate = m_lastUpdate[url];
+ lastUpdate.version = versionedDocument->version;
+ lastUpdate.invalidUpdatesSince.reset();
+ }
+ return versionToDiagnose;
+}
+
+void QmlLintSuggestions::diagnose(const QByteArray &url)
+{
+ auto versionedDocument = chooseVersionToDiagnose(url);
+
+ std::visit(qOverloadedVisitor{
+ [](NoDocumentAvailable) {},
+ [this, &url](const TryAgainLater &tryAgainLater) {
+ QTimer::singleShot(tryAgainLater.time, Qt::VeryCoarseTimer, this,
+ [this, url]() { diagnose(url); });
+ },
+ [this, &url](const VersionedDocument &versionedDocument) {
+ diagnoseHelper(url, versionedDocument);
+ },
+
+ },
+ versionedDocument);
+}
+
+void QmlLintSuggestions::diagnoseHelper(const QByteArray &url,
+ const VersionedDocument &versionedDocument)
+{
+ auto [version, doc] = versionedDocument;
+
+ PublishDiagnosticsParams diagnosticParams;
+ diagnosticParams.uri = url;
+ diagnosticParams.version = version;
+
+ qCDebug(lintLog) << "has doc, do real lint";
+ QStringList imports = m_codeModel->buildPathsForFileUrl(url);
+ imports.append(m_codeModel->importPaths());
+ const QString filename = doc.canonicalFilePath();
+ // add source directory as last import as fallback in case there is no qmldir in the build
+ // folder this mimics qmllint behaviors
+ imports.append(QFileInfo(filename).dir().absolutePath());
+ // add m_server->clientInfo().rootUri & co?
+ bool silent = true;
+ const QString fileContents = doc.field(Fields::code).value().toString();
+ const QStringList qmltypesFiles;
+ const QStringList resourceFiles = resourceFilesFromBuildFolders(imports);
+
+ QList<QQmlJS::LoggerCategory> categories;
+
+ QQmlJSLinter linter(imports);
+
+ linter.lintFile(filename, &fileContents, silent, nullptr, imports, qmltypesFiles,
+ resourceFiles, categories);
+
+ // ### TODO: C++20 replace with bind_front
+ auto advancePositionPastLocation = [&fileContents](const QQmlJS::SourceLocation &location, Position &position)
+ {
+ advancePositionPastLocation_helper(fileContents, location, position);
+ };
+ auto messageToDiagnostic = [&advancePositionPastLocation,
+ versionedDocument](const Message &message) {
+ return messageToDiagnostic_helper(advancePositionPastLocation, versionedDocument.version,
+ message);
+ };
+
+ QList<Diagnostic> diagnostics;
+ doc.iterateErrors(
+ [&diagnostics, &advancePositionPastLocation](const DomItem &, const ErrorMessage &msg) {
+ Diagnostic diagnostic;
+ diagnostic.severity = severityFromMsgType(QtMsgType(int(msg.level)));
+ // do something with msg.errorGroups ?
+ auto &location = msg.location;
+ Range &range = diagnostic.range;
+ range.start.line = location.startLine - 1;
+ range.start.character = location.startColumn - 1;
+ range.end = range.start;
+ advancePositionPastLocation(location, range.end);
+ diagnostic.code = QByteArray(msg.errorId.data(), msg.errorId.size());
+ diagnostic.source = "domParsing";
+ diagnostic.message = msg.message.toUtf8();
+ diagnostics.append(diagnostic);
+ return true;
+ },
+ true);
+
+ if (const QQmlJSLogger *logger = linter.logger()) {
+ qsizetype nDiagnostics = diagnostics.size();
+ for (const auto &messages : { logger->infos(), logger->warnings(), logger->errors() })
+ for (const Message &message : messages)
+ diagnostics.append(messageToDiagnostic(message));
+ if (diagnostics.size() != nDiagnostics && imports.size() == 1)
+ diagnostics.append(createMissingBuildDirDiagnostic());
+ }
+
+ diagnosticParams.diagnostics = diagnostics;
+
+ m_server->protocol()->notifyPublishDiagnostics(diagnosticParams);
+ qCDebug(lintLog) << "lint" << QString::fromUtf8(url) << "found"
+ << diagnosticParams.diagnostics.size() << "issues"
+ << QTypedJson::toJsonValue(diagnosticParams);
+}
+
+} // namespace QmlLsp
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmllintsuggestions_p.h b/src/qmlls/qqmllintsuggestions_p.h
new file mode 100644
index 0000000000..28ab3ccb5d
--- /dev/null
+++ b/src/qmlls/qqmllintsuggestions_p.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMLLINTSUGGESTIONS_P_H
+#define QMLLINTSUGGESTIONS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlcodemodel_p.h"
+
+#include <chrono>
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+namespace QmlLsp {
+struct LastLintUpdate
+{
+ std::optional<int> version;
+ std::optional<std::chrono::steady_clock::time_point> invalidUpdatesSince;
+};
+
+class QmlLintSuggestions : public QLanguageServerModule
+{
+ Q_OBJECT
+public:
+ QmlLintSuggestions(QLanguageServer *server, QmlLsp::QQmlCodeModel *codeModel);
+
+ QString name() const override { return QLatin1StringView("QmlLint Suggestions"); }
+public Q_SLOTS:
+ void diagnose(const QByteArray &uri);
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+
+private:
+ struct VersionedDocument
+ {
+ std::optional<int> version;
+ QQmlJS::Dom::DomItem item;
+ };
+ struct TryAgainLater
+ {
+ std::chrono::milliseconds time;
+ };
+ struct NoDocumentAvailable
+ {
+ };
+
+ using VersionToDiagnose = std::variant<VersionedDocument, TryAgainLater, NoDocumentAvailable>;
+
+ VersionToDiagnose chooseVersionToDiagnose(const QByteArray &url);
+ VersionToDiagnose chooseVersionToDiagnoseHelper(const QByteArray &url);
+ void diagnoseHelper(const QByteArray &uri, const VersionedDocument &document);
+
+ QMutex m_mutex;
+ QHash<QByteArray, LastLintUpdate> m_lastUpdate;
+ QLanguageServer *m_server;
+ QmlLsp::QQmlCodeModel *m_codeModel;
+};
+} // namespace QmlLsp
+QT_END_NAMESPACE
+#endif // QMLLINTSUGGESTIONS_P_H
diff --git a/src/qmlls/qqmllscompletion.cpp b/src/qmlls/qqmllscompletion.cpp
new file mode 100644
index 0000000000..8ecbcffc70
--- /dev/null
+++ b/src/qmlls/qqmllscompletion.cpp
@@ -0,0 +1,1888 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmllscompletion_p.h"
+
+using namespace QLspSpecification;
+using namespace QQmlJS::Dom;
+using namespace Qt::StringLiterals;
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QQmlLSCompletionLog, "qt.languageserver.completions")
+
+/*!
+\class QQmlLSCompletion
+\internal
+\brief QQmlLSCompletion provides completions for all kinds of QML and JS constructs.
+
+Use the \l{completions} method to obtain completions at a certain DomItem.
+
+All the other methods in this class are helper methods: some compute completions for specific QML
+and JS constructs and some are shared between multiple QML or JS constructs to avoid code
+duplication. Most of the helper methods add their completion items via a BackInsertIterator.
+
+Some helper methods are called "suggest*" and will try to suggest code that does not exist yet. For
+example, any JS statement can be expected inside a Blockstatement so suggestJSStatementCompletion()
+is used to suggest JS statements inside of BlockStatements. Another example might be
+suggestReachableTypes() that will suggest Types for type annotations, attached types or Qml Object
+hierarchies, or suggestCaseAndDefaultStatementCompletion() that will only suggest "case" and
+"default" clauses for switch statements.
+
+Some helper methods are called "inside*" and will try to suggest code inside an existing structure.
+For example, insideForStatementCompletion() will try to suggest completion for the different code
+pieces initializer, condition, increment and statement that exist inside of:
+\badcode
+for(initializer; condition; increment)
+ statement
+\endcode
+*/
+
+CompletionItem QQmlLSCompletion::makeSnippet(QUtf8StringView qualifier, QUtf8StringView label,
+ QUtf8StringView insertText)
+{
+ CompletionItem res;
+ if (!qualifier.isEmpty()) {
+ res.label = qualifier.data();
+ res.label += '.';
+ }
+ res.label += label.data();
+ res.insertTextFormat = InsertTextFormat::Snippet;
+ if (!qualifier.isEmpty()) {
+ res.insertText = qualifier.data();
+ *res.insertText += '.';
+ *res.insertText += insertText.data();
+ } else {
+ res.insertText = insertText.data();
+ }
+ res.kind = int(CompletionItemKind::Snippet);
+ res.insertTextMode = InsertTextMode::AdjustIndentation;
+ return res;
+}
+
+CompletionItem QQmlLSCompletion::makeSnippet(QUtf8StringView label, QUtf8StringView insertText)
+{
+ return makeSnippet(QByteArray(), label, insertText);
+}
+
+/*!
+\internal
+\brief Compare left and right locations to the position denoted by ctx, see special cases below.
+
+Statements and expressions need to provide different completions depending on where the cursor is.
+For example, lets take following for-statement:
+\badcode
+for (let i = 0; <here> ; ++i) {}
+\endcode
+We want to provide script expression completion (method names, property names, available JS
+variables names, QML objects ids, and so on) at the place denoted by \c{<here>}.
+The question is: how do we know that the cursor is really at \c{<here>}? In the case of the
+for-loop, we can compare the position of the cursor with the first and the second semicolon of the
+for loop.
+
+If the first semicolon does not exist, it has an invalid sourcelocation and the cursor is
+definitively \e{not} at \c{<here>}. Therefore, return false when \c{left} is invalid.
+
+If the second semicolon does not exist, then just ignore it: it might not have been written yet.
+*/
+bool QQmlLSCompletion::betweenLocations(QQmlJS::SourceLocation left,
+ const QQmlLSCompletionPosition &positionInfo,
+ QQmlJS::SourceLocation right) const
+{
+ if (!left.isValid())
+ return false;
+ // note: left.end() == ctx.offset() means that the cursor lies exactly after left
+ if (!(left.end() <= positionInfo.offset()))
+ return false;
+ if (!right.isValid())
+ return true;
+
+ // note: ctx.offset() == right.begin() means that the cursor lies exactly before right
+ return positionInfo.offset() <= right.begin();
+}
+
+/*!
+\internal
+Returns true if ctx denotes an offset lying behind left.end(), and false otherwise.
+*/
+bool QQmlLSCompletion::afterLocation(QQmlJS::SourceLocation left,
+ const QQmlLSCompletionPosition &positionInfo) const
+{
+ return betweenLocations(left, positionInfo, QQmlJS::SourceLocation{});
+}
+
+/*!
+\internal
+Returns true if ctx denotes an offset lying before right.begin(), and false otherwise.
+*/
+bool QQmlLSCompletion::beforeLocation(const QQmlLSCompletionPosition &ctx,
+ QQmlJS::SourceLocation right) const
+{
+ if (!right.isValid())
+ return true;
+
+ // note: ctx.offset() == right.begin() means that the cursor lies exactly before right
+ if (ctx.offset() <= right.begin())
+ return true;
+
+ return false;
+}
+
+bool QQmlLSCompletion::ctxBeforeStatement(const QQmlLSCompletionPosition &positionInfo,
+ const DomItem &parentForContext,
+ FileLocationRegion firstRegion) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+ const bool result = beforeLocation(positionInfo, regions[firstRegion]);
+ return result;
+}
+
+void
+QQmlLSCompletion::suggestBindingCompletion(const DomItem &itemAtPosition, BackInsertIterator it) const
+{
+ suggestReachableTypes(itemAtPosition, LocalSymbolsType::AttachedType, CompletionItemKind::Class,
+ it);
+
+ const QQmlJSScope::ConstPtr scope = [&]() {
+ if (!QQmlLSUtils::isFieldMemberAccess(itemAtPosition))
+ return itemAtPosition.qmlObject().semanticScope();
+
+ const DomItem owner = itemAtPosition.directParent().field(Fields::left);
+ auto expressionType = QQmlLSUtils::resolveExpressionType(
+ owner, ResolveActualTypeForFieldMemberExpression);
+ return expressionType ? expressionType->semanticScope : QQmlJSScope::ConstPtr{};
+ }();
+
+ if (!scope)
+ return;
+
+ propertyCompletion(scope, nullptr, it);
+ signalHandlerCompletion(scope, nullptr, it);
+}
+
+void QQmlLSCompletion::insideImportCompletionHelper(const DomItem &file,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const
+{
+ // returns completions for import statements, ctx is supposed to be in an import statement
+ const CompletionContextStrings &ctx = positionInfo.cursorPosition;
+ ImportCompletionType importCompletionType = ImportCompletionType::None;
+ QRegularExpression spaceRe(uR"(\W+)"_s);
+ QList<QStringView> linePieces = ctx.preLine().split(spaceRe, Qt::SkipEmptyParts);
+ qsizetype effectiveLength = linePieces.size()
+ + ((!ctx.preLine().isEmpty() && ctx.preLine().last().isSpace()) ? 1 : 0);
+ if (effectiveLength < 2) {
+ CompletionItem comp;
+ comp.label = "import";
+ comp.kind = int(CompletionItemKind::Keyword);
+ it = comp;
+ }
+ if (linePieces.isEmpty() || linePieces.first() != u"import")
+ return;
+ if (effectiveLength == 2) {
+ // the cursor is after the import, possibly in a partial module name
+ importCompletionType = ImportCompletionType::Module;
+ } else if (effectiveLength == 3) {
+ if (linePieces.last() != u"as") {
+ // the cursor is after the module, possibly in a partial version token (or partial as)
+ CompletionItem comp;
+ comp.label = "as";
+ comp.kind = int(CompletionItemKind::Keyword);
+ it = comp;
+ importCompletionType = ImportCompletionType::Version;
+ }
+ }
+ DomItem env = file.environment();
+ if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
+ switch (importCompletionType) {
+ case ImportCompletionType::None:
+ break;
+ case ImportCompletionType::Module: {
+ QDuplicateTracker<QString> modulesSeen;
+ for (const QString &uri : envPtr->moduleIndexUris(env)) {
+ QStringView base = ctx.base(); // if we allow spaces we should get rid of them
+ if (uri.startsWith(base)) {
+ QStringList rest = uri.mid(base.size()).split(u'.');
+ if (rest.isEmpty())
+ continue;
+
+ const QString label = rest.first();
+ if (!modulesSeen.hasSeen(label)) {
+ CompletionItem comp;
+ comp.label = label.toUtf8();
+ comp.kind = int(CompletionItemKind::Module);
+ it = comp;
+ }
+ }
+ }
+ break;
+ }
+ case ImportCompletionType::Version:
+ if (ctx.base().isEmpty()) {
+ for (int majorV :
+ envPtr->moduleIndexMajorVersions(env, linePieces.at(1).toString())) {
+ CompletionItem comp;
+ comp.label = QString::number(majorV).toUtf8();
+ comp.kind = int(CompletionItemKind::Constant);
+ it = comp;
+ }
+ } else {
+ bool hasMajorVersion = ctx.base().endsWith(u'.');
+ int majorV = -1;
+ if (hasMajorVersion)
+ majorV = ctx.base().mid(0, ctx.base().size() - 1).toInt(&hasMajorVersion);
+ if (!hasMajorVersion)
+ break;
+ if (std::shared_ptr<ModuleIndex> mIndex =
+ envPtr->moduleIndexWithUri(env, linePieces.at(1).toString(), majorV)) {
+ for (int minorV : mIndex->minorVersions()) {
+ CompletionItem comp;
+ comp.label = QString::number(minorV).toUtf8();
+ comp.kind = int(CompletionItemKind::Constant);
+ it = comp;
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+void QQmlLSCompletion::idsCompletions(const DomItem &component, BackInsertIterator it) const
+{
+ qCDebug(QQmlLSCompletionLog) << "adding ids completions";
+ for (const QString &k : component.field(Fields::ids).keys()) {
+ CompletionItem comp;
+ comp.label = k.toUtf8();
+ comp.kind = int(CompletionItemKind::Value);
+ it = comp;
+ }
+}
+
+static bool testScopeSymbol(const QQmlJSScope::ConstPtr &scope, LocalSymbolsTypes options,
+ CompletionItemKind kind)
+{
+ const bool currentIsSingleton = scope->isSingleton();
+ const bool currentIsAttached = !scope->attachedType().isNull();
+ if ((options & LocalSymbolsType::Singleton) && currentIsSingleton) {
+ return true;
+ }
+ if ((options & LocalSymbolsType::AttachedType) && currentIsAttached) {
+ return true;
+ }
+ const bool isObjectType = scope->isReferenceType();
+ if (options & LocalSymbolsType::ObjectType && !currentIsSingleton && isObjectType) {
+ return kind != CompletionItemKind::Constructor || scope->isCreatable();
+ }
+ if (options & LocalSymbolsType::ValueType && !currentIsSingleton && !isObjectType) {
+ return true;
+ }
+ return false;
+}
+
+/*!
+\internal
+Obtain the types reachable from \c{el} as a CompletionItems.
+*/
+void QQmlLSCompletion::suggestReachableTypes(const DomItem &el, LocalSymbolsTypes options,
+ CompletionItemKind kind, BackInsertIterator it) const
+{
+ auto file = el.containingFile().as<QmlFile>();
+ if (!file)
+ return;
+ auto resolver = file->typeResolver();
+ if (!resolver)
+ return;
+
+ const QString requiredQualifiers = QQmlLSUtils::qualifiersFrom(el);
+ const auto keyValueRange = resolver->importedTypes().asKeyValueRange();
+ for (const auto &type : keyValueRange) {
+ // ignore special QQmlJSImporterMarkers
+ const bool isMarkerType = type.first.contains(u"$internal$.")
+ || type.first.contains(u"$anonymous$.") || type.first.contains(u"$module$.");
+ if (isMarkerType || !type.first.startsWith(requiredQualifiers))
+ continue;
+
+ auto &scope = type.second.scope;
+ if (!scope)
+ continue;
+
+ if (!testScopeSymbol(scope, options, kind))
+ continue;
+
+ CompletionItem completion;
+ completion.label = QStringView(type.first).sliced(requiredQualifiers.size()).toUtf8();
+ completion.kind = int(kind);
+ it = completion;
+ }
+}
+
+void QQmlLSCompletion::jsIdentifierCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames,
+ BackInsertIterator it) const
+{
+ for (const auto &[name, jsIdentifier] : scope->ownJSIdentifiers().asKeyValueRange()) {
+ CompletionItem completion;
+ if (usedNames && usedNames->hasSeen(name)) {
+ continue;
+ }
+ completion.label = name.toUtf8();
+ completion.kind = int(CompletionItemKind::Variable);
+ QString detail = u"has type "_s;
+ if (jsIdentifier.typeName) {
+ if (jsIdentifier.isConst) {
+ detail.append(u"const ");
+ }
+ detail.append(*jsIdentifier.typeName);
+ } else {
+ detail.append(jsIdentifier.isConst ? u"const"_s : u"var"_s);
+ }
+ completion.detail = detail.toUtf8();
+ it = completion;
+ }
+}
+
+void QQmlLSCompletion::methodCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames,
+ BackInsertIterator it) const
+{
+ // JS functions in current and base scopes
+ for (const auto &[name, method] : scope->methods().asKeyValueRange()) {
+ if (method.access() != QQmlJSMetaMethod::Public)
+ continue;
+ if (usedNames && usedNames->hasSeen(name)) {
+ continue;
+ }
+ CompletionItem completion;
+ completion.label = name.toUtf8();
+ completion.kind = int(CompletionItemKind::Method);
+ it = completion;
+ // TODO: QQmlLSUtils::reachableSymbols seems to be able to do documentation and detail
+ // and co, it should also be done here if possible.
+ }
+}
+
+void QQmlLSCompletion::propertyCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames,
+ BackInsertIterator it) const
+{
+ for (const auto &[name, property] : scope->properties().asKeyValueRange()) {
+ if (usedNames && usedNames->hasSeen(name)) {
+ continue;
+ }
+ CompletionItem completion;
+ completion.label = name.toUtf8();
+ completion.kind = int(CompletionItemKind::Property);
+ QString detail{ u"has type "_s };
+ if (!property.isWritable())
+ detail.append(u"readonly "_s);
+ detail.append(property.typeName().isEmpty() ? u"var"_s : property.typeName());
+ completion.detail = detail.toUtf8();
+ it = completion;
+ }
+}
+
+void QQmlLSCompletion::enumerationCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames,
+ BackInsertIterator it) const
+{
+ for (const QQmlJSMetaEnum &enumerator : scope->enumerations()) {
+ if (usedNames && usedNames->hasSeen(enumerator.name())) {
+ continue;
+ }
+ CompletionItem completion;
+ completion.label = enumerator.name().toUtf8();
+ completion.kind = static_cast<int>(CompletionItemKind::Enum);
+ it = completion;
+ }
+}
+
+void QQmlLSCompletion::enumerationValueCompletionHelper(const QStringList &enumeratorKeys,
+ BackInsertIterator it) const
+{
+ for (const QString &enumeratorKey : enumeratorKeys) {
+ CompletionItem completion;
+ completion.label = enumeratorKey.toUtf8();
+ completion.kind = static_cast<int>(CompletionItemKind::EnumMember);
+ it = completion;
+ }
+}
+
+/*!
+\internal
+Creates completion items for enumerationvalues.
+If enumeratorName is a valid enumerator then only do completion for the requested enumerator, and
+otherwise do completion for \b{all other possible} enumerators.
+
+For example:
+```
+id: someItem
+enum Hello { World }
+enum MyEnum { ValueOne, ValueTwo }
+
+// Hello does refer to a enumerator:
+property var a: Hello.<complete only World here>
+
+// someItem does not refer to a enumerator:
+property var b: someItem.<complete World, ValueOne and ValueTwo here>
+```
+*/
+
+void QQmlLSCompletion::enumerationValueCompletion(const QQmlJSScope::ConstPtr &scope,
+ const QString &enumeratorName,
+ BackInsertIterator result) const
+{
+ auto enumerator = scope->enumeration(enumeratorName);
+ if (enumerator.isValid()) {
+ enumerationValueCompletionHelper(enumerator.keys(), result);
+ return;
+ }
+
+ for (const QQmlJSMetaEnum &enumerator : scope->enumerations()) {
+ enumerationValueCompletionHelper(enumerator.keys(), result);
+ }
+}
+
+/*!
+\internal
+Calls F on all JavaScript-parents of scope. For example, you can use this method to
+collect all the JavaScript Identifiers from following code:
+```
+{ // this block statement contains only 'x'
+ let x = 3;
+ { // this block statement contains only 'y', and 'x' has to be retrieved via its parent.
+ let y = 4;
+ }
+}
+```
+*/
+template<typename F>
+void collectFromAllJavaScriptParents(const F &&f, const QQmlJSScope::ConstPtr &scope)
+{
+ for (QQmlJSScope::ConstPtr current = scope; current; current = current->parentScope()) {
+ f(current);
+ if (current->scopeType() == QQmlSA::ScopeType::QMLScope)
+ return;
+ }
+}
+
+/*!
+\internal
+Generate autocompletions for JS expressions, suggest possible properties, methods, etc.
+
+If scriptIdentifier is inside a Field Member Expression, like \c{onCompleted} in
+\c{Component.onCompleted} for example, then this method will only suggest properties, methods, etc
+from the correct type. For the previous example that would be properties, methods, etc. from the
+Component attached type.
+*/
+void QQmlLSCompletion::suggestJSExpressionCompletion(const DomItem &scriptIdentifier,
+ BackInsertIterator result) const
+{
+ QDuplicateTracker<QString> usedNames;
+ QQmlJSScope::ConstPtr nearestScope;
+
+ // note: there is an edge case, where the user asks for completion right after the dot
+ // of some qualified expression like `root.hello`. In this case, scriptIdentifier is actually
+ // the BinaryExpression instead of the left-hand-side that has not be written down yet.
+ const bool askForCompletionOnDot = QQmlLSUtils::isFieldMemberExpression(scriptIdentifier);
+ const bool hasQualifier =
+ QQmlLSUtils::isFieldMemberAccess(scriptIdentifier) || askForCompletionOnDot;
+
+ if (!hasQualifier) {
+ for (QUtf8StringView view : std::array<QUtf8StringView, 3>{ "null", "false", "true" }) {
+ CompletionItem completion;
+ completion.label = view.data();
+ completion.kind = int(CompletionItemKind::Value);
+ result = completion;
+ }
+ idsCompletions(scriptIdentifier.component(), result);
+ suggestReachableTypes(scriptIdentifier,
+ LocalSymbolsType::Singleton | LocalSymbolsType::AttachedType,
+ CompletionItemKind::Class, result);
+
+ auto scope = scriptIdentifier.nearestSemanticScope();
+ if (!scope)
+ return;
+ nearestScope = scope;
+
+ enumerationCompletion(nearestScope, &usedNames, result);
+ } else {
+ const DomItem owner =
+ (askForCompletionOnDot ? scriptIdentifier : scriptIdentifier.directParent())
+ .field(Fields::left);
+ auto expressionType = QQmlLSUtils::resolveExpressionType(
+ owner, ResolveActualTypeForFieldMemberExpression);
+ if (!expressionType || !expressionType->semanticScope)
+ return;
+ nearestScope = expressionType->semanticScope;
+ // Use root element scope to use find the enumerations
+ // This should be changed when we support usages in external files
+ if (expressionType->type == QmlComponentIdentifier)
+ nearestScope = owner.rootQmlObject(GoTo::MostLikely).semanticScope();
+ if (expressionType->name) {
+ // note: you only get enumeration values in qualified expressions, never alone
+ enumerationValueCompletion(nearestScope, *expressionType->name, result);
+
+ // skip enumeration types if already inside an enumeration type
+ if (auto enumerator = nearestScope->enumeration(*expressionType->name);
+ !enumerator.isValid()) {
+ enumerationCompletion(nearestScope, &usedNames, result);
+ }
+
+ if (expressionType->type == EnumeratorIdentifier)
+ return;
+ }
+ }
+
+ Q_ASSERT(nearestScope);
+
+ methodCompletion(nearestScope, &usedNames, result);
+ propertyCompletion(nearestScope, &usedNames, result);
+
+ if (!hasQualifier) {
+ // collect all of the stuff from parents
+ collectFromAllJavaScriptParents(
+ [this, &usedNames, result](const QQmlJSScope::ConstPtr &scope) {
+ jsIdentifierCompletion(scope, &usedNames, result);
+ },
+ nearestScope);
+ collectFromAllJavaScriptParents(
+ [this, &usedNames, result](const QQmlJSScope::ConstPtr &scope) {
+ methodCompletion(scope, &usedNames, result);
+ },
+ nearestScope);
+ collectFromAllJavaScriptParents(
+ [this, &usedNames, result](const QQmlJSScope::ConstPtr &scope) {
+ propertyCompletion(scope, &usedNames, result);
+ },
+ nearestScope);
+
+ auto file = scriptIdentifier.containingFile().as<QmlFile>();
+ if (!file)
+ return;
+ auto resolver = file->typeResolver();
+ if (!resolver)
+ return;
+
+ const auto globals = resolver->jsGlobalObject();
+ methodCompletion(globals, &usedNames, result);
+ propertyCompletion(globals, &usedNames, result);
+ }
+}
+
+static const QQmlJSScope *resolve(const QQmlJSScope *current, const QStringList &names)
+{
+ for (const QString &name : names) {
+ if (auto property = current->property(name); property.isValid()) {
+ if (auto propertyType = property.type().get()) {
+ current = propertyType;
+ continue;
+ }
+ }
+ return {};
+ }
+ return current;
+}
+
+bool QQmlLSCompletion::cursorInFrontOfItem(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo)
+{
+ auto fileLocations = FileLocations::treeOf(parentForContext)->info().fullRegion;
+ return positionInfo.offset() <= fileLocations.offset;
+}
+
+bool QQmlLSCompletion::cursorAfterColon(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo)
+{
+ auto location = FileLocations::treeOf(currentItem)->info();
+ auto region = location.regions.constFind(ColonTokenRegion);
+
+ if (region == location.regions.constEnd())
+ return false;
+
+ if (region.value().isValid() && region.value().offset < positionInfo.offset()) {
+ return true;
+ }
+ return false;
+}
+
+/*!
+\internal
+\brief Mapping from pragma names to allowed pragma values.
+
+This mapping of pragma names to pragma values is not complete. In fact, it only contains the
+pragma names and values that one should see autocompletion for.
+Some pragmas like FunctionSignatureBehavior or Strict or the Reference/Value of ValueTypeBehavior,
+for example, should currently not be proposed as completion items by qmlls.
+
+An empty QList-value in the QMap means that the pragma does not accept pragma values.
+*/
+static const QMap<QString, QList<QString>> valuesForPragmas{
+ { u"ComponentBehavior"_s, { u"Unbound"_s, u"Bound"_s } },
+ { u"NativeMethodBehavior"_s, { u"AcceptThisObject"_s, u"RejectThisObject"_s } },
+ { u"ListPropertyAssignBehavior"_s, { u"Append"_s, u"Replace"_s, u"ReplaceIfNotDefault"_s } },
+ { u"Singleton"_s, {} },
+ { u"ValueTypeBehavior"_s, { u"Addressable"_s, u"Inaddressable"_s } },
+};
+
+void QQmlLSCompletion::insidePragmaCompletion(QQmlJS::Dom::DomItem currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ if (cursorAfterColon(currentItem, positionInfo)) {
+ const QString name = currentItem.field(Fields::name).value().toString();
+ auto values = valuesForPragmas.constFind(name);
+ if (values == valuesForPragmas.constEnd())
+ return;
+
+ for (const auto &value : *values) {
+ CompletionItem comp;
+ comp.label = value.toUtf8();
+ comp.kind = static_cast<int>(CompletionItemKind::Value);
+ result = comp;
+ }
+ return;
+ }
+
+ for (const auto &pragma : valuesForPragmas.asKeyValueRange()) {
+ CompletionItem comp;
+ comp.label = pragma.first.toUtf8();
+ if (!pragma.second.isEmpty()) {
+ comp.insertText = QString(pragma.first).append(u": ").toUtf8();
+ }
+ comp.kind = static_cast<int>(CompletionItemKind::Value);
+ result = comp;
+ }
+}
+
+void QQmlLSCompletion::insideQmlObjectCompletion(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation leftBrace = regions[LeftBraceRegion];
+ const QQmlJS::SourceLocation rightBrace = regions[RightBraceRegion];
+
+ if (beforeLocation(positionInfo, leftBrace)) {
+ LocalSymbolsTypes options;
+ options.setFlag(LocalSymbolsType::ObjectType);
+ suggestReachableTypes(positionInfo.itemAtPosition, options, CompletionItemKind::Constructor,
+ result);
+ suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
+
+ if (QQmlLSUtils::isFieldMemberExpression(positionInfo.itemAtPosition)) {
+ /*!
+ \internal
+ In the case that a missing identifier is followed by an assignment to the default
+ property, the parser will create a QmlObject out of both binding and default
+ binding. For example, in \code property int x: root. Item {} \endcode the parser will
+ create one binding containing one QmlObject of type `root.Item`, instead of two
+ bindings (one for `x` and one for the default property). For this special case, if
+ completion is requested inside `root.Item`, then try to also suggest JS expressions.
+
+ Note: suggestJSExpressionCompletion() will suggest nothing if the
+ fieldMemberExpression starts with the name of a qualified module or a filename, so
+ this only adds invalid suggestions in the case that there is something shadowing the
+ qualified module name or filename, like a property name for example.
+
+ Note 2: This does not happen for field member accesses. For example, in
+ \code
+ property int x: root.x
+ Item {}
+ \endcode
+ The parser will create both bindings correctly.
+ */
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ }
+ return;
+ }
+
+ if (betweenLocations(leftBrace, positionInfo, rightBrace)) {
+ // default/required property completion
+ for (QUtf8StringView view :
+ std::array<QUtf8StringView, 6>{ "", "readonly ", "default ", "default required ",
+ "required default ", "required " }) {
+ // readonly properties require an initializer
+ if (view != QUtf8StringView("readonly ")) {
+ result = makeSnippet(
+ QByteArray(view.data()).append("property type name;"),
+ QByteArray(view.data()).append("property ${1:type} ${0:name};"));
+ }
+
+ result = makeSnippet(
+ QByteArray(view.data()).append("property type name: value;"),
+ QByteArray(view.data()).append("property ${1:type} ${2:name}: ${0:value};"));
+ }
+
+ // signal
+ result = makeSnippet("signal name(arg1:type1, ...)", "signal ${1:name}($0)");
+
+ // signal without parameters
+ result = makeSnippet("signal name;", "signal ${0:name};");
+
+ // make already existing property required
+ result = makeSnippet("required name;", "required ${0:name};");
+
+ // function
+ result = makeSnippet("function name(args...): returnType { statements...}",
+ "function ${1:name}($2): ${3:returnType} {\n\t$0\n}");
+
+ // enum
+ result = makeSnippet("enum name { Values...}", "enum ${1:name} {\n\t${0:values}\n}");
+
+ // inline component
+ result = makeSnippet("component Name: BaseType { ... }",
+ "component ${1:name}: ${2:baseType} {\n\t$0\n}");
+
+ // add bindings
+ const DomItem containingObject = parentForContext.qmlObject();
+ suggestBindingCompletion(containingObject, result);
+
+ // add Qml Types for default binding
+ const DomItem containingFile = parentForContext.containingFile();
+ suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
+ CompletionItemKind::Constructor, result);
+ suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insidePropertyDefinitionCompletion(
+ const DomItem &currentItem, const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ auto info = FileLocations::treeOf(currentItem)->info();
+ const QQmlJS::SourceLocation propertyKeyword = info.regions[PropertyKeywordRegion];
+
+ // do completions for the keywords
+ if (positionInfo.offset() < propertyKeyword.offset + propertyKeyword.length) {
+ const QQmlJS::SourceLocation readonlyKeyword = info.regions[ReadonlyKeywordRegion];
+ const QQmlJS::SourceLocation defaultKeyword = info.regions[DefaultKeywordRegion];
+ const QQmlJS::SourceLocation requiredKeyword = info.regions[RequiredKeywordRegion];
+
+ bool completeReadonly = true;
+ bool completeRequired = true;
+ bool completeDefault = true;
+
+ // if there is already a readonly keyword before the cursor: do not auto complete it again
+ if (readonlyKeyword.isValid() && readonlyKeyword.offset < positionInfo.offset()) {
+ completeReadonly = false;
+ // also, required keywords do not like readonly keywords
+ completeRequired = false;
+ }
+
+ // same for required
+ if (requiredKeyword.isValid() && requiredKeyword.offset < positionInfo.offset()) {
+ completeRequired = false;
+ // also, required keywords do not like readonly keywords
+ completeReadonly = false;
+ }
+
+ // same for default
+ if (defaultKeyword.isValid() && defaultKeyword.offset < positionInfo.offset()) {
+ completeDefault = false;
+ }
+ auto addCompletionKeyword = [&result](QUtf8StringView view, bool complete) {
+ if (!complete)
+ return;
+ CompletionItem item;
+ item.label = view.data();
+ item.kind = int(CompletionItemKind::Keyword);
+ result = item;
+ };
+ addCompletionKeyword(u8"readonly", completeReadonly);
+ addCompletionKeyword(u8"required", completeRequired);
+ addCompletionKeyword(u8"default", completeDefault);
+ addCompletionKeyword(u8"property", true);
+
+ return;
+ }
+
+ const QQmlJS::SourceLocation propertyIdentifier = info.regions[IdentifierRegion];
+ if (propertyKeyword.end() <= positionInfo.offset()
+ && positionInfo.offset() < propertyIdentifier.offset) {
+ suggestReachableTypes(currentItem,
+ LocalSymbolsType::ObjectType | LocalSymbolsType::ValueType,
+ CompletionItemKind::Class, result);
+ }
+ // do not autocomplete the rest
+ return;
+}
+
+void QQmlLSCompletion::insideBindingCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const DomItem containingBinding = currentItem.filterUp(
+ [](DomType type, const QQmlJS::Dom::DomItem &) { return type == DomType::Binding; },
+ FilterUpOptions::ReturnOuter);
+
+ // do scriptidentifiercompletion after the ':' of a binding
+ if (cursorAfterColon(containingBinding, positionInfo)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+
+ if (auto type = QQmlLSUtils::resolveExpressionType(currentItem, ResolveOwnerType)) {
+ const QStringList names = currentItem.field(Fields::name).toString().split(u'.');
+ const QQmlJSScope *current = resolve(type->semanticScope.get(), names);
+ // add type names when binding to an object type or a property with var type
+ if (!current || current->accessSemantics() == QQmlSA::AccessSemantics::Reference) {
+ LocalSymbolsTypes options;
+ options.setFlag(LocalSymbolsType::ObjectType);
+ suggestReachableTypes(positionInfo.itemAtPosition, options,
+ CompletionItemKind::Constructor, result);
+ suggestSnippetsForRightHandSideOfBinding(positionInfo.itemAtPosition, result);
+ }
+ }
+ return;
+ }
+
+ // ignore the binding if asking for completion in front of the binding
+ if (cursorInFrontOfItem(containingBinding, positionInfo)) {
+ insideQmlObjectCompletion(currentItem.containingObject(), positionInfo, result);
+ return;
+ }
+
+ const DomItem containingObject = currentItem.qmlObject();
+
+ suggestBindingCompletion(positionInfo.itemAtPosition, result);
+
+ // add Qml Types for default binding
+ suggestReachableTypes(positionInfo.itemAtPosition, LocalSymbolsType::ObjectType,
+ CompletionItemKind::Constructor, result);
+ suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
+}
+
+void QQmlLSCompletion::insideImportCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const DomItem containingFile = currentItem.containingFile();
+ insideImportCompletionHelper(containingFile, positionInfo, result);
+
+ // when in front of the import statement: propose types for root Qml Object completion
+ if (cursorInFrontOfItem(currentItem, positionInfo)) {
+ suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
+ CompletionItemKind::Constructor, result);
+ }
+}
+
+void QQmlLSCompletion::insideQmlFileCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const DomItem containingFile = currentItem.containingFile();
+ // completions for code outside the root Qml Object
+ // global completions
+ if (positionInfo.cursorPosition.atLineStart()) {
+ if (positionInfo.cursorPosition.base().isEmpty()) {
+ for (const QStringView &s : std::array<QStringView, 2>({ u"pragma", u"import" })) {
+ CompletionItem comp;
+ comp.label = s.toUtf8();
+ comp.kind = int(CompletionItemKind::Keyword);
+ result = comp;
+ }
+ }
+ }
+ // Types for root Qml Object completion
+ suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
+ CompletionItemKind::Constructor, result);
+}
+
+/*!
+\internal
+Generate the snippets for let, var and const variable declarations.
+*/
+void QQmlLSCompletion::suggestVariableDeclarationStatementCompletion(
+ BackInsertIterator result, QQmlLSUtilsAppendOption option) const
+{
+ // let/var/const statement
+ for (auto view : std::array<QUtf8StringView, 3>{ "let", "var", "const" }) {
+ auto snippet = makeSnippet(QByteArray(view.data()).append(" variable = value"),
+ QByteArray(view.data()).append(" ${1:variable} = $0"));
+ if (option == AppendSemicolon) {
+ snippet.insertText->append(";");
+ snippet.label.append(";");
+ }
+ result = snippet;
+ }
+}
+
+/*!
+\internal
+Generate the snippets for case and default statements.
+*/
+void QQmlLSCompletion::suggestCaseAndDefaultStatementCompletion(BackInsertIterator result) const
+{
+ // case snippet
+ result = makeSnippet("case value: statements...", "case ${1:value}:\n\t$0");
+ // case + brackets snippet
+ result = makeSnippet("case value: { statements... }", "case ${1:value}: {\n\t$0\n}");
+
+ // default snippet
+ result = makeSnippet("default: statements...", "default:\n\t$0");
+ // default + brackets snippet
+ result = makeSnippet("default: { statements... }", "default: {\n\t$0\n}");
+}
+
+/*!
+\internal
+Break and continue can be inserted only in following situations:
+\list
+ \li Break and continue inside a loop.
+ \li Break inside a (nested) LabelledStatement
+ \li Break inside a (nested) SwitchStatement
+\endlist
+*/
+void QQmlLSCompletion::suggestContinueAndBreakStatementIfNeeded(const DomItem &itemAtPosition,
+ BackInsertIterator result) const
+{
+ bool alreadyInLabel = false;
+ bool alreadyInSwitch = false;
+ for (DomItem current = itemAtPosition; current; current = current.directParent()) {
+ switch (current.internalKind()) {
+ case DomType::ScriptExpression:
+ // reached end of script expression
+ return;
+
+ case DomType::ScriptForStatement:
+ case DomType::ScriptForEachStatement:
+ case DomType::ScriptWhileStatement:
+ case DomType::ScriptDoWhileStatement: {
+ CompletionItem continueKeyword;
+ continueKeyword.label = "continue";
+ continueKeyword.kind = int(CompletionItemKind::Keyword);
+ result = continueKeyword;
+
+ // do not add break twice
+ if (!alreadyInSwitch && !alreadyInLabel) {
+ CompletionItem breakKeyword;
+ breakKeyword.label = "break";
+ breakKeyword.kind = int(CompletionItemKind::Keyword);
+ result = breakKeyword;
+ }
+ // early exit: cannot suggest more completions
+ return;
+ }
+ case DomType::ScriptSwitchStatement: {
+ // check if break was already inserted
+ if (alreadyInSwitch || alreadyInLabel)
+ break;
+ alreadyInSwitch = true;
+
+ CompletionItem breakKeyword;
+ breakKeyword.label = "break";
+ breakKeyword.kind = int(CompletionItemKind::Keyword);
+ result = breakKeyword;
+ break;
+ }
+ case DomType::ScriptLabelledStatement: {
+ // check if break was already inserted because of switch or loop
+ if (alreadyInSwitch || alreadyInLabel)
+ break;
+ alreadyInLabel = true;
+
+ CompletionItem breakKeyword;
+ breakKeyword.label = "break";
+ breakKeyword.kind = int(CompletionItemKind::Keyword);
+ result = breakKeyword;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+/*!
+\internal
+Generates snippets or keywords for all possible JS statements where it makes sense. To use whenever
+any JS statement can be expected, but when no JS statement is there yet.
+
+Only generates JS expression completions when itemAtPosition is a qualified name.
+
+Here is a list of statements that do \e{not} get any snippets:
+\list
+ \li BlockStatement does not need a code snippet, editors automatically include the closing
+bracket anyway. \li EmptyStatement completion would only generate a single \c{;} \li
+ExpressionStatement completion cannot generate any snippet, only identifiers \li WithStatement
+completion is not recommended: qmllint will warn about usage of with statements \li
+LabelledStatement completion might need to propose labels (TODO?) \li DebuggerStatement completion
+does not strike as being very useful \endlist
+*/
+void QQmlLSCompletion::suggestJSStatementCompletion(const DomItem &itemAtPosition,
+ BackInsertIterator result) const
+{
+ suggestJSExpressionCompletion(itemAtPosition, result);
+
+ if (QQmlLSUtils::isFieldMemberAccess(itemAtPosition))
+ return;
+
+ // expression statements
+ suggestVariableDeclarationStatementCompletion(result);
+ // block statement
+ result = makeSnippet("{ statements... }", "{\n\t$0\n}");
+
+ // if + brackets statement
+ result = makeSnippet("if (condition) { statements }", "if ($1) {\n\t$0\n}");
+
+ // do statement
+ result = makeSnippet("do { statements } while (condition);", "do {\n\t$1\n} while ($0);");
+
+ // while + brackets statement
+ result = makeSnippet("while (condition) { statements...}", "while ($1) {\n\t$0\n}");
+
+ // for + brackets loop statement
+ result = makeSnippet("for (initializer; condition; increment) { statements... }",
+ "for ($1;$2;$3) {\n\t$0\n}");
+
+ // for ... in + brackets loop statement
+ result = makeSnippet("for (property in object) { statements... }", "for ($1 in $2) {\n\t$0\n}");
+
+ // for ... of + brackets loop statement
+ result = makeSnippet("for (element of array) { statements... }", "for ($1 of $2) {\n\t$0\n}");
+
+ // try + catch statement
+ result = makeSnippet("try { statements... } catch(error) { statements... }",
+ "try {\n\t$1\n} catch($2) {\n\t$0\n}");
+
+ // try + finally statement
+ result = makeSnippet("try { statements... } finally { statements... }",
+ "try {\n\t$1\n} finally {\n\t$0\n}");
+
+ // try + catch + finally statement
+ result = makeSnippet(
+ "try { statements... } catch(error) { statements... } finally { statements... }",
+ "try {\n\t$1\n} catch($2) {\n\t$3\n} finally {\n\t$0\n}");
+
+ // one can always assume that JS code in QML is inside a function, so always propose `return`
+ for (auto &&view : { "return"_ba, "throw"_ba }) {
+ CompletionItem item;
+ item.label = std::move(view);
+ item.kind = int(CompletionItemKind::Keyword);
+ result = item;
+ }
+
+ // rules for case+default statements:
+ // 1) when inside a CaseBlock, or
+ // 2) inside a CaseClause, as an (non-nested) element of the CaseClause statementlist.
+ // 3) inside a DefaultClause, as an (non-nested) element of the DefaultClause statementlist,
+ //
+ // switch (x) {
+ // // (1)
+ // case 1:
+ // myProperty = 5;
+ // // (2) -> could be another statement of current case, but also a new case or default!
+ // default:
+ // myProperty = 5;
+ // // (3) -> could be another statement of current default, but also a new case or default!
+ // }
+ const DomType currentKind = itemAtPosition.internalKind();
+ const DomType parentKind = itemAtPosition.directParent().internalKind();
+ if (currentKind == DomType::ScriptCaseBlock || currentKind == DomType::ScriptCaseClause
+ || currentKind == DomType::ScriptDefaultClause
+ || (currentKind == DomType::List
+ && (parentKind == DomType::ScriptCaseClause
+ || parentKind == DomType::ScriptDefaultClause))) {
+ suggestCaseAndDefaultStatementCompletion(result);
+ }
+ suggestContinueAndBreakStatementIfNeeded(itemAtPosition, result);
+}
+
+void QQmlLSCompletion::insideForStatementCompletion(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
+ const QQmlJS::SourceLocation firstSemicolon = regions[FirstSemicolonTokenRegion];
+ const QQmlJS::SourceLocation secondSemicolon = regions[SecondSemicolonRegion];
+ const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
+
+ if (betweenLocations(leftParenthesis, positionInfo, firstSemicolon)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ suggestVariableDeclarationStatementCompletion(result,
+ QQmlLSUtilsAppendOption::AppendNothing);
+ return;
+ }
+ if (betweenLocations(firstSemicolon, positionInfo, secondSemicolon)
+ || betweenLocations(secondSemicolon, positionInfo, rightParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+
+ if (afterLocation(rightParenthesis, positionInfo)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideScriptLiteralCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ Q_UNUSED(currentItem);
+ if (positionInfo.cursorPosition.base().isEmpty()) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideCallExpression(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(currentItem)->info().regions;
+ const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
+ const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
+ if (beforeLocation(positionInfo, leftParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideIfStatement(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(currentItem)->info().regions;
+ const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
+ const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
+ const QQmlJS::SourceLocation elseKeyword = regions[ElseKeywordRegion];
+
+ if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (betweenLocations(rightParenthesis, positionInfo, elseKeyword)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (afterLocation(elseKeyword, positionInfo)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideReturnStatement(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(currentItem)->info().regions;
+ const QQmlJS::SourceLocation returnKeyword = regions[ReturnKeywordRegion];
+
+ if (afterLocation(returnKeyword, positionInfo)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideWhileStatement(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(currentItem)->info().regions;
+ const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
+ const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
+
+ if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (afterLocation(rightParenthesis, positionInfo)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideDoWhileStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+ const QQmlJS::SourceLocation doKeyword = regions[DoKeywordRegion];
+ const QQmlJS::SourceLocation whileKeyword = regions[WhileKeywordRegion];
+ const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
+ const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
+
+ if (betweenLocations(doKeyword, positionInfo, whileKeyword)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideForEachStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation inOf = regions[InOfTokenRegion];
+ const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
+ const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
+
+ if (betweenLocations(leftParenthesis, positionInfo, inOf)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ suggestVariableDeclarationStatementCompletion(result);
+ return;
+ }
+ if (betweenLocations(inOf, positionInfo, rightParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+
+ if (afterLocation(rightParenthesis, positionInfo)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideSwitchStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
+ const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
+
+ if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideCaseClause(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation caseKeyword = regions[CaseKeywordRegion];
+ const QQmlJS::SourceLocation colonToken = regions[ColonTokenRegion];
+
+ if (betweenLocations(caseKeyword, positionInfo, colonToken)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (afterLocation(colonToken, positionInfo)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+
+}
+
+/*!
+\internal
+Checks if a case or default clause does happen before ctx in the code.
+*/
+bool QQmlLSCompletion::isCaseOrDefaultBeforeCtx(const DomItem &currentClause,
+ const QQmlLSCompletionPosition &positionInfo,
+ FileLocationRegion keywordRegion) const
+{
+ Q_ASSERT(keywordRegion == QQmlJS::Dom::CaseKeywordRegion
+ || keywordRegion == QQmlJS::Dom::DefaultKeywordRegion);
+
+ if (!currentClause)
+ return false;
+
+ const auto token = FileLocations::treeOf(currentClause)->info().regions[keywordRegion];
+ if (afterLocation(token, positionInfo))
+ return true;
+
+ return false;
+}
+
+/*!
+\internal
+
+Search for a `case ...:` or a `default: ` clause happening before ctx, and return the
+corresponding DomItem of type DomType::CaseClauses or DomType::DefaultClause.
+
+Return an empty DomItem if neither case nor default was found.
+*/
+DomItem
+QQmlLSCompletion::previousCaseOfCaseBlock(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo) const
+{
+ const DomItem caseClauses = parentForContext.field(Fields::caseClauses);
+ for (int i = 0; i < caseClauses.indexes(); ++i) {
+ const DomItem currentClause = caseClauses.index(i);
+ if (isCaseOrDefaultBeforeCtx(currentClause, positionInfo, QQmlJS::Dom::CaseKeywordRegion)) {
+ return currentClause;
+ }
+ }
+
+ const DomItem defaultClause = parentForContext.field(Fields::defaultClause);
+ if (isCaseOrDefaultBeforeCtx(defaultClause, positionInfo, QQmlJS::Dom::DefaultKeywordRegion))
+ return parentForContext.field(Fields::defaultClause);
+
+ const DomItem moreCaseClauses = parentForContext.field(Fields::moreCaseClauses);
+ for (int i = 0; i < moreCaseClauses.indexes(); ++i) {
+ const DomItem currentClause = moreCaseClauses.index(i);
+ if (isCaseOrDefaultBeforeCtx(currentClause, positionInfo, QQmlJS::Dom::CaseKeywordRegion)) {
+ return currentClause;
+ }
+ }
+
+ return {};
+}
+
+void QQmlLSCompletion::insideCaseBlock(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation leftBrace = regions[LeftBraceRegion];
+ const QQmlJS::SourceLocation rightBrace = regions[RightBraceRegion];
+
+ if (!betweenLocations(leftBrace, positionInfo, rightBrace))
+ return;
+
+ // TODO: looks fishy
+ // if there is a previous case or default clause, you can still add statements to it
+ if (const auto previousCase = previousCaseOfCaseBlock(parentForContext, positionInfo)) {
+ suggestJSStatementCompletion(previousCase, result);
+ return;
+ }
+
+ // otherwise, only complete case and default
+ suggestCaseAndDefaultStatementCompletion(result);
+}
+
+void QQmlLSCompletion::insideDefaultClause(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation colonToken = regions[ColonTokenRegion];
+
+ if (afterLocation(colonToken, positionInfo)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return ;
+ }
+}
+
+void QQmlLSCompletion::insideBinaryExpressionCompletion(
+ const DomItem &parentForContext, const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation operatorLocation = regions[OperatorTokenRegion];
+
+ if (beforeLocation(positionInfo, operatorLocation)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (afterLocation(operatorLocation, positionInfo)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+/*!
+\internal
+Doing completion in variable declarations requires taking a look at all different cases:
+
+\list
+ \li Normal variable names, like \c{let helloWorld = 123;}
+ In this case, only autocomplete scriptexpressionidentifiers after the '=' token.
+ Do not propose existing names for the variable name, because the variable name needs to be
+ an identifier that is not used anywhere (to avoid shadowing and confusing code),
+
+ \li Deconstructed arrays, like \c{let [ helloWorld, ] = [ 123, ];}
+ In this case, only autocomplete scriptexpressionidentifiers after the '=' token.
+ Do not propose already existing identifiers inside the left hand side array.
+
+ \li Deconstructed arrays with initializers, like \c{let [ helloWorld = someVar, ] = [ 123, ];}
+ Note: this assigns the value of someVar to helloWorld if the right hand side's first element
+ is undefined or does not exist.
+
+ In this case, only autocomplete scriptexpressionidentifiers after the '=' tokens.
+ Only propose already existing identifiers inside the left hand side array when behind a '='
+ token.
+
+ \li Deconstructed Objects, like \c{let { helloWorld, } = { helloWorld: 123, };}
+ In this case, only autocomplete scriptexpressionidentifiers after the '=' token.
+ Do not propose already existing identifiers inside the left hand side object.
+
+ \li Deconstructed Objects with initializers, like \c{let { helloWorld = someVar, } = {};}
+ Note: this assigns the value of someVar to helloWorld if the right hand side's object does
+ not have a property called 'helloWorld'.
+
+ In this case, only autocomplete scriptexpressionidentifiers after the '=' token.
+ Only propose already existing identifiers inside the left hand side object when behind a '='
+ token.
+
+ \li Finally, you are allowed to nest and combine all above possibilities together for all your
+ deconstruction needs, so the exact same completion needs to be done for
+ DomType::ScriptPatternElement too.
+
+\endlist
+*/
+void QQmlLSCompletion::insideScriptPattern(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation equal = regions[EqualTokenRegion];
+
+ if (!afterLocation(equal, positionInfo))
+ return;
+
+ // otherwise, only complete case and default
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+}
+
+/*!
+\internal
+See comment on insideScriptPattern().
+*/
+void QQmlLSCompletion::insideVariableDeclarationEntry(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ insideScriptPattern(parentForContext, positionInfo, result);
+}
+
+void QQmlLSCompletion::insideThrowStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation throwKeyword = regions[ThrowKeywordRegion];
+
+ if (afterLocation(throwKeyword, positionInfo)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideLabelledStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation colon = regions[ColonTokenRegion];
+
+ if (afterLocation(colon, positionInfo)) {
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ // note: the case "beforeLocation(ctx, colon)" probably never happens:
+ // this is because without the colon, the parser will probably not parse this as a
+ // labelledstatement but as a normal expression statement.
+ // So this case only happens when the colon already exists, and the user goes back to the
+ // label name and requests completion for that label.
+}
+
+/*!
+\internal
+Collect the current set of labels that some DomItem can jump to.
+*/
+static void collectLabels(const DomItem &context, QQmlLSCompletion::BackInsertIterator result)
+{
+ for (DomItem current = context; current; current = current.directParent()) {
+ if (current.internalKind() == DomType::ScriptLabelledStatement) {
+ const QString label = current.field(Fields::label).value().toString();
+ if (label.isEmpty())
+ continue;
+ CompletionItem item;
+ item.label = label.toUtf8();
+ item.kind = int(CompletionItemKind::Value); // variable?
+ // TODO: more stuff here?
+ result = item;
+ } else if (current.internalKind() == DomType::ScriptExpression) {
+ // quick exit when leaving the JS part
+ return;
+ }
+ }
+ return;
+}
+
+void QQmlLSCompletion::insideContinueStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation continueKeyword = regions[ContinueKeywordRegion];
+
+ if (afterLocation(continueKeyword, positionInfo)) {
+ collectLabels(parentForContext, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideBreakStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation breakKeyword = regions[BreakKeywordRegion];
+
+ if (afterLocation(breakKeyword, positionInfo)) {
+ collectLabels(parentForContext, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideConditionalExpression(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation questionMark = regions[QuestionMarkTokenRegion];
+ const QQmlJS::SourceLocation colon = regions[ColonTokenRegion];
+
+ if (beforeLocation(positionInfo, questionMark)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (betweenLocations(questionMark, positionInfo, colon)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+ if (afterLocation(colon, positionInfo)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideUnaryExpression(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation operatorToken = regions[OperatorTokenRegion];
+
+ if (afterLocation(operatorToken, positionInfo)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insidePostExpression(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation operatorToken = regions[OperatorTokenRegion];
+
+ if (beforeLocation(positionInfo, operatorToken)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::insideParenthesizedExpression(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator result) const
+{
+ const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
+
+ const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
+ const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
+
+ if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
+ suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
+ return;
+ }
+}
+
+void QQmlLSCompletion::signalHandlerCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames,
+ BackInsertIterator result) const
+{
+ const auto keyValues = scope->methods().asKeyValueRange();
+ for (const auto &[name, method] : keyValues) {
+ if (method.access() != QQmlJSMetaMethod::Public
+ || method.methodType() != QQmlJSMetaMethodType::Signal) {
+ continue;
+ }
+ if (usedNames && usedNames->hasSeen(name)) {
+ continue;
+ }
+
+ CompletionItem completion;
+ completion.label = QQmlSignalNames::signalNameToHandlerName(name).toUtf8();
+ completion.kind = int(CompletionItemKind::Method);
+ result = completion;
+ }
+}
+
+/*!
+\internal
+Decide which completions can be used at currentItem and compute them.
+*/
+QList<CompletionItem>
+QQmlLSCompletion::completions(const DomItem &currentItem,
+ const CompletionContextStrings &contextStrings) const
+{
+ QList<CompletionItem> result;
+ collectCompletions(currentItem, contextStrings, std::back_inserter(result));
+ return result;
+}
+
+void QQmlLSCompletion::collectCompletions(const DomItem &currentItem,
+ const CompletionContextStrings &contextStrings,
+ BackInsertIterator result) const
+{
+ /*!
+ Completion is not provided on a script identifier expression because script identifier
+ expressions lack context information. Instead, find the first parent that has enough
+ context information and provide completion for this one.
+ For example, a script identifier expression \c{le} in
+ \badcode
+ for (;le;) { ... }
+ \endcode
+ will get completion for a property called \c{leProperty}, while the same script identifier
+ expression in
+ \badcode
+ for (le;;) { ... }
+ \endcode
+ will, in addition to \c{leProperty}, also get completion for the \c{let} statement snippet.
+ In this example, the parent used for the completion is the for-statement, of type
+ DomType::ScriptForStatement.
+
+ In addition of the parent for the context, use positionInfo to have exact information on where
+ the cursor is (to compare with the SourceLocations of tokens) and which item is at this position
+ (required to provide completion at the correct position, for example for attached properties).
+ */
+ const QQmlLSCompletionPosition positionInfo{ currentItem, contextStrings };
+ for (DomItem currentParent = currentItem; currentParent;
+ currentParent = currentParent.directParent()) {
+ const DomType currentType = currentParent.internalKind();
+
+ switch (currentType) {
+ case DomType::Id:
+ // suppress completions for ids
+ return;
+ case DomType::Pragma:
+ insidePragmaCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptType: {
+ if (currentParent.directParent().internalKind() == DomType::QmlObject) {
+ insideQmlObjectCompletion(currentParent.directParent(), positionInfo, result);
+ return;
+ }
+
+ LocalSymbolsTypes options;
+ options.setFlag(LocalSymbolsType::ObjectType);
+ options.setFlag(LocalSymbolsType::ValueType);
+ suggestReachableTypes(currentItem, options, CompletionItemKind::Class, result);
+ return;
+ }
+ case DomType::ScriptFormalParameter:
+ // no autocompletion inside of function parameter definition
+ return;
+ case DomType::Binding:
+ insideBindingCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::Import:
+ insideImportCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptForStatement:
+ insideForStatementCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptBlockStatement:
+ suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
+ return;
+ case DomType::QmlFile:
+ insideQmlFileCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::QmlObject:
+ insideQmlObjectCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::MethodInfo:
+ // suppress completions
+ return;
+ case DomType::PropertyDefinition:
+ insidePropertyDefinitionCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptBinaryExpression:
+ // ignore field member expressions: these need additional context from its parents
+ if (QQmlLSUtils::isFieldMemberExpression(currentParent))
+ continue;
+ insideBinaryExpressionCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptLiteral:
+ insideScriptLiteralCompletion(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptCallExpression:
+ insideCallExpression(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptIfStatement:
+ insideIfStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptReturnStatement:
+ insideReturnStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptWhileStatement:
+ insideWhileStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptDoWhileStatement:
+ insideDoWhileStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptForEachStatement:
+ insideForEachStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptTryCatchStatement:
+ /*!
+ \internal
+ The Ecmascript standard specifies that there can only be a block statement between \c
+ try and \c catch(...), \c try and \c finally and \c catch(...) and \c finally, so all of
+ these completions are already handled by the DomType::ScriptBlockStatement completion.
+ The only place in the try statement where there is no BlockStatement and therefore needs
+ its own completion is inside the catch parameter, but that is
+ \quotation
+ An optional identifier or pattern to hold the caught exception for the associated catch
+ block.
+ \endquotation
+ citing
+ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch?retiredLocale=de#exceptionvar.
+ This means that no completion is needed inside a catch-expression, as it should contain
+ an identifier that is not yet used anywhere.
+ Therefore, no completion is required at all when inside a try-statement but outside a
+ block-statement.
+ */
+ return;
+ case DomType::ScriptSwitchStatement:
+ insideSwitchStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptCaseClause:
+ insideCaseClause(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptDefaultClause:
+ if (ctxBeforeStatement(positionInfo, currentParent, QQmlJS::Dom::DefaultKeywordRegion))
+ continue;
+ insideDefaultClause(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptCaseBlock:
+ insideCaseBlock(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptVariableDeclaration:
+ // not needed: thats a list of ScriptVariableDeclarationEntry, and those entries cannot
+ // be suggested because they all start with `{`, `[` or an identifier that should not be
+ // in use yet.
+ return;
+ case DomType::ScriptVariableDeclarationEntry:
+ insideVariableDeclarationEntry(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptProperty:
+ // fallthrough: a ScriptProperty is a ScriptPattern but inside a JS Object. It gets the
+ // same completions as a ScriptPattern.
+ case DomType::ScriptPattern:
+ insideScriptPattern(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptThrowStatement:
+ insideThrowStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptLabelledStatement:
+ insideLabelledStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptContinueStatement:
+ insideContinueStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptBreakStatement:
+ insideBreakStatement(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptConditionalExpression:
+ insideConditionalExpression(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptUnaryExpression:
+ insideUnaryExpression(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptPostExpression:
+ insidePostExpression(currentParent, positionInfo, result);
+ return;
+ case DomType::ScriptParenthesizedExpression:
+ insideParenthesizedExpression(currentParent, positionInfo, result);
+ return;
+
+ // TODO: Implement those statements.
+ // In the meanwhile, suppress completions to avoid weird behaviors.
+ case DomType::ScriptArray:
+ case DomType::ScriptObject:
+ case DomType::ScriptElision:
+ case DomType::ScriptArrayEntry:
+ return;
+
+ default:
+ continue;
+ }
+ Q_UNREACHABLE();
+ }
+
+ // no completion could be found
+ qCDebug(QQmlLSUtilsLog) << "No completion was found for current request.";
+ return;
+}
+
+QQmlLSCompletion::QQmlLSCompletion(const QFactoryLoader &pluginLoader)
+{
+ const auto keys = pluginLoader.metaDataKeys();
+ for (qsizetype i = 0; i < keys.size(); ++i) {
+ auto instance = std::unique_ptr<QQmlLSPlugin>(
+ qobject_cast<QQmlLSPlugin *>(pluginLoader.instance(i)));
+ if (!instance)
+ continue;
+ if (auto completionInstance = instance->createCompletionPlugin())
+ m_plugins.push_back(std::move(completionInstance));
+ }
+}
+
+/*!
+\internal
+Helper method to call a method on all loaded plugins.
+*/
+void QQmlLSCompletion::collectFromPlugins(qxp::function_ref<CompletionFromPluginFunction> f,
+ BackInsertIterator result) const
+{
+ for (const auto &plugin : m_plugins) {
+ Q_ASSERT(plugin);
+ f(plugin.get(), result);
+ }
+}
+
+void QQmlLSCompletion::suggestSnippetsForLeftHandSideOfBinding(const DomItem &itemAtPosition,
+ BackInsertIterator result) const
+{
+ collectFromPlugins(
+ [&itemAtPosition](QQmlLSCompletionPlugin *p, BackInsertIterator result) {
+ p->suggestSnippetsForLeftHandSideOfBinding(itemAtPosition, result);
+ },
+ result);
+}
+
+void QQmlLSCompletion::suggestSnippetsForRightHandSideOfBinding(const DomItem &itemAtPosition,
+ BackInsertIterator result) const
+{
+ collectFromPlugins(
+ [&itemAtPosition](QQmlLSCompletionPlugin *p, BackInsertIterator result) {
+ p->suggestSnippetsForRightHandSideOfBinding(itemAtPosition, result);
+ },
+ result);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmllscompletion_p.h b/src/qmlls/qqmllscompletion_p.h
new file mode 100644
index 0000000000..a31d3daebc
--- /dev/null
+++ b/src/qmlls/qqmllscompletion_p.h
@@ -0,0 +1,226 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLLSCOMPLETION_H
+#define QQMLLSCOMPLETION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmlcompletioncontextstrings_p.h"
+#include "qqmllsutils_p.h"
+#include "qqmllsplugin_p.h"
+
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtCore/private/qduplicatetracker_p.h>
+#include <QtCore/private/qfactoryloader_p.h>
+#include <QtCore/qpluginloader.h>
+#include <QtCore/qxpfunctional.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QQmlLSCompletionLog)
+
+enum QQmlLSUtilsAppendOption { AppendSemicolon, AppendNothing };
+
+class QQmlLSCompletion
+{
+ using DomItem = QQmlJS::Dom::DomItem;
+public:
+ QQmlLSCompletion(const QFactoryLoader &pluginLoader);
+
+ using CompletionItem = QLspSpecification::CompletionItem;
+ using BackInsertIterator = std::back_insert_iterator<QList<CompletionItem>>;
+ QList<CompletionItem> completions(const DomItem &currentItem,
+ const CompletionContextStrings &ctx) const;
+
+ static CompletionItem makeSnippet(QUtf8StringView qualifier, QUtf8StringView label,
+ QUtf8StringView insertText);
+
+ static CompletionItem makeSnippet(QUtf8StringView label, QUtf8StringView insertText);
+
+private:
+ struct QQmlLSCompletionPosition
+ {
+ DomItem itemAtPosition;
+ CompletionContextStrings cursorPosition;
+ qsizetype offset() const { return cursorPosition.offset(); }
+ };
+
+ void collectCompletions(const DomItem &currentItem, const CompletionContextStrings &ctx,
+ BackInsertIterator result) const;
+
+ bool betweenLocations(QQmlJS::SourceLocation left, const QQmlLSCompletionPosition &positionInfo,
+ QQmlJS::SourceLocation right) const;
+ bool afterLocation(QQmlJS::SourceLocation left,
+ const QQmlLSCompletionPosition &positionInfo) const;
+ bool beforeLocation(const QQmlLSCompletionPosition &ctx, QQmlJS::SourceLocation right) const;
+ bool ctxBeforeStatement(const QQmlLSCompletionPosition &positionInfo,
+ const DomItem &parentForContext,
+ QQmlJS::Dom::FileLocationRegion firstRegion) const;
+ bool isCaseOrDefaultBeforeCtx(const DomItem &currentClause,
+ const QQmlLSCompletionPosition &positionInfo,
+ QQmlJS::Dom::FileLocationRegion keywordRegion) const;
+ DomItem previousCaseOfCaseBlock(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo) const;
+
+ void idsCompletions(const DomItem &component, BackInsertIterator it) const;
+
+ void suggestReachableTypes(const DomItem &context,
+ QQmlJS::Dom::LocalSymbolsTypes typeCompletionType,
+ QLspSpecification::CompletionItemKind kind,
+ BackInsertIterator it) const;
+
+ void suggestJSStatementCompletion(const DomItem &currentItem, BackInsertIterator it) const;
+ void suggestCaseAndDefaultStatementCompletion(BackInsertIterator it) const;
+ void suggestVariableDeclarationStatementCompletion(
+ BackInsertIterator it, QQmlLSUtilsAppendOption option = AppendSemicolon) const;
+
+ void suggestJSExpressionCompletion(const DomItem &context, BackInsertIterator it) const;
+
+ void suggestBindingCompletion(const DomItem &itemAtPosition, BackInsertIterator it) const;
+
+ void insideImportCompletionHelper(const DomItem &file,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+
+ void jsIdentifierCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames, BackInsertIterator it) const;
+
+ void methodCompletion(const QQmlJSScope::ConstPtr &scope, QDuplicateTracker<QString> *usedNames,
+ BackInsertIterator it) const;
+ void propertyCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames, BackInsertIterator it) const;
+ void enumerationCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames, BackInsertIterator it) const;
+ void enumerationValueCompletionHelper(const QStringList &enumeratorKeys,
+ BackInsertIterator it) const;
+
+ void enumerationValueCompletion(const QQmlJSScope::ConstPtr &scope,
+ const QString &enumeratorName, BackInsertIterator it) const;
+
+ static bool cursorInFrontOfItem(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo);
+ static bool cursorAfterColon(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo);
+ void insidePragmaCompletion(QQmlJS::Dom::DomItem currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideQmlObjectCompletion(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insidePropertyDefinitionCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideBindingCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideImportCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideQmlFileCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void suggestContinueAndBreakStatementIfNeeded(const DomItem &itemAtPosition,
+ BackInsertIterator it) const;
+ void insideScriptLiteralCompletion(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideCallExpression(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideIfStatement(const DomItem &currentItem, const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideReturnStatement(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideWhileStatement(const DomItem &currentItem,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideDoWhileStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideForStatementCompletion(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideForEachStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideSwitchStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition positionInfo,
+ BackInsertIterator it) const;
+ void insideCaseClause(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideCaseBlock(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo, BackInsertIterator it) const;
+ void insideDefaultClause(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideBinaryExpressionCompletion(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideScriptPattern(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideVariableDeclarationEntry(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideThrowStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideLabelledStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideContinueStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideBreakStatement(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideConditionalExpression(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideUnaryExpression(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insidePostExpression(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void insideParenthesizedExpression(const DomItem &parentForContext,
+ const QQmlLSCompletionPosition &positionInfo,
+ BackInsertIterator it) const;
+ void signalHandlerCompletion(const QQmlJSScope::ConstPtr &scope,
+ QDuplicateTracker<QString> *usedNames,
+ BackInsertIterator it) const;
+
+ void suggestSnippetsForLeftHandSideOfBinding(const DomItem &items,
+ BackInsertIterator result) const;
+
+ void suggestSnippetsForRightHandSideOfBinding(const DomItem &items,
+ BackInsertIterator result) const;
+
+private:
+ using CompletionFromPluginFunction = void(QQmlLSCompletionPlugin *plugin,
+ BackInsertIterator result);
+ void collectFromPlugins(const qxp::function_ref<CompletionFromPluginFunction> f,
+ BackInsertIterator result) const;
+
+ QStringList m_loadPaths;
+
+ std::vector<std::unique_ptr<QQmlLSCompletionPlugin>> m_plugins;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLLSCOMPLETION_H
diff --git a/src/qmlls/qqmllscompletionplugin.cpp b/src/qmlls/qqmllscompletionplugin.cpp
new file mode 100644
index 0000000000..fd47c691f7
--- /dev/null
+++ b/src/qmlls/qqmllscompletionplugin.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmllscompletionplugin_p.h"
diff --git a/src/qmlls/qqmllscompletionplugin_p.h b/src/qmlls/qqmllscompletionplugin_p.h
new file mode 100644
index 0000000000..0dde7bec76
--- /dev/null
+++ b/src/qmlls/qqmllscompletionplugin_p.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLLSCOMPLETIONPLUGIN_H
+#define QQMLLSCOMPLETIONPLUGIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <iterator>
+
+#include <QtQmlDom/private/qqmldomelements_p.h>
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlLSCompletionPlugin
+{
+public:
+ QQmlLSCompletionPlugin() = default;
+ virtual ~QQmlLSCompletionPlugin() = default;
+
+ using BackInsertIterator = std::back_insert_iterator<QList<QLspSpecification::CompletionItem>>;
+
+ virtual void suggestSnippetsForLeftHandSideOfBinding(const QQmlJS::Dom::DomItem &items,
+ BackInsertIterator result) const = 0;
+
+ virtual void suggestSnippetsForRightHandSideOfBinding(const QQmlJS::Dom::DomItem &items,
+ BackInsertIterator result) const = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLLSCOMPLETIONPLUGIN_H
diff --git a/src/qmlls/qqmllsplugin_p.h b/src/qmlls/qqmllsplugin_p.h
new file mode 100644
index 0000000000..07699ce2c5
--- /dev/null
+++ b/src/qmlls/qqmllsplugin_p.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMLLSPLUGIN_P_H
+#define QMLLSPLUGIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <memory>
+
+#include <QtCore/qtclasshelpermacros.h>
+#include <QtCore/qobject.h>
+#include <QtQmlLS/private/qqmllscompletionplugin_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlLSPlugin
+{
+public:
+ QQmlLSPlugin() = default;
+ virtual ~QQmlLSPlugin() = default;
+
+ Q_DISABLE_COPY_MOVE(QQmlLSPlugin)
+
+ virtual std::unique_ptr<QQmlLSCompletionPlugin> createCompletionPlugin() const = 0;
+};
+
+#define QmlLSPluginInterface_iid "org.qt-project.Qt.QmlLS.Plugin/1.0"
+Q_DECLARE_INTERFACE(QQmlLSPlugin, QmlLSPluginInterface_iid)
+
+QT_END_NAMESPACE
+
+#endif // QMLLSPLUGIN_P_H
diff --git a/src/qmlls/qqmllsutils.cpp b/src/qmlls/qqmllsutils.cpp
new file mode 100644
index 0000000000..bc9f21d7b9
--- /dev/null
+++ b/src/qmlls/qqmllsutils.cpp
@@ -0,0 +1,2183 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmllsutils_p.h"
+
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+#include <QtCore/qthreadpool.h>
+#include <QtCore/private/qduplicatetracker_p.h>
+#include <QtCore/QRegularExpression>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomscriptelements_p.h>
+#include <QtQmlDom/private/qqmldom_utils_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQmlCompiler/private/qqmljsutils_p.h>
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <optional>
+#include <set>
+#include <stack>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+using namespace QLspSpecification;
+using namespace QQmlJS::Dom;
+using namespace Qt::StringLiterals;
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QQmlLSUtilsLog, "qt.languageserver.utils")
+
+QString QQmlLSUtils::qualifiersFrom(const DomItem &el)
+{
+ const bool isAccess = QQmlLSUtils::isFieldMemberAccess(el);
+ if (!isAccess && !QQmlLSUtils::isFieldMemberExpression(el))
+ return {};
+
+ const DomItem fieldMemberExpressionBeginning = el.filterUp(
+ [](DomType, const DomItem &item) { return !QQmlLSUtils::isFieldMemberAccess(item); },
+ FilterUpOptions::ReturnOuter);
+ QStringList qualifiers =
+ QQmlLSUtils::fieldMemberExpressionBits(fieldMemberExpressionBeginning, el);
+
+ QString result;
+ for (const QString &qualifier : qualifiers)
+ result.append(qualifier).append(QChar(u'.'));
+ return result;
+}
+
+/*!
+ \internal
+ Helper to check if item is a Field Member Expression \c {<someExpression>.propertyName}.
+*/
+bool QQmlLSUtils::isFieldMemberExpression(const DomItem &item)
+{
+ return item.internalKind() == DomType::ScriptBinaryExpression
+ && item.field(Fields::operation).value().toInteger()
+ == ScriptElements::BinaryExpression::FieldMemberAccess;
+}
+
+/*!
+ \internal
+ Helper to check if item is a Field Member Access \c memberAccess in
+ \c {<someExpression>.memberAccess}.
+*/
+bool QQmlLSUtils::isFieldMemberAccess(const DomItem &item)
+{
+ auto parent = item.directParent();
+ if (!isFieldMemberExpression(parent))
+ return false;
+
+ DomItem rightHandSide = parent.field(Fields::right);
+ return item == rightHandSide;
+}
+
+/*!
+ \internal
+ Get the bits of a field member expression, like \c{a}, \c{b} and \c{c} for \c{a.b.c}.
+
+ stopAtChild can either be an FieldMemberExpression, a ScriptIdentifierExpression or a default
+ constructed DomItem: This exits early before processing Field::right of an
+ FieldMemberExpression stopAtChild, or before processing a ScriptIdentifierExpression stopAtChild.
+ No early exits if stopAtChild is default constructed.
+*/
+QStringList QQmlLSUtils::fieldMemberExpressionBits(const DomItem &item, const DomItem &stopAtChild)
+{
+ const bool isAccess = isFieldMemberAccess(item);
+ const bool isExpression = isFieldMemberExpression(item);
+
+ // assume it is a non-qualified name
+ if (!isAccess && !isExpression)
+ return { item.value().toString() };
+
+ const DomItem stopMarker =
+ isFieldMemberExpression(stopAtChild) ? stopAtChild : stopAtChild.directParent();
+
+ QStringList result;
+ DomItem current =
+ isAccess ? item.directParent() : (isFieldMemberExpression(item) ? item : DomItem{});
+
+ for (; isFieldMemberExpression(current); current = current.field(Fields::right)) {
+ result << current.field(Fields::left).value().toString();
+
+ if (current == stopMarker)
+ return result;
+ }
+ result << current.value().toString();
+
+ return result;
+}
+
+/*!
+ \internal
+ The language server protocol calls "URI" what QML calls "URL".
+ According to RFC 3986, a URL is a special case of URI that not only
+ identifies a resource but also shows how to access it.
+ In QML, however, URIs are distinct from URLs. URIs are the
+ identifiers of modules, for example "QtQuick.Controls".
+ In order to not confuse the terms we interpret language server URIs
+ as URLs in the QML code model.
+ This method marks a point of translation between the terms, but does
+ not have to change the actual URI/URL.
+
+ \sa QQmlLSUtils::qmlUriToLspUrl
+ */
+QByteArray QQmlLSUtils::lspUriToQmlUrl(const QByteArray &uri)
+{
+ return uri;
+}
+
+QByteArray QQmlLSUtils::qmlUrlToLspUri(const QByteArray &url)
+{
+ return url;
+}
+
+/*!
+ \internal
+ \brief Converts a QQmlJS::SourceLocation to a LSP Range.
+
+ QQmlJS::SourceLocation starts counting lines and rows at 1, but the LSP Range starts at 0.
+ Also, the QQmlJS::SourceLocation contains startLine, startColumn and length while the LSP Range
+ contains startLine, startColumn, endLine and endColumn, which must be computed from the actual
+ qml code.
+ */
+QLspSpecification::Range QQmlLSUtils::qmlLocationToLspLocation(const QString &code,
+ QQmlJS::SourceLocation qmlLocation)
+{
+ Range range;
+
+ range.start.line = qmlLocation.startLine - 1;
+ range.start.character = qmlLocation.startColumn - 1;
+
+ auto end = QQmlLSUtils::textRowAndColumnFrom(code, qmlLocation.end());
+ range.end.line = end.line;
+ range.end.character = end.character;
+ return range;
+}
+
+/*!
+ \internal
+ \brief Convert a text position from (line, column) into an offset.
+
+ Row, Column and the offset are all 0-based.
+ For example, \c{s[textOffsetFrom(s, 5, 55)]} returns the character of s at line 5 and column 55.
+
+ \sa QQmlLSUtils::textRowAndColumnFrom
+*/
+qsizetype QQmlLSUtils::textOffsetFrom(const QString &text, int row, int column)
+{
+ int targetLine = row;
+ qsizetype i = 0;
+ while (i != text.size() && targetLine != 0) {
+ QChar c = text.at(i++);
+ if (c == u'\n') {
+ --targetLine;
+ }
+ if (c == u'\r') {
+ if (i != text.size() && text.at(i) == u'\n')
+ ++i;
+ --targetLine;
+ }
+ }
+ qsizetype leftChars = column;
+ while (i != text.size() && leftChars) {
+ QChar c = text.at(i);
+ if (c == u'\n' || c == u'\r')
+ break;
+ ++i;
+ if (!c.isLowSurrogate())
+ --leftChars;
+ }
+ return i;
+}
+
+/*!
+ \internal
+ \brief Convert a text position from an offset into (line, column).
+
+ Row, Column and the offset are all 0-based.
+ For example, \c{textRowAndColumnFrom(s, 55)} returns the line and columns of the
+ character at \c {s[55]}.
+
+ \sa QQmlLSUtils::textOffsetFrom
+*/
+QQmlLSUtilsTextPosition QQmlLSUtils::textRowAndColumnFrom(const QString &text, qsizetype offset)
+{
+ int row = 0;
+ int column = 0;
+ qsizetype currentLineOffset = 0;
+ for (qsizetype i = 0; i < offset; i++) {
+ QChar c = text[i];
+ if (c == u'\n') {
+ row++;
+ currentLineOffset = i + 1;
+ } else if (c == u'\r') {
+ if (i > 0 && text[i - 1] == u'\n')
+ currentLineOffset++;
+ }
+ }
+ column = offset - currentLineOffset;
+
+ return { row, column };
+}
+
+static QList<QQmlLSUtilsItemLocation>::const_iterator
+handlePropertyDefinitionAndBindingOverlap(const QList<QQmlLSUtilsItemLocation> &items,
+ qsizetype offsetInFile)
+{
+ auto smallest = std::min_element(
+ items.begin(), items.end(),
+ [](const QQmlLSUtilsItemLocation &a, const QQmlLSUtilsItemLocation &b) {
+ return a.fileLocation->info().fullRegion.length
+ < b.fileLocation->info().fullRegion.length;
+ });
+
+ if (smallest->domItem.internalKind() == DomType::Binding) {
+ // weird edge case: the filelocations of property definitions and property bindings are
+ // actually overlapping, which means that qmlls cannot distinguish between bindings and
+ // bindings in property definitions. Those need to be treated differently for
+ // autocompletion, for example.
+ // Therefore: when inside a binding and a propertydefinition, choose the property definition
+ // if offsetInFile is before the colon, like for example:
+ // property var helloProperty: Rectangle { /*...*/ }
+ // |----return propertydef---|-- return Binding ---|
+
+ // get the smallest property definition to avoid getting the property definition that the
+ // current QmlObject is getting bound to!
+ auto smallestPropertyDefinition = std::min_element(
+ items.begin(), items.end(),
+ [](const QQmlLSUtilsItemLocation &a, const QQmlLSUtilsItemLocation &b) {
+ // make property definition smaller to avoid getting smaller items that are not
+ // property definitions
+ const bool aIsPropertyDefinition =
+ a.domItem.internalKind() == DomType::PropertyDefinition;
+ const bool bIsPropertyDefinition =
+ b.domItem.internalKind() == DomType::PropertyDefinition;
+ return aIsPropertyDefinition > bIsPropertyDefinition
+ && a.fileLocation->info().fullRegion.length
+ < b.fileLocation->info().fullRegion.length;
+ });
+
+ if (smallestPropertyDefinition->domItem.internalKind() != DomType::PropertyDefinition)
+ return smallest;
+
+ const auto propertyDefinitionColon =
+ smallestPropertyDefinition->fileLocation->info().regions[ColonTokenRegion];
+ const auto smallestColon = smallest->fileLocation->info().regions[ColonTokenRegion];
+ // sanity check: is it the definition of the current binding? check if they both have their
+ // ':' at the same location
+ if (propertyDefinitionColon.isValid() && propertyDefinitionColon == smallestColon
+ && offsetInFile < smallestColon.offset) {
+ return smallestPropertyDefinition;
+ }
+ }
+ return smallest;
+}
+
+static QList<QQmlLSUtilsItemLocation>
+filterItemsFromTextLocation(const QList<QQmlLSUtilsItemLocation> &items, qsizetype offsetInFile)
+{
+ if (items.size() < 2)
+ return items;
+
+ // if there are multiple items, take the smallest one + its neighbors
+ // this allows to prefer inline components over main components, when both contain the
+ // current textposition, and to disregard internal structures like property maps, which
+ // "contain" everything from their first-appearing to last-appearing property (e.g. also
+ // other stuff in between those two properties).
+
+ QList<QQmlLSUtilsItemLocation> filteredItems;
+
+ auto smallest = handlePropertyDefinitionAndBindingOverlap(items, offsetInFile);
+
+ filteredItems.append(*smallest);
+
+ const QQmlJS::SourceLocation smallestLoc = smallest->fileLocation->info().fullRegion;
+ const quint32 smallestBegin = smallestLoc.begin();
+ const quint32 smallestEnd = smallestLoc.end();
+
+ for (auto it = items.begin(); it != items.end(); it++) {
+ if (it == smallest)
+ continue;
+
+ const QQmlJS::SourceLocation itLoc = it->fileLocation->info().fullRegion;
+ const quint32 itBegin = itLoc.begin();
+ const quint32 itEnd = itLoc.end();
+ if (itBegin == smallestEnd || smallestBegin == itEnd) {
+ filteredItems.append(*it);
+ }
+ }
+ return filteredItems;
+}
+
+/*!
+ \internal
+ \brief Find the DomItem representing the object situated in file at given line and
+ character/column.
+
+ If line and character point between two objects, two objects might be returned.
+ If line and character point to whitespace, it might return an inner node of the QmlDom-Tree.
+ */
+QList<QQmlLSUtilsItemLocation> QQmlLSUtils::itemsFromTextLocation(const DomItem &file, int line,
+ int character)
+{
+ QList<QQmlLSUtilsItemLocation> itemsFound;
+ std::shared_ptr<QmlFile> filePtr = file.ownerAs<QmlFile>();
+ if (!filePtr)
+ return itemsFound;
+ FileLocations::Tree t = filePtr->fileLocationsTree();
+ Q_ASSERT(t);
+ QString code = filePtr->code(); // do something more advanced wrt to changes wrt to this->code?
+ QList<QQmlLSUtilsItemLocation> toDo;
+ qsizetype targetPos = textOffsetFrom(code, line, character);
+ Q_ASSERT(targetPos >= 0);
+ auto containsTarget = [targetPos](QQmlJS::SourceLocation l) {
+ if constexpr (sizeof(qsizetype) <= sizeof(quint32)) {
+ return l.begin() <= quint32(targetPos) && quint32(targetPos) <= l.end();
+ } else {
+ return l.begin() <= targetPos && targetPos <= l.end();
+ }
+ };
+ if (containsTarget(t->info().fullRegion)) {
+ QQmlLSUtilsItemLocation loc;
+ loc.domItem = file;
+ loc.fileLocation = t;
+ toDo.append(loc);
+ }
+ while (!toDo.isEmpty()) {
+ QQmlLSUtilsItemLocation iLoc = toDo.last();
+ toDo.removeLast();
+
+ bool inParentButOutsideChildren = true;
+
+ auto subEls = iLoc.fileLocation->subItems();
+ for (auto it = subEls.begin(); it != subEls.end(); ++it) {
+ auto subLoc = std::static_pointer_cast<AttachedInfoT<FileLocations>>(it.value());
+ Q_ASSERT(subLoc);
+
+ if (containsTarget(subLoc->info().fullRegion)) {
+ QQmlLSUtilsItemLocation subItem;
+ subItem.domItem = iLoc.domItem.path(it.key());
+ if (!subItem.domItem) {
+ qCDebug(QQmlLSUtilsLog)
+ << "A DomItem child is missing or the FileLocationsTree structure does "
+ "not follow the DomItem Structure.";
+ continue;
+ }
+ // the parser inserts empty Script Expressions for bindings that are not completely
+ // written out yet. Ignore them here.
+ if (subItem.domItem.internalKind() == DomType::ScriptExpression
+ && subLoc->info().fullRegion.length == 0) {
+ continue;
+ }
+ subItem.fileLocation = subLoc;
+ toDo.append(subItem);
+ inParentButOutsideChildren = false;
+ }
+ }
+ if (inParentButOutsideChildren) {
+ itemsFound.append(iLoc);
+ }
+ }
+
+ // filtering step:
+ auto filtered = filterItemsFromTextLocation(itemsFound, targetPos);
+ return filtered;
+}
+
+DomItem QQmlLSUtils::baseObject(const DomItem &object)
+{
+ DomItem prototypes;
+ DomItem qmlObject = object.qmlObject();
+ // object is (or is inside) an inline component definition
+ if (object.internalKind() == DomType::QmlComponent || !qmlObject) {
+ prototypes = object.component()
+ .field(Fields::objects)
+ .index(0)
+ .field(QQmlJS::Dom::Fields::prototypes);
+ } else {
+ // object is (or is inside) a QmlObject
+ prototypes = qmlObject.field(QQmlJS::Dom::Fields::prototypes);
+ }
+ switch (prototypes.indexes()) {
+ case 0:
+ return {};
+ case 1:
+ break;
+ default:
+ qDebug() << "Multiple prototypes found for " << object.name() << ", taking the first one.";
+ break;
+ }
+ QQmlJS::Dom::DomItem base = prototypes.index(0).proceedToScope();
+ return base;
+}
+
+static std::optional<QQmlLSUtilsLocation> locationFromDomItem(const DomItem &item,
+ FileLocationRegion region)
+{
+ QQmlLSUtilsLocation location;
+ location.filename = item.canonicalFilePath();
+
+ auto tree = FileLocations::treeOf(item);
+ // tree is null for C++ defined types, for example
+ if (!tree)
+ return {};
+
+ location.sourceLocation = FileLocations::region(tree, region);
+ if (!location.sourceLocation.isValid() && region != QQmlJS::Dom::MainRegion)
+ location.sourceLocation = FileLocations::region(tree, MainRegion);
+ return location;
+}
+
+/*!
+ \internal
+ \brief Returns the location of the type definition pointed by object.
+
+ For a \c PropertyDefinition, return the location of the type of the property.
+ For a \c Binding, return the bound item's type location if an QmlObject is bound, and otherwise
+ the type of the property.
+ For a \c QmlObject, return the location of the QmlObject's base.
+ For an \c Id, return the location of the object to which the id resolves.
+ For a \c Methodparameter, return the location of the type of the parameter.
+ Otherwise, return std::nullopt.
+ */
+std::optional<QQmlLSUtilsLocation> QQmlLSUtils::findTypeDefinitionOf(const DomItem &object)
+{
+ DomItem typeDefinition;
+
+ switch (object.internalKind()) {
+ case QQmlJS::Dom::DomType::QmlComponent:
+ typeDefinition = object.field(Fields::objects).index(0);
+ break;
+ case QQmlJS::Dom::DomType::QmlObject:
+ typeDefinition = baseObject(object);
+ break;
+ case QQmlJS::Dom::DomType::Binding: {
+ auto binding = object.as<Binding>();
+ Q_ASSERT(binding);
+
+ // try to grab the type from the bound object
+ if (binding->valueKind() == BindingValueKind::Object) {
+ typeDefinition = baseObject(object.field(Fields::value));
+ break;
+ } else {
+ // use the type of the property it is bound on for scriptexpression etc.
+ DomItem propertyDefinition;
+ const QString bindingName = binding->name();
+ object.containingObject().visitLookup(
+ bindingName,
+ [&propertyDefinition](const DomItem &item) {
+ if (item.internalKind() == QQmlJS::Dom::DomType::PropertyDefinition) {
+ propertyDefinition = item;
+ return false;
+ }
+ return true;
+ },
+ LookupType::PropertyDef);
+ typeDefinition = propertyDefinition.field(Fields::type).proceedToScope();
+ break;
+ }
+ Q_UNREACHABLE();
+ }
+ case QQmlJS::Dom::DomType::Id:
+ typeDefinition = object.field(Fields::referredObject).proceedToScope();
+ break;
+ case QQmlJS::Dom::DomType::PropertyDefinition:
+ case QQmlJS::Dom::DomType::MethodParameter:
+ case QQmlJS::Dom::DomType::MethodInfo:
+ typeDefinition = object.field(Fields::type).proceedToScope();
+ break;
+ case QQmlJS::Dom::DomType::ScriptIdentifierExpression: {
+ if (DomItem type = object.filterUp(
+ [](DomType k, const DomItem &) { return k == DomType::ScriptType; },
+ FilterUpOptions::ReturnOuter)) {
+
+ const QString name = fieldMemberExpressionBits(type.field(Fields::typeName)).join(u'.');
+ switch (type.directParent().internalKind()) {
+ case DomType::QmlObject:
+ // is the type name of a QmlObject, like Item in `Item {...}`
+ typeDefinition = baseObject(type.directParent());
+ break;
+ case DomType::QmlComponent:
+ typeDefinition = type.directParent();
+ return locationFromDomItem(typeDefinition, FileLocationRegion::IdentifierRegion);
+ break;
+ default:
+ // is a type annotation, like Item in `function f(x: Item) { ... }`
+ typeDefinition = object.path(Paths::lookupTypePath(name));
+ if (typeDefinition.internalKind() == DomType::Export) {
+ typeDefinition = typeDefinition.field(Fields::type).get();
+ }
+ }
+ break;
+ }
+ if (DomItem id = object.filterUp(
+ [](DomType k, const DomItem &) { return k == DomType::Id; },
+ FilterUpOptions::ReturnOuter)) {
+
+ typeDefinition = id.field(Fields::referredObject).proceedToScope();
+ break;
+ }
+
+ auto scope = QQmlLSUtils::resolveExpressionType(
+ object, QQmlLSUtilsResolveOptions::ResolveActualTypeForFieldMemberExpression);
+ if (!scope)
+ return {};
+
+ if (scope->type == QmlObjectIdIdentifier) {
+ return QQmlLSUtilsLocation{ scope->semanticScope->filePath(),
+ scope->semanticScope->sourceLocation() };
+ }
+
+ typeDefinition = QQmlLSUtils::sourceLocationToDomItem(
+ object.containingFile(), scope->semanticScope->sourceLocation());
+ return locationFromDomItem(typeDefinition.component(),
+ FileLocationRegion::IdentifierRegion);
+ }
+ default:
+ qDebug() << "QQmlLSUtils::findTypeDefinitionOf: Found unimplemented Type"
+ << object.internalKindStr();
+ return {};
+ }
+
+ return locationFromDomItem(typeDefinition, FileLocationRegion::MainRegion);
+}
+
+static bool findDefinitionFromItem(const DomItem &item, const QString &name)
+{
+ if (const QQmlJSScope::ConstPtr &scope = item.semanticScope()) {
+ qCDebug(QQmlLSUtilsLog) << "Searching for definition in" << item.internalKindStr();
+ if (auto jsIdentifier = scope->ownJSIdentifier(name)) {
+ qCDebug(QQmlLSUtilsLog) << "Found scope" << scope->baseTypeName();
+ return true;
+ }
+ }
+ return false;
+}
+
+static DomItem findJSIdentifierDefinition(const DomItem &item, const QString &name)
+{
+ DomItem definitionOfItem;
+ item.visitUp([&name, &definitionOfItem](const DomItem &i) {
+ if (findDefinitionFromItem(i, name)) {
+ definitionOfItem = i;
+ return false;
+ }
+ // early exit: no JS definitions/usages outside the ScriptExpression DOM element.
+ if (i.internalKind() == DomType::ScriptExpression)
+ return false;
+ return true;
+ });
+
+ if (definitionOfItem)
+ return definitionOfItem;
+
+ // special case: somebody asks for usages of a function parameter from its definition
+ // function parameters are defined in the method's scope
+ if (DomItem res = item.filterUp([](DomType k, const DomItem &) { return k == DomType::MethodInfo; },
+ FilterUpOptions::ReturnOuter)) {
+ DomItem candidate = res.field(Fields::body).field(Fields::scriptElement);
+ if (findDefinitionFromItem(candidate, name)) {
+ return candidate;
+ }
+ }
+
+ return definitionOfItem;
+}
+
+/*!
+\internal
+Represents a signal, signal handler, property, property changed signal or a property changed
+handler.
+ */
+struct SignalOrProperty
+{
+ /*!
+ \internal The name of the signal or property, independent of whether this is a changed signal
+ or handler.
+ */
+ QString name;
+ QQmlLSUtilsIdentifierType type;
+};
+
+/*!
+\internal
+\brief Find out if \c{name} is a signal, signal handler, property, property changed signal, or a
+property changed handler in the given QQmlJSScope.
+
+Heuristic to find if name is a property, property changed signal, .... because those can appear
+under different names, for example \c{mySignal} and \c{onMySignal} for a signal.
+This will give incorrect results as soon as properties/signals/methods are called \c{onMySignal},
+\c{on<some already existing property>Changed}, ..., but the good news is that the engine also
+will act weird in these cases (e.g. one cannot bind to a property called like an already existing
+signal or a property changed handler).
+For future reference: you can always add additional checks to check the existence of those buggy
+properties/signals/methods by looking if they exist in the QQmlJSScope.
+*/
+static std::optional<SignalOrProperty> resolveNameInQmlScope(const QString &name,
+ const QQmlJSScope::ConstPtr &owner)
+{
+ if (owner->hasProperty(name)) {
+ return SignalOrProperty{ name, PropertyIdentifier };
+ }
+
+ if (const auto propertyName = QQmlSignalNames::changedHandlerNameToPropertyName(name)) {
+ if (owner->hasProperty(*propertyName)) {
+ return SignalOrProperty{ *propertyName, PropertyChangedHandlerIdentifier };
+ }
+ }
+
+ if (const auto signalName = QQmlSignalNames::handlerNameToSignalName(name)) {
+ if (auto methods = owner->methods(*signalName); !methods.isEmpty()) {
+ if (methods.front().methodType() == QQmlJSMetaMethodType::Signal) {
+ return SignalOrProperty{ *signalName, SignalHandlerIdentifier };
+ }
+ }
+ }
+
+ if (const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
+ if (owner->hasProperty(*propertyName)) {
+ return SignalOrProperty{ *propertyName, PropertyChangedSignalIdentifier };
+ }
+ }
+
+ if (auto methods = owner->methods(name); !methods.isEmpty()) {
+ if (methods.front().methodType() == QQmlJSMetaMethodType::Signal) {
+ return SignalOrProperty{ name, SignalIdentifier };
+ }
+ return SignalOrProperty{ name, MethodIdentifier };
+ }
+ return std::nullopt;
+}
+
+/*!
+\internal
+Returns a list of names, that when belonging to the same targetType, should be considered equal.
+This is used to find signal handlers as usages of their corresponding signals, for example.
+*/
+static QStringList namesOfPossibleUsages(const QString &name,
+ const DomItem &item,
+ const QQmlJSScope::ConstPtr &targetType)
+{
+ QStringList namesToCheck = { name };
+ if (item.internalKind() == DomType::EnumItem || item.internalKind() == DomType::EnumDecl)
+ return namesToCheck;
+
+ auto namings = resolveNameInQmlScope(name, targetType);
+ if (!namings)
+ return namesToCheck;
+ switch (namings->type) {
+ case PropertyIdentifier: {
+ // for a property, also find bindings to its onPropertyChanged handler + propertyChanged
+ // signal
+ const QString propertyChangedHandler =
+ QQmlSignalNames::propertyNameToChangedHandlerName(namings->name);
+ namesToCheck.append(propertyChangedHandler);
+
+ const QString propertyChangedSignal =
+ QQmlSignalNames::propertyNameToChangedSignalName(namings->name);
+ namesToCheck.append(propertyChangedSignal);
+ break;
+ }
+ case PropertyChangedHandlerIdentifier: {
+ // for a property changed handler, also find the usages of its property + propertyChanged
+ // signal
+ namesToCheck.append(namings->name);
+ namesToCheck.append(QQmlSignalNames::propertyNameToChangedSignalName(namings->name));
+ break;
+ }
+ case PropertyChangedSignalIdentifier: {
+ // for a property changed signal, also find the usages of its property + onPropertyChanged
+ // handlers
+ namesToCheck.append(namings->name);
+ namesToCheck.append(QQmlSignalNames::propertyNameToChangedHandlerName(namings->name));
+ break;
+ }
+ case SignalIdentifier: {
+ // for a signal, also find bindings to its onSignalHandler.
+ namesToCheck.append(QQmlSignalNames::signalNameToHandlerName(namings->name));
+ break;
+ }
+ case SignalHandlerIdentifier: {
+ // for a signal handler, also find the usages of the signal it handles
+ namesToCheck.append(namings->name);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ return namesToCheck;
+}
+
+template<typename Predicate>
+QQmlJSScope::ConstPtr findDefiningScopeIf(QQmlJSScope::ConstPtr referrerScope, Predicate &&check)
+{
+ QQmlJSScope::ConstPtr result;
+ QQmlJSUtils::searchBaseAndExtensionTypes(referrerScope, [&](QQmlJSScope::ConstPtr scope) {
+ if (check(scope)) {
+ result = scope;
+ return true;
+ }
+ return false;
+ });
+
+ return result;
+}
+
+/*!
+\internal
+\brief Finds the scope where a property is first defined.
+
+Starts looking for the name starting from the given scope and traverse through base and
+extension types.
+*/
+static QQmlJSScope::ConstPtr findDefiningScopeForProperty(QQmlJSScope::ConstPtr referrerScope,
+ const QString &nameToCheck)
+{
+ return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
+ return scope->hasOwnProperty(nameToCheck);
+ });
+}
+
+/*!
+\internal
+See also findDefiningScopeForProperty().
+
+Special case: you can also bind to a signal handler.
+*/
+static QQmlJSScope::ConstPtr findDefiningScopeForBinding(QQmlJSScope::ConstPtr referrerScope,
+ const QString &nameToCheck)
+{
+ return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
+ return scope->hasOwnProperty(nameToCheck) || scope->hasOwnMethod(nameToCheck);
+ });
+}
+
+/*!
+\internal
+See also findDefiningScopeForProperty().
+*/
+static QQmlJSScope::ConstPtr findDefiningScopeForMethod(QQmlJSScope::ConstPtr referrerScope,
+ const QString &nameToCheck)
+{
+ return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
+ return scope->hasOwnMethod(nameToCheck);
+ });
+}
+
+/*!
+\internal
+See also findDefiningScopeForProperty().
+*/
+static QQmlJSScope::ConstPtr findDefiningScopeForEnumeration(QQmlJSScope::ConstPtr referrerScope,
+ const QString &nameToCheck)
+{
+ return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
+ return scope->hasOwnEnumeration(nameToCheck);
+ });
+}
+
+/*!
+\internal
+See also findDefiningScopeForProperty().
+*/
+static QQmlJSScope::ConstPtr findDefiningScopeForEnumerationKey(QQmlJSScope::ConstPtr referrerScope,
+ const QString &nameToCheck)
+{
+ return findDefiningScopeIf(referrerScope, [&nameToCheck](const QQmlJSScope::ConstPtr &scope) {
+ return scope->hasOwnEnumerationKey(nameToCheck);
+ });
+}
+
+/*!
+ Filter away the parts of the Dom not needed for find usages, by following the profiler's
+ information.
+ 1. "propertyInfos" tries to require all inherited properties of some QmlObject. That is super
+ slow (profiler says it eats 90% of the time needed by `tst_qmlls_utils findUsages`!) and is not
+ needed for usages.
+ 2. "get" tries to resolve references, like base types saved in prototypes for example, and is not
+ needed to find usages. Profiler says it eats 70% of the time needed by `tst_qmlls_utils
+ findUsages`.
+ 3. "defaultPropertyName" also recurses through base types and is not needed to find usages.
+*/
+static FieldFilter filterForFindUsages()
+{
+ FieldFilter filter{ {},
+ {
+ { QString(), QString::fromUtf16(Fields::propertyInfos) },
+ { QString(), QString::fromUtf16(Fields::defaultPropertyName) },
+ { QString(), QString::fromUtf16(Fields::get) },
+ } };
+ return filter;
+};
+
+static void findUsagesOfNonJSIdentifiers(const DomItem &item, const QString &name,
+ QList<QQmlLSUtilsLocation> &result)
+{
+ const auto expressionType = QQmlLSUtils::resolveExpressionType(item, ResolveOwnerType);
+ if (!expressionType)
+ return;
+
+ const QStringList namesToCheck = namesOfPossibleUsages(name, item, expressionType->semanticScope);
+
+ const auto addLocationIfTypeMatchesTarget = [&result,
+ &expressionType](const DomItem &toBeResolved,
+ FileLocationRegion subRegion) {
+ const auto currentType = QQmlLSUtils::resolveExpressionType(
+ toBeResolved, QQmlLSUtilsResolveOptions::ResolveOwnerType);
+ if (!currentType)
+ return;
+
+ const QQmlJSScope::ConstPtr target = expressionType->semanticScope;
+ const QQmlJSScope::ConstPtr current = currentType->semanticScope;
+ if (target == current) {
+ auto tree = FileLocations::treeOf(toBeResolved);
+ QQmlJS::SourceLocation sourceLocation;
+
+ sourceLocation = FileLocations::region(tree, subRegion);
+ if (!sourceLocation.isValid())
+ return;
+
+ QQmlLSUtilsLocation location{ toBeResolved.canonicalFilePath(), sourceLocation };
+ if (!result.contains(location))
+ result.append(location);
+ }
+ };
+
+ auto findUsages = [&addLocationIfTypeMatchesTarget, &name,
+ &namesToCheck](Path, const DomItem &current, bool) -> bool {
+ bool continueForChildren = true;
+ if (auto scope = current.semanticScope()) {
+ // is the current property shadowed by some JS identifier? ignore current + its children
+ if (scope->ownJSIdentifier(name)) {
+ return false;
+ }
+ }
+ switch (current.internalKind()) {
+ case DomType::QmlObject:
+ case DomType::Binding:
+ case DomType::MethodInfo:
+ case DomType::PropertyDefinition: {
+ const QString propertyName = current.field(Fields::name).value().toString();
+ if (namesToCheck.contains(propertyName))
+ addLocationIfTypeMatchesTarget(current, IdentifierRegion);
+ return continueForChildren;
+ }
+ case DomType::ScriptIdentifierExpression: {
+ const QString identifierName = current.field(Fields::identifier).value().toString();
+ if (namesToCheck.contains(identifierName))
+ addLocationIfTypeMatchesTarget(current, MainRegion);
+ return continueForChildren;
+ }
+ case DomType::ScriptLiteral: {
+ const QString literal = current.field(Fields::value).value().toString();
+ if (namesToCheck.contains(literal))
+ addLocationIfTypeMatchesTarget(current, MainRegion);
+ return continueForChildren;
+ }
+ case DomType::EnumItem: {
+ // Only look for the first enum defined. The inner enums
+ // have no way to be accessed.
+ const auto parentPath = current.containingObject().pathFromOwner();
+ const auto index = parentPath.last().headIndex();
+ if (index != 0)
+ return continueForChildren;
+ const QString enumValue = current.field(Fields::name).value().toString();
+ if (namesToCheck.contains(enumValue))
+ addLocationIfTypeMatchesTarget(current, IdentifierRegion);
+ return continueForChildren;
+ }
+ case DomType::EnumDecl: {
+ // Only look for the first enum defined. The inner enums
+ // have no way to be accessed.
+ const auto parentPath = current.pathFromOwner();
+ const auto index = parentPath.last().headIndex();
+ if (index != 0)
+ return continueForChildren;
+ const QString enumValue = current.field(Fields::name).value().toString();
+ if (namesToCheck.contains(enumValue))
+ addLocationIfTypeMatchesTarget(current, IdentifierRegion);
+ return continueForChildren;
+ }
+ default:
+ return continueForChildren;
+ };
+
+ Q_UNREACHABLE_RETURN(continueForChildren);
+ };
+
+ const DomItem qmlFiles = item.top().field(Fields::qmlFileWithPath);
+ const auto filter = filterForFindUsages();
+ for (const QString &file : qmlFiles.keys()) {
+ const DomItem currentFileComponents =
+ qmlFiles.key(file).field(Fields::currentItem).field(Fields::components);
+ currentFileComponents.visitTree(Path(), emptyChildrenVisitor,
+ VisitOption::Recurse | VisitOption::VisitSelf, findUsages,
+ emptyChildrenVisitor, filter);
+ }
+}
+
+static QQmlLSUtilsLocation locationFromJSIdentifierDefinition(const DomItem &definitionOfItem,
+ const QString &name)
+{
+ Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
+ && definitionOfItem.semanticScope()->ownJSIdentifier(name).has_value(),
+ "QQmlLSUtils::locationFromJSIdentifierDefinition",
+ "JS definition does not actually define the JS identifier. "
+ "Did you obtain definitionOfItem from findJSIdentifierDefinition() ?");
+ QQmlJS::SourceLocation location =
+ definitionOfItem.semanticScope()->ownJSIdentifier(name).value().location;
+
+ QQmlLSUtilsLocation result = { definitionOfItem.canonicalFilePath(), location };
+ return result;
+}
+
+static void findUsagesHelper(
+ const DomItem &item, const QString &name, QList<QQmlLSUtilsLocation> &result)
+{
+ qCDebug(QQmlLSUtilsLog) << "Looking for JS identifier with name" << name;
+ DomItem definitionOfItem = findJSIdentifierDefinition(item, name);
+
+ // if there is no definition found: check if name was a property or an id instead
+ if (!definitionOfItem) {
+ qCDebug(QQmlLSUtilsLog) << "No defining JS-Scope found!";
+ findUsagesOfNonJSIdentifiers(item, name, result);
+ return;
+ }
+
+ definitionOfItem.visitTree(
+ Path(), emptyChildrenVisitor, VisitOption::VisitAdopted | VisitOption::Recurse,
+ [&name, &result](Path, const DomItem &item, bool) -> bool {
+ qCDebug(QQmlLSUtilsLog) << "Visiting a " << item.internalKindStr();
+ if (item.internalKind() == DomType::ScriptIdentifierExpression
+ && item.field(Fields::identifier).value().toString() == name) {
+ // add this usage
+ auto fileLocation = FileLocations::treeOf(item);
+ if (!fileLocation) {
+ qCWarning(QQmlLSUtilsLog) << "Failed finding filelocation of found usage";
+ return true;
+ }
+ const QQmlJS::SourceLocation location = fileLocation->info().fullRegion;
+ const QString fileName = item.canonicalFilePath();
+ result.append({ fileName, location });
+ return true;
+ } else if (QQmlJSScope::ConstPtr scope = item.semanticScope();
+ scope && scope->ownJSIdentifier(name)) {
+ // current JS identifier has been redefined, do not visit children
+ return false;
+ }
+ return true;
+ },
+ emptyChildrenVisitor, filterForFindUsages());
+
+ const QQmlLSUtilsLocation definition =
+ locationFromJSIdentifierDefinition(definitionOfItem, name);
+ if (!result.contains(definition))
+ result.append(definition);
+}
+
+QList<QQmlLSUtilsLocation> QQmlLSUtils::findUsagesOf(const DomItem &item)
+{
+ QList<QQmlLSUtilsLocation> result;
+
+ switch (item.internalKind()) {
+ case DomType::ScriptIdentifierExpression: {
+ const QString name = item.field(Fields::identifier).value().toString();
+ findUsagesHelper(item, name, result);
+ break;
+ }
+ case DomType::ScriptVariableDeclarationEntry: {
+ const QString name = item.field(Fields::identifier).value().toString();
+ findUsagesHelper(item, name, result);
+ break;
+ }
+ case DomType::EnumDecl:
+ case DomType::EnumItem:
+ case DomType::QmlObject:
+ case DomType::PropertyDefinition:
+ case DomType::Binding:
+ case DomType::MethodInfo: {
+ const QString name = item.field(Fields::name).value().toString();
+ findUsagesHelper(item, name, result);
+ break;
+ }
+ case DomType::QmlComponent: {
+ QString name = item.field(Fields::name).value().toString();
+
+ // get rid of extra qualifiers
+ if (const auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
+ name = name.sliced(dotIndex + 1);
+ findUsagesHelper(item, name, result);
+ break;
+ }
+ default:
+ qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
+ << "was not implemented for QQmlLSUtils::findUsagesOf";
+ return result;
+ }
+
+ std::sort(result.begin(), result.end());
+
+ if (QQmlLSUtilsLog().isDebugEnabled()) {
+ qCDebug(QQmlLSUtilsLog) << "Found following usages:";
+ for (auto r : result) {
+ qCDebug(QQmlLSUtilsLog)
+ << r.filename << " @ " << r.sourceLocation.startLine << ":"
+ << r.sourceLocation.startColumn << " with length " << r.sourceLocation.length;
+ }
+ }
+
+ return result;
+}
+
+static std::optional<QQmlLSUtilsIdentifierType>
+hasMethodOrSignal(const QQmlJSScope::ConstPtr &scope, const QString &name)
+{
+ auto methods = scope->methods(name);
+ if (methods.isEmpty())
+ return {};
+
+ const bool isSignal = methods.front().methodType() == QQmlJSMetaMethodType::Signal;
+ QQmlLSUtilsIdentifierType type = isSignal ? QQmlLSUtilsIdentifierType::SignalIdentifier
+ : QQmlLSUtilsIdentifierType::MethodIdentifier;
+ return type;
+}
+
+/*!
+\internal
+Searches for a method by traversing the parent scopes.
+
+We assume here that it is possible to call methods from parent scope to simplify things, as the
+linting module already warns about calling methods from parent scopes.
+
+Note: in QML, one can only call methods from the current scope, and from the QML file root scope.
+Everything else needs a qualifier.
+*/
+static std::optional<QQmlLSUtilsExpressionType>
+methodFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &name,
+ QQmlLSUtilsResolveOptions options)
+{
+ for (QQmlJSScope::ConstPtr current = referrerScope; current; current = current->parentScope()) {
+ if (auto type = hasMethodOrSignal(current, name)) {
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{ name,
+ findDefiningScopeForMethod(current, name),
+ *type };
+ case ResolveActualTypeForFieldMemberExpression:
+ // QQmlJSScopes were not implemented for methods yet, but JS functions have methods
+ // and properties see
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
+ // for the list of properties/methods of functions. Therefore return a null scope.
+ // see also code below for non-qualified method access
+ return QQmlLSUtilsExpressionType{ name, {}, *type };
+ }
+ }
+
+ if (const auto signalName = QQmlSignalNames::handlerNameToSignalName(name)) {
+ if (auto type = hasMethodOrSignal(current, *signalName)) {
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{
+ name, findDefiningScopeForMethod(current, *signalName),
+ SignalHandlerIdentifier
+ };
+ case ResolveActualTypeForFieldMemberExpression:
+ // Properties and methods of JS methods are not supported yet
+ return QQmlLSUtilsExpressionType{ name, {}, SignalHandlerIdentifier };
+ }
+ }
+ }
+ }
+ return {};
+}
+
+
+/*!
+\internal
+See comment on methodFromReferrerScope: the same applies to properties.
+*/
+static std::optional<QQmlLSUtilsExpressionType>
+propertyFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &propertyName,
+ QQmlLSUtilsResolveOptions options)
+{
+ for (QQmlJSScope::ConstPtr current = referrerScope; current; current = current->parentScope()) {
+ const auto resolved = resolveNameInQmlScope(propertyName, current);
+ if (!resolved)
+ continue;
+
+ if (auto property = current->property(resolved->name); property.isValid()) {
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{
+ propertyName, findDefiningScopeForProperty(current, propertyName),
+ resolved->type
+ };
+ case ResolveActualTypeForFieldMemberExpression:
+ return QQmlLSUtilsExpressionType{ propertyName, property.type(),
+ resolved->type };
+ }
+ }
+ }
+ return {};
+}
+
+/*!
+\internal
+See comment on methodFromReferrerScope: the same applies to property bindings.
+
+If resolver is not null then it is used to resolve the id with which a generalized grouped
+properties starts.
+*/
+static std::optional<QQmlLSUtilsExpressionType>
+propertyBindingFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, const QString &name,
+ QQmlLSUtilsResolveOptions options,
+ QQmlJSTypeResolver *resolverForIds)
+{
+ auto bindings = referrerScope->propertyBindings(name);
+ if (bindings.isEmpty())
+ return {};
+
+ const auto binding = bindings.front();
+
+ if ((binding.bindingType() != QQmlSA::BindingType::AttachedProperty)
+ && (binding.bindingType() != QQmlSA::BindingType::GroupProperty))
+ return {};
+
+ const bool bindingIsAttached = binding.bindingType() == QQmlSA::BindingType::AttachedProperty;
+
+ // Generalized grouped properties, like Bindings or PropertyChanges, for example, have bindings
+ // starting in an id (like `someId.someProperty: ...`).
+ // If `someid` is not a property and is a deferred name, then it should be an id.
+ if (!bindingIsAttached && !referrerScope->hasProperty(name)
+ && referrerScope->isNameDeferred(name)) {
+ if (!resolverForIds)
+ return {};
+
+ QQmlJSRegisterContent fromId = resolverForIds->scopedType(
+ referrerScope, name, QQmlJSRegisterContent::InvalidLookupIndex,
+ AssumeComponentsAreBound);
+ if (fromId.variant() == QQmlJSRegisterContent::ObjectById)
+ return QQmlLSUtilsExpressionType{ name, fromId.type(), QmlObjectIdIdentifier };
+
+ return QQmlLSUtilsExpressionType{ name, {}, QmlObjectIdIdentifier };
+ }
+
+ const auto typeIdentifier =
+ bindingIsAttached ? AttachedTypeIdentifier : GroupedPropertyIdentifier;
+
+ const auto getScope = [&bindingIsAttached, &binding]() -> QQmlJSScope::ConstPtr {
+ if (bindingIsAttached)
+ return binding.attachingType();
+
+ return binding.groupType();
+ };
+
+ switch (options) {
+ case ResolveOwnerType: {
+ return QQmlLSUtilsExpressionType{
+ name,
+ // note: always return the type of the attached type as the owner.
+ // Find usages on "Keys.", for example, should yield all usages of the "Keys"
+ // attached property.
+ bindingIsAttached ? getScope() : findDefiningScopeForProperty(referrerScope, name),
+ typeIdentifier
+ };
+ }
+ case ResolveActualTypeForFieldMemberExpression:
+ return QQmlLSUtilsExpressionType{ name, getScope(), typeIdentifier };
+ }
+ Q_UNREACHABLE_RETURN({});
+}
+
+/*! \internal
+ Finds the scope within the special elements like Connections,
+ PropertyChanges, Bindings or AnchorChanges.
+*/
+static QQmlJSScope::ConstPtr findScopeOfSpecialItems(QQmlJSScope::ConstPtr scope, const DomItem &item)
+{
+ if (!scope)
+ return {};
+
+ const QSet<QString> specialItems = {u"QQmlConnections"_s,
+ u"QQuickPropertyChanges"_s,
+ u"QQmlBind"_s,
+ u"QQuickAnchorChanges"_s};
+
+ const auto special = QQmlJSUtils::searchBaseAndExtensionTypes(
+ scope, [&specialItems](QQmlJSScope::ConstPtr visitedScope) {
+ const auto typeName = visitedScope->internalName();
+ if (specialItems.contains(typeName))
+ return true;
+ return false;
+ });
+
+ if (!special)
+ return {};
+
+ // Perform target name search if there is binding to property "target"
+ QString targetName;
+ if (scope->hasOwnPropertyBindings(u"target"_s)) {
+ // TODO: propagate the whole binding.
+ // We can figure out the meaning of target in more cases.
+
+ DomItem current = item.qmlObject();
+ auto target = current.bindings().key(u"target"_s).index(0);
+ if (target) {
+ targetName = target.field(Fields::value)
+ .field(Fields::scriptElement)
+ .field(Fields::identifier)
+ .value()
+ .toString();
+ }
+ }
+
+ if (!targetName.isEmpty()) {
+ // target: someId
+ auto resolver = item.containingFile().ownerAs<QmlFile>()->typeResolver();
+ if (!resolver)
+ return {};
+
+ // Note: It does not have to be an ID. It can be a property.
+ return resolver->containedType(resolver->scopedType(scope, targetName));
+ } else {
+ if (item.internalKind() == DomType::Binding &&
+ item.field(Fields::bindingType).value().toInteger() == int(BindingType::OnBinding)) {
+ // Binding on sth : {} syntax
+ // Target scope is the current scope
+ return scope;
+ }
+ return scope->parentScope();
+ }
+
+ return {};
+}
+
+static std::optional<QQmlLSUtilsExpressionType>
+resolveFieldMemberExpressionType(const DomItem &item, QQmlLSUtilsResolveOptions options)
+{
+ const QString name = item.field(Fields::identifier).value().toString();
+ DomItem parent = item.directParent();
+ auto owner = QQmlLSUtils::resolveExpressionType(
+ parent.field(Fields::left),
+ QQmlLSUtilsResolveOptions::ResolveActualTypeForFieldMemberExpression);
+ if (!owner)
+ return {};
+
+ if (auto scope = methodFromReferrerScope(owner->semanticScope, name, options))
+ return *scope;
+
+ if (auto scope = propertyBindingFromReferrerScope(owner->semanticScope, name, options, nullptr))
+ return *scope;
+
+ if (auto scope = propertyFromReferrerScope(owner->semanticScope, name, options))
+ return *scope;
+
+ // Ignore enum usages from other files for now.
+ if (owner->type == QmlComponentIdentifier) {
+ // Check if name is a enum value <TypeName>.<EnumValue>
+ // Enumerations should live under the root element scope of the file that defines the enum,
+ // therefore use the DomItem to find the root element of the qml file instead of directly
+ // using owner->semanticScope.
+ const auto scope = item.goToFile(owner->semanticScope->filePath())
+ .rootQmlObject(GoTo::MostLikely)
+ .semanticScope();
+ if (scope->hasEnumerationKey(name)) {
+ return QQmlLSUtilsExpressionType{name, scope, EnumeratorValueIdentifier};
+ }
+ // Or it is a enum name <TypeName>.<EnumName>.<EnumValue>
+ else if (scope->hasEnumeration(name)) {
+ return QQmlLSUtilsExpressionType{name, scope, EnumeratorIdentifier};
+ }
+
+ // check inline components <TypeName>.<InlineComponentName>
+ for (auto it = owner->semanticScope->childScopesBegin(),
+ end = owner->semanticScope->childScopesEnd();
+ it != end; ++it) {
+ if ((*it)->inlineComponentName() == name) {
+ return QQmlLSUtilsExpressionType{ name, *it, QmlComponentIdentifier };
+ }
+ }
+ return {};
+ }
+
+ qCDebug(QQmlLSUtilsLog) << "Could not find identifier expression for" << item.internalKindStr();
+ return owner;
+}
+
+static std::optional<QQmlLSUtilsExpressionType>
+resolveIdentifierExpressionType(const DomItem &item, QQmlLSUtilsResolveOptions options)
+{
+ if (QQmlLSUtils::isFieldMemberAccess(item)) {
+ return resolveFieldMemberExpressionType(item, options);
+ }
+
+ const QString name = item.field(Fields::identifier).value().toString();
+
+ if (DomItem definitionOfItem = findJSIdentifierDefinition(item, name)) {
+ Q_ASSERT_X(!definitionOfItem.semanticScope().isNull()
+ && definitionOfItem.semanticScope()->ownJSIdentifier(name),
+ "QQmlLSUtils::findDefinitionOf",
+ "JS definition does not actually define the JS identifer. "
+ "It should be empty.");
+ auto scope = definitionOfItem.semanticScope();
+ auto jsIdentifier = scope->ownJSIdentifier(name);
+ if (jsIdentifier->scope) {
+ return QQmlLSUtilsExpressionType{ name, jsIdentifier->scope.toStrongRef(),
+ QQmlLSUtilsIdentifierType::JavaScriptIdentifier };
+ } else {
+ return QQmlLSUtilsExpressionType{ name, scope,
+ QQmlLSUtilsIdentifierType::JavaScriptIdentifier };
+ }
+ }
+
+ const auto referrerScope = item.nearestSemanticScope();
+ if (!referrerScope)
+ return {};
+
+ // check if its a method
+ if (auto scope = methodFromReferrerScope(referrerScope, name, options))
+ return scope;
+
+ const auto resolver = item.containingFile().ownerAs<QmlFile>()->typeResolver();
+ if (!resolver)
+ return {};
+
+ // check if its found as a property binding
+ if (auto scope = propertyBindingFromReferrerScope(referrerScope, name, options, resolver.get()))
+ return *scope;
+
+ // check if its an (unqualified) property
+ if (auto scope = propertyFromReferrerScope(referrerScope, name, options))
+ return *scope;
+
+ // Returns the baseType, can't use it with options.
+ if (auto scope = resolver->typeForName(name)) {
+ if (scope->isSingleton())
+ return QQmlLSUtilsExpressionType{ name, scope,
+ QQmlLSUtilsIdentifierType::SingletonIdentifier };
+
+ if (auto attachedScope = scope->attachedType()) {
+ return QQmlLSUtilsExpressionType{
+ name, attachedScope, QQmlLSUtilsIdentifierType::AttachedTypeIdentifier
+ };
+ }
+
+ // its a (inline) component!
+ return QQmlLSUtilsExpressionType{ name, scope, QmlComponentIdentifier };
+ }
+
+ // check if its an id
+ QQmlJSRegisterContent fromId =
+ resolver->scopedType(referrerScope, name, QQmlJSRegisterContent::InvalidLookupIndex,
+ AssumeComponentsAreBound);
+ if (fromId.variant() == QQmlJSRegisterContent::ObjectById)
+ return QQmlLSUtilsExpressionType{ name, fromId.type(), QmlObjectIdIdentifier };
+
+ const QQmlJSScope::ConstPtr jsGlobal = resolver->jsGlobalObject();
+ // check if its a JS global method
+ if (auto scope = methodFromReferrerScope(jsGlobal, name, options))
+ return scope;
+
+ // check if its an JS global property
+ if (auto scope = propertyFromReferrerScope(jsGlobal, name, options))
+ return *scope;
+
+ return {};
+}
+
+static std::optional<QQmlLSUtilsExpressionType>
+resolveSignalOrPropertyExpressionType(const QString &name, const QQmlJSScope::ConstPtr &scope,
+ QQmlLSUtilsResolveOptions options)
+{
+ auto signalOrProperty = resolveNameInQmlScope(name, scope);
+ if (!signalOrProperty)
+ return {};
+
+ switch (signalOrProperty->type) {
+ case PropertyIdentifier:
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{ name, findDefiningScopeForProperty(scope, name),
+ signalOrProperty->type };
+ case ResolveActualTypeForFieldMemberExpression:
+ return QQmlLSUtilsExpressionType{ name, scope->property(name).type(),
+ signalOrProperty->type };
+ }
+ Q_UNREACHABLE_RETURN({});
+ case PropertyChangedHandlerIdentifier:
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{
+ name, findDefiningScopeForProperty(scope, signalOrProperty->name),
+ signalOrProperty->type
+ };
+ case ResolveActualTypeForFieldMemberExpression:
+ // Properties and methods are not implemented on methods.
+ Q_UNREACHABLE_RETURN({});
+ }
+ Q_UNREACHABLE_RETURN({});
+ case SignalHandlerIdentifier:
+ case PropertyChangedSignalIdentifier:
+ case SignalIdentifier:
+ case MethodIdentifier:
+ switch (options) {
+ case ResolveOwnerType: {
+ return QQmlLSUtilsExpressionType{ name, findDefiningScopeForMethod(scope, name),
+ signalOrProperty->type };
+ }
+ case ResolveActualTypeForFieldMemberExpression:
+ // Properties and methods are not implemented on methods.
+ Q_UNREACHABLE_RETURN({});
+ }
+ Q_UNREACHABLE_RETURN({});
+ default:
+ Q_UNREACHABLE_RETURN({});
+ }
+}
+
+/*!
+ \internal
+ Resolves the type of the given DomItem, when possible (e.g., when there are enough type
+ annotations).
+*/
+std::optional<QQmlLSUtilsExpressionType>
+QQmlLSUtils::resolveExpressionType(const QQmlJS::Dom::DomItem &item,
+ QQmlLSUtilsResolveOptions options)
+{
+ switch (item.internalKind()) {
+ case DomType::ScriptIdentifierExpression: {
+ return resolveIdentifierExpressionType(item, options);
+ }
+ case DomType::PropertyDefinition: {
+ auto propertyDefinition = item.as<PropertyDefinition>();
+ if (propertyDefinition && propertyDefinition->semanticScope()) {
+ const auto &scope = propertyDefinition->semanticScope();
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{ propertyDefinition->name, scope,
+ PropertyIdentifier };
+ case ResolveActualTypeForFieldMemberExpression:
+ // There should not be any PropertyDefinition inside a FieldMemberExpression.
+ Q_UNREACHABLE_RETURN({});
+ }
+ Q_UNREACHABLE_RETURN({});
+ }
+ return {};
+ }
+ case DomType::Binding: {
+ auto binding = item.as<Binding>();
+ if (binding) {
+ std::optional<QQmlJSScope::ConstPtr> owner = item.qmlObject().semanticScope();
+ if (!owner)
+ return {};
+ const QString name = binding->name();
+
+ if (name == u"id")
+ return QQmlLSUtilsExpressionType{ name, owner.value(), QmlObjectIdIdentifier };
+
+ if (QQmlJSScope::ConstPtr targetScope = findScopeOfSpecialItems(owner.value(), item)) {
+ const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
+ if (!signalOrProperty)
+ return {};
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{
+ name, findDefiningScopeForBinding(targetScope, signalOrProperty->name),
+ signalOrProperty->type
+ };
+ case ResolveActualTypeForFieldMemberExpression:
+ // Bindings can't be inside of FieldMemberExpressions.
+ Q_UNREACHABLE_RETURN({});
+ }
+ }
+ if (auto result = resolveSignalOrPropertyExpressionType(name, owner.value(), options)) {
+ return result;
+ }
+ qDebug(QQmlLSUtilsLog) << "QQmlLSUtils::resolveExpressionType() could not resolve the"
+ "type of a Binding.";
+ }
+
+ return {};
+ }
+ case DomType::QmlObject: {
+ auto object = item.as<QmlObject>();
+ if (!object)
+ return {};
+ if (auto scope = object->semanticScope()) {
+ const auto name = item.name();
+ const bool isComponent = name.front().isUpper();
+ if (isComponent)
+ scope = scope->baseType();
+ const QQmlLSUtilsIdentifierType type =
+ isComponent ? QmlComponentIdentifier : GroupedPropertyIdentifier;
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{ name, scope, type };
+ case ResolveActualTypeForFieldMemberExpression:
+ return QQmlLSUtilsExpressionType{ name, scope, type};
+ }
+ }
+ return {};
+ }
+ case DomType::QmlComponent: {
+ auto component = item.as<QmlComponent>();
+ if (!component)
+ return {};
+ const auto scope = component->semanticScope();
+ if (!scope)
+ return {};
+
+ QString name = item.name();
+ if (auto dotIndex = name.indexOf(u'.'); dotIndex != -1)
+ name = name.sliced(dotIndex + 1);
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{ name, scope, QmlComponentIdentifier };
+ case ResolveActualTypeForFieldMemberExpression:
+ return QQmlLSUtilsExpressionType{ name, scope, QmlComponentIdentifier };
+ }
+ Q_UNREACHABLE_RETURN({});
+ }
+ case DomType::MethodInfo: {
+ auto object = item.as<MethodInfo>();
+ if (object && object->semanticScope()) {
+ std::optional<QQmlJSScope::ConstPtr> scope = object->semanticScope();
+ if (!scope)
+ return {};
+
+ if (QQmlJSScope::ConstPtr targetScope =
+ findScopeOfSpecialItems(scope.value()->parentScope(), item)) {
+ const auto signalOrProperty = resolveNameInQmlScope(object->name, targetScope);
+ if (!signalOrProperty)
+ return {};
+
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{ object->name,
+ findDefiningScopeForMethod(
+ targetScope, signalOrProperty->name),
+ signalOrProperty->type };
+ case ResolveActualTypeForFieldMemberExpression:
+ // not supported for methods
+ return {};
+ }
+ }
+
+ // in case scope is the semantic scope for the function bodies: grab the owner's scope
+ // this happens for all methods but not for signals (they do not have a body)
+ if (scope.value()->scopeType() == QQmlJSScope::ScopeType::JSFunctionScope)
+ scope = scope.value()->parentScope();
+
+ if (auto result = resolveSignalOrPropertyExpressionType(object->name, scope.value(),
+ options)) {
+ return result;
+ }
+ qDebug(QQmlLSUtilsLog) << "QQmlLSUtils::resolveExpressionType() could not resolve the"
+ "type of a MethodInfo.";
+ }
+
+ return {};
+ }
+ case DomType::ScriptBinaryExpression: {
+ if (isFieldMemberExpression(item)) {
+ return resolveExpressionType(item.field(Fields::right), options);
+ }
+ return {};
+ }
+ case DomType::ScriptLiteral: {
+ /* special case
+ Binding { target: someId; property: "someProperty"}
+ */
+ const auto scope = item.qmlObject().semanticScope();
+ const auto name = item.field(Fields::value).value().toString();
+ if (QQmlJSScope::ConstPtr targetScope = findScopeOfSpecialItems(scope, item)) {
+ const auto signalOrProperty = resolveNameInQmlScope(name, targetScope);
+ if (!signalOrProperty)
+ return {};
+ switch (options) {
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{
+ name, findDefiningScopeForProperty(targetScope, signalOrProperty->name),
+ signalOrProperty->type
+ };
+ case ResolveActualTypeForFieldMemberExpression:
+ // ScriptLiteral's can't be inside of FieldMemberExpression's, especially when they
+ // are inside a special item.
+ Q_UNREACHABLE_RETURN({});
+ }
+ }
+ return {};
+ }
+ case DomType::EnumItem: {
+ const QString enumValue = item.field(Fields::name).value().toString();
+ QQmlJSScope::ConstPtr referrerScope = item.rootQmlObject(GoTo::MostLikely).semanticScope();
+ if (!referrerScope->hasEnumerationKey(enumValue))
+ return {};
+ switch (options) {
+ // special case: use the owner's scope here, as enums do not have their own
+ // QQmlJSScope.
+ case ResolveActualTypeForFieldMemberExpression:
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{
+ enumValue, findDefiningScopeForEnumerationKey(referrerScope, enumValue),
+ EnumeratorValueIdentifier
+ };
+ }
+ Q_UNREACHABLE_RETURN({});
+ }
+ case DomType::EnumDecl: {
+ const QString enumName = item.field(Fields::name).value().toString();
+ QQmlJSScope::ConstPtr referrerScope = item.rootQmlObject(GoTo::MostLikely).semanticScope();
+ if (!referrerScope->hasEnumeration(enumName))
+ return {};
+ switch (options) {
+ // special case: use the owner's scope here, as enums do not have their own QQmlJSScope.
+ case ResolveActualTypeForFieldMemberExpression:
+ case ResolveOwnerType:
+ return QQmlLSUtilsExpressionType{
+ enumName, findDefiningScopeForEnumeration(referrerScope, enumName),
+ EnumeratorIdentifier
+ };
+ }
+
+ Q_UNREACHABLE_RETURN({});
+ }
+ default: {
+ qCDebug(QQmlLSUtilsLog) << "Type" << item.internalKindStr()
+ << "is unimplemented in QQmlLSUtils::resolveExpressionType";
+ return {};
+ }
+ }
+ Q_UNREACHABLE();
+}
+
+DomItem QQmlLSUtils::sourceLocationToDomItem(const DomItem &file,
+ const QQmlJS::SourceLocation &location)
+{
+ // QQmlJS::SourceLocation starts counting at 1 but the utils and the LSP start at 0.
+ auto items = QQmlLSUtils::itemsFromTextLocation(file, location.startLine - 1,
+ location.startColumn - 1);
+ switch (items.size()) {
+ case 0:
+ return {};
+ case 1:
+ return items.front().domItem;
+ case 2: {
+ // special case: because location points to the beginning of the type definition,
+ // itemsFromTextLocation might also return the type on its left, in case it is directly
+ // adjacent to it. In this case always take the right (=with the higher column-number)
+ // item.
+ auto &first = items.front();
+ auto &second = items.back();
+ Q_ASSERT_X(first.fileLocation->info().fullRegion.startLine
+ == second.fileLocation->info().fullRegion.startLine,
+ "QQmlLSUtils::findTypeDefinitionOf(DomItem)",
+ "QQmlLSUtils::itemsFromTextLocation returned non-adjacent items.");
+ if (first.fileLocation->info().fullRegion.startColumn
+ > second.fileLocation->info().fullRegion.startColumn)
+ return first.domItem;
+ else
+ return second.domItem;
+ break;
+ }
+ default:
+ qDebug() << "Found multiple candidates for type of scriptidentifierexpression";
+ break;
+ }
+ return {};
+}
+
+static std::optional<QQmlLSUtilsLocation>
+findMethodDefinitionOf(const DomItem &file, QQmlJS::SourceLocation location, const QString &name)
+{
+ DomItem owner = QQmlLSUtils::sourceLocationToDomItem(file, location).qmlObject();
+ DomItem method = owner.field(Fields::methods).key(name).index(0);
+ auto fileLocation = FileLocations::treeOf(method);
+ if (!fileLocation)
+ return {};
+
+ auto regions = fileLocation->info().regions;
+
+ if (auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
+ QQmlLSUtilsLocation result;
+ result.sourceLocation = *it;
+ result.filename = method.canonicalFilePath();
+ return result;
+ }
+
+ return {};
+}
+
+static std::optional<QQmlLSUtilsLocation>
+findPropertyDefinitionOf(const DomItem &file, QQmlJS::SourceLocation propertyDefinitionLocation,
+ const QString &name)
+{
+ DomItem propertyOwner =
+ QQmlLSUtils::sourceLocationToDomItem(file, propertyDefinitionLocation).qmlObject();
+ DomItem propertyDefinition = propertyOwner.field(Fields::propertyDefs).key(name).index(0);
+ auto fileLocation = FileLocations::treeOf(propertyDefinition);
+ if (!fileLocation)
+ return {};
+
+ auto regions = fileLocation->info().regions;
+
+ if (auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
+ QQmlLSUtilsLocation result;
+ result.sourceLocation = *it;
+ result.filename = propertyDefinition.canonicalFilePath();
+ return result;
+ }
+
+ return {};
+}
+
+std::optional<QQmlLSUtilsLocation> QQmlLSUtils::findDefinitionOf(const DomItem &item)
+{
+ auto resolvedExpression =
+ resolveExpressionType(item, QQmlLSUtilsResolveOptions::ResolveOwnerType);
+
+ if (!resolvedExpression || !resolvedExpression->name || !resolvedExpression->semanticScope) {
+ qCDebug(QQmlLSUtilsLog) << "QQmlLSUtils::findDefinitionOf: Type could not be resolved.";
+ return {};
+ }
+
+ switch (resolvedExpression->type) {
+ case JavaScriptIdentifier: {
+ const QQmlJS::SourceLocation location =
+ resolvedExpression->semanticScope->ownJSIdentifier(*resolvedExpression->name)
+ .value()
+ .location;
+
+ return QQmlLSUtilsLocation{ resolvedExpression->semanticScope->filePath(), location };
+ }
+
+ case PropertyIdentifier: {
+ const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
+ const QQmlJS::SourceLocation ownerLocation =
+ resolvedExpression->semanticScope->sourceLocation();
+ return findPropertyDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name);
+ }
+ case PropertyChangedSignalIdentifier:
+ case PropertyChangedHandlerIdentifier:
+ case SignalIdentifier:
+ case SignalHandlerIdentifier:
+ case MethodIdentifier: {
+ const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
+ const QQmlJS::SourceLocation ownerLocation =
+ resolvedExpression->semanticScope->sourceLocation();
+ return findMethodDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name);
+ }
+ case QmlObjectIdIdentifier: {
+ DomItem qmlObject = QQmlLSUtils::sourceLocationToDomItem(
+ item.containingFile(), resolvedExpression->semanticScope->sourceLocation());
+ // in the Dom, the id is saved in a QMultiHash inside the Component of an QmlObject.
+ const DomItem domId = qmlObject.component()
+ .field(Fields::ids)
+ .key(*resolvedExpression->name)
+ .index(0)
+ .field(Fields::value);
+ if (!domId) {
+ qCDebug(QQmlLSUtilsLog)
+ << "QmlComponent in Dom structure has no id, was it misconstructed?";
+ return {};
+ }
+
+ QQmlLSUtilsLocation result;
+ result.sourceLocation = FileLocations::treeOf(domId)->info().fullRegion;
+ result.filename = domId.canonicalFilePath();
+ return result;
+ }
+ case QmlComponentIdentifier: {
+ QQmlLSUtilsLocation result;
+ result.sourceLocation = resolvedExpression->semanticScope->sourceLocation();
+ result.filename = resolvedExpression->semanticScope->filePath();
+ return result;
+ }
+ case SingletonIdentifier:
+ case EnumeratorIdentifier:
+ case EnumeratorValueIdentifier:
+ case AttachedTypeIdentifier:
+ case GroupedPropertyIdentifier:
+ qCDebug(QQmlLSUtilsLog) << "QQmlLSUtils::findDefinitionOf was not implemented for type"
+ << resolvedExpression->type;
+ return {};
+ }
+ Q_UNREACHABLE_RETURN({});
+}
+
+static QQmlJSScope::ConstPtr propertyOwnerFrom(const QQmlJSScope::ConstPtr &type,
+ const QString &name)
+{
+ Q_ASSERT(!name.isEmpty());
+ Q_ASSERT(type);
+
+ QQmlJSScope::ConstPtr typeWithDefinition = type;
+ while (typeWithDefinition && !typeWithDefinition->hasOwnProperty(name))
+ typeWithDefinition = typeWithDefinition->baseType();
+
+ if (!typeWithDefinition) {
+ qCDebug(QQmlLSUtilsLog)
+ << "QQmlLSUtils::checkNameForRename cannot find property definition,"
+ " ignoring.";
+ }
+
+ return typeWithDefinition;
+}
+
+static QQmlJSScope::ConstPtr methodOwnerFrom(const QQmlJSScope::ConstPtr &type,
+ const QString &name)
+{
+ Q_ASSERT(!name.isEmpty());
+ Q_ASSERT(type);
+
+ QQmlJSScope::ConstPtr typeWithDefinition = type;
+ while (typeWithDefinition && !typeWithDefinition->hasOwnMethod(name))
+ typeWithDefinition = typeWithDefinition->baseType();
+
+ if (!typeWithDefinition) {
+ qCDebug(QQmlLSUtilsLog)
+ << "QQmlLSUtils::checkNameForRename cannot find method definition,"
+ " ignoring.";
+ }
+
+ return typeWithDefinition;
+}
+
+static QQmlJSScope::ConstPtr
+expressionTypeWithDefinition(const QQmlLSUtilsExpressionType &ownerType)
+{
+ switch (ownerType.type) {
+ case PropertyIdentifier:
+ return propertyOwnerFrom(ownerType.semanticScope, *ownerType.name);
+ case PropertyChangedHandlerIdentifier: {
+ const auto propertyName =
+ QQmlSignalNames::changedHandlerNameToPropertyName(*ownerType.name);
+ return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
+ break;
+ }
+ case PropertyChangedSignalIdentifier: {
+ const auto propertyName = QQmlSignalNames::changedSignalNameToPropertyName(*ownerType.name);
+ return propertyOwnerFrom(ownerType.semanticScope, *propertyName);
+ }
+ case MethodIdentifier:
+ case SignalIdentifier:
+ return methodOwnerFrom(ownerType.semanticScope, *ownerType.name);
+ case SignalHandlerIdentifier: {
+ const auto signalName = QQmlSignalNames::handlerNameToSignalName(*ownerType.name);
+ return methodOwnerFrom(ownerType.semanticScope, *signalName);
+ }
+ case JavaScriptIdentifier:
+ case QmlObjectIdIdentifier:
+ case SingletonIdentifier:
+ case EnumeratorIdentifier:
+ case EnumeratorValueIdentifier:
+ case AttachedTypeIdentifier:
+ case GroupedPropertyIdentifier:
+ case QmlComponentIdentifier:
+ return ownerType.semanticScope;
+ }
+ return {};
+}
+
+std::optional<QQmlLSUtilsErrorMessage> QQmlLSUtils::checkNameForRename(
+ const DomItem &item, const QString &dirtyNewName,
+ const std::optional<QQmlLSUtilsExpressionType> &ownerType)
+{
+ if (!ownerType) {
+ if (const auto resolved = QQmlLSUtils::resolveExpressionType(item, ResolveOwnerType))
+ return checkNameForRename(item, dirtyNewName, resolved);
+ }
+
+ // general checks for ECMAscript identifiers
+ if (!isValidEcmaScriptIdentifier(dirtyNewName))
+ return QQmlLSUtilsErrorMessage{ 0, u"Invalid EcmaScript identifier!"_s };
+
+ const auto userSemanticScope = item.nearestSemanticScope();
+
+ if (!ownerType || !userSemanticScope) {
+ return QQmlLSUtilsErrorMessage{ 0, u"Requested item cannot be renamed"_s };
+ }
+
+ // type specific checks
+ switch (ownerType->type) {
+ case PropertyChangedSignalIdentifier: {
+ if (!QQmlSignalNames::isChangedSignalName(dirtyNewName)) {
+ return QQmlLSUtilsErrorMessage{ 0, u"Invalid name for a property changed signal."_s };
+ }
+ break;
+ }
+ case PropertyChangedHandlerIdentifier: {
+ if (!QQmlSignalNames::isChangedHandlerName(dirtyNewName)) {
+ return QQmlLSUtilsErrorMessage{
+ 0, u"Invalid name for a property changed handler identifier."_s
+ };
+ }
+ break;
+ }
+ case SignalHandlerIdentifier: {
+ if (!QQmlSignalNames::isHandlerName(dirtyNewName)) {
+ return QQmlLSUtilsErrorMessage{ 0, u"Invalid name for a signal handler identifier."_s };
+ }
+ break;
+ }
+ // TODO: any other specificities?
+ case QmlObjectIdIdentifier:
+ if (dirtyNewName.front().isLetter() && !dirtyNewName.front().isLower()) {
+ return QQmlLSUtilsErrorMessage{
+ 0, u"Object id names cannot start with an upper case letter."_s
+ };
+ }
+ break;
+ case JavaScriptIdentifier:
+ case PropertyIdentifier:
+ case SignalIdentifier:
+ case MethodIdentifier:
+ default:
+ break;
+ };
+
+ auto typeWithDefinition = expressionTypeWithDefinition(*ownerType);
+
+ if (!typeWithDefinition) {
+ return QQmlLSUtilsErrorMessage{
+ 0,
+ u"Renaming has not been implemented for the requested item."_s,
+ };
+ }
+
+ // is it not defined in QML?
+ if (!typeWithDefinition->isComposite()) {
+ return QQmlLSUtilsErrorMessage{ 0, u"Cannot rename items defined in non-QML files."_s };
+ }
+
+ // is it defined in the current module?
+ const QString moduleOfDefinition = ownerType->semanticScope->moduleName();
+ const QString moduleOfCurrentItem = userSemanticScope->moduleName();
+ if (moduleOfDefinition != moduleOfCurrentItem) {
+ return QQmlLSUtilsErrorMessage{
+ 0,
+ u"Cannot rename items defined in the %1 module fromits usage in the %2 module."_s
+ .arg(moduleOfDefinition, moduleOfCurrentItem),
+ };
+ }
+
+ return {};
+}
+
+static std::optional<QString> oldNameFrom(const DomItem &item)
+{
+ switch (item.internalKind()) {
+ case DomType::ScriptIdentifierExpression: {
+ return item.field(Fields::identifier).value().toString();
+ }
+ case DomType::ScriptVariableDeclarationEntry: {
+ return item.field(Fields::identifier).value().toString();
+ }
+ case DomType::PropertyDefinition:
+ case DomType::Binding:
+ case DomType::MethodInfo: {
+ return item.field(Fields::name).value().toString();
+ }
+ default:
+ qCDebug(QQmlLSUtilsLog) << item.internalKindStr()
+ << "was not implemented for QQmlLSUtils::renameUsagesOf";
+ return std::nullopt;
+ }
+ Q_UNREACHABLE_RETURN(std::nullopt);
+}
+
+static std::optional<QString> newNameFrom(const QString &dirtyNewName,
+ QQmlLSUtilsIdentifierType alternative)
+{
+ // When renaming signal/property changed handlers and property changed signals:
+ // Get the actual corresponding signal name (for signal handlers) or property name (for
+ // property changed signal + handlers) that will be used for the renaming.
+ switch (alternative) {
+ case SignalHandlerIdentifier: {
+ return QQmlSignalNames::handlerNameToSignalName(dirtyNewName);
+ }
+ case PropertyChangedHandlerIdentifier: {
+ return QQmlSignalNames::changedHandlerNameToPropertyName(dirtyNewName);
+ }
+ case PropertyChangedSignalIdentifier: {
+ return QQmlSignalNames::changedSignalNameToPropertyName(dirtyNewName);
+ }
+ case SignalIdentifier:
+ case PropertyIdentifier:
+ default:
+ return std::nullopt;
+ }
+ Q_UNREACHABLE_RETURN(std::nullopt);
+}
+
+/*!
+\internal
+\brief Rename the appearance of item to newName.
+
+Special cases:
+\list
+ \li Renaming a property changed signal or property changed handler does the same as renaming
+ the underlying property, except that newName gets
+ \list
+ \li its "on"-prefix and "Changed"-suffix chopped of if item is a property changed handlers
+ \li its "Changed"-suffix chopped of if item is a property changed signals
+ \endlist
+ \li Renaming a signal handler does the same as renaming a signal, but the "on"-prefix in newName
+ is chopped of.
+
+ All of the chopping operations are done using the static helpers from QQmlSignalNames.
+\endlist
+*/
+QList<QQmlLSUtilsEdit> QQmlLSUtils::renameUsagesOf(
+ const DomItem &item, const QString &dirtyNewName,
+ const std::optional<QQmlLSUtilsExpressionType> &targetType)
+{
+ QList<QQmlLSUtilsEdit> results;
+ const QList<QQmlLSUtilsLocation> locations = findUsagesOf(item);
+ if (locations.isEmpty())
+ return results;
+
+ auto oldName = oldNameFrom(item);
+ if (!oldName)
+ return results;
+
+ QQmlJSScope::ConstPtr semanticScope;
+ if (targetType) {
+ semanticScope = targetType->semanticScope;
+ } else if (const auto resolved = QQmlLSUtils::resolveExpressionType(
+ item, QQmlLSUtilsResolveOptions::ResolveOwnerType)) {
+ semanticScope = resolved->semanticScope;
+ } else {
+ return results;
+ }
+
+ QString newName;
+ if (const auto resolved = resolveNameInQmlScope(*oldName, semanticScope)) {
+ newName = newNameFrom(dirtyNewName, resolved->type).value_or(dirtyNewName);
+ oldName = resolved->name;
+ } else {
+ newName = dirtyNewName;
+ }
+
+ const qsizetype oldNameLength = oldName->length();
+ const qsizetype oldHandlerNameLength =
+ QQmlSignalNames::signalNameToHandlerName(*oldName).length();
+ const qsizetype oldChangedSignalNameLength =
+ QQmlSignalNames::propertyNameToChangedSignalName(*oldName).length();
+ const qsizetype oldChangedHandlerNameLength =
+ QQmlSignalNames::propertyNameToChangedHandlerName(*oldName).length();
+
+ const QString newHandlerName = QQmlSignalNames::signalNameToHandlerName(newName);
+ const QString newChangedSignalName = QQmlSignalNames::propertyNameToChangedSignalName(newName);
+ const QString newChangedHandlerName =
+ QQmlSignalNames::propertyNameToChangedHandlerName(newName);
+
+ // set the new name at the found usages, but add "on"-prefix and "Changed"-suffix if needed
+ for (const auto &location : locations) {
+ const qsizetype currentLength = location.sourceLocation.length;
+ QQmlLSUtilsEdit edit;
+ edit.location = location;
+ if (oldNameLength == currentLength) {
+ // normal case, nothing to do
+ edit.replacement = newName;
+
+ } else if (oldHandlerNameLength == currentLength) {
+ // signal handler location
+ edit.replacement = newHandlerName;
+
+ } else if (oldChangedSignalNameLength == currentLength) {
+ // property changed signal location
+ edit.replacement = newChangedSignalName;
+
+ } else if (oldChangedHandlerNameLength == currentLength) {
+ // property changed handler location
+ edit.replacement = newChangedHandlerName;
+
+ } else {
+ qCDebug(QQmlLSUtilsLog) << "Found usage with wrong identifier length, ignoring...";
+ continue;
+ }
+ results.append(edit);
+ }
+
+ return results;
+}
+
+QQmlLSUtilsLocation QQmlLSUtilsLocation::from(const QString &fileName, const QString &code,
+ quint32 startLine, quint32 startCharacter,
+ quint32 length)
+{
+ quint32 offset = QQmlLSUtils::textOffsetFrom(code, startLine - 1, startCharacter - 1);
+
+ QQmlLSUtilsLocation location{
+ fileName, QQmlJS::SourceLocation{ offset, length, startLine, startCharacter }
+ };
+ return location;
+}
+
+QQmlLSUtilsEdit QQmlLSUtilsEdit::from(const QString &fileName, const QString &code,
+ quint32 startLine, quint32 startCharacter, quint32 length,
+ const QString &newName)
+{
+ QQmlLSUtilsEdit rename;
+ rename.location = QQmlLSUtilsLocation::from(fileName, code, startLine, startCharacter, length);
+ rename.replacement = newName;
+ return rename;
+}
+
+bool QQmlLSUtils::isValidEcmaScriptIdentifier(QStringView identifier)
+{
+ QQmlJS::Lexer lexer(nullptr);
+ lexer.setCode(identifier.toString(), 0);
+ const int token = lexer.lex();
+ if (token != static_cast<int>(QQmlJS::Lexer::T_IDENTIFIER))
+ return false;
+ // make sure there is nothing following the lexed identifier
+ const int eofToken = lexer.lex();
+ return eofToken == static_cast<int>(QQmlJS::Lexer::EOF_SYMBOL);
+}
+
+
+/*!
+\internal
+Returns the name of the cmake program along with the arguments needed to build the
+qmltyperegistration. This command generates the .qmltypes, qmldir and .qrc files required for qmlls
+to provide correct information on C++ defined QML elements.
+
+We assume here that CMake is available in the path. This should be the case for linux and macOS by
+default.
+For windows, having CMake in the path is not too unrealistic, for example,
+https://doc.qt.io/qt-6/windows-building.html#step-2-install-build-requirements claims that you need
+to have CMake in your path to build Qt. So a developer machine running qmlls has a high chance of
+having CMake in their path, if CMake is installed and used.
+*/
+QPair<QString, QStringList> QQmlLSUtils::cmakeBuildCommand(const QString &path)
+{
+ const QPair<QString, QStringList> result{
+ u"cmake"_s, { u"--build"_s, path, u"-t"_s, u"all_qmltyperegistrations"_s }
+ };
+ return result;
+}
+
+
+QByteArray QQmlLSUtils::getDocumentationFromLocation(const DomItem &file, const QQmlLSUtilsTextPosition &position)
+{
+ QByteArray result;
+ const auto [line, character] = position;
+ const auto itemLocation = itemsFromTextLocation(file, line, character);
+
+ // TODO:
+ // Process found item's internalKind and fetch its documentation.
+ Q_UNUSED(itemLocation);
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmllsutils_p.h b/src/qmlls/qqmllsutils_p.h
new file mode 100644
index 0000000000..0ba58f8294
--- /dev/null
+++ b/src/qmlls/qqmllsutils_p.h
@@ -0,0 +1,178 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QLANGUAGESERVERUTILS_P_H
+#define QLANGUAGESERVERUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <algorithm>
+#include <optional>
+#include <tuple>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QQmlLSUtilsLog);
+
+struct QQmlLSUtilsItemLocation
+{
+ QQmlJS::Dom::DomItem domItem;
+ QQmlJS::Dom::FileLocations::Tree fileLocation;
+};
+
+struct QQmlLSUtilsTextPosition
+{
+ int line;
+ int character;
+};
+
+enum QQmlLSUtilsIdentifierType : char {
+ JavaScriptIdentifier,
+ PropertyIdentifier,
+ PropertyChangedSignalIdentifier,
+ PropertyChangedHandlerIdentifier,
+ SignalIdentifier,
+ SignalHandlerIdentifier,
+ MethodIdentifier,
+ QmlObjectIdIdentifier,
+ SingletonIdentifier,
+ EnumeratorIdentifier,
+ EnumeratorValueIdentifier,
+ AttachedTypeIdentifier,
+ GroupedPropertyIdentifier,
+ QmlComponentIdentifier,
+};
+
+struct QQmlLSUtilsErrorMessage
+{
+ int code;
+ QString message;
+};
+
+struct QQmlLSUtilsExpressionType
+{
+ std::optional<QString> name;
+ QQmlJSScope::ConstPtr semanticScope;
+ QQmlLSUtilsIdentifierType type;
+};
+
+struct QQmlLSUtilsLocation
+{
+ QString filename;
+ QQmlJS::SourceLocation sourceLocation;
+
+ static QQmlLSUtilsLocation from(const QString &fileName, const QString &code, quint32 startLine,
+ quint32 startCharacter, quint32 length);
+
+ friend bool operator<(const QQmlLSUtilsLocation &a, const QQmlLSUtilsLocation &b)
+ {
+ return std::make_tuple(a.filename, a.sourceLocation.begin(), a.sourceLocation.end())
+ < std::make_tuple(b.filename, b.sourceLocation.begin(), b.sourceLocation.end());
+ }
+ friend bool operator==(const QQmlLSUtilsLocation &a, const QQmlLSUtilsLocation &b)
+ {
+ return std::make_tuple(a.filename, a.sourceLocation.begin(), a.sourceLocation.end())
+ == std::make_tuple(b.filename, b.sourceLocation.begin(), b.sourceLocation.end());
+ }
+};
+
+struct QQmlLSUtilsEdit
+{
+ QQmlLSUtilsLocation location;
+ QString replacement;
+
+ static QQmlLSUtilsEdit from(const QString &fileName, const QString &code, quint32 startLine,
+ quint32 startCharacter, quint32 length, const QString &newName);
+
+ friend bool operator<(const QQmlLSUtilsEdit &a, const QQmlLSUtilsEdit &b)
+ {
+ return std::make_tuple(a.location, a.replacement)
+ < std::make_tuple(b.location, b.replacement);
+ }
+ friend bool operator==(const QQmlLSUtilsEdit &a, const QQmlLSUtilsEdit &b)
+ {
+ return std::make_tuple(a.location, a.replacement)
+ == std::make_tuple(b.location, b.replacement);
+ }
+};
+
+/*!
+ \internal
+ Choose whether to resolve the owner type or the entire type (the latter is only required to
+ resolve the types of qualified names and property accesses).
+
+ For properties, methods, enums and co:
+ * ResolveOwnerType returns the base type of the owner that owns the property, method, enum
+ and co. For example, resolving "x" in "myRectangle.x" will return the Item as the owner, as
+ Item is the base type of Rectangle that defines the "x" property.
+ * ResolveActualTypeForFieldMemberExpression is used to resolve field member expressions, and
+ might lose some information about the owner. For example, resolving "x" in "myRectangle.x"
+ will return the JS type for float that was used to define the "x" property.
+ */
+enum QQmlLSUtilsResolveOptions {
+ ResolveOwnerType,
+ ResolveActualTypeForFieldMemberExpression,
+};
+
+enum class ImportCompletionType { None, Module, Version };
+
+using DomItem = QQmlJS::Dom::DomItem;
+
+class QQmlLSUtils
+{
+public:
+ static qsizetype textOffsetFrom(const QString &code, int row, int character);
+ static QQmlLSUtilsTextPosition textRowAndColumnFrom(const QString &code, qsizetype offset);
+ static QList<QQmlLSUtilsItemLocation> itemsFromTextLocation(const DomItem &file,
+ int line, int character);
+ static DomItem sourceLocationToDomItem(const DomItem &file,
+ const QQmlJS::SourceLocation &location);
+ static QByteArray lspUriToQmlUrl(const QByteArray &uri);
+ static QByteArray qmlUrlToLspUri(const QByteArray &url);
+ static QLspSpecification::Range qmlLocationToLspLocation(const QString &code,
+ QQmlJS::SourceLocation qmlLocation);
+ static DomItem baseObject(const DomItem &qmlObject);
+ static std::optional<QQmlLSUtilsLocation>
+ findTypeDefinitionOf(const DomItem &item);
+ static std::optional<QQmlLSUtilsLocation> findDefinitionOf(const DomItem &item);
+ static QList<QQmlLSUtilsLocation> findUsagesOf(const DomItem &item);
+
+ static std::optional<QQmlLSUtilsErrorMessage> checkNameForRename(
+ const DomItem &item, const QString &newName,
+ const std::optional<QQmlLSUtilsExpressionType> &targetType = std::nullopt);
+ static QList<QQmlLSUtilsEdit> renameUsagesOf(
+ const DomItem &item, const QString &newName,
+ const std::optional<QQmlLSUtilsExpressionType> &targetType = std::nullopt);
+
+ static std::optional<QQmlLSUtilsExpressionType> resolveExpressionType(
+ const DomItem &item, QQmlLSUtilsResolveOptions);
+ static bool isValidEcmaScriptIdentifier(QStringView view);
+
+ static QPair<QString, QStringList> cmakeBuildCommand(const QString &path);
+
+ // Documentation Hints
+ static QByteArray getDocumentationFromLocation(const DomItem &file, const QQmlLSUtilsTextPosition &position);
+
+ static bool isFieldMemberExpression(const DomItem &item);
+ static bool isFieldMemberAccess(const DomItem &item);
+ static QStringList fieldMemberExpressionBits(const DomItem &item,
+ const DomItem &stopAtChild = {});
+
+ static QString qualifiersFrom(const DomItem &el);
+};
+QT_END_NAMESPACE
+
+#endif // QLANGUAGESERVERUTILS_P_H
diff --git a/src/qmlls/qqmlrangeformatting.cpp b/src/qmlls/qqmlrangeformatting.cpp
new file mode 100644
index 0000000000..1bcabc190d
--- /dev/null
+++ b/src/qmlls/qqmlrangeformatting.cpp
@@ -0,0 +1,144 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qqmlrangeformatting_p.h>
+#include <qqmlcodemodel_p.h>
+#include <qqmllsutils_p.h>
+
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomindentinglinewriter_p.h>
+#include <QtQmlDom/private/qqmldomcodeformatter_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+#include <QtQmlDom/private/qqmldommock_p.h>
+#include <QtQmlDom/private/qqmldomcompare_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(formatLog)
+
+QQmlRangeFormatting::QQmlRangeFormatting(QmlLsp::QQmlCodeModel *codeModel)
+ : QQmlBaseModule(codeModel)
+{
+}
+
+QString QQmlRangeFormatting::name() const
+{
+ return u"QQmlRangeFormatting"_s;
+}
+
+void QQmlRangeFormatting::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerDocumentRangeFormattingRequestHandler(getRequestHandler());
+}
+
+void QQmlRangeFormatting::setupCapabilities(const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ serverCapabilities.capabilities.documentRangeFormattingProvider = true;
+}
+
+void QQmlRangeFormatting::process(RequestPointerArgument request)
+{
+ using namespace QQmlJS::Dom;
+ QList<QLspSpecification::TextEdit> result{};
+
+ QmlLsp::OpenDocument doc = m_codeModel->openDocumentByUrl(
+ QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri));
+
+ DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely);
+ if (!file) {
+ qWarning() << u"Could not find the file"_s << doc.snapshot.doc.toString();
+ return;
+ }
+
+ if (auto envPtr = file.environment().ownerAs<DomEnvironment>())
+ envPtr->clearReferenceCache();
+
+ auto qmlFile = file.ownerAs<QmlFile>();
+ auto code = qmlFile->code();
+
+ // Range requested to be formatted
+ const auto selectedRange = request->m_parameters.range;
+ const auto selectedRangeStartLine = selectedRange.start.line;
+ const auto selectedRangeEndLine = selectedRange.end.line;
+ Q_ASSERT(selectedRangeStartLine >= 0);
+ Q_ASSERT(selectedRangeEndLine >= 0);
+
+ LineWriterOptions options;
+ options.updateOptions = LineWriterOptions::Update::None;
+ options.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+
+ QTextStream in(&code);
+ FormatTextStatus status = FormatTextStatus::initialStatus();
+ FormatPartialStatus partialStatus({}, options.formatOptions, status);
+
+ // Get the token status of the previous line without performing write operation
+ int lineNumber = 0;
+ while (!in.atEnd()) {
+ const auto line = in.readLine();
+ partialStatus = formatCodeLine(line, options.formatOptions, partialStatus.currentStatus);
+ if (++lineNumber >= selectedRangeStartLine)
+ break;
+ }
+
+ QString resultText;
+ QTextStream out(&resultText);
+ IndentingLineWriter lw([&out](QStringView writtenText) { out << writtenText.toUtf8(); },
+ QString(), options, partialStatus.currentStatus);
+ OutWriter ow(lw);
+ ow.indentNextlines = true;
+
+ // TODO: This is a workaround and will/should be handled by the actual formatter
+ // once we improve the range-formatter design in QTBUG-116139
+ const auto removeSpaces = [](const QString &line) {
+ QString result;
+ QTextStream out(&result);
+ bool previousIsSpace = false;
+
+ int newLineCount = 0;
+ for (int i = 0; i < line.length(); ++i) {
+ QChar c = line.at(i);
+ if (c.isSpace()) {
+ if (c == '\n'_L1 && newLineCount < 2) {
+ out << '\n'_L1;
+ ++newLineCount;
+ } else if (c == '\r'_L1 && (i + 1) < line.length() && line.at(i + 1) == '\n'_L1
+ && newLineCount < 2) {
+ out << "\r\n";
+ ++newLineCount;
+ ++i;
+ } else {
+ if (!previousIsSpace)
+ out << ' '_L1;
+ }
+ previousIsSpace = true;
+ } else {
+ out << c;
+ previousIsSpace = false;
+ newLineCount = 0;
+ }
+ }
+
+ out.flush();
+ return result;
+ };
+
+ const auto startOffset = QQmlLSUtils::textOffsetFrom(code, selectedRangeStartLine, 0);
+ const auto endOffset = QQmlLSUtils::textOffsetFrom(code, selectedRangeEndLine + 1, 0);
+ const auto &toFormat = code.mid(startOffset, endOffset - startOffset);
+ ow.write(removeSpaces(toFormat));
+ ow.flush();
+ ow.eof();
+
+ const auto documentLineCount = QQmlLSUtils::textRowAndColumnFrom(code, code.length()).line;
+ code.replace(startOffset, toFormat.length(), resultText);
+
+ QLspSpecification::TextEdit add;
+ add.newText = code.toUtf8();
+ add.range = { { 0, 0 }, { documentLineCount + 1 } };
+ result.append(add);
+
+ request->m_response.sendResponse(result);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlrangeformatting_p.h b/src/qmlls/qqmlrangeformatting_p.h
new file mode 100644
index 0000000000..5c51ea4f12
--- /dev/null
+++ b/src/qmlls/qqmlrangeformatting_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLRANGEFORMATTING_P_H
+#define QQMLRANGEFORMATTING_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlbasemodule_p.h"
+#include "qqmlcodemodel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct RangeFormattingRequest
+ : public BaseRequest<QLspSpecification::DocumentRangeFormattingParams,
+ QLspSpecification::Responses::DocumentRangeFormattingResponseType>
+{
+};
+
+class QQmlRangeFormatting : public QQmlBaseModule<RangeFormattingRequest>
+{
+ Q_OBJECT
+public:
+ QQmlRangeFormatting(QmlLsp::QQmlCodeModel *codeModel);
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+ void process(RequestPointerArgument req) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLRANGEFORMATTING_P_H
diff --git a/src/qmlls/qqmlrenamesymbolsupport.cpp b/src/qmlls/qqmlrenamesymbolsupport.cpp
new file mode 100644
index 0000000000..a812b5a25c
--- /dev/null
+++ b/src/qmlls/qqmlrenamesymbolsupport.cpp
@@ -0,0 +1,108 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmllsutils_p.h"
+#include "qqmlrenamesymbolsupport_p.h"
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+QQmlRenameSymbolSupport::QQmlRenameSymbolSupport(QmlLsp::QQmlCodeModel *model) : BaseT(model) { }
+
+QString QQmlRenameSymbolSupport::name() const
+{
+ return u"QmlRenameSymbolSupport"_s;
+}
+
+void QQmlRenameSymbolSupport::setupCapabilities(
+ const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverCapabilities)
+{
+ // use a bool for now. Alternatively, if the client supports "prepareSupport", one could
+ // use a RenameOptions here. See following page for more information about prepareSupport:
+ // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareRename
+ serverCapabilities.capabilities.renameProvider = true;
+}
+
+void QQmlRenameSymbolSupport::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
+{
+ protocol->registerRenameRequestHandler(getRequestHandler());
+}
+
+void QQmlRenameSymbolSupport::process(QQmlRenameSymbolSupport::RequestPointerArgument request)
+{
+ QLspSpecification::WorkspaceEdit result;
+ ResponseScopeGuard guard(result, request->m_response);
+
+ auto itemsFound = itemsForRequest(request);
+ if (guard.setErrorFrom(itemsFound))
+ return;
+
+ QQmlLSUtilsItemLocation &front = std::get<QList<QQmlLSUtilsItemLocation>>(itemsFound).front();
+
+ const QString newName = QString::fromUtf8(request->m_parameters.newName);
+ auto expressionType = QQmlLSUtils::resolveExpressionType(front.domItem, ResolveOwnerType);
+
+ if (!expressionType) {
+ guard.setError(QQmlLSUtilsErrorMessage{ 0, u"Cannot rename the requested object"_s });
+ return;
+ }
+
+ if (guard.setErrorFrom(QQmlLSUtils::checkNameForRename(front.domItem, newName, expressionType)))
+ return;
+
+ auto &editsByFileForResult = result.documentChanges.emplace();
+
+ // The QLspSpecification::WorkspaceEdit requires the changes to be grouped by files, so
+ // collect them into editsByFileUris.
+ QMap<QUrl, QList<QLspSpecification::TextEdit>> editsByFileUris;
+
+ auto renames = QQmlLSUtils::renameUsagesOf(front.domItem, newName, expressionType);
+
+ QQmlJS::Dom::DomItem files = front.domItem.top().field(QQmlJS::Dom::Fields::qmlFileWithPath);
+
+ QHash<QString, QString> codeCache;
+
+ for (const auto &rename : renames) {
+ QLspSpecification::TextEdit edit;
+
+ const QUrl uri = QUrl::fromLocalFile(rename.location.filename);
+
+ auto cacheEntry = codeCache.find(rename.location.filename);
+ if (cacheEntry == codeCache.end()) {
+ auto file = files.key(rename.location.filename)
+ .field(QQmlJS::Dom::Fields::currentItem)
+ .ownerAs<QQmlJS::Dom::QmlFile>();
+ if (!file) {
+ qDebug() << "File" << rename.location.filename
+ << "not found in DOM! Available files are" << files.keys();
+ continue;
+ }
+ cacheEntry = codeCache.insert(rename.location.filename, file->code());
+ }
+
+ edit.range = QQmlLSUtils::qmlLocationToLspLocation(cacheEntry.value(),
+ rename.location.sourceLocation);
+ edit.newText = rename.replacement.toUtf8();
+
+ editsByFileUris[uri].append(edit);
+ }
+
+ for (auto it = editsByFileUris.keyValueBegin(); it != editsByFileUris.keyValueEnd(); ++it) {
+ QLspSpecification::TextDocumentEdit editsForCurrentFile;
+ editsForCurrentFile.textDocument.uri = it->first.toEncoded();
+
+ // TODO: do we need to take care of the optional versioning in
+ // editsForCurrentFile.textDocument.version? see
+ // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#optionalVersionedTextDocumentIdentifier
+ // for more details
+
+ for (const auto &x : std::as_const(it->second)) {
+ editsForCurrentFile.edits.append(x);
+ }
+ editsByFileForResult.append(editsForCurrentFile);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlrenamesymbolsupport_p.h b/src/qmlls/qqmlrenamesymbolsupport_p.h
new file mode 100644
index 0000000000..0f1d8be252
--- /dev/null
+++ b/src/qmlls/qqmlrenamesymbolsupport_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLRENAMESYMBOLSUPPORT_P_H
+#define QQMLRENAMESYMBOLSUPPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlanguageserver_p.h"
+#include "qqmlcodemodel_p.h"
+#include "qqmlbasemodule_p.h"
+
+QT_BEGIN_NAMESPACE
+struct RenameRequest : public BaseRequest<QLspSpecification::RenameParams,
+ QLspSpecification::Responses::RenameResponseType>
+{
+};
+
+class QQmlRenameSymbolSupport : public QQmlBaseModule<RenameRequest>
+{
+ Q_OBJECT
+public:
+ QQmlRenameSymbolSupport(QmlLsp::QQmlCodeModel *codeModel);
+
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+
+ void process(RequestPointerArgument request) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLRENAMESYMBOLSUPPORT_P_H
diff --git a/src/qmlls/qqmlsemantictokens.cpp b/src/qmlls/qqmlsemantictokens.cpp
new file mode 100644
index 0000000000..3da56782fc
--- /dev/null
+++ b/src/qmlls/qqmlsemantictokens.cpp
@@ -0,0 +1,773 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qqmlsemantictokens_p.h>
+
+#include <QtQmlLS/private/qqmllsutils_p.h>
+#include <QtQmlDom/private/qqmldomscriptelements_p.h>
+#include <QtQmlDom/private/qqmldomfieldfilter_p.h>
+
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(semanticTokens, "qt.languageserver.semanticTokens")
+
+using namespace QQmlJS::AST;
+using namespace QQmlJS::Dom;
+using namespace QLspSpecification;
+
+static int tokenTypeFromRegion(QQmlJS::Dom::FileLocationRegion region)
+{
+ switch (region) {
+ case AsTokenRegion:
+ case BreakKeywordRegion:
+ case DoKeywordRegion:
+ case CaseKeywordRegion:
+ case CatchKeywordRegion:
+ case ComponentKeywordRegion:
+ case ContinueKeywordRegion:
+ case ElseKeywordRegion:
+ case EnumKeywordRegion:
+ case ForKeywordRegion:
+ case FinallyKeywordRegion:
+ case FunctionKeywordRegion:
+ case ImportTokenRegion:
+ case OnTokenRegion:
+ case PragmaKeywordRegion:
+ case ReturnKeywordRegion:
+ case SignalKeywordRegion:
+ case ThrowKeywordRegion:
+ case TryKeywordRegion:
+ case WhileKeywordRegion:
+ case PropertyKeywordRegion:
+ case InOfTokenRegion:
+ case DefaultKeywordRegion:
+ case ReadonlyKeywordRegion:
+ case RequiredKeywordRegion:
+ case IfKeywordRegion:
+ case SwitchKeywordRegion:
+ return int(SemanticTokenTypes::Keyword);
+ case QuestionMarkTokenRegion:
+ case EllipsisTokenRegion:
+ case OperatorTokenRegion:
+ return int(SemanticTokenTypes::Operator);
+ case QQmlJS::Dom::TypeIdentifierRegion:
+ return int(SemanticTokenTypes::Type);
+ case PragmaValuesRegion:
+ case IdentifierRegion:
+ case IdNameRegion:
+ return int(SemanticTokenTypes::Variable);
+ case ImportUriRegion:
+ return int(SemanticTokenTypes::Namespace);
+ case IdTokenRegion:
+ case OnTargetRegion:
+ return int(SemanticTokenTypes::Property);
+ case VersionRegion:
+ case EnumValueRegion:
+ return int(SemanticTokenTypes::Number);
+ default:
+ return int(SemanticTokenTypes::Variable);
+ }
+ Q_UNREACHABLE_RETURN({});
+}
+
+static FieldFilter highlightingFilter()
+{
+ QMultiMap<QString, QString> fieldFilterAdd{};
+ QMultiMap<QString, QString> fieldFilterRemove{
+ { QString(), QString::fromUtf16(Fields::propertyInfos) },
+ { QString(), QString::fromUtf16(Fields::fileLocationsTree) },
+ { QString(), QString::fromUtf16(Fields::importScope) },
+ { QString(), QString::fromUtf16(Fields::defaultPropertyName) },
+ { QString(), QString::fromUtf16(Fields::get) },
+ };
+ return FieldFilter{ fieldFilterAdd, fieldFilterRemove };
+}
+
+HighlightingVisitor::HighlightingVisitor(Highlights &highlights,
+ const std::optional<HighlightsRange> &range)
+ : m_highlights(highlights), m_range(range)
+{
+}
+
+bool HighlightingVisitor::operator()(Path, const DomItem &item, bool)
+{
+ if (m_range.has_value()) {
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return true;
+ const auto regions = fLocs->info().regions;
+ if (!HighlightingUtils::rangeOverlapsWithSourceLocation(regions[MainRegion],
+ m_range.value()))
+ return true;
+ }
+ switch (item.internalKind()) {
+ case DomType::Comment: {
+ highlightComment(item);
+ return true;
+ }
+ case DomType::Import: {
+ highlightImport(item);
+ return true;
+ }
+ case DomType::Binding: {
+ highlightBinding(item);
+ return true;
+ }
+ case DomType::Pragma: {
+ highlightPragma(item);
+ return true;
+ }
+ case DomType::EnumDecl: {
+ highlightEnumDecl(item);
+ return true;
+ }
+ case DomType::EnumItem: {
+ highlightEnumItem(item);
+ return true;
+ }
+ case DomType::QmlObject: {
+ highlightQmlObject(item);
+ return true;
+ }
+ case DomType::QmlComponent: {
+ highlightComponent(item);
+ return true;
+ }
+ case DomType::PropertyDefinition: {
+ highlightPropertyDefinition(item);
+ return true;
+ }
+ case DomType::MethodInfo: {
+ highlightMethod(item);
+ return true;
+ }
+ case DomType::ScriptLiteral: {
+ highlightScriptLiteral(item);
+ return true;
+ }
+ case DomType::ScriptIdentifierExpression: {
+ highlightIdentifier(item);
+ return true;
+ }
+ default:
+ if (item.ownerAs<ScriptExpression>())
+ highlightScriptExpressions(item);
+ return true;
+ }
+ Q_UNREACHABLE_RETURN(false);
+}
+
+void HighlightingVisitor::highlightComment(const DomItem &item)
+{
+ const auto comment = item.as<Comment>();
+ Q_ASSERT(comment);
+ const auto locs = HighlightingUtils::sourceLocationsFromMultiLineToken(
+ comment->info().comment(), comment->info().sourceLocation());
+ for (const auto &loc : locs)
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Comment));
+}
+
+void HighlightingVisitor::highlightImport(const DomItem &item)
+{
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ const auto import = item.as<Import>();
+ Q_ASSERT(import);
+ m_highlights.addHighlight(regions, ImportTokenRegion);
+ if (import->uri.isModule())
+ m_highlights.addHighlight(regions[ImportUriRegion], int(SemanticTokenTypes::Namespace));
+ else
+ m_highlights.addHighlight(regions[ImportUriRegion], int(SemanticTokenTypes::String));
+ if (regions.contains(VersionRegion))
+ m_highlights.addHighlight(regions, VersionRegion);
+ if (regions.contains(AsTokenRegion)) {
+ m_highlights.addHighlight(regions, AsTokenRegion);
+ m_highlights.addHighlight(regions[IdNameRegion], int(SemanticTokenTypes::Namespace));
+ }
+}
+
+void HighlightingVisitor::highlightBinding(const DomItem &item)
+{
+ const auto binding = item.as<Binding>();
+ Q_ASSERT(binding);
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs) {
+ qCDebug(semanticTokens) << "Can't find the locations for" << item.internalKind();
+ return;
+ }
+ const auto regions = fLocs->info().regions;
+ // If dotted name, then defer it to be handled in ScriptIdentifierExpression
+ if (binding->name().contains("."_L1))
+ return;
+
+ if (binding->bindingType() != BindingType::Normal) {
+ m_highlights.addHighlight(regions, OnTokenRegion);
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Property));
+ return;
+ }
+
+ return highlightBySemanticAnalysis(item, regions[IdentifierRegion]);
+}
+
+void HighlightingVisitor::highlightPragma(const DomItem &item)
+{
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ m_highlights.addHighlight(regions, PragmaKeywordRegion);
+ m_highlights.addHighlight(regions, IdentifierRegion);
+ const auto pragma = item.as<Pragma>();
+ for (auto i = 0; i < pragma->values.size(); ++i) {
+ DomItem value = item.field(Fields::values).index(i);
+ const auto valueRegions = FileLocations::treeOf(value)->info().regions;
+ m_highlights.addHighlight(valueRegions, PragmaValuesRegion);
+ }
+ return;
+}
+
+void HighlightingVisitor::highlightEnumDecl(const DomItem &item)
+{
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ m_highlights.addHighlight(regions, EnumKeywordRegion);
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Enum));
+}
+
+void HighlightingVisitor::highlightEnumItem(const DomItem &item)
+{
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::EnumMember));
+ if (regions.contains(EnumValueRegion))
+ m_highlights.addHighlight(regions, EnumValueRegion);
+}
+
+void HighlightingVisitor::highlightQmlObject(const DomItem &item)
+{
+ const auto qmlObject = item.as<QmlObject>();
+ Q_ASSERT(qmlObject);
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ // Handle ids here
+ if (!qmlObject->idStr().isEmpty()) {
+ m_highlights.addHighlight(regions, IdTokenRegion);
+ m_highlights.addHighlight(regions, IdNameRegion);
+ }
+ // If dotted name, then defer it to be handled in ScriptIdentifierExpression
+ if (qmlObject->name().contains("."_L1))
+ return;
+
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Type));
+}
+
+void HighlightingVisitor::highlightComponent(const DomItem &item)
+{
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ m_highlights.addHighlight(regions, ComponentKeywordRegion);
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Type));
+}
+
+void HighlightingVisitor::highlightPropertyDefinition(const DomItem &item)
+{
+ const auto propertyDef = item.as<PropertyDefinition>();
+ Q_ASSERT(propertyDef);
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ int modifier = 0;
+ HighlightingUtils::addModifier(SemanticTokenModifiers::Definition, &modifier);
+ if (propertyDef->isDefaultMember) {
+ HighlightingUtils::addModifier(SemanticTokenModifiers::DefaultLibrary,
+ &modifier);
+ m_highlights.addHighlight(regions[DefaultKeywordRegion],
+ int(SemanticTokenTypes::Keyword));
+ }
+ if (propertyDef->isRequired) {
+ HighlightingUtils::addModifier(SemanticTokenModifiers::Abstract, &modifier);
+ m_highlights.addHighlight(regions[RequiredKeywordRegion],
+ int(SemanticTokenTypes::Keyword));
+ }
+ if (propertyDef->isReadonly) {
+ HighlightingUtils::addModifier(SemanticTokenModifiers::Readonly, &modifier);
+ m_highlights.addHighlight(regions[ReadonlyKeywordRegion],
+ int(SemanticTokenTypes::Keyword));
+ }
+ m_highlights.addHighlight(regions, PropertyKeywordRegion);
+ if (propertyDef->isAlias())
+ m_highlights.addHighlight(regions[TypeIdentifierRegion],
+ int(SemanticTokenTypes::Keyword));
+ else
+ m_highlights.addHighlight(regions, TypeIdentifierRegion);
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Property),
+ modifier);
+}
+
+void HighlightingVisitor::highlightMethod(const DomItem &item)
+{
+ const auto method = item.as<MethodInfo>();
+ Q_ASSERT(method);
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ switch (method->methodType) {
+ case MethodInfo::Signal: {
+ m_highlights.addHighlight(regions, SignalKeywordRegion);
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Method));
+ break;
+ }
+ case MethodInfo::Method: {
+ m_highlights.addHighlight(regions, FunctionKeywordRegion);
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Method));
+ m_highlights.addHighlight(regions[TypeIdentifierRegion], int(SemanticTokenTypes::Type));
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+
+ for (auto i = 0; i < method->parameters.size(); ++i) {
+ DomItem parameter = item.field(Fields::parameters).index(i);
+ const auto paramRegions = FileLocations::treeOf(parameter)->info().regions;
+ m_highlights.addHighlight(paramRegions[IdentifierRegion],
+ int(SemanticTokenTypes::Parameter));
+ m_highlights.addHighlight(paramRegions[TypeIdentifierRegion], int(SemanticTokenTypes::Type));
+ }
+ return;
+}
+
+void HighlightingVisitor::highlightScriptLiteral(const DomItem &item)
+{
+ const auto literal = item.as<ScriptElements::Literal>();
+ Q_ASSERT(literal);
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ if (std::holds_alternative<QString>(literal->literalValue())) {
+ const QString value = '\"' + std::get<QString>(literal->literalValue()) + '\"';
+ const auto &locs = HighlightingUtils::sourceLocationsFromMultiLineToken(
+ value, regions[MainRegion]);
+ for (const auto &loc : locs)
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::String));
+ } else if (std::holds_alternative<double>(literal->literalValue()))
+ m_highlights.addHighlight(regions[MainRegion], int(SemanticTokenTypes::Number));
+ else if (std::holds_alternative<bool>(literal->literalValue()))
+ m_highlights.addHighlight(regions[MainRegion], int(SemanticTokenTypes::Keyword));
+ else if (std::holds_alternative<std::nullptr_t>(literal->literalValue()))
+ m_highlights.addHighlight(regions[MainRegion], int(SemanticTokenTypes::Keyword));
+ else
+ qCWarning(semanticTokens) << "Invalid literal variant";
+}
+
+void HighlightingVisitor::highlightIdentifier(const DomItem &item)
+{
+ using namespace QLspSpecification;
+ const auto id = item.as<ScriptElements::IdentifierExpression>();
+ Q_ASSERT(id);
+ const auto loc = id->mainRegionLocation();
+ // Many of the scriptIdentifiers expressions are already handled by
+ // other cases. In those cases, if the location offset is already in the list
+ // we don't need to perform expensive resolveExpressionType operation.
+ if (m_highlights.highlights().contains(loc.offset))
+ return;
+
+ highlightBySemanticAnalysis(item, loc);
+}
+
+void HighlightingVisitor::highlightBySemanticAnalysis(const DomItem &item, QQmlJS::SourceLocation loc)
+{
+ const auto expression = QQmlLSUtils::resolveExpressionType(
+ item, QQmlLSUtilsResolveOptions::ResolveOwnerType);
+
+ if (!expression) {
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Variable));
+ return;
+ }
+ switch (expression->type) {
+ case QmlComponentIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Type));
+ return;
+ case JavaScriptIdentifier: {
+ const auto name = expression->name;
+ const auto scope = expression->semanticScope;
+ SemanticTokenTypes tokenType = SemanticTokenTypes::Variable;
+ int modifier = 0;
+ if (const auto jsIdentifier = scope->jsIdentifier(name.value())) {
+ switch (jsIdentifier.value().kind) {
+ case QQmlJSScope::JavaScriptIdentifier::Parameter:
+ tokenType = SemanticTokenTypes::Parameter;
+ break;
+ case QQmlJSScope::JavaScriptIdentifier::LexicalScoped: // let or const
+ case QQmlJSScope::JavaScriptIdentifier::FunctionScoped: // var
+ case QQmlJSScope::JavaScriptIdentifier::Injected:
+ default:
+ tokenType = SemanticTokenTypes::Variable;
+ break;
+ }
+ if (jsIdentifier.value().isConst) {
+ HighlightingUtils::addModifier(SemanticTokenModifiers::Readonly,
+ &modifier);
+ }
+ }
+ m_highlights.addHighlight(loc, int(tokenType), modifier);
+ return;
+ }
+ case PropertyIdentifier: {
+ if (const auto scope = expression->semanticScope) {
+ const auto name = expression->name;
+ const auto property = scope->property(name.value());
+ int modifier = 0;
+ if (!property.isWritable()) {
+ HighlightingUtils::addModifier(SemanticTokenModifiers::Readonly,
+ &modifier);
+ }
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Property), modifier);
+ }
+ return;
+ }
+ case PropertyChangedSignalIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
+ return;
+ case PropertyChangedHandlerIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
+ return;
+ case SignalIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
+ return;
+ case SignalHandlerIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
+ return;
+ case MethodIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
+ return;
+ case QmlObjectIdIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Variable));
+ return;
+ case SingletonIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Type));
+ return;
+ case EnumeratorIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Enum));
+ return;
+ case EnumeratorValueIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::EnumMember));
+ return;
+ case AttachedTypeIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Type));
+ return;
+ case GroupedPropertyIdentifier:
+ m_highlights.addHighlight(loc, int(SemanticTokenTypes::Property));
+ return;
+ default:
+ qCWarning(semanticTokens)
+ << QString::fromLatin1("Semantic token for %1 has not been implemented yet")
+ .arg(int(expression->type));
+ }
+ Q_UNREACHABLE_RETURN();
+}
+
+void HighlightingVisitor::highlightScriptExpressions(const DomItem &item)
+{
+ const auto fLocs = FileLocations::treeOf(item);
+ if (!fLocs)
+ return;
+ const auto regions = fLocs->info().regions;
+ switch (item.internalKind()) {
+ case DomType::ScriptLiteral:
+ highlightScriptLiteral(item);
+ return;
+ case DomType::ScriptForStatement:
+ m_highlights.addHighlight(regions, ForKeywordRegion);
+ m_highlights.addHighlight(regions[TypeIdentifierRegion],
+ int(SemanticTokenTypes::Keyword));
+ return;
+
+ case DomType::ScriptVariableDeclaration: {
+ m_highlights.addHighlight(regions[TypeIdentifierRegion],
+ int(SemanticTokenTypes::Keyword));
+ return;
+ }
+ case DomType::ScriptReturnStatement:
+ m_highlights.addHighlight(regions, ReturnKeywordRegion);
+ return;
+ case DomType::ScriptCaseClause:
+ m_highlights.addHighlight(regions, CaseKeywordRegion);
+ return;
+ case DomType::ScriptDefaultClause:
+ m_highlights.addHighlight(regions, DefaultKeywordRegion);
+ return;
+ case DomType::ScriptSwitchStatement:
+ m_highlights.addHighlight(regions, SwitchKeywordRegion);
+ return;
+ case DomType::ScriptWhileStatement:
+ m_highlights.addHighlight(regions, WhileKeywordRegion);
+ return;
+ case DomType::ScriptDoWhileStatement:
+ m_highlights.addHighlight(regions, DoKeywordRegion);
+ m_highlights.addHighlight(regions, WhileKeywordRegion);
+ return;
+ case DomType::ScriptTryCatchStatement:
+ m_highlights.addHighlight(regions, TryKeywordRegion);
+ m_highlights.addHighlight(regions, CatchKeywordRegion);
+ m_highlights.addHighlight(regions, FinallyKeywordRegion);
+ return;
+ case DomType::ScriptForEachStatement:
+ m_highlights.addHighlight(regions[TypeIdentifierRegion],
+ int(SemanticTokenTypes::Keyword));
+ m_highlights.addHighlight(regions, ForKeywordRegion);
+ m_highlights.addHighlight(regions, InOfTokenRegion);
+ return;
+ case DomType::ScriptThrowStatement:
+ m_highlights.addHighlight(regions, ThrowKeywordRegion);
+ return;
+ case DomType::ScriptBreakStatement:
+ m_highlights.addHighlight(regions, BreakKeywordRegion);
+ return;
+ case DomType::ScriptContinueStatement:
+ m_highlights.addHighlight(regions, ContinueKeywordRegion);
+ return;
+ case DomType::ScriptIfStatement:
+ m_highlights.addHighlight(regions, IfKeywordRegion);
+ m_highlights.addHighlight(regions, ElseKeywordRegion);
+ return;
+ case DomType::ScriptLabelledStatement:
+ m_highlights.addHighlight(regions, IdentifierRegion);
+ return;
+ case DomType::ScriptConditionalExpression:
+ m_highlights.addHighlight(regions, QuestionMarkTokenRegion);
+ m_highlights.addHighlight(regions, ColonTokenRegion);
+ return;
+ case DomType::ScriptUnaryExpression:
+ case DomType::ScriptPostExpression:
+ m_highlights.addHighlight(regions, OperatorTokenRegion);
+ return;
+ case DomType::ScriptType:
+ m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Type));
+ m_highlights.addHighlight(regions[TypeIdentifierRegion], int(SemanticTokenTypes::Type));
+ return;
+ default:
+ qCDebug(semanticTokens)
+ << "Script Expressions with kind" << item.internalKind() << "not implemented";
+ return;
+ }
+ Q_UNREACHABLE_RETURN();
+}
+
+/*!
+\internal
+\brief Returns multiple source locations for a given raw comment
+
+Needed by semantic highlighting of comments. LSP clients usually don't support multiline
+tokens. In QML, we can have multiline tokens like string literals and comments.
+This method generates multiple source locations of sub-elements of token split by a newline
+delimiter.
+*/
+QList<QQmlJS::SourceLocation>
+HighlightingUtils::sourceLocationsFromMultiLineToken(QStringView stringLiteral,
+ const QQmlJS::SourceLocation &locationInDocument)
+{
+ auto lineBreakLength = qsizetype(std::char_traits<char>::length("\n"));
+ const auto lineLengths = [&lineBreakLength](QStringView literal) {
+ std::vector<qsizetype> lineLengths;
+ qsizetype startIndex = 0;
+ qsizetype pos = literal.indexOf(u'\n');
+ while (pos != -1) {
+ // TODO: QTBUG-106813
+ // Since a document could be opened in normalized form
+ // we can't use platform dependent newline handling here.
+ // Thus, we check manually if the literal contains \r so that we split
+ // the literal at the correct offset.
+ if (pos - 1 > 0 && literal[pos - 1] == u'\r') {
+ // Handle Windows line endings
+ lineBreakLength = qsizetype(std::char_traits<char>::length("\r\n"));
+ // Move pos to the index of '\r'
+ pos = pos - 1;
+ }
+ lineLengths.push_back(pos - startIndex);
+ // Advance the lookup index, so it won't find the same index.
+ startIndex = pos + lineBreakLength;
+ pos = literal.indexOf('\n'_L1, startIndex);
+ }
+ // Push the last line
+ if (startIndex < literal.length()) {
+ lineLengths.push_back(literal.length() - startIndex);
+ }
+ return lineLengths;
+ };
+
+ QList<QQmlJS::SourceLocation> result;
+ // First token location should start from the "stringLiteral"'s
+ // location in the qml document.
+ QQmlJS::SourceLocation lineLoc = locationInDocument;
+ for (const auto lineLength : lineLengths(stringLiteral)) {
+ lineLoc.length = lineLength;
+ result.push_back(lineLoc);
+
+ // update for the next line
+ lineLoc.offset += lineLoc.length + lineBreakLength;
+ ++lineLoc.startLine;
+ lineLoc.startColumn = 1;
+ }
+ return result;
+}
+
+QList<int> HighlightingUtils::encodeSemanticTokens(Highlights &highlights)
+{
+ QList<int> result;
+ const auto highlightingTokens = highlights.highlights();
+ constexpr auto tokenEncodingLength = 5;
+ result.reserve(tokenEncodingLength * highlightingTokens.size());
+
+ int prevLine = 0;
+ int prevColumn = 0;
+
+ std::for_each(highlightingTokens.constBegin(), highlightingTokens.constEnd(), [&](const auto &token) {
+ Q_ASSERT(token.startLine >= prevLine);
+ if (token.startLine != prevLine)
+ prevColumn = 0;
+ result.emplace_back(token.startLine - prevLine);
+ result.emplace_back(token.startColumn - prevColumn);
+ result.emplace_back(token.length);
+ result.emplace_back(token.tokenType);
+ result.emplace_back(token.tokenModifier);
+ prevLine = token.startLine;
+ prevColumn = token.startColumn;
+ });
+
+ return result;
+}
+
+/*!
+\internal
+Computes the modifier value. Modifier is read as binary value in the protocol. The location
+of the bits set are interpreted as the indices of the tokenModifiers list registered by the
+server. Then, the client modifies the highlighting of the token.
+
+tokenModifiersList: ["declaration", definition, readonly, static ,,,]
+
+To set "definition" and "readonly", we need to send 0b00000110
+*/
+void HighlightingUtils::addModifier(SemanticTokenModifiers modifier, int *baseModifier)
+{
+ if (!baseModifier)
+ return;
+ *baseModifier |= (1 << int(modifier));
+}
+
+/*!
+\internal
+Check if the ranges overlap by ensuring that one range starts before the other ends
+*/
+bool HighlightingUtils::rangeOverlapsWithSourceLocation(const QQmlJS::SourceLocation &loc,
+ const HighlightsRange &r)
+{
+ int startOffsetItem = int(loc.offset);
+ int endOffsetItem = startOffsetItem + int(loc.length);
+ return (startOffsetItem <= r.endOffset) && (r.startOffset <= endOffsetItem);
+}
+
+/*
+\internal
+Increments the resultID by one.
+*/
+void HighlightingUtils::updateResultID(QByteArray &resultID)
+{
+ int length = resultID.length();
+ for (int i = length - 1; i >= 0; --i) {
+ if (resultID[i] == '9') {
+ resultID[i] = '0';
+ } else {
+ resultID[i] = resultID[i] + 1;
+ return;
+ }
+ }
+ resultID.prepend('1');
+}
+
+/*
+\internal
+A utility method that computes the difference of two list. The first argument is the encoded token data
+of the file before edited. The second argument is the encoded token data after the file is edited. Returns
+a list of SemanticTokensEdit as expected by the protocol.
+*/
+QList<SemanticTokensEdit> HighlightingUtils::computeDiff(const QList<int> &oldData, const QList<int> &newData)
+{
+ // Find the iterators pointing the first mismatch, from the start
+ const auto [oldStart, newStart] =
+ std::mismatch(oldData.cbegin(), oldData.cend(), newData.cbegin(), newData.cend());
+
+ // Find the iterators pointing the first mismatch, from the end
+ // but the iterators shouldn't pass over the start iterators found above.
+ const auto [r1, r2] = std::mismatch(oldData.crbegin(), std::make_reverse_iterator(oldStart),
+ newData.crbegin(), std::make_reverse_iterator(newStart));
+ const auto oldEnd = r1.base();
+ const auto newEnd = r2.base();
+
+ // no change
+ if (oldStart == oldEnd && newStart == newEnd)
+ return {};
+
+ SemanticTokensEdit edit;
+ edit.start = int(std::distance(newData.cbegin(), newStart));
+ edit.deleteCount = int(std::distance(oldStart, oldEnd));
+
+ if (newStart >= newData.cbegin() && newEnd <= newData.cend() && newStart < newEnd)
+ edit.data.emplace(newStart, newEnd);
+
+ return { edit };
+}
+
+
+void Highlights::addHighlight(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier)
+{
+ if (!loc.isValid()) {
+ qCDebug(semanticTokens) << "Invalid locations: Cannot add highlight to token";
+ return;
+ }
+
+ if (!m_highlights.contains(loc.offset))
+ m_highlights.insert(loc.offset, Token(loc, tokenType, tokenModifier));
+}
+
+void Highlights::addHighlight(const QMap<FileLocationRegion, QQmlJS::SourceLocation> &regions,
+ FileLocationRegion region, int modifier)
+{
+ if (!regions.contains(region)) {
+ qCDebug(semanticTokens) << "Invalid region: Cannot add highlight to token";
+ return;
+ }
+
+ const auto loc = regions.value(region);
+ return addHighlight(loc, tokenTypeFromRegion(region), modifier);
+}
+
+QList<int> Highlights::collectTokens(const QQmlJS::Dom::DomItem &item,
+ const std::optional<HighlightsRange> &range)
+{
+ using namespace QQmlJS::Dom;
+ HighlightingVisitor highlightDomElements(*this, range);
+ // In QmlFile level, visitTree visits even FileLocations tree which takes quite a time to
+ // finish. HighlightingFilter is added to prevent unnecessary visits.
+ item.visitTree(Path(), highlightDomElements, VisitOption::Default, emptyChildrenVisitor,
+ emptyChildrenVisitor, highlightingFilter());
+
+ return HighlightingUtils::encodeSemanticTokens(*this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qqmlsemantictokens_p.h b/src/qmlls/qqmlsemantictokens_p.h
new file mode 100644
index 0000000000..34c2bbd76f
--- /dev/null
+++ b/src/qmlls/qqmlsemantictokens_p.h
@@ -0,0 +1,132 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLSEMANTICTOKENS_P_H
+#define QQMLSEMANTICTOKENS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLanguageServer/private/qlanguageserverspec_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(semanticTokens)
+
+// Represents a semantic highlighting token
+// startLine and startColumn are 0-based as in LSP spec.
+struct Token
+{
+ Token() = default;
+ Token(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier = 0)
+ : offset(loc.offset),
+ length(loc.length),
+ startLine(loc.startLine - 1),
+ startColumn(loc.startColumn - 1),
+ tokenType(tokenType),
+ tokenModifier(tokenModifier)
+ {
+ }
+
+ inline friend bool operator<(const Token &lhs, const Token &rhs)
+ {
+ return lhs.offset < rhs.offset;
+ }
+
+ inline friend bool operator==(const Token &lhs, const Token &rhs)
+ {
+ return lhs.offset == rhs.offset && lhs.length == rhs.length
+ && lhs.startLine == rhs.startLine && lhs.startColumn == rhs.startColumn
+ && lhs.tokenType == rhs.tokenType && lhs.tokenModifier == rhs.tokenModifier;
+ }
+
+ int offset;
+ int length;
+ int startLine;
+ int startColumn;
+ int tokenType;
+ int tokenModifier;
+};
+
+using HighlightsContainer = QMap<int, Token>;
+
+/*!
+\internal
+Offsets start from zero.
+*/
+struct HighlightsRange
+{
+ int startOffset;
+ int endOffset;
+};
+
+class Highlights
+{
+public:
+ using HighlightsContainer = QMap<int, Token>;
+ void addHighlight(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier = 0);
+ void addHighlight(const QMap<QQmlJS::Dom::FileLocationRegion, QQmlJS::SourceLocation> &regions,
+ QQmlJS::Dom::FileLocationRegion region, int tokenModifier = 0);
+ QList<int> collectTokens(const QQmlJS::Dom::DomItem &item,
+ const std::optional<HighlightsRange> &range);
+
+ HighlightsContainer &highlights() { return m_highlights; }
+ const HighlightsContainer &highlights() const { return m_highlights; }
+
+private:
+ HighlightsContainer m_highlights;
+};
+
+struct HighlightingUtils
+{
+ static QList<int> encodeSemanticTokens(Highlights &highlights);
+ static QList<QQmlJS::SourceLocation>
+ sourceLocationsFromMultiLineToken(QStringView code,
+ const QQmlJS::SourceLocation &tokenLocation);
+ static void addModifier(QLspSpecification::SemanticTokenModifiers modifier, int *baseModifier);
+ static bool rangeOverlapsWithSourceLocation(const QQmlJS::SourceLocation &loc, const HighlightsRange &r);
+ static QList<QLspSpecification::SemanticTokensEdit> computeDiff(const QList<int> &, const QList<int> &);
+ static void updateResultID(QByteArray &resultID);
+};
+
+class HighlightingVisitor
+{
+public:
+ HighlightingVisitor(Highlights &highlights, const std::optional<HighlightsRange> &range);
+ bool operator()(QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &item, bool);
+
+private:
+ void highlightComment(const QQmlJS::Dom::DomItem &item);
+ void highlightImport(const QQmlJS::Dom::DomItem &item);
+ void highlightBinding(const QQmlJS::Dom::DomItem &item);
+ void highlightPragma(const QQmlJS::Dom::DomItem &item);
+ void highlightEnumItem(const QQmlJS::Dom::DomItem &item);
+ void highlightEnumDecl(const QQmlJS::Dom::DomItem &item);
+ void highlightQmlObject(const QQmlJS::Dom::DomItem &item);
+ void highlightComponent(const QQmlJS::Dom::DomItem &item);
+ void highlightPropertyDefinition(const QQmlJS::Dom::DomItem &item);
+ void highlightMethod(const QQmlJS::Dom::DomItem &item);
+ void highlightScriptLiteral(const QQmlJS::Dom::DomItem &item);
+ void highlightIdentifier(const QQmlJS::Dom::DomItem &item);
+ void highlightBySemanticAnalysis(const QQmlJS::Dom::DomItem &item, QQmlJS::SourceLocation loc);
+ void highlightScriptExpressions(const QQmlJS::Dom::DomItem &item);
+
+private:
+ Highlights &m_highlights;
+ std::optional<HighlightsRange> m_range;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSEMANTICTOKENS_P_H
diff --git a/src/qmlls/qtextblock.cpp b/src/qmlls/qtextblock.cpp
new file mode 100644
index 0000000000..6fc1fa6a5c
--- /dev/null
+++ b/src/qmlls/qtextblock.cpp
@@ -0,0 +1,101 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtextblock_p.h"
+#include "qtextdocument_p.h"
+
+#include <QtCore/qstring.h>
+
+namespace Utils {
+
+bool TextBlock::isValid() const
+{
+ return m_document;
+}
+
+void TextBlock::setBlockNumber(int blockNumber)
+{
+ m_blockNumber = blockNumber;
+}
+
+int TextBlock::blockNumber() const
+{
+ return m_blockNumber;
+}
+
+void TextBlock::setPosition(int position)
+{
+ m_position = position;
+}
+
+int TextBlock::position() const
+{
+ return m_position;
+}
+
+void TextBlock::setLength(int length)
+{
+ m_length = length;
+}
+
+int TextBlock::length() const
+{
+ return m_length;
+}
+
+TextBlock TextBlock::next() const
+{
+ return m_document->findBlockByNumber(m_blockNumber + 1);
+}
+
+TextBlock TextBlock::previous() const
+{
+ return m_document->findBlockByNumber(m_blockNumber - 1);
+}
+
+int TextBlock::userState() const
+{
+ return m_document->userState(m_blockNumber);
+}
+
+void TextBlock::setUserState(int state)
+{
+ m_document->setUserState(m_blockNumber, state);
+}
+
+void TextBlock::setDocument(TextDocument *document)
+{
+ m_document = document;
+}
+
+TextDocument *TextBlock::document() const
+{
+ return m_document;
+}
+
+QString TextBlock::text() const
+{
+ return document()->toPlainText().mid(position(), length());
+}
+
+int TextBlock::revision() const
+{
+ return m_revision;
+}
+
+void TextBlock::setRevision(int rev)
+{
+ m_revision = rev;
+}
+
+bool operator==(const TextBlock &t1, const TextBlock &t2)
+{
+ return t1.document() == t2.document() && t1.blockNumber() == t2.blockNumber();
+}
+
+bool operator!=(const TextBlock &t1, const TextBlock &t2)
+{
+ return !(t1 == t2);
+}
+
+} // namespace Utils
diff --git a/src/qmlls/qtextblock_p.h b/src/qmlls/qtextblock_p.h
new file mode 100644
index 0000000000..138d28c033
--- /dev/null
+++ b/src/qmlls/qtextblock_p.h
@@ -0,0 +1,73 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTEXTBLOCK_P_H
+#define QTEXTBLOCK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+
+namespace Utils {
+
+class TextDocument;
+class TextBlockUserData;
+
+class TextBlock
+{
+public:
+ bool isValid() const;
+
+ void setBlockNumber(int blockNumber);
+ int blockNumber() const;
+
+ void setPosition(int position);
+ int position() const;
+
+ void setLength(int length);
+ int length() const;
+
+ TextBlock next() const;
+ TextBlock previous() const;
+
+ int userState() const;
+ void setUserState(int state);
+
+ bool isVisible() const;
+ void setVisible(bool visible);
+
+ void setLineCount(int count);
+ int lineCount() const;
+
+ void setDocument(TextDocument *document);
+ TextDocument *document() const;
+
+ QString text() const;
+
+ int revision() const;
+ void setRevision(int rev);
+
+ friend bool operator==(const TextBlock &t1, const TextBlock &t2);
+ friend bool operator!=(const TextBlock &t1, const TextBlock &t2);
+
+private:
+ TextDocument *m_document = nullptr;
+ int m_revision = 0;
+
+ int m_position = 0;
+ int m_length = 0;
+ int m_blockNumber = -1;
+};
+
+} // namespace Utils
+
+#endif // TEXTBLOCK_P_H
diff --git a/src/qmlls/qtextcursor.cpp b/src/qmlls/qtextcursor.cpp
new file mode 100644
index 0000000000..79289c3fa5
--- /dev/null
+++ b/src/qmlls/qtextcursor.cpp
@@ -0,0 +1,122 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtextcursor_p.h"
+#include "qtextdocument_p.h"
+#include "qtextblock_p.h"
+
+namespace Utils {
+
+class TextFrame;
+class TextTable;
+class TextTableCell;
+
+TextCursor::TextCursor(TextDocument *document) : m_document(document) { }
+
+bool TextCursor::movePosition(TextCursor::MoveOperation op, TextCursor::MoveMode mode, int n)
+{
+ Q_UNUSED(n);
+ switch (op) {
+ case NoMove:
+ return true;
+ case Start:
+ m_position = 0;
+ break;
+ case PreviousCharacter:
+ while (--n >= 0) {
+ if (m_position == 0)
+ return false;
+ --m_position;
+ }
+ break;
+ case End:
+ m_position = m_document->characterCount();
+ break;
+ case NextCharacter:
+ while (--n >= 0) {
+ if (m_position == m_document->characterCount())
+ return false;
+ ++m_position;
+ }
+ break;
+ }
+
+ if (mode == MoveAnchor)
+ m_anchor = m_position;
+
+ return false;
+}
+
+int TextCursor::position() const
+{
+ return m_position;
+}
+
+void TextCursor::setPosition(int pos, Utils::TextCursor::MoveMode mode)
+{
+ m_position = pos;
+ if (mode == MoveAnchor)
+ m_anchor = pos;
+}
+
+QString TextCursor::selectedText() const
+{
+ return m_document->toPlainText().mid(qMin(m_position, m_anchor), qAbs(m_position - m_anchor));
+}
+
+void TextCursor::clearSelection()
+{
+ m_anchor = m_position;
+}
+
+TextDocument *TextCursor::document() const
+{
+ return m_document;
+}
+
+void TextCursor::insertText(const QString &text)
+{
+ const QString orig = m_document->toPlainText();
+ const QString left = orig.left(qMin(m_position, m_anchor));
+ const QString right = orig.mid(qMax(m_position, m_anchor));
+ m_document->setPlainText(left + text + right);
+}
+
+TextBlock TextCursor::block() const
+{
+ TextBlock current = m_document->firstBlock();
+ while (current.isValid()) {
+ if (current.position() <= position()
+ && current.position() + current.length() > current.position())
+ break;
+ current = current.next();
+ }
+ return current;
+}
+
+int TextCursor::positionInBlock() const
+{
+ return m_position - block().position();
+}
+
+int TextCursor::blockNumber() const
+{
+ return block().blockNumber();
+}
+
+void TextCursor::removeSelectedText()
+{
+ insertText(QString());
+}
+
+int TextCursor::selectionEnd() const
+{
+ return qMax(m_position, m_anchor);
+}
+
+bool TextCursor::isNull() const
+{
+ return m_document == nullptr;
+}
+
+} // namespace Utils
diff --git a/src/qmlls/qtextcursor_p.h b/src/qmlls/qtextcursor_p.h
new file mode 100644
index 0000000000..275acb93ec
--- /dev/null
+++ b/src/qmlls/qtextcursor_p.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef TEXTCURSOR_H
+#define TEXTCURSOR_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+
+namespace Utils {
+
+class TextDocument;
+class TextBlock;
+
+class TextCursor
+{
+public:
+ enum MoveOperation {
+ NoMove,
+ Start,
+ PreviousCharacter,
+ End,
+ NextCharacter,
+ };
+
+ enum MoveMode { MoveAnchor, KeepAnchor };
+
+ enum SelectionType { Document };
+
+ TextCursor();
+ TextCursor(const TextBlock &block);
+ TextCursor(TextDocument *document);
+
+ bool movePosition(MoveOperation op, MoveMode = MoveAnchor, int n = 1);
+ int position() const;
+ void setPosition(int pos, MoveMode mode = MoveAnchor);
+ QString selectedText() const;
+ void clearSelection();
+ int anchor() const;
+ TextDocument *document() const;
+ void insertText(const QString &text);
+ TextBlock block() const;
+ int positionInBlock() const;
+ int blockNumber() const;
+
+ void select(SelectionType selection);
+
+ bool hasSelection() const;
+
+ void removeSelectedText();
+ int selectionEnd() const;
+
+ bool isNull() const;
+
+private:
+ TextDocument *m_document = nullptr;
+ int m_position = 0;
+ int m_anchor = 0;
+};
+} // namespace Utils
+
+#endif // TEXTCURSOR_H
diff --git a/src/qmlls/qtextdocument.cpp b/src/qmlls/qtextdocument.cpp
new file mode 100644
index 0000000000..54e200274e
--- /dev/null
+++ b/src/qmlls/qtextdocument.cpp
@@ -0,0 +1,124 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtextdocument_p.h"
+#include "qtextblock_p.h"
+
+namespace Utils {
+
+TextDocument::TextDocument(const QString &text)
+{
+ setPlainText(text);
+}
+
+TextBlock TextDocument::findBlockByNumber(int blockNumber) const
+{
+ return (blockNumber >= 0 && blockNumber < m_blocks.size())
+ ? m_blocks.at(blockNumber).textBlock
+ : TextBlock();
+}
+
+TextBlock TextDocument::findBlockByLineNumber(int lineNumber) const
+{
+ return findBlockByNumber(lineNumber);
+}
+
+QChar TextDocument::characterAt(int pos) const
+{
+ return m_content.at(pos);
+}
+
+int TextDocument::characterCount() const
+{
+ return m_content.size();
+}
+
+TextBlock TextDocument::begin() const
+{
+ return m_blocks.isEmpty() ? TextBlock() : m_blocks.at(0).textBlock;
+}
+
+TextBlock TextDocument::firstBlock() const
+{
+ return begin();
+}
+
+TextBlock TextDocument::lastBlock() const
+{
+ return m_blocks.isEmpty() ? TextBlock() : m_blocks.last().textBlock;
+}
+
+std::optional<int> TextDocument::version() const
+{
+ return m_version;
+}
+
+void TextDocument::setVersion(std::optional<int> v)
+{
+ m_version = v;
+}
+
+QString TextDocument::toPlainText() const
+{
+ return m_content;
+}
+
+void TextDocument::setPlainText(const QString &text)
+{
+ m_content = text;
+ m_blocks.clear();
+
+ const auto appendToBlocks = [this](int blockNumber, int start, int length) {
+ Block block;
+ block.textBlock.setBlockNumber(blockNumber);
+ block.textBlock.setPosition(start);
+ block.textBlock.setDocument(this);
+ block.textBlock.setLength(length);
+ m_blocks.append(block);
+ };
+
+ int blockStart = 0;
+ int blockNumber = -1;
+ while (blockStart < text.size()) {
+ int blockEnd = text.indexOf(u'\n', blockStart) + 1;
+ if (blockEnd == 0)
+ blockEnd = text.size();
+ appendToBlocks(++blockNumber, blockStart, blockEnd - blockStart);
+ blockStart = blockEnd;
+ }
+ // Add an empty block if the text ends with \n. This is required for retrieving
+ // the actual line of the text editor if requested, for example, in findBlockByNumber.
+ // Consider a case with text aa\nbb\n\n. You are on 4th line of the text editor and even
+ // if it is an empty line, we introduce a text block for it to maybe use later.
+ if (text.endsWith(u'\n'))
+ appendToBlocks(++blockNumber, blockStart, 0);
+}
+
+bool TextDocument::isModified() const
+{
+ return m_modified;
+}
+
+void TextDocument::setModified(bool modified)
+{
+ m_modified = modified;
+}
+
+void TextDocument::setUserState(int blockNumber, int state)
+{
+ if (blockNumber >= 0 && blockNumber < m_blocks.size())
+ m_blocks[blockNumber].userState = state;
+}
+
+int TextDocument::userState(int blockNumber) const
+{
+ return (blockNumber >= 0 && blockNumber < m_blocks.size()) ? m_blocks[blockNumber].userState
+ : -1;
+}
+
+QMutex *TextDocument::mutex() const
+{
+ return &m_mutex;
+}
+
+} // namespace Utils
diff --git a/src/qmlls/qtextdocument_p.h b/src/qmlls/qtextdocument_p.h
new file mode 100644
index 0000000000..4df516e54a
--- /dev/null
+++ b/src/qmlls/qtextdocument_p.h
@@ -0,0 +1,78 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTEXTDOCUMENT_P_H
+#define QTEXTDOCUMENT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtextblock_p.h"
+
+#include <QtCore/qchar.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qmutex.h>
+
+#include <optional>
+
+namespace Utils {
+
+class TextBlockUserData;
+
+class TextDocument
+{
+public:
+ TextDocument() = default;
+ explicit TextDocument(const QString &text);
+
+ TextBlock findBlockByNumber(int blockNumber) const;
+ TextBlock findBlockByLineNumber(int lineNumber) const;
+ QChar characterAt(int pos) const;
+ int characterCount() const;
+ TextBlock begin() const;
+ TextBlock firstBlock() const;
+ TextBlock lastBlock() const;
+
+ std::optional<int> version() const;
+ void setVersion(std::optional<int>);
+
+ QString toPlainText() const;
+ void setPlainText(const QString &text);
+
+ bool isModified() const;
+ void setModified(bool modified);
+
+ void setUndoRedoEnabled(bool enable);
+
+ void clear();
+
+ void setUserState(int blockNumber, int state);
+ int userState(int blockNumber) const;
+ QMutex *mutex() const;
+
+private:
+ struct Block
+ {
+ TextBlock textBlock;
+ int userState = -1;
+ };
+
+ QVector<Block> m_blocks;
+
+ QString m_content;
+ bool m_modified = false;
+ std::optional<int> m_version;
+ mutable QMutex m_mutex;
+};
+} // namespace Utils
+
+#endif // TEXTDOCUMENT_P_H
diff --git a/src/qmlls/qtextsynchronization.cpp b/src/qmlls/qtextsynchronization.cpp
new file mode 100644
index 0000000000..5a1e39e855
--- /dev/null
+++ b/src/qmlls/qtextsynchronization.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtextsynchronization_p.h"
+#include "qqmllsutils_p.h"
+#include "qtextdocument_p.h"
+
+using namespace QLspSpecification;
+using namespace Qt::StringLiterals;
+
+QT_BEGIN_NAMESPACE
+
+TextSynchronization::TextSynchronization(QmlLsp::QQmlCodeModel *codeModel, QObject *parent)
+ : QLanguageServerModule(parent), m_codeModel(codeModel)
+{
+}
+
+void TextSynchronization::didCloseTextDocument(const DidCloseTextDocumentParams &params)
+{
+ m_codeModel->closeOpenFile(QQmlLSUtils::lspUriToQmlUrl(params.textDocument.uri));
+}
+
+void TextSynchronization::didOpenTextDocument(const DidOpenTextDocumentParams &params)
+{
+ const TextDocumentItem &item = params.textDocument;
+ const QString fileName = m_codeModel->url2Path(QQmlLSUtils::lspUriToQmlUrl(item.uri));
+ m_codeModel->newOpenFile(QQmlLSUtils::lspUriToQmlUrl(item.uri), item.version,
+ QString::fromUtf8(item.text));
+}
+
+void TextSynchronization::didDidChangeTextDocument(const DidChangeTextDocumentParams &params)
+{
+ QByteArray url = QQmlLSUtils::lspUriToQmlUrl(params.textDocument.uri);
+ const QString fileName = m_codeModel->url2Path(url);
+ auto openDoc = m_codeModel->openDocumentByUrl(url);
+ std::shared_ptr<Utils::TextDocument> document = openDoc.textDocument;
+ if (!document) {
+ qCWarning(lspServerLog) << "Ingnoring changes to non open or closed document"
+ << QString::fromUtf8(url);
+ return;
+ }
+ const auto &changes = params.contentChanges;
+ {
+ QMutexLocker l(document->mutex());
+ for (const auto &change : changes) {
+ if (!change.range) {
+ document->setPlainText(QString::fromUtf8(change.text));
+ continue;
+ }
+
+ const auto &range = *change.range;
+ const auto &rangeStart = range.start;
+ const int start =
+ document->findBlockByNumber(rangeStart.line).position() + rangeStart.character;
+ const auto &rangeEnd = range.end;
+ const int end =
+ document->findBlockByNumber(rangeEnd.line).position() + rangeEnd.character;
+
+ document->setPlainText(document->toPlainText().replace(start, end - start,
+ QString::fromUtf8(change.text)));
+ }
+ document->setVersion(params.textDocument.version);
+ qCDebug(lspServerLog).noquote()
+ << "text is\n:----------" << document->toPlainText() << "\n_________";
+ }
+ m_codeModel->addOpenToUpdate(url);
+ m_codeModel->openNeedUpdate();
+}
+
+void TextSynchronization::registerHandlers(QLanguageServer *server, QLanguageServerProtocol *)
+{
+ QObject::connect(server->notifySignals(),
+ &QLspNotifySignals::receivedDidOpenTextDocumentNotification, this,
+ &TextSynchronization::didOpenTextDocument);
+
+ QObject::connect(server->notifySignals(),
+ &QLspNotifySignals::receivedDidChangeTextDocumentNotification, this,
+ &TextSynchronization::didDidChangeTextDocument);
+
+ QObject::connect(server->notifySignals(),
+ &QLspNotifySignals::receivedDidCloseTextDocumentNotification, this,
+ &TextSynchronization::didCloseTextDocument);
+}
+
+QString TextSynchronization::name() const
+{
+ return u"TextSynchonization"_s;
+}
+
+void TextSynchronization::setupCapabilities(const QLspSpecification::InitializeParams &,
+ QLspSpecification::InitializeResult &serverInfo)
+{
+ TextDocumentSyncOptions syncOptions;
+ syncOptions.openClose = true;
+ syncOptions.change = TextDocumentSyncKind::Incremental;
+ serverInfo.capabilities.textDocumentSync = syncOptions;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qtextsynchronization_p.h b/src/qmlls/qtextsynchronization_p.h
new file mode 100644
index 0000000000..2c26b84aea
--- /dev/null
+++ b/src/qmlls/qtextsynchronization_p.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTEXTSYNCHRONIZATION_P_H
+#define QTEXTSYNCHRONIZATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmlcodemodel_p.h"
+#include "qlanguageserver_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class TextSynchronization : public QLanguageServerModule
+{
+ Q_OBJECT
+public:
+ TextSynchronization(QmlLsp::QQmlCodeModel *codeModel, QObject *parent = nullptr);
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+
+public Q_SLOTS:
+ void didOpenTextDocument(const QLspSpecification::DidOpenTextDocumentParams &params);
+ void didDidChangeTextDocument(const QLspSpecification::DidChangeTextDocumentParams &params);
+ void didCloseTextDocument(const QLspSpecification::DidCloseTextDocumentParams &params);
+
+private:
+ QmlLsp::QQmlCodeModel *m_codeModel;
+};
+
+QT_END_NAMESPACE
+#endif // QTEXTSYNCHRONIZATION_P_H
diff --git a/src/qmlls/qworkspace.cpp b/src/qmlls/qworkspace.cpp
new file mode 100644
index 0000000000..ea8f2d6005
--- /dev/null
+++ b/src/qmlls/qworkspace.cpp
@@ -0,0 +1,172 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qworkspace_p.h"
+#include "qqmllanguageserver_p.h"
+#include "qqmllsutils_p.h"
+
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+#include <QtLanguageServer/private/qlspnotifysignals_p.h>
+
+#include <QtCore/qfile.h>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace QLspSpecification;
+
+void WorkspaceHandlers::registerHandlers(QLanguageServer *server, QLanguageServerProtocol *)
+{
+ QObject::connect(server->notifySignals(),
+ &QLspNotifySignals::receivedDidChangeWorkspaceFoldersNotification, this,
+ [server, this](const DidChangeWorkspaceFoldersParams &params) {
+ const WorkspaceFoldersChangeEvent &event = params.event;
+
+ const QList<WorkspaceFolder> &removed = event.removed;
+ QList<QByteArray> toRemove;
+ for (const WorkspaceFolder &folder : removed) {
+ toRemove.append(QQmlLSUtils::lspUriToQmlUrl(folder.uri));
+ m_codeModel->removeDirectory(m_codeModel->url2Path(
+ QQmlLSUtils::lspUriToQmlUrl(folder.uri)));
+ }
+ m_codeModel->removeRootUrls(toRemove);
+ const QList<WorkspaceFolder> &added = event.added;
+ QList<QByteArray> toAdd;
+ QStringList pathsToAdd;
+ for (const WorkspaceFolder &folder : added) {
+ toAdd.append(QQmlLSUtils::lspUriToQmlUrl(folder.uri));
+ pathsToAdd.append(m_codeModel->url2Path(
+ QQmlLSUtils::lspUriToQmlUrl(folder.uri)));
+ }
+ m_codeModel->addRootUrls(toAdd);
+ m_codeModel->addDirectoriesToIndex(pathsToAdd, server);
+ });
+
+ QObject::connect(server->notifySignals(),
+ &QLspNotifySignals::receivedDidChangeWatchedFilesNotification, this,
+ [this](const DidChangeWatchedFilesParams &params) {
+ const QList<FileEvent> &changes = params.changes;
+ for (const FileEvent &change : changes) {
+ const QString filename =
+ m_codeModel->url2Path(QQmlLSUtils::lspUriToQmlUrl(change.uri));
+ switch (FileChangeType(change.type)) {
+ case FileChangeType::Created:
+ // m_codeModel->addFile(filename);
+ break;
+ case FileChangeType::Changed: {
+ QFile file(filename);
+ if (file.open(QIODevice::ReadOnly))
+ // m_modelManager->setFileContents(filename, file.readAll());
+ break;
+ break;
+ }
+ case FileChangeType::Deleted:
+ // m_modelManager->removeFile(filename);
+ break;
+ }
+ }
+ // update due to dep changes...
+ });
+
+ QObject::connect(server, &QLanguageServer::clientInitialized, this,
+ &WorkspaceHandlers::clientInitialized);
+}
+
+QString WorkspaceHandlers::name() const
+{
+ return u"Workspace"_s;
+}
+
+void WorkspaceHandlers::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &serverInfo)
+{
+ if (!clientInfo.capabilities.workspace
+ || !clientInfo.capabilities.workspace->value(u"workspaceFolders"_s).toBool(false))
+ return;
+ WorkspaceFoldersServerCapabilities folders;
+ folders.supported = true;
+ folders.changeNotifications = true;
+ if (!serverInfo.capabilities.workspace)
+ serverInfo.capabilities.workspace = QJsonObject();
+ serverInfo.capabilities.workspace->insert(u"workspaceFolders"_s,
+ QTypedJson::toJsonValue(folders));
+}
+
+void WorkspaceHandlers::clientInitialized(QLanguageServer *server)
+{
+ QLanguageServerProtocol *protocol = server->protocol();
+ const auto clientInfo = server->clientInfo();
+ QList<Registration> registrations;
+ if (clientInfo.capabilities.workspace
+ && clientInfo.capabilities.workspace
+ ->value(u"didChangeWatchedFiles"_s)[u"dynamicRegistration"_s]
+ .toBool(false)) {
+ const int watchAll =
+ int(WatchKind::Create) | int(WatchKind::Change) | int(WatchKind::Delete);
+ DidChangeWatchedFilesRegistrationOptions watchedFilesParams;
+ FileSystemWatcher qmlWatcher;
+ qmlWatcher.globPattern = QByteArray("*.{qml,js,mjs}");
+ qmlWatcher.kind = watchAll;
+ FileSystemWatcher qmldirWatcher;
+ qmldirWatcher.globPattern = "qmldir";
+ qmldirWatcher.kind = watchAll;
+ FileSystemWatcher qmltypesWatcher;
+ qmltypesWatcher.globPattern = QByteArray("*.qmltypes");
+ qmltypesWatcher.kind = watchAll;
+ watchedFilesParams.watchers = QList<FileSystemWatcher>({
+ std::move(qmlWatcher),
+ std::move(qmldirWatcher),
+ std::move(qmltypesWatcher)
+ });
+ registrations.append(Registration {
+ // use ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles as id too
+ ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles,
+ ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles,
+ QTypedJson::toJsonValue(watchedFilesParams) });
+ }
+
+ if (!registrations.isEmpty()) {
+ RegistrationParams params;
+ params.registrations = registrations;
+ protocol->requestRegistration(
+ params,
+ []() {
+ // successful registration
+ },
+ [protocol](const ResponseError &err) {
+ LogMessageParams msg;
+ msg.message = QByteArray("registration of file udates failed, will miss file "
+ "changes done outside the editor due to error ");
+ msg.message.append(QString::number(err.code).toUtf8());
+ if (!err.message.isEmpty())
+ msg.message.append(" ");
+ msg.message.append(err.message);
+ msg.type = MessageType::Warning;
+ qCWarning(lspServerLog) << QString::fromUtf8(msg.message);
+ protocol->notifyLogMessage(msg);
+ });
+ }
+
+ QSet<QString> rootPaths;
+ if (std::holds_alternative<QByteArray>(clientInfo.rootUri)) {
+ QString path = m_codeModel->url2Path(
+ QQmlLSUtils::lspUriToQmlUrl(std::get<QByteArray>(clientInfo.rootUri)));
+ rootPaths.insert(path);
+ } else if (clientInfo.rootPath && std::holds_alternative<QByteArray>(*clientInfo.rootPath)) {
+ QString path = QString::fromUtf8(std::get<QByteArray>(*clientInfo.rootPath));
+ rootPaths.insert(path);
+ }
+
+ if (clientInfo.workspaceFolders
+ && std::holds_alternative<QList<WorkspaceFolder>>(*clientInfo.workspaceFolders)) {
+ for (const WorkspaceFolder &workspace :
+ std::as_const(std::get<QList<WorkspaceFolder>>(*clientInfo.workspaceFolders))) {
+ const QUrl workspaceUrl(QString::fromUtf8(QQmlLSUtils::lspUriToQmlUrl(workspace.uri)));
+ rootPaths.insert(workspaceUrl.toLocalFile());
+ }
+ }
+ if (m_status == Status::Indexing)
+ m_codeModel->addDirectoriesToIndex(QStringList(rootPaths.begin(), rootPaths.end()), server);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlls/qworkspace_p.h b/src/qmlls/qworkspace_p.h
new file mode 100644
index 0000000000..b703249c9b
--- /dev/null
+++ b/src/qmlls/qworkspace_p.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWORKSPACE_P_H
+#define QWORKSPACE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmlcodemodel_p.h"
+#include "qlanguageserver_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class WorkspaceHandlers : public QLanguageServerModule
+{
+ Q_OBJECT
+public:
+ enum class Status { NoIndex, Indexing };
+ WorkspaceHandlers(QmlLsp::QQmlCodeModel *codeModel) : m_codeModel(codeModel) { }
+ QString name() const override;
+ void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
+ void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
+ QLspSpecification::InitializeResult &) override;
+public Q_SLOTS:
+ void clientInitialized(QLanguageServer *);
+
+private:
+ QmlLsp::QQmlCodeModel *m_codeModel = nullptr;
+ Status m_status = Status::NoIndex;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWORKSPACE_P_H
diff --git a/src/qmlmodels/CMakeLists.txt b/src/qmlmodels/CMakeLists.txt
index b3875a8091..18ae5ceeff 100644
--- a/src/qmlmodels/CMakeLists.txt
+++ b/src/qmlmodels/CMakeLists.txt
@@ -31,8 +31,7 @@ qt_internal_add_qml_module(QmlModels
Qt::CorePrivate
Qt::QmlPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
qt_internal_extend_target(QmlModels CONDITION QT_FEATURE_qml_itemmodel
SOURCES
@@ -63,8 +62,12 @@ qt_internal_extend_target(QmlModels CONDITION QT_FEATURE_qml_delegate_model
SOURCES
qqmlabstractdelegatecomponent.cpp qqmlabstractdelegatecomponent_p.h
qqmladaptormodel.cpp qqmladaptormodel_p.h
+ qqmladaptormodelenginedata.cpp qqmladaptormodelenginedata_p.h
qqmldelegatemodel.cpp qqmldelegatemodel_p.h
qqmldelegatemodel_p_p.h
+ qqmldmabstractitemmodeldata.cpp qqmldmabstractitemmodeldata_p.h
+ qqmldmlistaccessordata.cpp qqmldmlistaccessordata_p.h
+ qqmldmobjectdata.cpp qqmldmobjectdata_p.h
qqmllistaccessor.cpp qqmllistaccessor_p.h
qqmllistcompositor.cpp qqmllistcompositor_p.h
qquickpackage.cpp qquickpackage_p.h
diff --git a/src/qmlmodels/doc/qtqmlmodels.qdocconf b/src/qmlmodels/doc/qtqmlmodels.qdocconf
index 35910b7c71..5c9c04a85a 100644
--- a/src/qmlmodels/doc/qtqmlmodels.qdocconf
+++ b/src/qmlmodels/doc/qtqmlmodels.qdocconf
@@ -35,5 +35,5 @@ navigation.qmltypespage = "Qt Qml Models QML Types"
# suppress qdoc warnings for \instantiates entries
spurious += "C\\+\\+ class .*\\\\instantiates .*"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel.qml b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel.qml
index a2f12d5567..51bba64534 100644
--- a/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel.qml
+++ b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
-import QtQml.Models 2.2
+import QtQuick
+import QtQml.Models
Rectangle {
width: 200; height: 100
diff --git a/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml
index b124bb7b2a..01abaf1909 100644
--- a/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml
+++ b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
-import QtQml.Models 2.2
+import QtQuick
+import QtQml.Models
ListView {
id: view
diff --git a/src/qmlmodels/doc/snippets/delegatemodel/delegatemodelgroup.qml b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodelgroup.qml
index 0325d7f7f3..1f55f0d193 100644
--- a/src/qmlmodels/doc/snippets/delegatemodel/delegatemodelgroup.qml
+++ b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodelgroup.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
-import QtQml.Models 2.2
+import QtQuick
+import QtQml.Models
Rectangle {
width: 200; height: 100
diff --git a/src/qmlmodels/doc/snippets/package/Delegate.qml b/src/qmlmodels/doc/snippets/package/Delegate.qml
index 48edb90125..d671c15b26 100644
--- a/src/qmlmodels/doc/snippets/package/Delegate.qml
+++ b/src/qmlmodels/doc/snippets/package/Delegate.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [0]
Package {
diff --git a/src/qmlmodels/doc/snippets/package/view.qml b/src/qmlmodels/doc/snippets/package/view.qml
index e0c43dad55..4df31a8498 100644
--- a/src/qmlmodels/doc/snippets/package/view.qml
+++ b/src/qmlmodels/doc/snippets/package/view.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
-import QtQml.Models 2.1
+import QtQuick
+import QtQml.Models
Rectangle {
id: root
diff --git a/src/qmlmodels/doc/snippets/qml/listmodel/WorkerScript.qml b/src/qmlmodels/doc/snippets/qml/listmodel/WorkerScript.qml
new file mode 100644
index 0000000000..4799878375
--- /dev/null
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/WorkerScript.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ color: "white"
+ width: 200
+ height: 300
+
+ ListView {
+ anchors.fill: parent
+ model: listModel
+ delegate: Component {
+ Text {
+ required property string time
+ text: time
+ }
+ }
+
+ ListModel { id: listModel }
+
+ WorkerScript {
+ id: worker
+ source: "dataloader.mjs"
+ }
+
+// ![0]
+ Timer {
+ id: timer
+ interval: 2000; repeat: true
+ running: true
+ triggeredOnStart: true
+
+ onTriggered: {
+ var msg = {'action': 'appendCurrentTime', 'model': listModel};
+ worker.sendMessage(msg);
+ }
+ }
+// ![0]
+ }
+}
+
diff --git a/src/qmlmodels/doc/snippets/qml/listmodel/dataloader.mjs b/src/qmlmodels/doc/snippets/qml/listmodel/dataloader.mjs
new file mode 100644
index 0000000000..eed1bca6f1
--- /dev/null
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/dataloader.mjs
@@ -0,0 +1,13 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+// ![0]
+WorkerScript.onMessage = function(msg) {
+ if (msg.action == 'appendCurrentTime') {
+ var data = {'time': new Date().toTimeString()};
+ msg.model.append(data);
+ msg.model.sync(); // updates the changes to the list
+ }
+}
+// ![0]
+
diff --git a/src/qmlmodels/doc/snippets/qml/listmodel/listelements.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listelements.qml
index bab4aec6a2..e2a1525534 100644
--- a/src/qmlmodels/doc/snippets/qml/listmodel/listelements.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listelements.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 250
diff --git a/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-modify.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-modify.qml
index 3178abfcfd..99d6a3adc7 100644
--- a/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-modify.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-modify.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 200; height: 200
diff --git a/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-nested.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-nested.qml
index 15a5fc858f..8018f7b69f 100644
--- a/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-nested.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-nested.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 200; height: 200
diff --git a/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-simple.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-simple.qml
index 2640a38f13..8ba0a6d182 100644
--- a/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-simple.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-simple.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 200; height: 200
diff --git a/src/qmlmodels/doc/snippets/qml/listmodel/listmodel.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel.qml
index 05deec06b6..3eefc4b9a5 100644
--- a/src/qmlmodels/doc/snippets/qml/listmodel/listmodel.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
ListModel {
id: fruitModel
diff --git a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml
index ab44bf5696..70ce56f599 100644
--- a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml
+++ b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![file]
-import QtQuick 2.12
-import QtQuick.Window 2.12
-import Qt.labs.qmlmodels 1.0
+import QtQuick
+import QtQuick.Window
+import Qt.labs.qmlmodels
Window {
width: 400
diff --git a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
index 45c6f1818d..5241317c4d 100644
--- a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
+++ b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![file]
-import QtQuick 2.12
-import QtQuick.Controls 2.5
-import Qt.labs.qmlmodels 1.0
+import QtQuick
+import QtQuick.Controls
+import Qt.labs.qmlmodels
ApplicationWindow {
width: 400
diff --git a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
index e00ae6bfc0..0c9622c440 100644
--- a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
+++ b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![file]
-import QtQuick 2.12
-import QtQuick.Window 2.12
-import Qt.labs.qmlmodels 1.0
+import QtQuick
+import QtQuick.Window
+import Qt.labs.qmlmodels
Window {
width: 400
diff --git a/src/qmlmodels/doc/src/qtqmlmodel.qdoc b/src/qmlmodels/doc/src/qtqmlmodel.qdoc
index 321b3fd57b..f75530e93c 100644
--- a/src/qmlmodels/doc/src/qtqmlmodel.qdoc
+++ b/src/qmlmodels/doc/src/qtqmlmodel.qdoc
@@ -17,7 +17,7 @@
\endqml
\note QtQml.Models module started at version 2.1 to match the version
- of the parent module, \l{Qt QML}.
+ of the parent module, \l{Qt Qml}.
In addition, Qt.labs.qmlmodels provides experimental QML types for models.
To use these experimental types, import the module with the following line:
diff --git a/src/qmlmodels/qqmlabstractdelegatecomponent_p.h b/src/qmlmodels/qqmlabstractdelegatecomponent_p.h
index 91339616f7..ae4811dd9c 100644
--- a/src/qmlmodels/qqmlabstractdelegatecomponent_p.h
+++ b/src/qmlmodels/qqmlabstractdelegatecomponent_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
// TODO: consider making QQmlAbstractDelegateComponent public API
class QQmlAdaptorModel;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent
+class Q_QMLMODELS_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent
{
Q_OBJECT
QML_NAMED_ELEMENT(AbstractDelegateComponent)
@@ -37,6 +37,7 @@ public:
~QQmlAbstractDelegateComponent() override;
virtual QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = 0) const = 0;
+ virtual QString role() const = 0;
Q_SIGNALS:
void delegateChanged();
diff --git a/src/qmlmodels/qqmladaptormodel.cpp b/src/qmlmodels/qqmladaptormodel.cpp
index dd08f91c61..f72a5e6b16 100644
--- a/src/qmlmodels/qqmladaptormodel.cpp
+++ b/src/qmlmodels/qqmladaptormodel.cpp
@@ -3,949 +3,19 @@
#include "qqmladaptormodel_p.h"
-#include <private/qqmldelegatemodel_p_p.h>
-#include <private/qmetaobjectbuilder_p.h>
-#include <private/qqmlproperty_p.h>
-
-#include <private/qv4value_p.h>
-#include <private/qv4functionobject_p.h>
+#include <private/qqmldmabstractitemmodeldata_p.h>
+#include <private/qqmldmlistaccessordata_p.h>
+#include <private/qqmldmobjectdata_p.h>
QT_BEGIN_NAMESPACE
-class QQmlAdaptorModelEngineData : public QV4::ExecutionEngine::Deletable
-{
-public:
- QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4);
- ~QQmlAdaptorModelEngineData();
-
- QV4::ExecutionEngine *v4;
- QV4::PersistentValue listItemProto;
-};
-
-V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData)
-
-static QV4::ReturnedValue get_index(const QV4::FunctionObject *f, const QV4::Value *thisObject, const QV4::Value *, int)
-{
- QV4::Scope scope(f);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
- if (!o)
- RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
-
- RETURN_RESULT(QV4::Encode(o->d()->item->index));
-}
-
-template <typename T, typename M> static void setModelDataType(QMetaObjectBuilder *builder, M *metaType)
-{
- builder->setFlags(MetaObjectFlag::DynamicMetaObject);
- builder->setClassName(T::staticMetaObject.className());
- builder->setSuperClass(&T::staticMetaObject);
- metaType->propertyOffset = T::staticMetaObject.propertyCount();
- metaType->signalOffset = T::staticMetaObject.methodCount();
-}
-
-static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType)
-{
- builder->addSignal("__" + QByteArray::number(propertyId) + "()");
- QMetaPropertyBuilder property = builder->addProperty(
- propertyName, propertyType, propertyId);
- property.setWritable(true);
-}
-
-class VDMModelDelegateDataType;
-
-class QQmlDMCachedModelData : public QQmlDelegateModelItem
-{
-public:
- QQmlDMCachedModelData(
- const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- VDMModelDelegateDataType *dataType,
- int index, int row, int column);
-
- int metaCall(QMetaObject::Call call, int id, void **arguments);
-
- virtual QVariant value(int role) const = 0;
- virtual void setValue(int role, const QVariant &value) = 0;
-
- void setValue(const QString &role, const QVariant &value) override;
- bool resolveIndex(const QQmlAdaptorModel &model, int idx) override;
-
- static QV4::ReturnedValue get_property(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
- static QV4::ReturnedValue set_property(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
-
- VDMModelDelegateDataType *type;
- QVector<QVariant> cachedData;
-};
-
-class VDMModelDelegateDataType
- : public QQmlRefCount
- , public QQmlAdaptorModel::Accessors
- , public QAbstractDynamicMetaObject
-{
-public:
- VDMModelDelegateDataType(QQmlAdaptorModel *model)
- : model(model)
- , propertyOffset(0)
- , signalOffset(0)
- , hasModelData(false)
- {
- }
-
- bool notify(
- const QQmlAdaptorModel &,
- const QList<QQmlDelegateModelItem *> &items,
- int index,
- int count,
- const QVector<int> &roles) const override
- {
- bool changed = roles.isEmpty() && !watchedRoles.isEmpty();
- if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) {
- QList<int> roleIds;
- for (const QByteArray &r : watchedRoles) {
- QHash<QByteArray, int>::const_iterator it = roleNames.find(r);
- if (it != roleNames.end())
- roleIds << it.value();
- }
- const_cast<VDMModelDelegateDataType *>(this)->watchedRoleIds = roleIds;
- }
-
- QVector<int> signalIndexes;
- for (int i = 0; i < roles.size(); ++i) {
- const int role = roles.at(i);
- if (!changed && watchedRoleIds.contains(role))
- changed = true;
-
- int propertyId = propertyRoles.indexOf(role);
- if (propertyId != -1)
- signalIndexes.append(propertyId + signalOffset);
- }
- if (roles.isEmpty()) {
- const int propertyRolesCount = propertyRoles.size();
- signalIndexes.reserve(propertyRolesCount);
- for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId)
- signalIndexes.append(propertyId + signalOffset);
- }
-
- QVarLengthArray<QQmlGuard<QQmlDelegateModelItem>> guardedItems;
- for (const auto item : items)
- guardedItems.append(item);
-
- for (const auto &item : std::as_const(guardedItems)) {
- if (item.isNull())
- continue;
-
- const int idx = item->modelIndex();
- if (idx >= index && idx < index + count) {
- for (int i = 0; i < signalIndexes.size(); ++i)
- QMetaObject::activate(item, signalIndexes.at(i), nullptr);
- }
- }
- return changed;
- }
-
- void replaceWatchedRoles(
- QQmlAdaptorModel &,
- const QList<QByteArray> &oldRoles,
- const QList<QByteArray> &newRoles) const override
- {
- VDMModelDelegateDataType *dataType = const_cast<VDMModelDelegateDataType *>(this);
-
- dataType->watchedRoleIds.clear();
- for (const QByteArray &oldRole : oldRoles)
- dataType->watchedRoles.removeOne(oldRole);
- dataType->watchedRoles += newRoles;
- }
-
- static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
- {
- QV4::Scope scope(b);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
- if (!o)
- RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
-
- const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(o->d()->item)->type->model;
- if (o->d()->item->index >= 0) {
- if (const QAbstractItemModel *const aim = model->aim())
- RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))));
- }
- RETURN_RESULT(QV4::Encode(false));
- }
-
-
- void initializeConstructor(QQmlAdaptorModelEngineData *const data)
- {
- QV4::ExecutionEngine *v4 = data->v4;
- QV4::Scope scope(v4);
- QV4::ScopedObject proto(scope, v4->newObject());
- proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr);
- proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), get_hasModelChildren, nullptr);
- QV4::ScopedProperty p(scope);
-
- typedef QHash<QByteArray, int>::const_iterator iterator;
- for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) {
- const int propertyId = propertyRoles.indexOf(it.value());
- const QByteArray &propertyName = it.key();
-
- QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName)));
- QV4::ExecutionContext *global = v4->rootContext();
- QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property));
- QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property));
- p->setGetter(g);
- p->setSetter(s);
- proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
- }
- prototype.set(v4, proto);
- }
-
- // QAbstractDynamicMetaObject
-
- void objectDestroyed(QObject *) override
- {
- release();
- }
-
- int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override
- {
- return static_cast<QQmlDMCachedModelData *>(object)->metaCall(call, id, arguments);
- }
-
- QV4::PersistentValue prototype;
- QList<int> propertyRoles;
- QList<int> watchedRoleIds;
- QList<QByteArray> watchedRoles;
- QHash<QByteArray, int> roleNames;
- QQmlAdaptorModel *model;
- int propertyOffset;
- int signalOffset;
- bool hasModelData;
-};
-
-QQmlDMCachedModelData::QQmlDMCachedModelData(
- const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- VDMModelDelegateDataType *dataType, int index, int row, int column)
- : QQmlDelegateModelItem(metaType, dataType, index, row, column)
- , type(dataType)
-{
- if (index == -1)
- cachedData.resize(type->hasModelData ? 1 : type->propertyRoles.size());
-
- QObjectPrivate::get(this)->metaObject = type;
-
- type->addref();
-}
-
-int QQmlDMCachedModelData::metaCall(QMetaObject::Call call, int id, void **arguments)
-{
- if (call == QMetaObject::ReadProperty && id >= type->propertyOffset) {
- const int propertyIndex = id - type->propertyOffset;
- if (index == -1) {
- if (!cachedData.isEmpty()) {
- *static_cast<QVariant *>(arguments[0]) = cachedData.at(
- type->hasModelData ? 0 : propertyIndex);
- }
- } else if (*type->model) {
- *static_cast<QVariant *>(arguments[0]) = value(type->propertyRoles.at(propertyIndex));
- }
- return -1;
- } else if (call == QMetaObject::WriteProperty && id >= type->propertyOffset) {
- const int propertyIndex = id - type->propertyOffset;
- if (index == -1) {
- const QMetaObject *meta = metaObject();
- if (cachedData.size() > 1) {
- cachedData[propertyIndex] = *static_cast<QVariant *>(arguments[0]);
- QMetaObject::activate(this, meta, propertyIndex, nullptr);
- } else if (cachedData.size() == 1) {
- cachedData[0] = *static_cast<QVariant *>(arguments[0]);
- QMetaObject::activate(this, meta, 0, nullptr);
- QMetaObject::activate(this, meta, 1, nullptr);
- }
- } else if (*type->model) {
- setValue(type->propertyRoles.at(propertyIndex), *static_cast<QVariant *>(arguments[0]));
- }
- return -1;
- } else {
- return qt_metacall(call, id, arguments);
- }
-}
-
-void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value)
-{
- QHash<QByteArray, int>::iterator it = type->roleNames.find(role.toUtf8());
- if (it != type->roleNames.end()) {
- for (int i = 0; i < type->propertyRoles.size(); ++i) {
- if (type->propertyRoles.at(i) == *it) {
- cachedData[i] = value;
- return;
- }
- }
- }
-}
-
-bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx)
-{
- if (index == -1) {
- Q_ASSERT(idx >= 0);
- cachedData.clear();
- setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx));
- const QMetaObject *meta = metaObject();
- const int propertyCount = type->propertyRoles.size();
- for (int i = 0; i < propertyCount; ++i)
- QMetaObject::activate(this, meta, i, nullptr);
- return true;
- } else {
- return false;
- }
-}
-
-QV4::ReturnedValue QQmlDMCachedModelData::get_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
-{
- QV4::Scope scope(b);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
- if (!o)
- return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
-
- uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
-
- QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item);
- if (o->d()->item->index == -1) {
- if (!modelData->cachedData.isEmpty()) {
- return scope.engine->fromVariant(
- modelData->cachedData.at(modelData->type->hasModelData ? 0 : propertyId));
- }
- } else if (*modelData->type->model) {
- return scope.engine->fromVariant(
- modelData->value(modelData->type->propertyRoles.at(propertyId)));
- }
- return QV4::Encode::undefined();
-}
-
-QV4::ReturnedValue QQmlDMCachedModelData::set_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
-{
- QV4::Scope scope(b);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
- if (!o)
- return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
- if (!argc)
- return scope.engine->throwTypeError();
-
- uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
-
- if (o->d()->item->index == -1) {
- QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item);
- if (!modelData->cachedData.isEmpty()) {
- if (modelData->cachedData.size() > 1) {
- modelData->cachedData[propertyId]
- = QV4::ExecutionEngine::toVariant(argv[0], QMetaType {});
- QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), propertyId, nullptr);
- } else if (modelData->cachedData.size() == 1) {
- modelData->cachedData[0] = QV4::ExecutionEngine::toVariant(argv[0], QMetaType {});
- QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 0, nullptr);
- QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 1, nullptr);
- }
- }
- }
- return QV4::Encode::undefined();
-}
-
-//-----------------------------------------------------------------
-// QAbstractItemModel
-//-----------------------------------------------------------------
-
-class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData
-{
- Q_OBJECT
- Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
-
-public:
- QQmlDMAbstractItemModelData(
- const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- VDMModelDelegateDataType *dataType,
- int index, int row, int column)
- : QQmlDMCachedModelData(metaType, dataType, index, row, column)
- {
- }
-
- bool hasModelChildren() const
- {
- if (index >= 0) {
- if (const QAbstractItemModel *const model = type->model->aim())
- return model->hasChildren(model->index(row, column, type->model->rootIndex));
- }
- return false;
- }
-
- QVariant value(int role) const override
- {
- if (const QAbstractItemModel *aim = type->model->aim())
- return aim->index(row, column, type->model->rootIndex).data(role);
- return QVariant();
- }
-
- void setValue(int role, const QVariant &value) override
- {
- if (QAbstractItemModel *aim = type->model->aim())
- aim->setData(aim->index(row, column, type->model->rootIndex), value, role);
- }
-
- QV4::ReturnedValue get() override
- {
- if (type->prototype.isUndefined()) {
- QQmlAdaptorModelEngineData * const data = engineData(v4);
- type->initializeConstructor(data);
- }
- QV4::Scope scope(v4);
- QV4::ScopedObject proto(scope, type->prototype.value());
- QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
- o->setPrototypeOf(proto);
- ++scriptRef;
- return o.asReturnedValue();
- }
-};
-
-class VDMAbstractItemModelDataType : public VDMModelDelegateDataType
-{
-public:
- VDMAbstractItemModelDataType(QQmlAdaptorModel *model)
- : VDMModelDelegateDataType(model)
- {
- }
-
- int rowCount(const QQmlAdaptorModel &model) const override
- {
- if (const QAbstractItemModel *aim = model.aim())
- return aim->rowCount(model.rootIndex);
- return 0;
- }
-
- int columnCount(const QQmlAdaptorModel &model) const override
- {
- if (const QAbstractItemModel *aim = model.aim())
- return aim->columnCount(model.rootIndex);
- return 0;
- }
-
- void cleanup(QQmlAdaptorModel &) const override
- {
- release();
- }
-
- QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
- {
- if (!metaObject) {
- VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
- dataType->initializeMetaType(model);
- }
-
- if (const QAbstractItemModel *aim = model.aim()) {
- QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8());
- if (it != roleNames.end()) {
- return aim->index(model.rowAt(index), model.columnAt(index),
- model.rootIndex).data(*it);
- } else if (role == QLatin1String("hasModelChildren")) {
- return QVariant(aim->hasChildren(aim->index(model.rowAt(index),
- model.columnAt(index),
- model.rootIndex)));
- }
- }
- return QVariant();
- }
-
- QVariant parentModelIndex(const QQmlAdaptorModel &model) const override
- {
- if (const QAbstractItemModel *aim = model.aim())
- return QVariant::fromValue(aim->parent(model.rootIndex));
- return QVariant();
- }
-
- QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
- {
- if (const QAbstractItemModel *aim = model.aim())
- return QVariant::fromValue(aim->index(model.rowAt(index), model.columnAt(index),
- model.rootIndex));
- return QVariant();
- }
-
- bool canFetchMore(const QQmlAdaptorModel &model) const override
- {
- if (const QAbstractItemModel *aim = model.aim())
- return aim->canFetchMore(model.rootIndex);
- return false;
- }
-
- void fetchMore(QQmlAdaptorModel &model) const override
- {
- if (QAbstractItemModel *aim = model.aim())
- aim->fetchMore(model.rootIndex);
- }
-
- QQmlDelegateModelItem *createItem(
- QQmlAdaptorModel &model,
- const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- int index, int row, int column) const override
- {
- VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
- if (!metaObject)
- dataType->initializeMetaType(model);
- return new QQmlDMAbstractItemModelData(metaType, dataType, index, row, column);
- }
-
- void initializeMetaType(const QQmlAdaptorModel &model)
- {
- QMetaObjectBuilder builder;
- setModelDataType<QQmlDMAbstractItemModelData>(&builder, this);
-
- const QByteArray propertyType = QByteArrayLiteral("QVariant");
- const QAbstractItemModel *aim = model.aim();
- const QHash<int, QByteArray> names = aim ? aim->roleNames() : QHash<int, QByteArray>();
- for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) {
- const int propertyId = propertyRoles.size();
- propertyRoles.append(it.key());
- roleNames.insert(it.value(), it.key());
- addProperty(&builder, propertyId, it.value(), propertyType);
- }
- if (propertyRoles.size() == 1) {
- hasModelData = true;
- const int role = names.begin().key();
- const QByteArray propertyName = QByteArrayLiteral("modelData");
-
- propertyRoles.append(role);
- roleNames.insert(propertyName, role);
- addProperty(&builder, 1, propertyName, propertyType);
- }
-
- metaObject.reset(builder.toMetaObject());
- *static_cast<QMetaObject *>(this) = *metaObject;
- propertyCache = QQmlPropertyCache::createStandalone(
- metaObject.data(), model.modelItemRevision);
- }
-};
-
-//-----------------------------------------------------------------
-// QQmlListAccessor
-//-----------------------------------------------------------------
-
-class QQmlDMListAccessorData : public QQmlDelegateModelItem
-{
- Q_OBJECT
- Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
-public:
- QQmlDMListAccessorData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- QQmlAdaptorModel::Accessors *accessor,
- int index, int row, int column, const QVariant &value)
- : QQmlDelegateModelItem(metaType, accessor, index, row, column)
- , cachedData(value)
- {
- }
-
- QVariant modelData() const
- {
- return cachedData;
- }
-
- void setModelData(const QVariant &data)
- {
- if (data == cachedData)
- return;
-
- cachedData = data;
- emit modelDataChanged();
- }
-
- static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
- {
- QV4::ExecutionEngine *v4 = b->engine();
- const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
- if (!o)
- return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
-
- return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData);
- }
-
- static QV4::ReturnedValue set_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
- {
- QV4::ExecutionEngine *v4 = b->engine();
- const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
- if (!o)
- return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
- if (!argc)
- return v4->throwTypeError();
-
- static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(
- QV4::ExecutionEngine::toVariant(argv[0], QMetaType {}));
- return QV4::Encode::undefined();
- }
-
- QV4::ReturnedValue get() override
- {
- QQmlAdaptorModelEngineData *data = engineData(v4);
- QV4::Scope scope(v4);
- QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
- QV4::ScopedObject p(scope, data->listItemProto.value());
- o->setPrototypeOf(p);
- ++scriptRef;
- return o.asReturnedValue();
- }
-
- void setValue(const QString &role, const QVariant &value) override
- {
- if (role == QLatin1String("modelData"))
- cachedData = value;
- }
-
- bool resolveIndex(const QQmlAdaptorModel &model, int idx) override
- {
- if (index == -1) {
- index = idx;
- cachedData = model.list.at(idx);
- emit modelIndexChanged();
- emit modelDataChanged();
- return true;
- } else {
- return false;
- }
- }
-
-
-Q_SIGNALS:
- void modelDataChanged();
-
-private:
- QVariant cachedData;
-};
-
-
-class VDMListDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors
-{
-public:
- VDMListDelegateDataType()
- : QQmlRefCount()
- , QQmlAdaptorModel::Accessors()
- {}
-
- void cleanup(QQmlAdaptorModel &) const override
- {
- const_cast<VDMListDelegateDataType *>(this)->release();
- }
-
- int rowCount(const QQmlAdaptorModel &model) const override
- {
- return model.list.count();
- }
-
- int columnCount(const QQmlAdaptorModel &) const override
- {
- return 1;
- }
-
- QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
- {
- const QVariant entry = model.list.at(index);
- if (role == QLatin1String("modelData"))
- return entry;
-
- const QMetaType type = entry.metaType();
- if (type == QMetaType::fromType<QVariantMap>())
- return entry.toMap().value(role);
-
- if (type == QMetaType::fromType<QVariantHash>())
- return entry.toHash().value(role);
-
- const QMetaType::TypeFlags typeFlags = type.flags();
- if (typeFlags & QMetaType::PointerToQObject)
- return entry.value<QObject *>()->property(role.toUtf8());
-
- const QMetaObject *metaObject = type.metaObject();
- if (!metaObject) {
- // NB: This acquires the lock on QQmlMetaTypeData. If we had a QQmlEngine here,
- // we could use QQmlGadgetPtrWrapper::instance() to avoid this.
- if (const QQmlValueType *valueType = QQmlMetaType::valueType(type))
- metaObject = valueType->staticMetaObject();
- else
- return QVariant();
- }
-
- const int propertyIndex = metaObject->indexOfProperty(role.toUtf8());
- return metaObject->property(propertyIndex).readOnGadget(entry.constData());
- }
-
- QQmlDelegateModelItem *createItem(
- QQmlAdaptorModel &model,
- const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- int index, int row, int column) const override
- {
- VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this);
- if (!propertyCache) {
- dataType->propertyCache = QQmlPropertyCache::createStandalone(
- &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision);
- }
-
- return new QQmlDMListAccessorData(
- metaType,
- dataType,
- index, row, column,
- index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant());
- }
-
- bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
- {
- for (auto modelItem : items) {
- const int modelItemIndex = modelItem->index;
- if (modelItemIndex < index || modelItemIndex >= index + count)
- continue;
-
- auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem);
- QVariant updatedModelData = model.list.at(listModelItem->index);
- listModelItem->setModelData(updatedModelData);
- }
- return true;
- }
-};
-
-//-----------------------------------------------------------------
-// QObject
-//-----------------------------------------------------------------
-
-class VDMObjectDelegateDataType;
-class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface
-{
- Q_OBJECT
- Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged)
- Q_INTERFACES(QQmlAdaptorModelProxyInterface)
-public:
- QQmlDMObjectData(
- const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- VDMObjectDelegateDataType *dataType,
- int index, int row, int column,
- QObject *object);
-
- void setModelData(QObject *modelData)
- {
- if (modelData == object)
- return;
-
- object = modelData;
- emit modelDataChanged();
- }
-
- QObject *modelData() const { return object; }
- QObject *proxiedObject() override { return object; }
-
- QPointer<QObject> object;
-
-Q_SIGNALS:
- void modelDataChanged();
-};
-
-class VDMObjectDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors
-{
-public:
- int propertyOffset;
- int signalOffset;
- bool shared;
- QMetaObjectBuilder builder;
-
- VDMObjectDelegateDataType()
- : propertyOffset(0)
- , signalOffset(0)
- , shared(true)
- {
- }
-
- VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type)
- : QQmlRefCount()
- , QQmlAdaptorModel::Accessors()
- , propertyOffset(type.propertyOffset)
- , signalOffset(type.signalOffset)
- , shared(false)
- , builder(type.metaObject.data(), QMetaObjectBuilder::Properties
- | QMetaObjectBuilder::Signals
- | QMetaObjectBuilder::SuperClass
- | QMetaObjectBuilder::ClassName)
- {
- builder.setFlags(MetaObjectFlag::DynamicMetaObject);
- }
-
- int rowCount(const QQmlAdaptorModel &model) const override
- {
- return model.list.count();
- }
-
- int columnCount(const QQmlAdaptorModel &) const override
- {
- return 1;
- }
-
- QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
- {
- if (QObject *object = model.list.at(index).value<QObject *>())
- return object->property(role.toUtf8());
- return QVariant();
- }
-
- QQmlDelegateModelItem *createItem(
- QQmlAdaptorModel &model,
- const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- int index, int row, int column) const override
- {
- VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this);
- if (!metaObject)
- dataType->initializeMetaType(model);
- return index >= 0 && index < model.list.count()
- ? new QQmlDMObjectData(metaType, dataType, index, row, column, qvariant_cast<QObject *>(model.list.at(index)))
- : nullptr;
- }
-
- void initializeMetaType(QQmlAdaptorModel &model)
- {
- Q_UNUSED(model);
- setModelDataType<QQmlDMObjectData>(&builder, this);
-
- metaObject.reset(builder.toMetaObject());
- // Note: ATM we cannot create a shared property cache for this class, since each model
- // object can have different properties. And to make those properties available to the
- // delegate, QQmlDMObjectData makes use of a QAbstractDynamicMetaObject subclass
- // (QQmlDMObjectDataMetaObject), which we cannot represent in a QQmlPropertyCache.
- // By not having a shared property cache, revisioned properties in QQmlDelegateModelItem
- // will always be available to the delegate, regardless of the import version.
- }
-
- void cleanup(QQmlAdaptorModel &) const override
- {
- const_cast<VDMObjectDelegateDataType *>(this)->release();
- }
-
- bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
- {
- for (auto modelItem : items) {
- const int modelItemIndex = modelItem->index;
- if (modelItemIndex < index || modelItemIndex >= index + count)
- continue;
-
- auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem);
- QObject *updatedModelData = qvariant_cast<QObject *>(model.list.at(objectModelItem->index));
- objectModelItem->setModelData(updatedModelData);
- }
- return true;
- }
-};
-
-class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
-{
-public:
- QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type)
- : m_data(data)
- , m_type(type)
- {
- QObjectPrivate *op = QObjectPrivate::get(m_data);
- *static_cast<QMetaObject *>(this) = *type->metaObject;
- op->metaObject = this;
- m_type->addref();
- }
-
- ~QQmlDMObjectDataMetaObject()
- {
- m_type->release();
- }
-
- int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override
- {
- Q_ASSERT(o == m_data);
- Q_UNUSED(o);
-
- static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
- if (id >= m_type->propertyOffset
- && (call == QMetaObject::ReadProperty
- || call == QMetaObject::WriteProperty
- || call == QMetaObject::ResetProperty)) {
- if (m_data->object)
- QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments);
- return -1;
- } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) {
- QMetaObject::activate(m_data, this, id - m_type->signalOffset, nullptr);
- return -1;
- } else {
- return m_data->qt_metacall(call, id, arguments);
- }
- }
-
- int createProperty(const char *name, const char *) override
- {
- if (!m_data->object)
- return -1;
- const QMetaObject *metaObject = m_data->object->metaObject();
- static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
-
- const int previousPropertyCount = propertyCount() - propertyOffset();
- int propertyIndex = metaObject->indexOfProperty(name);
- if (propertyIndex == -1)
- return -1;
- if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount())
- return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
-
- if (m_type->shared) {
- VDMObjectDelegateDataType *type = m_type;
- m_type = new VDMObjectDelegateDataType(*m_type);
- type->release();
- }
-
- const int previousMethodCount = methodCount();
- int notifierId = previousMethodCount - methodOffset();
- for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) {
- QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset);
- QMetaPropertyBuilder propertyBuilder;
- if (property.hasNotifySignal()) {
- m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()");
- propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId);
- ++notifierId;
- } else {
- propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName());
- }
- propertyBuilder.setWritable(property.isWritable());
- propertyBuilder.setResettable(property.isResettable());
- propertyBuilder.setConstant(property.isConstant());
- }
-
- m_type->metaObject.reset(m_type->builder.toMetaObject());
- *static_cast<QMetaObject *>(this) = *m_type->metaObject;
-
- notifierId = previousMethodCount;
- for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) {
- QMetaProperty property = metaObject->property(i + objectPropertyOffset);
- if (property.hasNotifySignal()) {
- QQmlPropertyPrivate::connect(
- m_data->object, property.notifySignalIndex(), m_data, notifierId);
- ++notifierId;
- }
- }
- return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
- }
-
- QQmlDMObjectData *m_data;
- VDMObjectDelegateDataType *m_type;
-};
-
-QQmlDMObjectData::QQmlDMObjectData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
- VDMObjectDelegateDataType *dataType,
- int index, int row, int column,
- QObject *object)
- : QQmlDelegateModelItem(metaType, dataType, index, row, column)
- , object(object)
-{
- new QQmlDMObjectDataMetaObject(this, dataType);
-}
-
-//-----------------------------------------------------------------
-// QQmlAdaptorModel
-//-----------------------------------------------------------------
-
-static const QQmlAdaptorModel::Accessors qt_vdm_null_accessors;
-
QQmlAdaptorModel::Accessors::~Accessors()
{
}
QQmlAdaptorModel::QQmlAdaptorModel()
: QQmlGuard<QObject>(QQmlAdaptorModel::objectDestroyedImpl, nullptr)
- , accessors(&qt_vdm_null_accessors)
+ , accessors(&m_nullAccessors)
{
}
@@ -983,24 +53,24 @@ void QQmlAdaptorModel::setModel(const QVariant &variant)
} else if (list.type() != QQmlListAccessor::Invalid
&& list.type() != QQmlListAccessor::Instance) { // Null QObject
setObject(nullptr);
- accessors = new VDMListDelegateDataType;
+ accessors = new VDMListDelegateDataType(this);
} else {
setObject(nullptr);
- accessors = &qt_vdm_null_accessors;
+ accessors = &m_nullAccessors;
}
}
void QQmlAdaptorModel::invalidateModel()
{
accessors->cleanup(*this);
- accessors = &qt_vdm_null_accessors;
+ accessors = &m_nullAccessors;
// Don't clear the model object as we still need the guard to clear the list variant if the
// object is destroyed.
}
bool QQmlAdaptorModel::isValid() const
{
- return accessors != &qt_vdm_null_accessors;
+ return accessors != &m_nullAccessors;
}
int QQmlAdaptorModel::count() const
@@ -1046,21 +116,4 @@ void QQmlAdaptorModel::objectDestroyedImpl(QQmlGuardImpl *guard)
This->setModel(QVariant());
}
-QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4)
- : v4(v4)
-{
- QV4::Scope scope(v4);
- QV4::ScopedObject proto(scope, v4->newObject());
- proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr);
- proto->defineAccessorProperty(QStringLiteral("modelData"),
- QQmlDMListAccessorData::get_modelData, QQmlDMListAccessorData::set_modelData);
- listItemProto.set(v4, proto);
-}
-
-QQmlAdaptorModelEngineData::~QQmlAdaptorModelEngineData()
-{
-}
-
QT_END_NAMESPACE
-
-#include <qqmladaptormodel.moc>
diff --git a/src/qmlmodels/qqmladaptormodel_p.h b/src/qmlmodels/qqmladaptormodel_p.h
index da8a4881a6..d43cff129d 100644
--- a/src/qmlmodels/qqmladaptormodel_p.h
+++ b/src/qmlmodels/qqmladaptormodel_p.h
@@ -34,7 +34,7 @@ class QQmlDelegateModel;
class QQmlDelegateModelItem;
class QQmlDelegateModelItemMetaType;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlGuard<QObject>
+class Q_QMLMODELS_EXPORT QQmlAdaptorModel : public QQmlGuard<QObject>
{
public:
class Accessors
@@ -52,7 +52,7 @@ public:
virtual QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &,
const QQmlRefPointer<QQmlDelegateModelItemMetaType> &,
- int, int, int) const { return nullptr; }
+ int, int, int) { return nullptr; }
virtual bool notify(
const QQmlAdaptorModel &,
@@ -75,7 +75,7 @@ public:
QQmlPropertyCache::ConstPtr propertyCache;
};
- const Accessors *accessors;
+ Accessors *accessors;
QPersistentModelIndex rootIndex;
QQmlListAccessor list;
// we need to ensure that a JS created model does not get gced, but cannot
@@ -136,6 +136,8 @@ public:
private:
static void objectDestroyedImpl(QQmlGuardImpl *);
+
+ Accessors m_nullAccessors;
};
class QQmlAdaptorModelProxyInterface
diff --git a/src/qmlmodels/qqmladaptormodelenginedata.cpp b/src/qmlmodels/qqmladaptormodelenginedata.cpp
new file mode 100644
index 0000000000..dd3301d258
--- /dev/null
+++ b/src/qmlmodels/qqmladaptormodelenginedata.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <private/qqmladaptormodelenginedata_p.h>
+#include <private/qqmldmlistaccessordata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4)
+ : v4(v4)
+{
+ QV4::Scope scope(v4);
+ QV4::ScopedObject proto(scope, v4->newObject());
+ proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr);
+ proto->defineAccessorProperty(
+ QStringLiteral("modelData"),
+ QQmlDMListAccessorData::get_modelData, QQmlDMListAccessorData::set_modelData);
+ proto->defineAccessorProperty(
+ QString(),
+ QQmlDMListAccessorData::get_modelData, QQmlDMListAccessorData::set_modelData);
+ listItemProto.set(v4, proto);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmladaptormodelenginedata_p.h b/src/qmlmodels/qqmladaptormodelenginedata_p.h
new file mode 100644
index 0000000000..290a74e1ee
--- /dev/null
+++ b/src/qmlmodels/qqmladaptormodelenginedata_p.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QQMLADAPTORMODELENGINEDATA_P_H
+#define QQMLADAPTORMODELENGINEDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmldelegatemodel_p_p.h>
+#include <private/qmetaobjectbuilder_p.h>
+#include <private/qqmlproperty_p.h>
+
+#include <private/qv4value_p.h>
+#include <private/qv4functionobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlAdaptorModelEngineData : public QV4::ExecutionEngine::Deletable
+{
+public:
+ QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4);
+
+ QV4::ExecutionEngine *v4;
+ QV4::PersistentValue listItemProto;
+
+ static QV4::ReturnedValue get_index(const QV4::FunctionObject *f, const QV4::Value *thisObject, const QV4::Value *, int)
+ {
+ QV4::Scope scope(f);
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
+ if (!o)
+ RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
+
+ RETURN_RESULT(QV4::Encode(o->d()->item->index));
+ }
+
+ template <typename T, typename M> static void setModelDataType(QMetaObjectBuilder *builder, M *metaType)
+ {
+ builder->setFlags(MetaObjectFlag::DynamicMetaObject);
+ builder->setClassName(T::staticMetaObject.className());
+ builder->setSuperClass(&T::staticMetaObject);
+ metaType->propertyOffset = T::staticMetaObject.propertyCount();
+ metaType->signalOffset = T::staticMetaObject.methodCount();
+ }
+
+ static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType)
+ {
+ builder->addSignal("__" + QByteArray::number(propertyId) + "()");
+ QMetaPropertyBuilder property = builder->addProperty(
+ propertyName, propertyType, propertyId);
+ property.setWritable(true);
+ }
+
+ V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, get)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLADAPTORMODELENGINEDATA_P_H
diff --git a/src/qmlmodels/qqmlchangeset.cpp b/src/qmlmodels/qqmlchangeset.cpp
index 24e8f315dc..35fa95d6a8 100644
--- a/src/qmlmodels/qqmlchangeset.cpp
+++ b/src/qmlmodels/qqmlchangeset.cpp
@@ -516,6 +516,8 @@ void QQmlChangeSet::change(QVector<Change> *changes)
}
/*!
+ \internal
+ \relates QQmlChangeSet
Prints the contents of a change \a set to the \a debug stream.
*/
@@ -536,6 +538,8 @@ QDebug operator <<(QDebug debug, const QQmlChangeSet &set)
}
/*!
+ \internal
+ \relates QQmlChangeSet
Prints a \a change to the \a debug stream.
*/
diff --git a/src/qmlmodels/qqmlchangeset_p.h b/src/qmlmodels/qqmlchangeset_p.h
index eed46d4f4d..b31177bf45 100644
--- a/src/qmlmodels/qqmlchangeset_p.h
+++ b/src/qmlmodels/qqmlchangeset_p.h
@@ -15,14 +15,17 @@
// We mean it.
//
+#include <QtQmlIntegration/qqmlintegration.h>
#include <QtCore/qdebug.h>
#include <QtCore/qvector.h>
#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
QT_BEGIN_NAMESPACE
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlChangeSet
+class Q_QMLMODELS_EXPORT QQmlChangeSet
{
+ Q_GADGET
+ QML_ANONYMOUS
public:
struct MoveKey
{
@@ -117,8 +120,8 @@ inline size_t qHash(const QQmlChangeSet::MoveKey &key) { return qHash(qMakePair(
inline bool operator ==(const QQmlChangeSet::MoveKey &l, const QQmlChangeSet::MoveKey &r) {
return l.moveId == r.moveId && l.offset == r.offset; }
-Q_QMLMODELS_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change);
-Q_QMLMODELS_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet &change);
+Q_QMLMODELS_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change);
+Q_QMLMODELS_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet &change);
QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index fcbcec15a1..e24c10b786 100644
--- a/src/qmlmodels/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -103,7 +103,7 @@ public:
QV4::PersistentValue changeProto;
};
-V4_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData)
+V4_DEFINE_EXTENSION(QQmlDelegateModelEngineData, qdmEngineData)
void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop)
@@ -351,26 +351,16 @@ void QQmlDelegateModelPrivate::connectToAbstractItemModel()
auto aim = m_adaptorModel.aim();
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_columnsInserted(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
- q, QQmlDelegateModel, SLOT(_q_columnsRemoved(QModelIndex,int,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
- q, QQmlDelegateModel, SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
- q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()),
- q, QQmlDelegateModel, SLOT(_q_modelReset()));
- qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
+ QObject::connect(aim, &QAbstractItemModel::rowsInserted, q, &QQmlDelegateModel::_q_rowsInserted);
+ QObject::connect(aim, &QAbstractItemModel::rowsRemoved, q, &QQmlDelegateModel::_q_rowsRemoved);
+ QObject::connect(aim, &QAbstractItemModel::rowsAboutToBeRemoved, q, &QQmlDelegateModel::_q_rowsAboutToBeRemoved);
+ QObject::connect(aim, &QAbstractItemModel::columnsInserted, q, &QQmlDelegateModel::_q_columnsInserted);
+ QObject::connect(aim, &QAbstractItemModel::columnsRemoved, q, &QQmlDelegateModel::_q_columnsRemoved);
+ QObject::connect(aim, &QAbstractItemModel::columnsMoved, q, &QQmlDelegateModel::_q_columnsMoved);
+ QObject::connect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged);
+ QObject::connect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved);
+ QObject::connect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
+ QObject::connect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
}
void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
@@ -381,26 +371,16 @@ void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
auto aim = m_adaptorModel.aim();
- QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)),
- q, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- q, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(columnsInserted(QModelIndex,int,int)), q,
- SLOT(_q_columnsInserted(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(columnsRemoved(QModelIndex,int,int)), q,
- SLOT(_q_columnsRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), q,
- SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int)));
- QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
- q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
- QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
- QObject::disconnect(aim, SIGNAL(modelReset()),
- q, SLOT(_q_modelReset()));
- QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
+ QObject::disconnect(aim, &QAbstractItemModel::rowsInserted, q, &QQmlDelegateModel::_q_rowsInserted);
+ QObject::disconnect(aim, &QAbstractItemModel::rowsAboutToBeRemoved, q, &QQmlDelegateModel::_q_rowsAboutToBeRemoved);
+ QObject::disconnect(aim, &QAbstractItemModel::rowsRemoved, q, &QQmlDelegateModel::_q_rowsRemoved);
+ QObject::disconnect(aim, &QAbstractItemModel::columnsInserted, q, &QQmlDelegateModel::_q_columnsInserted);
+ QObject::disconnect(aim, &QAbstractItemModel::columnsRemoved, q, &QQmlDelegateModel::_q_columnsRemoved);
+ QObject::disconnect(aim, &QAbstractItemModel::columnsMoved, q, &QQmlDelegateModel::_q_columnsMoved);
+ QObject::disconnect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged);
+ QObject::disconnect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved);
+ QObject::disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
+ QObject::disconnect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
}
void QQmlDelegateModel::setModel(const QVariant &model)
@@ -461,7 +441,7 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
if (adc) {
d->m_delegateChooser = adc;
- d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged,
+ d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this,
[d](){ d->delegateChanged(); });
}
}
@@ -922,7 +902,7 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod
if (incubatorPriv->hadTopLevelRequiredProperties()) {
// If we have required properties, we clear the context object
// so that the model role names are not polluting the context.
- // Unless the context is bound, in which case we have never set context object.
+ // Unless the context is bound, in which case we have never set the context object.
if (incubating && !isBound) {
Q_ASSERT(incubating->contextData);
incubating->contextData->setContextObject(nullptr);
@@ -931,16 +911,20 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod
proxyContext->setContextObject(nullptr);
}
+ // Retrieve the metaObject before the potential return so that the accessors have a chance
+ // to perform some finalization in case they produce a dynamic metaobject. Here we know for
+ // sure that we are using required properties.
+ const QMetaObject *qmlMetaObject = modelItemToIncubate->metaObject();
+
if (incubatorPriv->requiredProperties()->empty())
return;
RequiredProperties *requiredProperties = incubatorPriv->requiredProperties();
- auto qmlMetaObject = modelItemToIncubate->metaObject();
// if a required property was not in the model, it might still be a static property of the
// QQmlDelegateModelItem or one of its derived classes this is the case for index, row,
// column, model and more
- // the most derived subclass of QQmlDelegateModelItem is QQmlDMAbstractModelData at depth 2,
- // so 4 should be plenty
+ // the most derived subclasses of QQmlDelegateModelItem are QQmlDMAbstractItemModelData and
+ // QQmlDMObjectData at depth 2, so 4 should be plenty
QVarLengthArray<QPair<const QMetaObject *, QObject *>, 4> mos;
// we first check the dynamic meta object for properties originating from the model
// contains abstractitemmodelproperties
@@ -987,6 +971,13 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod
modelItemToIncubate->contextData->setContextObject(modelItemToIncubate);
if (proxiedObject)
proxyContext->setContextObject(proxiedObject);
+
+ // Retrieve the metaObject() once so that the accessors have a chance to perform some
+ // finalization in case they produce a dynamic metaobject. For example, they might be
+ // inclined to create a propertyCache now because there are no required properties and any
+ // revisioned properties should be hidden after all. Here is the first time we know for
+ // sure whether we are using context properties.
+ modelItemToIncubate->metaObject();
}
}
@@ -1203,6 +1194,9 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
addCacheItem(cacheItem, it);
reuseItem(cacheItem, index, flags);
cacheItem->referenceObject();
+
+ if (index == m_compositor.count(group) - 1)
+ requestMoreIfNecessary();
return cacheItem->object;
}
@@ -1285,7 +1279,9 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
if (cacheItem->object && (!cacheItem->incubationTask || isDoneIncubating(cacheItem->incubationTask->status())))
return cacheItem->object;
- cacheItem->releaseObject();
+ if (cacheItem->objectRef > 0)
+ cacheItem->releaseObject();
+
if (!cacheItem->isReferenced()) {
removeCacheItem(cacheItem);
delete cacheItem;
@@ -1450,6 +1446,50 @@ void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector<int>
d->itemsChanged(changes);
d->emitChanges();
}
+ const bool needToCheckDelegateChoiceInvalidation = d->m_delegateChooser && !roles.isEmpty();
+ if (!needToCheckDelegateChoiceInvalidation)
+ return;
+
+ // here, we only really can handle AIM based models, because only there
+ // we can do something sensible with roles
+ if (!d->m_adaptorModel.adaptsAim())
+ return;
+
+ const auto aim = d->m_adaptorModel.aim();
+ const auto choiceRole = d->m_delegateChooser->role().toUtf8();
+ const auto &roleNames = aim->roleNames();
+ auto it = std::find_if(roles.begin(), roles.end(), [&](int role) {
+ return roleNames[role] == choiceRole;
+ });
+ if (it == roles.end())
+ return;
+
+ // Compare handleModelReset - we're doing a more localized version
+
+ /* A role change affecting the DelegateChoice is equivalent to removing all
+ affected items (including invalidating their cache entries) and afterwards
+ reinserting them.
+ */
+ QVector<Compositor::Remove> removes;
+ QVector<Compositor::Insert> inserts;
+ d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes);
+ const QList<QQmlDelegateModelItem *> cache = d->m_cache;
+ for (QQmlDelegateModelItem *item : cache)
+ item->referenceObject();
+ for (const auto& removed: removes) {
+ if (!d->m_cache.isSharedWith(cache))
+ break;
+ QQmlDelegateModelItem *item = cache.value(removed.cacheIndex(), nullptr);
+ if (!d->m_cache.contains(item))
+ continue;
+ if (item->modelIndex() != -1)
+ item->setModelIndex(-1, -1, -1);
+ }
+ for (QQmlDelegateModelItem *item : cache)
+ item->releaseObject();
+ d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts);
+ d->itemsMoved(removes, inserts);
+ d->emitChanges();
}
static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas)
@@ -1602,8 +1642,8 @@ void QQmlDelegateModelPrivate::itemsRemoved(
if (movedItems && remove.isMove()) {
movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex(), remove.count));
- QList<QQmlDelegateModelItem *>::iterator begin = m_cache.begin() + remove.cacheIndex();
- QList<QQmlDelegateModelItem *>::iterator end = begin + remove.count;
+ QList<QQmlDelegateModelItem *>::const_iterator begin = m_cache.constBegin() + remove.cacheIndex();
+ QList<QQmlDelegateModelItem *>::const_iterator end = begin + remove.count;
m_cache.erase(begin, end);
} else {
for (; cacheIndex < remove.cacheIndex() + remove.count - removedCache; ++cacheIndex) {
@@ -1617,7 +1657,7 @@ void QQmlDelegateModelPrivate::itemsRemoved(
emitDestroyingItem(object);
cacheItem->scriptRef -= 1;
}
- if (!cacheItem->isReferenced()) {
+ if (!cacheItem->isReferenced() && !remove.inGroup(Compositor::Persisted)) {
m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag);
m_cache.removeAt(cacheIndex);
delete cacheItem;
@@ -1833,14 +1873,41 @@ void QQmlDelegateModelPrivate::emitChanges()
for (int i = 1; i < m_groupCount; ++i)
QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset);
- auto cacheCopy = m_cache; // deliberate; emitChanges may alter m_cache
- for (QQmlDelegateModelItem *cacheItem : std::as_const(cacheCopy)) {
- if (cacheItem->attached)
- cacheItem->attached->emitChanges();
+ // emitChanges may alter m_cache and delete items
+ QVarLengthArray<QPointer<QQmlDelegateModelAttached>> attachedObjects;
+ attachedObjects.reserve(m_cache.length());
+ for (const QQmlDelegateModelItem *cacheItem : std::as_const(m_cache))
+ attachedObjects.append(cacheItem->attached);
+
+ for (const QPointer<QQmlDelegateModelAttached> &attached : std::as_const(attachedObjects)) {
+ if (attached && attached->m_cacheItem)
+ attached->emitChanges();
}
}
-void QQmlDelegateModel::_q_modelReset()
+void QQmlDelegateModel::_q_modelAboutToBeReset()
+{
+ auto aim = static_cast<QAbstractItemModel *>(sender());
+ auto oldRoleNames = aim->roleNames();
+ // this relies on the fact that modelAboutToBeReset must be followed
+ // by a modelReset signal before any further modelAboutToBeReset can occur
+ QObject::connect(aim, &QAbstractItemModel::modelReset, this, [&, oldRoleNames](){
+ auto aim = static_cast<QAbstractItemModel *>(sender());
+ if (oldRoleNames == aim->roleNames()) {
+ // if the rolenames stayed the same (most common case), then we don't have
+ // to throw away all the setup that we did
+ handleModelReset();
+ } else {
+ // If they did change, we give up and just start from scratch via setMode
+ setModel(QVariant::fromValue(model()));
+ // but we still have to call handleModelReset, otherwise views will
+ // not refresh
+ handleModelReset();
+ }
+ }, Qt::SingleShotConnection);
+}
+
+void QQmlDelegateModel::handleModelReset()
{
Q_D(QQmlDelegateModel);
if (!d->m_delegate)
@@ -2005,7 +2072,7 @@ void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &par
// Ignored
} else {
// We don't know what's going on, so reset the model
- _q_modelReset();
+ handleModelReset();
}
}
@@ -2018,26 +2085,28 @@ QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj
return new QQmlDelegateModelAttached(obj);
}
-bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups)
+QQmlDelegateModelPrivate::InsertionResult
+QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups)
{
if (!m_context || !m_context->isValid())
- return false;
+ return InsertionResult::Error;
QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, -1);
if (!cacheItem)
- return false;
+ return InsertionResult::Error;
if (!object.isObject())
- return false;
+ return InsertionResult::Error;
QV4::ExecutionEngine *v4 = object.as<QV4::Object>()->engine();
QV4::Scope scope(v4);
QV4::ScopedObject o(scope, object);
if (!o)
- return false;
+ return InsertionResult::Error;
QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
QV4::ScopedValue propertyName(scope);
QV4::ScopedValue v(scope);
+ const auto oldCache = m_cache;
while (1) {
propertyName = it.nextPropertyNameAsString(v);
if (propertyName->isNull())
@@ -2046,6 +2115,9 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const
propertyName->toQStringNoThrow(),
QV4::ExecutionEngine::toVariant(v, QMetaType {}));
}
+ const bool cacheModified = !m_cache.isSharedWith(oldCache);
+ if (cacheModified)
+ return InsertionResult::Retry;
cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag;
@@ -2055,7 +2127,7 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const
m_cache.insert(before.cacheIndex(), cacheItem);
m_compositor.insert(before, nullptr, 0, 1, cacheItem->groups);
- return true;
+ return InsertionResult::Success;
}
//============================================================================
@@ -2720,20 +2792,24 @@ void QQmlDelegateModelAttached::emitChanges()
m_previousGroups = m_cacheItem->groups;
int indexChanges = 0;
- for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) {
+ const int groupCount = m_cacheItem->metaType->groupCount;
+ for (int i = 1; i < groupCount; ++i) {
if (m_previousIndex[i] != m_currentIndex[i]) {
m_previousIndex[i] = m_currentIndex[i];
indexChanges |= (1 << i);
}
}
+ // Don't access m_cacheItem anymore once we've started sending signals.
+ // We don't own it and someone might delete it.
+
int notifierId = 0;
const QMetaObject *meta = metaObject();
- for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) {
+ for (int i = 1; i < groupCount; ++i, ++notifierId) {
if (groupChanges & (1 << i))
QMetaObject::activate(this, meta, notifierId, nullptr);
}
- for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) {
+ for (int i = 1; i < groupCount; ++i, ++notifierId) {
if (indexChanges & (1 << i))
QMetaObject::activate(this, meta, notifierId, nullptr);
}
@@ -2762,9 +2838,9 @@ void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4)
Q_Q(QQmlDelegateModelGroup);
if (isChangedConnected() && !changeSet.isEmpty()) {
emit q->changed(QJSValuePrivate::fromReturnedValue(
- engineData(v4)->array(v4, changeSet.removes())),
+ qdmEngineData(v4)->array(v4, changeSet.removes())),
QJSValuePrivate::fromReturnedValue(
- engineData(v4)->array(v4, changeSet.inserts())));
+ qdmEngineData(v4)->array(v4, changeSet.inserts())));
}
if (changeSet.difference() != 0)
emit q->countChanged();
@@ -3028,7 +3104,7 @@ bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *ind
items that are later replaced by actual data.
*/
-void QQmlDelegateModelGroup::insert(QQmlV4Function *args)
+void QQmlDelegateModelGroup::insert(QQmlV4FunctionPtr args)
{
Q_D(QQmlDelegateModelGroup);
QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
@@ -3052,9 +3128,8 @@ void QQmlDelegateModelGroup::insert(QQmlV4Function *args)
v = (*args)[i];
}
- Compositor::insert_iterator before = index < model->m_compositor.count(group)
- ? model->m_compositor.findInsertPosition(group, index)
- : model->m_compositor.end();
+ if (v->as<QV4::ArrayObject>())
+ return;
int groups = 1 << d->group;
if (++i < args->length()) {
@@ -3062,11 +3137,16 @@ void QQmlDelegateModelGroup::insert(QQmlV4Function *args)
groups |= model->m_cacheMetaType->parseGroups(val);
}
- if (v->as<QV4::ArrayObject>()) {
- return;
- } else if (v->as<QV4::Object>()) {
- model->insert(before, v, groups);
- model->emitChanges();
+ if (v->as<QV4::Object>()) {
+ auto insertionResult = QQmlDelegateModelPrivate::InsertionResult::Retry;
+ do {
+ Compositor::insert_iterator before = index < model->m_compositor.count(group)
+ ? model->m_compositor.findInsertPosition(group, index)
+ : model->m_compositor.end();
+ insertionResult = model->insert(before, v, groups);
+ } while (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Retry);
+ if (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Success)
+ model->emitChanges();
}
}
@@ -3087,7 +3167,7 @@ void QQmlDelegateModelGroup::insert(QQmlV4Function *args)
group remain instantiated when not referenced by any view.
*/
-void QQmlDelegateModelGroup::create(QQmlV4Function *args)
+void QQmlDelegateModelGroup::create(QQmlV4FunctionPtr args)
{
Q_D(QQmlDelegateModelGroup);
if (!d->model)
@@ -3116,16 +3196,19 @@ void QQmlDelegateModelGroup::create(QQmlV4Function *args)
groups |= model->m_cacheMetaType->parseGroups(val);
}
- Compositor::insert_iterator before = index < model->m_compositor.count(group)
- ? model->m_compositor.findInsertPosition(group, index)
- : model->m_compositor.end();
+ auto insertionResult = QQmlDelegateModelPrivate::InsertionResult::Retry;
+ do {
+ Compositor::insert_iterator before = index < model->m_compositor.count(group)
+ ? model->m_compositor.findInsertPosition(group, index)
+ : model->m_compositor.end();
- index = before.index[d->group];
- group = d->group;
+ index = before.index[d->group];
+ group = d->group;
- if (!model->insert(before, v, groups)) {
+ insertionResult = model->insert(before, v, groups);
+ } while (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Retry);
+ if (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Error)
return;
- }
}
}
if (index < 0 || index >= model->m_compositor.count(group)) {
@@ -3162,7 +3245,7 @@ void QQmlDelegateModelGroup::create(QQmlV4Function *args)
that the previously unresolved item has simply moved.
*/
-void QQmlDelegateModelGroup::resolve(QQmlV4Function *args)
+void QQmlDelegateModelGroup::resolve(QQmlV4FunctionPtr args)
{
Q_D(QQmlDelegateModelGroup);
if (!d->model)
@@ -3266,7 +3349,7 @@ void QQmlDelegateModelGroup::resolve(QQmlV4Function *args)
Removes \a count items starting at \a index from the group.
*/
-void QQmlDelegateModelGroup::remove(QQmlV4Function *args)
+void QQmlDelegateModelGroup::remove(QQmlV4FunctionPtr args)
{
Q_D(QQmlDelegateModelGroup);
if (!d->model)
@@ -3306,7 +3389,7 @@ void QQmlDelegateModelGroup::remove(QQmlV4Function *args)
}
bool QQmlDelegateModelGroupPrivate::parseGroupArgs(
- QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const
+ QQmlV4FunctionPtr args, Compositor::Group *group, int *index, int *count, int *groups) const
{
if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType)
return false;
@@ -3340,7 +3423,7 @@ bool QQmlDelegateModelGroupPrivate::parseGroupArgs(
Adds \a count items starting at \a index to \a groups.
*/
-void QQmlDelegateModelGroup::addGroups(QQmlV4Function *args)
+void QQmlDelegateModelGroup::addGroups(QQmlV4FunctionPtr args)
{
Q_D(QQmlDelegateModelGroup);
Compositor::Group group = d->group;
@@ -3370,7 +3453,7 @@ void QQmlDelegateModelGroup::addGroups(QQmlV4Function *args)
Removes \a count items starting at \a index from \a groups.
*/
-void QQmlDelegateModelGroup::removeGroups(QQmlV4Function *args)
+void QQmlDelegateModelGroup::removeGroups(QQmlV4FunctionPtr args)
{
Q_D(QQmlDelegateModelGroup);
Compositor::Group group = d->group;
@@ -3401,7 +3484,7 @@ void QQmlDelegateModelGroup::removeGroups(QQmlV4Function *args)
their existing groups and added to \a groups.
*/
-void QQmlDelegateModelGroup::setGroups(QQmlV4Function *args)
+void QQmlDelegateModelGroup::setGroups(QQmlV4FunctionPtr args)
{
Q_D(QQmlDelegateModelGroup);
Compositor::Group group = d->group;
@@ -3436,7 +3519,7 @@ void QQmlDelegateModelGroup::setGroups(QQmlV4Function *args)
reordering you have done via this function.
*/
-void QQmlDelegateModelGroup::move(QQmlV4Function *args)
+void QQmlDelegateModelGroup::move(QQmlV4FunctionPtr args)
{
Q_D(QQmlDelegateModelGroup);
@@ -3926,7 +4009,7 @@ public:
const QQmlChangeSet::Change &change = array->at(index);
- QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value());
+ QV4::ScopedObject changeProto(scope, qdmEngineData(v4)->changeProto.value());
QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4));
object->setPrototypeOf(changeProto);
object->d()->change = change;
diff --git a/src/qmlmodels/qqmldelegatemodel_p.h b/src/qmlmodels/qqmldelegatemodel_p.h
index bfb5b6d38a..2eeffb9e0c 100644
--- a/src/qmlmodels/qqmldelegatemodel_p.h
+++ b/src/qmlmodels/qqmldelegatemodel_p.h
@@ -29,13 +29,12 @@ QT_BEGIN_NAMESPACE
class QQmlChangeSet;
class QQuickPackage;
-class QQmlV4Function;
class QQmlDelegateModelGroup;
class QQmlDelegateModelAttached;
class QQmlDelegateModelPrivate;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus
+class Q_QMLMODELS_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlDelegateModel)
@@ -114,7 +113,7 @@ private Q_SLOTS:
void _q_itemsInserted(int index, int count);
void _q_itemsRemoved(int index, int count);
void _q_itemsMoved(int from, int to, int count);
- void _q_modelReset();
+ void _q_modelAboutToBeReset();
void _q_rowsInserted(const QModelIndex &,int,int);
void _q_columnsInserted(const QModelIndex &, int, int);
void _q_columnsRemoved(const QModelIndex &, int, int);
@@ -126,13 +125,14 @@ private Q_SLOTS:
void _q_layoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint);
private:
+ void handleModelReset();
bool isDescendantOf(const QPersistentModelIndex &desc, const QList<QPersistentModelIndex> &parents) const;
Q_DISABLE_COPY(QQmlDelegateModel)
};
class QQmlDelegateModelGroupPrivate;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject
+class Q_QMLMODELS_EXPORT QQmlDelegateModelGroup : public QObject
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
@@ -156,14 +156,14 @@ public:
Q_INVOKABLE QJSValue get(int index);
public Q_SLOTS:
- void insert(QQmlV4Function *);
- void create(QQmlV4Function *);
- void resolve(QQmlV4Function *);
- void remove(QQmlV4Function *);
- void addGroups(QQmlV4Function *);
- void removeGroups(QQmlV4Function *);
- void setGroups(QQmlV4Function *);
- void move(QQmlV4Function *);
+ void insert(QQmlV4FunctionPtr);
+ void create(QQmlV4FunctionPtr);
+ void resolve(QQmlV4FunctionPtr);
+ void remove(QQmlV4FunctionPtr);
+ void addGroups(QQmlV4FunctionPtr);
+ void removeGroups(QQmlV4FunctionPtr);
+ void setGroups(QQmlV4FunctionPtr);
+ void move(QQmlV4FunctionPtr);
Q_SIGNALS:
void countChanged();
@@ -179,13 +179,13 @@ class QQmlDelegateModelAttachedMetaObject;
class QQmlDelegateModelAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT)
- Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
- Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged)
- Q_PROPERTY(bool inPersistedItems READ inPersistedItems WRITE setInPersistedItems NOTIFY groupsChanged)
- Q_PROPERTY(bool inItems READ inItems WRITE setInItems NOTIFY groupsChanged)
- Q_PROPERTY(int persistedItemsIndex READ persistedItemsIndex NOTIFY groupsChanged)
- Q_PROPERTY(int itemsIndex READ itemsIndex NOTIFY groupsChanged)
+ Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT FINAL)
+ Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged FINAL)
+ Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged FINAL)
+ Q_PROPERTY(bool inPersistedItems READ inPersistedItems WRITE setInPersistedItems NOTIFY groupsChanged FINAL)
+ Q_PROPERTY(bool inItems READ inItems WRITE setInItems NOTIFY groupsChanged FINAL)
+ Q_PROPERTY(int persistedItemsIndex READ persistedItemsIndex NOTIFY groupsChanged FINAL)
+ Q_PROPERTY(int itemsIndex READ itemsIndex NOTIFY groupsChanged FINAL)
public:
QQmlDelegateModelAttached(QObject *parent);
@@ -232,7 +232,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlDelegateModel)
-QML_DECLARE_TYPE(QQmlDelegateModelGroup)
-
#endif // QQMLDATAMODEL_P_H
diff --git a/src/qmlmodels/qqmldelegatemodel_p_p.h b/src/qmlmodels/qqmldelegatemodel_p_p.h
index a6ab5a970d..3c7ab9281d 100644
--- a/src/qmlmodels/qqmldelegatemodel_p_p.h
+++ b/src/qmlmodels/qqmldelegatemodel_p_p.h
@@ -14,6 +14,7 @@
#include <private/qqmlopenmetaobject_p.h>
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qpointer.h>
//
// W A R N I N G
@@ -37,7 +38,8 @@ typedef QQmlListCompositor Compositor;
class QQmlDelegateModelAttachedMetaObject;
class QQmlAbstractDelegateComponent;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount
+class Q_QMLMODELS_EXPORT QQmlDelegateModelItemMetaType final
+ : public QQmlRefCounted<QQmlDelegateModelItemMetaType>
{
public:
QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames);
@@ -231,7 +233,7 @@ public:
bool parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const;
bool parseGroupArgs(
- QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const;
+ QQmlV4FunctionPtr args, Compositor::Group *group, int *index, int *count, int *groups) const;
Compositor::Group group;
QPointer<QQmlDelegateModel> model;
@@ -301,7 +303,12 @@ public:
void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override;
void delegateChanged(bool add = true, bool remove = true);
- bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups);
+ enum class InsertionResult {
+ Success,
+ Error,
+ Retry
+ };
+ InsertionResult insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups);
int adaptorModelCount() const;
@@ -354,7 +361,7 @@ public:
class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter
{
Q_OBJECT
- Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup)
+ Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup FINAL)
public:
QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = nullptr);
~QQmlPartsModel();
@@ -419,7 +426,9 @@ public:
QList<QQmlPartsModel *> models;
};
-class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount
+class QQmlDelegateModelAttachedMetaObject final
+ : public QAbstractDynamicMetaObject,
+ public QQmlRefCounted<QQmlDelegateModelAttachedMetaObject>
{
public:
QQmlDelegateModelAttachedMetaObject(
diff --git a/src/qmlmodels/qqmldmabstractitemmodeldata.cpp b/src/qmlmodels/qqmldmabstractitemmodeldata.cpp
new file mode 100644
index 0000000000..bf4a0226b1
--- /dev/null
+++ b/src/qmlmodels/qqmldmabstractitemmodeldata.cpp
@@ -0,0 +1,253 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <private/qqmldmabstractitemmodeldata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlDMAbstractItemModelData::QQmlDMAbstractItemModelData(
+ const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ VDMAbstractItemModelDataType *dataType, int index, int row, int column)
+ : QQmlDelegateModelItem(metaType, dataType, index, row, column)
+ , m_type(dataType)
+{
+ if (index == -1)
+ m_cachedData.resize(m_type->propertyRoles.size());
+
+ QObjectPrivate::get(this)->metaObject = m_type;
+
+ m_type->addref();
+}
+
+int QQmlDMAbstractItemModelData::metaCall(QMetaObject::Call call, int id, void **arguments)
+{
+ if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) {
+ const int propertyIndex = id - m_type->propertyOffset;
+ if (index == -1) {
+ if (!m_cachedData.isEmpty())
+ *static_cast<QVariant *>(arguments[0]) = m_cachedData.at(propertyIndex);
+ } else if (*m_type->model) {
+ *static_cast<QVariant *>(arguments[0]) = value(m_type->propertyRoles.at(propertyIndex));
+ }
+ return -1;
+ } else if (call == QMetaObject::WriteProperty && id >= m_type->propertyOffset) {
+ const int propertyIndex = id - m_type->propertyOffset;
+ const QMetaObject *meta = metaObject();
+ if (index == -1) {
+ if (m_cachedData.size() > 1) {
+ m_cachedData[propertyIndex] = *static_cast<QVariant *>(arguments[0]);
+ QMetaObject::activate(this, meta, propertyIndex, nullptr);
+ } else if (m_cachedData.size() == 1) {
+ m_cachedData[0] = *static_cast<QVariant *>(arguments[0]);
+ QMetaObject::activate(this, meta, 0, nullptr);
+ }
+ } else if (*m_type->model) {
+ QQmlGuard<QQmlDMAbstractItemModelData> guard(this);
+ setValue(m_type->propertyRoles.at(propertyIndex), *static_cast<QVariant *>(arguments[0]));
+ if (guard.isNull())
+ return -1;
+
+ QMetaObject::activate(this, meta, propertyIndex, nullptr);
+ }
+ emit modelDataChanged();
+ return -1;
+ } else {
+ return qt_metacall(call, id, arguments);
+ }
+}
+
+void QQmlDMAbstractItemModelData::setValue(const QString &role, const QVariant &value)
+{
+ // Used only for initialization of the cached data. Does not have to emit change signals.
+
+ if (m_type->propertyRoles.size() == 1
+ && (role.isEmpty() || role == QLatin1String("modelData"))) {
+ // If the model has only a single role, the modelData is that role.
+ m_cachedData[0] = value;
+ return;
+ }
+
+ const auto it = m_type->roleNames.constFind(role.toUtf8());
+ if (it != m_type->roleNames.cend()) {
+ for (int i = 0; i < m_type->propertyRoles.size(); ++i) {
+ if (m_type->propertyRoles.at(i) == *it) {
+ m_cachedData[i] = value;
+ return;
+ }
+ }
+ }
+}
+
+bool QQmlDMAbstractItemModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx)
+{
+ if (index == -1) {
+ Q_ASSERT(idx >= 0);
+ m_cachedData.clear();
+ setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx));
+ const QMetaObject *meta = metaObject();
+ const int propertyCount = m_type->propertyRoles.size();
+ for (int i = 0; i < propertyCount; ++i)
+ QMetaObject::activate(this, meta, i, nullptr);
+ emit modelDataChanged();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+QV4::ReturnedValue QQmlDMAbstractItemModelData::get_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
+ if (!o)
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
+
+ const qsizetype propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
+
+ QQmlDMAbstractItemModelData *modelData = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item);
+ if (o->d()->item->index == -1) {
+ if (!modelData->m_cachedData.isEmpty())
+ return scope.engine->fromVariant(modelData->m_cachedData.at(propertyId));
+ } else if (*modelData->m_type->model) {
+ return scope.engine->fromVariant(
+ modelData->value(modelData->m_type->propertyRoles.at(propertyId)));
+ }
+ return QV4::Encode::undefined();
+}
+
+QV4::ReturnedValue QQmlDMAbstractItemModelData::set_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
+ if (!o)
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ const qsizetype propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
+
+ if (o->d()->item->index == -1) {
+ QQmlDMAbstractItemModelData *modelData = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item);
+ if (!modelData->m_cachedData.isEmpty()) {
+ if (modelData->m_cachedData.size() > 1) {
+ modelData->m_cachedData[propertyId]
+ = QV4::ExecutionEngine::toVariant(argv[0], QMetaType {});
+ QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), propertyId, nullptr);
+ } else if (modelData->m_cachedData.size() == 1) {
+ modelData->m_cachedData[0] = QV4::ExecutionEngine::toVariant(argv[0], QMetaType {});
+ QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 0, nullptr);
+ }
+ emit modelData->modelDataChanged();
+ }
+ }
+ return QV4::Encode::undefined();
+}
+
+QV4::ReturnedValue QQmlDMAbstractItemModelData::get_modelData(
+ const QV4::FunctionObject *b, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc)
+{
+ Q_UNUSED(argv)
+ Q_UNUSED(argc)
+
+ QV4::Scope scope(b);
+ QV4::Scoped<QQmlDelegateModelItemObject> o(
+ scope, thisObject->as<QQmlDelegateModelItemObject>());
+ if (!o)
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
+
+ return scope.engine->fromVariant(
+ static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->modelData());
+}
+
+QV4::ReturnedValue QQmlDMAbstractItemModelData::set_modelData(
+ const QV4::FunctionObject *b, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
+ if (!o)
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->setModelData(
+ QV4::ExecutionEngine::toVariant(argv[0], QMetaType()));
+
+ return QV4::Encode::undefined();
+}
+
+QVariant QQmlDMAbstractItemModelData::modelData() const
+{
+ if (m_type->propertyRoles.size() == 1) {
+ // If the model has only a single role, the modelData is that role.
+ return index == -1
+ ? m_cachedData.isEmpty() ? QVariant() : m_cachedData[0]
+ : value(m_type->propertyRoles[0]);
+ }
+
+ // If we're using context properties, the model object is also the context object.
+ // In that case we cannot provide modelData. Otherwise return the object itself as modelData.
+ return (contextData->contextObject() == this)
+ ? QVariant()
+ : QVariant::fromValue(this);
+}
+
+void QQmlDMAbstractItemModelData::setModelData(const QVariant &modelData)
+{
+ if (m_type->propertyRoles.size() != 1) {
+ qWarning() << "Cannot overwrite model object";
+ return;
+ }
+
+ // If the model has only a single role, the modelData is that role.
+ if (index == -1) {
+ if (m_cachedData.isEmpty())
+ m_cachedData.append(modelData);
+ else
+ m_cachedData[0] = modelData;
+ } else {
+ setValue(m_type->propertyRoles[0], modelData);
+ }
+
+ QMetaObject::activate(this, metaObject(), 0, nullptr);
+ emit modelDataChanged();
+}
+
+bool QQmlDMAbstractItemModelData::hasModelChildren() const
+{
+ if (index >= 0) {
+ if (const QAbstractItemModel *const model = m_type->model->aim())
+ return model->hasChildren(model->index(row, column, m_type->model->rootIndex));
+ }
+ return false;
+}
+
+QVariant QQmlDMAbstractItemModelData::value(int role) const
+{
+ if (const QAbstractItemModel *aim = m_type->model->aim())
+ return aim->index(row, column, m_type->model->rootIndex).data(role);
+ return QVariant();
+}
+
+void QQmlDMAbstractItemModelData::setValue(int role, const QVariant &value)
+{
+ if (QAbstractItemModel *aim = m_type->model->aim())
+ aim->setData(aim->index(row, column, m_type->model->rootIndex), value, role);
+}
+
+QV4::ReturnedValue QQmlDMAbstractItemModelData::get()
+{
+ if (m_type->prototype.isUndefined()) {
+ QQmlAdaptorModelEngineData * const data = QQmlAdaptorModelEngineData::get(v4);
+ m_type->initializeConstructor(data);
+ }
+ QV4::Scope scope(v4);
+ QV4::ScopedObject proto(scope, m_type->prototype.value());
+ QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
+ o->setPrototypeOf(proto);
+ ++scriptRef;
+ return o.asReturnedValue();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmldmabstractitemmodeldata_p.h b/src/qmlmodels/qqmldmabstractitemmodeldata_p.h
new file mode 100644
index 0000000000..e3769b6d98
--- /dev/null
+++ b/src/qmlmodels/qqmldmabstractitemmodeldata_p.h
@@ -0,0 +1,334 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLDMABSTRACTITEMMODELDATA_P_H
+#define QQMLDMABSTRACTITEMMODELDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmladaptormodelenginedata_p.h>
+#include <private/qqmldelegatemodel_p_p.h>
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class VDMAbstractItemModelDataType;
+class QQmlDMAbstractItemModelData : public QQmlDelegateModelItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
+ QT_ANONYMOUS_PROPERTY(QVariant READ modelData NOTIFY modelDataChanged FINAL)
+
+public:
+ QQmlDMAbstractItemModelData(
+ const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ VDMAbstractItemModelDataType *dataType,
+ int index, int row, int column);
+
+ int metaCall(QMetaObject::Call call, int id, void **arguments);
+ bool hasModelChildren() const;
+
+ QV4::ReturnedValue get() override;
+ void setValue(const QString &role, const QVariant &value) override;
+ bool resolveIndex(const QQmlAdaptorModel &model, int idx) override;
+
+ static QV4::ReturnedValue get_property(
+ const QV4::FunctionObject *b, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue set_property(
+ const QV4::FunctionObject *b, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc);
+
+ static QV4::ReturnedValue get_modelData(
+ const QV4::FunctionObject *b, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue set_modelData(
+ const QV4::FunctionObject *b, const QV4::Value *thisObject,
+ const QV4::Value *argv, int argc);
+
+ QVariant modelData() const;
+ void setModelData(const QVariant &modelData);
+
+ const VDMAbstractItemModelDataType *type() const { return m_type; }
+
+Q_SIGNALS:
+ void modelDataChanged();
+
+private:
+ QVariant value(int role) const;
+ void setValue(int role, const QVariant &value);
+
+ VDMAbstractItemModelDataType *m_type;
+ QVector<QVariant> m_cachedData;
+};
+
+class VDMAbstractItemModelDataType final
+ : public QQmlRefCounted<VDMAbstractItemModelDataType>
+ , public QQmlAdaptorModel::Accessors
+ , public QAbstractDynamicMetaObject
+{
+public:
+ VDMAbstractItemModelDataType(QQmlAdaptorModel *model)
+ : model(model)
+ , propertyOffset(0)
+ , signalOffset(0)
+ {
+ }
+
+ bool notify(
+ const QQmlAdaptorModel &,
+ const QList<QQmlDelegateModelItem *> &items,
+ int index,
+ int count,
+ const QVector<int> &roles) const override
+ {
+ bool changed = roles.isEmpty() && !watchedRoles.isEmpty();
+ if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) {
+ QList<int> roleIds;
+ for (const QByteArray &r : watchedRoles) {
+ QHash<QByteArray, int>::const_iterator it = roleNames.find(r);
+ if (it != roleNames.end())
+ roleIds << it.value();
+ }
+ const_cast<VDMAbstractItemModelDataType *>(this)->watchedRoleIds = roleIds;
+ }
+
+ QVector<int> signalIndexes;
+ for (int i = 0; i < roles.size(); ++i) {
+ const int role = roles.at(i);
+ if (!changed && watchedRoleIds.contains(role))
+ changed = true;
+
+ int propertyId = propertyRoles.indexOf(role);
+ if (propertyId != -1)
+ signalIndexes.append(propertyId + signalOffset);
+ }
+ if (roles.isEmpty()) {
+ const int propertyRolesCount = propertyRoles.size();
+ signalIndexes.reserve(propertyRolesCount);
+ for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId)
+ signalIndexes.append(propertyId + signalOffset);
+ }
+
+ QVarLengthArray<QQmlGuard<QQmlDMAbstractItemModelData>> guardedItems;
+ for (const auto item : items) {
+ Q_ASSERT(qobject_cast<QQmlDMAbstractItemModelData *>(item) == item);
+ guardedItems.append(static_cast<QQmlDMAbstractItemModelData *>(item));
+ }
+
+ for (const auto &item : std::as_const(guardedItems)) {
+ if (item.isNull())
+ continue;
+
+ const int idx = item->modelIndex();
+ if (idx >= index && idx < index + count) {
+ for (int i = 0; i < signalIndexes.size(); ++i)
+ QMetaObject::activate(item, signalIndexes.at(i), nullptr);
+ emit item->modelDataChanged();
+ }
+ }
+ return changed;
+ }
+
+ void replaceWatchedRoles(
+ QQmlAdaptorModel &,
+ const QList<QByteArray> &oldRoles,
+ const QList<QByteArray> &newRoles) const override
+ {
+ VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
+
+ dataType->watchedRoleIds.clear();
+ for (const QByteArray &oldRole : oldRoles)
+ dataType->watchedRoles.removeOne(oldRole);
+ dataType->watchedRoles += newRoles;
+ }
+
+ static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+ {
+ QV4::Scope scope(b);
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
+ if (!o)
+ RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
+
+ const QQmlAdaptorModel *const model
+ = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->type()->model;
+ if (o->d()->item->index >= 0) {
+ if (const QAbstractItemModel *const aim = model->aim())
+ RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))));
+ }
+ RETURN_RESULT(QV4::Encode(false));
+ }
+
+
+ void initializeConstructor(QQmlAdaptorModelEngineData *const data)
+ {
+ QV4::ExecutionEngine *v4 = data->v4;
+ QV4::Scope scope(v4);
+ QV4::ScopedObject proto(scope, v4->newObject());
+ proto->defineAccessorProperty(QStringLiteral("index"), QQmlAdaptorModelEngineData::get_index, nullptr);
+ proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), get_hasModelChildren, nullptr);
+ proto->defineAccessorProperty(QStringLiteral("modelData"),
+ QQmlDMAbstractItemModelData::get_modelData,
+ QQmlDMAbstractItemModelData::set_modelData);
+ QV4::ScopedProperty p(scope);
+
+ typedef QHash<QByteArray, int>::const_iterator iterator;
+ for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) {
+ const qsizetype propertyId = propertyRoles.indexOf(it.value());
+ const QByteArray &propertyName = it.key();
+
+ QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName)));
+ QV4::ExecutionContext *global = v4->rootContext();
+ QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMAbstractItemModelData::get_property));
+ QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMAbstractItemModelData::set_property));
+ p->setGetter(g);
+ p->setSetter(s);
+ proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
+ }
+ prototype.set(v4, proto);
+ }
+
+ // QAbstractDynamicMetaObject
+
+ void objectDestroyed(QObject *) override
+ {
+ release();
+ }
+
+ int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override
+ {
+ return static_cast<QQmlDMAbstractItemModelData *>(object)->metaCall(call, id, arguments);
+ }
+
+ int rowCount(const QQmlAdaptorModel &model) const override
+ {
+ if (const QAbstractItemModel *aim = model.aim())
+ return aim->rowCount(model.rootIndex);
+ return 0;
+ }
+
+ int columnCount(const QQmlAdaptorModel &model) const override
+ {
+ if (const QAbstractItemModel *aim = model.aim())
+ return aim->columnCount(model.rootIndex);
+ return 0;
+ }
+
+ void cleanup(QQmlAdaptorModel &) const override
+ {
+ release();
+ }
+
+ QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
+ {
+ if (!metaObject) {
+ VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
+ dataType->initializeMetaType(model);
+ }
+
+ if (const QAbstractItemModel *aim = model.aim()) {
+ const QModelIndex modelIndex
+ = aim->index(model.rowAt(index), model.columnAt(index), model.rootIndex);
+
+ const auto it = roleNames.find(role.toUtf8()), end = roleNames.end();
+ if (it != roleNames.end())
+ return modelIndex.data(*it);
+
+ if (role.isEmpty() || role == QLatin1String("modelData")) {
+ if (roleNames.size() == 1)
+ return modelIndex.data(roleNames.begin().value());
+
+ QVariantMap modelData;
+ for (auto jt = roleNames.begin(); jt != end; ++jt)
+ modelData.insert(QString::fromUtf8(jt.key()), modelIndex.data(jt.value()));
+ return modelData;
+ }
+
+ if (role == QLatin1String("hasModelChildren"))
+ return QVariant(aim->hasChildren(modelIndex));
+ }
+ return QVariant();
+ }
+
+ QVariant parentModelIndex(const QQmlAdaptorModel &model) const override
+ {
+ if (const QAbstractItemModel *aim = model.aim())
+ return QVariant::fromValue(aim->parent(model.rootIndex));
+ return QVariant();
+ }
+
+ QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
+ {
+ if (const QAbstractItemModel *aim = model.aim())
+ return QVariant::fromValue(aim->index(model.rowAt(index), model.columnAt(index),
+ model.rootIndex));
+ return QVariant();
+ }
+
+ bool canFetchMore(const QQmlAdaptorModel &model) const override
+ {
+ if (const QAbstractItemModel *aim = model.aim())
+ return aim->canFetchMore(model.rootIndex);
+ return false;
+ }
+
+ void fetchMore(QQmlAdaptorModel &model) const override
+ {
+ if (QAbstractItemModel *aim = model.aim())
+ aim->fetchMore(model.rootIndex);
+ }
+
+ QQmlDelegateModelItem *createItem(
+ QQmlAdaptorModel &model,
+ const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ int index, int row, int column) override
+ {
+ if (!metaObject)
+ initializeMetaType(model);
+ return new QQmlDMAbstractItemModelData(metaType, this, index, row, column);
+ }
+
+ void initializeMetaType(const QQmlAdaptorModel &model)
+ {
+ QMetaObjectBuilder builder;
+ QQmlAdaptorModelEngineData::setModelDataType<QQmlDMAbstractItemModelData>(&builder, this);
+
+ const QByteArray propertyType = QByteArrayLiteral("QVariant");
+ const QAbstractItemModel *aim = model.aim();
+ const QHash<int, QByteArray> names = aim ? aim->roleNames() : QHash<int, QByteArray>();
+ for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) {
+ const int propertyId = propertyRoles.size();
+ propertyRoles.append(it.key());
+ roleNames.insert(it.value(), it.key());
+ QQmlAdaptorModelEngineData::addProperty(&builder, propertyId, it.value(), propertyType);
+ }
+
+ metaObject.reset(builder.toMetaObject());
+ *static_cast<QMetaObject *>(this) = *metaObject;
+ propertyCache = QQmlPropertyCache::createStandalone(
+ metaObject.data(), model.modelItemRevision);
+ }
+
+ QV4::PersistentValue prototype;
+ QList<int> propertyRoles;
+ QList<int> watchedRoleIds;
+ QList<QByteArray> watchedRoles;
+ QHash<QByteArray, int> roleNames;
+ QQmlAdaptorModel *model;
+ int propertyOffset;
+ int signalOffset;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLDMABSTRACTITEMMODELDATA_P_H
diff --git a/src/qmlmodels/qqmldmlistaccessordata.cpp b/src/qmlmodels/qqmldmlistaccessordata.cpp
new file mode 100644
index 0000000000..bfd353771c
--- /dev/null
+++ b/src/qmlmodels/qqmldmlistaccessordata.cpp
@@ -0,0 +1,157 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <private/qqmldmlistaccessordata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlDMListAccessorData::QQmlDMListAccessorData(
+ const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ VDMListDelegateDataType *dataType,
+ int index, int row, int column, const QVariant &value)
+ : QQmlDelegateModelItem(metaType, dataType, index, row, column)
+ , cachedData(value)
+{
+ QObjectPrivate::get(this)->metaObject = dataType;
+ dataType->addref();
+}
+
+QQmlDMListAccessorData::~QQmlDMListAccessorData()
+{
+ QObjectPrivate *d = QObjectPrivate::get(this);
+ static_cast<VDMListDelegateDataType *>(d->metaObject)->release();
+ d->metaObject = nullptr;
+}
+
+void QQmlDMListAccessorData::setModelData(const QVariant &data) {
+ if (data == cachedData)
+ return;
+
+ cachedData = data;
+ cachedDataClean = false;
+ static_cast<const VDMListDelegateDataType *>(QObjectPrivate::get(this)->metaObject)
+ ->emitAllSignals(this);
+}
+
+void QQmlDMListAccessorData::setValue(const QString &role, const QVariant &value)
+{
+ // Used only for initialization of the cached data. Does not have to emit change signals.
+ Q_ASSERT(!cachedDataClean);
+
+ if (role == QLatin1String("modelData") || role.isEmpty())
+ cachedData = value;
+ else
+ VDMListDelegateDataType::setValue(&cachedData, role, value);
+}
+
+bool QQmlDMListAccessorData::resolveIndex(const QQmlAdaptorModel &model, int idx)
+{
+ if (index == -1) {
+ index = idx;
+ setModelData(model.list.at(idx));
+ emit modelIndexChanged();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void VDMListDelegateDataType::emitAllSignals(QQmlDMListAccessorData *accessor) const
+{
+ for (int i = propertyOffset, end = propertyCount(); i != end; ++i)
+ QMetaObject::activate(accessor, this, i - propertyOffset, nullptr);
+ emit accessor->modelDataChanged();
+}
+
+int VDMListDelegateDataType::metaCall(
+ QObject *object, QMetaObject::Call call, int id, void **arguments)
+{
+ Q_ASSERT(qobject_cast<QQmlDMListAccessorData *>(object));
+ QQmlDMListAccessorData *accessor = static_cast<QQmlDMListAccessorData *>(object);
+
+ switch (call) {
+ case QMetaObject::ReadProperty: {
+ if (id < propertyOffset)
+ break;
+
+ QVariant *result = static_cast<QVariant *>(arguments[0]);
+ const QByteArray name = property(id).name();
+ const QVariant data = accessor->index == -1
+ ? accessor->modelData()
+ : model->list.at(accessor->index);
+ *result = value(&data, name);
+ return -1;
+ }
+ case QMetaObject::WriteProperty: {
+ if (id < propertyOffset)
+ break;
+
+ const QVariant &argument = *static_cast<QVariant *>(arguments[0]);
+ const QByteArray name = property(id).name();
+ QVariant data = accessor->index == -1
+ ? accessor->modelData()
+ : model->list.at(accessor->index);
+ if (argument == value(&data, name))
+ return -1;
+ setValue(&data, name, argument);
+ if (accessor->index == -1) {
+ accessor->cachedData = data;
+ accessor->cachedDataClean = false;
+ } else {
+ model->list.set(accessor->index, data);
+ }
+ QMetaObject::activate(accessor, this, id - propertyOffset, nullptr);
+ emit accessor->modelDataChanged();
+ return -1;
+ }
+ default:
+ break;
+ }
+
+ return accessor->qt_metacall(call, id, arguments);
+}
+
+int VDMListDelegateDataType::createProperty(const char *name, const char *)
+{
+ const int propertyIndex = propertyCount() - propertyOffset;
+
+ // We use QVariant because the types may be different in the different objects.
+ QQmlAdaptorModelEngineData::addProperty(
+ &builder, propertyIndex, name, QByteArrayLiteral("QVariant"));
+
+ metaObject.reset(builder.toMetaObject());
+ *static_cast<QMetaObject *>(this) = *metaObject;
+ return propertyIndex + propertyOffset;
+}
+
+QMetaObject *VDMListDelegateDataType::toDynamicMetaObject(QObject *object)
+{
+ if (const QQmlRefPointer<QQmlContextData> &contextData
+ = static_cast<QQmlDMListAccessorData *>(object)->contextData) {
+ if (contextData->contextObject() == object) {
+ // We are using context properties. There should be a propertyCache so that row and
+ // column are hidden. We shall also return the static metaObject in that case.
+
+ if (!propertyCache) {
+ propertyCache = QQmlPropertyCache::createStandalone(
+ &QQmlDMListAccessorData::staticMetaObject, model->modelItemRevision);
+ if (QQmlData *ddata = QQmlData::get(object, true))
+ ddata->propertyCache = propertyCache;
+ }
+
+ // ### Qt 7: Return const from toDynamicMetaObject() and drop the const_cast.
+ return const_cast<QMetaObject *>(&QQmlDMListAccessorData::staticMetaObject);
+ }
+ }
+
+ // If the context object is not the model object, we are using required properties.
+ // In that case, create any extra properties.
+ QQmlDMListAccessorData *data = static_cast<QQmlDMListAccessorData *>(object);
+ if (!data->cachedDataClean) {
+ createMissingProperties(&data->cachedData);
+ data->cachedDataClean = true;
+ }
+ return this;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmldmlistaccessordata_p.h b/src/qmlmodels/qqmldmlistaccessordata_p.h
new file mode 100644
index 0000000000..e02c3a88f6
--- /dev/null
+++ b/src/qmlmodels/qqmldmlistaccessordata_p.h
@@ -0,0 +1,293 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLDMLISTACCESSORDATA_P_H
+#define QQMLDMLISTACCESSORDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmladaptormodelenginedata_p.h>
+#include <private/qqmldelegatemodel_p_p.h>
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class VDMListDelegateDataType;
+
+class QQmlDMListAccessorData : public QQmlDelegateModelItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
+ QT_ANONYMOUS_PROPERTY(QVariant READ modelData WRITE setModelData NOTIFY modelDataChanged FINAL)
+public:
+ QQmlDMListAccessorData(
+ const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ VDMListDelegateDataType *dataType, int index, int row, int column,
+ const QVariant &value);
+ ~QQmlDMListAccessorData();
+
+ QVariant modelData() const
+ {
+ return cachedData;
+ }
+
+ void setModelData(const QVariant &data);
+
+ static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+ {
+ QV4::ExecutionEngine *v4 = b->engine();
+ const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
+ if (!o)
+ return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
+
+ return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData);
+ }
+
+ static QV4::ReturnedValue set_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+ {
+ QV4::ExecutionEngine *v4 = b->engine();
+ const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
+ if (!o)
+ return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
+ if (!argc)
+ return v4->throwTypeError();
+
+ static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(
+ QV4::ExecutionEngine::toVariant(argv[0], QMetaType {}));
+ return QV4::Encode::undefined();
+ }
+
+ QV4::ReturnedValue get() override
+ {
+ QQmlAdaptorModelEngineData *data = QQmlAdaptorModelEngineData::get(v4);
+ QV4::Scope scope(v4);
+ QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
+ QV4::ScopedObject p(scope, data->listItemProto.value());
+ o->setPrototypeOf(p);
+ ++scriptRef;
+ return o.asReturnedValue();
+ }
+
+ void setValue(const QString &role, const QVariant &value) override;
+ bool resolveIndex(const QQmlAdaptorModel &model, int idx) override;
+
+Q_SIGNALS:
+ void modelDataChanged();
+
+private:
+ friend class VDMListDelegateDataType;
+ QVariant cachedData;
+
+ // Gets cleaned when the metaobject has processed it.
+ bool cachedDataClean = false;
+};
+
+
+class VDMListDelegateDataType final
+ : public QQmlRefCounted<VDMListDelegateDataType>
+ , public QQmlAdaptorModel::Accessors
+ , public QAbstractDynamicMetaObject
+{
+public:
+ VDMListDelegateDataType(QQmlAdaptorModel *model)
+ : model(model)
+ {
+ QQmlAdaptorModelEngineData::setModelDataType<QQmlDMListAccessorData>(&builder, this);
+ metaObject.reset(builder.toMetaObject());
+ *static_cast<QMetaObject *>(this) = *metaObject.data();
+ }
+
+ void cleanup(QQmlAdaptorModel &) const override
+ {
+ release();
+ }
+
+ int rowCount(const QQmlAdaptorModel &model) const override
+ {
+ return model.list.count();
+ }
+
+ int columnCount(const QQmlAdaptorModel &model) const override
+ {
+ switch (model.list.type()) {
+ case QQmlListAccessor::Invalid:
+ return 0;
+ case QQmlListAccessor::StringList:
+ case QQmlListAccessor::UrlList:
+ case QQmlListAccessor::Integer:
+ return 1;
+ default:
+ break;
+ }
+
+ // If there are no properties, we can get modelData itself.
+ return std::max(1, propertyCount() - propertyOffset);
+ }
+
+ static const QMetaObject *metaObjectFromType(QMetaType type)
+ {
+ if (const QMetaObject *metaObject = type.metaObject())
+ return metaObject;
+
+ // NB: This acquires the lock on QQmlMetaTypeData. If we had a QQmlEngine here,
+ // we could use QQmlGadgetPtrWrapper::instance() to avoid this.
+ if (const QQmlValueType *valueType = QQmlMetaType::valueType(type))
+ return valueType->staticMetaObject();
+
+ return nullptr;
+ }
+
+ template<typename String>
+ static QString toQString(const String &string)
+ {
+ if constexpr (std::is_same_v<String, QString>)
+ return string;
+ else if constexpr (std::is_same_v<String, QByteArray>)
+ return QString::fromUtf8(string);
+ else if constexpr (std::is_same_v<String, const char *>)
+ return QString::fromUtf8(string);
+ Q_UNREACHABLE_RETURN(QString());
+ }
+
+ template<typename String>
+ static QByteArray toUtf8(const String &string)
+ {
+ if constexpr (std::is_same_v<String, QString>)
+ return string.toUtf8();
+ else if constexpr (std::is_same_v<String, QByteArray>)
+ return string;
+ else if constexpr (std::is_same_v<String, const char *>)
+ return QByteArray::fromRawData(string, qstrlen(string));
+ Q_UNREACHABLE_RETURN(QByteArray());
+ }
+
+ template<typename String>
+ static QVariant value(const QVariant *row, const String &role)
+ {
+ const QMetaType type = row->metaType();
+ if (type == QMetaType::fromType<QVariantMap>())
+ return row->toMap().value(toQString(role));
+
+ if (type == QMetaType::fromType<QVariantHash>())
+ return row->toHash().value(toQString(role));
+
+ const QMetaType::TypeFlags typeFlags = type.flags();
+ if (typeFlags & QMetaType::PointerToQObject)
+ return row->value<QObject *>()->property(toUtf8(role));
+
+ if (const QMetaObject *metaObject = metaObjectFromType(type)) {
+ const int propertyIndex = metaObject->indexOfProperty(toUtf8(role));
+ if (propertyIndex >= 0)
+ return metaObject->property(propertyIndex).readOnGadget(row->constData());
+ }
+
+ return QVariant();
+ }
+
+ template<typename String>
+ void createPropertyIfMissing(const String &string)
+ {
+ for (int i = 0, end = propertyCount(); i < end; ++i) {
+ if (QAnyStringView(property(i).name()) == QAnyStringView(string))
+ return;
+ }
+
+ createProperty(toUtf8(string), nullptr);
+ }
+
+ void createMissingProperties(const QVariant *row)
+ {
+ const QMetaType type = row->metaType();
+ if (type == QMetaType::fromType<QVariantMap>()) {
+ const QVariantMap map = row->toMap();
+ for (auto it = map.keyBegin(), end = map.keyEnd(); it != end; ++it)
+ createPropertyIfMissing(*it);
+ } else if (type == QMetaType::fromType<QVariantHash>()) {
+ const QVariantHash map = row->toHash();
+ for (auto it = map.keyBegin(), end = map.keyEnd(); it != end; ++it)
+ createPropertyIfMissing(*it);
+ } else if (type.flags() & QMetaType::PointerToQObject) {
+ const QMetaObject *metaObject = row->value<QObject *>()->metaObject();
+ for (int i = 0, end = metaObject->propertyCount(); i < end; ++i)
+ createPropertyIfMissing(metaObject->property(i).name());
+ } else if (const QMetaObject *metaObject = metaObjectFromType(type)) {
+ for (int i = 0, end = metaObject->propertyCount(); i < end; ++i)
+ createPropertyIfMissing(metaObject->property(i).name());
+ }
+ }
+
+ template<typename String>
+ static void setValue(QVariant *row, const String &role, const QVariant &value)
+ {
+ const QMetaType type = row->metaType();
+ if (type == QMetaType::fromType<QVariantMap>()) {
+ static_cast<QVariantMap *>(row->data())->insert(toQString(role), value);
+ } else if (type == QMetaType::fromType<QVariantHash>()) {
+ static_cast<QVariantHash *>(row->data())->insert(toQString(role), value);
+ } else if (type.flags() & QMetaType::PointerToQObject) {
+ row->value<QObject *>()->setProperty(toUtf8(role), value);
+ } else if (const QMetaObject *metaObject = metaObjectFromType(type)) {
+ const int propertyIndex = metaObject->indexOfProperty(toUtf8(role));
+ if (propertyIndex >= 0)
+ metaObject->property(propertyIndex).writeOnGadget(row->data(), value);
+ }
+ }
+
+ QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
+ {
+ const QVariant entry = model.list.at(index);
+ if (role == QLatin1String("modelData") || role.isEmpty())
+ return entry;
+
+ return value(&entry, role);
+ }
+
+ QQmlDelegateModelItem *createItem(
+ QQmlAdaptorModel &model,
+ const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ int index, int row, int column) override
+ {
+ const QVariant value = (index >= 0 && index < model.list.count())
+ ? model.list.at(index)
+ : QVariant();
+ return new QQmlDMListAccessorData(metaType, this, index, row, column, value);
+ }
+
+ bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
+ {
+ for (auto modelItem : items) {
+ const int modelItemIndex = modelItem->index;
+ if (modelItemIndex < index || modelItemIndex >= index + count)
+ continue;
+
+ auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem);
+ QVariant updatedModelData = model.list.at(listModelItem->index);
+ listModelItem->setModelData(updatedModelData);
+ }
+ return true;
+ }
+
+ void emitAllSignals(QQmlDMListAccessorData *accessor) const;
+
+ int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) final;
+ int createProperty(const char *name, const char *) final;
+ QMetaObject *toDynamicMetaObject(QObject *accessors) final;
+
+ QMetaObjectBuilder builder;
+ QQmlAdaptorModel *model = nullptr;
+ int propertyOffset = 0;
+ int signalOffset = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLDMLISTACCESSORDATA_P_H
diff --git a/src/qmlmodels/qqmldmobjectdata.cpp b/src/qmlmodels/qqmldmobjectdata.cpp
new file mode 100644
index 0000000000..d10d80d80a
--- /dev/null
+++ b/src/qmlmodels/qqmldmobjectdata.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <private/qqmldmobjectdata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlDMObjectData::QQmlDMObjectData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ VDMObjectDelegateDataType *dataType,
+ int index, int row, int column,
+ QObject *object)
+ : QQmlDelegateModelItem(metaType, dataType, index, row, column)
+ , object(object)
+{
+ new QQmlDMObjectDataMetaObject(this, dataType);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmldmobjectdata_p.h b/src/qmlmodels/qqmldmobjectdata_p.h
new file mode 100644
index 0000000000..0368572b36
--- /dev/null
+++ b/src/qmlmodels/qqmldmobjectdata_p.h
@@ -0,0 +1,247 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLDMOBJECTDATA_P_H
+#define QQMLDMOBJECTDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmladaptormodelenginedata_p.h>
+#include <private/qqmldelegatemodel_p_p.h>
+
+#include <private/qobject_p.h>
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class VDMObjectDelegateDataType;
+class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged)
+ QT_ANONYMOUS_PROPERTY(QObject * READ modelData NOTIFY modelDataChanged FINAL)
+ Q_INTERFACES(QQmlAdaptorModelProxyInterface)
+public:
+ QQmlDMObjectData(
+ const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ VDMObjectDelegateDataType *dataType,
+ int index, int row, int column,
+ QObject *object);
+
+ void setModelData(QObject *modelData)
+ {
+ if (modelData == object)
+ return;
+
+ object = modelData;
+ emit modelDataChanged();
+ }
+
+ QObject *modelData() const { return object; }
+ QObject *proxiedObject() override { return object; }
+
+ QPointer<QObject> object;
+
+Q_SIGNALS:
+ void modelDataChanged();
+};
+
+class VDMObjectDelegateDataType final
+ : public QQmlRefCounted<VDMObjectDelegateDataType>,
+ public QQmlAdaptorModel::Accessors
+{
+public:
+ int propertyOffset;
+ int signalOffset;
+ bool shared;
+ QMetaObjectBuilder builder;
+
+ VDMObjectDelegateDataType()
+ : propertyOffset(0)
+ , signalOffset(0)
+ , shared(true)
+ {
+ }
+
+ VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type)
+ : propertyOffset(type.propertyOffset)
+ , signalOffset(type.signalOffset)
+ , shared(false)
+ , builder(type.metaObject.data(), QMetaObjectBuilder::Properties
+ | QMetaObjectBuilder::Signals
+ | QMetaObjectBuilder::SuperClass
+ | QMetaObjectBuilder::ClassName)
+ {
+ builder.setFlags(MetaObjectFlag::DynamicMetaObject);
+ }
+
+ int rowCount(const QQmlAdaptorModel &model) const override
+ {
+ return model.list.count();
+ }
+
+ int columnCount(const QQmlAdaptorModel &) const override
+ {
+ return 1;
+ }
+
+ QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
+ {
+ if (QObject *object = model.list.at(index).value<QObject *>())
+ return object->property(role.toUtf8());
+ return QVariant();
+ }
+
+ QQmlDelegateModelItem *createItem(
+ QQmlAdaptorModel &model,
+ const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
+ int index, int row, int column) override
+ {
+ if (!metaObject)
+ initializeMetaType(model);
+ return index >= 0 && index < model.list.count()
+ ? new QQmlDMObjectData(metaType, this, index, row, column, qvariant_cast<QObject *>(model.list.at(index)))
+ : nullptr;
+ }
+
+ void initializeMetaType(QQmlAdaptorModel &model)
+ {
+ Q_UNUSED(model);
+ QQmlAdaptorModelEngineData::setModelDataType<QQmlDMObjectData>(&builder, this);
+
+ metaObject.reset(builder.toMetaObject());
+ // Note: ATM we cannot create a shared property cache for this class, since each model
+ // object can have different properties. And to make those properties available to the
+ // delegate, QQmlDMObjectData makes use of a QAbstractDynamicMetaObject subclass
+ // (QQmlDMObjectDataMetaObject), which we cannot represent in a QQmlPropertyCache.
+ // By not having a shared property cache, revisioned properties in QQmlDelegateModelItem
+ // will always be available to the delegate, regardless of the import version.
+ }
+
+ void cleanup(QQmlAdaptorModel &) const override
+ {
+ release();
+ }
+
+ bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
+ {
+ for (auto modelItem : items) {
+ const int modelItemIndex = modelItem->index;
+ if (modelItemIndex < index || modelItemIndex >= index + count)
+ continue;
+
+ auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem);
+ QObject *updatedModelData = qvariant_cast<QObject *>(model.list.at(objectModelItem->index));
+ objectModelItem->setModelData(updatedModelData);
+ }
+ return true;
+ }
+};
+
+class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
+{
+public:
+ QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type)
+ : m_data(data)
+ , m_type(type)
+ {
+ QObjectPrivate *op = QObjectPrivate::get(m_data);
+ *static_cast<QMetaObject *>(this) = *type->metaObject;
+ op->metaObject = this;
+ m_type->addref();
+ }
+
+ ~QQmlDMObjectDataMetaObject()
+ {
+ m_type->release();
+ }
+
+ int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override
+ {
+ Q_ASSERT(o == m_data);
+ Q_UNUSED(o);
+
+ static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
+ if (id >= m_type->propertyOffset
+ && (call == QMetaObject::ReadProperty
+ || call == QMetaObject::WriteProperty
+ || call == QMetaObject::ResetProperty)) {
+ if (m_data->object)
+ QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments);
+ return -1;
+ } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) {
+ QMetaObject::activate(m_data, this, id - m_type->signalOffset, nullptr);
+ return -1;
+ } else {
+ return m_data->qt_metacall(call, id, arguments);
+ }
+ }
+
+ int createProperty(const char *name, const char *) override
+ {
+ if (!m_data->object)
+ return -1;
+ const QMetaObject *metaObject = m_data->object->metaObject();
+ static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
+
+ const int previousPropertyCount = propertyCount() - propertyOffset();
+ int propertyIndex = metaObject->indexOfProperty(name);
+ if (propertyIndex == -1)
+ return -1;
+ if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount())
+ return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
+
+ if (m_type->shared) {
+ VDMObjectDelegateDataType *type = m_type;
+ m_type = new VDMObjectDelegateDataType(*m_type);
+ type->release();
+ }
+
+ const int previousMethodCount = methodCount();
+ int notifierId = previousMethodCount - methodOffset();
+ for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) {
+ QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset);
+ QMetaPropertyBuilder propertyBuilder;
+ if (property.hasNotifySignal()) {
+ m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()");
+ propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId);
+ ++notifierId;
+ } else {
+ propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName());
+ }
+ propertyBuilder.setWritable(property.isWritable());
+ propertyBuilder.setResettable(property.isResettable());
+ propertyBuilder.setConstant(property.isConstant());
+ }
+
+ m_type->metaObject.reset(m_type->builder.toMetaObject());
+ *static_cast<QMetaObject *>(this) = *m_type->metaObject;
+
+ notifierId = previousMethodCount;
+ for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) {
+ QMetaProperty property = metaObject->property(i + objectPropertyOffset);
+ if (property.hasNotifySignal()) {
+ QQmlPropertyPrivate::connect(
+ m_data->object, property.notifySignalIndex(), m_data, notifierId);
+ ++notifierId;
+ }
+ }
+ return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
+ }
+
+ QQmlDMObjectData *m_data;
+ VDMObjectDelegateDataType *m_type;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLDMOBJECTDATA_P_H
diff --git a/src/qmlmodels/qqmlinstantiator.cpp b/src/qmlmodels/qqmlinstantiator.cpp
index fca56dd45a..552d1a6b73 100644
--- a/src/qmlmodels/qqmlinstantiator.cpp
+++ b/src/qmlmodels/qqmlinstantiator.cpp
@@ -29,25 +29,25 @@ QQmlInstantiatorPrivate::QQmlInstantiatorPrivate()
{
}
-QQmlInstantiatorPrivate::~QQmlInstantiatorPrivate()
-{
- qDeleteAll(objects);
-}
-
void QQmlInstantiatorPrivate::clear()
{
Q_Q(QQmlInstantiator);
if (!instanceModel)
return;
- if (!objects.size())
+
+ if (objects.isEmpty())
return;
for (int i=0; i < objects.size(); i++) {
- q->objectRemoved(i, objects[i]);
- instanceModel->release(objects[i]);
+ QObject *object = objects[i];
+ emit q->objectRemoved(i, object);
+ instanceModel->release(object);
+ if (object && object->parent() == q)
+ object->setParent(nullptr);
}
+
objects.clear();
- q->objectChanged();
+ emit q->objectChanged();
}
QObject *QQmlInstantiatorPrivate::modelObject(int index, bool async)
@@ -207,10 +207,12 @@ QQmlInstantiator::QQmlInstantiator(QObject *parent)
QQmlInstantiator::~QQmlInstantiator()
{
+ Q_D(QQmlInstantiator);
+ d->clear();
}
/*!
- \qmlsignal QtQml::Instantiator::objectAdded(int index, QtObject object)
+ \qmlsignal QtQml.Models::Instantiator::objectAdded(int index, QtObject object)
This signal is emitted when an object is added to the Instantiator. The \a index
parameter holds the index which the object has been given, and the \a object
@@ -218,7 +220,7 @@ QQmlInstantiator::~QQmlInstantiator()
*/
/*!
- \qmlsignal QtQml::Instantiator::objectRemoved(int index, QtObject object)
+ \qmlsignal QtQml.Models::Instantiator::objectRemoved(int index, QtObject object)
This signal is emitted when an object is removed from the Instantiator. The \a index
parameter holds the index which the object had been given, and the \a object
@@ -228,7 +230,7 @@ QQmlInstantiator::~QQmlInstantiator()
in these cases it will be deleted shortly after the signal is handled.
*/
/*!
- \qmlproperty bool QtQml::Instantiator::active
+ \qmlproperty bool QtQml.Models::Instantiator::active
When active is true, and the delegate component is ready, the Instantiator will
create objects according to the model. When active is false, no objects
@@ -253,7 +255,7 @@ void QQmlInstantiator::setActive(bool newVal)
}
/*!
- \qmlproperty bool QtQml::Instantiator::asynchronous
+ \qmlproperty bool QtQml.Models::Instantiator::asynchronous
When asynchronous is true the Instantiator will attempt to create objects
asynchronously. This means that objects may not be available immediately,
@@ -280,7 +282,7 @@ void QQmlInstantiator::setAsync(bool newVal)
/*!
- \qmlproperty int QtQml::Instantiator::count
+ \qmlproperty int QtQml.Models::Instantiator::count
The number of objects the Instantiator is currently managing.
*/
@@ -292,7 +294,7 @@ int QQmlInstantiator::count() const
}
/*!
- \qmlproperty QtQml::Component QtQml::Instantiator::delegate
+ \qmlproperty QtQml::Component QtQml.Models::Instantiator::delegate
\qmldefault
The component used to create all objects.
@@ -331,7 +333,7 @@ void QQmlInstantiator::setDelegate(QQmlComponent* c)
}
/*!
- \qmlproperty variant QtQml::Instantiator::model
+ \qmlproperty variant QtQml.Models::Instantiator::model
This property can be set to any of the supported \l {qml-data-models}{data models}:
@@ -412,7 +414,7 @@ void QQmlInstantiator::setModel(const QVariant &v)
}
/*!
- \qmlproperty QtObject QtQml::Instantiator::object
+ \qmlproperty QtObject QtQml.Models::Instantiator::object
This is a reference to the first created object, intended as a convenience
for the case where only one object has been created.
@@ -426,7 +428,7 @@ QObject *QQmlInstantiator::object() const
}
/*!
- \qmlmethod QtObject QtQml::Instantiator::objectAt(int index)
+ \qmlmethod QtObject QtQml.Models::Instantiator::objectAt(int index)
Returns a reference to the object with the given \a index.
*/
diff --git a/src/qmlmodels/qqmlinstantiator_p.h b/src/qmlmodels/qqmlinstantiator_p.h
index 264b2b86be..9f0b83d1a0 100644
--- a/src/qmlmodels/qqmlinstantiator_p.h
+++ b/src/qmlmodels/qqmlinstantiator_p.h
@@ -24,7 +24,7 @@ QT_REQUIRE_CONFIG(qml_object_model);
QT_BEGIN_NAMESPACE
class QQmlInstantiatorPrivate;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlInstantiator : public QObject, public QQmlParserStatus
+class Q_QMLMODELS_EXPORT QQmlInstantiator : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
diff --git a/src/qmlmodels/qqmlinstantiator_p_p.h b/src/qmlmodels/qqmlinstantiator_p_p.h
index 49bd54f010..b65d19d767 100644
--- a/src/qmlmodels/qqmlinstantiator_p_p.h
+++ b/src/qmlmodels/qqmlinstantiator_p_p.h
@@ -21,17 +21,18 @@
#include <private/qqmlchangeset_p.h>
#include <private/qqmlobjectmodel_p.h>
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(qml_object_model);
QT_BEGIN_NAMESPACE
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlInstantiatorPrivate : public QObjectPrivate
+class Q_QMLMODELS_EXPORT QQmlInstantiatorPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQmlInstantiator)
public:
QQmlInstantiatorPrivate();
- ~QQmlInstantiatorPrivate();
void clear();
void regenerate();
diff --git a/src/qmlmodels/qqmlitemmodels.qdoc b/src/qmlmodels/qqmlitemmodels.qdoc
index 7f52e795c0..188ef3252d 100644
--- a/src/qmlmodels/qqmlitemmodels.qdoc
+++ b/src/qmlmodels/qqmlitemmodels.qdoc
@@ -29,7 +29,12 @@
\li \b internalId : quint64
\endlist
- All these properties are read-only, as are their C++ counterparts.
+ All these properties are read-only, as are their C++ counterparts. In addition,
+ we also expose the following functions:
+
+ \list
+ \li QVariant \b{data}(int \e role) (since Qt 6.7)
+ \endlist
\note The usual caveats apply to QModelIndex in QML. If the underlying model changes
or gets deleted, it may become dangerous to access its properties. Therefore, you
diff --git a/src/qmlmodels/qqmllistaccessor.cpp b/src/qmlmodels/qqmllistaccessor.cpp
index f1728d5e6c..acf6a14e02 100644
--- a/src/qmlmodels/qqmllistaccessor.cpp
+++ b/src/qmlmodels/qqmllistaccessor.cpp
@@ -5,13 +5,11 @@
#include <private/qqmlmetatype_p.h>
-#include <QtCore/qstringlist.h>
#include <QtCore/qdebug.h>
+#include <QtCore/qsequentialiterable.h>
+#include <QtCore/qstringlist.h>
#include <QtCore/qurl.h>
-// ### Remove me
-#include <private/qqmlengine_p.h>
-
QT_BEGIN_NAMESPACE
QQmlListAccessor::QQmlListAccessor()
@@ -42,22 +40,46 @@ void QQmlListAccessor::setList(const QVariant &v)
if (!d.isValid()) {
m_type = Invalid;
- } else if (variantsType == QMetaType::fromType<QStringList>()) {
+ return;
+ }
+
+ if (variantsType == QMetaType::fromType<QStringList>()) {
m_type = StringList;
- } else if (variantsType == QMetaType::fromType<QList<QUrl>>()) {
+ return;
+ }
+
+ if (variantsType == QMetaType::fromType<QList<QUrl>>()) {
m_type = UrlList;
- } else if (variantsType == QMetaType::fromType<QVariantList>()) {
+ return;
+ }
+
+ if (variantsType == QMetaType::fromType<QVariantList>()) {
m_type = VariantList;
- } else if (variantsType == QMetaType::fromType<QList<QObject *>>()) {
+ return;
+ }
+
+ if (variantsType == QMetaType::fromType<QList<QObject *>>()) {
m_type = ObjectList;
- } else if (variantsType.flags() & QMetaType::IsQmlList) {
+ return;
+ }
+
+ if (variantsType.flags() & QMetaType::IsQmlList) {
d = QVariant::fromValue(QQmlListReference(d));
m_type = ListProperty;
- } else if (variantsType == QMetaType::fromType<QQmlListReference>()) {
+ return;
+ }
+
+ if (variantsType == QMetaType::fromType<QQmlListReference>()) {
m_type = ListProperty;
- } else if (variantsType.flags() & QMetaType::PointerToQObject) {
+ return;
+ }
+
+ if (variantsType.flags() & QMetaType::PointerToQObject) {
m_type = Instance;
- } else if (int i = 0; [&](){bool ok = false; i = v.toInt(&ok); return ok;}()) {
+ return;
+ }
+
+ if (int i = 0; [&](){bool ok = false; i = v.toInt(&ok); return ok;}()) {
// Here we have to check for an upper limit, because down the line code might (well, will)
// allocate memory depending on the number of elements. The upper limit cannot be INT_MAX:
// QVector<QPointer<QQuickItem>> something;
@@ -71,22 +93,54 @@ void QQmlListAccessor::setList(const QVariant &v)
if (i < 0) {
qWarning("Model size of %d is less than 0", i);
m_type = Invalid;
- } else if (i > upperLimit) {
+ return;
+ }
+
+ if (i > upperLimit) {
qWarning("Model size of %d is bigger than the upper limit %d", i, upperLimit);
m_type = Invalid;
- } else {
- m_type = Integer;
- d = i;
+ return;
}
- } else {
- const QQmlType type = QQmlMetaType::qmlListType(v.metaType());
- if (type.isSequentialContainer()) {
- m_metaSequence = type.listMetaSequence();
+
+ m_type = Integer;
+ d = i;
+ return;
+ }
+
+ const QQmlType type = QQmlMetaType::qmlListType(variantsType);
+ if (type.isSequentialContainer()) {
+ m_metaSequence = type.listMetaSequence();
+ m_type = Sequence;
+ return;
+ }
+
+ QSequentialIterable iterable;
+ if (QMetaType::convert(
+ variantsType, d.constData(),
+ QMetaType::fromType<QSequentialIterable>(), &iterable)) {
+ const QMetaSequence sequence = iterable.metaContainer();
+
+ if (sequence.hasSize() && sequence.canGetValueAtIndex()) {
+ // If the resulting iterable is useful for anything, use it.
+ m_metaSequence = sequence;
m_type = Sequence;
- } else {
- m_type = Instance;
+ return;
+ }
+
+ if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) {
+ // As a last resort, try to read the contents of the container via an iterator
+ // and build a QVariantList from them.
+ QVariantList variantList;
+ for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it)
+ variantList.push_back(*it);
+ d = std::move(variantList);
+ m_type = VariantList;
+ return;
}
}
+
+ m_type = Instance;
+ return;
}
qsizetype QQmlListAccessor::count() const
@@ -161,6 +215,54 @@ QVariant QQmlListAccessor::at(qsizetype idx) const
Q_UNREACHABLE_RETURN(QVariant());
}
+void QQmlListAccessor::set(qsizetype idx, const QVariant &value)
+{
+ Q_ASSERT(idx >= 0 && idx < count());
+ switch (m_type) {
+ case StringList:
+ Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
+ (*static_cast<QStringList *>(d.data()))[idx] = value.toString();
+ break;
+ case UrlList:
+ Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
+ (*static_cast<QList<QUrl> *>(d.data()))[idx] = value.value<QUrl>();
+ break;
+ case VariantList:
+ Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
+ (*static_cast<QVariantList *>(d.data()))[idx] = value;
+ break;
+ case ObjectList:
+ Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
+ (*static_cast<QList<QObject *> *>(d.data()))[idx] = value.value<QObject *>();
+ break;
+ case ListProperty:
+ Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
+ static_cast<QQmlListReference *>(d.data())->replace(idx, value.value<QObject *>());
+ break;
+ case Sequence: {
+ Q_ASSERT(m_metaSequence != QMetaSequence());
+ const QMetaType valueMetaType = m_metaSequence.valueMetaType();
+ if (valueMetaType == QMetaType::fromType<QVariant>()) {
+ m_metaSequence.setValueAtIndex(d.data(), idx, &value);
+ } else if (valueMetaType == value.metaType()) {
+ m_metaSequence.setValueAtIndex(d.data(), idx, value.constData());
+ } else {
+ QVariant converted = value;
+ converted.convert(valueMetaType);
+ m_metaSequence.setValueAtIndex(d.data(), idx, converted.constData());
+ }
+ break;
+ }
+ case Instance:
+ d = value;
+ break;
+ case Integer:
+ break;;
+ case Invalid:
+ break;
+ }
+}
+
bool QQmlListAccessor::isValid() const
{
return m_type != Invalid;
diff --git a/src/qmlmodels/qqmllistaccessor_p.h b/src/qmlmodels/qqmllistaccessor_p.h
index 31c8713cee..d713ad7f85 100644
--- a/src/qmlmodels/qqmllistaccessor_p.h
+++ b/src/qmlmodels/qqmllistaccessor_p.h
@@ -34,6 +34,7 @@ public:
qsizetype count() const;
QVariant at(qsizetype) const;
+ void set(qsizetype, const QVariant &);
enum Type {
Invalid,
diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp
index 14c8a6fd85..de7d97af2f 100644
--- a/src/qmlmodels/qqmllistmodel.cpp
+++ b/src/qmlmodels/qqmllistmodel.cpp
@@ -3,22 +3,25 @@
#include "qqmllistmodel_p_p.h"
#include "qqmllistmodelworkeragent_p.h"
-#include <private/qqmlopenmetaobject_p.h>
-#include <private/qqmljsast_p.h>
-#include <private/qqmljsengine_p.h>
+
#include <private/qjsvalue_p.h>
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlengine_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
+#include <private/qqmllistwrapper_p.h>
#include <private/qqmlnotifier_p.h>
+#include <private/qqmlopenmetaobject_p.h>
-#include <private/qv4object_p.h>
-#include <private/qv4dateobject_p.h>
-#include <private/qv4urlobject_p.h>
-#include <private/qv4objectiterator_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qv4dateobject_p.h>
#include <private/qv4lookup_p.h>
+#include <private/qv4object_p.h>
+#include <private/qv4objectiterator_p.h>
#include <private/qv4qmlcontext_p.h>
+#include <private/qv4sequenceobject_p.h>
+#include <private/qv4urlobject_p.h>
#include <qqmlcontext.h>
#include <qqmlinfo.h>
@@ -319,9 +322,12 @@ QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementInde
void *memory = operator new(sizeof(QObject) + sizeof(QQmlData));
void *ddataMemory = ((char *)memory) + sizeof(QObject);
e->m_objectCache = new (memory) QObject;
- QQmlData *ddata = new (ddataMemory) QQmlData;
- ddata->ownMemory = false;
- QObjectPrivate::get(e->m_objectCache)->declarativeData = ddata;
+
+ const QAbstractDeclarativeData *old = std::exchange(
+ QObjectPrivate::get(e->m_objectCache)->declarativeData,
+ new (ddataMemory) QQmlData(QQmlData::DoesNotOwnMemory));
+ Q_ASSERT(!old); // QObject should really not manipulate QQmlData
+
(void)new ModelNodeMetaObject(e->m_objectCache, model, elementIndex);
}
return e->m_objectCache;
@@ -678,18 +684,11 @@ void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement
e->setDoublePropertyFast(r, propertyValue->asDouble());
}
} else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
- const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
- if (r.type == ListLayout::Role::List) {
- ListModel *subModel = new ListModel(r.subLayout, nullptr);
-
- int arrayLength = a->getLength();
- for (int j=0 ; j < arrayLength ; ++j) {
- o = a->get(j);
- subModel->append(o);
- }
-
- e->setListPropertyFast(r, subModel);
- }
+ setArrayLike(&o, propertyName, e, a);
+ } else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
+ setArrayLike(&o, propertyName, e, s);
+ } else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
+ setArrayLike(&o, propertyName, e, l);
} else if (propertyValue->isBoolean()) {
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
if (r.type == ListLayout::Role::Bool) {
@@ -1913,6 +1912,7 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
/*!
\qmltype ListModel
\instantiates QQmlListModel
+ \inherits AbstractListModel
\inqmlmodule QtQml.Models
\ingroup qtquick-models
\brief Defines a free-form list data source.
@@ -1930,6 +1930,10 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
Elements can be manipulated via the model using the setProperty() method, which
allows the roles of the specified element to be set and changed.
+ ListModel inherits from \l{QAbstractListModel} and provides its \l{Q_INVOKABLE}
+ methods. You can, for example use \l{QAbstractItemModel::index} to retrieve a
+ \l{QModelIndex} for a row and column.
+
\section1 Example Usage
The following example shows a ListModel containing three elements, with the roles
@@ -1978,7 +1982,7 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
\section1 Using Threaded List Models with WorkerScript
- ListModel can be used together with WorkerScript access a list model
+ ListModel can be used together with WorkerScript to access a list model
from multiple threads. This is useful if list modifications are
synchronous and take some time: the list operations can be moved to a
different thread to avoid blocking of the main GUI thread.
@@ -1986,11 +1990,11 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
Here is an example that uses WorkerScript to periodically append the
current time to a list model:
- \snippet ../../examples/quick/threading/threadedlistmodel/timedisplay.qml 0
+ \snippet qml/listmodel/WorkerScript.qml 0
The included file, \tt dataloader.mjs, looks like this:
- \snippet ../../examples/quick/threading/threadedlistmodel/dataloader.mjs 0
+ \snippet qml/listmodel/dataloader.mjs 0
The timer in the main example sends messages to the worker script by calling
\l WorkerScript::sendMessage(). When this message is received,
@@ -2001,7 +2005,7 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
You must call sync() or else the changes made to the list from that
thread will not be reflected in the list model in the main thread.
- \sa {qml-data-models}{Data Models}, {Qt Quick Examples - Threading}, {Qt QML}
+ \sa {qml-data-models}{Data Models}, {Qt Qml}
*/
QQmlListModel::QQmlListModel(QObject *parent)
@@ -2381,7 +2385,7 @@ void QQmlListModel::clear()
\sa clear()
*/
-void QQmlListModel::remove(QQmlV4Function *args)
+void QQmlListModel::remove(QQmlV4FunctionPtr args)
{
int argLength = args->length();
@@ -2469,7 +2473,7 @@ void QQmlListModel::updateTranslations()
\sa set(), append()
*/
-void QQmlListModel::insert(QQmlV4Function *args)
+void QQmlListModel::insert(QQmlV4FunctionPtr args)
{
if (args->length() == 2) {
QV4::Scope scope(args->v4engine());
@@ -2585,7 +2589,7 @@ void QQmlListModel::move(int from, int to, int n)
\sa set(), remove()
*/
-void QQmlListModel::append(QQmlV4Function *args)
+void QQmlListModel::append(QQmlV4FunctionPtr args)
{
if (args->length() == 1) {
QV4::Scope scope(args->v4engine());
diff --git a/src/qmlmodels/qqmllistmodel_p.h b/src/qmlmodels/qqmllistmodel_p.h
index 5c44405626..f623fe6112 100644
--- a/src/qmlmodels/qqmllistmodel_p.h
+++ b/src/qmlmodels/qqmllistmodel_p.h
@@ -41,7 +41,7 @@ namespace QV4 {
struct ModelObject;
}
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel
+class Q_QMLMODELS_EXPORT QQmlListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
@@ -65,9 +65,9 @@ public:
int count() const;
Q_INVOKABLE void clear();
- Q_INVOKABLE void remove(QQmlV4Function *args);
- Q_INVOKABLE void append(QQmlV4Function *args);
- Q_INVOKABLE void insert(QQmlV4Function *args);
+ Q_INVOKABLE void remove(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void append(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void insert(QQmlV4FunctionPtr args);
Q_INVOKABLE QJSValue get(int index) const;
Q_INVOKABLE void set(int index, const QJSValue &value);
Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value);
@@ -181,7 +181,4 @@ inline QQmlCustomParser *qmlCreateCustomParser<QQmlListModel>()
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlListModel)
-QML_DECLARE_TYPE(QQmlListElement)
-
#endif // QQMLLISTMODEL_H
diff --git a/src/qmlmodels/qqmllistmodel_p_p.h b/src/qmlmodels/qqmllistmodel_p_p.h
index 662a12f9e7..36afe209d0 100644
--- a/src/qmlmodels/qqmllistmodel_p_p.h
+++ b/src/qmlmodels/qqmllistmodel_p_p.h
@@ -391,6 +391,23 @@ private:
void updateCacheIndices(int start = 0, int end = -1);
+ template<typename ArrayLike>
+ void setArrayLike(QV4::ScopedObject *o, QV4::String *propertyName, ListElement *e, ArrayLike *a)
+ {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
+ if (r.type == ListLayout::Role::List) {
+ ListModel *subModel = new ListModel(r.subLayout, nullptr);
+
+ int arrayLength = a->getLength();
+ for (int j=0 ; j < arrayLength ; ++j) {
+ *o = a->get(j);
+ subModel->append(*o);
+ }
+
+ e->setListPropertyFast(r, subModel);
+ }
+ }
+
friend class ListElement;
friend class QQmlListModelWorkerAgent;
friend class QQmlListModelParser;
diff --git a/src/qmlmodels/qqmllistmodelworkeragent.cpp b/src/qmlmodels/qqmllistmodelworkeragent.cpp
index 74c4e812bf..c50296b1f5 100644
--- a/src/qmlmodels/qqmllistmodelworkeragent.cpp
+++ b/src/qmlmodels/qqmllistmodelworkeragent.cpp
@@ -71,17 +71,17 @@ void QQmlListModelWorkerAgent::clear()
m_copy->clear();
}
-void QQmlListModelWorkerAgent::remove(QQmlV4Function *args)
+void QQmlListModelWorkerAgent::remove(QQmlV4FunctionPtr args)
{
m_copy->remove(args);
}
-void QQmlListModelWorkerAgent::append(QQmlV4Function *args)
+void QQmlListModelWorkerAgent::append(QQmlV4FunctionPtr args)
{
m_copy->append(args);
}
-void QQmlListModelWorkerAgent::insert(QQmlV4Function *args)
+void QQmlListModelWorkerAgent::insert(QQmlV4FunctionPtr args)
{
m_copy->insert(args);
}
diff --git a/src/qmlmodels/qqmllistmodelworkeragent_p.h b/src/qmlmodels/qqmllistmodelworkeragent_p.h
index 21e99f0ec4..647cc1b997 100644
--- a/src/qmlmodels/qqmllistmodelworkeragent_p.h
+++ b/src/qmlmodels/qqmllistmodelworkeragent_p.h
@@ -34,8 +34,8 @@ class QQmlListModel;
class QQmlListModelWorkerAgent : public QObject
{
Q_OBJECT
- Q_PROPERTY(int count READ count)
- Q_PROPERTY(QV4::ExecutionEngine *engine READ engine WRITE setEngine NOTIFY engineChanged)
+ Q_PROPERTY(int count READ count FINAL)
+ Q_PROPERTY(QQmlV4ExecutionEnginePtr engine READ engine WRITE setEngine NOTIFY engineChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -52,9 +52,9 @@ public:
int count() const;
Q_INVOKABLE void clear();
- Q_INVOKABLE void remove(QQmlV4Function *args);
- Q_INVOKABLE void append(QQmlV4Function *args);
- Q_INVOKABLE void insert(QQmlV4Function *args);
+ Q_INVOKABLE void remove(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void append(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void insert(QQmlV4FunctionPtr args);
Q_INVOKABLE QJSValue get(int index) const;
Q_INVOKABLE void set(int index, const QJSValue &value);
Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value);
@@ -64,7 +64,7 @@ public:
void modelDestroyed();
Q_SIGNALS:
- void engineChanged(QV4::ExecutionEngine *engine);
+ void engineChanged(QQmlV4ExecutionEnginePtr engine);
protected:
bool event(QEvent *) override;
diff --git a/src/qmlmodels/qqmlmodelindexvaluetype_p.h b/src/qmlmodels/qqmlmodelindexvaluetype_p.h
index 9d5099a8f1..35ebacc051 100644
--- a/src/qmlmodels/qqmlmodelindexvaluetype_p.h
+++ b/src/qmlmodels/qqmlmodelindexvaluetype_p.h
@@ -42,6 +42,9 @@ public:
Q_INVOKABLE QString toString() const
{ return QLatin1String("QModelIndex") + propertiesString(v); }
+ Q_REVISION(6, 7) Q_INVOKABLE QVariant data(int role = Qt::DisplayRole) const
+ { return v.data(role); }
+
inline int row() const noexcept { return v.row(); }
inline int column() const noexcept { return v.column(); }
inline QModelIndex parent() const { return v.parent(); }
@@ -54,6 +57,8 @@ public:
static QPersistentModelIndex toPersistentModelIndex(const QModelIndex &index)
{ return QPersistentModelIndex(index); }
+
+ operator QModelIndex() const { return v; }
};
struct QQmlPersistentModelIndexValueType
@@ -76,12 +81,17 @@ public:
Q_INVOKABLE QString toString() const
{ return QLatin1String("QPersistentModelIndex") + QQmlModelIndexValueType::propertiesString(v); }
+ Q_REVISION(6, 7) Q_INVOKABLE QVariant data(int role = Qt::DisplayRole) const
+ { return v.data(role); }
+
inline int row() const { return v.row(); }
inline int column() const { return v.column(); }
inline QModelIndex parent() const { return v.parent(); }
inline bool isValid() const { return v.isValid(); }
inline QAbstractItemModel *model() const { return const_cast<QAbstractItemModel *>(v.model()); }
inline quint64 internalId() const { return v.internalId(); }
+
+ operator QPersistentModelIndex() const { return v; }
};
struct QQmlItemSelectionRangeValueType
@@ -129,6 +139,8 @@ public:
inline QAbstractItemModel *model() const { return const_cast<QAbstractItemModel *>(v.model()); }
inline bool isValid() const { return v.isValid(); }
inline bool isEmpty() const { return v.isEmpty(); }
+
+ operator QItemSelectionRange() const { return v; }
};
struct QModelIndexListForeign
diff --git a/src/qmlmodels/qqmlobjectmodel_p.h b/src/qmlmodels/qqmlobjectmodel_p.h
index c15913220a..e161586c90 100644
--- a/src/qmlmodels/qqmlobjectmodel_p.h
+++ b/src/qmlmodels/qqmlobjectmodel_p.h
@@ -28,7 +28,7 @@ class QObject;
class QQmlChangeSet;
class QAbstractItemModel;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlInstanceModel : public QObject
+class Q_QMLMODELS_EXPORT QQmlInstanceModel : public QObject
{
Q_OBJECT
@@ -82,7 +82,7 @@ private:
class QQmlObjectModelAttached;
class QQmlObjectModelPrivate;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel
+class Q_QMLMODELS_EXPORT QQmlObjectModel : public QQmlInstanceModel
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlObjectModel)
@@ -138,7 +138,7 @@ public:
attachedProperties.remove(parent());
}
- Q_PROPERTY(int index READ index NOTIFY indexChanged)
+ Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
int index() const { return m_index; }
void setIndex(int idx) {
if (m_index != idx) {
@@ -168,7 +168,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQmlInstanceModel)
-QML_DECLARE_TYPE(QQmlObjectModel)
-
#endif // QQMLINSTANCEMODEL_P_H
diff --git a/src/qmlmodels/qqmltableinstancemodel.cpp b/src/qmlmodels/qqmltableinstancemodel.cpp
index e4fcae2a44..dcc15f90a5 100644
--- a/src/qmlmodels/qqmltableinstancemodel.cpp
+++ b/src/qmlmodels/qqmltableinstancemodel.cpp
@@ -436,11 +436,15 @@ void QQmlTableInstanceModel::setModel(const QVariant &model)
// needs to stay in sync with the model. So we need to drain the pool
// completely when the model changes.
drainReusableItemsPool(0);
- if (auto const aim = abstractItemModel())
+ if (auto const aim = abstractItemModel()) {
disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback);
+ disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
+ }
m_adaptorModel.setModel(model);
- if (auto const aim = abstractItemModel())
+ if (auto const aim = abstractItemModel()) {
connect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback);
+ connect(aim, &QAbstractItemModel::modelAboutToBeReset, this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
+ }
}
void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles)
@@ -458,6 +462,21 @@ void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const
}
}
+void QQmlTableInstanceModel::modelAboutToBeResetCallback()
+{
+ // When the model is reset, we can no longer rely on any of the data it has
+ // provided us so far. Normally it's enough for the view to recreate all the
+ // delegate items in that case, except if the model roles has changed as well
+ // (since those are cached by QQmlAdaptorModel / Accessors). For the latter case, we
+ // simply set the model once more in the delegate model to rebuild everything.
+ auto const aim = abstractItemModel();
+ auto oldRoleNames = aim->roleNames();
+ QObject::connect(aim, &QAbstractItemModel::modelReset, this, [this, aim, oldRoleNames](){
+ if (oldRoleNames != aim->roleNames())
+ setModel(model());
+ }, Qt::SingleShotConnection);
+}
+
QQmlComponent *QQmlTableInstanceModel::delegate() const
{
return m_delegate;
diff --git a/src/qmlmodels/qqmltableinstancemodel_p.h b/src/qmlmodels/qqmltableinstancemodel_p.h
index 6d3f455adc..cb4fb4c0dd 100644
--- a/src/qmlmodels/qqmltableinstancemodel_p.h
+++ b/src/qmlmodels/qqmltableinstancemodel_p.h
@@ -18,6 +18,8 @@
#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include <QtQmlModels/private/qqmldelegatemodel_p_p.h>
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(qml_table_model);
QT_BEGIN_NAMESPACE
@@ -45,7 +47,7 @@ public:
QQmlTableInstanceModel *tableInstanceModel = nullptr;
};
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel
+class Q_QMLMODELS_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel
{
Q_OBJECT
@@ -115,6 +117,7 @@ private:
void destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode);
void dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles);
+ void modelAboutToBeResetCallback();
static bool isDoneIncubating(QQmlDelegateModelItem *modelItem);
static void deleteModelItemLater(QQmlDelegateModelItem *modelItem);
diff --git a/src/qmlmodels/qqmltreemodeltotablemodel.cpp b/src/qmlmodels/qqmltreemodeltotablemodel.cpp
index d300ff2d1a..db128761cd 100644
--- a/src/qmlmodels/qqmltreemodeltotablemodel.cpp
+++ b/src/qmlmodels/qqmltreemodeltotablemodel.cpp
@@ -26,53 +26,62 @@ QAbstractItemModel *QQmlTreeModelToTableModel::model() const
return m_model;
}
-void QQmlTreeModelToTableModel::setModel(QAbstractItemModel *arg)
+void QQmlTreeModelToTableModel::connectToModel()
{
- struct Cx {
- const char *signal;
- const char *slot;
- };
- static const Cx connections[] = {
- { SIGNAL(destroyed(QObject*)),
- SLOT(modelHasBeenDestroyed()) },
- { SIGNAL(modelReset()),
- SLOT(modelHasBeenReset()) },
- { SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)),
- SLOT(modelDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)) },
-
- { SIGNAL(layoutAboutToBeChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint)),
- SLOT(modelLayoutAboutToBeChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint)) },
- { SIGNAL(layoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint)),
- SLOT(modelLayoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint)) },
-
- { SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)),
- SLOT(modelRowsAboutToBeInserted(const QModelIndex &, int, int)) },
- { SIGNAL(rowsInserted(const QModelIndex&, int, int)),
- SLOT(modelRowsInserted(const QModelIndex&, int, int)) },
- { SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),
- SLOT(modelRowsAboutToBeRemoved(const QModelIndex&, int, int)) },
- { SIGNAL(rowsRemoved(const QModelIndex&, int, int)),
- SLOT(modelRowsRemoved(const QModelIndex&, int, int)) },
- { SIGNAL(rowsAboutToBeMoved(const QModelIndex&, int, int, const QModelIndex&, int)),
- SLOT(modelRowsAboutToBeMoved(const QModelIndex&, int, int, const QModelIndex&, int)) },
- { SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)),
- SLOT(modelRowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)) },
- { nullptr, nullptr }
+ m_connections = {
+ QObject::connect(m_model, &QAbstractItemModel::destroyed,
+ this, &QQmlTreeModelToTableModel::modelHasBeenDestroyed),
+ QObject::connect(m_model, &QAbstractItemModel::modelReset,
+ this, &QQmlTreeModelToTableModel::modelHasBeenReset),
+ QObject::connect(m_model, &QAbstractItemModel::dataChanged,
+ this, &QQmlTreeModelToTableModel::modelDataChanged),
+
+ QObject::connect(m_model, &QAbstractItemModel::layoutAboutToBeChanged,
+ this, &QQmlTreeModelToTableModel::modelLayoutAboutToBeChanged),
+ QObject::connect(m_model, &QAbstractItemModel::layoutChanged,
+ this, &QQmlTreeModelToTableModel::modelLayoutChanged),
+
+ QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted,
+ this, &QQmlTreeModelToTableModel::modelRowsAboutToBeInserted),
+ QObject::connect(m_model, &QAbstractItemModel::rowsInserted,
+ this, &QQmlTreeModelToTableModel::modelRowsInserted),
+ QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
+ this, &QQmlTreeModelToTableModel::modelRowsAboutToBeRemoved),
+ QObject::connect(m_model, &QAbstractItemModel::rowsRemoved,
+ this, &QQmlTreeModelToTableModel::modelRowsRemoved),
+ QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeMoved,
+ this, &QQmlTreeModelToTableModel::modelRowsAboutToBeMoved),
+ QObject::connect(m_model, &QAbstractItemModel::rowsMoved,
+ this, &QQmlTreeModelToTableModel::modelRowsMoved),
+
+ QObject::connect(m_model, &QAbstractItemModel::columnsAboutToBeInserted,
+ this, &QQmlTreeModelToTableModel::modelColumnsAboutToBeInserted),
+ QObject::connect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved,
+ this, &QQmlTreeModelToTableModel::modelColumnsAboutToBeRemoved),
+ QObject::connect(m_model, &QAbstractItemModel::columnsInserted,
+ this, &QQmlTreeModelToTableModel::modelColumnsInserted),
+ QObject::connect(m_model, &QAbstractItemModel::columnsRemoved,
+ this, &QQmlTreeModelToTableModel::modelColumnsRemoved)
};
+}
+void QQmlTreeModelToTableModel::setModel(QAbstractItemModel *arg)
+{
if (m_model != arg) {
if (m_model) {
- for (const Cx *c = &connections[0]; c->signal; c++)
- disconnect(m_model, c->signal, this, c->slot);
+ for (const auto &c : m_connections)
+ QObject::disconnect(c);
+ m_connections.fill({});
}
clearModelData();
m_model = arg;
- if (m_model) {
- for (const Cx *c = &connections[0]; c->signal; c++)
- connect(m_model, c->signal, this, c->slot);
+ if (m_rootIndex.isValid() && m_rootIndex.model() != m_model)
+ m_rootIndex = QModelIndex();
+ if (m_model) {
+ connectToModel();
showModelTopLevelItems();
}
@@ -971,6 +980,40 @@ void QQmlTreeModelToTableModel::modelRowsMoved(const QModelIndex & sourceParent,
ASSERT_CONSISTENCY();
}
+void QQmlTreeModelToTableModel::modelColumnsAboutToBeInserted(const QModelIndex & parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ beginInsertColumns({}, start, end);
+}
+
+void QQmlTreeModelToTableModel::modelColumnsAboutToBeRemoved(const QModelIndex & parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ beginRemoveColumns({}, start, end);
+}
+
+void QQmlTreeModelToTableModel::modelColumnsInserted(const QModelIndex & parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(start);
+ Q_UNUSED(end);
+ endInsertColumns();
+ m_items.clear();
+ showModelTopLevelItems();
+ ASSERT_CONSISTENCY();
+}
+
+void QQmlTreeModelToTableModel::modelColumnsRemoved(const QModelIndex & parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(start);
+ Q_UNUSED(end);
+ endRemoveColumns();
+ m_items.clear();
+ showModelTopLevelItems();
+ ASSERT_CONSISTENCY();
+}
+
void QQmlTreeModelToTableModel::dump() const
{
if (!m_model)
diff --git a/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h b/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
index 1c503ca37c..209977f48b 100644
--- a/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
+++ b/src/qmlmodels/qqmltreemodeltotablemodel_p_p.h
@@ -26,11 +26,11 @@ QT_BEGIN_NAMESPACE
class QAbstractItemModel;
-class Q_QMLMODELS_PRIVATE_EXPORT QQmlTreeModelToTableModel : public QAbstractItemModel
+class Q_QMLMODELS_EXPORT QQmlTreeModelToTableModel : public QAbstractItemModel
{
Q_OBJECT
- Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
- Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex RESET resetRootIndex NOTIFY rootIndexChanged)
+ Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged FINAL)
+ Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex RESET resetRootIndex NOTIFY rootIndexChanged FINAL)
struct TreeItem;
@@ -118,6 +118,10 @@ private Q_SLOTS:
void modelRowsInserted(const QModelIndex & parent, int start, int end);
void modelRowsMoved(const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationRow);
void modelRowsRemoved(const QModelIndex & parent, int start, int end);
+ void modelColumnsAboutToBeInserted(const QModelIndex & parent, int start, int end);
+ void modelColumnsAboutToBeRemoved(const QModelIndex & parent, int start, int end);
+ void modelColumnsInserted(const QModelIndex & parent, int start, int end);
+ void modelColumnsRemoved(const QModelIndex & parent, int start, int end);
private:
struct TreeItem {
@@ -158,6 +162,7 @@ private:
const QModelIndex &bottomRight,
const QVector<int> &roles);
void emitQueuedSignals();
+ void connectToModel();
QPointer<QAbstractItemModel> m_model = nullptr;
QPersistentModelIndex m_rootIndex;
@@ -169,6 +174,7 @@ private:
bool m_modelLayoutChanged = false;
int m_signalAggregatorStack = 0;
QVector<DataChangedParams> m_queuedDataChanged;
+ std::array<QMetaObject::Connection, 15> m_connections;
int m_column = 0;
};
diff --git a/src/qmlmodels/qquickpackage.cpp b/src/qmlmodels/qquickpackage.cpp
index 70a3a6c19b..7ab1e8345f 100644
--- a/src/qmlmodels/qquickpackage.cpp
+++ b/src/qmlmodels/qquickpackage.cpp
@@ -38,11 +38,11 @@ QT_BEGIN_NAMESPACE
\note Package is part of QtQml.Models since version 2.14 and part of QtQuick since version 2.0.
Importing Package via QtQuick is deprecated since Qt 5.14.
- \sa {Qt Quick Examples - Views}, {Qt Quick Demo - Photo Viewer}, {Qt QML}
+ \sa {Qt Quick Examples - Views}, {Qt Qml}
*/
/*!
- \qmlattachedproperty string QtQuick::Package::name
+ \qmlattachedproperty string QtQml.Models::Package::name
This attached property holds the name of an item within a Package.
*/
diff --git a/src/qmlmodels/qquickpackage_p.h b/src/qmlmodels/qquickpackage_p.h
index ba30f79390..827d718248 100644
--- a/src/qmlmodels/qquickpackage_p.h
+++ b/src/qmlmodels/qquickpackage_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickPackagePrivate;
class QQuickPackageAttached;
-class Q_QMLMODELS_PRIVATE_EXPORT QQuickPackage : public QObject
+class Q_QMLMODELS_EXPORT QQuickPackage : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPackage)
@@ -49,7 +49,7 @@ public:
class QQuickPackageAttached : public QObject
{
Q_OBJECT
-Q_PROPERTY(QString name READ name WRITE setName)
+Q_PROPERTY(QString name READ name WRITE setName FINAL)
public:
QQuickPackageAttached(QObject *parent);
virtual ~QQuickPackageAttached();
@@ -64,6 +64,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPackage)
-
#endif // QQUICKPACKAGE_H
diff --git a/src/qmlmodels/qtqmlmodelsglobal_p.h b/src/qmlmodels/qtqmlmodelsglobal_p.h
index 65e48f6554..966836ec6b 100644
--- a/src/qmlmodels/qtqmlmodelsglobal_p.h
+++ b/src/qmlmodels/qtqmlmodelsglobal_p.h
@@ -18,7 +18,7 @@
#include <QtQml/private/qtqmlglobal_p.h>
#include <QtQmlModels/qtqmlmodelsglobal.h>
#include <QtQmlModels/private/qtqmlmodels-config_p.h>
-#include <QtQmlModels/private/qtqmlmodelsexports_p.h>
+#include <QtQmlModels/qtqmlmodelsexports.h>
#define Q_QMLMODELS_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT
diff --git a/src/qmlnetwork/CMakeLists.txt b/src/qmlnetwork/CMakeLists.txt
new file mode 100644
index 0000000000..2e9978703b
--- /dev/null
+++ b/src/qmlnetwork/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_qml_module(QmlNetwork
+ URI "QtNetwork"
+ VERSION "${PROJECT_VERSION}"
+ DESIGNER_SUPPORTED
+ DEPENDENCIES
+ QtQml/auto
+ SOURCES
+ qqmlnetworkinformation_p.h
+ qqmlnetworkinformation.cpp
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Network
+ Qt::Qml
+ GENERATE_CPP_EXPORTS
+ )
+
+qt_internal_extend_target(QmlNetwork CONDITION QT_FEATURE_qml_ssl
+ SOURCES
+ ssl/qqmlsslnamespace_p.h ssl/qqmlsslsocketnamespace_p.h
+ ssl/qqmlsslconfiguration.cpp ssl/qqmlsslconfiguration_p.h
+ ssl/qqmlsslkey_p.h ssl/qqmlsslkey.cpp
+)
+
+qt_internal_add_docs(QmlNetwork
+ doc/qtqmlnetwork.qdocconf
+)
diff --git a/src/qmlnetwork/doc/qtqmlnetwork.qdocconf b/src/qmlnetwork/doc/qtqmlnetwork.qdocconf
new file mode 100644
index 0000000000..2b4e899aed
--- /dev/null
+++ b/src/qmlnetwork/doc/qtqmlnetwork.qdocconf
@@ -0,0 +1,37 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+project = QtQmlNetwork
+description = Qt QML Network Reference Documentation
+version = $QT_VERSION
+
+qhp.projects = QtQmlNetwork
+
+qhp.QtQmlNetwork.file = qtqmlnetwork.qhp
+qhp.QtQmlNetwork.namespace = org.qt-project.qtqmlnetwork.$QT_VERSION_TAG
+qhp.QtQmlNetwork.virtualFolder = qtqmlnetwork
+qhp.QtQmlNetwork.indexTitle = Qt QML Network
+qhp.QtQmlNetwork.indexRoot =
+
+qhp.QtQmlNetwork.subprojects = qmltypes
+qhp.QtQmlNetwork.subprojects.qmltypes.title = QML Types
+qhp.QtQmlNetwork.subprojects.qmltypes.indexTitle = Qt QML Network QML Types
+qhp.QtQmlNetwork.subprojects.qmltypes.selectors = qmlclass
+qhp.QtQmlNetwork.subprojects.qmltypes.sortPages = true
+
+depends = qtcore qtdoc qtqml qtnetwork
+
+# This module has no documented C++ types, clear the module header
+moduleheader =
+
+headerdirs += ../
+sourcedirs += ../
+
+imagedirs += images
+
+navigation.landingpage = "Qt QML Network"
+navigation.qmltypespage = "Qt QML Network QML Types"
+
+tagfile = qtqmlnetwork.tags
+
+# Enforce zero documentation warnings
+warninglimit = 0
diff --git a/src/qmlnetwork/doc/src/qtqmlnetwork-qmltypes.qdoc b/src/qmlnetwork/doc/src/qtqmlnetwork-qmltypes.qdoc
new file mode 100644
index 0000000000..f28ab17a3f
--- /dev/null
+++ b/src/qmlnetwork/doc/src/qtqmlnetwork-qmltypes.qdoc
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \qmlmodule QtNetwork
+ \title Qt QML Network QML Types
+ \since 6.7
+ \ingroup qmlmodules
+ \brief Provides core network functionality in QML.
+
+ The Qt QML Network module provides core network functionality in QML.
+
+ The QML types can be imported into your application using the
+ following import statement in your .qml file:
+
+ \qml
+ import QtNetwork
+ \endqml
+
+ \section1 QML Types
+
+ \generatelist {qmltypesbymodule QtNetwork}
+
+ \section1 Related Information
+
+ \list
+ \li \l {Qt Network}
+ \li \l {Qt QML QML Types}{Base QML Types}
+ \endlist
+
+ \noautolist
+*/
diff --git a/src/qmlnetwork/doc/src/qtqmlnetwork.qdoc b/src/qmlnetwork/doc/src/qtqmlnetwork.qdoc
new file mode 100644
index 0000000000..56d768efa0
--- /dev/null
+++ b/src/qmlnetwork/doc/src/qtqmlnetwork.qdoc
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtqmlnetwork-index.html
+ \title Qt QML Network
+
+ \brief The Qt QML Network module provides core network functionality in QML.
+
+ The module exposes the Qt C++ \l [QtNetwork] {Qt Network} {Network}
+ functionality to QML.
+
+ \section1 QML Types
+
+ \generatelist {qmltypesbymodule QtNetwork}
+
+ \section1 Related Information
+
+ \list
+ \li \l{Qt Network}
+ \li \l{Qt Qml}
+ \endlist
+*/
diff --git a/src/qmlnetwork/qqmlnetworkinformation.cpp b/src/qmlnetwork/qqmlnetworkinformation.cpp
new file mode 100644
index 0000000000..bdfb2ea656
--- /dev/null
+++ b/src/qmlnetwork/qqmlnetworkinformation.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmlnetworkinformation_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QNetworkInformation *QQmlNetworkInformation::create(QQmlEngine *, QJSEngine *)
+{
+ static QNetworkInformation *s_singletonInstance = []() {
+ QNetworkInformation::loadDefaultBackend();
+ QNetworkInformation *singletonInstance = QNetworkInformation::instance();
+
+ Q_ASSERT(singletonInstance);
+ QJSEngine::setObjectOwnership(singletonInstance, QJSEngine::CppOwnership);
+ return singletonInstance;
+ }();
+
+ return s_singletonInstance;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qqmlnetworkinformation_p.cpp"
diff --git a/src/qmlnetwork/qqmlnetworkinformation.qdoc b/src/qmlnetwork/qqmlnetworkinformation.qdoc
new file mode 100644
index 0000000000..1628c6cbb3
--- /dev/null
+++ b/src/qmlnetwork/qqmlnetworkinformation.qdoc
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \qmltype NetworkInformation
+ \inqmlmodule QtNetwork
+ \instantiates QNetworkInformation
+ \brief Provides a cross-platform interface to network-related information.
+
+ NetworkInformation provides a cross-platform interface to network-related information.
+
+ NetworkInformation is a singleton.
+
+ \sa QNetworkInformation
+*/
+
+/*!
+ \qmlproperty enumeration NetworkInformation::reachability
+ \readonly
+
+ Holds the current state of the system's network connectivity.
+
+ \value NetworkInformation.Reachability.Unknown
+ Connection may be established but the OS has yet to confirm full
+ connectivity, or this feature is not supported.
+ \value NetworkInformation.Reachability.Disconnected
+ The system may not have connectivity at all.
+ \value NetworkInformation.Reachability.Local
+ The system is connected to a network, but might only be able to
+ access devices on the local network.
+ \value NetworkInformation.Reachability.Site
+ The system is connected to a network, but might only be able to
+ access devices on the local subnet or an intranet.
+ \value NetworkInformation.Reachability.Online
+ The system is connected to a network and able to access the Internet.
+*/
+
+/*!
+ \qmlproperty bool NetworkInformation::isBehindCaptivePortal
+ \readonly
+
+ Indicates if the user's device is currently known to be behind a captive portal.
+*/
+
+/*!
+ \qmlproperty enumeration NetworkInformation::transportMedium
+ \readonly
+
+ Holds the currently active transport medium for the application.
+
+ \value NetworkInformation.TransportMedium.Unknown
+ If the OS reports no active medium, the active medium is not recognized by Qt,
+ or the TransportMedium feature is not supported.
+ \value NetworkInformation.TransportMedium.Ethernet
+ The currently active connection is using Ethernet. Note: This value may also be
+ returned when Windows is connected to a Bluetooth personal area network.
+ \value NetworkInformation.TransportMedium.Cellular
+ The currently active connection is using a cellular network.
+ \value NetworkInformation.TransportMedium.WiFi
+ The currently active connection is using Wi-Fi.
+ \value NetworkInformation.TransportMedium.Bluetooth
+ The currently active connection is connected using Bluetooth.
+*/
+
+/*!
+ \qmlproperty bool NetworkInformation::isMetered
+ \readonly
+
+ Returns whether the current connection is (known to be) metered or not.
+*/
diff --git a/src/qmlnetwork/qqmlnetworkinformation_p.h b/src/qmlnetwork/qqmlnetworkinformation_p.h
new file mode 100644
index 0000000000..a473abbb47
--- /dev/null
+++ b/src/qmlnetwork/qqmlnetworkinformation_p.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLNETWORKINFORMATION_P_H
+#define QQMLNETWORKINFORMATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QQmlEngine>
+#include <QJSEngine>
+
+#include <QtNetwork/qnetworkinformation.h>
+#include <qtqmlnetworkexports.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_QMLNETWORK_EXPORT QQmlNetworkInformation
+{
+ Q_GADGET
+ QML_FOREIGN(QNetworkInformation)
+ QML_NAMED_ELEMENT(NetworkInformation)
+ QML_ADDED_IN_VERSION(6, 7)
+ QML_SINGLETON
+
+public:
+ static QNetworkInformation *create(QQmlEngine *, QJSEngine *);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qmlnetwork/ssl/qqmlssl.qdoc b/src/qmlnetwork/ssl/qqmlssl.qdoc
new file mode 100644
index 0000000000..cf3eb0802d
--- /dev/null
+++ b/src/qmlnetwork/ssl/qqmlssl.qdoc
@@ -0,0 +1,200 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \qmltype sslConfiguration
+ \inqmlmodule QtNetwork
+
+ \brief The sslConfiguration class holds the configuration and state of an
+ SSL connection.
+
+ sslConfiguration is used to relay information
+ about an open SSL connection and to allow the application to control
+ certain features of that connection.
+ The sslConfiguration encapsulates QSslConfiguration object inside.
+ The sslConfiguration calls defaultSslCongiguration in ctor.
+
+ \sa QSslConfiguration
+*/
+
+/*!
+ \qmlproperty QList sslConfiguration::sslOptions
+ Holds the list of SslOption values, that are activated by configuration.
+
+ \sa QSsl::SslOption
+*/
+
+/*!
+ \qmlproperty string sslConfiguration::ciphers
+ Holds the cryptographic cipher suite for this configuration to \a ciphers,
+ which is a colon-separated list of cipher suite names.
+
+ \sa QSslConfiguration::ciphers
+*/
+
+/*!
+ \qmlproperty enumeration sslConfiguration::protocol
+ Holds SslProtocol enumeration value.
+ \sa QSsl::SslProtocol
+ */
+
+/*!
+ \qmlproperty enumeration sslConfiguration::peerVerifyMode
+ Holds PeerVerifyMode enumeration value.
+
+ \sa QSslSocket::PeerVerifyMode
+ */
+
+/*!
+ \qmlproperty int sslConfiguration::peerVerifyDepth
+
+ Holds the maximum number of certificates in the peer's certificate chain
+ to be checked during the SSL handshake phase, or 0 (the default) if no
+ maximum depth has been set, indicating that the whole certificate chain
+ should be checked.
+
+ \sa QSslConfiguration::peerVerifyDepth
+ */
+
+/*!
+ \qmlproperty bytearray sslConfiguration::sessionTicket
+
+ Holds the session ticket used in the SSL handshake in ASN.1
+ format, suitable to e.g. be persisted to disk.
+
+ \sa QSslConfiguration::sessionTicket
+ */
+
+/*!
+ \qmlmethod void sslConfiguration::setCertificateFiles(const QStringList &certificateFiles);
+
+ This function loads into configuration the list of certificates \a certificateFiles
+ provided by user.
+*/
+
+/*!
+ \qmlmethod void sslConfiguration::setPrivateKey(const QQmlSslKey &privateKey);
+
+ This function sets into configuration user defined Private key value \a privateKey.
+
+ \sa QSslKey, sslKey
+*/
+
+/*!
+ \qmltype sslDtlsConfiguration
+ \inqmlmodule QtNetwork
+
+ \brief The sslDtlsConfiguration class holds the Dtls
+ default configuration and state of an SSL connection.
+
+ \sa QSslConfiguration
+*/
+
+/*!
+ \qmlproperty QList sslDtlsConfiguration::sslOptions
+ Holds the list of SslOption values, that are activated by configuration.
+
+ \sa QSsl::SslOption
+*/
+
+/*!
+ \qmlproperty string sslDtlsConfiguration::ciphers
+ Holds the cryptographic cipher suite for this configuration to \a ciphers,
+ which is a colon-separated list of cipher suite names.
+
+ \sa QSslConfiguration::ciphers
+*/
+
+/*!
+ \qmlproperty enumeration sslDtlsConfiguration::protocol
+ Holds SslProtocol enumeration value.
+ \sa QSsl::SslProtocol
+ */
+
+/*!
+ \qmlproperty enumeration sslDtlsConfiguration::peerVerifyMode
+ Holds PeerVerifyMode enumeration value.
+
+ \sa QSslSocket::PeerVerifyMode
+*/
+
+/*!
+ \qmlproperty int sslDtlsConfiguration::peerVerifyDepth
+
+ Holds the maximum number of certificates in the peer's certificate chain
+ to be checked during the SSL handshake phase, or 0 (the default) if no
+ maximum depth has been set, indicating that the whole certificate chain
+ should be checked.
+
+ \sa QSslConfiguration::peerVerifyDepth
+*/
+
+/*!
+ \qmlproperty bytearray sslDtlsConfiguration::sessionTicket
+
+ Holds the session ticket used in the SSL handshake in ASN.1
+ format, suitable to e.g. be persisted to disk.
+
+ \sa QSslConfiguration::sessionTicket
+*/
+
+/*!
+ \qmlmethod void sslDtlsConfiguration::setCertificateFiles(const QStringList &certificateFiles);
+
+ This function loads into configuration the list of certificates \a certificateFiles
+ provided by user.
+*/
+
+/*!
+ \qmlmethod void sslDtlsConfiguration::setPrivateKey(const QQmlSslKey &privateKey);
+
+ This function sets into configuration user defined Private key value \a privateKey.
+
+ \sa QSslKey, sslKey
+*/
+
+/*!
+ \qmltype sslKey
+ \inqmlmodule QtNetwork
+
+ \instantiates QSslKey
+ \brief The sslKey class provides the interface for private and public keys.
+
+ The sslKey provides an API for managing keys.
+ The QML sslKey class is \c Q_GADGET based, it generates QSslKey object
+ based on it's QML properties.
+
+ \sa QSslKey
+*/
+
+/*!
+ \qmlproperty string sslKey::keyFile
+ Holds the path to the \c *.pem key file.
+*/
+
+/*!
+ \qmlproperty enumeration sslKey::keyAlgorithm
+ Holds KeyAlgorithm enumeration value.
+
+ \sa QSsl::KeyAlgorithm
+*/
+
+/*!
+ \qmlproperty enumeration sslKey::keyFormat
+ Holds EncodingFormat enumeration value.
+ \sa QSsl::EncodingFormat
+*/
+
+/*!
+ \qmlproperty bytearray sslKey::keyPassPhrase
+
+ Holds the value to be used to decrypt QSslKey.
+
+ \sa QSslKey
+*/
+
+/*!
+ \qmlproperty enumeration sslKey::keyType
+ Holds KeyType enumeration value.
+ \sa QSsl::KeyType
+*/
diff --git a/src/qmlnetwork/ssl/qqmlsslconfiguration.cpp b/src/qmlnetwork/ssl/qqmlsslconfiguration.cpp
new file mode 100644
index 0000000000..74d171fc5e
--- /dev/null
+++ b/src/qmlnetwork/ssl/qqmlsslconfiguration.cpp
@@ -0,0 +1,175 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtCore/qfile.h>
+#include <QtNetwork/qsslcipher.h>
+#include "qqmlsslconfiguration_p.h"
+#include <array>
+
+QT_BEGIN_NAMESPACE
+static constexpr std::array<QSsl::SslOption, 8> SslOptions = {
+ QSsl::SslOptionDisableEmptyFragments,
+ QSsl::SslOptionDisableSessionTickets,
+ QSsl::SslOptionDisableCompression,
+ QSsl::SslOptionDisableServerNameIndication,
+ QSsl::SslOptionDisableLegacyRenegotiation,
+ QSsl::SslOptionDisableSessionSharing,
+ QSsl::SslOptionDisableSessionPersistence,
+ QSsl::SslOptionDisableServerCipherPreference
+};
+
+QString QQmlSslConfiguration::ciphers() const
+{
+ return m_ciphers;
+}
+
+QList<QSsl::SslOption> QQmlSslConfiguration::sslOptions() const
+{
+ return m_sslOptions;
+}
+
+QSsl::SslProtocol QQmlSslConfiguration::protocol() const
+{
+ return m_configuration.protocol();
+}
+
+QSslSocket::PeerVerifyMode QQmlSslConfiguration::peerVerifyMode() const
+{
+ return m_configuration.peerVerifyMode();
+}
+
+int QQmlSslConfiguration::peerVerifyDepth() const
+{
+ return m_configuration.peerVerifyDepth();
+}
+
+QByteArray QQmlSslConfiguration::sessionTicket() const
+{
+ return m_configuration.sessionTicket();
+}
+
+QSslConfiguration const QQmlSslConfiguration::configuration()
+{
+ return m_configuration;
+}
+
+void QQmlSslConfiguration::setCertificateFiles(const QStringList &certificateFiles)
+{
+ if (m_certificateFiles == certificateFiles)
+ return;
+
+ m_certificateFiles = certificateFiles;
+ QList<QSslCertificate> certificates;
+ for (const QString &fileName: m_certificateFiles) {
+ QFile certificateFile(fileName);
+ if (certificateFile.open(QIODevice::ReadOnly)) {
+ QByteArray cert = certificateFile.readAll();
+ certificates.append(QSslCertificate(cert));
+ } else {
+ qWarning() << "File: " << fileName << "is not found. It will be skipped.";
+ }
+ }
+
+ if (!certificates.isEmpty())
+ m_configuration.setCaCertificates(certificates);
+ else
+ qWarning() << "No certificates loaded.";
+}
+
+void QQmlSslConfiguration::setProtocol(QSsl::SslProtocol protocol)
+{
+ if (m_configuration.protocol() == protocol)
+ return;
+
+ m_configuration.setProtocol(protocol);
+}
+
+void QQmlSslConfiguration::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode)
+{
+ if (m_configuration.peerVerifyMode() == mode)
+ return;
+
+ m_configuration.setPeerVerifyMode(mode);
+}
+
+void QQmlSslConfiguration::setPeerVerifyDepth(int depth)
+{
+ if (m_configuration.peerVerifyDepth() == depth)
+ return;
+
+ m_configuration.setPeerVerifyDepth(depth);
+}
+
+void QQmlSslConfiguration::setCiphers(const QString &ciphers)
+{
+ if (ciphers == m_ciphers)
+ return;
+
+ m_ciphers = ciphers;
+ m_configuration.setCiphers(ciphers); // split(":") is used inside
+}
+
+void QQmlSslConfiguration::setSslOptions(const QList<QSsl::SslOption> &options)
+{
+ if (m_sslOptions == options)
+ return;
+
+ m_sslOptions = options;
+ for (QSsl::SslOption option: m_sslOptions)
+ m_configuration.setSslOption(option, true);
+}
+
+void QQmlSslConfiguration::setSessionTicket(const QByteArray &sessionTicket)
+{
+ if (m_configuration.sessionTicket() == sessionTicket)
+ return;
+
+ m_configuration.setSessionTicket(sessionTicket);
+}
+
+void QQmlSslConfiguration::setPrivateKey(const QQmlSslKey &privateKey)
+{
+ m_configuration.setPrivateKey(privateKey.getSslKey());
+}
+
+void QQmlSslConfiguration::setSslOptionsList(const QSslConfiguration &configuration)
+{
+ for (QSsl::SslOption option: SslOptions) {
+ if (configuration.testSslOption(option))
+ m_sslOptions.append(option);
+ }
+}
+
+void QQmlSslConfiguration::setCiphersList(const QSslConfiguration &configuration)
+{
+ QList<QSslCipher> ciphers = configuration.ciphers();
+ for (int i = 0; i < ciphers.size(); ++i) {
+ if (i != 0) {
+ m_ciphers += QString::fromUtf8(":");
+ }
+ m_ciphers += ciphers[i].name();
+ }
+}
+
+QQmlSslDefaultConfiguration::QQmlSslDefaultConfiguration()
+ : QQmlSslConfiguration()
+{
+ m_configuration = QSslConfiguration::defaultConfiguration();
+ setSslOptionsList(m_configuration);
+ setCiphersList(m_configuration);
+}
+
+QQmlSslDefaultDtlsConfiguration::QQmlSslDefaultDtlsConfiguration()
+ : QQmlSslConfiguration()
+{
+#if QT_CONFIG(dtls)
+ m_configuration = QSslConfiguration::defaultDtlsConfiguration();
+#else
+ qWarning() << "No dtls support enabled";
+ m_configuration = QSslConfiguration::defaultConfiguration();
+#endif // QT_CONFIG(dtls)
+ setSslOptionsList(m_configuration);
+ setCiphersList(m_configuration);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlnetwork/ssl/qqmlsslconfiguration_p.h b/src/qmlnetwork/ssl/qqmlsslconfiguration_p.h
new file mode 100644
index 0000000000..a88b990e5b
--- /dev/null
+++ b/src/qmlnetwork/ssl/qqmlsslconfiguration_p.h
@@ -0,0 +1,109 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLSSLCONFIGURATION_P_H
+#define QQMLSSLCONFIGURATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtqmlnetworkexports.h>
+#include "qqmlsslkey_p.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QMetaType>
+#include <QtQml/qqml.h>
+#include <QtNetwork/qsslconfiguration.h>
+#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qssl.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QMLNETWORK_EXPORT QQmlSslConfiguration
+{
+ Q_GADGET
+
+ Q_PROPERTY(QString ciphers READ ciphers WRITE setCiphers)
+ Q_PROPERTY(QList<QSsl::SslOption> sslOptions READ sslOptions WRITE setSslOptions)
+ Q_PROPERTY(QSsl::SslProtocol protocol READ protocol WRITE setProtocol)
+ Q_PROPERTY(QSslSocket::PeerVerifyMode peerVerifyMode READ peerVerifyMode
+ WRITE setPeerVerifyMode)
+ Q_PROPERTY(int peerVerifyDepth READ peerVerifyDepth WRITE setPeerVerifyDepth)
+ Q_PROPERTY(QByteArray sessionTicket READ sessionTicket WRITE setSessionTicket)
+
+public:
+ Q_INVOKABLE void setCertificateFiles(const QStringList &certificateFiles);
+ Q_INVOKABLE void setPrivateKey(const QQmlSslKey &privateKey);
+
+ QString ciphers() const;
+ QList<QSsl::SslOption> sslOptions() const;
+ QSsl::SslProtocol protocol() const;
+ QSslSocket::PeerVerifyMode peerVerifyMode() const;
+ int peerVerifyDepth() const;
+ QByteArray sessionTicket() const;
+ QSslConfiguration const configuration();
+
+ void setProtocol(QSsl::SslProtocol protocol);
+ void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode);
+ void setPeerVerifyDepth(int depth);
+ void setCiphers(const QString &ciphers);
+ void setSslOptions(const QList<QSsl::SslOption> &options);
+ void setSessionTicket(const QByteArray &sessionTicket);
+
+private:
+ inline friend bool operator==(const QQmlSslConfiguration &lval,
+ const QQmlSslConfiguration &rval)
+ {
+ return lval.m_certificateFiles == rval.m_certificateFiles
+ && lval.m_ciphers == rval.m_ciphers
+ && lval.m_sslOptions == rval.m_sslOptions
+ && lval.m_configuration == rval.m_configuration;
+ }
+
+ inline friend bool operator!=(const QQmlSslConfiguration &lval,
+ const QQmlSslConfiguration &rval)
+ {
+ return !(lval == rval);
+ }
+
+protected:
+ void setSslOptionsList(const QSslConfiguration &configuration);
+ void setCiphersList(const QSslConfiguration &configuration);
+
+ QStringList m_certificateFiles;
+ QString m_ciphers;
+ QList<QSsl::SslOption> m_sslOptions;
+ QSslConfiguration m_configuration;
+};
+
+class Q_QMLNETWORK_EXPORT QQmlSslDefaultConfiguration : public QQmlSslConfiguration
+{
+ Q_GADGET
+ QML_NAMED_ELEMENT(sslConfiguration)
+ QML_ADDED_IN_VERSION(6, 7)
+
+public:
+ QQmlSslDefaultConfiguration();
+};
+
+class Q_QMLNETWORK_EXPORT QQmlSslDefaultDtlsConfiguration : public QQmlSslConfiguration
+{
+ Q_GADGET
+ QML_NAMED_ELEMENT(sslDtlsConfiguration)
+ QML_ADDED_IN_VERSION(6, 7)
+
+public:
+ QQmlSslDefaultDtlsConfiguration();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSSLCONFIGURATION_P_H
diff --git a/src/qmlnetwork/ssl/qqmlsslkey.cpp b/src/qmlnetwork/ssl/qqmlsslkey.cpp
new file mode 100644
index 0000000000..e3e38c15a9
--- /dev/null
+++ b/src/qmlnetwork/ssl/qqmlsslkey.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtCore/qfile.h>
+#include "qqmlsslkey_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSslKey QQmlSslKey::getSslKey() const
+{
+ if (m_keyFile.isEmpty()) {
+ qWarning() << "SslConfiguration::getSslKey: No key paths set";
+ return QSslKey();
+ }
+
+ QFile file(m_keyFile);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "SslConfiguration::getSslKey: Couldn't open file:" << m_keyFile;
+ return QSslKey();
+ }
+
+ return QSslKey(file.readAll(),
+ m_keyAlgorithm,
+ m_keyFormat,
+ m_keyType,
+ m_keyPassPhrase);
+}
+
+void QQmlSslKey::setKeyFile(const QString &key)
+{
+ if (m_keyFile == key)
+ return;
+
+ m_keyFile = key;
+}
+
+void QQmlSslKey::setKeyAlgorithm(QSsl::KeyAlgorithm value)
+{
+ if (m_keyAlgorithm == value)
+ return;
+
+ m_keyAlgorithm = value;
+}
+
+void QQmlSslKey::setKeyFormat(QSsl::EncodingFormat value)
+{
+ if (m_keyFormat == value)
+ return;
+
+ m_keyFormat = value;
+}
+
+void QQmlSslKey::setKeyPassPhrase(const QByteArray &value)
+{
+ if (m_keyPassPhrase == value)
+ return;
+
+ m_keyPassPhrase = value;
+}
+
+void QQmlSslKey::setKeyType(QSsl::KeyType type)
+{
+ if (m_keyType == type)
+ return;
+ m_keyType = type;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlnetwork/ssl/qqmlsslkey_p.h b/src/qmlnetwork/ssl/qqmlsslkey_p.h
new file mode 100644
index 0000000000..ce9adc210c
--- /dev/null
+++ b/src/qmlnetwork/ssl/qqmlsslkey_p.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLSSLKEY_P_H
+#define QQMLSSLKEY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtqmlnetworkexports.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QMetaType>
+#include <QtNetwork/qsslkey.h>
+#include <QtNetwork/qssl.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QMLNETWORK_EXPORT QQmlSslKey
+{
+ Q_GADGET
+ QML_NAMED_ELEMENT(sslKey)
+ QML_ADDED_IN_VERSION(6, 7)
+
+ Q_PROPERTY(QString keyFile READ keyFile
+ WRITE setKeyFile)
+ Q_PROPERTY(QSsl::KeyAlgorithm keyAlgorithm READ keyAlgorithm
+ WRITE setKeyAlgorithm)
+ Q_PROPERTY(QSsl::EncodingFormat keyFormat READ keyFormat
+ WRITE setKeyFormat)
+ Q_PROPERTY(QByteArray keyPassPhrase READ keyPassPhrase
+ WRITE setKeyPassPhrase)
+ Q_PROPERTY(QSsl::KeyType keyType READ keyType WRITE setKeyType)
+
+public:
+ QSslKey getSslKey() const;
+ QString keyFile() const { return m_keyFile; }
+ QSsl::KeyAlgorithm keyAlgorithm() const { return m_keyAlgorithm; }
+ QSsl::EncodingFormat keyFormat() const { return m_keyFormat; }
+ QByteArray keyPassPhrase() const { return m_keyPassPhrase; }
+ QSsl::KeyType keyType() const { return m_keyType; }
+
+ void setKeyFile(const QString &key);
+ void setKeyAlgorithm(QSsl::KeyAlgorithm value);
+ void setKeyFormat(QSsl::EncodingFormat value);
+ void setKeyPassPhrase(const QByteArray &value);
+ void setKeyType(QSsl::KeyType type);
+
+private:
+ inline friend bool operator==(const QQmlSslKey &lvalue, const QQmlSslKey &rvalue)
+ {
+ return (lvalue.m_keyFile == rvalue.m_keyFile
+ && lvalue.m_keyAlgorithm == rvalue.m_keyAlgorithm
+ && lvalue.m_keyFormat == rvalue.m_keyFormat
+ && lvalue.m_keyType == rvalue.m_keyType
+ && lvalue.m_keyPassPhrase == rvalue.m_keyPassPhrase);
+ }
+
+ QString m_keyFile;
+ QByteArray m_keyPassPhrase;
+ QSsl::KeyAlgorithm m_keyAlgorithm = QSsl::Rsa;
+ QSsl::EncodingFormat m_keyFormat = QSsl::Pem;
+ QSsl::KeyType m_keyType = QSsl::PrivateKey;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSSLKEY_P_H
diff --git a/src/qmlnetwork/ssl/qqmlsslnamespace_p.h b/src/qmlnetwork/ssl/qqmlsslnamespace_p.h
new file mode 100644
index 0000000000..827704df6f
--- /dev/null
+++ b/src/qmlnetwork/ssl/qqmlsslnamespace_p.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLSSLNAMESPACE_P_H
+#define QQMLSSLNAMESPACE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtqmlnetworkexports.h>
+
+#include <QtCore/QMetaType>
+#include <QtQml/qqml.h>
+#include <QtNetwork/QSsl>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSslForeignNamespace
+{
+ Q_NAMESPACE
+ QML_FOREIGN_NAMESPACE(QSsl)
+ QML_NAMED_ELEMENT(Ssl)
+ QML_ADDED_IN_VERSION(6, 7)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSSLNAMESPACE_P_H
diff --git a/src/qmlnetwork/ssl/qqmlsslsocketnamespace_p.h b/src/qmlnetwork/ssl/qqmlsslsocketnamespace_p.h
new file mode 100644
index 0000000000..eefbb20358
--- /dev/null
+++ b/src/qmlnetwork/ssl/qqmlsslsocketnamespace_p.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLSSLSOCKETNAMESPACE_P_H
+#define QQMLSSLSOCKETNAMESPACE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtqmlnetworkexports.h>
+
+#include <QtCore/QMetaObject>
+#include <QtQml/qqml.h>
+#include <QtNetwork/qsslsocket.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace SslSocketForeignNamespace
+{
+ Q_NAMESPACE
+ QML_FOREIGN_NAMESPACE(QSslSocket)
+ QML_NAMED_ELEMENT(SslSocket)
+ QML_ADDED_IN_VERSION(6, 7)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSSLSOCKETNAMESPACE_P_H
diff --git a/src/qmltest/CMakeLists.txt b/src/qmltest/CMakeLists.txt
index f42d6cc44b..51fd6648ca 100644
--- a/src/qmltest/CMakeLists.txt
+++ b/src/qmltest/CMakeLists.txt
@@ -49,6 +49,7 @@ qt_internal_add_qml_module(QuickTest
Qt::Test
PRIVATE_MODULE_INTERFACE
Qt::TestPrivate
+ GENERATE_CPP_EXPORTS
)
qt_internal_extend_target(QuickTest CONDITION QT_FEATURE_qml_debug
diff --git a/src/qmltest/SignalSpy.qml b/src/qmltest/SignalSpy.qml
index dd6c8ec892..ea77a1704d 100644
--- a/src/qmltest/SignalSpy.qml
+++ b/src/qmltest/SignalSpy.qml
@@ -45,6 +45,16 @@ Item {
id: spy
visible: false
+ Component.onDestruction: {
+ // We are potentially destroyed before the target object,
+ // and since only the sender (target) being destroyed destroys a connection
+ // in QML, and not the receiver (us/"spy"), we need to manually disconnect.
+ // When QTBUG-118166 is implemented, we can remove this.
+ let signalFunc = target ? qttest_signalFunc(target, signalName) : null
+ if (signalFunc)
+ signalFunc.disconnect(spy.qtest_activated)
+ }
+
TestUtil {
id: util
}
@@ -191,8 +201,7 @@ Item {
qtest_reentrancy_guard = true;
if (qtest_prevTarget != null) {
- var prevHandlerName = qtest_signalHandlerName(qtest_prevSignalName)
- var prevFunc = qtest_prevTarget[prevHandlerName]
+ let prevFunc = qttest_signalFunc(qtest_prevTarget, qtest_prevSignalName)
if (prevFunc)
prevFunc.disconnect(spy.qtest_activated)
qtest_prevTarget = null
@@ -200,22 +209,16 @@ Item {
}
if (target != null && signalName != "") {
// Look for the signal name in the object
- var func = target[signalName]
- if (typeof func !== "function") {
- // If it is not a function, try looking for signal handler
- // i.e. (onSignal) this is needed for cases where there is a property
- // and a signal with the same name, e.g. Mousearea.pressed
- func = target[qtest_signalHandlerName(signalName)]
- }
- if (func === undefined) {
- spy.qtest_valid = false
- console.log("Signal '" + signalName + "' not found")
- } else {
+ let func = qttest_signalFunc(target, signalName)
+ if (func) {
qtest_prevTarget = target
qtest_prevSignalName = signalName
func.connect(spy.qtest_activated)
spy.qtest_valid = true
spy.qtest_signalArguments = []
+ } else {
+ spy.qtest_valid = false
+ console.log("Signal '" + signalName + "' not found")
}
} else {
spy.qtest_valid = false
@@ -232,8 +235,18 @@ Item {
/*! \internal */
function qtest_signalHandlerName(sn) {
- if (sn.substr(0, 2) === "on" && sn[2] === sn[2].toUpperCase())
- return sn
- return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1)
+ return util.signalHandlerName(sn)
+ }
+
+ /*! \internal */
+ function qttest_signalFunc(_target, _signalName) {
+ let signalFunc = _target[_signalName]
+ if (typeof signalFunc !== "function") {
+ // If it is not a function, try looking for signal handler
+ // i.e. (onSignal) this is needed for cases where there is a property
+ // and a signal with the same name, e.g. Mousearea.pressed
+ signalFunc = _target[qtest_signalHandlerName(_signalName)]
+ }
+ return signalFunc
}
}
diff --git a/src/qmltest/TestCase.qml b/src/qmltest/TestCase.qml
index b7848a9fd7..9dac6ae249 100644
--- a/src/qmltest/TestCase.qml
+++ b/src/qmltest/TestCase.qml
@@ -66,7 +66,8 @@ import "testlogger.js" as TestLogger
Table data can be provided to a test using a function name that ends
with "_data". Alternatively, the \c init_data() function can be used
- to provide default test data for all test functions in a TestCase type:
+ to provide default test data for all test functions without a matching
+ "_data" function in a TestCase type:
\code
@@ -95,7 +96,7 @@ import "testlogger.js" as TestLogger
compare(data.a + data.b, data.answer)
}
- function test__default_table(data) {
+ function test_default_table(data) {
//data comes from init_data
compare(data.a + data.b, data.answer)
}
@@ -121,8 +122,8 @@ import "testlogger.js" as TestLogger
name: "CreateBenchmark"
function benchmark_create_component() {
- var component = Qt.createComponent("item.qml")
- var obj = component.createObject(top)
+ let component = Qt.createComponent("item.qml")
+ let obj = component.createObject(top)
obj.destroy()
component.destroy()
}
@@ -165,6 +166,15 @@ import "testlogger.js" as TestLogger
and mouseMove() methods can be used to simulate mouse events in a
similar fashion.
+ If your test creates other windows, it's possible that those windows
+ become active, stealing the focus from the TestCase's window. To ensure
+ that the TestCase's window is active, use the following code:
+
+ \code
+ testCase.Window.window.requestActivate()
+ tryCompare(testCase.Window.window, "active", true)
+ \endcode
+
\b{Note:} keyboard and mouse events can only be delivered once the
main window has been shown. Attempts to deliver events before then
will fail. Use the \l when and windowShown properties to track
@@ -183,7 +193,7 @@ import "testlogger.js" as TestLogger
when: windowShown
function test_click() {
- var item = Qt.createQmlObject("import QtQuick 2.0; Item {}", testCase);
+ let item = Qt.createQmlObject("import QtQuick 2.0; Item {}", testCase);
verify(item);
// Test item...
@@ -210,7 +220,7 @@ import "testlogger.js" as TestLogger
when: windowShown
function test_click() {
- var item = createTemporaryQmlObject("import QtQuick 2.0; Item {}", testCase);
+ let item = createTemporaryQmlObject("import QtQuick 2.0; Item {}", testCase);
verify(item);
// Test item...
@@ -224,6 +234,114 @@ import "testlogger.js" as TestLogger
of \l Component, the \l createTemporaryObject() function can be used.
\sa {QtTest::SignalSpy}{SignalSpy}, {Qt Quick Test}
+
+ \section1 Separating Tests from Application Logic
+
+ In most cases, you would want to separate your tests from the application
+ logic by splitting them into different projects and linking them.
+
+ For example, you could have the following project structure:
+
+ \badcode
+ .
+ | — CMakeLists.txt
+ | - main.qml
+ | — src
+ | — main.cpp
+ | — MyModule
+ | — MyButton.qml
+ | — CMakeLists.txt
+ | — tests
+ | — tst_testqml.qml
+ | — main.cpp
+ | — setup.cpp
+ | — setup.h
+ \endcode
+
+ Now, to test \c MyModule/MyButton.qml, create a library for
+ \c MyModule in \c MyModule/CMakeLists.txt and link it to your
+ test project, \c tests/UnitQMLTests/CMakeLists.txt:
+
+ \if defined(onlinedocs)
+ \tab {build-qt-app}{tab-cmake-add-library}{MyModule/CMakeLists.txt}{checked}
+ \tab {build-qt-app}{tab-cmake-link-against-library}{tests/CMakeLists.txt}{}
+ \tab {build-qt-app}{tab-tests_main}{tests/main.cpp}{}
+ \tab {build-qt-app}{tab-tests-setup-cpp}{tests/setup.cpp}{}
+ \tab
+ {build-qt-app}{tab-tests-setup-h}{tests/setup.h}{}
+ \tab {build-qt-app}{tab-project-cmake}{CMakeLists.txt}{}
+ \tabcontent {tab-cmake-add-library}
+ \else
+ \section2 Add Library
+ \endif
+ \dots
+ \snippet testApp/MyModule/CMakeLists.txt add library
+ \dots
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-cmake-link-against-library}
+ \else
+ \section2 Link Against Library
+ \endif
+ \dots
+ \snippet testApp/tests/CMakeLists.txt link against library
+ \dots
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-tests_main}
+ \else
+ \section2 Test main.cpp
+ \endif
+ \snippet testApp/tests/main.cpp main
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-tests-setup-cpp}
+ \else
+ \section2 Test Setup C++
+ \endif
+ \snippet testApp/tests/setup.cpp setup
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-tests-setup-h}
+ \else
+ \section2 Test Setup Header
+ \endif
+ \snippet testApp/tests/setup.h setup
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-project-cmake}
+ \else
+ \section2 Project CMakeLists
+ \endif
+ \dots
+ \snippet testApp/CMakeLists.txt project-cmake
+ \dots
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
+
+
+ Then, in \c tests/tst_testqml.qml, you can import
+ \c MyModule/MyButton.qml:
+
+ \if defined(onlinedocs)
+ \tab {test-qml}{tab-qml-import}{tests/tst_testqml.qml}{checked}
+ \tab {test-qml}{tab-qml-my-button}{MyModule/MyButton.qml}{}
+ \tabcontent {tab-qml-import}
+ \else
+ \section2 Import QML
+ \endif
+ \snippet testApp/tests/tst_testqml.qml import
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qml-my-button}
+ \else
+ \section2 Define QML Button
+ \endif
+ \snippet testApp/MyModule/MyButton.qml define
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
*/
@@ -473,7 +591,7 @@ Item {
if (!expressionFunction())
wait(0)
- var i = 0
+ let i = 0
while (i < timeout && !expressionFunction()) {
wait(50)
i += 50
@@ -567,7 +685,7 @@ Item {
throw new Error("QtQuickTest::fail")
}
- if (timeout !== undefined && typeof(timeout) != "number") {
+ if (timeout !== undefined && typeof(timeout) !== "number") {
qtest_results.fail("Second argument must be a number; actual type is " + typeof timeout,
util.callerFile(), util.callerLine())
throw new Error("QtQuickTest::fail")
@@ -620,7 +738,7 @@ Item {
throw new Error("QtQuickTest::fail");
}
- var object = Qt.createQmlObject(qml, parent, filePath);
+ let object = Qt.createQmlObject(qml, parent, filePath);
qtest_temporaryObjects.push(object);
return object;
}
@@ -661,7 +779,7 @@ Item {
if (parent === undefined)
parent = null
- var object = component.createObject(parent, properties ? properties : ({}));
+ let object = component.createObject(parent, properties ? properties : ({}));
qtest_temporaryObjects.push(object);
return object;
}
@@ -672,8 +790,8 @@ Item {
Destroys all temporary objects that still exist.
*/
function qtest_destroyTemporaryObjects() {
- for (var i = 0; i < qtest_temporaryObjects.length; ++i) {
- var temporaryObject = qtest_temporaryObjects[i];
+ for (let i = 0; i < qtest_temporaryObjects.length; ++i) {
+ let temporaryObject = qtest_temporaryObjects[i];
// ### the typeof check can be removed when QTBUG-57749 is fixed
if (temporaryObject && typeof temporaryObject.destroy === "function")
temporaryObject.destroy();
@@ -743,27 +861,27 @@ Item {
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
function qtest_compareInternal(act, exp) {
- var success = false;
+ let success = false;
if (act === exp) {
success = true; // catch the most you can
} else if (act === null || exp === null || typeof act === "undefined" || typeof exp === "undefined") {
success = false; // don't lose time with error prone cases
} else {
- var typeExp = qtest_typeof(exp), typeAct = qtest_typeof(act)
+ let typeExp = qtest_typeof(exp), typeAct = qtest_typeof(act)
if (typeExp !== typeAct) {
// allow object vs string comparison (e.g. for colors)
// else break on different types
- if ((typeExp === "string" && (typeAct === "object") || typeAct == "declarativeitem")
- || ((typeExp === "object" || typeExp == "declarativeitem") && typeAct === "string")) {
- success = (act == exp)
+ if ((typeExp === "string" && (typeAct === "object") || typeAct === "declarativeitem")
+ || ((typeExp === "object" || typeExp === "declarativeitem") && typeAct === "string")) {
+ success = (act == exp) // @disable-check M126
}
} else if (typeExp === "string" || typeExp === "boolean" ||
typeExp === "null" || typeExp === "undefined") {
if (exp instanceof act.constructor || act instanceof exp.constructor) {
// to catch short annotaion VS 'new' annotation of act declaration
- // e.g. var i = 1;
- // var j = new Number(1);
- success = (act == exp)
+ // e.g. let i = 1;
+ // let j = new Number(1);
+ success = (act == exp) // @disable-check M126
} else {
success = (act === exp)
}
@@ -798,9 +916,9 @@ Item {
/*! \internal */
function qtest_compareInternalObjects(act, exp) {
- var i;
- var eq = true; // unless we can proove it
- var aProperties = [], bProperties = []; // collection of strings
+ let i;
+ let eq = true; // unless we can proove it
+ let aProperties = [], bProperties = []; // collection of strings
// comparing constructors is more strict than using instanceof
if (act.constructor !== exp.constructor) {
@@ -819,8 +937,8 @@ Item {
bProperties.push(i); // collect exp's properties
}
- if (aProperties.length == 0 && bProperties.length == 0) { // at least a special case for QUrl
- return eq && (JSON.stringify(act) == JSON.stringify(exp));
+ if (aProperties.length === 0 && bProperties.length === 0) { // at least a special case for QUrl
+ return eq && (JSON.stringify(act) === JSON.stringify(exp));
}
// Ensures identical properties name
@@ -830,11 +948,11 @@ Item {
/*! \internal */
function qtest_compareInternalArrays(actual, expected) {
- if (actual.length != expected.length) {
+ if (actual.length !== expected.length) {
return false
}
- for (var i = 0, len = actual.length; i < len; i++) {
+ for (let i = 0, len = actual.length; i < len; i++) {
if (!qtest_compareInternal(actual[i], expected[i])) {
return false
}
@@ -853,10 +971,10 @@ Item {
\sa tryCompare(), fuzzyCompare
*/
function compare(actual, expected, msg) {
- var act = qtest_results.stringify(actual)
- var exp = qtest_results.stringify(expected)
+ let act = qtest_results.stringify(actual)
+ let exp = qtest_results.stringify(expected)
- var success = qtest_compareInternal(actual, expected)
+ let success = qtest_compareInternal(actual, expected)
if (msg === undefined) {
if (success)
msg = "COMPARE()"
@@ -885,7 +1003,7 @@ Item {
if (delta === undefined)
qtest_fail("A delta value is required for fuzzyCompare", 2)
- var success = qtest_results.fuzzyCompare(actual, expected, delta)
+ let success = qtest_results.fuzzyCompare(actual, expected, delta)
if (msg === undefined) {
if (success)
msg = "FUZZYCOMPARE()"
@@ -923,12 +1041,12 @@ Item {
For example:
\code
- var image = grabImage(rect);
+ let image = grabImage(rect);
compare(image.red(10, 10), 255);
compare(image.pixel(20, 20), Qt.rgba(255, 0, 0, 255));
rect.width += 10;
- var newImage = grabImage(rect);
+ let newImage = grabImage(rect);
verify(!newImage.equals(image));
\endcode
@@ -939,7 +1057,7 @@ Item {
example:
\code
- var image = grabImage(rect);
+ let image = grabImage(rect);
try {
compare(image.width, 100);
} catch (ex) {
@@ -968,7 +1086,7 @@ Item {
*/
function findChild(parent, objectName) {
// First, search the visual item hierarchy.
- var child = qtest_findVisualChild(parent, objectName);
+ let child = qtest_findVisualChild(parent, objectName);
if (child)
return child;
@@ -981,16 +1099,16 @@ Item {
if (!parent || parent.children === undefined)
return null;
- for (var i = 0; i < parent.children.length; ++i) {
+ for (let i = 0; i < parent.children.length; ++i) {
// Is this direct child of ours the child we're after?
- var child = parent.children[i];
+ let child = parent.children[i];
if (child.objectName === objectName)
return child;
}
- for (i = 0; i < parent.children.length; ++i) {
+ for (let i = 0; i < parent.children.length; ++i) {
// Try the direct child's children.
- child = qtest_findVisualChild(parent.children[i], objectName);
+ let child = qtest_findVisualChild(parent.children[i], objectName);
if (child)
return child;
}
@@ -1023,18 +1141,18 @@ Item {
\sa compare(), SignalSpy::wait()
*/
function tryCompare(obj, prop, ...args) {
- if (typeof(prop) != "string" && typeof(prop) != "number") {
+ if (typeof(prop) !== "string" && typeof(prop) !== "number") {
qtest_results.fail("A property name as string or index is required for tryCompare",
util.callerFile(), util.callerLine())
throw new Error("QtQuickTest::fail")
}
- if (args.length == 0) {
+ if (args.length === 0) {
qtest_results.fail("A value is required for tryCompare",
util.callerFile(), util.callerLine())
throw new Error("QtQuickTest::fail")
}
let [value, timeout, msg] = args
- if (timeout !== undefined && typeof(timeout) != "number") {
+ if (timeout !== undefined && typeof(timeout) !== "number") {
qtest_results.fail("timeout should be a number",
util.callerFile(), util.callerLine())
throw new Error("QtQuickTest::fail")
@@ -1045,15 +1163,15 @@ Item {
msg = "property " + prop
if (!qtest_compareInternal(obj[prop], value))
wait(0)
- var i = 0
+ let i = 0
while (i < timeout && !qtest_compareInternal(obj[prop], value)) {
wait(50)
i += 50
}
- var actual = obj[prop]
- var act = qtest_results.stringify(actual)
- var exp = qtest_results.stringify(value)
- var success = qtest_compareInternal(actual, value)
+ let actual = obj[prop]
+ let act = qtest_results.stringify(actual)
+ let exp = qtest_results.stringify(value)
+ let success = qtest_compareInternal(actual, value)
if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine()))
throw new Error("QtQuickTest::fail")
}
@@ -1229,7 +1347,16 @@ Item {
Waits for \a ms milliseconds while processing Qt events.
- \sa sleep(), waitForRendering()
+ \note This methods uses a precise timer to do the actual waiting. The
+ event you are waiting for may not. In particular, any animations as
+ well as the \l{Timer} QML type can use either precise or coarse
+ timers, depending on various factors. For a coarse timer you have
+ to expect a drift of around 5% in relation to the precise timer used
+ by TestCase::wait(). Qt cannot give hard guarantees on the drift,
+ though, because the operating system usually doesn't offer hard
+ guarantees on timers.
+
+ \sa sleep(), waitForRendering(), Qt::TimerType
*/
function wait(ms) {
qtest_results.wait(ms)
@@ -1280,9 +1407,9 @@ Item {
function keyPress(key, modifiers, delay) {
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
- if (typeof(key) == "string" && key.length == 1) {
+ if (typeof(key) === "string" && key.length === 1) {
if (!qtest_events.keyPressChar(key, modifiers, delay))
qtest_fail("window not shown", 2)
} else {
@@ -1306,9 +1433,9 @@ Item {
function keyRelease(key, modifiers, delay) {
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
- if (typeof(key) == "string" && key.length == 1) {
+ if (typeof(key) === "string" && key.length === 1) {
if (!qtest_events.keyReleaseChar(key, modifiers, delay))
qtest_fail("window not shown", 2)
} else {
@@ -1332,9 +1459,9 @@ Item {
function keyClick(key, modifiers, delay) {
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
- if (typeof(key) == "string" && key.length == 1) {
+ if (typeof(key) === "string" && key.length === 1) {
if (!qtest_events.keyClickChar(key, modifiers, delay))
qtest_fail("window not shown", 2)
} else {
@@ -1387,7 +1514,7 @@ Item {
button = Qt.LeftButton
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
if (x === undefined)
x = item.width / 2
@@ -1421,7 +1548,7 @@ Item {
button = Qt.LeftButton
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
if (x === undefined)
x = item.width / 2
@@ -1456,17 +1583,17 @@ Item {
button = Qt.LeftButton
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
- var moveDelay = Math.max(1, delay === -1 ? qtest_events.defaultMouseDelay : delay)
+ let moveDelay = Math.max(1, delay === -1 ? qtest_events.defaultMouseDelay : delay)
// Divide dx and dy to have intermediate mouseMove while dragging
// Fractions of dx/dy need be superior to the dragThreshold
// to make the drag works though
- var intermediateDx = Math.round(dx/3)
+ let intermediateDx = Math.round(dx/3)
if (Math.abs(intermediateDx) < (util.dragThreshold + 1))
intermediateDx = 0
- var intermediateDy = Math.round(dy/3)
+ let intermediateDy = Math.round(dy/3)
if (Math.abs(intermediateDy) < (util.dragThreshold + 1))
intermediateDy = 0
@@ -1474,14 +1601,14 @@ Item {
// Trigger dragging by dragging past the drag threshold, but making sure to only drag
// along a certain axis if a distance greater than zero was given for that axis.
- var dragTriggerXDistance = dx > 0 ? (util.dragThreshold + 1) : 0
- var dragTriggerYDistance = dy > 0 ? (util.dragThreshold + 1) : 0
- mouseMove(item, x + dragTriggerXDistance, y + dragTriggerYDistance, moveDelay, button)
+ let dragTriggerXDistance = dx > 0 ? (util.dragThreshold + 1) : 0
+ let dragTriggerYDistance = dy > 0 ? (util.dragThreshold + 1) : 0
+ mouseMove(item, x + dragTriggerXDistance, y + dragTriggerYDistance, moveDelay, button, modifiers)
if (intermediateDx !== 0 || intermediateDy !== 0) {
- mouseMove(item, x + intermediateDx, y + intermediateDy, moveDelay, button)
- mouseMove(item, x + 2*intermediateDx, y + 2*intermediateDy, moveDelay, button)
+ mouseMove(item, x + intermediateDx, y + intermediateDy, moveDelay, button, modifiers)
+ mouseMove(item, x + 2*intermediateDx, y + 2*intermediateDy, moveDelay, button, modifiers)
}
- mouseMove(item, x + dx, y + dy, moveDelay, button)
+ mouseMove(item, x + dx, y + dy, moveDelay, button, modifiers)
mouseRelease(item, x + dx, y + dy, button, modifiers, delay)
}
@@ -1509,7 +1636,7 @@ Item {
button = Qt.LeftButton
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
if (x === undefined)
x = item.width / 2
@@ -1550,7 +1677,7 @@ Item {
button = Qt.LeftButton
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
if (x === undefined)
x = item.width / 2
@@ -1577,19 +1704,21 @@ Item {
\sa mousePress(), mouseRelease(), mouseClick(), mouseDoubleClickSequence(), mouseDrag(), mouseWheel()
*/
- function mouseMove(item, x, y, delay, buttons) {
+ function mouseMove(item, x, y, delay, buttons, modifiers) {
if (!qtest_verifyItem(item, "mouseMove"))
return
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
- if (buttons == undefined)
+ if (buttons === undefined)
buttons = Qt.NoButton
+ if (modifiers === undefined)
+ modifiers = Qt.NoModifiers
if (x === undefined)
x = item.width / 2
if (y === undefined)
y = item.height / 2
- if (!qtest_events.mouseMove(item, x, y, delay, buttons))
+ if (!qtest_events.mouseMove(item, x, y, delay, buttons, modifiers))
qtest_fail("window not shown", 2)
}
@@ -1613,15 +1742,15 @@ Item {
if (!qtest_verifyItem(item, "mouseWheel"))
return
- if (delay == undefined)
+ if (delay === undefined)
delay = -1
- if (buttons == undefined)
+ if (buttons === undefined)
buttons = Qt.NoButton
if (modifiers === undefined)
modifiers = Qt.NoModifier
- if (xDelta == undefined)
+ if (xDelta === undefined)
xDelta = 0
- if (yDelta == undefined)
+ if (yDelta === undefined)
yDelta = 0
if (!qtest_events.mouseWheel(item, x, y, buttons, modifiers, xDelta, yDelta, delay))
qtest_fail("window not shown", 2)
@@ -1658,7 +1787,7 @@ Item {
id: test1
function test_touch() {
- var touch = touchEvent(area);
+ let touch = touchEvent(area);
touch.press(0, area, 10, 10);
touch.commit();
verify(area.touched);
@@ -1803,7 +1932,7 @@ Item {
qtest_testCaseResult = testCase[prop](arg)
} catch (e) {
qtest_testCaseResult = []
- if (e.message.indexOf("QtQuickTest::") != 0) {
+ if (e.message.indexOf("QtQuickTest::") !== 0) {
// Test threw an unrecognized exception - fail.
qtest_results.fail("Uncaught exception: " + e.message,
e.fileName, e.lineNumber)
@@ -1820,10 +1949,12 @@ Item {
qtest_results.finishTestData()
qtest_runInternal("cleanup")
qtest_destroyTemporaryObjects()
- qtest_results.finishTestDataCleanup()
+
// wait(0) will call processEvents() so objects marked for deletion
// in the test function will be deleted.
wait(0)
+
+ qtest_results.finishTestDataCleanup()
}
}
@@ -1839,12 +1970,12 @@ Item {
break
// Execute the benchmark function.
- if (prop.indexOf("benchmark_once_") != 0)
+ if (prop.indexOf("benchmark_once_") !== 0)
qtest_results.startBenchmark(TestResult.RepeatUntilValidMeasurement, qtest_results.dataTag)
else
qtest_results.startBenchmark(TestResult.RunOnce, qtest_results.dataTag)
while (!qtest_results.isBenchmarkDone()) {
- var success = qtest_runInternal(prop, arg)
+ let success = qtest_runInternal(prop, arg)
qtest_results.finishTestData()
if (!success)
break
@@ -1868,7 +1999,11 @@ Item {
if (!when || completed || running || !qtest_componentCompleted)
return;
- verify(TestLogger.log_can_start_test(qtest_testId))
+ if (!TestLogger.log_can_start_test(qtest_testId)) {
+ console.error("Interleaved test execution detected. This shouldn't happen")
+ return;
+ }
+
if (TestLogger.log_start_test(qtest_testId)) {
qtest_results.reset()
qtest_results.testCaseName = name
@@ -1884,12 +2019,12 @@ Item {
if (qtest_results.functionsToRun.length > 0) {
checkNames = true
- var found = false
+ let found = false
if (name.length > 0) {
- for (var index in qtest_results.functionsToRun) {
+ for (let index in qtest_results.functionsToRun) {
let caseFuncName = qtest_results.functionsToRun[index]
- if (caseFuncName.indexOf(name + "::") != 0)
+ if (caseFuncName.indexOf(name + "::") !== 0)
continue
found = true
@@ -1916,7 +2051,7 @@ Item {
// Run the initTestCase function.
qtest_results.functionName = "initTestCase"
- var runTests = true
+ let runTests = true
if (!qtest_runInternal("initTestCase"))
runTests = false
qtest_results.finishTestData()
@@ -1924,27 +2059,27 @@ Item {
qtest_results.finishTestFunction()
// Run the test methods.
- var testList = []
+ let testList = []
if (runTests) {
- for (var prop in testCase) {
- if (prop.indexOf("test_") != 0 && prop.indexOf("benchmark_") != 0)
+ for (let prop in testCase) {
+ if (prop.indexOf("test_") !== 0 && prop.indexOf("benchmark_") !== 0)
continue
- var tail = prop.lastIndexOf("_data");
- if (tail != -1 && tail == (prop.length - 5))
+ let tail = prop.lastIndexOf("_data");
+ if (tail !== -1 && tail === (prop.length - 5))
continue
testList.push(prop)
}
testList.sort()
}
- for (var index in testList) {
- var prop = testList[index]
+ for (let index in testList) {
+ let prop = testList[index]
if (checkNames && !(prop in testsToRun))
continue
- var datafunc = prop + "_data"
- var isBenchmark = (prop.indexOf("benchmark_") == 0)
+ let datafunc = prop + "_data"
+ let isBenchmark = (prop.indexOf("benchmark_") === 0)
qtest_results.functionName = prop
if (!(datafunc in testCase))
@@ -1952,15 +2087,15 @@ Item {
if (datafunc in testCase) {
if (qtest_runInternal(datafunc)) {
- var table = qtest_testCaseResult
- var haveData = false
+ let table = qtest_testCaseResult
+ let haveData = false
let checkTags = (checkNames && testsToRun[prop].length > 0)
qtest_results.initTestTable()
- for (var index in table) {
+ for (let index in table) {
haveData = true
- var row = table[index]
+ let row = table[index]
if (!row.tag)
row.tag = "row " + index // Must have something
if (checkTags) {
@@ -2007,13 +2142,13 @@ Item {
// Complain about missing functions that we were supposed to run.
if (checkNames) {
let missingTests = []
- for (var func in testsToRun) {
+ for (let func in testsToRun) {
let caseFuncName = name + '::' + func
let tags = testsToRun[func]
if (tags.length <= 0)
missingTests.push(caseFuncName)
else
- for (var i in tags)
+ for (let i in tags)
missingTests.push(caseFuncName + ':' + tags[i])
}
missingTests.sort()
@@ -2038,7 +2173,7 @@ Item {
}
onWhenChanged: {
- if (when != qtest_prevWhen) {
+ if (when !== qtest_prevWhen) {
qtest_prevWhen = when
if (when)
TestSchedule.testCases.push(testCase)
diff --git a/src/qmltest/doc/qtqmltest.qdocconf b/src/qmltest/doc/qtqmltest.qdocconf
index 2dbe68011e..75099bf15f 100644
--- a/src/qmltest/doc/qtqmltest.qdocconf
+++ b/src/qmltest/doc/qtqmltest.qdocconf
@@ -37,5 +37,5 @@ navigation.landingpage = "Qt Quick Test"
navigation.cppclassespage = "Qt Quick Test C++ API"
navigation.qmltypespage = "Qt Quick Test QML Types"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/qmltest/doc/snippets/modules_MyModule_CMakeLists.txt b/src/qmltest/doc/snippets/modules_MyModule_CMakeLists.txt
new file mode 100644
index 0000000000..c43434f8d3
--- /dev/null
+++ b/src/qmltest/doc/snippets/modules_MyModule_CMakeLists.txt
@@ -0,0 +1,42 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.20)
+
+set(MODULE_NAME "SecondPlugin")
+project(${MODULE_NAME} LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+file(GLOB QML_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
+source_group("Qml Files" FILES ${QML_SOURCES})
+
+# file(GLOB CPP_SOURCES *.cpp)
+# file(GLOB HPP_SOURCES *.h)
+//! [add library]
+qt_add_library(${MyModule} STATIC)
+
+qt6_add_qml_module(${MyModule}
+ URI ${MyModule}
+ VERSION 1.0
+ QML_FILES ${QML_SOURCES}
+ # SOURCES ${CPP_SOURCES} ${HPP_SOURCES}
+)
+//! [add library]
+set_target_properties(${MODULE_NAME} PROPERTIES
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE FALSE
+)
+
+
+target_link_libraries(${MODULE_NAME} PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+)
+
+target_include_directories(${MODULE_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/src/qmltest/doc/snippets/overview.cmake b/src/qmltest/doc/snippets/overview.cmake
index 4c239b4f9c..11119b0701 100644
--- a/src/qmltest/doc/snippets/overview.cmake
+++ b/src/qmltest/doc/snippets/overview.cmake
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#! [cmake_use]
find_package(Qt6 REQUIRED COMPONENTS QuickTest)
diff --git a/src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp b/src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp
index 1486e26696..32e5c984ac 100644
--- a/src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp
+++ b/src/qmltest/doc/snippets/src_qmltest_qquicktest.cpp
@@ -6,6 +6,7 @@
#include <QtQuickTest>
#include <QQmlEngine>
#include <QQmlContext>
+#include <QGuiApplication>
class Setup : public QObject
{
@@ -15,10 +16,21 @@ public:
Setup() {}
public slots:
+ void applicationAvailable()
+ {
+ // Initialization that only requires the QGuiApplication object to be available
+ }
+
void qmlEngineAvailable(QQmlEngine *engine)
{
+ // Initialization requiring the QQmlEngine to be constructed
engine->rootContext()->setContextProperty("myContextProperty", QVariant(true));
}
+
+ void cleanupTestCase()
+ {
+ // Implement custom resource cleanup
+ }
};
QUICK_TEST_MAIN_WITH_SETUP(mytest, Setup)
diff --git a/src/qmltest/doc/snippets/testApp/CMakeLists.txt b/src/qmltest/doc/snippets/testApp/CMakeLists.txt
new file mode 100644
index 0000000000..71eb6bebb7
--- /dev/null
+++ b/src/qmltest/doc/snippets/testApp/CMakeLists.txt
@@ -0,0 +1,46 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.20)
+
+project(MyApplication VERSION 0.1 LANGUAGES CXX)
+
+qt_standard_project_setup(REQUIRES 6.6)
+
+find_package(Qt6 COMPONENTS REQUIRED Quick QuickControls2)
+
+#! [project-cmake]
+add_subdirectory(MyModule)
+add_subdirectory(tests)
+
+qt_add_executable(MyApplication
+ src/main.cpp
+)
+
+qt_add_qml_module(MyApplication
+ URI MyApplication
+ QML_FILES main.qml
+)
+#! [project-cmake]
+
+set_target_properties(MyApplication PROPERTIES
+ MACOSX_BUNDLE_GUI_IDENTIFIER my.demoapp.com
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+target_include_directories(MyApplication
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
+)
+
+target_link_libraries(MyApplication
+ PRIVATE
+ Qt6::Quick
+ Qt6::QuickControls2
+ MyModule
+)
+
+install(TARGETS MyApplication
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
diff --git a/src/qmltest/doc/snippets/testApp/MyModule/CMakeLists.txt b/src/qmltest/doc/snippets/testApp/MyModule/CMakeLists.txt
new file mode 100644
index 0000000000..dbbbbd45b0
--- /dev/null
+++ b/src/qmltest/doc/snippets/testApp/MyModule/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.20)
+
+project(${MODULE_NAME} LANGUAGES CXX)
+
+qt_standard_project_setup(REQUIRES 6.6)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+#! [add library]
+qt_add_library(MyModule STATIC)
+
+qt6_add_qml_module(MyModule
+ URI MyModule
+ QML_FILES MyButton.qml
+)
+#! [add library]
+
+set_target_properties(MyModule PROPERTIES
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(MyModule PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+)
+
+target_include_directories(MyModule PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/src/qmltest/doc/snippets/testApp/MyModule/MyButton.qml b/src/qmltest/doc/snippets/testApp/MyModule/MyButton.qml
new file mode 100644
index 0000000000..47e32c3b6c
--- /dev/null
+++ b/src/qmltest/doc/snippets/testApp/MyModule/MyButton.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//! [define]
+import QtQuick
+import QtQuick.Controls
+
+Button {
+ width: 50
+ height: 50
+ onClicked: width = 100
+}
+//! [define]
diff --git a/src/qmltest/doc/snippets/testApp/tests/CMakeLists.txt b/src/qmltest/doc/snippets/testApp/tests/CMakeLists.txt
new file mode 100644
index 0000000000..3255a919f2
--- /dev/null
+++ b/src/qmltest/doc/snippets/testApp/tests/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.2)
+
+project(TestMyApplication LANGUAGES CXX)
+
+enable_testing()
+
+find_package(Qt6 REQUIRED COMPONENTS QuickTest Qml)
+
+qt_standard_project_setup(REQUIRES 6.6)
+
+add_definitions(-DQUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
+
+#! [link against library]
+add_executable(TestMyApplication main.cpp
+ setup.cpp setup.h)
+
+add_test(NAME TestMyApplication COMMAND TestMyApplication)
+
+target_link_libraries(TestMyApplication
+ PRIVATE
+ Qt6::QuickTest
+ Qt6::Qml
+ MyModule
+ MyModuleplugin
+)
+#! [link against library]
diff --git a/src/qmltest/doc/snippets/testApp/tests/main.cpp b/src/qmltest/doc/snippets/testApp/tests/main.cpp
new file mode 100644
index 0000000000..d9f1cd68ed
--- /dev/null
+++ b/src/qmltest/doc/snippets/testApp/tests/main.cpp
@@ -0,0 +1,9 @@
+// # Copyright (C) 2023 The Qt Company Ltd.
+// # SPDX-License-Identifier: BSD-3-Clause
+
+//! [main]
+#include <QtQuickTest/quicktest.h>
+#include "setup.h"
+
+QUICK_TEST_MAIN_WITH_SETUP(TestQML, Setup)
+//! [main]
diff --git a/src/qmltest/doc/snippets/testApp/tests/setup.cpp b/src/qmltest/doc/snippets/testApp/tests/setup.cpp
new file mode 100644
index 0000000000..92ef206e26
--- /dev/null
+++ b/src/qmltest/doc/snippets/testApp/tests/setup.cpp
@@ -0,0 +1,21 @@
+// # Copyright (C) 2023 The Qt Company Ltd.
+// # SPDX-License-Identifier: BSD-3-Clause
+
+//! [setup]
+#include "setup.h"
+
+void Setup::applicationAvailable()
+{
+ // custom code that doesn't require QQmlEngine
+}
+
+void Setup::qmlEngineAvailable(QQmlEngine *engine)
+{
+ // add import paths
+}
+
+void Setup::cleanupTestCase()
+{
+ // custom code to clean up before destruction starts
+}
+//! [setup]
diff --git a/src/qmltest/doc/snippets/testApp/tests/setup.h b/src/qmltest/doc/snippets/testApp/tests/setup.h
new file mode 100644
index 0000000000..b6c5248667
--- /dev/null
+++ b/src/qmltest/doc/snippets/testApp/tests/setup.h
@@ -0,0 +1,24 @@
+// # Copyright (C) 2023 The Qt Company Ltd.
+// # SPDX-License-Identifier: BSD-3-Clause
+
+//! [setup]
+#ifndef SETUP_H
+#define SETUP_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class Setup : public QObject
+{
+ Q_OBJECT
+public:
+ Setup() = default;
+
+public slots:
+ void applicationAvailable();
+ void qmlEngineAvailable(QQmlEngine *engine);
+ void cleanupTestCase();
+};
+
+#endif // SETUP_H
+//! [setup]
diff --git a/src/qmltest/doc/snippets/testApp/tests/tst_testqml.qml b/src/qmltest/doc/snippets/testApp/tests/tst_testqml.qml
new file mode 100644
index 0000000000..5973a31ebb
--- /dev/null
+++ b/src/qmltest/doc/snippets/testApp/tests/tst_testqml.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//! [import]
+import QtQuick
+import QtQuick.Controls
+
+import QtTest
+import MyModule
+
+Item {
+ width: 800
+ height: 600
+
+ MyButton {
+ id: myButton
+ anchors.centerIn: parent
+ }
+
+ TestCase {
+ name: "MyButton"
+ when: windowShown
+
+ function test_clickToExpand() {
+ const widthBeforeClick = myButton.width;
+ mouseClick(myButton);
+ const widthAfterClick = myButton.width;
+ verify(widthBeforeClick < widthAfterClick);
+ }
+ }
+}
+//! [import]
diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc
index d227f44bce..3fa6ccba6f 100644
--- a/src/qmltest/doc/src/qtquicktest-index.qdoc
+++ b/src/qmltest/doc/src/qtquicktest-index.qdoc
@@ -94,16 +94,68 @@
\snippet src_qmltest_qquicktest_snippet.cpp 1
Where "example" is the identifier to use to uniquely identify
- this set of tests. Finally, add \c{CONFIG += qmltestcase} to the project
- file:
+ this set of tests.
+
+ \if defined(onlinedocs)
+ \tab {run-qtquicktest}{tab-cmake}{CMake}{checked}
+ \tab {run-qtquicktest}{tab-qmake}{qmake}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section1 Using CMake
+ \endif
+ Configure your CMakeLists.txt file and build your project using your
+ favorite generator.
+ \badcode
+ cmake_minimum_required(VERSION 3.2)
+
+ project(tst_example LANGUAGES CXX)
+
+ enable_testing()
+
+ find_package(Qt6 REQUIRED COMPONENTS QuickTest Qml)
+
+ #[[The test harness scans the specified source directory recursively
+ for "tst_*.qml" files. By default, it looks in the current directory,
+ which is usually where the executable is. This command makes it look
+ in the project's source directory instead.]]
+ add_definitions(-DQUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
+
+ qt_standard_project_setup(REQUIRES 6.6)
+ add_executable(tst_example tst_example.cpp)
+
+ add_test(NAME tst_example COMMAND tst_example)
+
+ target_link_libraries(tst_example
+ PRIVATE
+ Qt6::QuickTest
+ Qt6::Qml
+ )
+ \endcode
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+ \else
+ \section1 Using qmake
+ \endif
+ Add \c{CONFIG += qmltestcase} to your project file:
\badcode
- TEMPLATE = app
- TARGET = tst_example
- CONFIG += warn_on qmltestcase
- SOURCES += tst_example.cpp
+ TEMPLATE = app
+ TARGET = tst_example
+ CONFIG += warn_on qmltestcase
+ SOURCES += tst_example.cpp
\endcode
+ If \c IMPORTPATH is specified in your .pro file, each import path added to \c IMPORTPATH
+ will be passed as a command-line argument when the test is run using "make check":
+
+ \badcode
+ IMPORTPATH += $$PWD/../imports/my_module1 $$PWD/../imports/my_module2
+ \endcode
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
+
The test harness scans the specified source directory recursively
for "tst_*.qml" files. If \c{QUICK_TEST_SOURCE_DIR} is not defined,
then the current directory will be scanned when the harness is run.
@@ -136,12 +188,6 @@
If your test case needs QML imports, then you can add them as
\c{-import} options to the test program command-line.
- If \c IMPORTPATH is specified in your .pro file, each import path added to \c IMPORTPATH
- will be passed as a command-line argument when the test is run using "make check":
-
- \badcode
- IMPORTPATH += $$PWD/../imports/my_module1 $$PWD/../imports/my_module2
- \endcode
The \c{-functions} command-line option will return a list of the current
tests functions. It is possible to run a single test function using the name
@@ -157,6 +203,10 @@
tst_example -help
\endcode
+ \note Running a Qt Quick test case will always show a window on the screen,
+ even if the test code doesn't involve any Quick UI. To avoid that, run the
+ test executable with \c {-platform offscreen}.
+
\section1 Executing C++ Before QML Tests
To execute C++ code before any of the QML tests are run, the
@@ -164,7 +214,7 @@
setting context properties on the QML engine, amongst other things.
The macro is identical to \c QUICK_TEST_MAIN, except that it takes an
- additional \c QObject* argument. The test framework will call slots and
+ additional type argument. The test framework will call slots and
invokable functions with the following names:
\table
diff --git a/src/qmltest/doc/src/qtquicktest.qdoc b/src/qmltest/doc/src/qtquicktest.qdoc
index 357aa9b524..d349a725f8 100644
--- a/src/qmltest/doc/src/qtquicktest.qdoc
+++ b/src/qmltest/doc/src/qtquicktest.qdoc
@@ -41,9 +41,9 @@
The \a name argument uniquely identifies this set of tests.
This macro is identical to QUICK_TEST_MAIN(), except that it takes an
- additional argument \a QuickTestSetupClass, a pointer to a QObject-derived
- class. With this class it is possible to define additional setup code to
- execute before running the QML test.
+ additional argument \a QuickTestSetupClass, the type of a QObject-derived
+ class which will be instantiated. With this class it is possible to define
+ additional setup code to execute before running the QML test.
\note The macro assumes that your test sources are in the current
directory, unless the \c QUICK_TEST_SOURCE_DIR environment variable is set.
diff --git a/src/qmltest/qtestoptions_p.h b/src/qmltest/qtestoptions_p.h
index 3b2a029a3a..3e2d01a84a 100644
--- a/src/qmltest/qtestoptions_p.h
+++ b/src/qmltest/qtestoptions_p.h
@@ -15,12 +15,7 @@
// We mean it.
//
-#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
-# include <QtTest/qtest_global.h>
-# include <QtCore/private/qglobal_p.h>
-#else
# include <QtTest/qttestglobal.h>
-#endif
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp
index 1fd36edd62..ce29dadb47 100644
--- a/src/qmltest/quicktest.cpp
+++ b/src/qmltest/quicktest.cpp
@@ -4,6 +4,7 @@
#include "quicktest_p.h"
#include "quicktestresult_p.h"
#include <QtTest/qtestsystem.h>
+#include <QtTest/private/qtestcrashhandler_p.h>
#include "qtestoptions_p.h"
#include <QtQml/qqml.h>
#include <QtQml/qqmlengine.h>
@@ -223,22 +224,45 @@ static void handleCompileErrors(
results.stopLogging();
}
-bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000)
+class SimpleReceiver : public QObject {
+ Q_OBJECT
+public:
+ bool signalReceived = false;
+public slots:
+ void slotFun() { signalReceived = true; }
+};
+
+bool qWaitForSignal(QObject *obj, const char* signal, int timeout)
{
- QSignalSpy spy(obj, signal);
- QElapsedTimer timer;
- timer.start();
-
- while (!spy.size()) {
- int remaining = timeout - int(timer.elapsed());
- if (remaining <= 0)
- break;
- QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QTest::qSleep(10);
+ if (!obj || !signal) {
+ qWarning("qWaitForSignal: invalid arguments");
+ return false;
+ }
+ if (((signal[0] - '0') & 0x03) != QSIGNAL_CODE) {
+ qWarning("qWaitForSignal: not a valid signal, use the SIGNAL macro");
+ return false;
+ }
+
+ int sig = obj->metaObject()->indexOfSignal(signal + 1);
+ if (sig == -1) {
+ const QByteArray ba = QMetaObject::normalizedSignature(signal + 1);
+ sig = obj->metaObject()->indexOfSignal(ba.constData());
+ if (sig == -1) {
+ qWarning("qWaitForSignal: no such signal %s::%s", obj->metaObject()->className(),
+ signal);
+ return false;
+ }
}
- return spy.size();
+ SimpleReceiver receiver;
+ static int slot = receiver.metaObject()->indexOfSlot("slotFun()");
+ if (!QMetaObject::connect(obj, sig, &receiver, slot)) {
+ qWarning("qWaitForSignal: failed to connect to signal %s::%s",
+ obj->metaObject()->className(), signal);
+ return false;
+ }
+
+ return QTest::qWaitFor([&]() { return receiver.signalReceived; }, timeout);
}
template <typename... Args>
@@ -264,7 +288,7 @@ class TestCaseCollector
public:
typedef QList<QString> TestCaseList;
- TestCaseCollector(const QFileInfo &fileInfo, QQmlEngine *engine)
+ TestCaseCollector(const QFileInfo &fileInfo, QQmlEngine *engine) : m_engine(engine)
{
QString path = fileInfo.absoluteFilePath();
if (path.startsWith(QLatin1String(":/")))
@@ -276,7 +300,8 @@ public:
if (component.isReady()) {
QQmlRefPointer<QV4::ExecutableCompilationUnit> rootCompilationUnit
= QQmlComponentPrivate::get(&component)->compilationUnit;
- TestCaseEnumerationResult result = enumerateTestCases(rootCompilationUnit.data());
+ TestCaseEnumerationResult result = enumerateTestCases(
+ rootCompilationUnit->baseCompilationUnit().data());
m_testCases = result.testCases + result.finalizedPartialTestCases();
m_errors += result.errors;
}
@@ -288,6 +313,7 @@ public:
private:
TestCaseList m_testCases;
QList<QQmlError> m_errors;
+ QQmlEngine *m_engine = nullptr;
struct TestCaseEnumerationResult
{
@@ -316,8 +342,8 @@ private:
};
TestCaseEnumerationResult enumerateTestCases(
- const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
- const Object *object = nullptr)
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
+ const QV4::CompiledData::Object *object = nullptr)
{
QQmlType testCaseType;
for (quint32 i = 0, count = compilationUnit->importCount(); i < count; ++i) {
@@ -330,7 +356,8 @@ private:
if (!typeQualifier.isEmpty())
testCaseTypeName = typeQualifier % QLatin1Char('.') % testCaseTypeName;
- testCaseType = compilationUnit->typeNameCache->query(testCaseTypeName).type;
+ testCaseType = compilationUnit->typeNameCache->query(
+ testCaseTypeName, QQmlTypeLoader::get(m_engine)).type;
if (testCaseType.isValid())
break;
}
@@ -339,11 +366,11 @@ private:
if (!object) // Start at root of compilation unit if not enumerating a specific child
object = compilationUnit->objectAt(0);
- if (object->hasFlag(Object::IsInlineComponentRoot))
+ if (object->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot))
return result;
- if (const auto superTypeUnit = compilationUnit->resolvedTypes.value(
- object->inheritedTypeNameIndex)->compilationUnit()) {
+ if (const auto superTypeUnit = compilationUnit->resolvedType(object->inheritedTypeNameIndex)
+ ->compilationUnit()) {
// We have a non-C++ super type, which could indicate we're a subtype of a TestCase
if (testCaseType.isValid() && superTypeUnit->url() == testCaseType.sourceUrl())
result.isTestCase = true;
@@ -386,7 +413,7 @@ private:
for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
if (binding->type() == QV4::CompiledData::Binding::Type_Object) {
- const Object *child = compilationUnit->objectAt(binding->value.objectIndex);
+ const QV4::CompiledData::Object *child = compilationUnit->objectAt(binding->value.objectIndex);
result << enumerateTestCases(compilationUnit, child);
}
}
@@ -541,6 +568,11 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
return 1;
}
+ std::optional<QTest::CrashHandler::FatalSignalHandler> handler;
+ QTest::CrashHandler::prepareStackTrace();
+ if (!QTest::Internal::noCrashHandler)
+ handler.emplace();
+
qputenv("QT_QTESTLIB_RUNNING", "1");
QSet<QString> commandLineTestFunctions(QTest::testFunctions.cbegin(), QTest::testFunctions.cend());
@@ -616,39 +648,33 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
handleCompileErrors(fi, view.errors(), view.engine(), &view);
continue;
}
- if (!QTestRootObject::instance()->hasQuit) {
- // If the test already quit, then it was performed
- // synchronously during setSource(). Otherwise it is
- // an asynchronous test and we need to show the window
- // and wait for the first frame to be rendered
- // and then wait for quit indication.
- view.setFramePosition(QPoint(50, 50));
- if (view.size().isEmpty()) { // Avoid hangs with empty windows.
- view.resize(200, 200);
- }
- view.show();
- if (!QTest::qWaitForWindowExposed(&view)) {
- qWarning().nospace()
- << "Test '" << QDir::toNativeSeparators(path) << "' window not exposed after show().";
- }
- view.requestActivate();
- if (!QTest::qWaitForWindowActive(&view)) {
- qWarning().nospace()
- << "Test '" << QDir::toNativeSeparators(path) << "' window not active after requestActivate().";
- }
- if (view.isExposed()) {
- // Defer property update until event loop has started
- QTimer::singleShot(0, []() {
- QTestRootObject::instance()->setWindowShown(true);
- });
- } else {
- qWarning().nospace()
- << "Test '" << QDir::toNativeSeparators(path) << "' window was never exposed! "
- << "If the test case was expecting windowShown, it will hang.";
- }
- if (!QTestRootObject::instance()->hasQuit && QTestRootObject::instance()->hasTestCase())
- eventLoop.exec();
+
+ view.setFramePosition(QPoint(50, 50));
+ if (view.size().isEmpty()) { // Avoid hangs with empty windows.
+ view.resize(200, 200);
+ }
+ view.show();
+ if (!QTest::qWaitForWindowExposed(&view)) {
+ qWarning().nospace()
+ << "Test '" << QDir::toNativeSeparators(path) << "' window not exposed after show().";
+ }
+ view.requestActivate();
+ if (!QTest::qWaitForWindowActive(&view)) {
+ qWarning().nospace()
+ << "Test '" << QDir::toNativeSeparators(path) << "' window not active after requestActivate().";
+ }
+ if (view.isExposed()) {
+ // Defer property update until event loop has started
+ QTimer::singleShot(0, []() {
+ QTestRootObject::instance()->setWindowShown(true);
+ });
+ } else {
+ qWarning().nospace()
+ << "Test '" << QDir::toNativeSeparators(path) << "' window was never exposed! "
+ << "If the test case was expecting windowShown, it will hang.";
}
+ if (!QTestRootObject::instance()->hasQuit && QTestRootObject::instance()->hasTestCase())
+ eventLoop.exec();
}
if (setup)
@@ -673,3 +699,4 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
QT_END_NAMESPACE
#include "moc_quicktest_p.cpp"
+#include "quicktest.moc"
diff --git a/src/qmltest/quicktest.h b/src/qmltest/quicktest.h
index 7687282d2a..4dd9127b18 100644
--- a/src/qmltest/quicktest.h
+++ b/src/qmltest/quicktest.h
@@ -12,8 +12,8 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickWindow;
-Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir);
-Q_QUICK_TEST_EXPORT int quick_test_main_with_setup(int argc, char **argv, const char *name, const char *sourceDir, QObject *setup);
+Q_QMLTEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir);
+Q_QMLTEST_EXPORT int quick_test_main_with_setup(int argc, char **argv, const char *name, const char *sourceDir, QObject *setup);
#ifdef QUICK_TEST_SOURCE_DIR
@@ -68,17 +68,17 @@ Q_QUICK_TEST_EXPORT int quick_test_main_with_setup(int argc, char **argv, const
namespace QQuickTest {
static const int defaultTimeout = 5000;
-Q_QUICK_TEST_EXPORT bool qIsPolishScheduled(const QQuickItem *item);
-Q_QUICK_TEST_EXPORT bool qIsPolishScheduled(const QQuickWindow *window);
+Q_QMLTEST_EXPORT bool qIsPolishScheduled(const QQuickItem *item);
+Q_QMLTEST_EXPORT bool qIsPolishScheduled(const QQuickWindow *window);
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
#if QT_DEPRECATED_SINCE(6, 4)
QT_DEPRECATED_X("Use qWaitForPolish(QQuickItem *) instead")
- Q_QUICK_TEST_EXPORT bool qWaitForItemPolished(const QQuickItem *item, int timeout = defaultTimeout);
+ Q_QMLTEST_EXPORT bool qWaitForItemPolished(const QQuickItem *item, int timeout = defaultTimeout);
#endif
#endif
-Q_QUICK_TEST_EXPORT bool qWaitForPolish(const QQuickItem *item, int timeout = defaultTimeout);
-Q_QUICK_TEST_EXPORT bool qWaitForPolish(const QQuickWindow *window, int timeout = defaultTimeout);
+Q_QMLTEST_EXPORT bool qWaitForPolish(const QQuickItem *item, int timeout = defaultTimeout);
+Q_QMLTEST_EXPORT bool qWaitForPolish(const QQuickWindow *window, int timeout = defaultTimeout);
}
QT_END_NAMESPACE
diff --git a/src/qmltest/quicktest_p.h b/src/qmltest/quicktest_p.h
index 938ae6cdfc..2deffab8ca 100644
--- a/src/qmltest/quicktest_p.h
+++ b/src/qmltest/quicktest_p.h
@@ -21,9 +21,11 @@
#include <QtQml/qqmlpropertymap.h>
#include <QtQml/qqml.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
-class Q_QUICK_TEST_PRIVATE_EXPORT QTestRootObject : public QObject
+class Q_QMLTEST_EXPORT QTestRootObject : public QObject
{
Q_OBJECT
Q_PROPERTY(bool windowShown READ windowShown NOTIFY windowShownChanged)
@@ -83,6 +85,8 @@ private:
QQmlPropertyMap *m_defined;
};
+bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000);
+
QT_END_NAMESPACE
#endif // QUICKTEST_P_H
diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp
index b1b8e60eab..9bd2a9887c 100644
--- a/src/qmltest/quicktestevent.cpp
+++ b/src/qmltest/quicktestevent.cpp
@@ -318,14 +318,14 @@ bool QuickTestEvent::mouseDoubleClickSequence
}
bool QuickTestEvent::mouseMove
- (QObject *item, qreal x, qreal y, int delay, int buttons)
+ (QObject *item, qreal x, qreal y, int delay, int buttons, int modifiers)
{
QWindow *view = eventWindow(item);
if (!view)
return false;
const Qt::MouseButtons effectiveButtons = buttons ? Qt::MouseButtons(buttons) : m_pressedButtons;
QtQuickTest::mouseEvent(QtQuickTest::MouseMove, view, item,
- Qt::MouseButton(int(effectiveButtons)), Qt::NoModifier,
+ Qt::MouseButton(int(effectiveButtons)), Qt::KeyboardModifiers(modifiers),
QPointF(x, y), delay);
return true;
}
diff --git a/src/qmltest/quicktestevent_p.h b/src/qmltest/quicktestevent_p.h
index c75d8f6706..6f73130b5b 100644
--- a/src/qmltest/quicktestevent_p.h
+++ b/src/qmltest/quicktestevent_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
class QuickTestEvent;
-class Q_QUICK_TEST_PRIVATE_EXPORT QQuickTouchEventSequence : public QObject
+class Q_QMLTEST_EXPORT QQuickTouchEventSequence : public QObject
{
Q_OBJECT
QML_ANONYMOUS
@@ -45,7 +45,7 @@ private:
QuickTestEvent * const m_testEvent;
};
-class Q_QUICK_TEST_PRIVATE_EXPORT QuickTestEvent : public QObject
+class Q_QMLTEST_EXPORT QuickTestEvent : public QObject
{
Q_OBJECT
Q_PROPERTY(int defaultMouseDelay READ defaultMouseDelay FINAL)
@@ -77,7 +77,7 @@ public Q_SLOTS:
int modifiers, int delay);
bool mouseDoubleClickSequence(QObject *item, qreal x, qreal y, int button,
int modifiers, int delay);
- bool mouseMove(QObject *item, qreal x, qreal y, int delay, int buttons);
+ bool mouseMove(QObject *item, qreal x, qreal y, int delay, int buttons, int modifiers);
#if QT_CONFIG(wheelevent)
bool mouseWheel(QObject *item, qreal x, qreal y, int buttons,
diff --git a/src/qmltest/quicktestglobal.h b/src/qmltest/quicktestglobal.h
index 42f886dfac..3f8c5c3582 100644
--- a/src/qmltest/quicktestglobal.h
+++ b/src/qmltest/quicktestglobal.h
@@ -5,19 +5,6 @@
#define TESTQUICKGLOBAL_H
#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_STATIC
-# if defined(QT_BUILD_QMLTEST_LIB)
-# define Q_QUICK_TEST_EXPORT Q_DECL_EXPORT
-# else
-# define Q_QUICK_TEST_EXPORT Q_DECL_IMPORT
-# endif
-#else
-# define Q_QUICK_TEST_EXPORT
-#endif
-
-QT_END_NAMESPACE
+#include <QtQuickTest/qtquicktestexports.h>
#endif
diff --git a/src/qmltest/quicktestglobal_p.h b/src/qmltest/quicktestglobal_p.h
index 3c94256137..e3be0ebde1 100644
--- a/src/qmltest/quicktestglobal_p.h
+++ b/src/qmltest/quicktestglobal_p.h
@@ -18,6 +18,4 @@
#include "quicktestglobal.h"
#include "private/qglobal_p.h"
-#define Q_QUICK_TEST_PRIVATE_EXPORT Q_QUICK_TEST_EXPORT
-
#endif // QUICKTESTGLOBAL_P_H
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index e248625e01..c5014f66d4 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -4,6 +4,7 @@
#include "quicktestresult_p.h"
#include "quicktest.h"
+#include "quicktest_p.h"
#include <QtTest/qtestcase.h>
#include <QtTest/qtestsystem.h>
#include <QtTest/private/qtestblacklist_p.h>
@@ -12,11 +13,6 @@
#include <QtTest/private/qtestlog_p.h>
#include "qtestoptions_p.h"
#include <QtTest/qbenchmark.h>
-// qbenchmark_p.h pulls windows.h via 3rd party; prevent it from defining
-// the min/max macros which would clash with qnumeric_p.h's usage of min()/max().
-#if defined(Q_OS_WIN32) && !defined(NOMINMAX)
-# define NOMINMAX
-#endif
#include <QtTest/private/qbenchmark_p.h>
#include <QtCore/qset.h>
#include <QtCore/qmap.h>
@@ -45,9 +41,7 @@ static const char *globalProgramName = nullptr;
static bool loggingStarted = false;
static QBenchmarkGlobalData globalBenchmarkData;
-extern bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000);
-
-class Q_QUICK_TEST_EXPORT QuickTestImageObject : public QObject
+class Q_QMLTEST_EXPORT QuickTestImageObject : public QObject
{
Q_OBJECT
@@ -229,7 +223,8 @@ void QuickTestResult::setFunctionName(const QString &name)
QString fullName = d->testCaseName + QLatin1String("::") + name;
QTestResult::setCurrentTestFunction
(d->intern(fullName).constData());
- QTestPrivate::checkBlackLists(fullName.toUtf8().constData(), nullptr);
+ if (QTestPrivate::checkBlackLists(fullName.toUtf8().constData(), nullptr))
+ QTestResult::setBlacklistCurrentTest(true);
}
} else {
QTestResult::setCurrentTestFunction(nullptr);
@@ -258,7 +253,10 @@ void QuickTestResult::setDataTag(const QString &tag)
if (!tag.isEmpty()) {
QTestData *data = &(QTest::newRow(tag.toUtf8().constData()));
QTestResult::setCurrentTestData(data);
- QTestPrivate::checkBlackLists((testCaseName() + QLatin1String("::") + functionName()).toUtf8().constData(), tag.toUtf8().constData());
+ if (QTestPrivate::checkBlackLists((testCaseName() + QLatin1String("::")
+ + functionName()).toUtf8().constData(), tag.toUtf8().constData())) {
+ QTestResult::setBlacklistCurrentTest(true);
+ }
emit dataTagChanged();
} else {
QTestResult::setCurrentTestData(nullptr);
@@ -506,7 +504,7 @@ bool QuickTestResult::fuzzyCompare(const QVariant &actual, const QVariant &expec
return false;
}
-void QuickTestResult::stringify(QQmlV4Function *args)
+void QuickTestResult::stringify(QQmlV4FunctionPtr args)
{
if (args->length() < 1)
args->setReturnValue(QV4::Encode::null());
diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h
index d0ae853f86..1e274e1804 100644
--- a/src/qmltest/quicktestresult_p.h
+++ b/src/qmltest/quicktestresult_p.h
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE
class QUrl;
class QuickTestResultPrivate;
-class Q_QUICK_TEST_PRIVATE_EXPORT QuickTestResult : public QObject
+class Q_QMLTEST_EXPORT QuickTestResult : public QObject
{
Q_OBJECT
Q_PROPERTY(QString testCaseName READ testCaseName WRITE setTestCaseName NOTIFY testCaseNameChanged)
@@ -91,7 +91,7 @@ public Q_SLOTS:
void finishTestDataCleanup();
void finishTestFunction();
- void stringify(QQmlV4Function *args);
+ void stringify(QQmlV4FunctionPtr args);
void fail(const QString &message, const QUrl &location, int line);
bool verify(bool success, const QString &message,
diff --git a/src/qmltest/quicktestutil.cpp b/src/qmltest/quicktestutil.cpp
index 15f530c60e..c7cac4b4b2 100644
--- a/src/qmltest/quicktestutil.cpp
+++ b/src/qmltest/quicktestutil.cpp
@@ -87,7 +87,7 @@ int QuickTestUtil::callerLine(int frameIndex) const
QVector<QV4::StackFrame> stack = v4->stackTrace(frameIndex + 2);
if (stack.size() > frameIndex + 1)
- return stack.at(frameIndex + 1).line;
+ return qAbs(stack.at(frameIndex + 1).line);
return -1;
}
diff --git a/src/qmltest/quicktestutil_p.h b/src/qmltest/quicktestutil_p.h
index 2c06f37224..d9c0241cbe 100644
--- a/src/qmltest/quicktestutil_p.h
+++ b/src/qmltest/quicktestutil_p.h
@@ -19,11 +19,12 @@
#include <QtCore/qobject.h>
#include <QtQml/qqml.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <QtQml/qjsvalue.h>
QT_BEGIN_NAMESPACE
-class Q_QUICK_TEST_PRIVATE_EXPORT QuickTestUtil : public QObject
+class Q_QMLTEST_EXPORT QuickTestUtil : public QObject
{
Q_OBJECT
Q_PROPERTY(bool printAvailableFunctions READ printAvailableFunctions NOTIFY printAvailableFunctionsChanged)
@@ -50,6 +51,13 @@ public Q_SLOTS:
QJSValue callerFile(int frameIndex = 0) const;
int callerLine(int frameIndex = 0) const;
+
+ Q_REVISION(6, 7) QString signalHandlerName(const QString &signalName)
+ {
+ if (QQmlSignalNames::isHandlerName(signalName))
+ return signalName;
+ return QQmlSignalNames::signalNameToHandlerName(signalName);
+ }
};
QT_END_NAMESPACE
diff --git a/src/qmltoolingsettings/CMakeLists.txt b/src/qmltoolingsettings/CMakeLists.txt
new file mode 100644
index 0000000000..8776a6dd98
--- /dev/null
+++ b/src/qmltoolingsettings/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_module(QmlToolingSettingsPrivate
+ STATIC
+ INTERNAL_MODULE
+ SOURCES
+ qqmltoolingsettings_p.h
+ qqmltoolingsettings.cpp
+
+ qqmltoolingutils_p.h
+ qqmltoolingutils.cpp
+ PUBLIC_LIBRARIES
+ Qt::Core
+ GENERATE_CPP_EXPORTS
+)
diff --git a/src/qmltoolingsettings/qqmltoolingsettings.cpp b/src/qmltoolingsettings/qqmltoolingsettings.cpp
new file mode 100644
index 0000000000..5f8b92a8d2
--- /dev/null
+++ b/src/qmltoolingsettings/qqmltoolingsettings.cpp
@@ -0,0 +1,140 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmltoolingsettings_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qset.h>
+#if QT_CONFIG(settings)
+#include <QtCore/qsettings.h>
+#endif
+#include <QtCore/qstandardpaths.h>
+
+using namespace Qt::StringLiterals;
+
+void QQmlToolingSettings::addOption(const QString &name, QVariant defaultValue)
+{
+ if (defaultValue.isValid()) {
+ m_values[name] = defaultValue;
+ }
+}
+
+bool QQmlToolingSettings::read(const QString &settingsFilePath)
+{
+#if QT_CONFIG(settings)
+ if (!QFileInfo::exists(settingsFilePath))
+ return false;
+
+ if (m_currentSettingsPath == settingsFilePath)
+ return true;
+
+ QSettings settings(settingsFilePath, QSettings::IniFormat);
+
+ for (const QString &key : settings.allKeys())
+ m_values[key] = settings.value(key).toString();
+
+ m_currentSettingsPath = settingsFilePath;
+
+ return true;
+#else
+ Q_UNUSED(settingsFilePath);
+ return false;
+#endif
+}
+
+bool QQmlToolingSettings::writeDefaults() const
+{
+#if QT_CONFIG(settings)
+ const QString path = QFileInfo(u".%1.ini"_s.arg(m_toolName)).absoluteFilePath();
+
+ QSettings settings(path, QSettings::IniFormat);
+ for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it) {
+ settings.setValue(it.key(), it.value().isNull() ? QString() : it.value());
+ }
+
+ settings.sync();
+
+ if (settings.status() != QSettings::NoError) {
+ qWarning() << "Failed to write default settings to" << path
+ << "Error:" << settings.status();
+ return false;
+ }
+
+ qInfo() << "Wrote default settings to" << path;
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool QQmlToolingSettings::search(const QString &path)
+{
+#if QT_CONFIG(settings)
+ QFileInfo fileInfo(path);
+ QDir dir(fileInfo.isDir() ? path : fileInfo.dir());
+
+ QSet<QString> dirs;
+
+ const QString settingsFileName = u".%1.ini"_s.arg(m_toolName);
+
+ while (dir.exists() && dir.isReadable()) {
+ const QString dirPath = dir.absolutePath();
+
+ if (m_seenDirectories.contains(dirPath)) {
+ const QString cachedIniPath = m_seenDirectories[dirPath];
+ if (cachedIniPath.isEmpty())
+ return false;
+
+ return read(cachedIniPath);
+ }
+
+ dirs << dirPath;
+
+ const QString iniFile = dir.absoluteFilePath(settingsFileName);
+
+ if (read(iniFile)) {
+ for (const QString &dir : std::as_const(dirs))
+ m_seenDirectories[dir] = iniFile;
+ return true;
+ }
+
+ if (!dir.cdUp())
+ break;
+ }
+
+ if (const QString iniFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, u"%1.ini"_s.arg(m_toolName));
+ !iniFile.isEmpty()) {
+ if (read(iniFile)) {
+ for (const QString &dir : std::as_const(dirs))
+ m_seenDirectories[dir] = iniFile;
+ return true;
+ }
+ }
+
+ // No INI file found anywhere, record the failure so we won't have to traverse the entire
+ // filesystem again
+ for (const QString &dir : std::as_const(dirs))
+ m_seenDirectories[dir] = QString();
+
+#endif
+ Q_UNUSED(path);
+ return false;
+}
+
+QVariant QQmlToolingSettings::value(QString name) const
+{
+ return m_values.value(name);
+}
+
+bool QQmlToolingSettings::isSet(QString name) const
+{
+ if (!m_values.contains(name))
+ return false;
+
+ QVariant variant = m_values[name];
+
+ // Unset is encoded as an empty string
+ return !(variant.canConvert(QMetaType(QMetaType::QString)) && variant.toString().isEmpty());
+}
diff --git a/src/qmltoolingsettings/qqmltoolingsettings_p.h b/src/qmltoolingsettings/qqmltoolingsettings_p.h
new file mode 100644
index 0000000000..555f6dec79
--- /dev/null
+++ b/src/qmltoolingsettings/qqmltoolingsettings_p.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLTOOLINGSETTINGS_P_H
+#define QQMLTOOLINGSETTINGS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlToolingSettings
+{
+public:
+ QQmlToolingSettings(const QString &toolName) : m_toolName(toolName) { }
+
+ void addOption(const QString &name, const QVariant defaultValue = QVariant());
+
+ bool writeDefaults() const;
+ bool search(const QString &path);
+
+ QVariant value(QString name) const;
+ bool isSet(QString name) const;
+
+private:
+ QString m_toolName;
+ QString m_currentSettingsPath;
+ QHash<QString, QString> m_seenDirectories;
+ QVariantHash m_values;
+
+ bool read(const QString &settingsFilePath);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTOOLINGSETTINGS_P_H
diff --git a/src/qmltoolingsettings/qqmltoolingutils.cpp b/src/qmltoolingsettings/qqmltoolingutils.cpp
new file mode 100644
index 0000000000..1cf1602b4b
--- /dev/null
+++ b/src/qmltoolingsettings/qqmltoolingutils.cpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmltoolingutils_p.h"
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdir.h>
+
+using namespace Qt::StringLiterals;
+
+/*!
+\internal
+
+Helper utils to help QQmlTooling retrieve certain values from the environment or command line
+options.
+It helps to keep the warning messages consistent between tools like qmlls and qmllint when
+they use environment variables, for examples.
+*/
+
+void QQmlToolingUtils::warnForInvalidDirs(const QStringList &dirs, const QString &origin)
+{
+ for (const QString &path : dirs) {
+ QFileInfo info(path);
+ if (!info.exists()) {
+ qWarning().noquote().nospace()
+ << u"Argument \"%1\" %2 does not exist."_s.arg(path, origin);
+ continue;
+ }
+ if (!info.isDir()) {
+ qWarning().noquote().nospace()
+ << "Argument \"" << path << "\" " << origin << " is not a directory.";
+ continue;
+ }
+ }
+}
+
+QStringList
+QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(const QString &environmentVariableName)
+{
+ const QStringList envPaths = qEnvironmentVariable(environmentVariableName.toUtf8())
+ .split(QDir::listSeparator(), Qt::SkipEmptyParts);
+ warnForInvalidDirs(envPaths,
+ u"from environment variable \"%1\""_s.arg(environmentVariableName));
+ return envPaths;
+}
+
+QStringList QQmlToolingUtils::getAndWarnForInvalidDirsFromOption(const QCommandLineParser &parser,
+ const QCommandLineOption &option)
+{
+ if (!parser.isSet(option))
+ return {};
+
+ const QStringList dirs = parser.values(option);
+ const QString optionName = option.names().constFirst();
+ warnForInvalidDirs(dirs, u"passed to -%1"_s.arg(optionName));
+ return dirs;
+}
diff --git a/src/qmltoolingsettings/qqmltoolingutils_p.h b/src/qmltoolingsettings/qqmltoolingutils_p.h
new file mode 100644
index 0000000000..877d490355
--- /dev/null
+++ b/src/qmltoolingsettings/qqmltoolingutils_p.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLTOOLINGUTILS_P_H
+#define QQMLTOOLINGUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qcommandlineparser.h>
+#include <QtCore/qcommandlineoption.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlToolingUtils
+{
+private:
+ static void warnForInvalidDirs(const QStringList &dirs, const QString &origin);
+public:
+ static QStringList getAndWarnForInvalidDirsFromEnv(const QString &environmentVariableName);
+ static QStringList getAndWarnForInvalidDirsFromOption(const QCommandLineParser &parser,
+ const QCommandLineOption &option);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTOOLINGUTILS_P_H
diff --git a/src/qmltyperegistrar/CMakeLists.txt b/src/qmltyperegistrar/CMakeLists.txt
index f28ccd1e43..7b17e8ddba 100644
--- a/src/qmltyperegistrar/CMakeLists.txt
+++ b/src/qmltyperegistrar/CMakeLists.txt
@@ -9,16 +9,27 @@ qt_internal_add_module(QmlTypeRegistrarPrivate
STATIC
INTERNAL_MODULE
SOURCES
- qqmljsstreamwriter.cpp qqmljsstreamwriter_p.h
+ qanystringviewutils_p.h
qmetatypesjsonprocessor.cpp qmetatypesjsonprocessor_p.h
+ qqmljsstreamwriter.cpp qqmljsstreamwriter_p.h
qqmltyperegistrar.cpp qqmltyperegistrar_p.h
+ qqmltyperegistrarconstants_p.h
qqmltypesclassdescription.cpp qqmltypesclassdescription_p.h
qqmltypescreator.cpp qqmltypescreator_p.h
+ qqmltyperegistrarutils_p.h qqmltyperegistrarutils.cpp
+ NO_UNITY_BUILD_SOURCES
+ qqmltypescreator.cpp # S_NAME etc clash
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
PUBLIC_LIBRARIES
- Qt::Core
+ Qt::CorePrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
+ )
+
+qt_internal_add_resource(QmlTypeRegistrarPrivate "jsRootMetaTypes"
+ PREFIX
+ "/qt-project.org/meta_types"
+ FILES
+ jsroot_metatypes.json
)
diff --git a/src/qmltyperegistrar/jsroot_metatypes.json b/src/qmltyperegistrar/jsroot_metatypes.json
new file mode 100644
index 0000000000..768d5f78b2
--- /dev/null
+++ b/src/qmltyperegistrar/jsroot_metatypes.json
@@ -0,0 +1,11097 @@
+[
+ {
+ "classes": [
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Object",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Object",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "ObjectPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Object"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "valueOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "hasOwnProperty"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "isPrototypeOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "propertyIsEnumerable"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "__defineGetter__"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "__defineSetter__"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "__proto__"
+ }
+ ],
+ "qualifiedClassName": "ObjectPrototype"
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "object",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "object",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "StringPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "String"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "valueOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "charAt"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "charCodeAt"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "codePointAt"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "concat"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "endsWith"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "indexOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "includes"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "lastIndexOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "localeCompare"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "match"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "normalize"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "padEnd"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "padStart"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "repeat"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "replace"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "search"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "slice"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "split"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "startsWith"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "substr"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "substring"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLowerCase"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleLowerCase"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toUpperCase"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleUpperCase"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "trim"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "arg"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "length",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "StringPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "String",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "length",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "String",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "StringPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "NumberPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Number"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "valueOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toFixed"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toExponential"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toPrecision"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "NumberPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Number",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Number",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "NumberPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "BooleanPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Boolean"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "valueOf"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "BooleanPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Boolean",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Boolean",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "BooleanPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "concat"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "copyWithin"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "entries"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "fill"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "find"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "findIndex"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "includes"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "join"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "keys"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "pop"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "push"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "reverse"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "shift"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "slice"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "sort"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "splice"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "unshift"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "indexOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "lastIndexOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "every"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "some"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "forEach"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "map"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "filter"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "reduce"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "reduceRight"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "values"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "length",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "length",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "FunctionPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Function"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "apply"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "call"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "bind"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "connect"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "disconnect"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "length",
+ "type": "number"
+ },
+ {
+ "member": "fakeMember",
+ "name": "caller"
+ },
+ {
+ "member": "fakeMember",
+ "name": "arguments"
+ }
+ ],
+ "qualifiedClassName": "FunctionPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Function",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "prototype",
+ "type": "Object"
+ },
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "length",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Function",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "FunctionPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "DatePrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Date"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toDateString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toTimeString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleDateString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleTimeString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "valueOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getTime"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getYear"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getFullYear"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUTCFullYear"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getMonth"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUTCMonth"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getDate"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUTCDate"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getDay"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUTCDay"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getHours"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUTCHours"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getMinutes"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUTCMinutes"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getSeconds"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUTCSeconds"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getMilliseconds"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUTCMilliseconds"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getTimezoneOffset"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setTime"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setMilliseconds"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUTCMilliseconds"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setSeconds"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUTCSeconds"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setMinutes"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUTCMinutes"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setHours"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUTCHours"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setDate"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUTCDate"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setMonth"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUTCMonth"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setYear"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setFullYear"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUTCFullYear"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toUTCString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toGMTString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toISOString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toJSON"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "DatePrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Date",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Date",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "DatePrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "RegExpPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "RegExp"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "exec"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "test"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "compile"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "flags",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "global"
+ },
+ {
+ "member": "fakeMember",
+ "name": "ignoreCase"
+ },
+ {
+ "member": "fakeMember",
+ "name": "multiline"
+ },
+ {
+ "member": "fakeMember",
+ "name": "source",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "sticky"
+ },
+ {
+ "member": "fakeMember",
+ "name": "unicode"
+ }
+ ],
+ "qualifiedClassName": "RegExpPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "RegExp",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "lastIndex",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "RegExp",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "RegExpPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "ErrorPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Error"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "message",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "name",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "ErrorPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Error",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "stack",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "fileName"
+ },
+ {
+ "member": "fakeMember",
+ "name": "lineNumber"
+ }
+ ],
+ "qualifiedClassName": "Error",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "EvalErrorPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "EvalError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "message",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "name",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "EvalErrorPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "EvalError",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "stack",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "fileName"
+ },
+ {
+ "member": "fakeMember",
+ "name": "lineNumber"
+ }
+ ],
+ "qualifiedClassName": "EvalError",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "EvalErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "RangeErrorPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "RangeError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "message",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "name",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "RangeErrorPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "RangeError",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "stack",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "fileName"
+ },
+ {
+ "member": "fakeMember",
+ "name": "lineNumber"
+ }
+ ],
+ "qualifiedClassName": "RangeError",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "RangeErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "ReferenceErrorPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "ReferenceError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "message",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "name",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "ReferenceErrorPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "ReferenceError",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "stack",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "fileName"
+ },
+ {
+ "member": "fakeMember",
+ "name": "lineNumber"
+ }
+ ],
+ "qualifiedClassName": "ReferenceError",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ReferenceErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "SyntaxErrorPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "SyntaxError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "message",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "name",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "SyntaxErrorPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "SyntaxError",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "stack",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "fileName"
+ },
+ {
+ "member": "fakeMember",
+ "name": "lineNumber"
+ }
+ ],
+ "qualifiedClassName": "SyntaxError",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "SyntaxErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "TypeErrorPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "TypeError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "message",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "name",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "TypeErrorPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "TypeError",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "stack",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "fileName"
+ },
+ {
+ "member": "fakeMember",
+ "name": "lineNumber"
+ }
+ ],
+ "qualifiedClassName": "TypeError",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "TypeErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "URIErrorPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "URIError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "message",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "name",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "URIErrorPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "URIError",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "stack",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "fileName"
+ },
+ {
+ "member": "fakeMember",
+ "name": "lineNumber"
+ }
+ ],
+ "qualifiedClassName": "URIError",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "URIErrorPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "PromisePrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Promise"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "then"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "catch"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "PromisePrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Promise",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Promise",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "PromisePrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "URLPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toJSON"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "hash"
+ },
+ {
+ "member": "fakeMember",
+ "name": "host"
+ },
+ {
+ "member": "fakeMember",
+ "name": "hostname"
+ },
+ {
+ "member": "fakeMember",
+ "name": "href"
+ },
+ {
+ "member": "fakeMember",
+ "name": "origin"
+ },
+ {
+ "member": "fakeMember",
+ "name": "password"
+ },
+ {
+ "member": "fakeMember",
+ "name": "pathname"
+ },
+ {
+ "member": "fakeMember",
+ "name": "port"
+ },
+ {
+ "member": "fakeMember",
+ "name": "protocol"
+ },
+ {
+ "member": "fakeMember",
+ "name": "search"
+ },
+ {
+ "member": "fakeMember",
+ "name": "searchParams"
+ },
+ {
+ "member": "fakeMember",
+ "name": "username"
+ }
+ ],
+ "qualifiedClassName": "URLPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "URL",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "URL",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "URLPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "URLSearchParamsPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "sort"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "append"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "delete"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "has"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "set"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "get"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getAll"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "forEach"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "entries"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "keys"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "values"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "URLSearchParamsPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "URLSearchParams",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "URLSearchParams",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "URLSearchParamsPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "SharedArrayBufferPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "SharedArrayBuffer"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "slice"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "byteLength",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "SharedArrayBufferPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "SharedArrayBuffer",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "SharedArrayBuffer",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "SharedArrayBufferPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "ArrayBufferPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "ArrayBuffer"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "slice"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "byteLength",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "ArrayBufferPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "ArrayBuffer",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "ArrayBuffer",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ArrayBufferPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "DataViewPrototypeBuffer",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "DataViewPrototypeBuffer",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ArrayBufferPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "DataViewPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "DataView"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getInt8"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUint8"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getInt16"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUint16"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getInt32"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUint32"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getFloat32"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getFloat64"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setInt8"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUint8"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setInt16"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUint16"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setInt32"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUint32"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setFloat32"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setFloat64"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUInt8"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUInt16"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getUInt32"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUInt8"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUInt16"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setUInt32"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "buffer",
+ "type": "DataViewPrototypeBuffer"
+ },
+ {
+ "member": "fakeMember",
+ "name": "byteLength",
+ "type": "number"
+ },
+ {
+ "member": "fakeMember",
+ "name": "byteOffset",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "DataViewPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "DataView",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "DataView",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "DataViewPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "WeakSetPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "WeakSet"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "add"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "delete"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "has"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "WeakSetPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "WeakSet",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "WeakSet",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "WeakSetPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "SetPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Set"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "add"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "clear"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "delete"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "entries"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "forEach"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "has"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "keys"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "values"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "size",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "SetPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Set",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Set",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "SetPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "WeakMapPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "WeakMap"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "delete"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "get"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "has"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "set"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "WeakMapPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "WeakMap",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "WeakMap",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "WeakMapPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "MapPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Map"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "clear"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "delete"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "forEach"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "get"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "has"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "keys"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "set"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "values"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "entries"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "size",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "MapPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Map",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Map",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "MapPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "IntrinsicTypedArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "copyWithin"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "entries"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "every"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "fill"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "filter"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "find"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "findIndex"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "forEach"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "includes"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "indexOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "join"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "keys"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "lastIndexOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "map"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "reduce"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "reduceRight"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "reverse"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "some"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "set"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "slice"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "subarray"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toLocaleString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "values"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "buffer"
+ },
+ {
+ "member": "fakeMember",
+ "name": "byteLength"
+ },
+ {
+ "member": "fakeMember",
+ "name": "byteOffset"
+ },
+ {
+ "member": "fakeMember",
+ "name": "length"
+ }
+ ],
+ "qualifiedClassName": "IntrinsicTypedArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Int8ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Int8Array"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Int8ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Int8Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Int8Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Int8ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Uint8ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Uint8Array"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Uint8ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Uint8Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Uint8Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Uint8ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Int16ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Int16Array"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Int16ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Int16Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Int16Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Int16ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Uint16ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Uint16Array"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Uint16ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Uint16Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Uint16Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Uint16ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Int32ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Int32Array"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Int32ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Int32Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Int32Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Int32ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Uint32ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Uint32Array"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Uint32ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Uint32Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Uint32Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Uint32ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Uint8ClampedArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Uint8ClampedArray"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Uint8ClampedArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Uint8ClampedArray",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Uint8ClampedArray",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Uint8ClampedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Float32ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Float32Array"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Float32ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Float32Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Float32Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Float32ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Float64ArrayPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "Float64Array"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "BYTES_PER_ELEMENT",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Float64ArrayPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "IntrinsicTypedArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Float64Array",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Float64Array",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "Float64ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Atomics",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "add"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "and"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "compareExchange"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "exchange"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "isLockFree"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "load"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "or"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "store"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "sub"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "wait"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "wake"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "xor"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Atomics",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Math",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "abs"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "acos"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "acosh"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "asin"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "asinh"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "atan"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "atanh"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "atan2"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "cbrt"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "ceil"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "clz32"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "cos"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "cosh"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "exp"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "expm1"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "floor"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "fround"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "hypot"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "imul"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "log"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "log10"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "log1p"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "log2"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "max"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "min"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "pow"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "random"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "round"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "sign"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "sin"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "sinh"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "sqrt"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "tan"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "tanh"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "trunc"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "E",
+ "type": "number"
+ },
+ {
+ "name": "LN2",
+ "type": "number"
+ },
+ {
+ "name": "LN10",
+ "type": "number"
+ },
+ {
+ "name": "LOG2E",
+ "type": "number"
+ },
+ {
+ "name": "LOG10E",
+ "type": "number"
+ },
+ {
+ "name": "PI",
+ "type": "number"
+ },
+ {
+ "name": "SQRT1_2",
+ "type": "number"
+ },
+ {
+ "name": "SQRT2",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "Math",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "JSON",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "parse"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "stringify"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "JSON",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Reflect",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "apply"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "construct"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "defineProperty"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "deleteProperty"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "get"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getOwnPropertyDescriptor"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "getPrototypeOf"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "has"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "isExtensible"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "ownKeys"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "preventExtensions"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "set"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setPrototypeOf"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Reflect",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Proxy",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Proxy",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "QtPrototype",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "name": "color0",
+ "type": "number"
+ },
+ {
+ "name": "color1",
+ "type": "number"
+ },
+ {
+ "name": "black",
+ "type": "number"
+ },
+ {
+ "name": "white",
+ "type": "number"
+ },
+ {
+ "name": "darkGray",
+ "type": "number"
+ },
+ {
+ "name": "gray",
+ "type": "number"
+ },
+ {
+ "name": "lightGray",
+ "type": "number"
+ },
+ {
+ "name": "red",
+ "type": "number"
+ },
+ {
+ "name": "green",
+ "type": "number"
+ },
+ {
+ "name": "blue",
+ "type": "number"
+ },
+ {
+ "name": "cyan",
+ "type": "number"
+ },
+ {
+ "name": "magenta",
+ "type": "number"
+ },
+ {
+ "name": "yellow",
+ "type": "number"
+ },
+ {
+ "name": "darkRed",
+ "type": "number"
+ },
+ {
+ "name": "darkGreen",
+ "type": "number"
+ },
+ {
+ "name": "darkBlue",
+ "type": "number"
+ },
+ {
+ "name": "darkCyan",
+ "type": "number"
+ },
+ {
+ "name": "darkMagenta",
+ "type": "number"
+ },
+ {
+ "name": "darkYellow",
+ "type": "number"
+ },
+ {
+ "name": "transparent",
+ "type": "number"
+ },
+ {
+ "name": "Unknown",
+ "type": "number"
+ },
+ {
+ "name": "Light",
+ "type": "number"
+ },
+ {
+ "name": "Dark",
+ "type": "number"
+ },
+ {
+ "name": "NoButton",
+ "type": "number"
+ },
+ {
+ "name": "LeftButton",
+ "type": "number"
+ },
+ {
+ "name": "RightButton",
+ "type": "number"
+ },
+ {
+ "name": "MiddleButton",
+ "type": "number"
+ },
+ {
+ "name": "BackButton",
+ "type": "number"
+ },
+ {
+ "name": "XButton1",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton1",
+ "type": "number"
+ },
+ {
+ "name": "ForwardButton",
+ "type": "number"
+ },
+ {
+ "name": "XButton2",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton2",
+ "type": "number"
+ },
+ {
+ "name": "TaskButton",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton3",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton4",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton5",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton6",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton7",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton8",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton9",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton10",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton11",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton12",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton13",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton14",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton15",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton16",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton17",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton18",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton19",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton20",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton21",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton22",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton23",
+ "type": "number"
+ },
+ {
+ "name": "ExtraButton24",
+ "type": "number"
+ },
+ {
+ "name": "AllButtons",
+ "type": "number"
+ },
+ {
+ "name": "MaxMouseButton",
+ "type": "number"
+ },
+ {
+ "name": "MouseButtonMask",
+ "type": "number"
+ },
+ {
+ "name": "Horizontal",
+ "type": "number"
+ },
+ {
+ "name": "Vertical",
+ "type": "number"
+ },
+ {
+ "name": "NoFocus",
+ "type": "number"
+ },
+ {
+ "name": "TabFocus",
+ "type": "number"
+ },
+ {
+ "name": "ClickFocus",
+ "type": "number"
+ },
+ {
+ "name": "StrongFocus",
+ "type": "number"
+ },
+ {
+ "name": "WheelFocus",
+ "type": "number"
+ },
+ {
+ "name": "NoTabFocus",
+ "type": "number"
+ },
+ {
+ "name": "TabFocusTextControls",
+ "type": "number"
+ },
+ {
+ "name": "TabFocusListControls",
+ "type": "number"
+ },
+ {
+ "name": "TabFocusAllControls",
+ "type": "number"
+ },
+ {
+ "name": "AscendingOrder",
+ "type": "number"
+ },
+ {
+ "name": "DescendingOrder",
+ "type": "number"
+ },
+ {
+ "name": "KeepEmptyParts",
+ "type": "number"
+ },
+ {
+ "name": "SkipEmptyParts",
+ "type": "number"
+ },
+ {
+ "name": "AlignLeft",
+ "type": "number"
+ },
+ {
+ "name": "AlignLeading",
+ "type": "number"
+ },
+ {
+ "name": "AlignRight",
+ "type": "number"
+ },
+ {
+ "name": "AlignTrailing",
+ "type": "number"
+ },
+ {
+ "name": "AlignHCenter",
+ "type": "number"
+ },
+ {
+ "name": "AlignJustify",
+ "type": "number"
+ },
+ {
+ "name": "AlignAbsolute",
+ "type": "number"
+ },
+ {
+ "name": "AlignHorizontal_Mask",
+ "type": "number"
+ },
+ {
+ "name": "AlignTop",
+ "type": "number"
+ },
+ {
+ "name": "AlignBottom",
+ "type": "number"
+ },
+ {
+ "name": "AlignVCenter",
+ "type": "number"
+ },
+ {
+ "name": "AlignBaseline",
+ "type": "number"
+ },
+ {
+ "name": "AlignVertical_Mask",
+ "type": "number"
+ },
+ {
+ "name": "AlignCenter",
+ "type": "number"
+ },
+ {
+ "name": "TextSingleLine",
+ "type": "number"
+ },
+ {
+ "name": "TextDontClip",
+ "type": "number"
+ },
+ {
+ "name": "TextExpandTabs",
+ "type": "number"
+ },
+ {
+ "name": "TextShowMnemonic",
+ "type": "number"
+ },
+ {
+ "name": "TextWordWrap",
+ "type": "number"
+ },
+ {
+ "name": "TextWrapAnywhere",
+ "type": "number"
+ },
+ {
+ "name": "TextDontPrint",
+ "type": "number"
+ },
+ {
+ "name": "TextIncludeTrailingSpaces",
+ "type": "number"
+ },
+ {
+ "name": "TextHideMnemonic",
+ "type": "number"
+ },
+ {
+ "name": "TextJustificationForced",
+ "type": "number"
+ },
+ {
+ "name": "TextForceLeftToRight",
+ "type": "number"
+ },
+ {
+ "name": "TextForceRightToLeft",
+ "type": "number"
+ },
+ {
+ "name": "TextLongestVariant",
+ "type": "number"
+ },
+ {
+ "name": "ElideLeft",
+ "type": "number"
+ },
+ {
+ "name": "ElideRight",
+ "type": "number"
+ },
+ {
+ "name": "ElideMiddle",
+ "type": "number"
+ },
+ {
+ "name": "ElideNone",
+ "type": "number"
+ },
+ {
+ "name": "Widget",
+ "type": "number"
+ },
+ {
+ "name": "Window",
+ "type": "number"
+ },
+ {
+ "name": "Dialog",
+ "type": "number"
+ },
+ {
+ "name": "Sheet",
+ "type": "number"
+ },
+ {
+ "name": "Drawer",
+ "type": "number"
+ },
+ {
+ "name": "Popup",
+ "type": "number"
+ },
+ {
+ "name": "Tool",
+ "type": "number"
+ },
+ {
+ "name": "ToolTip",
+ "type": "number"
+ },
+ {
+ "name": "SplashScreen",
+ "type": "number"
+ },
+ {
+ "name": "Desktop",
+ "type": "number"
+ },
+ {
+ "name": "SubWindow",
+ "type": "number"
+ },
+ {
+ "name": "ForeignWindow",
+ "type": "number"
+ },
+ {
+ "name": "CoverWindow",
+ "type": "number"
+ },
+ {
+ "name": "WindowType_Mask",
+ "type": "number"
+ },
+ {
+ "name": "MSWindowsFixedSizeDialogHint",
+ "type": "number"
+ },
+ {
+ "name": "MSWindowsOwnDC",
+ "type": "number"
+ },
+ {
+ "name": "BypassWindowManagerHint",
+ "type": "number"
+ },
+ {
+ "name": "X11BypassWindowManagerHint",
+ "type": "number"
+ },
+ {
+ "name": "FramelessWindowHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowTitleHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowSystemMenuHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowMinimizeButtonHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowMaximizeButtonHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowMinMaxButtonsHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowContextHelpButtonHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowShadeButtonHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowStaysOnTopHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowTransparentForInput",
+ "type": "number"
+ },
+ {
+ "name": "WindowOverridesSystemGestures",
+ "type": "number"
+ },
+ {
+ "name": "WindowDoesNotAcceptFocus",
+ "type": "number"
+ },
+ {
+ "name": "MaximizeUsingFullscreenGeometryHint",
+ "type": "number"
+ },
+ {
+ "name": "CustomizeWindowHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowStaysOnBottomHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowCloseButtonHint",
+ "type": "number"
+ },
+ {
+ "name": "MacWindowToolBarButtonHint",
+ "type": "number"
+ },
+ {
+ "name": "BypassGraphicsProxyWidget",
+ "type": "number"
+ },
+ {
+ "name": "NoDropShadowWindowHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowFullscreenButtonHint",
+ "type": "number"
+ },
+ {
+ "name": "WindowNoState",
+ "type": "number"
+ },
+ {
+ "name": "WindowMinimized",
+ "type": "number"
+ },
+ {
+ "name": "WindowMaximized",
+ "type": "number"
+ },
+ {
+ "name": "WindowFullScreen",
+ "type": "number"
+ },
+ {
+ "name": "WindowActive",
+ "type": "number"
+ },
+ {
+ "name": "ApplicationSuspended",
+ "type": "number"
+ },
+ {
+ "name": "ApplicationHidden",
+ "type": "number"
+ },
+ {
+ "name": "ApplicationInactive",
+ "type": "number"
+ },
+ {
+ "name": "ApplicationActive",
+ "type": "number"
+ },
+ {
+ "name": "PrimaryOrientation",
+ "type": "number"
+ },
+ {
+ "name": "PortraitOrientation",
+ "type": "number"
+ },
+ {
+ "name": "LandscapeOrientation",
+ "type": "number"
+ },
+ {
+ "name": "InvertedPortraitOrientation",
+ "type": "number"
+ },
+ {
+ "name": "InvertedLandscapeOrientation",
+ "type": "number"
+ },
+ {
+ "name": "WA_Disabled",
+ "type": "number"
+ },
+ {
+ "name": "WA_UnderMouse",
+ "type": "number"
+ },
+ {
+ "name": "WA_MouseTracking",
+ "type": "number"
+ },
+ {
+ "name": "WA_OpaquePaintEvent",
+ "type": "number"
+ },
+ {
+ "name": "WA_StaticContents",
+ "type": "number"
+ },
+ {
+ "name": "WA_LaidOut",
+ "type": "number"
+ },
+ {
+ "name": "WA_PaintOnScreen",
+ "type": "number"
+ },
+ {
+ "name": "WA_NoSystemBackground",
+ "type": "number"
+ },
+ {
+ "name": "WA_UpdatesDisabled",
+ "type": "number"
+ },
+ {
+ "name": "WA_Mapped",
+ "type": "number"
+ },
+ {
+ "name": "WA_InputMethodEnabled",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_Visible",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_Hidden",
+ "type": "number"
+ },
+ {
+ "name": "WA_ForceDisabled",
+ "type": "number"
+ },
+ {
+ "name": "WA_KeyCompression",
+ "type": "number"
+ },
+ {
+ "name": "WA_PendingMoveEvent",
+ "type": "number"
+ },
+ {
+ "name": "WA_PendingResizeEvent",
+ "type": "number"
+ },
+ {
+ "name": "WA_SetPalette",
+ "type": "number"
+ },
+ {
+ "name": "WA_SetFont",
+ "type": "number"
+ },
+ {
+ "name": "WA_SetCursor",
+ "type": "number"
+ },
+ {
+ "name": "WA_NoChildEventsFromChildren",
+ "type": "number"
+ },
+ {
+ "name": "WA_WindowModified",
+ "type": "number"
+ },
+ {
+ "name": "WA_Resized",
+ "type": "number"
+ },
+ {
+ "name": "WA_Moved",
+ "type": "number"
+ },
+ {
+ "name": "WA_PendingUpdate",
+ "type": "number"
+ },
+ {
+ "name": "WA_InvalidSize",
+ "type": "number"
+ },
+ {
+ "name": "WA_CustomWhatsThis",
+ "type": "number"
+ },
+ {
+ "name": "WA_LayoutOnEntireRect",
+ "type": "number"
+ },
+ {
+ "name": "WA_OutsideWSRange",
+ "type": "number"
+ },
+ {
+ "name": "WA_GrabbedShortcut",
+ "type": "number"
+ },
+ {
+ "name": "WA_TransparentForMouseEvents",
+ "type": "number"
+ },
+ {
+ "name": "WA_PaintUnclipped",
+ "type": "number"
+ },
+ {
+ "name": "WA_SetWindowIcon",
+ "type": "number"
+ },
+ {
+ "name": "WA_NoMouseReplay",
+ "type": "number"
+ },
+ {
+ "name": "WA_DeleteOnClose",
+ "type": "number"
+ },
+ {
+ "name": "WA_RightToLeft",
+ "type": "number"
+ },
+ {
+ "name": "WA_SetLayoutDirection",
+ "type": "number"
+ },
+ {
+ "name": "WA_NoChildEventsForParent",
+ "type": "number"
+ },
+ {
+ "name": "WA_ForceUpdatesDisabled",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_Created",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_CompressKeys",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_InPaintEvent",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_Reparented",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_ConfigPending",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_Polished",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_OwnSizePolicy",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_ExplicitShowHide",
+ "type": "number"
+ },
+ {
+ "name": "WA_ShowModal",
+ "type": "number"
+ },
+ {
+ "name": "WA_MouseNoMask",
+ "type": "number"
+ },
+ {
+ "name": "WA_NoMousePropagation",
+ "type": "number"
+ },
+ {
+ "name": "WA_Hover",
+ "type": "number"
+ },
+ {
+ "name": "WA_InputMethodTransparent",
+ "type": "number"
+ },
+ {
+ "name": "WA_QuitOnClose",
+ "type": "number"
+ },
+ {
+ "name": "WA_KeyboardFocusChange",
+ "type": "number"
+ },
+ {
+ "name": "WA_AcceptDrops",
+ "type": "number"
+ },
+ {
+ "name": "WA_DropSiteRegistered",
+ "type": "number"
+ },
+ {
+ "name": "WA_WindowPropagation",
+ "type": "number"
+ },
+ {
+ "name": "WA_NoX11EventCompression",
+ "type": "number"
+ },
+ {
+ "name": "WA_TintedBackground",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11OpenGLOverlay",
+ "type": "number"
+ },
+ {
+ "name": "WA_AlwaysShowToolTips",
+ "type": "number"
+ },
+ {
+ "name": "WA_MacOpaqueSizeGrip",
+ "type": "number"
+ },
+ {
+ "name": "WA_SetStyle",
+ "type": "number"
+ },
+ {
+ "name": "WA_SetLocale",
+ "type": "number"
+ },
+ {
+ "name": "WA_MacShowFocusRect",
+ "type": "number"
+ },
+ {
+ "name": "WA_MacNormalSize",
+ "type": "number"
+ },
+ {
+ "name": "WA_MacSmallSize",
+ "type": "number"
+ },
+ {
+ "name": "WA_MacMiniSize",
+ "type": "number"
+ },
+ {
+ "name": "WA_LayoutUsesWidgetRect",
+ "type": "number"
+ },
+ {
+ "name": "WA_StyledBackground",
+ "type": "number"
+ },
+ {
+ "name": "WA_CanHostQMdiSubWindowTitleBar",
+ "type": "number"
+ },
+ {
+ "name": "WA_MacAlwaysShowToolWindow",
+ "type": "number"
+ },
+ {
+ "name": "WA_StyleSheet",
+ "type": "number"
+ },
+ {
+ "name": "WA_ShowWithoutActivating",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11BypassTransientForHint",
+ "type": "number"
+ },
+ {
+ "name": "WA_NativeWindow",
+ "type": "number"
+ },
+ {
+ "name": "WA_DontCreateNativeAncestors",
+ "type": "number"
+ },
+ {
+ "name": "WA_DontShowOnScreen",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeDesktop",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeDock",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeToolBar",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeMenu",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeUtility",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeSplash",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeDialog",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeDropDownMenu",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypePopupMenu",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeToolTip",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeNotification",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeCombo",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11NetWmWindowTypeDND",
+ "type": "number"
+ },
+ {
+ "name": "WA_SetWindowModality",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_WindowOpacitySet",
+ "type": "number"
+ },
+ {
+ "name": "WA_TranslucentBackground",
+ "type": "number"
+ },
+ {
+ "name": "WA_AcceptTouchEvents",
+ "type": "number"
+ },
+ {
+ "name": "WA_WState_AcceptedTouchBeginEvent",
+ "type": "number"
+ },
+ {
+ "name": "WA_TouchPadAcceptSingleTouchEvents",
+ "type": "number"
+ },
+ {
+ "name": "WA_X11DoNotAcceptFocus",
+ "type": "number"
+ },
+ {
+ "name": "WA_AlwaysStackOnTop",
+ "type": "number"
+ },
+ {
+ "name": "WA_TabletTracking",
+ "type": "number"
+ },
+ {
+ "name": "WA_ContentsMarginsRespectsSafeArea",
+ "type": "number"
+ },
+ {
+ "name": "WA_StyleSheetTarget",
+ "type": "number"
+ },
+ {
+ "name": "WA_AttributeCount",
+ "type": "number"
+ },
+ {
+ "name": "AA_QtQuickUseDefaultSizePolicy",
+ "type": "number"
+ },
+ {
+ "name": "AA_DontShowIconsInMenus",
+ "type": "number"
+ },
+ {
+ "name": "AA_NativeWindows",
+ "type": "number"
+ },
+ {
+ "name": "AA_DontCreateNativeWidgetSiblings",
+ "type": "number"
+ },
+ {
+ "name": "AA_PluginApplication",
+ "type": "number"
+ },
+ {
+ "name": "AA_DontUseNativeMenuBar",
+ "type": "number"
+ },
+ {
+ "name": "AA_MacDontSwapCtrlAndMeta",
+ "type": "number"
+ },
+ {
+ "name": "AA_Use96Dpi",
+ "type": "number"
+ },
+ {
+ "name": "AA_DisableNativeVirtualKeyboard",
+ "type": "number"
+ },
+ {
+ "name": "AA_SynthesizeTouchForUnhandledMouseEvents",
+ "type": "number"
+ },
+ {
+ "name": "AA_SynthesizeMouseForUnhandledTouchEvents",
+ "type": "number"
+ },
+ {
+ "name": "AA_UseHighDpiPixmaps",
+ "type": "number"
+ },
+ {
+ "name": "AA_ForceRasterWidgets",
+ "type": "number"
+ },
+ {
+ "name": "AA_UseDesktopOpenGL",
+ "type": "number"
+ },
+ {
+ "name": "AA_UseOpenGLES",
+ "type": "number"
+ },
+ {
+ "name": "AA_UseSoftwareOpenGL",
+ "type": "number"
+ },
+ {
+ "name": "AA_ShareOpenGLContexts",
+ "type": "number"
+ },
+ {
+ "name": "AA_SetPalette",
+ "type": "number"
+ },
+ {
+ "name": "AA_EnableHighDpiScaling",
+ "type": "number"
+ },
+ {
+ "name": "AA_DisableHighDpiScaling",
+ "type": "number"
+ },
+ {
+ "name": "AA_UseStyleSheetPropagationInWidgetStyles",
+ "type": "number"
+ },
+ {
+ "name": "AA_DontUseNativeDialogs",
+ "type": "number"
+ },
+ {
+ "name": "AA_SynthesizeMouseForUnhandledTabletEvents",
+ "type": "number"
+ },
+ {
+ "name": "AA_CompressHighFrequencyEvents",
+ "type": "number"
+ },
+ {
+ "name": "AA_DontCheckOpenGLContextThreadAffinity",
+ "type": "number"
+ },
+ {
+ "name": "AA_DisableShaderDiskCache",
+ "type": "number"
+ },
+ {
+ "name": "AA_DontShowShortcutsInContextMenus",
+ "type": "number"
+ },
+ {
+ "name": "AA_CompressTabletEvents",
+ "type": "number"
+ },
+ {
+ "name": "AA_DisableSessionManager",
+ "type": "number"
+ },
+ {
+ "name": "AA_DontUseNativeMenuWindows",
+ "type": "number"
+ },
+ {
+ "name": "AA_AttributeCount",
+ "type": "number"
+ },
+ {
+ "name": "ColorMode_Mask",
+ "type": "number"
+ },
+ {
+ "name": "AutoColor",
+ "type": "number"
+ },
+ {
+ "name": "ColorOnly",
+ "type": "number"
+ },
+ {
+ "name": "MonoOnly",
+ "type": "number"
+ },
+ {
+ "name": "AlphaDither_Mask",
+ "type": "number"
+ },
+ {
+ "name": "ThresholdAlphaDither",
+ "type": "number"
+ },
+ {
+ "name": "OrderedAlphaDither",
+ "type": "number"
+ },
+ {
+ "name": "DiffuseAlphaDither",
+ "type": "number"
+ },
+ {
+ "name": "NoAlpha",
+ "type": "number"
+ },
+ {
+ "name": "Dither_Mask",
+ "type": "number"
+ },
+ {
+ "name": "DiffuseDither",
+ "type": "number"
+ },
+ {
+ "name": "OrderedDither",
+ "type": "number"
+ },
+ {
+ "name": "ThresholdDither",
+ "type": "number"
+ },
+ {
+ "name": "DitherMode_Mask",
+ "type": "number"
+ },
+ {
+ "name": "AutoDither",
+ "type": "number"
+ },
+ {
+ "name": "PreferDither",
+ "type": "number"
+ },
+ {
+ "name": "AvoidDither",
+ "type": "number"
+ },
+ {
+ "name": "NoOpaqueDetection",
+ "type": "number"
+ },
+ {
+ "name": "NoFormatConversion",
+ "type": "number"
+ },
+ {
+ "name": "TransparentMode",
+ "type": "number"
+ },
+ {
+ "name": "OpaqueMode",
+ "type": "number"
+ },
+ {
+ "name": "Key_Space",
+ "type": "number"
+ },
+ {
+ "name": "Key_Any",
+ "type": "number"
+ },
+ {
+ "name": "Key_Exclam",
+ "type": "number"
+ },
+ {
+ "name": "Key_QuoteDbl",
+ "type": "number"
+ },
+ {
+ "name": "Key_NumberSign",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dollar",
+ "type": "number"
+ },
+ {
+ "name": "Key_Percent",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ampersand",
+ "type": "number"
+ },
+ {
+ "name": "Key_Apostrophe",
+ "type": "number"
+ },
+ {
+ "name": "Key_ParenLeft",
+ "type": "number"
+ },
+ {
+ "name": "Key_ParenRight",
+ "type": "number"
+ },
+ {
+ "name": "Key_Asterisk",
+ "type": "number"
+ },
+ {
+ "name": "Key_Plus",
+ "type": "number"
+ },
+ {
+ "name": "Key_Comma",
+ "type": "number"
+ },
+ {
+ "name": "Key_Minus",
+ "type": "number"
+ },
+ {
+ "name": "Key_Period",
+ "type": "number"
+ },
+ {
+ "name": "Key_Slash",
+ "type": "number"
+ },
+ {
+ "name": "Key_0",
+ "type": "number"
+ },
+ {
+ "name": "Key_1",
+ "type": "number"
+ },
+ {
+ "name": "Key_2",
+ "type": "number"
+ },
+ {
+ "name": "Key_3",
+ "type": "number"
+ },
+ {
+ "name": "Key_4",
+ "type": "number"
+ },
+ {
+ "name": "Key_5",
+ "type": "number"
+ },
+ {
+ "name": "Key_6",
+ "type": "number"
+ },
+ {
+ "name": "Key_7",
+ "type": "number"
+ },
+ {
+ "name": "Key_8",
+ "type": "number"
+ },
+ {
+ "name": "Key_9",
+ "type": "number"
+ },
+ {
+ "name": "Key_Colon",
+ "type": "number"
+ },
+ {
+ "name": "Key_Semicolon",
+ "type": "number"
+ },
+ {
+ "name": "Key_Less",
+ "type": "number"
+ },
+ {
+ "name": "Key_Equal",
+ "type": "number"
+ },
+ {
+ "name": "Key_Greater",
+ "type": "number"
+ },
+ {
+ "name": "Key_Question",
+ "type": "number"
+ },
+ {
+ "name": "Key_At",
+ "type": "number"
+ },
+ {
+ "name": "Key_A",
+ "type": "number"
+ },
+ {
+ "name": "Key_B",
+ "type": "number"
+ },
+ {
+ "name": "Key_C",
+ "type": "number"
+ },
+ {
+ "name": "Key_D",
+ "type": "number"
+ },
+ {
+ "name": "Key_E",
+ "type": "number"
+ },
+ {
+ "name": "Key_F",
+ "type": "number"
+ },
+ {
+ "name": "Key_G",
+ "type": "number"
+ },
+ {
+ "name": "Key_H",
+ "type": "number"
+ },
+ {
+ "name": "Key_I",
+ "type": "number"
+ },
+ {
+ "name": "Key_J",
+ "type": "number"
+ },
+ {
+ "name": "Key_K",
+ "type": "number"
+ },
+ {
+ "name": "Key_L",
+ "type": "number"
+ },
+ {
+ "name": "Key_M",
+ "type": "number"
+ },
+ {
+ "name": "Key_N",
+ "type": "number"
+ },
+ {
+ "name": "Key_O",
+ "type": "number"
+ },
+ {
+ "name": "Key_P",
+ "type": "number"
+ },
+ {
+ "name": "Key_Q",
+ "type": "number"
+ },
+ {
+ "name": "Key_R",
+ "type": "number"
+ },
+ {
+ "name": "Key_S",
+ "type": "number"
+ },
+ {
+ "name": "Key_T",
+ "type": "number"
+ },
+ {
+ "name": "Key_U",
+ "type": "number"
+ },
+ {
+ "name": "Key_V",
+ "type": "number"
+ },
+ {
+ "name": "Key_W",
+ "type": "number"
+ },
+ {
+ "name": "Key_X",
+ "type": "number"
+ },
+ {
+ "name": "Key_Y",
+ "type": "number"
+ },
+ {
+ "name": "Key_Z",
+ "type": "number"
+ },
+ {
+ "name": "Key_BracketLeft",
+ "type": "number"
+ },
+ {
+ "name": "Key_Backslash",
+ "type": "number"
+ },
+ {
+ "name": "Key_BracketRight",
+ "type": "number"
+ },
+ {
+ "name": "Key_AsciiCircum",
+ "type": "number"
+ },
+ {
+ "name": "Key_Underscore",
+ "type": "number"
+ },
+ {
+ "name": "Key_QuoteLeft",
+ "type": "number"
+ },
+ {
+ "name": "Key_BraceLeft",
+ "type": "number"
+ },
+ {
+ "name": "Key_Bar",
+ "type": "number"
+ },
+ {
+ "name": "Key_BraceRight",
+ "type": "number"
+ },
+ {
+ "name": "Key_AsciiTilde",
+ "type": "number"
+ },
+ {
+ "name": "Key_nobreakspace",
+ "type": "number"
+ },
+ {
+ "name": "Key_exclamdown",
+ "type": "number"
+ },
+ {
+ "name": "Key_cent",
+ "type": "number"
+ },
+ {
+ "name": "Key_sterling",
+ "type": "number"
+ },
+ {
+ "name": "Key_currency",
+ "type": "number"
+ },
+ {
+ "name": "Key_yen",
+ "type": "number"
+ },
+ {
+ "name": "Key_brokenbar",
+ "type": "number"
+ },
+ {
+ "name": "Key_section",
+ "type": "number"
+ },
+ {
+ "name": "Key_diaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_copyright",
+ "type": "number"
+ },
+ {
+ "name": "Key_ordfeminine",
+ "type": "number"
+ },
+ {
+ "name": "Key_guillemotleft",
+ "type": "number"
+ },
+ {
+ "name": "Key_notsign",
+ "type": "number"
+ },
+ {
+ "name": "Key_hyphen",
+ "type": "number"
+ },
+ {
+ "name": "Key_registered",
+ "type": "number"
+ },
+ {
+ "name": "Key_macron",
+ "type": "number"
+ },
+ {
+ "name": "Key_degree",
+ "type": "number"
+ },
+ {
+ "name": "Key_plusminus",
+ "type": "number"
+ },
+ {
+ "name": "Key_twosuperior",
+ "type": "number"
+ },
+ {
+ "name": "Key_threesuperior",
+ "type": "number"
+ },
+ {
+ "name": "Key_acute",
+ "type": "number"
+ },
+ {
+ "name": "Key_micro",
+ "type": "number"
+ },
+ {
+ "name": "Key_mu",
+ "type": "number"
+ },
+ {
+ "name": "Key_paragraph",
+ "type": "number"
+ },
+ {
+ "name": "Key_periodcentered",
+ "type": "number"
+ },
+ {
+ "name": "Key_cedilla",
+ "type": "number"
+ },
+ {
+ "name": "Key_onesuperior",
+ "type": "number"
+ },
+ {
+ "name": "Key_masculine",
+ "type": "number"
+ },
+ {
+ "name": "Key_guillemotright",
+ "type": "number"
+ },
+ {
+ "name": "Key_onequarter",
+ "type": "number"
+ },
+ {
+ "name": "Key_onehalf",
+ "type": "number"
+ },
+ {
+ "name": "Key_threequarters",
+ "type": "number"
+ },
+ {
+ "name": "Key_questiondown",
+ "type": "number"
+ },
+ {
+ "name": "Key_Agrave",
+ "type": "number"
+ },
+ {
+ "name": "Key_Aacute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Acircumflex",
+ "type": "number"
+ },
+ {
+ "name": "Key_Atilde",
+ "type": "number"
+ },
+ {
+ "name": "Key_Adiaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_Aring",
+ "type": "number"
+ },
+ {
+ "name": "Key_AE",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ccedilla",
+ "type": "number"
+ },
+ {
+ "name": "Key_Egrave",
+ "type": "number"
+ },
+ {
+ "name": "Key_Eacute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ecircumflex",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ediaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_Igrave",
+ "type": "number"
+ },
+ {
+ "name": "Key_Iacute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Icircumflex",
+ "type": "number"
+ },
+ {
+ "name": "Key_Idiaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_ETH",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ntilde",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ograve",
+ "type": "number"
+ },
+ {
+ "name": "Key_Oacute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ocircumflex",
+ "type": "number"
+ },
+ {
+ "name": "Key_Otilde",
+ "type": "number"
+ },
+ {
+ "name": "Key_Odiaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_multiply",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ooblique",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ugrave",
+ "type": "number"
+ },
+ {
+ "name": "Key_Uacute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Ucircumflex",
+ "type": "number"
+ },
+ {
+ "name": "Key_Udiaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_Yacute",
+ "type": "number"
+ },
+ {
+ "name": "Key_THORN",
+ "type": "number"
+ },
+ {
+ "name": "Key_ssharp",
+ "type": "number"
+ },
+ {
+ "name": "Key_division",
+ "type": "number"
+ },
+ {
+ "name": "Key_ydiaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_Escape",
+ "type": "number"
+ },
+ {
+ "name": "Key_Tab",
+ "type": "number"
+ },
+ {
+ "name": "Key_Backtab",
+ "type": "number"
+ },
+ {
+ "name": "Key_Backspace",
+ "type": "number"
+ },
+ {
+ "name": "Key_Return",
+ "type": "number"
+ },
+ {
+ "name": "Key_Enter",
+ "type": "number"
+ },
+ {
+ "name": "Key_Insert",
+ "type": "number"
+ },
+ {
+ "name": "Key_Delete",
+ "type": "number"
+ },
+ {
+ "name": "Key_Pause",
+ "type": "number"
+ },
+ {
+ "name": "Key_Print",
+ "type": "number"
+ },
+ {
+ "name": "Key_SysReq",
+ "type": "number"
+ },
+ {
+ "name": "Key_Clear",
+ "type": "number"
+ },
+ {
+ "name": "Key_Home",
+ "type": "number"
+ },
+ {
+ "name": "Key_End",
+ "type": "number"
+ },
+ {
+ "name": "Key_Left",
+ "type": "number"
+ },
+ {
+ "name": "Key_Up",
+ "type": "number"
+ },
+ {
+ "name": "Key_Right",
+ "type": "number"
+ },
+ {
+ "name": "Key_Down",
+ "type": "number"
+ },
+ {
+ "name": "Key_PageUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_PageDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_Shift",
+ "type": "number"
+ },
+ {
+ "name": "Key_Control",
+ "type": "number"
+ },
+ {
+ "name": "Key_Meta",
+ "type": "number"
+ },
+ {
+ "name": "Key_Alt",
+ "type": "number"
+ },
+ {
+ "name": "Key_CapsLock",
+ "type": "number"
+ },
+ {
+ "name": "Key_NumLock",
+ "type": "number"
+ },
+ {
+ "name": "Key_ScrollLock",
+ "type": "number"
+ },
+ {
+ "name": "Key_F1",
+ "type": "number"
+ },
+ {
+ "name": "Key_F2",
+ "type": "number"
+ },
+ {
+ "name": "Key_F3",
+ "type": "number"
+ },
+ {
+ "name": "Key_F4",
+ "type": "number"
+ },
+ {
+ "name": "Key_F5",
+ "type": "number"
+ },
+ {
+ "name": "Key_F6",
+ "type": "number"
+ },
+ {
+ "name": "Key_F7",
+ "type": "number"
+ },
+ {
+ "name": "Key_F8",
+ "type": "number"
+ },
+ {
+ "name": "Key_F9",
+ "type": "number"
+ },
+ {
+ "name": "Key_F10",
+ "type": "number"
+ },
+ {
+ "name": "Key_F11",
+ "type": "number"
+ },
+ {
+ "name": "Key_F12",
+ "type": "number"
+ },
+ {
+ "name": "Key_F13",
+ "type": "number"
+ },
+ {
+ "name": "Key_F14",
+ "type": "number"
+ },
+ {
+ "name": "Key_F15",
+ "type": "number"
+ },
+ {
+ "name": "Key_F16",
+ "type": "number"
+ },
+ {
+ "name": "Key_F17",
+ "type": "number"
+ },
+ {
+ "name": "Key_F18",
+ "type": "number"
+ },
+ {
+ "name": "Key_F19",
+ "type": "number"
+ },
+ {
+ "name": "Key_F20",
+ "type": "number"
+ },
+ {
+ "name": "Key_F21",
+ "type": "number"
+ },
+ {
+ "name": "Key_F22",
+ "type": "number"
+ },
+ {
+ "name": "Key_F23",
+ "type": "number"
+ },
+ {
+ "name": "Key_F24",
+ "type": "number"
+ },
+ {
+ "name": "Key_F25",
+ "type": "number"
+ },
+ {
+ "name": "Key_F26",
+ "type": "number"
+ },
+ {
+ "name": "Key_F27",
+ "type": "number"
+ },
+ {
+ "name": "Key_F28",
+ "type": "number"
+ },
+ {
+ "name": "Key_F29",
+ "type": "number"
+ },
+ {
+ "name": "Key_F30",
+ "type": "number"
+ },
+ {
+ "name": "Key_F31",
+ "type": "number"
+ },
+ {
+ "name": "Key_F32",
+ "type": "number"
+ },
+ {
+ "name": "Key_F33",
+ "type": "number"
+ },
+ {
+ "name": "Key_F34",
+ "type": "number"
+ },
+ {
+ "name": "Key_F35",
+ "type": "number"
+ },
+ {
+ "name": "Key_Super_L",
+ "type": "number"
+ },
+ {
+ "name": "Key_Super_R",
+ "type": "number"
+ },
+ {
+ "name": "Key_Menu",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hyper_L",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hyper_R",
+ "type": "number"
+ },
+ {
+ "name": "Key_Help",
+ "type": "number"
+ },
+ {
+ "name": "Key_Direction_L",
+ "type": "number"
+ },
+ {
+ "name": "Key_Direction_R",
+ "type": "number"
+ },
+ {
+ "name": "Key_AltGr",
+ "type": "number"
+ },
+ {
+ "name": "Key_Multi_key",
+ "type": "number"
+ },
+ {
+ "name": "Key_Codeinput",
+ "type": "number"
+ },
+ {
+ "name": "Key_SingleCandidate",
+ "type": "number"
+ },
+ {
+ "name": "Key_MultipleCandidate",
+ "type": "number"
+ },
+ {
+ "name": "Key_PreviousCandidate",
+ "type": "number"
+ },
+ {
+ "name": "Key_Mode_switch",
+ "type": "number"
+ },
+ {
+ "name": "Key_Kanji",
+ "type": "number"
+ },
+ {
+ "name": "Key_Muhenkan",
+ "type": "number"
+ },
+ {
+ "name": "Key_Henkan",
+ "type": "number"
+ },
+ {
+ "name": "Key_Romaji",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hiragana",
+ "type": "number"
+ },
+ {
+ "name": "Key_Katakana",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hiragana_Katakana",
+ "type": "number"
+ },
+ {
+ "name": "Key_Zenkaku",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hankaku",
+ "type": "number"
+ },
+ {
+ "name": "Key_Zenkaku_Hankaku",
+ "type": "number"
+ },
+ {
+ "name": "Key_Touroku",
+ "type": "number"
+ },
+ {
+ "name": "Key_Massyo",
+ "type": "number"
+ },
+ {
+ "name": "Key_Kana_Lock",
+ "type": "number"
+ },
+ {
+ "name": "Key_Kana_Shift",
+ "type": "number"
+ },
+ {
+ "name": "Key_Eisu_Shift",
+ "type": "number"
+ },
+ {
+ "name": "Key_Eisu_toggle",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_Start",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_End",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_Hanja",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_Jamo",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_Romaja",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_Jeonja",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_Banja",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_PreHanja",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_PostHanja",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangul_Special",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Grave",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Acute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Circumflex",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Tilde",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Macron",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Breve",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Abovedot",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Diaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Abovering",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Doubleacute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Caron",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Cedilla",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Ogonek",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Iota",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Voiced_Sound",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Semivoiced_Sound",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowdot",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Hook",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Horn",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Stroke",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Abovecomma",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Abovereversedcomma",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Doublegrave",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowring",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowmacron",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowcircumflex",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowtilde",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowbreve",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowdiaeresis",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Invertedbreve",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowcomma",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Currency",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_a",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_A",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_e",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_E",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_i",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_I",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_o",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_O",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_u",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_U",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Small_Schwa",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Capital_Schwa",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Greek",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Lowline",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Aboveverticalline",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Belowverticalline",
+ "type": "number"
+ },
+ {
+ "name": "Key_Dead_Longsolidusoverlay",
+ "type": "number"
+ },
+ {
+ "name": "Key_Back",
+ "type": "number"
+ },
+ {
+ "name": "Key_Forward",
+ "type": "number"
+ },
+ {
+ "name": "Key_Stop",
+ "type": "number"
+ },
+ {
+ "name": "Key_Refresh",
+ "type": "number"
+ },
+ {
+ "name": "Key_VolumeDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_VolumeMute",
+ "type": "number"
+ },
+ {
+ "name": "Key_VolumeUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_BassBoost",
+ "type": "number"
+ },
+ {
+ "name": "Key_BassUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_BassDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_TrebleUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_TrebleDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_MediaPlay",
+ "type": "number"
+ },
+ {
+ "name": "Key_MediaStop",
+ "type": "number"
+ },
+ {
+ "name": "Key_MediaPrevious",
+ "type": "number"
+ },
+ {
+ "name": "Key_MediaNext",
+ "type": "number"
+ },
+ {
+ "name": "Key_MediaRecord",
+ "type": "number"
+ },
+ {
+ "name": "Key_MediaPause",
+ "type": "number"
+ },
+ {
+ "name": "Key_MediaTogglePlayPause",
+ "type": "number"
+ },
+ {
+ "name": "Key_HomePage",
+ "type": "number"
+ },
+ {
+ "name": "Key_Favorites",
+ "type": "number"
+ },
+ {
+ "name": "Key_Search",
+ "type": "number"
+ },
+ {
+ "name": "Key_Standby",
+ "type": "number"
+ },
+ {
+ "name": "Key_OpenUrl",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchMail",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchMedia",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch0",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch1",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch2",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch3",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch4",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch5",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch6",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch7",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch8",
+ "type": "number"
+ },
+ {
+ "name": "Key_Launch9",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchA",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchB",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchC",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchD",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchE",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchF",
+ "type": "number"
+ },
+ {
+ "name": "Key_MonBrightnessUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_MonBrightnessDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_KeyboardLightOnOff",
+ "type": "number"
+ },
+ {
+ "name": "Key_KeyboardBrightnessUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_KeyboardBrightnessDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_PowerOff",
+ "type": "number"
+ },
+ {
+ "name": "Key_WakeUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_Eject",
+ "type": "number"
+ },
+ {
+ "name": "Key_ScreenSaver",
+ "type": "number"
+ },
+ {
+ "name": "Key_WWW",
+ "type": "number"
+ },
+ {
+ "name": "Key_Memo",
+ "type": "number"
+ },
+ {
+ "name": "Key_LightBulb",
+ "type": "number"
+ },
+ {
+ "name": "Key_Shop",
+ "type": "number"
+ },
+ {
+ "name": "Key_History",
+ "type": "number"
+ },
+ {
+ "name": "Key_AddFavorite",
+ "type": "number"
+ },
+ {
+ "name": "Key_HotLinks",
+ "type": "number"
+ },
+ {
+ "name": "Key_BrightnessAdjust",
+ "type": "number"
+ },
+ {
+ "name": "Key_Finance",
+ "type": "number"
+ },
+ {
+ "name": "Key_Community",
+ "type": "number"
+ },
+ {
+ "name": "Key_AudioRewind",
+ "type": "number"
+ },
+ {
+ "name": "Key_BackForward",
+ "type": "number"
+ },
+ {
+ "name": "Key_ApplicationLeft",
+ "type": "number"
+ },
+ {
+ "name": "Key_ApplicationRight",
+ "type": "number"
+ },
+ {
+ "name": "Key_Book",
+ "type": "number"
+ },
+ {
+ "name": "Key_CD",
+ "type": "number"
+ },
+ {
+ "name": "Key_Calculator",
+ "type": "number"
+ },
+ {
+ "name": "Key_ToDoList",
+ "type": "number"
+ },
+ {
+ "name": "Key_ClearGrab",
+ "type": "number"
+ },
+ {
+ "name": "Key_Close",
+ "type": "number"
+ },
+ {
+ "name": "Key_Copy",
+ "type": "number"
+ },
+ {
+ "name": "Key_Cut",
+ "type": "number"
+ },
+ {
+ "name": "Key_Display",
+ "type": "number"
+ },
+ {
+ "name": "Key_DOS",
+ "type": "number"
+ },
+ {
+ "name": "Key_Documents",
+ "type": "number"
+ },
+ {
+ "name": "Key_Excel",
+ "type": "number"
+ },
+ {
+ "name": "Key_Explorer",
+ "type": "number"
+ },
+ {
+ "name": "Key_Game",
+ "type": "number"
+ },
+ {
+ "name": "Key_Go",
+ "type": "number"
+ },
+ {
+ "name": "Key_iTouch",
+ "type": "number"
+ },
+ {
+ "name": "Key_LogOff",
+ "type": "number"
+ },
+ {
+ "name": "Key_Market",
+ "type": "number"
+ },
+ {
+ "name": "Key_Meeting",
+ "type": "number"
+ },
+ {
+ "name": "Key_MenuKB",
+ "type": "number"
+ },
+ {
+ "name": "Key_MenuPB",
+ "type": "number"
+ },
+ {
+ "name": "Key_MySites",
+ "type": "number"
+ },
+ {
+ "name": "Key_News",
+ "type": "number"
+ },
+ {
+ "name": "Key_OfficeHome",
+ "type": "number"
+ },
+ {
+ "name": "Key_Option",
+ "type": "number"
+ },
+ {
+ "name": "Key_Paste",
+ "type": "number"
+ },
+ {
+ "name": "Key_Phone",
+ "type": "number"
+ },
+ {
+ "name": "Key_Calendar",
+ "type": "number"
+ },
+ {
+ "name": "Key_Reply",
+ "type": "number"
+ },
+ {
+ "name": "Key_Reload",
+ "type": "number"
+ },
+ {
+ "name": "Key_RotateWindows",
+ "type": "number"
+ },
+ {
+ "name": "Key_RotationPB",
+ "type": "number"
+ },
+ {
+ "name": "Key_RotationKB",
+ "type": "number"
+ },
+ {
+ "name": "Key_Save",
+ "type": "number"
+ },
+ {
+ "name": "Key_Send",
+ "type": "number"
+ },
+ {
+ "name": "Key_Spell",
+ "type": "number"
+ },
+ {
+ "name": "Key_SplitScreen",
+ "type": "number"
+ },
+ {
+ "name": "Key_Support",
+ "type": "number"
+ },
+ {
+ "name": "Key_TaskPane",
+ "type": "number"
+ },
+ {
+ "name": "Key_Terminal",
+ "type": "number"
+ },
+ {
+ "name": "Key_Tools",
+ "type": "number"
+ },
+ {
+ "name": "Key_Travel",
+ "type": "number"
+ },
+ {
+ "name": "Key_Video",
+ "type": "number"
+ },
+ {
+ "name": "Key_Word",
+ "type": "number"
+ },
+ {
+ "name": "Key_Xfer",
+ "type": "number"
+ },
+ {
+ "name": "Key_ZoomIn",
+ "type": "number"
+ },
+ {
+ "name": "Key_ZoomOut",
+ "type": "number"
+ },
+ {
+ "name": "Key_Away",
+ "type": "number"
+ },
+ {
+ "name": "Key_Messenger",
+ "type": "number"
+ },
+ {
+ "name": "Key_WebCam",
+ "type": "number"
+ },
+ {
+ "name": "Key_MailForward",
+ "type": "number"
+ },
+ {
+ "name": "Key_Pictures",
+ "type": "number"
+ },
+ {
+ "name": "Key_Music",
+ "type": "number"
+ },
+ {
+ "name": "Key_Battery",
+ "type": "number"
+ },
+ {
+ "name": "Key_Bluetooth",
+ "type": "number"
+ },
+ {
+ "name": "Key_WLAN",
+ "type": "number"
+ },
+ {
+ "name": "Key_UWB",
+ "type": "number"
+ },
+ {
+ "name": "Key_AudioForward",
+ "type": "number"
+ },
+ {
+ "name": "Key_AudioRepeat",
+ "type": "number"
+ },
+ {
+ "name": "Key_AudioRandomPlay",
+ "type": "number"
+ },
+ {
+ "name": "Key_Subtitle",
+ "type": "number"
+ },
+ {
+ "name": "Key_AudioCycleTrack",
+ "type": "number"
+ },
+ {
+ "name": "Key_Time",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hibernate",
+ "type": "number"
+ },
+ {
+ "name": "Key_View",
+ "type": "number"
+ },
+ {
+ "name": "Key_TopMenu",
+ "type": "number"
+ },
+ {
+ "name": "Key_PowerDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_Suspend",
+ "type": "number"
+ },
+ {
+ "name": "Key_ContrastAdjust",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchG",
+ "type": "number"
+ },
+ {
+ "name": "Key_LaunchH",
+ "type": "number"
+ },
+ {
+ "name": "Key_TouchpadToggle",
+ "type": "number"
+ },
+ {
+ "name": "Key_TouchpadOn",
+ "type": "number"
+ },
+ {
+ "name": "Key_TouchpadOff",
+ "type": "number"
+ },
+ {
+ "name": "Key_MicMute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Red",
+ "type": "number"
+ },
+ {
+ "name": "Key_Green",
+ "type": "number"
+ },
+ {
+ "name": "Key_Yellow",
+ "type": "number"
+ },
+ {
+ "name": "Key_Blue",
+ "type": "number"
+ },
+ {
+ "name": "Key_ChannelUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_ChannelDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_Guide",
+ "type": "number"
+ },
+ {
+ "name": "Key_Info",
+ "type": "number"
+ },
+ {
+ "name": "Key_Settings",
+ "type": "number"
+ },
+ {
+ "name": "Key_MicVolumeUp",
+ "type": "number"
+ },
+ {
+ "name": "Key_MicVolumeDown",
+ "type": "number"
+ },
+ {
+ "name": "Key_New",
+ "type": "number"
+ },
+ {
+ "name": "Key_Open",
+ "type": "number"
+ },
+ {
+ "name": "Key_Find",
+ "type": "number"
+ },
+ {
+ "name": "Key_Undo",
+ "type": "number"
+ },
+ {
+ "name": "Key_Redo",
+ "type": "number"
+ },
+ {
+ "name": "Key_MediaLast",
+ "type": "number"
+ },
+ {
+ "name": "Key_Select",
+ "type": "number"
+ },
+ {
+ "name": "Key_Yes",
+ "type": "number"
+ },
+ {
+ "name": "Key_No",
+ "type": "number"
+ },
+ {
+ "name": "Key_Cancel",
+ "type": "number"
+ },
+ {
+ "name": "Key_Printer",
+ "type": "number"
+ },
+ {
+ "name": "Key_Execute",
+ "type": "number"
+ },
+ {
+ "name": "Key_Sleep",
+ "type": "number"
+ },
+ {
+ "name": "Key_Play",
+ "type": "number"
+ },
+ {
+ "name": "Key_Zoom",
+ "type": "number"
+ },
+ {
+ "name": "Key_Exit",
+ "type": "number"
+ },
+ {
+ "name": "Key_Context1",
+ "type": "number"
+ },
+ {
+ "name": "Key_Context2",
+ "type": "number"
+ },
+ {
+ "name": "Key_Context3",
+ "type": "number"
+ },
+ {
+ "name": "Key_Context4",
+ "type": "number"
+ },
+ {
+ "name": "Key_Call",
+ "type": "number"
+ },
+ {
+ "name": "Key_Hangup",
+ "type": "number"
+ },
+ {
+ "name": "Key_Flip",
+ "type": "number"
+ },
+ {
+ "name": "Key_ToggleCallHangup",
+ "type": "number"
+ },
+ {
+ "name": "Key_VoiceDial",
+ "type": "number"
+ },
+ {
+ "name": "Key_LastNumberRedial",
+ "type": "number"
+ },
+ {
+ "name": "Key_Camera",
+ "type": "number"
+ },
+ {
+ "name": "Key_CameraFocus",
+ "type": "number"
+ },
+ {
+ "name": "Key_unknown",
+ "type": "number"
+ },
+ {
+ "name": "NoModifier",
+ "type": "number"
+ },
+ {
+ "name": "ShiftModifier",
+ "type": "number"
+ },
+ {
+ "name": "ControlModifier",
+ "type": "number"
+ },
+ {
+ "name": "AltModifier",
+ "type": "number"
+ },
+ {
+ "name": "MetaModifier",
+ "type": "number"
+ },
+ {
+ "name": "KeypadModifier",
+ "type": "number"
+ },
+ {
+ "name": "GroupSwitchModifier",
+ "type": "number"
+ },
+ {
+ "name": "KeyboardModifierMask",
+ "type": "number"
+ },
+ {
+ "name": "META",
+ "type": "number"
+ },
+ {
+ "name": "SHIFT",
+ "type": "number"
+ },
+ {
+ "name": "CTRL",
+ "type": "number"
+ },
+ {
+ "name": "ALT",
+ "type": "number"
+ },
+ {
+ "name": "MODIFIER_MASK",
+ "type": "number"
+ },
+ {
+ "name": "NoArrow",
+ "type": "number"
+ },
+ {
+ "name": "UpArrow",
+ "type": "number"
+ },
+ {
+ "name": "DownArrow",
+ "type": "number"
+ },
+ {
+ "name": "LeftArrow",
+ "type": "number"
+ },
+ {
+ "name": "RightArrow",
+ "type": "number"
+ },
+ {
+ "name": "NoPen",
+ "type": "number"
+ },
+ {
+ "name": "SolidLine",
+ "type": "number"
+ },
+ {
+ "name": "DashLine",
+ "type": "number"
+ },
+ {
+ "name": "DotLine",
+ "type": "number"
+ },
+ {
+ "name": "DashDotLine",
+ "type": "number"
+ },
+ {
+ "name": "DashDotDotLine",
+ "type": "number"
+ },
+ {
+ "name": "CustomDashLine",
+ "type": "number"
+ },
+ {
+ "name": "FlatCap",
+ "type": "number"
+ },
+ {
+ "name": "SquareCap",
+ "type": "number"
+ },
+ {
+ "name": "RoundCap",
+ "type": "number"
+ },
+ {
+ "name": "MPenCapStyle",
+ "type": "number"
+ },
+ {
+ "name": "MiterJoin",
+ "type": "number"
+ },
+ {
+ "name": "BevelJoin",
+ "type": "number"
+ },
+ {
+ "name": "RoundJoin",
+ "type": "number"
+ },
+ {
+ "name": "SvgMiterJoin",
+ "type": "number"
+ },
+ {
+ "name": "MPenJoinStyle",
+ "type": "number"
+ },
+ {
+ "name": "NoBrush",
+ "type": "number"
+ },
+ {
+ "name": "SolidPattern",
+ "type": "number"
+ },
+ {
+ "name": "Dense1Pattern",
+ "type": "number"
+ },
+ {
+ "name": "Dense2Pattern",
+ "type": "number"
+ },
+ {
+ "name": "Dense3Pattern",
+ "type": "number"
+ },
+ {
+ "name": "Dense4Pattern",
+ "type": "number"
+ },
+ {
+ "name": "Dense5Pattern",
+ "type": "number"
+ },
+ {
+ "name": "Dense6Pattern",
+ "type": "number"
+ },
+ {
+ "name": "Dense7Pattern",
+ "type": "number"
+ },
+ {
+ "name": "HorPattern",
+ "type": "number"
+ },
+ {
+ "name": "VerPattern",
+ "type": "number"
+ },
+ {
+ "name": "CrossPattern",
+ "type": "number"
+ },
+ {
+ "name": "BDiagPattern",
+ "type": "number"
+ },
+ {
+ "name": "FDiagPattern",
+ "type": "number"
+ },
+ {
+ "name": "DiagCrossPattern",
+ "type": "number"
+ },
+ {
+ "name": "LinearGradientPattern",
+ "type": "number"
+ },
+ {
+ "name": "RadialGradientPattern",
+ "type": "number"
+ },
+ {
+ "name": "ConicalGradientPattern",
+ "type": "number"
+ },
+ {
+ "name": "TexturePattern",
+ "type": "number"
+ },
+ {
+ "name": "AbsoluteSize",
+ "type": "number"
+ },
+ {
+ "name": "RelativeSize",
+ "type": "number"
+ },
+ {
+ "name": "ArrowCursor",
+ "type": "number"
+ },
+ {
+ "name": "UpArrowCursor",
+ "type": "number"
+ },
+ {
+ "name": "CrossCursor",
+ "type": "number"
+ },
+ {
+ "name": "WaitCursor",
+ "type": "number"
+ },
+ {
+ "name": "IBeamCursor",
+ "type": "number"
+ },
+ {
+ "name": "SizeVerCursor",
+ "type": "number"
+ },
+ {
+ "name": "SizeHorCursor",
+ "type": "number"
+ },
+ {
+ "name": "SizeBDiagCursor",
+ "type": "number"
+ },
+ {
+ "name": "SizeFDiagCursor",
+ "type": "number"
+ },
+ {
+ "name": "SizeAllCursor",
+ "type": "number"
+ },
+ {
+ "name": "BlankCursor",
+ "type": "number"
+ },
+ {
+ "name": "SplitVCursor",
+ "type": "number"
+ },
+ {
+ "name": "SplitHCursor",
+ "type": "number"
+ },
+ {
+ "name": "PointingHandCursor",
+ "type": "number"
+ },
+ {
+ "name": "ForbiddenCursor",
+ "type": "number"
+ },
+ {
+ "name": "WhatsThisCursor",
+ "type": "number"
+ },
+ {
+ "name": "BusyCursor",
+ "type": "number"
+ },
+ {
+ "name": "OpenHandCursor",
+ "type": "number"
+ },
+ {
+ "name": "ClosedHandCursor",
+ "type": "number"
+ },
+ {
+ "name": "DragCopyCursor",
+ "type": "number"
+ },
+ {
+ "name": "DragMoveCursor",
+ "type": "number"
+ },
+ {
+ "name": "DragLinkCursor",
+ "type": "number"
+ },
+ {
+ "name": "LastCursor",
+ "type": "number"
+ },
+ {
+ "name": "BitmapCursor",
+ "type": "number"
+ },
+ {
+ "name": "CustomCursor",
+ "type": "number"
+ },
+ {
+ "name": "PlainText",
+ "type": "number"
+ },
+ {
+ "name": "RichText",
+ "type": "number"
+ },
+ {
+ "name": "AutoText",
+ "type": "number"
+ },
+ {
+ "name": "MarkdownText",
+ "type": "number"
+ },
+ {
+ "name": "IgnoreAspectRatio",
+ "type": "number"
+ },
+ {
+ "name": "KeepAspectRatio",
+ "type": "number"
+ },
+ {
+ "name": "KeepAspectRatioByExpanding",
+ "type": "number"
+ },
+ {
+ "name": "LeftDockWidgetArea",
+ "type": "number"
+ },
+ {
+ "name": "RightDockWidgetArea",
+ "type": "number"
+ },
+ {
+ "name": "TopDockWidgetArea",
+ "type": "number"
+ },
+ {
+ "name": "BottomDockWidgetArea",
+ "type": "number"
+ },
+ {
+ "name": "DockWidgetArea_Mask",
+ "type": "number"
+ },
+ {
+ "name": "AllDockWidgetAreas",
+ "type": "number"
+ },
+ {
+ "name": "NoDockWidgetArea",
+ "type": "number"
+ },
+ {
+ "name": "LeftToolBarArea",
+ "type": "number"
+ },
+ {
+ "name": "RightToolBarArea",
+ "type": "number"
+ },
+ {
+ "name": "TopToolBarArea",
+ "type": "number"
+ },
+ {
+ "name": "BottomToolBarArea",
+ "type": "number"
+ },
+ {
+ "name": "ToolBarArea_Mask",
+ "type": "number"
+ },
+ {
+ "name": "AllToolBarAreas",
+ "type": "number"
+ },
+ {
+ "name": "NoToolBarArea",
+ "type": "number"
+ },
+ {
+ "name": "TextDate",
+ "type": "number"
+ },
+ {
+ "name": "ISODate",
+ "type": "number"
+ },
+ {
+ "name": "RFC2822Date",
+ "type": "number"
+ },
+ {
+ "name": "ISODateWithMs",
+ "type": "number"
+ },
+ {
+ "name": "LocalTime",
+ "type": "number"
+ },
+ {
+ "name": "UTC",
+ "type": "number"
+ },
+ {
+ "name": "OffsetFromUTC",
+ "type": "number"
+ },
+ {
+ "name": "TimeZone",
+ "type": "number"
+ },
+ {
+ "name": "Monday",
+ "type": "number"
+ },
+ {
+ "name": "Tuesday",
+ "type": "number"
+ },
+ {
+ "name": "Wednesday",
+ "type": "number"
+ },
+ {
+ "name": "Thursday",
+ "type": "number"
+ },
+ {
+ "name": "Friday",
+ "type": "number"
+ },
+ {
+ "name": "Saturday",
+ "type": "number"
+ },
+ {
+ "name": "Sunday",
+ "type": "number"
+ },
+ {
+ "name": "ScrollBarAsNeeded",
+ "type": "number"
+ },
+ {
+ "name": "ScrollBarAlwaysOff",
+ "type": "number"
+ },
+ {
+ "name": "ScrollBarAlwaysOn",
+ "type": "number"
+ },
+ {
+ "name": "CaseInsensitive",
+ "type": "number"
+ },
+ {
+ "name": "CaseSensitive",
+ "type": "number"
+ },
+ {
+ "name": "TopLeftCorner",
+ "type": "number"
+ },
+ {
+ "name": "TopRightCorner",
+ "type": "number"
+ },
+ {
+ "name": "BottomLeftCorner",
+ "type": "number"
+ },
+ {
+ "name": "BottomRightCorner",
+ "type": "number"
+ },
+ {
+ "name": "TopEdge",
+ "type": "number"
+ },
+ {
+ "name": "LeftEdge",
+ "type": "number"
+ },
+ {
+ "name": "RightEdge",
+ "type": "number"
+ },
+ {
+ "name": "BottomEdge",
+ "type": "number"
+ },
+ {
+ "name": "AutoConnection",
+ "type": "number"
+ },
+ {
+ "name": "DirectConnection",
+ "type": "number"
+ },
+ {
+ "name": "QueuedConnection",
+ "type": "number"
+ },
+ {
+ "name": "BlockingQueuedConnection",
+ "type": "number"
+ },
+ {
+ "name": "UniqueConnection",
+ "type": "number"
+ },
+ {
+ "name": "SingleShotConnection",
+ "type": "number"
+ },
+ {
+ "name": "WidgetShortcut",
+ "type": "number"
+ },
+ {
+ "name": "WindowShortcut",
+ "type": "number"
+ },
+ {
+ "name": "ApplicationShortcut",
+ "type": "number"
+ },
+ {
+ "name": "WidgetWithChildrenShortcut",
+ "type": "number"
+ },
+ {
+ "name": "OddEvenFill",
+ "type": "number"
+ },
+ {
+ "name": "WindingFill",
+ "type": "number"
+ },
+ {
+ "name": "MaskInColor",
+ "type": "number"
+ },
+ {
+ "name": "MaskOutColor",
+ "type": "number"
+ },
+ {
+ "name": "NoClip",
+ "type": "number"
+ },
+ {
+ "name": "ReplaceClip",
+ "type": "number"
+ },
+ {
+ "name": "IntersectClip",
+ "type": "number"
+ },
+ {
+ "name": "ContainsItemShape",
+ "type": "number"
+ },
+ {
+ "name": "IntersectsItemShape",
+ "type": "number"
+ },
+ {
+ "name": "ContainsItemBoundingRect",
+ "type": "number"
+ },
+ {
+ "name": "IntersectsItemBoundingRect",
+ "type": "number"
+ },
+ {
+ "name": "ReplaceSelection",
+ "type": "number"
+ },
+ {
+ "name": "AddToSelection",
+ "type": "number"
+ },
+ {
+ "name": "FastTransformation",
+ "type": "number"
+ },
+ {
+ "name": "SmoothTransformation",
+ "type": "number"
+ },
+ {
+ "name": "XAxis",
+ "type": "number"
+ },
+ {
+ "name": "YAxis",
+ "type": "number"
+ },
+ {
+ "name": "ZAxis",
+ "type": "number"
+ },
+ {
+ "name": "MouseFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "TabFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "BacktabFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "ActiveWindowFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "PopupFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "ShortcutFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "MenuBarFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "OtherFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "NoFocusReason",
+ "type": "number"
+ },
+ {
+ "name": "NoContextMenu",
+ "type": "number"
+ },
+ {
+ "name": "DefaultContextMenu",
+ "type": "number"
+ },
+ {
+ "name": "ActionsContextMenu",
+ "type": "number"
+ },
+ {
+ "name": "CustomContextMenu",
+ "type": "number"
+ },
+ {
+ "name": "PreventContextMenu",
+ "type": "number"
+ },
+ {
+ "name": "ImEnabled",
+ "type": "number"
+ },
+ {
+ "name": "ImCursorRectangle",
+ "type": "number"
+ },
+ {
+ "name": "ImFont",
+ "type": "number"
+ },
+ {
+ "name": "ImCursorPosition",
+ "type": "number"
+ },
+ {
+ "name": "ImSurroundingText",
+ "type": "number"
+ },
+ {
+ "name": "ImCurrentSelection",
+ "type": "number"
+ },
+ {
+ "name": "ImMaximumTextLength",
+ "type": "number"
+ },
+ {
+ "name": "ImAnchorPosition",
+ "type": "number"
+ },
+ {
+ "name": "ImHints",
+ "type": "number"
+ },
+ {
+ "name": "ImPreferredLanguage",
+ "type": "number"
+ },
+ {
+ "name": "ImAbsolutePosition",
+ "type": "number"
+ },
+ {
+ "name": "ImTextBeforeCursor",
+ "type": "number"
+ },
+ {
+ "name": "ImTextAfterCursor",
+ "type": "number"
+ },
+ {
+ "name": "ImEnterKeyType",
+ "type": "number"
+ },
+ {
+ "name": "ImAnchorRectangle",
+ "type": "number"
+ },
+ {
+ "name": "ImInputItemClipRectangle",
+ "type": "number"
+ },
+ {
+ "name": "ImReadOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImPlatformData",
+ "type": "number"
+ },
+ {
+ "name": "ImQueryInput",
+ "type": "number"
+ },
+ {
+ "name": "ImQueryAll",
+ "type": "number"
+ },
+ {
+ "name": "ImhNone",
+ "type": "number"
+ },
+ {
+ "name": "ImhHiddenText",
+ "type": "number"
+ },
+ {
+ "name": "ImhSensitiveData",
+ "type": "number"
+ },
+ {
+ "name": "ImhNoAutoUppercase",
+ "type": "number"
+ },
+ {
+ "name": "ImhPreferNumbers",
+ "type": "number"
+ },
+ {
+ "name": "ImhPreferUppercase",
+ "type": "number"
+ },
+ {
+ "name": "ImhPreferLowercase",
+ "type": "number"
+ },
+ {
+ "name": "ImhNoPredictiveText",
+ "type": "number"
+ },
+ {
+ "name": "ImhDate",
+ "type": "number"
+ },
+ {
+ "name": "ImhTime",
+ "type": "number"
+ },
+ {
+ "name": "ImhPreferLatin",
+ "type": "number"
+ },
+ {
+ "name": "ImhMultiLine",
+ "type": "number"
+ },
+ {
+ "name": "ImhNoEditMenu",
+ "type": "number"
+ },
+ {
+ "name": "ImhNoTextHandles",
+ "type": "number"
+ },
+ {
+ "name": "ImhDigitsOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImhFormattedNumbersOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImhUppercaseOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImhLowercaseOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImhDialableCharactersOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImhEmailCharactersOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImhUrlCharactersOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImhLatinOnly",
+ "type": "number"
+ },
+ {
+ "name": "ImhExclusiveInputMask",
+ "type": "number"
+ },
+ {
+ "name": "EnterKeyDefault",
+ "type": "number"
+ },
+ {
+ "name": "EnterKeyReturn",
+ "type": "number"
+ },
+ {
+ "name": "EnterKeyDone",
+ "type": "number"
+ },
+ {
+ "name": "EnterKeyGo",
+ "type": "number"
+ },
+ {
+ "name": "EnterKeySend",
+ "type": "number"
+ },
+ {
+ "name": "EnterKeySearch",
+ "type": "number"
+ },
+ {
+ "name": "EnterKeyNext",
+ "type": "number"
+ },
+ {
+ "name": "EnterKeyPrevious",
+ "type": "number"
+ },
+ {
+ "name": "ToolButtonIconOnly",
+ "type": "number"
+ },
+ {
+ "name": "ToolButtonTextOnly",
+ "type": "number"
+ },
+ {
+ "name": "ToolButtonTextBesideIcon",
+ "type": "number"
+ },
+ {
+ "name": "ToolButtonTextUnderIcon",
+ "type": "number"
+ },
+ {
+ "name": "ToolButtonFollowStyle",
+ "type": "number"
+ },
+ {
+ "name": "LeftToRight",
+ "type": "number"
+ },
+ {
+ "name": "RightToLeft",
+ "type": "number"
+ },
+ {
+ "name": "LayoutDirectionAuto",
+ "type": "number"
+ },
+ {
+ "name": "CopyAction",
+ "type": "number"
+ },
+ {
+ "name": "MoveAction",
+ "type": "number"
+ },
+ {
+ "name": "LinkAction",
+ "type": "number"
+ },
+ {
+ "name": "ActionMask",
+ "type": "number"
+ },
+ {
+ "name": "TargetMoveAction",
+ "type": "number"
+ },
+ {
+ "name": "IgnoreAction",
+ "type": "number"
+ },
+ {
+ "name": "Unchecked",
+ "type": "number"
+ },
+ {
+ "name": "PartiallyChecked",
+ "type": "number"
+ },
+ {
+ "name": "Checked",
+ "type": "number"
+ },
+ {
+ "name": "DisplayRole",
+ "type": "number"
+ },
+ {
+ "name": "DecorationRole",
+ "type": "number"
+ },
+ {
+ "name": "EditRole",
+ "type": "number"
+ },
+ {
+ "name": "ToolTipRole",
+ "type": "number"
+ },
+ {
+ "name": "StatusTipRole",
+ "type": "number"
+ },
+ {
+ "name": "WhatsThisRole",
+ "type": "number"
+ },
+ {
+ "name": "FontRole",
+ "type": "number"
+ },
+ {
+ "name": "TextAlignmentRole",
+ "type": "number"
+ },
+ {
+ "name": "BackgroundRole",
+ "type": "number"
+ },
+ {
+ "name": "ForegroundRole",
+ "type": "number"
+ },
+ {
+ "name": "CheckStateRole",
+ "type": "number"
+ },
+ {
+ "name": "AccessibleTextRole",
+ "type": "number"
+ },
+ {
+ "name": "AccessibleDescriptionRole",
+ "type": "number"
+ },
+ {
+ "name": "SizeHintRole",
+ "type": "number"
+ },
+ {
+ "name": "InitialSortOrderRole",
+ "type": "number"
+ },
+ {
+ "name": "DisplayPropertyRole",
+ "type": "number"
+ },
+ {
+ "name": "DecorationPropertyRole",
+ "type": "number"
+ },
+ {
+ "name": "ToolTipPropertyRole",
+ "type": "number"
+ },
+ {
+ "name": "StatusTipPropertyRole",
+ "type": "number"
+ },
+ {
+ "name": "WhatsThisPropertyRole",
+ "type": "number"
+ },
+ {
+ "name": "UserRole",
+ "type": "number"
+ },
+ {
+ "name": "NoItemFlags",
+ "type": "number"
+ },
+ {
+ "name": "ItemIsSelectable",
+ "type": "number"
+ },
+ {
+ "name": "ItemIsEditable",
+ "type": "number"
+ },
+ {
+ "name": "ItemIsDragEnabled",
+ "type": "number"
+ },
+ {
+ "name": "ItemIsDropEnabled",
+ "type": "number"
+ },
+ {
+ "name": "ItemIsUserCheckable",
+ "type": "number"
+ },
+ {
+ "name": "ItemIsEnabled",
+ "type": "number"
+ },
+ {
+ "name": "ItemIsAutoTristate",
+ "type": "number"
+ },
+ {
+ "name": "ItemNeverHasChildren",
+ "type": "number"
+ },
+ {
+ "name": "ItemIsUserTristate",
+ "type": "number"
+ },
+ {
+ "name": "MatchExactly",
+ "type": "number"
+ },
+ {
+ "name": "MatchContains",
+ "type": "number"
+ },
+ {
+ "name": "MatchStartsWith",
+ "type": "number"
+ },
+ {
+ "name": "MatchEndsWith",
+ "type": "number"
+ },
+ {
+ "name": "MatchRegularExpression",
+ "type": "number"
+ },
+ {
+ "name": "MatchWildcard",
+ "type": "number"
+ },
+ {
+ "name": "MatchFixedString",
+ "type": "number"
+ },
+ {
+ "name": "MatchTypeMask",
+ "type": "number"
+ },
+ {
+ "name": "MatchCaseSensitive",
+ "type": "number"
+ },
+ {
+ "name": "MatchWrap",
+ "type": "number"
+ },
+ {
+ "name": "MatchRecursive",
+ "type": "number"
+ },
+ {
+ "name": "NonModal",
+ "type": "number"
+ },
+ {
+ "name": "WindowModal",
+ "type": "number"
+ },
+ {
+ "name": "ApplicationModal",
+ "type": "number"
+ },
+ {
+ "name": "NoTextInteraction",
+ "type": "number"
+ },
+ {
+ "name": "TextSelectableByMouse",
+ "type": "number"
+ },
+ {
+ "name": "TextSelectableByKeyboard",
+ "type": "number"
+ },
+ {
+ "name": "LinksAccessibleByMouse",
+ "type": "number"
+ },
+ {
+ "name": "LinksAccessibleByKeyboard",
+ "type": "number"
+ },
+ {
+ "name": "TextEditable",
+ "type": "number"
+ },
+ {
+ "name": "TextEditorInteraction",
+ "type": "number"
+ },
+ {
+ "name": "TextBrowserInteraction",
+ "type": "number"
+ },
+ {
+ "name": "MinimumSize",
+ "type": "number"
+ },
+ {
+ "name": "PreferredSize",
+ "type": "number"
+ },
+ {
+ "name": "MaximumSize",
+ "type": "number"
+ },
+ {
+ "name": "MinimumDescent",
+ "type": "number"
+ },
+ {
+ "name": "NSizeHints",
+ "type": "number"
+ },
+ {
+ "name": "TouchPointUnknownState",
+ "type": "number"
+ },
+ {
+ "name": "TouchPointPressed",
+ "type": "number"
+ },
+ {
+ "name": "TouchPointMoved",
+ "type": "number"
+ },
+ {
+ "name": "TouchPointStationary",
+ "type": "number"
+ },
+ {
+ "name": "TouchPointReleased",
+ "type": "number"
+ },
+ {
+ "name": "NoGesture",
+ "type": "number"
+ },
+ {
+ "name": "GestureStarted",
+ "type": "number"
+ },
+ {
+ "name": "GestureUpdated",
+ "type": "number"
+ },
+ {
+ "name": "GestureFinished",
+ "type": "number"
+ },
+ {
+ "name": "GestureCanceled",
+ "type": "number"
+ },
+ {
+ "name": "TapGesture",
+ "type": "number"
+ },
+ {
+ "name": "TapAndHoldGesture",
+ "type": "number"
+ },
+ {
+ "name": "PanGesture",
+ "type": "number"
+ },
+ {
+ "name": "PinchGesture",
+ "type": "number"
+ },
+ {
+ "name": "SwipeGesture",
+ "type": "number"
+ },
+ {
+ "name": "CustomGesture",
+ "type": "number"
+ },
+ {
+ "name": "LastGestureType",
+ "type": "number"
+ },
+ {
+ "name": "BeginNativeGesture",
+ "type": "number"
+ },
+ {
+ "name": "EndNativeGesture",
+ "type": "number"
+ },
+ {
+ "name": "PanNativeGesture",
+ "type": "number"
+ },
+ {
+ "name": "ZoomNativeGesture",
+ "type": "number"
+ },
+ {
+ "name": "SmartZoomNativeGesture",
+ "type": "number"
+ },
+ {
+ "name": "RotateNativeGesture",
+ "type": "number"
+ },
+ {
+ "name": "SwipeNativeGesture",
+ "type": "number"
+ },
+ {
+ "name": "LogicalMoveStyle",
+ "type": "number"
+ },
+ {
+ "name": "VisualMoveStyle",
+ "type": "number"
+ },
+ {
+ "name": "PreciseTimer",
+ "type": "number"
+ },
+ {
+ "name": "CoarseTimer",
+ "type": "number"
+ },
+ {
+ "name": "VeryCoarseTimer",
+ "type": "number"
+ },
+ {
+ "name": "NoScrollPhase",
+ "type": "number"
+ },
+ {
+ "name": "ScrollBegin",
+ "type": "number"
+ },
+ {
+ "name": "ScrollUpdate",
+ "type": "number"
+ },
+ {
+ "name": "ScrollEnd",
+ "type": "number"
+ },
+ {
+ "name": "ScrollMomentum",
+ "type": "number"
+ },
+ {
+ "name": "MouseEventNotSynthesized",
+ "type": "number"
+ },
+ {
+ "name": "MouseEventSynthesizedBySystem",
+ "type": "number"
+ },
+ {
+ "name": "MouseEventSynthesizedByQt",
+ "type": "number"
+ },
+ {
+ "name": "MouseEventSynthesizedByApplication",
+ "type": "number"
+ },
+ {
+ "name": "NoMouseEventFlag",
+ "type": "number"
+ },
+ {
+ "name": "MouseEventCreatedDoubleClick",
+ "type": "number"
+ },
+ {
+ "name": "MouseEventFlagMask",
+ "type": "number"
+ },
+ {
+ "name": "ChecksumIso3309",
+ "type": "number"
+ },
+ {
+ "name": "ChecksumItuV41",
+ "type": "number"
+ },
+ {
+ "name": "Unset",
+ "type": "number"
+ },
+ {
+ "name": "Round",
+ "type": "number"
+ },
+ {
+ "name": "Ceil",
+ "type": "number"
+ },
+ {
+ "name": "Floor",
+ "type": "number"
+ },
+ {
+ "name": "RoundPreferFloor",
+ "type": "number"
+ },
+ {
+ "name": "PassThrough",
+ "type": "number"
+ },
+ {
+ "name": "Undetermined",
+ "type": "number"
+ },
+ {
+ "name": "Granted",
+ "type": "number"
+ },
+ {
+ "name": "Denied",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "QtPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "QtApplicationArgumentsPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "sort"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "valueOf"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "length"
+ }
+ ],
+ "qualifiedClassName": "QtApplicationArgumentsPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ArrayPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "QtApplicationArguments",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "0"
+ },
+ {
+ "member": "fakeMember",
+ "name": "1"
+ },
+ {
+ "member": "fakeMember",
+ "name": "length",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "QtApplicationArguments",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "QtApplicationArgumentsPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "QtApplication",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "objectNameChanged"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "aboutToQuit"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "nameChanged"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "versionChanged"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "organizationChanged"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "domainChanged"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setName"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setVersion"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setOrganization"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "setDomain"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "objectName",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "arguments",
+ "type": "QtApplicationArguments"
+ },
+ {
+ "member": "fakeMember",
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "organization",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "domain",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "QtApplication",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "QtPlatform",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "objectNameChanged"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "objectName",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "os",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "pluginName",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "QtPlatform",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "QtInputMethod",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "objectNameChanged"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "objectName",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "QtInputMethod",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "QtStyleHints",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "objectNameChanged"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "objectName",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "QtStyleHints",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Qt",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "objectNameChanged"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "include"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "isQtObject"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "color"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "rgba"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "hsla"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "hsva"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "colorEqual"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "rect"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "point"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "size"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "vector2d"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "vector3d"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "vector4d"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "quaternion"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "matrix4x4"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "lighter"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "darker"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "alpha"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "tint"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "formatDate"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "formatTime"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "formatDateTime"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "locale"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "url"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "resolvedUrl"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "openUrlExternally"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "font"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "fontFamilies"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "md5"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "btoa"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "atob"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "quit"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "exit"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "createQmlObject"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "createComponent"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "binding"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "callLater"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "objectName",
+ "type": "string"
+ },
+ {
+ "member": "fakeMember",
+ "name": "application",
+ "type": "QtApplication"
+ },
+ {
+ "member": "fakeMember",
+ "name": "platform",
+ "type": "QtPlatform"
+ },
+ {
+ "member": "fakeMember",
+ "name": "inputMethod",
+ "type": "QtInputMethod"
+ },
+ {
+ "member": "fakeMember",
+ "name": "styleHints",
+ "type": "QtStyleHints"
+ },
+ {
+ "member": "fakeMember",
+ "name": "uiLanguage",
+ "type": "string"
+ }
+ ],
+ "qualifiedClassName": "Qt",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "QtPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "Console",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "debug"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "log"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "info"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "warn"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "error"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "assert"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "count"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "profile"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "profileEnd"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "time"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "timeEnd"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "trace"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "exception"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "Console",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "GlobalObject",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Object",
+ "returnType": "Object"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "String",
+ "returnType": "String"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Symbol",
+ "returnType": "undefined"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Number",
+ "returnType": "Number"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Boolean",
+ "returnType": "Boolean"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Array",
+ "returnType": "Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Function",
+ "returnType": "Function"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Date",
+ "returnType": "Date"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "RegExp",
+ "returnType": "RegExp"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Error",
+ "returnType": "Error"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "EvalError",
+ "returnType": "EvalError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "RangeError",
+ "returnType": "RangeError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "ReferenceError",
+ "returnType": "ReferenceError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "SyntaxError",
+ "returnType": "SyntaxError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "TypeError",
+ "returnType": "TypeError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "URIError",
+ "returnType": "URIError"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Promise",
+ "returnType": "Promise"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "URL",
+ "returnType": "URL"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "URLSearchParams",
+ "returnType": "URLSearchParams"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "SharedArrayBuffer",
+ "returnType": "SharedArrayBuffer"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "ArrayBuffer",
+ "returnType": "ArrayBuffer"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "DataView",
+ "returnType": "DataView"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "WeakSet",
+ "returnType": "WeakSet"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Set",
+ "returnType": "Set"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "WeakMap",
+ "returnType": "WeakMap"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Map",
+ "returnType": "Map"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Int8Array",
+ "returnType": "Int8Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Uint8Array",
+ "returnType": "Uint8Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Int16Array",
+ "returnType": "Int16Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Uint16Array",
+ "returnType": "Uint16Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Int32Array",
+ "returnType": "Int32Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Uint32Array",
+ "returnType": "Uint32Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Uint8ClampedArray",
+ "returnType": "Uint8ClampedArray"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Float32Array",
+ "returnType": "Float32Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Float64Array",
+ "returnType": "Float64Array"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "Proxy",
+ "returnType": "Proxy"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "eval"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ },
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "parseInt"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "parseFloat"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "isNaN"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "isFinite"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "decodeURI"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "decodeURIComponent"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "encodeURI"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "encodeURIComponent"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "escape"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ {
+ }
+ ],
+ "isJavaScriptFunction": true,
+ "name": "unescape"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "qsTranslate"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "QT_TRANSLATE_NOOP"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "qsTr"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "QT_TR_NOOP"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "qsTrId"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "QT_TRID_NOOP"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "print"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "gc"
+ }
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "Atomics",
+ "type": "Atomics"
+ },
+ {
+ "member": "fakeMember",
+ "name": "Math",
+ "type": "Math"
+ },
+ {
+ "member": "fakeMember",
+ "name": "JSON",
+ "type": "JSON"
+ },
+ {
+ "member": "fakeMember",
+ "name": "Reflect",
+ "type": "Reflect"
+ },
+ {
+ "name": "undefined"
+ },
+ {
+ "name": "NaN",
+ "type": "number"
+ },
+ {
+ "name": "Infinity",
+ "type": "number"
+ },
+ {
+ "member": "fakeMember",
+ "name": "Qt",
+ "type": "Qt"
+ },
+ {
+ "member": "fakeMember",
+ "name": "console",
+ "type": "Console"
+ }
+ ],
+ "qualifiedClassName": "GlobalObject",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "function",
+ "methods": [
+ ],
+ "object": true,
+ "properties": [
+ {
+ "member": "fakeMember",
+ "name": "prototype",
+ "type": "Object"
+ },
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "length",
+ "type": "number"
+ }
+ ],
+ "qualifiedClassName": "function",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "FunctionPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "string",
+ "gadget": true,
+ "methods": [
+ ],
+ "properties": [
+ ],
+ "qualifiedClassName": "string",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "StringPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "undefined",
+ "gadget": true,
+ "methods": [
+ ],
+ "properties": [
+ ],
+ "qualifiedClassName": "undefined"
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "number",
+ "gadget": true,
+ "methods": [
+ ],
+ "properties": [
+ ],
+ "qualifiedClassName": "number",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "NumberPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "boolean",
+ "gadget": true,
+ "methods": [
+ ],
+ "properties": [
+ ],
+ "qualifiedClassName": "boolean",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "BooleanPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "SymbolPrototype",
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isConstructor": true,
+ "isJavaScriptFunction": true,
+ "name": "constructor",
+ "returnType": "undefined"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "toString"
+ },
+ {
+ "access": "public",
+ "arguments": [
+ ],
+ "isJavaScriptFunction": true,
+ "name": "valueOf"
+ }
+ ],
+ "object": true,
+ "properties": [
+ ],
+ "qualifiedClassName": "SymbolPrototype",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "ObjectPrototype"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ }
+ ],
+ "className": "symbol",
+ "gadget": true,
+ "methods": [
+ ],
+ "properties": [
+ ],
+ "qualifiedClassName": "symbol",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "SymbolPrototype"
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/src/qmltyperegistrar/qanystringviewutils_p.h b/src/qmltyperegistrar/qanystringviewutils_p.h
new file mode 100644
index 0000000000..22c4bd63a0
--- /dev/null
+++ b/src/qmltyperegistrar/qanystringviewutils_p.h
@@ -0,0 +1,187 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QANYSTRINGVIEWUTILS_P_H
+#define QANYSTRINGVIEWUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include <QtCore/private/qjson_p.h>
+
+#include <QtCore/qanystringview.h>
+#include <QtCore/qcbormap.h>
+#include <QtCore/qcborvalue.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QAnyStringViewUtils {
+
+inline QAnyStringView toStringView(const QCborValue &value)
+{
+ const QCborContainerPrivate *container = QJsonPrivate::Value::container(value);
+ if (!container)
+ return QAnyStringView();
+
+ const qint64 n = QJsonPrivate::Value::valueHelper(value);
+ const auto &e = container->elements.at(n);
+ const auto data = container->byteData(e);
+ if (!data)
+ return QAnyStringView();
+ if (e.flags & QtCbor::Element::StringIsUtf16)
+ return data->asStringView();
+ if (e.flags & QtCbor::Element::StringIsAscii)
+ return data->asLatin1();
+ return data->asUtf8StringView();
+}
+
+inline QAnyStringView toStringView(const QCborMap &map, QLatin1StringView key)
+{
+ return toStringView(map[key]);
+}
+
+// Note: This only works if part is US-ASCII, but there is no type to encode this information!
+inline bool endsWith(QAnyStringView whole, QLatin1StringView part)
+{
+ Q_ASSERT(QtPrivate::isAscii(part));
+ return whole.length() >= part.length() && whole.last(part.length()) == part;
+}
+
+// Note: This only works if part is US-ASCII, but there is no type to encode this information!
+inline bool startsWith(QAnyStringView whole, QLatin1StringView part)
+{
+ Q_ASSERT(QtPrivate::isAscii(part));
+ return whole.length() >= part.length() && whole.first(part.length()) == part;
+}
+
+inline bool doesContain(QStringView whole, QLatin1Char part) { return whole.contains(part); }
+inline bool doesContain(QLatin1StringView whole, QLatin1Char part) { return whole.contains(part); }
+inline bool doesContain(QUtf8StringView whole, QLatin1Char part)
+{
+ return QByteArrayView(whole.data(), whole.size()).contains(part.toLatin1());
+}
+inline bool contains(QAnyStringView whole, QLatin1Char part)
+{
+ return whole.visit([&](auto view) { return doesContain(view, part); });
+}
+
+inline qsizetype getLastIndexOf(QStringView whole, QLatin1StringView part)
+{
+ return whole.lastIndexOf(part);
+}
+inline qsizetype getLastIndexOf(QLatin1StringView whole, QLatin1StringView part)
+{
+ return whole.lastIndexOf(part);
+}
+inline qsizetype getLastIndexOf(QUtf8StringView whole, QLatin1StringView part)
+{
+ return QByteArrayView(whole.data(), whole.size()).lastIndexOf(part);
+}
+inline qsizetype lastIndexOf(QAnyStringView whole, QLatin1StringView part)
+{
+ Q_ASSERT(QtPrivate::isAscii(part));
+ return whole.visit([&](auto view) { return getLastIndexOf(view, part); });
+}
+
+inline int toInt(QUtf8StringView view)
+{
+ return QByteArrayView(view.data(), view.length()).toInt();
+}
+inline int toInt(QLatin1StringView view) { return view.toInt(); }
+inline int toInt(QStringView view) { return view.toInt(); }
+
+inline int toInt(QAnyStringView string)
+{
+ return string.visit([](auto view) { return toInt(view); });
+}
+
+template<typename StringView>
+QAnyStringView doTrimmed(StringView string)
+{
+ if constexpr (std::is_same_v<StringView, QStringView>)
+ return string.trimmed();
+ if constexpr (std::is_same_v<StringView, QLatin1StringView>)
+ return string.trimmed();
+ if constexpr (std::is_same_v<StringView, QUtf8StringView>)
+ return QByteArrayView(string.data(), string.length()).trimmed();
+}
+
+
+inline QAnyStringView trimmed(QAnyStringView string)
+{
+ return string.visit([](auto data) {
+ return doTrimmed(data);
+ });
+}
+
+template<typename StringView, typename Handler>
+auto processAsUtf8(StringView string, Handler &&handler)
+{
+ if constexpr (std::is_same_v<StringView, QStringView>)
+ return handler(QByteArrayView(string.toUtf8()));
+ if constexpr (std::is_same_v<StringView, QLatin1StringView>)
+ return handler(QByteArrayView(string.data(), string.length()));
+ if constexpr (std::is_same_v<StringView, QUtf8StringView>)
+ return handler(QByteArrayView(string.data(), string.length()));
+ if constexpr (std::is_same_v<StringView, QByteArrayView>)
+ return handler(string);
+ if constexpr (std::is_same_v<StringView, QByteArray>)
+ return handler(QByteArrayView(string));
+ if constexpr (std::is_same_v<StringView, QAnyStringView>) {
+
+ // Handler is:
+ // * a reference if an lvalue ref is passed
+ // * a value otherwise
+ // We conserve its nature for passing to the lambda below.
+ // This is necessary because we need to decide on the nature of
+ // the lambda capture as part of the syntax (prefix '&' or not).
+ // So we always pass a reference-conserving wrapper as value.
+ struct Wrapper { Handler handler; };
+
+ return string.visit([w = Wrapper { std::forward<Handler>(handler) }](auto view) mutable {
+ static_assert(!(std::is_same_v<decltype(view), QAnyStringView>));
+ return processAsUtf8(std::move(view), std::forward<Handler>(w.handler));
+ });
+ }
+ Q_UNREACHABLE();
+}
+
+// Note: This only works if sep is US-ASCII, but there is no type to encode this information!
+inline QList<QAnyStringView> split(QAnyStringView source, QLatin1StringView sep)
+{
+ Q_ASSERT(QtPrivate::isAscii(sep));
+
+ QList<QAnyStringView> list;
+ if (source.isEmpty()) {
+ list.append(source);
+ return list;
+ }
+
+ qsizetype start = 0;
+ qsizetype end = source.length();
+
+ for (qsizetype current = 0; current < end; ++current) {
+ if (source.mid(current, sep.length()) == sep) {
+ list.append(source.mid(start, current - start));
+ start = current + sep.length();
+ }
+ }
+
+ if (start < end)
+ list.append(source.mid(start, end - start));
+
+ return list;
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QANYSTRINGVIEWUTILS_P_H
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
index 899e8f91bd..480d4ba187 100644
--- a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
@@ -3,50 +3,90 @@
#include "qmetatypesjsonprocessor_p.h"
+#include "qanystringviewutils_p.h"
+#include "qqmltyperegistrarconstants_p.h"
+#include "qqmltyperegistrarutils_p.h"
+#include "qqmltypesclassdescription_p.h"
+#include "qqmltyperegistrarutils_p.h"
+
+#include <QtCore/qcborarray.h>
+#include <QtCore/qcbormap.h>
#include <QtCore/qfile.h>
-#include <QtCore/qjsonarray.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qqueue.h>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using namespace Constants;
+using namespace Constants::MetatypesDotJson;
+using namespace Constants::MetatypesDotJson::Qml;
+using namespace QAnyStringViewUtils;
+
+const MetaTypePrivate MetaType::s_empty;
+
+// TODO: This could be optimized to store the objects in a more compact way.
+std::vector<std::unique_ptr<MetaTypePrivate>> s_pool;
+
+static QCborValue fromJson(const QByteArray &json, QJsonParseError *error)
+{
+ const QJsonDocument jsonValue = QJsonDocument::fromJson(json, error);
+ if (jsonValue.isArray())
+ return QCborValue::fromJsonValue(jsonValue.array());
+ if (jsonValue.isObject())
+ return QCborValue::fromJsonValue(jsonValue.object());
+ return QCborValue();
+}
+
+QList<QAnyStringView> MetaTypesJsonProcessor::namespaces(const MetaType &classDef)
+{
+ const QAnyStringView unqualified = classDef.className();
+ const QAnyStringView qualified = classDef.qualifiedClassName();
+
+ QList<QAnyStringView> namespaces;
+ if (qualified != unqualified) {
+ namespaces = split(qualified, "::"_L1);
+ Q_ASSERT(namespaces.last() == unqualified);
+ namespaces.pop_back();
+ }
+
+ return namespaces;
+}
bool MetaTypesJsonProcessor::processTypes(const QStringList &files)
{
for (const QString &source: files) {
- QJsonDocument metaObjects;
+ QCborValue metaObjects;
{
QFile f(source);
if (!f.open(QIODevice::ReadOnly)) {
- fprintf(stderr, "Error opening %s for reading\n", qPrintable(source));
+ error(source) << "Cannot open file for reading";
return false;
}
- QJsonParseError error = {0, QJsonParseError::NoError};
- metaObjects = QJsonDocument::fromJson(f.readAll(), &error);
- if (error.error != QJsonParseError::NoError) {
- fprintf(stderr, "Error %d while parsing %s: %s\n", error.error, qPrintable(source),
- qPrintable(error.errorString()));
+ QJsonParseError parseError = {0, QJsonParseError::NoError};
+ metaObjects = fromJson(f.readAll(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ error(source)
+ << "Failed to parse JSON:" << parseError.error
+ << parseError.errorString();
return false;
}
}
if (metaObjects.isArray()) {
- const QJsonArray metaObjectsArray = metaObjects.array();
- for (const QJsonValue metaObject : metaObjectsArray) {
- if (!metaObject.isObject()) {
- fprintf(stderr, "Error parsing %s: JSON is not an object\n",
- qPrintable(source));
+ const QCborArray metaObjectsArray = metaObjects.toArray();
+ for (const QCborValue &metaObject : metaObjectsArray) {
+ if (!metaObject.isMap()) {
+ error(source) << "JSON is not an object";
return false;
}
- processTypes(metaObject.toObject());
+ processTypes(metaObject.toMap());
}
- } else if (metaObjects.isObject()) {
- processTypes(metaObjects.object());
+ } else if (metaObjects.isMap()) {
+ processTypes(metaObjects.toMap());
} else {
- fprintf(stderr, "Error parsing %s: JSON is not an object or an array\n",
- qPrintable(source));
+ error(source) << "JSON is not an object or an array";
return false;
}
}
@@ -54,102 +94,114 @@ bool MetaTypesJsonProcessor::processTypes(const QStringList &files)
return true;
}
-bool MetaTypesJsonProcessor::processForeignTypes(const QStringList &foreignTypesFiles)
+bool MetaTypesJsonProcessor::processForeignTypes(const QString &types)
{
- bool success = true;
+ QFile typesFile(types);
+ if (!typesFile.open(QIODevice::ReadOnly)) {
+ error(types) << "Cannot open foreign types file";
+ return false;
+ }
- for (const QString &types : foreignTypesFiles) {
- QFile typesFile(types);
- if (!typesFile.open(QIODevice::ReadOnly)) {
- fprintf(stderr, "Cannot open foreign types file %s\n", qPrintable(types));
- success = false;
- continue;
- }
+ QJsonParseError parseError = {0, QJsonParseError::NoError};
+ QCborValue foreignMetaObjects = fromJson(typesFile.readAll(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ error(types)
+ << "Failed to parse JSON:" << parseError.error
+ << parseError.errorString();
+ return false;
+ }
- QJsonParseError error = {0, QJsonParseError::NoError};
- QJsonDocument foreignMetaObjects = QJsonDocument::fromJson(typesFile.readAll(), &error);
- if (error.error != QJsonParseError::NoError) {
- fprintf(stderr, "Error %d while parsing %s: %s\n", error.error, qPrintable(types),
- qPrintable(error.errorString()));
- success = false;
- continue;
+ const QCborArray foreignObjectsArray = foreignMetaObjects.toArray();
+ for (const QCborValue &metaObject : foreignObjectsArray) {
+ if (!metaObject.isMap()) {
+ error(types) << "JSON is not an object";
+ return false;
}
- const QJsonArray foreignObjectsArray = foreignMetaObjects.array();
- for (const QJsonValue metaObject : foreignObjectsArray) {
- if (!metaObject.isObject()) {
- fprintf(stderr, "Error parsing %s: JSON is not an object\n",
- qPrintable(types));
- success = false;
- continue;
- }
+ processForeignTypes(metaObject.toMap());
+ }
- processForeignTypes(metaObject.toObject());
- }
+ return true;
+}
+
+bool MetaTypesJsonProcessor::processForeignTypes(const QStringList &foreignTypesFiles)
+{
+ bool success = true;
+
+ for (const QString &types : foreignTypesFiles) {
+ if (!processForeignTypes(types))
+ success = false;
}
return success;
}
-static void sortStringList(QStringList *list)
+template<typename String>
+static void sortStringList(QList<String> *list)
{
std::sort(list->begin(), list->end());
const auto newEnd = std::unique(list->begin(), list->end());
- list->erase(QStringList::const_iterator(newEnd), list->constEnd());
+ list->erase(typename QList<String>::const_iterator(newEnd), list->constEnd());
}
void MetaTypesJsonProcessor::postProcessTypes()
{
sortTypes(m_types);
- sortStringList(&m_includes);
}
void MetaTypesJsonProcessor::postProcessForeignTypes()
{
sortTypes(m_foreignTypes);
+ sortStringList(&m_primitiveTypes);
addRelatedTypes();
sortStringList(&m_referencedTypes);
- sortTypes(m_types);
+ sortStringList(&m_includes);
}
QString MetaTypesJsonProcessor::extractRegisteredTypes() const
{
QString registrationHelper;
for (const auto &obj: m_types) {
- const QString className = obj[u"className"].toString();
- const QString foreignClassName = className+ u"Foreign";
- const auto classInfos = obj[u"classInfos"].toArray();
- QString qmlElement;
+ const QString className = obj.className().toString();
+ const QString qualifiedClassName = obj.qualifiedClassName().toString();
+ const QString foreignClassName = className + u"Foreign";
+ QStringList qmlElements;
QString qmlUncreatable;
QString qmlAttached;
bool isSingleton = false;
bool isExplicitlyUncreatable = false;
- for (QJsonValue entry: classInfos) {
- const auto name = entry[u"name"].toString();
- const auto value = entry[u"value"].toString();
- if (name == u"QML.Element") {
- if (value == u"auto") {
- qmlElement = u"QML_NAMED_ELEMENT("_s + className + u")"_s;
- } else if (value == u"anonymous") {
- qmlElement = u"QML_ANONYMOUS"_s;
+ bool isNamespace = obj.kind() == MetaType::Kind::Namespace;
+ for (const ClassInfo &entry: obj.classInfos()) {
+ const auto name = entry.name;
+ const auto value = entry.value;
+ if (name == S_ELEMENT) {
+ if (value == S_AUTO) {
+ qmlElements.append(u"QML_NAMED_ELEMENT("_s + className + u")"_s);
+ } else if (value == S_ANONYMOUS) {
+ qmlElements.append(u"QML_ANONYMOUS"_s);
} else {
- qmlElement = u"QML_NAMED_ELEMENT(" + value + u")";
+ qmlElements.append(u"QML_NAMED_ELEMENT("_s + value.toString() + u")");
}
- } else if (name == u"QML.Creatable" && value == u"false") {
+ } else if (name == S_CREATABLE && value == S_FALSE) {
isExplicitlyUncreatable = true;
- } else if (name == u"QML.UncreatableReason") {
- qmlUncreatable = u"QML_UNCREATABLE(\"" + value + u"\")";
- } else if (name == u"QML.Attached") {
- qmlAttached = u"QML_ATTACHED("_s + value + u")";
- } else if (name == u"QML.Singleton") {
+ } else if (name == S_UNCREATABLE_REASON) {
+ qmlUncreatable = u"QML_UNCREATABLE(\""_s + value.toString() + u"\")";
+ } else if (name == S_ATTACHED) {
+ qmlAttached = u"QML_ATTACHED("_s + value.toString() + u")";
+ } else if (name == S_SINGLETON) {
isSingleton = true;
}
}
- if (qmlElement.isEmpty())
+ if (qmlElements.isEmpty())
continue; // no relevant entries found
const QString spaces = u" "_s;
- registrationHelper += u"\nstruct "_s + foreignClassName + u"{\n Q_GADGET\n"_s;
- registrationHelper += spaces + u"QML_FOREIGN(" + className + u")\n"_s;
- registrationHelper += spaces + qmlElement + u"\n"_s;
+ if (isNamespace) {
+ registrationHelper += u"\nnamespace "_s + foreignClassName + u"{\n Q_NAMESPACE\n"_s;
+ registrationHelper += spaces + u"QML_FOREIGN_NAMESPACE(" + qualifiedClassName + u")\n"_s;
+ } else {
+ registrationHelper += u"\nstruct "_s + foreignClassName + u"{\n Q_GADGET\n"_s;
+ registrationHelper += spaces + u"QML_FOREIGN(" + qualifiedClassName + u")\n"_s;
+ }
+ registrationHelper += spaces + qmlElements.join(u"\n"_s) + u"\n"_s;
if (isSingleton)
registrationHelper += spaces + u"QML_SINGLETON\n"_s;
if (isExplicitlyUncreatable) {
@@ -160,60 +212,132 @@ QString MetaTypesJsonProcessor::extractRegisteredTypes() const
}
if (!qmlAttached.isEmpty())
registrationHelper += spaces + qmlAttached + u"\n";
- registrationHelper += u"};\n";
+ registrationHelper += u"}";
+ if (!isNamespace)
+ registrationHelper += u";";
+ registrationHelper += u"\n";
}
return registrationHelper;
}
-MetaTypesJsonProcessor::RegistrationMode MetaTypesJsonProcessor::qmlTypeRegistrationMode(
- const QJsonObject &classDef)
+MetaTypesJsonProcessor::PreProcessResult MetaTypesJsonProcessor::preProcess(
+ const MetaType &classDef, PopulateMode populateMode)
{
- const QJsonArray classInfos = classDef[QLatin1String("classInfos")].toArray();
- for (const QJsonValue info : classInfos) {
- const QString name = info[QLatin1String("name")].toString();
- if (name == QLatin1String("QML.Element")) {
- if (classDef[QLatin1String("object")].toBool())
- return ObjectRegistration;
- if (classDef[QLatin1String("gadget")].toBool())
- return GadgetRegistration;
- if (classDef[QLatin1String("namespace")].toBool())
- return NamespaceRegistration;
- qWarning() << "Not registering classInfo which is neither an object, "
- "nor a gadget, nor a namespace:"
- << name;
- break;
+ // If this type is a self-extending value type or a sequence type or has a JavaScript extension
+ // and is not the root object, then it's foreign type has no entry of its own.
+ // In that case we need to generate a "primitive" entry.
+
+ QList<QAnyStringView> aliases;
+ QAnyStringView foreign;
+ RegistrationMode mode = NoRegistration;
+ bool isSelfExtendingValueType = false;
+ bool hasJavaScriptExtension = false;
+ bool isRootObject = false;
+ bool isSequence = false;
+
+ for (const ClassInfo &classInfo : classDef.classInfos()) {
+ if (classInfo.name == S_FOREIGN)
+ foreign = classInfo.value;
+ else if (classInfo.name == S_PRIMITIVE_ALIAS)
+ aliases.append(classInfo.value);
+ else if (classInfo.name == S_EXTENSION_IS_JAVA_SCRIPT)
+ hasJavaScriptExtension = (classInfo.value == S_TRUE);
+ else if (classInfo.name == S_EXTENDED && classDef.kind() == MetaType::Kind::Gadget)
+ isSelfExtendingValueType = classInfo.value == classDef.className();
+ else if (classInfo.name == S_ROOT)
+ isRootObject = (classInfo.value == S_TRUE);
+ else if (classInfo.name == S_SEQUENCE)
+ isSequence = true;
+ else if (populateMode == PopulateMode::Yes && classInfo.name == S_ELEMENT) {
+ switch (classDef.kind()) {
+ case MetaType::Kind::Object:
+ mode = ObjectRegistration;
+ break;
+ case MetaType::Kind::Gadget:
+ mode = GadgetRegistration;
+ break;
+ case MetaType::Kind::Namespace:
+ mode = NamespaceRegistration;
+ break;
+ default:
+ warning(classDef)
+ << "Not registering a classInfo which is neither an object,"
+ << "nor a gadget, nor a namespace:"
+ << classInfo.name.toString();
+ break;
+ }
}
}
- return NoRegistration;
+
+ return PreProcessResult {
+ aliases,
+ (!isRootObject && (isSequence || isSelfExtendingValueType || hasJavaScriptExtension))
+ ? foreign
+ : QAnyStringView(),
+ mode
+ };
+
+}
+
+// TODO: Remove this when QAnyStringView gets a proper qHash()
+static size_t qHash(QAnyStringView string, size_t seed = 0)
+{
+ return string.visit([seed](auto view) {
+ if constexpr (std::is_same_v<decltype(view), QStringView>)
+ return qHash(view, seed);
+ if constexpr (std::is_same_v<decltype(view), QLatin1StringView>)
+ return qHash(view, seed);
+ if constexpr (std::is_same_v<decltype(view), QUtf8StringView>)
+ return qHash(QByteArrayView(view.data(), view.length()), seed);
+ });
+}
+
+static bool qualifiedClassNameLessThan(const MetaType &a, const MetaType &b)
+{
+ return a.qualifiedClassName() < b.qualifiedClassName();
}
void MetaTypesJsonProcessor::addRelatedTypes()
{
- const QLatin1String classInfosKey("classInfos");
- const QLatin1String nameKey("name");
- const QLatin1String qualifiedClassNameKey("qualifiedClassName");
- const QLatin1String qmlNamePrefix("QML.");
- const QLatin1String qmlForeignName("QML.Foreign");
- const QLatin1String qmlExtendedName("QML.Extended");
- const QLatin1String qmlAttachedName("QML.Attached");
- const QLatin1String qmlSequenceName("QML.Sequence");
- const QLatin1String valueKey("value");
- const QLatin1String superClassesKey("superClasses");
- const QLatin1String accessKey("access");
- const QLatin1String publicAccess("public");
-
- QSet<QString> processedRelatedNames;
- QQueue<QJsonObject> typeQueue;
+ QSet<QAnyStringView> processedRelatedNativeNames;
+ QSet<QAnyStringView> processedRelatedJavaScriptNames;
+ QSet<QAnyStringView> unresolvedForeignNames;
+ QQueue<MetaType> typeQueue;
typeQueue.append(m_types);
+ const auto addRelatedName
+ = [&](QAnyStringView relatedName, const QList<QAnyStringView> &namespaces) {
+ if (const FoundType related = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, relatedName, namespaces)) {
+
+ if (!related.javaScript.isEmpty())
+ processedRelatedJavaScriptNames.insert(related.javaScript.qualifiedClassName());
+
+ if (!related.native.isEmpty())
+ processedRelatedNativeNames.insert(related.native.qualifiedClassName());
+
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ const auto addRelatedType = [&](const MetaType &type) {
+ const QAnyStringView qualifiedName = type.qualifiedClassName();
+ if (type.inputFile().isEmpty())
+ processedRelatedJavaScriptNames.insert(qualifiedName);
+ else
+ processedRelatedNativeNames.insert(qualifiedName);
+ };
+
// First mark all classes registered from this module as already processed.
- for (const QJsonObject &type : m_types) {
- processedRelatedNames.insert(type.value(qualifiedClassNameKey).toString());
- const auto classInfos = type.value(classInfosKey).toArray();
- for (const QJsonValue classInfo : classInfos) {
- const QJsonObject obj = classInfo.toObject();
- if (obj.value(nameKey).toString() == qmlForeignName) {
- processedRelatedNames.insert(obj.value(valueKey).toString());
+ for (const MetaType &type : std::as_const(m_types)) {
+ addRelatedType(type);
+ for (const ClassInfo &obj : type.classInfos()) {
+ if (obj.name == S_FOREIGN) {
+ const QAnyStringView foreign = obj.value;
+ if (!addRelatedName(foreign, namespaces(type)))
+ unresolvedForeignNames.insert(foreign);
break;
}
}
@@ -221,143 +345,503 @@ void MetaTypesJsonProcessor::addRelatedTypes()
// Then mark all classes registered from other modules as already processed.
// We don't want to generate them again for this module.
- for (const QJsonObject &foreignType : m_foreignTypes) {
- const auto classInfos = foreignType.value(classInfosKey).toArray();
+ for (const MetaType &foreignType : std::as_const(m_foreignTypes)) {
bool seenQmlPrefix = false;
- for (const QJsonValue classInfo : classInfos) {
- const QJsonObject obj = classInfo.toObject();
- const QString name = obj.value(nameKey).toString();
- if (!seenQmlPrefix && name.startsWith(qmlNamePrefix)) {
- processedRelatedNames.insert(foreignType.value(qualifiedClassNameKey).toString());
+ for (const ClassInfo &obj : foreignType.classInfos()) {
+ const QAnyStringView name = obj.name;
+ if (!seenQmlPrefix && startsWith(name, "QML."_L1)) {
+ addRelatedType(foreignType);
seenQmlPrefix = true;
}
- if (name == qmlForeignName) {
- processedRelatedNames.insert(obj.value(valueKey).toString());
+ if (name == S_FOREIGN) {
+ const QAnyStringView foreign = obj.value;
+ if (!addRelatedName(foreign, namespaces(foreignType)))
+ unresolvedForeignNames.insert(foreign);
break;
}
}
}
- auto addType = [&](const QString &typeName) {
- m_referencedTypes.append(typeName);
- if (processedRelatedNames.contains(typeName))
+ const auto addReference = [&](const MetaType &type, QSet<QAnyStringView> *processedRelatedNames,
+ FoundType::Origin origin) {
+ if (type.isEmpty())
+ return;
+ QAnyStringView qualifiedName = type.qualifiedClassName();
+ m_referencedTypes.append(qualifiedName);
+ const qsizetype size = processedRelatedNames->size();
+ processedRelatedNames->insert(qualifiedName);
+
+ if (processedRelatedNames->size() == size)
+ return;
+
+ typeQueue.enqueue(type);
+
+ if (origin == FoundType::OwnTypes)
return;
- processedRelatedNames.insert(typeName);
- if (const QJsonObject *other
- = QmlTypesClassDescription::findType(m_foreignTypes, typeName)) {
- m_types.append(*other);
- typeQueue.enqueue(*other);
+
+ // Add to own types since we need it for our registrations.
+ const auto insert = std::lower_bound(
+ m_types.constBegin(), m_types.constEnd(), type,
+ qualifiedClassNameLessThan);
+ m_types.insert(insert, type);
+
+ // Also add its include. We don't want to rely on transitive inclues.
+ const QString inputFile = type.inputFile();
+ if (!inputFile.isEmpty())
+ m_includes.append(inputFile);
+
+ // Remove from the foreign types to avoid the ODR warning.
+ const auto remove = std::equal_range(
+ m_foreignTypes.constBegin(), m_foreignTypes.constEnd(), type,
+ qualifiedClassNameLessThan);
+ for (auto it = remove.first; it != remove.second; ++it) {
+ if (*it == type) {
+ m_foreignTypes.erase(it);
+ break;
+ }
}
};
+ const auto addInterface
+ = [&](QAnyStringView typeName, const QList<QAnyStringView> &namespaces) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName, namespaces)) {
+ if (!other.native.isEmpty()) {
+ addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ return true;
+ }
+ } else {
+ // Do not warn about unresolved interfaces.
+ // They don't have to have Q_OBJECT or Q_GADGET.
+ unresolvedForeignNames.insert(typeName);
+ }
+
+ processedRelatedNativeNames.insert(typeName);
+ return false;
+ };
+
+ const auto addType = [&](const MetaType &context, QAnyStringView typeName,
+ const QList<QAnyStringView> &namespaces, QAnyStringView relation) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName, namespaces)) {
+ addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ addReference(other.javaScript, &processedRelatedJavaScriptNames, other.javaScriptOrigin);
+ return true;
+ }
+
+ // If it's an enum, add the surrounding type.
+ const QLatin1StringView separator("::");
+ if (const qsizetype index = lastIndexOf(typeName, separator); index > 0) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName.left(index), namespaces)) {
+
+ const QAnyStringView enumName = typeName.mid(index + separator.length());
+
+ for (const Enum &enumerator : other.native.enums()) {
+ if (enumerator.name != enumName && enumerator.alias != enumName)
+ continue;
+
+ addReference(other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ addReference(
+ other.javaScript, &processedRelatedJavaScriptNames,
+ other.javaScriptOrigin);
+ return true;
+ }
+ }
+ }
+
+ // If it's an enum of the context type itself, we don't have to do anything.
+ for (const Enum &enumerator : context.enums()) {
+ if (enumerator.name == typeName || enumerator.alias == typeName)
+ return true;
+ }
+
+ // If we've detected this type as unresolved foreign and it actually belongs to this module,
+ // we'll get to it again when we process it as foreign type. In that case we'll look at the
+ // special cases for sequences and extensions.
+ if (!unresolvedForeignNames.contains(typeName) && !isPrimitive(typeName))
+ warning(context) << typeName << "is used as" << relation << "type but cannot be found.";
+
+ processedRelatedNativeNames.insert(typeName);
+ processedRelatedJavaScriptNames.insert(typeName);
+ return false;
+ };
+
+ const auto doAddReferences = [&](QAnyStringView typeName,
+ const QList<QAnyStringView> &namespaces) {
+ if (const FoundType other = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, typeName, namespaces)) {
+ addReference(
+ other.native, &processedRelatedNativeNames, other.nativeOrigin);
+ addReference(
+ other.javaScript, &processedRelatedJavaScriptNames, other.javaScriptOrigin);
+ return true;
+ }
+
+ return false;
+ };
+
+ const auto addSupers = [&](const MetaType &context, const QList<QAnyStringView> &namespaces) {
+ for (const Interface &iface : context.ifaces())
+ addInterface(interfaceName(iface), namespaces);
+
+ // We don't warn about missing bases for value types. They don't have to be registered.
+ bool warnAboutSupers = context.kind() != MetaType::Kind::Gadget;
+
+ QList<QAnyStringView> missingSupers;
+
+ for (const BaseType &superObject : context.superClasses()) {
+ if (superObject.access != Access::Public)
+ continue;
+
+ QAnyStringView typeName = superObject.name;
+ if (doAddReferences(typeName, namespaces))
+ warnAboutSupers = false;
+ else
+ missingSupers.append(typeName);
+ }
+
+ for (QAnyStringView typeName : std::as_const(missingSupers)) {
+ // If we've found one valid base type, don't complain about the others.
+ if (warnAboutSupers
+ && !unresolvedForeignNames.contains(typeName)
+ && !isPrimitive(typeName)) {
+ warning(context) << typeName << "is used as base type but cannot be found.";
+ }
+
+ processedRelatedNativeNames.insert(typeName);
+ processedRelatedJavaScriptNames.insert(typeName);
+ }
+ };
+
+ const auto addProperties = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Property &property : context.properties()) {
+ ResolvedTypeAlias resolved(property.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "property");
+ }
+ };
+
+ const auto addMethods = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Method::Container &methods
+ : {context.methods(), context.constructors(), context.sigs() }) {
+ for (const Method &methodObject : methods) {
+ for (const Argument &argument : std::as_const(methodObject.arguments)) {
+ ResolvedTypeAlias resolved(argument.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "argument");
+ }
+
+ ResolvedTypeAlias resolved(methodObject.returnType);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "return");
+ }
+ }
+ };
+
+ const auto addEnums = [&](const MetaType &context,
+ const QList<QAnyStringView> &namespaces) {
+ for (const Enum &enumerator : context.enums()) {
+ ResolvedTypeAlias resolved(enumerator.type);
+ if (!resolved.type.isEmpty())
+ addType(context, resolved.type, namespaces, "enum");
+ }
+ };
+
+ const auto addRelation = [&](const MetaType &classDef, const ClassInfo &obj,
+ const QList<QAnyStringView> &namespaces) {
+ const QAnyStringView objNameValue = obj.name;
+ if (objNameValue == S_ATTACHED) {
+ addType(classDef, obj.value, namespaces, "attached");
+ return true;
+ } else if (objNameValue == S_SEQUENCE) {
+ ResolvedTypeAlias value(obj.value);
+ addType(classDef, value.type, namespaces, "sequence value");
+ return true;
+ } else if (objNameValue == S_EXTENDED) {
+ const QAnyStringView value = obj.value;
+ addType(classDef, value, namespaces, "extension");
+ return true;
+ }
+ return false;
+ };
+
// Then recursively iterate the super types and attached types, marking the
// ones we are interested in as related.
while (!typeQueue.isEmpty()) {
- const QJsonObject classDef = typeQueue.dequeue();
-
- const auto classInfos = classDef.value(classInfosKey).toArray();
- for (const QJsonValue classInfo : classInfos) {
- const QJsonObject obj = classInfo.toObject();
- const QString objNameValue = obj.value(nameKey).toString();
- if (objNameValue == qmlAttachedName || objNameValue == qmlSequenceName
- || objNameValue == qmlExtendedName) {
- addType(obj.value(valueKey).toString());
- } else if (objNameValue == qmlForeignName) {
- const QString foreignClassName = obj.value(valueKey).toString();
- if (const QJsonObject *other = QmlTypesClassDescription::findType(
- m_foreignTypes, foreignClassName)) {
- const auto otherSupers = other->value(superClassesKey).toArray();
- if (!otherSupers.isEmpty()) {
- const QJsonObject otherSuperObject = otherSupers.first().toObject();
- if (otherSuperObject.value(accessKey).toString() == publicAccess)
- addType(otherSuperObject.value(nameKey).toString());
- }
-
- const auto otherClassInfos = other->value(classInfosKey).toArray();
- for (const QJsonValue otherClassInfo : otherClassInfos) {
- const QJsonObject obj = otherClassInfo.toObject();
- const QString objNameValue = obj.value(nameKey).toString();
- if (objNameValue == qmlAttachedName || objNameValue == qmlSequenceName
- || objNameValue == qmlExtendedName) {
- addType(obj.value(valueKey).toString());
- break;
- }
- // No, you cannot chain QML_FOREIGN declarations. Sorry.
- }
+ QAnyStringView unresolvedForeign;
+
+ const MetaType classDef = typeQueue.dequeue();
+ const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
+
+ for (const ClassInfo &obj : classDef.classInfos()) {
+ if (addRelation(classDef, obj, namespaces))
+ continue;
+ if (obj.name != S_FOREIGN)
+ continue;
+
+ const QAnyStringView foreignClassName = obj.value;
+
+ // A type declared as QML_FOREIGN will usually be a foreign type, but it can
+ // actually be an additional registration of a local type, too.
+ if (const FoundType found = QmlTypesClassDescription::findType(
+ m_foreignTypes, {}, foreignClassName, namespaces)) {
+ const MetaType other = found.select(classDef, "Foreign");
+ const QList<QAnyStringView> otherNamespaces
+ = MetaTypesJsonProcessor::namespaces(other);
+ addSupers(other, otherNamespaces);
+ addProperties(other, otherNamespaces);
+ addMethods(other, otherNamespaces);
+ addEnums(other, otherNamespaces);
+
+ for (const ClassInfo &obj : other.classInfos()) {
+ if (addRelation(classDef, obj, otherNamespaces))
+ break;
+ // No, you cannot chain S_FOREIGN declarations. Sorry.
}
+ } else if (!QmlTypesClassDescription::findType(
+ m_types, {}, foreignClassName, namespaces)) {
+ unresolvedForeign = foreignClassName;
}
}
- const auto supers = classDef.value(superClassesKey).toArray();
- if (!supers.isEmpty()) {
- const QJsonObject superObject = supers.first().toObject();
- if (superObject.value(accessKey).toString() == publicAccess)
- addType(superObject.value(nameKey).toString());
+ if (!unresolvedForeign.isEmpty() && !isPrimitive(unresolvedForeign)) {
+ warning(classDef)
+ << unresolvedForeign
+ << "is declared as foreign type, but cannot be found.";
}
+
+ addSupers(classDef, namespaces);
+ addProperties(classDef, namespaces);
+ addMethods(classDef, namespaces);
+ addEnums(classDef, namespaces);
}
}
-void MetaTypesJsonProcessor::sortTypes(QVector<QJsonObject> &types)
+void MetaTypesJsonProcessor::sortTypes(QVector<MetaType> &types)
{
- const QLatin1String qualifiedClassNameKey("qualifiedClassName");
- std::sort(types.begin(), types.end(), [&](const QJsonObject &a, const QJsonObject &b) {
- return a.value(qualifiedClassNameKey).toString() <
- b.value(qualifiedClassNameKey).toString();
- });
+ std::sort(types.begin(), types.end(), qualifiedClassNameLessThan);
}
-QString MetaTypesJsonProcessor::resolvedInclude(const QString &include)
+QString MetaTypesJsonProcessor::resolvedInclude(QAnyStringView include)
{
- return (m_privateIncludes && include.endsWith(QLatin1String("_p.h")))
- ? QLatin1String("private/") + include
- : include;
+ if (!m_privateIncludes)
+ return include.toString();
+
+ if (endsWith(include, "_p.h"_L1))
+ return QLatin1String("private/") + include.toString();
+
+ if (startsWith(include, "qplatform"_L1) || startsWith(include, "qwindowsystem"_L1))
+ return QLatin1String("qpa/") + include.toString();
+
+ return include.toString();
}
-void MetaTypesJsonProcessor::processTypes(const QJsonObject &types)
+void MetaTypesJsonProcessor::processTypes(const QCborMap &types)
{
- const QString include = resolvedInclude(types[QLatin1String("inputFile")].toString());
- const QJsonArray classes = types[QLatin1String("classes")].toArray();
- for (const QJsonValue cls : classes) {
- QJsonObject classDef = cls.toObject();
- classDef.insert(QLatin1String("inputFile"), include);
+ const QString include = resolvedInclude(toStringView(types, S_INPUT_FILE));
+ const QCborArray classes = types[S_CLASSES].toArray();
+ for (const QCborValue &cls : classes) {
+ const MetaType classDef(cls.toMap(), include);
- switch (qmlTypeRegistrationMode(classDef)) {
+ const PreProcessResult preprocessed = preProcess(classDef, PopulateMode::Yes);
+ switch (preprocessed.mode) {
case NamespaceRegistration:
case GadgetRegistration:
case ObjectRegistration: {
- if (!include.endsWith(QLatin1String(".h"))
- && !include.endsWith(QLatin1String(".hpp"))
- && !include.endsWith(QLatin1String(".hxx"))
- && !include.endsWith(QLatin1String(".hh"))
- && !include.endsWith(u".py")
- && include.contains(QLatin1Char('.'))) {
- fprintf(stderr,
- "Class %s is declared in %s, which appears not to be a header.\n"
- "The compilation of its registration to QML may fail.\n",
- qPrintable(classDef.value(QLatin1String("qualifiedClassName"))
- .toString()),
- qPrintable(include));
+ if (!endsWith(include, QLatin1String(".h"))
+ && !endsWith(include, QLatin1String(".hpp"))
+ && !endsWith(include, QLatin1String(".hxx"))
+ && !endsWith(include, QLatin1String(".hh"))
+ && !endsWith(include, QLatin1String(".py"))
+ && contains(include, QLatin1Char('.'))) {
+ warning(include)
+ << "Class" << classDef.qualifiedClassName()
+ << "is declared in" << include << "which appears not to be a header."
+ << "The compilation of its registration to QML may fail.";
}
m_includes.append(include);
- m_types.append(classDef);
+ m_types.emplaceBack(classDef);
break;
}
case NoRegistration:
- m_foreignTypes.append(classDef);
+ m_foreignTypes.emplaceBack(classDef);
break;
}
+
+ if (!preprocessed.foreignPrimitive.isEmpty()) {
+ m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
+ m_primitiveTypes.append(preprocessed.primitiveAliases);
+ }
}
}
-void MetaTypesJsonProcessor::processForeignTypes(const QJsonObject &types)
+void MetaTypesJsonProcessor::processForeignTypes(const QCborMap &types)
{
- const QString include = resolvedInclude(types[QLatin1String("inputFile")].toString());
- const QJsonArray classes = types[QLatin1String("classes")].toArray();
- for (const QJsonValue cls : classes) {
- QJsonObject classDef = cls.toObject();
- classDef.insert(QLatin1String("inputFile"), include);
- m_foreignTypes.append(classDef);
+ const QString include = resolvedInclude(toStringView(types, S_INPUT_FILE));
+ const QCborArray classes = types[S_CLASSES].toArray();
+ for (const QCborValue &cls : classes) {
+ const MetaType classDef(cls.toMap(), include);
+ PreProcessResult preprocessed = preProcess(classDef, PopulateMode::No);
+
+ m_foreignTypes.emplaceBack(classDef);
+ if (!preprocessed.foreignPrimitive.isEmpty()) {
+ m_primitiveTypes.emplaceBack(preprocessed.foreignPrimitive);
+ m_primitiveTypes.append(preprocessed.primitiveAliases);
+ }
+ }
+}
+
+static QTypeRevision getRevision(const QCborMap &cbor)
+{
+ const auto it = cbor.find(S_REVISION);
+ return it == cbor.end()
+ ? QTypeRevision()
+ : QTypeRevision::fromEncodedVersion(it->toInteger());
+}
+
+static Access getAccess(const QCborMap &cbor)
+{
+ const QAnyStringView access = toStringView(cbor, S_ACCESS);
+ if (access == S_PUBLIC)
+ return Access::Public;
+ if (access == S_PROTECTED)
+ return Access::Protected;
+ return Access::Private;
+}
+
+BaseType::BaseType(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , access(getAccess(cbor))
+{
+}
+
+ClassInfo::ClassInfo(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , value(toStringView(cbor, S_VALUE))
+{
+}
+
+Interface::Interface(const QCborValue &cbor)
+{
+ if (cbor.isArray()) {
+ QCborArray needlessWrapping = cbor.toArray();
+ className = needlessWrapping.size() > 0
+ ? toStringView(needlessWrapping[0].toMap(), S_CLASS_NAME)
+ : QAnyStringView();
+ } else {
+ className = toStringView(cbor.toMap(), S_CLASS_NAME);
+ }
+}
+
+Property::Property(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , type(toStringView(cbor, S_TYPE))
+ , member(toStringView(cbor, S_MEMBER))
+ , read(toStringView(cbor, S_READ))
+ , write(toStringView(cbor, S_WRITE))
+ , reset(toStringView(cbor, S_RESET))
+ , notify(toStringView(cbor, S_NOTIFY))
+ , bindable(toStringView(cbor, S_BINDABLE))
+ , privateClass(toStringView(cbor, S_PRIVATE_CLASS))
+ , index(cbor[S_INDEX].toInteger(-1))
+ , revision(getRevision(cbor))
+ , isFinal(cbor[S_FINAL].toBool())
+ , isConstant(cbor[S_CONSTANT].toBool())
+ , isRequired(cbor[S_REQUIRED].toBool())
+{
+}
+
+Argument::Argument(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , type(toStringView(cbor, S_TYPE))
+{
+}
+
+Method::Method(const QCborMap &cbor, bool isConstructor)
+ : name(toStringView(cbor, S_NAME))
+ , returnType(toStringView(cbor, S_RETURN_TYPE))
+ , revision(getRevision(cbor))
+ , access(getAccess(cbor))
+ , isCloned(cbor[S_IS_CLONED].toBool())
+ , isJavaScriptFunction(cbor[S_IS_JAVASCRIPT_FUNCTION].toBool())
+ , isConstructor(isConstructor || cbor[S_IS_CONSTRUCTOR].toBool())
+{
+ const QCborArray args = cbor[S_ARGUMENTS].toArray();
+ for (const QCborValue &argument : args)
+ arguments.emplace_back(argument.toMap());
+
+ if (arguments.size() == 1) {
+ const QAnyStringView type = arguments[0].type;
+ if (type == "QQmlV4FunctionPtr"_L1 || type == "QQmlV4Function*"_L1)
+ isJavaScriptFunction = true;
+ }
+}
+
+Enum::Enum(const QCborMap &cbor)
+ : name(toStringView(cbor, S_NAME))
+ , alias(toStringView(cbor, S_ALIAS))
+ , type(toStringView(cbor, S_TYPE))
+ , isFlag(cbor[S_IS_FLAG].toBool())
+ , isClass(cbor[S_IS_CLASS].toBool())
+{
+ const QCborArray vals = cbor[S_VALUES].toArray();
+ for (const QCborValue &value : vals)
+ values.emplace_back(toStringView(value));
+}
+
+MetaTypePrivate::MetaTypePrivate(const QCborMap &cbor, const QString &inputFile)
+ : cbor(cbor)
+ , inputFile(inputFile)
+{
+ className = toStringView(cbor, S_CLASS_NAME);
+ qualifiedClassName = toStringView(cbor, S_QUALIFIED_CLASS_NAME);
+
+ const QCborArray cborSuperClasses = cbor[S_SUPER_CLASSES].toArray();
+ for (const QCborValue &superClass : cborSuperClasses)
+ superClasses.emplace_back(superClass.toMap());
+
+ const QCborArray cborClassInfos = cbor[S_CLASS_INFOS].toArray();
+ for (const QCborValue &classInfo : cborClassInfos)
+ classInfos.emplace_back(classInfo.toMap());
+
+ const QCborArray cborIfaces = cbor[S_INTERFACES].toArray();
+ for (const QCborValue &iface : cborIfaces)
+ ifaces.emplace_back(iface);
+
+ const QCborArray cborProperties = cbor[S_PROPERTIES].toArray();
+ for (const QCborValue &property : cborProperties)
+ properties.emplace_back(property.toMap());
+
+ for (const QCborArray &cborMethods : { cbor[S_METHODS].toArray(), cbor[S_SLOTS].toArray() }) {
+ for (const QCborValue &method : cborMethods)
+ methods.emplace_back(method.toMap(), false);
}
+
+ const QCborArray cborSigs = cbor[S_SIGNALS].toArray();
+ for (const QCborValue &sig : cborSigs)
+ sigs.emplace_back(sig.toMap(), false);
+
+ const QCborArray cborConstructors = cbor[S_CONSTRUCTORS].toArray();
+ for (const QCborValue &constructor : cborConstructors)
+ constructors.emplace_back(constructor.toMap(), true);
+
+ const QCborArray cborEnums = cbor[S_ENUMS].toArray();
+ for (const QCborValue &enumerator : cborEnums)
+ enums.emplace_back(enumerator.toMap());
+
+ if (cbor[S_GADGET].toBool())
+ kind = Kind::Gadget;
+ else if (cbor[S_OBJECT].toBool())
+ kind = Kind::Object;
+ else if (cbor[S_NAMESPACE].toBool())
+ kind = Kind::Namespace;
}
+MetaType::MetaType(const QCborMap &cbor, const QString &inputFile)
+ : d(s_pool.emplace_back(std::make_unique<MetaTypePrivate>(cbor, inputFile)).get())
+{}
+
QT_END_NAMESPACE
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
index 544f472383..9d86665c52 100644
--- a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
@@ -15,29 +15,219 @@
// We mean it.
//
-#include "qqmltypesclassdescription_p.h"
-
+#include <QtCore/qcbormap.h>
#include <QtCore/qstring.h>
+#include <QtCore/qtyperevision.h>
+#include <QtCore/qvarlengtharray.h>
#include <QtCore/qvector.h>
-#include <QtCore/qjsonobject.h>
QT_BEGIN_NAMESPACE
+// With all the QAnyStringViews in this file we rely on the Cbor data to stay
+// in place if you don't change the Cbor contents. We assume that Cbor data
+// is implicitly shared so that merely copying const Cbor objects does not copy
+// the contents.
+
+enum class Access { Public, Protected, Private };
+
+struct BaseType
+{
+ using Container = QVarLengthArray<BaseType, 1>;
+
+ BaseType() = default;
+ BaseType(const QCborMap &cbor);
+
+ QAnyStringView name;
+ Access access;
+};
+
+struct ClassInfo
+{
+ using Container = std::vector<ClassInfo>;
+
+ ClassInfo() = default;
+ ClassInfo(const QCborMap &cbor);
+
+ QAnyStringView name;
+ QAnyStringView value;
+};
+
+struct Interface
+{
+ using Container = QVarLengthArray<Interface, 1>;
+
+ Interface() = default;
+ Interface(const QCborValue &cbor);
+
+ QAnyStringView className;
+};
+
+struct Property
+{
+ using Container = std::vector<Property>;
+
+ Property() = default;
+ Property(const QCborMap &cbor);
+
+ QAnyStringView name;
+ QAnyStringView type;
+
+ QAnyStringView member;
+ QAnyStringView read;
+ QAnyStringView write;
+ QAnyStringView reset;
+ QAnyStringView notify;
+ QAnyStringView bindable;
+
+ QAnyStringView privateClass;
+
+ int index = -1;
+
+ QTypeRevision revision;
+
+ bool isFinal = false;
+ bool isConstant = false;
+ bool isRequired = false;
+};
+
+struct Argument
+{
+ using Container = std::vector<Argument>;
+
+ Argument() = default;
+ Argument(const QCborMap &cbor);
+
+ QAnyStringView name;
+ QAnyStringView type;
+};
+
+struct Method
+{
+ using Container = std::vector<Method>;
+
+ Method() = default;
+ Method(const QCborMap &cbor, bool isConstructor);
+
+ QAnyStringView name;
+
+ Argument::Container arguments;
+ QAnyStringView returnType;
+
+ QTypeRevision revision;
+
+ Access access = Access::Public;
+
+ bool isCloned = false;
+ bool isJavaScriptFunction = false;
+ bool isConstructor = false;
+};
+
+struct Enum
+{
+ using Container = std::vector<Enum>;
+
+ Enum() = default;
+ Enum(const QCborMap &cbor);
+
+ QAnyStringView name;
+ QAnyStringView alias;
+ QAnyStringView type;
+
+ QList<QAnyStringView> values;
+
+ bool isFlag = false;
+ bool isClass = false;
+};
+
+struct MetaTypePrivate
+{
+ Q_DISABLE_COPY_MOVE(MetaTypePrivate)
+
+ enum Kind : quint8 { Object, Gadget, Namespace, Unknown };
+
+ MetaTypePrivate() = default;
+ MetaTypePrivate(const QCborMap &cbor, const QString &inputFile);
+
+ const QCborMap cbor; // need to keep this to hold on to the strings
+ const QString inputFile;
+
+ QAnyStringView className;
+ QAnyStringView qualifiedClassName;
+ BaseType::Container superClasses;
+ ClassInfo::Container classInfos;
+ Interface::Container ifaces;
+
+ Property::Container properties;
+
+ Method::Container methods;
+ Method::Container sigs;
+ Method::Container constructors;
+
+ Enum::Container enums;
+
+ Kind kind = Unknown;
+};
+
+class MetaType
+{
+public:
+ using Kind = MetaTypePrivate::Kind;
+
+ MetaType() = default;
+ MetaType(const QCborMap &cbor, const QString &inputFile);
+
+ bool isEmpty() const { return d == &s_empty; }
+
+ QString inputFile() const { return d->inputFile; }
+ QAnyStringView className() const { return d->className; }
+ QAnyStringView qualifiedClassName() const { return d->qualifiedClassName; }
+ const BaseType::Container &superClasses() const { return d->superClasses; }
+ const ClassInfo::Container &classInfos() const { return d->classInfos; }
+ const Interface::Container &ifaces() const { return d->ifaces; }
+
+ const Property::Container &properties() const { return d->properties; }
+ const Method::Container &methods() const { return d->methods; }
+ const Method::Container &sigs() const { return d->sigs; }
+ const Method::Container &constructors() const { return d->constructors; }
+
+ const Enum::Container &enums() const { return d->enums; }
+
+ Kind kind() const { return d->kind; }
+
+private:
+ friend bool operator==(const MetaType &a, const MetaType &b) noexcept
+ {
+ return a.d == b.d;
+ }
+
+ friend bool operator!=(const MetaType &a, const MetaType &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ static const MetaTypePrivate s_empty;
+ const MetaTypePrivate *d = &s_empty;
+};
+
class MetaTypesJsonProcessor
{
public:
+ static QList<QAnyStringView> namespaces(const MetaType &classDef);
+
MetaTypesJsonProcessor(bool privateIncludes) : m_privateIncludes(privateIncludes) {}
bool processTypes(const QStringList &files);
+
+ bool processForeignTypes(const QString &foreignTypesFile);
bool processForeignTypes(const QStringList &foreignTypesFiles);
void postProcessTypes();
void postProcessForeignTypes();
- QVector<QJsonObject> types() const { return m_types; }
- QVector<QJsonObject> foreignTypes() const { return m_foreignTypes; }
- QStringList referencedTypes() const { return m_referencedTypes; }
- QStringList includes() const { return m_includes; }
+ QVector<MetaType> types() const { return m_types; }
+ QVector<MetaType> foreignTypes() const { return m_foreignTypes; }
+ QList<QAnyStringView> referencedTypes() const { return m_referencedTypes; }
+ QList<QString> includes() const { return m_includes; }
QString extractRegisteredTypes() const;
@@ -49,20 +239,39 @@ private:
NamespaceRegistration
};
- static RegistrationMode qmlTypeRegistrationMode(const QJsonObject &classDef);
+ struct PreProcessResult {
+ QList<QAnyStringView> primitiveAliases;
+ QAnyStringView foreignPrimitive;
+ RegistrationMode mode;
+ };
+
+ struct PotentialPrimitiveType {
+ QAnyStringView name;
+ QString file;
+ };
+
+ enum class PopulateMode { No, Yes };
+ static PreProcessResult preProcess(const MetaType &classDef, PopulateMode populateMode);
void addRelatedTypes();
- void sortTypes(QVector<QJsonObject> &types);
- QString resolvedInclude(const QString &include);
- void processTypes(const QJsonObject &types);
- void processForeignTypes(const QJsonObject &types);
+ void sortTypes(QVector<MetaType> &types);
+ QString resolvedInclude(QAnyStringView include);
+ void processTypes(const QCborMap &types);
+ void processForeignTypes(const QCborMap &types);
- QStringList m_includes;
- QStringList m_referencedTypes;
- QVector<QJsonObject> m_types;
- QVector<QJsonObject> m_foreignTypes;
+ bool isPrimitive(QAnyStringView type) const
+ {
+ return std::binary_search(m_primitiveTypes.begin(), m_primitiveTypes.end(), type);
+ }
+
+ QList<QString> m_includes;
+ QList<QAnyStringView> m_referencedTypes;
+ QList<QAnyStringView> m_primitiveTypes;
+ QVector<MetaType> m_types;
+ QVector<MetaType> m_foreignTypes;
bool m_privateIncludes = false;
};
+
QT_END_NAMESPACE
#endif // METATYPESJSONPROCESSOR_P_H
diff --git a/src/qmltyperegistrar/qqmljsstreamwriter.cpp b/src/qmltyperegistrar/qqmljsstreamwriter.cpp
index e435b8df92..32ca4e3b1b 100644
--- a/src/qmltyperegistrar/qqmljsstreamwriter.cpp
+++ b/src/qmltyperegistrar/qqmljsstreamwriter.cpp
@@ -2,12 +2,39 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qqmljsstreamwriter_p.h"
+#include "qanystringviewutils_p.h"
#include <QtCore/QBuffer>
#include <QtCore/QStringList>
QT_BEGIN_NAMESPACE
+// TODO: All of this could be improved by using and re-using one buffer for all the writing.
+// We don't really need to allocate any temporary byte arrays.
+
+static QByteArray enquoteByteArray(QByteArrayView string)
+{
+ const qsizetype length = string.length();
+ QByteArray buffer;
+ buffer.reserve(length + 2);
+ buffer.append(' ');
+ buffer.append(string);
+ buffer.append(' ');
+ buffer.replace('\\', "\\\\").replace('"', "\\\"");
+ buffer[0] = '"';
+ buffer[buffer.length() - 1] = '"';
+ return buffer;
+}
+
+static QByteArray enquoteAnyString(QAnyStringView string)
+{
+ return string.visit([](auto data) {
+ return QAnyStringViewUtils::processAsUtf8(data, [](QByteArrayView view) {
+ return enquoteByteArray(view);
+ });
+ });
+}
+
QQmlJSStreamWriter::QQmlJSStreamWriter(QByteArray *array)
: m_indentDepth(0)
, m_pendingLineLength(0)
@@ -25,19 +52,28 @@ void QQmlJSStreamWriter::writeEndDocument()
{
}
-void QQmlJSStreamWriter::writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as)
+void QQmlJSStreamWriter::writeLibraryImport(
+ QByteArrayView uri, int majorVersion, int minorVersion, QByteArrayView as)
{
- m_stream->write(QString::fromLatin1("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8());
- if (!as.isEmpty())
- m_stream->write(QString::fromLatin1(" as %1").arg(as).toUtf8());
+ m_stream->write("import ");
+ m_stream->write(uri.data(), uri.length());
+ m_stream->write(" ");
+ m_stream->write(QByteArray::number(majorVersion));
+ m_stream->write(".");
+ m_stream->write(QByteArray::number(minorVersion));
+ if (!as.isEmpty()) {
+ m_stream->write(" as ");
+ m_stream->write(as.data(), as.length());
+ }
m_stream->write("\n");
}
-void QQmlJSStreamWriter::writeStartObject(const QString &component)
+void QQmlJSStreamWriter::writeStartObject(QByteArrayView component)
{
flushPotentialLinesWithNewlines();
writeIndent();
- m_stream->write(QString::fromLatin1("%1 {").arg(component).toUtf8());
+ m_stream->write(component.data(), component.length());
+ m_stream->write(" {");
++m_indentDepth;
m_maybeOneline = true;
}
@@ -69,41 +105,64 @@ void QQmlJSStreamWriter::writeEndObject()
}
}
-void QQmlJSStreamWriter::writeScriptBinding(const QString &name, const QString &rhs)
+void QQmlJSStreamWriter::writeScriptBinding(QByteArrayView name, QByteArrayView rhs)
+{
+ QByteArray buffer;
+ buffer.reserve(name.length() + 2 + rhs.length());
+ buffer.append(name);
+ buffer.append(": ");
+ buffer.append(rhs);
+ writePotentialLine(buffer);
+}
+
+void QQmlJSStreamWriter::writeStringBinding(QByteArrayView name, QAnyStringView value)
{
- writePotentialLine(QString::fromLatin1("%1: %2").arg(name, rhs).toUtf8());
+ writeScriptBinding(name, enquoteAnyString(value));
}
-void QQmlJSStreamWriter::writeBooleanBinding(const QString &name, bool value)
+void QQmlJSStreamWriter::writeNumberBinding(QByteArrayView name, qint64 value)
{
- writeScriptBinding(name, value ? QLatin1String("true") : QLatin1String("false"));
+ writeScriptBinding(name, QByteArray::number(value));
}
-void QQmlJSStreamWriter::writeArrayBinding(const QString &name, const QStringList &elements)
+void QQmlJSStreamWriter::writeBooleanBinding(QByteArrayView name, bool value)
+{
+ writeScriptBinding(name, value ? "true" : "false");
+}
+
+template<typename String, typename ElementHandler>
+void QQmlJSStreamWriter::doWriteArrayBinding(
+ QByteArrayView name, const QList<String> &elements, ElementHandler &&handler)
{
flushPotentialLinesWithNewlines();
writeIndent();
// try to use a single line
- QString singleLine;
- singleLine += QString::fromLatin1("%1: [").arg(name);
+ QByteArray singleLine(name.data(), name.length());
+ singleLine += ": [";
for (int i = 0; i < elements.size(); ++i) {
- singleLine += elements.at(i);
+ QAnyStringViewUtils::processAsUtf8(elements.at(i), [&](QByteArrayView element) {
+ singleLine += handler(element);
+ });
if (i != elements.size() - 1)
- singleLine += QLatin1String(", ");
+ singleLine += ", ";
}
- singleLine += QLatin1String("]\n");
+ singleLine += "]\n";
if (singleLine.size() + m_indentDepth * 4 < 80) {
- m_stream->write(singleLine.toUtf8());
+ m_stream->write(singleLine);
return;
}
// write multi-line
- m_stream->write(QString::fromLatin1("%1: [\n").arg(name).toUtf8());
+ m_stream->write(name.data(), name.length());
+ m_stream->write(": [\n");
++m_indentDepth;
for (int i = 0; i < elements.size(); ++i) {
writeIndent();
- m_stream->write(elements.at(i).toUtf8());
+ QAnyStringViewUtils::processAsUtf8(elements.at(i), [&](QByteArrayView element) {
+ const auto handled = handler(element);
+ m_stream->write(handled.data(), handled.length());
+ });
if (i != elements.size() - 1) {
m_stream->write(",\n");
} else {
@@ -115,28 +174,41 @@ void QQmlJSStreamWriter::writeArrayBinding(const QString &name, const QStringLis
m_stream->write("]\n");
}
-void QQmlJSStreamWriter::write(const QString &data)
+void QQmlJSStreamWriter::writeArrayBinding(QByteArrayView name, const QByteArrayList &elements)
+{
+ doWriteArrayBinding(name, elements, [](QByteArrayView view) { return view; });
+}
+
+void QQmlJSStreamWriter::writeStringListBinding(
+ QByteArrayView name, const QList<QAnyStringView> &elements)
+{
+ doWriteArrayBinding(name, elements, enquoteByteArray);
+}
+
+void QQmlJSStreamWriter::write(QByteArrayView data)
{
flushPotentialLinesWithNewlines();
- m_stream->write(data.toUtf8());
+ m_stream->write(data.data(), data.length());
}
-void QQmlJSStreamWriter::writeScriptObjectLiteralBinding(const QString &name, const QList<QPair<QString, QString> > &keyValue)
+void QQmlJSStreamWriter::writeEnumObjectLiteralBinding(
+ QByteArrayView name, const QList<QPair<QAnyStringView, int> > &keyValue)
{
flushPotentialLinesWithNewlines();
writeIndent();
- m_stream->write(QString::fromLatin1("%1: {\n").arg(name).toUtf8());
+ m_stream->write(name.data(), name.length());
+ m_stream->write(": {\n");
++m_indentDepth;
- for (int i = 0; i < keyValue.size(); ++i) {
- const QString key = keyValue.at(i).first;
- const QString value = keyValue.at(i).second;
+ for (int i = 0, end = keyValue.size(); i != end; ++i) {
writeIndent();
- m_stream->write(QString::fromLatin1("%1: %2").arg(key, value).toUtf8());
- if (i != keyValue.size() - 1) {
+ const auto &entry = keyValue[i];
+ m_stream->write(enquoteAnyString(entry.first));
+ m_stream->write(": ");
+ m_stream->write(QByteArray::number(entry.second));
+ if (i != end - 1)
m_stream->write(",\n");
- } else {
+ else
m_stream->write("\n");
- }
}
--m_indentDepth;
writeIndent();
@@ -145,7 +217,8 @@ void QQmlJSStreamWriter::writeScriptObjectLiteralBinding(const QString &name, co
void QQmlJSStreamWriter::writeIndent()
{
- m_stream->write(QByteArray(m_indentDepth * 4, ' '));
+ for (int i = 0; i < m_indentDepth; ++i)
+ m_stream->write(" ");
}
void QQmlJSStreamWriter::writePotentialLine(const QByteArray &line)
diff --git a/src/qmltyperegistrar/qqmljsstreamwriter_p.h b/src/qmltyperegistrar/qqmljsstreamwriter_p.h
index d0984b8ca9..ef961de69c 100644
--- a/src/qmltyperegistrar/qqmljsstreamwriter_p.h
+++ b/src/qmltyperegistrar/qqmljsstreamwriter_p.h
@@ -29,21 +29,34 @@ public:
void writeStartDocument();
void writeEndDocument();
- void writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as = QString());
- //void writeFilesystemImport(const QString &file, const QString &as = QString());
- void writeStartObject(const QString &component);
+ void writeLibraryImport(
+ QByteArrayView uri, int majorVersion, int minorVersion, QByteArrayView as = {});
+ void writeStartObject(QByteArrayView component);
void writeEndObject();
- void writeScriptBinding(const QString &name, const QString &rhs);
- void writeScriptObjectLiteralBinding(const QString &name, const QList<QPair<QString, QString> > &keyValue);
- void writeArrayBinding(const QString &name, const QStringList &elements);
- void write(const QString &data);
- void writeBooleanBinding(const QString &name, bool value);
+ void writeScriptBinding(QByteArrayView name, QByteArrayView rhs);
+ void writeStringBinding(QByteArrayView name, QAnyStringView value);
+ void writeNumberBinding(QByteArrayView name, qint64 value);
+
+ // TODO: Drop this once we can drop qmlplugindump. It is substantially weird.
+ void writeEnumObjectLiteralBinding(
+ QByteArrayView name, const QList<QPair<QAnyStringView, int>> &keyValue);
+
+ // TODO: these would look better with generator functions.
+ void writeArrayBinding(QByteArrayView name, const QByteArrayList &elements);
+ void writeStringListBinding(QByteArrayView name, const QList<QAnyStringView> &elements);
+
+ void write(QByteArrayView data);
+ void writeBooleanBinding(QByteArrayView name, bool value);
private:
void writeIndent();
void writePotentialLine(const QByteArray &line);
void flushPotentialLinesWithNewlines();
+ template<typename String, typename ElementHandler>
+ void doWriteArrayBinding(
+ QByteArrayView name, const QList<String> &elements, ElementHandler &&handler);
+
int m_indentDepth;
QList<QByteArray> m_pendingLines;
int m_pendingLineLength;
diff --git a/src/qmltyperegistrar/qqmltyperegistrar.cpp b/src/qmltyperegistrar/qqmltyperegistrar.cpp
index 43cf5ece2e..058a3180b0 100644
--- a/src/qmltyperegistrar/qqmltyperegistrar.cpp
+++ b/src/qmltyperegistrar/qqmltyperegistrar.cpp
@@ -2,17 +2,27 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QFile>
-#include <QJsonArray>
-#include <QJsonValue>
+#include <QCborArray>
+#include <QCborValue>
#include "qqmltyperegistrar_p.h"
#include "qqmltypescreator_p.h"
+#include "qanystringviewutils_p.h"
+#include "qqmltyperegistrarconstants_p.h"
+#include "qqmltyperegistrarutils_p.h"
+
+#include <algorithm>
QT_BEGIN_NAMESPACE
using namespace Qt::Literals;
+using namespace Constants;
+using namespace Constants::MetatypesDotJson;
+using namespace Constants::MetatypesDotJson::Qml;
+using namespace QAnyStringViewUtils;
struct ExclusiveVersionRange
{
+ QAnyStringView fileName;
QString claimerName;
QTypeRevision addedIn;
QTypeRevision removedIn;
@@ -51,12 +61,12 @@ bool QmlTypeRegistrar::argumentsFromCommandLineAndFile(QStringList &allArguments
QString optionsFile = argument;
optionsFile.remove(0, 1);
if (optionsFile.isEmpty()) {
- fprintf(stderr, "The @ option requires an input file");
+ warning(optionsFile) << "The @ option requires an input file";
return false;
}
QFile f(optionsFile);
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- fprintf(stderr, "Cannot open options file specified with @");
+ warning(optionsFile) << "Cannot open options file specified with @";
return false;
}
while (!f.atEnd()) {
@@ -74,56 +84,62 @@ bool QmlTypeRegistrar::argumentsFromCommandLineAndFile(QStringList &allArguments
int QmlTypeRegistrar::runExtract(const QString &baseName, const MetaTypesJsonProcessor &processor)
{
if (processor.types().isEmpty()) {
- fprintf(stderr, "Error: No types to register found in library\n");
+ error(baseName) << "No types to register found in library";
return EXIT_FAILURE;
}
QFile headerFile(baseName + u".h");
bool ok = headerFile.open(QFile::WriteOnly);
if (!ok) {
- fprintf(stderr, "Error: Cannot open %s for writing\n", qPrintable(headerFile.fileName()));
+ error(headerFile.fileName()) << "Cannot open header file for writing";
return EXIT_FAILURE;
}
+
+ QString includeGuard = baseName;
+ static const QRegularExpression nonAlNum(QLatin1String("[^a-zA-Z0-9_]"));
+ includeGuard.replace(nonAlNum, QLatin1String("_"));
+
auto prefix = QString::fromLatin1(
"#ifndef %1_H\n"
"#define %1_H\n"
"#include <QtQml/qqml.h>\n"
- "#include <QtQml/qqmlmoduleregistration.h>\n").arg(baseName.toUpper());
- const QStringList includes = processor.includes();
+ "#include <QtQml/qqmlmoduleregistration.h>\n").arg(includeGuard);
+ const QList<QString> includes = processor.includes();
for (const QString &include: includes)
prefix += u"\n#include <%1>"_s.arg(include);
- headerFile.write((prefix + processor.extractRegisteredTypes()).toUtf8() + "\n#endif");
+ headerFile.write((prefix + processor.extractRegisteredTypes()).toUtf8() + "\n#endif\n");
QFile sourceFile(baseName + u".cpp");
ok = sourceFile.open(QFile::WriteOnly);
if (!ok) {
- fprintf(stderr, "Error: Cannot open %s for writing\n", qPrintable(sourceFile.fileName()));
+ error(sourceFile.fileName()) << "Cannot open implementation file for writing";
return EXIT_FAILURE;
}
// the string split is necessaury because cmake's automoc scanner would otherwise pick up the include
QString code = u"#include \"%1.h\"\n#include "_s.arg(baseName);
code += uR"("moc_%1.cpp")"_s.arg(baseName);
sourceFile.write(code.toUtf8());
+ sourceFile.write("\n");
return EXIT_SUCCESS;
}
-QJsonValue QmlTypeRegistrar::findType(const QString &name) const
+MetaType QmlTypeRegistrar::findType(QAnyStringView name) const
{
- for (const QJsonObject &type : m_types) {
- if (type[QLatin1String("qualifiedClassName")] != name)
+ for (const MetaType &type : m_types) {
+ if (type.qualifiedClassName() != name)
continue;
return type;
}
- return QJsonValue();
+ return MetaType();
};
-QJsonValue QmlTypeRegistrar::findTypeForeign(const QString &name) const
+MetaType QmlTypeRegistrar::findTypeForeign(QAnyStringView name) const
{
- for (const QJsonObject &type : m_foreignTypes) {
- if (type[QLatin1String("qualifiedClassName")] != name)
+ for (const MetaType &type : m_foreignTypes) {
+ if (type.qualifiedClassName() != name)
continue;
return type;
}
- return QJsonValue();
+ return MetaType();
};
QString conflictingVersionToString(const ExclusiveVersionRange &r)
@@ -141,7 +157,18 @@ QString conflictingVersionToString(const ExclusiveVersionRange &r)
return s;
};
-void QmlTypeRegistrar::write(QTextStream &output)
+// Return a name for the registration variable containing the module to
+// avoid clashes in Unity builds.
+static QString registrationVarName(const QString &module)
+{
+ auto specialCharPred = [](QChar c) { return !c.isLetterOrNumber(); };
+ QString result = module;
+ result[0] = result.at(0).toLower();
+ result.erase(std::remove_if(result.begin(), result.end(), specialCharPred), result.end());
+ return result + "Registration"_L1;
+}
+
+void QmlTypeRegistrar::write(QTextStream &output, QAnyStringView outFileName) const
{
output << uR"(/****************************************************************************
** Generated QML type registration code
@@ -161,7 +188,8 @@ void QmlTypeRegistrar::write(QTextStream &output)
// Keep this in sync with _qt_internal_get_escaped_uri in CMake
QString moduleAsSymbol = m_module;
- moduleAsSymbol.replace(QRegularExpression(QStringLiteral("[^A-Za-z0-9]")), QStringLiteral("_"));
+ static const QRegularExpression nonAlnumRegexp(QLatin1String("[^A-Za-z0-9]"));
+ moduleAsSymbol.replace(nonAlnumRegexp, QStringLiteral("_"));
QString underscoredModuleAsSymbol = m_module;
underscoredModuleAsSymbol.replace(QLatin1Char('.'), QLatin1Char('_'));
@@ -169,7 +197,7 @@ void QmlTypeRegistrar::write(QTextStream &output)
if (underscoredModuleAsSymbol != moduleAsSymbol
|| underscoredModuleAsSymbol.isEmpty()
|| underscoredModuleAsSymbol.front().isDigit()) {
- qWarning() << m_module << "is an invalid QML module URI. You cannot import this.";
+ warning(outFileName) << m_module << "is an invalid QML module URI. You cannot import this.";
}
const QString functionName = QStringLiteral("qml_register_types_") + moduleAsSymbol;
@@ -201,73 +229,122 @@ void QmlTypeRegistrar::write(QTextStream &output)
.arg(majorVersion);
}
- QVector<QString> typesRegisteredAnonymously;
+ QVector<QAnyStringView> typesRegisteredAnonymously;
+
+ const auto fillTypesRegisteredAnonymously = [&](const auto &members, QAnyStringView typeName) {
+ bool foundRevisionEntry = false;
+ for (const auto &entry : members) {
+ if (entry.revision.isValid()) {
+ foundRevisionEntry = true;
+ break;
+ }
+ }
+
+ if (!foundRevisionEntry)
+ return false;
+
+ if (typesRegisteredAnonymously.contains(typeName))
+ return true;
+
+ typesRegisteredAnonymously.append(typeName);
+
+ if (m_followForeignVersioning) {
+ output << uR"(
+ qmlRegisterAnonymousTypesAndRevisions<%1>("%2", %3);)"_s.arg(typeName.toString(), m_module)
+ .arg(majorVersion);
+ return true;
+ }
+
+ for (const auto &version
+ : m_pastMajorVersions + decltype(m_pastMajorVersions){ majorVersion }) {
+ output << uR"(
+ qmlRegisterAnonymousType<%1, 254>("%2", %3);)"_s.arg(typeName.toString(), m_module)
+ .arg(version);
+ }
+
+ return true;
+ };
+
+
QHash<QString, QList<ExclusiveVersionRange>> qmlElementInfos;
- for (const QJsonObject &classDef : m_types) {
- const QString className = classDef[QLatin1String("qualifiedClassName")].toString();
+ for (const MetaType &classDef : std::as_const(m_types)) {
+ // Do not generate C++ registrations for JavaScript types.
+ if (classDef.inputFile().isEmpty())
+ continue;
+
+ QString className = classDef.qualifiedClassName().toString();
QString targetName = className;
- QString extendedName;
- bool seenQmlElement = false;
- QString qmlElementName;
+
+ // If either the foreign or the local part is a namespace we need to
+ // generate a namespace registration.
+ bool targetIsNamespace = classDef.kind() == MetaType::Kind::Namespace;
+
+ QAnyStringView extendedName;
+ QList<QString> qmlElementNames;
QTypeRevision addedIn;
QTypeRevision removedIn;
- const QJsonArray classInfos = classDef.value(QLatin1String("classInfos")).toArray();
- for (const QJsonValueConstRef v : classInfos) {
- const QString name = v[QStringLiteral("name")].toString();
- if (name == QStringLiteral("QML.Element")) {
- seenQmlElement = true;
- qmlElementName = v[QStringLiteral("value")].toString();
- } else if (name == QStringLiteral("QML.Foreign"))
- targetName = v[QLatin1String("value")].toString();
- else if (name == QStringLiteral("QML.Extended"))
- extendedName = v[QStringLiteral("value")].toString();
- else if (name == QStringLiteral("QML.AddedInVersion")) {
- int version = v[QStringLiteral("value")].toString().toInt();
+ for (const ClassInfo &v : classDef.classInfos()) {
+ const QAnyStringView name = v.name;
+ if (name == S_ELEMENT) {
+ qmlElementNames.append(v.value.toString());
+ } else if (name == S_FOREIGN) {
+ targetName = v.value.toString();
+ } else if (name == S_FOREIGN_IS_NAMESPACE) {
+ targetIsNamespace = targetIsNamespace || (v.value == S_TRUE);
+ } else if (name == S_EXTENDED) {
+ extendedName = v.value;
+ } else if (name == S_ADDED_IN_VERSION) {
+ int version = toInt(v.value);
addedIn = QTypeRevision::fromEncodedVersion(version);
- } else if (name == QStringLiteral("QML.RemovedInVersion")) {
- int version = v[QStringLiteral("value")].toString().toInt();
+ addedIn = handleInMinorVersion(addedIn, majorVersion);
+ } else if (name == S_REMOVED_IN_VERSION) {
+ int version = toInt(v.value);
removedIn = QTypeRevision::fromEncodedVersion(version);
+ removedIn = handleInMinorVersion(removedIn, majorVersion);
}
}
- if (seenQmlElement && qmlElementName != u"anonymous") {
- if (qmlElementName == u"auto")
+ for (QString qmlElementName : std::as_const(qmlElementNames)) {
+ if (qmlElementName == S_ANONYMOUS)
+ continue;
+ if (qmlElementName == S_AUTO)
qmlElementName = className;
- qmlElementInfos[qmlElementName].append({ className, addedIn, removedIn });
+ qmlElementInfos[qmlElementName].append({
+ classDef.inputFile(),
+ className,
+ addedIn,
+ removedIn
+ });
}
// We want all related metatypes to be registered by name, so that we can look them up
// without including the C++ headers. That's the reason for the QMetaType(foo).id() calls.
- if (classDef.value(QLatin1String("namespace")).toBool()) {
+ if (targetIsNamespace) {
// We need to figure out if the _target_ is a namespace. If not, it already has a
// QMetaType and we don't need to generate one.
QString targetTypeName = targetName;
- const auto targetIsNamespace = [&]() {
- if (className == targetName)
- return true;
- const QJsonObject *target = QmlTypesClassDescription::findType(m_types, targetName);
- if (!target)
- target = QmlTypesClassDescription::findType(m_foreignTypes, targetName);
+ const QList<QAnyStringView> namespaces
+ = MetaTypesJsonProcessor::namespaces(classDef);
- if (!target)
- return false;
+ const FoundType target = QmlTypesClassDescription::findType(
+ m_types, m_foreignTypes, targetName, namespaces);
- if (target->value(QStringLiteral("namespace")).toBool())
- return true;
+ if (!target.javaScript.isEmpty() && target.native.isEmpty())
+ warning(target.javaScript) << "JavaScript type cannot be used as namespace";
- if (target->value(QStringLiteral("object")).toBool())
- targetTypeName += QStringLiteral(" *");
+ if (target.native.kind() == MetaType::Kind::Object)
+ targetTypeName += " *"_L1;
- return false;
- };
-
- if (targetIsNamespace()) {
+ // If there is no foreign type, the local one is a namespace.
+ // Otherwise, only do metaTypeForNamespace if the target _metaobject_ is a namespace.
+ // Not if we merely consider it to be a namespace for QML purposes.
+ if (className == targetName || target.native.kind() == MetaType::Kind::Namespace) {
output << uR"(
{
Q_CONSTINIT static auto metaType = QQmlPrivate::metaTypeForNamespace(
@@ -276,14 +353,21 @@ void QmlTypeRegistrar::write(QTextStream &output)
QMetaType(&metaType).id();
})"_s.arg(targetName, targetTypeName);
} else {
+ Q_ASSERT(!targetTypeName.isEmpty());
output << u"\n QMetaType::fromType<%1>().id();"_s.arg(targetTypeName);
}
- auto metaObjectPointer = [](const QString &name) -> QString {
- return u'&' + name + QStringLiteral("::staticMetaObject");
+ auto metaObjectPointer = [](QAnyStringView name) -> QString {
+ QString result;
+ const QLatin1StringView staticMetaObject = "::staticMetaObject"_L1;
+ result.reserve(1 + name.length() + staticMetaObject.length());
+ result.append('&'_L1);
+ name.visit([&](auto view) { result.append(view); });
+ result.append(staticMetaObject);
+ return result;
};
- if (seenQmlElement) {
+ if (!qmlElementNames.isEmpty()) {
output << uR"(
qmlRegisterNamespaceAndRevisions(%1, "%2", %3, nullptr, %4, %5);)"_s
.arg(metaObjectPointer(targetName), m_module)
@@ -293,99 +377,65 @@ void QmlTypeRegistrar::write(QTextStream &output)
: metaObjectPointer(extendedName));
}
} else {
- if (seenQmlElement) {
- auto checkRevisions = [&](const QJsonArray &array, const QString &type) {
- for (auto it = array.constBegin(); it != array.constEnd(); ++it) {
- auto object = it->toObject();
- if (!object.contains(QLatin1String("revision")))
+ if (!qmlElementNames.isEmpty()) {
+ auto checkRevisions = [&](const auto &array, QLatin1StringView type) {
+ for (auto it = array.begin(); it != array.end(); ++it) {
+ if (!it->revision.isValid())
continue;
- QTypeRevision revision = QTypeRevision::fromEncodedVersion(object[QLatin1String("revision")].toInt());
+ QTypeRevision revision = it->revision;
if (m_moduleVersion < revision) {
- qWarning().noquote()
- << "Warning:" << className << "is trying to register" << type
- << object[QStringLiteral("name")].toString()
+ warning(classDef)
+ << className << "is trying to register" << type
+ << it->name
<< "with future version" << revision
<< "when module version is only" << m_moduleVersion;
}
}
};
- const QJsonArray methods = classDef[QLatin1String("methods")].toArray();
- const QJsonArray properties = classDef[QLatin1String("properties")].toArray();
+ const Method::Container methods = classDef.methods();
+ const Property::Container properties = classDef.properties();
if (m_moduleVersion.isValid()) {
- checkRevisions(properties, QLatin1String("property"));
- checkRevisions(methods, QLatin1String("method"));
+ checkRevisions(properties, S_PROPERTY);
+ checkRevisions(methods, S_METHOD);
}
output << uR"(
- qmlRegisterTypesAndRevisions<%1>("%2", %3);)"_s.arg(className, m_module)
- .arg(majorVersion);
+ qmlRegisterTypesAndRevisions<%1>("%2", %3);)"_s.arg(className, m_module).arg(majorVersion);
- const QJsonValue superClasses = classDef[QLatin1String("superClasses")];
+ const BaseType::Container superClasses = classDef.superClasses();
- if (superClasses.isArray()) {
- for (const QJsonValueRef object : superClasses.toArray()) {
- if (object[QStringLiteral("access")] != QStringLiteral("public"))
+ for (const BaseType &object : classDef.superClasses()) {
+ if (object.access != Access::Public)
continue;
- QString superClassName = object[QStringLiteral("name")].toString();
+ QAnyStringView superClassName = object.name;
- QVector<QString> classesToCheck;
+ QVector<QAnyStringView> classesToCheck;
- auto checkForRevisions = [&](const QString &typeName) -> void {
- auto type = findType(typeName);
+ auto checkForRevisions = [&](QAnyStringView typeName) -> void {
+ auto typeAsMap = findType(typeName);
- if (!type.isObject()) {
- type = findTypeForeign(typeName);
- if (!type.isObject())
+ if (typeAsMap.isEmpty()) {
+ typeAsMap = findTypeForeign(typeName);
+ if (typeAsMap.isEmpty())
return;
- for (const QString &section :
- { QStringLiteral("properties"), QStringLiteral("signals"),
- QStringLiteral("methods") }) {
- bool foundRevisionEntry = false;
- for (const QJsonValueRef entry : type[section].toArray()) {
- if (entry.toObject().contains(QStringLiteral("revision"))) {
- foundRevisionEntry = true;
- break;
- }
- }
- if (foundRevisionEntry) {
- if (typesRegisteredAnonymously.contains(typeName))
- break;
-
- typesRegisteredAnonymously.append(typeName);
-
- if (m_followForeignVersioning) {
- output << uR"(
- qmlRegisterAnonymousTypesAndRevisions<%1>("%2", %3);)"_s.arg(typeName, m_module)
- .arg(majorVersion);
- break;
- }
-
- for (const auto &version : m_pastMajorVersions
- + decltype(m_pastMajorVersions){
- majorVersion }) {
- output << uR"(
- qmlRegisterAnonymousType<%1, 254>("%2", %3);)"_s.arg(typeName, m_module)
- .arg(version);
- }
- break;
+ if (!fillTypesRegisteredAnonymously(
+ typeAsMap.properties(), typeName)) {
+ if (!fillTypesRegisteredAnonymously(
+ typeAsMap.sigs(), typeName)) {
+ fillTypesRegisteredAnonymously(
+ typeAsMap.methods(), typeName);
}
}
}
- const QJsonValue superClasses = type[QLatin1String("superClasses")];
-
- if (superClasses.isArray()) {
- for (const QJsonValueRef object : superClasses.toArray()) {
- if (object[QStringLiteral("access")]
- != QStringLiteral("public"))
- continue;
- classesToCheck << object[QStringLiteral("name")].toString();
- }
+ for (const BaseType &object : typeAsMap.superClasses()) {
+ if (object.access == Access::Public)
+ classesToCheck << object.name;
}
};
@@ -394,11 +444,11 @@ void QmlTypeRegistrar::write(QTextStream &output)
while (!classesToCheck.isEmpty())
checkForRevisions(classesToCheck.takeFirst());
}
- }
} else {
+ Q_ASSERT(!className.isEmpty());
output << uR"(
QMetaType::fromType<%1%2>().id();)"_s.arg(
- className, classDef.value(QLatin1String("object")).toBool() ? u" *" : u"");
+ className, classDef.kind() == MetaType::Kind::Object ? u" *" : u"");
}
}
}
@@ -427,36 +477,37 @@ void QmlTypeRegistrar::write(QTextStream &output)
[&](const auto &q) {
registeringCppClasses += u", %1"_s.arg(conflictingVersionToString(q));
});
- qWarning().noquote() << "Warning:" << qmlName
- << "was registered multiple times by following Cpp classes: "
- << registeringCppClasses;
+ warning(conflictingExportStartIt->fileName)
+ << qmlName << "is registered multiple times by the following C++ classes:"
+ << registeringCppClasses;
conflictingExportStartIt = conflictingExportEndIt;
}
}
+
output << uR"(
qmlRegisterModule("%1", %2, %3);
}
-static const QQmlModuleRegistration registration("%1", %4);
+static const QQmlModuleRegistration %5("%1", %4);
)"_s.arg(m_module)
.arg(majorVersion)
.arg(minorVersion)
- .arg(functionName);
+ .arg(functionName, registrationVarName(m_module));
if (!m_targetNamespace.isEmpty())
output << u"} // namespace %1\n"_s.arg(m_targetNamespace);
}
-void QmlTypeRegistrar::generatePluginTypes(const QString &pluginTypesFile)
+bool QmlTypeRegistrar::generatePluginTypes(const QString &pluginTypesFile)
{
QmlTypesCreator creator;
creator.setOwnTypes(m_types);
creator.setForeignTypes(m_foreignTypes);
creator.setReferencedTypes(m_referencedTypes);
- creator.setModule(m_module);
+ creator.setModule(m_module.toUtf8());
creator.setVersion(QTypeRevision::fromVersion(m_moduleVersion.majorVersion(), 0));
- creator.generate(pluginTypesFile);
+ return creator.generate(pluginTypesFile);
}
void QmlTypeRegistrar::setModuleNameAndNamespace(const QString &module,
@@ -477,13 +528,13 @@ void QmlTypeRegistrar::setIncludes(const QList<QString> &includes)
{
m_includes = includes;
}
-void QmlTypeRegistrar::setTypes(const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreignTypes)
+void QmlTypeRegistrar::setTypes(
+ const QVector<MetaType> &types, const QVector<MetaType> &foreignTypes)
{
m_types = types;
m_foreignTypes = foreignTypes;
}
-void QmlTypeRegistrar::setReferencedTypes(const QStringList &referencedTypes)
+void QmlTypeRegistrar::setReferencedTypes(const QList<QAnyStringView> &referencedTypes)
{
m_referencedTypes = referencedTypes;
}
diff --git a/src/qmltyperegistrar/qqmltyperegistrar_p.h b/src/qmltyperegistrar/qqmltyperegistrar_p.h
index a01b4f1e15..e0811f2ade 100644
--- a/src/qmltyperegistrar/qqmltyperegistrar_p.h
+++ b/src/qmltyperegistrar/qqmltyperegistrar_p.h
@@ -15,8 +15,8 @@
// We mean it.
//
-#include <QJsonObject>
-#include <QTypeRevision>
+#include <QtCore/qcbormap.h>
+#include <QtCore/qversionnumber.h>
#include <cstdlib>
@@ -30,24 +30,24 @@ class QmlTypeRegistrar
QString m_targetNamespace;
QTypeRevision m_moduleVersion;
QList<quint8> m_pastMajorVersions;
- QStringList m_includes;
+ QList<QString> m_includes;
bool m_followForeignVersioning = false;
- QVector<QJsonObject> m_types;
- QVector<QJsonObject> m_foreignTypes;
- QStringList m_referencedTypes;
+ QVector<MetaType> m_types;
+ QVector<MetaType> m_foreignTypes;
+ QList<QAnyStringView> m_referencedTypes;
- QJsonValue findType(const QString &name) const;
- QJsonValue findTypeForeign(const QString &name) const;
+ MetaType findType(QAnyStringView name) const;
+ MetaType findTypeForeign(QAnyStringView name) const;
public:
- void write(QTextStream &os);
- void generatePluginTypes(const QString &pluginTypesFile);
+ void write(QTextStream &os, QAnyStringView outFileName) const;
+ bool generatePluginTypes(const QString &pluginTypesFile);
void setModuleNameAndNamespace(const QString &module, const QString &targetNamespace);
void setModuleVersions(QTypeRevision moduleVersion, const QList<quint8> &pastMajorVersions,
bool followForeignVersioning);
void setIncludes(const QList<QString> &includes);
- void setTypes(const QVector<QJsonObject> &types, const QVector<QJsonObject> &foreignTypes);
- void setReferencedTypes(const QStringList &referencedTypes);
+ void setTypes(const QVector<MetaType> &types, const QVector<MetaType> &foreignTypes);
+ void setReferencedTypes(const QList<QAnyStringView> &referencedTypes);
static bool argumentsFromCommandLineAndFile(QStringList &allArguments,
const QStringList &arguments);
diff --git a/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h b/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h
new file mode 100644
index 0000000000..465cc23532
--- /dev/null
+++ b/src/qmltyperegistrar/qqmltyperegistrarconstants_p.h
@@ -0,0 +1,174 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLTYPEREGISTRARCONSTANTS_P_H
+#define QQMLTYPEREGISTRARCONSTANTS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qlatin1stringview.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Constants {
+
+// Strings that commonly occur in .qmltypes files.
+namespace DotQmltypes {
+static constexpr QLatin1StringView S_ACCESS_SEMANTICS { "accessSemantics" };
+static constexpr QLatin1StringView S_ALIAS { "alias" };
+static constexpr QLatin1StringView S_ALIASES { "aliases" };
+static constexpr QLatin1StringView S_ARGUMENTS { "arguments" };
+static constexpr QLatin1StringView S_ATTACHED_TYPE { "attachedType" };
+static constexpr QLatin1StringView S_BINDABLE { "bindable" };
+static constexpr QLatin1StringView S_COMPONENT { "Component" };
+static constexpr QLatin1StringView S_DEFAULT_PROPERTY { "defaultProperty" };
+static constexpr QLatin1StringView S_DEFERRED_NAMES { "deferredNames" };
+static constexpr QLatin1StringView S_ENUM { "Enum" };
+static constexpr QLatin1StringView S_EXPORTS { "exports" };
+static constexpr QLatin1StringView S_EXPORT_META_OBJECT_REVISIONS { "exportMetaObjectRevisions" };
+static constexpr QLatin1StringView S_EXTENSION { "extension" };
+static constexpr QLatin1StringView S_EXTENSION_IS_JAVA_SCRIPT { "extensionIsJavaScript" };
+static constexpr QLatin1StringView S_EXTENSION_IS_NAMESPACE { "extensionIsNamespace" };
+static constexpr QLatin1StringView S_FILE { "file" };
+static constexpr QLatin1StringView S_HAS_CUSTOM_PARSER { "hasCustomParser" };
+static constexpr QLatin1StringView S_IMMEDIATE_NAMES { "immediateNames" };
+static constexpr QLatin1StringView S_INDEX { "index" };
+static constexpr QLatin1StringView S_INTERFACES { "interfaces" };
+static constexpr QLatin1StringView S_IS_CLONED { "isCloned" };
+static constexpr QLatin1StringView S_IS_CONSTANT { "isConstant" };
+static constexpr QLatin1StringView S_IS_CONSTRUCTOR { "isConstructor" };
+static constexpr QLatin1StringView S_IS_CREATABLE { "isCreatable" };
+static constexpr QLatin1StringView S_IS_FINAL { "isFinal" };
+static constexpr QLatin1StringView S_IS_FLAG { "isFlag" };
+static constexpr QLatin1StringView S_IS_JAVASCRIPT_FUNCTION { "isJavaScriptFunction" };
+static constexpr QLatin1StringView S_IS_LIST { "isList" };
+static constexpr QLatin1StringView S_IS_POINTER { "isPointer" };
+static constexpr QLatin1StringView S_IS_READONLY { "isReadonly" };
+static constexpr QLatin1StringView S_IS_REQUIRED { "isRequired" };
+static constexpr QLatin1StringView S_IS_SCOPED { "isScoped" };
+static constexpr QLatin1StringView S_IS_SINGLETON { "isSingleton" };
+static constexpr QLatin1StringView S_IS_STRUCTURED { "isStructured" };
+static constexpr QLatin1StringView S_METHOD { "Method" };
+static constexpr QLatin1StringView S_MODULE { "Module" };
+static constexpr QLatin1StringView S_NAME { "name" };
+static constexpr QLatin1StringView S_NONE { "none" };
+static constexpr QLatin1StringView S_NOTIFY { "notify" };
+static constexpr QLatin1StringView S_PARAMETER { "Parameter" };
+static constexpr QLatin1StringView S_PARENT_PROPERTY { "parentProperty" };
+static constexpr QLatin1StringView S_PRIVATE_CLASS { "privateClass" };
+static constexpr QLatin1StringView S_PROPERTY { "Property" };
+static constexpr QLatin1StringView S_PROTOTYPE { "prototype" };
+static constexpr QLatin1StringView S_READ { "read" };
+static constexpr QLatin1StringView S_REFERENCE { "reference" };
+static constexpr QLatin1StringView S_RESET { "reset" };
+static constexpr QLatin1StringView S_REVISION { "revision" };
+static constexpr QLatin1StringView S_SEQUENCE { "sequence" };
+static constexpr QLatin1StringView S_SIGNAL { "Signal" };
+static constexpr QLatin1StringView S_TYPE { "type" };
+static constexpr QLatin1StringView S_VALUE { "value" };
+static constexpr QLatin1StringView S_VALUES { "values" };
+static constexpr QLatin1StringView S_VALUE_TYPE { "valueType" };
+static constexpr QLatin1StringView S_WRITE { "write" };
+}
+
+// Strings that commonly occur in metatypes.json files.
+namespace MetatypesDotJson {
+static constexpr QLatin1StringView S_ACCESS { "access" };
+static constexpr QLatin1StringView S_ALIAS { "alias" };
+static constexpr QLatin1StringView S_ANONYMOUS { "anonymous" };
+static constexpr QLatin1StringView S_ARGUMENTS { "arguments" };
+static constexpr QLatin1StringView S_AUTO { "auto" };
+static constexpr QLatin1StringView S_BINDABLE { "bindable" };
+static constexpr QLatin1StringView S_CLASSES { "classes" };
+static constexpr QLatin1StringView S_CLASS_INFOS { "classInfos" };
+static constexpr QLatin1StringView S_CLASS_NAME { "className" };
+static constexpr QLatin1StringView S_CONSTANT { "constant" };
+static constexpr QLatin1StringView S_CONSTRUCT { "construct" };
+static constexpr QLatin1StringView S_CONSTRUCTORS { "constructors" };
+static constexpr QLatin1StringView S_DEFAULT_PROPERTY { "DefaultProperty" };
+static constexpr QLatin1StringView S_DEFERRED_PROPERTY_NAMES { "DeferredPropertyNames" };
+static constexpr QLatin1StringView S_ENUMS { "enums" };
+static constexpr QLatin1StringView S_FALSE { "false" };
+static constexpr QLatin1StringView S_FINAL { "final" };
+static constexpr QLatin1StringView S_GADGET { "gadget" };
+static constexpr QLatin1StringView S_IMMEDIATE_PROPERTY_NAMES { "ImmediatePropertyNames" };
+static constexpr QLatin1StringView S_INDEX { "index" };
+static constexpr QLatin1StringView S_INPUT_FILE { "inputFile" };
+static constexpr QLatin1StringView S_INTERFACES { "interfaces" };
+static constexpr QLatin1StringView S_IS_CLASS { "isClass" };
+static constexpr QLatin1StringView S_IS_CLONED { "isCloned" };
+static constexpr QLatin1StringView S_IS_CONSTRUCTOR { "isConstructor" };
+static constexpr QLatin1StringView S_IS_FLAG { "isFlag" };
+static constexpr QLatin1StringView S_IS_JAVASCRIPT_FUNCTION { "isJavaScriptFunction" };
+static constexpr QLatin1StringView S_MEMBER { "member" };
+static constexpr QLatin1StringView S_METHOD { "method" };
+static constexpr QLatin1StringView S_METHODS { "methods" };
+static constexpr QLatin1StringView S_NAME { "name" };
+static constexpr QLatin1StringView S_NAMESPACE { "namespace" };
+static constexpr QLatin1StringView S_NOTIFY { "notify" };
+static constexpr QLatin1StringView S_OBJECT { "object" };
+static constexpr QLatin1StringView S_PARENT_PROPERTY { "ParentProperty" };
+static constexpr QLatin1StringView S_PRIVATE { "private" };
+static constexpr QLatin1StringView S_PRIVATE_CLASS { "privateClass" };
+static constexpr QLatin1StringView S_PROPERTIES { "properties" };
+static constexpr QLatin1StringView S_PROPERTY { "property" };
+static constexpr QLatin1StringView S_PROTECTED { "protected" };
+static constexpr QLatin1StringView S_PUBLIC { "public" };
+static constexpr QLatin1StringView S_QUALIFIED_CLASS_NAME { "qualifiedClassName" };
+static constexpr QLatin1StringView S_READ { "read" };
+
+static constexpr QLatin1StringView S_REGISTER_ENUM_CLASSES_UNSCOPED {
+ "RegisterEnumClassesUnscoped"
+};
+
+static constexpr QLatin1StringView S_REQUIRED { "required" };
+static constexpr QLatin1StringView S_RESET { "reset" };
+static constexpr QLatin1StringView S_RETURN_TYPE { "returnType" };
+static constexpr QLatin1StringView S_REVISION { "revision" };
+static constexpr QLatin1StringView S_SIGNALS { "signals" };
+static constexpr QLatin1StringView S_SLOTS { "slots" };
+static constexpr QLatin1StringView S_STRUCTURED { "structured" };
+static constexpr QLatin1StringView S_SUPER_CLASSES { "superClasses" };
+static constexpr QLatin1StringView S_TRUE { "true" };
+static constexpr QLatin1StringView S_TYPE { "type" };
+static constexpr QLatin1StringView S_VALUE { "value" };
+static constexpr QLatin1StringView S_VALUES { "values" };
+static constexpr QLatin1StringView S_WRITE { "write" };
+
+// QML-Related Strings that commonly occur in metatypes.json files.
+namespace Qml {
+static constexpr QLatin1StringView S_ADDED_IN_VERSION { "QML.AddedInVersion" };
+static constexpr QLatin1StringView S_ATTACHED { "QML.Attached" };
+static constexpr QLatin1StringView S_CREATABLE { "QML.Creatable" };
+static constexpr QLatin1StringView S_CREATION_METHOD { "QML.CreationMethod" };
+static constexpr QLatin1StringView S_ELEMENT { "QML.Element" };
+static constexpr QLatin1StringView S_EXTENDED { "QML.Extended" };
+static constexpr QLatin1StringView S_EXTENSION_IS_JAVA_SCRIPT { "QML.ExtensionIsJavaScript" };
+static constexpr QLatin1StringView S_EXTENSION_IS_NAMESPACE { "QML.ExtensionIsNamespace" };
+static constexpr QLatin1StringView S_FOREIGN { "QML.Foreign" };
+static constexpr QLatin1StringView S_FOREIGN_IS_NAMESPACE { "QML.ForeignIsNamespace" };
+static constexpr QLatin1StringView S_HAS_CUSTOM_PARSER { "QML.HasCustomParser" };
+static constexpr QLatin1StringView S_PRIMITIVE_ALIAS { "QML.PrimitiveAlias" };
+static constexpr QLatin1StringView S_REMOVED_IN_VERSION { "QML.RemovedInVersion" };
+static constexpr QLatin1StringView S_ROOT { "QML.Root" };
+static constexpr QLatin1StringView S_SEQUENCE { "QML.Sequence" };
+static constexpr QLatin1StringView S_SINGLETON { "QML.Singleton" };
+static constexpr QLatin1StringView S_UNCREATABLE_REASON { "QML.UncreatableReason" };
+} // namespace Qml
+
+} // namespace MetatypesJson
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPEREGISTRARCONSTANTS_P_H
diff --git a/src/qmltyperegistrar/qqmltyperegistrarutils.cpp b/src/qmltyperegistrar/qqmltyperegistrarutils.cpp
new file mode 100644
index 0000000000..f73686f3b4
--- /dev/null
+++ b/src/qmltyperegistrar/qqmltyperegistrarutils.cpp
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qqmltyperegistrarutils_p.h"
+
+#include "qanystringviewutils_p.h"
+#include "qqmltyperegistrarconstants_p.h"
+#include "qqmltyperegistrarconstants_p.h"
+#include "qmetatypesjsonprocessor_p.h"
+
+#include <QtCore/qcborarray.h>
+#include <QtCore/qcbormap.h>
+#include <QtCore/qcborvalue.h>
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+QTypeRevision handleInMinorVersion(QTypeRevision revision, int majorVersion)
+{
+ if (!revision.hasMajorVersion() && revision.hasMinorVersion()) {
+ // this version has been obtained by QML_{ADDED,REMOVED}_IN_MINOR_VERSION
+ revision = QTypeRevision::fromVersion(majorVersion, revision.minorVersion());
+ }
+ return revision;
+}
+
+QAnyStringView interfaceName(const Interface &iface)
+{
+ return iface.className;
+}
+
+static QDebug message(QDebug base, QAnyStringView message, QAnyStringView fileName, int lineNumber)
+{
+ const QString lineString = lineNumber ? QString::number(lineNumber) : QString();
+ return (base.noquote().nospace()
+ << message << ": " << fileName << ":" << lineString << ":").space();
+}
+
+QDebug warning(QAnyStringView fileName, int lineNumber)
+{
+ return message(qWarning(), "Warning", fileName, lineNumber);
+}
+
+QDebug warning(const MetaType &classDef)
+{
+ // TODO: Once we have line numbers, use them
+ const QAnyStringView file = classDef.inputFile();
+ if (!file.isEmpty())
+ return warning(file);
+
+ return warning(classDef.qualifiedClassName());
+}
+
+QDebug error(QAnyStringView fileName, int lineNumber)
+{
+ return message(qCritical(), "Error", fileName, lineNumber);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmltyperegistrar/qqmltyperegistrarutils_p.h b/src/qmltyperegistrar/qqmltyperegistrarutils_p.h
new file mode 100644
index 0000000000..79bd224a7a
--- /dev/null
+++ b/src/qmltyperegistrar/qqmltyperegistrarutils_p.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLTYPEREGISTRAR_UTILS_P_H
+#define QQMLTYPEREGISTRAR_UTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmetatypesjsonprocessor_p.h"
+
+#include <QtCore/qversionnumber.h>
+
+QT_BEGIN_NAMESPACE
+
+QTypeRevision handleInMinorVersion(QTypeRevision revision, int majorVersion);
+QAnyStringView interfaceName(const Interface &iface);
+
+QDebug warning(const MetaType &classDef);
+QDebug warning(QAnyStringView fileName, int lineNumber = 0);
+
+QDebug error(QAnyStringView fileName, int lineNumber = 0);
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPEREGISTRAR_UTILS_P_H
diff --git a/src/qmltyperegistrar/qqmltypesclassdescription.cpp b/src/qmltyperegistrar/qqmltypesclassdescription.cpp
index 62800dba73..7c42052a70 100644
--- a/src/qmltyperegistrar/qqmltypesclassdescription.cpp
+++ b/src/qmltyperegistrar/qqmltypesclassdescription.cpp
@@ -1,98 +1,188 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "qqmltyperegistrarutils_p.h"
#include "qqmltypesclassdescription_p.h"
-#include "qqmltypescreator_p.h"
-#include <QtCore/qjsonarray.h>
+#include "qanystringviewutils_p.h"
+#include "qmetatypesjsonprocessor_p.h"
+#include "qqmltyperegistrarconstants_p.h"
+#include <QtCore/qcborarray.h>
+#include <QtCore/qcbormap.h>
QT_BEGIN_NAMESPACE
-static void collectExtraVersions(const QJsonObject *component, const QString &key,
- QList<QTypeRevision> &extraVersions)
+using namespace Qt::StringLiterals;
+using namespace Constants;
+using namespace Constants::MetatypesDotJson;
+using namespace Constants::MetatypesDotJson::Qml;
+using namespace QAnyStringViewUtils;
+
+template<typename Container>
+static void collectExtraVersions(const Container &items, QList<QTypeRevision> &extraVersions)
{
- const QJsonArray &items = component->value(key).toArray();
- for (const QJsonValue item : items) {
- const QJsonObject obj = item.toObject();
- const auto revision = obj.find(QLatin1String("revision"));
- if (revision != obj.end()) {
- const auto extraVersion = QTypeRevision::fromEncodedVersion(revision.value().toInt());
- if (!extraVersions.contains(extraVersion))
- extraVersions.append(extraVersion);
+ for (const auto &obj : items) {
+ if (obj.revision.isValid() && !extraVersions.contains(obj.revision))
+ extraVersions.append(obj.revision);
+ }
+}
+
+struct Compare {
+ bool operator()(const QAnyStringView &typeName, const MetaType &type) const
+ {
+ return typeName < type.qualifiedClassName();
+ }
+
+ bool operator()(const MetaType &type, const QAnyStringView &typeName) const
+ {
+ return type.qualifiedClassName() < typeName;
+ }
+};
+
+FoundType::FoundType(const MetaType &single, FoundType::Origin origin)
+{
+ if (single.inputFile().isEmpty()) {
+ javaScript = single;
+ javaScriptOrigin = origin;
+ } else {
+ native = single;
+ nativeOrigin = origin;
+ }
+}
+
+MetaType FoundType::select(const MetaType &category, QAnyStringView relation) const
+{
+ if (category.inputFile().isEmpty()) {
+ if (javaScript.isEmpty()) {
+ warning(category)
+ << relation << "type of" << category.qualifiedClassName()
+ << "is not a JavaScript type";
}
+ return javaScript;
+ }
+
+ if (native.isEmpty()) {
+ warning(category)
+ << relation << "of" << category.qualifiedClassName()
+ << "is not a native type";
}
+ return native;
}
-const QJsonObject *QmlTypesClassDescription::findType(const QVector<QJsonObject> &types,
- const QString &name)
+FoundType QmlTypesClassDescription::findType(
+ const QVector<MetaType> &types, const QVector<MetaType> &foreign,
+ const QAnyStringView &name, const QList<QAnyStringView> &namespaces)
{
- static const QLatin1String qualifiedClassNameKey("qualifiedClassName");
- auto it = std::lower_bound(types.begin(), types.end(), name,
- [&](const QJsonObject &type, const QString &typeName) {
- return type.value(qualifiedClassNameKey).toString() < typeName;
- });
+ const auto tryFindType = [&](QAnyStringView qualifiedName) -> FoundType {
+ FoundType result;
+ for (const QVector<MetaType> &t : {types, foreign}) {
+ const auto [first, last] = std::equal_range(
+ t.begin(), t.end(), qualifiedName, Compare());
+ for (auto it = first; it != last; ++it) {
+ Q_ASSERT(it->qualifiedClassName() == qualifiedName);
+
+ if (it->inputFile().isEmpty()) {
+ if (result.javaScript.isEmpty()) {
+ result.javaScript = *it;
+ result.javaScriptOrigin = (&t == &types)
+ ? FoundType::OwnTypes
+ : FoundType::ForeignTypes;
+ } else {
+ warning(result.javaScript)
+ << "Multiple JavaScript types called" << qualifiedName << "found!";
+ }
+ } else if (result.native.isEmpty()) {
+ result.native = *it;
+ result.nativeOrigin = (&t == &types)
+ ? FoundType::OwnTypes
+ : FoundType::ForeignTypes;
+ } else {
+ warning(result.native)
+ << "Multiple C++ types called" << qualifiedName << "found!"
+ << "This violates the One Definition Rule!";
+ }
+ }
+ }
+
+ return result;
+ };
+
+ if (startsWith(name, QLatin1String("::")))
+ return tryFindType(name.mid(2));
- return (it != types.end() && it->value(qualifiedClassNameKey) == name) ? &(*it) : nullptr;
+ QString qualified;
+ for (int i = 0, end = namespaces.length(); i != end; ++i) {
+ for (int j = 0; j < end - i; ++j) {
+ namespaces[j].visit([&](auto data) { qualified.append(data); });
+ qualified.append(QLatin1String("::"));
+ }
+ name.visit([&](auto data) { qualified.append(data); });
+ if (const FoundType found = tryFindType(qualified))
+ return found;
+
+ qualified.truncate(0);
+ }
+
+ return tryFindType(name);
}
void QmlTypesClassDescription::collectSuperClasses(
- const QJsonObject *classDef, const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign, CollectMode mode, QTypeRevision defaultRevision)
+ const MetaType &classDef, const QVector<MetaType> &types,
+ const QVector<MetaType> &foreign, CollectMode mode, QTypeRevision defaultRevision)
{
- const auto supers = classDef->value(QLatin1String("superClasses")).toArray();
- for (const QJsonValue superValue : supers) {
- const QJsonObject superObject = superValue.toObject();
- if (superObject[QLatin1String("access")].toString() == QLatin1String("public")) {
- const QString superName = superObject[QLatin1String("name")].toString();
+ const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
+ for (const BaseType &superObject : std::as_const(classDef.superClasses())) {
+ if (superObject.access == Access::Public) {
+ const QAnyStringView superName = superObject.name;
const CollectMode superMode = (mode == TopLevel) ? SuperClass : RelatedType;
- if (const QJsonObject *other = findType(types, superName))
- collect(other, types, foreign, superMode, defaultRevision);
- else if (const QJsonObject *other = findType(foreign, superName))
+ if (const FoundType found = findType(types, foreign, superName, namespaces)) {
+ const MetaType other = found.select(classDef, "Base");
collect(other, types, foreign, superMode, defaultRevision);
- else // If we cannot locate a type for it, there is no point in recording the superClass
- continue;
+ if (mode == TopLevel && superClass.isEmpty())
+ superClass = other.qualifiedClassName();
+ }
- if (mode == TopLevel && superClass.isEmpty())
- superClass = superName;
+ // If we cannot locate a type for it, there is no point in recording the superClass
}
}
}
-void QmlTypesClassDescription::collectInterfaces(const QJsonObject *classDef)
+void QmlTypesClassDescription::collectInterfaces(const MetaType &classDef)
{
- if (classDef->contains(QLatin1String("interfaces"))) {
- const QJsonArray array = classDef->value(QLatin1String("interfaces")).toArray();
- for (const QJsonValue value : array) {
- auto object = value.toArray()[0].toObject();
- implementsInterfaces << object[QLatin1String("className")].toString();
- }
- }
+ for (const Interface &iface : classDef.ifaces())
+ implementsInterfaces << interfaceName(iface);
}
void QmlTypesClassDescription::collectLocalAnonymous(
- const QJsonObject *classDef, const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign, QTypeRevision defaultRevision)
+ const MetaType &classDef, const QVector<MetaType> &types,
+ const QVector<MetaType> &foreign, QTypeRevision defaultRevision)
{
- file = classDef->value(QLatin1String("inputFile")).toString();
+ file = classDef.inputFile();
resolvedClass = classDef;
- className = classDef->value(QLatin1String("qualifiedClassName")).toString();
-
- if (classDef->value(QStringLiteral("object")).toBool())
- accessSemantics = QStringLiteral("reference");
- else if (classDef->value(QStringLiteral("gadget")).toBool())
- accessSemantics = QStringLiteral("value");
- else
- accessSemantics = QStringLiteral("none");
-
- const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray();
- for (const QJsonValue classInfo : classInfos) {
- const QJsonObject obj = classInfo.toObject();
- if (obj[QStringLiteral("name")].toString() == QStringLiteral("DefaultProperty"))
- defaultProp = obj[QStringLiteral("value")].toString();
- if (obj[QStringLiteral("name")].toString() == QStringLiteral("ParentProperty"))
- parentProp = obj[QStringLiteral("value")].toString();
+ className = classDef.qualifiedClassName();
+
+ switch (classDef.kind()) {
+ case MetaType::Kind::Object:
+ accessSemantics = DotQmltypes::S_REFERENCE;
+ break;
+ case MetaType::Kind::Gadget:
+ accessSemantics = DotQmltypes::S_VALUE;
+ break;
+ case MetaType::Kind::Namespace:
+ case MetaType::Kind::Unknown:
+ accessSemantics = DotQmltypes::S_NONE;
+ break;
+ }
+
+ for (const ClassInfo &obj : classDef.classInfos()) {
+ if (obj.name == S_DEFAULT_PROPERTY)
+ defaultProp = obj.value;
+ else if (obj.name == S_PARENT_PROPERTY)
+ parentProp = obj.value;
+ else if (obj.name == S_REGISTER_ENUM_CLASSES_UNSCOPED && obj.value == S_FALSE)
+ registerEnumClassesScoped = true;
}
collectInterfaces(classDef);
@@ -100,147 +190,198 @@ void QmlTypesClassDescription::collectLocalAnonymous(
}
void QmlTypesClassDescription::collect(
- const QJsonObject *classDef, const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign, CollectMode mode, QTypeRevision defaultRevision)
+ const MetaType &classDef, const QVector<MetaType> &types,
+ const QVector<MetaType> &foreign, CollectMode mode, QTypeRevision defaultRevision)
{
if (file.isEmpty())
- file = classDef->value(QLatin1String("inputFile")).toString();
+ file = classDef.inputFile();
+
+ const QAnyStringView classDefName = classDef.className();
+ const QList<QAnyStringView> namespaces = MetaTypesJsonProcessor::namespaces(classDef);
- const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray();
- const QString classDefName = classDef->value(QLatin1String("className")).toString();
- QString foreignTypeName;
- for (const QJsonValue classInfo : classInfos) {
- const QJsonObject obj = classInfo.toObject();
- const QString name = obj[QLatin1String("name")].toString();
- const QString value = obj[QLatin1String("value")].toString();
+ QAnyStringView foreignTypeName;
+ bool foreignIsNamespace = false;
+ bool isConstructible = false;
+ for (const ClassInfo &obj : classDef.classInfos()) {
+ const QAnyStringView name = obj.name;
+ const QAnyStringView value = obj.value;
- if (name == QLatin1String("DefaultProperty")) {
+ if (name == S_DEFAULT_PROPERTY) {
if (mode != RelatedType && defaultProp.isEmpty())
defaultProp = value;
- } else if (name == QLatin1String("ParentProperty")) {
+ continue;
+ }
+
+ if (name == S_PARENT_PROPERTY) {
if (mode != RelatedType && parentProp.isEmpty())
parentProp = value;
- } else if (name == QLatin1String("QML.AddedInVersion")) {
- const QTypeRevision revision = QTypeRevision::fromEncodedVersion(value.toInt());
- if (mode == TopLevel) {
+ continue;
+ }
+
+ if (name == S_REGISTER_ENUM_CLASSES_UNSCOPED) {
+ if (mode != RelatedType && value == S_FALSE)
+ registerEnumClassesScoped = true;
+ continue;
+ }
+
+ if (name == S_ADDED_IN_VERSION) {
+ const QTypeRevision revision = handleInMinorVersion(
+ QTypeRevision::fromEncodedVersion(toInt(value)),
+ defaultRevision.majorVersion());
+ revisions.append(revision);
+ if (mode == TopLevel)
addedInRevision = revision;
- revisions.append(revision);
- } else if (!elementName.isEmpty()) {
- revisions.append(revision);
- }
+ continue;
}
if (mode != TopLevel)
continue;
+ if (name == S_REMOVED_IN_VERSION) {
+ removedInRevision = handleInMinorVersion(
+ QTypeRevision::fromEncodedVersion(toInt(value)),
+ defaultRevision.majorVersion());
+ continue;
+ }
+
// These only apply to the original class
- if (name == QLatin1String("QML.Element")) {
- if (value == QLatin1String("auto"))
- elementName = classDefName;
- else if (value != QLatin1String("anonymous"))
- elementName = value;
- } else if (name == QLatin1String("QML.RemovedInVersion")) {
- removedInRevision = QTypeRevision::fromEncodedVersion(value.toInt());
- } else if (name == QLatin1String("QML.Creatable")) {
- isCreatable = (value != QLatin1String("false"));
- } else if (name == QLatin1String("QML.Attached")) {
- attachedType = value;
- collectRelated(value, types, foreign, defaultRevision);
- } else if (name == QLatin1String("QML.Extended")) {
- extensionType = value;
- collectRelated(value, types, foreign, defaultRevision);
- } else if (name == QLatin1String("QML.ExtensionIsNamespace")) {
- if (value == QLatin1String("true"))
+ if (name == S_ELEMENT) {
+ if (value == S_AUTO)
+ elementNames.append(classDefName);
+ else if (value != S_ANONYMOUS)
+ elementNames.append(value);
+ } else if (name == S_CREATABLE) {
+ isCreatable = (value != S_FALSE);
+ } else if (name == S_CREATION_METHOD) {
+ isStructured = (value == S_STRUCTURED);
+ isConstructible = isStructured || (value == S_CONSTRUCT);
+ } else if (name == S_ATTACHED) {
+ if (const FoundType attached = collectRelated(
+ value, types, foreign, defaultRevision, namespaces)) {
+ attachedType = attached.select(classDef, "Attached").qualifiedClassName();
+ }
+ } else if (name == S_EXTENDED) {
+ if (const FoundType extension = collectRelated(
+ value, types, foreign, defaultRevision, namespaces)) {
+ javaScriptExtensionType = extension.javaScript.qualifiedClassName();
+ nativeExtensionType = extension.native.qualifiedClassName();
+ }
+ } else if (name == S_EXTENSION_IS_JAVA_SCRIPT) {
+ if (value == S_TRUE)
+ extensionIsJavaScript = true;
+ } else if (name == S_EXTENSION_IS_NAMESPACE) {
+ if (value == S_TRUE)
extensionIsNamespace = true;
- } else if (name == QLatin1String("QML.Sequence")) {
- sequenceValueType = value;
- collectRelated(value, types, foreign, defaultRevision);
- } else if (name == QLatin1String("QML.Singleton")) {
- if (value == QLatin1String("true"))
+ } else if (name == S_SEQUENCE) {
+ if (const FoundType element = collectRelated(
+ value, types, foreign, defaultRevision, namespaces)) {
+ sequenceValueType = element.select(classDef, "Sequence value").qualifiedClassName();
+ } else {
+ // TODO: get rid of this once we have JSON data for the builtins.
+ sequenceValueType = value;
+ }
+ } else if (name == S_SINGLETON) {
+ if (value == S_TRUE)
isSingleton = true;
- } else if (name == QLatin1String("QML.Foreign")) {
+ } else if (name == S_FOREIGN) {
foreignTypeName = value;
- } else if (name == QLatin1String("QML.OmitFromQmlTypes")) {
- if (value == QLatin1String("true"))
- omitFromQmlTypes = true;
- } else if (name == QLatin1String("QML.HasCustomParser")) {
- if (value == QLatin1String("true"))
+ } else if (name == S_FOREIGN_IS_NAMESPACE) {
+ foreignIsNamespace = (value == S_TRUE);
+ } else if (name == S_PRIMITIVE_ALIAS) {
+ primitiveAliases.append(value);
+ } else if (name == S_ROOT) {
+ isRootClass = (value == S_TRUE);
+ } else if (name == S_HAS_CUSTOM_PARSER) {
+ if (value == S_TRUE)
hasCustomParser = true;
- } else if (name == QLatin1String("DeferredPropertyNames")) {
- deferredNames = value.split(u',');
- } else if (name == QLatin1String("ImmediatePropertyNames")) {
- immediateNames = value.split(u',');
+ } else if (name == S_DEFERRED_PROPERTY_NAMES) {
+ deferredNames = split(value, QLatin1StringView(","));
+ } else if (name == S_IMMEDIATE_PROPERTY_NAMES) {
+ immediateNames = split(value, QLatin1StringView(","));
}
}
+ if (addedInRevision.isValid() && !elementNames.isEmpty())
+ revisions.append(addedInRevision);
+
// If the local type is a namespace the result can only be a namespace,
// no matter what the foreign type is.
- const bool isNamespace = classDef->value(QLatin1String("namespace")).toBool();
+ const bool isNamespace = foreignIsNamespace || classDef.kind() == MetaType::Kind::Namespace;
+ MetaType resolved = classDef;
if (!foreignTypeName.isEmpty()) {
- const QJsonObject *other = findType(foreign, foreignTypeName);
-
- // We can re-use a type with own QML_* macros as target of QML_FOREIGN
- if (!other)
- other = findType(types, foreignTypeName);
-
- if (other) {
- classDef = other;
-
- // Default properties are always local.
- defaultProp.clear();
-
- // Foreign type can have a default property or an attached types
- const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray();
- for (const QJsonValue classInfo : classInfos) {
- const QJsonObject obj = classInfo.toObject();
- const QString foreignName = obj[QLatin1String("name")].toString();
- const QString foreignValue = obj[QLatin1String("value")].toString();
- if (defaultProp.isEmpty() && foreignName == QLatin1String("DefaultProperty")) {
+ // We can re-use a type with own QML.* macros as target of QML.Foreign
+ if (const FoundType found = findType(foreign, types, foreignTypeName, namespaces)) {
+ resolved = found.select(classDef, "Foreign");
+
+ // Default properties and enum classes are always local.
+ defaultProp = {};
+ registerEnumClassesScoped = false;
+
+ // Foreign type can have a default property or an attached type,
+ // or RegisterEnumClassesUnscoped classinfo.
+ for (const ClassInfo &obj : resolved.classInfos()) {
+ const QAnyStringView foreignName = obj.name;
+ const QAnyStringView foreignValue = obj.value;
+ if (defaultProp.isEmpty() && foreignName == S_DEFAULT_PROPERTY) {
defaultProp = foreignValue;
- } else if (parentProp.isEmpty() && foreignName == QLatin1String("ParentProperty")) {
+ } else if (parentProp.isEmpty() && foreignName == S_PARENT_PROPERTY) {
parentProp = foreignValue;
- } else if (foreignName == QLatin1String("QML.Attached")) {
- attachedType = foreignValue;
- collectRelated(foreignValue, types, foreign, defaultRevision);
- } else if (foreignName == QLatin1String("QML.Extended")) {
- extensionType = foreignValue;
- collectRelated(foreignValue, types, foreign, defaultRevision);
- } else if (foreignName == QLatin1String("QML.ExtensionIsNamespace")) {
- if (foreignValue == QLatin1String("true"))
+ } else if (foreignName == S_REGISTER_ENUM_CLASSES_UNSCOPED) {
+ if (foreignValue == S_FALSE)
+ registerEnumClassesScoped = true;
+ } else if (foreignName == S_ATTACHED) {
+ if (const FoundType attached = collectRelated(
+ foreignValue, types, foreign, defaultRevision, namespaces)) {
+ attachedType = attached.select(resolved, "Attached").qualifiedClassName();
+ }
+ } else if (foreignName == S_EXTENDED) {
+ if (const FoundType extension = collectRelated(
+ foreignValue, types, foreign, defaultRevision, namespaces)) {
+ nativeExtensionType = extension.native.qualifiedClassName();
+ javaScriptExtensionType = extension.javaScript.qualifiedClassName();
+ }
+ } else if (foreignName == S_EXTENSION_IS_JAVA_SCRIPT) {
+ if (foreignValue == S_TRUE)
+ extensionIsJavaScript = true;
+ } else if (foreignName == S_EXTENSION_IS_NAMESPACE) {
+ if (foreignValue == S_TRUE)
extensionIsNamespace = true;
- } else if (foreignName == QLatin1String("QML.Sequence")) {
- sequenceValueType = foreignValue;
- collectRelated(foreignValue, types, foreign, defaultRevision);
+ } else if (foreignName == S_SEQUENCE) {
+ if (const FoundType element = collectRelated(
+ foreignValue, types, foreign, defaultRevision, namespaces)) {
+ sequenceValueType
+ = element.select(resolved, "Sequence value").qualifiedClassName();
+ }
}
}
} else {
className = foreignTypeName;
- classDef = nullptr;
+ resolved = MetaType();
}
}
- if (classDef) {
- if (mode == RelatedType || !elementName.isEmpty()) {
- collectExtraVersions(classDef, QString::fromLatin1("properties"), revisions);
- collectExtraVersions(classDef, QString::fromLatin1("slots"), revisions);
- collectExtraVersions(classDef, QString::fromLatin1("methods"), revisions);
- collectExtraVersions(classDef, QString::fromLatin1("signals"), revisions);
+ if (!resolved.isEmpty()) {
+ if (mode == RelatedType || !elementNames.isEmpty()) {
+ collectExtraVersions(resolved.properties(), revisions);
+ collectExtraVersions(resolved.methods(), revisions);
+ collectExtraVersions(resolved.sigs(), revisions);
}
- collectSuperClasses(classDef, types, foreign, mode, defaultRevision);
+ collectSuperClasses(resolved, types, foreign, mode, defaultRevision);
}
if (mode != TopLevel)
return;
- if (classDef)
- collectInterfaces(classDef);
+ if (!resolved.isEmpty())
+ collectInterfaces(resolved);
if (!addedInRevision.isValid()) {
- revisions.append(defaultRevision);
addedInRevision = defaultRevision;
- } else if (addedInRevision < defaultRevision) {
+ }
+ if (addedInRevision <= defaultRevision
+ && (!removedInRevision.isValid() || defaultRevision < removedInRevision)) {
revisions.append(defaultRevision);
}
@@ -248,37 +389,117 @@ void QmlTypesClassDescription::collect(
const auto end = std::unique(revisions.begin(), revisions.end());
revisions.erase(QList<QTypeRevision>::const_iterator(end), revisions.constEnd());
- resolvedClass = classDef;
- if (className.isEmpty() && classDef)
- className = classDef->value(QLatin1String("qualifiedClassName")).toString();
+ resolvedClass = resolved;
+ if (className.isEmpty() && !resolved.isEmpty())
+ className = resolved.qualifiedClassName();
if (!sequenceValueType.isEmpty()) {
isCreatable = false;
- accessSemantics = QLatin1String("sequence");
+ accessSemantics = DotQmltypes::S_SEQUENCE;
} else if (isNamespace) {
isCreatable = false;
- accessSemantics = QLatin1String("none");
- } else if (classDef && classDef->value(QLatin1String("object")).toBool()) {
- accessSemantics = QLatin1String("reference");
+ accessSemantics = DotQmltypes::S_NONE;
+ } else if (resolved.kind() == MetaType::Kind::Object) {
+ accessSemantics = DotQmltypes::S_REFERENCE;
} else {
- isCreatable = false;
- // If no classDef, we assume it's a value type defined by the foreign/extended trick.
- // Objects and namespaces always have metaobjects and therefore classDefs.
- accessSemantics = (!classDef || classDef->value(QLatin1String("gadget")).toBool())
- ? QLatin1String("value")
- : QLatin1String("none");
+ isCreatable = isConstructible;
+
+ if (resolved.isEmpty()) {
+ if (elementNames.isEmpty()) {
+ // If no resolved, we generally assume it's a value type defined by the
+ // foreign/extended trick.
+ accessSemantics = DotQmltypes::S_VALUE;
+ }
+
+ for (auto elementName = elementNames.begin(); elementName != elementNames.end();) {
+ if (elementName->isEmpty() || elementName->front().isLower()) {
+ // If no resolved, we generally assume it's a value type defined by the
+ // foreign/extended trick.
+ accessSemantics = DotQmltypes::S_VALUE;
+ ++elementName;
+ } else {
+ // Objects and namespaces always have metaobjects and therefore classDefs.
+ // However, we may not be able to resolve the metaobject at compile time. See
+ // the "Invisible" test case. In that case, we must not assume anything about
+ // access semantics.
+
+ warning(classDef)
+ << "Refusing to generate non-lowercase name"
+ << *elementName << "for unknown foreign type";
+ elementName = elementNames.erase(elementName);
+
+ if (elementNames.isEmpty()) {
+ // Make it completely inaccessible.
+ // We cannot get enums from anonymous types after all.
+ accessSemantics = DotQmltypes::S_NONE;
+ }
+ }
+ }
+ } else if (resolved.kind() == MetaType::Kind::Gadget) {
+ accessSemantics = DotQmltypes::S_VALUE;
+ } else {
+ accessSemantics = DotQmltypes::S_NONE;
+ }
+ }
+}
+
+FoundType QmlTypesClassDescription::collectRelated(
+ QAnyStringView related, const QVector<MetaType> &types, const QVector<MetaType> &foreign,
+ QTypeRevision defaultRevision, const QList<QAnyStringView> &namespaces)
+{
+ if (FoundType other = findType(types, foreign, related, namespaces)) {
+ if (!other.native.isEmpty())
+ collect(other.native, types, foreign, RelatedType, defaultRevision);
+ if (!other.javaScript.isEmpty())
+ collect(other.javaScript, types, foreign, RelatedType, defaultRevision);
+ return other;
}
+ return FoundType();
}
-void QmlTypesClassDescription::collectRelated(const QString &related,
- const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign,
- QTypeRevision defaultRevision)
+
+ResolvedTypeAlias::ResolvedTypeAlias(QAnyStringView alias)
+ : type(alias)
{
- if (const QJsonObject *other = findType(types, related))
- collect(other, types, foreign, RelatedType, defaultRevision);
- else if (const QJsonObject *other = findType(foreign, related))
- collect(other, types, foreign, RelatedType, defaultRevision);
+ if (type.isEmpty())
+ return;
+
+ if (type == "void") {
+ type = "";
+ return;
+ }
+
+ // This is a best effort approach and will not return correct results in the
+ // presence of typedefs.
+
+ auto handleList = [&](QLatin1StringView list) {
+ if (!startsWith(type, list) || type.back() != '>'_L1)
+ return false;
+
+ const int listSize = list.size();
+ const QAnyStringView elementType = trimmed(type.mid(listSize, type.size() - listSize - 1));
+
+ // QQmlListProperty internally constructs the pointer. Passing an explicit '*' will
+ // produce double pointers. QList is only for value types. We can't handle QLists
+ // of pointers (unless specially registered, but then they're not isList).
+ if (elementType.back() == '*'_L1)
+ return false;
+
+ isList = true;
+ type = elementType;
+ return true;
+ };
+
+ if (!handleList("QQmlListProperty<"_L1) && !handleList("QList<"_L1)) {
+ if (type.back() == '*'_L1) {
+ isPointer = true;
+ type = type.chopped(1);
+ }
+ if (startsWith(type, "const "_L1)) {
+ isConstant = true;
+ type = type.sliced(strlen("const "));
+ }
+ }
}
QT_END_NAMESPACE
diff --git a/src/qmltyperegistrar/qqmltypesclassdescription_p.h b/src/qmltyperegistrar/qqmltypesclassdescription_p.h
index cb08237a20..60aafb948f 100644
--- a/src/qmltyperegistrar/qqmltypesclassdescription_p.h
+++ b/src/qmltyperegistrar/qqmltypesclassdescription_p.h
@@ -15,38 +15,71 @@
// We mean it.
//
+#include <private/qmetatypesjsonprocessor_p.h>
+
#include <QtCore/qstring.h>
-#include <QtCore/qjsonobject.h>
+#include <QtCore/qcbormap.h>
#include <QtCore/qvector.h>
#include <QtCore/qset.h>
#include <QtCore/qversionnumber.h>
QT_BEGIN_NAMESPACE
+struct FoundType
+{
+ enum Origin {
+ Unknown,
+ OwnTypes,
+ ForeignTypes,
+ };
+
+ FoundType() = default;
+ FoundType(const MetaType &single, Origin origin);
+
+ MetaType native;
+ MetaType javaScript;
+
+ Origin nativeOrigin = Unknown;
+ Origin javaScriptOrigin = Unknown;
+
+ operator bool() const { return !native.isEmpty() || !javaScript.isEmpty(); }
+
+ MetaType select(const MetaType &category, QAnyStringView relation) const;
+
+};
+
struct QmlTypesClassDescription
{
- const QJsonObject *resolvedClass = nullptr;
- QString file;
- QString className;
- QString elementName;
- QString defaultProp;
- QString parentProp;
- QString superClass;
- QString attachedType;
- QString extensionType;
- QString sequenceValueType;
- QString accessSemantics;
+ // All the string views in this class are based on string data in the JSON they are parsed from.
+ // You must keep the relevant QCborValues alive while the QmlTypesClassDescription exists.
+
+ MetaType resolvedClass;
+ QAnyStringView file;
+ QAnyStringView className;
+ QList<QAnyStringView> primitiveAliases;
+ QList<QAnyStringView> elementNames;
+ QAnyStringView defaultProp;
+ QAnyStringView parentProp;
+ QAnyStringView superClass;
+ QAnyStringView attachedType;
+ QAnyStringView javaScriptExtensionType;
+ QAnyStringView nativeExtensionType;
+ QAnyStringView sequenceValueType;
+ QAnyStringView accessSemantics;
QList<QTypeRevision> revisions;
QTypeRevision addedInRevision;
QTypeRevision removedInRevision;
bool isCreatable = true;
+ bool isStructured = false;
bool isSingleton = false;
bool hasCustomParser = false;
- bool omitFromQmlTypes = false;
+ bool isRootClass = false;
+ bool extensionIsJavaScript = false;
bool extensionIsNamespace = false;
- QStringList implementsInterfaces;
- QStringList deferredNames;
- QStringList immediateNames;
+ bool registerEnumClassesScoped = false;
+ QList<QAnyStringView> implementsInterfaces;
+ QList<QAnyStringView> deferredNames;
+ QList<QAnyStringView> immediateNames;
enum CollectMode {
TopLevel,
@@ -54,23 +87,38 @@ struct QmlTypesClassDescription
RelatedType
};
- void collect(const QJsonObject *classDef, const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign, CollectMode mode,
- QTypeRevision defaultRevision);
- void collectRelated(const QString &related, const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign, QTypeRevision defaultRevision);
+ void collect(
+ const MetaType &classDef, const QVector<MetaType> &types,
+ const QVector<MetaType> &foreign, CollectMode mode, QTypeRevision defaultRevision);
+ FoundType collectRelated(
+ QAnyStringView related, const QVector<MetaType> &types,
+ const QVector<MetaType> &foreign, QTypeRevision defaultRevision,
+ const QList<QAnyStringView> &namespaces);
- static const QJsonObject *findType(const QVector<QJsonObject> &types, const QString &name);
+ static FoundType findType(
+ const QVector<MetaType> &types, const QVector<MetaType> &foreign,
+ const QAnyStringView &name, const QList<QAnyStringView> &namespaces);
- void collectLocalAnonymous(const QJsonObject *classDef,const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign, QTypeRevision defaultRevision);
+ void collectLocalAnonymous(
+ const MetaType &classDef, const QVector<MetaType> &types,
+ const QVector<MetaType> &foreign, QTypeRevision defaultRevision);
private:
void collectSuperClasses(
- const QJsonObject *classDef, const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign, CollectMode mode, QTypeRevision defaultRevision);
- void collectInterfaces(const QJsonObject *classDef);
+ const MetaType &classDef, const QVector<MetaType> &types,
+ const QVector<MetaType> &foreign, CollectMode mode, QTypeRevision defaultRevision);
+ void collectInterfaces(const MetaType &classDef);
+};
+
+struct ResolvedTypeAlias
+{
+ ResolvedTypeAlias(QAnyStringView alias);
+
+ QAnyStringView type;
+ bool isList = false;
+ bool isPointer = false;
+ bool isConstant = false;
};
QT_END_NAMESPACE
diff --git a/src/qmltyperegistrar/qqmltypescreator.cpp b/src/qmltyperegistrar/qqmltypescreator.cpp
index dbc438c50e..297a23679a 100644
--- a/src/qmltyperegistrar/qqmltypescreator.cpp
+++ b/src/qmltyperegistrar/qqmltypescreator.cpp
@@ -1,103 +1,117 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "qqmltypescreator_p.h"
+#include "qanystringviewutils_p.h"
+#include "qqmltyperegistrarconstants_p.h"
+#include "qqmltyperegistrarutils_p.h"
#include "qqmltypesclassdescription_p.h"
+#include "qqmltypescreator_p.h"
#include <QtCore/qset.h>
-#include <QtCore/qjsonarray.h>
+#include <QtCore/qcborarray.h>
+#include <QtCore/qcbormap.h>
#include <QtCore/qsavefile.h>
#include <QtCore/qfile.h>
-#include <QtCore/qjsondocument.h>
#include <QtCore/qversionnumber.h>
-using namespace Qt::StringLiterals;
+#include <QtCore/private/qstringalgorithms_p.h>
QT_BEGIN_NAMESPACE
-static QString enquote(const QString &string)
-{
- QString s = string;
- return QString::fromLatin1("\"%1\"").arg(s.replace(QLatin1Char('\\'), QLatin1String("\\\\"))
- .replace(QLatin1Char('"'),QLatin1String("\\\"")));
-}
+using namespace Qt::StringLiterals;
+using namespace Constants;
+using namespace Constants::DotQmltypes;
+using namespace QAnyStringViewUtils;
-static QString convertPrivateClassToUsableForm(QString s)
+static QString convertPrivateClassToUsableForm(QAnyStringView s)
{
// typical privateClass entry in MOC looks like: ClassName::d_func(), where
// ClassName is a non-private class name. we don't need "::d_func()" piece
// so that could be removed, but we need "Private" so that ClassName becomes
// ClassNamePrivate (at present, simply consider this correct)
- s.replace(u"::d_func()"_s, u"Private"_s);
- return s;
+ return s.toString().replace("::d_func()"_L1, "Private"_L1);
}
void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &collector)
{
if (!collector.file.isEmpty())
- m_qml.writeScriptBinding(QLatin1String("file"), enquote(collector.file));
- m_qml.writeScriptBinding(QLatin1String("name"), enquote(collector.className));
+ m_qml.writeStringBinding(S_FILE, collector.file);
+ m_qml.writeStringBinding(S_NAME, collector.className);
+
+ if (!collector.primitiveAliases.isEmpty())
+ m_qml.writeStringListBinding(S_ALIASES, collector.primitiveAliases);
if (!collector.accessSemantics.isEmpty())
- m_qml.writeScriptBinding(QLatin1String("accessSemantics"), enquote(collector.accessSemantics));
+ m_qml.writeStringBinding(S_ACCESS_SEMANTICS, collector.accessSemantics);
if (!collector.defaultProp.isEmpty())
- m_qml.writeScriptBinding(QLatin1String("defaultProperty"), enquote(collector.defaultProp));
+ m_qml.writeStringBinding(S_DEFAULT_PROPERTY, collector.defaultProp);
if (!collector.parentProp.isEmpty())
- m_qml.writeScriptBinding(QLatin1String("parentProperty"), enquote(collector.parentProp));
+ m_qml.writeStringBinding(S_PARENT_PROPERTY, collector.parentProp);
if (!collector.superClass.isEmpty())
- m_qml.writeScriptBinding(QLatin1String("prototype"), enquote(collector.superClass));
+ m_qml.writeStringBinding(S_PROTOTYPE, collector.superClass);
if (!collector.sequenceValueType.isEmpty()) {
- const QString name = collector.sequenceValueType.endsWith('*'_L1)
+ const QAnyStringView name = collector.sequenceValueType.back() == '*'_L1
? collector.sequenceValueType.chopped(1)
: collector.sequenceValueType;
- m_qml.writeScriptBinding(QLatin1String("valueType"), enquote(name));
+ m_qml.writeStringBinding(S_VALUE_TYPE, name);
}
- if (!collector.extensionType.isEmpty())
- m_qml.writeScriptBinding(QLatin1String("extension"), enquote(collector.extensionType));
-
- if (collector.extensionIsNamespace)
- m_qml.writeScriptBinding(QLatin1String("extensionIsNamespace"), QLatin1String("true"));
-
- if (!collector.implementsInterfaces.isEmpty()) {
- QStringList interfaces;
- for (const QString &interface : collector.implementsInterfaces)
- interfaces << enquote(interface);
+ if (collector.extensionIsJavaScript) {
+ if (!collector.javaScriptExtensionType.isEmpty()) {
+ m_qml.writeStringBinding(S_EXTENSION, collector.javaScriptExtensionType);
+ m_qml.writeBooleanBinding(S_EXTENSION_IS_JAVA_SCRIPT, true);
+ } else {
+ warning(collector.file)
+ << "JavaScript extension type for" << collector.className
+ << "does not exist";
+ }
- m_qml.writeArrayBinding(QLatin1String("interfaces"), interfaces);
+ if (collector.extensionIsNamespace) {
+ warning(collector.file)
+ << "Extension type for" << collector.className
+ << "cannot be both a JavaScript type and a namespace";
+ if (!collector.nativeExtensionType.isEmpty()) {
+ m_qml.writeStringBinding(S_EXTENSION, collector.nativeExtensionType);
+ m_qml.writeBooleanBinding(S_EXTENSION_IS_NAMESPACE, true);
+ }
+ }
+ } else if (!collector.nativeExtensionType.isEmpty()) {
+ m_qml.writeStringBinding(S_EXTENSION, collector.nativeExtensionType);
+ if (collector.extensionIsNamespace)
+ m_qml.writeBooleanBinding(S_EXTENSION_IS_NAMESPACE, true);
+ } else if (collector.extensionIsNamespace) {
+ warning(collector.file)
+ << "Extension namespace for" << collector.className << "does not exist";
+ m_qml.writeBooleanBinding(S_EXTENSION_IS_NAMESPACE, true);
}
- if (!collector.deferredNames.isEmpty()) {
- QStringList deferredNames;
- for (const QString &name : collector.deferredNames)
- deferredNames << enquote(name);
+ if (!collector.implementsInterfaces.isEmpty())
+ m_qml.writeStringListBinding(S_INTERFACES, collector.implementsInterfaces);
- m_qml.writeArrayBinding(QLatin1String("deferredNames"), deferredNames);
- }
-
- if (!collector.immediateNames.isEmpty()) {
- QStringList immediateNames;
- for (const QString &name : collector.immediateNames)
- immediateNames << enquote(name);
+ if (!collector.deferredNames.isEmpty())
+ m_qml.writeStringListBinding(S_DEFERRED_NAMES, collector.deferredNames);
- m_qml.writeArrayBinding(QLatin1String("immediateNames"), immediateNames);
- }
+ if (!collector.immediateNames.isEmpty())
+ m_qml.writeStringListBinding(S_IMMEDIATE_NAMES, collector.immediateNames);
- if (collector.elementName.isEmpty()) // e.g. if QML_ANONYMOUS
+ if (collector.elementNames.isEmpty()) // e.g. if QML_ANONYMOUS
return;
if (!collector.sequenceValueType.isEmpty()) {
- qWarning() << "Ignoring name of sequential container:" << collector.elementName;
- qWarning() << "Sequential containers are anonymous. Use QML_ANONYMOUS to register them.";
+ warning(collector.file) << "Ignoring names of sequential container:";
+ for (const QAnyStringView &name : std::as_const(collector.elementNames))
+ warning(collector.file) << " - " << name.toString();
+ warning(collector.file)
+ << "Sequential containers are anonymous. Use QML_ANONYMOUS to register them.";
return;
}
- QStringList exports;
- QStringList metaObjects;
+ QByteArrayList exports;
+ QByteArrayList metaObjects;
for (auto it = collector.revisions.begin(), end = collector.revisions.end(); it != end; ++it) {
const QTypeRevision revision = *it;
@@ -108,340 +122,331 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
if (revision.hasMajorVersion() && revision.majorVersion() > m_version.majorVersion())
break;
- exports.append(enquote(QString::fromLatin1("%1/%2 %3.%4")
- .arg(m_module, collector.elementName)
- .arg(revision.hasMajorVersion() ? revision.majorVersion()
- : m_version.majorVersion())
- .arg(revision.minorVersion())));
- metaObjects.append(QString::number(revision.toEncodedVersion<quint16>()));
+ for (const QAnyStringView &elementName : std::as_const(collector.elementNames)) {
+ QByteArray exportEntry = m_module + '/';
+
+ elementName.visit([&](auto view) {
+ processAsUtf8(view, [&](QByteArrayView view) { exportEntry.append(view); });
+ });
+ exportEntry += ' ' + QByteArray::number(revision.hasMajorVersion()
+ ? revision.majorVersion()
+ : m_version.majorVersion());
+ exportEntry += '.' + QByteArray::number(revision.minorVersion());
+
+ exports.append(exportEntry);
+ }
+ metaObjects.append(QByteArray::number(revision.toEncodedVersion<quint16>()));
}
- m_qml.writeArrayBinding(QLatin1String("exports"), exports);
+ QList<QAnyStringView> exportStrings;
+ exportStrings.reserve(exports.length());
+ for (const QByteArray &entry: exports)
+ exportStrings.append(QUtf8StringView(entry));
- if (!collector.isCreatable || collector.isSingleton)
- m_qml.writeScriptBinding(QLatin1String("isCreatable"), QLatin1String("false"));
+ m_qml.writeStringListBinding(S_EXPORTS, exportStrings);
+ m_qml.writeBooleanBinding(S_IS_CREATABLE, collector.isCreatable && !collector.isSingleton);
+
+ if (collector.isStructured)
+ m_qml.writeBooleanBinding(S_IS_STRUCTURED, true);
if (collector.isSingleton)
- m_qml.writeScriptBinding(QLatin1String("isSingleton"), QLatin1String("true"));
+ m_qml.writeBooleanBinding(S_IS_SINGLETON, true);
if (collector.hasCustomParser)
- m_qml.writeScriptBinding(QLatin1String("hasCustomParser"), QLatin1String("true"));
+ m_qml.writeBooleanBinding(S_HAS_CUSTOM_PARSER, true);
- m_qml.writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjects);
+ m_qml.writeArrayBinding(S_EXPORT_META_OBJECT_REVISIONS, metaObjects);
if (!collector.attachedType.isEmpty())
- m_qml.writeScriptBinding(QLatin1String("attachedType"), enquote(collector.attachedType));
+ m_qml.writeStringBinding(S_ATTACHED_TYPE, collector.attachedType);
}
-void QmlTypesCreator::writeType(const QJsonObject &property, const QString &key)
+void QmlTypesCreator::writeType(QAnyStringView type)
{
- auto it = property.find(key);
- if (it == property.end())
- return;
-
- QString type = (*it).toString();
- if (type.isEmpty() || type == QLatin1String("void"))
+ ResolvedTypeAlias resolved(type);
+ if (resolved.type.isEmpty())
return;
- const QLatin1String typeKey("type");
-
- bool isList = false;
- bool isPointer = false;
- // This is a best effort approach (like isPointer) and will not return correct results in the
- // presence of typedefs.
- bool isConstant = false;
-
- auto handleList = [&](QLatin1String list) {
- if (!type.startsWith(list) || !type.endsWith(QLatin1Char('>')))
- return false;
-
- const int listSize = list.size();
- const QString elementType = type.mid(listSize, type.size() - listSize - 1).trimmed();
-
- // QQmlListProperty internally constructs the pointer. Passing an explicit '*' will
- // produce double pointers. QList is only for value types. We can't handle QLists
- // of pointers (unless specially registered, but then they're not isList).
- if (elementType.endsWith(QLatin1Char('*')))
- return false;
-
- isList = true;
- type = elementType;
- return true;
- };
-
- if (!handleList(QLatin1String("QQmlListProperty<"))
- && !handleList(QLatin1String("QList<"))) {
- if (type.endsWith(QLatin1Char('*'))) {
- isPointer = true;
- type = type.left(type.size() - 1);
- }
- if (type.startsWith(u"const ")) {
- isConstant = true;
- type = type.sliced(strlen("const "));
- }
- }
-
- if (type == QLatin1String("qreal")) {
-#ifdef QT_COORD_TYPE_STRING
- type = QLatin1String(QT_COORD_TYPE_STRING)
-#else
- type = QLatin1String("double");
-#endif
- } else if (type == QLatin1String("qint32")) {
- type = QLatin1String("int");
- } else if (type == QLatin1String("quint32")) {
- type = QLatin1String("uint");
- } else if (type == QLatin1String("qint64")) {
- type = QLatin1String("qlonglong");
- } else if (type == QLatin1String("quint64")) {
- type = QLatin1String("qulonglong");
- }
-
- m_qml.writeScriptBinding(typeKey, enquote(type));
- const QLatin1String trueString("true");
- if (isList)
- m_qml.writeScriptBinding(QLatin1String("isList"), trueString);
- if (isPointer)
- m_qml.writeScriptBinding(QLatin1String("isPointer"), trueString);
- if (isConstant)
- m_qml.writeScriptBinding(QLatin1String("isConstant"), trueString);
+ m_qml.writeStringBinding(S_TYPE, resolved.type);
+ if (resolved.isList)
+ m_qml.writeBooleanBinding(S_IS_LIST, true);
+ if (resolved.isPointer)
+ m_qml.writeBooleanBinding(S_IS_POINTER, true);
+ if (resolved.isConstant)
+ m_qml.writeBooleanBinding(S_IS_CONSTANT, true);
}
-void QmlTypesCreator::writeProperties(const QJsonArray &properties)
+void QmlTypesCreator::writeProperties(const Property::Container &properties)
{
- for (const QJsonValue property : properties) {
- const QJsonObject obj = property.toObject();
- const QString name = obj[QLatin1String("name")].toString();
- m_qml.writeStartObject(QLatin1String("Property"));
- m_qml.writeScriptBinding(QLatin1String("name"), enquote(name));
- const auto it = obj.find(QLatin1String("revision"));
- if (it != obj.end())
- m_qml.writeScriptBinding(QLatin1String("revision"), QString::number(it.value().toInt()));
-
- writeType(obj, QLatin1String("type"));
-
- const auto bindable = obj.constFind(QLatin1String("bindable"));
- if (bindable != obj.constEnd())
- m_qml.writeScriptBinding(QLatin1String("bindable"), enquote(bindable->toString()));
- const auto read = obj.constFind(QLatin1String("read"));
- if (read != obj.constEnd())
- m_qml.writeScriptBinding(QLatin1String("read"), enquote(read->toString()));
- const auto write = obj.constFind(QLatin1String("write"));
- if (write != obj.constEnd())
- m_qml.writeScriptBinding(QLatin1String("write"), enquote(write->toString()));
- const auto reset = obj.constFind(QLatin1String("reset"));
- if (reset != obj.constEnd())
- m_qml.writeScriptBinding(QLatin1String("reset"), enquote(reset->toString()));
- const auto notify = obj.constFind(QLatin1String("notify"));
- if (notify != obj.constEnd())
- m_qml.writeScriptBinding(QLatin1String("notify"), enquote(notify->toString()));
- const auto index = obj.constFind(QLatin1String("index"));
- if (index != obj.constEnd()) {
- m_qml.writeScriptBinding(QLatin1String("index"),
- QString::number(index.value().toInt()));
+ for (const Property &obj : properties) {
+ const QAnyStringView name = obj.name;
+ m_qml.writeStartObject(S_PROPERTY);
+ m_qml.writeStringBinding(S_NAME, name);
+ if (obj.revision.isValid())
+ m_qml.writeNumberBinding(S_REVISION, obj.revision.toEncodedVersion<int>());
+
+ writeType(obj.type);
+
+ const auto bindable = obj.bindable;
+ if (!bindable.isEmpty())
+ m_qml.writeStringBinding(S_BINDABLE, bindable);
+ const auto read = obj.read;
+ if (!read.isEmpty())
+ m_qml.writeStringBinding(S_READ, read);
+ const auto write = obj.write;
+ if (!write.isEmpty())
+ m_qml.writeStringBinding(S_WRITE, write);
+ const auto reset = obj.reset;
+ if (!reset.isEmpty())
+ m_qml.writeStringBinding(S_RESET, reset);
+ const auto notify = obj.notify;
+ if (!notify.isEmpty())
+ m_qml.writeStringBinding(S_NOTIFY, notify);
+ const auto index = obj.index;
+ if (index != -1) {
+ m_qml.writeNumberBinding(S_INDEX, index);
}
- const auto privateClass = obj.constFind(QLatin1String("privateClass"));
- if (privateClass != obj.constEnd()) {
- m_qml.writeScriptBinding(
- QLatin1String("privateClass"),
- enquote(convertPrivateClassToUsableForm(privateClass->toString())));
+ const auto privateClass = obj.privateClass;
+ if (!privateClass.isEmpty()) {
+ m_qml.writeStringBinding(
+ S_PRIVATE_CLASS, convertPrivateClassToUsableForm(privateClass));
}
- if (!obj.contains(QLatin1String("write")) && !obj.contains(QLatin1String("member")))
- m_qml.writeScriptBinding(QLatin1String("isReadonly"), QLatin1String("true"));
+ if (obj.write.isEmpty() && obj.member.isEmpty())
+ m_qml.writeBooleanBinding(S_IS_READONLY, true);
- const auto final = obj.constFind(QLatin1String("final"));
- if (final != obj.constEnd() && final->toBool())
- m_qml.writeScriptBinding(QLatin1String("isFinal"), QLatin1String("true"));
+ if (obj.isFinal)
+ m_qml.writeBooleanBinding(S_IS_FINAL, true);
- const auto constant = obj.constFind(QLatin1String("constant"));
- if (constant != obj.constEnd() && constant->toBool())
- m_qml.writeScriptBinding(QLatin1String("isConstant"), QLatin1String("true"));
+ if (obj.isConstant)
+ m_qml.writeBooleanBinding(S_IS_CONSTANT, true);
- const auto required = obj.constFind(QLatin1String("required"));
- if (required != obj.constEnd() && required->toBool())
- m_qml.writeScriptBinding(QLatin1String("isRequired"), QLatin1String("true"));
+ if (obj.isRequired)
+ m_qml.writeBooleanBinding(S_IS_REQUIRED, true);
m_qml.writeEndObject();
}
}
-void QmlTypesCreator::writeMethods(const QJsonArray &methods, const QString &type)
+void QmlTypesCreator::writeMethods(const Method::Container &methods, QLatin1StringView type)
{
- const auto writeFlag = [this](const QLatin1String &name, const QJsonObject &obj) {
- const auto flag = obj.find(name);
- if (flag != obj.constEnd() && flag->toBool())
- m_qml.writeBooleanBinding(name, true);
- };
-
- for (const QJsonValue method : methods) {
- const QJsonObject obj = method.toObject();
- const QString name = obj[QLatin1String("name")].toString();
+ for (const Method &obj : methods) {
+ const QAnyStringView name = obj.name;
if (name.isEmpty())
continue;
- const QJsonArray arguments = method[QLatin1String("arguments")].toArray();
- const auto revision = obj.find(QLatin1String("revision"));
- m_qml.writeStartObject(type);
- m_qml.writeScriptBinding(QLatin1String("name"), enquote(name));
- if (revision != obj.end())
- m_qml.writeScriptBinding(QLatin1String("revision"), QString::number(revision.value().toInt()));
- writeType(obj, QLatin1String("returnType"));
-
- writeFlag(QLatin1String("isCloned"), obj);
- writeFlag(QLatin1String("isConstructor"), obj);
- writeFlag(QLatin1String("isJavaScriptFunction"), obj);
+ const auto revision = obj.revision;
+ m_qml.writeStartObject(type);
+ m_qml.writeStringBinding(S_NAME, name);
+ if (revision.isValid())
+ m_qml.writeNumberBinding(S_REVISION, revision.toEncodedVersion<int>());
+ writeType(obj.returnType);
+
+ if (obj.isCloned)
+ m_qml.writeBooleanBinding(S_IS_CLONED, true);
+ if (obj.isConstructor)
+ m_qml.writeBooleanBinding(S_IS_CONSTRUCTOR, true);
+ if (obj.isJavaScriptFunction)
+ m_qml.writeBooleanBinding(S_IS_JAVASCRIPT_FUNCTION, true);
+
+ const Argument::Container &arguments = obj.arguments;
for (qsizetype i = 0, end = arguments.size(); i != end; ++i) {
- const QJsonObject obj = arguments[i].toObject();
- if (i == 0 && end == 1 &&
- obj[QLatin1String("type")].toString() == QLatin1String("QQmlV4Function*")) {
- m_qml.writeScriptBinding(QLatin1String("isJavaScriptFunction"),
- QLatin1String("true"));
- break;
- }
- m_qml.writeStartObject(QLatin1String("Parameter"));
- const QString name = obj[QLatin1String("name")].toString();
+ const Argument &obj = arguments[i];
+ m_qml.writeStartObject(S_PARAMETER);
+ const QAnyStringView name = obj.name;
if (!name.isEmpty())
- m_qml.writeScriptBinding(QLatin1String("name"), enquote(name));
- writeType(obj, QLatin1String("type"));
+ m_qml.writeStringBinding(S_NAME, name);
+ writeType(obj.type);
m_qml.writeEndObject();
}
m_qml.writeEndObject();
}
}
-void QmlTypesCreator::writeEnums(const QJsonArray &enums)
+void QmlTypesCreator::writeEnums(
+ const Enum::Container &enums, QmlTypesCreator::EnumClassesMode enumClassesMode)
{
- for (const QJsonValue item : enums) {
- const QJsonObject obj = item.toObject();
- const QJsonArray values = obj.value(QLatin1String("values")).toArray();
- QStringList valueList;
-
- for (const QJsonValue value : values)
- valueList.append(enquote(value.toString()));
-
- m_qml.writeStartObject(QLatin1String("Enum"));
- m_qml.writeScriptBinding(QLatin1String("name"),
- enquote(obj.value(QLatin1String("name")).toString()));
- auto alias = obj.find(QLatin1String("alias"));
- if (alias != obj.end())
- m_qml.writeScriptBinding(alias.key(), enquote(alias->toString()));
- auto isFlag = obj.find(QLatin1String("isFlag"));
- if (isFlag != obj.end() && isFlag->toBool())
- m_qml.writeBooleanBinding(isFlag.key(), true);
- m_qml.writeArrayBinding(QLatin1String("values"), valueList);
+ for (const Enum &obj : enums) {
+ m_qml.writeStartObject(S_ENUM);
+ m_qml.writeStringBinding(S_NAME, obj.name);
+ if (!obj.alias.isEmpty())
+ m_qml.writeStringBinding(S_ALIAS, obj.alias);
+ if (obj.isFlag)
+ m_qml.writeBooleanBinding(S_IS_FLAG, true);
+
+ if (enumClassesMode == EnumClassesMode::Scoped) {
+ if (obj.isClass)
+ m_qml.writeBooleanBinding(S_IS_SCOPED, true);
+ }
+
+ writeType(obj.type);
+ m_qml.writeStringListBinding(S_VALUES, obj.values);
m_qml.writeEndObject();
}
}
-static bool isAllowedInMajorVersion(const QJsonValue &member, QTypeRevision maxMajorVersion)
+template<typename Member>
+bool isAllowedInMajorVersion(const Member &memberObject, QTypeRevision maxMajorVersion)
{
- const auto memberObject = member.toObject();
- const auto it = memberObject.find(QLatin1String("revision"));
- if (it == memberObject.end())
- return true;
-
- const QTypeRevision memberRevision = QTypeRevision::fromEncodedVersion(it->toInt());
+ const QTypeRevision memberRevision = memberObject.revision;
return !memberRevision.hasMajorVersion()
|| memberRevision.majorVersion() <= maxMajorVersion.majorVersion();
}
-static QJsonArray members(const QJsonObject *classDef,
- const QString &key, QTypeRevision maxMajorVersion)
+template<typename Members, typename Postprocess>
+Members members(const Members &candidates, QTypeRevision maxMajorVersion, Postprocess &&process)
{
- QJsonArray classDefMembers;
+ Members classDefMembers;
- const QJsonArray candidates = classDef->value(key).toArray();
- for (const QJsonValue member : candidates) {
+ for (const auto &member : candidates) {
if (isAllowedInMajorVersion(member, maxMajorVersion))
- classDefMembers.append(member);
+ classDefMembers.push_back(process(member));
}
return classDefMembers;
}
-void QmlTypesCreator::writeComponents()
+template<typename Members>
+Members members(const Members &candidates, QTypeRevision maxMajorVersion)
{
- const QLatin1String signalsKey("signals");
- const QLatin1String enumsKey("enums");
- const QLatin1String propertiesKey("properties");
- const QLatin1String slotsKey("slots");
- const QLatin1String methodsKey("methods");
-
- const QLatin1String signalElement("Signal");
- const QLatin1String componentElement("Component");
- const QLatin1String methodElement("Method");
+ return members(candidates, maxMajorVersion, [](const auto &member) { return member; });
+}
- for (const QJsonObject &component : m_ownTypes) {
- QmlTypesClassDescription collector;
- collector.collect(&component, m_ownTypes, m_foreignTypes,
- QmlTypesClassDescription::TopLevel, m_version);
+template<typename Members>
+Members constructors(const Members &candidates, QTypeRevision maxMajorVersion)
+{
+ return members(candidates, maxMajorVersion, [](const auto &member) {
+ auto ctor = member;
+ ctor.isConstructor = true;
+ return ctor;
+ });
+}
- if (collector.omitFromQmlTypes)
- continue;
+void QmlTypesCreator::writeRootMethods(const MetaType &classDef)
+{
+ // Hide destroyed() signals
+ Method::Container componentSignals = members(classDef.sigs(), m_version);
+ for (auto it = componentSignals.begin(); it != componentSignals.end();) {
+ if (it->name == "destroyed"_L1)
+ it = componentSignals.erase(it);
+ else
+ ++it;
+ }
+ writeMethods(componentSignals, S_SIGNAL);
+
+ // Hide deleteLater() methods
+ Method::Container componentMethods = members(classDef.methods(), m_version);
+ for (auto it = componentMethods.begin(); it != componentMethods.end();) {
+ if (it->name == "deleteLater"_L1)
+ it = componentMethods.erase(it);
+ else
+ ++it;
+ }
- m_qml.writeStartObject(componentElement);
+ // Add toString()
+ Method toStringMethod;
+ toStringMethod.name = "toString"_L1;
+ toStringMethod.access = Access::Public;
+ toStringMethod.returnType = "QString"_L1;
+ componentMethods.push_back(std::move(toStringMethod));
+
+ // Add destroy(int)
+ Method destroyMethodWithArgument;
+ destroyMethodWithArgument.name = "destroy"_L1;
+ destroyMethodWithArgument.access = Access::Public;
+ Argument delayArgument;
+ delayArgument.name = "delay"_L1;
+ delayArgument.type = "int"_L1;
+ destroyMethodWithArgument.arguments.push_back(std::move(delayArgument));
+ componentMethods.push_back(std::move(destroyMethodWithArgument));
+
+ // Add destroy()
+ Method destroyMethod;
+ destroyMethod.name = "destroy"_L1;
+ destroyMethod.access = Access::Public;
+ destroyMethod.isCloned = true;
+ componentMethods.push_back(std::move(destroyMethod));
+
+ writeMethods(componentMethods, S_METHOD);
+};
+
+void QmlTypesCreator::writeComponent(const QmlTypesClassDescription &collector)
+{
+ m_qml.writeStartObject(S_COMPONENT);
- writeClassProperties(collector);
+ writeClassProperties(collector);
- if (const QJsonObject *classDef = collector.resolvedClass) {
- writeEnums(members(classDef, enumsKey, m_version));
+ if (const MetaType &classDef = collector.resolvedClass; !classDef.isEmpty()) {
+ writeEnums(
+ classDef.enums(),
+ collector.registerEnumClassesScoped
+ ? EnumClassesMode::Scoped
+ : EnumClassesMode::Unscoped);
- writeProperties(members(classDef, propertiesKey, m_version));
+ writeProperties(members(classDef.properties(), m_version));
- writeMethods(members(classDef, signalsKey, m_version), signalElement);
- writeMethods(members(classDef, slotsKey, m_version), methodElement);
- writeMethods(members(classDef, methodsKey, m_version), methodElement);
+ if (collector.isRootClass) {
+ writeRootMethods(classDef);
+ } else {
+ writeMethods(members(classDef.sigs(), m_version), S_SIGNAL);
+ writeMethods(members(classDef.methods(), m_version), S_METHOD);
}
- m_qml.writeEndObject();
- if (collector.resolvedClass != &component
+ writeMethods(constructors(classDef.constructors(), m_version), S_METHOD);
+ }
+ m_qml.writeEndObject();
+}
+
+void QmlTypesCreator::writeComponents()
+{
+ for (const MetaType &component : std::as_const(m_ownTypes)) {
+ QmlTypesClassDescription collector;
+ collector.collect(component, m_ownTypes, m_foreignTypes,
+ QmlTypesClassDescription::TopLevel, m_version);
+
+ writeComponent(collector);
+
+ if (collector.resolvedClass != component
&& std::binary_search(
m_referencedTypes.begin(), m_referencedTypes.end(),
- component.value(QStringLiteral("qualifiedClassName")).toString())) {
+ component.qualifiedClassName())) {
// This type is referenced from elsewhere and has a QML_FOREIGN of its own. We need to
// also generate a description of the local type then. All the QML_* macros are
// ignored, and the result is an anonymous type.
- m_qml.writeStartObject(componentElement);
-
QmlTypesClassDescription collector;
- collector.collectLocalAnonymous(&component, m_ownTypes, m_foreignTypes, m_version);
-
- writeClassProperties(collector);
- writeEnums(members(&component, enumsKey, m_version));
-
- writeProperties(members(&component, propertiesKey, m_version));
+ collector.collectLocalAnonymous(component, m_ownTypes, m_foreignTypes, m_version);
+ Q_ASSERT(!collector.isRootClass);
- writeMethods(members(&component, signalsKey, m_version), signalElement);
- writeMethods(members(&component, slotsKey, m_version), methodElement);
- writeMethods(members(&component, methodsKey, m_version), methodElement);
-
- m_qml.writeEndObject();
+ writeComponent(collector);
}
}
}
-void QmlTypesCreator::generate(const QString &outFileName)
+bool QmlTypesCreator::generate(const QString &outFileName)
{
m_qml.writeStartDocument();
- m_qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 2);
- m_qml.write(QString::fromLatin1(
+ m_qml.writeLibraryImport("QtQuick.tooling", 1, 2);
+ m_qml.write(
"\n// This file describes the plugin-supplied types contained in the library."
"\n// It is used for QML tooling purposes only."
"\n//"
- "\n// This file was auto-generated by qmltyperegistrar.\n\n"));
- m_qml.writeStartObject(QLatin1String("Module"));
+ "\n// This file was auto-generated by qmltyperegistrar.\n\n");
+ m_qml.writeStartObject(S_MODULE);
writeComponents();
m_qml.writeEndObject();
QSaveFile file(outFileName);
- file.open(QIODevice::WriteOnly);
- file.write(m_output);
- file.commit();
+ if (!file.open(QIODevice::WriteOnly))
+ return false;
+
+ if (file.write(m_output) != m_output.size())
+ return false;
+
+ return file.commit();
}
QT_END_NAMESPACE
diff --git a/src/qmltyperegistrar/qqmltypescreator_p.h b/src/qmltyperegistrar/qqmltypescreator_p.h
index 928e3cf6d8..10c5a3d35d 100644
--- a/src/qmltyperegistrar/qqmltypescreator_p.h
+++ b/src/qmltyperegistrar/qqmltypescreator_p.h
@@ -28,28 +28,33 @@ class QmlTypesCreator
public:
QmlTypesCreator() : m_qml(&m_output) {}
- void generate(const QString &outFileName);
+ bool generate(const QString &outFileName);
- void setOwnTypes(QVector<QJsonObject> ownTypes) { m_ownTypes = std::move(ownTypes); }
- void setForeignTypes(QVector<QJsonObject> foreignTypes) { m_foreignTypes = std::move(foreignTypes); }
- void setReferencedTypes(QStringList referencedTypes) { m_referencedTypes = std::move(referencedTypes); }
- void setModule(QString module) { m_module = std::move(module); }
+ void setOwnTypes(QVector<MetaType> ownTypes) { m_ownTypes = std::move(ownTypes); }
+ void setForeignTypes(QVector<MetaType> foreignTypes) { m_foreignTypes = std::move(foreignTypes); }
+ void setReferencedTypes(QList<QAnyStringView> referencedTypes) { m_referencedTypes = std::move(referencedTypes); }
+ void setModule(QByteArray module) { m_module = std::move(module); }
void setVersion(QTypeRevision version) { m_version = version; }
private:
+ void writeComponent(const QmlTypesClassDescription &collector);
void writeClassProperties(const QmlTypesClassDescription &collector);
- void writeType(const QJsonObject &property, const QString &key);
- void writeProperties(const QJsonArray &properties);
- void writeMethods(const QJsonArray &methods, const QString &type);
- void writeEnums(const QJsonArray &enums);
+ void writeType(QAnyStringView type);
+ void writeProperties(const Property::Container &properties);
+ void writeMethods(const Method::Container &methods, QLatin1StringView type);
+
+ enum class EnumClassesMode { Scoped, Unscoped };
+ void writeEnums(const Enum::Container &enums, EnumClassesMode enumClassesMode);
+
void writeComponents();
+ void writeRootMethods(const MetaType &classDef);
QByteArray m_output;
QQmlJSStreamWriter m_qml;
- QVector<QJsonObject> m_ownTypes;
- QVector<QJsonObject> m_foreignTypes;
- QStringList m_referencedTypes;
- QString m_module;
+ QVector<MetaType> m_ownTypes;
+ QVector<MetaType> m_foreignTypes;
+ QList<QAnyStringView> m_referencedTypes;
+ QByteArray m_module;
QTypeRevision m_version = QTypeRevision::zero();
};
diff --git a/src/qmlworkerscript/CMakeLists.txt b/src/qmlworkerscript/CMakeLists.txt
index 2bf6f205e2..1704d5ae16 100644
--- a/src/qmlworkerscript/CMakeLists.txt
+++ b/src/qmlworkerscript/CMakeLists.txt
@@ -54,13 +54,7 @@ qt_internal_add_qml_module(QmlWorkerScript
Qt::CorePrivate
Qt::QmlPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
-
-qt_internal_extend_target(QmlWorkerScript CONDITION WIN32
- DEFINES
- NOMINMAX
-)
+ )
qt_internal_extend_target(QmlWorkerScript CONDITION disassembler AND ((TEST_architecture_arch STREQUAL "i386") OR (TEST_architecture_arch STREQUAL "x86_64"))
DEFINES
diff --git a/src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf b/src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf
index ae8fd2b439..e9dab95f1e 100644
--- a/src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf
+++ b/src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf
@@ -29,5 +29,5 @@ exampledirs += snippets
navigation.qmltypespage = "Qt Qml WorkerScript QML Types"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/qmlworkerscript/doc/snippets/qml/workerscript/workerscript.qml b/src/qmlworkerscript/doc/snippets/qml/workerscript/workerscript.qml
index 87e179cc1e..509c1f5bf0 100644
--- a/src/qmlworkerscript/doc/snippets/qml/workerscript/workerscript.qml
+++ b/src/qmlworkerscript/doc/snippets/qml/workerscript/workerscript.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 300; height: 300
diff --git a/src/qmlworkerscript/qquickworkerscript.cpp b/src/qmlworkerscript/qquickworkerscript.cpp
index 8df631c6d3..f892343b85 100644
--- a/src/qmlworkerscript/qquickworkerscript.cpp
+++ b/src/qmlworkerscript/qquickworkerscript.cpp
@@ -180,8 +180,8 @@ bool QQuickWorkerScriptEnginePrivate::event(QEvent *event)
} else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) {
QMutexLocker locker(&m_lock);
WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event);
- auto itr = workers.find(workerEvent->workerId());
- if (itr != workers.end()) {
+ auto itr = workers.constFind(workerEvent->workerId());
+ if (itr != workers.cend()) {
if (itr->isT1())
delete itr->asT1();
workers.erase(itr);
@@ -255,7 +255,7 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
if (fileName.endsWith(QLatin1String(".mjs"))) {
auto module = engine->loadModule(url);
if (module.compiled) {
- if (module.compiled->instantiate(engine))
+ if (module.compiled->instantiate())
module.compiled->evaluate();
} else if (module.native) {
// Nothing to do. There is no global code in a native module.
@@ -417,8 +417,8 @@ int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
void QQuickWorkerScriptEngine::removeWorkerScript(int id)
{
- const auto it = d->workers.find(id);
- if (it == d->workers.end())
+ const auto it = d->workers.constFind(id);
+ if (it == d->workers.cend())
return;
if (it->isT1()) {
@@ -508,9 +508,6 @@ void QQuickWorkerScriptEngine::run()
Worker scripts that are plain JavaScript sources can not use \l {qtqml-javascript-imports.html}{.import} syntax.
Scripts that are ECMAScript modules can freely use import and export statements.
-
- \sa {Qt Quick Examples - Threading},
- {Threaded ListModel Example}
*/
QQuickWorkerScript::QQuickWorkerScript(QObject *parent)
: QObject(parent), m_engine(nullptr), m_scriptId(-1), m_componentComplete(true)
@@ -583,7 +580,7 @@ bool QQuickWorkerScript::ready() const
of ListModel objects, any modifications by the other thread to an object
passed in \c message will not be reflected in the original object.
*/
-void QQuickWorkerScript::sendMessage(QQmlV4Function *args)
+void QQuickWorkerScript::sendMessage(QQmlV4FunctionPtr args)
{
if (!engine()) {
qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment");
diff --git a/src/qmlworkerscript/qquickworkerscript_p.h b/src/qmlworkerscript/qquickworkerscript_p.h
index d2cf5d6601..1c4c0500fe 100644
--- a/src/qmlworkerscript/qquickworkerscript_p.h
+++ b/src/qmlworkerscript/qquickworkerscript_p.h
@@ -47,8 +47,7 @@ private:
QQuickWorkerScriptEnginePrivate *d;
};
-class QQmlV4Function;
-class Q_QMLWORKERSCRIPT_PRIVATE_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus
+class Q_QMLWORKERSCRIPT_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QQuickWorkerScript)
@@ -69,7 +68,7 @@ public:
bool ready() const;
public Q_SLOTS:
- void sendMessage(QQmlV4Function*);
+ void sendMessage(QQmlV4FunctionPtr);
Q_SIGNALS:
void sourceChanged();
@@ -91,6 +90,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickWorkerScript)
-
#endif // QQUICKWORKERSCRIPT_P_H
diff --git a/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h b/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h
index 2bcbc2ad7d..e0810f40cb 100644
--- a/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h
+++ b/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h
@@ -17,7 +17,7 @@
#include <QtQml/private/qtqmlglobal_p.h>
#include <QtQmlWorkerScript/qtqmlworkerscriptglobal.h>
-#include <QtQmlWorkerScript/private/qtqmlworkerscriptexports_p.h>
+#include <QtQmlWorkerScript/qtqmlworkerscriptexports.h>
#define Q_QMLWORKERSCRIPT_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT
diff --git a/src/qmlworkerscript/qv4serialize.cpp b/src/qmlworkerscript/qv4serialize.cpp
index 06f7828b4c..cacaf1b0dd 100644
--- a/src/qmlworkerscript/qv4serialize.cpp
+++ b/src/qmlworkerscript/qv4serialize.cpp
@@ -3,12 +3,13 @@
#include "qv4serialize_p.h"
-#include <private/qv4value_p.h>
#include <private/qv4dateobject_p.h>
-#include <private/qv4regexpobject_p.h>
-#include <private/qv4sequenceobject_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4regexp_p.h>
+#include <private/qv4regexpobject_p.h>
+#include <private/qv4sequenceobject_p.h>
+#include <private/qv4value_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qmlxmllistmodel/CMakeLists.txt b/src/qmlxmllistmodel/CMakeLists.txt
index 148c7e1099..3237ba891a 100644
--- a/src/qmlxmllistmodel/CMakeLists.txt
+++ b/src/qmlxmllistmodel/CMakeLists.txt
@@ -22,8 +22,7 @@ qt_internal_add_qml_module(QmlXmlListModel
PRIVATE_MODULE_INTERFACE
Qt::CorePrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
qt_internal_add_docs(QmlXmlListModel
doc/qtqmlxmllistmodel.qdocconf
diff --git a/src/qmlxmllistmodel/doc/qtqmlxmllistmodel.qdocconf b/src/qmlxmllistmodel/doc/qtqmlxmllistmodel.qdocconf
index f23336d133..57b8ecd51c 100644
--- a/src/qmlxmllistmodel/doc/qtqmlxmllistmodel.qdocconf
+++ b/src/qmlxmllistmodel/doc/qtqmlxmllistmodel.qdocconf
@@ -31,5 +31,5 @@ imagedirs += images
navigation.qmltypespage = "Qt XmlListModel QML Types"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/qmlxmllistmodel/qqmlxmllistmodel.cpp b/src/qmlxmllistmodel/qqmlxmllistmodel.cpp
index 95d871d7f1..b2b1d4ab33 100644
--- a/src/qmlxmllistmodel/qqmlxmllistmodel.cpp
+++ b/src/qmlxmllistmodel/qqmlxmllistmodel.cpp
@@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE
\inqmlmodule QtQml.XmlListModel
\brief For specifying a role to an \l XmlListModel.
- \sa {All QML Types}{Qt QML}
+ \sa {All QML Types}{Qt Qml}
*/
/*!
@@ -559,14 +559,12 @@ int QQmlXmlListModel::nextQueryId()
\qmlproperty enumeration QtQml.XmlListModel::XmlListModel::status
Specifies the model loading status, which can be one of the following:
- \list
- \li XmlListModel.Null - No XML data has been set for this model.
- \li XmlListModel.Ready - The XML data has been loaded into the model.
- \li XmlListModel.Loading - The model is in the process of reading and
- loading XML data.
- \li XmlListModel.Error - An error occurred while the model was loading. See
- \l errorString() for details about the error.
- \endlist
+ \value XmlListModel.Null No XML data has been set for this model.
+ \value XmlListModel.Ready The XML data has been loaded into the model.
+ \value XmlListModel.Loading The model is in the process of reading and
+ loading XML data.
+ \value XmlListModel.Error An error occurred while the model was loading. See
+ \l errorString() for details about the error.
\sa progress
*/
@@ -596,7 +594,7 @@ qreal QQmlXmlListModel::progress() const
}
/*!
- \qmlmethod QtQuick.XmlListModel::XmlListModel::errorString()
+ \qmlmethod QtQml.XmlListModel::XmlListModel::errorString()
Returns a string description of the last error that occurred
if \l status is \l {XmlListModel}.Error.
@@ -682,23 +680,9 @@ void QQmlXmlListModel::reload()
}
}
-#define XMLLISTMODEL_MAX_REDIRECT 16
-
#if QT_CONFIG(qml_network)
void QQmlXmlListModel::requestFinished()
{
- m_redirectCount++;
- if (m_redirectCount < XMLLISTMODEL_MAX_REDIRECT) {
- QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = m_reply->url().resolved(redirect.toUrl());
- deleteReply();
- setSource(url);
- return;
- }
- }
- m_redirectCount = 0;
-
if (m_reply->error() != QNetworkReply::NoError) {
m_errorString = m_reply->errorString();
deleteReply();
diff --git a/src/qmlxmllistmodel/qqmlxmllistmodel_p.h b/src/qmlxmllistmodel/qqmlxmllistmodel_p.h
index d3fe32e5be..81622d880e 100644
--- a/src/qmlxmllistmodel/qqmlxmllistmodel_p.h
+++ b/src/qmlxmllistmodel/qqmlxmllistmodel_p.h
@@ -50,13 +50,15 @@ struct QQmlXmlListModelQueryJob
};
struct QQmlXmlListModelQueryResult
{
+ Q_GADGET
QML_ANONYMOUS
+public:
int queryId;
QList<QFlatMap<int, QString>> data;
QList<QPair<void *, QString>> errors;
};
-class Q_QMLXMLLISTMODEL_PRIVATE_EXPORT QQmlXmlListModelRole : public QObject
+class Q_QMLXMLLISTMODEL_EXPORT QQmlXmlListModelRole : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
@@ -90,7 +92,7 @@ private:
class QQmlXmlListModelQueryExecutor;
-class Q_QMLXMLLISTMODEL_PRIVATE_EXPORT QQmlXmlListModel : public QAbstractListModel,
+class Q_QMLXMLLISTMODEL_EXPORT QQmlXmlListModel : public QAbstractListModel,
public QQmlParserStatus
{
Q_OBJECT
@@ -188,7 +190,6 @@ private:
qreal m_progress = 0;
int m_queryId = -1;
int m_nextQueryIdGenerator = -1;
- int m_redirectCount = 0;
int m_highestRole = Qt::UserRole;
using ResultFutureWatcher = QFutureWatcher<QQmlXmlListModelQueryResult>;
QFlatMap<int, ResultFutureWatcher *> m_watchers;
diff --git a/src/qmlxmllistmodel/qtqmlxmllistmodelglobal_p.h b/src/qmlxmllistmodel/qtqmlxmllistmodelglobal_p.h
index de855a0dee..d809e76e99 100644
--- a/src/qmlxmllistmodel/qtqmlxmllistmodelglobal_p.h
+++ b/src/qmlxmllistmodel/qtqmlxmllistmodelglobal_p.h
@@ -16,6 +16,6 @@
//
#include <QtCore/qglobal.h>
-#include <QtQmlXmlListModel/private/qtqmlxmllistmodelexports_p.h>
+#include <QtQmlXmlListModel/qtqmlxmllistmodelexports.h>
#endif // QTQMLXMLLISTMODELGLOBAL_P_H
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index 5819b5f1c8..86edb893c2 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -75,6 +75,7 @@ qt_internal_add_qml_module(Quick
items/qquickrectangle_p_p.h
items/qquickrendercontrol.cpp items/qquickrendercontrol.h items/qquickrendercontrol_p.h
items/qquickrendertarget.cpp items/qquickrendertarget.h items/qquickrendertarget_p.h
+ items/qquickrhiitem.cpp items/qquickrhiitem.h items/qquickrhiitem_p.h
items/qquickscalegrid.cpp
items/qquickscalegrid_p_p.h
items/qquickscreen.cpp items/qquickscreen_p.h
@@ -89,13 +90,14 @@ qt_internal_add_qml_module(Quick
items/qquicktextedit_p_p.h
items/qquicktextinput.cpp items/qquicktextinput_p.h
items/qquicktextinput_p_p.h
- items/qquicktextnode.cpp items/qquicktextnode_p.h
+ items/qsginternaltextnode.cpp items/qsginternaltextnode_p.h
items/qquicktextnodeengine.cpp items/qquicktextnodeengine_p.h
items/qquicktextutil.cpp items/qquicktextutil_p.h
items/qquicktranslate.cpp items/qquicktranslate_p.h
items/qquickview.cpp items/qquickview.h items/qquickview_p.h
items/qquickwindow.cpp items/qquickwindow.h items/qquickwindow_p.h
items/qquickwindowattached.cpp items/qquickwindowattached_p.h
+ items/qquickwindowcontainer.cpp items/qquickwindowcontainer_p.h
items/qquickwindowmodule.cpp items/qquickwindowmodule_p.h
items/qquickwindowmodule_p_p.h
qtquickglobal.h qtquickglobal_p.h
@@ -133,13 +135,18 @@ qt_internal_add_qml_module(Quick
scenegraph/coreapi/qsgtexture.cpp scenegraph/coreapi/qsgtexture.h scenegraph/coreapi/qsgtexture_p.h
scenegraph/coreapi/qsgtexture_platform.h
scenegraph/qsgadaptationlayer.cpp scenegraph/qsgadaptationlayer_p.h
+ scenegraph/qsgcurveabstractnode_p.h
scenegraph/qsgbasicglyphnode.cpp scenegraph/qsgbasicglyphnode_p.h
scenegraph/qsgbasicinternalimagenode.cpp scenegraph/qsgbasicinternalimagenode_p.h
scenegraph/qsgbasicinternalrectanglenode.cpp scenegraph/qsgbasicinternalrectanglenode_p.h
scenegraph/qsgcontext.cpp scenegraph/qsgcontext_p.h
scenegraph/qsgcontextplugin.cpp scenegraph/qsgcontextplugin_p.h
+ scenegraph/qsgcurvefillnode.cpp scenegraph/qsgcurvefillnode_p.cpp scenegraph/qsgcurvefillnode_p.h scenegraph/qsgcurvefillnode_p_p.h
+ scenegraph/qsgcurvestrokenode.cpp scenegraph/qsgcurvestrokenode_p.cpp scenegraph/qsgcurvestrokenode_p.h scenegraph/qsgcurvestrokenode_p_p.h
scenegraph/qsgdefaultcontext.cpp scenegraph/qsgdefaultcontext_p.h
scenegraph/qsgdefaultglyphnode.cpp scenegraph/qsgdefaultglyphnode_p.cpp scenegraph/qsgdefaultglyphnode_p.h
+ scenegraph/qsgcurveglyphatlas.cpp scenegraph/qsgcurveglyphatlas_p.h
+ scenegraph/qsgcurveglyphnode.cpp scenegraph/qsgcurveglyphnode_p.h
scenegraph/qsgdefaultglyphnode_p_p.h
scenegraph/qsgdefaultinternalimagenode.cpp scenegraph/qsgdefaultinternalimagenode_p.h
scenegraph/qsgdefaultinternalrectanglenode.cpp scenegraph/qsgdefaultinternalrectanglenode_p.h
@@ -148,10 +155,12 @@ qt_internal_add_qml_module(Quick
scenegraph/qsgdistancefieldglyphnode_p_p.h
scenegraph/qsgrenderloop.cpp scenegraph/qsgrenderloop_p.h
scenegraph/qsgrhidistancefieldglyphcache.cpp scenegraph/qsgrhidistancefieldglyphcache_p.h
+ scenegraph/qsgrhiinternaltextnode.cpp scenegraph/qsgrhiinternaltextnode_p.h
scenegraph/qsgrhilayer.cpp scenegraph/qsgrhilayer_p.h
scenegraph/qsgrhishadereffectnode.cpp scenegraph/qsgrhishadereffectnode_p.h
scenegraph/qsgrhisupport.cpp scenegraph/qsgrhisupport_p.h
scenegraph/qsgrhitextureglyphcache.cpp scenegraph/qsgrhitextureglyphcache_p.h
+ scenegraph/qsgcurveprocessor.cpp scenegraph/qsgcurveprocessor_p.h
scenegraph/util/qsgareaallocator.cpp scenegraph/util/qsgareaallocator_p.h
scenegraph/util/qsgdefaultimagenode.cpp scenegraph/util/qsgdefaultimagenode_p.h
scenegraph/util/qsgdefaultninepatchnode.cpp scenegraph/util/qsgdefaultninepatchnode_p.h
@@ -165,10 +174,13 @@ qt_internal_add_qml_module(Quick
scenegraph/util/qsgrhiatlastexture.cpp scenegraph/util/qsgrhiatlastexture_p.h
scenegraph/util/qsgsimplerectnode.cpp scenegraph/util/qsgsimplerectnode.h
scenegraph/util/qsgsimpletexturenode.cpp scenegraph/util/qsgsimpletexturenode.h
+ scenegraph/util/qsgtextnode.cpp scenegraph/util/qsgtexturematerial.h scenegraph/util/qsgtextnode.h
scenegraph/util/qsgtexturematerial.cpp scenegraph/util/qsgtexturematerial.h scenegraph/util/qsgtexturematerial_p.h
scenegraph/util/qsgtextureprovider.cpp scenegraph/util/qsgtextureprovider.h
scenegraph/util/qsgtexturereader.cpp scenegraph/util/qsgtexturereader_p.h
scenegraph/util/qsgvertexcolormaterial.cpp scenegraph/util/qsgvertexcolormaterial.h
+ scenegraph/util/qquadpath.cpp scenegraph/util/qquadpath_p.h
+ scenegraph/util/qsggradientcache.cpp scenegraph/util/qsggradientcache_p.h
util/qminimalflatset_p.h
util/qquickanimation.cpp util/qquickanimation_p.h
util/qquickanimation_p_p.h
@@ -185,6 +197,7 @@ qt_internal_add_qml_module(Quick
util/qquickforeignutils.cpp util/qquickforeignutils_p.h
util/qquickglobal.cpp
util/qquickimageprovider.cpp util/qquickimageprovider.h util/qquickimageprovider_p.h
+ util/qquickpixmap_p.h
util/qquickpixmapcache.cpp util/qquickpixmapcache_p.h
util/qquickprofiler_p.h
util/qquickpropertychanges.cpp util/qquickpropertychanges_p.h
@@ -199,6 +212,7 @@ qt_internal_add_qml_module(Quick
util/qquicksvgparser.cpp util/qquicksvgparser_p.h
util/qquicksystempalette.cpp util/qquicksystempalette_p.h
util/qquicktextmetrics.cpp util/qquicktextmetrics_p.h
+ util/qquicktextselection.cpp util/qquicktextselection_p.h
util/qquicktimeline.cpp
util/qquicktimeline_p_p.h
util/qquicktransition.cpp util/qquicktransition_p.h
@@ -207,6 +221,11 @@ qt_internal_add_qml_module(Quick
util/qquickvalidator.cpp util/qquickvalidator_p.h
util/qquickvaluetypes.cpp util/qquickvaluetypes_p.h
util/qquickframeanimation.cpp util/qquickframeanimation_p.h
+ NO_UNITY_BUILD_SOURCES
+ scenegraph/qsgdefaultcontext.cpp # redefinition of 'qmlDisableDistanceField' (from qquicktextinput.cpp)
+ scenegraph/util/qsgtexturematerial.cpp # redefinition of 'isPowerOfTwo' (from qsgdefaultinternalimagenode.cpp)
+ util/qquickvalidator.cpp # expl. spec. of 'QMetaTypeId<QValidator *>' after inst.
+ quick_qmltyperegistrations.cpp # qquickvalidator_p.h: error: explicit specialization of 'QMetaTypeId<QValidator *>' after instantiation
DEFINES
QT_NO_FOREACH
QT_NO_INTEGER_EVENT_COORDINATES
@@ -229,8 +248,7 @@ qt_internal_add_qml_module(Quick
Qt::QmlModelsPrivate
Qt::QmlPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
# We need to do additional initialization, so we have to provide our own
# plugin class rather than using the generated one
@@ -244,6 +262,7 @@ qt_internal_add_shaders(Quick "scenegraph_shaders"
BATCHABLE
PRECOMPILE
OPTIMIZED
+ MULTIVIEW
PREFIX
"/qt-project.org"
FILES
@@ -302,6 +321,149 @@ qt_internal_add_shaders(Quick "scenegraph_shaders"
"scenegraph/shaders_ng/visualization.vert"
)
+qt_internal_add_shaders(Quick "scenegraph_curve_shaders"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ PREFIX
+ "/qt-project.org"
+ FILES
+ "scenegraph/shaders_ng/shapecurve.frag"
+ "scenegraph/shaders_ng/shapecurve.vert"
+ "scenegraph/shaders_ng/shapestroke.frag"
+ "scenegraph/shaders_ng/shapestroke.vert"
+)
+
+qt_internal_add_shaders(Quick "scenegraph_curve_shaders_derivatives"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ DEFINES "USE_DERIVATIVES"
+ PREFIX
+ "/qt-project.org"
+ FILES
+ "scenegraph/shaders_ng/shapecurve.frag"
+ "scenegraph/shaders_ng/shapecurve.vert"
+ OUTPUTS
+ "scenegraph/shaders_ng/shapecurve_derivatives.frag.qsb"
+ "scenegraph/shaders_ng/shapecurve_derivatives.vert.qsb"
+)
+
+qt_internal_add_shaders(Quick "scenegraph_curve_shaders_lg"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ DEFINES
+ "LINEARGRADIENT"
+ PREFIX
+ "/qt-project.org"
+ FILES
+ "scenegraph/shaders_ng/shapecurve.frag"
+ "scenegraph/shaders_ng/shapecurve.vert"
+ OUTPUTS
+ "scenegraph/shaders_ng/shapecurve_lg.frag.qsb"
+ "scenegraph/shaders_ng/shapecurve_lg.vert.qsb"
+)
+
+qt_internal_add_shaders(Quick "scenegraph_curve_shaders_lg_derivatives"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ DEFINES
+ "LINEARGRADIENT"
+ "USE_DERIVATIVES"
+ PREFIX
+ "/qt-project.org"
+ FILES
+ "scenegraph/shaders_ng/shapecurve.frag"
+ "scenegraph/shaders_ng/shapecurve.vert"
+ OUTPUTS
+ "scenegraph/shaders_ng/shapecurve_lg_derivatives.frag.qsb"
+ "scenegraph/shaders_ng/shapecurve_lg_derivatives.vert.qsb"
+)
+
+qt_internal_add_shaders(Quick "scenegraph_curve_shaders_rg"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ DEFINES
+ "RADIALGRADIENT"
+ PREFIX
+ "/qt-project.org"
+ FILES
+ "scenegraph/shaders_ng/shapecurve.frag"
+ "scenegraph/shaders_ng/shapecurve.vert"
+ OUTPUTS
+ "scenegraph/shaders_ng/shapecurve_rg.frag.qsb"
+ "scenegraph/shaders_ng/shapecurve_rg.vert.qsb"
+)
+
+qt_internal_add_shaders(Quick "scenegraph_curve_shaders_rg_derivatives"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ DEFINES
+ "RADIALGRADIENT"
+ "USE_DERIVATIVES"
+ PREFIX
+ "/qt-project.org"
+ FILES
+ "scenegraph/shaders_ng/shapecurve.frag"
+ "scenegraph/shaders_ng/shapecurve.vert"
+ OUTPUTS
+ "scenegraph/shaders_ng/shapecurve_rg_derivatives.frag.qsb"
+ "scenegraph/shaders_ng/shapecurve_rg_derivatives.vert.qsb"
+)
+
+qt_internal_add_shaders(Quick "scenegraph_curve_shaders_cg"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ DEFINES
+ "CONICALGRADIENT"
+ PREFIX
+ "/qt-project.org"
+ FILES
+ "scenegraph/shaders_ng/shapecurve.frag"
+ "scenegraph/shaders_ng/shapecurve.vert"
+ OUTPUTS
+ "scenegraph/shaders_ng/shapecurve_cg.frag.qsb"
+ "scenegraph/shaders_ng/shapecurve_cg.vert.qsb"
+)
+
+qt_internal_add_shaders(Quick "scenegraph_curve_shaders_cg_derivatives"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
+ DEFINES
+ "CONICALGRADIENT"
+ "USE_DERIVATIVES"
+ PREFIX
+ "/qt-project.org"
+ FILES
+ "scenegraph/shaders_ng/shapecurve.frag"
+ "scenegraph/shaders_ng/shapecurve.vert"
+ OUTPUTS
+ "scenegraph/shaders_ng/shapecurve_cg_derivatives.frag.qsb"
+ "scenegraph/shaders_ng/shapecurve_cg_derivatives.vert.qsb"
+)
+
qt_internal_extend_target(Quick CONDITION QT_FEATURE_qml_network
LIBRARIES
Qt::Network
@@ -391,12 +553,23 @@ qt_internal_extend_target(Quick CONDITION QT_FEATURE_opengl OR QT_FEATURE_opengl
util/qquickopenglutils.cpp util/qquickopenglutils.h
)
-qt_internal_extend_target(Quick CONDITION IOS OR MACOS
+qt_internal_extend_target(Quick CONDITION QT_FEATURE_metal
SOURCES
scenegraph/coreapi/qsgtexture_mac.mm
scenegraph/qsgrhisupport_mac.mm
)
+qt_internal_extend_target(Quick CONDITION ANDROID
+ SOURCES
+ platform/android/qandroidquickviewembedding.cpp platform/android/qandroidquickviewembedding_p.h
+)
+if (ANDROID)
+ add_subdirectory(jar)
+ set_property(TARGET Quick PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
+ jar/Qt${QtDeclarative_VERSION_MAJOR}AndroidQuick.jar
+ )
+endif()
+
qt_internal_extend_target(Quick CONDITION QT_FEATURE_thread
SOURCES
scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
@@ -514,7 +687,14 @@ qt_internal_extend_target(Quick CONDITION QT_FEATURE_im
util/qquickinputmethod.cpp util/qquickinputmethod_p.h
)
-qt_internal_create_tracepoints(Quick qtquick.tracepoints)
+qt_internal_generate_tracepoints(Quick quick
+ SOURCES
+ scenegraph/qsgrenderloop.cpp
+ scenegraph/coreapi/qsgrenderer.cpp
+ scenegraph/qsgthreadedrenderloop.cpp
+ scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
+ scenegraph/qsgadaptationlayer.cpp
+)
qt_internal_add_docs(Quick
doc/qtquick.qdocconf
)
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index 4a446e468d..514b1a9214 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -456,7 +456,7 @@ QAccessible::Role QAccessibleQuickItem::role() const
QAccessible::Role role = QAccessible::NoRole;
if (item())
- role = QQuickItemPrivate::get(item())->accessibleRole();
+ role = QQuickItemPrivate::get(item())->effectiveAccessibleRole();
if (role == QAccessible::NoRole) {
if (qobject_cast<QQuickText*>(const_cast<QQuickItem *>(item())))
role = QAccessible::StaticText;
@@ -622,6 +622,8 @@ QString QAccessibleQuickItem::text(QAccessible::Text textType) const
// the following block handles item-specific behavior
if (role() == QAccessible::EditableText) {
if (textType == QAccessible::Value) {
+ if (auto textInput = qobject_cast<QQuickTextInput *>(item()))
+ return textInput->displayText();
if (QTextDocument *doc = textDocument()) {
return doc->toPlainText();
}
diff --git a/src/quick/accessible/qaccessiblequickitem_p.h b/src/quick/accessible/qaccessiblequickitem_p.h
index 9239295175..50fd09a5c0 100644
--- a/src/quick/accessible/qaccessiblequickitem_p.h
+++ b/src/quick/accessible/qaccessiblequickitem_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QTextDocument;
-class Q_QUICK_PRIVATE_EXPORT QAccessibleQuickItem : public QAccessibleObject, public QAccessibleActionInterface, public QAccessibleValueInterface, public QAccessibleTextInterface
+class Q_QUICK_EXPORT QAccessibleQuickItem : public QAccessibleObject, public QAccessibleActionInterface, public QAccessibleValueInterface, public QAccessibleTextInterface
{
public:
QAccessibleQuickItem(QQuickItem *item);
diff --git a/src/quick/accessible/qaccessiblequickview.cpp b/src/quick/accessible/qaccessiblequickview.cpp
index 5cd93b9613..08f5889070 100644
--- a/src/quick/accessible/qaccessiblequickview.cpp
+++ b/src/quick/accessible/qaccessiblequickview.cpp
@@ -21,7 +21,7 @@ QAccessibleQuickWindow::QAccessibleQuickWindow(QQuickWindow *object)
QList<QQuickItem *> QAccessibleQuickWindow::rootItems() const
{
- if (QQuickItem *ci = window()->contentItem())
+ if (QQuickItem *ci = window() ? window()->contentItem() : nullptr)
return accessibleUnignoredChildren(ci);
return QList<QQuickItem *>();
}
@@ -47,7 +47,7 @@ QAccessibleInterface *QAccessibleQuickWindow::child(int index) const
QAccessibleInterface *QAccessibleQuickWindow::focusChild() const
{
- QObject *focusObject = window()->focusObject();
+ QObject *focusObject = window() ? window()->focusObject() : nullptr;
if (focusObject) {
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(focusObject);
if (!iface || iface == this || !iface->focusChild())
@@ -67,18 +67,22 @@ QAccessible::State QAccessibleQuickWindow::state() const
QAccessible::State st;
if (window() == QGuiApplication::focusWindow())
st.active = true;
- if (!window()->isVisible())
+ if (!window() || !window()->isVisible())
st.invisible = true;
return st;
}
QRect QAccessibleQuickWindow::rect() const
{
+ if (!window())
+ return {};
return QRect(window()->x(), window()->y(), window()->width(), window()->height());
}
QString QAccessibleQuickWindow::text(QAccessible::Text text) const
{
+ if (!window())
+ return {};
#ifdef Q_ACCESSIBLE_QUICK_ITEM_ENABLE_DEBUG_DESCRIPTION
if (text == QAccessible::DebugDescription) {
return QString::fromLatin1(object()->metaObject()->className()) ;
diff --git a/src/quick/configure.cmake b/src/quick/configure.cmake
index ad4c645983..99dcc24569 100644
--- a/src/quick/configure.cmake
+++ b/src/quick/configure.cmake
@@ -119,6 +119,13 @@ qt_feature("quick-draganddrop" PUBLIC
PURPOSE "Drag and drop support for Qt Quick"
CONDITION ( QT_FEATURE_draganddrop ) AND ( QT_FEATURE_regularexpression )
)
+
+qt_feature("quick-pixmap-cache-threaded-download" PUBLIC
+ SECTION "Qt Quick"
+ LABEL "Threaded download in pixmap cache"
+ PURPOSE "Pixmap cache pixmap downloads on separate threads"
+ CONDITION ( QT_FEATURE_thread ) AND ( NOT WASM )
+)
qt_configure_add_summary_section(NAME "Qt Quick")
qt_configure_add_summary_entry(ARGS "quick-animatedimage")
qt_configure_add_summary_entry(ARGS "quick-canvas")
diff --git a/src/quick/designer/qqmldesignermetaobject_p.h b/src/quick/designer/qqmldesignermetaobject_p.h
index 71623f46ab..7fd8e77387 100644
--- a/src/quick/designer/qqmldesignermetaobject_p.h
+++ b/src/quick/designer/qqmldesignermetaobject_p.h
@@ -22,6 +22,8 @@
#include <private/qqmlopenmetaobject_p.h>
#include <private/qqmlvmemetaobject_p.h>
+#include <QtCore/qpointer.h>
+
#include <memory>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/designer/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp
index 5fccd43e96..6f937e38d6 100644
--- a/src/quick/designer/qquickdesignersupport.cpp
+++ b/src/quick/designer/qquickdesignersupport.cpp
@@ -139,7 +139,7 @@ QTransform QQuickDesignerSupport::parentTransform(QQuickItem *referencedItem)
QTransform parentTransform;
- QQuickItemPrivate::get(referencedItem)->itemToParentTransform(parentTransform);
+ QQuickItemPrivate::get(referencedItem)->itemToParentTransform(&parentTransform);
return parentTransform;
}
diff --git a/src/quick/doc/images/declarative-scalegrid.png b/src/quick/doc/images/declarative-scalegrid.png
index 32d8712568..69ad06f834 100644
--- a/src/quick/doc/images/declarative-scalegrid.png
+++ b/src/quick/doc/images/declarative-scalegrid.png
Binary files differ
diff --git a/src/quick/doc/images/how-to-time-picker-dark.png b/src/quick/doc/images/how-to-time-picker-dark.png
new file mode 100644
index 0000000000..87fa6bd2cd
--- /dev/null
+++ b/src/quick/doc/images/how-to-time-picker-dark.png
Binary files differ
diff --git a/src/quick/doc/images/how-to-time-picker-light.png b/src/quick/doc/images/how-to-time-picker-light.png
new file mode 100644
index 0000000000..6eb1aba2ef
--- /dev/null
+++ b/src/quick/doc/images/how-to-time-picker-light.png
Binary files differ
diff --git a/src/quick/doc/images/pinchAndDragHandlers-drag-one-rect.png b/src/quick/doc/images/pinchAndDragHandlers-drag-one-rect.png
new file mode 100644
index 0000000000..0c9482d8f7
--- /dev/null
+++ b/src/quick/doc/images/pinchAndDragHandlers-drag-one-rect.png
Binary files differ
diff --git a/src/quick/doc/images/pinchAndDragHandlers-drag-two-rects.png b/src/quick/doc/images/pinchAndDragHandlers-drag-two-rects.png
new file mode 100644
index 0000000000..494d92e836
--- /dev/null
+++ b/src/quick/doc/images/pinchAndDragHandlers-drag-two-rects.png
Binary files differ
diff --git a/src/quick/doc/images/pinchAndDragHandlers-pinch.png b/src/quick/doc/images/pinchAndDragHandlers-pinch.png
new file mode 100644
index 0000000000..ed21a74c38
--- /dev/null
+++ b/src/quick/doc/images/pinchAndDragHandlers-pinch.png
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlers/dragReleaseMenu.webp b/src/quick/doc/images/pointerHandlers/dragReleaseMenu.webp
new file mode 100644
index 0000000000..16aaca4d86
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/dragReleaseMenu.webp
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp b/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
new file mode 100644
index 0000000000..3edab7d7a1
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp b/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp
new file mode 100644
index 0000000000..05cb2f2276
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp b/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp
new file mode 100644
index 0000000000..0455097159
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp
Binary files differ
diff --git a/src/quick/doc/images/qml-borderimage-rounded.png b/src/quick/doc/images/qml-borderimage-rounded.png
new file mode 100644
index 0000000000..562fc277bb
--- /dev/null
+++ b/src/quick/doc/images/qml-borderimage-rounded.png
Binary files differ
diff --git a/src/quick/doc/images/qml-borderimage-scaled.png b/src/quick/doc/images/qml-borderimage-scaled.png
index e42891ecc7..230e8a010e 100644
--- a/src/quick/doc/images/qml-borderimage-scaled.png
+++ b/src/quick/doc/images/qml-borderimage-scaled.png
Binary files differ
diff --git a/src/quick/doc/images/qml-borderimage-tiled.png b/src/quick/doc/images/qml-borderimage-tiled.png
index e27d9601c4..f7523e8e94 100644
--- a/src/quick/doc/images/qml-borderimage-tiled.png
+++ b/src/quick/doc/images/qml-borderimage-tiled.png
Binary files differ
diff --git a/src/quick/doc/images/qml-item-canvas-startAngle.png b/src/quick/doc/images/qml-item-canvas-startAngle.png
index bf82c3aa4b..7930284896 100644
--- a/src/quick/doc/images/qml-item-canvas-startAngle.png
+++ b/src/quick/doc/images/qml-item-canvas-startAngle.png
Binary files differ
diff --git a/src/quick/doc/images/simpleProxy.png b/src/quick/doc/images/simpleProxy.png
new file mode 100644
index 0000000000..f99898dca8
--- /dev/null
+++ b/src/quick/doc/images/simpleProxy.png
Binary files differ
diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf
index 02e12c4c11..deda199b96 100644
--- a/src/quick/doc/qtquick.qdocconf
+++ b/src/quick/doc/qtquick.qdocconf
@@ -37,10 +37,12 @@ tagfile = qtquick.tags
depends += \
qtcore \
+ qtopengl \
qtqml \
qtqmlmodels \
qtqmlxmllistmodel \
qtqmltest \
+ qtqmlcore \
qtgui \
qtlinguist \
qtquickcontrols \
@@ -51,8 +53,10 @@ depends += \
qmake \
qtsql \
qtshadertools \
+ qtsvg \
qmake \
- qtcmake
+ qtcmake \
+ qtquickeffectmaker
{headerdirs,sourcedirs} += \
.. \
@@ -60,7 +64,9 @@ depends += \
../../quickwidgets \
../../qmllocalstorage \
../../quicklayouts \
- ../../labs
+ ../../labs \
+ ../../quick/jar/org/qtproject/qt/android \
+ ../../../examples/platforms
# both have their own documentation project
excludedirs += \
@@ -70,7 +76,10 @@ excludedirs += \
exampledirs += \
../../../examples/quick \
../../qmlmodels/doc/snippets \
- snippets
+ ../../quickcontrols/doc/snippets \
+ snippets \
+ ../../../tests/auto/quick/doc \
+ ../../../examples/platforms
imagedirs += images
@@ -78,7 +87,8 @@ imagedirs += images
{headerdirs,sourcedirs} += \
../../particles \
../../quickshapes \
- ../../effects
+ ../../effects \
+ ../../quickvectorimage
# Add imports and plugins directories because of dependencies
{headerdirs,sourcedirs} += \
@@ -86,12 +96,16 @@ imagedirs += images
../../plugins
excludefiles += ../util/qquickpropertychanges_p.h
-examples.fileextensions += "*.qm"
+examples.fileextensions += "*.qm" \
+ "*.java" \
+ "*.kt"
-manifestmeta.thumbnail.names += "QtQuick/Threaded ListModel Example" \
- "QtQuick/QML Dynamic View Ordering Tutorial*"
+manifestmeta.thumbnail.names += "QtQuick/QML Dynamic View Ordering Tutorial*"
-manifestmeta.highlighted.names = "QtQuick/Qt Quick Demo - Same Game"
+# Highlight examples for the Graphics & Multimedia category
+manifestmeta.highlighted.names = \
+ "QtQuick/Qt Quick Layouts - Responsive Layout Example" \
+ "QtQuick/Scene Graph - RHI Under QML"
navigation.landingpage = "Qt Quick"
navigation.cppclassespage = "Qt Quick C++ Classes"
@@ -99,12 +113,15 @@ navigation.qmltypespage = "Qt Quick QML Types"
# \svgcolor {#ffdead}
macro.svgcolor.HTML = "<div style=\"padding:10px;color:#fff;background:\1;\"></div>"
+macro.svgcolor.DocBook = "<db:phrase role=\"color:\1\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</db:phrase>"
+
+macro.QQEM = "Qt Quick Effect Maker"
# YouTube video thumbnail that show up in offline docs
-{HTML.extraimages,qhp.QtQuick.extraFiles} += images/9BcAYDlpuT8.jpg
+{HTML.extraimages,DocBook.extraFiles,qhp.QtQuick.extraFiles} += images/9BcAYDlpuT8.jpg
# suppress qdoc warnings for \instantiates entries
spurious += "C\\+\\+ class .*\\\\instantiates .*"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/quick/doc/snippets/code/doc_src_qtquick.cmake b/src/quick/doc/snippets/code/doc_src_qtquick.cmake
index 5bbb4ca0b0..e1ebae4021 100644
--- a/src/quick/doc/snippets/code/doc_src_qtquick.cmake
+++ b/src/quick/doc/snippets/code/doc_src_qtquick.cmake
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#! [0]
find_package(Qt6 REQUIRED COMPONENTS Quick)
diff --git a/src/quick/doc/snippets/imgprovider/imageprovider-example.qml b/src/quick/doc/snippets/imgprovider/imageprovider-example.qml
index db65a3bc41..05f8e4375b 100644
--- a/src/quick/doc/snippets/imgprovider/imageprovider-example.qml
+++ b/src/quick/doc/snippets/imgprovider/imageprovider-example.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Column {
Image { source: "image://colors/yellow" }
diff --git a/src/quick/doc/snippets/layouts/responsiveDeclarative.qml b/src/quick/doc/snippets/layouts/responsiveDeclarative.qml
new file mode 100644
index 0000000000..e5a0c6c4f4
--- /dev/null
+++ b/src/quick/doc/snippets/layouts/responsiveDeclarative.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+import QtQuick.Controls
+
+Window {
+ visible: true
+ width: 350
+ height: 250
+ //! [document]
+ GridLayout {
+ columns: width < 300 ? 1 : 2
+ anchors.fill: parent
+
+ Rectangle {
+ id: rectangle1
+ color: "tomato"
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+
+ Rectangle {
+ id: rectangle2
+ color: "lightskyblue"
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ }
+ //! [document]
+}
diff --git a/src/quick/doc/snippets/layouts/responsiveStates.qml b/src/quick/doc/snippets/layouts/responsiveStates.qml
new file mode 100644
index 0000000000..5506407230
--- /dev/null
+++ b/src/quick/doc/snippets/layouts/responsiveStates.qml
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+
+Window {
+ visible: true
+ width: 350
+ height: 250
+ //! [document]
+ GridLayout {
+ anchors.fill: parent
+
+ Rectangle {
+ id: rectangle1
+ color: "tomato"
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+
+ Rectangle {
+ id: rectangle2
+ color: "lightskyblue"
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+
+ states: [
+ State {
+ when: width < 300
+ PropertyChanges { target: rectangle2; Layout.row: 1 }
+ PropertyChanges { target: rectangle2; Layout.column: 0 }
+ },
+ State {
+ when: width >= 300
+ PropertyChanges { target: rectangle2; Layout.row: 0 }
+ PropertyChanges { target: rectangle2; Layout.column: 1 }
+ }
+ ]
+ }
+ //! [document]
+}
diff --git a/src/quick/doc/snippets/layouts/simpleProxy.qml b/src/quick/doc/snippets/layouts/simpleProxy.qml
new file mode 100644
index 0000000000..460e6bda32
--- /dev/null
+++ b/src/quick/doc/snippets/layouts/simpleProxy.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+import QtQuick.Controls
+
+Window {
+ visible: true
+
+ width: 350
+ //! [document]
+ //! [item definition]
+ Rectangle {
+ id: rectangle1
+ color: "tomato"
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+
+ Rectangle {
+ id: rectangle2
+ color: "lightskyblue"
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ //! [item definition]
+
+ //! [layout definition]
+ GridLayout {
+ id: l1
+ columns: 1
+ visible: false
+ anchors.fill: parent
+ LayoutItemProxy { target: rectangle1 }
+ LayoutItemProxy { target: rectangle2 }
+ }
+
+ GridLayout {
+ id: l2
+ columns: 2
+ visible: true
+ anchors.fill: parent
+ LayoutItemProxy { target: rectangle1 }
+ LayoutItemProxy { target: rectangle2 }
+ }
+ //! [layout definition]
+
+ //! [layout choice]
+ onWidthChanged: {
+ if (width < 300) {
+ l2.visible = false
+ l1.visible = true
+ } else {
+ l1.visible = false
+ l2.visible = true
+ }
+ }
+ //! [layout choice]
+ //! [document]
+}
diff --git a/src/quick/doc/snippets/pointerHandlers/dragHandler.qml b/src/quick/doc/snippets/pointerHandlers/dragHandler.qml
index 6c694f6512..5a8f5d2a37 100644
--- a/src/quick/doc/snippets/pointerHandlers/dragHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/dragHandler.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
Rectangle {
width: 100
diff --git a/src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml b/src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml
index 2d2bb78c07..103e4febff 100644
--- a/src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml
+++ b/src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
Item {
width: 640
diff --git a/src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml b/src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml
index 39765fd4b8..a2e2ae7c04 100644
--- a/src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml
+++ b/src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
Item {
width: 640
diff --git a/src/quick/doc/snippets/pointerHandlers/dragReleaseMenu.qml b/src/quick/doc/snippets/pointerHandlers/dragReleaseMenu.qml
new file mode 100644
index 0000000000..e6d2266408
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/dragReleaseMenu.qml
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//![0]
+import QtQuick
+
+Rectangle {
+ id: rect
+ width: 140; height: 100
+
+ //![1]
+ TapHandler {
+ id: menuPopupHandler
+ gesturePolicy: TapHandler.DragWithinBounds
+ onPressedChanged:
+ if (pressed) {
+ menu.x = point.position.x - menu.width / 2
+ menu.y = point.position.y - menu.height / 2
+ } else {
+ feedback.text = menu.highlightedMenuItem
+ selectFlash.start()
+ }
+ onCanceled: feedback.text = "canceled"
+ }
+ //![1]
+
+ Column {
+ id: menu
+ visible: menuPopupHandler.pressed
+ opacity: Math.min(1, menuPopupHandler.timeHeld)
+ property string highlightedMenuItem: ""
+ Repeater {
+ model: [ "top", "middle", "bottom" ]
+ delegate: Rectangle {
+ property bool highlighted: menuPopupHandler.pressed &&
+ contains(mapFromItem(rect, menuPopupHandler.point.position))
+ onHighlightedChanged: {
+ if (highlighted)
+ menu.highlightedMenuItem = menuItemText.text
+ else if (menu.highlightedMenuItem === menuItemText.text)
+ menu.highlightedMenuItem = ""
+ }
+ width: 100
+ height: 20
+ color: highlighted ? "lightsteelblue" : "aliceblue"
+ Text {
+ id: menuItemText
+ anchors.centerIn: parent
+ text: modelData
+ }
+ }
+ }
+ }
+
+ Text {
+ id: feedback
+ y: 6; anchors.horizontalCenter: parent.horizontalCenter
+ textFormat: Text.MarkdownText
+ text: "hold for context menu"
+
+ SequentialAnimation on font.weight {
+ id: selectFlash
+ running: false
+ loops: 3
+ PropertyAction { value: Font.Black }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: Font.Normal }
+ PauseAnimation { duration: 100 }
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml b/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml
index 601e94fc5d..83ab245244 100644
--- a/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml
+++ b/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.14
-import Qt.labs.animation 1.0
+import QtQuick
+import Qt.labs.animation
Item {
width: 320; height: 480
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml b/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml
index f79133df8d..40c9367f6a 100644
--- a/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml
+++ b/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml
@@ -15,7 +15,7 @@ Rectangle {
HoverHandler {
id: mouse
- acceptedDevices: PointerDevice.Mouse
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
cursorShape: Qt.PointingHandCursor
}
}
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml b/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
index a72bbf8232..a301547b07 100644
--- a/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
+++ b/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
Rectangle {
id: button
diff --git a/src/quick/doc/snippets/pointerHandlers/pinchAndDragHandlers.qml b/src/quick/doc/snippets/pointerHandlers/pinchAndDragHandlers.qml
new file mode 100644
index 0000000000..a6b990ac62
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pinchAndDragHandlers.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![entire]
+import QtQuick
+
+Rectangle {
+ id: root
+ width: 400
+ height: 400
+ color: ph.active ? "aquamarine" : "beige"
+
+ PinchHandler {
+ id: ph
+ grabPermissions: PointerHandler.TakeOverForbidden
+ }
+
+ Rectangle {
+ objectName: "rect1"
+ x: 50
+ width: 100
+ height: 100
+ color: dh1.active ? "tomato" : "wheat"
+ DragHandler {
+ id: dh1
+ objectName: "dh1"
+ }
+ }
+
+ Rectangle {
+ objectName: "rect2"
+ x: 250
+ width: 100
+ height: 100
+ color: dh2.active ? "tomato" : "lightsteelblue"
+ DragHandler {
+ id: dh2
+ objectName: "dh2"
+ }
+ }
+
+ Rectangle {
+ objectName: "rect3"
+ x: 150
+ y: 150
+ width: 100
+ height: 100
+ color: dh3.active ? "tomato" : "darksalmon"
+ DragHandler {
+ id: dh3
+ objectName: "dh3"
+ }
+ }
+}
+//![entire]
diff --git a/src/quick/doc/snippets/pointerHandlers/pinchHandler.qml b/src/quick/doc/snippets/pointerHandlers/pinchHandler.qml
index 5027204ff6..e25d23da2a 100644
--- a/src/quick/doc/snippets/pointerHandlers/pinchHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pinchHandler.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
Rectangle {
width: 400
diff --git a/src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml b/src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml
index 92d26b8e67..7bff2e0a70 100644
--- a/src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
Item {
width: 640
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandler.qml b/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
index f5fe29566e..d363150ab5 100644
--- a/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
@@ -1,8 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
-import QtQuick.Window 2.2
+import QtQuick
Window {
width: 480
@@ -14,6 +13,7 @@ Window {
z: 10000
anchors.fill: parent
+ //![1]
PointHandler {
id: handler
acceptedDevices: PointerDevice.TouchScreen | PointerDevice.TouchPad
@@ -26,6 +26,7 @@ Window {
width: 20; height: width; radius: width / 2
}
}
+ //![1]
}
}
//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml
new file mode 100644
index 0000000000..3eab8b319b
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick
+
+Item {
+ width: 480; height: 320
+
+ Rectangle {
+ color: handler.active ? "tomato" : "wheat"
+ x: handler.point.position.x - width / 2
+ y: handler.point.position.y - height / 2
+ width: 20; height: width; radius: width / 2
+ }
+
+ PointHandler {
+ id: handler
+ acceptedButtons: Qt.MiddleButton | Qt.RightButton
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml
new file mode 100644
index 0000000000..9e3cb6f465
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick
+
+Item {
+ id: feedbackPane
+ width: 480; height: 320
+
+ PointHandler {
+ id: control
+ acceptedModifiers: Qt.ControlModifier
+ cursorShape: Qt.PointingHandCursor
+ target: Rectangle {
+ parent: feedbackPane
+ color: control.active ? "indianred" : "khaki"
+ x: control.point.position.x - width / 2
+ y: control.point.position.y - height / 2
+ width: 20; height: width; radius: width / 2
+ }
+ }
+
+ PointHandler {
+ id: shift
+ acceptedModifiers: Qt.ShiftModifier | Qt.MetaModifier
+ cursorShape: Qt.CrossCursor
+ target: Rectangle {
+ parent: feedbackPane
+ color: shift.active ? "darkslateblue" : "lightseagreen"
+ x: shift.point.position.x - width / 2
+ y: shift.point.position.y - height / 2
+ width: 30; height: width; radius: width / 2
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml
new file mode 100644
index 0000000000..d04bd4f149
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//![0]
+import QtQuick
+
+Canvas {
+ id: canvas
+ width: 800
+ height: 600
+ antialiasing: true
+ renderTarget: Canvas.FramebufferObject
+ property var points: []
+ onPaint: {
+ if (points.length < 2)
+ return
+ var ctx = canvas.getContext('2d');
+ ctx.save()
+ ctx.strokeStyle = stylusHandler.active ? "blue" : "white"
+ ctx.lineCap = "round"
+ ctx.beginPath()
+ ctx.moveTo(points[0].x, points[0].y)
+ for (var i = 1; i < points.length; i++)
+ ctx.lineTo(points[i].x, points[i].y)
+ ctx.lineWidth = 3
+ ctx.stroke()
+ points = points.slice(points.length - 2, 1)
+ ctx.restore()
+ }
+
+ PointHandler {
+ id: stylusHandler
+ acceptedPointerTypes: PointerDevice.Pen
+ onPointChanged: {
+ canvas.points.push(point.position)
+ canvas.requestPaint()
+ }
+ }
+
+ PointHandler {
+ id: eraserHandler
+ acceptedPointerTypes: PointerDevice.Eraser
+ onPointChanged: {
+ canvas.points.push(point.position)
+ canvas.requestPaint()
+ }
+ }
+
+ Rectangle {
+ width: 10; height: 10
+ color: stylusHandler.active ? "green" : eraserHandler.active ? "red" : "beige"
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml
new file mode 100644
index 0000000000..1b66531dbf
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick
+
+Item {
+ width: 480; height: 320
+
+ Rectangle {
+ anchors.fill: handlingContainer
+ anchors.margins: -handler.margin
+ color: "beige"
+ }
+
+ Rectangle {
+ id: handlingContainer
+ width: 200; height: 200
+ anchors.centerIn: parent
+ border.color: "green"
+ color: handler.active ? "lightsteelblue" : "khaki"
+
+ Text {
+ text: "X"
+ x: handler.point.position.x - width / 2
+ y: handler.point.position.y - height / 2
+ visible: handler.active
+ }
+
+ PointHandler {
+ id: handler
+ margin: 30
+ }
+ }
+
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml
new file mode 100644
index 0000000000..3e8634a605
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ id: button
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml
new file mode 100644
index 0000000000..4563b44014
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ id: button
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.WithinBounds
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerGrabChanged.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerGrabChanged.qml
new file mode 100644
index 0000000000..73631d3551
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerGrabChanged.qml
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+
+Rectangle {
+ width: 100
+ height: 100
+
+ //![0]
+ TapHandler {
+ gesturePolicy: TapHandler.ReleaseWithinBounds // exclusive grab on press
+ onGrabChanged:
+ (transition, eventPoint) => {
+ switch (transition) {
+ case PointerDevice.GrabExclusive:
+ console.log("took exclusive grab of point", eventPoint.id,
+ "on", eventPoint.device.name)
+ break
+ case PointerDevice.UngrabExclusive:
+ console.log("gave up exclusive grab of point", eventPoint.id,
+ "on", eventPoint.device.name)
+ break
+ case PointerDevice.CancelGrabExclusive:
+ console.log("exclusive grab of point", eventPoint.id,
+ "on", eventPoint.device.name, "has been cancelled")
+ break
+ }
+
+ switch (eventPoint.state) {
+ case EventPoint.Pressed:
+ console.log("on press @", eventPoint.position);
+ break
+ case EventPoint.Updated:
+ console.log("on update @", eventPoint.position);
+ break
+ case EventPoint.Released:
+ console.log("on release @", eventPoint.position);
+ break
+ default:
+ console.log(eventPoint.position, "state", eventPoint.state)
+ break
+ }
+ }
+ }
+ //![0]
+}
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml
new file mode 100644
index 0000000000..917c863e53
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.DragThreshold // the default
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+ Button { x: 30; y: 30 }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml b/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml
index 38be381b81..1740b7bfe0 100644
--- a/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.14
+import QtQuick
Rectangle {
width: 170; height: 120
diff --git a/src/quick/doc/snippets/qml/anchoranimation.qml b/src/quick/doc/snippets/qml/anchoranimation.qml
index 97a6c79e55..e6fed602ed 100644
--- a/src/quick/doc/snippets/qml/anchoranimation.qml
+++ b/src/quick/doc/snippets/qml/anchoranimation.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
id: container
diff --git a/src/quick/doc/snippets/qml/anchorchanges.qml b/src/quick/doc/snippets/qml/anchorchanges.qml
index d3ee2be000..7a306e0d6b 100644
--- a/src/quick/doc/snippets/qml/anchorchanges.qml
+++ b/src/quick/doc/snippets/qml/anchorchanges.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: window
diff --git a/src/quick/doc/snippets/qml/animatedimage.qml b/src/quick/doc/snippets/qml/animatedimage.qml
index 3d46887a6c..baca3f279a 100644
--- a/src/quick/doc/snippets/qml/animatedimage.qml
+++ b/src/quick/doc/snippets/qml/animatedimage.qml
@@ -5,7 +5,7 @@
// examples/quick/imageelements/animatedimage
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: animation.width; height: animation.height + 8
diff --git a/src/quick/doc/snippets/qml/animation.qml b/src/quick/doc/snippets/qml/animation.qml
index 24d23860da..5f10726f48 100644
--- a/src/quick/doc/snippets/qml/animation.qml
+++ b/src/quick/doc/snippets/qml/animation.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
diff --git a/src/quick/doc/snippets/qml/animators.qml b/src/quick/doc/snippets/qml/animators.qml
index 04082ba1fb..b1e929eaaf 100644
--- a/src/quick/doc/snippets/qml/animators.qml
+++ b/src/quick/doc/snippets/qml/animators.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.2
+import QtQuick
Rectangle {
diff --git a/src/quick/doc/snippets/qml/behavior.qml b/src/quick/doc/snippets/qml/behavior.qml
index 0fc6e7e289..8370863cbe 100644
--- a/src/quick/doc/snippets/qml/behavior.qml
+++ b/src/quick/doc/snippets/qml/behavior.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
diff --git a/src/quick/doc/snippets/qml/borderimage/borderimage-rounded.qml b/src/quick/doc/snippets/qml/borderimage/borderimage-rounded.qml
new file mode 100644
index 0000000000..a0b9cc680b
--- /dev/null
+++ b/src/quick/doc/snippets/qml/borderimage/borderimage-rounded.qml
@@ -0,0 +1,124 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: page
+ color: "white"
+ width: 182; height: 164
+
+//! [tiled border image]
+BorderImage {
+ anchors { fill: parent; margins: 6 }
+ border { left: 30; top: 30; right: 30; bottom: 30 }
+ horizontalTileMode: BorderImage.Round
+ verticalTileMode: BorderImage.Round
+ source: "pics/borderframe.png"
+}
+//! [tiled border image]
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 5
+ color: "transparent"
+ border.color: "gray"
+
+ Rectangle {
+ x: 30; y: 0
+ width: 1; height: parent.height
+ color: "gray"
+
+ Text {
+ text: "1"
+ font.pixelSize: 9
+ color: "red"
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ y: 20
+ }
+
+ Text {
+ text: "4"
+ font.pixelSize: 9
+ color: "red"
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ }
+
+ Text {
+ text: "7"
+ font.pixelSize: 9
+ color: "red"
+ y: parent.height - 30
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ }
+ }
+
+ Rectangle {
+ x: parent.width - 30; y: 0
+ width: 1; height: parent.height
+ color: "gray"
+
+ Text {
+ text: "3"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ y: 20
+ }
+
+ Text {
+ text: "6"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Text {
+ text: "9"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ y: parent.height - 30
+ }
+ }
+
+ Text {
+ text: "5"
+ font.pixelSize: 9
+ color: "red"
+ anchors.centerIn: parent
+ }
+
+ Rectangle {
+ x: 0; y: 30
+ width: parent.width; height: 1
+ color: "gray"
+
+ Text {
+ text: "2"
+ font.pixelSize: 9
+ color: "red"
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: -10
+ }
+ }
+
+ Rectangle {
+ x: 0; y: parent.height - 30
+ width: parent.width; height: 1
+ color: "gray"
+
+ Text {
+ text: "8"
+ font.pixelSize: 9
+ color: "red"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ }
+}
diff --git a/src/quick/doc/snippets/qml/borderimage/borderimage-scaled.qml b/src/quick/doc/snippets/qml/borderimage/borderimage-scaled.qml
index 3a763f4998..5229060435 100644
--- a/src/quick/doc/snippets/qml/borderimage/borderimage-scaled.qml
+++ b/src/quick/doc/snippets/qml/borderimage/borderimage-scaled.qml
@@ -1,16 +1,17 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: page
color: "white"
- width: 180; height: 180
+ width: 182; height: 182
+ border.color: "gray"
//! [scaled border image]
BorderImage {
- width: 180; height: 180
+ anchors { fill: parent; margins: 1 }
border { left: 30; top: 30; right: 30; bottom: 30 }
horizontalTileMode: BorderImage.Stretch
verticalTileMode: BorderImage.Stretch
@@ -20,25 +21,98 @@ BorderImage {
Rectangle {
x: 30; y: 0
- width: 1; height: 180
+ width: 1; height: parent.height
color: "gray"
+
+ Text {
+ text: "1"
+ font.pixelSize: 9
+ color: "red"
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ y: 20
+ }
+
+ Text {
+ text: "4"
+ font.pixelSize: 9
+ color: "red"
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ }
+
+ Text {
+ text: "7"
+ font.pixelSize: 9
+ color: "red"
+ y: parent.height - 30
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ }
}
Rectangle {
- x: 150; y: 0
- width: 1; height: 180
+ x: parent.width - 30; y: 0
+ width: 1; height: parent.height
color: "gray"
+
+ Text {
+ text: "3"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ y: 20
+ }
+
+ Text {
+ text: "6"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Text {
+ text: "9"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ y: parent.height - 30
+ }
+ }
+
+ Text {
+ text: "5"
+ font.pixelSize: 9
+ color: "red"
+ anchors.centerIn: parent
}
Rectangle {
x: 0; y: 30
- width: 180; height: 1
+ width: parent.width; height: 1
color: "gray"
+
+ Text {
+ text: "2"
+ font.pixelSize: 9
+ color: "red"
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: -10
+ }
}
Rectangle {
- x: 0; y: 150
- width: 180; height: 1
+ x: 0; y: parent.height - 30
+ width: parent.width; height: 1
color: "gray"
+
+ Text {
+ text: "8"
+ font.pixelSize: 9
+ color: "red"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
}
}
diff --git a/src/quick/doc/snippets/qml/borderimage/borderimage-tiled.qml b/src/quick/doc/snippets/qml/borderimage/borderimage-tiled.qml
index 525824d364..2f7b120b7f 100644
--- a/src/quick/doc/snippets/qml/borderimage/borderimage-tiled.qml
+++ b/src/quick/doc/snippets/qml/borderimage/borderimage-tiled.qml
@@ -1,16 +1,17 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: page
color: "white"
- width: 180; height: 180
+ width: 182; height: 182
+ border.color: "gray"
//! [tiled border image]
BorderImage {
- width: 180; height: 180
+ anchors { fill: parent; margins: 1 }
border { left: 30; top: 30; right: 30; bottom: 30 }
horizontalTileMode: BorderImage.Repeat
verticalTileMode: BorderImage.Repeat
@@ -20,25 +21,98 @@ BorderImage {
Rectangle {
x: 30; y: 0
- width: 1; height: 180
+ width: 1; height: parent.height
color: "gray"
+
+ Text {
+ text: "1"
+ font.pixelSize: 9
+ color: "red"
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ y: 20
+ }
+
+ Text {
+ text: "4"
+ font.pixelSize: 9
+ color: "red"
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ }
+
+ Text {
+ text: "7"
+ font.pixelSize: 9
+ color: "red"
+ y: parent.height - 30
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ }
}
Rectangle {
- x: 150; y: 0
- width: 1; height: 180
+ x: parent.width - 30; y: 0
+ width: 1; height: parent.height
color: "gray"
+
+ Text {
+ text: "3"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ y: 20
+ }
+
+ Text {
+ text: "6"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Text {
+ text: "9"
+ font.pixelSize: 9
+ color: "red"
+ x: 1
+ y: parent.height - 30
+ }
+ }
+
+ Text {
+ text: "5"
+ font.pixelSize: 9
+ color: "red"
+ anchors.centerIn: parent
}
Rectangle {
x: 0; y: 30
- width: 180; height: 1
+ width: parent.width; height: 1
color: "gray"
+
+ Text {
+ text: "2"
+ font.pixelSize: 9
+ color: "red"
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: -10
+ }
}
Rectangle {
- x: 0; y: 150
- width: 180; height: 1
+ x: 0; y: parent.height - 30
+ width: parent.width; height: 1
color: "gray"
+
+ Text {
+ text: "8"
+ font.pixelSize: 9
+ color: "red"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
}
}
diff --git a/src/quick/doc/snippets/qml/borderimage/normal-image.qml b/src/quick/doc/snippets/qml/borderimage/normal-image.qml
index 3bcbb258f1..44ca440732 100644
--- a/src/quick/doc/snippets/qml/borderimage/normal-image.qml
+++ b/src/quick/doc/snippets/qml/borderimage/normal-image.qml
@@ -1,40 +1,48 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: page
color: "white"
- width: 120; height: 120
+ width: 182; height: 182
//! [normal image]
Image {
source: "pics/borderframe.png"
+ anchors.centerIn: parent
}
//! [normal image]
Rectangle {
- x: 30; y: 0
- width: 1; height: 120
- color: "gray"
- }
+ width: 120; height: 120
+ color: "transparent"
+ border.color: "gray"
+ anchors.centerIn: parent
- Rectangle {
- x: 90; y: 0
- width: 1; height: 120
- color: "gray"
- }
+ Rectangle {
+ x: 30; y: 0
+ width: 1; height: 120
+ color: "gray"
+ }
- Rectangle {
- x: 0; y: 30
- width: 200; height: 1
- color: "gray"
- }
+ Rectangle {
+ x: 90; y: 0
+ width: 1; height: 120
+ color: "gray"
+ }
- Rectangle {
- x: 0; y: 90
- width: 200; height: 1
- color: "gray"
+ Rectangle {
+ x: 0; y: 30
+ width: 120; height: 1
+ color: "gray"
+ }
+
+ Rectangle {
+ x: 0; y: 90
+ width: 120; height: 1
+ color: "gray"
+ }
}
}
diff --git a/src/quick/doc/snippets/qml/borderimage/pics/borderframe.png b/src/quick/doc/snippets/qml/borderimage/pics/borderframe.png
new file mode 100644
index 0000000000..97a9452d5c
--- /dev/null
+++ b/src/quick/doc/snippets/qml/borderimage/pics/borderframe.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/borderimage/pics/borderframe.svg b/src/quick/doc/snippets/qml/borderimage/pics/borderframe.svg
new file mode 100644
index 0000000000..1f6c1f2901
--- /dev/null
+++ b/src/quick/doc/snippets/qml/borderimage/pics/borderframe.svg
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="120.00004"
+ height="120.00018"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ sodipodi:docname="borderframe.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.1"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : -406.18091 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : -406.18091 : 1"
+ inkscape:persp3d-origin="372.04724 : -581.57461 : 1"
+ id="perspective10" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.8998663"
+ inkscape:cx="239.22736"
+ inkscape:cy="198.43502"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="2688"
+ inkscape:window-height="1698"
+ inkscape:window-x="4020"
+ inkscape:window-y="204"
+ inkscape:showpageshadow="0"
+ inkscape:pagecheckerboard="1"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:window-maximized="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2383"
+ visible="true"
+ enabled="true"
+ spacingx="1"
+ spacingy="1"
+ originx="-93.000067"
+ originy="-75.362307" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-93.000067,-75.362307)">
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.88977;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 105.75595,88.1182 c 0,-11.811047 11.81103,-11.811047 11.81103,0 h 23.62207 L 153.00008,76.307153 164.81112,88.1182 h 23.62206 c 0,-11.811047 11.81104,-11.811047 11.81104,0 11.81104,0 11.81104,11.811049 0,11.811049 v 23.622101 l 11.81104,11.81105 -11.81104,11.81105 v 23.62209 c 11.81104,0 11.81104,11.81105 0,11.81105 0,11.81105 -11.81104,11.81105 -11.81104,0 h -23.62206 l -11.81104,11.81105 -11.81103,-11.81105 h -23.62207 c 0,11.81105 -11.81103,11.81105 -11.81103,0 -11.811037,0 -11.811037,-11.81105 0,-11.81105 V 147.17345 L 93.944913,135.3624 105.75595,123.55135 V 99.929249 c -11.811037,0 -11.811037,-11.811049 0,-11.811049 z"
+ id="rect2387"
+ sodipodi:nodetypes="ccccccccccccccccccccccccc" />
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccc"
+ id="path3160"
+ d="m 110.48036,92.84263 c 0,-10.629942 0,-9.448848 4.72442,-1e-5 l 27.16537,1e-5 10.62993,-10.629942 10.62993,10.629942 27.16538,-1e-5 c 4.72441,-9.448838 4.72441,-10.629932 4.72441,1e-5 10.62993,0 9.44883,-1e-5 0,4.724411 v 27.165419 l 10.62993,10.62995 -10.62993,10.62994 v 27.1654 c 9.44883,4.72442 10.62993,4.72443 0,4.72443 0,10.62995 0,9.44883 -4.72441,-1e-5 l -27.16538,1e-5 -10.62993,10.62995 -10.62993,-10.62995 -27.16537,-1e-5 c -4.72442,9.44884 -4.72442,10.62996 -4.72442,1e-5 -10.629933,0 -9.44883,-1e-5 0,-4.72443 v -27.1654 L 99.850427,135.36241 110.48036,124.73246 V 97.567041 c -9.44883,-4.724421 -10.629933,-4.724411 0,-4.724411 z"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.77954;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/src/quick/doc/snippets/qml/boundaryRule.qml b/src/quick/doc/snippets/qml/boundaryRule.qml
index 65c8585e3f..2a1ad46f0a 100644
--- a/src/quick/doc/snippets/qml/boundaryRule.qml
+++ b/src/quick/doc/snippets/qml/boundaryRule.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.14
-import Qt.labs.animation 1.0
+import QtQuick
+import Qt.labs.animation
Rectangle {
id: root
diff --git a/src/quick/doc/snippets/qml/coloranimation.qml b/src/quick/doc/snippets/qml/coloranimation.qml
index 9e0bf37ca1..698d7f886d 100644
--- a/src/quick/doc/snippets/qml/coloranimation.qml
+++ b/src/quick/doc/snippets/qml/coloranimation.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 100; height: 100
diff --git a/src/quick/doc/snippets/qml/colors.qml b/src/quick/doc/snippets/qml/colors.qml
index b0cc9d14c9..65cf07906b 100644
--- a/src/quick/doc/snippets/qml/colors.qml
+++ b/src/quick/doc/snippets/qml/colors.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 160; height: 250
diff --git a/src/quick/doc/snippets/qml/column/column-transitions.qml b/src/quick/doc/snippets/qml/column/column-transitions.qml
index 80f18b629b..70d8435d07 100644
--- a/src/quick/doc/snippets/qml/column/column-transitions.qml
+++ b/src/quick/doc/snippets/qml/column/column-transitions.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [document]
Column {
diff --git a/src/quick/doc/snippets/qml/column/column.qml b/src/quick/doc/snippets/qml/column/column.qml
index 8df93a83d3..3f9076bd3d 100644
--- a/src/quick/doc/snippets/qml/column/column.qml
+++ b/src/quick/doc/snippets/qml/column/column.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Item {
width: 310; height: 170
diff --git a/src/quick/doc/snippets/qml/column/vertical-positioner.qml b/src/quick/doc/snippets/qml/column/vertical-positioner.qml
index 01b4743c7c..5d8850fc27 100644
--- a/src/quick/doc/snippets/qml/column/vertical-positioner.qml
+++ b/src/quick/doc/snippets/qml/column/vertical-positioner.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [document]
Column {
diff --git a/src/quick/doc/snippets/qml/drag.qml b/src/quick/doc/snippets/qml/drag.qml
index 7205ad2114..2cc57c38b3 100644
--- a/src/quick/doc/snippets/qml/drag.qml
+++ b/src/quick/doc/snippets/qml/drag.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 200
diff --git a/src/quick/doc/snippets/qml/example.md b/src/quick/doc/snippets/qml/example.md
new file mode 100644
index 0000000000..43956c7830
--- /dev/null
+++ b/src/quick/doc/snippets/qml/example.md
@@ -0,0 +1,146 @@
+# Markdown in Qt Quick
+
+The Text, TextEdit and TextArea items support rich text formatted in HTML.
+Since Qt 5.14, they now support two dialects of Markdown as well:
+[The CommonMark Specification](https://spec.commonmark.org/0.29/) is the
+conservative formal specification, while
+[GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown)
+adds extra features such as task lists and tables.
+
+## Font and Paragraph Styles
+
+Markdown supports **bold**, *italic*, ~~strikethrough~~ and `monospace` font
+styles.
+
+> A block quote is indented according to the convention for email quoting.
+
+ A block of code;
+ can be indented;
+ with 4 spaces or a tab;
+
+or
+
+```
+Block {
+ id: code
+ CanBe {
+ wrappedBy: "triple backticks"
+ }
+}
+```
+
+Block quotes can be nested, and block quotes can include indented code blocks.
+
+In [The CommonMark Specification](https://spec.commonmark.org/0.29/)
+John MacFarlane writes:
+
+> What distinguishes Markdown from many other lightweight markup syntaxes,
+> which are often easier to write, is its readability. As Gruber writes:
+
+> > The overriding design goal for Markdown's formatting syntax is to make it
+> > as readable as possible. The idea is that a Markdown-formatted document should
+> > be publishable as-is, as plain text, without looking like it's been marked up
+> > with tags or formatting instructions. (
+> > [http://daringfireball.net/projects/markdown/](http://daringfireball.net/projects/markdown/))
+
+> The point can be illustrated by comparing a sample of AsciiDoc with an
+> equivalent sample of Markdown. Here is a sample of AsciiDoc from the AsciiDoc
+> manual:
+
+> 1. List item one.
+> +
+> List item one continued with a second paragraph followed by an
+> Indented block.
+> +
+> .................
+> $ ls *.sh
+> $ mv *.sh ~/tmp
+> .................
+> +
+> List item continued with a third paragraph.
+>
+> 2. List item two continued with an open block.
+> ...
+>
+
+## Hyperlinks
+
+Hyperlinks can be written with the link text first, and the URL immediately
+following: [Qt Assistant](http://doc.qt.io/qt-6/qtassistant-index.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:
+[The Qt Creator Manual][creatormanual]
+
+## Images
+
+Inline images like this one ![red square](images/red.png) flow with the surrounding text.
+
+The code for including an image is just a link that starts with a bang.
+An image in its own paragraph is given its own space.
+
+## Lists
+
+Different kinds of lists can be included. Standard bullet lists can be nested,
+using different symbols for each level of the list. List items can have nested
+items such as block quotes, code blocks and images. Check boxes can be included
+to form a task list.
+
+- Disc symbols are typically used for top-level list items.
+ * Circle symbols can be used to distinguish between items in lower-level
+ lists.
+ + Square symbols provide a reasonable alternative to discs and circles.
+ * Lists can be continued...
+ * further down
+- List items can include images: ![red square](images/red.png)
+- and even nested quotes, like this:
+
+ 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.
+ >
+ > It can display both plain and rich text. For example:
+ >
+ > TextEdit {
+ > width: 240
+ > text: "<b>Hello</b> <i>World!</i>"
+ > font.family: "Helvetica"
+ > font.pointSize: 20
+ > color: "blue"
+ > focus: true
+ > }
+- List items with check boxes allow task lists to be incorporated:
+ * [ ] This task is not yet done
+ * [x] We aced this one!
+
+Ordered lists can be used for tables of contents, for example. Each number
+should end with a period or a parenthesis:
+
+1. Markdown in Qt Quick
+ 1) Font and Paragraph Styles
+ 5) Hyperlinks
+ 3) Images ![red square](images/red.png)
+ 2) Lists
+ 4) Tables
+2. Related work
+
+The list will automatically be renumbered during rendering.
+
+## Thematic Breaks
+
+A horizontal rule is possible, as in HTML:
+
+- - -
+
+## Tables
+
+One of the GitHub extensions is support for tables:
+
+| |Development Tools |Programming Techniques |Graphical User Interfaces|
+|-------------|------------------------------------|---------------------------|-------------------------|
+|9:00 - 11:00 |Introduction to Qt |||
+|11:00 - 13:00|Using Qt Creator |QML and its runtime |Layouts in Qt |
+|13:00 - 15:00|Qt Quick Designer Tutorial |Extreme Programming |Writing Custom Styles |
+|15:00 - 17:00|Qt Linguist and Internationalization| | |
diff --git a/src/quick/doc/snippets/qml/externalDragScaledImage.qml b/src/quick/doc/snippets/qml/externalDragScaledImage.qml
new file mode 100644
index 0000000000..a7069db849
--- /dev/null
+++ b/src/quick/doc/snippets/qml/externalDragScaledImage.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+//![0]
+import QtQuick
+
+Item {
+ width: 200; height: 200
+
+ Image {
+ anchors.centerIn: parent
+ source: "images/qt_logo.svg"
+ sourceSize.width: 96
+
+ Drag.dragType: Drag.Automatic
+ Drag.supportedActions: Qt.CopyAction
+ Drag.mimeData: {
+ "text/plain": "Qt Quick rocks!"
+ }
+ Drag.imageSource: "images/qt_logo.svg"
+ Drag.imageSourceSize: Qt.size(48, 35)
+ Drag.active: dragHandler.active
+
+ DragHandler {
+ id: dragHandler
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/externaldrag.qml b/src/quick/doc/snippets/qml/externaldrag.qml
index 3c504004de..723701a7a5 100644
--- a/src/quick/doc/snippets/qml/externaldrag.qml
+++ b/src/quick/doc/snippets/qml/externaldrag.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
Item {
width: 200; height: 200
@@ -12,7 +12,6 @@ Item {
color: "green"
radius: 5
- Drag.active: dragHandler.active
Drag.dragType: Drag.Automatic
Drag.supportedActions: Qt.CopyAction
Drag.mimeData: {
@@ -30,8 +29,11 @@ Item {
onActiveChanged:
if (active) {
parent.grabToImage(function(result) {
- parent.Drag.imageSource = result.url;
+ parent.Drag.imageSource = result.url
+ parent.Drag.active = true
})
+ } else {
+ parent.Drag.active = false
}
}
}
diff --git a/src/quick/doc/snippets/qml/flickable.qml b/src/quick/doc/snippets/qml/flickable.qml
index 0c756f76d4..7334059b9e 100644
--- a/src/quick/doc/snippets/qml/flickable.qml
+++ b/src/quick/doc/snippets/qml/flickable.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Flickable {
width: 200; height: 200
diff --git a/src/quick/doc/snippets/qml/flickableScrollbar.qml b/src/quick/doc/snippets/qml/flickableScrollbar.qml
index 3b2b10ac2b..981b725313 100644
--- a/src/quick/doc/snippets/qml/flickableScrollbar.qml
+++ b/src/quick/doc/snippets/qml/flickableScrollbar.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/flipable/flipable.qml b/src/quick/doc/snippets/qml/flipable/flipable.qml
index 0961653dc0..80821e610f 100644
--- a/src/quick/doc/snippets/qml/flipable/flipable.qml
+++ b/src/quick/doc/snippets/qml/flipable/flipable.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
-import QtQuick 2.0
+import QtQuick
Flipable {
id: flipable
diff --git a/src/quick/doc/snippets/qml/flow.qml b/src/quick/doc/snippets/qml/flow.qml
index 2757dc4bcb..6d8db1e295 100644
--- a/src/quick/doc/snippets/qml/flow.qml
+++ b/src/quick/doc/snippets/qml/flow.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
color: "lightblue"
diff --git a/src/quick/doc/snippets/qml/focus/MyClickableWidget.qml b/src/quick/doc/snippets/qml/focus/MyClickableWidget.qml
index 5718fb0e4f..7762a937f0 100644
--- a/src/quick/doc/snippets/qml/focus/MyClickableWidget.qml
+++ b/src/quick/doc/snippets/qml/focus/MyClickableWidget.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [clickable in focusscope]
FocusScope {
diff --git a/src/quick/doc/snippets/qml/focus/MyWidget.qml b/src/quick/doc/snippets/qml/focus/MyWidget.qml
index f43d7af2de..cb04176880 100644
--- a/src/quick/doc/snippets/qml/focus/MyWidget.qml
+++ b/src/quick/doc/snippets/qml/focus/MyWidget.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [mywidget]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/focus/advancedFocus.qml b/src/quick/doc/snippets/qml/focus/advancedFocus.qml
index 42ec222237..6874b8fa5b 100644
--- a/src/quick/doc/snippets/qml/focus/advancedFocus.qml
+++ b/src/quick/doc/snippets/qml/focus/advancedFocus.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [FocusScope delegate]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/focus/basicwidget.qml b/src/quick/doc/snippets/qml/focus/basicwidget.qml
index e5f96ce4eb..377e852a1d 100644
--- a/src/quick/doc/snippets/qml/focus/basicwidget.qml
+++ b/src/quick/doc/snippets/qml/focus/basicwidget.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [focus true]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/focus/clickablewidget.qml b/src/quick/doc/snippets/qml/focus/clickablewidget.qml
index 99df888ba8..502fa36bf8 100644
--- a/src/quick/doc/snippets/qml/focus/clickablewidget.qml
+++ b/src/quick/doc/snippets/qml/focus/clickablewidget.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [clickable window]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/focus/myfocusscopewidget.qml b/src/quick/doc/snippets/qml/focus/myfocusscopewidget.qml
index 5dd5f3f8b4..6703d3102a 100644
--- a/src/quick/doc/snippets/qml/focus/myfocusscopewidget.qml
+++ b/src/quick/doc/snippets/qml/focus/myfocusscopewidget.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [widget in focusscope]
FocusScope {
diff --git a/src/quick/doc/snippets/qml/focus/rectangle.qml b/src/quick/doc/snippets/qml/focus/rectangle.qml
index 119495767e..4264673581 100644
--- a/src/quick/doc/snippets/qml/focus/rectangle.qml
+++ b/src/quick/doc/snippets/qml/focus/rectangle.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [simple key event]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/focus/widget.qml b/src/quick/doc/snippets/qml/focus/widget.qml
index 5a0200d26c..1c44f97c4d 100644
--- a/src/quick/doc/snippets/qml/focus/widget.qml
+++ b/src/quick/doc/snippets/qml/focus/widget.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [window]
diff --git a/src/quick/doc/snippets/qml/gradient.qml b/src/quick/doc/snippets/qml/gradient.qml
index 861af15736..39ec1d37bd 100644
--- a/src/quick/doc/snippets/qml/gradient.qml
+++ b/src/quick/doc/snippets/qml/gradient.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![code]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/grid-spacing.qml b/src/quick/doc/snippets/qml/grid-spacing.qml
index ae976f1510..763b4acfbf 100644
--- a/src/quick/doc/snippets/qml/grid-spacing.qml
+++ b/src/quick/doc/snippets/qml/grid-spacing.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 112; height: 112
diff --git a/src/quick/doc/snippets/qml/grid/grid.qml b/src/quick/doc/snippets/qml/grid/grid.qml
index c66e32bc07..db72263ebb 100644
--- a/src/quick/doc/snippets/qml/grid/grid.qml
+++ b/src/quick/doc/snippets/qml/grid/grid.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Grid {
columns: 3
diff --git a/src/quick/doc/snippets/qml/gridview/ContactModel.qml b/src/quick/doc/snippets/qml/gridview/ContactModel.qml
index 42319725cf..a6c7f949b8 100644
--- a/src/quick/doc/snippets/qml/gridview/ContactModel.qml
+++ b/src/quick/doc/snippets/qml/gridview/ContactModel.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
ListModel {
diff --git a/src/quick/doc/snippets/qml/gridview/gridview.qml b/src/quick/doc/snippets/qml/gridview/gridview.qml
index 247865d41f..3ce3cba61d 100644
--- a/src/quick/doc/snippets/qml/gridview/gridview.qml
+++ b/src/quick/doc/snippets/qml/gridview/gridview.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![import]
-import QtQuick 2.0
+import QtQuick
//![import]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/image.qml b/src/quick/doc/snippets/qml/image.qml
index f73793384c..40d85e44c6 100644
--- a/src/quick/doc/snippets/qml/image.qml
+++ b/src/quick/doc/snippets/qml/image.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Image {
source: "pics/qtlogo.png"
diff --git a/src/quick/doc/snippets/qml/images/qt-logo.png b/src/quick/doc/snippets/qml/images/qt-logo.png
new file mode 100644
index 0000000000..30c621c9c6
--- /dev/null
+++ b/src/quick/doc/snippets/qml/images/qt-logo.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/images/qt_logo.svg b/src/quick/doc/snippets/qml/images/qt_logo.svg
new file mode 100644
index 0000000000..062daff3e9
--- /dev/null
+++ b/src/quick/doc/snippets/qml/images/qt_logo.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="462pt"
+ height="339pt"
+ viewBox="0 0 462 339"
+ version="1.1"
+ id="svg2"
+>
+ <path
+ fill="#41cd52"
+ d=" M 63.50 0.00 L 462.00 0.00 L 462.00 274.79 C 440.60 296.26 419.13 317.66 397.61 339.00 L 0.00 339.00 L 0.00 63.39 C 21.08 42.18 42.34 21.13 63.50 0.00 Z"
+ id="path6"/>
+ <path
+ d=" M 122.37 71.33 C 137.50 61.32 156.21 58.79 174.00 58.95 C 190.94 59.16 208.72 62.13 222.76 72.24 C 232.96 79.41 239.59 90.48 244.01 101.93 C 251.16 120.73 253.26 141.03 253.50 161.01 C 253.53 181.13 252.62 201.69 245.96 220.86 C 241.50 233.90 233.01 245.48 221.81 253.52 C 229.87 266.58 238.09 279.54 246.15 292.60 C 236.02 297.27 225.92 301.97 215.78 306.62 C 207.15 292.38 198.56 278.11 189.90 263.89 C 178.19 265.81 166.21 265.66 154.44 264.36 C 140.34 262.67 125.97 258.37 115.09 248.88 C 106.73 241.64 101.48 231.51 97.89 221.21 C 92.01 203.79 90.43 185.25 90.16 166.97 C 90.02 147.21 91.28 127.14 97.24 108.18 C 101.85 93.92 109.48 79.69 122.37 71.33 Z"
+ id="path8"
+ fill="#ffffff"/>
+ <path
+ d=" M 294.13 70.69 C 304.73 70.68 315.33 70.68 325.93 70.69 C 325.96 84.71 325.92 98.72 325.95 112.74 C 339.50 112.76 353.05 112.74 366.60 112.75 C 366.37 121.85 366.12 130.95 365.86 140.05 C 352.32 140.08 338.79 140.04 325.25 140.07 C 325.28 163.05 325.18 186.03 325.30 209.01 C 325.56 215.30 325.42 221.94 328.19 227.75 C 330.21 232.23 335.65 233.38 340.08 233.53 C 348.43 233.50 356.77 233.01 365.12 232.86 C 365.63 241.22 366.12 249.59 366.60 257.95 C 349.99 260.74 332.56 264.08 316.06 258.86 C 309.11 256.80 302.63 252.19 299.81 245.32 C 294.76 233.63 294.35 220.62 294.13 208.07 C 294.11 185.40 294.13 162.74 294.12 140.07 C 286.73 140.05 279.34 140.08 271.95 140.05 C 271.93 130.96 271.93 121.86 271.95 112.76 C 279.34 112.73 286.72 112.77 294.11 112.74 C 294.14 98.72 294.10 84.71 294.13 70.69 Z"
+ id="path10"
+ fill="#ffffff"/>
+ <path
+ fill="#41cd52"
+ d=" M 160.51 87.70 C 170.80 86.36 181.60 86.72 191.34 90.61 C 199.23 93.73 205.93 99.84 209.47 107.58 C 214.90 119.31 216.98 132.26 218.03 145.05 C 219.17 162.07 219.01 179.25 216.66 196.17 C 215.01 206.24 212.66 216.85 205.84 224.79 C 198.92 232.76 188.25 236.18 178.01 236.98 C 167.21 237.77 155.82 236.98 146.07 231.87 C 140.38 228.84 135.55 224.09 132.73 218.27 C 129.31 211.30 127.43 203.69 126.11 196.07 C 122.13 171.91 121.17 146.91 126.61 122.89 C 128.85 113.83 132.11 104.53 138.73 97.70 C 144.49 91.85 152.51 88.83 160.51 87.70 Z"
+ id="path12"/>
+</svg>
diff --git a/src/quick/doc/snippets/qml/images/red.png b/src/quick/doc/snippets/qml/images/red.png
new file mode 100644
index 0000000000..9038fef784
--- /dev/null
+++ b/src/quick/doc/snippets/qml/images/red.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/item/childrenRect.qml b/src/quick/doc/snippets/qml/item/childrenRect.qml
index bd7261074e..750a6fa125 100644
--- a/src/quick/doc/snippets/qml/item/childrenRect.qml
+++ b/src/quick/doc/snippets/qml/item/childrenRect.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [local]
Item {
diff --git a/src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml b/src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml
index 2865c1d067..9aa8ddcc75 100644
--- a/src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml
+++ b/src/quick/doc/snippets/qml/item/containmentMask-circle-js.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQml 2.12
-import QtQuick 2.12
+import QtQml
+import QtQuick
//![0]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/item/containmentMask-shape.qml b/src/quick/doc/snippets/qml/item/containmentMask-shape.qml
index 607c0e148a..171437c380 100644
--- a/src/quick/doc/snippets/qml/item/containmentMask-shape.qml
+++ b/src/quick/doc/snippets/qml/item/containmentMask-shape.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.12
-import QtQuick.Shapes 1.12
+import QtQuick
+import QtQuick.Shapes
//![0]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/item/itemGrab.qml b/src/quick/doc/snippets/qml/item/itemGrab.qml
index ef4430175a..dc5a52a123 100644
--- a/src/quick/doc/snippets/qml/item/itemGrab.qml
+++ b/src/quick/doc/snippets/qml/item/itemGrab.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.4
+import QtQuick
Item {
width: 320
@@ -12,12 +12,13 @@ Rectangle {
id: sourceRectangle
width: 100
height: 100
+ focus: true
gradient: Gradient {
GradientStop { position: 0; color: "steelblue" }
GradientStop { position: 1; color: "black" }
}
- Component.onCompleted: {
+ Keys.onSpacePressed: {
sourceRectangle.grabToImage(function(result) {
result.saveToFile("something.png")
})
@@ -30,7 +31,7 @@ Image {
id: image
}
-Component.onCompleted: {
+Keys.onSpacePressed: {
sourceRectangle.grabToImage(function(result) {
image.source = result.url
}, Qt.size(50, 50))
diff --git a/src/quick/doc/snippets/qml/keynavigation.qml b/src/quick/doc/snippets/qml/keynavigation.qml
index 9536cf85f2..498d53e1c7 100644
--- a/src/quick/doc/snippets/qml/keynavigation.qml
+++ b/src/quick/doc/snippets/qml/keynavigation.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Grid {
width: 100; height: 100
diff --git a/src/quick/doc/snippets/qml/keys/keys-handler.qml b/src/quick/doc/snippets/qml/keys/keys-handler.qml
index 00d7ca7fed..3b63f0ac0a 100644
--- a/src/quick/doc/snippets/qml/keys/keys-handler.qml
+++ b/src/quick/doc/snippets/qml/keys/keys-handler.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
width: 400; height: 400
diff --git a/src/quick/doc/snippets/qml/keys/keys-pressed.qml b/src/quick/doc/snippets/qml/keys/keys-pressed.qml
index fe27486b30..ddc4a239c7 100644
--- a/src/quick/doc/snippets/qml/keys/keys-pressed.qml
+++ b/src/quick/doc/snippets/qml/keys/keys-pressed.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
width: 400; height: 400
diff --git a/src/quick/doc/snippets/qml/layerblending.qml b/src/quick/doc/snippets/qml/layerblending.qml
index 0e3c628585..4f174360bf 100644
--- a/src/quick/doc/snippets/qml/layerblending.qml
+++ b/src/quick/doc/snippets/qml/layerblending.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2014 Gunnar Sletta <gunnar@sletta.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
width: 300
diff --git a/src/quick/doc/snippets/qml/layout-simple.qml b/src/quick/doc/snippets/qml/layout-simple.qml
index ef707b3337..4c972a9441 100644
--- a/src/quick/doc/snippets/qml/layout-simple.qml
+++ b/src/quick/doc/snippets/qml/layout-simple.qml
@@ -1,9 +1,9 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.9
-import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
//! [1]
Window {
diff --git a/src/quick/doc/snippets/qml/layoutmirroring.qml b/src/quick/doc/snippets/qml/layoutmirroring.qml
index 6ac975c1de..0c7154e0a1 100644
--- a/src/quick/doc/snippets/qml/layoutmirroring.qml
+++ b/src/quick/doc/snippets/qml/layoutmirroring.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
LayoutMirroring.enabled: true
diff --git a/src/quick/doc/snippets/qml/listview-decorations.qml b/src/quick/doc/snippets/qml/listview-decorations.qml
index 3c5c3bcfc1..af8d2bdcb0 100644
--- a/src/quick/doc/snippets/qml/listview-decorations.qml
+++ b/src/quick/doc/snippets/qml/listview-decorations.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
Rectangle {
@@ -24,7 +24,8 @@ ListModel {
Component {
id: nameDelegate
Text {
- text: name;
+ required property string name
+ text: name
font.pixelSize: 24
}
}
diff --git a/src/quick/doc/snippets/qml/listview-sections.qml b/src/quick/doc/snippets/qml/listview-sections.qml
index f6c4159fd7..54fc4dd8c8 100644
--- a/src/quick/doc/snippets/qml/listview-sections.qml
+++ b/src/quick/doc/snippets/qml/listview-sections.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/listview.qml b/src/quick/doc/snippets/qml/listview.qml
index 028a859fed..948249f849 100644
--- a/src/quick/doc/snippets/qml/listview.qml
+++ b/src/quick/doc/snippets/qml/listview.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/listview/ContactModel.qml b/src/quick/doc/snippets/qml/listview/ContactModel.qml
index bf4662581f..8ea6bf42aa 100644
--- a/src/quick/doc/snippets/qml/listview/ContactModel.qml
+++ b/src/quick/doc/snippets/qml/listview/ContactModel.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
ListModel {
ListElement {
diff --git a/src/quick/doc/snippets/qml/listview/ReusableDelegate.qml b/src/quick/doc/snippets/qml/listview/ReusableDelegate.qml
index bed0df639f..e8e1077509 100644
--- a/src/quick/doc/snippets/qml/listview/ReusableDelegate.qml
+++ b/src/quick/doc/snippets/qml/listview/ReusableDelegate.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.15
+import QtQuick
//![0]
Component {
diff --git a/src/quick/doc/snippets/qml/listview/listview.qml b/src/quick/doc/snippets/qml/listview/listview.qml
index cd8b4c1962..c8df8e727b 100644
--- a/src/quick/doc/snippets/qml/listview/listview.qml
+++ b/src/quick/doc/snippets/qml/listview/listview.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![import]
-import QtQuick 2.0
+import QtQuick
//![import]
Item {
diff --git a/src/quick/doc/snippets/qml/loader/KeyReader.qml b/src/quick/doc/snippets/qml/loader/KeyReader.qml
index 1f20b16291..2bb1c270e6 100644
--- a/src/quick/doc/snippets/qml/loader/KeyReader.qml
+++ b/src/quick/doc/snippets/qml/loader/KeyReader.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
Item {
diff --git a/src/quick/doc/snippets/qml/loader/MyComponent.qml b/src/quick/doc/snippets/qml/loader/MyComponent.qml
index 06ec24dde5..6554648387 100644
--- a/src/quick/doc/snippets/qml/loader/MyComponent.qml
+++ b/src/quick/doc/snippets/qml/loader/MyComponent.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Text { text: index }
diff --git a/src/quick/doc/snippets/qml/loader/MyItem.qml b/src/quick/doc/snippets/qml/loader/MyItem.qml
index dfc77c5c50..2385680d80 100644
--- a/src/quick/doc/snippets/qml/loader/MyItem.qml
+++ b/src/quick/doc/snippets/qml/loader/MyItem.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: myItem
diff --git a/src/quick/doc/snippets/qml/loader/connections.qml b/src/quick/doc/snippets/qml/loader/connections.qml
index 7e49094b2c..fca17479ba 100644
--- a/src/quick/doc/snippets/qml/loader/connections.qml
+++ b/src/quick/doc/snippets/qml/loader/connections.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 100; height: 100
diff --git a/src/quick/doc/snippets/qml/loader/creationContext1.qml b/src/quick/doc/snippets/qml/loader/creationContext1.qml
index 9a19cea82a..91a4051d9d 100644
--- a/src/quick/doc/snippets/qml/loader/creationContext1.qml
+++ b/src/quick/doc/snippets/qml/loader/creationContext1.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/quick/doc/snippets/qml/loader/creationContext2.qml b/src/quick/doc/snippets/qml/loader/creationContext2.qml
index 26c911dd5c..846b9de317 100644
--- a/src/quick/doc/snippets/qml/loader/creationContext2.qml
+++ b/src/quick/doc/snippets/qml/loader/creationContext2.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
width: 400
diff --git a/src/quick/doc/snippets/qml/loader/creationContext3.qml b/src/quick/doc/snippets/qml/loader/creationContext3.qml
index 3bc6439071..e9d47f1e18 100644
--- a/src/quick/doc/snippets/qml/loader/creationContext3.qml
+++ b/src/quick/doc/snippets/qml/loader/creationContext3.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
width: 400
diff --git a/src/quick/doc/snippets/qml/loader/creationContext4.qml b/src/quick/doc/snippets/qml/loader/creationContext4.qml
index 3b10ff2894..fdbeacbfa1 100644
--- a/src/quick/doc/snippets/qml/loader/creationContext4.qml
+++ b/src/quick/doc/snippets/qml/loader/creationContext4.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
diff --git a/src/quick/doc/snippets/qml/loader/focus.qml b/src/quick/doc/snippets/qml/loader/focus.qml
index 6f8adc25a2..724b6f6094 100644
--- a/src/quick/doc/snippets/qml/loader/focus.qml
+++ b/src/quick/doc/snippets/qml/loader/focus.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 200; height: 200
diff --git a/src/quick/doc/snippets/qml/loader/simple.qml b/src/quick/doc/snippets/qml/loader/simple.qml
index 48080be758..f8d2249ac7 100644
--- a/src/quick/doc/snippets/qml/loader/simple.qml
+++ b/src/quick/doc/snippets/qml/loader/simple.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 200
diff --git a/src/quick/doc/snippets/qml/loader/sizeitem.qml b/src/quick/doc/snippets/qml/loader/sizeitem.qml
index 3252d0201e..f0cf7204c2 100644
--- a/src/quick/doc/snippets/qml/loader/sizeitem.qml
+++ b/src/quick/doc/snippets/qml/loader/sizeitem.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 200
diff --git a/src/quick/doc/snippets/qml/loader/sizeloader.qml b/src/quick/doc/snippets/qml/loader/sizeloader.qml
index 4229df9127..8d13def3ba 100644
--- a/src/quick/doc/snippets/qml/loader/sizeloader.qml
+++ b/src/quick/doc/snippets/qml/loader/sizeloader.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 200
diff --git a/src/quick/doc/snippets/qml/localstorage/hello.qml b/src/quick/doc/snippets/qml/localstorage/hello.qml
index bfb2a8c67e..915667848b 100644
--- a/src/quick/doc/snippets/qml/localstorage/hello.qml
+++ b/src/quick/doc/snippets/qml/localstorage/hello.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
color: "white"
diff --git a/src/quick/doc/snippets/qml/models/views-models-delegates.qml b/src/quick/doc/snippets/qml/models/views-models-delegates.qml
index 4d97ae2cf4..8a2f0519d6 100644
--- a/src/quick/doc/snippets/qml/models/views-models-delegates.qml
+++ b/src/quick/doc/snippets/qml/models/views-models-delegates.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [rectangle]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml b/src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml
index fe8816fc1e..d99f0efc6e 100644
--- a/src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml
+++ b/src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
//! [parent begin]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/mousearea/mousearea.qml b/src/quick/doc/snippets/qml/mousearea/mousearea.qml
index 4cb5e226fd..6793284080 100644
--- a/src/quick/doc/snippets/qml/mousearea/mousearea.qml
+++ b/src/quick/doc/snippets/qml/mousearea/mousearea.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [import]
-import QtQuick 2.0
+import QtQuick
//! [import]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/mousearea/mouseareadragfilter.qml b/src/quick/doc/snippets/qml/mousearea/mouseareadragfilter.qml
index db8772bbb8..75b14530d1 100644
--- a/src/quick/doc/snippets/qml/mousearea/mouseareadragfilter.qml
+++ b/src/quick/doc/snippets/qml/mousearea/mouseareadragfilter.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [dragfilter]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 480
diff --git a/src/quick/doc/snippets/qml/multipointtoucharea/multipointtoucharea.qml b/src/quick/doc/snippets/qml/multipointtoucharea/multipointtoucharea.qml
index 1cab349f3a..dff1326cdb 100644
--- a/src/quick/doc/snippets/qml/multipointtoucharea/multipointtoucharea.qml
+++ b/src/quick/doc/snippets/qml/multipointtoucharea/multipointtoucharea.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 400; height: 400
diff --git a/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml b/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml
new file mode 100644
index 0000000000..51f076172d
--- /dev/null
+++ b/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+//![0]
+Window {
+ // visible is false by default
+ Window {
+ transientParent: null
+ visible: true
+ }
+//![0]
+
+ id: outer
+ Timer {
+ interval: 2000
+ running: true
+ onTriggered: outer.visible = true
+ }
+//![1]
+}
+//![1]
diff --git a/src/quick/doc/snippets/qml/numberanimation.qml b/src/quick/doc/snippets/qml/numberanimation.qml
index 6ad18388d7..28de4c7ba3 100644
--- a/src/quick/doc/snippets/qml/numberanimation.qml
+++ b/src/quick/doc/snippets/qml/numberanimation.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 100; height: 100
diff --git a/src/quick/doc/snippets/qml/parallelanimation.qml b/src/quick/doc/snippets/qml/parallelanimation.qml
index 95b45da4c5..1ffcd75e9d 100644
--- a/src/quick/doc/snippets/qml/parallelanimation.qml
+++ b/src/quick/doc/snippets/qml/parallelanimation.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
diff --git a/src/quick/doc/snippets/qml/parentanimation.qml b/src/quick/doc/snippets/qml/parentanimation.qml
index ea66881196..ced59b6842 100644
--- a/src/quick/doc/snippets/qml/parentanimation.qml
+++ b/src/quick/doc/snippets/qml/parentanimation.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 100
diff --git a/src/quick/doc/snippets/qml/parentchange.qml b/src/quick/doc/snippets/qml/parentchange.qml
index f3d7623185..4b04219f52 100644
--- a/src/quick/doc/snippets/qml/parentchange.qml
+++ b/src/quick/doc/snippets/qml/parentchange.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 100
diff --git a/src/quick/doc/snippets/qml/path/arcdirection.qml b/src/quick/doc/snippets/qml/path/arcdirection.qml
index eec544f65b..ef7a68d6d2 100644
--- a/src/quick/doc/snippets/qml/path/arcdirection.qml
+++ b/src/quick/doc/snippets/qml/path/arcdirection.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Path {
startX: 50; startY: 50
diff --git a/src/quick/doc/snippets/qml/path/arcradius.qml b/src/quick/doc/snippets/qml/path/arcradius.qml
index cf7e4226e4..c303f8fa7f 100644
--- a/src/quick/doc/snippets/qml/path/arcradius.qml
+++ b/src/quick/doc/snippets/qml/path/arcradius.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Path {
startX: 0; startY: 100
diff --git a/src/quick/doc/snippets/qml/path/arcrotation.qml b/src/quick/doc/snippets/qml/path/arcrotation.qml
index dde11593ec..26ab0f4661 100644
--- a/src/quick/doc/snippets/qml/path/arcrotation.qml
+++ b/src/quick/doc/snippets/qml/path/arcrotation.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.9
+import QtQuick
//![0]
Path {
startX: 50; startY: 100
diff --git a/src/quick/doc/snippets/qml/path/basicarc.qml b/src/quick/doc/snippets/qml/path/basicarc.qml
index 56c3790424..60a04fc4ce 100644
--- a/src/quick/doc/snippets/qml/path/basicarc.qml
+++ b/src/quick/doc/snippets/qml/path/basicarc.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Path {
startX: 100; startY: 0
diff --git a/src/quick/doc/snippets/qml/path/basiccurve.qml b/src/quick/doc/snippets/qml/path/basiccurve.qml
index 1f3e03056d..a5c521b967 100644
--- a/src/quick/doc/snippets/qml/path/basiccurve.qml
+++ b/src/quick/doc/snippets/qml/path/basiccurve.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Canvas {
width: 400; height: 200
diff --git a/src/quick/doc/snippets/qml/path/largearc.qml b/src/quick/doc/snippets/qml/path/largearc.qml
index ec457ace28..929da8e016 100644
--- a/src/quick/doc/snippets/qml/path/largearc.qml
+++ b/src/quick/doc/snippets/qml/path/largearc.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Path {
startX: 0; startY: 100
diff --git a/src/quick/doc/snippets/qml/pathinterpolator.qml b/src/quick/doc/snippets/qml/pathinterpolator.qml
index fd693998a4..6fa0a8a4da 100644
--- a/src/quick/doc/snippets/qml/pathinterpolator.qml
+++ b/src/quick/doc/snippets/qml/pathinterpolator.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 400
diff --git a/src/quick/doc/snippets/qml/pathview/ContactModel.qml b/src/quick/doc/snippets/qml/pathview/ContactModel.qml
index 8573899707..827184bbb2 100644
--- a/src/quick/doc/snippets/qml/pathview/ContactModel.qml
+++ b/src/quick/doc/snippets/qml/pathview/ContactModel.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
ListModel {
ListElement {
diff --git a/src/quick/doc/snippets/qml/pathview/pathattributes.qml b/src/quick/doc/snippets/qml/pathview/pathattributes.qml
index 1323a25002..7d07b76bf0 100644
--- a/src/quick/doc/snippets/qml/pathview/pathattributes.qml
+++ b/src/quick/doc/snippets/qml/pathview/pathattributes.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 240; height: 200
diff --git a/src/quick/doc/snippets/qml/pathview/pathview.qml b/src/quick/doc/snippets/qml/pathview/pathview.qml
index 4ee4e8ff83..2a801e5d7c 100644
--- a/src/quick/doc/snippets/qml/pathview/pathview.qml
+++ b/src/quick/doc/snippets/qml/pathview/pathview.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 240; height: 200
@@ -12,15 +12,20 @@ Rectangle {
id: delegate
Column {
id: wrapper
+
+ required property url icon
+ required property string name
+
opacity: PathView.isCurrentItem ? 1 : 0.5
+
Image {
anchors.horizontalCenter: nameText.horizontalCenter
width: 64; height: 64
- source: icon
+ source: wrapper.icon
}
Text {
id: nameText
- text: name
+ text: wrapper.name
font.pointSize: 16
}
}
diff --git a/src/quick/doc/snippets/qml/propertyaction-sequential.qml b/src/quick/doc/snippets/qml/propertyaction-sequential.qml
index 9ad5f06717..c2f029e709 100644
--- a/src/quick/doc/snippets/qml/propertyaction-sequential.qml
+++ b/src/quick/doc/snippets/qml/propertyaction-sequential.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Row {
diff --git a/src/quick/doc/snippets/qml/propertyaction.qml b/src/quick/doc/snippets/qml/propertyaction.qml
index 1c4b05facc..28c2b6e247 100644
--- a/src/quick/doc/snippets/qml/propertyaction.qml
+++ b/src/quick/doc/snippets/qml/propertyaction.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Row {
diff --git a/src/quick/doc/snippets/qml/propertyanimation.qml b/src/quick/doc/snippets/qml/propertyanimation.qml
index f950c1eeb8..8850e72d24 100644
--- a/src/quick/doc/snippets/qml/propertyanimation.qml
+++ b/src/quick/doc/snippets/qml/propertyanimation.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Row {
diff --git a/src/quick/doc/snippets/qml/propertychanges.qml b/src/quick/doc/snippets/qml/propertychanges.qml
index ecfaf8a3f9..621abad278 100644
--- a/src/quick/doc/snippets/qml/propertychanges.qml
+++ b/src/quick/doc/snippets/qml/propertychanges.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![import]
-import QtQuick 2.0
+import QtQuick
//![import]
Column {
diff --git a/src/quick/doc/snippets/qml/qml-data-models/dynamic-listmodel.qml b/src/quick/doc/snippets/qml/qml-data-models/dynamic-listmodel.qml
index 4bf9fccb93..eacba90716 100644
--- a/src/quick/doc/snippets/qml/qml-data-models/dynamic-listmodel.qml
+++ b/src/quick/doc/snippets/qml/qml-data-models/dynamic-listmodel.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 250
@@ -15,8 +15,12 @@ Item {
anchors.fill: parent
model: fruitModel
delegate: Row {
- Text { text: "Fruit: " + name }
- Text { text: "Cost: $" + cost }
+ id: delegate
+ required property string name
+ required property real cost
+
+ Text { text: "Fruit: " + delegate.name }
+ Text { text: "Cost: $" + delegate.cost }
}
}
//! [view]
diff --git a/src/quick/doc/snippets/qml/qml-data-models/listelements.qml b/src/quick/doc/snippets/qml/qml-data-models/listelements.qml
index bab4aec6a2..aa8e5e4833 100644
--- a/src/quick/doc/snippets/qml/qml-data-models/listelements.qml
+++ b/src/quick/doc/snippets/qml/qml-data-models/listelements.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 250
@@ -31,8 +31,12 @@ Item {
anchors.fill: parent
model: fruitModel
delegate: Row {
- Text { text: "Fruit: " + name }
- Text { text: "Cost: $" + cost }
+ id: delegate
+ required property string name
+ required property real cost
+
+ Text { text: "Fruit: " + delegate.name }
+ Text { text: "Cost: $" + delegate.cost }
}
}
//! [view]
diff --git a/src/quick/doc/snippets/qml/qml-data-models/listmodel-listview-required.qml b/src/quick/doc/snippets/qml/qml-data-models/listmodel-listview-required.qml
index 040b033141..dd7c21b88e 100644
--- a/src/quick/doc/snippets/qml/qml-data-models/listmodel-listview-required.qml
+++ b/src/quick/doc/snippets/qml/qml-data-models/listmodel-listview-required.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200
diff --git a/src/quick/doc/snippets/qml/qml-data-models/listmodel-listview.qml b/src/quick/doc/snippets/qml/qml-data-models/listmodel-listview.qml
index 03f3df2304..2942eaf3df 100644
--- a/src/quick/doc/snippets/qml/qml-data-models/listmodel-listview.qml
+++ b/src/quick/doc/snippets/qml/qml-data-models/listmodel-listview.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Item {
width: 200; height: 250
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/components/Button.qml b/src/quick/doc/snippets/qml/qml-extending-types/components/Button.qml
index 2ce2669ab3..c317be5f2f 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/components/Button.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/components/Button.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// Button.qml
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 100; height: 100
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/components/application.qml b/src/quick/doc/snippets/qml/qml-extending-types/components/application.qml
index dedc28b3f3..e6572c542f 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/components/application.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/components/application.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// application.qml
-import QtQuick 2.0
+import QtQuick
Column {
Button { width: 50; height: 50 }
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/methods/app.qml b/src/quick/doc/snippets/qml/qml-extending-types/methods/app.qml
index 14221c8904..f5b7714c1a 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/methods/app.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/methods/app.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Rectangle {
id: rect
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/properties/ImageViewer.qml b/src/quick/doc/snippets/qml/qml-extending-types/properties/ImageViewer.qml
index 29567389c6..5d48114c0b 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/properties/ImageViewer.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/properties/ImageViewer.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// ImageViewer.qml
-import QtQuick 2.0
+import QtQuick
Item {
id: item
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/properties/alias-override.qml b/src/quick/doc/snippets/qml/qml-extending-types/properties/alias-override.qml
index 9af9879839..f214e55dc9 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/properties/alias-override.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/properties/alias-override.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Rectangle {
property alias color: childRect.color
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/properties/alias.qml b/src/quick/doc/snippets/qml/qml-extending-types/properties/alias.qml
index 03e288b6e3..c6adac69fa 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/properties/alias.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/properties/alias.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// Button.qml
-import QtQuick 2.0
+import QtQuick
Item {
property alias buttonText: textItem.text
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/properties/alias/ImageViewer.qml b/src/quick/doc/snippets/qml/qml-extending-types/properties/alias/ImageViewer.qml
index 32db5bc230..2fbb13e927 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/properties/alias/ImageViewer.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/properties/alias/ImageViewer.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// ImageViewer.qml
-import QtQuick 2.0
+import QtQuick
Item {
id: item
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/properties/alias/application.qml b/src/quick/doc/snippets/qml/qml-extending-types/properties/alias/application.qml
index 4b827450c4..697a07062d 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/properties/alias/application.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/properties/alias/application.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// application.qml
-import QtQuick 2.0
+import QtQuick
ImageViewer {
id: viewer
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/properties/application.qml b/src/quick/doc/snippets/qml/qml-extending-types/properties/application.qml
index f880f2d555..4cda04660e 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/properties/application.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/properties/application.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
ImageViewer {
id: viewer
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/properties/property-signals.qml b/src/quick/doc/snippets/qml/qml-extending-types/properties/property-signals.qml
index faa6dfb51e..23a48c4d9f 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/properties/property-signals.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/properties/property-signals.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
property int myNumber
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml b/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml
index f37b88ea0a..e53526f16f 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
// Button.qml
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/signals/basic.qml b/src/quick/doc/snippets/qml/qml-extending-types/signals/basic.qml
index b6fd62ce66..7bf81cc227 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/signals/basic.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/signals/basic.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// Button.qml
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/signals/connectdynamic.qml b/src/quick/doc/snippets/qml/qml-extending-types/signals/connectdynamic.qml
index a481990144..5e6469a44b 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/signals/connectdynamic.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/signals/connectdynamic.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
id: item
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/signals/connectslots.qml b/src/quick/doc/snippets/qml/qml-extending-types/signals/connectslots.qml
index e08c16808c..6c9ad3fa03 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/signals/connectslots.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/signals/connectslots.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Item {
id: item
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/signals/no-parameters.qml b/src/quick/doc/snippets/qml/qml-extending-types/signals/no-parameters.qml
index cc248b0c18..dca0073177 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/signals/no-parameters.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/signals/no-parameters.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
// application.qml
-import QtQuick 2.0
+import QtQuick
Button {
width: 100; height: 100
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/signals/parameters.qml b/src/quick/doc/snippets/qml/qml-extending-types/signals/parameters.qml
index 7400e84ae5..a473d9c503 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/signals/parameters.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/signals/parameters.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
// application.qml
Button {
diff --git a/src/quick/doc/snippets/qml/rectangle/rectangle-colors.qml b/src/quick/doc/snippets/qml/rectangle/rectangle-colors.qml
index a2edc4d350..bfa106dc4a 100644
--- a/src/quick/doc/snippets/qml/rectangle/rectangle-colors.qml
+++ b/src/quick/doc/snippets/qml/rectangle/rectangle-colors.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
width: 100; height: 200
diff --git a/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml b/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
index 560950f849..bdf6950189 100644
--- a/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
+++ b/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
width: 100; height: 300
diff --git a/src/quick/doc/snippets/qml/rectangle/rectangle.qml b/src/quick/doc/snippets/qml/rectangle/rectangle.qml
index 14baa83bfc..552119b5e5 100644
--- a/src/quick/doc/snippets/qml/rectangle/rectangle.qml
+++ b/src/quick/doc/snippets/qml/rectangle/rectangle.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 100
diff --git a/src/quick/doc/snippets/qml/regularexpression.qml b/src/quick/doc/snippets/qml/regularexpression.qml
index a38dec48f2..f08a55725b 100644
--- a/src/quick/doc/snippets/qml/regularexpression.qml
+++ b/src/quick/doc/snippets/qml/regularexpression.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.14
+import QtQuick
//![0]
TextInput {
id: hexNumber
diff --git a/src/quick/doc/snippets/qml/repeaters/repeater-grid-index.qml b/src/quick/doc/snippets/qml/repeaters/repeater-grid-index.qml
index 37188f2001..945641e37c 100644
--- a/src/quick/doc/snippets/qml/repeaters/repeater-grid-index.qml
+++ b/src/quick/doc/snippets/qml/repeaters/repeater-grid-index.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 400; height: 400; color: "black"
diff --git a/src/quick/doc/snippets/qml/repeaters/repeater.qml b/src/quick/doc/snippets/qml/repeaters/repeater.qml
index cdb1f6f50a..b1cee3ea8d 100644
--- a/src/quick/doc/snippets/qml/repeaters/repeater.qml
+++ b/src/quick/doc/snippets/qml/repeaters/repeater.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [import]
-import QtQuick 2.0
+import QtQuick
//! [import]
Row {
@@ -24,7 +24,10 @@ Row {
Column {
Repeater {
model: 10
- Text { text: "I'm item " + index }
+ Text {
+ required property int index
+ text: "I'm item " + index
+ }
}
}
//! [index]
@@ -33,7 +36,10 @@ Column {
Column {
Repeater {
model: ["apples", "oranges", "pears"]
- Text { text: "Data: " + modelData }
+ Text {
+ required property string modelData
+ text: "Data: " + modelData
+ }
}
}
//! [modeldata]
diff --git a/src/quick/doc/snippets/qml/righttoleft.qml b/src/quick/doc/snippets/qml/righttoleft.qml
index fde45e6e69..dc81e88455 100644
--- a/src/quick/doc/snippets/qml/righttoleft.qml
+++ b/src/quick/doc/snippets/qml/righttoleft.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
import "righttoleft"
Column {
diff --git a/src/quick/doc/snippets/qml/righttoleft/Child.qml b/src/quick/doc/snippets/qml/righttoleft/Child.qml
index 7118a5feb0..6bedd78a62 100644
--- a/src/quick/doc/snippets/qml/righttoleft/Child.qml
+++ b/src/quick/doc/snippets/qml/righttoleft/Child.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 50; height: 50
diff --git a/src/quick/doc/snippets/qml/rotation.qml b/src/quick/doc/snippets/qml/rotation.qml
index d44ca5f4d6..55a6d54cbb 100644
--- a/src/quick/doc/snippets/qml/rotation.qml
+++ b/src/quick/doc/snippets/qml/rotation.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
-import QtQuick 2.0
+import QtQuick
Row {
x: 10; y: 10
diff --git a/src/quick/doc/snippets/qml/rotationanimation.qml b/src/quick/doc/snippets/qml/rotationanimation.qml
index 6f3d0722c6..9e4f45b876 100644
--- a/src/quick/doc/snippets/qml/rotationanimation.qml
+++ b/src/quick/doc/snippets/qml/rotationanimation.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 300; height: 300
diff --git a/src/quick/doc/snippets/qml/row.qml b/src/quick/doc/snippets/qml/row.qml
index 5951ab8b8b..dd1f7cc2e8 100644
--- a/src/quick/doc/snippets/qml/row.qml
+++ b/src/quick/doc/snippets/qml/row.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 320; height: 110
diff --git a/src/quick/doc/snippets/qml/row/row.qml b/src/quick/doc/snippets/qml/row/row.qml
index 75e8fad100..88f7aa743a 100644
--- a/src/quick/doc/snippets/qml/row/row.qml
+++ b/src/quick/doc/snippets/qml/row/row.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.0
+import QtQuick
Row {
spacing: 2
diff --git a/src/quick/doc/snippets/qml/sequentialanimation.qml b/src/quick/doc/snippets/qml/sequentialanimation.qml
index 2098f395e7..c40d272220 100644
--- a/src/quick/doc/snippets/qml/sequentialanimation.qml
+++ b/src/quick/doc/snippets/qml/sequentialanimation.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
diff --git a/src/quick/doc/snippets/qml/smoothedanimation.qml b/src/quick/doc/snippets/qml/smoothedanimation.qml
index 1cd4ee6b6f..ae9c20a7b9 100644
--- a/src/quick/doc/snippets/qml/smoothedanimation.qml
+++ b/src/quick/doc/snippets/qml/smoothedanimation.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 800; height: 600
diff --git a/src/quick/doc/snippets/qml/splashWindow.qml b/src/quick/doc/snippets/qml/splashWindow.qml
new file mode 100644
index 0000000000..c790fa5c1f
--- /dev/null
+++ b/src/quick/doc/snippets/qml/splashWindow.qml
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [entire]
+import QtQuick
+
+Window {
+ id: mainWindow
+ title: "Main Window"
+ color: "#456"
+ property real defaultSpacing: 10
+
+ property Splash splash: Splash {
+ onTimeout: mainWindow.show()
+ }
+
+ component Splash: Window {
+ id: splash
+
+ // a splash screen has no titlebar
+ flags: Qt.SplashScreen
+ // the transparent color lets background behind the image edges show through
+ color: "transparent"
+ modality: Qt.ApplicationModal // in case another application window is showing
+ title: "Splash Window" // for the taskbar/dock, task switcher etc.
+ visible: true
+
+ // here we use the Screen attached property to center the splash window
+ //! [screen-properties]
+ x: (Screen.width - splashImage.width) / 2
+ y: (Screen.height - splashImage.height) / 2
+ //! [screen-properties]
+ width: splashImage.width
+ height: splashImage.height
+
+ property int timeoutInterval: 2000
+ signal timeout
+
+ Image {
+ id: splashImage
+ source: "images/qt-logo.png"
+ }
+
+ TapHandler {
+ onTapped: splash.timeout()
+ }
+
+ Timer {
+ interval: splash.timeoutInterval; running: true; repeat: false
+ onTriggered: {
+ splash.visible = false
+ splash.timeout()
+ }
+ }
+ }
+}
+//! [entire]
diff --git a/src/quick/doc/snippets/qml/springanimation.qml b/src/quick/doc/snippets/qml/springanimation.qml
index 6937cf8fe6..4c2c974450 100644
--- a/src/quick/doc/snippets/qml/springanimation.qml
+++ b/src/quick/doc/snippets/qml/springanimation.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Item {
width: 300; height: 300
diff --git a/src/quick/doc/snippets/qml/state-when.qml b/src/quick/doc/snippets/qml/state-when.qml
index 58e8e7dd98..98ff68594f 100644
--- a/src/quick/doc/snippets/qml/state-when.qml
+++ b/src/quick/doc/snippets/qml/state-when.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Rectangle {
id: myRect
diff --git a/src/quick/doc/snippets/qml/state.qml b/src/quick/doc/snippets/qml/state.qml
index e3cfac72f6..c749df23b5 100644
--- a/src/quick/doc/snippets/qml/state.qml
+++ b/src/quick/doc/snippets/qml/state.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: myRect
diff --git a/src/quick/doc/snippets/qml/states.qml b/src/quick/doc/snippets/qml/states.qml
index 285f395f0b..426ce3be4f 100644
--- a/src/quick/doc/snippets/qml/states.qml
+++ b/src/quick/doc/snippets/qml/states.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![document]
-import QtQuick 2.0
+import QtQuick
//![parent begin]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/states/statechangescript.qml b/src/quick/doc/snippets/qml/states/statechangescript.qml
index 074f02988d..5461861c5b 100644
--- a/src/quick/doc/snippets/qml/states/statechangescript.qml
+++ b/src/quick/doc/snippets/qml/states/statechangescript.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Item {
//! [state and transition]
diff --git a/src/quick/doc/snippets/qml/systempalette.qml b/src/quick/doc/snippets/qml/systempalette.qml
index 102e5c28d3..c157856350 100644
--- a/src/quick/doc/snippets/qml/systempalette.qml
+++ b/src/quick/doc/snippets/qml/systempalette.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
diff --git a/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.h b/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.h
index a4def6be54..1de11ed48f 100644
--- a/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.h
+++ b/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.h
@@ -12,7 +12,7 @@ class TableModel : public QAbstractTableModel
{
Q_OBJECT
QML_ELEMENT
- QML_ADDED_IN_MINOR_VERSION(1)
+ QML_ADDED_IN_VERSION(1, 1)
public:
int rowCount(const QModelIndex & = QModelIndex()) const override
diff --git a/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.qml b/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.qml
index b06783d864..6158c2f75f 100644
--- a/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.qml
+++ b/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.qml
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.12
+import QtQuick
import TableModel 0.1
TableView {
diff --git a/src/quick/doc/snippets/qml/tableview/editdelegate.qml b/src/quick/doc/snippets/qml/tableview/editdelegate.qml
index b320969347..11de5f5434 100644
--- a/src/quick/doc/snippets/qml/tableview/editdelegate.qml
+++ b/src/quick/doc/snippets/qml/tableview/editdelegate.qml
@@ -44,8 +44,8 @@ Window {
TableView.onCommit: {
display = text
- // display = text is short-hand for:
- // let index = TableView.view.modelIndex(column, row)
+ // 'display = text' is short-hand for:
+ // let index = TableView.view.index(row, column)
// TableView.view.model.setData(index, text, Qt.DisplayRole)
}
}
diff --git a/src/quick/doc/snippets/qml/tableview/keyboard-navigation.qml b/src/quick/doc/snippets/qml/tableview/keyboard-navigation.qml
index 8af308dec5..9231b9e1a5 100644
--- a/src/quick/doc/snippets/qml/tableview/keyboard-navigation.qml
+++ b/src/quick/doc/snippets/qml/tableview/keyboard-navigation.qml
@@ -3,7 +3,7 @@
import QtQuick
import QtQuick.Controls
-import Qt.labs.qmlmodels 1.0
+import Qt.labs.qmlmodels
//![0]
ApplicationWindow {
diff --git a/src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml b/src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml
index be861ec28a..f0a28a4be6 100644
--- a/src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml
+++ b/src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.14
-import Qt.labs.qmlmodels 1.0
+import QtQuick
+import Qt.labs.qmlmodels
TableView {
anchors.fill: parent
diff --git a/src/quick/doc/snippets/qml/tableview/reusabledelegate.qml b/src/quick/doc/snippets/qml/tableview/reusabledelegate.qml
index f3ca4830dd..0f68786ac4 100644
--- a/src/quick/doc/snippets/qml/tableview/reusabledelegate.qml
+++ b/src/quick/doc/snippets/qml/tableview/reusabledelegate.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.12
+import QtQuick
//![0]
Component {
diff --git a/src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml b/src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml
index 08848fc1ef..40006e4e6c 100644
--- a/src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml
+++ b/src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.12
+import QtQuick
//![0]
TableView {
diff --git a/src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml b/src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml
index b4fbf19bec..6911d47eb5 100644
--- a/src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml
+++ b/src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.12
+import QtQuick
//![0]
TableView {
diff --git a/src/quick/doc/snippets/qml/text/onLinkActivated.qml b/src/quick/doc/snippets/qml/text/onLinkActivated.qml
index 3bb59f2f6e..25223a5d55 100644
--- a/src/quick/doc/snippets/qml/text/onLinkActivated.qml
+++ b/src/quick/doc/snippets/qml/text/onLinkActivated.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
width: 700; height: 400
diff --git a/src/quick/doc/snippets/qml/text/textEditFormats.qml b/src/quick/doc/snippets/qml/text/textEditFormats.qml
index caf1a842a3..259beab672 100644
--- a/src/quick/doc/snippets/qml/text/textEditFormats.qml
+++ b/src/quick/doc/snippets/qml/text/textEditFormats.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.14
+import QtQuick
//![0]
Column {
diff --git a/src/quick/doc/snippets/qml/text/textFormats.qml b/src/quick/doc/snippets/qml/text/textFormats.qml
index c2de9b4ca8..d79bcb01bb 100644
--- a/src/quick/doc/snippets/qml/text/textFormats.qml
+++ b/src/quick/doc/snippets/qml/text/textFormats.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.14
+import QtQuick
//![0]
Column {
diff --git a/src/quick/doc/snippets/qml/textEditStatusSwitch.qml b/src/quick/doc/snippets/qml/textEditStatusSwitch.qml
new file mode 100644
index 0000000000..ad29220482
--- /dev/null
+++ b/src/quick/doc/snippets/qml/textEditStatusSwitch.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+
+//! [0]
+TextEdit {
+ id: edit
+ width: 300
+ height: 200
+ textFormat: TextEdit.MarkdownText
+ textDocument.source: "example.md"
+ wrapMode: TextEdit.WordWrap
+
+ Text {
+ anchors {
+ bottom: parent.bottom
+ right: parent.right
+ }
+ color: edit.textDocument.status === TextDocument.Loaded ? "darkolivegreen" : "tomato"
+ text:
+ switch (edit.textDocument.status) {
+ case TextDocument.Loading:
+ return qsTr("Loading ") + edit.textDocument.source
+ case TextDocument.Loaded:
+ return qsTr("Loaded ") + edit.textDocument.source
+ default:
+ return edit.textDocument.errorString
+ }
+ }
+}
+//! [0]
diff --git a/src/quick/doc/snippets/qml/texteditor.qml b/src/quick/doc/snippets/qml/texteditor.qml
index 8b667001c7..687d419ca3 100644
--- a/src/quick/doc/snippets/qml/texteditor.qml
+++ b/src/quick/doc/snippets/qml/texteditor.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [0]
Flickable {
diff --git a/src/quick/doc/snippets/qml/texthandling.qml b/src/quick/doc/snippets/qml/texthandling.qml
index 2aa4a97030..bf98fb71c3 100644
--- a/src/quick/doc/snippets/qml/texthandling.qml
+++ b/src/quick/doc/snippets/qml/texthandling.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [document]
-import QtQuick 2.14
+import QtQuick
//! [parent begin]
diff --git a/src/quick/doc/snippets/qml/transition-animation.qml b/src/quick/doc/snippets/qml/transition-animation.qml
new file mode 100644
index 0000000000..a0482018ad
--- /dev/null
+++ b/src/quick/doc/snippets/qml/transition-animation.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+
+
+Item {
+//![0]
+ transitions: Transition {
+ PropertyAnimation { duration: 3000 }
+ ColorAnimation { duration: 3000 }
+ }
+//![0]
+}
diff --git a/src/quick/doc/snippets/qml/transition-from-to-modified.qml b/src/quick/doc/snippets/qml/transition-from-to-modified.qml
index 2e5c074e23..1ec9edd0a1 100644
--- a/src/quick/doc/snippets/qml/transition-from-to-modified.qml
+++ b/src/quick/doc/snippets/qml/transition-from-to-modified.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
diff --git a/src/quick/doc/snippets/qml/transition-from-to.qml b/src/quick/doc/snippets/qml/transition-from-to.qml
index c282c3c16f..c9ebc3aa30 100644
--- a/src/quick/doc/snippets/qml/transition-from-to.qml
+++ b/src/quick/doc/snippets/qml/transition-from-to.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/transition-reversible.qml b/src/quick/doc/snippets/qml/transition-reversible.qml
index d5f225eb40..57acd92f89 100644
--- a/src/quick/doc/snippets/qml/transition-reversible.qml
+++ b/src/quick/doc/snippets/qml/transition-reversible.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.12
+import QtQuick
//![0]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/transition.qml b/src/quick/doc/snippets/qml/transition.qml
index 97188a8181..abcb9b6e19 100644
--- a/src/quick/doc/snippets/qml/transition.qml
+++ b/src/quick/doc/snippets/qml/transition.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
-import QtQuick 2.0
+import QtQuick
Rectangle {
id: rect
diff --git a/src/quick/doc/snippets/qml/transitions-list.qml b/src/quick/doc/snippets/qml/transitions-list.qml
index de8a364744..9ad70d6ccf 100644
--- a/src/quick/doc/snippets/qml/transitions-list.qml
+++ b/src/quick/doc/snippets/qml/transitions-list.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.13
+import QtQuick
Rectangle {
id: page
diff --git a/src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml b/src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml
index 97574487e9..49483af572 100644
--- a/src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml
+++ b/src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml
@@ -1,26 +1,31 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
import QtQuick
+import QtQuick.Controls
-Window {
- width: 600
- height: 400
+ApplicationWindow {
+ width: 800
+ height: 600
visible: true
TreeView {
+ id: treeView
anchors.fill: parent
+ anchors.margins: 10
+ clip: true
+
+ selectionModel: ItemSelectionModel {}
+
// The model needs to be a QAbstractItemModel
// model: yourTreeModel
delegate: Item {
- id: treeDelegate
-
implicitWidth: padding + label.x + label.implicitWidth + padding
implicitHeight: label.implicitHeight * 1.5
- readonly property real indent: 20
+ readonly property real indentation: 20
readonly property real padding: 5
// Assigned to by TreeView:
@@ -29,24 +34,52 @@ Window {
required property bool expanded
required property int hasChildren
required property int depth
+ required property int row
+ required property int column
+ required property bool current
- TapHandler {
- onTapped: treeView.toggleExpanded(row)
+ // Rotate indicator when expanded by the user
+ // (requires TreeView to have a selectionModel)
+ property Animation indicatorAnimation: NumberAnimation {
+ target: indicator
+ property: "rotation"
+ from: expanded ? 0 : 90
+ to: expanded ? 90 : 0
+ duration: 100
+ easing.type: Easing.OutQuart
}
+ TableView.onPooled: indicatorAnimation.complete()
+ TableView.onReused: if (current) indicatorAnimation.start()
+ onExpandedChanged: indicator.rotation = expanded ? 90 : 0
- Text {
+ Rectangle {
+ id: background
+ anchors.fill: parent
+ color: row === treeView.currentRow ? palette.highlight : "black"
+ opacity: (treeView.alternatingRows && row % 2 !== 0) ? 0.3 : 0.1
+ }
+
+ Label {
id: indicator
- visible: treeDelegate.isTreeNode && treeDelegate.hasChildren
- x: padding + (treeDelegate.depth * treeDelegate.indent)
- anchors.verticalCenter: label.verticalCenter
- text: "â–¸"
- rotation: treeDelegate.expanded ? 90 : 0
+ x: padding + (depth * indentation)
+ anchors.verticalCenter: parent.verticalCenter
+ visible: isTreeNode && hasChildren
+ text: "â–¶"
+
+ TapHandler {
+ onSingleTapped: {
+ let index = treeView.index(row, column)
+ treeView.selectionModel.setCurrentIndex(index, ItemSelectionModel.NoUpdate)
+ treeView.toggleExpanded(row)
+ }
+ }
}
- Text {
+ Label {
id: label
- x: padding + (treeDelegate.isTreeNode ? (treeDelegate.depth + 1) * treeDelegate.indent : 0)
- width: treeDelegate.width - treeDelegate.padding - x
+ x: padding + (isTreeNode ? (depth + 1) * indentation : 0)
+ anchors.verticalCenter: parent.verticalCenter
+ width: parent.width - padding - x
clip: true
text: model.display
}
diff --git a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-basic.qml b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-basic.qml
index 5a597d2a89..f5ef24a174 100644
--- a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-basic.qml
+++ b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-basic.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [0]
ListView {
diff --git a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-delayedbyindex.qml b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-delayedbyindex.qml
index a12458770a..444fdd5434 100644
--- a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-delayedbyindex.qml
+++ b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-delayedbyindex.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
ListView {
width: 240; height: 320
diff --git a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-intermediatemove.qml b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-intermediatemove.qml
index 656417aba0..1f913fe5c8 100644
--- a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-intermediatemove.qml
+++ b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-intermediatemove.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
ListView {
width: 240; height: 320
diff --git a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-interruptedgood.qml b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-interruptedgood.qml
index d7e8ce60fb..79d39a49f1 100644
--- a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-interruptedgood.qml
+++ b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-interruptedgood.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
ListView {
width: 240; height: 320
diff --git a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-pathanim.qml b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-pathanim.qml
index 362aa81923..8767aab58c 100644
--- a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-pathanim.qml
+++ b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-pathanim.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
ListView {
width: 240; height: 320
diff --git a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactionbad.qml b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactionbad.qml
index faf5e76ca9..984bdc1e84 100644
--- a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactionbad.qml
+++ b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactionbad.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//! [0]
ListView {
diff --git a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactiongood.qml b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactiongood.qml
index 1cf181de3f..c0819c2103 100644
--- a/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactiongood.qml
+++ b/src/quick/doc/snippets/qml/viewtransitions/viewtransitions-scriptactiongood.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
ListView {
width: 240; height: 320
diff --git a/src/quick/doc/snippets/qml/visualparent.qml b/src/quick/doc/snippets/qml/visualparent.qml
index eeca529612..22da8a6622 100644
--- a/src/quick/doc/snippets/qml/visualparent.qml
+++ b/src/quick/doc/snippets/qml/visualparent.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/visualparent2.qml b/src/quick/doc/snippets/qml/visualparent2.qml
index 4824686273..a5957100ef 100644
--- a/src/quick/doc/snippets/qml/visualparent2.qml
+++ b/src/quick/doc/snippets/qml/visualparent2.qml
@@ -1,7 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.0
+import QtQuick
//![0]
Rectangle {
diff --git a/src/quick/doc/snippets/qml/windowActiveAttached.qml b/src/quick/doc/snippets/qml/windowActiveAttached.qml
new file mode 100644
index 0000000000..791091ef82
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowActiveAttached.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//![entire]
+import QtQuick
+
+Text {
+ text: Window.active ? "active" : "inactive"
+}
+//![entire]
diff --git a/src/quick/doc/snippets/qml/windowPalette.qml b/src/quick/doc/snippets/qml/windowPalette.qml
new file mode 100644
index 0000000000..0638213c57
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowPalette.qml
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//![entire]
+import QtQuick
+import QtQuick.Controls
+
+//![declaration-and-color]
+Window {
+ visible: true
+
+ // here we use the Window.active and Window.palette ordinary properties
+ color: active ? palette.active.window : palette.inactive.window
+//![declaration-and-color]
+
+ // colors that are not customized here come from SystemPalette
+ palette.active.window: "peachpuff"
+ palette.windowText: "brown"
+
+ Text {
+ anchors.centerIn: parent
+ // here we use the Window.active attached property and the Item.palette property
+ color: Window.active ? palette.active.windowText : palette.inactive.windowText
+ text: Window.active ? "active" : "inactive"
+ }
+
+ Button {
+ text: "Button"
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 6
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+//![closing-brace]
+}
+//![closing-brace]
+//![entire]
diff --git a/src/quick/doc/snippets/qml/windowVisibility.qml b/src/quick/doc/snippets/qml/windowVisibility.qml
new file mode 100644
index 0000000000..fcdbd5aef1
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowVisibility.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [entire]
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ id: win
+ flags: Qt.Window | Qt.WindowFullscreenButtonHint
+ visibility: fullscreenButton.checked ? Window.FullScreen : Window.Windowed
+
+ Button {
+ id: fullscreenButton
+ anchors {
+ right: parent.right
+ top: parent.top
+ margins: 6
+ }
+ width: height
+ checkable: true
+ Binding on checked { value: win.visibility === Window.FullScreen }
+ text: "⛶"
+ ToolTip.visible: hovered
+ ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
+ ToolTip.text: win.visibility === Window.FullScreen ? qsTr("restore") : qsTr("fill screen")
+ }
+}
+//! [entire]
diff --git a/src/quick/doc/snippets/qml/windowconstraints.qml b/src/quick/doc/snippets/qml/windowconstraints.qml
index 87424ffe79..e7cb0b747c 100644
--- a/src/quick/doc/snippets/qml/windowconstraints.qml
+++ b/src/quick/doc/snippets/qml/windowconstraints.qml
@@ -1,9 +1,9 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.2
-import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
Window {
//! [binddefaultsize]
diff --git a/src/quick/doc/snippets/qquickrhiitem/qquickrhiitem_intro.cpp b/src/quick/doc/snippets/qquickrhiitem/qquickrhiitem_intro.cpp
new file mode 100644
index 0000000000..3a3d1585bf
--- /dev/null
+++ b/src/quick/doc/snippets/qquickrhiitem/qquickrhiitem_intro.cpp
@@ -0,0 +1,147 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtQuick/QQuickRhiItem>
+#include <rhi/qrhi.h>
+
+//![0]
+class ExampleRhiItemRenderer : public QQuickRhiItemRenderer
+{
+public:
+ void initialize(QRhiCommandBuffer *cb) override;
+ void synchronize(QQuickRhiItem *item) override;
+ void render(QRhiCommandBuffer *cb) override;
+
+private:
+ QRhi *m_rhi = nullptr;
+ std::unique_ptr<QRhiBuffer> m_vbuf;
+ std::unique_ptr<QRhiBuffer> m_ubuf;
+ std::unique_ptr<QRhiShaderResourceBindings> m_srb;
+ std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
+ QMatrix4x4 m_viewProjection;
+ float m_angle = 0.0f;
+};
+
+class ExampleRhiItem : public QQuickRhiItem
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(ExampleRhiItem)
+ Q_PROPERTY(float angle READ angle WRITE setAngle NOTIFY angleChanged)
+
+public:
+ QQuickRhiItemRenderer *createRenderer() override;
+
+ float angle() const { return m_angle; }
+ void setAngle(float a);
+
+signals:
+ void angleChanged();
+
+private:
+ float m_angle = 0.0f;
+};
+
+QQuickRhiItemRenderer *ExampleRhiItem::createRenderer()
+{
+ return new ExampleRhiItemRenderer;
+}
+
+void ExampleRhiItem::setAngle(float a)
+{
+ if (m_angle == a)
+ return;
+
+ m_angle = a;
+ emit angleChanged();
+ update();
+}
+
+void ExampleRhiItemRenderer::synchronize(QQuickRhiItem *rhiItem)
+{
+ ExampleRhiItem *item = static_cast<ExampleRhiItem *>(rhiItem);
+ if (item->angle() != m_angle)
+ m_angle = item->angle();
+}
+
+static QShader getShader(const QString &name)
+{
+ QFile f(name);
+ return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
+}
+
+static float vertexData[] = {
+ 0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
+ 0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
+};
+
+void ExampleRhiItemRenderer::initialize(QRhiCommandBuffer *cb)
+{
+ if (m_rhi != rhi()) {
+ m_pipeline.reset();
+ m_rhi = rhi();
+ }
+
+ if (!m_pipeline) {
+ m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)));
+ m_vbuf->create();
+
+ m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64));
+ m_ubuf->create();
+
+ m_srb.reset(m_rhi->newShaderResourceBindings());
+ m_srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, m_ubuf.get()),
+ });
+ m_srb->create();
+
+ m_pipeline.reset(m_rhi->newGraphicsPipeline());
+ m_pipeline->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/shaders/color.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/shaders/color.frag.qsb")) }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
+ });
+ m_pipeline->setVertexInputLayout(inputLayout);
+ m_pipeline->setShaderResourceBindings(m_srb.get());
+ m_pipeline->setRenderPassDescriptor(renderTarget()->renderPassDescriptor());
+ m_pipeline->create();
+
+ QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData);
+ cb->resourceUpdate(resourceUpdates);
+ }
+
+ const QSize outputSize = renderTarget()->pixelSize();
+ m_viewProjection = m_rhi->clipSpaceCorrMatrix();
+ m_viewProjection.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
+ m_viewProjection.translate(0, 0, -4);
+}
+
+void ExampleRhiItemRenderer::render(QRhiCommandBuffer *cb)
+{
+ QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ QMatrix4x4 modelViewProjection = m_viewProjection;
+ modelViewProjection.rotate(m_angle, 0, 1, 0);
+ resourceUpdates->updateDynamicBuffer(m_ubuf.get(), 0, 64, modelViewProjection.constData());
+
+ const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f);
+ cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, resourceUpdates);
+
+ cb->setGraphicsPipeline(m_pipeline.get());
+ const QSize outputSize = renderTarget()->pixelSize();
+ cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(3);
+
+ cb->endPass();
+}
+//![0]
diff --git a/src/quick/doc/src/advtutorial.qdoc b/src/quick/doc/src/advtutorial.qdoc
index dc79abbc1e..6a00162753 100644
--- a/src/quick/doc/src/advtutorial.qdoc
+++ b/src/quick/doc/src/advtutorial.qdoc
@@ -411,7 +411,7 @@ score data to the web server. If it had returned a QML file (or a URL to a QML f
way as you did with the blocks.
An alternate way to access and submit web-based data would be to use QML types designed for this purpose. XmlListModel
-makes it very easy to fetch and display XML based data such as RSS in a QML application (see the Flickr demo for an example).
+makes it very easy to fetch and display XML based data such as RSS in a QML application.
\section2 That's It!
diff --git a/src/quick/doc/src/concepts/effects/particles.qdoc b/src/quick/doc/src/concepts/effects/particles.qdoc
index 618e736b70..e66d6f9b4b 100644
--- a/src/quick/doc/src/concepts/effects/particles.qdoc
+++ b/src/quick/doc/src/concepts/effects/particles.qdoc
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
+ \keyword Qt Quick Particles
\qmlmodule QtQuick.Particles
\title Qt Quick Particles QML Types
\ingroup qmlmodules
diff --git a/src/quick/doc/src/concepts/effects/topic.qdoc b/src/quick/doc/src/concepts/effects/topic.qdoc
index afdbf0f9a2..bd54313106 100644
--- a/src/quick/doc/src/concepts/effects/topic.qdoc
+++ b/src/quick/doc/src/concepts/effects/topic.qdoc
@@ -15,6 +15,9 @@ a useful way to subtly communicate to the user (for example, which visual
item is active, or how focus is being transferred). Over-use of visual
effects can actually detract from the user-experience.
+For more information about applying post-processing effects, such as blur, drop
+shadow, or colorize, to an \l Item, see the \l MultiEffect QML type.
+
\section1 Visual Transformation
Visual objects can be transformed. For example, they can be scaled or rotated.
diff --git a/src/quick/doc/src/concepts/input/focus.qdoc b/src/quick/doc/src/concepts/input/focus.qdoc
index 225eea7788..8c6f73024b 100644
--- a/src/quick/doc/src/concepts/input/focus.qdoc
+++ b/src/quick/doc/src/concepts/input/focus.qdoc
@@ -18,8 +18,8 @@ scope based extension to Qt's traditional keyboard focus model.
When the user presses or releases a key, the following occurs:
\list 1
\li Qt receives the key action and generates a key event.
-\li If a \l QQuickWindow is the active window, the key event
-is delivered to it.
+\li If a \l QQuickWindow is the \l{QGuiApplication::focusWindow()}{focus window}
+of the application, the key event is delivered to it.
\li The key event is delivered by the scene to the \l Item with
\e {active focus}. If no item has active focus, the key event is ignored.
\li If the \l QQuickItem with active focus accepts the key event, propagation
diff --git a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
index 182773b0f5..a28ce21172 100644
--- a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
+++ b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
@@ -17,6 +17,8 @@
behavioral concerns are better separated, and the behavior is built up by
finer-grained composition.
+ The \l {Qt Quick Examples - Pointer Handlers} demonstrates some use cases for these.
+
The pre-existing \l Keys attached property is similar in concept, so we
refer to the pointing-device-oriented handlers plus \c Keys together as the
set of Input Handlers. We expect to offer more attached-property use cases
@@ -70,7 +72,8 @@
PointHandler) can work only with passive grabs; others require exclusive
grabs; and others can "lurk" with passive grabs until they detect that a
gesture is being performed, and then make the transition from passive to
- exclusive grab.
+ exclusive grab. TapHandler's grabbing behavior is
+ \l {TapHandler::gesturePolicy}{configurable}.
When a grab transition is requested, \l PointerHandler::grabPermissions,
\l QQuickItem::keepMouseGrab() and \l QQuickItem::keepTouchGrab() control
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
index b064368ce7..908a82fdcb 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
@@ -25,12 +25,14 @@
\list
\li \l {Qt Quick Layouts Overview}
+ \li \l {Qt Quick Responsive Layouts}
\endlist
\section1 Examples
\list
\li \l {Qt Quick Layouts - Basic Example}
+ \li \l {Qt Quick Layouts - Responsive Layout Example}
\endlist
\section1 Reference
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc
index 115b8faf44..6173e78f6d 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc
@@ -144,11 +144,11 @@
effective preferred height is determined from the
\l{Item::implicitHeight}{implicitHeight}.
- \note If you don't specify neither preferredWidth nor implicitWidth,
- the Layout will query \l width as an ultimate value for the effective
- preferred width. However, you shouldn't rely on \l width as a source for
- the effective preferred width, as that may cause unexpected behavior.
- For instance, changing the \l{Item::width}{width} or
+ \note If you don't specify neither preferredWidth nor implicitWidth, the
+ Layout will query \l {Item::}{width} as an ultimate value for the effective
+ preferred width. However, you shouldn't rely on \l {Item::}{width} as a
+ source for the effective preferred width, as that may cause unexpected
+ behavior. For instance, changing the \l{Item::}{width} or
\l{Item::height}{height} properties won't trigger a layout rearrangement,
or the layout might use the actual width and height -- not the width and
height specified in your QML file -- when forced to do a full rebuild.
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-responsive.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-responsive.qdoc
new file mode 100644
index 0000000000..6287815f65
--- /dev/null
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-responsive.qdoc
@@ -0,0 +1,101 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtquicklayouts-responsive.html
+ \title Qt Quick Responsive Layouts
+ \brief A guideline to make Qt Quick Layouts adaptive to screen size.
+
+ Layouts are a good technique to make resizable user interfaces. However,
+ this approach has its limitations as we cannot shrink and expand items
+ limitless without sacrificing usability and aesthetics. At some point, it
+ makes more sense to reorganize, remove or add certain elements. Adapting to
+ different devices (e.g. phones and tables) and screen orientations
+ (landscape or portrait) can be implemented similarly. This is what
+ we usually understand as responsive layouts and \l {Qt Quick Layouts}
+ provide various APIs to implement them.
+
+ \section1 Static hierarchy, adaptive layout
+
+ Layouts have a hierarchy, which is usually defined by the declarative QML
+ code. For some simple responsive layouts, it is sufficient to keep the
+ hierarchy unmodified and instead just tweak some of the properties that
+ affect layouting.
+
+ \section2 Declarative description
+
+ The simplest approach to change layouting is to modify layout properties
+ and \l {Layout} attached properties with small expressions. You can for
+ instance use ternary operators in order to modify the layout depending
+ on its width.
+ \l {Item} properties, such as \l {Item::visible}{Item.visible},
+ hiding or showing various parts of the interface, can be modified the same
+ way.
+
+ In the following snippet, this concept is used to change a two-column
+ layout into a single-column layout if the window width is smaller than a
+ certain value.
+
+ \snippet layouts/responsiveDeclarative.qml document
+
+ The resulting layouts look like this, depending on the width of the window.
+
+ \div {class="float-right"}
+ \inlineimage simpleProxy.png
+ \enddiv
+
+ Various levels of layouts and items can be nested but \l {Item}{Items} can only be moved within a their \l{Item::parent}{Item.parent}.
+
+ \section2 States
+
+ The same result can be achieved with \l {Qt Quick States}. The upside of
+ using states is that the \l {Layout} properties for a specific layout are
+ collected at a single point in the QML file (at least the changing ones).
+ The previously shown example can be implemented as follows and the result
+ looks and behaves the exact same.
+
+ \snippet layouts/responsiveStates.qml document
+
+ \section2 LayoutItemProxy
+
+ A third approach is the application of the \l {LayoutItemProxy}. The
+ implementation of the previously shown minimalistic example can be found in
+ the type documentation. In contrast to previously shown solutions, the
+ \l {LayoutItemProxy} enables the declaration of completely separate layouts
+ for various form factors. Especially with more complex layouts this might be
+ useful to improve and maintain a reasonable source code structure.
+
+ Note, that the \l{LayoutItemProxy} API is a technical preview and might
+ be subject to change or removal in future Qt versions.
+
+ \section1 Adaptive hierarchy, adaptive layout
+
+ More complex reconstructions of the layout might require changes to
+ the hierarchy. A small stand-alone button in a small layout might be
+ combined with other buttons and put into a box of a larger layout. An item
+ that is fully visible in one layout, might require a \l {Flickable} in
+ another, smaller layout. In this scenario, it is best to rely on the \l {LayoutItemProxy}. The \l {LayoutItemProxy} allows to move \l{Item}{Items} across various hierarchy levels and between different \l{Item::parent}{Item.parent}.
+
+ The \l {Qt Quick Layouts - Responsive Layout Example} shows a case where an
+ item is moved between different hierarchy levels, put into a \l {Flickable}
+ in one case and on the top level in another layout. The two resulting
+ layouts look as follows.
+
+ \div {class="float-right"}
+ \image qtquicklayouts-example-responsivelayouts.png
+ \enddiv
+
+
+ \section1 Useful links: Consult your design guidelines
+
+ Many design guidelines offer help and tips to create responsive layouts.
+ Implementing the respective techniques is possible with the APIs mentioned
+ above. For further information we recommend the following links:
+
+ \list
+ \li \l {https://developer.apple.com/design/human-interface-guidelines/layout}{Apple human interface guidelines}
+ \li \l {https://m3.material.io/foundations/layout/applying-layout/window-size-classes}{Material3 layouts}
+ \li \l {https://learn.microsoft.com/en-us/windows/apps/design/layout/responsive-design}{Microsoft Fluent responsive design techniques}
+ \endlist
+
+*/
diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
index d84ccc9176..3e8fbf7dfd 100644
--- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
+++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
@@ -43,7 +43,7 @@ within the Qt install directory.
\note There is no way for the view to know that the contents of a QStringList
have changed. If the QStringList changes, it will be necessary to reset
-the model by calling QQmlContext::setContextProperty() again.
+the model by setting the view's \c model property again.
\section2 QVariantList-based Model
@@ -79,10 +79,8 @@ the ListView delegate:
\snippet models/objectlistmodel/view.qml 0
-Note the use of \c color property with qualifier.
-The properties of the object are not replicated in the \c model
-object, as they are easily available via the \c modelData
-object.
+Note the use of the \c color property. You can require existing properties
+by declaring them as \c required in a derived type.
The complete source code for this example is available in
\l {models/objectlistmodel}{examples/quick/models/objectlistmodel}
@@ -90,7 +88,7 @@ within the Qt install directory.
Note: There is no way for the view to know that the contents of a QList
has changed. If the QList changes, it is necessary to reset
-the model by calling QQmlContext::setContextProperty() again.
+the model by setting the \c model property again.
\section2 QAbstractItemModel Subclass
@@ -230,12 +228,10 @@ model:
\section2 Exposing C++ Data Models to QML
-The above examples use QQmlContext::setContextProperty() to set
+The above examples use required properties on the view to set
model values directly in QML components. An alternative to this is to
-register the C++ model class as a QML type (either
-\l{Defining QML Types from C++}{directly} from a C++ entry-point, or within
-the initialization function of a \l{Creating C++ Plugins for QML}
-{QML C++ plugin}, as shown below). This would allow the model classes to be
+register the C++ model class as a QML type (see
+\l{Defining QML Types from C++}). This allows the model classes to be
created directly as types within QML:
\table
@@ -244,16 +240,12 @@ created directly as types within QML:
\li C++
\li
\code
-class MyModelPlugin : public QQmlExtensionPlugin
+class MyModel : public QAbstractItemModel
{
Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.QmlExtension.MyModel" FILE "mymodel.json")
-public:
- void registerTypes(const char *uri)
- {
- qmlRegisterType<MyModel>(uri, 1, 0,
- "MyModel");
- }
+ QML_ELEMENT
+
+ // [...]
}
\endcode
\row
@@ -262,7 +254,6 @@ public:
\qml
MyModel {
id: myModel
- ListElement { someProperty: "some value" }
}
\endqml
@@ -270,14 +261,17 @@ MyModel {
ListView {
width: 200; height: 250
model: myModel
- delegate: Text { text: someProperty }
+ delegate: Text {
+ required property string someProperty
+ text: someProperty
+ }
}
\endqml
\endtable
-See \l {Writing QML Extensions with C++} for details on writing QML C++
-plugins.
+See \l {Writing QML Extensions with C++} for details on writing QML types
+in C++.
\section2 Changing Model Data
diff --git a/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc b/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
index dc44407766..3aaf40f199 100644
--- a/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
+++ b/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
@@ -4,9 +4,9 @@
/*!
\page qtquick-modelviewsdata-modelview.html
\title Models and Views in Qt Quick
-\brief how to display and form data in Qt Quick
+\brief how to display and format data in Qt Quick
-Simply put, applications need to form data and display the data. Qt Quick has the
+Most applications need to format data and display the data. Qt Quick has the
notion of \e models, \e views, and \e delegates to display data. They modularize
the visualization of data in order to give the developer or designer control
over the different aspects of the data. A developer can swap a list view with a
@@ -21,7 +21,7 @@ types for creating models.
\li \b View - a container that displays the data. The view might
display the data in a list or a grid.
\li \b Delegate - dictates how the data should appear in the view.
-The delegate takes each data in the model and encapsulates it. The data is
+The delegate takes each unit of data in the model and encapsulates it. The data is
accessible through the delegate. The delegate can also write data
back into editable models (e.g. in a TextField's onAccepted Handler).
\endlist
@@ -58,7 +58,7 @@ To visualize data, bind the view's \c model property to a model and the
Views allow visual customization through \e decoration properties such as
the \c header, \c footer, and \c section properties. By binding an object,
usually another visual object, to these properties, the views are
- decoratable. A footer may include a \l Rectangle type showcasing borders
+ decoratable. A footer may include a \l Rectangle type showing borders
or a header that displays a logo on top of the list.
Suppose that a specific club wants to decorate its members list with its brand
@@ -117,6 +117,14 @@ To visualize data, bind the view's \c model property to a model and the
\snippet qml/listview.qml delegate
\image listview-setup.png
+ \section2 Positioning of View Delegates
+
+ The type of view will determine how the items are positioned. \l {ListView}
+ will position the items in a straight line, depending on the \l {ListView::}{orientation},
+ while a \l {GridView} can lay them out in a 2 dimentional grid. It's \b {not} recommended
+ to bind directly on \l {Item::x}{x} and \l {Item::y}{y}, since the view's layouting
+ behavior will always take precedence over any positional binding.
+
\section2 Accessing Views and Models from Delegates
The list view to which the delegate is bound is accessible from the delegate
@@ -144,26 +152,41 @@ To visualize data, bind the view's \c model property to a model and the
bind to. Here is a ListModel with two roles, \e type and \e age, and a
ListView with a delegate that binds to these roles to display their values:
- \snippet qml/qml-data-models/listmodel-listview.qml document
+ \snippet qml/qml-data-models/listmodel-listview-required.qml document
- To get finer control over which roles are accessible, and to make delegates
- more self-contained and usable outside of views,
- \l{Required Properties}{required properties} can be used. If a delegate
- contains required properties, the named roles are not provided. Instead,
- the QML engine will check if the name of a required property matches that of
- a model role. If so, that property will be bound to the corresponding value
- from the model.
+ In most cases you should use \l{Required Properties}{required properties} to
+ pass model data into your delegates. If a delegate contains required
+ properties, the QML engine will check if the name of a required property
+ matches that of a model role. If so, that property will be bound to the
+ corresponding value from the model.
- \snippet qml/qml-data-models/listmodel-listview-required.qml document
+ In rare corner cases, you may want to transfer the model properties through
+ the QML context rather than as required properties. If no required
+ properties are present in your delegate, the named roles are provided as
+ context properties:
+
+ \snippet qml/qml-data-models/listmodel-listview.qml document
+
+ Context properties are invisible to tooling and prevent the
+ \l{Qt Quick Compiler} from optimizing your code. They make it harder to
+ reason about the specific data your delegate expects. There is no way to
+ explicitly populate the QML context from QML. If your component expects
+ data to be passed via the QML context, you can only use it in places
+ where the right context is made available via native means. This can be
+ your own C++ code or the specific implementations of surrounding elements.
+ Conversely, required properties can be set in a number of ways from QML or
+ via native means. Therefore, passing data via the QML context reduces the
+ re-usability of your components.
If there is a naming clash between the model's properties and the delegate's
properties, the roles can be accessed with the qualified \e model name
- instead. For example, if a \l Text type had \e type or \e age properties,
- the text in the above example would display those property values instead of
- the \e type and \e age values from the model item. In this case, the
- properties could have been referenced as \c model.type and \c model.age
- instead to ensure the delegate displays the property values from the model
- item.
+ instead. For example, if a \l Text type had (non-required) \e type or \e age
+ properties, the text in the above example would display those property
+ values instead of the \e type and \e age values from the model item. In this
+ case, the properties could have been referenced as \c model.type and
+ \c model.age instead to ensure the delegate displays the property values from
+ the model item. For this to work, you need to require a \c model property in
+ your delegate (unless you are using context properties).
A special \e index role containing the index of the item in the model is
also available to the delegate. Note this index is set to -1 if the item is
@@ -173,12 +196,71 @@ To visualize data, bind the view's \c model property to a model and the
possible to delay delegate destruction in some views via a \c delayRemove
attached property.)
- Models that do not have named roles (such as the ListModel shown
- below) will have the data provided via the \e modelData role. The \e
- modelData role is also provided for models that have only one role. In this
- case the \e modelData role contains the same data as the named role.
+ Remember that you can use integers or arrays as model:
- \note \e model, \e index, and \e modelData roles are not accessible
+ \qml
+ Repeater {
+ model: 5
+ Text {
+ required property int modelData
+ text: modelData
+ }
+ }
+ \endqml
+
+ \qml
+ Repeater {
+ model: ["one", "two", "three"]
+ Text {
+ required property string modelData
+ text: modelData
+ }
+ }
+ \endqml
+
+ Such models provide a singular, anonymous piece of data to each instance
+ of the delegate. Accessing this piece of data is the primary reason to
+ use \e modelData, but other models also provide \e modelData.
+
+ The object provided via the \e model role has a property with an empty name.
+ This anonymous property holds the \e modelData. Furthermore, the object
+ provided via the \e model role has another property called \e modelData.
+ This property is deprecated and also holds the \e modelData.
+
+ In addition to the \e model role, a \e modelData role is provided. The
+ \e modelData role holds the same data as the \e modelData property and the
+ anonymous property of the object provided via the \e model role.
+
+ The differences between the \e model role and the various means to access
+ \e modelData are as follows:
+
+ \list
+ \li Models that do not have named roles (such as integers or an array of
+ strings) have their data provided via the \e modelData role. The
+ \e modelData role does not necessarily contain an object in this case.
+ In the case of an integer model it would contain an integer (the index
+ of the current model item). In the case of an array of strings it would
+ contain a string. The \e model role still contains an object, but
+ without any properties for named roles. \e model still contains its
+ usual \e modelData and anonymous properties, though.
+ \li If the model has only one named role, the \e modelData role contains
+ the same data as the named role. It is not necessarily an object and it
+ does not contain the named role as a named property the way it usually
+ would. The \e model role still contains an object with the named role as
+ property, and the \e modelData and anonymous properties in this case.
+ \li For models with multiple roles, the \e modelData role is only provided as
+ a required property, not as a context property. This is due to backwards
+ compatibility with older versions of Qt.
+ \endlist
+
+ The anonymous property on \e model allows you to cleanly write delegates
+ that receive both their model data and the role name they should react
+ to as properties from the outside. You can provide a model without or
+ with only one named role, and an empty string as role. Then, a binding that
+ simply accesses \c{model[role]} will do what you expect. You don't have to
+ add special code for this case.
+
+ \note The \e model, \e index, and \e modelData roles are not accessible
if the delegate contains required properties, unless it has also required
properties with matching names.
@@ -285,7 +367,11 @@ To visualize data, bind the view's \c model property to a model and the
Component {
id: itemDelegate
- Text { text: "I am item number: " + index }
+
+ Text {
+ required property int index
+ text: "I am item number: " + index
+ }
}
ListView {
@@ -320,7 +406,11 @@ To visualize data, bind the view's \c model property to a model and the
Component {
id: myDelegate
- Text { text: model.color }
+
+ Text {
+ required property var model
+ text: model.color
+ }
}
ListView {
@@ -343,6 +433,20 @@ To visualize data, bind the view's \c model property to a model and the
\l{Using C++ Models with Qt Quick Views}
article.
+ \section2 Array models
+
+ You can use JavaScript arrays and various kinds of QML lists as models.
+ The elements of the list will be made available as model and modelData
+ by the rules outlined above: Singular data like integers or strings are
+ made available as singular modelData. Structured data like JavaScript
+ objects or QObjects are made available as structured model and modelData.
+
+ The individual model roles are also made available if you request them as
+ required properties. Since we cannot know in advance what objects will
+ appear in an array, any required property in a delegate will be populated,
+ possibly with a coercion of \c undefined to the required type. The
+ individual model roles are not made available via the QML context, though.
+ They would shadow all other context properties.
\section1 Repeaters
@@ -373,8 +477,8 @@ the model.
For more details, see the \l{qtquick-modelviewsdata-modelview.html#integers-as-models}{QML Data Models} document.
-If the model is a string list, the delegate is also exposed to a read-only
-\c modelData property that holds the string. For example:
+If the model is a string list, the delegate is also exposed to the usual
+read-only \c modelData property that holds the string. For example:
\table
\row
@@ -400,6 +504,8 @@ ListView {
anchors.fill: parent
model: EditableModel {}
delegate: TextEdit {
+ required property var model
+
width: ListView.view.width
height: 30
text: model.edit
diff --git a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
index 722d7b747b..7c7c314fc3 100644
--- a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
+++ b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
@@ -145,6 +145,16 @@ enter either \c LTR or \c RTL as the translation for the locale.
</context>
\endcode
+Next, add the following bindings to the root QML component of your application:
+\code
+LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
+LayoutMirroring.childrenInherit: true
+\endcode
+
+The first binding ensures that the UI will be mirrored appropriately when a
+right-to-left locale is set. The second binding ensures that child items of the
+root component will also respect mirroring.
+
You can test that the layout direction works as expected by running your Qt Quick application with
the compiled translation file:
diff --git a/src/quick/doc/src/concepts/positioning/topic.qdoc b/src/quick/doc/src/concepts/positioning/topic.qdoc
index cd07d75985..dc9f647ee5 100644
--- a/src/quick/doc/src/concepts/positioning/topic.qdoc
+++ b/src/quick/doc/src/concepts/positioning/topic.qdoc
@@ -5,6 +5,7 @@
\page qtquick-positioning-topic.html
\title Important Concepts In Qt Quick - Positioning
\brief Overview of positioning concepts
+\ingroup explanations-programminglanguages
Visual items in QML can be positioned in a variety of ways. The most important
positioning-related concept is that of anchoring, a form of relative
diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
index c93733d819..d4c0c0eb2b 100644
--- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
@@ -17,7 +17,7 @@ in form of plugins (openvg) or built-in to the Qt Quick library
From Qt 5.14 onwards, the default adaptation gains the option of rendering via
a graphics abstraction layer, the Qt Rendering Hardware Interface (RHI),
-provided by the \l QtGui module. When enabled, no direct OpenGL calls are made.
+provided by the \l [QtGui]{Qt GUI} module. When enabled, no direct OpenGL calls are made.
Rather, the scene graph renders by using the APIs provided by the abstraction
layer, which is then translated into OpenGL, Vulkan, Metal, or Direct 3D calls.
Shader handling is also unified by writing shader code once, compiling to
@@ -98,10 +98,11 @@ graphics. For more details, see
\page qtquick-visualcanvas-adaptations-software.html
The Software adaptation is an alternative renderer for \l {Qt Quick} 2 that uses the Raster paint
-engine to render the contents of the scene graph, instead of OpenGL. Consequently, some features
-and optimizations are not available. Most Qt Quick 2 applications can run without any modification,
-but any attempts to use unsupported features are ignored. By using the Software adaptation, it is
-possible to run Qt Quick 2 applications on hardware and platforms that do not have OpenGL support.
+engine to render the contents of the scene graph, instead of a hardware-accelerated 3D graphics API.
+Consequently, some features and optimizations are not available. Most Qt Quick 2 applications can run
+without any modification, but any attempts to use unsupported features are ignored.
+By using the Software adaptation, it is possible to run Qt Quick 2 applications on hardware and
+platforms that do not have \l{topics-graphics}{hardware-accelerated 3D graphics API} support.
The Software adaptation was previously known as the Qt Quick 2D Renderer. However, unlike the 2D
Renderer, this new, integrated version supports partial updates. This means that a full update
@@ -121,14 +122,9 @@ even though they are not visible.
\section2 Rendering Text
The text rendering with the Software adaptation is based on software rasterization and does not
-respond as well to transformations such as scaling, compared to when using OpenGL. The quality is
-similar to choosing \l [QML] {Text::renderType}{Text.NativeRendering} with \l [QML] {Text} items.
-
-\section2 Qt Multimedia VideoOutput
-
-The Qt Multimedia module's VideoOutput item is not supported with the Software adaptation. This
-is because VideoOutput uses the QVideoRendererControl item which requires custom QSGGeometryNode
-behavior, which is only present in the default OpenGL adaptation.
+respond as well to transformations such as scaling, compared to when using a hardware-accelerated
+3D graphics API. The quality is similar to choosing \l [QML] {Text::renderType}{Text.NativeRendering}
+with \l [QML] {Text} items.
*/
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index 30ccb9967e..050bf00f62 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -248,9 +248,9 @@ environment.
The non-threaded render loop is currently used by default on Windows with
OpenGL when not using the system's standard opengl32.dll, \macos with OpenGL,
-and Linux with some drivers. For the latter this is mostly a precautionary
-measure, as not all combinations of OpenGL drivers and windowing systems have
-been tested.
+WebAssembly, and Linux with some drivers. For the latter this is mostly a
+precautionary measure, as not all combinations of OpenGL drivers and windowing
+systems have been tested.
On macOS and OpenGL, the threaded render loop is not supported when building
with XCode 10 (10.14 SDK) or later, since this opts in to layer-backed views on
@@ -258,6 +258,10 @@ macOS 10.14. You can build with Xcode 9 (10.13 SDK) to opt out of
layer-backing, in which case the threaded render loop is available and used by
default. There is no such restriction with Metal.
+The threaded render loop is not supported on WebAssembly, since the web platform
+has limited support for using WebGL on other threads than the main thread, and
+limited support for blocking the main thread.
+
Even when using the non-threaded render loop, you should write your code as if
you are using the threaded renderer, as failing to do so will make the code
non-portable.
@@ -319,6 +323,20 @@ same time this is exactly what can cause trouble if vsync-based throttling is
not functioning as expected, because if what the render loop thinks is
happening is not matching reality, incorrect animation pacing will occur.
+\note Starting from Qt 6.5, the threaded render loop offers the possibility of
+opting in to another animation driver, based solely on the elapsed time
+(QElapsedTimer). To enable this, set the \c{QSG_USE_SIMPLE_ANIMATION_DRIVER}
+environment variable to a non-zero value. This has the benefits of not needing
+any of the infrastructure for falling back to a QTimer when there are multiple
+windows, not needing heuristics trying determine if vsync-based throttling is
+missing or broken, being compatible with any kind of temporal drifts in vsync
+throttling, and not being tied to the primary screen's refresh rate, thus
+potentially working better in multi-screen setups. It also drives render
+thread animations (the \l Animator types) correctly even if vsync-based
+throttling is broken or disabled. On the other hand, animations may be
+perceived as less smooth with this approach. With compatibility in mind, it is
+offered as an opt-in feature at the moment.
+
In summary, the \c threaded render loop is expected to provide smoother
animations with less stutter as long as the following conditions are met:
@@ -410,8 +428,10 @@ situations.
Remember however, that by design none of this helps render thread animations
(the \l Animator types). In the absence of vsync-based blocking,
-\l{Animator}{animators} will advance incorrectly, faster than expected, even
-when the workarounds are activated for regular \l{Animation}{animations}.
+\l{Animator}{animators} will advance incorrectly by default, faster than
+expected, even when the workarounds are activated for regular
+\l{Animation}{animations}. If this becomes an issue, consider using the
+alternative animation driver by setting \c{QSG_USE_SIMPLE_ANIMATION_DRIVER}.
\note Be aware that the rendering loop logic and event processing on the GUI
(main) thread is not necessarily unthrottled even if waiting for vsync is
@@ -450,23 +470,53 @@ presentation rate simply because there is no presenting of the frame happening.
This is optional, by default animations will advance based on the system timer.
-\section2 Mixing Scene Graph and the native graphics API
+\section2 Extending the Scene Graph with QRhi-based and native 3D rendering
+
+The scene graph offers three methods for integrating application-provided
+graphics commands:
+
+\list
+
+\li Issuing either \l{QRhi}-based or OpenGL, Vulkan, Metal, Direct3D commands
+directly before or after the scene graph's own rendering. This in effect
+prepends or appends a set of draw calls into the main render pass. No additional
+render target is used.
+
+\li Rendering to a texture and creating a textured node in the scene graph. This
+involves an additional render pass and render target.
+
+\li Issuing draw calls inline with the scene graph's own rendering by
+instantiating a QSGRenderNode subclass in the scene graph. This is similar to
+the first approach but the custom draw calls are effectively injected into the
+scene graph's command stream.
+
+\endlist
-The scene graph offers two methods for integrating application-provided
-graphics commands: by issuing OpenGL, Vulkan, Metal, etc. commands directly,
-and by creating a textured node in the scene graph.
+\section3 Underlay/overlay mode
By connecting to the \l QQuickWindow::beforeRendering() and \l
-QQuickWindow::afterRendering() signals, applications can make OpenGL calls
-directly into the same context as the scene graph is rendering to. With APIs
-like Vulkan or Metal, applications can query native objects, such as, the scene
-graph's command buffer, via QSGRendererInterface, and record commands to it as
-they see fit. As the signal names indicate, the user can then render content
-either under a Qt Quick scene or over it. The benefit of integrating in this
-manner is that no extra framebuffer nor memory is needed to perform the
-rendering, and a possibly expensive texturing step is eliminated. The downside
-is that Qt Quick decides when to call the signals and this is the only time the
-OpenGL application is allowed to draw.
+QQuickWindow::afterRendering() signals, applications can make \l QRhi or native
+3D API calls directly into the same context as the scene graph is rendering to.
+With APIs like Vulkan or Metal, applications can query native objects, such as,
+the scene graph's command buffer, via QSGRendererInterface, and record commands
+to it as they see fit. As the signal names indicate, the user can then render
+content either under a Qt Quick scene or over it. The benefit of integrating in
+this manner is that no extra render targets are needed to perform the rendering,
+and a possibly expensive texturing step is eliminated. The downside is that the
+custom rendering can only be issued either at the beginning or at the end of Qt
+Quick's own rendering. Using QSGRenderNode instead of the QQuickWindow signals
+can lift that restriction somewhat, but in either case care must be taken when
+it comes to 3D content and depth buffer usage since relying on depth testing and
+rendering with depth write enabled can easily create situations where the custom
+content and the Qt Quick content's depth buffer usage conflict with each other.
+
+From Qt 6.6 the \l QRhi APIs are considered semi-public, i.e. offered to the
+applications and documented, albeit with a limited compatibility guarantee. This
+allows creating portable, cross-platform 2D/3D rendering code by using the same
+graphics and shader abstractions the scene graph itself uses.
+
+The \l {Scene Graph - RHI Under QML} example gives an example on how to
+implement the underlay/overlay approach using \l QRhi.
The \l {Scene Graph - OpenGL Under QML} example gives an example on
how to use these signals using OpenGL.
@@ -480,36 +530,58 @@ how to use these signals using Metal.
The \l {Scene Graph - Vulkan Under QML} example gives an example on
how to use these signals using Vulkan.
-The other alternative, only available for OpenGL currently, is to create a
-QQuickFramebufferObject, render into it, and let it be displayed in the scene
-graph as a texture. The \l {Scene Graph - Rendering FBOs} example shows how
-this can be done.
-
-Graphics APIs other than OpenGL can also follow this approach, even though
-QQuickFramebufferObject does not currently support them. Creating and rendering
-to a texture directly with the underlying API, followed by wrapping and using
-this resource in a Qt Quick scene in a custom QQuickItem, is demonstrated in
-the \l {Scene Graph - Metal Texture Import} example. That example uses Metal,
-the concepts however apply to all other graphics APIs as well.
-
-\warning Starting with Qt 6.0, direct usage of the underlying graphics API must
-be enclosed by a call to \l QQuickWindow::beginExternalCommands() and \l
+Starting with Qt 6.0, direct usage of the underlying graphics API must be
+enclosed by a call to \l QQuickWindow::beginExternalCommands() and \l
QQuickWindow::endExternalCommands(). This concept may be familiar from \l
QPainter::beginNativePainting(), and serves a similar purpose: it allows the Qt
Quick Scene Graph to recognize that any cached state and assumptions about the
state within the currently recorded render pass, if there is one, are now
invalid, because the application code may have altered it by working directly
-with the underlying graphics API.
+with the underlying graphics API. This is not applicable and necessary when
+using \l QRhi.
+
+When mixing custom OpenGL rendering with the scene graph, it is important the
+application does not leave the OpenGL context in a state with buffers bound,
+attributes enabled, special values in the z-buffer or stencil-buffer or similar.
+Doing so can result in unpredictable behavior.
+
+The custom rendering code must be thread aware in the sense that it should not
+assume being executed on the GUI (main) thread of the application. When
+connecting to the \l QQuickWindow signals, the application should use
+Qt::DirectConnection and understand that the connected slots are invoked on the
+scene graph's dedicated render thread, if there is one.
+
+\section3 The texture-based approach
+
+The texture-based alternative is the most flexible approach when the application
+needs to have a "flattened", 2D image of some custom 3D rendering within the Qt
+Quick scene. This also allows using a dedicated depth/stencil buffer that is
+independent of the buffers used by the main render pass.
+
+When using OpenGL, the legacy convenience class QQuickFramebufferObject can be
+used to achieve this. QRhi-based custom renderers and graphics APIs other than
+OpenGL can also follow this approach, even though QQuickFramebufferObject does
+not currently support them. Creating and rendering to a texture directly with
+the underlying API, followed by wrapping and using this resource in a Qt Quick
+scene in a custom QQuickItem, is demonstrated in the following examples:
+
+\l {Scene Graph - RHI Texture Item} example.
+
+\l {Scene Graph - Vulkan Texture Import} example.
+
+\l {Scene Graph - Metal Texture Import} example.
-\warning When mixing OpenGL content with scene graph rendering, it is
-important the application does not leave the OpenGL context in a state
-with buffers bound, attributes enabled, special values in the z-buffer
-or stencil-buffer or similar. Doing so can result in unpredictable
-behavior.
+\section3 The inline approach
-\warning The custom rendering code must be thread aware in the sense that it
-should not assume being executed on the GUI (main) thread of the application.
+Using \l QSGRenderNode the custom draw calls are injected not at the beginning
+or the end of the recording of the scene graph's render pass, but rather during
+the scene graph's rendering process. This is achieved by creating a custom \l
+QQuickItem based by an instance of \l QSGRenderNode, a scene graph node that
+exists specifically to allow issuing graphics commands either via \l QRhi or a
+native 3D API such as OpenGL, Vulkan, Metal, or Direct 3D.
+The \l {Scene Graph - Custom QSGRenderNode} example gives a demonstration of
+this approach.
\section2 Custom Items using QPainter
@@ -1138,7 +1210,7 @@ with multiple windows.
From Qt 6.0 onwards, the default adaptation always renders via a graphics
abstraction layer, the Qt Rendering Hardware Interface (RHI), provided by the
- \l QtGui module. This means that, unlike Qt 5, no direct OpenGL calls are made
+ \l [QtGui]{Qt GUI} module. This means that, unlike Qt 5, no direct OpenGL calls are made
by the scene graph. Rather, it records resource and draw commands by using the
RHI APIs, which then translate the command stream into OpenGL, Vulkan, Metal,
or Direct 3D calls. Shader handling is also unified by writing shader code
@@ -1155,7 +1227,7 @@ with multiple windows.
\row
\li \c QSG_RHI_BACKEND
- \li \c vulkan, \c metal, \c opengl, \c d3d11
+ \li \c vulkan, \c metal, \c opengl, \c d3d11, \c d3d12
\li Requests the specific RHI backend. By default the targeted graphics API
is chosen based on the platform, unless overridden by this variable or the
equivalent C++ APIs. The defaults are currently Direct3D 11 for Windows,
@@ -1196,8 +1268,8 @@ with multiple windows.
\endcode
See QSGRendererInterface::GraphicsApi. The enum values \c OpenGL, \c Vulkan,
- \c Metal, \c Direct3D11 are equivalent in effect to running with \c
- QSG_RHI_BACKEND set to the equivalent string key.
+ \c Metal, \c Direct3D11, \c Direct3D12 are equivalent in effect to running
+ with \c QSG_RHI_BACKEND set to the equivalent string key.
All QRhi backends will choose the system default GPU adapter or physical
device, unless overridden by \c{QSG_RHI_PREFER_SOFTWARE_RENDERER} or a
@@ -1209,6 +1281,6 @@ with multiple windows.
as environment variables are available as C++ APIs in
QQuickGraphicsConfiguration. For example, setting \c QSG_RHI_DEBUG_LAYER and
calling
- \l{QQuickGraphicsConfiguration::setDebugLayerEnabled()}{setDebugLayerEnabled(true)}
+ \l{QQuickGraphicsConfiguration::setDebugLayer()}{setDebugLayer(true)}
are equivalent.
*/
diff --git a/src/quick/doc/src/dynamicview-tutorial.qdoc b/src/quick/doc/src/dynamicview-tutorial.qdoc
index 3a3f73a533..bb6c18b90c 100644
--- a/src/quick/doc/src/dynamicview-tutorial.qdoc
+++ b/src/quick/doc/src/dynamicview-tutorial.qdoc
@@ -30,6 +30,7 @@ directory.
\nextpage QML Dynamic View Ordering Tutorial 2 - Dragging View Items
\example tutorials/dynamicview/dynamicview1
+\examplecategory {User Interface Components}
We begin our application by defining a ListView, a model which will provide data to the view, and a
delegate which provides a template for constructing items in the view.
diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc
index d49b718a26..84f2986722 100644
--- a/src/quick/doc/src/examples.qdoc
+++ b/src/quick/doc/src/examples.qdoc
@@ -6,7 +6,6 @@
\page qtquick-codesamples.html
\title Qt Quick Examples and Tutorials
\brief Building UIs with QML
-\ingroup all-examples
\ingroup qtquick
\keyword qtquick-samples
@@ -14,7 +13,7 @@ Qt includes several examples to demonstrate a particular usage. The examples
run as applications or as non-GUI examples in Qt Creator. Qt tutorials show
the step-by-step information and give insight to particular code snippets.
-This page lists the \l{Qt QML} and \l{Qt Quick} examples, however, many other
+This page lists the \l{Qt Qml} and \l{Qt Quick} examples, however, many other
\l{All Modules}{Qt modules} contain examples related to their QML API.
\section1 Running the Examples and Demos
@@ -60,7 +59,7 @@ steps such as use cases and introductory material. For more information about Qt
\b{Beginning with QML and Qt Quick}
\list
\li \l{First Steps with QML}
- \li \l{Getting Started Programming with Qt Quick}{Qt Quick Text Editor}
+ \li \l{Getting Started Programming with Qt Quick}{Alarms Application}
\li \l{Qt Design Studio: Your First UI}{Qt Design Studio Tutorials}
\li \l{QML Advanced Tutorial}{SameGame}
\endlist
@@ -81,11 +80,12 @@ steps such as use cases and introductory material. For more information about Qt
\section2 Extending QML
-The following tutorials show how a QML-based application can be combined with
-C++ code using the \l{Qt QML} module. For information about extending QML, visit
-the \l{Writing QML Extensions with C++} page.
-
-\annotatedlist{qmlextendingexamples}
+The following tutorials show how a QML-based application can be combined and
+exteneded with C++ code using the \l{Qt Qml} module.
+\list
+ \li \l {Writing QML Extensions with C++}
+ \li \l {Writing advanced QML Extensions with C++}
+\endlist
\section1 Examples
@@ -98,20 +98,18 @@ Creator.
\b{QML Types and Controls}
\list
\li \l{Qt Quick Controls - Gallery}{Controls Gallery}
- \li \l{Calendar Example}
\li \l{tableview/gameoflife}{TableView}
\li \l{Qt Quick Examples - Text}{Text and Fonts}
- \li \l{Qt Quick Examples - Toggle Switch}{Custom Toggle Switch}
\endlist
\enddiv
\div {class="doc-column"}
\b{Layouts and Views}
\list
\li \l{Qt Quick Layouts - Basic Example}
+ \li \l{Qt Quick Layouts - Responsive Layout Example}
\li \l{Qt Quick Examples - Positioners}{Positioners}
\li \l{Qt Quick Examples - Views}{Views}
\li \l{Qt Quick Examples - Window and Screen}{Windows and Screen}
- \li \l{Qt Quick Examples - Right to Left}{Right-to-Left and Text Layout}
\endlist
\enddiv
\div {class="doc-column"}
@@ -121,6 +119,15 @@ Creator.
\li \l{Qt Quick Examples - Animation}{Animation}
\li \l{Qt Quick Examples - Canvas}{Canvas API}
\li \l{Qt Quick Examples - Shader Effects}{Shader Effects}
+ \li \l{Qt Quick Examples - MultiEffect Test Bed}{MultiEffect}
+ \li \l{Qt Quick Examples - MultiEffect Item Switcher}{MultiEffect-based Item Transitions}
+ \endlist
+ \enddiv
+ \div {class="doc-column"}
+ \b{Shapes}
+ \list
+ \li \l{Qt Quick Examples - Shapes}{Shapes}
+ \li \l{Weather Forecast Example}{Weather Forecast}
\endlist
\enddiv
\enddiv
@@ -130,15 +137,13 @@ Creator.
\b{Keyboard, Focus, and Touch}
\list
\li \l{Qt Quick Examples - Key Interaction}{Key Interaction}
- \li \l{Qt Quick Examples - MouseArea}{MouseArea}
+ \li \l{Qt Quick Examples - Pointer Handlers}{Pointer Handlers}
\endlist
\enddiv
\div {class="doc-column"}
\b{System and Events}
\list
- \li \l{Qt Quick Examples - Threading}{Threading}
\li \l{Qt Quick Examples - Accessibility}{Accessibility}
- \li \l{Qt Quick Examples - externaldraganddrop}{External Drag and Drop}
\li \l{Qt Quick Examples - Drag and Drop}{Drag and Drop}
\li \l{Qt Quick Examples - Item Variable Refresh Rate}{Item Variable Refresh Rate}
\endlist
@@ -147,16 +152,37 @@ Creator.
\b{Scene Graph}
\list
\li \l{Scene Graph - Custom Material}{Custom Material}
+ \li \l{Scene Graph - RHI Under QML}{Portable QRhi-based 3D rendering as a scene underlay}
+ \li \l{Scene Graph - RHI Texture Item}{Displaying a QRhi-rendered image in a QQuickItem}
+ \li \l{Scene Graph - Custom QSGRenderNode}{Implementing a QRhi-based QSGRenderNode}
+ \li \l{QQuickRenderControl RHI Example}{Redirecting Qt Quick rendering into a QRhiTexture}
\li \l{Scene Graph - Two Texture Providers}{Texture Providers and Materials}
\li \l{Scene Graph - Custom Geometry}{Custom Geometry}
\li \l{Scene Graph - Graph}{Graph}
- \li \l{Scene Graph - OpenGL Under QML}{OpenGL Under QML}
- \li \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11 Under QML}
- \li \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML}
- \li \l{Scene Graph - Vulkan Texture Import}{Vulkan Texture Import}
- \li \l{Scene Graph - Metal Under QML}{Metal Under QML}
- \li \l{Scene Graph - Metal Texture Import}{Metal Texture Import}
- \li \l{Scene Graph - Rendering FBOs}{Rendering to OpenGL FBOs}
+ \endlist
+ \enddiv
+\enddiv
+
+\div {class="multi-column"}
+ \div {class="doc-column"}
+ \b{Extending the Scene Graph using native 3D APIs}
+ \list
+ \li \l{Scene Graph - Vulkan Under QML}{Vulkan-based 3D rendering as a scene underlay}
+ \li \l{Scene Graph - Vulkan Texture Import}{Implementing a custom QQuickItem that displays a native Vulkan image}
+ \li \l{Scene Graph - Metal Under QML}{Metal-based 3D rendering as a scene underlay}
+ \li \l{Scene Graph - Metal Texture Import}{Implementing a custom QQuickItem that displays a native Metal texture}
+ \li \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11-based rendering as a scene underlay}
+ \li \l{Scene Graph - OpenGL Under QML}{OpenGL-based rendering as a scene underlay}
+ \li \l{QQuickRenderControl OpenGL Example}{Redirecting Qt Quick rendering into an OpenGL texture}
+ \li \l{QQuickRenderControl D3D11 Example}{Redirecting Qt Quick rendering into a Direct 3D texture}
+ \endlist
+ \enddiv
+ \div {class="doc-column"}
+ \b{Adding Qt Quick scenes in QWidget applications}
+ \list
+ \li \l{Qt Quick Widgets Example}{Using QQuickWidget}
+ \li \l{QQuickWidget - QQuickView Comparison Example}{QQuickWidget - QQuickView Comparison (OpenGL)}
+ \li \l{Qt Quick Examples - Embedded in Widgets}{Embedding a QQuickView as Native Window}
\endlist
\enddiv
\enddiv
diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
index 2e2fc05aac..1934be6f26 100644
--- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
@@ -6,6 +6,7 @@
\title Best Practices for QML and Qt Quick
\brief Lists best practices for working with QML and Qt Quick.
\ingroup best-practices
+\ingroup explanations-programminglanguages
Despite all of the benefits that QML and Qt Quick offer, they can be
challenging in certain situations. The following sections elaborate on some of
@@ -28,11 +29,16 @@ options that align with the latest UI design trends. If these UI controls do not
satisfy your application's needs, only then it is recommended to create a
custom control.
+You can use the controls when you design UIs in Qt Design Studio. In addition,
+it provides timeline-based animations, visual effects, layouts, and a
+live-preview for prototyping applications.
\section2 Related Information
\list
\li \l{Qt Quick Controls}
+\li \l{Customizing Qt Quick Controls}
\li \l{Qt Quick}
+\li \l{Qt Design Studio Manual}
\endlist
\omit
@@ -88,12 +94,17 @@ qt_add_qml_module(my_module
All QML files listed under \c {QML_FILES} will automatically get compiled \l {Ahead-of-Time Compilation}{ahead of time}.
+You should keep the QML files in the same directory as the CMakeLists.txt with
+the qt_add_qml_module. Otherwise their \l{The Implicit Import}{implicit imports}
+will be different from the \l{QML Modules} they belong to. This is a frequent
+source of mistakes.
+
\section2 Related Information
\list
\li \l{The Qt Resource System}
\endlist
-\section1 Separate UI from Logic
+\section1 Separate UI from Business Logic
One of the key goals that most application developers want to achieve is to
create a maintainable application. One of the ways to achieve this goal is
@@ -109,8 +120,8 @@ reasons why an application's UI should be written in QML:
\li JavaScript can easily be used in QML to respond to events.
\endlist
-Being a strongly typed language, C++ is best suited for an application's logic.
-Typically, such code performs tasks such as complex calculations
+Being a strongly typed language, C++ is best suited for an application's
+business logic. Typically, such code performs tasks such as complex calculations
or data processing, which are faster in C++ than QML.
Qt offers various approaches to integrate QML and C++ code in an application.
@@ -154,6 +165,22 @@ see \l {Choosing the Correct Integration Method Between C++ and QML}.
\li \l{Qt Quick Controls - Chat Tutorial}{Chat application tutorial}
\endlist
+\section1 Using Qt Design Studio
+
+Qt Design Studio uses UI files that have the filename extension \e {.ui.qml}
+to separate the visual parts of the UI from the UI logic you implement in
+\e {.qml} files. You should edit UI files only in the \uicontrol {2D} view in
+Qt Design Studio. If you use some other tool to add code that Qt Design Studio
+does not support, it displays error messages. Fix the errors to enable visual
+editing of the UI files again. Typically, you should move the unsupported code
+to a \e {.qml} file.
+
+\section2 Related Information
+
+\list
+ \li \l{Qt Design Studio: UI Files}
+\endlist
+
\section1 Using Qt Quick Layouts
Qt offers Qt Quick Layouts to arrange Qt Quick items visually in a layout.
@@ -228,7 +255,7 @@ property MyMenu optionsMenu
\section1 Performance
For information on performance in QML and Qt Quick,
-see \l {Performance Considerations And Suggestions}.
+see \l {QML Performance Considerations And Suggestions}.
\section1 Prefer Declarative Bindings Over Imperative Assignments
diff --git a/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc b/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc
deleted file mode 100644
index 0a9d6dbc58..0000000000
--- a/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-/*!
-\page qtquick-tool-qmllint.html
-\title qmllint
-\brief A tool for verifying the syntax of QML files and warning about
-anti-patterns.
-
-\e 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.
-
-By default, some issues will result in warnings that will be printed and result
-in a non-zero exit code.
-Minor issues however (such as unused imports) are just informational messages
-by default and will not affect the exit code.
-qmllint is very configurable and allows for disabling warnings or changing how
-they are treated.
-Users may freely turn any issue into a warning, informational message, or
-disable them outright.
-
-qmllint warns about:
-\list
- \li Unqualified accesses of properties
- \li Usage of signal handlers without a matching signal
- \li Usage of with statements in QML
- \li Issues related to compiling QML code
- \li Unused imports
- \li Deprecated components and properties
- \li And many other things
-\endlist
-
-\note In order for qmllint to work properly, it requires type information.
-That information is provided by QML modules in the import paths.
-The current directory, as well as the import paths for Qt's built-in types,
-are used as import paths by default.
-To add more import paths not included in the default,
-add them via the \c{-I} flag.
-
-To get an overview and explanation of all available command line options, run \c{qmllint --help}.
-
-\section2 Compiler warnings
-
-qmllint can warn you about code that cannot be compiled by \l{qmlsc}.
-
-These warnigs are not enabled by default. In order to enable them specify
-\c{--compiler warning} or adjust your settings file accordingly.
-
-\section2 Marking components and properties as deprecated
-
-qmllint allows you to mark both properties and components as deprecated:
-
-\code
-@Deprecated { reason: "Use NewCustomText instead" }
-Text {
- @Deprecated { reason: "Use newProperty instead" }
- property int oldProperty
- property int newProperty
- Component.onCompleted: console.log(oldProperty); // Warning: XY.qml:8:40: Property "oldProperty" is deprecated (Reason: Use newProperty instead)
-}
-\endcode
-
-Deprecation warnings for components will be shown every time the component is created.
-
-\section2 Disabling warnings inline
-
-You may at any point disable warnings temporarily in a file using \c{// qmllint
-disable}.
-
-You can do this at the end of a line when a single line produces warnings:
-
-\code
-Item {
- property string foo
- Item {
- property string bar: foo // qmllint disable unqualified
- }
-}
-\endcode
-
-Alternatively you can disable comments for a block of lines by putting the
-comment in a line only containing \c{// qmllint disable}, ending the block with
-\c{// qmllint enable}:
-
-\code
-Item {
- property string foo
- Item {
- // qmllint disable unqualified
- property string bar: foo
- property string bar2: foo
- // qmllint enable unqualified
- }
-}
-\endcode
-
-qmllint interprets all single line comments starting with \c {qmllint} as
-directives. Thus you may not start a comment that way unless you wish to enable
-or disable warnings.
-
-\note As done in the examples above it is preferable to explicitly specify the
-warning or a list of warnings you want to disable instead of disabling all
-warnings. This can be done by simply listing warning categories after \c{qmllint disable} (the names are
-the same as the options listed in \c{--help}).
-
-\section2 Settings
-
-In addition to passing command-line options, you can also
-configure qmllint via a settings file.
-The command line \c{--write-defaults} will generate one for you.
-
-Setting files are named \c{.qmllint.ini} and look like this:
-
-\quotefile qmllint/config.ini
-
-Warning levels may be set to \c{info}, \c{warning} or \c{disable} just as with
-command line options.
-
-qmllint will automatically look for a settings file at the location of the qml
-file that is being linted.
-It also looks through all parent directories to find this file and
-automatically applies the settings therein. You can disable this behavior by
-using \c{--ignore-settings}.
-You may always override these defaults by specifying command line parameters
-that take precedence over the warning levels in settings.
-
-\section2 Scripting
-
-qmllint can write or output JSON via the \c{--json <file>} option which will return valid JSON
-with warning messages, file and line location of warnings, and their severity
-level. Use the special filename '-' to write to stdout instead of a file.
-This can be used to more easily integrate qmllint in your pre-commit hooks or
-CI testing.
-
-\sa {Type Description Files}{qmltypes}
-\sa {QtQuick Tools and Utilities}{qtquick-tools-and-utilities}
-*/
diff --git a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
index 50b929e0ed..17f6c31238 100644
--- a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
@@ -24,6 +24,12 @@ 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 Qt Quick Effect Maker (QQEM)
+
+\l{\QQEM} is a tool for creating shader effects for Qt Quick with
+high productivity and performance. You can run Qt Quick Effect Maker as a
+standalone tool.
+
\section1 QML Debugger
The \l{Qt Creator: QML Debugger}{QML Debugger} is a very useful utility that
@@ -69,7 +75,7 @@ tests are setup, UI tests are a lot easier to run.
\section1 qmllint
-\l{qtquick-tool-qmllint.html}{qmllint} is a tool shipped with Qt, that verifies
+\l{qmllint Reference}{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.
@@ -98,7 +104,17 @@ all the available options.
The Qt Quick Compiler consist of two components:
\list
- \li \l {QML Type Compiler}
- \li \l {QML Script Compiler}
+ \li \l {QML type compiler}
+ \li \l {QML script compiler}
\endlist
+
+\section1 \QMLLS
+
+\l{\QMLLS Reference}{\QMLLS} is a tool shipped with Qt that helps you edit
+QML code in your favorite (LSP-compatible) editor.
+
+\section1 SVG to QML converter [tech preview]
+
+The \l{svgtoqml} tool converts an SVG document to a QML file that can be used as a component.
+
*/
diff --git a/src/quick/doc/src/includes/item.qdocinc b/src/quick/doc/src/includes/item.qdocinc
index 4cb2652bbf..733589be5d 100644
--- a/src/quick/doc/src/includes/item.qdocinc
+++ b/src/quick/doc/src/includes/item.qdocinc
@@ -3,6 +3,9 @@
The following properties of the item are used in the mapping:
\l x, \l y, \l scale, \l rotation, \l transformOrigin, and \l [QML]{Item::}{transform}.
+If the items are part of different scenes, the mapping includes
+the relative position of the two scenes.
+
//! [mapping]
@@ -13,3 +16,4 @@ accept the event if you reimplement this function. If you don't accept the
event, call \c event->ignore().
//! [accepting-events]
+
diff --git a/src/quick/doc/src/includes/layout.qdocinc b/src/quick/doc/src/includes/layout.qdocinc
index 723fbc515a..a36f05ee4f 100644
--- a/src/quick/doc/src/includes/layout.qdocinc
+++ b/src/quick/doc/src/includes/layout.qdocinc
@@ -14,5 +14,7 @@
\li \l{Layout::rightMargin}{Layout.rightMargin}
\li \l{Layout::topMargin}{Layout.topMargin}
\li \l{Layout::bottomMargin}{Layout.bottomMargin}
+\li \l{Layout::horizontalStretchFactor}{Layout.horizontalStretchFactor}
+\li \l{Layout::verticalStretchFactor}{Layout.verticalStretchFactor}
//! [attached-properties]
diff --git a/src/quick/doc/src/internal/deliverMatchingPointsToItem.puml b/src/quick/doc/src/internal/deliverMatchingPointsToItem.puml
new file mode 100644
index 0000000000..6a0fa04717
--- /dev/null
+++ b/src/quick/doc/src/internal/deliverMatchingPointsToItem.puml
@@ -0,0 +1,14 @@
+\startuml
+partition "deliverMatchingPointsToItem" {
+ :localizePointerEvent;
+ :QQuickItemPrivate::handlePointerEvent;
+ repeat :loop over extra->pointerHandlers;
+ :QQuickPointerHandler::handlePointerEvent;
+ :wantsPointerEvent;
+ repeat :loop over QEventPoints;
+ :wantsEventPoint;
+ repeat while (more?) is (yes)
+ :handlePointerEventImpl;
+ repeat while (more?) is (yes)
+}
+\enduml
diff --git a/src/quick/doc/src/internal/ideal-pointer-event-delivery-single-drag.dox b/src/quick/doc/src/internal/ideal-pointer-event-delivery-single-drag.dox
new file mode 100644
index 0000000000..4a8b8eef43
--- /dev/null
+++ b/src/quick/doc/src/internal/ideal-pointer-event-delivery-single-drag.dox
@@ -0,0 +1,242 @@
+/*! \internal
+ \page qq-ideal-pointer-event-delivery-single-drag Dragging one DragHandler with one touchpoint
+ \tableofcontents
+
+ <a href="https://doc.qt.io/qt-6/qtquick-input-topic.html">Multi-touch</a>
+ is intended to be a strong feature in Qt Quick, so let's run this example
+ on a touchscreen:
+
+ \snippet pointerHandlers/pinchAndDragHandlers.qml entire
+
+ The intended behavior is that we have three Rectangles that can be dragged,
+ and you can alternatively perform a pinch gesture on the parent Rectangle
+ to scale and rotate it. The object instances involved look like this:
+
+ \dotfile pinchAndDragHandlers.dot "pinch and drag handlers"
+
+ (In these diagrams, â„š is a shortcut for QQuick, to save space.
+ ResizeItemToWindow comes from \c qtdeclarative/tools/qml/ResizeItemToWindow.qml
+ which is a resource in the
+ <a href="https://doc.qt.io/qt-6/qtquick-qml-runtime.html">qml executable</a>
+ which wraps our top-level Rectangle into a Window.)
+
+ \section qq-ideal-pointer-event-delivery-press-draghandler-prep Touch press on a DragHandler: preparation
+
+ Let's start with the scenario that you attempt to drag one Rectangle
+ with one finger. A QTouchEvent arrives, it contains a single QEventPoint
+ representing the single finger, and we have to decide which items and
+ handlers we're going to visit.
+
+ \image html pinchAndDragHandlers-singlePressPrep.svg "touch press event delivery: preparation"
+
+ Since Qt 5.8 (change ccc5c54602821761a2f1a42c4bc473afd53439c9), we stopped
+ doing ad-hoc recursive delivery of touch and mouse events: we wanted to
+ ensure that delivery is deterministic (in spite of what user code may do to
+ the parent hierarchy during delivery), so we first build a list of Items to
+ visit, in QQuickDeliveryAgentPrivate::pointerTargets() (which is recursive
+ itself). This is somewhat expensive, but fortunately we only need to do
+ that when handling the event that begins a gesture, such as a press event.
+ A press event is a pointer event in which \e any QEventPoint has the
+ \c Pressed QEventPoint::state.
+
+ But how do we decide which items are relevant and need to be visited?
+ First, the QEventPoint must fall within the item's bounds, so we need to
+ call QQuickItem::mapFromScene() to localize from the scene (window)
+ coordinates to item coordinates, and then QQuickItem::contains() to check
+ whether it's inside. (QQuickItem::contains() is virtual so that QQuickShape
+ can be non-rectangular; also, any Item can have a
+ QQuickItem::containmentMask() to declare non-rectangular bounds.) Then, we
+ call QQuickItemPrivate::anyPointerHandlerWants() which calls
+ QQuickPointerHandler::wantsEventPoint() on each of the item's pointer
+ handlers: if any handler wants the eventpoint, we need to visit that item.
+ Otherwise, if QQuickItem::acceptTouchEvents() returns false, we do \e not
+ need to visit that item. Thus, pointerTargets() pre-visits all items in the
+ scene to build the list of potential targets.
+
+ \section qq-ideal-pointer-event-delivery-press-draghandler Touch press on a DragHandler: delivery to targets
+
+ After building the list, we are prepared to begin actual delivery of the
+ press event. QQuickDeliveryAgentPrivate::deliverPressOrReleaseEvent() loops
+ over \c targetItems and calls
+ QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(). (Parent-item
+ filtering could have intercepted it before that; but as stated, we're
+ neglecting that complication for now.)
+
+ \image html pinchAndDragHandlers-singlePressDelivery.svg "touch press event actual delivery"
+
+ For each item in \c targetItems, again we need to call mapFromScene() and
+ set QEventPoint::position() to item-local coordinates:
+ QQuickDeliveryAgentPrivate::localizePointerEvent() takes care of that.
+ We always let Pointer Handlers handle the event before the Item itself
+ (because this allows a handler to override or augment behavior that a C++
+ QQuickItem subclass has in its QQuickItem::touchEvent() function: another
+ complication that we're neglecting for now). The implementation for that
+ is in QQuickItemPrivate::handlePointerEvent(): it simply loops over any
+ handlers that are found in the list QQuickItemPrivate::ExtraData::pointerHandlers
+ and calls QQuickPointerHandler::handlePointerEvent() on each of those.
+ That's not a virtual function; but it calls wantsPointerEvent()
+ again, and then handlePointerEventImpl() which \e is virtual.
+
+ PinchHandler would get the event first if it was relevant, because
+ pointerTargets() was a preorder traversal, so parents come before children
+ in the \c targetItems list. But QQuickMultiPointHandler::wantsPointerEvent()
+ has already returned false, because QQuickMultiPointHandler::eligiblePoints()
+ has only found one point, and QQuickMultiPointHandler::minimumPointCount()
+ is 2 by default. (You might be wondering, what if the user presses a
+ second finger later to start the pinch gesture? We'll get to that below.)
+ So it can be skipped; next in \c targetItems should be a Rectangle whose
+ bounds contain QEventPoint::position() \e and that has a DragHandler.
+ By default, one point is enough for a DragHandler: its inherited
+ QQuickMultiPointHandler::wantsPointerEvent() has already returned \c true,
+ and QQuickMultiPointHandlerPrivate::currentPoints is already storing
+ information about the QEventPoints that it wants to handle.
+ (QEventPoint::id() is guaranteed to remain constant during one gesture:
+ once pressed, the same finger keeps manipulating the same QEventPoint.
+ In \c currentPoints[0], QQuickHandlerPoint::id() remembers it.)
+ So QQuickDragHandler::handlePointerEventImpl() can immediately iterate
+ \c currentPoints, find the QEventPoint that has the same ID; and then
+ it calls QQuickPointerHandler::setPassiveGrab(), because DragHandler
+ needs to monitor the position of that point. The drag gesture will not
+ begin until the point is dragged a distance in pixels greater than
+ QStyleHints::startDragDistance(), and it's not appropriate for DragHandler
+ to take the exclusive grab of that touchpoint until it's sure the user
+ really means to drag. (What if the same Rectangle also had a TapHandler?
+ The user could either drag, or tap without dragging; but at the time of the
+ press, it's ambiguous, so both handlers would need their own grabs, to express
+ interest in monitoring that touchpoint.) But without any grab, the handler
+ would not be visited again when the next event occurs: a touchpoint
+ movement or release.
+
+ We've omitted details about the meaning of QEventPoint::accepted() and
+ QEvent::accepted() flags. Pointer handlers need to take grabs explicitly:
+ that helps to remove ambiguity about the consequences of the \c accepted flags.
+
+ \section qq-ideal-pointer-event-delivery-move-draghandler Touch move on a DragHandler: delivery to grabber
+
+ \image html pinchAndDragHandlers-drag-one-rect.png "dragging one rectangle via touch"
+
+ So we're done handling the press; now let's try to start dragging.
+
+ Let's say the user's finger quickly moves far enough on the touchscreen to
+ generate a single QTouchEvent with a delta greater than the drag threshold.
+
+ Again, QGuiApplicationPrivate::processTouchEvent() handles the next QPA
+ touch event (QWindowSystemInterfacePrivate::TouchEvent). For each
+ touchpoint (for each finger being held down), it calls
+ QPointingDevicePrivate::pointById() to retrieve the
+ QPointingDevicePrivate::EventPointData that was stored previously when the
+ press event was delivered, and updates its stored state, including the
+ QEventPoint instance, to be current for this incoming move event.
+ QEventPoint::globalPressPosition() is not updated though: it continues to
+ hold the position at which the press occurred; therefore, any object that
+ ends up handing the moving point can check to see how far it moved since
+ press. Likewise, QEventPoint::pressTimestamp() holds the time at which it
+ was pressed.
+
+ \image html pinchAndDragHandlers-singleMoveDelivery.svg "touch move event delivery"
+
+ Another QTouchEvent instance is stack-allocated, with type
+ QEvent::TouchUpdate, in which \c point(0) is a QEventPoint (with state
+ QEventPoint::Updated) with QEventPoint::scenePosition() being the current
+ finger position in the window, while QEventPoint::scenePressPosition()
+ remembers where it was pressed during the previous press event. It's sent
+ to the application via QGuiApplication::sendSpontaneousEvent(), then to
+ â‘  QQuickWindow::event(), which dispatches to â‘¡ QQuickDeliveryAgent::event().
+ â‘¢ QQuickDeliveryAgentPrivate::deliverPointerEvent() calls
+ â‘£ QQuickDeliveryAgentPrivate::deliverUpdatedPoints(), which (among other
+ things) iterates the QEventPoints, and for each of those, iterates the
+ passive grabbers in QPointingDevicePrivate::EventPointData::passiveGrabbers
+ and calls ⑤ QQuickDeliveryAgentPrivate::deliverToPassiveGrabbers(). It uses
+ ⑥ QQuickDeliveryAgentPrivate::localizePointerEvent() to ⑦ map
+ QEventPoint::position() to the passive-grabbing DragHandler's parent item's
+ coordinate system, and calls ⑧ QQuickPointerHandler::handlePointerEvent().
+ ⑨ QQuickMultiPointHandler::wantsPointerEvent() returns \c true because all
+ the same QQuickMultiPointHandlerPrivate::currentPoints still exist in this
+ QTouchEvent (with no points left over); so
+ â‘© QQuickDragHandler::handlePointerEventImpl() is called. For each point, it
+ calculates the movement delta \c (scenePosition() - scenePressPosition());
+ ⑪ QQuickPointerHandlerPrivate::dragOverThreshold() checks whether it's moved
+ far enough to activate dragging. (If multiple points were being dragged,
+ handlePointerEventImpl() would also check whether they are all being
+ dragged in approximately the same direction.) It did move far enough, and
+ now DragHandler knows it should take responsibility for this gesture:
+ apparently the user is really trying to drag its parent item, the
+ Rectangle. It calls â‘« QQuickMultiPointHandler::grabPoints() to try to take
+ the exclusive grab. Nothing is interfering with that, so the attempt
+ succeeds and returns \c true; therefore, it's ok to call
+ ⑬ QQuickPointerHandler::setActive(), which triggers
+ â‘­ QQuickDragHandler::onActiveChanged(), which updates some internal state
+ etc.; and then â‘® emits the activeChanged() signal. In our example, the
+ Rectangle has a binding to ⑯ change color when the DragHandler becomes
+ active. And since by default, DragHandler's \c target is the same as its
+ \c parent, QQuickDragHandler::handlePointerEventImpl() ends with a call to
+ â‘° QQuickMultiPointHandler::moveTarget(). That uses QMetaProperty::write()
+ to ⑱ change the Rectangle's \c x and \c y properties; the reason we do it
+ that way is in case property value interceptors (BoundaryRule or Behavior)
+ are in use. And so the Rectangle moves as far as the finger is dragged.
+
+ \section qq-ideal-pointer-event-delivery-release-draghandler Touch release: delivery to grabber
+
+ Now let's say there's a QPA event with
+ QWindowSystemInterface::TouchPoint::state being QEventPoint::Released.
+
+ \image html pinchAndDragHandlers-singleReleaseDelivery.svg "touch release event delivery"
+
+ It's processed like the touch move: the persistent QEventPoint is updated
+ with current values again, and another QTouchEvent instance is
+ stack-allocated, with type QEvent::TouchEnd, and â‘  sent to the window and â‘¡
+ the delivery agent. When it gets to â‘¢ QQuickDeliveryAgentPrivate::deliverPointerEvent(),
+ â‘£ deliverUpdatedPoints() is called first (same as for the move). As usual,
+ the event is not given to the item or its handlers until it's been ⑤
+ localized to the item's coordinate system.
+
+ â‘¥ QQuickPointerHandler::handlePointerEvent() calls
+ ⑦ QQuickMultiPointHandler::wantsPointerEvent(), which is able to see that the
+ point it's tracking (in QQuickMultiPointHandlerPrivate::currentPoints) is
+ no longer eligible because it's been released; so it calls
+ ⑧ QQuickPointerHandler::setActive() with \c false immediately, which calls
+ ⑨ onActiveChanged() and emits the ⑩ activeChanged signal. (Thus our Rectangle
+ ⑪ changes its color again.) QQuickPointerHandler::handlePointerEvent() then
+ calls QPointerEvent::setExclusiveGrabber() with \c nullptr to give up its
+ exclusive grab. â‘« QPointingDevice::grabChanged() is emitted.
+ QQuickDeliveryAgentPrivate::onGrabChanged() handles that signal, and calls
+ ⑬ QQuickDragHandler::onGrabChanged(), which has minimal consequences in this
+ case. (QQuickPointerHandler::onGrabChanged() calls
+ QQuickPointerHandler::setActive() with \c false again: it's a failsafe that
+ some other scenarios rely on.)
+
+ \section qq-ideal-pointer-event-delivery-touch-summary Touch delivery activity diagrams
+
+ So let's generalize the functionality we've covered so far.
+ As we'll see later, mouse events are treated a bit differently;
+ but ideally it would be the same: a mouse event is just a QPointerEvent
+ that comes from a different device, containing only one QEventPoint,
+ just like our single-finger touch event.
+
+ A begin (press) event goes to deliverPressOrReleaseEvent(), and then
+ if the QEventPoints weren't all accepted, it goes to deliverUpdatedPoints().
+
+ An update (move) event goes to deliverUpdatedPoints() only.
+
+ An end (release) event goes to deliverUpdatedPoints() and then
+ deliverPressOrReleaseEvent().
+
+ \startuml
+ !include ideal-pointer-event-delivery.puml
+ \enduml
+
+ QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem() is called from
+ two places (deliverPressOrReleaseEvent() and deliverUpdatedPoints()), so
+ it's shown in a separate activity diagram:
+
+ \startuml
+ !include deliverMatchingPointsToItem.puml
+ \enduml
+
+ In conclusion, we've seen the details of touch event dispatching for one
+ short drag gesture to one DragHandler. In practice, Pointer Handlers are
+ still not the most common way to handle pointer events (even if we'd like
+ to end up there eventually): there are a lot of legacy QQuickItem
+ subclasses that do their event handling by overriding virtual functions
+ rather than by having handlers added. But let's save that for later.
+*/
diff --git a/src/quick/doc/src/internal/ideal-pointer-event-delivery.dox b/src/quick/doc/src/internal/ideal-pointer-event-delivery.dox
new file mode 100644
index 0000000000..15d96b0e10
--- /dev/null
+++ b/src/quick/doc/src/internal/ideal-pointer-event-delivery.dox
@@ -0,0 +1,22 @@
+/*! \internal
+ \mainpage Qt Quick Pointer Event Delivery
+
+ QPointerEvent instances are stack-allocated in
+ QGuiApplicationPrivate::processMouseEvent(),
+ QGuiApplicationPrivate::processTouchEvent() etc., and sent to the
+ application via QCoreApplication::sendSpontaneousEvent() (taking QWindow
+ and QEvent pointer arguments). If the window is a QQuickWindow, the QTouchEvent
+ arrives to QQuickWindow::event() which then dispatches to the
+ QQuickDeliveryAgent. QQuickDeliveryAgent contains much of the event
+ delivery code. In fact, it's quite complex, for legacy reasons such as
+ touch->mouse synthesis in three possible layers, handling events in
+ QQuickItem subclasses in C++, event compression, multiple kinds of event
+ filtering, drag-and-drop, dealing with popup menus and so on; but let's
+ start with the ideal case: no synthesis, no filtering, no popups, and all
+ events are handled in
+ <a href="https://doc.qt.io/qt-6/qtquickhandlers-index.html">Pointer Handlers</a>.
+
+ Here are some "ideal" scenarios:
+
+ - \subpage qq-ideal-pointer-event-delivery-single-drag
+*/
diff --git a/src/quick/doc/src/internal/ideal-pointer-event-delivery.puml b/src/quick/doc/src/internal/ideal-pointer-event-delivery.puml
new file mode 100644
index 0000000000..1d730b6f34
--- /dev/null
+++ b/src/quick/doc/src/internal/ideal-pointer-event-delivery.puml
@@ -0,0 +1,55 @@
+\startuml
+start
+:QGuiApplicationPrivate::process[Mouse|Touch|Tablet]Event();
+:QGuiApplication::sendSpontaneousEvent();
+:â„šWindow::event();
+:â„šDeliveryAgent::event();
+partition â„šDeliveryAgentPrivate::deliverPointerEvent() {
+ if (isBeginEvent()) then (yes)
+ partition "deliverPressOrReleaseEvent()" {
+ repeat :loop over QEventPoints;
+ :â„šDeliveryAgentPrivate::pointerTargets();
+ repeat while (more?) is (yes)
+ ->no;
+ repeat :loop over targetItems;
+ :**deliverMatchingPointsToItem()**;
+ repeat while (more?) is (yes)
+ ->no;
+ }
+ endif
+ if (allUpdatedPointsAccepted()) then (no)
+ partition "deliverUpdatedPoints()" {
+ repeat :loop over exclusive grabbers;
+ if (grabber type?) then (pointer handler)
+ :â„šPointerHandler::handlePointerEvent();
+ else (Item)
+ :**deliverMatchingPointsToItem()**;
+ endif
+ repeat while (more grabbers?) is (yes)
+ repeat :loop over QEventPoints;
+ partition "deliverToPassiveGrabbers()" {
+ repeat :loop over passive grabbers;
+ if (grabber type?) then (pointer handler)
+ :â„šPointerHandler::handlePointerEvent();
+ else (Item)
+ :QCoreApplication::sendEvent(item, event);
+ endif
+ repeat while (more grabbers?) is (yes)
+ }
+ repeat while (more points?) is (yes)
+ }
+ endif
+ if (isEndEvent()) then (yes)
+ partition "deliverPressOrReleaseEvent()" {
+ repeat :loop over QEventPoints;
+ :â„šDeliveryAgentPrivate::pointerTargets();
+ repeat while (more?) is (yes)
+ ->no;
+ repeat :loop over targetItems;
+ :**deliverMatchingPointsToItem()**;
+ repeat while (more?) is (yes)
+ }
+ endif
+}
+stop
+\enduml
diff --git a/src/quick/doc/src/internal/pinchAndDragHandlers-singleMoveDelivery.svg b/src/quick/doc/src/internal/pinchAndDragHandlers-singleMoveDelivery.svg
new file mode 100644
index 0000000000..3a69414d83
--- /dev/null
+++ b/src/quick/doc/src/internal/pinchAndDragHandlers-singleMoveDelivery.svg
@@ -0,0 +1,851 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!-- Pages: 1 -->
+
+<svg
+ width="737.57623pt"
+ height="332.46246pt"
+ viewBox="0 0 737.65964 332.46246"
+ version="1.1"
+ id="svg806"
+ sodipodi:docname="pinchAndDragHandlers-singleMoveDelivery.svg"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs810">
+ <marker
+ style="overflow:visible"
+ id="marker1576"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="marker1576-4"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574-4" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="Arrow5"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874264"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path8893" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="marker1576-4-5"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574-4-3" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="namedview808"
+ pagecolor="#ffffff"
+ bordercolor="#111111"
+ borderopacity="1"
+ inkscape:showpageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="1"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="pt"
+ showgrid="false"
+ inkscape:zoom="2.7028887"
+ inkscape:cx="512.78471"
+ inkscape:cy="236.59872"
+ inkscape:window-width="3840"
+ inkscape:window-height="2139"
+ inkscape:window-x="3840"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg806" />
+ <g
+ id="graph0"
+ class="graph"
+ transform="translate(4,328.48129)">
+ <polygon
+ fill="#ffffff"
+ stroke="none"
+ points="615.07,4 -4,4 -4,-328 615.07,-328 "
+ id="polygon654"
+ transform="matrix(1.1915609,0,0,1,0.76624365,0)" />
+ <!-- llfkmbbephlg -->
+ <g
+ id="node1"
+ class="node"
+ style="stroke-width:1.00011;stroke-dasharray:none">
+ <title
+ id="title656">llfkmbbephlg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="250.78,-288 474.53,-288 474.53,-324 262.78,-324 262.78,-328 250.78,-328 "
+ id="polygon658"
+ style="stroke-width:1.00011;stroke-dasharray:none" />
+ <polyline
+ fill="none"
+ stroke="#000000"
+ points="250.78,-324 262.78,-324"
+ id="polyline660"
+ style="stroke-width:1.00011;stroke-dasharray:none" />
+ <text
+ text-anchor="middle"
+ x="362.66"
+ y="-300.95001"
+ font-family="Times, serif"
+ font-size="6pt"
+ id="text662"
+ style="stroke-width:1.00011;stroke-dasharray:none">ResizeItemToWindow_QMLTYPE_1</text>
+ </g>
+ <!-- llfkmbbeijgg -->
+ <g
+ id="node2"
+ class="node">
+ <title
+ id="title665">llfkmbbeijgg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="322.78,-216 402.53,-216 402.53,-252 322.78,-252 "
+ id="polygon667" />
+ <text
+ text-anchor="middle"
+ x="362.66"
+ y="-229.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text669">â„šRootItem</text>
+ </g>
+ <!-- llfkmbbephlg&#45;&gt;llfkmbbeijgg -->
+ <g
+ id="edge1"
+ class="edge">
+ <title
+ id="title672">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path674" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="362.66,-286.8 358.66,-280.8 362.66,-274.8 366.66,-280.8 "
+ id="polygon676" />
+ </g>
+ <!-- llfkmbbepmbg -->
+ <g
+ id="node3"
+ class="node">
+ <title
+ id="title679">llfkmbbepmbg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="245.66,-144 327.66,-144 327.66,-180 245.66,-180 "
+ id="polygon681" />
+ <text
+ text-anchor="middle"
+ x="286.66"
+ y="-157.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text683">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbeijgg&#45;&gt;llfkmbbepmbg -->
+ <g
+ id="edge2"
+ class="edge">
+ <title
+ id="title686">llfkmbbeijgg-&gt;llfkmbbepmbg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 334.28,-206.87 c -9.61,8.86 -20.1,18.52 -28.79,26.52"
+ id="path688" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="343.21,-215.09 336.09,-213.96 334.38,-206.96 341.51,-208.08 "
+ id="polygon690" />
+ </g>
+ <!-- llfkmbclplkg -->
+ <g
+ id="node11"
+ class="node">
+ <title
+ id="title693">llfkmbclplkg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="345.8,-156.44 439.66,-144 533.52,-156.44 533.43,-176.56 345.89,-176.56 "
+ id="polygon695" />
+ <text
+ text-anchor="middle"
+ x="439.66"
+ y="-157.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text697">â„šDeliveryAgent</text>
+ </g>
+ <!-- llfkmbbeijgg&#45;&gt;llfkmbclplkg -->
+ <g
+ id="edge10"
+ class="edge">
+ <title
+ id="title700">llfkmbbeijgg-&gt;llfkmbclplkg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 391.18,-207.07 c 11.28,10.25 23.79,21.62 33.27,30.25"
+ id="path702" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="382.36,-215.09 384.11,-208.09 391.24,-207.02 389.49,-214.01 "
+ id="polygon704" />
+ </g>
+ <!-- llfkmbodpifg -->
+ <g
+ id="node4"
+ class="node">
+ <title
+ id="title707">llfkmbodpifg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="177.32,-95.56 88.66,-108 0,-95.56 0.08,-75.44 177.23,-75.44 "
+ id="polygon709" />
+ <text
+ text-anchor="middle"
+ x="88.660004"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text711">â„šPinchHandler</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbodpifg -->
+ <g
+ id="edge3"
+ class="edge">
+ <title
+ id="title714">llfkmbbepmbg-&gt;llfkmbodpifg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 233.33,-142.15 c -34.69,12.27 -78.95,27.92 -109.33,38.66"
+ id="path716" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="244.78,-146.2 237.79,-147.97 233.47,-142.2 240.46,-140.42 "
+ id="polygon718" />
+ </g>
+ <!-- llfkmbnfping -->
+ <g
+ id="node5"
+ class="node">
+ <title
+ id="title721">llfkmbnfping</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="195.66,-72 277.66,-72 277.66,-108 195.66,-108 "
+ id="polygon723" />
+ <text
+ text-anchor="middle"
+ x="236.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text725">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbnfping -->
+ <g
+ id="edge4"
+ class="edge">
+ <title
+ id="title728">llfkmbbepmbg-&gt;llfkmbnfping</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 266.73,-133.1 c -6.04,8.45 -12.48,17.48 -17.85,25"
+ id="path730" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="273.78,-142.96 267.03,-140.41 266.8,-133.2 273.54,-135.76 "
+ id="polygon732" />
+ </g>
+ <!-- llfkmbbdfibg -->
+ <g
+ id="node7"
+ class="node">
+ <title
+ id="title735">llfkmbbdfibg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="295.66,-72 377.66,-72 377.66,-108 295.66,-108 "
+ id="polygon737" />
+ <text
+ text-anchor="middle"
+ x="336.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text739">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbbdfibg -->
+ <g
+ id="edge6"
+ class="edge">
+ <title
+ id="title742">llfkmbbepmbg-&gt;llfkmbbdfibg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 306.59,-133.1 c 6.03,8.45 12.48,17.48 17.85,25"
+ id="path744" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="299.54,-142.96 299.77,-135.76 306.52,-133.2 306.28,-140.41 "
+ id="polygon746" />
+ </g>
+ <!-- llfkmbodeang -->
+ <g
+ id="node9"
+ class="node">
+ <title
+ id="title749">llfkmbodeang</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="440.66,-72 522.66,-72 522.66,-108 440.66,-108 "
+ id="polygon751" />
+ <text
+ text-anchor="middle"
+ x="481.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text753">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbodeang -->
+ <g
+ id="edge8"
+ class="edge">
+ <title
+ id="title756">llfkmbbepmbg-&gt;llfkmbodeang</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 340.14,-141.8 c 31.61,11.35 71.02,25.49 100.22,35.98"
+ id="path758" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="328.86,-145.85 333.16,-140.06 340.16,-141.8 335.86,-147.59 "
+ id="polygon760" />
+ </g>
+ <!-- llfkmbbfjjgg -->
+ <g
+ id="node6"
+ class="node">
+ <title
+ id="title763">llfkmbbfjjgg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="233.07,-23.56 147.66,-36 62.25,-23.56 62.33,-3.44 232.99,-3.44 "
+ id="polygon765" />
+ <text
+ text-anchor="middle"
+ x="147.66"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text767">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbnfping&#45;&gt;llfkmbbfjjgg -->
+ <g
+ id="edge5"
+ class="edge">
+ <title
+ id="title770">llfkmbnfping-&gt;llfkmbbfjjgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 204.77,-63.92 c -12.86,10.12 -27.25,21.44 -38.37,30.18"
+ id="path772" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="213.95,-71.14 206.76,-70.57 204.52,-63.72 211.71,-64.29 "
+ id="polygon774" />
+ </g>
+ <!-- llfkmbbfibdg -->
+ <g
+ id="node8"
+ class="node">
+ <title
+ id="title777">llfkmbbfibdg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="422.07,-23.56 336.66,-36 251.25,-23.56 251.33,-3.44 421.99,-3.44 "
+ id="polygon779" />
+ <text
+ text-anchor="middle"
+ x="336.66"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text781">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbbdfibg&#45;&gt;llfkmbbfibdg -->
+ <g
+ id="edge7"
+ class="edge">
+ <title
+ id="title784">llfkmbbdfibg-&gt;llfkmbbfibdg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 336.66,-58.81 c 0,7.77 0,15.86 0,22.71"
+ id="path786" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="336.66,-70.8 332.66,-64.8 336.66,-58.8 340.66,-64.8 "
+ id="polygon788" />
+ </g>
+ <!-- llfkmbcknglg -->
+ <g
+ id="node10"
+ class="node">
+ <title
+ id="title791">llfkmbcknglg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="611.07,-23.56 525.66,-36 440.25,-23.56 440.33,-3.44 610.99,-3.44 "
+ id="polygon793" />
+ <text
+ text-anchor="middle"
+ x="525.65997"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text795">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbodeang&#45;&gt;llfkmbcknglg -->
+ <g
+ id="edge9"
+ class="edge">
+ <title
+ id="title798">llfkmbodeang-&gt;llfkmbcknglg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 499.32,-60.9 c 5.62,8.94 11.63,18.5 16.49,26.23"
+ id="path800" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="493.01,-70.93 492.82,-63.72 499.4,-60.77 499.59,-67.98 "
+ id="polygon802" />
+ </g>
+ <g
+ id="g11866"
+ class="edge"
+ transform="rotate(-90,623.51573,-276.02262)">
+ <title
+ id="title11860">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path11862" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="366.66,-280.8 362.66,-286.8 358.66,-280.8 362.66,-274.8 "
+ id="polygon11864" />
+ </g>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 708.8157,-33.802688 c 0,-1.93521 -13.67346,-3.50801 -30.51031,-3.50801 -16.83686,0 -30.51503,1.5728 -30.51503,3.50801 0,0 0,31.5378501 0,31.5378501 0,1.93522003 13.67817,3.50801 30.51503,3.50801 16.83685,0 30.51031,-1.57278997 30.51031,-3.50801 0,0 0,-31.5378501 0,-31.5378501"
+ id="path12337"
+ style="stroke-width:0.568213" />
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 708.8157,-33.802688 c 0,1.92838 -13.67346,3.50116 -30.51031,3.50116 -16.83686,0 -30.51503,-1.57278 -30.51503,-3.50116"
+ id="path12339"
+ style="stroke-width:0.568213" />
+ <text
+ text-anchor="middle"
+ x="678.23846"
+ y="-14.011026"
+ font-family="Times, serif"
+ font-size="6pt"
+ id="text12341"
+ style="stroke-width:0.681807">currentPoints</text>
+ <g
+ id="g14256"
+ class="edge"
+ transform="rotate(-90,507.65517,-314.66065)">
+ <title
+ id="title14250">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path14252" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="362.66,-286.8 358.66,-280.8 362.66,-274.8 366.66,-280.8 "
+ id="polygon14254" />
+ </g>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 631.59317,-188.30128 c 0,-1.93521 -13.67346,-3.50801 -30.51031,-3.50801 -16.83686,0 -30.51503,1.5728 -30.51503,3.50801 0,0 0,31.53785 0,31.53785 0,1.93522 13.67817,3.50801 30.51503,3.50801 16.83685,0 30.51031,-1.57279 30.51031,-3.50801 0,0 0,-31.53785 0,-31.53785"
+ id="path14258"
+ style="stroke-width:0.568213" />
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 631.59317,-188.30128 c 0,1.92838 -13.67346,3.50116 -30.51031,3.50116 -16.83686,0 -30.51503,-1.57278 -30.51503,-3.50116"
+ id="path14260"
+ style="stroke-width:0.568213" />
+ <text
+ text-anchor="middle"
+ x="601.01593"
+ y="-168.50961"
+ font-family="Times, serif"
+ font-size="6pt"
+ id="text14262"
+ style="stroke-width:0.681807">targetItems</text>
+ <g
+ id="g3826"
+ class="edge"
+ transform="rotate(180,500.1871,-52.761609)">
+ <title
+ id="title3820">llfkmbodeang-&gt;llfkmbcknglg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 499.32,-60.9 c 5.62,8.94 11.63,18.5 16.49,26.23"
+ id="path3822" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="492.82,-63.72 499.4,-60.77 499.59,-67.98 493.01,-70.93 "
+ id="polygon3824" />
+ </g>
+ </g>
+ <g
+ id="g2388"
+ transform="translate(166.85425,118.15234)">
+ <rect
+ style="fill:#ffffff;stroke:#000000;stroke-width:0.869398;stroke-linecap:square;stroke-dasharray:0.869398, 0.869398;stroke-opacity:1"
+ id="rect981"
+ width="115.87793"
+ height="32.582088"
+ x="-181.46954"
+ y="-115.9365"
+ ry="0"
+ transform="matrix(1,0,-0.22867874,0.97350194,0,0)" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9pt;line-height:125%;font-family:Times, serif;-inkscape-font-specification:'Times, serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ x="-149.44366"
+ y="-93.742676"
+ id="text1897"><tspan
+ sodipodi:role="line"
+ id="tspan1895"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9pt;font-family:Times, serif;-inkscape-font-specification:'Times, serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="-149.44366"
+ y="-93.742676">QGuiApplication</tspan></text>
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576)"
+ d="m 508.49821,147.40834 c -5.01114,-15.80414 29.88553,-12.14178 19.22974,-0.30829"
+ id="path2661"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 529.10578,290.07508 c 2.22687,-20.59627 43.00062,-73.20365 2.77101,-64.44971"
+ id="path9238"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="450.67688"
+ y="193.66904"
+ id="text9282"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="450.67688"
+ y="193.66904"
+ id="tspan9280">⑦ mapFromScene()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="497.35483"
+ y="100.48878"
+ id="text9334"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="497.35483"
+ y="100.48878"
+ id="tspan9332">â‘¢ deliverPointerEvent()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="497.35483"
+ y="111.44315"
+ id="tspan1575">â‘£ deliverUpdatedPoints()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="497.35483"
+ y="122.39753"
+ id="tspan1577">⑤ deliverToPassiveGrabbers()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="497.35483"
+ y="133.3519"
+ id="tspan9365">â‘¥ localizePointerEvent()</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 443.986,186.10711 c 3.61207,29.57315 14.20183,7.52734 15.4725,29.56066"
+ id="path9427"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576)"
+ d="m 546.28994,291.07097 c -3.52959,-16.19952 30.87947,-9.32879 19.17598,1.46963"
+ id="path14423"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="557.57617"
+ y="233.58138"
+ id="text14441"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="557.57617"
+ y="233.58138"
+ id="tspan14459"
+ sodipodi:role="line">â‘© handlePointerEventImpl()</tspan><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="557.57617"
+ y="244.53575"
+ sodipodi:role="line"
+ id="tspan2285">⑪ dragOverThreshold()</tspan><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="557.57617"
+ y="255.49011"
+ sodipodi:role="line"
+ id="tspan2287">â‘« grabPoints()</tspan><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="557.57617"
+ y="266.44449"
+ sodipodi:role="line"
+ id="tspan2289">⑬ setActive()</tspan><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="557.57617"
+ y="277.39886"
+ sodipodi:role="line"
+ id="tspan2603">â‘° moveTarget()</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.03579;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.03579, 1.03579;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#Arrow5)"
+ d="M 249.32923,23.153194 H 125.81655"
+ id="path1311" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="129.35161"
+ y="18.770849"
+ id="text1315"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="129.35161"
+ y="18.770849"
+ id="tspan1317">â‘  event(QTouchEvent*)</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.03579;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.03579, 1.03579;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#Arrow5)"
+ d="M 480.13219,146.11428 434.01617,42.608119"
+ id="path2865"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="224.50352"
+ y="-380.62888"
+ id="text2981"
+ transform="rotate(65.571486)"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="224.50352"
+ y="-380.62888"
+ id="tspan2979">â‘¡ event(QTouchEvent*)</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4-5)"
+ d="m 442.97658,187.15842 c -50.02267,78.60672 40.30509,72.83709 29.70231,109.51513"
+ id="path9238-5"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="496.79193"
+ y="226.98381"
+ id="text9304-6"
+ transform="rotate(6.9725705)"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="496.79193"
+ y="226.98381"
+ id="tspan9302"> ⑧ handlePointerEvent()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="496.79193"
+ y="237.93819"
+ id="tspan2397">⑨ wantsPointerEvent()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="496.79193"
+ y="247.93932"
+ id="tspan2371" /></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="639.57428"
+ y="275.21478"
+ id="text2293"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="639.57428"
+ y="275.21478"
+ id="tspan2291">â‘­ onActiveChanged()</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 625.08309,266.11561 c 33.3115,8.38416 -25.30249,21.02634 -26.36628,32.66492"
+ id="path2399"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="537.05072"
+ y="198.02666"
+ id="text2575"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="537.05072"
+ y="198.02666"
+ id="tspan2573">⑮ activeChanged Ⓢ</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="537.05072"
+ y="208.98103"
+ id="tspan4018">⑯ setColor()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="537.05072"
+ y="219.93539"
+ id="tspan2601">⑱ setX() / setY()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text3431"
+ transform="rotate(-38.038218,476.61944,239.40311)"><textPath
+ xlink:href="#path9238-5"
+ id="textPath3609"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ id="tspan3429">QTouchEvent*</tspan></textPath></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text3832"
+ transform="rotate(58.431196)"
+ x="474.64865"
+ y="-273.09793"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ id="tspan3828">target</tspan></text>
+ <circle
+ id="path4083"
+ style="fill:#000000;stroke:#e6a11b;stroke-width:0.750085"
+ cx="536.95331"
+ cy="217.47479"
+ r="0.0092422031" />
+ <circle
+ id="path4109"
+ style="fill:#000000;stroke:#e6a11b;stroke-width:0.750085"
+ cx="550.58368"
+ cy="218.91232"
+ r="0.0092422031" />
+ <circle
+ id="path4135"
+ style="fill:#000000;stroke:#e6a11b;stroke-width:0.750085"
+ cx="584.84467"
+ cy="-85.462952"
+ r="0.0092422031" />
+</svg>
diff --git a/src/quick/doc/src/internal/pinchAndDragHandlers-singlePressDelivery.svg b/src/quick/doc/src/internal/pinchAndDragHandlers-singlePressDelivery.svg
new file mode 100644
index 0000000000..e2f982fe5c
--- /dev/null
+++ b/src/quick/doc/src/internal/pinchAndDragHandlers-singlePressDelivery.svg
@@ -0,0 +1,714 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!-- Pages: 1 -->
+
+<svg
+ width="715.88171pt"
+ height="331.96246pt"
+ viewBox="0 0 715.96267 331.96246"
+ version="1.1"
+ id="svg806"
+ sodipodi:docname="pinchAndDragHandlers-singlePressDelivery.svg"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs810">
+ <marker
+ style="overflow:visible"
+ id="marker1576"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="marker1576-4"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574-4" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="namedview808"
+ pagecolor="#ffffff"
+ bordercolor="#111111"
+ borderopacity="1"
+ inkscape:showpageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="1"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="pt"
+ showgrid="false"
+ inkscape:zoom="2.7028887"
+ inkscape:cx="482.07682"
+ inkscape:cy="213.29032"
+ inkscape:window-width="3840"
+ inkscape:window-height="2139"
+ inkscape:window-x="3840"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg806" />
+ <g
+ id="graph0"
+ class="graph"
+ transform="translate(3.9999999,327.98119)">
+ <polygon
+ fill="#ffffff"
+ stroke="none"
+ points="615.07,4 -4,4 -4,-328 615.07,-328 "
+ id="polygon654"
+ transform="matrix(1.1565133,0,0,1,0.62605332,0)" />
+ <!-- llfkmbbephlg -->
+ <g
+ id="node1"
+ class="node"
+ style="stroke-width:1.00011;stroke-dasharray:none">
+ <title
+ id="title656">llfkmbbephlg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="262.78,-328 250.78,-328 250.78,-288 474.53,-288 474.53,-324 262.78,-324 "
+ id="polygon658"
+ style="stroke-width:1.00011;stroke-dasharray:none" />
+ <polyline
+ fill="none"
+ stroke="#000000"
+ points="250.78,-324 262.78,-324"
+ id="polyline660"
+ style="stroke-width:1.00011;stroke-dasharray:none" />
+ <text
+ text-anchor="middle"
+ x="362.66"
+ y="-300.95001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text662"
+ style="stroke-width:1.00011;stroke-dasharray:none">ResizeItemToWindow_QMLTYPE_1</text>
+ </g>
+ <!-- llfkmbbeijgg -->
+ <g
+ id="node2"
+ class="node">
+ <title
+ id="title665">llfkmbbeijgg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="402.53,-252 322.78,-252 322.78,-216 402.53,-216 "
+ id="polygon667" />
+ <text
+ text-anchor="middle"
+ x="362.66"
+ y="-229.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text669">â„šRootItem</text>
+ </g>
+ <!-- llfkmbbephlg&#45;&gt;llfkmbbeijgg -->
+ <g
+ id="edge1"
+ class="edge">
+ <title
+ id="title672">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path674" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="362.66,-274.8 366.66,-280.8 362.66,-286.8 358.66,-280.8 "
+ id="polygon676" />
+ </g>
+ <!-- llfkmbbepmbg -->
+ <g
+ id="node3"
+ class="node">
+ <title
+ id="title679">llfkmbbepmbg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="327.66,-180 245.66,-180 245.66,-144 327.66,-144 "
+ id="polygon681" />
+ <text
+ text-anchor="middle"
+ x="286.66"
+ y="-157.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text683">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbeijgg&#45;&gt;llfkmbbepmbg -->
+ <g
+ id="edge2"
+ class="edge">
+ <title
+ id="title686">llfkmbbeijgg-&gt;llfkmbbepmbg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 334.28,-206.87 c -9.61,8.86 -20.1,18.52 -28.79,26.52"
+ id="path688" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="334.38,-206.96 341.51,-208.08 343.21,-215.09 336.09,-213.96 "
+ id="polygon690" />
+ </g>
+ <!-- llfkmbclplkg -->
+ <g
+ id="node11"
+ class="node">
+ <title
+ id="title693">llfkmbclplkg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="533.43,-176.56 345.89,-176.56 345.8,-156.44 439.66,-144 533.52,-156.44 "
+ id="polygon695" />
+ <text
+ text-anchor="middle"
+ x="439.66"
+ y="-157.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text697">â„šDeliveryAgent</text>
+ </g>
+ <!-- llfkmbbeijgg&#45;&gt;llfkmbclplkg -->
+ <g
+ id="edge10"
+ class="edge">
+ <title
+ id="title700">llfkmbbeijgg-&gt;llfkmbclplkg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 391.18,-207.07 c 11.28,10.25 23.79,21.62 33.27,30.25"
+ id="path702" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="391.24,-207.02 389.49,-214.01 382.36,-215.09 384.11,-208.09 "
+ id="polygon704" />
+ </g>
+ <!-- llfkmbodpifg -->
+ <g
+ id="node4"
+ class="node">
+ <title
+ id="title707">llfkmbodpifg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="0.08,-75.44 177.23,-75.44 177.32,-95.56 88.66,-108 0,-95.56 "
+ id="polygon709" />
+ <text
+ text-anchor="middle"
+ x="88.660004"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text711">â„šPinchHandler</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbodpifg -->
+ <g
+ id="edge3"
+ class="edge">
+ <title
+ id="title714">llfkmbbepmbg-&gt;llfkmbodpifg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 233.33,-142.15 c -34.69,12.27 -78.95,27.92 -109.33,38.66"
+ id="path716" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="233.47,-142.2 240.46,-140.42 244.78,-146.2 237.79,-147.97 "
+ id="polygon718" />
+ </g>
+ <!-- llfkmbnfping -->
+ <g
+ id="node5"
+ class="node">
+ <title
+ id="title721">llfkmbnfping</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="277.66,-108 195.66,-108 195.66,-72 277.66,-72 "
+ id="polygon723" />
+ <text
+ text-anchor="middle"
+ x="236.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text725">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbnfping -->
+ <g
+ id="edge4"
+ class="edge">
+ <title
+ id="title728">llfkmbbepmbg-&gt;llfkmbnfping</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 266.73,-133.1 c -6.04,8.45 -12.48,17.48 -17.85,25"
+ id="path730" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="266.8,-133.2 273.54,-135.76 273.78,-142.96 267.03,-140.41 "
+ id="polygon732" />
+ </g>
+ <!-- llfkmbbdfibg -->
+ <g
+ id="node7"
+ class="node">
+ <title
+ id="title735">llfkmbbdfibg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="377.66,-108 295.66,-108 295.66,-72 377.66,-72 "
+ id="polygon737" />
+ <text
+ text-anchor="middle"
+ x="336.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text739">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbbdfibg -->
+ <g
+ id="edge6"
+ class="edge">
+ <title
+ id="title742">llfkmbbepmbg-&gt;llfkmbbdfibg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 306.59,-133.1 c 6.03,8.45 12.48,17.48 17.85,25"
+ id="path744" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="306.52,-133.2 306.28,-140.41 299.54,-142.96 299.77,-135.76 "
+ id="polygon746" />
+ </g>
+ <!-- llfkmbodeang -->
+ <g
+ id="node9"
+ class="node">
+ <title
+ id="title749">llfkmbodeang</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="522.66,-108 440.66,-108 440.66,-72 522.66,-72 "
+ id="polygon751" />
+ <text
+ text-anchor="middle"
+ x="481.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text753">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbodeang -->
+ <g
+ id="edge8"
+ class="edge">
+ <title
+ id="title756">llfkmbbepmbg-&gt;llfkmbodeang</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 340.14,-141.8 c 31.61,11.35 71.02,25.49 100.22,35.98"
+ id="path758" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="340.16,-141.8 335.86,-147.59 328.86,-145.85 333.16,-140.06 "
+ id="polygon760" />
+ </g>
+ <!-- llfkmbbfjjgg -->
+ <g
+ id="node6"
+ class="node">
+ <title
+ id="title763">llfkmbbfjjgg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="62.33,-3.44 232.99,-3.44 233.07,-23.56 147.66,-36 62.25,-23.56 "
+ id="polygon765" />
+ <text
+ text-anchor="middle"
+ x="147.66"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text767">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbnfping&#45;&gt;llfkmbbfjjgg -->
+ <g
+ id="edge5"
+ class="edge">
+ <title
+ id="title770">llfkmbnfping-&gt;llfkmbbfjjgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 204.77,-63.92 c -12.86,10.12 -27.25,21.44 -38.37,30.18"
+ id="path772" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="204.52,-63.72 211.71,-64.29 213.95,-71.14 206.76,-70.57 "
+ id="polygon774" />
+ </g>
+ <!-- llfkmbbfibdg -->
+ <g
+ id="node8"
+ class="node">
+ <title
+ id="title777">llfkmbbfibdg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="251.33,-3.44 421.99,-3.44 422.07,-23.56 336.66,-36 251.25,-23.56 "
+ id="polygon779" />
+ <text
+ text-anchor="middle"
+ x="336.66"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text781">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbbdfibg&#45;&gt;llfkmbbfibdg -->
+ <g
+ id="edge7"
+ class="edge">
+ <title
+ id="title784">llfkmbbdfibg-&gt;llfkmbbfibdg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 336.66,-58.81 c 0,7.77 0,15.86 0,22.71"
+ id="path786" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="336.66,-58.8 340.66,-64.8 336.66,-70.8 332.66,-64.8 "
+ id="polygon788" />
+ </g>
+ <!-- llfkmbcknglg -->
+ <g
+ id="node10"
+ class="node">
+ <title
+ id="title791">llfkmbcknglg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="440.33,-3.44 610.99,-3.44 611.07,-23.56 525.66,-36 440.25,-23.56 "
+ id="polygon793" />
+ <text
+ text-anchor="middle"
+ x="525.65997"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text795">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbodeang&#45;&gt;llfkmbcknglg -->
+ <g
+ id="edge9"
+ class="edge">
+ <title
+ id="title798">llfkmbodeang-&gt;llfkmbcknglg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 499.32,-60.9 c 5.62,8.94 11.63,18.5 16.49,26.23"
+ id="path800" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="499.4,-60.77 499.59,-67.98 493.01,-70.93 492.82,-63.72 "
+ id="polygon802" />
+ </g>
+ <g
+ id="g11866"
+ class="edge"
+ transform="rotate(-90,623.51573,-276.02262)">
+ <title
+ id="title11860">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path11862" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="358.66,-280.8 362.66,-274.8 366.66,-280.8 362.66,-286.8 "
+ id="polygon11864" />
+ </g>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 708.8157,-33.802688 c 0,-1.93521 -13.67346,-3.50801 -30.51031,-3.50801 -16.83686,0 -30.51503,1.5728 -30.51503,3.50801 0,0 0,31.5378501 0,31.5378501 0,1.93522003 13.67817,3.50801 30.51503,3.50801 16.83685,0 30.51031,-1.57278997 30.51031,-3.50801 0,0 0,-31.5378501 0,-31.5378501"
+ id="path12337"
+ style="stroke-width:0.568213" />
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 708.8157,-33.802688 c 0,1.92838 -13.67346,3.50116 -30.51031,3.50116 -16.83686,0 -30.51503,-1.57278 -30.51503,-3.50116"
+ id="path12339"
+ style="stroke-width:0.568213" />
+ <text
+ text-anchor="middle"
+ x="678.23846"
+ y="-14.011026"
+ font-family="Times, serif"
+ font-size="6pt"
+ id="text12341"
+ style="stroke-width:0.681807">currentPoints</text>
+ <g
+ id="g14256"
+ class="edge"
+ transform="rotate(-90,507.65517,-314.66065)">
+ <title
+ id="title14250">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path14252" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="362.66,-274.8 366.66,-280.8 362.66,-286.8 358.66,-280.8 "
+ id="polygon14254" />
+ </g>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 631.59317,-188.30128 c 0,-1.93521 -13.67346,-3.50801 -30.51031,-3.50801 -16.83686,0 -30.51503,1.5728 -30.51503,3.50801 0,0 0,31.53785 0,31.53785 0,1.93522 13.67817,3.50801 30.51503,3.50801 16.83685,0 30.51031,-1.57279 30.51031,-3.50801 0,0 0,-31.53785 0,-31.53785"
+ id="path14258"
+ style="stroke-width:0.568213" />
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 631.59317,-188.30128 c 0,1.92838 -13.67346,3.50116 -30.51031,3.50116 -16.83686,0 -30.51503,-1.57278 -30.51503,-3.50116"
+ id="path14260"
+ style="stroke-width:0.568213" />
+ <text
+ text-anchor="middle"
+ x="601.01593"
+ y="-168.50961"
+ font-family="Times, serif"
+ font-size="6pt"
+ id="text14262"
+ style="stroke-width:0.681807">targetItems</text>
+ </g>
+ <g
+ id="g2388"
+ transform="translate(166.85425,117.65224)">
+ <rect
+ style="fill:#ffffff;stroke:#000000;stroke-width:0.869398;stroke-linecap:square;stroke-dasharray:0.869398, 0.869398;stroke-opacity:1"
+ id="rect981"
+ width="115.87793"
+ height="32.582088"
+ x="-181.46954"
+ y="-115.9365"
+ ry="0"
+ transform="matrix(1,0,-0.22867874,0.97350194,0,0)" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9pt;line-height:125%;font-family:Times, serif;-inkscape-font-specification:'Times, serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ x="-149.44366"
+ y="-93.742676"
+ id="text1897"><tspan
+ sodipodi:role="line"
+ id="tspan1895"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9pt;font-family:Times, serif;-inkscape-font-specification:'Times, serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="-149.44366"
+ y="-93.742676">QGuiApplication</tspan></text>
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576)"
+ d="m 534.3515,175.55291 c 8.9308,13.96865 -25.72468,19.46301 -18.49471,5.27481"
+ id="path2661"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#e6a11b;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="113.75787"
+ y="213.93288"
+ id="text8866"
+ transform="rotate(-11.181322)"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#e6a11b;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="113.75787"
+ y="213.93288"
+ id="tspan8864">skipped</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#e6a11b;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="M 248.80679,166.05103 C 128.29267,210.33716 109.36613,183.17227 102.95996,215.73635"
+ id="path2625"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#e6a11b;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 205.26622,258.16221 c -30.99608,41.44745 -40.03963,1.76839 -45.06759,29.20422"
+ id="path9198"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#e6a11b;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 325.15207,257.83821 c -2.27262,29.75722 -30.19879,12.70432 -28.21604,34.22384"
+ id="path9218"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 515.92916,258.31316 c 8.69611,26.1658 28.09925,12.12573 28.29307,30.37142"
+ id="path9238"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="412.41052"
+ y="194.7211"
+ id="text9282"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="412.41052"
+ y="194.7211"
+ id="tspan9280">â‘¢ mapFromScene()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="527.64844"
+ y="266.12436"
+ id="text9304"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="527.64844"
+ y="266.12436"
+ id="tspan11558">⑤ handlePointerEvent()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="539.94745"
+ y="184.11032"
+ id="text9334"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="539.94745"
+ y="184.11032"
+ id="tspan9332">â‘  deliverMatchingPointsToItem()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="539.94745"
+ y="195.0647"
+ id="tspan9365">â‘¡ localizePointerEvent()</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#e6a11b;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 371.17602,176.60114 c -19.44733,11.33075 -28.42572,-6.22235 -36.44805,-6.86652"
+ id="path9367"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#e6a11b;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 371.17602,176.60114 c -14.82664,21.37092 -94.59207,21.74646 -97.17351,38.08234"
+ id="path9387"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#e6a11b;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 371.17602,176.60114 c -14.82664,21.37092 4.12412,22.17486 1.54268,38.51074"
+ id="path9407"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 371.17602,176.60114 c -14.82664,21.37092 90.86392,22.23065 88.28248,38.56653"
+ id="path9427"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#e6a11b;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="362.56143"
+ y="141.40547"
+ id="text11290"
+ transform="rotate(10.069153)"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#e6a11b;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="362.56143"
+ y="141.40547"
+ id="tspan11288">skipped</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="459.80051"
+ y="208.66859"
+ id="text11424"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="459.80051"
+ y="208.66859"
+ id="tspan11422">â‘£ handlePointerEvent()</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576)"
+ d="m 553.17544,291.61876 c -3.52959,-16.19952 30.87947,-9.32879 19.17598,1.46963"
+ id="path14423"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="577.46771"
+ y="276.84311"
+ id="text14441"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="577.46771"
+ y="276.84311"
+ id="tspan14439">â‘¥ handlePointerEventImpl()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="577.46771"
+ y="287.79749"
+ id="tspan14459">⑦ setPassiveGrab()</tspan></text>
+</svg>
diff --git a/src/quick/doc/src/internal/pinchAndDragHandlers-singlePressPrep.svg b/src/quick/doc/src/internal/pinchAndDragHandlers-singlePressPrep.svg
new file mode 100644
index 0000000000..4cec17b399
--- /dev/null
+++ b/src/quick/doc/src/internal/pinchAndDragHandlers-singlePressPrep.svg
@@ -0,0 +1,648 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Pages: 1 -->
+
+<svg
+ width="695.15735pt"
+ height="331.96246pt"
+ viewBox="0 0 695.23596 331.96246"
+ version="1.1"
+ id="svg806"
+ sodipodi:docname="pinchAndDragHandlers-singlePressPrep.svg"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs810">
+ <marker
+ style="overflow:visible"
+ id="Arrow5"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874264"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path8893" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="marker1576"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="marker1576-4"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574-4" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="namedview808"
+ pagecolor="#ffffff"
+ bordercolor="#111111"
+ borderopacity="1"
+ inkscape:showpageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="1"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="pt"
+ showgrid="false"
+ inkscape:zoom="2.6661199"
+ inkscape:cx="434.33906"
+ inkscape:cy="270.61799"
+ inkscape:window-width="3840"
+ inkscape:window-height="2139"
+ inkscape:window-x="3840"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg806" />
+ <g
+ id="graph0"
+ class="graph"
+ transform="translate(3.9999998,327.9812)">
+ <polygon
+ fill="#ffffff"
+ stroke="none"
+ points="615.07,-328 615.07,4 -4,4 -4,-328 "
+ id="polygon654"
+ transform="matrix(1.1230329,0,0,1,0.4921318,0)" />
+ <!-- llfkmbbephlg -->
+ <g
+ id="node1"
+ class="node"
+ style="stroke-width:1.00011;stroke-dasharray:none">
+ <title
+ id="title656">llfkmbbephlg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="250.78,-288 474.53,-288 474.53,-324 262.78,-324 262.78,-328 250.78,-328 "
+ id="polygon658"
+ style="stroke-width:1.00011;stroke-dasharray:none" />
+ <polyline
+ fill="none"
+ stroke="#000000"
+ points="250.78,-324 262.78,-324"
+ id="polyline660"
+ style="stroke-width:1.00011;stroke-dasharray:none" />
+ <text
+ text-anchor="middle"
+ x="362.66"
+ y="-300.95001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text662"
+ style="stroke-width:1.00011;stroke-dasharray:none">ResizeItemToWindow_QMLTYPE_1</text>
+ </g>
+ <!-- llfkmbbeijgg -->
+ <g
+ id="node2"
+ class="node">
+ <title
+ id="title665">llfkmbbeijgg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="402.53,-252 322.78,-252 322.78,-216 402.53,-216 "
+ id="polygon667" />
+ <text
+ text-anchor="middle"
+ x="362.66"
+ y="-229.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text669">â„šRootItem</text>
+ </g>
+ <!-- llfkmbbephlg&#45;&gt;llfkmbbeijgg -->
+ <g
+ id="edge1"
+ class="edge">
+ <title
+ id="title672">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path674" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="362.66,-274.8 366.66,-280.8 362.66,-286.8 358.66,-280.8 "
+ id="polygon676" />
+ </g>
+ <!-- llfkmbbepmbg -->
+ <g
+ id="node3"
+ class="node">
+ <title
+ id="title679">llfkmbbepmbg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="327.66,-180 245.66,-180 245.66,-144 327.66,-144 "
+ id="polygon681" />
+ <text
+ text-anchor="middle"
+ x="286.66"
+ y="-157.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text683">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbeijgg&#45;&gt;llfkmbbepmbg -->
+ <g
+ id="edge2"
+ class="edge">
+ <title
+ id="title686">llfkmbbeijgg-&gt;llfkmbbepmbg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 334.28,-206.87 c -9.61,8.86 -20.1,18.52 -28.79,26.52"
+ id="path688" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="334.38,-206.96 341.51,-208.08 343.21,-215.09 336.09,-213.96 "
+ id="polygon690" />
+ </g>
+ <!-- llfkmbclplkg -->
+ <g
+ id="node11"
+ class="node">
+ <title
+ id="title693">llfkmbclplkg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="345.89,-176.56 345.8,-156.44 439.66,-144 533.52,-156.44 533.43,-176.56 "
+ id="polygon695" />
+ <text
+ text-anchor="middle"
+ x="439.66"
+ y="-157.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text697">â„šDeliveryAgent</text>
+ </g>
+ <!-- llfkmbbeijgg&#45;&gt;llfkmbclplkg -->
+ <g
+ id="edge10"
+ class="edge">
+ <title
+ id="title700">llfkmbbeijgg-&gt;llfkmbclplkg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 391.18,-207.07 c 11.28,10.25 23.79,21.62 33.27,30.25"
+ id="path702" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="391.24,-207.02 389.49,-214.01 382.36,-215.09 384.11,-208.09 "
+ id="polygon704" />
+ </g>
+ <!-- llfkmbodpifg -->
+ <g
+ id="node4"
+ class="node">
+ <title
+ id="title707">llfkmbodpifg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="177.23,-75.44 177.32,-95.56 88.66,-108 0,-95.56 0.08,-75.44 "
+ id="polygon709" />
+ <text
+ text-anchor="middle"
+ x="88.660004"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text711">â„šPinchHandler</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbodpifg -->
+ <g
+ id="edge3"
+ class="edge">
+ <title
+ id="title714">llfkmbbepmbg-&gt;llfkmbodpifg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 233.33,-142.15 c -34.69,12.27 -78.95,27.92 -109.33,38.66"
+ id="path716" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="233.47,-142.2 240.46,-140.42 244.78,-146.2 237.79,-147.97 "
+ id="polygon718" />
+ </g>
+ <!-- llfkmbnfping -->
+ <g
+ id="node5"
+ class="node">
+ <title
+ id="title721">llfkmbnfping</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="277.66,-108 195.66,-108 195.66,-72 277.66,-72 "
+ id="polygon723" />
+ <text
+ text-anchor="middle"
+ x="236.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text725">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbnfping -->
+ <g
+ id="edge4"
+ class="edge">
+ <title
+ id="title728">llfkmbbepmbg-&gt;llfkmbnfping</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 266.73,-133.1 c -6.04,8.45 -12.48,17.48 -17.85,25"
+ id="path730" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="266.8,-133.2 273.54,-135.76 273.78,-142.96 267.03,-140.41 "
+ id="polygon732" />
+ </g>
+ <!-- llfkmbbdfibg -->
+ <g
+ id="node7"
+ class="node">
+ <title
+ id="title735">llfkmbbdfibg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="377.66,-108 295.66,-108 295.66,-72 377.66,-72 "
+ id="polygon737" />
+ <text
+ text-anchor="middle"
+ x="336.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text739">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbbdfibg -->
+ <g
+ id="edge6"
+ class="edge">
+ <title
+ id="title742">llfkmbbepmbg-&gt;llfkmbbdfibg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 306.59,-133.1 c 6.03,8.45 12.48,17.48 17.85,25"
+ id="path744" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="306.52,-133.2 306.28,-140.41 299.54,-142.96 299.77,-135.76 "
+ id="polygon746" />
+ </g>
+ <!-- llfkmbodeang -->
+ <g
+ id="node9"
+ class="node">
+ <title
+ id="title749">llfkmbodeang</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="522.66,-108 440.66,-108 440.66,-72 522.66,-72 "
+ id="polygon751" />
+ <text
+ text-anchor="middle"
+ x="481.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text753">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbodeang -->
+ <g
+ id="edge8"
+ class="edge">
+ <title
+ id="title756">llfkmbbepmbg-&gt;llfkmbodeang</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 340.14,-141.8 c 31.61,11.35 71.02,25.49 100.22,35.98"
+ id="path758" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="340.16,-141.8 335.86,-147.59 328.86,-145.85 333.16,-140.06 "
+ id="polygon760" />
+ </g>
+ <!-- llfkmbbfjjgg -->
+ <g
+ id="node6"
+ class="node">
+ <title
+ id="title763">llfkmbbfjjgg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="232.99,-3.44 233.07,-23.56 147.66,-36 62.25,-23.56 62.33,-3.44 "
+ id="polygon765" />
+ <text
+ text-anchor="middle"
+ x="147.66"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text767">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbnfping&#45;&gt;llfkmbbfjjgg -->
+ <g
+ id="edge5"
+ class="edge">
+ <title
+ id="title770">llfkmbnfping-&gt;llfkmbbfjjgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 204.77,-63.92 c -12.86,10.12 -27.25,21.44 -38.37,30.18"
+ id="path772" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="204.52,-63.72 211.71,-64.29 213.95,-71.14 206.76,-70.57 "
+ id="polygon774" />
+ </g>
+ <!-- llfkmbbfibdg -->
+ <g
+ id="node8"
+ class="node">
+ <title
+ id="title777">llfkmbbfibdg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="421.99,-3.44 422.07,-23.56 336.66,-36 251.25,-23.56 251.33,-3.44 "
+ id="polygon779" />
+ <text
+ text-anchor="middle"
+ x="336.66"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text781">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbbdfibg&#45;&gt;llfkmbbfibdg -->
+ <g
+ id="edge7"
+ class="edge">
+ <title
+ id="title784">llfkmbbdfibg-&gt;llfkmbbfibdg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 336.66,-58.81 c 0,7.77 0,15.86 0,22.71"
+ id="path786" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="336.66,-58.8 340.66,-64.8 336.66,-70.8 332.66,-64.8 "
+ id="polygon788" />
+ </g>
+ <!-- llfkmbcknglg -->
+ <g
+ id="node10"
+ class="node">
+ <title
+ id="title791">llfkmbcknglg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="610.99,-3.44 611.07,-23.56 525.66,-36 440.25,-23.56 440.33,-3.44 "
+ id="polygon793" />
+ <text
+ text-anchor="middle"
+ x="525.65997"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text795">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbodeang&#45;&gt;llfkmbcknglg -->
+ <g
+ id="edge9"
+ class="edge">
+ <title
+ id="title798">llfkmbodeang-&gt;llfkmbcknglg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 499.32,-60.9 c 5.62,8.94 11.63,18.5 16.49,26.23"
+ id="path800" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="499.4,-60.77 499.59,-67.98 493.01,-70.93 492.82,-63.72 "
+ id="polygon802" />
+ </g>
+ </g>
+ <g
+ id="g2388"
+ transform="translate(166.85425,117.65225)">
+ <rect
+ style="fill:#ffffff;stroke:#000000;stroke-width:0.869398;stroke-linecap:square;stroke-dasharray:0.869398, 0.869398;stroke-opacity:1"
+ id="rect981"
+ width="115.87793"
+ height="32.582088"
+ x="-181.46954"
+ y="-115.9365"
+ ry="0"
+ transform="matrix(1,0,-0.22867874,0.97350194,0,0)" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9pt;line-height:125%;font-family:Times, serif;-inkscape-font-specification:'Times, serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ x="-149.44366"
+ y="-93.742676"
+ id="text1897"><tspan
+ sodipodi:role="line"
+ id="tspan1895"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9pt;font-family:Times, serif;-inkscape-font-specification:'Times, serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="-149.44366"
+ y="-93.742676">QGuiApplication</tspan></text>
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.03579;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.03579, 1.03579;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#Arrow5)"
+ d="M 250.09544,22.1843 H 126.58276"
+ id="path1311" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="130.11781"
+ y="17.801956"
+ id="text1315"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="130.11781"
+ y="17.801956"
+ id="tspan1317">â‘  event(QTouchEvent*)</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.03579;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.03579, 1.03579;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#Arrow5)"
+ d="M 480.8984,145.14539 434.78238,41.639225"
+ id="path2865"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="223.93822"
+ y="-381.72717"
+ id="text2981"
+ transform="rotate(65.571486)"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="223.93822"
+ y="-381.72717"
+ id="tspan2979">â‘¡ event(QTouchEvent*)</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576)"
+ d="m 534.3515,175.55292 c 8.9308,13.96865 -25.72468,19.46301 -18.49471,5.27481"
+ id="path2661"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="540.16193"
+ y="178.55621"
+ id="text8573"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="540.16193"
+ y="178.55621"
+ id="tspan8587">â‘¢ deliverPointerEvent()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="540.16193"
+ y="189.51059"
+ id="tspan8591">â‘£ deliverPressOrReleaseEvent()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="540.16193"
+ y="200.46495"
+ id="tspan8595">⑤ pointerTargets()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="187.83101"
+ y="197.08606"
+ id="text8866"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="187.83101"
+ y="197.08606"
+ id="tspan8864">â‘¥ wantsEventPoint()</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 414.44327,180.9263 c -120.51412,44.28613 -305.07714,2.24598 -311.48331,34.81006"
+ id="path2625"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="M 414.44327,180.9263 C 293.92915,225.21243 166.6048,254.80236 160.19863,287.36644"
+ id="path9198"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="M 414.44327,180.9263 C 293.92915,225.21243 303.3422,259.49798 296.93603,292.06206"
+ id="path9218"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 414.44327,180.9263 c 8.69611,26.1658 156.27171,32.20621 129.77896,107.75829"
+ id="path9238"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="180.63849"
+ y="263.64062"
+ id="text9260"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="162.79994"
+ y="275.0322"
+ id="tspan9258">⑦ wantsEventPoint()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;text-anchor:end;text-align:end"
+ x="295.72836"
+ y="286.02438"
+ id="text9282"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;text-anchor:end;text-align:end"
+ x="203.01468"
+ y="286.02438"
+ id="tspan9280">⑧ wantsEventPoint()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="550.57092"
+ y="278.21912"
+ id="text9304"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="550.57092"
+ y="278.21912"
+ id="tspan9302">⑨ wantsEventPoint()</tspan></text>
+ <g
+ id="node34"
+ class="node"
+ transform="matrix(0.68180684,0,0,0.68180684,-606.60021,386.57842)">
+ <title
+ id="title12335">llncgjfhndpg</title>
+ </g>
+</svg>
diff --git a/src/quick/doc/src/internal/pinchAndDragHandlers-singleReleaseDelivery.svg b/src/quick/doc/src/internal/pinchAndDragHandlers-singleReleaseDelivery.svg
new file mode 100644
index 0000000000..d96bf4ff10
--- /dev/null
+++ b/src/quick/doc/src/internal/pinchAndDragHandlers-singleReleaseDelivery.svg
@@ -0,0 +1,825 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Pages: 1 -->
+
+<svg
+ width="737.57623pt"
+ height="332.46246pt"
+ viewBox="0 0 737.65964 332.46246"
+ version="1.1"
+ id="svg806"
+ sodipodi:docname="pinchAndDragHandlers-singleReleaseDelivery.svg"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs810">
+ <marker
+ style="overflow:visible"
+ id="marker1576"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="marker1576-4"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574-4" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="Arrow5"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874264"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path8893" />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="marker1576-4-5"
+ refX="0"
+ refY="0"
+ orient="auto-start-reverse"
+ inkscape:stockid="Arrow5"
+ markerWidth="5.8874259"
+ markerHeight="6.6094756"
+ viewBox="0 0 5.8874262 6.6094758"
+ inkscape:isstock="true"
+ inkscape:collect="always"
+ preserveAspectRatio="xMidYMid">
+ <path
+ transform="scale(0.5)"
+ style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+ d="m 6,0 c -3,1 -7,3 -9,5 0,0 0,-4 2,-5 -2,-1 -2,-5 -2,-5 2,2 6,4 9,5 z"
+ id="path1574-4-3" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="namedview808"
+ pagecolor="#ffffff"
+ bordercolor="#111111"
+ borderopacity="1"
+ inkscape:showpageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="1"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="pt"
+ showgrid="false"
+ inkscape:zoom="2.7028887"
+ inkscape:cx="472.64247"
+ inkscape:cy="236.96869"
+ inkscape:window-width="3840"
+ inkscape:window-height="2139"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg806" />
+ <g
+ id="graph0"
+ class="graph"
+ transform="translate(4,328.48129)">
+ <polygon
+ fill="#ffffff"
+ stroke="none"
+ points="615.07,4 -4,4 -4,-328 615.07,-328 "
+ id="polygon654"
+ transform="matrix(1.1915609,0,0,1,0.76624365,0)" />
+ <!-- llfkmbbephlg -->
+ <g
+ id="node1"
+ class="node"
+ style="stroke-width:1.00011;stroke-dasharray:none">
+ <title
+ id="title656">llfkmbbephlg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="250.78,-288 474.53,-288 474.53,-324 262.78,-324 262.78,-328 250.78,-328 "
+ id="polygon658"
+ style="stroke-width:1.00011;stroke-dasharray:none" />
+ <polyline
+ fill="none"
+ stroke="#000000"
+ points="250.78,-324 262.78,-324"
+ id="polyline660"
+ style="stroke-width:1.00011;stroke-dasharray:none" />
+ <text
+ text-anchor="middle"
+ x="362.66"
+ y="-300.95001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text662"
+ style="stroke-width:1.00011;stroke-dasharray:none">ResizeItemToWindow_QMLTYPE_1</text>
+ </g>
+ <!-- llfkmbbeijgg -->
+ <g
+ id="node2"
+ class="node">
+ <title
+ id="title665">llfkmbbeijgg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="322.78,-216 402.53,-216 402.53,-252 322.78,-252 "
+ id="polygon667" />
+ <text
+ text-anchor="middle"
+ x="362.66"
+ y="-229.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text669">â„šRootItem</text>
+ </g>
+ <!-- llfkmbbephlg&#45;&gt;llfkmbbeijgg -->
+ <g
+ id="edge1"
+ class="edge">
+ <title
+ id="title672">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path674" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="362.66,-286.8 358.66,-280.8 362.66,-274.8 366.66,-280.8 "
+ id="polygon676" />
+ </g>
+ <!-- llfkmbbepmbg -->
+ <g
+ id="node3"
+ class="node">
+ <title
+ id="title679">llfkmbbepmbg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="245.66,-144 327.66,-144 327.66,-180 245.66,-180 "
+ id="polygon681" />
+ <text
+ text-anchor="middle"
+ x="286.66"
+ y="-157.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text683">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbeijgg&#45;&gt;llfkmbbepmbg -->
+ <g
+ id="edge2"
+ class="edge">
+ <title
+ id="title686">llfkmbbeijgg-&gt;llfkmbbepmbg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 334.28,-206.87 c -9.61,8.86 -20.1,18.52 -28.79,26.52"
+ id="path688" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="343.21,-215.09 336.09,-213.96 334.38,-206.96 341.51,-208.08 "
+ id="polygon690" />
+ </g>
+ <!-- llfkmbclplkg -->
+ <g
+ id="node11"
+ class="node">
+ <title
+ id="title693">llfkmbclplkg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="345.8,-156.44 439.66,-144 533.52,-156.44 533.43,-176.56 345.89,-176.56 "
+ id="polygon695" />
+ <text
+ text-anchor="middle"
+ x="439.66"
+ y="-157.32001"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text697">â„šDeliveryAgent</text>
+ </g>
+ <!-- llfkmbbeijgg&#45;&gt;llfkmbclplkg -->
+ <g
+ id="edge10"
+ class="edge">
+ <title
+ id="title700">llfkmbbeijgg-&gt;llfkmbclplkg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 391.18,-207.07 c 11.28,10.25 23.79,21.62 33.27,30.25"
+ id="path702" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="382.36,-215.09 384.11,-208.09 391.24,-207.02 389.49,-214.01 "
+ id="polygon704" />
+ </g>
+ <!-- llfkmbodpifg -->
+ <g
+ id="node4"
+ class="node">
+ <title
+ id="title707">llfkmbodpifg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="177.32,-95.56 88.66,-108 0,-95.56 0.08,-75.44 177.23,-75.44 "
+ id="polygon709" />
+ <text
+ text-anchor="middle"
+ x="88.660004"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text711">â„šPinchHandler</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbodpifg -->
+ <g
+ id="edge3"
+ class="edge">
+ <title
+ id="title714">llfkmbbepmbg-&gt;llfkmbodpifg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 233.33,-142.15 c -34.69,12.27 -78.95,27.92 -109.33,38.66"
+ id="path716" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="244.78,-146.2 237.79,-147.97 233.47,-142.2 240.46,-140.42 "
+ id="polygon718" />
+ </g>
+ <!-- llfkmbnfping -->
+ <g
+ id="node5"
+ class="node">
+ <title
+ id="title721">llfkmbnfping</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="195.66,-72 277.66,-72 277.66,-108 195.66,-108 "
+ id="polygon723" />
+ <text
+ text-anchor="middle"
+ x="236.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text725">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbnfping -->
+ <g
+ id="edge4"
+ class="edge">
+ <title
+ id="title728">llfkmbbepmbg-&gt;llfkmbnfping</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 266.73,-133.1 c -6.04,8.45 -12.48,17.48 -17.85,25"
+ id="path730" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="273.78,-142.96 267.03,-140.41 266.8,-133.2 273.54,-135.76 "
+ id="polygon732" />
+ </g>
+ <!-- llfkmbbdfibg -->
+ <g
+ id="node7"
+ class="node">
+ <title
+ id="title735">llfkmbbdfibg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="295.66,-72 377.66,-72 377.66,-108 295.66,-108 "
+ id="polygon737" />
+ <text
+ text-anchor="middle"
+ x="336.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text739">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbbdfibg -->
+ <g
+ id="edge6"
+ class="edge">
+ <title
+ id="title742">llfkmbbepmbg-&gt;llfkmbbdfibg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 306.59,-133.1 c 6.03,8.45 12.48,17.48 17.85,25"
+ id="path744" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="299.54,-142.96 299.77,-135.76 306.52,-133.2 306.28,-140.41 "
+ id="polygon746" />
+ </g>
+ <!-- llfkmbodeang -->
+ <g
+ id="node9"
+ class="node">
+ <title
+ id="title749">llfkmbodeang</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="440.66,-72 522.66,-72 522.66,-108 440.66,-108 "
+ id="polygon751" />
+ <text
+ text-anchor="middle"
+ x="481.66"
+ y="-85.330002"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text753">â„šRectangle</text>
+ </g>
+ <!-- llfkmbbepmbg&#45;&gt;llfkmbodeang -->
+ <g
+ id="edge8"
+ class="edge">
+ <title
+ id="title756">llfkmbbepmbg-&gt;llfkmbodeang</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 340.14,-141.8 c 31.61,11.35 71.02,25.49 100.22,35.98"
+ id="path758" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="328.86,-145.85 333.16,-140.06 340.16,-141.8 335.86,-147.59 "
+ id="polygon760" />
+ </g>
+ <!-- llfkmbbfjjgg -->
+ <g
+ id="node6"
+ class="node">
+ <title
+ id="title763">llfkmbbfjjgg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="233.07,-23.56 147.66,-36 62.25,-23.56 62.33,-3.44 232.99,-3.44 "
+ id="polygon765" />
+ <text
+ text-anchor="middle"
+ x="147.66"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text767">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbnfping&#45;&gt;llfkmbbfjjgg -->
+ <g
+ id="edge5"
+ class="edge">
+ <title
+ id="title770">llfkmbnfping-&gt;llfkmbbfjjgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 204.77,-63.92 c -12.86,10.12 -27.25,21.44 -38.37,30.18"
+ id="path772" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="213.95,-71.14 206.76,-70.57 204.52,-63.72 211.71,-64.29 "
+ id="polygon774" />
+ </g>
+ <!-- llfkmbbfibdg -->
+ <g
+ id="node8"
+ class="node">
+ <title
+ id="title777">llfkmbbfibdg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="422.07,-23.56 336.66,-36 251.25,-23.56 251.33,-3.44 421.99,-3.44 "
+ id="polygon779" />
+ <text
+ text-anchor="middle"
+ x="336.66"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text781">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbbdfibg&#45;&gt;llfkmbbfibdg -->
+ <g
+ id="edge7"
+ class="edge">
+ <title
+ id="title784">llfkmbbdfibg-&gt;llfkmbbfibdg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 336.66,-58.81 c 0,7.77 0,15.86 0,22.71"
+ id="path786" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="336.66,-70.8 332.66,-64.8 336.66,-58.8 340.66,-64.8 "
+ id="polygon788" />
+ </g>
+ <!-- llfkmbcknglg -->
+ <g
+ id="node10"
+ class="node">
+ <title
+ id="title791">llfkmbcknglg</title>
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="611.07,-23.56 525.66,-36 440.25,-23.56 440.33,-3.44 610.99,-3.44 "
+ id="polygon793" />
+ <text
+ text-anchor="middle"
+ x="525.65997"
+ y="-13.33"
+ font-family="Times, serif"
+ font-size="9pt"
+ id="text795">â„šDragHandler</text>
+ </g>
+ <!-- llfkmbodeang&#45;&gt;llfkmbcknglg -->
+ <g
+ id="edge9"
+ class="edge">
+ <title
+ id="title798">llfkmbodeang-&gt;llfkmbcknglg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 499.32,-60.9 c 5.62,8.94 11.63,18.5 16.49,26.23"
+ id="path800" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="493.01,-70.93 492.82,-63.72 499.4,-60.77 499.59,-67.98 "
+ id="polygon802" />
+ </g>
+ <g
+ id="g11866"
+ class="edge"
+ transform="rotate(-90,623.51573,-276.02262)">
+ <title
+ id="title11860">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path11862" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="366.66,-280.8 362.66,-286.8 358.66,-280.8 362.66,-274.8 "
+ id="polygon11864" />
+ </g>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 708.8157,-33.802688 c 0,-1.93521 -13.67346,-3.50801 -30.51031,-3.50801 -16.83686,0 -30.51503,1.5728 -30.51503,3.50801 0,0 0,31.5378501 0,31.5378501 0,1.93522003 13.67817,3.50801 30.51503,3.50801 16.83685,0 30.51031,-1.57278997 30.51031,-3.50801 0,0 0,-31.5378501 0,-31.5378501"
+ id="path12337"
+ style="stroke-width:0.568213" />
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 708.8157,-33.802688 c 0,1.92838 -13.67346,3.50116 -30.51031,3.50116 -16.83686,0 -30.51503,-1.57278 -30.51503,-3.50116"
+ id="path12339"
+ style="stroke-width:0.568213" />
+ <text
+ text-anchor="middle"
+ x="678.23846"
+ y="-14.011026"
+ font-family="Times, serif"
+ font-size="6pt"
+ id="text12341"
+ style="stroke-width:0.681807">currentPoints</text>
+ <g
+ id="g14256"
+ class="edge"
+ transform="rotate(-90,507.65517,-314.66065)">
+ <title
+ id="title14250">llfkmbbephlg-&gt;llfkmbbeijgg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 362.66,-274.81 c 0,7.77 0,15.86 0,22.71"
+ id="path14252" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="362.66,-286.8 358.66,-280.8 362.66,-274.8 366.66,-280.8 "
+ id="polygon14254" />
+ </g>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 631.59317,-188.30128 c 0,-1.93521 -13.67346,-3.50801 -30.51031,-3.50801 -16.83686,0 -30.51503,1.5728 -30.51503,3.50801 0,0 0,31.53785 0,31.53785 0,1.93522 13.67817,3.50801 30.51503,3.50801 16.83685,0 30.51031,-1.57279 30.51031,-3.50801 0,0 0,-31.53785 0,-31.53785"
+ id="path14258"
+ style="stroke-width:0.568213" />
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 631.59317,-188.30128 c 0,1.92838 -13.67346,3.50116 -30.51031,3.50116 -16.83686,0 -30.51503,-1.57278 -30.51503,-3.50116"
+ id="path14260"
+ style="stroke-width:0.568213" />
+ <text
+ text-anchor="middle"
+ x="601.01593"
+ y="-168.50961"
+ font-family="Times, serif"
+ font-size="6pt"
+ id="text14262"
+ style="stroke-width:0.681807">targetItems</text>
+ <g
+ id="g3826"
+ class="edge"
+ transform="rotate(180,500.1871,-52.761609)">
+ <title
+ id="title3820">llfkmbodeang-&gt;llfkmbcknglg</title>
+ <path
+ fill="none"
+ stroke="#000000"
+ d="m 499.32,-60.9 c 5.62,8.94 11.63,18.5 16.49,26.23"
+ id="path3822" />
+ <polygon
+ fill="none"
+ stroke="#000000"
+ points="492.82,-63.72 499.4,-60.77 499.59,-67.98 493.01,-70.93 "
+ id="polygon3824" />
+ </g>
+ </g>
+ <g
+ id="g2388"
+ transform="translate(166.85425,118.15234)">
+ <rect
+ style="fill:#ffffff;stroke:#000000;stroke-width:0.869398;stroke-linecap:square;stroke-dasharray:0.869398, 0.869398;stroke-opacity:1"
+ id="rect981"
+ width="115.87793"
+ height="32.582088"
+ x="-181.46954"
+ y="-115.9365"
+ ry="0"
+ transform="matrix(1,0,-0.22867874,0.97350194,0,0)" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9pt;line-height:125%;font-family:Times, serif;-inkscape-font-specification:'Times, serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ x="-149.44366"
+ y="-93.742676"
+ id="text1897"><tspan
+ sodipodi:role="line"
+ id="tspan1895"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9pt;font-family:Times, serif;-inkscape-font-specification:'Times, serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="-149.44366"
+ y="-93.742676">QGuiApplication</tspan></text>
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576)"
+ d="m 508.49821,147.40834 c -5.01114,-15.80414 29.88553,-12.14178 19.22974,-0.30829"
+ id="path2661"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 529.10578,290.07508 c 2.22687,-20.59627 43.00062,-73.20365 2.77101,-64.44971"
+ id="path9238"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="495.45242"
+ y="108.62244"
+ id="text9334"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="495.45242"
+ y="108.62244"
+ id="tspan9332">â‘¢ deliverPointerEvent()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="495.45242"
+ y="119.57681"
+ id="tspan1575">â‘£ deliverUpdatedPoints()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="495.45242"
+ y="130.53119"
+ id="tspan9365">⑤ localizePointerEvent()</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576)"
+ d="m 546.28994,291.07097 c -3.52959,-16.19952 30.87947,-9.32879 19.17598,1.46963"
+ id="path14423"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="553.95435"
+ y="276.75214"
+ id="text14441"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="553.95435"
+ y="276.75214"
+ sodipodi:role="line"
+ id="tspan2603">⑧ setActive() </tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.03579;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.03579, 1.03579;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#Arrow5)"
+ d="M 249.32923,23.153194 H 125.81655"
+ id="path1311" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="129.35161"
+ y="18.770849"
+ id="text1315"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="129.35161"
+ y="18.770849"
+ id="tspan1317">â‘  event(QTouchEvent*)</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.03579;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.03579, 1.03579;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#Arrow5)"
+ d="M 480.13219,146.11428 434.01617,42.608119"
+ id="path2865"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="224.50352"
+ y="-380.62888"
+ id="text2981"
+ transform="rotate(65.571486)"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="224.50352"
+ y="-380.62888"
+ id="tspan2979">â‘¡ event(QTouchEvent*)</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4-5)"
+ d="m 442.97658,187.15842 c -50.02267,78.60672 40.30509,72.83709 29.70231,109.51513"
+ id="path9238-5"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="490.02765"
+ y="220.77097"
+ id="text9304-6"
+ transform="rotate(6.9725705)"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="490.02765"
+ y="220.77097"
+ id="tspan9302"> â‘¥ handlePointerEvent()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="490.02765"
+ y="231.72534"
+ id="tspan2397">⑦ wantsPointerEvent()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="490.02765"
+ y="242.6797"
+ id="tspan1147">⑬ onGrabChanged()</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="490.02765"
+ y="252.68085"
+ id="tspan2371" /></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="635.83441"
+ y="282.59363"
+ id="text2293"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="635.83441"
+ y="282.59363"
+ id="tspan2291">⑨ onActiveChanged()</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 621.84617,274.48471 c 33.3115,8.38416 -22.06557,12.65724 -23.12936,24.29582"
+ id="path2399"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="553.40424"
+ y="227.66728"
+ id="text2575"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="553.40424"
+ y="227.66728"
+ id="tspan2573">⑩ activeChanged Ⓢ</tspan><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="553.40424"
+ y="238.62166"
+ id="tspan2601">⑪ setColor()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text3431"
+ transform="rotate(-38.038218,476.61944,239.40311)"><textPath
+ xlink:href="#path9238-5"
+ id="textPath3609"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ id="tspan3429">QTouchEvent*</tspan></textPath></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text3832"
+ transform="rotate(58.431196)"
+ x="474.64865"
+ y="-273.09793"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ id="tspan3828">target</tspan></text>
+ <circle
+ id="path4083"
+ style="fill:#000000;stroke:#e6a11b;stroke-width:0.750085"
+ cx="536.95331"
+ cy="217.47479"
+ r="0.0092422031" />
+ <circle
+ id="path4109"
+ style="fill:#000000;stroke:#e6a11b;stroke-width:0.750085"
+ cx="550.58368"
+ cy="218.91232"
+ r="0.0092422031" />
+ <circle
+ id="path4135"
+ style="fill:#000000;stroke:#e6a11b;stroke-width:0.750085"
+ cx="584.84467"
+ cy="-85.462952"
+ r="0.0092422031" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#1c1be6;stroke-width:1.00011;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.00011, 1.00011;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker1576-4)"
+ d="m 588.30987,191.03752 c -32.55329,-6.31366 -64.31079,-1.46976 -66.079,-13.41654"
+ id="path1091"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;line-height:125%;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="592.49213"
+ y="194.76711"
+ id="text1121"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6pt;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1c1be6;fill-opacity:1;stroke:none;stroke-width:0.750085px"
+ x="592.49213"
+ y="194.76711"
+ id="tspan1119">⑫ grabChanged Ⓢ</tspan></text>
+</svg>
diff --git a/src/quick/doc/src/internal/pinchAndDragHandlers.dot b/src/quick/doc/src/internal/pinchAndDragHandlers.dot
new file mode 100644
index 0000000000..f1b5182a0d
--- /dev/null
+++ b/src/quick/doc/src/internal/pinchAndDragHandlers.dot
@@ -0,0 +1,30 @@
+digraph {
+ llfkmbbephlg[label="ResizeItemToWindow_QMLTYPE_1", shape="tab"]
+ llfkmbbephlg -> llfkmbbeijgg[arrowtail=odiamond, dir=back]
+
+ llfkmbbeijgg[label="â„šRootItem", shape="box"]
+
+ llfkmbbepmbg[label="â„šRectangle", shape="box"]
+ llfkmbbeijgg -> llfkmbbepmbg[arrowtail=odiamond, dir=back]
+
+ llfkmbodpifg[label="â„šPinchHandler", shape="house"]
+ llfkmbbepmbg -> llfkmbodpifg[arrowtail=odiamond, dir=back]
+ llfkmbnfping[label="â„šRectangle", shape="box"]
+ llfkmbbepmbg -> llfkmbnfping[arrowtail=odiamond, dir=back]
+
+ llfkmbbfjjgg[label="â„šDragHandler", shape="house"]
+ llfkmbnfping -> llfkmbbfjjgg[arrowtail=odiamond, dir=back]
+ llfkmbbdfibg[label="â„šRectangle", shape="box"]
+ llfkmbbepmbg -> llfkmbbdfibg[arrowtail=odiamond, dir=back]
+
+ llfkmbbfibdg[label="â„šDragHandler", shape="house"]
+ llfkmbbdfibg -> llfkmbbfibdg[arrowtail=odiamond, dir=back]
+ llfkmbodeang[label="â„šRectangle", shape="box"]
+ llfkmbbepmbg -> llfkmbodeang[arrowtail=odiamond, dir=back]
+
+ llfkmbcknglg[label="â„šDragHandler", shape="house"]
+ llfkmbodeang -> llfkmbcknglg[arrowtail=odiamond, dir=back]
+ llfkmbclplkg[label="â„šDeliveryAgent", shape="invhouse"]
+ llfkmbbeijgg -> llfkmbclplkg[arrowtail=odiamond, dir=back]
+
+}
diff --git a/src/quick/doc/src/internal/pinchAndDragHandlers.svg b/src/quick/doc/src/internal/pinchAndDragHandlers.svg
new file mode 100644
index 0000000000..674315d5d2
--- /dev/null
+++ b/src/quick/doc/src/internal/pinchAndDragHandlers.svg
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Pages: 1 -->
+<svg width="619pt" height="332pt"
+ viewBox="0.00 0.00 619.07 332.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 328)">
+<polygon fill="white" stroke="none" points="-4,4 -4,-328 615.07,-328 615.07,4 -4,4"/>
+<!-- llfkmbbephlg -->
+<g id="node1" class="node">
+<title>llfkmbbephlg</title>
+<polygon fill="none" stroke="black" points="474.53,-324 262.78,-324 262.78,-328 250.78,-328 250.78,-288 474.53,-288 474.53,-324"/>
+<polyline fill="none" stroke="black" points="250.78,-324 262.78,-324"/>
+<text text-anchor="middle" x="362.66" y="-300.95" font-family="Times,serif" font-size="9pt">ResizeItemToWindow_QMLTYPE_1</text>
+</g>
+<!-- llfkmbbeijgg -->
+<g id="node2" class="node">
+<title>llfkmbbeijgg</title>
+<polygon fill="none" stroke="black" points="402.53,-252 322.78,-252 322.78,-216 402.53,-216 402.53,-252"/>
+<text text-anchor="middle" x="362.66" y="-229.32" font-family="Times,serif" font-size="9pt">â„šRootItem</text>
+</g>
+<!-- llfkmbbephlg&#45;&gt;llfkmbbeijgg -->
+<g id="edge1" class="edge">
+<title>llfkmbbephlg&#45;&gt;llfkmbbeijgg</title>
+<path fill="none" stroke="black" d="M362.66,-274.81C362.66,-267.04 362.66,-258.95 362.66,-252.1"/>
+<polygon fill="none" stroke="black" points="362.66,-274.8 366.66,-280.8 362.66,-286.8 358.66,-280.8 362.66,-274.8"/>
+</g>
+<!-- llfkmbbepmbg -->
+<g id="node3" class="node">
+<title>llfkmbbepmbg</title>
+<polygon fill="none" stroke="black" points="327.66,-180 245.66,-180 245.66,-144 327.66,-144 327.66,-180"/>
+<text text-anchor="middle" x="286.66" y="-157.32" font-family="Times,serif" font-size="9pt">â„šRectangle</text>
+</g>
+<!-- llfkmbbeijgg&#45;&gt;llfkmbbepmbg -->
+<g id="edge2" class="edge">
+<title>llfkmbbeijgg&#45;&gt;llfkmbbepmbg</title>
+<path fill="none" stroke="black" d="M334.28,-206.87C324.67,-198.01 314.18,-188.35 305.49,-180.35"/>
+<polygon fill="none" stroke="black" points="334.38,-206.96 341.51,-208.08 343.21,-215.09 336.09,-213.96 334.38,-206.96"/>
+</g>
+<!-- llfkmbclplkg -->
+<g id="node11" class="node">
+<title>llfkmbclplkg</title>
+<polygon fill="none" stroke="black" points="345.8,-156.44 439.66,-144 533.52,-156.44 533.43,-176.56 345.89,-176.56 345.8,-156.44"/>
+<text text-anchor="middle" x="439.66" y="-157.32" font-family="Times,serif" font-size="9pt">â„šDeliveryAgent</text>
+</g>
+<!-- llfkmbbeijgg&#45;&gt;llfkmbclplkg -->
+<g id="edge10" class="edge">
+<title>llfkmbbeijgg&#45;&gt;llfkmbclplkg</title>
+<path fill="none" stroke="black" d="M391.18,-207.07C402.46,-196.82 414.97,-185.45 424.45,-176.82"/>
+<polygon fill="none" stroke="black" points="391.24,-207.02 389.49,-214.01 382.36,-215.09 384.11,-208.09 391.24,-207.02"/>
+</g>
+<!-- llfkmbodpifg -->
+<g id="node4" class="node">
+<title>llfkmbodpifg</title>
+<polygon fill="none" stroke="black" points="177.32,-95.56 88.66,-108 0,-95.56 0.08,-75.44 177.23,-75.44 177.32,-95.56"/>
+<text text-anchor="middle" x="88.66" y="-85.33" font-family="Times,serif" font-size="9pt">â„šPinchHandler</text>
+</g>
+<!-- llfkmbbepmbg&#45;&gt;llfkmbodpifg -->
+<g id="edge3" class="edge">
+<title>llfkmbbepmbg&#45;&gt;llfkmbodpifg</title>
+<path fill="none" stroke="black" d="M233.33,-142.15C198.64,-129.88 154.38,-114.23 124,-103.49"/>
+<polygon fill="none" stroke="black" points="233.47,-142.2 240.46,-140.42 244.78,-146.2 237.79,-147.97 233.47,-142.2"/>
+</g>
+<!-- llfkmbnfping -->
+<g id="node5" class="node">
+<title>llfkmbnfping</title>
+<polygon fill="none" stroke="black" points="277.66,-108 195.66,-108 195.66,-72 277.66,-72 277.66,-108"/>
+<text text-anchor="middle" x="236.66" y="-85.33" font-family="Times,serif" font-size="9pt">â„šRectangle</text>
+</g>
+<!-- llfkmbbepmbg&#45;&gt;llfkmbnfping -->
+<g id="edge4" class="edge">
+<title>llfkmbbepmbg&#45;&gt;llfkmbnfping</title>
+<path fill="none" stroke="black" d="M266.73,-133.1C260.69,-124.65 254.25,-115.62 248.88,-108.1"/>
+<polygon fill="none" stroke="black" points="266.8,-133.2 273.54,-135.76 273.78,-142.96 267.03,-140.41 266.8,-133.2"/>
+</g>
+<!-- llfkmbbdfibg -->
+<g id="node7" class="node">
+<title>llfkmbbdfibg</title>
+<polygon fill="none" stroke="black" points="377.66,-108 295.66,-108 295.66,-72 377.66,-72 377.66,-108"/>
+<text text-anchor="middle" x="336.66" y="-85.33" font-family="Times,serif" font-size="9pt">â„šRectangle</text>
+</g>
+<!-- llfkmbbepmbg&#45;&gt;llfkmbbdfibg -->
+<g id="edge6" class="edge">
+<title>llfkmbbepmbg&#45;&gt;llfkmbbdfibg</title>
+<path fill="none" stroke="black" d="M306.59,-133.1C312.62,-124.65 319.07,-115.62 324.44,-108.1"/>
+<polygon fill="none" stroke="black" points="306.52,-133.2 306.28,-140.41 299.54,-142.96 299.77,-135.76 306.52,-133.2"/>
+</g>
+<!-- llfkmbodeang -->
+<g id="node9" class="node">
+<title>llfkmbodeang</title>
+<polygon fill="none" stroke="black" points="522.66,-108 440.66,-108 440.66,-72 522.66,-72 522.66,-108"/>
+<text text-anchor="middle" x="481.66" y="-85.33" font-family="Times,serif" font-size="9pt">â„šRectangle</text>
+</g>
+<!-- llfkmbbepmbg&#45;&gt;llfkmbodeang -->
+<g id="edge8" class="edge">
+<title>llfkmbbepmbg&#45;&gt;llfkmbodeang</title>
+<path fill="none" stroke="black" d="M340.14,-141.8C371.75,-130.45 411.16,-116.31 440.36,-105.82"/>
+<polygon fill="none" stroke="black" points="340.16,-141.8 335.86,-147.59 328.86,-145.85 333.16,-140.06 340.16,-141.8"/>
+</g>
+<!-- llfkmbbfjjgg -->
+<g id="node6" class="node">
+<title>llfkmbbfjjgg</title>
+<polygon fill="none" stroke="black" points="233.07,-23.56 147.66,-36 62.25,-23.56 62.33,-3.44 232.99,-3.44 233.07,-23.56"/>
+<text text-anchor="middle" x="147.66" y="-13.33" font-family="Times,serif" font-size="9pt">â„šDragHandler</text>
+</g>
+<!-- llfkmbnfping&#45;&gt;llfkmbbfjjgg -->
+<g id="edge5" class="edge">
+<title>llfkmbnfping&#45;&gt;llfkmbbfjjgg</title>
+<path fill="none" stroke="black" d="M204.77,-63.92C191.91,-53.8 177.52,-42.48 166.4,-33.74"/>
+<polygon fill="none" stroke="black" points="204.52,-63.72 211.71,-64.29 213.95,-71.14 206.76,-70.57 204.52,-63.72"/>
+</g>
+<!-- llfkmbbfibdg -->
+<g id="node8" class="node">
+<title>llfkmbbfibdg</title>
+<polygon fill="none" stroke="black" points="422.07,-23.56 336.66,-36 251.25,-23.56 251.33,-3.44 421.99,-3.44 422.07,-23.56"/>
+<text text-anchor="middle" x="336.66" y="-13.33" font-family="Times,serif" font-size="9pt">â„šDragHandler</text>
+</g>
+<!-- llfkmbbdfibg&#45;&gt;llfkmbbfibdg -->
+<g id="edge7" class="edge">
+<title>llfkmbbdfibg&#45;&gt;llfkmbbfibdg</title>
+<path fill="none" stroke="black" d="M336.66,-58.81C336.66,-51.04 336.66,-42.95 336.66,-36.1"/>
+<polygon fill="none" stroke="black" points="336.66,-58.8 340.66,-64.8 336.66,-70.8 332.66,-64.8 336.66,-58.8"/>
+</g>
+<!-- llfkmbcknglg -->
+<g id="node10" class="node">
+<title>llfkmbcknglg</title>
+<polygon fill="none" stroke="black" points="611.07,-23.56 525.66,-36 440.25,-23.56 440.33,-3.44 610.99,-3.44 611.07,-23.56"/>
+<text text-anchor="middle" x="525.66" y="-13.33" font-family="Times,serif" font-size="9pt">â„šDragHandler</text>
+</g>
+<!-- llfkmbodeang&#45;&gt;llfkmbcknglg -->
+<g id="edge9" class="edge">
+<title>llfkmbodeang&#45;&gt;llfkmbcknglg</title>
+<path fill="none" stroke="black" d="M499.32,-60.9C504.94,-51.96 510.95,-42.4 515.81,-34.67"/>
+<polygon fill="none" stroke="black" points="499.4,-60.77 499.59,-67.98 493.01,-70.93 492.82,-63.72 499.4,-60.77"/>
+</g>
+</g>
+</svg>
diff --git a/src/quick/doc/src/internal/textEdit.dot b/src/quick/doc/src/internal/textEdit.dot
new file mode 100644
index 0000000000..19986bcc21
--- /dev/null
+++ b/src/quick/doc/src/internal/textEdit.dot
@@ -0,0 +1,52 @@
+digraph G {
+ node [
+ shape=record
+ ]
+
+ textedit[label="â„šTextEdit"]
+ texteditpriv[label="â„šTextEditPrivate"]
+ texteditextra[label="{â„šTextEditPrivate::ExtraData|padding...}"]
+ textctrl[label="â„šTextControl"]
+ qqpxm[label="â„šPixmap"]
+
+ subgraph cluster_1 {
+ label = "swappable document"
+ color=green
+
+ doc[label="QTextDocument"]
+ docpriv[label="QTextDocumentPrivate"]
+ doclayout[label="QTextDocumentLayout"]
+ qqtih[label="{â„šTextImageHandler||QSizeF intrinsicSize(doc, pos, format)}"]
+
+ doc -> docpriv [arrowtail=diamond, dir=back];
+ doc -> doclayout[arrowhead=vee, label="creates lazily", style="dotted", color="blue"]
+ }
+
+ subgraph cluster_2 {
+ label = "lazily constructed"
+ color=grey
+
+ qqdoc[label="{â„šTextDocument||textDocument()\nsetTextDocument()}"]
+ qqdocpriv[label="â„šTextDocumentPrivate"]
+
+ qqdoc -> qqdocpriv[arrowtail=diamond, dir=back]
+ }
+
+ textedit -> texteditpriv[arrowtail=diamond, dir=back]
+ texteditpriv -> texteditextra[arrowtail=diamond, dir=back]
+ texteditpriv -> doc[arrowtail=odiamond, dir=back]
+ texteditpriv -> qqdoc[arrowtail=odiamond, dir=back]
+ texteditpriv -> textctrl[arrowtail=diamond, dir=back]
+ texteditpriv -> qqpxm[arrowtail=diamond, dir=back, headlabel = "n", label="pixmapsInProgress"]
+ texteditpriv -> qqtih[arrowhead=vee, label="creates", style="dotted", color="blue"]
+ texteditpriv -> qqdoc[arrowhead=vee, label="creates", style="dotted", color="blue"]
+ texteditpriv -> doc[arrowhead=vee, label="creates", style="dotted", color="blue"]
+ textedit -> qqpxm[arrowhead=vee, label="creates", style="dotted", color="blue"]
+ qqtih -> doc[arrowhead=vee, label="parent", style="dashed"]
+ doc -> textedit[arrowhead=vee, label="parent", style="dashed"]
+ qqdoc -> textedit[arrowhead=vee, label="parent", style="dashed"]
+ doclayout -> doc[arrowhead=vee, label="parent", style="dashed"]
+ docpriv -> doclayout[arrowtail=diamond, dir=back]
+ doclayout -> qqtih[arrowtail=odiamond, dir=back, label="registered handler", style="dashed"] # storage is complex
+ qqdocpriv -> doc[arrowtail=odiamond, dir=back, taillabel="QPointer"]
+}
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index a2c913de49..e1ebca8106 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -17,11 +17,11 @@ import QtQuick
\endqml
Visit the \l {Qt Quick} module documentation for more
-information about the concepts which are central to \c QtQuick.
+information about the concepts that are central to \c QtQuick.
\section1 Submodules
- Qt Quick includes several submodules which contain additional types.
+ Qt Quick includes several submodules that contain additional types:
\list
\li \l{Qt Quick Local Storage QML Types}{Local Storage} - a submodule
@@ -33,6 +33,12 @@ information about the concepts which are central to \c QtQuick.
\li \l{Qt Quick Layouts QML Types}{Layouts} - contains types that are used
to arrange items in the user interface
\li \l{Qt Quick Test QML Types}{Tests} - types for testing QML applications.
+ \li \l{Qt Quick Effects QML Types}{Effects} - provides types for applying
+ one or more simple graphical effects to Qt Quick items.
+ \li \l{Qt Quick Shapes QML Types}{Shapes} - provides types for rendering vector shapes in
+ a Qt Quick scene.
+ \li \l{Qt Quick Vector Image QML Types}{Vector Image} - provides types for displaying vector
+ image files.
\endlist
\target value-types
@@ -151,7 +157,9 @@ available when you import \c QtQuick.
\li \l bool \c font.kerning
\li \l bool \c font.preferShaping
\li \l enumeration \c font.hintingPreference
+ \li \c object \l [QML] {QtQuick::Text::}{font.features}
\li \l string \c font.styleName
+ \li \c object \c [QML] {QtQuick::Text::}{font.variableAxes}
\endlist
Example:
@@ -165,71 +173,36 @@ available when you import \c QtQuick.
This value type is provided by the QtQuick import.
- Font weighting is classified on a scale from 0 to 99, where a weight of 0 is ultralight,
- and 99 is extremely black. The following values are supported:
+ Numerical values for font weights follow the CSS specification, where a
+ weight of 100 is extremely light, and 900 is extremely bold.
+ The following values are supported:
- \table
- \row
- \li \c Font.Thin
- \li 0
- \row
- \li \c Font.ExtraLight
- \li 12
- \row
- \li \c Font.Light
- \li 25
- \row
- \li \c Font.Normal
- \li 50
- \row
- \li \c Font.Medium
- \li 57
- \row
- \li \c Font.DemiBold
- \li 63
- \row
- \li \c Font.Bold
- \li 75
- \row
- \li \c Font.ExtraBold
- \li 81
- \row
- \li \c Font.Black
- \li 87
- \endtable
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
Capitalization supports the following values:
- \table
- \row
- \li \c Font.MixedCase
- \li No capitalization change is applied.
- \row
- \li \c Font.AllUppercase
- \li Alters the text to be rendered in all uppercase type.
- \row
- \li \c Font.AllLowercase
- \li Alters the text to be rendered in all lowercase type.
- \row
- \li \c Font.SmallCaps
- \li Alters the text to be rendered in small-caps type.
- \row
- \li \c Font.Capitalize
- \li Alters the text to be rendered with the first character of each word as an uppercase character.
- \endtable
+ \value Font.MixedCase No capitalization change is applied.
+ \value Font.AllUppercase Alters the text to be rendered in all uppercase type.
+ \value Font.AllLowercase Alters the text to be rendered in all lowercase type.
+ \value Font.SmallCaps Alters the text to be rendered in small-caps type.
+ \value Font.Capitalize Alters the text to be rendered with the first character of each word as an uppercase character.
Setting the hinting preference only has an effect when using the "NativeRendering" render type.
The property supports the following values:
- \list
- \value Font.PreferDefaultHinting - Use the default hinting level for the target platform.
- \value Font.PreferNoHinting - If possible, render text without hinting the outlines
- of the glyphs.
- \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
- but align glyphs to the pixel grid in the vertical direction.
- \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
- vertical directions.
- \endlist
+ \value Font.PreferDefaultHinting Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting If possible, render text without hinting the outlines of the glyphs.
+ \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction.
+ \value Font.PreferFullHinting If possible, render text with hinting in both horizontal and vertical directions.
\sa {QML Value Types}
*/
diff --git a/src/quick/doc/src/qtquick-android.qdoc b/src/quick/doc/src/qtquick-android.qdoc
new file mode 100644
index 0000000000..b21378565d
--- /dev/null
+++ b/src/quick/doc/src/qtquick-android.qdoc
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtquick-android-classes.html
+\title Qt Quick Android Classes
+\summary The Qt Quick module provides classes for embedding Qt Quick in Android Applications.
+
+ \note Classes under this module requires \l{Qt for Android}.
+
+ \annotatedlist qt_android_classes
+
+ For more information on Qt Quick module, see the \l{Qt Quick} module documentation.
+*/
diff --git a/src/quick/doc/src/qtquick-how-tos.qdoc b/src/quick/doc/src/qtquick-how-tos.qdoc
new file mode 100644
index 0000000000..c030b071e2
--- /dev/null
+++ b/src/quick/doc/src/qtquick-how-tos.qdoc
@@ -0,0 +1,109 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtquick-how-tos.html
+ \title Qt Quick How-tos
+
+ This page aims to provide an easily discoverable, useful reference that
+ shows the simplest and best way of performing specific tasks in Qt Quick.
+ Each solution provides QML and/or C++ code snippets where applicable, and
+ every snippet is automatically tested by Qt to ensure they remain
+ functional.
+
+ How do I:
+
+ \list
+ \li \l {Call a C++ function from QML when a Button is clicked}
+ \li \l {See which item has active focus}
+ \li \l {Create a time picker like Android's TimePickerDialog}
+ \li \l {Use a C++ enum in JavaScript}
+ \endlist
+
+
+ \section1 Call a C++ function from QML when a Button is clicked
+
+ Assuming that the C++ type should be globally available to the QML
+ files in the application, the simplest way is to make it a QML singleton
+ with \l QML_SINGLETON. For example, in the header file, \c backend.h:
+
+ \snippet how-tos/how-to-cpp-button/backend.h file
+
+ \c backend.cpp:
+
+ \snippet how-tos/how-to-cpp-button/backend.cpp file
+
+ You can then call that function from any QML file:
+
+ \snippet how-tos/how-to-cpp-button/Main.qml file
+
+ If the C++ type only needs to be available to a small set of QML files,
+ consider using \l QML_ELEMENT. For more ways of exposing C++ types to QML,
+ see \l {Choosing the Correct Integration Method Between C++ and QML}.
+
+ This example assumes that the \c Backend type is available in a QML module.
+ With CMake, this is done via \l qt_add_qml_module. For an example that
+ demonstrates this in detail, see \l {Building a QML application}.
+
+
+ \section1 See which item has active focus
+
+ Write a \l {Property change signal handlers}{property change signal handler}
+ for the window's \l {Window::}{activeFocusItem} property:
+
+ \snippet how-tos/how-to-qml/active-focus-debugging/ActiveFocusDebuggingMain.qml file
+
+ This will print the item which currently has active focus to the console.
+ To ensure that the output is useful, give each item a descriptive
+ \l {QtObject::}{objectName}.
+
+
+ \section1 Create a time picker like Android's TimePickerDialog
+
+ We've prepared an example that consists of a few
+ \l {https://code.qt.io/cgit/qt/qtdeclarative.git/tree/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimeComponentLabel.qml?h=\QtMajorVersion.\QtMinorVersion}
+ {QML files} which demonstrate how to do this. They can be used
+ in your application in the following manner:
+
+ \snippet how-tos/how-to-qml/time-picker/TimePickerMain.qml file
+
+ \table
+ \row
+ \li \image how-to-time-picker-light.png
+ \caption TimePickerDialog in its light theme.
+ \li \image how-to-time-picker-dark.png
+ \caption TimePickerDialog in its dark theme.
+ \endtable
+
+
+ \section1 Use a C++ enum in JavaScript
+
+ To expose a C++ enum to JavaScript (that is, \l QJSEngine, not
+ \l QQmlEngine or \l QQmlApplicationEngine), use
+ \l QJSEngine::newQMetaObject():
+
+ \quotefromfile how-tos/how-to-cpp-enum-js/tst_how-to-cpp-enum-js.cpp
+ \skipto QJSEngine engine
+ \printuntil setProperty
+ \skipto Backend backend
+ \printuntil backend.load()
+
+ The enum can then be used from JavaScript:
+
+ \snippet how-tos/how-to-cpp-enum-js/script.mjs file
+
+ When using \l QQmlEngine or \l QQmlApplicationEngine, there are easier
+ options; see
+ \l {Choosing the Correct Integration Method Between C++ and QML}
+ for more information.
+
+ \c backend.h:
+
+ \snippet how-tos/how-to-cpp-enum-js/backend.h file
+
+ \c backend.cpp:
+
+ \snippet how-tos/how-to-cpp-enum-js/backend.cpp file
+
+ For more information, see \l {QObject Integration}.
+*/
diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc
index 6797169312..31dd680d93 100644
--- a/src/quick/doc/src/qtquick.qdoc
+++ b/src/quick/doc/src/qtquick.qdoc
@@ -7,7 +7,7 @@
\brief The Qt Quick module implements the "standard library" for QML
The Qt Quick module is the standard library for writing QML applications.
-While the \l{Qt QML} module provides the QML engine and
+While the \l{Qt Qml} module provides the QML engine and
language infrastructure, the Qt Quick module provides all the basic
types necessary for creating user interfaces with QML. It
provides a visual canvas and includes types for creating and animating
@@ -61,8 +61,10 @@ shader effects.
{States, Transitions And Animations}
\li \l {Important Concepts In Qt Quick - Data - Models, Views and data Storage}
{Data - Models, Views and Data Storage}
- \li \l {Important Concepts In Qt Quick - Graphical Effects}{Particles And
- Graphical Effects}
+ \li \list \l {Important Concepts In Qt Quick - Graphical Effects}
+ {Particles And Graphical Effects}
+ \li \l MultiEffect
+ \endlist
\li \l {Important Concepts In Qt Quick - Convenience Types}
{Convenience Types}
\endlist
@@ -71,7 +73,7 @@ When using the Qt Quick module, you will need to know how to write QML
applications using the QML language. In particular, QML Basics and QML
Essentials from the \l{QML Applications} page.
-To find out more about using the QML language, see the \l{Qt QML} module documentation.
+To find out more about using the QML language, see the \l{Qt Qml} module documentation.
\section1 C++ Extension Points
@@ -99,9 +101,11 @@ To find out more about using the QML language, see the \l{Qt QML} module documen
\li \l {QML Applications}
- essential information for application development with QML and Qt
Quick
- \li \l {Qt QML}
+ \li \l {Qt Qml}
- documentation for the Qt QML module, which provides the QML engine
and language infrastructure
+ \li \l {Qt Quick How-tos}
+ - shows how to achieve specific tasks in Qt Quick
\endlist
\endlist
@@ -119,6 +123,8 @@ To find out more about using the QML language, see the \l{Qt QML} module documen
\li \l {Qt Quick QML Types}
- a list of QML types provided by the \c{QtQuick} import
\list
+ \li \l {Qt Quick Effects QML Types} {Effects}
+ - provides graphical effects to apply to Qt Quick items
\li \l {Qt Quick Local Storage QML Types} {Local Storage}
- a submodule containing a JavaScript interface for an SQLite
database
@@ -128,7 +134,13 @@ To find out more about using the QML language, see the \l{Qt QML} module documen
- provides layouts for arranging Qt Quick items
\li \l {Qt Quick Test QML Types} {Tests}
- contains types for writing unit test for a QML application
+ \li \l{Qt Quick Shapes QML Types}{Shapes}
+ - provides types for rendering vector shapes in a Qt Quick scene.
+ \li \l{Qt Quick Vector Image QML Types}{Vector Image} - provides types for displaying
+ vector image files.
\endlist
+ \li \l {Qt Quick Android Classes}
+ - provides classes for using QML with Java/Kotlin Android APIs.
\endlist
\section1 Licenses and Attributions
diff --git a/src/quick/handlers/qquickdragaxis_p.h b/src/quick/handlers/qquickdragaxis_p.h
index 5d0feba908..d9aecf9d9d 100644
--- a/src/quick/handlers/qquickdragaxis_p.h
+++ b/src/quick/handlers/qquickdragaxis_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickPointerHandler;
-class Q_QUICK_PRIVATE_EXPORT QQuickDragAxis : public QObject
+class Q_QUICK_EXPORT QQuickDragAxis : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 3d807e0f2d..5e9d6d8230 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -52,7 +52,7 @@ Q_LOGGING_CATEGORY(lcDragHandler, "qt.quick.handler.drag")
At this time, drag-and-drop is not yet supported.
- \sa Drag, MouseArea
+ \sa Drag, MouseArea, {Qt Quick Examples - Pointer Handlers}
*/
QQuickDragHandler::QQuickDragHandler(QQuickItem *parent)
@@ -95,13 +95,13 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDe
This property holds the snap mode.
- The snap mode configures snapping of the \l target item's center to the event point.
+ The snap mode configures snapping of the \l target item's center to the \l eventPoint.
Possible values:
\value DragHandler.SnapNever Never snap
- \value DragHandler.SnapAuto The \l target snaps if the event point was pressed outside of the \l target
+ \value DragHandler.SnapAuto The \l target snaps if the \l eventPoint was pressed outside of the \l target
item \e and the \l target is a descendant of \l {PointerHandler::}{parent} item (default)
- \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the event point was pressed outside of the \l target
+ \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the \l eventPoint was pressed outside of the \l target
\value DragHandler.SnapAlways Always snap
*/
QQuickDragHandler::SnapMode QQuickDragHandler::snapMode() const
@@ -184,7 +184,9 @@ void QQuickDragHandler::handlePointerEventImpl(QPointerEvent *event)
// and in approximately the same direction
qreal minAngle = 361;
qreal maxAngle = -361;
- bool allOverThreshold = !event->isEndEvent();
+ bool allOverThreshold = QQuickDeliveryAgentPrivate::isTouchEvent(event) ?
+ static_cast<QTouchEvent *>(event)->touchPointStates() != QEventPoint::Released :
+ !event->isEndEvent();
QVector<QEventPoint> chosenPoints;
if (event->isBeginEvent())
@@ -264,17 +266,6 @@ void QQuickDragHandler::handlePointerEventImpl(QPointerEvent *event)
}
}
-void QQuickDragHandler::enforceConstraints()
-{
- if (!target() || !target()->parentItem())
- return;
- QPointF pos = target()->position();
- QPointF copy(pos);
- enforceAxisConstraints(&pos);
- if (pos != copy)
- target()->setPosition(pos);
-}
-
void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos)
{
if (m_xAxis.enabled())
diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h
index f67e0ccf72..e8f2ef5e1d 100644
--- a/src/quick/handlers/qquickdraghandler_p.h
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-class Q_QUICK_PRIVATE_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
+class Q_QUICK_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
{
Q_OBJECT
Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
@@ -62,8 +62,6 @@ public:
QQuickDragHandler::SnapMode snapMode() const;
void setSnapMode(QQuickDragHandler::SnapMode mode);
- void enforceConstraints();
-
Q_SIGNALS:
void translationChanged(QVector2D delta);
Q_REVISION(2, 14) void snapModeChanged();
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
index cb15dbccd6..cbbb14fd8c 100644
--- a/src/quick/handlers/qquickhandlerpoint.cpp
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -9,12 +9,12 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcTouchTarget)
/*!
- \qmltype HandlerPoint
+ \qmltype handlerPoint
\instantiates QQuickHandlerPoint
\inqmlmodule QtQuick
\brief An event point.
- A QML representation of a QEventPoint.
+ A handler-owned QML representation of a QEventPoint.
It's possible to make bindings to properties of a handler's current
\l {SinglePointHandler::point}{point} or
@@ -23,15 +23,15 @@ Q_DECLARE_LOGGING_CATEGORY(lcTouchTarget)
\snippet pointerHandlers/dragHandlerNullTarget.qml 0
The point is kept up-to-date when the DragHandler is actively responding to
- an EventPoint; but after the point is released, or when the current point is
+ an \l eventPoint; but after the point is released, or when the current point is
being handled by a different handler, \c position.x and \c position.y are 0.
- \note This is practically identical to QtQuick::EventPoint; however an
- EventPoint is a long-lived QObject which is invalidated between gestures
- and reused for subsequent event deliveries. Continuous bindings to its
+ \note This is practically identical to \l eventPoint; however an eventPoint
+ is a short-lived copy of a long-lived Q_GADGET which is invalidated between
+ gestures and reused for subsequent event deliveries. Continuous bindings to its
properties are not possible, and an individual handler cannot rely on it
outside the period when that point is part of an active gesture which that
- handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
+ handler is handling. handlerPoint is a Q_GADGET that the handler owns.
This allows you to make lifetime bindings to its properties.
\sa SinglePointHandler::point, MultiPointHandler::centroid
@@ -145,13 +145,13 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty int QtQuick::HandlerPoint::id
+ \qmlproperty int QtQuick::handlerPoint::id
\brief The ID number of the point
During a touch gesture, from the time that the first finger is pressed
until the last finger is released, each touchpoint will have a unique ID
number. Likewise, if input from multiple devices occurs (for example
- simultaneous mouse and touch presses), all the current event points from
+ simultaneous mouse and touch presses), all the current \l{eventPoint}{eventPoints} from
all the devices will have unique IDs.
\note Do not assume that id numbers start at zero or that they are
@@ -163,7 +163,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty pointingDeviceUniqueId QtQuick::HandlerPoint::uniqueId
+ \qmlproperty pointingDeviceUniqueId QtQuick::handlerPoint::uniqueId
\brief The unique ID of the point, if any
This is normally empty, because touchscreens cannot uniquely identify fingers.
@@ -185,25 +185,25 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::position
+ \qmlproperty QPointF QtQuick::handlerPoint::position
\brief The position within the \c parent Item
- This is the position of the event point relative to the bounds of
+ This is the position of the \l eventPoint relative to the bounds of
the \l {PointerHandler::parent} {parent}.
*/
/*!
\readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::scenePosition
+ \qmlproperty QPointF QtQuick::handlerPoint::scenePosition
\brief The position within the scene
- This is the position of the event point relative to the bounds of the Qt
+ This is the position of the \l eventPoint relative to the bounds of the Qt
Quick scene (typically the whole window).
*/
/*!
\readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::pressPosition
+ \qmlproperty QPointF QtQuick::handlerPoint::pressPosition
\brief The pressed position within the \c parent Item
This is the position at which this point was pressed, relative to the
@@ -212,7 +212,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::scenePressPosition
+ \qmlproperty QPointF QtQuick::handlerPoint::scenePressPosition
\brief The pressed position within the scene
This is the position at which this point was pressed, in the coordinate
@@ -221,7 +221,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::sceneGrabPosition
+ \qmlproperty QPointF QtQuick::handlerPoint::sceneGrabPosition
\brief The grabbed position within the scene
If this point has been grabbed by a Pointer Handler or an Item, it means
@@ -232,7 +232,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty enumeration QtQuick::HandlerPoint::pressedButtons
+ \qmlproperty enumeration QtQuick::handlerPoint::pressedButtons
\brief Which mouse or stylus buttons are currently pressed
\sa MouseArea::pressedButtons
@@ -240,7 +240,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty enumeration QtQuick::HandlerPoint::modifiers
+ \qmlproperty enumeration QtQuick::handlerPoint::modifiers
\brief Which modifier keys are currently pressed
This property holds the keyboard modifiers that were pressed at the time
@@ -249,20 +249,20 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty QVector2D QtQuick::HandlerPoint::velocity
+ \qmlproperty QVector2D QtQuick::handlerPoint::velocity
\brief A vector representing the average speed and direction of movement
This is a velocity vector pointing in the direction of movement, in logical
pixels per second. It has x and y components, at least one of which will be
nonzero when this point is in motion. It holds the average recent velocity:
- how fast and in which direction the event point has been moving recently.
+ how fast and in which direction the \l eventPoint has been moving recently.
\sa QtQuick::TouchPoint::velocity, QEventPoint::velocity
*/
/*!
\readonly
- \qmlproperty qreal QtQuick::HandlerPoint::rotation
+ \qmlproperty qreal QtQuick::handlerPoint::rotation
This property holds the rotation angle of the stylus on a graphics tablet
or the contact patch of a touchpoint on a touchscreen.
@@ -273,7 +273,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty qreal QtQuick::HandlerPoint::pressure
+ \qmlproperty qreal QtQuick::handlerPoint::pressure
This property tells how hard the user is pressing the stylus on a graphics
tablet or the finger against a touchscreen, in the range from \c 0 (no
@@ -286,7 +286,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
/*!
\readonly
- \qmlproperty size QtQuick::HandlerPoint::ellipseDiameters
+ \qmlproperty size QtQuick::handlerPoint::ellipseDiameters
This property holds the diameters of the contact patch, if the event
comes from a touchpoint and the device provides this information.
@@ -310,6 +310,13 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
\sa QtQuick::TouchPoint::ellipseDiameters, QEventPoint::ellipseDiameters
*/
+/*!
+ \readonly
+ \qmlproperty PointerDevice QtQuick::handlerPoint::device
+
+ This property holds the device that the point (and its event) came from.
+*/
+
QT_END_NAMESPACE
#include "moc_qquickhandlerpoint_p.cpp"
diff --git a/src/quick/handlers/qquickhandlerpoint_p.h b/src/quick/handlers/qquickhandlerpoint_p.h
index 9e2b873e70..1654406d86 100644
--- a/src/quick/handlers/qquickhandlerpoint_p.h
+++ b/src/quick/handlers/qquickhandlerpoint_p.h
@@ -22,22 +22,22 @@ QT_BEGIN_NAMESPACE
class QQuickMultiPointHandler;
class QQuickSinglePointHandler;
-class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint {
+class Q_QUICK_EXPORT QQuickHandlerPoint {
Q_GADGET
- Q_PROPERTY(int id READ id)
- Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId)
- Q_PROPERTY(QPointF position READ position)
- Q_PROPERTY(QPointF scenePosition READ scenePosition)
- Q_PROPERTY(QPointF pressPosition READ pressPosition)
- Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition)
- Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition)
- Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons)
- Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers)
- Q_PROPERTY(QVector2D velocity READ velocity)
- Q_PROPERTY(qreal rotation READ rotation)
- Q_PROPERTY(qreal pressure READ pressure)
- Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters)
- Q_PROPERTY(QPointingDevice *device READ device)
+ Q_PROPERTY(int id READ id FINAL)
+ Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId FINAL)
+ Q_PROPERTY(QPointF position READ position FINAL)
+ Q_PROPERTY(QPointF scenePosition READ scenePosition FINAL)
+ Q_PROPERTY(QPointF pressPosition READ pressPosition FINAL)
+ Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition FINAL)
+ Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition FINAL)
+ Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons FINAL)
+ Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers FINAL)
+ Q_PROPERTY(QVector2D velocity READ velocity FINAL)
+ Q_PROPERTY(qreal rotation READ rotation FINAL)
+ Q_PROPERTY(qreal pressure READ pressure FINAL)
+ Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters FINAL)
+ Q_PROPERTY(QPointingDevice *device READ device FINAL)
QML_ANONYMOUS
public:
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index 16e5ef5ef3..5bf53235c5 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -33,7 +33,7 @@ Q_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
The \l cursorShape property allows changing the cursor whenever
\l hovered changes to \c true.
- \sa MouseArea, PointHandler
+ \sa MouseArea, PointHandler, {Qt Quick Examples - Pointer Handlers}
*/
class QQuickHoverHandlerPrivate : public QQuickSinglePointHandlerPrivate
@@ -145,7 +145,8 @@ bool QQuickHoverHandler::wantsPointerEvent(QPointerEvent *event)
if (event->isSinglePointEvent() && static_cast<QSinglePointEvent *>(event)->button())
return false;
auto &point = event->point(0);
- if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(event, point) && parentContains(point)) {
+ const bool inside = parentContains(point);
+ if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(event, point) && inside) {
// assume this is a mouse or tablet event, so there's only one point
setPointId(point.id());
return true;
@@ -162,7 +163,7 @@ bool QQuickHoverHandler::wantsPointerEvent(QPointerEvent *event)
// But after kCursorOverrideTimeout ms, QQuickItemPrivate::effectiveCursorHandler()
// will ignore it, just in case there is no QQuickPointerTabletEvent to unset it.
// For example, a tablet proximity leave event could occur, but we don't deliver it to the window.
- if (!(m_hoveredTablet && QQuickDeliveryAgentPrivate::isMouseEvent(event)))
+ if (!inside || !(m_hoveredTablet && QQuickDeliveryAgentPrivate::isMouseEvent(event)))
setHovered(false);
return false;
@@ -230,6 +231,9 @@ void QQuickHoverHandler::setHovered(bool hovered)
\value PointerDevice.Puck A digitizer with crosshairs, on a graphics tablet.
\value PointerDevice.AllDevices Any type of pointing device.
+ \note Not all platforms are yet able to distinguish mouse and touchpad; and
+ on those that do, you often want to make mouse and touchpad behavior the same.
+
\sa QInputDevice::DeviceType
*/
@@ -338,6 +342,13 @@ void QQuickHoverHandler::setHovered(bool hovered)
\sa Qt::CursorShape, QQuickItem::cursor()
*/
+/*!
+ \internal
+ \qmlproperty flags HoverHandler::dragThreshold
+
+ This property is not used in HoverHandler.
+*/
+
QT_END_NAMESPACE
#include "moc_qquickhoverhandler_p.cpp"
diff --git a/src/quick/handlers/qquickhoverhandler_p.h b/src/quick/handlers/qquickhoverhandler_p.h
index 45caa1189b..a83e1fba86 100644
--- a/src/quick/handlers/qquickhoverhandler_p.h
+++ b/src/quick/handlers/qquickhoverhandler_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickHoverHandlerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler
+class Q_QUICK_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged)
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index 1ef76148c5..c50de437cd 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -229,7 +229,7 @@ void QQuickMultiPointHandler::setMaximumPointCount(int maximumPointCount)
/*!
\readonly
- \qmlproperty QtQuick::HandlerPoint QtQuick::MultiPointHandler::centroid
+ \qmlproperty QtQuick::handlerPoint QtQuick::MultiPointHandler::centroid
A point exactly in the middle of the currently-pressed touch points.
If only one point is pressed, it's the same as that point.
diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h
index d15fad997d..8d60984d12 100644
--- a/src/quick/handlers/qquickmultipointhandler_p.h
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickMultiPointHandlerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
+class Q_QUICK_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
{
Q_OBJECT
Q_PROPERTY(int minimumPointCount READ minimumPointCount WRITE setMinimumPointCount NOTIFY minimumPointCountChanged)
diff --git a/src/quick/handlers/qquickmultipointhandler_p_p.h b/src/quick/handlers/qquickmultipointhandler_p_p.h
index 077b3dac55..c9c140e387 100644
--- a/src/quick/handlers/qquickmultipointhandler_p_p.h
+++ b/src/quick/handlers/qquickmultipointhandler_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandlerPrivate : public QQuickPointerDeviceHandlerPrivate
+class Q_QUICK_EXPORT QQuickMultiPointHandlerPrivate : public QQuickPointerDeviceHandlerPrivate
{
Q_DECLARE_PUBLIC(QQuickMultiPointHandler)
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index 94b64c357d..8de7390ae4 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -56,7 +56,7 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
but if it's a disallowed number, it does not scale or rotate
its \l target, and the \l active property remains \c false.
- \sa PinchArea, QPointerEvent::pointCount(), QNativeGestureEvent::fingerCount()
+ \sa PinchArea, QPointerEvent::pointCount(), QNativeGestureEvent::fingerCount(), {Qt Quick Examples - Pointer Handlers}
*/
QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent)
@@ -146,7 +146,6 @@ void QQuickPinchHandler::setActiveScale(qreal scale)
*/
/*!
- \readonly
\qmlproperty real QtQuick::PinchHandler::persistentScale
The scale factor that will automatically be set on the \l target if it is not null.
@@ -161,12 +160,12 @@ void QQuickPinchHandler::setActiveScale(qreal scale)
and \c scaleChanged(1) is emitted.
*/
-void QQuickPinchHandler::setPersistentScale(qreal rot)
+void QQuickPinchHandler::setPersistentScale(qreal scale)
{
- if (rot == persistentScale())
+ if (scale == persistentScale())
return;
- m_scaleAxis.updateValue(m_scaleAxis.activeValue(), rot);
+ m_scaleAxis.updateValue(m_scaleAxis.activeValue(), scale);
emit scaleChanged(1);
}
@@ -248,7 +247,6 @@ void QQuickPinchHandler::setActiveRotation(qreal rot)
}
/*!
- \readonly
\qmlproperty real QtQuick::PinchHandler::persistentRotation
The rotation to be applied to the \l target if it is not null.
@@ -297,7 +295,7 @@ void QQuickPinchHandler::setPersistentRotation(qreal rot)
The translation of the cluster of points while the pinch gesture is being
performed. It is \c {0, 0} when the gesture begins, and increases as the
- event point(s) are dragged downward and to the right. After the gesture
+ \l {eventPoint}{eventPoint(s)} are dragged downward and to the right. After the gesture
ends, it stays the same; and when the next pinch gesture begins, it is
reset to \c {0, 0} again.
@@ -478,12 +476,6 @@ void QQuickPinchHandler::onActiveChanged()
{
QQuickMultiPointHandler::onActiveChanged();
const bool curActive = active();
- if (const QQuickItem *t = target(); curActive && t) {
- m_xAxis.m_accumulatedValue = t->position().x();
- m_yAxis.m_accumulatedValue = t->position().y();
- m_scaleAxis.m_accumulatedValue = t->scale();
- m_rotationAxis.m_accumulatedValue = t->rotation();
- }
m_xAxis.onActiveChanged(curActive, 0);
m_yAxis.onActiveChanged(curActive, 0);
m_scaleAxis.onActiveChanged(curActive, 1);
@@ -492,9 +484,12 @@ void QQuickPinchHandler::onActiveChanged()
if (curActive) {
m_startAngles = angles(centroid().sceneGrabPosition());
m_startDistance = averageTouchPointDistance(centroid().sceneGrabPosition());
+ m_startTargetPos = target() ? target()->position() : QPointF();
qCDebug(lcPinchHandler) << "activated with starting scale" << m_scaleAxis.m_startValue
- << "target scale" << m_scaleAxis.m_startValue << "rotation" << m_rotationAxis.m_startValue;
+ << "rotation" << m_rotationAxis.m_startValue
+ << "target pos" << m_startTargetPos;
} else {
+ m_startTargetPos = QPointF();
qCDebug(lcPinchHandler) << "deactivated with scale" << m_scaleAxis.m_activeValue << "rotation" << m_rotationAxis.m_activeValue;
}
}
@@ -524,7 +519,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
emit updated();
return;
case Qt::ZoomNativeGesture:
- setActiveScale(1 + gesture->value());
+ setActiveScale(m_scaleAxis.activeValue() * (1 + gesture->value()));
break;
case Qt::RotateNativeGesture:
setActiveRotation(m_rotationAxis.activeValue() + gesture->value());
@@ -677,17 +672,16 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
if (target() && target()->parentItem()) {
- const QPointF centroidParentPos = target()->parentItem()->mapFromScene(centroid().scenePosition());
+ auto *t = target();
+ const QPointF centroidParentPos = t->parentItem()->mapFromScene(centroid().scenePosition());
// 3. Drag/translate
- const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(centroid().sceneGrabPosition());
+ const QPointF centroidStartParentPos = t->parentItem()->mapFromScene(centroid().sceneGrabPosition());
auto activeTranslation = centroidParentPos - centroidStartParentPos;
// apply rotation + scaling around the centroid - then apply translation.
- QPointF pos = QQuickItemPrivate::get(target())->adjustedPosForTransform(centroidParentPos,
- startPos(), QVector2D(activeTranslation),
- m_scaleAxis.m_startValue,
- m_scaleAxis.persistentValue() / m_scaleAxis.m_startValue,
- m_rotationAxis.m_startValue,
- m_rotationAxis.persistentValue() - m_rotationAxis.m_startValue);
+ QPointF pos = QQuickItemPrivate::get(t)->adjustedPosForTransform(centroidParentPos,
+ m_startTargetPos, QVector2D(activeTranslation),
+ t->scale(), m_scaleAxis.persistentValue() / m_scaleAxis.m_startValue,
+ t->rotation(), m_rotationAxis.persistentValue() - m_rotationAxis.m_startValue);
if (xAxis()->enabled())
pos.setX(qBound(xAxis()->minimum(), pos.x(), xAxis()->maximum()));
@@ -700,12 +694,12 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
const QVector2D delta(activeTranslation.x() - m_xAxis.activeValue(),
activeTranslation.y() - m_yAxis.activeValue());
- m_xAxis.updateValue(activeTranslation.x(), pos.x(), delta.x());
- m_yAxis.updateValue(activeTranslation.y(), pos.y(), delta.y());
+ m_xAxis.updateValue(activeTranslation.x(), m_xAxis.persistentValue() + delta.x(), delta.x());
+ m_yAxis.updateValue(activeTranslation.y(), m_yAxis.persistentValue() + delta.y(), delta.y());
emit translationChanged(delta);
- target()->setPosition(QPointF(m_xAxis.persistentValue(), m_yAxis.persistentValue()));
- target()->setRotation(m_rotationAxis.persistentValue());
- target()->setScale(m_scaleAxis.persistentValue());
+ t->setPosition(pos);
+ t->setRotation(m_rotationAxis.persistentValue());
+ t->setScale(m_scaleAxis.persistentValue());
} else {
auto activeTranslation = centroid().scenePosition() - centroid().scenePressPosition();
auto accumulated = QPointF(m_xAxis.m_startValue, m_yAxis.m_startValue) + activeTranslation;
@@ -726,14 +720,16 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
emit updated();
}
-QPointF QQuickPinchHandler::startPos()
-{
- return {m_xAxis.m_startValue, m_yAxis.m_startValue};
-}
+/*!
+ \internal
+ \qmlproperty flags QtQuick::PinchHandler::acceptedButtons
+
+ This property is not used in PinchHandler.
+*/
/*!
\readonly
- \qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid
+ \qmlproperty QtQuick::handlerPoint QtQuick::PinchHandler::centroid
A point exactly in the middle of the currently-pressed touch points.
The \l target will be rotated around this point.
diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h
index d86b96c932..114ae9642a 100644
--- a/src/quick/handlers/qquickpinchhandler_p.h
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-class Q_QUICK_PRIVATE_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
+class Q_QUICK_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
{
Q_OBJECT
@@ -103,17 +103,15 @@ Q_SIGNALS:
void minimumRotationChanged();
void maximumRotationChanged();
void updated();
- Q_REVISION(6, 5) void scaleChanged(qreal delta);
- Q_REVISION(6, 5) void rotationChanged(qreal delta);
- Q_REVISION(6, 5) void translationChanged(QVector2D delta);
+ void scaleChanged(qreal delta);
+ void rotationChanged(qreal delta);
+ void translationChanged(QVector2D delta);
protected:
bool wantsPointerEvent(QPointerEvent *event) override;
void onActiveChanged() override;
void handlePointerEventImpl(QPointerEvent *event) override;
- QPointF startPos();
-
private:
QQuickDragAxis m_xAxis = {this, u"x"_s};
QQuickDragAxis m_yAxis = {this, u"y"_s};
@@ -123,6 +121,7 @@ private:
// internal
qreal m_startDistance = 0;
qreal m_accumulatedStartCentroidDistance = 0;
+ QPointF m_startTargetPos;
QVector<PointData> m_startAngles;
QQuickMatrix4x4 m_transform;
};
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index b610e083cc..a6b98ec2b5 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -110,7 +110,7 @@ Qt::KeyboardModifiers QQuickPointerDeviceHandler::acceptedModifiers() const
\qml
Item {
TapHandler {
- acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
onTapped: console.log("clicked")
}
TapHandler {
@@ -119,6 +119,9 @@ Qt::KeyboardModifiers QQuickPointerDeviceHandler::acceptedModifiers() const
}
}
\endqml
+
+ \note Not all platforms are yet able to distinguish mouse and touchpad; and
+ on those that do, you often want to make mouse and touchpad behavior the same.
*/
void QQuickPointerDeviceHandler::setAcceptedDevices(QPointingDevice::DeviceTypes acceptedDevices)
{
@@ -139,7 +142,7 @@ void QQuickPointerDeviceHandler::setAcceptedDevices(QPointingDevice::DeviceTypes
By default, this property is set to
\l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
If you set it to an OR combination of device types, it will ignore events
- from non-matching events.
+ from non-matching \l {PointerDevice}{devices}.
For example, a control could be made to respond to mouse, touch, and stylus clicks
in some way, but delete itself if tapped with an eraser tool on a graphics tablet,
@@ -149,7 +152,7 @@ void QQuickPointerDeviceHandler::setAcceptedDevices(QPointingDevice::DeviceTypes
Rectangle {
id: rect
TapHandler {
- acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Finger | PointerDevice.Pen
+ acceptedPointerTypes: PointerDevice.Generic | PointerDevice.Finger | PointerDevice.Pen
onTapped: console.log("clicked")
}
TapHandler {
@@ -270,7 +273,7 @@ bool QQuickPointerDeviceHandler::wantsPointerEvent(QPointerEvent *event)
if (d->acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != d->acceptedModifiers)
return false;
// Some handlers (HoverHandler, PinchHandler) set acceptedButtons to Qt::NoButton to indicate that button state is irrelevant.
- if (event->pointingDevice()->pointerType() != QPointingDevice::PointerType::Finger &&
+ if (event->pointingDevice()->type() != QPointingDevice::DeviceType::TouchScreen &&
acceptedButtons() != Qt::NoButton && event->type() != QEvent::Wheel &&
(static_cast<QSinglePointEvent *>(event)->buttons() & acceptedButtons()) == 0 &&
(static_cast<QSinglePointEvent *>(event)->button() & acceptedButtons()) == 0)
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h
index be7bda2c0a..50f6e1d7e0 100644
--- a/src/quick/handlers/qquickpointerdevicehandler_p.h
+++ b/src/quick/handlers/qquickpointerdevicehandler_p.h
@@ -20,7 +20,7 @@ QT_BEGIN_NAMESPACE
class QQuickPointerDeviceHandlerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
+class Q_QUICK_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
{
Q_OBJECT
Q_PROPERTY(QInputDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p_p.h b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
index 809f51656a..ac90005199 100644
--- a/src/quick/handlers/qquickpointerdevicehandler_p_p.h
+++ b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
+class Q_QUICK_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
{
Q_DECLARE_PUBLIC(QQuickPointerDeviceHandler)
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index 3a15eb4d41..a29c8ae2d3 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -8,6 +8,8 @@
#include <QtQuick/private/qquickdeliveryagent_p_p.h>
#include <QtGui/private/qinputdevice_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch")
@@ -27,6 +29,19 @@ Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent)
events from any kind of pointing device (touch, mouse or graphics tablet).
*/
+/*! \internal
+ So far we only offer public QML API for Pointer Handlers, but we expect
+ in some future version of Qt to have public C++ API as well. This will open
+ up the possibility to instantiate handlers in custom items (which we should
+ begin doing in Qt Quick Controls in the near future), and to subclass to make
+ custom handlers (as TableView is already doing).
+
+ To make a custom Pointer Handler, first try to choose the parent class
+ according to your needs. If the gesture that you want to recognize could
+ involve multiple touchpoints (even if it could start with only one point),
+ subclass QQuickMultiPointHandler. If you are sure that you never want to
+ handle more than one QEventPoint, subclass QQuickSinglePointHandler.
+*/
QQuickPointerHandler::QQuickPointerHandler(QQuickItem *parent)
: QQuickPointerHandler(*(new QQuickPointerHandlerPrivate), parent)
{
@@ -57,7 +72,7 @@ QQuickPointerHandler::~QQuickPointerHandler()
\qmlproperty real PointerHandler::margin
The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
- item within which an event point can activate this handler. For example, on
+ item within which an \l eventPoint can activate this handler. For example, on
a PinchHandler where the \l {PointerHandler::target}{target} is also the
\c parent, it's useful to set this to a distance at least half the width
of a typical user's finger, so that if the \c parent has been scaled down
@@ -90,7 +105,7 @@ void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
\qmlproperty int PointerHandler::dragThreshold
\since 5.15
- The distance in pixels that the user must drag an event point in order to
+ The distance in pixels that the user must drag an \l eventPoint in order to
have it treated as a drag gesture.
The default value depends on the platform and screen resolution.
@@ -182,11 +197,13 @@ void QQuickPointerHandler::setCursorShape(Qt::CursorShape shape)
return;
d->cursorShape = shape;
d->cursorSet = true;
+ d->cursorDirty = true;
if (auto *parent = parentItem()) {
QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parent);
itemPriv->hasCursorHandler = true;
itemPriv->setHasCursorInChild(true);
}
+
emit cursorShapeChanged();
}
@@ -217,8 +234,8 @@ bool QQuickPointerHandler::isCursorShapeExplicitlySet() const
The \a grabber (subject) will be the Input Handler whose state is changing,
or null if the state change regards an Item.
The \a transition (verb) tells what happened.
- The \a point (object) is the point that was grabbed or ungrabbed.
- EventPoint has the sole responsibility to call this function.
+ The \a point (object) is the \l eventPoint that was grabbed or ungrabbed.
+ QQuickDeliveryAgent calls this function.
The Input Handler must react in whatever way is appropriate, and must
emit the relevant signals (for the benefit of QML code).
A subclass is allowed to override this virtual function, but must always
@@ -414,7 +431,7 @@ bool QQuickPointerHandler::approveGrabTransition(QPointerEvent *event, const QEv
\value PointerHandler.ApprovesCancellation
This handler will allow its grab to be set to null.
\value PointerHandler.ApprovesTakeOverByAnything
- This handler gives permission for any any type of Item or Handler to take the grab.
+ This handler gives permission for any type of Item or Handler to take the grab.
The default is
\c {PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything}
@@ -437,10 +454,17 @@ void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission)
emit grabPermissionChanged();
}
+/*!
+ Overridden only because QQmlParserStatus requires it.
+*/
void QQuickPointerHandler::classBegin()
{
}
+/*!
+ Overridden from QQmlParserStatus to ensure that parentItem() sets its
+ cursor if this handler's \l cursorShape property has been set.
+*/
void QQuickPointerHandler::componentComplete()
{
Q_D(const QQuickPointerHandler);
@@ -453,6 +477,12 @@ void QQuickPointerHandler::componentComplete()
}
}
+/*! \internal
+ \deprecated You should handle the event during delivery by overriding
+ handlePointerEventImpl() or QQuickSinglePointHandler::handleEventPoint().
+ Therefore currentEvent() should not be needed. It is here only because
+ onActiveChanged() does not take the event as an argument.
+*/
QPointerEvent *QQuickPointerHandler::currentEvent()
{
Q_D(const QQuickPointerHandler);
@@ -507,19 +537,33 @@ QPointF QQuickPointerHandler::eventPos(const QEventPoint &point) const
return (target() ? target()->mapFromScene(point.scenePosition()) : point.scenePosition());
}
+/*!
+ Returns \c true if margin() > 0 and \a point is within the margin beyond
+ QQuickItem::boundingRect(), or else returns QQuickItem::contains()
+ QEventPoint::position() effectively (because parentContains(scenePosition)
+ calls QQuickItem::mapFromScene()).
+*/
bool QQuickPointerHandler::parentContains(const QEventPoint &point) const
{
return parentContains(point.scenePosition());
}
+/*!
+ Returns \c true if \a scenePosition is within the margin() beyond
+ QQuickItem::boundingRect() (if margin > 0), or parentItem() contains
+ \a scenePosition according to QQuickItem::contains(). (So if the \l margin
+ property is set, that overrides the bounds-check, and QQuickItem::contains()
+ is not called.) As a precheck, it's also required that the window contains
+ \a scenePosition mapped to global coordinates, if parentItem() is in a window.
+*/
bool QQuickPointerHandler::parentContains(const QPointF &scenePosition) const
{
if (QQuickItem *par = parentItem()) {
if (par->window()) {
- QRect windowGeometry = par->window()->geometry();
+ QRectF windowGeometry = par->window()->geometry();
if (!par->window()->isTopLevel())
- windowGeometry = QRect(QWindowPrivate::get(par->window())->globalPosition(), par->window()->size());
- QPoint screenPosition = par->window()->mapToGlobal(scenePosition.toPoint());
+ windowGeometry = QRectF(QWindowPrivate::get(par->window())->globalPosition(), par->window()->size());
+ QPointF screenPosition = par->window()->mapToGlobal(scenePosition);
if (!windowGeometry.contains(screenPosition))
return false;
}
@@ -560,12 +604,6 @@ void QQuickPointerHandler::setEnabled(bool enabled)
emit enabledChanged();
}
-bool QQuickPointerHandler::active() const
-{
- Q_D(const QQuickPointerHandler);
- return d->active;
-}
-
/*!
\qmlproperty Item QtQuick::PointerHandler::target
@@ -577,6 +615,14 @@ bool QQuickPointerHandler::active() const
but manipulate another; or to \c null, to disable the default behavior
and do something else instead.
*/
+QQuickItem *QQuickPointerHandler::target() const
+{
+ Q_D(const QQuickPointerHandler);
+ if (!d->targetExplicitlySet)
+ return parentItem();
+ return d->target;
+}
+
void QQuickPointerHandler::setTarget(QQuickItem *target)
{
Q_D(QQuickPointerHandler);
@@ -590,6 +636,27 @@ void QQuickPointerHandler::setTarget(QQuickItem *target)
emit targetChanged();
}
+/*!
+ \qmlproperty Item QtQuick::PointerHandler::parent
+
+ The \l Item which is the scope of the handler; the Item in which it was
+ declared. The handler will handle events on behalf of this Item, which
+ means a pointer event is relevant if at least one of its
+ \l {eventPoint}{eventPoints} occurs within the Item's interior. Initially
+ \l [QML] {target} {target()} is the same, but it can be reassigned.
+
+ \sa {target}, QObject::parent()
+*/
+/*! \internal
+ We still haven't shipped official support for declaring handlers in
+ QtQuick3D.Model objects. Many prerequisites are in place for that, so we
+ should try to keep it working; but there are issues with getting
+ DragHandler to drag its target intuitively in 3D space, for example.
+ TapHandler would work well enough.
+
+ \note When a handler is declared in a \l [QtQuick3D] {Model}{QtQuick3D.Model}
+ object, the parent is not an Item, therefore this property is \c null.
+*/
QQuickItem *QQuickPointerHandler::parentItem() const
{
return qmlobject_cast<QQuickItem *>(QObject::parent());
@@ -612,14 +679,6 @@ void QQuickPointerHandler::setParentItem(QQuickItem *p)
emit parentChanged();
}
-QQuickItem *QQuickPointerHandler::target() const
-{
- Q_D(const QQuickPointerHandler);
- if (!d->targetExplicitlySet)
- return parentItem();
- return d->target;
-}
-
/*! \internal
Pointer Handlers do most of their work in implementations of virtual functions
that are called directly from QQuickItem, not by direct event handling.
@@ -642,6 +701,11 @@ bool QQuickPointerHandler::event(QEvent *e)
}
}
+/*! \internal
+ The entry point to handle the \a event: it's called from
+ QQuickItemPrivate::handlePointerEvent(), begins with wantsPointerEvent(),
+ and calls handlePointerEventImpl() if that returns \c true.
+*/
void QQuickPointerHandler::handlePointerEvent(QPointerEvent *event)
{
Q_D(QQuickPointerHandler);
@@ -668,6 +732,28 @@ void QQuickPointerHandler::handlePointerEvent(QPointerEvent *event)
QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->device()).append(this);
}
+/*!
+ It is the responsibility of this function to decide whether the \a event
+ could be relevant at all to this handler, as a preliminary check.
+
+ Returns \c true if this handler would like handlePointerEventImpl() to be called.
+ If it returns \c false, the handler will be deactivated: \c setActive(false)
+ will be called, and any remaining exclusive grab will be relinquished,
+ as a fail-safe.
+
+ If you override this function, you should call the immediate parent class
+ implementation (and return \c false if it returns \c false); that in turn
+ calls its parent class implementation, and so on.
+ QQuickSinglePointHandler::wantsPointerEvent() and
+ QQuickMultiPointHandler::wantsPointerEvent() call wantsEventPoint(), which
+ is also virtual. You usually can get the behavior you want by subclassing
+ the appropriate handler type, overriding
+ QQuickSinglePointHandler::handleEventPoint() or handlePointerEventImpl(),
+ and perhaps overriding wantsEventPoint() if needed.
+
+ \sa wantsEventPoint(), QQuickPointerDeviceHandler::wantsPointerEvent(),
+ QQuickMultiPointHandler::wantsPointerEvent(), QQuickSinglePointHandler::wantsPointerEvent()
+ */
bool QQuickPointerHandler::wantsPointerEvent(QPointerEvent *event)
{
Q_D(const QQuickPointerHandler);
@@ -675,6 +761,27 @@ bool QQuickPointerHandler::wantsPointerEvent(QPointerEvent *event)
return d->enabled;
}
+/*!
+ Returns \c true if the given \a point (as part of \a event) could be
+ relevant at all to this handler, as a preliminary check.
+
+ If you override this function, you should call the immediate parent class
+ implementation (and return \c false if it returns \c false); that in turn
+ calls its parent class implementation, and so on.
+
+ In particular, the bounds checking is done here: the base class
+ QQuickPointerHandler::wantsEventPoint() calls parentContains(point)
+ (which allows the flexibility promised by margin(), QQuickItem::contains()
+ and QQuickItem::containmentMask()). Pointer Handlers can receive
+ QEventPoints that are outside the parent item's bounds: this allows some
+ flexibility for dealing with multi-point gestures in which one or more
+ fingers have strayed outside the bounds, and yet the gesture is still
+ unambiguously intended for the target() item.
+
+ You should not generally react to the \a event or \a point here, but it's
+ ok to set state to remember what needs to be done in your overridden
+ handlePointerEventImpl() or QQuickSinglePointHandler::handleEventPoint().
+*/
bool QQuickPointerHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
{
Q_UNUSED(event);
@@ -689,12 +796,18 @@ bool QQuickPointerHandler::wantsEventPoint(const QPointerEvent *event, const QEv
\readonly
\qmlproperty bool QtQuick::PointerHandler::active
- This holds true whenever this Input Handler has taken sole responsibility
- for handing one or more EventPoints, by successfully taking an exclusive
- grab of those points. This means that it is keeping its properties
- up-to-date according to the movements of those Event Points and actively
+ This holds \c true whenever this Input Handler has taken sole responsibility
+ for handing one or more \l {eventPoint}{eventPoints}, by successfully taking an
+ exclusive grab of those points. This means that it is keeping its properties
+ up-to-date according to the movements of those eventPoints and actively
manipulating its \l target (if any).
*/
+bool QQuickPointerHandler::active() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->active;
+}
+
void QQuickPointerHandler::setActive(bool active)
{
Q_D(QQuickPointerHandler);
@@ -706,33 +819,46 @@ void QQuickPointerHandler::setActive(bool active)
}
}
-void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *)
-{
-}
-
/*!
- \qmlproperty Item QtQuick::PointerHandler::parent
-
- The \l Item which is the scope of the handler; the Item in which it was declared.
- The handler will handle events on behalf of this Item, which means a
- pointer event is relevant if at least one of its event points occurs within
- the Item's interior. Initially \l [QML] {target} {target()} is the same, but it
- can be reassigned.
-
- \note When a handler is declared in a \l [QtQuick3D] {Model}{QtQuick3D.Model}
- object, the parent is not an Item, therefore this property is \c null.
-
- \sa {target}, QObject::parent()
+ This function can be overridden to implement whatever behavior a specific
+ subclass is intended to have:
+ \list
+ \li Handle all the event's QPointerEvent::points() for which
+ wantsEventPoint() already returned \c true.
+ \li Call setPassiveGrab() setExclusiveGrab() or cancelAllGrabs() as
+ necessary.
+ \li Call QEvent::accept() to stop propagation, or ignore() to allow it
+ to keep going.
+ \endlist
*/
+void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *event)
+{
+ Q_UNUSED(event);
+}
/*!
- \qmlsignal QtQuick::PointerHandler::grabChanged(GrabTransition transition, eventPoint point)
+ \qmlsignal QtQuick::PointerHandler::grabChanged(PointerDevice::GrabTransition transition, eventPoint point)
This signal is emitted when the grab has changed in some way which is
relevant to this handler.
The \a transition (verb) tells what happened.
The \a point (object) is the point that was grabbed or ungrabbed.
+
+ Valid values for \a transition are:
+
+ \value PointerDevice.GrabExclusive
+ This handler has taken primary responsibility for handling the \a point.
+ \value PointerDevice.UngrabExclusive
+ This handler has given up its previous exclusive grab.
+ \value PointerDevice.CancelGrabExclusive
+ This handler's exclusive grab has been taken over or cancelled.
+ \value PointerDevice.GrabPassive
+ This handler has acquired a passive grab, to monitor the \a point.
+ \value PointerDevice.UngrabPassive
+ This handler has given up its previous passive grab.
+ \value PointerDevice.CancelGrabPassive
+ This handler's previous passive grab has terminated abnormally.
*/
/*!
@@ -753,9 +879,17 @@ QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate()
, hadKeepMouseGrab(false)
, hadKeepTouchGrab(false)
, cursorSet(false)
+ , cursorDirty(false)
{
}
+/*! \internal
+ Returns \c true if the movement delta \a d in pixels along the \a axis
+ exceeds QQuickPointerHandler::dragThreshold() \e or QEventPoint::velocity()
+ exceeds QStyleHints::startDragVelocity().
+
+ \sa QQuickDeliveryAgentPrivate::dragOverThreshold()
+*/
template <typename TEventPoint>
bool QQuickPointerHandlerPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const TEventPoint &p) const
{
@@ -770,6 +904,12 @@ bool QQuickPointerHandlerPrivate::dragOverThreshold(qreal d, Qt::Axis axis, cons
return overThreshold;
}
+/*!
+ Returns \c true if the movement \a delta in pixels exceeds
+ QQuickPointerHandler::dragThreshold().
+
+ \sa QQuickDeliveryAgentPrivate::dragOverThreshold()
+*/
bool QQuickPointerHandlerPrivate::dragOverThreshold(QVector2D delta) const
{
Q_Q(const QQuickPointerHandler);
@@ -777,6 +917,13 @@ bool QQuickPointerHandlerPrivate::dragOverThreshold(QVector2D delta) const
return qAbs(delta.x()) > threshold || qAbs(delta.y()) > threshold;
}
+/*!
+ Returns \c true if the movement delta of \a point in pixels
+ (calculated as QEventPoint::scenePosition() - QEventPoint::scenePressPosition())
+ exceeds QQuickPointerHandler::dragThreshold().
+
+ \sa QQuickDeliveryAgentPrivate::dragOverThreshold()
+*/
bool QQuickPointerHandlerPrivate::dragOverThreshold(const QEventPoint &point) const
{
QPointF delta = point.scenePosition() - point.scenePressPosition();
diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h
index 1001718394..e54338f677 100644
--- a/src/quick/handlers/qquickpointerhandler_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -23,7 +23,7 @@
#include <QtQml/QQmlParserStatus>
#include <QtQml/qqmlregistration.h>
#include <QtQuick/qtquickglobal.h>
-#include <QtQuick/private/qtquickexports_p.h>
+#include <QtQuick/qtquickexports.h>
QT_BEGIN_NAMESPACE
@@ -33,7 +33,7 @@ class QQuickItem;
class QQuickPointerHandlerPrivate;
class QPointerEvent;
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject, public QQmlParserStatus
+class Q_QUICK_EXPORT QQuickPointerHandler : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
diff --git a/src/quick/handlers/qquickpointerhandler_p_p.h b/src/quick/handlers/qquickpointerhandler_p_p.h
index 36797ef8b3..7072222773 100644
--- a/src/quick/handlers/qquickpointerhandler_p_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandlerPrivate : public QObjectPrivate
+class Q_QUICK_EXPORT QQuickPointerHandlerPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickPointerHandler)
@@ -57,6 +57,7 @@ public:
bool hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state
bool hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state
bool cursorSet : 1;
+ bool cursorDirty : 1;
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointhandler.cpp b/src/quick/handlers/qquickpointhandler.cpp
index 1c5f36b6dd..ab1f274aa1 100644
--- a/src/quick/handlers/qquickpointhandler.cpp
+++ b/src/quick/handlers/qquickpointhandler.cpp
@@ -3,7 +3,9 @@
#include "qquickpointhandler_p.h"
#include <private/qquickwindow_p.h>
+
#include <QDebug>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -66,7 +68,8 @@ QT_BEGIN_NAMESPACE
PointHandler will not automatically manipulate the \c target item in any way.
You need to use bindings to make it react to the \l point.
- \note On macOS, PointHandler does not react to the trackpad by default.
+ \note On macOS, PointHandler does not react to multiple fingers on the
+ trackpad by default, although it does react to a pressed point (mouse position).
That is because macOS can provide either native gesture recognition, or raw
touchpoints, but not both. We prefer to use the native gesture event in
PinchHandler, so we do not want to disable it by enabling touch. However
@@ -75,7 +78,7 @@ QT_BEGIN_NAMESPACE
want to react to all the touchpoints but do not require the smooth
native-gesture experience.
- \sa MultiPointTouchArea
+ \sa MultiPointTouchArea, HoverHandler, {Qt Quick Examples - Pointer Handlers}
*/
QQuickPointHandler::QQuickPointHandler(QQuickItem *parent)
@@ -128,6 +131,127 @@ QVector2D QQuickPointHandler::translation() const
return QVector2D(point().position() - point().pressPosition());
}
+/*!
+ \qmlproperty flags PointHandler::acceptedButtons
+
+ The mouse buttons that can activate this PointHandler.
+
+ By default, this property is set to \l {QtQuick::MouseEvent::button} {Qt.LeftButton}.
+ It can be set to an OR combination of mouse buttons, and will ignore events
+ in which other buttons are pressed or held.
+
+ \snippet pointerHandlers/pointHandlerAcceptedButtons.qml 0
+
+ \note On a touchscreen, there are no buttons, so this property does not
+ prevent PointHandler from reacting to touchpoints.
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedDevices
+
+ The types of pointing devices that can activate this PointHandler.
+
+ By default, this property is set to
+ \l{QInputDevice::DeviceType}{PointerDevice.AllDevices}.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching \l {PointerDevice}{devices}:
+
+ \snippet pointerHandlers/pointHandler.qml 1
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedPointerTypes
+
+ The types of pointing instruments (finger, stylus, eraser, etc.)
+ that can activate this PointHandler.
+
+ By default, this property is set to
+ \l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching \l {PointerDevice}{devices}:
+
+ \snippet pointerHandlers/pointHandlerCanvasDrawing.qml 0
+
+ The \l {Qt Quick Examples - Pointer Handlers} includes a more complex example for
+ drawing on a Canvas with a graphics tablet.
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedModifiers
+
+ If this property is set, PointHandler requires the given keyboard modifiers
+ to be pressed in order to react to \l {PointerEvent}{PointerEvents}, and
+ otherwise ignores them.
+
+ If this property is set to \c Qt.KeyboardModifierMask (the default value),
+ then PointHandler ignores the modifier keys.
+
+ For example, an \l [QML] Item could have two handlers, one of which is
+ enabled only if the required keyboard modifier is pressed:
+
+ \snippet pointerHandlers/pointHandlerAcceptedModifiers.qml 0
+
+ If you set \c acceptedModifiers to an OR combination of modifier keys,
+ it means \e all of those modifiers must be pressed to activate the handler.
+
+ The available modifiers are as follows:
+
+ \value NoModifier No modifier key is allowed.
+ \value ShiftModifier A Shift key on the keyboard must be pressed.
+ \value ControlModifier A Ctrl key on the keyboard must be pressed.
+ \value AltModifier An Alt key on the keyboard must be pressed.
+ \value MetaModifier A Meta key on the keyboard must be pressed.
+ \value KeypadModifier A keypad button must be pressed.
+ \value GroupSwitchModifier X11 only (unless activated on Windows by a command line argument).
+ A Mode_switch key on the keyboard must be pressed.
+ \value KeyboardModifierMask The handler does not care which modifiers are pressed.
+
+ \sa Qt::KeyboardModifier
+*/
+
+/*!
+ \readonly
+ \qmlproperty bool PointHandler::active
+
+ This holds \c true whenever the constraints are satisfied and this
+ PointHandler is reacting. This means that it is keeping its properties
+ up-to-date according to the movements of the \l {eventPoint}{eventPoints}
+ that satisfy the constraints.
+*/
+
+/*!
+ \internal
+ \qmlproperty flags PointHandler::dragThreshold
+
+ This property is not used in PointHandler.
+*/
+
+/*!
+ \qmlproperty real PointHandler::margin
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which an \l eventPoint can activate this handler.
+
+ The default value is \c 0.
+
+ \snippet pointerHandlers/pointHandlerMargin.qml 0
+*/
+
+/*!
+ \qmlproperty real PointHandler::target
+
+ A property that can conveniently hold an Item to be manipulated or to show
+ feedback. Unlike other \l {Qt Quick Input Handlers}{Pointer Handlers},
+ PointHandler does not do anything with the \c target on its own: you
+ usually need to create reactive bindings to properties such as
+ \l SinglePointHandler::point and \l PointHandler::active. If you declare
+ an Item instance here, you need to explicitly set its \l {Item::}{parent},
+ because PointHandler is not an Item.
+
+ By default, it is the same as the \l {PointerHandler::}{parent}, the Item
+ within which the handler is declared.
+*/
+
QT_END_NAMESPACE
#include "moc_qquickpointhandler_p.cpp"
diff --git a/src/quick/handlers/qquickpointhandler_p.h b/src/quick/handlers/qquickpointhandler_p.h
index b8a5a049f5..46ee9bcbc7 100644
--- a/src/quick/handlers/qquickpointhandler_p.h
+++ b/src/quick/handlers/qquickpointhandler_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickPointHandler : public QQuickSinglePointHandler
+class Q_QUICK_EXPORT QQuickPointHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
index c732a2562e..d2c38179a3 100644
--- a/src/quick/handlers/qquicksinglepointhandler.cpp
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -57,9 +57,21 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QPointerEvent *event)
point = &p;
}
}
- if (missing)
- qCWarning(lcTouchTarget) << this << "pointId" << Qt::hex << d->pointInfo.id()
- << "is missing from current event, but was neither canceled nor released";
+ if (missing) {
+ // Received a stray touch begin event => reset and start over.
+ if (event->type() == QEvent::TouchBegin && event->points().count() == 1) {
+ const QEventPoint &point = event->point(0);
+ qCDebug(lcTouchTarget) << this << "pointId" << Qt::hex << point.id()
+ << "was received as a stray TouchBegin event. Canceling existing gesture"
+ " and starting over.";
+ d->pointInfo.reset(event, point);
+ return true;
+ } else {
+ qCWarning(lcTouchTarget) << this << "pointId" << Qt::hex << d->pointInfo.id()
+ << "is missing from current event, but was neither canceled nor released."
+ " Ignoring:" << event->type();
+ }
+ }
if (point) {
if (candidatePointCount == 1 || (candidatePointCount > 1 && d->ignoreAdditionalPoints)) {
point->setAccepted();
@@ -105,11 +117,16 @@ void QQuickSinglePointHandler::handlePointerEventImpl(QPointerEvent *event)
void QQuickSinglePointHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point)
{
- if (point.state() != QEventPoint::Released)
- return;
+ if (point.state() == QEventPoint::Released) {
+ // If it's a mouse or tablet event, with buttons,
+ // do not deactivate unless all acceptable buttons are released.
+ if (event->isSinglePointEvent()) {
+ const Qt::MouseButtons releasedButtons = static_cast<QSinglePointEvent *>(event)->buttons();
+ if ((releasedButtons & acceptedButtons()) != Qt::NoButton)
+ return;
+ }
- const Qt::MouseButtons releasedButtons = static_cast<QSinglePointEvent *>(event)->buttons();
- if ((releasedButtons & acceptedButtons()) == Qt::NoButton) {
+ // Deactivate this handler on release
setExclusiveGrab(event, point, false);
d_func()->reset();
}
@@ -171,9 +188,9 @@ QQuickHandlerPoint QQuickSinglePointHandler::point() const
/*!
\readonly
- \qmlproperty HandlerPoint QtQuick::SinglePointHandler::point
+ \qmlproperty handlerPoint QtQuick::SinglePointHandler::point
- The event point currently being handled. When no point is currently being
+ The \l eventPoint currently being handled. When no point is currently being
handled, this object is reset to default values (all coordinates are 0).
*/
diff --git a/src/quick/handlers/qquicksinglepointhandler_p.h b/src/quick/handlers/qquicksinglepointhandler_p.h
index 038265c114..b35c5805e1 100644
--- a/src/quick/handlers/qquicksinglepointhandler_p.h
+++ b/src/quick/handlers/qquicksinglepointhandler_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickSinglePointHandlerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointHandler : public QQuickPointerDeviceHandler
+class Q_QUICK_EXPORT QQuickSinglePointHandler : public QQuickPointerDeviceHandler
{
Q_OBJECT
Q_PROPERTY(QQuickHandlerPoint point READ point NOTIFY pointChanged)
diff --git a/src/quick/handlers/qquicksinglepointhandler_p_p.h b/src/quick/handlers/qquicksinglepointhandler_p_p.h
index 7b319a3d48..d8eeac32e7 100644
--- a/src/quick/handlers/qquicksinglepointhandler_p_p.h
+++ b/src/quick/handlers/qquicksinglepointhandler_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointHandlerPrivate : public QQuickPointerDeviceHandlerPrivate
+class Q_QUICK_EXPORT QQuickSinglePointHandlerPrivate : public QQuickPointerDeviceHandlerPrivate
{
Q_DECLARE_PUBLIC(QQuickSinglePointHandler)
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 306eec65b8..accf307382 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTapHandler, "qt.quick.handler.tap")
-qreal QQuickTapHandler::m_multiTapInterval(0.0);
+quint64 QQuickTapHandler::m_multiTapInterval(0);
// single tap distance is the same as the drag threshold
int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1);
int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
@@ -50,14 +50,15 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
QStyleHints::touchDoubleTapDistance() with touch, and the time between
taps must not exceed QStyleHints::mouseDoubleClickInterval().
- \sa MouseArea
+ \sa MouseArea, {Qt Quick Examples - Pointer Handlers}
*/
QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
: QQuickSinglePointHandler(parent)
+ , m_longPressThreshold(QGuiApplication::styleHints()->mousePressAndHoldInterval())
{
if (m_mouseMultiClickDistanceSquared < 0) {
- m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0;
+ m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval();
m_mouseMultiClickDistanceSquared = qApp->styleHints()->mouseDoubleClickDistance();
m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
m_touchMultiTapDistanceSquared = qApp->styleHints()->touchDoubleTapDistance();
@@ -78,6 +79,8 @@ bool QQuickTapHandler::wantsEventPoint(const QPointerEvent *event, const QEventP
bool ret = false;
bool overThreshold = d_func()->dragOverThreshold(point);
if (overThreshold && m_gesturePolicy != DragWithinBounds) {
+ if (m_longPressTimer.isActive())
+ qCDebug(lcTapHandler) << objectName() << "drag threshold exceeded";
m_longPressTimer.stop();
m_holdTimer.invalidate();
}
@@ -87,16 +90,17 @@ bool QQuickTapHandler::wantsEventPoint(const QPointerEvent *event, const QEventP
ret = parentContains(point);
break;
case QEventPoint::Updated:
+ ret = point.id() == this->point().id();
switch (m_gesturePolicy) {
case DragThreshold:
- ret = !overThreshold && parentContains(point);
+ ret = ret && !overThreshold && parentContains(point);
break;
case WithinBounds:
case DragWithinBounds:
- ret = parentContains(point);
+ ret = ret && parentContains(point);
break;
case ReleaseWithinBounds:
- ret = point.id() == this->point().id();
+ // no change to ret: depends only whether it's the already-tracking point ID
break;
}
break;
@@ -144,19 +148,28 @@ void QQuickTapHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point
/*!
\qmlproperty real QtQuick::TapHandler::longPressThreshold
- The time in seconds that an event point must be pressed in order to
- trigger a long press gesture and emit the \l longPressed() signal.
- If the point is released before this time limit, a tap can be detected
- if the \l gesturePolicy constraint is satisfied. The default value is
- QStyleHints::mousePressAndHoldInterval() converted to seconds.
+ The time in seconds that an \l eventPoint must be pressed in order to
+ trigger a long press gesture and emit the \l longPressed() signal, if the
+ value is greater than \c 0. If the point is released before this time
+ limit, a tap can be detected if the \l gesturePolicy constraint is
+ satisfied. If \c longPressThreshold is \c 0, the timer is disabled and the
+ signal will not be emitted. If \c longPressThreshold is set to \c undefined,
+ the default value is used instead, and can be read back from this property.
+
+ The default value is QStyleHints::mousePressAndHoldInterval() converted to
+ seconds.
*/
qreal QQuickTapHandler::longPressThreshold() const
{
- return longPressThresholdMilliseconds() / 1000.0;
+ return m_longPressThreshold / qreal(1000);
}
void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold)
{
+ if (longPressThreshold < 0) {
+ resetLongPressThreshold();
+ return;
+ }
int ms = qRound(longPressThreshold * 1000);
if (m_longPressThreshold == ms)
return;
@@ -165,9 +178,14 @@ void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold)
emit longPressThresholdChanged();
}
-int QQuickTapHandler::longPressThresholdMilliseconds() const
+void QQuickTapHandler::resetLongPressThreshold()
{
- return (m_longPressThreshold < 0 ? QGuiApplication::styleHints()->mousePressAndHoldInterval() : m_longPressThreshold);
+ int ms = QGuiApplication::styleHints()->mousePressAndHoldInterval();
+ if (m_longPressThreshold == ms)
+ return;
+
+ m_longPressThreshold = ms;
+ emit longPressThresholdChanged();
}
void QQuickTapHandler::timerEvent(QTimerEvent *event)
@@ -175,6 +193,7 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
if (event->timerId() == m_longPressTimer.timerId()) {
m_longPressTimer.stop();
qCDebug(lcTapHandler) << objectName() << "longPressed";
+ m_longPressed = true;
emit longPressed();
} else if (event->timerId() == m_doubleTapTimer.timerId()) {
m_doubleTapTimer.stop();
@@ -199,49 +218,96 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
The \c gesturePolicy also affects grab behavior as described below.
- \value TapHandler.DragThreshold
- (the default value) The event point must not move significantly.
- If the mouse, finger or stylus moves past the system-wide drag
- threshold (QStyleHints::startDragDistance), the tap gesture is
- canceled, even if the button or finger is still pressed. This policy
- can be useful whenever TapHandler needs to cooperate with other
- input handlers (for example \l DragHandler) or event-handling Items
- (for example QtQuick Controls), because in this case TapHandler
- will not take the exclusive grab, but merely a
- \l {QPointerEvent::addPassiveGrabber()}{passive grab}.
-
- \value TapHandler.WithinBounds
- If the event point leaves the bounds of the \c parent Item, the tap
- gesture is canceled. The TapHandler will take the
- \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on
- press, but will release the grab as soon as the boundary constraint
- is no longer satisfied.
-
- \value TapHandler.ReleaseWithinBounds
- At the time of release (the mouse button is released or the finger
- is lifted), if the event point is outside the bounds of the
- \c parent Item, a tap gesture is not recognized. This corresponds to
- typical behavior for button widgets: you can cancel a click by
- dragging outside the button, and you can also change your mind by
- dragging back inside the button before release. Note that it's
- necessary for TapHandler to take the
- \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press
- and retain it until release in order to detect this gesture.
-
- \value TapHandler.DragWithinBounds
- On press, TapHandler takes the
- \l {QPointerEvent::setExclusiveGrabber}{exclusive grab}; after that,
- the event point can be dragged within the bounds of the \c parent
- item, while the \l timeHeld property keeps counting, and the
- \l longPressed() signal will be emitted regardless of drag distance.
- However, like \c WithinBounds, if the point leaves the bounds,
- the tap gesture is \l {PointerHandler::}{canceled()}, \l active()
- becomes \c false, and \l timeHeld stops counting. This is suitable
- for implementing press-drag-release components, such as menus, in
- which a single TapHandler detects press, \c timeHeld drives an
- "opening" animation, and then the user can drag to a menu item and
- release, while never leaving the bounds of the parent scene containing
- the menu. This value was added in Qt 6.3.
+ \table
+ \header
+ \li Constant
+ \li Description
+ \row
+ \li \c TapHandler.DragThreshold
+ \image pointerHandlers/tapHandlerOverlappingButtons.webp
+ Grab on press: \e passive
+ \li (the default value) The \l eventPoint must not move significantly.
+ If the mouse, finger or stylus moves past the system-wide drag
+ threshold (QStyleHints::startDragDistance), the tap gesture is
+ canceled, even if the device or finger is still pressed. This policy
+ can be useful whenever TapHandler needs to cooperate with other
+ input handlers (for example \l DragHandler) or event-handling Items
+ (for example \l {Qt Quick Controls}), because in this case TapHandler
+ will not take the exclusive grab, but merely a
+ \l {QPointerEvent::addPassiveGrabber()}{passive grab}.
+ That is, \c DragThreshold is especially useful to \e augment
+ existing behavior: it reacts to tap/click/long-press even when
+ another item or handler is already reacting, perhaps even in a
+ different layer of the UI. The following snippet shows one
+ TapHandler as used in one component; but if we stack up two
+ instances of the component, you will see the handlers in both of them
+ react simultaneously when a press occurs over both of them, because
+ the passive grab does not stop event propagation:
+ \quotefromfile pointerHandlers/tapHandlerOverlappingButtons.qml
+ \skipto Item
+ \printuntil component Button
+ \skipto TapHandler
+ \printuntil }
+ \skipuntil Text {
+ \skipuntil }
+ \printuntil Button
+ \printuntil Button
+ \printuntil }
+
+ \row
+ \li \c TapHandler.WithinBounds
+ \image pointerHandlers/tapHandlerButtonWithinBounds.webp
+ Grab on press: \e exclusive
+ \li If the \l eventPoint leaves the bounds of the \c parent Item, the tap
+ gesture is canceled. The TapHandler will take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on
+ press, but will release the grab as soon as the boundary constraint
+ is no longer satisfied.
+ \snippet pointerHandlers/tapHandlerButtonWithinBounds.qml 1
+
+ \row
+ \li \c TapHandler.ReleaseWithinBounds
+ \image pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
+ Grab on press: \e exclusive
+ \li At the time of release (the mouse button is released or the finger
+ is lifted), if the \l eventPoint is outside the bounds of the
+ \c parent Item, a tap gesture is not recognized. This corresponds to
+ typical behavior for button widgets: you can cancel a click by
+ dragging outside the button, and you can also change your mind by
+ dragging back inside the button before release. Note that it's
+ necessary for TapHandler to take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press
+ and retain it until release in order to detect this gesture.
+ \snippet pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml 1
+
+ \row
+ \li \c TapHandler.DragWithinBounds
+ \image pointerHandlers/dragReleaseMenu.webp
+ Grab on press: \e exclusive
+ \li On press, TapHandler takes the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab}; after that,
+ the \l eventPoint can be dragged within the bounds of the \c parent
+ item, while the \l timeHeld property keeps counting, and the
+ \l longPressed() signal will be emitted regardless of drag distance.
+ However, like \c WithinBounds, if the point leaves the bounds,
+ the tap gesture is \l {PointerHandler::}{canceled()}, \l active()
+ becomes \c false, and \l timeHeld stops counting. This is suitable
+ for implementing press-drag-release components, such as menus, in
+ which a single TapHandler detects press, \c timeHeld drives an
+ "opening" animation, and then the user can drag to a menu item and
+ release, while never leaving the bounds of the parent scene containing
+ the menu. This value was added in Qt 6.3.
+ \snippet pointerHandlers/dragReleaseMenu.qml 1
+ \endtable
+
+ The \l {Qt Quick Examples - Pointer Handlers} demonstrates some use cases for these.
+
+ \note If you find that TapHandler is reacting in cases that conflict with
+ some other behavior, the first thing you should try is to think about which
+ \c gesturePolicy is appropriate. If you cannot fix it by changing \c gesturePolicy,
+ some cases are better served by adjusting \l {PointerHandler::}{grabPermissions},
+ either in this handler, or in another handler that should \e prevent TapHandler
+ from reacting.
*/
void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
{
@@ -290,7 +356,7 @@ void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals ex
Holds true whenever the mouse or touch point is pressed,
and any movement since the press is compliant with the current
- \l gesturePolicy. When the event point is released or the policy is
+ \l gesturePolicy. When the \l eventPoint is released or the policy is
violated, \e pressed will change to false.
*/
void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event, QEventPoint &point)
@@ -302,21 +368,12 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
connectPreRenderSignal(press);
updateTimeHeld();
if (press) {
- m_longPressTimer.start(longPressThresholdMilliseconds(), this);
+ if (m_longPressThreshold > 0)
+ m_longPressTimer.start(m_longPressThreshold, this);
m_holdTimer.start();
} else {
m_longPressTimer.stop();
m_holdTimer.invalidate();
- if (m_exclusiveSignals == (SingleTap | DoubleTap)) {
- if (m_tapCount == 0) {
- m_singleTapReleasedPoint = point;
- m_singleTapReleasedButton = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
- qCDebug(lcTapHandler) << objectName() << "waiting to emit singleTapped:" << qApp->styleHints()->mouseDoubleClickInterval() << "ms";
- m_doubleTapTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), this);
- } else if (m_doubleTapTimer.isActive()) {
- qCDebug(lcTapHandler) << objectName() << "tap" << (m_tapCount + 1) << "after" << event->timestamp() / 1000.0 - m_lastTapTimestamp << "sec";
- }
- }
}
if (press) {
// on press, grab before emitting changed signals
@@ -326,33 +383,57 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
setExclusiveGrab(event, point, press);
}
if (!cancel && !press && parentContains(point)) {
- if (point.timeHeld() < longPressThreshold()) {
+ if (m_longPressed) {
+ qCDebug(lcTapHandler) << objectName() << "long press threshold" << longPressThreshold() << "exceeded:" << point.timeHeld();
+ } else {
// Assuming here that pointerEvent()->timestamp() is in ms.
- const qreal ts = event->timestamp() / 1000.0;
- const qreal interval = ts - m_lastTapTimestamp;
+ const quint64 ts = event->timestamp();
+ const quint64 interval = ts - m_lastTapTimestamp;
const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
- if (interval < m_multiTapInterval && distanceSquared <
+ const auto singleTapReleasedButton = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
+ if ((interval < m_multiTapInterval && distanceSquared <
(event->device()->type() == QInputDevice::DeviceType::Mouse ?
m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
+ && m_singleTapReleasedButton == singleTapReleasedButton) {
++m_tapCount;
- else
+ } else {
+ m_singleTapReleasedButton = singleTapReleasedButton;
+ m_singleTapReleasedPoint = point;
m_tapCount = 1;
+ }
qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times; interval since last:" << interval
<< "sec; distance since last:" << qSqrt(distanceSquared);
auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
emit tapped(point, button);
emit tapCountChanged();
- if (m_tapCount == 1 && !m_exclusiveSignals.testFlag(DoubleTap))
- emit singleTapped(point, button);
- else if (m_tapCount == 2 && !m_exclusiveSignals.testFlag(SingleTap)) {
- emit doubleTapped(point, button);
+ switch (m_exclusiveSignals) {
+ case NotExclusive:
+ if (m_tapCount == 1)
+ emit singleTapped(point, button);
+ else if (m_tapCount == 2)
+ emit doubleTapped(point, button);
+ break;
+ case SingleTap:
+ if (m_tapCount == 1)
+ emit singleTapped(point, button);
+ break;
+ case DoubleTap:
+ if (m_tapCount == 2)
+ emit doubleTapped(point, button);
+ break;
+ case (SingleTap | DoubleTap):
+ if (m_tapCount == 1) {
+ qCDebug(lcTapHandler) << objectName() << "waiting to emit singleTapped:" << m_multiTapInterval << "ms";
+ m_doubleTapTimer.start(m_multiTapInterval, this);
+ }
}
+ qCDebug(lcTapHandler) << objectName() << "tap" << m_tapCount << "after" << event->timestamp() - m_lastTapTimestamp << "ms";
+
m_lastTapTimestamp = ts;
m_lastTapPos = point.scenePosition();
- } else {
- qCDebug(lcTapHandler) << objectName() << "tap threshold" << longPressThreshold() << "exceeded:" << point.timeHeld();
}
}
+ m_longPressed = false;
emit pressedChanged();
if (!press && m_gesturePolicy != DragThreshold) {
// on release, ungrab after emitting changed signals
@@ -380,13 +461,25 @@ void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDev
void QQuickTapHandler::connectPreRenderSignal(bool conn)
{
+ // disconnect pre-existing connection, if any
+ disconnect(m_preRenderSignalConnection);
+
auto par = parentItem();
- if (!par)
+ if (!par || !par->window())
return;
- if (conn)
- connect(par->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
- else
- disconnect(par->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
+
+ /*
+ Note: beforeSynchronizing is emitted from the SG thread, and the
+ timeHeldChanged signal can be used to do arbitrary things in user QML.
+
+ But the docs say the GUI thread is blockd, and "Therefore, it is safe
+ to access GUI thread thread data in a slot or lambda that is connected
+ with Qt::DirectConnection." We use the default AutoConnection just in case.
+ */
+ if (conn) {
+ m_preRenderSignalConnection = connect(par->window(), &QQuickWindow::beforeSynchronizing,
+ this, &QQuickTapHandler::updateTimeHeld);
+ }
}
void QQuickTapHandler::updateTimeHeld()
@@ -399,8 +492,8 @@ void QQuickTapHandler::updateTimeHeld()
\readonly
The number of taps which have occurred within the time and space
- constraints to be considered a single gesture. For example, to detect
- a triple-tap, you can write:
+ constraints to be considered a single gesture. The counter is reset to 1
+ if the button changed. For example, to detect a triple-tap, you can write:
\qml
Rectangle {
diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h
index 4fdb7065eb..a9908ebabd 100644
--- a/src/quick/handlers/qquicktaphandler_p.h
+++ b/src/quick/handlers/qquicktaphandler_p.h
@@ -24,13 +24,13 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickTapHandler : public QQuickSinglePointHandler
+class Q_QUICK_EXPORT QQuickTapHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
Q_PROPERTY(int tapCount READ tapCount NOTIFY tapCountChanged)
Q_PROPERTY(qreal timeHeld READ timeHeld NOTIFY timeHeldChanged)
- Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged)
+ Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged RESET resetLongPressThreshold)
Q_PROPERTY(GesturePolicy gesturePolicy READ gesturePolicy WRITE setGesturePolicy NOTIFY gesturePolicyChanged)
Q_PROPERTY(QQuickTapHandler::ExclusiveSignals exclusiveSignals READ exclusiveSignals WRITE setExclusiveSignals NOTIFY exclusiveSignalsChanged REVISION(6, 5))
@@ -48,11 +48,11 @@ public:
enum ExclusiveSignal {
NotExclusive = 0,
- SingleTap = 1 << 1,
- DoubleTap = 1 << 2
+ SingleTap = 1 << 0,
+ DoubleTap = 1 << 1
};
Q_DECLARE_FLAGS(ExclusiveSignals, ExclusiveSignal)
- Q_FLAG(ExclusiveSignal)
+ Q_FLAG(ExclusiveSignals)
explicit QQuickTapHandler(QQuickItem *parent = nullptr);
@@ -63,6 +63,7 @@ public:
qreal longPressThreshold() const;
void setLongPressThreshold(qreal longPressThreshold);
+ void resetLongPressThreshold();
GesturePolicy gesturePolicy() const { return m_gesturePolicy; }
void setGesturePolicy(GesturePolicy gesturePolicy);
@@ -92,25 +93,26 @@ protected:
private:
void setPressed(bool press, bool cancel, QPointerEvent *event, QEventPoint &point);
- int longPressThresholdMilliseconds() const;
void connectPreRenderSignal(bool conn = true);
void updateTimeHeld();
private:
QPointF m_lastTapPos;
- qreal m_lastTapTimestamp = 0;
+ quint64 m_lastTapTimestamp = 0;
QElapsedTimer m_holdTimer;
QBasicTimer m_longPressTimer;
QBasicTimer m_doubleTapTimer;
QEventPoint m_singleTapReleasedPoint;
+ QMetaObject::Connection m_preRenderSignalConnection;
Qt::MouseButton m_singleTapReleasedButton;
int m_tapCount = 0;
int m_longPressThreshold = -1;
GesturePolicy m_gesturePolicy = GesturePolicy::DragThreshold;
ExclusiveSignals m_exclusiveSignals = NotExclusive;
bool m_pressed = false;
+ bool m_longPressed = false;
- static qreal m_multiTapInterval;
+ static quint64 m_multiTapInterval;
static int m_mouseMultiClickDistanceSquared;
static int m_touchMultiTapDistanceSquared;
};
diff --git a/src/quick/handlers/qquickwheelhandler.cpp b/src/quick/handlers/qquickwheelhandler.cpp
index fc353620e5..34110ff6cc 100644
--- a/src/quick/handlers/qquickwheelhandler.cpp
+++ b/src/quick/handlers/qquickwheelhandler.cpp
@@ -46,7 +46,7 @@ Q_LOGGING_CATEGORY(lcWheelHandler, "qt.quick.handler.wheel")
WheelHandler handles only a rotating mouse wheel by default; this
can be changed by setting acceptedDevices.
- \sa MouseArea, Flickable
+ \sa MouseArea, Flickable, {Qt Quick Examples - Pointer Handlers}
*/
QQuickWheelHandler::QQuickWheelHandler(QQuickItem *parent)
@@ -84,7 +84,7 @@ void QQuickWheelHandler::setOrientation(Qt::Orientation orientation)
\qmlproperty bool QtQuick::WheelHandler::invertible
Whether or not to reverse the direction of property change if
- QQuickPointerScrollEvent::inverted is true. The default is \c true.
+ \l QWheelEvent::inverted is \c true. The default is \c true.
If the operating system has a "natural scrolling" setting that causes
scrolling to be in the same direction as the finger movement, then if this
@@ -524,7 +524,7 @@ QMetaProperty &QQuickWheelHandlerPrivate::targetMetaProperty() const
By default, this property is set to
\l{QInputDevice::DeviceType}{PointerDevice.Mouse}, so as to react only to
- events events from an actual mouse wheel.
+ events from an actual mouse wheel.
WheelHandler can be made to respond to both mouse wheel and touchpad
scrolling by setting acceptedDevices to
diff --git a/src/quick/handlers/qquickwheelhandler_p.h b/src/quick/handlers/qquickwheelhandler_p.h
index bf05ff8af3..38d6fea8e6 100644
--- a/src/quick/handlers/qquickwheelhandler_p.h
+++ b/src/quick/handlers/qquickwheelhandler_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickWheelEvent;
class QQuickWheelHandlerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickWheelHandler : public QQuickSinglePointHandler
+class Q_QUICK_EXPORT QQuickWheelHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
diff --git a/src/quick/handlers/qquickwheelhandler_p_p.h b/src/quick/handlers/qquickwheelhandler_p_p.h
index 2124e29599..db1d394e96 100644
--- a/src/quick/handlers/qquickwheelhandler_p_p.h
+++ b/src/quick/handlers/qquickwheelhandler_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickWheelHandlerPrivate : public QQuickSinglePointHandlerPrivate
+class Q_QUICK_EXPORT QQuickWheelHandlerPrivate : public QQuickSinglePointHandlerPrivate
{
Q_DECLARE_PUBLIC(QQuickWheelHandler)
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 0eb4fe40d3..d78bd040c2 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -9,7 +9,7 @@
#include <private/qquickcontext2dtexture_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <qsgtextureprovider.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <QtGui/QGuiApplication>
#include <qsgtextureprovider.h>
@@ -252,7 +252,7 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
QPainter instead of the more expensive and likely less performing
JavaScript and Context2D approach.
- \sa Context2D, QQuickPaintedItem
+ \sa Context2D, QQuickPaintedItem, {Qt Quick Examples - Pointer Handlers}
*/
QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
@@ -444,9 +444,8 @@ void QQuickCanvasItem::setCanvasWindow(const QRectF& rect)
\qmlproperty enumeration QtQuick::Canvas::renderTarget
Holds the current canvas render target.
- \list
- \li Canvas.Image - render to an in memory image buffer.
- \endlist
+ \value Canvas.Image Render to an in-memory image buffer.
+ \value Canvas.FramebufferObject As of Qt 6.0, this value is ignored.
This hint is supplied along with renderStrategy to the graphics context to
determine the method of rendering. A renderStrategy, renderTarget or a
@@ -480,11 +479,9 @@ void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target)
\qmlproperty enumeration QtQuick::Canvas::renderStrategy
Holds the current canvas rendering strategy.
- \list
- \li Canvas.Immediate - context will perform graphics commands immediately in the main UI thread.
- \li Canvas.Threaded - context will defer graphics commands to a private rendering thread.
- \li Canvas.Cooperative - context will defer graphics commands to the applications global render thread.
- \endlist
+ \value Canvas.Immediate context will perform graphics commands immediately in the main UI thread.
+ \value Canvas.Threaded context will defer graphics commands to a private rendering thread.
+ \value Canvas.Cooperative context will defer graphics commands to the applications global render thread.
This hint is supplied along with renderTarget to the graphics context to
determine the method of rendering. A renderStrategy, renderTarget or a
@@ -813,7 +810,7 @@ QSGTextureProvider *QQuickCanvasItem::textureProvider() const
*/
-void QQuickCanvasItem::getContext(QQmlV4Function *args)
+void QQuickCanvasItem::getContext(QQmlV4FunctionPtr args)
{
Q_D(QQuickCanvasItem);
@@ -857,7 +854,7 @@ void QQuickCanvasItem::getContext(QQmlV4Function *args)
scene.
*/
-void QQuickCanvasItem::requestAnimationFrame(QQmlV4Function *args)
+void QQuickCanvasItem::requestAnimationFrame(QQmlV4FunctionPtr args)
{
QV4::Scope scope(args->v4engine());
QV4::ScopedFunctionObject f(scope, (*args)[0]);
@@ -886,7 +883,7 @@ void QQuickCanvasItem::requestAnimationFrame(QQmlV4Function *args)
This function will cancel the animation callback referenced by \a handle.
*/
-void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV4Function *args)
+void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV4FunctionPtr args)
{
QV4::Scope scope(args->v4engine());
QV4::ScopedValue v(scope, (*args)[0]);
@@ -961,12 +958,12 @@ bool QQuickCanvasItem::save(const QString &filename, const QSizeF &imageSize) co
return toImage(QRectF(QPointF(0, 0), imageSize)).save(url.toLocalFile());
}
-QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url)
+QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url, QSizeF sourceSize)
{
Q_D(QQuickCanvasItem);
QUrl fullPathUrl = d->baseUrl.resolved(url);
if (!d->pixmaps.contains(fullPathUrl)) {
- loadImage(url);
+ loadImage(url, sourceSize);
}
return d->pixmaps.value(fullPathUrl);
}
@@ -980,7 +977,7 @@ QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& ur
*/
/*!
- \qmlmethod QtQuick::Canvas::loadImage(url image)
+ \qmlmethod QtQuick::Canvas::loadImage(url image, size sourceSize = undefined)
Loads the given \a image asynchronously.
@@ -989,10 +986,14 @@ QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& ur
\note Only loaded images can be painted on the Canvas item.
+ If \a sourceSize is specified, the image will be scaled to that size during loading. This is
+ useful for loading scalable (vector) images (eg. SVGs) at their intended display size. This
+ parameter was introduced in Qt 6.7.
+
\sa unloadImage(), imageLoaded(), isImageLoaded(),
Context2D::createImageData(), Context2D::drawImage()
*/
-void QQuickCanvasItem::loadImage(const QUrl& url)
+void QQuickCanvasItem::loadImage(const QUrl& url, QSizeF sourceSize)
{
Q_D(QQuickCanvasItem);
QUrl fullPathUrl = d->baseUrl.resolved(url);
@@ -1004,6 +1005,8 @@ void QQuickCanvasItem::loadImage(const QUrl& url)
pix->load(qmlEngine(this)
, fullPathUrl
+ , QRect()
+ , sourceSize.toSize()
, QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
if (pix->isLoading())
pix->connectFinished(this, SIGNAL(imageLoaded()));
@@ -1203,7 +1206,7 @@ QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
This signal is emitted when the \a region needs to be rendered. If a context
is active it can be referenced from the context property.
- This signal can be triggered by markdirty(), requestPaint() or by changing
+ This signal can be triggered by markDirty(), requestPaint() or by changing
the current canvas window.
*/
diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h
index d7821a53ba..300039743d 100644
--- a/src/quick/items/context2d/qquickcanvasitem_p.h
+++ b/src/quick/items/context2d/qquickcanvasitem_p.h
@@ -31,9 +31,8 @@ class QQuickCanvasContext;
class QQuickCanvasItemPrivate;
class QQuickPixmap;
-class QQmlV4Function;
-class QQuickCanvasPixmap : public QQmlRefCount
+class QQuickCanvasPixmap final : public QQmlRefCounted<QQuickCanvasPixmap>
{
public:
QQuickCanvasPixmap(const QImage& image);
@@ -110,17 +109,17 @@ public:
QImage toImage(const QRectF& rect = QRectF()) const;
- Q_INVOKABLE void getContext(QQmlV4Function *args);
+ Q_INVOKABLE void getContext(QQmlV4FunctionPtr args);
- Q_INVOKABLE void requestAnimationFrame(QQmlV4Function *args);
- Q_INVOKABLE void cancelRequestAnimationFrame(QQmlV4Function *args);
+ Q_INVOKABLE void requestAnimationFrame(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void cancelRequestAnimationFrame(QQmlV4FunctionPtr args);
Q_INVOKABLE void requestPaint();
Q_INVOKABLE void markDirty(const QRectF& dirtyRect = QRectF());
Q_INVOKABLE bool save(const QString &filename, const QSizeF &imageSize = QSizeF()) const;
Q_INVOKABLE QString toDataURL(const QString& type = QLatin1String("image/png")) const;
- QQmlRefPointer<QQuickCanvasPixmap> loadedPixmap(const QUrl& url);
+ QQmlRefPointer<QQuickCanvasPixmap> loadedPixmap(const QUrl& url, QSizeF sourceSize = QSizeF());
bool isTextureProvider() const override;
QSGTextureProvider *textureProvider() const override;
@@ -139,7 +138,7 @@ Q_SIGNALS:
void imageLoaded();
public Q_SLOTS:
- void loadImage(const QUrl& url);
+ void loadImage(const QUrl& url, QSizeF sourceSize = QSizeF());
void unloadImage(const QUrl& url);
bool isImageLoaded(const QUrl& url) const;
bool isImageLoading(const QUrl& url) const;
@@ -186,6 +185,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCanvasItem)
-
#endif //QQUICKCANVASITEM_P_H
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index b9354b0ac2..1a6391a270 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -44,6 +44,8 @@
#include <private/qsgdefaultrendercontext_p.h>
+#include <QtCore/qpointer.h>
+
#include <cmath>
#if defined(Q_OS_QNX) || defined(Q_OS_ANDROID)
#include <ctype.h>
@@ -96,7 +98,7 @@ QT_BEGIN_NAMESPACE
THROW_GENERIC_ERROR("Not a Context2D object");
#define qClamp(val, min, max) qMin(qMax(val, min), max)
#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
-Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name)
+Q_QUICK_EXPORT QColor qt_color_from_string(const QV4::Value &name)
{
QByteArray str = name.toQString().toUtf8();
@@ -932,7 +934,7 @@ void QV4::Heap::QQuickJSContext2DImageData::init()
DEFINE_OBJECT_VTABLE(QQuickJSContext2DImageData);
-static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionEngine *v4, const QImage& image)
+static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionEngine *v4, QImage&& image)
{
QV4::Scope scope(v4);
QQuickContext2DEngineData *ed = engineData(scope.engine);
@@ -946,7 +948,7 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
} else {
// After qtbase 88e56d0932a3615231adf40d5ae033e742d72c33, the image size can be off by one.
Q_ASSERT(qAbs(image.width() - qRound(w * image.devicePixelRatio())) <= 1 && qAbs(image.height() - qRound(h * image.devicePixelRatio())) <= 1);
- *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
+ *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? std::move(image) : std::move(image).convertToFormat(QImage::Format_ARGB32);
}
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DImageData>());
@@ -1289,29 +1291,81 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_globalAlpha(const QV4::Function
/*!
\qmlproperty string QtQuick::Context2D::globalCompositeOperation
- Holds the current the current composition operation, from the list below:
- \list
- \li source-atop - A atop B. Display the source image wherever both images are opaque.
- Display the destination image wherever the destination image is opaque but the source image is transparent.
- Display transparency elsewhere.
- \li source-in - A in B. Display the source image wherever both the source image and destination image are opaque.
- Display transparency elsewhere.
- \li source-out - A out B. Display the source image wherever the source image is opaque and the destination image is transparent.
- Display transparency elsewhere.
- \li source-over - (default) A over B. Display the source image wherever the source image is opaque.
- Display the destination image elsewhere.
- \li destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
- \li destination-in - B in A. Same as source-in but using the destination image instead of the source image and vice versa.
- \li destination-out - B out A. Same as source-out but using the destination image instead of the source image and vice versa.
- \li destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa.
- \li lighter - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
- \li copy - A (B is ignored). Display the source image instead of the destination image.
- \li xor - A xor B. Exclusive OR of the source image and destination image.
- \endlist
-
- Additionally, this property also accepts the compositon modes listed in QPainter::CompositionMode. According to the W3C standard, these
- extension composition modes are provided as "vendorName-operationName" syntax, for example: QPainter::CompositionMode_Exclusion is provided as
- "qt-exclusion".
+ Holds the current the current composition operation. Allowed operations are:
+
+ \value "source-atop"
+ QPainter::CompositionMode_SourceAtop
+ A atop B. Display the source image wherever both images are opaque.
+ Display the destination image wherever the destination image is opaque
+ but the source image is transparent. Display transparency elsewhere.
+ \value "source-in"
+ QPainter::CompositionMode_SourceIn
+ A in B. Display the source image wherever both the source image and
+ destination image are opaque. Display transparency elsewhere.
+ \value "source-out"
+ QPainter::CompositionMode_SourceOut
+ A out B. Display the source image wherever the source image is opaque
+ and the destination image is transparent. Display transparency elsewhere.
+ \value "source-over"
+ QPainter::CompositionMode_SourceOver (default)
+ A over B. Display the source image wherever the source image is opaque.
+ Display the destination image elsewhere.
+ \value "destination-atop"
+ QPainter::CompositionMode_DestinationAtop
+ B atop A. Same as \c source-atop but using the destination image instead
+ of the source image and vice versa.
+ \value "destination-in"
+ QPainter::CompositionMode_DestinationIn
+ B in A. Same as \c source-in but using the destination image instead of
+ the source image and vice versa.
+ \value "destination-out"
+ QPainter::CompositionMode_DestinationOut
+ B out A. Same as \c source-out but using the destination image instead
+ of the source image and vice versa.
+ \value "destination-over"
+ QPainter::CompositionMode_DestinationOver
+ B over A. Same as \c source-over but using the destination image
+ instead of the source image and vice versa.
+ \value "lighter"
+ QPainter::CompositionMode_Plus
+ A plus B. Display the sum of the source image and destination image,
+ with color values approaching \c 255 (100%) as a limit.
+ \value "copy"
+ QPainter::CompositionMode_Source
+ A (B is ignored). Display the source image instead of the destination image.
+ \value "xor"
+ QPainter::CompositionMode_Xor
+ A xor B. Exclusive OR of the source image and destination image.
+ \value "qt-clear"
+ QPainter::CompositionMode_Clear
+ \value "qt-destination"
+ QPainter::CompositionMode_Destination
+ \value "qt-multiply"
+ QPainter::CompositionMode_Multiply
+ \value "qt-screen"
+ QPainter::CompositionMode_Screen
+ \value "qt-overlay"
+ QPainter::CompositionMode_Overlay
+ \value "qt-darken"
+ QPainter::CompositionMode_Darken
+ \value "qt-lighten"
+ QPainter::CompositionMode_Lighten
+ \value "qt-color-dodge"
+ QPainter::CompositionMode_ColorDodge
+ \value "qt-color-burn"
+ QPainter::CompositionMode_ColorBurn
+ \value "qt-hard-light"
+ QPainter::CompositionMode_HardLight
+ \value "qt-soft-light"
+ QPainter::CompositionMode_SoftLight
+ \value "qt-difference"
+ QPainter::CompositionMode_Difference
+ \value "qt-exclusion"
+ QPainter::CompositionMode_Exclusion
+
+ In compliance with the W3C standard, the extended composition modes beyond
+ the required modes are provided as "vendorName-operationName" syntax, for
+ example: QPainter::CompositionMode_Exclusion is provided as "qt-exclusion".
*/
QV4::ReturnedValue QQuickJSContext2D::method_get_globalCompositeOperation(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
@@ -1422,18 +1476,19 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(const QV4::FunctionOb
}
RETURN_UNDEFINED();
}
+
/*!
\qmlproperty enumeration QtQuick::Context2D::fillRule
- Holds the current fill rule used for filling shapes. The following fill rules are supported:
- \list
- \li Qt.OddEvenFill
- \li Qt.WindingFill
- \endlist
- Note: Unlike the QPainterPath, the Canvas API uses the winding fill as the default fill rule.
- The fillRule property is part of the context rendering state.
+ Holds the current fill rule used for filling shapes. The following fill rules are supported:
- \sa fillStyle
- */
+ \value Qt.OddEvenFill Qt::OddEvenFill
+ \value Qt.WindingFill (default) Qt::WindingFill
+
+ \note Unlike QPainterPath, the Canvas API uses the winding fill as the default fill rule.
+ The fillRule property is part of the context rendering state.
+
+ \sa fillStyle
+*/
QV4::ReturnedValue QQuickJSContext2D::method_get_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
QV4::Scope scope(b);
@@ -1687,48 +1742,49 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(cons
}
/*!
- \qmlmethod variant QtQuick::Context2D::createPattern(color color, enumeration patternMode)
- This is an overloaded function.
- Returns a CanvasPattern object that uses the given \a color and \a patternMode.
- The valid pattern modes are:
- \list
- \li Qt.SolidPattern
- \li Qt.Dense1Pattern
- \li Qt.Dense2Pattern
- \li Qt.Dense3Pattern
- \li Qt.Dense4Pattern
- \li Qt.Dense5Pattern
- \li Qt.Dense6Pattern
- \li Qt.Dense7Pattern
- \li Qt.HorPattern
- \li Qt.VerPattern
- \li Qt.CrossPattern
- \li Qt.BDiagPattern
- \li Qt.FDiagPattern
- \li Qt.DiagCrossPattern
-\endlist
+ \qmlmethod variant QtQuick::Context2D::createPattern(color color, enumeration patternMode)
+ This is an overloaded function.
+ Returns a CanvasPattern object that uses the given \a color and \a patternMode.
+ The valid pattern modes are:
+
+ \value Qt.SolidPattern Qt::SolidPattern
+ \value Qt.Dense1Pattern Qt::Dense1Pattern
+ \value Qt.Dense2Pattern Qt::Dense2Pattern
+ \value Qt.Dense3Pattern Qt::Dense3Pattern
+ \value Qt.Dense4Pattern Qt::Dense4Pattern
+ \value Qt.Dense5Pattern Qt::Dense5Pattern
+ \value Qt.Dense6Pattern Qt::Dense6Pattern
+ \value Qt.Dense7Pattern Qt::Dense7Pattern
+ \value Qt.HorPattern Qt::HorPattern
+ \value Qt.VerPattern Qt::VerPattern
+ \value Qt.CrossPattern Qt::CrossPattern
+ \value Qt.BDiagPattern Qt::BDiagPattern
+ \value Qt.FDiagPattern Qt::FDiagPattern
+ \value Qt.DiagCrossPattern Qt::DiagCrossPattern
+
\sa Qt::BrushStyle
- */
+*/
/*!
- \qmlmethod variant QtQuick::Context2D::createPattern(Image image, string repetition)
- Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument.
+ \qmlmethod variant QtQuick::Context2D::createPattern(Image image, string repetition)
+ Returns a CanvasPattern object that uses the given image and repeats in the
+ direction(s) given by the repetition argument.
- The \a image parameter must be a valid Image item, a valid CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception.
+ The \a image parameter must be a valid Image item, a valid CanvasImageData
+ object or loaded image url. If there is no image data, thus function throws an
+ INVALID_STATE_ERR exception.
- The allowed values for \a repetition are:
+ The allowed values for \a repetition are:
- \list
- \li "repeat" - both directions
- \li "repeat-x - horizontal only
- \li "repeat-y" - vertical only
- \li "no-repeat" - neither
- \endlist
+ \value "repeat" both directions
+ \value "repeat-x horizontal only
+ \value "repeat-y" vertical only
+ \value "no-repeat" neither
- If the repetition argument is empty or null, the value "repeat" is used.
+ If the repetition argument is empty or null, the value "repeat" is used.
- \sa strokeStyle
- \sa fillStyle
- */
+ \sa strokeStyle
+ \sa fillStyle
+*/
QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
{
QV4::Scope scope(b);
@@ -1792,13 +1848,20 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::F
// line styles
/*!
\qmlproperty string QtQuick::Context2D::lineCap
- Holds the current line cap style.
- The possible line cap styles are:
- \list
- \li butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value.
- \li round - a semi-circle with the diameter equal to the width of the line must then be added on to the end of the line.
- \li square - a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line.
- \endlist
+ Holds the current line cap style.
+ The possible line cap styles are:
+
+ \value "butt"
+ (default) Qt::FlatCap the end of each line has a flat edge
+ perpendicular to the direction of the line.
+ \value "round"
+ Qt::RoundCap a semi-circle with the diameter equal to the width of the
+ line is added on to the end of the line.
+ \value "square"
+ Qt::SquareCap a rectangle with the length of the line width and the
+ width of half the line width, placed flat against the edge
+ perpendicular to the direction of the line.
+
Other values are ignored.
*/
QV4::ReturnedValue QQuickJSContext2D::method_get_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
@@ -1848,16 +1911,18 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_lineCap(const QV4::FunctionObje
/*!
\qmlproperty string QtQuick::Context2D::lineJoin
- Holds the current line join style. A join exists at any point in a subpath
- shared by two consecutive lines. When a subpath is closed, then a join also exists
- at its first point (equivalent to its last point) connecting the first and last lines in the subpath.
+ Holds the current line join style. A join exists at any point in a subpath
+ shared by two consecutive lines. When a subpath is closed, then a join also
+ exists at its first point (equivalent to its last point) connecting the
+ first and last lines in the subpath.
The possible line join styles are:
- \list
- \li bevel - this is all that is rendered at joins.
- \li round - a filled arc connecting the two aforementioned corners of the join, abutting (and not overlapping) the aforementioned triangle, with the diameter equal to the line width and the origin at the point of the join, must be rendered at joins.
- \li miter - a second filled triangle must (if it can given the miter length) be rendered at the join, this is the default line join style.
- \endlist
+
+ \value "bevel" Qt::BevelJoin The triangular notch between the two lines is filled.
+ \value "round" Qt::RoundJoin A circular arc between the two lines is filled.
+ \value "miter" (default) Qt::MiterJoin The outer edges of the lines are extended to
+ meet at an angle, and this area is filled.
+
Other values are ignored.
*/
QV4::ReturnedValue QQuickJSContext2D::method_get_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
@@ -2306,8 +2371,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::Func
\image qml-item-canvas-startAngle.png
- The \a anticlockwise parameter is \c true for each arc in the figure above
- because they are all drawn in the anticlockwise direction.
+ The \a anticlockwise parameter is \c false for each arc in the figure above
+ because they are all drawn in the clockwise direction.
\sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D
Context Standard for arc()}
@@ -2790,19 +2855,20 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_font(const QV4::FunctionObject
}
/*!
- \qmlproperty string QtQuick::Context2D::textAlign
-
- Holds the current text alignment settings.
- The possible values are:
- \list
- \li start
- \li end
- \li left
- \li right
- \li center
- \endlist
- Other values are ignored. The default value is "start".
- */
+ \qmlproperty string QtQuick::Context2D::textAlign
+
+ Holds the current text alignment settings. The possible values are:
+
+ \value "start" (default) Align to the start edge of the text (left side in
+ left-to-right text, right side in right-to-left text).
+ \value "end" Align to the end edge of the text (right side in left-to-right
+ text, left side in right-to-left text).
+ \value "left" Qt::AlignLeft
+ \value "right" Qt::AlignRight
+ \value "center" Qt::AlignHCenter
+
+ Other values are ignored.
+*/
QV4::ReturnedValue QQuickJSContext2D::method_get_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
QV4::Scope scope(b);
@@ -2857,20 +2923,19 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(const QV4::FunctionOb
}
/*!
- \qmlproperty string QtQuick::Context2D::textBaseline
-
- Holds the current baseline alignment settings.
- The possible values are:
- \list
- \li top
- \li hanging
- \li middle
- \li alphabetic
- \li ideographic
- \li bottom
- \endlist
- Other values are ignored. The default value is "alphabetic".
- */
+ \qmlproperty string QtQuick::Context2D::textBaseline
+
+ Holds the current baseline alignment settings. The possible values are:
+
+ \value "top" The top of the em square
+ \value "hanging" The hanging baseline
+ \value "middle" The middle of the em square
+ \value "alphabetic" (default) The alphabetic baseline
+ \value "ideographic" The ideographic-under baseline
+ \value "bottom" The bottom of the em square
+
+ Other values are ignored. The default value is "alphabetic".
+*/
QV4::ReturnedValue QQuickJSContext2D::method_get_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
QV4::Scope scope(b);
@@ -3347,7 +3412,7 @@ bool QQuickJSContext2DPixelData::virtualPut(QV4::Managed *m, QV4::PropertyKey id
/*!
\qmlmethod CanvasImageData QtQuick::Context2D::createImageData(CanvasImageData imageData)
- Creates a CanvasImageData object with the same dimensions as the argument.
+ Creates a CanvasImageData object with the same dimensions as the \a imageData argument.
*/
/*!
\qmlmethod CanvasImageData QtQuick::Context2D::createImageData(Url imageUrl)
@@ -3378,7 +3443,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(const QV4:
}
} else if (arg0->isString()) {
QImage image = r->d()->context()->createPixmap(QUrl(arg0->toQStringNoThrow()))->image();
- RETURN_RESULT(qt_create_image_data(image.width(), image.height(), scope.engine, image));
+ RETURN_RESULT(qt_create_image_data(image.width(), image.height(), scope.engine, std::move(image)));
}
} else if (argc == 2) {
qreal w = argv[0].toNumber();
@@ -3419,7 +3484,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_getImageData(const QV4::Fu
THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments");
QImage image = r->d()->context()->canvas()->toImage(QRectF(x, y, w, h));
- RETURN_RESULT(qt_create_image_data(w, h, scope.engine, image));
+ RETURN_RESULT(qt_create_image_data(w, h, scope.engine, std::move(image)));
}
RETURN_RESULT(QV4::Encode::null());
}
@@ -3534,7 +3599,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(const QV4::Fu
\code
var gradient = ctx.createLinearGradient(0, 0, 100, 100);
gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1));
- gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1');
+ gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1)');
\endcode
*/
QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
@@ -4076,6 +4141,7 @@ static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetr
break;
case QQuickContext2D::Right:
offset = metrics.horizontalAdvance(text);
+ break;
case QQuickContext2D::Left:
default:
break;
@@ -4089,9 +4155,9 @@ void QQuickContext2D::setGrabbedImage(const QImage& grab)
m_grabbed = true;
}
-QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url)
+QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url, QSizeF sourceSize)
{
- return m_canvas->loadedPixmap(url);
+ return m_canvas->loadedPixmap(url, sourceSize);
}
QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index 9ae769f504..50917d3ecf 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -212,7 +212,7 @@ public:
bool isPointInPath(qreal x, qreal y) const;
QPainterPath createTextGlyphs(qreal x, qreal y, const QString& text);
- QQmlRefPointer<QQuickCanvasPixmap> createPixmap(const QUrl& url);
+ QQmlRefPointer<QQuickCanvasPixmap> createPixmap(const QUrl& url, QSizeF sourceSize = QSizeF());
QSurface *surface() const { return m_surface.data(); }
void setGrabbedImage(const QImage& grab);
@@ -239,8 +239,6 @@ public:
static QMutex mutex;
};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickContext2D)
#endif // QQUICKCONTEXT2D_P_H
diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h
index 7b70bdb75c..92d1307a9a 100644
--- a/src/quick/items/qquickaccessibleattached_p.h
+++ b/src/quick/items/qquickaccessibleattached_p.h
@@ -45,7 +45,7 @@ QT_BEGIN_NAMESPACE
} \
Q_SIGNAL void P ## Changed(bool arg);
-class Q_QUICK_PRIVATE_EXPORT QQuickAccessibleAttached : public QObject
+class Q_QUICK_EXPORT QQuickAccessibleAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QAccessible::Role role READ role WRITE setRole NOTIFY roleChanged FINAL)
@@ -211,8 +211,6 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAccessibleAttached)
-
#endif // accessibility
#endif
diff --git a/src/quick/items/qquickanchors.cpp b/src/quick/items/qquickanchors.cpp
index dfb8ce0a18..e6de648313 100644
--- a/src/quick/items/qquickanchors.cpp
+++ b/src/quick/items/qquickanchors.cpp
@@ -49,11 +49,7 @@ static inline qreal hcenter(const QQuickItem *item)
if (!QQuickAnchorsPrivate::get(anchors)->centerAligned)
return width / 2;
}
- int iw = width;
- if (iw % 2)
- return (width + 1) / 2;
- else
- return width / 2;
+ return qRound(width / 2);
}
static inline qreal vcenter(const QQuickItem *item)
@@ -63,11 +59,7 @@ static inline qreal vcenter(const QQuickItem *item)
if (!QQuickAnchorsPrivate::get(anchors)->centerAligned)
return height / 2;
}
- int ih = height;
- if (ih % 2)
- return (height + 1) / 2;
- else
- return height / 2;
+ return qRound(height / 2);
}
//local position
diff --git a/src/quick/items/qquickanchors_p.h b/src/quick/items/qquickanchors_p.h
index 1409cfee06..f33e21c5bd 100644
--- a/src/quick/items/qquickanchors_p.h
+++ b/src/quick/items/qquickanchors_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickAnchorsPrivate;
class QQuickAnchorLine;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchors : public QObject
+class Q_QUICK_EXPORT QQuickAnchors : public QObject
{
Q_OBJECT
@@ -180,6 +180,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAnchors::Anchors)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAnchors)
-
#endif // QQUICKANCHORS_P_H
diff --git a/src/quick/items/qquickanchors_p_p.h b/src/quick/items/qquickanchors_p_p.h
index eebfc5c06b..12e6dc465a 100644
--- a/src/quick/items/qquickanchors_p_p.h
+++ b/src/quick/items/qquickanchors_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchorLine
+class Q_QUICK_EXPORT QQuickAnchorLine
{
Q_GADGET
QML_ANONYMOUS
diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp
index e4bc156a30..93f7dd8340 100644
--- a/src/quick/items/qquickanimatedimage.cpp
+++ b/src/quick/items/qquickanimatedimage.cpp
@@ -106,6 +106,26 @@ void QQuickAnimatedImagePrivate::clearCache()
with QQuickImageProvider.
*/
+/*!
+ \qmlproperty size QtQuick::AnimatedImage::sourceSize
+
+ This property holds the scaled width and height of the full-frame image.
+
+ Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
+ the painting of the image, this property sets the maximum number of pixels
+ stored for cached frames so that large animations do not use more
+ memory than necessary.
+
+ If the original size is larger than \c sourceSize, the image is scaled down.
+
+ The natural size of the image can be restored by setting this property to
+ \c undefined.
+
+ \note \e {Changing this property dynamically causes the image source to be reloaded,
+ potentially even from the network, if it is not in the disk cache.}
+
+ \sa Image::sourceSize
+*/
QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent)
: QQuickImage(*(new QQuickAnimatedImagePrivate), parent)
{
@@ -287,10 +307,7 @@ void QQuickAnimatedImage::load()
Q_D(QQuickAnimatedImage);
if (d->url.isEmpty()) {
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(d->progress);
- }
+ d->setProgress(0);
d->setImage(QImage());
if (sourceSize() != d->oldSourceSize) {
@@ -298,9 +315,7 @@ void QQuickAnimatedImage::load()
emit sourceSizeChanged();
}
- d->status = Null;
- emit statusChanged(d->status);
-
+ d->setStatus(Null);
if (isPlaying() != d->oldPlaying)
emit playingChanged();
} else {
@@ -313,19 +328,18 @@ void QQuickAnimatedImage::load()
resolve2xLocalFile(resolvedUrl, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio);
QString lf = QQmlFile::urlToLocalFileOrQrc(loadUrl);
+ d->status = Null; // reset status, no emit
+
if (!lf.isEmpty()) {
d->setMovie(new QMovie(lf));
movieRequestFinished();
} else {
#if QT_CONFIG(qml_network)
- if (d->status != Loading) {
- d->status = Loading;
- emit statusChanged(d->status);
- }
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(d->progress);
- }
+ if (d->reply)
+ return;
+
+ d->setStatus(Loading);
+ d->setProgress(0);
QNetworkRequest req(d->url);
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
@@ -337,27 +351,21 @@ void QQuickAnimatedImage::load()
}
}
-#define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16
-
void QQuickAnimatedImage::movieRequestFinished()
{
Q_D(QQuickAnimatedImage);
#if QT_CONFIG(qml_network)
if (d->reply) {
- d->redirectCount++;
- if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
- QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = d->reply->url().resolved(redirect.toUrl());
- d->reply->deleteLater();
- setSource(url);
- return;
- }
- }
+ auto movie = new QMovie(d->reply);
+ // From this point, we no longer need to handle the reply.
+ // I.e. it will be used only as a data source for QMovie,
+ // so it should live as long as the movie lives.
+ d->reply->disconnect(this);
+ d->reply->setParent(movie);
+ d->reply = nullptr;
- d->redirectCount=0;
- d->setMovie(new QMovie(d->reply));
+ d->setMovie(movie);
}
#endif
@@ -373,13 +381,8 @@ void QQuickAnimatedImage::movieRequestFinished()
emit sourceSizeChanged();
}
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(d->progress);
- }
-
- d->status = Error;
- emit statusChanged(d->status);
+ d->setProgress(0);
+ d->setStatus(Error);
if (isPlaying() != d->oldPlaying)
emit playingChanged();
@@ -392,10 +395,7 @@ void QQuickAnimatedImage::movieRequestFinished()
d->movie->setCacheMode(QMovie::CacheAll);
d->movie->setSpeed(qRound(d->speed * 100.0));
- if (d->progress != 1.0) {
- d->progress = 1.0;
- emit progressChanged(d->progress);
- }
+ d->setProgress(1);
bool pausedAtStart = d->paused;
if (d->movie && d->playing)
@@ -416,8 +416,7 @@ void QQuickAnimatedImage::movieRequestFinished()
}
}
- d->status = Ready;
- emit statusChanged(d->status);
+ d->setStatus(Ready);
if (isPlaying() != d->oldPlaying)
emit playingChanged();
diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h
index 38beb6d1c3..4d8be4068a 100644
--- a/src/quick/items/qquickanimatedimage_p.h
+++ b/src/quick/items/qquickanimatedimage_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QMovie;
class QQuickAnimatedImagePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimatedImage : public QQuickImage
+class Q_QUICK_EXPORT QQuickAnimatedImage : public QQuickImage
{
Q_OBJECT
@@ -85,6 +85,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAnimatedImage)
-
#endif // QQUICKANIMATEDIMAGE_P_H
diff --git a/src/quick/items/qquickanimatedimage_p_p.h b/src/quick/items/qquickanimatedimage_p_p.h
index 2b01b4365d..1bd5729b93 100644
--- a/src/quick/items/qquickanimatedimage_p_p.h
+++ b/src/quick/items/qquickanimatedimage_p_p.h
@@ -34,11 +34,7 @@ class QQuickAnimatedImagePrivate : public QQuickImagePrivate
public:
QQuickAnimatedImagePrivate()
- : playing(true), paused(false), oldPlaying(false), padding(0)
- , presetCurrentFrame(0), speed(1.0), movie(nullptr)
-#if QT_CONFIG(qml_network)
- , reply(nullptr), redirectCount(0)
-#endif
+ : playing(true), paused(false), oldPlaying(false)
{
}
@@ -46,18 +42,18 @@ public:
void setMovie(QMovie *movie);
void clearCache();
+ qreal speed = 1;
+ QMovie *movie = nullptr;
+ int presetCurrentFrame = 0;
+ QMap<int, QQuickPixmap *> frameMap;
+
+#if QT_CONFIG(qml_network)
+ QNetworkReply *reply = nullptr;
+#endif
+
bool playing : 1;
bool paused : 1;
bool oldPlaying : 1;
- unsigned padding: 29;
- int presetCurrentFrame;
- qreal speed;
- QMovie *movie;
-#if QT_CONFIG(qml_network)
- QNetworkReply *reply;
- int redirectCount;
-#endif
- QMap<int, QQuickPixmap *> frameMap;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index 26358cd7ef..8f4c6ff7fc 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -31,7 +31,7 @@ class QQuickSpriteEngine;
class QSGGeometryNode;
class QQuickAnimatedSpritePrivate;
class QSGSpriteNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimatedSprite : public QQuickItem
+class Q_QUICK_EXPORT QQuickAnimatedSprite : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp
index c8ebc59356..40daf518d3 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -63,9 +63,8 @@ QT_BEGIN_NAMESPACE
\image qml-borderimage-normal-image.png
\endfloat
- An unscaled image is displayed using an Image. The \l border property is
- used to determine the parts of the image that will lie inside the unscaled corner
- areas and the parts that will be stretched horizontally and vertically.
+ For comparison, an unscaled image is displayed using a simple Image item.
+ Here we have overlaid lines to show how we'd like to break it up with BorderImage:
\snippet qml/borderimage/normal-image.qml normal image
@@ -74,12 +73,15 @@ QT_BEGIN_NAMESPACE
\image qml-borderimage-scaled.png
\endfloat
- A BorderImage is used to display the image, and it is given a size that is
+ But when a BorderImage is used to display the image, the \l border property is
+ used to determine the parts of the image that will lie inside the unscaled corner
+ areas, and the parts that will be stretched horizontally and vertically.
+ Then, you can give it a size that is
larger than the original image. Since the \l horizontalTileMode property is set to
\l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in
regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property
is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image
- in regions 4 and 6 are stretched vertically.
+ in regions 4 and 6 are stretched vertically:
\snippet qml/borderimage/borderimage-scaled.qml scaled border image
@@ -92,17 +94,25 @@ QT_BEGIN_NAMESPACE
\l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat},
the parts of image in regions 2 and 8 are tiled so that they fill the space at the
top and bottom of the item. Similarly, the \l verticalTileMode property is set to
- \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions
- 4 and 6 are tiled so that they fill the space at the left and right of the item.
+ \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, so the parts of image in regions
+ 4 and 6 are tiled to fill the space at the left and right of the item:
\snippet qml/borderimage/borderimage-tiled.qml tiled border image
\clearfloat
+ \beginfloatleft
+ \image qml-borderimage-rounded.png
+ \endfloat
+
In some situations, the width of regions 2 and 8 may not be an exact multiple of the width
of the corresponding regions in the source image. Similarly, the height of regions 4 and 6
- may not be an exact multiple of the height of the corresponding regions. It can be useful
- to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of
- \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these.
+ may not be an exact multiple of the height of the corresponding regions. If you use
+ \l{BorderImage::horizontalTileMode}{BorderImage.Round} mode, it will choose an integer
+ number of tiles and shrink them to fit:
+
+ \snippet qml/borderimage/borderimage-rounded.qml tiled border image
+
+ \clearfloat
The Border Image example in \l{Qt Quick Examples - Image Elements} shows how a BorderImage
can be used to simulate a shadow effect on a rectangular item.
@@ -149,12 +159,10 @@ QQuickBorderImage::~QQuickBorderImage()
This property describes the status of image loading. It can be one of:
- \list
- \li BorderImage.Null - no image has been set
- \li BorderImage.Ready - the image has been loaded
- \li BorderImage.Loading - the image is currently being loaded
- \li BorderImage.Error - an error occurred while loading the image
- \endlist
+ \value BorderImage.Null No image has been set
+ \value BorderImage.Ready The image has been loaded
+ \value BorderImage.Loading The image is currently being loaded
+ \value BorderImage.Error An error occurred while loading the image
\sa progress
*/
@@ -268,20 +276,19 @@ void QQuickBorderImage::load()
: d->url);
if (!lf.isEmpty()) {
QFile file(lf);
- file.open(QIODevice::ReadOnly);
- setGridScaledImage(QQuickGridScaledImage(&file));
+ if (!file.open(QIODevice::ReadOnly))
+ d->setStatus(Error);
+ else
+ setGridScaledImage(QQuickGridScaledImage(&file));
} else {
#if QT_CONFIG(qml_network)
- if (d->progress != 0.0) {
- d->progress = 0.0;
- emit progressChanged(d->progress);
- }
- d->status = Loading;
+ d->setProgress(0);
+ d->setStatus(Loading);
+
QNetworkRequest req(d->url);
d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
qmlobject_connect(d->sciReply, QNetworkReply, SIGNAL(finished()),
this, QQuickBorderImage, SLOT(sciRequestFinished()));
- emit statusChanged(d->status);
#endif
}
} else {
@@ -332,11 +339,9 @@ QQuickScaleGrid *QQuickBorderImage::border()
This property describes how to repeat or stretch the middle parts of the border image.
- \list
- \li BorderImage.Stretch - Scales the image to fit to the available area.
- \li BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
- \li BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
- \endlist
+ \value BorderImage.Stretch Scales the image to fit to the available area.
+ \value BorderImage.Repeat Tile the image until there is no more space. May crop the last image.
+ \value BorderImage.Round Like Repeat, but scales the images down to ensure that the last image is not cropped.
The default tile mode for each property is BorderImage.Stretch.
*/
@@ -376,8 +381,7 @@ void QQuickBorderImage::setGridScaledImage(const QQuickGridScaledImage& sci)
{
Q_D(QQuickBorderImage);
if (!sci.isValid()) {
- d->status = Error;
- emit statusChanged(d->status);
+ d->setStatus(Error);
} else {
QQuickScaleGrid *sg = border();
sg->setTop(sci.gridTop());
@@ -396,24 +400,18 @@ void QQuickBorderImage::requestFinished()
{
Q_D(QQuickBorderImage);
- QSize impsize = d->pix.implicitSize();
+ const QSize impsize = d->pix.implicitSize();
+ setImplicitSize(impsize.width() / d->devicePixelRatio, impsize.height() / d->devicePixelRatio);
+
if (d->pix.isError()) {
- d->status = Error;
qmlWarning(this) << d->pix.error();
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(d->progress);
- }
+ d->setStatus(Error);
+ d->setProgress(0);
} else {
- d->status = Ready;
- if (d->progress != 1.0) {
- d->progress = 1.0;
- emit progressChanged(d->progress);
- }
+ d->setStatus(Ready);
+ d->setProgress(1);
}
- setImplicitSize(impsize.width() / d->devicePixelRatio, impsize.height() / d->devicePixelRatio);
- emit statusChanged(d->status);
if (sourceSize() != d->oldSourceSize) {
d->oldSourceSize = sourceSize();
emit sourceSizeChanged();
@@ -427,28 +425,14 @@ void QQuickBorderImage::requestFinished()
}
#if QT_CONFIG(qml_network)
-#define BORDERIMAGE_MAX_REDIRECT 16
-
void QQuickBorderImage::sciRequestFinished()
{
Q_D(QQuickBorderImage);
- d->redirectCount++;
- if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) {
- QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = d->sciReply->url().resolved(redirect.toUrl());
- setSource(url);
- return;
- }
- }
- d->redirectCount=0;
-
if (d->sciReply->error() != QNetworkReply::NoError) {
- d->status = Error;
+ d->setStatus(Error);
d->sciReply->deleteLater();
d->sciReply = nullptr;
- emit statusChanged(d->status);
} else {
QQuickGridScaledImage sci(d->sciReply);
d->sciReply->deleteLater();
diff --git a/src/quick/items/qquickborderimage_p.h b/src/quick/items/qquickborderimage_p.h
index 92dd90b654..3c87955189 100644
--- a/src/quick/items/qquickborderimage_p.h
+++ b/src/quick/items/qquickborderimage_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickScaleGrid;
class QQuickGridScaledImage;
class QQuickBorderImagePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickBorderImage : public QQuickImageBase
+class Q_QUICK_EXPORT QQuickBorderImage : public QQuickImageBase
{
Q_OBJECT
@@ -77,6 +77,5 @@ private:
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickBorderImage)
#endif // QQUICKBORDERIMAGE_P_H
diff --git a/src/quick/items/qquickborderimage_p_p.h b/src/quick/items/qquickborderimage_p_p.h
index 82867d9385..02d5ac9067 100644
--- a/src/quick/items/qquickborderimage_p_p.h
+++ b/src/quick/items/qquickborderimage_p_p.h
@@ -31,11 +31,6 @@ class QQuickBorderImagePrivate : public QQuickImageBasePrivate
public:
QQuickBorderImagePrivate()
- : border(0), horizontalTileMode(QQuickBorderImage::Stretch),
- verticalTileMode(QQuickBorderImage::Stretch), pixmapChanged(false)
-#if QT_CONFIG(qml_network)
- , sciReply(0), redirectCount(0)
-#endif
{
}
@@ -43,7 +38,6 @@ public:
{
}
-
QQuickScaleGrid *getScaleGrid()
{
Q_Q(QQuickBorderImage);
@@ -66,15 +60,14 @@ public:
QRectF *innerSourceRect,
QRectF *subSourceRect);
- QQuickScaleGrid *border;
QUrl sciurl;
- QQuickBorderImage::TileMode horizontalTileMode;
- QQuickBorderImage::TileMode verticalTileMode;
- bool pixmapChanged : 1;
+ QQuickScaleGrid *border = nullptr;
+ QQuickBorderImage::TileMode horizontalTileMode = QQuickBorderImage::Stretch;
+ QQuickBorderImage::TileMode verticalTileMode = QQuickBorderImage::Stretch;
+ bool pixmapChanged = false;
#if QT_CONFIG(qml_network)
- QNetworkReply *sciReply;
- int redirectCount;
+ QNetworkReply *sciReply = nullptr;
#endif
};
diff --git a/src/quick/items/qquickclipnode_p.h b/src/quick/items/qquickclipnode_p.h
index 953d0da08b..37b1b1778f 100644
--- a/src/quick/items/qquickclipnode_p.h
+++ b/src/quick/items/qquickclipnode_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickDefaultClipNode : public QSGClipNode
+class Q_QUICK_EXPORT QQuickDefaultClipNode : public QSGClipNode
{
public:
QQuickDefaultClipNode(const QRectF &);
diff --git a/src/quick/items/qquickcolorgroup.cpp b/src/quick/items/qquickcolorgroup.cpp
index fd913aba7a..bc1b4dee5a 100644
--- a/src/quick/items/qquickcolorgroup.cpp
+++ b/src/quick/items/qquickcolorgroup.cpp
@@ -45,9 +45,21 @@ QT_BEGIN_NAMESPACE
base: "green"
}
\endcode
+
+ The \l Palette type exposes color groups for each QML item state.
*/
/*!
+ \qmlproperty color QtQuick::ColorGroup::accent
+ \since 6.6
+
+ A color that typically contrasts or compliments \l base, \l window, and \l
+ button colors. It usually represents the users' choice of desktop
+ personalisation. Styling of interactive components is a typical use case.
+ Unless explicitly set, it defaults to \l highlight.
+*/
+
+/*!
\qmlproperty color QtQuick::ColorGroup::alternateBase
Used as the alternate background color in item views with alternating row colors.
@@ -180,8 +192,6 @@ QT_BEGIN_NAMESPACE
Additional signal indicates that the current state of this color group
has been changed. Usually it means that one of the colors is changed.
-
- \sa Palette
*/
/*!
@@ -499,6 +509,21 @@ void QQuickColorGroup::resetPlaceholderText()
resetColor(QPalette::PlaceholderText, &QQuickColorGroup::placeholderTextChanged);
}
+QColor QQuickColorGroup::accent() const
+{
+ return color(QPalette::Accent);
+}
+
+void QQuickColorGroup::setAccent(const QColor &color)
+{
+ setColor(QPalette::Accent, color, &QQuickColorGroup::accentChanged);
+}
+
+void QQuickColorGroup::resetAccent()
+{
+ resetColor(QPalette::Accent, &QQuickColorGroup::accentChanged);
+}
+
QPalette::ColorGroup QQuickColorGroup::groupTag() const
{
return m_groupTag;
diff --git a/src/quick/items/qquickcolorgroup_p.h b/src/quick/items/qquickcolorgroup_p.h
index 54c883e1c7..202bba7ee1 100644
--- a/src/quick/items/qquickcolorgroup_p.h
+++ b/src/quick/items/qquickcolorgroup_p.h
@@ -22,12 +22,14 @@
#include <QtQml/qqml.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickPalette;
class QQuickPaletteColorProvider;
-class Q_QUICK_PRIVATE_EXPORT QQuickColorGroup : public QObject
+class Q_QUICK_EXPORT QQuickColorGroup : public QObject
{
Q_OBJECT
@@ -52,6 +54,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickColorGroup : public QObject
Q_PROPERTY(QColor windowText READ windowText WRITE setWindowText RESET resetWindowText NOTIFY windowTextChanged FINAL)
Q_PROPERTY(QColor placeholderText READ placeholderText WRITE setPlaceholderText
RESET resetPlaceholderText NOTIFY placeholderTextChanged REVISION(6, 2) FINAL)
+ Q_PROPERTY(QColor accent READ accent WRITE setAccent RESET resetAccent NOTIFY accentChanged REVISION(6, 6) FINAL)
QML_NAMED_ELEMENT(ColorGroup)
QML_ADDED_IN_VERSION(6, 0)
@@ -144,6 +147,10 @@ public:
void setPlaceholderText(const QColor &color);
void resetPlaceholderText();
+ QColor accent() const;
+ void setAccent(const QColor &color);
+ void resetAccent();
+
QPalette::ColorGroup groupTag() const;
void setGroupTag(QPalette::ColorGroup tag);
@@ -173,6 +180,7 @@ Q_SIGNALS:
void windowChanged();
void windowTextChanged();
Q_REVISION(6, 2) void placeholderTextChanged();
+ Q_REVISION(6, 6) void accentChanged();
void changed();
@@ -197,6 +205,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickColorGroup)
-
#endif // QQUICKCOLORGROUP_H
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index bdca024b1d..47064ad433 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -9,7 +9,7 @@
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <private/qquickitemchangelistener_p.h>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qv4scopedvalue_p.h>
#include <QtCore/qbuffer.h>
#include <QtCore/qmimedata.h>
@@ -55,7 +55,7 @@ using namespace Qt::StringLiterals;
\l {supportedActions}{drop action} chosen by the recipient of the event,
otherwise it will return Qt.IgnoreAction.
- \sa {Qt Quick Examples - Drag and Drop}, {Qt Quick Examples - externaldraganddrop}
+ \sa {Qt Quick Examples - Drag and Drop}
*/
void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change,
@@ -354,7 +354,7 @@ void QQuickDragAttached::setImageSource(const QUrl &url)
if (url.isEmpty()) {
d->pixmapLoader.clear();
} else {
- d->pixmapLoader.load(qmlEngine(parent()), url);
+ d->loadPixmap();
}
Q_EMIT imageSourceChanged();
@@ -362,6 +362,48 @@ void QQuickDragAttached::setImageSource(const QUrl &url)
}
/*!
+ \qmlattachedproperty size QtQuick::Drag::imageSourceSize
+ \since 6.8
+
+ This property holds the size of the image that will be used to represent
+ the data during the drag and drop operation. Changing this property after
+ the drag operation has started will have no effect.
+
+ This property sets the maximum number of pixels stored for the loaded
+ image so that large images do not use more memory than necessary.
+ See \l {QtQuick::Image::sourceSize}{Image.sourceSize} for more details.
+
+ The example below shows an SVG image rendered at one size, and re-renders
+ it at a different size for the drag image:
+
+ \snippet qml/externalDragScaledImage.qml 0
+
+ \sa imageSource, Item::grabToImage()
+*/
+
+QSize QQuickDragAttached::imageSourceSize() const
+{
+ Q_D(const QQuickDragAttached);
+ int width = d->imageSourceSize.width();
+ int height = d->imageSourceSize.height();
+ return QSize(width != -1 ? width : d->pixmapLoader.width(),
+ height != -1 ? height : d->pixmapLoader.height());
+}
+
+void QQuickDragAttached::setImageSourceSize(const QSize &size)
+{
+ Q_D(QQuickDragAttached);
+ if (d->imageSourceSize != size) {
+ d->imageSourceSize = size;
+
+ if (!d->imageSource.isEmpty())
+ d->loadPixmap();
+
+ Q_EMIT imageSourceSizeChanged();
+ }
+}
+
+/*!
\qmlattachedproperty stringlist QtQuick::Drag::keys
This property holds a list of keys that can be used by a DropArea to filter drag events.
@@ -477,11 +519,9 @@ void QQuickDragAttached::setProposedAction(Qt::DropAction action)
A drag can also be started manually using \l startDrag.
- \list
- \li Drag.None - do not start drags automatically
- \li Drag.Automatic - start drags automatically
- \li Drag.Internal (default) - start backwards compatible drags automatically
- \endlist
+ \value Drag.None do not start drags automatically
+ \value Drag.Automatic start drags automatically
+ \value Drag.Internal (default) start backwards compatible drags automatically
When using \c Drag.Automatic you should also define \l mimeData and bind the
\l active property to the active property of MouseArea : \l {MouseArea::drag.active}
@@ -540,7 +580,7 @@ void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
property for the started sequence.
*/
-void QQuickDragAttached::start(QQmlV4Function *args)
+void QQuickDragAttached::start(QQmlV4FunctionPtr args)
{
Q_D(QQuickDragAttached);
if (d->inEvent) {
@@ -576,12 +616,10 @@ void QQuickDragAttached::start(QQmlV4Function *args)
The returned drop action may be one of:
- \list
- \li Qt.CopyAction Copy the data to the target
- \li Qt.MoveAction Move the data from the source to the target
- \li Qt.LinkAction Create a link from the source to the target.
- \li Qt.IgnoreAction Ignore the action (do nothing with the data).
- \endlist
+ \value Qt.CopyAction Copy the data to the target
+ \value Qt.MoveAction Move the data from the source to the target
+ \value Qt.LinkAction Create a link from the source to the target.
+ \value Qt.IgnoreAction Ignore the action (do nothing with the data).
*/
int QQuickDragAttached::drop()
@@ -705,11 +743,10 @@ QMimeData *QQuickDragAttachedPrivate::createMimeData() const
else
qmlWarning(q) << "Don't know how to encode text as " << mimeType;
} else {
- mimeData->setData(mimeType, text.toUtf8().constData());
+ mimeData->setData(mimeType, text.toUtf8());
}
} else {
- qmlWarning(q) << "Mime data contains a string, but mime type " << mimeType
- << " is not a supported text type";
+ mimeData->setData(mimeType, text.toUtf8());
}
break;
}
@@ -765,6 +802,17 @@ QMimeData *QQuickDragAttachedPrivate::createMimeData() const
return mimeData;
}
+void QQuickDragAttachedPrivate::loadPixmap()
+{
+ Q_Q(QQuickDragAttached);
+
+ QUrl loadUrl = imageSource;
+ const QQmlContext *context = qmlContext(q->parent());
+ if (context)
+ loadUrl = context->resolvedUrl(imageSource);
+ pixmapLoader.load(context ? context->engine() : nullptr, loadUrl, QRect(), q->imageSourceSize());
+}
+
Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedActions)
{
Q_Q(QQuickDragAttached);
@@ -808,7 +856,7 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct
property for the started sequence.
*/
-void QQuickDragAttached::startDrag(QQmlV4Function *args)
+void QQuickDragAttached::startDrag(QQmlV4FunctionPtr args)
{
Q_D(QQuickDragAttached);
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index 806dbc7602..fa73e91485 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -119,24 +119,23 @@ private:
friend class QQuickDragAttachedPrivate;
};
-class QQmlV4Function;
class QQuickDragAttached;
-class Q_QUICK_PRIVATE_EXPORT QQuickDrag : public QObject
+class Q_QUICK_EXPORT QQuickDrag : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget)
- Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged)
- Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged)
- Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged)
- Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged)
- Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged)
- Q_PROPERTY(bool active READ active NOTIFY activeChanged)
- Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged)
- Q_PROPERTY(bool smoothed READ smoothed WRITE setSmoothed NOTIFY smoothedChanged)
+ Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget FINAL)
+ Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged FINAL FINAL)
+ Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged FINAL)
+ Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged FINAL)
+ Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged FINAL)
+ Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged FINAL)
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged FINAL)
+ Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged FINAL)
+ Q_PROPERTY(bool smoothed READ smoothed WRITE setSmoothed NOTIFY smoothedChanged FINAL)
// Note, threshold was added in QtQuick 2.2 but REVISION is not supported (or needed) for grouped
// properties See QTBUG-33179
- Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged RESET resetThreshold)
+ Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged RESET resetThreshold FINAL)
//### consider drag and drop
QML_NAMED_ELEMENT(Drag)
@@ -211,21 +210,23 @@ private:
};
class QQuickDragAttachedPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickDragAttached : public QObject
+class Q_QUICK_EXPORT QQuickDragAttached : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickDragAttached)
- Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
- Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource)
- Q_PROPERTY(QObject *target READ target NOTIFY targetChanged)
- Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged)
- Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource NOTIFY imageSourceChanged)
- Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged)
- Q_PROPERTY(QVariantMap mimeData READ mimeData WRITE setMimeData NOTIFY mimeDataChanged)
- Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged)
- Q_PROPERTY(Qt::DropAction proposedAction READ proposedAction WRITE setProposedAction NOTIFY proposedActionChanged)
- Q_PROPERTY(QQuickDrag::DragType dragType READ dragType WRITE setDragType NOTIFY dragTypeChanged)
+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged FINAL)
+ Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource FINAL)
+ Q_PROPERTY(QObject *target READ target NOTIFY targetChanged FINAL)
+ Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged FINAL)
+ Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource NOTIFY imageSourceChanged FINAL)
+ // imageSourceSize is new in Qt 6.8; revision omitted because of QTBUG-33179
+ Q_PROPERTY(QSize imageSourceSize READ imageSourceSize WRITE setImageSourceSize NOTIFY imageSourceSizeChanged FINAL)
+ Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged FINAL)
+ Q_PROPERTY(QVariantMap mimeData READ mimeData WRITE setMimeData NOTIFY mimeDataChanged FINAL)
+ Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged FINAL)
+ Q_PROPERTY(Qt::DropAction proposedAction READ proposedAction WRITE setProposedAction NOTIFY proposedActionChanged FINAL)
+ Q_PROPERTY(QQuickDrag::DragType dragType READ dragType WRITE setDragType NOTIFY dragTypeChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -249,6 +250,9 @@ public:
QUrl imageSource() const;
void setImageSource(const QUrl &url);
+ QSize imageSourceSize() const;
+ void setImageSourceSize(const QSize &size);
+
QStringList keys() const;
void setKeys(const QStringList &keys);
@@ -269,8 +273,8 @@ public:
bool event(QEvent *event) override;
public Q_SLOTS:
- void start(QQmlV4Function *);
- void startDrag(QQmlV4Function *);
+ void start(QQmlV4FunctionPtr);
+ void startDrag(QQmlV4FunctionPtr);
void cancel();
Q_SIGNALS:
@@ -282,6 +286,7 @@ Q_SIGNALS:
void targetChanged();
void hotSpotChanged();
void imageSourceChanged();
+ void imageSourceSizeChanged(); // new in Qt 6.8
void keysChanged();
void mimeDataChanged();
void supportedActionsChanged();
@@ -291,6 +296,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDrag)
-
#endif
diff --git a/src/quick/items/qquickdrag_p_p.h b/src/quick/items/qquickdrag_p_p.h
index 3cb122dbcc..657dfefb1d 100644
--- a/src/quick/items/qquickdrag_p_p.h
+++ b/src/quick/items/qquickdrag_p_p.h
@@ -25,7 +25,7 @@
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
QT_BEGIN_NAMESPACE
@@ -67,6 +67,7 @@ public:
Qt::DropAction startDrag(Qt::DropActions supportedActions);
void setTarget(QQuickItem *item);
QMimeData *createMimeData() const;
+ void loadPixmap();
QQuickDragGrabber dragGrabber;
@@ -86,6 +87,7 @@ public:
bool overrideActions : 1;
QPointF hotSpot;
QUrl imageSource;
+ QSize imageSourceSize;
QQuickPixmap pixmapLoader;
QStringList keys;
QVariantMap externalMimeData;
diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp
index 1c865a6327..2538e0e238 100644
--- a/src/quick/items/qquickdroparea.cpp
+++ b/src/quick/items/qquickdroparea.cpp
@@ -7,6 +7,7 @@
#include <private/qv4arraybuffer_p.h>
+#include <QtCore/qpointer.h>
#include <QtCore/qregularexpression.h>
QT_BEGIN_NAMESPACE
@@ -72,7 +73,7 @@ QQuickDropAreaPrivate::~QQuickDropAreaPrivate()
The \l drag.source property is communicated to the source of a drag event as
the recipient of a drop on the drag target.
- \sa {Qt Quick Examples - Drag and Drop}, {Qt Quick Examples - externaldraganddrop}
+ \sa {Qt Quick Examples - Drag and Drop}
*/
QQuickDropArea::QQuickDropArea(QQuickItem *parent)
@@ -353,12 +354,10 @@ void QQuickDropArea::dropEvent(QDropEvent *event)
The drop action may be one of:
- \list
- \li Qt.CopyAction Copy the data to the target.
- \li Qt.MoveAction Move the data from the source to the target.
- \li Qt.LinkAction Create a link from the source to the target.
- \li Qt.IgnoreAction Ignore the action (do nothing with the data).
- \endlist
+ \value Qt.CopyAction Copy the data to the target.
+ \value Qt.MoveAction Move the data from the source to the target.
+ \value Qt.LinkAction Create a link from the source to the target.
+ \value Qt.IgnoreAction Ignore the action (do nothing with the data).
*/
/*!
diff --git a/src/quick/items/qquickdroparea_p.h b/src/quick/items/qquickdroparea_p.h
index 35d295f5aa..e9fff0afa0 100644
--- a/src/quick/items/qquickdroparea_p.h
+++ b/src/quick/items/qquickdroparea_p.h
@@ -29,23 +29,23 @@ class QQuickDropAreaPrivate;
class QQuickDragEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x)
- Q_PROPERTY(qreal y READ y)
- Q_PROPERTY(QObject *source READ source)
- Q_PROPERTY(QStringList keys READ keys)
- Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions)
- Q_PROPERTY(Qt::DropActions proposedAction READ proposedAction)
- Q_PROPERTY(Qt::DropAction action READ action WRITE setAction RESET resetAction)
- Q_PROPERTY(bool accepted READ accepted WRITE setAccepted)
- Q_PROPERTY(bool hasColor READ hasColor)
- Q_PROPERTY(bool hasHtml READ hasHtml)
- Q_PROPERTY(bool hasText READ hasText)
- Q_PROPERTY(bool hasUrls READ hasUrls)
- Q_PROPERTY(QVariant colorData READ colorData)
- Q_PROPERTY(QString html READ html)
- Q_PROPERTY(QString text READ text)
- Q_PROPERTY(QList<QUrl> urls READ urls)
- Q_PROPERTY(QStringList formats READ formats)
+ Q_PROPERTY(qreal x READ x FINAL)
+ Q_PROPERTY(qreal y READ y FINAL)
+ Q_PROPERTY(QObject *source READ source FINAL)
+ Q_PROPERTY(QStringList keys READ keys FINAL)
+ Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions FINAL)
+ Q_PROPERTY(Qt::DropActions proposedAction READ proposedAction FINAL)
+ Q_PROPERTY(Qt::DropAction action READ action WRITE setAction RESET resetAction FINAL)
+ Q_PROPERTY(bool accepted READ accepted WRITE setAccepted FINAL)
+ Q_PROPERTY(bool hasColor READ hasColor FINAL)
+ Q_PROPERTY(bool hasHtml READ hasHtml FINAL)
+ Q_PROPERTY(bool hasText READ hasText FINAL)
+ Q_PROPERTY(bool hasUrls READ hasUrls FINAL)
+ Q_PROPERTY(QVariant colorData READ colorData FINAL)
+ Q_PROPERTY(QString html READ html FINAL)
+ Q_PROPERTY(QString text READ text FINAL)
+ Q_PROPERTY(QList<QUrl> urls READ urls FINAL)
+ Q_PROPERTY(QStringList formats READ formats FINAL)
QML_NAMED_ELEMENT(DragEvent)
QML_UNCREATABLE("DragEvent is only meant to be created by DropArea")
QML_ADDED_IN_VERSION(2, 0)
@@ -92,9 +92,9 @@ private:
class QQuickDropAreaDrag : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x NOTIFY positionChanged)
- Q_PROPERTY(qreal y READ y NOTIFY positionChanged)
- Q_PROPERTY(QObject *source READ source NOTIFY sourceChanged)
+ Q_PROPERTY(qreal x READ x NOTIFY positionChanged FINAL)
+ Q_PROPERTY(qreal y READ y NOTIFY positionChanged FINAL)
+ Q_PROPERTY(QObject *source READ source NOTIFY sourceChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -117,7 +117,7 @@ private:
};
class QQuickDropAreaPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickDropArea : public QQuickItem
+class Q_QUICK_EXPORT QQuickDropArea : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged)
@@ -161,6 +161,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDropArea)
-
#endif // QQUICKDROPAREA_P_H
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 5c722df4fe..d6d012a021 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -97,16 +97,15 @@ Item {
This property holds the keyboard modifier flags that existed immediately
before the event occurred.
- It contains a bitwise combination of:
- \list
- \li \l {Qt::NoModifier} {Qt.NoModifier} - No modifier key is pressed.
- \li \l {Qt::ShiftModifier} {Qt.ShiftModifier} - A Shift key on the keyboard is pressed.
- \li \l {Qt::ControlModifier} {Qt.ControlModifier} - A Ctrl key on the keyboard is pressed.
- \li \l {Qt::AltModifier} {Qt.AltModifier} - An Alt key on the keyboard is pressed.
- \li \l {Qt::MetaModifier} {Qt.MetaModifier} - A Meta key on the keyboard is pressed.
- \li \l {Qt::KeypadModifier} {Qt.KeypadModifier} - A keypad button is pressed.
- \li \l {Qt::GroupSwitchModifier} {Qt.GroupSwitchModifier} - X11 only. A Mode_switch key on the keyboard is pressed.
- \endlist
+ It contains a bitwise combination of numeric values (the same as in Qt::KeyboardModifier):
+
+ \value Qt.NoModifier No modifier key is pressed.
+ \value Qt.ShiftModifier} A Shift key on the keyboard is pressed.
+ \value Qt.ControlModifier A Ctrl key on the keyboard is pressed.
+ \value Qt.AltModifier An Alt key on the keyboard is pressed.
+ \value Qt.MetaModifier A Meta key on the keyboard is pressed.
+ \value Qt.KeypadModifier A keypad button is pressed.
+ \value Qt.GroupSwitchModifier X11 only. A Mode_switch key on the keyboard is pressed.
For example, to react to a Shift key + Enter key combination:
\qml
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 24f3cef6c9..f82905333b 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -36,17 +36,18 @@ class QPointerEvent;
class QMouseEvent;
class QQuickPointerHandler;
-class Q_QUICK_PRIVATE_EXPORT QQuickKeyEvent : public QObject
+class Q_QUICK_EXPORT QQuickKeyEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(int key READ key CONSTANT)
- Q_PROPERTY(QString text READ text CONSTANT)
- Q_PROPERTY(int modifiers READ modifiers CONSTANT)
- Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat CONSTANT)
- Q_PROPERTY(int count READ count CONSTANT)
- Q_PROPERTY(quint32 nativeScanCode READ nativeScanCode CONSTANT)
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- QML_ANONYMOUS
+ Q_PROPERTY(int key READ key CONSTANT FINAL)
+ Q_PROPERTY(QString text READ text CONSTANT FINAL)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT FINAL)
+ Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat CONSTANT FINAL)
+ Q_PROPERTY(int count READ count CONSTANT FINAL)
+ Q_PROPERTY(quint32 nativeScanCode READ nativeScanCode CONSTANT FINAL)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted FINAL)
+ QML_NAMED_ELEMENT(KeyEvent)
+ QML_UNCREATABLE("Should only be used by signal handlers in the Keys attached property")
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -102,21 +103,23 @@ private:
bool m_autoRepeat = false;
};
-// used in Qt Location
-class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject
+class Q_QUICK_EXPORT QQuickMouseEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x CONSTANT)
- Q_PROPERTY(qreal y READ y CONSTANT)
- Q_PROPERTY(int button READ button CONSTANT)
- Q_PROPERTY(int buttons READ buttons CONSTANT)
- Q_PROPERTY(int modifiers READ modifiers CONSTANT)
- Q_PROPERTY(int source READ source CONSTANT REVISION(2, 7))
- Q_PROPERTY(bool wasHeld READ wasHeld CONSTANT)
- Q_PROPERTY(bool isClick READ isClick CONSTANT)
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- Q_PROPERTY(int flags READ flags CONSTANT REVISION(2, 11))
- QML_ANONYMOUS
+ Q_PROPERTY(qreal x READ x CONSTANT FINAL)
+ Q_PROPERTY(qreal y READ y CONSTANT FINAL)
+ Q_PROPERTY(int button READ button CONSTANT FINAL)
+ Q_PROPERTY(int buttons READ buttons CONSTANT FINAL)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT FINAL)
+#if QT_DEPRECATED_SINCE(6, 6)
+ Q_PROPERTY(int source READ source CONSTANT REVISION(2, 7) FINAL)
+#endif
+ Q_PROPERTY(bool isClick READ isClick CONSTANT FINAL)
+ Q_PROPERTY(bool wasHeld READ wasHeld CONSTANT FINAL)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted FINAL)
+ Q_PROPERTY(int flags READ flags CONSTANT REVISION(2, 11) FINAL)
+ QML_NAMED_ELEMENT(MouseEvent)
+ QML_UNCREATABLE("Should only be used by mouse event signal handlers, for example in MouseArea")
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -145,7 +148,9 @@ public:
int button() const { return _button; }
int buttons() const { return _buttons; }
int modifiers() const { return _modifiers; }
+#if QT_DEPRECATED_SINCE(6, 6)
int source() const { return _source; }
+#endif
bool wasHeld() const { return _wasHeld; }
bool isClick() const { return _isClick; }
@@ -153,7 +158,9 @@ public:
void setX(qreal x) { _x = x; }
void setY(qreal y) { _y = y; }
void setPosition(const QPointF &point) { _x = point.x(); _y = point.y(); }
+#if QT_DEPRECATED_SINCE(6, 6)
void setSource(Qt::MouseEventSource s) { _source = s; }
+#endif
bool isAccepted() { return _accepted; }
void setAccepted(bool accepted) { _accepted = accepted; }
@@ -172,20 +179,21 @@ private:
};
#if QT_CONFIG(wheelevent)
-class QQuickWheelEvent : public QObject
+class Q_QUICK_EXPORT QQuickWheelEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(const QPointingDevice *device READ pointingDevice CONSTANT)
- Q_PROPERTY(qreal x READ x CONSTANT)
- Q_PROPERTY(qreal y READ y CONSTANT)
- Q_PROPERTY(QPoint angleDelta READ angleDelta CONSTANT)
- Q_PROPERTY(QPoint pixelDelta READ pixelDelta CONSTANT)
- Q_PROPERTY(Qt::ScrollPhase phase READ phase CONSTANT)
- Q_PROPERTY(int buttons READ buttons CONSTANT)
- Q_PROPERTY(int modifiers READ modifiers CONSTANT)
- Q_PROPERTY(bool inverted READ inverted CONSTANT)
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- QML_ANONYMOUS
+ Q_PROPERTY(const QPointingDevice *device READ pointingDevice CONSTANT FINAL)
+ Q_PROPERTY(qreal x READ x CONSTANT FINAL)
+ Q_PROPERTY(qreal y READ y CONSTANT FINAL)
+ Q_PROPERTY(QPoint angleDelta READ angleDelta CONSTANT FINAL)
+ Q_PROPERTY(QPoint pixelDelta READ pixelDelta CONSTANT FINAL)
+ Q_PROPERTY(Qt::ScrollPhase phase READ phase CONSTANT FINAL)
+ Q_PROPERTY(int buttons READ buttons CONSTANT FINAL)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT FINAL)
+ Q_PROPERTY(bool inverted READ inverted CONSTANT FINAL)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted FINAL)
+ QML_NAMED_ELEMENT(WheelEvent)
+ QML_UNCREATABLE("Should only be used by wheel event signal handlers, for example in MouseArea")
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -231,11 +239,12 @@ private:
};
#endif
-class Q_QUICK_PRIVATE_EXPORT QQuickCloseEvent : public QObject
+class Q_QUICK_EXPORT QQuickCloseEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- QML_ANONYMOUS
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted FINAL)
+ QML_NAMED_ELEMENT(CloseEvent)
+ QML_UNCREATABLE("Should only be used by Window's closing signal")
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -250,14 +259,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickKeyEvent)
-QML_DECLARE_TYPE(QQuickMouseEvent)
-#if QT_CONFIG(wheelevent)
-QML_DECLARE_TYPE(QQuickWheelEvent)
-#endif
-QML_DECLARE_TYPE(QQuickCloseEvent)
-QML_DECLARE_TYPE(QPointingDevice)
-QML_DECLARE_TYPE(QPointingDeviceUniqueId)
-QML_DECLARE_TYPE(QPointerEvent)
-
#endif // QQUICKEVENTS_P_P_H
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index c2f71a9438..715f75cde7 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -7,7 +7,7 @@
#include "qquickwindow.h"
#include "qquickwindow_p.h"
#include "qquickmousearea_p.h"
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
#include "qquickdrag_p.h"
#endif
@@ -22,7 +22,7 @@
#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/qstylehints.h>
#include <QtCore/qmath.h>
-#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformtheme.h>
#include <math.h>
#include <cmath>
@@ -40,21 +40,6 @@ Q_LOGGING_CATEGORY(lcVel, "qt.quick.flickable.velocity")
// will ensure the Flickable retains the grab on consecutive flicks.
static const int RetainGrabVelocity = 100;
-// Currently std::round can't be used on Android when using ndk g++, so
-// use C version instead. We could just define two versions of Round, one
-// for float and one for double, but then only one of them would be used
-// and compiler would trigger a warning about unused function.
-//
-// See https://code.google.com/p/android/issues/detail?id=54418
-template<typename T>
-static T Round(T t) {
- return round(t);
-}
-template<>
-Q_DECL_UNUSED float Round<float>(float f) {
- return roundf(f);
-}
-
static qreal EaseOvershoot(qreal t) {
return qAtan(t);
}
@@ -101,7 +86,7 @@ void QQuickFlickableVisibleArea::updateVisible()
qreal pagePos = 0;
qreal pageSize = 0;
if (!qFuzzyIsNull(maxYBounds)) {
- qreal y = p->pixelAligned ? Round(p->vData.move.value()) : p->vData.move.value();
+ qreal y = p->pixelAligned ? std::round(p->vData.move.value()) : p->vData.move.value();
pagePos = (-y + flickable->minYExtent()) / maxYBounds;
pageSize = viewheight / maxYBounds;
}
@@ -120,7 +105,7 @@ void QQuickFlickableVisibleArea::updateVisible()
const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
const qreal maxXBounds = maxxextent + viewwidth;
if (!qFuzzyIsNull(maxXBounds)) {
- qreal x = p->pixelAligned ? Round(p->hData.move.value()) : p->hData.move.value();
+ qreal x = p->pixelAligned ? std::round(p->hData.move.value()) : p->hData.move.value();
pagePos = (-x + flickable->minXExtent()) / maxXBounds;
pageSize = viewwidth / maxXBounds;
} else {
@@ -249,8 +234,9 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, syncDrag(false)
, lastPosTime(-1)
, lastPressTime(0)
- , deceleration(QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickDeceleration).toReal())
- , maxVelocity(QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickMaximumVelocity).toReal())
+ , deceleration(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickDeceleration).toReal())
+ , wheelDeceleration(15000)
+ , maxVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
, delayedPressEvent(nullptr), pressDelay(0), fixupDuration(400)
, flickBoost(1.0), initialWheelFlickDistance(qApp->styleHints()->wheelScrollLines() * 24)
, fixupMode(Normal), vTime(0), visibleArea(nullptr)
@@ -259,6 +245,9 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, boundsMovement(QQuickFlickable::FollowBoundsBehavior)
, rebound(nullptr)
{
+ const int wheelDecelerationEnv = qEnvironmentVariableIntValue("QT_QUICK_FLICKABLE_WHEEL_DECELERATION");
+ if (wheelDecelerationEnv > 0)
+ wheelDeceleration = wheelDecelerationEnv;
}
void QQuickFlickablePrivate::init()
@@ -276,6 +265,7 @@ void QQuickFlickablePrivate::init()
q->setFlag(QQuickItem::ItemIsViewport);
QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
/*!
@@ -339,20 +329,21 @@ void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometr
}
}
-bool QQuickFlickablePrivate::flickX(qreal velocity)
+bool QQuickFlickablePrivate::flickX(QEvent::Type eventType, qreal velocity)
{
Q_Q(QQuickFlickable);
- return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
+ return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, eventType, velocity);
}
-bool QQuickFlickablePrivate::flickY(qreal velocity)
+bool QQuickFlickablePrivate::flickY(QEvent::Type eventType, qreal velocity)
{
Q_Q(QQuickFlickable);
- return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
+ return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, eventType, velocity);
}
bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
+ QQuickTimeLineCallback::Callback fixupCallback,
+ QEvent::Type eventType, qreal velocity)
{
Q_Q(QQuickFlickable);
qreal maxDistance = -1;
@@ -374,13 +365,14 @@ bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExt
v = maxVelocity;
}
+ qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
+ qCDebug(lcFlickable) << "choosing deceleration" << accel << "for" << eventType;
// adjust accel so that we hit a full pixel
- qreal accel = deceleration;
qreal v2 = v * v;
qreal dist = v2 / (accel * 2.0);
if (v > 0)
dist = -dist;
- qreal target = -Round(-(data.move.value() - dist));
+ qreal target = std::round(data.move.value() - dist);
dist = -target + data.move.value();
accel = v2 / (2.0f * qAbs(dist));
@@ -502,18 +494,18 @@ void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExt
} else if (data.move.value() <= maxExtent) {
resetTimeline(data);
adjustContentPos(data, maxExtent);
- } else if (-Round(-data.move.value()) != data.move.value()) {
+ } else if (-std::round(-data.move.value()) != data.move.value()) {
// We could animate, but since it is less than 0.5 pixel it's probably not worthwhile.
resetTimeline(data);
qreal val = data.move.value();
- if (std::abs(-Round(-val) - val) < 0.25) // round small differences
- val = -Round(-val);
+ if (std::abs(std::round(val) - val) < 0.25) // round small differences
+ val = std::round(val);
else if (data.smoothVelocity.value() > 0) // continue direction of motion for larger
- val = -std::floor(-val);
+ val = std::ceil(val);
else if (data.smoothVelocity.value() < 0)
- val = -std::ceil(-val);
+ val = std::floor(val);
else // otherwise round
- val = -Round(-val);
+ val = std::round(val);
timeline.set(data.move, val);
}
data.inOvershoot = false;
@@ -549,7 +541,7 @@ void QQuickFlickablePrivate::updateBeginningEnd()
// Vertical
const qreal maxyextent = -q->maxYExtent();
const qreal minyextent = -q->minYExtent();
- const qreal ypos = -vData.move.value();
+ const qreal ypos = pixelAligned ? -std::round(vData.move.value()) : -vData.move.value();
bool atBeginning = fuzzyLessThanOrEqualTo(ypos, std::ceil(minyextent));
bool atEnd = fuzzyLessThanOrEqualTo(std::floor(maxyextent), ypos);
@@ -569,7 +561,7 @@ void QQuickFlickablePrivate::updateBeginningEnd()
// Horizontal
const qreal maxxextent = -q->maxXExtent();
const qreal minxextent = -q->minXExtent();
- const qreal xpos = -hData.move.value();
+ const qreal xpos = pixelAligned ? -std::round(hData.move.value()) : -hData.move.value();
atBeginning = fuzzyLessThanOrEqualTo(xpos, std::ceil(minxextent));
atEnd = fuzzyLessThanOrEqualTo(std::floor(maxxextent), xpos);
@@ -764,8 +756,6 @@ void QQuickFlickablePrivate::updateBeginningEnd()
\snippet qml/flickableScrollbar.qml 0
\dots 8
\snippet qml/flickableScrollbar.qml 1
-
- \sa {customitems/scrollbar}{UI Components: Scrollbar Example}
*/
QQuickFlickable::QQuickFlickable(QQuickItem *parent)
: QQuickItem(*(new QQuickFlickablePrivate), parent)
@@ -1111,14 +1101,17 @@ void QQuickFlickablePrivate::handlePressEvent(QPointerEvent *event)
}
q->setKeepMouseGrab(stealMouse);
- maybeBeginDrag(computeCurrentTime(event), event->points().first().position());
+ maybeBeginDrag(computeCurrentTime(event), event->points().first().position(),
+ event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->buttons()
+ : Qt::NoButton);
}
-void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn)
+void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn, Qt::MouseButtons buttons)
{
Q_Q(QQuickFlickable);
clearDelayedPress();
- pressed = true;
+ // consider dragging only when event is left mouse button or touch event which has no button
+ pressed = buttons.testFlag(Qt::LeftButton) || (buttons == Qt::NoButton);
if (hData.transitionToBounds)
hData.transitionToBounds->stopTransition();
@@ -1382,10 +1375,14 @@ void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
const QVector2D velocity = firstPointLocalVelocity(event);
bool overThreshold = false;
- if (q->yflick())
- overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
- if (q->xflick())
- overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ if (event->pointCount() == 1) {
+ if (q->yflick())
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
+ if (q->xflick())
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ } else {
+ qCDebug(lcFilter) << q->objectName() << "ignoring multi-touch" << event;
+ }
drag(currentTimestamp, event->type(), pos, deltas, overThreshold, false, false, velocity);
}
@@ -1447,24 +1444,32 @@ void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
}
flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
- const int flickThreshold = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickStartDistance).toInt();
+ const int flickThreshold = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickStartDistance).toInt();
+
+ bool anyPointGrabbed = event->points().constEnd() !=
+ std::find_if(event->points().constBegin(),event->points().constEnd(),
+ [q, event](const QEventPoint &point) { return event->exclusiveGrabber(point) == q; });
bool flickedVertically = false;
vVelocity *= flickBoost;
- bool isVerticalFlickAllowed = q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(pos.y() - pressPos.y()) > flickThreshold;
+ const bool isVerticalFlickAllowed = anyPointGrabbed &&
+ q->yflick() && qAbs(vVelocity) > _q_MinimumFlickVelocity &&
+ qAbs(pos.y() - pressPos.y()) > flickThreshold;
if (isVerticalFlickAllowed) {
velocityTimeline.reset(vData.smoothVelocity);
vData.smoothVelocity.setValue(-vVelocity);
- flickedVertically = flickY(vVelocity);
+ flickedVertically = flickY(event->type(), vVelocity);
}
bool flickedHorizontally = false;
hVelocity *= flickBoost;
- bool isHorizontalFlickAllowed = q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(pos.x() - pressPos.x()) > flickThreshold;
+ const bool isHorizontalFlickAllowed = anyPointGrabbed &&
+ q->xflick() && qAbs(hVelocity) > _q_MinimumFlickVelocity &&
+ qAbs(pos.x() - pressPos.x()) > flickThreshold;
if (isHorizontalFlickAllowed) {
velocityTimeline.reset(hData.smoothVelocity);
hData.smoothVelocity.setValue(-hVelocity);
- flickedHorizontally = flickX(hVelocity);
+ flickedHorizontally = flickX(event->type(), hVelocity);
}
if (!isVerticalFlickAllowed)
@@ -1474,8 +1479,15 @@ void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
fixupX();
flickingStarted(flickedHorizontally, flickedVertically);
- if (!isViewMoving())
+ if (!isViewMoving()) {
q->movementEnding();
+ } else {
+ if (flickedVertically)
+ vMoved = true;
+ if (flickedHorizontally)
+ hMoved = true;
+ q->movementStarting();
+ }
}
void QQuickFlickable::mousePressEvent(QMouseEvent *event)
@@ -1508,13 +1520,14 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
if (d->delayedPressEvent) {
d->replayDelayedPress();
- // Now send the release
- if (auto grabber = qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(event->point(0)))) {
- // not copying or detaching anything, so make sure we return the original event unchanged
- const auto oldPosition = event->point(0).position();
- QMutableEventPoint::setPosition(event->point(0), grabber->mapFromScene(event->scenePosition()));
+ auto &firstPoint = event->point(0);
+ if (const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
+ // Since we sent the delayed press to the window, we need to resend the release to the window too.
+ // We're not copying or detaching, so restore the original event position afterwards.
+ const auto oldPosition = firstPoint.position();
+ QMutableEventPoint::setPosition(firstPoint, event->scenePosition());
QCoreApplication::sendEvent(window(), event);
- QMutableEventPoint::setPosition(event->point(0), oldPosition);
+ QMutableEventPoint::setPosition(firstPoint, oldPosition);
}
// And the event has been consumed
@@ -1533,6 +1546,15 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
void QQuickFlickable::touchEvent(QTouchEvent *event)
{
Q_D(QQuickFlickable);
+
+ if (event->type() == QEvent::TouchCancel) {
+ if (d->interactive && d->wantsPointerEvent(event))
+ d->cancelInteraction();
+ else
+ QQuickItem::touchEvent(event);
+ return;
+ }
+
bool unhandled = false;
const auto &firstPoint = event->points().first();
switch (firstPoint.state()) {
@@ -1558,11 +1580,11 @@ void QQuickFlickable::touchEvent(QTouchEvent *event)
if (d->delayedPressEvent) {
d->replayDelayedPress();
- // Now send the release
- auto &firstPoint = event->point(0);
- if (auto grabber = qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(firstPoint))) {
- const auto localPos = grabber->mapFromScene(firstPoint.scenePosition());
- QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
+ const auto &firstPoint = event->point(0);
+ if (const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
+ // Since we sent the delayed press to the window, we need to resend the release to the window too.
+ QScopedPointer<QPointerEvent> localizedEvent(
+ QQuickDeliveryAgentPrivate::clonePointerEvent(event, firstPoint.scenePosition()));
QCoreApplication::sendEvent(window(), localizedEvent.data());
}
@@ -1644,48 +1666,117 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
// no pixel delta (physical mouse wheel, or "dumb" touchpad), so use angleDelta
int xDelta = event->angleDelta().x();
int yDelta = event->angleDelta().y();
- // For a single "clicky" wheel event (angleDelta +/- 120),
- // we want flick() to end up moving a distance proportional to QStyleHints::wheelScrollLines().
- // The decel algo from there is
- // qreal dist = v2 / (accel * 2.0);
- // i.e. initialWheelFlickDistance = (120 / dt)^2 / (deceleration * 2)
- // now solve for dt:
- // dt = 120 / sqrt(deceleration * 2 * initialWheelFlickDistance)
- if (!isMoving())
- elapsed = 120 / qSqrt(d->deceleration * 2 * d->initialWheelFlickDistance);
- if (yflick() && yDelta != 0) {
- qreal instVelocity = yDelta / elapsed;
- // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
- if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0))
- d->vData.velocityBuffer.clear();
- d->vData.addVelocitySample(instVelocity, d->maxVelocity);
- d->vData.updateVelocity();
- if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
- const bool newFlick = d->flickY(d->vData.velocity);
- if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) {
- d->flickingStarted(false, true);
- d->vMoved = true;
+
+ if (d->wheelDeceleration > _q_MaximumWheelDeceleration) {
+ const qreal wheelScroll = -qApp->styleHints()->wheelScrollLines() * 24;
+ // If wheelDeceleration is very large, i.e. the user or the platform does not want to have any mouse wheel
+ // acceleration behavior, we want to move a distance proportional to QStyleHints::wheelScrollLines()
+ if (yflick() && yDelta != 0) {
+ d->moveReason = QQuickFlickablePrivate::Mouse; // ItemViews will set fixupMode to Immediate in fixup() without this.
+ d->vMoved = true;
+ qreal scrollPixel = (-yDelta / 120.0 * wheelScroll);
+ if (scrollPixel > 0) { // Forward direction (away from user)
+ if (d->vData.move.value() >= minYExtent())
+ d->vMoved = false;
+ } else { // Backward direction (towards user)
+ if (d->vData.move.value() <= maxYExtent())
+ d->vMoved = false;
+ }
+ if (d->vMoved) {
+ if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
+ const qreal estContentPos = scrollPixel + d->vData.move.value();
+ if (scrollPixel > 0) { // Forward direction (away from user)
+ if (estContentPos > minYExtent())
+ scrollPixel = minYExtent() - d->vData.move.value();
+ } else { // Backward direction (towards user)
+ if (estContentPos < maxYExtent())
+ scrollPixel = maxYExtent() - d->vData.move.value();
+ }
+ }
+ d->resetTimeline(d->vData);
movementStarting();
+ d->timeline.moveBy(d->vData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
+ d->vData.fixingUp = true;
+ d->timeline.callback(QQuickTimeLineCallback(&d->vData.move, QQuickFlickablePrivate::fixupY_callback, d));
}
event->accept();
}
- }
- if (xflick() && xDelta != 0) {
- qreal instVelocity = xDelta / elapsed;
- // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
- if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0))
- d->hData.velocityBuffer.clear();
- d->hData.addVelocitySample(instVelocity, d->maxVelocity);
- d->hData.updateVelocity();
- if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
- const bool newFlick = d->flickX(d->hData.velocity);
- if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) {
- d->flickingStarted(true, false);
- d->hMoved = true;
+ if (xflick() && xDelta != 0) {
+ d->moveReason = QQuickFlickablePrivate::Mouse; // ItemViews will set fixupMode to Immediate in fixup() without this.
+ d->hMoved = true;
+ qreal scrollPixel = (-xDelta / 120.0 * wheelScroll);
+ if (scrollPixel > 0) { // Forward direction (away from user)
+ if (d->hData.move.value() >= minXExtent())
+ d->hMoved = false;
+ } else { // Backward direction (towards user)
+ if (d->hData.move.value() <= maxXExtent())
+ d->hMoved = false;
+ }
+ if (d->hMoved) {
+ if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
+ const qreal estContentPos = scrollPixel + d->hData.move.value();
+ if (scrollPixel > 0) { // Forward direction (away from user)
+ if (estContentPos > minXExtent())
+ scrollPixel = minXExtent() - d->hData.move.value();
+ } else { // Backward direction (towards user)
+ if (estContentPos < maxXExtent())
+ scrollPixel = maxXExtent() - d->hData.move.value();
+ }
+ }
+ d->resetTimeline(d->hData);
movementStarting();
+ d->timeline.moveBy(d->hData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
+ d->hData.fixingUp = true;
+ d->timeline.callback(QQuickTimeLineCallback(&d->hData.move, QQuickFlickablePrivate::fixupX_callback, d));
}
event->accept();
}
+ } else {
+ // wheelDeceleration is set to some reasonable value: the user or the platform wants to have
+ // the classic Qt Quick mouse wheel acceleration behavior.
+ // For a single "clicky" wheel event (angleDelta +/- 120),
+ // we want flick() to end up moving a distance proportional to QStyleHints::wheelScrollLines().
+ // The decel algo from there is
+ // qreal dist = v2 / (accel * 2.0);
+ // i.e. initialWheelFlickDistance = (120 / dt)^2 / (deceleration * 2)
+ // now solve for dt:
+ // dt = 120 / sqrt(deceleration * 2 * initialWheelFlickDistance)
+ if (!isMoving())
+ elapsed = 120 / qSqrt(d->wheelDeceleration * 2 * d->initialWheelFlickDistance);
+ if (yflick() && yDelta != 0) {
+ qreal instVelocity = yDelta / elapsed;
+ // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
+ if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0))
+ d->vData.velocityBuffer.clear();
+ d->vData.addVelocitySample(instVelocity, d->maxVelocity);
+ d->vData.updateVelocity();
+ if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
+ const bool newFlick = d->flickY(event->type(), d->vData.velocity);
+ if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) {
+ d->flickingStarted(false, true);
+ d->vMoved = true;
+ movementStarting();
+ }
+ event->accept();
+ }
+ }
+ if (xflick() && xDelta != 0) {
+ qreal instVelocity = xDelta / elapsed;
+ // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
+ if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0))
+ d->hData.velocityBuffer.clear();
+ d->hData.addVelocitySample(instVelocity, d->maxVelocity);
+ d->hData.updateVelocity();
+ if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
+ const bool newFlick = d->flickX(event->type(), d->hData.velocity);
+ if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) {
+ d->flickingStarted(true, false);
+ d->hMoved = true;
+ movementStarting();
+ }
+ event->accept();
+ }
+ }
}
} else {
// use pixelDelta (probably from a trackpad): this is where we want to be on most platforms eventually
@@ -1812,7 +1903,7 @@ void QQuickFlickablePrivate::replayDelayedPress()
void QQuickFlickablePrivate::setViewportX(qreal x)
{
Q_Q(QQuickFlickable);
- qreal effectiveX = pixelAligned ? -Round(-x) : x;
+ qreal effectiveX = pixelAligned ? -std::round(-x) : x;
const qreal maxX = q->maxXExtent();
const qreal minX = q->minXExtent();
@@ -1847,7 +1938,7 @@ void QQuickFlickablePrivate::setViewportX(qreal x)
void QQuickFlickablePrivate::setViewportY(qreal y)
{
Q_Q(QQuickFlickable);
- qreal effectiveY = pixelAligned ? -Round(-y) : y;
+ qreal effectiveY = pixelAligned ? -std::round(-y) : y;
const qreal maxY = q->maxYExtent();
const qreal minY = q->minYExtent();
@@ -2015,7 +2106,7 @@ void QQuickFlickable::geometryChange(const QRectF &newGeometry, const QRectF &ol
Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
Calling this method will update the corresponding moving and flicking properties and signals,
- just like a real flick.
+ just like a real touchscreen flick.
*/
void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
@@ -2027,8 +2118,8 @@ void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
d->vData.velocity = yVelocity;
d->hData.vTime = d->vData.vTime = d->timeline.time();
- const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(xVelocity);
- const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(yVelocity);
+ const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(QEvent::TouchUpdate, xVelocity);
+ const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(QEvent::TouchUpdate, yVelocity);
if (flickedX)
d->hMoved = true;
@@ -2580,12 +2671,31 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
QQuickDeliveryAgentPrivate::isTabletEvent(event)))
return false; // don't filter hover events or wheel events, for example
Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself");
- qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
Q_D(QQuickFlickable);
// If a touch event contains a new press point, don't steal right away: watch the movements for a while
if (isTouch && static_cast<QTouchEvent *>(event)->touchPointStates().testFlag(QEventPoint::State::Pressed))
d->stealMouse = false;
+ // If multiple touchpoints are within bounds, don't grab: it's probably meant for multi-touch interaction in some child
+ if (event->pointCount() > 1) {
+ qCDebug(lcFilter) << objectName() << "ignoring multi-touch" << event << "for" << receiver;
+ d->stealMouse = false;
+ } else {
+ qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
+ }
+
const auto &firstPoint = event->points().first();
+
+ if (event->pointCount() == 1 && event->exclusiveGrabber(firstPoint) == this) {
+ // We have an exclusive grab (since we're e.g dragging), but at the same time, we have
+ // a child with a passive grab (which is why this filter is being called). And because
+ // of that, we end up getting the same pointer events twice; First in our own event
+ // handlers (because of the grab), then once more in here, since we filter the child.
+ // To avoid processing the event twice (e.g avoid calling handleReleaseEvent once more
+ // from below), we mark the event as filtered, and simply return.
+ event->setAccepted(true);
+ return true;
+ }
+
QPointF localPos = mapFromScene(firstPoint.scenePosition());
bool receiverDisabled = receiver && !receiver->isEnabled();
bool stealThisEvent = d->stealMouse;
@@ -2595,7 +2705,7 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
// 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 QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
if (mouseArea->drag() && mouseArea->drag()->target())
preventStealing = true;
#endif
@@ -2720,21 +2830,10 @@ void QQuickFlickable::setMaximumFlickVelocity(qreal v)
\qmlproperty real QtQuick::Flickable::flickDeceleration
This property holds the rate at which a flick will decelerate:
the higher the number, the faster it slows down when the user stops
- flicking via touch, touchpad or mouse wheel. For example 0.0001 is nearly
+ flicking via touch. For example 0.0001 is nearly
"frictionless", and 10000 feels quite "sticky".
The default value is platform dependent. Values of zero or less are not allowed.
-
- \note For touchpad flicking, some platforms drive Flickable directly by
- sending QWheelEvents with QWheelEvent::phase() being \c Qt::ScrollMomentum,
- after the user has released all fingers from the touchpad. In that case,
- the operating system is controlling the deceleration, and this property has
- no effect.
-
- \note For mouse wheel scrolling, and for gesture scrolling on touchpads
- that do not have a momentum phase, extremely large values of
- flickDeceleration can make Flickable very resistant to scrolling,
- especially if \l maximumFlickVelocity is too small.
*/
qreal QQuickFlickable::flickDeceleration() const
{
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index a059924ed1..3f6a0e67fb 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickFlickablePrivate;
class QQuickFlickableVisibleArea;
-class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem
+class Q_QUICK_EXPORT QQuickFlickable : public QQuickItem
{
Q_OBJECT
@@ -287,6 +287,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFlickable)
-
#endif // QQUICKFLICKABLE_P_H
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 04d27c0b8e..8d251ba9ff 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -30,14 +30,11 @@
QT_BEGIN_NAMESPACE
-// Really slow flicks can be annoying.
-const qreal MinimumFlickVelocity = 75.0;
-
class QQuickFlickableVisibleArea;
class QQuickTransition;
class QQuickFlickableReboundTransition;
-class Q_QUICK_PRIVATE_EXPORT QQuickFlickablePrivate : public QQuickItemPrivate, public QQuickItemChangeListener
+class Q_QUICK_EXPORT QQuickFlickablePrivate : public QQuickItemPrivate, public QQuickItemChangeListener
{
Q_DECLARE_PUBLIC(QQuickFlickable)
@@ -60,6 +57,8 @@ public:
QQuickFlickablePrivate *parent;
};
+ enum MovementReason { Other, SetIndex, Mouse };
+
struct AxisData {
AxisData(QQuickFlickablePrivate *fp, void (QQuickFlickablePrivate::*func)(qreal))
: move(fp, func)
@@ -140,10 +139,11 @@ public:
uint unused : 17;
};
- bool flickX(qreal velocity);
- bool flickY(qreal velocity);
+ bool flickX(QEvent::Type eventType, qreal velocity);
+ bool flickY(QEvent::Type eventType, qreal velocity);
virtual bool flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
+ QQuickTimeLineCallback::Callback fixupCallback,
+ QEvent::Type eventType, qreal velocity);
void flickingStarted(bool flickingH, bool flickingV);
void fixupX();
@@ -184,6 +184,8 @@ public:
AxisData hData;
AxisData vData;
+ MovementReason moveReason = Other;
+
QQuickTimeLine timeline;
bool hMoved : 1;
bool vMoved : 1;
@@ -201,6 +203,7 @@ public:
QPointF pressPos;
QVector2D accumulatedWheelPixelDelta;
qreal deceleration;
+ qreal wheelDeceleration;
qreal maxVelocity;
QPointerEvent *delayedPressEvent;
QBasicTimer delayedPressTimer;
@@ -231,7 +234,8 @@ public:
void handleMoveEvent(QPointerEvent *);
void handleReleaseEvent(QPointerEvent *);
- void maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn);
+ void maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn,
+ Qt::MouseButtons buttons = Qt::NoButton);
void drag(qint64 currentTimestamp, QEvent::Type eventType, const QPointF &localPos,
const QVector2D &deltas, bool overThreshold, bool momentum,
bool velocitySensitiveOverBounds, const QVector2D &velocity);
@@ -247,14 +251,14 @@ public:
static void data_clear(QQmlListProperty<QObject> *);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickFlickableVisibleArea : public QObject
+class Q_QUICK_EXPORT QQuickFlickableVisibleArea : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged)
- Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged)
- Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged)
- Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged)
+ Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged FINAL)
+ Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged FINAL)
+ Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged FINAL)
+ Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -284,6 +288,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFlickableVisibleArea)
-
#endif // QQUICKFLICKABLE_P_P_H
diff --git a/src/quick/items/qquickflickablebehavior_p.h b/src/quick/items/qquickflickablebehavior_p.h
index d99fc988d1..ec43aaca7d 100644
--- a/src/quick/items/qquickflickablebehavior_p.h
+++ b/src/quick/items/qquickflickablebehavior_p.h
@@ -62,4 +62,11 @@
#define QML_FLICK_MULTIFLICK_MAXBOOST 3.0
#endif
+// Really slow flicks can be annoying.
+const qreal _q_MinimumFlickVelocity = 75.0;
+
+// If QQuickFlickablePrivate::wheelDeceleration (perhaps overridden via QT_QUICK_FLICKABLE_WHEEL_DECELERATION)
+// is greater than this, we switch to proportional wheel scrolling: no "acceleration" at all.
+const qreal _q_MaximumWheelDeceleration = 14999;
+
#endif //QQUICKFLICKABLEBEHAVIOR_H
diff --git a/src/quick/items/qquickflipable.cpp b/src/quick/items/qquickflipable.cpp
index d599618834..2e48566295 100644
--- a/src/quick/items/qquickflipable.cpp
+++ b/src/quick/items/qquickflipable.cpp
@@ -3,10 +3,12 @@
#include "qquickflipable_p.h"
#include "qquickitem_p.h"
-
+#include "qquicktranslate_p.h"
#include <QtQml/qqmlinfo.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
// XXX todo - i think this needs work and a bit of a re-think
@@ -201,9 +203,20 @@ void QQuickFlipable::updatePolish()
d->updateSide();
}
-// determination on the currently visible side of the flipable
-// has to be done on the complete scene transform to give
-// correct results.
+/*! \internal
+ Flipable must use the complete scene transform to correctly determine the
+ currently visible side.
+
+ It must also be independent of camera distance, in case the contents are
+ too wide: for rotation transforms we simply call QMatrix4x4::rotate(),
+ whereas QQuickRotation::applyTo(QMatrix4x4*) calls
+ QMatrix4x4::projectedRotate() which by default assumes the camera distance
+ is 1024 virtual pixels. So for example if contents inside Flipable are to
+ be flipped around the y axis, and are wider than 1024*2, some of the
+ rendering goes behind the "camera". That's expected for rendering (since we
+ didn't provide API to change camera distance), but not ok for deciding when
+ to flip.
+*/
void QQuickFlipablePrivate::updateSide()
{
Q_Q(QQuickFlipable);
@@ -213,35 +226,50 @@ void QQuickFlipablePrivate::updateSide()
sideDirty = false;
- QTransform sceneTransform;
- itemToParentTransform(sceneTransform);
-
- QPointF p1(0, 0);
- QPointF p2(1, 0);
- QPointF p3(1, 1);
-
- QPointF scenep1 = sceneTransform.map(p1);
- QPointF scenep2 = sceneTransform.map(p2);
- QPointF scenep3 = sceneTransform.map(p3);
-#if 0
- p1 = q->mapToParent(p1);
- p2 = q->mapToParent(p2);
- p3 = q->mapToParent(p3);
-#endif
-
- qreal cross = (scenep1.x() - scenep2.x()) * (scenep3.y() - scenep2.y()) -
- (scenep1.y() - scenep2.y()) * (scenep3.x() - scenep2.x());
-
- wantBackYFlipped = scenep1.x() >= scenep2.x();
- wantBackXFlipped = scenep2.y() >= scenep3.y();
-
- QQuickFlipable::Side newSide;
- if (cross > 0) {
- newSide = QQuickFlipable::Back;
- } else {
- newSide = QQuickFlipable::Front;
+ QMatrix4x4 sceneTransform;
+
+ const qreal tx = x.value();
+ const qreal ty = y.value();
+ if (!qFuzzyIsNull(tx) || !qFuzzyIsNull(ty))
+ sceneTransform.translate(tx, ty);
+
+ for (const auto *transform : std::as_const(transforms)) {
+ if (const auto *rot = qobject_cast<const QQuickRotation *>(transform)) {
+ // rotation is a special case: we want to call rotate() instead of projectedRotate()
+ const auto angle = rot->angle();
+ const auto axis = rot->axis();
+ if (!(qFuzzyIsNull(angle) || axis.isNull())) {
+ sceneTransform.translate(rot->origin());
+ sceneTransform.rotate(angle, axis.x(), axis.y(), axis.z());
+ sceneTransform.translate(-rot->origin());
+ }
+ } else {
+ transform->applyTo(&sceneTransform);
+ }
}
+ const bool hasRotation = !qFuzzyIsNull(rotation());
+ const bool hasScale = !qFuzzyCompare(scale(), 1);
+ if (hasScale || hasRotation) {
+ QPointF tp = computeTransformOrigin();
+ sceneTransform.translate(tp.x(), tp.y());
+ if (hasScale)
+ sceneTransform.scale(scale(), scale());
+ if (hasRotation)
+ sceneTransform.rotate(rotation(), 0, 0, 1);
+ sceneTransform.translate(-tp.x(), -tp.y());
+ }
+
+ const QVector3D origin(sceneTransform.map(QPointF(0, 0)));
+ const QVector3D right = QVector3D(sceneTransform.map(QPointF(1, 0))) - origin;
+ const QVector3D top = QVector3D(sceneTransform.map(QPointF(0, 1))) - origin;
+
+ wantBackYFlipped = right.x() < 0;
+ wantBackXFlipped = top.y() < 0;
+
+ const QQuickFlipable::Side newSide =
+ QVector3D::crossProduct(top, right).z() > 0 ? QQuickFlipable::Back : QQuickFlipable::Front;
+
if (newSide != current) {
current = newSide;
if (current == QQuickFlipable::Back && back)
diff --git a/src/quick/items/qquickflipable_p.h b/src/quick/items/qquickflipable_p.h
index 78abbf740f..93f012a844 100644
--- a/src/quick/items/qquickflipable_p.h
+++ b/src/quick/items/qquickflipable_p.h
@@ -28,7 +28,7 @@ QT_REQUIRE_CONFIG(quick_flipable);
QT_BEGIN_NAMESPACE
class QQuickFlipablePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickFlipable : public QQuickItem
+class Q_QUICK_EXPORT QQuickFlipable : public QQuickItem
{
Q_OBJECT
@@ -71,6 +71,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFlipable)
-
#endif // QQUICKFLIPABLE_P_H
diff --git a/src/quick/items/qquickfocusscope_p.h b/src/quick/items/qquickfocusscope_p.h
index 530227f8ec..17b07d6a6e 100644
--- a/src/quick/items/qquickfocusscope_p.h
+++ b/src/quick/items/qquickfocusscope_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickFocusScope : public QQuickItem
+class Q_QUICK_EXPORT QQuickFocusScope : public QQuickItem
{
Q_OBJECT
QML_NAMED_ELEMENT(FocusScope)
@@ -32,6 +32,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFocusScope)
-
#endif // QQUICKFOCUSSCOPE_P_H
diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
index 0633d01ce4..cdae6fbec7 100644
--- a/src/quick/items/qquickframebufferobject.cpp
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -8,7 +8,7 @@
#include <private/qquickitem_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <qsgtextureprovider.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QSGSimpleTextureNode>
#include <QSGRendererInterface>
@@ -81,7 +81,7 @@ public:
* and can be used directly in \l {ShaderEffect}{ShaderEffects} and other
* classes that consume texture providers.
*
- * \sa {Scene Graph - Rendering FBOs}, {Scene Graph and Rendering}
+ * \sa {Scene Graph and Rendering}
*/
/*!
diff --git a/src/quick/items/qquickgraphicsconfiguration.cpp b/src/quick/items/qquickgraphicsconfiguration.cpp
index ec0a9981dc..1cfa160e18 100644
--- a/src/quick/items/qquickgraphicsconfiguration.cpp
+++ b/src/quick/items/qquickgraphicsconfiguration.cpp
@@ -3,10 +3,7 @@
#include "qquickgraphicsconfiguration_p.h"
#include <QCoreApplication>
-
-#if QT_CONFIG(vulkan)
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -57,8 +54,10 @@ QT_BEGIN_NAMESPACE
Vulkan, or graphics APIs where the concept is applicable. Where some
concepts are not applicable, the related settings are simply ignored.
- Examples of functions in this category are preferredInstanceExtensions()
- and setDeviceExtensions().
+ Examples of functions in this category are setDeviceExtensions() and
+ preferredInstanceExtensions(). The latter is useful when the application
+ manages its own \l QVulkanInstance which is then associated with the
+ QQuickWindow via \l QWindow::setVulkanInstance().
\section1 Qt Quick Scene Graph Renderer Configuration
@@ -181,7 +180,8 @@ QT_BEGIN_NAMESPACE
- With pipeline cache saving enabled, Qt stores all render and compute
pipelines encountered into an MTLBinaryArchive. Saving the pipeline cache
stores the blob retrieved from the archive, with additional metadata to
- identify the device.
+ identify the device. \b{Note:} currently MTLBinaryArchive usage is disabled
+ on macOS and iOS due to various issues on some hardware and OS versions.
\li OpenGL - There is no native concept of pipelines, the "pipeline cache"
stores a collection of program binaries retrieved via
@@ -190,7 +190,7 @@ QT_BEGIN_NAMESPACE
metadata to identify the device, driver, and its version that the binaries
were retrieved from. Persistent caching of program binaries is not new in
Qt: Qt 5 already had similar functionality in QOpenGLShaderProgram, see
- \l{QOpenGLShaderProgram::addCacheableShaderFromSourceCode()}{addCacheableShaderFromSourceCode()}
+ \l{QOpenGLShaderProgram::}{addCacheableShaderFromSourceCode()}
for example. In fact that mechanism is always active in Qt 6 as well when
using Qt Quick with OpenGL. However, when using the new, graphics API
independent pipeline cache abstraction provided here, the Qt 5 era program
@@ -212,24 +212,25 @@ QT_BEGIN_NAMESPACE
HLSL shader. A good example is Qt Quick 3D, where the runtime-generated
shaders for materials imply having to deal with HLSL source code. Saving
and reloading the Qt Quick pipeline cache can therefore bring considerable
- improvements in scenes with one or more \l{QtQuick3D::}View3D items in
+ improvements in scenes with one or more \l{View3D} items in
them. A counterexample may be Qt Quick itself: as most built-in shaders for
2D content ship with DirectX bytecode generated at build time, the cache is
not going to present any significant improvements.
\endlist
- All this is independent from the shader processing performed by the \l
- QtShaderTools module and its command-line tools such as \c qsb. As an
- example, take Vulkan. Having the Vulkan-compatible GLSL source code
- compiled to SPIR-V either at offline or build time (directly via qsb or
- CMake) is good, because the expensive compilation from source form is
- avoided at run time. SPIR-V is however a vendor-independent intermediate
- format. At runtime, when constructing graphics or compute pipelines, there
- is likely another round of compilation happening, this time from the
- intermediate format to the vendor-specific instruction set of the GPU (and
- this may be dependent on certain state in the graphics pipeline and the
- render targets as well). The pipeline cache helps with this latter phase.
+ All this is independent from the shader processing performed by the
+ \l [QtShaderTools]{Qt Shader Tools} module and its command-line tools such
+ as \c qsb. As an example, take Vulkan. Having the Vulkan-compatible GLSL
+ source code compiled to SPIR-V either at offline or build time (directly
+ via qsb or CMake) is good, because the expensive compilation from source
+ form is avoided at run time. SPIR-V is however a vendor-independent
+ intermediate format. At runtime, when constructing graphics or compute
+ pipelines, there is likely another round of compilation happening, this
+ time from the intermediate format to the vendor-specific instruction set of
+ the GPU (and this may be dependent on certain state in the graphics
+ pipeline and the render targets as well). The pipeline cache helps with
+ this latter phase.
\note Many graphics API implementation employ their own persistent disk
cache transparently to the applications. Using the pipeline cache feature
@@ -572,6 +573,51 @@ bool QQuickGraphicsConfiguration::isDebugMarkersEnabled() const
}
/*!
+ When enabled, GPU timing data is collected from command buffers on
+ platforms and 3D APIs where this is supported. This data is then printed in
+ the renderer logs that can be enabled via \c{QSG_RENDER_TIMING} environment
+ variable or logging categories such as \c{qt.scenegraph.time.renderloop},
+ and may also be made visible to other modules, such as Qt Quick 3D's
+ \l DebugView item.
+
+ By default this is disabled, because collecting the data may involve
+ additional work, such as inserting timestamp queries in the command stream,
+ depending on the underlying graphics API. To enable, either call this
+ function with \a enable set to true, or set the \c{QSG_RHI_PROFILE}
+ environment variable to a non-zero value.
+
+ Graphics APIs where this can be expected to be supported are Direct 3D 11,
+ Direct 3D 12, Vulkan (as long as the underlying Vulkan implementation
+ supports timestamp queries), Metal, and OpenGL with a core or compatibility
+ profile context for version 3.3 or newer. Timestamps are not supported with
+ OpenGL ES.
+
+ \since 6.6
+
+ \sa timestampsEnabled(), setDebugMarkers()
+ */
+void QQuickGraphicsConfiguration::setTimestamps(bool enable)
+{
+ if (d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps) != enable) {
+ detach();
+ d->flags.setFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps, enable);
+ }
+}
+
+/*!
+ \return true if GPU timing collection is enabled.
+
+ By default the value is false.
+
+ \since 6.6
+ \sa setTimestamps()
+ */
+bool QQuickGraphicsConfiguration::timestampsEnabled() const
+{
+ return d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps);
+}
+
+/*!
Requests choosing an adapter or physical device that uses software-based
rasterization. Applicable only when the underlying API has support for
enumerating adapters (for example, Direct 3D or Vulkan), and is ignored
@@ -624,7 +670,7 @@ bool QQuickGraphicsConfiguration::prefersSoftwareDevice() const
\since 6.5
- \sa isAutomaticPipelineCacheEnbled()
+ \sa isAutomaticPipelineCacheEnabled()
*/
void QQuickGraphicsConfiguration::setAutomaticPipelineCache(bool enable)
{
@@ -779,9 +825,9 @@ QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate()
if (enableDebugLayer)
flags |= EnableDebugLayer;
- static const bool enableDebugMarkers = qEnvironmentVariableIntValue("QSG_RHI_PROFILE");
- if (enableDebugMarkers)
- flags |= EnableDebugMarkers;
+ static const bool enableProfilingRelated = qEnvironmentVariableIntValue("QSG_RHI_PROFILE");
+ if (enableProfilingRelated)
+ flags |= EnableDebugMarkers | EnableTimestamps;
static const bool preferSoftwareDevice = qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER");
if (preferSoftwareDevice)
@@ -801,12 +847,12 @@ QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate()
pipelineCacheLoadFile = pipelineCacheLoadFileEnv;
}
-QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate *other)
+QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate &other)
: ref(1),
- deviceExtensions(other->deviceExtensions),
- flags(other->flags),
- pipelineCacheSaveFile(other->pipelineCacheSaveFile),
- pipelineCacheLoadFile(other->pipelineCacheLoadFile)
+ deviceExtensions(other.deviceExtensions),
+ flags(other.flags),
+ pipelineCacheSaveFile(other.pipelineCacheSaveFile),
+ pipelineCacheLoadFile(other.pipelineCacheLoadFile)
{
}
diff --git a/src/quick/items/qquickgraphicsconfiguration.h b/src/quick/items/qquickgraphicsconfiguration.h
index d520a4a5ab..95777a1c51 100644
--- a/src/quick/items/qquickgraphicsconfiguration.h
+++ b/src/quick/items/qquickgraphicsconfiguration.h
@@ -34,6 +34,9 @@ public:
void setDebugMarkers(bool enable);
bool isDebugMarkersEnabled() const;
+ void setTimestamps(bool enable);
+ bool timestampsEnabled() const;
+
void setPreferSoftwareDevice(bool enable);
bool prefersSoftwareDevice() const;
diff --git a/src/quick/items/qquickgraphicsconfiguration_p.h b/src/quick/items/qquickgraphicsconfiguration_p.h
index 12980d154a..08b5e6026f 100644
--- a/src/quick/items/qquickgraphicsconfiguration_p.h
+++ b/src/quick/items/qquickgraphicsconfiguration_p.h
@@ -21,20 +21,21 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickGraphicsConfigurationPrivate
+class Q_QUICK_EXPORT QQuickGraphicsConfigurationPrivate
{
public:
static QQuickGraphicsConfigurationPrivate *get(QQuickGraphicsConfiguration *p) { return p->d; }
static const QQuickGraphicsConfigurationPrivate *get(const QQuickGraphicsConfiguration *p) { return p->d; }
QQuickGraphicsConfigurationPrivate();
- QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate *other);
+ QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate &other);
enum Flag {
UseDepthBufferFor2D = 0x01,
EnableDebugLayer = 0x02,
EnableDebugMarkers = 0x04,
PreferSoftwareDevice = 0x08,
- AutoPipelineCache = 0x10
+ AutoPipelineCache = 0x10,
+ EnableTimestamps = 0x20
};
Q_DECLARE_FLAGS(Flags, Flag)
diff --git a/src/quick/items/qquickgraphicsdevice.cpp b/src/quick/items/qquickgraphicsdevice.cpp
index 5bd0865660..84eeb3e69e 100644
--- a/src/quick/items/qquickgraphicsdevice.cpp
+++ b/src/quick/items/qquickgraphicsdevice.cpp
@@ -93,11 +93,14 @@ QQuickGraphicsDevice QQuickGraphicsDevice::fromOpenGLContext(QOpenGLContext *con
/*!
\return a new QQuickGraphicsDevice describing a DXGI adapter and D3D feature level.
- This factory function is suitable for Direct3D 11, particularly in
+ This factory function is suitable for Direct3D 11 and 12, particularly in
combination with OpenXR. \a adapterLuidLow and \a adapterLuidHigh together
specify a LUID, while a featureLevel specifies a \c{D3D_FEATURE_LEVEL_}
value. \a featureLevel can be set to 0 if it is not intended to be
specified, in which case the scene graph's defaults will be used.
+
+ \note With Direct 3D 12 \a featureLevel specifies the \c minimum feature
+ level passed on to D3D12CreateDevice().
*/
#if defined(Q_OS_WIN) || defined(Q_QDOC)
QQuickGraphicsDevice QQuickGraphicsDevice::fromAdapter(quint32 adapterLuidLow,
@@ -120,6 +123,10 @@ QQuickGraphicsDevice QQuickGraphicsDevice::fromAdapter(quint32 adapterLuidLow,
be a \c{ID3D11Device*}, \a context is expected to be a
\c{ID3D11DeviceContext*}.
+ It also supports Direct 3D 12, if that is the 3D API used at run time. With
+ D3D12 \a context is unused and can be set to null. \a device is expected to
+ be a \c{ID3D12Device*}.
+
\note the resulting QQuickGraphicsDevice does not own any native resources,
it merely contains references. It is the caller's responsibility to ensure
that the native resource exists as long as necessary.
@@ -146,7 +153,7 @@ QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceAndContext(void *device, vo
that the native resource exists as long as necessary.
*/
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceAndCommandQueue(MTLDevice *device,
MTLCommandQueue *commandQueue)
{
@@ -205,12 +212,14 @@ QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceObjects(VkPhysicalDevice ph
#endif
/*!
- \internal
+ \return a new QQuickGraphicsDevice referencing an existing \a rhi object.
\note Similarly to fromOpenGLContext(), the caller must be careful to only
share a QRhi (and so the underlying graphics context or device) between
QQuickWindows that are known to be compatible, not breaking the underlying
graphics API's rules when it comes to threading, pixel formats, etc.
+
+ \since 6.6
*/
QQuickGraphicsDevice QQuickGraphicsDevice::fromRhi(QRhi *rhi)
{
@@ -226,10 +235,10 @@ QQuickGraphicsDevicePrivate::QQuickGraphicsDevicePrivate()
{
}
-QQuickGraphicsDevicePrivate::QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate *other)
+QQuickGraphicsDevicePrivate::QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate &other)
: ref(1),
- type(other->type),
- u(other->u)
+ type(other.type),
+ u(other.u)
{
}
diff --git a/src/quick/items/qquickgraphicsdevice.h b/src/quick/items/qquickgraphicsdevice.h
index 40c8020a2d..d668f5e576 100644
--- a/src/quick/items/qquickgraphicsdevice.h
+++ b/src/quick/items/qquickgraphicsdevice.h
@@ -10,7 +10,7 @@
#include <QtGui/qvulkaninstance.h>
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
Q_FORWARD_DECLARE_OBJC_CLASS(MTLDevice);
Q_FORWARD_DECLARE_OBJC_CLASS(MTLCommandQueue);
#endif
@@ -40,7 +40,7 @@ public:
static QQuickGraphicsDevice fromDeviceAndContext(void *device, void *context);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
static QQuickGraphicsDevice fromDeviceAndCommandQueue(MTLDevice *device, MTLCommandQueue *commandQueue);
#endif
diff --git a/src/quick/items/qquickgraphicsdevice_p.h b/src/quick/items/qquickgraphicsdevice_p.h
index 74f1e3af6e..d1c3bf1284 100644
--- a/src/quick/items/qquickgraphicsdevice_p.h
+++ b/src/quick/items/qquickgraphicsdevice_p.h
@@ -21,13 +21,13 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickGraphicsDevicePrivate
+class Q_QUICK_EXPORT QQuickGraphicsDevicePrivate
{
public:
static QQuickGraphicsDevicePrivate *get(QQuickGraphicsDevice *p) { return p->d; }
static const QQuickGraphicsDevicePrivate *get(const QQuickGraphicsDevice *p) { return p->d; }
QQuickGraphicsDevicePrivate();
- QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate *other);
+ QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate &other);
enum class Type {
Null,
diff --git a/src/quick/items/qquickgraphicsinfo.cpp b/src/quick/items/qquickgraphicsinfo.cpp
index 45c86f1b24..7b84a7eb54 100644
--- a/src/quick/items/qquickgraphicsinfo.cpp
+++ b/src/quick/items/qquickgraphicsinfo.cpp
@@ -56,16 +56,16 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property describes the graphics API that is currently in use.
The possible values are:
- \list
- \li GraphicsInfo.Unknown - the default value when no active scenegraph is associated with the item
- \li GraphicsInfo.Software - Qt Quick's software renderer based on QPainter with the raster paint engine
- \li GraphicsInfo.OpenVG - OpenVG
- \li GraphicsInfo.OpenGL - OpenGL or OpenGL ES on top of QRhi, a graphics abstraction layer
- \li GraphicsInfo.Direct3D11 - Direct3D 11 on top of QRhi, a graphics abstraction layer
- \li GraphicsInfo.Vulkan - Vulkan on top of QRhi, a graphics abstraction layer
- \li GraphicsInfo.Metal - Metal on top of QRhi, a graphics abstraction layer
- \li GraphicsInfo.Null - Null (no output) on top of QRhi, a graphics abstraction layer
- \endlist
+
+ \value GraphicsInfo.Unknown the default value when no active scenegraph is associated with the item
+ \value GraphicsInfo.Software Qt Quick's software renderer based on QPainter with the raster paint engine
+ \value GraphicsInfo.OpenVG OpenVG
+ \value GraphicsInfo.OpenGL OpenGL or OpenGL ES on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Direct3D11 Direct3D 11 on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Direct3D12 Direct3D 12 on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Vulkan Vulkan on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Metal Metal on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Null Null (no output) on top of QRhi, a graphics abstraction layer
*/
/*!
@@ -74,12 +74,10 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property contains the shading language supported by the Qt Quick
backend the application is using.
- \list
- \li GraphicsInfo.UnknownShadingLanguage - Not yet known due to no window and scenegraph associated
- \li GraphicsInfo.GLSL - GLSL or GLSL ES
- \li GraphicsInfo.HLSL - HLSL
- \li GraphicsInfo.RhiShader - QShader
- \endlist
+ \value GraphicsInfo.UnknownShadingLanguage Not yet known due to no window and scenegraph associated
+ \value GraphicsInfo.GLSL GLSL or GLSL ES
+ \value GraphicsInfo.HLSL HLSL
+ \value GraphicsInfo.RhiShader QShader
\note The value is only up-to-date once the item is associated with a
window. Bindings relying on the value have to keep this in mind since the
@@ -100,10 +98,8 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property contains a bitmask of the shader compilation approaches
supported by the Qt Quick backend the application is using.
- \list
- \li GraphicsInfo.RuntimeCompilation
- \li GraphicsInfo.OfflineCompilation
- \endlist
+ \value GraphicsInfo.RuntimeCompilation
+ \value GraphicsInfo.OfflineCompilation
With OpenGL the value is GraphicsInfo.RuntimeCompilation, which corresponds
to the traditional way of using ShaderEffect. Non-OpenGL backends are
@@ -127,11 +123,9 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property contains a bitmask of the supported ways of providing shader
sources.
- \list
- \li GraphicsInfo.ShaderSourceString
- \li GraphicsInfo.ShaderSourceFile
- \li GraphicsInfo.ShaderByteCode
- \endlist
+ \value GraphicsInfo.ShaderSourceString
+ \value GraphicsInfo.ShaderSourceFile
+ \value GraphicsInfo.ShaderByteCode
With OpenGL the value is GraphicsInfo.ShaderSourceString, which corresponds
to the traditional way of inlining GLSL source code into QML. Other,
@@ -182,11 +176,10 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property holds the configured OpenGL context profile.
The possible values are:
- \list
- \li GraphicsInfo.OpenGLNoProfile (default) - OpenGL version is lower than 3.2 or OpenGL is not in use.
- \li GraphicsInfo.OpenGLCoreProfile - Functionality deprecated in OpenGL version 3.0 is not available.
- \li GraphicsInfo.OpenGLCompatibilityProfile - Functionality from earlier OpenGL versions is available.
- \endlist
+
+ \value GraphicsInfo.OpenGLNoProfile (default) OpenGL version is lower than 3.2 or OpenGL is not in use.
+ \value GraphicsInfo.OpenGLCoreProfile Functionality deprecated in OpenGL version 3.0 is not available.
+ \value GraphicsInfo.OpenGLCompatibilityProfile Functionality from earlier OpenGL versions is available.
Reusable QML components will typically use this property in bindings in order to
choose between core and non core profile compatible shader sources.
@@ -203,11 +196,10 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
other than OpenGL.
The possible values are:
- \list
- \li GraphicsInfo.SurfaceFormatUnspecified (default) - Unspecified rendering method
- \li GraphicsInfo.SurfaceFormatOpenGL - Desktop OpenGL or other graphics API
- \li GraphicsInfo.SurfaceFormatOpenGLES - OpenGL ES
- \endlist
+
+ \value GraphicsInfo.SurfaceFormatUnspecified (default) Unspecified rendering method
+ \value GraphicsInfo.SurfaceFormatOpenGL Desktop OpenGL or other graphics API
+ \value GraphicsInfo.SurfaceFormatOpenGLES OpenGL ES
\note This is applicable only to OpenGL.
diff --git a/src/quick/items/qquickgraphicsinfo_p.h b/src/quick/items/qquickgraphicsinfo_p.h
index be3bef7fa9..a18694a83b 100644
--- a/src/quick/items/qquickgraphicsinfo_p.h
+++ b/src/quick/items/qquickgraphicsinfo_p.h
@@ -55,6 +55,7 @@ public:
Vulkan = QSGRendererInterface::Vulkan,
Metal = QSGRendererInterface::Metal,
Null = QSGRendererInterface::Null,
+ Direct3D12 = QSGRendererInterface::Direct3D12,
OpenGLRhi = QSGRendererInterface::OpenGLRhi,
Direct3D11Rhi = QSGRendererInterface::Direct3D11Rhi,
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 7f45d45708..e67d80b2d6 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -162,7 +162,9 @@ public:
void setPosition(qreal pos) override;
void layoutVisibleItems(int fromModelIndex = 0) override;
bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView) override;
+#if QT_CONFIG(quick_viewtransitions)
void translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult) override;
+#endif
bool needsRefillForAddedOrRemovedIndex(int index) const override;
qreal headerSize() const override;
@@ -181,7 +183,7 @@ public:
void fixupPosition() override;
void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity) override;
+ QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity) override;
QQuickGridView::Flow flow;
qreal cellWidth;
@@ -503,8 +505,10 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
qCDebug(lcItemViewDelegateLifecycle) << "refill: append item" << modelIndex << colPos << rowPos;
if (!(item = static_cast<FxGridItemSG*>(createItem(modelIndex, incubationMode))))
break;
+#if QT_CONFIG(quick_viewtransitions)
if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
item->setPosition(colPos, rowPos, true);
+#endif
QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
visibleItems.append(item);
if (++colNum >= columns) {
@@ -538,8 +542,10 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
if (!(item = static_cast<FxGridItemSG*>(createItem(visibleIndex-1, incubationMode))))
break;
--visibleIndex;
+#if QT_CONFIG(quick_viewtransitions)
if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
item->setPosition(colPos, rowPos, true);
+#endif
QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
visibleItems.prepend(item);
if (--colNum < 0) {
@@ -555,11 +561,14 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
void QQuickGridViewPrivate::removeItem(FxViewItem *item)
{
+#if QT_CONFIG(quick_viewtransitions)
if (item->transitionScheduledOrRunning()) {
qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName();
item->releaseAfterTransition = true;
releasePendingTransition.append(item);
- } else {
+ } else
+#endif
+ {
releaseItem(item, QQmlDelegateModel::NotReusable);
}
}
@@ -883,7 +892,11 @@ void QQuickGridViewPrivate::initializeCurrentItem()
FxViewItem *actualItem = visibleItem(currentIndex);
// don't reposition the item if it's about to be transitioned to another position
- if ((!actualItem || !actualItem->transitionScheduledOrRunning()))
+ if ((!actualItem
+#if QT_CONFIG(quick_viewtransitions)
+ || !actualItem->transitionScheduledOrRunning()
+#endif
+ ))
gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
}
}
@@ -995,13 +1008,13 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
}
bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
+ QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity)
{
data.fixingUp = false;
moveReason = Mouse;
if ((!haveHighlightRange || highlightRange != QQuickGridView::StrictlyEnforceRange)
&& snapMode == QQuickGridView::NoSnap) {
- return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
+ return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, eventType, velocity);
}
qreal maxDistance = 0;
qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
@@ -1051,7 +1064,7 @@ bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
else
v = maxVelocity;
}
- qreal accel = deceleration;
+ qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
qreal v2 = v * v;
qreal overshootDist = 0.0;
if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickGridView::SnapOneRow) {
@@ -1253,23 +1266,28 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
/*!
\qmlattachedproperty bool QtQuick::GridView::isCurrentItem
+ \readonly
+
This attached property is true if this delegate is the current item; otherwise false.
It is attached to each instance of the delegate.
+
+ \snippet qml/gridview/gridview.qml isCurrentItem
*/
/*!
\qmlattachedproperty GridView QtQuick::GridView::view
+ \readonly
+
This attached property holds the view that manages this delegate instance.
It is attached to each instance of the delegate and also to the header, the footer
and the highlight delegates.
-
- \snippet qml/gridview/gridview.qml isCurrentItem
*/
/*!
\qmlattachedproperty bool QtQuick::GridView::delayRemove
+
This attached property holds whether the delegate may be destroyed. It
is attached to each instance of the delegate. The default value is false.
@@ -1364,9 +1382,53 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
/*!
\qmlproperty int QtQuick::GridView::count
- This property holds the number of items in the view.
+ This property holds the number of items in the model.
+*/
+
+/*!
+ \qmlproperty bool QtQuick::GridView::reuseItems
+
+ This property enables you to reuse items that are instantiated
+ from the \l delegate. If set to \c false, any currently
+ pooled items are destroyed.
+
+ This property is \c false by default.
+
+ \since 5.15
+
+ \sa {Reusing items}, pooled(), reused()
+*/
+
+/*!
+ \qmlattachedsignal QtQuick::GridView::pooled()
+
+ This signal is emitted after an item has been added to the reuse
+ pool. You can use it to pause ongoing timers or animations inside
+ the item, or free up resources that cannot be reused.
+
+ This signal is emitted only if the \l reuseItems property is \c true.
+
+ \sa {Reusing items}, reuseItems, reused()
*/
+/*!
+ \qmlattachedsignal QtQuick::GridView::reused()
+
+ This signal is emitted after an item has been reused. At this point, the
+ item has been taken out of the pool and placed inside the content view,
+ and the model properties such as \c index and \c row have been updated.
+
+ Other properties that are not provided by the model does not change when an
+ item is reused. You should avoid storing any state inside a delegate, but if
+ you do, manually reset that state on receiving this signal.
+
+ This signal is emitted when the item is reused, and not the first time the
+ item is created.
+
+ This signal is emitted only if the \l reuseItems property is \c true.
+
+ \sa {Reusing items}, reuseItems, pooled()
+*/
/*!
\qmlproperty Component QtQuick::GridView::highlight
@@ -1427,36 +1489,32 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
Valid values for \c highlightRangeMode are:
- \list
- \li GridView.ApplyRange - the view attempts to maintain the highlight within the range.
+ \value GridView.ApplyRange the view attempts to maintain the highlight within the range.
However, the highlight can move outside of the range at the ends of the view or due
to mouse interaction.
- \li GridView.StrictlyEnforceRange - the highlight never moves outside of the range.
+ \value GridView.StrictlyEnforceRange the highlight never moves outside of the range.
The current item changes if a keyboard or mouse action would cause the highlight to move
outside of the range.
- \li GridView.NoHighlightRange - this is the default value.
- \endlist
+ \value GridView.NoHighlightRange the default value
*/
/*!
- \qmlproperty enumeration QtQuick::GridView::layoutDirection
- This property holds the layout direction of the grid.
+ \qmlproperty enumeration QtQuick::GridView::layoutDirection
+ This property holds the layout direction of the grid.
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is
- dependent on the \l GridView::flow property.
- \li Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent
- on the \l GridView::flow property.
- \endlist
+ \value Qt.LeftToRight (default) Items will be laid out starting in the top, left corner. The flow is
+ dependent on the \l GridView::flow property.
+ \value Qt.RightToLeft Items will be laid out starting in the top, right corner. The flow is dependent
+ on the \l GridView::flow property.
- \b Note: If GridView::flow is set to GridView.FlowLeftToRight, this is not to be confused if
- GridView::layoutDirection is set to Qt.RightToLeft. The GridView.FlowLeftToRight flow value simply
- indicates that the flow is horizontal.
+ \b Note: If GridView::flow is set to GridView.FlowLeftToRight, this is not to be confused if
+ GridView::layoutDirection is set to Qt.RightToLeft. The GridView.FlowLeftToRight flow value simply
+ indicates that the flow is horizontal.
- \sa GridView::effectiveLayoutDirection, GridView::verticalLayoutDirection
+ \sa GridView::effectiveLayoutDirection, GridView::verticalLayoutDirection
*/
@@ -1477,10 +1535,8 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
Possible values:
- \list
- \li GridView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
- \li GridView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
- \endlist
+ \value GridView.TopToBottom (default) Items are laid out from the top of the view down to the bottom of the view.
+ \value GridView.BottomToTop Items are laid out from the bottom of the view up to the top of the view.
\sa GridView::layoutDirection
*/
@@ -1588,10 +1644,8 @@ void QQuickGridView::setHighlightMoveDuration(int duration)
Possible values:
- \list
- \li GridView.FlowLeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically
- \li GridView.FlowTopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally
- \endlist
+ \value GridView.FlowLeftToRight (default) Items are laid out from left to right, and the view scrolls vertically
+ \value GridView.FlowTopToBottom Items are laid out from top to bottom, and the view scrolls horizontally
*/
QQuickGridView::Flow QQuickGridView::flow() const
{
@@ -1668,15 +1722,12 @@ void QQuickGridView::setCellHeight(qreal cellHeight)
This property determines how the view scrolling will settle following a drag or flick.
The possible values are:
- \list
- \li GridView.NoSnap (default) - the view stops anywhere within the visible area.
- \li GridView.SnapToRow - the view settles with a row (or column for \c GridView.FlowTopToBottom flow)
- aligned with the start of the view.
- \li GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.FlowTopToBottom flow)
- away from the first visible row at the time the mouse button is released.
- This mode is particularly useful for moving one page at a time.
- \endlist
-
+ \value GridView.NoSnap (default) the view stops anywhere within the visible area.
+ \value GridView.SnapToRow the view settles with a row (or column for \c GridView.FlowTopToBottom flow)
+ aligned with the start of the view.
+ \value GridView.SnapOneRow the view will settle no more than one row (or column for \c GridView.FlowTopToBottom flow)
+ away from the first visible row at the time the mouse button is released.
+ This mode is particularly useful for moving one page at a time.
*/
QQuickGridView::SnapMode QQuickGridView::snapMode() const
{
@@ -2356,6 +2407,9 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
{
Q_Q(QQuickGridView);
+ if (q->size().isEmpty())
+ return false;
+
int modelIndex = change.index;
int count = change.count;
@@ -2404,6 +2458,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
}
}
+#if QT_CONFIG(quick_viewtransitions)
// Update the indexes of the following visible items.
for (FxViewItem *item : std::as_const(visibleItems)) {
if (item->index != -1 && item->index >= modelIndex) {
@@ -2414,6 +2469,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
}
}
+#endif
int prevVisibleCount = visibleItems.size();
if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
@@ -2443,9 +2499,11 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
insertResult->changedFirstItem = true;
if (!change.isMove()) {
addedItems->append(item);
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
else
+#endif
item->moveTo(QPointF(colPos, rowPos), true);
}
insertResult->sizeChangesBeforeVisiblePos += rowSize();
@@ -2497,13 +2555,19 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
if (change.isMove()) {
// we know this is a move target, since move displaced items that are
// shuffled into view due to a move would be added in refill()
- if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
+ if (newItem
+#if QT_CONFIG(quick_viewtransitions)
+ && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)
+#endif
+ )
movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
} else {
addedItems->append(item);
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
else
+#endif
item->moveTo(QPointF(colPos, rowPos), true);
}
insertResult->sizeChangesAfterVisiblePos += rowSize();
@@ -2523,6 +2587,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
return visibleItems.size() > prevVisibleCount;
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
{
if (!transitioner)
@@ -2562,6 +2627,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
}
}
}
+#endif
bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) const
{
@@ -2576,18 +2642,15 @@ bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) co
Positions the view such that the \a index is at the position specified by
\a mode:
- \list
- \li GridView.Beginning - position item at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
- \li GridView.Center - position item in the center of the view.
- \li GridView.End - position item at bottom (or right for horizontal orientation) of the view.
- \li GridView.Visible - if any part of the item is visible then take no action, otherwise
- bring the item into view.
- \li GridView.Contain - ensure the entire item is visible. If the item is larger than
- the view the item is positioned at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
- \li GridView.SnapPosition - position the item at \l preferredHighlightBegin. This mode
- is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
- via \l snapMode.
- \endlist
+ \value GridView.Beginning position item at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
+ \value GridView.Center position item in the center of the view.
+ \value GridView.End position item at bottom (or right for horizontal orientation) of the view.
+ \value GridView.Visible if any part of the item is visible then take no action, otherwise
+ bring the item into view.
+ \value GridView.Contain ensure the entire item is visible. If the item is larger than the view, the item
+ is positioned at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
+ \value GridView.SnapPosition position the item at \l preferredHighlightBegin. This mode is only valid if
+ \l highlightRangeMode is \c StrictlyEnforceRange or snapping is enabled via \l snapMode.
If positioning the view at the index would cause empty space to be displayed at
the beginning or end of the view, the view will be positioned at the boundary.
diff --git a/src/quick/items/qquickgridview_p.h b/src/quick/items/qquickgridview_p.h
index b90e5e6c9e..1f9698a706 100644
--- a/src/quick/items/qquickgridview_p.h
+++ b/src/quick/items/qquickgridview_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickGridViewAttached;
class QQuickGridViewPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickGridView : public QQuickItemView
+class Q_QUICK_EXPORT QQuickGridView : public QQuickItemView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickGridView)
@@ -101,6 +101,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickGridView)
-
#endif // QQUICKGRIDVIEW_P_H
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 3b6ddf729f..3aaa59819c 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -115,6 +115,43 @@ QQuickImagePrivate::QQuickImagePrivate()
convert foo.png \( +clone -alpha Extract \) -channel RGB -compose Multiply -composite foo_pm.png
\endcode
+ Do not confuse container formats, such as, \c KTX, and the format of the
+ actual texture data stored in the container file. For example, reading a
+ \c KTX file is supported on all platforms, independently of what GPU driver is
+ used at run time. However, this does not guarantee that the compressed
+ texture format, used by the data in the file, is supported at run time. For
+ example, if the KTX file contains compressed data with the format
+ \c{ETC2 RGBA8}, and the 3D graphics API implementation used at run time does not
+ support \c ETC2 compressed textures, the Image item will not display
+ anything.
+
+ \note Compressed texture format support is not under Qt's control, and it
+ is up to the application or device developer to ensure the compressed
+ texture data is provided in the appropriate format for the target
+ environment(s).
+
+ Do not assume that compressed format support is specific to a platform. It
+ may also be specific to the driver and 3D API implementation in use on that
+ particular platform. In practice, implementations of different 3D graphics
+ APIs (e.g., Vulkan and OpenGL) on the same platform (e.g., Windows) from
+ the same vendor for the same hardware may offer a different set of
+ compressed texture formats.
+
+ When targeting desktop environments (Windows, macOS, Linux) only, a general
+ recommendation is to consider using the \c{DXTn}/\c{BCn} formats since
+ these tend to have the widest support amongst the implementations of Direct
+ 3D, Vulkan, OpenGL, and Metal on these platforms. In contrast, when
+ targeting mobile or embedded devices, the \c ETC2 or \c ASTC formats are
+ likely to be a better choice since these are typically the formats
+ supported by the OpenGL ES implementations on such hardware.
+
+ An application that intends to run across desktop, mobile, and embedded
+ hardware should plan and design its use of compressed textures carefully.
+ It is highly likely that relying on a single format is not going to be
+ sufficient, and therefore the application will likely need to branch based
+ on the platform to use compressed textures in a format appropriate there,
+ or perhaps to skip using compressed textures in some cases.
+
\section1 Automatic Detection of File Extension
If the \l source URL indicates a non-existing local file or resource, the
@@ -182,10 +219,7 @@ void QQuickImagePrivate::setImage(const QImage &image)
{
Q_Q(QQuickImage);
pix.setImage(image);
-
q->pixmapChange();
- status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready;
-
q->update();
}
@@ -193,10 +227,7 @@ void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap)
{
Q_Q(QQuickImage);
pix.setPixmap(pixmap);
-
q->pixmapChange();
- status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready;
-
q->update();
}
@@ -205,15 +236,15 @@ void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap)
Set this property to define what happens when the source image has a different size
than the item.
- \list
- \li Image.Stretch - the image is scaled to fit
- \li Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
- \li Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
- \li Image.Tile - the image is duplicated horizontally and vertically
- \li Image.TileVertically - the image is stretched horizontally and tiled vertically
- \li Image.TileHorizontally - the image is stretched vertically and tiled horizontally
- \li Image.Pad - the image is not transformed
- \endlist
+
+ \value Image.Stretch the image is scaled to fit
+ \value Image.PreserveAspectFit the image is scaled uniformly to fit without cropping
+ \value Image.PreserveAspectCrop the image is scaled uniformly to fill, cropping if necessary
+ \value Image.Tile the image is duplicated horizontally and vertically
+ \value Image.TileVertically the image is stretched horizontally and tiled vertically
+ \value Image.TileHorizontally the image is stretched vertically and tiled horizontally
+ \value Image.Pad the image is not transformed
+ \br
\table
@@ -348,12 +379,11 @@ qreal QQuickImage::paintedHeight() const
\readonly
This property holds the status of image loading. It can be one of:
- \list
- \li Image.Null - no image has been set
- \li Image.Ready - the image has been loaded
- \li Image.Loading - the image is currently being loaded
- \li Image.Error - an error occurred while loading the image
- \endlist
+
+ \value Image.Null No image has been set
+ \value Image.Ready The image has been loaded
+ \value Image.Loading The image is currently being loaded
+ \value Image.Error An error occurred while loading the image
Use this status to provide an update or respond to the status change in some way.
For example, you could:
@@ -458,6 +488,8 @@ qreal QQuickImage::paintedHeight() const
\note \e {Changing this property dynamically causes the image source to be reloaded,
potentially even from the network, if it is not in the disk cache.}
+
+ \sa {Qt Quick Examples - Pointer Handlers}
*/
/*!
diff --git a/src/quick/items/qquickimage_p.h b/src/quick/items/qquickimage_p.h
index d7a55e529b..73dd3e2eb7 100644
--- a/src/quick/items/qquickimage_p.h
+++ b/src/quick/items/qquickimage_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
class QQuickImagePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickImage : public QQuickImageBase
+class Q_QUICK_EXPORT QQuickImage : public QQuickImageBase
{
Q_OBJECT
@@ -30,6 +30,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickImage : public QQuickImageBase
Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedGeometryChanged)
Q_PROPERTY(HAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged)
Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged)
+ Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged)
Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged REVISION(2, 3))
Q_PROPERTY(bool autoTransform READ autoTransform WRITE setAutoTransform NOTIFY autoTransformChanged REVISION(2, 5))
Q_PROPERTY(QRectF sourceClipRect READ sourceClipRect WRITE setSourceClipRect RESET resetSourceClipRect NOTIFY sourceClipRectChanged REVISION(2, 15))
@@ -100,5 +101,5 @@ private:
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickImage)
+
#endif // QQUICKIMAGE_P_H
diff --git a/src/quick/items/qquickimage_p_p.h b/src/quick/items/qquickimage_p_p.h
index 7b6dc00d6f..8f910385e8 100644
--- a/src/quick/items/qquickimage_p_p.h
+++ b/src/quick/items/qquickimage_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickImageTextureProvider : public QSGTextureProvider
+class Q_QUICK_EXPORT QQuickImageTextureProvider : public QSGTextureProvider
{
Q_OBJECT
public:
@@ -38,7 +38,7 @@ public:
bool m_mipmap;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickImagePrivate : public QQuickImageBasePrivate
+class Q_QUICK_EXPORT QQuickImagePrivate : public QQuickImageBasePrivate
{
Q_DECLARE_PUBLIC(QQuickImage)
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 78dfecc42a..bcc685d64c 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -15,16 +15,7 @@
QT_BEGIN_NAMESPACE
-bool isScalableImageFormat(const QUrl &url)
-{
- if (url.scheme() == QLatin1String("image"))
- return true;
-
- const QString stringUrl = url.path(QUrl::PrettyDecoded);
- return stringUrl.endsWith(QLatin1String("svg"))
- || stringUrl.endsWith(QLatin1String("svgz"))
- || stringUrl.endsWith(QLatin1String("pdf"));
-}
+using namespace Qt::Literals::StringLiterals;
// This function gives derived classes the chance set the devicePixelRatio
// if they're not happy with our implementation of it.
@@ -33,7 +24,7 @@ bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio
// QQuickImageProvider and SVG and PDF can generate a high resolution image when
// sourceSize is set. If sourceSize is not set then the provider default size will
// be used, as usual.
- const bool setDevicePixelRatio = isScalableImageFormat(url);
+ const bool setDevicePixelRatio = QQuickPixmap::isScalableImageFormat(url);
if (setDevicePixelRatio)
devicePixelRatio = targetDevicePixelRatio;
@@ -41,6 +32,28 @@ bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio
return setDevicePixelRatio;
}
+void QQuickImageBasePrivate::setStatus(QQuickImageBase::Status value)
+{
+ Q_Q(QQuickImageBase);
+
+ if (status == value)
+ return;
+
+ status = value;
+ emit q->statusChanged(status);
+}
+
+void QQuickImageBasePrivate::setProgress(qreal value)
+{
+ Q_Q(QQuickImageBase);
+
+ if (qFuzzyCompare(progress, value))
+ return;
+
+ progress = value;
+ emit q->progressChanged(progress);
+}
+
QQuickImageBase::QQuickImageBase(QQuickItem *parent)
: QQuickImplicitSizeItem(*(new QQuickImageBasePrivate), parent)
{
@@ -63,14 +76,12 @@ QQuickImageBase::Status QQuickImageBase::status() const
return d->status;
}
-
qreal QQuickImageBase::progress() const
{
Q_D(const QQuickImageBase);
return d->progress;
}
-
bool QQuickImageBase::asynchronous() const
{
Q_D(const QQuickImageBase);
@@ -145,6 +156,7 @@ void QQuickImageBase::setSourceClipRect(const QRectF &r)
return;
d->sourceClipRect = r;
+ d->providerOptions.setSourceClipRect(r);
emit sourceClipRectChanged();
if (isComponentComplete())
load();
@@ -253,11 +265,8 @@ void QQuickImageBase::loadEmptyUrl()
{
Q_D(QQuickImageBase);
d->pix.clear(this);
- if (d->progress != 0.0) {
- d->progress = 0.0;
- emit progressChanged(d->progress);
- }
- d->status = Null;
+ d->setProgress(0);
+ d->status = Null; // do not emit statusChanged until after setImplicitSize
setImplicitSize(0, 0); // also called in QQuickImageBase::pixmapChange, but not QQuickImage/QQuickBorderImage overrides
pixmapChange(); // This calls update() in QQuickBorderImage and QQuickImage, not in QQuickImageBase...
@@ -292,7 +301,7 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
d->devicePixelRatio = 1.0;
bool updatedDevicePixelRatio = false;
if (d->sourcesize.isValid()
- || (isScalableImageFormat(d->url) && d->url.scheme() != QLatin1String("image"))) {
+ || (QQuickPixmap::isScalableImageFormat(d->url) && d->url.scheme() != "image"_L1)) {
updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio);
}
@@ -304,6 +313,8 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
}
}
+ d->status = Null; // reset status, no emit
+
d->pix.load(qmlEngine(this),
loadUrl,
d->sourceClipRect.toRect(),
@@ -314,14 +325,8 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
d->devicePixelRatio);
if (d->pix.isLoading()) {
- if (d->progress != 0.0) {
- d->progress = 0.0;
- emit progressChanged(d->progress);
- }
- if (d->status != Loading) {
- d->status = Loading;
- emit statusChanged(d->status);
- }
+ d->setProgress(0);
+ d->setStatus(Loading);
static int thisRequestProgress = -1;
static int thisRequestFinished = -1;
@@ -360,19 +365,15 @@ void QQuickImageBase::requestFinished()
qmlWarning(this) << d->pix.error();
d->pix.clear(this);
d->status = Error;
- if (d->progress != 0.0) {
- d->progress = 0.0;
- emit progressChanged(d->progress);
- }
+ d->setProgress(0);
} else {
- d->status = Ready;
- if (d->progress != 1.0) {
- d->progress = 1.0;
- emit progressChanged(d->progress);
- }
+ d->status = Ready; // do not emit statusChanged until after setImplicitSize
+ d->setProgress(1);
}
+
pixmapChange();
emit statusChanged(d->status);
+
if (sourceSize() != d->oldSourceSize) {
d->oldSourceSize = sourceSize();
emit sourceSizeChanged();
@@ -396,10 +397,8 @@ void QQuickImageBase::requestFinished()
void QQuickImageBase::requestProgress(qint64 received, qint64 total)
{
Q_D(QQuickImageBase);
- if (d->status == Loading && total > 0) {
- d->progress = qreal(received)/total;
- emit progressChanged(d->progress);
- }
+ if (d->status == Loading && total > 0)
+ d->setProgress(qreal(received) / total);
}
void QQuickImageBase::itemChange(ItemChange change, const ItemChangeData &value)
diff --git a/src/quick/items/qquickimagebase_p.h b/src/quick/items/qquickimagebase_p.h
index 5ad698816d..08f9abbc63 100644
--- a/src/quick/items/qquickimagebase_p.h
+++ b/src/quick/items/qquickimagebase_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
class QQuickImageBasePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickImageBase : public QQuickImplicitSizeItem
+class Q_QUICK_EXPORT QQuickImageBase : public QQuickImplicitSizeItem
{
Q_OBJECT
@@ -31,7 +31,6 @@ class Q_QUICK_PRIVATE_EXPORT QQuickImageBase : public QQuickImplicitSizeItem
Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged)
- Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged)
Q_PROPERTY(bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged)
Q_PROPERTY(bool mirrorVertically READ mirrorVertically WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged REVISION(6, 2))
Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY currentFrameChanged REVISION(2, 14))
diff --git a/src/quick/items/qquickimagebase_p_p.h b/src/quick/items/qquickimagebase_p_p.h
index 04113da181..3cbb9facb0 100644
--- a/src/quick/items/qquickimagebase_p_p.h
+++ b/src/quick/items/qquickimagebase_p_p.h
@@ -18,23 +18,18 @@
#include "qquickimplicitsizeitem_p_p.h"
#include "qquickimagebase_p.h"
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
QT_BEGIN_NAMESPACE
class QNetworkReply;
-class Q_QUICK_PRIVATE_EXPORT QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_EXPORT QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate
{
Q_DECLARE_PUBLIC(QQuickImageBase)
public:
QQuickImageBasePrivate()
- : status(QQuickImageBase::Null),
- progress(0.0),
- devicePixelRatio(1.0),
- currentFrame(0),
- frameCount(0),
- async(false),
+ : async(false),
cache(true),
mirrorHorizontally(false),
mirrorVertically(false),
@@ -44,18 +39,23 @@ public:
virtual bool updateDevicePixelRatio(qreal targetDevicePixelRatio);
- QQuickPixmap pix;
- QQuickImageBase::Status status;
+ void setStatus(QQuickImageBase::Status value);
+ void setProgress(qreal value);
+
QUrl url;
- qreal progress;
+ QQuickPixmap pix;
QSize sourcesize;
QSize oldSourceSize;
- qreal devicePixelRatio;
QRectF sourceClipRect;
QQuickImageProviderOptions providerOptions;
QColorSpace colorSpace;
- int currentFrame;
- int frameCount;
+
+ int currentFrame = 0;
+ int frameCount = 0;
+ qreal progress = 0;
+ qreal devicePixelRatio = 1;
+ QQuickImageBase::Status status = QQuickImageBase::Null;
+
bool async : 1;
bool cache : 1;
bool mirrorHorizontally: 1;
diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h
index 507ad537ac..198c82f52c 100644
--- a/src/quick/items/qquickimplicitsizeitem_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
class QQuickImplicitSizeItemPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItem : public QQuickItem
+class Q_QUICK_EXPORT QQuickImplicitSizeItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged)
diff --git a/src/quick/items/qquickimplicitsizeitem_p_p.h b/src/quick/items/qquickimplicitsizeitem_p_p.h
index c1ac09f850..4510391e71 100644
--- a/src/quick/items/qquickimplicitsizeitem_p_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItemPrivate : public QQuickItemPrivate
+class Q_QUICK_EXPORT QQuickImplicitSizeItemPrivate : public QQuickItemPrivate
{
Q_DECLARE_PUBLIC(QQuickImplicitSizeItem)
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index cc860c9ae3..e7b2a31f04 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -48,6 +48,8 @@
# include <QtGui/qcursor.h>
#endif
+#include <QtCore/qpointer.h>
+
#include <algorithm>
#include <limits>
@@ -86,6 +88,14 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = nullptr, int depth = 1
}
}
+static void setActiveFocus(QQuickItem *item, Qt::FocusReason reason)
+{
+ QQuickItemPrivate *d = QQuickItemPrivate::get(item);
+ if (d->subFocusItem && d->window && d->flags & QQuickItem::ItemIsFocusScope)
+ QQuickWindowPrivate::get(d->window)->clearFocusInScope(item, d->subFocusItem, reason);
+ item->forceActiveFocus(reason);
+}
+
/*!
\qmltype Transform
\instantiates QQuickTransform
@@ -319,10 +329,10 @@ QVariant QQuickItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const
}
#endif // im
-void QQuickItemKeyFilter::shortcutOverride(QKeyEvent *event)
+void QQuickItemKeyFilter::shortcutOverrideEvent(QKeyEvent *event)
{
if (m_next)
- m_next->shortcutOverride(event);
+ m_next->shortcutOverrideEvent(event);
else
event->ignore();
}
@@ -568,14 +578,12 @@ void QQuickKeyNavigationAttached::setBacktab(QQuickItem *i)
This property determines whether the keys are processed before
or after the attached item's own key handling.
- \list
- \li KeyNavigation.BeforeItem - process the key events before normal
- item key processing. If the event is used for key navigation, it will be accepted and will not
- be passed on to the item.
- \li KeyNavigation.AfterItem (default) - process the key events after normal item key
- handling. If the item accepts the key event it will not be
- handled by the KeyNavigation attached property handler.
- \endlist
+ \value KeyNavigation.BeforeItem process the key events before normal
+ item key processing. If the event is used for key navigation, it will be accepted and
+ will not be passed on to the item.
+ \value KeyNavigation.AfterItem (default) process the key events after normal item key
+ handling. If the item accepts the key event it will not be
+ handled by the KeyNavigation attached property handler.
*/
QQuickKeyNavigationAttached::Priority QQuickKeyNavigationAttached::priority() const
{
@@ -868,14 +876,11 @@ bool QQuickKeysAttached::isConnected(const char *signalName) const
This property determines whether the keys are processed before
or after the attached item's own key handling.
- \list
- \li Keys.BeforeItem (default) - process the key events before normal
- item key processing. If the event is accepted it will not
- be passed on to the item.
- \li Keys.AfterItem - process the key events after normal item key
- handling. If the item accepts the key event it will not be
- handled by the Keys attached property handler.
- \endlist
+ \value Keys.BeforeItem (default) process the key events before normal item key processing.
+ If the event is accepted, it will not be passed on to the item.
+ \value Keys.AfterItem process the key events after normal item key handling. If the item
+ accepts the key event, it will not be handled by the
+ Keys attached property handler.
\sa {Key Handling Priorities}
*/
@@ -1385,7 +1390,7 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
}
#endif // im
-void QQuickKeysAttached::shortcutOverride(QKeyEvent *event)
+void QQuickKeysAttached::shortcutOverrideEvent(QKeyEvent *event)
{
Q_D(QQuickKeysAttached);
QQuickKeyEvent &keyEvent = d->theKeyEvent;
@@ -1700,6 +1705,53 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
}
}
+
+bool QQuickItemPrivate::setFocusIfNeeded(QEvent::Type eventType)
+{
+ Q_Q(QQuickItem);
+ const bool setFocusOnRelease = QGuiApplication::styleHints()->setFocusOnTouchRelease();
+ Qt::FocusPolicy policy = Qt::ClickFocus;
+
+ switch (eventType) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::TouchBegin:
+ if (setFocusOnRelease)
+ return false;
+ break;
+ case QEvent::MouseButtonRelease:
+ case QEvent::TouchEnd:
+ if (!setFocusOnRelease)
+ return false;
+ break;
+ case QEvent::Wheel:
+ policy = Qt::WheelFocus;
+ break;
+ default:
+ break;
+ }
+
+ if ((focusPolicy & policy) == policy) {
+ setActiveFocus(q, Qt::MouseFocusReason);
+ return true;
+ }
+
+ return false;
+}
+
+Qt::FocusReason QQuickItemPrivate::lastFocusChangeReason() const
+{
+ return static_cast<Qt::FocusReason>(focusReason);
+}
+
+void QQuickItemPrivate::setLastFocusChangeReason(Qt::FocusReason reason)
+{
+ if (focusReason == reason)
+ return;
+
+ focusReason = reason;
+}
+
/*!
\class QQuickItem
\brief The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
@@ -2211,6 +2263,11 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
*/
/*!
+ \fn void QQuickItem::focusPolicyChanged(Qt::FocusPolicy)
+ \internal
+*/
+
+/*!
\fn void QQuickItem::activeFocusOnTabChanged(bool)
\internal
*/
@@ -2381,7 +2438,7 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
return true;
#if QT_CONFIG(accessibility)
- QAccessible::Role role = QQuickItemPrivate::get(item)->accessibleRole();
+ QAccessible::Role role = QQuickItemPrivate::get(item)->effectiveAccessibleRole();
if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) {
return true;
} else if (role == QAccessible::ComboBox || role == QAccessible::SpinBox) {
@@ -2413,12 +2470,32 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
*/
bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward)
{
- QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward);
+ QQuickWindow *window = item->window();
+ const bool wrap = !window || window->isTopLevel();
+
+ QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward, wrap);
if (next == item)
return false;
- next->forceActiveFocus(forward ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ const auto reason = forward ? Qt::TabFocusReason : Qt::BacktabFocusReason;
+
+ if (!wrap && !next) {
+ // Focus chain wrapped and we are not top-level window
+ // Give focus to parent window
+ Q_ASSERT(window);
+ Q_ASSERT(window->parent());
+
+
+ qt_window_private(window->parent())->setFocusToTarget(
+ forward ? QWindowPrivate::FocusTarget::Next
+ : QWindowPrivate::FocusTarget::Prev,
+ reason);
+ window->parent()->requestActivate();
+ return true;
+ }
+
+ next->forceActiveFocus(reason);
return true;
}
@@ -2467,7 +2544,7 @@ QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int star
return nullptr;
}
-QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward)
+QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap)
{
Q_ASSERT(item);
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: item:" << item << ", forward:" << forward;
@@ -2561,6 +2638,14 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
}
current = parent;
} else if (hasChildren) {
+ if (!wrap && (forward || firstFromItem != from)) {
+ qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain:"
+ << "Focus chain about to wrap.";
+ // If focus chain wraps, we should give the parent window
+ // a chance to get focus, so we should stop here
+ return nullptr;
+ }
+
// Wrap around after checking all items forward
if (forward) {
current = firstChild;
@@ -2806,8 +2891,8 @@ void QQuickItem::stackBefore(const QQuickItem *sibling)
parentPrivate->childItems.move(myIndex, myIndex < siblingIndex ? siblingIndex - 1 : siblingIndex);
- parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
parentPrivate->markSortedChildrenDirty(this);
+ parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.size(); ++ii)
QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
@@ -2851,8 +2936,8 @@ void QQuickItem::stackAfter(const QQuickItem *sibling)
parentPrivate->childItems.move(myIndex, myIndex > siblingIndex ? siblingIndex + 1 : siblingIndex);
- parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
parentPrivate->markSortedChildrenDirty(this);
+ parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.size(); ++ii)
QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
@@ -3050,8 +3135,8 @@ void QQuickItemPrivate::derefWindow()
paintNode = nullptr;
for (int ii = 0; ii < childItems.size(); ++ii) {
- QQuickItem *child = childItems.at(ii);
- QQuickItemPrivate::get(child)->derefWindow();
+ if (QQuickItem *child = childItems.at(ii))
+ QQuickItemPrivate::get(child)->derefWindow();
}
dirty(Window);
@@ -3063,7 +3148,7 @@ void QQuickItemPrivate::derefWindow()
/*!
-Returns a transform that maps points from window space into item space.
+ Returns a transform that maps points from window space into item space.
*/
QTransform QQuickItemPrivate::windowToItemTransform() const
{
@@ -3072,21 +3157,21 @@ QTransform QQuickItemPrivate::windowToItemTransform() const
}
/*!
-Returns a transform that maps points from item space into window space.
+ Returns a transform that maps points from item space into window space.
*/
QTransform QQuickItemPrivate::itemToWindowTransform() const
{
// item's parent must not be itself, otherwise calling itemToWindowTransform() on it is infinite recursion
Q_ASSERT(!parentItem || QQuickItemPrivate::get(parentItem) != this);
QTransform rv = parentItem ? QQuickItemPrivate::get(parentItem)->itemToWindowTransform() : QTransform();
- itemToParentTransform(rv);
+ itemToParentTransform(&rv);
return rv;
}
/*!
-Motifies \a t with this items local transform relative to its parent.
+ Modifies \a t with this item's local transform relative to its parent.
*/
-void QQuickItemPrivate::itemToParentTransform(QTransform &t) const
+void QQuickItemPrivate::itemToParentTransform(QTransform *t) const
{
/* Read the current x and y values. As this is an internal method,
we don't care about it being usable in bindings. Instead, we
@@ -3098,21 +3183,21 @@ void QQuickItemPrivate::itemToParentTransform(QTransform &t) const
qreal x = this->x.valueBypassingBindings();
qreal y = this->y.valueBypassingBindings();
if (x || y)
- t.translate(x, y);
+ t->translate(x, y);
if (!transforms.isEmpty()) {
- QMatrix4x4 m(t);
+ QMatrix4x4 m(*t);
for (int ii = transforms.size() - 1; ii >= 0; --ii)
transforms.at(ii)->applyTo(&m);
- t = m.toTransform();
+ *t = m.toTransform();
}
if (scale() != 1. || rotation() != 0.) {
QPointF tp = computeTransformOrigin();
- t.translate(tp.x(), tp.y());
- t.scale(scale(), scale());
- t.rotate(rotation());
- t.translate(-tp.x(), -tp.y());
+ t->translate(tp.x(), tp.y());
+ t->scale(scale(), scale());
+ t->rotate(rotation());
+ t->translate(-tp.x(), -tp.y());
}
}
@@ -3201,6 +3286,8 @@ QQuickItemPrivate::QQuickItemPrivate()
, maybeHasSubsceneDeliveryAgent(true)
, subtreeTransformChangedEnabled(true)
, inDestructor(false)
+ , focusReason(Qt::OtherFocusReason)
+ , focusPolicy(Qt::NoFocus)
, dirtyAttributes(0)
, nextDirtyItem(nullptr)
, prevDirtyItem(nullptr)
@@ -3218,6 +3305,7 @@ QQuickItemPrivate::QQuickItemPrivate()
, baselineOffset(0)
, itemNodeInstance(nullptr)
, paintNode(nullptr)
+ , szPolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Fixed)
{
}
@@ -3242,6 +3330,17 @@ void QQuickItemPrivate::init(QQuickItem *parent)
}
}
+QLayoutPolicy QQuickItemPrivate::sizePolicy() const
+{
+ return szPolicy;
+}
+
+void QQuickItemPrivate::setSizePolicy(const QLayoutPolicy::Policy& horizontalPolicy, const QLayoutPolicy::Policy& verticalPolicy)
+{
+ szPolicy.setHorizontalPolicy(horizontalPolicy);
+ szPolicy.setVerticalPolicy(verticalPolicy);
+}
+
void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
{
if (!o)
@@ -3251,34 +3350,15 @@ void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
if (QQuickItem *item = qmlobject_cast<QQuickItem *>(o)) {
item->setParentItem(that);
- } else {
- if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
- if (pointerHandler->parent() != that) {
- qCDebug(lcHandlerParent) << "reparenting handler" << pointerHandler << ":" << pointerHandler->parent() << "->" << that;
- pointerHandler->setParent(that);
- }
- QQuickItemPrivate::get(that)->addPointerHandler(pointerHandler);
- } else {
- QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o);
- QQuickItem *item = that;
- QQuickWindow *itemWindow = that->window();
- while (!itemWindow && item && item->parentItem()) {
- item = item->parentItem();
- itemWindow = item->window();
- }
-
- if (thisWindow) {
- if (itemWindow) {
- qCDebug(lcTransient) << thisWindow << "is transient for" << itemWindow;
- thisWindow->setTransientParent(itemWindow);
- } else {
- QObject::connect(item, SIGNAL(windowChanged(QQuickWindow*)),
- thisWindow, SLOT(setTransientParent_helper(QQuickWindow*)));
- }
- }
- o->setParent(that);
- resources_append(prop, o);
+ } else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
+ if (pointerHandler->parent() != that) {
+ qCDebug(lcHandlerParent) << "reparenting handler" << pointerHandler << ":" << pointerHandler->parent() << "->" << that;
+ pointerHandler->setParent(that);
}
+ QQuickItemPrivate::get(that)->addPointerHandler(pointerHandler);
+ } else {
+ o->setParent(that);
+ resources_append(prop, o);
}
}
@@ -3354,6 +3434,22 @@ void QQuickItemPrivate::data_clear(QQmlListProperty<QObject> *property)
children_clear(&childrenProperty);
}
+void QQuickItemPrivate::data_removeLast(QQmlListProperty<QObject> *property)
+{
+ QQuickItem *item = static_cast<QQuickItem*>(property->object);
+ QQuickItemPrivate *privateItem = QQuickItemPrivate::get(item);
+
+ QQmlListProperty<QQuickItem> childrenProperty = privateItem->children();
+ if (children_count(&childrenProperty) > 0) {
+ children_removeLast(&childrenProperty);
+ return;
+ }
+
+ QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
+ if (resources_count(&resourcesProperty) > 0)
+ resources_removeLast(&resourcesProperty);
+}
+
QObject *QQuickItemPrivate::resources_at(QQmlListProperty<QObject> *prop, qsizetype index)
{
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
@@ -3390,6 +3486,21 @@ void QQuickItemPrivate::resources_clear(QQmlListProperty<QObject> *prop)
}
}
+void QQuickItemPrivate::resources_removeLast(QQmlListProperty<QObject> *prop)
+{
+ QQuickItem *quickItem = static_cast<QQuickItem *>(prop->object);
+ QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(quickItem);
+ if (quickItemPrivate->extra.isAllocated()) {//If extra is not allocated resources is empty.
+ QList<QObject *> *resources = &quickItemPrivate->extra->resourcesList;
+ if (resources->isEmpty())
+ return;
+
+ qmlobject_disconnect(resources->last(), QObject, SIGNAL(destroyed(QObject*)),
+ quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*)));
+ resources->removeLast();
+ }
+}
+
QQuickItem *QQuickItemPrivate::children_at(QQmlListProperty<QQuickItem> *prop, qsizetype index)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
@@ -3425,6 +3536,14 @@ void QQuickItemPrivate::children_clear(QQmlListProperty<QQuickItem> *prop)
p->childItems.at(0)->setParentItem(nullptr);
}
+void QQuickItemPrivate::children_removeLast(QQmlListProperty<QQuickItem> *prop)
+{
+ QQuickItem *that = static_cast<QQuickItem *>(prop->object);
+ QQuickItemPrivate *p = QQuickItemPrivate::get(that);
+ if (!p->childItems.isEmpty())
+ p->childItems.last()->setParentItem(nullptr);
+}
+
qsizetype QQuickItemPrivate::visibleChildren_count(QQmlListProperty<QQuickItem> *prop)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
@@ -3653,10 +3772,16 @@ void QQuickItemPrivate::siblingOrderChanged()
QQmlListProperty<QObject> QQuickItemPrivate::data()
{
- return QQmlListProperty<QObject>(q_func(), nullptr, QQuickItemPrivate::data_append,
- QQuickItemPrivate::data_count,
- QQuickItemPrivate::data_at,
- QQuickItemPrivate::data_clear);
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QObject> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::data_append;
+ result.count = QQuickItemPrivate::data_count;
+ result.at = QQuickItemPrivate::data_at;
+ result.clear = QQuickItemPrivate::data_clear;
+ result.removeLast = QQuickItemPrivate::data_removeLast;
+ return result;
}
/*!
@@ -4021,7 +4146,7 @@ void QQuickItem::inputMethodEvent(QInputMethodEvent *event)
/*!
This event handler can be reimplemented in a subclass to receive focus-in
- events for an item. The event information is provided by the \c event
+ events for an item. The event information is provided by the \a event
parameter.
\input item.qdocinc accepting-events
@@ -4029,8 +4154,9 @@ void QQuickItem::inputMethodEvent(QInputMethodEvent *event)
If you do reimplement this function, you should call the base class
implementation.
*/
-void QQuickItem::focusInEvent(QFocusEvent * /*event*/)
+void QQuickItem::focusInEvent(QFocusEvent *event)
{
+ Q_D(QQuickItem);
#if QT_CONFIG(accessibility)
if (QAccessible::isActive()) {
if (QObject *acc = QQuickAccessibleAttached::findAccessible(this)) {
@@ -4039,17 +4165,20 @@ void QQuickItem::focusInEvent(QFocusEvent * /*event*/)
}
}
#endif
+ d->setLastFocusChangeReason(event->reason());
}
/*!
This event handler can be reimplemented in a subclass to receive focus-out
- events for an item. The event information is provided by the \c event
+ events for an item. The event information is provided by the \a event
parameter.
\input item.qdocinc accepting-events
*/
-void QQuickItem::focusOutEvent(QFocusEvent * /*event*/)
+void QQuickItem::focusOutEvent(QFocusEvent *event)
{
+ Q_D(QQuickItem);
+ d->setLastFocusChangeReason(event->reason());
}
/*!
@@ -4274,7 +4403,11 @@ void QQuickItem::dropEvent(QDropEvent *event)
This method will only be called if filtersChildMouseEvents() is \c true.
Return \c true if the specified \a event should not be passed on to the
- specified child \a item, and \c false otherwise.
+ specified child \a item, and \c false otherwise. If you return \c true, you
+ should also \l {QEvent::accept()}{accept} or \l {QEvent::ignore()}{ignore}
+ the \a event, to signal if event propagation should stop or continue.
+ The \a event will, however, always be sent to all childMouseEventFilters
+ up the parent chain.
\note Despite the name, this function filters all QPointerEvent instances
during delivery to all children (typically mouse, touch, and tablet
@@ -4516,7 +4649,7 @@ void QQuickItem::ensurePolished()
}
#if QT_DEPRECATED_SINCE(6, 5)
-static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *itemForWarning, const QString &functionNameForWarning,
+static bool unwrapMapFromToFromItemArgs(QQmlV4FunctionPtr args, const QQuickItem *itemForWarning, const QString &functionNameForWarning,
QQuickItem **itemObj, qreal *x, qreal *y, qreal *w, qreal *h, bool *isRect)
{
QV4::ExecutionEngine *v4 = args->v4engine();
@@ -4613,7 +4746,7 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *
\input item.qdocinc mapping
If \a item is a \c null value, this maps the point or rect from the coordinate system of
- the root QML view.
+ the \l{Scene Coordinates}{scene}.
The versions accepting point and rect are since Qt 5.15.
*/
@@ -4622,7 +4755,7 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *
/*!
\internal
*/
-void QQuickItem::mapFromItem(QQmlV4Function *args) const
+void QQuickItem::mapFromItem(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4671,7 +4804,7 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
\input item.qdocinc mapping
If \a item is a \c null value, this maps the point or rect to the coordinate system of the
- root QML view.
+ \l{Scene Coordinates}{scene}.
The versions accepting point and rect are since Qt 5.15.
*/
@@ -4680,7 +4813,7 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
/*!
\internal
*/
-void QQuickItem::mapToItem(QQmlV4Function *args) const
+void QQuickItem::mapToItem(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4698,7 +4831,7 @@ void QQuickItem::mapToItem(QQmlV4Function *args) const
args->setReturnValue(rv.asReturnedValue());
}
-static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem *itemForWarning, const QString &functionNameForWarning, qreal *x, qreal *y)
+static bool unwrapMapFromToFromGlobalArgs(QQmlV4FunctionPtr args, const QQuickItem *itemForWarning, const QString &functionNameForWarning, qreal *x, qreal *y)
{
QV4::ExecutionEngine *v4 = args->v4engine();
if (args->length() != 1 && args->length() != 2) {
@@ -4756,7 +4889,7 @@ static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem
/*!
\internal
*/
-void QQuickItem::mapFromGlobal(QQmlV4Function *args) const
+void QQuickItem::mapFromGlobal(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4786,7 +4919,7 @@ void QQuickItem::mapFromGlobal(QQmlV4Function *args) const
/*!
\internal
*/
-void QQuickItem::mapToGlobal(QQmlV4Function *args) const
+void QQuickItem::mapToGlobal(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4860,7 +4993,6 @@ void QQuickItem::forceActiveFocus()
void QQuickItem::forceActiveFocus(Qt::FocusReason reason)
{
- Q_D(QQuickItem);
setFocus(true, reason);
QQuickItem *parent = parentItem();
QQuickItem *scope = nullptr;
@@ -4872,14 +5004,6 @@ void QQuickItem::forceActiveFocus(Qt::FocusReason reason)
}
parent = parent->parentItem();
}
- // In certain reparenting scenarios, d->focus might be true and the scope
- // might also have focus, so that setFocus() returns early without actually
- // acquiring active focus, because it thinks it already has it. In that
- // case, try to set the DeliveryAgent's active focus. (QTBUG-89736).
- if (scope && !d->activeFocus) {
- if (auto da = d->deliveryAgentPrivate())
- da->setFocusInScope(scope, this, Qt::OtherFocusReason);
- }
}
/*!
@@ -4982,12 +5106,17 @@ void QQuickItemPrivate::dumpItemTree(int indent) const
{
Q_Q(const QQuickItem);
- qDebug().nospace().noquote() << QString(indent * 4, QLatin1Char(' ')) <<
+ const auto indentStr = QString(indent * 4, QLatin1Char(' '));
+ qDebug().nospace().noquote() << indentStr <<
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
const_cast<QQuickItem *>(q);
#else
q;
#endif
+ if (extra.isAllocated()) {
+ for (const auto handler : extra->pointerHandlers)
+ qDebug().nospace().noquote() << indentStr << u" \u26ee " << handler;
+ }
for (const QQuickItem *ch : childItems) {
auto itemPriv = QQuickItemPrivate::get(ch);
itemPriv->dumpItemTree(indent + 1);
@@ -4996,10 +5125,16 @@ void QQuickItemPrivate::dumpItemTree(int indent) const
QQmlListProperty<QObject> QQuickItemPrivate::resources()
{
- return QQmlListProperty<QObject>(q_func(), nullptr, QQuickItemPrivate::resources_append,
- QQuickItemPrivate::resources_count,
- QQuickItemPrivate::resources_at,
- QQuickItemPrivate::resources_clear);
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QObject> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::resources_append;
+ result.count = QQuickItemPrivate::resources_count;
+ result.at = QQuickItemPrivate::resources_at;
+ result.clear = QQuickItemPrivate::resources_clear;
+ result.removeLast = QQuickItemPrivate::resources_removeLast;
+ return result;
}
/*!
@@ -5021,11 +5156,16 @@ QQmlListProperty<QObject> QQuickItemPrivate::resources()
*/
QQmlListProperty<QQuickItem> QQuickItemPrivate::children()
{
- return QQmlListProperty<QQuickItem>(q_func(), nullptr, QQuickItemPrivate::children_append,
- QQuickItemPrivate::children_count,
- QQuickItemPrivate::children_at,
- QQuickItemPrivate::children_clear);
-
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QQuickItem> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::children_append;
+ result.count = QQuickItemPrivate::children_count;
+ result.at = QQuickItemPrivate::children_at;
+ result.clear = QQuickItemPrivate::children_clear;
+ result.removeLast = QQuickItemPrivate::children_removeLast;
+ return result;
}
/*!
@@ -5319,7 +5459,8 @@ bool QQuickItemPrivate::transformChanged(QQuickItem *transformedItem)
if (subtreeTransformChangedEnabled) {
// Inform the children in paint order: by the time we visit leaf items,
// they can see any consequences in their parents
- for (auto child : paintOrderChildItems())
+ const auto children = paintOrderChildItems();
+ for (QQuickItem *child : children)
childWantsIt |= QQuickItemPrivate::get(child)->transformChanged(transformedItem);
}
@@ -5337,18 +5478,35 @@ bool QQuickItemPrivate::transformChanged(QQuickItem *transformedItem)
}
// If ItemObservesViewport, clipRect() calculates the intersection with the viewport;
// so each time the item moves in the viewport, its clipnode needs to be updated.
- if (thisWantsIt && q->clip())
+ if (thisWantsIt && q->clip() && !(dirtyAttributes & QQuickItemPrivate::Clip))
dirty(QQuickItemPrivate::Clip);
return ret;
}
+/*! \internal
+ Returns the new position (proposed values for the x and y properties)
+ to which this item should be moved to compensate for the given change
+ in scale from \a startScale to \a activeScale and in rotation from
+ \a startRotation to \a activeRotation. \a centroidParentPos is the
+ point that we wish to hold in place (and then apply \a activeTranslation to),
+ in this item's parent's coordinate system. \a startPos is this item's
+ position in its parent's coordinate system when the gesture began.
+ \a activeTranslation is the amount of translation that should be added to
+ the return value, i.e. the displacement by which the centroid is expected
+ to move.
+
+ If \a activeTranslation is \c (0, 0) the centroid is to be held in place.
+ If \a activeScale is \c 1, it means scale is intended to be held constant,
+ the same as \a startScale. If \a activeRotation is \c 0, it means rotation
+ is intended to be held constant, the same as \a startRotation.
+*/
QPointF QQuickItemPrivate::adjustedPosForTransform(const QPointF &centroidParentPos,
const QPointF &startPos,
- const QVector2D &activeTranslation, //[0,0] means no additional translation from startPos
+ const QVector2D &activeTranslation,
qreal startScale,
- qreal activeScale, // 1.0 means no additional scale from startScale
+ qreal activeScale,
qreal startRotation,
- qreal activeRotation) // 0.0 means no additional rotation from startRotation
+ qreal activeRotation)
{
Q_Q(QQuickItem);
QVector3D xformOrigin(q->transformOriginPoint());
@@ -5474,6 +5632,41 @@ bool QQuickItemPrivate::filterKeyEvent(QKeyEvent *e, bool post)
return e->isAccepted();
}
+void QQuickItemPrivate::deliverPointerEvent(QEvent *event)
+{
+ Q_Q(QQuickItem);
+ const auto eventType = event->type();
+ const bool focusAccepted = setFocusIfNeeded(eventType);
+
+ switch (eventType) {
+ case QEvent::MouseButtonPress:
+ q->mousePressEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::MouseButtonRelease:
+ q->mouseReleaseEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::MouseButtonDblClick:
+ q->mouseDoubleClickEvent(static_cast<QMouseEvent *>(event));
+ break;
+#if QT_CONFIG(wheelevent)
+ case QEvent::Wheel:
+ q->wheelEvent(static_cast<QWheelEvent*>(event));
+ break;
+#endif
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ case QEvent::TouchCancel:
+ q->touchEvent(static_cast<QTouchEvent *>(event));
+ break;
+ default:
+ break;
+ }
+
+ if (focusAccepted)
+ event->accept();
+}
+
void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e)
{
Q_Q(QQuickItem);
@@ -5542,7 +5735,7 @@ void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e)
void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
{
if (extra.isAllocated() && extra->keyHandler)
- extra->keyHandler->shortcutOverride(event);
+ extra->keyHandler->shortcutOverrideEvent(event);
else
event->ignore();
}
@@ -5945,8 +6138,8 @@ void QQuickItem::setZ(qreal v)
d->dirty(QQuickItemPrivate::ZValue);
if (d->parentItem) {
- QQuickItemPrivate::get(d->parentItem)->dirty(QQuickItemPrivate::ChildrenStackingChanged);
QQuickItemPrivate::get(d->parentItem)->markSortedChildrenDirty(this);
+ QQuickItemPrivate::get(d->parentItem)->dirty(QQuickItemPrivate::ChildrenStackingChanged);
}
emit zChanged();
@@ -6310,7 +6503,11 @@ void QQuickItem::setOpacity(qreal newOpacity)
\note This property's value is only affected by changes to this property or
the parent's \c visible property. It does not change, for example, if this
- item moves off-screen, or if the \l opacity changes to 0.
+ item moves off-screen, or if the \l opacity changes to 0. However, for
+ historical reasons, this property is true after the item's construction, even
+ if the item hasn't been added to a scene yet. Changing or reading this
+ property of an item that has not been added to a scene might not produce
+ the expected results.
\note The notification signal for this property gets emitted during destruction
of the visual parent. C++ signal handlers cannot assume that items in the
@@ -6380,9 +6577,9 @@ void QQuickItem::setVisible(bool v)
Thus, a disabled item can continue to receive hover events, even when this
property is \c false. This makes it possible to show informational feedback
(such as \l ToolTip) even when an interactive item is disabled.
- The same is also true for any \l {HoverHandlers}{QQuickHoverHandler}
+ The same is also true for any \l {HoverHandler}{HoverHandlers}
added as children of the item. A HoverHandler can, however, be
- \l{disabled}{QQuickHoverHandler::enabled} explicitly, or for example
+ \l {PointerHandler::enabled}{disabled} explicitly, or for example
be bound to the \c enabled state of the item.
\sa visible
@@ -6561,9 +6758,6 @@ QString QQuickItemPrivate::dirtyToString() const
void QQuickItemPrivate::dirty(DirtyType type)
{
Q_Q(QQuickItem);
- if (type & (TransformOrigin | Transform | BasicTransform | Position | Size))
- transformChanged(q);
-
if (!(dirtyAttributes & type) || (window && !prevDirtyItem)) {
dirtyAttributes |= type;
if (window && componentComplete) {
@@ -6571,6 +6765,8 @@ void QQuickItemPrivate::dirty(DirtyType type)
QQuickWindowPrivate::get(window)->dirtyItem(q);
}
}
+ if (type & (TransformOrigin | Transform | BasicTransform | Position | Size | Clip))
+ transformChanged(q);
}
void QQuickItemPrivate::addToDirtyList()
@@ -6666,8 +6862,18 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
switch (change) {
case QQuickItem::ItemChildAddedChange: {
q->itemChange(change, data);
- if (!subtreeTransformChangedEnabled)
- subtreeTransformChangedEnabled = true;
+ // The newly added child or any of its descendants may have
+ // ItemObservesViewport set, in which case we need to both
+ // inform the item that the transform has changed, and re-apply
+ // subtreeTransformChangedEnabled to both this item and its
+ // ancestors.
+ if (QQuickItemPrivate::get(data.item)->transformChanged(q)) {
+ if (!subtreeTransformChangedEnabled) {
+ qCDebug(lcVP) << "turned on transformChanged notification for subtree of" << q;
+ subtreeTransformChangedEnabled = true;
+ }
+ enableSubtreeChangeNotificationsForParentHierachy();
+ }
notifyChangeListeners(QQuickItemPrivate::Children, &QQuickItemChangeListener::itemChildAdded, q, data.item);
break;
}
@@ -6779,6 +6985,7 @@ void QQuickItem::setSmooth(bool smooth)
This property holds whether the item wants to be in the tab focus
chain. By default, this is set to \c false.
*/
+// TODO FOCUS: Deprecate
bool QQuickItem::activeFocusOnTab() const
{
Q_D(const QQuickItem);
@@ -6895,15 +7102,23 @@ void QQuickItem::setFlag(Flag flag, bool enabled)
else
setFlags((Flags)(d->flags & ~(quint32)flag));
- if (enabled && flag == ItemObservesViewport) {
- QQuickItem *par = parentItem();
- while (par) {
- auto parPriv = QQuickItemPrivate::get(par);
- if (!parPriv->subtreeTransformChangedEnabled)
- qCDebug(lcVP) << "turned on transformChanged notification for subtree of" << par;
- parPriv->subtreeTransformChangedEnabled = true;
- par = par->parentItem();
- }
+ // We don't return early if the flag did not change. That's useful in case
+ // we need to intentionally trigger this parent-chain traversal again.
+ if (enabled && flag == ItemObservesViewport)
+ d->enableSubtreeChangeNotificationsForParentHierachy();
+}
+
+void QQuickItemPrivate::enableSubtreeChangeNotificationsForParentHierachy()
+{
+ Q_Q(QQuickItem);
+
+ QQuickItem *par = q->parentItem();
+ while (par) {
+ auto parPriv = QQuickItemPrivate::get(par);
+ if (!parPriv->subtreeTransformChangedEnabled)
+ qCDebug(lcVP) << "turned on transformChanged notification for subtree of" << par;
+ parPriv->subtreeTransformChangedEnabled = true;
+ par = par->parentItem();
}
}
@@ -6994,15 +7209,17 @@ void QQuickItem::setX(qreal v)
if (qt_is_nan(v))
return;
- const qreal oldx = d->x;
+ const qreal oldx = d->x.valueBypassingBindings();
if (oldx == v)
return;
- d->x = v;
+ d->x.setValueBypassingBindings(v);
d->dirty(QQuickItemPrivate::Position);
- const qreal y = d->y, w = d->width, h = d->height;
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
geometryChange(QRectF(v, y, w, h), QRectF(oldx, y, w, h));
}
@@ -7013,17 +7230,19 @@ void QQuickItem::setY(qreal v)
if (qt_is_nan(v))
return;
- const qreal oldy = d->y;
+ const qreal oldy = d->y.valueBypassingBindings();
if (oldy == v)
return;
- d->y = v;
+ d->y.setValueBypassingBindings(v);
d->dirty(QQuickItemPrivate::Position);
// we use v instead of d->y, as that avoid a method call
// and we have v anyway in scope
- const qreal x = d->x, w = d->width, h = d->height;
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
geometryChange(QRectF(x, v, w, h), QRectF(x, oldy, w, h));
}
@@ -7033,11 +7252,12 @@ void QQuickItem::setY(qreal v)
void QQuickItem::setPosition(const QPointF &pos)
{
Q_D(QQuickItem);
- if (QPointF(d->x, d->y) == pos)
- return;
- const qreal oldx = d->x;
- const qreal oldy = d->y;
+ const qreal oldx = d->x.valueBypassingBindings();
+ const qreal oldy = d->y.valueBypassingBindings();
+
+ if (QPointF(oldx, oldy) == pos)
+ return;
/* This preserves the bindings, because that was what the code used to do
The effect of this is that you can have
@@ -7057,7 +7277,8 @@ void QQuickItem::setPosition(const QPointF &pos)
d->dirty(QQuickItemPrivate::Position);
- const qreal w = d->width, h = d->height;
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
geometryChange(QRectF(pos.x(), pos.y(), w, h), QRectF(oldx, oldy, w, h));
}
@@ -7093,15 +7314,17 @@ void QQuickItem::setWidth(qreal w)
return;
d->widthValidFlag = true;
- const qreal oldWidth = d->width;
+ const qreal oldWidth = d->width.valueBypassingBindings();
if (oldWidth == w)
return;
- d->width = w;
+ d->width.setValueBypassingBindings(w);
d->dirty(QQuickItemPrivate::Size);
- const qreal x = d->x, y = d->y, h = d->height;
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
geometryChange(QRectF(x, y, w, h), QRectF(x, y, oldWidth, h));
}
@@ -7299,15 +7522,17 @@ void QQuickItem::setHeight(qreal h)
return;
d->heightValidFlag = true;
- const qreal oldHeight = d->height;
+ const qreal oldHeight = d->height.valueBypassingBindings();
if (oldHeight == h)
return;
- d->height = h;
+ d->height.setValueBypassingBindings(h);
d->dirty(QQuickItemPrivate::Size);
- const qreal x = d->x, y = d->y, w = d->width;
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
geometryChange(QRectF(x, y, w, h), QRectF(x, y, w, oldHeight));
}
@@ -7411,11 +7636,11 @@ void QQuickItem::setImplicitSize(qreal w, qreal h)
const qreal oldHeight = height;
if (!wDone) {
width = w;
- d->width = w;
+ d->width.setValueBypassingBindings(w);
}
if (!hDone) {
height = h;
- d->height = h;
+ d->height.setValueBypassingBindings(h);
}
d->dirty(QQuickItemPrivate::Size);
@@ -7471,17 +7696,19 @@ void QQuickItem::setSize(const QSizeF &size)
d->heightValidFlag = true;
d->widthValidFlag = true;
- if (d->width == size.width() && d->height == size.height())
+ const qreal oldHeight = d->height.valueBypassingBindings();
+ const qreal oldWidth = d->width.valueBypassingBindings();
+
+ if (oldWidth == size.width() && oldHeight == size.height())
return;
- const qreal oldHeight = d->height;
- const qreal oldWidth = d->width;
d->height.setValueBypassingBindings(size.height());
d->width.setValueBypassingBindings(size.width());
d->dirty(QQuickItemPrivate::Size);
- const qreal x = d->x, y = d->y;
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
geometryChange(QRectF(x, y, size.width(), size.height()), QRectF(x, y, oldWidth, oldHeight));
}
@@ -7662,15 +7889,16 @@ void QQuickItem::setFocus(bool focus)
void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
{
Q_D(QQuickItem);
- if (d->focus == focus)
+ // Need to find our nearest focus scope
+ QQuickItem *scope = parentItem();
+ while (scope && !scope->isFocusScope() && scope->parentItem())
+ scope = scope->parentItem();
+
+ if (d->focus == focus && (!focus || !scope || QQuickItemPrivate::get(scope)->subFocusItem == this))
return;
bool notifyListeners = false;
if (d->window || d->parentItem) {
- // Need to find our nearest focus scope
- QQuickItem *scope = parentItem();
- while (scope && !scope->isFocusScope() && scope->parentItem())
- scope = scope->parentItem();
if (d->window) {
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
@@ -7744,6 +7972,52 @@ QQuickItem *QQuickItem::scopedFocusItem() const
}
/*!
+ \qmlproperty enumeration QtQuick::Item::focusPolicy
+ \since 6.7
+
+ This property determines the way the item accepts focus.
+
+ \value Qt.TabFocus The item accepts focus by tabbing.
+ \value Qt.ClickFocus The item accepts focus by clicking.
+ \value Qt.StrongFocus The item accepts focus by both tabbing and clicking.
+ \value Qt.WheelFocus The item accepts focus by tabbing, clicking, and using the mouse wheel.
+ \value Qt.NoFocus The item does not accept focus.
+
+ \note This property was a member of the \l[QML]{Control} QML type in Qt 6.6 and earlier.
+*/
+/*!
+ \property QQuickItem::focusPolicy
+ \since 6.7
+
+ This property determines the way the item accepts focus.
+
+*/
+Qt::FocusPolicy QQuickItem::focusPolicy() const
+{
+ Q_D(const QQuickItem);
+ uint policy = d->focusPolicy;
+ if (activeFocusOnTab())
+ policy |= Qt::TabFocus;
+ return static_cast<Qt::FocusPolicy>(policy);
+}
+
+/*!
+ Sets the focus policy of this item to \a policy.
+
+ \sa focusPolicy()
+*/
+void QQuickItem::setFocusPolicy(Qt::FocusPolicy policy)
+{
+ Q_D(QQuickItem);
+ if (d->focusPolicy == policy)
+ return;
+
+ d->focusPolicy = policy;
+ setActiveFocusOnTab(policy & Qt::TabFocus);
+ emit focusPolicyChanged(policy);
+}
+
+/*!
Returns \c true if this item is an ancestor of \a child (i.e., if this item
is \a child's parent, or one of \a child's parent's ancestors).
@@ -8343,7 +8617,7 @@ void QQuickItem::setKeepTouchGrab(bool keep)
Returns \c true if this item contains \a point, which is in local coordinates;
returns \c false otherwise. This is the same check that is used for
hit-testing a QEventPoint during event delivery, and is affected by
- containmentMask() if it is set.
+ \l containmentMask if it is set.
*/
/*!
Returns \c true if this item contains \a point, which is in local coordinates;
@@ -8351,7 +8625,7 @@ void QQuickItem::setKeepTouchGrab(bool keep)
This function can be overridden in order to handle point collisions in items
with custom shapes. The default implementation checks whether the point is inside
- containmentMask() if it is set, or inside the bounding box otherwise.
+ \l containmentMask() if it is set, or inside the bounding box otherwise.
\note This method is used for hit-testing each QEventPoint during event
delivery, so the implementation should be kept as lightweight as possible.
@@ -8381,10 +8655,10 @@ bool QQuickItem::contains(const QPointF &point) const
\qmlproperty QObject* QtQuick::Item::containmentMask
\since 5.11
This property holds an optional mask for the Item to be used in the
- QtQuick::Item::contains() method. Its main use is currently to determine
+ \l contains() method. Its main use is currently to determine
whether a \l {QPointerEvent}{pointer event} has landed into the item or not.
- By default the \l contains method will return true for any point
+ By default the \c contains() method will return true for any point
within the Item's bounding box. \c containmentMask allows for
more fine-grained control. For example, if a custom C++
QQuickItem subclass with a specialized contains() method
@@ -8495,7 +8769,7 @@ void QQuickItem::setContainmentMask(QObject *mask)
\input item.qdocinc mapping
- If \a item is 0, this maps \a point to the coordinate system of the
+ If \a item is \nullptr, this maps \a point to the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8503,8 +8777,13 @@ void QQuickItem::setContainmentMask(QObject *mask)
QPointF QQuickItem::mapToItem(const QQuickItem *item, const QPointF &point) const
{
QPointF p = mapToScene(point);
- if (item)
+ if (item) {
+ const QQuickWindow *itemWindow = item->window();
+ if (itemWindow != nullptr && itemWindow != window())
+ p = itemWindow->mapFromGlobal(window()->mapToGlobal(p));
+
p = item->mapFromScene(p);
+ }
return p;
}
@@ -8553,7 +8832,7 @@ QPointF QQuickItem::mapToGlobal(const QPointF &point) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a rect to the coordinate system of the
+ If \a item is \nullptr, this maps \a rect to the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8589,14 +8868,20 @@ QRectF QQuickItem::mapRectToScene(const QRectF &rect) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a point from the coordinate system of the
+ If \a item is \nullptr, this maps \a point from the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QPointF QQuickItem::mapFromItem(const QQuickItem *item, const QPointF &point) const
{
- QPointF p = item?item->mapToScene(point):point;
+ QPointF p = point;
+ if (item) {
+ p = item->mapToScene(point);
+
+ if (item->window() != window())
+ p = window()->mapFromGlobal(item->window()->mapToGlobal(p));
+ }
return mapFromScene(p);
}
@@ -8656,7 +8941,7 @@ QPointF QQuickItem::mapFromGlobal(const QPointF &point) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a rect from the coordinate system of the
+ If \a item is \nullptr, this maps \a rect from the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8764,8 +9049,14 @@ bool QQuickItem::event(QEvent *ev)
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::TouchCancel:
- touchEvent(static_cast<QTouchEvent*>(ev));
- break;
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+#if QT_CONFIG(wheelevent)
+ case QEvent::Wheel:
+#endif
+ d->deliverPointerEvent(ev);
+ break;
case QEvent::StyleAnimationUpdate:
if (isVisible()) {
ev->accept();
@@ -8797,20 +9088,6 @@ bool QQuickItem::event(QEvent *ev)
case QEvent::MouseMove:
mouseMoveEvent(static_cast<QMouseEvent*>(ev));
break;
- case QEvent::MouseButtonPress:
- mousePressEvent(static_cast<QMouseEvent*>(ev));
- break;
- case QEvent::MouseButtonRelease:
- mouseReleaseEvent(static_cast<QMouseEvent*>(ev));
- break;
- case QEvent::MouseButtonDblClick:
- mouseDoubleClickEvent(static_cast<QMouseEvent*>(ev));
- break;
-#if QT_CONFIG(wheelevent)
- case QEvent::Wheel:
- wheelEvent(static_cast<QWheelEvent*>(ev));
- break;
-#endif
#if QT_CONFIG(quick_draganddrop)
case QEvent::DragEnter:
dragEnterEvent(static_cast<QDragEnterEvent*>(ev));
@@ -8870,6 +9147,16 @@ QDebug operator<<(QDebug debug,
const QRectF rect(item->position(), QSizeF(item->width(), item->height()));
debug << item->metaObject()->className() << '(' << static_cast<void *>(item);
+
+ // Deferred properties will cause recursion when calling nameForObject
+ // before the component is completed, so guard against this situation.
+ if (item->isComponentComplete()) {
+ if (QQmlContext *context = qmlContext(item)) {
+ const auto objectId = context->nameForObject(item);
+ if (!objectId.isEmpty())
+ debug << ", id=" << objectId;
+ }
+ }
if (!item->objectName().isEmpty())
debug << ", name=" << item->objectName();
debug << ", parent=" << static_cast<void *>(item->parentItem())
@@ -9030,12 +9317,12 @@ void QQuickItemPrivate::localizedTouchEvent(const QTouchEvent *event, bool isFil
bool hasAnotherGrabber = pointGrabber && pointGrabber != q;
// if there's no exclusive grabber, look for passive grabbers during filtering
if (isFiltering && !pointGrabber) {
- auto pg = event->passiveGrabbers(p);
+ const auto pg = event->passiveGrabbers(p);
if (!pg.isEmpty()) {
// It seems unlikely to have multiple passive grabbers of one eventpoint with different grandparents.
// So hopefully if we start from one passive grabber and go up the parent chain from there,
// we will find any filtering parent items that exist.
- auto handler = qmlobject_cast<QQuickPointerHandler *>(pg.first());
+ auto handler = qmlobject_cast<QQuickPointerHandler *>(pg.constFirst());
if (handler)
pointGrabber = handler->parentItem();
}
@@ -9368,14 +9655,12 @@ void QQuickItemLayer::setMipmap(bool mipmap)
Modifying this property makes most sense when the \a layer.effect is also
specified.
- \list
- \li ShaderEffectSource.RGBA8
- \li ShaderEffectSource.RGBA16F
- \li ShaderEffectSource.RGBA32F
- \li ShaderEffectSource.Alpha - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \li ShaderEffectSource.RGB - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \li ShaderEffectSource.RGBA - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \endlist
+ \value ShaderEffectSource.RGBA8
+ \value ShaderEffectSource.RGBA16F
+ \value ShaderEffectSource.RGBA32F
+ \value ShaderEffectSource.Alpha Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGB Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGBA Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
\sa {Item Layers}
*/
@@ -9496,12 +9781,10 @@ void QQuickItemLayer::setSize(const QSize &size)
Modifying this property makes most sense when the \a layer.effect is
specified.
- \list
- \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
- \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
- \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
- \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
- \endlist
+ \value ShaderEffectSource.ClampToEdge GL_CLAMP_TO_EDGE both horizontally and vertically
+ \value ShaderEffectSource.RepeatHorizontally GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
+ \value ShaderEffectSource.RepeatVertically GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
+ \value ShaderEffectSource.Repeat GL_REPEAT both horizontally and vertically
\note Some OpenGL ES 2 implementations do not support the GL_REPEAT
wrap mode with non-power-of-two textures.
@@ -9531,11 +9814,9 @@ void QQuickItemLayer::setWrapMode(QQuickShaderEffectSource::WrapMode mode)
such as those specified by ShaderEffect. If no effect is specified for the layered
item, mirroring has no effect on the UI representation of the item.
- \list
- \li ShaderEffectSource.NoMirroring - No mirroring
- \li ShaderEffectSource.MirrorHorizontally - The generated texture is flipped along X-axis.
- \li ShaderEffectSource.MirrorVertically - The generated texture is flipped along Y-axis.
- \endlist
+ \value ShaderEffectSource.NoMirroring No mirroring
+ \value ShaderEffectSource.MirrorHorizontally The generated texture is flipped along X-axis.
+ \value ShaderEffectSource.MirrorVertically The generated texture is flipped along Y-axis.
*/
void QQuickItemLayer::setTextureMirroring(QQuickShaderEffectSource::TextureMirroring mirroring)
@@ -9721,13 +10002,20 @@ QQuickItemPrivate::ExtraData::ExtraData()
#if QT_CONFIG(accessibility)
-QAccessible::Role QQuickItemPrivate::accessibleRole() const
+QAccessible::Role QQuickItemPrivate::effectiveAccessibleRole() const
{
Q_Q(const QQuickItem);
- QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false));
- if (accessibleAttached)
- return accessibleAttached->role();
+ auto *attached = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false);
+ auto role = QAccessible::NoRole;
+ if (auto *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(attached))
+ role = accessibleAttached->role();
+ if (role == QAccessible::NoRole)
+ role = accessibleRole();
+ return role;
+}
+QAccessible::Role QQuickItemPrivate::accessibleRole() const
+{
return QAccessible::NoRole;
}
#endif
@@ -9758,7 +10046,7 @@ void QV4::Heap::QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkS
QObjectWrapper::markObjects(that, markStack);
}
-quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
+quint64 QQuickItemPrivate::_q_createJSWrapper(QQmlV4ExecutionEnginePtr engine)
{
return (engine->memoryManager->allocate<QQuickItemWrapper>(q_func()))->asReturnedValue();
}
@@ -9802,6 +10090,9 @@ QPointF QQuickItem::mapToGlobal(qreal x, qreal y) const
QPointF QQuickItem::mapFromGlobal(qreal x, qreal y) const
{ return mapFromGlobal(QPointF(x, y)); }
+//! \internal
+QQuickItemChangeListener::~QQuickItemChangeListener() = default;
+
QT_END_NAMESPACE
#include <moc_qquickitem.cpp>
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 6a1f8db8c8..6b54b1af24 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -45,7 +45,6 @@ private:
class QCursor;
class QQuickItemLayer;
-class QQmlV4Function;
class QQuickState;
class QQuickAnchorLine;
class QQuickTransition;
@@ -102,6 +101,8 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus
Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged FINAL)
Q_PROPERTY(bool activeFocusOnTab READ activeFocusOnTab WRITE setActiveFocusOnTab NOTIFY activeFocusOnTabChanged FINAL REVISION(2, 1))
+ Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged REVISION(6, 7))
+
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged)
Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin NOTIFY transformOriginChanged)
@@ -120,7 +121,7 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus
Q_CLASSINFO("DefaultProperty", "data")
Q_CLASSINFO("ParentProperty", "parent")
- Q_CLASSINFO("qt_QmlJSWrapperFactoryMethod", "_q_createJSWrapper(QV4::ExecutionEngine*)")
+ Q_CLASSINFO("qt_QmlJSWrapperFactoryMethod", "_q_createJSWrapper(QQmlV4ExecutionEnginePtr)")
QML_NAMED_ELEMENT(Item)
QML_ADDED_IN_VERSION(2, 0)
@@ -270,6 +271,9 @@ public:
bool isFocusScope() const;
QQuickItem *scopedFocusItem() const;
+ Qt::FocusPolicy focusPolicy() const;
+ void setFocusPolicy(Qt::FocusPolicy policy);
+
bool isAncestorOf(const QQuickItem *child) const;
Qt::MouseButtons acceptedMouseButtons() const;
@@ -318,7 +322,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 5)
QT_DEPRECATED_VERSION_X_6_5("Use typed overload or mapRectFromItem")
- void mapFromItem(QQmlV4Function*) const;
+ void mapFromItem(QQmlV4FunctionPtr) const;
#endif
Q_INVOKABLE QPointF mapFromItem(const QQuickItem *item, const QPointF &point) const;
// overloads mainly exist for QML
@@ -328,7 +332,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 5)
QT_DEPRECATED_VERSION_X_6_5("Use typed overload or mapRectToItem")
- void mapToItem(QQmlV4Function*) const;
+ void mapToItem(QQmlV4FunctionPtr) const;
#endif
Q_INVOKABLE QPointF mapToItem(const QQuickItem *item, const QPointF &point) const;
// overloads mainly exist for QML
@@ -338,7 +342,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 5)
QT_DEPRECATED_VERSION_X_6_5("Use the typed overload")
- Q_REVISION(2, 7) void mapFromGlobal(QQmlV4Function*) const;
+ Q_REVISION(2, 7) void mapFromGlobal(QQmlV4FunctionPtr) const;
#endif
Q_REVISION(2, 7) Q_INVOKABLE QPointF mapFromGlobal(qreal x, qreal y) const;
// overload mainly exists for QML
@@ -346,7 +350,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 5)
QT_DEPRECATED_VERSION_X_6_5("Use the typed overload")
- Q_REVISION(2, 7) void mapToGlobal(QQmlV4Function*) const;
+ Q_REVISION(2, 7) void mapToGlobal(QQmlV4FunctionPtr) const;
#endif
Q_REVISION(2, 7) Q_INVOKABLE QPointF mapToGlobal(qreal x, qreal y) const;
// overload only exist for QML
@@ -383,6 +387,7 @@ Q_SIGNALS:
void stateChanged(const QString &);
void focusChanged(bool);
void activeFocusChanged(bool);
+ Q_REVISION(6, 7) void focusPolicyChanged(Qt::FocusPolicy);
Q_REVISION(2, 1) void activeFocusOnTabChanged(bool);
void parentChanged(QQuickItem *);
void transformOriginChanged(TransformOrigin);
@@ -465,7 +470,7 @@ protected:
private:
Q_PRIVATE_SLOT(d_func(), void _q_resourceObjectDeleted(QObject *))
- Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QV4::ExecutionEngine *))
+ Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QQmlV4ExecutionEnginePtr))
friend class QQuickWindowPrivate;
friend class QQuickDeliveryAgentPrivate;
@@ -473,6 +478,10 @@ private:
friend class QAccessibleQuickItem;
friend class QQuickAccessibleAttached;
friend class QQuickAnchorChanges;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_QUICK_EXPORT QDebug operator<<(QDebug debug, QQuickItem *item);
+#endif
+
Q_DISABLE_COPY(QQuickItem)
Q_DECLARE_PRIVATE(QQuickItem)
};
@@ -504,7 +513,4 @@ QDebug Q_QUICK_EXPORT operator<<(QDebug debug,
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickItem)
-QML_DECLARE_TYPE(QQuickTransform)
-
#endif // QQUICKITEM_H
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 8fe7686a62..bb238904ca 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -42,6 +42,9 @@
#include <QtCore/qlist.h>
#include <QtCore/qdebug.h>
#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qpointer.h>
+
+#include <QtGui/private/qlayoutpolicy_p.h>
QT_BEGIN_NAMESPACE
@@ -100,21 +103,21 @@ public:
#if QT_CONFIG(quick_shadereffect)
-class Q_QUICK_PRIVATE_EXPORT QQuickItemLayer : public QObject, public QQuickItemChangeListener
+class Q_QUICK_EXPORT QQuickItemLayer : public QObject, public QQuickItemChangeListener
{
Q_OBJECT
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
- Q_PROPERTY(QSize textureSize READ size WRITE setSize NOTIFY sizeChanged)
- Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged)
- Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged)
- Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged)
- Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged REVISION(6, 5))
- Q_PROPERTY(QQuickShaderEffectSource::WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
- Q_PROPERTY(QQuickShaderEffectSource::Format format READ format WRITE setFormat NOTIFY formatChanged)
- Q_PROPERTY(QByteArray samplerName READ name WRITE setName NOTIFY nameChanged)
- Q_PROPERTY(QQmlComponent *effect READ effect WRITE setEffect NOTIFY effectChanged)
- Q_PROPERTY(QQuickShaderEffectSource::TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged)
- Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged FINAL)
+ Q_PROPERTY(QSize textureSize READ size WRITE setSize NOTIFY sizeChanged FINAL)
+ Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged FINAL)
+ Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged FINAL)
+ Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged FINAL)
+ Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged REVISION(6, 5) FINAL)
+ Q_PROPERTY(QQuickShaderEffectSource::WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged FINAL)
+ Q_PROPERTY(QQuickShaderEffectSource::Format format READ format WRITE setFormat NOTIFY formatChanged FINAL)
+ Q_PROPERTY(QByteArray samplerName READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(QQmlComponent *effect READ effect WRITE setEffect NOTIFY effectChanged FINAL)
+ Q_PROPERTY(QQuickShaderEffectSource::TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged FINAL)
+ Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -217,7 +220,7 @@ private:
#endif
-class Q_QUICK_PRIVATE_EXPORT QQuickItemPrivate
+class Q_QUICK_EXPORT QQuickItemPrivate
: public QObjectPrivate
, public QQuickPaletteProviderPrivateBase<QQuickItem, QQuickItemPrivate>
{
@@ -265,21 +268,23 @@ public:
static qsizetype data_count(QQmlListProperty<QObject> *);
static QObject *data_at(QQmlListProperty<QObject> *, qsizetype);
static void data_clear(QQmlListProperty<QObject> *);
+ static void data_removeLast(QQmlListProperty<QObject> *);
// resources property
static QObject *resources_at(QQmlListProperty<QObject> *, qsizetype);
static void resources_append(QQmlListProperty<QObject> *, QObject *);
static qsizetype resources_count(QQmlListProperty<QObject> *);
static void resources_clear(QQmlListProperty<QObject> *);
+ static void resources_removeLast(QQmlListProperty<QObject> *);
// children property
static void children_append(QQmlListProperty<QQuickItem> *, QQuickItem *);
static qsizetype children_count(QQmlListProperty<QQuickItem> *);
static QQuickItem *children_at(QQmlListProperty<QQuickItem> *, qsizetype);
static void children_clear(QQmlListProperty<QQuickItem> *);
+ static void children_removeLast(QQmlListProperty<QQuickItem> *);
// visibleChildren property
- static void visibleChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *o);
static qsizetype visibleChildren_count(QQmlListProperty<QQuickItem> *prop);
static QQuickItem *visibleChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index);
@@ -290,7 +295,7 @@ public:
static void transform_clear(QQmlListProperty<QQuickTransform> *list);
void _q_resourceObjectDeleted(QObject *);
- quint64 _q_createJSWrapper(QV4::ExecutionEngine *engine);
+ quint64 _q_createJSWrapper(QQmlV4ExecutionEnginePtr engine);
enum ChangeType {
Geometry = 0x01,
@@ -338,7 +343,7 @@ public:
#endif // QT_NO_DEBUG_STREAM
};
- // call QQuickItemChangeListener PMF
+ // call QQuickItemChangeListener
template <typename Fn, typename ...Args>
void notifyChangeListeners(QQuickItemPrivate::ChangeTypes changeTypes, Fn &&function, Args &&...args)
{
@@ -347,20 +352,12 @@ public:
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & changeTypes)
- (change.listener->*function)(args...);
- }
- }
- // call functor
- template <typename Fn>
- void notifyChangeListeners(QQuickItemPrivate::ChangeTypes changeTypes, Fn &&function) {
- if (changeListeners.isEmpty())
- return;
-
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & changeTypes)
- function(change);
+ if (change.types & changeTypes) {
+ if constexpr (std::is_member_function_pointer_v<Fn>)
+ (change.listener->*function)(args...);
+ else
+ function(change, args...);
+ }
}
}
@@ -488,6 +485,7 @@ public:
// focus chain and prevents tabbing outside.
quint32 isTabFence:1;
quint32 replayingPressEvent:1;
+ // Bit 40
quint32 touchEnabled:1;
quint32 hasCursorHandler:1;
// set true when this item does not expect events via a subscene delivery agent; false otherwise
@@ -496,6 +494,9 @@ public:
// (e.g. when parent has ItemIsViewport and child has ItemObservesViewport)
quint32 subtreeTransformChangedEnabled:1;
quint32 inDestructor:1; // has entered ~QQuickItem
+ quint32 focusReason:4;
+ quint32 focusPolicy:4;
+ // Bit 53
enum DirtyType {
TransformOrigin = 0x00000001,
@@ -561,16 +562,20 @@ public:
QPointer<QQuickItem> subFocusItem;
void updateSubFocusItem(QQuickItem *scope, bool focus);
+ bool setFocusIfNeeded(QEvent::Type);
+ Qt::FocusReason lastFocusChangeReason() const;
+ void setLastFocusChangeReason(Qt::FocusReason reason);
+
QTransform windowToItemTransform() const;
QTransform itemToWindowTransform() const;
- void itemToParentTransform(QTransform &) const;
+ void itemToParentTransform(QTransform *) const;
QTransform globalToWindowTransform() const;
QTransform windowToGlobalTransform() const;
static bool focusNextPrev(QQuickItem *item, bool forward);
static QQuickItem *nextTabChildItem(const QQuickItem *item, int start);
static QQuickItem *prevTabChildItem(const QQuickItem *item, int start);
- static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward);
+ static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap = true);
static bool canAcceptTabFocus(QQuickItem *item);
@@ -609,7 +614,10 @@ public:
virtual void implicitHeightChanged();
#if QT_CONFIG(accessibility)
+ QAccessible::Role effectiveAccessibleRole() const;
+private:
virtual QAccessible::Role accessibleRole() const;
+public:
#endif
void setImplicitAntialiasing(bool antialiasing);
@@ -645,6 +653,8 @@ public:
#endif
void deliverShortcutOverrideEvent(QKeyEvent *);
+ void deliverPointerEvent(QEvent *);
+
bool anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const;
virtual bool handlePointerEvent(QPointerEvent *, bool avoidGrabbers = false);
@@ -687,6 +697,8 @@ public:
void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &);
+ void enableSubtreeChangeNotificationsForParentHierachy();
+
virtual void mirrorChange() {}
void setHasCursorInChild(bool hasCursor);
@@ -698,6 +710,10 @@ public:
virtual void updatePolish() { }
virtual void dumpItemTree(int indent) const;
+
+ QLayoutPolicy sizePolicy() const;
+ void setSizePolicy(const QLayoutPolicy::Policy &horizontalPolicy, const QLayoutPolicy::Policy &verticalPolicy);
+ QLayoutPolicy szPolicy;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItemPrivate::ExtraDataTags)
@@ -720,7 +736,7 @@ public:
virtual void inputMethodEvent(QInputMethodEvent *event, bool post);
virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
#endif
- virtual void shortcutOverride(QKeyEvent *event);
+ virtual void shortcutOverrideEvent(QKeyEvent *event);
virtual void componentComplete();
bool m_processPost;
@@ -750,18 +766,18 @@ public:
bool backtabSet : 1;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickKeyNavigationAttached : public QObject, public QQuickItemKeyFilter
+class Q_QUICK_EXPORT QQuickKeyNavigationAttached : public QObject, public QQuickItemKeyFilter
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickKeyNavigationAttached)
- Q_PROPERTY(QQuickItem *left READ left WRITE setLeft NOTIFY leftChanged)
- Q_PROPERTY(QQuickItem *right READ right WRITE setRight NOTIFY rightChanged)
- Q_PROPERTY(QQuickItem *up READ up WRITE setUp NOTIFY upChanged)
- Q_PROPERTY(QQuickItem *down READ down WRITE setDown NOTIFY downChanged)
- Q_PROPERTY(QQuickItem *tab READ tab WRITE setTab NOTIFY tabChanged)
- Q_PROPERTY(QQuickItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged)
- Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
+ Q_PROPERTY(QQuickItem *left READ left WRITE setLeft NOTIFY leftChanged FINAL)
+ Q_PROPERTY(QQuickItem *right READ right WRITE setRight NOTIFY rightChanged FINAL)
+ Q_PROPERTY(QQuickItem *up READ up WRITE setUp NOTIFY upChanged FINAL)
+ Q_PROPERTY(QQuickItem *down READ down WRITE setDown NOTIFY downChanged FINAL)
+ Q_PROPERTY(QQuickItem *tab READ tab WRITE setTab NOTIFY tabChanged FINAL)
+ Q_PROPERTY(QQuickItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged FINAL)
+ Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged FINAL)
QML_NAMED_ELEMENT(KeyNavigation)
QML_ADDED_IN_VERSION(2, 0)
@@ -810,8 +826,8 @@ private:
class QQuickLayoutMirroringAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled RESET resetEnabled NOTIFY enabledChanged)
- Q_PROPERTY(bool childrenInherit READ childrenInherit WRITE setChildrenInherit NOTIFY childrenInheritChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled RESET resetEnabled NOTIFY enabledChanged FINAL)
+ Q_PROPERTY(bool childrenInherit READ childrenInherit WRITE setChildrenInherit NOTIFY childrenInheritChanged FINAL)
QML_NAMED_ELEMENT(LayoutMirroring)
QML_ADDED_IN_VERSION(2, 0)
@@ -840,7 +856,7 @@ private:
class QQuickEnterKeyAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(Qt::EnterKeyType type READ type WRITE setType NOTIFY typeChanged)
+ Q_PROPERTY(Qt::EnterKeyType type READ type WRITE setType NOTIFY typeChanged FINAL)
QML_NAMED_ELEMENT(EnterKey)
QML_UNCREATABLE("EnterKey is only available via attached properties")
@@ -883,14 +899,14 @@ public:
QQuickKeyEvent theKeyEvent;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickKeysAttached : public QObject, public QQuickItemKeyFilter
+class Q_QUICK_EXPORT QQuickKeysAttached : public QObject, public QQuickItemKeyFilter
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickKeysAttached)
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
- Q_PROPERTY(QQmlListProperty<QQuickItem> forwardTo READ forwardTo)
- Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QQuickItem> forwardTo READ forwardTo FINAL)
+ Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged FINAL)
QML_NAMED_ELEMENT(Keys)
QML_ADDED_IN_VERSION(2, 0)
@@ -978,7 +994,7 @@ private:
void inputMethodEvent(QInputMethodEvent *, bool post) override;
QVariant inputMethodQuery(Qt::InputMethodQuery query) const override;
#endif
- void shortcutOverride(QKeyEvent *event) override;
+ void shortcutOverrideEvent(QKeyEvent *event) override;
static QByteArray keyToSignal(int key);
bool isConnected(const char *signalName) const;
@@ -1049,12 +1065,4 @@ Q_DECLARE_TYPEINFO(QQuickItemPrivate::ChangeListener, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
-#if QT_CONFIG(quick_shadereffect)
-QML_DECLARE_TYPE(QQuickItemLayer)
-#endif
-QML_DECLARE_TYPE(QQuickKeysAttached)
-QML_DECLARE_TYPE(QQuickKeyNavigationAttached)
-QML_DECLARE_TYPE(QQuickLayoutMirroringAttached)
-QML_DECLARE_TYPE(QQuickEnterKeyAttached)
-
#endif // QQUICKITEM_P_H
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 51fd2a3588..11b2642748 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -770,13 +770,13 @@ void QQuickPathAnimation::setOrientationExitDuration(int duration)
qreal QQuickPathAnimation::endRotation() const
{
Q_D(const QQuickPathAnimation);
- return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
+ return d->endRotation.isValid() ? d->endRotation.value() : qreal(0);
}
void QQuickPathAnimation::setEndRotation(qreal rotation)
{
Q_D(QQuickPathAnimation);
- if (!d->endRotation.isNull && d->endRotation == rotation)
+ if (d->endRotation.isValid() && d->endRotation == rotation)
return;
d->endRotation = rotation;
@@ -946,11 +946,11 @@ void QQuickPathAnimationUpdater::setValue(qreal v)
//shortest distance to correct orientation
qreal diff = angle - startRotation;
while (diff > 180.0) {
- startRotation.value += 360.0;
+ startRotation = startRotation.value() + 360.0;
diff -= 360.0;
}
while (diff < -180.0) {
- startRotation.value -= 360.0;
+ startRotation = startRotation.value() - 360.0;
diff += 360.0;
}
}
@@ -993,9 +993,8 @@ QQuickPathAnimationAnimator::QQuickPathAnimationAnimator(QQuickPathAnimationPriv
QQuickPathAnimationAnimator::~QQuickPathAnimationAnimator()
{
if (animationTemplate && pathUpdater()) {
- QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it =
- animationTemplate->activeAnimations.find(pathUpdater()->target);
- if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ auto it = animationTemplate->activeAnimations.constFind(pathUpdater()->target);
+ if (it != animationTemplate->activeAnimations.cend() && it.value() == this)
animationTemplate->activeAnimations.erase(it);
}
}
diff --git a/src/quick/items/qquickitemanimation_p.h b/src/quick/items/qquickitemanimation_p.h
index 25fc83883f..e2ea93dd60 100644
--- a/src/quick/items/qquickitemanimation_p.h
+++ b/src/quick/items/qquickitemanimation_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
class QQuickParentAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickParentAnimation : public QQuickAnimationGroup
+class Q_QUICK_EXPORT QQuickParentAnimation : public QQuickAnimationGroup
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickParentAnimation)
@@ -58,7 +58,7 @@ protected:
};
class QQuickAnchorAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchorAnimation : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickAnchorAnimation : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnchorAnimation)
@@ -95,7 +95,7 @@ protected:
class QQuickItem;
class QQuickPath;
class QQuickPathAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPathAnimation : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickPathAnimation : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QQuickPathAnimation)
@@ -174,10 +174,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickParentAnimation)
-QML_DECLARE_TYPE(QQuickAnchorAnimation)
-#if QT_CONFIG(quick_path)
-QML_DECLARE_TYPE(QQuickPathAnimation)
-#endif
-
#endif // QQUICKITEMANIMATION_H
diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h
index 57bbf77f70..aed00218e5 100644
--- a/src/quick/items/qquickitemchangelistener_p.h
+++ b/src/quick/items/qquickitemchangelistener_p.h
@@ -15,7 +15,7 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
+#include <QtQuick/private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -81,10 +81,10 @@ private:
#define QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING
-class QQuickItemChangeListener
+class Q_QUICK_EXPORT QQuickItemChangeListener
{
public:
- virtual ~QQuickItemChangeListener() {}
+ virtual ~QQuickItemChangeListener();
virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* oldGeometry */) {}
virtual void itemSiblingOrderChanged(QQuickItem *) {}
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
index bcd1afc19d..e2164129be 100644
--- a/src/quick/items/qquickitemgrabresult.cpp
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -14,11 +14,13 @@
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlInfo>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qquickitem_p.h>
#include <private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
const QEvent::Type Event_Grab_Completed = static_cast<QEvent::Type>(QEvent::User + 1);
@@ -30,6 +32,7 @@ public:
: cacheEntry(nullptr)
, qmlEngine(nullptr)
, texture(nullptr)
+ , devicePixelRatio(1.0)
{
}
@@ -63,6 +66,7 @@ public:
QSGLayer *texture;
QSizeF itemSize;
QSize textureSize;
+ qreal devicePixelRatio;
};
/*!
@@ -235,7 +239,9 @@ void QQuickItemGrabResult::setup()
}
QSGRenderContext *rc = QQuickWindowPrivate::get(d->window.data())->context;
+ d->devicePixelRatio = d->window->effectiveDevicePixelRatio();
d->texture = rc->sceneGraphContext()->createLayer(rc);
+ d->texture->setDevicePixelRatio(d->devicePixelRatio);
d->texture->setItem(QQuickItemPrivate::get(d->item)->itemNode());
d->itemSize = QSizeF(d->item->width(), d->item->height());
}
@@ -248,11 +254,13 @@ void QQuickItemGrabResult::render()
d->texture->setRect(QRectF(0, d->itemSize.height(), d->itemSize.width(), -d->itemSize.height()));
const QSize minSize = QQuickWindowPrivate::get(d->window.data())->context->sceneGraphContext()->minimumFBOSize();
- d->texture->setSize(QSize(qMax(minSize.width(), d->textureSize.width()),
- qMax(minSize.height(), d->textureSize.height())));
+ const QSize effectiveTextureSize = d->textureSize * d->devicePixelRatio;
+ d->texture->setSize(QSize(qMax(minSize.width(), effectiveTextureSize.width()),
+ qMax(minSize.height(), effectiveTextureSize.height())));
d->texture->scheduleUpdate();
d->texture->updateTexture();
- d->image = d->texture->toImage();
+ d->image = d->texture->toImage();
+ d->image.setDevicePixelRatio(d->devicePixelRatio);
delete d->texture;
d->texture = nullptr;
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 9cff9c4c4f..569a0ad74d 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -93,22 +93,11 @@ QT_END_NAMESPACE
static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject *parent)
{
- // When setting a parent (especially during dynamic object creation) in QML,
- // also try to set up the analogous item/window relationship.
if (QQuickItem *parentItem = qmlobject_cast<QQuickItem *>(parent)) {
- QQuickItem *item = qmlobject_cast<QQuickItem *>(obj);
- if (item) {
+ if (QQuickItem *item = qmlobject_cast<QQuickItem *>(obj)) {
// An Item has another Item
item->setParentItem(parentItem);
return QQmlPrivate::Parented;
- } else if (parentItem->window()) {
- QQuickWindow *win = qmlobject_cast<QQuickWindow *>(obj);
- if (win) {
- // A Window inside an Item should be transient for that item's window
- qCDebug(lcTransient) << win << "is transient for" << parentItem->window();
- win->setTransientParent(parentItem->window());
- return QQmlPrivate::Parented;
- }
} else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
QQuickItemPrivate::get(parentItem)->addPointerHandler(handler);
handler->setParent(parent);
@@ -116,13 +105,7 @@ static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject
}
return QQmlPrivate::IncompatibleObject;
} else if (QQuickWindow *parentWindow = qmlobject_cast<QQuickWindow *>(parent)) {
- QQuickWindow *win = qmlobject_cast<QQuickWindow *>(obj);
- if (win) {
- // A Window inside a Window should be transient for it
- qCDebug(lcTransient) << win << "is transient for" << parentWindow;
- win->setTransientParent(parentWindow);
- return QQmlPrivate::Parented;
- } else if (QQuickItem *item = qmlobject_cast<QQuickItem *>(obj)) {
+ if (QQuickItem *item = qmlobject_cast<QQuickItem *>(obj)) {
// The parent of an Item inside a Window is actually the implicit content Item
item->setParentItem(parentWindow->contentItem());
return QQmlPrivate::Parented;
@@ -160,4 +143,139 @@ void QQuickItemsModule::defineModule()
qt_quickitems_defineModule();
}
+/*!
+ \qmltype PointerEvent
+ \instantiates QPointerEvent
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointerEvent.
+
+ PointerEvent is the QML name of the QPointerEvent class.
+*/
+
+/*!
+ \qmltype PointerDevice
+ \instantiates QPointingDevice
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointingDevice.
+
+ PointerDevice is the QML name of the QPointingDevice class.
+ It has the same properties and enums as \l QPointingDevice.
+*/
+
+/*!
+ \qmlproperty enumeration PointerDevice::deviceType
+
+ This property tells the type of device that generated a PointerEvent.
+
+ Valid values are:
+
+ \value PointerDevice.Unknown The device cannot be identified.
+ \value PointerDevice.Mouse A mouse.
+ \value PointerDevice.TouchScreen A touchscreen.
+ \value PointerDevice.TouchPad A touchpad or trackpad.
+ \value PointerDevice.Stylus A stylus on a graphics tablet.
+ \value PointerDevice.Airbrush An airbrush on a graphics tablet.
+ \value PointerDevice.Puck A digitizer with crosshairs, on a graphics tablet.
+
+ \sa QInputDevice::DeviceType, PointerDeviceHandler::acceptedDevices
+*/
+
+/*!
+ \qmlproperty enumeration PointerDevice::pointerType
+
+ This property tells what is interacting with the PointerDevice.
+
+ There is some redundancy between this property and \l deviceType.
+ For example, if a touchscreen is used, then \c deviceType is
+ \c TouchScreen and \c pointerType is \c Finger. But on a graphics
+ tablet, it's often possible for both ends of the stylus to be used,
+ and programs need to distinguish them.
+ \l PointerDeviceHandler::acceptedDevices and
+ \l PointerDeviceHandler::acceptedPointerTypes can be used in combination
+ to filter the subset of events that a particular handler should react to.
+
+ Valid values are:
+
+ \value PointerDevice.Unknown The device cannot be identified.
+ \value PointerDevice.Generic A mouse or a device that emulates a mouse.
+ \value PointerDevice.Finger A finger on a touchscreen.
+ \value PointerDevice.Pen A stylus on a graphics tablet.
+ \value PointerDevice.Eraser An eraser on a graphics tablet.
+ \value PointerDevice.Cursor A digitizer with crosshairs, on a graphics tablet.
+
+ \sa QPointingDevice::PointerType, PointerDeviceHandler::acceptedPointerTypes
+*/
+
+/*!
+ \qmlproperty int PointerDevice::maximumPoints
+
+ This property tells the maximum number of simultaneous touch points
+ (fingers) that can be detected.
+*/
+
+/*!
+ \qmlproperty int PointerDevice::buttonCount
+
+ This property tells the maximum number of on-device buttons that can be
+ detected.
+*/
+
+/*!
+ \qmltype pointingDeviceUniqueId
+ \instantiates QPointingDeviceUniqueId
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointingDeviceUniqueId.
+
+ pointingDeviceUniqueId is the QML name of the QPointingDeviceUniqueId class.
+*/
+
+/*!
+ \qmlproperty qint64 pointingDeviceUniqueId::numericId
+
+ This property gives the numeric ID of the \l PointerDevice, if available;
+ otherwise it is \c -1.
+*/
+
+/*!
+ \qmlproperty pointingDeviceUniqueId PointerDevice::uniqueId
+
+ This property may provide a unique ID for the device, if available. For
+ example, a graphics tablet stylus device may have a unique serial number.
+
+ \sa eventPoint, QEventPoint::uniqueId()
+*/
+
+/*!
+ \qmlsignal PointerDevice::grabChanged(QtObject grabber, enumeration transition, PointerEvent event, eventPoint point)
+
+ This signal is emitted when the \a grabber object gains or loses an
+ exclusive or passive grab of \a point during delivery of \a event.
+ The \a transition tells what happened, from the perspective of the
+ \c grabber object, which may be either an \l Item or an
+ \l {Qt Quick Input Handlers}{Input Handler}.
+
+ Valid values for \a transition are:
+
+ \value PointerDevice.GrabExclusive
+ The \a grabber has taken primary responsibility for handling the \a point.
+ \value PointerDevice.UngrabExclusive
+ The \a grabber has given up its previous exclusive grab.
+ \value PointerDevice.CancelGrabExclusive
+ The exclusive grab of \a grabber has been taken over or cancelled.
+ \value PointerDevice.GrabPassive
+ The \a grabber has acquired a passive grab, to monitor the \a point.
+ \value PointerDevice.UngrabPassive
+ The \a grabber has given up its previous passive grab.
+ \value PointerDevice.CancelGrabPassive
+ The previous passive grab has terminated abnormally.
+
+ \note A grab transition from one object to another results in two signals,
+ to notify that one object has lost its grab, and to notify that there is
+ another grabber. In other cases, when transitioning to or from a non-grabbing
+ state, only one signal is emitted.
+
+ \sa QPointerEvent::setExclusiveGrabber(), QPointerEvent::addPassiveGrabber(),
+ QPointerEvent::removePassiveGrabber(), PointerHandler::grabChanged()
+*/
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index a149f8d098..70d460a323 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -160,8 +160,8 @@ void QQuickItemView::setModel(const QVariant &m)
disconnect(d->model, SIGNAL(createdItem(int,QObject*)), this, SLOT(createdItem(int,QObject*)));
disconnect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*)));
if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
- disconnect(delegateModel, SIGNAL(itemPooled(int, QObject *)), this, SLOT(onItemPooled(int, QObject *)));
- disconnect(delegateModel, SIGNAL(itemReused(int, QObject *)), this, SLOT(onItemReused(int, QObject *)));
+ disconnect(delegateModel, SIGNAL(itemPooled(int,QObject*)), this, SLOT(onItemPooled(int,QObject*)));
+ disconnect(delegateModel, SIGNAL(itemReused(int,QObject*)), this, SLOT(onItemReused(int,QObject*)));
}
}
@@ -199,8 +199,8 @@ void QQuickItemView::setModel(const QVariant &m)
connect(d->model, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*)));
connect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*)));
if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
- connect(delegateModel, SIGNAL(itemPooled(int, QObject *)), this, SLOT(onItemPooled(int, QObject *)));
- connect(delegateModel, SIGNAL(itemReused(int, QObject *)), this, SLOT(onItemReused(int, QObject *)));
+ connect(delegateModel, SIGNAL(itemPooled(int,QObject*)), this, SLOT(onItemPooled(int,QObject*)));
+ connect(delegateModel, SIGNAL(itemReused(int,QObject*)), this, SLOT(onItemReused(int,QObject*)));
}
if (isComponentComplete()) {
d->updateSectionCriteria();
@@ -211,10 +211,12 @@ void QQuickItemView::setModel(const QVariant &m)
setCurrentIndex(d->model->count() > 0 ? 0 : -1);
d->updateViewport();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner && d->transitioner->populateTransition) {
d->transitioner->setPopulateTransitionEnabled(true);
d->forceLayoutPolish();
}
+#endif
}
connect(d->model, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
@@ -684,6 +686,7 @@ void QQuickItemView::setReuseItems(bool reuse)
emit reuseItemsChanged();
}
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *QQuickItemView::populateTransition() const
{
Q_D(const QQuickItemView);
@@ -811,6 +814,7 @@ void QQuickItemView::setDisplacedTransition(QQuickTransition *transition)
emit displacedTransitionChanged();
}
}
+#endif
void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
{
@@ -1148,11 +1152,13 @@ void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry
// don't allow item movement transitions to trigger a re-layout and
// start new transitions
bool prevInLayout = inLayout;
+#if QT_CONFIG(quick_viewtransitions)
if (!inLayout) {
FxViewItem *actualItem = transitioner ? visibleItem(currentIndex) : nullptr;
if (actualItem && actualItem->transitionRunning())
inLayout = true;
}
+#endif
updateHighlight();
inLayout = prevInLayout;
}
@@ -1165,17 +1171,20 @@ void QQuickItemView::destroyRemoved()
{
Q_D(QQuickItemView);
+#if QT_CONFIG(quick_viewtransitions)
bool hasRemoveTransition = false;
bool hasRemoveTransitionAsTarget = false;
if (d->transitioner) {
hasRemoveTransition = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false);
hasRemoveTransitionAsTarget = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true);
}
+#endif
for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
it != d->visibleItems.end();) {
FxViewItem *item = *it;
if (item->index == -1 && (!item->attached || item->attached->delayRemove() == false)) {
+#if QT_CONFIG(quick_viewtransitions)
if (hasRemoveTransitionAsTarget) {
// don't remove from visibleItems until next layout()
d->runDelayedRemoveTransition = true;
@@ -1184,9 +1193,12 @@ void QQuickItemView::destroyRemoved()
} else {
if (hasRemoveTransition)
d->runDelayedRemoveTransition = true;
+#endif
d->releaseItem(item, d->reusableFlag);
it = d->visibleItems.erase(it);
+#if QT_CONFIG(quick_viewtransitions)
}
+#endif
} else {
++it;
}
@@ -1201,8 +1213,10 @@ void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
Q_D(QQuickItemView);
if (reset) {
cancelFlick();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
+#endif
d->moveReason = QQuickItemViewPrivate::SetIndex;
d->regenerate();
if (d->highlight && d->currentItem) {
@@ -1212,8 +1226,10 @@ void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
}
d->moveReason = QQuickItemViewPrivate::Other;
emit countChanged();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner && d->transitioner->populateTransition)
d->forceLayoutPolish();
+#endif
} else {
if (d->inLayout) {
d->bufferedChanges.prepare(d->currentIndex, d->itemCount);
@@ -1251,7 +1267,9 @@ void QQuickItemView::trackedPositionChanged()
return;
}
- if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
+ const bool needMoveToTrackHighlight = d->autoHighlight || d->highlightRange != NoHighlightRange;
+
+ if (d->moveReason == QQuickItemViewPrivate::SetIndex && needMoveToTrackHighlight) {
qreal trackedPos = d->trackedItem->position();
qreal trackedSize = d->trackedItem->size();
qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
@@ -1449,8 +1467,10 @@ void QQuickItemView::componentComplete()
d->updateFooter();
d->updateViewport();
d->setPosition(d->contentStartOffset());
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
+#endif
if (d->isValid()) {
d->refill();
@@ -1478,7 +1498,6 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
, displayMarginBeginning(0), displayMarginEnd(0)
, layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
- , moveReason(Other)
, visibleIndex(0)
, currentIndex(-1), currentItem(nullptr)
, trackedItem(nullptr), requestedIndex(-1)
@@ -1487,7 +1506,9 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, highlightRangeStart(0), highlightRangeEnd(0)
, highlightMoveDuration(150)
, headerComponent(nullptr), header(nullptr), footerComponent(nullptr), footer(nullptr)
+#if QT_CONFIG(quick_viewtransitions)
, transitioner(nullptr)
+#endif
, minExtent(0), maxExtent(0)
, ownModel(false), wrap(false)
, keyNavigationEnabled(true)
@@ -1495,7 +1516,10 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
, haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
, fillCacheBuffer(false), inRequest(false)
- , runDelayedRemoveTransition(false), delegateValidated(false), isClearing(false)
+#if QT_CONFIG(quick_viewtransitions)
+ , runDelayedRemoveTransition(false)
+#endif
+ , delegateValidated(false), isClearing(false)
{
bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
bufferPause.setLoopCount(1);
@@ -1504,9 +1528,11 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
QQuickItemViewPrivate::~QQuickItemViewPrivate()
{
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
transitioner->setChangeListener(nullptr);
delete transitioner;
+#endif
}
bool QQuickItemViewPrivate::isValid() const
@@ -1682,11 +1708,13 @@ void QQuickItemViewPrivate::clear(bool onDestruction)
releaseVisibleItems(QQmlInstanceModel::NotReusable);
visibleIndex = 0;
+#if QT_CONFIG(quick_viewtransitions)
for (FxViewItem *item : std::as_const(releasePendingTransition)) {
item->releaseAfterTransition = false;
releaseItem(item, QQmlInstanceModel::NotReusable);
}
releasePendingTransition.clear();
+#endif
auto oldCurrentItem = currentItem;
releaseItem(currentItem, QQmlDelegateModel::NotReusable);
@@ -1736,6 +1764,8 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
Q_Q(QQuickItemView);
if (!model || !model->isValid() || !q->isComponentComplete())
return;
+ if (q->size().isEmpty() && visibleItems.isEmpty())
+ return;
if (!model->count()) {
updateHeader();
updateFooter();
@@ -1759,7 +1789,6 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
qreal fillTo = to;
bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
- bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
if (added) {
@@ -1775,6 +1804,8 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
}
}
+ bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
+
if (added || removed) {
markExtentsDirty();
updateBeginningEnd();
@@ -1836,12 +1867,15 @@ void QQuickItemViewPrivate::layout()
clear();
setPosition(contentStartOffset());
updateViewport();
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
transitioner->setPopulateTransitionEnabled(false);
+#endif
inLayout = false;
return;
}
+#if QT_CONFIG(quick_viewtransitions)
if (runDelayedRemoveTransition && transitioner
&& transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
// assume that any items moving now are moving due to the remove - if they schedule
@@ -1849,6 +1883,7 @@ void QQuickItemViewPrivate::layout()
for (int i=0; i<visibleItems.size(); i++)
visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
}
+#endif
ChangeResult insertionPosChanges;
ChangeResult removalPosChanges;
@@ -1862,6 +1897,7 @@ void QQuickItemViewPrivate::layout()
}
forceLayout = false;
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
// Give the view one more chance to refill itself,
// in case its size is changed such that more delegates become visible after component completed
@@ -1871,12 +1907,15 @@ void QQuickItemViewPrivate::layout()
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
}
}
+#endif
updateSections();
layoutVisibleItems();
storeFirstVisibleItemPosition();
+#if QT_CONFIG(quick_viewtransitions)
int lastIndexInView = findLastIndexInView();
+#endif
refill();
markExtentsDirty();
updateHighlight();
@@ -1891,6 +1930,7 @@ void QQuickItemViewPrivate::layout()
updateViewport();
updateUnrequestedPositions();
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner) {
// items added in the last refill() may need to be transitioned in - e.g. a remove
// causes items to slide up into view
@@ -1931,11 +1971,14 @@ void QQuickItemViewPrivate::layout()
transitioner->setPopulateTransitionEnabled(false);
transitioner->resetTargetLists();
}
+#endif
if (!currentItem)
updateCurrent(currentIndex);
+#if QT_CONFIG(quick_viewtransitions)
runDelayedRemoveTransition = false;
+#endif
inLayout = false;
}
@@ -1991,6 +2034,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
removalResult.countChangeBeforeVisible += (correctedFirstVisibleIndex - r.index);
}
}
+#if QT_CONFIG(quick_viewtransitions)
if (runDelayedRemoveTransition) {
QQmlChangeSet::Change removal;
for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
@@ -2004,6 +2048,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
}
}
}
+#endif
*totalRemovalResult += removalResult;
if (!removals.isEmpty()) {
updateVisibleIndex();
@@ -2042,6 +2087,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
item->attached->emitAdd();
}
+#if QT_CONFIG(quick_viewtransitions)
// for each item that was moved directly into the view as a result of a move(),
// find the index it was moved from in order to set its initial position, so that we
// can transition it from this "original" position to its new position in the view
@@ -2057,13 +2103,16 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
}
}
}
+#endif
// reposition visibleItems.first() correctly so that the content y doesn't jump
if (removedCount != prevVisibleItemsCount)
repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
// Whatever removed/moved items remain are no longer visible items.
+#if QT_CONFIG(quick_viewtransitions)
prepareRemoveTransitions(&currentChanges.removedItems);
+#endif
for (auto it = currentChanges.removedItems.begin();
it != currentChanges.removedItems.end(); ++it) {
releaseItem(it.value(), reusableFlag);
@@ -2120,10 +2169,12 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo
} else if (item->index >= removal.index + removal.count) {
// after removed items
item->index -= removal.count;
+#if QT_CONFIG(quick_viewtransitions)
if (removal.isMove())
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
else
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
+#endif
++it;
} else {
// removed item
@@ -2157,7 +2208,9 @@ void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQmlChangeSet::Ch
}
if (removal.isMove()) {
currentChanges.removedItems.replace(removal.moveKey(item->index), item);
+#if QT_CONFIG(quick_viewtransitions)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
+#endif
} else {
// track item so it is released later
currentChanges.removedItems.insert(QQmlChangeSet::MoveKey(), item);
@@ -2211,6 +2264,7 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs
}
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickItemViewPrivate::createTransitioner()
{
if (!transitioner) {
@@ -2286,6 +2340,7 @@ void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitiona
}
}
}
+#endif
/*
This may return 0 if the item is being created asynchronously.
@@ -2299,6 +2354,7 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::Inc
if (requestedIndex == modelIndex && incubationMode == QQmlIncubator::Asynchronous)
return nullptr;
+#if QT_CONFIG(quick_viewtransitions)
for (int i=0; i<releasePendingTransition.size(); i++) {
if (releasePendingTransition.at(i)->index == modelIndex
&& !releasePendingTransition.at(i)->isPendingRemoval()) {
@@ -2306,6 +2362,7 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::Inc
return releasePendingTransition.takeAt(i);
}
}
+#endif
inRequest = true;
diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h
index f74ca5177b..cecbf094cb 100644
--- a/src/quick/items/qquickitemview_p.h
+++ b/src/quick/items/qquickitemview_p.h
@@ -31,7 +31,7 @@ class QQmlChangeSet;
class QQuickItemViewPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickItemView : public QQuickFlickable
+class Q_QUICK_EXPORT QQuickItemView : public QQuickFlickable
{
Q_OBJECT
@@ -57,6 +57,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickItemView : public QQuickFlickable
Q_PROPERTY(QQmlComponent *footer READ footer WRITE setFooter NOTIFY footerChanged)
Q_PROPERTY(QQuickItem *footerItem READ footerItem NOTIFY footerItemChanged)
+#if QT_CONFIG(quick_viewtransitions)
Q_PROPERTY(QQuickTransition *populate READ populateTransition WRITE setPopulateTransition NOTIFY populateTransitionChanged)
Q_PROPERTY(QQuickTransition *add READ addTransition WRITE setAddTransition NOTIFY addTransitionChanged)
Q_PROPERTY(QQuickTransition *addDisplaced READ addDisplacedTransition WRITE setAddDisplacedTransition NOTIFY addDisplacedTransitionChanged)
@@ -65,6 +66,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickItemView : public QQuickFlickable
Q_PROPERTY(QQuickTransition *remove READ removeTransition WRITE setRemoveTransition NOTIFY removeTransitionChanged)
Q_PROPERTY(QQuickTransition *removeDisplaced READ removeDisplacedTransition WRITE setRemoveDisplacedTransition NOTIFY removeDisplacedTransitionChanged)
Q_PROPERTY(QQuickTransition *displaced READ displacedTransition WRITE setDisplacedTransition NOTIFY displacedTransitionChanged)
+#endif
Q_PROPERTY(QQmlComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged)
Q_PROPERTY(QQuickItem *highlightItem READ highlightItem NOTIFY highlightItemChanged)
@@ -144,6 +146,7 @@ public:
void setHeader(QQmlComponent *);
QQuickItem *headerItem() const;
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *populateTransition() const;
void setPopulateTransition(QQuickTransition *transition);
@@ -167,6 +170,7 @@ public:
QQuickTransition *displacedTransition() const;
void setDisplacedTransition(QQuickTransition *transition);
+#endif
QQmlComponent *highlight() const;
void setHighlight(QQmlComponent *);
@@ -233,6 +237,7 @@ Q_SIGNALS:
void headerItemChanged();
void footerItemChanged();
+#if QT_CONFIG(quick_viewtransitions)
void populateTransitionChanged();
void addTransitionChanged();
void addDisplacedTransitionChanged();
@@ -241,6 +246,7 @@ Q_SIGNALS:
void removeTransitionChanged();
void removeDisplacedTransitionChanged();
void displacedTransitionChanged();
+#endif
void highlightChanged();
void highlightItemChanged();
@@ -277,17 +283,17 @@ private:
};
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewAttached : public QObject
+class Q_QUICK_EXPORT QQuickItemViewAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickItemView *view READ view NOTIFY viewChanged)
- Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged)
- Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged)
+ Q_PROPERTY(QQuickItemView *view READ view NOTIFY viewChanged FINAL)
+ Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged FINAL)
+ Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged FINAL)
- Q_PROPERTY(QString section READ section NOTIFY sectionChanged)
- Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged)
- Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged)
+ Q_PROPERTY(QString section READ section NOTIFY sectionChanged FINAL)
+ Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged FINAL)
+ Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged FINAL)
public:
QQuickItemViewAttached(QObject *parent)
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index b978410bce..c05233d82a 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -21,12 +21,15 @@ QT_REQUIRE_CONFIG(quick_itemview);
#include "qquickitemview_p.h"
#include "qquickitemviewfxitem_p_p.h"
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickitemviewtransition_p.h"
+#endif
#include "qquickflickable_p_p.h"
#include <QtQmlModels/private/qqmlobjectmodel_p.h>
#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include <QtQmlModels/private/qqmlchangeset_p.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -64,7 +67,12 @@ public:
};
-class Q_QUICK_AUTOTEST_EXPORT QQuickItemViewPrivate : public QQuickFlickablePrivate, public QQuickItemViewTransitionChangeListener, public QAnimationJobChangeListener
+class Q_QUICK_AUTOTEST_EXPORT QQuickItemViewPrivate
+ : public QQuickFlickablePrivate
+#if QT_CONFIG(quick_viewtransitions)
+ , public QQuickItemViewTransitionChangeListener
+#endif
+ , public QAnimationJobChangeListener
{
public:
Q_DECLARE_PUBLIC(QQuickItemView)
@@ -112,7 +120,6 @@ public:
};
enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
- enum MovementReason { Other, SetIndex, Mouse };
bool isValid() const;
qreal position() const;
@@ -166,11 +173,13 @@ public:
void repositionFirstItem(FxViewItem *prevVisibleItemsFirst, qreal prevVisibleItemsFirstPos,
FxViewItem *prevFirstVisible, ChangeResult *insertionResult, ChangeResult *removalResult);
+#if QT_CONFIG(quick_viewtransitions)
void createTransitioner();
void prepareVisibleItemTransitions();
void prepareRemoveTransitions(QMultiHash<QQmlChangeSet::MoveKey, FxViewItem *> *removedItems);
bool prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds);
void viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) override;
+#endif
int findMoveKeyIndex(QQmlChangeSet::MoveKey key, const QVector<QQmlChangeSet::Change> &changes) const;
@@ -187,7 +196,10 @@ public:
bool hasPendingChanges() const {
return currentChanges.hasPendingChanges()
|| bufferedChanges.hasPendingChanges()
- ||runDelayedRemoveTransition;
+#if QT_CONFIG(quick_viewtransitions)
+ ||runDelayedRemoveTransition
+#endif
+ ;
}
void refillOrLayout() {
@@ -224,8 +236,6 @@ public:
Qt::LayoutDirection layoutDirection;
QQuickItemView::VerticalLayoutDirection verticalLayoutDirection;
- MovementReason moveReason;
-
QList<FxViewItem *> visibleItems;
qreal firstVisibleItemPosition = 0;
void storeFirstVisibleItemPosition() {
@@ -266,8 +276,10 @@ public:
MovedItem(FxViewItem *i, QQmlChangeSet::MoveKey k)
: item(i), moveKey(k) {}
};
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner *transitioner;
QVector<FxViewItem *> releasePendingTransition;
+#endif
mutable qreal minExtent;
mutable qreal maxExtent;
@@ -286,7 +298,9 @@ public:
bool highlightRangeEndValid : 1;
bool fillCacheBuffer : 1;
bool inRequest : 1;
+#if QT_CONFIG(quick_viewtransitions)
bool runDelayedRemoveTransition : 1;
+#endif
bool delegateValidated : 1;
bool isClearing : 1;
@@ -333,7 +347,9 @@ protected:
QList<FxViewItem *> *newItems, QList<MovedItem> *movingIntoView) = 0;
virtual bool needsRefillForAddedOrRemovedIndex(int) const { return false; }
+#if QT_CONFIG(quick_viewtransitions)
virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult) = 0;
+#endif
virtual void initializeViewItem(FxViewItem *) {}
virtual void initializeCurrentItem() {}
diff --git a/src/quick/items/qquickitemviewfxitem.cpp b/src/quick/items/qquickitemviewfxitem.cpp
index 70e122ca60..d372fde0c8 100644
--- a/src/quick/items/qquickitemviewfxitem.cpp
+++ b/src/quick/items/qquickitemviewfxitem.cpp
@@ -10,7 +10,9 @@ QT_BEGIN_NAMESPACE
QQuickItemViewFxItem::QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener* changeListener)
: item(item)
, changeListener(changeListener)
+#if QT_CONFIG(quick_viewtransitions)
, transitionableItem(nullptr)
+#endif
, ownItem(ownItem)
, releaseAfterTransition(false)
, trackGeom(false)
@@ -19,8 +21,10 @@ QQuickItemViewFxItem::QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuic
QQuickItemViewFxItem::~QQuickItemViewFxItem()
{
+#if QT_CONFIG(quick_viewtransitions)
delete transitionableItem;
transitionableItem = nullptr;
+#endif
if (ownItem && item) {
trackGeometry(false);
@@ -31,28 +35,47 @@ QQuickItemViewFxItem::~QQuickItemViewFxItem()
qreal QQuickItemViewFxItem::itemX() const
{
- return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0);
+ return
+#if QT_CONFIG(quick_viewtransitions)
+ transitionableItem ? transitionableItem->itemX() :
+#endif
+ (item ? item->x() : 0);
}
qreal QQuickItemViewFxItem::itemY() const
{
- return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0);
+ return
+#if QT_CONFIG(quick_viewtransitions)
+ transitionableItem ? transitionableItem->itemY() :
+#endif
+ (item ? item->y() : 0);
}
void QQuickItemViewFxItem::moveTo(const QPointF &pos, bool immediate)
{
+#if QT_CONFIG(quick_viewtransitions)
if (transitionableItem)
transitionableItem->moveTo(pos, immediate);
- else if (item)
+ else
+#else
+ Q_UNUSED(immediate)
+#endif
+ if (item)
item->setPosition(pos);
}
void QQuickItemViewFxItem::setVisible(bool visible)
{
- if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
+ if (!visible
+#if QT_CONFIG(quick_viewtransitions)
+ && transitionableItem && transitionableItem->transitionScheduledOrRunning()
+#endif
+ )
return;
- if (item)
+ if (item) {
QQuickItemPrivate::get(item)->setCulled(!visible);
+ QQuickItemPrivate::get(item)->isAccessible = visible;
+ }
}
void QQuickItemViewFxItem::trackGeometry(bool track)
@@ -87,6 +110,7 @@ void QQuickItemViewFxItem::setGeometry(const QRectF &geometry)
item->setSize(geometry.size());
}
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner::TransitionType QQuickItemViewFxItem::scheduledTransitionType() const
{
return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
@@ -126,6 +150,7 @@ void QQuickItemViewFxItem::startTransition(QQuickItemViewTransitioner *transitio
if (transitionableItem)
transitionableItem->startTransition(transitioner, index);
}
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitemviewfxitem_p_p.h b/src/quick/items/qquickitemviewfxitem_p_p.h
index b453054a89..e6e10c4233 100644
--- a/src/quick/items/qquickitemviewfxitem_p_p.h
+++ b/src/quick/items/qquickitemviewfxitem_p_p.h
@@ -17,14 +17,18 @@
#include <QtQuick/private/qtquickglobal_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#if QT_CONFIG(quick_viewtransitions)
#include <QtQuick/private/qquickitemviewtransition_p.h>
+#endif
#include <private/qanimationjobutil_p.h>
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(quick_itemview);
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewFxItem
+class Q_QUICK_EXPORT QQuickItemViewFxItem
{
public:
QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener *changeListener);
@@ -42,6 +46,7 @@ public:
QRectF geometry() const;
void setGeometry(const QRectF &geometry);
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner::TransitionType scheduledTransitionType() const;
bool transitionScheduledOrRunning() const;
bool transitionRunning() const;
@@ -50,6 +55,7 @@ public:
void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
void startTransition(QQuickItemViewTransitioner *transitioner);
+#endif
// these are positions and sizes along the current direction of scrolling/flicking
virtual qreal position() const = 0;
@@ -62,7 +68,9 @@ public:
SelfDeletable m_selfDeletable;
QPointer<QQuickItem> item;
QQuickItemChangeListener *changeListener;
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitionableItem *transitionableItem;
+#endif
int index = -1;
bool ownItem : 1;
bool releaseAfterTransition : 1;
diff --git a/src/quick/items/qquickitemviewtransition_p.h b/src/quick/items/qquickitemviewtransition_p.h
index 01e504a440..7a2d23c3cd 100644
--- a/src/quick/items/qquickitemviewtransition_p.h
+++ b/src/quick/items/qquickitemviewtransition_p.h
@@ -26,6 +26,8 @@ QT_REQUIRE_CONFIG(quick_viewtransitions);
#include <private/qquicktransition_p.h>
#include <private/qanimationjobutil_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickItem;
@@ -34,7 +36,7 @@ class QQuickItemViewTransitionableItem;
class QQuickItemViewTransitionJob;
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitionChangeListener
+class Q_QUICK_EXPORT QQuickItemViewTransitionChangeListener
{
public:
QQuickItemViewTransitionChangeListener() {}
@@ -44,7 +46,7 @@ public:
};
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitioner
+class Q_QUICK_EXPORT QQuickItemViewTransitioner
{
public:
enum TransitionType {
@@ -104,7 +106,7 @@ private:
/*
An item that can be transitioned using QQuickViewTransitionJob.
*/
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitionableItem
+class Q_QUICK_EXPORT QQuickItemViewTransitionableItem
{
public:
QQuickItemViewTransitionableItem(QQuickItem *i);
@@ -152,12 +154,12 @@ class QQuickViewTransitionAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(int index READ index NOTIFY indexChanged)
- Q_PROPERTY(QQuickItem* item READ item NOTIFY itemChanged)
- Q_PROPERTY(QPointF destination READ destination NOTIFY destinationChanged)
+ Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
+ Q_PROPERTY(QQuickItem* item READ item NOTIFY itemChanged FINAL)
+ Q_PROPERTY(QPointF destination READ destination NOTIFY destinationChanged FINAL)
- Q_PROPERTY(QList<int> targetIndexes READ targetIndexes NOTIFY targetIndexesChanged)
- Q_PROPERTY(QQmlListProperty<QObject> targetItems READ targetItems NOTIFY targetItemsChanged)
+ Q_PROPERTY(QList<int> targetIndexes READ targetIndexes NOTIFY targetIndexesChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QObject> targetItems READ targetItems NOTIFY targetItemsChanged FINAL)
QML_NAMED_ELEMENT(ViewTransition)
QML_ADDED_IN_VERSION(2, 0)
@@ -196,6 +198,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickViewTransitionAttached)
-
#endif // QQUICKITEMVIEWTRANSITION_P_P_H
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 7693c9d288..6b185b4e7c 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -3,6 +3,7 @@
#include "qquicklistview_p.h"
#include "qquickitemview_p_p.h"
+#include "qquickflickablebehavior_p.h"
#include <private/qqmlobjectmodel_p.h>
#include <QtQml/qqmlexpression.h>
@@ -14,7 +15,6 @@
#include <private/qquicksmoothedanimation_p_p.h>
#include <private/qqmlcomponent_p.h>
-#include "qplatformdefs.h"
QT_BEGIN_NAMESPACE
@@ -74,7 +74,9 @@ public:
void layoutVisibleItems(int fromModelIndex = 0) override;
bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView) override;
+#if QT_CONFIG(quick_viewtransitions)
void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult) override;
+#endif
void updateSectionCriteria() override;
void updateSections() override;
@@ -105,7 +107,7 @@ public:
void fixupPosition() override;
void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity) override;
+ QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity) override;
QQuickItemViewAttached *getAttachedObject(const QObject *object) const override;
@@ -287,7 +289,8 @@ public:
: itemX() + itemWidth());
}
}
- void setPosition(qreal pos, bool immediate = false) {
+
+ void setPosition(qreal pos, bool immediate = false, bool resetInactiveAxis = true) {
// position the section immediately even if there is a transition
if (section()) {
if (view->orientation() == QQuickListView::Vertical) {
@@ -302,8 +305,9 @@ public:
section()->setX(pos);
}
}
- moveTo(pointForPosition(pos), immediate);
+ moveTo(pointForPosition(pos, resetInactiveAxis), immediate);
}
+
void setSize(qreal size) {
if (view->orientation() == QQuickListView::Vertical)
item->setHeight(size);
@@ -318,26 +322,26 @@ public:
QQuickListView *view;
private:
- QPointF pointForPosition(qreal pos) const {
+ QPointF pointForPosition(qreal pos, bool resetInactiveAxis) const {
if (view->orientation() == QQuickListView::Vertical) {
if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
if (section())
pos += section()->height();
- return QPointF(itemX(), -itemHeight() - pos);
+ return QPointF(resetInactiveAxis ? 0 : itemX(), -itemHeight() - pos);
} else {
if (section())
pos += section()->height();
- return QPointF(itemX(), pos);
+ return QPointF(resetInactiveAxis ? 0 : itemX(), pos);
}
} else {
if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
if (section())
pos += section()->width();
- return QPointF(-itemWidth() - pos, itemY());
+ return QPointF(-itemWidth() - pos, resetInactiveAxis ? 0 : itemY());
} else {
if (section())
pos += section()->width();
- return QPointF(pos, itemY());
+ return QPointF(pos, resetInactiveAxis ? 0 : itemY());
}
}
}
@@ -700,6 +704,8 @@ bool QQuickListViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::Reu
bool released = QQuickItemViewPrivate::releaseItem(item, reusableFlag);
if (released && it && att && att->m_sectionItem) {
+ QQuickItemPrivate::get(att->m_sectionItem)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+
// We hold no more references to this item
int i = 0;
do {
@@ -755,7 +761,9 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, incubationMode))))
break;
qCDebug(lcItemViewDelegateLifecycle) << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer << "item" << (QObject *)(item->item);
+#if QT_CONFIG(quick_viewtransitions)
if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
+#endif
item->setPosition(pos, true);
if (item->item)
QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
@@ -774,7 +782,9 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
qCDebug(lcItemViewDelegateLifecycle) << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer << "item" << (QObject *)(item->item);
--visibleIndex;
visiblePos -= item->size() + spacing;
+#if QT_CONFIG(quick_viewtransitions)
if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
+#endif
item->setPosition(visiblePos, true);
if (item->item)
QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
@@ -787,11 +797,14 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
void QQuickListViewPrivate::removeItem(FxViewItem *item)
{
+#if QT_CONFIG(quick_viewtransitions)
if (item->transitionScheduledOrRunning()) {
qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item" << item->index << (QObject *)(item->item);
item->releaseAfterTransition = true;
releasePendingTransition.append(item);
- } else {
+ } else
+#endif
+ {
qCDebug(lcItemViewDelegateLifecycle) << "\treleasing stationary item" << item->index << (QObject *)(item->item);
releaseItem(item, reusableFlag);
}
@@ -864,6 +877,14 @@ void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
FxListItemSG *firstItem = static_cast<FxListItemSG *>(visibleItems.constFirst());
bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
+
+#if QT_CONFIG(quick_viewtransitions)
+ /* Set position of first item in list view when populate transition is configured, as it doesn't set
+ while adding visible item (addVisibleItem()) to the view */
+ if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true))
+ resetFirstItemPosition(isContentFlowReversed() ? -firstItem->position()-firstItem->size() : firstItem->position());
+#endif
+
firstVisibleItemPosition = firstItem->position();
qreal sum = firstItem->size();
qreal pos = firstItem->position() + firstItem->size() + spacing;
@@ -1061,19 +1082,24 @@ QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
setSectionHelper(context, sectionItem, section);
} else {
- QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
- QQmlContext *context = new QQmlContext(
- creationContext ? creationContext : qmlContext(q));
QQmlComponent* delegate = sectionCriteria->delegate();
- QQmlComponentPrivate* delegatePriv = QQmlComponentPrivate::get(delegate);
+ const bool reuseExistingContext = delegate->isBound();
+ auto delegatePriv = QQmlComponentPrivate::get(delegate);
+ QQmlPropertyCache::ConstPtr rootPropertyCache;
+
+ QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
+ auto baseContext = creationContext ? creationContext : qmlContext(q);
+ // if we need to insert a context property, we need a separate context
+ QQmlContext *context = reuseExistingContext ? baseContext : new QQmlContext(baseContext);
QObject *nobj = delegate->beginCreate(context);
if (nobj) {
if (delegatePriv->hadTopLevelRequiredProperties()) {
delegate->setInitialProperties(nobj, {{QLatin1String("section"), section}});
- } else {
+ } else if (!reuseExistingContext) {
context->setContextProperty(QLatin1String("section"), section);
}
- QQml_setParent_noEvent(context, nobj);
+ if (!reuseExistingContext)
+ QQml_setParent_noEvent(context, nobj);
sectionItem = qobject_cast<QQuickItem *>(nobj);
if (!sectionItem) {
delete nobj;
@@ -1086,12 +1112,15 @@ QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
// sections are not controlled by FxListItemSG, so apply attached properties here
QQuickItemViewAttached *attached = static_cast<QQuickItemViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(sectionItem));
attached->setView(q);
- } else {
+ } else if (!reuseExistingContext) {
delete context;
}
sectionCriteria->delegate()->completeCreate();
}
+ if (sectionItem)
+ QQuickItemPrivate::get(sectionItem)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
+
return sectionItem;
}
@@ -1100,6 +1129,9 @@ void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
if (!item)
return;
int i = 0;
+
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+
do {
if (!sectionCache[i]) {
sectionCache[i] = item;
@@ -1439,26 +1471,26 @@ void QQuickListViewPrivate::updateFooter()
FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
if (footerPositioning == QQuickListView::OverlayFooter) {
- listItem->setPosition(isContentFlowReversed() ? -position() - footerSize() : position() + size() - footerSize());
+ listItem->setPosition(isContentFlowReversed() ? -position() - footerSize() : position() + size() - footerSize(), false, false);
} else if (visibleItems.size()) {
if (footerPositioning == QQuickListView::PullBackFooter) {
qreal viewPos = isContentFlowReversed() ? -position() : position() + size();
// using qBound() would throw an assert here, because max < min is a valid case
// here, if the list's delegates do not fill the whole view
qreal clampedPos = qMax(originPosition() - footerSize() + size(), qMin(listItem->position(), lastPosition()));
- listItem->setPosition(qBound(viewPos - footerSize(), clampedPos, viewPos));
+ listItem->setPosition(qBound(viewPos - footerSize(), clampedPos, viewPos), false, false);
} else {
qreal endPos = lastPosition();
if (findLastVisibleIndex() == model->count()-1) {
- listItem->setPosition(endPos);
+ listItem->setPosition(endPos, false, false);
} else {
qreal visiblePos = position() + q->height();
if (endPos <= visiblePos || listItem->position() < endPos)
- listItem->setPosition(endPos);
+ listItem->setPosition(endPos, false, false);
}
}
} else {
- listItem->setPosition(visiblePos);
+ listItem->setPosition(visiblePos, false, false);
}
if (created)
@@ -1505,7 +1537,7 @@ void QQuickListViewPrivate::updateHeader()
FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
if (headerPositioning == QQuickListView::OverlayHeader) {
- listItem->setPosition(isContentFlowReversed() ? -position() - size() : position());
+ listItem->setPosition(isContentFlowReversed() ? -position() - size() : position(), false, false);
} else if (visibleItems.size()) {
const bool fixingUp = (orient == QQuickListView::Vertical ? vData : hData).fixingUp;
if (headerPositioning == QQuickListView::PullBackHeader) {
@@ -1517,18 +1549,18 @@ void QQuickListViewPrivate::updateHeader()
// using qBound() would throw an assert here, because max < min is a valid case
// here, if the list's delegates do not fill the whole view
qreal clampedPos = qMax(originPosition() - headerSize(), qMin(headerPosition, lastPosition() - size()));
- listItem->setPosition(qBound(viewPos - headerSize(), clampedPos, viewPos));
+ listItem->setPosition(qBound(viewPos - headerSize(), clampedPos, viewPos), false, false);
} else {
qreal startPos = originPosition();
if (visibleIndex == 0) {
- listItem->setPosition(startPos - headerSize());
+ listItem->setPosition(startPos - headerSize(), false, false);
} else {
if (position() <= startPos || listItem->position() > startPos - headerSize())
- listItem->setPosition(startPos - headerSize());
+ listItem->setPosition(startPos - headerSize(), false, false);
}
}
} else {
- listItem->setPosition(-headerSize());
+ listItem->setPosition(-headerSize(), false, false);
}
if (created)
@@ -1579,8 +1611,10 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry
// position all subsequent items
if (visibleItems.size() && item == visibleItems.constFirst()->item) {
FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.constFirst());
+#if QT_CONFIG(quick_viewtransitions)
if (listItem->transitionScheduledOrRunning())
return;
+#endif
if (orient == QQuickListView::Vertical) {
const qreal oldItemEndPosition = verticalLayoutDirection == QQuickItemView::BottomToTop ? -oldGeometry.y() : oldGeometry.y() + oldGeometry.height();
const qreal heightDiff = item->height() - oldGeometry.height();
@@ -1819,13 +1853,13 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
}
bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
+ QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity)
{
data.fixingUp = false;
moveReason = Mouse;
if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
correctFlick = true;
- return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
+ return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, eventType, velocity);
}
qreal maxDistance = 0;
const qreal dataValue =
@@ -1881,7 +1915,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
}
if (!hData.flicking && !vData.flicking) {
// the initial flick - estimate boundary
- qreal accel = deceleration;
+ qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
qreal v2 = v * v;
overshootDist = 0.0;
// + averageSize/4 to encourage moving at least one item in the flick direction
@@ -1946,7 +1980,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
else if (velocity > 0 && newtarget >= minExtent)
newtarget = minExtent + overshootDist;
if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
- if (qAbs(velocity) < MinimumFlickVelocity)
+ if (qAbs(velocity) < _q_MinimumFlickVelocity)
correctFlick = false;
return false;
}
@@ -1973,7 +2007,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
void QQuickListViewPrivate::setSectionHelper(QQmlContext *context, QQuickItem *sectionItem, const QString &section)
{
- if (context->contextProperty(QLatin1String("section")).isValid())
+ if (!QQmlContextData::get(context)->isInternal() && context->contextProperty(QLatin1String("section")).isValid())
context->setContextProperty(QLatin1String("section"), section);
else
sectionItem->setProperty("section", section);
@@ -2004,6 +2038,11 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
ListView are laid out horizontally or vertically. List views are inherently
flickable because ListView inherits from \l Flickable.
+ \note ListView will only load as many delegate items as needed to fill up the view.
+ Items outside of the view will not be loaded unless a sufficient \l cacheBuffer has
+ been set. Hence, a ListView with zero width or height might not load any delegate
+ items at all.
+
\section1 Example Usage
The following example shows the definition of a simple list model defined
@@ -2155,7 +2194,7 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
of type \l [QML] {real}, so it is possible to set fractional
values like \c 0.1.
- \section1 Reusing items
+ \section1 Reusing Items
Since 5.15, ListView can be configured to recycle items instead of instantiating
from the \l delegate whenever new rows are flicked into view. This approach improves
@@ -2183,12 +2222,28 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
\note While an item is in the pool, it might still be alive and respond
to connected signals and bindings.
+ \note For an item to be pooled, it needs to be completely flicked out of the bounds
+ of the view, \e including the extra margins set with \l {ListView::}{cacheBuffer}.
+ Some items will also never be pooled or reused, such as \l currentItem.
+
The following example shows a delegate that animates a spinning rectangle. When
it is pooled, the animation is temporarily paused:
\snippet qml/listview/ReusableDelegate.qml 0
\sa {QML Data Models}, GridView, PathView, {Qt Quick Examples - Views}
+
+ \section1 Variable Delegate Size and Section Labels
+
+ Variable delegate sizes might lead to resizing and skipping of any attached
+ \l {ScrollBar}. This is because ListView estimates its content size from
+ allocated items (usually only the visible items, the rest are assumed to be of
+ similar size), and variable delegate sizes prevent an accurate estimation. To
+ reduce this effect, \l {ListView::}{cacheBuffer} can be set to higher values,
+ effectively creating more items and improving the size estimate of unallocated
+ items, at the expense of additional memory usage. \l{ListView::section}{Sections}
+ have the same effect because they attach and elongate the section label to the
+ first item within the section.
*/
QQuickListView::QQuickListView(QQuickItem *parent)
: QQuickItemView(*(new QQuickListViewPrivate), parent)
@@ -2201,6 +2256,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty bool QtQuick::ListView::isCurrentItem
+ \readonly
+
This attached property is true if this delegate is the current item; otherwise false.
It is attached to each instance of the delegate.
@@ -2212,6 +2269,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty ListView QtQuick::ListView::view
+ \readonly
+
This attached property holds the view that manages this delegate instance.
It is attached to each instance of the delegate and also to the header, the footer,
@@ -2220,6 +2279,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty string QtQuick::ListView::previousSection
+ \readonly
+
This attached property holds the section of the previous element.
It is attached to each instance of the delegate.
@@ -2229,6 +2290,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty string QtQuick::ListView::nextSection
+ \readonly
+
This attached property holds the section of the next element.
It is attached to each instance of the delegate.
@@ -2238,6 +2301,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty string QtQuick::ListView::section
+ \readonly
+
This attached property holds the section of this element.
It is attached to each instance of the delegate.
@@ -2346,7 +2411,7 @@ QQuickListView::~QQuickListView()
/*!
\qmlproperty int QtQuick::ListView::count
- This property holds the number of items in the view.
+ This property holds the number of items in the model.
*/
/*!
@@ -2449,15 +2514,13 @@ QQuickListView::~QQuickListView()
Valid values for \c highlightRangeMode are:
- \list
- \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
- However, the highlight can move outside of the range at the ends of the list or due
- to mouse interaction.
- \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
- The current item changes if a keyboard or mouse action would cause the highlight to move
- outside of the range.
- \li ListView.NoHighlightRange - this is the default value.
- \endlist
+ \value ListView.ApplyRange the view attempts to maintain the highlight within the range.
+ However, the highlight can move outside of the range at the
+ ends of the list or due to mouse interaction.
+ \value ListView.StrictlyEnforceRange the highlight never moves outside of the range.
+ The current item changes if a keyboard or mouse action would
+ cause the highlight to move outside of the range.
+ \value ListView.NoHighlightRange this is the default value.
*/
void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
{
@@ -2504,20 +2567,12 @@ void QQuickListView::setSpacing(qreal spacing)
Possible values:
- \list
- \li ListView.Horizontal - Items are laid out horizontally
- \li ListView.Vertical (default) - Items are laid out vertically
- \endlist
-
- \table
- \row
- \li Horizontal orientation:
- \image ListViewHorizontal.png
-
- \row
- \li Vertical orientation:
- \image listview-highlight.png
- \endtable
+ \value ListView.Horizontal Items are laid out horizontally
+ \br
+ \inlineimage ListViewHorizontal.png
+ \value ListView.Vertical (default) Items are laid out vertically
+ \br
+ \inlineimage listview-highlight.png
\sa {Flickable Direction}
*/
@@ -2558,10 +2613,8 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items will be laid out from left to right.
- \li Qt.RightToLeft - Items will be laid out from right to left.
- \endlist
+ \value Qt.LeftToRight (default) Items will be laid out from left to right.
+ \value Qt.RightToLeft Items will be laid out from right to left.
Setting this property has no effect if the \l orientation is Qt.Vertical.
@@ -2587,10 +2640,8 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
Possible values:
- \list
- \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
- \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
- \endlist
+ \value ListView.TopToBottom (default) Items are laid out from the top of the view down to the bottom of the view.
+ \value ListView.BottomToTop Items are laid out from the bottom of the view up to the top of the view.
Setting this property has no effect if the \l orientation is Qt.Horizontal.
@@ -2698,35 +2749,35 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
\c section.criteria holds the criteria for forming each section based on
\c section.property. This value can be one of:
- \list
- \li ViewSection.FullString (default) - sections are created based on the
- \c section.property value.
- \li ViewSection.FirstCharacter - sections are created based on the first
- character of the \c section.property value (for example, 'A', 'B', 'C'
- sections, etc. for an address book)
- \endlist
+ \value ViewSection.FullString (default) sections are created based on the
+ \c section.property value.
+ \value ViewSection.FirstCharacter sections are created based on the first character of
+ the \c section.property value (for example,
+ 'A', 'B', 'C' ... sections for an address book.)
A case insensitive comparison is used when determining section
boundaries.
\c section.delegate holds the delegate component for each section. The
default \l {QQuickItem::z}{stacking order} of section delegate instances
- is \c 2.
+ is \c 2. If you declare a \c required property named "section" in it,
+ that property will contain the section's title.
\c section.labelPositioning determines whether the current and/or
next section labels stick to the start/end of the view, and whether
the labels are shown inline. This value can be a combination of:
- \list
- \li ViewSection.InlineLabels - section labels are shown inline between
- the item delegates separating sections (default).
- \li ViewSection.CurrentLabelAtStart - the current section label sticks to the
- start of the view as it is moved.
- \li ViewSection.NextLabelAtEnd - the next section label (beyond all visible
- sections) sticks to the end of the view as it is moved. \note Enabling
- \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
- section, which has performance implications, especially for slower models.
- \endlist
+ \value ViewSection.InlineLabels
+ (default) section labels are shown inline between the item delegates
+ separating sections.
+ \value ViewSection.CurrentLabelAtStart
+ the current section label sticks to the start of the view as it is moved.
+ \value ViewSection.NextLabelAtEnd
+ the next section label (beyond all visible sections) sticks to the end
+ of the view as it is moved.
+ \note Enabling \c ViewSection.NextLabelAtEnd requires the view to scan
+ ahead for the next section, which has performance implications,
+ especially for slower models.
Each item in the list has attached properties named \c ListView.section,
\c ListView.previousSection and \c ListView.nextSection.
@@ -2877,18 +2928,14 @@ void QQuickListView::setHighlightResizeDuration(int duration)
This property determines how the view scrolling will settle following a drag or flick.
The possible values are:
- \list
- \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
- \li ListView.SnapToItem - the view settles with an item aligned with the start of
- the view.
- \li ListView.SnapOneItem - the view settles no more than one item away from the first
- visible item at the time the mouse button is released. This mode is particularly
- useful for moving one page at a time. When SnapOneItem is enabled, the ListView will
- show a stronger affinity to neighboring items when movement occurs. For example, a
- short drag that snaps back to the current item with SnapToItem might snap to a
- neighboring item with SnapOneItem.
-
- \endlist
+ \value ListView.NoSnap (default) the view stops anywhere within the visible area.
+ \value ListView.SnapToItem the view settles with an item aligned with the start of the view.
+ \value ListView.SnapOneItem the view settles no more than one item away from the first
+ visible item at the time the mouse button is released. This mode is particularly
+ useful for moving one page at a time. When SnapOneItem is enabled, the ListView will
+ show a stronger affinity to neighboring items when movement occurs. For example, a
+ short drag that snaps back to the current item with SnapToItem might snap to a
+ neighboring item with SnapOneItem.
\c snapMode does not affect the \l currentIndex. To update the
\l currentIndex as the list is moved, set \l highlightRangeMode
@@ -3431,12 +3478,12 @@ void QQuickListView::viewportMoved(Qt::Orientations orient)
const qreal minY = minYExtent();
if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
&& minY != d->vData.flickTarget)
- d->flickY(-d->vData.smoothVelocity.value());
+ d->flickY(QEvent::TouchUpdate, -d->vData.smoothVelocity.value());
} else if (d->vData.velocity < 0) {
const qreal maxY = maxYExtent();
if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
&& maxY != d->vData.flickTarget)
- d->flickY(-d->vData.smoothVelocity.value());
+ d->flickY(QEvent::TouchUpdate, -d->vData.smoothVelocity.value());
}
}
@@ -3445,12 +3492,12 @@ void QQuickListView::viewportMoved(Qt::Orientations orient)
const qreal minX = minXExtent();
if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
&& minX != d->hData.flickTarget)
- d->flickX(-d->hData.smoothVelocity.value());
+ d->flickX(QEvent::TouchUpdate, -d->hData.smoothVelocity.value());
} else if (d->hData.velocity < 0) {
const qreal maxX = maxXExtent();
if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
&& maxX != d->hData.flickTarget)
- d->flickX(-d->hData.smoothVelocity.value());
+ d->flickX(QEvent::TouchUpdate, -d->hData.smoothVelocity.value());
}
}
d->inFlickCorrection = false;
@@ -3610,9 +3657,16 @@ void QQuickListViewPrivate::updateSectionCriteria()
bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
{
+ Q_Q(QQuickListView);
+#if QT_CONFIG(quick_viewtransitions)
+ Q_UNUSED(movingIntoView)
+#endif
int modelIndex = change.index;
int count = change.count;
+ if (q->size().isEmpty() && visibleItems.isEmpty())
+ return false;
+
qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
int index = visibleItems.size() ? mapFromModel(modelIndex) : 0;
qreal lastVisiblePos = buffer + displayMarginEnd + tempPos + size();
@@ -3652,10 +3706,12 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
for (FxViewItem *item : std::as_const(visibleItems)) {
if (item->index != -1 && item->index >= modelIndex) {
item->index += count;
+#if QT_CONFIG(quick_viewtransitions)
if (change.isMove())
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
else
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
+#endif
}
}
@@ -3690,9 +3746,11 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
insertResult->changedFirstItem = true;
if (!change.isMove()) {
addedItems->append(item);
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
else
+#endif
static_cast<FxListItemSG *>(item)->setPosition(pos, true);
}
insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
@@ -3721,7 +3779,9 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
FxViewItem *item = nullptr;
if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
item->index = it.index;
+#if QT_CONFIG(quick_viewtransitions)
bool newItem = !item;
+#endif
it.removedAtIndex = false;
if (!item)
item = createItem(it.index, QQmlIncubator::Synchronous);
@@ -3738,13 +3798,17 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
if (change.isMove()) {
// we know this is a move target, since move displaced items that are
// shuffled into view due to a move would be added in refill()
+#if QT_CONFIG(quick_viewtransitions)
if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
+#endif
} else {
addedItems->append(item);
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
else
+#endif
static_cast<FxListItemSG *>(item)->setPosition(pos, true);
}
insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
@@ -3758,13 +3822,17 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
FxViewItem *item = visibleItems.at(index);
if (prevItem->index != item->index - 1) {
int i = index;
+#if QT_CONFIG(quick_viewtransitions)
qreal prevPos = prevItem->position();
+#endif
while (i < visibleItems.size()) {
FxListItemSG *nvItem = static_cast<FxListItemSG *>(visibleItems.takeLast());
insertResult->sizeChangesAfterVisiblePos -= nvItem->size() + spacing;
addedItems->removeOne(nvItem);
+#if QT_CONFIG(quick_viewtransitions)
if (nvItem->transitionScheduledOrRunning())
nvItem->setPosition(prevPos + (nvItem->index - prevItem->index) * averageSize);
+#endif
removeItem(nvItem);
}
}
@@ -3776,6 +3844,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
return visibleAffected;
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
{
Q_UNUSED(insertionResult);
@@ -3809,25 +3878,22 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
}
}
}
+#endif
/*!
\qmlmethod QtQuick::ListView::positionViewAtIndex(int index, PositionMode mode)
- Positions the view such that the \a index is at the position specified by
- \a mode:
-
- \list
- \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
- \li ListView.Center - position item in the center of the view.
- \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
- \li ListView.Visible - if any part of the item is visible then take no action, otherwise
- bring the item into view.
- \li ListView.Contain - ensure the entire item is visible. If the item is larger than
- the view the item is positioned at the top (or left for horizontal orientation) of the view.
- \li ListView.SnapPosition - position the item at \l preferredHighlightBegin. This mode
- is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
- via \l snapMode.
- \endlist
+ Positions the view such that the \a index is at the position specified by \a mode:
+
+ \value ListView.Beginning position item at the top (or left for horizontal orientation) of the view.
+ \value ListView.Center position item in the center of the view.
+ \value ListView.End position item at bottom (or right for horizontal orientation) of the view.
+ \value ListView.Visible if any part of the item is visible then take no action, otherwise
+ bring the item into view.
+ \value ListView.Contain ensure the entire item is visible. If the item is larger than the view,
+ the item is positioned at the top (or left for horizontal orientation) of the view.
+ \value ListView.SnapPosition position the item at \l preferredHighlightBegin. This mode is only valid
+ if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled via \l snapMode.
If positioning the view at \a index would cause empty space to be displayed at
the beginning or end of the view, the view will be positioned at the boundary.
diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h
index e4719b5d36..4651bc689f 100644
--- a/src/quick/items/qquicklistview_p.h
+++ b/src/quick/items/qquicklistview_p.h
@@ -23,11 +23,13 @@ QT_REQUIRE_CONFIG(quick_listview);
#include <private/qtquickglobal_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickListView;
class QQuickListViewPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickViewSection : public QObject
+class Q_QUICK_EXPORT QQuickViewSection : public QObject
{
Q_OBJECT
Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged)
@@ -75,7 +77,7 @@ private:
class QQmlInstanceModel;
class QQuickListViewAttached;
-class Q_QUICK_PRIVATE_EXPORT QQuickListView : public QQuickItemView
+class Q_QUICK_EXPORT QQuickListView : public QQuickItemView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickListView)
@@ -169,7 +171,7 @@ protected:
qreal maxXExtent() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickListViewAttached : public QQuickItemViewAttached
+class Q_QUICK_EXPORT QQuickListViewAttached : public QQuickItemViewAttached
{
Q_OBJECT
@@ -185,7 +187,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickListView)
-QML_DECLARE_TYPE(QQuickViewSection)
-
#endif // QQUICKLISTVIEW_P_H
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index da04a2956c..15cf20ea7a 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -161,9 +161,7 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\section2 Loader Sizing Behavior
- If the source component is not an Item type, Loader does not
- apply any special sizing rules. When used to load visual types,
- Loader applies the following sizing rules:
+ When used to load visual types, Loader applies the following sizing rules:
\list
\li If an explicit size is not specified for the Loader, the Loader
@@ -190,6 +188,8 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\li The red rectangle will be 50x50, centered in the root item.
\endtable
+ If the source component is not an Item type, Loader does not apply any
+ special sizing rules.
\section2 Receiving Signals from Loaded Objects
@@ -673,13 +673,6 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
if (status == QQmlIncubator::Ready) {
object = incubator->object();
item = qmlobject_cast<QQuickItem*>(object);
- if (!item) {
- QQuickWindow *window = qmlobject_cast<QQuickWindow*>(object);
- if (window) {
- qCDebug(lcTransient) << window << "is transient for" << q->window();
- window->setTransientParent(q->window());
- }
- }
emit q->itemChanged();
initResize();
incubator->clear();
@@ -795,7 +788,7 @@ void QQuickLoader::componentComplete()
{
Q_D(QQuickLoader);
QQuickItem::componentComplete();
- if (active()) {
+ if (active() && (status() != Ready)) {
if (d->loadingFromSource)
d->createComponent();
d->load();
@@ -804,12 +797,15 @@ void QQuickLoader::componentComplete()
void QQuickLoader::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
{
- if (change == ItemSceneChange) {
- QQuickWindow *loadedWindow = qmlobject_cast<QQuickWindow *>(item());
- if (loadedWindow) {
- qCDebug(lcTransient) << loadedWindow << "is transient for" << value.window;
- loadedWindow->setTransientParent(value.window);
- }
+ switch (change) {
+ case ItemChildAddedChange:
+ Q_ASSERT(value.item);
+ if (value.item->flags().testFlag(QQuickItem::ItemObservesViewport))
+ // Re-trigger the parent traversal to get subtreeTransformChangedEnabled turned on
+ value.item->setFlag(QQuickItem::ItemObservesViewport);
+ break;
+ default:
+ break;
}
QQuickItem::itemChange(change, value);
}
@@ -915,12 +911,24 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
const bool needToUpdateWidth = loaderGeometryChanged && q->widthValid();
const bool needToUpdateHeight = loaderGeometryChanged && q->heightValid();
- if (needToUpdateWidth && needToUpdateHeight)
+ if (needToUpdateWidth && needToUpdateHeight) {
+ /* setSize keeps bindings intact (for backwards compatibility reasons),
+ but here we actually want the loader to control the size, so any
+ prexisting bindings ought to be removed
+ */
+ auto *itemPriv = QQuickItemPrivate::get(item);
+ // takeBinding would work without the check, but this is more efficient
+ // for the common case where we don't have a binding
+ if (itemPriv->width.hasBinding())
+ itemPriv->width.takeBinding();
+ if (itemPriv->height.hasBinding())
+ itemPriv->height.takeBinding();
item->setSize(QSizeF(q->width(), q->height()));
- else if (needToUpdateWidth)
+ } else if (needToUpdateWidth) {
item->setWidth(q->width());
- else if (needToUpdateHeight)
+ } else if (needToUpdateHeight) {
item->setHeight(q->height());
+ }
if (updatingSize)
return;
diff --git a/src/quick/items/qquickloader_p.h b/src/quick/items/qquickloader_p.h
index f8c65513d5..c3d2015aed 100644
--- a/src/quick/items/qquickloader_p.h
+++ b/src/quick/items/qquickloader_p.h
@@ -20,8 +20,7 @@
QT_BEGIN_NAMESPACE
class QQuickLoaderPrivate;
-class QQmlV4Function;
-class Q_QUICK_PRIVATE_EXPORT QQuickLoader : public QQuickImplicitSizeItem
+class Q_QUICK_EXPORT QQuickLoader : public QQuickImplicitSizeItem
{
Q_OBJECT
@@ -90,6 +89,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLoader)
-
#endif // QQUICKLOADER_P_H
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 43257e0c84..39011f1162 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -27,7 +27,6 @@ QT_BEGIN_NAMESPACE
class QQuickLoaderPrivate;
-class QQmlV4Function;
class QQuickLoaderIncubator : public QQmlIncubator
{
public:
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index dc44deca38..a6edfc4754 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
+DEFINE_BOOL_CONFIG_OPTION(qmlMaVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
@@ -53,7 +53,7 @@ void QQuickMouseAreaPrivate::init()
q->setAcceptedMouseButtons(Qt::LeftButton);
q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch
q->setFiltersChildMouseEvents(true);
- if (qmlVisualTouchDebugging()) {
+ if (qmlMaVisualTouchDebugging()) {
q->setFlag(QQuickItem::ItemHasContents);
}
}
@@ -743,7 +743,9 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
QQuickMouseEvent &me = d->quickMouseEvent;
me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress, event->flags());
+#if QT_DEPRECATED_SINCE(6, 6)
me.setSource(event->source());
+#endif
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
@@ -788,13 +790,18 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event)
QQuickMouseEvent &me = d->quickMouseEvent;
me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true,
false, event->flags());
+#if QT_DEPRECATED_SINCE(6, 6)
me.setSource(event->source());
+#endif
me.setAccepted(d->isDoubleClickConnected());
emit this->doubleClicked(&me);
if (!me.isAccepted())
d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick);
if (d->pressed)
d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
+
+ // Do not call the base implementation: we don't want to call event->ignore().
+ return;
}
QQuickItem::mouseDoubleClickEvent(event);
}
@@ -902,6 +909,7 @@ void QQuickMouseArea::ungrabMouse()
emit pressedButtonsChanged();
if (d->hovered && !isUnderMouse()) {
+ qCDebug(lcHoverTrace) << "losing hover: not under the mouse";
d->hovered = false;
emit hoveredChanged();
}
@@ -966,6 +974,7 @@ bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event)
emit pressedChanged();
emit containsPressChanged();
if (d->hovered) {
+ qCDebug(lcHoverTrace) << "losing hover: button released";
d->hovered = false;
emit hoveredChanged();
}
@@ -1012,7 +1021,9 @@ void QQuickMouseArea::timerEvent(QTimerEvent *event)
d->longPress = true;
QQuickMouseEvent &me = d->quickMouseEvent;
me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress, d->lastFlags);
+#if QT_DEPRECATED_SINCE(6, 6)
me.setSource(Qt::MouseEventSynthesizedByQt);
+#endif
me.setAccepted(d->isPressAndHoldConnected());
emit pressAndHold(&me);
if (!me.isAccepted())
@@ -1028,7 +1039,7 @@ void QQuickMouseArea::geometryChange(const QRectF &newGeometry, const QRectF &ol
Q_D(QQuickMouseArea);
QQuickItem::geometryChange(newGeometry, oldGeometry);
- if (d->lastScenePos.isNull)
+ if (!d->lastScenePos.isValid())
d->lastScenePos = mapToScene(d->lastPos);
else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y())
d->lastPos = mapFromScene(d->lastScenePos);
@@ -1130,8 +1141,10 @@ void QQuickMouseArea::setHoverEnabled(bool h)
\qmlproperty bool QtQuick::MouseArea::containsMouse
This property holds whether the mouse is currently inside the mouse area.
- \warning If hoverEnabled is false, containsMouse will only be valid
+ \warning If hoverEnabled is \c false, \c containsMouse will be \c true
when the mouse is pressed while the mouse cursor is inside the MouseArea.
+ But if you set \c {mouse.accepted = false} in an \c onPressed handler,
+ \c containsMouse will remain \c false because the press was rejected.
*/
bool QQuickMouseArea::hovered() const
{
@@ -1229,7 +1242,9 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
if (wasPressed != p) {
QQuickMouseEvent &me = d->quickMouseEvent;
me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress, d->lastFlags);
+#if QT_DEPRECATED_SINCE(6, 6)
me.setSource(source);
+#endif
if (p) {
d->pressed |= button;
if (!d->doubleClick)
@@ -1241,6 +1256,8 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
if (!me.isAccepted()) {
d->pressed = Qt::NoButton;
+ if (!hoverEnabled())
+ setHovered(false);
}
if (!oldPressed) {
@@ -1267,6 +1284,7 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
return me.isAccepted();
}
+ Q_UNUSED(source)
return false;
}
@@ -1438,7 +1456,7 @@ QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
Q_UNUSED(data);
Q_D(QQuickMouseArea);
- if (!qmlVisualTouchDebugging())
+ if (!qmlMaVisualTouchDebugging())
return nullptr;
QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
diff --git a/src/quick/items/qquickmousearea_p.h b/src/quick/items/qquickmousearea_p.h
index 9ce57bcf60..96e60002b5 100644
--- a/src/quick/items/qquickmousearea_p.h
+++ b/src/quick/items/qquickmousearea_p.h
@@ -26,7 +26,7 @@ class QQuickDrag;
class QQuickMouseAreaPrivate;
class QQuickWheelEvent;
// used in Qt Location
-class Q_QUICK_PRIVATE_EXPORT QQuickMouseArea : public QQuickItem
+class Q_QUICK_EXPORT QQuickMouseArea : public QQuickItem
{
Q_OBJECT
@@ -163,6 +163,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMouseArea)
-
#endif // QQUICKMOUSEAREA_P_H
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index fc994fd18c..b0b99334a2 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -17,7 +17,7 @@
QT_BEGIN_NAMESPACE
-DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
+DEFINE_BOOL_CONFIG_OPTION(qmlMptaVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
/*!
\qmltype TouchPoint
@@ -329,18 +329,39 @@ void QQuickTouchPoint::setUniqueId(const QPointingDeviceUniqueId &id)
If minimumTouchPoints is set to a value greater than one, this signal will not be emitted until the minimum number
of required touch points has been reached.
+
+ \note If you use the \c touchPoints argument in your signal handler code,
+ it's best to rename it in your formal parameter to avoid confusion with the
+ \c touchPoints property (see \l{QML Coding Conventions}):
+ \qml
+ onPressed: (points) => console.log("pressed", points.length)
+ \endqml
*/
/*!
\qmlsignal QtQuick::MultiPointTouchArea::updated(list<TouchPoint> touchPoints)
This signal is emitted when existing touch points are updated. \a touchPoints is a list of these updated points.
+
+ \note If you use the \c touchPoints argument in your signal handler code,
+ it's best to rename it in your formal parameter to avoid confusion with the
+ \c touchPoints property (see \l{QML Coding Conventions}):
+ \qml
+ onUpdated: (points) => console.log("updated", points.length)
+ \endqml
*/
/*!
\qmlsignal QtQuick::MultiPointTouchArea::released(list<TouchPoint> touchPoints)
This signal is emitted when existing touch points are removed. \a touchPoints is a list of these removed points.
+
+ \note If you use the \c touchPoints argument in your signal handler code,
+ it's best to rename it in your formal parameter to avoid confusion with the
+ \c touchPoints property (see \l{QML Coding Conventions}):
+ \qml
+ onReleased: (points) => console.log("released", points.length)
+ \endqml
*/
/*!
@@ -356,8 +377,17 @@ void QQuickTouchPoint::setUniqueId(const QPointingDeviceUniqueId &id)
\c canceled should be handled in addition to \l released.
\a touchPoints is the list of canceled points.
+
+ \note If you use the \c touchPoints argument in your signal handler code,
+ it's best to rename it in your formal parameter to avoid confusion with the
+ \c touchPoints property (see \l{QML Coding Conventions}):
+ \qml
+ onCanceled: (points) => console.log("canceled", points.length)
+ \endqml
*/
+// TODO Qt 7: remove the notes above about the signal touchPoints arguments
+
/*!
\qmlsignal QtQuick::MultiPointTouchArea::gestureStarted(GestureEvent gesture)
@@ -407,11 +437,11 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent)
{
setAcceptedMouseButtons(Qt::LeftButton);
setFiltersChildMouseEvents(true);
- if (qmlVisualTouchDebugging()) {
+ if (qmlMptaVisualTouchDebugging()) {
setFlag(QQuickItem::ItemHasContents);
}
setAcceptTouchEvents(true);
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
setAcceptHoverEvents(true); // needed to enable touch events on mouse hover.
#endif
}
@@ -596,6 +626,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event, RemapEventPoints
}
if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) {
for (QEventPoint &p : touchPoints) {
+ QPointF oldPos = p.position();
+ auto transformBack = qScopeGuard([&] { QMutableEventPoint::setPosition(p, oldPos); });
if (touchPointsFromEvent && remap == RemapEventPoints::ToLocal)
QMutableEventPoint::setPosition(p, mapFromScene(p.scenePosition()));
QEventPoint::State touchPointState = p.state();
@@ -715,17 +747,17 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QMouseEvent *e)
_mouseTouchPoint = dtp;
}
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
void QQuickMultiPointTouchArea::hoverEnterEvent(QHoverEvent *event)
{
- Q_UNUSED(event);
- setTouchEventsEnabled(true);
+ setTouchEventsEnabled(isEnabled());
+ QQuickItem::hoverEnterEvent(event);
}
void QQuickMultiPointTouchArea::hoverLeaveEvent(QHoverEvent *event)
{
- Q_UNUSED(event);
setTouchEventsEnabled(false);
+ QQuickItem::hoverLeaveEvent(event);
}
void QQuickMultiPointTouchArea::setTouchEventsEnabled(bool enable)
@@ -739,7 +771,14 @@ void QQuickMultiPointTouchArea::setTouchEventsEnabled(bool enable)
registerTouchWindow(window(), enable);
}
-#endif // Q_OS_OSX
+
+void QQuickMultiPointTouchArea::itemChange(ItemChange change, const ItemChangeData &data)
+{
+ if (change == ItemEnabledHasChanged)
+ setAcceptHoverEvents(data.boolValue);
+ QQuickItem::itemChange(change, data);
+}
+#endif // Q_OS_MACOS
void QQuickMultiPointTouchArea::addTouchPrototype(QQuickTouchPoint *prototype)
{
@@ -1000,7 +1039,7 @@ QSGNode *QQuickMultiPointTouchArea::updatePaintNode(QSGNode *oldNode, UpdatePain
{
Q_UNUSED(data);
- if (!qmlVisualTouchDebugging())
+ if (!qmlMptaVisualTouchDebugging())
return nullptr;
QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h
index f92705f28e..51ddb1645c 100644
--- a/src/quick/items/qquickmultipointtoucharea_p.h
+++ b/src/quick/items/qquickmultipointtoucharea_p.h
@@ -30,7 +30,7 @@
QT_BEGIN_NAMESPACE
class QQuickMultiPointTouchArea;
-class Q_QUICK_PRIVATE_EXPORT QQuickTouchPoint : public QObject
+class Q_QUICK_EXPORT QQuickTouchPoint : public QObject
{
Q_OBJECT
Q_PROPERTY(int pointId READ pointId NOTIFY pointIdChanged)
@@ -152,8 +152,8 @@ private:
class QQuickGrabGestureEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQmlListProperty<QObject> touchPoints READ touchPoints CONSTANT)
- Q_PROPERTY(qreal dragThreshold READ dragThreshold CONSTANT)
+ Q_PROPERTY(QQmlListProperty<QObject> touchPoints READ touchPoints CONSTANT FINAL)
+ Q_PROPERTY(qreal dragThreshold READ dragThreshold CONSTANT FINAL)
QML_NAMED_ELEMENT(GestureEvent)
QML_ADDED_IN_VERSION(2, 0)
QML_UNCREATABLE("GestureEvent is only available in the context of handling the gestureStarted signal from MultiPointTouchArea.")
@@ -176,12 +176,12 @@ private:
QList<QObject*> _touchPoints;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointTouchArea : public QQuickItem
+class Q_QUICK_EXPORT QQuickMultiPointTouchArea : public QQuickItem
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QQuickMultiPointTouchArea)
- Q_PROPERTY(QQmlListProperty<QQuickTouchPoint> touchPoints READ touchPoints)
+ Q_PROPERTY(QQmlListProperty<QQuickTouchPoint> touchPoints READ touchPoints CONSTANT)
Q_PROPERTY(int minimumTouchPoints READ minimumTouchPoints WRITE setMinimumTouchPoints NOTIFY minimumTouchPointsChanged)
Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints WRITE setMaximumTouchPoints NOTIFY maximumTouchPointsChanged)
Q_PROPERTY(bool mouseEnabled READ mouseEnabled WRITE setMouseEnabled NOTIFY mouseEnabledChanged)
@@ -219,12 +219,23 @@ public:
}
Q_SIGNALS:
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
void pressed(const QList<QObject*> &touchPoints);
void updated(const QList<QObject*> &touchPoints);
void released(const QList<QObject*> &touchPoints);
void canceled(const QList<QObject*> &touchPoints);
+#else
+ void pressed(const QList<QObject*> &points);
+ void updated(const QList<QObject*> &points);
+ void released(const QList<QObject*> &points);
+ void canceled(const QList<QObject*> &points);
+#endif
void gestureStarted(QQuickGrabGestureEvent *gesture);
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
void touchUpdated(const QList<QObject*> &touchPoints);
+#else
+ void touchUpdated(const QList<QObject*> &points);
+#endif
void minimumTouchPointsChanged();
void maximumTouchPointsChanged();
void mouseEnabledChanged();
@@ -252,10 +263,11 @@ protected:
bool shouldFilter(QEvent *event);
void grabGesture(QPointingDevice *dev);
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
void hoverEnterEvent(QHoverEvent *event) override;
void hoverLeaveEvent(QHoverEvent *event) override;
void setTouchEventsEnabled(bool enable);
+ void itemChange(ItemChange change, const ItemChangeData &data) override;
#endif
private:
@@ -278,8 +290,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTouchPoint)
-QML_DECLARE_TYPE(QQuickGrabGestureEvent)
-QML_DECLARE_TYPE(QQuickMultiPointTouchArea)
-
#endif // QQUICKMULTIPOINTTOUCHAREA_H
diff --git a/src/quick/items/qquickpainteditem.cpp b/src/quick/items/qquickpainteditem.cpp
index d363fbcd31..df4ee6014d 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -8,7 +8,7 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <qsgtextureprovider.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <qmath.h>
@@ -87,7 +87,6 @@ QQuickPaintedItemPrivate::QQuickPaintedItemPrivate()
, fillColor(Qt::transparent)
, renderTarget(QQuickPaintedItem::Image)
, opaquePainting(false)
- , antialiasing(false)
, mipmap(false)
, textureProvider(nullptr)
, node(nullptr)
@@ -177,6 +176,7 @@ void QQuickPaintedItem::setOpaquePainting(bool opaque)
QQuickItem::update();
}
+//### Qt7: remove the aa functions; they shadow the QQuickItem property
/*!
Returns true if antialiased painting is enabled; otherwise, false is returned.
@@ -186,8 +186,7 @@ void QQuickPaintedItem::setOpaquePainting(bool opaque)
*/
bool QQuickPaintedItem::antialiasing() const
{
- Q_D(const QQuickPaintedItem);
- return d->antialiasing;
+ return QQuickItem::antialiasing();
}
/*!
@@ -199,13 +198,7 @@ bool QQuickPaintedItem::antialiasing() const
*/
void QQuickPaintedItem::setAntialiasing(bool enable)
{
- Q_D(QQuickPaintedItem);
-
- if (d->antialiasing == enable)
- return;
-
- d->antialiasing = enable;
- update();
+ QQuickItem::setAntialiasing(enable);
}
/*!
@@ -416,6 +409,10 @@ void QQuickPaintedItem::setContentsScale(qreal scale)
\brief The item's background fill color.
By default, the fill color is set to Qt::transparent.
+
+ Set the fill color to an invalid color (e.g. QColor()) to disable background
+ filling. This may improve performance, and is safe to do if the paint() function
+ draws to all pixels on each frame.
*/
QColor QQuickPaintedItem::fillColor() const
{
@@ -546,7 +543,7 @@ QSGNode *QQuickPaintedItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
node->setPreferredRenderTarget(d->renderTarget);
node->setFastFBOResizing(d->performanceHints & FastFBOResizing);
- node->setSmoothPainting(d->antialiasing);
+ node->setSmoothPainting(antialiasing());
node->setLinearFiltering(d->smooth);
node->setMipmapping(d->mipmap);
node->setOpaquePainting(d->opaquePainting);
diff --git a/src/quick/items/qquickpainteditem_p.h b/src/quick/items/qquickpainteditem_p.h
index fd677f5519..2695d13da4 100644
--- a/src/quick/items/qquickpainteditem_p.h
+++ b/src/quick/items/qquickpainteditem_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickPaintedItemTextureProvider;
class QSGPainterNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickPaintedItemPrivate : public QQuickItemPrivate
+class Q_QUICK_EXPORT QQuickPaintedItemPrivate : public QQuickItemPrivate
{
public:
QQuickPaintedItemPrivate();
@@ -40,7 +40,6 @@ public:
QRect dirtyRect;
bool opaquePainting: 1;
- bool antialiasing: 1;
bool mipmap: 1;
mutable QQuickPaintedItemTextureProvider *textureProvider;
diff --git a/src/quick/items/qquickpalette.cpp b/src/quick/items/qquickpalette.cpp
index 07d1e61b59..79b22bd8a0 100644
--- a/src/quick/items/qquickpalette.cpp
+++ b/src/quick/items/qquickpalette.cpp
@@ -33,7 +33,7 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
\internal
\class QQuickPalette
- \brief The QQuickPalette class contains color groups for each QML item state.
+ \brief Contains color groups for each QML item state.
\inmodule QtQuick
\since 6.0
@@ -48,10 +48,10 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
\inherits QQuickColorGroup
\inqmlmodule QtQuick
\ingroup qtquick-visual
- \brief The QQuickPalette class contains color groups for each QML item state.
+ \brief Contains color groups for each QML item state.
- A palette consists of three color groups: Active, Disabled, and Inactive.
- Active color group is the default group, its colors are used for other groups
+ A palette consists of three color groups: \c active, \c disabled, and \c inactive.
+ The \c active color group is the default group: its colors are used for other groups
if colors of these groups aren't explicitly specified.
In the following example, color is applied for all color groups:
@@ -93,22 +93,23 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
\endcode
It is also possible to specify colors like this:
- \code
- palette {
- buttonText: "azure"
- button: "khaki"
- disabled {
- buttonText: "lavender"
- button: "coral"
- }
- }
- \endcode
- This approach is convenient when you need to specify a whole palette with all color groups.
+ \snippet qtquickcontrols-custom-palette-buttons.qml palette
+
+ This approach is especially convenient when you need to specify a whole
+ palette with all color groups; but as with the other cases above, the
+ colors that are not specified are initialized from SystemPalette, or
+ potentially the \l {Styling Qt Quick Controls}{Qt Quick Controls style},
+ if one is in use.
+
+ \note Some Controls styles use some palette colors, but many styles use
+ independent colors.
+
+ \sa Window::palette, Item::palette, Popup::palette, SystemPalette
*/
/*!
- \qmlproperty QQuickColorGroup QtQuick::Palette::active
+ \qmlproperty ColorGroup QtQuick::Palette::active
The Active group is used for windows that are in focus.
@@ -116,7 +117,7 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
*/
/*!
- \qmlproperty QQuickColorGroup QtQuick::Palette::inactive
+ \qmlproperty ColorGroup QtQuick::Palette::inactive
The Inactive group is used for windows that have no keyboard focus.
@@ -124,7 +125,7 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
*/
/*!
- \qmlproperty QQuickColorGroup QtQuick::Palette::disabled
+ \qmlproperty ColorGroup QtQuick::Palette::disabled
The Disabled group is used for elements that are disabled for some reason.
@@ -152,6 +153,24 @@ QQuickColorGroup *QQuickPalette::disabled() const
return colorGroup(QPalette::Disabled);
}
+void QQuickPalette::resetActive()
+{
+ if (colorProvider().resetColor(QPalette::Active))
+ Q_EMIT changed();
+}
+
+void QQuickPalette::resetInactive()
+{
+ if (colorProvider().resetColor(QPalette::Inactive))
+ Q_EMIT changed();
+}
+
+void QQuickPalette::resetDisabled()
+{
+ if (colorProvider().resetColor(QPalette::Disabled))
+ Q_EMIT changed();
+}
+
/*!
\internal
diff --git a/src/quick/items/qquickpalette_p.h b/src/quick/items/qquickpalette_p.h
index ba93b867cc..3e40cf9036 100644
--- a/src/quick/items/qquickpalette_p.h
+++ b/src/quick/items/qquickpalette_p.h
@@ -16,20 +16,21 @@
#include <QtQuick/private/qquickcolorgroup_p.h>
+#include <QtCore/qpointer.h>
+
#include <array>
QT_BEGIN_NAMESPACE
class QQuickAbstractPaletteProvider;
-class Q_QUICK_PRIVATE_EXPORT QQuickPalette : public QQuickColorGroup
+class Q_QUICK_EXPORT QQuickPalette : public QQuickColorGroup
{
Q_OBJECT
- Q_PROPERTY(QQuickColorGroup *active READ active WRITE setActive NOTIFY activeChanged)
- Q_PROPERTY(QQuickColorGroup *inactive READ inactive WRITE setInactive NOTIFY inactiveChanged)
- Q_PROPERTY(QQuickColorGroup *disabled READ disabled WRITE setDisabled NOTIFY disabledChanged)
-
+ Q_PROPERTY(QQuickColorGroup *active READ active WRITE setActive RESET resetActive NOTIFY activeChanged FINAL)
+ Q_PROPERTY(QQuickColorGroup *inactive READ inactive WRITE setInactive RESET resetInactive NOTIFY inactiveChanged FINAL)
+ Q_PROPERTY(QQuickColorGroup *disabled READ disabled WRITE setDisabled RESET resetDisabled NOTIFY disabledChanged FINAL)
QML_NAMED_ELEMENT(Palette)
QML_ADDED_IN_VERSION(6, 0)
@@ -43,6 +44,9 @@ public:
QQuickColorGroup *active() const;
QQuickColorGroup *inactive() const;
QQuickColorGroup *disabled() const;
+ void resetActive();
+ void resetInactive();
+ void resetDisabled();
QPalette::ColorGroup currentColorGroup() const override;
void setCurrentGroup(QPalette::ColorGroup currentGroup);
@@ -71,6 +75,7 @@ private:
void setColorGroup(QPalette::ColorGroup groupTag,
const QQuickColorGroup::GroupPtr &group,
void (QQuickPalette::*notifier)());
+
QQuickColorGroup::GroupPtr colorGroup(QPalette::ColorGroup groupTag) const;
QQuickColorGroup::GroupPtr findColorGroup(QPalette::ColorGroup groupTag) const;
@@ -88,6 +93,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPalette)
-
#endif // QQUICKPALETTE_H
diff --git a/src/quick/items/qquickpalettecolorprovider.cpp b/src/quick/items/qquickpalettecolorprovider.cpp
index 2d36ff01c5..af6f0ef7a5 100644
--- a/src/quick/items/qquickpalettecolorprovider.cpp
+++ b/src/quick/items/qquickpalettecolorprovider.cpp
@@ -3,6 +3,7 @@
#include "qquickpalettecolorprovider_p.h"
#include <QtQuick/private/qquickabstractpaletteprovider_p.h>
+#include <QtGui/private/qpalette_p.h>
QT_BEGIN_NAMESPACE
@@ -44,10 +45,55 @@ bool QQuickPaletteColorProvider::setColor(QPalette::ColorGroup g, QPalette::Colo
bool QQuickPaletteColorProvider::resetColor(QPalette::ColorGroup group, QPalette::ColorRole role)
{
- const auto &defaultPalette = paletteProvider()->defaultPalette() ;
- const auto &defaultColor = defaultPalette.color(adjustCg(group), role);
+ if (!m_requestedPalette.isAllocated())
+ return false;
- return setColor(group, role, defaultColor);
+ QPalette::ResolveMask unsetResolveMask = 0;
+
+ if (group == QPalette::Current)
+ group = m_requestedPalette->currentColorGroup();
+
+ if (group == QPalette::All) {
+ for (int g = QPalette::Active; g < QPalette::NColorGroups; ++g)
+ unsetResolveMask |= (QPalette::ResolveMask(1) << QPalettePrivate::bitPosition(QPalette::ColorGroup(g), role));
+ } else {
+ unsetResolveMask = (QPalette::ResolveMask(1) << QPalettePrivate::bitPosition(group, role));
+ }
+
+ m_requestedPalette->setResolveMask(m_requestedPalette->resolveMask() & ~unsetResolveMask);
+
+ return updateInheritedPalette();
+}
+
+bool QQuickPaletteColorProvider::resetColor(QPalette::ColorGroup group)
+{
+ if (!m_requestedPalette.isAllocated())
+ return false;
+
+ QPalette::ResolveMask unsetResolveMask = 0;
+
+ auto getResolveMask = [] (QPalette::ColorGroup group) {
+ QPalette::ResolveMask mask = 0;
+ for (int roleIndex = QPalette::WindowText; roleIndex < QPalette::NColorRoles; ++roleIndex) {
+ const auto cr = QPalette::ColorRole(roleIndex);
+ mask |= (QPalette::ResolveMask(1) << QPalettePrivate::bitPosition(group, cr));
+ }
+ return mask;
+ };
+
+ if (group == QPalette::Current)
+ group = m_requestedPalette->currentColorGroup();
+
+ if (group == QPalette::All) {
+ for (int g = QPalette::Active; g < QPalette::NColorGroups; ++g)
+ unsetResolveMask |= getResolveMask(QPalette::ColorGroup(g));
+ } else {
+ unsetResolveMask = getResolveMask(group);
+ }
+
+ m_requestedPalette->setResolveMask(m_requestedPalette->resolveMask() & ~unsetResolveMask);
+
+ return updateInheritedPalette();
}
bool QQuickPaletteColorProvider::fromQPalette(QPalette p)
diff --git a/src/quick/items/qquickpalettecolorprovider_p.h b/src/quick/items/qquickpalettecolorprovider_p.h
index ff715219dc..2a779f20d3 100644
--- a/src/quick/items/qquickpalettecolorprovider_p.h
+++ b/src/quick/items/qquickpalettecolorprovider_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractPaletteProvider;
-class Q_QUICK_PRIVATE_EXPORT QQuickPaletteColorProvider
+class Q_QUICK_EXPORT QQuickPaletteColorProvider
: public std::enable_shared_from_this<QQuickPaletteColorProvider>
{
public:
@@ -34,6 +34,7 @@ public:
const QColor &color(QPalette::ColorGroup group, QPalette::ColorRole role) const;
bool setColor(QPalette::ColorGroup group, QPalette::ColorRole role, QColor color);
bool resetColor(QPalette::ColorGroup group, QPalette::ColorRole role);
+ bool resetColor(QPalette::ColorGroup group);
bool fromQPalette(QPalette p);
QPalette palette() const;
diff --git a/src/quick/items/qquickpaletteproviderprivatebase_p.h b/src/quick/items/qquickpaletteproviderprivatebase_p.h
index a0e0302ea9..869e86c954 100644
--- a/src/quick/items/qquickpaletteproviderprivatebase_p.h
+++ b/src/quick/items/qquickpaletteproviderprivatebase_p.h
@@ -221,7 +221,7 @@ void QQuickPaletteProviderPrivateBase<I, Impl>::registerPalette(PalettePtr palet
// In order to avoid extra noise, we should connect
// the following signals only after everything is already setup
I::connect(paletteData(), &QQuickPalette::changed, itemWithPalette(), &I::paletteChanged);
- I::connect(paletteData(), &QQuickPalette::changed, [this]{ updateChildrenPalettes(toQPalette()); });
+ I::connect(paletteData(), &QQuickPalette::changed, itemWithPalette(), [this]{ updateChildrenPalettes(toQPalette()); });
}
template<class T> struct dependent_false : std::false_type {};
@@ -230,9 +230,9 @@ template<class Impl, class I> decltype(auto) getPrivateImpl(I &t) { return Impl:
template <class T>
decltype(auto) getPrivate(T &item)
{
- if constexpr (std::is_same_v<T, QQuickWindow>) {
+ if constexpr (std::is_base_of_v<T, QQuickWindow>) {
return getPrivateImpl<QQuickWindowPrivate>(item);
- } else if constexpr (std::is_same_v<T, QQuickItem>) {
+ } else if constexpr (std::is_base_of_v<T, QQuickItem>) {
return getPrivateImpl<QQuickItemPrivate>(item);
} else {
static_assert (dependent_false<T>::value, "Extend please.");
@@ -257,12 +257,15 @@ template<class I, class Impl>
QPalette QQuickPaletteProviderPrivateBase<I, Impl>::parentPalette(const QPalette &fallbackPalette) const
{
if constexpr (!isRootWindow<I>()) {
- for (auto parentItem = itemWithPalette()->parentItem(); parentItem;
- parentItem = parentItem->parentItem()) {
-
- // Don't allocate a new palette here. Use only if it's already pre allocated
- if (parentItem && getPrivate(*parentItem)->providesPalette()) {
- return getPrivate(*parentItem)->palette()->toQPalette();
+ // Popups should always inherit from their window, even child popups: QTBUG-115707.
+ if (!std::is_base_of_v<QQuickPopup, I>) {
+ for (auto parentItem = itemWithPalette()->parentItem(); parentItem;
+ parentItem = parentItem->parentItem()) {
+
+ // Don't allocate a new palette here. Use only if it's already pre allocated
+ if (parentItem && getPrivate(*parentItem)->providesPalette()) {
+ return getPrivate(*parentItem)->palette()->toQPalette();
+ }
}
}
@@ -279,7 +282,7 @@ const QQuickItem* rootItem(const I &item)
{
if constexpr (isRootWindow<I>()) {
return item.contentItem();
- } else if constexpr (std::is_same_v<QQuickPopup, I>) {
+ } else if constexpr (std::is_base_of_v<QQuickPopup, I>) {
return nullptr;
} else {
return &item;
@@ -342,9 +345,12 @@ void QQuickPaletteProviderPrivateBase<I, Impl>::connectItem()
if constexpr (!isRootWindow<I>()) {
// Item with palette has the same lifetime as its implementation that inherits this class
- I::connect(itemWithPalette(), &I::parentChanged , [this]() { inheritPalette(parentPalette(defaultPalette())); });
- I::connect(itemWithPalette(), &I::windowChanged , [this]() { inheritPalette(parentPalette(defaultPalette())); });
- I::connect(itemWithPalette(), &I::enabledChanged, [this]() { setCurrentColorGroup(); });
+ I::connect(itemWithPalette(), &I::parentChanged,
+ itemWithPalette(), [this]() { inheritPalette(parentPalette(defaultPalette())); });
+ I::connect(itemWithPalette(), &I::windowChanged,
+ itemWithPalette(), [this]() { inheritPalette(parentPalette(defaultPalette())); });
+ I::connect(itemWithPalette(), &I::enabledChanged,
+ itemWithPalette(), [this]() { setCurrentColorGroup(); });
}
}
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index 6de81470c4..ce61543f42 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -10,7 +10,7 @@
#include <private/qqmlglobal_p.h>
#include <private/qqmlopenmetaobject_p.h>
#include <private/qqmlchangeset_p.h>
-#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformtheme.h>
#include <QtQml/qqmlinfo.h>
@@ -26,10 +26,11 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcItemViewDelegateLifecycle)
+#if !QT_CONFIG(quick_itemview)
+Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle")
+#endif
Q_LOGGING_CATEGORY(lcPathView, "qt.quick.pathview")
-static const qreal MinimumFlickVelocity = 75;
-
static QQmlOpenMetaObjectType *qPathViewAttachedType = nullptr;
QQuickPathViewAttached::QQuickPathViewAttached(QObject *parent)
@@ -64,9 +65,9 @@ QQuickPathViewPrivate::QQuickPathViewPrivate()
, moving(false), flicking(false), dragging(false), inRequest(false), delegateValidated(false)
, inRefill(false)
, dragMargin(0), deceleration(100)
- , maximumFlickVelocity(QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickMaximumVelocity).toReal())
+ , maximumFlickVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
, moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0)
- , pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0)
+ , pathItems(-1), requestedIndex(-1), cacheSize(0), requestedCacheSize(0), requestedZ(0)
, moveReason(Other), movementDirection(QQuickPathView::Shortest), moveDirection(QQuickPathView::Shortest)
, attType(nullptr), highlightComponent(nullptr), highlightItem(nullptr)
, moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition)
@@ -75,6 +76,7 @@ QQuickPathViewPrivate::QQuickPathViewPrivate()
, highlightRangeMode(QQuickPathView::StrictlyEnforceRange)
, highlightMoveDuration(300), modelCount(0), snapMode(QQuickPathView::NoSnap)
{
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
void QQuickPathViewPrivate::init()
@@ -231,6 +233,10 @@ void QQuickPathViewPrivate::clear()
void QQuickPathViewPrivate::updateMappedRange()
{
+ // Update the actual cache size to be at max
+ // the available non-visible items.
+ cacheSize = qMax(0, qMin(requestedCacheSize, modelCount - pathItems));
+
if (model && pathItems != -1 && pathItems < modelCount) {
mappedRange = qreal(modelCount)/pathItems;
mappedCache = qreal(cacheSize)/pathItems/2; // Half of cache at each end
@@ -265,9 +271,10 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
// returns true if position is between lower and upper, taking into
// account the circular space.
-bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower, qreal upper) const
+bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower,
+ qreal upper, bool emptyRangeCheck) const
{
- if (qFuzzyCompare(lower, upper))
+ if (emptyRangeCheck && qFuzzyCompare(lower, upper))
return true;
if (lower > upper) {
if (position > upper && position > lower)
@@ -530,6 +537,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty PathView QtQuick::PathView::view
+ \readonly
+
This attached property holds the view that manages this delegate instance.
It is attached to each instance of the delegate.
@@ -537,6 +546,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty bool QtQuick::PathView::onPath
+ \readonly
+
This attached property holds whether the item is currently on the path.
If a pathItemCount has been set, it is possible that some items may
@@ -557,6 +568,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty bool QtQuick::PathView::isCurrentItem
+ \readonly
+
This attached property is true if this delegate is the current item; otherwise false.
It is attached to each instance of the delegate.
@@ -894,18 +907,16 @@ QQuickItem *QQuickPathView::highlightItem() const
Valid values for \c highlightRangeMode are:
- \list
- \li \e PathView.NoHighlightRange - no range is applied and the
- highlight will move freely within the view.
- \li \e PathView.ApplyRange - the view will attempt to maintain
- the highlight within the range, however the highlight can
- move outside of the range at the ends of the path or due to
- a mouse interaction.
- \li \e PathView.StrictlyEnforceRange - the highlight will never
- move outside of the range. This means that the current item
- will change if a keyboard or mouse action would cause the
- highlight to move outside of the range.
- \endlist
+ \value PathView.NoHighlightRange no range is applied: the highlight
+ will move freely within the view.
+ \value PathView.ApplyRange the view will attempt to maintain the highlight
+ within the range, however the highlight can move
+ outside of the range at the ends of the path or
+ due to a mouse interaction.
+ \value PathView.StrictlyEnforceRange the highlight will never move outside of the range.
+ This means that the current item will change if a
+ keyboard or mouse action would cause the highlight
+ to move outside of the range.
The default value is \e PathView.StrictlyEnforceRange.
@@ -1291,16 +1302,16 @@ void QQuickPathView::resetPathItemCount()
int QQuickPathView::cacheItemCount() const
{
Q_D(const QQuickPathView);
- return d->cacheSize;
+ return d->requestedCacheSize;
}
void QQuickPathView::setCacheItemCount(int i)
{
Q_D(QQuickPathView);
- if (i == d->cacheSize || i < 0)
+ if (i == d->requestedCacheSize || i < 0)
return;
- d->cacheSize = i;
+ d->requestedCacheSize = i;
d->updateMappedRange();
refill();
emit cacheItemCountChanged();
@@ -1312,13 +1323,11 @@ void QQuickPathView::setCacheItemCount(int i)
This property determines how the items will settle following a drag or flick.
The possible values are:
- \list
- \li PathView.NoSnap (default) - the items stop anywhere along the path.
- \li PathView.SnapToItem - the items settle with an item aligned with the \l preferredHighlightBegin.
- \li PathView.SnapOneItem - the items settle no more than one item away from the item nearest
+ \value PathView.NoSnap (default) the items stop anywhere along the path.
+ \value PathView.SnapToItem the items settle with an item aligned with the \l preferredHighlightBegin.
+ \value PathView.SnapOneItem the items settle no more than one item away from the item nearest
\l preferredHighlightBegin at the time the press is released. This mode is particularly
useful for moving one page at a time.
- \endlist
\c snapMode does not affect the \l currentIndex. To update the
\l currentIndex as the view is moved, set \l highlightRangeMode
@@ -1348,12 +1357,10 @@ void QQuickPathView::setSnapMode(SnapMode mode)
This property determines the direction in which items move when setting the current index.
The possible values are:
- \list
- \li PathView.Shortest (default) - the items move in the direction that requires the least
- movement, which could be either \c Negative or \c Positive.
- \li PathView.Negative - the items move backwards towards their destination.
- \li PathView.Positive - the items move forwards towards their destination.
- \endlist
+ \value PathView.Shortest (default) the items move in the direction that requires the least
+ movement, which could be either \c Negative or \c Positive.
+ \value PathView.Negative the items move backwards towards their destination.
+ \value PathView.Positive the items move forwards towards their destination.
For example, suppose that there are 5 items in the model, and \l currentIndex is \c 0.
If currentIndex is set to \c 2,
@@ -1389,15 +1396,13 @@ void QQuickPathView::setMovementDirection(QQuickPathView::MovementDirection dir)
Positions the view such that the \a index is at the position specified by
\a mode:
- \list
- \li PathView.Beginning - position item at the beginning of the path.
- \li PathView.Center - position item in the center of the path.
- \li PathView.End - position item at the end of the path.
- \li PathView.Contain - ensure the item is positioned on the path.
- \li PathView.SnapPosition - position the item at \l preferredHighlightBegin. This mode
- is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
- via \l snapMode.
- \endlist
+ \value PathView.Beginning position item at the beginning of the path.
+ \value PathView.Center position item in the center of the path.
+ \value PathView.End position item at the end of the path.
+ \value PathView.Contain ensure the item is positioned on the path.
+ \value PathView.SnapPosition position the item at \l preferredHighlightBegin. This mode
+ is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
+ via \l snapMode.
\b Note: methods should only be called after the Component has completed. To position
the view at startup, this method should be called by Component.onCompleted. For
@@ -1510,7 +1515,7 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const
}
/*!
- \qmlmethod Item QtQuick::QQuickPathView::itemAtIndex(int index)
+ \qmlmethod Item QtQuick::PathView::itemAtIndex(int index)
Returns the item for \a index. If there is no item for that index, for example
because it has not been created yet, or because it has been panned out of
@@ -1536,6 +1541,14 @@ QQuickItem *QQuickPathView::itemAtIndex(int index) const
return nullptr;
}
+/*!
+ \internal
+
+ Returns a point in the path, that has the closest distance from \a point.
+ A value in the range 0-1 will be written to \a nearPercent if given, which
+ represents where on the path the \a point is closest to. \c 0 means the very
+ beginning of the path, and \c 1 means the very end.
+*/
QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
{
const auto pathLength = path->path().length();
@@ -1751,7 +1764,7 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *event)
qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
const auto averageItemLength = path->path().length() / count;
qreal pixelVelocity = averageItemLength * velocity;
- if (qAbs(pixelVelocity) > MinimumFlickVelocity) {
+ if (qAbs(pixelVelocity) > _q_MinimumFlickVelocity) {
if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
// limit velocity
qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
@@ -1855,7 +1868,8 @@ bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
const bool filtered = stealThisEvent || grabberDisabled;
if (filtered)
- pe->setAccepted(false);
+ pe->setAccepted(stealThisEvent && grabber == this && grabber->isEnabled());
+
return filtered;
} else if (d->timer.isValid()) {
d->timer.invalidate();
@@ -2011,6 +2025,7 @@ void QQuickPathView::refill()
startPos = d->highlightRangeStart;
// With no items, then "end" is just off the top so we populate via append
endIdx = (qRound(d->modelCount - d->offset) - 1) % d->modelCount;
+ endIdx = qMax(-1, endIdx); // endIdx shouldn't be smaller than -1
endPos = d->positionOfIndex(endIdx);
}
//Append
@@ -2018,8 +2033,8 @@ void QQuickPathView::refill()
if (idx >= d->modelCount)
idx = 0;
qreal nextPos = d->positionOfIndex(idx);
- while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache) || !d->items.size())
- && d->items.size() < count+d->cacheSize) {
+ while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache, false) || !d->items.size())
+ && d->items.size() < count + d->cacheSize) {
qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.size();
QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
if (!item) {
diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h
index da207bbf29..187ebad65a 100644
--- a/src/quick/items/qquickpathview_p.h
+++ b/src/quick/items/qquickpathview_p.h
@@ -30,7 +30,7 @@ class QQmlChangeSet;
class QQuickPathViewPrivate;
class QQuickPathViewAttached;
-class Q_QUICK_PRIVATE_EXPORT QQuickPathView : public QQuickItem
+class Q_QUICK_EXPORT QQuickPathView : public QQuickItem
{
Q_OBJECT
@@ -218,9 +218,9 @@ class QQuickPathViewAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickPathView *view READ view CONSTANT)
- Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged)
- Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged)
+ Q_PROPERTY(QQuickPathView *view READ view CONSTANT FINAL)
+ Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged FINAL)
+ Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged FINAL)
public:
QQuickPathViewAttached(QObject *parent);
@@ -264,6 +264,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPathView)
-
#endif // QQUICKPATHVIEW_P_H
diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h
index 61c0b2ce62..3b7487afbf 100644
--- a/src/quick/items/qquickpathview_p_p.h
+++ b/src/quick/items/qquickpathview_p_p.h
@@ -31,6 +31,8 @@ QT_REQUIRE_CONFIG(quick_pathview);
#include <private/qquicktimeline_p_p.h>
#include <private/qpodvector_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlOpenMetaObjectType;
@@ -73,7 +75,7 @@ public:
void clear();
void updateMappedRange();
qreal positionOfIndex(qreal index) const;
- bool isInBound(qreal position, qreal lower, qreal upper) const;
+ bool isInBound(qreal position, qreal lower, qreal upper, bool emptyRangeCheck = true) const;
void createHighlight();
void updateHighlight();
void setHighlightPosition(qreal pos);
@@ -138,6 +140,7 @@ public:
int pathItems;
int requestedIndex;
int cacheSize;
+ int requestedCacheSize;
qreal requestedZ;
QList<QQuickItem *> items;
QList<QQuickItem *> itemCache;
diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp
index d547fae6c9..482941b9ba 100644
--- a/src/quick/items/qquickpincharea.cpp
+++ b/src/quick/items/qquickpincharea.cpp
@@ -247,7 +247,7 @@ QQuickPinchArea::QQuickPinchArea(QQuickItem *parent)
Q_D(QQuickPinchArea);
d->init();
setAcceptTouchEvents(true);
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
setAcceptHoverEvents(true); // needed to enable touch events on mouse hover.
#endif
}
diff --git a/src/quick/items/qquickpincharea_p.h b/src/quick/items/qquickpincharea_p.h
index 5e08aa3eeb..9ea16f3ea4 100644
--- a/src/quick/items/qquickpincharea_p.h
+++ b/src/quick/items/qquickpincharea_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickPinch : public QObject
+class Q_QUICK_EXPORT QQuickPinch : public QObject
{
Q_OBJECT
@@ -160,24 +160,24 @@ private:
bool m_active;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPinchEvent : public QObject
+class Q_QUICK_EXPORT QQuickPinchEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(QPointF center READ center)
- Q_PROPERTY(QPointF startCenter READ startCenter)
- Q_PROPERTY(QPointF previousCenter READ previousCenter)
- Q_PROPERTY(qreal scale READ scale)
- Q_PROPERTY(qreal previousScale READ previousScale)
- Q_PROPERTY(qreal angle READ angle)
- Q_PROPERTY(qreal previousAngle READ previousAngle)
- Q_PROPERTY(qreal rotation READ rotation)
- Q_PROPERTY(QPointF point1 READ point1)
- Q_PROPERTY(QPointF startPoint1 READ startPoint1)
- Q_PROPERTY(QPointF point2 READ point2)
- Q_PROPERTY(QPointF startPoint2 READ startPoint2)
- Q_PROPERTY(int pointCount READ pointCount)
- Q_PROPERTY(bool accepted READ accepted WRITE setAccepted)
+ Q_PROPERTY(QPointF center READ center FINAL)
+ Q_PROPERTY(QPointF startCenter READ startCenter FINAL)
+ Q_PROPERTY(QPointF previousCenter READ previousCenter FINAL)
+ Q_PROPERTY(qreal scale READ scale FINAL)
+ Q_PROPERTY(qreal previousScale READ previousScale FINAL)
+ Q_PROPERTY(qreal angle READ angle FINAL)
+ Q_PROPERTY(qreal previousAngle READ previousAngle FINAL)
+ Q_PROPERTY(qreal rotation READ rotation FINAL)
+ Q_PROPERTY(QPointF point1 READ point1 FINAL)
+ Q_PROPERTY(QPointF startPoint1 READ startPoint1 FINAL)
+ Q_PROPERTY(QPointF point2 READ point2 FINAL)
+ Q_PROPERTY(QPointF startPoint2 READ startPoint2 FINAL)
+ Q_PROPERTY(int pointCount READ pointCount FINAL)
+ Q_PROPERTY(bool accepted READ accepted WRITE setAccepted FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -230,9 +230,8 @@ private:
};
-class QQuickMouseEvent;
class QQuickPinchAreaPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPinchArea : public QQuickItem
+class Q_QUICK_EXPORT QQuickPinchArea : public QQuickItem
{
Q_OBJECT
@@ -278,9 +277,5 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPinch)
-QML_DECLARE_TYPE(QQuickPinchEvent)
-QML_DECLARE_TYPE(QQuickPinchArea)
-
#endif // QQUICKPINCHAREA_H
diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp
index 5bb1f4f6d9..ad5447df6f 100644
--- a/src/quick/items/qquickpositioners.cpp
+++ b/src/quick/items/qquickpositioners.cpp
@@ -12,7 +12,7 @@
QT_BEGIN_NAMESPACE
-static const QQuickItemPrivate::ChangeTypes watchedChanges
+static const QQuickItemPrivate::ChangeTypes positionerWatchedChanges
= QQuickItemPrivate::Geometry
| QQuickItemPrivate::SiblingOrder
| QQuickItemPrivate::Visibility
@@ -21,19 +21,21 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges
void QQuickBasePositionerPrivate::watchChanges(QQuickItem *other)
{
QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other);
- otherPrivate->addItemChangeListener(this, watchedChanges);
+ otherPrivate->addItemChangeListener(this, positionerWatchedChanges);
}
void QQuickBasePositionerPrivate::unwatchChanges(QQuickItem* other)
{
QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other);
- otherPrivate->removeItemChangeListener(this, watchedChanges);
+ otherPrivate->removeItemChangeListener(this, positionerWatchedChanges);
}
QQuickBasePositioner::PositionedItem::PositionedItem(QQuickItem *i)
: item(i)
+#if QT_CONFIG(quick_viewtransitions)
, transitionableItem(nullptr)
+#endif
, index(-1)
, isNew(false)
, isVisible(true)
@@ -46,27 +48,40 @@ QQuickBasePositioner::PositionedItem::PositionedItem(QQuickItem *i)
QQuickBasePositioner::PositionedItem::~PositionedItem()
{
+#if QT_CONFIG(quick_viewtransitions)
delete transitionableItem;
+#endif
}
qreal QQuickBasePositioner::PositionedItem::itemX() const
{
- return transitionableItem ? transitionableItem->itemX() : item->x();
+ return
+#if QT_CONFIG(quick_viewtransitions)
+ transitionableItem ? transitionableItem->itemX() :
+#endif
+ item->x();
}
qreal QQuickBasePositioner::PositionedItem::itemY() const
{
- return transitionableItem ? transitionableItem->itemY() : item->y();
+ return
+#if QT_CONFIG(quick_viewtransitions)
+ transitionableItem ? transitionableItem->itemY() :
+#endif
+ item->y();
}
void QQuickBasePositioner::PositionedItem::moveTo(const QPointF &pos)
{
+#if QT_CONFIG(quick_viewtransitions)
if (transitionableItem)
transitionableItem->moveTo(pos);
else
+#endif
item->setPosition(pos);
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickBasePositioner::PositionedItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
{
if (!transitioner)
@@ -86,6 +101,7 @@ void QQuickBasePositioner::PositionedItem::startTransition(QQuickItemViewTransit
if (transitionableItem)
transitionableItem->startTransition(transitioner, index);
}
+#endif
void QQuickBasePositioner::PositionedItem::updatePadding(qreal lp, qreal tp, qreal rp, qreal bp)
{
@@ -132,7 +148,9 @@ QQuickBasePositioner::QQuickBasePositioner(QQuickBasePositionerPrivate &dd, Posi
QQuickBasePositioner::~QQuickBasePositioner()
{
Q_D(QQuickBasePositioner);
+#if QT_CONFIG(quick_viewtransitions)
delete d->transitioner;
+#endif
for (int i = 0; i < positionedItems.count(); ++i)
d->unwatchChanges(positionedItems.at(i).item);
for (int i = 0; i < unpositionedItems.count(); ++i)
@@ -164,6 +182,7 @@ void QQuickBasePositioner::setSpacing(qreal s)
emit spacingChanged();
}
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *QQuickBasePositioner::populate() const
{
Q_D(const QQuickBasePositioner);
@@ -216,17 +235,24 @@ void QQuickBasePositioner::setAdd(QQuickTransition *add)
d->transitioner->addTransition = add;
emit addChanged();
}
+#endif
void QQuickBasePositioner::componentComplete()
{
+#if QT_CONFIG(quick_viewtransitions)
Q_D(QQuickBasePositioner);
+#endif
QQuickItem::componentComplete();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
+#endif
positionedItems.reserve(childItems().size());
prePositioning();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(false);
+#endif
}
void QQuickBasePositioner::itemChange(ItemChange change, const ItemChangeData &value)
@@ -275,7 +301,9 @@ void QQuickBasePositioner::prePositioning()
for (int ii = 0; ii < unpositionedItems.count(); ii++)
oldItems.append(unpositionedItems[ii]);
unpositionedItems.clear();
+#if QT_CONFIG(quick_viewtransitions)
int addedIndex = -1;
+#endif
for (int ii = 0; ii < children.size(); ++ii) {
QQuickItem *child = children.at(ii);
@@ -295,6 +323,7 @@ void QQuickBasePositioner::prePositioning()
posItem.index = positionedItems.count();
positionedItems.append(posItem);
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
if (addedIndex < 0)
addedIndex = posItem.index;
@@ -304,6 +333,7 @@ void QQuickBasePositioner::prePositioning()
else if (!d->transitioner->populateTransitionEnabled())
theItem->transitionNextReposition(d->transitioner, QQuickItemViewTransitioner::AddTransition, true);
}
+#endif
}
} else {
PositionedItem *item = &oldItems[wIdx];
@@ -320,11 +350,13 @@ void QQuickBasePositioner::prePositioning()
item->index = positionedItems.count();
positionedItems.append(*item);
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
if (addedIndex < 0)
addedIndex = item->index;
positionedItems[positionedItems.count()-1].transitionNextReposition(d->transitioner, QQuickItemViewTransitioner::AddTransition, true);
}
+#endif
} else {
item->isNew = false;
item->index = positionedItems.count();
@@ -333,6 +365,7 @@ void QQuickBasePositioner::prePositioning()
}
}
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
for (int i=0; i<positionedItems.count(); i++) {
if (!positionedItems[i].isNew) {
@@ -346,6 +379,7 @@ void QQuickBasePositioner::prePositioning()
}
}
}
+#endif
QSizeF contentSize(0,0);
reportConflictingAnchors();
@@ -354,6 +388,7 @@ void QQuickBasePositioner::prePositioning()
updateAttachedProperties();
}
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
QRectF viewBounds(QPointF(), contentSize);
for (int i=0; i<positionedItems.count(); i++)
@@ -362,6 +397,7 @@ void QQuickBasePositioner::prePositioning()
positionedItems[i].startTransition(d->transitioner);
d->transitioner->resetTargetLists();
}
+#endif
d->doingPositioning = false;
@@ -403,13 +439,17 @@ void QQuickBasePositioner::positionItemY(qreal y, PositionedItem *target)
void QQuickBasePositioner::removePositionedItem(QPODVector<PositionedItem,8> *items, int index)
{
Q_ASSERT(index >= 0 && index < items->count());
+#if QT_CONFIG(quick_viewtransitions)
delete items->at(index).transitionableItem;
+#endif
items->remove(index);
}
void QQuickBasePositioner::clearPositionedItems(QPODVector<PositionedItem,8> *items)
{
+#if QT_CONFIG(quick_viewtransitions)
for (int i=0; i<items->count(); i++)
delete items->at(i).transitionableItem;
+#endif
items->clear();
}
@@ -1111,14 +1151,12 @@ QQuickRow::QQuickRow(QQuickItem *parent)
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set,
- the left anchor remains to the left of the row.
- \li Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set,
- the right anchor remains to the right of the row.
- \endlist
+ \value Qt.LeftToRight (default) Items are laid out from left to right. If the width of the row is
+ explicitly set, the left anchor remains to the left of the row.
+ \value Qt.RightToLeft Items are laid out from right to left. If the width of the row is
+ explicitly set, the right anchor remains to the right of the row.
- \sa Grid::layoutDirection, Flow::layoutDirection, {Qt Quick Examples - Right to Left}
+ \sa Grid::layoutDirection, Flow::layoutDirection
*/
Qt::LayoutDirection QQuickRow::layoutDirection() const
@@ -1531,7 +1569,7 @@ void QQuickGrid::setColumnSpacing(const qreal columnSpacing)
\l Grid::flow property.
\endlist
- \sa Flow::layoutDirection, Row::layoutDirection, {Qt Quick Examples - Right to Left}
+ \sa Flow::layoutDirection, Row::layoutDirection
*/
Qt::LayoutDirection QQuickGrid::layoutDirection() const
{
@@ -2031,7 +2069,7 @@ void QQuickFlow::setFlow(Flow flow)
\l Flow::flow property.
\endlist
- \sa Grid::layoutDirection, Row::layoutDirection, {Qt Quick Examples - Right to Left}
+ \sa Grid::layoutDirection, Row::layoutDirection
*/
Qt::LayoutDirection QQuickFlow::layoutDirection() const
diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h
index 42ab3bc6a7..02a368882f 100644
--- a/src/quick/items/qquickpositioners_p.h
+++ b/src/quick/items/qquickpositioners_p.h
@@ -20,7 +20,9 @@
QT_REQUIRE_CONFIG(quick_positioners);
#include "qquickimplicitsizeitem_p.h"
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickitemviewtransition_p.h"
+#endif
#include <private/qpodvector_p.h>
@@ -38,9 +40,9 @@ class QQuickPositionerAttached : public QObject
public:
QQuickPositionerAttached(QObject *parent);
- Q_PROPERTY(int index READ index NOTIFY indexChanged)
- Q_PROPERTY(bool isFirstItem READ isFirstItem NOTIFY isFirstItemChanged)
- Q_PROPERTY(bool isLastItem READ isLastItem NOTIFY isLastItemChanged)
+ Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
+ Q_PROPERTY(bool isFirstItem READ isFirstItem NOTIFY isFirstItemChanged FINAL)
+ Q_PROPERTY(bool isLastItem READ isLastItem NOTIFY isLastItemChanged FINAL)
int index() const { return m_index; }
void setIndex(int index);
@@ -62,14 +64,16 @@ private:
bool m_isLastItem;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickBasePositioner : public QQuickImplicitSizeItem
+class Q_QUICK_EXPORT QQuickBasePositioner : public QQuickImplicitSizeItem
{
Q_OBJECT
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
+#if QT_CONFIG(quick_viewtransitions)
Q_PROPERTY(QQuickTransition *populate READ populate WRITE setPopulate NOTIFY populateChanged)
Q_PROPERTY(QQuickTransition *move READ move WRITE setMove NOTIFY moveChanged)
Q_PROPERTY(QQuickTransition *add READ add WRITE setAdd NOTIFY addChanged)
+#endif
Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged REVISION(2, 6))
Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged REVISION(2, 6))
@@ -91,6 +95,7 @@ public:
qreal spacing() const;
void setSpacing(qreal);
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *populate() const;
void setPopulate(QQuickTransition *);
@@ -99,6 +104,7 @@ public:
QQuickTransition *add() const;
void setAdd(QQuickTransition *);
+#endif
static QQuickPositionerAttached *qmlAttachedProperties(QObject *obj);
@@ -164,14 +170,18 @@ protected:
void moveTo(const QPointF &pos);
+#if QT_CONFIG(quick_viewtransitions)
void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
void startTransition(QQuickItemViewTransitioner *transitioner);
+#endif
void updatePadding(qreal lp, qreal tp, qreal rp, qreal bp);
QQuickItem *item;
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitionableItem *transitionableItem;
+#endif
int index;
bool isNew;
bool isVisible;
@@ -197,7 +207,7 @@ private:
Q_DECLARE_PRIVATE(QQuickBasePositioner)
};
-class Q_QUICK_PRIVATE_EXPORT QQuickColumn : public QQuickBasePositioner
+class Q_QUICK_EXPORT QQuickColumn : public QQuickBasePositioner
{
Q_OBJECT
QML_NAMED_ELEMENT(Column)
@@ -211,7 +221,7 @@ protected:
};
class QQuickRowPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRow: public QQuickBasePositioner
+class Q_QUICK_EXPORT QQuickRow: public QQuickBasePositioner
{
Q_OBJECT
Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
@@ -238,7 +248,7 @@ private:
};
class QQuickGridPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickGrid : public QQuickBasePositioner
+class Q_QUICK_EXPORT QQuickGrid : public QQuickBasePositioner
{
Q_OBJECT
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
@@ -326,7 +336,7 @@ private:
};
class QQuickFlowPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickFlow: public QQuickBasePositioner
+class Q_QUICK_EXPORT QQuickFlow: public QQuickBasePositioner
{
Q_OBJECT
Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged)
@@ -360,14 +370,6 @@ private:
Q_DECLARE_PRIVATE(QQuickFlow)
};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickColumn)
-QML_DECLARE_TYPE(QQuickRow)
-QML_DECLARE_TYPE(QQuickGrid)
-QML_DECLARE_TYPE(QQuickFlow)
-
-QML_DECLARE_TYPE(QQuickBasePositioner)
-
#endif // QQUICKPOSITIONERS_P_H
diff --git a/src/quick/items/qquickpositioners_p_p.h b/src/quick/items/qquickpositioners_p_p.h
index f91b093684..004a0f5ed1 100644
--- a/src/quick/items/qquickpositioners_p_p.h
+++ b/src/quick/items/qquickpositioners_p_p.h
@@ -54,7 +54,10 @@ public:
QQuickBasePositionerPrivate()
: spacing(0), type(QQuickBasePositioner::None)
- , transitioner(0), positioningDirty(false)
+#if QT_CONFIG(quick_viewtransitions)
+ , transitioner(0)
+#endif
+ , positioningDirty(false)
, doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight)
{
@@ -68,7 +71,9 @@ public:
qreal spacing;
QQuickBasePositioner::PositionerType type;
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner *transitioner;
+#endif
void watchChanges(QQuickItem *other);
void unwatchChanges(QQuickItem* other);
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index 7037526264..403d0c1034 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -229,10 +229,9 @@ QQmlListProperty<QQuickGradientStop> QQuickGradient::stops()
\since 5.12
Set this property to define the direction of the gradient.
- \list
- \li Gradient.Vertical - a vertical gradient
- \li Gradient.Horizontal - a horizontal gradient
- \endlist
+
+ \value Gradient.Vertical a vertical gradient
+ \value Gradient.Horizontal a horizontal gradient
The default is Gradient.Vertical.
*/
@@ -265,6 +264,17 @@ void QQuickGradient::doUpdate()
int QQuickRectanglePrivate::doUpdateSlotIdx = -1;
+void QQuickRectanglePrivate::maybeSetImplicitAntialiasing()
+{
+ bool implicitAA = (radius != 0);
+ if (extraRectangle.isAllocated() && !implicitAA) {
+ implicitAA = extraRectangle.value().topLeftRadius > 0.0
+ || extraRectangle.value().topRightRadius > 0.0
+ || extraRectangle.value().bottomLeftRadius > 0.0
+ || extraRectangle.value().bottomRightRadius > 0.0;
+ }
+ setImplicitAntialiasing(implicitAA);
+}
/*!
\qmltype Rectangle
\instantiates QQuickRectangle
@@ -289,7 +299,10 @@ int QQuickRectanglePrivate::doUpdateSlotIdx = -1;
You can also create rounded rectangles using the \l radius property. Since this
introduces curved edges to the corners of a rectangle, it may be appropriate to
- set the \l Item::antialiasing property to improve its appearance.
+ set the \l Item::antialiasing property to improve its appearance. To set the
+ radii individually for different corners, you can use the properties
+ \l topLeftRadius, \l topRightRadius, \l bottomLeftRadius and
+ \l bottomRightRadius.
\section1 Example Usage
@@ -340,6 +353,7 @@ void QQuickRectangle::doUpdate()
\qmlpropertygroup QtQuick::Rectangle::border
\qmlproperty int QtQuick::Rectangle::border.width
\qmlproperty color QtQuick::Rectangle::border.color
+ \qmlproperty bool QtQuick::Rectangle::border.pixelAligned
The width and color used to draw the border of the rectangle.
@@ -349,6 +363,10 @@ void QQuickRectangle::doUpdate()
rectangle itself or its position relative to other items if anchors are used.
The border is rendered within the rectangle's boundaries.
+
+ If \c pixelAligned is \c true (the default), the rendered border width is rounded to a whole
+ number of pixels, after device pixel ratio scaling. Setting \c pixelAligned to \c false will
+ allow fractional border widths, which may be desirable when \c antialiasing is enabled.
*/
QQuickPen *QQuickRectangle::border()
{
@@ -463,9 +481,12 @@ void QQuickRectangle::resetGradient()
\qmlproperty real QtQuick::Rectangle::radius
This property holds the corner radius used to draw a rounded rectangle.
- If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be
- painted as a normal rectangle. The same radius is used by all 4 corners; there is currently
- no way to specify different radii for different corners.
+ If radius is non-zero, the rectangle will be painted as a rounded rectangle,
+ otherwise it will be painted as a normal rectangle. Individual corner radii
+ can be set as well (see below). These values will override \l radius. If
+ they are unset (by setting them to \c undefined), \l radius will be used instead.
+
+ \sa topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius
*/
qreal QQuickRectangle::radius() const
{
@@ -480,10 +501,240 @@ void QQuickRectangle::setRadius(qreal radius)
return;
d->radius = radius;
- d->setImplicitAntialiasing(radius != 0.0);
+ d->maybeSetImplicitAntialiasing();
update();
emit radiusChanged();
+
+ if (d->extraRectangle.isAllocated()) {
+ if (d->extraRectangle->topLeftRadius < 0.)
+ emit topLeftRadiusChanged();
+ if (d->extraRectangle->topRightRadius < 0.)
+ emit topRightRadiusChanged();
+ if (d->extraRectangle->bottomLeftRadius < 0.)
+ emit bottomLeftRadiusChanged();
+ if (d->extraRectangle->bottomRightRadius < 0.)
+ emit bottomRightRadiusChanged();
+ } else {
+ emit topLeftRadiusChanged();
+ emit topRightRadiusChanged();
+ emit bottomLeftRadiusChanged();
+ emit bottomRightRadiusChanged();
+ }
+}
+
+/*!
+ \since 6.7
+ \qmlproperty real QtQuick::Rectangle::topLeftRadius
+ This property holds the radius used to draw the top left corner.
+
+ If \l topLeftRadius is not set, \l radius will be used instead.
+ If \l topLeftRadius is zero, the corner will be sharp.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa radius, topRightRadius, bottomLeftRadius, bottomRightRadius
+*/
+qreal QQuickRectangle::topLeftRadius() const
+{
+ Q_D(const QQuickRectangle);
+ if (d->extraRectangle.isAllocated() && d->extraRectangle->topLeftRadius >= 0.)
+ return d->extraRectangle.value().topLeftRadius;
+ return d->radius;
+}
+
+void QQuickRectangle::setTopLeftRadius(qreal radius)
+{
+ Q_D(QQuickRectangle);
+ if (d->extraRectangle.value().topLeftRadius == radius)
+ return;
+
+ if (radius < 0) { // use the fact that radius < 0 resets the radius.
+ qmlWarning(this) << "topLeftRadius (" << radius << ") cannot be less than 0.";
+ return;
+ }
+ d->extraRectangle.value().topLeftRadius = radius;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit topLeftRadiusChanged();
+}
+
+void QQuickRectangle::resetTopLeftRadius()
+{
+ Q_D(QQuickRectangle);
+ if (!d->extraRectangle.isAllocated())
+ return;
+ if (d->extraRectangle.value().topLeftRadius < 0)
+ return;
+
+ d->extraRectangle.value().topLeftRadius = -1.;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit topLeftRadiusChanged();
+}
+
+/*!
+ \since 6.7
+ \qmlproperty real QtQuick::Rectangle::topRightRadius
+ This property holds the radius used to draw the top right corner.
+
+ If \l topRightRadius is not set, \l radius will be used instead.
+ If \l topRightRadius is zero, the corner will be sharp.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa radius, topLeftRadius, bottomLeftRadius, bottomRightRadius
+*/
+qreal QQuickRectangle::topRightRadius() const
+{
+ Q_D(const QQuickRectangle);
+ if (d->extraRectangle.isAllocated() && d->extraRectangle->topRightRadius >= 0.)
+ return d->extraRectangle.value().topRightRadius;
+ return d->radius;
+}
+
+void QQuickRectangle::setTopRightRadius(qreal radius)
+{
+ Q_D(QQuickRectangle);
+ if (d->extraRectangle.value().topRightRadius == radius)
+ return;
+
+ if (radius < 0) { // use the fact that radius < 0 resets the radius.
+ qmlWarning(this) << "topRightRadius (" << radius << ") cannot be less than 0.";
+ return;
+ }
+ d->extraRectangle.value().topRightRadius = radius;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit topRightRadiusChanged();
+}
+
+void QQuickRectangle::resetTopRightRadius()
+{
+ Q_D(QQuickRectangle);
+ if (!d->extraRectangle.isAllocated())
+ return;
+ if (d->extraRectangle.value().topRightRadius < 0)
+ return;
+
+ d->extraRectangle.value().topRightRadius = -1.;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit topRightRadiusChanged();
+}
+
+/*!
+ \since 6.7
+ \qmlproperty real QtQuick::Rectangle::bottomLeftRadius
+ This property holds the radius used to draw the bottom left corner.
+
+ If \l bottomLeftRadius is not set, \l radius will be used instead.
+ If \l bottomLeftRadius is zero, the corner will be sharp.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa radius, topLeftRadius, topRightRadius, bottomRightRadius
+*/
+qreal QQuickRectangle::bottomLeftRadius() const
+{
+ Q_D(const QQuickRectangle);
+ if (d->extraRectangle.isAllocated() && d->extraRectangle->bottomLeftRadius >= 0.)
+ return d->extraRectangle.value().bottomLeftRadius;
+ return d->radius;
+}
+
+void QQuickRectangle::setBottomLeftRadius(qreal radius)
+{
+ Q_D(QQuickRectangle);
+ if (d->extraRectangle.value().bottomLeftRadius == radius)
+ return;
+
+ if (radius < 0) { // use the fact that radius < 0 resets the radius.
+ qmlWarning(this) << "bottomLeftRadius (" << radius << ") cannot be less than 0.";
+ return;
+ }
+
+ d->extraRectangle.value().bottomLeftRadius = radius;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit bottomLeftRadiusChanged();
+}
+
+void QQuickRectangle::resetBottomLeftRadius()
+{
+ Q_D(QQuickRectangle);
+ if (!d->extraRectangle.isAllocated())
+ return;
+ if (d->extraRectangle.value().bottomLeftRadius < 0)
+ return;
+
+ d->extraRectangle.value().bottomLeftRadius = -1.;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit bottomLeftRadiusChanged();
+}
+
+/*!
+ \since 6.7
+ \qmlproperty real QtQuick::Rectangle::bottomRightRadius
+ This property holds the radius used to draw the bottom right corner.
+
+ If \l bottomRightRadius is not set, \l radius will be used instead.
+ If \l bottomRightRadius is zero, the corner will be sharp.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa radius, topLeftRadius, topRightRadius, bottomLeftRadius
+*/
+qreal QQuickRectangle::bottomRightRadius() const
+{
+ Q_D(const QQuickRectangle);
+ if (d->extraRectangle.isAllocated() && d->extraRectangle->bottomRightRadius >= 0.)
+ return d->extraRectangle.value().bottomRightRadius;
+ return d->radius;
+}
+
+void QQuickRectangle::setBottomRightRadius(qreal radius)
+{
+ Q_D(QQuickRectangle);
+ if (d->extraRectangle.value().bottomRightRadius == radius)
+ return;
+
+ if (radius < 0) { // use the fact that radius < 0 resets the radius.
+ qmlWarning(this) << "bottomRightRadius (" << radius << ") cannot be less than 0.";
+ return;
+ }
+
+ d->extraRectangle.value().bottomRightRadius = radius;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit bottomRightRadiusChanged();
+}
+
+void QQuickRectangle::resetBottomRightRadius()
+{
+ Q_D(QQuickRectangle);
+ if (!d->extraRectangle.isAllocated())
+ return;
+ if (d->extraRectangle.value().bottomRightRadius < 0)
+ return;
+
+ d->extraRectangle.value().bottomRightRadius = -1.;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit bottomRightRadiusChanged();
}
/*!
@@ -554,6 +805,17 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
}
rectangle->setRadius(d->radius);
+ if (d->extraRectangle.isAllocated()) {
+ rectangle->setTopLeftRadius(d->extraRectangle.value().topLeftRadius);
+ rectangle->setTopRightRadius(d->extraRectangle.value().topRightRadius);
+ rectangle->setBottomLeftRadius(d->extraRectangle.value().bottomLeftRadius);
+ rectangle->setBottomRightRadius(d->extraRectangle.value().bottomRightRadius);
+ } else {
+ rectangle->setTopLeftRadius(-1.);
+ rectangle->setTopRightRadius(-1.);
+ rectangle->setBottomLeftRadius(-1.);
+ rectangle->setBottomRightRadius(-1.);
+ }
rectangle->setAntialiasing(antialiasing());
QGradientStops stops;
diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h
index 9e5b530382..690154cd65 100644
--- a/src/quick/items/qquickrectangle_p.h
+++ b/src/quick/items/qquickrectangle_p.h
@@ -23,13 +23,13 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickPen : public QObject
+class Q_QUICK_EXPORT QQuickPen : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged)
- Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
- Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged)
+ Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged FINAL)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
+ Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -58,7 +58,7 @@ private:
bool m_valid : 1;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickGradientStop : public QObject
+class Q_QUICK_EXPORT QQuickGradientStop : public QObject
{
Q_OBJECT
@@ -84,7 +84,7 @@ private:
QColor m_color;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickGradient : public QObject
+class Q_QUICK_EXPORT QQuickGradient : public QObject
{
Q_OBJECT
@@ -125,7 +125,7 @@ private:
};
class QQuickRectanglePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRectangle : public QQuickItem
+class Q_QUICK_EXPORT QQuickRectangle : public QQuickItem
{
Q_OBJECT
@@ -133,6 +133,10 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRectangle : public QQuickItem
Q_PROPERTY(QJSValue gradient READ gradient WRITE setGradient RESET resetGradient)
Q_PROPERTY(QQuickPen * border READ border CONSTANT)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
+ Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged RESET resetTopLeftRadius REVISION(6, 7) FINAL)
+ Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged RESET resetTopRightRadius REVISION(6, 7) FINAL)
+ Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged RESET resetBottomLeftRadius REVISION(6, 7) FINAL)
+ Q_PROPERTY(qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged RESET resetBottomRightRadius REVISION(6, 7) FINAL)
QML_NAMED_ELEMENT(Rectangle)
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -150,9 +154,26 @@ public:
qreal radius() const;
void setRadius(qreal radius);
+ qreal topLeftRadius() const;
+ void setTopLeftRadius(qreal radius);
+ void resetTopLeftRadius();
+ qreal topRightRadius() const;
+ void setTopRightRadius(qreal radius);
+ void resetTopRightRadius();
+ qreal bottomLeftRadius() const;
+ void setBottomLeftRadius(qreal radius);
+ void resetBottomLeftRadius();
+ qreal bottomRightRadius() const;
+ void setBottomRightRadius(qreal radius);
+ void resetBottomRightRadius();
+
Q_SIGNALS:
void colorChanged();
void radiusChanged();
+ Q_REVISION(6, 7) void topLeftRadiusChanged();
+ Q_REVISION(6, 7) void topRightRadiusChanged();
+ Q_REVISION(6, 7) void bottomLeftRadiusChanged();
+ Q_REVISION(6, 7) void bottomRightRadiusChanged();
protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
@@ -167,9 +188,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPen)
-QML_DECLARE_TYPE(QQuickGradientStop)
-QML_DECLARE_TYPE(QQuickGradient)
-QML_DECLARE_TYPE(QQuickRectangle)
-
#endif // QQUICKRECTANGLE_P_H
diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h
index 64bf215bc7..435cea1e52 100644
--- a/src/quick/items/qquickrectangle_p_p.h
+++ b/src/quick/items/qquickrectangle_p_p.h
@@ -17,6 +17,7 @@
#include "qquickitem_p.h"
#include <QtCore/qmetaobject.h>
+#include <private/qlazilyallocated_p.h>
QT_BEGIN_NAMESPACE
@@ -40,7 +41,26 @@ public:
QJSValue gradient;
QQuickPen *pen;
qreal radius;
+
+ struct ExtraData {
+ ExtraData()
+ : topLeftRadius(-1.),
+ topRightRadius(-1.),
+ bottomLeftRadius(-1.),
+ bottomRightRadius(-1.)
+ {
+ }
+
+ qreal topLeftRadius;
+ qreal topRightRadius;
+ qreal bottomLeftRadius;
+ qreal bottomRightRadius;
+ };
+ QLazilyAllocated<ExtraData> extraRectangle;
+
static int doUpdateSlotIdx;
+
+ void maybeSetImplicitAntialiasing();
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index 0575c5e5eb..3a92507141 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -26,7 +26,7 @@
#include <QtCore/private/qobject_p.h>
#include <QtQuick/private/qquickwindow_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
Management of the graphics devices, contexts, image and texture objects is up
to the application. The device or context that will be used by Qt Quick must
- be created before calling initialize(). The creation of the the texture object
+ be created before calling initialize(). The creation of the texture object
can be deferred, see below. Qt 5.4 introduces the ability for QOpenGLContext
to adopt existing native contexts. Together with QQuickRenderControl this
makes it possible to create a QOpenGLContext that shares with an external
@@ -281,6 +281,17 @@ int QQuickRenderControl::samples() const
\note This function does not need to be, and must not be, called when using
the \c software adaptation of Qt Quick.
+ With the default Qt Quick adaptation this function creates a new \l QRhi
+ object, similarly to what would happen with an on-screen QQuickWindow when
+ QQuickRenderControl was not used. To make this new QRhi object adopt some
+ existing device or context resource (e.g. use an existing QOpenGLContext
+ instead of creating a new one), use QQuickWindow::setGraphicsDevice() as
+ mentioned above. When the application wants to make the Qt Quick rendering
+ use an already existing \l QRhi object, that is possible as well via
+ \l QQuickGraphicsDevice::fromRhi(). When such a QQuickGraphicsDevice,
+ referencing an already existing QRhi, is set, there will be no new,
+ dedicated \l QRhi object created in initialize().
+
\since 6.0
\sa QQuickRenderTarget, QQuickGraphicsDevice, QQuickGraphicsConfiguration::preferredInstanceExtensions()
@@ -357,7 +368,8 @@ bool QQuickRenderControl::sync()
return false;
}
if (!d->cb) {
- qWarning("QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided");
+ qWarning("QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided "
+ "(perhaps beginFrame() was not called or it was unsuccessful?)");
return false;
}
cd->setCustomCommandBuffer(d->cb);
@@ -549,6 +561,16 @@ bool QQuickRenderControlPrivate::isRenderWindowFor(QQuickWindow *quickWin, const
return false;
}
+bool QQuickRenderControlPrivate::isRenderWindow(const QWindow *w)
+{
+ Q_Q(QQuickRenderControl);
+
+ if (window && w)
+ return q->renderWindowFor(window, nullptr) == w;
+
+ return false;
+}
+
/*!
\return the QQuickWindow this QQuickRenderControl is associated with.
@@ -565,6 +587,53 @@ QQuickWindow *QQuickRenderControl::window() const
}
/*!
+ \return the QRhi this QQuickRenderControl is associated with.
+
+ \note The QRhi exists only when initialize() has successfully completed.
+ Before that the return value is null.
+
+ \note This function is not applicable and returns null when using the
+ \c software adaptation of Qt Quick.
+
+ \since 6.6
+
+ \sa commandBuffer(), beginFrame(), endFrame()
+ */
+QRhi *QQuickRenderControl::rhi() const
+{
+ Q_D(const QQuickRenderControl);
+ return d->rhi;
+}
+
+/*!
+ \return the current command buffer.
+
+ Once beginFrame() is called, a QRhiCommandBuffer is set up automatically.
+ That is the command buffer Qt Quick scenegraph uses, but in some cases
+ applications may also want to query it, for example to issue resource
+ updates (for example, a texture readback).
+
+ The returned command buffer reference should only be used between
+ beginFrame() and endFrame(). There are specific exceptions, for example
+ calling
+ \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()} on
+ the command buffer right after endFrame(), but before the next
+ beginFrame(), is valid.
+
+ \note This function is not applicable and returns null when using the
+ \c software adaptation of Qt Quick.
+
+ \since 6.6
+
+ \sa rhi(), beginFrame(), endFrame()
+ */
+QRhiCommandBuffer *QQuickRenderControl::commandBuffer() const
+{
+ Q_D(const QQuickRenderControl);
+ return d->cb;
+}
+
+/*!
Specifies the start of a graphics frame. Calls to sync() or render() must
be enclosed by calls to beginFrame() and endFrame().
@@ -574,11 +643,11 @@ QQuickWindow *QQuickRenderControl::window() const
to the user of QQuickRenderControl to specify these points.
A typical update step, including initialization of rendering into an
- existing texture, could like like the following. The example snippet
+ existing texture, could look like the following. The example snippet
assumes Direct3D 11 but the same concepts apply other graphics APIs as
well.
- \badcode
+ \code
if (!m_quickInitialized) {
m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(m_engine->device(), m_engine->context()));
@@ -600,6 +669,15 @@ QQuickWindow *QQuickRenderControl::window() const
m_renderControl->endFrame(); // Qt Quick's rendering commands are submitted to the device context here
\endcode
+ \note This function does not need to be, and must not be, called when using
+ the \c software adaptation of Qt Quick.
+
+ \note Internally beginFrame() and endFrame() invoke
+ \l{QRhi::}{beginOffscreenFrame()} and \l{QRhi::}{endOffscreenFrame()},
+ respectively. This implies that there must not be a frame (neither
+ offscreen, nor swapchain-based) being recorded on the QRhi when
+ this function is called.
+
\since 6.0
\sa endFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
@@ -607,8 +685,18 @@ QQuickWindow *QQuickRenderControl::window() const
void QQuickRenderControl::beginFrame()
{
Q_D(QQuickRenderControl);
- if (!d->rhi || d->rhi->isRecordingFrame())
+ if (!d->rhi) {
+ qWarning("QQuickRenderControl: No QRhi in beginFrame()");
+ return;
+ }
+ if (d->frameStatus == QQuickRenderControlPrivate::RecordingFrame) {
+ qWarning("QQuickRenderControl: beginFrame() must be followed by a call to endFrame() before calling beginFrame() again");
return;
+ }
+ if (d->rhi->isRecordingFrame()) {
+ qWarning("QQuickRenderControl: Attempted to beginFrame() while the QRhi is already recording a frame");
+ return;
+ }
emit d->window->beforeFrameBegin();
@@ -639,6 +727,9 @@ void QQuickRenderControl::beginFrame()
scenegraph are submitted to the context or command queue, whichever is
applicable.
+ \note This function does not need to be, and must not be, called when using
+ the \c software adaptation of Qt Quick.
+
\since 6.0
\sa beginFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
@@ -646,11 +737,22 @@ void QQuickRenderControl::beginFrame()
void QQuickRenderControl::endFrame()
{
Q_D(QQuickRenderControl);
- if (!d->rhi || !d->rhi->isRecordingFrame())
+ if (!d->rhi) {
+ qWarning("QQuickRenderControl: No QRhi in endFrame()");
return;
+ }
+ if (d->frameStatus != QQuickRenderControlPrivate::RecordingFrame) {
+ qWarning("QQuickRenderControl: endFrame() must only be called after a successful beginFrame()");
+ return;
+ }
+ if (!d->rhi->isRecordingFrame()) {
+ qWarning("QQuickRenderControl: Attempted to endFrame() while the QRhi is not recording a frame");
+ return;
+ }
d->rhi->endOffscreenFrame();
- d->cb = nullptr;
+ // do not null out d->cb; this allows calling lastCompletedGpuTime() for example
+
d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
emit d->window->afterFrameEnd();
diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h
index 195b518dcc..1d8da84e57 100644
--- a/src/quick/items/qquickrendercontrol.h
+++ b/src/quick/items/qquickrendercontrol.h
@@ -14,6 +14,8 @@ class QQuickWindow;
class QOpenGLContext;
class QQuickRenderControlPrivate;
class QThread;
+class QRhi;
+class QRhiCommandBuffer;
class Q_QUICK_EXPORT QQuickRenderControl : public QObject
{
@@ -44,6 +46,9 @@ public:
QQuickWindow *window() const;
+ QRhi *rhi() const;
+ QRhiCommandBuffer *commandBuffer() const;
+
protected:
explicit QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject *parent = nullptr);
diff --git a/src/quick/items/qquickrendercontrol_p.h b/src/quick/items/qquickrendercontrol_p.h
index 29b83827d6..5b1d7e860e 100644
--- a/src/quick/items/qquickrendercontrol_p.h
+++ b/src/quick/items/qquickrendercontrol_p.h
@@ -25,7 +25,7 @@ class QRhiCommandBuffer;
class QOffscreenSurface;
class QQuickGraphicsConfiguration;
-class Q_QUICK_PRIVATE_EXPORT QQuickRenderControlPrivate : public QObjectPrivate
+class Q_QUICK_EXPORT QQuickRenderControlPrivate : public QObjectPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickRenderControl)
@@ -44,7 +44,7 @@ public:
}
static bool isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin);
- virtual bool isRenderWindow(const QWindow *w) { Q_UNUSED(w); return false; }
+ virtual bool isRenderWindow(const QWindow *w);
static void cleanup();
diff --git a/src/quick/items/qquickrendertarget.cpp b/src/quick/items/qquickrendertarget.cpp
index 7242a55d88..bce5b358ff 100644
--- a/src/quick/items/qquickrendertarget.cpp
+++ b/src/quick/items/qquickrendertarget.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickrendertarget_p.h"
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qsgrhisupport_p.h>
@@ -25,14 +25,16 @@ QQuickRenderTargetPrivate::QQuickRenderTargetPrivate()
{
}
-QQuickRenderTargetPrivate::QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate *other)
+QQuickRenderTargetPrivate::QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate &other)
: ref(1),
- type(other->type),
- pixelSize(other->pixelSize),
- devicePixelRatio(other->devicePixelRatio),
- sampleCount(other->sampleCount),
- u(other->u),
- mirrorVertically(other->mirrorVertically)
+ type(other.type),
+ pixelSize(other.pixelSize),
+ devicePixelRatio(other.devicePixelRatio),
+ sampleCount(other.sampleCount),
+ u(other.u),
+ customDepthTexture(other.customDepthTexture),
+ mirrorVertically(other.mirrorVertically),
+ multisampleResolve(other.multisampleResolve)
{
}
@@ -161,6 +163,84 @@ void QQuickRenderTarget::setMirrorVertically(bool enable)
}
/*!
+ \return the currently set depth texture or, in most cases, \nullptr.
+
+ The value is only non-null when setDepthTexture() was called.
+
+ \since 6.8
+ */
+QRhiTexture *QQuickRenderTarget::depthTexture() const
+{
+ return d->customDepthTexture;
+}
+
+/*!
+ Requests using the given \a texture as the depth or depth-stencil buffer.
+ Ownership of \a texture is not taken.
+
+ The request is only taken into account when relevant. For example, calling
+ this function has no effect with fromRhiRenderTarget(), fromPaintDevice(),
+ or fromOpenGLRenderBuffer().
+
+ Normally a depth-stencil buffer is created automatically, transparently to
+ the user of QQuickRenderTarget. Therefore, there is no need to call this
+ function in most cases when working with QQuickRenderTarget. In special
+ circumstances, it can however become essential to be able to provide a
+ texture to render depth (or depth and stencil) data into, instead of letting
+ Qt Quick create its own intermediate textures or buffers. An example of this
+ is \l{https://www.khronos.org/openxr/}{OpenXR} and its extensions such as
+ \l{https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_KHR_composition_layer_depth}{XR_KHR_composition_layer_depth}.
+ In order to "submit the depth buffer" to the XR compositor, one has to, in
+ practice, retrieve an already created depth (depth-stencil) texture from
+ OpenXR (from the XrSwapchain) and use that texture as the render target for
+ depth data. That would not be possible without this function.
+
+ \note The \a texture is always expected to be a non-multisample 2D texture
+ or texture array (for multiview). If MSAA is involved, the samples are
+ resolved into \a texture at the end of the render pass, regardless of having
+ the MultisampleResolve flag set or not. MSAA is only supported for depth
+ (depth-stencil) textures when the underlying 3D API supports this, and this
+ support is not universally available. See \l{QRhi::ResolveDepthStencil}{the
+ relevant QRhi feature flag} for details. When this is not supported and
+ multisampling is requested in combination with a custom depth texture, \a
+ texture is not going to be touched during rendering and a warning is
+ printed.
+
+ \since 6.8
+
+ \note When it comes to OpenGL and OpenGL ES, using depth textures is not
+ functional on OpenGL ES 2.0 and requires at least OpenGL ES 3.0. Multisample
+ (MSAA) support is not available without at least OpenGL ES 3.1, or OpenGL
+ 3.0 on desktop.
+ */
+void QQuickRenderTarget::setDepthTexture(QRhiTexture *texture)
+{
+ if (d->customDepthTexture == texture)
+ return;
+
+ detach();
+ d->customDepthTexture = texture;
+}
+
+/*!
+ \enum QQuickRenderTarget::Flag
+ Flags for the static QQuickRenderTarget constructor functions.
+
+ \value MultisampleResolve Indicates the the \c sampleCount argument is not
+ the number of samples for the provided texture (and that the texture is
+ still a non-multisample texture), but rather the desired samples for
+ multisample antialiasing. Triggers automatically creating and managing an
+ intermediate multisample texture (or texture array) as the color buffer,
+ transparently to the application. The samples are resolved into the provided
+ texture at the end of the render pass automatically. When this flag is not
+ set, and the \c sampleCount argument is greater than 1, it implies the
+ provided texture is multisample. The flag has no effect is the
+ \c sampleCount is 1 (indicating that multisampling is not involved).
+
+ \since 6.8
+*/
+
+/*!
\return a new QQuickRenderTarget referencing an OpenGL texture object
specified by \a textureId.
@@ -171,7 +251,7 @@ void QQuickRenderTarget::setMirrorVertically(bool enable)
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -212,8 +292,9 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint fo
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format);
- d->u.nativeTexture = { textureId, 0, uint(rhiFormat), 0 };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format, &formatFlags);
+ d->u.nativeTexture = { textureId, 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -228,7 +309,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint fo
\a pixelSize specifies the size of the image, in pixels. Currently
only 2D textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native
object is a multisample texture.
@@ -252,6 +333,90 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, const Q
}
/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing an OpenGL 2D texture or texture
+ array object specified by \a textureId.
+
+ \a format specifies the native internal format of the texture. Only texture
+ formats that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a textureId is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a textureId. This is the recommended approach to
+ perform MSAA when the native OpenGL texture is not already multisample.
+
+ When \a arraySize is greater than 1, it implies multiview rendering
+ (\l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview},
+ \l QRhiColorAttachment::setMultiViewCount()), which can be relevant with
+ VR/AR especially. In this case \a arraySize is the number of views,
+ typically \c 2. See \l QSGMaterial::viewCount() for details on enabling
+ multiview rendering within the Qt Quick scenegraph.
+
+ A depth-stencil buffer, if applicable, is created and used automatically.
+ When the color buffer is multisample, the depth-stencil buffer will
+ automatically be multisample too. For multiview rendering, the depth-stencil
+ texture will automatically be made an array with a matching \a arraySize.
+
+ The OpenGL object name \a textureId must be a valid 2D texture name in the
+ rendering context used by the Qt Quick scenegraph. When \a arraySize is
+ greater than 1, \a textureId must be a valid 2D texture array name.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \note The implementation of this overload is not compatible with OpenGL ES
+ 2.0 or 3.0, and requires OpenGL ES 3.1 at minimum. (or OpenGL 3.0 on
+ desktop)
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl, fromOpenGLTexture()
+ */
+QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!textureId) {
+ qWarning("QQuickRenderTarget: textureId is invalid");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format, &formatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { textureId, 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { textureId, 0, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+ }
+
+ return rt;
+}
+
+/*!
\return a new QQuickRenderTarget referencing an OpenGL renderbuffer object
specified by \a renderbufferId.
@@ -264,7 +429,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, const Q
\a pixelSize specifies the size of the image, in pixels.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample renderbuffer.
@@ -311,7 +476,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLRenderBuffer(uint renderbufferI
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -349,9 +514,9 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint form
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromD3D11(format, &flags);
- d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -366,7 +531,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint form
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -385,7 +550,208 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
{
return fromD3D11Texture(texture, 0 /* DXGI_FORMAT_UNKNOWN */, pixelSize, sampleCount);
}
-#endif
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a D3D11 texture object
+ specified by \a texture.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture and \a
+ sampleCount defines the number of samples desired. The resulting
+ QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture as its color attachment, and will resolve the samples
+ into \a texture. This is the recommended approach to perform MSAA when the
+ native texture is not already multisample.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl, fromD3D11Texture()
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount, Flags flags)
+{
+ QQuickRenderTarget rt = fromD3D11Texture(texture, format, pixelSize, sampleCount);
+ QQuickRenderTargetPrivate::get(&rt)->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+ return rt;
+}
+
+/*!
+ \return a new QQuickRenderTarget referencing a D3D12 texture object
+ specified by \a texture.
+
+ \a resourceState must a valid bitmask with bits from D3D12_RESOURCE_STATES,
+ specifying the resource's current state.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically.
+
+ \note the resulting QQuickRenderTarget does not own any native resources,
+ it merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.6
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D12Texture(void *texture,
+ int resourceState,
+ uint format,
+ const QSize &pixelSize,
+ int sampleCount)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), resourceState, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+
+ return rt;
+}
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a D3D12 2D texture or 2D
+ texture array object specified by \a texture.
+
+ \a resourceState must a valid bitmask with bits from D3D12_RESOURCE_STATES,
+ specifying the resource's current state.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is the DXGI_FORMAT used for the render target view (RTV).
+ Often the same as \a format. Functional only when
+ \l{https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html}{relaxed
+ format casting} is supported by the driver, the argument is ignored otherwise.
+ In practice support is expected to be always available on Windows 10 1703
+ and newer.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a texture. This is the recommended approach to
+ perform MSAA when the native D3D12 texture is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering
+ (\l{https://microsoft.github.io/DirectX-Specs/d3d/ViewInstancing.html}{view
+ instancing}), which can be relevant with VR/AR especially. \a arraySize is
+ the number of views, typically \c 2. See \l QSGMaterial::viewCount() for
+ details on enabling multiview rendering within the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D12Texture(void *texture,
+ int resourceState,
+ uint format,
+ uint viewFormat,
+ const QSize &pixelSize,
+ int sampleCount,
+ int arraySize,
+ Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(texture), resourceState, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(texture), resourceState, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
+#endif // Q_OS_WIN
/*!
\return a new QQuickRenderTarget referencing a Metal texture object
@@ -397,7 +763,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -414,7 +780,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uint format,
const QSize &pixelSize, int sampleCount)
{
@@ -435,9 +801,9 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uin
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &flags);
- d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -452,7 +818,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uin
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -471,6 +837,94 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
{
return fromMetalTexture(texture, 0 /* MTLPixelFormatInvalid */, pixelSize, sampleCount);
}
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a Metal 2D texture or 2D
+ texture array given in \a texture.
+
+ \a format specifies the MTLPixelFormat of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is usually set to the same value as \a format. In some cases,
+ such as when rendering into a texture with a \c{_SRGB} format and the
+ implicit linear->sRGB conversion on shader writes is not wanted, the value
+ can be different. Note however that the value may be ignored by Qt, when at
+ run time QRhi reports that the \l{QRhi::TextureViewFormat} feature is
+ unsupported.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a texture. This is the recommended approach to
+ perform MSAA when the native Metal texture is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering, which can be relevant with VR/AR
+ especially. \a arraySize is the number of views, typically \c 2. See
+ \l QSGMaterial::viewCount() for details on enabling multiview rendering within
+ the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uint format, uint viewFormat,
+ const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(texture), 0, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
#endif
/*!
@@ -484,7 +938,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -502,8 +956,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
#if QT_CONFIG(vulkan) || defined(Q_QDOC)
-QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format,
- const QSize &pixelSize, int sampleCount)
+QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount)
{
QQuickRenderTarget rt;
QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
@@ -522,9 +975,9 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &flags);
- d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &formatFlags);
+ d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -532,14 +985,14 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
/*!
\overload
- \return a new QQuickRenderTarget referencing n Vulkan image object specified
+ \return a new QQuickRenderTarget referencing a Vulkan image object specified
by \a image. The image is assumed to have a format of
VK_FORMAT_R8G8B8A8_UNORM.
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -558,11 +1011,113 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
{
return fromVulkanImage(image, layout, VK_FORMAT_UNDEFINED, pixelSize, sampleCount);
}
-#endif
/*!
- \internal
+ \overload
+
+ \return a new QQuickRenderTarget referencing a Vulkan image object
+ specified by \a image. The current \a layout of the image must be provided
+ as well. The image must be either a 2D texture or 2D texture array.
+
+ \a format specifies the VkFormat of the image. Only image formats that are
+ supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is usually set to the same value as \a format. In some cases,
+ such as when rendering into a texture with a \c{_SRGB} format and the
+ implicit linear->sRGB conversion on shader writes is not wanted, the value
+ can be different. (for example, a \a format of \c VK_FORMAT_R8G8B8A8_SRGB
+ and \a viewFormat of \c VK_FORMAT_R8G8B8A8_UNORM).
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a image is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a image. This is the recommended approach to
+ perform MSAA when the native Vulkan image is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering
+ (\l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_multiview.html}{VK_KHR_multiview}),
+ which can be relevant with VR/AR especially. \a arraySize is the number of
+ views, typically \c 2. See \l QSGMaterial::viewCount() for details on
+ enabling multiview rendering within the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
+QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, VkFormat viewFormat,
+ const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (image == VK_NULL_HANDLE) {
+ qWarning("QQuickRenderTarget: image is invalid");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(image), layout, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
+#endif // Vulkan
+
+/*!
+ \return a new QQuickRenderTarget referencing an existing \a renderTarget.
+
+ \a renderTarget will in most cases be a QRhiTextureRenderTarget, which
+ allows directing the Qt Quick scene's rendering into a QRhiTexture.
+
+ \note the resulting QQuickRenderTarget does not own \a renderTarget and any
+ underlying native resources, it merely contains references and the
+ associated metadata of the size and sample count. It is the caller's
+ responsibility to ensure that the referenced resources exists as long as
+ necessary.
+
+ \since 6.6
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+*/
QQuickRenderTarget QQuickRenderTarget::fromRhiRenderTarget(QRhiRenderTarget *renderTarget)
{
QQuickRenderTarget rt;
@@ -628,7 +1183,8 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
|| d->pixelSize != other.d->pixelSize
|| d->devicePixelRatio != other.d->devicePixelRatio
|| d->sampleCount != other.d->sampleCount
- || d->mirrorVertically != other.d->mirrorVertically)
+ || d->mirrorVertically != other.d->mirrorVertically
+ || d->multisampleResolve != other.d->multisampleResolve)
{
return false;
}
@@ -638,9 +1194,21 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
break;
case QQuickRenderTargetPrivate::Type::NativeTexture:
if (d->u.nativeTexture.object != other.d->u.nativeTexture.object
- || d->u.nativeTexture.layout != other.d->u.nativeTexture.layout
+ || d->u.nativeTexture.layoutOrState != other.d->u.nativeTexture.layoutOrState
|| d->u.nativeTexture.rhiFormat != other.d->u.nativeTexture.rhiFormat
- || d->u.nativeTexture.rhiFlags != other.d->u.nativeTexture.rhiFlags)
+ || d->u.nativeTexture.rhiFormatFlags != other.d->u.nativeTexture.rhiFormatFlags
+ || d->u.nativeTexture.rhiViewFormat != other.d->u.nativeTexture.rhiViewFormat
+ || d->u.nativeTexture.rhiViewFormatFlags != other.d->u.nativeTexture.rhiViewFormatFlags)
+ return false;
+ break;
+ case QQuickRenderTargetPrivate::Type::NativeTextureArray:
+ if (d->u.nativeTextureArray.object != other.d->u.nativeTextureArray.object
+ || d->u.nativeTextureArray.layoutOrState != other.d->u.nativeTextureArray.layoutOrState
+ || d->u.nativeTextureArray.arraySize != other.d->u.nativeTextureArray.arraySize
+ || d->u.nativeTextureArray.rhiFormat != other.d->u.nativeTextureArray.rhiFormat
+ || d->u.nativeTextureArray.rhiFormatFlags != other.d->u.nativeTextureArray.rhiFormatFlags
+ || d->u.nativeTextureArray.rhiViewFormat != other.d->u.nativeTextureArray.rhiViewFormat
+ || d->u.nativeTextureArray.rhiViewFormatFlags != other.d->u.nativeTextureArray.rhiViewFormatFlags)
return false;
break;
case QQuickRenderTargetPrivate::Type::NativeRenderbuffer:
@@ -662,21 +1230,260 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
return true;
}
-static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment,
+static bool createRhiRenderTargetWithRenderBuffer(QRhiRenderBuffer *renderBuffer,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+
+ std::unique_ptr<QRhiRenderBuffer> depthStencil;
+ if (dst->implicitBuffers.depthStencil) {
+ if (dst->implicitBuffers.depthStencil->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencil->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencil);
+ dst->implicitBuffers.depthStencil = nullptr;
+ }
+ }
+ dst->implicitBuffers.reset(rhi);
+
+ if (!depthStencil) {
+ depthStencil.reset(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment(renderBuffer);
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ rtDesc.setDepthStencilBuffer(depthStencil.get());
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget with renderbuffer"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build renderbuffer-based render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ dst->implicitBuffers.depthStencil = depthStencil.release();
+
+ return true;
+}
+
+static bool createRhiRenderTarget(QRhiTexture *texture,
const QSize &pixelSize,
int sampleCount,
+ bool multisampleResolve,
QRhi *rhi,
QQuickWindowRenderTarget *dst)
{
- std::unique_ptr<QRhiRenderBuffer> depthStencil(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
- if (!depthStencil->create()) {
- qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
- return false;
+ // Simple path: no user-supplied depth texture. So create our own
+ // depth-stencil buffer, using renderbuffers (so this is still GLES 2.0
+ // compatible), with MSAA support being GLES 3.0 compatible.
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiRenderBuffer> depthStencil;
+ if (dst->implicitBuffers.depthStencil) {
+ if (dst->implicitBuffers.depthStencil->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencil->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencil);
+ dst->implicitBuffers.depthStencil = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
}
+ dst->implicitBuffers.reset(rhi);
+
+ if (!depthStencil) {
+ depthStencil.reset(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTexture(multisampleTextureFormat, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample color buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
rtDesc.setDepthStencilBuffer(depthStencil.get());
std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build texture render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ dst->implicitBuffers.depthStencil = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ return true;
+}
+
+static bool createRhiRenderTargetWithDepthTexture(QRhiTexture *texture,
+ QRhiTexture *depthTexture,
+ const QSize &pixelSize,
+ int sampleCount,
+ bool multisampleResolve,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ // This version takes a user-supplied depthTexture. That texture is always
+ // non-multisample. If sample count is > 1, we still need our own
+ // multisample depth-stencil buffer, and the depth(stencil) data is expected
+ // to be resolved (and written out) to depthTexture, _if_ the underlying API
+ // supports it (see QRhi's ResolveDepthStencil feature). The intermediate,
+ // multisample depth-stencil buffer must be a texture here (not
+ // renderbuffer), specifically for OpenGL ES and its related multisample
+ // extensions.
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiTexture> depthStencil;
+ if (dst->implicitBuffers.depthStencilTexture) {
+ if (dst->implicitBuffers.depthStencilTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencilTexture->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencilTexture);
+ dst->implicitBuffers.depthStencilTexture = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
+ }
+
+ dst->implicitBuffers.reset(rhi);
+
+ bool needsDepthStencilBuffer = true;
+ if (sampleCount <= 1) {
+ depthStencil.reset();
+ needsDepthStencilBuffer = false;
+ }
+ if (depthTexture->pixelSize() != pixelSize) {
+ qWarning("Custom depth texture size (%dx%d) does not match the QQuickRenderTarget (%dx%d)",
+ depthTexture->pixelSize().width(),
+ depthTexture->pixelSize().height(),
+ pixelSize.width(),
+ pixelSize.height());
+ return false;
+ }
+ if (depthTexture->sampleCount() > 1) {
+ qWarning("Custom depth texture cannot be multisample");
+ return false;
+ }
+ if (needsDepthStencilBuffer && !depthStencil) {
+ depthStencil.reset(rhi->newTexture(QRhiTexture::D24S8, pixelSize, sampleCount, QRhiTexture::RenderTarget));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil texture for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTexture(multisampleTextureFormat, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample color buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
+
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ if (sampleCount > 1) {
+ rtDesc.setDepthTexture(depthStencil.get());
+ if (rhi->isFeatureSupported(QRhi::ResolveDepthStencil))
+ rtDesc.setDepthResolveTexture(depthTexture);
+ else
+ qWarning("Depth-stencil resolve is not supported by the underlying 3D API, depth contents will not be resolved");
+ } else {
+ rtDesc.setDepthTexture(depthTexture);
+ }
+
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget"));
std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
rt->setRenderPassDescriptor(rp.get());
@@ -685,37 +1492,222 @@ static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment,
return false;
}
- dst->renderTarget = rt.release();
- dst->rpDesc = rp.release();
- dst->depthStencil = depthStencil.release();
- dst->owns = true; // ownership of the native resource itself is not transferred but the QRhi objects are on us now
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ if (depthStencil)
+ dst->implicitBuffers.depthStencilTexture = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ return true;
+}
+
+static bool createRhiRenderTargetMultiView(QRhiTexture *texture,
+ QRhiTexture *maybeCustomDepthTexture,
+ const QSize &pixelSize,
+ int arraySize,
+ int sampleCount,
+ bool multisampleResolve,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ // Multiview path, working with texture arrays. Optionally with a
+ // user-supplied, non-multisample depth texture (array). (same semantics
+ // then as with createRhiRenderTargetWithDepthTexture, but everything is a
+ // 2D texture array here)
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiTexture> depthStencil;
+ if (dst->implicitBuffers.depthStencilTexture) {
+ if (dst->implicitBuffers.depthStencilTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencilTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.depthStencilTexture->arraySize() == arraySize)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencilTexture);
+ dst->implicitBuffers.depthStencilTexture = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->arraySize() == arraySize
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
+ }
+
+ dst->implicitBuffers.reset(rhi);
+
+ bool needsDepthStencilBuffer = true;
+ if (maybeCustomDepthTexture) {
+ if (sampleCount <= 1) {
+ depthStencil.reset();
+ needsDepthStencilBuffer = false;
+ }
+ if (maybeCustomDepthTexture->arraySize() != arraySize) {
+ qWarning("Custom depth texture array size (%d) does not match QQuickRenderTarget (%d)",
+ maybeCustomDepthTexture->arraySize(), arraySize);
+ return false;
+ }
+ if (maybeCustomDepthTexture->pixelSize() != pixelSize) {
+ qWarning("Custom depth texture size (%dx%d) does not match the QQuickRenderTarget (%dx%d)",
+ maybeCustomDepthTexture->pixelSize().width(),
+ maybeCustomDepthTexture->pixelSize().height(),
+ pixelSize.width(),
+ pixelSize.height());
+ return false;
+ }
+ if (maybeCustomDepthTexture->sampleCount() > 1) {
+ qWarning("Custom depth texture cannot be multisample");
+ return false;
+ }
+ }
+ if (needsDepthStencilBuffer && !depthStencil) {
+ depthStencil.reset(rhi->newTextureArray(QRhiTexture::D24S8, arraySize, pixelSize, sampleCount, QRhiTexture::RenderTarget));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer (multiview) for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil texture array for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTextureArray(multisampleTextureFormat, arraySize, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer (multiview) for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample texture array for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ colorAttachment.setMultiViewCount(arraySize);
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
+
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ if (sampleCount > 1) {
+ rtDesc.setDepthTexture(depthStencil.get());
+ if (maybeCustomDepthTexture) {
+ if (rhi->isFeatureSupported(QRhi::ResolveDepthStencil))
+ rtDesc.setDepthResolveTexture(maybeCustomDepthTexture);
+ else
+ qWarning("Depth-stencil resolve is not supported by the underlying 3D API, depth contents will not be resolved");
+ }
+ } else {
+ if (depthStencil)
+ rtDesc.setDepthTexture(depthStencil.get());
+ else if (maybeCustomDepthTexture)
+ rtDesc.setDepthTexture(maybeCustomDepthTexture);
+ }
+
+ QRhiTextureRenderTarget::Flags rtFlags;
+ if (!maybeCustomDepthTexture)
+ rtFlags |= QRhiTextureRenderTarget::DoNotStoreDepthStencilContents;
+
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc, rtFlags));
+ rt->setName(QByteArrayLiteral("RT for multiview QQuickRenderTarget"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build multiview texture render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ if (depthStencil)
+ dst->implicitBuffers.depthStencilTexture = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ dst->rt.multiViewCount = arraySize;
return true;
}
bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst)
{
+ // dst->implicitBuffers may contain valid objects. If so, and their
+ // properties are suitable, they are expected to be reused. Once taken what
+ // we can reuse, it needs to be reset().
+
switch (type) {
case Type::Null:
- dst->renderTarget = nullptr;
- dst->paintDevice = nullptr;
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
return true;
case Type::NativeTexture:
{
- const auto format = u.nativeTexture.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
- : QRhiTexture::Format(u.nativeTexture.rhiFormat);
- const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTexture.rhiFlags);
- std::unique_ptr<QRhiTexture> texture(rhi->newTexture(format, pixelSize, sampleCount, flags));
- if (!texture->createFrom({ u.nativeTexture.object, u.nativeTexture.layout })) {
+ QRhiTexture::Format format = u.nativeTexture.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTexture.rhiFormat);
+ QRhiTexture::Format viewFormat = u.nativeTexture.rhiViewFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTexture.rhiViewFormat);
+ const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTexture.rhiFormatFlags);
+ std::unique_ptr<QRhiTexture> texture(rhi->newTexture(format, pixelSize, multisampleResolve ? 1 : sampleCount, flags));
+ const bool textureIsSrgb = flags.testFlag(QRhiTexture::sRGB);
+ const bool viewIsSrgb = QRhiTexture::Flags(u.nativeTexture.rhiViewFormatFlags).testFlag(QRhiTexture::sRGB);
+ if (viewFormat != format || viewIsSrgb != textureIsSrgb)
+ texture->setWriteViewFormat({ viewFormat, viewIsSrgb });
+ if (!texture->createFrom({ u.nativeTexture.object, u.nativeTexture.layoutOrState })) {
qWarning("Failed to build wrapper texture for QQuickRenderTarget");
return false;
}
- QRhiColorAttachment att(texture.get());
- if (!createRhiRenderTarget(att, pixelSize, sampleCount, rhi, dst))
+ if (customDepthTexture) {
+ if (!createRhiRenderTargetWithDepthTexture(texture.get(), customDepthTexture, pixelSize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ } else {
+ if (!createRhiRenderTarget(texture.get(), pixelSize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ }
+ dst->res.texture = texture.release();
+ }
+ return true;
+
+ case Type::NativeTextureArray:
+ {
+ QRhiTexture::Format format = u.nativeTextureArray.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTextureArray.rhiFormat);
+ QRhiTexture::Format viewFormat = u.nativeTextureArray.rhiViewFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTextureArray.rhiViewFormat);
+ const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTextureArray.rhiFormatFlags);
+ const int arraySize = u.nativeTextureArray.arraySize;
+ std::unique_ptr<QRhiTexture> texture(rhi->newTextureArray(format, arraySize, pixelSize, multisampleResolve ? 1 : sampleCount, flags));
+ const bool textureIsSrgb = flags.testFlag(QRhiTexture::sRGB);
+ const bool viewIsSrgb = QRhiTexture::Flags(u.nativeTextureArray.rhiViewFormatFlags).testFlag(QRhiTexture::sRGB);
+ if (viewFormat != format || viewIsSrgb != textureIsSrgb)
+ texture->setWriteViewFormat({ viewFormat, viewIsSrgb });
+ if (!texture->createFrom({ u.nativeTextureArray.object, u.nativeTextureArray.layoutOrState })) {
+ qWarning("Failed to build wrapper texture array for QQuickRenderTarget");
return false;
- dst->texture = texture.release();
+ }
+ if (!createRhiRenderTargetMultiView(texture.get(), customDepthTexture, pixelSize, arraySize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ dst->res.texture = texture.release();
}
return true;
@@ -726,27 +1718,59 @@ bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst
qWarning("Failed to build wrapper renderbuffer for QQuickRenderTarget");
return false;
}
- QRhiColorAttachment att(renderbuffer.get());
- if (!createRhiRenderTarget(att, pixelSize, sampleCount, rhi, dst))
+ if (customDepthTexture)
+ qWarning("Custom depth texture is not supported with renderbuffers in QQuickRenderTarget");
+ if (!createRhiRenderTargetWithRenderBuffer(renderbuffer.get(), pixelSize, sampleCount, rhi, dst))
return false;
- dst->renderBuffer = renderbuffer.release();
+ dst->res.renderBuffer = renderbuffer.release();
}
return true;
case Type::RhiRenderTarget:
- dst->renderTarget = u.rhiRt;
- dst->rpDesc = u.rhiRt->renderPassDescriptor(); // just for QQuickWindowRenderTarget::reset()
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
+ dst->rt.renderTarget = u.rhiRt;
+ dst->rt.owns = false;
+ if (dst->rt.renderTarget->resourceType() == QRhiResource::TextureRenderTarget) {
+ auto texRt = static_cast<QRhiTextureRenderTarget *>(dst->rt.renderTarget);
+ const QRhiTextureRenderTargetDescription desc = texRt->description();
+ bool first = true;
+ for (auto it = desc.cbeginColorAttachments(), end = desc.cendColorAttachments(); it != end; ++it) {
+ if (it->multiViewCount() <= 1)
+ continue;
+ if (first || dst->rt.multiViewCount == it->multiViewCount()) {
+ first = false;
+ if (it->texture() && it->texture()->flags().testFlag(QRhiTexture::TextureArray)) {
+ if (it->texture()->arraySize() >= it->layer() + it->multiViewCount()) {
+ dst->rt.multiViewCount = it->multiViewCount();
+ } else {
+ qWarning("Invalid QQuickRenderTarget; needs at least %d elements in texture array, got %d",
+ it->layer() + it->multiViewCount(),
+ it->texture()->arraySize());
+ return false;
+ }
+ } else {
+ qWarning("Invalid QQuickRenderTarget; multiview requires a texture array");
+ return false;
+ }
+ } else {
+ qWarning("Inconsistent multiViewCount in QQuickRenderTarget (was %d, now found an attachment with %d)",
+ dst->rt.multiViewCount, it->multiViewCount());
+ return false;
+ }
+ }
+ }
+ if (customDepthTexture)
+ qWarning("Custom depth texture is not supported with QRhiRenderTarget in QQuickRenderTarget");
return true;
+
case Type::PaintDevice:
- dst->paintDevice = u.paintDevice;
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
+ dst->sw.paintDevice = u.paintDevice;
+ dst->sw.owns = false;
return true;
-
- default:
- break;
}
- return false;
+
+ Q_UNREACHABLE_RETURN(false);
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrendertarget.h b/src/quick/items/qquickrendertarget.h
index ded9c80274..1b93db7695 100644
--- a/src/quick/items/qquickrendertarget.h
+++ b/src/quick/items/qquickrendertarget.h
@@ -11,7 +11,7 @@
#include <QtGui/qvulkaninstance.h>
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
Q_FORWARD_DECLARE_OBJC_CLASS(MTLTexture);
#endif
@@ -19,11 +19,17 @@ QT_BEGIN_NAMESPACE
class QQuickRenderTargetPrivate;
class QRhiRenderTarget;
+class QRhiTexture;
class QPaintDevice;
class Q_QUICK_EXPORT QQuickRenderTarget
{
public:
+ enum class Flag {
+ MultisampleResolve = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
QQuickRenderTarget();
~QQuickRenderTarget();
QQuickRenderTarget(const QQuickRenderTarget &other);
@@ -37,25 +43,36 @@ public:
bool mirrorVertically() const;
void setMirrorVertically(bool enable);
+ QRhiTexture *depthTexture() const;
+ void setDepthTexture(QRhiTexture *texture);
+
#if QT_CONFIG(opengl) || defined(Q_QDOC)
- static QQuickRenderTarget fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount = 1);
static QQuickRenderTarget fromOpenGLTexture(uint textureId, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags);
+
static QQuickRenderTarget fromOpenGLRenderBuffer(uint renderbufferId, const QSize &pixelSize, int sampleCount = 1);
#endif
#if defined(Q_OS_WIN) || defined(Q_QDOC)
- static QQuickRenderTarget fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount = 1);
static QQuickRenderTarget fromD3D11Texture(void *texture, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount, Flags flags);
+
+ static QQuickRenderTarget fromD3D12Texture(void *texture, int resourceState, uint format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromD3D12Texture(void *texture, int resourceState, uint format, uint viewFormat, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
- static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, uint format, const QSize &pixelSize, int sampleCount = 1);
+#if QT_CONFIG(metal) || defined(Q_QDOC)
static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, uint format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, uint format, uint viewFormat, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags);
#endif
#if QT_CONFIG(vulkan) || defined(Q_QDOC)
- static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount = 1);
static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, VkFormat viewFormat, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags);
#endif
static QQuickRenderTarget fromRhiRenderTarget(QRhiRenderTarget *renderTarget);
@@ -74,6 +91,8 @@ private:
{ return !lhs.isEqual(rhs); }
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickRenderTarget::Flags)
+
QT_END_NAMESPACE
#endif // QQUICKRENDERTARGET_H
diff --git a/src/quick/items/qquickrendertarget_p.h b/src/quick/items/qquickrendertarget_p.h
index dd781709af..d2e9962693 100644
--- a/src/quick/items/qquickrendertarget_p.h
+++ b/src/quick/items/qquickrendertarget_p.h
@@ -22,20 +22,21 @@
QT_BEGIN_NAMESPACE
class QRhi;
-class QQuickWindowRenderTarget;
+struct QQuickWindowRenderTarget;
-class Q_QUICK_PRIVATE_EXPORT QQuickRenderTargetPrivate
+class Q_QUICK_EXPORT QQuickRenderTargetPrivate
{
public:
static QQuickRenderTargetPrivate *get(QQuickRenderTarget *rt) { return rt->d; }
static const QQuickRenderTargetPrivate *get(const QQuickRenderTarget *rt) { return rt->d; }
QQuickRenderTargetPrivate();
- QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate *other);
+ QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate &other);
bool resolve(QRhi *rhi, QQuickWindowRenderTarget *dst);
enum class Type {
Null,
NativeTexture,
+ NativeTextureArray,
NativeRenderbuffer,
RhiRenderTarget,
PaintDevice
@@ -48,18 +49,32 @@ public:
int sampleCount = 1;
struct NativeTexture {
quint64 object;
- int layout;
+ int layoutOrState;
uint rhiFormat;
- uint rhiFlags;
+ uint rhiFormatFlags;
+ uint rhiViewFormat;
+ uint rhiViewFormatFlags;
+ };
+ struct NativeTextureArray {
+ quint64 object;
+ int layoutOrState;
+ int arraySize;
+ uint rhiFormat;
+ uint rhiFormatFlags;
+ uint rhiViewFormat;
+ uint rhiViewFormatFlags;
};
union {
NativeTexture nativeTexture;
+ NativeTextureArray nativeTextureArray;
quint64 nativeRenderbufferObject;
QRhiRenderTarget *rhiRt;
QPaintDevice *paintDevice;
} u;
+ QRhiTexture *customDepthTexture = nullptr;
bool mirrorVertically = false;
+ bool multisampleResolve = false;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrepeater_p.h b/src/quick/items/qquickrepeater_p.h
index 2a3b077feb..e3519b9602 100644
--- a/src/quick/items/qquickrepeater_p.h
+++ b/src/quick/items/qquickrepeater_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQmlChangeSet;
class QQuickRepeaterPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRepeater : public QQuickItem
+class Q_QUICK_EXPORT QQuickRepeater : public QQuickItem
{
Q_OBJECT
@@ -79,6 +79,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRepeater)
-
#endif // QQUICKREPEATER_P_H
diff --git a/src/quick/items/qquickrhiitem.cpp b/src/quick/items/qquickrhiitem.cpp
new file mode 100644
index 0000000000..69b6bb15ed
--- /dev/null
+++ b/src/quick/items/qquickrhiitem.cpp
@@ -0,0 +1,1144 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickrhiitem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QQuickRhiItem
+ \inmodule QtQuick
+ \since 6.7
+
+ \brief The QQuickRhiItem class is a portable alternative to
+ QQuickFramebufferObject that is not tied to OpenGL, but rather allows
+ integrating rendering with the QRhi APIs with Qt Quick.
+
+ \preliminary
+
+ \note QQuickRhiItem is in tech preview in Qt 6.7. \b {The API is under
+ development and subject to change.}
+
+ QQuickRhiItem is effectively the counterpart of \l QRhiWidget in the world of
+ Qt Quick. Both of these are meant to be subclassed, and they both enable
+ recording QRhi-based rendering that targets an offscreen color buffer. The
+ resulting 2D image is then composited with the rest of the Qt Quick scene.
+
+ \note While QQuickRhiItem is a public Qt API, the QRhi family of classes in
+ the Qt Gui module, including QShader and QShaderDescription, offer limited
+ compatibility guarantees. There are no source or binary compatibility
+ guarantees for these classes, meaning the API is only guaranteed to work
+ with the Qt version the application was developed against. Source
+ incompatible changes are however aimed to be kept at a minimum and will
+ only be made in minor releases (6.7, 6.8, and so on). \c{qquickrhiitem.h}
+ does not directly include any QRhi-related headers. To use those classes
+ when implementing a QQuickRhiItem subclass, link to
+ \c{Qt::GuiPrivate} (if using CMake), and include the appropriate headers
+ with the \c rhi prefix, for example \c{#include <rhi/qrhi.h>}.
+
+ QQuickRhiItem is a replacement for the legacy \l QQuickFramebufferObject
+ class. The latter is inherently tied to OpenGL / OpenGL ES, whereas
+ QQuickRhiItem works with the QRhi classes, allowing to run the same
+ rendering code with Vulkan, Metal, Direct 3D 11/12, and OpenGL / OpenGL ES.
+ Conceptually and functionally they are very close, and migrating from
+ QQuickFramebufferObject to QQuickRhiItem is straightforward.
+ QQuickFramebufferObject continues to be available to ensure compatibility
+ for existing application code that works directly with the OpenGL API.
+
+ \note QQuickRhiItem will not be functional when using the \c software
+ adaptation of the Qt Quick scene graph.
+
+ On most platforms, the scene graph rendering, and thus the rendering
+ performed by the QQuickRhiItem will occur on a \l {Scene Graph and
+ Rendering}{dedicated thread}. For this reason, the QQuickRhiItem class
+ enforces a strict separation between the item implementation (the
+ QQuickItem subclass) and the actual rendering logic. All item logic, such
+ as properties and UI-related helper functions exposed to QML must be
+ located in the QQuickRhiItem subclass. Everything that relates to rendering
+ must be located in the QQuickRhiItemRenderer class. To avoid race
+ conditions and read/write issues from two threads it is important that the
+ renderer and the item never read or write shared variables. Communication
+ between the item and the renderer should primarily happen via the
+ QQuickRhiItem::synchronize() function. This function will be called on the
+ render thread while the GUI thread is blocked. Using queued connections or
+ events for communication between item and renderer is also possible.
+
+ Applications must subclass both QQuickRhiItem and QQuickRhiItemRenderer.
+ The pure virtual createRenderer() function must be reimplemented to return
+ a new instance of the QQuickRhiItemRenderer subclass.
+
+ As with QRhiWidget, QQuickRhiItem automatically managed the color buffer,
+ which is a 2D texture (QRhiTexture) normally, or a QRhiRenderBuffer when
+ multisampling is in use. (some 3D APIs differentiate between textures and
+ renderbuffers, while with some others the underlying native resource is the
+ same; renderbuffers are used mainly to allow multisampling with OpenGL ES
+ 3.0)
+
+ The size of the texture will by default adapt to the size of the item (with
+ the \l{QQuickWindow::effectiveDevicePixelRatio()}{device pixel ratio} taken
+ into account). If the item size changes, the texture is recreated with the
+ correct size. If a fixed size is preferred, set \l fixedColorBufferWidth and
+ \l fixedColorBufferHeight to non-zero values.
+
+ QQuickRhiItem is a \l{QSGTextureProvider}{texture provider} and can be used
+ directly in \l {ShaderEffect}{ShaderEffects} and other classes that consume
+ texture providers.
+
+ While not a primary use case, QQuickRhiItem also allows incorporating
+ rendering code that directly uses a 3D graphics API such as Vulkan, Metal,
+ Direct 3D, or OpenGL. See \l QRhiCommandBuffer::beginExternal() for details
+ on recording native commands within a QRhi render pass, as well as
+ \l QRhiTexture::createFrom() for a way to wrap an existing native texture and
+ then use it with QRhi in a subsequent render pass. See also
+ \l QQuickGraphicsConfiguration regarding configuring the native 3D API
+ environment (e.g. device extensions) and note that the \l QQuickWindow can be
+ associated with a custom \l QVulkanInstance by calling
+ \l QWindow::setVulkanInstance() early enough.
+
+ \note QQuickRhiItem always uses the same QRhi instance the QQuickWindow
+ uses (and by extension, the same OpenGL context, Vulkan device, etc.). To
+ choose which underlying 3D graphics API is used, call
+ \l{QQuickWindow::setGraphicsApi()}{setGraphicsApi()} on the QQuickWindow
+ early enough. Changing it is not possible once the scene graph has
+ initialized, and all QQuickRhiItem instances in the scene will render using
+ the same 3D API.
+
+ \section2 A simple example
+
+ Take the following subclass of QQuickRhiItem. It is shown here in complete
+ form. It renders a single triangle with a perspective projection, where the
+ triangle is rotated based on the \c angle property of the custom item.
+ (meaning it can be driven for example with animations such as
+ \l NumberAnimation from QML)
+
+ \snippet qquickrhiitem/qquickrhiitem_intro.cpp 0
+
+ It is notable that this simple class is almost exactly the same as the code
+ shown in the \l QRhiWidget introduction. The vertex and fragment shaders
+ are the same as shown there.
+
+ Once exposed to QML (note the \c QML_NAMED_ELEMENT), our custom item can be
+ instantiated in any scene. (after importing the appropriate \c URI specified
+ for \l{qt6_add_qml_module}{qt_add_qml_module} in the CMake project)
+
+ \code
+ ExampleRhiItem {
+ anchors.fill: parent
+ anchors.margins: 10
+ NumberAnimation on angle { from: 0; to: 360: duration: 5000; loops: Animation.Infinite }
+ }
+ \endcode
+
+ See \l{Scene Graph - RHI Texture Item} for a more complex example.
+
+ \sa QQuickRhiItemRenderer, {Scene Graph - RHI Texture Item}, QRhi, {Scene Graph and Rendering}
+ */
+
+/*!
+ \class QQuickRhiItemRenderer
+ \inmodule QtQuick
+ \since 6.7
+
+ \brief A QQuickRhiItemRenderer implements the rendering logic of a
+ QQuickRhiItem.
+
+ \preliminary
+
+ \note QQuickRhiItem and QQuickRhiItemRenderer are in tech preview in Qt
+ 6.7. \b {The API is under development and subject to change.}
+
+ \sa QQuickRhiItem, QRhi
+ */
+
+QQuickRhiItemNode::QQuickRhiItemNode(QQuickRhiItem *item)
+ : m_item(item)
+{
+ m_window = m_item->window();
+ connect(m_window, &QQuickWindow::beforeRendering, this, &QQuickRhiItemNode::render,
+ Qt::DirectConnection);
+ connect(m_window, &QQuickWindow::screenChanged, this, [this]() {
+ if (m_window->effectiveDevicePixelRatio() != m_dpr)
+ m_item->update();
+ }, Qt::DirectConnection);
+}
+
+QSGTexture *QQuickRhiItemNode::texture() const
+{
+ return m_sgTexture.get();
+}
+
+void QQuickRhiItemNode::resetColorBufferObjects()
+{
+ // owns either m_colorTexture or m_resolveTexture
+ m_sgTexture.reset();
+
+ m_colorTexture = nullptr;
+ m_resolveTexture = nullptr;
+
+ m_msaaColorBuffer.reset();
+}
+
+void QQuickRhiItemNode::resetRenderTargetObjects()
+{
+ m_renderTarget.reset();
+ m_renderPassDescriptor.reset();
+ m_depthStencilBuffer.reset();
+}
+
+void QQuickRhiItemNode::sync()
+{
+ if (!m_rhi) {
+ m_rhi = m_window->rhi();
+ if (!m_rhi) {
+ qWarning("No QRhi found for window %p, QQuickRhiItem will not be functional", m_window);
+ return;
+ }
+ }
+
+ m_dpr = m_window->effectiveDevicePixelRatio();
+ const int minTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMin);
+ const int maxTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+
+ QQuickRhiItemPrivate *itemD = m_item->d_func();
+ QSize newSize = QSize(itemD->fixedTextureWidth, itemD->fixedTextureHeight);
+ if (newSize.isEmpty())
+ newSize = QSize(int(m_item->width()), int(m_item->height())) * m_dpr;
+
+ newSize.setWidth(qMin(maxTexSize, qMax(minTexSize, newSize.width())));
+ newSize.setHeight(qMin(maxTexSize, qMax(minTexSize, newSize.height())));
+
+ if (m_colorTexture) {
+ if (m_colorTexture->format() != itemD->rhiTextureFormat
+ || m_colorTexture->sampleCount() != itemD->samples)
+ {
+ resetColorBufferObjects();
+ resetRenderTargetObjects();
+ }
+ }
+
+ if (m_msaaColorBuffer) {
+ if (m_msaaColorBuffer->backingFormat() != itemD->rhiTextureFormat
+ || m_msaaColorBuffer->sampleCount() != itemD->samples)
+ {
+ resetColorBufferObjects();
+ resetRenderTargetObjects();
+ }
+ }
+
+ if (m_sgTexture && m_sgTexture->hasAlphaChannel() != itemD->blend) {
+ resetColorBufferObjects();
+ resetRenderTargetObjects();
+ }
+
+ if (!m_colorTexture && itemD->samples <= 1) {
+ if (!m_rhi->isTextureFormatSupported(itemD->rhiTextureFormat)) {
+ qWarning("QQuickRhiItem: The requested texture format (%d) is not supported by the "
+ "underlying 3D graphics API implementation", int(itemD->rhiTextureFormat));
+ }
+ m_colorTexture = m_rhi->newTexture(itemD->rhiTextureFormat, newSize, itemD->samples,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
+ if (!m_colorTexture->create()) {
+ qWarning("Failed to create backing texture for QQuickRhiItem");
+ delete m_colorTexture;
+ m_colorTexture = nullptr;
+ return;
+ }
+ }
+
+ if (itemD->samples > 1) {
+ if (!m_msaaColorBuffer) {
+ if (!m_rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) {
+ qWarning("QQuickRhiItem: Multisample renderbuffers are reported as unsupported; "
+ "sample count %d will not work as expected", itemD->samples);
+ }
+ if (!m_rhi->isTextureFormatSupported(itemD->rhiTextureFormat)) {
+ qWarning("QQuickRhiItem: The requested texture format (%d) is not supported by the "
+ "underlying 3D graphics API implementation", int(itemD->rhiTextureFormat));
+ }
+ m_msaaColorBuffer.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, newSize, itemD->samples,
+ {}, itemD->rhiTextureFormat));
+ if (!m_msaaColorBuffer->create()) {
+ qWarning("Failed to create multisample color buffer for QQuickRhiItem");
+ m_msaaColorBuffer.reset();
+ return;
+ }
+ }
+ if (!m_resolveTexture) {
+ m_resolveTexture = m_rhi->newTexture(itemD->rhiTextureFormat, newSize, 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
+ if (!m_resolveTexture->create()) {
+ qWarning("Failed to create resolve texture for QQuickRhiItem");
+ delete m_resolveTexture;
+ m_resolveTexture = nullptr;
+ return;
+ }
+ }
+ } else if (m_resolveTexture) {
+ m_resolveTexture->deleteLater();
+ m_resolveTexture = nullptr;
+ }
+
+ if (m_colorTexture && m_colorTexture->pixelSize() != newSize) {
+ m_colorTexture->setPixelSize(newSize);
+ if (!m_colorTexture->create())
+ qWarning("Failed to rebuild texture for QQuickRhiItem after resizing");
+ }
+
+ if (m_msaaColorBuffer && m_msaaColorBuffer->pixelSize() != newSize) {
+ m_msaaColorBuffer->setPixelSize(newSize);
+ if (!m_msaaColorBuffer->create())
+ qWarning("Failed to rebuild multisample color buffer for QQuickRhiitem after resizing");
+ }
+
+ if (m_resolveTexture && m_resolveTexture->pixelSize() != newSize) {
+ m_resolveTexture->setPixelSize(newSize);
+ if (!m_resolveTexture->create())
+ qWarning("Failed to rebuild resolve texture for QQuickRhiItem after resizing");
+ }
+
+ if (!m_sgTexture) {
+ QQuickWindow::CreateTextureOptions options;
+ if (itemD->blend)
+ options |= QQuickWindow::TextureHasAlphaChannel;
+ // the QSGTexture takes ownership of the QRhiTexture
+ m_sgTexture.reset(m_window->createTextureFromRhiTexture(m_colorTexture ? m_colorTexture : m_resolveTexture,
+ options));
+ setTexture(m_sgTexture.get());
+ }
+
+ if (itemD->autoRenderTarget) {
+ const QSize pixelSize = m_colorTexture ? m_colorTexture->pixelSize()
+ : m_msaaColorBuffer->pixelSize();
+ if (!m_depthStencilBuffer) {
+ m_depthStencilBuffer.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, itemD->samples));
+ if (!m_depthStencilBuffer->create()) {
+ qWarning("Failed to create depth-stencil buffer for QQuickRhiItem");
+ resetRenderTargetObjects();
+ return;
+ }
+ } else if (m_depthStencilBuffer->pixelSize() != pixelSize) {
+ m_depthStencilBuffer->setPixelSize(pixelSize);
+ if (!m_depthStencilBuffer->create()) {
+ qWarning("Failed to rebuild depth-stencil buffer for QQuickRhiItem with new size");
+ return;
+ }
+ }
+ if (!m_renderTarget) {
+ QRhiColorAttachment color0;
+ if (m_colorTexture)
+ color0.setTexture(m_colorTexture);
+ else
+ color0.setRenderBuffer(m_msaaColorBuffer.get());
+ if (itemD->samples > 1)
+ color0.setResolveTexture(m_resolveTexture);
+ QRhiTextureRenderTargetDescription rtDesc(color0, m_depthStencilBuffer.get());
+ m_renderTarget.reset(m_rhi->newTextureRenderTarget(rtDesc));
+ m_renderPassDescriptor.reset(m_renderTarget->newCompatibleRenderPassDescriptor());
+ m_renderTarget->setRenderPassDescriptor(m_renderPassDescriptor.get());
+ if (!m_renderTarget->create()) {
+ qWarning("Failed to create render target for QQuickRhiitem");
+ resetRenderTargetObjects();
+ return;
+ }
+ }
+ } else {
+ resetRenderTargetObjects();
+ }
+
+ if (newSize != itemD->effectiveTextureSize) {
+ itemD->effectiveTextureSize = newSize;
+ emit m_item->effectiveColorBufferSizeChanged();
+ }
+
+ QRhiCommandBuffer *cb = queryCommandBuffer();
+ if (cb)
+ m_renderer->initialize(cb);
+
+ m_renderer->synchronize(m_item);
+}
+
+QRhiCommandBuffer *QQuickRhiItemNode::queryCommandBuffer()
+{
+ QRhiSwapChain *swapchain = m_window->swapChain();
+ QSGRendererInterface *rif = m_window->rendererInterface();
+
+ // Handle both cases: on-screen QQuickWindow vs. off-screen QQuickWindow
+ // e.g. by using QQuickRenderControl to redirect into a texture.
+ QRhiCommandBuffer *cb = swapchain ? swapchain->currentFrameCommandBuffer()
+ : static_cast<QRhiCommandBuffer *>(
+ rif->getResource(m_window, QSGRendererInterface::RhiRedirectCommandBuffer));
+
+ if (!cb) {
+ qWarning("QQuickRhiItem: Neither swapchain nor redirected command buffer are available.");
+ return nullptr;
+ }
+
+ return cb;
+}
+
+void QQuickRhiItemNode::render()
+{
+ // called before Qt Quick starts recording its main render pass
+
+ if (!isValid() || !m_renderPending)
+ return;
+
+ QRhiCommandBuffer *cb = queryCommandBuffer();
+ if (!cb)
+ return;
+
+ m_renderPending = false;
+ m_renderer->render(cb);
+
+ markDirty(QSGNode::DirtyMaterial);
+ emit textureChanged();
+}
+
+/*!
+ Constructs a new QQuickRhiItem with the given \a parent.
+ */
+QQuickRhiItem::QQuickRhiItem(QQuickItem *parent)
+ : QQuickItem(*new QQuickRhiItemPrivate, parent)
+{
+ setFlag(ItemHasContents);
+}
+
+/*!
+ Destructor.
+*/
+QQuickRhiItem::~QQuickRhiItem()
+{
+}
+
+/*!
+ \internal
+ */
+QSGNode *QQuickRhiItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ // Changing to an empty size should not involve destroying and then later
+ // recreating the node, because we do not know how expensive the user's
+ // renderer setup is. Rather, keep the node if it already exist, and clamp
+ // all accesses to width and height. Hence the unusual !oldNode condition here.
+ if (!oldNode && (width() <= 0 || height() <= 0))
+ return nullptr;
+
+ Q_D(QQuickRhiItem);
+ QQuickRhiItemNode *n = static_cast<QQuickRhiItemNode *>(oldNode);
+ if (!n) {
+ if (!d->node)
+ d->node = new QQuickRhiItemNode(this);
+ if (!d->node->hasRenderer()) {
+ QQuickRhiItemRenderer *r = createRenderer();
+ if (r) {
+ r->node = d->node;
+ d->node->setRenderer(r);
+ } else {
+ qWarning("No QQuickRhiItemRenderer was created; the item will not render");
+ delete d->node;
+ d->node = nullptr;
+ return nullptr;
+ }
+ }
+ n = d->node;
+ }
+
+ n->sync();
+
+ if (!n->isValid()) {
+ delete n;
+ d->node = nullptr;
+ return nullptr;
+ }
+
+ if (window()->rhi()->isYUpInFramebuffer()) {
+ n->setTextureCoordinatesTransform(d->mirrorVertically
+ ? QSGSimpleTextureNode::NoTransform
+ : QSGSimpleTextureNode::MirrorVertically);
+ } else {
+ n->setTextureCoordinatesTransform(d->mirrorVertically
+ ? QSGSimpleTextureNode::MirrorVertically
+ : QSGSimpleTextureNode::NoTransform);
+ }
+ n->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
+ n->setRect(0, 0, qMax<int>(0, width()), qMax<int>(0, height()));
+
+ n->scheduleUpdate();
+
+ return n;
+}
+
+/*!
+ \reimp
+ */
+bool QQuickRhiItem::event(QEvent *e)
+{
+ return QQuickItem::event(e);
+}
+
+/*!
+ \reimp
+ */
+void QQuickRhiItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+ if (newGeometry.size() != oldGeometry.size())
+ update();
+}
+
+/*!
+ \reimp
+ */
+void QQuickRhiItem::releaseResources()
+{
+ // called on the gui thread if the item is removed from scene
+
+ Q_D(QQuickRhiItem);
+ d->node = nullptr;
+}
+
+void QQuickRhiItem::invalidateSceneGraph()
+{
+ // called on the render thread when the scenegraph is invalidated
+
+ Q_D(QQuickRhiItem);
+ d->node = nullptr;
+}
+
+/*!
+ \reimp
+ */
+bool QQuickRhiItem::isTextureProvider() const
+{
+ return true;
+}
+
+/*!
+ \reimp
+ */
+QSGTextureProvider *QQuickRhiItem::textureProvider() const
+{
+ if (QQuickItem::isTextureProvider()) // e.g. if Item::layer::enabled == true
+ return QQuickItem::textureProvider();
+
+ Q_D(const QQuickRhiItem);
+ if (!d->node) // create a node to have a provider, the texture will be null but that's ok
+ d->node = new QQuickRhiItemNode(const_cast<QQuickRhiItem *>(this));
+
+ return d->node;
+}
+
+/*!
+ \property QQuickRhiItem::sampleCount
+
+ This property controls for sample count for multisample antialiasing.
+ By default the value is \c 1 which means MSAA is disabled.
+
+ Valid values are 1, 4, 8, and sometimes 16 and 32.
+ \l QRhi::supportedSampleCounts() can be used to query the supported sample
+ counts at run time, but typically applications should request 1 (no MSAA),
+ 4x (normal MSAA) or 8x (high MSAA).
+
+ \note Setting a new value implies that all QRhiGraphicsPipeline objects
+ created by the renderer must use the same sample count from then on.
+ Existing QRhiGraphicsPipeline objects created with a different sample count
+ must not be used anymore. When the value changes, all color and
+ depth-stencil buffers are destroyed and recreated automatically, and
+ \l {QQuickRhiItemRenderer::}{initialize()} is invoked again. However, when
+ isAutoRenderTargetEnabled() is \c false, it will be up to the application to
+ manage this with regards to the depth-stencil buffer or additional color
+ buffers.
+
+ Changing the sample count from the default 1 to a higher value implies that
+ \l {QQuickRhiItemRenderer::}{colorTexture()} becomes \nullptr and
+ \l {QQuickRhiItemRenderer::}{msaaColorBuffer()} starts returning a
+ valid object. Switching back to 1 (or 0), implies the opposite: in the next
+ call to initialize() msaaColorBuffer() is going to return \nullptr, whereas
+ colorTexture() becomes once again valid. In addition,
+ \l {QQuickRhiItemRenderer::}{resolveTexture()}
+ returns a valid (non-multisample) QRhiTexture whenever the sample count is
+ greater than 1 (i.e., MSAA is in use).
+
+ \sa QQuickRhiItemRenderer::msaaColorBuffer(),
+ QQuickRhiItemRenderer::resolveTexture()
+ */
+
+int QQuickRhiItem::sampleCount() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->samples;
+}
+
+void QQuickRhiItem::setSampleCount(int samples)
+{
+ Q_D(QQuickRhiItem);
+ if (d->samples == samples)
+ return;
+
+ d->samples = samples;
+ emit sampleCountChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::colorBufferFormat
+
+ This property controls the texture format for the texture used as the color
+ buffer. The default value is TextureFormat::RGBA8. QQuickRhiItem supports
+ rendering to a subset of the formats supported by \l QRhiTexture. Only
+ formats that are reported as supported from
+ \l QRhi::isTextureFormatSupported() should be specified, rendering will not be
+ functional otherwise.
+
+ \note Setting a new format when the item and its renderer are already
+ initialized and have rendered implies that all QRhiGraphicsPipeline objects
+ created by the renderer may become unusable, if the associated
+ QRhiRenderPassDescriptor is now incompatible due to the different texture
+ format. Similarly to changing
+ \l sampleCount dynamically, this means that initialize() or render()
+ implementations must then take care of releasing the existing pipelines and
+ creating new ones.
+ */
+
+QQuickRhiItem::TextureFormat QQuickRhiItem::colorBufferFormat() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->itemTextureFormat;
+}
+
+void QQuickRhiItem::setColorBufferFormat(TextureFormat format)
+{
+ Q_D(QQuickRhiItem);
+ if (d->itemTextureFormat == format)
+ return;
+
+ d->itemTextureFormat = format;
+ switch (format) {
+ case TextureFormat::RGBA8:
+ d->rhiTextureFormat = QRhiTexture::RGBA8;
+ break;
+ case TextureFormat::RGBA16F:
+ d->rhiTextureFormat = QRhiTexture::RGBA16F;
+ break;
+ case TextureFormat::RGBA32F:
+ d->rhiTextureFormat = QRhiTexture::RGBA32F;
+ break;
+ case TextureFormat::RGB10A2:
+ d->rhiTextureFormat = QRhiTexture::RGB10A2;
+ break;
+ }
+ emit colorBufferFormatChanged();
+ update();
+}
+
+/*!
+ \return the current automatic depth-stencil buffer and render target management setting.
+
+ By default this value is \c true.
+
+ \sa setAutoRenderTarget()
+ */
+bool QQuickRhiItem::isAutoRenderTargetEnabled() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->autoRenderTarget;
+}
+
+/*!
+ Controls if a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
+ is created and maintained automatically by the item. The default value is
+ \c true. Call this function early on, for example from the derived class'
+ constructor, with \a enabled set to \c false to disable this.
+
+ In automatic mode, the size and sample count of the depth-stencil buffer
+ follows the color buffer texture's settings. In non-automatic mode,
+ renderTarget() and depthStencilBuffer() always return \nullptr and it is
+ then up to the application's implementation of initialize() to take care of
+ setting up and managing these objects.
+ */
+void QQuickRhiItem::setAutoRenderTarget(bool enabled)
+{
+ Q_D(QQuickRhiItem);
+ if (d->autoRenderTarget == enabled)
+ return;
+
+ d->autoRenderTarget = enabled;
+ emit autoRenderTargetChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::mirrorVertically
+
+ This property controls if texture UVs are flipped when drawing the textured
+ quad. It has no effect on the contents of the offscreen color buffer and
+ the rendering implemented by the QQuickRhiItemRenderer.
+
+ The default value is \c false.
+ */
+
+bool QQuickRhiItem::isMirrorVerticallyEnabled() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->mirrorVertically;
+}
+
+void QQuickRhiItem::setMirrorVertically(bool enable)
+{
+ Q_D(QQuickRhiItem);
+ if (d->mirrorVertically == enable)
+ return;
+
+ d->mirrorVertically = enable;
+ emit mirrorVerticallyChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::fixedColorBufferWidth
+
+ The fixed width, in pixels, of the item's associated texture or
+ renderbuffer. Relevant when a fixed color buffer size is desired that does
+ not depend on the item's size. This size has no effect on the geometry of
+ the item (its size and placement within the scene), which means the
+ texture's content will appear stretched (scaled up) or scaled down onto the
+ item's area.
+
+ For example, setting a size that is exactly twice the item's (pixel) size
+ effectively performs 2x supersampling (rendering at twice the resolution
+ and then implicitly scaling down when texturing the quad corresponding to
+ the item in the scene).
+
+ By default the value is \c 0. A value of 0 means that texture's size
+ follows the item's size. (\c{texture size} = \c{item size} * \c{device
+ pixel ratio}).
+ */
+int QQuickRhiItem::fixedColorBufferWidth() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->fixedTextureWidth;
+}
+
+void QQuickRhiItem::setFixedColorBufferWidth(int width)
+{
+ Q_D(QQuickRhiItem);
+ if (d->fixedTextureWidth == width)
+ return;
+
+ d->fixedTextureWidth = width;
+ emit fixedColorBufferWidthChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::fixedColorBufferHeight
+
+ The fixed height, in pixels, of the item's associated texture. Relevant when
+ a fixed texture size is desired that does not depend on the item's size.
+ This size has no effect on the geometry of the item (its size and placement
+ within the scene), which means the texture's content will appear stretched
+ (scaled up) or scaled down onto the item's area.
+
+ For example, setting a size that is exactly twice the item's (pixel) size
+ effectively performs 2x supersampling (rendering at twice the resolution
+ and then implicitly scaling down when texturing the quad corresponding to
+ the item in the scene).
+
+ By default the value is \c 0. A value of 0 means that texture's size
+ follows the item's size. (\c{texture size} = \c{item size} * \c{device
+ pixel ratio}).
+ */
+
+int QQuickRhiItem::fixedColorBufferHeight() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->fixedTextureHeight;
+}
+
+void QQuickRhiItem::setFixedColorBufferHeight(int height)
+{
+ Q_D(QQuickRhiItem);
+ if (d->fixedTextureHeight == height)
+ return;
+
+ d->fixedTextureHeight = height;
+ emit fixedColorBufferHeightChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::effectiveColorBufferSize
+
+ This property exposes the size, in pixels, of the underlying color buffer
+ (the QRhiTexture or QRhiRenderBuffer). It is provided for use on the GUI
+ (main) thread, in QML bindings or JavaScript.
+
+ \note QQuickRhiItemRenderer implementations, operating on the scene graph
+ render thread, should not use this property. Those should rather query the
+ size from the
+ \l{QQuickRhiItemRenderer::renderTarget()}{render target}.
+
+ \note The value becomes available asynchronously from the main thread's
+ perspective in the sense that the value changes when rendering happens on
+ the render thread. This means that this property is useful mainly in QML
+ bindings. Application code must not assume that the value is up to date
+ already when the QQuickRhiItem object is constructed.
+
+ This is a read-only property.
+ */
+
+QSize QQuickRhiItem::effectiveColorBufferSize() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->effectiveTextureSize;
+}
+
+/*!
+ \property QQuickRhiItem::alphaBlending
+
+ Controls if blending is always enabled when drawing the quad textured with
+ the content generated by the QQuickRhiItem and its renderer.
+
+ The default value is \c false. This is for performance reasons: if
+ semi-transparency is not involved, because the QQuickRhiItemRenderer clears
+ to an opaque color and never renders fragments with alpha smaller than 1,
+ then there is no point in enabling blending.
+
+ If the QQuickRhiItemRenderer subclass renders with semi-transparency involved,
+ set this property to true.
+
+ \note Under certain conditions blending is still going to happen regardless
+ of the value of this property. For example, if the item's
+ \l{QQuickItem::opacity}{opacity} (more precisely, the combined opacity
+ inherited from the parent chain) is smaller than 1, blending will be
+ automatically enabled even when this property is set to false.
+
+ \note The Qt Quick scene graph relies on and expect pre-multiplied alpha.
+ For example, if the intention is to clear the background in the renderer to
+ an alpha value of 0.5, then make sure to multiply the red, green, and blue
+ clear color values with 0.5 as well. Otherwise the blending results will be
+ incorrect.
+ */
+
+bool QQuickRhiItem::alphaBlending() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->blend;
+}
+
+void QQuickRhiItem::setAlphaBlending(bool enable)
+{
+ Q_D(QQuickRhiItem);
+ if (d->blend == enable)
+ return;
+
+ d->blend = enable;
+ emit alphaBlendingChanged();
+ update();
+}
+
+/*!
+ Constructs a new renderer.
+
+ This function is called on the rendering thread during the scene graph sync
+ phase when the GUI thread is blocked.
+
+ \sa QQuickRhiItem::createRenderer()
+ */
+QQuickRhiItemRenderer::QQuickRhiItemRenderer()
+{
+}
+
+/*!
+ The Renderer is automatically deleted when the scene graph resources for
+ the QQuickRhiItem item are cleaned up.
+
+ This function is called on the rendering thread.
+
+ Under certain conditions it is normal and expected that the renderer object
+ is destroyed and then recreated. This is because the renderer's lifetime
+ effectively follows the underlying scene graph node. For example, when
+ changing the parent of a QQuickRhiItem object so that it then belongs to a
+ different \l QQuickWindow, the scene graph nodes are all dropped and
+ recreated due to the window change. This will also involve dropping and
+ creating a new QQuickRhiItemRenderer.
+
+ Unlike \l QRhiWidget, QQuickRhiItemRenderer has no need to implement
+ additional code paths for releasing (or early-relasing) graphics resources
+ created via QRhi. It is sufficient to release everything in the destructor,
+ or rely on smart pointers.
+ */
+QQuickRhiItemRenderer::~QQuickRhiItemRenderer()
+{
+}
+
+/*!
+ Call this function when the content of the offscreen color buffer should be
+ updated. (i.e. to request that render() is called again; the call will
+ happen at a later point, and note that updates are typically throttled to
+ the presentation rate)
+
+ This function can be called from render() to schedule an update.
+
+ \note This function should be used from inside the renderer. To update
+ the item on the GUI thread, use QQuickRhiItem::update().
+ */
+void QQuickRhiItemRenderer::update()
+{
+ if (node)
+ node->scheduleUpdate();
+}
+
+/*!
+ \return the current QRhi object.
+
+ Must only be called from initialize() and render().
+ */
+QRhi *QQuickRhiItemRenderer::rhi() const
+{
+ return node ? node->m_rhi : nullptr;
+}
+
+/*!
+ \return the texture serving as the color buffer for the item.
+
+ Must only be called from initialize() and render().
+
+ Unlike the depth-stencil buffer and the QRhiRenderTarget, this texture is
+ always available and is managed by the QQuickRhiItem, independent of the
+ value of \l {QQuickRhiItem::}{autoRenderTarget}.
+
+ \note When \l {QQuickRhiItem::}{sampleCount} is larger than 1, and so
+ multisample antialiasing is enabled, the return value is \nullptr. Instead,
+ query the \l QRhiRenderBuffer by calling msaaColorBuffer().
+
+ \note The backing texture size and sample count can also be queried via the
+ QRhiRenderTarget returned from renderTarget(). This can be more convenient
+ and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
+ it works regardless of multisampling is in use or not.
+
+ \sa msaaColorBuffer(), depthStencilBuffer(), renderTarget(), resolveTexture()
+ */
+QRhiTexture *QQuickRhiItemRenderer::colorTexture() const
+{
+ return node ? node->m_colorTexture : nullptr;
+}
+
+/*!
+ \return the renderbuffer serving as the multisample color buffer for the item.
+
+ Must only be called from initialize() and render().
+
+ When \l {QQuickRhiItem::}{sampleCount} is larger than 1, and so multisample
+ antialising is enabled, the returned QRhiRenderBuffer has a matching sample
+ count and serves as the color buffer. Graphics pipelines used to render into
+ this buffer must be created with the same sample count, and the
+ depth-stencil buffer's sample count must match as well. The multisample
+ content is expected to be resolved into the texture returned from
+ resolveTexture(). When \l {QQuickRhiItem::}{autoRenderTarget} is \c true,
+ renderTarget() is set up automatically to do this, by setting up
+ msaaColorBuffer() as the
+ \l{QRhiColorAttachment::renderBuffer()}{renderbuffer} of color attachment 0
+ and resolveTexture() as its
+ \l{QRhiColorAttachment::resolveTexture()}{resolveTexture}.
+
+ When MSAA is not in use, the return value is \nullptr. Use colorTexture()
+ instead then.
+
+ Depending on the underlying 3D graphics API, there may be no practical
+ difference between multisample textures and color renderbuffers with a
+ sample count larger than 1 (QRhi may just map both to the same native
+ resource type). Some older APIs however may differentiate between textures
+ and renderbuffers. In order to support OpenGL ES 3.0, where multisample
+ renderbuffers are available, but multisample textures are not, QQuickRhiItem
+ always performs MSAA by using a multisample QRhiRenderBuffer as the color
+ attachment (and never a multisample QRhiTexture).
+
+ \note The backing texture size and sample count can also be queried via the
+ QRhiRenderTarget returned from renderTarget(). This can be more convenient
+ and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
+ it works regardless of multisampling is in use or not.
+
+ \sa colorTexture(), depthStencilBuffer(), renderTarget(), resolveTexture()
+ */
+QRhiRenderBuffer *QQuickRhiItemRenderer::msaaColorBuffer() const
+{
+ return node ? node->m_msaaColorBuffer.get() : nullptr;
+}
+
+/*!
+ \return the non-multisample texture to which the multisample content is resolved.
+
+ The result is \nullptr when multisample antialiasing is not enabled.
+
+ Must only be called from initialize() and render().
+
+ With MSAA enabled, this is the texture that gets used by the item's
+ underlying scene graph node when texturing a quad in the main render pass
+ of Qt Quick. However, the QQuickRhiItemRenderer's rendering must target the
+ (multisample) QRhiRenderBuffer returned from msaaColorBuffer(). When
+ \l {QQuickRhiItem::}{autoRenderTarget} is \c true, this is taken care of by
+ the QRhiRenderTarget returned from renderTarget(). Otherwise, it is up to
+ the subclass code to correctly configure a render target object with both
+ the color buffer and resolve textures.
+
+ \sa colorTexture()
+ */
+QRhiTexture *QQuickRhiItemRenderer::resolveTexture() const
+{
+ return node ? node->m_resolveTexture : nullptr;
+}
+
+/*!
+ \return the depth-stencil buffer used by the item's rendering.
+
+ Must only be called from initialize() and render().
+
+ Available only when \l {QQuickRhiItem::}{autoRenderTarget} is \c true.
+ Otherwise the returned value is \nullptr and it is up the reimplementation
+ of initialize() to create and manage a depth-stencil buffer and a
+ QRhiTextureRenderTarget.
+
+ \sa colorTexture(), renderTarget()
+ */
+QRhiRenderBuffer *QQuickRhiItemRenderer::depthStencilBuffer() const
+{
+ return node ? node->m_depthStencilBuffer.get() : nullptr;
+}
+
+/*!
+ \return the render target object that must be used with
+ \l QRhiCommandBuffer::beginPass() in reimplementations of render().
+
+ Must only be called from initialize() and render().
+
+ Available only when \l {QQuickRhiItem::}{autoRenderTarget} is \c true.
+ Otherwise the returned value is \nullptr and it is up the reimplementation
+ of initialize() to create and manage a depth-stencil buffer and a
+ QRhiTextureRenderTarget.
+
+ When creating \l{QRhiGraphicsPipeline}{graphics pipelines}, a
+ QRhiRenderPassDescriptor is needed. This can be queried from the returned
+ QRhiTextureRenderTarget by calling
+ \l{QRhiTextureRenderTarget::renderPassDescriptor()}{renderPassDescriptor()}.
+
+ \note The returned QRhiTextureRenderTarget always reports a
+ \l{QRhiTextureRenderTarget::}{devicePixelRatio()} of \c 1.
+ This is because only swapchains and the associated window have a concept of
+ device pixel ratio, not textures, and the render target here always refers
+ to a texture. If the on-screen scale factor is relevant for rendering,
+ query and store it via the item's
+ \c{window()->effectiveDevicePixelRatio()} in \l synchronize().
+ When doing so, always prefer using \l{QQuickWindow::}{effectiveDevicePixelRatio()}
+ over the base class' \l{QWindow::}{devicePixelRatio()}.
+
+ \sa colorTexture(), depthStencilBuffer(), QQuickWindow::effectiveDevicePixelRatio()
+ */
+QRhiRenderTarget *QQuickRhiItemRenderer::renderTarget() const
+{
+ return node ? node->m_renderTarget.get() : nullptr;
+}
+
+/*!
+ \fn QQuickRhiItemRenderer *QQuickRhiItem::createRenderer()
+
+ Reimplement this function to create and return a new instance of a
+ QQuickRhiItemRenderer subclass.
+
+ This function will be called on the rendering thread while the GUI thread
+ is blocked.
+ */
+
+/*!
+ \fn void QQuickRhiItemRenderer::initialize(QRhiCommandBuffer *cb)
+
+ Called when the item is initialized for the first time, when the
+ associated texture's size, format, or sample count changes, or when the
+ QRhi or texture change for any reason. The function is expected to
+ maintain (create if not yet created, adjust and rebuild if the size has
+ changed) the graphics resources used by the rendering code in render().
+
+ To query the QRhi, QRhiTexture, and other related objects, call rhi(),
+ colorTexture(), depthStencilBuffer(), and renderTarget().
+
+ When the item size changes, the QRhi object, the color buffer texture,
+ and the depth stencil buffer objects are all the same instances (so the
+ getters return the same pointers) as before, but the color and
+ depth/stencil buffers will likely have been rebuilt, meaning the
+ \l{QRhiTexture::pixelSize()}{size} and the underlying native texture
+ resource may be different than in the last invocation.
+
+ Reimplementations should also be prepared that the QRhi object and the
+ color buffer texture may change between invocations of this function. For
+ example, when the item is reparented so that it belongs to a new
+ QQuickWindow, the the QRhi and all related resources managed by the
+ QQuickRhiItem will be different instances than before in the subsequent
+ call to this function. Is is then important that all existing QRhi
+ resources previously created by the subclass are destroyed because they
+ belong to the previous QRhi that should not be used anymore.
+
+ When \l {QQuickRhiItem::}{autoRenderTarget} is \c true, which is the
+ default, a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
+ associated with the colorTexture() (or msaaColorBuffer()) and the
+ depth-stencil buffer are created and managed automatically.
+ Reimplementations of initialize() and render() can query those objects via
+ depthStencilBuffer() and renderTarget(). When
+ \l {QQuickRhiItem::}{autoRenderTarget} is set to \c false, these objects are
+ no longer created and managed automatically. Rather, it will be
+ up the the initialize() implementation to create buffers and set up the
+ render target as it sees fit. When manually managing additional color or
+ depth-stencil attachments for the render target, their size and sample
+ count must always follow the size and sample count of colorTexture() (or
+ msaaColorBuffer()), otherwise rendering or 3D API validation errors may
+ occur.
+
+ The subclass-created graphics resources are expected to be released in the
+ destructor implementation of the subclass.
+
+ \a cb is the QRhiCommandBuffer for the current frame. The function is
+ called with a frame being recorded, but without an active render pass. The
+ command buffer is provided primarily to allow enqueuing
+ \l{QRhiCommandBuffer::resourceUpdate()}{resource updates} without deferring
+ to render().
+
+ This function is called on the render thread, if there is one.
+
+ \sa render()
+ */
+
+/*!
+ \fn void QQuickRhiItemRenderer::synchronize(QQuickRhiItem *item)
+
+ This function is called on the render thread, if there is one, while the
+ main/GUI thread is blocked. It is called from
+ \l{QQuickItem::updatePaintNode()}{the \a {item}'s synchronize step},
+ and allows reading and writing data belonging to the main and render
+ threads. Typically property values stored in the QQuickRhiItem are copied
+ into the QQuickRhiItemRenderer, so that they can be safely read afterwards
+ in render() when the render and main threads continue to work in parallel.
+
+ \sa initialize(), render()
+ */
+
+/*!
+ \fn void QQuickRhiItemRenderer::render(QRhiCommandBuffer *cb)
+
+ Called when the backing color buffer's contents needs updating.
+
+ There is always at least one call to initialize() before this function is
+ called.
+
+ To request updates, call \l QQuickItem::update() when calling from QML or
+ from C++ code on the main/GUI thread (e.g. when in a property setter), or
+ \l update() when calling from within a QQuickRhiItemRenderer callback.
+ Calling QQuickRhiItemRenderer's update() from within
+ render() will lead to triggering updates continuously.
+
+ \a cb is the QRhiCommandBuffer for the current frame. The function is
+ called with a frame being recorded, but without an active render pass.
+
+ This function is called on the render thread, if there is one.
+
+ \sa initialize(), synchronize()
+ */
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrhiitem.h b/src/quick/items/qquickrhiitem.h
new file mode 100644
index 0000000000..78bfb9fe4b
--- /dev/null
+++ b/src/quick/items/qquickrhiitem.h
@@ -0,0 +1,124 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKRHIITEM_H
+#define QQUICKRHIITEM_H
+
+#include <QtQuick/QQuickItem>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickRhiItem;
+class QQuickRhiItemPrivate;
+class QQuickRhiItemNode;
+class QRhi;
+class QRhiCommandBuffer;
+class QRhiTexture;
+class QRhiRenderBuffer;
+class QRhiRenderTarget;
+
+class Q_QUICK_EXPORT QQuickRhiItemRenderer
+{
+public:
+ QQuickRhiItemRenderer();
+ virtual ~QQuickRhiItemRenderer();
+
+protected:
+ virtual void initialize(QRhiCommandBuffer *cb) = 0;
+ virtual void synchronize(QQuickRhiItem *item) = 0;
+ virtual void render(QRhiCommandBuffer *cb) = 0;
+
+ void update();
+
+ QRhi *rhi() const;
+ QRhiTexture *colorTexture() const;
+ QRhiRenderBuffer *msaaColorBuffer() const;
+ QRhiTexture *resolveTexture() const;
+ QRhiRenderBuffer *depthStencilBuffer() const;
+ QRhiRenderTarget *renderTarget() const;
+
+private:
+ QQuickRhiItemNode *node;
+ friend class QQuickRhiItem;
+ friend class QQuickRhiItemNode;
+
+ Q_DISABLE_COPY_MOVE(QQuickRhiItemRenderer)
+};
+
+class Q_QUICK_EXPORT QQuickRhiItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQuickRhiItem)
+
+ Q_PROPERTY(int sampleCount READ sampleCount WRITE setSampleCount NOTIFY sampleCountChanged FINAL)
+ Q_PROPERTY(TextureFormat colorBufferFormat READ colorBufferFormat WRITE setColorBufferFormat NOTIFY colorBufferFormatChanged FINAL)
+ Q_PROPERTY(bool mirrorVertically READ isMirrorVerticallyEnabled WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged FINAL)
+ Q_PROPERTY(bool alphaBlending READ alphaBlending WRITE setAlphaBlending NOTIFY alphaBlendingChanged FINAL)
+ Q_PROPERTY(int fixedColorBufferWidth READ fixedColorBufferWidth WRITE setFixedColorBufferWidth NOTIFY fixedColorBufferWidthChanged FINAL)
+ Q_PROPERTY(int fixedColorBufferHeight READ fixedColorBufferHeight WRITE setFixedColorBufferHeight NOTIFY fixedColorBufferHeightChanged FINAL)
+ Q_PROPERTY(QSize effectiveColorBufferSize READ effectiveColorBufferSize NOTIFY effectiveColorBufferSizeChanged FINAL)
+
+public:
+ enum class TextureFormat {
+ RGBA8,
+ RGBA16F,
+ RGBA32F,
+ RGB10A2
+ };
+ Q_ENUM(TextureFormat)
+
+ explicit QQuickRhiItem(QQuickItem *parent = nullptr);
+ ~QQuickRhiItem() override;
+
+ int sampleCount() const;
+ void setSampleCount(int samples);
+
+ TextureFormat colorBufferFormat() const;
+ void setColorBufferFormat(TextureFormat format);
+
+ bool isMirrorVerticallyEnabled() const;
+ void setMirrorVertically(bool enable);
+
+ bool alphaBlending() const;
+ void setAlphaBlending(bool enable);
+
+ int fixedColorBufferWidth() const;
+ void setFixedColorBufferWidth(int width);
+ int fixedColorBufferHeight() const;
+ void setFixedColorBufferHeight(int height);
+
+ QSize effectiveColorBufferSize() const;
+
+ bool isTextureProvider() const override;
+ QSGTextureProvider *textureProvider() const override;
+
+Q_SIGNALS:
+ void sampleCountChanged();
+ void colorBufferFormatChanged();
+ void autoRenderTargetChanged();
+ void mirrorVerticallyChanged();
+ void alphaBlendingChanged();
+ void fixedColorBufferWidthChanged();
+ void fixedColorBufferHeightChanged();
+ void effectiveColorBufferSizeChanged();
+
+protected:
+ virtual QQuickRhiItemRenderer *createRenderer() = 0;
+
+ bool isAutoRenderTargetEnabled() const;
+ void setAutoRenderTarget(bool enabled);
+
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
+ bool event(QEvent *) override;
+ void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void releaseResources() override;
+
+private Q_SLOTS:
+ void invalidateSceneGraph();
+
+ friend class QQuickRhiItemNode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKRHIITEM_H
diff --git a/src/quick/items/qquickrhiitem_p.h b/src/quick/items/qquickrhiitem_p.h
new file mode 100644
index 0000000000..e60de7e55e
--- /dev/null
+++ b/src/quick/items/qquickrhiitem_p.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKRHIITEM_P_H
+#define QQUICKRHIITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickrhiitem.h"
+#include <QtQuick/QSGTextureProvider>
+#include <QtQuick/QSGSimpleTextureNode>
+#include <QtQuick/private/qquickitem_p.h>
+#include <rhi/qrhi.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickRhiItemNode : public QSGTextureProvider, public QSGSimpleTextureNode
+{
+ Q_OBJECT
+
+public:
+ QQuickRhiItemNode(QQuickRhiItem *item);
+
+ QSGTexture *texture() const override;
+
+ void sync();
+ QRhiCommandBuffer *queryCommandBuffer();
+ void resetColorBufferObjects();
+ void resetRenderTargetObjects();
+
+ bool isValid() const
+ {
+ return m_rhi && m_sgTexture;
+ }
+
+ void scheduleUpdate()
+ {
+ m_renderPending = true;
+ m_window->update(); // ensure getting to beforeRendering() at some point
+ }
+
+ bool hasRenderer() const
+ {
+ return m_renderer != nullptr;
+ }
+
+ void setRenderer(QQuickRhiItemRenderer *r)
+ {
+ m_renderer.reset(r);
+ }
+
+ QQuickRhiItem *m_item;
+ QQuickWindow *m_window;
+ QSize m_pixelSize;
+ qreal m_dpr = 0.0f;
+ QRhi *m_rhi = nullptr;
+ bool m_renderPending = true;
+ std::unique_ptr<QSGTexture> m_sgTexture;
+ std::unique_ptr<QQuickRhiItemRenderer> m_renderer;
+ QRhiTexture *m_colorTexture = nullptr;
+ QRhiTexture *m_resolveTexture = nullptr;
+ std::unique_ptr<QRhiRenderBuffer> m_msaaColorBuffer;
+ std::unique_ptr<QRhiRenderBuffer> m_depthStencilBuffer;
+ std::unique_ptr<QRhiTextureRenderTarget> m_renderTarget;
+ std::unique_ptr<QRhiRenderPassDescriptor> m_renderPassDescriptor;
+
+public slots:
+ void render();
+};
+
+class QQuickRhiItemPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickRhiItem)
+
+public:
+ static QQuickRhiItemPrivate *get(QQuickRhiItem *item) { return item->d_func(); }
+
+ mutable QQuickRhiItemNode *node = nullptr;
+ QQuickRhiItem::TextureFormat itemTextureFormat = QQuickRhiItem::TextureFormat::RGBA8;
+ QRhiTexture::Format rhiTextureFormat = QRhiTexture::RGBA8;
+ int samples = 1;
+ bool autoRenderTarget = true;
+ bool mirrorVertically = false;
+ bool blend = false;
+ int fixedTextureWidth = 0;
+ int fixedTextureHeight = 0;
+ QSize effectiveTextureSize;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/items/qquickscalegrid_p_p.h b/src/quick/items/qquickscalegrid_p_p.h
index e89001ceff..20edb3b0cb 100644
--- a/src/quick/items/qquickscalegrid_p_p.h
+++ b/src/quick/items/qquickscalegrid_p_p.h
@@ -20,19 +20,19 @@
#include <QtQml/qqml.h>
#include <QtCore/qobject.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickScaleGrid : public QObject
+class Q_QUICK_EXPORT QQuickScaleGrid : public QObject
{
Q_OBJECT
- Q_PROPERTY(int left READ left WRITE setLeft NOTIFY leftBorderChanged)
- Q_PROPERTY(int top READ top WRITE setTop NOTIFY topBorderChanged)
- Q_PROPERTY(int right READ right WRITE setRight NOTIFY rightBorderChanged)
- Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY bottomBorderChanged)
+ Q_PROPERTY(int left READ left WRITE setLeft NOTIFY leftBorderChanged FINAL)
+ Q_PROPERTY(int top READ top WRITE setTop NOTIFY topBorderChanged FINAL)
+ Q_PROPERTY(int right READ right WRITE setRight NOTIFY rightBorderChanged FINAL)
+ Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY bottomBorderChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -99,6 +99,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScaleGrid)
-
#endif // QQUICKSCALEGRID_P_P_H
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index ea3ae58fd0..c8405ca9ad 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -39,6 +39,8 @@ QT_BEGIN_NAMESPACE
Note that the Screen type is not valid at Component.onCompleted, because
the Item or Window has not been displayed on a screen by this time.
+
+ \sa {Qt Quick Examples - Window and Screen}
*/
/*!
diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h
index 059c33b0b7..02d33a1523 100644
--- a/src/quick/items/qquickscreen_p.h
+++ b/src/quick/items/qquickscreen_p.h
@@ -18,6 +18,8 @@
#include <QtQml/qqml.h>
#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
@@ -26,25 +28,25 @@ class QQuickWindow;
class QScreen;
-class Q_QUICK_PRIVATE_EXPORT QQuickScreenInfo : public QObject
+class Q_QUICK_EXPORT QQuickScreenInfo : public QObject
{
Q_OBJECT
- Q_PROPERTY(QString name READ name NOTIFY nameChanged)
- Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged REVISION(2, 10))
- Q_PROPERTY(QString model READ model NOTIFY modelChanged REVISION(2, 10))
- Q_PROPERTY(QString serialNumber READ serialNumber NOTIFY serialNumberChanged REVISION(2, 10))
- Q_PROPERTY(int width READ width NOTIFY widthChanged)
- Q_PROPERTY(int height READ height NOTIFY heightChanged)
- Q_PROPERTY(int desktopAvailableWidth READ desktopAvailableWidth NOTIFY desktopGeometryChanged)
- Q_PROPERTY(int desktopAvailableHeight READ desktopAvailableHeight NOTIFY desktopGeometryChanged)
- Q_PROPERTY(qreal logicalPixelDensity READ logicalPixelDensity NOTIFY logicalPixelDensityChanged)
- Q_PROPERTY(qreal pixelDensity READ pixelDensity NOTIFY pixelDensityChanged)
- Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged)
- Q_PROPERTY(Qt::ScreenOrientation primaryOrientation READ primaryOrientation NOTIFY primaryOrientationChanged)
- Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged)
-
- Q_PROPERTY(int virtualX READ virtualX NOTIFY virtualXChanged REVISION(2, 3))
- Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION(2, 3))
+ Q_PROPERTY(QString name READ name NOTIFY nameChanged FINAL)
+ Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged REVISION(2, 10) FINAL)
+ Q_PROPERTY(QString model READ model NOTIFY modelChanged REVISION(2, 10) FINAL)
+ Q_PROPERTY(QString serialNumber READ serialNumber NOTIFY serialNumberChanged REVISION(2, 10) FINAL)
+ Q_PROPERTY(int width READ width NOTIFY widthChanged FINAL)
+ Q_PROPERTY(int height READ height NOTIFY heightChanged FINAL)
+ Q_PROPERTY(int desktopAvailableWidth READ desktopAvailableWidth NOTIFY desktopGeometryChanged FINAL)
+ Q_PROPERTY(int desktopAvailableHeight READ desktopAvailableHeight NOTIFY desktopGeometryChanged FINAL)
+ Q_PROPERTY(qreal logicalPixelDensity READ logicalPixelDensity NOTIFY logicalPixelDensityChanged FINAL)
+ Q_PROPERTY(qreal pixelDensity READ pixelDensity NOTIFY pixelDensityChanged FINAL)
+ Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged FINAL)
+ Q_PROPERTY(Qt::ScreenOrientation primaryOrientation READ primaryOrientation NOTIFY primaryOrientationChanged FINAL)
+ Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged FINAL)
+
+ Q_PROPERTY(int virtualX READ virtualX NOTIFY virtualXChanged REVISION(2, 3) FINAL)
+ Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION(2, 3) FINAL)
QML_NAMED_ELEMENT(ScreenInfo)
QML_ADDED_IN_VERSION(2, 3)
QML_UNCREATABLE("ScreenInfo can only be used via the attached property.")
@@ -91,7 +93,7 @@ protected:
QPointer<QScreen> m_screen;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickScreenAttached : public QQuickScreenInfo
+class Q_QUICK_EXPORT QQuickScreenAttached : public QQuickScreenInfo
{
Q_OBJECT
@@ -114,7 +116,7 @@ private:
QQuickItem* m_attachee;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickScreen : public QObject
+class Q_QUICK_EXPORT QQuickScreen : public QObject
{
Q_OBJECT
QML_ATTACHED(QQuickScreenAttached)
@@ -128,6 +130,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScreenInfo)
-
#endif
diff --git a/src/quick/items/qquickselectable_p.h b/src/quick/items/qquickselectable_p.h
index 726352719f..dbee64c49e 100644
--- a/src/quick/items/qquickselectable_p.h
+++ b/src/quick/items/qquickselectable_p.h
@@ -20,12 +20,17 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickSelectable
+class Q_QUICK_EXPORT QQuickSelectable
{
public:
+ enum class CallBackFlag {
+ CancelSelection,
+ SelectionRectangleChanged
+ };
+
virtual QQuickItem *selectionPointerHandlerTarget() const = 0;
- virtual bool startSelection(const QPointF &pos) = 0;
+ virtual bool startSelection(const QPointF &pos, Qt::KeyboardModifiers modifiers) = 0;
virtual void setSelectionStartPos(const QPointF &pos) = 0;
virtual void setSelectionEndPos(const QPointF &pos) = 0;
virtual void clearSelection() = 0;
@@ -33,6 +38,8 @@ public:
virtual QRectF selectionRectangle() const = 0;
virtual QSizeF scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step) = 0;
+
+ virtual void setCallback(std::function<void(CallBackFlag)> func) = 0;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index d47203057f..7dea334785 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -486,6 +486,15 @@ QT_BEGIN_NAMESPACE
\li Samplers must use binding points starting from 1.
+ \li When Qt Quick is rendering with \c multiview enabled, e.g. because it is
+ part of a 3D scene rendering in a VR/AR environment where the left and right
+ eye content are generated in a single pass, the ShaderEffect's shaders have
+ to be written with this in mind. With a view count of 2 for example, there
+ will be \c 2 matrices (qt_Matrix is an array of mat4 with two elements). The
+ vertex shader is expected to take \c gl_ViewIndex into account. See the \c
+ Multiview section in the \l{QSB Manual} for general information on creating
+ multiview-capable shaders.
+
\endlist
\sa {Item Layers}, {QSB Manual}, {Qt Shader Tools Build System Integration}
@@ -641,11 +650,9 @@ void QQuickShaderEffect::setMesh(const QVariant &mesh)
This property defines which sides of the item should be visible.
- \list
- \li ShaderEffect.NoCulling - Both sides are visible
- \li ShaderEffect.BackFaceCulling - only front side is visible
- \li ShaderEffect.FrontFaceCulling - only back side is visible
- \endlist
+ \value ShaderEffect.NoCulling Both sides are visible
+ \value ShaderEffect.BackFaceCulling only the front side is visible
+ \value ShaderEffect.FrontFaceCulling only the back side is visible
The default is NoCulling.
*/
@@ -700,11 +707,9 @@ void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
This property tells the current status of the shaders.
- \list
- \li ShaderEffect.Compiled - the shader program was successfully compiled and linked.
- \li ShaderEffect.Uncompiled - the shader program has not yet been compiled.
- \li ShaderEffect.Error - the shader program failed to compile or link.
- \endlist
+ \value ShaderEffect.Compiled the shader program was successfully compiled and linked.
+ \value ShaderEffect.Uncompiled the shader program has not yet been compiled.
+ \value ShaderEffect.Error the shader program failed to compile or link.
When setting the fragment or vertex shader source code, the status will
become Uncompiled. The first time the ShaderEffect is rendered with new
@@ -985,9 +990,11 @@ void QQuickShaderEffectPrivate::handleEvent(QEvent *event)
{
if (event->type() == QEvent::DynamicPropertyChange) {
const auto propertyName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();
- const auto mappedId = findMappedShaderVariableId(propertyName);
- if (mappedId)
- propertyChanged(*mappedId);
+ for (int i = 0; i < NShader; ++i) {
+ const auto mappedId = findMappedShaderVariableId(propertyName, Shader(i));
+ if (mappedId)
+ propertyChanged(*mappedId);
+ }
}
}
@@ -1039,6 +1046,7 @@ QSGNode *QQuickShaderEffectPrivate::handleUpdatePaintNode(QSGNode *oldNode, QQui
sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
sd.materialTypeCacheKey = q->window();
+ sd.viewCount = QQuickWindowPrivate::get(q->window())->multiViewCount();
node->syncMaterial(&sd);
@@ -1121,6 +1129,7 @@ bool QQuickShaderEffectPrivate::updateUniformValue(const QByteArray &name, const
sd.fragment.dirtyConstants = &dirtyConstants[Fragment];
sd.fragment.dirtyTextures = {};
sd.materialTypeCacheKey = q->window();
+ sd.viewCount = QQuickWindowPrivate::get(q->window())->multiViewCount();
node->syncMaterial(&sd);
@@ -1274,7 +1283,7 @@ bool QQuickShaderEffectPrivate::updateShader(Shader shaderType, const QUrl &file
// provided and monitored like with an application-provided shader.
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("source");
- v.bindPoint = 0; // fake
+ v.bindPoint = 1; // fake, must match the default source bindPoint in qquickshadereffectnode.cpp
v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
: QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
m_shaders[shaderType].shaderInfo.variables.append(v);
@@ -1442,6 +1451,17 @@ std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const Q
return {};
}
+std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const QByteArray &name, Shader shaderType) const
+{
+ const auto &vars = m_shaders[shaderType].shaderInfo.variables;
+ for (int idx = 0; idx < vars.size(); ++idx) {
+ if (vars[idx].name == name)
+ return indexToMappedId(shaderType, idx);
+ }
+
+ return {};
+}
+
bool QQuickShaderEffectPrivate::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
{
for (int shaderType = 0; shaderType < NShader; ++shaderType) {
diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h
index 08db23394e..cfd95864de 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
class QQuickShaderEffectPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
+class Q_QUICK_EXPORT QQuickShaderEffect : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QUrl fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged)
diff --git a/src/quick/items/qquickshadereffect_p_p.h b/src/quick/items/qquickshadereffect_p_p.h
index 2310b6d366..e7224e50a9 100644
--- a/src/quick/items/qquickshadereffect_p_p.h
+++ b/src/quick/items/qquickshadereffect_p_p.h
@@ -92,6 +92,7 @@ private:
void disconnectSignals(Shader shaderType);
void clearMappers(Shader shaderType);
std::optional<int> findMappedShaderVariableId(const QByteArray &name) const;
+ std::optional<int> findMappedShaderVariableId(const QByteArray &name, Shader shaderType) const;
bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const;
bool inDestructor = false;
diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h
index e647daef56..f8a8be1b9f 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -31,13 +31,13 @@ QT_REQUIRE_CONFIG(quick_shadereffect);
QT_BEGIN_NAMESPACE
-Q_QUICK_PRIVATE_EXPORT const char *qtPositionAttributeName();
-Q_QUICK_PRIVATE_EXPORT const char *qtTexCoordAttributeName();
+Q_QUICK_EXPORT const char *qtPositionAttributeName();
+Q_QUICK_EXPORT const char *qtTexCoordAttributeName();
class QSGGeometry;
class QRectF;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectMesh : public QObject
+class Q_QUICK_EXPORT QQuickShaderEffectMesh : public QObject
{
Q_OBJECT
@@ -62,7 +62,7 @@ protected:
QQuickShaderEffectMesh(QObjectPrivate &dd, QObject *parent = nullptr);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickGridMesh : public QQuickShaderEffectMesh
+class Q_QUICK_EXPORT QQuickGridMesh : public QQuickShaderEffectMesh
{
Q_OBJECT
Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index 9df53ff9e9..f9e52a9a6f 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -231,12 +231,10 @@ QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
The default value is \c{ShaderEffectSource.ClampToEdge}.
- \list
- \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
- \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
- \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
- \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
- \endlist
+ \value ShaderEffectSource.ClampToEdge GL_CLAMP_TO_EDGE both horizontally and vertically
+ \value ShaderEffectSource.RepeatHorizontally GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
+ \value ShaderEffectSource.RepeatVertically GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
+ \value ShaderEffectSource.Repeat GL_REPEAT both horizontally and vertically
\note Some OpenGL ES 2 implementations do not support the GL_REPEAT
wrap mode with non-power-of-two textures.
@@ -385,14 +383,12 @@ void QQuickShaderEffectSource::setTextureSize(const QSize &size)
Modifying this property makes most sense when the item is used as a
source texture of a \l ShaderEffect.
- \list
- \li ShaderEffectSource.RGBA8
- \li ShaderEffectSource.RGBA16F
- \li ShaderEffectSource.RGBA32F
- \li ShaderEffectSource.Alpha - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \li ShaderEffectSource.RGB - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \li ShaderEffectSource.RGBA - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \endlist
+ \value ShaderEffectSource.RGBA8
+ \value ShaderEffectSource.RGBA16F
+ \value ShaderEffectSource.RGBA32F
+ \value ShaderEffectSource.Alpha Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGB Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGBA Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
*/
QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
@@ -524,11 +520,9 @@ void QQuickShaderEffectSource::setRecursive(bool enabled)
such as those specified by ShaderEffect. Mirroring has no effect on the UI representation of
the ShaderEffectSource item itself.
- \list
- \li ShaderEffectSource.NoMirroring - No mirroring
- \li ShaderEffectSource.MirrorHorizontally - The generated texture is flipped along X-axis.
- \li ShaderEffectSource.MirrorVertically - The generated texture is flipped along Y-axis.
- \endlist
+ \value ShaderEffectSource.NoMirroring No mirroring
+ \value ShaderEffectSource.MirrorHorizontally The generated texture is flipped along X-axis.
+ \value ShaderEffectSource.MirrorVertically The generated texture is flipped along Y-axis.
*/
QQuickShaderEffectSource::TextureMirroring QQuickShaderEffectSource::textureMirroring() const
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index b49df4f075..ea9a6842e1 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -39,7 +39,7 @@ class QSGSimpleRectNode;
class QQuickShaderEffectSourceTextureProvider;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectSource : public QQuickItem, public QQuickItemChangeListener
+class Q_QUICK_EXPORT QQuickShaderEffectSource : public QQuickItem, public QQuickItemChangeListener
{
Q_OBJECT
Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h
index 3054b8b2a6..004944c628 100644
--- a/src/quick/items/qquicksprite_p.h
+++ b/src/quick/items/qquicksprite_p.h
@@ -23,7 +23,7 @@ QT_REQUIRE_CONFIG(quick_sprite);
#include <QUrl>
#include <QVariantMap>
#include <QQmlListProperty>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include "qquickspriteengine_p.h"
#include <QDebug>
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index ee2fa34f72..3bd129cf98 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -28,13 +28,13 @@ QT_REQUIRE_CONFIG(quick_sprite);
#include <QImage>
#include <QPair>
#include <QRandomGenerator>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
class QQuickSprite;
-class Q_QUICK_PRIVATE_EXPORT QQuickStochasticState : public QObject //Currently for internal use only - Sprite and ParticleGroup
+class Q_QUICK_EXPORT QQuickStochasticState : public QObject //Currently for internal use only - Sprite and ParticleGroup
{
Q_OBJECT
Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
@@ -147,12 +147,12 @@ private:
bool m_randomStart = false;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickStochasticEngine : public QObject
+class Q_QUICK_EXPORT QQuickStochasticEngine : public QObject
{
Q_OBJECT
//TODO: Optimize single state case?
- Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged)
- Q_PROPERTY(QQmlListProperty<QQuickStochasticState> states READ states)
+ Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QQuickStochasticState> states READ states FINAL)
public:
explicit QQuickStochasticEngine(QObject *parent = nullptr);
QQuickStochasticEngine(const QList<QQuickStochasticState*> &states, QObject *parent = nullptr);
@@ -226,10 +226,10 @@ protected:
bool m_addAdvance;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickSpriteEngine : public QQuickStochasticEngine
+class Q_QUICK_EXPORT QQuickSpriteEngine : public QQuickStochasticEngine
{
Q_OBJECT
- Q_PROPERTY(QQmlListProperty<QQuickSprite> sprites READ sprites)
+ Q_PROPERTY(QQmlListProperty<QQuickSprite> sprites READ sprites FINAL)
public:
explicit QQuickSpriteEngine(QObject *parent = nullptr);
QQuickSpriteEngine(const QList<QQuickSprite*> &sprites, QObject *parent = nullptr);
diff --git a/src/quick/items/qquickspritesequence_p.h b/src/quick/items/qquickspritesequence_p.h
index f4fc6def58..eb80401d6e 100644
--- a/src/quick/items/qquickspritesequence_p.h
+++ b/src/quick/items/qquickspritesequence_p.h
@@ -28,7 +28,7 @@ class QQuickSprite;
class QQuickSpriteEngine;
class QQuickSpriteSequencePrivate;
class QSGSpriteNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickSpriteSequence : public QQuickItem
+class Q_QUICK_EXPORT QQuickSpriteSequence : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index a6e5eed56c..35b29b9134 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -7,7 +7,10 @@
#include <private/qquickstate_p_p.h>
#include <QtQml/qqmlinfo.h>
+
#include <QtCore/qmath.h>
+#include <QtCore/qpointer.h>
+
#include <memory>
QT_BEGIN_NAMESPACE
@@ -156,7 +159,7 @@ QQuickParentChange::QQuickParentChange(QObject *parent)
QQmlScriptString QQuickParentChange::x() const
{
Q_D(const QQuickParentChange);
- return d->xString.value;
+ return d->xString.value();
}
void QQuickParentChange::setX(const QQmlScriptString &x)
@@ -174,7 +177,7 @@ bool QQuickParentChange::xIsSet() const
QQmlScriptString QQuickParentChange::y() const
{
Q_D(const QQuickParentChange);
- return d->yString.value;
+ return d->yString.value();
}
void QQuickParentChange::setY(const QQmlScriptString &y)
@@ -192,7 +195,7 @@ bool QQuickParentChange::yIsSet() const
QQmlScriptString QQuickParentChange::width() const
{
Q_D(const QQuickParentChange);
- return d->widthString.value;
+ return d->widthString.value();
}
void QQuickParentChange::setWidth(const QQmlScriptString &width)
@@ -210,7 +213,7 @@ bool QQuickParentChange::widthIsSet() const
QQmlScriptString QQuickParentChange::height() const
{
Q_D(const QQuickParentChange);
- return d->heightString.value;
+ return d->heightString.value();
}
void QQuickParentChange::setHeight(const QQmlScriptString &height)
@@ -228,7 +231,7 @@ bool QQuickParentChange::heightIsSet() const
QQmlScriptString QQuickParentChange::scale() const
{
Q_D(const QQuickParentChange);
- return d->scaleString.value;
+ return d->scaleString.value();
}
void QQuickParentChange::setScale(const QQmlScriptString &scale)
@@ -246,7 +249,7 @@ bool QQuickParentChange::scaleIsSet() const
QQmlScriptString QQuickParentChange::rotation() const
{
Q_D(const QQuickParentChange);
- return d->rotationString.value;
+ return d->rotationString.value();
}
void QQuickParentChange::setRotation(const QQmlScriptString &rotation)
@@ -313,13 +316,14 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->xString.isValid()) {
bool ok = false;
- qreal x = d->xString.value.numberLiteral(&ok);
+ qreal x = d->xString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction xa(d->target, QLatin1String("x"), x);
actions << xa;
} else {
QQmlProperty property(d->target, QLatin1String("x"));
- auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->xString.value, d->target, qmlContext(this));
+ auto newBinding = QQmlAnyBinding::createFromScriptString(
+ property, d->xString.value(), d->target, qmlContext(this));
QQuickStateAction xa;
xa.property = property;
xa.toBinding = newBinding;
@@ -331,13 +335,14 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->yString.isValid()) {
bool ok = false;
- qreal y = d->yString.value.numberLiteral(&ok);
+ qreal y = d->yString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction ya(d->target, QLatin1String("y"), y);
actions << ya;
} else {
QQmlProperty property(d->target, QLatin1String("y"));
- auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->yString.value, d->target, qmlContext(this));
+ auto newBinding = QQmlAnyBinding::createFromScriptString(
+ property, d->yString.value(), d->target, qmlContext(this));
QQuickStateAction ya;
ya.property = property;
ya.toBinding = newBinding;
@@ -349,13 +354,14 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->scaleString.isValid()) {
bool ok = false;
- qreal scale = d->scaleString.value.numberLiteral(&ok);
+ qreal scale = d->scaleString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction sa(d->target, QLatin1String("scale"), scale);
actions << sa;
} else {
QQmlProperty property(d->target, QLatin1String("scale"));
- auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->scaleString.value, d->target, qmlContext(this));
+ auto newBinding = QQmlAnyBinding::createFromScriptString(
+ property, d->scaleString.value(), d->target, qmlContext(this));
QQuickStateAction sa;
sa.property = property;
sa.toBinding = newBinding;
@@ -367,13 +373,14 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->rotationString.isValid()) {
bool ok = false;
- qreal rotation = d->rotationString.value.numberLiteral(&ok);
+ qreal rotation = d->rotationString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction ra(d->target, QLatin1String("rotation"), rotation);
actions << ra;
} else {
QQmlProperty property(d->target, QLatin1String("rotation"));
- auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->rotationString.value, d->target, qmlContext(this));
+ auto newBinding = QQmlAnyBinding::createFromScriptString(
+ property, d->rotationString.value(), d->target, qmlContext(this));
QQuickStateAction ra;
ra.property = property;
ra.toBinding = newBinding;
@@ -385,7 +392,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->widthString.isValid()) {
bool ok = false;
- qreal width = d->widthString.value.numberLiteral(&ok);
+ qreal width = d->widthString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction wa(d->target, QLatin1String("width"), width);
actions << wa;
@@ -403,7 +410,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->heightString.isValid()) {
bool ok = false;
- qreal height = d->heightString.value.numberLiteral(&ok);
+ qreal height = d->heightString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction ha(d->target, QLatin1String("height"), height);
actions << ha;
@@ -1063,6 +1070,9 @@ void QQuickAnchorChanges::reverse()
stateHAnchors != QQuickAnchors::LeftAnchor &&
stateHAnchors != QQuickAnchors::RightAnchor &&
stateHAnchors != QQuickAnchors::HCenterAnchor);
+ // in case of an additive AnchorChange, we _did_ end up modifying the width
+ stateSetWidth |= ((stateHAnchors & QQuickAnchors::LeftAnchor) && (origHAnchors & QQuickAnchors::RightAnchor)) ||
+ ((stateHAnchors & QQuickAnchors::RightAnchor) && (origHAnchors & QQuickAnchors::LeftAnchor));
bool origSetWidth = (origHAnchors &&
origHAnchors != QQuickAnchors::LeftAnchor &&
origHAnchors != QQuickAnchors::RightAnchor &&
@@ -1078,12 +1088,15 @@ void QQuickAnchorChanges::reverse()
stateVAnchors != QQuickAnchors::BottomAnchor &&
stateVAnchors != QQuickAnchors::VCenterAnchor &&
stateVAnchors != QQuickAnchors::BaselineAnchor);
+ // in case of an additive AnchorChange, we _did_ end up modifying the height
+ stateSetHeight |= ((stateVAnchors & QQuickAnchors::TopAnchor) && (origVAnchors & QQuickAnchors::BottomAnchor)) ||
+ ((stateVAnchors & QQuickAnchors::BottomAnchor) && (origVAnchors & QQuickAnchors::TopAnchor));
bool origSetHeight = (origVAnchors &&
origVAnchors != QQuickAnchors::TopAnchor &&
origVAnchors != QQuickAnchors::BottomAnchor &&
origVAnchors != QQuickAnchors::VCenterAnchor &&
origVAnchors != QQuickAnchors::BaselineAnchor);
- if (d->origHeight.isValid() && stateSetHeight && !origSetHeight && !!qt_is_nan(d->origHeight)) {
+ if (d->origHeight.isValid() && stateSetHeight && !origSetHeight && !qt_is_nan(d->origHeight)) {
targetPrivate->heightValidFlag = true;
if (targetPrivate->height != d->origHeight)
targetPrivate->height.setValueBypassingBindings(d->origHeight);
@@ -1097,7 +1110,12 @@ void QQuickAnchorChanges::reverse()
const QRectF newGeometry(d->target->position(), d->target->size());
if (newGeometry != oldGeometry) {
- targetPrivate->dirty(QQuickItemPrivate::Position);
+ QQuickItemPrivate::DirtyType dirtyFlags {};
+ if (newGeometry.topLeft() != oldGeometry.topLeft())
+ dirtyFlags = QQuickItemPrivate::DirtyType(dirtyFlags | QQuickItemPrivate::Position);
+ if (newGeometry.size() != oldGeometry.size())
+ dirtyFlags = QQuickItemPrivate::DirtyType(dirtyFlags | QQuickItemPrivate::Size);
+ targetPrivate->dirty(dirtyFlags);
d->target->geometryChange(newGeometry, oldGeometry);
}
}
diff --git a/src/quick/items/qquickstateoperations_p.h b/src/quick/items/qquickstateoperations_p.h
index 54b9a8325b..170e5b3b14 100644
--- a/src/quick/items/qquickstateoperations_p.h
+++ b/src/quick/items/qquickstateoperations_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
class QQuickParentChangePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickParentChange : public QQuickStateOperation, public QQuickStateActionEvent
+class Q_QUICK_EXPORT QQuickParentChange : public QQuickStateOperation, public QQuickStateActionEvent
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickParentChange)
@@ -91,17 +91,17 @@ public:
class QQuickAnchorChanges;
class QQuickAnchorSetPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchorSet : public QObject
+class Q_QUICK_EXPORT QQuickAnchorSet : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQmlScriptString left READ left WRITE setLeft RESET resetLeft)
- Q_PROPERTY(QQmlScriptString right READ right WRITE setRight RESET resetRight)
- Q_PROPERTY(QQmlScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter)
- Q_PROPERTY(QQmlScriptString top READ top WRITE setTop RESET resetTop)
- Q_PROPERTY(QQmlScriptString bottom READ bottom WRITE setBottom RESET resetBottom)
- Q_PROPERTY(QQmlScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter)
- Q_PROPERTY(QQmlScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline)
+ Q_PROPERTY(QQmlScriptString left READ left WRITE setLeft RESET resetLeft FINAL)
+ Q_PROPERTY(QQmlScriptString right READ right WRITE setRight RESET resetRight FINAL)
+ Q_PROPERTY(QQmlScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter FINAL)
+ Q_PROPERTY(QQmlScriptString top READ top WRITE setTop RESET resetTop FINAL)
+ Q_PROPERTY(QQmlScriptString bottom READ bottom WRITE setBottom RESET resetBottom FINAL)
+ Q_PROPERTY(QQmlScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter FINAL)
+ Q_PROPERTY(QQmlScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -146,7 +146,7 @@ private:
};
class QQuickAnchorChangesPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchorChanges : public QQuickStateOperation, public QQuickStateActionEvent
+class Q_QUICK_EXPORT QQuickAnchorChanges : public QQuickStateOperation, public QQuickStateActionEvent
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnchorChanges)
@@ -185,9 +185,5 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickParentChange)
-QML_DECLARE_TYPE(QQuickAnchorSet)
-QML_DECLARE_TYPE(QQuickAnchorChanges)
-
#endif // QQUICKSTATEOPERATIONS_P_H
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 81eddb03ce..6203f6f3c1 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -40,6 +40,16 @@
in a TableView. To create models with multiple columns, either use
\l TableModel or a C++ model that inherits QAbstractItemModel.
+ A TableView does not include headers by default. You can add headers
+ using the \l HorizontalHeaderView and \l VerticalHeaderView from
+ Qt Quick Controls.
+
+ \note TableView will only \l {isRowLoaded()}{load} as many delegate items as
+ needed to fill up the view. There is no guarantee that items outside the view
+ will be loaded, although TableView will sometimes pre-load items for
+ optimization reasons. Hence, a TableView with zero width or height might not
+ load any delegate items at all.
+
\section1 Example Usage
\section2 C++ Models
@@ -102,7 +112,7 @@
the width of the column. Otherwise, it will check if an explicit width has
been set with \l setColumnWidth(). If not, \l implicitColumnWidth() will be used.
The implicit width of a column is the same as the largest
- \l {implicit width}{QQuickItem::implicitWidth()} found among the currently loaded
+ \l {Item::implicitWidth}{implicit width} found among the currently loaded
delegate items in that column. Trying to set an explicit \c width directly on
a delegate has no effect, and will be ignored and overwritten. The same logic also
applies to row heights.
@@ -126,7 +136,7 @@
of the view, and is recalculated again if it's flicked back in. This means that if the
width depends on the \l implicitColumnWidth(), the calculation can be different each time,
depending on which row you're at when the column enters (since \l implicitColumnWidth()
- only considers the delegate items that are currently \l {loaded}{isColumnLoaded()}).
+ only considers the delegate items that are currently \l {isColumnLoaded()}{loaded}).
To avoid this, you should use a \l columnWidthProvider, or ensure that all the delegate
items in the same column have the same \c implicitWidth.
@@ -156,13 +166,13 @@
You can let the user edit table cells by providing an edit delegate. The
edit delegate will be instantiated according to the \l editTriggers, which
by default is when the user double taps on a cell, or presses e.g
- \l Qt.Key_Enter or \l Qt.Key_Return. The edit delegate is set using
+ \l Qt::Key_Enter or \l Qt::Key_Return. The edit delegate is set using
\l {TableView::editDelegate}, which is an attached property that you set
on the \l delegate. The following snippet shows how to do that:
\snippet qml/tableview/editdelegate.qml 0
- If the user presses Qt.Key_Enter or Qt.Key_Return while the edit delegate
+ If the user presses Qt::Key_Enter or Qt::Key_Return while the edit delegate
is active, TableView will emit the \l TableView::commit signal to the edit
delegate, so that it can write back the changed data to the model.
@@ -207,7 +217,7 @@
\snippet qml/tableview/overlay.qml 0
You could also parent the overlay directly to the cell instead of the
- \l contentItem. But doing so will be fragile since the cell is unloaded
+ \l {Flickable::}{contentItem}. But doing so will be fragile since the cell is unloaded
or reused whenever it's flicked out of the viewport.
\sa layoutChanged()
@@ -248,7 +258,7 @@
to let the user select cells.
\note By default, a cell will become
- \l {QQuickItemSelectionModel::currentIndex()}{current}, and any selections will
+ \l {ItemSelectionModel::currentIndex}{current}, and any selections will
be removed, when the user taps on it. If such default tap behavior is not wanted
(e.g if you use custom pointer handlers inside your delegate), you can set
\l pointerNavigationEnabled to \c false.
@@ -257,11 +267,17 @@
In order to support keyboard navigation, you need to assign an \l ItemSelectionModel
to the \l selectionModel property. TableView will then use this model to manipulate
- the model's \l {ItemSelectionModel::currentIndex}{currentIndex}. You can
- disable keyboard navigation fully (in case you want to implement your own key
- handlers) by setting \l keyNavigationEnabled to \c false. Below is an
- example that demonstrates how to use keyboard navigation together with
- \c current and \c selected properties:
+ the model's \l {ItemSelectionModel::currentIndex}{currentIndex}.
+
+ It's the responsibility of the delegate to render itself as
+ \l {ItemSelectionModel::currentIndex}{current}. You can do this by adding a
+ property \c {required property bool current} to it, and let the appearance
+ depend on its state. The \c current property's value is set by the TableView.
+ You can also disable keyboard navigation fully (in case you want to implement your
+ own key handlers) by setting \l keyNavigationEnabled to \c false.
+
+ The following example demonstrates how you can use keyboard navigation together
+ with \c current and \c selected properties:
\snippet qml/tableview/keyboard-navigation.qml 0
@@ -324,7 +340,7 @@
}
\endcode
- \sa mimeData(), dropMimeData(), QUndoStack, QUndoCommand, QClipboard
+ \sa QAbstractItemModel::mimeData(), QAbstractItemModel::dropMimeData(), QUndoStack, QUndoCommand, QClipboard
*/
/*!
@@ -442,11 +458,32 @@
\l {Item::}{implicitHeight}. The TableView lays out the items based on that
information. Explicit width or height settings are ignored and overwritten.
+ Inside the delegate, you can optionally add one or more of the following
+ properties. TableView modifies the values of these properties to inform the
+ delegate which state it's in. This can be used by the delegate to render
+ itself differently according on its own state.
+
+ \list
+ \li required property bool current - \c true if the delegate is \l {Keyboard navigation}{current.}
+ \li required property bool selected - \c true if the delegate is \l {Selecting items}{selected.}
+ \li required property bool editing - \c true if the delegate is being \l {Editing cells}{edited.}
+ \endlist
+
+ The following example shows how to use these properties:
+ \code
+ delegate: Rectangle {
+ required property bool current
+ required property bool selected
+ border.width: current ? 1 : 0
+ color: selected ? palette.highlight : palette.base
+ }
+ \endcode
+
\note Delegates are instantiated as needed and may be destroyed at any time.
They are also reused if the \l reuseItems property is set to \c true. You
should therefore avoid storing state information in the delegates.
- \sa {Row heights and column widths}, {Reusing items}
+ \sa {Row heights and column widths}, {Reusing items}, {Required Properties}
*/
/*!
@@ -570,7 +607,7 @@
\readonly
This read-only property holds the column in the view that contains the
- item that is current. If no item is current, it will be \c -1.
+ item that is \l {Keyboard navigation}{current.} If no item is current, it will be \c -1.
\note In order for TableView to report what the current column is, you
need to assign an \l ItemSelectionModel to \l selectionModel.
@@ -583,7 +620,7 @@
\readonly
This read-only property holds the row in the view that contains the item
- that is current. If no item is current, it will be \c -1.
+ that is \l {Keyboard navigation}{current.} If no item is current, it will be \c -1.
\note In order for TableView to report what the current row is, you
need to assign an \l ItemSelectionModel to \l selectionModel.
@@ -656,16 +693,47 @@
\qmlproperty enumeration QtQuick::TableView::selectionBehavior
\since 6.4
- This property holds whether the user can select single cells, rows or columns.
+ This property holds whether the user can select cells, rows or columns.
- \list
- \li TableView.SelectionDisabled - the user cannot perform selections.
- \li TableView.SelectCells (default) - the user can select individual cells.
- \li TableView.SelectRows - the user can only select rows.
- \li TableView.SelectColumns - the user can only select columns.
- \endlist
+ \value TableView.SelectionDisabled
+ The user cannot perform selections
+ \value TableView.SelectCells
+ (Default value) The user can select individual cells
+ \value TableView.SelectRows
+ The user can only select rows
+ \value TableView.SelectColumns
+ The user can only select columns
- \sa {Selecting items}, selectionModel, keyNavigationEnabled
+ \sa {Selecting items}, selectionMode, selectionModel, keyNavigationEnabled
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::TableView::selectionMode
+ \since 6.6
+
+ If \l selectionBehavior is set to \c {TableView.SelectCells}, this property holds
+ whether the user can select one cell at a time, or multiple cells.
+ If \l selectionBehavior is set to \c {TableView.SelectRows}, this property holds
+ whether the user can select one row at a time, or multiple rows.
+ If \l selectionBehavior is set to \c {TableView.SelectColumns}, this property holds
+ whether the user can select one column at a time, or multiple columns.
+
+ The following modes are available:
+
+ \value TableView.SingleSelection
+ The user can select a single cell, row or column.
+ \value TableView.ContiguousSelection
+ The user can select a single contiguous block of cells.
+ An existing selection can be made bigger or smaller by holding down
+ the \c Shift modifier while selecting.
+ \value TableView.ExtendedSelection
+ (Default value) The user can select multiple individual blocks of
+ cells. An existing selection can be made bigger or smaller by
+ holding down the \c Shift modifier while selecting. A new selection
+ block can be started without clearing the current selection by
+ holding down the \c Control modifier while selecting.
+
+ \sa {Selecting items}, selectionBehavior, selectionModel, keyNavigationEnabled
*/
/*!
@@ -698,25 +766,25 @@
But the application can call \l edit() and \l closeEditor() manually.
\value TableView.SingleTapped - the user can edit a cell by single tapping it.
\value TableView.DoubleTapped - the user can edit a cell by double tapping it.
- \value TableView.SelectedTapped - the user can edit the
- \l {QItemSelectionModel::currentIndex()}{current cell} by tapping it.
+ \value TableView.SelectedTapped - the user can edit a
+ \l {QItemSelectionModel::selectedIndexes()}{selected cell} by tapping it.
\value TableView.EditKeyPressed - the user can edit the
- \l {QItemSelectionModel::currentIndex()}{current cell} by pressing one
+ \l {ItemSelectionModel::currentIndex}{current cell} by pressing one
of the edit keys. The edit keys are decided by the OS, but are normally
- \c Qt.Key_Enter and \c Qt.Key_Return.
+ \c Qt::Key_Enter and \c Qt::Key_Return.
\value TableView.AnyKeyPressed - the user can edit the
- \l {TableView::current}{current cell} by pressing any key, other
+ \l {ItemSelectionModel::currentIndex}{current cell} by pressing any key, other
than the cell navigation keys. The pressed key is also sent to the
focus object inside the \l {TableView::editDelegate}{edit delegate}.
For \c TableView.SelectedTapped, \c TableView.EditKeyPressed, and
\c TableView.AnyKeyPressed to have any effect, TableView needs to have a
\l {selectionModel}{selection model} assigned, since they depend on a
- \l {QItemSelectionModel::currentIndex()}{current index} being set. To be
+ \l {ItemSelectionModel::currentIndex}{current index} being set. To be
able to receive any key events at all, TableView will also need to have
\l QQuickItem::activeFocus.
- When editing a cell, the user can press \c Qt.Key_Tab or \c Qt.Key_Backtab
+ When editing a cell, the user can press \c Qt::Key_Tab or \c Qt::Key_Backtab
to \l {TableView::commit}{commit} the data, and move editing to the next
cell. This behavior can be disabled by setting
\l QQuickItem::activeFocusOnTab on TableView to \c false.
@@ -801,6 +869,20 @@
*/
/*!
+ \qmlmethod QtQuick::TableView::positionViewAtIndex(QModelIndex index, PositionMode mode, point offset, rect subRect)
+ \since 6.5
+
+ Positions the view such that \a index is at the position specified
+ by \a mode, \a offset and \a subRect.
+
+ Convenience method for calling
+ \code
+ positionViewAtRow(rowAtIndex(index), mode & Qt.AlignVertical_Mask, offset.y, subRect)
+ positionViewAtColumn(columnAtIndex(index), mode & Qt.AlignVertical_Mask, offset.x, subRect)
+ \endcode
+*/
+
+/*!
\qmlmethod bool QtQuick::TableView::isColumnLoaded(int column)
\since 6.2
@@ -832,14 +914,9 @@
/*!
\qmlmethod QtQuick::TableView::positionViewAtCell(int column, int row, PositionMode mode, point offset, rect subRect)
+ \deprecated
- Positions \l {Flickable::}{contentX} and \l {Flickable::}{contentY} such
- that \a row and \a column is at the position specified by \a mode, \a offset and \a subRect.
-
- Convenience for calling
- \code
- positionViewAtCell(Qt.point(column, row), mode, offset, subRect)
- \endcode
+ Use \l {positionViewAtIndex()}{positionViewAtIndex(index(row, column), ...)} instead.
*/
/*!
@@ -879,8 +956,27 @@
/*!
\qmlmethod Item QtQuick::TableView::itemAtCell(int column, int row)
+ \deprecated
- Convenience for calling \c{itemAtCell(Qt.point(column, row))}.
+ Use \l {itemAtIndex()}{itemAtIndex(index(row, column))} instead.
+*/
+
+/*!
+ \qmlmethod Item QtQuick::TableView::itemAtIndex(QModelIndex index)
+ \since 6.5
+
+ Returns the instantiated delegate item for the cell that represents
+ \a index. If the item is not \l {isRowLoaded()}{loaded}, the value
+ will be \c null.
+
+ \note only the items that are visible in the view are normally loaded.
+ As soon as a cell is flicked out of the view, the item inside will
+ either be unloaded or placed in the recycle pool. As such, the return
+ value should never be stored.
+
+ \note If the \l model is not a QAbstractItemModel, you can also use
+ \l {itemAtCell()}{itemAtCell(Qt.point(column, row))}. But be aware
+ that \c {point.x} maps to columns and \c {point.y} maps to rows.
*/
/*!
@@ -929,15 +1025,6 @@
Returns the width of the given \a column. If the column is not
loaded (and therefore not visible), the return value will be \c -1.
- \note It's the applications responsibility to store what the
- column widths are, by using a \l columnWidthProvider. Hence,
- there is no setter function. This getter function is mostly
- useful if the TableView doesn't have a columnWidthProvider set, since
- otherwise you can call that function instead (which will work, even
- for columns that are not currently visible).
- If no columnWidthProvider is set, the width of a column will be
- equal to its \l implicitColumnWidth().
-
\sa columnWidthProvider, implicitColumnWidth(), isColumnLoaded(), {Row heights and column widths}
*/
@@ -948,15 +1035,6 @@
Returns the height of the given \a row. If the row is not
loaded (and therefore not visible), the return value will be \c -1.
- \note It's the applications responsibility to store what the
- row heights are, by using a \l rowHeightProvider. Hence,
- there is no setter function. This getter function is mostly
- useful if the TableView doesn't have a rowHeightProvider set, since
- otherwise you can call that function instead (which will work, even
- for rows that are not currently visible).
- If no rowHeightProvider is set, the height of a row will be
- equal to its \l implicitRowHeight().
-
\sa rowHeightProvider, implicitRowHeight(), isRowLoaded(), {Row heights and column widths}
*/
@@ -1052,7 +1130,7 @@
\qmlmethod qreal QtQuick::TableView::explicitColumnWidth(int column)
Returns the width of the \a column set with \l setColumnWidth(). This width might
- differ from the actual width of the column, if a \l columnWidthProvider()
+ differ from the actual width of the column, if a \l columnWidthProvider
is in use. To get the actual width of a column, use \l columnWidth().
A return value equal to \c 0 means that the column has been told to hide.
@@ -1124,7 +1202,7 @@
\qmlmethod qreal QtQuick::TableView::explicitRowHeight(int row)
Returns the height of the \a row set with \l setRowHeight(). This height might
- differ from the actual height of the column, if a \l rowHeightProvider()
+ differ from the actual height of the column, if a \l rowHeightProvider
is in use. To get the actual height of a row, use \l rowHeight().
A return value equal to \c 0 means that the row has been told to hide.
@@ -1139,18 +1217,14 @@
/*!
\qmlmethod QModelIndex QtQuick::TableView::modelIndex(int row, int column)
\since 6.4
+ \deprecated
- Returns the \l QModelIndex that maps to \a column and \a row in the view.
+ Use \l {QtQuick::TableView::}{index(int row, int column)} instead.
- \a row and \a column should be the row and column in the view (table row and
- table column), and not a row and column in the model.
-
- \note Because of an API incompatible change in Qt 6.4.0 and Qt 6.4.1, the
+ \note Because of an API incompatible change between Qt 6.4.0 and Qt 6.4.2, the
order of \c row and \c column was specified in the opposite order. If you
rely on the order to be \c {modelIndex(column, row)}, you can set the
environment variable \c QT_QUICK_TABLEVIEW_COMPAT_VERSION to \c 6.4
-
- \sa rowAtIndex(), columnAtIndex()
*/
/*!
@@ -1162,19 +1236,35 @@
modelIndex(cell.y, cell.x)
\endcode
- A cell is simply a \l point that combines row and column into
+ A \a cell is simply a \l point that combines row and column into
a single type.
\note \c {point.x} will map to the column, and \c {point.y} will map to the row.
*/
/*!
+ \qmlmethod QModelIndex QtQuick::TableView::index(int row, int column)
+ \since 6.4.3
+
+ Returns the \l QModelIndex that maps to \a row and \a column in the view.
+
+ \a row and \a column should be the row and column in the view (table row and
+ table column), and not a row and column in the model. For a plain
+ TableView, this is equivalent of calling \c {model.index(row, column).}
+ But for a subclass of TableView, like TreeView, where the data model is
+ wrapped inside an internal proxy model that flattens the tree structure
+ into a table, you need to use this function to resolve the model index.
+
+ \sa rowAtIndex(), columnAtIndex()
+*/
+
+/*!
\qmlmethod int QtQuick::TableView::rowAtIndex(QModelIndex modelIndex)
\since 6.4
Returns the row in the view that maps to \a modelIndex in the model.
- \sa columnAtIndex(), modelIndex()
+ \sa columnAtIndex(), index()
*/
/*!
@@ -1183,7 +1273,7 @@
Returns the column in the view that maps to \a modelIndex in the model.
- \sa rowAtIndex(), modelIndex()
+ \sa rowAtIndex(), index()
*/
/*!
@@ -1291,13 +1381,13 @@
This signal is emitted by the \l {TableView::editDelegate}{edit delegate}
This attached signal is emitted when the \l {TableView::editDelegate}{edit delegate}
- is active, and the user presses \l Qt.Key_Enter or \l Qt.Key_Return. It will also
+ is active, and the user presses \l Qt::Key_Enter or \l Qt::Key_Return. It will also
be emitted if TableView has \l QQuickItem::activeFocusOnTab set, and the user
- presses Qt.Key_Tab or Qt.Key_Backtab.
+ presses Qt::Key_Tab or Qt::Key_Backtab.
This signal will \e not be emitted if editing ends because of reasons other
than the ones mentioned. This includes e.g if the user presses
- Qt.Key_Escape, taps outside the delegate, the row or column being
+ Qt::Key_Escape, taps outside the delegate, the row or column being
edited is deleted, or if the application calls \l closeEditor().
Upon receiving the signal, the edit delegate should write any modified data
@@ -1315,7 +1405,7 @@
This attached property holds the edit delegate. It's instantiated
when editing begins, and parented to the delegate it edits. It
supports the same required properties as the
- \l {\l delegate}{TableView delegate}, including \c index, \c row and \c column.
+ \l {TableView::delegate}{TableView delegate}, including \c index, \c row and \c column.
Properties of the model, like \c display and \c edit, are also available
(depending on the \l {QAbstractItemModel::roleNames()}{role names} exposed
by the model).
@@ -1330,12 +1420,12 @@
and \l closeEditor(), respectively. The \c Qt::ItemIsEditable flag will
then be ignored.
- Editing ends when the user presses \c Qt.Key_Enter or \c Qt.Key_Return
- (and also \c Qt.Key_Tab or \c Qt.Key_Backtab, if TableView has
+ Editing ends when the user presses \c Qt::Key_Enter or \c Qt::Key_Return
+ (and also \c Qt::Key_Tab or \c Qt::Key_Backtab, if TableView has
\l QQuickItem::activeFocusOnTab set). In that case, the \l TableView::commit
signal will be emitted, so that the edit delegate can respond by writing any
modified data back to the model. If editing ends because of other reasons
- (e.g if the user presses Qt.Key_Escape), the signal will not be emitted.
+ (e.g if the user presses Qt::Key_Escape), the signal will not be emitted.
In any case will \l {Component::destruction}{destruction()} be emitted in the end.
While the edit delegate is showing, the cell underneath will still be visible, and
@@ -1344,7 +1434,7 @@
of the edit delegate be a solid \l Rectangle, or hide some of the items
inside the \l {TableView::delegate}{TableView delegate.}. The latter can be done
by defining a property \c {required property bool editing} inside it, that you
- bind to the \l visible property of some of the child items.
+ bind to the \l {QQuickItem::}{visible} property of some of the child items.
The following snippet shows how to do that:
\snippet qml/tableview/editdelegate.qml 1
@@ -1370,6 +1460,51 @@ static const char* kRequiredProperty_selected = "selected";
static const char* kRequiredProperty_current = "current";
static const char* kRequiredProperty_editing = "editing";
+QDebug operator<<(QDebug dbg, QQuickTableViewPrivate::RebuildState state)
+{
+#define TV_REBUILDSTATE(STATE) \
+ case QQuickTableViewPrivate::RebuildState::STATE: \
+ dbg << QStringLiteral(#STATE); break;
+
+ switch (state) {
+ TV_REBUILDSTATE(Begin);
+ TV_REBUILDSTATE(LoadInitalTable);
+ TV_REBUILDSTATE(VerifyTable);
+ TV_REBUILDSTATE(LayoutTable);
+ TV_REBUILDSTATE(CancelOvershoot);
+ TV_REBUILDSTATE(UpdateContentSize);
+ TV_REBUILDSTATE(PreloadColumns);
+ TV_REBUILDSTATE(PreloadRows);
+ TV_REBUILDSTATE(MovePreloadedItemsToPool);
+ TV_REBUILDSTATE(Done);
+ }
+
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, QQuickTableViewPrivate::RebuildOptions options)
+{
+#define TV_REBUILDOPTION(OPTION) \
+ if (options & QQuickTableViewPrivate::RebuildOption::OPTION) \
+ dbg << QStringLiteral(#OPTION)
+
+ if (options == QQuickTableViewPrivate::RebuildOption::None) {
+ dbg << QStringLiteral("None");
+ } else {
+ TV_REBUILDOPTION(All);
+ TV_REBUILDOPTION(LayoutOnly);
+ TV_REBUILDOPTION(ViewportOnly);
+ TV_REBUILDOPTION(CalculateNewTopLeftRow);
+ TV_REBUILDOPTION(CalculateNewTopLeftColumn);
+ TV_REBUILDOPTION(CalculateNewContentWidth);
+ TV_REBUILDOPTION(CalculateNewContentHeight);
+ TV_REBUILDOPTION(PositionViewAtRow);
+ TV_REBUILDOPTION(PositionViewAtColumn);
+ }
+
+ return dbg;
+}
+
QQuickTableViewPrivate::EdgeRange::EdgeRange()
: startIndex(kEdgeIndexNotSet)
, endIndex(kEdgeIndexNotSet)
@@ -1528,10 +1663,15 @@ QQuickItem *QQuickTableViewPrivate::selectionPointerHandlerTarget() const
return const_cast<QQuickTableView *>(q_func())->contentItem();
}
-bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
+bool QQuickTableViewPrivate::startSelection(const QPointF &pos, Qt::KeyboardModifiers modifiers)
{
Q_Q(QQuickTableView);
- Q_UNUSED(pos);
+ if (!selectionModel) {
+ if (warnNoSelectionModel)
+ qmlWarning(q_func()) << "Cannot start selection: no SelectionModel assigned!";
+ warnNoSelectionModel = false;
+ return false;
+ }
if (selectionBehavior == QQuickTableView::SelectionDisabled) {
qmlWarning(q) << "Cannot start selection: TableView.selectionBehavior == TableView.SelectionDisabled";
@@ -1542,6 +1682,25 @@ bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
if (resizeHandler->state() != QQuickTableViewResizeHandler::Listening)
return false;
+ // For SingleSelection and ContiguousSelection, we should only allow one selection at a time
+ if (selectionMode == QQuickTableView::SingleSelection
+ || selectionMode == QQuickTableView::ContiguousSelection)
+ clearSelection();
+ else if (selectionModel)
+ existingSelection = selectionModel->selection();
+
+ // If pos is on top of an unselected cell, we start a session where the user selects which
+ // cells to become selected. Otherwise, if pos is on top of an already selected cell and
+ // ctrl is being held, we start a session where the user selects which selected cells to
+ // become unselected.
+ selectionFlag = QItemSelectionModel::Select;
+ if (modifiers & Qt::ControlModifier) {
+ QPoint startCell = clampedCellAtPos(pos);
+ const QModelIndex startIndex = q->index(startCell.y(), startCell.x());
+ if (selectionModel->isSelected(startIndex))
+ selectionFlag = QItemSelectionModel::Deselect;
+ }
+
selectionStartCell = QPoint(-1, -1);
selectionEndCell = QPoint(-1, -1);
q->closeEditor();
@@ -1550,6 +1709,8 @@ bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
{
+ Q_Q(QQuickTableView);
+ Q_ASSERT(selectionFlag != QItemSelectionModel::NoUpdate);
if (loadedItems.isEmpty())
return;
if (!selectionModel) {
@@ -1562,13 +1723,26 @@ void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
if (!qaim)
return;
+ if (selectionMode == QQuickTableView::SingleSelection
+ && cellIsValid(selectionStartCell)) {
+ return;
+ }
+
const QRect prevSelection = selection();
- const QPoint clampedCell = clampedCellAtPos(pos);
+
+ QPoint clampedCell;
+ if (pos.x() == -1) {
+ // Special case: use current cell as start cell
+ clampedCell = q->cellAtIndex(selectionModel->currentIndex());
+ } else {
+ clampedCell = clampedCellAtPos(pos);
+ if (cellIsValid(clampedCell))
+ setCurrentIndex(clampedCell);
+ }
+
if (!cellIsValid(clampedCell))
return;
- setCurrentIndex(clampedCell);
-
switch (selectionBehavior) {
case QQuickTableView::SelectCells:
selectionStartCell = clampedCell;
@@ -1587,11 +1761,13 @@ void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
return;
// Update selection model
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
updateSelection(prevSelection, selection());
}
void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
{
+ Q_ASSERT(selectionFlag != QItemSelectionModel::NoUpdate);
if (loadedItems.isEmpty())
return;
if (!selectionModel) {
@@ -1605,9 +1781,15 @@ void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
return;
const QRect prevSelection = selection();
- const QPoint clampedCell = clampedCellAtPos(pos);
- if (!cellIsValid(clampedCell))
- return;
+
+ QPoint clampedCell;
+ if (selectionMode == QQuickTableView::SingleSelection) {
+ clampedCell = selectionStartCell;
+ } else {
+ clampedCell = clampedCellAtPos(pos);
+ if (!cellIsValid(clampedCell))
+ return;
+ }
setCurrentIndex(clampedCell);
@@ -1629,6 +1811,7 @@ void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
return;
// Update selection model
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
updateSelection(prevSelection, selection());
}
@@ -1659,42 +1842,69 @@ void QQuickTableViewPrivate::updateSelection(const QRect &oldSelection, const QR
const QRect oldRect = oldSelection.normalized();
const QRect newRect = newSelection.normalized();
+ QItemSelection select;
+ QItemSelection deselect;
+
// Select cells inside the new selection rect
{
const QModelIndex startIndex = qaim->index(newRect.y(), newRect.x());
const QModelIndex endIndex = qaim->index(newRect.y() + newRect.height(), newRect.x() + newRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ select = QItemSelection(startIndex, endIndex);
}
// Unselect cells in the new minus old rects
if (oldRect.x() < newRect.x()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), oldRect.x());
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), newRect.x() - 1);
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ deselect.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
} else if (oldRect.x() + oldRect.width() > newRect.x() + newRect.width()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), newRect.x() + newRect.width() + 1);
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), oldRect.x() + oldRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ deselect.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
}
if (oldRect.y() < newRect.y()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), oldRect.x());
const QModelIndex endIndex = qaim->index(newRect.y() - 1, oldRect.x() + oldRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ deselect.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
} else if (oldRect.y() + oldRect.height() > newRect.y() + newRect.height()) {
const QModelIndex startIndex = qaim->index(newRect.y() + newRect.height() + 1, oldRect.x());
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), oldRect.x() + oldRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ deselect.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ }
+
+ if (selectionFlag == QItemSelectionModel::Select) {
+ // Don't clear the selection that existed before the user started a new selection block
+ deselect.merge(existingSelection, QItemSelectionModel::Deselect);
+ selectionModel->select(deselect, QItemSelectionModel::Deselect);
+ selectionModel->select(select, QItemSelectionModel::Select);
+ } else if (selectionFlag == QItemSelectionModel::Deselect){
+ QItemSelection oldSelection = existingSelection;
+ oldSelection.merge(select, QItemSelectionModel::Deselect);
+ selectionModel->select(oldSelection, QItemSelectionModel::Select);
+ selectionModel->select(select, QItemSelectionModel::Deselect);
+ } else {
+ Q_UNREACHABLE();
}
}
-void QQuickTableViewPrivate::clearSelection()
+void QQuickTableViewPrivate::cancelSelectionTracking()
{
+ // Cancel any ongoing key/mouse aided selection tracking
selectionStartCell = QPoint(-1, -1);
selectionEndCell = QPoint(-1, -1);
+ existingSelection.clear();
+ selectionFlag = QItemSelectionModel::NoUpdate;
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::CancelSelection);
+}
- if (selectionModel)
- selectionModel->clearSelection();
+void QQuickTableViewPrivate::clearSelection()
+{
+ if (!selectionModel)
+ return;
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
+ selectionModel->clearSelection();
}
void QQuickTableViewPrivate::normalizeSelection()
@@ -1728,7 +1938,7 @@ QRectF QQuickTableViewPrivate::selectionRectangle() const
// If the corner cells of the selection are loaded, we can position the
// selection rectangle at its exact location. Otherwise we extend it out
// to the edges of the content item. This is not ideal, but the best we
- // can do while the location of the the corner cells are unknown.
+ // can do while the location of the corner cells are unknown.
// This will at least move the selection handles (and other overlay) out
// of the viewport until the affected cells are eventually loaded.
int left = 0;
@@ -1832,6 +2042,11 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
return dist;
}
+void QQuickTableViewPrivate::setCallback(std::function<void (CallBackFlag)> func)
+{
+ selectableCallbackFunction = func;
+}
+
QQuickTableViewAttached *QQuickTableViewPrivate::getAttachedObject(const QObject *object) const
{
QObject *attachedObject = qmlAttachedPropertiesObject<QQuickTableView>(object);
@@ -2019,7 +2234,10 @@ void QQuickTableViewPrivate::updateContentWidth()
if (loadedItems.isEmpty()) {
QBoolBlocker fixupGuard(inUpdateContentSize, true);
- q->QQuickFlickable::setContentWidth(0);
+ if (model && model->count() > 0 && tableModel && tableModel->delegate())
+ q->QQuickFlickable::setContentWidth(kDefaultColumnWidth);
+ else
+ q->QQuickFlickable::setContentWidth(0);
return;
}
@@ -2052,7 +2270,10 @@ void QQuickTableViewPrivate::updateContentHeight()
if (loadedItems.isEmpty()) {
QBoolBlocker fixupGuard(inUpdateContentSize, true);
- q->QQuickFlickable::setContentHeight(0);
+ if (model && model->count() > 0 && tableModel && tableModel->delegate())
+ q->QQuickFlickable::setContentHeight(kDefaultRowHeight);
+ else
+ q->QQuickFlickable::setContentHeight(0);
return;
}
@@ -2365,21 +2586,22 @@ void QQuickTableViewPrivate::forceLayout(bool immediate)
const QSize actualTableSize = calculateTableSize();
if (tableSize != actualTableSize) {
- // This can happen if the app is calling forceLayout while
- // the model is updated, but before we're notified about it.
- rebuildOptions = RebuildOption::All;
- } else {
- // Resizing a column (or row) can result in the table going from being
- // e.g completely inside the viewport to go outside. And in the latter
- // case, the user needs to be able to scroll the viewport, also if
- // flags such as Flickable.StopAtBounds is in use. So we need to
- // update contentWidth/Height to support that case.
- rebuildOptions = RebuildOption::LayoutOnly
- | RebuildOption::CalculateNewContentWidth
- | RebuildOption::CalculateNewContentHeight
- | checkForVisibilityChanges();
+ // The table size will have changed if forceLayout is called after
+ // the row count in the model has changed, but before we received
+ // a rowsInsertedCallback about it (and vice versa for columns).
+ rebuildOptions |= RebuildOption::ViewportOnly;
}
+ // Resizing a column (or row) can result in the table going from being
+ // e.g completely inside the viewport to go outside. And in the latter
+ // case, the user needs to be able to scroll the viewport, also if
+ // flags such as Flickable.StopAtBounds is in use. So we need to
+ // update contentWidth/Height to support that case.
+ rebuildOptions |= RebuildOption::LayoutOnly
+ | RebuildOption::CalculateNewContentWidth
+ | RebuildOption::CalculateNewContentHeight
+ | checkForVisibilityChanges();
+
scheduleRebuildTable(rebuildOptions);
if (immediate) {
@@ -2661,21 +2883,6 @@ qreal QQuickTableViewPrivate::sizeHintForRow(int row) const
return rowHeight;
}
-void QQuickTableViewPrivate::updateTableSize()
-{
- // tableSize is the same as row and column count, and will always
- // be the same as the number of rows and columns in the model.
- Q_Q(QQuickTableView);
-
- const QSize prevTableSize = tableSize;
- tableSize = calculateTableSize();
-
- if (prevTableSize.width() != tableSize.width())
- emit q->columnsChanged();
- if (prevTableSize.height() != tableSize.height())
- emit q->rowsChanged();
-}
-
QSize QQuickTableViewPrivate::calculateTableSize()
{
QSize size(0, 0);
@@ -3206,18 +3413,8 @@ void QQuickTableViewPrivate::processRebuildTable()
Q_Q(QQuickTableView);
if (rebuildState == RebuildState::Begin) {
- if (Q_UNLIKELY(lcTableViewDelegateLifecycle().isDebugEnabled())) {
- qCDebug(lcTableViewDelegateLifecycle()) << "begin rebuild:" << q;
- if (rebuildOptions & RebuildOption::All)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::All, options:" << rebuildOptions;
- else if (rebuildOptions & RebuildOption::ViewportOnly)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::ViewportOnly, options:" << rebuildOptions;
- else if (rebuildOptions & RebuildOption::LayoutOnly)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::LayoutOnly, options:" << rebuildOptions;
- else
- Q_TABLEVIEW_UNREACHABLE(rebuildOptions);
- }
-
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin rebuild:" << q << "options:" << rebuildOptions;
+ tableSizeBeforeRebuild = tableSize;
edgesBeforeRebuild = loadedItems.isEmpty() ? QMargins()
: QMargins(q->leftColumn(), q->topRow(), q->rightColumn(), q->bottomRow());
}
@@ -3286,6 +3483,10 @@ void QQuickTableViewPrivate::processRebuildTable()
}
if (rebuildState == RebuildState::Done) {
+ if (tableSizeBeforeRebuild.width() != tableSize.width())
+ emit q->columnsChanged();
+ if (tableSizeBeforeRebuild.height() != tableSize.height())
+ emit q->rowsChanged();
if (edgesBeforeRebuild.left() != q->leftColumn())
emit q->leftColumnChanged();
if (edgesBeforeRebuild.right() != q->rightColumn())
@@ -3324,7 +3525,7 @@ bool QQuickTableViewPrivate::moveToNextRebuildState()
else
rebuildState = RebuildState(int(rebuildState) + 1);
- qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
+ qCDebug(lcTableViewDelegateLifecycle()) << rebuildState;
return true;
}
@@ -3435,7 +3636,7 @@ void QQuickTableViewPrivate::calculateTopLeft(QPoint &topLeftCell, QPointF &topL
void QQuickTableViewPrivate::loadInitialTable()
{
- updateTableSize();
+ tableSize = calculateTableSize();
if (positionXAnimation.isRunning()) {
positionXAnimation.stop();
@@ -3941,10 +4142,11 @@ bool QQuickTableViewPrivate::currentInSelectionModel(const QPoint &cell) const
void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected)
{
- if (!selectionModel->hasSelection()) {
- // Ensure that we cancel any ongoing key/mouse-based selections
- // if selectionModel.clearSelection() is called.
- clearSelection();
+ if (!inSelectionModelUpdate) {
+ // The selection model was manipulated outside of TableView
+ // and SelectionRectangle. In that case we cancel any ongoing
+ // selection tracking.
+ cancelSelectionTracking();
}
const auto &selectedIndexes = selected.indexes();
@@ -3957,6 +4159,13 @@ void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelecti
void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select)
{
+ if (modelIndex.isValid() && modelIndex.model() != selectionSourceModel()) {
+ qmlWarning(q_func())
+ << "Cannot select cells: TableView.selectionModel.model is not "
+ << "compatible with the model displayed in the view";
+ return;
+ }
+
const int cellIndex = modelIndexToCellIndex(modelIndex);
if (!loadedItems.contains(cellIndex))
return;
@@ -3965,6 +4174,24 @@ void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelI
setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(select), cellIndex, item, false);
}
+QAbstractItemModel *QQuickTableViewPrivate::selectionSourceModel()
+{
+ // TableView.selectionModel.model should always be the same as TableView.model.
+ // After all, when the user selects an index in the view, the same index should
+ // be selected in the selection model. We therefore set the model in
+ // selectionModel.model automatically.
+ // But it's not always the case that the model shown in the view is the same
+ // as TableView.model. Subclasses with a proxy model will instead show the
+ // proxy model (e.g TreeView and HeaderView). And then it's no longer clear if
+ // we should use the proxy model or the TableView.model as source model in
+ // TableView.selectionModel. It's up to the subclass. But in short, if the proxy
+ // model shares the same model items as TableView.model (just with e.g a filter
+ // applied, or sorted etc), then TableView.model should be used. If the proxy
+ // model is a completely different model that shares no model items with
+ // TableView.model, then the proxy model should be used (e.g HeaderView).
+ return qaim(modelImpl());
+}
+
QAbstractItemModel *QQuickTableViewPrivate::qaim(QVariant modelAsVariant) const
{
// If modelAsVariant wraps a qaim, return it
@@ -3992,11 +4219,12 @@ void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems()
void QQuickTableViewPrivate::currentChangedInSelectionModel(const QModelIndex &current, const QModelIndex &previous)
{
- // Warn if the source models are not the same
- const QAbstractItemModel *qaimInSelection = selectionModel ? selectionModel->model() : nullptr;
- const QAbstractItemModel *qaimInTableView = qaim(modelImpl());
- if (qaimInSelection && qaimInSelection != qaimInTableView)
- qmlWarning(q_func()) << "TableView.selectionModel.model differs from TableView.model";
+ if (current.isValid() && current.model() != selectionSourceModel()) {
+ qmlWarning(q_func())
+ << "Cannot change current index: TableView.selectionModel.model is not "
+ << "compatible with the model displayed in the view";
+ return;
+ }
updateCurrentRowAndColumn();
setCurrentOnDelegateItem(previous, false);
@@ -4159,9 +4387,6 @@ QVariant QQuickTableViewPrivate::modelImpl() const
void QQuickTableViewPrivate::setModelImpl(const QVariant &newModel)
{
- if (newModel == assignedModel)
- return;
-
assignedModel = newModel;
scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
emit q_func()->modelChanged();
@@ -4169,7 +4394,7 @@ void QQuickTableViewPrivate::setModelImpl(const QVariant &newModel)
void QQuickTableViewPrivate::syncModel()
{
- if (modelVariant == assignedModel)
+ if (compareModel(modelVariant, assignedModel))
return;
if (model) {
@@ -4445,6 +4670,13 @@ void QQuickTableViewPrivate::modelResetCallback()
scheduleRebuildTable(RebuildOption::All);
}
+bool QQuickTableViewPrivate::compareModel(const QVariant& model1, const QVariant& model2) const
+{
+ return (model1 == model2 ||
+ (model1.userType() == qMetaTypeId<QJSValue>() && model2.userType() == qMetaTypeId<QJSValue>() &&
+ model1.value<QJSValue>().strictlyEquals(model2.value<QJSValue>())));
+}
+
void QQuickTableViewPrivate::positionViewAtRow(int row, Qt::Alignment alignment, qreal offset, const QRectF subRect)
{
Qt::Alignment verticalAlignment = alignment & (Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom);
@@ -4660,7 +4892,7 @@ void QQuickTableViewPrivate::init()
positionYAnimation.setProperty(QStringLiteral("contentY"));
positionYAnimation.setEasing(QEasingCurve::OutQuart);
- auto tapHandler = new QQuickTapHandler(q->contentItem());
+ auto tapHandler = new QQuickTableViewTapHandler(q);
hoverHandler = new QQuickTableViewHoverHandler(q);
resizeHandler = new QQuickTableViewResizeHandler(q);
@@ -4724,24 +4956,33 @@ void QQuickTableViewPrivate::handleTap(const QQuickHandlerPoint &point)
if (resizeHandler->state() != QQuickTableViewResizeHandler::Listening)
return;
- QModelIndex prevIndex;
- if (selectionModel) {
- prevIndex = selectionModel->currentIndex();
- if (pointerNavigationEnabled) {
- clearSelection();
- setCurrentIndexFromTap(point.position());
- }
- }
+ const QModelIndex tappedIndex = q->modelIndex(q->cellAtPosition(point.position()));
+ bool tappedCellIsSelected = false;
- if (editTriggers != QQuickTableView::NoEditTriggers)
- q->closeEditor();
+ if (selectionModel)
+ tappedCellIsSelected = selectionModel->isSelected(tappedIndex);
- const QModelIndex tappedIndex = q->modelIndex(q->cellAtPosition(point.position()));
if (canEdit(tappedIndex, false)) {
- if (editTriggers & QQuickTableView::SingleTapped)
+ if (editTriggers & QQuickTableView::SingleTapped) {
+ if (selectionBehavior != QQuickTableView::SelectionDisabled)
+ clearSelection();
q->edit(tappedIndex);
- else if ((editTriggers & QQuickTableView::SelectedTapped) && tappedIndex == prevIndex)
+ return;
+ } else if (editTriggers & QQuickTableView::SelectedTapped && tappedCellIsSelected) {
q->edit(tappedIndex);
+ return;
+ }
+ }
+
+ // Since the tap didn't result in selecting or editing cells, we clear
+ // the current selection and move the current index instead.
+ if (pointerNavigationEnabled) {
+ q->closeEditor();
+ if (selectionBehavior != QQuickTableView::SelectionDisabled) {
+ clearSelection();
+ cancelSelectionTracking();
+ }
+ setCurrentIndexFromTap(point.position());
}
}
@@ -4840,7 +5081,6 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
const QModelIndex currentIndex = selectionModel->currentIndex();
const QPoint currentCell = q->cellAtIndex(currentIndex);
- const bool select = (e->modifiers() & Qt::ShiftModifier) && (e->key() != Qt::Key_Backtab);
if (!q->activeFocusOnTab()) {
switch (e->key()) {
@@ -4864,7 +5104,7 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
case Qt::Key_Backtab:
// Special case: the current index doesn't map to a cell in the view (perhaps
// because it isn't set yet). In that case, we set it to be the top-left cell.
- const QModelIndex topLeftIndex = q->modelIndex(topRow(), leftColumn());
+ const QModelIndex topLeftIndex = q->index(topRow(), leftColumn());
selectionModel->setCurrentIndex(topLeftIndex, QItemSelectionModel::NoUpdate);
return true;
}
@@ -4872,25 +5112,38 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
}
auto beginMoveCurrentIndex = [&](){
- if (!select) {
+ const bool shouldSelect = (e->modifiers() & Qt::ShiftModifier) && (e->key() != Qt::Key_Backtab);
+ const bool startNewSelection = selectionRectangle().isEmpty();
+ if (!shouldSelect) {
clearSelection();
- } else if (selectionRectangle().isEmpty()) {
+ cancelSelectionTracking();
+ } else if (startNewSelection) {
+ // Try to start a new selection if no selection exists from before.
+ // The startSelection() call is theoretically allowed to refuse, although this
+ // is less likely when starting a selection using the keyboard.
const int serializedStartIndex = modelIndexToCellIndex(selectionModel->currentIndex());
if (loadedItems.contains(serializedStartIndex)) {
const QRectF startGeometry = loadedItems.value(serializedStartIndex)->geometry();
- setSelectionStartPos(startGeometry.center());
+ if (startSelection(startGeometry.center(), Qt::ShiftModifier)) {
+ setSelectionStartPos(startGeometry.center());
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::SelectionRectangleChanged);
+ }
}
}
};
auto endMoveCurrentIndex = [&](const QPoint &cell){
- if (select) {
+ const bool isSelecting = selectionFlag != QItemSelectionModel::NoUpdate;
+ if (isSelecting) {
if (polishScheduled)
forceLayout(true);
const int serializedEndIndex = modelIndexAtCell(cell);
if (loadedItems.contains(serializedEndIndex)) {
const QRectF endGeometry = loadedItems.value(serializedEndIndex)->geometry();
setSelectionEndPos(endGeometry.center());
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::SelectionRectangleChanged);
}
}
selectionModel->setCurrentIndex(q->modelIndex(cell), QItemSelectionModel::NoUpdate);
@@ -5170,6 +5423,14 @@ QQuickTableView::QQuickTableView(QQuickTableViewPrivate &dd, QQuickItem *parent)
QQuickTableView::~QQuickTableView()
{
+ Q_D(QQuickTableView);
+
+ if (d->syncView) {
+ // Remove this TableView as a sync child from the syncView
+ auto syncView_d = d->syncView->d_func();
+ syncView_d->syncChildren.removeOne(this);
+ syncView_d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+ }
}
void QQuickTableView::componentFinalized()
@@ -5301,12 +5562,13 @@ QVariant QQuickTableView::model() const
void QQuickTableView::setModel(const QVariant &newModel)
{
Q_D(QQuickTableView);
+ if (d->compareModel(newModel, d->assignedModel))
+ return;
closeEditor();
d->setModelImpl(newModel);
-
if (d->selectionModel)
- d->selectionModel->setModel(d->qaim(newModel));
+ d->selectionModel->setModel(d->selectionSourceModel());
}
QQmlComponent *QQuickTableView::delegate() const
@@ -5473,7 +5735,7 @@ void QQuickTableView::setSelectionModel(QItemSelectionModel *selectionModel)
d->selectionModel = selectionModel;
if (d->selectionModel) {
- d->selectionModel->setModel(d->qaim(d->modelImpl()));
+ d->selectionModel->setModel(d->selectionSourceModel());
QQuickTableViewPrivate::connect(d->selectionModel, &QItemSelectionModel::selectionChanged,
d, &QQuickTableViewPrivate::selectionChangedInSelectionModel);
QQuickTableViewPrivate::connect(d->selectionModel, &QItemSelectionModel::currentChanged,
@@ -5574,7 +5836,7 @@ int QQuickTableView::currentColumn() const
void QQuickTableView::positionViewAtRow(int row, PositionMode mode, qreal offset, const QRectF &subRect)
{
Q_D(QQuickTableView);
- if (row < 0 || row >= rows())
+ if (row < 0 || row >= rows() || d->loadedRows.isEmpty())
return;
// Note: PositionMode::Contain is from here on translated to (Qt::AlignTop | Qt::AlignBottom).
@@ -5640,7 +5902,7 @@ void QQuickTableView::positionViewAtRow(int row, PositionMode mode, qreal offset
void QQuickTableView::positionViewAtColumn(int column, PositionMode mode, qreal offset, const QRectF &subRect)
{
Q_D(QQuickTableView);
- if (column < 0 || column >= columns())
+ if (column < 0 || column >= columns() || d->loadedColumns.isEmpty())
return;
// Note: PositionMode::Contain is from here on translated to (Qt::AlignLeft | Qt::AlignRight).
@@ -5705,9 +5967,35 @@ void QQuickTableView::positionViewAtColumn(int column, PositionMode mode, qreal
void QQuickTableView::positionViewAtCell(const QPoint &cell, PositionMode mode, const QPointF &offset, const QRectF &subRect)
{
- positionViewAtCell(cell.x(), cell.y(), mode, offset, subRect);
+ PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
+ PositionMode verticalMode = mode & ~(AlignLeft | AlignRight | AlignHCenter);
+ if (!horizontalMode && !verticalMode) {
+ qmlWarning(this) << "Unsupported mode:" << int(mode);
+ return;
+ }
+
+ if (horizontalMode)
+ positionViewAtColumn(cell.x(), horizontalMode, offset.x(), subRect);
+ if (verticalMode)
+ positionViewAtRow(cell.y(), verticalMode, offset.y(), subRect);
+}
+
+void QQuickTableView::positionViewAtIndex(const QModelIndex &index, PositionMode mode, const QPointF &offset, const QRectF &subRect)
+{
+ PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
+ PositionMode verticalMode = mode & ~(AlignLeft | AlignRight | AlignHCenter);
+ if (!horizontalMode && !verticalMode) {
+ qmlWarning(this) << "Unsupported mode:" << int(mode);
+ return;
+ }
+
+ if (horizontalMode)
+ positionViewAtColumn(columnAtIndex(index), horizontalMode, offset.x(), subRect);
+ if (verticalMode)
+ positionViewAtRow(rowAtIndex(index), verticalMode, offset.y(), subRect);
}
+#if QT_DEPRECATED_SINCE(6, 5)
void QQuickTableView::positionViewAtCell(int column, int row, PositionMode mode, const QPointF &offset, const QRectF &subRect)
{
PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
@@ -5722,6 +6010,7 @@ void QQuickTableView::positionViewAtCell(int column, int row, PositionMode mode,
if (verticalMode)
positionViewAtRow(row, verticalMode, offset.y(), subRect);
}
+#endif
QQuickItem *QQuickTableView::itemAtCell(const QPoint &cell) const
{
@@ -5732,10 +6021,21 @@ QQuickItem *QQuickTableView::itemAtCell(const QPoint &cell) const
return d->loadedItems.value(modelIndex)->item;
}
+#if QT_DEPRECATED_SINCE(6, 5)
QQuickItem *QQuickTableView::itemAtCell(int column, int row) const
{
return itemAtCell(QPoint(column, row));
}
+#endif
+
+QQuickItem *QQuickTableView::itemAtIndex(const QModelIndex &index) const
+{
+ Q_D(const QQuickTableView);
+ const int serializedIndex = d->modelIndexToCellIndex(index);
+ if (!d->loadedItems.contains(serializedIndex))
+ return nullptr;
+ return d->loadedItems.value(serializedIndex)->item;
+}
#if QT_DEPRECATED_SINCE(6, 4)
QPoint QQuickTableView::cellAtPos(qreal x, qreal y, bool includeSpacing) const
@@ -6009,6 +6309,7 @@ QPoint QQuickTableView::cellAtIndex(const QModelIndex &index) const
return {index.column(), index.row()};
}
+#if QT_DEPRECATED_SINCE(6, 4)
QModelIndex QQuickTableView::modelIndex(int row, int column) const
{
static bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
@@ -6019,9 +6320,18 @@ QModelIndex QQuickTableView::modelIndex(int row, int column) const
// to continue accepting calls to modelIndex(column, row).
return modelIndex({row, column});
} else {
+ qmlWarning(this) << "modelIndex(row, column) is deprecated. "
+ "Use index(row, column) instead. For more information, see "
+ "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
return modelIndex({column, row});
}
}
+#endif
+
+QModelIndex QQuickTableView::index(int row, int column) const
+{
+ return modelIndex({column, row});
+}
int QQuickTableView::rowAtIndex(const QModelIndex &index) const
{
@@ -6307,6 +6617,21 @@ void QQuickTableView::setSelectionBehavior(SelectionBehavior selectionBehavior)
emit selectionBehaviorChanged();
}
+QQuickTableView::SelectionMode QQuickTableView::selectionMode() const
+{
+ return d_func()->selectionMode;
+}
+
+void QQuickTableView::setSelectionMode(SelectionMode selectionMode)
+{
+ Q_D(QQuickTableView);
+ if (d->selectionMode == selectionMode)
+ return;
+
+ d->selectionMode = selectionMode;
+ emit selectionModeChanged();
+}
+
bool QQuickTableView::resizableColumns() const
{
return d_func()->resizableColumns;
@@ -6402,6 +6727,7 @@ QQuickTableViewResizeHandler::QQuickTableViewResizeHandler(QQuickTableView *view
// Set a grab permission that stops the flickable, as well as
// any drag handler inside the delegate, from stealing the drag.
setGrabPermissions(QQuickPointerHandler::CanTakeOverFromAnything);
+ setObjectName("tableViewResizeHandler");
}
void QQuickTableViewResizeHandler::onGrabChanged(QQuickPointerHandler *grabber
@@ -6430,8 +6756,13 @@ void QQuickTableViewResizeHandler::onGrabChanged(QQuickPointerHandler *grabber
bool QQuickTableViewResizeHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
{
- Q_UNUSED(event);
- Q_UNUSED(point);
+ if (!QQuickSinglePointHandler::wantsEventPoint(event, point))
+ return false;
+
+ // If we have a mouse wheel event then we do not want to do anything related to resizing.
+ if (event->type() == QEvent::Type::Wheel)
+ return false;
+
// When the user is flicking, we disable resizing, so that
// he doesn't start to resize by accident.
auto tableView = static_cast<QQuickTableView *>(parentItem()->parent());
@@ -6514,7 +6845,7 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
#if QT_CONFIG(cursor)
tableViewPrivate->updateCursor();
#endif
- // fallthrough
+ Q_FALLTHROUGH();
case Dragging: {
const qreal distX = point.position().x() - m_columnStartX;
const qreal distY = point.position().y() - m_rowStartY;
@@ -6532,6 +6863,21 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
}
}
+// ----------------------------------------------
+
+QQuickTableViewTapHandler::QQuickTableViewTapHandler(QQuickTableView *view)
+ : QQuickTapHandler(view->contentItem())
+{
+ setObjectName("tableViewTapHandler");
+}
+
+bool QQuickTableViewTapHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
+{
+ auto tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ auto tableViewPrivate = QQuickTableViewPrivate::get(tableView);
+ return tableViewPrivate->pointerNavigationEnabled && QQuickTapHandler::wantsEventPoint(event, point);
+}
+
QT_END_NAMESPACE
#include "moc_qquicktableview_p.cpp"
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
index 162d576320..cdde6de3d6 100644
--- a/src/quick/items/qquicktableview_p.h
+++ b/src/quick/items/qquicktableview_p.h
@@ -31,7 +31,7 @@ class QQuickTableViewAttached;
class QQuickTableViewPrivate;
class QItemSelectionModel;
-class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable, public QQmlFinalizerHook
+class Q_QUICK_EXPORT QQuickTableView : public QQuickFlickable, public QQmlFinalizerHook
{
Q_OBJECT
Q_INTERFACES(QQmlFinalizerHook)
@@ -64,6 +64,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable, public QQ
Q_PROPERTY(bool resizableColumns READ resizableColumns WRITE setResizableColumns NOTIFY resizableColumnsChanged REVISION(6, 5) FINAL)
Q_PROPERTY(bool resizableRows READ resizableRows WRITE setResizableRows NOTIFY resizableRowsChanged REVISION(6, 5) FINAL)
Q_PROPERTY(EditTriggers editTriggers READ editTriggers WRITE setEditTriggers NOTIFY editTriggersChanged REVISION(6, 5) FINAL)
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged REVISION(6, 6) FINAL)
QML_NAMED_ELEMENT(TableView)
QML_ADDED_IN_VERSION(2, 12)
@@ -92,6 +93,13 @@ public:
};
Q_ENUM(SelectionBehavior)
+ enum SelectionMode {
+ SingleSelection,
+ ContiguousSelection,
+ ExtendedSelection
+ };
+ Q_ENUM(SelectionMode)
+
enum EditTrigger {
NoEditTriggers = 0x0,
SingleTapped = 0x1,
@@ -162,6 +170,8 @@ public:
SelectionBehavior selectionBehavior() const;
void setSelectionBehavior(SelectionBehavior selectionBehavior);
+ SelectionMode selectionMode() const;
+ void setSelectionMode(SelectionMode selectionMode);
bool resizableColumns() const;
void setResizableColumns(bool enabled);
@@ -174,15 +184,16 @@ public:
Q_INVOKABLE void forceLayout();
Q_INVOKABLE void positionViewAtCell(const QPoint &cell, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF());
- Q_INVOKABLE void positionViewAtCell(int column, int row, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF());
+ Q_INVOKABLE void positionViewAtIndex(const QModelIndex &index, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF());
Q_INVOKABLE void positionViewAtRow(int row, PositionMode mode, qreal offset = 0, const QRectF &subRect = QRectF());
Q_INVOKABLE void positionViewAtColumn(int column, PositionMode mode, qreal offset = 0, const QRectF &subRect = QRectF());
Q_INVOKABLE QQuickItem *itemAtCell(const QPoint &cell) const;
- Q_INVOKABLE QQuickItem *itemAtCell(int column, int row) const;
Q_REVISION(6, 4) Q_INVOKABLE QPoint cellAtPosition(const QPointF &position, bool includeSpacing = false) const;
Q_REVISION(6, 4) Q_INVOKABLE QPoint cellAtPosition(qreal x, qreal y, bool includeSpacing = false) const;
#if QT_DEPRECATED_SINCE(6, 4)
+ QT_DEPRECATED_VERSION_X_6_4("Use index(row, column) instead")
+ Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(int row, int column) const;
QT_DEPRECATED_VERSION_X_6_4("Use cellAtPosition() instead")
Q_INVOKABLE QPoint cellAtPos(const QPointF &position, bool includeSpacing = false) const;
Q_INVOKABLE QPoint cellAtPos(qreal x, qreal y, bool includeSpacing = false) const;
@@ -196,8 +207,8 @@ public:
Q_REVISION(6, 2) Q_INVOKABLE qreal implicitColumnWidth(int column) const;
Q_REVISION(6, 2) Q_INVOKABLE qreal implicitRowHeight(int row) const;
+ Q_REVISION(6, 4) Q_INVOKABLE QModelIndex index(int row, int column) const;
Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(const QPoint &cell) const;
- Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(int row, int column) const;
Q_REVISION(6, 4) Q_INVOKABLE virtual QPoint cellAtIndex(const QModelIndex &index) const;
Q_REVISION(6, 4) Q_INVOKABLE int rowAtIndex(const QModelIndex &index) const;
Q_REVISION(6, 4) Q_INVOKABLE int columnAtIndex(const QModelIndex &index) const;
@@ -213,6 +224,15 @@ public:
Q_REVISION(6, 5) Q_INVOKABLE void edit(const QModelIndex &index);
Q_REVISION(6, 5) Q_INVOKABLE void closeEditor();
+ Q_REVISION(6, 5) Q_INVOKABLE QQuickItem *itemAtIndex(const QModelIndex &index) const;
+
+#if QT_DEPRECATED_SINCE(6, 5)
+ QT_DEPRECATED_VERSION_X_6_5("Use itemAtIndex(index(row, column)) instead")
+ Q_INVOKABLE QQuickItem *itemAtCell(int column, int row) const;
+ QT_DEPRECATED_VERSION_X_6_5("Use positionViewAtIndex(index(row, column)) instead")
+ Q_INVOKABLE void positionViewAtCell(int column, int row, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF());
+#endif
+
static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
Q_SIGNALS:
@@ -243,6 +263,7 @@ Q_SIGNALS:
Q_REVISION(6, 5) void resizableRowsChanged();
Q_REVISION(6, 5) void editTriggersChanged();
Q_REVISION(6, 5) void layoutChanged();
+ Q_REVISION(6, 6) void selectionModeChanged();
protected:
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
@@ -265,11 +286,11 @@ private:
qreal maxYExtent() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickTableViewAttached : public QObject
+class Q_QUICK_EXPORT QQuickTableViewAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickTableView *view READ view NOTIFY viewChanged)
- Q_PROPERTY(QQmlComponent *editDelegate READ editDelegate WRITE setEditDelegate NOTIFY editDelegateChanged)
+ Q_PROPERTY(QQuickTableView *view READ view NOTIFY viewChanged FINAL)
+ Q_PROPERTY(QQmlComponent *editDelegate READ editDelegate WRITE setEditDelegate NOTIFY editDelegateChanged FINAL)
public:
QQuickTableViewAttached(QObject *parent)
@@ -311,6 +332,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickTableView::EditTriggers)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTableView)
-
#endif // QQUICKTABLEVIEW_P_H
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index d8c2b036ac..867e03485a 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -31,6 +31,7 @@
#include <QtQuick/private/qquickselectable_p.h>
#include <QtQuick/private/qquicksinglepointhandler_p.h>
#include <QtQuick/private/qquickhoverhandler_p.h>
+#include <QtQuick/private/qquicktaphandler_p.h>
QT_BEGIN_NAMESPACE
@@ -108,7 +109,22 @@ protected:
QPointerEvent *ev, QEventPoint &point) override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate, public QQuickSelectable
+/*! \internal
+ * QQuickTableViewTapHandler used to handle tap events explicitly for table view
+ */
+class QQuickTableViewTapHandler : public QQuickTapHandler
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickTableViewTapHandler(QQuickTableView *view);
+ bool wantsEventPoint(const QPointerEvent *event, const QEventPoint &point) override;
+
+ friend class QQuickTableViewPrivate;
+};
+
+
+class Q_QUICK_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate, public QQuickSelectable
{
public:
Q_DECLARE_PUBLIC(QQuickTableView)
@@ -343,6 +359,10 @@ public:
QPointer<QItemSelectionModel> selectionModel;
QQuickTableView::SelectionBehavior selectionBehavior = QQuickTableView::SelectCells;
+ QQuickTableView::SelectionMode selectionMode = QQuickTableView::ExtendedSelection;
+ QItemSelectionModel::SelectionFlag selectionFlag = QItemSelectionModel::NoUpdate;
+ std::function<void(CallBackFlag)> selectableCallbackFunction;
+ bool inSelectionModelUpdate = false;
int assignedPositionViewAtRowAfterRebuild = 0;
int assignedPositionViewAtColumnAfterRebuild = 0;
@@ -360,8 +380,10 @@ public:
QPoint selectionStartCell = {-1, -1};
QPoint selectionEndCell = {-1, -1};
+ QItemSelection existingSelection;
QMargins edgesBeforeRebuild;
+ QSize tableSizeBeforeRebuild;
int currentRow = -1;
int currentColumn = -1;
@@ -502,6 +524,7 @@ public:
virtual void syncModel();
virtual void syncSyncView();
virtual void syncPositionView();
+ virtual QAbstractItemModel *selectionSourceModel();
inline void syncRebuildOptions();
void connectToModel();
@@ -515,6 +538,7 @@ public:
void columnsRemovedCallback(const QModelIndex &parent, int begin, int end);
void layoutChangedCallback(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
void modelResetCallback();
+ bool compareModel(const QVariant& model1, const QVariant& model2) const;
void positionViewAtRow(int row, Qt::Alignment alignment, qreal offset, const QRectF subRect = QRectF());
void positionViewAtColumn(int column, Qt::Alignment alignment, qreal offset, const QRectF subRect = QRectF());
@@ -531,7 +555,6 @@ public:
void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected);
void updateSelectedOnAllDelegateItems();
void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select);
- void syncSourceModelInSelectionModel();
bool currentInSelectionModel(const QPoint &cell) const;
void currentChangedInSelectionModel(const QModelIndex &current, const QModelIndex &previous);
@@ -560,13 +583,15 @@ public:
// QQuickSelectable
QQuickItem *selectionPointerHandlerTarget() const override;
- bool startSelection(const QPointF &pos) override;
+ bool startSelection(const QPointF &pos, Qt::KeyboardModifiers modifiers) override;
void setSelectionStartPos(const QPointF &pos) override;
void setSelectionEndPos(const QPointF &pos) override;
void clearSelection() override;
void normalizeSelection() override;
QRectF selectionRectangle() const override;
QSizeF scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step) override;
+ void setCallback(std::function<void(CallBackFlag)> func) override;
+ void cancelSelectionTracking();
QPoint clampedCellAtPos(const QPointF &pos) const;
virtual void updateSelection(const QRect &oldSelection, const QRect &newSelection);
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 517ccba845..af3fe3d1d4 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -10,10 +10,8 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qsgadaptationlayer_p.h>
-#include "qquicktextnode_p.h"
-#include "qquickimage_p_p.h"
+#include "qsginternaltextnode_p.h"
#include "qquicktextutil_p.h"
-#include "qquicktextdocument_p.h"
#include <QtQuick/private/qsgtexture_p.h>
@@ -29,7 +27,7 @@
#include <private/qtextengine_p.h>
#include <private/qquickstyledtext_p.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <qmath.h>
#include <limits.h>
@@ -37,6 +35,9 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
+Q_LOGGING_CATEGORY(lcText, "qt.quick.text")
+
+using namespace Qt::StringLiterals;
const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
@@ -47,7 +48,7 @@ const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
const int QQuickTextPrivate::largeTextSizeThreshold = QQUICKTEXT_LARGETEXT_THRESHOLD;
QQuickTextPrivate::QQuickTextPrivate()
- : fontInfo(font), elideLayout(nullptr), textLine(nullptr), lineWidth(0)
+ : fontInfo(font), lineWidth(0)
, color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000)
, lineCount(1), multilengthEos(-1)
, elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop)
@@ -80,7 +81,6 @@ QQuickTextPrivate::ExtraData::ExtraData()
, doc(nullptr)
, minimumPixelSize(12)
, minimumPointSize(12)
- , nbActiveDownloads(0)
, maximumLineCount(INT_MAX)
, renderTypeQuality(QQuickText::DefaultRenderTypeQuality)
, lineHeightValid(false)
@@ -99,9 +99,6 @@ void QQuickTextPrivate::init()
QQuickTextPrivate::~QQuickTextPrivate()
{
- delete elideLayout;
- delete textLine; textLine = nullptr;
-
if (extra.isAllocated()) {
qDeleteAll(extra->imgTags);
extra->imgTags.clear();
@@ -204,7 +201,7 @@ void QQuickTextPrivate::setBottomPadding(qreal value, bool reset)
Used to decide if the Text should use antialiasing or not. Only Text
with renderType of Text.NativeRendering can disable antialiasing.
- The default is true.
+ The default is \c true.
*/
void QQuickText::q_updateLayout()
@@ -273,32 +270,115 @@ void QQuickTextPrivate::updateLayout()
q->polish();
}
+/*! \internal
+ QTextDocument::loadResource() calls this to load inline images etc.
+ But if it's a local file, don't do it: let QTextDocument::loadResource()
+ load it in the default way. QQuickPixmap is for QtQuick-specific uses.
+*/
+QVariant QQuickText::loadResource(int type, const QUrl &source)
+{
+ Q_D(QQuickText);
+ const QUrl url = d->extra->doc->baseUrl().resolved(source);
+ if (url.isLocalFile()) {
+ // qmlWarning if the file doesn't exist (because QTextDocument::loadResource() can't do that)
+ const QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
+ if (!fi.exists())
+ qmlWarning(this) << "Cannot open: " << url.toString();
+ // let QTextDocument::loadResource() handle local file loading
+ return {};
+ }
+ // see if we already started a load job
+ for (auto it = d->extra->pixmapsInProgress.cbegin(); it != d->extra->pixmapsInProgress.cend();) {
+ auto *job = *it;
+ if (job->url() == url) {
+ if (job->isError()) {
+ qmlWarning(this) << job->error();
+ delete *it;
+ it = d->extra->pixmapsInProgress.erase(it);
+ return QImage();
+ }
+ qCDebug(lcText) << "already downloading" << url;
+ // existing job: return a null variant if it's not done yet
+ return job->isReady() ? job->image() : QVariant();
+ }
+ ++it;
+ }
+ qCDebug(lcText) << "loading" << source << "resolved" << url
+ << "type" << static_cast<QTextDocument::ResourceType>(type);
+ QQmlContext *context = qmlContext(this);
+ Q_ASSERT(context);
+ // don't cache it in QQuickPixmapCache, because it's cached in QTextDocumentPrivate::cachedResources
+ QQuickPixmap *p = new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
+ p->connectFinished(this, SLOT(resourceRequestFinished()));
+ d->extra->pixmapsInProgress.append(p);
+ // the new job is probably not done; return a null variant if the caller should poll again
+ return p->isReady() ? p->image() : QVariant();
+}
+
+/*! \internal
+ Handle completion of a download that QQuickText::loadResource() started.
+*/
+void QQuickText::resourceRequestFinished()
+{
+ Q_D(QQuickText);
+ bool allDone = true;
+ for (auto it = d->extra->pixmapsInProgress.cbegin(); it != d->extra->pixmapsInProgress.cend();) {
+ auto *job = *it;
+ if (job->isError()) {
+ // get QTextDocument::loadResource() to call QQuickText::loadResource() again, to return the placeholder
+ qCDebug(lcText) << "failed to load" << job->url();
+ d->extra->doc->resource(QTextDocument::ImageResource, job->url());
+ } else if (job->isReady()) {
+ // get QTextDocument::loadResource() to call QQuickText::loadResource() again, and cache the result
+ auto res = d->extra->doc->resource(QTextDocument::ImageResource, job->url());
+ // If QTextDocument::resource() returned a valid variant, it's been cached too. Either way, the job is done.
+ qCDebug(lcText) << (res.isValid() ? "done downloading" : "failed to load") << job->url();
+ delete *it;
+ it = d->extra->pixmapsInProgress.erase(it);
+ } else {
+ allDone = false;
+ ++it;
+ }
+ }
+ if (allDone) {
+ Q_ASSERT(d->extra->pixmapsInProgress.isEmpty());
+ d->updateLayout();
+ }
+}
+
+/*! \internal
+ Handle completion of StyledText image downloads (there's no QTextDocument instance in that case).
+*/
void QQuickText::imageDownloadFinished()
{
Q_D(QQuickText);
+ if (!d->extra.isAllocated())
+ return;
- (d->extra->nbActiveDownloads)--;
+ if (std::any_of(d->extra->imgTags.cbegin(), d->extra->imgTags.cend(),
+ [] (auto *image) { return image->pix && image->pix->isLoading(); })) {
+ // return if we still have any active download
+ return;
+ }
// when all the remote images have been downloaded,
// if one of the sizes was not specified at parsing time
// we use the implicit size from pixmapcache and re-layout.
- if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
- bool needToUpdateLayout = false;
- for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
- if (!img->size.isValid()) {
- img->size = img->pix->implicitSize();
- needToUpdateLayout = true;
- }
+ bool needToUpdateLayout = false;
+ for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
+ if (!img->size.isValid()) {
+ img->size = img->pix->implicitSize();
+ needToUpdateLayout = true;
}
+ }
- if (needToUpdateLayout) {
- d->textHasChanged = true;
- d->updateLayout();
- } else {
- d->updateType = QQuickTextPrivate::UpdatePaintNode;
- update();
- }
+ if (needToUpdateLayout) {
+ d->textHasChanged = true;
+ d->updateLayout();
+ } else {
+ d->updateType = QQuickTextPrivate::UpdatePaintNode;
+ update();
}
}
@@ -430,8 +510,13 @@ void QQuickTextPrivate::updateSize()
layedOutTextRect = QRectF(QPointF(0,0), dsize);
size = QSizeF(extra->doc->idealWidth(),dsize.height());
- QFontMetricsF fm(font);
- updateBaseline(fm.ascent(), q->height() - size.height() - vPadding);
+
+ qreal baseline = QFontMetricsF(font).ascent();
+ QTextBlock firstBlock = extra->doc->firstBlock();
+ if (firstBlock.isValid() && firstBlock.layout() != nullptr && firstBlock.lineCount() > 0)
+ baseline = firstBlock.layout()->lineAt(0).ascent();
+
+ updateBaseline(baseline, q->height() - size.height() - vPadding);
//### need to confirm cost of always setting these for richText
internalWidthUpdate = true;
@@ -468,7 +553,7 @@ void QQuickTextPrivate::updateSize()
QTextLine firstLine = firstBlock.layout()->lineAt(0);
QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
advance = QSizeF(lastLine.horizontalAdvance(),
- (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ (lastLine.y() + lastBlock.layout()->position().y() + lastLine.ascent()) - (firstLine.y() + firstBlock.layout()->position().y() + firstLine.ascent()));
} else {
advance = QSizeF();
}
@@ -591,7 +676,7 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height,
Q_Q(QQuickText);
if (!textLine)
- textLine = new QQuickTextLine;
+ textLine.reset(new QQuickTextLine);
textLine->setFullLayoutTextLength(fullLayoutTextLength);
textLine->setLine(&line);
textLine->setY(height);
@@ -607,7 +692,7 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height,
if (lineHeight() != 1.0)
textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
- emit q->lineLaidOut(textLine);
+ emit q->lineLaidOut(textLine.get());
height += textLine->height();
}
@@ -1103,7 +1188,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (elide) {
if (!elideLayout) {
- elideLayout = new QTextLayout;
+ elideLayout.reset(new QTextLayout);
elideLayout->setCacheEnabled(true);
}
QTextEngine *engine = layout.engine();
@@ -1153,8 +1238,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (visibleCount == 1)
layout.clearLayout();
} else {
- delete elideLayout;
- elideLayout = nullptr;
+ elideLayout.reset();
}
QTextLine firstLine = visibleCount == 1 && elideLayout
@@ -1203,12 +1287,10 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
if (!image->pix) {
const QQmlContext *context = qmlContext(q);
const QUrl url = context->resolvedUrl(q->baseUrl()).resolved(image->url);
- image->pix = new QQuickPixmap(context->engine(), url, QRect(), image->size);
+ image->pix.reset(new QQuickPixmap(context->engine(), url, QRect(), image->size * devicePixelRatio()));
+
if (image->pix->isLoading()) {
image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
- if (!extra.isAllocated() || !extra->nbActiveDownloads)
- extra.value().nbActiveDownloads = 0;
- extra->nbActiveDownloads++;
} else if (image->pix->isReady()) {
if (!image->size.isValid()) {
image->size = image->pix->implicitSize();
@@ -1266,13 +1348,14 @@ void QQuickTextPrivate::ensureDoc()
{
if (!extra.isAllocated() || !extra->doc) {
Q_Q(QQuickText);
- extra.value().doc = new QQuickTextDocumentWithImageResources(q);
- extra->doc->setPageSize(QSizeF(0, 0));
- extra->doc->setDocumentMargin(0);
+ extra.value().doc = new QTextDocument(q);
+ auto *doc = extra->doc;
+ extra->imageHandler = new QQuickTextImageHandler(doc);
+ doc->documentLayout()->registerHandler(QTextFormat::ImageObject, extra->imageHandler);
+ doc->setPageSize(QSizeF(0, 0));
+ doc->setDocumentMargin(0);
const QQmlContext *context = qmlContext(q);
- extra->doc->setBaseUrl(context ? context->resolvedUrl(q->baseUrl()) : q->baseUrl());
- qmlobject_connect(extra->doc, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()),
- q, QQuickText, SLOT(q_updateLayout()));
+ doc->setBaseUrl(context ? context->resolvedUrl(q->baseUrl()) : q->baseUrl());
}
}
@@ -1289,10 +1372,14 @@ void QQuickTextPrivate::updateDocumentText()
#else
extra->doc->setPlainText(text);
#endif
- extra->doc->clearResources();
rightToLeftText = extra->doc->toPlainText().isRightToLeft();
}
+qreal QQuickTextPrivate::devicePixelRatio() const
+{
+ return (window ? window->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
+}
+
/*!
\qmltype Text
\instantiates QQuickText
@@ -1301,8 +1388,8 @@ void QQuickTextPrivate::updateDocumentText()
\inherits Item
\brief Specifies how to add formatted text to a scene.
- Text items can display both plain and rich text. For example, red text with
- a specific font and size can be defined like this:
+ Text items can display both plain and rich text. For example, you can define
+ red text with a specific font and size like this:
\qml
Text {
@@ -1313,25 +1400,47 @@ void QQuickTextPrivate::updateDocumentText()
}
\endqml
- Rich text is defined using HTML-style markup:
+ Use HTML-style markup or Markdown to define rich text:
+ \if defined(onlinedocs)
+ \tab {build-qt-app}{tab-html}{HTML-style}{checked}
+ \tab {build-qt-app}{tab-md}{Markdown}{}
+ \tabcontent {tab-html}
+ \else
+ \section1 Using HTML-style
+ \endif
\qml
Text {
text: "<b>Hello</b> <i>World!</i>"
}
\endqml
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-md}
+ \else
+ \section1 Using Markdown
+ \endif
+ \qml
+ Text {
+ text: "**Hello** *World!*"
+ }
+ \endqml
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
\image declarative-text.png
- If height and width are not explicitly set, Text will attempt to determine how
- much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
- prefer width to height (all text will be placed on a single line).
+ If height and width are not explicitly set, Text will try to determine how
+ much room is needed and set it accordingly. Unless \l wrapMode is set, it
+ will always prefer width to height (all text will be placed on a single
+ line).
- The \l elide property can alternatively be used to fit a single line of
- plain text to a set width.
+ To fit a single line of plain text to a set width, you can use the \l elide
+ property.
- Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
- HTML img tags that load remote images, the text is reloaded.
+ Note that the \l{Supported HTML Subset} is limited. Also, if the text
+ contains HTML img tags that load remote images, the text is reloaded.
Text provides read-only text. For editable text, see \l TextEdit.
@@ -1353,13 +1462,18 @@ QQuickText::QQuickText(QQuickTextPrivate &dd, QQuickItem *parent)
QQuickText::~QQuickText()
{
+ Q_D(QQuickText);
+ if (d->extra.isAllocated()) {
+ qDeleteAll(d->extra->pixmapsInProgress);
+ d->extra->pixmapsInProgress.clear();
+ }
}
/*!
\qmlproperty bool QtQuick::Text::clip
This property holds whether the text is clipped.
- Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
+ Note that if the text does not fit in the bounding rectangle, it will be abruptly chopped.
If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
*/
@@ -1448,7 +1562,8 @@ QQuickText::~QQuickText()
Sets the family name of the font.
- The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
+ The family name is case insensitive and may optionally include a foundry
+ name, for example "Helvetica [Cronyx]".
If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
If the family isn't available a family will be set using the font matching algorithm.
*/
@@ -1474,17 +1589,16 @@ QQuickText::~QQuickText()
The requested weight of the font. The weight requested must be an integer
between 1 and 1000, or one of the predefined values:
- \list
- \li Font.Thin
- \li Font.Light
- \li Font.ExtraLight
- \li Font.Normal - the default
- \li Font.Medium
- \li Font.DemiBold
- \li Font.Bold
- \li Font.ExtraBold
- \li Font.Black
- \endlist
+
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400 (default)
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
\qml
Text { text: "Hello"; font.weight: Font.DemiBold }
@@ -1548,13 +1662,12 @@ QQuickText::~QQuickText()
Sets the capitalization for the text.
- \list
- \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
- \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
- \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
- \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
- \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
- \endlist
+ \value Font.MixedCase the normal case: no capitalization change is applied
+ \value Font.AllUppercase alters the text to be rendered in all uppercase type
+ \value Font.AllLowercase alters the text to be rendered in all lowercase type
+ \value Font.SmallCaps alters the text to be rendered in small-caps type
+ \value Font.Capitalize alters the text to be rendered with the first character of
+ each word as an uppercase character
\qml
Text { text: "Hello"; font.capitalization: Font.AllLowercase }
@@ -1571,23 +1684,21 @@ QQuickText::~QQuickText()
\note This property only has an effect when used together with render type Text.NativeRendering.
- \list
- \value Font.PreferDefaultHinting - Use the default hinting level for the target platform.
- \value Font.PreferNoHinting - If possible, render text without hinting the outlines
+ \value Font.PreferDefaultHinting Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting If possible, render text without hinting the outlines
of the glyphs. The text layout will be typographically accurate, using the same metrics
- as are used e.g. when printing.
- \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ as are used, for example, when printing.
+ \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting,
but align glyphs to the pixel grid in the vertical direction. The text will appear
crisper on displays where the density is too low to give an accurate rendering
of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
layout will be scalable to higher density devices (such as printers) without impacting
details such as line breaks.
- \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ \value Font.PreferFullHinting If possible, render text with hinting in both horizontal and
vertical directions. The text will be altered to optimize legibility on the target
device, but since the metrics will depend on the target size of the text, the positions
of glyphs, line breaks, and other typographical detail will not scale, meaning that a
text layout may look different on devices with different pixel densities.
- \endlist
\qml
Text { text: "Hello"; renderType: Text.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
@@ -1613,7 +1724,7 @@ QQuickText::~QQuickText()
Sometimes, a font will apply complex rules to a set of characters in order to
display them correctly. In some writing systems, such as Brahmic scripts, this is
- required in order for the text to be legible, but in e.g. Latin script, it is merely
+ required in order for the text to be legible, but in for example Latin script, it is merely
a cosmetic feature. Setting the \c preferShaping property to false will disable all
such features when they are not required, which will improve performance in most cases.
@@ -1623,6 +1734,141 @@ QQuickText::~QQuickText()
Text { text: "Some text"; font.preferShaping: false }
\endqml
*/
+
+/*!
+ \qmlproperty object QtQuick::Text::font.variableAxes
+ \since 6.7
+
+//! [qml-font-variable-axes]
+ Applies floating point values to variable axes in variable fonts.
+
+ Variable fonts provide a way to store multiple variations (with different weights, widths
+ or styles) in the same font file. The variations are given as floating point values for
+ a pre-defined set of parameters, called "variable axes". Specific instances are typically
+ given names by the font designer, and, in Qt, these can be selected using setStyleName()
+ just like traditional sub-families.
+
+ In some cases, it is also useful to provide arbitrary values for the different axes. For
+ instance, if a font has a Regular and Bold sub-family, you may want a weight in-between these.
+ You could then manually request this by supplying a custom value for the "wght" axis in the
+ font.
+
+ \qml
+ Text {
+ text: "Foobar"
+ font.family: "MyVariableFont"
+ font.variableAxes: { "wght": (Font.Normal + Font.Bold) / 2.0 }
+ }
+ \endqml
+
+ If the "wght" axis is supported by the font and the given value is within its defined range,
+ a font corresponding to the weight 550.0 will be provided.
+
+ There are a few standard axes than many fonts provide, such as "wght" (weight), "wdth" (width),
+ "ital" (italic) and "opsz" (optical size). They each have indivdual ranges defined in the font
+ itself. For instance, "wght" may span from 100 to 900 (QFont::Thin to QFont::Black) whereas
+ "ital" can span from 0 to 1 (from not italic to fully italic).
+
+ A font may also choose to define custom axes; the only limitation is that the name has to
+ meet the requirements for a QFont::Tag (sequence of four latin-1 characters.)
+
+ By default, no variable axes are set.
+
+ \note On Windows, variable axes are not supported if the optional GDI font backend is in use.
+
+ \sa QFont::setVariableAxis()
+//! [qml-font-variable-axes]
+*/
+
+
+/*!
+ \qmlproperty object QtQuick::Text::font.features
+ \since 6.6
+
+//! [qml-font-features]
+ Applies integer values to specific OpenType features when shaping the text based on the contents
+ in \a features. This provides advanced access to the font shaping process, and can be used
+ to support font features that are otherwise not covered in the API.
+
+ The font features are represented by a map from four-letter tags to integer values. This integer
+ value passed along with the tag in most cases represents a boolean value: A zero value means the
+ feature is disabled, and a non-zero value means it is enabled. For certain font features,
+ however, it may have other interpretations. For example, when applied to the \c salt feature, the
+ value is an index that specifies the stylistic alternative to use.
+
+ For example, the \c frac font feature will convert diagonal fractions separated with a slash
+ (such as \c 1/2) with a different representation. Typically this will involve baking the full
+ fraction into a single character width (such as \c ½).
+
+ If a font supports the \c frac feature, then it can be enabled in the shaper as in the following
+ code:
+
+ \qml
+ Text {
+ text: "One divided by two is 1/2"
+ font.family: "MyFractionFont"
+ font.features: { "frac": 1 }
+ }
+ \endqml
+
+ Multiple features can be assigned values in the same mapping. For instance,
+ if you would like to also disable kerning for the font, you can explicitly
+ disable this as follows:
+
+ \qml
+ Text {
+ text: "One divided by two is 1/2"
+ font.family: "MyFractionFont"
+ font.features: { "frac": 1, "kern": 0 }
+ }
+ \endqml
+
+ You can also collect the font properties in an object:
+
+ \qml
+ Text {
+ text: "One divided by two is 1/2"
+ font: {
+ family: "MyFractionFont"
+ features: { "frac": 1, "kern": 0 }
+ }
+ }
+ \endqml
+
+ \note By default, Qt will enable and disable certain font features based on other font
+ properties. In particular, the \c kern feature will be enabled/disabled depending on the
+ \l font.kerning property of the QFont. In addition, all ligature features (\c liga, \c clig,
+ \c dlig, \c hlig) will be disabled if a \l font.letterSpacing is set, but only for writing
+ systems where the use of ligature is cosmetic. For writing systems where ligatures are required,
+ the features will remain in their default state. The values set using \c font.features will
+ override the default behavior. If, for instance, \c{"kern"} is set to 1, then kerning will
+ always be enabled, regardless of whether the \l font.kerning property is set to false. Similarly,
+ if it is set to \c 0, it will always be disabled.
+
+ \sa QFont::setFeature()
+//! [qml-font-features]
+*/
+
+/*!
+ \qmlproperty object QtQuick::Text::font.contextFontMerging
+ \since 6.8
+
+//! [qml-font-context-font-merging]
+ If the selected font does not contain a certain character, Qt automatically chooses a
+ similar-looking fallback font that contains the character. By default this is done on a
+ character-by-character basis.
+
+ This means that in certain uncommon cases, many different fonts may be used to represent one
+ string of text even if it's in the same script. Setting \c contextFontMerging to true will try
+ finding the fallback font that matches the largest subset of the input string instead. This
+ will be more expensive for strings where missing glyphs occur, but may give more consistent
+ results. By default, \c contextFontMerging is \c{false}.
+
+ \sa QFont::StyleStrategy
+//! [qml-font-context-font-merging]
+*/
+
+
QFont QQuickText::font() const
{
Q_D(const QQuickText);
@@ -1677,14 +1923,30 @@ void QQuickText::itemChange(ItemChange change, const ItemChangeData &value)
break;
case ItemDevicePixelRatioHasChanged:
- if (d->renderType == NativeRendering) {
- // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
- // Text layout code respects the current device pixel ratio automatically, we only need
- // to rerun layout after the ratio changed.
- // Changes of implicit size should be minimal; they are hard to avoid.
- d->implicitWidthValid = false;
- d->implicitHeightValid = false;
- d->updateLayout();
+ {
+ bool needUpdateLayout = false;
+ if (d->renderType == NativeRendering) {
+ // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
+ // Text layout code respects the current device pixel ratio automatically, we only need
+ // to rerun layout after the ratio changed.
+ // Changes of implicit size should be minimal; they are hard to avoid.
+ d->implicitWidthValid = false;
+ d->implicitHeightValid = false;
+ needUpdateLayout = true;
+ }
+
+ if (d->extra.isAllocated()) {
+ // check if we have scalable inline images with explicit size set, which should be reloaded
+ for (QQuickStyledTextImgTag *image : std::as_const(d->extra->visibleImgTags)) {
+ if (image->size.isValid() && QQuickPixmap::isScalableImageFormat(image->url)) {
+ image->pix.reset();
+ needUpdateLayout = true;
+ }
+ }
+ }
+
+ if (needUpdateLayout)
+ d->updateLayout();
}
break;
@@ -1823,12 +2085,11 @@ void QQuickText::setLinkColor(const QColor &color)
Set an additional text style.
Supported text styles are:
- \list
- \li Text.Normal - the default
- \li Text.Outline
- \li Text.Raised
- \li Text.Sunken
- \endlist
+
+ \value Text.Normal - the default
+ \value Text.Outline
+ \value Text.Raised
+ \value Text.Sunken
\qml
Row {
@@ -2028,12 +2289,17 @@ void QQuickText::setVAlign(VAlignment align)
Set this property to wrap the text to the Text item's width. The text will only
wrap if an explicit width has been set. wrapMode can be one of:
- \list
- \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
- \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
- \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
- \li Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
- \endlist
+ \value Text.NoWrap
+ (default) no wrapping will be performed. If the text contains
+ insufficient newlines, then \l contentWidth will exceed a set width.
+ \value Text.WordWrap
+ wrapping is done on word boundaries only. If a word is too long,
+ \l contentWidth will exceed a set width.
+ \value Text.WrapAnywhere
+ wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \value Text.Wrap
+ if possible, wrapping occurs at a word boundary; otherwise it will occur
+ at the appropriate point on the line, even in the middle of a word.
*/
QQuickText::WrapMode QQuickText::wrapMode() const
{
@@ -2139,7 +2405,7 @@ void QQuickText::resetMaximumLineCount()
\l {https://guides.github.com/features/mastering-markdown/}{GitHub}
extensions for tables and task lists (since 5.14)
- If the text format is \c Text.AutoText the Text item
+ If the text format is \c Text.AutoText, the Text item
will automatically determine whether the text should be treated as
styled text. This determination is made using Qt::mightBeRichText(),
which can detect the presence of an HTML tag on the first line of text,
@@ -2184,7 +2450,6 @@ void QQuickText::resetMaximumLineCount()
\list
\li code blocks use the \l {QFontDatabase::FixedFont}{default monospace font} but without a surrounding highlight box
\li block quotes are indented, but there is no vertical line alongside the quote
- \li horizontal rules are not rendered
\endlist
*/
QQuickText::TextFormat QQuickText::textFormat() const
@@ -2230,12 +2495,11 @@ void QQuickText::setTextFormat(TextFormat format)
This property cannot be used with rich text.
Eliding can be:
- \list
- \li Text.ElideNone - the default
- \li Text.ElideLeft
- \li Text.ElideMiddle
- \li Text.ElideRight
- \endlist
+
+ \value Text.ElideNone - the default
+ \value Text.ElideLeft
+ \value Text.ElideMiddle
+ \value Text.ElideRight
If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
text. The text will only elide if \c maximumLineCount, or \c height has been set.
@@ -2269,7 +2533,7 @@ void QQuickText::setElideMode(QQuickText::TextElideMode mode)
/*!
\qmlproperty url QtQuick::Text::baseUrl
- This property specifies a base URL which is used to resolve relative URLs
+ This property specifies a base URL that is used to resolve relative URLs
within the text.
Urls are resolved to be within the same directory as the target of the base
@@ -2439,8 +2703,10 @@ void QQuickText::geometryChange(const QRectF &newGeometry, const QRectF &oldGeom
}
}
} else if (!heightChanged && widthMaximum) {
- if (!qFuzzyIsNull(oldGeometry.width())) {
+ if (oldGeometry.width() > 0) {
// no change to height, width is adequate and wasn't 0 before
+ // (old width could also be negative if it was 0 and the margins
+ // were set)
goto geomChangeDone;
}
}
@@ -2485,46 +2751,48 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height() + d->lineHeightOffset(), d->availableHeight(), d->vAlign) + topPadding();
- QQuickTextNode *node = nullptr;
+ QSGInternalTextNode *node = nullptr;
if (!oldNode)
- node = new QQuickTextNode(this);
+ node = d->sceneGraphContext()->createInternalTextNode(d->sceneGraphRenderContext());
else
- node = static_cast<QQuickTextNode *>(oldNode);
+ node = static_cast<QSGInternalTextNode *>(oldNode);
- node->setUseNativeRenderer(d->renderType == NativeRendering);
+ node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+
+ node->setTextStyle(QSGTextNode::TextStyle(d->style));
+ node->setRenderType(QSGTextNode::RenderType(d->renderType));
node->setRenderTypeQuality(d->renderTypeQuality());
- node->deleteContent();
+ node->clear();
node->setMatrix(QMatrix4x4());
- const QColor color = QColor::fromRgba(d->color);
- const QColor styleColor = QColor::fromRgba(d->styleColor);
- const QColor linkColor = QColor::fromRgba(d->linkColor);
+ node->setColor(QColor::fromRgba(d->color));
+ node->setStyleColor(QColor::fromRgba(d->styleColor));
+ node->setLinkColor(QColor::fromRgba(d->linkColor));
if (d->richText) {
+ node->setViewport(clipRect());
const qreal dx = QQuickTextUtil::alignedX(d->layedOutTextRect.width(), d->availableWidth(), effectiveHAlign()) + leftPadding();
d->ensureDoc();
- node->addTextDocument(QPointF(dx, dy), d->extra->doc, color, d->style, styleColor, linkColor);
+ node->addTextDocument(QPointF(dx, dy), d->extra->doc);
} else if (d->layedOutTextRect.width() > 0) {
+ if (flags().testFlag(ItemObservesViewport))
+ node->setViewport(clipRect());
+ else
+ node->setViewport(QRectF{});
const qreal dx = QQuickTextUtil::alignedX(d->lineWidth, d->availableWidth(), effectiveHAlign()) + leftPadding();
int unelidedLineCount = d->lineCount;
if (d->elideLayout)
unelidedLineCount -= 1;
- if (unelidedLineCount > 0) {
- node->addTextLayout(
- QPointF(dx, dy),
- &d->layout,
- color, d->style, styleColor, linkColor,
- QColor(), QColor(), -1, -1,
- 0, unelidedLineCount);
- }
+ if (unelidedLineCount > 0)
+ node->addTextLayout(QPointF(dx, dy), &d->layout, -1, -1,0, unelidedLineCount);
+
if (d->elideLayout)
- node->addTextLayout(QPointF(dx, dy), d->elideLayout, color, d->style, styleColor, linkColor);
+ node->addTextLayout(QPointF(dx, dy), d->elideLayout.get());
if (d->extra.isAllocated()) {
for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
- QQuickPixmap *pix = img->pix;
- if (pix && pix->isReady())
- node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
+ if (img->pix && img->pix->isReady())
+ node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, img->size.width(), img->size.height()), img->pix->image());
}
}
}
@@ -2539,6 +2807,11 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
void QQuickText::updatePolish()
{
Q_D(QQuickText);
+ const bool clipNodeChanged =
+ d->componentComplete && d->clipNode() && d->clipNode()->rect() != clipRect();
+ if (clipNodeChanged)
+ d->dirty(QQuickItemPrivate::Clip);
+
// If the fonts used for rendering are different from the ones used in the GUI thread,
// it means we will get warnings and corrupted text. If this case is detected, we need
// to update the text layout before creating the scenegraph nodes.
@@ -2556,7 +2829,7 @@ void QQuickText::updatePolish()
\qmlproperty real QtQuick::Text::contentWidth
Returns the width of the text, including width past the width
- which is covered due to insufficient wrapping if WrapMode is set.
+ that is covered due to insufficient wrapping if WrapMode is set.
*/
qreal QQuickText::contentWidth() const
{
@@ -2568,7 +2841,7 @@ qreal QQuickText::contentWidth() const
\qmlproperty real QtQuick::Text::contentHeight
Returns the height of the text, including height past the height
- which is covered due to there being more text than fits in the set height.
+ that is covered due to there being more text than fits in the set height.
*/
qreal QQuickText::contentHeight() const
{
@@ -2611,11 +2884,9 @@ void QQuickText::setLineHeight(qreal lineHeight)
This property determines how the line height is specified.
The possible values are:
- \list
- \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
- line (as a multiplier). For example, set to 2 for double spacing.
- \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
- \endlist
+ \value Text.ProportionalHeight (default) sets the spacing proportional to the line
+ (as a multiplier). For example, set to 2 for double spacing.
+ \value Text.FixedHeight sets the line height to a fixed line height (in pixels).
*/
QQuickText::LineHeightMode QQuickText::lineHeightMode() const
{
@@ -2643,16 +2914,16 @@ void QQuickText::setLineHeightMode(LineHeightMode mode)
This property specifies how the font size of the displayed text is determined.
The possible values are:
- \list
- \li Text.FixedSize (default) - The size specified by \l font.pixelSize
- or \l font.pointSize is used.
- \li Text.HorizontalFit - The largest size up to the size specified that fits
- within the width of the item without wrapping is used.
- \li Text.VerticalFit - The largest size up to the size specified that fits
- the height of the item is used.
- \li Text.Fit - The largest size up to the size specified that fits within the
- width and height of the item is used.
- \endlist
+ \value Text.FixedSize
+ (default) The size specified by \l font.pixelSize or \l font.pointSize is used.
+ \value Text.HorizontalFit
+ The largest size up to the size specified that fits within the width of the item
+ without wrapping is used.
+ \value Text.VerticalFit
+ The largest size up to the size specified that fits the height of the item is used.
+ \value Text.Fit
+ The largest size up to the size specified that fits within the width and height
+ of the item is used.
The font size of fitted text has a minimum bound specified by the
minimumPointSize or minimumPixelSize property and maximum bound specified
@@ -2757,8 +3028,8 @@ void QQuickText::setMinimumPointSize(int size)
int QQuickText::resourcesLoading() const
{
Q_D(const QQuickText);
- if (d->richText && d->extra.isAllocated() && d->extra->doc)
- return d->extra->doc->resourcesLoading();
+ if (d->richText && d->extra.isAllocated())
+ return d->extra->pixmapsInProgress.size();
return 0;
}
@@ -2808,7 +3079,7 @@ QString QQuickTextPrivate::anchorAt(const QPointF &mousePos) const
if (styledText) {
QString link = anchorAt(&layout, translatedMousePos);
if (link.isEmpty() && elideLayout)
- link = anchorAt(elideLayout, translatedMousePos);
+ link = anchorAt(elideLayout.get(), translatedMousePos);
return link;
} else if (richText && extra.isAllocated() && extra->doc) {
translatedMousePos.rx() -= QQuickTextUtil::alignedX(layedOutTextRect.width(), availableWidth(), q->effectiveHAlign());
@@ -2981,13 +3252,13 @@ void QQuickText::invalidate()
{
Q_D(QQuickText);
d->textHasChanged = true;
- d->updateLayout();
+ QMetaObject::invokeMethod(this,[&]{q_updateLayout();});
}
bool QQuickTextPrivate::transformChanged(QQuickItem *transformedItem)
{
// If there's a lot of text, we may need QQuickText::updatePaintNode() to call
- // QQuickTextNode::addTextLayout() again to populate a different range of lines
+ // QSGInternalTextNode::addTextLayout() again to populate a different range of lines
if (flags & QQuickItem::ItemObservesViewport) {
updateType = UpdatePaintNode;
dirty(QQuickItemPrivate::Content);
@@ -2997,6 +3268,7 @@ bool QQuickTextPrivate::transformChanged(QQuickItem *transformedItem)
/*!
\qmlproperty int QtQuick::Text::renderTypeQuality
+ \since 6.0
Override the default rendering type quality for this component. This is a low-level
customization which can be ignored in most cases. It currently only has an effect
@@ -3010,13 +3282,11 @@ bool QQuickTextPrivate::transformChanged(QQuickItem *transformedItem)
The \c renderTypeQuality may be any integer over 0, or one of the following
predefined values
- \list
- \li Text.DefaultRenderTypeQuality (default) = -1
- \li Text.LowRenderTypeQuality = 26
- \li Text.NormalRenderTypeQuality = 52
- \li Text.HighRenderTypeQuality = 104
- \li Text.VeryHighRenderTypeQuality = 208
- \endlist
+ \value Text.DefaultRenderTypeQuality -1 (default)
+ \value Text.LowRenderTypeQuality 26
+ \value Text.NormalRenderTypeQuality 52
+ \value Text.HighRenderTypeQuality 104
+ \value Text.VeryHighRenderTypeQuality 208
*/
int QQuickText::renderTypeQuality() const
{
@@ -3045,16 +3315,23 @@ void QQuickText::setRenderTypeQuality(int renderTypeQuality)
Override the default rendering type for this component.
Supported render types are:
- \list
- \li Text.QtRendering
- \li Text.NativeRendering
- \endlist
- Select Text.NativeRendering if you prefer text to look native on the target platform and do
+ \value Text.QtRendering Text is rendered using a scalable distance field for each glyph.
+ \value Text.NativeRendering Text is rendered using a platform-specific technique.
+ \value Text.CurveRendering Text is rendered using a curve rasterizer running directly on the
+ graphics hardware. (Introduced in Qt 6.7.0.)
+
+ Select \c Text.NativeRendering if you prefer text to look native on the target platform and do
not require advanced features such as transformation of the text. Using such features in
combination with the NativeRendering render type will lend poor and sometimes pixelated
results.
+ Both \c Text.QtRendering and \c Text.CurveRendering are hardware-accelerated techniques.
+ \c QtRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveRendering should be considered as an alternative in cases
+ where \c QtRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+
The default rendering type is determined by \l QQuickWindow::textRenderType().
*/
QQuickText::RenderType QQuickText::renderType() const
@@ -3310,7 +3587,7 @@ void QQuickText::resetBottomPadding()
*/
/*!
- \qmlproperty string QtQuick::Text::fontInfo.pixelSize
+ \qmlproperty int QtQuick::Text::fontInfo.pixelSize
\since 5.9
The pixel size of the font info that has been resolved for the current font
@@ -3347,7 +3624,7 @@ QJSValue QQuickText::fontInfo() const
in a text flow.
Note that the advance can be negative if the text flows from
- the right to the left.
+ right to left.
*/
QSizeF QQuickText::advance() const
{
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index e1d786d098..ed474b29c0 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickTextPrivate;
class QQuickTextLine;
-class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem, public QQuickTextInterface
+class Q_QUICK_EXPORT QQuickText : public QQuickImplicitSizeItem, public QQuickTextInterface
{
Q_OBJECT
Q_INTERFACES(QQuickTextInterface)
@@ -109,7 +109,8 @@ public:
Q_ENUM(WrapMode)
enum RenderType { QtRendering,
- NativeRendering
+ NativeRendering,
+ CurveRendering
};
Q_ENUM(RenderType)
@@ -300,6 +301,8 @@ protected:
private Q_SLOTS:
void q_updateLayout();
void triggerPreprocess();
+ Q_REVISION(6, 7) QVariant loadResource(int type, const QUrl &source);
+ void resourceRequestFinished();
void imageDownloadFinished();
private:
@@ -310,16 +313,16 @@ private:
Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickText::HAlignment, QQuickText::VAlignment)
class QTextLine;
-class QQuickTextLine : public QObject
+class Q_QUICK_EXPORT QQuickTextLine : public QObject
{
Q_OBJECT
- Q_PROPERTY(int number READ number)
- Q_PROPERTY(qreal width READ width WRITE setWidth)
- Q_PROPERTY(qreal height READ height WRITE setHeight)
- Q_PROPERTY(qreal x READ x WRITE setX)
- Q_PROPERTY(qreal y READ y WRITE setY)
- Q_PROPERTY(qreal implicitWidth READ implicitWidth REVISION(2, 15))
- Q_PROPERTY(bool isLast READ isLast REVISION(2, 15))
+ Q_PROPERTY(int number READ number FINAL)
+ Q_PROPERTY(qreal width READ width WRITE setWidth FINAL)
+ Q_PROPERTY(qreal height READ height WRITE setHeight FINAL)
+ Q_PROPERTY(qreal x READ x WRITE setX FINAL)
+ Q_PROPERTY(qreal y READ y WRITE setY FINAL)
+ Q_PROPERTY(qreal implicitWidth READ implicitWidth REVISION(2, 15) FINAL)
+ Q_PROPERTY(bool isLast READ isLast REVISION(2, 15) FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -354,7 +357,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickText)
-QML_DECLARE_TYPE(QQuickTextLine)
-
#endif // QQUICKTEXT_P_H
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index 9870197c31..6dba7a7d75 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -23,13 +23,13 @@
#include <QtGui/qtextlayout.h>
#include <private/qquickstyledtext_p.h>
#include <private/qlazilyallocated_p.h>
+#include <private/qquicktextdocument_p.h>
QT_BEGIN_NAMESPACE
class QTextLayout;
-class QQuickTextDocumentWithImageResources;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextPrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_EXPORT QQuickTextPrivate : public QQuickImplicitSizeItemPrivate
{
Q_DECLARE_PUBLIC(QQuickText)
public:
@@ -71,12 +71,12 @@ public:
bool explicitRightPadding : 1;
bool explicitBottomPadding : 1;
qreal lineHeight;
- QQuickTextDocumentWithImageResources *doc;
+ QTextDocument *doc;
+ QQuickTextImageHandler *imageHandler = nullptr;
QString activeLink;
QString hoveredLink;
int minimumPixelSize;
int minimumPointSize;
- int nbActiveDownloads;
int maximumLineCount;
int renderTypeQuality;
bool lineHeightValid : 1;
@@ -84,6 +84,7 @@ public:
QQuickText::FontSizeMode fontSizeMode;
QList<QQuickStyledTextImgTag*> imgTags;
QList<QQuickStyledTextImgTag*> visibleImgTags;
+ QList<QQuickPixmap *> pixmapsInProgress;
QUrl baseUrl;
};
QLazilyAllocated<ExtraData> extra;
@@ -94,8 +95,8 @@ public:
QFontInfo fontInfo;
QTextLayout layout;
- QTextLayout *elideLayout;
- QQuickTextLine *textLine;
+ QScopedPointer<QTextLayout> elideLayout;
+ QScopedPointer<QQuickTextLine> textLine;
qreal lineWidth;
@@ -162,6 +163,8 @@ public:
void ensureDoc();
void updateDocumentText();
+ qreal devicePixelRatio() const;
+
QRectF setupTextLayout(qreal * const baseline);
void setupCustomLineGeometry(QTextLine &line, qreal &height, int fullLayoutTextLength, int lineOffset = 0);
bool isLinkActivatedConnected();
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index 929ce10136..62b63f120d 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -81,6 +81,7 @@ QQuickTextControlPrivate::QQuickTextControlPrivate()
cursorRectangleChanged(false),
hoveredMarker(false),
selectByTouchDrag(false),
+ imSelectionAfterPress(false),
lastSelectionStart(-1),
lastSelectionEnd(-1)
{}
@@ -614,6 +615,12 @@ QTextDocument *QQuickTextControl::document() const
return d->doc;
}
+void QQuickTextControl::setDocument(QTextDocument *doc)
+{
+ Q_D(QQuickTextControl);
+ d->doc = doc;
+}
+
void QQuickTextControl::updateCursorRectangle(bool force)
{
Q_D(QQuickTextControl);
@@ -992,6 +999,7 @@ void QQuickTextControlPrivate::mousePressEvent(QMouseEvent *e, const QPointF &po
mousePressed = (interactionFlags & Qt::TextSelectableByMouse) && (e->button() & Qt::LeftButton);
mousePressPos = pos.toPoint();
+ imSelectionAfterPress = false;
if (sendMouseEventToInputContext(e, pos))
return;
@@ -1177,7 +1185,7 @@ void QQuickTextControlPrivate::mouseReleaseEvent(QMouseEvent *e, const QPointF &
q->insertFromMimeData(md);
#endif
}
- if (!isMouse && !selectByTouchDrag && interactionFlags.testFlag(Qt::TextEditable))
+ if (!isMouse && !selectByTouchDrag && !imSelectionAfterPress && interactionFlags.testFlag(Qt::TextEditable))
setCursorPosition(pos);
repaintOldAndNewSelection(oldSelection);
@@ -1322,6 +1330,8 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
for (int i = 0; i < e->attributes().size(); ++i) {
const QInputMethodEvent::Attribute &a = e->attributes().at(i);
if (a.type == QInputMethodEvent::Selection) {
+ if (mousePressed)
+ imSelectionAfterPress = true;
QTextCursor oldCursor = cursor;
int blockStart = a.start + cursor.block().position();
cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h
index 4994c4a51e..293409ca43 100644
--- a/src/quick/items/qquicktextcontrol_p.h
+++ b/src/quick/items/qquicktextcontrol_p.h
@@ -46,6 +46,7 @@ public:
virtual ~QQuickTextControl();
QTextDocument *document() const;
+ void setDocument(QTextDocument *doc);
void setTextCursor(const QTextCursor &cursor);
QTextCursor textCursor() const;
diff --git a/src/quick/items/qquicktextcontrol_p_p.h b/src/quick/items/qquicktextcontrol_p_p.h
index 5717cf6377..f04d298e4a 100644
--- a/src/quick/items/qquicktextcontrol_p_p.h
+++ b/src/quick/items/qquicktextcontrol_p_p.h
@@ -131,6 +131,7 @@ public:
bool cursorRectangleChanged : 1;
bool hoveredMarker: 1;
bool selectByTouchDrag: 1;
+ bool imSelectionAfterPress: 1;
int lastSelectionStart;
int lastSelectionEnd;
diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp
index ff28abe600..7812cf107e 100644
--- a/src/quick/items/qquicktextdocument.cpp
+++ b/src/quick/items/qquicktextdocument.cpp
@@ -5,15 +5,42 @@
#include "qquicktextdocument_p.h"
#include "qquicktextedit_p.h"
-#include "qquicktextedit_p_p.h"
-#include "qquicktext_p_p.h"
-#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlcontext.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuick/private/qquickpixmap_p.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcTextDoc, "qt.quick.textdocument")
+
+using namespace Qt::StringLiterals;
+
+/*!
+ \qmltype TextDocument
+ \instantiates QQuickTextDocument
+ \inqmlmodule QtQuick
+ \brief A wrapper around TextEdit's backing QTextDocument.
+ \preliminary
+
+ To load text into the document, set the \l source property. If the user then
+ modifies the text and wants to save the same document, call \l save() to save
+ it to the same source again (only if \l {QUrl::isLocalFile()}{it's a local file}).
+ Or call \l saveAs() to save it to a different file.
+
+ This class cannot be instantiated in QML, but is available from \l TextEdit::textDocument.
+
+ \note All loading and saving is done synchronously for now.
+ This may block the UI if the \l source is a slow network drive.
+ This may be improved in future versions of Qt.
+
+ \note This API is considered tech preview and may change in future versions of Qt.
+*/
+
/*!
\class QQuickTextDocument
\since 5.1
@@ -21,23 +48,12 @@ QT_BEGIN_NAMESPACE
\inmodule QtQuick
This class provides access to the QTextDocument of QQuickTextEdit elements.
- This is provided to allow usage of the \l{Rich Text Processing} functionalities of Qt.
- You are not allowed to modify the document, but it can be used to output content, for example with \l{QTextDocumentWriter}),
- or provide additional formatting, for example with \l{QSyntaxHighlighter}.
-
- The class has to be used from C++ directly, using the property of the \l TextEdit.
-
- Warning: The QTextDocument provided is used internally by \l {Qt Quick} elements to provide text manipulation primitives.
- You are not allowed to perform any modification of the internal state of the QTextDocument. If you do, the element
- in question may stop functioning or crash.
+ This is provided to allow usage of the \l{Rich Text Processing} functionalities of Qt,
+ including document modifications. It can also be used to output content,
+ for example with \l{QTextDocumentWriter}, or provide additional formatting,
+ for example with \l{QSyntaxHighlighter}.
*/
-class QQuickTextDocumentPrivate : public QObjectPrivate
-{
-public:
- QPointer<QTextDocument> document;
-};
-
/*!
Constructs a QQuickTextDocument object with
\a parent as the parent object.
@@ -47,74 +63,543 @@ QQuickTextDocument::QQuickTextDocument(QQuickItem *parent)
{
Q_D(QQuickTextDocument);
Q_ASSERT(parent);
- Q_ASSERT(qobject_cast<QQuickTextEdit*>(parent));
- d->document = QPointer<QTextDocument>(qobject_cast<QQuickTextEdit*>(parent)->d_func()->document);
+ d->editor = qobject_cast<QQuickTextEdit *>(parent);
+ Q_ASSERT(d->editor);
+ connect(textDocument(), &QTextDocument::modificationChanged,
+ this, &QQuickTextDocument::modifiedChanged);
}
/*!
- Returns a pointer to the QTextDocument object.
+ \property QQuickTextDocument::status
+ \brief the status of document loading or saving
+ \since 6.7
+ \preliminary
+
+ This property holds the status of document loading or saving. It can be one of:
+
+ \value Null No file has been loaded
+ \value Loading Reading from \l source has begun
+ \value Loaded Reading has successfully finished
+ \value Saving File writing has begun after save() or saveAs()
+ \value Saved Writing has successfully finished
+ \value ReadError An error occurred while reading from \l source
+ \value WriteError An error occurred in save() or saveAs()
+ \value NonLocalFileError saveAs() was called with a URL pointing
+ to a remote resource rather than a local file
+
+ \sa errorString, source, save(), saveAs()
*/
-QTextDocument* QQuickTextDocument::textDocument() const
+
+/*!
+ \qmlproperty enumeration QtQuick::TextDocument::status
+ \readonly
+ \since 6.7
+ \preliminary
+
+ This property holds the status of document loading or saving. It can be one of:
+
+ \value TextDocument.Null No file has been loaded
+ \value TextDocument.Loading Reading from \l source has begun
+ \value TextDocument.Loaded Reading has successfully finished
+ \value TextDocument.Saving File writing has begun after save() or saveAs()
+ \value TextDocument.Saved Writing has successfully finished
+ \value TextDocument.ReadError An error occurred while reading from \l source
+ \value TextDocument.WriteError An error occurred in save() or saveAs()
+ \value TextDocument.NonLocalFileError saveAs() was called with a URL pointing
+ to a remote resource rather than a local file
+
+ Use this status to provide an update or respond to the status change in some way.
+ For example, you could:
+
+ \list
+ \li Trigger a state change:
+ \qml
+ State {
+ name: 'loaded'
+ when: textEdit.textDocument.status == textEdit.textDocument.Loaded
+ }
+ \endqml
+
+ \li Implement an \c onStatusChanged signal handler:
+ \qml
+ TextEdit {
+ onStatusChanged: {
+ if (textDocument.status === textDocument.Loaded)
+ console.log('Loaded')
+ }
+ }
+ \endqml
+
+ \li Bind to the status value:
+
+ \snippet qml/textEditStatusSwitch.qml 0
+
+ \endlist
+
+ \sa errorString, source, save(), saveAs()
+*/
+QQuickTextDocument::Status QQuickTextDocument::status() const
{
Q_D(const QQuickTextDocument);
- return d->document.data();
+ return d->status;
}
-QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent)
-: QTextDocument(parent), outstanding(0)
+/*!
+ \property QQuickTextDocument::errorString
+ \brief a human-readable string describing the error that occurred during loading or saving, if any
+ \since 6.7
+ \preliminary
+
+ By default this string is empty.
+
+ \sa status, source, save(), saveAs()
+*/
+
+/*!
+ \qmlproperty string QtQuick::TextDocument::errorString
+ \readonly
+ \since 6.7
+ \preliminary
+
+ This property holds a human-readable string describing the error that
+ occurred during loading or saving, if any; otherwise, an empty string.
+
+ \sa status, source, save(), saveAs()
+*/
+QString QQuickTextDocument::errorString() const
{
- setUndoRedoEnabled(false);
- documentLayout()->registerHandler(QTextFormat::ImageObject, this);
- connect(this, &QTextDocument::baseUrlChanged, [this]() {
- clearResources();
- markContentsDirty(0, characterCount());
- });
+ Q_D(const QQuickTextDocument);
+ return d->errorString;
}
-QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
+void QQuickTextDocumentPrivate::setStatus(QQuickTextDocument::Status s, const QString &err)
{
- if (!m_resources.isEmpty())
- qDeleteAll(m_resources);
+ Q_Q(QQuickTextDocument);
+ if (status == s)
+ return;
+
+ status = s;
+ emit q->statusChanged();
+
+ if (errorString == err)
+ return;
+ errorString = err;
+ emit q->errorStringChanged();
+ if (!err.isEmpty())
+ qmlWarning(q) << err;
}
-QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
+/*!
+ \property QQuickTextDocument::source
+ \brief the URL from which to load document contents
+ \since 6.7
+ \preliminary
+
+ QQuickTextDocument can handle any text format supported by Qt, loaded from
+ any URL scheme supported by Qt.
+
+ The \c source property cannot be changed while the document's \l modified
+ state is \c true. If the user has modified the document contents, you
+ should prompt the user whether to \l save(), or else discard changes by
+ setting \l modified to \c false before setting the \c source property to a
+ different URL.
+
+ \sa QTextDocumentWriter::supportedDocumentFormats()
+*/
+
+/*!
+ \qmlproperty url QtQuick::TextDocument::source
+ \since 6.7
+ \preliminary
+
+ QQuickTextDocument can handle any text format supported by Qt, loaded from
+ any URL scheme supported by Qt.
+
+ The URL may be absolute, or relative to the URL of the component.
+
+ The \c source property cannot be changed while the document's \l modified
+ state is \c true. If the user has modified the document contents, you
+ should prompt the user whether to \l save(), or else discard changes by
+ setting \c {modified = false} before setting the \l source property to a
+ different URL.
+
+ \sa QTextDocumentWriter::supportedDocumentFormats()
+*/
+QUrl QQuickTextDocument::source() const
+{
+ Q_D(const QQuickTextDocument);
+ return d->url;
+}
+
+void QQuickTextDocument::setSource(const QUrl &url)
{
- QVariant resource = QTextDocument::loadResource(type, name);
- if (resource.isNull() && type == QTextDocument::ImageResource) {
- QQmlContext *context = qmlContext(parent());
- QUrl url = baseUrl().resolved(name);
- QQuickPixmap *p = loadPixmap(context, url);
- resource = p->image();
+ Q_D(QQuickTextDocument);
+
+ if (url == d->url)
+ return;
+
+ if (isModified()) {
+ qmlWarning(this) << "Existing document modified: you should save(),"
+ "or call TextEdit.clear() before setting a different source";
+ return;
}
- return resource;
+ d->url = url;
+ emit sourceChanged();
+ d->load();
+}
+
+/*!
+ \property QQuickTextDocument::modified
+ \brief whether the document has been modified by the user
+ \since 6.7
+ \preliminary
+
+ This property holds whether the document has been modified by the user
+ since the last time it was loaded or saved. By default, this property is
+ \c false.
+
+ As with \l QTextDocument::modified, you can set the modified property:
+ for example, set it to \c false to allow setting the \l source property
+ to a different URL (thus discarding the user's changes).
+
+ \sa QTextDocument::modified
+*/
+
+/*!
+ \qmlproperty bool QtQuick::TextDocument::modified
+ \since 6.7
+ \preliminary
+
+ This property holds whether the document has been modified by the user
+ since the last time it was loaded or saved. By default, this property is
+ \c false.
+
+ As with \l QTextDocument::modified, you can set the modified property:
+ for example, set it to \c false to allow setting the \l source property
+ to a different URL (thus discarding the user's changes).
+
+ \sa QTextDocument::modified
+*/
+bool QQuickTextDocument::isModified() const
+{
+ const auto *doc = textDocument();
+ return doc && doc->isModified();
+}
+
+void QQuickTextDocument::setModified(bool modified)
+{
+ if (auto *doc = textDocument())
+ doc->setModified(modified);
+}
+
+void QQuickTextDocumentPrivate::load()
+{
+ auto *doc = editor->document();
+ if (!doc) {
+ setStatus(QQuickTextDocument::Status::ReadError,
+ QQuickTextDocument::tr("Null document object: cannot load"));
+ return;
+ }
+ const QQmlContext *context = qmlContext(editor);
+ const QUrl &resolvedUrl = context ? context->resolvedUrl(url) : url;
+ const QString filePath = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
+ QFile file(filePath);
+ if (file.exists()) {
+#if QT_CONFIG(mimetype)
+ QMimeType mimeType = QMimeDatabase().mimeTypeForFile(filePath);
+ const bool isHtml = mimeType.inherits("text/html"_L1);
+ const bool isMarkdown = mimeType.inherits("text/markdown"_L1);
+#else
+ const bool isHtml = filePath.endsWith(".html"_L1, Qt::CaseInsensitive) ||
+ filePath.endsWith(".htm"_L1, Qt::CaseInsensitive);
+ const bool isMarkdown = filePath.endsWith(".md"_L1, Qt::CaseInsensitive) ||
+ filePath.endsWith(".markdown"_L1, Qt::CaseInsensitive);
+#endif
+ if (isHtml)
+ detectedFormat = Qt::RichText;
+ else if (isMarkdown)
+ detectedFormat = Qt::MarkdownText;
+ else
+ detectedFormat = Qt::PlainText;
+ if (file.open(QFile::ReadOnly | QFile::Text)) {
+ setStatus(QQuickTextDocument::Status::Loading, {});
+ QByteArray data = file.readAll();
+ doc->setBaseUrl(resolvedUrl.adjusted(QUrl::RemoveFilename));
+#if QT_CONFIG(textmarkdownreader) || QT_CONFIG(texthtmlparser)
+ const bool plainText = editor->textFormat() == QQuickTextEdit::PlainText;
+#endif
+#if QT_CONFIG(textmarkdownreader)
+ if (!plainText && isMarkdown) {
+ doc->setMarkdown(QString::fromUtf8(data));
+ } else
+#endif
+#if QT_CONFIG(texthtmlparser)
+ if (!plainText && isHtml) {
+ // If a user loads an HTML file, remember the encoding.
+ // If the user then calls save() later, the same encoding will be used.
+ encoding = QStringConverter::encodingForHtml(data);
+ if (encoding) {
+ QStringDecoder decoder(*encoding);
+ doc->setHtml(decoder(data));
+ } else {
+ // fall back to utf8
+ doc->setHtml(QString::fromUtf8(data));
+ }
+ } else
+#endif
+ {
+ doc->setPlainText(QString::fromUtf8(data));
+ }
+ setStatus(QQuickTextDocument::Status::Loaded, {});
+ qCDebug(lcTextDoc) << editor << "loaded" << filePath
+ << "as" << editor->textFormat() << "detected" << detectedFormat
+#if QT_CONFIG(mimetype)
+ << "(file type" << mimeType << ')'
+#endif
+ ;
+ doc->setModified(false);
+ return;
+ }
+ setStatus(QQuickTextDocument::Status::ReadError,
+ QQuickTextDocument::tr("Failed to read: %1").arg(file.errorString()));
+ } else {
+ setStatus(QQuickTextDocument::Status::ReadError,
+ QQuickTextDocument::tr("%1 does not exist").arg(filePath));
+ }
}
-void QQuickTextDocumentWithImageResources::requestFinished()
+void QQuickTextDocumentPrivate::writeTo(const QUrl &fileUrl)
{
- outstanding--;
- if (outstanding == 0) {
- markContentsDirty(0, characterCount());
- emit imagesLoaded();
+ auto *doc = editor->document();
+ if (!doc)
+ return;
+
+ const QString filePath = fileUrl.toLocalFile();
+ const bool sameUrl = fileUrl == url;
+ if (!sameUrl) {
+#if QT_CONFIG(mimetype)
+ const auto type = QMimeDatabase().mimeTypeForUrl(fileUrl);
+ if (type.inherits("text/html"_L1))
+ detectedFormat = Qt::RichText;
+ else if (type.inherits("text/markdown"_L1))
+ detectedFormat = Qt::MarkdownText;
+ else
+ detectedFormat = Qt::PlainText;
+#else
+ if (filePath.endsWith(".html"_L1, Qt::CaseInsensitive) ||
+ filePath.endsWith(".htm"_L1, Qt::CaseInsensitive))
+ detectedFormat = Qt::RichText;
+ else if (filePath.endsWith(".md"_L1, Qt::CaseInsensitive) ||
+ filePath.endsWith(".markdown"_L1, Qt::CaseInsensitive))
+ detectedFormat = Qt::MarkdownText;
+ else
+ detectedFormat = Qt::PlainText;
+#endif
}
+ QFile file(filePath);
+ if (!file.open(QFile::WriteOnly | QFile::Truncate |
+ (detectedFormat == Qt::RichText ? QFile::NotOpen : QFile::Text))) {
+ setStatus(QQuickTextDocument::Status::WriteError,
+ QQuickTextDocument::tr("Cannot save: %1").arg(file.errorString()));
+ return;
+ }
+ setStatus(QQuickTextDocument::Status::Saving, {});
+ QByteArray raw;
+
+ switch (detectedFormat) {
+#if QT_CONFIG(textmarkdownwriter)
+ case Qt::MarkdownText:
+ raw = doc->toMarkdown().toUtf8();
+ break;
+#endif
+#if QT_CONFIG(texthtmlparser)
+ case Qt::RichText:
+ if (sameUrl && encoding) {
+ QStringEncoder enc(*encoding);
+ raw = enc.encode(doc->toHtml());
+ } else {
+ // default to UTF-8 unless the user is saving the same file as previously loaded
+ raw = doc->toHtml().toUtf8();
+ }
+ break;
+#endif
+ default:
+ raw = doc->toPlainText().toUtf8();
+ break;
+ }
+
+ file.write(raw);
+ file.close();
+ setStatus(QQuickTextDocument::Status::Saved, {});
+ doc->setModified(false);
}
-QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
- QTextDocument *, int, const QTextFormat &format)
+QTextDocument *QQuickTextDocumentPrivate::document() const
+{
+ return editor->document();
+}
+
+void QQuickTextDocumentPrivate::setDocument(QTextDocument *doc)
+{
+ Q_Q(QQuickTextDocument);
+ QTextDocument *oldDoc = editor->document();
+ if (doc == oldDoc)
+ return;
+
+ if (oldDoc)
+ oldDoc->disconnect(q);
+ if (doc) {
+ q->connect(doc, &QTextDocument::modificationChanged,
+ q, &QQuickTextDocument::modifiedChanged);
+ }
+ editor->setDocument(doc);
+ emit q->textDocumentChanged();
+}
+
+/*!
+ Returns a pointer to the QTextDocument object.
+*/
+QTextDocument *QQuickTextDocument::textDocument() const
+{
+ Q_D(const QQuickTextDocument);
+ return d->document();
+}
+
+/*!
+ \brief Sets the given \a document.
+ \since 6.7
+
+ The caller retains ownership of the document.
+*/
+void QQuickTextDocument::setTextDocument(QTextDocument *document)
+{
+ d_func()->setDocument(document);
+}
+
+/*!
+ \fn void QQuickTextDocument::textDocumentChanged()
+ \since 6.7
+
+ This signal is emitted when the underlying QTextDocument is
+ replaced with a different instance.
+
+ \sa setTextDocument()
+*/
+
+/*!
+ \preliminary
+ \fn void QQuickTextDocument::sourceChanged()
+*/
+
+/*!
+ \preliminary
+ \fn void QQuickTextDocument::modifiedChanged()
+*/
+
+/*!
+ \preliminary
+ \fn void QQuickTextDocument::statusChanged()
+*/
+
+/*!
+ \preliminary
+ \fn void QQuickTextDocument::errorStringChanged()
+*/
+
+/*!
+ \fn void QQuickTextDocument::save()
+ \since 6.7
+ \preliminary
+
+ Saves the contents to the same file and format specified by \l source.
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, saveAs()
+*/
+
+/*!
+ \qmlmethod void QtQuick::TextDocument::save()
+ \brief Saves the contents to the same file and format specified by \l source.
+ \since 6.7
+ \preliminary
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, saveAs()
+*/
+void QQuickTextDocument::save()
+{
+ Q_D(QQuickTextDocument);
+ d->writeTo(d->url);
+}
+
+/*!
+ \fn void QQuickTextDocument::saveAs(const QUrl &url)
+ \brief Saves the contents to the file and format specified by \a url.
+ \since 6.7
+ \preliminary
+
+ The file extension in \a url specifies the file format
+ (as determined by QMimeDatabase::mimeTypeForUrl()).
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, save()
+*/
+
+/*!
+ \qmlmethod void QtQuick::TextDocument::saveAs(url url)
+ \brief Saves the contents to the file and format specified by \a url.
+ \since 6.7
+ \preliminary
+
+ The file extension in \a url specifies the file format
+ (as determined by QMimeDatabase::mimeTypeForUrl()).
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, save()
+*/
+void QQuickTextDocument::saveAs(const QUrl &url)
+{
+ Q_D(QQuickTextDocument);
+ if (!url.isLocalFile()) {
+ d->setStatus(QQuickTextDocument::Status::NonLocalFileError,
+ QQuickTextDocument::tr("Can only save to local files"));
+ return;
+ }
+ d->writeTo(url);
+
+ if (url == d->url)
+ return;
+
+ d->url = url;
+ emit sourceChanged();
+}
+
+QQuickTextImageHandler::QQuickTextImageHandler(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QSizeF QQuickTextImageHandler::intrinsicSize(
+ QTextDocument *doc, int, const QTextFormat &format)
{
if (format.isImageFormat()) {
QTextImageFormat imageFormat = format.toImageFormat();
-
const int width = qRound(imageFormat.width());
const bool hasWidth = imageFormat.hasProperty(QTextFormat::ImageWidth) && width > 0;
const int height = qRound(imageFormat.height());
const bool hasHeight = imageFormat.hasProperty(QTextFormat::ImageHeight) && height > 0;
-
QSizeF size(width, height);
if (!hasWidth || !hasHeight) {
- QVariant res = resource(QTextDocument::ImageResource, QUrl(imageFormat.name()));
+ QVariant res = doc->resource(QTextDocument::ImageResource, QUrl(imageFormat.name()));
QImage image = res.value<QImage>();
if (image.isNull()) {
+ // autotests expect us to reserve a 16x16 space for a "broken image" icon,
+ // even though we don't actually display one
if (!hasWidth)
size.setWidth(16);
if (!hasHeight)
@@ -122,7 +607,6 @@ QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
return size;
}
QSize imgSize = image.size();
-
if (!hasWidth) {
if (!hasHeight)
size.setWidth(imgSize.width());
@@ -141,54 +625,6 @@ QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
return QSizeF();
}
-void QQuickTextDocumentWithImageResources::drawObject(
- QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &)
-{
-}
-
-QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format) const
-{
- QVariant res = resource(QTextDocument::ImageResource, QUrl(format.name()));
- return res.value<QImage>();
-}
-
-QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
- QQmlContext *context, const QUrl &url)
-{
-
- QHash<QUrl, QQuickPixmap *>::Iterator iter = m_resources.find(url);
-
- if (iter == m_resources.end()) {
- QQuickPixmap *p = new QQuickPixmap(context->engine(), url);
- iter = m_resources.insert(url, p);
-
- if (p->isLoading()) {
- p->connectFinished(this, SLOT(requestFinished()));
- outstanding++;
- }
- }
-
- QQuickPixmap *p = *iter;
- if (p->isError()) {
- if (!errors.contains(url)) {
- errors.insert(url);
- qmlWarning(parent()) << p->error();
- }
- }
- return p;
-}
-
-void QQuickTextDocumentWithImageResources::clearResources()
-{
- for (QQuickPixmap *pixmap : std::as_const(m_resources))
- pixmap->clear(this);
- qDeleteAll(m_resources);
- m_resources.clear();
- outstanding = 0;
-}
-
-QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
-
QT_END_NAMESPACE
#include "moc_qquicktextdocument.cpp"
diff --git a/src/quick/items/qquicktextdocument.h b/src/quick/items/qquicktextdocument.h
index b73a76ec39..bccda205bd 100644
--- a/src/quick/items/qquicktextdocument.h
+++ b/src/quick/items/qquicktextdocument.h
@@ -13,12 +13,51 @@ class QQuickTextDocumentPrivate;
class Q_QUICK_EXPORT QQuickTextDocument : public QObject
{
Q_OBJECT
- QML_ANONYMOUS
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged REVISION(6, 7))
+ Q_PROPERTY(bool modified READ isModified WRITE setModified NOTIFY modifiedChanged REVISION(6, 7))
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged REVISION(6, 7))
+ Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged REVISION(6, 7))
+
+ QML_NAMED_ELEMENT(TextDocument)
+ QML_UNCREATABLE("TextDocument is only available as a property of TextEdit or TextArea.")
QML_ADDED_IN_VERSION(2, 0)
public:
+ enum class Status : quint8 {
+ Null = 0,
+ Loading,
+ Loaded,
+ Saving,
+ Saved,
+ ReadError,
+ WriteError,
+ NonLocalFileError,
+ };
+ Q_ENUM(Status)
+
QQuickTextDocument(QQuickItem *parent);
+
+ QUrl source() const;
+ void setSource(const QUrl &url);
+
+ bool isModified() const;
+ void setModified(bool modified);
+
QTextDocument *textDocument() const;
+ void setTextDocument(QTextDocument *document);
+
+ Q_REVISION(6, 7) Q_INVOKABLE void save();
+ Q_REVISION(6, 7) Q_INVOKABLE void saveAs(const QUrl &url);
+
+ Status status() const;
+ QString errorString() const;
+
+Q_SIGNALS:
+ Q_REVISION(6,7) void textDocumentChanged();
+ Q_REVISION(6, 7) void sourceChanged();
+ Q_REVISION(6, 7) void modifiedChanged();
+ Q_REVISION(6, 7) void statusChanged();
+ Q_REVISION(6, 7) void errorStringChanged();
private:
Q_DISABLE_COPY(QQuickTextDocument)
@@ -27,6 +66,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextDocument)
-
#endif
diff --git a/src/quick/items/qquicktextdocument_p.h b/src/quick/items/qquicktextdocument_p.h
index d7404aee3e..3e5236a55c 100644
--- a/src/quick/items/qquicktextdocument_p.h
+++ b/src/quick/items/qquicktextdocument_p.h
@@ -15,56 +15,76 @@
// We mean it.
//
-#include <QtCore/qhash.h>
-#include <QtCore/qvariant.h>
-#include <QtGui/qimage.h>
-#include <QtGui/qtextdocument.h>
+#include "qquicktextdocument.h"
+
#include <QtGui/qabstracttextdocumentlayout.h>
-#include <QtGui/qtextlayout.h>
-#include <QtCore/private/qglobal_p.h>
+#include <QtGui/qtextdocument.h>
+#include <QtGui/qtextdocumentfragment.h>
+#include <QtGui/qtextformat.h>
+#include <QtCore/qrect.h>
+#include <QtCore/private/qobject_p_p.h>
+
+#if QT_CONFIG(mimetype)
+#include <QtCore/qmimedatabase.h>
+#endif
QT_BEGIN_NAMESPACE
-class QQuickItem;
class QQuickPixmap;
-class QQmlContext;
-
-class Q_AUTOTEST_EXPORT QQuickTextDocumentWithImageResources : public QTextDocument, public QTextObjectInterface
+class QQuickTextEdit;
+
+/*! \internal
+ QTextImageHandler would attempt to resolve relative paths, and load the
+ image itself if the document returns an invalid image from loadResource().
+ We replace it with this version instead, because Qt Quick's text resources
+ are resolved against the Text item's context, and because we override
+ intrinsicSize(). drawObject() is empty because we don't need to use this
+ handler to paint images: they get put into scene graph nodes instead.
+*/
+class QQuickTextImageHandler : public QObject, public QTextObjectInterface
{
Q_OBJECT
Q_INTERFACES(QTextObjectInterface)
public:
- QQuickTextDocumentWithImageResources(QQuickItem *parent);
- virtual ~QQuickTextDocumentWithImageResources();
-
- int resourcesLoading() const { return outstanding; }
-
+ QQuickTextImageHandler(QObject *parent = nullptr);
+ ~QQuickTextImageHandler() override = default;
QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) override;
- void drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) override;
-
- QImage image(const QTextImageFormat &format) const;
-
-public Q_SLOTS:
- void clearResources();
-
-Q_SIGNALS:
- void imagesLoaded();
-
-protected:
- QVariant loadResource(int type, const QUrl &name) override;
-
- QQuickPixmap *loadPixmap(QQmlContext *context, const QUrl &name);
-
-private Q_SLOTS:
- void requestFinished();
+ void drawObject(QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &) override { }
+};
-private:
- QHash<QUrl, QQuickPixmap *> m_resources;
+class QQuickTextDocumentPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickTextDocument)
+public:
+ static QQuickTextDocumentPrivate *get(QQuickTextDocument *doc) { return doc->d_func(); }
+ static const QQuickTextDocumentPrivate *get(const QQuickTextDocument *doc) { return doc->d_func(); }
+
+ void load();
+ void writeTo(const QUrl &fileUrl);
+ QTextDocument *document() const;
+ void setDocument(QTextDocument *doc);
+ void setStatus(QQuickTextDocument::Status s, const QString &err);
+
+ // so far the QQuickItem given to the QQuickTextDocument ctor is always a QQuickTextEdit
+ QQuickTextEdit *editor = nullptr;
+ QUrl url;
+ QString errorString;
+ Qt::TextFormat detectedFormat = Qt::AutoText; // url's extension, independent of TextEdit.textFormat
+ std::optional<QStringConverter::Encoding> encoding; // only relevant for HTML (Qt::RichText)
+ QQuickTextDocument::Status status = QQuickTextDocument::Status::Null;
+};
- int outstanding;
- static QSet<QUrl> errors;
+namespace QtPrivate {
+class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout
+{
+public:
+ inline QTextCharFormat formatAccessor(int pos)
+ {
+ return format(pos);
+ }
};
+} // namespace QtPrivate
QT_END_NAMESPACE
-#endif // QQUICKDOCUMENT_P_H
+#endif // QQUICKTEXTDOCUMENT_P_H
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 191a33167f..02b34a6671 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -5,9 +5,8 @@
#include "qquicktextedit_p_p.h"
#include "qquicktextcontrol_p.h"
#include "qquicktextdocument_p.h"
-#include "qquickevents_p_p.h"
#include "qquickwindow.h"
-#include "qquicktextnode_p.h"
+#include "qsginternaltextnode_p.h"
#include "qquicktextnodeengine_p.h"
#include <QtCore/qmath.h>
@@ -23,6 +22,11 @@
#include <private/qqmlproperty_p.h>
#include <private/qtextengine_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <QtQuick/private/qquickpixmapcache_p.h>
+
+#if QT_CONFIG(accessibility)
+#include <private/qquickaccessibleattached_p.h>
+#endif
#include "qquicktextdocument.h"
@@ -33,6 +37,8 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcVP)
Q_LOGGING_CATEGORY(lcTextEdit, "qt.quick.textedit")
+using namespace Qt::StringLiterals;
+
/*!
\qmltype TextEdit
\instantiates QQuickTextEdit
@@ -79,7 +85,7 @@ TextEdit {
You can translate between cursor positions (characters from the start of the document) and pixel
points using positionAt() and positionToRectangle().
- \sa Text, TextInput
+ \sa Text, TextInput, TextArea, {Qt Quick Controls - Text Editor}
*/
/*!
@@ -101,22 +107,13 @@ static const int nodeBreakingSize = 300;
const int QQuickTextEditPrivate::largeTextSizeThreshold = QQUICKTEXT_LARGETEXT_THRESHOLD;
namespace {
- class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout
- {
- public:
- inline QTextCharFormat formatAccessor(int pos)
- {
- return format(pos);
- }
- };
-
class RootNode : public QSGTransformNode
{
public:
RootNode() : cursorNode(nullptr), frameDecorationsNode(nullptr)
{ }
- void resetFrameDecorations(QQuickTextNode* newNode)
+ void resetFrameDecorations(QSGInternalTextNode* newNode)
{
if (frameDecorationsNode) {
removeChildNode(frameDecorationsNode);
@@ -139,7 +136,7 @@ namespace {
}
QSGInternalRectangleNode *cursorNode;
- QQuickTextNode* frameDecorationsNode;
+ QSGInternalTextNode* frameDecorationsNode;
};
}
@@ -151,6 +148,12 @@ QQuickTextEdit::QQuickTextEdit(QQuickItem *parent)
d->init();
}
+QQuickTextEdit::~QQuickTextEdit()
+{
+ Q_D(QQuickTextEdit);
+ qDeleteAll(d->pixmapsInProgress);
+}
+
QQuickTextEdit::QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent)
: QQuickImplicitSizeItem(dd, parent)
{
@@ -211,17 +214,16 @@ QString QQuickTextEdit::text() const
The requested weight of the font. The weight requested must be an integer
between 1 and 1000, or one of the predefined values:
- \list
- \li Font.Thin
- \li Font.Light
- \li Font.ExtraLight
- \li Font.Normal - the default
- \li Font.Medium
- \li Font.DemiBold
- \li Font.Bold
- \li Font.ExtraBold
- \li Font.Black
- \endlist
+
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400 (default)
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
\qml
TextEdit { text: "Hello"; font.weight: Font.DemiBold }
@@ -286,13 +288,12 @@ QString QQuickTextEdit::text() const
Sets the capitalization for the text.
- \list
- \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
- \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
- \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
- \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
- \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
- \endlist
+ \value Font.MixedCase no capitalization change is applied
+ \value Font.AllUppercase alters the text to be rendered in all uppercase type
+ \value Font.AllLowercase alters the text to be rendered in all lowercase type
+ \value Font.SmallCaps alters the text to be rendered in small-caps type
+ \value Font.Capitalize alters the text to be rendered with the first character of
+ each word as an uppercase character
\qml
TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase }
@@ -309,23 +310,21 @@ QString QQuickTextEdit::text() const
\note This property only has an effect when used together with render type TextEdit.NativeRendering.
- \list
- \li Font.PreferDefaultHinting - Use the default hinting level for the target platform.
- \li Font.PreferNoHinting - If possible, render text without hinting the outlines
- of the glyphs. The text layout will be typographically accurate, using the same metrics
- as are used e.g. when printing.
- \li Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
- but align glyphs to the pixel grid in the vertical direction. The text will appear
- crisper on displays where the density is too low to give an accurate rendering
- of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
- layout will be scalable to higher density devices (such as printers) without impacting
- details such as line breaks.
- \li Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
- vertical directions. The text will be altered to optimize legibility on the target
- device, but since the metrics will depend on the target size of the text, the positions
- of glyphs, line breaks, and other typographical detail will not scale, meaning that a
- text layout may look different on devices with different pixel densities.
- \endlist
+ \value Font.PreferDefaultHinting Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting If possible, render text without hinting the outlines
+ of the glyphs. The text layout will be typographically accurate, using the same metrics
+ as are used e.g. when printing.
+ \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction. The text will appear
+ crisper on displays where the density is too low to give an accurate rendering
+ of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
+ layout will be scalable to higher density devices (such as printers) without impacting
+ details such as line breaks.
+ \value Font.PreferFullHinting If possible, render text with hinting in both horizontal and
+ vertical directions. The text will be altered to optimize legibility on the target
+ device, but since the metrics will depend on the target size of the text, the positions
+ of glyphs, line breaks, and other typographical detail will not scale, meaning that a
+ text layout may look different on devices with different pixel densities.
\qml
TextEdit { text: "Hello"; renderType: TextEdit.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
@@ -363,6 +362,27 @@ QString QQuickTextEdit::text() const
*/
/*!
+ \qmlproperty object QtQuick::TextEdit::font.variableAxes
+ \since 6.7
+
+ \include qquicktext.cpp qml-font-variable-axes
+*/
+
+/*!
+ \qmlproperty object QtQuick::TextEdit::font.features
+ \since 6.6
+
+ \include qquicktext.cpp qml-font-features
+*/
+
+/*!
+ \qmlproperty object QtQuick::TextEdit::font.contextFontMerging
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-context-font-merging
+*/
+
+/*!
\qmlproperty string QtQuick::TextEdit::text
The text to display. If the text format is AutoText the text edit will
@@ -376,7 +396,20 @@ QString QQuickTextEdit::text() const
remarkably better performance for modifying especially large rich text
content.
- \sa clear(), textFormat
+ Note that some keyboards use a predictive function. In this case,
+ the text being composed by the input method is not part of this property.
+ The part of the text related to the predictions is underlined and stored in
+ the \l preeditText property.
+
+ If you used \l TextDocument::source to load text, you can retrieve the
+ loaded text from this property. In that case, you can then change
+ \l textFormat to do format conversions that will change the value of the
+ \c text property. For example, if \c textFormat is \c RichText or
+ \c AutoText and you load an HTML file, then set \c textFormat to
+ \c MarkdownText afterwards, the \c text property will contain the
+ conversion from HTML to Markdown.
+
+ \sa clear(), preeditText, textFormat
*/
void QQuickTextEdit::setText(const QString &text)
{
@@ -384,7 +417,6 @@ void QQuickTextEdit::setText(const QString &text)
if (QQuickTextEdit::text() == text)
return;
- d->document->clearResources();
d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
d->markdownText = d->format == MarkdownText;
if (!isComponentComplete()) {
@@ -426,6 +458,11 @@ void QQuickTextEdit::q_invalidate()
\since 5.7
This property contains partial text input from an input method.
+
+ To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
+ flag in inputMethodHints.
+
+ \sa inputMethodHints
*/
QString QQuickTextEdit::preeditText() const
{
@@ -442,6 +479,7 @@ QString QQuickTextEdit::preeditText() const
\value TextEdit.PlainText (default) all styling tags are treated as plain text
\value TextEdit.AutoText detected via the Qt::mightBeRichText() heuristic
+ or the file format of \l TextDocument::source
\value TextEdit.RichText \l {Supported HTML Subset} {a subset of HTML 4}
\value TextEdit.MarkdownText \l {https://commonmark.org/help/}{CommonMark} plus the
\l {https://guides.github.com/features/mastering-markdown/}{GitHub}
@@ -449,9 +487,12 @@ QString QQuickTextEdit::preeditText() const
The default is \c TextEdit.PlainText. If the text format is set to
\c TextEdit.AutoText, the text edit will automatically determine whether
- the text should be treated as rich text. This determination is made using
- Qt::mightBeRichText(), which can detect the presence of an HTML tag on the
- first line of text, but cannot distinguish Markdown from plain text.
+ the text should be treated as rich text. If the \l text property is set,
+ this determination is made using Qt::mightBeRichText(), which can detect
+ the presence of an HTML tag on the first line of text, but cannot
+ distinguish Markdown from plain text. If the \l TextDocument::source
+ property is set, this determination is made from the
+ \l {QMimeDatabase::mimeTypeForFile()}{mime type of the file}.
\table
\row
@@ -464,14 +505,23 @@ QString QQuickTextEdit::preeditText() const
\l {https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown}{GitHub checkbox extension}
are interactively checkable.
- \note Interactively typing markup or markdown formatting is not supported.
+ If the \l TextDocument::source property is set, changing the \c textFormat
+ property after loading has the effect of converting from the detected
+ format to the requested format. For example, you can convert between HTML
+ and Markdown. However if either of those "rich" formats is loaded and then
+ you set \c textFormat to \c PlainText, the TextEdit will show the raw
+ markup. Thus, suitable bindings (e.g. to a checkable Control) can enable
+ the user to toggle back and forth between "raw" and WYSIWYG editing.
+
+ \note Interactively typing markup or markdown formatting in WYSIWYG mode
+ is not supported; but you can switch to \c PlainText, make changes, then
+ switch back to the appropriate \c textFormat.
\note With \c Text.MarkdownText, and with the supported subset of HTML,
some decorative elements are not rendered as they would be in a web browser:
\list
\li code blocks use the \l {QFontDatabase::FixedFont}{default monospace font} but without a surrounding highlight box
\li block quotes are indented, but there is no vertical line alongside the quote
- \li horizontal rules are not rendered
\endlist
*/
QQuickTextEdit::TextFormat QQuickTextEdit::textFormat() const
@@ -486,35 +536,135 @@ void QQuickTextEdit::setTextFormat(TextFormat format)
if (format == d->format)
return;
- const bool wasRich = d->richText;
- const bool wasMarkdown = d->markdownText;
- d->richText = format == RichText || (format == AutoText && (wasRich || Qt::mightBeRichText(text())));
- d->markdownText = format == MarkdownText;
+ auto mightBeRichText = [this]() {
+ return Qt::mightBeRichText(text());
+ };
+
+ auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
+ if (d->format == PlainText)
+ return PlainText;
+ if (d->richText) return RichText;
+ if (d->markdownText) return MarkdownText;
+ if (detectedFormat == Qt::AutoText && mightBeRichText())
+ return RichText;
+ return PlainText;
+ };
+
+ auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
+ if (format == AutoText) {
+ if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
+ return MarkdownText;
+ if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
+ return RichText;
+ return PlainText; // fallback
+ }
+ return format;
+ };
+
+ bool textCachedChanged = false;
+ bool converted = false;
if (isComponentComplete()) {
-#if QT_CONFIG(texthtmlparser)
- if (wasRich && !d->richText && !d->markdownText) {
- d->control->setPlainText(!d->textCached ? d->control->toHtml() : d->text);
- updateSize();
- } else if (!wasRich && d->richText) {
- d->control->setHtml(!d->textCached ? d->control->toPlainText() : d->text);
- updateSize();
+ Qt::TextFormat detectedFormat = Qt::AutoText; // default if we don't know
+ if (d->quickDocument) {
+ // If QQuickTextDocument is in use, content can be loaded from a file,
+ // and then mime type detection overrides mightBeRichText().
+ detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
}
+
+ const TextFormat sourceFormat = findSourceFormat(detectedFormat);
+ const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
+
+ d->richText = destinationFormat == RichText;
+ d->markdownText = destinationFormat == MarkdownText;
+
+ // If converting between markdown and HTML, avoid using cached text: have QTD re-generate it
+ if (format != PlainText && (sourceFormat != destinationFormat)) {
+ d->textCached = false;
+ textCachedChanged = true;
+ }
+
+ switch (destinationFormat) {
+ case PlainText:
+#if QT_CONFIG(texthtmlparser)
+ if (sourceFormat == RichText) {
+ // If rich or unknown text was loaded and now the user wants plain text, get the raw HTML.
+ // But if we didn't set textCached to false above, assume d->text already contains HTML.
+ // This will allow the user to see the actual HTML they loaded (rather than Qt regenerating crufty HTML).
+ d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
+ converted = true;
+ }
#endif
#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
- if (wasMarkdown && !d->markdownText && !d->richText) {
- d->control->setPlainText(!d->textCached ? d->control->toMarkdown() : d->text);
- updateSize();
- } else if (!wasMarkdown && d->markdownText) {
- d->control->setMarkdownText(!d->textCached ? d->control->toPlainText() : d->text);
- updateSize();
- }
+ if (sourceFormat == MarkdownText) {
+ // If markdown or unknown text was loaded and now the user wants plain text, get the raw Markdown.
+ // But if we didn't set textCached to false above, assume d->text already contains markdown.
+ // This will allow the user to see the actual markdown they loaded.
+ d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
+ converted = true;
+ }
+#endif
+ break;
+ case RichText:
+#if QT_CONFIG(texthtmlparser)
+ switch (sourceFormat) {
+ case MarkdownText:
+ // If markdown was loaded and now the user wants HTML, convert markdown to HTML.
+ d->control->setHtml(d->control->toHtml());
+ converted = true;
+ break;
+ case PlainText:
+ // If plain text was loaded and now the user wants HTML, interpret plain text as HTML.
+ // But if we didn't set textCached to false above, assume d->text already contains HTML.
+ d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
+ converted = true;
+ break;
+ case AutoText:
+ case RichText: // nothing to do
+ break;
+ }
#endif
+ break;
+ case MarkdownText:
+#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
+ switch (sourceFormat) {
+ case RichText:
+ // If HTML was loaded and now the user wants markdown, convert HTML to markdown.
+ d->control->setMarkdownText(d->control->toMarkdown());
+ converted = true;
+ break;
+ case PlainText:
+ // If plain text was loaded and now the user wants markdown, interpret plain text as markdown.
+ // But if we didn't set textCached to false above, assume d->text already contains markdown.
+ d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
+ converted = true;
+ break;
+ case AutoText:
+ case MarkdownText: // nothing to do
+ break;
+ }
+#endif
+ break;
+ case AutoText: // nothing to do
+ break;
+ }
+
+ if (converted)
+ updateSize();
+ } else {
+ d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
+ d->markdownText = format == MarkdownText;
}
+ qCDebug(lcTextEdit) << d->format << "->" << format
+ << "rich?" << d->richText << "md?" << d->markdownText
+ << "converted?" << converted << "cache invalidated?" << textCachedChanged;
+
d->format = format;
d->control->setAcceptRichText(d->format != PlainText);
emit textFormatChanged(d->format);
+ if (textCachedChanged)
+ emit textChanged();
}
/*!
@@ -523,16 +673,23 @@ void QQuickTextEdit::setTextFormat(TextFormat format)
Override the default rendering type for this component.
Supported render types are:
- \list
- \li Text.QtRendering
- \li Text.NativeRendering
- \endlist
- Select Text.NativeRendering if you prefer text to look native on the target platform and do
+ \value TextEdit.QtRendering Text is rendered using a scalable distance field for each glyph.
+ \value TextEdit.NativeRendering Text is rendered using a platform-specific technique.
+ \value TextEdit.CurveRendering Text is rendered using a curve rasterizer running directly on
+ the graphics hardware. (Introduced in Qt 6.7.0.)
+
+ Select \c TextEdit.NativeRendering if you prefer text to look native on the target platform and do
not require advanced features such as transformation of the text. Using such features in
combination with the NativeRendering render type will lend poor and sometimes pixelated
results.
+ Both \c TextEdit.QtRendering and \c TextEdit.CurveRendering are hardware-accelerated techniques.
+ \c QtRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveRendering should be considered as an alternative in cases
+ where \c QtRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+
The default rendering type is determined by \l QQuickWindow::textRenderType().
*/
QQuickTextEdit::RenderType QQuickTextEdit::renderType() const
@@ -678,19 +835,21 @@ void QQuickTextEdit::setSelectedTextColor(const QColor &color)
the left.
Valid values for \c horizontalAlignment are:
- \list
- \li TextEdit.AlignLeft (default)
- \li TextEdit.AlignRight
- \li TextEdit.AlignHCenter
- \li TextEdit.AlignJustify
- \endlist
+
+ \value TextEdit.AlignLeft
+ left alignment with ragged edges on the right (default)
+ \value TextEdit.AlignRight
+ align each line to the right with ragged edges on the left
+ \value TextEdit.AlignHCenter
+ align each line to the center
+ \value TextEdit.AlignJustify
+ align each line to both right and left, spreading out words as necessary
Valid values for \c verticalAlignment are:
- \list
- \li TextEdit.AlignTop (default)
- \li TextEdit.AlignBottom
- \li TextEdit.AlignVCenter
- \endlist
+
+ \value TextEdit.AlignTop start at the top of the item (default)
+ \value TextEdit.AlignBottom align the last line to the bottom and other lines above
+ \value TextEdit.AlignVCenter align the center vertically
When using the attached property LayoutMirroring::enabled to mirror application
layouts, the horizontal alignment of text will also be mirrored. However, the property
@@ -706,9 +865,8 @@ QQuickTextEdit::HAlignment QQuickTextEdit::hAlign() const
void QQuickTextEdit::setHAlign(HAlignment align)
{
Q_D(QQuickTextEdit);
- bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
- d->hAlignImplicit = false;
- if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
+
+ if (d->setHAlign(align, true) && isComponentComplete()) {
d->updateDefaultTextOption();
updateSize();
}
@@ -743,21 +901,34 @@ QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign() const
return effectiveAlignment;
}
-bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment alignment, bool forceAlign)
+bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment align, bool forceAlign)
{
Q_Q(QQuickTextEdit);
- if (hAlign != alignment || forceAlign) {
- QQuickTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
- hAlign = alignment;
- emit q->horizontalAlignmentChanged(alignment);
- if (oldEffectiveHAlign != q->effectiveHAlign())
- emit q->effectiveHorizontalAlignmentChanged();
+ if (hAlign == align && !forceAlign)
+ return false;
+
+ const bool wasImplicit = hAlignImplicit;
+ const auto oldEffectiveHAlign = q->effectiveHAlign();
+
+ hAlignImplicit = !forceAlign;
+ if (hAlign != align) {
+ hAlign = align;
+ emit q->horizontalAlignmentChanged(align);
+ }
+
+ if (q->effectiveHAlign() != oldEffectiveHAlign) {
+ emit q->effectiveHorizontalAlignmentChanged();
return true;
}
+
+ if (forceAlign && wasImplicit) {
+ // QTBUG-120052 - when horizontal text alignment is set explicitly,
+ // we need notify any other controls that may depend on it, like QQuickPlaceholderText
+ emit q->effectiveHorizontalAlignmentChanged();
+ }
return false;
}
-
Qt::LayoutDirection QQuickTextEditPrivate::textDirection(const QString &text) const
{
const QChar *character = text.constData();
@@ -780,20 +951,24 @@ Qt::LayoutDirection QQuickTextEditPrivate::textDirection(const QString &text) co
bool QQuickTextEditPrivate::determineHorizontalAlignment()
{
Q_Q(QQuickTextEdit);
- if (hAlignImplicit && q->isComponentComplete()) {
- Qt::LayoutDirection direction = contentDirection;
+ if (!hAlignImplicit || !q->isComponentComplete())
+ return false;
+
+ Qt::LayoutDirection direction = contentDirection;
#if QT_CONFIG(im)
- if (direction == Qt::LayoutDirectionAuto) {
- const QString preeditText = control->textCursor().block().layout()->preeditAreaText();
- direction = textDirection(preeditText);
- }
- if (direction == Qt::LayoutDirectionAuto)
- direction = qGuiApp->inputMethod()->inputDirection();
+ if (direction == Qt::LayoutDirectionAuto) {
+ QTextBlock block = control->textCursor().block();
+ if (!block.layout())
+ return false;
+ direction = textDirection(block.layout()->preeditAreaText());
+ }
+ if (direction == Qt::LayoutDirectionAuto)
+ direction = qGuiApp->inputMethod()->inputDirection();
#endif
- return setHAlign(direction == Qt::RightToLeft ? QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft);
- }
- return false;
+ const auto implicitHAlign = direction == Qt::RightToLeft ?
+ QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft;
+ return setHAlign(implicitHAlign);
}
void QQuickTextEditPrivate::mirrorChange()
@@ -840,6 +1015,26 @@ Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints() const
}
#endif
+#if QT_CONFIG(accessibility)
+void QQuickTextEditPrivate::accessibilityActiveChanged(bool active)
+{
+ if (!active)
+ return;
+
+ Q_Q(QQuickTextEdit);
+ if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
+ qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true))) {
+ accessibleAttached->setRole(effectiveAccessibleRole());
+ accessibleAttached->set_readOnly(q->isReadOnly());
+ }
+}
+
+QAccessible::Role QQuickTextEditPrivate::accessibleRole() const
+{
+ return QAccessible::EditableText;
+}
+#endif
+
void QQuickTextEditPrivate::setTopPadding(qreal value, bool reset)
{
Q_Q(QQuickTextEdit);
@@ -930,20 +1125,26 @@ void QQuickTextEdit::setVAlign(QQuickTextEdit::VAlignment alignment)
moveCursorDelegate();
emit verticalAlignmentChanged(d->vAlign);
}
+
/*!
\qmlproperty enumeration QtQuick::TextEdit::wrapMode
Set this property to wrap the text to the TextEdit item's width.
The text will only wrap if an explicit width has been set.
- \list
- \li TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
- \li TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
- \li TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
- \li TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
- \endlist
+ \value TextEdit.NoWrap
+ (default) no wrapping will be performed. If the text contains insufficient newlines,
+ \l {Item::}{implicitWidth} will exceed a set width.
+ \value TextEdit.WordWrap
+ wrapping is done on word boundaries only. If a word is too long,
+ \l {Item::}{implicitWidth} will exceed a set width.
+ \value TextEdit.WrapAnywhere
+ wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \value TextEdit.Wrap
+ if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate
+ point on the line, even in the middle of a word.
- The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap.
+ The default is \c TextEdit.NoWrap. If you set a width, consider using \c TextEdit.Wrap.
*/
QQuickTextEdit::WrapMode QQuickTextEdit::wrapMode() const
{
@@ -1107,6 +1308,24 @@ int QQuickTextEdit::positionAt(qreal x, qreal y) const
}
/*!
+ \qmlproperty QtQuick::TextSelection QtQuick::TextEdit::cursorSelection
+ \since 6.7
+ \preliminary
+
+ This property is an object that provides properties of the text that is
+ currently selected, if any, alongside the text cursor.
+
+ \sa selectedText, selectionStart, selectionEnd
+*/
+QQuickTextSelection *QQuickTextEdit::cursorSelection() const
+{
+ Q_D(const QQuickTextEdit);
+ if (!d->cursorSelection)
+ d->cursorSelection = new QQuickTextSelection(const_cast<QQuickTextEdit *>(this));
+ return d->cursorSelection;
+}
+
+/*!
\qmlmethod QtQuick::TextEdit::moveCursorSelection(int position, SelectionMode mode)
Moves the cursor to \a position and updates the selection according to the optional \a mode
@@ -1120,13 +1339,12 @@ int QQuickTextEdit::positionAt(qreal x, qreal y) const
The selection mode specifies whether the selection is updated on a per character or a per word
basis. If not specified the selection mode will default to \c {TextEdit.SelectCharacters}.
- \list
- \li TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
- the previous cursor position) to the specified position.
- \li TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
- words between the specified position and the previous cursor position. Words partially in the
- range are included.
- \endlist
+ \value TextEdit.SelectCharacters
+ Sets either the selectionStart or selectionEnd (whichever was at the previous cursor position)
+ to the specified position.
+ \value TextEdit.SelectWords
+ Sets the selectionStart and selectionEnd to include all words between the specified position
+ and the previous cursor position. Words partially in the range are included.
For example, take this sequence of calls:
@@ -1424,38 +1642,31 @@ void QQuickTextEdit::setTextMargin(qreal margin)
Flags that alter behaviour are:
- \list
- \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
- \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
- in any persistent storage like predictive user dictionary.
- \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
- when a sentence ends.
- \li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
- \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
- \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
- \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
-
- \li Qt.ImhDate - The text editor functions as a date field.
- \li Qt.ImhTime - The text editor functions as a time field.
- \endlist
+ \value Qt.ImhHiddenText Characters should be hidden, as is typically used when entering passwords.
+ \value Qt.ImhSensitiveData Typed text should not be stored by the active input method
+ in any persistent storage like predictive user dictionary.
+ \value Qt.ImhNoAutoUppercase The input method should not try to automatically switch to
+ upper case when a sentence ends.
+ \value Qt.ImhPreferNumbers Numbers are preferred (but not required).
+ \value Qt.ImhPreferUppercase Upper case letters are preferred (but not required).
+ \value Qt.ImhPreferLowercase Lower case letters are preferred (but not required).
+ \value Qt.ImhNoPredictiveText Do not use predictive text (i.e. dictionary lookup) while typing.
+ \value Qt.ImhDate The text editor functions as a date field.
+ \value Qt.ImhTime The text editor functions as a time field.
Flags that restrict input (exclusive flags) are:
- \list
- \li Qt.ImhDigitsOnly - Only digits are allowed.
- \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
- \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
- \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
- \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
- \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
- \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
- \endlist
+ \value Qt.ImhDigitsOnly Only digits are allowed.
+ \value Qt.ImhFormattedNumbersOnly Only number input is allowed. This includes decimal point and minus sign.
+ \value Qt.ImhUppercaseOnly Only upper case letter input is allowed.
+ \value Qt.ImhLowercaseOnly Only lower case letter input is allowed.
+ \value Qt.ImhDialableCharactersOnly Only characters suitable for phone dialing are allowed.
+ \value Qt.ImhEmailCharactersOnly Only characters suitable for email addresses are allowed.
+ \value Qt.ImhUrlCharactersOnly Only characters suitable for URLs are allowed.
Masks:
- \list
- \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
- \endlist
+ \value Qt.ImhExclusiveInputMask This mask yields nonzero if any of the exclusive flags are used.
*/
Qt::InputMethodHints QQuickTextEdit::inputMethodHints() const
@@ -1487,14 +1698,35 @@ void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
void QQuickTextEdit::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickTextEdit);
- if (!d->inLayout && ((newGeometry.width() != oldGeometry.width() && widthValid())
- || (newGeometry.height() != oldGeometry.height() && heightValid()))) {
+ if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
+ || (newGeometry.height() != oldGeometry.height()))) {
updateSize();
updateWholeDocument();
- moveCursorDelegate();
+ if (widthValid() || heightValid())
+ moveCursorDelegate();
}
QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
+}
+
+void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QQuickTextEdit);
+ Q_UNUSED(value);
+ switch (change) {
+ case ItemDevicePixelRatioHasChanged:
+ if (d->renderType == NativeRendering) {
+ // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
+ // Text layout code respects the current device pixel ratio automatically, we only need
+ // to rerun layout after the ratio changed.
+ updateSize();
+ updateWholeDocument();
+ }
+ break;
+ default:
+ break;
+ }
+ QQuickImplicitSizeItem::itemChange(change, value);
}
/*!
@@ -1509,20 +1741,17 @@ void QQuickTextEdit::componentComplete()
const QUrl url = baseUrl();
const QQmlContext *context = qmlContext(this);
d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
+ if (!d->text.isEmpty()) {
#if QT_CONFIG(texthtmlparser)
- if (d->richText)
- d->control->setHtml(d->text);
- else
+ if (d->richText)
+ d->control->setHtml(d->text);
+ else
#endif
#if QT_CONFIG(textmarkdownreader)
- if (d->markdownText)
- d->control->setMarkdownText(d->text);
- else
-#endif
- if (!d->text.isEmpty()) {
if (d->markdownText)
d->control->setMarkdownText(d->text);
else
+#endif
d->control->setPlainText(d->text);
}
@@ -1535,6 +1764,17 @@ void QQuickTextEdit::componentComplete()
if (d->cursorComponent && isCursorVisible())
QQuickTextUtil::createCursor(d);
polish();
+
+#if QT_CONFIG(accessibility)
+ if (QAccessible::isActive())
+ d->accessibilityActiveChanged(true);
+#endif
+}
+
+int QQuickTextEdit::resourcesLoading() const
+{
+ Q_D(const QQuickTextEdit);
+ return d->pixmapsInProgress.size();
}
/*!
@@ -1627,10 +1867,8 @@ void QQuickTextEdit::setSelectByMouse(bool on)
Specifies how text should be selected using a mouse.
- \list
- \li TextEdit.SelectCharacters - The selection is updated with individual characters. (Default)
- \li TextEdit.SelectWords - The selection is updated with whole words.
- \endlist
+ \value TextEdit.SelectCharacters (default) The selection is updated with individual characters.
+ \value TextEdit.SelectWords The selection is updated with whole words.
This property only applies when \l selectByMouse is true.
*/
@@ -1694,6 +1932,13 @@ void QQuickTextEdit::setReadOnly(bool r)
} else if (hasActiveFocus()) {
setCursorVisible(true);
}
+
+#if QT_CONFIG(accessibility)
+ if (QAccessible::isActive()) {
+ if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(this))
+ accessibleAttached->set_readOnly(r);
+ }
+#endif
}
bool QQuickTextEdit::isReadOnly() const
@@ -2055,6 +2300,87 @@ void QQuickTextEdit::triggerPreprocess()
update();
}
+/*! \internal
+ QTextDocument::loadResource() calls this to load inline images etc.
+ But if it's a local file, don't do it: let QTextDocument::loadResource()
+ load it in the default way. QQuickPixmap is for QtQuick-specific uses.
+*/
+QVariant QQuickTextEdit::loadResource(int type, const QUrl &source)
+{
+ Q_D(QQuickTextEdit);
+ const QUrl url = d->document->baseUrl().resolved(source);
+ if (url.isLocalFile()) {
+ // qmlWarning if the file doesn't exist (because QTextDocument::loadResource() can't do that)
+ QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
+ if (!fi.exists())
+ qmlWarning(this) << "Cannot open: " << url.toString();
+ // let QTextDocument::loadResource() handle local file loading
+ return {};
+ }
+
+ // see if we already started a load job
+ auto existingJobIter = std::find_if(
+ d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
+ [&url](const auto *job) { return job->url() == url; } );
+ if (existingJobIter != d->pixmapsInProgress.cend()) {
+ const QQuickPixmap *job = *existingJobIter;
+ if (job->isError()) {
+ qmlWarning(this) << job->error();
+ d->pixmapsInProgress.erase(existingJobIter);
+ delete job;
+ return QImage();
+ } else {
+ qCDebug(lcTextEdit) << "already downloading" << url;
+ // existing job: return a null variant if it's not done yet
+ return job->isReady() ? job->image() : QVariant();
+ }
+ }
+
+ // not found: start a new load job
+ qCDebug(lcTextEdit) << "loading" << source << "resolved" << url
+ << "type" << static_cast<QTextDocument::ResourceType>(type);
+ QQmlContext *context = qmlContext(this);
+ Q_ASSERT(context);
+ // don't cache it in QQuickPixmapCache, because it's cached in QTextDocumentPrivate::cachedResources
+ QQuickPixmap *p = new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
+ p->connectFinished(this, SLOT(resourceRequestFinished()));
+ d->pixmapsInProgress.append(p);
+ // the new job is probably not done; return a null variant if the caller should poll again
+ return p->isReady() ? p->image() : QVariant();
+}
+
+/*! \internal
+ Handle completion of a download that QQuickTextEdit::loadResource() started.
+*/
+void QQuickTextEdit::resourceRequestFinished()
+{
+ Q_D(QQuickTextEdit);
+ for (auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
+ auto *job = *it;
+ if (job->isError()) {
+ // get QTextDocument::loadResource() to call QQuickTextEdit::loadResource() again, to return the placeholder
+ qCDebug(lcTextEdit) << "failed to load (error)" << job->url();
+ d->document->resource(QTextDocument::ImageResource, job->url());
+ // that will call QQuickTextEdit::loadResource() which will delete the job;
+ // so leave it in pixmapsInProgress for now, and stop this loop
+ break;
+ } else if (job->isReady()) {
+ // get QTextDocument::loadResource() to call QQuickTextEdit::loadResource() again, and cache the result
+ auto res = d->document->resource(QTextDocument::ImageResource, job->url());
+ // If QTextDocument::resource() returned a valid variant, it's been cached too. Either way, the job is done.
+ qCDebug(lcTextEdit) << (res.isValid() ? "done downloading" : "failed to load") << job->url() << job->rect();
+ d->pixmapsInProgress.erase(it);
+ delete job;
+ break;
+ }
+ }
+ if (d->pixmapsInProgress.isEmpty()) {
+ invalidate();
+ updateSize();
+ q_invalidate();
+ }
+}
+
typedef QQuickTextEditPrivate::Node TextNode;
using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
@@ -2063,7 +2389,7 @@ static inline bool operator<(const TextNode &n1, const TextNode &n2)
return n1.startPos() < n2.startPos();
}
-static inline void updateNodeTransform(QQuickTextNode* node, const QPointF &topLeft)
+static inline void updateNodeTransform(QSGInternalTextNode *node, const QPointF &topLeft)
{
QMatrix4x4 transformMatrix;
transformMatrix.translate(topLeft.x(), topLeft.y());
@@ -2089,6 +2415,22 @@ void QQuickTextEdit::invalidateFontCaches()
}
}
+QTextDocument *QQuickTextEdit::document() const
+{
+ Q_D(const QQuickTextEdit);
+ return d->document;
+}
+
+void QQuickTextEdit::setDocument(QTextDocument *doc)
+{
+ Q_D(QQuickTextEdit);
+ if (d->ownsDocument)
+ delete d->document;
+ d->document = doc;
+ d->ownsDocument = false;
+ d->control->setDocument(doc);
+}
+
inline void resetEngine(QQuickTextNodeEngine *engine, const QColor& textColor, const QColor& selectedTextColor, const QColor& selectionColor)
{
*engine = QQuickTextNodeEngine();
@@ -2114,7 +2456,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
delete oldNode;
oldNode = nullptr;
- // If we had any QQuickTextNode node references, they were deleted along with the root node
+ // If we had any QSGInternalTextNode node references, they were deleted along with the root node
// But here we must delete the Node structures in textNodeMap
d->textNodeMap.clear();
}
@@ -2123,6 +2465,10 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
RootNode *rootNode = static_cast<RootNode *>(oldNode);
TextNodeIterator nodeIterator = d->textNodeMap.begin();
+ std::optional<int> firstPosAcrossAllNodes;
+ if (nodeIterator != d->textNodeMap.end())
+ firstPosAcrossAllNodes = nodeIterator->startPos();
+
while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
++nodeIterator;
@@ -2139,7 +2485,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
firstDirtyPos = nodeIterator->startPos();
// ### this could be optimized if the first and last dirty nodes are not connected
// as the intermediate text nodes would usually only need to be transformed differently.
- QQuickTextNode *firstCleanNode = nullptr;
+ QSGInternalTextNode *firstCleanNode = nullptr;
auto it = d->textNodeMap.constEnd();
while (it != nodeIterator) {
--it;
@@ -2165,7 +2511,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
rootNode->resetFrameDecorations(d->createTextNode());
resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor);
- QQuickTextNode *node = nullptr;
+ QSGInternalTextNode *node = nullptr;
int currentNodeSize = 0;
int nodeStart = firstDirtyPos;
@@ -2184,14 +2530,17 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
d->firstBlockInViewport = -1;
d->firstBlockPastViewport = -1;
+ int frameCount = -1;
while (!frames.isEmpty()) {
QTextFrame *textFrame = frames.takeFirst();
+ ++frameCount;
+ if (frameCount > 0)
+ firstDirtyPos = 0;
+ qCDebug(lcVP) << "frame" << frameCount << textFrame
+ << "from" << positionToRectangle(textFrame->firstPosition()).topLeft()
+ << "to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
frames.append(textFrame->childFrames());
frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
-
- if (textFrame->lastPosition() < firstDirtyPos
- || textFrame->firstPosition() >= firstCleanNode.startPos())
- continue;
resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
if (textFrame->firstPosition() > textFrame->lastPosition()
@@ -2199,7 +2548,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
node = d->createTextNode();
updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
const int pos = textFrame->firstPosition() - 1;
- ProtectedLayoutAccessor *a = static_cast<ProtectedLayoutAccessor *>(d->document->documentLayout());
+ auto *a = static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
QTextCharFormat format = a->formatAccessor(pos);
QTextBlock block = textFrame->firstCursorPosition().block();
nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
@@ -2241,7 +2590,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
inView = coveredRegion.bottom() > viewport.top();
}
- if (d->firstBlockInViewport < 0 && inView) {
+ const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
+ if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
// During backward scrolling, we need to iterate backwards from textNodeMap.begin() to fill the top of the viewport.
if (coveredRegion.top() > viewport.top() + 1) {
qCDebug(lcVP) << "checking backwards from block" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion;
@@ -2261,7 +2611,6 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
}
}
qCDebug(lcVP) << "first block in viewport" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion;
- d->firstBlockInViewport = block.blockNumber();
if (block.layout())
d->renderedRegion = coveredRegion;
} else {
@@ -2274,9 +2623,16 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
}
break; // skip rest of blocks in this frame
}
- if (inView && !block.text().isEmpty() && coveredRegion.isValid())
+ if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
d->renderedRegion = d->renderedRegion.united(coveredRegion);
+ // In case we're going to visit more (nested) frames after this, ensure that we
+ // don't omit any blocks that fit within the region that we claim as fully rendered.
+ if (!frames.isEmpty())
+ viewport = viewport.united(d->renderedRegion);
+ }
}
+ if (inView && d->firstBlockInViewport < 0)
+ d->firstBlockInViewport = block.blockNumber();
}
bool createdNodeInView = false;
@@ -2420,12 +2776,7 @@ bool QQuickTextEdit::isInputMethodComposing() const
}
QQuickTextEditPrivate::ExtraData::ExtraData()
- : padding(0)
- , topPadding(0)
- , leftPadding(0)
- , rightPadding(0)
- , bottomPadding(0)
- , explicitTopPadding(false)
+ : explicitTopPadding(false)
, explicitLeftPadding(false)
, explicitRightPadding(false)
, explicitBottomPadding(false)
@@ -2451,7 +2802,10 @@ void QQuickTextEditPrivate::init()
q->setAcceptHoverEvents(true);
- document = new QQuickTextDocumentWithImageResources(q);
+ document = new QTextDocument(q);
+ ownsDocument = true;
+ auto *imageHandler = new QQuickTextImageHandler(document);
+ document->documentLayout()->registerHandler(QTextFormat::ImageObject, imageHandler);
control = new QQuickTextControl(document, q);
control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
@@ -2472,10 +2826,9 @@ void QQuickTextEditPrivate::init()
#if QT_CONFIG(clipboard)
qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
#endif
- qmlobject_connect(document, QQuickTextDocumentWithImageResources, SIGNAL(undoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
- qmlobject_connect(document, QQuickTextDocumentWithImageResources, SIGNAL(redoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
- qmlobject_connect(document, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()), q, QQuickTextEdit, SLOT(updateSize()));
- QObject::connect(document, &QQuickTextDocumentWithImageResources::contentsChange, q, &QQuickTextEdit::q_contentsChange);
+ qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
+ qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
+ QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
@@ -2485,10 +2838,12 @@ void QQuickTextEditPrivate::init()
document->setUndoRedoEnabled(false); // flush undo buffer.
document->setUndoRedoEnabled(true);
updateDefaultTextOption();
+ document->setModified(false); // we merely changed some defaults: no edits worth saving yet
q->updateSize();
#if QT_CONFIG(cursor)
updateMouseCursorShape();
#endif
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
void QQuickTextEditPrivate::resetInputMethod()
@@ -2669,9 +3024,6 @@ void QQuickTextEdit::updateSize()
return;
}
- qreal naturalWidth = d->implicitWidth - leftPadding() - rightPadding();
-
- qreal newWidth = d->document->idealWidth();
// ### assumes that if the width is set, the text will fill to edges
// ### (unless wrap is false, then clipping will occur)
if (widthValid()) {
@@ -2683,8 +3035,7 @@ void QQuickTextEdit::updateSize()
}
if (d->requireImplicitWidth) {
d->document->setTextWidth(-1);
- naturalWidth = d->document->idealWidth();
-
+ const qreal naturalWidth = d->document->idealWidth();
const bool wasInLayout = d->inLayout;
d->inLayout = true;
if (d->isImplicitResizeEnabled())
@@ -2694,19 +3045,22 @@ void QQuickTextEdit::updateSize()
return; // get this far we'll get a warning to that effect.
}
const qreal newTextWidth = width() - leftPadding() - rightPadding();
- if (d->document->textWidth() != newTextWidth) {
+ if (d->document->textWidth() != newTextWidth)
+ d->document->setTextWidth(newTextWidth);
+ } else if (d->wrapMode == NoWrap) {
+ // normally, if explicit width is not set, we should call setTextWidth(-1) here,
+ // as we don't need to fit the text to any fixed width. But because of some bug
+ // in QTextDocument it also breaks RTL text alignment, so we use "idealWidth" instead.
+ const qreal newTextWidth = d->document->idealWidth();
+ if (d->document->textWidth() != newTextWidth)
d->document->setTextWidth(newTextWidth);
- newWidth = d->document->idealWidth();
- }
- //### need to confirm cost of always setting these
- } else if (d->wrapMode == NoWrap && d->document->textWidth() != newWidth) {
- d->document->setTextWidth(newWidth); // ### Text does not align if width is not set or the idealWidth exceeds the textWidth (QTextDoc bug)
} else {
d->document->setTextWidth(-1);
}
QFontMetricsF fm(d->font);
- qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
+ const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
+ const qreal newWidth = d->document->idealWidth();
if (d->isImplicitResizeEnabled()) {
// ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
@@ -2718,12 +3072,23 @@ void QQuickTextEdit::updateSize()
d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
- setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
+
+ qreal baseline = fm.ascent();
+ QTextBlock firstBlock = d->document->firstBlock();
+ if (firstBlock.isValid() && firstBlock.layout() != nullptr && firstBlock.lineCount() > 0)
+ baseline = firstBlock.layout()->lineAt(0).ascent();
+
+ setBaselineOffset(baseline + d->yoff + d->textMargin);
QSizeF size(newWidth, newHeight);
if (d->contentSize != size) {
d->contentSize = size;
- emit contentSizeChanged();
+ // Note: inResize is a bitfield so QScopedValueRollback can't be used here
+ const bool wasInResize = d->inResize;
+ d->inResize = true;
+ if (!wasInResize)
+ emit contentSizeChanged();
+ d->inResize = wasInResize;
updateTotalLines();
}
}
@@ -2861,6 +3226,34 @@ void QQuickTextEditPrivate::updateDefaultTextOption()
}
}
+void QQuickTextEditPrivate::onDocumentStatusChanged()
+{
+ Q_ASSERT(quickDocument);
+ switch (quickDocument->status()) {
+ case QQuickTextDocument::Status::Loaded:
+ case QQuickTextDocument::Status::Saved:
+ switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
+ case Qt::RichText:
+ richText = (format == QQuickTextEdit::RichText || format == QQuickTextEdit::AutoText);
+ markdownText = false;
+ break;
+ case Qt::MarkdownText:
+ richText = false;
+ markdownText = (format == QQuickTextEdit::MarkdownText || format == QQuickTextEdit::AutoText);
+ break;
+ case Qt::PlainText:
+ richText = false;
+ markdownText = false;
+ break;
+ case Qt::AutoText: // format not detected
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
void QQuickTextEdit::focusInEvent(QFocusEvent *event)
{
Q_D(QQuickTextEdit);
@@ -2905,19 +3298,21 @@ void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
}
}
-void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QQuickTextNode *node, TextNodeIterator &it, int startPos)
+void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it, int startPos)
{
engine->addToSceneGraph(node, QQuickText::Normal, QColor());
it = textNodeMap.insert(it, TextNode(startPos, node));
++it;
root->appendChildNode(node);
+ ++renderedBlockCount;
}
-QQuickTextNode *QQuickTextEditPrivate::createTextNode()
+QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
{
Q_Q(QQuickTextEdit);
- QQuickTextNode* node = new QQuickTextNode(q);
- node->setUseNativeRenderer(renderType == QQuickTextEdit::NativeRendering);
+ QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
+ node->setRenderType(QSGTextNode::RenderType(renderType));
+ node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
return node;
}
@@ -3047,8 +3442,9 @@ void QQuickTextEdit::remove(int start, int end)
\since 5.1
Returns the QQuickTextDocument of this TextEdit.
- It can be used to implement syntax highlighting using
- \l QSyntaxHighlighter.
+ Since Qt 6.7, it has features for loading and saving files.
+ It can also be used in C++ as a means of accessing the underlying QTextDocument
+ instance, for example to install a \l QSyntaxHighlighter.
\sa QQuickTextDocument
*/
@@ -3056,8 +3452,11 @@ void QQuickTextEdit::remove(int start, int end)
QQuickTextDocument *QQuickTextEdit::textDocument()
{
Q_D(QQuickTextEdit);
- if (!d->quickDocument)
+ if (!d->quickDocument) {
d->quickDocument = new QQuickTextDocument(this);
+ connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
+ [d]() { d->onDocumentStatusChanged(); } );
+ }
return d->quickDocument;
}
@@ -3399,4 +3798,3 @@ QQuickPre64TextEdit::QQuickPre64TextEdit(QQuickItem *parent)
QT_END_NAMESPACE
#include "moc_qquicktextedit_p.cpp"
-
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index c98d062c6b..ef09457327 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -22,11 +22,21 @@
QT_BEGIN_NAMESPACE
+class QTextDocument;
class QQuickTextDocument;
class QQuickTextEditPrivate;
+class QQuickTextSelection;
class QTextBlock;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem, public QQuickTextInterface
+class QQuickTextBlockForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QTextBlock)
+ QML_EXTENDED(QQuickTextBlockForeign)
+};
+
+class Q_QUICK_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem, public QQuickTextInterface
{
Q_OBJECT
Q_INTERFACES(QQuickTextInterface)
@@ -78,6 +88,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem, pub
Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION(2, 6))
Q_PROPERTY(QString preeditText READ preeditText NOTIFY preeditTextChanged REVISION(2, 7))
Q_PROPERTY(qreal tabStopDistance READ tabStopDistance WRITE setTabStopDistance NOTIFY tabStopDistanceChanged REVISION(2, 10))
+ Q_PROPERTY(QQuickTextSelection* cursorSelection READ cursorSelection REVISION(6, 7) CONSTANT FINAL)
QML_NAMED_ELEMENT(TextEdit)
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
QML_ADDED_IN_VERSION(6, 4)
@@ -85,6 +96,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem, pub
public:
QQuickTextEdit(QQuickItem *parent=nullptr);
+ ~QQuickTextEdit() override;
enum HAlignment {
AlignLeft = Qt::AlignLeft,
@@ -124,7 +136,8 @@ public:
Q_ENUM(SelectionMode)
enum RenderType { QtRendering,
- NativeRendering
+ NativeRendering,
+ CurveRendering
};
Q_ENUM(RenderType)
@@ -208,6 +221,8 @@ public:
void componentComplete() override;
+ int resourcesLoading() const; // mainly for testing
+
/* FROM EDIT */
void setReadOnly(bool);
bool isReadOnly() const;
@@ -231,6 +246,8 @@ public:
Q_INVOKABLE void moveCursorSelection(int pos);
Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode);
+ QQuickTextSelection *cursorSelection() const;
+
QRectF boundingRect() const override;
QRectF clipRect() const override;
@@ -353,11 +370,15 @@ private Q_SLOTS:
void q_updateAlignment();
void updateSize();
void triggerPreprocess();
+ Q_REVISION(6, 7) QVariant loadResource(int type, const QUrl &source);
+ void resourceRequestFinished();
private:
void markDirtyNodesForRange(int start, int end, int charDelta);
void updateTotalLines();
void invalidateFontCaches();
+ QTextDocument* document() const;
+ void setDocument(QTextDocument *doc);
protected:
QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent = nullptr);
@@ -366,6 +387,7 @@ protected:
#endif
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void itemChange(ItemChange change, const ItemChangeData &value) override;
bool event(QEvent *) override;
void keyPressEvent(QKeyEvent *) override;
@@ -389,7 +411,7 @@ protected:
void updatePolish() override;
friend class QQuickTextUtil;
- friend class QQuickTextDocument;
+ friend class QQuickTextDocumentPrivate;
private:
Q_DISABLE_COPY(QQuickTextEdit)
@@ -411,6 +433,4 @@ Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickTextEdit::HAlignment, QQuick
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextEdit)
-
#endif // QQUICKTEXTEDIT_P_H
diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h
index db07462a5a..da01be47c2 100644
--- a/src/quick/items/qquicktextedit_p_p.h
+++ b/src/quick/items/qquicktextedit_p_p.h
@@ -19,20 +19,30 @@
#include "qquickimplicitsizeitem_p_p.h"
#include "qquicktextutil_p.h"
+#include <QtQuick/private/qquicktextselection_p.h>
+
#include <QtQml/qqml.h>
#include <QtCore/qlist.h>
#include <private/qlazilyallocated_p.h>
+#include <private/qquicktextdocument_p.h>
+
+#if QT_CONFIG(accessibility)
+#include <QtGui/qaccessible.h>
+#endif
#include <limits>
QT_BEGIN_NAMESPACE
class QTextLayout;
-class QQuickTextDocumentWithImageResources;
+class QQuickPixmap;
class QQuickTextControl;
-class QQuickTextNode;
+class QSGInternalTextNode;
class QQuickTextNodeEngine;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_EXPORT QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate
+#if QT_CONFIG(accessibility)
+ , public QAccessible::ActivationObserver
+#endif
{
public:
Q_DECLARE_PUBLIC(QQuickTextEdit)
@@ -41,21 +51,21 @@ public:
struct Node {
explicit Node(int startPos = std::numeric_limits<int>::max(),
- QQuickTextNode *node = nullptr)
- : m_startPos(startPos), m_node(node), m_dirty(false) { }
- QQuickTextNode* textNode() const { return m_node; }
+ QSGInternalTextNode *node = nullptr)
+ : m_node(node), m_startPos(startPos) { }
+ QSGInternalTextNode *textNode() const { return m_node; }
void moveStartPos(int delta) { Q_ASSERT(m_startPos + delta > 0); m_startPos += delta; }
int startPos() const { return m_startPos; }
void setDirty() { m_dirty = true; }
bool dirty() const { return m_dirty; }
private:
+ QSGInternalTextNode *m_node;
int m_startPos;
- QQuickTextNode* m_node;
- bool m_dirty;
+ bool m_dirty = false;
#ifndef QT_NO_DEBUG_STREAM
- friend QDebug Q_QUICK_PRIVATE_EXPORT operator<<(QDebug, const Node &);
+ friend QDebug Q_QUICK_EXPORT operator<<(QDebug, const Node &);
#endif
};
typedef QList<Node>::iterator TextNodeIterator;
@@ -63,11 +73,11 @@ public:
struct ExtraData {
ExtraData();
- qreal padding;
- qreal topPadding;
- qreal leftPadding;
- qreal rightPadding;
- qreal bottomPadding;
+ qreal padding = 0;
+ qreal topPadding = 0;
+ qreal leftPadding = 0;
+ qreal rightPadding = 0;
+ qreal bottomPadding = 0;
bool explicitTopPadding : 1;
bool explicitLeftPadding : 1;
bool explicitRightPadding : 1;
@@ -78,25 +88,22 @@ public:
QQuickTextEditPrivate()
- : color(QRgb(0xFF000000)), selectionColor(QRgb(0xFF000080)), selectedTextColor(QRgb(0xFFFFFFFF))
- , textMargin(0.0), xoff(0), yoff(0)
- , font(sourceFont), cursorComponent(nullptr), cursorItem(nullptr), document(nullptr), control(nullptr)
- , quickDocument(nullptr), lastSelectionStart(0), lastSelectionEnd(0), lineCount(0)
- , hAlign(QQuickTextEdit::AlignLeft), vAlign(QQuickTextEdit::AlignTop)
- , format(QQuickTextEdit::PlainText), wrapMode(QQuickTextEdit::NoWrap)
- , renderType(QQuickTextUtil::textRenderType<QQuickTextEdit>())
- , contentDirection(Qt::LayoutDirectionAuto)
- , mouseSelectionMode(QQuickTextEdit::SelectCharacters)
-#if QT_CONFIG(im)
- , inputMethodHints(Qt::ImhNone)
-#endif
- , updateType(UpdatePaintNode)
- , dirty(false), richText(false), cursorVisible(false), cursorPending(false)
+ : dirty(false), richText(false), cursorVisible(false), cursorPending(false)
, focusOnPress(true), persistentSelection(false), requireImplicitWidth(false)
, selectByMouse(true), canPaste(false), canPasteValid(false), hAlignImplicit(true)
, textCached(true), inLayout(false), selectByKeyboard(false), selectByKeyboardSet(false)
- , hadSelection(false), markdownText(false)
+ , hadSelection(false), markdownText(false), inResize(false), ownsDocument(false)
{
+#if QT_CONFIG(accessibility)
+ QAccessible::installActivationObserver(this);
+#endif
+ }
+
+ ~QQuickTextEditPrivate()
+ {
+#if QT_CONFIG(accessibility)
+ QAccessible::removeActivationObserver(this);
+#endif
}
static QQuickTextEditPrivate *get(QQuickTextEdit *item) {
@@ -106,6 +113,7 @@ public:
void resetInputMethod();
void updateDefaultTextOption();
+ void onDocumentStatusChanged();
void relayoutDocument();
bool determineHorizontalAlignment();
bool setHAlign(QQuickTextEdit::HAlignment, bool forceAlign = false);
@@ -121,13 +129,18 @@ public:
void setNativeCursorEnabled(bool) {}
void handleFocusEvent(QFocusEvent *event);
- void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QQuickTextNode*, TextNodeIterator&, int startPos);
- QQuickTextNode* createTextNode();
+ void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QSGInternalTextNode *, TextNodeIterator&, int startPos);
+ QSGInternalTextNode* createTextNode();
#if QT_CONFIG(im)
Qt::InputMethodHints effectiveInputMethodHints() const;
#endif
+#if QT_CONFIG(accessibility)
+ void accessibilityActiveChanged(bool active) override;
+ QAccessible::Role accessibleRole() const override;
+#endif
+
inline qreal padding() const { return extra.isAllocated() ? extra->padding : 0.0; }
void setTopPadding(qreal value, bool reset = false);
void setLeftPadding(qreal value, bool reset = false);
@@ -137,33 +150,36 @@ public:
bool isImplicitResizeEnabled() const;
void setImplicitResizeEnabled(bool enabled);
- QColor color;
- QColor selectionColor;
- QColor selectedTextColor;
+ QColor color = QRgb(0xFF000000);
+ QColor selectionColor = QRgb(0xFF000080);
+ QColor selectedTextColor = QRgb(0xFFFFFFFF);
QSizeF contentSize;
- qreal textMargin;
- qreal xoff;
- qreal yoff;
+ qreal textMargin = 0;
+ qreal xoff = 0;
+ qreal yoff = 0;
QString text;
QUrl baseUrl;
QFont sourceFont;
QFont font;
- QQmlComponent* cursorComponent;
- QQuickItem* cursorItem;
- QQuickTextDocumentWithImageResources *document;
- QQuickTextControl *control;
- QQuickTextDocument *quickDocument;
+ QQmlComponent* cursorComponent = nullptr;
+ QQuickItem* cursorItem = nullptr;
+ QTextDocument *document = nullptr;
+ QQuickTextControl *control = nullptr;
+ QQuickTextDocument *quickDocument = nullptr;
+ mutable QQuickTextSelection *cursorSelection = nullptr;
QList<Node> textNodeMap;
+ QList<QQuickPixmap *> pixmapsInProgress;
- int lastSelectionStart;
- int lastSelectionEnd;
- int lineCount;
- int firstBlockInViewport = -1; // only for the autotest; can be wrong after scrolling sometimes
+ int lastSelectionStart = 0;
+ int lastSelectionEnd = 0;
+ int lineCount = 0;
+ int firstBlockInViewport = -1; // can be wrong after scrolling sometimes
int firstBlockPastViewport = -1; // only for the autotest
+ int renderedBlockCount = -1; // only for the autotest
QRectF renderedRegion;
enum UpdateType {
@@ -173,17 +189,17 @@ public:
UpdateAll
};
- QQuickTextEdit::HAlignment hAlign;
- QQuickTextEdit::VAlignment vAlign;
- QQuickTextEdit::TextFormat format;
- QQuickTextEdit::WrapMode wrapMode;
- QQuickTextEdit::RenderType renderType;
- Qt::LayoutDirection contentDirection;
- QQuickTextEdit::SelectionMode mouseSelectionMode;
+ QQuickTextEdit::HAlignment hAlign = QQuickTextEdit::AlignLeft;
+ QQuickTextEdit::VAlignment vAlign = QQuickTextEdit::AlignTop;
+ QQuickTextEdit::TextFormat format = QQuickTextEdit::PlainText;
+ QQuickTextEdit::WrapMode wrapMode = QQuickTextEdit::NoWrap;
+ QQuickTextEdit::RenderType renderType = QQuickTextUtil::textRenderType<QQuickTextEdit>();
+ Qt::LayoutDirection contentDirection = Qt::LayoutDirectionAuto;
+ QQuickTextEdit::SelectionMode mouseSelectionMode = QQuickTextEdit::SelectCharacters;
#if QT_CONFIG(im)
- Qt::InputMethodHints inputMethodHints;
+ Qt::InputMethodHints inputMethodHints = Qt::ImhNone;
#endif
- UpdateType updateType;
+ UpdateType updateType = UpdatePaintNode;
bool dirty : 1;
bool richText : 1;
@@ -202,12 +218,14 @@ public:
bool selectByKeyboardSet:1;
bool hadSelection : 1;
bool markdownText : 1;
+ bool inResize : 1;
+ bool ownsDocument : 1;
static const int largeTextSizeThreshold;
};
#ifndef QT_NO_DEBUG_STREAM
-QDebug Q_QUICK_PRIVATE_EXPORT operator<<(QDebug debug, const QQuickTextEditPrivate::Node &);
+QDebug Q_QUICK_EXPORT operator<<(QDebug debug, const QQuickTextEditPrivate::Node &);
#endif
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 1df764f79b..c013595f45 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -13,7 +13,7 @@
#include <QtQml/qqmlinfo.h>
#include <QtGui/qevent.h>
#include <QTextBoundaryFinder>
-#include "qquicktextnode_p.h"
+#include "qsginternaltextnode_p.h"
#include <QtQuick/qsgsimplerectnode.h>
#include <QtGui/qstylehints.h>
@@ -90,7 +90,13 @@ void QQuickTextInput::componentComplete()
The text in the TextInput.
- \sa clear()
+ Note that some keyboards use a predictive function. In this case,
+ the text being composed by the input method is not part of this property.
+ The part of the text related to the predictions is underlined and stored in
+ the \l preeditText property. To get whole text displayed in the TextInput
+ use \l displayText property.
+
+ \sa clear(), displayText, preeditText
*/
QString QQuickTextInput::text() const
{
@@ -127,16 +133,23 @@ void QQuickTextInput::setText(const QString &s)
Override the default rendering type for this component.
Supported render types are:
- \list
- \li Text.QtRendering
- \li Text.NativeRendering
- \endlist
- Select Text.NativeRendering if you prefer text to look native on the target platform and do
+ \value TextInput.QtRendering Text is rendered using a scalable distance field for each glyph.
+ \value TextInput.NativeRendering Text is rendered using a platform-specific technique.
+ \value TextInput.CurveRendering Text is rendered using a curve rasterizer running directly on
+ the graphics hardware. (Introduced in Qt 6.7.0.)
+
+ Select \c TextInput.NativeRendering if you prefer text to look native on the target platform and do
not require advanced features such as transformation of the text. Using such features in
combination with the NativeRendering render type will lend poor and sometimes pixelated
results.
+ Both \c TextInput.QtRendering and \c TextInput.CurveRendering are hardware-accelerated techniques.
+ \c QtRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveRendering should be considered as an alternative in cases
+ where \c QtRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+
The default rendering type is determined by \l QQuickWindow::textRenderType().
*/
QQuickTextInput::RenderType QQuickTextInput::renderType() const
@@ -231,17 +244,16 @@ QString QQuickTextInputPrivate::realText() const
The requested weight of the font. The weight requested must be an integer
between 1 and 1000, or one of the predefined values:
- \list
- \li Font.Thin
- \li Font.Light
- \li Font.ExtraLight
- \li Font.Normal - the default
- \li Font.Medium
- \li Font.DemiBold
- \li Font.Bold
- \li Font.ExtraBold
- \li Font.Black
- \endlist
+
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400 (default)
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
\qml
TextInput { text: "Hello"; font.weight: Font.DemiBold }
@@ -305,13 +317,12 @@ QString QQuickTextInputPrivate::realText() const
Sets the capitalization for the text.
- \list
- \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
- \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
- \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
- \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
- \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
- \endlist
+ \value Font.MixedCase the normal case: no capitalization change is applied
+ \value Font.AllUppercase alters the text to be rendered in all uppercase type
+ \value Font.AllLowercase alters the text to be rendered in all lowercase type
+ \value Font.SmallCaps alters the text to be rendered in small-caps type
+ \value Font.Capitalize alters the text to be rendered with the first character of
+ each word as an uppercase character
\qml
TextInput { text: "Hello"; font.capitalization: Font.AllLowercase }
@@ -328,23 +339,21 @@ QString QQuickTextInputPrivate::realText() const
\note This property only has an effect when used together with render type TextInput.NativeRendering.
- \list
- \value Font.PreferDefaultHinting - Use the default hinting level for the target platform.
- \value Font.PreferNoHinting - If possible, render text without hinting the outlines
+ \value Font.PreferDefaultHinting Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting If possible, render text without hinting the outlines
of the glyphs. The text layout will be typographically accurate, using the same metrics
as are used e.g. when printing.
- \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting,
but align glyphs to the pixel grid in the vertical direction. The text will appear
crisper on displays where the density is too low to give an accurate rendering
of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
layout will be scalable to higher density devices (such as printers) without impacting
details such as line breaks.
- \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ \value Font.PreferFullHinting If possible, render text with hinting in both horizontal and
vertical directions. The text will be altered to optimize legibility on the target
device, but since the metrics will depend on the target size of the text, the positions
of glyphs, line breaks, and other typographical detail will not scale, meaning that a
text layout may look different on devices with different pixel densities.
- \endlist
\qml
TextInput { text: "Hello"; renderType: TextInput.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
@@ -380,6 +389,27 @@ QString QQuickTextInputPrivate::realText() const
TextInput { text: "Some text"; font.preferShaping: false }
\endqml
*/
+
+/*!
+ \qmlproperty object QtQuick::TextInput::font.variableAxes
+ \since 6.7
+
+ \include qquicktext.cpp qml-font-variable-axes
+*/
+
+/*!
+ \qmlproperty object QtQuick::TextInput::font.features
+ \since 6.6
+
+ \include qquicktext.cpp qml-font-features
+*/
+
+/*!
+ \qmlproperty object QtQuick::TextInput::font.contextFontMerging
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-context-font-merging
+*/
QFont QQuickTextInput::font() const
{
Q_D(const QQuickTextInput);
@@ -532,9 +562,8 @@ QQuickTextInput::HAlignment QQuickTextInput::hAlign() const
void QQuickTextInput::setHAlign(HAlignment align)
{
Q_D(QQuickTextInput);
- bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
- d->hAlignImplicit = false;
- if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
+
+ if (d->setHAlign(align, true) && isComponentComplete()) {
d->updateLayout();
updateCursorRectangle();
}
@@ -569,17 +598,34 @@ QQuickTextInput::HAlignment QQuickTextInput::effectiveHAlign() const
return effectiveAlignment;
}
-bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment alignment, bool forceAlign)
+bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment align, bool forceAlign)
{
Q_Q(QQuickTextInput);
- if ((hAlign != alignment || forceAlign) && alignment <= QQuickTextInput::AlignHCenter) { // justify not supported
- QQuickTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
- hAlign = alignment;
- emit q->horizontalAlignmentChanged(alignment);
- if (oldEffectiveHAlign != q->effectiveHAlign())
- emit q->effectiveHorizontalAlignmentChanged();
+ if (align > QQuickTextInput::AlignHCenter)
+ return false; // justify is not supported
+
+ if (hAlign == align && !forceAlign)
+ return false;
+
+ const bool wasImplicit = hAlignImplicit;
+ const auto oldEffectiveHAlign = q->effectiveHAlign();
+
+ hAlignImplicit = !forceAlign;
+ if (hAlign != align) {
+ hAlign = align;
+ emit q->horizontalAlignmentChanged(align);
+ }
+
+ if (q->effectiveHAlign() != oldEffectiveHAlign) {
+ emit q->effectiveHorizontalAlignmentChanged();
return true;
}
+
+ if (forceAlign && wasImplicit) {
+ // QTBUG-120052 - when horizontal text alignment is set explicitly,
+ // we need notify any other controls that may depend on it, like QQuickPlaceholderText
+ emit q->effectiveHorizontalAlignmentChanged();
+ }
return false;
}
@@ -623,16 +669,19 @@ Qt::LayoutDirection QQuickTextInputPrivate::layoutDirection() const
bool QQuickTextInputPrivate::determineHorizontalAlignment()
{
- if (hAlignImplicit) {
- // if no explicit alignment has been set, follow the natural layout direction of the text
- Qt::LayoutDirection direction = textDirection();
+ if (!hAlignImplicit)
+ return false;
+
+ // if no explicit alignment has been set, follow the natural layout direction of the text
+ Qt::LayoutDirection direction = textDirection();
#if QT_CONFIG(im)
- if (direction == Qt::LayoutDirectionAuto)
- direction = QGuiApplication::inputMethod()->inputDirection();
+ if (direction == Qt::LayoutDirectionAuto)
+ direction = QGuiApplication::inputMethod()->inputDirection();
#endif
- return setHAlign(direction == Qt::RightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
- }
- return false;
+
+ const auto implicitHAlign = direction == Qt::RightToLeft ?
+ QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft;
+ return setHAlign(implicitHAlign);
}
QQuickTextInput::VAlignment QQuickTextInput::vAlign() const
@@ -660,12 +709,17 @@ void QQuickTextInput::setVAlign(QQuickTextInput::VAlignment alignment)
Set this property to wrap the text to the TextInput item's width.
The text will only wrap if an explicit width has been set.
- \list
- \li TextInput.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
- \li TextInput.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
- \li TextInput.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
- \li TextInput.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
- \endlist
+ \value TextInput.NoWrap
+ (default) no wrapping will be performed. If the text contains
+ insufficient newlines, then \l contentWidth will exceed a set width.
+ \value TextInput.WordWrap
+ wrapping is done on word boundaries only. If a word is too long,
+ \l contentWidth will exceed a set width.
+ \value TextInput.WrapAnywhere
+ wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \value TextInput.Wrap
+ if possible, wrapping occurs at a word boundary; otherwise it will occur
+ at the appropriate point on the line, even in the middle of a word.
The default is TextInput.NoWrap. If you set a width, consider using TextInput.Wrap.
*/
@@ -1199,14 +1253,13 @@ Qt::InputMethodHints QQuickTextInputPrivate::effectiveInputMethodHints() const
\qmlproperty enumeration QtQuick::TextInput::echoMode
Specifies how the text should be displayed in the TextInput.
- \list
- \li TextInput.Normal - Displays the text as it is. (Default)
- \li TextInput.Password - Displays platform-dependent password mask
- characters instead of the actual characters.
- \li TextInput.NoEcho - Displays nothing.
- \li TextInput.PasswordEchoOnEdit - Displays characters as they are entered
- while editing, otherwise identical to \c TextInput.Password.
- \endlist
+
+ \value TextInput.Normal Displays the text as it is. (Default)
+ \value TextInput.Password Displays platform-dependent password mask
+ characters instead of the actual characters.
+ \value TextInput.NoEcho Displays nothing.
+ \value TextInput.PasswordEchoOnEdit Displays characters as they are entered
+ while editing, otherwise identical to \c TextInput.Password.
*/
QQuickTextInput::EchoMode QQuickTextInput::echoMode() const
{
@@ -1247,40 +1300,31 @@ void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
Flags that alter behaviour are:
- \list
- \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
- This is automatically set when setting echoMode to \c TextInput.Password.
- \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
- in any persistent storage like predictive user dictionary.
- \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
- when a sentence ends.
- \li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
- \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
- \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
- \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
-
- \li Qt.ImhDate - The text editor functions as a date field.
- \li Qt.ImhTime - The text editor functions as a time field.
- \li Qt.ImhMultiLine - The text editor doesn't close software input keyboard when Return or Enter key is pressed (since QtQuick 2.4).
- \endlist
+ \value Qt.ImhHiddenText Characters should be hidden, as is typically used when entering passwords.
+ \value Qt.ImhSensitiveData Typed text should not be stored by the active input method
+ in any persistent storage like predictive user dictionary.
+ \value Qt.ImhNoAutoUppercase The input method should not try to automatically switch to
+ upper case when a sentence ends.
+ \value Qt.ImhPreferNumbers Numbers are preferred (but not required).
+ \value Qt.ImhPreferUppercase Upper case letters are preferred (but not required).
+ \value Qt.ImhPreferLowercase Lower case letters are preferred (but not required).
+ \value Qt.ImhNoPredictiveText Do not use predictive text (i.e. dictionary lookup) while typing.
+ \value Qt.ImhDate The text editor functions as a date field.
+ \value Qt.ImhTime The text editor functions as a time field.
Flags that restrict input (exclusive flags) are:
- \list
- \li Qt.ImhDigitsOnly - Only digits are allowed.
- \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
- \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
- \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
- \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
- \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
- \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
- \endlist
+ \value Qt.ImhDigitsOnly Only digits are allowed.
+ \value Qt.ImhFormattedNumbersOnly Only number input is allowed. This includes decimal point and minus sign.
+ \value Qt.ImhUppercaseOnly Only upper case letter input is allowed.
+ \value Qt.ImhLowercaseOnly Only lower case letter input is allowed.
+ \value Qt.ImhDialableCharactersOnly Only characters suitable for phone dialing are allowed.
+ \value Qt.ImhEmailCharactersOnly Only characters suitable for email addresses are allowed.
+ \value Qt.ImhUrlCharactersOnly Only characters suitable for URLs are allowed.
Masks:
- \list
- \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
- \endlist
+ \value Qt.ImhExclusiveInputMask This mask yields nonzero if any of the exclusive flags are used.
*/
Qt::InputMethodHints QQuickTextInput::inputMethodHints() const
@@ -1397,7 +1441,7 @@ QRectF QQuickTextInput::positionToRectangle(int pos) const
Returns the position before the character that is nearest x.
*/
-void QQuickTextInput::positionAt(QQmlV4Function *args) const
+void QQuickTextInput::positionAt(QQmlV4FunctionPtr args) const
{
Q_D(const QQuickTextInput);
@@ -1571,7 +1615,11 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
}
}
- if (isMouse) {
+ if (isMouse
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ || d->selectByTouchDrag
+#endif
+ ) {
bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
int cursor = d->positionAt(event->position());
d->moveCursor(cursor, mark);
@@ -1640,9 +1688,11 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
}
}
#endif
+
// On a touchscreen or with a stylus, set cursor position and focus on release, not on press;
// if Flickable steals the grab in the meantime, the cursor won't move.
- if (!isMouse)
+ // Check d->hasSelectedText() to keep touch-and-hold word selection working.
+ if (!isMouse && !d->hasSelectedText())
d->moveCursor(d->positionAt(event->position()), false);
if (d->focusOnPress && qGuiApp->styleHints()->setFocusOnTouchRelease())
@@ -1751,6 +1801,26 @@ void QQuickTextInput::geometryChange(const QRectF &newGeometry,
QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
}
+void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QQuickTextInput);
+ Q_UNUSED(value);
+ switch (change) {
+ case ItemDevicePixelRatioHasChanged:
+ if (d->renderType == NativeRendering) {
+ // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
+ // Text layout code respects the current device pixel ratio automatically, we only need
+ // to rerun layout after the ratio changed.
+ d->updateLayout();
+ }
+ break;
+
+ default:
+ break;
+ }
+ QQuickImplicitSizeItem::itemChange(change, value);
+}
+
void QQuickTextInputPrivate::ensureVisible(int position, int preeditCursor, int preeditLength)
{
Q_Q(QQuickTextInput);
@@ -1911,9 +1981,9 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
d->updateType = QQuickTextInputPrivate::UpdateNone;
- QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
+ QSGInternalTextNode *node = static_cast<QSGInternalTextNode *>(oldNode);
if (node == nullptr)
- node = new QQuickTextNode(this);
+ node = d->sceneGraphContext()->createInternalTextNode(d->sceneGraphRenderContext());
d->textNode = node;
const bool showCursor = !isReadOnly() && d->cursorItem == nullptr && d->cursorVisible && d->m_blinkStatus;
@@ -1924,9 +1994,19 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
else
node->clearCursor();
} else {
- node->setUseNativeRenderer(d->renderType == NativeRendering);
- node->deleteContent();
+ node->setRenderType(QSGTextNode::RenderType(d->renderType));
+ node->clear();
node->setMatrix(QMatrix4x4());
+ node->setTextStyle(QSGInternalTextNode::Normal);
+ node->setColor(d->color);
+ node->setSelectionTextColor(d->selectedTextColor);
+ node->setSelectionColor(d->selectionColor);
+ node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+
+ if (flags().testFlag(ItemObservesViewport))
+ node->setViewport(clipRect());
+ else
+ node->setViewport(QRectF{});
QPointF offset(leftPadding(), topPadding());
if (d->autoScroll && d->m_textLayout.lineCount() > 0) {
@@ -1942,16 +2022,14 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
|| !d->m_textLayout.preeditAreaText().isEmpty()
#endif
) {
- node->addTextLayout(offset, &d->m_textLayout, d->color,
- QQuickText::Normal, QColor(), QColor(),
- d->selectionColor, d->selectedTextColor,
+ node->addTextLayout(offset, &d->m_textLayout,
d->selectionStart(),
d->selectionEnd() - 1); // selectionEnd() returns first char after
- // selection
+ // selection
}
if (showCursor)
- node->setCursor(cursorRectangle(), d->color);
+ node->setCursor(cursorRectangle(), d->color);
d->textLayoutDirty = false;
}
@@ -1974,8 +2052,14 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
|| d->extra->enterKeyAttached->type() == Qt::EnterKeyDefault) {
QQuickItem *next = const_cast<QQuickTextInput*>(this)->nextItemInFocusChain();
- while (next && next != this && !next->activeFocusOnTab())
+ QQuickItem *originalNext = next;
+ while (next && next != this && !next->activeFocusOnTab()) {
next = next->nextItemInFocusChain();
+ if (next == originalNext) {
+ // There seems to be no suitable element in the focus chain
+ next = nullptr;
+ }
+ }
if (next) {
const auto nextYPos = next->mapToGlobal(QPoint(0, 0)).y();
const auto currentYPos = this->mapToGlobal(QPoint(0, 0)).y();
@@ -2411,7 +2495,10 @@ QString QQuickTextInput::displayText() const
This property contains partial text input from an input method.
- \sa displayText
+ To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
+ flag in inputMethodHints.
+
+ \sa displayText, inputMethodHints
*/
QString QQuickTextInput::preeditText() const
{
@@ -2457,10 +2544,8 @@ void QQuickTextInput::setSelectByMouse(bool on)
Specifies how text should be selected using a mouse.
- \list
- \li TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
- \li TextInput.SelectWords - The selection is updated with whole words.
- \endlist
+ \value TextInput.SelectCharacters (default) The selection is updated with individual characters.
+ \value TextInput.SelectWords The selection is updated with whole words.
This property only applies when \l selectByMouse is true.
*/
@@ -2515,7 +2600,7 @@ bool QQuickTextInput::canPaste() const
Q_D(const QQuickTextInput);
if (!d->canPasteValid) {
if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
- const_cast<QQuickTextInputPrivate *>(d)->canPaste = !d->m_readOnly && mimeData->hasText();
+ const_cast<QQuickTextInputPrivate *>(d)->canPaste = !d->m_readOnly && mimeData->hasText() && !mimeData->text().isEmpty();
const_cast<QQuickTextInputPrivate *>(d)->canPasteValid = true;
}
return d->canPaste;
@@ -2600,13 +2685,12 @@ void QQuickTextInput::moveCursorSelection(int position)
The selection mode specifies whether the selection is updated on a per character or a per word
basis. If not specified the selection mode will default to \c {TextInput.SelectCharacters}.
- \list
- \li TextInput.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
- the previous cursor position) to the specified position.
- \li TextInput.SelectWords - Sets the selectionStart and selectionEnd to include all
- words between the specified position and the previous cursor position. Words partially in the
- range are included.
- \endlist
+ \value TextInput.SelectCharacters
+ Sets either the selectionStart or selectionEnd (whichever was at the previous cursor position)
+ to the specified position.
+ \value TextInput.SelectWords
+ Sets the selectionStart and selectionEnd to include all words between the specified position
+ and the previous cursor position. Words partially in the range are included.
For example, take this sequence of calls:
@@ -2796,6 +2880,7 @@ void QQuickTextInputPrivate::init()
}
m_inputControl = new QInputControl(QInputControl::LineEdit, q);
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
void QQuickTextInputPrivate::cancelInput()
@@ -3526,6 +3611,7 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
}
selectionChange = true;
} else {
+ selectionChange = m_selstart != m_selend;
m_selstart = m_selend = 0;
}
cursorPositionChanged = true;
@@ -3604,7 +3690,7 @@ void QQuickTextInputPrivate::selectWordAtPos(int cursor)
moveCursor(c, false);
// ## text layout should support end of words.
int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
- while (end > cursor && m_text[end-1].isSpace())
+ while (end > cursor && m_text.at(end - 1).isSpace())
--end;
moveCursor(end, true);
}
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 433eed4d78..5212e6117e 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
class QQuickTextInputPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem, public QQuickTextInterface
+class Q_QUICK_EXPORT QQuickTextInput : public QQuickImplicitSizeItem, public QQuickTextInterface
{
Q_OBJECT
Q_INTERFACES(QQuickTextInterface)
@@ -138,12 +138,13 @@ public:
Q_ENUM(CursorPosition)
enum RenderType { QtRendering,
- NativeRendering
+ NativeRendering,
+ CurveRendering
};
Q_ENUM(RenderType)
//Auxilliary functions needed to control the TextInput from QML
- Q_INVOKABLE void positionAt(QQmlV4Function *args) const;
+ Q_INVOKABLE void positionAt(QQmlV4FunctionPtr args) const;
Q_INVOKABLE QRectF positionToRectangle(int pos) const;
Q_INVOKABLE void moveCursorSelection(int pos);
Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode);
@@ -349,6 +350,7 @@ protected:
#endif
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void itemChange(ItemChange change, const ItemChangeData &value) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
@@ -415,6 +417,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextInput)
-
#endif // QQUICKTEXTINPUT_P_H
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index 01d90f6b10..6bc83d318b 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -36,10 +36,10 @@
QT_BEGIN_NAMESPACE
-class QQuickTextNode;
+class QSGInternalTextNode;
class QInputControl;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickTextInput)
@@ -199,7 +199,7 @@ public:
QFont sourceFont;
QQuickItem *cursorItem;
- QQuickTextNode *textNode;
+ QSGInternalTextNode *textNode;
std::unique_ptr<MaskInputData[]> m_maskData;
QInputControl *m_inputControl;
diff --git a/src/quick/items/qquicktextinterface_p.h b/src/quick/items/qquicktextinterface_p.h
index 4210c6c0ad..3a8a31be10 100644
--- a/src/quick/items/qquicktextinterface_p.h
+++ b/src/quick/items/qquicktextinterface_p.h
@@ -17,7 +17,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickTextInterface
+class Q_QUICK_EXPORT QQuickTextInterface
{
public:
virtual void invalidate() = 0;
diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp
deleted file mode 100644
index 171cbdee8b..0000000000
--- a/src/quick/items/qquicktextnode.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qquicktextnode_p.h"
-
-#include "qquicktextnodeengine_p.h"
-
-#include <private/qsgadaptationlayer_p.h>
-#include <private/qsgdistancefieldglyphnode_p.h>
-#include <private/qquickclipnode_p.h>
-#include <private/qquickitem_p.h>
-#include <QtQuick/private/qsgcontext_p.h>
-
-#include <QtCore/qpoint.h>
-#include <qtextdocument.h>
-#include <qtextlayout.h>
-#include <qabstracttextdocumentlayout.h>
-#include <qxmlstream.h>
-#include <private/qquickstyledtext_p.h>
-#include <private/qquicktext_p_p.h>
-#include <private/qfont_p.h>
-#include <private/qfontengine_p.h>
-
-#include <private/qtextdocumentlayout_p.h>
-#include <qhash.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace {
-
- class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout
- {
- public:
- inline QTextCharFormat formatAccessor(int pos)
- {
- return format(pos);
- }
- };
-
-}
-
-Q_DECLARE_LOGGING_CATEGORY(lcVP)
-
-/*!
- Creates an empty QQuickTextNode
-*/
-QQuickTextNode::QQuickTextNode(QQuickItem *ownerElement)
- : m_cursorNode(nullptr), m_ownerElement(ownerElement), m_useNativeRenderer(false), m_renderTypeQuality(-1)
-{
-#ifdef QSG_RUNTIME_DESCRIPTION
- qsgnode_set_description(this, QLatin1String("text"));
-#endif
-}
-
-QQuickTextNode::~QQuickTextNode()
-{
- qDeleteAll(m_textures);
-}
-
-QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
- QQuickText::TextStyle style, const QColor &styleColor,
- QSGNode *parentNode)
-{
- QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- QRawFont font = glyphs.rawFont();
- bool preferNativeGlyphNode = m_useNativeRenderer;
- if (!preferNativeGlyphNode) {
- QRawFontPrivate *fontPriv = QRawFontPrivate::get(font);
- if (fontPriv->fontEngine->hasUnreliableGlyphOutline()) {
- preferNativeGlyphNode = true;
- } else {
- QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
- preferNativeGlyphNode = !fe->isSmoothlyScalable;
- }
- }
-
- QSGGlyphNode *node = sg->sceneGraphContext()->createGlyphNode(sg, preferNativeGlyphNode, m_renderTypeQuality);
-
- node->setOwnerElement(m_ownerElement);
- node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
- node->setStyle(style);
- node->setStyleColor(styleColor);
- node->setColor(color);
- node->update();
-
- /* We flag the geometry as static, but we never call markVertexDataDirty
- or markIndexDataDirty on them. This is because all text nodes are
- discarded when a change occurs. If we start appending/removing from
- existing geometry, then we also need to start marking the geometry as
- dirty.
- */
- node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
- node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
-
- if (parentNode == nullptr)
- parentNode = this;
- parentNode->appendChildNode(node);
-
- if (style == QQuickText::Outline && color.alpha() > 0 && styleColor != color) {
- QSGGlyphNode *fillNode = sg->sceneGraphContext()->createGlyphNode(sg, preferNativeGlyphNode, m_renderTypeQuality);
- fillNode->setOwnerElement(m_ownerElement);
- fillNode->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
- fillNode->setStyle(QQuickText::Normal);
- fillNode->setPreferredAntialiasingMode(QSGGlyphNode::GrayAntialiasing);
- fillNode->setColor(color);
- fillNode->update();
-
- fillNode->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
- fillNode->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
-
- parentNode->appendChildNode(fillNode);
- fillNode->setRenderOrder(node->renderOrder() + 1);
- }
-
- return node;
-}
-
-void QQuickTextNode::setCursor(const QRectF &rect, const QColor &color)
-{
- if (m_cursorNode != nullptr)
- delete m_cursorNode;
-
- QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- m_cursorNode = sg->sceneGraphContext()->createInternalRectangleNode(rect, color);
- appendChildNode(m_cursorNode);
-}
-
-void QQuickTextNode::clearCursor()
-{
- if (m_cursorNode)
- removeChildNode(m_cursorNode);
- delete m_cursorNode;
- m_cursorNode = nullptr;
-}
-
-void QQuickTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
-{
- QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- appendChildNode(sg->sceneGraphContext()->createInternalRectangleNode(rect, color));
-}
-
-
-void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
-{
- QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- QSGInternalImageNode *node = sg->sceneGraphContext()->createInternalImageNode(sg);
- QSGTexture *texture = sg->createTexture(image);
- if (m_ownerElement->smooth())
- texture->setFiltering(QSGTexture::Linear);
- m_textures.append(texture);
- node->setTargetRect(rect);
- node->setInnerTargetRect(rect);
- node->setTexture(texture);
- if (m_ownerElement->smooth())
- node->setFiltering(QSGTexture::Linear);
- appendChildNode(node);
- node->update();
-}
-
-void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *textDocument,
- const QColor &textColor,
- QQuickText::TextStyle style, const QColor &styleColor,
- const QColor &anchorColor,
- const QColor &selectionColor, const QColor &selectedTextColor,
- int selectionStart, int selectionEnd)
-{
- QQuickTextNodeEngine engine;
- engine.setTextColor(textColor);
- engine.setSelectedTextColor(selectedTextColor);
- engine.setSelectionColor(selectionColor);
- engine.setAnchorColor(anchorColor);
- engine.setPosition(position);
-
- QList<QTextFrame *> frames;
- frames.append(textDocument->rootFrame());
- while (!frames.isEmpty()) {
- QTextFrame *textFrame = frames.takeFirst();
- frames.append(textFrame->childFrames());
-
- engine.addFrameDecorations(textDocument, textFrame);
-
- if (textFrame->firstPosition() > textFrame->lastPosition()
- && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
- const int pos = textFrame->firstPosition() - 1;
- ProtectedLayoutAccessor *a = static_cast<ProtectedLayoutAccessor *>(textDocument->documentLayout());
- QTextCharFormat format = a->formatAccessor(pos);
- QRectF rect = a->frameBoundingRect(textFrame);
-
- QTextBlock block = textFrame->firstCursorPosition().block();
- engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
- engine.addTextObject(block, rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument,
- pos, textFrame->frameFormat().position());
- } else {
- QTextFrame::iterator it = textFrame->begin();
-
- while (!it.atEnd()) {
- Q_ASSERT(!engine.currentLine().isValid());
-
- QTextBlock block = it.currentBlock();
- engine.addTextBlock(textDocument, block, position, textColor, anchorColor, selectionStart, selectionEnd,
- (textDocument->characterCount() > QQuickTextPrivate::largeTextSizeThreshold ?
- m_ownerElement->clipRect() : QRectF()));
- ++it;
- }
- }
- }
-
- engine.addToSceneGraph(this, style, styleColor);
-}
-
-void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color,
- QQuickText::TextStyle style, const QColor &styleColor,
- const QColor &anchorColor,
- const QColor &selectionColor, const QColor &selectedTextColor,
- int selectionStart, int selectionEnd,
- int lineStart, int lineCount)
-{
- QQuickTextNodeEngine engine;
- engine.setTextColor(color);
- engine.setSelectedTextColor(selectedTextColor);
- engine.setSelectionColor(selectionColor);
- engine.setAnchorColor(anchorColor);
- engine.setPosition(position);
-
-#if QT_CONFIG(im)
- int preeditLength = textLayout->preeditAreaText().size();
- int preeditPosition = textLayout->preeditAreaPosition();
-#endif
-
- QVarLengthArray<QTextLayout::FormatRange> colorChanges;
- engine.mergeFormats(textLayout, &colorChanges);
-
- // If there's a lot of text, insert only the range of lines that can possibly be visible within the viewport.
- QRectF viewport;
- if (m_ownerElement->flags().testFlag(QQuickItem::ItemObservesViewport)) {
- viewport = m_ownerElement->clipRect();
- qCDebug(lcVP) << "text viewport" << viewport;
- }
- lineCount = lineCount >= 0
- ? qMin(lineStart + lineCount, textLayout->lineCount())
- : textLayout->lineCount();
-
- bool inViewport = false;
- for (int i=lineStart; i<lineCount; ++i) {
- QTextLine line = textLayout->lineAt(i);
-
- int start = line.textStart();
- int length = line.textLength();
- int end = start + length;
-
-#if QT_CONFIG(im)
- if (preeditPosition >= 0
- && preeditPosition >= start
- && preeditPosition < end) {
- end += preeditLength;
- }
-#endif
- if (viewport.isNull() || (line.y() + line.height() > viewport.top() && line.y() < viewport.bottom())) {
- if (!inViewport && !viewport.isNull()) {
- m_firstLineInViewport = i;
- qCDebug(lcVP) << "first line in viewport" << i << "@" << line.y();
- }
- inViewport = true;
- engine.setCurrentLine(line);
- engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd);
- } else if (inViewport) {
- Q_ASSERT(!viewport.isNull());
- m_firstLinePastViewport = i;
- qCDebug(lcVP) << "first omitted line past bottom of viewport" << i << "@" << line.y();
- break; // went past the bottom of the viewport, so we're done
- }
- }
-
- engine.addToSceneGraph(this, style, styleColor);
-}
-
-void QQuickTextNode::deleteContent()
-{
- while (firstChild() != nullptr)
- delete firstChild();
- m_cursorNode = nullptr;
- qDeleteAll(m_textures);
- m_textures.clear();
-}
-
-QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h
deleted file mode 100644
index 0538336311..0000000000
--- a/src/quick/items/qquicktextnode_p.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QQUICKTEXTNODE_P_H
-#define QQUICKTEXTNODE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtQuick/qsgnode.h>
-#include "qquicktext_p.h"
-#include <qglyphrun.h>
-
-#include <QtGui/qcolor.h>
-#include <QtGui/qtextlayout.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qscopedpointer.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSGGlyphNode;
-class QTextBlock;
-class QColor;
-class QTextDocument;
-class QSGContext;
-class QRawFont;
-class QSGInternalRectangleNode;
-class QSGClipNode;
-class QSGTexture;
-
-class QQuickTextNodeEngine;
-
-class Q_QUICK_PRIVATE_EXPORT QQuickTextNode : public QSGTransformNode
-{
-public:
- QQuickTextNode(QQuickItem *ownerElement);
- ~QQuickTextNode();
-
- static bool isComplexRichText(QTextDocument *);
-
- void deleteContent();
- void addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color = QColor(),
- QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
- const QColor &anchorColor = QColor(),
- const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(),
- int selectionStart = -1, int selectionEnd = -1,
- int lineStart = 0, int lineCount = -1);
- void addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color = QColor(),
- QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
- const QColor &anchorColor = QColor(),
- const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(),
- int selectionStart = -1, int selectionEnd = -1);
-
- void setCursor(const QRectF &rect, const QColor &color);
- void clearCursor();
- QSGInternalRectangleNode *cursorNode() const { return m_cursorNode; }
-
- QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
- QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
- QSGNode *parentNode = 0);
- void addImage(const QRectF &rect, const QImage &image);
- void addRectangleNode(const QRectF &rect, const QColor &color);
-
- bool useNativeRenderer() const { return m_useNativeRenderer; }
- void setUseNativeRenderer(bool on) { m_useNativeRenderer = on; }
-
- void setRenderTypeQuality(int renderTypeQuality) { m_renderTypeQuality = renderTypeQuality; }
- int renderTypeQuality() const { return m_renderTypeQuality; }
-
- QPair<int, int> renderedLineRange() const { return { m_firstLineInViewport, m_firstLinePastViewport }; }
-
-private:
- QSGInternalRectangleNode *m_cursorNode;
- QList<QSGTexture *> m_textures;
- QQuickItem *m_ownerElement;
- bool m_useNativeRenderer;
- int m_renderTypeQuality;
- int m_firstLineInViewport = -1;
- int m_firstLinePastViewport = -1;
-
- friend class QQuickTextEdit;
- friend class QQuickTextEditPrivate;
-};
-
-QT_END_NAMESPACE
-
-#endif // QQUICKTEXTNODE_P_H
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index d2256128cf..c486fece40 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -13,7 +13,6 @@
#include <QtGui/qtextlist.h>
#include <private/qquicktext_p.h>
-#include <private/qquicktextdocument_p.h>
#include <private/qtextdocumentlayout_p.h>
#include <private/qtextimagehandler_p.h>
#include <private/qrawfont_p.h>
@@ -429,15 +428,8 @@ void QQuickTextNodeEngine::addTextObject(const QTextBlock &block, const QPointF
if (format.objectType() == QTextFormat::ImageObject) {
QTextImageFormat imageFormat = format.toImageFormat();
- if (QQuickTextDocumentWithImageResources *imageDoc = qobject_cast<QQuickTextDocumentWithImageResources *>(textDocument)) {
- image = imageDoc->image(imageFormat);
-
- if (image.isNull())
- return;
- } else {
- QTextImageHandler *imageHandler = static_cast<QTextImageHandler *>(handler);
- image = imageHandler->image(textDocument, imageFormat);
- }
+ QTextImageHandler *imageHandler = static_cast<QTextImageHandler *>(handler);
+ image = imageHandler->image(textDocument, imageFormat);
}
if (image.isNull()) {
@@ -668,10 +660,14 @@ void QQuickTextNodeEngine::addFrameDecorations(QTextDocument *document, QTextFra
if (borderStyle == QTextFrameFormat::BorderStyle_None)
return;
- addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(),
- -frameFormat.rightMargin() - borderWidth,
- -frameFormat.bottomMargin() - borderWidth),
- borderWidth, borderStyle, borderBrush);
+ const auto collapsed = table->format().borderCollapse();
+
+ if (!collapsed) {
+ addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(),
+ -frameFormat.rightMargin() - borderWidth,
+ -frameFormat.bottomMargin() - borderWidth),
+ borderWidth, borderStyle, borderBrush);
+ }
if (table != nullptr) {
int rows = table->rows();
int columns = table->columns();
@@ -681,7 +677,7 @@ void QQuickTextNodeEngine::addFrameDecorations(QTextDocument *document, QTextFra
QTextTableCell cell = table->cellAt(row, column);
QRectF cellRect = documentLayout->tableCellBoundingRect(table, cell);
- addBorder(cellRect.adjusted(-borderWidth, -borderWidth, 0, 0), borderWidth,
+ addBorder(cellRect.adjusted(-borderWidth, -borderWidth, collapsed ? -borderWidth : 0, collapsed ? -borderWidth : 0), borderWidth,
borderStyle, borderBrush);
}
}
@@ -702,6 +698,9 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
BinaryTreeNode *node = m_processedNodes.data() + i;
if (node->image.isNull()) {
+ if (node->glyphRun.isEmpty())
+ continue;
+
BinaryTreeNodeKey key(node);
QList<BinaryTreeNode *> &nodes = map[key];
@@ -756,7 +755,7 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
}
}
-void QQuickTextNodeEngine::addToSceneGraph(QQuickTextNode *parentNode,
+void QQuickTextNodeEngine::addToSceneGraph(QSGInternalTextNode *parentNode,
QQuickText::TextStyle style,
const QColor &styleColor)
{
@@ -801,7 +800,7 @@ void QQuickTextNodeEngine::addToSceneGraph(QQuickTextNode *parentNode,
? m_selectedTextColor
: textDecoration.color;
- parentNode->addRectangleNode(textDecoration.rect, color);
+ parentNode->addDecorationNode(textDecoration.rect, color);
}
// Finally add the selected text on top of everything
@@ -1040,6 +1039,12 @@ void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QText
line.setPosition(QPointF(0, 0));
layout.endLayout();
+ // set the color for the bullets, instead of using the previous QTextBlock's color.
+ if (charFormat.foreground().style() == Qt::NoBrush)
+ setTextColor(textColor);
+ else
+ setTextColor(charFormat.foreground().color());
+
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
for (int i=0; i<glyphRuns.size(); ++i)
addUnselectedGlyphs(glyphRuns.at(i));
@@ -1068,7 +1073,7 @@ void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QText
else
setPosition(blockBoundingRect.topLeft());
- if (text.contains(QChar::ObjectReplacementCharacter)) {
+ if (text.contains(QChar::ObjectReplacementCharacter) && charFormat.objectType() != QTextFormat::NoObject) {
QTextFrame *frame = qobject_cast<QTextFrame *>(textDocument->objectForFormat(charFormat));
if (!frame || frame->frameFormat().position() == QTextFrameFormat::InFlow) {
int blockRelativePosition = textPos - block.position();
diff --git a/src/quick/items/qquicktextnodeengine_p.h b/src/quick/items/qquicktextnodeengine_p.h
index 1fbbbc2d93..1ed98ce208 100644
--- a/src/quick/items/qquicktextnodeengine_p.h
+++ b/src/quick/items/qquicktextnodeengine_p.h
@@ -9,7 +9,7 @@
#include <QtGui/qtextdocument.h>
#include <QtGui/qtextlayout.h>
#include "qquickclipnode_p.h"
-#include "qquicktextnode_p.h"
+#include "qsginternaltextnode_p.h"
#ifndef QQUICKTEXTNODEENGINE_P_H
#define QQUICKTEXTNODEENGINE_P_H
@@ -160,7 +160,7 @@ public:
void mergeProcessedNodes(QList<BinaryTreeNode *> *regularNodes,
QList<BinaryTreeNode *> *imageNodes);
- void addToSceneGraph(QQuickTextNode *parent,
+ void addToSceneGraph(QSGInternalTextNode *parent,
QQuickText::TextStyle style = QQuickText::Normal,
const QColor &styleColor = QColor());
@@ -191,7 +191,6 @@ public:
-
private:
struct TextDecoration
{
@@ -234,7 +233,7 @@ private:
bool m_hasSelection : 1;
bool m_hasContents : 1;
- friend class QQuickTextNode;
+ friend class QSGInternalTextNode;
};
diff --git a/src/quick/items/qquicktextutil_p.h b/src/quick/items/qquicktextutil_p.h
index 212c488c73..9f8e2ae393 100644
--- a/src/quick/items/qquicktextutil_p.h
+++ b/src/quick/items/qquicktextutil_p.h
@@ -99,6 +99,8 @@ typename T::RenderType QQuickTextUtil::textRenderType()
return T::QtRendering;
case QQuickWindow::NativeTextRendering:
return T::NativeRendering;
+ case QQuickWindow::CurveTextRendering:
+ return T::CurveRendering;
}
Q_UNREACHABLE_RETURN(T::QtRendering);
diff --git a/src/quick/items/qquicktranslate_p.h b/src/quick/items/qquicktranslate_p.h
index 3dc3310da1..496a115d3f 100644
--- a/src/quick/items/qquicktranslate_p.h
+++ b/src/quick/items/qquicktranslate_p.h
@@ -24,7 +24,7 @@
QT_BEGIN_NAMESPACE
class QQuickTranslatePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickTranslate : public QQuickTransform
+class Q_QUICK_EXPORT QQuickTranslate : public QQuickTransform
{
Q_OBJECT
@@ -53,7 +53,7 @@ private:
};
class QQuickScalePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickScale : public QQuickTransform
+class Q_QUICK_EXPORT QQuickScale : public QQuickTransform
{
Q_OBJECT
@@ -92,7 +92,7 @@ private:
};
class QQuickRotationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRotation : public QQuickTransform
+class Q_QUICK_EXPORT QQuickRotation : public QQuickTransform
{
Q_OBJECT
@@ -126,7 +126,7 @@ private:
};
class QQuickMatrix4x4Private;
-class Q_QUICK_PRIVATE_EXPORT QQuickMatrix4x4 : public QQuickTransform
+class Q_QUICK_EXPORT QQuickMatrix4x4 : public QQuickTransform
{
Q_OBJECT
@@ -151,6 +151,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTranslate)
-
#endif
diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp
index e73eb459d4..033884d58d 100644
--- a/src/quick/items/qquicktreeview.cpp
+++ b/src/quick/items/qquicktreeview.cpp
@@ -42,7 +42,7 @@
a set of properties that can be used to position and render each node
in the tree correctly.
- An example of a custom delegate is shown below:
+ An example of a custom delegate with an animating indicator is shown below:
\snippet qml/treeview/qml-customdelegate.qml 0
@@ -81,16 +81,26 @@
By default, TreeView \l {toggleExpanded()}{toggles} the expanded state
of a row when you double tap on it. Since this is in conflict with
- double tapping to edit a cell, TreeView sets \l editTriggers to
+ double tapping to edit a cell, TreeView sets \l {TableView::}{editTriggers} to
\c TableView.EditKeyPressed by default (which is different from TableView,
which uses \c {TableView.EditKeyPressed | TableView.DoubleTapped}.
- If you change \l editTriggers to also contain \c TableView.DoubleTapped,
+ If you change \l {TableView::}{editTriggers} to also contain \c TableView.DoubleTapped,
toggling the expanded state with a double tap will be disabled.
\note A TreeView only accepts a model that inherits \l QAbstractItemModel.
*/
/*!
+ \qmlproperty QModelIndex QtQuick::TreeView::rootIndex
+ \since 6.6
+
+ This property holds the model index of the root item in the tree.
+ By default, this is the same as the root index in the model, but you can
+ set it to be a child index instead, to show only a branch of the tree.
+ Set it to \c undefined to show the whole model.
+*/
+
+/*!
\qmlmethod int QtQuick::TreeView::depth(row)
Returns the depth (the number of parents up to the root) of the given \a row.
@@ -268,9 +278,6 @@ void QQuickTreeViewPrivate::setModelImpl(const QVariant &newModel)
{
Q_Q(QQuickTreeView);
- if (newModel == m_assignedModel)
- return;
-
m_assignedModel = newModel;
QVariant effectiveModel = m_assignedModel;
if (effectiveModel.userType() == qMetaTypeId<QJSValue>())
@@ -337,55 +344,39 @@ void QQuickTreeViewPrivate::updateSelection(const QRect &oldSelection, const QRe
{
Q_Q(QQuickTreeView);
- const QRect oldRect = oldSelection.normalized();
- const QRect newRect = newSelection.normalized();
-
if (oldSelection == newSelection)
return;
- // Select the rows inside newRect that doesn't overlap with oldRect
- for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) {
- if (oldRect.y() != -1 && oldRect.y() <= row && row <= oldRect.y() + oldRect.height())
- continue;
- const QModelIndex startIndex = q->modelIndex(row, newRect.x());
- const QModelIndex endIndex = q->modelIndex(row, newRect.x() + newRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ QItemSelection select;
+ QItemSelection deselect;
+
+ // Because each row can have a different parent, we need to create separate QItemSelections
+ // per row. But all the cells in a given row have the same parent, so they can be combined.
+ // As a result, the final QItemSelection can end up more fragmented compared to a selection
+ // in QQuickTableView, where all cells have the same parent. In the end, if TreeView has
+ // a lot of columns and the selection mode is "SelectCells", using the mouse to adjust
+ // a selection containing a _large_ number of columns can be slow.
+ const QRect cells = newSelection.normalized();
+ for (int row = cells.y(); row <= cells.y() + cells.height(); ++row) {
+ const QModelIndex startIndex = q->index(row, cells.x());
+ const QModelIndex endIndex = q->index(row, cells.x() + cells.width());
+ select.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
}
- if (oldRect.x() != -1) {
- // Since oldRect is valid, this update is a continuation of an already existing selection!
-
- // Select the columns inside newRect that don't overlap with oldRect
- for (int column = newRect.x(); column <= newRect.x() + newRect.width(); ++column) {
- if (oldRect.x() <= column && column <= oldRect.x() + oldRect.width())
- continue;
- for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row)
- selectionModel->select(q->modelIndex(row, column), QItemSelectionModel::Select);
- }
-
- // Unselect the rows inside oldRect that don't overlap with newRect
- for (int row = oldRect.y(); row <= oldRect.y() + oldRect.height(); ++row) {
- if (newRect.y() <= row && row <= newRect.y() + newRect.height())
- continue;
- const QModelIndex startIndex = q->modelIndex(row, oldRect.x());
- const QModelIndex endIndex = q->modelIndex(row, oldRect.x() + oldRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
- }
+ const QModelIndexList indexes = selectionModel->selection().indexes();
+ for (const QModelIndex &index : indexes) {
+ if (!select.contains(index) && !existingSelection.contains(index))
+ deselect.merge(QItemSelection(index, index), QItemSelectionModel::Select);
+ }
- // Unselect the columns inside oldRect that don't overlap with newRect
- for (int column = oldRect.x(); column <= oldRect.x() + oldRect.width(); ++column) {
- if (newRect.x() <= column && column <= newRect.x() + newRect.width())
- continue;
- // Since we're not allowed to call select/unselect on the selectionModel with
- // indices from different parents, and since indicies from different parents are
- // expected when working with trees, we need to unselect the indices in the column
- // one by one, rather than the whole column in one go. This, however, can cause a
- // lot of selection fragments in the selectionModel, which eventually can hurt
- // performance. But large selections containing a lot of columns is not normally
- // the case for a treeview, so accept this potential corner case for now.
- for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row)
- selectionModel->select(q->modelIndex(row, column), QItemSelectionModel::Deselect);
- }
+ if (selectionFlag == QItemSelectionModel::Select) {
+ selectionModel->select(deselect, QItemSelectionModel::Deselect);
+ selectionModel->select(select, QItemSelectionModel::Select);
+ } else {
+ QItemSelection oldSelection = existingSelection;
+ oldSelection.merge(select, QItemSelectionModel::Deselect);
+ selectionModel->select(oldSelection, QItemSelectionModel::Select);
+ selectionModel->select(select, QItemSelectionModel::Deselect);
}
}
@@ -403,6 +394,8 @@ QQuickTreeView::QQuickTreeView(QQuickItem *parent)
d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged,
d, &QQuickTreeViewPrivate::dataChangedCallback);
+ QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged,
+ this, &QQuickTreeView::rootIndexChanged);
auto tapHandler = new QQuickTapHandler(this);
tapHandler->setAcceptedModifiers(Qt::NoModifier);
@@ -421,6 +414,25 @@ QQuickTreeView::~QQuickTreeView()
{
}
+QModelIndex QQuickTreeView::rootIndex() const
+{
+ return d_func()->m_treeModelToTableModel.rootIndex();
+}
+
+void QQuickTreeView::setRootIndex(const QModelIndex &index)
+{
+ Q_D(QQuickTreeView);
+ d->m_treeModelToTableModel.setRootIndex(index);
+ positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
+}
+
+void QQuickTreeView::resetRootIndex()
+{
+ Q_D(QQuickTreeView);
+ d->m_treeModelToTableModel.resetRootIndex();
+ positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
+}
+
int QQuickTreeView::depth(int row) const
{
Q_D(const QQuickTreeView);
@@ -610,6 +622,7 @@ QPoint QQuickTreeView::cellAtIndex(const QModelIndex &index) const
return QPoint(tableIndex.column(), tableIndex.row());
}
+#if QT_DEPRECATED_SINCE(6, 4)
QModelIndex QQuickTreeView::modelIndex(int row, int column) const
{
static const bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
@@ -621,9 +634,13 @@ QModelIndex QQuickTreeView::modelIndex(int row, int column) const
// to continue accepting calls to modelIndex(column, row).
return modelIndex({row, column});
} else {
+ qmlWarning(this) << "modelIndex(row, column) is deprecated. "
+ "Use index(row, column) instead. For more information, see "
+ "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
return modelIndex({column, row});
}
}
+#endif
void QQuickTreeView::keyPressEvent(QKeyEvent *event)
{
diff --git a/src/quick/items/qquicktreeview_p.h b/src/quick/items/qquicktreeview_p.h
index b499f900cd..fe5fed6b95 100644
--- a/src/quick/items/qquicktreeview_p.h
+++ b/src/quick/items/qquicktreeview_p.h
@@ -22,9 +22,10 @@ QT_BEGIN_NAMESPACE
class QQuickTreeViewPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickTreeView : public QQuickTableView
+class Q_QUICK_EXPORT QQuickTreeView : public QQuickTableView
{
Q_OBJECT
+ Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex RESET resetRootIndex NOTIFY rootIndexChanged REVISION(6, 6) FINAL)
QML_NAMED_ELEMENT(TreeView)
QML_ADDED_IN_VERSION(6, 3)
@@ -32,6 +33,10 @@ public:
QQuickTreeView(QQuickItem *parent = nullptr);
~QQuickTreeView() override;
+ QModelIndex rootIndex() const;
+ void setRootIndex(const QModelIndex &index);
+ void resetRootIndex();
+
Q_INVOKABLE int depth(int row) const;
Q_INVOKABLE bool isExpanded(int row) const;
@@ -44,12 +49,17 @@ public:
Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex &index);
Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const override;
- Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const override;
+#if QT_DEPRECATED_SINCE(6, 4)
+ QT_DEPRECATED_VERSION_X_6_4("Use index(row, column) instead")
+ Q_REVISION(6, 4) Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
+#endif
+
Q_SIGNALS:
void expanded(int row, int depth);
void collapsed(int row, bool recursively);
+ Q_REVISION(6, 6) void rootIndexChanged();
protected:
void keyPressEvent(QKeyEvent *event) override;
@@ -61,6 +71,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTreeView)
-
#endif // QQUICKTREEVIEW_P_H
diff --git a/src/quick/items/qquicktreeview_p_p.h b/src/quick/items/qquicktreeview_p_p.h
index c14d7c4107..bb6a8d7ce4 100644
--- a/src/quick/items/qquicktreeview_p_p.h
+++ b/src/quick/items/qquicktreeview_p_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickTreeViewPrivate : public QQuickTableViewPrivate
+class Q_QUICK_EXPORT QQuickTreeViewPrivate : public QQuickTableViewPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickTreeView)
diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp
index 7f0faa119e..cb9fab654c 100644
--- a/src/quick/items/qquickview.cpp
+++ b/src/quick/items/qquickview.cpp
@@ -35,7 +35,7 @@ void QQuickViewPrivate::init(QQmlEngine* e)
// The content item has CppOwnership policy (set in QQuickWindow). Ensure the presence of a JS
// wrapper so that the garbage collector can see the policy.
QV4::ExecutionEngine *v4 = engine.data()->handle();
- QV4::QObjectWrapper::wrap(v4, contentItem);
+ QV4::QObjectWrapper::ensureWrapper(v4, contentItem);
}
}
@@ -48,12 +48,11 @@ QQuickViewPrivate::~QQuickViewPrivate()
{
}
-void QQuickViewPrivate::execute()
+QQuickViewPrivate::ExecuteState QQuickViewPrivate::executeHelper()
{
- Q_Q(QQuickView);
if (!engine) {
qWarning() << "QQuickView: invalid qml engine.";
- return;
+ return Stop;
}
if (root)
@@ -62,6 +61,14 @@ void QQuickViewPrivate::execute()
delete component;
component = nullptr;
}
+ return ExecuteState::Continue;
+}
+
+void QQuickViewPrivate::execute()
+{
+ if (executeHelper() == Stop)
+ return;
+ Q_Q(QQuickView);
if (!source.isEmpty()) {
component = new QQmlComponent(engine.data(), source, q);
if (!component->isLoading()) {
@@ -73,6 +80,22 @@ void QQuickViewPrivate::execute()
}
}
+void QQuickViewPrivate::execute(QAnyStringView uri, QAnyStringView typeName)
+{
+ if (executeHelper() == Stop)
+ return;
+ Q_Q(QQuickView);
+
+ component = new QQmlComponent(engine.data(), uri, typeName, q);
+ if (!component->isLoading()) {
+ q->continueExecute();
+ } else {
+ QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
+ q, SLOT(continueExecute()));
+ }
+
+}
+
void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
const QRectF &oldGeometry)
{
@@ -129,7 +152,7 @@ QQuickView::QQuickView(QWindow *parent)
/*!
Constructs a QQuickView with the given QML \a source and \a parent.
- The default value of \a parent is 0.
+ The default value of \a parent is \c{nullptr}.
*/
QQuickView::QQuickView(const QUrl &source, QWindow *parent)
@@ -139,6 +162,19 @@ QQuickView::QQuickView(const QUrl &source, QWindow *parent)
}
/*!
+ \since 6.7
+ Constructs a QQuickView with the element specified by \a uri and \a typeName
+ and parent \a parent.
+ The default value of \a parent is \c{nullptr}.
+ \sa loadFromModule
+ */
+QQuickView::QQuickView(QAnyStringView uri, QAnyStringView typeName, QWindow *parent)
+ : QQuickView(parent)
+{
+ loadFromModule(uri, typeName);
+}
+
+/*!
Constructs a QQuickView with the given QML \a engine and \a parent.
Note: In this case, the QQuickView does not own the given \a engine object;
@@ -203,6 +239,26 @@ void QQuickView::setSource(const QUrl& url)
}
/*!
+ \since 6.7
+ Loads the QML component identified by \a uri and \a typeName. If the component
+ is backed by a QML file, \l{source} will be set accordingly. For types defined
+ in \c{C++}, \c{source} will be empty.
+
+ If any \l{source} was set before this method was called, it will be cleared.
+
+ Calling this method multiple times with the same \a uri and \a typeName will result
+ in the QML component being reinstantiated.
+
+ \sa setSource, QQmlComponent::loadFromModule, QQmlApplicationEngine::loadFromModule
+ */
+void QQuickView::loadFromModule(QAnyStringView uri, QAnyStringView typeName)
+{
+ Q_D(QQuickView);
+ d->source = {}; // clear URL
+ d->execute(uri, typeName);
+}
+
+/*!
Sets the initial properties \a initialProperties with which the QML
component gets initialized after calling \l QQuickView::setSource().
@@ -468,6 +524,11 @@ void QQuickView::continueExecute()
return;
}
+ // If we used loadFromModule, we might not have a URL so far.
+ // Thus, query the component to retrieve the associated URL, if any
+ if (d->source.isEmpty())
+ d->source = d->component->url();
+
if (d->setRootObject(obj.get()))
Q_UNUSED(obj.release());
emit statusChanged(status());
diff --git a/src/quick/items/qquickview.h b/src/quick/items/qquickview.h
index aeff7cd88d..92bc495a98 100644
--- a/src/quick/items/qquickview.h
+++ b/src/quick/items/qquickview.h
@@ -26,6 +26,7 @@ public:
explicit QQuickView(QWindow *parent = nullptr);
QQuickView(QQmlEngine* engine, QWindow *parent);
explicit QQuickView(const QUrl &source, QWindow *parent = nullptr);
+ explicit QQuickView(QAnyStringView uri, QAnyStringView typeName, QWindow *parent = nullptr);
QQuickView(const QUrl &source, QQuickRenderControl *renderControl);
~QQuickView() override;
@@ -52,6 +53,7 @@ public:
public Q_SLOTS:
void setSource(const QUrl&);
+ void loadFromModule(QAnyStringView uri, QAnyStringView typeName);
void setInitialProperties(const QVariantMap &initialProperties);
void setContent(const QUrl& url, QQmlComponent *component, QObject *item);
diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h
index 9e495d9139..1ad1897af3 100644
--- a/src/quick/items/qquickview_p.h
+++ b/src/quick/items/qquickview_p.h
@@ -35,7 +35,7 @@ class QQmlError;
class QQuickItem;
class QQmlComponent;
-class Q_QUICK_PRIVATE_EXPORT QQuickViewPrivate : public QQuickWindowPrivate,
+class Q_QUICK_EXPORT QQuickViewPrivate : public QQuickWindowPrivate,
public QQuickItemChangeListener
{
Q_DECLARE_PUBLIC(QQuickView)
@@ -46,7 +46,10 @@ public:
QQuickViewPrivate();
~QQuickViewPrivate();
+ enum ExecuteState { Continue, Stop };
+ ExecuteState executeHelper();
void execute();
+ void execute(QAnyStringView uri, QAnyStringView typeName);
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) override;
void initResize();
void updateSize();
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 86a60fd89d..3392ab075d 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -8,10 +8,12 @@
#include "qquickitem_p.h"
#include "qquickevents_p_p.h"
#include "qquickgraphicsdevice_p.h"
+#include "qquickwindowcontainer_p.h"
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgplaintexture_p.h>
#include <QtQuick/private/qquickpointerhandler_p.h>
+#include <QtQuick/private/qquickpointerhandler_p_p.h>
#include <private/qsgrenderloop_p.h>
#include <private/qsgrhisupport_p.h>
#include <private/qquickrendercontrol_p.h>
@@ -36,7 +38,7 @@
#include <QtQml/qqmlinfo.h>
#include <QtQml/private/qqmlmetatype_p.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
@@ -48,8 +50,12 @@
#ifndef QT_NO_DEBUG_STREAM
#include <private/qdebug_p.h>
#endif
+#include <QtCore/qpointer.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
+
+#include <utility>
+#include <mutex>
QT_BEGIN_NAMESPACE
@@ -58,6 +64,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcMouse)
Q_DECLARE_LOGGING_CATEGORY(lcTouch)
Q_DECLARE_LOGGING_CATEGORY(lcPtr)
Q_LOGGING_CATEGORY(lcDirty, "qt.quick.dirty")
+Q_LOGGING_CATEGORY(lcQuickWindow, "qt.quick.window")
Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
bool QQuickWindowPrivate::defaultAlphaBuffer = false;
@@ -81,8 +88,8 @@ public:
QAnimationDriver *animationDriver = m_renderLoop->animationDriver();
if (animationDriver) {
- connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
- connect(m_renderLoop, SIGNAL(timeToIncubate()), this, SLOT(incubate()));
+ connect(animationDriver, &QAnimationDriver::stopped, this, &QQuickWindowIncubationController::animationStopped);
+ connect(m_renderLoop, &QSGRenderLoop::timeToIncubate, this, &QQuickWindowIncubationController::incubate);
}
}
@@ -198,6 +205,8 @@ void QQuickWindow::showEvent(QShowEvent *)
void QQuickWindow::hideEvent(QHideEvent *)
{
Q_D(QQuickWindow);
+ if (auto da = d->deliveryAgentPrivate())
+ da->handleWindowHidden(this);
if (d->windowManager)
d->windowManager->hide(this);
}
@@ -324,6 +333,22 @@ struct PolishLoopDetector
int numPolishLoopsInSequence = 0;
};
+static const QQuickItem *firstItemWithDirtyChildrenStacking(const QQuickItem *item)
+{
+ if (QQuickItemPrivate::get(item)->dirtyAttributes
+ & QQuickItemPrivate::ChildrenStackingChanged) {
+ return item;
+ }
+
+ const auto childItems = item->childItems();
+ for (const auto *childItem : childItems) {
+ if (auto *dirtyItem = firstItemWithDirtyChildrenStacking(childItem))
+ return dirtyItem;
+ }
+
+ return nullptr;
+}
+
void QQuickWindowPrivate::polishItems()
{
// An item can trigger polish on another item, or itself for that matter,
@@ -357,6 +382,11 @@ void QQuickWindowPrivate::polishItems()
deliveryAgentPrivate()->updateFocusItemTransform();
}
#endif
+
+ if (auto *dirtyItem = firstItemWithDirtyChildrenStacking(contentItem)) {
+ qCDebug(lcQuickWindow) << dirtyItem << "has dirty child stacking order";
+ updateChildWindowStackingOrder();
+ }
}
/*!
@@ -396,6 +426,7 @@ void QQuickWindow::physicalDpiChanged()
d->lastReportedItemDevicePixelRatio = newPixelRatio;
if (d->contentItem)
updatePixelRatioHelper(d->contentItem, newPixelRatio);
+ d->forcePolish();
}
void QQuickWindow::handleFontDatabaseChanged()
@@ -404,23 +435,6 @@ void QQuickWindow::handleFontDatabaseChanged()
d->pendingFontUpdate = true;
}
-void QQuickWindow::handleScreenChanged(QScreen *screen)
-{
- Q_D(QQuickWindow);
- // we connected to the initial screen in QQuickWindowPrivate::init, but the screen changed
- disconnect(d->physicalDpiChangedConnection);
- if (screen) {
- physicalDpiChanged();
- // When physical DPI changes on the same screen, either the resolution or the device pixel
- // ratio changed. We must check what it is. Device pixel ratio does not have its own
- // ...Changed() signal. Reconnect, same as in QQuickWindowPrivate::init.
- d->physicalDpiChangedConnection = connect(screen, &QScreen::physicalDotsPerInchChanged,
- this, &QQuickWindow::physicalDpiChanged);
- }
-
- d->forcePolish();
-}
-
void forcePolishHelper(QQuickItem *item)
{
if (item->flags() & QQuickItem::ItemHasContents) {
@@ -432,6 +446,13 @@ void forcePolishHelper(QQuickItem *item)
forcePolishHelper(items.at(i));
}
+void QQuickWindow::handleScreenChanged(QScreen *screen)
+{
+ Q_D(QQuickWindow);
+ Q_UNUSED(screen);
+ d->forcePolish();
+}
+
/*!
Schedules polish events on all items in the scene.
*/
@@ -454,27 +475,37 @@ void forceUpdate(QQuickItem *item)
forceUpdate(items.at(i));
}
-void QQuickWindowRenderTarget::reset(QRhi *rhi)
+void QQuickWindowRenderTarget::reset(QRhi *rhi, ResetFlags flags)
{
- if (owns) {
- if (rhi) {
- delete renderTarget;
- delete rpDesc;
- delete texture;
- delete renderBuffer;
- delete depthStencil;
- }
+ if (rhi) {
+ if (rt.owns)
+ delete rt.renderTarget;
- delete paintDevice;
+ delete res.texture;
+ delete res.renderBuffer;
+ delete res.rpDesc;
}
- renderTarget = nullptr;
- rpDesc = nullptr;
- texture = nullptr;
- renderBuffer = nullptr;
- depthStencil = nullptr;
- paintDevice = nullptr;
- owns = false;
+ rt = {};
+ res = {};
+
+ if (!flags.testFlag(ResetFlag::KeepImplicitBuffers))
+ implicitBuffers.reset(rhi);
+
+ if (sw.owns)
+ delete sw.paintDevice;
+
+ sw = {};
+}
+
+void QQuickWindowRenderTarget::ImplicitBuffers::reset(QRhi *rhi)
+{
+ if (rhi) {
+ delete depthStencil;
+ delete depthStencilTexture;
+ delete multisampleTexture;
+ }
+ *this = {};
}
void QQuickWindowPrivate::invalidateFontData(QQuickItem *item)
@@ -491,19 +522,18 @@ void QQuickWindowPrivate::invalidateFontData(QQuickItem *item)
void QQuickWindowPrivate::ensureCustomRenderTarget()
{
// resolve() can be expensive when importing an existing native texture, so
- // it is important to only do it when the QQuickRenderTarget* was really changed
+ // it is important to only do it when the QQuickRenderTarget was really changed.
if (!redirect.renderTargetDirty)
return;
redirect.renderTargetDirty = false;
- redirect.rt.reset(rhi);
+ redirect.rt.reset(rhi, QQuickWindowRenderTarget::ResetFlag::KeepImplicitBuffers);
- // a default constructed QQuickRenderTarget means no redirection
- if (customRenderTarget.isNull())
- return;
-
- QQuickRenderTargetPrivate::get(&customRenderTarget)->resolve(rhi, &redirect.rt);
+ if (!QQuickRenderTargetPrivate::get(&customRenderTarget)->resolve(rhi, &redirect.rt)) {
+ qWarning("Failed to set up render target redirection for QQuickWindow");
+ redirect.rt.reset(rhi);
+ }
}
void QQuickWindowPrivate::setCustomCommandBuffer(QRhiCommandBuffer *cb)
@@ -553,13 +583,7 @@ void QQuickWindowPrivate::syncSceneGraph()
animationController->afterNodeSync();
- // Copy the current state of clearing from window into renderer.
renderer->setClearColor(clearColor);
- // Cannot skip clearing the color buffer in Qt 6 anymore.
- const QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearColorBuffer
- | QSGAbstractRenderer::ClearStencilBuffer
- | QSGAbstractRenderer::ClearDepthBuffer;
- renderer->setClearMode(mode);
renderer->setVisualizationMode(visualizationMode);
@@ -584,6 +608,30 @@ void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud)
emit w->afterRenderPassRecording();
}
+int QQuickWindowPrivate::multiViewCount()
+{
+ if (rhi) {
+ ensureCustomRenderTarget();
+ if (redirect.rt.rt.renderTarget)
+ return redirect.rt.rt.multiViewCount;
+ }
+
+ // Note that on QRhi level 0 and 1 are often used interchangeably, as both mean
+ // no-multiview. Here in Qt Quick let's always use 1 as the default
+ // (no-multiview), so that higher layers (effects, materials) do not need to
+ // handle both 0 and 1, only 1.
+ return 1;
+}
+
+QRhiRenderTarget *QQuickWindowPrivate::activeCustomRhiRenderTarget()
+{
+ if (rhi) {
+ ensureCustomRenderTarget();
+ return redirect.rt.rt.renderTarget;
+ }
+ return nullptr;
+}
+
void QQuickWindowPrivate::renderSceneGraph()
{
Q_Q(QQuickWindow);
@@ -597,8 +645,8 @@ void QQuickWindowPrivate::renderSceneGraph()
QRhiRenderTarget *rt;
QRhiRenderPassDescriptor *rp;
QRhiCommandBuffer *cb;
- if (redirect.rt.renderTarget) {
- rt = redirect.rt.renderTarget;
+ if (redirect.rt.rt.renderTarget) {
+ rt = redirect.rt.rt.renderTarget;
rp = rt->renderPassDescriptor();
if (!rp) {
qWarning("Custom render target is set but no renderpass descriptor has been provided.");
@@ -619,8 +667,9 @@ void QQuickWindowPrivate::renderSceneGraph()
cb = swapchain->currentFrameCommandBuffer();
}
sgRenderTarget = QSGRenderTarget(rt, rp, cb);
+ sgRenderTarget.multiViewCount = multiViewCount();
} else {
- sgRenderTarget = QSGRenderTarget(redirect.rt.paintDevice);
+ sgRenderTarget = QSGRenderTarget(redirect.rt.sw.paintDevice);
}
context->beginNextFrame(renderer,
@@ -633,19 +682,12 @@ void QQuickWindowPrivate::renderSceneGraph()
emit q->beforeRendering();
runAndClearJobs(&beforeRenderingJobs);
- QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
- bool flipY = rhi ? !rhi->isYUpInNDC() : false;
- if (!customRenderTarget.isNull() && customRenderTarget.mirrorVertically())
- flipY = !flipY;
- if (flipY)
- matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
-
const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
QSize pixelSize;
- if (redirect.rt.renderTarget)
- pixelSize = redirect.rt.renderTarget->pixelSize();
- else if (redirect.rt.paintDevice)
- pixelSize = QSize(redirect.rt.paintDevice->width(), redirect.rt.paintDevice->height());
+ if (redirect.rt.rt.renderTarget)
+ pixelSize = redirect.rt.rt.renderTarget->pixelSize();
+ else if (redirect.rt.sw.paintDevice)
+ pixelSize = QSize(redirect.rt.sw.paintDevice->width(), redirect.rt.sw.paintDevice->height());
else if (rhi)
pixelSize = swapchain->currentPixelSize();
else // software or other backend
@@ -654,7 +696,16 @@ void QQuickWindowPrivate::renderSceneGraph()
renderer->setDevicePixelRatio(devicePixelRatio);
renderer->setDeviceRect(QRect(QPoint(0, 0), pixelSize));
renderer->setViewportRect(QRect(QPoint(0, 0), pixelSize));
- renderer->setProjectionMatrixToRect(QRectF(QPointF(0, 0), pixelSize / devicePixelRatio), matrixFlags);
+
+ QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
+ bool flipY = rhi ? !rhi->isYUpInNDC() : false;
+ if (!customRenderTarget.isNull() && customRenderTarget.mirrorVertically())
+ flipY = !flipY;
+ if (flipY)
+ matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
+
+ const QRectF rect(QPointF(0, 0), pixelSize / devicePixelRatio);
+ renderer->setProjectionMatrixToRect(rect, matrixFlags, rhi && !rhi->isYUpInNDC());
context->renderNextFrame(renderer);
@@ -682,7 +733,6 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, clearColor(Qt::white)
, persistentGraphics(true)
, persistentSceneGraph(true)
- , componentCompleted(true)
, inDestructor(false)
, incubationController(nullptr)
, hasActiveSwapchain(false)
@@ -701,6 +751,24 @@ QQuickWindowPrivate::~QQuickWindowPrivate()
deliveryAgent = nullptr;
}
+void QQuickWindowPrivate::setPalette(QQuickPalette* palette)
+{
+ if (windowPaletteRef == palette)
+ return;
+
+ if (windowPaletteRef)
+ disconnect(windowPaletteRef, &QQuickPalette::changed, this, &QQuickWindowPrivate::updateWindowPalette);
+ windowPaletteRef = palette;
+ updateWindowPalette();
+ if (windowPaletteRef)
+ connect(windowPaletteRef, &QQuickPalette::changed, this, &QQuickWindowPrivate::updateWindowPalette);
+}
+
+void QQuickWindowPrivate::updateWindowPalette()
+{
+ QQuickPaletteProviderPrivateBase::setPalette(windowPaletteRef);
+}
+
void QQuickWindowPrivate::updateChildrenPalettes(const QPalette &parentPalette)
{
Q_Q(QQuickWindow);
@@ -744,12 +812,8 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
q,
&QQuickWindow::handleFontDatabaseChanged);
- if (QScreen *screen = q->screen()) {
+ if (q->screen()) {
lastReportedItemDevicePixelRatio = q->effectiveDevicePixelRatio();
- // if the screen changes, then QQuickWindow::handleScreenChanged disconnects
- // and connects to the new screen
- physicalDpiChangedConnection = QObject::connect(screen, &QScreen::physicalDotsPerInchChanged,
- q, &QQuickWindow::physicalDpiChanged);
}
QSGContext *sg;
@@ -772,15 +836,14 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
animationController.reset(new QQuickAnimatorController(q));
- QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
- QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
- QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
+ QObject::connect(context, &QSGRenderContext::initialized, q, &QQuickWindow::sceneGraphInitialized, Qt::DirectConnection);
+ QObject::connect(context, &QSGRenderContext::invalidated, q, &QQuickWindow::sceneGraphInvalidated, Qt::DirectConnection);
+ QObject::connect(context, &QSGRenderContext::invalidated, q, &QQuickWindow::cleanupSceneGraph, Qt::DirectConnection);
- QObject::connect(q, SIGNAL(focusObjectChanged(QObject*)), q, SIGNAL(activeFocusItemChanged()));
- QObject::connect(q, SIGNAL(screenChanged(QScreen*)), q, SLOT(handleScreenChanged(QScreen*)));
- QObject::connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
- q, SLOT(handleApplicationStateChanged(Qt::ApplicationState)));
- QObject::connect(q, SIGNAL(frameSwapped()), q, SLOT(runJobsAfterSwap()), Qt::DirectConnection);
+ QObject::connect(q, &QQuickWindow::focusObjectChanged, q, &QQuickWindow::activeFocusItemChanged);
+ QObject::connect(q, &QQuickWindow::screenChanged, q, &QQuickWindow::handleScreenChanged);
+ QObject::connect(qApp, &QGuiApplication::applicationStateChanged, q, &QQuickWindow::handleApplicationStateChanged);
+ QObject::connect(q, &QQuickWindow::frameSwapped, q, &QQuickWindow::runJobsAfterSwap, Qt::DirectConnection);
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->addWindow(q);
@@ -861,13 +924,15 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the
window for use with \c {QtQuick} graphical types.
- A Window can be declared inside an Item or inside another Window; in that
+ A Window can be declared inside an Item or inside another Window, in which
case the inner Window will automatically become "transient for" the outer
- Window: that is, most platforms will show it centered upon the outer window
- by default, and there may be other platform-dependent behaviors, depending
- also on the \l flags. If the nested window is intended to be a dialog in
- your application, you should also set \l flags to Qt.Dialog, because some
- window managers will not provide the centering behavior without that flag.
+ Window, with the outer Window as its \l transientParent. Most platforms will
+ show the Window centered upon the outer window in this case, and there may be
+ other platform-dependent behaviors, depending also on the \l flags. If the nested
+ window is intended to be a dialog in your application, you should also set \l flags
+ to \c Qt.Dialog, because some window managers will not provide the centering behavior
+ without that flag.
+
You can also declare multiple windows inside a top-level \l QtObject, in which
case the windows will have no transient relationship.
@@ -891,6 +956,10 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
// The confirmExitPopup allows user to save or discard the document,
// or to cancel the closing.
\endcode
+
+ \note If using \l {Qt Quick Controls}, it's recommended to use
+ \l ApplicationWindow instead of Window, as it has better styling
+ support.
*/
/*!
@@ -1121,18 +1190,15 @@ QQuickWindow::~QQuickWindow()
delete root;
d->deliveryAgent = nullptr; // avoid forwarding events there during destruction
- d->renderJobMutex.lock();
- qDeleteAll(d->beforeSynchronizingJobs);
- d->beforeSynchronizingJobs.clear();
- qDeleteAll(d->afterSynchronizingJobs);
- d->afterSynchronizingJobs.clear();
- qDeleteAll(d->beforeRenderingJobs);
- d->beforeRenderingJobs.clear();
- qDeleteAll(d->afterRenderingJobs);
- d->afterRenderingJobs.clear();
- qDeleteAll(d->afterSwapJobs);
- d->afterSwapJobs.clear();
- d->renderJobMutex.unlock();
+
+ {
+ const std::lock_guard locker(d->renderJobMutex);
+ qDeleteAll(std::exchange(d->beforeSynchronizingJobs, {}));
+ qDeleteAll(std::exchange(d->afterSynchronizingJobs, {}));
+ qDeleteAll(std::exchange(d->beforeRenderingJobs, {}));
+ qDeleteAll(std::exchange(d->afterRenderingJobs, {}));;
+ qDeleteAll(std::exchange(d->afterSwapJobs, {}));
+ }
// It is important that the pixmap cache is cleaned up during shutdown.
// Besides playing nice, this also solves a practical problem that
@@ -1336,6 +1402,41 @@ QObject *QQuickWindow::focusObject() const
return const_cast<QQuickWindow*>(this);
}
+/*!
+ \internal
+
+ Clears all exclusive and passive grabs for the points in \a pointerEvent.
+
+ We never allow any kind of grab to persist after release, unless we're waiting
+ for a synth event from QtGui (as with most tablet events), so for points that
+ are fully released, the grab is cleared.
+
+ Called when QQuickWindow::event dispatches events, or when the QQuickOverlay
+ has filtered an event so that it bypasses normal delivery.
+*/
+void QQuickWindowPrivate::clearGrabbers(QPointerEvent *pointerEvent)
+{
+ if (pointerEvent->isEndEvent()
+ && !(QQuickDeliveryAgentPrivate::isTabletEvent(pointerEvent)
+ && (qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents)
+ || QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse))) {
+ if (pointerEvent->isSinglePointEvent()) {
+ if (static_cast<QSinglePointEvent *>(pointerEvent)->buttons() == Qt::NoButton) {
+ auto &firstPt = pointerEvent->point(0);
+ pointerEvent->setExclusiveGrabber(firstPt, nullptr);
+ pointerEvent->clearPassiveGrabbers(firstPt);
+ }
+ } else {
+ for (auto &point : pointerEvent->points()) {
+ if (point.state() == QEventPoint::State::Released) {
+ pointerEvent->setExclusiveGrabber(point, nullptr);
+ pointerEvent->clearPassiveGrabbers(point);
+ }
+ }
+ }
+ }
+}
+
/*! \reimp */
bool QQuickWindow::event(QEvent *event)
{
@@ -1478,26 +1579,7 @@ bool QQuickWindow::event(QEvent *event)
// or fix QTBUG-90851 so that the event always has points?
bool ret = (da && da->event(event));
- // failsafe: never allow any kind of grab to persist after release,
- // unless we're waiting for a synth event from QtGui (as with most tablet events)
- if (pe->isEndEvent() && !(QQuickDeliveryAgentPrivate::isTabletEvent(pe) &&
- (qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents) ||
- QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse))) {
- if (pe->isSinglePointEvent()) {
- if (static_cast<QSinglePointEvent *>(pe)->buttons() == Qt::NoButton) {
- auto &firstPt = pe->point(0);
- pe->setExclusiveGrabber(firstPt, nullptr);
- pe->clearPassiveGrabbers(firstPt);
- }
- } else {
- for (auto &point : pe->points()) {
- if (point.state() == QEventPoint::State::Released) {
- pe->setExclusiveGrabber(point, nullptr);
- pe->clearPassiveGrabbers(point);
- }
- }
- }
- }
+ d->clearGrabbers(pe);
if (ret)
return true;
@@ -1553,6 +1635,27 @@ bool QQuickWindow::event(QEvent *event)
d->inheritPalette(QGuiApplication::palette());
if (d->contentItem)
QCoreApplication::sendEvent(d->contentItem, event);
+ break;
+ case QEvent::DevicePixelRatioChange:
+ physicalDpiChanged();
+ break;
+ case QEvent::ChildWindowAdded: {
+ auto *childEvent = static_cast<QChildWindowEvent*>(event);
+ auto *childWindow = childEvent->child();
+ qCDebug(lcQuickWindow) << "Child window" << childWindow << "added to" << this;
+ if (childWindow->handle()) {
+ // The reparenting has already resulted in the native window
+ // being added to its parent, on top of all other windows. We need
+ // to do a synchronous re-stacking of the windows here, to avoid
+ // leaving the window in the wrong position while waiting for the
+ // asynchronous callback to QQuickWindow::polishItems().
+ d->updateChildWindowStackingOrder();
+ } else {
+ qCDebug(lcQuickWindow) << "No platform window yet."
+ << "Deferring child window stacking until surface creation";
+ }
+ break;
+ }
default:
break;
}
@@ -1568,6 +1671,35 @@ bool QQuickWindow::event(QEvent *event)
return QWindow::event(event);
}
+void QQuickWindowPrivate::updateChildWindowStackingOrder(QQuickItem *item)
+{
+ Q_Q(QQuickWindow);
+
+ if (!item) {
+ qCDebug(lcQuickWindow) << "Updating child window stacking order for" << q;
+ item = contentItem;
+ }
+ auto *itemPrivate = QQuickItemPrivate::get(item);
+ const auto paintOrderChildItems = itemPrivate->paintOrderChildItems();
+ for (auto *child : paintOrderChildItems) {
+ if (auto *windowContainer = qobject_cast<QQuickWindowContainer*>(child)) {
+ auto *window = windowContainer->containedWindow();
+ if (!window) {
+ qCDebug(lcQuickWindow) << windowContainer << "has no contained window yet";
+ continue;
+ }
+ if (window->parent() != q) {
+ qCDebug(lcQuickWindow) << window << "is not yet child of this window";
+ continue;
+ }
+ qCDebug(lcQuickWindow) << "Raising" << window << "owned by" << windowContainer;
+ window->raise();
+ }
+
+ updateChildWindowStackingOrder(child);
+ }
+}
+
/*! \reimp */
void QQuickWindow::keyPressEvent(QKeyEvent *e)
{
@@ -1664,11 +1796,14 @@ void QQuickWindowPrivate::updateCursor(const QPointF &scenePos, QQuickItem *root
if (!rootItem)
rootItem = contentItem;
auto cursorItemAndHandler = findCursorItemAndHandler(rootItem, scenePos);
- if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second) {
+ if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second ||
+ (cursorItemAndHandler.second && QQuickPointerHandlerPrivate::get(cursorItemAndHandler.second)->cursorDirty)) {
QWindow *renderWindow = QQuickRenderControl::renderWindowFor(q);
QWindow *window = renderWindow ? renderWindow : q;
cursorItem = cursorItemAndHandler.first;
cursorHandler = cursorItemAndHandler.second;
+ if (cursorHandler)
+ QQuickPointerHandlerPrivate::get(cursorItemAndHandler.second)->cursorDirty = false;
if (cursorItem) {
const auto cursor = QQuickItemPrivate::get(cursorItem)->effectiveCursor(cursorHandler);
qCDebug(lcHoverTrace) << "setting cursor" << cursor << "from" << cursorHandler << "or" << cursorItem;
@@ -1722,6 +1857,35 @@ void QQuickWindowPrivate::clearFocusObject()
da->clearFocusObject();
}
+void QQuickWindowPrivate::setFocusToTarget(FocusTarget target, Qt::FocusReason reason)
+{
+ QQuickItem *newFocusItem = nullptr;
+ if (contentItem) {
+ switch (target) {
+ case FocusTarget::First:
+ newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(contentItem, true);
+ break;
+ case FocusTarget::Last:
+ newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(contentItem, false);
+ break;
+ case FocusTarget::Next:
+ case FocusTarget::Prev: {
+ auto da = deliveryAgentPrivate();
+ Q_ASSERT(da);
+ QQuickItem *focusItem = da->focusTargetItem() ? da->focusTargetItem() : contentItem;
+ bool forward = (target == FocusTarget::Next);
+ newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(focusItem, forward);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (newFocusItem)
+ newFocusItem->setFocus(true, reason);
+}
+
/*!
\qmlproperty list<QtObject> Window::data
\qmldefault
@@ -1751,10 +1915,6 @@ void QQuickWindowPrivate::data_append(QQmlListProperty<QObject> *property, QObje
if (!o)
return;
QQuickWindow *that = static_cast<QQuickWindow *>(property->object);
- if (QQuickWindow *window = qmlobject_cast<QQuickWindow *>(o)) {
- qCDebug(lcTransient) << window << "is transient for" << that;
- window->setTransientParent(that);
- }
QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(that->contentItem())->data();
itemProperty.append(&itemProperty, o);
}
@@ -1814,9 +1974,7 @@ void QQuickWindowPrivate::rhiCreationFailureMessage(const QString &backendName,
void QQuickWindowPrivate::cleanupNodes()
{
- for (int ii = 0; ii < cleanupNodeList.size(); ++ii)
- delete cleanupNodeList.at(ii);
- cleanupNodeList.clear();
+ qDeleteAll(std::exchange(cleanupNodeList, {}));
}
void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
@@ -1963,19 +2121,14 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
itemPriv->itemNode()->setMatrix(matrix);
}
- bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
- ((item->clip() == false) != (itemPriv->clipNode() == nullptr));
- int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
- bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
- ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr));
-
+ const bool clipEffectivelyChanged = dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window);
if (clipEffectivelyChanged) {
- QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
- (QSGNode *) itemPriv->itemNode();
+ QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *)itemPriv->opacityNode()
+ : (QSGNode *)itemPriv->itemNode();
QSGNode *child = itemPriv->rootNode();
- if (item->clip()) {
- Q_ASSERT(itemPriv->clipNode() == nullptr);
+ if (bool initializeClipNode = item->clip() && itemPriv->clipNode() == nullptr;
+ initializeClipNode) {
QQuickDefaultClipNode *clip = new QQuickDefaultClipNode(item->clipRect());
itemPriv->extra.value().clipNode = clip;
clip->update();
@@ -1989,9 +2142,14 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
parent->appendChildNode(clip);
}
- } else {
+ } else if (bool updateClipNode = item->clip() && itemPriv->clipNode() != nullptr;
+ updateClipNode) {
+ QQuickDefaultClipNode *clip = itemPriv->clipNode();
+ clip->setClipRect(item->clipRect());
+ clip->update();
+ } else if (bool removeClipNode = !item->clip() && itemPriv->clipNode() != nullptr;
+ removeClipNode) {
QQuickDefaultClipNode *clip = itemPriv->clipNode();
- Q_ASSERT(clip);
parent->removeChildNode(clip);
if (child) {
clip->removeChildNode(child);
@@ -2005,6 +2163,10 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
}
}
+ const int effectRefCount = itemPriv->extra.isAllocated() ? itemPriv->extra->effectRefCount : 0;
+ const bool effectRefEffectivelyChanged =
+ (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window))
+ && ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr));
if (effectRefEffectivelyChanged) {
if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
itemPriv->childContainerNode()->removeAllChildNodes();
@@ -2216,14 +2378,6 @@ void QQuickWindow::cleanupSceneGraph()
d->runAndClearJobs(&d->afterSwapJobs);
}
-void QQuickWindow::setTransientParent_helper(QQuickWindow *window)
-{
- qCDebug(lcTransient) << this << "is transient for" << window;
- setTransientParent(window);
- disconnect(sender(), SIGNAL(windowChanged(QQuickWindow*)),
- this, SLOT(setTransientParent_helper(QQuickWindow*)));
-}
-
QOpenGLContext *QQuickWindowPrivate::openglContext()
{
#if QT_CONFIG(opengl)
@@ -2258,7 +2412,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::frameSwapped()
+ \qmlsignal QtQuick::Window::frameSwapped()
This signal is emitted when a frame has been queued for presenting. With
vertical synchronization enabled the signal is emitted at most once per
@@ -2274,7 +2428,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::sceneGraphInitialized()
+ \qmlsignal QtQuick::Window::sceneGraphInitialized()
\internal
*/
@@ -2296,7 +2450,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::sceneGraphInvalidated()
+ \qmlsignal QtQuick::Window::sceneGraphInvalidated()
\internal
*/
@@ -2316,7 +2470,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::sceneGraphError(SceneGraphError error, QString message)
+ \qmlsignal QtQuick::Window::sceneGraphError(SceneGraphError error, QString message)
This signal is emitted when an \a error occurred during scene graph initialization.
@@ -2373,7 +2527,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::closing(CloseEvent close)
+ \qmlsignal QtQuick::Window::closing(CloseEvent close)
\since 5.1
This signal is emitted when the user tries to close the window.
@@ -2653,9 +2807,16 @@ QQmlIncubationController *QQuickWindow::incubationController() const
text. Using such features in combination with the NativeTextRendering
render type will lend poor and sometimes pixelated results.
- \value QtTextRendering Use Qt's own rasterization algorithm.
+ Both \c QtTextRendering and \c CurveTextRendering are hardware-accelerated techniques.
+ \c QtTextRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveTextRendering should be considered as an alternative in cases
+ where \c QtTextRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+ \value QtTextRendering Use Qt's own rasterization algorithm.
\value NativeTextRendering Use the operating system's native rasterizer for text.
+ \value CurveTextRendering Text is rendered using a curve rasterizer running directly on
+ the graphics hardware. (Introduced in Qt 6.7.0.)
*/
/*!
@@ -2685,7 +2846,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::beforeSynchronizing()
+ \qmlsignal QtQuick::Window::beforeSynchronizing()
\internal
*/
@@ -2712,7 +2873,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterSynchronizing()
+ \qmlsignal QtQuick::Window::afterSynchronizing()
\internal
\since 5.3
*/
@@ -2732,23 +2893,23 @@ QQmlIncubationController *QQuickWindow::incubationController() const
to this signal is still important if the recording of copy type of commands
is desired since those cannot be enqueued within a render pass.
- When using OpenGL, the QOpenGLContext used for rendering by the scene graph
- will be bound at this point.
-
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
- states and leaving these enabled or set to non-default values when returning
- from the connected slot can interfere with the scene graph's rendering.
+ \note When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
+ states and leaving these enabled or set to non-default values when
+ returning from the connected slot can interfere with the scene graph's
+ rendering. The QOpenGLContext used for rendering by the scene graph will be
+ bound when the signal is emitted.
- \sa rendererInterface(), {Scene Graph - OpenGL Under QML}, {Scene Graph - Metal Under QML},
- {Scene Graph - Vulkan Under QML}, {Scene Graph - Direct3D 11 Under QML}
+ \sa rendererInterface(), {Scene Graph - RHI Under QML}, {Scene Graph -
+ OpenGL Under QML}, {Scene Graph - Metal Under QML}, {Scene Graph - Vulkan
+ Under QML}, {Scene Graph - Direct3D 11 Under QML}
*/
/*!
- \qmlsignal QtQuick.Window::Window::beforeRendering()
+ \qmlsignal QtQuick::Window::beforeRendering()
\internal
*/
@@ -2767,23 +2928,23 @@ QQmlIncubationController *QQuickWindow::incubationController() const
and afterRenderPassRecording(), that is typically used to achieve under- or
overlaying of the custom rendering.
- When using OpenGL, the QOpenGLContext used for rendering by the scene graph
- will be bound at this point.
-
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
- states and leaving these enabled or set to non-default values when returning
- from the connected slot can interfere with the scene graph's rendering.
+ \note When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
+ states and leaving these enabled or set to non-default values when
+ returning from the connected slot can interfere with the scene graph's
+ rendering. The QOpenGLContext used for rendering by the scene graph will be
+ bound when the signal is emitted.
- \sa rendererInterface(), {Scene Graph - OpenGL Under QML}, {Scene Graph - Metal Under QML},
- {Scene Graph - Vulkan Under QML}, {Scene Graph - Direct3D 11 Under QML}
+ \sa rendererInterface(), {Scene Graph - RHI Under QML}, {Scene Graph -
+ OpenGL Under QML}, {Scene Graph - Metal Under QML}, {Scene Graph - Vulkan
+ Under QML}, {Scene Graph - Direct3D 11 Under QML}
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterRendering()
+ \qmlsignal QtQuick::Window::afterRendering()
\internal
*/
@@ -2813,10 +2974,12 @@ QQmlIncubationController *QQuickWindow::incubationController() const
\sa rendererInterface()
\since 5.14
+
+ \sa {Scene Graph - RHI Under QML}
*/
/*!
- \qmlsignal QtQuick.Window::Window::beforeRenderPassRecording()
+ \qmlsignal QtQuick::Window::beforeRenderPassRecording()
\internal
\since 5.14
*/
@@ -2846,6 +3009,8 @@ QQmlIncubationController *QQuickWindow::incubationController() const
\sa rendererInterface()
\since 5.14
+
+ \sa {Scene Graph - RHI Under QML}
*/
/*!
@@ -2871,7 +3036,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::beforeFrameBegin()
+ \qmlsignal QtQuick::Window::beforeFrameBegin()
\internal
*/
@@ -2896,12 +3061,12 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterFrameEnd()
+ \qmlsignal QtQuick::Window::afterFrameEnd()
\internal
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterRenderPassRecording()
+ \qmlsignal QtQuick::Window::afterRenderPassRecording()
\internal
\since 5.14
*/
@@ -2921,7 +3086,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterAnimating()
+ \qmlsignal QtQuick::Window::afterAnimating()
This signal is emitted on the GUI thread before requesting the render thread to
perform the synchronization of the scene graph.
@@ -2955,7 +3120,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::sceneGraphAboutToStop()
+ \qmlsignal QtQuick::Window::sceneGraphAboutToStop()
\internal
\since 5.3
*/
@@ -2989,9 +3154,12 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
textures will in most cases be faster to render. When this flag is not set,
the texture will have an alpha channel based on the image's format.
- When \a options contains TextureHasMipmaps, the engine will create a
- texture which can use mipmap filtering. Mipmapped textures can not be in
- an atlas.
+ When \a options contains TextureHasMipmaps, the engine will create a texture
+ which can use mipmap filtering. Mipmapped textures can not be in an atlas.
+
+ Setting TextureHasAlphaChannel in \a options serves no purpose for this
+ function since assuming an alpha channel and blending is the default. To opt
+ out, set TextureIsOpaque.
When the scene graph uses OpenGL, the returned texture will be using \c
GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format. With
@@ -3002,12 +3170,12 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
initialized.
\warning The returned texture is not memory managed by the scene graph and
- must be explicitly deleted by the caller on the rendering thread.
- This is achieved by deleting the texture from a QSGNode destructor
- or by using deleteLater() in the case where the texture already has affinity
- to the rendering thread.
+ must be explicitly deleted by the caller on the rendering thread. This is
+ achieved by deleting the texture from a QSGNode destructor or by using
+ deleteLater() in the case where the texture already has affinity to the
+ rendering thread.
- This function can be called from any thread.
+ This function can be called from both the main and the render thread.
\sa sceneGraphInitialized(), QSGTexture
*/
@@ -3024,8 +3192,58 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
return d->context->createTexture(image, flags);
}
+/*!
+ Creates a new QSGTexture from the supplied \a texture.
+
+ Use \a options to customize the texture attributes. Only the
+ TextureHasAlphaChannel flag is taken into account by this function. When
+ set, the resulting QSGTexture is always treated by the scene graph renderer
+ as needing blending. For textures that are fully opaque, not setting the
+ flag can save the cost of performing alpha blending during rendering. The
+ flag has no direct correspondence to the \l{QRhiTexture::format()}{format}
+ of the QRhiTexture, i.e. not setting the flag while having a texture format
+ such as the commonly used \l QRhiTexture::RGBA8 is perfectly normal.
+
+ Mipmapping is not controlled by \a options since \a texture is already
+ created and has the presence or lack of mipmaps baked in.
+
+ The returned QSGTexture owns the QRhiTexture, meaning \a texture is
+ destroyed together with the returned QSGTexture.
+
+ If \a texture owns its underlying native graphics resources (OpenGL texture
+ object, Vulkan image, etc.), that depends on how the QRhiTexture was created
+ (\l{QRhiTexture::create()} or \l{QRhiTexture::createFrom()}), and that is
+ not controlled or changed by this function.
+
+ \note This is only functional when the scene graph has already initialized
+ and is using the default, \l{QRhi}-based \l{Scene Graph
+ Adaptations}{adaptation}. The return value is \nullptr otherwise.
+
+ \note This function can only be called on the scene graph render thread.
+
+ \since 6.6
+
+ \sa createTextureFromImage(), sceneGraphInitialized(), QSGTexture
+ */
+QSGTexture *QQuickWindow::createTextureFromRhiTexture(QRhiTexture *texture, CreateTextureOptions options) const
+{
+ Q_D(const QQuickWindow);
+ if (!d->rhi)
+ return nullptr;
+
+ QSGPlainTexture *t = new QSGPlainTexture;
+ t->setOwnsTexture(true);
+ t->setTexture(texture);
+ t->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel);
+ t->setTextureSize(texture->pixelSize());
+ return t;
+}
+
+// Legacy, private alternative to createTextureFromRhiTexture() that internally
+// creates a QRhiTexture wrapping the existing native graphics resource.
+// New code should prefer using the public API.
QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeObjectHandle,
- int nativeLayout,
+ int nativeLayoutOrState,
uint nativeFormat,
const QSize &size,
QQuickWindow::CreateTextureOptions options,
@@ -3035,7 +3253,7 @@ QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeOb
return nullptr;
QSGPlainTexture *texture = new QSGPlainTexture;
- texture->setTextureFromNativeTexture(rhi, nativeObjectHandle, nativeLayout, nativeFormat,
+ texture->setTextureFromNativeTexture(rhi, nativeObjectHandle, nativeLayoutOrState, nativeFormat,
size, options, flags);
texture->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel);
// note that the QRhiTexture does not (and cannot) own the native object
@@ -3050,6 +3268,10 @@ QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeOb
The background color for the window.
Setting this property is more efficient than using a separate Rectangle.
+
+ \note If you set the color to \c "transparent" or to a color with alpha translucency,
+ you should also set suitable \l flags such as \c {flags: Qt.FramelessWindowHint}.
+ Otherwise, window translucency may not be enabled consistently on all platforms.
*/
/*!
@@ -3136,7 +3358,7 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
buffering of resources, such as buffers, is up to the graphics API client
to manage. Most commonly, a uniform buffer where the data changes between
frames cannot simply change its contents when submitting a frame, given
- that that frame may still be active ("in flight") when starting to record
+ that the frame may still be active ("in flight") when starting to record
the next frame. To avoid stalling the pipeline, one way is to have multiple
buffers (and memory allocations) under the hood, thus realizing at least a
double buffered scheme for such resources.
@@ -3302,10 +3524,12 @@ void QQuickWindow::endExternalCommands()
whether it's a dialog, popup, or a regular window, and whether it should
have a title bar, etc.
- The flags which you read from this property might differ from the ones
+ The flags that you read from this property might differ from the ones
that you set if the requested flags could not be fulfilled.
- \sa Qt::WindowFlags
+ \snippet qml/splashWindow.qml entire
+
+ \sa Qt::WindowFlags, {Qt Quick Examples - Window and Screen}
*/
/*!
@@ -3336,6 +3560,11 @@ void QQuickWindow::endExternalCommands()
The (x,y) position is relative to the \l Screen if there is only one,
or to the virtual desktop (arrangement of multiple screens).
+ \note Not all windowing systems support setting or querying top level
+ window positions. On such a system, programmatically moving windows
+ may not have any effect, and artificial values may be returned for
+ the current positions, such as \c QPoint(0, 0).
+
\qml
Window { x: 100; y: 100; width: 100; height: 100 }
\endqml
@@ -3372,10 +3601,13 @@ void QQuickWindow::endExternalCommands()
Setting visible to false is the same as setting \l visibility to \l {QWindow::}{Hidden}.
+ The default value is \c false, unless overridden by setting \l visibility.
+
\sa visibility
*/
/*!
+ \keyword qml-window-visibility-prop
\qmlproperty QWindow::Visibility Window::visibility
The screen-occupation state of the window.
@@ -3389,15 +3621,18 @@ void QQuickWindow::endExternalCommands()
visibility property you will always get the actual state, never
\c AutomaticVisibility.
- When a window is not visible its visibility is Hidden, and setting
+ When a window is not visible, its visibility is \c Hidden, and setting
visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false.
- \sa visible
+ \snippet qml/windowVisibility.qml entire
+
+ \sa visible, {Qt Quick Examples - Window and Screen}
\since 5.1
*/
/*!
\qmlattachedproperty QWindow::Visibility Window::visibility
+ \readonly
\since 5.4
This attached property holds whether the window is currently shown
@@ -3405,7 +3640,7 @@ void QQuickWindow::endExternalCommands()
hidden. The \c Window attached property can be attached to any Item. If the
item is not shown in any window, the value will be \l {QWindow::}{Hidden}.
- \sa visible, visibility
+ \sa visible, {qml-window-visibility-prop}{visibility}
*/
/*!
@@ -3486,33 +3721,35 @@ void QQuickWindow::endExternalCommands()
shown, that minimizing the parent window will also minimize the transient
window, and so on; however results vary somewhat from platform to platform.
- Normally if you declare a Window inside an Item or inside another Window,
- this relationship is deduced automatically. In that case, if you declare
- this window's \l visible property \c true, it will not actually be shown
- until the \c transientParent window is shown.
-
- However if you set this property, then Qt Quick will no longer wait until
- the \c transientParent window is shown before showing this window. If you
- want to to be able to show a transient window independently of the "parent"
- Item or Window within which it was declared, you can remove that
- relationship by setting \c transientParent to \c null:
-
- \qml
- import QtQuick.Window 2.13
-
- Window {
- // visible is false by default
- Window {
- transientParent: null
- visible: true
- }
- }
- \endqml
+ Declaring a Window inside an Item or another Window, either via the
+ \l{Window::data}{default property} or a dedicated property, will automatically
+ set up a transient parent relationship to the containing window,
+ unless the \l transientParent property is explicitly set. This applies
+ when creating Window items via \l [QML] {QtQml::Qt::createComponent()}
+ {Qt.createComponent} or \l [QML] {QtQml::Qt::createQmlObject()}
+ {Qt.createQmlObject} as well, as long as an Item or Window is passed
+ as the \c parent argument.
+
+ A Window with a transient parent will not be shown until its transient
+ parent is shown, even if the \l visible property is \c true. This also
+ applies for the automatic transient parent relationship described above.
+ In particular, if the Window's containing element is an Item, the window
+ will not be shown until the containing item is added to a scene, via its
+ \l{Concepts - Visual Parent in Qt Quick}{visual parent hierarchy}. Setting
+ the \l transientParent to \c null will override this behavior:
+
+ \snippet qml/nestedWindowTransientParent.qml 0
+ \snippet qml/nestedWindowTransientParent.qml 1
In order to cause the window to be centered above its transient parent by
default, depending on the window manager, it may also be necessary to set
the \l Window::flags property with a suitable \l Qt::WindowType (such as
\c Qt::Dialog).
+
+ If a \l{QtQuick::Window::parent}{visual parent} is set on the Window
+ the visual parent will take precedence over the transientParent.
+
+ \sa QtQuick::Window::parent
*/
/*!
@@ -3553,6 +3790,9 @@ void QQuickWindow::endExternalCommands()
The active status of the window.
+ \snippet qml/windowPalette.qml declaration-and-color
+ \snippet qml/windowPalette.qml closing-brace
+
\sa requestActivate()
*/
@@ -3566,14 +3806,7 @@ void QQuickWindow::endExternalCommands()
Here is an example which changes a label to show the active state of the
window in which it is shown:
- \qml
- import QtQuick 2.4
- import QtQuick.Window 2.2
-
- Text {
- text: Window.active ? "active" : "inactive"
- }
- \endqml
+ \snippet qml/windowActiveAttached.qml entire
*/
/*!
@@ -3838,6 +4071,48 @@ QSGRendererInterface *QQuickWindow::rendererInterface() const
}
/*!
+ \return the QRhi object used by this window for rendering.
+
+ Available only when the window is using Qt's 3D API and shading language
+ abstractions, meaning the result is always null when using the \c software
+ adaptation.
+
+ The result is valid only when rendering has been initialized, which is
+ indicated by the emission of the sceneGraphInitialized() signal. Before
+ that point, the returned value is null. With a regular, on-screen
+ QQuickWindow scenegraph initialization typically happens when the native
+ window gets exposed (shown) the first time. When using QQuickRenderControl,
+ initialization is done in the explicit
+ \l{QQuickRenderControl::initialize()}{initialize()} call.
+
+ In practice this function is a shortcut to querying the QRhi via the
+ QSGRendererInterface.
+
+ \since 6.6
+ */
+QRhi *QQuickWindow::rhi() const
+{
+ Q_D(const QQuickWindow);
+ return d->rhi;
+}
+
+/*!
+ \return the QRhiSwapChain used by this window, if there is one.
+
+ \note Only on-screen windows backed by one of the standard render loops
+ (such as, \c basic or \c threaded) will have a swapchain. Otherwise the
+ returned value is null. For example, the result is always null when the
+ window is used with QQuickRenderControl.
+
+ \since 6.6
+ */
+QRhiSwapChain *QQuickWindow::swapChain() const
+{
+ Q_D(const QQuickWindow);
+ return d->swapchain;
+}
+
+/*!
Requests the specified graphics \a api.
When the built-in, default graphics adaptation is used, \a api specifies
@@ -4114,6 +4389,18 @@ QQuickGraphicsConfiguration QQuickWindow::graphicsConfiguration() const
}
/*!
+ Creates a text node. When the scenegraph is not initialized, the return value is null.
+
+ \since 6.7
+ \sa QSGTextNode
+ */
+QSGTextNode *QQuickWindow::createTextNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createTextNode(d->context) : nullptr;
+}
+
+/*!
Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
This is cross-backend alternative to constructing a QSGSimpleRectNode directly.
@@ -4191,10 +4478,11 @@ void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType)
palette which serves as a default for all application windows. You can also set the default palette
for windows by passing a custom palette to QGuiApplication::setPalette(), before loading any QML.
- ApplicationWindow propagates explicit palette properties to child controls. If you change a specific
- property on the window's palette, that property propagates to all child controls in the window,
+ Window propagates explicit palette properties to child items and controls,
overriding any system defaults for that property.
+ \snippet qml/windowPalette.qml entire
+
\sa Item::palette, Popup::palette, ColorGroup, SystemPalette
//! internal \sa QQuickAbstractPaletteProvider, QQuickPalette
*/
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 3f3c82a4b8..17c4bb6fd4 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -33,6 +33,10 @@ class QQuickPalette;
class QQuickRenderTarget;
class QQuickGraphicsDevice;
class QQuickGraphicsConfiguration;
+class QRhi;
+class QRhiSwapChain;
+class QRhiTexture;
+class QSGTextNode;
class Q_QUICK_EXPORT QQuickWindow : public QWindow
{
@@ -78,7 +82,8 @@ public:
enum TextRenderType {
QtTextRendering,
- NativeTextRendering
+ NativeTextRendering,
+ CurveTextRendering
};
Q_ENUM(TextRenderType)
@@ -115,6 +120,7 @@ public:
// Scene graph specific functions
QSGTexture *createTextureFromImage(const QImage &image) const;
QSGTexture *createTextureFromImage(const QImage &image, CreateTextureOptions options) const;
+ QSGTexture *createTextureFromRhiTexture(QRhiTexture *texture, CreateTextureOptions options = {}) const;
void setColor(const QColor &color);
QColor color() const;
@@ -151,10 +157,14 @@ public:
QSGRectangleNode *createRectangleNode() const;
QSGImageNode *createImageNode() const;
QSGNinePatchNode *createNinePatchNode() const;
+ QSGTextNode *createTextNode() const;
static TextRenderType textRenderType();
static void setTextRenderType(TextRenderType renderType);
+ QRhi *rhi() const;
+ QRhiSwapChain *swapChain() const;
+
Q_SIGNALS:
void frameSwapped();
void sceneGraphInitialized();
@@ -220,7 +230,6 @@ private Q_SLOTS:
void cleanupSceneGraph();
void physicalDpiChanged();
void handleScreenChanged(QScreen *screen);
- void setTransientParent_helper(QQuickWindow *window);
void runJobsAfterSwap();
void handleApplicationStateChanged(Qt::ApplicationState state);
void handleFontDatabaseChanged();
@@ -234,6 +243,7 @@ private:
#endif
friend class QQuickItem;
+ friend class QQuickItemPrivate;
friend class QQuickWidget;
friend class QQuickRenderControl;
friend class QQuickAnimatorController;
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 57020eaa3e..8ba4e56515 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -56,8 +56,10 @@ class QRhiRenderBuffer;
class QRhiRenderPassDescriptor;
class QRhiTexture;
+Q_DECLARE_LOGGING_CATEGORY(lcQuickWindow)
+
//Make it easy to identify and customize the root item if needed
-class Q_QUICK_PRIVATE_EXPORT QQuickRootItem : public QQuickItem
+class Q_QUICK_EXPORT QQuickRootItem : public QQuickItem
{
Q_OBJECT
QML_ANONYMOUS
@@ -70,20 +72,39 @@ public Q_SLOTS:
void setHeight(int h) {QQuickItem::setHeight(qreal(h));}
};
-class QQuickWindowRenderTarget
+struct QQuickWindowRenderTarget
{
-public:
- void reset(QRhi *rhi);
- QRhiRenderTarget *renderTarget = nullptr;
- QRhiRenderPassDescriptor *rpDesc = nullptr;
- QRhiTexture *texture = nullptr;
- QRhiRenderBuffer *renderBuffer = nullptr;
- QRhiRenderBuffer *depthStencil = nullptr;
- QPaintDevice *paintDevice = nullptr;
- bool owns = false;
+ enum class ResetFlag {
+ KeepImplicitBuffers = 0x01
+ };
+ Q_DECLARE_FLAGS(ResetFlags, ResetFlag)
+ void reset(QRhi *rhi, ResetFlags flags = {});
+
+ struct {
+ QRhiRenderTarget *renderTarget = nullptr;
+ bool owns = false;
+ int multiViewCount = 1;
+ } rt;
+ struct {
+ QRhiTexture *texture = nullptr;
+ QRhiRenderBuffer *renderBuffer = nullptr;
+ QRhiRenderPassDescriptor *rpDesc = nullptr;
+ } res;
+ struct ImplicitBuffers {
+ QRhiRenderBuffer *depthStencil = nullptr;
+ QRhiTexture *depthStencilTexture = nullptr;
+ QRhiTexture *multisampleTexture = nullptr;
+ void reset(QRhi *rhi);
+ } implicitBuffers;
+ struct {
+ QPaintDevice *paintDevice = nullptr;
+ bool owns = false;
+ } sw;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickWindowPrivate
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickWindowRenderTarget::ResetFlags)
+
+class Q_QUICK_EXPORT QQuickWindowPrivate
: public QWindowPrivate
, public QQuickPaletteProviderPrivateBase<QQuickWindow, QQuickWindowPrivate>
{
@@ -101,6 +122,8 @@ public:
QQuickWindowPrivate();
~QQuickWindowPrivate() override;
+ void setPalette(QQuickPalette *p) override;
+ void updateWindowPalette();
void updateChildrenPalettes(const QPalette &parentPalette) override;
void init(QQuickWindow *, QQuickRenderControl *control = nullptr);
@@ -123,6 +146,7 @@ public:
#endif
void clearFocusObject() override;
+ void setFocusToTarget(FocusTarget, Qt::FocusReason) override;
void dirtyItem(QQuickItem *);
void cleanup(QSGNode *);
@@ -146,17 +170,17 @@ public:
Q_DECLARE_FLAGS(TextureFromNativeTextureFlags, TextureFromNativeTextureFlag)
QSGTexture *createTextureFromNativeTexture(quint64 nativeObjectHandle,
- int nativeLayout,
+ int nativeLayoutOrState,
uint nativeFormat,
const QSize &size,
QQuickWindow::CreateTextureOptions options,
TextureFromNativeTextureFlags flags = {}) const;
QSGTexture *createTextureFromNativeTexture(quint64 nativeObjectHandle,
- int nativeLayout,
+ int nativeLayoutOrState,
const QSize &size,
QQuickWindow::CreateTextureOptions options,
TextureFromNativeTextureFlags flags = {}) const {
- return createTextureFromNativeTexture(nativeObjectHandle, nativeLayout, 0, size, options, flags);
+ return createTextureFromNativeTexture(nativeObjectHandle, nativeLayoutOrState, 0, size, options, flags);
}
QQuickItem::UpdatePaintNodeData updatePaintNodeData;
@@ -179,6 +203,13 @@ public:
void fireFrameSwapped() { Q_EMIT q_func()->frameSwapped(); }
void fireAboutToStop() { Q_EMIT q_func()->sceneGraphAboutToStop(); }
+ void clearGrabbers(QPointerEvent *event);
+
+ void updateChildWindowStackingOrder(QQuickItem *item = nullptr);
+
+ int multiViewCount();
+ QRhiRenderTarget *activeCustomRhiRenderTarget();
+
QSGRenderContext *context;
QSGRenderer *renderer;
QByteArray visualizationMode; // Default renderer supports "clip", "overdraw", "changes", "batches" and blank.
@@ -191,7 +222,6 @@ public:
uint persistentGraphics : 1;
uint persistentSceneGraph : 1;
- uint componentCompleted : 1;
uint inDestructor : 1;
// Storage for setRenderTarget(QQuickRenderTarget).
@@ -258,6 +288,7 @@ public:
uint updatesEnabled : 1;
bool pendingFontUpdate = false;
bool windowEventDispatch = false;
+ QPointer<QQuickPalette> windowPaletteRef;
private:
static void cleanupNodesOnShutdown(QQuickItem *);
diff --git a/src/quick/items/qquickwindowattached_p.h b/src/quick/items/qquickwindowattached_p.h
index 8f11e6f4df..6cb0dd6433 100644
--- a/src/quick/items/qquickwindowattached_p.h
+++ b/src/quick/items/qquickwindowattached_p.h
@@ -24,17 +24,17 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickWindow;
-class Q_QUICK_PRIVATE_EXPORT QQuickWindowAttached : public QObject
+class Q_QUICK_EXPORT QQuickWindowAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QWindow::Visibility visibility READ visibility NOTIFY visibilityChanged)
- Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
- Q_PROPERTY(QQuickItem* activeFocusItem READ activeFocusItem NOTIFY activeFocusItemChanged)
- Q_PROPERTY(QQuickItem* contentItem READ contentItem NOTIFY contentItemChanged)
- Q_PROPERTY(int width READ width NOTIFY widthChanged)
- Q_PROPERTY(int height READ height NOTIFY heightChanged)
- Q_PROPERTY(QQuickWindow *window READ window NOTIFY windowChanged)
+ Q_PROPERTY(QWindow::Visibility visibility READ visibility NOTIFY visibilityChanged FINAL)
+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged FINAL)
+ Q_PROPERTY(QQuickItem* activeFocusItem READ activeFocusItem NOTIFY activeFocusItemChanged FINAL)
+ Q_PROPERTY(QQuickItem* contentItem READ contentItem NOTIFY contentItemChanged FINAL)
+ Q_PROPERTY(int width READ width NOTIFY widthChanged FINAL)
+ Q_PROPERTY(int height READ height NOTIFY heightChanged FINAL)
+ Q_PROPERTY(QQuickWindow *window READ window NOTIFY windowChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
diff --git a/src/quick/items/qquickwindowcontainer.cpp b/src/quick/items/qquickwindowcontainer.cpp
new file mode 100644
index 0000000000..839a30330d
--- /dev/null
+++ b/src/quick/items/qquickwindowcontainer.cpp
@@ -0,0 +1,606 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickwindowcontainer_p.h"
+
+#include <QtQuick/qquickrendercontrol.h>
+
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickrectangle_p.h>
+#include <QtQuick/private/qquickwindowmodule_p.h>
+#include <QtQuick/private/qquickimplicitsizeitem_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcWindowContainer, "qt.quick.window.container")
+
+using namespace Qt::StringLiterals;
+
+/*!
+ \qmltype WindowContainer
+ \inqmlmodule QtQuick
+ \ingroup qtquick-visual
+ \inherits Item
+ \instantiates QQuickItem
+ \since 6.7
+ \preliminary
+
+ \brief Allows embedding arbitrary QWindows into a Qt Quick scene.
+
+ The window will become a child of the item's window,
+ with its position, size, z-order, etc. managed by the item.
+
+ Sibling items with a higher z-order than the window container
+ will not automatically overlap the embedded window, as the
+ window lives on top of the Qt Quick scene. To work around this,
+ place the sibling items inside their own dedicated child window:
+
+ \code
+ Item {
+ id: someItem
+ WindowContainer {
+ window: foreignWindow
+ }
+ Window {
+ parent: someItem
+ Item {
+ id: siblingItem
+ }
+ }
+ }
+ \endcode
+
+ Similarly, child Items of the window container will not automatically
+ overlap the embedded window. To work around this, place the child
+ item inside a dedicated child window.
+
+ \code
+ Item {
+ id: someItem
+ WindowContainer {
+ id: windowContainer
+ window: foreignWindow
+ Window {
+ parent: windowContainer
+ Item {
+ id: childItem
+ }
+ }
+ }
+ }
+ \endcode
+
+ If positioning and sizing of a Window via anchors is required,
+ the Window can be wrapped in a window container:
+
+ \code
+ Item {
+ id: someItem
+ WindowContainer {
+ anchors.fill: parent
+ window: Window {
+ Item {
+ }
+ }
+ }
+ }
+ \endcode
+
+ \note The window container does not interoperate with QQuickWidget,
+ QQuickWindow::setRenderTarget(), QQuickRenderControl, or similar
+ functionality.
+
+ \sa {QtQuick::Window::parent}
+*/
+
+/*!
+ \qmlproperty QWindow QtQuick::WindowContainer::window
+
+ This property holds the window to embed.
+*/
+
+class QQuickWindowContainerPrivate : public QQuickImplicitSizeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickWindowContainer)
+protected:
+ bool transformChanged(QQuickItem *transformedItem) override;
+
+public:
+ QWindow *window = nullptr;
+ QQuickWindowContainer::ContainerMode containerMode;
+};
+
+/*!
+ \internal
+
+ Creates a new window container.
+
+ The container mode determines who has the last word in what the state
+ of the contained window should be. If the window container is explicitly
+ requested by the user via WindowContainer, the properties are set on the
+ item, and the embedded window should match that. If the window container
+ is implicitly created by setting a visual parent on a Window, the properties
+ are set on the Window, and the window container should respect that.
+*/
+QQuickWindowContainer::QQuickWindowContainer(QQuickItem *parent, ContainerMode containerMode)
+ : QQuickImplicitSizeItem(*(new QQuickWindowContainerPrivate), parent)
+{
+ Q_D(QQuickWindowContainer);
+
+ qCDebug(lcWindowContainer).verbosity(1) << "Creating window container"
+ << this << "with parent" << parent << "and" << containerMode;
+
+ d->containerMode = containerMode;
+
+ setFlag(QQuickItem::ItemObservesViewport); // For clipping
+
+ connect(this, &QQuickItem::windowChanged,
+ this, &QQuickWindowContainer::parentWindowChanged);
+
+ if (lcWindowContainer().isDebugEnabled()) {
+ auto *debugRectangle = new QQuickRectangle(this);
+ debugRectangle->setColor(QColor(255, 0, 255, 20));
+ auto *border = debugRectangle->border();
+ border->setColor(Qt::magenta);
+ border->setWidth(1.0);
+ QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(debugRectangle);
+ rectPrivate->anchors()->setFill(this);
+ }
+}
+
+QQuickWindowContainer::~QQuickWindowContainer()
+{
+ Q_D(const QQuickWindowContainer);
+ qCDebug(lcWindowContainer) << "Destructing window container" << this;
+
+ disconnect(this);
+ if (d->window) {
+ auto ownership = QJSEngine::objectOwnership(d->window);
+ qCDebug(lcWindowContainer) << "Contained window" << d->window
+ << "has" << (ownership == QQmlEngine::JavaScriptOwnership ?
+ "JavaScript" : "C++") << "ownership";
+ if (ownership == QQmlEngine::JavaScriptOwnership) {
+ delete d->window;
+ } else {
+ d->window->destroy();
+ d->window->setParent(nullptr);
+ }
+ }
+}
+
+void QQuickWindowContainer::releaseResources()
+{
+ Q_D(const QQuickWindowContainer);
+ qCDebug(lcWindowContainer) << "Destroying" << d->window
+ << "with platform window" << (d->window ? d->window->handle() : nullptr);
+ if (d->window)
+ d->window->destroy();
+}
+
+void QQuickWindowContainer::classBegin()
+{
+ qCDebug(lcWindowContainer) << "Class begin for" << this;
+
+ QQuickImplicitSizeItem::classBegin();
+}
+
+void QQuickWindowContainer::componentComplete()
+{
+ Q_D(const QQuickWindowContainer);
+
+ qCDebug(lcWindowContainer) << "Component completed for" << this;
+ QQuickImplicitSizeItem::componentComplete();
+
+ if (d->window)
+ initializeContainedWindow();
+}
+
+QWindow *QQuickWindowContainer::containedWindow() const
+{
+ Q_D(const QQuickWindowContainer);
+ return d->window;
+}
+
+void QQuickWindowContainer::setContainedWindow(QWindow *window)
+{
+ qCDebug(lcWindowContainer) << "Setting contained window for" << this << "to" << window;
+
+ Q_D(QQuickWindowContainer);
+
+ if (window == d->window)
+ return;
+
+ if (auto *previousWindow = d->window) {
+ qCDebug(lcWindowContainer) << "Decoupling container from" << d->window;
+ previousWindow->disconnect(this);
+ previousWindow->removeEventFilter(this);
+ previousWindow->setParent(nullptr);
+ }
+
+ d->window = window;
+
+ if (d->window) {
+ if (d->containerMode == ItemControlsWindow) {
+ if (auto *quickWindow = qobject_cast<QQuickWindowQmlImpl*>(d->window)) {
+ // Make sure the Window reflects the window container as its visual parent
+ quickWindow->setVisualParent(this);
+ }
+ }
+
+ // When the window controls the container, we need to reflect any changes
+ // in the window back to the container, so they stay in sync. And when the
+ // container controls the window, we still want to reflect width/height as
+ // new implicit size, and override any other changes with the item state.
+ connect(d->window, &QWindow::xChanged, this, &QQuickWindowContainer::windowUpdated);
+ connect(d->window, &QWindow::yChanged, this, &QQuickWindowContainer::windowUpdated);
+ connect(d->window, &QWindow::widthChanged, this, &QQuickWindowContainer::windowUpdated);
+ connect(d->window, &QWindow::heightChanged, this, &QQuickWindowContainer::windowUpdated);
+ connect(d->window, &QWindow::visibleChanged, this, &QQuickWindowContainer::windowUpdated);
+
+ connect(d->window, &QObject::destroyed, this, &QQuickWindowContainer::windowDestroyed);
+
+ d->window->installEventFilter(this);
+
+ if (d->componentComplete)
+ initializeContainedWindow();
+ } else {
+ // Reset state based on not having a window
+ syncWindowToItem();
+ }
+
+ emit containedWindowChanged(d->window);
+}
+
+void QQuickWindowContainer::initializeContainedWindow()
+{
+ Q_D(const QQuickWindowContainer);
+ Q_ASSERT(d->componentComplete);
+ Q_ASSERT(d->window);
+
+ qCDebug(lcWindowContainer) << "Doing initial sync between" << d->window << "and" << this;
+
+ syncWindowToItem();
+ polish();
+}
+
+static QTransform sanitizeTransform(const QTransform &transform)
+{
+ if (transform.isRotating()) {
+ // FIXME: Can we keep more here?
+ return QTransform::fromTranslate(transform.dx(), transform.dy());
+ }
+
+ return transform;
+}
+
+void QQuickWindowContainer::syncWindowToItem()
+{
+ Q_D(const QQuickWindowContainer);
+
+ const auto windowGeometry = d->window ? d->window->geometry() : QRect();
+
+ qCDebug(lcWindowContainer) << "Syncing window state from" << d->window
+ << "with geometry" << windowGeometry << "to" << this
+ << "with mode" << d->containerMode;
+
+ const auto transform = sanitizeTransform(d->windowToItemTransform());
+
+ // The window might have a larger size than the item's natural
+ // size, if there's a scale applied somewhere in the hierarchy.
+ auto itemSize = d->window ? transform.mapRect(windowGeometry).size()
+ : QSize();
+
+ if (d->containerMode == WindowControlsItem) {
+ // When the Window controls the window container the position is
+ // set up front, when creating the window container, and from that
+ // point on set exclusively via the window container, so we skip
+ // setting the position here, and only set the size.
+ setSize(itemSize);
+ setVisible(d->window ? d->window->isVisible() : false);
+ } else {
+ // Position defined by item, so don't sync from window
+ // Visible defined by item, so don't sync from window
+ setImplicitWidth(itemSize.width());
+ setImplicitHeight(itemSize.height());
+ }
+}
+
+/*!
+ \internal
+
+ updatePolish() should perform any layout as required for this item.
+
+ For us, that means propagating the item's state to the window.
+*/
+void QQuickWindowContainer::updatePolish()
+{
+ Q_D(QQuickWindowContainer);
+
+ qCDebug(lcWindowContainer) << "Propagating" << this << "state"
+ << "to" << d->window;
+
+ auto *parentWindow = window();
+
+ // FIXME: If we are part of a QQuickWidget, we have a QQuickRenderControl,
+ // and should look up the parent window via that, and apply the offset we
+ // get to the item transform below. But at the moment it's not possible
+ // to observe changes to the offset, which is critical to support this
+ // for child windows.
+
+ if (!d->window || !parentWindow)
+ return;
+
+ if (d->window->parent() != parentWindow) {
+ qCDebug(lcWindowContainer) << "Updating window parent to" << parentWindow;
+ d->window->setParent(parentWindow);
+ }
+
+ auto transform = sanitizeTransform(d->itemToWindowTransform());
+
+ // Find the window's geometry, based on the item's bounding rect,
+ // mapped to the scene. The mapping includes any x/y position set
+ // on the item itself, as well as any transforms applied to the item
+ // or its ancestor (scale, translation).
+ const QRectF itemSceneRect = transform.mapRect(boundingRect());
+ // FIXME: Rounding to a QRect here means we'll have some jitter or off
+ // placement when the underlying item is not on a integer coordinate.
+ QRect windowGeometry = itemSceneRect.toRect();
+ if (windowGeometry != d->window->geometry()) {
+ QRectF itemRect(position(), size());
+ qCDebug(lcWindowContainer) << "Updating window geometry to" << windowGeometry
+ << "based on item rect" << itemRect << "and scene rect" << itemSceneRect;
+ d->window->setGeometry(windowGeometry);
+ }
+
+ // Clip the container to its own and ancestor clip rects, by setting
+ // a mask on the window. This does not necessarily clip native windows,
+ // as QWindow::setMask() is not guaranteed to visually clip the window,
+ // only to mask input, but in most cases we should be good. For the
+ // cases where this fails, we can potentially use an intermediate window
+ // as parent of the contained window, if the platform allows clipping
+ // child windows to parent window geometry. We do not want to resize the
+ // contained window, as that will just fill the content into a smaller
+ // area.
+ const auto clipMask = [&]{
+ if (clipRect() == boundingRect())
+ return QRect();
+
+ // The clip rect has all the relevant transforms applied to it,
+ // except for the item's own scale. As the mask is in window
+ // local coordinates in the possibly scaled window, we need
+ // to apply the scale manually.
+ auto scaleTransform = QTransform::fromScale(transform.m11(), transform.m22());
+ auto rect = scaleTransform.mapRect(clipRect()).toRect();
+
+ // An empty clip rect means clip away everything, while for a
+ // window, an empty mask means mask nothing. Fake the former
+ // by setting a mask outside of the window's bounds. We have
+ // to do this check after rounding the clip rect to a QRect.
+ // FIXME: Verify this works on all platforms
+ if (rect.isEmpty())
+ return QRect(-1, -1, 1, 1);
+
+ return rect;
+ }();
+
+ if (clipMask != d->window->mask().boundingRect()) {
+ qCDebug(lcWindowContainer) << "Updating window clip mask to" << clipMask
+ << "based on clip rect" << clipRect();
+ d->window->setMask(clipMask);
+ }
+
+ // FIXME: Opacity support. Need to calculate effective opacity ourselves,
+ // and there doesn't seem to be any existing observer for opacity changes.
+ // Not all platforms implement opacity for child windows yet.
+
+ // FIXME: If a scale is applied to the item or its parents, we end up
+ // with a bigger item, and window, but we don't translate the scale to
+ // an increase device-pixel-ratio of the window. As a result, the window
+ // will likely just render more content, instead of the same content at
+ // a potentially higher density.
+
+ if (d->window->isVisible() != isVisible()) {
+ qCDebug(lcWindowContainer) << "Updating window visibility"
+ << "based on item visible" << isVisible();
+ d->window->setVisible(isVisible());
+ }
+}
+
+/*!
+ \internal
+
+ QQuickItem::clipRect() doesn't take ItemClipsChildrenToShape into
+ account, so a parent item that has clip:false, but ItemIsViewport
+ will still result in affecting the clip.
+
+ We want to stay consistent with the clipping in the scene graph,
+ which is based on QQuickItem::clip(), so we override the clipRect
+ to take ItemClipsChildrenToShape into account.
+*/
+QRectF QQuickWindowContainer::clipRect() const
+{
+ QRectF rect = boundingRect();
+
+ for (auto *viewport = viewportItem(); viewport; viewport = viewport->viewportItem()) {
+ if (viewport == this)
+ break;
+
+ if (viewport->flags().testFlag(QQuickItem::ItemClipsChildrenToShape)) {
+ // FIXME: This fails to take into account viewports that override clipRect()
+ const auto mappedViewportRect = mapRectFromItem(viewport, viewport->boundingRect());
+ rect = mappedViewportRect.intersected(rect);
+ }
+
+ if (viewport->viewportItem() == viewport)
+ break; // Content item returns itself as viewport
+ }
+
+ return rect;
+}
+
+// ----------------------- Window updates -----------------------
+
+/*!
+ \internal
+
+ Called when the contained QWindow is changed.
+
+ Depending on the sync mode we need to reflect these changes
+ to the item, or override them by applying the item state.
+*/
+void QQuickWindowContainer::windowUpdated()
+{
+ Q_D(const QQuickWindowContainer);
+
+ if (lcWindowContainer().isDebugEnabled()) {
+ auto metaMethod = sender()->metaObject()->method(senderSignalIndex());
+ auto signalName = QString::fromUtf8(metaMethod.name());
+ qCDebug(lcWindowContainer).noquote() << d->window << signalName;
+ }
+
+ syncWindowToItem();
+
+ if (d->containerMode == ItemControlsWindow) {
+ qCDebug(lcWindowContainer) << "Overriding window state by polishing";
+ // Ideally we'd always call ensurePolished() here, to synchronously
+ // override the window state ASAP, rather than wait for polish to
+ // trigger it asynchronously, but due to QWindowPrivate::setVisible
+ // emitting visibleChanged before updating the platform window, we
+ // end up applying our override temporarily, only to have QWindowPrivate
+ // follow up with the original change to the platform window.
+ if (d->window->isVisible() != isVisible())
+ polish();
+ else
+ ensurePolished();
+ }
+}
+
+bool QQuickWindowContainer::eventFilter(QObject *object, QEvent *event)
+{
+ Q_D(const QQuickWindowContainer);
+ Q_ASSERT(object == d->window);
+
+ if (event->type() == QEvent::PlatformSurface) {
+ auto type = static_cast<QPlatformSurfaceEvent*>(event)->surfaceEventType();
+ if (type == QPlatformSurfaceEvent::SurfaceCreated) {
+ qCDebug(lcWindowContainer) << "Surface created for" << object;
+ syncWindowToItem();
+ // The surface creation has already resulted in the native window
+ // being added to its parent, on top of all other windows. We need
+ // to do a synchronous re-stacking of the windows here, to avoid
+ // leaving the window in the wrong position while waiting for the
+ // asynchronous callback to QQuickWindow::polishItems().
+ if (auto *quickWindow = qobject_cast<QQuickWindow*>(window()))
+ QQuickWindowPrivate::get(quickWindow)->updateChildWindowStackingOrder();
+ }
+ }
+
+ return QQuickImplicitSizeItem::eventFilter(object, event);
+}
+
+void QQuickWindowContainer::windowDestroyed()
+{
+ Q_D(QQuickWindowContainer);
+ qCDebug(lcWindowContainer) << "Window" << (void*)d->window << "destroyed";
+
+ d->window->removeEventFilter(this);
+ d->window = nullptr;
+
+ syncWindowToItem(); // Reset state based on not having a window
+ emit containedWindowChanged(d->window);
+}
+
+// ----------------------- Item updates -----------------------
+
+/*!
+ \internal
+
+ Called when the item's geometry has changed
+*/
+void QQuickWindowContainer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ qCDebug(lcWindowContainer) << this << "geometry changed from"
+ << oldGeometry << "to" << newGeometry;
+
+ QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
+ if (newGeometry.isValid())
+ polish();
+}
+
+/*!
+ \internal
+
+ Called when the item's (effective) state has changed
+*/
+void QQuickWindowContainer::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
+{
+ switch (change) {
+ case ItemVisibleHasChanged:
+ qCDebug(lcWindowContainer) << "Visible changed for" << this << "to" << isVisible();
+ polish();
+ break;
+ default:
+ break;
+ }
+
+ QQuickImplicitSizeItem::itemChange(change, data);
+}
+
+/*!
+ \internal
+
+ Called when the window container item is moved to another window
+*/
+void QQuickWindowContainer::parentWindowChanged(QQuickWindow *parentWindow)
+{
+ qCDebug(lcWindowContainer) << this << "parent window changed to" << parentWindow;
+
+ Q_D(QQuickWindowContainer);
+
+ if (!parentWindow) {
+ // We have been removed from the window we were part of,
+ // possibly because the window is going away. We need to
+ // make sure the contained window is no longer a child of
+ // former window, as otherwise it will be wiped out along
+ // with it. We can't wait for updatePolish() to do that
+ // as polish has no effect when an item is not part of a
+ // window.
+ if (d->window) {
+ // The window should already be destroyed from the
+ // call to releaseResources(), which is part of the
+ // removal of an item from a scene, but just in case
+ // we do it here as well.
+ d->window->destroy();
+
+ d->window->setParent(nullptr);
+ }
+ } else {
+ polish();
+ }
+}
+
+bool QQuickWindowContainerPrivate::transformChanged(QQuickItem *transformedItem)
+{
+ Q_Q(QQuickWindowContainer);
+
+ if (this->window) {
+ auto *transformedItemPrivate = QQuickItemPrivate::get(transformedItem);
+ qCDebug(lcWindowContainer) << "Transform changed for" << transformedItem
+ << "with dirty state" << transformedItemPrivate->dirtyToString();
+
+ if (transformedItemPrivate->dirtyAttributes
+ & QQuickItemPrivate::BasicTransform) {
+ // For some reason scale transforms, which result in the window
+ // being resized, end up with the window lagging a frame or two
+ // behind the item. Polish synchronously instead, to mitigate
+ // this, even if it may result in the opposite situation.
+ q->ensurePolished();
+ } else {
+ q->polish();
+ }
+ }
+
+ return QQuickItemPrivate::transformChanged(transformedItem);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindowcontainer_p.h b/src/quick/items/qquickwindowcontainer_p.h
new file mode 100644
index 0000000000..5da0c6ba27
--- /dev/null
+++ b/src/quick/items/qquickwindowcontainer_p.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKWINDOWCONTAINER_P_H
+#define QQUICKWINDOWCONTAINER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+
+#include <QtCore/private/qobject_p.h>
+
+#include <QtQuick/private/qquickimplicitsizeitem_p.h>
+#include <QtQuick/qquickwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickWindowContainerPrivate;
+class Q_QUICK_EXPORT QQuickWindowContainer : public QQuickImplicitSizeItem
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(WindowContainer)
+ Q_PROPERTY(QWindow *window READ containedWindow WRITE setContainedWindow
+ NOTIFY containedWindowChanged DESIGNABLE false FINAL)
+
+ QML_ADDED_IN_VERSION(6, 7)
+
+public:
+ enum ContainerMode {
+ WindowControlsItem,
+ ItemControlsWindow
+ };
+
+ explicit QQuickWindowContainer(QQuickItem *parent = nullptr,
+ ContainerMode containerMode = ItemControlsWindow);
+ ~QQuickWindowContainer();
+
+ QWindow *containedWindow() const;
+ void setContainedWindow(QWindow *window);
+ Q_SIGNAL void containedWindowChanged(QWindow *window);
+
+protected:
+ void classBegin() override;
+ void componentComplete() override;
+
+ void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &) override;
+
+ void updatePolish() override;
+
+ bool eventFilter(QObject *object, QEvent *event) override;
+
+ QRectF clipRect() const override;
+
+ void releaseResources() override;
+
+private:
+ Q_DECLARE_PRIVATE(QQuickWindowContainer)
+ friend class QQuickWindowQmlImpl;
+
+ void initializeContainedWindow();
+ void windowUpdated();
+ void syncWindowToItem();
+ void parentWindowChanged(QQuickWindow *window);
+ void windowDestroyed();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKWINDOWCONTAINER_P_H
diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp
index 8a815569fe..e539880452 100644
--- a/src/quick/items/qquickwindowmodule.cpp
+++ b/src/quick/items/qquickwindowmodule.cpp
@@ -20,6 +20,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
QQuickWindowQmlImplPrivate::QQuickWindowQmlImplPrivate() = default;
@@ -29,30 +31,42 @@ QQuickWindowQmlImpl::QQuickWindowQmlImpl(QWindow *parent)
{
}
-void QQuickWindowQmlImpl::setVisible(bool visible)
+QQuickWindowQmlImpl::QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow *parent)
+ : QQuickWindow(dd, parent)
{
- Q_D(QQuickWindowQmlImpl);
- d->visible = visible;
- if (d->complete && (!transientParent() || transientParentVisible()))
- QQuickWindow::setVisible(visible);
-}
+ connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged);
+ connect(this, &QWindow::visibilityChanged, this, [&]{
+ Q_D(QQuickWindowQmlImpl);
+ // Update the window's actual visibility and turn off visibilityExplicitlySet,
+ // so that future applyWindowVisibility() calls do not apply both window state
+ // and visible state, unless setVisibility() is called again by the user.
+ d->visibility = QWindow::visibility();
+ d->visibilityExplicitlySet = false;
+ emit QQuickWindowQmlImpl::visibilityChanged(d->visibility);
+ });
+ connect(this, &QWindow::screenChanged, this, &QQuickWindowQmlImpl::screenChanged);
-void QQuickWindowQmlImpl::setVisibility(Visibility visibility)
-{
- Q_D(QQuickWindowQmlImpl);
- d->visibility = visibility;
- if (d->complete)
- QQuickWindow::setVisibility(visibility);
+ // We shadow the x and y properties, so that we can re-map them in case
+ // we have an Item as our visual parent, which will result in creating an
+ // implicit window container that we control. Ensure that signals still work,
+ // and that they reflect the mapped values if a window container is used.
+ QObject::connect(this, &QWindow::xChanged, this, [this] { emit xChanged(x()); });
+ QObject::connect(this, &QWindow::yChanged, this, [this] { emit yChanged(y()); });
}
-QQuickWindowAttached *QQuickWindowQmlImpl::qmlAttachedProperties(QObject *object)
+QQuickWindowQmlImpl::~QQuickWindowQmlImpl()
{
- return new QQuickWindowAttached(object);
+ // Destroy the window while we are still alive, so that any signals
+ // emitted by the destruction can be delivered properly.
+ destroy();
}
void QQuickWindowQmlImpl::classBegin()
{
Q_D(QQuickWindowQmlImpl);
+ qCDebug(lcQuickWindow) << "Class begin for" << this;
+ d->componentComplete = false;
+
QQmlEngine* e = qmlEngine(this);
QQmlEngine::setContextForObject(contentItem(), e->rootContext());
@@ -66,89 +80,453 @@ void QQuickWindowQmlImpl::classBegin()
// The content item has CppOwnership policy (set in QQuickWindow). Ensure the presence of a JS
// wrapper so that the garbage collector can see the policy.
QV4::ExecutionEngine *v4 = e->handle();
- QV4::QObjectWrapper::wrap(v4, d->contentItem);
+ QV4::QObjectWrapper::ensureWrapper(v4, d->contentItem);
}
}
void QQuickWindowQmlImpl::componentComplete()
{
Q_D(QQuickWindowQmlImpl);
- d->complete = true;
- QQuickItem *itemParent = qmlobject_cast<QQuickItem *>(QObject::parent());
- const bool transientParentAlreadySet = QQuickWindowPrivate::get(this)->transientParentPropertySet;
- if (!transientParentAlreadySet && itemParent && !itemParent->window()) {
- qCDebug(lcTransient) << "window" << title() << "has invisible Item parent" << itemParent << "transientParent"
- << transientParent() << "declared visibility" << d->visibility << "; delaying show";
- connect(itemParent, &QQuickItem::windowChanged, this,
- &QQuickWindowQmlImpl::setWindowVisibility, Qt::QueuedConnection);
- } else if (transientParent() && !transientParent()->isVisible()) {
- connect(transientParent(), &QQuickWindow::visibleChanged, this,
- &QQuickWindowQmlImpl::setWindowVisibility, Qt::QueuedConnection);
- } else {
- setWindowVisibility();
- }
+ qCDebug(lcQuickWindow) << "Component completed for" << this;
+ d->componentComplete = true;
+
+ applyVisualParent();
+
+ // Apply automatic transient parent if needed, and opt in to future
+ // parent change events, so we can keep the transient parent in sync.
+ updateTransientParent();
+ d->receiveParentEvents = true;
+
+ applyWindowVisibility();
+
+ // If the transient parent changes, and we've deferred making
+ // the window visible, we need to re-evaluate our decision.
+ connect(this, &QWindow::transientParentChanged,
+ this, &QQuickWindowQmlImpl::applyWindowVisibility);
}
-QQuickWindowQmlImpl::QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow *parent)
- : QQuickWindow(dd, parent)
+void QQuickWindowQmlImpl::setVisible(bool visible)
{
- // These two signals are called during QWindow's dtor, thus they have to be queued connections
- // or else our slots will be called instantly when our destructor has already run but our
- // connections haven't been removed yet.
- connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged,
- Qt::QueuedConnection);
- connect(this, &QWindow::visibilityChanged, this, &QQuickWindowQmlImpl::visibilityChanged,
- Qt::QueuedConnection);
+ Q_D(QQuickWindowQmlImpl);
+ d->visible = visible;
+ d->visibleExplicitlySet = true;
+ if (d->componentComplete)
+ applyWindowVisibility();
+}
- connect(this, &QWindow::screenChanged, this, &QQuickWindowQmlImpl::screenChanged);
+void QQuickWindowQmlImpl::setVisibility(Visibility visibility)
+{
+ Q_D(QQuickWindowQmlImpl);
+ d->visibility = visibility;
+ d->visibilityExplicitlySet = true;
+ if (d->componentComplete)
+ applyWindowVisibility();
+}
+
+bool QQuickWindowQmlImpl::event(QEvent *event)
+{
+ Q_D(QQuickWindowQmlImpl);
+
+ if (event->type() == QEvent::ParentWindowChange) {
+ qCDebug(lcQuickWindow) << "Parent of" << this << "changed to" << parent();
+ if (d->visualParent) {
+ // If the window parent changes, and we've deferred making
+ // the window visible, we need to re-evaluate our decision.
+ applyWindowVisibility();
+ } else {
+ QObject::disconnect(d->itemParentWindowChangeListener);
+ updateTransientParent();
+ }
+ }
+ return QQuickWindow::event(event);
}
-void QQuickWindowQmlImpl::setWindowVisibility()
+/*
+ Update the transient parent of the window based on its
+ QObject parent (Item or Window), unless the user has
+ set an explicit transient parent.
+*/
+void QQuickWindowQmlImpl::updateTransientParent()
{
Q_D(QQuickWindowQmlImpl);
- if (transientParent() && !transientParentVisible())
+
+ // We defer updating the transient parent until the component
+ // has been fully completed, and we know whether an explicit
+ // transient parent has been set.
+ if (!d->componentComplete)
return;
- if (QQuickItem *senderItem = qmlobject_cast<QQuickItem *>(sender())) {
- disconnect(senderItem, &QQuickItem::windowChanged, this, &QQuickWindowQmlImpl::setWindowVisibility);
- } else if (sender()) {
- disconnect(transientParent(), &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::setWindowVisibility);
+ // If an explicit transient parent has been set,
+ // we don't want to apply our magic.
+ if (d->transientParentPropertySet)
+ return;
+
+ // Nor if we have a visual parent that makes this a true child window
+ if (d->visualParent)
+ return;
+
+ auto *objectParent = QObject::parent();
+ qCDebug(lcTransient) << "Applying transient parent magic to"
+ << this << "based on object parent" << objectParent << "🪄";
+
+ QWindow *transientParent = nullptr;
+ if (auto *windowParent = qmlobject_cast<QWindow *>(objectParent)) {
+ transientParent = windowParent;
+ } else if (auto *itemParent = qmlobject_cast<QQuickItem *>(objectParent)) {
+ if (!d->itemParentWindowChangeListener) {
+ d->itemParentWindowChangeListener = connect(
+ itemParent, &QQuickItem::windowChanged,
+ this, &QQuickWindowQmlImpl::updateTransientParent);
+ }
+ transientParent = itemParent->window();
}
- // We have deferred window creation until we have the full picture of what
- // the user wanted in terms of window state, geometry, visibility, etc.
+ if (!transientParent) {
+ qCDebug(lcTransient) << "No transient parent resolved from object parent";
+ return;
+ }
- if ((d->visibility == Hidden && d->visible) || (d->visibility > AutomaticVisibility && !d->visible)) {
- QQmlData *data = QQmlData::get(this);
- Q_ASSERT(data && data->context);
+ qCDebug(lcTransient) << "Setting" << transientParent << "as transient parent of" << this;
+ setTransientParent(transientParent);
- QQmlError error;
- error.setObject(this);
+ // We want to keep applying the automatic transient parent
+ d->transientParentPropertySet = false;
+}
- QQmlRefPointer<QQmlContextData> urlContext = data->context;
- while (urlContext && urlContext->url().isEmpty())
- urlContext = urlContext->parent();
- error.setUrl(urlContext ? urlContext->url() : QUrl());
+void QQuickWindowQmlImpl::applyWindowVisibility()
+{
+ Q_D(QQuickWindowQmlImpl);
- QString objectId = data->context->findObjectId(this);
- if (!objectId.isEmpty())
- error.setDescription(QCoreApplication::translate("QQuickWindowQmlImpl",
- "Conflicting properties 'visible' and 'visibility' for Window '%1'").arg(objectId));
- else
- error.setDescription(QCoreApplication::translate("QQuickWindowQmlImpl",
- "Conflicting properties 'visible' and 'visibility'"));
+ Q_ASSERT(d->componentComplete);
- QQmlEnginePrivate::get(data->context->engine())->warning(error);
+ const bool visible = d->visibilityExplicitlySet
+ ? d->visibility != Hidden : d->visible;
+
+ qCDebug(lcQuickWindow) << "Applying visible" << visible << "for" << this;
+
+ if (visible) {
+ if (d->visualParent) {
+ // Even though we're complete, and have a visual parent set,
+ // we may not be part of a window yet, or we may have been
+ // removed from a window that's going away. Showing this window
+ // now would make it a top level, which is not what we want.
+ if (!QWindow::parent()) {
+ qCDebug(lcQuickWindow) << "Waiting for visual parent to reparent us into a window";
+ // We apply the visibility again on ParentWindowChange
+ return;
+ }
+ } else {
+ // Handle deferred visibility due to possible transient parent
+ auto *itemParent = qmlobject_cast<QQuickItem *>(QObject::parent());
+ if (!d->transientParentPropertySet && itemParent && !itemParent->window()) {
+ qCDebug(lcTransient) << "Waiting for parent" << itemParent << "to resolve"
+ << "its window. Deferring visibility";
+ return;
+ }
+
+ const QWindow *transientParent = QWindow::transientParent();
+ if (transientParent && !transientParentVisible()) {
+ // Defer visibility of this window until the transient parent has
+ // been made visible, or we've get a new transient parent.
+ qCDebug(lcTransient) << "Transient parent" << transientParent
+ << "not visible yet. Deferring visibility";
+
+ // QWindowPrivate::setVisible emits visibleChanged _before_ actually
+ // propagating the visibility to the platform window, so we can't use
+ // a direct connection here, as that would result in showing this
+ // window before the transient parent.
+ connect(transientParent, &QQuickWindow::visibleChanged, this,
+ &QQuickWindowQmlImpl::applyWindowVisibility,
+ Qt::ConnectionType(Qt::QueuedConnection | Qt::SingleShotConnection));
+ return;
+ }
+ }
+ }
+
+ if (d->visibleExplicitlySet && d->visibilityExplicitlySet &&
+ ((d->visibility == Hidden && d->visible) ||
+ (d->visibility > AutomaticVisibility && !d->visible))) {
+ // FIXME: Should we bail out in this case?
+ qmlWarning(this) << "Conflicting properties 'visible' and 'visibility'";
}
if (d->visibility == AutomaticVisibility) {
- setWindowState(QGuiApplicationPrivate::platformIntegration()->defaultWindowState(flags()));
- setVisible(d->visible);
+ // We're either showing for the first time, with the default
+ // visibility of AutomaticVisibility, or the user has called
+ // setVisibility with AutomaticVisibility at some point, so
+ // apply both window state and visible.
+ if (QWindow::parent() || visualParent())
+ setWindowState(Qt::WindowNoState);
+ else
+ setWindowState(QGuiApplicationPrivate::platformIntegration()->defaultWindowState(flags()));
+ QQuickWindow::setVisible(d->visible);
+ } else if (d->visibilityExplicitlySet) {
+ // We're not AutomaticVisibility, but the user has requested
+ // an explicit visibility, so apply both window state and visible.
+ QQuickWindow::setVisibility(d->visibility);
+ } else {
+ // Our window state should be up to date, so only apply visible
+ QQuickWindow::setVisible(d->visible);
+ }
+}
+
+bool QQuickWindowQmlImpl::transientParentVisible()
+{
+ Q_ASSERT(transientParent());
+ if (!transientParent()->isVisible()) {
+ // handle case where transient parent is offscreen window
+ QWindow *rw = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow*>(transientParent()));
+ return rw && rw->isVisible();
+ }
+ return true;
+}
+
+// -------------------------- Visual Parent ---------------------------
+
+/*!
+ \qmlproperty var QtQuick::Window::parent
+ \since 6.7
+ \preliminary
+
+ This property holds the visual parent of the window.
+
+ The visual parent can be either another Window, or an Item.
+
+ A window with a visual parent will result in the window becoming a child
+ window of its visual parent, either directly if the visual parent is another
+ Window, or indirectly via the visual parent Item's window.
+
+ Just like QtQuick::Item::parent, the window will be positioned relative to
+ its visual parent.
+
+ The stacking order between sibling Windows follows the document order,
+ just like Items, but can be customized via the Window's \l{QtQuick::Window::z}
+ {z-order} property.
+
+ Setting a visual parent on a Window will take precedence over the
+ \l{QtQuick::Window::transientParent}{transient parent}.
+
+ \sa{Concepts - Visual Parent in Qt Quick}, transientParent
+*/
+
+void QQuickWindowQmlImpl::setVisualParent(QObject *visualParent)
+{
+ Q_D(QQuickWindowQmlImpl);
+ if (visualParent == d->visualParent)
+ return;
+
+ qCDebug(lcQuickWindow) << "Setting visual parent of" << this << "to" << visualParent;
+
+ if (d->visualParent) {
+ // Disconnect from deferred window listener
+ d->visualParent->disconnect(this);
+ }
+
+ d->visualParent = visualParent;
+
+ if (d->componentComplete)
+ applyVisualParent();
+
+ emit visualParentChanged(d->visualParent);
+}
+
+void QQuickWindowQmlImpl::applyVisualParent()
+{
+ Q_D(QQuickWindowQmlImpl);
+ Q_ASSERT(d->componentComplete);
+
+ qCDebug(lcQuickWindow) << "Applying" << this << "visual parent" << d->visualParent;
+
+ if (!d->visualParent) {
+ if (d->windowContainer) {
+ d->windowContainer->setContainedWindow(nullptr);
+ delete std::exchange(d->windowContainer, nullptr);
+ }
+ QQuickWindow::setParent(nullptr);
+ return;
+ }
+
+ QQuickItem *parentItem = nullptr;
+ if ((parentItem = qobject_cast<QQuickItem*>(d->visualParent)))
+ ; // All good, can use directly
+ else if (auto *parentWindow = qobject_cast<QWindow*>(d->visualParent)) {
+ if (auto *parentQuickWindow = qobject_cast<QQuickWindow*>(parentWindow)) {
+ parentItem = parentQuickWindow->contentItem();
+ } else {
+ qmlWarning(this) << "Parenting into non-Quick window. "
+ << "Stacking, position, and destruction must be handled manually";
+ QQuickWindow::setParent(parentWindow); // Try our best
+ return;
+ }
+ }
+
+ if (!parentItem) {
+ qmlWarning(this) << "Unsupported visual parent type"
+ << d->visualParent->metaObject()->className();
+ return;
+ }
+
+ if (!parentItem->window()) {
+ qCDebug(lcQuickWindow) << "No window yet. Deferring.";
+ connect(parentItem, &QQuickItem::windowChanged, this, [this]{
+ qCDebug(lcQuickWindow) << "Got window. Applying deferred visual parent item.";
+ applyVisualParent();
+ }, Qt::SingleShotConnection);
+ return;
+ }
+
+ if (qobject_cast<QQuickWindowContainer*>(d->visualParent)) {
+ qCDebug(lcQuickWindow) << "Visual parent is window container, everything is in order";
+ return;
+ }
+
+ if (!d->windowContainer) {
+ d->windowContainer = new QQuickWindowContainer(parentItem,
+ QQuickWindowContainer::WindowControlsItem);
+ d->windowContainer->setObjectName(objectName() + "Container"_L1);
+
+ auto *objectParent = this->QObject::parent();
+ if (objectParent == parentItem) {
+ // We want to reflect the QML document order of sibling windows in the
+ // resulting stacking order of the windows. We can do so by carefully
+ // using the the information we have about the child object order.
+
+ // We know that the window's object child index is correct in relation
+ // to the other child windows of the parent. Since the window container
+ // is going to represent the window from now on, make the window container
+ // take the window's place in the parent's child object list.
+ auto &objectChildren = QObjectPrivate::get(objectParent)->children;
+ auto windowIndex = objectChildren.indexOf(this);
+ auto containerIndex = objectChildren.indexOf(d->windowContainer);
+ objectChildren.move(containerIndex, windowIndex);
+ containerIndex = windowIndex;
+
+ // The parent's item children are unfortunately managed separately from
+ // the object children. But thanks to the logic above we can use the now
+ // correct object order of the window container in the object children list
+ // to also ensure a correct stacking order between the sibling child items.
+ for (int i = containerIndex + 1; i < objectChildren.size(); ++i) {
+ if (auto *childItem = qobject_cast<QQuickItem*>(objectChildren.at(i))) {
+ qCDebug(lcQuickWindow) << "Stacking" << d->windowContainer
+ << "below" << childItem;
+ d->windowContainer->stackBefore(childItem);
+ break;
+ }
+ }
+ } else {
+ // Having another visual parent than the direct object parent will
+ // mess up the stacking order. This is also the case for normal items.
+ qCDebug(lcQuickWindow) << "Visual parent is not object parent."
+ << "Can not reflect document order as stacking order.";
+ }
+
+ QQmlEngine::setContextForObject(d->windowContainer, qmlContext(this));
+
+ d->windowContainer->classBegin();
+ d->windowContainer->setContainedWindow(this);
+ // Once the window has a window container, all x/y/z changes of
+ // the window will go through the container, and ensure the
+ // correct mapping. But any changes that happened prior to
+ // this have not been mapped yet, so do that now.
+ d->windowContainer->setPosition(position());
+ d->windowContainer->setZ(d->z);
+ d->windowContainer->componentComplete();
+
+ QObject::connect(d->windowContainer, &QQuickItem::zChanged,
+ this, &QQuickWindowQmlImpl::zChanged);
} else {
- setVisibility(d->visibility);
+ d->windowContainer->setParentItem(parentItem);
}
}
+QObject *QQuickWindowQmlImpl::visualParent() const
+{
+ Q_D(const QQuickWindowQmlImpl);
+ return d->visualParent;
+}
+
+// We shadow the x and y properties of the Window, so that in case
+// the window has an Item as its visual parent we can re-map the
+// coordinates via the corresponding window container. We need to
+// do this also for the signal emissions, as otherwise the Window's
+// change signals will reflect different values than what we report
+// via the accessors. It would be nicer if this logic was contained
+// in the window container, for example via meta object property
+// interception, but that does not allow intercepting signal emissions.
+
+void QQuickWindowQmlImpl::setX(int x)
+{
+ Q_D(QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ d->windowContainer->setX(x);
+ else
+ QQuickWindow::setX(x);
+}
+
+int QQuickWindowQmlImpl::x() const
+{
+ Q_D(const QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ return d->windowContainer->x();
+ else
+ return QQuickWindow::x();
+}
+
+void QQuickWindowQmlImpl::setY(int y)
+{
+ Q_D(QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ d->windowContainer->setY(y);
+ else
+ QQuickWindow::setY(y);
+}
+
+int QQuickWindowQmlImpl::y() const
+{
+ Q_D(const QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ return d->windowContainer->y();
+ else
+ return QQuickWindow::y();
+}
+
+/*!
+ \qmlproperty real QtQuick::Window::z
+ \preliminary
+
+ Sets the stacking order of sibling windows.
+
+ By default the stacking order is 0.
+
+ Windows with a higher stacking value are drawn on top of windows with a
+ lower stacking order. Windows with the same stacking value are drawn
+ bottom up in the order they appear in the QML document.
+
+ \note This property only has an effect for child windows.
+
+ \sa QtQuick::Item::z
+*/
+
+void QQuickWindowQmlImpl::setZ(qreal z)
+{
+ Q_D(QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ d->windowContainer->setZ(z);
+ else
+ d->z = z;
+}
+
+qreal QQuickWindowQmlImpl::z() const
+{
+ Q_D(const QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ return d->windowContainer->z();
+ else
+ return d->z;
+}
+
+// --------------------------------------------------------------------
+
QObject *QQuickWindowQmlImpl::screen() const
{
return new QQuickScreenInfo(const_cast<QQuickWindowQmlImpl *>(this), QWindow::screen());
@@ -160,15 +538,9 @@ void QQuickWindowQmlImpl::setScreen(QObject *screen)
QWindow::setScreen(screenWrapper ? screenWrapper->wrappedScreen() : nullptr);
}
-bool QQuickWindowQmlImpl::transientParentVisible()
+QQuickWindowAttached *QQuickWindowQmlImpl::qmlAttachedProperties(QObject *object)
{
- Q_ASSERT(transientParent());
- if (!transientParent()->isVisible()) {
- // handle case where transient parent is offscreen window
- QWindow *rw = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow*>(transientParent()));
- return rw && rw->isVisible();
- }
- return true;
+ return new QQuickWindowAttached(object);
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h
index c64ed537f7..281c67800e 100644
--- a/src/quick/items/qquickwindowmodule_p.h
+++ b/src/quick/items/qquickwindowmodule_p.h
@@ -32,7 +32,7 @@ struct QWindowForeign
QML_ADDED_IN_VERSION(2, 1)
};
-class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public QQmlParserStatus
+class Q_QUICK_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
@@ -41,12 +41,17 @@ class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public Q
Q_PROPERTY(QWindow::Visibility visibility READ visibility WRITE setVisibility NOTIFY
visibilityChanged)
Q_PROPERTY(QObject *screen READ screen WRITE setScreen NOTIFY screenChanged REVISION(2, 3))
+ Q_PROPERTY(QObject *parent READ visualParent WRITE setVisualParent NOTIFY visualParentChanged DESIGNABLE false FINAL REVISION(6, 7))
+ Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged)
+ Q_PROPERTY(int y READ y WRITE setY NOTIFY yChanged)
+ Q_PROPERTY(qreal z READ z WRITE setZ NOTIFY zChanged FINAL REVISION(6, 7))
QML_ATTACHED(QQuickWindowAttached)
QML_NAMED_ELEMENT(Window)
QML_ADDED_IN_VERSION(2, 1)
public:
QQuickWindowQmlImpl(QWindow *parent = nullptr);
+ ~QQuickWindowQmlImpl();
void setVisible(bool visible);
void setVisibility(QWindow::Visibility visibility);
@@ -54,24 +59,43 @@ public:
QObject *screen() const;
void setScreen(QObject *screen);
+ QObject *visualParent() const;
+ void setVisualParent(QObject *parent);
+
+ void setX(int arg);
+ int x() const;
+ void setY(int arg);
+ int y() const;
+ void setZ(qreal arg);
+ qreal z() const;
+
static QQuickWindowAttached *qmlAttachedProperties(QObject *object);
Q_SIGNALS:
void visibleChanged(bool arg);
void visibilityChanged(QWindow::Visibility visibility);
+ Q_REVISION(6, 7) void visualParentChanged(QObject *);
Q_REVISION(2, 3) void screenChanged();
+ void xChanged(int arg);
+ void yChanged(int arg);
+ Q_REVISION(6, 7) void zChanged();
+
protected:
void classBegin() override;
void componentComplete() override;
+ bool event(QEvent *) override;
+
QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow *parent);
private Q_SLOTS:
- void setWindowVisibility();
+ Q_REVISION(6, 7) void applyWindowVisibility();
+ Q_REVISION(6, 7) void updateTransientParent();
private:
bool transientParentVisible();
+ void applyVisualParent();
private:
Q_DISABLE_COPY(QQuickWindowQmlImpl)
@@ -80,6 +104,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickWindowQmlImpl)
-
#endif
diff --git a/src/quick/items/qquickwindowmodule_p_p.h b/src/quick/items/qquickwindowmodule_p_p.h
index 3057cfa2c2..227b8aa01e 100644
--- a/src/quick/items/qquickwindowmodule_p_p.h
+++ b/src/quick/items/qquickwindowmodule_p_p.h
@@ -16,18 +16,29 @@
#include "qquickwindow_p.h"
#include <QtQml/private/qv4persistent_p.h>
+#include "qquickwindowcontainer_p.h"
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImplPrivate : public QQuickWindowPrivate
+class Q_QUICK_EXPORT QQuickWindowQmlImplPrivate : public QQuickWindowPrivate
{
public:
QQuickWindowQmlImplPrivate();
- bool complete = false;
+ bool componentComplete = true;
+
bool visible = false;
+ bool visibleExplicitlySet = false;
QQuickWindow::Visibility visibility = QQuickWindow::AutomaticVisibility;
+ bool visibilityExplicitlySet = false;
+
QV4::PersistentValue rootItemMarker;
+
+ QMetaObject::Connection itemParentWindowChangeListener;
+
+ QObject *visualParent = nullptr;
+ QPointer<QQuickWindowContainer> windowContainer;
+ qreal z = 0.0;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qsginternaltextnode.cpp b/src/quick/items/qsginternaltextnode.cpp
new file mode 100644
index 0000000000..6a64c0b9e4
--- /dev/null
+++ b/src/quick/items/qsginternaltextnode.cpp
@@ -0,0 +1,265 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsginternaltextnode_p.h"
+
+#include "qquicktextnodeengine_p.h"
+
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgdistancefieldglyphnode_p.h>
+#include <private/qquickclipnode_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qquicktextdocument_p.h>
+
+#include <QtCore/qpoint.h>
+#include <qtextdocument.h>
+#include <qtextlayout.h>
+#include <qabstracttextdocumentlayout.h>
+#include <private/qquickstyledtext_p.h>
+#include <private/qquicktext_p_p.h>
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+
+#include <private/qtextdocumentlayout_p.h>
+#include <qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcVP)
+
+/*!
+ Creates an empty QSGInternalTextNode
+*/
+QSGInternalTextNode::QSGInternalTextNode(QSGRenderContext *renderContext)
+ : m_renderContext(renderContext)
+{
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("text"));
+#endif
+
+ static_assert(int(QSGTextNode::Normal) == int(QQuickText::Normal));
+ static_assert(int(QSGTextNode::Outline) == int(QQuickText::Outline));
+ static_assert(int(QSGTextNode::Raised) == int(QQuickText::Raised));
+ static_assert(int(QSGTextNode::Sunken) == int(QQuickText::Sunken));
+
+ static_assert(int(QSGTextNode::QtRendering) == int(QQuickText::QtRendering));
+ static_assert(int(QSGTextNode::NativeRendering) == int(QQuickText::NativeRendering));
+ static_assert(int(QSGTextNode::CurveRendering) == int(QQuickText::CurveRendering));
+}
+
+QSGInternalTextNode::~QSGInternalTextNode()
+{
+ qDeleteAll(m_textures);
+}
+
+QSGGlyphNode *QSGInternalTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
+ QQuickText::TextStyle style, const QColor &styleColor,
+ QSGNode *parentNode)
+{
+ QRawFont font = glyphs.rawFont();
+
+ QSGTextNode::RenderType preferredRenderType = m_renderType;
+ if (m_renderType != NativeRendering) {
+ if (const QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine)
+ if (fe->hasUnreliableGlyphOutline() || !fe->isSmoothlyScalable)
+ preferredRenderType = QSGTextNode::NativeRendering;
+ }
+
+ QSGGlyphNode *node = m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext,
+ preferredRenderType,
+ m_renderTypeQuality);
+ node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
+ node->setStyle(style);
+ node->setStyleColor(styleColor);
+ node->setColor(color);
+ node->update();
+
+ /* We flag the geometry as static, but we never call markVertexDataDirty
+ or markIndexDataDirty on them. This is because all text nodes are
+ discarded when a change occurs. If we start appending/removing from
+ existing geometry, then we also need to start marking the geometry as
+ dirty.
+ */
+ node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
+ node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
+
+ if (parentNode == nullptr)
+ parentNode = this;
+ parentNode->appendChildNode(node);
+
+ if (style == QQuickText::Outline && color.alpha() > 0 && styleColor != color) {
+ QSGGlyphNode *fillNode = m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext,
+ preferredRenderType,
+ m_renderTypeQuality);
+ fillNode->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
+ fillNode->setStyle(QQuickText::Normal);
+ fillNode->setPreferredAntialiasingMode(QSGGlyphNode::GrayAntialiasing);
+ fillNode->setColor(color);
+ fillNode->update();
+
+ fillNode->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
+ fillNode->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
+
+ parentNode->appendChildNode(fillNode);
+ fillNode->setRenderOrder(node->renderOrder() + 1);
+ }
+
+ return node;
+}
+
+void QSGInternalTextNode::setCursor(const QRectF &rect, const QColor &color)
+{
+ if (m_cursorNode != nullptr)
+ delete m_cursorNode;
+
+ m_cursorNode = m_renderContext->sceneGraphContext()->createInternalRectangleNode(rect, color);
+ appendChildNode(m_cursorNode);
+}
+
+void QSGInternalTextNode::clearCursor()
+{
+ if (m_cursorNode)
+ removeChildNode(m_cursorNode);
+ delete m_cursorNode;
+ m_cursorNode = nullptr;
+}
+
+void QSGInternalTextNode::addDecorationNode(const QRectF &rect, const QColor &color)
+{
+ addRectangleNode(rect, color);
+}
+
+void QSGInternalTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
+{
+ appendChildNode(m_renderContext->sceneGraphContext()->createInternalRectangleNode(rect, color));
+}
+
+void QSGInternalTextNode::addImage(const QRectF &rect, const QImage &image)
+{
+ QSGInternalImageNode *node = m_renderContext->sceneGraphContext()->createInternalImageNode(m_renderContext);
+ QSGTexture *texture = m_renderContext->createTexture(image);
+ texture->setFiltering(m_filtering);
+ m_textures.append(texture);
+ node->setTargetRect(rect);
+ node->setInnerTargetRect(rect);
+ node->setTexture(texture);
+ node->setFiltering(m_filtering);
+ appendChildNode(node);
+ node->update();
+}
+
+void QSGInternalTextNode::doAddTextDocument(QPointF position, QTextDocument *textDocument,
+ int selectionStart, int selectionEnd)
+{
+ QQuickTextNodeEngine engine;
+ engine.setTextColor(m_color);
+ engine.setSelectedTextColor(m_selectionTextColor);
+ engine.setSelectionColor(m_selectionColor);
+ engine.setAnchorColor(m_linkColor);
+ engine.setPosition(position);
+
+ QList<QTextFrame *> frames;
+ frames.append(textDocument->rootFrame());
+ while (!frames.isEmpty()) {
+ QTextFrame *textFrame = frames.takeFirst();
+ frames.append(textFrame->childFrames());
+
+ engine.addFrameDecorations(textDocument, textFrame);
+
+ if (textFrame->firstPosition() > textFrame->lastPosition()
+ && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
+ const int pos = textFrame->firstPosition() - 1;
+ auto *a = static_cast<QtPrivate::ProtectedLayoutAccessor *>(textDocument->documentLayout());
+ QTextCharFormat format = a->formatAccessor(pos);
+ QRectF rect = a->frameBoundingRect(textFrame);
+
+ QTextBlock block = textFrame->firstCursorPosition().block();
+ engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
+ engine.addTextObject(block, rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument,
+ pos, textFrame->frameFormat().position());
+ } else {
+ QTextFrame::iterator it = textFrame->begin();
+
+ while (!it.atEnd()) {
+ Q_ASSERT(!engine.currentLine().isValid());
+
+ QTextBlock block = it.currentBlock();
+ engine.addTextBlock(textDocument, block, position, m_color, m_linkColor, selectionStart, selectionEnd,
+ (textDocument->characterCount() > QQuickTextPrivate::largeTextSizeThreshold ?
+ m_viewport : QRectF()));
+ ++it;
+ }
+ }
+ }
+
+ engine.addToSceneGraph(this, QQuickText::TextStyle(m_textStyle), m_styleColor);
+}
+
+void QSGInternalTextNode::doAddTextLayout(QPointF position, QTextLayout *textLayout,
+ int selectionStart, int selectionEnd,
+ int lineStart, int lineCount)
+{
+ QQuickTextNodeEngine engine;
+ engine.setTextColor(m_color);
+ engine.setSelectedTextColor(m_selectionTextColor);
+ engine.setSelectionColor(m_selectionColor);
+ engine.setAnchorColor(m_linkColor);
+ engine.setPosition(position);
+
+#if QT_CONFIG(im)
+ int preeditLength = textLayout->preeditAreaText().size();
+ int preeditPosition = textLayout->preeditAreaPosition();
+#endif
+
+ QVarLengthArray<QTextLayout::FormatRange> colorChanges;
+ engine.mergeFormats(textLayout, &colorChanges);
+
+ lineCount = lineCount >= 0
+ ? qMin(lineStart + lineCount, textLayout->lineCount())
+ : textLayout->lineCount();
+
+ bool inViewport = false;
+ for (int i=lineStart; i<lineCount; ++i) {
+ QTextLine line = textLayout->lineAt(i);
+
+ int start = line.textStart();
+ int length = line.textLength();
+ int end = start + length;
+
+#if QT_CONFIG(im)
+ if (preeditPosition >= 0
+ && preeditPosition >= start
+ && preeditPosition < end) {
+ end += preeditLength;
+ }
+#endif
+ // If there's a lot of text, insert only the range of lines that can possibly be visible within the viewport.
+ if (m_viewport.isNull() || (line.y() + line.height() > m_viewport.top() && line.y() < m_viewport.bottom())) {
+ if (!inViewport && !m_viewport.isNull()) {
+ m_firstLineInViewport = i;
+ qCDebug(lcVP) << "first line in viewport" << i << "@" << line.y();
+ }
+ inViewport = true;
+ engine.setCurrentLine(line);
+ engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd);
+ } else if (inViewport) {
+ Q_ASSERT(!m_viewport.isNull());
+ m_firstLinePastViewport = i;
+ qCDebug(lcVP) << "first omitted line past bottom of viewport" << i << "@" << line.y();
+ break; // went past the bottom of the viewport, so we're done
+ }
+ }
+
+ engine.addToSceneGraph(this, QQuickText::TextStyle(m_textStyle), m_styleColor);
+}
+
+void QSGInternalTextNode::clear()
+{
+ while (firstChild() != nullptr)
+ delete firstChild();
+ m_cursorNode = nullptr;
+ qDeleteAll(m_textures);
+ m_textures.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qsginternaltextnode_p.h b/src/quick/items/qsginternaltextnode_p.h
new file mode 100644
index 0000000000..0d29657912
--- /dev/null
+++ b/src/quick/items/qsginternaltextnode_p.h
@@ -0,0 +1,199 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGINTERNALTEXTNODE_P_H
+#define QSGINTERNALTEXTNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgtextnode.h"
+#include "qquicktext_p.h"
+#include <qglyphrun.h>
+
+#include <QtGui/qcolor.h>
+#include <QtGui/qtextlayout.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGGlyphNode;
+class QTextBlock;
+class QColor;
+class QTextDocument;
+class QSGContext;
+class QRawFont;
+class QSGInternalRectangleNode;
+class QSGClipNode;
+class QSGTexture;
+class QSGRenderContext;
+
+class QQuickTextNodeEngine;
+
+class Q_QUICK_EXPORT QSGInternalTextNode : public QSGTextNode
+{
+public:
+ QSGInternalTextNode(QSGRenderContext *renderContext);
+ ~QSGInternalTextNode();
+
+ static bool isComplexRichText(QTextDocument *);
+
+ void setColor(QColor color) override
+ {
+ m_color = color;
+ }
+
+ QColor color() const override
+ {
+ return m_color;
+ }
+
+ void setTextStyle(TextStyle textStyle) override
+ {
+ m_textStyle = textStyle;
+ }
+
+ TextStyle textStyle() override
+ {
+ return m_textStyle;
+ }
+
+ void setStyleColor(QColor styleColor) override
+ {
+ m_styleColor = styleColor;
+ }
+
+ QColor styleColor() const override
+ {
+ return m_styleColor;
+ }
+
+ void setLinkColor(QColor linkColor) override
+ {
+ m_linkColor = linkColor;
+ }
+
+ QColor linkColor() const override
+ {
+ return m_linkColor;
+ }
+
+ void setSelectionColor(QColor selectionColor) override
+ {
+ m_selectionColor = selectionColor;
+ }
+
+ QColor selectionColor() const override
+ {
+ return m_selectionColor;
+ }
+
+ void setSelectionTextColor(QColor selectionTextColor) override
+ {
+ m_selectionTextColor = selectionTextColor;
+ }
+
+ QColor selectionTextColor() const override
+ {
+ return m_selectionTextColor;
+ }
+
+ void setRenderTypeQuality(int renderTypeQuality) override
+ {
+ m_renderTypeQuality = renderTypeQuality;
+ }
+ int renderTypeQuality() const override
+ {
+ return m_renderTypeQuality;
+ }
+
+ void setRenderType(RenderType renderType) override
+ {
+ m_renderType = renderType;
+ }
+
+ RenderType renderType() const override
+ {
+ return m_renderType;
+ }
+
+ void setFiltering(QSGTexture::Filtering filtering) override
+ {
+ m_filtering = filtering;
+ }
+
+ QSGTexture::Filtering filtering() const override
+ {
+ return m_filtering;
+ }
+
+ void setViewport(const QRectF &viewport) override
+ {
+ m_viewport = viewport;
+ }
+
+ QRectF viewport() const override
+ {
+ return m_viewport;
+ }
+
+ void setCursor(const QRectF &rect, const QColor &color);
+ void clearCursor();
+
+ void addRectangleNode(const QRectF &rect, const QColor &color);
+ virtual void addDecorationNode(const QRectF &rect, const QColor &color);
+ void addImage(const QRectF &rect, const QImage &image);
+ void clear() override;
+ QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
+ QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
+ QSGNode *parentNode = 0);
+
+ QSGInternalRectangleNode *cursorNode() const { return m_cursorNode; }
+ QPair<int, int> renderedLineRange() const { return { m_firstLineInViewport, m_firstLinePastViewport }; }
+
+protected:
+ void doAddTextLayout(QPointF position,
+ QTextLayout *textLayout,
+ int selectionStart,
+ int selectionEnd,
+ int lineStart,
+ int lineCount) override;
+
+ void doAddTextDocument(QPointF position,
+ QTextDocument *textDocument,
+ int selectionStart,
+ int selectionEnd) override;
+
+private:
+ QSGInternalRectangleNode *m_cursorNode = nullptr;
+ QList<QSGTexture *> m_textures;
+ QSGRenderContext *m_renderContext = nullptr;
+ RenderType m_renderType = QtRendering;
+ TextStyle m_textStyle = Normal;
+ QRectF m_viewport;
+ QColor m_color = QColor(0, 0, 0);
+ QColor m_styleColor = QColor(0, 0, 0);
+ QColor m_linkColor = QColor(0, 0, 255);
+ QColor m_selectionColor = QColor(0, 0, 128);
+ QColor m_selectionTextColor = QColor(255, 255, 255);
+ QSGTexture::Filtering m_filtering = QSGTexture::Nearest;
+ int m_renderTypeQuality = -1;
+ int m_firstLineInViewport = -1;
+ int m_firstLinePastViewport = -1;
+
+ friend class QQuickTextEdit;
+ friend class QQuickTextEditPrivate;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGINTERNALTEXTNODE_P_H
diff --git a/src/quick/jar/CMakeLists.txt b/src/quick/jar/CMakeLists.txt
new file mode 100644
index 0000000000..9555bbca16
--- /dev/null
+++ b/src/quick/jar/CMakeLists.txt
@@ -0,0 +1,13 @@
+qt_internal_add_jar(Qt${QtDeclarative_VERSION_MAJOR}AndroidQuick
+ INCLUDE_JARS
+ ${QT_ANDROID_JAR}
+ ${QT6_INSTALL_PREFIX}/jar/Qt${QtDeclarative_VERSION_MAJOR}Android.jar
+ SOURCES org/qtproject/qt/android/QtQuickView.java
+ OUTPUT_DIR "${QT_BUILD_DIR}/jar")
+
+qt_path_join(destination ${INSTALL_DATADIR} "jar")
+
+install_jar(Qt${QtDeclarative_VERSION_MAJOR}AndroidQuick
+ DESTINATION ${destination}
+ COMPONENT Devel
+)
diff --git a/src/quick/jar/org/qtproject/qt/android/QtQuickView.java b/src/quick/jar/org/qtproject/qt/android/QtQuickView.java
new file mode 100644
index 0000000000..0f8e999d96
--- /dev/null
+++ b/src/quick/jar/org/qtproject/qt/android/QtQuickView.java
@@ -0,0 +1,264 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.util.Log;
+
+import java.security.InvalidParameterException;
+
+/**
+ * The QtQuickView class lets you easily add QML content to your Android app as a
+ * {@link android.view.View View}. QtQuickView instantiates a QQuickView with a given
+ * QML component source URI path and embeds it inside itself. You can add it in your Android app's
+ * layout as with any other View. QtQuickView is a good choice when you want to extend your non-Qt
+ * Android app with QML content but do not want to make the entire app using the Qt framework.
+ * It brings the power of Qt Quick into your Android app, making it possible to use various Qt Quick
+ * APIs, in Android Java or Kotlin apps.
+ * </p>
+ * <b>Note:</b> This class is a technical preview. It is subject to change, and no source nor
+ * binary compatibility guarantees exist.
+ * <p>
+ * <b>Known limitations:</b>
+ * <p><ul>
+ * <li> Only CMake is supported, not qmake.
+ * <li> Only one QtQuickView can be added to your app, adding multiple outcomes unknown.
+ * </ul><p>
+ * @see <a href="https://doc.qt.io/qt-6/qquickview.html">Qt QQuickView</a>
+ **/
+public class QtQuickView extends QtView {
+ private final static String TAG = "QtQuickView";
+
+ /**
+ * A callback that notifies clients when a signal is emitted from the QML root object.
+ **/
+ @FunctionalInterface
+ public interface SignalListener<T>
+ {
+ /**
+ * Called on the Android UI thread when the signal has been emitted.
+ * @param signalName literal signal name
+ * @param value the value delivered by the signal or null if the signal is parameterless
+ **/
+ void onSignalEmitted(String signalName, T value);
+ }
+
+ /**
+ * A callback that notifies clients about the status of QML loading.
+ **/
+ public interface StatusChangeListener
+ {
+ /**
+ * Called on the Android UI thread when the QML component status has changed.
+ * @param status The current status. The status can be STATUS_NULL, STATUS_READY,
+ * STATUS_LOADING or STATUS_ERROR.
+ **/
+ void onStatusChanged(int status);
+ }
+
+ /**
+ * QML loading status: No source is set or the root object has not been created yet.
+ **/
+ public static final int STATUS_NULL = 0;
+ /**
+ * QML loading status: The QML view is loaded and the root object is available.
+ * Invoking methods that operate on the QML root object, i.e.
+ * {@link QtQuickView#setProperty() setProperty}, {@link QtQuickView#getProperty() getProperty},
+ * and {@link QtQuickView#addSignalListener() addSignalListener} would succeed <b>only<b> if
+ * the current status is ready.
+ **/
+ public static final int STATUS_READY = 1;
+ /**
+ * QML loading status: The QML view is loading the root object from network.
+ **/
+ public static final int STATUS_LOADING = 2;
+ /**
+ * QML loading status: One or more errors has occurred.
+ **/
+ public static final int STATUS_ERROR = 3;
+
+ private String m_qmlUri;
+ private String[] m_qmlImportPaths = null;
+ private StatusChangeListener m_statusChangeListener = null;
+ private int m_lastStatus = STATUS_NULL;
+ private boolean m_hasQueuedStatus = false;
+
+ native void createQuickView(String qmlUri, int width, int height, long parentWindowReference,
+ String[] qmlImportPaths);
+ native void setRootObjectProperty(long windowReference, String propertyName, Object value);
+ native Object getRootObjectProperty(long windowReference, String propertyName);
+ native int addRootObjectSignalListener(long windowReference, String signalName, Class argType,
+ Object listener);
+ native boolean removeRootObjectSignalListener(long windowReference, int signalListenerId);
+
+ /**
+ * Creates a QtQuickView to load and view a QML component. Instantiating a QtQuickView will load
+ * the Qt libraries, including the app library specified by <code>appName</code>. Then it
+ * creates a QQuickView that loads the QML source specified by <code>qmlUri</code>.
+ * <p>
+ * @param context the parent Context
+ * @param qmlUri the URI of the main QML file
+ * @param appName the name of the Qt app library to load and start. This corresponds to the
+ * target name set in Qt app's CMakeLists.txt
+ * @throws InvalidParameterException if either qmlUri or appName is empty or null
+ * @see <a href="https://doc.qt.io/qt-6/qquickview.html">Qt QQuickView</a>
+ **/
+ public QtQuickView(Context context, String qmlUri, String appName)
+ throws InvalidParameterException {
+ this(context, qmlUri, appName, null);
+ }
+
+ /**
+ * Creates a QtQuickView to load and view a QML component. Instantiating a QtQuickView will load
+ * the Qt libraries, including the app library specified by appName. Then it creates a
+ * QQuickView that loads the QML source specified by qmlUri. This overload accepts an array of
+ * strings in the case where the QML application should load QML modules from custom paths.
+ * <p>
+ * @param context the parent Context
+ * @param qmlUri the URI of the main QML file
+ * @param appName the name of the Qt app library to load and start. This corresponds to
+ * the target name set in the Qt app's CMakeLists.txt
+ * @param qmlImportPaths an array of strings for additional import paths to be passed to
+ QQmlEngine, or null if additional import paths are not required
+ * @throws InvalidParameterException if either qmlUri or appName is empty or null
+ * @see <a href="https://doc.qt.io/qt-6/qqmlengine.html">Qt QQmlEngine</a>
+ **/
+ public QtQuickView(Context context, String qmlUri, String appName, String[] qmlImportPaths)
+ throws InvalidParameterException
+ {
+ super(context, appName);
+ if (qmlUri == null || qmlUri.isEmpty()) {
+ throw new InvalidParameterException(
+ "QtQuickView: argument 'qmlUri' may not be empty or null");
+ }
+ m_qmlUri = qmlUri;
+ m_qmlImportPaths = qmlImportPaths;
+ }
+
+ @Override
+ protected void createWindow(long parentWindowReference) {
+ createQuickView(m_qmlUri, getWidth(), getHeight(), parentWindowReference, m_qmlImportPaths);
+ }
+
+ /**
+ * Sets the value of an existing property on the QML root object. The supported types are
+ * {@link java.lang.Integer}, {@link java.lang.Double}, {@link java.lang.Float},
+ * {@link java.lang.Boolean} and {@link java.lang.String}. These types get converted to their
+ * corresponding QML types int, double/float, bool and string. This function does not add
+ * properties to the QML root object if they do not exist, but prints a warning.
+ * <p>
+ * @param propertyName the name of the existing root object property to set the value of
+ * @param value the value to set the property to
+ * @see <a href="https://doc.qt.io/qt-6/qml-int.html">QML int</a>,
+ * @see <a href="https://doc.qt.io/qt-6/qml-double.html">QML double/float</a>,
+ * @see <a href="https://doc.qt.io/qt-6/qml-bool.html">QML bool</a>,
+ * @see <a href="https://doc.qt.io/qt-6/qml-string.html">QML string</a>.
+ **/
+ public void setProperty(String propertyName, Object value)
+ {
+ setRootObjectProperty(windowReference(), propertyName, value);
+ }
+
+ /**
+ * Gets the value of an existing property of the QML root object. The supported types are
+ * {@link java.lang.Integer}, {@link java.lang.Double}, {@link java.lang.Float},
+ * {@link java.lang.Boolean} and {@link java.lang.String}. These types get converted to their
+ * corresponding QML types int, double/float, bool and string. If the property does not
+ * exist or the status of the QML component is anything other than
+ * {@link QtQuickView#STATUS_READY STATUS_READY}, this function will return null.
+ * <p>
+ * @param propertyName the name of the existing root object property
+ * @throws ClassCastException if the returned type could not be casted to the requested type.
+ * @see <a href="https://doc.qt.io/qt-6/qml-int.html">QML int</a>,
+ * @see <a href="https://doc.qt.io/qt-6/qml-double.html">QML double/float</a>,
+ * @see <a href="https://doc.qt.io/qt-6/qml-bool.html">QML bool</a>,
+ * @see <a href="https://doc.qt.io/qt-6/qml-string.html">QML string</a>.
+ **/
+ // getRootObjectProperty always returns a primitive type or an Object
+ // so it is safe to suppress the unchecked warning
+ @SuppressWarnings("unchecked")
+ public <T> T getProperty(String propertyName)
+ {
+ return (T)getRootObjectProperty(windowReference(), propertyName);
+ }
+
+ /**
+ * Connects a SignalListener to a signal of the QML root object.
+ * <p>
+ * @param signalName the name of the root object signal
+ * @param argType the Class type of the signal argument
+ * @param listener an instance of the SignalListener interface
+ * @return a connection id between signal and listener or the existing connection id if there is
+ * an existing connection between the same signal and listener. Return a negative value
+ * if the signal does not exists on the QML root object.
+ **/
+ public <T> int connectSignalListener(String signalName, Class<T> argType,
+ SignalListener<T> listener)
+ {
+ int signalListenerId =
+ addRootObjectSignalListener(windowReference(), signalName, argType, listener);
+ if (signalListenerId < 0) {
+ Log.w(TAG, "The signal " + signalName + " does not exist in the root object "
+ + "or the arguments do not match with the listener.");
+ }
+ return signalListenerId;
+ }
+
+ /**
+ * Disconnects a SignalListener with a given id obtained from
+ * {@link QtQuickView#connectSignalListener() connectSignalListener} call, from listening to
+ * a signal.
+ * <p>
+ * @param signalListenerId the connection id
+ * @return Returns true if the connection id is valid and has been successfuly removed,
+ * otherwise returns false.
+ **/
+ public boolean disconnectSignalListener(int signalListenerId)
+ {
+ return removeRootObjectSignalListener(windowReference(), signalListenerId);
+ }
+
+ /**
+ * Gets the status of the QML component.
+ * <p>
+ * @return Returns STATUS_READY when the QML component is ready. Invoking methods that operate
+ * on the QML root object ({@link QtQuickView#setProperty() setProperty},
+ * {@link QtQuickView#getProperty() getProperty}, and
+ * {@link QtQuickView#addSignalListener() addSignalListener}) would succeed <b>only</b>
+ * if the current STATUS_READY. It can also return STATUS_NULL, STATUS_LOADING, or
+ * STATUS_ERROR based on the status of see underlaying
+ * @see <a href="https://doc.qt.io/qt-6/qquickview.html">QQuickView</a> instance.
+ **/
+ public int getStatus()
+ {
+ return m_lastStatus;
+ }
+
+ /**
+ * Sets a StatusChangeListener to listen to status changes.
+ * <p>
+ * @param listener an instance of a StatusChangeListener interface
+ **/
+ public void setStatusChangeListener(StatusChangeListener listener)
+ {
+ m_statusChangeListener = listener;
+
+ if (m_hasQueuedStatus) {
+ QtNative.runAction(() -> { m_statusChangeListener.onStatusChanged(m_lastStatus); });
+ m_hasQueuedStatus = false;
+ }
+ }
+
+ private void handleStatusChange(int status)
+ {
+ m_lastStatus = status;
+
+ if (m_statusChangeListener != null)
+ QtNative.runAction(() -> { m_statusChangeListener.onStatusChanged(status); });
+ else
+ m_hasQueuedStatus = true;
+ }
+}
diff --git a/src/quick/jar/org/qtproject/qt/android/QtQuickView.qdoc b/src/quick/jar/org/qtproject/qt/android/QtQuickView.qdoc
new file mode 100644
index 0000000000..26d08e41c4
--- /dev/null
+++ b/src/quick/jar/org/qtproject/qt/android/QtQuickView.qdoc
@@ -0,0 +1,239 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtquickview-android-class.html
+ \title Qt Quick View Android Class
+ \ingroup qt_android_classes
+ \brief Allows you to add QML content to your Android app as a View.
+ \techpreview
+ \since 6.7
+
+ The QtQuickView class lets you easily add QML content to your Android app as
+ a \l {Android: View}{View}.
+
+ \target QtQuickView
+ \table
+ \row
+ \li Class:
+ \li QtQuickView
+ \row
+ \li Package Name:
+ \li org.qtproject.qt.android
+ \row
+ \li Extends:
+ \li org.qtproject.qt.android.QtView
+
+ – org.qtproject.qt.android.QtLayout
+
+ –– android.view.ViewGroup
+ \endtable
+
+ \section1 Detailed description
+
+ The QtQuickView class lets you easily add QML content to your Android app as
+ a \l {Android: View}{View}. \c QtQuickView instantiates a \l QQuickView with
+ a given QML component source (a local or network file) and embeds it to itself.
+ You can add it to your Android app's layout as with any other View. \c QtQuickView
+ is a good choice when you want to extend your non-Qt Android app with QML content but
+ do not want to make the entire app using the Qt framework. It brings the power
+ of Qt Quick into your Android app, making it possible to use various Qt Quick
+ APIs in Android apps.
+
+ A typical use of the class:
+
+ \code
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ ...
+
+ QtQuickView qmlView = new QtQuickView(this, "qrc:/qt/qml/target/main.qml", "target");
+ qmlView.setStatusChangeListener(status -> {
+ Log.i(TAG, "QML loading status changed to " + status);
+ });
+
+ // Add QML to your layout
+ layout.addView(qmlView, params);
+ ...
+ }
+ \endcode
+
+ For a more detailed example, see \l {QML in Android Studio Projects}.
+
+ \section1 Constructors
+
+ \section2 public QtQuickView(Context parent, String qmlUri, String appName)
+
+ Creates a QtQuickView to load and render a QML component. Instantiating a
+ QtQuickView will load the Qt libraries, including the app library specified
+ by \e appName. Then, it creates a QQuickView that loads the QML source specified
+ by \e qmlUri.
+
+ \section3 Parameters
+
+ \list
+ \li \b context: the parent Context.
+ \li \b qmlUri: the URI of the main QML file.
+ \li \b appName: the name of the Qt app library to load and start.
+ This corresponds to the target name set in the Qt app's CMakeLists.txt.
+ \endlist
+
+ \section3 Throws
+
+ Throws a \l {Android: InvalidParameterException}{InvalidParameterException} if
+ a parameter is invalid.
+
+ \section2 public QtQuickView(Context context, String qmlUri, String appName, String[] qmlImportPaths)
+
+ Creates a QtQuickView to load and view a QML component. Instantiating a
+ QtQuickView will load the Qt libraries, including the app library specified
+ by \e appName. Then, it creates a QQuickView that loads the QML source specified
+ by \e qmlUri. This overload accepts an array of strings \e qmlImportPaths in the
+ case where the QML application should load QML modules from custom paths.
+
+ \section3 Parameters
+
+ \list
+ \li \b context: the parent Context.
+ \li \b qmlUri: the URI of the main QML file.
+ \li \b appName: the name of the Qt app library to load and start.
+ This corresponds to the target name set in the Qt app's CMakeLists.txt.
+ \li \b qmlImportPaths: an array of strings for additional import paths to
+ be passed to.
+ \endlist
+
+ \section3 Throws
+
+ Throws a \l {Android: InvalidParameterException}{InvalidParameterException} if
+ a parameter is invalid.
+
+ \section1 Interfaces
+
+ \section2 public interface SignalListener<T>
+ \target SignalListener
+
+ Invoked on the Android UI thread when the signal has been emitted.
+
+ \section3 Parameters
+
+ \list
+ \li \b signalName: literal signal name
+ \li \b value: the value delivered by the signal or null if the signal is
+ without a parameter.
+ \endlist
+
+ \section2 public interface StatusChangeListener
+ \target StatusChangeListener
+
+ Invoked on the Android UI thread when the QML component status has changed.
+
+ \section3 Parameters
+
+ \list
+ \li \b status: The current status.
+ \endlist
+
+ \section1 Fields
+
+ \section2 Status values
+ \target Status values
+
+ The status can be \e STATUS_NULL, \e STATUS_READY, \e STATUS_LOADING or
+ \e STATUS_ERROR. For more information, see \l {QQuickView::Status}.
+
+ \section1 Methods
+
+ \section2 public void setProperty(String propertyName, Object value)
+ \target setProperty()
+
+ Sets the value of an existing property on the QML root object. The supported
+ types are \c Integer, \c Double, \c Float, \c Boolean, and \c String. These
+ types get converted to their corresponding QML types int, double/float, bool,
+ and string. This function does not add properties to the QML root object if
+ they do not exist.
+
+ \section3 Parameters
+ \list
+ \li \b propertyName: the name of the existing root object property to set its value
+ \li \b value: the value of the property
+ \endlist
+
+ \section2 public <T extends Object> T getProperty(String propertyName)
+ \target getProperty()
+
+ Gets the value of an existing property of the QML root object. The supported
+ return types are \e Integer, \e Double, \e Float, \e Boolean, and \e String.
+ These types get converted from their corresponding QML types int, double/float,
+ bool, and string.
+
+ \section3 Parameters
+ \list
+ \li \b propertyName: the name of the existing root object property.
+ \endlist
+
+ \section3 Returns
+
+ If the property does not exist or the status of the QML component is
+ anything other than \l {Status values}{STATUS_READY}, this function will return null.
+
+ \section3 Throws
+
+ Throws a \l {Android: ClassCastException}{ClassCastException} if type casting fails.
+
+ \section2 public <T> int addSignalListener(String signalName, Class<T> argType, SignalListener<T> listener)
+ \target addSignalListener()
+
+ Associates a \l {SignalListener} with a signal of the QML root object.
+
+ \section3 Parameters
+ \list
+ \li \b signalName: the name of the root object signal.
+ \li \b argType: the Class type of the signal argument.
+ \li \b listener: an instance of the SignalListener interface.
+ \endlist
+
+ \section3 Returns
+
+ A \c {Connection ID} between signal and listener or the existing connection
+ ID if there is an existing connection between the same signal and listener.
+ Returns a negative value if the signal does not exist on the QML root object.
+
+ \section2 public boolean removeSignalListener(int signalListenerId)
+
+ Stops a \l {SignalListener} with a given id obtained from \l addSignalListener()
+ call, from listening to a signal.
+
+ \section3 Parameters
+ \list
+ \li \b signalListenerId: the connection ID.
+ \endlist
+
+ \section3 Returns
+ \e True if the connection ID is valid and has been successfully removed,
+ otherwise returns false.
+
+ \section2 public int getStatus()
+ \target getStatus()
+
+ Gets the \l {Status values}{status} of the QML component.
+
+ \section3 Returns
+
+ \e STATUS_READY when the QML is ready. Invoking methods that operate on the QML
+ root object, such as \l {setProperty()}, \l {getProperty()}, and
+ \l {addSignalListener()}, would succeed \b only if the current status is
+ \c STATUS_READY. It can also return other \l {Status values}{status} values
+ representing the status of the underlying QQuickView instance.
+
+ \section2 public void setStatusChangeListener(StatusChangeListener listener)
+
+ Sets a \l {StatusChangeListener} to listen to status changes.
+
+ \section3 Parameters
+
+ \list
+ \li \b listener: an instance of a \l {StatusChangeListener} interface.
+ \endlist
+*/
diff --git a/src/quick/platform/android/qandroidquickviewembedding.cpp b/src/quick/platform/android/qandroidquickviewembedding.cpp
new file mode 100644
index 0000000000..8b25ff0deb
--- /dev/null
+++ b/src/quick/platform/android/qandroidquickviewembedding.cpp
@@ -0,0 +1,439 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtQuick/private/qandroidquickviewembedding_p.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qjniarray.h>
+#include <QtCore/qjnitypes.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQuick/qquickitem.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_JNI_CLASS(QtDelegate, "org/qtproject/qt/android/QtEmbeddedContextDelegate");
+Q_DECLARE_JNI_CLASS(QtQuickView, "org/qtproject/qt/android/QtQuickView");
+Q_DECLARE_JNI_CLASS(QtWindow, "org/qtproject/qt/android/QtWindow");
+Q_DECLARE_JNI_CLASS(View, "android/view/View");
+
+Q_DECLARE_JNI_CLASS(Void, "java/lang/Void");
+Q_DECLARE_JNI_CLASS(Integer, "java/lang/Integer");
+Q_DECLARE_JNI_CLASS(Double, "java/lang/Double");
+Q_DECLARE_JNI_CLASS(Float, "java/lang/Float");
+Q_DECLARE_JNI_CLASS(Boolean, "java/lang/Boolean");
+Q_DECLARE_JNI_CLASS(String, "java/lang/String");
+Q_DECLARE_JNI_CLASS(Class, "java/lang/Class");
+
+namespace QtAndroidQuickViewEmbedding
+{
+ void createQuickView(JNIEnv*, jobject nativeWindow, jstring qmlUri, jint width, jint height,
+ jlong parentWindowReference, QtJniTypes::StringArray qmlImportPaths)
+ {
+ static_assert (sizeof(jlong) >= sizeof(void*),
+ "Insufficient size of Java type to hold the c++ pointer");
+ const QUrl qmlUrl(QJniObject(qmlUri).toString());
+
+ QStringList importPaths;
+ if (qmlImportPaths.isValid()) {
+ QJniArray<QtJniTypes::String> importPathsArray(qmlImportPaths);
+ importPaths.reserve(importPathsArray.size());
+ for (int i = 0; i < importPathsArray.size(); ++i)
+ importPaths << importPathsArray.at(i).toString();
+ }
+
+ QMetaObject::invokeMethod(qApp, [qtViewObject = QJniObject(nativeWindow),
+ parentWindowReference,
+ width,
+ height,
+ qmlUrl,
+ importPaths] {
+ QWindow *parentWindow = reinterpret_cast<QWindow *>(parentWindowReference);
+ QQuickView *view = new QQuickView(parentWindow);
+ QQmlEngine *engine = view->engine();
+ new SignalHelper(view);
+ QObject::connect(view, &QQuickView::statusChanged,
+ [qtViewObject](QQuickView::Status status) {
+ qtViewObject.callMethod<void>("handleStatusChange", status);
+ });
+ view->setResizeMode(QQuickView::SizeRootObjectToView);
+ view->setColor(QColor(Qt::transparent));
+ view->setWidth(width);
+ view->setHeight(height);
+ for (const QString &path : importPaths)
+ engine->addImportPath(path);
+
+ const QtJniTypes::QtWindow window = reinterpret_cast<jobject>(view->winId());
+ qtViewObject.callMethod<void>("addQtWindow",
+ window,
+ reinterpret_cast<jlong>(view),
+ parentWindowReference);
+ view->setSource(qmlUrl);
+ });
+ }
+
+ void setRootObjectProperty(JNIEnv *env, jobject object, jlong windowReference,
+ jstring propertyName, jobject value)
+ {
+ Q_UNUSED(env);
+ Q_UNUSED(object);
+
+ QQuickItem *rootObject = reinterpret_cast<QQuickView *>(windowReference)->rootObject();
+ if (!rootObject) {
+ qWarning() << "QtQuickView instance does not own a root object.";
+ return;
+ }
+
+ const QString property = QJniObject(propertyName).toString();
+ const QMetaObject *rootMetaObject = rootObject->metaObject();
+ int propertyIndex = rootMetaObject->indexOfProperty(qPrintable(property));
+ if (propertyIndex < 0) {
+ qWarning("Property %s does not exist in the root QML object.", qPrintable(property));
+ return;
+ }
+
+ QMetaProperty metaProperty = rootMetaObject->property(propertyIndex);
+ const QJniObject propertyValue(value);
+ const QByteArray valueClassname = propertyValue.className();
+
+ if (valueClassname == QtJniTypes::Traits<QtJniTypes::String>::className())
+ metaProperty.write(rootObject, propertyValue.toString());
+ else if (valueClassname == QtJniTypes::Traits<QtJniTypes::Integer>::className())
+ metaProperty.write(rootObject, propertyValue.callMethod<jint>("intValue"));
+ else if (valueClassname == QtJniTypes::Traits<QtJniTypes::Double>::className())
+ metaProperty.write(rootObject, propertyValue.callMethod<jdouble>("doubleValue"));
+ else if (valueClassname == QtJniTypes::Traits<QtJniTypes::Float>::className())
+ metaProperty.write(rootObject, propertyValue.callMethod<jfloat>("floatValue"));
+ else if (valueClassname == QtJniTypes::Traits<QtJniTypes::Boolean>::className())
+ metaProperty.write(rootObject, propertyValue.callMethod<jboolean>("booleanValue"));
+ else
+ qWarning("Setting the property type of %s is not supported.", valueClassname.data());
+ }
+
+ jobject getRootObjectProperty(JNIEnv *env, jobject object, jlong windowReference,
+ jstring propertyName)
+ {
+ Q_UNUSED(object);
+ Q_ASSERT(env);
+
+ const QString property = QJniObject(propertyName).toString();
+ QQuickView *view = reinterpret_cast<QQuickView *>(windowReference);
+ QQuickItem *rootObject = view->rootObject();
+ if (!rootObject) {
+ qWarning("Cannot read property %s as the QtQuickView instance (%s)"
+ "does not own a root object.",
+ qPrintable(property),
+ qPrintable(view->source().toString()));
+ return nullptr;
+ }
+
+ const QMetaObject *rootMetaObject = rootObject->metaObject();
+ int propertyIndex = rootMetaObject->indexOfProperty(property.toUtf8().constData());
+ if (propertyIndex < 0) {
+ qWarning("Cannot read property %s as it does not exist in the root QML object.",
+ qPrintable(property));
+ return nullptr;
+ }
+
+ QMetaProperty metaProperty = rootMetaObject->property(propertyIndex);
+ QVariant propertyValue = metaProperty.read(rootObject);
+ const int propertyTypeId = propertyValue.typeId();
+
+ switch (propertyTypeId) {
+ case QMetaType::Type::Int:
+ return env->NewLocalRef(
+ QJniObject::construct<QtJniTypes::Integer>(get<int>(std::move(propertyValue)))
+ .object());
+ case QMetaType::Type::Double:
+ return env->NewLocalRef(
+ QJniObject::construct<QtJniTypes::Double>(get<double>(std::move(propertyValue)))
+ .object());
+ case QMetaType::Type::Float:
+ return env->NewLocalRef(
+ QJniObject::construct<QtJniTypes::Float>(get<float>(std::move(propertyValue)))
+ .object());
+ case QMetaType::Type::Bool:
+ return env->NewLocalRef(
+ QJniObject::construct<QtJniTypes::Boolean>(get<bool>(std::move(propertyValue)))
+ .object());
+ case QMetaType::Type::QString:
+ return env->NewLocalRef(
+ QJniObject::fromString(get<QString>(std::move(propertyValue))).object());
+ default:
+ qWarning("Property %s cannot be converted to a supported Java data type.",
+ qPrintable(property));
+ }
+
+ return nullptr;
+ }
+
+ int addRootObjectSignalListener(JNIEnv *env, jobject, jlong windowReference, jstring signalName,
+ jclass argType, jobject listener)
+ {
+ Q_ASSERT(env);
+ static QHash<QByteArray, int> javaToQMetaType = {
+ { "java/lang/Void", QMetaType::Type::Void },
+ { "java/lang/String", QMetaType::Type::QString },
+ { "java/lang/Integer", QMetaType::Type::Int },
+ { "java/lang/Double", QMetaType::Type::Double },
+ { "java/lang/Float", QMetaType::Type::Float },
+ { "java/lang/Boolean", QMetaType::Type::Bool }
+ };
+
+ QQuickView *view = reinterpret_cast<QQuickView *>(windowReference);
+ if (!view) {
+ qWarning() << "QtQuickView is not loaded or ready yet.";
+ return -1;
+ }
+ QQuickItem *rootObject = view->rootObject();
+ if (!rootObject) {
+ qWarning() << "QtQuickView instance does not own a root object.";
+ return -1;
+ }
+
+ SignalHelper *signalHelper = view->findChild<SignalHelper *>();
+ const QByteArray javaArgClass = QJniObject(argType).className();
+ const char *qArgName =
+ QMetaType(javaToQMetaType.value(javaArgClass, QMetaType::Type::UnknownType)).name();
+ const QString signalMethodName = QJniObject(signalName).toString();
+
+ const QMetaObject *metaObject = rootObject->metaObject();
+ int signalIndex = -1;
+ int propertyIndex = -1;
+
+ QByteArray signalSignature = QMetaObject::normalizedSignature(qPrintable(
+ QStringLiteral("%1(%2)").arg(signalMethodName).arg(QLatin1StringView(qArgName))));
+ signalIndex = metaObject->indexOfSignal(signalSignature.constData());
+
+ // Try to check if the signal is a parameterless notifier of a property
+ // or a property name itself.
+ if (signalIndex == -1) {
+ signalSignature = QMetaObject::normalizedSignature(
+ qPrintable(QStringLiteral("%1()").arg(signalMethodName)));
+ for (int i = 0; i < metaObject->propertyCount(); ++i) {
+ QMetaProperty metaProperty = metaObject->property(i);
+ QMetaMethod notifyMethod = metaProperty.notifySignal();
+
+ if (signalSignature == notifyMethod.methodSignature()) {
+ signalIndex = metaObject->property(i).notifySignalIndex();
+ propertyIndex = i;
+ break;
+ } else if (signalMethodName == QLatin1StringView(metaProperty.name())) {
+ signalIndex = metaObject->property(i).notifySignalIndex();
+ signalSignature = notifyMethod.methodSignature();
+ propertyIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (signalIndex == -1)
+ return -1;
+
+ const QMetaObject *helperMetaObject = signalHelper->metaObject();
+ QByteArray helperSignalSignature = signalSignature;
+ helperSignalSignature.replace(0, signalSignature.indexOf('('), "forwardSignal");
+ int helperSlotIndex = helperMetaObject->indexOfSlot(helperSignalSignature.constData());
+ if (helperSlotIndex == -1)
+ return -1;
+
+ // Return the id if the signal is already connected to the same listener.
+ QJniObject listenerJniObject(listener);
+ if (signalHelper->listenersMap.contains(signalSignature)) {
+ auto listenerInfos = signalHelper->listenersMap.values(signalSignature);
+ auto isSameListener = [listenerJniObject](const SignalHelper::ListenerInfo &listenerInfo) {
+ return listenerInfo.listener == listenerJniObject;
+ };
+ auto iterator = std::find_if(listenerInfos.constBegin(),
+ listenerInfos.constEnd(),
+ isSameListener);
+ if (iterator != listenerInfos.end()) {
+ qWarning("Signal listener with the ID of %i is already connected to %s signal.",
+ iterator->id,
+ signalSignature.constData());
+ return iterator->id;
+ }
+ }
+
+ QMetaMethod signalMethod = metaObject->method(signalIndex);
+ QMetaMethod signalForwarderMethod = helperMetaObject->method(helperSlotIndex);
+ signalHelper->connectionHandleCounter++;
+
+ QMetaObject::Connection connection;
+ if (signalHelper->listenersMap.contains(signalSignature)) {
+ connection = signalHelper
+ ->connections[signalHelper->listenersMap.value(signalSignature).id];
+ } else {
+ connection = QObject::connect(rootObject,
+ signalMethod,
+ signalHelper,
+ signalForwarderMethod);
+ }
+
+ SignalHelper::ListenerInfo listenerInfo;
+ listenerInfo.listener = listenerJniObject;
+ listenerInfo.javaArgType = javaArgClass;
+ listenerInfo.propertyIndex = propertyIndex;
+ listenerInfo.signalSignature = signalSignature;
+ listenerInfo.id = signalHelper->connectionHandleCounter;
+
+ signalHelper->listenersMap.insert(signalSignature, listenerInfo);
+ signalHelper->connections.insert(listenerInfo.id, connection);
+
+ return listenerInfo.id;
+ }
+
+ bool removeRootObjectSignalListener(JNIEnv *, jobject, jlong windowReference,
+ jint signalListenerId)
+ {
+ QQuickView *view = reinterpret_cast<QQuickView *>(windowReference);
+ QQuickItem *rootObject = view->rootObject();
+ if (!rootObject) {
+ qWarning() << "QtQuickView instance does not own a root object.";
+ return false;
+ }
+
+ SignalHelper *signalHelper = view->findChild<SignalHelper *>();
+ if (!signalHelper->connections.contains(signalListenerId))
+ return false;
+
+ QByteArray signalSignature;
+ for (auto listenerInfoIter = signalHelper->listenersMap.begin();
+ listenerInfoIter != signalHelper->listenersMap.end();) {
+ if (listenerInfoIter->id == signalListenerId) {
+ signalSignature = listenerInfoIter->signalSignature;
+ signalHelper->listenersMap.erase(listenerInfoIter);
+ break;
+ } else {
+ ++listenerInfoIter;
+ }
+ }
+
+ // disconnect if its the last listener associated with the signal signatures
+ if (!signalHelper->listenersMap.contains(signalSignature))
+ rootObject->disconnect(signalHelper->connections.value(signalListenerId));
+
+ signalHelper->connections.remove(signalListenerId);
+ return true;
+ }
+
+ void SignalHelper::forwardSignal()
+ {
+ invokeListener(sender(), senderSignalIndex(), QVariant());
+ }
+
+ void SignalHelper::forwardSignal(int signalValue)
+ {
+ invokeListener(sender(), senderSignalIndex(), QVariant(signalValue));
+ }
+
+ void SignalHelper::forwardSignal(bool signalValue)
+ {
+ invokeListener(sender(), senderSignalIndex(), QVariant(signalValue));
+ }
+
+ void SignalHelper::forwardSignal(double signalValue)
+ {
+ invokeListener(sender(), senderSignalIndex(), QVariant(signalValue));
+ }
+
+ void SignalHelper::forwardSignal(float signalValue)
+ {
+ invokeListener(sender(), senderSignalIndex(), QVariant(signalValue));
+ }
+
+ void SignalHelper::forwardSignal(QString signalValue)
+ {
+ invokeListener(sender(), senderSignalIndex(), QVariant(signalValue));
+ }
+
+ void SignalHelper::invokeListener(QObject *sender, int senderSignalIndex, QVariant signalValue)
+ {
+ using namespace QtJniTypes;
+
+ const QMetaObject *metaObject = sender->metaObject();
+ const QMetaMethod signalMethod = metaObject->method(senderSignalIndex);
+
+ for (auto listenerInfoIter = listenersMap.constFind(signalMethod.methodSignature());
+ listenerInfoIter != listenersMap.constEnd() &&
+ listenerInfoIter.key() == signalMethod.methodSignature();
+ ++listenerInfoIter) {
+ const ListenerInfo listenerInfo = *listenerInfoIter;
+ const QByteArray javaArgType = listenerInfo.javaArgType;
+ QJniObject jSignalMethodName =
+ QJniObject::fromString(QLatin1StringView(signalMethod.name()));
+
+ if (listenerInfo.propertyIndex != -1 && javaArgType != Traits<Void>::className())
+ signalValue = metaObject->property(listenerInfo.propertyIndex).read(sender);
+
+ int valueTypeId = signalValue.typeId();
+ QJniObject jValue;
+
+ switch (valueTypeId) {
+ case QMetaType::Type::UnknownType:
+ break;
+ case QMetaType::Type::Int:
+ jValue = qVariantToJniObject<Integer,jint>(signalValue);
+ break;
+ case QMetaType::Type::Double:
+ jValue = qVariantToJniObject<Double,jdouble>(signalValue);
+ break;
+ case QMetaType::Type::Float:
+ jValue = qVariantToJniObject<Float,jfloat>(signalValue);
+ break;
+ case QMetaType::Type::Bool:
+ jValue = qVariantToJniObject<Boolean,jboolean>(signalValue);
+ break;
+ case QMetaType::Type::QString:
+ jValue = QJniObject::fromString(get<QString>(std::move(signalValue)));
+ break;
+ default:
+ qWarning("Mismatching argument types between QML signal (%s) and the Java function "
+ "(%s). Sending null as argument.",
+ signalMethod.methodSignature().constData(), javaArgType.constData());
+ }
+
+ QNativeInterface::QAndroidApplication::runOnAndroidMainThread(
+ [listenerInfo, jSignalMethodName, jValue]() {
+ listenerInfo.listener.callMethod<void, jstring, jobject>("onSignalEmitted",
+ jSignalMethodName.object<jstring>(),
+ jValue.object());
+ });
+ }
+ }
+
+ bool registerNatives(QJniEnvironment& env) {
+ return env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtQuickView>::className(),
+ {Q_JNI_NATIVE_SCOPED_METHOD(createQuickView,
+ QtAndroidQuickViewEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(setRootObjectProperty,
+ QtAndroidQuickViewEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(getRootObjectProperty,
+ QtAndroidQuickViewEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(addRootObjectSignalListener,
+ QtAndroidQuickViewEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(removeRootObjectSignalListener,
+ QtAndroidQuickViewEmbedding)});
+ }
+}
+
+Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+{
+ Q_UNUSED(vm)
+ Q_UNUSED(reserved)
+
+ static bool initialized = false;
+ if (initialized)
+ return JNI_VERSION_1_6;
+ initialized = true;
+
+ QJniEnvironment env;
+ if (!env.isValid())
+ return JNI_ERR;
+ if (!QtAndroidQuickViewEmbedding::registerNatives(env))
+ return JNI_ERR;
+ return JNI_VERSION_1_6;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/platform/android/qandroidquickviewembedding_p.h b/src/quick/platform/android/qandroidquickviewembedding_p.h
new file mode 100644
index 0000000000..ace387a7f1
--- /dev/null
+++ b/src/quick/platform/android/qandroidquickviewembedding_p.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QANDROIDQUICKVIEWEMBEDDING_P_H
+#define QANDROIDQUICKVIEWEMBEDDING_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjnitypes.h>
+#include <QtQuick/qquickview.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_JNI_TYPE(StringArray, "[Ljava/lang/String;")
+
+namespace QtAndroidQuickViewEmbedding
+{
+ bool registerNatives(QJniEnvironment& env);
+ void createQuickView(JNIEnv *env, jobject nativeWindow, jstring qmlUri, jint width, jint height,
+ jlong parentWindowReference, QtJniTypes::StringArray qmlImportPaths);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(createQuickView)
+ void setRootObjectProperty(JNIEnv *env, jobject, jlong parentWindowReference,
+ jstring propertyName, jobject value);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(setRootObjectProperty)
+ jobject getRootObjectProperty(JNIEnv *env, jobject, jlong parentWindowReference,
+ jstring propertyName);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(getRootObjectProperty)
+ int addRootObjectSignalListener(JNIEnv *env, jobject, jlong parentWindowReference,
+ jstring signalName, jclass argType, jobject listener);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(addRootObjectSignalListener)
+ bool removeRootObjectSignalListener(JNIEnv *env, jobject, jlong parentWindowReference,
+ jint signalListenerId);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(removeRootObjectSignalListener)
+
+ class SignalHelper : public QObject
+ {
+ Q_OBJECT
+ public:
+ struct ListenerInfo
+ {
+ ListenerInfo() : propertyIndex(-1) { }
+ int id;
+ QJniObject listener;
+ QByteArray javaArgType;
+ QByteArray signalSignature;
+ int propertyIndex;
+ };
+
+ int connectionHandleCounter;
+ explicit SignalHelper(QQuickView *parent) : QObject(parent), connectionHandleCounter(0) { }
+ QMultiMap<QByteArray, ListenerInfo> listenersMap;
+ QHash<int, QMetaObject::Connection> connections;
+ void invokeListener(QObject *sender, int senderSignalIndex, QVariant signalValue);
+
+ template<typename JT, typename T>
+ inline QJniObject qVariantToJniObject(const QVariant& v) {
+ return QJniObject(QtJniTypes::Traits<JT>::className(), get<T>(std::move(v)));
+ };
+
+ public slots:
+ void forwardSignal();
+ void forwardSignal(int);
+ void forwardSignal(double);
+ void forwardSignal(float);
+ void forwardSignal(bool);
+ void forwardSignal(QString);
+ };
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDQUICKVIEWEMBEDDING_P_H
diff --git a/src/quick/qtquick.tracepoints b/src/quick/qtquick.tracepoints
deleted file mode 100644
index f6518444d2..0000000000
--- a/src/quick/qtquick.tracepoints
+++ /dev/null
@@ -1,48 +0,0 @@
-QSG_renderWindow_entry()
-QSG_renderWindow_exit()
-QSG_renderScene_entry()
-QSG_renderScene_exit()
-QSG_syncAndRender_entry()
-QSG_syncAndRender_exit()
-QSG_polishAndSync_entry()
-QSG_polishAndSync_exit()
-QSG_preprocess_entry()
-QSG_preprocess_exit()
-QSG_update_entry()
-QSG_update_exit()
-
-QSG_polishItems_entry()
-QSG_polishItems_exit()
-QSG_sync_entry()
-QSG_sync_exit()
-QSG_wait_entry()
-QSG_wait_exit()
-QSG_render_entry()
-QSG_render_exit()
-QSG_swap_entry()
-QSG_swap_exit()
-QSG_animations_entry()
-QSG_animations_exit()
-
-QSG_texture_prepare_entry()
-QSG_texture_prepare_exit()
-QSG_texture_bind_entry()
-QSG_texture_bind_exit()
-QSG_texture_delete_entry()
-QSG_texture_delete_exit()
-QSG_texture_upload_entry()
-QSG_texture_upload_exit()
-QSG_texture_mipmap_entry()
-QSG_texture_mipmap_exit()
-
-QSG_binding_entry()
-QSG_binding_exit()
-QSG_prepareMaterial_entry()
-QSG_prepareMaterial_exit()
-
-QSGDistanceFieldGlyphCache_update_entry(int count)
-QSGDistanceFieldGlyphCache_update_exit()
-QSGDistanceFieldGlyphCache_glyphRender_entry()
-QSGDistanceFieldGlyphCache_glyphRender_exit()
-QSGDistanceFieldGlyphCache_glyphStore_entry()
-QSGDistanceFieldGlyphCache_glyphStore_exit()
diff --git a/src/quick/qtquickglobal_p.h b/src/quick/qtquickglobal_p.h
index 7198493cda..835aeb2aa9 100644
--- a/src/quick/qtquickglobal_p.h
+++ b/src/quick/qtquickglobal_p.h
@@ -22,13 +22,13 @@
//
#include "qtquickglobal.h"
-#include <QtQuick/private/qtquickexports_p.h>
+#include <QtQuick/qtquickexports.h>
QT_BEGIN_NAMESPACE
-void Q_QUICK_PRIVATE_EXPORT qml_register_types_QtQuick();
+void Q_QUICK_EXPORT qml_register_types_QtQuick();
-void Q_QUICK_PRIVATE_EXPORT QQuick_initializeModule();
+void Q_QUICK_EXPORT QQuick_initializeModule();
Q_DECLARE_LOGGING_CATEGORY(lcTouch)
Q_DECLARE_LOGGING_CATEGORY(lcMouse)
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
index d33919f344..c9385630d9 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -178,8 +178,12 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
for (auto j = m_renderableNodes.begin(); j != m_renderableNodes.end(); ++j) {
auto node = *j;
- if (!node->isOpaque() && !m_dirtyRegion.isEmpty()) {
- // Only blended nodes need to be updated
+ if ((!node->isOpaque() || node->boundingRectMax() != node->boundingRectMin()) && !m_dirtyRegion.isEmpty()) {
+ // Blended nodes need to be updated
+ // QTBUG-113745: Also nodes with floating point boundary rectangles need to
+ // be updated. The reason is that m_obscuredRegion contains only the rounded
+ // down bounding rectangle (node->boundingRectMin()) and thus not the whole
+ // node. As a result up to 1 pixel would be overpainted when it should not.
node->addDirtyRegion(m_dirtyRegion, true);
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
index 5d27708342..b0426a0b37 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -26,7 +26,7 @@ class QSGSimpleRectNode;
class QSGSoftwareRenderableNode;
class QSGSoftwareRenderableNodeUpdater;
-class Q_QUICK_PRIVATE_EXPORT QSGAbstractSoftwareRenderer : public QSGRenderer
+class Q_QUICK_EXPORT QSGAbstractSoftwareRenderer : public QSGRenderer
{
public:
QSGAbstractSoftwareRenderer(QSGRenderContext *context);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
index 4da188bbfe..4e07508ea0 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -75,10 +75,10 @@ QSGPainterNode *QSGSoftwareContext::createPainterNode(QQuickPaintedItem *item)
return new QSGSoftwarePainterNode(item);
}
-QSGGlyphNode *QSGSoftwareContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode, int renderTypeQuality)
+QSGGlyphNode *QSGSoftwareContext::createGlyphNode(QSGRenderContext *rc, QSGTextNode::RenderType renderType, int renderTypeQuality)
{
Q_UNUSED(rc);
- Q_UNUSED(preferNativeGlyphNode);
+ Q_UNUSED(renderType);
Q_UNUSED(renderTypeQuality);
return new QSGSoftwareGlyphNode();
}
@@ -107,6 +107,17 @@ void QSGSoftwareRenderContext::initializeIfNeeded()
void QSGSoftwareRenderContext::invalidate()
{
+ qDeleteAll(m_texturesToDelete);
+ m_texturesToDelete.clear();
+
+ qDeleteAll(m_textures);
+ m_textures.clear();
+
+ Q_ASSERT(m_fontEnginesToClean.isEmpty());
+
+ qDeleteAll(m_glyphCaches);
+ m_glyphCaches.clear();
+
m_sg->renderContextInvalidated(this);
emit invalidated();
}
@@ -190,7 +201,7 @@ void *QSGSoftwareContext::getResource(QQuickWindow *window, Resource resource) c
if (resource == PainterResource)
return window->isSceneGraphInitialized() ? static_cast<QSGSoftwareRenderContext *>(cd->context)->m_activePainter : nullptr;
else if (resource == RedirectPaintDevice)
- return cd->redirect.rt.paintDevice;
+ return cd->redirect.rt.sw.paintDevice;
return nullptr;
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
index 6b95ebb1ad..68f18b490a 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
@@ -55,7 +55,7 @@ public:
QSGInternalRectangleNode *createInternalRectangleNode() override;
QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
- QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode, int renderTypeQuality) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, QSGTextNode::RenderType renderType, int renderTypeQuality) override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
QSurfaceFormat defaultSurfaceFormat() const override;
QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
index 15e8096d8d..83eef8b54f 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
@@ -59,6 +59,10 @@ void QSGSoftwareGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &g
{
m_position = position;
m_glyphRun = glyphs;
+ // Decorations handled by text node
+ m_glyphRun.setOverline(false);
+ m_glyphRun.setStrikeOut(false);
+ m_glyphRun.setUnderline(false);
m_bounding_rect = calculateBoundingRect(position, glyphs);
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
index 488f622dce..411c189b3d 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
@@ -11,6 +11,10 @@ QT_BEGIN_NAMESPACE
QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode()
: m_penWidth(0)
, m_radius(0)
+ , m_topLeftRadius(-1)
+ , m_topRightRadius(-1)
+ , m_bottomLeftRadius(-1)
+ , m_bottomRightRadius(-1)
, m_vertical(true)
, m_cornerPixmapIsDirty(true)
, m_devicePixelRatio(1)
@@ -171,6 +175,42 @@ void QSGSoftwareInternalRectangleNode::setRadius(qreal radius)
}
}
+void QSGSoftwareInternalRectangleNode::setTopLeftRadius(qreal radius)
+{
+ if (m_topLeftRadius != radius) {
+ m_topLeftRadius = radius;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setTopRightRadius(qreal radius)
+{
+ if (m_topRightRadius != radius) {
+ m_topRightRadius = radius;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setBottomLeftRadius(qreal radius)
+{
+ if (m_bottomLeftRadius != radius) {
+ m_bottomLeftRadius = radius;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setBottomRightRadius(qreal radius)
+{
+ if (m_bottomRightRadius != radius) {
+ m_bottomRightRadius = radius;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
void QSGSoftwareInternalRectangleNode::setAligned(bool /*aligned*/)
{
}
@@ -185,9 +225,8 @@ void QSGSoftwareInternalRectangleNode::update()
}
if (!m_stops.isEmpty()) {
- QLinearGradient gradient(QPoint(0,0), QPoint(m_vertical ? 0 : 1, m_vertical ? 1 : 0));
+ QLinearGradient gradient(QPoint(0,0), QPoint(m_vertical ? 0 : m_rect.width(), m_vertical ? m_rect.height() : 0));
gradient.setStops(m_stops);
- gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
m_brush = QBrush(gradient);
} else {
m_brush = QBrush(m_color);
@@ -212,13 +251,21 @@ void QSGSoftwareInternalRectangleNode::paint(QPainter *painter)
//Rotated rectangles lose the benefits of direct rendering, and have poor rendering
//quality when using only blits and fills.
- if (m_radius == 0 && m_penWidth == 0) {
+ if (m_radius == 0
+ && m_penWidth == 0
+ && m_topLeftRadius <= 0
+ && m_topRightRadius <= 0
+ && m_bottomLeftRadius <= 0
+ && m_bottomRightRadius <= 0) {
//Non-Rounded Rects without borders (fall back to drawRect)
//Most common case
painter->setPen(Qt::NoPen);
painter->setBrush(m_brush);
painter->drawRect(m_rect);
- } else {
+ } else if (m_topLeftRadius < 0
+ && m_topRightRadius < 0
+ && m_bottomLeftRadius < 0
+ && m_bottomRightRadius < 0) {
//Rounded Rects and Rects with Borders
//Avoids broken behaviors of QPainter::drawRect/roundedRect
QPixmap pixmap = QPixmap(qRound(m_rect.width() * m_devicePixelRatio), qRound(m_rect.height() * m_devicePixelRatio));
@@ -231,12 +278,34 @@ void QSGSoftwareInternalRectangleNode::paint(QPainter *painter)
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
painter->drawPixmap(m_rect, pixmap);
painter->setRenderHints(previousRenderHints);
+ } else {
+ // Corners with different radii. Split implementation to avoid
+ // performance regression of the majority of cases
+ QPixmap pixmap = QPixmap(qRound(m_rect.width() * m_devicePixelRatio), qRound(m_rect.height() * m_devicePixelRatio));
+ pixmap.fill(Qt::transparent);
+ pixmap.setDevicePixelRatio(m_devicePixelRatio);
+ QPainter pixmapPainter(&pixmap);
+ // Slow function relying on paths
+ paintRectangleIndividualCorners(&pixmapPainter, QRect(0, 0, m_rect.width(), m_rect.height()));
+
+ QPainter::RenderHints previousRenderHints = painter->renderHints();
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
+ painter->drawPixmap(m_rect, pixmap);
+ painter->setRenderHints(previousRenderHints);
+
}
} else {
//Paint directly
- paintRectangle(painter, m_rect);
+ if (m_topLeftRadius < 0
+ && m_topRightRadius < 0
+ && m_bottomLeftRadius < 0
+ && m_bottomRightRadius < 0) {
+ paintRectangle(painter, m_rect);
+ } else {
+ paintRectangleIndividualCorners(painter, m_rect);
+ }
}
}
@@ -387,6 +456,75 @@ void QSGSoftwareInternalRectangleNode::paintRectangle(QPainter *painter, const Q
painter->setRenderHints(previousRenderHints);
}
+void QSGSoftwareInternalRectangleNode::paintRectangleIndividualCorners(QPainter *painter, const QRect &rect)
+{
+ QPainterPath path;
+
+ const float w = m_penWidth;
+
+ // Radius should never exceeds half of the width or half of the height
+ const float radiusTL = qMin(qMin(rect.width(), rect.height()) * 0.5f, float(m_topLeftRadius < 0. ? m_radius : m_topLeftRadius));
+ const float radiusTR = qMin(qMin(rect.width(), rect.height()) * 0.5f, float(m_topRightRadius < 0. ? m_radius : m_topRightRadius));
+ const float radiusBL = qMin(qMin(rect.width(), rect.height()) * 0.5f, float(m_bottomLeftRadius < 0. ? m_radius : m_bottomLeftRadius));
+ const float radiusBR = qMin(qMin(rect.width(), rect.height()) * 0.5f, float(m_bottomRightRadius < 0 ? m_radius : m_bottomRightRadius));
+
+ const float innerRadiusTL = qMin(qMin(rect.width(), rect.height()) * 0.5f, radiusTL - w);
+ const float innerRadiusTR = qMin(qMin(rect.width(), rect.height()) * 0.5f, radiusTR - w);
+ const float innerRadiusBL = qMin(qMin(rect.width(), rect.height()) * 0.5f, radiusBL - w);
+ const float innerRadiusBR = qMin(qMin(rect.width(), rect.height()) * 0.5f, radiusBR - w);
+
+ QRect rect2 = rect.adjusted(0, 0, 1, 1);
+
+ path.moveTo(rect2.topRight() - QPointF(radiusTR, -w));
+ if (innerRadiusTR > 0.)
+ path.arcTo(QRectF(rect2.topRight() - QPointF(radiusTR + innerRadiusTR, -w), 2. * QSizeF(innerRadiusTR, innerRadiusTR)), 90, -90);
+ else
+ path.lineTo(rect2.topRight() - QPointF(w, -w));
+
+ if (innerRadiusBR > 0.)
+ path.arcTo(QRectF(rect2.bottomRight() - QPointF(radiusBR + innerRadiusBR, radiusBR + innerRadiusBR), 2. * QSizeF(innerRadiusBR, innerRadiusBR)), 0, -90);
+ else
+ path.lineTo(rect2.bottomRight() - QPointF(w, w));
+
+ if (innerRadiusBL > 0.)
+ path.arcTo(QRectF(rect2.bottomLeft() - QPointF(-w, radiusBL + innerRadiusBL), 2. * QSizeF(innerRadiusBL, innerRadiusBL)), -90, -90);
+ else
+ path.lineTo(rect2.bottomLeft() - QPointF(-w, w));
+ if (innerRadiusTL > 0.)
+ path.arcTo(QRectF(rect2.topLeft() + QPointF(w, w), 2. * QSizeF(innerRadiusTL, innerRadiusTL)), -180, -90);
+ else
+ path.lineTo(rect2.topLeft() + QPointF(w, w));
+ path.closeSubpath();
+
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_brush);
+ painter->drawPath(path);
+
+ if (w > 0) {
+ path.moveTo(rect2.topRight() - QPointF(radiusTR, 0.));
+ if (radiusTR > 0.)
+ path.arcTo(QRectF(rect2.topRight() - 2. * QPointF(radiusTR, 0.), 2. * QSizeF(radiusTR, radiusTR)), 90, -90);
+ else
+ path.lineTo(rect2.topRight());
+ if (radiusBR > 0.)
+ path.arcTo(QRectF(rect2.bottomRight() - 2. * QPointF(radiusBR, radiusBR), 2. * QSizeF(radiusBR, radiusBR)), 0, -90);
+ else
+ path.lineTo(rect2.bottomRight());
+ if (radiusBL > 0.)
+ path.arcTo(QRectF(rect2.bottomLeft() - 2. * QPointF(0., radiusBL), 2. * QSizeF(radiusBL, radiusBL)), -90, -90);
+ else
+ path.lineTo(rect2.bottomLeft());
+ if (radiusTL > 0.)
+ path.arcTo(QRectF(rect2.topLeft() - 2. * QPointF(0., 0.), 2. * QSizeF(radiusTL, radiusTL)), -180, -90);
+ else
+ path.lineTo(rect2.topLeft());
+ path.closeSubpath();
+
+ painter->setBrush(m_penColor);
+ painter->drawPath(path);
+ }
+}
+
void QSGSoftwareInternalRectangleNode::generateCornerPixmap()
{
//Generate new corner Pixmap
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
index bf815f42df..ac58e7b254 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
@@ -35,6 +35,10 @@ public:
void setGradientStops(const QGradientStops &stops) override;
void setGradientVertical(bool vertical) override;
void setRadius(qreal radius) override;
+ void setTopLeftRadius(qreal radius) override;
+ void setTopRightRadius(qreal radius) override;
+ void setBottomLeftRadius(qreal radius) override;
+ void setBottomRightRadius(qreal radius) override;
void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing); }
void setAligned(bool aligned) override;
@@ -46,14 +50,19 @@ public:
QRectF rect() const;
private:
void paintRectangle(QPainter *painter, const QRect &rect);
+ void paintRectangleIndividualCorners(QPainter *painter, const QRect &rect);
void generateCornerPixmap();
QRect m_rect;
QColor m_color;
QColor m_penColor;
- double m_penWidth;
+ qreal m_penWidth;
QGradientStops m_stops;
- double m_radius;
+ qreal m_radius;
+ qreal m_topLeftRadius;
+ qreal m_topRightRadius;
+ qreal m_bottomLeftRadius;
+ qreal m_bottomRightRadius;
QPen m_pen;
QBrush m_brush;
bool m_vertical;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp
index 75b106f464..a200b84af8 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp
@@ -137,7 +137,10 @@ void QSGSoftwarePainterNode::update()
void QSGSoftwarePainterNode::paint(QPainter *painter)
{
+ bool before = painter->testRenderHint(QPainter::SmoothPixmapTransform);
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, m_linear_filtering);
painter->drawPixmap(0, 0, m_size.width(), m_size.height(), m_pixmap);
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, before);
}
void QSGSoftwarePainterNode::paint()
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
index e59f894d7f..79fa4a78ad 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -225,8 +225,8 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
return QRegion();
} else {
QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode);
- QMatrix4x4 m = m_transform;
- rd->m_matrix = &m;
+ rd->m_localMatrix = m_transform;
+ rd->m_matrix = &rd->m_localMatrix;
rd->m_opacity = m_opacity;
// all the clip region below is in world coordinates, taking m_transform into account already
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
index 5e695bd615..281539b3cb 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
@@ -36,7 +36,7 @@ class QSGSoftwareNinePatchNode;
class QSGSoftwareSpriteNode;
class QSGRenderNode;
-class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderableNode
+class Q_QUICK_EXPORT QSGSoftwareRenderableNode
{
public:
enum NodeType {
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
index 9dff08cf42..e87f79f8f3 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QPaintDevice;
class QBackingStore;
-class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer
+class Q_QUICK_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer
{
public:
QSGSoftwareRenderer(QSGRenderContext *context);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
index 54bcdf9b49..39aa89472f 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
@@ -29,23 +29,6 @@
QT_BEGIN_NAMESPACE
-// Passed from the RL to the RT when a window is removed obscured and should be
-// removed from the render loop.
-const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
-
-// Passed from the RL to RT when GUI has been locked, waiting for sync.
-const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
-
-// Passed by the RL to the RT to maybe release resource if no windows are
-// rendering.
-const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
-
-// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called.
-const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
-
-// Passed by the window when there is a render job to run.
-const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
-
class QSGSoftwareWindowEvent : public QEvent
{
public:
@@ -57,7 +40,7 @@ class QSGSoftwareTryReleaseEvent : public QSGSoftwareWindowEvent
{
public:
QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
- : QSGSoftwareWindowEvent(win, WM_TryRelease), destroying(destroy) { }
+ : QSGSoftwareWindowEvent(win, QEvent::Type(WM_TryRelease)), destroying(destroy) { }
bool destroying;
};
@@ -65,7 +48,7 @@ class QSGSoftwareSyncEvent : public QSGSoftwareWindowEvent
{
public:
QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
- : QSGSoftwareWindowEvent(c, WM_RequestSync)
+ : QSGSoftwareWindowEvent(c, QEvent::Type(WM_RequestSync))
, size(c->size())
, dpr(c->effectiveDevicePixelRatio())
, syncInExpose(inExpose)
@@ -80,7 +63,7 @@ class QSGSoftwareGrabEvent : public QSGSoftwareWindowEvent
{
public:
QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
- : QSGSoftwareWindowEvent(c, WM_Grab), image(result) { }
+ : QSGSoftwareWindowEvent(c, QEvent::Type(WM_Grab)), image(result) { }
QImage *image;
};
@@ -88,7 +71,7 @@ class QSGSoftwareJobEvent : public QSGSoftwareWindowEvent
{
public:
QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
- : QSGSoftwareWindowEvent(c, WM_PostJob), job(postedJob) { }
+ : QSGSoftwareWindowEvent(c, QEvent::Type(WM_PostJob)), job(postedJob) { }
~QSGSoftwareJobEvent() { delete job; }
QRunnable *job;
};
@@ -516,11 +499,11 @@ void QSGSoftwareRenderThread::syncAndRender()
QQuickProfiler::SceneGraphRenderLoopSwap);
}
-template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window)
+QSGSoftwareThreadedRenderLoop::WindowData *QSGSoftwareThreadedRenderLoop::windowFor(QQuickWindow *window)
{
- for (const T &t : list) {
+ for (const auto &t : std::as_const(m_windows)) {
if (t.window == window)
- return const_cast<T *>(&t);
+ return const_cast<WindowData *>(&t);
}
return nullptr;
}
@@ -552,7 +535,7 @@ void QSGSoftwareThreadedRenderLoop::hide(QQuickWindow *window)
qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window;
if (window->isExposed())
- handleObscurity(windowFor(m_windows, window));
+ handleObscurity(windowFor(window));
releaseResources(window);
}
@@ -569,7 +552,7 @@ void QSGSoftwareThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
{
qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window;
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
if (!w)
return;
@@ -603,7 +586,7 @@ void QSGSoftwareThreadedRenderLoop::exposureChanged(QQuickWindow *window)
if (window->isExposed()) {
handleExposure(window);
} else {
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
if (w)
handleObscurity(w);
}
@@ -613,13 +596,13 @@ QImage QSGSoftwareThreadedRenderLoop::grab(QQuickWindow *window)
{
qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window;
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
// Have to support invisible (but created()'ed) windows as well.
// Unlike with GL, leaving that case for QQuickWindow to handle is not feasible.
const bool tempExpose = !w;
if (tempExpose) {
handleExposure(window);
- w = windowFor(m_windows, window);
+ w = windowFor(window);
Q_ASSERT(w);
}
@@ -650,7 +633,7 @@ QImage QSGSoftwareThreadedRenderLoop::grab(QQuickWindow *window)
void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window)
{
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
if (!w)
return;
@@ -667,7 +650,7 @@ void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window)
void QSGSoftwareThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
{
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
if (w)
scheduleUpdate(w);
}
@@ -676,7 +659,7 @@ void QSGSoftwareThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
{
qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window;
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
if (w)
polishAndSync(w, false);
}
@@ -700,14 +683,14 @@ void QSGSoftwareThreadedRenderLoop::releaseResources(QQuickWindow *window)
{
qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window;
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
if (w)
handleResourceRelease(w, false);
}
void QSGSoftwareThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
{
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
if (w && w->thread && w->thread->exposedWindow)
w->thread->postEvent(new QSGSoftwareJobEvent(window, job));
else
@@ -791,7 +774,7 @@ void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window)
{
qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window;
- WindowData *w = windowFor(m_windows, window);
+ WindowData *w = windowFor(window);
if (!w) {
qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list");
WindowData win;
@@ -851,7 +834,7 @@ void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoo
if (w->thread->isRunning()) {
w->thread->mutex.lock();
- w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, WM_Obscure));
+ w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, QEvent::Type(WM_Obscure)));
w->thread->waitCondition.wait(&w->thread->mutex);
w->thread->mutex.unlock();
}
@@ -921,7 +904,7 @@ void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop:
// Flush pending touch events.
QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
// The delivery of the event might have caused the window to stop rendering
- w = windowFor(m_windows, window);
+ w = windowFor(window);
if (!w || !w->thread || !w->thread->exposedWindow) {
qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort");
return;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
index adb3c401db..02adb28ab3 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
@@ -61,6 +61,8 @@ private:
uint forceRenderPass : 1;
};
+ WindowData *windowFor(QQuickWindow *window);
+
void startOrStopAnimationTimer();
void handleExposure(QQuickWindow *window);
void handleObscurity(WindowData *w);
@@ -72,7 +74,7 @@ private:
QAnimationDriver *m_anim;
int animationTimer = 0;
bool lockedForSync = false;
- QVector<WindowData> m_windows;
+ QList<WindowData> m_windows;
friend class QSGSoftwareRenderThread;
};
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
index 0e33312a9e..629c6427b8 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
@@ -6,7 +6,7 @@
#include <QDebug>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickitem_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
index a9ff902e6e..eef95a3fee 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
@@ -18,7 +18,7 @@
#include <private/qtexturefiledata_p.h>
#include <private/qsgcontext_p.h>
#include <private/qsgtexture_p.h>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QQuickTextureFactory>
#include <QOpenGLFunctions>
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TEXTUREIO);
-class Q_QUICK_PRIVATE_EXPORT QSGCompressedTexture : public QSGTexture
+class Q_QUICK_EXPORT QSGCompressedTexture : public QSGTexture
{
Q_OBJECT
public:
@@ -63,7 +63,7 @@ namespace QSGOpenGLAtlasTexture {
class Manager;
}
-class Q_QUICK_PRIVATE_EXPORT QSGCompressedTextureFactory : public QQuickTextureFactory
+class Q_QUICK_EXPORT QSGCompressedTextureFactory : public QQuickTextureFactory
{
public:
QSGCompressedTextureFactory(const QTextureFileData& texData);
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
index a51521eeaa..ffa5599231 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
@@ -14,19 +14,6 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \enum QSGAbstractRenderer::ClearModeBit
-
- Used with setClearMode() to indicate which buffer should
- be cleared before the scene render.
-
- \value ClearColorBuffer Clear the color buffer using clearColor().
- \value ClearDepthBuffer Clear the depth buffer.
- \value ClearStencilBuffer Clear the stencil buffer.
-
- \sa setClearMode(), setClearColor()
- */
-
-/*!
\enum QSGAbstractRenderer::MatrixTransformFlag
Used with setProjectionMatrixToRect() to indicate the expectations towards
@@ -61,8 +48,9 @@ QT_BEGIN_NAMESPACE
QSGAbstractRendererPrivate::QSGAbstractRendererPrivate()
: m_root_node(nullptr)
, m_clear_color(Qt::transparent)
- , m_clear_mode(QSGAbstractRenderer::ClearColorBuffer | QSGAbstractRenderer::ClearDepthBuffer)
{
+ m_projection_matrix.resize(1);
+ m_projection_matrix_native_ndc.resize(1);
}
/*!
@@ -193,15 +181,7 @@ QRect QSGAbstractRenderer::viewportRect() const
*/
void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect)
{
- QMatrix4x4 matrix;
- matrix.ortho(rect.x(),
- rect.x() + rect.width(),
- rect.y() + rect.height(),
- rect.y(),
- 1,
- -1);
- setProjectionMatrix(matrix);
- setProjectionMatrixWithNativeNDC(matrix);
+ setProjectionMatrixToRect(rect, {}, false);
}
/*!
@@ -217,46 +197,77 @@ void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect)
*/
void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect, MatrixTransformFlags flags)
{
+ setProjectionMatrixToRect(rect, flags, flags.testFlag(MatrixTransformFlipY));
+}
+
+/*!
+ Convenience method that calls setProjectionMatrix() with an
+ orthographic matrix generated from \a rect.
+
+ Set MatrixTransformFlipY in \a flags when the graphics API uses Y down in
+ its normalized device coordinate system (for example, Vulkan).
+
+ Convenience method that calls setProjectionMatrixWithNativeNDC() with an
+ orthographic matrix generated from \a rect.
+
+ Set true to \a nativeNDCFlipY to flip the Y axis relative to
+ projection matrix in its normalized device coordinate system.
+
+ \sa setProjectionMatrix(), projectionMatrix()
+ \sa setProjectionMatrixWithNativeNDC(), projectionMatrixWithNativeNDC()
+
+ \since 6.7
+ */
+void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect, MatrixTransformFlags flags,
+ bool nativeNDCFlipY)
+{
const bool flipY = flags.testFlag(MatrixTransformFlipY);
+
+ const float left = rect.x();
+ const float right = rect.x() + rect.width();
+ float bottom = rect.y() + rect.height();
+ float top = rect.y();
+
+ if (flipY)
+ std::swap(top, bottom);
+
QMatrix4x4 matrix;
- matrix.ortho(rect.x(),
- rect.x() + rect.width(),
- flipY ? rect.y() : rect.y() + rect.height(),
- flipY ? rect.y() + rect.height() : rect.y(),
- 1,
- -1);
- setProjectionMatrix(matrix);
-
- if (flipY) {
+ matrix.ortho(left, right, bottom, top, 1, -1);
+ setProjectionMatrix(matrix, 0);
+
+ if (nativeNDCFlipY) {
+ std::swap(top, bottom);
+
matrix.setToIdentity();
- matrix.ortho(rect.x(),
- rect.x() + rect.width(),
- rect.y() + rect.height(),
- rect.y(),
- 1,
- -1);
+ matrix.ortho(left, right, bottom, top, 1, -1);
}
- setProjectionMatrixWithNativeNDC(matrix);
+ setProjectionMatrixWithNativeNDC(matrix, 0);
}
/*!
Use \a matrix to project the QSGNode coordinates onto surface pixels.
+ \a index specifies the view index when multiview rendering is in use.
+
\sa projectionMatrix(), setProjectionMatrixToRect()
*/
-void QSGAbstractRenderer::setProjectionMatrix(const QMatrix4x4 &matrix)
+void QSGAbstractRenderer::setProjectionMatrix(const QMatrix4x4 &matrix, int index)
{
Q_D(QSGAbstractRenderer);
- d->m_projection_matrix = matrix;
+ if (d->m_projection_matrix.count() <= index)
+ d->m_projection_matrix.resize(index + 1);
+ d->m_projection_matrix[index] = matrix;
}
/*!
\internal
*/
-void QSGAbstractRenderer::setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix)
+void QSGAbstractRenderer::setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix, int index)
{
Q_D(QSGAbstractRenderer);
- d->m_projection_matrix_native_ndc = matrix;
+ if (d->m_projection_matrix_native_ndc.count() <= index)
+ d->m_projection_matrix_native_ndc.resize(index + 1);
+ d->m_projection_matrix_native_ndc[index] = matrix;
}
/*!
@@ -264,67 +275,54 @@ void QSGAbstractRenderer::setProjectionMatrixWithNativeNDC(const QMatrix4x4 &mat
\sa setProjectionMatrix(), setProjectionMatrixToRect()
*/
-QMatrix4x4 QSGAbstractRenderer::projectionMatrix() const
+QMatrix4x4 QSGAbstractRenderer::projectionMatrix(int index) const
{
Q_D(const QSGAbstractRenderer);
- return d->m_projection_matrix;
+ return d->m_projection_matrix[index];
}
-/*!
- \internal
- */
-QMatrix4x4 QSGAbstractRenderer::projectionMatrixWithNativeNDC() const
+int QSGAbstractRenderer::projectionMatrixCount() const
{
Q_D(const QSGAbstractRenderer);
- return d->m_projection_matrix_native_ndc;
+ return d->m_projection_matrix.count();
}
-/*!
- Use \a color to clear the framebuffer when clearMode() is
- set to QSGAbstractRenderer::ClearColorBuffer.
-
- \sa clearColor(), setClearMode()
- */
-void QSGAbstractRenderer::setClearColor(const QColor &color)
+int QSGAbstractRenderer::projectionMatrixWithNativeNDCCount() const
{
- Q_D(QSGAbstractRenderer);
- d->m_clear_color = color;
+ Q_D(const QSGAbstractRenderer);
+ return d->m_projection_matrix_native_ndc.count();
}
/*!
- Returns the color that clears the framebuffer at the beginning
- of the rendering.
-
- \sa setClearColor(), clearMode()
+ \internal
*/
-QColor QSGAbstractRenderer::clearColor() const
+QMatrix4x4 QSGAbstractRenderer::projectionMatrixWithNativeNDC(int index) const
{
Q_D(const QSGAbstractRenderer);
- return d->m_clear_color;
+ return d->m_projection_matrix_native_ndc[index];
}
/*!
- Defines which attachment of the framebuffer should be cleared
- before each scene render with the \a mode flag.
+ Sets the \a color to clear the framebuffer.
- \sa clearMode(), setClearColor()
+ \sa clearColor()
*/
-void QSGAbstractRenderer::setClearMode(ClearMode mode)
+void QSGAbstractRenderer::setClearColor(const QColor &color)
{
Q_D(QSGAbstractRenderer);
- d->m_clear_mode = mode;
+ d->m_clear_color = color;
}
/*!
- Flags defining which attachment of the framebuffer will be cleared
- before each scene render.
+ Returns the color that clears the framebuffer at the beginning
+ of the rendering.
- \sa setClearMode(), clearColor()
+ \sa setClearColor()
*/
-QSGAbstractRenderer::ClearMode QSGAbstractRenderer::clearMode() const
+QColor QSGAbstractRenderer::clearColor() const
{
Q_D(const QSGAbstractRenderer);
- return d->m_clear_mode;
+ return d->m_clear_color;
}
/*!
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
index ec2cb66662..5d7a3b13b6 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
@@ -27,19 +27,10 @@ QT_BEGIN_NAMESPACE
class QSGAbstractRendererPrivate;
-class Q_QUICK_PRIVATE_EXPORT QSGAbstractRenderer : public QObject
+class Q_QUICK_EXPORT QSGAbstractRenderer : public QObject
{
Q_OBJECT
public:
- enum ClearModeBit
- {
- ClearColorBuffer = 0x0001,
- ClearDepthBuffer = 0x0002,
- ClearStencilBuffer = 0x0004
- };
- Q_DECLARE_FLAGS(ClearMode, ClearModeBit)
- Q_FLAG(ClearMode)
-
enum MatrixTransformFlag
{
MatrixTransformFlipY = 0x01
@@ -61,17 +52,18 @@ public:
void setProjectionMatrixToRect(const QRectF &rect);
void setProjectionMatrixToRect(const QRectF &rect, MatrixTransformFlags flags);
- void setProjectionMatrix(const QMatrix4x4 &matrix);
- void setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix);
- QMatrix4x4 projectionMatrix() const;
- QMatrix4x4 projectionMatrixWithNativeNDC() const;
+ void setProjectionMatrixToRect(const QRectF &rect, MatrixTransformFlags flags,
+ bool nativeNDCFlipY);
+ void setProjectionMatrix(const QMatrix4x4 &matrix, int index = 0);
+ void setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix, int index = 0);
+ QMatrix4x4 projectionMatrix(int index) const;
+ QMatrix4x4 projectionMatrixWithNativeNDC(int index) const;
+ int projectionMatrixCount() const;
+ int projectionMatrixWithNativeNDCCount() const;
void setClearColor(const QColor &color);
QColor clearColor() const;
- void setClearMode(ClearMode mode);
- ClearMode clearMode() const;
-
virtual void renderScene() = 0;
virtual void prepareSceneInline();
@@ -89,8 +81,6 @@ private:
friend class QSGRootNode;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSGAbstractRenderer::ClearMode)
-
QT_END_NAMESPACE
#endif
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p_p.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p_p.h
index afac489a6a..3bc04247db 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p_p.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QSGAbstractRendererPrivate : public QObjectPrivate
+class Q_QUICK_EXPORT QSGAbstractRendererPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QSGAbstractRenderer)
public:
@@ -36,13 +36,12 @@ public:
QSGRootNode *m_root_node;
QColor m_clear_color;
- QSGAbstractRenderer::ClearMode m_clear_mode;
QRect m_device_rect;
QRect m_viewport_rect;
- QMatrix4x4 m_projection_matrix;
- QMatrix4x4 m_projection_matrix_native_ndc;
+ QVarLengthArray<QMatrix4x4, 1> m_projection_matrix;
+ QVarLengthArray<QMatrix4x4, 1> m_projection_matrix_native_ndc;
uint m_mirrored : 1;
};
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index cbf459555f..5746d984bf 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
#ifndef QT_NO_DEBUG
-Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
+Q_QUICK_EXPORT bool qsg_test_and_clear_material_failure();
#endif
int qt_sg_envInt(const char *name, int defaultValue);
@@ -201,13 +201,22 @@ QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode)
return topology;
}
+void qsg_setMultiViewFlagsOnMaterial(QSGMaterial *material, int multiViewCount)
+{
+ material->setFlag(QSGMaterial::MultiView2, multiViewCount == 2);
+ material->setFlag(QSGMaterial::MultiView3, multiViewCount == 3);
+ material->setFlag(QSGMaterial::MultiView4, multiViewCount == 4);
+}
+
ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material,
const QSGGeometry *geometry,
- QSGRendererInterface::RenderMode renderMode)
+ QSGRendererInterface::RenderMode renderMode,
+ int multiViewCount)
{
- QSGMaterialType *type = material->type();
+ qsg_setMultiViewFlagsOnMaterial(material, multiViewCount);
- ShaderKey key = qMakePair(type, renderMode);
+ QSGMaterialType *type = material->type();
+ ShaderKey key = { type, renderMode, multiViewCount };
Shader *shader = rewrittenShaders.value(key, nullptr);
if (shader)
return shader;
@@ -219,8 +228,8 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material,
shader->inputLayout = calculateVertexInputLayout(s, geometry, true);
QSGMaterialShaderPrivate *sD = QSGMaterialShaderPrivate::get(s);
shader->stages = {
- { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage), QShader::BatchableVertexShader },
- { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ { QRhiShaderStage::Vertex, sD->shader(QShader::VertexStage), QShader::BatchableVertexShader },
+ { QRhiShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
};
shader->lastOpacity = 0;
@@ -231,11 +240,13 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material,
ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material,
const QSGGeometry *geometry,
- QSGRendererInterface::RenderMode renderMode)
+ QSGRendererInterface::RenderMode renderMode,
+ int multiViewCount)
{
- QSGMaterialType *type = material->type();
+ qsg_setMultiViewFlagsOnMaterial(material, multiViewCount);
- ShaderKey key = qMakePair(type, renderMode);
+ QSGMaterialType *type = material->type();
+ ShaderKey key = { type, renderMode, multiViewCount };
Shader *shader = stockShaders.value(key, nullptr);
if (shader)
return shader;
@@ -247,8 +258,8 @@ ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *mate
shader->inputLayout = calculateVertexInputLayout(s, geometry, false);
QSGMaterialShaderPrivate *sD = QSGMaterialShaderPrivate::get(s);
shader->stages = {
- { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage) },
- { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ { QRhiShaderStage::Vertex, sD->shader(QShader::VertexStage) },
+ { QRhiShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
};
shader->lastOpacity = 0;
@@ -696,7 +707,7 @@ BatchCompatibility Batch::isMaterialCompatible(Element *e) const
QSGMaterial *m = e->node->activeMaterial();
QSGMaterial *nm = n->node->activeMaterial();
- return (nm->type() == m->type() && nm->compare(m) == 0)
+ return (nm->type() == m->type() && nm->viewCount() == m->viewCount() && nm->compare(m) == 0)
? BatchIsCompatible
: BatchBreaksOnCompare;
}
@@ -839,6 +850,8 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx, QSGRendererInterface::RenderMod
, m_elementsToDelete(64)
, m_tmpAlphaElements(16)
, m_tmpOpaqueElements(16)
+ , m_vboPool(16)
+ , m_iboPool(16)
, m_rebuild(FullRebuild)
, m_zRange(0)
#if defined(QSGBATCHRENDERER_INVALIDATE_WEDGED_NODES)
@@ -915,6 +928,10 @@ Renderer::~Renderer()
qsg_wipeBatch(m_alphaBatches.at(i));
for (int i = 0; i < m_batchPool.size(); ++i)
qsg_wipeBatch(m_batchPool.at(i));
+ for (int i = 0; i < m_vboPool.size(); ++i)
+ delete m_vboPool.at(i);
+ for (int i = 0; i < m_iboPool.size(); ++i)
+ delete m_iboPool.at(i);
}
for (Node *n : std::as_const(m_nodes)) {
@@ -960,12 +977,28 @@ void Renderer::releaseCachedResources()
m_rhi->releaseCachedResources();
- m_vertexUploadPool.resize(0);
- m_indexUploadPool.resize(0);
+ m_vertexUploadPool.shrink(0);
+ m_vertexUploadPool.reset();
+ m_indexUploadPool.shrink(0);
+ m_indexUploadPool.reset();
+
+ for (int i = 0; i < m_vboPool.size(); ++i)
+ delete m_vboPool.at(i);
+ m_vboPool.reset();
+
+ for (int i = 0; i < m_iboPool.size(); ++i)
+ delete m_iboPool.at(i);
+ m_iboPool.reset();
}
void Renderer::invalidateAndRecycleBatch(Batch *b)
{
+ if (b->vbo.buf != nullptr)
+ m_vboPool.add(b->vbo.buf);
+ if (b->ibo.buf != nullptr)
+ m_iboPool.add(b->ibo.buf);
+ b->vbo.buf = nullptr;
+ b->ibo.buf = nullptr;
b->invalidate();
for (int i=0; i<m_batchPool.size(); ++i)
if (b == m_batchPool.at(i))
@@ -994,8 +1027,9 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
{
// Batches are pooled and reused which means the QRhiBuffer will be
// still valid in a recycled Batch. We only hit the newBuffer() path
- // for brand new Batches.
- if (!buffer->buf) {
+ // when there are no buffers to recycle.
+ QDataBuffer<QRhiBuffer *> *bufferPool = isIndexBuf ? &m_iboPool : &m_vboPool;
+ if (!buffer->buf && bufferPool->isEmpty()) {
buffer->buf = m_rhi->newBuffer(QRhiBuffer::Immutable,
isIndexBuf ? QRhiBuffer::IndexBuffer : QRhiBuffer::VertexBuffer,
buffer->size);
@@ -1005,6 +1039,28 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
buffer->buf = nullptr;
}
} else {
+ if (!buffer->buf) {
+ const quint32 expectedSize = buffer->size;
+ qsizetype foundBufferIndex = 0;
+ for (qsizetype i = 0; i < bufferPool->size(); ++i) {
+ QRhiBuffer *testBuffer = bufferPool->at(i);
+ if (!buffer->buf
+ || (testBuffer->size() >= expectedSize && testBuffer->size() < buffer->buf->size())
+ || (testBuffer->size() < expectedSize && testBuffer->size() > buffer->buf->size())) {
+ foundBufferIndex = i;
+ buffer->buf = testBuffer;
+ if (buffer->buf->size() == expectedSize)
+ break;
+ }
+ }
+
+ if (foundBufferIndex < bufferPool->size() - 1) {
+ qSwap(bufferPool->data()[foundBufferIndex],
+ bufferPool->data()[bufferPool->size() - 1]);
+ }
+ bufferPool->pop_back();
+ }
+
bool needsRebuild = false;
if (buffer->buf->size() < buffer->size) {
buffer->buf->setSize(buffer->size);
@@ -1220,8 +1276,13 @@ void Renderer::nodeWasRemoved(Node *node)
if (e) {
e->removed = true;
m_elementsToDelete.add(e);
- if (m_renderNodeElements.isEmpty())
+ if (m_renderNodeElements.isEmpty()) {
m_forceNoDepthBuffer = false;
+ // Must have a full rebuild given useDepthBuffer() now returns
+ // a different value than before, meaning there can once again
+ // be an opaque pass.
+ m_rebuild |= FullRebuild;
+ }
if (e->batch != nullptr)
e->batch->needsPurge = true;
@@ -1680,6 +1741,7 @@ void Renderer::prepareOpaqueBatches()
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
+ && gni->activeMaterial()->viewCount() == gnj->activeMaterial()->viewCount()
&& gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
ej->batch = batch;
next->nextInBatch = ej;
@@ -1791,6 +1853,7 @@ void Renderer::prepareAlphaBatches()
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
+ && gni->activeMaterial()->viewCount() == gnj->activeMaterial()->viewCount()
&& gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
if (!overlapBounds.intersects(ej->bounds) || !checkOverlap(i+1, j - 1, ej->bounds)) {
ej->batch = batch;
@@ -1994,7 +2057,7 @@ void Renderer::uploadBatch(Batch *b)
bool canMerge = (g->drawingMode() == QSGGeometry::DrawTriangles || g->drawingMode() == QSGGeometry::DrawTriangleStrip ||
g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawPoints)
&& b->positionAttribute >= 0
- && g->indexType() == QSGGeometry::UnsignedShortType
+ && (g->indexType() == QSGGeometry::UnsignedShortType && g->indexCount() > 0)
&& (flags & (QSGMaterial::NoBatching | QSGMaterial_FullMatrix)) == 0
&& ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
&& b->isSafeToBatch();
@@ -2243,8 +2306,10 @@ QRhiGraphicsPipeline *Renderer::buildStencilPipeline(const Batch *batch, bool fi
ps->setTopology(m_stencilClipCommon.topology);
- ps->setShaderStages({ QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Vertex, m_stencilClipCommon.vs),
- QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Fragment, m_stencilClipCommon.fs) });
+ ps->setMultiViewCount(renderTarget().multiViewCount);
+
+ ps->setShaderStages({ QRhiShaderStage(QRhiShaderStage::Vertex, m_stencilClipCommon.vs),
+ QRhiShaderStage(QRhiShaderStage::Fragment, m_stencilClipCommon.fs) });
ps->setVertexInputLayout(m_stencilClipCommon.inputLayout);
ps->setShaderResourceBindings(batch->stencilClipState.srb); // use something, it just needs to be layout-compatible
ps->setRenderPassDescriptor(renderTarget().rpDesc);
@@ -2291,7 +2356,7 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch)
const quint32 StencilClipUbufSize = 64;
while (clip) {
- QMatrix4x4 m = m_current_projection_matrix_native_ndc;
+ QMatrix4x4 m = m_current_projection_matrix_native_ndc[0]; // never hit for 3D and so multiview
if (clip->matrix())
m *= *clip->matrix();
@@ -2462,7 +2527,7 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch)
drawCall.ubufOffset = aligned(uOffset, m_ubufAlignment);
uOffset = drawCall.ubufOffset + StencilClipUbufSize;
- QMatrix4x4 matrixYUpNDC = m_current_projection_matrix;
+ QMatrix4x4 matrixYUpNDC = m_current_projection_matrix[0];
if (clip->matrix())
matrixYUpNDC *= *clip->matrix();
@@ -2636,6 +2701,7 @@ bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms,
ps->setTopology(qsg_topology(m_gstate.drawMode));
ps->setCullMode(m_gstate.cullMode);
ps->setPolygonMode(m_gstate.polygonMode);
+ ps->setMultiViewCount(m_gstate.multiViewCount);
QRhiGraphicsPipeline::TargetBlend blend;
blend.colorWrite = m_gstate.colorWrite;
@@ -3079,16 +3145,24 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
else
m_current_model_view_matrix.setToIdentity();
m_current_determinant = m_current_model_view_matrix.determinant();
- m_current_projection_matrix = projectionMatrix();
- m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+
+ const int viewCount = projectionMatrixCount();
+ m_current_projection_matrix.resize(viewCount);
+ for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
+ m_current_projection_matrix[viewIndex] = projectionMatrix(viewIndex);
+
+ m_current_projection_matrix_native_ndc.resize(projectionMatrixWithNativeNDCCount());
+ for (int viewIndex = 0; viewIndex < projectionMatrixWithNativeNDCCount(); ++viewIndex)
+ m_current_projection_matrix_native_ndc[viewIndex] = projectionMatrixWithNativeNDC(viewIndex);
QSGMaterial *material = gn->activeMaterial();
if (m_renderMode != QSGRendererInterface::RenderMode3D)
updateClipState(gn->clipList(), batch);
const QSGGeometry *g = gn->geometry();
- ShaderManager::Shader *sms = useDepthBuffer() ? m_shaderManager->prepareMaterial(material, g, m_renderMode)
- : m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode);
+ const int multiViewCount = renderTarget().multiViewCount;
+ ShaderManager::Shader *sms = useDepthBuffer() ? m_shaderManager->prepareMaterial(material, g, m_renderMode, multiViewCount)
+ : m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode, multiViewCount);
if (!sms)
return false;
@@ -3252,8 +3326,14 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
batch->uploadedThisFrame = false;
}
- m_current_projection_matrix = projectionMatrix();
- m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+ const int viewCount = projectionMatrixCount();
+ m_current_projection_matrix.resize(viewCount);
+ for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
+ m_current_projection_matrix[viewIndex] = projectionMatrix(viewIndex);
+
+ m_current_projection_matrix_native_ndc.resize(projectionMatrixWithNativeNDCCount());
+ for (int viewIndex = 0; viewIndex < projectionMatrixWithNativeNDCCount(); ++viewIndex)
+ m_current_projection_matrix_native_ndc[viewIndex] = projectionMatrixWithNativeNDC(viewIndex);
QSGGeometryNode *gn = e->node;
if (m_renderMode != QSGRendererInterface::RenderMode3D)
@@ -3266,7 +3346,7 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
// unmerged batch since the material (and so the shaders) is the same.
QSGGeometry *g = gn->geometry();
QSGMaterial *material = gn->activeMaterial();
- ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g);
+ ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode, renderTarget().multiViewCount);
if (!sms)
return false;
@@ -3326,11 +3406,19 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
m_current_model_view_matrix = rootMatrix * *gn->matrix();
m_current_determinant = m_current_model_view_matrix.determinant();
- m_current_projection_matrix = projectionMatrix();
- m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+ const int viewCount = projectionMatrixCount();
+ m_current_projection_matrix.resize(viewCount);
+ for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
+ m_current_projection_matrix[viewIndex] = projectionMatrix(viewIndex);
+
+ m_current_projection_matrix_native_ndc.resize(projectionMatrixWithNativeNDCCount());
+ for (int viewIndex = 0; viewIndex < projectionMatrixWithNativeNDCCount(); ++viewIndex)
+ m_current_projection_matrix_native_ndc[viewIndex] = projectionMatrixWithNativeNDC(viewIndex);
+
if (useDepthBuffer()) {
- m_current_projection_matrix(2, 2) = m_zRange;
- m_current_projection_matrix(2, 3) = calculateElementZOrder(e, m_zRange);
+ // this cannot be multiview
+ m_current_projection_matrix[0](2, 2) = m_zRange;
+ m_current_projection_matrix[0](2, 3) = calculateElementZOrder(e, m_zRange);
}
QSGMaterialShader::RenderState renderState = state(QSGMaterialShader::RenderState::DirtyStates(int(dirty)));
@@ -3664,14 +3752,14 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
if (Q_UNLIKELY(debug_render())) ctx->timeSorting = ctx->timer.restart();
- quint32 largestVBO = 0;
- quint32 largestIBO = 0;
+ // Set size to 0, nothing is deallocated, they will "grow" again
+ // as part of uploadBatch.
+ m_vertexUploadPool.reset();
+ m_indexUploadPool.reset();
if (Q_UNLIKELY(debug_upload())) qDebug("Uploading Opaque Batches:");
for (int i=0; i<m_opaqueBatches.size(); ++i) {
Batch *b = m_opaqueBatches.at(i);
- largestVBO = qMax(b->vbo.size, largestVBO);
- largestIBO = qMax(b->ibo.size, largestIBO);
uploadBatch(b);
}
if (Q_UNLIKELY(debug_render())) ctx->timeUploadOpaque = ctx->timer.restart();
@@ -3680,14 +3768,9 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
for (int i=0; i<m_alphaBatches.size(); ++i) {
Batch *b = m_alphaBatches.at(i);
uploadBatch(b);
- largestVBO = qMax(b->vbo.size, largestVBO);
- largestIBO = qMax(b->ibo.size, largestIBO);
}
if (Q_UNLIKELY(debug_render())) ctx->timeUploadAlpha = ctx->timer.restart();
- m_vertexUploadPool.resize(largestVBO);
- m_indexUploadPool.resize(largestIBO);
-
if (Q_UNLIKELY(debug_render())) {
qDebug().nospace() << "Rendering:" << Qt::endl
<< " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << Qt::endl
@@ -3728,6 +3811,7 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
m_gstate.stencilTest = false;
m_gstate.sampleCount = renderTarget().rt->sampleCount();
+ m_gstate.multiViewCount = renderTarget().multiViewCount;
ctx->opaqueRenderBatches.clear();
if (Q_LIKELY(renderOpaque)) {
@@ -3825,6 +3909,8 @@ void Renderer::recordRenderPass(RenderPassContext *ctx)
cb->debugMarkBegin(QByteArrayLiteral("Qt Quick scene render"));
for (int i = 0, ie = ctx->opaqueRenderBatches.size(); i != ie; ++i) {
+ if (i == 0)
+ cb->debugMarkMsg(QByteArrayLiteral("Qt Quick opaque batches"));
PreparedRenderBatch *renderBatch = &ctx->opaqueRenderBatches[i];
if (renderBatch->batch->merged)
renderMergedBatch(renderBatch);
@@ -3833,6 +3919,12 @@ void Renderer::recordRenderPass(RenderPassContext *ctx)
}
for (int i = 0, ie = ctx->alphaRenderBatches.size(); i != ie; ++i) {
+ if (i == 0) {
+ if (m_renderMode == QSGRendererInterface::RenderMode3D)
+ cb->debugMarkMsg(QByteArrayLiteral("Qt Quick 2D-in-3D batches"));
+ else
+ cb->debugMarkMsg(QByteArrayLiteral("Qt Quick alpha batches"));
+ }
PreparedRenderBatch *renderBatch = &ctx->alphaRenderBatches[i];
if (renderBatch->batch->merged)
renderMergedBatch(renderBatch);
@@ -3843,8 +3935,15 @@ void Renderer::recordRenderPass(RenderPassContext *ctx)
}
if (m_renderMode == QSGRendererInterface::RenderMode3D) {
- // depth post-pass
+ // Depth post-pass to fill up the depth buffer in a way that it
+ // corresponds to what got rendered to the color buffer in the previous
+ // (alpha) pass. The previous pass cannot enable depth write due to Z
+ // fighting. Rather, do it separately in a dedicated color-write-off,
+ // depth-write-on pass. This enables the 3D content drawn afterwards to
+ // depth test against the 2D items' rendering.
for (int i = 0, ie = ctx->alphaRenderBatches.size(); i != ie; ++i) {
+ if (i == 0)
+ cb->debugMarkMsg(QByteArrayLiteral("Qt Quick 2D-in-3D depth post-pass"));
PreparedRenderBatch *renderBatch = &ctx->alphaRenderBatches[i];
if (renderBatch->batch->merged)
renderMergedBatch(renderBatch, true);
@@ -3933,7 +4032,8 @@ bool Renderer::prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBat
}
xform = xform->parent();
}
- rd->m_matrix = &matrix;
+ rd->m_localMatrix = matrix;
+ rd->m_matrix = &rd->m_localMatrix;
QSGNode *opacity = e->renderNode->parent();
rd->m_opacity = 1.0;
@@ -3947,10 +4047,15 @@ bool Renderer::prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBat
rd->m_rt = renderTarget();
- rd->m_projectionMatrix = projectionMatrix();
+ const int viewCount = projectionMatrixCount();
+ rd->m_projectionMatrix.resize(viewCount);
+ for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
+ rd->m_projectionMatrix[viewIndex] = projectionMatrix(viewIndex);
+
if (useDepthBuffer()) {
- rd->m_projectionMatrix(2, 2) = m_zRange;
- rd->m_projectionMatrix(2, 3) = calculateElementZOrder(e, m_zRange);
+ // this cannot be multiview
+ rd->m_projectionMatrix[0](2, 2) = m_zRange;
+ rd->m_projectionMatrix[0](2, 3) = calculateElementZOrder(e, m_zRange);
}
e->renderNode->prepare();
@@ -3970,7 +4075,9 @@ void Renderer::renderRhiRenderNode(const Batch *batch)
QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(e->renderNode);
RenderNodeState state;
- state.m_projectionMatrix = &rd->m_projectionMatrix;
+ // Expose only the first matrix through the state object, the rest are
+ // queriable through the QSGRenderNode getters anyway.
+ state.m_projectionMatrix = &rd->m_projectionMatrix[0];
const std::array<int, 4> scissor = batch->clipState.scissor.scissor();
state.m_scissorRect = QRect(scissor[0], scissor[1], scissor[2], scissor[3]);
state.m_stencilValue = batch->clipState.stencilRef;
@@ -4041,7 +4148,8 @@ bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept
&& a.sampleCount == b.sampleCount
&& a.drawMode == b.drawMode
&& a.lineWidth == b.lineWidth
- && a.polygonMode == b.polygonMode;
+ && a.polygonMode == b.polygonMode
+ && a.multiViewCount == b.multiViewCount;
}
bool operator!=(const GraphicsState &a, const GraphicsState &b) noexcept
@@ -4061,7 +4169,8 @@ size_t qHash(const GraphicsState &s, size_t seed) noexcept
+ s.cullMode
+ s.usesScissor
+ s.stencilTest
- + s.sampleCount;
+ + s.sampleCount
+ + s.multiViewCount;
}
bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) noexcept
@@ -4085,6 +4194,23 @@ size_t qHash(const GraphicsPipelineStateKey &k, size_t seed) noexcept
^ k.extra.srbLayoutDescriptionHash;
}
+bool operator==(const ShaderKey &a, const ShaderKey &b) noexcept
+{
+ return a.type == b.type
+ && a.renderMode == b.renderMode
+ && a.multiViewCount == b.multiViewCount;
+}
+
+bool operator!=(const ShaderKey &a, const ShaderKey &b) noexcept
+{
+ return !(a == b);
+}
+
+size_t qHash(const ShaderKey &k, size_t seed) noexcept
+{
+ return qHash(k.type, seed) ^ int(k.renderMode) ^ k.multiViewCount;
+}
+
Visualizer::Visualizer(Renderer *renderer)
: m_renderer(renderer),
m_visualizeMode(VisualizeNothing)
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 9a024804a7..a84797a4c6 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -27,7 +27,7 @@
#include <QtCore/QBitArray>
#include <QtCore/QStack>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -607,6 +607,7 @@ struct GraphicsState
QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles;
float lineWidth = 1.0f;
QRhiGraphicsPipeline::PolygonMode polygonMode = QRhiGraphicsPipeline::Fill;
+ int multiViewCount = 0;
};
bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept;
@@ -640,6 +641,17 @@ bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKe
bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) noexcept;
size_t qHash(const GraphicsPipelineStateKey &k, size_t seed = 0) noexcept;
+struct ShaderKey
+{
+ QSGMaterialType *type;
+ QSGRendererInterface::RenderMode renderMode;
+ int multiViewCount;
+};
+
+bool operator==(const ShaderKey &a, const ShaderKey &b) noexcept;
+bool operator!=(const ShaderKey &a, const ShaderKey &b) noexcept;
+size_t qHash(const ShaderKey &k, size_t seed = 0) noexcept;
+
struct ShaderManagerShader
{
~ShaderManagerShader() {
@@ -647,7 +659,7 @@ struct ShaderManagerShader
}
QSGMaterialShader *materialShader = nullptr;
QRhiVertexInputLayout inputLayout;
- QVarLengthArray<QRhiGraphicsShaderStage, 2> stages;
+ QVarLengthArray<QRhiShaderStage, 2> stages;
float lastOpacity;
};
@@ -674,11 +686,16 @@ public Q_SLOTS:
void invalidated();
public:
- Shader *prepareMaterial(QSGMaterial *material, const QSGGeometry *geometry = nullptr, QSGRendererInterface::RenderMode renderMode = QSGRendererInterface::RenderMode2D);
- Shader *prepareMaterialNoRewrite(QSGMaterial *material, const QSGGeometry *geometry = nullptr, QSGRendererInterface::RenderMode renderMode = QSGRendererInterface::RenderMode2D);
+ Shader *prepareMaterial(QSGMaterial *material,
+ const QSGGeometry *geometry = nullptr,
+ QSGRendererInterface::RenderMode renderMode = QSGRendererInterface::RenderMode2D,
+ int multiViewCount = 0);
+ Shader *prepareMaterialNoRewrite(QSGMaterial *material,
+ const QSGGeometry *geometry = nullptr,
+ QSGRendererInterface::RenderMode renderMode = QSGRendererInterface::RenderMode2D,
+ int multiViewCount = 0);
private:
- typedef QPair<QSGMaterialType *, QSGRendererInterface::RenderMode> ShaderKey;
QHash<ShaderKey, Shader *> rewrittenShaders;
QHash<ShaderKey, Shader *> stockShaders;
@@ -723,7 +740,7 @@ protected:
QHash<Node *, uint> m_visualizeChangeSet;
};
-class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer
+class Q_QUICK_EXPORT Renderer : public QSGRenderer
{
public:
Renderer(QSGDefaultRenderContext *ctx, QSGRendererInterface::RenderMode renderMode = QSGRendererInterface::RenderMode2D);
@@ -858,6 +875,9 @@ private:
QDataBuffer<Element *> m_tmpAlphaElements;
QDataBuffer<Element *> m_tmpOpaqueElements;
+ QDataBuffer<QRhiBuffer *> m_vboPool;
+ QDataBuffer<QRhiBuffer *> m_iboPool;
+
uint m_rebuild;
qreal m_zRange;
#if defined(QSGBATCHRENDERER_INVALIDATE_WEDGED_NODES)
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index 32ba34587a..9541107b78 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -496,13 +496,18 @@ const void *QSGGeometry::indexData() const
Specifies the drawing mode, also called primitive topology.
+ \note Starting with Qt 6 the scene graph only exposes topologies that are
+ supported across all the supported 3D graphics APIs. As a result, the
+ values \c DrawLineLoop and \c DrawTriangleFan are no longer supported at
+ run time in Qt 6, even though the enum values themselves are still present.
+
\value DrawPoints
\value DrawLines
- \value DrawLineLoop
+ \omitvalue DrawLineLoop
\value DrawLineStrip
\value DrawTriangles
\value DrawTriangleStrip
- \value DrawTriangleFan
+ \omitvalue DrawTriangleFan
*/
/*!
@@ -536,10 +541,10 @@ void QSGGeometry::setDrawingMode(unsigned int mode)
}
/*!
- Gets the current line or point width or to be used for this
- geometry. This property only applies to line width when the drawingMode
- is DrawLines, DarwLineStrip, or DrawLineLoop. When supported, it also
- applies to point size when the drawingMode is DrawPoints.
+ Gets the current line or point width or to be used for this geometry. This
+ property only applies to line width when the drawingMode is DrawLines or
+ DrawLineStrip. When supported, it also applies to point size when the
+ drawingMode is DrawPoints.
The default value is \c 1.0
@@ -558,10 +563,10 @@ float QSGGeometry::lineWidth() const
}
/*!
- Sets the line or point width to be used for this geometry to \a
- width. This property only applies to line width when the drawingMode is
- DrawLines, DrawLineStrip, or DrawLineLoop. When supported, it also
- applies to point size when the drawingMode is DrawPoints.
+ Sets the line or point width to be used for this geometry to \a width. This
+ property only applies to line width when the drawingMode is DrawLines or
+ DrawLineStrip. When supported, it also applies to point size when the
+ drawingMode is DrawPoints.
\note Support for point and line drawing may be limited at run time,
depending on the platform and graphics API. For example, some APIs do
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 0b4a7cdce1..7fff62a915 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -30,8 +30,6 @@ public:
StaticPattern = 3
};
- // Equivalents to GL_* drawing modes.
- // Keep in sync with GL headers.
enum DrawingMode {
DrawPoints = 0x0000,
DrawLines = 0x0001,
@@ -42,8 +40,6 @@ public:
DrawTriangleFan = 0x0006
};
- // Equivalents to GL_BYTE and similar type constants.
- // Keep in sync with GL headers.
enum Type {
ByteType = 0x1400,
UnsignedByteType = 0x1401,
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index 9c7cbe4d8d..49508c1c35 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -6,6 +6,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak)
+
#ifndef QT_NO_DEBUG
bool qsg_material_failure = false;
bool qsg_test_and_clear_material_failure()
@@ -21,10 +23,6 @@ void qsg_set_material_failure()
}
#endif
-#ifndef QT_NO_DEBUG
-static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
-#endif
-
/*!
\group qtquick-scenegraph-materials
\title Qt Quick Scene Graph Material Classes
@@ -39,7 +37,7 @@ static int qt_material_count = 0;
static void qt_print_material_count()
{
- qDebug("Number of leaked materials: %i", qt_material_count);
+ qCDebug(lcQsgLeak, "Number of leaked materials: %i", qt_material_count);
qt_material_count = -1;
}
#endif
@@ -109,7 +107,7 @@ QSGMaterial::QSGMaterial()
{
Q_UNUSED(m_reserved);
#ifndef QT_NO_DEBUG
- if (qsg_leak_check) {
+ if (lcQsgLeak().isDebugEnabled()) {
++qt_material_count;
static bool atexit_registered = false;
if (!atexit_registered) {
@@ -128,10 +126,10 @@ QSGMaterial::QSGMaterial()
QSGMaterial::~QSGMaterial()
{
#ifndef QT_NO_DEBUG
- if (qsg_leak_check) {
+ if (lcQsgLeak().isDebugEnabled()) {
--qt_material_count;
if (qt_material_count < 0)
- qDebug("Material destroyed after qt_print_material_count() was called.");
+ qCDebug(lcQsgLeak, "Material destroyed after qt_print_material_count() was called.");
}
#endif
}
@@ -165,6 +163,10 @@ QSGMaterial::~QSGMaterial()
\value CustomCompileStep In Qt 6 this flag is identical to NoBatching. Prefer using
NoBatching instead.
+
+ \omitvalue MultiView2
+ \omitvalue MultiView3
+ \omitvalue MultiView4
*/
/*!
@@ -249,4 +251,185 @@ int QSGMaterial::compare(const QSGMaterial *other) const
RenderMode3D is in use.
*/
+/*!
+ \return The number of views in case of the material is used in multiview
+ rendering.
+
+ \note The return value is valid only when called from createShader(), and
+ afterwards. The value is not necessarily up-to-date before createShader()
+ is invokved by the scene graph.
+
+ Normally the return value is \c 1. A view count greater than 2 implies a
+ \e{multiview render pass}. Materials that support multiview are expected to
+ query viewCount() in createShader(), or in their QSGMaterialShader
+ constructor, and ensure the appropriate shaders are picked. The vertex
+ shader is then expected to use
+ \c{gl_ViewIndex} to index the modelview-projection matrix array as there
+ are multiple matrices in multiview mode. (one for each view)
+
+ As an example, take the following simple vertex shader:
+
+ \badcode
+ #version 440
+
+ layout(location = 0) in vec4 vertexCoord;
+ layout(location = 1) in vec4 vertexColor;
+
+ layout(location = 0) out vec4 color;
+
+ layout(std140, binding = 0) uniform buf {
+ mat4 matrix[2];
+ float opacity;
+ };
+
+ void main()
+ {
+ gl_Position = matrix[gl_ViewIndex] * vertexCoord;
+ color = vertexColor * opacity;
+ }
+ \endcode
+
+ This shader is prepared to handle 2 views, and 2 views only. It is not
+ compatible with other view counts. When conditioning the shader, the \c qsb
+ tool has to be invoked with \c{--view-count 2} or, if using the CMake
+ integration,
+ \c{VIEW_COUNT 2} must be specified in the \c{qt_add_shaders()} command.
+
+ \note A line with \c{#extension GL_EXT_multiview : require} is injected
+ automatically by \c qsb whenever a view count of 2 or greater is set.
+
+ Developers are encouraged to use the automatically injected preprocessor
+ variable \c{QSHADER_VIEW_COUNT} to simplify the handling of the different
+ number of views. For example, if there is a need to support both
+ non-multiview and multiview with a view count of 2 in the same source file,
+ the following could be done:
+
+ \badcode
+ #version 440
+
+ layout(location = 0) in vec4 vertexCoord;
+ layout(location = 1) in vec4 vertexColor;
+
+ layout(location = 0) out vec4 color;
+
+ layout(std140, binding = 0) uniform buf {
+ #if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+ #else
+ mat4 matrix;
+ #endif
+ float opacity;
+ };
+
+ void main()
+ {
+ #if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vertexCoord;
+ #else
+ gl_Position = matrix * vertexCoord;
+ #endif
+ color = vertexColor * opacity;
+ }
+ \endcode
+
+ The same source file can now be run through \c qsb or \c{qt_add_shaders()}
+ twice, once without specify the view count, and once with the view count
+ set to 2. The material can then pick the appropriate .qsb file based on
+ viewCount() at run time.
+
+ With CMake, this could looks similar to the following. With this example
+ the corresponding QSGMaterialShader is expected to choose between
+ \c{:/shaders/example.vert.qsb} and \c{:/shaders/multiview/example.vert.qsb}
+ based on the value of viewCount(). (same goes for the fragment shader)
+
+ \badcode
+ qt_add_shaders(application "application_shaders"
+ PREFIX
+ /
+ FILES
+ shaders/example.vert
+ shaders/example.frag
+ )
+
+ qt_add_shaders(application "application_multiview_shaders"
+ GLSL
+ 330,300es
+ HLSL
+ 61
+ MSL
+ 12
+ VIEW_COUNT
+ 2
+ PREFIX
+ /
+ FILES
+ shaders/example.vert
+ shaders/example.frag
+ OUTPUTS
+ shaders/multiview/example.vert
+ shaders/multiview/example.frag
+ )
+ \endcode
+
+ \note The fragment shader should be treated the same way the vertex shader
+ is, even though the fragment shader code cannot have any dependency on the
+ view count (\c{gl_ViewIndex}), for maximum portability. There are two
+ reasons for including fragment shaders too in the multiview set. One is that
+ mixing different shader versions within the same graphics pipeline can be
+ problematic, depending on the underlying graphics API: with D3D12 for
+ example, mixing HLSL shaders for shader model 5.0 and 6.1 would generate an
+ error. The other is that having \c QSHADER_VIEW_COUNT defined in fragment
+ shaders can be very useful, for example when sharing a uniform buffer layout
+ between the vertex and fragment stages.
+
+ \note For OpenGL the minimum GLSL version for vertex shaders relying on
+ \c{gl_ViewIndex} is \c 330. Lower versions may be accepted at build time,
+ but may lead to an error at run time, depending on the OpenGL implementation.
+
+ As a convenience, there is also a \c MULTIVIEW option for qt_add_shaders().
+ This first runs the \c qsb tool normally, then overrides \c VIEW_COUNT to
+ \c 2, sets \c GLSL, \c HLSL, \c MSL to some suitable defaults, and runs \c
+ qsb again, this time outputting .qsb files with a suffix added. The material
+ implementation can then use the \l QSGMaterialShader::setShaderFileName()
+ overload taking a \c viewCount argument, that automatically picks the
+ correct .qsb file.
+
+ The following is therefore mostly equivalent to the example call shown
+ above, except that no manually managed output files need to be specified.
+ Note that there can be cases when the automatically chosen shading language
+ versions are not sufficient, in which case applications should continue
+ specify everything explicitly.
+
+ \badcode
+ qt_add_shaders(application "application_multiview_shaders"
+ MULTIVIEW
+ PREFIX
+ /
+ FILES
+ shaders/example.vert
+ shaders/example.frag
+ )
+ \endcode
+
+ See \l QRhi::MultiView, \l QRhiColorAttachment::setMultiViewCount(), and
+ \l QRhiGraphicsPipeline::setMultiViewCount() for further, lower-level details
+ on multiview support in Qt. The Qt Quick scene graph renderer is prepared to
+ recognize multiview render targets, when specified via
+ \l QQuickRenderTarget::fromRhiRenderTarget() or the \c MultiView
+ functions such as \l{QQuickRenderTarget::}{fromVulkanImageMultiView()}, and
+ propagate the view count to graphics pipelines and the materials.
+
+ \since 6.8
+ */
+int QSGMaterial::viewCount() const
+{
+ if (m_flags.testFlag(MultiView4))
+ return 4;
+ if (m_flags.testFlag(MultiView3))
+ return 3;
+ if (m_flags.testFlag(MultiView2))
+ return 2;
+ return 1;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h
index 29de92d53e..ca3f134a9d 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.h
@@ -21,6 +21,10 @@ public:
RequiresFullMatrix = 0x0008 | RequiresFullMatrixExceptTranslate,
NoBatching = 0x0010,
+ MultiView2 = 0x10000,
+ MultiView3 = 0x20000,
+ MultiView4 = 0x40000,
+
#if QT_DEPRECATED_SINCE(6, 3)
CustomCompileStep Q_DECL_ENUMERATOR_DEPRECATED_X(
"Qt 6 does not have custom shader compilation support. If the intention is to just disable batching, use NoBatching instead."
@@ -40,6 +44,8 @@ public:
QSGMaterial::Flags flags() const { return m_flags; }
void setFlag(Flags flags, bool on = true);
+ int viewCount() const;
+
private:
Flags m_flags;
void *m_reserved;
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
index b4090eb9cd..a661e47765 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
@@ -208,8 +208,8 @@ void QSGMaterialShaderPrivate::prepare(QShader::Variant vertexShaderVariant)
clearCachedRendererData();
for (QShader::Stage stage : { QShader::VertexStage, QShader::FragmentStage }) {
- auto it = shaderFileNames.find(stage);
- if (it != shaderFileNames.end()) {
+ auto it = shaderFileNames.constFind(stage);
+ if (it != shaderFileNames.cend()) {
QString fn = *it;
const QShader s = loadShader(*it);
if (!s.isValid())
@@ -361,6 +361,33 @@ void QSGMaterialShader::setShaderFileName(Stage stage, const QString &filename)
}
/*!
+ Sets the \a filename for the shader for the specified \a stage.
+
+ The file is expected to contain a serialized QShader.
+
+ This overload is used when enabling \l{QSGMaterial::viewCount()}{multiview}
+ rendering, in particular when the \l{Qt Shader Tools Build System
+ Integration}{build system's MULTIVIEW convenience option} is used.
+
+ \a viewCount should be 2, 3, or 4. The \a filename is adjusted automatically
+ based on this.
+
+ \since 6.8
+ */
+void QSGMaterialShader::setShaderFileName(Stage stage, const QString &filename, int viewCount)
+{
+ Q_D(QSGMaterialShader);
+ if (viewCount == 2)
+ d->shaderFileNames[toShaderStage(stage)] = filename + QStringLiteral(".mv2qsb");
+ else if (viewCount == 3)
+ d->shaderFileNames[toShaderStage(stage)] = filename + QStringLiteral(".mv3qsb");
+ else if (viewCount == 4)
+ d->shaderFileNames[toShaderStage(stage)] = filename + QStringLiteral(".mv4qsb");
+ else
+ d->shaderFileNames[toShaderStage(stage)] = filename;
+}
+
+/*!
\return the currently set flags for this material shader.
*/
QSGMaterialShader::Flags QSGMaterialShader::flags() const
@@ -768,7 +795,16 @@ float QSGMaterialShader::RenderState::determinant() const
QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const
{
Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix(0);
+}
+
+/*!
+ \internal
+ */
+QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix(int index) const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix(index);
}
/*!
@@ -807,7 +843,25 @@ QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
{
Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix(0);
+}
+
+/*!
+ \internal
+ */
+QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix(int index) const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix(index);
+}
+
+/*!
+ \internal
+ */
+int QSGMaterialShader::RenderState::projectionMatrixCount() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->projectionMatrixCount();
}
/*!
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.h b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
index caf44942cf..795da77477 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
@@ -40,8 +40,11 @@ public:
float opacity() const;
QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 combinedMatrix(int index) const;
QMatrix4x4 modelViewMatrix() const;
QMatrix4x4 projectionMatrix() const;
+ QMatrix4x4 projectionMatrix(int index) const;
+ int projectionMatrixCount() const;
QRect viewportRect() const;
QRect deviceRect() const;
float determinant() const;
@@ -57,7 +60,7 @@ public:
const void *m_data;
};
- struct Q_QUICK_EXPORT GraphicsPipelineState {
+ struct GraphicsPipelineState {
enum BlendFactor {
Zero,
One,
@@ -147,6 +150,7 @@ protected:
// filename is for a file containing a serialized QShader.
void setShaderFileName(Stage stage, const QString &filename);
+ void setShaderFileName(Stage stage, const QString &filename, int viewCount);
void setShader(Stage stage, const QShader &shader);
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
index de0161e431..8b4a4c0b2f 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
@@ -18,14 +18,14 @@
#include <private/qtquickglobal_p.h>
#include "qsgmaterialshader.h"
#include "qsgmaterial.h"
-#include <QtGui/private/qrhi_p.h>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qrhi.h>
+#include <rhi/qshader.h>
QT_BEGIN_NAMESPACE
class QRhiSampler;
-class Q_QUICK_PRIVATE_EXPORT QSGMaterialShaderPrivate
+class Q_QUICK_EXPORT QSGMaterialShaderPrivate
{
public:
Q_DECLARE_PUBLIC(QSGMaterialShader)
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp
index 62979cb9fc..b77f33d6a3 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -11,13 +11,14 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak)
+
#ifndef QT_NO_DEBUG
-static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
static int qt_node_count = 0;
static void qt_print_node_count()
{
- qDebug("Number of leaked nodes: %i", qt_node_count);
+ qCDebug(lcQsgLeak, "Number of leaked nodes: %i", qt_node_count);
qt_node_count = -1;
}
#endif
@@ -256,7 +257,7 @@ QSGNode::QSGNode(QSGNodePrivate &dd, NodeType type)
void QSGNode::init()
{
#ifndef QT_NO_DEBUG
- if (qsg_leak_check) {
+ if (lcQsgLeak().isDebugEnabled()) {
++qt_node_count;
static bool atexit_registered = false;
if (!atexit_registered) {
@@ -281,10 +282,10 @@ void QSGNode::init()
QSGNode::~QSGNode()
{
#ifndef QT_NO_DEBUG
- if (qsg_leak_check) {
+ if (lcQsgLeak().isDebugEnabled()) {
--qt_node_count;
if (qt_node_count < 0)
- qDebug("Node destroyed after qt_print_node_count() was called.");
+ qCDebug(lcQsgLeak, "Node destroyed after qt_print_node_count() was called.");
}
#endif
destroy();
diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index 659d085922..50dc6021ea 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.h
+++ b/src/quick/scenegraph/coreapi/qsgnode.h
@@ -175,8 +175,8 @@ private:
QSGGeometry *m_geometry;
- int m_reserved_start_index;
- int m_reserved_end_index;
+ Q_DECL_UNUSED_MEMBER int m_reserved_start_index;
+ Q_DECL_UNUSED_MEMBER int m_reserved_end_index;
const QMatrix4x4 *m_matrix;
const QSGClipNode *m_clip_list;
diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
index 6babf76395..a2bf37b538 100644
--- a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
+++ b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
@@ -28,7 +28,7 @@ class QSGGeometryNode;
class QMatrix4x4;
class QSGRenderNode;
-class Q_QUICK_PRIVATE_EXPORT QSGNodeUpdater
+class Q_QUICK_EXPORT QSGNodeUpdater
{
public:
QSGNodeUpdater();
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index 5d67b5dd74..ffa3047850 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -14,6 +14,13 @@ static QElapsedTimer frameTimer;
static qint64 preprocessTime;
static qint64 updatePassTime;
+Q_TRACE_POINT(qtquick, QSG_preprocess_entry)
+Q_TRACE_POINT(qtquick, QSG_preprocess_exit)
+Q_TRACE_POINT(qtquick, QSG_update_entry)
+Q_TRACE_POINT(qtquick, QSG_update_exit)
+Q_TRACE_POINT(qtquick, QSG_renderScene_entry)
+Q_TRACE_POINT(qtquick, QSG_renderScene_exit)
+
int qt_sg_envInt(const char *name, int defaultValue)
{
if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
@@ -64,6 +71,8 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context)
, m_is_rendering(false)
, m_is_preprocessing(false)
{
+ m_current_projection_matrix.resize(1);
+ m_current_projection_matrix_native_ndc.resize(1);
}
@@ -103,7 +112,7 @@ void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater)
bool QSGRenderer::isMirrored() const
{
- QMatrix4x4 matrix = projectionMatrix();
+ QMatrix4x4 matrix = projectionMatrix(0);
// Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0;
}
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index 1485e903f4..28654521c9 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -29,10 +29,10 @@ class QRhiCommandBuffer;
class QRhiRenderPassDescriptor;
class QRhiResourceUpdateBatch;
-Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_fatal_render_error();
-Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error();
+Q_QUICK_EXPORT bool qsg_test_and_clear_fatal_render_error();
+Q_QUICK_EXPORT void qsg_set_fatal_renderer_error();
-class Q_QUICK_PRIVATE_EXPORT QSGRenderTarget
+class Q_QUICK_EXPORT QSGRenderTarget
{
public:
QSGRenderTarget() { }
@@ -53,18 +53,20 @@ public:
QRhiCommandBuffer *cb = nullptr;
QPaintDevice *paintDevice = nullptr;
+
+ int multiViewCount = 0;
};
-class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QSGAbstractRenderer
+class Q_QUICK_EXPORT QSGRenderer : public QSGAbstractRenderer
{
public:
QSGRenderer(QSGRenderContext *context);
virtual ~QSGRenderer();
// Accessed by QSGMaterial[Rhi]Shader::RenderState.
- QMatrix4x4 currentProjectionMatrix() const { return m_current_projection_matrix; }
+ QMatrix4x4 currentProjectionMatrix(int index) const { return m_current_projection_matrix[index]; }
QMatrix4x4 currentModelViewMatrix() const { return m_current_model_view_matrix; }
- QMatrix4x4 currentCombinedMatrix() const { return m_current_projection_matrix * m_current_model_view_matrix; }
+ QMatrix4x4 currentCombinedMatrix(int index) const { return m_current_projection_matrix[index] * m_current_model_view_matrix; }
qreal currentOpacity() const { return m_current_opacity; }
qreal determinant() const { return m_current_determinant; }
@@ -115,8 +117,8 @@ protected:
void addNodesToPreprocess(QSGNode *node);
void removeNodesToPreprocess(QSGNode *node);
- QMatrix4x4 m_current_projection_matrix; // includes adjustment, where applicable, so can be treated as Y up in NDC always
- QMatrix4x4 m_current_projection_matrix_native_ndc; // Vulkan has Y down in normalized device coordinates, others Y up...
+ QVarLengthArray<QMatrix4x4, 1> m_current_projection_matrix; // includes adjustment, where applicable, so can be treated as Y up in NDC always
+ QVarLengthArray<QMatrix4x4, 1> m_current_projection_matrix_native_ndc; // Vulkan has Y down in normalized device coordinates, others Y up...
QMatrix4x4 m_current_model_view_matrix;
qreal m_current_opacity;
qreal m_current_determinant;
@@ -154,7 +156,7 @@ QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState
}
-class Q_QUICK_PRIVATE_EXPORT QSGNodeDumper : public QSGNodeVisitor {
+class Q_QUICK_EXPORT QSGNodeDumper : public QSGNodeVisitor {
public:
static void dump(QSGNode *n);
@@ -167,8 +169,6 @@ private:
int m_indent = 0;
};
-
-
QT_END_NAMESPACE
#endif
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
index 5245b6d950..b251d13edd 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
@@ -40,11 +40,12 @@ QT_BEGIN_NAMESPACE
\value Unknown An unknown graphics API is in use
\value Software The Qt Quick 2D Renderer is in use
\value OpenVG OpenVG via EGL
- \value OpenGL OpenGL ES 2.0 or higher via a graphics abstraction layer. This value was introduced in Qt 5.14.
- \value Direct3D11 Direct3D 11 via a graphics abstraction layer. This value was introduced in Qt 5.14.
- \value Vulkan Vulkan 1.0 via a graphics abstraction layer. This value was introduced in Qt 5.14.
- \value Metal Metal via a graphics abstraction layer. This value was introduced in Qt 5.14.
- \value Null Null (no output) via a graphics abstraction layer. This value was introduced in Qt 5.14.
+ \value [since 5.14] OpenGL OpenGL ES 2.0 or higher via a graphics abstraction layer.
+ \value [since 5.14] Direct3D11 Direct3D 11 via a graphics abstraction layer.
+ \value [since 6.6] Direct3D12 Direct3D 12 via a graphics abstraction layer.
+ \value [since 5.14] Vulkan Vulkan 1.0 via a graphics abstraction layer.
+ \value [since 5.14] Metal Metal via a graphics abstraction layer.
+ \value [since 5.14] Null Null (no output) via a graphics abstraction layer.
\omitvalue OpenGLRhi
\omitvalue Direct3D11Rhi
\omitvalue VulkanRhi
@@ -77,65 +78,69 @@ QT_BEGIN_NAMESPACE
\value PainterResource The resource is a pointer to the active QPainter
used by the scenegraph, when running with the software backend.
- \value RhiResource The resource is a pointer to the QRhi instance used by
- the scenegraph, when applicable. This value was introduced in Qt 5.14.
+ \value [since 5.14] RhiResource The resource is a pointer to the QRhi instance used by
+ the scenegraph, when applicable.
- \value RhiSwapchainResource The resource is a pointer to a QRhiSwapchain
+ \value [since 6.0] RhiSwapchainResource The resource is a pointer to a QRhiSwapchain
instance that is associated with the window. The value is null when the
- window is used in combination with QQuickRenderControl. This value was
- introduced in Qt 6.0.
+ window is used in combination with QQuickRenderControl.
- \value RhiRedirectCommandBuffer The resource is a pointer to a
+ \value [since 6.0] RhiRedirectCommandBuffer The resource is a pointer to a
QRhiCommandBuffer instance that is associated with the window and its
QQuickRenderControl. The value is null when the window is not associated
- with a QQuickRenderControl. This value was introduced in Qt 6.0.
+ with a QQuickRenderControl.
- \value RhiRedirectRenderTarget The resource is a pointer to a
+ \value [since 6.0] RhiRedirectRenderTarget The resource is a pointer to a
QRhiTextureRenderTarget instance that is associated with the window and its
QQuickRenderControl. The value is null when the window is not associated
with a QQuickRenderControl. Note that the value always reflects the main
texture render target and it does not depend on the Qt Quick scene, meaning
it does not take any additional texture-targeting render passes generated
- by ShaderEffect or QQuickItem layers into account. This value was
- introduced in Qt 6.0.
+ by ShaderEffect or QQuickItem layers into account.
- \value PhysicalDeviceResource The resource is a pointer to the pysical
+ \value [since 5.14] PhysicalDeviceResource The resource is a pointer to the pysical
device object used by the scenegraph, when applicable. For example, a
\c{VkPhysicalDevice *}. Note that with Vulkan the returned value is a
- pointer to the VkPhysicalDevice, not the handle itself. This value was
- introduced in Qt 5.14.
+ pointer to the VkPhysicalDevice, not the handle itself.
- \value OpenGLContextResource The resource is a pointer to the
+ \value [since 5.14] OpenGLContextResource The resource is a pointer to the
QOpenGLContext used by the scenegraph (on the render thread), when
- applicable. This value was introduced in Qt 5.14.
+ applicable.
- \value DeviceContextResource The resource is a pointer to the device
+ \value [since 5.14] DeviceContextResource The resource is a pointer to the device
context used by the scenegraph, when applicable. For example, a
- \c{ID3D11DeviceContext *}. This value was introduced in Qt 5.14.
+ \c{ID3D11DeviceContext *}.
- \value CommandEncoderResource The resource is a pointer to the currently
+ \value [since 5.14] CommandEncoderResource The resource is a pointer to the currently
active render command encoder object used by the scenegraph, when
applicable. For example, a \c{MTLRenderCommandEncoder *}. This object has
limited validity, and is only valid while the scene graph is recording a
- render pass for the next frame. This value was introduced in Qt 5.14.
+ render pass for the next frame.
- \value VulkanInstanceResource The resource is a pointer to the
- QVulkanInstance used by the scenegraph, when applicable. This value was
- introduced in Qt 5.14.
+ \value [since 5.14] VulkanInstanceResource The resource is a pointer to the
+ QVulkanInstance used by the scenegraph, when applicable.
- \value RenderPassResource The resource is a pointer to the main render pass
+ \value [since 5.14] RenderPassResource The resource is a pointer to the main render pass
used by the scenegraph, describing the color and depth/stecil attachments
and how they are used. For example, a \c{VkRenderPass *}. Note that the
value always reflects the main render target (either the on-screen window
or the texture QQuickRenderControl redirects to) and it does not depend on
the Qt Quick scene, meaning it does not take any additional
texture-targeting render passes generated by ShaderEffect or QQuickItem
- layers into account. This value was introduced in Qt 5.14.
+ layers into account.
- \value RedirectPaintDevice The resource is a pointer to QPaintDevice instance
+ \value [since 6.4] RedirectPaintDevice The resource is a pointer to QPaintDevice instance
that is associated with the window and its QQuickRenderControl. The value is
- null when the window is not associated with a QQuickRenderControl. This value
- was introduced in Qt 6.4.
+ null when the window is not associated with a QQuickRenderControl.
+
+ \value [since 6.6] GraphicsQueueFamilyIndexResource The resource is a pointer to the
+ graphics queue family index used by the scenegraph, when applicable. With
+ Vulkan, this is a pointer to a \c uint32_t index value.
+
+ \value [since 6.6] GraphicsQueueIndexResource The resource is a pointer to the graphics
+ queue index (uint32_t) used by the scenegraph, when applicable. With
+ Vulkan, this is a pointer to a \c uint32_t index value, which in practice
+ is the index of the VkQueue reported for \c CommandQueueResource.
*/
/*!
@@ -143,9 +148,8 @@ QT_BEGIN_NAMESPACE
\value UnknownShadingLanguage Not yet known due to no window and scenegraph associated
\value GLSL GLSL or GLSL ES
\value HLSL HLSL
- \value RhiShader Consumes QShader instances containing shader variants for
- multiple target languages and intermediate formats. This value was introduced in
- Qt 5.14.
+ \value [since 5.14] RhiShader Consumes QShader instances containing shader variants for
+ multiple target languages and intermediate formats.
*/
/*!
@@ -234,15 +238,12 @@ void *QSGRendererInterface::getResource(QQuickWindow *window, const char *resour
bool QSGRendererInterface::isApiRhiBased(GraphicsApi api)
{
switch (api) {
- case OpenGLRhi:
- Q_FALLTHROUGH();
- case Direct3D11Rhi:
- Q_FALLTHROUGH();
- case VulkanRhi:
- Q_FALLTHROUGH();
- case MetalRhi:
- Q_FALLTHROUGH();
- case NullRhi:
+ case OpenGL:
+ case Direct3D11:
+ case Direct3D12:
+ case Vulkan:
+ case Metal:
+ case Null:
return true;
default:
return false;
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
index 7f08c8db34..d76bbed5cf 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.h
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
@@ -22,6 +22,7 @@ public:
Vulkan,
Metal,
Null,
+ Direct3D12,
OpenGLRhi = OpenGL,
Direct3D11Rhi = Direct3D11,
@@ -45,7 +46,9 @@ public:
CommandEncoderResource,
VulkanInstanceResource,
RenderPassResource,
- RedirectPaintDevice
+ RedirectPaintDevice,
+ GraphicsQueueFamilyIndexResource,
+ GraphicsQueueIndexResource,
};
enum ShaderType {
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index 7a4627e1b4..333d62d40b 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
@@ -12,6 +12,23 @@ QT_BEGIN_NAMESPACE
targeting the graphics API that is in use by the scenegraph.
\inmodule QtQuick
\since 5.8
+
+ QSGRenderNode allows creating scene graph nodes that perform their own
+ custom rendering via QRhi (the common approach from Qt 6.6 on), directly
+ via a 3D graphics API such as OpenGL, Vulkan, or Metal, or, when the \c
+ software backend is in use, via QPainter.
+
+ QSGRenderNode is the enabler for one of the three ways to integrate custom
+ 2D/3D rendering into a Qt Quick scene. The other two options are to perform
+ the rendering \c before or \c after the Qt Quick scene's own rendering,
+ or to generate a whole separate render pass targeting a dedicated render
+ target (a texture) and then have an item in the scene display the texture.
+ The QSGRenderNode-based approach is similar to the former, in the sense
+ that no additional render passes or render targets are involved, and allows
+ injecting custom rendering commands "inline" with the Qt Quick scene's
+ own rendering.
+
+ \sa {Scene Graph - Custom QSGRenderNode}
*/
QSGRenderNode::QSGRenderNode()
@@ -30,6 +47,13 @@ QSGRenderNode::QSGRenderNode()
deleted. Therefore there is no need to issue additional waits here, unless
the render() implementation is using additional command queues.
+ With QRhi and resources such as QRhiBuffer, QRhiTexture,
+ QRhiGraphicsPipeline, etc., it is often good practice to use smart
+ pointers, such as std::unique_ptr, which can often avoid the need to
+ implement a destructor, and lead to more compact source code. Keep in mind
+ however that implementing releaseResources(), most likely issuing a number
+ of reset() calls on the unique_ptrs, is still important.
+
\sa releaseResources()
*/
QSGRenderNode::~QSGRenderNode()
@@ -42,6 +66,7 @@ QSGRenderNodePrivate::QSGRenderNodePrivate()
, m_clip_list(nullptr)
, m_opacity(1)
{
+ m_projectionMatrix.resize(1);
}
/*!
@@ -49,17 +74,15 @@ QSGRenderNodePrivate::QSGRenderNodePrivate()
mask where each bit represents graphics states changed by the \l render()
function:
- \list
- \li DepthState - depth write mask, depth test enabled, depth comparison function
- \li StencilState - stencil write masks, stencil test enabled, stencil operations,
- stencil comparison functions
- \li ScissorState - scissor enabled, scissor test enabled
- \li ColorState - clear color, color write mask
- \li BlendState - blend enabled, blend function
- \li CullState - front face, cull face enabled
- \li ViewportState - viewport
- \li RenderTargetState - render target
- \endlist
+ \value DepthState depth write mask, depth test enabled, depth comparison function
+ \value StencilState stencil write masks, stencil test enabled, stencil operations,
+ stencil comparison functions
+ \value ScissorState scissor enabled, scissor test enabled
+ \value ColorState clear color, color write mask
+ \value BlendState blend enabled, blend function
+ \value CullState front face, cull face enabled
+ \value ViewportState viewport
+ \value RenderTargetState render target
With APIs other than OpenGL, the only relevant values are the ones that
correspond to dynamic state changes recorded on the command list/buffer.
@@ -73,16 +96,16 @@ QSGRenderNodePrivate::QSGRenderNodePrivate()
bindings, root signature, descriptor heaps, etc.) are always set again by
the scenegraph so render() can freely change them.
- \note RenderTargetState is no longer supported with APIs like Vulkan. This
+ RenderTargetState is no longer supported with APIs like Vulkan. This
is by nature. render() is invoked while the Qt Quick scenegraph's main
command buffer is recording a renderpass, so there is no possibility of
changing the target and starting another renderpass (on that command buffer
at least). Therefore returning a value with RenderTargetState set is not
sensible.
- The software backend exposes its QPainter and saves and restores before and
- after invoking render(). Therefore reporting any changed states from here
- is not necessary.
+ \note The \c software backend exposes its QPainter and saves and restores
+ before and after invoking render(). Therefore reporting any changed states
+ from here is not necessary.
The function is called by the renderer so it can reset the states after
rendering this node. This makes the implementation of render() simpler
@@ -92,6 +115,10 @@ QSGRenderNodePrivate::QSGRenderNodePrivate()
in render().
\note This function may be called before render().
+
+ \note With Qt 6 and QRhi-based rendering the only relevant values are
+ ViewportState and ScissorState. Other values can be returned but are
+ ignored in practice.
*/
QSGRenderNode::StateFlags QSGRenderNode::changedStates() const
{
@@ -110,6 +137,12 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const
The default implementation is empty.
+ When implementing a QSGRenderNode that uses QRhi to render, query the QRhi
+ object from the QQuickWindow via \l{QQuickWindow::rhi()}. To get a
+ QRhiCommandBuffer for submitting work to, call commandBuffer(). To query
+ information about the active render target, call renderTarget(). See the
+ \l{{Scene Graph - Custom QSGRenderNode}} example for details.
+
\since 6.0
*/
void QSGRenderNode::prepare()
@@ -160,57 +193,33 @@ void QSGRenderNode::prepare()
Some scenegraph backends, software in particular, use no scissor or
stencil. There the clip region is provided as an ordinary QRegion.
- With the legacy, direct OpenGL based renderer, the following states are set
- on the render thread's context before this function is called:
-
- \list
- \li glColorMask(true, true, true, true)
- \li glDepthMask(false)
- \li glDisable(GL_DEPTH_TEST)
- \li glStencilFunc(GL_EQUAL, state.stencilValue, 0xff); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) depending on clip
- \li glScissor(state.scissorRect.x(), state.scissorRect.y(),
- state.scissorRect.width(), state.scissorRect.height()) depending on clip
- \li glEnable(GL_BLEND)
- \li glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
- \li glDisable(GL_CULL_FACE)
- \endlist
-
- States that are not listed above, but are covered by \l StateFlags, can
- have arbitrary values.
-
- \note There is no state set with other graphics APIs, considering that many
- of them do not have a concept of the traditional OpenGL state machine.
- Rather, it is up to the implementation to create pipeline state objects
- with the desired blending, scissor, and stencil tests enabled. Note that
- this also includes OpenGL via the RHI. New QSGRenderNode implementations
- are recommended to set all scissor, stencil and blend state explicitly (as
- shown in the above list), even if they are targeting OpenGL.
-
- \l changedStates() should return which states this function changes. If a
- state is not covered by \l StateFlags, the state should be set to the
- default value according to the OpenGL specification. For other APIs, see
- the documentation for changedStates() for more information.
-
- \note Depth writes are disabled when this function is called
- (glDepthMask(false) with OpenGL). Enabling depth writes can lead to
- unexpected results, depending on the scenegraph backend in use and the
- content in the scene, so exercise caution with this.
-
- For APIs other than OpenGL, it will likely be necessary to query certain
- API-specific resources (for example, the graphics device or the command
- list/buffer to add the commands to). This is done via QSGRendererInterface.
-
- Assume nothing about the pipelines and dynamic states bound on the command
- list/buffer when this function is called.
-
- With some graphics APIs it can be necessary to reimplement prepare() in
- addition, or alternatively connect to the QQuickWindow::beforeRendering()
- signal. These are called/emitted before recording the beginning of a
- renderpass on the command buffer (vkCmdBeginRenderPass with Vulkan, or
- starting to encode via MTLRenderCommandEncoder in case of Metal. Recording
- copy operations cannot be done inside render() with such APIs. Rather, do
- such operations either in prepare() or the slot connected to
- beforeRendering (with DirectConnection).
+ When implementing a QSGRenderNode that uses QRhi to render, query the QRhi
+ object from the QQuickWindow via \l{QQuickWindow::rhi()}. To get a
+ QRhiCommandBuffer for submitting work to, call commandBuffer(). To query
+ information about the active render target, call renderTarget(). See the
+ \l{{Scene Graph - Custom QSGRenderNode}} example for details.
+
+ With Qt 6 and its QRhi-based scene graph renderer, no assumptions should be
+ made about the active (OpenGL) state when this function is called, even
+ when OpenGL is in use. Assume nothing about the pipelines and dynamic
+ states bound on the command list/buffer when this function is called.
+
+ \note Depth writes are expected to be disabled. Enabling depth writes can
+ lead to unexpected results, depending on the scenegraph backend in use and
+ the content in the scene, so exercise caution with this.
+
+ \note In Qt 6, \l changedStates() has limited use. See the documentation
+ for changedStates() for more information.
+
+ With some graphics APIs, including when using QRhi directly, it can be
+ necessary to reimplement prepare() in addition, or alternatively connect to
+ the QQuickWindow::beforeRendering() signal. These are called/emitted before
+ recording the beginning of a renderpass on the command buffer
+ (vkCmdBeginRenderPass with Vulkan, or starting to encode via
+ MTLRenderCommandEncoder in case of Metal. Recording copy operations cannot
+ be done inside render() with such APIs. Rather, do such operations either
+ in prepare() or the slot connected to beforeRendering (with
+ DirectConnection).
\sa QSGRendererInterface, QQuickWindow::rendererInterface()
*/
@@ -263,7 +272,7 @@ void QSGRenderNode::releaseResources()
\value BoundedRectRendering Indicates that the implementation of render()
does not render outside the area reported from rect() in item
coordinates. Such node implementations can lead to more efficient rendering,
- depending on the scenegraph backend. For example, the software backend can
+ depending on the scenegraph backend. For example, the \c software backend can
continue to use the more optimal partial update path when all render nodes
in the scene have this flag set.
@@ -282,9 +291,11 @@ void QSGRenderNode::releaseResources()
transparent pixels. Setting this flag can improve performance in some
cases.
- \omitvalue NoExternalRendering
+ \value NoExternalRendering Indicates that the implementation of prepare()
+ and render() use the QRhi family of APIs, instead of directly calling a 3D
+ API such as OpenGL, Vulkan, or Metal.
- \sa render(), rect()
+ \sa render(), prepare(), rect(), QRhi
*/
/*!
@@ -341,7 +352,15 @@ QRectF QSGRenderNode::rect() const
*/
const QMatrix4x4 *QSGRenderNode::projectionMatrix() const
{
- return &d->m_projectionMatrix;
+ return &d->m_projectionMatrix[0];
+}
+
+/*!
+ \internal
+ */
+const QMatrix4x4 *QSGRenderNode::projectionMatrix(int index) const
+{
+ return &d->m_projectionMatrix[index];
}
/*!
@@ -368,6 +387,59 @@ qreal QSGRenderNode::inheritedOpacity() const
return d->m_opacity;
}
+/*!
+ \return the current render target.
+
+ This is provided mainly to enable prepare() and render() implementations
+ that use QRhi accessing the QRhiRenderTarget's
+ \l{QRhiRenderPassDescriptor}{renderPassDescriptor} or
+ \l{QRhiRenderTarget::pixelSize()}{pixel size}.
+
+ To build a QRhiGraphicsPipeline, which implies having to provide a
+ QRhiRenderPassDescriptor, query the renderPassDescriptor from the render
+ target. Be aware however that the render target may change over the
+ lifetime of the custom QQuickItem and the QSGRenderNode. For example,
+ consider what happens when dynamically setting \c{layer.enabled: true} on
+ the item or an ancestor of it: this triggers rendering into a texture, not
+ directly to the window, which means the QSGRenderNode is going to work with
+ a different render target from then on. The new render target may then have
+ a different pixel format, which can make already built graphics pipelines
+ incompatible. This can be handled with logic such as the following:
+
+ \code
+ if (m_pipeline && renderTarget()->renderPassDescriptor()->serializedFormat() != m_renderPassFormat) {
+ delete m_pipeline;
+ m_pipeline = nullptr;
+ }
+ if (!m_pipeline) {
+ // Build a new QRhiGraphicsPipeline.
+ // ...
+ // Store the serialized format for fast and simple comparisons later on.
+ m_renderPassFormat = renderTarget()->renderPassDescriptor()->serializedFormat();
+ }
+ \endcode
+
+ \since 6.6
+
+ \sa commandBuffer()
+ */
+QRhiRenderTarget *QSGRenderNode::renderTarget() const
+{
+ return d->m_rt.rt;
+}
+
+/*!
+ \return the current command buffer.
+
+ \since 6.6
+
+ \sa renderTarget()
+ */
+QRhiCommandBuffer *QSGRenderNode::commandBuffer() const
+{
+ return d->m_rt.cb;
+}
+
QSGRenderNode::RenderState::~RenderState()
{
}
@@ -421,7 +493,7 @@ QSGRenderNode::RenderState::~RenderState()
\return the current clip region or null for backends where clipping is
implemented via stencil or scissoring.
- The software backend uses no projection, scissor or stencil, meaning most
+ The \c software backend uses no projection, scissor or stencil, meaning most
of the render state is not in use. However, the clip region that can be set
on the QPainter still has to be communicated since reconstructing this
manually in render() is not reasonable. It can therefore be queried via
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.h b/src/quick/scenegraph/coreapi/qsgrendernode.h
index fcf2b0e559..927a8dee59 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.h
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.h
@@ -9,6 +9,8 @@
QT_BEGIN_NAMESPACE
class QSGRenderNodePrivate;
+class QRhiRenderTarget;
+class QRhiCommandBuffer;
class Q_QUICK_EXPORT QSGRenderNode : public QSGNode
{
@@ -55,9 +57,12 @@ public:
virtual QRectF rect() const;
const QMatrix4x4 *projectionMatrix() const;
+ const QMatrix4x4 *projectionMatrix(int index) const;
const QMatrix4x4 *matrix() const;
const QSGClipNode *clipList() const;
qreal inheritedOpacity() const;
+ QRhiRenderTarget *renderTarget() const;
+ QRhiCommandBuffer *commandBuffer() const;
private:
QSGRenderNodePrivate *d;
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode_p.h b/src/quick/scenegraph/coreapi/qsgrendernode_p.h
index e00415f480..8bdd457b95 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrendernode_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QSGRenderNodePrivate
+class Q_QUICK_EXPORT QSGRenderNodePrivate
{
public:
QSGRenderNodePrivate();
@@ -32,7 +32,8 @@ public:
const QSGClipNode *m_clip_list;
qreal m_opacity;
QSGRenderTarget m_rt;
- QMatrix4x4 m_projectionMatrix;
+ QVarLengthArray<QMatrix4x4, 1> m_projectionMatrix;
+ QMatrix4x4 m_localMatrix; // ### Qt 7 m_matrix should not be a pointer
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
index 6e8fcae06f..d1f4e46ed0 100644
--- a/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
@@ -330,7 +330,7 @@ void RhiVisualizer::ChangeVis::gather(Node *n)
const QColor color = QColor::fromHsvF((visualizer->m_randomGenerator.generate() & 1023) / 1023.0f, 0.3f, 1.0f).toRgb();
const float alpha = 0.5f;
- QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix[0];
if (n->element()->batch->root)
matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
@@ -445,7 +445,7 @@ void RhiVisualizer::BatchVis::gather(Batch *b)
if (b->positionAttribute != 0)
return;
- QMatrix4x4 matrix(visualizer->m_renderer->m_current_projection_matrix);
+ QMatrix4x4 matrix(visualizer->m_renderer->m_current_projection_matrix[0]);
if (b->root)
matrix = matrix * qsg_matrixForRoot(b->root);
@@ -575,7 +575,7 @@ void RhiVisualizer::ClipVis::gather(QSGNode *node)
{
if (node->type() == QSGNode::ClipNodeType) {
QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
- QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix[0];
if (clipNode->matrix())
matrix = matrix * *clipNode->matrix();
@@ -674,7 +674,7 @@ void RhiVisualizer::ClipVis::render(QRhiCommandBuffer *cb)
void RhiVisualizer::OverdrawVis::gather(Node *n)
{
if (n->type() == QSGNode::GeometryNodeType && n->element()->batch) {
- QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix[0];
matrix(2, 2) = visualizer->m_renderer->m_zRange;
matrix(2, 3) = 1.0f - n->element()->order * visualizer->m_renderer->m_zRange;
diff --git a/src/quick/scenegraph/coreapi/qsgtexture.cpp b/src/quick/scenegraph/coreapi/qsgtexture.cpp
index 31e68b9f31..57111e9e5f 100644
--- a/src/quick/scenegraph/coreapi/qsgtexture.cpp
+++ b/src/quick/scenegraph/coreapi/qsgtexture.cpp
@@ -5,10 +5,11 @@
#include "qsgtexture_platform.h"
#include <private/qqmlglobal_p.h>
#include <private/qsgmaterialshader_p.h>
+#include <private/qsgrenderer_p.h>
#include <private/qquickitem_p.h> // qquickwindow_p.h cannot be included on its own due to template nonsense
#include <private/qquickwindow_p.h>
#include <QtCore/private/qnativeinterface_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(__GLIBC__)
#define CAN_BACKTRACE_EXECINFO
@@ -30,11 +31,12 @@
#ifndef QT_NO_DEBUG
Q_GLOBAL_STATIC(QSet<QSGTexture *>, qsg_valid_texture_set)
Q_GLOBAL_STATIC(QMutex, qsg_valid_texture_mutex)
-static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
#endif
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak)
+
bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) noexcept
{
return a.filtering == b.filtering
@@ -83,8 +85,9 @@ QSGTexturePrivate::QSGTexturePrivate(QSGTexture *t)
#endif
#ifdef Q_OS_WIN
, m_d3d11TextureAccessor(t)
+ , m_d3d12TextureAccessor(t)
#endif
-#if defined(__OBJC__)
+#if QT_CONFIG(metal)
, m_metalTextureAccessor(t)
#endif
#if QT_CONFIG(vulkan)
@@ -116,7 +119,7 @@ static QHash<QSGTexture*, SGTextureTraceItem*> qt_debug_allocated_textures;
inline static void qt_debug_print_texture_count()
{
- qDebug("Number of leaked textures: %i", qt_debug_texture_count);
+ qCDebug(lcQsgLeak, "Number of leaked textures: %i", qt_debug_texture_count);
qt_debug_texture_count = -1;
#if defined(CAN_BACKTRACE_EXECINFO)
@@ -243,8 +246,6 @@ static void qt_debug_remove_texture(QSGTexture* texture)
\note All classes with QSG prefix should be used solely on the scene graph's
rendering thread. See \l {Scene Graph and Rendering} for more information.
-
- \sa {Scene Graph - Rendering FBOs}
*/
/*!
@@ -304,7 +305,7 @@ QSGTexture::QSGTexture()
: QObject(*(new QSGTexturePrivate(this)))
{
#ifndef QT_NO_DEBUG
- if (qsg_leak_check)
+ if (lcQsgLeak().isDebugEnabled())
qt_debug_add_texture(this);
QMutexLocker locker(qsg_valid_texture_mutex());
@@ -319,7 +320,7 @@ QSGTexture::QSGTexture(QSGTexturePrivate &dd)
: QObject(dd)
{
#ifndef QT_NO_DEBUG
- if (qsg_leak_check)
+ if (lcQsgLeak().isDebugEnabled())
qt_debug_add_texture(this);
QMutexLocker locker(qsg_valid_texture_mutex());
@@ -333,7 +334,7 @@ QSGTexture::QSGTexture(QSGTexturePrivate &dd)
QSGTexture::~QSGTexture()
{
#ifndef QT_NO_DEBUG
- if (qsg_leak_check)
+ if (lcQsgLeak().isDebugEnabled())
qt_debug_remove_texture(this);
QMutexLocker locker(qsg_valid_texture_mutex());
@@ -580,8 +581,6 @@ QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
\warning This function can only be called from the rendering thread.
\since 6.0
-
- \internal
*/
QRhiTexture *QSGTexture::rhiTexture() const
{
@@ -602,8 +601,6 @@ QRhiTexture *QSGTexture::rhiTexture() const
\warning This function can only be called from the rendering thread.
\since 6.0
-
- \internal
*/
void QSGTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
{
@@ -686,6 +683,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting OpenGL texture objects.
\since 6.0
*/
@@ -786,6 +784,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Direct3D 11 texture objects.
\since 6.0
*/
@@ -837,6 +836,78 @@ void *QSGTexturePlatformD3D11::nativeTexture() const
return reinterpret_cast<void *>(quintptr(tex->nativeTexture().object));
return 0;
}
+
+namespace QNativeInterface {
+/*!
+ \class QNativeInterface::QSGD3D12Texture
+ \inmodule QtQuick
+ \ingroup native-interfaces
+ \ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
+ \brief Provides access to and enables adopting Direct3D 12 texture objects.
+ \since 6.6
+*/
+
+/*!
+ \fn void *QNativeInterface::QSGD3D12Texture::nativeTexture() const
+ \return the ID3D12Texture object.
+ */
+
+QT_DEFINE_NATIVE_INTERFACE(QSGD3D12Texture);
+
+/*!
+ Creates a new QSGTexture wrapping an existing Direct 3D 12 \a texture object
+ for \a window.
+
+ The native object is wrapped, but not owned, by the resulting QSGTexture.
+ The caller of the function is responsible for deleting the returned
+ QSGTexture, but that will not destroy the underlying native object.
+
+ This function is currently suitable for 2D RGBA textures only.
+
+ \warning This function will return null if the scene graph has not yet been
+ initialized.
+
+ Use \a options to customize the texture attributes. Only the
+ TextureHasAlphaChannel and TextureHasMipmaps are taken into account here.
+
+ \a size specifies the size in pixels.
+
+ \a resourceState must specify the
+ \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_resource_states}{current state}
+ of the texture resource.
+
+ \note This function must be called on the scene graph rendering thread.
+
+ \sa QQuickWindow::sceneGraphInitialized(), QSGTexture,
+ {Scene Graph - Metal Texture Import}, {Scene Graph - Vulkan Texture Import}
+
+ \since 6.6
+ */
+QSGTexture *QSGD3D12Texture::fromNative(void *texture,
+ int resourceState,
+ QQuickWindow *window,
+ const QSize &size,
+ QQuickWindow::CreateTextureOptions options)
+{
+ return QQuickWindowPrivate::get(window)->createTextureFromNativeTexture(quint64(texture), resourceState, size, options);
+}
+} // QNativeInterface
+
+int QSGTexturePlatformD3D12::nativeResourceState() const
+{
+ if (auto *tex = m_texture->rhiTexture())
+ return tex->nativeTexture().layout;
+ return 0;
+}
+
+void *QSGTexturePlatformD3D12::nativeTexture() const
+{
+ if (auto *tex = m_texture->rhiTexture())
+ return reinterpret_cast<void *>(quintptr(tex->nativeTexture().object));
+ return 0;
+}
+
#endif // win
#if defined(__OBJC__) || defined(Q_QDOC)
@@ -846,6 +917,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Metal texture objects.
\since 6.0
*/
@@ -893,6 +965,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Vulkan image objects.
\since 6.0
*/
@@ -974,11 +1047,12 @@ void *QSGTexture::resolveInterface(const char *name, int revision) const
#if QT_CONFIG(vulkan)
QT_NATIVE_INTERFACE_RETURN_IF(QSGVulkanTexture, &dd->m_vulkanTextureAccessor);
#endif
-#if defined(__OBJC__)
+#if QT_CONFIG(metal)
QT_NATIVE_INTERFACE_RETURN_IF(QSGMetalTexture, &dd->m_metalTextureAccessor);
#endif
#if defined(Q_OS_WIN)
QT_NATIVE_INTERFACE_RETURN_IF(QSGD3D11Texture, &dd->m_d3d11TextureAccessor);
+ QT_NATIVE_INTERFACE_RETURN_IF(QSGD3D12Texture, &dd->m_d3d12TextureAccessor);
#endif
#if QT_CONFIG(opengl)
QT_NATIVE_INTERFACE_RETURN_IF(QSGOpenGLTexture, &dd->m_openglTextureAccessor);
diff --git a/src/quick/scenegraph/coreapi/qsgtexture_mac.mm b/src/quick/scenegraph/coreapi/qsgtexture_mac.mm
index fb3c9fcf04..c68a1b732f 100644
--- a/src/quick/scenegraph/coreapi/qsgtexture_mac.mm
+++ b/src/quick/scenegraph/coreapi/qsgtexture_mac.mm
@@ -6,7 +6,7 @@
#include <private/qquickitem_p.h>
#include <private/qquickwindow_p.h>
#include <QtCore/private/qnativeinterface_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgtexture_p.h b/src/quick/scenegraph/coreapi/qsgtexture_p.h
index 8e57a174c6..178bd23265 100644
--- a/src/quick/scenegraph/coreapi/qsgtexture_p.h
+++ b/src/quick/scenegraph/coreapi/qsgtexture_p.h
@@ -39,7 +39,7 @@ bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b)
size_t qHash(const QSGSamplerDescription &s, size_t seed = 0) noexcept;
#if QT_CONFIG(opengl)
-class Q_QUICK_PRIVATE_EXPORT QSGTexturePlatformOpenGL : public QNativeInterface::QSGOpenGLTexture
+class Q_QUICK_EXPORT QSGTexturePlatformOpenGL : public QNativeInterface::QSGOpenGLTexture
{
public:
QSGTexturePlatformOpenGL(QSGTexture *t) : m_texture(t) { }
@@ -50,7 +50,7 @@ public:
#endif
#ifdef Q_OS_WIN
-class Q_QUICK_PRIVATE_EXPORT QSGTexturePlatformD3D11 : public QNativeInterface::QSGD3D11Texture
+class Q_QUICK_EXPORT QSGTexturePlatformD3D11 : public QNativeInterface::QSGD3D11Texture
{
public:
QSGTexturePlatformD3D11(QSGTexture *t) : m_texture(t) { }
@@ -58,21 +58,30 @@ public:
void *nativeTexture() const override;
};
+class Q_QUICK_EXPORT QSGTexturePlatformD3D12 : public QNativeInterface::QSGD3D12Texture
+{
+public:
+ QSGTexturePlatformD3D12(QSGTexture *t) : m_texture(t) { }
+ QSGTexture *m_texture;
+
+ int nativeResourceState() const override;
+ void *nativeTexture() const override;
+};
#endif
-#if defined(__OBJC__)
-class Q_QUICK_PRIVATE_EXPORT QSGTexturePlatformMetal : public QNativeInterface::QSGMetalTexture
+#if QT_CONFIG(metal)
+class Q_QUICK_EXPORT QSGTexturePlatformMetal : public QNativeInterface::QSGMetalTexture
{
public:
QSGTexturePlatformMetal(QSGTexture *t) : m_texture(t) { }
QSGTexture *m_texture;
- id<MTLTexture> nativeTexture() const override;
+ QT_OBJC_PROTOCOL(MTLTexture) nativeTexture() const override;
};
#endif
#if QT_CONFIG(vulkan)
-class Q_QUICK_PRIVATE_EXPORT QSGTexturePlatformVulkan : public QNativeInterface::QSGVulkanTexture
+class Q_QUICK_EXPORT QSGTexturePlatformVulkan : public QNativeInterface::QSGVulkanTexture
{
public:
QSGTexturePlatformVulkan(QSGTexture *t) : m_texture(t) { }
@@ -83,7 +92,7 @@ public:
};
#endif
-class Q_QUICK_PRIVATE_EXPORT QSGTexturePrivate : public QObjectPrivate
+class Q_QUICK_EXPORT QSGTexturePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QSGTexture)
public:
@@ -110,8 +119,9 @@ public:
#endif
#ifdef Q_OS_WIN
QSGTexturePlatformD3D11 m_d3d11TextureAccessor;
+ QSGTexturePlatformD3D12 m_d3d12TextureAccessor;
#endif
-#if defined(__OBJC__)
+#if QT_CONFIG(metal)
QSGTexturePlatformMetal m_metalTextureAccessor;
#endif
#if QT_CONFIG(vulkan)
@@ -119,7 +129,7 @@ public:
#endif
};
-Q_QUICK_PRIVATE_EXPORT bool qsg_safeguard_texture(QSGTexture *);
+Q_QUICK_EXPORT bool qsg_safeguard_texture(QSGTexture *);
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgtexture_platform.h b/src/quick/scenegraph/coreapi/qsgtexture_platform.h
index e8aef9078f..63c35c381d 100644
--- a/src/quick/scenegraph/coreapi/qsgtexture_platform.h
+++ b/src/quick/scenegraph/coreapi/qsgtexture_platform.h
@@ -15,8 +15,14 @@
#include <QtGui/qvulkaninstance.h>
#endif
-#if defined(__OBJC__) || defined(Q_QDOC)
-@protocol MTLTexture;
+#if QT_CONFIG(metal) || defined(Q_QDOC)
+# if defined(__OBJC__) || defined(Q_QDOC)
+ @protocol MTLTexture;
+# define QT_OBJC_PROTOCOL(protocol) id<protocol>
+# else
+ typedef struct objc_object *id;
+# define QT_OBJC_PROTOCOL(protocol) id
+# endif
#endif
QT_BEGIN_NAMESPACE
@@ -49,14 +55,25 @@ struct Q_QUICK_EXPORT QSGD3D11Texture
const QSize &size,
QQuickWindow::CreateTextureOptions options = {});
};
+struct Q_QUICK_EXPORT QSGD3D12Texture
+{
+ QT_DECLARE_NATIVE_INTERFACE(QSGD3D12Texture, 1, QSGTexture)
+ virtual void *nativeTexture() const = 0;
+ virtual int nativeResourceState() const = 0;
+ static QSGTexture *fromNative(void *texture,
+ int resourceState,
+ QQuickWindow *window,
+ const QSize &size,
+ QQuickWindow::CreateTextureOptions options = {});
+};
#endif
-#if defined(__OBJC__) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
struct Q_QUICK_EXPORT QSGMetalTexture
{
QT_DECLARE_NATIVE_INTERFACE(QSGMetalTexture, 1, QSGTexture)
- virtual id<MTLTexture> nativeTexture() const = 0;
- static QSGTexture *fromNative(id<MTLTexture> texture,
+ virtual QT_OBJC_PROTOCOL(MTLTexture) nativeTexture() const = 0;
+ static QSGTexture *fromNative(QT_OBJC_PROTOCOL(MTLTexture) texture,
QQuickWindow *window,
const QSize &size,
QQuickWindow::CreateTextureOptions options = {});
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index c20a126dce..9ce21d6b8f 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -18,6 +18,13 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_update_entry, int count)
+Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_update_exit)
+Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphRender_entry)
+Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphRender_exit)
+Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphStore_entry)
+Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphStore_exit)
+
static QElapsedTimer qsg_render_timer;
QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
@@ -226,16 +233,6 @@ void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &g
}
}
-void QSGDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
-{
- Q_UNUSED(ownerElement);
-}
-
-void QSGDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
-{
- Q_UNUSED(ownerElement);
-}
-
void QSGDistanceFieldGlyphCache::processPendingGlyphs()
{
/* Intentionally empty */
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 0e3d32528e..117e810411 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -31,7 +31,7 @@
#include <QtGui/private/qdatabuffer_p.h>
#include <private/qdistancefield_p.h>
#include <private/qintrusivelist_p.h>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
// ### remove
#include <QtQuick/private/qquicktext_p.h>
@@ -52,7 +52,7 @@ class QSGRenderNode;
class QSGRenderContext;
class QRhiTexture;
-class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx
+class Q_QUICK_EXPORT QSGNodeVisitorEx
{
public:
virtual ~QSGNodeVisitorEx();
@@ -89,7 +89,7 @@ public:
};
-class Q_QUICK_PRIVATE_EXPORT QSGVisitableNode : public QSGGeometryNode
+class Q_QUICK_EXPORT QSGVisitableNode : public QSGGeometryNode
{
public:
QSGVisitableNode() { setFlag(IsVisitableNode); }
@@ -98,7 +98,7 @@ public:
virtual void accept(QSGNodeVisitorEx *) = 0;
};
-class Q_QUICK_PRIVATE_EXPORT QSGInternalRectangleNode : public QSGVisitableNode
+class Q_QUICK_EXPORT QSGInternalRectangleNode : public QSGVisitableNode
{
public:
~QSGInternalRectangleNode() override;
@@ -110,6 +110,10 @@ public:
virtual void setGradientStops(const QGradientStops &stops) = 0;
virtual void setGradientVertical(bool vertical) = 0;
virtual void setRadius(qreal radius) = 0;
+ virtual void setTopLeftRadius(qreal radius) = 0;
+ virtual void setTopRightRadius(qreal radius) = 0;
+ virtual void setBottomLeftRadius(qreal radius) = 0;
+ virtual void setBottomRightRadius(qreal radius) = 0;
virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing); }
virtual void setAligned(bool aligned) = 0;
@@ -119,7 +123,7 @@ public:
};
-class Q_QUICK_PRIVATE_EXPORT QSGInternalImageNode : public QSGVisitableNode
+class Q_QUICK_EXPORT QSGInternalImageNode : public QSGVisitableNode
{
public:
~QSGInternalImageNode() override;
@@ -144,7 +148,7 @@ public:
void accept(QSGNodeVisitorEx *visitor) override { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); }
};
-class Q_QUICK_PRIVATE_EXPORT QSGPainterNode : public QSGVisitableNode
+class Q_QUICK_EXPORT QSGPainterNode : public QSGVisitableNode
{
public:
~QSGPainterNode() override;
@@ -205,7 +209,7 @@ protected:
#if QT_CONFIG(quick_sprite)
-class Q_QUICK_PRIVATE_EXPORT QSGSpriteNode : public QSGVisitableNode
+class Q_QUICK_EXPORT QSGSpriteNode : public QSGVisitableNode
{
public:
~QSGSpriteNode() override;
@@ -226,7 +230,7 @@ public:
#endif
-class Q_QUICK_PRIVATE_EXPORT QSGGuiThreadShaderEffectManager : public QObject
+class Q_QUICK_EXPORT QSGGuiThreadShaderEffectManager : public QObject
{
Q_OBJECT
@@ -267,7 +271,6 @@ public:
QShader rhiShader;
Type type;
QVector<Variable> variables;
- uint constantDataSize;
// Vertex inputs are not tracked here as QSGGeometry::AttributeSet
// hardwires that anyways so it is up to the shader to provide
@@ -283,10 +286,10 @@ Q_SIGNALS:
};
#ifndef QT_NO_DEBUG_STREAM
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v);
+Q_QUICK_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v);
#endif
-class Q_QUICK_PRIVATE_EXPORT QSGShaderEffectNode : public QObject, public QSGVisitableNode
+class Q_QUICK_EXPORT QSGShaderEffectNode : public QObject, public QSGVisitableNode
{
Q_OBJECT
@@ -337,6 +340,7 @@ public:
ShaderSyncData vertex;
ShaderSyncData fragment;
void *materialTypeCacheKey;
+ qint8 viewCount;
};
// Each ShaderEffect item has one node (render thread) and one manager (gui thread).
@@ -353,10 +357,10 @@ Q_SIGNALS:
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGShaderEffectNode::DirtyShaderFlags)
#ifndef QT_NO_DEBUG_STREAM
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd);
+Q_QUICK_EXPORT QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd);
#endif
-class Q_QUICK_PRIVATE_EXPORT QSGGlyphNode : public QSGVisitableNode
+class Q_QUICK_EXPORT QSGGlyphNode : public QSGVisitableNode
{
public:
enum AntialiasingMode
@@ -384,16 +388,12 @@ public:
virtual void update() = 0;
- void setOwnerElement(QQuickItem *ownerElement) { m_ownerElement = ownerElement; }
- QQuickItem *ownerElement() const { return m_ownerElement; }
-
void accept(QSGNodeVisitorEx *visitor) override { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); }
protected:
QRectF m_bounding_rect;
- QQuickItem *m_ownerElement = nullptr;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphConsumer
+class Q_QUICK_EXPORT QSGDistanceFieldGlyphConsumer
{
public:
virtual ~QSGDistanceFieldGlyphConsumer();
@@ -403,7 +403,7 @@ public:
};
typedef QIntrusiveList<QSGDistanceFieldGlyphConsumer, &QSGDistanceFieldGlyphConsumer::node> QSGDistanceFieldGlyphConsumerList;
-class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache
+class Q_QUICK_EXPORT QSGDistanceFieldGlyphCache
{
public:
QSGDistanceFieldGlyphCache(const QRawFont &font,
@@ -468,8 +468,6 @@ public:
void registerGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.insert(node); }
void unregisterGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.remove(node); }
- virtual void registerOwnerElement(QQuickItem *ownerElement);
- virtual void unregisterOwnerElement(QQuickItem *ownerElement);
virtual void processPendingGlyphs();
virtual bool eightBitFormatIsAlphaSwizzled() const = 0;
diff --git a/src/quick/scenegraph/qsgbasicglyphnode_p.h b/src/quick/scenegraph/qsgbasicglyphnode_p.h
index 5762673ce6..2063a2476c 100644
--- a/src/quick/scenegraph/qsgbasicglyphnode_p.h
+++ b/src/quick/scenegraph/qsgbasicglyphnode_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QSGMaterial;
-class Q_QUICK_PRIVATE_EXPORT QSGBasicGlyphNode: public QSGGlyphNode
+class Q_QUICK_EXPORT QSGBasicGlyphNode: public QSGGlyphNode
{
public:
QSGBasicGlyphNode();
diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp
index 8eb39b5f06..5c49bdb54a 100644
--- a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp
+++ b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp
@@ -10,13 +10,13 @@ QT_BEGIN_NAMESPACE
namespace
{
- struct SmoothVertex
+ struct SmoothImageVertex
{
float x, y, u, v;
float dx, dy, du, dv;
};
- const QSGGeometry::AttributeSet &smoothAttributeSet()
+ const QSGGeometry::AttributeSet &smoothImageAttributeSet()
{
static QSGGeometry::Attribute data[] = {
QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
@@ -24,7 +24,7 @@ namespace
QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::TexCoord1Attribute),
QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::TexCoord2Attribute)
};
- static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
+ static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothImageVertex), data };
return attrs;
}
}
@@ -97,7 +97,7 @@ void QSGBasicInternalImageNode::setAntialiasing(bool antialiasing)
return;
m_antialiasing = antialiasing;
if (m_antialiasing) {
- setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
+ setGeometry(new QSGGeometry(smoothImageAttributeSet(), 0));
setFlag(OwnsGeometry, true);
} else {
setGeometry(&m_geometry);
@@ -308,7 +308,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect,
if (antialiasing) {
if (!geometry || geometry->indexType() != indexType) {
- geometry = new QSGGeometry(smoothAttributeSet(),
+ geometry = new QSGGeometry(smoothImageAttributeSet(),
hCells * vCells * 4 + (hCells + vCells - 1) * 4,
hCells * vCells * 6 + (hCells + vCells) * 12,
indexType);
@@ -320,7 +320,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect,
Q_ASSERT(g);
g->setDrawingMode(QSGGeometry::DrawTriangles);
- SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
+ auto *vertices = reinterpret_cast<SmoothImageVertex *>(g->vertexData());
memset(vertices, 0, g->vertexCount() * g->sizeOfVertex());
void *indexData = g->indexData();
@@ -360,7 +360,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect,
bool isLeft = i == 0;
bool isRight = i == hCells - 1;
- SmoothVertex *v = vertices + index;
+ SmoothImageVertex *v = vertices + index;
int topLeft = index;
for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) {
@@ -521,12 +521,12 @@ void QSGBasicInternalImageNode::updateGeometry()
QSGGeometry *g = geometry();
Q_ASSERT(g != &m_geometry);
if (g->indexType() != QSGGeometry::UnsignedShortType) {
- setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
+ setGeometry(new QSGGeometry(smoothImageAttributeSet(), 0));
g = geometry();
}
g->allocate(8, 14);
g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
- SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
+ auto *vertices = reinterpret_cast<SmoothImageVertex *>(g->vertexData());
float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
? m_targetRect.width() : m_targetRect.height()) * 0.5f;
float sx = float(sr.width() / m_targetRect.width());
diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode_p.h b/src/quick/scenegraph/qsgbasicinternalimagenode_p.h
index c4aaba02fd..f5b43091c8 100644
--- a/src/quick/scenegraph/qsgbasicinternalimagenode_p.h
+++ b/src/quick/scenegraph/qsgbasicinternalimagenode_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QSGBasicInternalImageNode : public QSGInternalImageNode
+class Q_QUICK_EXPORT QSGBasicInternalImageNode : public QSGInternalImageNode
{
public:
QSGBasicInternalImageNode();
diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
index 9ce0ceefbc..8d1104a407 100644
--- a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
@@ -74,8 +74,7 @@ namespace
}
QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode()
- : m_radius(0)
- , m_pen_width(0)
+ : QSGInternalRectangleNode()
, m_aligned(true)
, m_antialiasing(false)
, m_gradient_is_opaque(true)
@@ -155,6 +154,35 @@ void QSGBasicInternalRectangleNode::setRadius(qreal radius)
m_dirty_geometry = true;
}
+void QSGBasicInternalRectangleNode::setTopLeftRadius(qreal radius)
+{
+ if (radius == m_topLeftRadius)
+ return;
+ m_topLeftRadius = radius;
+ m_dirty_geometry = true;
+}
+void QSGBasicInternalRectangleNode::setTopRightRadius(qreal radius)
+{
+ if (radius == m_topRightRadius)
+ return;
+ m_topRightRadius = radius;
+ m_dirty_geometry = true;
+}
+void QSGBasicInternalRectangleNode::setBottomLeftRadius(qreal radius)
+{
+ if (radius == m_bottomLeftRadius)
+ return;
+ m_bottomLeftRadius = radius;
+ m_dirty_geometry = true;
+}
+void QSGBasicInternalRectangleNode::setBottomRightRadius(qreal radius)
+{
+ if (radius == m_bottomRightRadius)
+ return;
+ m_bottomRightRadius = radius;
+ m_dirty_geometry = true;
+}
+
void QSGBasicInternalRectangleNode::setAntialiasing(bool antialiasing)
{
if (!supportsAntialiasing())
@@ -217,33 +245,93 @@ void QSGBasicInternalRectangleNode::updateGeometry()
Color4ub transparent = { 0, 0, 0, 0 };
const QGradientStops &stops = m_gradient_stops;
- float length = (m_gradient_is_vertical ? height : width);
+ float gradientStart = (m_gradient_is_vertical ? m_rect.top() : m_rect.left());
+ float gradientLength = (m_gradient_is_vertical ? height : width);
float secondaryLength = (m_gradient_is_vertical ? width : height);
int nextGradientStop = 0;
- float gradientPos = penWidth / length;
+ float gradientPos = penWidth / gradientLength;
while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos)
++nextGradientStop;
int lastGradientStop = stops.size() - 1;
- float lastGradientPos = 1.0f - penWidth / length;
+ float lastGradientPos = 1.0f - penWidth / gradientLength;
while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos)
--lastGradientStop;
int gradientIntersections = (lastGradientStop - nextGradientStop + 1);
- if (m_radius > 0) {
+ if (m_radius > 0
+ || m_topLeftRadius > 0
+ || m_topRightRadius > 0
+ || m_bottomLeftRadius > 0
+ || m_bottomRightRadius > 0) {
// Rounded corners.
- // Radius should never exceeds half of the width or half of the height
- float radius = qMin(qMin(width, height) * 0.5f, float(m_radius));
- QRectF innerRect = m_rect;
- innerRect.adjust(radius, radius, -radius, -radius);
-
- float innerRadius = radius - penWidth * 1.0f;
- float outerRadius = radius;
- float delta = qMin(width, height) * 0.5f;
+ // Radius should never exceed half the width or half the height.
+ float radiusTL = qMin(qMin(width, height) * 0.4999f, float(m_topLeftRadius < 0 ? m_radius : m_topLeftRadius));
+ float radiusTR = qMin(qMin(width, height) * 0.4999f, float(m_topRightRadius < 0 ? m_radius : m_topRightRadius));
+ float radiusBL = qMin(qMin(width, height) * 0.4999f, float(m_bottomLeftRadius < 0 ? m_radius : m_bottomLeftRadius));
+ float radiusBR = qMin(qMin(width, height) * 0.4999f, float(m_bottomRightRadius < 0 ? m_radius : m_bottomRightRadius));
+
+ // The code produces some artefacts when radius <= 0.5. A radius of half a pixel
+ // does not make much sense anyway, so we draw a normal corner in such a case.
+ if (radiusTL <= 0.5)
+ radiusTL = 0;
+ if (radiusTR <= 0.5)
+ radiusTR = 0;
+ if (radiusBL <= 0.5)
+ radiusBL = 0;
+ if (radiusBR <= 0.5)
+ radiusBR = 0;
+
+ // We want to keep a minimal inner radius in order to make the inner
+ // x-coordinates of an arc mathematically unique and identifiable.
+ const float innerRadiusTL = qMax(radiusTL - penWidth * 1.0f, 0.01);
+ const float innerRadiusTR = qMax(radiusTR - penWidth * 1.0f, 0.01);
+ const float innerRadiusBL = qMax(radiusBL - penWidth * 1.0f, 0.01);
+ const float innerRadiusBR = qMax(radiusBR - penWidth * 1.0f, 0.01);
+ const float outerRadiusTL = radiusTL;
+ const float outerRadiusTR = radiusTR;
+ const float outerRadiusBL = radiusBL;
+ const float outerRadiusBR = radiusBR;
+ const float delta = qMin(width, height) * 0.5f;
+
+ int segmentsTL = radiusTL == 0 ? 0 : qBound(3, qCeil(radiusTL * (M_PI / 6)), 18);
+ int segmentsTR = radiusTR == 0 ? 0 : qBound(3, qCeil(radiusTR * (M_PI / 6)), 18);
+ int segmentsBL = radiusBL == 0 ? 0 : qBound(3, qCeil(radiusBL * (M_PI / 6)), 18);
+ int segmentsBR = radiusBR == 0 ? 0 : qBound(3, qCeil(radiusBR * (M_PI / 6)), 18);
+
+ // If the radii on opposite sites in genraration direction are the same,
+ // we will set the segments of one side to 0 as these points would be
+ // calculated twice. Also, this optimizes for the case of similar radii
+ if (m_gradient_is_vertical) {
+ if (innerRadiusTL == innerRadiusTR) {
+ if (segmentsTL <= segmentsTR)
+ segmentsTL = 0;
+ else
+ segmentsTR = 0;
+ }
+ if (innerRadiusBL == innerRadiusBR){
+ if (segmentsBL <= segmentsBR)
+ segmentsBL = 0;
+ else
+ segmentsBR = 0;
+ }
+ } else {
+ if (innerRadiusTL == innerRadiusBL) {
+ if (segmentsTL <= segmentsBL)
+ segmentsTL = 0;
+ else
+ segmentsBL = 0;
+ }
+ if (innerRadiusTR == innerRadiusBR) {
+ if (segmentsTR <= segmentsBR)
+ segmentsTR = 0;
+ else
+ segmentsBR = 0;
+ }
+ }
- // Number of segments per corner, approximately one per 3 pixels.
- int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18);
+ const int sumSegments = segmentsTL + segmentsTR + segmentsBL + segmentsBR;
/*
@@ -265,8 +353,8 @@ void QSGBasicInternalRectangleNode::updateGeometry()
*/
- int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2;
- int outerVertexCount = (segments + 1) * 4;
+ const int innerVertexCount = (sumSegments + 4) * 2 + gradientIntersections * 2;
+ const int outerVertexCount = (sumSegments + 4) * 2;
int vertexCount = innerVertexCount;
if (m_antialiasing || penWidth)
vertexCount += innerVertexCount;
@@ -275,10 +363,11 @@ void QSGBasicInternalRectangleNode::updateGeometry()
if (m_antialiasing && penWidth)
vertexCount += outerVertexCount;
- int fillIndexCount = innerVertexCount;
- int innerAAIndexCount = innerVertexCount * 2 + 2;
- int borderIndexCount = innerVertexCount * 2 + 2;
- int outerAAIndexCount = outerVertexCount * 2 + 2;
+
+ const int fillIndexCount = innerVertexCount;
+ const int innerAAIndexCount = innerVertexCount * 2 + 2;
+ const int borderIndexCount = innerVertexCount * 2 + 2;
+ const int outerAAIndexCount = outerVertexCount * 2 + 2;
int indexCount = 0;
int fillHead = 0;
int innerAAHead = 0;
@@ -309,48 +398,322 @@ void QSGBasicInternalRectangleNode::updateGeometry()
quint16 *indices = g->indexDataAsUShort();
quint16 index = 0;
- float pp = 0; // previous inner primary coordinate.
- float pss = 0; // previous inner secondary start coordinate.
- float pse = 0; // previous inner secondary end coordinate.
- float angle = 0.5f * float(M_PI) / segments;
- float cosStep = qFastCos(angle);
- float sinStep = qFastSin(angle);
-
- float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left());
- float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right());
- float innerLength = (m_gradient_is_vertical ? innerRect.height() : innerRect.width());
- float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top());
- float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom());
+ float innerXPrev = 0.; // previous inner primary coordinate, both sides.
+ float innerYLeftPrev = 0.; // previous inner secondary coordinate, left.
+ float innerYRightPrev = 0.; // previous inner secondary coordinate, right.
+
+ const float angleTL = 0.5f * float(M_PI) / segmentsTL;
+ const float cosStepTL = qFastCos(angleTL);
+ const float sinStepTL = qFastSin(angleTL);
+ const float angleTR = 0.5f * float(M_PI) / segmentsTR;
+ const float cosStepTR = qFastCos(angleTR);
+ const float sinStepTR = qFastSin(angleTR);
+ const float angleBL = 0.5f * float(M_PI) / segmentsBL;
+ const float cosStepBL = qFastCos(angleBL);
+ const float sinStepBL = qFastSin(angleBL);
+ const float angleBR = 0.5f * float(M_PI) / segmentsBR;
+ const float cosStepBR = qFastCos(angleBR);
+ const float sinStepBR = qFastSin(angleBR);
+
+ //The x- and y-Axis are transposed, depending on gradient being vertical or horizontal
+ //Lets define some coordinates and radii. The first index is the part, the second index
+ //is the left or right side, as seen when moving from part0 to part1
+
+ // left vertices | right vertices
+ // |
+ // *************|**************
+ // * | | | *
+ // *--o | o--*
+ // * innerX/Y | innerX/Y *
+ // * | *
+ // * | * part 0
+ // * | *
+ // * | *
+ // * | *
+ // -----------------+--------------------> y
+ // * | *
+ // * | *
+ // * | *
+ // * | * part 1
+ // * | *
+ // * innerX/Y | innerX/Y *
+ // *--o | o--*
+ // * | | | *
+ // *************|**************
+ // |
+ // v x
+ //
+ // direction of vertex generation
+
+ const float outerXCenter[][2] = {{
+ float(m_gradient_is_vertical ? m_rect.top() + radiusTL : m_rect.left() + radiusTL),
+ float(m_gradient_is_vertical ? m_rect.top() + radiusTR : m_rect.left() + radiusBL)
+ }, {
+ float(m_gradient_is_vertical ? m_rect.bottom() - radiusBL : m_rect.right() - radiusTR),
+ float(m_gradient_is_vertical ? m_rect.bottom() - radiusBR : m_rect.right() - radiusBR)
+ }};
+
+ const float outerYCenter[][2] = {{
+ float(!m_gradient_is_vertical ? m_rect.top() + outerRadiusTL : m_rect.left() + outerRadiusTL),
+ float(!m_gradient_is_vertical ? m_rect.bottom() - outerRadiusBL : m_rect.right() - outerRadiusTR)
+ }, {
+ float(!m_gradient_is_vertical ? m_rect.top() + outerRadiusTR : m_rect.left() + outerRadiusBL),
+ float(!m_gradient_is_vertical ? m_rect.bottom() - outerRadiusBR : m_rect.right() - outerRadiusBR)
+ }};
+
+ const float innerXCenter[][2] = { {
+ float(m_gradient_is_vertical ? m_rect.top() + innerRadiusTL + penWidth : m_rect.left() + innerRadiusTL + penWidth),
+ float(m_gradient_is_vertical ? m_rect.top() + innerRadiusTR + penWidth: m_rect.left() + innerRadiusBL + penWidth)
+ }, {
+ float(m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBL - penWidth: m_rect.right() - innerRadiusTR - penWidth),
+ float(m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBR - penWidth: m_rect.right() - innerRadiusBR - penWidth)
+ }};
+
+ const float innerYCenter[][2] = { {
+ float(!m_gradient_is_vertical ? m_rect.top() + innerRadiusTL + penWidth : m_rect.left() + innerRadiusTL + penWidth),
+ float(!m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBL - penWidth : m_rect.right() - innerRadiusTR - penWidth)
+ },{
+ float(!m_gradient_is_vertical ? m_rect.top() + innerRadiusTR + penWidth : m_rect.left() + innerRadiusBL + penWidth),
+ float(!m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBR - penWidth : m_rect.right() - innerRadiusBR - penWidth)
+ }};
+
+ const float innerRadius[][2] = {{
+ innerRadiusTL,
+ !m_gradient_is_vertical ? innerRadiusBL : innerRadiusTR
+ }, {
+ !m_gradient_is_vertical ? innerRadiusTR : innerRadiusBL,
+ innerRadiusBR
+ }};
+
+ const float outerRadius[][2] = {{
+ outerRadiusTL,
+ !m_gradient_is_vertical ? outerRadiusBL : outerRadiusTR
+ }, {
+ !m_gradient_is_vertical ? outerRadiusTR : outerRadiusBL,
+ outerRadiusBR
+ }};
+
+ const int segments[][2] = {{
+ segmentsTL,
+ !m_gradient_is_vertical ? segmentsBL : segmentsTR
+ }, {
+ !m_gradient_is_vertical ? segmentsTR : segmentsBL,
+ segmentsBR
+ }};
+
+ const float cosStep[][2] = {{
+ cosStepTL,
+ !m_gradient_is_vertical ? cosStepBL : cosStepTR
+ }, {
+ !m_gradient_is_vertical ? cosStepTR : cosStepBL,
+ cosStepBR
+ }};
+
+ const float sinStep[][2] = {{
+ sinStepTL,
+ !m_gradient_is_vertical ? sinStepBL : sinStepTR
+ }, {
+ !m_gradient_is_vertical ? sinStepTR : sinStepBL,
+ sinStepBR
+ }};
+
+ auto fillColorFromX = [&](float x) {
+
+ float t = (x - gradientStart) / gradientLength;
+ t = qBound(0.0, t, 1.0);
+
+ int i = 1;
+ if (t < stops.first().first)
+ return colorToColor4ub(stops.first().second);
+ while (i < stops.size()) {
+ const QGradientStop &prev = stops.at(i - 1);
+ const QGradientStop &next = stops.at(i);
+ if (prev.first <= t && next.first > t) {
+ t = (t - prev.first) / (next.first - prev.first);
+ return colorToColor4ub(prev.second) * (1. - t) + colorToColor4ub(next.second) * t; }
+ i++;
+ }
+ return colorToColor4ub(stops.last().second);
+ };
for (int part = 0; part < 2; ++part) {
- float c = 1 - part;
- float s = part;
- for (int i = 0; i <= segments; ++i) {
- float p, ss, se;
- if (innerRadius > 0) {
- p = (part ? innerEnd : innerStart) - innerRadius * c; // current inner primary coordinate.
- ss = innerSecondaryStart - innerRadius * s; // current inner secondary start coordinate.
- se = innerSecondaryEnd + innerRadius * s; // current inner secondary end coordinate.
- gradientPos = ((part ? innerLength : 0) + radius - innerRadius * c) / length;
+ // cosine of the angle of the current segment, starting at 1 for part 0 and 0 for part 1
+ float cosSegmentAngleLeft = 1. - part;
+ // sine of the angle of the current segment
+ float sinSegmentAngleLeft = part;
+
+ float cosSegmentAngleRight = 1. - part;
+ float sinSegmentAngleRight = part;
+
+ bool advanceLeft = true;
+
+ // We draw both the left part and the right part of the rectangle at the same time.
+ // We also draw a vertex on the left side for every vertex on the right side. This
+ // syncronisation is required to make sure that all gradient stops can be inserted.
+ for (int iLeft = 0, iRight = 0; iLeft <= segments[part][0] || iRight <= segments[part][1]; ) {
+
+ float xLeft, yLeft,
+ xRight, yRight;
+
+ float outerXLeft, outerYLeft,
+ outerXRight, outerYRight;
+
+ float sinAngleLeft, cosAngleLeft,
+ sinAngleRight, cosAngleRight;
+
+ // calculate inner x-coordinates
+ xLeft = innerXCenter[part][0] - innerRadius[part][0] * cosSegmentAngleLeft;
+ xRight = innerXCenter[part][1] - innerRadius[part][1] * cosSegmentAngleRight;
+
+ // calcuate inner y-coordinates
+ yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinSegmentAngleLeft;
+ yRight = innerYCenter[part][1] + innerRadius[part][1] * sinSegmentAngleRight;
+
+ // Synchronize left and right hand x-coordinates. This is required to
+ // make sure that we can insert all gradient stops that require exactly two triangles at
+ // every x-coordinate. Take the smaller of both x-coordinates and then find the matching
+ // y-coordinates.
+ if ((iLeft <= segments[part][0] && xLeft <= xRight) || iRight > segments[part][1]) {
+ advanceLeft = true;
} else {
- p = (part ? innerEnd + innerRadius : innerStart - innerRadius); // current inner primary coordinate.
- ss = innerSecondaryStart - innerRadius; // current inner secondary start coordinate.
- se = innerSecondaryEnd + innerRadius; // current inner secondary end coordinate.
- gradientPos = ((part ? innerLength + innerRadius : -innerRadius) + radius) / length;
+ advanceLeft = false;
}
- float outerEdge = (part ? innerEnd : innerStart) - outerRadius * c; // current outer primary coordinate.
- float outerSecondaryStart = innerSecondaryStart - outerRadius * s; // current outer secondary start coordinate.
- float outerSecondaryEnd = innerSecondaryEnd + outerRadius * s; // current outer secondary end coordinate.
+ // Inner: Find the matching y-coordinates for the x-coordinate found above.
+ // Outer: Also set the sine and cosine to make sure that outer vertices are
+ // drawn correctly.
+ if (innerRadius[part][0] == innerRadius[part][1]) {
+ // Special case of equal radii. Optimize to avoid performance regression:
+ // Left and right is always equal and we can just copy the angles and
+ // mirror the coordinates.
+ if (advanceLeft) {
+ if (outerRadius[part][0] == 0) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = part ? -1. : 1.;
+ } else {
+ sinAngleLeft = sinSegmentAngleLeft;
+ cosAngleLeft = cosSegmentAngleLeft;
+ }
+ if (outerRadius[part][1] == 0) {
+ sinAngleRight = 1.;
+ cosAngleRight = part ? -1. : 1.;
+ } else {
+ sinAngleRight = sinSegmentAngleLeft;
+ cosAngleRight = cosSegmentAngleLeft;
+ }
+ xRight = xLeft;
+ yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight;
+ } else {
+ if (outerRadius[part][0] == 0) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = part ? -1. : 1.;
+ } else {
+ sinAngleLeft = sinSegmentAngleRight;
+ cosAngleLeft = cosSegmentAngleRight;
+ }
+ if (outerRadius[part][1] == 0) {
+ sinAngleRight = 1.;
+ cosAngleRight = part ? -1. : 1.;
+ } else {
+ sinAngleRight = sinSegmentAngleRight;
+ cosAngleRight = cosSegmentAngleRight;
+ }
+ xLeft = xRight;
+ yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft;
+ }
+ } else if (advanceLeft) {
+ if (outerRadius[part][0] == 0) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = part ? -1. : 1.;
+ } else {
+ sinAngleLeft = sinSegmentAngleLeft;
+ cosAngleLeft = cosSegmentAngleLeft;
+ }
+ if (outerRadius[part][1] == 0) {
+ // Outer: If the outer radius is zero we can return both sin and cos = 1
+ // to form a nice corner. Inner: Accept the x-coordinate from the other
+ // side and match the y-coordinate
+ sinAngleRight = 1.;
+ cosAngleRight = part ? -1. : 1.;
+ xRight = xLeft;
+ yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight;
+ } else if (xLeft >= innerXCenter[0][1] && xLeft <= innerXCenter[1][1]) {
+ // Outer: If we are on the straight line between the inner centers, we can
+ // just return sin = 1 and cos = 0. Inner: Accept the x-coordinate from the
+ // other side and match the y-coordinate
+ sinAngleRight = 1.;
+ cosAngleRight = 0.;
+ xRight = xLeft;
+ yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight;
+ } else {
+ // Inner: If we are on the rounded part of the oposite side, we have to find a vertex
+ // on that curve that matches the x-coordinate we selected.
+ // We always select the smaller x-coordinate and can therefore use a linear
+ // interpolation between the last point on this side and the point on this side
+ // that was not accepted because it was too large.
+ if (xRight != innerXPrev) {
+ float t = (xLeft - innerXPrev) / (xRight - innerXPrev);
+ yRight = innerYRightPrev * (1. - t) + yRight * t;
+ xRight = xLeft;
+ }
+ // Outer: With the coordinates from the interpolation we can calculate the sine
+ // and cosine of the respective angle quickly.
+ sinAngleRight = (yRight - innerYCenter[part][1]) / innerRadius[part][1];
+ cosAngleRight = -(xRight - innerXCenter[part][1]) / innerRadius[part][1];
+ }
+ } else {
+ // same as above but for the other side.
+ if (outerRadius[part][1] == 0) {
+ sinAngleRight = 1.;
+ cosAngleRight = part ? -1. : 1.;
+ } else {
+ sinAngleRight = sinSegmentAngleRight;
+ cosAngleRight = cosSegmentAngleRight;
+ }
+ if (outerRadius[part][0] == 0) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = part ? -1. : 1.;
+ xLeft = xRight;
+ yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft;
+ } else if (xRight >= innerXCenter[0][0] && xRight <= innerXCenter[1][0]) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = 0.;
+ xLeft = xRight;
+ yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft;
+ } else {
+ if (xLeft != innerXPrev) {
+ float t = (xRight - innerXPrev) / (xLeft - innerXPrev);
+ yLeft = innerYLeftPrev * (1. - t) + yLeft * t;
+ xLeft = xRight;
+ }
+ sinAngleLeft = -(yLeft - innerYCenter[part][0]) / innerRadius[part][0];
+ cosAngleLeft = -(xLeft - innerXCenter[part][0]) / innerRadius[part][0];
+ }
+ }
+
+ gradientPos = (xLeft - gradientStart) / gradientLength;
+
+ // calculate the matching outer coordinates
+ outerXLeft = outerXCenter[part][0] - outerRadius[part][0] * cosAngleLeft;
+ outerYLeft = outerYCenter[part][0] - outerRadius[part][0] * sinAngleLeft;
+ outerXRight = outerXCenter[part][1] - outerRadius[part][1] * cosAngleRight;
+ outerYRight = outerYCenter[part][1] + outerRadius[part][1] * sinAngleRight;
+
+ // insert gradient stops as required
while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
- // Insert vertices at gradient stops.
- float gp = (innerStart - radius) + stops.at(nextGradientStop).first * length;
- float t = (gp - pp) / (p - pp);
- float gis = pss * (1 - t) + t * ss; // gradient inner start
- float gie = pse * (1 - t) + t * se; // gradient inner end
+ float gradientX;
+ float gradientYLeft;
+ float gradientYRight;
+
+ // Insert vertices at gradient stops
+ gradientX = gradientStart + stops.at(nextGradientStop).first * gradientLength;
+ // bilinear interpolation of known vertices
+ float t = (gradientX - innerXPrev) / (xLeft - innerXPrev);
+ gradientYLeft = innerYLeftPrev * (1. - t) + t * yLeft;
+ gradientYRight = innerYRightPrev * (1. - t) + t * yRight;
- fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
+ fillColor = fillColorFromX(gradientX);
if (hasFill) {
indices[fillHead++] = index;
@@ -373,39 +736,35 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 3;
bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp);
- smoothVertices[index++].set(gp, gie, fillColor, dp, secondaryLength - gie - delta, m_gradient_is_vertical);
- smoothVertices[index++].set(gp, gis, fillColor, dp, delta - gis, m_gradient_is_vertical);
+ float dp = lower ? qMin(0.0f, gradientLength - gradientX - delta) : qMax(0.0f, delta - gradientX);
+ smoothVertices[index++].set(gradientX, gradientYRight, fillColor, dp, secondaryLength - gradientYRight - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYLeft, fillColor, dp, delta - gradientYLeft, m_gradient_is_vertical);
if (penWidth) {
- smoothVertices[index++].set(gp, gie, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(gp, gis, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYRight, borderColor, -0.49f * penWidth * cosAngleRight, 0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYLeft, borderColor, -0.49f * penWidth * cosAngleLeft, -0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
} else {
dp = lower ? delta : -delta;
- smoothVertices[index++].set(gp, gie, transparent, dp, delta, m_gradient_is_vertical);
- smoothVertices[index++].set(gp, gis, transparent, dp, -delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYRight, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYLeft, transparent, dp, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(gp, gie, fillColor, m_gradient_is_vertical);
- vertices[index++].set(gp, gis, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(gradientX, gradientYRight, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(gradientX, gradientYLeft, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(gp, gie, borderColor, m_gradient_is_vertical);
- vertices[index++].set(gp, gis, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(gradientX, gradientYRight, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(gradientX, gradientYLeft, borderColor, m_gradient_is_vertical);
}
}
- ++nextGradientStop;
+
+ innerXPrev = gradientX;
+ innerYLeftPrev = gradientYLeft;
+ innerYRightPrev = gradientYRight;
+
+ nextGradientStop++;
}
if (!stops.isEmpty()) {
- if (nextGradientStop == 0) {
- fillColor = colorToColor4ub(stops.at(0).second);
- } else if (nextGradientStop == stops.size()) {
- fillColor = colorToColor4ub(stops.last().second);
- } else {
- const QGradientStop &prev = stops.at(nextGradientStop - 1);
- const QGradientStop &next = stops.at(nextGradientStop);
- float t = (gradientPos - prev.first) / (next.first - prev.first);
- fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
- }
+ fillColor = fillColorFromX(xLeft);
}
if (hasFill) {
@@ -426,48 +785,57 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 1;
indices[innerAATail++] = index + 3;
- float dp = part ? qMin(0.0f, length - p - delta) : qMax(0.0f, delta - p);
- smoothVertices[index++].set(p, se, fillColor, dp, secondaryLength - se - delta, m_gradient_is_vertical);
- smoothVertices[index++].set(p, ss, fillColor, dp, delta - ss, m_gradient_is_vertical);
+ float dp = part ? qMin(0.0f, gradientLength - xRight - delta) : qMax(0.0f, delta - xRight);
+ smoothVertices[index++].set(xRight, yRight, fillColor, dp, secondaryLength - yRight - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(xLeft, yLeft, fillColor, dp, delta - yLeft, m_gradient_is_vertical);
dp = part ? delta : -delta;
if (penWidth) {
- smoothVertices[index++].set(p, se, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(p, ss, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, 0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, 0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, dp, delta, m_gradient_is_vertical);
- smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, dp, -delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(xRight, yRight, borderColor, -0.49f * penWidth * cosAngleRight, 0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
+ smoothVertices[index++].set(xLeft, yLeft, borderColor, -0.49f * penWidth * cosAngleLeft, -0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerXRight, outerYRight, borderColor, 0.49f * penWidth * cosAngleRight, -0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerXLeft, outerYLeft, borderColor, 0.49f * penWidth * cosAngleLeft, 0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerXRight, outerYRight, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerXLeft, outerYLeft, transparent, dp, -delta, m_gradient_is_vertical);
indices[--outerAAHead] = index - 2;
indices[--outerAAHead] = index - 4;
indices[outerAATail++] = index - 3;
indices[outerAATail++] = index - 1;
} else {
- smoothVertices[index++].set(p, se, transparent, dp, delta, m_gradient_is_vertical);
- smoothVertices[index++].set(p, ss, transparent, dp, -delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(xRight, yRight, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(xLeft, yLeft, transparent, dp, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(p, se, fillColor, m_gradient_is_vertical);
- vertices[index++].set(p, ss, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(xRight, yRight, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(xLeft, yLeft, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(p, se, borderColor, m_gradient_is_vertical);
- vertices[index++].set(p, ss, borderColor, m_gradient_is_vertical);
- vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical);
- vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(xRight, yRight, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(xLeft, yLeft, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerXRight, outerYRight, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerXLeft, outerYLeft, borderColor, m_gradient_is_vertical);
}
}
- pp = p;
- pss = ss;
- pse = se;
+ innerXPrev = xLeft;
+ innerYLeftPrev = yLeft;
+ innerYRightPrev = yRight;
- // Rotate
- qreal tmp = c;
- c = c * cosStep - s * sinStep;
- s = s * cosStep + tmp * sinStep;
+ // Advance the point. This corresponds to a rotation of the respective segment
+ if (advanceLeft) {
+ iLeft++;
+ qreal tmp = cosSegmentAngleLeft;
+ cosSegmentAngleLeft = cosSegmentAngleLeft * cosStep[part][0] - sinSegmentAngleLeft * sinStep[part][0];
+ sinSegmentAngleLeft = sinSegmentAngleLeft * cosStep[part][0] + tmp * sinStep[part][0];
+ } else {
+ iRight++;
+ qreal tmp = cosSegmentAngleRight;
+ cosSegmentAngleRight = cosSegmentAngleRight * cosStep[part][1] - sinSegmentAngleRight * sinStep[part][1];
+ sinSegmentAngleRight = sinSegmentAngleRight * cosStep[part][1] + tmp * sinStep[part][1];
+ }
}
}
+
Q_ASSERT(index == vertexCount);
// Close the triangle strips.
@@ -552,11 +920,11 @@ void QSGBasicInternalRectangleNode::updateGeometry()
for (int part = -1; part <= 1; part += 2) {
float innerEdge = (part == 1 ? innerEnd : innerStart);
float outerEdge = (part == 1 ? outerEnd : outerStart);
- gradientPos = (innerEdge - innerStart + penWidth) / length;
+ gradientPos = (innerEdge - innerStart + penWidth) / gradientLength;
while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
// Insert vertices at gradient stops.
- float gp = (innerStart - penWidth) + stops.at(nextGradientStop).first * length;
+ float gp = (innerStart - penWidth) + stops.at(nextGradientStop).first * gradientLength;
fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
@@ -581,7 +949,7 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 3;
bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp);
+ float dp = lower ? qMin(0.0f, gradientLength - gp - delta) : qMax(0.0f, delta - gp);
smoothVertices[index++].set(gp, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
smoothVertices[index++].set(gp, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
if (penWidth) {
@@ -633,7 +1001,7 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 1;
indices[innerAATail++] = index + 3;
- float dp = part == 1 ? qMin(0.0f, length - innerEdge - delta) : qMax(0.0f, delta - innerEdge);
+ float dp = part == 1 ? qMin(0.0f, gradientLength - innerEdge - delta) : qMax(0.0f, delta - innerEdge);
smoothVertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
smoothVertices[index++].set(innerEdge, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
index 808d1535e4..b806904f07 100644
--- a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QSGBasicInternalRectangleNode : public QSGInternalRectangleNode
+class Q_QUICK_EXPORT QSGBasicInternalRectangleNode : public QSGInternalRectangleNode
{
public:
QSGBasicInternalRectangleNode();
@@ -32,6 +32,10 @@ public:
void setGradientStops(const QGradientStops &stops) override;
void setGradientVertical(bool vertical) override;
void setRadius(qreal radius) override;
+ void setTopLeftRadius(qreal radius) override;
+ void setTopRightRadius(qreal radius) override;
+ void setBottomLeftRadius(qreal radius) override;
+ void setBottomRightRadius(qreal radius) override;
void setAntialiasing(bool antialiasing) override;
void setAligned(bool aligned) override;
void update() override;
@@ -48,8 +52,12 @@ protected:
QGradientStops m_gradient_stops;
QColor m_color;
QColor m_border_color;
- qreal m_radius;
- qreal m_pen_width;
+ float m_radius = 0.0f;
+ float m_topLeftRadius = -1.0f;
+ float m_topRightRadius = -1.0f;
+ float m_bottomLeftRadius = -1.0f;
+ float m_bottomRightRadius = -1.0f;
+ float m_pen_width = 0.0f;
uint m_aligned : 1;
uint m_antialiasing : 1;
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index f56e9898c6..01e0707556 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -4,8 +4,9 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <QtQuick/private/qsgadaptationlayer_p.h>
+#include <QtQuick/private/qsginternaltextnode_p.h>
#include <QGuiApplication>
#include <QScreen>
@@ -63,6 +64,13 @@ Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph")
// Timing inside the renderer base class
Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer")
+// Leak checks
+Q_LOGGING_CATEGORY(lcQsgLeak, "qt.scenegraph.leaks")
+
+// Applicable for render loops that install their own animation driver, such as
+// the 'threaded' loop. This env.var. is documented in the scenegraph docs.
+DEFINE_BOOL_CONFIG_OPTION(useElapsedTimerBasedAnimationDriver, QSG_USE_SIMPLE_ANIMATION_DRIVER);
+
bool qsg_useConsistentTiming()
{
int use = -1;
@@ -76,21 +84,9 @@ bool qsg_useConsistentTiming()
class QSGAnimationDriver : public QAnimationDriver
{
- Q_OBJECT
public:
- enum Mode {
- VSyncMode,
- TimerMode
- };
-
QSGAnimationDriver(QObject *parent)
: QAnimationDriver(parent)
- , m_time(0)
- , m_vsync(0)
- , m_mode(VSyncMode)
- , m_lag(0)
- , m_bad(0)
- , m_good(0)
{
QScreen *screen = QGuiApplication::primaryScreen();
if (screen) {
@@ -103,6 +99,35 @@ public:
} else {
m_vsync = 16.67f;
}
+ }
+
+ float vsyncInterval() const { return m_vsync; }
+
+ virtual bool isVSyncDependent() const = 0;
+
+protected:
+ float m_vsync = 0;
+};
+
+// default as in default for the threaded render loop
+class QSGDefaultAnimationDriver : public QSGAnimationDriver
+{
+ Q_OBJECT
+public:
+ enum Mode {
+ VSyncMode,
+ TimerMode
+ };
+
+ QSGDefaultAnimationDriver(QObject *parent)
+ : QSGAnimationDriver(parent)
+ , m_time(0)
+ , m_mode(VSyncMode)
+ , m_lag(0)
+ , m_bad(0)
+ , m_good(0)
+ {
+ QScreen *screen = QGuiApplication::primaryScreen();
if (screen && !qsg_useConsistentTiming()) {
if (m_vsync <= 0)
m_mode = TimerMode;
@@ -192,10 +217,12 @@ public:
advanceAnimation();
}
- float vsyncInterval() const { return m_vsync; } // this should always return something sane, regardless of m_mode
+ bool isVSyncDependent() const override
+ {
+ return true;
+ }
double m_time;
- float m_vsync;
Mode m_mode;
QElapsedTimer m_timer;
QElapsedTimer m_wallTime;
@@ -204,6 +231,81 @@ public:
int m_good;
};
+// Advance based on QElapsedTimer. (so like the TimerMode of QSGDefaultAnimationDriver)
+// Does not depend on vsync-based throttling.
+//
+// NB this is not the same as not installing a QAnimationDriver: the built-in
+// approach in QtCore is to rely on 16 ms timer events which are potentially a
+// lot less accurate.
+//
+// This has the benefits of:
+// - not needing any of the infrastructure for falling back to a
+// QTimer when there are multiple windows,
+// - needing no heuristics trying determine if vsync-based throttling
+// is missing or broken,
+// - being compatible with any kind of temporal drifts in vsync throttling
+// which is reportedly happening in various environments and platforms
+// still,
+// - not being tied to the primary screen's refresh rate, i.e. this is
+// correct even if the window is on some secondary screen with a
+// different refresh rate,
+// - not having to worry about the potential effects of variable refresh
+// rate solutions,
+// - render thread animators work correctly regardless of vsync.
+//
+// On the downside, some animations might appear less smooth (compared to the
+// ideal single window case of QSGDefaultAnimationDriver).
+//
+class QSGElapsedTimerAnimationDriver : public QSGAnimationDriver
+{
+public:
+ QSGElapsedTimerAnimationDriver(QObject *parent)
+ : QSGAnimationDriver(parent)
+ {
+ qCDebug(QSG_LOG_INFO, "Animation Driver: using QElapsedTimer, thread %p %s",
+ QThread::currentThread(),
+ QThread::currentThread() == qGuiApp->thread() ? "(gui/main thread)" : "(render thread)");
+ }
+
+ void start() override
+ {
+ m_wallTime.restart();
+ QAnimationDriver::start();
+ }
+
+ qint64 elapsed() const override
+ {
+ return m_wallTime.elapsed();
+ }
+
+ void advance() override
+ {
+ advanceAnimation();
+ }
+
+ bool isVSyncDependent() const override
+ {
+ return false;
+ }
+
+private:
+ QElapsedTimer m_wallTime;
+};
+
+QSGRenderContext::FontKey::FontKey(const QRawFont &font, int quality)
+{
+ QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
+ if (fe != nullptr)
+ faceId = fe->faceId();
+ style = font.style();
+ weight = font.weight();
+ renderTypeQuality = quality;
+ if (faceId.filename.isEmpty()) {
+ familyName = font.familyName();
+ styleName = font.styleName();
+ }
+}
+
/*!
\class QSGContext
@@ -245,6 +347,16 @@ QSGInternalRectangleNode *QSGContext::createInternalRectangleNode(const QRectF &
return node;
}
+QSGInternalTextNode *QSGContext::createInternalTextNode(QSGRenderContext *renderContext)
+{
+ return new QSGInternalTextNode(renderContext);
+}
+
+QSGTextNode *QSGContext::createTextNode(QSGRenderContext *renderContext)
+{
+ return createInternalTextNode(renderContext);
+}
+
/*!
Creates a new shader effect helper instance. This function is called on the
GUI thread, unlike the others. This is necessary in order to provide
@@ -270,7 +382,10 @@ QSGShaderEffectNode *QSGContext::createShaderEffectNode(QSGRenderContext *)
*/
QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent)
{
- return new QSGAnimationDriver(parent);
+ if (useElapsedTimerBasedAnimationDriver())
+ return new QSGElapsedTimerAnimationDriver(parent);
+
+ return new QSGDefaultAnimationDriver(parent);
}
/*!
@@ -282,6 +397,14 @@ float QSGContext::vsyncIntervalForAnimationDriver(QAnimationDriver *driver)
return static_cast<QSGAnimationDriver *>(driver)->vsyncInterval();
}
+/*!
+ \return true if \a driver relies on vsync-based throttling in some form.
+ */
+bool QSGContext::isVSyncDependent(QAnimationDriver *driver)
+{
+ return static_cast<QSGAnimationDriver *>(driver)->isVSyncDependent();
+}
+
QSize QSGContext::minimumFBOSize() const
{
return QSize(1, 1);
@@ -365,6 +488,16 @@ void QSGRenderContext::preprocess()
}
/*!
+ Factory function for curve atlases that can be used to provide geometry for the curve
+ renderer for a given font.
+*/
+QSGCurveGlyphAtlas *QSGRenderContext::curveGlyphAtlas(const QRawFont &font)
+{
+ Q_UNUSED(font);
+ return nullptr;
+}
+
+/*!
Factory function for scene graph backends of the distance-field glyph cache.
*/
QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &, int)
@@ -380,7 +513,13 @@ void QSGRenderContext::invalidateGlyphCaches()
void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
{
engine->ref.ref();
- m_fontEnginesToClean << engine;
+ m_fontEnginesToClean[engine]++;
+}
+
+void QSGRenderContext::unregisterFontengineForCleanup(QFontEngine *engine)
+{
+ m_fontEnginesToClean[engine]--;
+ Q_ASSERT(m_fontEnginesToClean.value(engine) >= 0);
}
QRhi *QSGRenderContext::rhi() const
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index 742b3b1448..885b207f40 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -24,15 +24,20 @@
#include <private/qtquickglobal_p.h>
#include <private/qrawfont_p.h>
+#include <private/qfontengine_p.h>
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgrendererinterface.h>
+#include <QtQuick/qsgtextnode.h>
+
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
class QSGContextPrivate;
class QSGInternalRectangleNode;
class QSGInternalImageNode;
+class QSGInternalTextNode;
class QSGPainterNode;
class QSGGlyphNode;
class QSGRenderer;
@@ -50,6 +55,7 @@ class QSGRendererInterface;
class QSGShaderEffectNode;
class QSGGuiThreadShaderEffectManager;
class QSGRectangleNode;
+class QSGTextNode;
class QSGImageNode;
class QSGNinePatchNode;
class QSGSpriteNode;
@@ -60,6 +66,8 @@ class QRhiRenderTarget;
class QRhiRenderPassDescriptor;
class QRhiCommandBuffer;
class QQuickGraphicsConfiguration;
+class QQuickItem;
+class QSGCurveGlyphAtlas;
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP)
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION)
@@ -70,7 +78,7 @@ Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER)
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO)
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_RENDERLOOP)
-class Q_QUICK_PRIVATE_EXPORT QSGContext : public QObject
+class Q_QUICK_EXPORT QSGContext : public QObject
{
Q_OBJECT
@@ -91,8 +99,9 @@ public:
QSGInternalRectangleNode *createInternalRectangleNode(const QRectF &rect, const QColor &c);
virtual QSGInternalRectangleNode *createInternalRectangleNode() = 0;
virtual QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) = 0;
+ virtual QSGInternalTextNode *createInternalTextNode(QSGRenderContext *renderContext);
virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0;
- virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode, int renderTypeQuality) = 0;
+ virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, QSGTextNode::RenderType renderType, int renderTypeQuality) = 0;
virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0;
virtual QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager();
virtual QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext);
@@ -101,12 +110,14 @@ public:
#endif
virtual QAnimationDriver *createAnimationDriver(QObject *parent);
virtual float vsyncIntervalForAnimationDriver(QAnimationDriver *driver);
+ virtual bool isVSyncDependent(QAnimationDriver *driver);
virtual QSize minimumFBOSize() const;
virtual QSurfaceFormat defaultSurfaceFormat() const = 0;
virtual QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext);
+ virtual QSGTextNode *createTextNode(QSGRenderContext *renderContext);
virtual QSGRectangleNode *createRectangleNode() = 0;
virtual QSGImageNode *createImageNode() = 0;
virtual QSGNinePatchNode *createNinePatchNode() = 0;
@@ -119,7 +130,7 @@ public:
static QString backend();
};
-class Q_QUICK_PRIVATE_EXPORT QSGRenderContext : public QObject
+class Q_QUICK_EXPORT QSGRenderContext : public QObject
{
Q_OBJECT
public:
@@ -157,6 +168,7 @@ public:
virtual void preprocess();
virtual void invalidateGlyphCaches();
virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality);
+ virtual QSGCurveGlyphAtlas *curveGlyphAtlas(const QRawFont &font);
QSGTexture *textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window);
virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const = 0;
@@ -165,6 +177,7 @@ public:
virtual int maxTextureSize() const = 0;
+ void unregisterFontengineForCleanup(QFontEngine *engine);
void registerFontengineForCleanup(QFontEngine *engine);
virtual QRhi *rhi() const;
@@ -178,17 +191,48 @@ public Q_SLOTS:
void textureFactoryDestroyed(QObject *o);
protected:
+ struct FontKey {
+ FontKey(const QRawFont &font, int renderTypeQuality);
+
+ QFontEngine::FaceId faceId;
+ QFont::Style style;
+ int weight;
+ int renderTypeQuality;
+ QString familyName;
+ QString styleName;
+ };
+ friend bool operator==(const QSGRenderContext::FontKey &f1, const QSGRenderContext::FontKey &f2);
+ friend size_t qHash(const QSGRenderContext::FontKey &f, size_t seed);
+
// Hold m_sg with QPointer in the rare case it gets deleted before us.
QPointer<QSGContext> m_sg;
QMutex m_mutex;
QHash<QObject *, QSGTexture *> m_textures;
QSet<QSGTexture *> m_texturesToDelete;
- QHash<QString, QSGDistanceFieldGlyphCache *> m_glyphCaches;
+ QHash<FontKey, QSGDistanceFieldGlyphCache *> m_glyphCaches;
- QSet<QFontEngine *> m_fontEnginesToClean;
+ // References to font engines that are currently in use by native rendering glyph nodes
+ // and which must be kept alive as long as they are used in the render thread.
+ QHash<QFontEngine *, int> m_fontEnginesToClean;
};
+inline bool operator ==(const QSGRenderContext::FontKey &f1, const QSGRenderContext::FontKey &f2)
+{
+ return f1.faceId == f2.faceId
+ && f1.style == f2.style
+ && f1.weight == f2.weight
+ && f1.renderTypeQuality == f2.renderTypeQuality
+ && f1.familyName == f2.familyName
+ && f1.styleName == f2.styleName;
+}
+
+inline size_t qHash(const QSGRenderContext::FontKey &f, size_t seed = 0)
+{
+ return qHashMulti(seed, f.faceId, f.renderTypeQuality, f.familyName, f.styleName, f.style, f.weight);
+}
+
+
QT_END_NAMESPACE
#endif // QSGCONTEXT_H
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index e21f2fbb6f..8026f68eb4 100644
--- a/src/quick/scenegraph/qsgcontextplugin.cpp
+++ b/src/quick/scenegraph/qsgcontextplugin.cpp
@@ -103,7 +103,7 @@ QSGAdaptationBackendData *contextFactory()
// caused by run time hocus pocus. If one wants to use the software backend
// in a GL or Vulkan capable Qt build (or on Windows or Apple platforms), it
// has to be requested explicitly.
-#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) && !defined(Q_OS_WIN) && !defined(Q_OS_MACOS) && !defined(Q_OS_IOS)
+#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) && !QT_CONFIG(metal) && !defined(Q_OS_WIN)
if (requestedBackend.isEmpty())
requestedBackend = QLatin1String("software");
#endif
diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h
index 853641b509..966cf0b978 100644
--- a/src/quick/scenegraph/qsgcontextplugin_p.h
+++ b/src/quick/scenegraph/qsgcontextplugin_p.h
@@ -26,7 +26,7 @@ class QSGContext;
class QSGRenderLoop;
-struct Q_QUICK_PRIVATE_EXPORT QSGContextFactoryInterface : public QFactoryInterface
+struct Q_QUICK_EXPORT QSGContextFactoryInterface : public QFactoryInterface
{
enum Flag {
SupportsShaderEffectNode = 0x01
@@ -46,7 +46,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGContextFactoryInterface::Flags)
"org.qt-project.Qt.QSGContextFactoryInterface"
Q_DECLARE_INTERFACE(QSGContextFactoryInterface, QSGContextFactoryInterface_iid)
-class Q_QUICK_PRIVATE_EXPORT QSGContextPlugin : public QObject, public QSGContextFactoryInterface
+class Q_QUICK_EXPORT QSGContextPlugin : public QObject, public QSGContextFactoryInterface
{
Q_OBJECT
Q_INTERFACES(QSGContextFactoryInterface:QFactoryInterface)
diff --git a/src/quick/scenegraph/qsgcurveabstractnode_p.h b/src/quick/scenegraph/qsgcurveabstractnode_p.h
new file mode 100644
index 0000000000..aadc17d3f0
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurveabstractnode_p.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGCURVEABSTRACTNODE_P_H
+#define QSGCURVEABSTRACTNODE_P_H
+
+#include <QtGui/qcolor.h>
+#include <QtQuick/qsgnode.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QSGCurveAbstractNode : public QSGGeometryNode
+{
+public:
+ virtual void setColor(QColor col) = 0;
+ virtual void cookGeometry() = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGCURVEABSTRACTNODE_P_H
diff --git a/src/quick/scenegraph/qsgcurvefillnode.cpp b/src/quick/scenegraph/qsgcurvefillnode.cpp
new file mode 100644
index 0000000000..9fa526bb0a
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurvefillnode.cpp
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgcurvefillnode_p.h"
+#include "qsgcurvefillnode_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGCurveFillNode::QSGCurveFillNode()
+{
+ setFlag(OwnsGeometry, true);
+ setGeometry(new QSGGeometry(attributes(), 0, 0));
+
+ updateMaterial();
+}
+
+void QSGCurveFillNode::updateMaterial()
+{
+ m_material.reset(new QSGCurveFillMaterial(this));
+ setMaterial(m_material.data());
+}
+
+void QSGCurveFillNode::cookGeometry()
+{
+ QSGGeometry *g = geometry();
+ if (g->indexType() != QSGGeometry::UnsignedIntType) {
+ g = new QSGGeometry(attributes(),
+ m_uncookedVertexes.size(),
+ m_uncookedIndexes.size(),
+ QSGGeometry::UnsignedIntType);
+ setGeometry(g);
+ } else {
+ g->allocate(m_uncookedVertexes.size(), m_uncookedIndexes.size());
+ }
+
+ g->setDrawingMode(QSGGeometry::DrawTriangles);
+ memcpy(g->vertexData(),
+ m_uncookedVertexes.constData(),
+ g->vertexCount() * g->sizeOfVertex());
+ memcpy(g->indexData(),
+ m_uncookedIndexes.constData(),
+ g->indexCount() * g->sizeOfIndex());
+
+ m_uncookedIndexes.clear();
+ m_uncookedVertexes.clear();
+}
+
+const QSGGeometry::AttributeSet &QSGCurveFillNode::attributes()
+{
+ static QSGGeometry::Attribute data[] = {
+ QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(1, 3, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(2, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute),
+ };
+ static QSGGeometry::AttributeSet attrs = { 4, sizeof(CurveNodeVertex), data };
+ return attrs;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcurvefillnode_p.cpp b/src/quick/scenegraph/qsgcurvefillnode_p.cpp
new file mode 100644
index 0000000000..387f4b3ce6
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurvefillnode_p.cpp
@@ -0,0 +1,309 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgcurvefillnode_p_p.h"
+#include "qsgcurvefillnode_p.h"
+#include "util/qsggradientcache_p.h"
+
+#include <private/qsgtexture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+ class QSGCurveFillMaterialShader : public QSGMaterialShader
+ {
+ public:
+ QSGCurveFillMaterialShader(QGradient::Type gradientType,
+ bool useDerivatives,
+ int viewCount);
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ };
+
+ QSGCurveFillMaterialShader::QSGCurveFillMaterialShader(QGradient::Type gradientType,
+ bool useDerivatives,
+ int viewCount)
+ {
+ QString baseName = QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shapecurve");
+
+ if (gradientType == QGradient::LinearGradient) {
+ baseName += QStringLiteral("_lg");
+ } else if (gradientType == QGradient::RadialGradient) {
+ baseName += QStringLiteral("_rg");
+ } else if (gradientType == QGradient::ConicalGradient) {
+ baseName += QStringLiteral("_cg");
+ }
+
+ if (useDerivatives)
+ baseName += QStringLiteral("_derivatives");
+
+ setShaderFileName(VertexStage, baseName + QStringLiteral(".vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, baseName + QStringLiteral(".frag.qsb"), viewCount);
+ }
+
+ void QSGCurveFillMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+ {
+ Q_UNUSED(oldMaterial);
+ const QSGCurveFillMaterial *m = static_cast<QSGCurveFillMaterial *>(newMaterial);
+ const QSGCurveFillNode *node = m->node();
+ if (binding != 1 || node->gradientType() == QGradient::NoGradient)
+ return;
+
+ const QSGGradientCacheKey cacheKey(node->fillGradient()->stops,
+ node->fillGradient()->spread);
+ QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
+ t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
+ *texture = t;
+ }
+
+ bool QSGCurveFillMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+ {
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 80);
+ const int matrixCount = qMin(state.projectionMatrixCount(), newEffect->viewCount());
+
+ int offset = 0;
+ float matrixScale = 0.0f;
+ if (state.isMatrixDirty()) {
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ memcpy(buf->data() + offset + viewIndex * 64, m.constData(), 64);
+ }
+
+ matrixScale = qSqrt(qAbs(state.determinant()));
+ memcpy(buf->data() + offset + matrixCount * 64, &matrixScale, 4);
+
+ changed = true;
+ }
+ offset += matrixCount * 64 + 4;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + offset, &opacity, 4);
+ changed = true;
+ }
+ offset += 4;
+
+ QSGCurveFillMaterial *newMaterial = static_cast<QSGCurveFillMaterial *>(newEffect);
+ QSGCurveFillMaterial *oldMaterial = static_cast<QSGCurveFillMaterial *>(oldEffect);
+
+ QSGCurveFillNode *newNode = newMaterial != nullptr ? newMaterial->node() : nullptr;
+ QSGCurveFillNode *oldNode = oldMaterial != nullptr ? oldMaterial->node() : nullptr;
+
+ if (newNode == nullptr)
+ return changed;
+
+ if (oldNode == nullptr || oldNode->debug() != newNode->debug()) {
+ float debug = newNode->debug();
+ memcpy(buf->data() + offset, &debug, 4);
+ changed = true;
+ }
+ offset += 8;
+
+ if (newNode->gradientType() == QGradient::NoGradient) {
+ Q_ASSERT(buf->size() >= offset + 16);
+
+ QVector4D newColor = QVector4D(newNode->color().redF(),
+ newNode->color().greenF(),
+ newNode->color().blueF(),
+ newNode->color().alphaF());
+ QVector4D oldColor = oldNode != nullptr
+ ? QVector4D(oldNode->color().redF(),
+ oldNode->color().greenF(),
+ oldNode->color().blueF(),
+ oldNode->color().alphaF())
+ : QVector4D{};
+
+ if (oldNode == nullptr || oldColor != newColor) {
+ memcpy(buf->data() + offset, &newColor, 16);
+ changed = true;
+ }
+
+ offset += 16;
+ } else if (newNode->gradientType() == QGradient::LinearGradient) {
+ Q_ASSERT(buf->size() >= offset + 8 + 8);
+
+ QVector2D newGradientStart = QVector2D(newNode->fillGradient()->a);
+ QVector2D oldGradientStart = oldNode != nullptr
+ ? QVector2D(oldNode->fillGradient()->a)
+ : QVector2D{};
+
+ if (newGradientStart != oldGradientStart || oldEffect == nullptr) {
+ memcpy(buf->data() + offset, &newGradientStart, 8);
+ changed = true;
+ }
+ offset += 8;
+
+ QVector2D newGradientEnd = QVector2D(newNode->fillGradient()->b);
+ QVector2D oldGradientEnd = oldNode!= nullptr
+ ? QVector2D(oldNode->fillGradient()->b)
+ : QVector2D{};
+
+ if (newGradientEnd != oldGradientEnd || oldEffect == nullptr) {
+ memcpy(buf->data() + offset, &newGradientEnd, 8);
+ changed = true;
+ }
+
+ offset += 8;
+ } else if (newNode->gradientType() == QGradient::RadialGradient) {
+ Q_ASSERT(buf->size() >= offset + 8 + 8 + 4 + 4);
+
+ QVector2D newFocalPoint = QVector2D(newNode->fillGradient()->b);
+ QVector2D oldFocalPoint = oldNode != nullptr
+ ? QVector2D(oldNode->fillGradient()->b)
+ : QVector2D{};
+ if (oldNode == nullptr || newFocalPoint != oldFocalPoint) {
+ memcpy(buf->data() + offset, &newFocalPoint, 8);
+ changed = true;
+ }
+ offset += 8;
+
+ QVector2D newCenterPoint = QVector2D(newNode->fillGradient()->a);
+ QVector2D oldCenterPoint = oldNode != nullptr
+ ? QVector2D(oldNode->fillGradient()->a)
+ : QVector2D{};
+
+ QVector2D newCenterToFocal = newCenterPoint - newFocalPoint;
+ QVector2D oldCenterToFocal = oldCenterPoint - oldFocalPoint;
+ if (oldNode == nullptr || newCenterToFocal != oldCenterToFocal) {
+ memcpy(buf->data() + offset, &newCenterToFocal, 8);
+ changed = true;
+ }
+ offset += 8;
+
+ float newCenterRadius = newNode->fillGradient()->v0;
+ float oldCenterRadius = oldNode != nullptr
+ ? oldNode->fillGradient()->v0
+ : 0.0f;
+ if (oldNode == nullptr || !qFuzzyCompare(newCenterRadius, oldCenterRadius)) {
+ memcpy(buf->data() + offset, &newCenterRadius, 4);
+ changed = true;
+ }
+ offset += 4;
+
+ float newFocalRadius = newNode->fillGradient()->v1;
+ float oldFocalRadius = oldNode != nullptr
+ ? oldNode->fillGradient()->v1
+ : 0.0f;
+ if (oldNode == nullptr || !qFuzzyCompare(newFocalRadius, oldFocalRadius)) {
+ memcpy(buf->data() + offset, &newFocalRadius, 4);
+ changed = true;
+ }
+ offset += 4;
+
+ } else if (newNode->gradientType() == QGradient::ConicalGradient) {
+ Q_ASSERT(buf->size() >= offset + 8 + 4);
+
+ QVector2D newFocalPoint = QVector2D(newNode->fillGradient()->a);
+ QVector2D oldFocalPoint = oldNode != nullptr
+ ? QVector2D(oldNode->fillGradient()->a)
+ : QVector2D{};
+ if (oldNode == nullptr || newFocalPoint != oldFocalPoint) {
+ memcpy(buf->data() + offset, &newFocalPoint, 8);
+ changed = true;
+ }
+ offset += 8;
+
+ float newAngle = newNode->fillGradient()->v0;
+ float oldAngle = oldNode != nullptr
+ ? oldNode->fillGradient()->v0
+ : 0.0f;
+ if (oldNode == nullptr || !qFuzzyCompare(newAngle, oldAngle)) {
+ newAngle = -qDegreesToRadians(newAngle);
+ memcpy(buf->data() + offset, &newAngle, 4);
+ changed = true;
+ }
+ offset += 4;
+ }
+
+ return changed;
+ }
+
+}
+
+QSGCurveFillMaterial::QSGCurveFillMaterial(QSGCurveFillNode *node)
+ : m_node(node)
+{
+ setFlag(Blending, true);
+ setFlag(RequiresDeterminant, true);
+}
+
+int QSGCurveFillMaterial::compare(const QSGMaterial *other) const
+{
+ if (other->type() != type())
+ return (type() - other->type());
+
+ const QSGCurveFillMaterial *otherMaterial =
+ static_cast<const QSGCurveFillMaterial *>(other);
+
+ QSGCurveFillNode *a = node();
+ QSGCurveFillNode *b = otherMaterial->node();
+ if (a == b)
+ return 0;
+
+ if (a->gradientType() == QGradient::NoGradient) {
+ if (int d = a->color().red() - b->color().red())
+ return d;
+ if (int d = a->color().green() - b->color().green())
+ return d;
+ if (int d = a->color().blue() - b->color().blue())
+ return d;
+ if (int d = a->color().alpha() - b->color().alpha())
+ return d;
+ } else {
+ const QSGGradientCache::GradientDesc &ga = *a->fillGradient();
+ const QSGGradientCache::GradientDesc &gb = *b->fillGradient();
+
+ if (int d = ga.a.x() - gb.a.x())
+ return d;
+ if (int d = ga.a.y() - gb.a.y())
+ return d;
+ if (int d = ga.b.x() - gb.b.x())
+ return d;
+ if (int d = ga.b.y() - gb.b.y())
+ return d;
+
+ if (int d = ga.v0 - gb.v0)
+ return d;
+ if (int d = ga.v1 - gb.v1)
+ return d;
+
+ if (int d = ga.spread - gb.spread)
+ return d;
+
+ if (int d = ga.stops.size() - gb.stops.size())
+ return d;
+
+ for (int i = 0; i < ga.stops.size(); ++i) {
+ if (int d = ga.stops[i].first - gb.stops[i].first)
+ return d;
+ if (int d = ga.stops[i].second.rgba() - gb.stops[i].second.rgba())
+ return d;
+ }
+ }
+
+ return 0;
+}
+
+QSGMaterialType *QSGCurveFillMaterial::type() const
+{
+ static QSGMaterialType type[4];
+ uint index = node()->gradientType();
+ Q_ASSERT((index & ~3) == 0); // Only two first bits for gradient type
+
+ return &type[index];
+}
+
+QSGMaterialShader *QSGCurveFillMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
+{
+ return new QSGCurveFillMaterialShader(node()->gradientType(),
+ renderMode == QSGRendererInterface::RenderMode3D,
+ viewCount());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcurvefillnode_p.h b/src/quick/scenegraph/qsgcurvefillnode_p.h
new file mode 100644
index 0000000000..c90c5b8d38
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurvefillnode_p.h
@@ -0,0 +1,198 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGCURVEFILLNODE_P_H
+#define QSGCURVEFILLNODE_P_H
+
+#include <QtGui/qbrush.h>
+
+#include <QtQuick/qtquickexports.h>
+#include <QtQuick/private/qsggradientcache_p.h>
+#include <QtQuick/qsgnode.h>
+
+#include "qsgcurveabstractnode_p.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGCurveFillNode : public QSGCurveAbstractNode
+{
+public:
+ QSGCurveFillNode();
+
+ void setColor(QColor col) override
+ {
+ if (m_color == col)
+ return;
+ m_color = col;
+ updateMaterial();
+ }
+
+ QColor color() const
+ {
+ return m_color;
+ }
+
+ void setFillGradient(const QSGGradientCache::GradientDesc &fillGradient)
+ {
+ m_fillGradient = fillGradient;
+ }
+
+ const QSGGradientCache::GradientDesc *fillGradient() const
+ {
+ return &m_fillGradient;
+ }
+
+ void setGradientType(QGradient::Type type)
+ {
+ if (m_gradientType != type) {
+ m_gradientType = type;
+ updateMaterial();
+ }
+ }
+
+ QGradient::Type gradientType() const
+ {
+ return m_gradientType;
+ }
+
+ float debug() const
+ {
+ return m_debug;
+ }
+
+ void setDebug(float newDebug)
+ {
+ m_debug = newDebug;
+ }
+
+ void appendTriangle(const std::array<QVector2D, 3> &v, // triangle vertices
+ const std::array<QVector2D, 3> &n, // vertex normals
+ std::function<QVector3D(QVector2D)> uvForPoint
+ )
+ {
+ QVector3D uv1 = uvForPoint(v[0]);
+ QVector3D uv2 = uvForPoint(v[1]);
+ QVector3D uv3 = uvForPoint(v[2]);
+
+ QVector2D duvdx = QVector2D(uvForPoint(v[0] + QVector2D(1, 0))) - QVector2D(uv1);
+ QVector2D duvdy = QVector2D(uvForPoint(v[0] + QVector2D(0, 1))) - QVector2D(uv1);
+
+ m_uncookedIndexes.append(m_uncookedVertexes.size());
+ m_uncookedVertexes.append( { v[0].x(), v[0].y(),
+ uv1.x(), uv1.y(), uv1.z(),
+ duvdx.x(), duvdx.y(),
+ duvdy.x(), duvdy.y(),
+ n[0].x(), n[0].y()
+ });
+
+ m_uncookedIndexes.append(m_uncookedVertexes.size());
+ m_uncookedVertexes.append( { v[1].x(), v[1].y(),
+ uv2.x(), uv2.y(), uv2.z(),
+ duvdx.x(), duvdx.y(),
+ duvdy.x(), duvdy.y(),
+ n[1].x(), n[1].y()
+ });
+
+ m_uncookedIndexes.append(m_uncookedVertexes.size());
+ m_uncookedVertexes.append( { v[2].x(), v[2].y(),
+ uv3.x(), uv3.y(), uv3.z(),
+ duvdx.x(), duvdx.y(),
+ duvdy.x(), duvdy.y(),
+ n[2].x(), n[2].y()
+ });
+ }
+
+ void appendTriangle(const QVector2D &v1,
+ const QVector2D &v2,
+ const QVector2D &v3,
+ const QVector3D &uv1,
+ const QVector3D &uv2,
+ const QVector3D &uv3,
+ const QVector2D &n1,
+ const QVector2D &n2,
+ const QVector2D &n3,
+ const QVector2D &duvdx,
+ const QVector2D &duvdy)
+ {
+ m_uncookedIndexes.append(m_uncookedVertexes.size());
+ m_uncookedVertexes.append( { v1.x(), v1.y(),
+ uv1.x(), uv1.y(), uv1.z(),
+ duvdx.x(), duvdx.y(),
+ duvdy.x(), duvdy.y(),
+ n1.x(), n1.y()
+ });
+
+ m_uncookedIndexes.append(m_uncookedVertexes.size());
+ m_uncookedVertexes.append( { v2.x(), v2.y(),
+ uv2.x(), uv2.y(), uv2.z(),
+ duvdx.x(), duvdx.y(),
+ duvdy.x(), duvdy.y(),
+ n2.x(), n2.y()
+ });
+
+ m_uncookedIndexes.append(m_uncookedVertexes.size());
+ m_uncookedVertexes.append( { v3.x(), v3.y(),
+ uv3.x(), uv3.y(), uv3.z(),
+ duvdx.x(), duvdx.y(),
+ duvdy.x(), duvdy.y(),
+ n3.x(), n3.y()
+ });
+ }
+
+ void appendTriangle(const QVector2D &v1,
+ const QVector2D &v2,
+ const QVector2D &v3,
+ std::function<QVector3D(QVector2D)> uvForPoint)
+ {
+ appendTriangle({v1, v2, v3}, {}, uvForPoint);
+ }
+
+ QVector<quint32> uncookedIndexes() const
+ {
+ return m_uncookedIndexes;
+ }
+
+ void cookGeometry() override;
+
+ void reserve(qsizetype size)
+ {
+ m_uncookedIndexes.reserve(size);
+ m_uncookedVertexes.reserve(size);
+ }
+
+private:
+ struct CurveNodeVertex
+ {
+ float x, y, u, v, w;
+ float dudx, dvdx, dudy, dvdy; // Size of pixel in curve space (must be same for all vertices in triangle)
+ float nx, ny; // normal vector describing the direction to shift the vertex for AA
+ };
+
+ void updateMaterial();
+ static const QSGGeometry::AttributeSet &attributes();
+
+ QColor m_color = Qt::white;
+ float m_debug = 0.0f;
+ QSGGradientCache::GradientDesc m_fillGradient;
+ QGradient::Type m_gradientType = QGradient::NoGradient;
+
+ QScopedPointer<QSGMaterial> m_material;
+
+ QVector<CurveNodeVertex> m_uncookedVertexes;
+ QVector<quint32> m_uncookedIndexes;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGCURVEFILLNODE_P_H
diff --git a/src/quick/scenegraph/qsgcurvefillnode_p_p.h b/src/quick/scenegraph/qsgcurvefillnode_p_p.h
new file mode 100644
index 0000000000..f2a6535c6f
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurvefillnode_p_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGCURVEFILLNODE_P_P_H
+#define QSGCURVEFILLNODE_P_P_H
+
+#include <QtQuick/qtquickexports.h>
+#include <QtQuick/qsgmaterial.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QSGCurveFillNode;
+class Q_QUICK_EXPORT QSGCurveFillMaterial : public QSGMaterial
+{
+public:
+ QSGCurveFillMaterial(QSGCurveFillNode *node);
+ int compare(const QSGMaterial *other) const override;
+
+ QSGCurveFillNode *node() const
+ {
+ return m_node;
+ }
+
+private:
+ QSGMaterialType *type() const override;
+ QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
+
+ QSGCurveFillNode *m_node;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGCURVEFILLNODE_P_P_H
diff --git a/src/quick/scenegraph/qsgcurveglyphatlas.cpp b/src/quick/scenegraph/qsgcurveglyphatlas.cpp
new file mode 100644
index 0000000000..410ce2dd26
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurveglyphatlas.cpp
@@ -0,0 +1,142 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgcurveglyphatlas_p.h"
+#include "qsgcurvefillnode_p.h"
+#include "qsgcurvestrokenode_p.h"
+#include "qsgcurveprocessor_p.h"
+#include "util/qquadpath_p.h"
+
+#include <QtGui/qrawfont.h>
+#include <QtGui/qpainterpath.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGCurveGlyphAtlas::QSGCurveGlyphAtlas(const QRawFont &font)
+ : m_font(font)
+{
+ // The font size used for the curve atlas currently affects the outlines, since we don't
+ // really support cosmetic outlines. Therefore we need to pick one which gives large enough
+ // triangles relative to glyph size that we can reuse the same triangles for any font size.
+ // When experimenting, 10 works for all font sizes we tested, so we currently default to this
+ // but allow overriding it.
+ static int curveGlyphAtlasFontSize = qEnvironmentVariableIntValue("QSGCURVEGLYPHATLAS_FONT_SIZE");
+ m_font.setPixelSize(curveGlyphAtlasFontSize > 0 ? qreal(curveGlyphAtlasFontSize) : 10.0);
+}
+
+QSGCurveGlyphAtlas::~QSGCurveGlyphAtlas()
+{
+}
+
+void QSGCurveGlyphAtlas::populate(const QList<glyph_t> &glyphs)
+{
+ for (glyph_t glyphIndex : glyphs) {
+ if (!m_glyphs.contains(glyphIndex)) {
+ QPainterPath path = m_font.pathForGlyph(glyphIndex);
+ QQuadPath quadPath = QQuadPath::fromPainterPath(path);
+ quadPath.setFillRule(Qt::WindingFill);
+
+ Glyph glyph;
+
+ QSGCurveProcessor::processStroke(quadPath, 2, 2, Qt::MiterJoin, Qt::FlatCap,
+ [&glyph](const std::array<QVector2D, 3> &s,
+ const std::array<QVector2D, 3> &p,
+ const std::array<QVector2D, 3> &n,
+ bool isLine) {
+ glyph.strokeVertices.append(s.at(0));
+ glyph.strokeVertices.append(s.at(1));
+ glyph.strokeVertices.append(s.at(2));
+
+ glyph.strokeUvs.append(p.at(0));
+ glyph.strokeUvs.append(p.at(1));
+ glyph.strokeUvs.append(p.at(2));
+
+ glyph.strokeNormals.append(n.at(0));
+ glyph.strokeNormals.append(n.at(1));
+ glyph.strokeNormals.append(n.at(2));
+
+ glyph.strokeElementIsLine.append(isLine);
+ });
+
+ quadPath = quadPath.subPathsClosed();
+ quadPath.addCurvatureData(); // ### Since the inside of glyphs is defined by order of
+ // vertices, this step could be simplified
+ QSGCurveProcessor::solveOverlaps(quadPath);
+
+ QSGCurveProcessor::processFill(quadPath,
+ Qt::WindingFill,
+ [&glyph](const std::array<QVector2D, 3> &v,
+ const std::array<QVector2D, 3> &n,
+ QSGCurveProcessor::uvForPointCallback uvForPoint)
+ {
+ glyph.vertices.append(v.at(0));
+ glyph.vertices.append(v.at(1));
+ glyph.vertices.append(v.at(2));
+
+ QVector3D uv1 = uvForPoint(v.at(0));
+ glyph.uvs.append(uv1);
+ glyph.uvs.append(uvForPoint(v.at(1)));
+ glyph.uvs.append(uvForPoint(v.at(2)));
+
+ glyph.normals.append(n.at(0));
+ glyph.normals.append(n.at(1));
+ glyph.normals.append(n.at(2));
+
+ glyph.duvdx.append(QVector2D(uvForPoint(v.at(0) + QVector2D(1, 0))) - QVector2D(uv1));
+ glyph.duvdy.append(QVector2D(uvForPoint(v.at(0) + QVector2D(0, 1))) - QVector2D(uv1));
+ });
+
+ m_glyphs.insert(glyphIndex, glyph);
+ }
+ }
+}
+
+void QSGCurveGlyphAtlas::addStroke(QSGCurveStrokeNode *node,
+ glyph_t glyphIndex,
+ const QPointF &position) const
+{
+ const Glyph &glyph = m_glyphs[glyphIndex];
+
+ const QVector2D v(position);
+ for (qsizetype i = glyph.strokeElementIsLine.size() - 1; i >= 0; --i) {
+ QVector2D v1 = glyph.strokeVertices.at(i * 3 + 0) + v;
+ QVector2D v2 = glyph.strokeVertices.at(i * 3 + 1) + v;
+ QVector2D v3 = glyph.strokeVertices.at(i * 3 + 2) + v;
+ if (glyph.strokeElementIsLine.at(i)) {
+ node->appendTriangle({ v1, v2, v3 },
+ std::array<QVector2D, 2>({ glyph.strokeUvs.at(i * 3 + 0) + v, glyph.strokeUvs.at(i * 3 + 2) + v }),
+ { glyph.strokeNormals.at(i * 3 + 0), glyph.strokeNormals.at(i * 3 + 1), glyph.strokeNormals.at(i * 3 + 2) });
+ } else {
+ node->appendTriangle({ v1, v2, v3 },
+ { glyph.strokeUvs.at(i * 3 + 0) + v, glyph.strokeUvs.at(i * 3 + 1) + v, glyph.strokeUvs.at(i * 3 + 2) + v },
+ { glyph.strokeNormals.at(i * 3 + 0), glyph.strokeNormals.at(i * 3 + 1), glyph.strokeNormals.at(i * 3 + 2) });
+
+ }
+ }
+}
+
+void QSGCurveGlyphAtlas::addGlyph(QSGCurveFillNode *node,
+ glyph_t glyphIndex,
+ const QPointF &position,
+ qreal pixelSize) const
+{
+ const Glyph &glyph = m_glyphs[glyphIndex];
+
+ const float scaleFactor = pixelSize / m_font.pixelSize();
+ const QVector2D v(position);
+ for (qsizetype i = 0; i < glyph.vertices.size() / 3; ++i) {
+ node->appendTriangle(scaleFactor * glyph.vertices.at(i * 3 + 0) + v,
+ scaleFactor * glyph.vertices.at(i * 3 + 1) + v,
+ scaleFactor * glyph.vertices.at(i * 3 + 2) + v,
+ glyph.uvs.at(i * 3 + 0),
+ glyph.uvs.at(i * 3 + 1),
+ glyph.uvs.at(i * 3 + 2),
+ glyph.normals.at(i * 3 + 0),
+ glyph.normals.at(i * 3 + 1),
+ glyph.normals.at(i * 3 + 2),
+ glyph.duvdx.at(i) / scaleFactor,
+ glyph.duvdy.at(i) / scaleFactor);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcurveglyphatlas_p.h b/src/quick/scenegraph/qsgcurveglyphatlas_p.h
new file mode 100644
index 0000000000..c82f1921f9
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurveglyphatlas_p.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGCURVEGLYPHATLAS_P_H
+#define QSGCURVEGLYPHATLAS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qrawfont.h>
+#include <QtGui/private/qtextengine_p.h>
+#include <QtQuick/qtquickexports.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGCurveFillNode;
+class QSGCurveStrokeNode;
+
+class Q_QUICK_EXPORT QSGCurveGlyphAtlas
+{
+public:
+ QSGCurveGlyphAtlas(const QRawFont &font);
+ virtual ~QSGCurveGlyphAtlas();
+
+ void populate(const QList<glyph_t> &glyphs);
+ void addGlyph(QSGCurveFillNode *node,
+ glyph_t glyph,
+ const QPointF &position,
+ qreal pixelSize) const;
+ void addStroke(QSGCurveStrokeNode *node,
+ glyph_t glyph,
+ const QPointF &position) const;
+
+ qreal fontSize() const
+ {
+ return m_font.pixelSize();
+ }
+
+private:
+ struct Glyph
+ {
+ QList<QVector2D> vertices;
+ QList<QVector3D> uvs;
+ QList<QVector2D> normals;
+ QList<QVector2D> duvdx;
+ QList<QVector2D> duvdy;
+
+ QList<QVector2D> strokeVertices;
+ QList<QVector2D> strokeUvs;
+ QList<QVector2D> strokeNormals;
+ QList<bool> strokeElementIsLine;
+ };
+
+ QHash<glyph_t, Glyph> m_glyphs;
+ QRawFont m_font;
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QSGCURVEGLYPHATLAS_P_H
diff --git a/src/quick/scenegraph/qsgcurveglyphnode.cpp b/src/quick/scenegraph/qsgcurveglyphnode.cpp
new file mode 100644
index 0000000000..c5252083d3
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurveglyphnode.cpp
@@ -0,0 +1,164 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgcurveglyphnode_p.h"
+#include "qsgcurveglyphatlas_p.h"
+#include "qsgcurvefillnode_p.h"
+#include "qsgcurvestrokenode_p.h"
+
+#include <private/qsgcurveabstractnode_p.h>
+#include <private/qsgcontext_p.h>
+#include <private/qsgtexturematerial_p.h>
+
+#include <private/qrawfont_p.h>
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGCurveGlyphNode::QSGCurveGlyphNode(QSGRenderContext *context)
+ : m_context(context)
+ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
+ , m_dirtyGeometry(false)
+{
+ setFlag(UsePreprocess);
+ setFlag(OwnsMaterial);
+
+ // #### To avoid asserts: we should probably merge this with QSGCurveFillNode
+ setGeometry(&m_geometry);
+ setMaterial(new QSGTextureMaterial);
+}
+
+QSGCurveGlyphNode::~QSGCurveGlyphNode()
+{
+}
+
+void QSGCurveGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
+{
+ Q_UNUSED(mode);
+}
+
+void QSGCurveGlyphNode::setColor(const QColor &color)
+{
+ m_color = color;
+ if (m_glyphNode != nullptr)
+ m_glyphNode->setColor(color);
+}
+
+void QSGCurveGlyphNode::setStyleColor(const QColor &styleColor)
+{
+ m_styleColor = styleColor;
+ if (m_styleNode != nullptr)
+ m_styleNode->setColor(styleColor);
+}
+
+void QSGCurveGlyphNode::setStyle(QQuickText::TextStyle style)
+{
+ if (m_style != style) {
+ m_style = style;
+ m_dirtyGeometry = true;
+ update();
+ }
+}
+
+void QSGCurveGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
+{
+ m_glyphs = glyphs;
+
+ QRawFont font = glyphs.rawFont();
+ m_fontSize = font.pixelSize();
+ m_position = QPointF(position.x(), position.y() - font.ascent());
+
+
+ m_dirtyGeometry = true;
+
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QString::number(glyphs.glyphIndexes().count())
+ + QStringLiteral(" curve glyphs: ")
+ + m_glyphs.rawFont().familyName()
+ + QStringLiteral(" ")
+ + QString::number(m_glyphs.rawFont().pixelSize()));
+#endif
+}
+
+void QSGCurveGlyphNode::update()
+{
+ markDirty(DirtyGeometry);
+}
+
+void QSGCurveGlyphNode::preprocess()
+{
+ if (m_dirtyGeometry)
+ updateGeometry();
+}
+
+void QSGCurveGlyphNode::updateGeometry()
+{
+ delete m_glyphNode;
+ m_glyphNode = nullptr;
+
+ delete m_styleNode;
+ m_styleNode = nullptr;
+
+ QSGCurveGlyphAtlas *curveGlyphAtlas = m_context->curveGlyphAtlas(m_glyphs.rawFont());
+ curveGlyphAtlas->populate(m_glyphs.glyphIndexes());
+
+ m_glyphNode = new QSGCurveFillNode;
+ m_glyphNode->setColor(m_color);
+
+ QPointF offset;
+
+ float fontScale = float(m_fontSize / curveGlyphAtlas->fontSize());
+ QSGCurveFillNode *raisedSunkenStyleNode = nullptr;
+ QSGCurveStrokeNode *outlineNode = nullptr;
+ if (m_style == QQuickText::Raised || m_style == QQuickText::Sunken) {
+ raisedSunkenStyleNode = new QSGCurveFillNode;
+ raisedSunkenStyleNode ->setColor(m_styleColor);
+
+ offset = m_style == QQuickText::Raised ? QPointF(0.0f, 1.0f) : QPointF(0.0f, -1.0f);
+ m_styleNode = raisedSunkenStyleNode;
+ } else if (m_style == QQuickText::Outline) {
+ outlineNode = new QSGCurveStrokeNode;
+ outlineNode->setColor(m_styleColor);
+ outlineNode->setStrokeWidth(2 / fontScale);
+ outlineNode->setLocalScale(fontScale);
+
+ m_styleNode = outlineNode;
+ }
+
+ const QVector<quint32> indexes = m_glyphs.glyphIndexes();
+ const QVector<QPointF> positions = m_glyphs.positions();
+ for (qsizetype i = 0; i < indexes.size(); ++i) {
+ if (i == 0)
+ m_baseLine = positions.at(i);
+ curveGlyphAtlas->addGlyph(m_glyphNode,
+ indexes.at(i),
+ m_position + positions.at(i),
+ m_fontSize);
+ if (raisedSunkenStyleNode != nullptr) {
+ curveGlyphAtlas->addGlyph(raisedSunkenStyleNode,
+ indexes.at(i),
+ m_position + positions.at(i) + offset,
+ m_fontSize);
+ }
+ if (outlineNode != nullptr) {
+ // Since the stroke node will scale everything by fontScale internally (the
+ // shader does not support pre-transforming the vertices), we have to also first
+ // do the inverse scale on the glyph position to get the correct position.
+ curveGlyphAtlas->addStroke(outlineNode,
+ indexes.at(i),
+ (m_position + positions.at(i)) / fontScale);
+ }
+ }
+
+ if (m_styleNode != nullptr) {
+ m_styleNode->cookGeometry();
+ appendChildNode(m_styleNode);
+ }
+
+ m_glyphNode->cookGeometry();
+ appendChildNode(m_glyphNode);
+
+ m_dirtyGeometry = false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcurveglyphnode_p.h b/src/quick/scenegraph/qsgcurveglyphnode_p.h
new file mode 100644
index 0000000000..cb634112d0
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurveglyphnode_p.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGCURVEGLYPHNODE_P_H
+#define QSGCURVEGLYPHNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtquickexports.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgbasicglyphnode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGCurveGlyphAtlas;
+class QSGCurveFillNode;
+class QSGCurveAbstractNode;
+
+class Q_QUICK_EXPORT QSGCurveGlyphNode : public QSGGlyphNode
+{
+public:
+ QSGCurveGlyphNode(QSGRenderContext *context);
+ ~QSGCurveGlyphNode();
+ void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override;
+ void update() override;
+ void preprocess() override;
+ void setPreferredAntialiasingMode(AntialiasingMode) override;
+ void updateGeometry();
+ void setColor(const QColor &color) override;
+ void setStyle(QQuickText::TextStyle style) override;
+
+ void setStyleColor(const QColor &color) override;
+ QPointF baseLine() const override { return m_baseLine; }
+
+private:
+ QSGRenderContext *m_context;
+ QSGGeometry m_geometry;
+ QColor m_color = Qt::black;
+
+ struct GlyphInfo {
+ QVector<quint32> indexes;
+ QVector<QPointF> positions;
+ };
+
+ uint m_dirtyGeometry: 1;
+ qreal m_fontSize = 0.0f;
+ QGlyphRun m_glyphs;
+ QQuickText::TextStyle m_style;
+ QColor m_styleColor;
+ QPointF m_baseLine;
+ QPointF m_position;
+
+ QSGCurveFillNode *m_glyphNode = nullptr;
+ QSGCurveAbstractNode *m_styleNode = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/qsgcurveprocessor.cpp b/src/quick/scenegraph/qsgcurveprocessor.cpp
new file mode 100644
index 0000000000..f2e95d691c
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurveprocessor.cpp
@@ -0,0 +1,1887 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgcurveprocessor_p.h"
+
+#include <QtGui/private/qtriangulator_p.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcSGCurveProcessor, "qt.quick.curveprocessor");
+Q_LOGGING_CATEGORY(lcSGCurveIntersectionSolver, "qt.quick.curveprocessor.intersections");
+
+namespace {
+// Input coordinate space is pre-mapped so that (0, 0) maps to [0, 0] in uv space.
+// v1 maps to [1,0], v2 maps to [0,1]. p is the point to be mapped to uv in this space (i.e. vector from p0)
+static inline QVector2D uvForPoint(QVector2D v1, QVector2D v2, QVector2D p)
+{
+ double divisor = v1.x() * v2.y() - v2.x() * v1.y();
+
+ float u = (p.x() * v2.y() - p.y() * v2.x()) / divisor;
+ float v = (p.y() * v1.x() - p.x() * v1.y()) / divisor;
+
+ return {u, v};
+}
+
+// Find uv coordinates for the point p, for a quadratic curve from p0 to p2 with control point p1
+// also works for a line from p0 to p2, where p1 is on the inside of the path relative to the line
+static inline QVector2D curveUv(QVector2D p0, QVector2D p1, QVector2D p2, QVector2D p)
+{
+ QVector2D v1 = 2 * (p1 - p0);
+ QVector2D v2 = p2 - v1 - p0;
+ return uvForPoint(v1, v2, p - p0);
+}
+
+static QVector3D elementUvForPoint(const QQuadPath::Element& e, QVector2D p)
+{
+ auto uv = curveUv(e.startPoint(), e.referencePoint(), e.endPoint(), p);
+ if (e.isLine())
+ return { uv.x(), uv.y(), 0.0f };
+ else
+ return { uv.x(), uv.y(), e.isConvex() ? -1.0f : 1.0f };
+}
+
+static inline QVector2D calcNormalVector(QVector2D a, QVector2D b)
+{
+ auto v = b - a;
+ return {v.y(), -v.x()};
+}
+
+// The sign of the return value indicates which side of the line defined by a and n the point p falls
+static inline float testSideOfLineByNormal(QVector2D a, QVector2D n, QVector2D p)
+{
+ float dot = QVector2D::dotProduct(p - a, n);
+ return dot;
+};
+
+static inline float determinant(const QVector2D &p1, const QVector2D &p2, const QVector2D &p3)
+{
+ return p1.x() * (p2.y() - p3.y())
+ + p2.x() * (p3.y() - p1.y())
+ + p3.x() * (p1.y() - p2.y());
+}
+
+/*
+ Clever triangle overlap algorithm. Stack Overflow says:
+
+ You can prove that the two triangles do not collide by finding an edge (out of the total 6
+ edges that make up the two triangles) that acts as a separating line where all the vertices
+ of one triangle lie on one side and the vertices of the other triangle lie on the other side.
+ If you can find such an edge then it means that the triangles do not intersect otherwise the
+ triangles are colliding.
+*/
+using TrianglePoints = std::array<QVector2D, 3>;
+using LinePoints = std::array<QVector2D, 2>;
+
+// The sign of the determinant tells the winding order: positive means counter-clockwise
+
+static inline double determinant(const TrianglePoints &p)
+{
+ return determinant(p[0], p[1], p[2]);
+}
+
+// Fix the triangle so that the determinant is positive
+static void fixWinding(TrianglePoints &p)
+{
+ double det = determinant(p);
+ if (det < 0.0) {
+ qSwap(p[0], p[1]);
+ }
+}
+
+// Return true if the determinant is negative, i.e. if the winding order is opposite of the triangle p1,p2,p3.
+// This means that p is strictly on the other side of p1-p2 relative to p3 [where p1,p2,p3 is a triangle with
+// a positive determinant].
+bool checkEdge(QVector2D &p1, QVector2D &p2, QVector2D &p, float epsilon)
+{
+ return determinant(p1, p2, p) <= epsilon;
+}
+
+// Check if lines l1 and l2 are intersecting and return the respective value. Solutions are stored to
+// the optional pointer solution.
+bool lineIntersection(const LinePoints &l1, const LinePoints &l2, QList<QPair<float, float>> *solution = nullptr)
+{
+ constexpr double eps2 = 1e-5; // Epsilon for parameter space t1-t2
+
+ // see https://www.wolframalpha.com/input?i=solve%28A+%2B+t+*+B+%3D+C+%2B+s*D%3B+E+%2B+t+*+F+%3D+G+%2B+s+*+H+for+s+and+t%29
+ const float A = l1[0].x();
+ const float B = l1[1].x() - l1[0].x();
+ const float C = l2[0].x();
+ const float D = l2[1].x() - l2[0].x();
+ const float E = l1[0].y();
+ const float F = l1[1].y() - l1[0].y();
+ const float G = l2[0].y();
+ const float H = l2[1].y() - l2[0].y();
+
+ float det = D * F - B * H;
+
+ if (det == 0)
+ return false;
+
+ float s = (F * (A - C) - B * (E - G)) / det;
+ float t = (H * (A - C) - D * (E - G)) / det;
+
+ // Intersections at 0 count. Intersections at 1 do not.
+ bool intersecting = (s >= 0 && s <= 1. - eps2 && t >= 0 && t <= 1. - eps2);
+
+ if (solution && intersecting)
+ solution->append(QPair<float, float>(t, s));
+
+ return intersecting;
+}
+
+
+bool checkTriangleOverlap(TrianglePoints &triangle1, TrianglePoints &triangle2, float epsilon = 1.0/32)
+{
+ // See if there is an edge of triangle1 such that all vertices in triangle2 are on the opposite side
+ fixWinding(triangle1);
+ for (int i = 0; i < 3; i++) {
+ int ni = (i + 1) % 3;
+ if (checkEdge(triangle1[i], triangle1[ni], triangle2[0], epsilon) &&
+ checkEdge(triangle1[i], triangle1[ni], triangle2[1], epsilon) &&
+ checkEdge(triangle1[i], triangle1[ni], triangle2[2], epsilon))
+ return false;
+ }
+
+ // See if there is an edge of triangle2 such that all vertices in triangle1 are on the opposite side
+ fixWinding(triangle2);
+ for (int i = 0; i < 3; i++) {
+ int ni = (i + 1) % 3;
+
+ if (checkEdge(triangle2[i], triangle2[ni], triangle1[0], epsilon) &&
+ checkEdge(triangle2[i], triangle2[ni], triangle1[1], epsilon) &&
+ checkEdge(triangle2[i], triangle2[ni], triangle1[2], epsilon))
+ return false;
+ }
+
+ return true;
+}
+
+bool checkLineTriangleOverlap(TrianglePoints &triangle, LinePoints &line, float epsilon = 1.0/32)
+{
+ // See if all vertices of the triangle are on the same side of the line
+ bool s1 = determinant(line[0], line[1], triangle[0]) < 0;
+ auto s2 = determinant(line[0], line[1], triangle[1]) < 0;
+ auto s3 = determinant(line[0], line[1], triangle[2]) < 0;
+ // If all determinants have the same sign, then there is no overlap
+ if (s1 == s2 && s2 == s3) {
+ return false;
+ }
+ // See if there is an edge of triangle1 such that both vertices in line are on the opposite side
+ fixWinding(triangle);
+ for (int i = 0; i < 3; i++) {
+ int ni = (i + 1) % 3;
+ if (checkEdge(triangle[i], triangle[ni], line[0], epsilon) &&
+ checkEdge(triangle[i], triangle[ni], line[1], epsilon))
+ return false;
+ }
+
+ return true;
+}
+
+static bool isOverlap(const QQuadPath &path, int e1, int e2)
+{
+ const QQuadPath::Element &element1 = path.elementAt(e1);
+ const QQuadPath::Element &element2 = path.elementAt(e2);
+
+ if (element1.isLine()) {
+ LinePoints line1{ element1.startPoint(), element1.endPoint() };
+ if (element2.isLine()) {
+ LinePoints line2{ element2.startPoint(), element2.endPoint() };
+ return lineIntersection(line1, line2);
+ } else {
+ TrianglePoints t2{ element2.startPoint(), element2.controlPoint(), element2.endPoint() };
+ return checkLineTriangleOverlap(t2, line1);
+ }
+ } else {
+ TrianglePoints t1{ element1.startPoint(), element1.controlPoint(), element1.endPoint() };
+ if (element2.isLine()) {
+ LinePoints line{ element2.startPoint(), element2.endPoint() };
+ return checkLineTriangleOverlap(t1, line);
+ } else {
+ TrianglePoints t2{ element2.startPoint(), element2.controlPoint(), element2.endPoint() };
+ return checkTriangleOverlap(t1, t2);
+ }
+ }
+
+ return false;
+}
+
+static float angleBetween(const QVector2D v1, const QVector2D v2)
+{
+ float dot = v1.x() * v2.x() + v1.y() * v2.y();
+ float cross = v1.x() * v2.y() - v1.y() * v2.x();
+ //TODO: Optimization: Maybe we don't need the atan2 here.
+ return atan2(cross, dot);
+}
+
+static bool isIntersecting(const TrianglePoints &t1, const TrianglePoints &t2, QList<QPair<float, float>> *solutions = nullptr)
+{
+ constexpr double eps = 1e-5; // Epsilon for coordinate space x-y
+ constexpr double eps2 = 1e-5; // Epsilon for parameter space t1-t2
+ constexpr int maxIterations = 7; // Maximum iterations allowed for Newton
+
+ // Convert to double to get better accuracy.
+ QPointF td1[3] = { t1[0].toPointF(), t1[1].toPointF(), t1[2].toPointF() };
+ QPointF td2[3] = { t2[0].toPointF(), t2[1].toPointF(), t2[2].toPointF() };
+
+ // F = P1(t1) - P2(t2) where P1 and P2 are bezier curve functions.
+ // F = (0, 0) at the intersection.
+ // t is the vector of bezier curve parameters for curves P1 and P2
+ auto F = [=](QPointF t) { return
+ td1[0] * (1 - t.x()) * (1. - t.x()) + 2 * td1[1] * (1. - t.x()) * t.x() + td1[2] * t.x() * t.x() -
+ td2[0] * (1 - t.y()) * (1. - t.y()) - 2 * td2[1] * (1. - t.y()) * t.y() - td2[2] * t.y() * t.y();};
+
+ // J is the Jacobi Matrix dF/dt where F and t are both vectors of dimension 2.
+ // Storing in a QLineF for simplicity.
+ auto J = [=](QPointF t) { return QLineF(
+ td1[0].x() * (-2 * (1-t.x())) + 2 * td1[1].x() * (1 - 2 * t.x()) + td1[2].x() * 2 * t.x(),
+ -td2[0].x() * (-2 * (1-t.y())) - 2 * td2[1].x() * (1 - 2 * t.y()) - td2[2].x() * 2 * t.y(),
+ td1[0].y() * (-2 * (1-t.x())) + 2 * td1[1].y() * (1 - 2 * t.x()) + td1[2].y() * 2 * t.x(),
+ -td2[0].y() * (-2 * (1-t.y())) - 2 * td2[1].y() * (1 - 2 * t.y()) - td2[2].y() * 2 * t.y());};
+
+ // solve the equation A(as 2x2 matrix)*x = b. Returns x.
+ auto solve = [](QLineF A, QPointF b) {
+ // invert A
+ const double det = A.x1() * A.y2() - A.y1() * A.x2();
+ QLineF Ainv(A.y2() / det, -A.y1() / det, -A.x2() / det, A.x1() / det);
+ // return A^-1 * b
+ return QPointF(Ainv.x1() * b.x() + Ainv.y1() * b.y(),
+ Ainv.x2() * b.x() + Ainv.y2() * b.y());
+ };
+
+#ifdef INTERSECTION_EXTRA_DEBUG
+ qCDebug(lcSGCurveIntersectionSolver) << "Checking" << t1[0] << t1[1] << t1[2];
+ qCDebug(lcSGCurveIntersectionSolver) << " vs" << t2[0] << t2[1] << t2[2];
+#endif
+
+ // TODO: Try to figure out reasonable starting points to reach all 4 possible intersections.
+ // This works but is kinda brute forcing it.
+ constexpr std::array tref = { QPointF{0.0, 0.0}, QPointF{0.5, 0.0}, QPointF{1.0, 0.0},
+ QPointF{0.0, 0.5}, QPointF{0.5, 0.5}, QPointF{1.0, 0.5},
+ QPointF{0.0, 1.0}, QPointF{0.5, 1.0}, QPointF{1.0, 1.0} };
+
+ for (auto t : tref) {
+ double err = 1;
+ QPointF fval = F(t);
+ int i = 0;
+
+ // TODO: Try to abort sooner, e.g. when falling out of the interval [0-1]?
+ while (err > eps && i < maxIterations) { // && t.x() >= 0 && t.x() <= 1 && t.y() >= 0 && t.y() <= 1) {
+ t = t - solve(J(t), fval);
+ fval = F(t);
+ err = qAbs(fval.x()) + qAbs(fval.y()); // Using the Manhatten length as an error indicator.
+ i++;
+#ifdef INTERSECTION_EXTRA_DEBUG
+ qCDebug(lcSGCurveIntersectionSolver) << " Newton iteration" << i << "t =" << t << "F =" << fval << "Error =" << err;
+#endif
+ }
+ // Intersections at 0 count. Intersections at 1 do not.
+ if (err < eps && t.x() >=0 && t.x() <= 1. - 10 * eps2 && t.y() >= 0 && t.y() <= 1. - 10 * eps2) {
+#ifdef INTERSECTION_EXTRA_DEBUG
+ qCDebug(lcSGCurveIntersectionSolver) << " Newton solution (after" << i << ")=" << t << "(" << F(t) << ")";
+#endif
+ if (solutions) {
+ bool append = true;
+ for (auto solution : *solutions) {
+ if (qAbs(solution.first - t.x()) < 10 * eps2 && qAbs(solution.second - t.y()) < 10 * eps2) {
+ append = false;
+ break;
+ }
+ }
+ if (append)
+ solutions->append({t.x(), t.y()});
+ }
+ else
+ return true;
+ }
+ }
+ if (solutions)
+ return solutions->size() > 0;
+ else
+ return false;
+}
+
+static bool isIntersecting(const QQuadPath &path, int e1, int e2, QList<QPair<float, float>> *solutions = nullptr)
+{
+
+ const QQuadPath::Element &elem1 = path.elementAt(e1);
+ const QQuadPath::Element &elem2 = path.elementAt(e2);
+
+ if (elem1.isLine() && elem2.isLine()) {
+ return lineIntersection(LinePoints {elem1.startPoint(), elem1.endPoint() },
+ LinePoints {elem2.startPoint(), elem2.endPoint() },
+ solutions);
+ } else {
+ return isIntersecting(TrianglePoints { elem1.startPoint(), elem1.controlPoint(), elem1.endPoint() },
+ TrianglePoints { elem2.startPoint(), elem2.controlPoint(), elem2.endPoint() },
+ solutions);
+ }
+}
+
+struct TriangleData
+{
+ TrianglePoints points;
+ int pathElementIndex;
+ TrianglePoints normals;
+};
+
+// Returns a normalized vector that is perpendicular to baseLine, pointing to the right
+inline QVector2D normalVector(QVector2D baseLine)
+{
+ QVector2D normal = QVector2D(-baseLine.y(), baseLine.x()).normalized();
+ return normal;
+}
+
+// Returns a vector that is normal to the path and pointing to the right. If endSide is false
+// the vector is normal to the start point, otherwise to the end point
+QVector2D normalVector(const QQuadPath::Element &element, bool endSide = false)
+{
+ if (element.isLine())
+ return normalVector(element.endPoint() - element.startPoint());
+ else if (!endSide)
+ return normalVector(element.controlPoint() - element.startPoint());
+ else
+ return normalVector(element.endPoint() - element.controlPoint());
+}
+
+// Returns a vector that is parallel to the path. If endSide is false
+// the vector starts at the start point and points forward,
+// otherwise it starts at the end point and points backward
+QVector2D tangentVector(const QQuadPath::Element &element, bool endSide = false)
+{
+ if (element.isLine()) {
+ if (!endSide)
+ return element.endPoint() - element.startPoint();
+ else
+ return element.startPoint() - element.endPoint();
+ } else {
+ if (!endSide)
+ return element.controlPoint() - element.startPoint();
+ else
+ return element.controlPoint() - element.endPoint();
+ }
+}
+
+// Really simplistic O(n^2) triangulator - only intended for five points
+QList<TriangleData> simplePointTriangulator(const QList<QVector2D> &pts, const QList<QVector2D> &normals, int elementIndex)
+{
+ int count = pts.size();
+ Q_ASSERT(count >= 3);
+ Q_ASSERT(normals.size() == count);
+
+ // First we find the convex hull: it's always in positive determinant winding order
+ QList<int> hull;
+ float det1 = determinant(pts[0], pts[1], pts[2]);
+ if (det1 > 0)
+ hull << 0 << 1 << 2;
+ else
+ hull << 2 << 1 << 0;
+ auto connectableInHull = [&](int idx) -> QList<int> {
+ QList<int> r;
+ const int n = hull.size();
+ const auto &pt = pts[idx];
+ for (int i = 0; i < n; ++i) {
+ const auto &i1 = hull.at(i);
+ const auto &i2 = hull.at((i+1) % n);
+ if (determinant(pts[i1], pts[i2], pt) < 0.0f)
+ r << i;
+ }
+ return r;
+ };
+ for (int i = 3; i < count; ++i) {
+ auto visible = connectableInHull(i);
+ if (visible.isEmpty())
+ continue;
+ int visCount = visible.count();
+ int hullCount = hull.count();
+ // Find where the visible part of the hull starts. (This is the part we need to triangulate to,
+ // and the part we're going to replace. "visible" contains the start point of the line segments that are visible from p.
+ int boundaryStart = visible[0];
+ for (int j = 0; j < visCount - 1; ++j) {
+ if ((visible[j] + 1) % hullCount != visible[j+1]) {
+ boundaryStart = visible[j + 1];
+ break;
+ }
+ }
+ // Finally replace the points that are now inside the hull
+ // We insert the new point after boundaryStart, and before boundaryStart + visCount (modulo...)
+ // and remove the points in between
+ int pointsToKeep = hullCount - visCount + 1;
+ QList<int> newHull;
+ newHull << i;
+ for (int j = 0; j < pointsToKeep; ++j) {
+ newHull << hull.at((j + boundaryStart + visCount) % hullCount);
+ }
+ hull = newHull;
+ }
+
+ // Now that we have a convex hull, we can trivially triangulate it
+ QList<TriangleData> ret;
+ for (int i = 1; i < hull.size() - 1; ++i) {
+ int i0 = hull[0];
+ int i1 = hull[i];
+ int i2 = hull[i+1];
+ ret.append({{pts[i0], pts[i1], pts[i2]}, elementIndex, {normals[i0], normals[i1], normals[i2]}});
+ }
+ return ret;
+}
+
+
+inline bool needsSplit(const QQuadPath::Element &el)
+{
+ Q_ASSERT(!el.isLine());
+ const auto v1 = el.controlPoint() - el.startPoint();
+ const auto v2 = el.endPoint() - el.controlPoint();
+ float cos = QVector2D::dotProduct(v1, v2) / (v1.length() * v2.length());
+ return cos < 0.9;
+}
+
+
+inline void splitElementIfNecessary(QQuadPath *path, int index, int level) {
+ if (level > 0 && needsSplit(path->elementAt(index))) {
+ path->splitElementAt(index);
+ splitElementIfNecessary(path, path->indexOfChildAt(index, 0), level - 1);
+ splitElementIfNecessary(path, path->indexOfChildAt(index, 1), level - 1);
+ }
+}
+
+static QQuadPath subdivide(const QQuadPath &path, int subdivisions)
+{
+ QQuadPath newPath = path;
+ newPath.iterateElements([&](QQuadPath::Element &e, int index) {
+ if (!e.isLine())
+ splitElementIfNecessary(&newPath, index, subdivisions);
+ });
+
+ return newPath;
+}
+
+static QList<TriangleData> customTriangulator2(const QQuadPath &path, float penWidth, Qt::PenJoinStyle joinStyle, Qt::PenCapStyle capStyle, float miterLimit)
+{
+ const bool bevelJoin = joinStyle == Qt::BevelJoin;
+ const bool roundJoin = joinStyle == Qt::RoundJoin;
+ const bool miterJoin = !bevelJoin && !roundJoin;
+
+ const bool roundCap = capStyle == Qt::RoundCap;
+ const bool squareCap = capStyle == Qt::SquareCap;
+ // We can't use the simple miter for miter joins, since the shader currently only supports round joins
+ const bool simpleMiter = joinStyle == Qt::RoundJoin;
+
+ Q_ASSERT(miterLimit > 0 || !miterJoin);
+ float inverseMiterLimit = miterJoin ? 1.0f / miterLimit : 1.0;
+
+ const float penFactor = penWidth / 2;
+
+ // Returns {inner1, inner2, outer1, outer2, outerMiter}
+ // where foo1 is for the end of element1 and foo2 is for the start of element2
+ // and inner1 == inner2 unless we had to give up finding a decent point
+ auto calculateJoin = [&](const QQuadPath::Element *element1, const QQuadPath::Element *element2,
+ bool &outerBisectorWithinMiterLimit, bool &innerIsRight, bool &giveUp) -> std::array<QVector2D, 5>
+ {
+ outerBisectorWithinMiterLimit = true;
+ innerIsRight = true;
+ giveUp = false;
+ if (!element1) {
+ Q_ASSERT(element2);
+ QVector2D n = normalVector(*element2);
+ return {n, n, -n, -n, -n};
+ }
+ if (!element2) {
+ Q_ASSERT(element1);
+ QVector2D n = normalVector(*element1, true);
+ return {n, n, -n, -n, -n};
+ }
+
+ Q_ASSERT(element1->endPoint() == element2->startPoint());
+
+ const auto p1 = element1->isLine() ? element1->startPoint() : element1->controlPoint();
+ const auto p2 = element1->endPoint();
+ const auto p3 = element2->isLine() ? element2->endPoint() : element2->controlPoint();
+
+ const auto v1 = (p1 - p2).normalized();
+ const auto v2 = (p3 - p2).normalized();
+ const auto b = (v1 + v2);
+
+ constexpr float epsilon = 1.0f / 32.0f;
+ bool smoothJoin = qAbs(b.x()) < epsilon && qAbs(b.y()) < epsilon;
+
+ if (smoothJoin) {
+ // v1 and v2 are almost parallel and pointing in opposite directions
+ // angle bisector formula will give an almost null vector: use normal of bisector of normals instead
+ QVector2D n1(-v1.y(), v1.x());
+ QVector2D n2(-v2.y(), v2.x());
+ QVector2D n = (n2 - n1).normalized();
+ return {n, n, -n, -n, -n};
+ }
+ // Calculate the length of the bisector, so it will cover the entire miter.
+ // Using the identity sin(x/2) == sqrt((1 - cos(x)) / 2), and the fact that the
+ // dot product of two unit vectors is the cosine of the angle between them
+ // The length of the miter is w/sin(x/2) where x is the angle between the two elements
+
+ const auto bisector = b.normalized();
+ float cos2x = QVector2D::dotProduct(v1, v2);
+ cos2x = qMin(1.0f, cos2x); // Allow for float inaccuracy
+ float sine = sqrt((1.0f - cos2x) / 2);
+ innerIsRight = determinant(p1, p2, p3) > 0;
+ sine = qMax(sine, 0.01f); // Avoid divide by zero
+ float length = penFactor / sine;
+
+ // Check if bisector is longer than one of the lines it's trying to bisect
+
+ auto tooLong = [](QVector2D p1, QVector2D p2, QVector2D n, float length, float margin) -> bool {
+ auto v = p2 - p1;
+ // It's too long if the projection onto the bisector is longer than the bisector
+ // and the projection onto the normal to the bisector is shorter
+ // than the pen margin (that projection is just v - proj)
+ // (we're adding a 10% safety margin to make room for AA -- not exact)
+ auto projLen = QVector2D::dotProduct(v, n);
+ return projLen * 0.9f < length && (v - n * projLen).length() * 0.9 < margin;
+ };
+
+
+ // The angle bisector of the tangent lines is not correct for curved lines. We could fix this by calculating
+ // the exact intersection point, but for now just give up and use the normals.
+
+ giveUp = !element1->isLine() || !element2->isLine()
+ || tooLong(p1, p2, bisector, length, penFactor)
+ || tooLong(p3, p2, bisector, length, penFactor);
+ outerBisectorWithinMiterLimit = sine >= inverseMiterLimit / 2.0f;
+ bool simpleJoin = simpleMiter && outerBisectorWithinMiterLimit && !giveUp;
+ const QVector2D bn = bisector / sine;
+
+ if (simpleJoin)
+ return {bn, bn, -bn, -bn, -bn}; // We only have one inner and one outer point TODO: change inner point when conflict/curve
+ const QVector2D n1 = normalVector(*element1, true);
+ const QVector2D n2 = normalVector(*element2);
+ if (giveUp) {
+ if (innerIsRight)
+ return {n1, n2, -n1, -n2, -bn};
+ else
+ return {-n1, -n2, n1, n2, -bn};
+
+ } else {
+ if (innerIsRight)
+ return {bn, bn, -n1, -n2, -bn};
+ else
+ return {bn, bn, n1, n2, -bn};
+ }
+ };
+
+ QList<TriangleData> ret;
+
+ auto triangulateCurve = [&](int idx, const QVector2D &p1, const QVector2D &p2, const QVector2D &p3, const QVector2D &p4,
+ const QVector2D &n1, const QVector2D &n2, const QVector2D &n3, const QVector2D &n4)
+ {
+ const auto &element = path.elementAt(idx);
+ Q_ASSERT(!element.isLine());
+ const auto &s = element.startPoint();
+ const auto &c = element.controlPoint();
+ const auto &e = element.endPoint();
+ // TODO: Don't flatten the path in addCurveStrokeNodes, but iterate over the children here instead
+ bool controlPointOnRight = determinant(s, c, e) > 0;
+ QVector2D startNormal = normalVector(element);
+ QVector2D endNormal = normalVector(element, true);
+ QVector2D controlPointNormal = (startNormal + endNormal).normalized();
+ if (controlPointOnRight)
+ controlPointNormal = -controlPointNormal;
+ QVector2D p5 = c + controlPointNormal * penFactor; // This is too simplistic
+ TrianglePoints t1{p1, p2, p5};
+ TrianglePoints t2{p3, p4, p5};
+ bool simpleCase = !checkTriangleOverlap(t1, t2);
+
+ if (simpleCase) {
+ ret.append({{p1, p2, p5}, idx, {n1, n2, controlPointNormal}});
+ ret.append({{p3, p4, p5}, idx, {n3, n4, controlPointNormal}});
+ if (controlPointOnRight) {
+ ret.append({{p1, p3, p5}, idx, {n1, n3, controlPointNormal}});
+ } else {
+ ret.append({{p2, p4, p5}, idx, {n2, n4, controlPointNormal}});
+ }
+ } else {
+ ret.append(simplePointTriangulator({p1, p2, p5, p3, p4}, {n1, n2, controlPointNormal, n3, n4}, idx));
+ }
+ };
+
+ // Each element is calculated independently, so we don't have to special-case closed sub-paths.
+ // Take care so the end points of one element are precisely equal to the start points of the next.
+ // Any additional triangles needed for joining are added at the end of the current element.
+
+ int count = path.elementCount();
+ int subStart = 0;
+ while (subStart < count) {
+ int subEnd = subStart;
+ for (int i = subStart + 1; i < count; ++i) {
+ const auto &e = path.elementAt(i);
+ if (e.isSubpathStart()) {
+ subEnd = i - 1;
+ break;
+ }
+ if (i == count - 1) {
+ subEnd = i;
+ break;
+ }
+ }
+ bool closed = path.elementAt(subStart).startPoint() == path.elementAt(subEnd).endPoint();
+ const int subCount = subEnd - subStart + 1;
+
+ auto addIdx = [&](int idx, int delta) -> int {
+ int subIdx = idx - subStart;
+ if (closed)
+ subIdx = (subIdx + subCount + delta) % subCount;
+ else
+ subIdx += delta;
+ return subStart + subIdx;
+ };
+ auto elementAt = [&](int idx, int delta) -> const QQuadPath::Element * {
+ int subIdx = idx - subStart;
+ if (closed) {
+ subIdx = (subIdx + subCount + delta) % subCount;
+ return &path.elementAt(subStart + subIdx);
+ }
+ subIdx += delta;
+ if (subIdx >= 0 && subIdx < subCount)
+ return &path.elementAt(subStart + subIdx);
+ return nullptr;
+ };
+
+ for (int i = subStart; i <= subEnd; ++i) {
+ const auto &element = path.elementAt(i);
+ const auto *nextElement = elementAt(i, +1);
+ const auto *prevElement = elementAt(i, -1);
+
+ const auto &s = element.startPoint();
+ const auto &e = element.endPoint();
+
+ bool startInnerIsRight;
+ bool startBisectorWithinMiterLimit; // Not used
+ bool giveUpOnStartJoin; // Not used
+ auto startJoin = calculateJoin(prevElement, &element,
+ startBisectorWithinMiterLimit, startInnerIsRight,
+ giveUpOnStartJoin);
+ const QVector2D &startInner = startJoin[1];
+ const QVector2D &startOuter = startJoin[3];
+
+ bool endInnerIsRight;
+ bool endBisectorWithinMiterLimit;
+ bool giveUpOnEndJoin;
+ auto endJoin = calculateJoin(&element, nextElement,
+ endBisectorWithinMiterLimit, endInnerIsRight,
+ giveUpOnEndJoin);
+ QVector2D endInner = endJoin[0];
+ QVector2D endOuter = endJoin[2];
+ QVector2D nextOuter = endJoin[3];
+ QVector2D outerB = endJoin[4];
+
+ QVector2D p1, p2, p3, p4;
+ QVector2D n1, n2, n3, n4;
+
+ if (startInnerIsRight) {
+ n1 = startInner;
+ n2 = startOuter;
+ } else {
+ n1 = startOuter;
+ n2 = startInner;
+ }
+
+ p1 = s + n1 * penFactor;
+ p2 = s + n2 * penFactor;
+
+ // repeat logic above for the other end:
+ if (endInnerIsRight) {
+ n3 = endInner;
+ n4 = endOuter;
+ } else {
+ n3 = endOuter;
+ n4 = endInner;
+ }
+
+ p3 = e + n3 * penFactor;
+ p4 = e + n4 * penFactor;
+
+ // End caps
+
+ if (!prevElement) {
+ QVector2D capSpace = tangentVector(element).normalized() * -penFactor;
+ if (roundCap) {
+ p1 += capSpace;
+ p2 += capSpace;
+ } else if (squareCap) {
+ QVector2D c1 = p1 + capSpace;
+ QVector2D c2 = p2 + capSpace;
+ ret.append({{p1, s, c1}, -1, {}});
+ ret.append({{c1, s, c2}, -1, {}});
+ ret.append({{p2, s, c2}, -1, {}});
+ }
+ }
+ if (!nextElement) {
+ QVector2D capSpace = tangentVector(element, true).normalized() * -penFactor;
+ if (roundCap) {
+ p3 += capSpace;
+ p4 += capSpace;
+ } else if (squareCap) {
+ QVector2D c3 = p3 + capSpace;
+ QVector2D c4 = p4 + capSpace;
+ ret.append({{p3, e, c3}, -1, {}});
+ ret.append({{c3, e, c4}, -1, {}});
+ ret.append({{p4, e, c4}, -1, {}});
+ }
+ }
+
+ if (element.isLine()) {
+ ret.append({{p1, p2, p3}, i, {n1, n2, n3}});
+ ret.append({{p2, p3, p4}, i, {n2, n3, n4}});
+ } else {
+ triangulateCurve(i, p1, p2, p3, p4, n1, n2, n3, n4);
+ }
+
+ bool trivialJoin = simpleMiter && endBisectorWithinMiterLimit && !giveUpOnEndJoin;
+ if (!trivialJoin && nextElement) {
+ // inside of join (opposite of bevel) is defined by
+ // triangle s, e, next.e
+ bool innerOnRight = endInnerIsRight;
+
+ const auto outer1 = e + endOuter * penFactor;
+ const auto outer2 = e + nextOuter * penFactor;
+ //const auto inner = e + endInner * penFactor;
+
+ if (bevelJoin || (miterJoin && !endBisectorWithinMiterLimit)) {
+ ret.append({{outer1, e, outer2}, -1, {}});
+ } else if (roundJoin) {
+ ret.append({{outer1, e, outer2}, i, {}});
+ QVector2D nn = calcNormalVector(outer1, outer2).normalized() * penFactor;
+ if (!innerOnRight)
+ nn = -nn;
+ ret.append({{outer1, outer1 + nn, outer2}, i, {}});
+ ret.append({{outer1 + nn, outer2, outer2 + nn}, i, {}});
+
+ } else if (miterJoin) {
+ QVector2D outer = e + outerB * penFactor;
+ ret.append({{outer1, e, outer}, -2, {}});
+ ret.append({{outer, e, outer2}, -2, {}});
+ }
+
+ if (!giveUpOnEndJoin) {
+ QVector2D inner = e + endInner * penFactor;
+ ret.append({{inner, e, outer1}, i, {endInner, {}, endOuter}});
+ // The remaining triangle ought to be done by nextElement, but we don't have start join logic there (yet)
+ int nextIdx = addIdx(i, +1);
+ ret.append({{inner, e, outer2}, nextIdx, {endInner, {}, nextOuter}});
+ }
+ }
+ }
+ subStart = subEnd + 1;
+ }
+ return ret;
+}
+
+// TODO: we could optimize by preprocessing e1, since we call this function multiple times on the same
+// elements
+// Returns true if a change was made
+static bool handleOverlap(QQuadPath &path, int e1, int e2, int recursionLevel = 0)
+{
+ // Splitting lines is not going to help with overlap, since we assume that lines don't intersect
+ if (path.elementAt(e1).isLine() && path.elementAt(e1).isLine())
+ return false;
+
+ if (!isOverlap(path, e1, e2)) {
+ return false;
+ }
+
+ if (recursionLevel > 8) {
+ qCDebug(lcSGCurveProcessor) << "Triangle overlap: recursion level" << recursionLevel << "aborting!";
+ return false;
+ }
+
+ if (path.elementAt(e1).childCount() > 1) {
+ auto e11 = path.indexOfChildAt(e1, 0);
+ auto e12 = path.indexOfChildAt(e1, 1);
+ handleOverlap(path, e11, e2, recursionLevel + 1);
+ handleOverlap(path, e12, e2, recursionLevel + 1);
+ } else if (path.elementAt(e2).childCount() > 1) {
+ auto e21 = path.indexOfChildAt(e2, 0);
+ auto e22 = path.indexOfChildAt(e2, 1);
+ handleOverlap(path, e1, e21, recursionLevel + 1);
+ handleOverlap(path, e1, e22, recursionLevel + 1);
+ } else {
+ path.splitElementAt(e1);
+ auto e11 = path.indexOfChildAt(e1, 0);
+ auto e12 = path.indexOfChildAt(e1, 1);
+ bool overlap1 = isOverlap(path, e11, e2);
+ bool overlap2 = isOverlap(path, e12, e2);
+ if (!overlap1 && !overlap2)
+ return true; // no more overlap: success!
+
+ // We need to split more:
+ if (path.elementAt(e2).isLine()) {
+ // Splitting a line won't help, so we just split e1 further
+ if (overlap1)
+ handleOverlap(path, e11, e2, recursionLevel + 1);
+ if (overlap2)
+ handleOverlap(path, e12, e2, recursionLevel + 1);
+ } else {
+ // See if splitting e2 works:
+ path.splitElementAt(e2);
+ auto e21 = path.indexOfChildAt(e2, 0);
+ auto e22 = path.indexOfChildAt(e2, 1);
+ if (overlap1) {
+ handleOverlap(path, e11, e21, recursionLevel + 1);
+ handleOverlap(path, e11, e22, recursionLevel + 1);
+ }
+ if (overlap2) {
+ handleOverlap(path, e12, e21, recursionLevel + 1);
+ handleOverlap(path, e12, e22, recursionLevel + 1);
+ }
+ }
+ }
+ return true;
+}
+}
+
+// Returns true if the path was changed
+bool QSGCurveProcessor::solveOverlaps(QQuadPath &path)
+{
+ bool changed = false;
+ if (path.testHint(QQuadPath::PathNonOverlappingControlPointTriangles))
+ return false;
+
+ const auto candidates = findOverlappingCandidates(path);
+ for (auto candidate : candidates)
+ changed = handleOverlap(path, candidate.first, candidate.second) || changed;
+
+ path.setHint(QQuadPath::PathNonOverlappingControlPointTriangles);
+ return changed;
+}
+
+// A fast algorithm to find path elements that might overlap. We will only check the overlap of the
+// triangles that define the elements.
+// We will order the elements first and then pool them depending on their x-values. This should
+// reduce the complexity to O(n log n), where n is the number of elements in the path.
+QList<QPair<int, int>> QSGCurveProcessor::findOverlappingCandidates(const QQuadPath &path)
+{
+ struct BRect { float xmin; float xmax; float ymin; float ymax; };
+
+ // Calculate all bounding rectangles
+ QVarLengthArray<int, 64> elementStarts, elementEnds;
+ QVarLengthArray<BRect, 64> boundingRects;
+ elementStarts.reserve(path.elementCount());
+ boundingRects.reserve(path.elementCount());
+ for (int i = 0; i < path.elementCount(); i++) {
+ QQuadPath::Element e = path.elementAt(i);
+
+ BRect bR{qMin(qMin(e.startPoint().x(), e.controlPoint().x()), e.endPoint().x()),
+ qMax(qMax(e.startPoint().x(), e.controlPoint().x()), e.endPoint().x()),
+ qMin(qMin(e.startPoint().y(), e.controlPoint().y()), e.endPoint().y()),
+ qMax(qMax(e.startPoint().y(), e.controlPoint().y()), e.endPoint().y())};
+ boundingRects.append(bR);
+ elementStarts.append(i);
+ }
+
+ // Sort the bounding rectangles by x-startpoint and x-endpoint
+ auto compareXmin = [&](int i, int j){return boundingRects.at(i).xmin < boundingRects.at(j).xmin;};
+ auto compareXmax = [&](int i, int j){return boundingRects.at(i).xmax < boundingRects.at(j).xmax;};
+ std::sort(elementStarts.begin(), elementStarts.end(), compareXmin);
+ elementEnds = elementStarts;
+ std::sort(elementEnds.begin(), elementEnds.end(), compareXmax);
+
+ QList<int> bRpool;
+ QList<QPair<int, int>> overlappingBB;
+
+ // Start from x = xmin and move towards xmax. Add a rectangle to the pool and check for
+ // intersections with all other rectangles in the pool. If a rectangles xmax is smaller
+ // than the new xmin, it can be removed from the pool.
+ int firstElementEnd = 0;
+ for (const int addIndex : std::as_const(elementStarts)) {
+ const BRect &newR = boundingRects.at(addIndex);
+ // First remove elements from the pool that cannot touch the new one
+ // because xmax is too small
+ while (bRpool.size() && firstElementEnd < elementEnds.size()) {
+ int removeIndex = elementEnds.at(firstElementEnd);
+ if (bRpool.contains(removeIndex) && newR.xmin > boundingRects.at(removeIndex).xmax) {
+ bRpool.removeOne(removeIndex);
+ firstElementEnd++;
+ } else {
+ break;
+ }
+ }
+
+ // Now compare the new element with all elements in the pool.
+ for (int j = 0; j < bRpool.size(); j++) {
+ int i = bRpool.at(j);
+ const BRect &r1 = boundingRects.at(i);
+ // We don't have to check for x because the pooling takes care of it.
+ //if (r1.xmax <= newR.xmin || newR.xmax <= r1.xmin)
+ // continue;
+
+ bool isNeighbor = false;
+ if (i - addIndex == 1) {
+ if (!path.elementAt(addIndex).isSubpathEnd())
+ isNeighbor = true;
+ } else if (addIndex - i == 1) {
+ if (!path.elementAt(i).isSubpathEnd())
+ isNeighbor = true;
+ }
+ // Neighbors need to be completely different (otherwise they just share a point)
+ if (isNeighbor && (r1.ymax <= newR.ymin || newR.ymax <= r1.ymin))
+ continue;
+ // Non-neighbors can also just touch
+ if (!isNeighbor && (r1.ymax < newR.ymin || newR.ymax < r1.ymin))
+ continue;
+ // If the bounding boxes are overlapping it is a candidate for an intersection.
+ overlappingBB.append(QPair<int, int>(i, addIndex));
+ }
+ bRpool.append(addIndex); //Add the new element to the pool.
+ }
+ return overlappingBB;
+}
+
+// Remove paths that are nested inside another path and not required to fill the path correctly
+bool QSGCurveProcessor::removeNestedSubpaths(QQuadPath &path)
+{
+ // Ensure that the path is not intersecting first
+ Q_ASSERT(path.testHint(QQuadPath::PathNonIntersecting));
+
+ if (path.fillRule() != Qt::WindingFill) {
+ // If the fillingRule is odd-even, all internal subpaths matter
+ return false;
+ }
+
+ // Store the starting and end elements of the subpaths to be able
+ // to jump quickly between them.
+ QList<int> subPathStartPoints;
+ QList<int> subPathEndPoints;
+ for (int i = 0; i < path.elementCount(); i++) {
+ if (path.elementAt(i).isSubpathStart())
+ subPathStartPoints.append(i);
+ if (path.elementAt(i).isSubpathEnd()) {
+ subPathEndPoints.append(i);
+ }
+ }
+ const int subPathCount = subPathStartPoints.size();
+
+ // If there is only one subpath, none have to be removed
+ if (subPathStartPoints.size() < 2)
+ return false;
+
+ // We set up a matrix that tells us which path is nested in which other path.
+ QList<bool> isInside;
+ bool isAnyInside = false;
+ isInside.reserve(subPathStartPoints.size() * subPathStartPoints.size());
+ for (int i = 0; i < subPathCount; i++) {
+ for (int j = 0; j < subPathCount; j++) {
+ if (i == j) {
+ isInside.append(false);
+ } else {
+ isInside.append(path.contains(path.elementAt(subPathStartPoints.at(i)).startPoint(),
+ subPathStartPoints.at(j), subPathEndPoints.at(j)));
+ if (isInside.last())
+ isAnyInside = true;
+ }
+ }
+ }
+
+ // If no nested subpaths are present we can return early.
+ if (!isAnyInside)
+ return false;
+
+ // To find out which paths are filled and which not, we first calculate the
+ // rotation direction (clockwise - counterclockwise).
+ QList<bool> clockwise;
+ clockwise.reserve(subPathCount);
+ for (int i = 0; i < subPathCount; i++) {
+ float sumProduct = 0;
+ for (int j = subPathStartPoints.at(i); j <= subPathEndPoints.at(i); j++) {
+ const QVector2D startPoint = path.elementAt(j).startPoint();
+ const QVector2D endPoint = path.elementAt(j).endPoint();
+ sumProduct += (endPoint.x() - startPoint.x()) * (endPoint.y() + startPoint.y());
+ }
+ clockwise.append(sumProduct > 0);
+ }
+
+ // Set up a list that tells us which paths create filling and which path create holes.
+ // Holes in Holes and fillings in fillings can then be removed.
+ QList<bool> isFilled;
+ isFilled.reserve(subPathStartPoints.size() );
+ for (int i = 0; i < subPathCount; i++) {
+ int crossings = clockwise.at(i) ? 1 : -1;
+ for (int j = 0; j < subPathStartPoints.size(); j++) {
+ if (isInside.at(i * subPathCount + j))
+ crossings += clockwise.at(j) ? 1 : -1;
+ }
+ isFilled.append(crossings != 0);
+ }
+
+ // A helper function to find the most inner subpath that is around a subpath.
+ // Returns -1 if the subpath is a toplevel subpath.
+ auto findClosestOuterSubpath = [&](int subPath) {
+ // All paths that contain the current subPath are candidates.
+ QList<int> candidates;
+ for (int i = 0; i < subPathStartPoints.size(); i++) {
+ if (isInside.at(subPath * subPathCount + i))
+ candidates.append(i);
+ }
+ int maxNestingLevel = -1;
+ int maxNestingLevelIndex = -1;
+ for (int i = 0; i < candidates.size(); i++) {
+ int nestingLevel = 0;
+ for (int j = 0; j < candidates.size(); j++) {
+ if (isInside.at(candidates.at(i) * subPathCount + candidates.at(j))) {
+ nestingLevel++;
+ }
+ }
+ if (nestingLevel > maxNestingLevel) {
+ maxNestingLevel = nestingLevel;
+ maxNestingLevelIndex = candidates.at(i);
+ }
+ }
+ return maxNestingLevelIndex;
+ };
+
+ bool pathChanged = false;
+ QQuadPath fixedPath;
+ fixedPath.setPathHints(path.pathHints());
+
+ // Go through all subpaths and find the closest surrounding subpath.
+ // If it is doing the same (create filling or create hole) we can remove it.
+ for (int i = 0; i < subPathCount; i++) {
+ int j = findClosestOuterSubpath(i);
+ if (j >= 0 && isFilled.at(i) == isFilled.at(j)) {
+ pathChanged = true;
+ } else {
+ for (int k = subPathStartPoints.at(i); k <= subPathEndPoints.at(i); k++)
+ fixedPath.addElement(path.elementAt(k));
+ }
+ }
+
+ if (pathChanged)
+ path = fixedPath;
+ return pathChanged;
+}
+
+// Returns true if the path was changed
+bool QSGCurveProcessor::solveIntersections(QQuadPath &path, bool removeNestedPaths)
+{
+ if (path.testHint(QQuadPath::PathNonIntersecting)) {
+ if (removeNestedPaths)
+ return removeNestedSubpaths(path);
+ else
+ return false;
+ }
+
+ if (path.elementCount() < 2) {
+ path.setHint(QQuadPath::PathNonIntersecting);
+ return false;
+ }
+
+ struct IntersectionData { int e1; int e2; float t1; float t2; bool in1 = false, in2 = false, out1 = false, out2 = false; };
+ QList<IntersectionData> intersections;
+
+ // Helper function to mark an intersection as handled when the
+ // path i is processed moving forward/backward
+ auto markIntersectionAsHandled = [=](IntersectionData *data, int i, bool forward) {
+ if (data->e1 == i) {
+ if (forward)
+ data->out1 = true;
+ else
+ data->in1 = true;
+ } else if (data->e2 == i){
+ if (forward)
+ data->out2 = true;
+ else
+ data->in2 = true;
+ } else {
+ Q_UNREACHABLE();
+ }
+ };
+
+ // First make a O(n log n) search for candidates.
+ const QList<QPair<int, int>> candidates = findOverlappingCandidates(path);
+ // Then check the candidates for actual intersections.
+ for (const auto &candidate : candidates) {
+ QList<QPair<float,float>> res;
+ int e1 = candidate.first;
+ int e2 = candidate.second;
+ if (isIntersecting(path, e1, e2, &res)) {
+ for (const auto &r : res)
+ intersections.append({e1, e2, r.first, r.second});
+ }
+ }
+
+ qCDebug(lcSGCurveIntersectionSolver) << "----- Checking for Intersections -----";
+ qCDebug(lcSGCurveIntersectionSolver) << "Found" << intersections.length() << "intersections";
+ if (lcSGCurveIntersectionSolver().isDebugEnabled()) {
+ for (const auto &i : intersections) {
+ auto p1 = path.elementAt(i.e1).pointAtFraction(i.t1);
+ auto p2 = path.elementAt(i.e2).pointAtFraction(i.t2);
+ qCDebug(lcSGCurveIntersectionSolver) << " between" << i.e1 << "and" << i.e2 << "at" << i.t1 << "/" << i.t2 << "->" << p1 << "/" << p2;
+ }
+ }
+
+ if (intersections.isEmpty()) {
+ path.setHint(QQuadPath::PathNonIntersecting);
+ if (removeNestedPaths) {
+ qCDebug(lcSGCurveIntersectionSolver) << "No Intersections found. Looking for enclosed subpaths.";
+ return removeNestedSubpaths(path);
+ } else {
+ qCDebug(lcSGCurveIntersectionSolver) << "Returning the path unchanged.";
+ return false;
+ }
+ }
+
+
+ // Store the starting and end elements of the subpaths to be able
+ // to jump quickly between them. Also keep a list of handled paths,
+ // so we know if we need to come back to a subpath or if it
+ // was already united with another subpath due to an intersection.
+ QList<int> subPathStartPoints;
+ QList<int> subPathEndPoints;
+ QList<bool> subPathHandled;
+ for (int i = 0; i < path.elementCount(); i++) {
+ if (path.elementAt(i).isSubpathStart())
+ subPathStartPoints.append(i);
+ if (path.elementAt(i).isSubpathEnd()) {
+ subPathEndPoints.append(i);
+ subPathHandled.append(false);
+ }
+ }
+
+ // A small helper to find the subPath of an element with index
+ auto subPathIndex = [&](int index) {
+ for (int i = 0; i < subPathStartPoints.size(); i++) {
+ if (index >= subPathStartPoints.at(i) && index <= subPathEndPoints.at(i))
+ return i;
+ }
+ return -1;
+ };
+
+ // Helper to ensure that index i and position t are valid:
+ auto ensureInBounds = [&](int *i, float *t, float deltaT) {
+ if (*t <= 0.f) {
+ if (path.elementAt(*i).isSubpathStart())
+ *i = subPathEndPoints.at(subPathIndex(*i));
+ else
+ *i = *i - 1;
+ *t = 1.f - deltaT;
+ } else if (*t >= 1.f) {
+ if (path.elementAt(*i).isSubpathEnd())
+ *i = subPathStartPoints.at(subPathIndex(*i));
+ else
+ *i = *i + 1;
+ *t = deltaT;
+ }
+ };
+
+ // Helper function to find a siutable starting point between start and end.
+ // A suitable starting point is where right is inside and left is outside
+ // If left is inside and right is outside it works too, just move in the
+ // other direction (forward = false).
+ auto findStart = [=](QQuadPath &path, int start, int end, int *result, bool *forward) {
+ for (int i = start; i < end; i++) {
+ int adjecent;
+ if (subPathStartPoints.contains(i))
+ adjecent = subPathEndPoints.at(subPathStartPoints.indexOf(i));
+ else
+ adjecent = i - 1;
+
+ QQuadPath::Element::FillSide fillSide = path.fillSideOf(i, 1e-4f);
+ const bool leftInside = fillSide == QQuadPath::Element::FillSideLeft;
+ const bool rightInside = fillSide == QQuadPath::Element::FillSideRight;
+ qCDebug(lcSGCurveIntersectionSolver) << "Element" << i << "/" << adjecent << "meeting point is left/right inside:" << leftInside << "/" << rightInside;
+ if (rightInside) {
+ *result = i;
+ *forward = true;
+ return true;
+ } else if (leftInside) {
+ *result = adjecent;
+ *forward = false;
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // Also store handledElements (handled is when we touch the start point).
+ // This is used to identify and abort on errors.
+ QVarLengthArray<bool> handledElements(path.elementCount(), false);
+ // Only store handledElements when it is not touched due to an intersection.
+ bool regularVisit = true;
+
+ QQuadPath fixedPath;
+ fixedPath.setFillRule(path.fillRule());
+
+ int i1 = 0;
+ float t1 = 0;
+
+ int i2 = 0;
+ float t2 = 0;
+
+ float t = 0;
+ bool forward = true;
+
+ int startedAtIndex = -1;
+ float startedAtT = -1;
+
+ if (!findStart(path, 0, path.elementCount(), &i1, &forward)) {
+ qCDebug(lcSGCurveIntersectionSolver) << "No suitable start found. This should not happen. Returning the path unchanged.";
+ return false;
+ }
+
+ // Helper function to start a new subpath and update temporary variables.
+ auto startNewSubPath = [&](int i, bool forward) {
+ if (forward) {
+ fixedPath.moveTo(path.elementAt(i).startPoint());
+ t = startedAtT = 0;
+ } else {
+ fixedPath.moveTo(path.elementAt(i).endPoint());
+ t = startedAtT = 1;
+ }
+ startedAtIndex = i;
+ subPathHandled[subPathIndex(i)] = true;
+ };
+ startNewSubPath(i1, forward);
+
+ // If not all interactions where found correctly, we might end up in an infinite loop.
+ // Therefore we count the total number of iterations and bail out at some point.
+ int totalIterations = 0;
+
+ // We need to store the last intersection so we don't jump back and forward immediately.
+ int prevIntersection = -1;
+
+ do {
+ // Sanity check: Make sure that we do not process the same corner point more than once.
+ if (regularVisit && (t == 0 || t == 1)) {
+ int nextIndex = i1;
+ if (t == 1 && path.elementAt(i1).isSubpathEnd()) {
+ nextIndex = subPathStartPoints.at(subPathIndex(i1));
+ } else if (t == 1) {
+ nextIndex = nextIndex + 1;
+ }
+ if (handledElements[nextIndex]) {
+ qCDebug(lcSGCurveIntersectionSolver) << "Revisiting an element when trying to solve intersections. This should not happen. Returning the path unchanged.";
+ return false;
+ }
+ handledElements[nextIndex] = true;
+ }
+ // Sanity check: Keep an eye on total iterations
+ totalIterations++;
+
+ qCDebug(lcSGCurveIntersectionSolver) << "Checking section" << i1 << "from" << t << "going" << (forward ? "forward" : "backward");
+
+ // Find the next intersection that is as close as possible to t but in direction of processing (forward or !forward).
+ int iC = -1; //intersection candidate
+ t1 = forward? 1 : -1; //intersection candidate t-value
+ for (int j = 0; j < intersections.size(); j++) {
+ if (j == prevIntersection)
+ continue;
+ if (i1 == intersections[j].e1 &&
+ intersections[j].t1 * (forward ? 1 : -1) >= t * (forward ? 1 : -1) &&
+ intersections[j].t1 * (forward ? 1 : -1) < t1 * (forward ? 1 : -1)) {
+ iC = j;
+ t1 = intersections[j].t1;
+ i2 = intersections[j].e2;
+ t2 = intersections[j].t2;
+ }
+ if (i1 == intersections[j].e2 &&
+ intersections[j].t2 * (forward ? 1 : -1) >= t * (forward ? 1 : -1) &&
+ intersections[j].t2 * (forward ? 1 : -1) < t1 * (forward ? 1 : -1)) {
+ iC = j;
+ t1 = intersections[j].t2;
+ i2 = intersections[j].e1;
+ t2 = intersections[j].t1;
+ }
+ }
+ prevIntersection = iC;
+
+ if (iC < 0) {
+ qCDebug(lcSGCurveIntersectionSolver) << " No intersection found on my way. Adding the rest of the segment " << i1;
+ regularVisit = true;
+ // If no intersection with the current element was found, just add the rest of the element
+ // to the fixed path and go on.
+ // If we reached the end (going forward) or start (going backward) of a subpath, we have
+ // to wrap aroud. Abort condition for the loop comes separately later.
+ if (forward) {
+ if (path.elementAt(i1).isLine()) {
+ fixedPath.lineTo(path.elementAt(i1).endPoint());
+ } else {
+ const QQuadPath::Element rest = path.elementAt(i1).segmentFromTo(t, 1);
+ fixedPath.quadTo(rest.controlPoint(), rest.endPoint());
+ }
+ if (path.elementAt(i1).isSubpathEnd()) {
+ int index = subPathEndPoints.indexOf(i1);
+ qCDebug(lcSGCurveIntersectionSolver) << " Going back to the start of subPath" << index;
+ i1 = subPathStartPoints.at(index);
+ } else {
+ i1++;
+ }
+ t = 0;
+ } else {
+ if (path.elementAt(i1).isLine()) {
+ fixedPath.lineTo(path.elementAt(i1).startPoint());
+ } else {
+ const QQuadPath::Element rest = path.elementAt(i1).segmentFromTo(0, t).reversed();
+ fixedPath.quadTo(rest.controlPoint(), rest.endPoint());
+ }
+ if (path.elementAt(i1).isSubpathStart()) {
+ int index = subPathStartPoints.indexOf(i1);
+ qCDebug(lcSGCurveIntersectionSolver) << " Going back to the end of subPath" << index;
+ i1 = subPathEndPoints.at(index);
+ } else {
+ i1--;
+ }
+ t = 1;
+ }
+ } else { // Here comes the part where we actually handle intersections.
+ qCDebug(lcSGCurveIntersectionSolver) << " Found an intersection at" << t1 << "with" << i2 << "at" << t2;
+
+ // Mark the subpath we intersected with as visisted. We do not have to come here explicitly again.
+ subPathHandled[subPathIndex(i2)] = true;
+
+ // Mark the path that lead us to this intersection as handled on the intersection level.
+ // Note the ! in front of forward. This is required because we move towards the intersection.
+ markIntersectionAsHandled(&intersections[iC], i1, !forward);
+
+ // Split the path from the last point to the newly found intersection.
+ // Add the part of the current segment to the fixedPath.
+ const QQuadPath::Element &elem1 = path.elementAt(i1);
+ if (elem1.isLine()) {
+ fixedPath.lineTo(elem1.pointAtFraction(t1));
+ } else {
+ QQuadPath::Element partUntilIntersection;
+ if (forward) {
+ partUntilIntersection = elem1.segmentFromTo(t, t1);
+ } else {
+ partUntilIntersection = elem1.segmentFromTo(t1, t).reversed();
+ }
+ fixedPath.quadTo(partUntilIntersection.controlPoint(), partUntilIntersection.endPoint());
+ }
+
+ // If only one unhandled path is left the decision how to proceed is trivial
+ if (intersections[iC].in1 && intersections[iC].in2 && intersections[iC].out1 && !intersections[iC].out2) {
+ i1 = intersections[iC].e2;
+ t = intersections[iC].t2;
+ forward = true;
+ } else if (intersections[iC].in1 && intersections[iC].in2 && !intersections[iC].out1 && intersections[iC].out2) {
+ i1 = intersections[iC].e1;
+ t = intersections[iC].t1;
+ forward = true;
+ } else if (intersections[iC].in1 && !intersections[iC].in2 && intersections[iC].out1 && intersections[iC].out2) {
+ i1 = intersections[iC].e2;
+ t = intersections[iC].t2;
+ forward = false;
+ } else if (!intersections[iC].in1 && intersections[iC].in2 && intersections[iC].out1 && intersections[iC].out2) {
+ i1 = intersections[iC].e1;
+ t = intersections[iC].t1;
+ forward = false;
+ } else {
+ // If no trivial path is left, calculate the intersection angle to decide which path to move forward.
+ // For winding fill we take the left most path forward, so the inside stays on the right side
+ // For odd even fill we take the right most path forward so we cut of the smallest area.
+ // We come back at the intersection and add the missing pieces as subpaths later on.
+ if (t2 != 0 && t2 != 1) {
+ QVector2D tangent1 = elem1.tangentAtFraction(t1);
+ if (!forward)
+ tangent1 = -tangent1;
+ const QQuadPath::Element &elem2 = path.elementAt(i2);
+ const QVector2D tangent2 = elem2.tangentAtFraction(t2);
+ const float angle = angleBetween(-tangent1, tangent2);
+ qCDebug(lcSGCurveIntersectionSolver) << " Angle at intersection is" << angle;
+ // A small angle. Everything smaller is interpreted as tangent
+ constexpr float deltaAngle = 1e-3f;
+ if ((angle > deltaAngle && path.fillRule() == Qt::WindingFill) || (angle < -deltaAngle && path.fillRule() == Qt::OddEvenFill)) {
+ forward = true;
+ i1 = i2;
+ t = t2;
+ qCDebug(lcSGCurveIntersectionSolver) << " Next going forward from" << t << "on" << i1;
+ } else if ((angle < -deltaAngle && path.fillRule() == Qt::WindingFill) || (angle > deltaAngle && path.fillRule() == Qt::OddEvenFill)) {
+ forward = false;
+ i1 = i2;
+ t = t2;
+ qCDebug(lcSGCurveIntersectionSolver) << " Next going backward from" << t << "on" << i1;
+ } else { // this is basically a tangential touch and and no crossing. So stay on the current path, keep direction
+ qCDebug(lcSGCurveIntersectionSolver) << " Found tangent. Staying on element";
+ }
+ } else {
+ // If we are intersecting exactly at a corner, the trick with the angle does not help.
+ // Therefore we have to rely on finding the next path by looking forward and see if the
+ // path there is valid. This is more expensive than the method above and is therefore
+ // just used as a fallback for corner cases.
+ constexpr float deltaT = 1e-4f;
+ int i2after = i2;
+ float t2after = t2 + deltaT;
+ ensureInBounds(&i2after, &t2after, deltaT);
+ QQuadPath::Element::FillSide fillSideForwardNew = path.fillSideOf(i2after, t2after);
+ if (fillSideForwardNew == QQuadPath::Element::FillSideRight) {
+ forward = true;
+ i1 = i2;
+ t = t2;
+ qCDebug(lcSGCurveIntersectionSolver) << " Next going forward from" << t << "on" << i1;
+ } else {
+ int i2before = i2;
+ float t2before = t2 - deltaT;
+ ensureInBounds(&i2before, &t2before, deltaT);
+ QQuadPath::Element::FillSide fillSideBackwardNew = path.fillSideOf(i2before, t2before);
+ if (fillSideBackwardNew == QQuadPath::Element::FillSideLeft) {
+ forward = false;
+ i1 = i2;
+ t = t2;
+ qCDebug(lcSGCurveIntersectionSolver) << " Next going backward from" << t << "on" << i1;
+ } else {
+ qCDebug(lcSGCurveIntersectionSolver) << " Staying on element.";
+ }
+ }
+ }
+ }
+
+ // Mark the path that takes us away from this intersection as handled on the intersection level.
+ if (!(i1 == startedAtIndex && t == startedAtT))
+ markIntersectionAsHandled(&intersections[iC], i1, forward);
+
+ // If we took all paths from an intersection it can be deleted.
+ if (intersections[iC].in1 && intersections[iC].in2 && intersections[iC].out1 && intersections[iC].out2) {
+ qCDebug(lcSGCurveIntersectionSolver) << " This intersection was processed completely and will be removed";
+ intersections.removeAt(iC);
+ prevIntersection = -1;
+ }
+ regularVisit = false;
+ }
+
+ if (i1 == startedAtIndex && t == startedAtT) {
+ // We reached the point on the subpath where we started. This subpath is done.
+ // We have to find an unhandled subpath or a new subpath that was generated by cuts/intersections.
+ qCDebug(lcSGCurveIntersectionSolver) << "Reached my starting point and try to find a new subpath.";
+
+ // Search for the next subpath to handle.
+ int nextUnhandled = -1;
+ for (int i = 0; i < subPathHandled.size(); i++) {
+ if (!subPathHandled.at(i)) {
+
+ // Not nesesarrily handled (if findStart return false) but if we find no starting
+ // point, we cannot/don't need to handle it anyway. So just mark it as handled.
+ subPathHandled[i] = true;
+
+ if (findStart(path, subPathStartPoints.at(i), subPathEndPoints.at(i), &i1, &forward)) {
+ nextUnhandled = i;
+ qCDebug(lcSGCurveIntersectionSolver) << "Found a new subpath" << i << "to be processed.";
+ startNewSubPath(i1, forward);
+ regularVisit = true;
+ break;
+ }
+ }
+ }
+
+ // If no valid subpath is left, we have to go back to the unhandled intersections.
+ while (nextUnhandled < 0) {
+ qCDebug(lcSGCurveIntersectionSolver) << "All subpaths handled. Looking for unhandled intersections.";
+ if (intersections.isEmpty()) {
+ qCDebug(lcSGCurveIntersectionSolver) << "All intersections handled. I am done.";
+ fixedPath.setHint(QQuadPath::PathNonIntersecting);
+ path = fixedPath;
+ return true;
+ }
+
+ IntersectionData &unhandledIntersec = intersections[0];
+ prevIntersection = 0;
+ regularVisit = false;
+ qCDebug(lcSGCurveIntersectionSolver) << "Revisiting intersection of" << unhandledIntersec.e1 << "with" << unhandledIntersec.e2;
+ qCDebug(lcSGCurveIntersectionSolver) << "Handled are" << unhandledIntersec.e1 << "in:" << unhandledIntersec.in1 << "out:" << unhandledIntersec.out1
+ << "/" << unhandledIntersec.e2 << "in:" << unhandledIntersec.in2 << "out:" << unhandledIntersec.out2;
+
+ // Searching for the correct direction to go forward.
+ // That requires that the intersection + small delta (here 1e-4)
+ // is a valid starting point (filling only on one side)
+ auto lookForwardOnIntersection = [&](bool *handledPath, int nextE, float nextT, bool nextForward) {
+ if (*handledPath)
+ return false;
+ constexpr float deltaT = 1e-4f;
+ int eForward = nextE;
+ float tForward = nextT + (nextForward ? deltaT : -deltaT);
+ ensureInBounds(&eForward, &tForward, deltaT);
+ QQuadPath::Element::FillSide fillSide = path.fillSideOf(eForward, tForward);
+ if ((nextForward && fillSide == QQuadPath::Element::FillSideRight) ||
+ (!nextForward && fillSide == QQuadPath::Element::FillSideLeft)) {
+ fixedPath.moveTo(path.elementAt(nextE).pointAtFraction(nextT));
+ i1 = startedAtIndex = nextE;
+ t = startedAtT = nextT;
+ forward = nextForward;
+ *handledPath = true;
+ return true;
+ }
+ return false;
+ };
+
+ if (lookForwardOnIntersection(&unhandledIntersec.in1, unhandledIntersec.e1, unhandledIntersec.t1, false))
+ break;
+ if (lookForwardOnIntersection(&unhandledIntersec.in2, unhandledIntersec.e2, unhandledIntersec.t2, false))
+ break;
+ if (lookForwardOnIntersection(&unhandledIntersec.out1, unhandledIntersec.e1, unhandledIntersec.t1, true))
+ break;
+ if (lookForwardOnIntersection(&unhandledIntersec.out2, unhandledIntersec.e2, unhandledIntersec.t2, true))
+ break;
+
+ intersections.removeFirst();
+ qCDebug(lcSGCurveIntersectionSolver) << "Found no way to move forward at this intersection and removed it.";
+ }
+ }
+
+ } while (totalIterations < path.elementCount() * 50);
+ // Check the totalIterations as a sanity check. Should never be triggered.
+
+ qCDebug(lcSGCurveIntersectionSolver) << "Could not solve intersections of path. This should not happen. Returning the path unchanged.";
+
+ return false;
+}
+
+
+void QSGCurveProcessor::processStroke(const QQuadPath &strokePath,
+ float miterLimit,
+ float penWidth,
+ Qt::PenJoinStyle joinStyle,
+ Qt::PenCapStyle capStyle,
+ addStrokeTriangleCallback addTriangle,
+ int subdivisions)
+{
+ auto thePath = subdivide(strokePath, subdivisions).flattened(); // TODO: don't flatten, but handle it in the triangulator
+ auto triangles = customTriangulator2(thePath, penWidth, joinStyle, capStyle, miterLimit);
+
+ auto addCurveTriangle = [&](const QQuadPath::Element &element, const TriangleData &t) {
+ addTriangle(t.points,
+ { element.startPoint(), element.controlPoint(), element.endPoint() },
+ t.normals,
+ element.isLine());
+ };
+
+ auto addBevelTriangle = [&](const TrianglePoints &p)
+ {
+ QVector2D fp1 = p[0];
+ QVector2D fp2 = p[2];
+
+ // That describes a path that passes through those points. We want the stroke
+ // edge, so we need to shift everything down by the stroke offset
+
+ QVector2D nn = calcNormalVector(p[0], p[2]);
+ if (determinant(p) < 0)
+ nn = -nn;
+ float delta = penWidth / 2;
+
+ QVector2D offset = nn.normalized() * delta;
+ fp1 += offset;
+ fp2 += offset;
+
+ TrianglePoints n;
+ // p1 is inside, so n[1] is {0,0}
+ n[0] = (p[0] - p[1]).normalized();
+ n[2] = (p[2] - p[1]).normalized();
+
+ addTriangle(p, { fp1, QVector2D(0.0f, 0.0f), fp2 }, n, true);
+ };
+
+ for (const auto &triangle : triangles) {
+ if (triangle.pathElementIndex < 0) {
+ addBevelTriangle(triangle.points);
+ continue;
+ }
+ const auto &element = thePath.elementAt(triangle.pathElementIndex);
+ addCurveTriangle(element, triangle);
+ }
+}
+
+// 2x variant of qHash(float)
+inline size_t qHash(QVector2D key, size_t seed = 0) noexcept
+{
+ Q_STATIC_ASSERT(sizeof(QVector2D) == sizeof(quint64));
+ // ensure -0 gets mapped to 0
+ key[0] += 0.0f;
+ key[1] += 0.0f;
+ quint64 k;
+ memcpy(&k, &key, sizeof(QVector2D));
+ return QHashPrivate::hash(k, seed);
+}
+
+void QSGCurveProcessor::processFill(const QQuadPath &fillPath,
+ Qt::FillRule fillRule,
+ addTriangleCallback addTriangle)
+{
+ QPainterPath internalHull;
+ internalHull.setFillRule(fillRule);
+
+ QMultiHash<QVector2D, int> pointHash;
+
+ auto roundVec2D = [](const QVector2D &p) -> QVector2D {
+ return { qRound64(p.x() * 32.0f) / 32.0f, qRound64(p.y() * 32.0f) / 32.0f };
+ };
+
+ auto addCurveTriangle = [&](const QQuadPath::Element &element,
+ const QVector2D &sp,
+ const QVector2D &ep,
+ const QVector2D &cp) {
+ addTriangle({ sp, cp, ep },
+ {},
+ [&element](QVector2D v) { return elementUvForPoint(element, v); });
+ };
+
+ auto addCurveTriangleWithNormals = [&](const QQuadPath::Element &element,
+ const std::array<QVector2D, 3> &v,
+ const std::array<QVector2D, 3> &n) {
+ addTriangle(v, n, [&element](QVector2D v) { return elementUvForPoint(element, v); });
+ };
+
+ auto outsideNormal = [](const QVector2D &startPoint,
+ const QVector2D &endPoint,
+ const QVector2D &insidePoint) {
+
+ QVector2D baseLine = endPoint - startPoint;
+ QVector2D insideVector = insidePoint - startPoint;
+ QVector2D normal = normalVector(baseLine);
+
+ bool swap = QVector2D::dotProduct(insideVector, normal) < 0;
+
+ return swap ? normal : -normal;
+ };
+
+ auto addTriangleForLine = [&](const QQuadPath::Element &element,
+ const QVector2D &sp,
+ const QVector2D &ep,
+ const QVector2D &cp) {
+ addCurveTriangle(element, sp, ep, cp);
+
+ // Add triangles on the outer side to make room for AA
+ const QVector2D normal = outsideNormal(sp, ep, cp);
+ constexpr QVector2D null;
+ addCurveTriangleWithNormals(element, {sp, sp, ep}, {null, normal, null});
+ addCurveTriangleWithNormals(element, {sp, ep, ep}, {normal, normal, null});
+ };
+
+ auto addTriangleForConcave = [&](const QQuadPath::Element &element,
+ const QVector2D &sp,
+ const QVector2D &ep,
+ const QVector2D &cp) {
+ addTriangleForLine(element, sp, ep, cp);
+ };
+
+ auto addTriangleForConvex = [&](const QQuadPath::Element &element,
+ const QVector2D &sp,
+ const QVector2D &ep,
+ const QVector2D &cp) {
+ addCurveTriangle(element, sp, ep, cp);
+ // Add two triangles on the outer side to get some more AA
+
+ constexpr QVector2D null;
+ // First triangle on the line sp-cp, replacing ep
+ {
+ const QVector2D normal = outsideNormal(sp, cp, ep);
+ addCurveTriangleWithNormals(element, {sp, sp, cp}, {null, normal, null});
+ }
+
+ // Second triangle on the line ep-cp, replacing sp
+ {
+ const QVector2D normal = outsideNormal(ep, cp, sp);
+ addCurveTriangleWithNormals(element, {ep, ep, cp}, {null, normal, null});
+ }
+ };
+
+ auto addFillTriangle = [&](const QVector2D &p1, const QVector2D &p2, const QVector2D &p3) {
+ constexpr QVector3D uv(0.0, 1.0, -1.0);
+ addTriangle({ p1, p2, p3 },
+ {},
+ [&uv](QVector2D) { return uv; });
+ };
+
+ fillPath.iterateElements([&](const QQuadPath::Element &element, int index) {
+ QVector2D sp(element.startPoint());
+ QVector2D cp(element.controlPoint());
+ QVector2D ep(element.endPoint());
+ QVector2D rsp = roundVec2D(sp);
+
+ if (element.isSubpathStart())
+ internalHull.moveTo(sp.toPointF());
+ if (element.isLine()) {
+ internalHull.lineTo(ep.toPointF());
+ pointHash.insert(rsp, index);
+ } else {
+ QVector2D rep = roundVec2D(ep);
+ QVector2D rcp = roundVec2D(cp);
+ if (element.isConvex()) {
+ internalHull.lineTo(ep.toPointF());
+ addTriangleForConvex(element, rsp, rep, rcp);
+ pointHash.insert(rsp, index);
+ } else {
+ internalHull.lineTo(cp.toPointF());
+ internalHull.lineTo(ep.toPointF());
+ addTriangleForConcave(element, rsp, rep, rcp);
+ pointHash.insert(rcp, index);
+ }
+ }
+ });
+
+ // Points in p are already rounded do 1/32
+ // Returns false if the triangle needs to be split. Adds the triangle to the graphics buffers and returns true otherwise.
+ // (Does not handle ambiguous vertices that are on multiple unrelated lines/curves)
+ auto onSameSideOfLine = [](const QVector2D &p1,
+ const QVector2D &p2,
+ const QVector2D &linePoint,
+ const QVector2D &lineNormal) {
+ float side1 = testSideOfLineByNormal(linePoint, lineNormal, p1);
+ float side2 = testSideOfLineByNormal(linePoint, lineNormal, p2);
+ return side1 * side2 >= 0;
+ };
+
+ auto pointInSafeSpace = [&](const QVector2D &p, const QQuadPath::Element &element) -> bool {
+ const QVector2D a = element.startPoint();
+ const QVector2D b = element.endPoint();
+ const QVector2D c = element.controlPoint();
+ // There are "safe" areas of the curve also across the baseline: the curve can never cross:
+ // line1: the line through A and B'
+ // line2: the line through B and A'
+ // Where A' = A "mirrored" through C and B' = B "mirrored" through C
+ const QVector2D n1 = calcNormalVector(a, c + (c - b));
+ const QVector2D n2 = calcNormalVector(b, c + (c - a));
+ bool safeSideOf1 = onSameSideOfLine(p, c, a, n1);
+ bool safeSideOf2 = onSameSideOfLine(p, c, b, n2);
+ return safeSideOf1 && safeSideOf2;
+ };
+
+ // Returns false if the triangle belongs to multiple elements and need to be split.
+ // Otherwise adds the triangle, optionally splitting it to avoid "unsafe space"
+ auto handleTriangle = [&](const QVector2D (&p)[3]) -> bool {
+ bool isLine = false;
+ bool isConcave = false;
+ bool isConvex = false;
+ int elementIndex = -1;
+
+ bool foundElement = false;
+ int si = -1;
+ int ei = -1;
+
+ for (int i = 0; i < 3; ++i) {
+ auto pointFoundRange = std::as_const(pointHash).equal_range(roundVec2D(p[i]));
+
+ if (pointFoundRange.first == pointHash.constEnd())
+ continue;
+
+ // This point is on some element, now find the element
+ int testIndex = *pointFoundRange.first;
+ bool ambiguous = std::next(pointFoundRange.first) != pointFoundRange.second;
+ if (ambiguous) {
+ // The triangle should be on the inside of exactly one of the elements
+ // We're doing the test for each of the points, which maybe duplicates some effort,
+ // but optimize for simplicity for now.
+ for (auto it = pointFoundRange.first; it != pointFoundRange.second; ++it) {
+ auto &el = fillPath.elementAt(*it);
+ bool fillOnLeft = !el.isFillOnRight();
+ auto sp = roundVec2D(el.startPoint());
+ auto ep = roundVec2D(el.endPoint());
+ // Check if the triangle is on the inside of el; i.e. each point is either sp, ep, or on the inside.
+ auto pointInside = [&](const QVector2D &p) {
+ return p == sp || p == ep
+ || QQuadPath::isPointOnLeft(p, el.startPoint(), el.endPoint()) == fillOnLeft;
+ };
+ if (pointInside(p[0]) && pointInside(p[1]) && pointInside(p[2])) {
+ testIndex = *it;
+ break;
+ }
+ }
+ }
+
+ const auto &element = fillPath.elementAt(testIndex);
+ // Now we check if p[i] -> p[j] is on the element for some j
+ // For a line, the relevant line is sp-ep
+ // For concave it's cp-sp/ep
+ // For convex it's sp-ep again
+ bool onElement = false;
+ for (int j = 0; j < 3; ++j) {
+ if (i == j)
+ continue;
+ if (element.isConvex() || element.isLine())
+ onElement = roundVec2D(element.endPoint()) == p[j];
+ else // concave
+ onElement = roundVec2D(element.startPoint()) == p[j] || roundVec2D(element.endPoint()) == p[j];
+ if (onElement) {
+ if (foundElement)
+ return false; // Triangle already on some other element: must split
+ si = i;
+ ei = j;
+ foundElement = true;
+ elementIndex = testIndex;
+ isConvex = element.isConvex();
+ isLine = element.isLine();
+ isConcave = !isLine && !isConvex;
+ break;
+ }
+ }
+ }
+
+ if (isLine) {
+ int ci = (6 - si - ei) % 3; // 1+2+3 is 6, so missing number is 6-n1-n2
+ addTriangleForLine(fillPath.elementAt(elementIndex), p[si], p[ei], p[ci]);
+ } else if (isConcave) {
+ addCurveTriangle(fillPath.elementAt(elementIndex), p[0], p[1], p[2]);
+ } else if (isConvex) {
+ int oi = (6 - si - ei) % 3;
+ const auto &otherPoint = p[oi];
+ const auto &element = fillPath.elementAt(elementIndex);
+ // We have to test whether the triangle can cross the line
+ // TODO: use the toplevel element's safe space
+ bool safeSpace = pointInSafeSpace(otherPoint, element);
+ if (safeSpace) {
+ addCurveTriangle(element, p[0], p[1], p[2]);
+ } else {
+ // Find a point inside the triangle that's also in the safe space
+ QVector2D newPoint = (p[0] + p[1] + p[2]) / 3;
+ // We should calculate the point directly, but just do a lazy implementation for now:
+ for (int i = 0; i < 7; ++i) {
+ safeSpace = pointInSafeSpace(newPoint, element);
+ if (safeSpace)
+ break;
+ newPoint = (p[si] + p[ei] + newPoint) / 3;
+ }
+ if (safeSpace) {
+ // Split triangle. We know the original triangle is only on one path element, so the other triangles are both fill.
+ // Curve triangle is (sp, ep, np)
+ addCurveTriangle(element, p[si], p[ei], newPoint);
+ // The other two are (sp, op, np) and (ep, op, np)
+ addFillTriangle(p[si], p[oi], newPoint);
+ addFillTriangle(p[ei], p[oi], newPoint);
+ } else {
+ // fallback to fill if we can't find a point in safe space
+ addFillTriangle(p[0], p[1], p[2]);
+ }
+ }
+
+ } else {
+ addFillTriangle(p[0], p[1], p[2]);
+ }
+ return true;
+ };
+
+ QTriangleSet triangles = qTriangulate(internalHull);
+ // Workaround issue in qTriangulate() for single-triangle path
+ if (triangles.indices.size() == 3)
+ triangles.indices.setDataUint({ 0, 1, 2 });
+
+ const quint32 *idxTable = static_cast<const quint32 *>(triangles.indices.data());
+ for (int triangle = 0; triangle < triangles.indices.size() / 3; ++triangle) {
+ const quint32 *idx = &idxTable[triangle * 3];
+
+ QVector2D p[3];
+ for (int i = 0; i < 3; ++i) {
+ p[i] = roundVec2D(QVector2D(float(triangles.vertices.at(idx[i] * 2)),
+ float(triangles.vertices.at(idx[i] * 2 + 1))));
+ }
+ if (qFuzzyIsNull(determinant(p[0], p[1], p[2])))
+ continue; // Skip degenerate triangles
+ bool needsSplit = !handleTriangle(p);
+ if (needsSplit) {
+ QVector2D c = (p[0] + p[1] + p[2]) / 3;
+ for (int i = 0; i < 3; ++i) {
+ qSwap(c, p[i]);
+ handleTriangle(p);
+ qSwap(c, p[i]);
+ }
+ }
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcurveprocessor_p.h b/src/quick/scenegraph/qsgcurveprocessor_p.h
new file mode 100644
index 0000000000..cdd93bc58e
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurveprocessor_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGCURVEPROCESSOR_P_H
+#define QSGCURVEPROCESSOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/qtquickexports.h>
+#include "util/qquadpath_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGCurveProcessor
+{
+public:
+ typedef std::function<QVector3D(QVector2D)> uvForPointCallback;
+ typedef std::function<void(const std::array<QVector2D, 3> &,
+ const std::array<QVector2D, 3> &,
+ uvForPointCallback)> addTriangleCallback;
+ typedef std::function<void(const std::array<QVector2D, 3> &,
+ const std::array<QVector2D, 3> &,
+ const std::array<QVector2D, 3> &,
+ bool)> addStrokeTriangleCallback;
+
+ static void processFill(const QQuadPath &path,
+ Qt::FillRule fillRule,
+ addTriangleCallback addTriangle);
+ static void processStroke(const QQuadPath &strokePath,
+ float miterLimit,
+ float penWidth,
+ Qt::PenJoinStyle joinStyle,
+ Qt::PenCapStyle capStyle,
+ addStrokeTriangleCallback addTriangle,
+ int subdivisions = 3);
+ static bool solveOverlaps(QQuadPath &path);
+ static QList<QPair<int, int>> findOverlappingCandidates(const QQuadPath &path);
+ static bool removeNestedSubpaths(QQuadPath &path);
+ static bool solveIntersections(QQuadPath &path, bool removeNestedPaths = true);
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGCURVEPROCESSOR_P_H
diff --git a/src/quick/scenegraph/qsgcurvestrokenode.cpp b/src/quick/scenegraph/qsgcurvestrokenode.cpp
new file mode 100644
index 0000000000..2bfaff1b4e
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurvestrokenode.cpp
@@ -0,0 +1,112 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgcurvestrokenode_p.h"
+#include "qsgcurvestrokenode_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGCurveStrokeNode::QSGCurveStrokeNode()
+{
+ setFlag(OwnsGeometry, true);
+ setGeometry(new QSGGeometry(attributes(), 0, 0));
+ updateMaterial();
+}
+
+void QSGCurveStrokeNode::QSGCurveStrokeNode::updateMaterial()
+{
+ m_material.reset(new QSGCurveStrokeMaterial(this));
+ setMaterial(m_material.data());
+}
+
+// Take the start, control and end point of a curve and return the points A, B, C
+// representing the curve as Q(s) = A*s*s + B*s + C
+std::array<QVector2D, 3> QSGCurveStrokeNode::curveABC(const std::array<QVector2D, 3> &p)
+{
+ QVector2D a = p[0] - 2*p[1] + p[2];
+ QVector2D b = 2*p[1] - 2*p[0];
+ QVector2D c = p[0];
+
+ return {a, b, c};
+}
+
+// Curve from p[0] to p[2] with control point p[1]
+void QSGCurveStrokeNode::appendTriangle(const std::array<QVector2D, 3> &v,
+ const std::array<QVector2D, 3> &p,
+ const std::array<QVector2D, 3> &n)
+{
+ auto abc = curveABC(p);
+
+ int currentVertex = m_uncookedVertexes.count();
+
+ for (int i = 0; i < 3; ++i) {
+ m_uncookedVertexes.append( { v[i].x(), v[i].y(),
+ abc[0].x(), abc[0].y(), abc[1].x(), abc[1].y(), abc[2].x(), abc[2].y(),
+ n[i].x(), n[i].y() } );
+ }
+ m_uncookedIndexes << currentVertex << currentVertex + 1 << currentVertex + 2;
+}
+
+// Straight line from p0 to p1
+void QSGCurveStrokeNode::appendTriangle(const std::array<QVector2D, 3> &v,
+ const std::array<QVector2D, 2> &p,
+ const std::array<QVector2D, 3> &n)
+{
+ // We could reduce this to a linear equation by setting A to (0,0).
+ // However, then we cannot use the cubic solution and need an additional
+ // code path in the shader. The following formulation looks more complicated
+ // but allows to always use the cubic solution.
+ auto A = p[1] - p[0];
+ auto B = QVector2D(0., 0.);
+ auto C = p[0];
+
+ int currentVertex = m_uncookedVertexes.count();
+
+// for (auto v : QList<QPair<QVector2D, QVector2D>>({{v0, n0}, {v1, n1}, {v2, n2}})) {
+ for (int i = 0; i < 3; ++i) {
+ m_uncookedVertexes.append( { v[i].x(), v[i].y(),
+ A.x(), A.y(), B.x(), B.y(), C.x(), C.y(),
+ n[i].x(), n[i].y() } );
+ }
+ m_uncookedIndexes << currentVertex << currentVertex + 1 << currentVertex + 2;
+}
+
+void QSGCurveStrokeNode::cookGeometry()
+{
+ QSGGeometry *g = geometry();
+ if (g->indexType() != QSGGeometry::UnsignedIntType) {
+ g = new QSGGeometry(attributes(),
+ m_uncookedVertexes.size(),
+ m_uncookedIndexes.size(),
+ QSGGeometry::UnsignedIntType);
+ setGeometry(g);
+ } else {
+ g->allocate(m_uncookedVertexes.size(), m_uncookedIndexes.size());
+ }
+
+ g->setDrawingMode(QSGGeometry::DrawTriangles);
+ memcpy(g->vertexData(),
+ m_uncookedVertexes.constData(),
+ g->vertexCount() * g->sizeOfVertex());
+ memcpy(g->indexData(),
+ m_uncookedIndexes.constData(),
+ g->indexCount() * g->sizeOfIndex());
+
+ m_uncookedIndexes.clear();
+ m_uncookedVertexes.clear();
+}
+
+const QSGGeometry::AttributeSet &QSGCurveStrokeNode::attributes()
+{
+ static QSGGeometry::Attribute data[] = {
+ QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), //vertexCoord
+ QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //A
+ QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //B
+ QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //C
+ QSGGeometry::Attribute::createWithAttributeType(4, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //normalVector
+ };
+ static QSGGeometry::AttributeSet attrs = { 5, sizeof(StrokeVertex), data };
+ return attrs;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcurvestrokenode_p.cpp b/src/quick/scenegraph/qsgcurvestrokenode_p.cpp
new file mode 100644
index 0000000000..b86493b169
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurvestrokenode_p.cpp
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgcurvestrokenode_p_p.h"
+#include "qsgcurvestrokenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+bool QSGCurveStrokeMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 64);
+ const int matrixCount = qMin(state.projectionMatrixCount(), newEffect->viewCount());
+
+ auto *newMaterial = static_cast<QSGCurveStrokeMaterial *>(newEffect);
+ auto *oldMaterial = static_cast<QSGCurveStrokeMaterial *>(oldEffect);
+
+ auto *newNode = newMaterial != nullptr ? newMaterial->node() : nullptr;
+ auto *oldNode = oldMaterial != nullptr ? oldMaterial->node() : nullptr;
+
+ if (state.isMatrixDirty()) {
+ float localScale = newNode != nullptr ? newNode->localScale() : 1.0f;
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ m.scale(localScale);
+ memcpy(buf->data() + viewIndex * 64, m.constData(), 64);
+ }
+ float matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio() * localScale;
+ memcpy(buf->data() + matrixCount * 64, &matrixScale, 4);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + matrixCount * 64 + 4, &opacity, 4);
+ changed = true;
+ }
+
+ int offset = matrixCount * 64 + 16;
+ if (newNode == nullptr)
+ return changed;
+
+ QVector4D newStrokeColor(newNode->color().redF(),
+ newNode->color().greenF(),
+ newNode->color().blueF(),
+ newNode->color().alphaF());
+ QVector4D oldStrokeColor = oldNode != nullptr
+ ? QVector4D(oldNode->color().redF(),
+ oldNode->color().greenF(),
+ oldNode->color().blueF(),
+ oldNode->color().alphaF())
+ : QVector4D{};
+
+ if (oldNode == nullptr || oldStrokeColor != newStrokeColor) {
+ memcpy(buf->data() + offset, &newStrokeColor, 16);
+ changed = true;
+ }
+ offset += 16;
+
+ if (oldNode == nullptr || newNode->strokeWidth() != oldNode->strokeWidth()) {
+ float w = newNode->strokeWidth();
+ memcpy(buf->data() + offset, &w, 4);
+ changed = true;
+ }
+ offset += 4;
+ if (oldNode == nullptr || newNode->debug() != oldNode->debug()) {
+ float w = newNode->debug();
+ memcpy(buf->data() + offset, &w, 4);
+ changed = true;
+ }
+// offset += 4;
+
+ return changed;
+}
+
+int QSGCurveStrokeMaterial::compare(const QSGMaterial *other) const
+{
+ int typeDif = type() - other->type();
+ if (!typeDif) {
+ auto *othernode = static_cast<const QSGCurveStrokeMaterial*>(other)->node();
+ if (node()->color() != othernode->color())
+ return node()->color().rgb() < othernode->color().rgb() ? -1 : 1;
+ if (node()->strokeWidth() != othernode->strokeWidth())
+ return node()->strokeWidth() < othernode->strokeWidth() ? -1 : 1;
+ }
+ return typeDif;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcurvestrokenode_p.h b/src/quick/scenegraph/qsgcurvestrokenode_p.h
new file mode 100644
index 0000000000..079414bc16
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurvestrokenode_p.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGCURVESTROKENODE_P_H
+#define QSGCURVESTROKENODE_P_H
+
+#include <QtQuick/qtquickexports.h>
+#include <QtQuick/qsgnode.h>
+
+#include "qsgcurveabstractnode_p.h"
+#include "qsgcurvestrokenode_p_p.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGCurveStrokeNode : public QSGCurveAbstractNode
+{
+public:
+ QSGCurveStrokeNode();
+
+ void setColor(QColor col) override
+ {
+ m_color = col;
+ }
+
+ QColor color() const
+ {
+ return m_color;
+ }
+
+ void setStrokeWidth(float width)
+ {
+ m_strokeWidth = width;
+ }
+
+ float strokeWidth() const
+ {
+ return m_strokeWidth;
+ }
+
+ void appendTriangle(const std::array<QVector2D, 3> &v, // triangle vertices
+ const std::array<QVector2D, 3> &p, // curve points
+ const std::array<QVector2D, 3> &n); // vertex normals
+ void appendTriangle(const std::array<QVector2D, 3> &v, // triangle vertices
+ const std::array<QVector2D, 2> &p, // line points
+ const std::array<QVector2D, 3> &n); // vertex normals
+
+ void cookGeometry() override;
+
+ static const QSGGeometry::AttributeSet &attributes();
+
+ QVector<quint32> uncookedIndexes() const
+ {
+ return m_uncookedIndexes;
+ }
+
+ float debug() const
+ {
+ return m_debug;
+ }
+
+ void setDebug(float newDebug)
+ {
+ m_debug = newDebug;
+ }
+
+ void setLocalScale(float scale)
+ {
+ m_localScale = scale;
+ }
+
+ float localScale() const
+ {
+ return m_localScale;
+ }
+
+private:
+
+ struct StrokeVertex
+ {
+ float x, y;
+ float ax, ay;
+ float bx, by;
+ float cx, cy;
+ float nx, ny; //normal vector: direction to move vertext to account for AA
+ };
+
+ void updateMaterial();
+
+ static std::array<QVector2D, 3> curveABC(const std::array<QVector2D, 3> &p);
+
+ QColor m_color;
+ float m_strokeWidth = 0.0f;
+ float m_debug = 0.0f;
+ float m_localScale = 1.0f;
+
+protected:
+ QScopedPointer<QSGCurveStrokeMaterial> m_material;
+
+ QVector<StrokeVertex> m_uncookedVertexes;
+ QVector<quint32> m_uncookedIndexes;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGCURVESTROKENODE_P_H
diff --git a/src/quick/scenegraph/qsgcurvestrokenode_p_p.h b/src/quick/scenegraph/qsgcurvestrokenode_p_p.h
new file mode 100644
index 0000000000..847098f8cc
--- /dev/null
+++ b/src/quick/scenegraph/qsgcurvestrokenode_p_p.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGCURVESTROKENODE_P_P_H
+#define QSGCURVESTROKENODE_P_P_H
+
+#include <QtQuick/qtquickexports.h>
+#include <QtQuick/qsgmaterial.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QSGCurveStrokeNode;
+class QSGCurveStrokeMaterial;
+
+class Q_QUICK_EXPORT QSGCurveStrokeMaterialShader : public QSGMaterialShader
+{
+public:
+ QSGCurveStrokeMaterialShader(int viewCount)
+ {
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shapestroke.vert.qsb"),
+ viewCount);
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shapestroke.frag.qsb"),
+ viewCount);
+ }
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+};
+
+
+class Q_QUICK_EXPORT QSGCurveStrokeMaterial : public QSGMaterial
+{
+public:
+ QSGCurveStrokeMaterial(QSGCurveStrokeNode *node)
+ : m_node(node)
+ {
+ setFlag(Blending, true);
+ }
+
+ int compare(const QSGMaterial *other) const override;
+
+ QSGCurveStrokeNode *node() const
+ {
+ return m_node;
+ }
+
+protected:
+ QSGMaterialType *type() const override
+ {
+ static QSGMaterialType t;
+ return &t;
+ }
+ QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override
+ {
+ return new QSGCurveStrokeMaterialShader(viewCount());
+ }
+
+ QSGCurveStrokeNode *m_node;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGCURVESTROKENODE_P_P_H
diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp
index f3f85445e5..7d1daa1716 100644
--- a/src/quick/scenegraph/qsgdefaultcontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultcontext.cpp
@@ -7,6 +7,7 @@
#include <QtQuick/private/qsgdefaultinternalimagenode_p.h>
#include <QtQuick/private/qsgdefaultpainternode_p.h>
#include <QtQuick/private/qsgdefaultglyphnode_p.h>
+#include <QtQuick/private/qsgcurveglyphnode_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h>
#include <QtQuick/private/qsgrhisupport_p.h>
@@ -19,6 +20,8 @@
#include <QtQuick/private/qsgdefaultspritenode_p.h>
#endif
#include <QtQuick/private/qsgrhishadereffectnode_p.h>
+#include <QtQuick/private/qsginternaltextnode_p.h>
+#include <QtQuick/private/qsgrhiinternaltextnode_p.h>
#include <QOpenGLContext>
@@ -29,10 +32,7 @@
#include <algorithm>
-#include <QtGui/private/qrhi_p.h>
-#if QT_CONFIG(opengl)
-# include <QtGui/private/qrhigles2_p.h>
-#endif
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -147,11 +147,18 @@ QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item)
return new QSGDefaultPainterNode(item);
}
+QSGInternalTextNode *QSGDefaultContext::createInternalTextNode(QSGRenderContext *renderContext)
+{
+ return new QSGRhiInternalTextNode(renderContext);
+}
+
QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc,
- bool preferNativeGlyphNode,
+ QSGTextNode::RenderType renderType,
int renderTypeQuality)
{
- if (m_distanceFieldDisabled || preferNativeGlyphNode) {
+ if (renderType == QSGTextNode::CurveRendering) {
+ return new QSGCurveGlyphNode(rc);
+ } else if (m_distanceFieldDisabled || renderType == QSGTextNode::NativeRendering) {
return new QSGDefaultGlyphNode(rc);
} else {
QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc);
diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h
index 8dd507bd9a..5f35e7b451 100644
--- a/src/quick/scenegraph/qsgdefaultcontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultcontext_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultContext : public QSGContext, public QSGRendererInterface
+class Q_QUICK_EXPORT QSGDefaultContext : public QSGContext, public QSGRendererInterface
{
public:
QSGDefaultContext(QObject *parent = nullptr);
@@ -33,7 +33,8 @@ public:
QSGInternalRectangleNode *createInternalRectangleNode() override;
QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
- QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode, int renderTypeQuality) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, QSGTextNode::RenderType renderType, int renderTypeQuality) override;
+ QSGInternalTextNode *createInternalTextNode(QSGRenderContext *renderContext) override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
QSurfaceFormat defaultSurfaceFormat() const override;
QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
index f9133455c8..445f338bbd 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
@@ -47,11 +47,14 @@ void QSGDefaultGlyphNode::update()
QRawFont font = m_glyphs.rawFont();
QMargins margins(0, 0, 0, 0);
- if (m_style == QQuickText::Normal) {
+ const auto *fontEngine = QRawFontPrivate::get(font)->fontEngine;
+ const bool isColorFont = fontEngine->glyphFormat == QFontEngine::Format_ARGB;
+
+ if (m_style == QQuickText::Normal || isColorFont) {
QFontEngine::GlyphFormat glyphFormat;
// Don't try to override glyph format of color fonts
- if (QRawFontPrivate::get(font)->fontEngine->glyphFormat == QFontEngine::Format_ARGB) {
+ if (isColorFont) {
glyphFormat = QFontEngine::Format_None;
} else {
switch (m_preferredAntialiasingMode) {
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 43a7e919bc..3d6cabe6fc 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -17,45 +17,16 @@
QT_BEGIN_NAMESPACE
-#ifndef GL_FRAMEBUFFER_SRGB
-#define GL_FRAMEBUFFER_SRGB 0x8DB9
-#endif
-
-#ifndef GL_FRAMEBUFFER_SRGB_CAPABLE
-#define GL_FRAMEBUFFER_SRGB_CAPABLE 0x8DBA
-#endif
-
static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity)
{
float o = c.w() * globalOpacity;
return QVector4D(c.x() * o, c.y() * o, c.z() * o, o);
}
-#if 0
-static inline qreal qt_sRGB_to_linear_RGB(qreal f)
-{
- return f > 0.04045 ? qPow((f + 0.055) / 1.055, 2.4) : f / 12.92;
-}
-
-static inline QVector4D qt_sRGB_to_linear_RGB(const QVector4D &color)
-{
- return QVector4D(qt_sRGB_to_linear_RGB(color.x()),
- qt_sRGB_to_linear_RGB(color.y()),
- qt_sRGB_to_linear_RGB(color.z()),
- color.w());
-}
-
-static inline qreal fontSmoothingGamma()
-{
- static qreal fontSmoothingGamma = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
- return fontSmoothingGamma;
-}
-#endif
-
class QSGTextMaskRhiShader : public QSGMaterialShader
{
public:
- QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat);
+ QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat, int viewCount);
bool updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
@@ -64,28 +35,25 @@ public:
protected:
QFontEngine::GlyphFormat m_glyphFormat;
+ quint32 m_currentUbufOffset;
};
-QSGTextMaskRhiShader::QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat)
+QSGTextMaskRhiShader::QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat, int viewCount)
: m_glyphFormat(glyphFormat)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.vert.qsb"));
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.frag.qsb"), viewCount);
}
-enum UbufOffset {
- ModelViewMatrixOffset = 0,
- ProjectionMatrixOffset = ModelViewMatrixOffset + 64,
- ColorOffset = ProjectionMatrixOffset + 64,
- TextureScaleOffset = ColorOffset + 16,
- DprOffset = TextureScaleOffset + 8,
-
- // + 1 float padding (vec4 must be aligned to 16)
- StyleColorOffset = DprOffset + 4 + 4,
- ShiftOffset = StyleColorOffset + 16
-};
+// uniform block layout:
+// mat4 modelViewMatrix
+// mat4 projectionMatrix or mat4 projectionMatrix[QSHADER_VIEW_COUNT]
+// vec2 textureScale
+// float dpr
+// vec4 color
+// [styled/outline only]
+// vec4 styleColor
+// vec2 shift
bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
@@ -102,34 +70,46 @@ bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
bool changed = false;
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= DprOffset + 4);
+ const int projectionMatrixCount = qMin(state.projectionMatrixCount(), newMaterial->viewCount());
+ quint32 offset = 0; // ModelViewMatrix
if (state.isMatrixDirty()) {
const QMatrix4x4 mv = state.modelViewMatrix();
- memcpy(buf->data() + ModelViewMatrixOffset, mv.constData(), 64);
- const QMatrix4x4 p = state.projectionMatrix();
- memcpy(buf->data() + ProjectionMatrixOffset, p.constData(), 64);
-
+ memcpy(buf->data() + offset, mv.constData(), 64);
changed = true;
}
+ offset += 64; // now at ProjectionMatrix or ProjectionMatrix[0]
+
+ for (int viewIndex = 0; viewIndex < projectionMatrixCount; ++viewIndex) {
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 p = state.projectionMatrix(viewIndex);
+ memcpy(buf->data() + offset, p.constData(), 64);
+ changed = true;
+ }
+ offset += 64;
+ }
+ // offset is now at TextureScale
QRhiTexture *oldRtex = oldMat ? oldMat->texture()->rhiTexture() : nullptr;
QRhiTexture *newRtex = mat->texture()->rhiTexture();
if (updated || !oldMat || oldRtex != newRtex) {
const QVector2D textureScale = QVector2D(1.0f / mat->rhiGlyphCache()->width(),
1.0f / mat->rhiGlyphCache()->height());
- memcpy(buf->data() + TextureScaleOffset, &textureScale, 8);
+ memcpy(buf->data() + offset, &textureScale, 8);
changed = true;
}
+ offset += 8; // now at Dpr
if (!oldMat) {
float dpr = state.devicePixelRatio();
- memcpy(buf->data() + DprOffset, &dpr, 4);
+ memcpy(buf->data() + offset, &dpr, 4);
}
+ offset += 4 + 4; // now at Color (with padding to ensure vec4 alignment)
// move texture uploads/copies onto the renderer's soon-to-be-committed list
mat->rhiGlyphCache()->commitResourceUpdates(state.resourceUpdateBatch());
+ m_currentUbufOffset = offset;
return changed;
}
@@ -149,15 +129,13 @@ void QSGTextMaskRhiShader::updateSampledImage(RenderState &state, int binding, Q
class QSG8BitTextMaskRhiShader : public QSGTextMaskRhiShader
{
public:
- QSG8BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
- : QSGTextMaskRhiShader(glyphFormat)
+ QSG8BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat, int viewCount, bool alphaTexture)
+ : QSGTextMaskRhiShader(glyphFormat, viewCount)
{
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask_a.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask_a.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
@@ -172,13 +150,13 @@ bool QSG8BitTextMaskRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
- memcpy(buf->data() + ColorOffset, &color, 16);
+ memcpy(buf->data() + m_currentUbufOffset, &color, 16);
changed = true;
}
+ m_currentUbufOffset += 16; // now at StyleColor
return changed;
}
@@ -186,12 +164,11 @@ bool QSG8BitTextMaskRhiShader::updateUniformData(RenderState &state,
class QSG24BitTextMaskRhiShader : public QSGTextMaskRhiShader
{
public:
- QSG24BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat)
- : QSGTextMaskRhiShader(glyphFormat)
+ QSG24BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat, int viewCount)
+ : QSGTextMaskRhiShader(glyphFormat, viewCount)
{
setFlag(UpdatesGraphicsPipelineState, true);
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/24bittextmask.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/24bittextmask.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
@@ -199,13 +176,6 @@ public:
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
-// ### gamma correction (sRGB) Unsurprisingly, the GL approach is not portable
-// to anything else - it just does not work that way, there is no opt-in/out
-// switch and magic winsys-provided maybe-sRGB buffers. When requesting an sRGB
-// QRhiSwapChain (which we do not do), it is full sRGB, with the sRGB
-// framebuffer update and blending always on... Could we do gamma correction in
-// the shader for text? (but that's bad for blending?)
-
bool QSG24BitTextMaskRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
@@ -215,14 +185,14 @@ bool QSG24BitTextMaskRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
// shader takes vec4 but uses alpha only; coloring happens via the blend constant
const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
- memcpy(buf->data() + ColorOffset, &color, 16);
+ memcpy(buf->data() + m_currentUbufOffset, &color, 16);
changed = true;
}
+ m_currentUbufOffset += 16; // now at StyleColor
return changed;
}
@@ -240,11 +210,8 @@ bool QSG24BitTextMaskRhiShader::updateGraphicsPipelineState(RenderState &state,
QVector4D color = mat->color();
- // if (useSRGB())
- // color = qt_sRGB_to_linear_RGB(color);
-
// this is dynamic state but it's - magic! - taken care of by the renderer
- ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z(), color.w());
+ ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z());
return true;
}
@@ -252,11 +219,10 @@ bool QSG24BitTextMaskRhiShader::updateGraphicsPipelineState(RenderState &state,
class QSG32BitColorTextRhiShader : public QSGTextMaskRhiShader
{
public:
- QSG32BitColorTextRhiShader(QFontEngine::GlyphFormat glyphFormat)
- : QSGTextMaskRhiShader(glyphFormat)
+ QSG32BitColorTextRhiShader(QFontEngine::GlyphFormat glyphFormat, int viewCount)
+ : QSGTextMaskRhiShader(glyphFormat, viewCount)
{
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/32bitcolortext.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/32bitcolortext.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
@@ -271,14 +237,14 @@ bool QSG32BitColorTextRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
// shader takes vec4 but uses alpha only
const QVector4D color(0, 0, 0, mat->color().w() * state.opacity());
- memcpy(buf->data() + ColorOffset, &color, 16);
+ memcpy(buf->data() + m_currentUbufOffset, &color, 16);
changed = true;
}
+ m_currentUbufOffset += 16; // now at StyleColor
return changed;
}
@@ -286,17 +252,14 @@ bool QSG32BitColorTextRhiShader::updateUniformData(RenderState &state,
class QSGStyledTextRhiShader : public QSG8BitTextMaskRhiShader
{
public:
- QSGStyledTextRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
- : QSG8BitTextMaskRhiShader(glyphFormat, alphaTexture)
+ QSGStyledTextRhiShader(QFontEngine::GlyphFormat glyphFormat, int viewCount, bool alphaTexture)
+ : QSG8BitTextMaskRhiShader(glyphFormat, viewCount, alphaTexture)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.vert.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext_a.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext_a.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &state,
@@ -312,17 +275,17 @@ bool QSGStyledTextRhiShader::updateUniformData(RenderState &state,
QSGStyledTextMaterial *oldMat = static_cast<QSGStyledTextMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= ShiftOffset + 8);
if (oldMat == nullptr || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
const QVector4D styleColor = qsg_premultiply(mat->styleColor(), state.opacity());
- memcpy(buf->data() + StyleColorOffset, &styleColor, 16);
+ memcpy(buf->data() + m_currentUbufOffset, &styleColor, 16);
changed = true;
}
+ m_currentUbufOffset += 16; // now at StyleShift
if (oldMat == nullptr || oldMat->styleShift() != mat->styleShift()) {
const QVector2D v = mat->styleShift();
- memcpy(buf->data() + ShiftOffset, &v, 8);
+ memcpy(buf->data() + m_currentUbufOffset, &v, 8);
changed = true;
}
@@ -332,17 +295,14 @@ bool QSGStyledTextRhiShader::updateUniformData(RenderState &state,
class QSGOutlinedTextRhiShader : public QSGStyledTextRhiShader
{
public:
- QSGOutlinedTextRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
- : QSGStyledTextRhiShader(glyphFormat, alphaTexture)
+ QSGOutlinedTextRhiShader(QFontEngine::GlyphFormat glyphFormat, int viewCount, bool alphaTexture)
+ : QSGStyledTextRhiShader(glyphFormat, viewCount, alphaTexture)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.vert.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext_a.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext_a.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.frag.qsb"), viewCount);
}
};
@@ -361,6 +321,8 @@ QSGTextMaskMaterial::QSGTextMaskMaterial(QSGRenderContext *rc, const QVector4D &
QSGTextMaskMaterial::~QSGTextMaskMaterial()
{
+ if (m_retainedFontEngine != nullptr)
+ m_rc->unregisterFontengineForCleanup(m_retainedFontEngine);
delete m_texture;
}
@@ -408,7 +370,8 @@ void QSGTextMaskMaterial::updateCache(QFontEngine::GlyphFormat glyphFormat)
qreal devicePixelRatio;
void *cacheKey;
Q_ASSERT(m_rhi);
- cacheKey = m_rhi;
+ Q_ASSERT(m_rc);
+ cacheKey = m_rc;
// Get the dpr the modern way. This value retrieved via the
// rendercontext matches what RenderState::devicePixelRatio()
// exposes to the material shaders later on.
@@ -423,6 +386,12 @@ void QSGTextMaskMaterial::updateCache(QFontEngine::GlyphFormat glyphFormat)
if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
m_glyphCache = new QSGRhiTextureGlyphCache(m_rc, glyphFormat, glyphCacheTransform, color);
fontEngine->setGlyphCache(cacheKey, m_glyphCache.data());
+ if (m_retainedFontEngine != nullptr)
+ m_rc->unregisterFontengineForCleanup(m_retainedFontEngine);
+
+ // Note: This is reference counted by the render context, so it will stay alive until
+ // we release that reference
+ m_retainedFontEngine = fontEngine;
m_rc->registerFontengineForCleanup(fontEngine);
}
}
@@ -557,12 +526,12 @@ QSGMaterialShader *QSGTextMaskMaterial::createShader(QSGRendererInterface::Rende
const QFontEngine::GlyphFormat glyphFormat = gc->glyphFormat();
switch (glyphFormat) {
case QFontEngine::Format_ARGB:
- return new QSG32BitColorTextRhiShader(glyphFormat);
+ return new QSG32BitColorTextRhiShader(glyphFormat, viewCount());
case QFontEngine::Format_A32:
- return new QSG24BitTextMaskRhiShader(glyphFormat);
+ return new QSG24BitTextMaskRhiShader(glyphFormat, viewCount());
case QFontEngine::Format_A8:
default:
- return new QSG8BitTextMaskRhiShader(glyphFormat, gc->eightBitFormatIsAlphaSwizzled());
+ return new QSG8BitTextMaskRhiShader(glyphFormat, viewCount(), gc->eightBitFormatIsAlphaSwizzled());
}
}
@@ -621,7 +590,7 @@ QSGMaterialShader *QSGStyledTextMaterial::createShader(QSGRendererInterface::Ren
{
Q_UNUSED(renderMode);
QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
- return new QSGStyledTextRhiShader(gc->glyphFormat(), gc->eightBitFormatIsAlphaSwizzled());
+ return new QSGStyledTextRhiShader(gc->glyphFormat(), viewCount(), gc->eightBitFormatIsAlphaSwizzled());
}
int QSGStyledTextMaterial::compare(const QSGMaterial *o) const
@@ -653,7 +622,7 @@ QSGMaterialShader *QSGOutlinedTextMaterial::createShader(QSGRendererInterface::R
{
Q_UNUSED(renderMode);
QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
- return new QSGOutlinedTextRhiShader(gc->glyphFormat(), gc->eightBitFormatIsAlphaSwizzled());
+ return new QSGOutlinedTextRhiShader(gc->glyphFormat(), viewCount(), gc->eightBitFormatIsAlphaSwizzled());
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
index e77e2715cd..d3b919c275 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
@@ -69,6 +69,7 @@ private:
QSGPlainTexture *m_texture;
QExplicitlySharedDataPointer<QFontEngineGlyphCache> m_glyphCache;
QRawFont m_font;
+ QFontEngine *m_retainedFontEngine = nullptr;
QRhi *m_rhi;
QVector4D m_color;
QSize m_size;
diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
index 9179382f89..d99f19bbe5 100644
--- a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
@@ -7,14 +7,14 @@
#include <private/qsgtexturematerial_p.h>
#include <qopenglfunctions.h>
#include <QtCore/qmath.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
class SmoothTextureMaterialRhiShader : public QSGTextureMaterialRhiShader
{
public:
- SmoothTextureMaterialRhiShader();
+ SmoothTextureMaterialRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
@@ -40,25 +40,28 @@ QSGMaterialType *QSGSmoothTextureMaterial::type() const
QSGMaterialShader *QSGSmoothTextureMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new SmoothTextureMaterialRhiShader;
+ return new SmoothTextureMaterialRhiShader(viewCount());
}
-SmoothTextureMaterialRhiShader::SmoothTextureMaterialRhiShader()
+SmoothTextureMaterialRhiShader::SmoothTextureMaterialRhiShader(int viewCount)
+ : QSGTextureMaterialRhiShader(viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothtexture.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothtexture.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothtexture.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothtexture.frag.qsb"), viewCount);
}
bool SmoothTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
bool changed = false;
QByteArray *buf = state.uniformData();
+ const int shaderMatrixCount = newMaterial->viewCount();
if (!oldMaterial) {
// The viewport is constant, so set the pixel size uniform only once (per batches with the same material).
const QRect r = state.viewportRect();
const QVector2D v(2.0f / r.width(), 2.0f / r.height());
- memcpy(buf->data() + 64 + 8, &v, 8);
+ // mat4 matrix, float opacity, vec2 pixelSize, and the vec2 must be 2*4 aligned
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &v, 8);
changed = true;
}
diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
index 2fc75367a3..1a64bdaace 100644
--- a/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QSGDefaultRenderContext;
-class Q_QUICK_PRIVATE_EXPORT QSGSmoothTextureMaterial : public QSGTextureMaterial
+class Q_QUICK_EXPORT QSGSmoothTextureMaterial : public QSGTextureMaterial
{
public:
QSGSmoothTextureMaterial();
@@ -36,7 +36,7 @@ protected:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalImageNode : public QSGBasicInternalImageNode
+class Q_QUICK_EXPORT QSGDefaultInternalImageNode : public QSGBasicInternalImageNode
{
public:
QSGDefaultInternalImageNode(QSGDefaultRenderContext *rc);
diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
index 19ee18c0de..57fe15d6bf 100644
--- a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
@@ -16,26 +16,30 @@ QT_BEGIN_NAMESPACE
class SmoothColorMaterialRhiShader : public QSGMaterialShader
{
public:
- SmoothColorMaterialRhiShader();
+ SmoothColorMaterialRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
-SmoothColorMaterialRhiShader::SmoothColorMaterialRhiShader()
+SmoothColorMaterialRhiShader::SmoothColorMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothcolor.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothcolor.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothcolor.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothcolor.frag.qsb"), viewCount);
}
-bool SmoothColorMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *oldMaterial)
+bool SmoothColorMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
bool changed = false;
QByteArray *buf = state.uniformData();
-
- if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
+
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
}
if (oldMaterial == nullptr) {
@@ -43,13 +47,13 @@ bool SmoothColorMaterialRhiShader::updateUniformData(RenderState &state, QSGMate
const QRect r = state.viewportRect();
const QVector2D v(2.0f / r.width(), 2.0f / r.height());
Q_ASSERT(sizeof(v) == 8);
- memcpy(buf->data() + 64, &v, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &v, 8);
changed = true;
}
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 72, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &opacity, 4);
changed = true;
}
@@ -78,7 +82,7 @@ QSGMaterialType *QSGSmoothColorMaterial::type() const
QSGMaterialShader *QSGSmoothColorMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new SmoothColorMaterialRhiShader;
+ return new SmoothColorMaterialRhiShader(viewCount());
}
QSGDefaultInternalRectangleNode::QSGDefaultInternalRectangleNode()
diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h
index 48236d9022..e4ded20888 100644
--- a/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QSGContext;
-class Q_QUICK_PRIVATE_EXPORT QSGSmoothColorMaterial : public QSGMaterial
+class Q_QUICK_EXPORT QSGSmoothColorMaterial : public QSGMaterial
{
public:
QSGSmoothColorMaterial();
@@ -36,7 +36,7 @@ protected:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalRectangleNode : public QSGBasicInternalRectangleNode
+class Q_QUICK_EXPORT QSGDefaultInternalRectangleNode : public QSGBasicInternalRectangleNode
{
public:
QSGDefaultInternalRectangleNode();
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index 3fee9d9faf..1b0753e9ae 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsgdefaultrendercontext_p.h"
+#include "qsgcurveglyphatlas_p.h"
#include <QtGui/QGuiApplication>
@@ -59,13 +60,32 @@ void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *par
void QSGDefaultRenderContext::invalidateGlyphCaches()
{
- auto it = m_glyphCaches.begin();
- while (it != m_glyphCaches.end()) {
- if (!(*it)->isActive()) {
- delete *it;
- it = m_glyphCaches.erase(it);
- } else {
- ++it;
+ {
+ auto it = m_glyphCaches.begin();
+ while (it != m_glyphCaches.end()) {
+ if (!(*it)->isActive()) {
+ delete *it;
+ it = m_glyphCaches.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ qDeleteAll(m_curveGlyphAtlases);
+ m_curveGlyphAtlases.clear();
+
+ {
+ auto it = m_fontEnginesToClean.begin();
+ while (it != m_fontEnginesToClean.end()) {
+ if (it.value() == 0) {
+ it.key()->clearGlyphCache(this);
+ if (!it.key()->ref.deref())
+ delete it.key();
+ it = m_fontEnginesToClean.erase(it);
+ } else {
+ ++it;
+ }
}
}
}
@@ -108,18 +128,20 @@ void QSGDefaultRenderContext::invalidate()
// code is only called from QQuickWindow's shutdown which is called
// only when the GUI is blocked, and multiple threads will call it in
// sequence. (see qsgdefaultglyphnode_p.cpp's init())
- for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
- end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
- (*it)->clearGlyphCache(m_rhi);
- if (!(*it)->ref.deref())
- delete *it;
+ for (auto it = m_fontEnginesToClean.constBegin(); it != m_fontEnginesToClean.constEnd(); ++it) {
+ it.key()->clearGlyphCache(this);
+ if (!it.key()->ref.deref())
+ delete it.key();
}
m_fontEnginesToClean.clear();
+ qDeleteAll(m_curveGlyphAtlases);
+ m_curveGlyphAtlases.clear();
+
qDeleteAll(m_glyphCaches);
m_glyphCaches.clear();
- releaseGlyphCacheResourceUpdates();
+ resetGlyphCacheResources();
m_rhi = nullptr;
@@ -205,27 +227,6 @@ QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompre
return nullptr;
}
-QString QSGDefaultRenderContext::fontKey(const QRawFont &font, int renderTypeQuality)
-{
- QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
- if (!fe->faceId().filename.isEmpty()) {
- QByteArray keyName =
- fe->faceId().filename + ' ' + QByteArray::number(fe->faceId().index)
- + (font.style() != QFont::StyleNormal ? QByteArray(" I") : QByteArray())
- + (font.weight() != QFont::Normal ? ' ' + QByteArray::number(font.weight()) : QByteArray())
- + ' ' + QByteArray::number(renderTypeQuality)
- + QByteArray(" DF");
- return QString::fromUtf8(keyName);
- } else {
- return QString::fromLatin1("%1_%2_%3_%4_%5")
- .arg(font.familyName())
- .arg(font.styleName())
- .arg(font.weight())
- .arg(font.style())
- .arg(renderTypeQuality);
- }
-}
-
void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialShader *shader, QShader::Variant shaderVariant)
{
QSGMaterialShaderPrivate::get(shader)->prepare(shaderVariant);
@@ -239,11 +240,23 @@ void QSGDefaultRenderContext::preprocess()
}
}
+QSGCurveGlyphAtlas *QSGDefaultRenderContext::curveGlyphAtlas(const QRawFont &font)
+{
+ FontKey key = FontKey(font, 0);
+ QSGCurveGlyphAtlas *atlas = m_curveGlyphAtlases.value(key, nullptr);
+ if (atlas == nullptr) {
+ atlas = new QSGCurveGlyphAtlas(font);
+ m_curveGlyphAtlases.insert(key, atlas);
+ }
+
+ return atlas;
+}
+
QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality)
{
- QString key = fontKey(font, renderTypeQuality);
+ FontKey key(font, renderTypeQuality);
QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0);
- if (!cache) {
+ if (!cache && font.isValid()) {
cache = new QSGRhiDistanceFieldGlyphCache(this, font, renderTypeQuality);
m_glyphCaches.insert(key, cache);
}
@@ -264,12 +277,23 @@ QRhiResourceUpdateBatch *QSGDefaultRenderContext::glyphCacheResourceUpdates()
return m_glyphCacheResourceUpdates;
}
-void QSGDefaultRenderContext::releaseGlyphCacheResourceUpdates()
+void QSGDefaultRenderContext::deferredReleaseGlyphCacheTexture(QRhiTexture *texture)
+{
+ if (texture)
+ m_pendingGlyphCacheTextures.insert(texture);
+}
+
+void QSGDefaultRenderContext::resetGlyphCacheResources()
{
if (m_glyphCacheResourceUpdates) {
m_glyphCacheResourceUpdates->release();
m_glyphCacheResourceUpdates = nullptr;
}
+
+ for (QRhiTexture *t : std::as_const(m_pendingGlyphCacheTextures))
+ t->deleteLater(); // the QRhiTexture object stays valid for the current frame
+
+ m_pendingGlyphCacheTextures.clear();
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index 448255a594..c3352aa89f 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -16,7 +16,7 @@
//
#include <QtQuick/private/qsgcontext_p.h>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
QT_BEGIN_NAMESPACE
@@ -24,6 +24,7 @@ class QRhi;
class QRhiCommandBuffer;
class QRhiRenderPassDescriptor;
class QRhiResourceUpdateBatch;
+class QRhiTexture;
class QSGMaterialShader;
class QSurface;
@@ -31,7 +32,7 @@ namespace QSGRhiAtlasTexture {
class Manager;
}
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultRenderContext : public QSGRenderContext
+class Q_QUICK_EXPORT QSGDefaultRenderContext : public QSGRenderContext
{
Q_OBJECT
public:
@@ -69,6 +70,7 @@ public:
void preprocess() override;
void invalidateGlyphCaches() override;
QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality) override;
+ QSGCurveGlyphAtlas *curveGlyphAtlas(const QRawFont &font) override;
QSGTexture *createTexture(const QImage &image, uint flags) const override;
QSGRenderer *createRenderer(QSGRendererInterface::RenderMode renderMode = QSGRendererInterface::RenderMode2D) override;
@@ -102,11 +104,10 @@ public:
QRhiResourceUpdateBatch *maybeGlyphCacheResourceUpdates();
QRhiResourceUpdateBatch *glyphCacheResourceUpdates();
- void releaseGlyphCacheResourceUpdates();
+ void deferredReleaseGlyphCacheTexture(QRhiTexture *texture);
+ void resetGlyphCacheResources();
protected:
- static QString fontKey(const QRawFont &font, int renderTypeQuality);
-
InitParams m_initParams;
QRhi *m_rhi;
int m_maxTextureSize;
@@ -116,6 +117,8 @@ protected:
qreal m_currentDevicePixelRatio;
bool m_useDepthBufferFor2D;
QRhiResourceUpdateBatch *m_glyphCacheResourceUpdates;
+ QSet<QRhiTexture *> m_pendingGlyphCacheTextures;
+ QHash<FontKey, QSGCurveGlyphAtlas *> m_curveGlyphAtlases;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultspritenode.cpp b/src/quick/scenegraph/qsgdefaultspritenode.cpp
index 233d8e17ac..242c844e9c 100644
--- a/src/quick/scenegraph/qsgdefaultspritenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultspritenode.cpp
@@ -53,7 +53,7 @@ QQuickSpriteMaterial::~QQuickSpriteMaterial()
class SpriteMaterialRhiShader : public QSGMaterialShader
{
public:
- SpriteMaterialRhiShader();
+ SpriteMaterialRhiShader(int viewCount);
bool updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
@@ -61,10 +61,10 @@ public:
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
-SpriteMaterialRhiShader::SpriteMaterialRhiShader()
+SpriteMaterialRhiShader::SpriteMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.frag.qsb"), viewCount);
}
bool SpriteMaterialRhiShader::updateUniformData(RenderState &state,
@@ -80,20 +80,24 @@ bool SpriteMaterialRhiShader::updateUniformData(RenderState &state,
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 96);
- if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
}
float animPosAndData[7] = { mat->animX1, mat->animY1, mat->animX2, mat->animY2,
mat->animW, mat->animH, mat->animT };
- memcpy(buf->data() + 64, animPosAndData, 28);
+ memcpy(buf->data() + 64 * shaderMatrixCount, animPosAndData, 28);
changed = true;
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 92, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 16 + 12, &opacity, 4);
changed = true;
}
@@ -120,7 +124,7 @@ void SpriteMaterialRhiShader::updateSampledImage(RenderState &state, int binding
QSGMaterialShader *QQuickSpriteMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new SpriteMaterialRhiShader;
+ return new SpriteMaterialRhiShader(viewCount());
}
static QSGGeometry::Attribute Sprite_Attributes[] = {
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
index 64f862f948..d5adb52a9f 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
@@ -41,7 +41,6 @@ QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode()
if (m_glyph_cache) {
m_glyph_cache->release(m_glyphs.glyphIndexes());
m_glyph_cache->unregisterGlyphNode(this);
- m_glyph_cache->unregisterOwnerElement(ownerElement());
}
}
@@ -90,15 +89,13 @@ void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphR
return;
if (m_glyph_cache != oldCache) {
- Q_ASSERT(ownerElement() != nullptr);
if (oldCache) {
oldCache->unregisterGlyphNode(this);
- oldCache->unregisterOwnerElement(ownerElement());
}
m_glyph_cache->registerGlyphNode(this);
- m_glyph_cache->registerOwnerElement(ownerElement());
}
- m_glyph_cache->populate(glyphs.glyphIndexes());
+ if (m_glyph_cache)
+ m_glyph_cache->populate(glyphs.glyphIndexes());
const QVector<quint32> glyphIndexes = m_glyphs.glyphIndexes();
for (int i = 0; i < glyphIndexes.size(); ++i)
@@ -158,7 +155,8 @@ void QSGDistanceFieldGlyphNode::invalidateGlyphs(const QVector<quint32> &glyphs)
void QSGDistanceFieldGlyphNode::updateGeometry()
{
- Q_ASSERT(m_glyph_cache);
+ if (!m_glyph_cache)
+ return;
// Remove previously created sub glyph nodes
// We assume all the children are sub glyph nodes
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
index b3da72dc81..afb41f32e4 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
@@ -36,7 +36,7 @@ static float spreadFunc(float glyphScale)
class QSGDistanceFieldTextMaterialRhiShader : public QSGMaterialShader
{
public:
- QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+ QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture, int viewCount);
bool updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
@@ -47,19 +47,16 @@ public:
protected:
float m_fontScale = 1.0;
float m_matrixScale = 1.0;
+ quint32 m_currentUbufOffset;
};
-QSGDistanceFieldTextMaterialRhiShader::QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture)
+QSGDistanceFieldTextMaterialRhiShader::QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture, int viewCount)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.vert.qsb"));
-
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.frag.qsb"), viewCount);
}
bool QSGDistanceFieldTextMaterialRhiShader::updateUniformData(RenderState &state,
@@ -85,36 +82,47 @@ bool QSGDistanceFieldTextMaterialRhiShader::updateUniformData(RenderState &state
updateRange = true;
}
if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
m_matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio();
updateRange = true;
}
+ quint32 offset = 0;
+ const int matrixCount = qMin(state.projectionMatrixCount(), newMaterial->viewCount());
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
+ offset += 64;
+ }
if (textureUpdated || !oldMat || oldMat->texture()->texture != mat->texture()->texture) {
const QVector2D ts(1.0f / mat->textureSize().width(), 1.0f / mat->textureSize().height());
Q_ASSERT(sizeof(ts) == 8);
- memcpy(buf->data() + 64, &ts, 8);
+ memcpy(buf->data() + offset, &ts, 8);
changed = true;
}
+ offset += 8 + 8; // 8 is padding for vec4 alignment
if (!oldMat || mat->color() != oldMat->color() || state.isOpacityDirty()) {
const QVector4D color = mat->color() * state.opacity();
Q_ASSERT(sizeof(color) == 16);
- memcpy(buf->data() + 80, &color, 16);
+ memcpy(buf->data() + offset, &color, 16);
changed = true;
}
+ offset += 16;
if (updateRange) { // deferred because depends on m_fontScale and m_matrixScale
const float combinedScale = m_fontScale * m_matrixScale;
const float base = thresholdFunc(combinedScale);
const float range = spreadFunc(combinedScale);
const QVector2D alphaMinMax(qMax(0.0f, base - range), qMin(base + range, 1.0f));
- memcpy(buf->data() + 96, &alphaMinMax, 8);
+ memcpy(buf->data() + offset, &alphaMinMax, 8);
changed = true;
}
+ offset += 8; // not adding any padding here since we are not sure what comes afterwards in the subclasses' shaders
// move texture uploads/copies onto the renderer's soon-to-be-committed list
static_cast<QSGRhiDistanceFieldGlyphCache *>(mat->glyphCache())->commitResourceUpdates(state.resourceUpdateBatch());
+ m_currentUbufOffset = offset;
return changed;
}
@@ -134,20 +142,17 @@ void QSGDistanceFieldTextMaterialRhiShader::updateSampledImage(RenderState &stat
class DistanceFieldAnisotropicTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
{
public:
- DistanceFieldAnisotropicTextMaterialRhiShader(bool alphaTexture);
+ DistanceFieldAnisotropicTextMaterialRhiShader(bool alphaTexture, int viewCount);
};
-DistanceFieldAnisotropicTextMaterialRhiShader::DistanceFieldAnisotropicTextMaterialRhiShader(bool alphaTexture)
- : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
+DistanceFieldAnisotropicTextMaterialRhiShader::DistanceFieldAnisotropicTextMaterialRhiShader(bool alphaTexture, int viewCount)
+ : QSGDistanceFieldTextMaterialRhiShader(alphaTexture, viewCount)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.vert.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_fwidth.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_fwidth.frag.qsb"), viewCount);
}
QSGDistanceFieldTextMaterial::QSGDistanceFieldTextMaterial()
@@ -180,9 +185,9 @@ void QSGDistanceFieldTextMaterial::setColor(const QColor &color)
QSGMaterialShader *QSGDistanceFieldTextMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
if (renderMode == QSGRendererInterface::RenderMode3D && m_glyph_cache->screenSpaceDerivativesSupported())
- return new DistanceFieldAnisotropicTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new DistanceFieldAnisotropicTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
else
- return new QSGDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new QSGDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
}
bool QSGDistanceFieldTextMaterial::updateTextureSize()
@@ -234,13 +239,13 @@ int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const
class DistanceFieldStyledTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
{
public:
- DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture);
+ DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture, int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
-DistanceFieldStyledTextMaterialRhiShader::DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture)
- : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
+DistanceFieldStyledTextMaterialRhiShader::DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture, int viewCount)
+ : QSGDistanceFieldTextMaterialRhiShader(alphaTexture, viewCount)
{
}
@@ -254,12 +259,17 @@ bool DistanceFieldStyledTextMaterialRhiShader::updateUniformData(RenderState &st
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 128);
+ // must add 8 bytes padding for vec4 alignment, the base class did not do this
+ m_currentUbufOffset += 8; // now at StyleColor
+
if (!oldMat || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
QVector4D styleColor = mat->styleColor();
styleColor *= state.opacity();
- memcpy(buf->data() + 112, &styleColor, 16);
+
+ memcpy(buf->data() + m_currentUbufOffset, &styleColor, 16);
changed = true;
}
+ m_currentUbufOffset += 16;
return changed;
}
@@ -292,42 +302,35 @@ int QSGDistanceFieldStyledTextMaterial::compare(const QSGMaterial *o) const
class DistanceFieldOutlineTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
{
public:
- DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture);
+ DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture, int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
-DistanceFieldOutlineTextMaterialRhiShader::DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture)
- : DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
+DistanceFieldOutlineTextMaterialRhiShader::DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture, int viewCount)
+ : DistanceFieldStyledTextMaterialRhiShader(alphaTexture, viewCount)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb"));
-
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb"), viewCount);
}
class DistanceFieldAnisotropicOutlineTextMaterialRhiShader : public DistanceFieldOutlineTextMaterialRhiShader
{
public:
- DistanceFieldAnisotropicOutlineTextMaterialRhiShader(bool alphaTexture);
+ DistanceFieldAnisotropicOutlineTextMaterialRhiShader(bool alphaTexture, int viewCount);
};
-DistanceFieldAnisotropicOutlineTextMaterialRhiShader::DistanceFieldAnisotropicOutlineTextMaterialRhiShader(bool alphaTexture)
- : DistanceFieldOutlineTextMaterialRhiShader(alphaTexture)
+DistanceFieldAnisotropicOutlineTextMaterialRhiShader::DistanceFieldAnisotropicOutlineTextMaterialRhiShader(bool alphaTexture, int viewCount)
+ : DistanceFieldOutlineTextMaterialRhiShader(alphaTexture, viewCount)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag.qsb"), viewCount);
}
bool DistanceFieldOutlineTextMaterialRhiShader::updateUniformData(RenderState &state,
@@ -349,8 +352,8 @@ bool DistanceFieldOutlineTextMaterialRhiShader::updateUniformData(RenderState &s
float alphaMin = qMax(0.0f, base - range);
float styleAlphaMin0 = qMax(0.0f, outlineLimit - range);
float styleAlphaMin1 = qMin(outlineLimit + range, alphaMin);
- memcpy(buf->data() + 128, &styleAlphaMin0, 4);
- memcpy(buf->data() + 132, &styleAlphaMin1, 4);
+ memcpy(buf->data() + m_currentUbufOffset, &styleAlphaMin0, 4);
+ memcpy(buf->data() + m_currentUbufOffset + 4, &styleAlphaMin1, 4);
changed = true;
}
@@ -375,30 +378,27 @@ QSGMaterialType *QSGDistanceFieldOutlineTextMaterial::type() const
QSGMaterialShader *QSGDistanceFieldOutlineTextMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
if (renderMode == QSGRendererInterface::RenderMode3D && m_glyph_cache->screenSpaceDerivativesSupported())
- return new DistanceFieldAnisotropicOutlineTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new DistanceFieldAnisotropicOutlineTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
else
- return new DistanceFieldOutlineTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new DistanceFieldOutlineTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
}
class DistanceFieldShiftedStyleTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
{
public:
- DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture);
+ DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture, int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
-DistanceFieldShiftedStyleTextMaterialRhiShader::DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture)
- : DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
+DistanceFieldShiftedStyleTextMaterialRhiShader::DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture, int viewCount)
+ : DistanceFieldStyledTextMaterialRhiShader(alphaTexture, viewCount)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb"), viewCount);
}
bool DistanceFieldShiftedStyleTextMaterialRhiShader::updateUniformData(RenderState &state,
@@ -416,7 +416,7 @@ bool DistanceFieldShiftedStyleTextMaterialRhiShader::updateUniformData(RenderSta
{
QVector2D shift(1.0 / mat->fontScale() * mat->shift().x(),
1.0 / mat->fontScale() * mat->shift().y());
- memcpy(buf->data() + 128, &shift, 8);
+ memcpy(buf->data() + m_currentUbufOffset, &shift, 8);
changed = true;
}
@@ -426,20 +426,17 @@ bool DistanceFieldShiftedStyleTextMaterialRhiShader::updateUniformData(RenderSta
class DistanceFieldAnisotropicShiftedTextMaterialRhiShader : public DistanceFieldShiftedStyleTextMaterialRhiShader
{
public:
- DistanceFieldAnisotropicShiftedTextMaterialRhiShader(bool alphaTexture);
+ DistanceFieldAnisotropicShiftedTextMaterialRhiShader(bool alphaTexture, int viewCount);
};
-DistanceFieldAnisotropicShiftedTextMaterialRhiShader::DistanceFieldAnisotropicShiftedTextMaterialRhiShader(bool alphaTexture)
- : DistanceFieldShiftedStyleTextMaterialRhiShader(alphaTexture)
+DistanceFieldAnisotropicShiftedTextMaterialRhiShader::DistanceFieldAnisotropicShiftedTextMaterialRhiShader(bool alphaTexture, int viewCount)
+ : DistanceFieldShiftedStyleTextMaterialRhiShader(alphaTexture, viewCount)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag.qsb"), viewCount);
}
QSGDistanceFieldShiftedStyleTextMaterial::QSGDistanceFieldShiftedStyleTextMaterial()
@@ -460,9 +457,9 @@ QSGMaterialType *QSGDistanceFieldShiftedStyleTextMaterial::type() const
QSGMaterialShader *QSGDistanceFieldShiftedStyleTextMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
if (renderMode == QSGRendererInterface::RenderMode3D && m_glyph_cache->screenSpaceDerivativesSupported())
- return new DistanceFieldAnisotropicShiftedTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new DistanceFieldAnisotropicShiftedTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
else
- return new DistanceFieldShiftedStyleTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new DistanceFieldShiftedStyleTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
}
int QSGDistanceFieldShiftedStyleTextMaterial::compare(const QSGMaterial *o) const
@@ -476,26 +473,22 @@ int QSGDistanceFieldShiftedStyleTextMaterial::compare(const QSGMaterial *o) cons
class QSGHiQSubPixelDistanceFieldTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
{
public:
- QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+ QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture, int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
-QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
- : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
+QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture, int viewCount)
+ : QSGDistanceFieldTextMaterialRhiShader(alphaTexture, viewCount)
{
setFlag(UpdatesGraphicsPipelineState, true);
-
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb"), viewCount);
}
bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateUniformData(RenderState &state,
@@ -510,15 +503,16 @@ bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateUniformData(RenderS
if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
float fontScale = mat->fontScale();
- memcpy(buf->data() + 104, &fontScale, 4);
+ memcpy(buf->data() + m_currentUbufOffset, &fontScale, 4);
changed = true;
}
+ m_currentUbufOffset += 4 + 4; // 4 for padding for vec2 alignment
if (!oldMat || state.isMatrixDirty()) {
int viewportWidth = state.viewportRect().width();
QMatrix4x4 mat = state.combinedMatrix().inverted();
QVector4D vecDelta = mat.column(0) * (qreal(2) / viewportWidth);
- memcpy(buf->data() + 112, &vecDelta, 16);
+ memcpy(buf->data() + m_currentUbufOffset, &vecDelta, 16);
}
return changed;
@@ -551,28 +545,25 @@ QSGMaterialType *QSGHiQSubPixelDistanceFieldTextMaterial::type() const
QSGMaterialShader *QSGHiQSubPixelDistanceFieldTextMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
if (renderMode == QSGRendererInterface::RenderMode3D && m_glyph_cache->screenSpaceDerivativesSupported())
- return new DistanceFieldAnisotropicTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new DistanceFieldAnisotropicTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
else
- return new QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
}
class QSGLoQSubPixelDistanceFieldTextMaterialRhiShader : public QSGHiQSubPixelDistanceFieldTextMaterialRhiShader
{
public:
- QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+ QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture, int viewCount);
};
-QSGLoQSubPixelDistanceFieldTextMaterialRhiShader::QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
- : QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(alphaTexture)
+QSGLoQSubPixelDistanceFieldTextMaterialRhiShader::QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture, int viewCount)
+ : QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(alphaTexture, viewCount)
{
- setShaderFileName(VertexStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb"), viewCount);
if (alphaTexture)
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb"), viewCount);
else
- setShaderFileName(FragmentStage,
- QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb"), viewCount);
}
QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const
@@ -584,9 +575,9 @@ QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const
QSGMaterialShader *QSGLoQSubPixelDistanceFieldTextMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
if (renderMode == QSGRendererInterface::RenderMode3D && m_glyph_cache->screenSpaceDerivativesSupported())
- return new DistanceFieldAnisotropicTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new DistanceFieldAnisotropicTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
else
- return new QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ return new QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled(), viewCount());
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
index 06d05a928b..207facf213 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QSGPlainTexture;
-class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldTextMaterial: public QSGMaterial
+class Q_QUICK_EXPORT QSGDistanceFieldTextMaterial: public QSGMaterial
{
public:
QSGDistanceFieldTextMaterial();
@@ -61,7 +61,7 @@ protected:
QSGPlainTexture *m_sgTexture;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldStyledTextMaterial : public QSGDistanceFieldTextMaterial
+class Q_QUICK_EXPORT QSGDistanceFieldStyledTextMaterial : public QSGDistanceFieldTextMaterial
{
public:
QSGDistanceFieldStyledTextMaterial();
@@ -78,7 +78,7 @@ protected:
QVector4D m_styleColor;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldOutlineTextMaterial : public QSGDistanceFieldStyledTextMaterial
+class Q_QUICK_EXPORT QSGDistanceFieldOutlineTextMaterial : public QSGDistanceFieldStyledTextMaterial
{
public:
QSGDistanceFieldOutlineTextMaterial();
@@ -88,7 +88,7 @@ public:
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldShiftedStyleTextMaterial : public QSGDistanceFieldStyledTextMaterial
+class Q_QUICK_EXPORT QSGDistanceFieldShiftedStyleTextMaterial : public QSGDistanceFieldStyledTextMaterial
{
public:
QSGDistanceFieldShiftedStyleTextMaterial();
@@ -105,7 +105,7 @@ protected:
QPointF m_shift;
};
-class Q_QUICK_PRIVATE_EXPORT QSGHiQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial
+class Q_QUICK_EXPORT QSGHiQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial
{
public:
QSGMaterialType *type() const override;
@@ -116,7 +116,7 @@ public:
}
};
-class Q_QUICK_PRIVATE_EXPORT QSGLoQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial
+class Q_QUICK_EXPORT QSGLoQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial
{
public:
QSGMaterialType *type() const override;
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index e620a22fb7..3c8da0fc6a 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -40,6 +40,18 @@ extern bool qsg_useConsistentTiming();
#define ENABLE_DEFAULT_BACKEND
+Q_TRACE_POINT(qtquick, QSG_renderWindow_entry)
+Q_TRACE_POINT(qtquick, QSG_renderWindow_exit)
+Q_TRACE_POINT(qtquick, QSG_polishItems_entry)
+Q_TRACE_POINT(qtquick, QSG_polishItems_exit)
+Q_TRACE_POINT(qtquick, QSG_sync_entry)
+Q_TRACE_POINT(qtquick, QSG_sync_exit)
+Q_TRACE_POINT(qtquick, QSG_render_entry)
+Q_TRACE_POINT(qtquick, QSG_render_exit)
+Q_TRACE_POINT(qtquick, QSG_swap_entry)
+Q_TRACE_POINT(qtquick, QSG_swap_exit)
+
+
/*
- Uses one QRhi per window. (and so each window has its own QOpenGLContext or ID3D11Device(Context) etc.)
- Animations are advanced using the standard timer (no custom animation
@@ -126,6 +138,7 @@ public:
void releaseSwapchain(QQuickWindow *window);
void handleDeviceLoss();
+ void teardownGraphics();
bool eventFilter(QObject *watched, QEvent *event) override;
@@ -154,6 +167,8 @@ public:
mutable QSet<QSGRenderContext *> pendingRenderContexts;
bool m_inPolish = false;
+
+ bool swRastFallbackDueToSwapchainFailure = false;
};
#endif
@@ -246,6 +261,15 @@ void QSGRenderLoop::setInstance(QSGRenderLoop *instance)
void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window)
{
+ // Must always be called on the gui thread.
+
+ // Guard for recursion; relevant due to the MessageBox() on Windows.
+ static QSet<QQuickWindow *> recurseGuard;
+ if (recurseGuard.contains(window))
+ return;
+
+ recurseGuard.insert(window);
+
QString translatedMessage;
QString untranslatedMessage;
QQuickWindowPrivate::rhiCreationFailureMessage(QSGRhiSupport::instance()->rhiBackendName(),
@@ -266,6 +290,8 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window)
#endif // Q_OS_WIN
if (!signalEmitted)
qFatal("%s", qPrintable(untranslatedMessage));
+
+ recurseGuard.remove(window);
}
#ifdef ENABLE_DEFAULT_BACKEND
@@ -354,13 +380,28 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
}
}
+void QSGGuiThreadRenderLoop::teardownGraphics()
+{
+ for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) {
+ if (it->rhi) {
+ QQuickWindowPrivate::get(it.key())->cleanupNodesOnShutdown();
+ if (it->rc)
+ it->rc->invalidate();
+ releaseSwapchain(it.key());
+ if (it->ownRhi)
+ QSGRhiSupport::instance()->destroyRhi(it->rhi, {});
+ it->rhi = nullptr;
+ }
+ }
+}
+
void QSGGuiThreadRenderLoop::handleDeviceLoss()
{
qWarning("Graphics device lost, cleaning up scenegraph and releasing RHIs");
for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) {
if (!it->rhi || !it->rhi->isDeviceLost())
- return;
+ continue;
QQuickWindowPrivate::get(it.key())->cleanupNodesOnShutdown();
@@ -424,7 +465,8 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data)
if (!offscreenSurface)
offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
- QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface);
+ const bool forcePreferSwRenderer = swRastFallbackDueToSwapchainFailure;
+ QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
data.rhi = rhiResult.rhi;
data.ownRhi = rhiResult.own;
@@ -496,7 +538,7 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data)
cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
}
cd->swapchain->setWindow(window);
- rhiSupport->applySwapChainFormat(cd->swapchain);
+ rhiSupport->applySwapChainFormat(cd->swapchain, window);
qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s",
data.sampleCount, alpha ? "yes" : "no");
cd->swapchain->setSampleCount(data.sampleCount);
@@ -591,15 +633,24 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
// signals and want to do graphics stuff already there.
if (cd->swapchain) {
Q_ASSERT(!effectiveOutputSize.isEmpty());
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
const QSize previousOutputSize = cd->swapchain->currentPixelSize();
if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
if (cd->swapchainJustBecameRenderable)
qCDebug(QSG_LOG_RENDERLOOP, "just became exposed");
cd->hasActiveSwapchain = cd->swapchain->createOrResize();
- if (!cd->hasActiveSwapchain && data.rhi->isDeviceLost()) {
- handleDeviceLoss();
- return;
+ if (!cd->hasActiveSwapchain) {
+ if (data.rhi->isDeviceLost()) {
+ handleDeviceLoss();
+ return;
+ } else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
+ qWarning("Failed to create swapchain."
+ " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
+ swRastFallbackDueToSwapchainFailure = true;
+ teardownGraphics();
+ return;
+ }
}
cd->swapchainJustBecameRenderable = false;
@@ -659,6 +710,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
Q_TRACE(QSG_swap_entry);
const bool needsPresent = alsoSwap && window->isVisible();
+ double lastCompletedGpuTime = 0;
if (cd->swapchain) {
QRhi::EndFrameFlags flags;
if (!needsPresent)
@@ -669,6 +721,8 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
handleDeviceLoss();
else if (frameResult == QRhi::FrameOpError)
qWarning("Failed to end frame");
+ } else {
+ lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
}
}
if (needsPresent)
@@ -694,6 +748,11 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
int((renderTime - syncTime) / 1000000),
int((swapTime - renderTime) / 1000000),
int(data.timeBetweenRenders.restart()));
+ if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
+ qCDebug(QSG_LOG_TIME_RENDERLOOP, "[window %p][gui thread] syncAndRender: last retrieved GPU frame time was %.4f ms",
+ window,
+ lastCompletedGpuTime * 1000.0);
+ }
}
// Might have been set during syncSceneGraph()
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 1a25154658..9157e2edd2 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -20,6 +20,7 @@
#include <private/qtquickglobal_p.h>
#include <QtCore/qset.h>
#include <QtCore/qobject.h>
+#include <QtCore/qcoreevent.h>
QT_BEGIN_NAMESPACE
@@ -29,7 +30,7 @@ class QSGRenderContext;
class QAnimationDriver;
class QRunnable;
-class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop : public QObject
+class Q_QUICK_EXPORT QSGRenderLoop : public QObject
{
Q_OBJECT
@@ -94,6 +95,33 @@ enum QSGRenderLoopType
ThreadedRenderLoop
};
+enum QSGCustomEvents {
+
+// Passed from the RL to the RT when a window is removed obscured and
+// should be removed from the render loop.
+WM_Obscure = QEvent::User + 1,
+
+// Passed from the RL to RT when GUI has been locked, waiting for sync
+// (updatePaintNode())
+WM_RequestSync = QEvent::User + 2,
+
+// Passed by the RL to the RT to free up maybe release SG and GL contexts
+// if no windows are rendering.
+WM_TryRelease = QEvent::User + 4,
+
+// Passed by the RL to the RT when a QQuickWindow::grabWindow() is
+// called.
+WM_Grab = QEvent::User + 5,
+
+// Passed by the window when there is a render job to run
+WM_PostJob = QEvent::User + 6,
+
+// When using the QRhi this is sent upon PlatformSurfaceAboutToBeDestroyed from
+// the event filter installed on the QQuickWindow.
+WM_ReleaseSwapchain = QEvent::User + 7,
+
+};
+
QT_END_NAMESPACE
#endif // QSGRENDERLOOP_P_H
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
index a68842577d..54cf298943 100644
--- a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
@@ -32,19 +32,10 @@ QSGRhiDistanceFieldGlyphCache::QSGRhiDistanceFieldGlyphCache(QSGDefaultRenderCon
QSGRhiDistanceFieldGlyphCache::~QSGRhiDistanceFieldGlyphCache()
{
- // A plain delete should work, but just in case commitResourceUpdates was
- // not called and something is enqueued on the update batch for a texture,
- // defer until the end of the frame.
- for (int i = 0; i < m_textures.size(); ++i) {
- if (m_textures[i].texture)
- m_textures[i].texture->deleteLater();
- }
+ for (const TextureInfo &t : std::as_const(m_textures))
+ m_rc->deferredReleaseGlyphCacheTexture(t.texture);
delete m_areaAllocator;
-
- // should be empty, but just in case
- for (QRhiTexture *t : std::as_const(m_pendingDispose))
- t->deleteLater();
}
void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
@@ -243,7 +234,7 @@ void QSGRhiDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int widt
resourceUpdates->copyTexture(texInfo->texture, oldTexture);
}
- m_pendingDispose.insert(oldTexture);
+ m_rc->deferredReleaseGlyphCacheTexture(oldTexture);
}
bool QSGRhiDistanceFieldGlyphCache::useTextureResizeWorkaround() const
@@ -522,14 +513,8 @@ void QSGRhiDistanceFieldGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatc
{
if (QRhiResourceUpdateBatch *resourceUpdates = m_rc->maybeGlyphCacheResourceUpdates()) {
mergeInto->merge(resourceUpdates);
- m_rc->releaseGlyphCacheResourceUpdates();
+ m_rc->resetGlyphCacheResources();
}
-
- // now let's assume the resource updates will be committed in this frame
- for (QRhiTexture *t : std::as_const(m_pendingDispose))
- t->deleteLater(); // will be deleted after the frame is submitted -> safe
-
- m_pendingDispose.clear();
}
bool QSGRhiDistanceFieldGlyphCache::eightBitFormatIsAlphaSwizzled() const
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
index b7653881f5..4959cd2c50 100644
--- a/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
@@ -17,13 +17,13 @@
#include "qsgadaptationlayer_p.h"
#include <private/qsgareaallocator_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
class QSGDefaultRenderContext;
-class Q_QUICK_PRIVATE_EXPORT QSGRhiDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
+class Q_QUICK_EXPORT QSGRhiDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
{
public:
QSGRhiDistanceFieldGlyphCache(QSGDefaultRenderContext *rc, const QRawFont &font, int renderTypeQuality);
diff --git a/src/quick/scenegraph/qsgrhiinternaltextnode.cpp b/src/quick/scenegraph/qsgrhiinternaltextnode.cpp
new file mode 100644
index 0000000000..6108ff7044
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhiinternaltextnode.cpp
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgrhiinternaltextnode_p.h"
+
+#include <private/qquadpath_p.h>
+#include <private/qsgcurvefillnode_p.h>
+#include <private/qsgcurvestrokenode_p.h>
+#include <private/qsgcurveprocessor_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGRhiInternalTextNode::QSGRhiInternalTextNode(QSGRenderContext *renderContext)
+ : QSGInternalTextNode(renderContext)
+{
+}
+
+void QSGRhiInternalTextNode::addDecorationNode(const QRectF &rect, const QColor &color)
+{
+ QSGCurveStrokeNode *node = new QSGCurveStrokeNode;
+ node->setColor(color);
+ node->setStrokeWidth(rect.height());
+
+ QQuadPath path;
+ QPointF c = rect.center();
+ path.moveTo(QVector2D(rect.left(), c.y()));
+ path.lineTo(QVector2D(rect.right(), c.y()));
+
+ QSGCurveProcessor::processStroke(path, 2, rect.height(), Qt::MiterJoin, Qt::FlatCap,
+ [&node](const std::array<QVector2D, 3> &s,
+ const std::array<QVector2D, 3> &p,
+ const std::array<QVector2D, 3> &n,
+ bool isLine) {
+ Q_ASSERT(isLine);
+ node->appendTriangle(s, std::array<QVector2D, 2>{p.at(0), p.at(2)}, n);
+ });
+ node->cookGeometry();
+ appendChildNode(node);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhiinternaltextnode_p.h b/src/quick/scenegraph/qsgrhiinternaltextnode_p.h
new file mode 100644
index 0000000000..88980773b3
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhiinternaltextnode_p.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGRHIINTERNALTEXTNODE_P_H
+#define QSGRHIINTERNALTEXTNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsginternaltextnode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGRhiInternalTextNode : public QSGInternalTextNode
+{
+public:
+ QSGRhiInternalTextNode(QSGRenderContext *renderContext);
+ void addDecorationNode(const QRectF &rect, const QColor &color) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHIINTERNALTEXTNODE_P_H
diff --git a/src/quick/scenegraph/qsgrhilayer.cpp b/src/quick/scenegraph/qsgrhilayer.cpp
index 14ed4dee6d..5299cb54ce 100644
--- a/src/quick/scenegraph/qsgrhilayer.cpp
+++ b/src/quick/scenegraph/qsgrhilayer.cpp
@@ -443,10 +443,13 @@ QImage QSGRhiLayer::toImage() const
return QImage();
}
- // There is no room for negotiation here, the texture is RGBA8, and the
- // readback happens with GL_RGBA on GL, so RGBA8888 is the only option.
+ // There is little room for negotiation here, the texture is one of the formats from setFormat.
// Also, Quick is always premultiplied alpha.
- const QImage::Format imageFormat = QImage::Format_RGBA8888_Premultiplied;
+ QImage::Format imageFormat = QImage::Format_RGBA8888_Premultiplied;
+ if (m_format == QRhiTexture::RGBA16F)
+ imageFormat = QImage::Format_RGBA16FPx4_Premultiplied;
+ else if (m_format == QRhiTexture::RGBA32F)
+ imageFormat = QImage::Format_RGBA32FPx4_Premultiplied;
const uchar *p = reinterpret_cast<const uchar *>(result.data.constData());
return QImage(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat).mirrored();
diff --git a/src/quick/scenegraph/qsgrhilayer_p.h b/src/quick/scenegraph/qsgrhilayer_p.h
index 502672b725..922192ec5e 100644
--- a/src/quick/scenegraph/qsgrhilayer_p.h
+++ b/src/quick/scenegraph/qsgrhilayer_p.h
@@ -17,13 +17,13 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgcontext_p.h>
#include <private/qsgtexture_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
class QSGDefaultRenderContext;
-class Q_QUICK_PRIVATE_EXPORT QSGRhiLayer : public QSGLayer
+class Q_QUICK_EXPORT QSGRhiLayer : public QSGLayer
{
Q_OBJECT
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode.cpp b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
index 6d6e774918..c3b7b42cb9 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode.cpp
+++ b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
@@ -7,7 +7,7 @@
#include <qsgmaterialshader.h>
#include <qsgtextureprovider.h>
#include <private/qsgplaintexture_p.h>
-#include <QtGui/private/qshaderdescription_p.h>
+#include <rhi/qshaderdescription.h>
#include <QQmlFile>
#include <QFile>
#include <QFileSelector>
@@ -23,7 +23,7 @@ void QSGRhiShaderLinker::reset(const QShader &vs, const QShader &fs)
m_error = false;
- m_constantBufferSize = 0;
+ //m_constantBufferSize = 0;
m_constants.clear();
m_samplers.clear();
m_samplerNameMap.clear();
@@ -39,7 +39,6 @@ void QSGRhiShaderLinker::feedConstants(const QSGShaderEffectNode::ShaderData &sh
Q_ASSERT(shader.shaderInfo.variables.size() == shader.varData.size());
static bool shaderEffectDebug = qEnvironmentVariableIntValue("QSG_RHI_SHADEREFFECT_DEBUG");
if (!dirtyIndices) {
- m_constantBufferSize = qMax(m_constantBufferSize, shader.shaderInfo.constantDataSize);
for (int i = 0; i < shader.shaderInfo.variables.size(); ++i) {
const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(i));
if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Constant) {
@@ -86,6 +85,12 @@ void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &sha
const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Source);
+
+#ifndef QT_NO_DEBUG
+ int existingBindPoint = m_samplerNameMap.value(var.name, -1);
+ Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
+#endif
+
m_samplers.insert(var.bindPoint, vd.value);
m_samplerNameMap.insert(var.name, var.bindPoint);
}
@@ -94,6 +99,12 @@ void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &sha
for (int idx : *dirtyIndices) {
const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(idx));
const QSGShaderEffectNode::VariableData &vd(shader.varData.at(idx));
+
+#ifndef QT_NO_DEBUG
+ int existingBindPoint = m_samplerNameMap.value(var.name, -1);
+ Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
+#endif
+
m_samplers.insert(var.bindPoint, vd.value);
m_samplerNameMap.insert(var.name, var.bindPoint);
}
@@ -125,7 +136,7 @@ void QSGRhiShaderLinker::dump()
qDebug() << "Failed to generate program data";
return;
}
- qDebug() << "Combined shader data" << m_vs << m_fs << "cbuffer size" << m_constantBufferSize;
+ qDebug() << "Combined shader data" << m_vs << m_fs;
qDebug() << " - constants" << m_constants;
qDebug() << " - samplers" << m_samplers;
}
@@ -176,7 +187,7 @@ struct QSGRhiShaderMaterialTypeCache
QSGMaterialType *type;
};
QHash<Key, MaterialType> m_types;
- QVector<QSGMaterialType *> m_graveyard;
+ QHash<Key, QSGMaterialType *> m_graveyard;
};
size_t qHash(const QSGRhiShaderMaterialTypeCache::Key &key, size_t seed = 0)
@@ -196,6 +207,14 @@ QSGMaterialType *QSGRhiShaderMaterialTypeCache::ref(const QShader &vs, const QSh
return it->type;
}
+ auto reuseIt = m_graveyard.constFind(k);
+ if (reuseIt != m_graveyard.cend()) {
+ QSGMaterialType *t = reuseIt.value();
+ m_types.insert(k, { 1, t });
+ m_graveyard.erase(reuseIt);
+ return t;
+ }
+
QSGMaterialType *t = new QSGMaterialType;
m_types.insert(k, { 1, t });
return t;
@@ -208,7 +227,7 @@ void QSGRhiShaderMaterialTypeCache::unref(const QShader &vs, const QShader &fs)
auto it = m_types.find(k);
if (it != m_types.end()) {
if (!--it->ref) {
- m_graveyard.append(it->type);
+ m_graveyard.insert(k, it->type);
m_types.erase(it);
}
}
@@ -286,8 +305,27 @@ bool QSGRhiShaderEffectMaterialShader::updateUniformData(RenderState &state, QSG
}
} else if (c.specialType == QSGShaderEffectNode::VariableData::Matrix) {
if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- fillUniformBlockMember<float>(dst, m.constData(), 16, c.size);
+ Q_ASSERT(state.projectionMatrixCount() == mat->viewCount());
+ const int rendererViewCount = state.projectionMatrixCount();
+ const int shaderMatrixCount = c.size / 64;
+ if (shaderMatrixCount < mat->viewCount() && mat->viewCount() >= 2) {
+ qWarning("qt_Matrix uniform block member size is wrong: expected at least view_count * 64 bytes, "
+ "where view_count is %d, meaning %d bytes in total, but got only %d bytes. "
+ "This may be due to the ShaderEffect and its shaders not being multiview-capable, "
+ "or they are used with an unexpected render target. "
+ "Check if the shaders declare qt_Matrix as appropriate, "
+ "and if gl_ViewIndex is used correctly in the vertex shader.",
+ mat->viewCount(), mat->viewCount() * 64, c.size);
+ }
+ const int matrixCount = qMin(rendererViewCount, shaderMatrixCount);
+ size_t offset = 0;
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ fillUniformBlockMember<float>(dst + offset, m.constData(), 16, 64);
+ offset += 64;
+ }
+ if (offset < c.size)
+ memset(dst + offset, 0, c.size - offset);
changed = true;
}
} else if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
@@ -651,10 +689,42 @@ static QShader loadShaderFromFile(const QString &filename)
return QShader::fromSerialized(f.readAll());
}
+struct QSGRhiShaderEffectDefaultShader
+{
+ QShader shader;
+ quint32 matrixArrayByteSize;
+ quint32 opacityOffset;
+ qint8 viewCount;
+ static QSGRhiShaderEffectDefaultShader create(const QString &filename, int viewCount);
+};
+
+QSGRhiShaderEffectDefaultShader QSGRhiShaderEffectDefaultShader::create(const QString &filename, int viewCount)
+{
+ QSGRhiShaderEffectDefaultShader s;
+ s.shader = loadShaderFromFile(filename);
+ const QList<QShaderDescription::BlockVariable> uboMembers = s.shader.description().uniformBlocks().constFirst().members;
+ for (const auto &member: uboMembers) {
+ if (member.name == QByteArrayLiteral("qt_Matrix"))
+ s.matrixArrayByteSize = member.size;
+ else if (member.name == QByteArrayLiteral("qt_Opacity"))
+ s.opacityOffset = member.offset;
+ }
+ s.viewCount = viewCount;
+ return s;
+}
+
void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
{
- static QShader defaultVertexShader;
- static QShader defaultFragmentShader;
+ static const int defaultVertexShaderCount = 2;
+ static QSGRhiShaderEffectDefaultShader defaultVertexShaders[defaultVertexShaderCount] = {
+ QSGRhiShaderEffectDefaultShader::create(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb"), 1),
+ QSGRhiShaderEffectDefaultShader::create(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb.mv2qsb"), 2)
+ };
+ static const int defaultFragmentShaderCount = 2;
+ static QSGRhiShaderEffectDefaultShader defaultFragmentShaders[defaultFragmentShaderCount] = {
+ QSGRhiShaderEffectDefaultShader::create(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb"), 1),
+ QSGRhiShaderEffectDefaultShader::create(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb.mv2qsb"), 2)
+ };
if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) {
m_material.setFlag(QSGMaterial::Blending, syncData->blending);
@@ -673,21 +743,45 @@ void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
}
m_material.m_hasCustomVertexShader = syncData->vertex.shader->hasShaderCode;
+ quint32 defaultMatrixArrayByteSize = 0;
if (m_material.m_hasCustomVertexShader) {
m_material.m_vertexShader = syncData->vertex.shader->shaderInfo.rhiShader;
} else {
- if (!defaultVertexShader.isValid())
- defaultVertexShader = loadShaderFromFile(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb"));
- m_material.m_vertexShader = defaultVertexShader;
+ bool found = false;
+ for (int i = 0; i < defaultVertexShaderCount; ++i) {
+ if (defaultVertexShaders[i].viewCount == syncData->viewCount) {
+ m_material.m_vertexShader = defaultVertexShaders[i].shader;
+ defaultMatrixArrayByteSize = defaultVertexShaders[i].matrixArrayByteSize;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ qWarning("No default vertex shader found for view count %d", syncData->viewCount);
+ m_material.m_vertexShader = defaultVertexShaders[0].shader;
+ defaultMatrixArrayByteSize = 64;
+ }
}
m_material.m_hasCustomFragmentShader = syncData->fragment.shader->hasShaderCode;
+ quint32 defaultOpacityOffset = 0;
if (m_material.m_hasCustomFragmentShader) {
m_material.m_fragmentShader = syncData->fragment.shader->shaderInfo.rhiShader;
} else {
- if (!defaultFragmentShader.isValid())
- defaultFragmentShader = loadShaderFromFile(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb"));
- m_material.m_fragmentShader = defaultFragmentShader;
+ bool found = false;
+ for (int i = 0; i < defaultFragmentShaderCount; ++i) {
+ if (defaultFragmentShaders[i].viewCount == syncData->viewCount) {
+ m_material.m_fragmentShader = defaultFragmentShaders[i].shader;
+ defaultOpacityOffset = defaultFragmentShaders[i].opacityOffset;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ qWarning("No default fragment shader found for view count %d", syncData->viewCount);
+ m_material.m_fragmentShader = defaultFragmentShaders[0].shader;
+ defaultOpacityOffset = 64;
+ }
}
m_material.m_materialType = shaderMaterialTypeCache[syncData->materialTypeCacheKey].ref(m_material.m_vertexShader,
@@ -705,16 +799,15 @@ void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
defaultSD.shaderInfo.rhiShader = m_material.m_vertexShader;
defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex;
- // { mat4 qt_Matrix; float qt_Opacity; } where only the matrix is used
+ // { mat4 qt_Matrix[VIEW_COUNT]; float qt_Opacity; } where only the matrix is used
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("qt_Matrix");
v.offset = 0;
- v.size = 16 * sizeof(float);
+ v.size = defaultMatrixArrayByteSize;
defaultSD.shaderInfo.variables.append(v);
QSGShaderEffectNode::VariableData vd;
vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
defaultSD.varData.append(vd);
- defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
m_material.m_linker.feedConstants(defaultSD);
}
@@ -727,10 +820,10 @@ void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
defaultSD.shaderInfo.rhiShader = m_material.m_fragmentShader;
defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
- // { mat4 qt_Matrix; float qt_Opacity; } where only the opacity is used
+ // { mat4 qt_Matrix[VIEW_COUNT]; float qt_Opacity; } where only the opacity is used
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("qt_Opacity");
- v.offset = 16 * sizeof(float);
+ v.offset = defaultOpacityOffset;
v.size = sizeof(float);
defaultSD.shaderInfo.variables.append(v);
QSGShaderEffectNode::VariableData vd;
@@ -750,8 +843,6 @@ void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
vd.specialType = QSGShaderEffectNode::VariableData::Source;
defaultSD.varData.append(vd);
- defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
-
m_material.m_linker.feedConstants(defaultSD);
m_material.m_linker.feedSamplers(defaultSD);
}
@@ -875,7 +966,6 @@ bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
}
const QShaderDescription desc = result->rhiShader.description();
- result->constantDataSize = 0;
int ubufBinding = -1;
const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
@@ -884,7 +974,6 @@ bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
if (ubufBinding == -1 && ubuf.binding >= 0) {
ubufBinding = ubuf.binding;
- result->constantDataSize = ubuf.size;
for (const QShaderDescription::BlockVariable &member : ubuf.members) {
ShaderInfo::Variable v;
v.type = ShaderInfo::Constant;
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode_p.h b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
index ed3bf1f1e9..376c7d38ab 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode_p.h
+++ b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
@@ -50,7 +50,6 @@ public:
bool m_error;
QShader m_vs;
QShader m_fs;
- uint m_constantBufferSize;
QHash<uint, Constant> m_constants; // offset -> Constant
QHash<int, QVariant> m_samplers; // binding -> value (source ref)
QHash<QByteArray, int> m_samplerNameMap; // name -> binding
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index f18d6a7d00..45c183a5f8 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -50,19 +50,22 @@ void QSGRhiSupport::applySettings()
if (m_requested.valid) {
// explicit rhi backend request from C++ (e.g. via QQuickWindow)
switch (m_requested.api) {
- case QSGRendererInterface::OpenGLRhi:
+ case QSGRendererInterface::OpenGL:
m_rhiBackend = QRhi::OpenGLES2;
break;
- case QSGRendererInterface::Direct3D11Rhi:
+ case QSGRendererInterface::Direct3D11:
m_rhiBackend = QRhi::D3D11;
break;
- case QSGRendererInterface::VulkanRhi:
+ case QSGRendererInterface::Direct3D12:
+ m_rhiBackend = QRhi::D3D12;
+ break;
+ case QSGRendererInterface::Vulkan:
m_rhiBackend = QRhi::Vulkan;
break;
- case QSGRendererInterface::MetalRhi:
+ case QSGRendererInterface::Metal:
m_rhiBackend = QRhi::Metal;
break;
- case QSGRendererInterface::NullRhi:
+ case QSGRendererInterface::Null:
m_rhiBackend = QRhi::Null;
break;
default:
@@ -79,6 +82,8 @@ void QSGRhiSupport::applySettings()
m_rhiBackend = QRhi::OpenGLES2;
} else if (rhiBackend == QByteArrayLiteral("d3d11") || rhiBackend == QByteArrayLiteral("d3d")) {
m_rhiBackend = QRhi::D3D11;
+ } else if (rhiBackend == QByteArrayLiteral("d3d12")) {
+ m_rhiBackend = QRhi::D3D12;
} else if (rhiBackend == QByteArrayLiteral("vulkan")) {
m_rhiBackend = QRhi::Vulkan;
} else if (rhiBackend == QByteArrayLiteral("metal")) {
@@ -92,7 +97,7 @@ void QSGRhiSupport::applySettings()
}
#if defined(Q_OS_WIN)
m_rhiBackend = QRhi::D3D11;
-#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#elif QT_CONFIG(metal)
m_rhiBackend = QRhi::Metal;
#elif QT_CONFIG(opengl)
m_rhiBackend = QRhi::OpenGLES2;
@@ -111,26 +116,11 @@ void QSGRhiSupport::applySettings()
// (QQuickWindow) may depend on the graphics API as well (surfaceType
// f.ex.), and all that is based on what we report from here. So further
// adjustments are not possible (or, at minimum, not safe and portable).
-
- m_killDeviceFrameCount = qEnvironmentVariableIntValue("QSG_RHI_SIMULATE_DEVICE_LOSS");
- if (m_killDeviceFrameCount > 0 && m_rhiBackend == QRhi::D3D11)
- qDebug("Graphics device will be reset every %d frames", m_killDeviceFrameCount);
-
- QByteArray hdrRequest = qgetenv("QSG_RHI_HDR");
- if (!hdrRequest.isEmpty()) {
- hdrRequest = hdrRequest.toLower();
- if (hdrRequest == QByteArrayLiteral("scrgb") || hdrRequest == QByteArrayLiteral("extendedsrgblinear"))
- m_swapChainFormat = QRhiSwapChain::HDRExtendedSrgbLinear;
- else if (hdrRequest == QByteArrayLiteral("hdr10"))
- m_swapChainFormat = QRhiSwapChain::HDR10;
- else
- qWarning("Unknown HDR mode '%s'", hdrRequest.constData());
- }
}
void QSGRhiSupport::adjustToPlatformQuirks()
{
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
// A macOS VM may not have Metal support at all. We have to decide at this
// point, it will be too late afterwards, and the only way is to see if
// MTLCreateSystemDefaultDevice succeeds.
@@ -225,12 +215,24 @@ void QSGRhiSupport::checkEnvQSgInfo()
#define GL_RGB10_A2 0x8059
#endif
-QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromGL(uint format)
+#ifndef GL_SRGB_ALPHA
+#define GL_SRGB_ALPHA 0x8C42
+#endif
+
+#ifndef GL_SRGB8_ALPHA8
+#define GL_SRGB8_ALPHA8 0x8C43
+#endif
+
+QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromGL(uint format, QRhiTexture::Flags *flags)
{
+ bool sRGB = false;
auto rhiFormat = QRhiTexture::UnknownFormat;
switch (format) {
- case GL_RGBA:
+ case GL_SRGB_ALPHA:
+ case GL_SRGB8_ALPHA8:
+ sRGB = true;
Q_FALLTHROUGH();
+ case GL_RGBA:
case GL_RGBA8:
case 0:
rhiFormat = QRhiTexture::RGBA8;
@@ -292,6 +294,8 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromGL(uint format)
qWarning("GL format %d is not supported", format);
break;
}
+ if (sRGB)
+ (*flags) |=(QRhiTexture::sRGB);
return rhiFormat;
}
#endif
@@ -508,7 +512,7 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromVulkan(uint format, QRh
#endif
#ifdef Q_OS_WIN
-QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromD3D11(uint format, QRhiTexture::Flags *flags)
+QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromDXGI(uint format, QRhiTexture::Flags *flags)
{
auto rhiFormat = QRhiTexture::UnknownFormat;
bool sRGB = false;
@@ -608,7 +612,7 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromD3D11(uint format, QRhi
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
namespace QSGRhiSupportMac {
QRhiTexture::Format toRhiTextureFormatFromMetal(uint format, QRhiTexture::Flags *flags);
}
@@ -655,15 +659,17 @@ QSGRendererInterface::GraphicsApi QSGRhiSupport::graphicsApi() const
{
switch (m_rhiBackend) {
case QRhi::Null:
- return QSGRendererInterface::NullRhi;
+ return QSGRendererInterface::Null;
case QRhi::Vulkan:
- return QSGRendererInterface::VulkanRhi;
+ return QSGRendererInterface::Vulkan;
case QRhi::OpenGLES2:
- return QSGRendererInterface::OpenGLRhi;
+ return QSGRendererInterface::OpenGL;
case QRhi::D3D11:
- return QSGRendererInterface::Direct3D11Rhi;
+ return QSGRendererInterface::Direct3D11;
+ case QRhi::D3D12:
+ return QSGRendererInterface::Direct3D12;
case QRhi::Metal:
- return QSGRendererInterface::MetalRhi;
+ return QSGRendererInterface::Metal;
default:
return QSGRendererInterface::Unknown;
}
@@ -677,6 +683,7 @@ QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const
case QRhi::OpenGLES2:
return QSurface::OpenGLSurface;
case QRhi::D3D11:
+ case QRhi::D3D12:
return QSurface::Direct3DSurface;
case QRhi::Metal:
return QSurface::MetalSurface;
@@ -714,6 +721,10 @@ static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res,
return &maybeVkRpNat->renderPass;
else
return nullptr;
+ case QSGRendererInterface::GraphicsQueueFamilyIndexResource:
+ return &vknat->gfxQueueFamilyIdx;
+ case QSGRendererInterface::GraphicsQueueIndexResource:
+ return &vknat->gfxQueueIdx;
default:
return nullptr;
}
@@ -746,9 +757,22 @@ static const void *qsgrhi_d3d11_rifResource(QSGRendererInterface::Resource res,
return nullptr;
}
}
+
+static const void *qsgrhi_d3d12_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
+{
+ const QRhiD3D12NativeHandles *d3dnat = static_cast<const QRhiD3D12NativeHandles *>(nat);
+ switch (res) {
+ case QSGRendererInterface::DeviceResource:
+ return d3dnat->dev;
+ case QSGRendererInterface::CommandQueueResource:
+ return d3dnat->commandQueue;
+ default:
+ return nullptr;
+ }
+}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
static const void *qsgrhi_mtl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat,
const QRhiNativeHandles *cbNat)
{
@@ -795,7 +819,7 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res,
case QSGRendererInterface::RhiRedirectCommandBuffer:
return QQuickWindowPrivate::get(w)->redirect.commandBuffer;
case QSGRendererInterface::RhiRedirectRenderTarget:
- return QQuickWindowPrivate::get(w)->redirect.rt.renderTarget;
+ return QQuickWindowPrivate::get(w)->redirect.rt.rt.renderTarget;
default:
break;
}
@@ -822,8 +846,10 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res,
#ifdef Q_OS_WIN
case QRhi::D3D11:
return qsgrhi_d3d11_rifResource(res, nat);
+ case QRhi::D3D12:
+ return qsgrhi_d3d12_rifResource(res, nat);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
case QRhi::Metal:
{
QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
@@ -1026,8 +1052,18 @@ void QSGRhiSupport::finalizePipelineCache(QRhi *rhi, const QQuickGraphicsConfigu
// If empty, do nothing. This is exactly what will happen if the rhi was
// created without QRhi::EnablePipelineCacheDataSave set.
- if (buf.isEmpty())
+ if (buf.isEmpty()) {
+ if (isAutomatic) {
+ // Attempt to remove the file. If it does not exist or this fails,
+ // that's fine. The goal is just to prevent warnings from
+ // setPipelineCacheData in future runs, e.g. if the Qt or driver
+ // version does not match _and_ we do not generate any data at run
+ // time, then not writing the file out also means the warning would
+ // appear again and again on every run. Prevent that.
+ QDir().remove(pipelineCacheSave);
+ }
return;
+ }
QLockFile lock(pipelineCacheLockFileName(pipelineCacheSave));
if (!lock.lock()) {
@@ -1068,7 +1104,7 @@ void QSGRhiSupport::finalizePipelineCache(QRhi *rhi, const QQuickGraphicsConfigu
}
// must be called on the render thread
-QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QSurface *offscreenSurface)
+QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QSurface *offscreenSurface, bool forcePreferSwRenderer)
{
QRhi *rhi = nullptr;
QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
@@ -1083,7 +1119,8 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS
const bool debugLayer = wd->graphicsConfig.isDebugLayerEnabled();
const bool debugMarkers = wd->graphicsConfig.isDebugMarkersEnabled();
- const bool preferSoftware = wd->graphicsConfig.prefersSoftwareDevice();
+ const bool timestamps = wd->graphicsConfig.timestampsEnabled();
+ const bool preferSoftware = wd->graphicsConfig.prefersSoftwareDevice() || forcePreferSwRenderer;
const bool pipelineCacheSave = !wd->graphicsConfig.pipelineCacheSaveFile().isEmpty()
|| (wd->graphicsConfig.isAutomaticPipelineCacheEnabled()
&& !isAutomaticPipelineCacheSaveSkippedForWindow(window->flags()));
@@ -1093,13 +1130,18 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS
"Creating QRhi with backend %s for window %p (wflags 0x%X)\n"
" Graphics API debug/validation layers: %d\n"
" Debug markers: %d\n"
- " Prefer software device: %d\n"
+ " Timestamps: %d\n"
+ " Prefer software device: %d%s\n"
" Shader/pipeline cache collection: %d",
- qPrintable(backendName), window, int(window->flags()), debugLayer, debugMarkers, preferSoftware, pipelineCacheSave);
+ qPrintable(backendName), window, int(window->flags()), debugLayer,
+ debugMarkers, timestamps, preferSoftware, forcePreferSwRenderer ? " [FORCED]" : "", pipelineCacheSave);
QRhi::Flags flags;
+ flags |= QRhi::SuppressSmokeTestWarnings;
if (debugMarkers)
flags |= QRhi::EnableDebugMarkers;
+ if (timestamps)
+ flags |= QRhi::EnableTimestamps;
if (preferSoftware)
flags |= QRhi::PreferSoftwareRenderer;
if (pipelineCacheSave)
@@ -1169,10 +1211,6 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS
if (backend == QRhi::D3D11) {
QRhiD3D11InitParams rhiParams;
rhiParams.enableDebugLayer = debugLayer;
- if (m_killDeviceFrameCount > 0) {
- rhiParams.framesUntilKillingDeviceViaTdr = m_killDeviceFrameCount;
- rhiParams.repeatDeviceKill = true;
- }
if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) {
QRhiD3D11NativeHandles importDev;
importDev.dev = customDevD->u.deviceAndContext.device;
@@ -1190,7 +1228,32 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS
rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
} else {
rhi = QRhi::create(backend, &rhiParams, flags);
- if (!rhi && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
+ if (!rhi && attemptReinitWithSwRastUponFail() && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
+ qCDebug(QSG_LOG_INFO, "Failed to create a D3D device with default settings; "
+ "attempting to get a software rasterizer backed device instead");
+ flags |= QRhi::PreferSoftwareRenderer;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+ }
+ } else if (backend == QRhi::D3D12) {
+ QRhiD3D12InitParams rhiParams;
+ rhiParams.enableDebugLayer = debugLayer;
+ if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) {
+ QRhiD3D12NativeHandles importDev;
+ importDev.dev = customDevD->u.deviceAndContext.device;
+ qCDebug(QSG_LOG_INFO, "Using existing native D3D12 device %p", importDev.dev);
+ rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
+ } else if (customDevD->type == QQuickGraphicsDevicePrivate::Type::Adapter) {
+ QRhiD3D12NativeHandles importDev;
+ importDev.adapterLuidLow = customDevD->u.adapter.luidLow;
+ importDev.adapterLuidHigh = customDevD->u.adapter.luidHigh;
+ importDev.minimumFeatureLevel = customDevD->u.adapter.featureLevel;
+ qCDebug(QSG_LOG_INFO, "Using D3D12 adapter LUID %u, %d and minimum feature level %d",
+ importDev.adapterLuidLow, importDev.adapterLuidHigh, importDev.minimumFeatureLevel);
+ rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
+ } else {
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ if (!rhi && attemptReinitWithSwRastUponFail() && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
qCDebug(QSG_LOG_INFO, "Failed to create a D3D device with default settings; "
"attempting to get a software rasterizer backed device instead");
flags |= QRhi::PreferSoftwareRenderer;
@@ -1199,7 +1262,7 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS
}
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (backend == QRhi::Metal) {
QRhiMetalInitParams rhiParams;
if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndCommandQueue) {
@@ -1460,10 +1523,28 @@ QImage QSGRhiSupport::grabOffscreenForProtectedContent(QQuickWindow *window)
}
#endif
-void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet)
+void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet, QQuickWindow *window)
{
+ Q_ASSERT(scWithWindowSet->window() == window);
+
+ QRhiSwapChain::Format swapChainFormat = QRhiSwapChain::SDR;
+
+ QByteArray hdrRequest = qgetenv("QSG_RHI_HDR");
+ if (hdrRequest.isEmpty())
+ hdrRequest = window->property("_qt_sg_hdr_format").toByteArray();
+
+ if (!hdrRequest.isEmpty()) {
+ hdrRequest = hdrRequest.toLower();
+ if (hdrRequest == QByteArrayLiteral("scrgb") || hdrRequest == QByteArrayLiteral("extendedsrgblinear"))
+ swapChainFormat = QRhiSwapChain::HDRExtendedSrgbLinear;
+ else if (hdrRequest == QByteArrayLiteral("hdr10"))
+ swapChainFormat = QRhiSwapChain::HDR10;
+ else if (hdrRequest == QByteArrayLiteral("p3"))
+ swapChainFormat = QRhiSwapChain::HDRExtendedDisplayP3Linear;
+ }
+
const char *fmtStr = "unknown";
- switch (m_swapChainFormat) {
+ switch (swapChainFormat) {
case QRhiSwapChain::SDR:
fmtStr = "SDR";
break;
@@ -1473,12 +1554,15 @@ void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet)
case QRhiSwapChain::HDR10:
fmtStr = "HDR10";
break;
+ case QRhiSwapChain::HDRExtendedDisplayP3Linear:
+ fmtStr = "Extended Linear Display P3";
+ break;
default:
break;
}
- if (!scWithWindowSet->isFormatSupported(m_swapChainFormat)) {
- if (m_swapChainFormat != QRhiSwapChain::SDR) {
+ if (!scWithWindowSet->isFormatSupported(swapChainFormat)) {
+ if (swapChainFormat != QRhiSwapChain::SDR) {
qCDebug(QSG_LOG_INFO, "Requested a %s swapchain but it is reported to be unsupported with the current display(s). "
"In multi-screen configurations make sure the window is located on a HDR-enabled screen. "
"Request ignored, using SDR swapchain.", fmtStr);
@@ -1486,9 +1570,9 @@ void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet)
return;
}
- scWithWindowSet->setFormat(m_swapChainFormat);
+ scWithWindowSet->setFormat(swapChainFormat);
- if (m_swapChainFormat != QRhiSwapChain::SDR) {
+ if (swapChainFormat != QRhiSwapChain::SDR) {
qCDebug(QSG_LOG_INFO, "Creating %s swapchain", fmtStr);
qCDebug(QSG_LOG_INFO) << "HDR output info:" << scWithWindowSet->hdrInfo();
}
@@ -1504,19 +1588,36 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormat(uint nativeFormat, QRhiTex
#if QT_CONFIG(opengl)
case QRhi::OpenGLES2:
Q_UNUSED(flags);
- return toRhiTextureFormatFromGL(nativeFormat);
+ return toRhiTextureFormatFromGL(nativeFormat, flags);
#endif
#ifdef Q_OS_WIN
case QRhi::D3D11:
- return toRhiTextureFormatFromD3D11(nativeFormat, flags);
+ case QRhi::D3D12:
+ return toRhiTextureFormatFromDXGI(nativeFormat, flags);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
case QRhi::Metal:
return toRhiTextureFormatFromMetal(nativeFormat, flags);
#endif
default:
return QRhiTexture::UnknownFormat;
}
+ Q_UNUSED(nativeFormat)
+ Q_UNUSED(flags)
+}
+
+bool QSGRhiSupport::attemptReinitWithSwRastUponFail() const
+{
+ const QRhi::Implementation backend = rhiBackend();
+
+ // On Windows it makes sense to retry using a software adapter whenever
+ // device creation or swapchain creation fails, as WARP is usually available
+ // (built in to the OS) and is good quality. This helps a lot in particular
+ // when running in a VM that cripples proper 3D graphics.
+ if (backend == QRhi::D3D11 || backend == QRhi::D3D12)
+ return true;
+
+ return false;
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhisupport_p.h b/src/quick/scenegraph/qsgrhisupport_p.h
index 42cbecabcb..535317d48c 100644
--- a/src/quick/scenegraph/qsgrhisupport_p.h
+++ b/src/quick/scenegraph/qsgrhisupport_p.h
@@ -18,25 +18,7 @@
#include "qsgrenderloop_p.h"
#include "qsgrendererinterface.h"
-#include <QtGui/private/qrhi_p.h>
-
-#include <QtGui/private/qrhinull_p.h>
-
-#if QT_CONFIG(opengl)
-#include <QtGui/private/qrhigles2_p.h>
-#endif
-
-#if QT_CONFIG(vulkan)
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
-
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#endif
-
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
-#endif
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -52,7 +34,7 @@ class QQuickGraphicsConfiguration;
// In addition, the class provides handy conversion and query stuff for the
// renderloop and the QSGRendererInterface implementations.
//
-class Q_QUICK_PRIVATE_EXPORT QSGRhiSupport
+class Q_QUICK_EXPORT QSGRhiSupport
{
public:
static QSGRhiSupport *instance_internal();
@@ -63,7 +45,7 @@ public:
static void checkEnvQSgInfo();
#if QT_CONFIG(opengl)
- static QRhiTexture::Format toRhiTextureFormatFromGL(uint format);
+ static QRhiTexture::Format toRhiTextureFormatFromGL(uint format, QRhiTexture::Flags *flags);
#endif
#if QT_CONFIG(vulkan)
@@ -71,10 +53,10 @@ public:
#endif
#if defined(Q_OS_WIN)
- static QRhiTexture::Format toRhiTextureFormatFromD3D11(uint format, QRhiTexture::Flags *flags);
+ static QRhiTexture::Format toRhiTextureFormatFromDXGI(uint format, QRhiTexture::Flags *flags);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
static QRhiTexture::Format toRhiTextureFormatFromMetal(uint format, QRhiTexture::Flags *flags);
#endif
@@ -95,7 +77,7 @@ public:
QRhi *rhi;
bool own;
};
- RhiCreateResult createRhi(QQuickWindow *window, QSurface *offscreenSurface);
+ RhiCreateResult createRhi(QQuickWindow *window, QSurface *offscreenSurface, bool forcePreferSwRenderer = false);
void destroyRhi(QRhi *rhi, const QQuickGraphicsConfiguration &config);
void prepareWindowForRhi(QQuickWindow *window);
@@ -104,11 +86,12 @@ public:
QImage grabOffscreenForProtectedContent(QQuickWindow *window);
#endif
- QRhiSwapChain::Format swapChainFormat() const { return m_swapChainFormat; }
- void applySwapChainFormat(QRhiSwapChain *scWithWindowSet);
+ void applySwapChainFormat(QRhiSwapChain *scWithWindowSet, QQuickWindow *window);
QRhiTexture::Format toRhiTextureFormat(uint nativeFormat, QRhiTexture::Flags *flags) const;
+ bool attemptReinitWithSwRastUponFail() const;
+
private:
QSGRhiSupport();
void applySettings();
@@ -121,8 +104,6 @@ private:
} m_requested;
bool m_settingsApplied = false;
QRhi::Implementation m_rhiBackend = QRhi::Null;
- int m_killDeviceFrameCount;
- QRhiSwapChain::Format m_swapChainFormat = QRhiSwapChain::SDR;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
index 2324db5762..7e626be8e2 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
@@ -23,15 +23,7 @@ QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QSGDefaultRenderContext *rc,
QSGRhiTextureGlyphCache::~QSGRhiTextureGlyphCache()
{
- // A plain delete should work, but just in case commitResourceUpdates was
- // not called and something is enqueued on the update batch for m_texture,
- // defer until the end of the frame.
- if (m_texture)
- m_texture->deleteLater();
-
- // should be empty, but just in case
- for (QRhiTexture *t : std::as_const(m_pendingDispose))
- t->deleteLater();
+ m_rc->deferredReleaseGlyphCacheTexture(m_texture);
}
QRhiTexture *QSGRhiTextureGlyphCache::createEmptyTexture(QRhiTexture::Format format)
@@ -97,7 +89,7 @@ void QSGRhiTextureGlyphCache::resizeTextureData(int width, int height)
resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
}
- m_pendingDispose.insert(m_texture);
+ m_rc->deferredReleaseGlyphCacheTexture(m_texture);
m_texture = t;
}
}
@@ -117,36 +109,42 @@ void QSGRhiTextureGlyphCache::prepareGlyphImage(QImage *img)
m_bgra = false;
if (img->format() == QImage::Format_Mono) {
- *img = img->convertToFormat(QImage::Format_Grayscale8);
- } else if (img->depth() == 32) {
- if (img->format() == QImage::Format_RGB32 || img->format() == QImage::Format_ARGB32_Premultiplied) {
- // We need to make the alpha component equal to the average of the RGB values.
- // This is needed when drawing sub-pixel antialiased text on translucent targets.
+ *img = std::move(*img).convertToFormat(QImage::Format_Grayscale8);
+ } else if (img->format() == QImage::Format_RGB32 || img->format() == QImage::Format_ARGB32_Premultiplied) {
+ // We need to make the alpha component equal to the average of the RGB values.
+ // This is needed when drawing sub-pixel antialiased text on translucent targets.
+ if (img->format() == QImage::Format_RGB32
+#if Q_BYTE_ORDER != Q_BIG_ENDIAN
+ || !supportsBgra
+#endif
+ ) {
for (int y = 0; y < maskHeight; ++y) {
- QRgb *src = (QRgb *) img->scanLine(y);
+ QRgb *src = reinterpret_cast<QRgb *>(img->scanLine(y));
for (int x = 0; x < maskWidth; ++x) {
- int r = qRed(src[x]);
- int g = qGreen(src[x]);
- int b = qBlue(src[x]);
- int avg;
- if (img->format() == QImage::Format_RGB32)
- avg = (r + g + b + 1) / 3; // "+1" for rounding.
- else // Format_ARGB_Premultiplied
- avg = qAlpha(src[x]);
-
- src[x] = qRgba(r, g, b, avg);
+ QRgb &rgb = src[x];
+
+ if (img->format() == QImage::Format_RGB32) {
+ int r = qRed(rgb);
+ int g = qGreen(rgb);
+ int b = qBlue(rgb);
+ int avg = (r + g + b + 1) / 3; // "+1" for rounding.
+ rgb = qRgba(r, g, b, avg);
+ }
+
#if Q_BYTE_ORDER != Q_BIG_ENDIAN
- if (supportsBgra) {
- m_bgra = true;
- } else {
+ if (!supportsBgra) {
// swizzle the bits to accommodate for the RGBA upload.
- src[x] = ARGB2RGBA(src[x]);
+ rgb = ARGB2RGBA(rgb);
m_bgra = false;
}
#endif
}
}
}
+#if Q_BYTE_ORDER != Q_BIG_ENDIAN
+ if (supportsBgra)
+ m_bgra = true;
+#endif
}
}
@@ -157,9 +155,9 @@ void QSGRhiTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, const Q
if (!m_resizeWithTextureCopy) {
QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);
- mask = image();
- subresDesc.setSourceTopLeft(QPoint(c.x, c.y));
- subresDesc.setSourceSize(QSize(c.w, c.h));
+ // Explicitly copy() here to avoid fillTexture detaching the *entire* image() when
+ // it is still referenced by QRhiTextureSubresourceUploadDescription.
+ mask = image().copy(QRect(c.x, c.y, c.w, c.h));
} else {
mask = textureMapForGlyph(glyph, subPixelPosition);
}
@@ -220,14 +218,8 @@ void QSGRhiTextureGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mer
{
if (QRhiResourceUpdateBatch *resourceUpdates = m_rc->maybeGlyphCacheResourceUpdates()) {
mergeInto->merge(resourceUpdates);
- m_rc->releaseGlyphCacheResourceUpdates();
+ m_rc->resetGlyphCacheResources();
}
-
- // now let's assume the resource updates will be committed in this frame
- for (QRhiTexture *t : std::as_const(m_pendingDispose))
- t->deleteLater(); // will be deleted after the frame is submitted -> safe
-
- m_pendingDispose.clear();
}
bool QSGRhiTextureGlyphCache::eightBitFormatIsAlphaSwizzled() const
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
index 2960c91b01..fbe020c3a2 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
@@ -16,7 +16,7 @@
//
#include <QtGui/private/qtextureglyphcache_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -60,7 +60,6 @@ private:
QSize m_size;
bool m_bgra = false;
QVarLengthArray<QRhiTextureUploadEntry, 16> m_uploads;
- QSet<QRhiTexture *> m_pendingDispose;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 2a1ceedb8f..84450c692b 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -85,6 +85,15 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtquick, QSG_polishAndSync_entry)
+Q_TRACE_POINT(qtquick, QSG_polishAndSync_exit)
+Q_TRACE_POINT(qtquick, QSG_wait_entry)
+Q_TRACE_POINT(qtquick, QSG_wait_exit)
+Q_TRACE_POINT(qtquick, QSG_syncAndRender_entry)
+Q_TRACE_POINT(qtquick, QSG_syncAndRender_exit)
+Q_TRACE_POINT(qtquick, QSG_animations_entry)
+Q_TRACE_POINT(qtquick, QSG_animations_exit)
+
#define QSG_RT_PAD " (RT) %s"
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
@@ -92,40 +101,16 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_
// RL: Render Loop
// RT: Render Thread
-// Passed from the RL to the RT when a window is removed obscured and
-// should be removed from the render loop.
-const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
-
-// Passed from the RL to RT when GUI has been locked, waiting for sync
-// (updatePaintNode())
-const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
-
-// Passed by the RL to the RT to free up maybe release SG and GL contexts
-// if no windows are rendering.
-const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
-
-// Passed by the RL to the RT when a QQuickWindow::grabWindow() is
-// called.
-const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
-
-// Passed by the window when there is a render job to run
-const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
-// When using the QRhi this is sent upon PlatformSurfaceAboutToBeDestroyed from
-// the event filter installed on the QQuickWindow.
-const QEvent::Type WM_ReleaseSwapchain = QEvent::Type(QEvent::User + 7);
-
-template <typename T> T *windowFor(const QList<T> &list, QQuickWindow *window)
+QSGThreadedRenderLoop::Window *QSGThreadedRenderLoop::windowFor(QQuickWindow *window)
{
- for (int i=0; i<list.size(); ++i) {
- const T &t = list.at(i);
+ for (const auto &t : std::as_const(m_windows)) {
if (t.window == window)
- return const_cast<T *>(&t);
+ return const_cast<Window *>(&t);
}
return nullptr;
}
-
class WMWindowEvent : public QEvent
{
public:
@@ -137,7 +122,7 @@ class WMTryReleaseEvent : public WMWindowEvent
{
public:
WMTryReleaseEvent(QQuickWindow *win, bool destroy, bool needsFallbackSurface)
- : WMWindowEvent(win, WM_TryRelease)
+ : WMWindowEvent(win, QEvent::Type(WM_TryRelease))
, inDestructor(destroy)
, needsFallback(needsFallbackSurface)
{}
@@ -150,7 +135,7 @@ class WMSyncEvent : public WMWindowEvent
{
public:
WMSyncEvent(QQuickWindow *c, bool inExpose, bool force, const QRhiSwapChainProxyData &scProxyData)
- : WMWindowEvent(c, WM_RequestSync)
+ : WMWindowEvent(c, QEvent::Type(WM_RequestSync))
, size(c->size())
, dpr(float(c->effectiveDevicePixelRatio()))
, syncInExpose(inExpose)
@@ -168,7 +153,8 @@ public:
class WMGrabEvent : public WMWindowEvent
{
public:
- WMGrabEvent(QQuickWindow *c, QImage *result) : WMWindowEvent(c, WM_Grab), image(result) {}
+ WMGrabEvent(QQuickWindow *c, QImage *result) :
+ WMWindowEvent(c, QEvent::Type(WM_Grab)), image(result) {}
QImage *image;
};
@@ -176,7 +162,7 @@ class WMJobEvent : public WMWindowEvent
{
public:
WMJobEvent(QQuickWindow *c, QRunnable *postedJob)
- : WMWindowEvent(c, WM_PostJob), job(postedJob) {}
+ : WMWindowEvent(c, QEvent::Type(WM_PostJob)), job(postedJob) {}
~WMJobEvent() { delete job; }
QRunnable *job;
};
@@ -184,7 +170,8 @@ public:
class WMReleaseSwapchainEvent : public WMWindowEvent
{
public:
- WMReleaseSwapchainEvent(QQuickWindow *c) : WMWindowEvent(c, WM_ReleaseSwapchain) { }
+ WMReleaseSwapchainEvent(QQuickWindow *c) :
+ WMWindowEvent(c, QEvent::Type(WM_ReleaseSwapchain)) { }
};
class QSGRenderThreadEventQueue : public QQueue<QEvent *>
@@ -293,6 +280,7 @@ public:
};
void ensureRhi();
+ void teardownGraphics();
void handleDeviceLoss();
QSGThreadedRenderLoop *wm;
@@ -322,6 +310,7 @@ public:
bool rhiDeviceLost = false;
bool rhiDoomed = false;
bool guiNotifiedAboutRhiFailure = false;
+ bool swRastFallbackDueToSwapchainFailure = false;
// Local event queue stuff...
bool stopEventProcessing;
@@ -588,22 +577,27 @@ void QSGRenderThread::sync(bool inExpose)
}
}
-void QSGRenderThread::handleDeviceLoss()
+void QSGRenderThread::teardownGraphics()
{
- if (!rhi || !rhi->isDeviceLost())
- return;
-
- qWarning("Graphics device lost, cleaning up scenegraph and releasing RHI");
QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
wd->cleanupNodesOnShutdown();
sgrc->invalidate();
wm->releaseSwapchain(window);
- rhiDeviceLost = true;
if (ownRhi)
QSGRhiSupport::instance()->destroyRhi(rhi, {});
rhi = nullptr;
}
+void QSGRenderThread::handleDeviceLoss()
+{
+ if (!rhi || !rhi->isDeviceLost())
+ return;
+
+ qWarning("Graphics device lost, cleaning up scenegraph and releasing RHI");
+ teardownGraphics();
+ rhiDeviceLost = true;
+}
+
void QSGRenderThread::syncAndRender()
{
const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
@@ -633,6 +627,7 @@ void QSGRenderThread::syncAndRender()
pendingUpdate = 0;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
// Begin the frame before syncing -> sync is where we may invoke
// updatePaintNode() on the items and they may want to do resource updates.
// Also relevant for applications that connect to the before/afterSynchronizing
@@ -654,10 +649,29 @@ void QSGRenderThread::syncAndRender()
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "just became exposed");
cd->hasActiveSwapchain = cd->swapchain->createOrResize();
- if (!cd->hasActiveSwapchain && rhi->isDeviceLost()) {
- handleDeviceLoss();
- QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
- return;
+ if (!cd->hasActiveSwapchain) {
+ bool bailOut = false;
+ if (rhi->isDeviceLost()) {
+ handleDeviceLoss();
+ bailOut = true;
+ } else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
+ qWarning("Failed to create swapchain."
+ " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
+ swRastFallbackDueToSwapchainFailure = true;
+ teardownGraphics();
+ bailOut = true;
+ }
+ if (bailOut) {
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ if (syncRequested) {
+ // Lock like sync() would do. Note that exposeRequested always includes syncRequested.
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed swapchain init, wake Gui");
+ mutex.lock();
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+ return;
+ }
}
cd->swapchainJustBecameRenderable = false;
@@ -739,7 +753,7 @@ void QSGRenderThread::syncAndRender()
// Zero size windows do not initialize a swapchain and
// rendercontext. So no sync or render can be done then.
const bool canRender = d->renderer && hasValidSwapChain;
-
+ double lastCompletedGpuTime = 0;
if (canRender) {
if (!syncRequested) // else this was already done in sync()
rhi->makeThreadLocalNativeContextCurrent();
@@ -761,6 +775,8 @@ void QSGRenderThread::syncAndRender()
qWarning("Failed to end frame");
if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ } else {
+ lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
}
d->fireFrameSwapped();
} else {
@@ -811,6 +827,12 @@ void QSGRenderThread::syncAndRender()
int((syncTime/1000000)),
int((renderTime - syncTime) / 1000000),
int((threadTimer.nsecsElapsed() - renderTime) / 1000000));
+ if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
+ qCDebug(QSG_LOG_TIME_RENDERLOOP, "[window %p][render thread %p] syncAndRender: last retrieved GPU frame time was %.4f ms",
+ window,
+ QThread::currentThread(),
+ lastCompletedGpuTime * 1000.0);
+ }
}
Q_TRACE(QSG_swap_exit);
@@ -856,7 +878,8 @@ void QSGRenderThread::ensureRhi()
if (rhiDoomed) // no repeated attempts if the initial attempt failed
return;
QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
- QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface);
+ const bool forcePreferSwRenderer = swRastFallbackDueToSwapchainFailure;
+ QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
rhi = rhiResult.rhi;
ownRhi = rhiResult.own;
if (rhi) {
@@ -915,7 +938,7 @@ void QSGRenderThread::ensureRhi()
}
cd->swapchain->setWindow(window);
cd->swapchain->setProxyData(scProxyData);
- QSGRhiSupport::instance()->applySwapChainFormat(cd->swapchain);
+ QSGRhiSupport::instance()->applySwapChainFormat(cd->swapchain, window);
qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
rhiSampleCount, alpha ? "yes" : "no");
cd->swapchain->setSampleCount(rhiSampleCount);
@@ -1054,6 +1077,9 @@ void QSGThreadedRenderLoop::animationStopped()
void QSGThreadedRenderLoop::startOrStopAnimationTimer()
{
+ if (!sg->isVSyncDependent(m_animation_driver))
+ return;
+
int exposedWindows = 0;
int unthrottledWindows = 0;
int badVSync = 0;
@@ -1123,7 +1149,7 @@ void QSGThreadedRenderLoop::hide(QQuickWindow *window)
qCDebug(QSG_LOG_RENDERLOOP) << "hide()" << window;
if (window->isExposed())
- handleObscurity(windowFor(m_windows, window));
+ handleObscurity(windowFor(window));
releaseResources(window);
}
@@ -1132,7 +1158,7 @@ void QSGThreadedRenderLoop::resize(QQuickWindow *window)
{
qCDebug(QSG_LOG_RENDERLOOP) << "reisze()" << window;
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (!w)
return;
@@ -1149,7 +1175,7 @@ void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
{
qCDebug(QSG_LOG_RENDERLOOP) << "begin windowDestroyed()" << window;
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (!w)
return;
@@ -1220,7 +1246,7 @@ void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window)
if (!skipThisExpose)
handleExposure(window);
} else {
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (w)
handleObscurity(w);
}
@@ -1234,7 +1260,7 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
{
qCDebug(QSG_LOG_RENDERLOOP) << "handleExposure()" << window;
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (!w) {
qCDebug(QSG_LOG_RENDERLOOP, "- adding window to list");
Window win;
@@ -1322,6 +1348,9 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
*/
void QSGThreadedRenderLoop::handleObscurity(Window *w)
{
+ if (!w)
+ return;
+
qCDebug(QSG_LOG_RENDERLOOP) << "handleObscurity()" << w->window;
if (w->thread->isRunning()) {
if (!QQuickWindowPrivate::get(w->window)->updatesEnabled) {
@@ -1329,7 +1358,7 @@ void QSGThreadedRenderLoop::handleObscurity(Window *w)
return;
}
w->thread->mutex.lock();
- w->thread->postEvent(new WMWindowEvent(w->window, WM_Obscure));
+ w->thread->postEvent(new WMWindowEvent(w->window, QEvent::Type(WM_Obscure)));
w->thread->waitCondition.wait(&w->thread->mutex);
w->thread->mutex.unlock();
}
@@ -1344,7 +1373,7 @@ bool QSGThreadedRenderLoop::eventFilter(QObject *watched, QEvent *event)
if (static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
if (window) {
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (w && w->thread->isRunning()) {
w->thread->mutex.lock();
w->thread->postEvent(new WMReleaseSwapchainEvent(window));
@@ -1369,14 +1398,14 @@ void QSGThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
qCDebug(QSG_LOG_RENDERLOOP, "- updatesEnabled is false, abort");
return;
}
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (w)
polishAndSync(w);
}
void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
{
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (w)
maybeUpdate(w);
}
@@ -1428,7 +1457,7 @@ void QSGThreadedRenderLoop::maybeUpdate(Window *w)
*/
void QSGThreadedRenderLoop::update(QQuickWindow *window)
{
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (!w)
return;
@@ -1448,7 +1477,7 @@ void QSGThreadedRenderLoop::update(QQuickWindow *window)
void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window)
{
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (w)
releaseResources(w, false);
}
@@ -1506,7 +1535,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
// Flush pending touch events.
QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
// The delivery of the event might have caused the window to stop rendering
- w = windowFor(m_windows, window);
+ w = windowFor(window);
if (!w || !w->thread || !w->thread->window) {
qCDebug(QSG_LOG_RENDERLOOP, "- removed after event flushing, abort");
return;
@@ -1520,7 +1549,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
const qint64 elapsedSinceLastMs = w->timeBetweenPolishAndSyncs.restart();
- if (w->actualWindowFormat.swapInterval() != 0) {
+ if (w->actualWindowFormat.swapInterval() != 0 && sg->isVSyncDependent(m_animation_driver)) {
w->psTimeAccumulator += elapsedSinceLastMs;
w->psTimeSampleCount += 1;
// cannot be too high because we'd then delay recognition of broken vsync at start
@@ -1627,10 +1656,13 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
QQuickProfiler::SceneGraphPolishAndSyncSync);
Q_TRACE(QSG_animations_entry);
- // Now is the time to advance the regular animations (as we are throttled to
- // vsync due to the wait above), but this is only relevant when there is one
- // single window. With multiple windows m_animation_timer is active, and
- // advance() happens instead in response to a good old timer event, not here.
+ // Now is the time to advance the regular animations (as we are throttled
+ // to vsync due to the wait above), but this is only relevant when there is
+ // one single window. With multiple windows m_animation_timer is active,
+ // and advance() happens instead in response to a good old timer event, not
+ // here. (the above applies only when the QSGAnimationDriver reports
+ // isVSyncDependent() == true, if not then we always use the driver and
+ // just advance here)
if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
qCDebug(QSG_LOG_RENDERLOOP, "- advancing animations");
m_animation_driver->advance();
@@ -1674,6 +1706,7 @@ bool QSGThreadedRenderLoop::event(QEvent *e)
switch ((int) e->type()) {
case QEvent::Timer: {
+ Q_ASSERT(sg->isVSyncDependent(m_animation_driver));
QTimerEvent *te = static_cast<QTimerEvent *>(e);
if (te->timerId() == m_animation_timer) {
qCDebug(QSG_LOG_RENDERLOOP, "- ticking non-render thread timer");
@@ -1681,6 +1714,7 @@ bool QSGThreadedRenderLoop::event(QEvent *e)
emit timeToIncubate();
return true;
}
+ break;
}
default:
@@ -1706,7 +1740,7 @@ QImage QSGThreadedRenderLoop::grab(QQuickWindow *window)
{
qCDebug(QSG_LOG_RENDERLOOP) << "grab()" << window;
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
Q_ASSERT(w);
if (!w->thread->isRunning())
@@ -1741,7 +1775,7 @@ QImage QSGThreadedRenderLoop::grab(QQuickWindow *window)
*/
void QSGThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
{
- Window *w = windowFor(m_windows, window);
+ Window *w = windowFor(window);
if (w && w->thread && w->thread->window)
w->thread->postEvent(new WMJobEvent(window, job));
else
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
index 4e0c471ab4..5d209f2f0f 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h
+++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
@@ -76,6 +76,8 @@ private:
friend class QSGRenderThread;
+
+ Window *windowFor(QQuickWindow *window);
void releaseResources(Window *window, bool inDestructor);
bool checkAndResetForceUpdate(QQuickWindow *window);
diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
index ed8da4cd30..c6e39c80db 100644
--- a/src/quick/scenegraph/shaders_ng/24bittextmask.frag
+++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -7,14 +10,18 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
-} ubuf;
+ vec4 color;
+};
void main()
{
vec4 glyph = texture(_qt_texture, sampleCoord);
- fragColor = vec4(glyph.rgb * ubuf.color.a, glyph.a);
+ fragColor = glyph * color.a;
}
diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
index 4198a4d339..9d67c8b302 100644
--- a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
+++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -7,13 +10,17 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
-} ubuf;
+ vec4 color;
+};
void main()
{
- fragColor = texture(_qt_texture, sampleCoord) * ubuf.color.a;
+ fragColor = texture(_qt_texture, sampleCoord) * color.a;
}
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
index a06743876d..530ebd69ef 100644
--- a/src/quick/scenegraph/shaders_ng/8bittextmask.frag
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -7,13 +10,17 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
-} ubuf;
+ vec4 color;
+};
void main()
{
- fragColor = ubuf.color * texture(_qt_texture, sampleCoord).r;
+ fragColor = color * texture(_qt_texture, sampleCoord).r;
}
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
index f725cbc5e7..d6ace867a3 100644
--- a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -7,13 +10,17 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
-} ubuf;
+ vec4 color;
+};
void main()
{
- fragColor = ubuf.color * texture(_qt_texture, sampleCoord).a; // take .a instead of .r
+ fragColor = color * texture(_qt_texture, sampleCoord).a; // take .a instead of .r
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag
index b1551d8ef4..1ece7b1e51 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -6,7 +9,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -15,12 +22,12 @@ layout(std140, binding = 0) uniform buf {
vec4 styleColor;
float outlineAlphaMax0;
float outlineAlphaMax1;
-} ubuf;
+};
void main()
{
float d = texture(_qt_texture, sampleCoord).r;
- float a = smoothstep(ubuf.alphaMin, ubuf.alphaMax, d);
- fragColor = step(1.0 - a, 1.0) * mix(ubuf.styleColor, ubuf.color, a)
- * smoothstep(ubuf.outlineAlphaMax0, ubuf.outlineAlphaMax1, d);
+ float a = smoothstep(alphaMin, alphaMax, d);
+ fragColor = step(1.0 - a, 1.0) * mix(styleColor, color, a)
+ * smoothstep(outlineAlphaMax0, outlineAlphaMax1, d);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert
index 8f0d618503..107ec9dfdc 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert
@@ -6,7 +6,11 @@ layout(location = 1) in vec2 tCoord;
layout(location = 0) out vec2 sampleCoord;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -15,12 +19,14 @@ layout(std140, binding = 0) uniform buf {
vec4 styleColor;
float outlineAlphaMax0;
float outlineAlphaMax1;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- sampleCoord = tCoord * ubuf.textureScale;
- gl_Position = ubuf.matrix * vCoord;
+ sampleCoord = tCoord * textureScale;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vCoord;
+#else
+ gl_Position = matrix * vCoord;
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag
index 7c6bd9a493..ac289221f9 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -6,7 +9,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -15,12 +22,12 @@ layout(std140, binding = 0) uniform buf {
vec4 styleColor;
float outlineAlphaMax0;
float outlineAlphaMax1;
-} ubuf;
+};
void main()
{
float d = texture(_qt_texture, sampleCoord).a;
- float a = smoothstep(ubuf.alphaMin, ubuf.alphaMax, d);
- fragColor = step(1.0 - a, 1.0) * mix(ubuf.styleColor, ubuf.color, a)
- * smoothstep(ubuf.outlineAlphaMax0, ubuf.outlineAlphaMax1, d);
+ float a = smoothstep(alphaMin, alphaMax, d);
+ fragColor = step(1.0 - a, 1.0) * mix(styleColor, color, a)
+ * smoothstep(outlineAlphaMax0, outlineAlphaMax1, d);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag
index 30ec465791..ddf8f28d1d 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -6,7 +9,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -15,7 +22,7 @@ layout(std140, binding = 0) uniform buf {
vec4 styleColor;
float outlineAlphaMax0;
float outlineAlphaMax1;
-} ubuf;
+};
void main()
{
@@ -24,8 +31,8 @@ void main()
// The outlineLimit is based on font size, but scales with the transform, so
// we can calculate it from the outline span.
- float outlineLimit = (ubuf.outlineAlphaMax1 - ubuf.outlineAlphaMax0) / 2.0 + ubuf.outlineAlphaMax0;
+ float outlineLimit = (outlineAlphaMax1 - outlineAlphaMax0) / 2.0 + outlineAlphaMax0;
float a = smoothstep(max(0.0, 0.5 - f), min(1.0, 0.5 + f), distance);
- fragColor = step(1.0 - a, 1.0) * mix(ubuf.styleColor, ubuf.color, a) * smoothstep(max(0.0, outlineLimit - f), min(outlineLimit + f, 0.5 - f), distance);
+ fragColor = step(1.0 - a, 1.0) * mix(styleColor, color, a) * smoothstep(max(0.0, outlineLimit - f), min(outlineLimit + f, 0.5 - f), distance);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag
index 511bffb09a..8aa5326be0 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -6,7 +9,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -15,7 +22,7 @@ layout(std140, binding = 0) uniform buf {
vec4 styleColor;
float outlineAlphaMax0;
float outlineAlphaMax1;
-} ubuf;
+};
void main()
{
@@ -24,8 +31,8 @@ void main()
// The outlineLimit is based on font size, but scales with the transform, so
// we can calculate it from the outline span.
- float outlineLimit = (ubuf.outlineAlphaMax1 - ubuf.outlineAlphaMax0) / 2.0 + ubuf.outlineAlphaMax0;
+ float outlineLimit = (outlineAlphaMax1 - outlineAlphaMax0) / 2.0 + outlineAlphaMax0;
float a = smoothstep(max(0.0, 0.5 - f), min(1.0, 0.5 + f), distance);
- fragColor = step(1.0 - a, 1.0) * mix(ubuf.styleColor, ubuf.color, a) * smoothstep(max(0.0, outlineLimit - f), min(outlineLimit + f, 0.5 - f), distance);
+ fragColor = step(1.0 - a, 1.0) * mix(styleColor, color, a) * smoothstep(max(0.0, outlineLimit - f), min(outlineLimit + f, 0.5 - f), distance);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag
index aa3390094b..6374f2d3ac 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -8,7 +11,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -16,12 +23,12 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
vec4 styleColor;
vec2 shift;
-} ubuf;
+};
void main()
{
- float a = smoothstep(ubuf.alphaMin, ubuf.alphaMax, texture(_qt_texture, sampleCoord).r);
- vec4 shifted = ubuf.styleColor * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ float a = smoothstep(alphaMin, alphaMax, texture(_qt_texture, sampleCoord).r);
+ vec4 shifted = styleColor * smoothstep(alphaMin, alphaMax,
texture(_qt_texture, shiftedSampleCoord).r);
- fragColor = mix(shifted, ubuf.color, a);
+ fragColor = mix(shifted, color, a);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert
index f3a7671435..5eeb6d8ecb 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert
@@ -7,7 +7,11 @@ layout(location = 0) out vec2 sampleCoord;
layout(location = 1) out vec2 shiftedSampleCoord;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -15,13 +19,15 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
vec4 styleColor;
vec2 shift;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- sampleCoord = tCoord * ubuf.textureScale;
- shiftedSampleCoord = (tCoord - ubuf.shift) * ubuf.textureScale;
- gl_Position = ubuf.matrix * vCoord;
+ sampleCoord = tCoord * textureScale;
+ shiftedSampleCoord = (tCoord - shift) * textureScale;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vCoord;
+#else
+ gl_Position = matrix * vCoord;
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag
index ab3a5f63ff..cd505e35fb 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -8,7 +11,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -16,12 +23,12 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
vec4 styleColor;
vec2 shift;
-} ubuf;
+};
void main()
{
- float a = smoothstep(ubuf.alphaMin, ubuf.alphaMax, texture(_qt_texture, sampleCoord).a);
- vec4 shifted = ubuf.styleColor * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ float a = smoothstep(alphaMin, alphaMax, texture(_qt_texture, sampleCoord).a);
+ vec4 shifted = styleColor * smoothstep(alphaMin, alphaMax,
texture(_qt_texture, shiftedSampleCoord).a);
- fragColor = mix(shifted, ubuf.color, a);
+ fragColor = mix(shifted, color, a);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag
index 8f528fea1e..e7112929a7 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -8,7 +11,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -16,7 +23,7 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
vec4 styleColor;
vec2 shift;
-} ubuf;
+};
void main()
{
@@ -28,6 +35,6 @@ void main()
float shiftedF = fwidth(shiftedDistance);
float shiftedA = smoothstep(0.5 - shiftedF, 0.5 + shiftedF, shiftedDistance);
- vec4 shifted = ubuf.styleColor * shiftedA;
- fragColor = mix(shifted, ubuf.color, a);
+ vec4 shifted = styleColor * shiftedA;
+ fragColor = mix(shifted, color, a);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag
index a71cc1d9b0..373e97ffff 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -8,7 +11,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -16,7 +23,7 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
vec4 styleColor;
vec2 shift;
-} ubuf;
+};
void main()
{
@@ -28,6 +35,6 @@ void main()
float shiftedF = fwidth(shiftedDistance);
float shiftedA = smoothstep(0.5 - shiftedF, 0.5 + shiftedF, shiftedDistance);
- vec4 shifted = ubuf.styleColor * shiftedA;
- fragColor = mix(shifted, ubuf.color, a);
+ vec4 shifted = styleColor * shiftedA;
+ fragColor = mix(shifted, color, a);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag
index d594207567..a94a1257b9 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldtext.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -6,15 +9,19 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
float alphaMax;
-} ubuf;
+};
void main()
{
- fragColor = ubuf.color * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
- texture(_qt_texture, sampleCoord).r);
+ fragColor = color * smoothstep(alphaMin, alphaMax,
+ texture(_qt_texture, sampleCoord).r);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.vert b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert
index d56ddddd24..eeb9f7bcce 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldtext.vert
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert
@@ -6,17 +6,23 @@ layout(location = 1) in vec2 tCoord;
layout(location = 0) out vec2 sampleCoord;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
float alphaMax;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- sampleCoord = tCoord * ubuf.textureScale;
- gl_Position = ubuf.matrix * vCoord;
+ sampleCoord = tCoord * textureScale;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vCoord;
+#else
+ gl_Position = matrix * vCoord;
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag
index bb807d86d8..dccca64a34 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -6,15 +9,19 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
float alphaMax;
-} ubuf;
+};
void main()
{
- fragColor = ubuf.color * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
- texture(_qt_texture, sampleCoord).a);
+ fragColor = color * smoothstep(alphaMin, alphaMax,
+ texture(_qt_texture, sampleCoord).a);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag
index 1aa1175b57..e4d2bced7a 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -6,16 +9,20 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
float alphaMax;
-} ubuf;
+};
void main()
{
float distance = texture(_qt_texture, sampleCoord).a;
float f = fwidth(distance);
- fragColor = ubuf.color * smoothstep(max(0.0, 0.5 - f), min(1.0, 0.5 + f), distance);
+ fragColor = color * smoothstep(max(0.0, 0.5 - f), min(1.0, 0.5 + f), distance);
}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag
index a698c19550..e895071811 100644
--- a/src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -6,16 +9,20 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
float alphaMax;
-} ubuf;
+};
void main()
{
float distance = texture(_qt_texture, sampleCoord).r;
float f = fwidth(distance);
- fragColor = ubuf.color * smoothstep(max(0.0, 0.5 - f), min(1.0, 0.5 + f), distance);
+ fragColor = color * smoothstep(max(0.0, 0.5 - f), min(1.0, 0.5 + f), distance);
}
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.frag b/src/quick/scenegraph/shaders_ng/flatcolor.frag
index 3a677b7c93..c5f9f3f2c4 100644
--- a/src/quick/scenegraph/shaders_ng/flatcolor.frag
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.frag
@@ -1,13 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec4 color;
-} ubuf;
+};
void main()
{
- fragColor = ubuf.color;
+ fragColor = color;
}
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.vert b/src/quick/scenegraph/shaders_ng/flatcolor.vert
index b5dfd32197..eec2a2d6ed 100644
--- a/src/quick/scenegraph/shaders_ng/flatcolor.vert
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.vert
@@ -3,13 +3,19 @@
layout(location = 0) in vec4 vertexCoord;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec4 color;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- gl_Position = ubuf.matrix * vertexCoord;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vertexCoord;
+#else
+ gl_Position = matrix * vertexCoord;
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag
index 723227a04d..fc3d04cb60 100644
--- a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -11,7 +14,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -19,7 +26,7 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
float fontScale;
vec4 vecDelta;
-} ubuf;
+};
void main()
{
@@ -31,10 +38,10 @@ void main()
n.w = textureProj(_qt_texture, sampleFarRight).r;
vec2 d = min(abs(n.yw - n.xz) * 2., 0.67);
- vec2 lo = mix(vec2(ubuf.alphaMin), vec2(0.5), d);
- vec2 hi = mix(vec2(ubuf.alphaMax), vec2(0.5), d);
+ vec2 lo = mix(vec2(alphaMin), vec2(0.5), d);
+ vec2 hi = mix(vec2(alphaMax), vec2(0.5), d);
n = smoothstep(lo.xxyy, hi.xxyy, n);
c = smoothstep(lo.x + lo.y, hi.x + hi.y, 2. * c);
- fragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * ubuf.color.w;
+ fragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * color.w;
}
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert
index 9c7281c31c..922074791b 100644
--- a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert
@@ -10,7 +10,11 @@ layout(location = 3) out vec3 sampleNearRight;
layout(location = 4) out vec3 sampleFarRight;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -18,24 +22,26 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
float fontScale;
vec4 vecDelta;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- sampleCoord = tCoord * ubuf.textureScale;
- gl_Position = ubuf.matrix * vCoord;
+ sampleCoord = tCoord * textureScale;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vCoord;
+#else
+ gl_Position = matrix * vCoord;
+#endif
// Calculate neighbor pixel position in item space.
- vec3 wDelta = gl_Position.w * ubuf.vecDelta.xyw;
+ vec3 wDelta = gl_Position.w * vecDelta.xyw;
vec3 farLeft = vCoord.xyw - 0.667 * wDelta;
vec3 nearLeft = vCoord.xyw - 0.333 * wDelta;
vec3 nearRight = vCoord.xyw + 0.333 * wDelta;
vec3 farRight = vCoord.xyw + 0.667 * wDelta;
// Calculate neighbor texture coordinate.
- vec2 scale = ubuf.textureScale / ubuf.fontScale;
+ vec2 scale = textureScale / fontScale;
vec2 base = sampleCoord - scale * vCoord.xy;
sampleFarLeft = vec3(base * farLeft.z + scale * farLeft.xy, farLeft.z);
sampleNearLeft = vec3(base * nearLeft.z + scale * nearLeft.xy, nearLeft.z);
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag
index a9d56f6380..3bd18f1dec 100644
--- a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -11,7 +14,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -19,7 +26,7 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
float fontScale;
vec4 vecDelta;
-} ubuf;
+};
void main()
{
@@ -31,10 +38,10 @@ void main()
n.w = textureProj(_qt_texture, sampleFarRight).a;
vec2 d = min(abs(n.yw - n.xz) * 2., 0.67);
- vec2 lo = mix(vec2(ubuf.alphaMin), vec2(0.5), d);
- vec2 hi = mix(vec2(ubuf.alphaMax), vec2(0.5), d);
+ vec2 lo = mix(vec2(alphaMin), vec2(0.5), d);
+ vec2 hi = mix(vec2(alphaMax), vec2(0.5), d);
n = smoothstep(lo.xxyy, hi.xxyy, n);
c = smoothstep(lo.x + lo.y, hi.x + hi.y, 2. * c);
- fragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * ubuf.color.w;
+ fragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * color.w;
}
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag
index 08b2ce5187..5571417bc5 100644
--- a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec3 sampleNearLeft;
@@ -8,7 +11,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -16,14 +23,14 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
float fontScale;
vec4 vecDelta;
-} ubuf;
+};
void main()
{
vec2 n;
n.x = textureProj(_qt_texture, sampleNearLeft).r;
n.y = textureProj(_qt_texture, sampleNearRight).r;
- n = smoothstep(ubuf.alphaMin, ubuf.alphaMax, n);
+ n = smoothstep(alphaMin, alphaMax, n);
float c = 0.5 * (n.x + n.y);
- fragColor = vec4(n.x, c, n.y, c) * ubuf.color.w;
+ fragColor = vec4(n.x, c, n.y, c) * color.w;
}
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert
index 187c384959..1b1d3b4013 100644
--- a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert
@@ -7,7 +7,11 @@ layout(location = 0) out vec3 sampleNearLeft;
layout(location = 1) out vec3 sampleNearRight;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -15,22 +19,24 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
float fontScale;
vec4 vecDelta;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- vec2 sampleCoord = tCoord * ubuf.textureScale;
- gl_Position = ubuf.matrix * vCoord;
+ vec2 sampleCoord = tCoord * textureScale;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vCoord;
+#else
+ gl_Position = matrix * vCoord;
+#endif
// Calculate neighbor pixel position in item space.
- vec3 wDelta = gl_Position.w * ubuf.vecDelta.xyw;
+ vec3 wDelta = gl_Position.w * vecDelta.xyw;
vec3 nearLeft = vCoord.xyw - 0.25 * wDelta;
vec3 nearRight = vCoord.xyw + 0.25 * wDelta;
// Calculate neighbor texture coordinate.
- vec2 scale = ubuf.textureScale / ubuf.fontScale;
+ vec2 scale = textureScale / fontScale;
vec2 base = sampleCoord - scale * vCoord.xy;
sampleNearLeft = vec3(base * nearLeft.z + scale * nearLeft.xy, nearLeft.z);
sampleNearRight = vec3(base * nearRight.z + scale * nearRight.xy, nearRight.z);
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag
index ef9407491b..3cd7176721 100644
--- a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec3 sampleNearLeft;
@@ -8,7 +11,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 textureScale;
vec4 color;
float alphaMin;
@@ -16,14 +23,14 @@ layout(std140, binding = 0) uniform buf {
// up to this point it must match distancefieldtext
float fontScale;
vec4 vecDelta;
-} ubuf;
+};
void main()
{
vec2 n;
n.x = textureProj(_qt_texture, sampleNearLeft).a;
n.y = textureProj(_qt_texture, sampleNearRight).a;
- n = smoothstep(ubuf.alphaMin, ubuf.alphaMax, n);
+ n = smoothstep(alphaMin, alphaMax, n);
float c = 0.5 * (n.x + n.y);
- fragColor = vec4(n.x, c, n.y, c) * ubuf.color.w;
+ fragColor = vec4(n.x, c, n.y, c) * color.w;
}
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.frag b/src/quick/scenegraph/shaders_ng/opaquetexture.frag
index 2cd2175f87..18dd7e0fcb 100644
--- a/src/quick/scenegraph/shaders_ng/opaquetexture.frag
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 qt_TexCoord;
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.vert b/src/quick/scenegraph/shaders_ng/opaquetexture.vert
index 5b52a59004..22df4a239a 100644
--- a/src/quick/scenegraph/shaders_ng/opaquetexture.vert
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.vert
@@ -6,13 +6,19 @@ layout(location = 1) in vec2 qt_VertexTexCoord;
layout(location = 0) out vec2 qt_TexCoord;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
mat4 qt_Matrix;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+#endif
+};
void main()
{
qt_TexCoord = qt_VertexTexCoord;
- gl_Position = ubuf.qt_Matrix * qt_VertexPosition;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = qt_Matrix[gl_ViewIndex] * qt_VertexPosition;
+#else
+ gl_Position = qt_Matrix * qt_VertexPosition;
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
index e2f82d3845..5c81f26272 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext.frag
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -12,14 +15,18 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
+ vec4 color;
// the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
-} ubuf;
+};
void main()
{
@@ -30,5 +37,5 @@ void main()
texture(_qt_texture, sCoordRight).r,
0.0, 1.0) - glyph,
0.0, 1.0);
- fragColor = outline * ubuf.styleColor + step(1.0 - glyph, 1.0) * glyph * ubuf.color;
+ fragColor = outline * styleColor + step(1.0 - glyph, 1.0) * glyph * color;
}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.vert b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
index 4068e42f28..a75b15556d 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext.vert
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
@@ -11,24 +11,30 @@ layout(location = 4) out vec2 sCoordRight;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
+ vec4 color;
// the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- sampleCoord = tCoord * ubuf.textureScale;
- sCoordUp = (tCoord - vec2(0.0, -1.0)) * ubuf.textureScale;
- sCoordDown = (tCoord - vec2(0.0, 1.0)) * ubuf.textureScale;
- sCoordLeft = (tCoord - vec2(-1.0, 0.0)) * ubuf.textureScale;
- sCoordRight = (tCoord - vec2(1.0, 0.0)) * ubuf.textureScale;
- vec4 xformed = ubuf.modelViewMatrix * vCoord;
- gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
+ sampleCoord = tCoord * textureScale;
+ sCoordUp = (tCoord - vec2(0.0, -1.0)) * textureScale;
+ sCoordDown = (tCoord - vec2(0.0, 1.0)) * textureScale;
+ sCoordLeft = (tCoord - vec2(-1.0, 0.0)) * textureScale;
+ sCoordRight = (tCoord - vec2(1.0, 0.0)) * textureScale;
+ vec4 xformed = modelViewMatrix * vCoord;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = projectionMatrix[gl_ViewIndex] * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
+#else
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
index 274d891a3c..c4db8b54f0 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -12,14 +15,18 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
+ vec4 color;
// the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
-} ubuf;
+};
void main()
{
@@ -30,5 +37,5 @@ void main()
texture(_qt_texture, sCoordRight).a,
0.0, 1.0) - glyph,
0.0, 1.0);
- fragColor = outline * ubuf.styleColor + step(1.0 - glyph, 1.0) * glyph * ubuf.color;
+ fragColor = outline * styleColor + step(1.0 - glyph, 1.0) * glyph * color;
}
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.frag b/src/quick/scenegraph/shaders_ng/shadereffect.frag
index bde493f6ce..3d1037ed05 100644
--- a/src/quick/scenegraph/shaders_ng/shadereffect.frag
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.frag
@@ -1,10 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform qt_buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
mat4 qt_Matrix;
+#endif
float qt_Opacity;
} qt_ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.vert b/src/quick/scenegraph/shaders_ng/shadereffect.vert
index ae65059f19..e030b5ee71 100644
--- a/src/quick/scenegraph/shaders_ng/shadereffect.vert
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.vert
@@ -6,14 +6,20 @@ layout(location = 1) in vec2 qt_MultiTexCoord0;
layout(location = 0) out vec2 qt_TexCoord0;
layout(std140, binding = 0) uniform qt_buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
mat4 qt_Matrix;
+#endif
float qt_Opacity;
} qt_ubuf; // must use a name that does not clash with custom code when no uniform blocks
-out gl_PerVertex { vec4 gl_Position; };
-
void main()
{
qt_TexCoord0 = qt_MultiTexCoord0;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = qt_ubuf.qt_Matrix[gl_ViewIndex] * qt_Vertex;
+#else
gl_Position = qt_ubuf.qt_Matrix * qt_Vertex;
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/shapecurve.frag b/src/quick/scenegraph/shaders_ng/shapecurve.frag
new file mode 100644
index 0000000000..bd0d02b73b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shapecurve.frag
@@ -0,0 +1,142 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#version 440
+
+layout(location = 0) in vec4 qt_TexCoord;
+layout(location = 1) in vec4 gradient;
+
+#if defined(LINEARGRADIENT)
+layout(location = 2) in float gradTabIndex;
+#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
+layout(location = 2) in vec2 coord;
+#endif
+
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
+ mat4 qt_Matrix;
+#endif
+ float matrixScale;
+ float opacity;
+ float debug;
+ float reserved3;
+
+#if defined(LINEARGRADIENT)
+ vec2 gradientStart;
+ vec2 gradientEnd;
+#elif defined(RADIALGRADIENT)
+ vec2 translationPoint;
+ vec2 focalToCenter;
+ float centerRadius;
+ float focalRadius;
+#elif defined(CONICALGRADIENT)
+ vec2 translationPoint;
+ float angle;
+#else
+ vec4 color;
+#endif
+} ubuf;
+
+#define INVERSE_2PI 0.1591549430918953358
+
+#if defined(LINEARGRADIENT) || defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
+layout(binding = 1) uniform sampler2D gradTabTexture;
+#endif
+
+vec4 baseColor()
+{
+#if defined(LINEARGRADIENT)
+ return texture(gradTabTexture, vec2(gradTabIndex, 0.5));
+#elif defined(RADIALGRADIENT)
+ float rd = ubuf.centerRadius - ubuf.focalRadius;
+ float b = 2.0 * (rd * ubuf.focalRadius + dot(coord, ubuf.focalToCenter));
+ float fmp2_m_radius2 = -ubuf.focalToCenter.x * ubuf.focalToCenter.x - ubuf.focalToCenter.y * ubuf.focalToCenter.y + rd * rd;
+ float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2);
+ float det = b * b - 4.0 * fmp2_m_radius2 * ((ubuf.focalRadius * ubuf.focalRadius) - dot(coord, coord));
+ vec4 result = vec4(0.0);
+ if (det >= 0.0) {
+ float detSqrt = sqrt(det);
+ float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2);
+ if (ubuf.focalRadius + w * (ubuf.centerRadius - ubuf.focalRadius) >= 0.0)
+ result = texture(gradTabTexture, vec2(w, 0.5));
+ }
+
+ return result;
+#elif defined(CONICALGRADIENT)
+ float t;
+ if (abs(coord.y) == abs(coord.x))
+ t = (atan(-coord.y + 0.002, coord.x) + ubuf.angle) * INVERSE_2PI;
+ else
+ t = (atan(-coord.y, coord.x) + ubuf.angle) * INVERSE_2PI;
+ return texture(gradTabTexture, vec2(t - floor(t), 0.5));
+#else
+ return vec4(ubuf.color.rgb, 1.0) * ubuf.color.a;
+#endif
+}
+
+void main()
+{
+ float f = qt_TexCoord.z * (qt_TexCoord.x * qt_TexCoord.x - qt_TexCoord.y) // curve
+ + (1.0 - abs(qt_TexCoord.z)) * (qt_TexCoord.x - qt_TexCoord.y); // line
+
+#if defined(USE_DERIVATIVES)
+ float _ddx = dFdx(f);
+ float _ddy = dFdy(f);
+ float df = length(vec2(_ddx, _ddy));
+#else
+ // We calculate the partial derivatives for f'(x, y) based on knowing the partial derivatives
+ // for the texture coordinates (u, v).
+ // So for curves:
+ // f(x,y) = u(x, y) * u(x, y) - v(x, y)
+ // f(x,y) = p(u(x,y)) - v(x, y) where p(u) = u^2
+ // So f'(x, y) = p'(u(x, y)) * u'(x, y) - v'(x, y)
+ // (by chain rule and sum rule)
+ // f'(x, y) = 2 * u(x, y) * u'(x, y) - v'(x, y)
+ // And so:
+ // df/dx = 2 * u(x, y) * du/dx - dv/dx
+ // df/dy = 2 * u(x, y) * du/dy - dv/dy
+ //
+ // and similarly for straight lines:
+ // f(x, y) = u(x, y) - v(x, y)
+ // f'(x, y) = dudx - dvdx
+
+ float dudx = gradient.x;
+ float dvdx = gradient.y;
+ float dudy = gradient.z;
+ float dvdy = gradient.w;
+
+ // Test with analytic derivatives
+// dudx = dFdx(qt_TexCoord.x);
+// dvdx = dFdx(qt_TexCoord.y);
+// dudy = dFdy(qt_TexCoord.x);
+// dvdy = dFdy(qt_TexCoord.y);
+
+ float dfx_curve = 2.0f * qt_TexCoord.x * dudx - dvdx;
+ float dfy_curve = 2.0f * qt_TexCoord.x * dudy - dvdy;
+
+ float dfx_line = dudx - dvdx;
+ float dfy_line = dudy - dvdy;
+
+ float dfx = qt_TexCoord.z * dfx_curve + (1.0 - abs(qt_TexCoord.z)) * dfx_line;
+ float dfy = qt_TexCoord.z * dfy_curve + (1.0 - abs(qt_TexCoord.z)) * dfy_line;
+ float df = length(vec2(dfx, dfy));
+#endif
+
+ float isLine = 1.0 - abs(qt_TexCoord.z);
+ float isCurve = 1.0 - isLine;
+ float debugR = isCurve * min(1.0, 1.0 - qt_TexCoord.z);
+ float debugG = isLine;
+ float debugB = isCurve * min(1.0, 1.0 - qt_TexCoord.z * -1.0) + debugG;
+ vec3 debugColor = vec3(debugR, debugG, debugB);
+
+ // Special case: mask out concave curve in "negative space".
+ int specialCaseMask = 1 - int(qt_TexCoord.w != 0.0) * (int(qt_TexCoord.x < 0.0) + int(qt_TexCoord.x > 1.0));
+ float fillCoverage = clamp(0.5 + f / df, 0.0, 1.0) * float(specialCaseMask);
+
+ fragColor = mix(baseColor() * fillCoverage, vec4(debugColor, 1.0), ubuf.debug) * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/shapecurve.vert b/src/quick/scenegraph/shaders_ng/shapecurve.vert
new file mode 100644
index 0000000000..688aa83139
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shapecurve.vert
@@ -0,0 +1,79 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+layout(location = 1) in vec4 vertexTexCoord;
+layout(location = 2) in vec4 vertexGradient;
+layout(location = 3) in vec2 normalVector;
+
+layout(location = 0) out vec4 qt_TexCoord;
+layout(location = 1) out vec4 gradient;
+
+#if defined(LINEARGRADIENT)
+layout(location = 2) out float gradTabIndex;
+#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
+layout(location = 2) out vec2 coord;
+#endif
+
+layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
+ mat4 qt_Matrix;
+#endif
+ float matrixScale;
+ float opacity;
+ float debug;
+ float reserved3;
+
+#if defined(LINEARGRADIENT)
+ vec2 gradientStart;
+ vec2 gradientEnd;
+#elif defined(RADIALGRADIENT)
+ vec2 translationPoint;
+ vec2 focalToCenter;
+ float centerRadius;
+ float focalRadius;
+#elif defined(CONICALGRADIENT)
+ vec2 translationPoint;
+ float angle;
+#else
+ vec4 color;
+#endif
+} ubuf;
+
+#define SQRT2 1.41421356237
+
+vec4 addOffset(vec4 texCoord, vec2 offset, vec4 duvdxy)
+{
+ float dudx = duvdxy.x;
+ float dvdx = duvdxy.y;
+ float dudy = duvdxy.z;
+ float dvdy = duvdxy.w;
+ float u = offset.x * dudx + offset.y * dudy;
+ float v = offset.x * dvdx + offset.y * dvdy;
+ // special case external triangles for concave curves
+ int specialCase = int(texCoord.z > 0) * (int(offset.x != 0) + int(offset.y != 0));
+ return vec4(texCoord.x + u, texCoord.y + v, texCoord.z, float(specialCase));
+}
+
+void main()
+{
+ vec2 offset = normalVector * SQRT2/ubuf.matrixScale;
+
+ qt_TexCoord = addOffset(vertexTexCoord, offset, vertexGradient);
+
+ gradient = vertexGradient / ubuf.matrixScale;
+
+#if defined(LINEARGRADIENT)
+ vec2 gradVec = ubuf.gradientEnd - ubuf.gradientStart;
+ gradTabIndex = dot(gradVec, vertexCoord.xy - ubuf.gradientStart.xy) / dot(gradVec, gradVec);
+#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
+ coord = vertexCoord.xy - ubuf.translationPoint;
+#endif
+
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = ubuf.qt_Matrix[gl_ViewIndex] * (vertexCoord + vec4(offset, 0, 0));
+#else
+ gl_Position = ubuf.qt_Matrix * (vertexCoord + vec4(offset, 0, 0));
+#endif
+}
diff --git a/src/quick/scenegraph/shaders_ng/shapestroke.frag b/src/quick/scenegraph/shaders_ng/shapestroke.frag
new file mode 100644
index 0000000000..cedfa0845a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shapestroke.frag
@@ -0,0 +1,134 @@
+#version 440
+
+layout(location = 0) in vec4 P;
+layout(location = 1) in vec2 A;
+layout(location = 2) in vec2 B;
+layout(location = 3) in vec2 C;
+layout(location = 4) in vec2 HG;
+layout(location = 5) in float offset;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
+ mat4 qt_Matrix;
+#endif
+
+ float matrixScale;
+ float opacity;
+ float reserved2;
+ float reserved3;
+
+ vec4 strokeColor;
+
+ float strokeWidth;
+ float debug;
+ float reserved5;
+ float reserved6;
+} ubuf;
+
+float cuberoot(float x)
+{
+ return sign(x) * pow(abs(x), 1 / 3.);
+}
+
+#define PI 3.1415926538
+
+vec3 solveDepressedCubic(float p, float q)
+{
+ float D = q * q / 4. + p * p * p / 27.;
+
+ float u1 = cuberoot(-q / 2. - sqrt(D));
+ float u2 = cuberoot(-q / 2. + sqrt(D));
+ vec3 rootsD1 = vec3(u1 - p / (3. * u1), u2 - p / (3. * u2), 0);
+
+ float v = 2.*sqrt(-p / 3.);
+ float t = acos(3. * q / p / v) / 3.;
+ float k = 2. * PI / 3.;
+ vec3 rootsD2 = vec3(v * cos(t), v * cos(t - k), v * cos(t - 2. * k));
+
+ return D > 0 ? rootsD1 : rootsD2;
+}
+
+mat2 qInverse(mat2 matrix) {
+ float a = matrix[0][0], b = matrix[0][1];
+ float c = matrix[1][0], d = matrix[1][1];
+
+ float determinant = a * d - b * c;
+ float invDet = 1.0 / determinant;
+
+ mat2 inverseMatrix;
+ inverseMatrix[0][0] = d * invDet;
+ inverseMatrix[0][1] = -b * invDet;
+ inverseMatrix[1][0] = -c * invDet;
+ inverseMatrix[1][1] = a * invDet;
+
+ return inverseMatrix;
+}
+
+void main()
+{
+ vec3 s = solveDepressedCubic(HG.x, HG.y) - vec3(offset, offset, offset);
+
+ vec2 Qmin = vec2(1e10, 1e10);
+ float dmin = 1e4;
+ for (int i = 0; i < 3; i++) {
+ float t = clamp(s[i], 0., 1.);
+ vec2 Q = A * t * t + B * t + C;
+ float d = length(Q - P.xy);
+ float foundNewMin = step(d, dmin);
+ dmin = min(d, dmin);
+ Qmin = foundNewMin * Q + (1. - foundNewMin) * Qmin;
+ }
+ vec2 n = (P.xy - Qmin) / dmin;
+ vec2 Q1 = (Qmin + ubuf.strokeWidth / 2. * n);
+ vec2 Q2 = (Qmin - ubuf.strokeWidth / 2. * n);
+
+ // Converting to screen coordinates:
+#if defined(USE_DERIVATIVES)
+ mat2 T = mat2(dFdx(P.x), dFdy(P.x), dFdx(P.y), dFdy(P.y));
+ mat2 Tinv = qInverse(T);
+ vec2 Q1_s = Tinv * Q1;
+ vec2 Q2_s = Tinv * Q2;
+ vec2 P_s = Tinv * P.xy;
+ vec2 n_s = Tinv * n;
+ n_s = n_s / length(n_s);
+#else
+ vec2 Q1_s = ubuf.matrixScale * Q1;
+ vec2 Q2_s = ubuf.matrixScale * Q2;
+ vec2 P_s = ubuf.matrixScale * P.xy;
+ vec2 n_s = n;
+#endif
+
+ // Geometric solution for anti aliasing using the known distances
+ // to the edges of the path in the screen coordinate system.
+ float dist1 = dot(P_s - Q1_s, n_s);
+ float dist2 = dot(P_s - Q2_s, n_s);
+
+ // Calculate the fill coverage if the line is crossing the square cell
+ // normally (vertically or horizontally).
+ // float fillCoverageLin = clamp(0.5-dist1, 0., 1.) - clamp(0.5-dist2, 0., 1.);
+
+ // Calculate the fill coverage if the line is crossing the square cell
+ // diagonally.
+ float fillCoverageDia = clamp(step(0., -dist1) + sign(dist1) * pow(max(0., sqrt(2.) / 2. - abs(dist1)), 2.), 0., 1.) -
+ clamp(step(0., -dist2) + sign(dist2) * pow(max(0., sqrt(2.) / 2. - abs(dist2)), 2.), 0., 1.);
+
+ // Merge the normal and the diagonal solution. The merge factor is periodic
+ // in 90 degrees and 0/1 at 0 and 45 degree. The simple equation was
+ // estimated after numerical experiments.
+ // float mergeFactor = 2 * abs(n_s.x) * abs(n_s.y);
+ // float fillCoverage = mergeFactor * fillCoverageDia + (1-mergeFactor) * fillCoverageLin;
+
+ // It seems to be sufficient to use the equation for the diagonal.
+ float fillCoverage = fillCoverageDia;
+
+ // The center line is sometimes not filled because of numerical issues. This fixes this.
+ float centerline = step(ubuf.strokeWidth * 0.01, dmin);
+ fillCoverage = fillCoverage * centerline + min(1., ubuf.strokeWidth * ubuf.matrixScale) * (1. - centerline);
+
+ fragColor = vec4(ubuf.strokeColor.rgb, 1.0) *ubuf.strokeColor.a * fillCoverage * ubuf.opacity
+ + ubuf.debug * vec4(0.0, 0.5, 1.0, 1.0) * (1.0 - fillCoverage) * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/shapestroke.vert b/src/quick/scenegraph/shaders_ng/shapestroke.vert
new file mode 100644
index 0000000000..e358e059eb
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shapestroke.vert
@@ -0,0 +1,82 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+layout(location = 1) in vec2 inA;
+layout(location = 2) in vec2 inB;
+layout(location = 3) in vec2 inC;
+layout(location = 4) in vec2 normalVector;
+
+layout(location = 0) out vec4 P;
+layout(location = 1) out vec2 A;
+layout(location = 2) out vec2 B;
+layout(location = 3) out vec2 C;
+layout(location = 4) out vec2 HG;
+layout(location = 5) out float offset;
+
+
+layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
+ mat4 qt_Matrix;
+#endif
+
+ float matrixScale;
+ float opacity;
+ float reserved2;
+ float reserved3;
+
+ vec4 strokeColor;
+
+ float strokeWidth;
+ float debug;
+ float reserved5;
+ float reserved6;
+} ubuf;
+
+#define SQRT2 1.41421356237
+
+float qdot(vec2 a, vec2 b)
+{
+ return a.x * b.x + a.y * b.y;
+}
+
+void main()
+{
+ P = vertexCoord + vec4(normalVector, 0.0, 0.0) * SQRT2/ubuf.matrixScale;
+
+ A = inA;
+ B = inB;
+ C = inC;
+
+ // Find the parameters H, G for the depressed cubic
+ // t^2+H*t+G=0
+ // that results from the equation
+ // Q'(s).(p-Q(s)) = 0
+ // The last parameter is the static offset between s and t:
+ // s = t - b/(3a)
+ // use it to get back the parameter t
+
+ // this is a constant for the curve
+ float a = -2. * qdot(A, A);
+ // this is a constant for the curve
+ float b = -3. * qdot(A, B);
+ //this is linear in p so it can be put into the shader with vertex data
+ float c = 2. * qdot(A, P.xy) - qdot(B, B) - 2. * qdot(A, C);
+ //this is linear in p so it can be put into the shader with vertex data
+ float d = qdot(B, P.xy) - qdot(B, C);
+ // convert to depressed cubic.
+ // both functions are linear in c and d and thus linear in p
+ float H = (3. * a * c - b * b) / (3. * a * a);
+ float G = (2. * b * b * b - 9. * a * b * c + 27. * a * a * d) / (27. * a * a * a);
+ HG = vec2(H, G);
+ offset = b/(3*a);
+
+
+
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = ubuf.qt_Matrix[gl_ViewIndex] * P;
+#else
+ gl_Position = ubuf.qt_Matrix * P;
+#endif
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.frag b/src/quick/scenegraph/shaders_ng/smoothcolor.frag
index ede283be0c..314a387922 100644
--- a/src/quick/scenegraph/shaders_ng/smoothcolor.frag
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec4 color;
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.vert b/src/quick/scenegraph/shaders_ng/smoothcolor.vert
index 03a3ff8975..6ec10a7e70 100644
--- a/src/quick/scenegraph/shaders_ng/smoothcolor.vert
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.vert
@@ -7,22 +7,32 @@ layout(location = 2) in vec4 vertexOffset;
layout(location = 0) out vec4 color;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 pixelSize;
float opacity;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- vec4 pos = ubuf.matrix * vertex;
+#if QSHADER_VIEW_COUNT >= 2
+ vec4 pos = matrix[gl_ViewIndex] * vertex;
+ vec4 m0 = matrix[gl_ViewIndex][0];
+ vec4 m1 = matrix[gl_ViewIndex][1];
+#else
+ vec4 pos = matrix * vertex;
+ vec4 m0 = matrix[0];
+ vec4 m1 = matrix[1];
+#endif
gl_Position = pos;
if (vertexOffset.x != 0.) {
- vec4 delta = ubuf.matrix[0] * vertexOffset.x;
+ vec4 delta = m0 * vertexOffset.x;
vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
- vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize);
dir -= ndir * delta.w * pos.w;
float numerator = dot(dir, ndir * pos.w * pos.w);
float scale = 0.0;
@@ -34,9 +44,9 @@ void main()
}
if (vertexOffset.y != 0.) {
- vec4 delta = ubuf.matrix[1] * vertexOffset.y;
+ vec4 delta = m1 * vertexOffset.y;
vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
- vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize);
dir -= ndir * delta.w * pos.w;
float numerator = dot(dir, ndir * pos.w * pos.w);
float scale = 0.0;
@@ -47,5 +57,5 @@ void main()
gl_Position += scale * delta;
}
- color = vertexColor * ubuf.opacity;
+ color = vertexColor * opacity;
}
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.frag b/src/quick/scenegraph/shaders_ng/smoothtexture.frag
index b06764ad95..a7ddc57535 100644
--- a/src/quick/scenegraph/shaders_ng/smoothtexture.frag
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 texCoord;
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.vert b/src/quick/scenegraph/shaders_ng/smoothtexture.vert
index 965c837852..4edde5472c 100644
--- a/src/quick/scenegraph/shaders_ng/smoothtexture.vert
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.vert
@@ -9,23 +9,33 @@ layout(location = 0) out vec2 texCoord;
layout(location = 1) out float vertexOpacity;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
mat4 qt_Matrix;
+#endif
float opacity;
vec2 pixelSize;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- vec4 pos = ubuf.qt_Matrix * vertex;
+#if QSHADER_VIEW_COUNT >= 2
+ vec4 pos = qt_Matrix[gl_ViewIndex] * vertex;
+ vec4 m0 = qt_Matrix[gl_ViewIndex][0];
+ vec4 m1 = qt_Matrix[gl_ViewIndex][1];
+#else
+ vec4 pos = qt_Matrix * vertex;
+ vec4 m0 = qt_Matrix[0];
+ vec4 m1 = qt_Matrix[1];
+#endif
gl_Position = pos;
texCoord = multiTexCoord;
if (vertexOffset.x != 0.) {
- vec4 delta = ubuf.qt_Matrix[0] * vertexOffset.x;
+ vec4 delta = m0 * vertexOffset.x;
vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
- vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize);
dir -= ndir * delta.w * pos.w;
float numerator = dot(dir, ndir * pos.w * pos.w);
float scale = 0.0;
@@ -38,9 +48,9 @@ void main()
}
if (vertexOffset.y != 0.) {
- vec4 delta = ubuf.qt_Matrix[1] * vertexOffset.y;
+ vec4 delta = m1 * vertexOffset.y;
vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
- vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize);
dir -= ndir * delta.w * pos.w;
float numerator = dot(dir, ndir * pos.w * pos.w);
float scale = 0.0;
@@ -57,5 +67,5 @@ void main()
if (onEdge && outerEdge)
vertexOpacity = 0.;
else
- vertexOpacity = ubuf.opacity;
+ vertexOpacity = opacity;
}
diff --git a/src/quick/scenegraph/shaders_ng/sprite.frag b/src/quick/scenegraph/shaders_ng/sprite.frag
index 338f5e957e..387be43bb1 100644
--- a/src/quick/scenegraph/shaders_ng/sprite.frag
+++ b/src/quick/scenegraph/shaders_ng/sprite.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec4 fTexS;
@@ -8,15 +11,19 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D tex;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec4 animPos;
vec3 animData;
float opacity;
-} ubuf;
+};
void main()
{
fragColor = mix(texture(tex, fTexS.xy),
texture(tex, fTexS.zw),
- progress) * ubuf.opacity;
+ progress) * opacity;
}
diff --git a/src/quick/scenegraph/shaders_ng/sprite.vert b/src/quick/scenegraph/shaders_ng/sprite.vert
index b76e2b206f..b693e31334 100644
--- a/src/quick/scenegraph/shaders_ng/sprite.vert
+++ b/src/quick/scenegraph/shaders_ng/sprite.vert
@@ -7,23 +7,29 @@ layout(location = 0) out vec4 fTexS;
layout(location = 1) out float progress;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec4 animPos; // x,y, x,y (two frames for interpolation)
vec3 animData; // w,h(premultiplied of anim), interpolation progress
float opacity;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- progress = ubuf.animData.z;
+ progress = animData.z;
// Calculate frame location in texture
- fTexS.xy = ubuf.animPos.xy + vTex.xy * ubuf.animData.xy;
+ fTexS.xy = animPos.xy + vTex.xy * animData.xy;
// Next frame is also passed, for interpolation
- fTexS.zw = ubuf.animPos.zw + vTex.xy * ubuf.animData.xy;
+ fTexS.zw = animPos.zw + vTex.xy * animData.xy;
- gl_Position = ubuf.matrix * vec4(vPos.x, vPos.y, 0, 1);
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vec4(vPos.x, vPos.y, 0, 1);
+#else
+ gl_Position = matrix * vec4(vPos.x, vPos.y, 0, 1);
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.frag b/src/quick/scenegraph/shaders_ng/stencilclip.frag
index 3f6222389d..ec4d3a05b1 100644
--- a/src/quick/scenegraph/shaders_ng/stencilclip.frag
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) out vec4 fragColor;
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag b/src/quick/scenegraph/shaders_ng/styledtext.frag
index 2e380dfeae..e55e10b05e 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext.frag
+++ b/src/quick/scenegraph/shaders_ng/styledtext.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -9,19 +12,23 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
+ vec4 color;
// the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
-} ubuf;
+};
void main()
{
float glyph = texture(_qt_texture, sampleCoord).r;
float style = clamp(texture(_qt_texture, shiftedSampleCoord).r - glyph,
0.0, 1.0);
- fragColor = style * ubuf.styleColor + glyph * ubuf.color;
+ fragColor = style * styleColor + glyph * color;
}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.vert b/src/quick/scenegraph/shaders_ng/styledtext.vert
index 271dae8d8a..5e0a8fa5c9 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext.vert
+++ b/src/quick/scenegraph/shaders_ng/styledtext.vert
@@ -8,21 +8,27 @@ layout(location = 1) out vec2 shiftedSampleCoord;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
+ vec4 color;
// the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- sampleCoord = tCoord * ubuf.textureScale;
- shiftedSampleCoord = (tCoord - ubuf.shift) * ubuf.textureScale;
- vec4 xformed = ubuf.modelViewMatrix * vCoord;
- gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
+ sampleCoord = tCoord * textureScale;
+ shiftedSampleCoord = (tCoord - shift) * textureScale;
+ vec4 xformed = modelViewMatrix * vCoord;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = projectionMatrix[gl_ViewIndex] * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
+#else
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
index 62e162c851..6a684c2abb 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -9,19 +12,23 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
+ vec4 color;
// the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
-} ubuf;
+};
void main()
{
float glyph = texture(_qt_texture, sampleCoord).a; // take .a instead of .r
float style = clamp(texture(_qt_texture, shiftedSampleCoord).a - glyph,
0.0, 1.0);
- fragColor = style * ubuf.styleColor + glyph * ubuf.color;
+ fragColor = style * styleColor + glyph * color;
}
diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag b/src/quick/scenegraph/shaders_ng/textmask.frag
index ed8da4cd30..0f9d6b4567 100644
--- a/src/quick/scenegraph/shaders_ng/textmask.frag
+++ b/src/quick/scenegraph/shaders_ng/textmask.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 sampleCoord;
@@ -7,14 +10,18 @@ layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
-} ubuf;
+ vec4 color;
+};
void main()
{
vec4 glyph = texture(_qt_texture, sampleCoord);
- fragColor = vec4(glyph.rgb * ubuf.color.a, glyph.a);
+ fragColor = vec4(glyph.rgb * color.a, glyph.a);
}
diff --git a/src/quick/scenegraph/shaders_ng/textmask.vert b/src/quick/scenegraph/shaders_ng/textmask.vert
index e0b3c01bce..41fffa282f 100644
--- a/src/quick/scenegraph/shaders_ng/textmask.vert
+++ b/src/quick/scenegraph/shaders_ng/textmask.vert
@@ -7,17 +7,23 @@ layout(location = 0) out vec2 sampleCoord;
layout(std140, binding = 0) uniform buf {
mat4 modelViewMatrix;
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 projectionMatrix[QSHADER_VIEW_COUNT];
+#else
mat4 projectionMatrix;
- vec4 color;
+#endif
vec2 textureScale;
float dpr;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+ vec4 color;
+};
void main()
{
- sampleCoord = tCoord * ubuf.textureScale;
- vec4 xformed = ubuf.modelViewMatrix * vCoord;
- gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
+ sampleCoord = tCoord * textureScale;
+ vec4 xformed = modelViewMatrix * vCoord;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = projectionMatrix[gl_ViewIndex] * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
+#else
+ gl_Position = projectionMatrix * vec4(floor(xformed.xyz * dpr + 0.5) / dpr, xformed.w);
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/texture.frag b/src/quick/scenegraph/shaders_ng/texture.frag
index bd22f817e0..a591e72ccf 100644
--- a/src/quick/scenegraph/shaders_ng/texture.frag
+++ b/src/quick/scenegraph/shaders_ng/texture.frag
@@ -1,16 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 qt_TexCoord;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
mat4 qt_Matrix;
+#endif
float opacity;
-} ubuf;
+};
layout(binding = 1) uniform sampler2D qt_Texture;
void main()
{
- fragColor = texture(qt_Texture, qt_TexCoord) * ubuf.opacity;
+ fragColor = texture(qt_Texture, qt_TexCoord) * opacity;
}
diff --git a/src/quick/scenegraph/shaders_ng/texture.vert b/src/quick/scenegraph/shaders_ng/texture.vert
index 537852d2bd..ae8a27f4a6 100644
--- a/src/quick/scenegraph/shaders_ng/texture.vert
+++ b/src/quick/scenegraph/shaders_ng/texture.vert
@@ -6,14 +6,20 @@ layout(location = 1) in vec2 qt_VertexTexCoord;
layout(location = 0) out vec2 qt_TexCoord;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
mat4 qt_Matrix;
+#endif
float opacity;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
qt_TexCoord = qt_VertexTexCoord;
- gl_Position = ubuf.qt_Matrix * qt_VertexPosition;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = qt_Matrix[gl_ViewIndex] * qt_VertexPosition;
+#else
+ gl_Position = qt_Matrix * qt_VertexPosition;
+#endif
}
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.frag b/src/quick/scenegraph/shaders_ng/vertexcolor.frag
index ede283be0c..314a387922 100644
--- a/src/quick/scenegraph/shaders_ng/vertexcolor.frag
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec4 color;
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.vert b/src/quick/scenegraph/shaders_ng/vertexcolor.vert
index bfb9a95073..792f663b86 100644
--- a/src/quick/scenegraph/shaders_ng/vertexcolor.vert
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.vert
@@ -6,14 +6,20 @@ layout(location = 1) in vec4 vertexColor;
layout(location = 0) out vec4 color;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
float opacity;
-} ubuf;
-
-out gl_PerVertex { vec4 gl_Position; };
+};
void main()
{
- gl_Position = ubuf.matrix * vertexCoord;
- color = vertexColor * ubuf.opacity;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = matrix[gl_ViewIndex] * vertexCoord;
+#else
+ gl_Position = matrix * vertexCoord;
+#endif
+ color = vertexColor * opacity;
}
diff --git a/src/quick/scenegraph/shaders_ng/visualization.frag b/src/quick/scenegraph/shaders_ng/visualization.frag
index 29f718fe5d..378afc2088 100644
--- a/src/quick/scenegraph/shaders_ng/visualization.frag
+++ b/src/quick/scenegraph/shaders_ng/visualization.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 pos;
diff --git a/src/quick/scenegraph/util/qquadpath.cpp b/src/quick/scenegraph/util/qquadpath.cpp
new file mode 100644
index 0000000000..9b5cbcb0ae
--- /dev/null
+++ b/src/quick/scenegraph/util/qquadpath.cpp
@@ -0,0 +1,950 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquadpath_p.h"
+#include <QtGui/private/qbezier_p.h>
+#include <QtMath>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QVarLengthArray>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcSGCurveProcessor);
+
+static qreal qt_scoreQuadratic(const QBezier &b, QPointF qcp)
+{
+ static bool init = false;
+ const int numSteps = 21;
+ Q_STATIC_ASSERT(numSteps % 2 == 1); // numTries must be odd
+ static qreal t2s[numSteps];
+ static qreal tmts[numSteps];
+ if (!init) {
+ // Precompute bezier factors
+ qreal t = 0.20;
+ const qreal step = (1 - (2 * t)) / (numSteps - 1);
+ for (int i = 0; i < numSteps; i++) {
+ t2s[i] = t * t;
+ tmts[i] = 2 * t * (1 - t);
+ t += step;
+ }
+ init = true;
+ }
+
+ const QPointF midPoint = b.midPoint();
+ auto distForIndex = [&](int i) -> qreal {
+ QPointF qp = (t2s[numSteps - 1 - i] * b.pt1()) + (tmts[i] * qcp) + (t2s[i] * b.pt4());
+ QPointF d = midPoint - qp;
+ return QPointF::dotProduct(d, d);
+ };
+
+ const int halfSteps = (numSteps - 1) / 2;
+ bool foundIt = false;
+ const qreal centerDist = distForIndex(halfSteps);
+ qreal minDist = centerDist;
+ // Search for the minimum in right half
+ for (int i = 0; i < halfSteps; i++) {
+ qreal tDist = distForIndex(halfSteps + 1 + i);
+ if (tDist < minDist) {
+ minDist = tDist;
+ } else {
+ foundIt = (i > 0);
+ break;
+ }
+ }
+ if (!foundIt) {
+ // Search in left half
+ minDist = centerDist;
+ for (int i = 0; i < halfSteps; i++) {
+ qreal tDist = distForIndex(halfSteps - 1 - i);
+ if (tDist < minDist) {
+ minDist = tDist;
+ } else {
+ foundIt = (i > 0);
+ break;
+ }
+ }
+ }
+ return foundIt ? minDist : centerDist;
+}
+
+static QPointF qt_quadraticForCubic(const QBezier &b)
+{
+ const QLineF st = b.startTangent();
+ const QLineF et = b.endTangent();
+ const QPointF midPoint = b.midPoint();
+ bool valid = true;
+ QPointF quadControlPoint;
+ if (st.intersects(et, &quadControlPoint) == QLineF::NoIntersection) {
+ valid = false;
+ } else {
+ // Check if intersection is on wrong side
+ const QPointF bl = b.pt4() - b.pt1();
+ const QPointF ml = midPoint - b.pt1();
+ const QPointF ql = quadControlPoint - b.pt1();
+ qreal cx1 = (ml.x() * bl.y()) - (ml.y() * bl.x());
+ qreal cx2 = (ql.x() * bl.y()) - (ql.y() * bl.x());
+ valid = (std::signbit(cx1) == std::signbit(cx2));
+ }
+ return valid ? quadControlPoint : midPoint;
+}
+
+static int qt_getInflectionPoints(const QBezier &orig, qreal *tpoints)
+{
+ auto isValidRoot = [](qreal r) {
+ return qIsFinite(r) && (r > 0) && (!qFuzzyIsNull(float(r))) && (r < 1)
+ && (!qFuzzyIsNull(float(r - 1)));
+ };
+
+ // normalize so pt1.x,pt1.y,pt4.y == 0
+ QTransform xf;
+ const QLineF l(orig.pt1(), orig.pt4());
+ xf.rotate(l.angle());
+ xf.translate(-orig.pt1().x(), -orig.pt1().y());
+ const QBezier n = orig.mapBy(xf);
+
+ const qreal x2 = n.pt2().x();
+ const qreal x3 = n.pt3().x();
+ const qreal x4 = n.pt4().x();
+ const qreal y2 = n.pt2().y();
+ const qreal y3 = n.pt3().y();
+
+ const qreal p = x3 * y2;
+ const qreal q = x4 * y2;
+ const qreal r = x2 * y3;
+ const qreal s = x4 * y3;
+
+ const qreal a = 18 * ((-3 * p) + (2 * q) + (3 * r) - s);
+ if (qFuzzyIsNull(float(a))) {
+ if (std::signbit(y2) != std::signbit(y3) && qFuzzyCompare(float(x4 - x3), float(x2))) {
+ tpoints[0] = 0.5; // approx
+ return 1;
+ } else if (!a) {
+ return 0;
+ }
+ }
+ const qreal b = 18 * (((3 * p) - q) - (3 * r));
+ const qreal c = 18 * (r - p);
+ const qreal rad = (b * b) - (4 * a * c);
+ if (rad < 0)
+ return 0;
+ const qreal sqr = qSqrt(rad);
+ const qreal root1 = (-b + sqr) / (2 * a);
+ const qreal root2 = (-b - sqr) / (2 * a);
+
+ int res = 0;
+ if (isValidRoot(root1))
+ tpoints[res++] = root1;
+ if (root2 != root1 && isValidRoot(root2))
+ tpoints[res++] = root2;
+
+ if (res == 2 && tpoints[0] > tpoints[1])
+ qSwap(tpoints[0], tpoints[1]);
+
+ return res;
+}
+
+static void qt_addToQuadratics(const QBezier &b, QPolygonF *p, int maxSplits, qreal maxDiff)
+{
+ QPointF qcp = qt_quadraticForCubic(b);
+ if (maxSplits <= 0 || qt_scoreQuadratic(b, qcp) < maxDiff) {
+ p->append(qcp);
+ p->append(b.pt4());
+ } else {
+ QBezier rhs = b;
+ QBezier lhs;
+ rhs.parameterSplitLeft(0.5, &lhs);
+ qt_addToQuadratics(lhs, p, maxSplits - 1, maxDiff);
+ qt_addToQuadratics(rhs, p, maxSplits - 1, maxDiff);
+ }
+}
+
+static void qt_toQuadratics(const QBezier &b, QPolygonF *out, qreal errorLimit = 0.01)
+{
+ out->resize(0);
+ out->append(b.pt1());
+
+ {
+ // Shortcut if the cubic is really a quadratic
+ const qreal f = 3.0 / 2.0;
+ const QPointF c1 = b.pt1() + f * (b.pt2() - b.pt1());
+ const QPointF c2 = b.pt4() + f * (b.pt3() - b.pt4());
+ if (c1 == c2) {
+ out->append(c1);
+ out->append(b.pt4());
+ return;
+ }
+ }
+
+ const QRectF cpr = b.bounds();
+ const QPointF dim = cpr.bottomRight() - cpr.topLeft();
+ qreal maxDiff = QPointF::dotProduct(dim, dim) * errorLimit * errorLimit; // maxdistance^2
+
+ qreal infPoints[2];
+ int numInfPoints = qt_getInflectionPoints(b, infPoints);
+ const int maxSubSplits = numInfPoints > 0 ? 2 : 3;
+ qreal t0 = 0;
+ // number of main segments == #inflectionpoints + 1
+ for (int i = 0; i < numInfPoints + 1; i++) {
+ qreal t1 = (i < numInfPoints) ? infPoints[i] : 1;
+ QBezier segment = b.bezierOnInterval(t0, t1);
+ qt_addToQuadratics(segment, out, maxSubSplits, maxDiff);
+ t0 = t1;
+ }
+}
+
+QVector2D QQuadPath::Element::pointAtFraction(float t) const
+{
+ if (isLine()) {
+ return sp + t * (ep - sp);
+ } else {
+ const float r = 1 - t;
+ return (r * r * sp) + (2 * t * r * cp) + (t * t * ep);
+ }
+}
+
+QQuadPath::Element QQuadPath::Element::segmentFromTo(float t0, float t1) const
+{
+ if (t0 <= 0 && t1 >= 1)
+ return *this;
+
+ Element part;
+ part.sp = pointAtFraction(t0);
+ part.ep = pointAtFraction(t1);
+
+ if (isLine()) {
+ part.cp = 0.5f * (part.sp + part.ep);
+ part.m_isLine = true;
+ } else {
+ // Split curve right at t0, yields { t0, rcp, endPoint } quad segment
+ const QVector2D rcp = (1 - t0) * controlPoint() + t0 * endPoint();
+ // Split that left at t1, yields { t0, lcp, t1 } quad segment
+ float segmentT = (t1 - t0) / (1 - t0);
+ part.cp = (1 - segmentT) * part.sp + segmentT * rcp;
+ }
+ return part;
+}
+
+QQuadPath::Element QQuadPath::Element::reversed() const {
+ Element swappedElement;
+ swappedElement.ep = sp;
+ swappedElement.cp = cp;
+ swappedElement.sp = ep;
+ swappedElement.m_isLine = m_isLine;
+ return swappedElement;
+}
+
+float QQuadPath::Element::extent() const
+{
+ // TBD: cache this value if we start using it a lot
+ QVector2D min(qMin(sp.x(), ep.x()), qMin(sp.y(), ep.y()));
+ QVector2D max(qMax(sp.x(), ep.x()), qMax(sp.y(), ep.y()));
+ if (!isLine()) {
+ min = QVector2D(qMin(min.x(), cp.x()), qMin(min.y(), cp.y()));
+ max = QVector2D(qMax(max.x(), cp.x()), qMax(max.y(), cp.y()));
+ }
+ return (max - min).length();
+}
+
+// Returns the number of intersections between element and a horizontal line at y.
+// The t values of max 2 intersection(s) are stored in the fractions array
+int QQuadPath::Element::intersectionsAtY(float y, float *fractions, bool swapXY) const
+{
+ Q_ASSERT(!isLine());
+
+ auto getY = [=](QVector2D p) -> float { return swapXY ? -p.x() : p.y(); };
+
+ const float y0 = getY(startPoint()) - y;
+ const float y1 = getY(controlPoint()) - y;
+ const float y2 = getY(endPoint()) - y;
+
+ int numRoots = 0;
+ const float a = y0 - (2 * y1) + y2;
+ if (a) {
+ const float b = (y1 * y1) - (y0 * y2);
+ if (b >= 0) {
+ const float sqr = qSqrt(b);
+ const float root1 = -(-y0 + y1 + sqr) / a;
+ if (qIsFinite(root1) && root1 >= 0 && root1 <= 1)
+ fractions[numRoots++] = root1;
+ const float root2 = (y0 - y1 + sqr) / a;
+ if (qIsFinite(root2) && root2 != root1 && root2 >= 0 && root2 <= 1)
+ fractions[numRoots++] = root2;
+ }
+ } else if (y1 != y2) {
+ const float root1 = (y2 - (2 * y1)) / (2 * (y2 - y1));
+ if (qIsFinite(root1) && root1 >= 0 && root1 <= 1)
+ fractions[numRoots++] = root1;
+ }
+
+ return numRoots;
+}
+
+static float crossProduct(const QVector2D &sp, const QVector2D &p, const QVector2D &ep)
+{
+ QVector2D v1 = ep - sp;
+ QVector2D v2 = p - sp;
+ return (v2.x() * v1.y()) - (v2.y() * v1.x());
+}
+
+bool QQuadPath::isPointOnLeft(const QVector2D &p, const QVector2D &sp, const QVector2D &ep)
+{
+ // Use cross product to compare directions of base vector and vector from start to p
+ return crossProduct(sp, p, ep) >= 0.0f;
+}
+
+bool QQuadPath::isPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep)
+{
+ return qFuzzyIsNull(crossProduct(sp, p, ep));
+}
+
+// Assumes sp != ep
+bool QQuadPath::isPointNearLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep)
+{
+ // epsilon is max length of p-to-baseline relative to length of baseline. So 0.01 means that
+ // the distance from p to the baseline must be less than 1% of the length of the baseline.
+ constexpr float epsilon = 0.01f;
+ QVector2D bv = ep - sp;
+ float bl2 = QVector2D::dotProduct(bv, bv);
+ float t = QVector2D::dotProduct(p - sp, bv) / bl2;
+ QVector2D pv = p - (sp + t * bv);
+ return (QVector2D::dotProduct(pv, pv) / bl2) < (epsilon * epsilon);
+}
+
+QVector2D QQuadPath::closestPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep)
+{
+ QVector2D line = ep - sp;
+ float t = QVector2D::dotProduct(p - sp, line) / QVector2D::dotProduct(line, line);
+ return sp + qBound(0.0f, t, 1.0f) * line;
+}
+
+// NOTE: it is assumed that subpaths are closed
+bool QQuadPath::contains(const QVector2D &point) const
+{
+ return contains(point, 0, elementCount() - 1);
+}
+
+bool QQuadPath::contains(const QVector2D &point, int fromIndex, int toIndex) const
+{
+ // if (!controlPointRect().contains(pt) : good opt when we add cpr caching
+ // return false;
+
+ int winding_number = 0;
+ for (int ei = fromIndex; ei <= toIndex; ei++) {
+ const Element &e = m_elements.at(ei);
+ int dir = 1;
+ float y1 = e.startPoint().y();
+ float y2 = e.endPoint().y();
+ if (y2 < y1) {
+ qSwap(y1, y2);
+ dir = -1;
+ }
+ if (e.m_isLine) {
+ if (point.y() < y1 || point.y() >= y2 || y1 == y2)
+ continue;
+ const float t = (point.y() - e.startPoint().y()) / (e.endPoint().y() - e.startPoint().y());
+ const float x = e.startPoint().x() + t * (e.endPoint().x() - e.startPoint().x());
+ if (x <= point.x())
+ winding_number += dir;
+ } else {
+ y1 = qMin(y1, e.controlPoint().y());
+ y2 = qMax(y2, e.controlPoint().y());
+ if (point.y() < y1 || point.y() >= y2)
+ continue;
+ float ts[2];
+ const int numRoots = e.intersectionsAtY(point.y(), ts);
+ // Count if there is exactly one intersection to the left
+ bool oneHit = false;
+ float tForHit = -1;
+ for (int i = 0; i < numRoots; i++) {
+ if (e.pointAtFraction(ts[i]).x() <= point.x()) {
+ oneHit = !oneHit;
+ tForHit = ts[i];
+ }
+ }
+ if (oneHit) {
+ dir = e.tangentAtFraction(tForHit).y() < 0 ? -1 : 1;
+ winding_number += dir;
+ }
+ }
+ };
+
+ return (fillRule() == Qt::WindingFill ? (winding_number != 0) : ((winding_number % 2) != 0));
+}
+
+// similar as contains. But we treat the element with the index elementIdx in a special way
+// that should be numerically more stable. The result is a contains for a point on the left
+// and for the right side of the element.
+QQuadPath::Element::FillSide QQuadPath::fillSideOf(int elementIdx, float elementT) const
+{
+ constexpr float toleranceT = 1e-3f;
+ const QVector2D point = m_elements.at(elementIdx).pointAtFraction(elementT);
+ const QVector2D tangent = m_elements.at(elementIdx).tangentAtFraction(elementT);
+
+ const bool swapXY = qAbs(tangent.x()) > qAbs(tangent.y());
+ auto getX = [=](QVector2D p) -> float { return swapXY ? p.y() : p.x(); };
+ auto getY = [=](QVector2D p) -> float { return swapXY ? -p.x() : p.y(); };
+
+ int winding_number = 0;
+ for (int i = 0; i < elementCount(); i++) {
+ const Element &e = m_elements.at(i);
+ int dir = 1;
+ float y1 = getY(e.startPoint());
+ float y2 = getY(e.endPoint());
+ if (y2 < y1) {
+ qSwap(y1, y2);
+ dir = -1;
+ }
+ if (e.m_isLine) {
+ if (getY(point) < y1 || getY(point) >= y2 || y1 == y2)
+ continue;
+ const float t = (getY(point) - getY(e.startPoint())) / (getY(e.endPoint()) - getY(e.startPoint()));
+ const float x = getX(e.startPoint()) + t * (getX(e.endPoint()) - getX(e.startPoint()));
+ if (x <= getX(point) && (i != elementIdx || qAbs(t - elementT) > toleranceT))
+ winding_number += dir;
+ } else {
+ y1 = qMin(y1, getY(e.controlPoint()));
+ y2 = qMax(y2, getY(e.controlPoint()));
+ if (getY(point) < y1 || getY(point) >= y2)
+ continue;
+ float ts[2];
+ const int numRoots = e.intersectionsAtY(getY(point), ts, swapXY);
+ // Count if there is exactly one intersection to the left
+ bool oneHit = false;
+ float tForHit = -1;
+ for (int j = 0; j < numRoots; j++) {
+ const float x = getX(e.pointAtFraction(ts[j]));
+ if (x <= getX(point) && (i != elementIdx || qAbs(ts[j] - elementT) > toleranceT)) {
+ oneHit = !oneHit;
+ tForHit = ts[j];
+ }
+ }
+ if (oneHit) {
+ dir = getY(e.tangentAtFraction(tForHit)) < 0 ? -1 : 1;
+ winding_number += dir;
+ }
+ }
+ };
+
+ int left_winding_number = winding_number;
+ int right_winding_number = winding_number;
+
+ int dir = getY(tangent) < 0 ? -1 : 1;
+
+ if (dir > 0)
+ left_winding_number += dir;
+ else
+ right_winding_number += dir;
+
+ bool leftInside = (fillRule() == Qt::WindingFill ? (left_winding_number != 0) : ((left_winding_number % 2) != 0));
+ bool rightInside = (fillRule() == Qt::WindingFill ? (right_winding_number != 0) : ((right_winding_number % 2) != 0));
+
+ if (leftInside && rightInside)
+ return QQuadPath::Element::FillSideBoth;
+ else if (leftInside)
+ return QQuadPath::Element::FillSideLeft;
+ else if (rightInside)
+ return QQuadPath::Element::FillSideRight;
+ else
+ return QQuadPath::Element::FillSideUndetermined; //should not happen except for numerical error.
+}
+
+void QQuadPath::addElement(const QVector2D &control, const QVector2D &endPoint, bool isLine)
+{
+ if (qFuzzyCompare(m_currentPoint, endPoint))
+ return; // 0 length element, skip
+
+ isLine = isLine || isPointNearLine(control, m_currentPoint, endPoint); // Turn flat quad into line
+
+ m_elements.resize(m_elements.size() + 1);
+ Element &elem = m_elements.last();
+ elem.sp = m_currentPoint;
+ elem.cp = isLine ? (0.5f * (m_currentPoint + endPoint)) : control;
+ elem.ep = endPoint;
+ elem.m_isLine = isLine;
+ elem.m_isSubpathStart = m_subPathToStart;
+ m_subPathToStart = false;
+ m_currentPoint = endPoint;
+}
+
+void QQuadPath::addElement(const Element &e)
+{
+ m_subPathToStart = false;
+ m_currentPoint = e.endPoint();
+ m_elements.append(e);
+}
+
+#if !defined(QQUADPATH_CONVEX_CHECK_ERROR_MARGIN)
+# define QQUICKSHAPECURVERENDERER_CONVEX_CHECK_ERROR_MARGIN (1.0f / 32.0f)
+#endif
+
+QQuadPath::Element::CurvatureFlags QQuadPath::coordinateOrderOfElement(const QQuadPath::Element &element) const
+{
+ QVector2D baseLine = element.endPoint() - element.startPoint();
+ QVector2D midPoint = element.midPoint();
+ // At the midpoint, the tangent of a quad is parallel to the baseline
+ QVector2D normal = QVector2D(-baseLine.y(), baseLine.x()).normalized();
+ float delta = qMin(element.extent() / 100, QQUICKSHAPECURVERENDERER_CONVEX_CHECK_ERROR_MARGIN);
+ QVector2D justRightOfMid = midPoint + (normal * delta);
+ bool pathContainsPoint = contains(justRightOfMid);
+ return pathContainsPoint ? Element::FillOnRight : Element::CurvatureFlags(0);
+}
+
+QQuadPath QQuadPath::fromPainterPath(const QPainterPath &path, PathHints hints)
+{
+ QQuadPath res;
+ res.reserve(path.elementCount());
+ res.setFillRule(path.fillRule());
+
+ const bool isQuadratic = hints & PathQuadratic;
+
+ QPolygonF quads;
+ QPointF sp;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ QPainterPath::Element element = path.elementAt(i);
+
+ QPointF ep(element);
+ switch (element.type) {
+ case QPainterPath::MoveToElement:
+ res.moveTo(QVector2D(ep));
+ break;
+ case QPainterPath::LineToElement:
+ res.lineTo(QVector2D(ep));
+ break;
+ case QPainterPath::CurveToElement: {
+ QPointF cp1 = ep;
+ QPointF cp2(path.elementAt(++i));
+ ep = path.elementAt(++i);
+ if (isQuadratic) {
+ const qreal f = 3.0 / 2.0;
+ const QPointF cp = sp + f * (cp1 - sp);
+ res.quadTo(QVector2D(cp), QVector2D(ep));
+ } else {
+ QBezier b = QBezier::fromPoints(sp, cp1, cp2, ep);
+ qt_toQuadratics(b, &quads);
+ for (int i = 1; i < quads.size(); i += 2) {
+ QVector2D cp(quads[i]);
+ QVector2D ep(quads[i + 1]);
+ res.quadTo(cp, ep);
+ }
+ }
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ sp = ep;
+ }
+
+ res.setPathHints(hints | PathQuadratic);
+ return res;
+}
+
+void QQuadPath::addCurvatureData()
+{
+ // We use the convention that the inside of a curve is on the *right* side of the
+ // direction of the baseline.Thus, as long as this is true: if the control point is
+ // on the left side of the baseline, the curve is convex and otherwise it is
+ // concave. The paths we get can be arbitrary order, but each subpath will have a
+ // consistent order. Therefore, for the first curve element in a subpath, we can
+ // determine if the direction already follows the convention or not, and then we
+ // can easily detect curvature of all subsequent elements in the subpath.
+
+ static bool checkAnomaly = qEnvironmentVariableIntValue("QT_QUICKSHAPES_CHECK_ALL_CURVATURE") != 0;
+ const bool pathHasFillOnRight = testHint(PathFillOnRight);
+
+ Element::CurvatureFlags flags = Element::CurvatureUndetermined;
+ for (QQuadPath::Element &element : m_elements) {
+ Q_ASSERT(element.childCount() == 0);
+ if (element.isSubpathStart()) {
+ if (pathHasFillOnRight && !checkAnomaly)
+ flags = Element::FillOnRight;
+ else
+ flags = coordinateOrderOfElement(element);
+ } else if (checkAnomaly) {
+ Element::CurvatureFlags newFlags = coordinateOrderOfElement(element);
+ if (flags != newFlags) {
+ qDebug() << "Curvature anomaly detected:" << element
+ << "Subpath fill on right:" << (flags & Element::FillOnRight)
+ << "Element fill on right:" << (newFlags & Element::FillOnRight);
+ flags = newFlags;
+ }
+ }
+
+ if (element.isLine()) {
+ element.m_curvatureFlags = flags;
+ } else {
+ bool controlPointOnLeft = element.isControlPointOnLeft();
+ bool isFillOnRight = flags & Element::FillOnRight;
+ bool isConvex = controlPointOnLeft == isFillOnRight;
+
+ if (isConvex)
+ element.m_curvatureFlags = Element::CurvatureFlags(flags | Element::Convex);
+ else
+ element.m_curvatureFlags = flags;
+ }
+ }
+}
+
+QRectF QQuadPath::controlPointRect() const
+{
+ QRectF res;
+ if (elementCount()) {
+ QVector2D min, max;
+ min = max = m_elements.constFirst().sp;
+ // No need to recurse, as split curve's controlpoints are within the parent curve's
+ for (const QQuadPath::Element &e : std::as_const(m_elements)) {
+ min.setX(std::min({ min.x(), e.sp.x(), e.cp.x(), e.ep.x() }));
+ min.setY(std::min({ min.y(), e.sp.y(), e.cp.y(), e.ep.y() }));
+ max.setX(std::max({ max.x(), e.sp.x(), e.cp.x(), e.ep.x() }));
+ max.setY(std::max({ max.y(), e.sp.y(), e.cp.y(), e.ep.y() }));
+ }
+ res = QRectF(min.toPointF(), max.toPointF());
+ }
+ return res;
+}
+
+// Count leaf elements
+int QQuadPath::elementCountRecursive() const
+{
+ int count = 0;
+ iterateElements([&](const QQuadPath::Element &, int) { count++; });
+ return count;
+}
+
+QPainterPath QQuadPath::toPainterPath() const
+{
+ // Currently only converts the main, unsplit path; no need for the split path identified
+ QPainterPath res;
+ res.reserve(elementCount());
+ res.setFillRule(fillRule());
+ for (const Element &element : m_elements) {
+ if (element.m_isSubpathStart)
+ res.moveTo(element.startPoint().toPointF());
+ if (element.m_isLine)
+ res.lineTo(element.endPoint().toPointF());
+ else
+ res.quadTo(element.controlPoint().toPointF(), element.endPoint().toPointF());
+ };
+ return res;
+}
+
+QString QQuadPath::asSvgString() const
+{
+ QString res;
+ QTextStream str(&res);
+ for (const Element &element : m_elements) {
+ if (element.isSubpathStart())
+ str << "M " << element.startPoint().x() << " " << element.startPoint().y() << " ";
+ if (element.isLine())
+ str << "L " << element.endPoint().x() << " " << element.endPoint().y() << " ";
+ else
+ str << "Q " << element.controlPoint().x() << " " << element.controlPoint().y() << " "
+ << element.endPoint().x() << " " << element.endPoint().y() << " ";
+ }
+ return res;
+}
+
+// Returns a new path since doing it inline would probably be less efficient
+// (technically changing it from O(n) to O(n^2))
+// Note that this function should be called before splitting any elements,
+// so we can assume that the structure is a list and not a tree
+QQuadPath QQuadPath::subPathsClosed(bool *didClose) const
+{
+ Q_ASSERT(m_childElements.isEmpty());
+ bool closed = false;
+ QQuadPath res = *this;
+ res.m_subPathToStart = false;
+ res.m_elements = {};
+ res.m_elements.reserve(elementCount());
+ int subStart = -1;
+ int prevElement = -1;
+ for (int i = 0; i < elementCount(); i++) {
+ const auto &element = m_elements.at(i);
+ if (element.m_isSubpathStart) {
+ if (subStart >= 0 && m_elements[i - 1].ep != m_elements[subStart].sp) {
+ res.m_currentPoint = m_elements[i - 1].ep;
+ res.lineTo(m_elements[subStart].sp);
+ closed = true;
+ auto &endElement = res.m_elements.last();
+ endElement.m_isSubpathEnd = true;
+ // lineTo() can bail out if the points are too close.
+ // In that case, just change the end point to be equal to the start
+ // (No need to test because the assignment is a no-op otherwise).
+ endElement.ep = m_elements[subStart].sp;
+ } else if (prevElement >= 0) {
+ res.m_elements[prevElement].m_isSubpathEnd = true;
+ }
+ subStart = i;
+ }
+ res.m_elements.append(element);
+ prevElement = res.m_elements.size() - 1;
+ }
+
+ if (subStart >= 0 && m_elements.last().ep != m_elements[subStart].sp) {
+ res.m_currentPoint = m_elements.last().ep;
+ res.lineTo(m_elements[subStart].sp);
+ closed = true;
+ }
+ if (!res.m_elements.isEmpty()) {
+ auto &endElement = res.m_elements.last();
+ endElement.m_isSubpathEnd = true;
+ endElement.ep = m_elements[subStart].sp;
+ }
+
+ if (didClose)
+ *didClose = closed;
+ return res;
+}
+
+QQuadPath QQuadPath::flattened() const
+{
+ QQuadPath res;
+ res.reserve(elementCountRecursive());
+ iterateElements([&](const QQuadPath::Element &elem, int) { res.m_elements.append(elem); });
+ res.setPathHints(pathHints());
+ res.setFillRule(fillRule());
+ return res;
+}
+
+class ElementCutter
+{
+public:
+ ElementCutter(const QQuadPath::Element &element)
+ : m_element(element)
+ {
+ m_currentPoint = m_element.startPoint();
+ if (m_element.isLine())
+ m_lineLength = (m_element.endPoint() - m_element.startPoint()).length();
+ else
+ fillLUT();
+ }
+
+ bool consume(float length)
+ {
+ m_lastT = m_currentT;
+ m_lastPoint = m_currentPoint;
+ float nextCut = m_consumed + length;
+ float cutT = m_element.isLine() ? nextCut / m_lineLength : tForLength(nextCut);
+ if (cutT < 1) {
+ m_currentT = cutT;
+ m_currentPoint = m_element.pointAtFraction(m_currentT);
+ m_consumed = nextCut;
+ return true;
+ } else {
+ m_currentT = 1;
+ m_currentPoint = m_element.endPoint();
+ return false;
+ }
+ }
+
+ QVector2D currentCutPoint()
+ {
+ return m_currentPoint;
+ }
+
+ QVector2D currentControlPoint()
+ {
+ Q_ASSERT(!m_element.isLine());
+ // Split curve right at lastT, yields { lastPoint, rcp, endPoint } quad segment
+ QVector2D rcp = (1 - m_lastT) * m_element.controlPoint() + m_lastT * m_element.endPoint();
+ // Split that left at currentT, yields { lastPoint, lcp, currentPoint } quad segment
+ float segmentT = (m_currentT - m_lastT) / (1 - m_lastT);
+ QVector2D lcp = (1 - segmentT) * m_lastPoint + segmentT * rcp;
+ return lcp;
+ }
+
+ float lastLength()
+ {
+ float elemLength = m_element.isLine() ? m_lineLength : m_lut.last();
+ return elemLength - m_consumed;
+ }
+
+private:
+ void fillLUT()
+ {
+ Q_ASSERT(!m_element.isLine());
+ QVector2D ap = m_element.startPoint() - 2 * m_element.controlPoint() + m_element.endPoint();
+ QVector2D bp = 2 * m_element.controlPoint() - 2 * m_element.startPoint();
+ float A = 4 * QVector2D::dotProduct(ap, ap);
+ float B = 4 * QVector2D::dotProduct(ap, bp);
+ float C = QVector2D::dotProduct(bp, bp);
+ float b = B / (2 * A);
+ float c = C / A;
+ float k = c - (b * b);
+ float l2 = b * std::sqrt(b * b + k);
+ float lnom = b + std::sqrt(b * b + k);
+ float l0 = 0.5f * std::sqrt(A);
+
+ m_lut.resize(LUTSize, 0);
+ for (int i = 1; i < LUTSize; i++) {
+ float t = float(i) / (LUTSize - 1);
+ float u = t + b;
+ float w = std::sqrt(u * u + k);
+ float l1 = u * w;
+ float lden = u + w;
+ float l3 = k * std::log(std::fabs(lden / lnom));
+ float res = l0 * (l1 - l2 + l3);
+ m_lut[i] = res;
+ }
+ }
+
+ float tForLength(float length)
+ {
+ Q_ASSERT(!m_element.isLine());
+ Q_ASSERT(!m_lut.isEmpty());
+
+ float res = 2; // I.e. invalid, outside [0, 1] range
+ auto it = std::upper_bound(m_lut.cbegin(), m_lut.cend(), length);
+ if (it != m_lut.cend()) {
+ float nextLength = *it--;
+ float prevLength = *it;
+ int prevIndex = std::distance(m_lut.cbegin(), it);
+ float fraction = (length - prevLength) / (nextLength - prevLength);
+ res = (prevIndex + fraction) / (LUTSize - 1);
+ }
+ return res;
+ }
+
+ const QQuadPath::Element &m_element;
+ float m_lastT = 0;
+ float m_currentT = 0;
+ QVector2D m_lastPoint;
+ QVector2D m_currentPoint;
+ float m_consumed = 0;
+ // For line elements:
+ float m_lineLength;
+ // For quadratic curve elements:
+ static constexpr int LUTSize = 21;
+ QVarLengthArray<float, LUTSize> m_lut;
+};
+
+QQuadPath QQuadPath::dashed(qreal lineWidth, const QList<qreal> &dashPattern, qreal dashOffset) const
+{
+ QVarLengthArray<float, 16> pattern;
+ float patternLength = 0;
+ for (int i = 0; i < 2 * (dashPattern.length() / 2); i++) {
+ float dashLength = qMax(lineWidth * dashPattern[i], qreal(0));
+ pattern.append(dashLength);
+ patternLength += dashLength;
+ }
+ if (patternLength == 0)
+ return {};
+
+ int startIndex = 0;
+ float startOffset = std::fmod(lineWidth * dashOffset, patternLength);
+ if (startOffset < 0)
+ startOffset += patternLength;
+ for (float dashLength : pattern) {
+ if (dashLength > startOffset)
+ break;
+ startIndex = (startIndex + 1) % pattern.size(); // The % guards against accuracy issues
+ startOffset -= dashLength;
+ }
+
+ int dashIndex = startIndex;
+ float offset = startOffset;
+ QQuadPath res;
+ for (int i = 0; i < elementCount(); i++) {
+ const Element &element = elementAt(i);
+ if (element.isSubpathStart()) {
+ res.moveTo(element.startPoint());
+ dashIndex = startIndex;
+ offset = startOffset;
+ }
+ ElementCutter cutter(element);
+ while (true) {
+ bool gotAll = cutter.consume(pattern.at(dashIndex) - offset);
+ QVector2D nextPoint = cutter.currentCutPoint();
+ if (dashIndex & 1)
+ res.moveTo(nextPoint); // gap
+ else if (element.isLine())
+ res.lineTo(nextPoint); // dash in line
+ else
+ res.quadTo(cutter.currentControlPoint(), nextPoint); // dash in curve
+ if (gotAll) {
+ offset = 0;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ } else {
+ offset += cutter.lastLength();
+ break;
+ }
+ }
+ }
+ res.setFillRule(fillRule());
+ res.setPathHints(pathHints());
+ return res;
+}
+
+void QQuadPath::splitElementAt(int index)
+{
+ const int newChildIndex = m_childElements.size();
+ m_childElements.resize(newChildIndex + 2);
+ Element &parent = elementAt(index);
+ parent.m_numChildren = 2;
+ parent.m_firstChildIndex = newChildIndex;
+
+ Element &quad1 = m_childElements[newChildIndex];
+ const QVector2D mp = parent.midPoint();
+ quad1.sp = parent.sp;
+ quad1.cp = 0.5f * (parent.sp + parent.cp);
+ quad1.ep = mp;
+ quad1.m_isSubpathStart = parent.m_isSubpathStart;
+ quad1.m_isSubpathEnd = false;
+ quad1.m_curvatureFlags = parent.m_curvatureFlags;
+ quad1.m_isLine = parent.m_isLine; //### || isPointNearLine(quad1.cp, quad1.sp, quad1.ep);
+
+ Element &quad2 = m_childElements[newChildIndex + 1];
+ quad2.sp = mp;
+ quad2.cp = 0.5f * (parent.ep + parent.cp);
+ quad2.ep = parent.ep;
+ quad2.m_isSubpathStart = false;
+ quad2.m_isSubpathEnd = parent.m_isSubpathEnd;
+ quad2.m_curvatureFlags = parent.m_curvatureFlags;
+ quad2.m_isLine = parent.m_isLine; //### || isPointNearLine(quad2.cp, quad2.sp, quad2.ep);
+
+#ifndef QT_NO_DEBUG
+ if (qFuzzyCompare(quad1.sp, quad1.ep) || qFuzzyCompare(quad2.sp, quad2.ep))
+ qCDebug(lcSGCurveProcessor) << "Splitting has resulted in ~null quad";
+#endif
+}
+
+static void printElement(QDebug stream, const QQuadPath::Element &element)
+{
+ auto printPoint = [&](QVector2D p) { stream << "(" << p.x() << ", " << p.y() << ") "; };
+ stream << "{ ";
+ printPoint(element.startPoint());
+ printPoint(element.controlPoint());
+ printPoint(element.endPoint());
+ stream << "} " << (element.isLine() ? "L " : "C ") << (element.isConvex() ? "X " : "O ")
+ << (element.isSubpathStart() ? "S" : element.isSubpathEnd() ? "E" : "");
+}
+
+QDebug operator<<(QDebug stream, const QQuadPath::Element &element)
+{
+ QDebugStateSaver saver(stream);
+ stream.nospace();
+ stream << "QuadPath::Element( ";
+ printElement(stream, element);
+ stream << " )";
+ return stream;
+}
+
+QDebug operator<<(QDebug stream, const QQuadPath &path)
+{
+ QDebugStateSaver saver(stream);
+ stream.nospace();
+ stream << "QuadPath(" << path.elementCount() << " main elements, "
+ << path.elementCountRecursive() << " leaf elements, "
+ << (path.fillRule() == Qt::OddEvenFill ? "OddEven" : "Winding") << Qt::endl;
+ int count = 0;
+ path.iterateElements([&](const QQuadPath::Element &e, int) {
+ stream << " " << count++ << (e.isSubpathStart() ? " >" : " ");
+ printElement(stream, e);
+ stream << Qt::endl;
+ });
+ stream << ")";
+ return stream;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qquadpath_p.h b/src/quick/scenegraph/util/qquadpath_p.h
new file mode 100644
index 0000000000..98ef5b664c
--- /dev/null
+++ b/src/quick/scenegraph/util/qquadpath_p.h
@@ -0,0 +1,341 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUADPATH_P_H
+#define QQUADPATH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qrect.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qdebug.h>
+#include <QtGui/qvector2d.h>
+#include <QtGui/qpainterpath.h>
+#include <QtQuick/qtquickexports.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QQuadPath
+{
+public:
+ // This is a copy of the flags in QQuickShapePath ### TODO: use a common definition
+ enum PathHint : quint8 {
+ PathLinear = 0x1,
+ PathQuadratic = 0x2,
+ PathConvex = 0x4,
+ PathFillOnRight = 0x8,
+ PathSolid = 0x10,
+ PathNonIntersecting = 0x20,
+ PathNonOverlappingControlPointTriangles = 0x40
+ };
+ Q_DECLARE_FLAGS(PathHints, PathHint)
+
+ class Q_QUICK_EXPORT Element
+ {
+ public:
+ Element ()
+ : m_isSubpathStart(false), m_isSubpathEnd(false), m_isLine(false)
+ {
+ }
+
+ Element (QVector2D s, QVector2D c, QVector2D e)
+ : sp(s), cp(c), ep(e), m_isSubpathStart(false), m_isSubpathEnd(false), m_isLine(false)
+ {
+ }
+
+ bool isSubpathStart() const
+ {
+ return m_isSubpathStart;
+ }
+
+ bool isSubpathEnd() const
+ {
+ return m_isSubpathEnd;
+ }
+
+ bool isLine() const
+ {
+ return m_isLine;
+ }
+
+ bool isConvex() const
+ {
+ return m_curvatureFlags & Convex;
+ }
+
+ QVector2D startPoint() const
+ {
+ return sp;
+ }
+
+ QVector2D controlPoint() const
+ {
+ return cp;
+ }
+
+ QVector2D endPoint() const
+ {
+ return ep;
+ }
+
+ QVector2D midPoint() const
+ {
+ return isLine() ? 0.5f * (sp + ep) : (0.25f * sp) + (0.5f * cp) + (0.25 * ep);
+ }
+
+ /* For a curve, returns the control point. For a line, returns an arbitrary point on the
+ * inside side of the line (assuming the curvature has been set for the path). The point
+ * doesn't need to actually be inside the shape: it just makes for easier calculations
+ * later when it is at the same side as the fill. */
+ QVector2D referencePoint() const
+ {
+ if (isLine()) {
+ QVector2D normal(sp.y() - ep.y(), ep.x() - sp.x());
+ return m_curvatureFlags & Element::FillOnRight ? sp + normal : sp - normal;
+ } else {
+ return cp;
+ }
+ }
+
+ Element segmentFromTo(float t0, float t1) const;
+
+ Element reversed() const;
+
+ int childCount() const { return m_numChildren; }
+
+ int indexOfChild(int childNumber) const
+ {
+ Q_ASSERT(childNumber >= 0 && childNumber < childCount());
+ return -(m_firstChildIndex + 1 + childNumber);
+ }
+
+ QVector2D pointAtFraction(float t) const;
+
+ QVector2D tangentAtFraction(float t) const
+ {
+ return isLine() ? (ep - sp) : ((1 - t) * 2 * (cp - sp)) + (t * 2 * (ep - cp));
+ }
+
+ QVector2D normalAtFraction(float t) const
+ {
+ const QVector2D tan = tangentAtFraction(t);
+ return QVector2D(-tan.y(), tan.x());
+ }
+
+ float extent() const;
+
+ void setAsConvex(bool isConvex)
+ {
+ if (isConvex)
+ m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags | Element::Convex);
+ else
+ m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags & ~Element::Convex);
+ }
+
+ void setFillOnRight(bool isFillOnRight)
+ {
+ if (isFillOnRight)
+ m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags | Element::FillOnRight);
+ else
+ m_curvatureFlags = Element::CurvatureFlags(m_curvatureFlags & ~Element::FillOnRight);
+ }
+
+ bool isFillOnRight() const { return m_curvatureFlags & FillOnRight; }
+
+ bool isControlPointOnLeft() const
+ {
+ return isPointOnLeft(cp, sp, ep);
+ }
+
+ enum CurvatureFlags : quint8 {
+ CurvatureUndetermined = 0,
+ FillOnRight = 1,
+ Convex = 2
+ };
+
+ enum FillSide : quint8 {
+ FillSideUndetermined = 0,
+ FillSideRight = 1,
+ FillSideLeft = 2,
+ FillSideBoth = 3
+ };
+
+ private:
+ int intersectionsAtY(float y, float *fractions, bool swapXY = false) const;
+
+ QVector2D sp;
+ QVector2D cp;
+ QVector2D ep;
+ int m_firstChildIndex = 0;
+ quint8 m_numChildren = 0;
+ CurvatureFlags m_curvatureFlags = CurvatureUndetermined;
+ quint8 m_isSubpathStart : 1;
+ quint8 m_isSubpathEnd : 1;
+ quint8 m_isLine : 1;
+ friend class QQuadPath;
+ friend Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath::Element &);
+ };
+
+ void moveTo(const QVector2D &to)
+ {
+ m_subPathToStart = true;
+ m_currentPoint = to;
+ }
+
+ void lineTo(const QVector2D &to)
+ {
+ addElement({}, to, true);
+ }
+
+ void quadTo(const QVector2D &control, const QVector2D &to)
+ {
+ addElement(control, to);
+ }
+
+ Element &elementAt(int i)
+ {
+ return i < 0 ? m_childElements[-(i + 1)] : m_elements[i];
+ }
+
+ const Element &elementAt(int i) const
+ {
+ return i < 0 ? m_childElements[-(i + 1)] : m_elements[i];
+ }
+
+ int indexOfChildAt(int i, int childNumber) const
+ {
+ return elementAt(i).indexOfChild(childNumber);
+ }
+
+ QRectF controlPointRect() const;
+
+ Qt::FillRule fillRule() const { return m_windingFill ? Qt::WindingFill : Qt::OddEvenFill; }
+ void setFillRule(Qt::FillRule rule) { m_windingFill = (rule == Qt::WindingFill); }
+
+ void reserve(int size) { m_elements.reserve(size); }
+ int elementCount() const { return m_elements.size(); }
+ bool isEmpty() const { return m_elements.size() == 0; }
+ int elementCountRecursive() const;
+
+ static QQuadPath fromPainterPath(const QPainterPath &path, PathHints hints = {});
+ QPainterPath toPainterPath() const;
+ QString asSvgString() const;
+
+ QQuadPath subPathsClosed(bool *didClose = nullptr) const;
+ void addCurvatureData();
+ QQuadPath flattened() const;
+ QQuadPath dashed(qreal lineWidth, const QList<qreal> &dashPattern, qreal dashOffset = 0) const;
+ void splitElementAt(int index);
+ bool contains(const QVector2D &point) const;
+ bool contains(const QVector2D &point, int fromIndex, int toIndex) const;
+ Element::FillSide fillSideOf(int elementIdx, float elementT) const;
+
+ template<typename Func>
+ void iterateChildrenOf(Element &e, Func &&lambda)
+ {
+ const int lastChildIndex = e.m_firstChildIndex + e.childCount() - 1;
+ for (int i = e.m_firstChildIndex; i <= lastChildIndex; i++) {
+ Element &c = m_childElements[i];
+ if (c.childCount() > 0)
+ iterateChildrenOf(c, lambda);
+ else
+ lambda(c, -(i + 1));
+ }
+ }
+
+ template<typename Func>
+ void iterateChildrenOf(const Element &e, Func &&lambda) const
+ {
+ const int lastChildIndex = e.m_firstChildIndex + e.childCount() - 1;
+ for (int i = e.m_firstChildIndex; i <= lastChildIndex; i++) {
+ const Element &c = m_childElements[i];
+ if (c.childCount() > 0)
+ iterateChildrenOf(c, lambda);
+ else
+ lambda(c, -(i + 1));
+ }
+ }
+
+ template<typename Func>
+ void iterateElements(Func &&lambda)
+ {
+ for (int i = 0; i < m_elements.size(); i++) {
+ Element &e = m_elements[i];
+ if (e.childCount() > 0)
+ iterateChildrenOf(e, lambda);
+ else
+ lambda(e, i);
+ }
+ }
+
+ template<typename Func>
+ void iterateElements(Func &&lambda) const
+ {
+ for (int i = 0; i < m_elements.size(); i++) {
+ const Element &e = m_elements[i];
+ if (e.childCount() > 0)
+ iterateChildrenOf(e, lambda);
+ else
+ lambda(e, i);
+ }
+ }
+
+ static QVector2D closestPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+ static bool isPointOnLeft(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+ static bool isPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+ static bool isPointNearLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+
+ bool testHint(PathHint hint) const
+ {
+ return m_hints.testFlag(hint);
+ }
+
+ void setHint(PathHint hint, bool on = true)
+ {
+ m_hints.setFlag(hint, on);
+ }
+
+ PathHints pathHints() const
+ {
+ return m_hints;
+ }
+
+ void setPathHints(PathHints newHints)
+ {
+ m_hints = newHints;
+ }
+
+private:
+ void addElement(const QVector2D &control, const QVector2D &to, bool isLine = false);
+ void addElement(const Element &e);
+ Element::CurvatureFlags coordinateOrderOfElement(const Element &element) const;
+
+ friend Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath &);
+
+ QList<Element> m_elements;
+ QList<Element> m_childElements;
+ QVector2D m_currentPoint;
+ bool m_subPathToStart = true;
+ bool m_windingFill = false;
+ PathHints m_hints;
+
+ friend class QSGCurveProcessor;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuadPath::PathHints);
+
+Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath::Element &);
+Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath &);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/util/qsgareaallocator.cpp b/src/quick/scenegraph/util/qsgareaallocator.cpp
index 977af81ab7..914b5b6c32 100644
--- a/src/quick/scenegraph/util/qsgareaallocator.cpp
+++ b/src/quick/scenegraph/util/qsgareaallocator.cpp
@@ -13,16 +13,13 @@
QT_BEGIN_NAMESPACE
-namespace
+enum SplitType
{
- enum SplitType
- {
- VerticalSplit,
- HorizontalSplit
- };
+ VerticalSplit,
+ HorizontalSplit
+};
- static const int maxMargin = 2;
-}
+static const int maxMargin = 2;
struct QSGAreaAllocatorNode
{
diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h
index 07bc1488a2..fa53dde037 100644
--- a/src/quick/scenegraph/util/qsgareaallocator_p.h
+++ b/src/quick/scenegraph/util/qsgareaallocator_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QRect;
class QPoint;
struct QSGAreaAllocatorNode;
-class Q_QUICK_PRIVATE_EXPORT QSGAreaAllocator
+class Q_QUICK_EXPORT QSGAreaAllocator
{
public:
QSGAreaAllocator(const QSize &size);
diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h
index 7a541d5d96..344acd5c55 100644
--- a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGImageNode
+class Q_QUICK_EXPORT QSGDefaultImageNode : public QSGImageNode
{
public:
QSGDefaultImageNode();
diff --git a/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h b/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h
index 298ed31237..ce33e449ab 100644
--- a/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultNinePatchNode : public QSGNinePatchNode
+class Q_QUICK_EXPORT QSGDefaultNinePatchNode : public QSGNinePatchNode
{
public:
QSGDefaultNinePatchNode();
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
index 2d6f6acf08..874e6dac38 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
@@ -108,7 +108,8 @@ void QSGDefaultPainterNode::paint()
}
painter.setCompositionMode(QPainter::CompositionMode_Source);
- painter.fillRect(clipRect, m_fillColor);
+ if (m_fillColor.isValid())
+ painter.fillRect(clipRect, m_fillColor);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
m_item->paint(&painter);
@@ -167,7 +168,7 @@ void QSGDefaultPainterNode::updateRenderTarget()
if (!m_image.isNull() && !m_dirtyGeometry)
return;
- m_image = QImage(m_textureSize, QImage::Format_ARGB32_Premultiplied);
+ m_image = QImage(m_textureSize, QImage::Format_RGBA8888_Premultiplied);
m_image.fill(Qt::transparent);
if (!m_texture) {
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
index 17e158204b..4038aa0733 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
class QSGDefaultRenderContext;
-class Q_QUICK_PRIVATE_EXPORT QSGPainterTexture : public QSGPlainTexture
+class Q_QUICK_EXPORT QSGPainterTexture : public QSGPlainTexture
{
public:
QSGPainterTexture();
@@ -40,7 +40,7 @@ private:
QRect m_dirty_rect;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultPainterNode : public QSGPainterNode
+class Q_QUICK_EXPORT QSGDefaultPainterNode : public QSGPainterNode
{
public:
QSGDefaultPainterNode(QQuickPaintedItem *item);
diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
index c7502c91fb..9c491cb7df 100644
--- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
@@ -9,7 +9,7 @@ QT_BEGIN_NAMESPACE
class FlatColorMaterialRhiShader : public QSGMaterialShader
{
public:
- FlatColorMaterialRhiShader();
+ FlatColorMaterialRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
@@ -18,10 +18,10 @@ public:
QSGMaterialType FlatColorMaterialRhiShader::type;
-FlatColorMaterialRhiShader::FlatColorMaterialRhiShader()
+FlatColorMaterialRhiShader::FlatColorMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/flatcolor.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/flatcolor.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/flatcolor.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/flatcolor.frag.qsb"), viewCount);
}
bool FlatColorMaterialRhiShader::updateUniformData(RenderState &state,
@@ -33,11 +33,15 @@ bool FlatColorMaterialRhiShader::updateUniformData(RenderState &state,
QSGFlatColorMaterial *mat = static_cast<QSGFlatColorMaterial *>(newMaterial);
bool changed = false;
QByteArray *buf = state.uniformData();
-
- if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
+
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
}
const QColor &c = mat->color();
@@ -47,7 +51,7 @@ bool FlatColorMaterialRhiShader::updateUniformData(RenderState &state,
const float opacity = state.opacity() * a;
QVector4D v(r * opacity, g * opacity, b * opacity, opacity);
Q_ASSERT(sizeof(v) == 16);
- memcpy(buf->data() + 64, &v, 16);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &v, 16);
changed = true;
}
@@ -130,7 +134,7 @@ QSGMaterialType *QSGFlatColorMaterial::type() const
QSGMaterialShader *QSGFlatColorMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new FlatColorMaterialRhiShader;
+ return new FlatColorMaterialRhiShader(viewCount());
}
diff --git a/src/quick/scenegraph/util/qsggradientcache.cpp b/src/quick/scenegraph/util/qsggradientcache.cpp
new file mode 100644
index 0000000000..ae88cd2e3e
--- /dev/null
+++ b/src/quick/scenegraph/util/qsggradientcache.cpp
@@ -0,0 +1,121 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsggradientcache_p.h"
+
+#include <QtGui/private/qdrawhelper_p.h>
+#include <QtGui/rhi/qrhi.h>
+
+#include <QtQuick/qsgtexture.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static void generateGradientColorTable(const QSGGradientCacheKey &gradient,
+ uint *colorTable, int size, float opacity)
+{
+ int pos = 0;
+ const QGradientStops &s = gradient.stops;
+ Q_ASSERT(!s.isEmpty());
+ const bool colorInterpolation = true;
+
+ uint alpha = qRound(opacity * 256);
+ uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha);
+ qreal incr = 1.0 / qreal(size);
+ qreal fpos = 1.5 * incr;
+ colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color));
+
+ while (fpos <= s.first().first) {
+ colorTable[pos] = colorTable[pos - 1];
+ pos++;
+ fpos += incr;
+ }
+
+ if (colorInterpolation)
+ current_color = qPremultiply(current_color);
+
+ const int sLast = s.size() - 1;
+ for (int i = 0; i < sLast; ++i) {
+ qreal delta = 1/(s[i+1].first - s[i].first);
+ uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha);
+ if (colorInterpolation)
+ next_color = qPremultiply(next_color);
+
+ while (fpos < s[i+1].first && pos < size) {
+ int dist = int(256 * ((fpos - s[i].first) * delta));
+ int idist = 256 - dist;
+ if (colorInterpolation)
+ colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
+ else
+ colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
+ ++pos;
+ fpos += incr;
+ }
+ current_color = next_color;
+ }
+
+ uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha)));
+ for ( ; pos < size; ++pos)
+ colorTable[pos] = last_color;
+
+ colorTable[size-1] = last_color;
+}
+
+QSGGradientCache::~QSGGradientCache()
+{
+ qDeleteAll(m_textures);
+}
+
+QSGGradientCache *QSGGradientCache::cacheForRhi(QRhi *rhi)
+{
+ static QHash<QRhi *, QSGGradientCache *> caches;
+ auto it = caches.constFind(rhi);
+ if (it != caches.constEnd())
+ return *it;
+
+ QSGGradientCache *cache = new QSGGradientCache;
+ rhi->addCleanupCallback([cache](QRhi *rhi) {
+ caches.remove(rhi);
+ delete cache;
+ });
+ caches.insert(rhi, cache);
+ return cache;
+}
+
+QSGTexture *QSGGradientCache::get(const QSGGradientCacheKey &grad)
+{
+ QSGPlainTexture *tx = m_textures[grad];
+ if (!tx) {
+ static const int W = 1024; // texture size is 1024x1
+ QImage gradTab(W, 1, QImage::Format_RGBA8888_Premultiplied);
+ if (!grad.stops.isEmpty())
+ generateGradientColorTable(grad, reinterpret_cast<uint *>(gradTab.bits()), W, 1.0f);
+ else
+ gradTab.fill(Qt::black);
+ tx = new QSGPlainTexture;
+ tx->setImage(gradTab);
+ switch (grad.spread) {
+ case QGradient::PadSpread:
+ tx->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ tx->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ break;
+ case QGradient::RepeatSpread:
+ tx->setHorizontalWrapMode(QSGTexture::Repeat);
+ tx->setVerticalWrapMode(QSGTexture::Repeat);
+ break;
+ case QGradient::ReflectSpread:
+ tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat);
+ tx->setVerticalWrapMode(QSGTexture::MirroredRepeat);
+ break;
+ default:
+ qWarning("Unknown gradient spread mode %d", grad.spread);
+ break;
+ }
+ tx->setFiltering(QSGTexture::Linear);
+ m_textures[grad] = tx;
+ }
+ return tx;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsggradientcache_p.h b/src/quick/scenegraph/util/qsggradientcache_p.h
new file mode 100644
index 0000000000..f384821978
--- /dev/null
+++ b/src/quick/scenegraph/util/qsggradientcache_p.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGGRADIENTCACHE_P_H
+#define QSGGRADIENTCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qhash.h>
+#include <QtGui/qbrush.h>
+
+#include <QtQuick/qtquickexports.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGTexture;
+class QSGPlainTexture;
+class QRhi;
+
+struct Q_QUICK_EXPORT QSGGradientCacheKey
+{
+ QSGGradientCacheKey(const QGradientStops &stops, QGradient::Spread spread)
+ : stops(stops), spread(spread)
+ { }
+ QGradientStops stops;
+ QGradient::Spread spread;
+ bool operator==(const QSGGradientCacheKey &other) const
+ {
+ return spread == other.spread && stops == other.stops;
+ }
+};
+
+inline size_t qHash(const QSGGradientCacheKey &v, size_t seed = 0)
+{
+ size_t h = seed + v.spread;
+ for (int i = 0; i < 3 && i < v.stops.size(); ++i)
+ h += v.stops[i].second.rgba();
+ return h;
+}
+
+class Q_QUICK_EXPORT QSGGradientCache
+{
+public:
+ struct GradientDesc { // can fully describe a linear/radial/conical gradient
+ QGradientStops stops;
+ QGradient::Spread spread = QGradient::PadSpread;
+ QPointF a; // start (L) or center point (R/C)
+ QPointF b; // end (L) or focal point (R)
+ qreal v0; // center radius (R) or start angle (C)
+ qreal v1; // focal radius (R)
+ };
+
+ ~QSGGradientCache();
+ static QSGGradientCache *cacheForRhi(QRhi *rhi);
+ QSGTexture *get(const QSGGradientCacheKey &grad);
+
+private:
+ QHash<QSGGradientCacheKey, QSGPlainTexture *> m_textures;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGGRADIENTCACHE_P_H
diff --git a/src/quick/scenegraph/util/qsgplaintexture.cpp b/src/quick/scenegraph/util/qsgplaintexture.cpp
index b262047c38..e3ea9ffc17 100644
--- a/src/quick/scenegraph/util/qsgplaintexture.cpp
+++ b/src/quick/scenegraph/util/qsgplaintexture.cpp
@@ -8,7 +8,7 @@
#include <private/qqmlglobal_p.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpa/qplatformnativeinterface.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtQuick/private/qsgrhisupport_p.h>
#include <qtquick_tracepoints_p.h>
@@ -67,7 +67,8 @@ void QSGPlainTexture::setTexture(QRhiTexture *texture) // RHI only
void QSGPlainTexture::setTextureFromNativeTexture(QRhi *rhi,
quint64 nativeObjectHandle,
- int nativeLayout, uint nativeFormat,
+ int nativeLayoutOrState,
+ uint nativeFormat,
const QSize &size,
QQuickWindow::CreateTextureOptions options,
QQuickWindowPrivate::TextureFromNativeTextureFlags flags)
@@ -90,7 +91,7 @@ void QSGPlainTexture::setTextureFromNativeTexture(QRhi *rhi,
QRhiTexture *t = rhi->newTexture(format, size, 1, texFlags);
// ownership of the native object is never taken
- t->createFrom({nativeObjectHandle, nativeLayout});
+ t->createFrom({nativeObjectHandle, nativeLayoutOrState});
setTexture(t);
}
diff --git a/src/quick/scenegraph/util/qsgplaintexture_p.h b/src/quick/scenegraph/util/qsgplaintexture_p.h
index 17b9e41413..caa14fa9df 100644
--- a/src/quick/scenegraph/util/qsgplaintexture_p.h
+++ b/src/quick/scenegraph/util/qsgplaintexture_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QSGPlainTexturePrivate;
-class Q_QUICK_PRIVATE_EXPORT QSGPlainTexture : public QSGTexture
+class Q_QUICK_EXPORT QSGPlainTexture : public QSGTexture
{
Q_OBJECT
Q_DECLARE_PRIVATE(QSGPlainTexture)
@@ -53,7 +53,7 @@ public:
void setTexture(QRhiTexture *texture);
void setTextureFromNativeTexture(QRhi *rhi,
quint64 nativeObjectHandle,
- int nativeLayout,
+ int nativeLayoutOrState,
uint nativeFormat,
const QSize &size,
QQuickWindow::CreateTextureOptions options,
diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture_p.h b/src/quick/scenegraph/util/qsgrhiatlastexture_p.h
index b766a8f400..5eb9c68495 100644
--- a/src/quick/scenegraph/util/qsgrhiatlastexture_p.h
+++ b/src/quick/scenegraph/util/qsgrhiatlastexture_p.h
@@ -19,7 +19,7 @@
#include <QtQuick/private/qsgplaintexture_p.h>
#include <QtQuick/private/qsgareaallocator_p.h>
#include <QtGui/QSurface>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
index 74512f6eb1..b8583654e3 100644
--- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
+++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
@@ -256,7 +256,7 @@ void QSGSimpleTextureNode::setTextureCoordinatesTransform(QSGSimpleTextureNode::
return;
d->texCoordMode = mode;
qsgsimpletexturenode_update(&m_geometry, texture(), m_rect, d->sourceRect, d->texCoordMode);
- markDirty(DirtyMaterial);
+ markDirty(DirtyGeometry | DirtyMaterial);
}
/*!
diff --git a/src/quick/scenegraph/util/qsgtextnode.cpp b/src/quick/scenegraph/util/qsgtextnode.cpp
new file mode 100644
index 0000000000..6466fd1298
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgtextnode.cpp
@@ -0,0 +1,320 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgtextnode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGTextNode
+
+ \brief The QSGTextNode class is a class for drawing text layouts and text documents in
+ the Qt Quick scene graph.
+ \inmodule QtQuick
+ \since 6.7
+
+ QSGTextNode can be useful for creating custom Qt Quick items that require text. It is used
+ in Qt Quick by the Text, TextEdit and TextInput elements.
+
+ You can create QSGTextNode objects using QQuickWindow::createTextNode(). The addTextLayout()
+ and addTextDocument() functions provide ways to add text to the QSGTextNode. The text must
+ already be laid out.
+
+ \note Properties must be set before \l addTextLayout() or \l addTextDocument() are called in
+ order to have an effect.
+
+ \note The destruction of QSGTextNode has to be managed with care. In particular, since it
+ references graphics resources, it must be deleted when the Qt Quick scene graph is invalidated.
+ If the node is part of the graph and has the \c OwnedByParent flag set (which is the default),
+ this will happen automatically. However, if the \c OwnedByParent flag is cleared and the node is
+ disposed of manually, care must be taken to do this when the scene graph is invalidated. This
+ can be done by connecting to the \l{QQuickWindow::sceneGraphInvalidated()} signal, or by
+ implementing a slot in the QQuickItem subclass which is named \c{invalidateSceneGraph()}.
+ See also the documentation of QQuickItem for more details.
+ */
+
+/*!
+ \enum QSGTextNode::TextStyle
+
+ This enum type describes styles that can be applied to text rendering.
+
+ \value Normal The text is drawn without any style applied.
+ \value Outline The text is drawn with an outline.
+ \value Raised The text is drawn raised.
+ \value Sunken The text is drawn sunken.
+
+ \sa setTextStyle(), setStyleColor()
+*/
+
+/*!
+ \enum QSGTextNode::RenderType
+
+ This enum type describes type of glyph node used for rendering the text.
+
+ \value QtRendering Text is rendered using a scalable distance field for each glyph.
+ \value NativeRendering Text is rendered using a platform-specific technique.
+ \value CurveRendering Text is rendered using a curve rasterizer running directly on the
+ graphics hardware.
+
+ Select \c NativeRendering if you prefer text to look native on the target platform and do
+ not require advanced features such as transformation of the text. Using such features in
+ combination with the NativeRendering render type will lend poor and sometimes pixelated
+ results.
+
+ Both \c Text.QtRendering and \c Text.CurveRendering are hardware-accelerated techniques.
+ \c QtRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveRendering should be considered as an alternative in cases
+ where \c QtRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+
+ \sa setRenderType(), setRenderTypeQuality()
+*/
+
+/*!
+ \fn void QSGTextNode::setColor(QColor color)
+
+ Sets the main color to use when rendering the text to \a color.
+
+ The default is black: \c QColor(0, 0, 0).
+*/
+
+/*!
+ \fn QColor QSGTextNode::color() const
+
+ Returns the main color used when rendering the text.
+*/
+
+/*!
+ \fn void QSGTextNode::setStyleColor(QColor styleColor)
+
+ Sets the style color to use when rendering the text to \a styleColor.
+
+ The default is black: \c QColor(0, 0, 0).
+
+ \sa setTextStyle()
+*/
+
+/*!
+ \fn QColor QSGTextNode::styleColor() const
+
+ Returns the style color used when rendering the text.
+
+ \sa textStyle()
+*/
+
+/*!
+ \fn void QSGTextNode::setTextStyle(QSGTextNode::TextStyle textStyle)
+
+ Sets the style of the rendered text to \a textStyle. The default is \c Normal.
+
+ \sa setStyleColor()
+*/
+
+/*!
+ \fn QSGTextNode::TextStyle QSGTextNode::textStyle()
+
+ Returns the style of the rendered text.
+
+ \sa styleColor()
+*/
+
+/*!
+ \fn void QSGTextNode::setLinkColor(QColor linkColor)
+
+ Sets the color of or hyperlinks to \a linkColor in the text.
+
+ The default is blue: \c QColor(0, 0, 255).
+*/
+
+/*!
+ \fn QColor QSGTextNode::linkColor() const
+
+ Returns the color of hyperlinks in the text.
+*/
+
+/*!
+ \fn void QSGTextNode::setSelectionColor(QColor color)
+
+ Sets the color of the selection background to \a color when any part of the text is
+ marked as selected.
+
+ The default is dark blue: \c QColor(0, 0, 128).
+*/
+
+/*!
+ \fn QColor QSGTextNode::selectionColor() const
+
+ Returns the color of the selection background when any part of the text is marked as selected.
+*/
+
+/*!
+ \fn QColor QSGTextNode::selectionTextColor() const
+
+ Returns the color of the selection text when any part of the text is marked as selected.
+*/
+
+/*!
+ \fn void QSGTextNode::setSelectionTextColor(QColor selectionTextColor)
+
+ Sets the color of the selection text to \a selectionTextColor when any part of the text is
+ marked as selected.
+
+ The default is white: \c QColor(255, 255, 255).
+*/
+
+
+/*!
+ \fn void QSGTextNode::setRenderType(RenderType renderType)
+
+ Sets the type of glyph node in use to \a renderType.
+
+ The default is \l QtRendering.
+*/
+
+/*!
+ \fn QSGTextNode::RenderType QSGTextNode::renderType() const
+
+ Returns the type of glyph node used for rendering the text.
+*/
+
+/*!
+ \fn void QSGTextNode::setRenderTypeQuality(int renderTypeQuality)
+
+ If the \l renderType() in use supports it, set the quality to use when rendering the text.
+ When supported, this can be used to trade visual fidelity for execution speed or memory.
+
+ When the \a renderTypeQuality is < 0, the default quality is used.
+
+ The \a renderTypeQuality can be any integer, although limitations imposed by the underlying
+ graphics hardware may be encountered if extreme values are set. The Qt Quick Text element
+ operates with the following predefined values:
+
+ \value DefaultRenderTypeQuality -1 (default)
+ \value LowRenderTypeQuality 26
+ \value NormalRenderTypeQuality 52
+ \value HighRenderTypeQuality 104
+ \value VeryHighRenderTypeQuality 208
+
+ This value is currently only respected by the QtRendering render type. Setting it changes the
+ resolution of the distance fields used to represent the glyphs. Setting it above normal will
+ cause memory consumption to increase, but reduces filtering artifacts on very large text.
+
+ The default is -1.
+*/
+
+/*!
+ \fn int QSGTextNode::renderTypeQuality() const
+
+ Returns the render type quality of the node. See \l setRenderTypeQuality() for details.
+*/
+
+/*!
+ \fn void QSGTextNode::setFiltering(QSGTexture::Filtering filtering)
+
+ Sets the sampling mode used when scaling images that are part of the displayed text to
+ \a filtering. For smoothly scaled images, use \l{QSGTexture::Linear} here.
+
+ The default is \l{QSGTexture::Nearest}.
+
+ \sa filtering()
+*/
+
+/*!
+ \fn QSGTexture::Filtering QSGTextNode::filtering() const
+
+ Returns the sampling mode used when scaling images that are part of the displayed text.
+
+ \sa setFiltering()
+*/
+
+/*!
+ \fn void QSGTextNode::setViewport(const QRectF &viewport)
+
+ Sets the bounding rect of the viewport where the text is displayed to \a viewport. Providing
+ this information makes it possible for the QSGTextNode to optimize which parts of the text
+ layout or document are included in the scene graph.
+
+ The default is a default-constructed QRectF. For this viewport, all contents will be included
+ in the graph.
+*/
+
+/*!
+ \fn QRectF QSGTextNode::viewport() const
+
+ Returns the current viewport set for this QSGTextNode.
+*/
+
+/*!
+ \fn QSGTextNode::addTextLayout(QPointF position, QTextLayout *layout, int selectionStart = -1, int selectionCount = -1, int lineStart = 0, int lineCount = -1)
+
+ Adds the contents of \a layout to the text node at \a position. If \a selectionStart is >= 0,
+ then this marks the first character in a selected area of \a selectionCount number of
+ characters. The selection is represented as a background fill with the \l selectionColor() and
+ the selected text is rendered in the \l selectionTextColor().
+
+ For convenience, \a lineStart and \a lineCount can be used to select the range of \l QTextLine
+ objects to include from the layout. This can be useful, for instance, when creating elided
+ layouts. If \a lineCount is < 0, then the the node will include the lines from \a lineStart to
+ the end of the layout.
+
+ This function forwards its arguments to the virtual function doAddTextLayout().
+
+ \sa clear(), doAddTextLayout()
+*/
+
+/*!
+ \fn QSGTextNode::addTextDocument(QPointF position, QTextDocument *document, int selectionStart = -1, int selectionCount = -1)
+
+ Adds the contents of \a document to the text node at \a position. If \a selectionStart is >= 0,
+ then this marks the first character in a selected area of \a selectionCount number of
+ characters. The selection is represented as a background fill with the \l selectionColor() and
+ the selected text is rendered in the \l selectionTextColor().
+
+ This function forwards its arguments to the virtual function doAddTextDocument().
+
+ \sa clear(), doAddTextDocument()
+*/
+
+/*!
+ \fn QSGTextNode::doAddTextLayout(QPointF position, QTextLayout *layout, int selectionStart, int selectionCount, int lineStart, int lineCount)
+
+ Virtual function called by addTextLayout(), which converts the contents of \a layout to scene
+ graph nodes and adds them to the current node at \a position.
+
+ If \a selectionStart is >= 0, then this marks the first character in a selected area of
+ \a selectionCount number of characters. The selection is represented as a background fill with
+ the \l selectionColor() and the selected text is rendered in the \l selectionTextColor().
+
+ For convenience, \a lineStart and \a lineCount can be used to select the range of \l QTextLine
+ objects to include from the layout. This can be useful, for instance, when creating elided
+ layouts. If \a lineCount is < 0, then the the node will include the lines from \a lineStart to
+ the end of the layout.
+
+ \sa clear(), addTextLayout()
+*/
+
+/*!
+ \fn QSGTextNode::doAddTextDocument(QPointF position, QTextDocument *document, int selectionStart, int selectionCount)
+
+ Virtual function called by addTextDocument(), which converts the contents of \a document to
+ scene graph nodes and adds them to the current node at \a position.
+
+ If \a selectionStart is >= 0, then this marks the first character in a selected area of
+ \a selectionCount number of characters. The selection is represented as a background fill with
+ the \l selectionColor() and the selected text is rendered in the \l selectionTextColor().
+
+ \sa clear(), addTextDocument()
+*/
+
+/*!
+ \fn QSGTextNode::clear()
+
+ Clears the contents of the node, deleting nodes and other data that represents the layouts
+ and documents that have been added to it.
+
+ \sa addTextLayout(), addTextDocument()
+*/
+
+QSGTextNode::~QSGTextNode() = default;
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtextnode.h b/src/quick/scenegraph/util/qsgtextnode.h
new file mode 100644
index 0000000000..47431929af
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgtextnode.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGTEXTNODE_H
+#define QSGTEXTNODE_H
+
+#include <QtGui/qtextlayout.h>
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgtexture.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGTextNode : public QSGTransformNode
+{
+public:
+ ~QSGTextNode() override;
+
+ // Should match the TextStyle in qquicktext_p.h
+ enum TextStyle : quint8
+ {
+ Normal,
+ Outline,
+ Raised,
+ Sunken
+ };
+
+ // Should match the RenderType in qquicktext_p.h
+ enum RenderType: quint8
+ {
+ QtRendering,
+ NativeRendering,
+ CurveRendering
+ };
+
+ virtual void setColor(QColor color) = 0;
+ virtual QColor color() const = 0;
+
+ virtual void setTextStyle(TextStyle textStyle) = 0;
+ virtual TextStyle textStyle() = 0;
+
+ virtual void setStyleColor(QColor styleColor) = 0;
+ virtual QColor styleColor() const = 0;
+
+ virtual void setLinkColor(QColor linkColor) = 0;
+ virtual QColor linkColor() const = 0;
+
+ virtual void setSelectionColor(QColor selectionColor) = 0;
+ virtual QColor selectionColor() const = 0;
+
+ virtual void setSelectionTextColor(QColor selectionTextColor) = 0;
+ virtual QColor selectionTextColor() const = 0;
+
+ virtual void setRenderType(RenderType renderType) = 0;
+ virtual RenderType renderType() const = 0;
+
+ virtual void setRenderTypeQuality(int renderTypeQuality) = 0;
+ virtual int renderTypeQuality() const = 0;
+
+ virtual void setFiltering(QSGTexture::Filtering) = 0;
+ virtual QSGTexture::Filtering filtering() const = 0;
+
+ virtual void clear() = 0;
+
+ virtual void setViewport(const QRectF &viewport) = 0;
+ virtual QRectF viewport() const = 0;
+
+ void addTextLayout(QPointF position,
+ QTextLayout *layout,
+ int selectionStart = -1,
+ int selectionCount = -1,
+ int lineStart = 0,
+ int lineCount = -1)
+ {
+ doAddTextLayout(position, layout, selectionStart, selectionCount, lineStart, lineCount);
+ }
+
+ void addTextDocument(QPointF position,
+ QTextDocument *document,
+ int selectionStart = -1,
+ int selectionCount = -1)
+ {
+ doAddTextDocument(position, document, selectionStart, selectionCount);
+ }
+
+private:
+ virtual void doAddTextLayout(QPointF position,
+ QTextLayout *layout,
+ int selectionStart,
+ int selectionCount,
+ int lineStart,
+ int lineCount) = 0;
+ virtual void doAddTextDocument(QPointF position,
+ QTextDocument *document,
+ int selectionStart,
+ int selectionCount) = 0;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXTNODE_H
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index 6a02aef992..c573284f47 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -3,7 +3,7 @@
#include "qsgtexturematerial_p.h"
#include <private/qsgtexture_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -13,21 +13,24 @@ inline static bool isPowerOfTwo(int x)
return x == (x & -x);
}
-QSGOpaqueTextureMaterialRhiShader::QSGOpaqueTextureMaterialRhiShader()
+QSGOpaqueTextureMaterialRhiShader::QSGOpaqueTextureMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.frag.qsb"), viewCount);
}
-bool QSGOpaqueTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *)
+bool QSGOpaqueTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *)
{
bool changed = false;
QByteArray *buf = state.uniformData();
+ const int matrixCount = qMin(state.projectionMatrixCount(), newMaterial->viewCount());
- if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
}
return changed;
@@ -142,7 +145,7 @@ QSGMaterialType *QSGOpaqueTextureMaterial::type() const
QSGMaterialShader *QSGOpaqueTextureMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new QSGOpaqueTextureMaterialRhiShader;
+ return new QSGOpaqueTextureMaterialRhiShader(viewCount());
}
@@ -333,24 +336,26 @@ QSGMaterialType *QSGTextureMaterial::type() const
QSGMaterialShader *QSGTextureMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new QSGTextureMaterialRhiShader;
+ return new QSGTextureMaterialRhiShader(viewCount());
}
-QSGTextureMaterialRhiShader::QSGTextureMaterialRhiShader()
+QSGTextureMaterialRhiShader::QSGTextureMaterialRhiShader(int viewCount)
+ : QSGOpaqueTextureMaterialRhiShader(viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.frag.qsb"), viewCount);
}
bool QSGTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
bool changed = false;
QByteArray *buf = state.uniformData();
+ const int shaderMatrixCount = newMaterial->viewCount();
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 64, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
changed = true;
}
diff --git a/src/quick/scenegraph/util/qsgtexturematerial_p.h b/src/quick/scenegraph/util/qsgtexturematerial_p.h
index 5472ea4aa6..9334d6126b 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial_p.h
+++ b/src/quick/scenegraph/util/qsgtexturematerial_p.h
@@ -20,10 +20,10 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QSGOpaqueTextureMaterialRhiShader : public QSGMaterialShader
+class Q_QUICK_EXPORT QSGOpaqueTextureMaterialRhiShader : public QSGMaterialShader
{
public:
- QSGOpaqueTextureMaterialRhiShader();
+ QSGOpaqueTextureMaterialRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
@@ -32,7 +32,7 @@ public:
class QSGTextureMaterialRhiShader : public QSGOpaqueTextureMaterialRhiShader
{
public:
- QSGTextureMaterialRhiShader();
+ QSGTextureMaterialRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
index 7d5250f5ea..0d2643effa 100644
--- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
@@ -8,33 +8,35 @@ QT_BEGIN_NAMESPACE
class QSGVertexColorMaterialRhiShader : public QSGMaterialShader
{
public:
- QSGVertexColorMaterialRhiShader();
+ QSGVertexColorMaterialRhiShader(int viewCount);
- bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
-QSGVertexColorMaterialRhiShader::QSGVertexColorMaterialRhiShader()
+QSGVertexColorMaterialRhiShader::QSGVertexColorMaterialRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/vertexcolor.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/vertexcolor.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/vertexcolor.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/vertexcolor.frag.qsb"), viewCount);
}
-bool QSGVertexColorMaterialRhiShader::updateUniformData(RenderState &state,
- QSGMaterial * /*newEffect*/,
- QSGMaterial * /*oldEffect*/)
+bool QSGVertexColorMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *)
{
bool changed = false;
QByteArray *buf = state.uniformData();
-
- if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
+
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
}
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 64, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &opacity, 4);
changed = true;
}
@@ -114,7 +116,7 @@ QSGMaterialType *QSGVertexColorMaterial::type() const
QSGMaterialShader *QSGVertexColorMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new QSGVertexColorMaterialRhiShader;
+ return new QSGVertexColorMaterialRhiShader(viewCount());
}
QT_END_NAMESPACE
diff --git a/src/quick/util/qminimalflatset_p.h b/src/quick/util/qminimalflatset_p.h
index a7aed41305..0a882205ef 100644
--- a/src/quick/util/qminimalflatset_p.h
+++ b/src/quick/util/qminimalflatset_p.h
@@ -1,8 +1,12 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QMINIMALFLATSET_P_H
-#define QMINIMALFLATSET_P_H
+#ifndef QTDECLARATIVE_QMINIMALFLATSET_P_H
+#define QTDECLARATIVE_QMINIMALFLATSET_P_H
+
+#if __has_include(<QtCore/private/qminimalflatset_p.h>)
+# include <QtCore/private/qminimalflatset_p.h>
+#else
//
// W A R N I N G
@@ -140,4 +144,6 @@ private:
QT_END_NAMESPACE
-#endif // QMINIMALFLATSET_P_H
+#endif // !__has_include(<QtCore/private/qminimalflatset_p.h>)
+
+#endif // QTDECLARATIVE_QMINIMALFLATSET_P_H
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 509026192f..12208f375d 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -165,6 +165,27 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
return prop;
}
+void QQuickAbstractAnimationPrivate::animationGroupDirty()
+{
+ Q_ASSERT(group != nullptr);
+ if (!componentComplete)
+ return;
+
+ auto *animGroupPriv = static_cast<QQuickAnimationGroupPrivate *>(QQuickAnimationGroupPrivate::get(group));
+ if (animGroupPriv->running && !animGroupPriv->animationDirty) {
+ animGroupPriv->animationDirty = true;
+
+ if (animGroupPriv->animationInstance && group->currentTime() == 0) {
+ // restart if the animation didn't proceed yet.
+ animGroupPriv->restartFromCurrentLoop();
+ }
+ }
+
+ // check the animationGroup is one of another animationGroup members
+ if (animGroupPriv->group)
+ animGroupPriv->animationGroupDirty();
+}
+
/*!
\qmlsignal QtQuick::Animation::started()
@@ -696,6 +717,8 @@ void QQuickPauseAnimation::setDuration(int duration)
return;
d->duration = duration;
emit durationChanged(duration);
+ if (d->group)
+ d->animationGroupDirty();
}
QAbstractAnimationJob* QQuickPauseAnimation::transition(QQuickStateActions &actions,
@@ -1052,7 +1075,7 @@ QAbstractAnimationJob* QQuickScriptAction::transition(QQuickStateActions &action
PropertyAction object) so that the rotation animation begins with the
correct transform origin.
- \sa {Animation and Transitions in Qt Quick}, {Qt QML}
+ \sa {Animation and Transitions in Qt Quick}, {Qt Qml}
*/
QQuickPropertyAction::QQuickPropertyAction(QObject *parent)
: QQuickAbstractAnimation(*(new QQuickPropertyActionPrivate), parent)
@@ -1076,6 +1099,8 @@ void QQuickPropertyAction::setTargetObject(QObject *o)
return;
d->target = o;
emit targetChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
QString QQuickPropertyAction::property() const
@@ -1091,6 +1116,8 @@ void QQuickPropertyAction::setProperty(const QString &n)
return;
d->propertyName = n;
emit propertyChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
@@ -1121,6 +1148,8 @@ void QQuickPropertyAction::setProperties(const QString &p)
return;
d->properties = p;
emit propertiesChanged(p);
+ if (d->group)
+ d->animationGroupDirty();
}
QQmlListProperty<QObject> QQuickPropertyAction::targets()
@@ -1159,7 +1188,7 @@ QVariant QQuickPropertyAction::value() const
void QQuickPropertyAction::setValue(const QVariant &v)
{
Q_D(QQuickPropertyAction);
- if (d->value.isNull || d->value != v) {
+ if (!d->value.isValid() || d->value != v) {
d->value = v;
emit valueChanged(v);
}
@@ -1620,14 +1649,16 @@ void QQuickRotationAnimation::setTo(qreal t)
Possible values are:
- \list
- \li RotationAnimation.Numerical (default) - Rotate by linearly interpolating between the two numbers.
- A rotation from 10 to 350 will rotate 340 degrees clockwise.
- \li RotationAnimation.Clockwise - Rotate clockwise between the two values
- \li RotationAnimation.Counterclockwise - Rotate counterclockwise between the two values
- \li RotationAnimation.Shortest - Rotate in the direction that produces the shortest animation path.
- A rotation from 10 to 350 will rotate 20 degrees counterclockwise.
- \endlist
+ \value RotationAnimation.Numerical
+ (default) Rotate by linearly interpolating between the two numbers.
+ A rotation from \c 10 to \c 350 will rotate 340 degrees clockwise.
+ \value RotationAnimation.Clockwise
+ Rotate clockwise between the two values
+ \value RotationAnimation.Counterclockwise
+ Rotate counterclockwise between the two values
+ \value RotationAnimation.Shortest
+ Rotate in the direction that produces the shortest animation path.
+ A rotation from \c 10 to \c 350 will rotate \c 20 degrees counterclockwise.
*/
QQuickRotationAnimation::RotationDirection QQuickRotationAnimation::direction() const
{
@@ -1720,6 +1751,36 @@ void QQuickAnimationGroupPrivate::removeLast_animation(QQmlListProperty<QQuickAb
q->d_func()->animations.last()->setGroup(nullptr);
}
+void QQuickAnimationGroupPrivate::restartFromCurrentLoop()
+{
+ Q_Q(QQuickAnimationGroup);
+ if (!animationDirty)
+ return;
+
+ animationDirty = false;
+
+ Q_ASSERT(animationInstance);
+ const int currentLoop = animationInstance->currentLoop();
+
+ QSignalBlocker signalBlocker(q);
+ q->stop();
+ q->start();
+
+ Q_ASSERT(animationInstance);
+ // Restarting adjusts animationInstance's loopCount
+ // Since we just want to start it from this loop,
+ // it will be restored again.
+ if (loopCount != -1)
+ animationInstance->setLoopCount(loopCount - currentLoop);
+}
+
+void QQuickAnimationGroupPrivate::animationCurrentLoopChanged(QAbstractAnimationJob *)
+{
+ if (!animationDirty)
+ return;
+ restartFromCurrentLoop();
+}
+
QQuickAnimationGroup::~QQuickAnimationGroup()
{
Q_D(QQuickAnimationGroup);
@@ -2084,7 +2145,7 @@ void QQuickBulkValueAnimator::debugAnimation(QDebug d) const
Note that PropertyAnimation inherits the abstract \l Animation type.
This includes additional properties and methods for controlling the animation.
- \section1 Modifying Properties Duration Animations
+ \section1 Modifying running animations
Since Qt 6.4, it is possible to set the \l from, \l to, \l duration, and
\l easing properties on a top-level animation while it is running. The
@@ -2133,6 +2194,8 @@ void QQuickPropertyAnimation::setDuration(int duration)
if (d->componentComplete && d->running)
d->ourPropertiesDirty = true;
emit durationChanged(duration);
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
@@ -2162,6 +2225,8 @@ void QQuickPropertyAnimation::setFrom(const QVariant &f)
if (d->componentComplete && d->running)
d->ourPropertiesDirty = true;
emit fromChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
@@ -2191,6 +2256,8 @@ void QQuickPropertyAnimation::setTo(const QVariant &t)
if (d->componentComplete && d->running)
d->ourPropertiesDirty = true;
emit toChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
@@ -2423,6 +2490,8 @@ void QQuickPropertyAnimation::setEasing(const QEasingCurve &e)
if (d->componentComplete && d->running)
d->ourPropertiesDirty = true;
emit easingChanged(e);
+ if (d->group)
+ d->animationGroupDirty();
}
QObject *QQuickPropertyAnimation::target() const
@@ -2438,6 +2507,8 @@ void QQuickPropertyAnimation::setTargetObject(QObject *o)
return;
d->target = o;
emit targetChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
QString QQuickPropertyAnimation::property() const
@@ -2453,6 +2524,8 @@ void QQuickPropertyAnimation::setProperty(const QString &n)
return;
d->propertyName = n;
emit propertyChanged();
+ if (d->group)
+ d->animationGroupDirty();
}
QString QQuickPropertyAnimation::properties() const
@@ -2469,6 +2542,8 @@ void QQuickPropertyAnimation::setProperties(const QString &prop)
d->properties = prop;
emit propertiesChanged(prop);
+ if (d->group)
+ d->animationGroupDirty();
}
/*!
@@ -2574,7 +2649,38 @@ void QQuickPropertyAnimation::setProperties(const QString &prop)
QQmlListProperty<QObject> QQuickPropertyAnimation::targets()
{
Q_D(QQuickPropertyAnimation);
- return QQmlListProperty<QObject>(this, &(d->targets));
+ using ListPtr = QList<QPointer<QObject>> *;
+ using LP = QQmlListProperty<QObject>;
+ LP::AppendFunction appendFn = [](LP *prop, QObject *value)
+ {
+ static_cast<ListPtr>(prop->data)->append(value);
+ };
+ LP::CountFunction countFn = [](LP *prop)
+ {
+ return static_cast<ListPtr>(prop->data)->size();
+ };
+
+ LP::AtFunction atFn = [](LP *prop, qsizetype index) -> QObject *
+ {
+ return static_cast<ListPtr>(prop->data)->at(index);
+ };
+
+ LP::ClearFunction clearFN = [](LP *prop)
+ {
+ return static_cast<ListPtr>(prop->data)->clear();
+ };
+
+ LP::ReplaceFunction replaceFn = [](LP *prop, qsizetype index, QObject *value)
+ {
+ static_cast<ListPtr>(prop->data)->replace(index, value);
+ };
+
+ LP::RemoveLastFunction removeLastFn = [](LP *prop)
+ {
+ static_cast<ListPtr>(prop->data)->removeLast();
+ };
+
+ return QQmlListProperty<QObject>(this, &(d->targets), appendFn, countFn, atFn, clearFN, replaceFn, removeLastFn);
}
/*!
@@ -2646,7 +2752,7 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
if (!d->propertyName.isEmpty())
props << d->propertyName;
- QList<QObject*> targets = d->targets;
+ QList<QPointer<QObject>> targets = d->targets;
if (d->target)
targets.append(d->target);
@@ -2675,10 +2781,14 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
for (int i = 0; i < props.size(); ++i) {
for (int j = 0; j < targets.size(); ++j) {
+ const auto& guarded = targets.at(j);
+ if (guarded.isNull())
+ continue;
+ QObject *target = guarded.get();
QQuickStateAction myAction;
QString errorMessage;
const QString &propertyName = props.at(i);
- myAction.property = d->createProperty(targets.at(j), propertyName, this, &errorMessage);
+ myAction.property = d->createProperty(target, propertyName, this, &errorMessage);
if (myAction.property.isValid()) {
if (usingDefaultProperties)
successfullyCreatedDefaultProperty = true;
diff --git a/src/quick/util/qquickanimation_p.h b/src/quick/util/qquickanimation_p.h
index 14f738ea7f..61e52869f5 100644
--- a/src/quick/util/qquickanimation_p.h
+++ b/src/quick/util/qquickanimation_p.h
@@ -31,7 +31,7 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractAnimationPrivate;
class QQuickAnimationGroup;
-class Q_QUICK_PRIVATE_EXPORT QQuickAbstractAnimation : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus
+class Q_QUICK_EXPORT QQuickAbstractAnimation : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAbstractAnimation)
@@ -125,7 +125,7 @@ private:
};
class QQuickPauseAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPauseAnimation : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickPauseAnimation : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPauseAnimation)
@@ -152,7 +152,7 @@ protected:
};
class QQuickScriptActionPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickScriptAction : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickScriptAction : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickScriptAction)
@@ -180,7 +180,7 @@ protected:
};
class QQuickPropertyActionPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPropertyAction : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickPropertyAction : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPropertyAction)
@@ -227,7 +227,7 @@ protected:
};
class QQuickPropertyAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPropertyAnimation : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickPropertyAnimation : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPropertyAnimation)
@@ -292,7 +292,7 @@ Q_SIGNALS:
void propertyChanged();
};
-class Q_QUICK_PRIVATE_EXPORT QQuickColorAnimation : public QQuickPropertyAnimation
+class Q_QUICK_EXPORT QQuickColorAnimation : public QQuickPropertyAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPropertyAnimation)
@@ -312,7 +312,7 @@ public:
void setTo(const QColor &);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickNumberAnimation : public QQuickPropertyAnimation
+class Q_QUICK_EXPORT QQuickNumberAnimation : public QQuickPropertyAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPropertyAnimation)
@@ -339,7 +339,7 @@ private:
void init();
};
-class Q_QUICK_PRIVATE_EXPORT QQuickVector3dAnimation : public QQuickPropertyAnimation
+class Q_QUICK_EXPORT QQuickVector3dAnimation : public QQuickPropertyAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPropertyAnimation)
@@ -361,7 +361,7 @@ public:
};
class QQuickRotationAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRotationAnimation : public QQuickPropertyAnimation
+class Q_QUICK_EXPORT QQuickRotationAnimation : public QQuickPropertyAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickRotationAnimation)
@@ -392,7 +392,7 @@ Q_SIGNALS:
};
class QQuickAnimationGroupPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimationGroup : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickAnimationGroup : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnimationGroup)
@@ -411,7 +411,7 @@ protected:
QQuickAnimationGroup(QQuickAnimationGroupPrivate &dd, QObject *parent);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickSequentialAnimation : public QQuickAnimationGroup
+class Q_QUICK_EXPORT QQuickSequentialAnimation : public QQuickAnimationGroup
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnimationGroup)
@@ -430,7 +430,7 @@ protected:
QObject *defaultTarget = nullptr) override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickParallelAnimation : public QQuickAnimationGroup
+class Q_QUICK_EXPORT QQuickParallelAnimation : public QQuickAnimationGroup
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnimationGroup)
@@ -452,16 +452,4 @@ protected:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAbstractAnimation)
-QML_DECLARE_TYPE(QQuickPauseAnimation)
-QML_DECLARE_TYPE(QQuickScriptAction)
-QML_DECLARE_TYPE(QQuickPropertyAction)
-QML_DECLARE_TYPE(QQuickPropertyAnimation)
-QML_DECLARE_TYPE(QQuickColorAnimation)
-QML_DECLARE_TYPE(QQuickNumberAnimation)
-QML_DECLARE_TYPE(QQuickSequentialAnimation)
-QML_DECLARE_TYPE(QQuickParallelAnimation)
-QML_DECLARE_TYPE(QQuickVector3dAnimation)
-QML_DECLARE_TYPE(QQuickRotationAnimation)
-
#endif // QQUICKANIMATION_H
diff --git a/src/quick/util/qquickanimation_p_p.h b/src/quick/util/qquickanimation_p_p.h
index a12a35f070..49f26726ee 100644
--- a/src/quick/util/qquickanimation_p_p.h
+++ b/src/quick/util/qquickanimation_p_p.h
@@ -31,7 +31,7 @@
#include <QDebug>
#include <private/qobject_p.h>
-
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -134,7 +134,7 @@ private:
T *m_instance;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickAbstractAnimationPrivate : public QObjectPrivate, public QAnimationJobChangeListener
+class Q_QUICK_EXPORT QQuickAbstractAnimationPrivate : public QObjectPrivate, public QAnimationJobChangeListener
{
Q_DECLARE_PUBLIC(QQuickAbstractAnimation)
public:
@@ -164,6 +164,7 @@ public:
QAbstractAnimationJob* animationInstance;
static QQmlProperty createProperty(QObject *obj, const QString &str, QObject *infoObj, QString *errorMessage = nullptr);
+ void animationGroupDirty();
};
class QQuickPauseAnimationPrivate : public QQuickAbstractAnimationPrivate
@@ -217,7 +218,7 @@ class QQuickAnimationGroupPrivate : public QQuickAbstractAnimationPrivate
Q_DECLARE_PUBLIC(QQuickAnimationGroup)
public:
QQuickAnimationGroupPrivate()
- : QQuickAbstractAnimationPrivate() {}
+ : QQuickAbstractAnimationPrivate(), animationDirty(false) {}
static void append_animation(QQmlListProperty<QQuickAbstractAnimation> *list, QQuickAbstractAnimation *role);
static QQuickAbstractAnimation *at_animation(QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype index);
@@ -227,9 +228,13 @@ public:
QQuickAbstractAnimation *role);
static void removeLast_animation(QQmlListProperty<QQuickAbstractAnimation> *list);
QList<QQuickAbstractAnimation *> animations;
+
+ void restartFromCurrentLoop();
+ void animationCurrentLoopChanged(QAbstractAnimationJob *job) override;
+ bool animationDirty: 1;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPropertyAnimationPrivate : public QQuickAbstractAnimationPrivate
+class Q_QUICK_EXPORT QQuickPropertyAnimationPrivate : public QQuickAbstractAnimationPrivate
{
Q_DECLARE_PUBLIC(QQuickPropertyAnimation)
public:
@@ -245,7 +250,7 @@ public:
QObject *target;
QString propertyName;
QString properties;
- QList<QObject *> targets;
+ QList<QPointer<QObject>> targets;
QList<QObject *> exclude;
QString defaultProperties;
diff --git a/src/quick/util/qquickanimationcontroller_p.h b/src/quick/util/qquickanimationcontroller_p.h
index 8150fb9709..6ce1fcc7a3 100644
--- a/src/quick/util/qquickanimationcontroller_p.h
+++ b/src/quick/util/qquickanimationcontroller_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
class QQuickAnimationControllerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimationController : public QObject, public QQmlFinalizerHook
+class Q_QUICK_EXPORT QQuickAnimationController : public QObject, public QQmlFinalizerHook
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QQuickAnimationController)
@@ -60,6 +60,4 @@ private Q_SLOTS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAnimationController)
-
#endif // QQUICKANIMATIONCONTROLLER_H
diff --git a/src/quick/util/qquickanimator.cpp b/src/quick/util/qquickanimator.cpp
index 7b73e9e59b..986f695fef 100644
--- a/src/quick/util/qquickanimator.cpp
+++ b/src/quick/util/qquickanimator.cpp
@@ -247,7 +247,8 @@ QAbstractAnimationJob *QQuickAnimator::transition(QQuickStateActions &actions,
Q_D(QQuickAnimator);
if (d->defaultProperty.isValid() && propertyName() != d->defaultProperty.name()) {
- qDebug() << Q_FUNC_INFO << "property name conflict...";
+ qmlWarning(this) << "property name conflict: \""
+ << propertyName() << "\" != \"" << d->defaultProperty.name() << "\"";
return nullptr;
}
@@ -448,14 +449,16 @@ QQuickAnimatorJob *QQuickRotationAnimator::createJob() const {
Possible values are:
- \list
- \li RotationAnimator.Numerical (default) - Rotate by linearly interpolating between the two numbers.
- A rotation from 10 to 350 will rotate 340 degrees clockwise.
- \li RotationAnimator.Clockwise - Rotate clockwise between the two values
- \li RotationAnimator.Counterclockwise - Rotate counterclockwise between the two values
- \li RotationAnimator.Shortest - Rotate in the direction that produces the shortest animation path.
- A rotation from 10 to 350 will rotate 20 degrees counterclockwise.
- \endlist
+ \value RotationAnimator.Numerical
+ (default) Rotate by linearly interpolating between the two numbers.
+ A rotation from 10 to 350 will rotate 340 degrees clockwise.
+ \value RotationAnimator.Clockwise
+ Rotate clockwise between the two values
+ \value RotationAnimator.Counterclockwise
+ Rotate counterclockwise between the two values
+ \value RotationAnimator.Shortest
+ Rotate in the direction that produces the shortest animation path.
+ A rotation from 10 to 350 will rotate 20 degrees counterclockwise.
*/
void QQuickRotationAnimator::setDirection(RotationDirection dir)
{
diff --git a/src/quick/util/qquickanimator_p.h b/src/quick/util/qquickanimator_p.h
index 0dd5738464..fc864359c7 100644
--- a/src/quick/util/qquickanimator_p.h
+++ b/src/quick/util/qquickanimator_p.h
@@ -23,7 +23,7 @@ class QQuickItem;
class QQuickAnimatorJob;
class QQuickAnimatorPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimator : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickAnimator : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnimator)
@@ -73,7 +73,7 @@ Q_SIGNALS:
void fromChanged(qreal from);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickScaleAnimator : public QQuickAnimator
+class Q_QUICK_EXPORT QQuickScaleAnimator : public QQuickAnimator
{
Q_OBJECT
QML_NAMED_ELEMENT(ScaleAnimator)
@@ -85,7 +85,7 @@ protected:
QString propertyName() const override { return QStringLiteral("scale"); }
};
-class Q_QUICK_PRIVATE_EXPORT QQuickXAnimator : public QQuickAnimator
+class Q_QUICK_EXPORT QQuickXAnimator : public QQuickAnimator
{
Q_OBJECT
QML_NAMED_ELEMENT(XAnimator)
@@ -97,7 +97,7 @@ protected:
QString propertyName() const override { return QStringLiteral("x"); }
};
-class Q_QUICK_PRIVATE_EXPORT QQuickYAnimator : public QQuickAnimator
+class Q_QUICK_EXPORT QQuickYAnimator : public QQuickAnimator
{
Q_OBJECT
QML_NAMED_ELEMENT(YAnimator)
@@ -109,7 +109,7 @@ protected:
QString propertyName() const override { return QStringLiteral("y"); }
};
-class Q_QUICK_PRIVATE_EXPORT QQuickOpacityAnimator : public QQuickAnimator
+class Q_QUICK_EXPORT QQuickOpacityAnimator : public QQuickAnimator
{
Q_OBJECT
QML_NAMED_ELEMENT(OpacityAnimator)
@@ -122,7 +122,7 @@ protected:
};
class QQuickRotationAnimatorPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRotationAnimator : public QQuickAnimator
+class Q_QUICK_EXPORT QQuickRotationAnimator : public QQuickAnimator
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickRotationAnimator)
@@ -149,7 +149,7 @@ protected:
#if QT_CONFIG(quick_shadereffect)
class QQuickUniformAnimatorPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickUniformAnimator : public QQuickAnimator
+class Q_QUICK_EXPORT QQuickUniformAnimator : public QQuickAnimator
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickUniformAnimator)
@@ -174,13 +174,4 @@ protected:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAnimator)
-QML_DECLARE_TYPE(QQuickXAnimator)
-QML_DECLARE_TYPE(QQuickYAnimator)
-QML_DECLARE_TYPE(QQuickScaleAnimator)
-QML_DECLARE_TYPE(QQuickRotationAnimator)
-QML_DECLARE_TYPE(QQuickOpacityAnimator)
-#if QT_CONFIG(quick_shadereffect)
-QML_DECLARE_TYPE(QQuickUniformAnimator)
-#endif
#endif // QQUICKANIMATOR_P_H
diff --git a/src/quick/util/qquickanimator_p_p.h b/src/quick/util/qquickanimator_p_p.h
index 29dd34a8e8..d138fc4f72 100644
--- a/src/quick/util/qquickanimator_p_p.h
+++ b/src/quick/util/qquickanimator_p_p.h
@@ -19,6 +19,8 @@
#include "qquickanimation_p_p.h"
#include <QtQuick/qquickitem.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickAnimatorJob;
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index 1ac87b6b74..83d5181c34 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -592,7 +592,7 @@ void QQuickUniformAnimatorJob::setTarget(QQuickItem *target)
void QQuickUniformAnimatorJob::updateCurrentTime(int time)
{
- if (!m_effect)
+ if (!m_effect || m_target != m_effect)
return;
m_value = m_from + (m_to - m_from) * progress(time);
@@ -617,7 +617,7 @@ void QQuickUniformAnimatorJob::postSync()
void QQuickUniformAnimatorJob::invalidate()
{
- m_effect = nullptr;
+
}
#endif
diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h
index fef29ac385..16e4a7063d 100644
--- a/src/quick/util/qquickanimatorjob_p.h
+++ b/src/quick/util/qquickanimatorjob_p.h
@@ -23,6 +23,7 @@
#include <QtQuick/qquickitem.h>
#include <QtCore/qeasingcurve.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -35,7 +36,7 @@ class QQuickAnimatorController;
class QSGOpacityNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimatorProxyJob : public QObject, public QAbstractAnimationJob
+class Q_QUICK_EXPORT QQuickAnimatorProxyJob : public QObject, public QAbstractAnimationJob
{
Q_OBJECT
@@ -77,7 +78,7 @@ private:
InternalState m_internalState;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimatorJob : public QAbstractAnimationJob
+class Q_QUICK_EXPORT QQuickAnimatorJob : public QAbstractAnimationJob
{
public:
virtual void setTarget(QQuickItem *target);
@@ -209,28 +210,28 @@ protected:
Helper *m_helper;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickScaleAnimatorJob : public QQuickTransformAnimatorJob
+class Q_QUICK_EXPORT QQuickScaleAnimatorJob : public QQuickTransformAnimatorJob
{
public:
void updateCurrentTime(int time) override;
void writeBack() override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickXAnimatorJob : public QQuickTransformAnimatorJob
+class Q_QUICK_EXPORT QQuickXAnimatorJob : public QQuickTransformAnimatorJob
{
public:
void updateCurrentTime(int time) override;
void writeBack() override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickYAnimatorJob : public QQuickTransformAnimatorJob
+class Q_QUICK_EXPORT QQuickYAnimatorJob : public QQuickTransformAnimatorJob
{
public:
void updateCurrentTime(int time) override;
void writeBack() override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickRotationAnimatorJob : public QQuickTransformAnimatorJob
+class Q_QUICK_EXPORT QQuickRotationAnimatorJob : public QQuickTransformAnimatorJob
{
public:
QQuickRotationAnimatorJob();
@@ -245,7 +246,7 @@ private:
QQuickRotationAnimator::RotationDirection m_direction;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickOpacityAnimatorJob : public QQuickAnimatorJob
+class Q_QUICK_EXPORT QQuickOpacityAnimatorJob : public QQuickAnimatorJob
{
public:
QQuickOpacityAnimatorJob();
@@ -262,7 +263,7 @@ private:
#if QT_CONFIG(quick_shadereffect)
class QQuickShaderEffect;
-class Q_QUICK_PRIVATE_EXPORT QQuickUniformAnimatorJob : public QQuickAnimatorJob
+class Q_QUICK_EXPORT QQuickUniformAnimatorJob : public QQuickAnimatorJob
{
public:
QQuickUniformAnimatorJob();
@@ -280,7 +281,7 @@ public:
private:
QByteArray m_uniform;
- QQuickShaderEffect *m_effect = nullptr;
+ QPointer<QQuickShaderEffect> m_effect;
};
#endif
diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp
index bee3b7313f..2cb8fe36ba 100644
--- a/src/quick/util/qquickapplication.cpp
+++ b/src/quick/util/qquickapplication.cpp
@@ -225,6 +225,7 @@ QQuickApplication::QQuickApplication(QObject *parent)
connect(guiApp, &QGuiApplication::applicationDisplayNameChanged,
this, &QQuickApplication::displayNameChanged);
+ connect(guiApp, &QGuiApplication::primaryScreenChanged, this, &QQuickApplication::updateScreens);
connect(guiApp, &QGuiApplication::screenAdded, this, &QQuickApplication::updateScreens);
connect(guiApp, &QGuiApplication::screenRemoved, this, &QQuickApplication::updateScreens);
updateScreens();
diff --git a/src/quick/util/qquickapplication_p.h b/src/quick/util/qquickapplication_p.h
index 680e25063d..540411756b 100644
--- a/src/quick/util/qquickapplication_p.h
+++ b/src/quick/util/qquickapplication_p.h
@@ -28,17 +28,17 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickApplication : public QQmlApplication
+class Q_QUICK_EXPORT QQuickApplication : public QQmlApplication
{
Q_OBJECT
- Q_PROPERTY(bool active READ active NOTIFY activeChanged) // deprecated, use 'state' instead
- Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection NOTIFY layoutDirectionChanged)
- Q_PROPERTY(bool supportsMultipleWindows READ supportsMultipleWindows CONSTANT)
- Q_PROPERTY(Qt::ApplicationState state READ state NOTIFY stateChanged)
- Q_PROPERTY(QFont font READ font CONSTANT)
- Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName NOTIFY displayNameChanged)
- Q_PROPERTY(QQmlListProperty<QQuickScreenInfo> screens READ screens NOTIFY screensChanged)
- Q_PROPERTY(QStyleHints *styleHints READ styleHints CONSTANT)
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged FINAL) // deprecated, use 'state' instead
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection NOTIFY layoutDirectionChanged FINAL)
+ Q_PROPERTY(bool supportsMultipleWindows READ supportsMultipleWindows CONSTANT FINAL)
+ Q_PROPERTY(Qt::ApplicationState state READ state NOTIFY stateChanged FINAL)
+ Q_PROPERTY(QFont font READ font CONSTANT FINAL)
+ Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName NOTIFY displayNameChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QQuickScreenInfo> screens READ screens NOTIFY screensChanged FINAL)
+ Q_PROPERTY(QStyleHints *styleHints READ styleHints CONSTANT FINAL)
QML_NAMED_ELEMENT(Application)
QML_SINGLETON
@@ -74,6 +74,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickApplication)
-
#endif // QQUICKAPPLICATION_P_H
diff --git a/src/quick/util/qquickbehavior.cpp b/src/quick/util/qquickbehavior.cpp
index 2522db37eb..dd8ddad7ca 100644
--- a/src/quick/util/qquickbehavior.cpp
+++ b/src/quick/util/qquickbehavior.cpp
@@ -14,6 +14,7 @@
#include <private/qquickanimatorjob_p.h>
#include <private/qobject_p.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -151,7 +152,7 @@ public:
UntypedProxyProperty::UntypedProxyProperty(QUntypedBindable bindable, QQuickBehaviorPrivate *behavior) :
m_sourcePropertyData(QUntypedBindablePrivate::getPropertyData(bindable)),
m_sourceInterface(QUntypedBindablePrivate::getInterface(bindable)),
- m_storage(QVariant(QUntypedBindablePrivate::getInterface(bindable)->metaType()))
+ m_storage(QVariant(bindable.metaType()))
{
behavior->setSource(m_bindingData);
}
@@ -182,7 +183,7 @@ UntypedProxyProperty::UntypedProxyProperty(QUntypedBindable bindable, QQuickBeha
state change. For general advice on using Behaviors to animate state changes, see
\l{Using Qt Quick Behaviors with States}.
- \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#Behaviors}{Behavior example}, {Qt QML}
+ \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#Behaviors}{Behavior example}, {Qt Qml}
*/
@@ -406,7 +407,7 @@ void QQuickBehavior::write(const QVariant &value)
QList<QQmlProperty> after;
auto *newInstance = d->animation->transition(actions, after, QQuickAbstractAnimation::Forward);
- Q_ASSERT(newInstance != d->animationInstance);
+ Q_ASSERT(!newInstance || newInstance != d->animationInstance);
delete d->animationInstance;
d->animationInstance = newInstance;
diff --git a/src/quick/util/qquickbehavior_p.h b/src/quick/util/qquickbehavior_p.h
index 416417b178..f9b34eee19 100644
--- a/src/quick/util/qquickbehavior_p.h
+++ b/src/quick/util/qquickbehavior_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractAnimation;
class QQuickBehaviorPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickBehavior : public QObject, public QQmlPropertyValueInterceptor, public QQmlFinalizerHook
+class Q_QUICK_EXPORT QQuickBehavior : public QObject, public QQmlPropertyValueInterceptor, public QQmlFinalizerHook
{
Q_OBJECT
Q_INTERFACES(QQmlFinalizerHook)
@@ -70,6 +70,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickBehavior)
-
#endif // QQUICKBEHAVIOR_H
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index 794ac70a25..34032d4801 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -18,6 +18,8 @@
#include <QtQuick/private/qquickrendercontrol_p.h>
#include <QtQuick/private/qquickwindow_p.h>
+#include <QtCore/qpointer.h>
+
#include <memory>
QT_BEGIN_NAMESPACE
@@ -96,6 +98,14 @@ bool QQuickDeliveryAgentPrivate::checkIfDoubleTapped(ulong newPressEventTimestam
return doubleClicked;
}
+/*! \internal
+ \deprecated events are handled by methods in which the event is an argument
+
+ Accessor for use by legacy methods such as QQuickItem::grabMouse(),
+ QQuickItem::ungrabMouse(), and QQuickItem::grabTouchPoints() which
+ are not given sufficient context to do the grabbing.
+ We should remove eventsInDelivery in Qt 7.
+*/
QPointerEvent *QQuickDeliveryAgentPrivate::eventInDelivery() const
{
if (eventsInDelivery.isEmpty())
@@ -305,6 +315,16 @@ static inline bool windowHasFocus(QQuickWindow *win)
return win == focusWindow || QQuickRenderControlPrivate::isRenderWindowFor(win, focusWindow) || !focusWindow;
}
+static QQuickItem *findFurthestFocusScopeAncestor(QQuickItem *item)
+{
+ QQuickItem *parentItem = item->parentItem();
+
+ if (parentItem && parentItem->flags() & QQuickItem::ItemIsFocusScope)
+ return findFurthestFocusScopeAncestor(parentItem);
+
+ return item;
+}
+
#ifdef Q_OS_WEBOS
// Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
static inline bool singleWindowOnScreen(QQuickWindow *win)
@@ -447,6 +467,16 @@ void QQuickDeliveryAgentPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *
if (isSubsceneAgent) {
auto da = QQuickWindowPrivate::get(rootItem->window())->deliveryAgent;
qCDebug(lcFocus) << " delegating setFocusInScope to" << da;
+
+ // When setting subFocusItem, hierarchy is important. Each focus ancestor's
+ // subFocusItem must be its nearest descendant with focus. Changing the rootItem's
+ // subFocusItem to 'item' here would make 'item' the subFocusItem of all ancestor
+ // focus scopes up until root item.
+ // That is why we should avoid altering subFocusItem until having traversed
+ // all the focus hierarchy.
+ QQuickItem *ancestorFS = findFurthestFocusScopeAncestor(item);
+ if (ancestorFS != item)
+ options |= QQuickDeliveryAgentPrivate::DontChangeSubFocusItem;
QQuickWindowPrivate::get(rootItem->window())->deliveryAgentPrivate()->setFocusInScope(da->rootItem(), item, reason, options);
}
if (oldActiveFocusItem == activeFocusItem)
@@ -599,16 +629,14 @@ bool QQuickDeliveryAgentPrivate::clearHover(ulong timestamp)
const QPointF lastPos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
const auto modifiers = QGuiApplication::keyboardModifiers();
- const bool clearHover = true;
- for (auto hoverItem : hoverItems) {
- auto item = hoverItem.first;
- if (item)
- deliverHoverEventToItem(item, lastPos, lastPos, modifiers, timestamp, clearHover);
+ for (const auto &[item, id] : hoverItems) {
+ if (item) {
+ deliverHoverEventToItem(item, lastPos, lastPos, modifiers, timestamp, HoverChange::Clear);
+ Q_ASSERT(id == 0);
+ }
}
- hoverItems.clear();
-
return true;
}
@@ -624,6 +652,27 @@ void QQuickDeliveryAgentPrivate::updateFocusItemTransform()
#endif
}
+/*!
+ Returns the item that should get active focus when the
+ root focus scope gets active focus.
+*/
+QQuickItem *QQuickDeliveryAgentPrivate::focusTargetItem() const
+{
+ if (activeFocusItem)
+ return activeFocusItem;
+
+ Q_ASSERT(rootItem);
+ QQuickItem *targetItem = rootItem;
+
+ while (targetItem->isFocusScope()
+ && targetItem->scopedFocusItem()
+ && targetItem->scopedFocusItem()->isEnabled()) {
+ targetItem = targetItem->scopedFocusItem();
+ }
+
+ return targetItem;
+}
+
/*! \internal
If called during event delivery, returns the agent that is delivering the
event, without checking whether \a item is reachable from there.
@@ -657,6 +706,10 @@ QQuickDeliveryAgent::Transform::~Transform()
{
}
+/*! \internal
+ Get the QQuickRootItem or subscene root item on behalf of which
+ this delivery agent was constructed to handle events.
+*/
QQuickItem *QQuickDeliveryAgent::rootItem() const
{
Q_D(const QQuickDeliveryAgent);
@@ -690,6 +743,13 @@ void QQuickDeliveryAgent::setSceneTransform(QQuickDeliveryAgent::Transform *tran
d->sceneTransform = transform;
}
+/*!
+ Handle \a ev on behalf of this delivery agent's window or subscene.
+
+ This is the usual main entry point for every incoming event:
+ QQuickWindow::event() and QQuick3DViewport::forwardEventToSubscenes()
+ both call this function.
+*/
bool QQuickDeliveryAgent::event(QEvent *ev)
{
Q_D(QQuickDeliveryAgent);
@@ -782,16 +842,7 @@ bool QQuickDeliveryAgent::event(QEvent *ev)
case QEvent::InputMethod:
case QEvent::InputMethodQuery:
{
- QQuickItem *target = d->activeFocusItem;
- // while an input method delivers the event, this window might still be inactive
- if (!target) {
- target = d->rootItem;
- if (!target || !target->isEnabled())
- break;
- // see setFocusInScope for a similar loop
- while (target->isFocusScope() && target->scopedFocusItem() && target->scopedFocusItem()->isEnabled())
- target = target->scopedFocusItem();
- }
+ QQuickItem *target = d->focusTargetItem();
if (target)
QCoreApplication::sendEvent(target, ev);
}
@@ -1018,8 +1069,7 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent(
// Prune the list for items that are no longer hovered
for (auto it = hoverItems.begin(); it != hoverItems.end();) {
- auto item = (*it).first.data();
- auto hoverId = (*it).second;
+ const auto &[item, hoverId] = *it;
if (hoverId == currentHoverId) {
// Still being hovered
it++;
@@ -1027,10 +1077,8 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent(
// No longer hovered. If hoverId is 0, it means that we have sent a HoverLeave
// event to the item already, and it can just be removed from the list. Note that
// the item can have been deleted as well.
- if (item && hoverId != 0) {
- const bool clearHover = true;
- deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, clearHover);
- }
+ if (item && hoverId != 0)
+ deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, HoverChange::Clear);
it = hoverItems.erase(it);
}
}
@@ -1111,10 +1159,7 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventRecursive(
// All decendants have been visited.
// Now deliver the event to the item
- return deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, false);
-
- // Continue propagation / recursion
- return false;
+ return deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, HoverChange::Set);
}
/*! \internal
@@ -1127,13 +1172,14 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventRecursive(
*/
bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
- Qt::KeyboardModifiers modifiers, ulong timestamp, bool clearHover)
+ Qt::KeyboardModifiers modifiers, ulong timestamp, HoverChange hoverChange)
{
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
const QPointF localPos = item->mapFromScene(scenePos);
const QPointF globalPos = item->mapToGlobal(localPos);
const bool isHovering = item->contains(localPos);
- const bool wasHovering = hoverItems.contains(item);
+ const auto hoverItemIterator = hoverItems.find(item);
+ const bool wasHovering = hoverItemIterator != hoverItems.end() && hoverItemIterator.value() != 0;
qCDebug(lcHoverTrace) << "item:" << item << "scene pos:" << scenePos << "localPos:" << localPos
<< "wasHovering:" << wasHovering << "isHovering:" << isHovering;
@@ -1143,20 +1189,24 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
// Start by sending out enter/move/leave events to the item.
// Note that hoverEnabled only controls if we should send out hover events to the
// item itself. HoverHandlers are not included, and are dealt with separately below.
- if (itemPrivate->hoverEnabled && isHovering && !clearHover) {
+ if (itemPrivate->hoverEnabled && isHovering && hoverChange == HoverChange::Set) {
// Add the item to the list of hovered items (if it doesn't exist there
// from before), and update hoverId to mark that it's (still) hovered.
// Also set hoveredLeafItemFound, so that only propagate in a straight
// line towards the root from now on.
hoveredLeafItemFound = true;
- hoverItems[item] = currentHoverId;
+ if (hoverItemIterator != hoverItems.end())
+ hoverItemIterator.value() = currentHoverId;
+ else
+ hoverItems[item] = currentHoverId;
+
if (wasHovering)
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp);
else
accepted = sendHoverEvent(QEvent::HoverEnter, item, scenePos, lastScenePos, modifiers, timestamp);
} else if (wasHovering) {
// A leave should never stop propagation
- hoverItems[item] = 0;
+ hoverItemIterator.value() = 0;
sendHoverEvent(QEvent::HoverLeave, item, scenePos, lastScenePos, modifiers, timestamp);
}
@@ -1170,7 +1220,7 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
// Note that since a HoverHandler can have a margin, a HoverHandler
// can be hovered even if the item itself is not.
- if (clearHover) {
+ if (hoverChange == HoverChange::Clear) {
// Note: a leave should never stop propagation
QHoverEvent hoverEvent(QEvent::HoverLeave, scenePos, globalPos, lastScenePos, modifiers);
hoverEvent.setTimestamp(timestamp);
@@ -1197,7 +1247,10 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
// Mark the whole item as updated, even if only the handler is
// actually in a hovered state (because of HoverHandler.margins)
hoveredLeafItemFound = true;
- hoverItems[item] = currentHoverId;
+ if (hoverItemIterator != hoverItems.end())
+ hoverItemIterator.value() = currentHoverId;
+ else
+ hoverItems[item] = currentHoverId;
if (hh->isBlocking()) {
qCDebug(lcHoverTrace) << "skipping rest of hover delivery due to blocking" << hh;
accepted = true;
@@ -1321,6 +1374,13 @@ void QQuickDeliveryAgentPrivate::handleWindowDeactivate(QQuickWindow *win)
}
}
+void QQuickDeliveryAgentPrivate::handleWindowHidden(QQuickWindow *win)
+{
+ qCDebug(lcFocus) << "hidden" << win->title();
+ clearHover();
+ lastMousePosition = QPointF();
+}
+
bool QQuickDeliveryAgentPrivate::allUpdatedPointsAccepted(const QPointerEvent *ev)
{
for (auto &point : ev->points()) {
@@ -1595,6 +1655,9 @@ void QQuickDeliveryAgentPrivate::handleTouchEvent(QTouchEvent *event)
}
}
+/*!
+ Handle \a event on behalf of this delivery agent's window or subscene.
+*/
void QQuickDeliveryAgentPrivate::handleMouseEvent(QMouseEvent *event)
{
Q_Q(QQuickDeliveryAgent);
@@ -1651,6 +1714,19 @@ void QQuickDeliveryAgentPrivate::handleMouseEvent(QMouseEvent *event)
}
}
+/*! \internal
+ Flush events before a frame is rendered in \a win.
+
+ This is here because of compressTouchEvent(): we need to ensure that
+ coalesced touch events are actually delivered in time to cause the desired
+ reactions of items and their handlers. And then since it was introduced
+ because of that, we started using this function for once-per-frame hover
+ events too, to take care of changing hover state when an item animates
+ under the mouse cursor at a time that the mouse cursor is not moving.
+
+ This is done before QQuickItem::updatePolish() is called on all the items
+ that requested polishing.
+*/
void QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(QQuickWindow *win)
{
Q_Q(QQuickDeliveryAgent);
@@ -1679,7 +1755,13 @@ void QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(QQuickWindow *win)
if (frameSynchronousHoverEnabled && !win->mouseGrabberItem() &&
!lastMousePosition.isNull() && QQuickWindowPrivate::get(win)->dirtyItemList) {
qCDebug(lcHoverTrace) << q << "delivering frame-sync hover to root @" << lastMousePosition;
- deliverHoverEvent(lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0);
+ if (deliverHoverEvent(lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0)) {
+#if QT_CONFIG(cursor)
+ QQuickWindowPrivate::get(rootItem->window())->updateCursor(
+ sceneTransform ? sceneTransform->map(lastMousePosition) : lastMousePosition, rootItem);
+#endif
+ }
+
qCDebug(lcHoverTrace) << q << "frame-sync hover delivery done";
}
#else
@@ -1691,6 +1773,14 @@ void QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(QQuickWindow *win)
QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = deliveringAgent;
}
+/*! \internal
+ React to the fact that \a grabber underwent a grab \a transition
+ while an item or handler was handling \a point from \a event.
+ I.e. handle the QPointingDevice::grabChanged() signal.
+
+ This notifies the relevant items and/or pointer handlers, and
+ does cleanup when grabs are lost or relinquished.
+*/
void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition,
const QPointerEvent *event, const QEventPoint &point)
{
@@ -1719,50 +1809,43 @@ void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice
handler->onGrabChanged(handler, transition, const_cast<QPointerEvent *>(event),
const_cast<QEventPoint &>(point));
}
- } else {
+ } else if (auto *grabberItem = qmlobject_cast<QQuickItem *>(grabber)) {
switch (transition) {
case QPointingDevice::CancelGrabExclusive:
case QPointingDevice::UngrabExclusive:
- if (auto *item = qmlobject_cast<QQuickItem *>(grabber)) {
- bool filtered = false;
- if (isDeliveringTouchAsMouse() ||
- point.device()->type() == QInputDevice::DeviceType::Mouse ||
- point.device()->type() == QInputDevice::DeviceType::TouchPad) {
- QMutableSinglePointEvent e(QEvent::UngrabMouse, point.device(), point);
- hasFiltered.clear();
- filtered = sendFilteredMouseEvent(&e, item, item->parentItem());
- if (!filtered) {
- lastUngrabbed = item;
- item->mouseUngrabEvent();
- }
+ if (isDeliveringTouchAsMouse()
+ || point.device()->type() == QInputDevice::DeviceType::Mouse
+ || point.device()->type() == QInputDevice::DeviceType::TouchPad) {
+ QMutableSinglePointEvent e(QEvent::UngrabMouse, point.device(), point);
+ hasFiltered.clear();
+ if (!sendFilteredMouseEvent(&e, grabberItem, grabberItem->parentItem())) {
+ lastUngrabbed = grabberItem;
+ grabberItem->mouseUngrabEvent();
}
- if (point.device()->type() == QInputDevice::DeviceType::TouchScreen) {
- bool allReleasedOrCancelled = true;
- if (transition == QPointingDevice::UngrabExclusive && event) {
- for (const auto &pt : event->points()) {
- if (pt.state() != QEventPoint::State::Released) {
- allReleasedOrCancelled = false;
- break;
- }
+ }
+ if (point.device()->type() == QInputDevice::DeviceType::TouchScreen) {
+ bool allReleasedOrCancelled = true;
+ if (transition == QPointingDevice::UngrabExclusive && event) {
+ for (const auto &pt : event->points()) {
+ if (pt.state() != QEventPoint::State::Released) {
+ allReleasedOrCancelled = false;
+ break;
}
}
- if (allReleasedOrCancelled)
- item->touchUngrabEvent();
}
+ if (allReleasedOrCancelled)
+ grabberItem->touchUngrabEvent();
}
break;
default:
break;
}
- auto grabberItem = static_cast<QQuickItem *>(grabber); // cannot be a handler: we checked above
- if (grabberItem) {
- auto itemPriv = QQuickItemPrivate::get(grabberItem);
- deliveryAgent = itemPriv->deliveryAgent();
- // An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
- // whereas the subscene root item already knows it has its own DA.
- if (isSubsceneAgent && grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
- itemPriv->maybeHasSubsceneDeliveryAgent = true;
- }
+ auto *itemPriv = QQuickItemPrivate::get(grabberItem);
+ deliveryAgent = itemPriv->deliveryAgent();
+ // An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
+ // whereas the subscene root item already knows it has its own DA.
+ if (isSubsceneAgent && grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
+ itemPriv->maybeHasSubsceneDeliveryAgent = true;
}
if (currentEventDeliveryAgent == q && event && event->device()) {
@@ -1794,6 +1877,15 @@ void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice
}
}
+/*! \internal
+ Called when a QPointingDevice is detected, to ensure that the
+ QPointingDevice::grabChanged() signal is connected to
+ QQuickDeliveryAgentPrivate::onGrabChanged().
+
+ \c knownPointingDevices is maintained only to track signal connections, and
+ should not be used for other purposes. The usual place to get a list of all
+ devices is QInputDevice::devices().
+*/
void QQuickDeliveryAgentPrivate::ensureDeviceConnected(const QPointingDevice *dev)
{
Q_Q(QQuickDeliveryAgent);
@@ -1804,6 +1896,13 @@ void QQuickDeliveryAgentPrivate::ensureDeviceConnected(const QPointingDevice *de
QObject::connect(dev, &QObject::destroyed, q, [this, dev] {this->knownPointingDevices.removeAll(dev);});
}
+/*! \internal
+ The entry point for delivery of \a event after determining that it \e is a
+ pointer event, and either does not need to be coalesced in
+ compressTouchEvent(), or already has been.
+
+ When it returns, event delivery is done.
+*/
void QQuickDeliveryAgentPrivate::deliverPointerEvent(QPointerEvent *event)
{
Q_Q(QQuickDeliveryAgent);
@@ -1873,11 +1972,36 @@ void QQuickDeliveryAgentPrivate::deliverPointerEvent(QPointerEvent *event)
lastUngrabbed = nullptr;
}
-// check if item or any of its child items contain the point, or if any pointer handler "wants" the point
+/*! \internal
+ Returns a list of all items that are spatially relevant to receive \a event
+ occurring at \a point, starting with \a item and recursively checking all
+ the children.
+ \list
+ \li If an item has pointer handlers, call
+ QQuickPointerHandler::wantsEventPoint()
+ on every handler to decide whether the item is eligible.
+ \li Otherwise, if \a checkMouseButtons is \c true, it means we are
+ finding targets for a mouse event, so no item for which
+ acceptedMouseButtons() is NoButton will be added.
+ \li Otherwise, if \a checkAcceptsTouch is \c true, it means we are
+ finding targets for a touch event, so either acceptTouchEvents() must
+ return true \e or it must accept a synthesized mouse event. I.e. if
+ acceptTouchEvents() returns false, it gets added only if
+ acceptedMouseButtons() is true.
+ \li If QQuickItem::clip() is \c true \e and the \a point is outside of
+ QQuickItem::clipRect(), its children are also omitted. (We stop the
+ recursion, because any clipped-off portions of children under \a point
+ are invisible.)
+ \li Ignore any item in a subscene that "belongs to" a different
+ DeliveryAgent. (In current practice, this only happens in 2D scenes in
+ Qt Quick 3D.)
+ \endlist
+
+ The list returned from this function is the list of items that will be
+ "visited" when delivering any event for which QPointerEvent::isBeginEvent()
+ is \c true.
+*/
// FIXME: should this be iterative instead of recursive?
-// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added.
-// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
-// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
QVector<QQuickItem *> QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *item, const QPointerEvent *event, const QEventPoint &point,
bool checkMouseButtons, bool checkAcceptsTouch) const
{
@@ -1889,7 +2013,7 @@ QVector<QQuickItem *> QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *ite
qCDebug(lcPtrLoc) << q << "point" << point.id() << point.scenePosition() << "->" << itemPos << ": relevant?" << relevant << "to" << item << point;
// if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- if (!relevant)
+ if (!item->clipRect().contains(itemPos))
return targets;
}
@@ -1927,8 +2051,10 @@ QVector<QQuickItem *> QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *ite
return targets;
}
-// return the joined lists
-// list1 has priority, common items come last
+/*! \internal
+ Returns a joined list consisting of the items in \a list1 and \a list2.
+ \a list1 has priority; common items come last.
+*/
QVector<QQuickItem *> QQuickDeliveryAgentPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
{
QVector<QQuickItem *> targets = list1;
@@ -1998,6 +2124,18 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event)
}
if (!relevantPassiveGrabbers.isEmpty())
deliverToPassiveGrabbers(relevantPassiveGrabbers, event);
+
+ // Ensure that HoverHandlers are updated, in case no items got dirty so far and there's no update request
+ if (event->type() == QEvent::TouchUpdate) {
+ for (const auto &[item, id] : hoverItems) {
+ if (item) {
+ bool res = deliverHoverEventToItem(item, point.scenePosition(), point.sceneLastPosition(),
+ event->modifiers(), event->timestamp(), HoverChange::Set);
+ // if the event was accepted, then the item's ID must be valid
+ Q_ASSERT(!res || hoverItems.value(item));
+ }
+ }
+ }
}
if (done)
@@ -2030,7 +2168,45 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event)
}
}
-// Deliver an event containing newly pressed or released touch points
+/*! \internal
+ Deliver a pointer \a event containing newly pressed or released QEventPoints.
+ If \a handlersOnly is \c true, skip the items and just deliver to Pointer Handlers
+ (via QQuickItemPrivate::handlePointerEvent()).
+
+ For the sake of determinism, this function first builds the list
+ \c targetItems by calling pointerTargets() on the root item. That is, the
+ list of items to "visit" is determined at the beginning, and will not be
+ affected if items reparent, hide, or otherwise try to make themselves
+ eligible or ineligible during delivery. (Avoid bugs due to ugly
+ just-in-time tricks in JS event handlers, filters etc.)
+
+ Whenever a touch gesture is in progress, and another touchpoint is pressed,
+ or an existing touchpoint is released, we "start over" with delivery:
+ that's why this function is called whenever the event \e contains newly
+ pressed or released points. It's not necessary for a handler or an item to
+ greedily grab all touchpoints just in case a valid gesture might start.
+ QQuickMultiPointHandler::wantsPointerEvent() can calmly return \c false if
+ the number of points is less than QQuickMultiPointHandler::minimumPointCount(),
+ because it knows it will be asked again if the number of points increases.
+
+ When \a handlersOnly is \c false, \a event visits the items in \c targetItems
+ via QQuickItem::event(). We have to call sendFilteredPointerEvent()
+ before visiting each item, just in case a Flickable (or some other
+ parent-filter) will decide to intercept the event. But we also have to be
+ very careful never to let the same Flickable filter the same event twice,
+ because when Flickable decides to intercept, it lets the child item have
+ that event, and then grabs the next event. That allows you to drag a
+ Slider, DragHandler or whatever inside a ListView delegate: if you're
+ dragging in the correct direction for the draggable child, it can use
+ QQuickItem::setKeepMouseGrab(), QQuickItem::setKeepTouchGrab() or
+ QQuickPointerHandler::grabPermissions() to prevent Flickable from
+ intercepting during filtering, only if it actually \e has the exclusive
+ grab already when Flickable attempts to take it. Typically, both the
+ Flickable and the child are checking the same drag threshold, so the
+ child must have a chance to grab and \e keep the grab before Flickable
+ gets a chance to steal it, even though Flickable actually sees the
+ event first during filtering.
+*/
bool QQuickDeliveryAgentPrivate::deliverPressOrReleaseEvent(QPointerEvent *event, bool handlersOnly)
{
QVector<QQuickItem *> targetItems;
@@ -2062,7 +2238,11 @@ bool QQuickDeliveryAgentPrivate::deliverPressOrReleaseEvent(QPointerEvent *event
}
}
- for (QQuickItem *item : targetItems) {
+ QVector<QPointer<QQuickItem>> safeTargetItems(targetItems.begin(), targetItems.end());
+
+ for (auto &item : safeTargetItems) {
+ if (item.isNull())
+ continue;
// failsafe: when items get into a subscene somehow, ensure that QQuickItemPrivate::deliveryAgent() can find it
if (isSubsceneAgent)
QQuickItemPrivate::get(item)->maybeHasSubsceneDeliveryAgent = true;
@@ -2092,6 +2272,14 @@ bool QQuickDeliveryAgentPrivate::deliverPressOrReleaseEvent(QPointerEvent *event
return event->allPointsAccepted();
}
+/*! \internal
+ Deliver \a pointerEvent to \a item and its handlers, if any.
+ If \a handlersOnly is \c true, skip QQuickItem::event() and just visit its
+ handlers via QQuickItemPrivate::handlePointerEvent().
+
+ This function exists just to de-duplicate the common code between
+ deliverPressOrReleaseEvent() and deliverUpdatedPoints().
+*/
void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly)
{
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
@@ -2125,6 +2313,7 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b
return;
// TODO: unite this mouse point delivery with the synthetic mouse event below
+ // TODO: remove isGrabber then?
if (isMouse) {
auto button = static_cast<QSinglePointEvent *>(pointerEvent)->button();
if ((isGrabber && button == Qt::NoButton) || item->acceptedMouseButtons().testFlag(button)) {
@@ -2170,11 +2359,6 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b
if (item->acceptTouchEvents()) {
qCDebug(lcTouch) << "considering delivering" << &touchEvent << " to " << item;
- // If any parent filters the event, we're done.
- hasFiltered.clear();
- if (sendFilteredPointerEvent(&touchEvent, item))
- return;
-
// Deliver the touch event to the given item
qCDebug(lcTouch) << "actually delivering" << &touchEvent << " to " << item;
QCoreApplication::sendEvent(item, &touchEvent);
@@ -2399,11 +2583,22 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(
}
#endif // quick_draganddrop
+/*! \internal
+ Allow \a filteringParent to filter \a event on behalf of \a receiver, via
+ QQuickItem::childMouseEventFilter(). This happens right \e before we would
+ send \a event to \a receiver.
+
+ Returns \c true only if \a event has been intercepted (by \a filteringParent
+ or some other filtering ancestor) and should \e not be sent to \a receiver.
+*/
bool QQuickDeliveryAgentPrivate::sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
}
+/*! \internal
+ The recursive implementation of sendFilteredPointerEvent().
+*/
bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
if (!allowChildEventFiltering)
@@ -2452,15 +2647,18 @@ bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *eve
QQuickItemPrivate::get(receiver)->localizedTouchEvent(static_cast<QTouchEvent *>(event), true, &filteringParentTouchEvent);
if (filteringParentTouchEvent.type() != QEvent::None) {
qCDebug(lcTouch) << "letting parent" << filteringParent << "filter for" << receiver << &filteringParentTouchEvent;
- if (filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent)) {
+ filtered = filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent);
+ if (filtered) {
qCDebug(lcTouch) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
+ event->setAccepted(filteringParentTouchEvent.isAccepted());
skipDelivery.append(filteringParent);
- for (auto point : filteringParentTouchEvent.points()) {
- const QQuickItem *exclusiveGrabber = qobject_cast<const QQuickItem *>(event->exclusiveGrabber(point));
- if (!exclusiveGrabber || !exclusiveGrabber->keepTouchGrab())
- event->setExclusiveGrabber(point, filteringParent);
+ if (event->isAccepted()) {
+ for (auto point : filteringParentTouchEvent.points()) {
+ const QQuickItem *exclusiveGrabber = qobject_cast<const QQuickItem *>(event->exclusiveGrabber(point));
+ if (!exclusiveGrabber || !exclusiveGrabber->keepTouchGrab())
+ event->setExclusiveGrabber(point, filteringParent);
+ }
}
- return true;
} else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) &&
!filteringParent->acceptTouchEvents()) {
qCDebug(lcTouch) << "touch event NOT intercepted by childMouseEventFilter of " << filteringParent
@@ -2496,17 +2694,17 @@ bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *eve
// touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
touchMouseId = tp.id();
touchMouseDevice = event->pointingDevice();
- if (filteringParent->childMouseEventFilter(receiver, &mouseEvent)) {
+ filtered = filteringParent->childMouseEventFilter(receiver, &mouseEvent);
+ if (filtered) {
qCDebug(lcTouch) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent;
+ event->setAccepted(mouseEvent.isAccepted());
skipDelivery.append(filteringParent);
- if (t != QEvent::MouseButtonRelease) {
+ if (event->isAccepted() && event->isBeginEvent()) {
qCDebug(lcTouchTarget) << "TP (mouse)" << Qt::hex << tp.id() << "->" << filteringParent;
filteringParentTouchEvent.setExclusiveGrabber(tp, filteringParent);
touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
- if (mouseEvent.isAccepted())
- filteringParent->grabMouse();
+ filteringParent->grabMouse();
}
- filtered = true;
}
if (touchMouseUnset)
// Now that we're done sending a synth mouse event, and it wasn't grabbed,
@@ -2526,6 +2724,17 @@ bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *eve
return sendFilteredPointerEventImpl(event, receiver, filteringParent->parentItem()) || filtered;
}
+/*! \internal
+ Allow \a filteringParent to filter \a event on behalf of \a receiver, via
+ QQuickItem::childMouseEventFilter(). This happens right \e before we would
+ send \a event to \a receiver.
+
+ Returns \c true only if \a event has been intercepted (by \a filteringParent
+ or some other filtering ancestor) and should \e not be sent to \a receiver.
+
+ Unlike sendFilteredPointerEvent(), this version does not synthesize a
+ mouse event from touch (presumably it's already an actual mouse event).
+*/
bool QQuickDeliveryAgentPrivate::sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
if (!filteringParent)
@@ -2548,6 +2757,13 @@ bool QQuickDeliveryAgentPrivate::sendFilteredMouseEvent(QEvent *event, QQuickIte
return sendFilteredMouseEvent(event, receiver, filteringParent->parentItem()) || filtered;
}
+/*! \internal
+ Returns \c true if the movement delta \a d in pixels along the \a axis
+ exceeds \a startDragThreshold if it is set, or QStyleHints::startDragDistance();
+ \e or, if QEventPoint::velocity() of \a event exceeds QStyleHints::startDragVelocity().
+
+ \sa QQuickPointerHandlerPrivate::dragOverThreshold()
+*/
bool QQuickDeliveryAgentPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold)
{
QStyleHints *styleHints = QGuiApplication::styleHints();
@@ -2562,6 +2778,13 @@ bool QQuickDeliveryAgentPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMous
return overThreshold;
}
+/*! \internal
+ Returns \c true if the movement delta \a d in pixels along the \a axis
+ exceeds \a startDragThreshold if it is set, or QStyleHints::startDragDistance();
+ \e or, if QEventPoint::velocity() of \a tp exceeds QStyleHints::startDragVelocity().
+
+ \sa QQuickPointerHandlerPrivate::dragOverThreshold()
+*/
bool QQuickDeliveryAgentPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold)
{
QStyleHints *styleHints = qApp->styleHints();
@@ -2574,6 +2797,11 @@ bool QQuickDeliveryAgentPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const
return overThreshold;
}
+/*! \internal
+ Returns \c true if the movement \a delta in pixels exceeds QStyleHints::startDragDistance().
+
+ \sa QQuickDeliveryAgentPrivate::dragOverThreshold()
+*/
bool QQuickDeliveryAgentPrivate::dragOverThreshold(QVector2D delta)
{
int threshold = qApp->styleHints()->startDragDistance();
diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h
index 66db4bc88c..5678afb9f7 100644
--- a/src/quick/util/qquickdeliveryagent_p_p.h
+++ b/src/quick/util/qquickdeliveryagent_p_p.h
@@ -21,6 +21,8 @@
#include <private/qevent_p.h>
#include <private/qpointingdevice_p.h>
+
+#include <QtCore/qpointer.h>
#include <private/qobject_p.h>
#include <memory>
@@ -40,7 +42,7 @@ struct QQuickPointingDeviceExtra {
QVector<QObject *> deliveryTargets;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickDeliveryAgentPrivate : public QObjectPrivate
+class Q_QUICK_EXPORT QQuickDeliveryAgentPrivate : public QObjectPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickDeliveryAgent)
@@ -65,6 +67,8 @@ public:
void clearFocusObject();
void updateFocusItemTransform();
+ QQuickItem *focusTargetItem() const;
+
// Keeps track of the item currently receiving mouse events
#if QT_CONFIG(quick_draganddrop)
QQuickDragGrabber *dragGrabber = nullptr;
@@ -131,6 +135,7 @@ public:
void flushFrameSynchronousEvents(QQuickWindow *win);
void deliverDelayedTouchEvent();
void handleWindowDeactivate(QQuickWindow *win);
+ void handleWindowHidden(QQuickWindow *win);
// utility functions that used to be in QQuickPointerEvent et al.
bool allUpdatedPointsAccepted(const QPointerEvent *ev);
@@ -161,9 +166,14 @@ public:
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
// hover delivery
+ enum class HoverChange : uint8_t {
+ Clear,
+ Set,
+ };
bool deliverHoverEvent(const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp);
bool deliverHoverEventRecursive(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp);
- bool deliverHoverEventToItem(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool clearHover);
+ bool deliverHoverEventToItem(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp,
+ HoverChange hoverChange);
bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp);
bool clearHover(ulong timestamp = 0);
diff --git a/src/quick/util/qquickfontloader.cpp b/src/quick/util/qquickfontloader.cpp
index 991ea7b295..9972071408 100644
--- a/src/quick/util/qquickfontloader.cpp
+++ b/src/quick/util/qquickfontloader.cpp
@@ -28,8 +28,6 @@
QT_BEGIN_NAMESPACE
-#define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16
-
class QQuickFontObject : public QObject
{
Q_OBJECT
@@ -44,7 +42,6 @@ Q_SIGNALS:
void fontDownloaded(int id);
private:
- int redirectCount = 0;
QNetworkReply *reply = nullptr;
private Q_SLOTS:
@@ -74,20 +71,6 @@ void QQuickFontObject::download(const QUrl &url, QNetworkAccessManager *manager)
void QQuickFontObject::replyFinished()
{
if (reply) {
- redirectCount++;
- if (redirectCount < FONTLOADER_MAXIMUM_REDIRECT_RECURSION) {
- QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = reply->url().resolved(redirect.toUrl());
- QNetworkAccessManager *manager = reply->manager();
- reply->deleteLater();
- reply = nullptr;
- download(url, manager);
- return;
- }
- }
- redirectCount = 0;
-
if (!reply->error()) {
id = QFontDatabase::addApplicationFontFromData(reply->readAll());
emit fontDownloaded(id);
@@ -312,7 +295,7 @@ void QQuickFontLoader::updateFontInfo(int id)
text: "Fancy font"
font.family: webFont.font.family
font.weight: webFont.font.weight
- font.style: webFont.font.style
+ font.styleName: webFont.font.styleName
font.pixelSize: 24
}
}
@@ -381,12 +364,11 @@ QString QQuickFontLoader::name() const
\qmlproperty enumeration QtQuick::FontLoader::status
This property holds the status of font loading. It can be one of:
- \list
- \li FontLoader.Null - no font has been set
- \li FontLoader.Ready - the font has been loaded
- \li FontLoader.Loading - the font is currently being loaded
- \li FontLoader.Error - an error occurred while loading the font
- \endlist
+
+ \value FontLoader.Null no font has been set
+ \value FontLoader.Ready the font has been loaded
+ \value FontLoader.Loading the font is currently being loaded
+ \value FontLoader.Error an error occurred while loading the font
Use this status to provide an update or respond to the status change in some way.
For example, you could:
diff --git a/src/quick/util/qquickfontloader_p.h b/src/quick/util/qquickfontloader_p.h
index 5d13b574fe..533541a84a 100644
--- a/src/quick/util/qquickfontloader_p.h
+++ b/src/quick/util/qquickfontloader_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
class QQuickFontLoaderPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickFontLoader : public QObject
+class Q_QUICK_EXPORT QQuickFontLoader : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickFontLoader)
@@ -64,7 +64,5 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFontLoader)
-
#endif // QQUICKFONTLOADER_H
diff --git a/src/quick/util/qquickfontmetrics_p.h b/src/quick/util/qquickfontmetrics_p.h
index a91e5288b5..a4ebf5cbf7 100644
--- a/src/quick/util/qquickfontmetrics_p.h
+++ b/src/quick/util/qquickfontmetrics_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
class QFont;
-class Q_QUICK_PRIVATE_EXPORT QQuickFontMetrics : public QObject
+class Q_QUICK_EXPORT QQuickFontMetrics : public QObject
{
Q_OBJECT
@@ -86,6 +86,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFontMetrics)
-
#endif // QQUICKFONTMETRICS_H
diff --git a/src/quick/util/qquickforeignutils.cpp b/src/quick/util/qquickforeignutils.cpp
index e8e60bbc92..242d961cdc 100644
--- a/src/quick/util/qquickforeignutils.cpp
+++ b/src/quick/util/qquickforeignutils.cpp
@@ -10,6 +10,7 @@ QT_BEGIN_NAMESPACE
\instantiates QEventPoint
\inqmlmodule QtQuick
\brief Qml equivalent for \l QEventPoint.
+ \sa handlerPoint
eventPoint is the Qml value type representation
of \l QEventPoint.
@@ -19,7 +20,7 @@ QT_BEGIN_NAMESPACE
\list
\li \l bool \c eventPoint.accepted: see also \l QEventPoint::accepted
- \li \l pointingDevice \c eventPoint.device: see also \l QEventPoint::device
+ \li \l PointerDevice \c eventPoint.device: see also \l QEventPoint::device
\li \l size \c eventPoint.ellipseDiameters: see also \l QEventPoint::ellipseDiameters
\li \l point \c eventPoint.globalGrabPosition: see also \l QEventPoint::globalGrabPosition
\li \l point \c eventPoint.globalLastPosition: see also \l QEventPoint::globalLastPosition
@@ -49,27 +50,21 @@ QT_BEGIN_NAMESPACE
State supports the following values:
- \table
- \row
- \li \c Qt.TouchPointUnknownState
- \li Unknown state.
- \row
- \li \c Qt.TouchPointStationary
- \li The event point did not move.
- \row
- \li \c Qt.TouchPointPressed
- \li The touch point or button is pressed.
- \row
- \li \c Qt.TouchPointMoved
- \li The event point was updated.
- \row
- \li \c Qt.TouchPointReleased
- \li The touch point or button was released.
- \endtable
+ \value EventPoint.Unknown
+ Unknown state; same as Qt.TouchPointUnknownState
+ \value EventPoint.Stationary
+ The event point did not move; same as Qt.TouchPointStationary
+ \value EventPoint.Pressed
+ The touch point or button is pressed; same as Qt.TouchPointPressed
+ \value EventPoint.Updated
+ The event point was updated; same as Qt.TouchPointMoved
+ \value EventPoint.Released
+ The touch point or button was released; same as Qt.TouchPointReleased
The States type is a typedef for \l {QFlags} {QFlags<State>}. It stores an OR combination of
State values. See also \l QEventPoint::States
+ \snippet pointerHandlers/tapHandlerGrabChanged.qml 0
*/
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickforeignutils_p.h b/src/quick/util/qquickforeignutils_p.h
index 1ce53ae7ef..5d257fb16d 100644
--- a/src/quick/util/qquickforeignutils_p.h
+++ b/src/quick/util/qquickforeignutils_p.h
@@ -99,6 +99,22 @@ struct QEventPointForeign
QML_ADDED_IN_VERSION(6, 5)
};
+// Prevent the same QEventPoint type from being exported into qmltypes
+// twice, as a value type and a namespace.
+// TODO: Remove once QTBUG-115855 is fixed.
+struct QEventPointDerived : public QEventPoint
+{
+ Q_GADGET
+};
+
+namespace QEventPointForeignNamespace
+{
+ Q_NAMESPACE
+ QML_FOREIGN_NAMESPACE(QEventPointDerived)
+ QML_NAMED_ELEMENT(EventPoint)
+ QML_ADDED_IN_VERSION(6, 6)
+};
+
QT_END_NAMESPACE
#endif // QTQUICKFOREIGN_P_H
diff --git a/src/quick/util/qquickframeanimation_p.h b/src/quick/util/qquickframeanimation_p.h
index 5011673bb6..39480d47c2 100644
--- a/src/quick/util/qquickframeanimation_p.h
+++ b/src/quick/util/qquickframeanimation_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
class QQuickFrameAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickFrameAnimation : public QObject, public QQmlParserStatus
+class Q_QUICK_EXPORT QQuickFrameAnimation : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickFrameAnimation)
@@ -80,6 +80,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFrameAnimation)
-
#endif
diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp
index 3eae2ac28d..f1aae5c6fa 100644
--- a/src/quick/util/qquickglobal.cpp
+++ b/src/quick/util/qquickglobal.cpp
@@ -24,6 +24,7 @@
#include <QtQml/private/qqmlanybinding_p.h>
#include <QtCore/qiterable.h>
+#include <QtCore/qpointer.h>
#ifdef Q_CC_MSVC
// MSVC2010 warns about 'unused variable t', even if it's used in t->~T()
diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp
index e3b96ddad4..27134c9deb 100644
--- a/src/quick/util/qquickimageprovider.cpp
+++ b/src/quick/util/qquickimageprovider.cpp
@@ -4,7 +4,7 @@
#include "qquickimageprovider.h"
#include "qquickimageprovider_p.h"
-#include "qquickpixmapcache_p.h"
+#include "qquickpixmap_p.h"
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qqmlglobal_p.h>
#include <QtGui/qcolorspace.h>
@@ -476,6 +476,7 @@ public:
}
QColorSpace targetColorSpace;
+ QRectF sourceClipRect;
QQuickImageProviderOptions::AutoTransform autoTransform = QQuickImageProviderOptions::UsePluginDefaultTransform;
bool preserveAspectRatioCrop = false;
bool preserveAspectRatioFit = false;
@@ -582,6 +583,19 @@ void QQuickImageProviderOptions::setTargetColorSpace(const QColorSpace &colorSpa
d->targetColorSpace = colorSpace;
}
+/*!
+ Returns the requested source clip rect.
+*/
+QRectF QQuickImageProviderOptions::sourceClipRect() const
+{
+ return d->sourceClipRect;
+}
+
+void QQuickImageProviderOptions::setSourceClipRect(const QRectF &rect)
+{
+ d->sourceClipRect = rect;
+}
+
QQuickImageProviderWithOptions::QQuickImageProviderWithOptions(ImageType type, Flags flags)
: QQuickAsyncImageProvider()
{
diff --git a/src/quick/util/qquickinputmethod_p.h b/src/quick/util/qquickinputmethod_p.h
index 7e0573e867..927f009b2c 100644
--- a/src/quick/util/qquickinputmethod_p.h
+++ b/src/quick/util/qquickinputmethod_p.h
@@ -25,22 +25,22 @@
#include <private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickInputMethod : public QObject
+class Q_QUICK_EXPORT QQuickInputMethod : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(InputMethod)
QML_ADDED_IN_VERSION(6, 4)
QML_SINGLETON
- Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged)
- Q_PROPERTY(QRectF anchorRectangle READ anchorRectangle NOTIFY anchorRectangleChanged)
- Q_PROPERTY(QRectF keyboardRectangle READ keyboardRectangle NOTIFY keyboardRectangleChanged)
+ Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged FINAL)
+ Q_PROPERTY(QRectF anchorRectangle READ anchorRectangle NOTIFY anchorRectangleChanged FINAL)
+ Q_PROPERTY(QRectF keyboardRectangle READ keyboardRectangle NOTIFY keyboardRectangleChanged FINAL)
Q_PROPERTY(QRectF inputItemClipRectangle READ inputItemClipRectangle NOTIFY
- inputItemClipRectangleChanged)
- Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
- Q_PROPERTY(bool animating READ isAnimating NOTIFY animatingChanged)
- Q_PROPERTY(QLocale locale READ locale NOTIFY localeChanged)
- Q_PROPERTY(Qt::LayoutDirection inputDirection READ inputDirection NOTIFY inputDirectionChanged)
+ inputItemClipRectangleChanged FINAL)
+ Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged FINAL)
+ Q_PROPERTY(bool animating READ isAnimating NOTIFY animatingChanged FINAL)
+ Q_PROPERTY(QLocale locale READ locale NOTIFY localeChanged FINAL)
+ Q_PROPERTY(Qt::LayoutDirection inputDirection READ inputDirection NOTIFY inputDirectionChanged FINAL)
public:
explicit QQuickInputMethod(QObject *parent = nullptr);
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index a38de75753..8995c64d0b 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -143,7 +143,7 @@ QQuickPath::~QQuickPath()
qreal QQuickPath::startX() const
{
Q_D(const QQuickPath);
- return d->startX.isNull ? 0 : d->startX.value;
+ return d->startX.isValid() ? d->startX.value() : 0;
}
void QQuickPath::setStartX(qreal x)
@@ -165,7 +165,7 @@ bool QQuickPath::hasStartX() const
qreal QQuickPath::startY() const
{
Q_D(const QQuickPath);
- return d->startY.isNull ? 0 : d->startY.value;
+ return d->startY.isValid() ? d->startY.value() : 0;
}
void QQuickPath::setStartY(qreal y)
@@ -372,6 +372,9 @@ void QQuickPath::processPath()
d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
}
+ if (d->simplify)
+ d->_path = d->_path.simplified();
+
emit changed();
}
@@ -405,8 +408,8 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
first.values[attributes.at(ii)] = 0;
attributePoints << first;
- qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
- qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
+ qreal startX = d->startX.isValid() ? d->startX.value() : startPoint.x();
+ qreal startY = d->startY.isValid() ? d->startY.value() : startPoint.y();
path.moveTo(startX, startY);
const QString percentString = QStringLiteral("_qfx_percent");
@@ -490,8 +493,8 @@ QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPoint
QPainterPath path;
- qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
- qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
+ qreal startX = d->startX.isValid() ? d->startX.value() : startPoint.x();
+ qreal startY = d->startY.isValid() ? d->startY.value() : startPoint.y();
path.moveTo(startX, startY);
int index = 0;
@@ -712,6 +715,32 @@ void QQuickPath::invalidateSequentialHistory() const
d->prevBez.isValid = false;
}
+/*! \qmlproperty bool QtQuick::Path::simplify
+ \since 6.6
+
+ When set to true, the path will be simplified. This implies merging all subpaths that intersect,
+ creating a path where there are no self-intersections. Consecutive parallel lines will also be
+ merged. The simplified path is intended to be used with ShapePath.OddEvenFill. Bezier curves may
+ be flattened to line segments due to numerical instability of doing bezier curve intersections.
+*/
+void QQuickPath::setSimplify(bool s)
+{
+ Q_D(QQuickPath);
+ if (d->simplify == s)
+ return;
+
+ d->simplify = s;
+ processPath();
+
+ emit simplifyChanged();
+}
+
+bool QQuickPath::simplify() const
+{
+ Q_D(const QQuickPath);
+ return d->simplify;
+}
+
/*!
\qmlproperty size QtQuick::Path::scale
@@ -952,12 +981,12 @@ qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
qreal QQuickCurve::x() const
{
- return _x.isNull ? 0 : _x.value;
+ return _x.isValid() ? _x.value() : 0;
}
void QQuickCurve::setX(qreal x)
{
- if (_x.isNull || _x != x) {
+ if (!_x.isValid() || _x != x) {
_x = x;
emit xChanged();
emit changed();
@@ -971,12 +1000,12 @@ bool QQuickCurve::hasX()
qreal QQuickCurve::y() const
{
- return _y.isNull ? 0 : _y.value;
+ return _y.isValid() ? _y.value() : 0;
}
void QQuickCurve::setY(qreal y)
{
- if (_y.isNull || _y != y) {
+ if (!_y.isValid() || _y != y) {
_y = y;
emit yChanged();
emit changed();
@@ -995,7 +1024,7 @@ qreal QQuickCurve::relativeX() const
void QQuickCurve::setRelativeX(qreal x)
{
- if (_relativeX.isNull || _relativeX != x) {
+ if (!_relativeX.isValid() || _relativeX != x) {
_relativeX = x;
emit relativeXChanged();
emit changed();
@@ -1014,7 +1043,7 @@ qreal QQuickCurve::relativeY() const
void QQuickCurve::setRelativeY(qreal y)
{
- if (_relativeY.isNull || _relativeY != y) {
+ if (!_relativeY.isValid() || _relativeY != y) {
_relativeY = y;
emit relativeYChanged();
emit changed();
@@ -1373,7 +1402,7 @@ qreal QQuickPathQuad::relativeControlX() const
void QQuickPathQuad::setRelativeControlX(qreal x)
{
- if (_relativeControlX.isNull || _relativeControlX != x) {
+ if (!_relativeControlX.isValid() || _relativeControlX != x) {
_relativeControlX = x;
emit relativeControlXChanged();
emit changed();
@@ -1392,7 +1421,7 @@ qreal QQuickPathQuad::relativeControlY() const
void QQuickPathQuad::setRelativeControlY(qreal y)
{
- if (_relativeControlY.isNull || _relativeControlY != y) {
+ if (!_relativeControlY.isValid() || _relativeControlY != y) {
_relativeControlY = y;
emit relativeControlYChanged();
emit changed();
@@ -1557,7 +1586,7 @@ qreal QQuickPathCubic::relativeControl1X() const
void QQuickPathCubic::setRelativeControl1X(qreal x)
{
- if (_relativeControl1X.isNull || _relativeControl1X != x) {
+ if (!_relativeControl1X.isValid() || _relativeControl1X != x) {
_relativeControl1X = x;
emit relativeControl1XChanged();
emit changed();
@@ -1576,7 +1605,7 @@ qreal QQuickPathCubic::relativeControl1Y() const
void QQuickPathCubic::setRelativeControl1Y(qreal y)
{
- if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
+ if (!_relativeControl1Y.isValid() || _relativeControl1Y != y) {
_relativeControl1Y = y;
emit relativeControl1YChanged();
emit changed();
@@ -1595,7 +1624,7 @@ qreal QQuickPathCubic::relativeControl2X() const
void QQuickPathCubic::setRelativeControl2X(qreal x)
{
- if (_relativeControl2X.isNull || _relativeControl2X != x) {
+ if (!_relativeControl2X.isValid() || _relativeControl2X != x) {
_relativeControl2X = x;
emit relativeControl2XChanged();
emit changed();
@@ -1614,7 +1643,7 @@ qreal QQuickPathCubic::relativeControl2Y() const
void QQuickPathCubic::setRelativeControl2Y(qreal y)
{
- if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
+ if (!_relativeControl2Y.isValid() || _relativeControl2Y != y) {
_relativeControl2Y = y;
emit relativeControl2YChanged();
emit changed();
@@ -2649,22 +2678,21 @@ void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
*/
/*!
- \qmlproperty enumeration QtQuick::PathText::font.weight
+ \qmlproperty int QtQuick::PathText::font.weight
Sets the font's weight.
The weight can be one of:
- \list
- \li Font.Thin
- \li Font.Light
- \li Font.ExtraLight
- \li Font.Normal - the default
- \li Font.Medium
- \li Font.DemiBold
- \li Font.Bold
- \li Font.ExtraBold
- \li Font.Black
- \endlist
+
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400 (default)
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
\qml
PathText { text: "Hello"; font.weight: Font.DemiBold }
@@ -2728,13 +2756,12 @@ void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
Sets the capitalization for the text.
- \list
- \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
- \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
- \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
- \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
- \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
- \endlist
+ \value Font.MixedCase no capitalization change is applied
+ \value Font.AllUppercase alters the text to be rendered in all uppercase type
+ \value Font.AllLowercase alters the text to be rendered in all lowercase type
+ \value Font.SmallCaps alters the text to be rendered in small-caps type
+ \value Font.Capitalize alters the text to be rendered with the first character of
+ each word as an uppercase character
\qml
PathText { text: "Hello"; font.capitalization: Font.AllLowercase }
@@ -2769,6 +2796,26 @@ void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
\endqml
*/
+/*!
+ \qmlproperty object QtQuick::PathText::font.variableAxes
+ \since 6.7
+
+ \include qquicktext.cpp qml-font-variable-axes
+*/
+
+/*!
+ \qmlproperty object QtQuick::PathText::font.features
+ \since 6.6
+
+ \include qquicktext.cpp qml-font-features
+*/
+
+/*!
+ \qmlproperty object QtQuick::PathText::font.contextFontMerging
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-context-font-merging
+*/
void QQuickPathText::updatePath() const
{
if (!_path.isEmpty())
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index 9333805389..55400d2ddd 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -40,7 +40,7 @@ struct QQuickPathData
QList<QQuickCurve*> curves;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathElement : public QObject
+class Q_QUICK_EXPORT QQuickPathElement : public QObject
{
Q_OBJECT
QML_ANONYMOUS
@@ -51,7 +51,7 @@ Q_SIGNALS:
void changed();
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathAttribute : public QQuickPathElement
+class Q_QUICK_EXPORT QQuickPathAttribute : public QQuickPathElement
{
Q_OBJECT
@@ -78,7 +78,7 @@ private:
qreal _value = 0;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickCurve : public QQuickPathElement
+class Q_QUICK_EXPORT QQuickCurve : public QQuickPathElement
{
Q_OBJECT
@@ -122,7 +122,7 @@ private:
QQmlNullableValue<qreal> _relativeY;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathLine : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathLine : public QQuickCurve
{
Q_OBJECT
QML_NAMED_ELEMENT(PathLine)
@@ -133,7 +133,7 @@ public:
void addToPath(QPainterPath &path, const QQuickPathData &) override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathMove : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathMove : public QQuickCurve
{
Q_OBJECT
QML_NAMED_ELEMENT(PathMove)
@@ -144,7 +144,7 @@ public:
void addToPath(QPainterPath &path, const QQuickPathData &) override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathQuad : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathQuad : public QQuickCurve
{
Q_OBJECT
@@ -187,7 +187,7 @@ private:
QQmlNullableValue<qreal> _relativeControlY;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathCubic : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathCubic : public QQuickCurve
{
Q_OBJECT
@@ -255,7 +255,7 @@ private:
QQmlNullableValue<qreal> _relativeControl2Y;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathCatmullRomCurve : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathCatmullRomCurve : public QQuickCurve
{
Q_OBJECT
QML_NAMED_ELEMENT(PathCurve)
@@ -266,7 +266,7 @@ public:
void addToPath(QPainterPath &path, const QQuickPathData &) override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathArc : public QQuickCurve
{
Q_OBJECT
Q_PROPERTY(qreal radiusX READ radiusX WRITE setRadiusX NOTIFY radiusXChanged)
@@ -316,7 +316,7 @@ private:
qreal _xAxisRotation = 0;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathAngleArc : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathAngleArc : public QQuickCurve
{
Q_OBJECT
Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged)
@@ -376,7 +376,7 @@ private:
bool _moveToStart = true;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathSvg : public QQuickCurve
{
Q_OBJECT
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
@@ -397,7 +397,7 @@ private:
QString _path;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathPercent : public QQuickPathElement
+class Q_QUICK_EXPORT QQuickPathPercent : public QQuickPathElement
{
Q_OBJECT
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged)
@@ -416,7 +416,7 @@ private:
qreal _value = 0;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathPolyline : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathPolyline : public QQuickCurve
{
Q_OBJECT
Q_PROPERTY(QPointF start READ start NOTIFY startChanged)
@@ -440,7 +440,7 @@ private:
QVector<QPointF> m_path;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathMultiline : public QQuickCurve
+class Q_QUICK_EXPORT QQuickPathMultiline : public QQuickCurve
{
Q_OBJECT
Q_PROPERTY(QPointF start READ start NOTIFY startChanged)
@@ -478,7 +478,7 @@ struct QQuickCachedBezier
};
class QQuickPathPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPath : public QObject, public QQmlParserStatus
+class Q_QUICK_EXPORT QQuickPath : public QObject, public QQmlParserStatus
{
Q_OBJECT
@@ -487,6 +487,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPath : public QObject, public QQmlParserStatu
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged)
Q_PROPERTY(qreal startY READ startY WRITE setStartY NOTIFY startYChanged)
Q_PROPERTY(bool closed READ isClosed NOTIFY changed)
+ Q_PROPERTY(bool simplify READ simplify WRITE setSimplify NOTIFY simplifyChanged REVISION(6, 6) FINAL)
Q_PROPERTY(QSizeF scale READ scale WRITE setScale NOTIFY scaleChanged REVISION(2, 14))
Q_CLASSINFO("DefaultProperty", "pathElements")
QML_NAMED_ELEMENT(Path)
@@ -517,10 +518,14 @@ public:
QSizeF scale() const;
void setScale(const QSizeF &scale);
+ bool simplify() const;
+ void setSimplify(bool s);
+
Q_SIGNALS:
void changed();
void startXChanged();
void startYChanged();
+ Q_REVISION(6, 6) void simplifyChanged();
Q_REVISION(2, 14) void scaleChanged();
protected:
@@ -574,7 +579,7 @@ public:
static QPointF sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle = nullptr);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathText : public QQuickPathElement
+class Q_QUICK_EXPORT QQuickPathText : public QQuickPathElement
{
Q_OBJECT
Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
@@ -676,20 +681,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPathElement)
-QML_DECLARE_TYPE(QQuickPathAttribute)
-QML_DECLARE_TYPE(QQuickCurve)
-QML_DECLARE_TYPE(QQuickPathLine)
-QML_DECLARE_TYPE(QQuickPathMove)
-QML_DECLARE_TYPE(QQuickPathQuad)
-QML_DECLARE_TYPE(QQuickPathCubic)
-QML_DECLARE_TYPE(QQuickPathCatmullRomCurve)
-QML_DECLARE_TYPE(QQuickPathArc)
-QML_DECLARE_TYPE(QQuickPathAngleArc)
-QML_DECLARE_TYPE(QQuickPathSvg)
-QML_DECLARE_TYPE(QQuickPathPercent)
-QML_DECLARE_TYPE(QQuickPathPolyline)
-QML_DECLARE_TYPE(QQuickPath)
-QML_DECLARE_TYPE(QQuickPathText)
-
#endif // QQUICKPATH_H
diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h
index 36067df5dd..ae57a56ccc 100644
--- a/src/quick/util/qquickpath_p_p.h
+++ b/src/quick/util/qquickpath_p_p.h
@@ -28,7 +28,7 @@ QT_REQUIRE_CONFIG(quick_path);
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickPathPrivate : public QObjectPrivate
+class Q_QUICK_EXPORT QQuickPathPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickPath)
@@ -53,6 +53,7 @@ public:
bool closed = false;
bool componentComplete = true;
bool isShapePath = false;
+ bool simplify = false;
};
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickpathinterpolator_p.h b/src/quick/util/qquickpathinterpolator_p.h
index 2d235f2064..c8910173f9 100644
--- a/src/quick/util/qquickpathinterpolator_p.h
+++ b/src/quick/util/qquickpathinterpolator_p.h
@@ -25,7 +25,7 @@ QT_REQUIRE_CONFIG(quick_path);
QT_BEGIN_NAMESPACE
class QQuickPath;
-class Q_QUICK_PRIVATE_EXPORT QQuickPathInterpolator : public QObject
+class Q_QUICK_EXPORT QQuickPathInterpolator : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged)
@@ -68,6 +68,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPathInterpolator)
-
#endif // QQUICKPATHINTERPOLATOR_P_H
diff --git a/src/quick/util/qquickpixmap_p.h b/src/quick/util/qquickpixmap_p.h
new file mode 100644
index 0000000000..f352da8d16
--- /dev/null
+++ b/src/quick/util/qquickpixmap_p.h
@@ -0,0 +1,205 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKPIXMAP_H
+#define QQUICKPIXMAP_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qpixmap.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qurl.h>
+#include <private/qtquickglobal_p.h>
+#include <QtQuick/qquickimageprovider.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlEngine;
+class QQuickPixmapData;
+class QQuickTextureFactory;
+class QQuickImageProviderOptionsPrivate;
+
+class QQuickDefaultTextureFactory : public QQuickTextureFactory
+{
+ Q_OBJECT
+public:
+ QQuickDefaultTextureFactory(const QImage &i);
+ QSGTexture *createTexture(QQuickWindow *window) const override;
+ QSize textureSize() const override { return size; }
+ int textureByteCount() const override { return size.width() * size.height() * 4; }
+ QImage image() const override { return im; }
+
+private:
+ QImage im;
+ QSize size;
+};
+
+class QQuickImageProviderPrivate
+{
+public:
+ QQuickImageProvider::ImageType type;
+ QQuickImageProvider::Flags flags;
+ bool isProviderWithOptions;
+};
+
+// ### Qt 6: Make public moving to qquickimageprovider.h
+class Q_QUICK_EXPORT QQuickImageProviderOptions
+{
+public:
+ enum AutoTransform {
+ UsePluginDefaultTransform = -1,
+ ApplyTransform = 0,
+ DoNotApplyTransform = 1
+ };
+
+ QQuickImageProviderOptions();
+ ~QQuickImageProviderOptions();
+
+ QQuickImageProviderOptions(const QQuickImageProviderOptions&);
+ QQuickImageProviderOptions& operator=(const QQuickImageProviderOptions&);
+
+ bool operator==(const QQuickImageProviderOptions&) const;
+
+ AutoTransform autoTransform() const;
+ void setAutoTransform(AutoTransform autoTransform);
+
+ bool preserveAspectRatioCrop() const;
+ void setPreserveAspectRatioCrop(bool preserveAspectRatioCrop);
+
+ bool preserveAspectRatioFit() const;
+ void setPreserveAspectRatioFit(bool preserveAspectRatioFit);
+
+ QColorSpace targetColorSpace() const;
+ void setTargetColorSpace(const QColorSpace &colorSpace);
+
+ QRectF sourceClipRect() const;
+ void setSourceClipRect(const QRectF &rect);
+
+private:
+ QSharedDataPointer<QQuickImageProviderOptionsPrivate> d;
+};
+
+/*! \internal
+ A class that encapsulates the action of fetching a pixmap, as well as the
+ pixmap itself (indirectly via QQuickPixmapData::textureFactory) and the
+ responsibility of canceling outstanding requests. Rather than relying on
+ QPixmapCache which doesn't cache all the information Qt Quick needs,
+ QQuickPixmap implements its own cache, that correctly degrades over time.
+ (QQuickPixmapData::release() marks it as being not-currently-used, and
+ QQuickPixmapCache::shrinkCache() sweeps away the least-recently-released
+ instances until the remaining bytes are less than cache_limit.)
+*/
+class Q_QUICK_EXPORT QQuickPixmap
+{
+ Q_DECLARE_TR_FUNCTIONS(QQuickPixmap)
+public:
+ enum Status { Null, Ready, Error, Loading };
+
+ enum Option {
+ Asynchronous = 0x00000001,
+ Cache = 0x00000002
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ QQuickPixmap();
+ QQuickPixmap(QQmlEngine *, const QUrl &);
+ QQuickPixmap(QQmlEngine *, const QUrl &, Options options);
+ QQuickPixmap(QQmlEngine *, const QUrl &, const QRect &region, const QSize &);
+ QQuickPixmap(const QUrl &, const QImage &image);
+ ~QQuickPixmap();
+
+ bool isNull() const;
+ bool isReady() const;
+ bool isError() const;
+ bool isLoading() const;
+
+ Status status() const;
+ QString error() const;
+ const QUrl &url() const;
+ const QSize &implicitSize() const;
+ const QRect &requestRegion() const;
+ const QSize &requestSize() const;
+ QQuickImageProviderOptions::AutoTransform autoTransform() const;
+ int frameCount() const;
+ QImage image() const;
+ void setImage(const QImage &);
+ void setPixmap(const QQuickPixmap &other);
+
+ QColorSpace colorSpace() const;
+
+ QQuickTextureFactory *textureFactory() const;
+
+ QRect rect() const;
+ int width() const;
+ int height() const;
+
+ void load(QQmlEngine *, const QUrl &);
+ void load(QQmlEngine *, const QUrl &, QQuickPixmap::Options options);
+ void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize);
+ void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize, QQuickPixmap::Options options);
+ void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize,
+ QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1,
+ qreal devicePixelRatio = 1.0);
+ void loadImageFromDevice(QQmlEngine *engine, QIODevice *device, const QUrl &url,
+ const QRect &requestRegion, const QSize &requestSize,
+ const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1);
+
+ void clear();
+ void clear(QObject *);
+
+ bool connectFinished(QObject *, const char *);
+ bool connectFinished(QObject *, int);
+ bool connectDownloadProgress(QObject *, const char *);
+ bool connectDownloadProgress(QObject *, int);
+
+ static void purgeCache();
+ static bool isCached(const QUrl &url, const QRect &requestRegion, const QSize &requestSize,
+ const int frame, const QQuickImageProviderOptions &options);
+ static bool isScalableImageFormat(const QUrl &url);
+
+ static const QLatin1String itemGrabberScheme;
+
+private:
+ Q_DISABLE_COPY(QQuickPixmap)
+ QQuickPixmapData *d;
+ friend class QQuickPixmapData;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPixmap::Options)
+
+// ### Qt 6: This should be made public in Qt 6. It's functionality can't be merged into
+// QQuickImageProvider without breaking source compatibility.
+class Q_QUICK_EXPORT QQuickImageProviderWithOptions : public QQuickAsyncImageProvider
+{
+public:
+ QQuickImageProviderWithOptions(ImageType type, Flags flags = Flags());
+
+ QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize) override;
+ QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) override;
+ QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize) override;
+ QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
+
+ virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options);
+ virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options);
+ virtual QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize, const QQuickImageProviderOptions &options);
+ virtual QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize, const QQuickImageProviderOptions &options);
+
+ static QSize loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options,
+ qreal devicePixelRatio = 1.0);
+ static QQuickImageProviderWithOptions *checkedCast(QQuickImageProvider *provider);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPIXMAP_H
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index e5c19edab0..10cc407c21 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -5,6 +5,7 @@
#include <QtQuick/private/qquickimageprovider_p.h>
#include <QtQuick/private/qquickprofiler_p.h>
#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgtexturereader_p.h>
#include <QtQuick/qquickwindow.h>
@@ -27,6 +28,7 @@
#include <QtCore/qbuffer.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmetaobject.h>
+#include <QtCore/qscopeguard.h>
#if QT_CONFIG(qml_network)
#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
@@ -34,9 +36,14 @@
#include <QtNetwork/qsslerror.h>
#endif
+#include <private/qdebug_p.h>
+
#define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT 8
-#define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16
+
+// After QQuickPixmapCache::unreferencePixmap() it may get deleted via a timer in 30 seconds
#define CACHE_EXPIRE_TIME 30
+
+// How many (1/4) of the unreferenced pixmaps to delete in QQuickPixmapCache::timerEvent()
#define CACHE_REMOVAL_FRACTION 4
#define PIXMAP_PROFILE(Code) Q_QUICK_PROFILE(QQuickProfiler::ProfilePixmapCache, Code)
@@ -49,16 +56,55 @@
QT_BEGIN_NAMESPACE
-const QLatin1String QQuickPixmap::itemGrabberScheme = QLatin1String("itemgrabber");
+using namespace Qt::Literals::StringLiterals;
-Q_LOGGING_CATEGORY(lcImg, "qt.quick.image")
+Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak)
+
+#if defined(QT_DEBUG) && QT_CONFIG(thread)
+class ThreadAffinityMarker
+{
+public:
+ ThreadAffinityMarker() { attachToCurrentThread(); }
+
+ void assertOnAssignedThread()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (!m_assignedThread)
+ attachToCurrentThread();
+ Q_ASSERT_X(m_assignedThread == QThread::currentThreadId(), Q_FUNC_INFO,
+ "Running on a wrong thread!");
+ }
-#ifndef QT_NO_DEBUG
-static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
+ void detachFromCurrentThread()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_assignedThread = nullptr;
+ }
+
+ void attachToCurrentThread() { m_assignedThread = QThread::currentThreadId(); }
+
+private:
+ Qt::HANDLE m_assignedThread;
+ QMutex m_mutex;
+};
+# define Q_THREAD_AFFINITY_MARKER(x) ThreadAffinityMarker x
+# define Q_ASSERT_CALLED_ON_VALID_THREAD(x) x.assertOnAssignedThread()
+# define Q_DETACH_THREAD_AFFINITY_MARKER(x) x.detachFromCurrentThread()
+#else
+# define Q_THREAD_AFFINITY_MARKER(x)
+# define Q_ASSERT_CALLED_ON_VALID_THREAD(x)
+# define Q_DETACH_THREAD_AFFINITY_MARKER(x)
#endif
-// The cache limit describes the maximum "junk" in the cache.
-static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded in qpixmapcache.cpp
+const QLatin1String QQuickPixmap::itemGrabberScheme = QLatin1String("itemgrabber");
+
+Q_LOGGING_CATEGORY(lcImg, "qt.quick.image")
+
+/*! \internal
+ The maximum currently-unused image data that can be stored for potential
+ later reuse, in bytes. See QQuickPixmapCache::shrinkCache()
+*/
+static int cache_limit = 2048 * 1024;
static inline QString imageProviderId(const QUrl &url)
{
@@ -110,9 +156,9 @@ public:
bool loading;
QQuickImageProviderOptions providerOptions;
- int redirectCount;
class Event : public QEvent {
+ Q_EVENT_DISABLE_COPY(Event);
public:
Event(ReadError, const QString &, const QSize &, QQuickTextureFactory *factory);
~Event();
@@ -136,22 +182,37 @@ private:
Q_DISABLE_COPY(QQuickPixmapReply)
public:
- static int finishedIndex;
- static int downloadProgressIndex;
+ static int finishedMethodIndex;
+ static int downloadProgressMethodIndex;
};
-class QQuickPixmapReaderThreadObject : public QObject {
+/*! \internal
+ Serves as an endpoint for notifications on the connected reader's thread, thus enforcing
+ execution of their continuation on the thread. */
+class ReaderThreadExecutionEnforcer : public QObject
+{
Q_OBJECT
public:
- QQuickPixmapReaderThreadObject(QQuickPixmapReader *);
- void processJobs();
- bool event(QEvent *e) override;
+ enum Event {
+ ProcessJobs = QEvent::User,
+ };
+
+ ReaderThreadExecutionEnforcer(QQuickPixmapReader *reader);
+
+ /*! \internal
+ Forces the execution of processJobs() on the original reader on the thread it's running on.
+ */
+ void processJobsOnReaderThreadLater();
+
public slots:
void asyncResponseFinished(QQuickImageResponse *response);
+ void asyncResponseFinished();
private slots:
void networkRequestDone();
- void asyncResponseFinished();
private:
+ Q_DISABLE_COPY(ReaderThreadExecutionEnforcer)
+ bool event(QEvent *e) override;
+
QQuickPixmapReader *reader;
};
@@ -168,12 +229,14 @@ public:
static QQuickPixmapReader *instance(QQmlEngine *engine);
static QQuickPixmapReader *existingInstance(QQmlEngine *engine);
+ void startJob(QQuickPixmapReply *job);
protected:
void run() override;
private:
- friend class QQuickPixmapReaderThreadObject;
+ Q_DISABLE_COPY(QQuickPixmapReader)
+ friend class ReaderThreadExecutionEnforcer;
void processJobs();
void processJob(QQuickPixmapReply *, const QUrl &, const QString &, QQuickImageProvider::ImageType, const QSharedPointer<QQuickImageProvider> &);
#if QT_CONFIG(qml_network)
@@ -182,14 +245,30 @@ private:
void asyncResponseFinished(QQuickImageResponse *);
QList<QQuickPixmapReply*> jobs;
- QList<QQuickPixmapReply*> cancelled;
+ QList<QQuickPixmapReply *> cancelledJobs;
QQmlEngine *engine;
- QObject *eventLoopQuitHack;
+#if QT_CONFIG(quick_pixmap_cache_threaded_download)
+ /*! \internal
+ Returns a pointer to the thread object owned by the run loop in QQuickPixmapReader::run.
+ */
+ ReaderThreadExecutionEnforcer *readerThreadExecutionEnforcer()
+ {
+ return runLoopReaderThreadExecutionEnforcer;
+ }
+ QObject *eventLoopQuitHack;
QMutex mutex;
-
- // Owned not by the QQuickPixmapReader, but by the run() function.
- QQuickPixmapReaderThreadObject *threadObject = nullptr;
+ ReaderThreadExecutionEnforcer *runLoopReaderThreadExecutionEnforcer = nullptr;
+#else
+ /*! \internal
+ Returns a pointer to the thread object owned by this instance.
+ */
+ ReaderThreadExecutionEnforcer *readerThreadExecutionEnforcer()
+ {
+ return ownedReaderThreadExecutionEnforcer.get();
+ }
+ std::unique_ptr<ReaderThreadExecutionEnforcer> ownedReaderThreadExecutionEnforcer;
+#endif
#if QT_CONFIG(qml_network)
QNetworkAccessManager *networkAccessManager();
@@ -198,20 +277,33 @@ private:
#endif
QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses;
- static int replyDownloadProgress;
- static int replyFinished;
- static int downloadProgress;
- static int threadNetworkRequestDone;
+ Q_THREAD_AFFINITY_MARKER(m_creatorThreadAffinityMarker);
+ Q_THREAD_AFFINITY_MARKER(m_readerThreadAffinityMarker);
+
+ static int replyDownloadProgressMethodIndex;
+ static int replyFinishedMethodIndex;
+ static int downloadProgressMethodIndex;
+ static int threadNetworkRequestDoneMethodIndex;
static QHash<QQmlEngine *,QQuickPixmapReader*> readers;
public:
static QMutex readerMutex;
};
-class QQuickPixmapStore;
+#if QT_CONFIG(quick_pixmap_cache_threaded_download)
+# define PIXMAP_READER_LOCK() QMutexLocker locker(&mutex)
+#else
+# define PIXMAP_READER_LOCK()
+#endif
+
+class QQuickPixmapCache;
+
+/*! \internal
+ The private storage for QQuickPixmap.
+*/
class QQuickPixmapData
{
public:
- QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QRect &r, const QSize &rs,
+ QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &rs,
const QQuickImageProviderOptions &po, const QString &e)
: refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Error),
url(u), errorString(e), requestRegion(r), requestSize(rs),
@@ -222,10 +314,9 @@ public:
, storeToCache(true)
#endif
{
- declarativePixmaps.insert(pixmap);
}
- QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QRect &r, const QSize &s, const QQuickImageProviderOptions &po,
+ QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &s, const QQuickImageProviderOptions &po,
QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1)
: refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Loading),
url(u), requestRegion(r), requestSize(s),
@@ -236,10 +327,9 @@ public:
, storeToCache(true)
#endif
{
- declarativePixmaps.insert(pixmap);
}
- QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, QQuickTextureFactory *texture,
+ QQuickPixmapData(const QUrl &u, QQuickTextureFactory *texture,
const QSize &s, const QRect &r, const QSize &rs, const QQuickImageProviderOptions &po,
QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1)
: refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Ready),
@@ -251,10 +341,9 @@ public:
, storeToCache(true)
#endif
{
- declarativePixmaps.insert(pixmap);
}
- QQuickPixmapData(QQuickPixmap *pixmap, QQuickTextureFactory *texture)
+ QQuickPixmapData(QQuickTextureFactory *texture)
: refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Ready),
appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform),
textureFactory(texture), reply(nullptr), prevUnreferenced(nullptr),
@@ -265,24 +354,18 @@ public:
{
if (texture)
requestSize = implicitSize = texture->textureSize();
- declarativePixmaps.insert(pixmap);
}
~QQuickPixmapData()
{
- while (!declarativePixmaps.isEmpty()) {
- QQuickPixmap *referencer = declarativePixmaps.first();
- declarativePixmaps.remove(referencer);
- referencer->d = nullptr;
- }
delete textureFactory;
}
int cost() const;
void addref();
- void release(QQuickPixmapStore *store = nullptr);
+ void release(QQuickPixmapCache *store = nullptr);
void addToCache();
- void removeFromCache(QQuickPixmapStore *store = nullptr);
+ void removeFromCache(QQuickPixmapCache *store = nullptr);
uint refCount;
int frameCount;
@@ -301,11 +384,14 @@ public:
QColorSpace targetColorSpace;
QIODevice *specialDevice = nullptr;
+
+ // actual image data, after loading
QQuickTextureFactory *textureFactory;
- QIntrusiveList<QQuickPixmap, &QQuickPixmap::dataListNode> declarativePixmaps;
QQuickPixmapReply *reply;
+ // prev/next pointers to form a linked list for dereferencing pixmaps that are currently unused
+ // (those get lazily deleted in QQuickPixmapCache::shrinkCache())
QQuickPixmapData *prevUnreferenced;
QQuickPixmapData**prevUnreferencedPtr;
QQuickPixmapData *nextUnreferenced;
@@ -313,20 +399,22 @@ public:
#ifdef Q_OS_WEBOS
bool storeToCache;
#endif
+
+private:
+ Q_DISABLE_COPY(QQuickPixmapData)
};
-int QQuickPixmapReply::finishedIndex = -1;
-int QQuickPixmapReply::downloadProgressIndex = -1;
+int QQuickPixmapReply::finishedMethodIndex = -1;
+int QQuickPixmapReply::downloadProgressMethodIndex = -1;
// XXX
QHash<QQmlEngine *,QQuickPixmapReader*> QQuickPixmapReader::readers;
QMutex QQuickPixmapReader::readerMutex;
-int QQuickPixmapReader::replyDownloadProgress = -1;
-int QQuickPixmapReader::replyFinished = -1;
-int QQuickPixmapReader::downloadProgress = -1;
-int QQuickPixmapReader::threadNetworkRequestDone = -1;
-
+int QQuickPixmapReader::replyDownloadProgressMethodIndex = -1;
+int QQuickPixmapReader::replyFinishedMethodIndex = -1;
+int QQuickPixmapReader::downloadProgressMethodIndex = -1;
+int QQuickPixmapReader::threadNetworkRequestDoneMethodIndex = -1;
void QQuickPixmapReply::postReply(ReadError error, const QString &errorString,
const QSize &implicitSize, QQuickTextureFactory *factory)
@@ -349,8 +437,9 @@ QQuickPixmapReply::Event::~Event()
QNetworkAccessManager *QQuickPixmapReader::networkAccessManager()
{
if (!accessManager) {
- Q_ASSERT(threadObject);
- accessManager = QQmlEnginePrivate::get(engine)->createNetworkAccessManager(threadObject);
+ Q_ASSERT(readerThreadExecutionEnforcer());
+ accessManager = QQmlEnginePrivate::get(engine)->createNetworkAccessManager(
+ readerThreadExecutionEnforcer());
}
return accessManager;
}
@@ -463,6 +552,8 @@ public:
}
bool hasOpenGL;
QStringList fileSuffixes;
+private:
+ Q_DISABLE_COPY(BackendSupport)
};
Q_GLOBAL_STATIC(BackendSupport, backendSupport);
@@ -489,10 +580,11 @@ QQuickPixmapReader::QQuickPixmapReader(QQmlEngine *eng)
, accessManager(nullptr)
#endif
{
+ Q_DETACH_THREAD_AFFINITY_MARKER(m_readerThreadAffinityMarker);
+#if QT_CONFIG(quick_pixmap_cache_threaded_download)
eventLoopQuitHack = new QObject;
eventLoopQuitHack->moveToThread(this);
- connect(eventLoopQuitHack, SIGNAL(destroyed(QObject *)), SLOT(quit()), Qt::DirectConnection);
-#if USE_THREADED_DOWNLOAD
+ QObject::connect(eventLoopQuitHack, &QObject::destroyed, this, &QThread::quit, Qt::DirectConnection);
start(QThread::LowestPriority);
#else
run(); // Call nonblocking run for ourselves.
@@ -501,38 +593,54 @@ QQuickPixmapReader::QQuickPixmapReader(QQmlEngine *eng)
QQuickPixmapReader::~QQuickPixmapReader()
{
+ Q_ASSERT_CALLED_ON_VALID_THREAD(m_creatorThreadAffinityMarker);
+
readerMutex.lock();
readers.remove(engine);
readerMutex.unlock();
- mutex.lock();
- // manually cancel all outstanding jobs.
- for (QQuickPixmapReply *reply : std::as_const(jobs)) {
- if (reply->data && reply->data->reply == reply)
- reply->data->reply = nullptr;
- delete reply;
- }
- jobs.clear();
-#if QT_CONFIG(qml_network)
-
- const auto cancelJob = [this](QQuickPixmapReply *reply) {
- if (reply->loading) {
- cancelled.append(reply);
- reply->data = nullptr;
+ {
+ PIXMAP_READER_LOCK();
+ // manually cancel all outstanding jobs.
+ for (QQuickPixmapReply *reply : std::as_const(jobs)) {
+ if (reply->data && reply->data->reply == reply)
+ reply->data->reply = nullptr;
+ delete reply;
}
- };
+ jobs.clear();
+#if QT_CONFIG(qml_network)
+ const auto cancelJob = [this](QQuickPixmapReply *reply) {
+ if (reply->loading) {
+ cancelledJobs.append(reply);
+ reply->data = nullptr;
+ }
+ };
- for (auto *reply : std::as_const(networkJobs))
- cancelJob(reply);
+ for (auto *reply : std::as_const(networkJobs))
+ cancelJob(reply);
- for (auto *reply : std::as_const(asyncResponses))
- cancelJob(reply);
+ for (auto *reply : std::as_const(asyncResponses))
+ cancelJob(reply);
+#endif
+#if !QT_CONFIG(quick_pixmap_cache_threaded_download)
+ // In this case we won't be waiting, but we are on the correct thread already, so we can
+ // perform housekeeping synchronously now.
+ processJobs();
+#else // QT_CONFIG(quick_pixmap_cache_threaded_download) is true
+ // Perform housekeeping on all the jobs cancelled above soon...
+ if (readerThreadExecutionEnforcer())
+ readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
#endif
- if (threadObject) threadObject->processJobs();
- mutex.unlock();
+ }
+#if QT_CONFIG(quick_pixmap_cache_threaded_download)
+ // ... schedule stopping of this thread via the eventLoopQuitHack (processJobs scheduled above
+ // will run first) ...
eventLoopQuitHack->deleteLater();
+ // ... and wait() for it all to finish, as the thread will only quit after eventLoopQuitHack
+ // has been deleted.
wait();
+#endif
#if QT_CONFIG(qml_network)
// While we've been waiting, the other thread may have added
@@ -558,28 +666,11 @@ QQuickPixmapReader::~QQuickPixmapReader()
#if QT_CONFIG(qml_network)
void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
{
+ Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
+
QQuickPixmapReply *job = networkJobs.take(reply);
if (job) {
- job->redirectCount++;
- if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) {
- QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = reply->url().resolved(redirect.toUrl());
- QNetworkRequest req(url);
- req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
-
- reply->deleteLater();
- reply = networkAccessManager()->get(req);
-
- QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
- QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
-
- networkJobs.insert(reply, job);
- return;
- }
- }
-
QImage image;
QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
QString errorString;
@@ -614,20 +705,22 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
// send completion event to the QQuickPixmapReply
if (!factory)
factory = QQuickTextureFactory::textureFactoryForImage(image);
- mutex.lock();
- if (!cancelled.contains(job))
+
+ PIXMAP_READER_LOCK();
+ if (!cancelledJobs.contains(job))
job->postReply(error, errorString, readSize, factory);
- mutex.unlock();
}
reply->deleteLater();
- // kick off event loop again incase we have dropped below max request count
- threadObject->processJobs();
+ // kick off event loop again in case we have dropped below max request count
+ readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
}
#endif // qml_network
void QQuickPixmapReader::asyncResponseFinished(QQuickImageResponse *response)
{
+ Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
+
QQuickPixmapReply *job = asyncResponses.take(response);
if (job) {
@@ -639,41 +732,40 @@ void QQuickPixmapReader::asyncResponseFinished(QQuickImageResponse *response)
errorString = response->errorString();
} else {
t = response->textureFactory();
- }
- mutex.lock();
- if (!cancelled.contains(job))
+ }
+
+ PIXMAP_READER_LOCK();
+ if (!cancelledJobs.contains(job))
job->postReply(error, errorString, t ? t->textureSize() : QSize(), t);
else
delete t;
- mutex.unlock();
}
response->deleteLater();
- // kick off event loop again incase we have dropped below max request count
- threadObject->processJobs();
+ // kick off event loop again in case we have dropped below max request count
+ readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
}
-QQuickPixmapReaderThreadObject::QQuickPixmapReaderThreadObject(QQuickPixmapReader *i)
-: reader(i)
-{
-}
+ReaderThreadExecutionEnforcer::ReaderThreadExecutionEnforcer(QQuickPixmapReader *i) : reader(i) { }
-void QQuickPixmapReaderThreadObject::processJobs()
+void ReaderThreadExecutionEnforcer::processJobsOnReaderThreadLater()
{
- QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+ QCoreApplication::postEvent(
+ this, new QEvent(QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs)));
}
-bool QQuickPixmapReaderThreadObject::event(QEvent *e)
+bool ReaderThreadExecutionEnforcer::event(QEvent *e)
{
- if (e->type() == QEvent::User) {
+ switch (e->type()) {
+ case QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs):
reader->processJobs();
return true;
- } else {
+ default:
return QObject::event(e);
}
}
-void QQuickPixmapReaderThreadObject::networkRequestDone()
+void ReaderThreadExecutionEnforcer::networkRequestDone()
{
#if QT_CONFIG(qml_network)
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
@@ -681,12 +773,12 @@ void QQuickPixmapReaderThreadObject::networkRequestDone()
#endif
}
-void QQuickPixmapReaderThreadObject::asyncResponseFinished(QQuickImageResponse *response)
+void ReaderThreadExecutionEnforcer::asyncResponseFinished(QQuickImageResponse *response)
{
reader->asyncResponseFinished(response);
}
-void QQuickPixmapReaderThreadObject::asyncResponseFinished()
+void ReaderThreadExecutionEnforcer::asyncResponseFinished()
{
QQuickImageResponse *response = static_cast<QQuickImageResponse *>(sender());
asyncResponseFinished(response);
@@ -694,16 +786,17 @@ void QQuickPixmapReaderThreadObject::asyncResponseFinished()
void QQuickPixmapReader::processJobs()
{
- QMutexLocker locker(&mutex);
+ Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
+ PIXMAP_READER_LOCK();
while (true) {
- if (cancelled.isEmpty() && jobs.isEmpty())
+ if (cancelledJobs.isEmpty() && jobs.isEmpty())
return; // Nothing else to do
// Clean cancelled jobs
- if (!cancelled.isEmpty()) {
- for (int i = 0; i < cancelled.size(); ++i) {
- QQuickPixmapReply *job = cancelled.at(i);
+ if (!cancelledJobs.isEmpty()) {
+ for (int i = 0; i < cancelledJobs.size(); ++i) {
+ QQuickPixmapReply *job = cancelledJobs.at(i);
#if QT_CONFIG(qml_network)
QNetworkReply *reply = networkJobs.key(job, 0);
if (reply) {
@@ -724,7 +817,7 @@ void QQuickPixmapReader::processJobs()
// deleteLater, since not owned by this thread
job->deleteLater();
}
- cancelled.clear();
+ cancelledJobs.clear();
}
if (!jobs.isEmpty()) {
@@ -753,7 +846,6 @@ void QQuickPixmapReader::processJobs()
;
}
-
if (usableJob) {
jobs.removeAt(i);
@@ -761,9 +853,13 @@ void QQuickPixmapReader::processJobs()
PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
+#if QT_CONFIG(quick_pixmap_cache_threaded_download)
locker.unlock();
+ auto relockMutexGuard = qScopeGuard(([&locker]() {
+ locker.relock();
+ }));
+#endif
processJob(job, url, localFile, imageType, provider);
- locker.relock();
}
}
@@ -776,6 +872,8 @@ void QQuickPixmapReader::processJobs()
void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, const QString &localFile,
QQuickImageProvider::ImageType imageType, const QSharedPointer<QQuickImageProvider> &provider)
{
+ Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
+
// fetch
if (url.scheme() == QLatin1String("image")) {
// Use QQuickImageProvider
@@ -783,10 +881,9 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
if (imageType == QQuickImageProvider::Invalid) {
QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString());
- mutex.lock();
- if (!cancelled.contains(runningJob))
+ PIXMAP_READER_LOCK();
+ if (!cancelledJobs.contains(runningJob))
runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize, nullptr);
- mutex.unlock();
return;
}
@@ -814,10 +911,11 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
errorCode = QQuickPixmapReply::Loading;
errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString());
}
- mutex.lock();
- if (!cancelled.contains(runningJob))
- runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image));
- mutex.unlock();
+ PIXMAP_READER_LOCK();
+ if (!cancelledJobs.contains(runningJob)) {
+ runningJob->postReply(errorCode, errorStr, readSize,
+ QQuickTextureFactory::textureFactoryForImage(image));
+ }
break;
}
@@ -835,10 +933,13 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
errorCode = QQuickPixmapReply::Loading;
errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString());
}
- mutex.lock();
- if (!cancelled.contains(runningJob))
- runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()));
- mutex.unlock();
+
+ PIXMAP_READER_LOCK();
+ if (!cancelledJobs.contains(runningJob)) {
+ runningJob->postReply(
+ errorCode, errorStr, readSize,
+ QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()));
+ }
break;
}
@@ -856,12 +957,11 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
errorCode = QQuickPixmapReply::Loading;
errorStr = QQuickPixmap::tr("Failed to get texture from provider: %1").arg(url.toString());
}
- mutex.lock();
- if (!cancelled.contains(runningJob))
+ PIXMAP_READER_LOCK();
+ if (!cancelledJobs.contains(runningJob))
runningJob->postReply(errorCode, errorStr, readSize, t);
else
delete t;
- mutex.unlock();
break;
}
@@ -876,7 +976,8 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
}
{
- QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished()));
+ QObject::connect(response, &QQuickImageResponse::finished, readerThreadExecutionEnforcer(),
+ qOverload<>(&ReaderThreadExecutionEnforcer::asyncResponseFinished));
// as the response object can outlive the provider QSharedPointer, we have to extend the pointee's lifetime by that of the response
// we do this by capturing a copy of the QSharedPointer in a lambda, and dropping it once the lambda has been called
auto provider_copy = provider; // capturing provider would capture it as a const reference, and copy capture with initializer is only available in C++14
@@ -889,8 +990,9 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
//
// loadAcquire() synchronizes-with storeRelease() in QQuickImageResponsePrivate::_q_finished():
if (static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished.loadAcquire()) {
- QMetaObject::invokeMethod(threadObject, "asyncResponseFinished",
- Qt::QueuedConnection, Q_ARG(QQuickImageResponse*, response));
+ QMetaObject::invokeMethod(readerThreadExecutionEnforcer(), "asyncResponseFinished",
+ Qt::QueuedConnection,
+ Q_ARG(QQuickImageResponse *, response));
}
asyncResponses.insert(response, runningJob);
@@ -929,10 +1031,9 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
errorCode = QQuickPixmapReply::Decoding;
}
- mutex.lock();
- if (!cancelled.contains(runningJob))
+ PIXMAP_READER_LOCK();
+ if (!cancelledJobs.contains(runningJob))
runningJob->postReply(errorCode, errorStr, readSize, factory);
- mutex.unlock();
return;
} else {
int frameCount;
@@ -952,10 +1053,11 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
errorCode = QQuickPixmapReply::Loading;
}
}
- mutex.lock();
- if (!cancelled.contains(runningJob))
- runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image));
- mutex.unlock();
+ PIXMAP_READER_LOCK();
+ if (!cancelledJobs.contains(runningJob)) {
+ runningJob->postReply(errorCode, errorStr, readSize,
+ QQuickTextureFactory::textureFactoryForImage(image));
+ }
} else {
#if QT_CONFIG(qml_network)
// Network resource
@@ -963,8 +1065,10 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
QNetworkReply *reply = networkAccessManager()->get(req);
- QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
- QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
+ QMetaObject::connect(reply, replyDownloadProgressMethodIndex, runningJob,
+ downloadProgressMethodIndex);
+ QMetaObject::connect(reply, replyFinishedMethodIndex, readerThreadExecutionEnforcer(),
+ threadNetworkRequestDoneMethodIndex);
networkJobs.insert(reply, runningJob);
#else
@@ -994,24 +1098,28 @@ QQuickPixmapReader *QQuickPixmapReader::existingInstance(QQmlEngine *engine)
QQuickPixmapReply *QQuickPixmapReader::getImage(QQuickPixmapData *data)
{
- mutex.lock();
QQuickPixmapReply *reply = new QQuickPixmapReply(data);
reply->engineForReader = engine;
- jobs.append(reply);
- // XXX
- if (threadObject) threadObject->processJobs();
- mutex.unlock();
return reply;
}
+void QQuickPixmapReader::startJob(QQuickPixmapReply *job)
+{
+ PIXMAP_READER_LOCK();
+ jobs.append(job);
+ if (readerThreadExecutionEnforcer())
+ readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
+}
+
void QQuickPixmapReader::cancel(QQuickPixmapReply *reply)
{
- mutex.lock();
+ PIXMAP_READER_LOCK();
if (reply->loading) {
- cancelled.append(reply);
+ cancelledJobs.append(reply);
reply->data = nullptr;
// XXX
- if (threadObject) threadObject->processJobs();
+ if (readerThreadExecutionEnforcer())
+ readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
} else {
// If loading was started (reply removed from jobs) but the reply was never processed
// (otherwise it would have deleted itself) we need to profile an error.
@@ -1020,50 +1128,46 @@ void QQuickPixmapReader::cancel(QQuickPixmapReply *reply)
}
delete reply;
}
- mutex.unlock();
}
void QQuickPixmapReader::run()
{
- if (replyDownloadProgress == -1) {
+ Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
+
+ if (replyDownloadProgressMethodIndex == -1) {
#if QT_CONFIG(qml_network)
- replyDownloadProgress = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
- replyFinished = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
- const QMetaObject *ir = &QQuickPixmapReaderThreadObject::staticMetaObject;
- threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
+ replyDownloadProgressMethodIndex =
+ QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
+ replyFinishedMethodIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
+ const QMetaObject *ir = &ReaderThreadExecutionEnforcer::staticMetaObject;
+ threadNetworkRequestDoneMethodIndex = ir->indexOfSlot("networkRequestDone()");
#endif
- downloadProgress = QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
+ downloadProgressMethodIndex =
+ QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
}
+#if QT_CONFIG(quick_pixmap_cache_threaded_download)
const auto guard = qScopeGuard([this]() {
- // We need to delete the threadObject from the same thread.
- QMutexLocker lock(&mutex);
- delete threadObject;
- threadObject = nullptr;
+ // We need to delete the runLoopReaderThreadExecutionEnforcer from the same thread.
+ PIXMAP_READER_LOCK();
+ delete runLoopReaderThreadExecutionEnforcer;
+ runLoopReaderThreadExecutionEnforcer = nullptr;
});
{
- QMutexLocker lock(&mutex);
- Q_ASSERT(!threadObject);
- threadObject = new QQuickPixmapReaderThreadObject(this);
+ PIXMAP_READER_LOCK();
+ Q_ASSERT(!runLoopReaderThreadExecutionEnforcer);
+ runLoopReaderThreadExecutionEnforcer = new ReaderThreadExecutionEnforcer(this);
}
processJobs();
-#if USE_THREADED_DOWNLOAD
exec();
+#else
+ ownedReaderThreadExecutionEnforcer = std::make_unique<ReaderThreadExecutionEnforcer>(this);
+ processJobs();
#endif
}
-class QQuickPixmapKey
-{
-public:
- const QUrl *url;
- const QRect *region;
- const QSize *size;
- int frame;
- QQuickImageProviderOptions options;
-};
-
inline bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs)
{
return *lhs.url == *rhs.url &&
@@ -1078,62 +1182,70 @@ inline size_t qHash(const QQuickPixmapKey &key, size_t seed) noexcept
return qHashMulti(seed, *key.url, *key.region, *key.size, key.frame, key.options.autoTransform());
}
-class QQuickPixmapStore : public QObject
+#ifndef QT_NO_DEBUG_STREAM
+inline QDebug operator<<(QDebug debug, const QQuickPixmapKey &key)
{
- Q_OBJECT
-public:
- QQuickPixmapStore();
- ~QQuickPixmapStore();
-
- void unreferencePixmap(QQuickPixmapData *);
- void referencePixmap(QQuickPixmapData *);
-
- void purgeCache();
-
-protected:
- void timerEvent(QTimerEvent *) override;
-
-public:
- QHash<QQuickPixmapKey, QQuickPixmapData *> m_cache;
-
-private:
- void shrinkCache(int remove);
-
- QQuickPixmapData *m_unreferencedPixmaps;
- QQuickPixmapData *m_lastUnreferencedPixmap;
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ if (!key.url) {
+ debug << "QQuickPixmapKey(0)";
+ return debug;
+ }
- int m_unreferencedCost;
- int m_timerId;
- bool m_destroying;
-};
-Q_GLOBAL_STATIC(QQuickPixmapStore, pixmapStore);
+ debug << "QQuickPixmapKey(" << key.url->toString() << " frame=" << key.frame;
+ if (!key.region->isEmpty()) {
+ debug << " region=";
+ QtDebugUtils::formatQRect(debug, *key.region);
+ }
+ if (!key.size->isEmpty()) {
+ debug << " size=";
+ QtDebugUtils::formatQSize(debug, *key.size);
+ }
+ debug << ')';
+ return debug;
+}
+#endif
+QQuickPixmapCache *QQuickPixmapCache::instance()
+{
+ static QQuickPixmapCache self;
+ return &self;
+}
-QQuickPixmapStore::QQuickPixmapStore()
- : m_unreferencedPixmaps(nullptr), m_lastUnreferencedPixmap(nullptr), m_unreferencedCost(0), m_timerId(-1), m_destroying(false)
+QQuickPixmapCache::~QQuickPixmapCache()
{
+ destroyCache();
}
-QQuickPixmapStore::~QQuickPixmapStore()
+/*! \internal
+ Empty the cache completely, to prevent leaks. Returns the number of
+ leaked pixmaps (should always be \c 0).
+
+ This is work the destructor needs to do, but we put it into a function
+ only to make it testable in autotests, because the static instance()
+ cannot be destroyed before shutdown.
+*/
+int QQuickPixmapCache::destroyCache()
{
+ if (m_destroying)
+ return -1;
+
m_destroying = true;
-#ifndef QT_NO_DEBUG
- int leakedPixmaps = 0;
-#endif
// Prevent unreferencePixmap() from assuming it needs to kick
// off the cache expiry timer, as we're shrinking the cache
// manually below after releasing all the pixmaps.
m_timerId = -2;
// unreference all (leaked) pixmaps
+ int leakedPixmaps = 0;
const auto cache = m_cache; // NOTE: intentional copy (QTBUG-65077); releasing items from the cache modifies m_cache.
for (auto *pixmap : cache) {
- int currRefCount = pixmap->refCount;
+ auto currRefCount = pixmap->refCount;
if (currRefCount) {
-#ifndef QT_NO_DEBUG
leakedPixmaps++;
-#endif
+ qCDebug(lcQsgLeak) << "leaked pixmap: refCount" << pixmap->refCount << pixmap->url << "frame" << pixmap->frame
+ << "size" << pixmap->requestSize << "region" << pixmap->requestRegion;
while (currRefCount > 0) {
pixmap->release(this);
currRefCount--;
@@ -1142,17 +1254,29 @@ QQuickPixmapStore::~QQuickPixmapStore()
}
// free all unreferenced pixmaps
- while (m_lastUnreferencedPixmap) {
+ while (m_lastUnreferencedPixmap)
shrinkCache(20);
- }
-#ifndef QT_NO_DEBUG
- if (leakedPixmaps && qsg_leak_check)
- qDebug("Number of leaked pixmaps: %i", leakedPixmaps);
-#endif
+ qCDebug(lcQsgLeak, "Number of leaked pixmaps: %i", leakedPixmaps);
+ return leakedPixmaps;
+}
+
+qsizetype QQuickPixmapCache::referencedCost() const
+{
+ qsizetype ret = 0;
+ QMutexLocker locker(&m_cacheMutex);
+ for (const auto *pixmap : std::as_const(m_cache)) {
+ if (pixmap->refCount)
+ ret += pixmap->cost();
+ }
+ return ret;
}
-void QQuickPixmapStore::unreferencePixmap(QQuickPixmapData *data)
+/*! \internal
+ Declare that \a data is currently unused so that shrinkCache() can lazily
+ delete it later.
+*/
+void QQuickPixmapCache::unreferencePixmap(QQuickPixmapData *data)
{
Q_ASSERT(data->prevUnreferenced == nullptr);
Q_ASSERT(data->prevUnreferencedPtr == nullptr);
@@ -1160,8 +1284,10 @@ void QQuickPixmapStore::unreferencePixmap(QQuickPixmapData *data)
data->nextUnreferenced = m_unreferencedPixmaps;
data->prevUnreferencedPtr = &m_unreferencedPixmaps;
- if (!m_destroying) // the texture factories may have been cleaned up already.
+ if (!m_destroying) { // the texture factories may have been cleaned up already.
m_unreferencedCost += data->cost();
+ qCDebug(lcImg) << data->url << "had cost" << data->cost() << "of total unreferenced" << m_unreferencedCost;
+ }
m_unreferencedPixmaps = data;
if (m_unreferencedPixmaps->nextUnreferenced) {
@@ -1180,7 +1306,11 @@ void QQuickPixmapStore::unreferencePixmap(QQuickPixmapData *data)
}
}
-void QQuickPixmapStore::referencePixmap(QQuickPixmapData *data)
+/*! \internal
+ Declare that \a data is being used (by a QQuickPixmap) so that
+ shrinkCache() won't delete it. (This is not reference counting though.)
+*/
+void QQuickPixmapCache::referencePixmap(QQuickPixmapData *data)
{
Q_ASSERT(data->prevUnreferencedPtr);
@@ -1197,10 +1327,16 @@ void QQuickPixmapStore::referencePixmap(QQuickPixmapData *data)
data->prevUnreferenced = nullptr;
m_unreferencedCost -= data->cost();
+ qCDebug(lcImg) << data->url << "subtracts cost" << data->cost() << "of total" << m_unreferencedCost;
}
-void QQuickPixmapStore::shrinkCache(int remove)
+/*! \internal
+ Delete the least-recently-released QQuickPixmapData instances
+ until the remaining bytes are less than cache_limit.
+*/
+void QQuickPixmapCache::shrinkCache(int remove)
{
+ qCDebug(lcImg) << "reduce unreferenced cost" << m_unreferencedCost << "to less than limit" << cache_limit;
while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
QQuickPixmapData *data = m_lastUnreferencedPixmap;
Q_ASSERT(data->nextUnreferenced == nullptr);
@@ -1219,7 +1355,7 @@ void QQuickPixmapStore::shrinkCache(int remove)
}
}
-void QQuickPixmapStore::timerEvent(QTimerEvent *)
+void QQuickPixmapCache::timerEvent(QTimerEvent *)
{
int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
@@ -1231,23 +1367,24 @@ void QQuickPixmapStore::timerEvent(QTimerEvent *)
}
}
-void QQuickPixmapStore::purgeCache()
+void QQuickPixmapCache::purgeCache()
{
shrinkCache(m_unreferencedCost);
}
void QQuickPixmap::purgeCache()
{
- pixmapStore()->purgeCache();
+ QQuickPixmapCache::instance()->purgeCache();
}
QQuickPixmapReply::QQuickPixmapReply(QQuickPixmapData *d)
: data(d), engineForReader(nullptr), requestRegion(d->requestRegion), requestSize(d->requestSize),
- url(d->url), loading(false), providerOptions(d->providerOptions), redirectCount(0)
+ url(d->url), loading(false), providerOptions(d->providerOptions)
{
- if (finishedIndex == -1) {
- finishedIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex();
- downloadProgressIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
+ if (finishedMethodIndex == -1) {
+ finishedMethodIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex();
+ downloadProgressMethodIndex =
+ QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
}
}
@@ -1303,10 +1440,10 @@ void QQuickPixmapData::addref()
++refCount;
PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
if (prevUnreferencedPtr)
- pixmapStore()->referencePixmap(this);
+ QQuickPixmapCache::instance()->referencePixmap(this);
}
-void QQuickPixmapData::release(QQuickPixmapStore *store)
+void QQuickPixmapData::release(QQuickPixmapCache *store)
{
Q_ASSERT(refCount > 0);
--refCount;
@@ -1323,7 +1460,7 @@ void QQuickPixmapData::release(QQuickPixmapStore *store)
QQuickPixmapReader::readerMutex.unlock();
}
- store = store ? store : pixmapStore();
+ store = store ? store : QQuickPixmapCache::instance();
if (pixmapStatus == QQuickPixmap::Ready
#ifdef Q_OS_WEBOS
&& storeToCache
@@ -1340,32 +1477,52 @@ void QQuickPixmapData::release(QQuickPixmapStore *store)
}
}
+/*! \internal
+ Add this to the QQuickPixmapCache singleton.
+
+ \note The actual image will end up in QQuickPixmapData::textureFactory.
+ At the time addToCache() is called, it's generally not yet loaded; so the
+ qCDebug() below cannot say how much data we're committing to storing.
+ (On the other hand, removeFromCache() can tell.) QQuickTextureFactory is an
+ abstraction for image data. See QQuickDefaultTextureFactory for example:
+ it stores a QImage directly. Other QQuickTextureFactory subclasses store data
+ in other ways.
+*/
void QQuickPixmapData::addToCache()
{
if (!inCache) {
QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
- pixmapStore()->m_cache.insert(key, this);
+ QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
+ if (lcImg().isDebugEnabled()) {
+ qCDebug(lcImg) << "adding" << key << "to total" << QQuickPixmapCache::instance()->m_cache.size();
+ for (auto it = QQuickPixmapCache::instance()->m_cache.keyBegin(); it != QQuickPixmapCache::instance()->m_cache.keyEnd(); ++it) {
+ if (*(it->url) == url && it->frame == frame)
+ qDebug(lcImg) << " similar pre-existing:" << *it;
+ }
+ }
+ QQuickPixmapCache::instance()->m_cache.insert(key, this);
inCache = true;
PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
- url, pixmapStore()->m_cache.size()));
+ url, QQuickPixmapCache::instance()->m_cache.size()));
}
}
-void QQuickPixmapData::removeFromCache(QQuickPixmapStore *store)
+void QQuickPixmapData::removeFromCache(QQuickPixmapCache *store)
{
-
if (inCache) {
if (!store)
- store = pixmapStore();
+ store = QQuickPixmapCache::instance();
QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
+ QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
store->m_cache.remove(key);
+ qCDebug(lcImg) << "removed" << key << implicitSize << "; total remaining" << QQuickPixmapCache::instance()->m_cache.size();
inCache = false;
PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
url, store->m_cache.size()));
}
}
-static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url,
+static QQuickPixmapData* createPixmapDataSync(QQmlEngine *engine, const QUrl &url,
const QRect &requestRegion, const QSize &requestSize,
const QQuickImageProviderOptions &providerOptions, int frame, bool *ok,
qreal devicePixelRatio)
@@ -1383,7 +1540,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
switch (imageType) {
case QQuickImageProvider::Invalid:
- return new QQuickPixmapData(declarativePixmap, url, requestRegion, requestSize, providerOptions,
+ return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()));
case QQuickImageProvider::Texture:
{
@@ -1391,7 +1548,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
: provider->requestTexture(imageId(url), &readSize, requestSize);
if (texture) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestRegion, requestSize,
+ return new QQuickPixmapData(url, texture, readSize, requestRegion, requestSize,
providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
}
break;
@@ -1403,7 +1560,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
: provider->requestImage(imageId(url), &readSize, requestSize);
if (!image.isNull()) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image),
+ return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image),
readSize, requestRegion, requestSize, providerOptions,
QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
}
@@ -1415,7 +1572,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
: provider->requestPixmap(imageId(url), &readSize, requestSize);
if (!pixmap.isNull()) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()),
+ return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()),
readSize, requestRegion, requestSize, providerOptions,
QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
}
@@ -1429,7 +1586,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
}
// provider has bad image type, or provider returned null image
- return new QQuickPixmapData(declarativePixmap, url, requestRegion, requestSize, providerOptions,
+ return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
}
@@ -1447,7 +1604,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
QQuickTextureFactory *factory = texReader.read();
if (factory) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, factory, factory->textureSize(), requestRegion, requestSize,
+ return new QQuickPixmapData(url, factory, factory->textureSize(), requestRegion, requestSize,
providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
} else {
errorString = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
@@ -1461,7 +1618,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize,
providerOptions, &appliedTransform, frame, devicePixelRatio)) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
+ return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
providerOptions, appliedTransform, frame, frameCount);
} else if (f.fileName() != localFile) {
errorString += QString::fromLatin1(" (%1)").arg(f.fileName());
@@ -1470,7 +1627,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
} else {
errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
}
- return new QQuickPixmapData(declarativePixmap, url, requestRegion, requestSize, providerOptions, errorString);
+ return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions, errorString);
}
@@ -1492,6 +1649,12 @@ QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url)
load(engine, url);
}
+QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, Options options)
+: d(nullptr)
+{
+ load(engine, url, options);
+}
+
QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QRect &region, const QSize &size)
: d(nullptr)
{
@@ -1500,7 +1663,7 @@ QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QRect &reg
QQuickPixmap::QQuickPixmap(const QUrl &url, const QImage &image)
{
- d = new QQuickPixmapData(this, url, new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(),
+ d = new QQuickPixmapData(url, new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(),
QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform);
d->addToCache();
}
@@ -1508,7 +1671,6 @@ QQuickPixmap::QQuickPixmap(const QUrl &url, const QImage &image)
QQuickPixmap::~QQuickPixmap()
{
if (d) {
- d->declarativePixmaps.remove(this);
d->release();
d = nullptr;
}
@@ -1616,18 +1778,24 @@ void QQuickPixmap::setImage(const QImage &p)
{
clear();
- if (!p.isNull())
- d = new QQuickPixmapData(this, QQuickTextureFactory::textureFactoryForImage(p));
+ if (!p.isNull()) {
+ if (d)
+ d->release();
+ d = new QQuickPixmapData(QQuickTextureFactory::textureFactoryForImage(p));
+ }
}
void QQuickPixmap::setPixmap(const QQuickPixmap &other)
{
+ if (d == other.d)
+ return;
clear();
if (other.d) {
+ if (d)
+ d->release();
d = other.d;
d->addref();
- d->declarativePixmaps.insert(this);
}
}
@@ -1680,14 +1848,14 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
qreal devicePixelRatio)
{
if (d) {
- d->declarativePixmaps.remove(this);
d->release();
d = nullptr;
}
QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
- QQuickPixmapStore *store = pixmapStore();
+ QQuickPixmapCache *store = QQuickPixmapCache::instance();
+ QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
#ifdef Q_OS_WEBOS
@@ -1711,6 +1879,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
iter = store->m_cache.find(key);
if (iter == store->m_cache.end()) {
+ locker.unlock();
if (url.scheme() == QLatin1String("image")) {
QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
if (auto provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>()) {
@@ -1727,7 +1896,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
if (!(options & QQuickPixmap::Asynchronous)) {
bool ok = false;
PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
- d = createPixmapDataSync(this, engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
+ d = createPixmapDataSync(engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
if (ok) {
PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
if (options & QQuickPixmap::Cache)
@@ -1747,7 +1916,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
return;
- d = new QQuickPixmapData(this, url, requestRegion, requestSize, providerOptions,
+ d = new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
if (options & QQuickPixmap::Cache)
d->addToCache();
@@ -1756,12 +1925,14 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
#endif
QQuickPixmapReader::readerMutex.lock();
- d->reply = QQuickPixmapReader::instance(engine)->getImage(d);
+ QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
+ d->reply = reader->getImage(d);
+ reader->startJob(d->reply);
QQuickPixmapReader::readerMutex.unlock();
} else {
d = *iter;
d->addref();
- d->declarativePixmaps.insert(this);
+ qCDebug(lcImg) << "loaded from cache" << url << "frame" << frame;
}
}
@@ -1778,37 +1949,43 @@ void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device, co
{
auto oldD = d;
QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
- QQuickPixmapStore *store = pixmapStore();
+ QQuickPixmapCache *store = QQuickPixmapCache::instance();
QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
+ QMutexLocker locker(&store->m_cacheMutex);
iter = store->m_cache.find(key);
if (iter == store->m_cache.end()) {
if (!engine)
return;
- d = new QQuickPixmapData(this, url, requestRegion, requestSize, providerOptions,
+ locker.unlock();
+ d = new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
d->specialDevice = device;
d->addToCache();
QQuickPixmapReader::readerMutex.lock();
- d->reply = QQuickPixmapReader::instance(engine)->getImage(d);
+ QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
+ d->reply = reader->getImage(d);
if (oldD) {
- QObject::connect(d->reply, &QQuickPixmapReply::finished, [oldD]() {
+ QObject::connect(d->reply, &QQuickPixmapReply::destroyed, store, [oldD]() {
oldD->release();
- });
+ }, Qt::QueuedConnection);
}
+ reader->startJob(d->reply);
QQuickPixmapReader::readerMutex.unlock();
} else {
d = *iter;
d->addref();
- d->declarativePixmaps.insert(this);
+ qCDebug(lcImg) << "loaded from cache" << url << "frame" << frame << "refCount" << d->refCount;
+ locker.unlock();
+ if (oldD)
+ oldD->release();
}
}
void QQuickPixmap::clear()
{
if (d) {
- d->declarativePixmaps.remove(this);
d->release();
d = nullptr;
}
@@ -1819,7 +1996,6 @@ void QQuickPixmap::clear(QObject *obj)
if (d) {
if (d->reply)
QObject::disconnect(d->reply, nullptr, obj, nullptr);
- d->declarativePixmaps.remove(this);
d->release();
d = nullptr;
}
@@ -1829,11 +2005,22 @@ bool QQuickPixmap::isCached(const QUrl &url, const QRect &requestRegion, const Q
const int frame, const QQuickImageProviderOptions &options)
{
QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, options };
- QQuickPixmapStore *store = pixmapStore();
+ QQuickPixmapCache *store = QQuickPixmapCache::instance();
return store->m_cache.contains(key);
}
+bool QQuickPixmap::isScalableImageFormat(const QUrl &url)
+{
+ if (url.scheme() == "image"_L1)
+ return true;
+
+ const QString stringUrl = url.path(QUrl::PrettyDecoded);
+ return stringUrl.endsWith("svg"_L1)
+ || stringUrl.endsWith("svgz"_L1)
+ || stringUrl.endsWith("pdf"_L1);
+}
+
bool QQuickPixmap::connectFinished(QObject *object, const char *method)
{
if (!d || !d->reply) {
@@ -1851,7 +2038,7 @@ bool QQuickPixmap::connectFinished(QObject *object, int method)
return false;
}
- return QMetaObject::connect(d->reply, QQuickPixmapReply::finishedIndex, object, method);
+ return QMetaObject::connect(d->reply, QQuickPixmapReply::finishedMethodIndex, object, method);
}
bool QQuickPixmap::connectDownloadProgress(QObject *object, const char *method)
@@ -1861,7 +2048,8 @@ bool QQuickPixmap::connectDownloadProgress(QObject *object, const char *method)
return false;
}
- return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
+ return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object,
+ method);
}
bool QQuickPixmap::connectDownloadProgress(QObject *object, int method)
@@ -1871,7 +2059,8 @@ bool QQuickPixmap::connectDownloadProgress(QObject *object, int method)
return false;
}
- return QMetaObject::connect(d->reply, QQuickPixmapReply::downloadProgressIndex, object, method);
+ return QMetaObject::connect(d->reply, QQuickPixmapReply::downloadProgressMethodIndex, object,
+ method);
}
QColorSpace QQuickPixmap::colorSpace() const
diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h
index ed6422a2bf..8c0c085b50 100644
--- a/src/quick/util/qquickpixmapcache_p.h
+++ b/src/quick/util/qquickpixmapcache_p.h
@@ -15,176 +15,72 @@
// We mean it.
//
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qstring.h>
-#include <QtGui/qpixmap.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qurl.h>
-#include <private/qtquickglobal_p.h>
#include <QtQuick/qquickimageprovider.h>
-
-#include <private/qintrusivelist_p.h>
+#include <private/qquickpixmap_p.h>
QT_BEGIN_NAMESPACE
-class QQmlEngine;
class QQuickPixmapData;
-class QQuickTextureFactory;
-class QQuickImageProviderOptionsPrivate;
-class QQuickDefaultTextureFactory : public QQuickTextureFactory
+/*! \internal
+ A composite key to identify a QQuickPixmapData instance in a QHash.
+*/
+struct Q_AUTOTEST_EXPORT QQuickPixmapKey
{
- Q_OBJECT
-public:
- QQuickDefaultTextureFactory(const QImage &i);
- QSGTexture *createTexture(QQuickWindow *window) const override;
- QSize textureSize() const override { return size; }
- int textureByteCount() const override { return size.width() * size.height() * 4; }
- QImage image() const override { return im; }
-
-private:
- QImage im;
- QSize size;
+ const QUrl *url;
+ const QRect *region;
+ const QSize *size;
+ int frame;
+ QQuickImageProviderOptions options;
};
-class QQuickImageProviderPrivate
+/*! \internal
+ A singleton managing the storage and garbage collection of QQuickPixmapData
+ instances. It's exported only for the sake of autotests, and should not be
+ manipulated outside qquickpixmap.cpp and qquickpixmapcache.cpp.
+*/
+class Q_AUTOTEST_EXPORT QQuickPixmapCache : public QObject
{
-public:
- QQuickImageProvider::ImageType type;
- QQuickImageProvider::Flags flags;
- bool isProviderWithOptions;
-};
+ Q_OBJECT
-// ### Qt 6: Make public moving to qquickimageprovider.h
-class Q_QUICK_PRIVATE_EXPORT QQuickImageProviderOptions
-{
public:
- enum AutoTransform {
- UsePluginDefaultTransform = -1,
- ApplyTransform = 0,
- DoNotApplyTransform = 1
- };
-
- QQuickImageProviderOptions();
- ~QQuickImageProviderOptions();
-
- QQuickImageProviderOptions(const QQuickImageProviderOptions&);
- QQuickImageProviderOptions& operator=(const QQuickImageProviderOptions&);
-
- bool operator==(const QQuickImageProviderOptions&) const;
+ static QQuickPixmapCache *instance();
+ ~QQuickPixmapCache() override;
- AutoTransform autoTransform() const;
- void setAutoTransform(AutoTransform autoTransform);
+ void unreferencePixmap(QQuickPixmapData *);
+ void referencePixmap(QQuickPixmapData *);
- bool preserveAspectRatioCrop() const;
- void setPreserveAspectRatioCrop(bool preserveAspectRatioCrop);
+ void purgeCache();
- bool preserveAspectRatioFit() const;
- void setPreserveAspectRatioFit(bool preserveAspectRatioFit);
-
- QColorSpace targetColorSpace() const;
- void setTargetColorSpace(const QColorSpace &colorSpace);
+protected:
+ void timerEvent(QTimerEvent *) override;
private:
- QSharedDataPointer<QQuickImageProviderOptionsPrivate> d;
-};
+ QQuickPixmapCache() = default;
+ Q_DISABLE_COPY(QQuickPixmapCache)
-class Q_QUICK_PRIVATE_EXPORT QQuickPixmap
-{
- Q_DECLARE_TR_FUNCTIONS(QQuickPixmap)
-public:
- QQuickPixmap();
- QQuickPixmap(QQmlEngine *, const QUrl &);
- QQuickPixmap(QQmlEngine *, const QUrl &, const QRect &region, const QSize &);
- QQuickPixmap(const QUrl &, const QImage &image);
- ~QQuickPixmap();
-
- enum Status { Null, Ready, Error, Loading };
-
- enum Option {
- Asynchronous = 0x00000001,
- Cache = 0x00000002
- };
- Q_DECLARE_FLAGS(Options, Option)
-
- bool isNull() const;
- bool isReady() const;
- bool isError() const;
- bool isLoading() const;
-
- Status status() const;
- QString error() const;
- const QUrl &url() const;
- const QSize &implicitSize() const;
- const QRect &requestRegion() const;
- const QSize &requestSize() const;
- QQuickImageProviderOptions::AutoTransform autoTransform() const;
- int frameCount() const;
- QImage image() const;
- void setImage(const QImage &);
- void setPixmap(const QQuickPixmap &other);
-
- QColorSpace colorSpace() const;
-
- QQuickTextureFactory *textureFactory() const;
-
- QRect rect() const;
- int width() const;
- int height() const;
-
- void load(QQmlEngine *, const QUrl &);
- void load(QQmlEngine *, const QUrl &, QQuickPixmap::Options options);
- void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize);
- void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize, QQuickPixmap::Options options);
- void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize,
- QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1,
- qreal devicePixelRatio = 1.0);
- void loadImageFromDevice(QQmlEngine *engine, QIODevice *device, const QUrl &url,
- const QRect &requestRegion, const QSize &requestSize,
- const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1);
-
- void clear();
- void clear(QObject *);
-
- bool connectFinished(QObject *, const char *);
- bool connectFinished(QObject *, int);
- bool connectDownloadProgress(QObject *, const char *);
- bool connectDownloadProgress(QObject *, int);
-
- static void purgeCache();
- static bool isCached(const QUrl &url, const QRect &requestRegion, const QSize &requestSize,
- const int frame, const QQuickImageProviderOptions &options);
-
- static const QLatin1String itemGrabberScheme;
+ void shrinkCache(int remove);
+ int destroyCache();
+ qsizetype referencedCost() const;
private:
- Q_DISABLE_COPY(QQuickPixmap)
- QQuickPixmapData *d;
- QIntrusiveListNode dataListNode;
- friend class QQuickPixmapData;
-};
+ QHash<QQuickPixmapKey, QQuickPixmapData *> m_cache;
+ mutable QMutex m_cacheMutex; // avoid simultaneous iteration and modification
-Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPixmap::Options)
+ QQuickPixmapData *m_unreferencedPixmaps = nullptr;
+ QQuickPixmapData *m_lastUnreferencedPixmap = nullptr;
-// ### Qt 6: This should be made public in Qt 6. It's functionality can't be merged into
-// QQuickImageProvider without breaking source compatibility.
-class Q_QUICK_PRIVATE_EXPORT QQuickImageProviderWithOptions : public QQuickAsyncImageProvider
-{
-public:
- QQuickImageProviderWithOptions(ImageType type, Flags flags = Flags());
-
- QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize) override;
- QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) override;
- QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize) override;
- QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
+ int m_unreferencedCost = 0;
+ int m_timerId = -1;
+ bool m_destroying = false;
- virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options);
- virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options);
- virtual QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize, const QQuickImageProviderOptions &options);
- virtual QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize, const QQuickImageProviderOptions &options);
-
- static QSize loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options,
- qreal devicePixelRatio = 1.0);
- static QQuickImageProviderWithOptions *checkedCast(QQuickImageProvider *provider);
+ friend class QQuickPixmap;
+ friend class QQuickPixmapData;
+ friend class tst_qquickpixmapcache;
+ friend class tst_qquicktext;
+ friend class tst_qquicktextedit;
};
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h
index 276172a2da..a5e69df3be 100644
--- a/src/quick/util/qquickprofiler_p.h
+++ b/src/quick/util/qquickprofiler_p.h
@@ -135,7 +135,7 @@ public:
}
};
-class Q_QUICK_PRIVATE_EXPORT QQuickProfiler : public QObject, public QQmlProfilerDefinitions {
+class Q_QUICK_EXPORT QQuickProfiler : public QObject, public QQmlProfilerDefinitions {
Q_OBJECT
public:
@@ -316,7 +316,17 @@ protected:
void processMessage(const QQuickProfilerData &message)
{
QMutexLocker lock(&m_dataMutex);
- m_data.append(message);
+ if (Q_LIKELY(m_data.isEmpty() || m_data.last().time <= message.time)) {
+ m_data.append(message);
+ return;
+ }
+
+ // Since the scenegraph data is recorded from different threads, contention for the lock
+ // can cause it to be processed out of order here. Insert the message at the right place.
+ const auto it = std::find_if(
+ m_data.rbegin(), m_data.rend(),
+ [t = message.time](const QQuickProfilerData &i) { return i.time <= t; });
+ m_data.insert(it.base(), message);
}
void startProfilingImpl(quint64 features);
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index fcb6e71e17..ab08a725bb 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -20,8 +20,10 @@
#include <private/qqmlirbuilder_p.h>
#include <QtCore/qdebug.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <private/qobject_p.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -101,7 +103,7 @@ QT_BEGIN_NAMESPACE
edited with \l{Qt Design Studio}. Mind that \l{Qt Design Studio} also imposes a number
of further restrictions on the files it can work with.
- \sa {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, {Qt QML}
+ \sa {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, {Qt Qml}
*/
/*!
@@ -267,7 +269,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
break;
}
- if (binding->isSignalHandler() || QmlIR::IRBuilder::isSignalPropertyName(propertyName)) {
+ if (binding->isSignalHandler() || QQmlSignalNames::isHandlerName(propertyName)) {
QQmlProperty prop = property(propertyName);
if (prop.isSignalProperty()) {
QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
diff --git a/src/quick/util/qquickpropertychanges_p.h b/src/quick/util/qquickpropertychanges_p.h
index 44a66bc6e8..b7413c1142 100644
--- a/src/quick/util/qquickpropertychanges_p.h
+++ b/src/quick/util/qquickpropertychanges_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
class QQuickPropertyChangesPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPropertyChanges : public QQuickStateOperation
+class Q_QUICK_EXPORT QQuickPropertyChanges : public QQuickStateOperation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPropertyChanges)
@@ -89,6 +89,4 @@ inline QQmlCustomParser *qmlCreateCustomParser<QQuickPropertyChanges>()
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPropertyChanges)
-
#endif // QQUICKPROPERTYCHANGES_H
diff --git a/src/quick/util/qquickshortcut.cpp b/src/quick/util/qquickshortcut.cpp
index 51de063090..a6a0972d87 100644
--- a/src/quick/util/qquickshortcut.cpp
+++ b/src/quick/util/qquickshortcut.cpp
@@ -18,8 +18,9 @@
\ingroup qtquick-input
\brief Provides keyboard shortcuts.
- The Shortcut type provides a way of handling keyboard shortcuts. The shortcut can
- be set to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts},
+ The Shortcut type lets you handle keyboard shortcuts. The shortcut can
+ be set to one of the
+ \l{QKeySequence::StandardKey}{standard keyboard shortcuts},
or it can be described with a string containing a sequence of up to four key
presses that are needed to \l{Shortcut::activated}{activate} the shortcut.
@@ -30,7 +31,7 @@
property int currentIndex
Shortcut {
- sequence: StandardKey.NextChild
+ sequences: [StandardKey.NextChild]
onActivated: view.currentIndex++
}
}
@@ -76,12 +77,12 @@ typedef bool (*ContextMatcher)(QObject *, Qt::ShortcutContext);
Q_GLOBAL_STATIC_WITH_ARGS(ContextMatcher, ctxMatcher, (qQuickShortcutContextMatcher))
-Q_QUICK_PRIVATE_EXPORT ContextMatcher qt_quick_shortcut_context_matcher()
+Q_QUICK_EXPORT ContextMatcher qt_quick_shortcut_context_matcher()
{
return *ctxMatcher();
}
-Q_QUICK_PRIVATE_EXPORT void qt_quick_set_shortcut_context_matcher(ContextMatcher matcher)
+Q_QUICK_EXPORT void qt_quick_set_shortcut_context_matcher(ContextMatcher matcher)
{
if (!ctxMatcher.isDestroyed())
*ctxMatcher() = matcher;
@@ -147,6 +148,10 @@ QQuickShortcut::~QQuickShortcut()
}
\endqml
+ \note Given that standard keys can resolve to one shortcut on some
+ platforms, but multiple shortcuts on other platforms, we recommend always
+ using \l{Shortcut::}{sequences} for standard keys.
+
\sa sequences
*/
QVariant QQuickShortcut::sequence() const
@@ -319,10 +324,11 @@ void QQuickShortcut::setAutoRepeat(bool repeat)
This property holds the \l{Qt::ShortcutContext}{shortcut context}.
Supported values are:
- \list
- \li \c Qt.WindowShortcut (default) - The shortcut is active when its parent item is in an active top-level window.
- \li \c Qt.ApplicationShortcut - The shortcut is active when one of the application's windows are active.
- \endlist
+
+ \value Qt.WindowShortcut
+ (default) The shortcut is active when its parent item is in an active top-level window.
+ \value Qt.ApplicationShortcut
+ The shortcut is active when one of the application's windows are active.
\qml
Shortcut {
diff --git a/src/quick/util/qquickshortcut_p.h b/src/quick/util/qquickshortcut_p.h
index baa884b6f0..a0e0c996ed 100644
--- a/src/quick/util/qquickshortcut_p.h
+++ b/src/quick/util/qquickshortcut_p.h
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE
class QShortcutEvent;
-class Q_QUICK_PRIVATE_EXPORT QQuickShortcut : public QObject, public QQmlParserStatus
+class Q_QUICK_EXPORT QQuickShortcut : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp
index 4681d5919e..b19e6f03d9 100644
--- a/src/quick/util/qquicksmoothedanimation.cpp
+++ b/src/quick/util/qquicksmoothedanimation.cpp
@@ -52,14 +52,12 @@ QSmoothedAnimation::~QSmoothedAnimation()
delete delayedStopTimer;
if (animationTemplate) {
if (target.object()) {
- QHash<QQmlProperty, QSmoothedAnimation* >::iterator it =
- animationTemplate->activeAnimations.find(target);
- if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ auto it = animationTemplate->activeAnimations.constFind(target);
+ if (it != animationTemplate->activeAnimations.cend() && it.value() == this)
animationTemplate->activeAnimations.erase(it);
} else {
//target is no longer valid, need to search linearly
- QHash<QQmlProperty, QSmoothedAnimation* >::iterator it;
- for (it = animationTemplate->activeAnimations.begin(); it != animationTemplate->activeAnimations.end(); ++it) {
+ for (auto it = animationTemplate->activeAnimations.cbegin(); it != animationTemplate->activeAnimations.cend(); ++it) {
if (it.value() == this) {
animationTemplate->activeAnimations.erase(it);
break;
@@ -421,11 +419,12 @@ QAbstractAnimationJob* QQuickSmoothedAnimation::transition(QQuickStateActions &a
Possible values are:
- \list
- \li SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction
- \li SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0
- \li SmoothedAnimation.Sync - the property is immediately set to the target value
- \endlist
+ \value SmoothedAnimation.Eased
+ (default) the animation will smoothly decelerate, and then reverse direction
+ \value SmoothedAnimation.Immediate
+ the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0
+ \value SmoothedAnimation.Sync
+ the property is immediately set to the target value
*/
QQuickSmoothedAnimation::ReversingMode QQuickSmoothedAnimation::reversingMode() const
{
diff --git a/src/quick/util/qquicksmoothedanimation_p.h b/src/quick/util/qquicksmoothedanimation_p.h
index bf8bf46aed..9a996b0ed2 100644
--- a/src/quick/util/qquicksmoothedanimation_p.h
+++ b/src/quick/util/qquicksmoothedanimation_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQmlProperty;
class QQuickSmoothedAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickSmoothedAnimation : public QQuickNumberAnimation
+class Q_QUICK_EXPORT QQuickSmoothedAnimation : public QQuickNumberAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickSmoothedAnimation)
@@ -65,6 +65,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSmoothedAnimation)
-
#endif // QQUICKSMOOTHEDANIMATION_H
diff --git a/src/quick/util/qquickspringanimation.cpp b/src/quick/util/qquickspringanimation.cpp
index 678f1c4238..4b615d69b7 100644
--- a/src/quick/util/qquickspringanimation.cpp
+++ b/src/quick/util/qquickspringanimation.cpp
@@ -132,12 +132,12 @@ QSpringAnimation::~QSpringAnimation()
{
if (animationTemplate) {
if (target.object()) {
- ActiveAnimationHashIt it = animationTemplate->activeAnimations.find(target);
- if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ auto it = animationTemplate->activeAnimations.constFind(target);
+ if (it != animationTemplate->activeAnimations.cend() && it.value() == this)
animationTemplate->activeAnimations.erase(it);
} else {
//target is no longer valid, need to search linearly
- for (ActiveAnimationHashIt it = animationTemplate->activeAnimations.begin(); it != animationTemplate->activeAnimations.end(); ++it) {
+ for (auto it = animationTemplate->activeAnimations.cbegin(); it != animationTemplate->activeAnimations.cend(); ++it) {
if (it.value() == this) {
animationTemplate->activeAnimations.erase(it);
break;
diff --git a/src/quick/util/qquickspringanimation_p.h b/src/quick/util/qquickspringanimation_p.h
index 552f67f640..ba7f83fa44 100644
--- a/src/quick/util/qquickspringanimation_p.h
+++ b/src/quick/util/qquickspringanimation_p.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
class QQuickSpringAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickSpringAnimation : public QQuickNumberAnimation
+class Q_QUICK_EXPORT QQuickSpringAnimation : public QQuickNumberAnimation
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QQuickSpringAnimation)
@@ -74,6 +74,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSpringAnimation)
-
#endif // QQUICKSPRINGANIMATION_H
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
index 51bc9852f4..1f066270c6 100644
--- a/src/quick/util/qquickstate.cpp
+++ b/src/quick/util/qquickstate.cpp
@@ -112,7 +112,7 @@ QQuickStateOperation::QQuickStateOperation(QObjectPrivate &dd, QObject *parent)
not allowed.
\sa {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States},
- {Animation and Transitions in Qt Quick}{Transitions}, {Qt QML}
+ {Animation and Transitions in Qt Quick}{Transitions}, {Qt Qml}
*/
QQuickState::QQuickState(QObject *parent)
: QObject(*(new QQuickStatePrivate), parent)
@@ -419,27 +419,25 @@ void QQuickState::addEntryToRevertList(const QQuickStateAction &action)
void QQuickState::removeAllEntriesFromRevertList(QObject *target)
{
- Q_D(QQuickState);
-
- if (isStateActive()) {
- const auto actionMatchesTarget = [target](QQuickSimpleAction &simpleAction) {
- if (simpleAction.property().object() == target) {
- QQmlPropertyPrivate::removeBinding(simpleAction.property());
- simpleAction.property().write(simpleAction.value());
- if (auto binding = simpleAction.binding()) {
- QQmlProperty prop = simpleAction.property();
- binding.installOn(prop);
- }
-
- return true;
- }
- return false;
- };
-
- d->revertList.erase(std::remove_if(d->revertList.begin(), d->revertList.end(),
- actionMatchesTarget),
- d->revertList.end());
- }
+ Q_D(QQuickState);
+
+ if (isStateActive()) {
+ const auto actionMatchesTarget = [target](const QQuickSimpleAction &simpleAction) {
+ if (simpleAction.property().object() == target) {
+ QQmlPropertyPrivate::removeBinding(simpleAction.property());
+ simpleAction.property().write(simpleAction.value());
+ if (auto binding = simpleAction.binding()) {
+ QQmlProperty prop = simpleAction.property();
+ binding.installOn(prop);
+ }
+
+ return true;
+ }
+ return false;
+ };
+
+ d->revertList.removeIf(actionMatchesTarget);
+ }
}
void QQuickState::addEntriesToRevertList(const QList<QQuickStateAction> &actionList)
diff --git a/src/quick/util/qquickstate_p.h b/src/quick/util/qquickstate_p.h
index 41b0a10835..b829248e24 100644
--- a/src/quick/util/qquickstate_p.h
+++ b/src/quick/util/qquickstate_p.h
@@ -31,7 +31,7 @@ class QQuickStateActionEvent;
class QQmlBinding;
class QQmlExpression;
-class Q_QUICK_PRIVATE_EXPORT QQuickStateAction
+class Q_QUICK_EXPORT QQuickStateAction
{
public:
QQuickStateAction();
@@ -89,7 +89,7 @@ public:
class QQuickStateGroup;
class QQuickState;
class QQuickStateOperationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickStateOperation : public QObject
+class Q_QUICK_EXPORT QQuickStateOperation : public QObject
{
Q_OBJECT
QML_ANONYMOUS
@@ -116,7 +116,7 @@ typedef QQuickStateOperation::ActionList QQuickStateActions;
class QQuickTransition;
class QQuickStatePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickState : public QObject
+class Q_QUICK_EXPORT QQuickState : public QObject
{
Q_OBJECT
@@ -178,7 +178,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickStateOperation)
-QML_DECLARE_TYPE(QQuickState)
-
#endif // QQUICKSTATE_H
diff --git a/src/quick/util/qquickstatechangescript_p.h b/src/quick/util/qquickstatechangescript_p.h
index 3489a33095..66772c7e00 100644
--- a/src/quick/util/qquickstatechangescript_p.h
+++ b/src/quick/util/qquickstatechangescript_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
class QQuickStateChangeScriptPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickStateChangeScript : public QQuickStateOperation, public QQuickStateActionEvent
+class Q_QUICK_EXPORT QQuickStateChangeScript : public QQuickStateOperation, public QQuickStateActionEvent
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickStateChangeScript)
@@ -50,6 +50,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickStateChangeScript)
-
#endif // QQUICKSTATEOPERATIONS_H
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index b4be946dc3..f61261035f 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -85,7 +85,7 @@ public:
}
\endqml
- \sa {Qt Quick States}{Qt Quick States}, {Animation and Transitions in Qt Quick}{Transitions}, {Qt QML}
+ \sa {Qt Quick States}{Qt Quick States}, {Animation and Transitions in Qt Quick}{Transitions}, {Qt Qml}
*/
QQuickStateGroup::QQuickStateGroup(QObject *parent)
@@ -96,8 +96,10 @@ QQuickStateGroup::QQuickStateGroup(QObject *parent)
QQuickStateGroup::~QQuickStateGroup()
{
Q_D(const QQuickStateGroup);
- for (int i = 0; i < d->states.size(); ++i)
- d->states.at(i)->setStateGroup(nullptr);
+ for (QQuickState *state : std::as_const(d->states)) {
+ if (state)
+ state->setStateGroup(nullptr);
+ }
if (d->nullState)
d->nullState->setStateGroup(nullptr);
}
@@ -143,11 +145,9 @@ QQmlListProperty<QQuickState> QQuickStateGroup::statesProperty()
void QQuickStateGroupPrivate::append_state(QQmlListProperty<QQuickState> *list, QQuickState *state)
{
QQuickStateGroup *_this = static_cast<QQuickStateGroup *>(list->object);
- if (state) {
- _this->d_func()->states.append(state);
+ _this->d_func()->states.append(state);
+ if (state)
state->setStateGroup(_this);
- }
-
}
qsizetype QQuickStateGroupPrivate::count_state(QQmlListProperty<QQuickState> *list)
@@ -164,34 +164,42 @@ QQuickState *QQuickStateGroupPrivate::at_state(QQmlListProperty<QQuickState> *li
void QQuickStateGroupPrivate::clear_states(QQmlListProperty<QQuickState> *list)
{
- QQuickStateGroup *_this = static_cast<QQuickStateGroup *>(list->object);
- _this->d_func()->setCurrentStateInternal(QString(), true);
- for (qsizetype i = 0; i < _this->d_func()->states.size(); ++i) {
- _this->d_func()->states.at(i)->setStateGroup(nullptr);
+ QQuickStateGroupPrivate *d = static_cast<QQuickStateGroup *>(list->object)->d_func();
+ d->setCurrentStateInternal(QString(), true);
+ for (QQuickState *state : std::as_const(d->states)) {
+ if (state)
+ state->setStateGroup(nullptr);
}
- _this->d_func()->states.clear();
+ d->states.clear();
}
void QQuickStateGroupPrivate::replace_states(QQmlListProperty<QQuickState> *list, qsizetype index, QQuickState *state)
{
- auto *self = qobject_cast<QQuickStateGroup *>(list->object);
+ auto *self = static_cast<QQuickStateGroup *>(list->object);
auto *d = self->d_func();
auto *oldState = d->states.at(index);
if (oldState != state) {
- oldState->setStateGroup(nullptr);
- state->setStateGroup(self);
+ if (oldState)
+ oldState->setStateGroup(nullptr);
+
+ if (state)
+ state->setStateGroup(self);
d->states.replace(index, state);
- if (d->currentState == oldState->name())
- d->setCurrentStateInternal(state->name(), true);
+ if (!oldState || d->currentState == oldState->name())
+ d->setCurrentStateInternal(state ? state->name() : QString(), true);
}
}
void QQuickStateGroupPrivate::removeLast_states(QQmlListProperty<QQuickState> *list)
{
- auto *d = qobject_cast<QQuickStateGroup *>(list->object)->d_func();
- if (d->currentState == d->states.last()->name())
- d->setCurrentStateInternal(d->states.size() > 1 ? d->states.first()->name() : QString(), true);
- d->states.last()->setStateGroup(nullptr);
+ auto *d = static_cast<QQuickStateGroup *>(list->object)->d_func();
+ if (QQuickState *last = d->states.last()) {
+ if (d->currentState == last->name()) {
+ QQuickState *first = d->states.size() > 1 ? d->states.first() : nullptr;
+ d->setCurrentStateInternal(first ? first->name() : QString(), true);
+ }
+ last->setStateGroup(nullptr);
+ }
d->states.removeLast();
}
@@ -300,17 +308,18 @@ void QQuickStateGroup::componentComplete()
QVarLengthArray<QString, 4> names;
names.reserve(d->states.size());
- for (int ii = 0; ii < d->states.size(); ++ii) {
- QQuickState *state = d->states.at(ii);
+ for (QQuickState *state : std::as_const(d->states)) {
+ if (!state)
+ continue;
+
if (!state->isNamed())
state->setName(QLatin1String("anonymousState") + QString::number(++d->unnamedCount));
QString stateName = state->name();
- if (names.contains(stateName)) {
+ if (names.contains(stateName))
qmlWarning(state->parent()) << "Found duplicate state name: " << stateName;
- } else {
+ else
names.append(std::move(stateName));
- }
}
if (d->updateAutoState()) {
@@ -338,40 +347,38 @@ bool QQuickStateGroupPrivate::updateAutoState()
return false;
bool revert = false;
- for (int ii = 0; ii < states.size(); ++ii) {
- QQuickState *state = states.at(ii);
- if (state->isWhenKnown()) {
- if (state->isNamed()) {
- bool whenValue = state->when();
- const QQmlProperty whenProp(state, u"when"_s);
- const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp);
- Q_ASSERT(!potentialWhenBinding.isUntypedPropertyBinding());
-
- // if there is a binding, the value in when might not be up-to-date at this point
- // so we manually re-evaluate the binding
- QQmlAbstractBinding *abstractBinding = potentialWhenBinding.asAbstractBinding();
- if (abstractBinding && abstractBinding->kind() == QQmlAbstractBinding::QmlBinding) {
- QQmlBinding *binding = static_cast<QQmlBinding *>(abstractBinding);
- if (binding->hasValidContext()) {
- const auto boolType = QMetaType::fromType<bool>();
- const bool isUndefined = !binding->evaluate(&whenValue, boolType);
- if (isUndefined)
- whenValue = false;
- }
- }
-
- if (whenValue) {
- qCDebug(lcStates) << "Setting auto state due to expression";
- if (currentState != state->name()) {
- q->setState(state->name());
- return true;
- } else {
- return false;
- }
- } else if (state->name() == currentState) {
- revert = true;
- }
+ for (QQuickState *state : std::as_const(states)) {
+ if (!state || !state->isWhenKnown() || !state->isNamed())
+ continue;
+
+ bool whenValue = state->when();
+ const QQmlPropertyIndex whenIndex(state->metaObject()->indexOfProperty("when"));
+ const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(state, whenIndex);
+ Q_ASSERT(!potentialWhenBinding.isUntypedPropertyBinding());
+
+ // if there is a binding, the value in when might not be up-to-date at this point
+ // so we manually re-evaluate the binding
+ QQmlAbstractBinding *abstractBinding = potentialWhenBinding.asAbstractBinding();
+ if (abstractBinding && abstractBinding->kind() == QQmlAbstractBinding::QmlBinding) {
+ QQmlBinding *binding = static_cast<QQmlBinding *>(abstractBinding);
+ if (binding->hasValidContext()) {
+ const auto boolType = QMetaType::fromType<bool>();
+ const bool isUndefined = !binding->evaluate(&whenValue, boolType);
+ if (isUndefined)
+ whenValue = false;
+ }
+ }
+
+ if (whenValue) {
+ qCDebug(lcStates) << "Setting auto state due to expression";
+ if (currentState != state->name()) {
+ q->setState(state->name());
+ return true;
+ } else {
+ return false;
}
+ } else if (state->name() == currentState) {
+ revert = true;
}
}
if (revert) {
@@ -475,9 +482,9 @@ void QQuickStateGroupPrivate::setCurrentStateInternal(const QString &state,
QQuickState *oldState = nullptr;
if (!currentState.isEmpty()) {
- for (int ii = 0; ii < states.size(); ++ii) {
- if (states.at(ii)->name() == currentState) {
- oldState = states.at(ii);
+ for (QQuickState *state : std::as_const(states)) {
+ if (state && state->name() == currentState) {
+ oldState = state;
break;
}
}
@@ -487,9 +494,9 @@ void QQuickStateGroupPrivate::setCurrentStateInternal(const QString &state,
emit q->stateChanged(currentState);
QQuickState *newState = nullptr;
- for (int ii = 0; ii < states.size(); ++ii) {
- if (states.at(ii)->name() == currentState) {
- newState = states.at(ii);
+ for (QQuickState *state : std::as_const(states)) {
+ if (state && state->name() == currentState) {
+ newState = state;
break;
}
}
@@ -511,9 +518,8 @@ void QQuickStateGroupPrivate::setCurrentStateInternal(const QString &state,
QQuickState *QQuickStateGroup::findState(const QString &name) const
{
Q_D(const QQuickStateGroup);
- for (int i = 0; i < d->states.size(); ++i) {
- QQuickState *state = d->states.at(i);
- if (state->name() == name)
+ for (QQuickState *state : std::as_const(d->states)) {
+ if (state && state->name() == name)
return state;
}
diff --git a/src/quick/util/qquickstategroup_p.h b/src/quick/util/qquickstategroup_p.h
index 37f0ec981f..0a962b7fce 100644
--- a/src/quick/util/qquickstategroup_p.h
+++ b/src/quick/util/qquickstategroup_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
class QQuickStateGroupPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickStateGroup : public QObject, public QQmlParserStatus
+class Q_QUICK_EXPORT QQuickStateGroup : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
@@ -61,6 +61,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickStateGroup)
-
#endif // QQUICKSTATEGROUP_H
diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp
index 1bee09a421..a595dbcbc2 100644
--- a/src/quick/util/qquickstyledtext.cpp
+++ b/src/quick/util/qquickstyledtext.cpp
@@ -530,10 +530,12 @@ void QQuickStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textI
while (!ch->isNull()) {
if (*ch == QLatin1Char(';')) {
auto entity = QStringView(textIn).mid(entityStart, entityLength);
+#if QT_CONFIG(texthtmlparser)
const QString parsedEntity = QTextHtmlParser::parseEntity(entity);
if (!parsedEntity.isNull())
textOut += parsedEntity;
else
+#endif
qCWarning(lcStyledText) << "StyledText doesn't support entity" << entity;
return;
} else if (*ch == QLatin1Char(' ')) {
@@ -655,7 +657,7 @@ void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QStri
do {
attr = parseAttribute(ch, textIn);
if (is_equal_ignoring_case(attr.first, QLatin1String("src"))) {
- image->url = QUrl(attr.second.toString());
+ image->url = QUrl(attr.second.toString());
} else if (is_equal_ignoring_case(attr.first, QLatin1String("width"))) {
image->size.setWidth(attr.second.toString().toInt());
} else if (is_equal_ignoring_case(attr.first, QLatin1String("height"))) {
@@ -675,20 +677,24 @@ void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QStri
// to avoid a relayout later on.
QUrl url = baseUrl.resolved(image->url);
if (url.isLocalFile()) {
- image->pix = new QQuickPixmap(context->engine(), url, QRect(), image->size);
+ image->pix.reset(new QQuickPixmap(context->engine(), url, QRect(), image->size));
if (image->pix && image->pix->isReady()) {
image->size = image->pix->implicitSize();
} else {
- delete image->pix;
- image->pix = nullptr;
+ image->pix.reset();
}
}
}
- imgWidth = image->size.width();
- image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0;
- imgTags->append(image);
-
+ // Return immediately if img tag has invalid url
+ if (!image->url.isValid()) {
+ delete image;
+ qCWarning(lcStyledText) << "StyledText - Invalid base url in img tag";
+ } else {
+ imgWidth = image->size.width();
+ image->offset = -std::fmod(imgWidth, spaceWidth) / 2.0;
+ imgTags->append(image);
+ }
} else {
// if we already have a list of img tags for this text
// we only want to update the positions of these tags.
diff --git a/src/quick/util/qquickstyledtext_p.h b/src/quick/util/qquickstyledtext_p.h
index b7d51871a3..9f8ccf31e4 100644
--- a/src/quick/util/qquickstyledtext_p.h
+++ b/src/quick/util/qquickstyledtext_p.h
@@ -19,7 +19,8 @@
#include <QPointF>
#include <QList>
#include <QUrl>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QScopedPointer>
+#include <QtQuick/private/qquickpixmap_p.h>
QT_BEGIN_NAMESPACE
@@ -31,9 +32,8 @@ class QQmlContext;
class Q_AUTOTEST_EXPORT QQuickStyledTextImgTag
{
public:
- QQuickStyledTextImgTag() { }
-
- ~QQuickStyledTextImgTag() { delete pix; }
+ QQuickStyledTextImgTag() = default;
+ ~QQuickStyledTextImgTag() = default;
enum Align {
Bottom,
@@ -47,7 +47,7 @@ public:
int position = 0;
qreal offset = 0.0; // this offset allows us to compensate for flooring reserved space
Align align = QQuickStyledTextImgTag::Bottom;
- QQuickPixmap *pix = nullptr;
+ QScopedPointer<QQuickPixmap> pix;
};
class Q_AUTOTEST_EXPORT QQuickStyledText
diff --git a/src/quick/util/qquicksvgparser_p.h b/src/quick/util/qquicksvgparser_p.h
index 6cbaca9f6a..5fc4d54d2d 100644
--- a/src/quick/util/qquicksvgparser_p.h
+++ b/src/quick/util/qquicksvgparser_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
namespace QQuickSvgParser
{
bool parsePathDataFast(const QString &dataStr, QPainterPath &path);
- Q_QUICK_PRIVATE_EXPORT void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation,
+ Q_QUICK_EXPORT void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation,
int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx,
qreal cury);
}
diff --git a/src/quick/util/qquicksystempalette.cpp b/src/quick/util/qquicksystempalette.cpp
index 7271537ec7..46b2fc00ba 100644
--- a/src/quick/util/qquicksystempalette.cpp
+++ b/src/quick/util/qquicksystempalette.cpp
@@ -27,8 +27,8 @@ public:
The SystemPalette type provides access to the Qt application
palettes. This provides information about the standard colors used
for application windows, buttons and other features. These colors
- are grouped into three \e {color groups}: \c Active, \c Inactive,
- and \c Disabled. See the QPalette documentation for details about
+ are grouped into three \e {color groups}: \c active, \c inactive,
+ and \c disabled. See the QPalette documentation for details about
color groups and the properties provided by SystemPalette.
This can be used to color items in a way that provides a more
@@ -230,16 +230,28 @@ QColor QQuickSystemPalette::placeholderText() const
Q_D(const QQuickSystemPalette);
return QGuiApplication::palette().color(d->group, QPalette::PlaceholderText);
}
+
+/*!
+ \qmlproperty color QtQuick::SystemPalette::accent
+ The accent color of the current color group.
+
+ \since 6.7
+ \sa QPalette::ColorRole
+*/
+QColor QQuickSystemPalette::accent() const
+{
+ Q_D(const QQuickSystemPalette);
+ return QGuiApplication::palette().color(d->group, QPalette::Accent);
+}
+
/*!
\qmlproperty enumeration QtQuick::SystemPalette::colorGroup
The color group of the palette. This can be one of:
- \list
- \li SystemPalette.Active (default)
- \li SystemPalette.Inactive
- \li SystemPalette.Disabled
- \endlist
+ \value SystemPalette.Active (default) QPalette::Active
+ \value SystemPalette.Inactive QPalette::Inactive
+ \value SystemPalette.Disabled QPalette::Disabled
\sa QPalette::ColorGroup
*/
diff --git a/src/quick/util/qquicksystempalette_p.h b/src/quick/util/qquicksystempalette_p.h
index c850dc56c9..a28ed33be5 100644
--- a/src/quick/util/qquicksystempalette_p.h
+++ b/src/quick/util/qquicksystempalette_p.h
@@ -26,7 +26,7 @@
QT_BEGIN_NAMESPACE
class QQuickSystemPalettePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickSystemPalette : public QObject
+class Q_QUICK_EXPORT QQuickSystemPalette : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickSystemPalette)
@@ -47,6 +47,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickSystemPalette : public QObject
Q_PROPERTY(QColor highlight READ highlight NOTIFY paletteChanged)
Q_PROPERTY(QColor highlightedText READ highlightedText NOTIFY paletteChanged)
Q_PROPERTY(QColor placeholderText READ placeholderText NOTIFY paletteChanged REVISION(6, 2))
+ Q_PROPERTY(QColor accent READ accent NOTIFY paletteChanged REVISION(6, 7) FINAL)
QML_NAMED_ELEMENT(SystemPalette)
QML_ADDED_IN_VERSION(2, 0)
@@ -76,6 +77,7 @@ public:
QColor highlightedText() const;
QColor placeholderText() const;
+ QColor accent() const;
QQuickSystemPalette::ColorGroup colorGroup() const;
void setColorGroup(QQuickSystemPalette::ColorGroup);
@@ -86,6 +88,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSystemPalette)
-
#endif // QQUICKSYSTEMPALETTE_H
diff --git a/src/quick/util/qquicktextmetrics.cpp b/src/quick/util/qquicktextmetrics.cpp
index 655bf2dd6e..ee200ff878 100644
--- a/src/quick/util/qquicktextmetrics.cpp
+++ b/src/quick/util/qquicktextmetrics.cpp
@@ -92,12 +92,10 @@ void QQuickTextMetrics::setText(const QString &text)
This property holds the elide mode of the text. This determines the
position in which the string is elided. The possible values are:
- \list
- \li \c Qt::ElideNone - No eliding; this is the default value.
- \li \c Qt::ElideLeft - For example: "...World"
- \li \c Qt::ElideMiddle - For example: "He...ld"
- \li \c Qt::ElideRight - For example: "Hello..."
- \endlist
+ \value Qt::ElideNone No eliding; this is the default value.
+ \value Qt::ElideLeft For example: "...World"
+ \value Qt::ElideMiddle For example: "He...ld"
+ \value Qt::ElideRight For example: "Hello..."
\sa elideWidth, QFontMetrics::elidedText
*/
@@ -238,12 +236,11 @@ QString QQuickTextMetrics::elidedText() const
Override the default rendering type for this component.
Supported render types are:
- \list
- \li Text.QtRendering
- \li Text.NativeRendering
- \endlist
- This should match the intended renderType where you draw the text.
+ \value TextEdit.QtRendering Text is rendered using a scalable distance field for each glyph.
+ \value TextEdit.NativeRendering Text is rendered using a platform-specific technique.
+
+ This should match the intended \c renderType where you draw the text.
\since 6.3
\sa {Text::renderType}{Text.renderType}
diff --git a/src/quick/util/qquicktextmetrics_p.h b/src/quick/util/qquicktextmetrics_p.h
index f98d877f24..1660dfc87b 100644
--- a/src/quick/util/qquicktextmetrics_p.h
+++ b/src/quick/util/qquicktextmetrics_p.h
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE
class QFont;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextMetrics : public QObject
+class Q_QUICK_EXPORT QQuickTextMetrics : public QObject
{
Q_OBJECT
@@ -91,6 +91,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextMetrics)
-
#endif // QQUICKTEXTMETRICS_H
diff --git a/src/quick/util/qquicktextselection.cpp b/src/quick/util/qquicktextselection.cpp
new file mode 100644
index 0000000000..c363434e5a
--- /dev/null
+++ b/src/quick/util/qquicktextselection.cpp
@@ -0,0 +1,187 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquicktextselection_p.h"
+
+#include <QFont>
+#include <QTextOption>
+#include <QtQuick/private/qquicktextcontrol_p.h>
+#include <QtQuick/private/qquicktextcontrol_p_p.h>
+#include <QtQuick/private/qquicktextedit_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype TextSelection
+ \instantiates QQuickTextSelection
+ \inqmlmodule QtQuick
+ \ingroup qtquick-visual
+ \ingroup qtquick-input
+ \brief Represents a contiguous selection of text and its properties.
+ \since 6.7
+
+ \l {QtQuick::TextEdit::cursorSelection}{TextEdit.cursorSelection}
+ represents the range of text that is currently selected (for example by
+ dragging the mouse). It can be used to query and modify the selected text,
+ as well as properties in the \l {QTextCharFormat}{character} and
+ \l {QTextBlockFormat}{block} formats.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa TextEdit, QTextCursor
+*/
+
+/*! \internal
+ QQuickTextSelection provides QML API using QTextCursor.
+ QQuickTextControl owns a text cursor, and one instance of
+ QQuickTextSelection represents it and delegates all operations to it.
+*/
+QQuickTextSelection::QQuickTextSelection(QObject *parent)
+ : QObject(parent)
+{
+ // When QQuickTextEdit creates its cursorSelection, it passes itself as the parent
+ if (auto *textEdit = qmlobject_cast<QQuickTextEdit *>(parent)) {
+ m_control = QQuickTextEditPrivate::get(textEdit)->control;
+ connect(m_control, &QQuickTextControl::currentCharFormatChanged,
+ this, &QQuickTextSelection::updateFromCharFormat);
+ connect(m_control, &QQuickTextControl::cursorPositionChanged,
+ this, &QQuickTextSelection::updateFromBlockFormat);
+ }
+}
+
+/*!
+ \qmlproperty string QtQuick::TextSelection::text
+
+ The selected text, without any rich text markup.
+
+ Setting this property replaces the selected text with the given string.
+*/
+QString QQuickTextSelection::text() const
+{
+ return cursor().selectedText();
+}
+
+void QQuickTextSelection::setText(const QString &text)
+{
+ auto cur = cursor();
+ if (cur.selectedText() == text)
+ return;
+
+ cur.insertText(text);
+ emit textChanged();
+}
+
+/*!
+ \qmlproperty color QtQuick::TextSelection::font
+
+ The font of the selected text.
+
+ \sa QTextCharFormat::font()
+*/
+QFont QQuickTextSelection::font() const
+{
+ return cursor().charFormat().font();
+}
+
+void QQuickTextSelection::setFont(const QFont &font)
+{
+ auto cur = cursor();
+ if (cur.selection().isEmpty())
+ cur.select(QTextCursor::WordUnderCursor);
+
+ if (font == cur.charFormat().font())
+ return;
+
+ QTextCharFormat fmt;
+ fmt.setFont(font);
+ cur.mergeCharFormat(fmt);
+ emit fontChanged();
+}
+
+/*!
+ \qmlproperty color QtQuick::TextSelection::color
+
+ The foreground color of the selected text.
+
+ \sa QTextCharFormat::foreground()
+*/
+QColor QQuickTextSelection::color() const
+{
+ return cursor().charFormat().foreground().color();
+}
+
+void QQuickTextSelection::setColor(QColor color)
+{
+ auto cur = cursor();
+ if (cur.selection().isEmpty())
+ cur.select(QTextCursor::WordUnderCursor);
+
+ if (color == cur.charFormat().foreground().color())
+ return;
+
+ QTextCharFormat fmt;
+ fmt.setForeground(color);
+ cur.mergeCharFormat(fmt);
+ emit colorChanged();
+}
+
+/*!
+ \qmlproperty enumeration QtQuick::TextSelection::alignment
+
+ The alignment of the block containing the selected text.
+
+ \sa QTextBlockFormat::alignment()
+*/
+Qt::Alignment QQuickTextSelection::alignment() const
+{
+ return cursor().blockFormat().alignment();
+}
+
+void QQuickTextSelection::setAlignment(Qt::Alignment align)
+{
+ if (align == alignment())
+ return;
+
+ QTextBlockFormat format;
+ format.setAlignment(align);
+ cursor().mergeBlockFormat(format);
+ emit alignmentChanged();
+}
+
+/*! \internal
+ Return the cursor, which is either the graphically-manipulable cursor from
+ QQuickTextControl if that is set, or else the internally-stored cursor
+ with which the user is trying to mutate and/or monitor the underlying document,
+ in the case that TextSelection is declared in QML.
+*/
+QTextCursor QQuickTextSelection::cursor() const
+{
+ if (m_control)
+ return m_control->textCursor();
+ return m_cursor;
+}
+
+inline void QQuickTextSelection::updateFromCharFormat(const QTextCharFormat &fmt)
+{
+ if (fmt.font() != m_charFormat.font())
+ emit fontChanged();
+ if (fmt.foreground().color() != m_charFormat.foreground().color())
+ emit colorChanged();
+
+ m_charFormat = fmt;
+}
+
+inline void QQuickTextSelection::updateFromBlockFormat()
+{
+ QTextBlockFormat fmt = cursor().blockFormat();
+
+ if (fmt.alignment() != m_blockFormat.alignment())
+ emit alignmentChanged();
+
+ m_blockFormat = fmt;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquicktextselection_p.cpp"
diff --git a/src/quick/util/qquicktextselection_p.h b/src/quick/util/qquicktextselection_p.h
new file mode 100644
index 0000000000..85feb9a5d8
--- /dev/null
+++ b/src/quick/util/qquicktextselection_p.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKTEXTSELECTION_H
+#define QQUICKTEXTSELECTION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtquickglobal_p.h>
+
+#include <QtQuick/qquicktextdocument.h>
+
+#include <QtQml/qqml.h>
+
+#include <QtGui/qtextcursor.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFont;
+class QQuickTextControl;
+
+class Q_QUICK_EXPORT QQuickTextSelection : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL)
+ Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged FINAL)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged FINAL)
+
+ QML_ANONYMOUS
+ QML_ADDED_IN_VERSION(6, 7)
+
+public:
+ explicit QQuickTextSelection(QObject *parent = nullptr);
+
+ QString text() const;
+ void setText(const QString &text);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QColor color() const;
+ void setColor(QColor color);
+
+ Qt::Alignment alignment() const;
+ void setAlignment(Qt::Alignment align);
+
+Q_SIGNALS:
+ void textChanged();
+ void fontChanged();
+ void colorChanged();
+ void alignmentChanged();
+
+private:
+ QTextCursor cursor() const;
+ void updateFromCharFormat(const QTextCharFormat &fmt);
+ void updateFromBlockFormat();
+
+private:
+ QTextCursor m_cursor;
+ QTextCharFormat m_charFormat;
+ QTextBlockFormat m_blockFormat;
+ QQuickTextControl *m_control = nullptr;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTextSelection)
+
+#endif // QQUICKTEXTSELECTION_H
diff --git a/src/quick/util/qquicktimeline_p_p.h b/src/quick/util/qquicktimeline_p_p.h
index 80076f7030..367bbf8c7d 100644
--- a/src/quick/util/qquicktimeline_p_p.h
+++ b/src/quick/util/qquicktimeline_p_p.h
@@ -26,7 +26,7 @@ class QQuickTimeLineValue;
class QQuickTimeLineCallback;
struct QQuickTimeLinePrivate;
class QQuickTimeLineObject;
-class Q_QUICK_PRIVATE_EXPORT QQuickTimeLine : public QObject, QAbstractAnimationJob
+class Q_QUICK_EXPORT QQuickTimeLine : public QObject, QAbstractAnimationJob
{
Q_OBJECT
public:
@@ -134,29 +134,21 @@ template<class T>
class QQuickTimeLineValueProxy : public QQuickTimeLineValue
{
public:
- QQuickTimeLineValueProxy(T *cls, void (T::*func)(qreal), qreal v = 0.)
- : QQuickTimeLineValue(v), _class(cls), _setFunctionReal(func), _setFunctionInt(nullptr)
+ QQuickTimeLineValueProxy(T *object, void (T::*func)(qreal), qreal v = 0.)
+ : QQuickTimeLineValue(v), object(object), setter(func)
{
- Q_ASSERT(_class);
- }
-
- QQuickTimeLineValueProxy(T *cls, void (T::*func)(int), qreal v = 0.)
- : QQuickTimeLineValue(v), _class(cls), _setFunctionReal(0), _setFunctionInt(func)
- {
- Q_ASSERT(_class);
+ Q_ASSERT(object);
}
void setValue(qreal v) override
{
QQuickTimeLineValue::setValue(v);
- if (_setFunctionReal) (_class->*_setFunctionReal)(v);
- else if (_setFunctionInt) (_class->*_setFunctionInt)((int)v);
+ (object->*setter)(v);
}
private:
- T *_class;
- void (T::*_setFunctionReal)(qreal);
- void (T::*_setFunctionInt)(int);
+ T *object;
+ void (T::*setter)(qreal);
};
QT_END_NAMESPACE
diff --git a/src/quick/util/qquicktransition.cpp b/src/quick/util/qquicktransition.cpp
index 062e6e2656..19a1ceb830 100644
--- a/src/quick/util/qquicktransition.cpp
+++ b/src/quick/util/qquicktransition.cpp
@@ -46,6 +46,11 @@ QT_BEGIN_NAMESPACE
values can be set to restrict the animations to only be applied when changing
from one particular state to another.
+ Top-level animations within a transition are run in parallel. To run them
+ sequentially, define them within a SequentialAnimation:
+
+ \snippet qml/transition-reversible.qml sequential animations
+
To define multiple Transitions, specify \l Item::transitions as a list:
\snippet qml/transitions-list.qml list of transitions
@@ -60,7 +65,7 @@ QT_BEGIN_NAMESPACE
\l Behavior, the Transition animation overrides the \l Behavior for that
state change.
- \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, {Qt QML}
+ \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, {Qt Qml}
*/
//ParallelAnimationWrapper allows us to do a "callback" when the animation finishes, rather than connecting
@@ -419,7 +424,7 @@ bool QQuickTransition::running() const
This property holds a list of the animations to be run for this transition.
- \snippet ../qml/dynamicscene/dynamicscene.qml top-level transitions
+ \snippet qml/transition-animation.qml 0
The top-level animations are run in parallel. To run them sequentially,
define them within a SequentialAnimation:
diff --git a/src/quick/util/qquicktransition_p.h b/src/quick/util/qquicktransition_p.h
index 7e953f528a..7a98a8bea0 100644
--- a/src/quick/util/qquicktransition_p.h
+++ b/src/quick/util/qquicktransition_p.h
@@ -55,7 +55,7 @@ private:
friend class QQuickTransition;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickTransition : public QObject
+class Q_QUICK_EXPORT QQuickTransition : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickTransition)
@@ -108,6 +108,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTransition)
-
#endif // QQUICKTRANSITION_H
diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp
index efaa01c305..700e06a8ba 100644
--- a/src/quick/util/qquicktransitionmanager.cpp
+++ b/src/quick/util/qquicktransitionmanager.cpp
@@ -190,8 +190,8 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
}
return false;
};
- auto newEnd = std::remove_if(applyList.begin(), applyList.end(), isHandledInTransition);
- applyList.erase(newEnd, applyList.end());
+
+ applyList.removeIf(isHandledInTransition);
}
// Any actions remaining have not been handled by the transition and should
diff --git a/src/quick/util/qquicktransitionmanager_p_p.h b/src/quick/util/qquicktransitionmanager_p_p.h
index 8b2f52aff9..3b20dddab0 100644
--- a/src/quick/util/qquicktransitionmanager_p_p.h
+++ b/src/quick/util/qquicktransitionmanager_p_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickState;
class QQuickStateAction;
class QQuickTransitionManagerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickTransitionManager
+class Q_QUICK_EXPORT QQuickTransitionManager
{
public:
QQuickTransitionManager();
diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp
index 76cba4c872..d5d584cccb 100644
--- a/src/quick/util/qquickvalidator.cpp
+++ b/src/quick/util/qquickvalidator.cpp
@@ -162,12 +162,8 @@ void QQuickDoubleValidator::resetLocaleName()
The possible values for this property are:
- \list
- \li DoubleValidator.StandardNotation
- \li DoubleValidator.ScientificNotation (default)
- \endlist
-
- If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2).
+ \value DoubleValidator.StandardNotation only decimal numbers with optional sign (e.g. \c -0.015)
+ \value DoubleValidator.ScientificNotation (default) the written number may have an exponent part (e.g. \c 1.5E-2)
*/
/*!
diff --git a/src/quick/util/qquickvalidator_p.h b/src/quick/util/qquickvalidator_p.h
index 72706386e5..da404d80ba 100644
--- a/src/quick/util/qquickvalidator_p.h
+++ b/src/quick/util/qquickvalidator_p.h
@@ -24,7 +24,7 @@
QT_BEGIN_NAMESPACE
#if QT_CONFIG(validator)
-class Q_QUICK_PRIVATE_EXPORT QQuickIntValidator : public QIntValidator
+class Q_QUICK_EXPORT QQuickIntValidator : public QIntValidator
{
Q_OBJECT
Q_PROPERTY(QString locale READ localeName WRITE setLocaleName RESET resetLocaleName NOTIFY localeNameChanged)
@@ -41,7 +41,7 @@ Q_SIGNALS:
void localeNameChanged();
};
-class Q_QUICK_PRIVATE_EXPORT QQuickDoubleValidator : public QDoubleValidator
+class Q_QUICK_EXPORT QQuickDoubleValidator : public QDoubleValidator
{
Q_OBJECT
Q_PROPERTY(QString locale READ localeName WRITE setLocaleName RESET resetLocaleName NOTIFY localeNameChanged)
@@ -61,13 +61,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-#if QT_CONFIG(validator)
-QML_DECLARE_TYPE(QValidator)
-QML_DECLARE_TYPE(QQuickIntValidator)
-QML_DECLARE_TYPE(QQuickDoubleValidator)
-#if QT_CONFIG(regularexpression)
-QML_DECLARE_TYPE(QRegularExpressionValidator)
-#endif
-#endif
-
#endif // QQUICKVALIDATOR_P_H
diff --git a/src/quick/util/qquickvaluetypes.cpp b/src/quick/util/qquickvaluetypes.cpp
index a5a9424597..350f55e143 100644
--- a/src/quick/util/qquickvaluetypes.cpp
+++ b/src/quick/util/qquickvaluetypes.cpp
@@ -5,6 +5,7 @@
#include <qtquickglobal.h>
#include <private/qqmlvaluetype_p.h>
+#include <private/qqmlstringconverters_p.h>
#include <private/qcolorspace_p.h>
#include <private/qfont_p.h>
@@ -162,48 +163,10 @@ void QQuickColorValueType::setHslLightness(qreal hslLightness)
v.setHslF(hue, saturation, hslLightness, alpha);
}
-template<typename T, int NumParams>
-QVariant createValueTypeFromNumberString(const QString &s)
-{
- Q_STATIC_ASSERT_X(NumParams == 2 || NumParams == 3 || NumParams == 4 || NumParams == 16,
- "Unsupported number of params; add an additional case below if necessary.");
-
- if (s.count(u',') != NumParams - 1)
- return QVariant();
-
- QVarLengthArray<float, NumParams> parameters;
- bool ok = true;
- for (qsizetype prev = 0, next = s.indexOf(u','), length = s.size(); ok && prev < length;) {
- parameters.append(s.mid(prev, next - prev).toFloat(&ok));
- prev = next + 1;
- next = (parameters.size() == NumParams - 1) ? length : s.indexOf(u',', prev);
- }
-
- if (!ok)
- return QVariant();
-
- if constexpr (NumParams == 2) {
- return T(parameters[0], parameters[1]);
- } else if constexpr (NumParams == 3) {
- return T(parameters[0], parameters[1], parameters[2]);
- } else if constexpr (NumParams == 4) {
- return T(parameters[0], parameters[1], parameters[2], parameters[3]);
- } else if constexpr (NumParams == 16) {
- return T(parameters[0], parameters[1], parameters[2], parameters[3],
- parameters[4], parameters[5], parameters[6], parameters[7],
- parameters[8], parameters[9], parameters[10], parameters[11],
- parameters[12], parameters[13], parameters[14], parameters[15]);
- } else {
- Q_UNREACHABLE();
- }
-
- return QVariant();
-}
-
QVariant QQuickVector2DValueType::create(const QJSValue &params)
{
if (params.isString())
- return createValueTypeFromNumberString<QVector2D, 2>(params.toString());
+ return QQmlStringConverters::valueTypeFromNumberString<QVector2D, 2, u','>(params.toString());
if (params.isArray())
return QVector2D(params.property(0).toNumber(), params.property(1).toNumber());
return QVariant();
@@ -296,8 +259,10 @@ bool QQuickVector2DValueType::fuzzyEquals(const QVector2D &vec) const
QVariant QQuickVector3DValueType::create(const QJSValue &params)
{
- if (params.isString())
- return createValueTypeFromNumberString<QVector3D, 3>(params.toString());
+ if (params.isString()) {
+ return QQmlStringConverters::valueTypeFromNumberString<QVector3D, 3, u',', u','>(
+ params.toString());
+ }
if (params.isArray()) {
return QVector3D(params.property(0).toNumber(), params.property(1).toNumber(),
@@ -415,8 +380,10 @@ bool QQuickVector3DValueType::fuzzyEquals(const QVector3D &vec) const
QVariant QQuickVector4DValueType::create(const QJSValue &params)
{
- if (params.isString())
- return createValueTypeFromNumberString<QVector4D, 4>(params.toString());
+ if (params.isString()) {
+ return QQmlStringConverters::valueTypeFromNumberString<QVector4D, 4, u',', u',', u','>(
+ params.toString());
+ }
if (params.isArray()) {
return QVector4D(params.property(0).toNumber(), params.property(1).toNumber(),
@@ -542,8 +509,10 @@ bool QQuickVector4DValueType::fuzzyEquals(const QVector4D &vec) const
QVariant QQuickQuaternionValueType::create(const QJSValue &params)
{
- if (params.isString())
- return createValueTypeFromNumberString<QQuaternion, 4>(params.toString());
+ if (params.isString()) {
+ return QQmlStringConverters::valueTypeFromNumberString<QQuaternion, 4, u',', u',', u','>(
+ params.toString());
+ }
if (params.isArray()) {
return QQuaternion(params.property(0).toNumber(), params.property(1).toNumber(),
@@ -682,8 +651,12 @@ QVariant QQuickMatrix4x4ValueType::create(const QJSValue &params)
if (params.isNull() || params.isUndefined())
return QMatrix4x4();
- if (params.isString())
- return createValueTypeFromNumberString<QMatrix4x4, 16>(params.toString());
+ if (params.isString()) {
+ return QQmlStringConverters::valueTypeFromNumberString<QMatrix4x4, 16, u',', u',', u',',
+ u',', u',', u',', u',', u',', u',',
+ u',', u',', u',', u',', u',', u','>(
+ params.toString());
+ }
if (params.isArray() && params.property(QStringLiteral("length")).toInt() == 16) {
return QMatrix4x4(params.property(0).toNumber(),
@@ -1047,6 +1020,79 @@ void QQuickFontValueType::setPreferShaping(bool enable)
v.setStyleStrategy(static_cast<QFont::StyleStrategy>(v.styleStrategy() | QFont::PreferNoShaping));
}
+void QQuickFontValueType::setVariableAxes(const QVariantMap &variableAxes)
+{
+ v.clearVariableAxes();
+ for (auto [variableAxisName, variableAxisValue] : variableAxes.asKeyValueRange()) {
+ const auto maybeTag = QFont::Tag::fromString(variableAxisName);
+ if (!maybeTag) {
+ qWarning() << "Invalid variable axis" << variableAxisName << "ignored";
+ continue;
+ }
+
+ bool ok;
+ float value = variableAxisValue.toFloat(&ok);
+ if (!ok) {
+ qWarning() << "Variable axis" << variableAxisName << "value" << variableAxisValue << "is not a floating point value.";
+ continue;
+ }
+
+ v.setVariableAxis(*maybeTag, value);
+ }
+}
+
+QVariantMap QQuickFontValueType::variableAxes() const
+{
+ QVariantMap ret;
+ for (const auto &tag : v.variableAxisTags())
+ ret.insert(QString::fromUtf8(tag.toString()), v.variableAxisValue(tag));
+
+ return ret;
+}
+
+void QQuickFontValueType::setFeatures(const QVariantMap &features)
+{
+ v.clearFeatures();
+ for (auto [featureName, featureValue] : features.asKeyValueRange()) {
+ const auto maybeTag = QFont::Tag::fromString(featureName);
+ if (!maybeTag) {
+ qWarning() << "Invalid font feature" << featureName << "ignored";
+ continue;
+ }
+
+ bool ok;
+ quint32 value = featureValue.toUInt(&ok);
+ if (!ok) {
+ qWarning() << "Font feature" << featureName << "value" << featureValue << "is not an integer.";
+ continue;
+ }
+
+ v.setFeature(*maybeTag, value);
+ }
+}
+
+QVariantMap QQuickFontValueType::features() const
+{
+ QVariantMap ret;
+ for (const auto &tag : v.featureTags())
+ ret.insert(QString::fromUtf8(tag.toString()), v.featureValue(tag));
+
+ return ret;
+}
+
+bool QQuickFontValueType::contextFontMerging() const
+{
+ return (v.styleStrategy() & QFont::ContextFontMerging) != 0;
+}
+
+void QQuickFontValueType::setContextFontMerging(bool enable)
+{
+ if (enable)
+ v.setStyleStrategy(static_cast<QFont::StyleStrategy>(v.styleStrategy() | QFont::ContextFontMerging));
+ else
+ v.setStyleStrategy(static_cast<QFont::StyleStrategy>(v.styleStrategy() & ~QFont::ContextFontMerging));
+}
+
QVariant QQuickColorSpaceValueType::create(const QJSValue &params)
{
if (!params.isObject())
diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h
index 70ad5c2b0d..c203d12fab 100644
--- a/src/quick/util/qquickvaluetypes_p.h
+++ b/src/quick/util/qquickvaluetypes_p.h
@@ -30,7 +30,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickColorValueType
+class Q_QUICK_EXPORT QQuickColorValueType
{
QColor v;
Q_PROPERTY(qreal r READ r WRITE setR FINAL)
@@ -43,7 +43,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickColorValueType
Q_PROPERTY(qreal hslHue READ hslHue WRITE setHslHue FINAL)
Q_PROPERTY(qreal hslSaturation READ hslSaturation WRITE setHslSaturation FINAL)
Q_PROPERTY(qreal hslLightness READ hslLightness WRITE setHslLightness FINAL)
- Q_PROPERTY(bool valid READ isValid)
+ Q_PROPERTY(bool valid READ isValid FINAL)
Q_GADGET
QML_ADDED_IN_VERSION(2, 0)
QML_FOREIGN(QColor)
@@ -83,9 +83,11 @@ public:
void setHslHue(qreal);
void setHslSaturation(qreal);
void setHslLightness(qreal);
+
+ operator QColor() const { return v; }
};
-class Q_QUICK_PRIVATE_EXPORT QQuickVector2DValueType
+class Q_QUICK_EXPORT QQuickVector2DValueType
{
QVector2D v;
Q_PROPERTY(qreal x READ x WRITE setX FINAL)
@@ -118,9 +120,11 @@ public:
Q_INVOKABLE QVector4D toVector4d() const;
Q_INVOKABLE bool fuzzyEquals(const QVector2D &vec, qreal epsilon) const;
Q_INVOKABLE bool fuzzyEquals(const QVector2D &vec) const;
+
+ operator QVector2D() const { return v; }
};
-class Q_QUICK_PRIVATE_EXPORT QQuickVector3DValueType
+class Q_QUICK_EXPORT QQuickVector3DValueType
{
QVector3D v;
Q_PROPERTY(qreal x READ x WRITE setX FINAL)
@@ -158,9 +162,11 @@ public:
Q_INVOKABLE QVector4D toVector4d() const;
Q_INVOKABLE bool fuzzyEquals(const QVector3D &vec, qreal epsilon) const;
Q_INVOKABLE bool fuzzyEquals(const QVector3D &vec) const;
+
+ operator QVector3D() const { return v; }
};
-class Q_QUICK_PRIVATE_EXPORT QQuickVector4DValueType
+class Q_QUICK_EXPORT QQuickVector4DValueType
{
QVector4D v;
Q_PROPERTY(qreal x READ x WRITE setX FINAL)
@@ -200,15 +206,17 @@ public:
Q_INVOKABLE QVector3D toVector3d() const;
Q_INVOKABLE bool fuzzyEquals(const QVector4D &vec, qreal epsilon) const;
Q_INVOKABLE bool fuzzyEquals(const QVector4D &vec) const;
+
+ operator QVector4D() const { return v; }
};
-class Q_QUICK_PRIVATE_EXPORT QQuickQuaternionValueType
+class Q_QUICK_EXPORT QQuickQuaternionValueType
{
QQuaternion v;
- Q_PROPERTY(qreal scalar READ scalar WRITE setScalar)
- Q_PROPERTY(qreal x READ x WRITE setX)
- Q_PROPERTY(qreal y READ y WRITE setY)
- Q_PROPERTY(qreal z READ z WRITE setZ)
+ Q_PROPERTY(qreal scalar READ scalar WRITE setScalar FINAL)
+ Q_PROPERTY(qreal x READ x WRITE setX FINAL)
+ Q_PROPERTY(qreal y READ y WRITE setY FINAL)
+ Q_PROPERTY(qreal z READ z WRITE setZ FINAL)
Q_GADGET
QML_ADDED_IN_VERSION(2, 0)
QML_FOREIGN(QQuaternion)
@@ -247,9 +255,11 @@ public:
Q_INVOKABLE bool fuzzyEquals(const QQuaternion &q, qreal epsilon) const;
Q_INVOKABLE bool fuzzyEquals(const QQuaternion &q) const;
+
+ operator QQuaternion() const { return v; }
};
-class Q_QUICK_PRIVATE_EXPORT QQuickMatrix4x4ValueType
+class Q_QUICK_EXPORT QQuickMatrix4x4ValueType
{
QMatrix4x4 v;
Q_PROPERTY(qreal m11 READ m11 WRITE setM11 FINAL)
@@ -339,11 +349,13 @@ public:
Q_INVOKABLE bool fuzzyEquals(const QMatrix4x4 &m, qreal epsilon) const;
Q_INVOKABLE bool fuzzyEquals(const QMatrix4x4 &m) const;
+
+ operator QMatrix4x4() const { return v; }
};
namespace QQuickFontEnums
{
-Q_NAMESPACE_EXPORT(Q_QUICK_PRIVATE_EXPORT)
+Q_NAMESPACE_EXPORT(Q_QUICK_EXPORT)
QML_NAMED_ELEMENT(Font)
QML_ADDED_IN_VERSION(2, 0)
@@ -374,7 +386,7 @@ enum HintingPreference {
Q_ENUM_NS(HintingPreference)
};
-class Q_QUICK_PRIVATE_EXPORT QQuickFontValueType
+class Q_QUICK_EXPORT QQuickFontValueType
{
QFont v;
Q_GADGET
@@ -395,6 +407,9 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFontValueType
Q_PROPERTY(QQuickFontEnums::HintingPreference hintingPreference READ hintingPreference WRITE setHintingPreference FINAL)
Q_PROPERTY(bool kerning READ kerning WRITE setKerning FINAL)
Q_PROPERTY(bool preferShaping READ preferShaping WRITE setPreferShaping FINAL)
+ Q_PROPERTY(QVariantMap features READ features WRITE setFeatures FINAL)
+ Q_PROPERTY(QVariantMap variableAxes READ variableAxes WRITE setVariableAxes FINAL)
+ Q_PROPERTY(bool contextFontMerging READ contextFontMerging WRITE setContextFontMerging FINAL)
QML_VALUE_TYPE(font)
QML_FOREIGN(QFont)
@@ -454,11 +469,22 @@ public:
bool preferShaping() const;
void setPreferShaping(bool b);
+
+ QVariantMap features() const;
+ void setFeatures(const QVariantMap &features);
+
+ QVariantMap variableAxes() const;
+ void setVariableAxes(const QVariantMap &variableAxes);
+
+ bool contextFontMerging() const;
+ void setContextFontMerging(bool b);
+
+ operator QFont() const { return v; }
};
namespace QQuickColorSpaceEnums
{
-Q_NAMESPACE_EXPORT(Q_QUICK_PRIVATE_EXPORT)
+Q_NAMESPACE_EXPORT(Q_QUICK_EXPORT)
QML_NAMED_ELEMENT(ColorSpace)
QML_ADDED_IN_VERSION(2, 15)
Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
@@ -491,7 +517,7 @@ enum class TransferFunction {
Q_ENUM_NS(TransferFunction)
}
-class Q_QUICK_PRIVATE_EXPORT QQuickColorSpaceValueType
+class Q_QUICK_EXPORT QQuickColorSpaceValueType
{
QColorSpace v;
Q_GADGET
@@ -518,6 +544,8 @@ public:
void setTransferFunction(QQuickColorSpaceEnums::TransferFunction transferFunction);
float gamma() const noexcept;
void setGamma(float gamma);
+
+ operator QColorSpace() const { return v; }
};
QT_END_NAMESPACE
diff --git a/src/quickcontrols/CMakeLists.txt b/src/quickcontrols/CMakeLists.txt
index 6eff9b10d3..18b170ad82 100644
--- a/src/quickcontrols/CMakeLists.txt
+++ b/src/quickcontrols/CMakeLists.txt
@@ -67,22 +67,31 @@ if(QT_FEATURE_quick_designer)
endif()
add_subdirectory(basic)
-add_subdirectory(fusion)
-add_subdirectory(imagine)
-add_subdirectory(material)
-add_subdirectory(universal)
+if(QT_FEATURE_quickcontrols2_fusion)
+ add_subdirectory(fusion)
+endif()
+
+if(QT_FEATURE_quickcontrols2_imagine)
+ add_subdirectory(imagine)
+endif()
+
+if(QT_FEATURE_quickcontrols2_material)
+ add_subdirectory(material)
+endif()
+
+if(QT_FEATURE_quickcontrols2_universal)
+ add_subdirectory(universal)
+endif()
-if(MACOS)
+if(QT_FEATURE_quickcontrols2_macos)
add_subdirectory(macos)
- ### Remove
- add_subdirectory(ios)
endif()
-if(IOS)
+if(QT_FEATURE_quickcontrols2_ios)
add_subdirectory(ios)
endif()
-if(WIN32)
+if(QT_FEATURE_quickcontrols2_windows)
add_subdirectory(windows)
endif()
diff --git a/src/quickcontrols/basic/CMakeLists.txt b/src/quickcontrols/basic/CMakeLists.txt
index 4bc75288af..a9ed8f1f2a 100644
--- a/src/quickcontrols/basic/CMakeLists.txt
+++ b/src/quickcontrols/basic/CMakeLists.txt
@@ -148,22 +148,22 @@ if (QT_FEATURE_quicktemplates2_calendar)
)
endif()
-qt_internal_add_qml_module(qtquickcontrols2basicstyleplugin
+add_subdirectory(impl)
+
+qt_internal_add_qml_module(QuickControls2Basic
URI "QtQuick.Controls.Basic"
VERSION "${PROJECT_VERSION}"
PAST_MAJOR_VERSIONS 2
CLASS_NAME QtQuickControls2BasicStylePlugin
DEPENDENCIES
QtQuick/auto
- IMPORTS
- QtQuick.Controls.impl/auto
PLUGIN_TARGET qtquickcontrols2basicstyleplugin
NO_PLUGIN_OPTIONAL
NO_GENERATE_PLUGIN_SOURCE
SOURCES
qquickbasicstyle.cpp qquickbasicstyle_p.h
qquickbasictheme.cpp qquickbasictheme_p.h
- qtquickcontrols2basicstyleplugin.cpp
+ qtquickcontrols2basicforeign.cpp qtquickcontrols2basicforeign_p.h
QML_FILES
${qml_files}
DEFINES
@@ -173,13 +173,25 @@ qt_internal_add_qml_module(qtquickcontrols2basicstyleplugin
Qt::CorePrivate
Qt::GuiPrivate
Qt::QmlPrivate
+ Qt::QuickControls2BasicStyleImpl
Qt::QuickControls2Private
Qt::QuickPrivate
Qt::QuickTemplates2Private
+ GENERATE_CPP_EXPORTS
+)
+
+target_sources(qtquickcontrols2basicstyleplugin
+ PRIVATE
+ qtquickcontrols2basicstyleplugin.cpp
+)
+
+target_link_libraries(qtquickcontrols2basicstyleplugin
+ PRIVATE
+ Qt::QuickControls2Private
)
# Resources:
-set(qtquickcontrols2basicstyleplugin_resource_files
+set(qtquickcontrols2basicstyle_resource_files
"images/arrow-indicator.png"
"images/arrow-indicator@2x.png"
"images/arrow-indicator@3x.png"
@@ -202,14 +214,12 @@ set(qtquickcontrols2basicstyleplugin_resource_files
"images/drop-indicator@4x.png"
)
-qt_internal_add_resource(qtquickcontrols2basicstyleplugin "qtquickcontrols2basicstyleplugin"
+qt_internal_add_resource(QuickControls2Basic "qtquickcontrols2basicstyle"
PREFIX
"/qt-project.org/imports/QtQuick/Controls/Basic"
FILES
- ${qtquickcontrols2basicstyleplugin_resource_files}
+ ${qtquickcontrols2basicstyle_resource_files}
)
-add_subdirectory(impl)
-
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2basicstyleplugin
qtquickcontrols2basicstyleimplplugin)
diff --git a/src/quickcontrols/basic/ComboBox.qml b/src/quickcontrols/basic/ComboBox.qml
index 8cd148e9fe..7c272a80e8 100644
--- a/src/quickcontrols/basic/ComboBox.qml
+++ b/src/quickcontrols/basic/ComboBox.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Templates as T
@@ -18,8 +20,11 @@ T.ComboBox {
rightPadding: padding + (control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
delegate: ItemDelegate {
+ required property var model
+ required property int index
+
width: ListView.view.width
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ text: model[control.textRole]
palette.text: control.palette.text
palette.highlightedText: control.palette.highlightedText
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
@@ -80,6 +85,7 @@ T.ComboBox {
height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin)
topMargin: 6
bottomMargin: 6
+ palette: control.palette
contentItem: ListView {
clip: true
@@ -93,14 +99,14 @@ T.ComboBox {
width: parent.width
height: parent.height
color: "transparent"
- border.color: control.palette.mid
+ border.color: palette.mid
}
T.ScrollIndicator.vertical: ScrollIndicator { }
}
background: Rectangle {
- color: control.palette.window
+ color: palette.window
}
}
}
diff --git a/src/quickcontrols/basic/Dial.qml b/src/quickcontrols/basic/Dial.qml
index 3692ae22de..6076f7b2a2 100644
--- a/src/quickcontrols/basic/Dial.qml
+++ b/src/quickcontrols/basic/Dial.qml
@@ -20,6 +20,8 @@ T.Dial {
color: control.visualFocus ? control.palette.highlight : control.palette.dark
progress: control.position
opacity: control.enabled ? 1 : 0.3
+ startAngle: control.startAngle
+ endAngle: control.endAngle
}
handle: ColorImage {
@@ -34,12 +36,13 @@ T.Dial {
opacity: control.enabled ? 1 : 0.3
transform: [
Translate {
- y: -Math.min(control.background.width, control.background.height) * 0.4 + control.handle.height / 2
+ y: -Math.min(control.background.width, control.background.height) * 0.4
+ + (control.handle ? control.handle.height / 2 : 0)
},
Rotation {
angle: control.angle
- origin.x: control.handle.width / 2
- origin.y: control.handle.height / 2
+ origin.x: control.handle ? control.handle.width / 2 : 0
+ origin.y: control.handle ? control.handle.height / 2 : 0
}
]
}
diff --git a/src/quickcontrols/basic/DialogButtonBox.qml b/src/quickcontrols/basic/DialogButtonBox.qml
index 89fca1e41d..dddbf146f5 100644
--- a/src/quickcontrols/basic/DialogButtonBox.qml
+++ b/src/quickcontrols/basic/DialogButtonBox.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import QtQuick
@@ -11,7 +11,7 @@ T.DialogButtonBox {
(control.count === 1 ? implicitContentWidth * 2 : implicitContentWidth) + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
- contentWidth: contentItem.contentWidth
+ contentWidth: (contentItem as ListView)?.contentWidth
spacing: 1
padding: 12
diff --git a/src/quickcontrols/basic/HorizontalHeaderView.qml b/src/quickcontrols/basic/HorizontalHeaderView.qml
index bbc8064a23..9f571a8f62 100644
--- a/src/quickcontrols/basic/HorizontalHeaderView.qml
+++ b/src/quickcontrols/basic/HorizontalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
@@ -16,6 +18,10 @@ T.HorizontalHeaderView {
implicitHeight: Math.max(1, contentHeight)
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -26,11 +32,9 @@ T.HorizontalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: "#ff26282a"
diff --git a/src/quickcontrols/basic/Menu.qml b/src/quickcontrols/basic/Menu.qml
index fd21f5de5b..8e08000337 100644
--- a/src/quickcontrols/basic/Menu.qml
+++ b/src/quickcontrols/basic/Menu.qml
@@ -22,8 +22,8 @@ T.Menu {
implicitHeight: contentHeight
model: control.contentModel
interactive: Window.window
- ? contentHeight + control.topPadding + control.bottomPadding > Window.window.height
- : false
+ ? contentHeight + control.topPadding + control.bottomPadding > control.height
+ : false
clip: true
currentIndex: control.currentIndex
diff --git a/src/quickcontrols/basic/TreeViewDelegate.qml b/src/quickcontrols/basic/TreeViewDelegate.qml
index 2248d23e25..f1cf265467 100644
--- a/src/quickcontrols/basic/TreeViewDelegate.qml
+++ b/src/quickcontrols/basic/TreeViewDelegate.qml
@@ -65,6 +65,7 @@ T.TreeViewDelegate {
// The edit delegate is a separate component, and doesn't need
// to follow the same strict rules that are applied to a control.
// qmllint disable attached-property-reuse
+ // qmllint disable controls-attached-property-reuse
// qmllint disable controls-sanity
TableView.editDelegate: FocusScope {
width: parent.width
@@ -72,7 +73,7 @@ T.TreeViewDelegate {
readonly property int __role: {
let model = control.treeView.model
- let index = control.treeView.modelIndex(column, row)
+ let index = control.treeView.index(row, column)
let editText = model.data(index, Qt.EditRole)
return editText !== undefined ? Qt.EditRole : Qt.DisplayRole
}
@@ -82,17 +83,18 @@ T.TreeViewDelegate {
x: control.contentItem.x
y: (parent.height - height) / 2
width: control.contentItem.width
- text: control.treeView.model.data(control.treeView.modelIndex(column, row), __role)
+ text: control.treeView.model.data(control.treeView.index(row, column), __role)
focus: true
}
TableView.onCommit: {
- let index = TableView.view.modelIndex(column, row)
+ let index = TableView.view.index(row, column)
TableView.view.model.setData(index, textField.text, __role)
}
Component.onCompleted: textField.selectAll()
}
// qmllint enable attached-property-reuse
+ // qmllint enable controls-attached-property-reuse
// qmllint enable controls-sanity
}
diff --git a/src/quickcontrols/basic/VerticalHeaderView.qml b/src/quickcontrols/basic/VerticalHeaderView.qml
index 23bb2d62e3..52a9bcb876 100644
--- a/src/quickcontrols/basic/VerticalHeaderView.qml
+++ b/src/quickcontrols/basic/VerticalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
@@ -16,6 +18,10 @@ T.VerticalHeaderView {
implicitHeight: syncView ? syncView.height : 0
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -26,11 +32,9 @@ T.VerticalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: "#ff26282a"
diff --git a/src/quickcontrols/basic/impl/CMakeLists.txt b/src/quickcontrols/basic/impl/CMakeLists.txt
index ec79fafdda..7f828c0350 100644
--- a/src/quickcontrols/basic/impl/CMakeLists.txt
+++ b/src/quickcontrols/basic/impl/CMakeLists.txt
@@ -5,12 +5,13 @@
## qtquickcontrols2basicstyleimplplugin Plugin:
#####################################################################
-qt_internal_add_qml_module(qtquickcontrols2basicstyleimplplugin
+qt_internal_add_qml_module(QuickControls2BasicStyleImpl
URI "QtQuick.Controls.Basic.impl"
VERSION "${PROJECT_VERSION}"
CLASS_NAME QtQuickControls2BasicStyleImplPlugin
PLUGIN_TARGET qtquickcontrols2basicstyleimplplugin
- NO_PLUGIN_OPTIONAL
+ DEPENDENCIES
+ QtQuick/auto
SOURCES
qquickbasicbusyindicator.cpp qquickbasicbusyindicator_p.h
qquickbasicdial.cpp qquickbasicdial_p.h
diff --git a/src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp b/src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp
index 2acefa2bed..d6cceb2a56 100644
--- a/src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp
+++ b/src/quickcontrols/basic/impl/qquickbasicbusyindicator.cpp
@@ -10,7 +10,7 @@
QT_BEGIN_NAMESPACE
static const int CircleCount = 10;
-static const int TotalDuration = 100 * CircleCount * 2;
+static const int QbbiTotalDuration = 100 * CircleCount * 2;
static const QRgb TransparentColor = 0x00000000;
static QPointF moveCircle(const QPointF &pos, qreal rotation, qreal distance)
@@ -35,7 +35,7 @@ QQuickBasicBusyIndicatorNode::QQuickBasicBusyIndicatorNode(QQuickBasicBusyIndica
: QQuickAnimatedNode(item)
{
setLoopCount(Infinite);
- setDuration(TotalDuration);
+ setDuration(QbbiTotalDuration);
setCurrentTime(item->elapsed());
for (int i = 0; i < CircleCount; ++i) {
@@ -51,7 +51,7 @@ QQuickBasicBusyIndicatorNode::QQuickBasicBusyIndicatorNode(QQuickBasicBusyIndica
void QQuickBasicBusyIndicatorNode::updateCurrentTime(int time)
{
- const qreal percentageComplete = time / qreal(TotalDuration);
+ const qreal percentageComplete = time / qreal(QbbiTotalDuration);
const qreal firstPhaseProgress = percentageComplete <= 0.5 ? percentageComplete * 2 : 0;
const qreal secondPhaseProgress = percentageComplete > 0.5 ? (percentageComplete - 0.5) * 2 : 0;
diff --git a/src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h b/src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h
index a39b61cde4..edf9d68078 100644
--- a/src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h
+++ b/src/quickcontrols/basic/impl/qquickbasicbusyindicator_p.h
@@ -56,6 +56,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickBasicBusyIndicator)
-
#endif // QQUICKDEFAULTBUSYINDICATOR_P_H
diff --git a/src/quickcontrols/basic/impl/qquickbasicdial.cpp b/src/quickcontrols/basic/impl/qquickbasicdial.cpp
index 7726bfb159..d2550b7c27 100644
--- a/src/quickcontrols/basic/impl/qquickbasicdial.cpp
+++ b/src/quickcontrols/basic/impl/qquickbasicdial.cpp
@@ -29,6 +29,34 @@ void QQuickBasicDial::setProgress(qreal progress)
update();
}
+qreal QQuickBasicDial::startAngle() const
+{
+ return m_startAngle;
+}
+
+void QQuickBasicDial::setStartAngle(qreal startAngle)
+{
+ if (startAngle == m_startAngle)
+ return;
+
+ m_startAngle = startAngle;
+ update();
+}
+
+qreal QQuickBasicDial::endAngle() const
+{
+ return m_endAngle;
+}
+
+void QQuickBasicDial::setEndAngle(qreal endAngle)
+{
+ if (endAngle == m_endAngle)
+ return;
+
+ m_endAngle = endAngle;
+ update();
+}
+
QColor QQuickBasicDial::color() const
{
return m_color;
@@ -70,8 +98,8 @@ void QQuickBasicDial::paint(QPainter *painter)
painter->setRenderHint(QPainter::Antialiasing);
- const qreal startAngle = (140 + 90);
- const qreal spanAngle = (m_progress * 280) * -1;
+ const qreal startAngle = 90. - m_startAngle;
+ const qreal spanAngle = m_progress * (m_startAngle - m_endAngle);
QPainterPath path;
path.arcMoveTo(rect, startAngle);
path.arcTo(rect, startAngle, spanAngle);
diff --git a/src/quickcontrols/basic/impl/qquickbasicdial_p.h b/src/quickcontrols/basic/impl/qquickbasicdial_p.h
index 1406bf6906..31e533fdc8 100644
--- a/src/quickcontrols/basic/impl/qquickbasicdial_p.h
+++ b/src/quickcontrols/basic/impl/qquickbasicdial_p.h
@@ -25,6 +25,8 @@ class QQuickBasicDial : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(qreal progress READ progress WRITE setProgress FINAL)
+ Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle FINAL)
+ Q_PROPERTY(qreal endAngle READ endAngle WRITE setEndAngle FINAL)
Q_PROPERTY(QColor color READ color WRITE setColor FINAL)
QML_NAMED_ELEMENT(DialImpl)
QML_ADDED_IN_VERSION(2, 0)
@@ -35,6 +37,12 @@ public:
qreal progress() const;
void setProgress(qreal progress);
+ qreal startAngle() const;
+ void setStartAngle(qreal startAngle);
+
+ qreal endAngle() const;
+ void setEndAngle(qreal endAngle);
+
QColor color() const;
void setColor(const QColor &color);
@@ -42,6 +50,8 @@ public:
private:
qreal m_progress = 0;
+ qreal m_startAngle = -140.;
+ qreal m_endAngle = 140.;
QColor m_color = Qt::black;
};
diff --git a/src/quickcontrols/basic/impl/qquickbasicprogressbar.cpp b/src/quickcontrols/basic/impl/qquickbasicprogressbar.cpp
index 4dbe04e4b1..1da27fa0aa 100644
--- a/src/quickcontrols/basic/impl/qquickbasicprogressbar.cpp
+++ b/src/quickcontrols/basic/impl/qquickbasicprogressbar.cpp
@@ -15,9 +15,9 @@ static const int BlockWidth = 16;
static const int BlockRestingSpacing = 4;
static const int BlockMovingSpacing = 48;
static const int BlockSpan = Blocks * (BlockWidth + BlockRestingSpacing) - BlockRestingSpacing;
-static const int TotalDuration = 4000;
-static const int SecondPhaseStart = TotalDuration * 0.4;
-static const int ThirdPhaseStart = TotalDuration * 0.6;
+static const int QbpbTotalDuration = 4000;
+static const int SecondPhaseStart = QbpbTotalDuration * 0.4;
+static const int ThirdPhaseStart = QbpbTotalDuration * 0.6;
static inline qreal blockStartX(int blockIndex)
{
@@ -53,7 +53,7 @@ QQuickBasicProgressBarNode::QQuickBasicProgressBarNode(QQuickBasicProgressBar *i
m_pixelsPerSecond(item->width())
{
setLoopCount(Infinite);
- setDuration(TotalDuration);
+ setDuration(QbpbTotalDuration);
}
void QQuickBasicProgressBarNode::updateCurrentTime(int time)
@@ -143,7 +143,6 @@ void QQuickBasicProgressBarNode::sync(QQuickItem *item)
QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode*>(transformNode->firstChild());
if (!rectNode) {
rectNode = d->sceneGraphContext()->createInternalRectangleNode();
- rectNode->setColor(bar->color());
transformNode->appendChildNode(rectNode);
}
@@ -151,6 +150,9 @@ void QQuickBasicProgressBarNode::sync(QQuickItem *item)
m.translate(blockStartX(i), 0);
transformNode->setMatrix(m);
+ // Set the color here too in case it was set after component completion,
+ // as updateCurrentTime doesn't sync it.
+ rectNode->setColor(bar->color());
rectNode->setRect(QRectF(QPointF(0, 0), QSizeF(BlockWidth, item->implicitHeight())));
rectNode->update();
@@ -165,10 +167,12 @@ void QQuickBasicProgressBarNode::sync(QQuickItem *item)
QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(firstChild());
if (!rectNode) {
rectNode = d->sceneGraphContext()->createInternalRectangleNode();
- rectNode->setColor(bar->color());
appendChildNode(rectNode);
}
+ // Always set the color, not just when creating the rectangle node, so that we respect
+ // changes that are made after component completion.
+ rectNode->setColor(bar->color());
rectNode->setRect(QRectF(QPointF(0, 0), QSizeF(bar->progress() * item->width(), item->implicitHeight())));
rectNode->update();
}
diff --git a/src/quickcontrols/basic/impl/qquickbasicprogressbar_p.h b/src/quickcontrols/basic/impl/qquickbasicprogressbar_p.h
index 72b5521263..5ca901924e 100644
--- a/src/quickcontrols/basic/impl/qquickbasicprogressbar_p.h
+++ b/src/quickcontrols/basic/impl/qquickbasicprogressbar_p.h
@@ -54,6 +54,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickBasicProgressBar)
-
#endif // QQUICKDEFAULTPROGRESSBAR_P_H
diff --git a/src/quickcontrols/basic/qquickbasicstyle_p.h b/src/quickcontrols/basic/qquickbasicstyle_p.h
index f543d27e47..00d90612c8 100644
--- a/src/quickcontrols/basic/qquickbasicstyle_p.h
+++ b/src/quickcontrols/basic/qquickbasicstyle_p.h
@@ -18,11 +18,11 @@
#include <QtCore/qobject.h>
#include <QtGui/qcolor.h>
#include <QtQml/qqml.h>
-#include <QtCore/private/qglobal_p.h>
+#include <QtQuickControls2Basic/qtquickcontrols2basicexports.h>
QT_BEGIN_NAMESPACE
-class QQuickBasicStyle : public QObject
+class Q_QUICKCONTROLS2BASIC_EXPORT QQuickBasicStyle : public QObject
{
Q_OBJECT
Q_PROPERTY(QColor backgroundColor READ backgroundColor CONSTANT FINAL)
diff --git a/src/quickcontrols/basic/qquickbasictheme_p.h b/src/quickcontrols/basic/qquickbasictheme_p.h
index 710c57f074..bcedee2a12 100644
--- a/src/quickcontrols/basic/qquickbasictheme_p.h
+++ b/src/quickcontrols/basic/qquickbasictheme_p.h
@@ -15,13 +15,13 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
+#include <QtQuickControls2Basic/qtquickcontrols2basicexports.h>
QT_BEGIN_NAMESPACE
class QQuickTheme;
-class QQuickBasicTheme
+class Q_QUICKCONTROLS2BASIC_EXPORT QQuickBasicTheme
{
public:
static void initialize(QQuickTheme *theme);
diff --git a/src/quickcontrols/basic/qtquickcontrols2basicforeign.cpp b/src/quickcontrols/basic/qtquickcontrols2basicforeign.cpp
new file mode 100644
index 0000000000..7a5472e758
--- /dev/null
+++ b/src/quickcontrols/basic/qtquickcontrols2basicforeign.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtquickcontrols2basicforeign_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
+
+#include "moc_qtquickcontrols2basicforeign_p.cpp"
diff --git a/src/quickcontrols/basic/qtquickcontrols2basicforeign_p.h b/src/quickcontrols/basic/qtquickcontrols2basicforeign_p.h
new file mode 100644
index 0000000000..203a1c69a3
--- /dev/null
+++ b/src/quickcontrols/basic/qtquickcontrols2basicforeign_p.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QTQUICKCONTROLS2BASICFOREIGN_P_H
+#define QTQUICKCONTROLS2BASICFOREIGN_P_H
+
+#include <QtQml/qqml.h>
+#include <QtQuickTemplates2/private/qquickoverlay_p.h>
+#if QT_CONFIG(quicktemplates2_container)
+#include <QtQuickTemplates2/private/qquicksplitview_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// These are necessary in order to use C++ types in a file where only QtQuick.Controls has been imported.
+// Control types like Button don't need this done for them, as each style module provides a Button type,
+// and QtQuick.Controls is a sort of alias for the active style import.
+
+struct QQuickOverlayAttachedForeign
+{
+ Q_GADGET
+ QML_NAMED_ELEMENT(Overlay)
+ QML_FOREIGN(QQuickOverlay)
+ QML_UNCREATABLE("")
+ QML_ADDED_IN_VERSION(2, 3)
+};
+
+#if QT_CONFIG(quicktemplates2_container)
+struct QQuickSplitHandleAttachedForeign
+{
+ Q_GADGET
+ QML_NAMED_ELEMENT(SplitHandle)
+ QML_FOREIGN(QQuickSplitHandleAttached)
+ QML_UNCREATABLE("")
+ QML_ADDED_IN_VERSION(2, 13)
+};
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QTQUICKCONTROLS2BASICFOREIGN_P_H
diff --git a/src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp b/src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp
index 57c52fff27..e2d726082b 100644
--- a/src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp
+++ b/src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp
@@ -5,7 +5,6 @@
#include "qquickbasictheme_p.h"
#include <QtQuickControls2/private/qquickstyleplugin_p.h>
-#include <QtQuickTemplates2/private/qquicktheme_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quickcontrols/configure.cmake b/src/quickcontrols/configure.cmake
index e713e73f7c..b146186426 100644
--- a/src/quickcontrols/configure.cmake
+++ b/src/quickcontrols/configure.cmake
@@ -48,19 +48,19 @@ qt_feature("quickcontrols2-macos" PRIVATE
SECTION "Quick Controls 2"
LABEL "macOS"
PURPOSE "Provides a native macOS desktop style."
- CONDITION QT_FEATURE_quickcontrols2_basic
+ CONDITION QT_FEATURE_quickcontrols2_basic AND MACOS
)
qt_feature("quickcontrols2-ios" PRIVATE
SECTION "Quick Controls 2"
LABEL "iOS"
PURPOSE "Provides a native-looking iOS style."
- CONDITION QT_FEATURE_quickcontrols2_basic
+ CONDITION QT_FEATURE_quickcontrols2_basic AND (IOS OR MACOS)
)
qt_feature("quickcontrols2-windows" PRIVATE
SECTION "Quick Controls 2"
LABEL "Windows"
PURPOSE "Provides a native Windows desktop style."
- CONDITION QT_FEATURE_quickcontrols2_basic
+ CONDITION QT_FEATURE_quickcontrols2_basic AND WIN32
)
qt_configure_add_summary_section(NAME "Qt Quick Controls 2")
qt_configure_add_summary_entry(
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-basic-popup-property-propagation.png b/src/quickcontrols/doc/images/qtquickcontrols-basic-popup-property-propagation.png
new file mode 100644
index 0000000000..a38e38d915
--- /dev/null
+++ b/src/quickcontrols/doc/images/qtquickcontrols-basic-popup-property-propagation.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-basic-thumbnail.png b/src/quickcontrols/doc/images/qtquickcontrols-basic-thumbnail.png
deleted file mode 100644
index e971a7aaec..0000000000
--- a/src/quickcontrols/doc/images/qtquickcontrols-basic-thumbnail.png
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-basic.png b/src/quickcontrols/doc/images/qtquickcontrols-basic.png
index 099db17038..2348d3f280 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-basic.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-basic.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-fusion-dark.png b/src/quickcontrols/doc/images/qtquickcontrols-fusion-dark.png
new file mode 100644
index 0000000000..4e3f72e2b6
--- /dev/null
+++ b/src/quickcontrols/doc/images/qtquickcontrols-fusion-dark.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-fusion-light.png b/src/quickcontrols/doc/images/qtquickcontrols-fusion-light.png
new file mode 100644
index 0000000000..44403c6786
--- /dev/null
+++ b/src/quickcontrols/doc/images/qtquickcontrols-fusion-light.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-fusion-thumbnail.png b/src/quickcontrols/doc/images/qtquickcontrols-fusion-thumbnail.png
deleted file mode 100644
index c270c6efba..0000000000
--- a/src/quickcontrols/doc/images/qtquickcontrols-fusion-thumbnail.png
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-imagine-thumbnail.png b/src/quickcontrols/doc/images/qtquickcontrols-imagine-thumbnail.png
deleted file mode 100644
index ecf2bb163d..0000000000
--- a/src/quickcontrols/doc/images/qtquickcontrols-imagine-thumbnail.png
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-imagine.png b/src/quickcontrols/doc/images/qtquickcontrols-imagine.png
index ffe2c267b2..4e928b7a6a 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-imagine.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-imagine.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-ios-dark.png b/src/quickcontrols/doc/images/qtquickcontrols-ios-dark.png
index fd4609c729..bfa014aee9 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-ios-dark.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-ios-dark.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-ios-light.png b/src/quickcontrols/doc/images/qtquickcontrols-ios-light.png
index f990078ddf..5a3e868274 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-ios-light.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-ios-light.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-macos-dark.png b/src/quickcontrols/doc/images/qtquickcontrols-macos-dark.png
index 97d8a5a4e7..b639221e9b 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-macos-dark.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-macos-dark.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-macos-light.png b/src/quickcontrols/doc/images/qtquickcontrols-macos-light.png
index 4a598ad498..653aefc3ed 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-macos-light.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-macos-light.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-macos-thumbnail.png b/src/quickcontrols/doc/images/qtquickcontrols-macos-thumbnail.png
deleted file mode 100644
index 57fd98d63a..0000000000
--- a/src/quickcontrols/doc/images/qtquickcontrols-macos-thumbnail.png
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-accent.png b/src/quickcontrols/doc/images/qtquickcontrols-material-accent.png
index a85afc68ed..e058fc81f4 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-accent.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-accent.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-attributes.png b/src/quickcontrols/doc/images/qtquickcontrols-material-attributes.png
index bb54071d79..60362a94aa 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-attributes.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-attributes.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-background.png b/src/quickcontrols/doc/images/qtquickcontrols-material-background.png
index 62028f523c..376bd2ef05 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-background.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-background.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-dark.png b/src/quickcontrols/doc/images/qtquickcontrols-material-dark.png
index f6e45b3b1c..3a24d4424a 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-dark.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-dark.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-elevation.png b/src/quickcontrols/doc/images/qtquickcontrols-material-elevation.png
index d152f14529..be72fa0da8 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-elevation.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-elevation.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-foreground.png b/src/quickcontrols/doc/images/qtquickcontrols-material-foreground.png
index 0d0e1b651a..e7564dee84 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-foreground.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-foreground.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-light.png b/src/quickcontrols/doc/images/qtquickcontrols-material-light.png
index c9abe2cb8b..a51ae9ecb0 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-light.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-light.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-theme.png b/src/quickcontrols/doc/images/qtquickcontrols-material-theme.png
index 1d09a82580..79bd1899ec 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-theme.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-theme.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-thumbnail.png b/src/quickcontrols/doc/images/qtquickcontrols-material-thumbnail.png
deleted file mode 100644
index d758ff1803..0000000000
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-thumbnail.png
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-variant-dense.png b/src/quickcontrols/doc/images/qtquickcontrols-material-variant-dense.png
index 02f39b7885..314e1338ce 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-variant-dense.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-variant-dense.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-material-variant-normal.png b/src/quickcontrols/doc/images/qtquickcontrols-material-variant-normal.png
index c1e366c531..2263b72b11 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-material-variant-normal.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-material-variant-normal.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-universal-dark.png b/src/quickcontrols/doc/images/qtquickcontrols-universal-dark.png
index 952651dca0..85b3e70db7 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-universal-dark.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-universal-dark.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-universal-light.png b/src/quickcontrols/doc/images/qtquickcontrols-universal-light.png
index e65447a4c5..fa9375903f 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-universal-light.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-universal-light.png
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-universal-thumbnail.png b/src/quickcontrols/doc/images/qtquickcontrols-universal-thumbnail.png
deleted file mode 100644
index db7fe5b076..0000000000
--- a/src/quickcontrols/doc/images/qtquickcontrols-universal-thumbnail.png
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-windows-thumbnail.png b/src/quickcontrols/doc/images/qtquickcontrols-windows-thumbnail.png
deleted file mode 100644
index 15408d3bba..0000000000
--- a/src/quickcontrols/doc/images/qtquickcontrols-windows-thumbnail.png
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/doc/images/qtquickcontrols-windows.png b/src/quickcontrols/doc/images/qtquickcontrols-windows.png
index 324a03412d..c0adcd5549 100644
--- a/src/quickcontrols/doc/images/qtquickcontrols-windows.png
+++ b/src/quickcontrols/doc/images/qtquickcontrols-windows.png
Binary files differ
diff --git a/src/quickcontrols/doc/qtquickcontrols.qdocconf b/src/quickcontrols/doc/qtquickcontrols.qdocconf
index a8f050bce7..8c5481bd98 100644
--- a/src/quickcontrols/doc/qtquickcontrols.qdocconf
+++ b/src/quickcontrols/doc/qtquickcontrols.qdocconf
@@ -1,5 +1,5 @@
include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
-include($QT_INSTALL_DOCS/config/exampleurl-qtquickcontrols.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtquickcontrols2.qdocconf)
include(manifest-meta.qdocconf)
moduleheader = QtQuickControls2
@@ -29,7 +29,7 @@ qhp.QtQuickControls.subprojects.examples.title = Examples
qhp.QtQuickControls.subprojects.examples.indexTitle = Qt Quick Controls Examples
qhp.QtQuickControls.subprojects.examples.selectors = fake:example
-depends = qtcore qtgui qtdoc qtqml qtqmlmodels qtquick qtsql qtwidgets qtlabsplatform qmake qtcmake
+depends = qtcore qtgui qtdoc qtqml qtqmlmodels qtquick qtquickdialogs qtsql qtwidgets qtlabsplatform qmake qtcmake qtvirtualkeyboard
# Specify the install path under QT_INSTALL_EXAMPLES
# Note: paths passed to \example command must contain the parent directory, e.g.
@@ -39,7 +39,7 @@ exampledirs += ../../../examples/quickcontrols \
../ \
snippets
-examples.fileextensions += "*.conf"
+examples.fileextensions += "*.conf *.theme"
examplesinstallpath = quickcontrols
@@ -66,39 +66,51 @@ tagfile = qtquickcontrols.tags
# \styleimport {QtQuick.Controls.Universal 2.0}
macro.styleimport.HTML = "<table class=\"alignedsummary\"><tbody><tr><td class=\"memItemLeft rightAlign topAlign\"> Import Statement:</td><td class=\"memItemRight bottomAlign\"> import \1</td></tr><tr><td class=\"memItemLeft rightAlign topAlign\"> Since:</td><td class=\"memItemRight bottomAlign\"> \2</td></tr></tbody></table>"
+macro.styleimport.DocBook = "<db:variablelist><db:varlistentry><db:term>Import Statement</db:term><db:listitem><db:para>import \1</db:para></db:listitem></db:varlistentry><db:varlistentry><db:term>Since</db:term><db:listitem><db:para>\2</db:para></db:listitem></db:varlistentry></db:variablelist>"
-# \styleproperty {Universal.accent} {enumeration} {html-target-id}
+# \styleproperty {propertyname} {enumeration}
# \target html-target-id
# This property holds ...
# (empty line)
# \endstyleproperty
-macro.styleproperty.HTML = "<div class=\"qmlproto\"><table class=\"qmlname\"><tbody><tr valign=\"top\" class=\"odd\" id=\"\3\"><td class=\"tblQmlPropNode\"><p><span class=\"name\">\1</span> : <span class=\"type\">\2</span></p></td></tr></tbody></table></div>"
-macro.endstyleproperty = "\\br"
+macro.styleproperty.HTML = "<div class=\"qmlproto\"><table class=\"qmlname\"><tbody><tr valign=\"top\" class=\"odd\"><td class=\"tblQmlPropNode\"><p><span class=\"name\">\1</span> : <span class=\"type\">\2</span></p></td></tr></tbody></table></div>"
+macro.endstyleproperty.HTML = "<br/>"
+macro.styleproperty.DocBook = "<db:section><db:title>\1 : \2</db:title><db:fieldsynopsis><db:type>\2</db:type><db:varname>\1</db:varname></db:fieldsynopsis>"
+macro.endstyleproperty.DocBook = "</db:section>"
-# \stylemethod {returntype} {methodname} {argtype} {argname} {html-target-id}
+# \stylemethod {returntype} {methodname} {argtype} {argname}
# \target html-target-id
# This property holds ...
# (empty line)
# \endstylemethod
-macro.stylemethod.HTML = "<div class=\"qmlproto\"><table class=\"qmlname\"><tbody><tr valign=\"top\" class=\"odd\" id=\"\5\"><td class=\"tblQmlFuncNode\"><p><span class=\"type\">\1</span> <span class=\"name\">\2</span>(<span class="type">\3</span> <i>\4</i>)</p></td></tr></tbody></table></div>"
-macro.endstylemethod = "\\br"
+macro.stylemethod.HTML = "<div class=\"qmlproto\"><table class=\"qmlname\"><tbody><tr valign=\"top\" class=\"odd\"><td class=\"tblQmlFuncNode\"><p><span class=\"type\">\1</span> <span class=\"name\">\2</span>(<span class="type">\3</span> <i>\4</i>)</p></td></tr></tbody></table></div>"
+macro.endstylemethod.HTML = "<br/>"
+macro.stylemethod.DocBook = "<db:section><db:title>\1 \2(\3 <db:emphasis>\4</db:emphasis>)</db:title><db:methodsynopsis><db:type>\1</db:type><db:methodname>\2</db:methodname><db:methodparam><db:type>\3</db:type><db:parameter>\4</db:parameter></db:methodparam></db:methodsynopsis>"
+macro.endstylemethod.DocBook = "</db:section>"
-# \stylemethod2 {returntype} {methodname} {arg1type} {arg1name} {arg2type} {arg2name} {html-target-id}
+# \stylemethod2 {returntype} {methodname} {arg1type} {arg1name} {arg2type} {arg2name}
# \target html-target-id
# This method returns ...
# (empty line)
# \endstylemethod2
-macro.stylemethod2.HTML = "<div class=\"qmlproto\"><table class=\"qmlname\"><tbody><tr valign=\"top\" class=\"odd\" id=\"\7\"><td class=\"tblQmlFuncNode\"><p><span class=\"type\">\1</span> <span class=\"name\">\2</span>(<span class="type">\3</span> <i>\4</i>, <span class="type">\5</span> <i>\6</i>)</p></td></tr></tbody></table></div>"
-macro.endstylemethod2 = "\\br"
+macro.stylemethod2.HTML = "<div class=\"qmlproto\"><table class=\"qmlname\"><tbody><tr valign=\"top\" class=\"odd\"><td class=\"tblQmlFuncNode\"><p><span class=\"type\">\1</span> <span class=\"name\">\2</span>(<span class="type">\3</span> <i>\4</i>, <span class="type">\5</span> <i>\6</i>)</p></td></tr></tbody></table></div>"
+macro.endstylemethod2.HTML = "<br/>"
+macro.stylemethod2.DocBook = "<db:section><db:title>\1 \2(\3 <db:emphasis>\4</db:emphasis>)</db:title><db:methodsynopsis><db:type>\1</db:type><db:methodname>\2</db:methodname><db:methodparam><db:type>\3</db:type><db:parameter>\4</db:parameter></db:methodparam><db:methodparam><db:type>\5</db:type><db:parameter>\6</db:parameter></db:methodparam></db:methodsynopsis>"
+macro.endstylemethod2.DocBook = "</db:section>"
# \stylecolor {#6A00FF} {(default)}
macro.stylecolor.HTML = "<div style=\"padding:10px;color:#fff;background:\1;\">\1 \2</div>"
+macro.stylecolor.DocBook = "<db:phrase role=\"color:\1\">\1 \2</db:phrase>"
macro.nbsp.HTML = "&nbsp;"
+macro.nbsp.DocBook = "&#160;"
-#Add output suffix to the html filenames
-outputsuffixes = QML
-outputsuffixes.QML = 2
+manifestmeta.highlighted.names = \
+ "QtQuickControls/File System Explorer" \
+ "QtQuickControls/Qt Quick Controls - Wearable Demo" \
+ "QtQuickControls/Qt Quick Controls - Gallery" \
+ "QtQuickControls/Qt Quick Controls - Event Calendar" \
+ "QtQuickControls/Qt Quick Controls - Contact List"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-action.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-action.qml
index 4f50ba46d7..5b003e622c 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-action.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-action.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-busyindicator-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-busyindicator-custom.qml
index 3eefb11110..951976210a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-busyindicator-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-busyindicator-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-button-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-button-custom.qml
index cf197a18ae..c9778faa3b 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-button-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-button-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-button-icononly.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-button-icononly.qml
index 027ecb4ed8..d56fee4b5a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-button-icononly.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-button-icononly.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-button-textbesideicon.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-button-textbesideicon.qml
index c2dce1d99a..c2d2e8e52f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-button-textbesideicon.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-button-textbesideicon.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-button-textonly.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-button-textonly.qml
index 4b12b8acc2..ef239d9814 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-button-textonly.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-button-textonly.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-calendarmodel.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-calendarmodel.qml
index af2777b123..c0fcda2d41 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-calendarmodel.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-calendarmodel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-custom.qml
index a7421af39c..d3c2f98ec1 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-group.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-group.qml
index 0d676be397..75f64593eb 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-group.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-checkbox-group.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-checkdelegate-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-checkdelegate-custom.qml
index a4f4b84d18..d2d8c622d5 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-checkdelegate-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-checkdelegate-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-accepted.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-accepted.qml
index 6f5eff6b55..0362041e18 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-accepted.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-accepted.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml
index 2481f6dcf8..ff499f6550 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-custom.qml
@@ -1,7 +1,9 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls
@@ -10,11 +12,14 @@ ComboBox {
model: ["First", "Second", "Third"]
delegate: ItemDelegate {
+ id: delegate
+
+ required property var model
+ required property int index
+
width: control.width
contentItem: Text {
- text: control.textRole
- ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole])
- : modelData
+ text: delegate.model[control.textRole]
color: "#21be2b"
font: control.font
elide: Text.ElideRight
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-find.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-find.qml
index 2513002264..d6af08166f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-find.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-find.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-popup.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-popup.qml
index 8c636a3d5c..94ffa2b2bc 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-popup.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-textat.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-textat.qml
index 8910ba3237..e81ae17dd0 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-textat.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-textat.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-valuerole.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-valuerole.qml
index 8d32201799..9329b19d1c 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-valuerole.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-combobox-valuerole.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-container-oncurrentindexchanged.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-container-oncurrentindexchanged.qml
new file mode 100644
index 0000000000..19b48191ee
--- /dev/null
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-container-oncurrentindexchanged.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick.Controls
+
+Container {
+ //! [1]
+ onCurrentIndexChanged: {
+ print("currentIndex changed to", currentIndex)
+ // ...
+ }
+ //! [1]
+}
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-custom-palette-buttons.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-custom-palette-buttons.qml
new file mode 100644
index 0000000000..3b72207d87
--- /dev/null
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-custom-palette-buttons.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//![entire]
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ visible: true
+
+ //![palette]
+ palette {
+ buttonText: "red"
+ button: "khaki"
+
+ disabled {
+ buttonText: "lavender"
+ button: "coral"
+ }
+ }
+ //![palette]
+
+ ColumnLayout {
+ id: layout
+ anchors.fill: parent
+ anchors.margins: 3
+ Button {
+ text: qsTr("Disabled button")
+ enabled: false
+ }
+
+ Button {
+ text: qsTr("Enabled button")
+ }
+
+ TextField {
+ Layout.fillWidth: true
+ placeholderText: "type something here"
+ }
+ }
+}
+//![entire]
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow-layout.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow-layout.qml
index 2358f252e0..7824faeb3e 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow-layout.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow-layout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow.qml
index 2188b4ccf1..f5d1edab0f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-dayofweekrow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-delaybutton-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-delaybutton-custom.qml
index a8b17ab36a..1803556052 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-delaybutton-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-delaybutton-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-dial-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-dial-custom.qml
index f1efc22c4d..65a7439ea1 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-dial-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-dial-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
@@ -10,6 +10,8 @@ Dial {
background: Rectangle {
x: control.width / 2 - width / 2
y: control.height / 2 - height / 2
+ implicitWidth: 140
+ implicitHeight: 140
width: Math.max(64, Math.min(control.width, control.height))
height: width
color: "transparent"
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modal.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modal.qml
index ba1580a4df..7258df5873 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modal.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modal.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modeless.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modeless.qml
index d70c9921e2..05d06a7dae 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modeless.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-dialog-modeless.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-dialog.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-dialog.qml
index 4d774c4e10..7fb50e6169 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-dialog.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox-attached.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox-attached.qml
index ef85771bfd..7582d605fa 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox-attached.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox-attached.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox.qml
index 13a927520f..424be46707 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-dialogbuttonbox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-frame-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-frame-custom.qml
index de3e1a2327..51dd757044 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-frame-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-frame-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-frame.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-frame.qml
index fcb128db54..f5bfd6178f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-frame.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-frame.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-checkable.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-checkable.qml
index 0ab0822aca..fa6a91065f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-checkable.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-checkable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-custom.qml
index 34d9df8a63..38c0f03366 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox.qml
index 98282ed348..9fb7a20873 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-groupbox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-headerview.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-headerview.qml
new file mode 100644
index 0000000000..b8f6935010
--- /dev/null
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-headerview.qml
@@ -0,0 +1,79 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//![0]
+import QtQuick
+import QtQuick.Controls
+import Qt.labs.qmlmodels
+
+ApplicationWindow {
+ visible: true
+ width: 640
+ height: 480
+ title: qsTr("HeaderView")
+
+ Rectangle {
+ anchors.fill: parent
+ // The background color will show through the cell
+ // spacing, and therefore become the grid line color.
+ color: Qt.styleHints.appearance === Qt.Light ? palette.mid : palette.midlight
+
+ HorizontalHeaderView {
+ id: horizontalHeader
+ anchors.left: tableView.left
+ anchors.top: parent.top
+ syncView: tableView
+ clip: true
+ }
+
+ VerticalHeaderView {
+ id: verticalHeader
+ anchors.top: tableView.top
+ anchors.left: parent.left
+ syncView: tableView
+ clip: true
+ }
+
+ TableView {
+ id: tableView
+ anchors.left: verticalHeader.right
+ anchors.top: horizontalHeader.bottom
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ clip: true
+
+ columnSpacing: 1
+ rowSpacing: 1
+
+ model: TableModel {
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "color" }
+
+ rows: [
+ {
+ "name": "cat",
+ "color": "black"
+ },
+ {
+ "name": "dog",
+ "color": "brown"
+ },
+ {
+ "name": "bird",
+ "color": "white"
+ }
+ ]
+ }
+
+ delegate: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 20
+ color: palette.base
+ Label {
+ text: display
+ }
+ }
+ }
+ }
+//![0]
+}
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate-custom.qml
index 3212cbc762..8dcc860dd7 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate.qml
index 49c12b1dff..4d7fa30f22 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-itemdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-label-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-label-custom.qml
index 79bc9d3c13..0443e4e357 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-label-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-label-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-label.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-label.qml
index 7ca63d0251..3dae69a1e1 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-label.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-label.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-material-accent.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-material-accent.qml
index 71a84fac82..5c6b58533a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-material-accent.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-material-accent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-material-attributes.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-material-attributes.qml
new file mode 100644
index 0000000000..1f0298de44
--- /dev/null
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-material-attributes.qml
@@ -0,0 +1,120 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Material
+import QtQuick.Shapes
+
+Pane {
+ width: 400
+ height: 300
+
+ Page {
+ anchors.fill: parent
+ anchors.margins: 40
+
+ header: ToolBar {
+ Label {
+ text: "Material"
+ anchors.centerIn: parent
+ }
+ }
+
+ TextField {
+ id: textField
+ text: "TextField"
+ anchors.centerIn: parent
+
+ Component.onCompleted: forceActiveFocus()
+ }
+ }
+
+ component Line: Shape {
+ // Account for 1-pixel-wide lines.
+ width: Math.max(1, endX - startX)
+ height: Math.max(1, endY - startY)
+
+ layer.enabled: true
+ layer.samples: 4
+
+ property alias startX: shapePath.startX
+ property alias startY: shapePath.startY
+ property alias endX: pathLine.x
+ property alias endY: pathLine.y
+
+ ShapePath {
+ id: shapePath
+ strokeWidth: 1
+ strokeColor: "#444"
+
+ PathLine {
+ id: pathLine
+ }
+ }
+ }
+
+ Label {
+ id: primaryLabel
+ x: 40
+ y: 3
+ text: "Primary"
+ }
+ Line {
+ id: primaryLine
+ x: primaryLabel.x + primaryLabel.width / 2
+ y: primaryLabel.y + primaryLabel.height
+ startX: 0.5
+ startY: 0
+ endX: 0.5
+ endY: 40
+ }
+
+ Label {
+ id: foregroundLabel
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: 3
+ text: "Foreground"
+ }
+ Line {
+ id: foregroundLine
+ x: foregroundLabel.x + foregroundLabel.width / 2
+ y: foregroundLabel.y + foregroundLabel.height
+ // Lines are drawn at the center of the pixel.
+ startX: 0.5
+ startY: 0
+ endX: 0.5
+ endY: 34
+ }
+
+ Label {
+ id: accentLabel
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: parent.height * 0.825
+ text: "Accent"
+ }
+ Line {
+ id: accentLine
+ x: accentLabel.x + accentLabel.width / 2
+ y: parent.height * 0.7
+ startX: 0.5
+ startY: 0
+ endX: 0.5
+ endY: 38
+ }
+
+ Label {
+ id: backgroundLabel
+ x: parent.width - width - 10
+ y: parent.height - height - 10
+ text: "Background"
+ }
+ Line {
+ id: backgroundLine
+ x: backgroundLabel.x + backgroundLabel.width / 2
+ y: backgroundLabel.y - height
+ startX: 0.5
+ startY: 0
+ endX: 0.5
+ endY: 40
+ }
+}
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-material-background.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-material-background.qml
index f9f1dd0915..8d4829e17a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-material-background.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-material-background.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-material-elevation.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-material-elevation.qml
index 4f8b27e7ef..8b84623dab 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-material-elevation.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-material-elevation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-material-foreground.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-material-foreground.qml
index b4a7743c16..d2c120e0e1 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-material-foreground.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-material-foreground.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-material-theme.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-material-theme.qml
index 4e5bf81cd9..bacb630a28 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-material-theme.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-material-theme.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-material-variant.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-material-variant.qml
index ad824f1e34..bd2f42e66d 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-material-variant.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-material-variant.qml
@@ -1,5 +1,15 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/*
+ This file is used by tst_snippets to generate qtquickcontrols-material-variant-normal.png:
+
+ SCREENSHOTS=1 ./tst_snippets verify:qtquickcontrols-material-variant
+
+ and qtquickcontrols-material-variant-dense.png:
+
+ SCREENSHOTS=1 QT_QUICK_CONTROLS_MATERIAL_VARIANT=Dense ./tst_snippets verify:qtquickcontrols-material-variant
+*/
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-menu-createObject.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-menu-createObject.qml
index 31991ced70..c6582fc037 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-menu-createObject.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-menu-createObject.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-menu-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-menu-custom.qml
index f38d5f370a..6d1c587f74 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-menu-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-menu-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-menu-instantiator.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-menu-instantiator.qml
index cda91182df..6ceeb217b3 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-menu-instantiator.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-menu-instantiator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-menubar-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-menubar-custom.qml
index 2d2d15efdf..d76f4f56cd 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-menubar-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-menubar-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-menubar.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-menubar.qml
index 55064604b4..b7f3a32f80 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-menubar.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-menubar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator-custom.qml
index 2b66187e10..81c1e7a7f5 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator.qml
index 6be0de85bc..34c5933788 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-menuseparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-layout.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-layout.qml
index 0620ee6d0b..0ac8ef912e 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-layout.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-layout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-localization.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-localization.qml
index 56a801ab75..e5bdc4fa37 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-localization.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid-localization.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
pragma ComponentBehavior: Bound
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid.qml
index c630edbe26..72aba7827f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-monthgrid.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modal.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modal.qml
index d1537d73bf..25a7485121 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modal.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modal.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modeless.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modeless.qml
index 06ebb16fb1..c51f8caea6 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modeless.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-overlay-modeless.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-overview.cmake b/src/quickcontrols/doc/snippets/qtquickcontrols-overview.cmake
index 6aa63152db..cb9ccdf309 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-overview.cmake
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-overview.cmake
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#! [0]
find_package(Qt6 REQUIRED COMPONENTS QuickControls2)
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-custom.qml
index 5372c41f31..93c2045fc3 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-interactive.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-interactive.qml
index e3e65beb22..d940f395ef 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-interactive.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator-interactive.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator.qml
index 6929565825..c0ae199360 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-pageindicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-pane-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-pane-custom.qml
index 96e3db1f37..4663ae9138 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-pane-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-pane-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-pane.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-pane.qml
index ed1b80d4e8..88282170c7 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-pane.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-pane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-popup-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-popup-custom.qml
index 951dfa7d52..bf054f0b3a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-popup-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-popup-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-popup-property-propagation.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-popup-property-propagation.qml
new file mode 100644
index 0000000000..7f34cd31e2
--- /dev/null
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-popup-property-propagation.qml
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [file]
+import QtQuick.Controls.Basic
+
+ApplicationWindow {
+ width: 500
+ height: 500
+ visible: true
+ font.pixelSize: 20
+ palette.windowText: "steelblue"
+
+ // This will have a pixelSize of 20 and be "steelblue" in color.
+ header: Label {
+ text: "ApplicationWindow Label"
+ leftPadding: 20
+ topPadding: 20
+ }
+
+ Pane {
+ width: 400
+ height: 400
+ anchors.centerIn: parent
+ palette.window: "#edf3f8"
+ palette.windowText: "tomato"
+
+ // This will have a pixelSize of 20 and be "tomato" in color.
+ Label {
+ text: "Pane Label"
+ }
+
+ Popup {
+ width: 300
+ height: 300
+ anchors.centerIn: parent
+ font.pixelSize: 10
+ visible: true
+
+ // This will have a pixelSize of 10 and "steelblue" in color.
+ Label {
+ text: "Popup Label"
+ }
+
+ Popup {
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+ visible: true
+
+ // This will have a pixelSize of 20 and be "steelblue" in color.
+ Label {
+ text: "Child Popup Label"
+ }
+ }
+ }
+ }
+}
+//! [file]
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-popup.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-popup.qml
index 5b9ec29fb2..5a768efeab 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-popup.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml
index 7158232081..ebfbaff62e 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
@@ -21,11 +21,40 @@ ProgressBar {
implicitWidth: 200
implicitHeight: 4
+ // Progress indicator for determinate state.
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
radius: 2
color: "#17a81a"
+ visible: !control.indeterminate
+ }
+
+ // Scrolling animation for indeterminate state.
+ Item {
+ anchors.fill: parent
+ visible: control.indeterminate
+ clip: true
+
+ Row {
+ spacing: 20
+
+ Repeater {
+ model: control.width / 40 + 1
+
+ Rectangle {
+ color: "#17a81a"
+ width: 20
+ height: control.height
+ }
+ }
+ XAnimator on x {
+ from: 0
+ to: -40
+ loops: Animation.Infinite
+ running: control.indeterminate
+ }
+ }
}
}
}
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-radiobutton-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-radiobutton-custom.qml
index 929fe7e8e2..731c67f6b2 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-radiobutton-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-radiobutton-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-radiodelegate-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-radiodelegate-custom.qml
index 5eb856b8b5..93feeeeece 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-radiodelegate-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-radiodelegate-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-rangeslider-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-rangeslider-custom.qml
index 81bcb0d668..86e71b3175 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-rangeslider-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-rangeslider-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-roundbutton.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-roundbutton.qml
index d83e4abc32..a8f0e4405d 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-roundbutton.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-roundbutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-active.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-active.qml
index 5292bc5499..d3b3044ec7 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-active.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-active.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-custom.qml
index 2a4faede61..5ad266dc93 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-non-attached.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-non-attached.qml
index 6ba505b199..26f416f580 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-non-attached.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-non-attached.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy-alwayson-when-needed.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy-alwayson-when-needed.qml
new file mode 100644
index 0000000000..a960d7ccea
--- /dev/null
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy-alwayson-when-needed.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+ListView {
+ id: listView
+ anchors.fill: parent
+ model: 20
+ delegate: ItemDelegate {
+ width: listView.width
+ text: modelData
+ }
+
+ ScrollBar.vertical: ScrollBar {
+ //! [1]
+ policy: listView.contentHeight > listView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
+ //! [1]
+ }
+}
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy-alwayson.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy-alwayson.qml
new file mode 100644
index 0000000000..64286eb980
--- /dev/null
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy-alwayson.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+//! [1]
+Flickable {
+ contentHeight: 2000
+ ScrollBar.vertical: ScrollBar {
+ policy: ScrollBar.AlwaysOn
+ }
+}
+//! [1]
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy.qml
deleted file mode 100644
index 7f08912e7e..0000000000
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollbar-policy.qml
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
-
-import QtQuick
-import QtQuick.Controls
-
-//! [1]
-Flickable {
- contentHeight: 2000
- ScrollBar.vertical: ScrollBar {
- policy: ScrollBar.AlwaysOn
- }
-}
-//! [1]
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-active.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-active.qml
index 0f6afdfc4d..0522be2ca2 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-active.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-active.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-custom.qml
index f447fd4c94..8c517b8221 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-non-attached.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-non-attached.qml
index 063c771f1b..66d09e2698 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-non-attached.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollindicator-non-attached.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-custom.qml
index 2f23b9fd58..4c1ec3d29a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-interactive.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-interactive.qml
index 3f7bc8eb6d..728b9d874f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-interactive.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-interactive.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-listview.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-listview.qml
index 053e8e395a..4d1a0c7192 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-listview.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-listview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-policy.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-policy.qml
index fa75cd14ed..445a14b4ee 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-policy.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview-policy.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview.qml
index e7f1adf758..18b6bc0de7 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-scrollview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml
index a53941e824..472b1b79d8 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
-
-import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQml.Models
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-slider-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-slider-custom.qml
index 66e0bd7990..dad6beaa86 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-slider-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-slider-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-custom.qml
index b32106a5bd..c2e6452da6 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-double.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-double.qml
index fe2dfbd722..ec1273d1af 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-double.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-double.qml
@@ -1,32 +1,40 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
//! [1]
SpinBox {
- id: spinbox
+ id: spinBox
from: 0
- value: 110
- to: 100 * 100
- stepSize: 100
+ value: decimalToInt(1.1)
+ to: decimalToInt(100)
+ stepSize: decimalFactor
+ editable: true
anchors.centerIn: parent
property int decimals: 2
- property real realValue: value / 100
+ property real realValue: value / decimalFactor
+ readonly property int decimalFactor: Math.pow(10, decimals)
+
+ function decimalToInt(decimal) {
+ return decimal * decimalFactor
+ }
validator: DoubleValidator {
- bottom: Math.min(spinbox.from, spinbox.to)
- top: Math.max(spinbox.from, spinbox.to)
+ bottom: Math.min(spinBox.from, spinBox.to)
+ top: Math.max(spinBox.from, spinBox.to)
+ decimals: spinBox.decimals
+ notation: DoubleValidator.StandardNotation
}
textFromValue: function(value, locale) {
- return Number(value / 100).toLocaleString(locale, 'f', spinbox.decimals)
+ return Number(value / decimalFactor).toLocaleString(locale, 'f', spinBox.decimals)
}
valueFromText: function(text, locale) {
- return Number.fromLocaleString(locale, text) * 100
+ return Math.round(Number.fromLocaleString(locale, text) * decimalFactor)
}
}
//! [1]
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-prefix.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-prefix.qml
new file mode 100644
index 0000000000..4db5081210
--- /dev/null
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-prefix.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+//! [1]
+SpinBox {
+ id: spinBox
+ from: 0
+ value: 11
+ to: 100
+ editable: true
+ anchors.centerIn: parent
+
+ property string prefix: "L="
+ property string suffix: "m"
+
+ validator: RegularExpressionValidator { regularExpression: /\D*(-?\d*\.?\d*)\D*/ }
+
+ textFromValue: function(value, locale) {
+ return prefix + Number(value).toLocaleString(locale, 'f', 0) + suffix
+ }
+
+ valueFromText: function(text, locale) {
+ let re = /\D*(-?\d*\.?\d*)\D*/
+ return Number.fromLocaleString(locale, re.exec(text)[1])
+ }
+}
+//! [1]
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-textual.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-textual.qml
index 271cad0f07..fcc932ee4d 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-textual.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox-textual.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox.qml
index 24fac81ca6..1813429dd5 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-spinbox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-splitview-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-splitview-custom.qml
index 0d5452c25d..c713c9623d 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-splitview-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-splitview-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-splitview-handle-containmentmask.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-splitview-handle-containmentmask.qml
index f12bfe1da8..08546f2f03 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-splitview-handle-containmentmask.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-splitview-handle-containmentmask.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-stackview-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-stackview-custom.qml
index 381fbe6b95..ec4db1c2de 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-stackview-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-stackview-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-stackview-visible.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-stackview-visible.qml
index d711a02e1a..e22d4904b0 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-stackview-visible.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-stackview-visible.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-custom.qml
index 973511c418..4382e5f9ec 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-transition.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-transition.qml
index 4215ea9202..3c461cfe4f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-transition.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate-transition.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate.qml
index fb2df4f6d3..9470de2d31 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-swipedelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -21,6 +21,7 @@ ListView {
required property string sender
required property string title
+ required property int index
ListView.onRemove: SequentialAnimation {
PropertyAction {
@@ -52,8 +53,6 @@ ListView {
SwipeDelegate.onClicked: listView.model.remove(index)
- required property int index
-
background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
}
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-custom.qml
index e77bc9a3e8..3eae7c28d5 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-indicator.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-indicator.qml
index 1831d92b9f..62cd48ff14 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-indicator.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-swipeview-indicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-switch-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-switch-custom.qml
index 5ca4b96140..6f0cac5b67 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-switch-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-switch-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-switchdelegate-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-switchdelegate-custom.qml
index 13cc7b4402..2df83b936f 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-switchdelegate-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-switchdelegate-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-custom.qml
index cdce972150..0310900c42 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-explicit.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-explicit.qml
index 2293984e30..1c3e574d61 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-explicit.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-explicit.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-flickable.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-flickable.qml
index 3851b226bd..d686d452e9 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-flickable.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar-flickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar.qml
index 3ce32c1c50..cc3fdfaced 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbutton.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbutton.qml
index c397092a8a..66994f8956 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tabbutton.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tabbutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-textarea-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-textarea-custom.qml
index 39a94e13cc..e264a921bb 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-textarea-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-textarea-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-textarea-scrollable.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-textarea-scrollable.qml
index 7f915815e3..fe884140a1 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-textarea-scrollable.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-textarea-scrollable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-custom.qml
index c4d0c88c2a..bed0e25173 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-disabled.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-disabled.qml
index aa8639190c..8b432ff827 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-disabled.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-disabled.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-focused.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-focused.qml
index 62299eeabc..e6892ec3f9 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-focused.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-focused.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-normal.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-normal.qml
index 72a5626142..272c3f3d6a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-normal.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-textfield-normal.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-toolbar-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-toolbar-custom.qml
index 2aabcbfbe3..e4ea2c8b04 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-toolbar-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-toolbar-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-toolbar.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-toolbar.qml
index cb07df80f6..64ec4d5431 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-toolbar.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-toolbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-toolbutton-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-toolbutton-custom.qml
index 6ec46b0adf..ed2c7aaf64 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-toolbutton-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-toolbutton-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator-custom.qml
index 201e04b0e8..120428801a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator.qml
index eb3543402d..f1fc11e7d9 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-toolseparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-custom.qml
index 089e6d02b9..04e390a611 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-hover.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-hover.qml
index 289334d20d..b84f8c30da 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-hover.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-hover.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-pressandhold.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-pressandhold.qml
index e209ce9ce9..a2fd762d75 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-pressandhold.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-pressandhold.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-slider.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-slider.qml
index 823e7f1842..99b2d62047 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-slider.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip-slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip.qml
index d0cf142376..1abaab2d62 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tooltip.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-custom.qml
index 3487e7316d..d3dbcf7acc 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-custom.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-listView.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-listView.qml
index 1b39c2aa16..1d531f0514 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-listView.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-listView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-pathView.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-pathView.qml
index 30e90b2ba5..b3ef0bea68 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-pathView.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-pathView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-timePicker.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-timePicker.qml
index a5d4b3585a..f174373c30 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-timePicker.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-tumbler-timePicker.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [tumbler]
import QtQuick
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-universal-accent.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-universal-accent.qml
index 52aca6eea9..6a0913dfe5 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-universal-accent.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-universal-accent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-universal-background.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-universal-background.qml
index 106549f789..a48dce116a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-universal-background.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-universal-background.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-universal-foreground.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-universal-foreground.qml
index 491a4f94d4..a09011579a 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-universal-foreground.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-universal-foreground.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-universal-theme.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-universal-theme.qml
index 5ade2c7c55..55b8244561 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-universal-theme.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-universal-theme.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn-layout.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn-layout.qml
index 6d1dd1ba79..1c0cb34ce2 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn-layout.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn-layout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn.qml
index e3c757a0b8..3cff1a329e 100644
--- a/src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn.qml
+++ b/src/quickcontrols/doc/snippets/qtquickcontrols-weeknumbercolumn.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/src/quickcontrols/doc/src/includes/container-currentindex.qdocinc b/src/quickcontrols/doc/src/includes/container-currentindex.qdocinc
index 9fcf43e168..776e39dcfc 100644
--- a/src/quickcontrols/doc/src/includes/container-currentindex.qdocinc
+++ b/src/quickcontrols/doc/src/includes/container-currentindex.qdocinc
@@ -6,4 +6,10 @@ of each control. To do this without breaking bindings, avoid setting
\c currentIndex directly, and instead use
\l {Container::}{setCurrentIndex()}, for example.
See \l {Managing the Current Index} for more information.
+
+To perform an action when \c currentIndex changes, use the
+\c onCurrentIndexChanged
+\l {Property Change Signal Handlers}{property change signal handler}:
+
+\snippet qtquickcontrols-container-oncurrentindexchanged.qml 1
//! [file]
diff --git a/src/quickcontrols/doc/src/includes/qquickcontrol-focusreason.qdocinc b/src/quickcontrols/doc/src/includes/qquickcontrol-focusreason.qdocinc
deleted file mode 100644
index b69e9e60ec..0000000000
--- a/src/quickcontrols/doc/src/includes/qquickcontrol-focusreason.qdocinc
+++ /dev/null
@@ -1,15 +0,0 @@
-This property holds the reason of the last focus change.
-
-\note This property does not indicate whether the control has \l {Item::activeFocus}
- {active focus}, but the reason why the control either gained or lost focus.
-
-\value Qt.MouseFocusReason A mouse action occurred.
-\value Qt.TabFocusReason The Tab key was pressed.
-\value Qt.BacktabFocusReason A Backtab occurred. The input for this may include the Shift or Control keys; e.g. Shift+Tab.
-\value Qt.ActiveWindowFocusReason The window system made this window either active or inactive.
-\value Qt.PopupFocusReason The application opened/closed a pop-up that grabbed/released the keyboard focus.
-\value Qt.ShortcutFocusReason The user typed a label's buddy shortcut
-\value Qt.MenuBarFocusReason The menu bar took focus.
-\value Qt.OtherFocusReason Another reason, usually application-specific.
-
-\sa Item::activeFocus
diff --git a/src/quickcontrols/doc/src/includes/qquickheaderview.qdocinc b/src/quickcontrols/doc/src/includes/qquickheaderview.qdocinc
new file mode 100644
index 0000000000..5115149762
--- /dev/null
+++ b/src/quickcontrols/doc/src/includes/qquickheaderview.qdocinc
@@ -0,0 +1,86 @@
+//! [detailed-description]
+A \1 provides a styled table header.
+It can either be used as an independent view or header for a \l TableView.
+
+You can add a header for a TableView by assigning a \1
+to the \l {TableView::syncView} property. The header and the table will
+then be kept in sync while flicking.
+
+By default, \1 displays
+\l {QAbstractItemModel::headerData()}{header data}
+from the \l {TableView::syncView}{sync view's} \l {TableView::model}{model}.
+If you don't wish to use this model, you can assign a different model to the
+\l {TableView::model}{model} property. If you assign a model that is a
+QAbstractItemModel, its header data will be used. Otherwise the data in
+the model will be used directly (for example, if you assign a model that
+is simply an array of strings).
+
+\note In order to show the header data of a QAbstractItemModel, \1
+will internally wrap the model's header data in an independent proxy
+model. This model shares no model items with the \l {TableView::model}{application model}.
+This means that if you call functions such as \l {TableView::index()}{index()}, the model
+index you get back will belong to the proxy model and not the application model.
+
+By default, \l textRole is set to \c "display", meaning that data from the
+model's \l {Qt::ItemDataRole}{Qt::DisplayRole} will be used. You can set
+this to another role name in order to have that data displayed instead.
+
+The application is responsible for placing the header at the
+correct location in the scene. You can add as many headers as you
+want to a single TableView, which can be useful if you for example want
+to place headers on all four sides of the table.
+
+The following snippet shows how you can add a horizontal and vertical header
+view to a table view:
+
+\snippet qtquickcontrols-headerview.qml 0
+
+A \1 will have
+\l {TableView::resizableColumns}{resizableColumns} set to \c true by default.
+//! [detailed-description]
+
+//! [syncView]
+This property holds the TableView to synchronize with.
+
+Once this property is bound to another TableView, both header and table
+will synchronize with regard to column widths, column spacing, and flicking
+\1.
+
+If the \l model is not explicitly set, then the header will use the syncView's
+model to label the columns.
+
+\sa model TableView
+//! [syncView]
+
+//! [model]
+This property holds the model providing data for the \1 header view.
+
+When model is not explicitly set, the header will use the syncView's
+model once syncView is set.
+
+If model is a QAbstractTableModel, its \1 headerData() will
+be accessed.
+
+If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
+will be accessed.
+
+Otherwise, the behavior is same as setting TableView::model.
+
+\sa TableView {TableView::model} {model} QAbstractTableModel
+//! [model]
+
+//! [textRole]
+This property holds the model role used to display text in each header cell.
+
+When the model has multiple roles, textRole can be set to determine which
+role should be displayed.
+
+If model is a QAbstractItemModel then it will default to "display"; otherwise
+it is empty.
+
+A warning is given if the model's \l {QAbstractItemModel::}{roleNames()}
+doesn't provide the role specified in \l textRole.
+The warning can be silenced by setting the \l textRole.
+
+\sa QAbstractItemModel::roleNames()
+//! [textRole]
diff --git a/src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc b/src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc
index 803425a88b..dc02dffe39 100644
--- a/src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc
+++ b/src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc
@@ -77,3 +77,8 @@
for example \c "Grey".
\endtable
//! [env]
+
+//! [placeholder-text-multiple-lines]
+As per the Material guidelines, placeholder text should be kept short and not
+take up multiple lines.
+//! [placeholder-text-multiple-lines]
diff --git a/src/quickcontrols/doc/src/includes/qquickstackview.qdocinc b/src/quickcontrols/doc/src/includes/qquickstackview.qdocinc
index 20c9bdc369..16d4e7374c 100644
--- a/src/quickcontrols/doc/src/includes/qquickstackview.qdocinc
+++ b/src/quickcontrols/doc/src/includes/qquickstackview.qdocinc
@@ -3,3 +3,29 @@ Only items that StackView created itself (from a \l Component or \l [QML]
url) will be destroyed when popped. See \l {Item Ownership} for more
information.
//! [pop-ownership]
+
+//! [operation-values]
+An \a operation can be optionally specified as the last argument. Supported
+operations:
+
+\value StackView.Immediate An immediate operation without transitions.
+\value StackView.PushTransition An operation with push transitions.
+\value StackView.ReplaceTransition An operation with replace transitions.
+\value StackView.PopTransition An operation with pop transitions.
+//! [operation-values]
+
+//! [optional-properties-after-each-item]
+The optional properties arguments come after each item, and specify a
+map of initial property values. For dynamically created items, these values
+are applied before the creation is finalized. This is more efficient than
+setting property values after creation, particularly where large sets of
+property values are defined, and also allows property bindings to be set
+up (using \l{Qt::binding}{Qt.binding()}) before the item is created.
+//! [optional-properties-after-each-item]
+
+//! [replaceCurrentItem]
+Pops \l currentItem from the stack and pushes \a \1. If the optional
+\a operation is specified, the relevant transition will be used. If the
+optional \a properties are specified, they will be applied to the item.
+Returns the item that became current.
+//! [replaceCurrentItem]
diff --git a/src/quickcontrols/doc/src/includes/style-screenshots.qdocinc b/src/quickcontrols/doc/src/includes/style-screenshots.qdocinc
new file mode 100644
index 0000000000..b7cd902937
--- /dev/null
+++ b/src/quickcontrols/doc/src/includes/style-screenshots.qdocinc
@@ -0,0 +1,9 @@
+//! [file]
+\table
+ \row
+ \li \image qtquickcontrols-\2-light.png
+ \caption The light theme of the \1 style.
+ \li \image qtquickcontrols-\2-dark.png
+ \caption The dark theme of the \1 style.
+\endtable
+//! [file]
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc
index 26fab0a2ce..7cc61b5701 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc
@@ -80,17 +80,12 @@
\section1 Font Configuration
The default \l {Control::font}{font} can be specified in a \c Font sub-group
- in each style's section in the configuration file. The \c Font sub-group can
- be defined in two alternative ways:
+ in each style's section in the configuration file:
\code
[Basic]
Font\Family=Open Sans
Font\PixelSize=20
-
- [Material\Font]
- Family=Open Sans
- PixelSize=20
\endcode
Supported font attributes:
@@ -137,13 +132,6 @@
Palette\WindowText=#212121
\endcode
- or:
- \code
- [Fusion\Palette]
- Window=#dedede
- WindowText=#212121
- \endcode
-
See \l [QtQuick]{Palette} QML type for more information.
\section1 Using the Configuration File in a Project
@@ -164,12 +152,39 @@
is possible to provide a different configuration file for different platforms and
locales. See \l QFileSelector documentation for more details.
- Finally, the \c .qrc file must be listed in the application's \c .pro file so that
- the build system knows about it. For example:
-
- \code
- RESOURCES = application.qrc
+ Finally, the \c .qrc file must be listed in the application's build file.
+ For example:
+
+ \if defined(onlinedocs)
+ \tab {build-qt-app}{tab-cmake}{CMakeLists.txt (CMake)}{checked}
+ \tab {build-qt-app}{tab-qmake}{.pro (qmake)}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section2 Using CMake
+ \endif
+ \badcode
+ set(CMAKE_AUTORCC ON)
+ qt_add_executable(my_app
+ application.qrc
+ main.cpp
+ ...
+ )
\endcode
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+ \else
+ \section2 Using qmake
+ \endif
+ \badcode
+ RESOURCES = application.qrc
+ ...
+ \endcode
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
+
+ See also: \l {Build System Integration}
\section1 Related Information
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc
index 19a2619164..5c93e43441 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc
@@ -183,27 +183,58 @@
\section2 Style-specific C++ Extensions
- Sometimes you may need to use C++ to extend your custom style. There are two
- ways to expose such types to QML:
+ Sometimes you may need to use C++ to extend your custom style.
\list
- \li If the style that uses the type is the only style used by an application,
- it's enough to register it with the QML engine via qmlRegisterType():
+ \li If the style that uses the type is the only style used by an
+ application, register the type with the QML engine by adding the QML_ELEMENT
+ macro and making the file part of your QML module:
+ \br
+ \br
+
+ \if defined(onlinedocs)
+ \tab {expose-cpp-to-qml}{tab-cmake}{CMake}{checked}
+ \tab {expose-cpp-to-qml}{tab-qmake}{qmake}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section3 Using CMake
+ \endif
+ \badcode
+ qt_add_qml_module(ACoolItem
+ URI MyItems
+ VERSION 1.0
+ SOURCES
+ acoolcppitem.cpp acoolcppitem.h
+ )
+ \endcode
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+ \else
+ \section3 Using QMake
+ \endif
+ \code
+ CONFIG += qmltypes
+ QML_IMPORT_NAME = MyItems
+ QML_IMPORT_MAJOR_VERSION = 1
+ \endcode
+
+ If the header the class is declared in is not accessible from your
+ project's include path, you may have to amend the include path so
+ that the generated registration code can be compiled.
\code
- qmlRegisterType<ACoolCppItem>("MyApp", 1, 0, "ACoolItem");
+ INCLUDEPATH += MyItems
\endcode
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
- See \l {Using C++ Data From QML} for more information about this.
+ See \l {Defining QML Types from C++} and \l {Building a QML application}
+ for more information.
\li If the style that uses the type is one of many styles used by an
- application, it may be better to only register it when necessary. This
- is the point at which it would make sense to implement your own
- \l {Creating C++ Plugins for QML}{QML plugin}.
-
- Using a plugin as part of your style is not that much different from
- using a set of QML files. The only difference is that the plugin and
- its \c qmldir file must be present in the same directory as the QML
- files.
+ application, consider putting each style into a separate module. The
+ modules will then be loaded on demand.
\endlist
\section3 Considerations for custom styles
@@ -433,9 +464,9 @@
Next, we add a drop shadow to the \l {Control::}{background} delegate of
the Button:
- \code
+ \qml
// ...
- import QtGraphicalEffects
+ import QtQuick.Effects
import MyStyle
// ...
@@ -443,14 +474,15 @@
// ...
layer.enabled: control.enabled && control.MyStyle.elevation > 0
- layer.effect: DropShadow {
- verticalOffset: 1
- color: control.visualFocus ? "#330066ff" : "#aaaaaa"
- samples: control.MyStyle.elevation
- spread: 0.5
+ layer.effect: MultiEffect {
+ shadowEnabled: true
+ shadowHorizontalOffset: 3
+ shadowVerticalOffset: 3
+ shadowColor: control.visualFocus ? "#330066ff" : "#aaaaaa"
+ shadowBlur: control.pressed ? 0.8 : 0.4
}
}
- \endcode
+ \endqml
Note that we:
@@ -590,46 +622,21 @@
\snippet qtquickcontrols-combobox-custom.qml file
As explained in \l {ComboBox Model Roles}, ComboBox supports multiple
- types of models. If you know that your ComboBox instances will only
- ever use one particular type of model, then it is possible to simplify
- the code above.
+ types of models.
- For example, if your models will only ever be derived from
- \l QAbstractItemModel and have more than one role, the following
- binding is sufficient:
+ Since \l {qml-data-models}{all the models provide an anonymous property}
+ with \c modelData, the following expression retrieves the right text in
+ all cases:
\code
text: model[control.textRole]
\endcode
- However, if you want to support all standard models, the more complex
- binding is necessary, and is explained in detail below.
-
- \quotefromfile qtquickcontrols-combobox-custom.qml
- \skipto control.textRole
- \printto Array.isArray
-
- The first line checks if the \l {ComboBox::}{textRole} property has
- been set. It is only necessary to set this property if the model
- has more than one role.
-
- \printline Array.isArray
-
- If \c textRole has been set, the next step is to check if the model
- is an array.
-
- If it is an array, the \c modelData context property will be available,
- and so it must be used. Square bracket notation is used to access
- the property of modelData since the property name is not known ahead
- of time.
-
- If it is not an array, the \c model context property must be used
- instead.
-
- \printline modelData
-
- Finally, if \c textRole has not been set, then the model only has one
- role, and so it is sufficient to use the value of \c modelData.
+ When you provide a specific \c textRole and a model with structured
+ data that provides the selected role, this is expression is a regular
+ property lookup. When you provide a model with singular data, such as
+ a list of strings, and an empty \c textRole, this expression retrieves
+ the \c modelData.
\section2 Customizing DelayButton
@@ -786,6 +793,9 @@
\snippet qtquickcontrols-progressbar-custom.qml file
+ Above, the content item is also animated to represent an
+ \l {ProgressBar::}{indeterminate} progress bar state.
+
\section2 Customizing RadioButton
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-examples.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-examples.qdoc
index 8cd6e7f914..76812bcbbe 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-examples.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-examples.qdoc
@@ -3,7 +3,6 @@
/*!
\group qtquickcontrols-examples
- \ingroup all-examples
\keyword Qt Quick Controls Examples
\title Qt Quick Controls Examples
\keyword Qt Quick Controls 2 Examples
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc
index 01c9b26bd2..a03970d1d8 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc
@@ -8,7 +8,7 @@
\l {QFileSelector}{File selectors} provide a convenient way of selecting
file variants. Qt offers the platform name and the locale as built-in
selectors. Qt Quick Controls extends the built-in selectors with the name
- (lowercase) of the style that an application is running with.
+ (Capitalized) of the style that an application is running with.
By using file selectors, style-specific tweaks can be applied without
creating a hard dependency to a style. From the available file variants,
@@ -77,7 +77,7 @@
\code
// +Material/CustomButton.qml
import QtQuick
- import QtGraphicalEffects
+ import QtQuick.Effects
import QtQuick.Controls
import QtQuick.Controls.Material
@@ -93,11 +93,12 @@
radius: width / 2
layer.enabled: control.enabled
- layer.effect: DropShadow {
- verticalOffset: 1
- color: Material.dropShadowColor
- samples: control.pressed ? 20 : 10
- spread: 0.5
+ layer.effect: MultiEffect {
+ shadowEnabled: true
+ shadowHorizontalOffset: 3
+ shadowVerticalOffset: 3
+ shadowColor: Material.dropShadowColor
+ shadowBlur: control.pressed ? 0.8 : 0.4
}
}
}
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-focus.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-focus.qdoc
index c39773020f..f3c1d208bb 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-focus.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-focus.qdoc
@@ -10,7 +10,7 @@
Qt Quick Controls follows the standard
\l {Keyboard Focus in Qt Quick}{Qt Quick focus system}, while also
providing some added convenience. For example, the
- \l {Control::}{focusPolicy} property can be used to control the ways in
+ \l [QML] {Item::}{focusPolicy} property can be used to control the ways in
which a control receives focus.
\section1 Focus Scope Controls
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-fusion.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-fusion.qdoc
index d3c4199e7d..05792fb7da 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-fusion.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-fusion.qdoc
@@ -11,7 +11,7 @@
look and feel. It implements the same design language as the
\l {Qt Widget Gallery}{Fusion style for Qt Widgets}.
- \image qtquickcontrols-fusion.png
+ \include style-screenshots.qdocinc {file} {Fusion} {fusion}
To run an application with the Fusion style, see
\l {Using Styles in Qt Quick Controls}.
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-imagine.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-imagine.qdoc
index 2e476c3b24..0f48f3f6a4 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-imagine.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-imagine.qdoc
@@ -129,7 +129,7 @@
\li .9.png (or .png)
\row
\li \l Dial
- \li background
+ \li background\sup{1}
\li disabled, pressed, focused, mirrored, hovered
\li .9.png (or .png)
\row
@@ -469,6 +469,10 @@
\li .9.png (or .png)
\endtable
+    \note \sup{1}) The Imagine style Dial does not yet support the
+    \l {Dial::}{startAngle} and \l {Dial::}{endAngle} properties that were
+    introduced in Qt 6.6, and instead uses a fixed background image.
+
\section2 Asset Examples
The following table lists examples of assets (taken from the
@@ -2489,7 +2493,7 @@
\section1 Attached Property Documentation
- \styleproperty {Imagine.path} {string} {imagine-path-attached-prop}
+ \styleproperty {Imagine.path} {string}
\target imagine-path-attached-prop
This attached property holds the path to the image assets...
@@ -2506,6 +2510,5 @@
\list
\li \l{Styling Qt Quick Controls}
\li \l{Qt Quick Controls - Imagine Style Example: Automotive}{Automotive Example}
- \li \l{Qt Quick Controls - Imagine Style Example: Music Player}{Music Player Example}
\endlist
*/
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-index.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-index.qdoc
index ae16f75078..b3e49e7309 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-index.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-index.qdoc
@@ -105,6 +105,31 @@
\li ...
\endtable
+ \section2 Revisions
+
+ Due to how Qt Quick Controls are implemented, new properties that are added
+ may clash with any user-defined properties of the same name. For example,
+ the following snippet will result in an error:
+
+ \qml
+ import QtQuick.Controls 2.13
+
+ ApplicationWindow {
+ // ...
+
+ ComboBox {
+ anchors.centerIn: parent
+
+ // As currentValue was added in 2.14, the versioned import above
+ // should cause this property to be used, but instead an error is produced:
+ // "Cannot override FINAL property"
+ property int currentValue: 0
+ }
+ }
+ \endqml
+
+ These properties should be renamed to avoid the conflict.
+
\section1 Module Evolution
Qt Quick Controls was originally written with touch interfaces as the
@@ -138,7 +163,6 @@
\li \l{Qt Quick Controls - Text Editor}{Text Editor}
\li \l{Qt Quick Controls - Wearable Demo}{Wearable Demo}
\li \l{Qt Quick Controls - Imagine Style Example: Automotive}{Automotive Example}
- \li \l{Qt Quick Controls - Imagine Style Example: Music Player}{Music Player Example}
\li \l{Qt Quick Controls Examples}{All Examples}
\endlist
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-ios.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-ios.qdoc
index 2c90552d20..9e107eecd2 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-ios.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-ios.qdoc
@@ -10,12 +10,6 @@
\styleimport {QtQuick.Controls.iOS} {Qt 6.4}
- \section1 Attached Properties
-
- \list
- \li \l {ios-theme-attached-prop}{\b theme} : enumeration
- \endlist
-
\section1 Detailed Description
\target detailed-desc-iOS
@@ -32,22 +26,15 @@
and iOS and should look the same on both platforms, besides minor differences that may occur
due to differences in available system fonts and font rendering engines.
- \table
- \row
- \li \image qtquickcontrols-ios-light.png
- \caption The iOS style in light theme
- \li \image qtquickcontrols-ios-dark.png
- \caption The iOS style in dark theme
- \endtable
+ \include style-screenshots.qdocinc {file} {iOS} {ios}
To run an application with the iOS style, see
\l {Using Styles in Qt Quick Controls}.
\section2 Current state
- The iOS style is currently in Tech Preview and is under development. Some controls are not
- yet supported. Those controls are: \l BusyIndicator, \l DelayButton, \l Dialog, \l DialogButtonBox,
- \l SelectionRectangle and \l Tumbler. Those will fall back to use the \l {Basic Style}.
+ The \l Tumbler control is not yet implemented for the iOS style and will fall back
+ to use the \l {Basic Style}.
\section2 Customization
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-macos.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-macos.qdoc
index 97d79c6278..c024b7fb82 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-macos.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-macos.qdoc
@@ -18,13 +18,7 @@
\note Be aware that the apperance of this style can change from one minor Qt version to the
next, to better blend in with native applications on the platform.
- \table
- \row
- \li \image qtquickcontrols-macos-light.png
- \caption The macOS style in light theme
- \li \image qtquickcontrols-macos-dark.png
- \caption The macOS style in dark theme
- \endtable
+ \include style-screenshots.qdocinc {file} {macOS} {macos}
To run an application with the macOS style, see
\l {Using Styles in Qt Quick Controls}.
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-material.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-material.qdoc
index 450cbb151c..4052337ef8 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-material.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-material.qdoc
@@ -32,17 +32,11 @@
\section1 Detailed Description
\target detailed-desc-material
- The Material style is based on the \l {https://www.google.com/design/spec/material-design/introduction.html}
+ The Material style is based on the \l {https://m3.material.io}
{Google Material Design Guidelines}. It allows for a unified experience
across platforms and device sizes.
- \table
- \row
- \li \image qtquickcontrols-material-light.png
- \caption The Material style in light theme
- \li \image qtquickcontrols-material-dark.png
- \caption The Material style in dark theme
- \endtable
+ \include style-screenshots.qdocinc {file} {Material} {material}
To run an application with the Material style, see
\l {Using Styles in Qt Quick Controls}.
@@ -54,26 +48,43 @@
may occur due to differences in available system fonts and font rendering
engines.
+ \note As the Material Design Guidelines change over time, this style may
+ change certain padding or font values, for example, in order to maintain
+ consistency with the guidelines.
+
\section2 Customization
- The Material style allows customizing five attributes, \l {material-theme-attached-prop}{theme},
- \l {material-primary-attached-prop}{primary}, \l {material-accent-attached-prop}{accent},
- \l {material-foreground-attached-prop}{foreground}, and \l {material-background-attached-prop}{background}.
+ The Material style supports several customizable attributes. Some of these
+ attributes \l {QQuickAttachedPropertyPropagator}{propagate} to children in
+ the same manner as \l {Control::font}{fonts}:
+
+ \list
+ \li \l {material-theme-attached-prop}{theme}
+ \li \l {material-primary-attached-prop}{primary}
+ \li \l {material-accent-attached-prop}{accent}
+ \li \l {material-foreground-attached-prop}{foreground}
+ \li \l {material-background-attached-prop}{background}
+ \endlist
\image qtquickcontrols-material-attributes.png
- All attributes can be specified for any window or item, and they automatically
- propagate to children in the same manner as \l {Control::font}{fonts}. In the
- following example, the window and all three radio buttons appear in the dark
- theme using a purple accent color:
+ The remaining attributes do not propagate to children:
+ \list
+ \li \l {material-elevation-attached-prop}{elevation}
+ \li \l {material-roundedScale-attached-prop}{roundedScale}
+ \li \l {material-containerStyle-attached-prop}{containerStyle}
+ \endlist
+
+ In the following example, the window and all three radio buttons appear in
+ the dark theme using a purple accent color:
\table
\row
\li
\qml
- import QtQuick 2.12
- import QtQuick.Controls 2.12
- import QtQuick.Controls.Material 2.12
+ import QtQuick
+ import QtQuick.Controls
+ import QtQuick.Controls.Material
ApplicationWindow {
visible: true
@@ -84,9 +95,16 @@
Column {
anchors.centerIn: parent
- RadioButton { text: qsTr("Small") }
- RadioButton { text: qsTr("Medium"); checked: true }
- RadioButton { text: qsTr("Large") }
+ RadioButton {
+ text: qsTr("Small")
+ }
+ RadioButton {
+ text: qsTr("Medium")
+ checked: true
+ }
+ RadioButton {
+ text: qsTr("Large")
+ }
}
}
\endqml
@@ -95,8 +113,8 @@
\endtable
In addition to specifying the attributes in QML, it is also possible to
- specify them via environment variables or in a configuration file. Attributes
- specified in QML take precedence over all other methods.
+ specify some of them via environment variables or in a configuration file.
+ Attributes specified in QML take precedence over all other methods.
\section3 Configuration File
@@ -227,9 +245,34 @@
Note that the heights shown above may vary based on differences in fonts
across platforms.
+ \section2 Control-Specific Notes
+
+ \target material-control-specific-notes-textarea
+ \section3 TextArea
+
+ TextArea supports two
+ \l {material-containerStyle-attached-prop}{containerStyles}: \c Filled and
+ \c Outlined. Outlined TextAreas have floating placeholder text that sits at
+ the top of the control. This requires the placeholder text to go outside
+ the bounds of the control, which can cause it to be
+ clipped when the TextArea or the Flickable it's a child of sets
+ \l {Item::}{clip} to \c true. To avoid this, \l {Control::}{topInset} is
+ set to an appropriate value in these cases.
+
+ \include qquickmaterialstyle.qdocinc placeholder-text-multiple-lines
+
+ \section3 TextField
+
+ The same \l {material-control-specific-notes-textarea}{issue with clipping}
+ explained above for TextArea can also occur with \l TextField. To avoid
+ this, \l {Control::}{topInset} is set to an appropriate value when the
+ TextField sets clip to \c true.
+
+ \include qquickmaterialstyle.qdocinc placeholder-text-multiple-lines
+
\section1 Attached Property Documentation
- \styleproperty {Material.accent} {color} {material-accent-attached-prop}
+ \styleproperty {Material.accent} {color}
\target material-accent-attached-prop
This attached property holds the accent color of the theme. The property
can be attached to any window or item. The value is propagated to children.
@@ -253,7 +296,7 @@
\endstyleproperty
- \styleproperty {Material.background} {color} {material-background-attached-prop}
+ \styleproperty {Material.background} {color}
\target material-background-attached-prop
This attached property holds the background color of the theme. The property
can be attached to any window or item. The value is propagated to children.
@@ -273,17 +316,18 @@
\endstyleproperty
- \styleproperty {Material.elevation} {int} {material-elevation-attached-prop}
+ \styleproperty {Material.elevation} {int}
\target material-elevation-attached-prop
This attached property holds the elevation of the control. The higher the
elevation, the deeper the shadow. The property can be attached to any control,
- but not all controls visualize elevation.
+ but not all controls visualize elevation. The value is not propagated to
+ children.
The default value is control-specific.
In the following example, the elevation of the pane is set to \c 6
in order to achieve the look of an
- \l {https://material.google.com/components/cards.html}{elevated card}:
+ \l {https://m3.material.io/components/cards/overview}{elevated card}:
\table
\row
@@ -295,7 +339,7 @@
\endstyleproperty
- \styleproperty {Material.foreground} {color} {material-foreground-attached-prop}
+ \styleproperty {Material.foreground} {color}
\target material-foreground-attached-prop
This attached property holds the foreground color of the theme. The property
can be attached to any window or item. The value is propagated to children.
@@ -315,7 +359,7 @@
\endstyleproperty
- \styleproperty {Material.primary} {color} {material-primary-attached-prop}
+ \styleproperty {Material.primary} {color}
\target material-primary-attached-prop
This attached property holds the primary color of the theme. The property
can be attached to any window or item. The value is propagated to children.
@@ -330,7 +374,7 @@
\endstyleproperty
- \styleproperty {Material.theme} {enumeration} {material-theme-attached-prop}
+ \styleproperty {Material.theme} {enumeration}
\target material-theme-attached-prop
This attached property holds whether the theme is light or dark. The property
can be attached to any window or item. The value is propagated to children.
@@ -357,11 +401,12 @@
\endstyleproperty
- \styleproperty {Material.roundedScale} {enumeration} {material-roundedScale-attached-prop}
+ \styleproperty {Material.roundedScale} {enumeration}
\target material-roundedScale-attached-prop
This attached property holds the radius of rounded corners used on the
target control. The property can be attached to any window or item, but
- only some controls support it.
+ only some controls support it. The value is not propagated to
+ children.
The default value is control-specific.
@@ -380,17 +425,18 @@
\endstyleproperty
- \styleproperty {Material.containerStyle} {enumeration} {material-containerStyle-attached-prop}
+ \styleproperty {Material.containerStyle} {enumeration}
\target material-containerStyle-attached-prop
This attached property holds the style of the container used by the
target control. The property can be attached to any window or item, but
- only TextField and TextArea support it by default.
+ only TextField and TextArea support it by default. The value is not
+ propagated to children.
The default value is control-specific.
- Available scales:
- \value Material.Filled Square corners
- \value Material.Outlined Extra small rounded corners
+ Available styles:
+ \value Material.Filled Use the filled container variant if available
+ \value Material.Outlined Use the outlined container variant if available
This property was added in Qt 6.5.
@@ -400,7 +446,7 @@
\section1 Attached Method Documentation
- \stylemethod2 {color} {color} {enumeration} {predefined} {enumeration} {shade} {material-color-attached-method}
+ \stylemethod2 {color} {color} {enumeration} {predefined} {enumeration} {shade}
\target material-color-attached-method
This attached method returns the effective color value of the specified
\l {pre-defined Material colors}{pre-defined Material color} combined with
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-styles.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-styles.qdoc
index 351732c9a3..037ca15081 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-styles.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-styles.qdoc
@@ -11,38 +11,44 @@
\section2 Basic Style
- \image qtquickcontrols-basic-thumbnail.png
+ \image qtquickcontrols-basic.png
+
The \l {Basic Style} is a simple and light-weight all-round style that offers
the maximum performance for Qt Quick Controls.
\section2 Fusion Style
- \image qtquickcontrols-fusion-thumbnail.png
+ \include style-screenshots.qdocinc {file} {Fusion} {fusion}
+
The \l {Fusion Style} is a platform-agnostic style that offers a desktop-oriented
look and feel for Qt Quick Controls.
\section2 Imagine Style
- \image qtquickcontrols-imagine-thumbnail.png
+ \image qtquickcontrols-imagine.png
+
The \l {Imagine Style} is based on image assets. The style comes with a default
set of images which can easily be changed by providing a directory
with images using a predefined naming convention.
\section2 macOS Style
- \image qtquickcontrols-macos-thumbnail.png
+ \include style-screenshots.qdocinc {file} {macOS} {macos}
+
The \l {macOS Style} is a native-looking style for macOS.
\note this style is only available for applications running on macOS.
-
\section2 iOS Style
+ \include style-screenshots.qdocinc {file} {iOS} {ios}
+
The \l {iOS Style} is a native-looking style for iOS based on image assets.
\note this style is only available for applications running on iOS.
\section2 Material Style
- \image qtquickcontrols-material-thumbnail.png
+ \include style-screenshots.qdocinc {file} {Material} {material}
+
The \l {Material Style} offers an appealing design based on the
\l {https://www.google.com/design/spec/material-design/introduction.html}
{Google Material Design Guidelines}, but requires more system resources than
@@ -50,14 +56,16 @@
\section2 Universal Style
- \image qtquickcontrols-universal-thumbnail.png
+ \include style-screenshots.qdocinc {file} {Universal} {universal}
+
The \l {Universal Style} offers an appealing design based on the
\l {https://dev.windows.com/design}{Microsoft Universal Design Guidelines},
but requires more system resources than the Basic style.
\section2 Windows Style
- \image qtquickcontrols-windows-thumbnail.png
+ \image qtquickcontrols-windows.png
+
The \l {Windows Style} is a native-looking style for Windows.
\note this style is only available for applications running on Windows.
@@ -101,12 +109,19 @@
import QtQuick.Controls.Basic auto
\endcode
- The benefit of compile-time style selection is that the QtQuick.Controls plugin
- is not used and therefore does not need to be deployed with the application.
+ The benefit of compile-time style selection is that the
+ \l {The QML script compiler}{QML compiler} knows which specific style
+ is in use and can generate C++ code for bindings.
+
+ Another benefit is that the QtQuick.Controls plugin is not used and
+ therefore does not need to be deployed with the application.
Explicit imports are also necessary if your application is built
\l {Static Builds}{statically}.
+ A disadvantage of compile-time style selection is that one executable
+ cannot support multiple styles, as each style requires its own.
+
\section2 Run-Time Style Selection
Run-time style selection is a way of specifying a style to use by importing
@@ -134,6 +149,12 @@
can support multiple styles, meaning that the end user can choose which
style to run the application with.
+ A disadvantage of this approach is that \l {The QML script compiler}
+ {QML compiler} can't know which specific style is in use and therefore
+ cannot generate C++ code for bindings on properties of Qt Quick Controls
+ types. This does not affect the QML compiler's abilities to generate C++
+ for bindings on types from other modules.
+
\section3 Using QQuickStyle in C++
\l[CPP]{QQuickStyle} provides C++ API for configuring a specific
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc
index 4c8db816d7..eba367061f 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-universal.qdoc
@@ -33,13 +33,7 @@
The Universal style has been designed to look good on all devices, from
phones and tablets to PCs.
- \table
- \row
- \li \image qtquickcontrols-universal-light.png
- \caption The Universal style in light theme
- \li \image qtquickcontrols-universal-dark.png
- \caption The Universal style in dark theme
- \endtable
+ \include style-screenshots.qdocinc {file} {Universal} {universal}
To run an application with the Universal style, see
\l {Using Styles in Qt Quick Controls}.
@@ -152,7 +146,7 @@
\section1 Attached Property Documentation
- \styleproperty {Universal.accent} {color} {universal-accent-attached-prop}
+ \styleproperty {Universal.accent} {color}
\target universal-accent-attached-prop
This attached property holds the accent color of the theme. The property
can be attached to any window or item. The value is propagated to children.
@@ -176,7 +170,7 @@
\endstyleproperty
- \styleproperty {Universal.background} {color} {universal-background-attached-prop}
+ \styleproperty {Universal.background} {color}
\target universal-background-attached-prop
This attached property holds the background color of the theme. The property
can be attached to any window or item. The value is propagated to children.
@@ -196,7 +190,7 @@
\endstyleproperty
- \styleproperty {Universal.foreground} {color} {universal-foreground-attached-prop}
+ \styleproperty {Universal.foreground} {color}
\target universal-foreground-attached-prop
This attached property holds the foreground color of the theme. The property
can be attached to any window or item. The value is propagated to children.
@@ -214,7 +208,7 @@
\image qtquickcontrols-universal-foreground.png
\endtable
- \styleproperty {Universal.theme} {enumeration} {universal-theme-attached-prop}
+ \styleproperty {Universal.theme} {enumeration}
\target universal-theme-attached-prop
This attached property holds whether the theme is light or dark. The property
can be attached to any window or item. The value is propagated to children.
@@ -243,7 +237,7 @@
\section1 Attached Method Documentation
- \stylemethod {color} {color} {enumeration} {predefined} {color-attached-method}
+ \stylemethod {color} {color} {enumeration} {predefined}
\target color-attached-method
This attached method returns the effective color value of the specified
\l {pre-defined Universal colors}{pre-defined Universal color}.
diff --git a/src/quickcontrols/doc/src/qtquickcontrols-windows.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-windows.qdoc
index fee2ffa98f..dd14b45f90 100644
--- a/src/quickcontrols/doc/src/qtquickcontrols-windows.qdoc
+++ b/src/quickcontrols/doc/src/qtquickcontrols-windows.qdoc
@@ -29,9 +29,9 @@
\section2 Current state
- The Windows style is under development, and some controls are not yet supported. Those
- controls are: \l BusyIndicator, \l DelayButton, \l PageIndicator, \l RangeSlider, \l Switch, \l TabBar and
- \l Tumbler. Those will fall back to use the \l {Fusion Style}.
+ The Windows style is under development, and some controls are not yet supported. Those controls
+ are: \l BusyIndicator, \l DelayButton, \l Menu, \l MenuBar, \l PageIndicator, \l RangeSlider,
+ \l Switch, \l TabBar and \l Tumbler. Those will fall back to use the \l {Fusion Style}.
\section2 Customization
diff --git a/src/quickcontrols/fusion/BusyIndicator.qml b/src/quickcontrols/fusion/BusyIndicator.qml
index dcd2f23f49..8de02b6aea 100644
--- a/src/quickcontrols/fusion/BusyIndicator.qml
+++ b/src/quickcontrols/fusion/BusyIndicator.qml
@@ -27,7 +27,7 @@ T.BusyIndicator {
Behavior on opacity { OpacityAnimator { duration: 250 } }
RotationAnimator on rotation {
- running: control.running || control.contentItem.visible
+ running: control.running && control.contentItem.visible
from: 0
to: 360
duration: 1000
diff --git a/src/quickcontrols/fusion/CMakeLists.txt b/src/quickcontrols/fusion/CMakeLists.txt
index 69f8f5f96d..572a3cf931 100644
--- a/src/quickcontrols/fusion/CMakeLists.txt
+++ b/src/quickcontrols/fusion/CMakeLists.txt
@@ -92,7 +92,9 @@ set_source_files_properties(VerticalHeaderView.qml PROPERTIES
QT_QML_SOURCE_VERSIONS "2.15;6.0"
)
-qt_internal_add_qml_module(qtquickcontrols2fusionstyleplugin
+add_subdirectory(impl)
+
+qt_internal_add_qml_module(QuickControls2Fusion
URI "QtQuick.Controls.Fusion"
VERSION "${PROJECT_VERSION}"
PAST_MAJOR_VERSIONS 2
@@ -107,7 +109,6 @@ qt_internal_add_qml_module(qtquickcontrols2fusionstyleplugin
SOURCES
qquickfusionstyle.cpp qquickfusionstyle_p.h
qquickfusiontheme.cpp qquickfusiontheme_p.h
- qtquickcontrols2fusionstyleplugin.cpp
QML_FILES
${qml_files}
DEFINES
@@ -117,12 +118,24 @@ qt_internal_add_qml_module(qtquickcontrols2fusionstyleplugin
Qt::CorePrivate
Qt::GuiPrivate
Qt::QmlPrivate
+ Qt::QuickControls2FusionStyleImpl
Qt::QuickControls2Private
Qt::QuickPrivate
Qt::QuickTemplates2Private
+ GENERATE_CPP_EXPORTS
+)
+
+target_sources(qtquickcontrols2fusionstyleplugin
+ PRIVATE
+ qtquickcontrols2fusionstyleplugin.cpp
)
-qt_internal_add_resource(qtquickcontrols2fusionstyleplugin "qtquickcontrols2fusionstyle"
+target_link_libraries(qtquickcontrols2fusionstyleplugin
+ PRIVATE
+ Qt::QuickControls2Private
+)
+
+qt_internal_add_resource(QuickControls2Fusion "qtquickcontrols2fusionstyle"
PREFIX
"/qt-project.org/imports/QtQuick/Controls/Fusion"
FILES
@@ -140,8 +153,6 @@ qt_internal_add_resource(qtquickcontrols2fusionstyleplugin "qtquickcontrols2fusi
"images/progressmask@4x.png"
)
-add_subdirectory(impl)
-
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2fusionstyleplugin quickwindow)
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2fusionstyleplugin
qtquickcontrols2fusionstyleimplplugin)
diff --git a/src/quickcontrols/fusion/ComboBox.qml b/src/quickcontrols/fusion/ComboBox.qml
index 6caa7d3f94..55d91e65ed 100644
--- a/src/quickcontrols/fusion/ComboBox.qml
+++ b/src/quickcontrols/fusion/ComboBox.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Window
import QtQuick.Templates as T
@@ -21,8 +23,11 @@ T.ComboBox {
rightPadding: padding + (control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
delegate: MenuItem {
+ required property var model
+ required property int index
+
width: ListView.view.width
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ text: model[control.textRole]
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
highlighted: control.highlightedIndex === index
hoverEnabled: control.hoverEnabled
@@ -111,6 +116,7 @@ T.ComboBox {
topMargin: 6
bottomMargin: 6
padding: 1
+ palette: control.palette
contentItem: ListView {
clip: true
@@ -124,15 +130,15 @@ T.ComboBox {
}
background: Rectangle {
- color: control.popup.palette.window
- border.color: Fusion.outline(control.palette)
+ color: palette.window
+ border.color: Fusion.outline(palette)
Rectangle {
z: -1
x: 1; y: 1
width: parent.width
height: parent.height
- color: control.palette.shadow
+ color: palette.shadow
opacity: 0.2
}
}
diff --git a/src/quickcontrols/fusion/Dial.qml b/src/quickcontrols/fusion/Dial.qml
index 7fa0af3379..6efd2581c3 100644
--- a/src/quickcontrols/fusion/Dial.qml
+++ b/src/quickcontrols/fusion/Dial.qml
@@ -28,12 +28,13 @@ T.Dial {
height: control.height / 7
transform: [
Translate {
- y: -Math.min(control.background.width, control.background.height) * 0.42 + control.handle.height
+ y: -Math.min(control.background.width, control.background.height) * 0.35
+ + (control.handle ? control.handle.height / 2 : 0)
},
Rotation {
angle: control.angle
- origin.x: control.handle.width / 2
- origin.y: control.handle.height / 2
+ origin.x: control.handle ? control.handle.width / 2 : 0
+ origin.y: control.handle ? control.handle.height / 2 : 0
}
]
}
diff --git a/src/quickcontrols/fusion/HorizontalHeaderView.qml b/src/quickcontrols/fusion/HorizontalHeaderView.qml
index 4e4a2ec7d7..dbea743aca 100644
--- a/src/quickcontrols/fusion/HorizontalHeaderView.qml
+++ b/src/quickcontrols/fusion/HorizontalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
import QtQuick.Controls.Fusion.impl
@@ -17,6 +19,10 @@ T.HorizontalHeaderView {
implicitHeight: Math.max(1, contentHeight)
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -37,11 +43,9 @@ T.HorizontalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/quickcontrols/fusion/Menu.qml b/src/quickcontrols/fusion/Menu.qml
index e0f565a69b..bfd1feddb6 100644
--- a/src/quickcontrols/fusion/Menu.qml
+++ b/src/quickcontrols/fusion/Menu.qml
@@ -26,8 +26,8 @@ T.Menu {
implicitHeight: contentHeight
model: control.contentModel
interactive: Window.window
- ? contentHeight + control.topPadding + control.bottomPadding > Window.window.height
- : false
+ ? contentHeight + control.topPadding + control.bottomPadding > control.height
+ : false
clip: true
currentIndex: control.currentIndex
diff --git a/src/quickcontrols/fusion/TabButton.qml b/src/quickcontrols/fusion/TabButton.qml
index add96a8fa3..48ee303f63 100644
--- a/src/quickcontrols/fusion/TabButton.qml
+++ b/src/quickcontrols/fusion/TabButton.qml
@@ -35,6 +35,7 @@ T.TabButton {
color: control.palette.buttonText
}
+
background: Rectangle {
y: control.checked || control.TabBar.position !== T.TabBar.Header ? 0 : 2
implicitHeight: 21
diff --git a/src/quickcontrols/fusion/TreeViewDelegate.qml b/src/quickcontrols/fusion/TreeViewDelegate.qml
index f8bbfb24b8..195693614b 100644
--- a/src/quickcontrols/fusion/TreeViewDelegate.qml
+++ b/src/quickcontrols/fusion/TreeViewDelegate.qml
@@ -64,6 +64,7 @@ T.TreeViewDelegate {
// The edit delegate is a separate component, and doesn't need
// to follow the same strict rules that are applied to a control.
// qmllint disable attached-property-reuse
+ // qmllint disable controls-attached-property-reuse
// qmllint disable controls-sanity
TableView.editDelegate: FocusScope {
width: parent.width
@@ -71,7 +72,7 @@ T.TreeViewDelegate {
readonly property int __role: {
let model = control.treeView.model
- let index = control.treeView.modelIndex(column, row)
+ let index = control.treeView.index(row, column)
let editText = model.data(index, Qt.EditRole)
return editText !== undefined ? Qt.EditRole : Qt.DisplayRole
}
@@ -81,17 +82,18 @@ T.TreeViewDelegate {
x: control.contentItem.x
y: (parent.height - height) / 2
width: control.contentItem.width
- text: control.treeView.model.data(control.treeView.modelIndex(column, row), __role)
+ text: control.treeView.model.data(control.treeView.index(row, column), __role)
focus: true
}
TableView.onCommit: {
- let index = TableView.view.modelIndex(column, row)
+ let index = TableView.view.index(row, column)
TableView.view.model.setData(index, textField.text, __role)
}
Component.onCompleted: textField.selectAll()
}
// qmllint enable attached-property-reuse
+ // qmllint enable controls-attached-property-reuse
// qmllint enable controls-sanity
}
diff --git a/src/quickcontrols/fusion/VerticalHeaderView.qml b/src/quickcontrols/fusion/VerticalHeaderView.qml
index b35c9b6d6c..c2cb281a24 100644
--- a/src/quickcontrols/fusion/VerticalHeaderView.qml
+++ b/src/quickcontrols/fusion/VerticalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
import QtQuick.Controls.Fusion.impl
@@ -17,6 +19,10 @@ T.VerticalHeaderView {
implicitHeight: syncView ? syncView.height : 0
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -36,11 +42,9 @@ T.VerticalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/quickcontrols/fusion/impl/CMakeLists.txt b/src/quickcontrols/fusion/impl/CMakeLists.txt
index e52f1ecc06..64ee4851fb 100644
--- a/src/quickcontrols/fusion/impl/CMakeLists.txt
+++ b/src/quickcontrols/fusion/impl/CMakeLists.txt
@@ -14,7 +14,7 @@ set(qml_files
"SwitchIndicator.qml"
)
-qt_internal_add_qml_module(qtquickcontrols2fusionstyleimplplugin
+qt_internal_add_qml_module(QuickControls2FusionStyleImpl
URI "QtQuick.Controls.Fusion.impl"
VERSION "${PROJECT_VERSION}"
PAST_MAJOR_VERSIONS 2
@@ -22,7 +22,6 @@ qt_internal_add_qml_module(qtquickcontrols2fusionstyleimplplugin
DEPENDENCIES
QtQuick/auto
PLUGIN_TARGET qtquickcontrols2fusionstyleimplplugin
- NO_PLUGIN_OPTIONAL
SOURCES
qquickfusionbusyindicator.cpp qquickfusionbusyindicator_p.h
qquickfusiondial.cpp qquickfusiondial_p.h
diff --git a/src/quickcontrols/fusion/impl/SwitchIndicator.qml b/src/quickcontrols/fusion/impl/SwitchIndicator.qml
index 8bc9238228..39e47c0e91 100644
--- a/src/quickcontrols/fusion/impl/SwitchIndicator.qml
+++ b/src/quickcontrols/fusion/impl/SwitchIndicator.qml
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import QtQuick
+import QtQuick.Window
import QtQuick.Templates as T
import QtQuick.Controls.impl
import QtQuick.Controls.Fusion
@@ -49,11 +50,13 @@ Rectangle {
gradient: Gradient {
GradientStop {
position: 0
- color: Fusion.highlight(indicator.control.palette)
+ color: Qt.alpha(indicator.control.palette.active.highlight,
+ indicator.Window ? indicator.Window.active ? 1 : 0.5 : 1)
}
GradientStop {
position: 1
- color: Qt.lighter(Fusion.highlight(indicator.control.palette), 1.2)
+ color: Qt.alpha(Qt.lighter(indicator.control.palette.active.highlight, 1.2),
+ indicator.Window ? indicator.Window.active ? 1 : 0.5 : 1)
}
}
}
diff --git a/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp b/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp
index cce69b0e4b..8e58b09ed3 100644
--- a/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp
+++ b/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp
@@ -71,8 +71,17 @@ void QQuickFusionBusyIndicator::itemChange(ItemChange change, const ItemChangeDa
{
QQuickPaintedItem::itemChange(change, data);
- if (change == ItemOpacityHasChanged && qFuzzyIsNull(data.realValue))
- setVisible(false);
+ switch (change) {
+ case ItemOpacityHasChanged:
+ if (qFuzzyIsNull(data.realValue))
+ setVisible(false);
+ break;
+ case ItemVisibleHasChanged:
+ update();
+ break;
+ default:
+ break;
+ }
}
QT_END_NAMESPACE
diff --git a/src/quickcontrols/fusion/qquickfusionstyle_p.h b/src/quickcontrols/fusion/qquickfusionstyle_p.h
index a264201211..47fdd77fd7 100644
--- a/src/quickcontrols/fusion/qquickfusionstyle_p.h
+++ b/src/quickcontrols/fusion/qquickfusionstyle_p.h
@@ -18,17 +18,19 @@
#include <QtCore/qobject.h>
#include <QtGui/qcolor.h>
#include <QtQml/qqml.h>
-#include <QtQuick/private/qquickpalette_p.h>
+#include <QtQuickControls2Fusion/qtquickcontrols2fusionexports.h>
QT_BEGIN_NAMESPACE
-class QQuickFusionStyle : public QObject
+class QQuickPalette;
+
+class Q_QUICKCONTROLS2FUSION_EXPORT QQuickFusionStyle : public QObject
{
Q_OBJECT
- Q_PROPERTY(QColor lightShade READ lightShade CONSTANT)
- Q_PROPERTY(QColor darkShade READ darkShade CONSTANT)
- Q_PROPERTY(QColor topShadow READ topShadow CONSTANT)
- Q_PROPERTY(QColor innerContrastLine READ innerContrastLine CONSTANT)
+ Q_PROPERTY(QColor lightShade READ lightShade CONSTANT FINAL)
+ Q_PROPERTY(QColor darkShade READ darkShade CONSTANT FINAL)
+ Q_PROPERTY(QColor topShadow READ topShadow CONSTANT FINAL)
+ Q_PROPERTY(QColor innerContrastLine READ innerContrastLine CONSTANT FINAL)
QML_NAMED_ELEMENT(Fusion)
QML_SINGLETON
QML_ADDED_IN_VERSION(2, 3)
diff --git a/src/quickcontrols/fusion/qquickfusiontheme_p.h b/src/quickcontrols/fusion/qquickfusiontheme_p.h
index a41c9d7315..b60521394c 100644
--- a/src/quickcontrols/fusion/qquickfusiontheme_p.h
+++ b/src/quickcontrols/fusion/qquickfusiontheme_p.h
@@ -15,13 +15,13 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
+#include <QtQuickControls2Fusion/qtquickcontrols2fusionexports.h>
QT_BEGIN_NAMESPACE
class QQuickTheme;
-class QQuickFusionTheme
+class Q_QUICKCONTROLS2FUSION_EXPORT QQuickFusionTheme
{
public:
static void initialize(QQuickTheme *theme);
diff --git a/src/quickcontrols/fusion/qtquickcontrols2fusionstyleplugin.cpp b/src/quickcontrols/fusion/qtquickcontrols2fusionstyleplugin.cpp
index 532d7597ce..f61dafa478 100644
--- a/src/quickcontrols/fusion/qtquickcontrols2fusionstyleplugin.cpp
+++ b/src/quickcontrols/fusion/qtquickcontrols2fusionstyleplugin.cpp
@@ -6,7 +6,6 @@
#include <QtQml/qqml.h>
#include <QtQuickControls2/private/qquickstyleplugin_p.h>
-#include <QtQuickTemplates2/private/qquicktheme_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quickcontrols/imagine/CMakeLists.txt b/src/quickcontrols/imagine/CMakeLists.txt
index e4a164add2..a029e09e82 100644
--- a/src/quickcontrols/imagine/CMakeLists.txt
+++ b/src/quickcontrols/imagine/CMakeLists.txt
@@ -85,7 +85,9 @@ set_source_files_properties(VerticalHeaderView.qml PROPERTIES
QT_QML_SOURCE_VERSIONS "2.15;6.0"
)
-qt_internal_add_qml_module(qtquickcontrols2imaginestyleplugin
+add_subdirectory(impl)
+
+qt_internal_add_qml_module(QuickControls2Imagine
URI "QtQuick.Controls.Imagine"
VERSION "${PROJECT_VERSION}"
PAST_MAJOR_VERSIONS 2
@@ -98,7 +100,6 @@ qt_internal_add_qml_module(qtquickcontrols2imaginestyleplugin
SOURCES
qquickimaginestyle.cpp qquickimaginestyle_p.h
qquickimaginetheme.cpp qquickimaginetheme_p.h
- qtquickcontrols2imaginestyleplugin.cpp
QML_FILES
${qml_files}
DEFINES
@@ -108,10 +109,22 @@ qt_internal_add_qml_module(qtquickcontrols2imaginestyleplugin
Qt::CorePrivate
Qt::GuiPrivate
Qt::QmlPrivate
+ Qt::QuickControls2ImagineStyleImpl
Qt::QuickControls2ImplPrivate
Qt::QuickControls2Private
Qt::QuickPrivate
Qt::QuickTemplates2Private
+ GENERATE_CPP_EXPORTS
+)
+
+target_sources(qtquickcontrols2imaginestyleplugin
+ PRIVATE
+ qtquickcontrols2imaginestyleplugin.cpp
+)
+
+target_link_libraries(qtquickcontrols2imaginestyleplugin
+ PRIVATE
+ Qt::QuickControls2Private
)
file(GLOB resource_glob_0 RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "images/*.png")
@@ -125,20 +138,18 @@ foreach(file IN LISTS resource_glob_1)
endforeach()
# Resources:
-set(qmake_qtquickcontrols2imaginestyleplugin_resource_files
+set(qmake_qtquickcontrols2imaginestyle_resource_files
${resource_glob_0}
${resource_glob_1}
)
-qt_internal_add_resource(qtquickcontrols2imaginestyleplugin "qmake_qtquickcontrols2imaginestyleplugin"
+qt_internal_add_resource(QuickControls2Imagine "qmake_qtquickcontrols2imaginestyle"
PREFIX
"/qt-project.org/imports/QtQuick/Controls/Imagine"
FILES
- ${qmake_qtquickcontrols2imaginestyleplugin_resource_files}
+ ${qmake_qtquickcontrols2imaginestyle_resource_files}
)
-add_subdirectory(impl)
-
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2imaginestyleplugin quickwindow)
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2imaginestyleplugin
qtquickcontrols2imaginestyleimplplugin)
diff --git a/src/quickcontrols/imagine/ComboBox.qml b/src/quickcontrols/imagine/ComboBox.qml
index 254313454c..582b820b82 100644
--- a/src/quickcontrols/imagine/ComboBox.qml
+++ b/src/quickcontrols/imagine/ComboBox.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Window
import QtQuick.Templates as T
@@ -25,8 +27,11 @@ T.ComboBox {
bottomInset: background ? -background.bottomInset || 0 : 0
delegate: ItemDelegate {
+ required property var model
+ required property int index
+
width: ListView.view.width
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ text: model[control.textRole]
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
highlighted: control.highlightedIndex === index
hoverEnabled: control.hoverEnabled
diff --git a/src/quickcontrols/imagine/Dial.qml b/src/quickcontrols/imagine/Dial.qml
index 85eb0a8422..d534fad44e 100644
--- a/src/quickcontrols/imagine/Dial.qml
+++ b/src/quickcontrols/imagine/Dial.qml
@@ -41,12 +41,13 @@ T.Dial {
transform: [
Translate {
- y: -Math.min(control.background.width, control.background.height) * 0.4 + control.handle.height / 2
+ y: -Math.min(control.background.width, control.background.height) * 0.4
+ + (control.handle ? control.handle.height / 2 : 0)
},
Rotation {
angle: control.angle
- origin.x: control.handle.width / 2
- origin.y: control.handle.height / 2
+ origin.x: control.handle ? control.handle.width / 2 : 0
+ origin.y: control.handle ? control.handle.height / 2 : 0
}
]
}
diff --git a/src/quickcontrols/imagine/HorizontalHeaderView.qml b/src/quickcontrols/imagine/HorizontalHeaderView.qml
index bbc8064a23..9f571a8f62 100644
--- a/src/quickcontrols/imagine/HorizontalHeaderView.qml
+++ b/src/quickcontrols/imagine/HorizontalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
@@ -16,6 +18,10 @@ T.HorizontalHeaderView {
implicitHeight: Math.max(1, contentHeight)
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -26,11 +32,9 @@ T.HorizontalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: "#ff26282a"
diff --git a/src/quickcontrols/imagine/Menu.qml b/src/quickcontrols/imagine/Menu.qml
index 6c58ea7290..3596ee9976 100644
--- a/src/quickcontrols/imagine/Menu.qml
+++ b/src/quickcontrols/imagine/Menu.qml
@@ -36,8 +36,8 @@ T.Menu {
implicitHeight: contentHeight
model: control.contentModel
interactive: Window.window
- ? contentHeight + control.topPadding + control.bottomPadding > Window.window.height
- : false
+ ? contentHeight + control.topPadding + control.bottomPadding > control.height
+ : false
clip: true
currentIndex: control.currentIndex
diff --git a/src/quickcontrols/imagine/RangeSlider.qml b/src/quickcontrols/imagine/RangeSlider.qml
index e0559e65b3..bb5c8a09c6 100644
--- a/src/quickcontrols/imagine/RangeSlider.qml
+++ b/src/quickcontrols/imagine/RangeSlider.qml
@@ -38,7 +38,7 @@ T.RangeSlider {
{"horizontal": control.horizontal},
{"disabled": !control.enabled},
{"pressed": control.first.pressed},
- {"focused": control.first.handle.activeFocus},
+ {"focused": control.first.handle?.activeFocus ?? false},
{"mirrored": control.mirrored},
{"hovered": control.first.hovered}
]
@@ -57,7 +57,7 @@ T.RangeSlider {
{"horizontal": control.horizontal},
{"disabled": !control.enabled},
{"pressed": control.second.pressed},
- {"focused": control.second.handle.activeFocus},
+ {"focused": control.second.handle?.activeFocus ?? false},
{"mirrored": control.mirrored},
{"hovered": control.second.hovered}
]
@@ -80,10 +80,13 @@ T.RangeSlider {
}
NinePatchImage {
- x: control.horizontal ? control.first.handle.width / 2 + control.first.position * (parent.width - control.first.handle.width) : (parent.width - width) / 2
- y: control.horizontal ? (parent.height - height) / 2 : control.first.handle.height / 2 + control.second.visualPosition * (parent.height - control.first.handle.height)
- width: control.horizontal ? control.second.position * (parent.width - control.first.handle.width) - control.first.position * (parent.width - control.first.handle.width) : parent.width
- height: control.vertical ? control.second.position * (parent.height - control.first.handle.height) - control.first.position * (parent.height - control.first.handle.height): parent.height
+ readonly property real handleWidth: control.first.handle ? control.first.handle.width : 0
+ readonly property real handleHeight: control.first.handle ? control.first.handle.height : 0
+
+ x: control.horizontal ? handleWidth / 2 + control.first.position * (parent.width - handleWidth) : (parent.width - width) / 2
+ y: control.horizontal ? (parent.height - height) / 2 : handleHeight / 2 + control.second.visualPosition * (parent.height - handleHeight)
+ width: control.horizontal ? control.second.position * (parent.width - handleWidth) - control.first.position * (parent.width - handleWidth) : parent.width
+ height: control.vertical ? control.second.position * (parent.height - handleHeight) - control.first.position * (parent.height - handleHeight): parent.height
source: control.Imagine.url + "rangeslider-progress"
NinePatchImageSelector on source {
diff --git a/src/quickcontrols/imagine/Slider.qml b/src/quickcontrols/imagine/Slider.qml
index 01b5899fcb..8176abcd78 100644
--- a/src/quickcontrols/imagine/Slider.qml
+++ b/src/quickcontrols/imagine/Slider.qml
@@ -59,15 +59,18 @@ T.Slider {
}
NinePatchImage {
+ readonly property real handleWidth: control.handle ? control.handle.width : 0
+ readonly property real handleHeight: control.handle ? control.handle.height : 0
+
x: control.horizontal ? 0 : (parent.width - width) / 2
y: control.horizontal
? (parent.height - height) / 2
- : control.handle.height / 2 + control.visualPosition * (parent.height - control.handle.height)
+ : handleHeight / 2 + control.visualPosition * (parent.height - handleHeight)
width: control.horizontal
- ? control.handle.width / 2 + control.position * (parent.width - control.handle.width)
+ ? handleWidth / 2 + control.position * (parent.width - handleWidth)
: parent.width
height: control.vertical
- ? control.handle.height / 2 + control.position * (parent.height - control.handle.height)
+ ? handleHeight / 2 + control.position * (parent.height - handleHeight)
: parent.height
source: control.Imagine.url + "slider-progress"
diff --git a/src/quickcontrols/imagine/VerticalHeaderView.qml b/src/quickcontrols/imagine/VerticalHeaderView.qml
index 23bb2d62e3..52a9bcb876 100644
--- a/src/quickcontrols/imagine/VerticalHeaderView.qml
+++ b/src/quickcontrols/imagine/VerticalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
@@ -16,6 +18,10 @@ T.VerticalHeaderView {
implicitHeight: syncView ? syncView.height : 0
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -26,11 +32,9 @@ T.VerticalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: "#ff26282a"
diff --git a/src/quickcontrols/imagine/impl/CMakeLists.txt b/src/quickcontrols/imagine/impl/CMakeLists.txt
index 830176f13e..5dd12b5de5 100644
--- a/src/quickcontrols/imagine/impl/CMakeLists.txt
+++ b/src/quickcontrols/imagine/impl/CMakeLists.txt
@@ -9,12 +9,11 @@ set(qml_files
"OpacityMask.qml"
)
-qt_internal_add_qml_module(qtquickcontrols2imaginestyleimplplugin
+qt_internal_add_qml_module(QuickControls2ImagineStyleImpl
URI "QtQuick.Controls.Imagine.impl"
VERSION "${PROJECT_VERSION}"
CLASS_NAME QtQuickControls2ImagineStyleImplPlugin
PLUGIN_TARGET qtquickcontrols2imaginestyleimplplugin
- NO_PLUGIN_OPTIONAL
QML_FILES
${qml_files}
IMPORTS
@@ -31,16 +30,13 @@ qt_internal_add_qml_module(qtquickcontrols2imaginestyleimplplugin
Qt::QuickTemplates2Private
)
-# Resources:
-set(qmake_qtquickcontrols2imaginestyleimplplugin_resource_files
- "shaders/+glslcore/OpacityMask.frag"
- "shaders/+qsb/OpacityMask.frag"
- "shaders/OpacityMask.frag"
-)
-
-qt_internal_add_resource(qtquickcontrols2imaginestyleimplplugin "qmake_qtquickcontrols2imaginestyleimplplugin"
+qt_internal_add_shaders(QuickControls2ImagineStyleImpl "qtquickcontrols2imaginestyleimplplugin_shaders"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
PREFIX
- "qt-project.org/imports/QtQuick/Controls/Imagine/impl"
+ "/qt-project.org/imports/QtQuick/Controls/Imagine/impl"
FILES
- ${qmake_qtquickcontrols2imaginestyleimplplugin_resource_files}
+ "shaders/OpacityMask.frag"
)
diff --git a/src/quickcontrols/imagine/impl/OpacityMask.qml b/src/quickcontrols/imagine/impl/OpacityMask.qml
index 6108901129..3e78c15865 100644
--- a/src/quickcontrols/imagine/impl/OpacityMask.qml
+++ b/src/quickcontrols/imagine/impl/OpacityMask.qml
@@ -30,6 +30,6 @@ Item {
anchors.fill: parent
- fragmentShader: "qrc:/qt-project.org/imports/QtQuick/Controls/Imagine/impl/shaders/OpacityMask.frag"
+ fragmentShader: "qrc:/qt-project.org/imports/QtQuick/Controls/Imagine/impl/shaders/OpacityMask.frag.qsb"
}
}
diff --git a/src/quickcontrols/imagine/impl/shaders/+glslcore/OpacityMask.frag b/src/quickcontrols/imagine/impl/shaders/+glslcore/OpacityMask.frag
deleted file mode 100644
index 529e269614..0000000000
--- a/src/quickcontrols/imagine/impl/shaders/+glslcore/OpacityMask.frag
+++ /dev/null
@@ -1,13 +0,0 @@
-#version 150
-
-uniform float qt_Opacity;
-uniform sampler2D source;
-uniform sampler2D maskSource;
-
-in vec2 qt_TexCoord0;
-out vec4 fragColor;
-
-void main()
-{
- fragColor = texture(source, qt_TexCoord0.st) * (texture(maskSource, qt_TexCoord0.st).a) * qt_Opacity;
-}
diff --git a/src/quickcontrols/imagine/impl/shaders/+qsb/OpacityMask.frag b/src/quickcontrols/imagine/impl/shaders/+qsb/OpacityMask.frag
deleted file mode 100644
index 331b30b807..0000000000
--- a/src/quickcontrols/imagine/impl/shaders/+qsb/OpacityMask.frag
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/imagine/impl/shaders/OpacityMask.frag b/src/quickcontrols/imagine/impl/shaders/OpacityMask.frag
index 84f9bc3ee6..5d14320e78 100644
--- a/src/quickcontrols/imagine/impl/shaders/OpacityMask.frag
+++ b/src/quickcontrols/imagine/impl/shaders/OpacityMask.frag
@@ -1,7 +1,20 @@
-varying highp vec2 qt_TexCoord0;
-uniform highp float qt_Opacity;
-uniform lowp sampler2D source;
-uniform lowp sampler2D maskSource;
-void main(void) {
- gl_FragColor = texture2D(source, qt_TexCoord0.st) * (texture2D(maskSource, qt_TexCoord0.st).a) * qt_Opacity;
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+} ubuf;
+
+layout(binding = 1) uniform sampler2D source;
+layout(binding = 2) uniform sampler2D maskSource;
+
+void main()
+{
+ fragColor = texture(source, qt_TexCoord0.st) * (texture(maskSource, qt_TexCoord0.st).a) * ubuf.qt_Opacity;
}
diff --git a/src/quickcontrols/imagine/impl/shaders/OpacityMask_rhi.frag b/src/quickcontrols/imagine/impl/shaders/OpacityMask_rhi.frag
deleted file mode 100644
index 9ae3249978..0000000000
--- a/src/quickcontrols/imagine/impl/shaders/OpacityMask_rhi.frag
+++ /dev/null
@@ -1,17 +0,0 @@
-#version 440
-
-layout(location = 0) in vec2 qt_TexCoord0;
-layout(location = 0) out vec4 fragColor;
-
-layout(std140, binding = 0) uniform buf {
- mat4 qt_Matrix;
- float qt_Opacity;
-} ubuf;
-
-layout(binding = 1) uniform sampler2D source;
-layout(binding = 2) uniform sampler2D maskSource;
-
-void main()
-{
- fragColor = texture(source, qt_TexCoord0.st) * (texture(maskSource, qt_TexCoord0.st).a) * ubuf.qt_Opacity;
-}
diff --git a/src/quickcontrols/imagine/impl/shaders/compile.bat b/src/quickcontrols/imagine/impl/shaders/compile.bat
deleted file mode 100644
index 42b8cb45c0..0000000000
--- a/src/quickcontrols/imagine/impl/shaders/compile.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-:: Copyright (C) 2020 The Qt Company Ltd.
-:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o +qsb/OpacityMask.frag OpacityMask_rhi.frag
diff --git a/src/quickcontrols/imagine/qquickimaginestyle.cpp b/src/quickcontrols/imagine/qquickimaginestyle.cpp
index 377103e2f2..bdd8b00a40 100644
--- a/src/quickcontrols/imagine/qquickimaginestyle.cpp
+++ b/src/quickcontrols/imagine/qquickimaginestyle.cpp
@@ -3,7 +3,9 @@
#include "qquickimaginestyle_p.h"
+#if QT_CONFIG(settings)
#include <QtCore/qsettings.h>
+#endif
#include <QtQuickControls2/private/qquickstyle_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quickcontrols/imagine/qquickimaginestyle_p.h b/src/quickcontrols/imagine/qquickimaginestyle_p.h
index 81f17fb58c..9d8f3d983a 100644
--- a/src/quickcontrols/imagine/qquickimaginestyle_p.h
+++ b/src/quickcontrols/imagine/qquickimaginestyle_p.h
@@ -18,10 +18,11 @@
#include <QtCore/qvariant.h>
#include <QtQml/qqml.h>
#include <QtQuickControls2/qquickattachedpropertypropagator.h>
+#include <QtQuickControls2Imagine/qtquickcontrols2imagineexports.h>
QT_BEGIN_NAMESPACE
-class QQuickImagineStyle : public QQuickAttachedPropertyPropagator
+class Q_QUICKCONTROLS2IMAGINE_EXPORT QQuickImagineStyle : public QQuickAttachedPropertyPropagator
{
Q_OBJECT
Q_PROPERTY(QString path READ path WRITE setPath RESET resetPath NOTIFY pathChanged FINAL)
diff --git a/src/quickcontrols/imagine/qquickimaginetheme.cpp b/src/quickcontrols/imagine/qquickimaginetheme.cpp
index 921034962a..7df719c84c 100644
--- a/src/quickcontrols/imagine/qquickimaginetheme.cpp
+++ b/src/quickcontrols/imagine/qquickimaginetheme.cpp
@@ -5,13 +5,17 @@
#include <QtQuickTemplates2/private/qquicktheme_p.h>
+#include <qfontdatabase.h>
+
QT_BEGIN_NAMESPACE
void QQuickImagineTheme::initialize(QQuickTheme *theme)
{
- QFont systemFont;
- systemFont.setFamilies(QStringList{QLatin1String("Open Sans")});
- theme->setFont(QQuickTheme::System, systemFont);
+ const auto defaultFontFamily = QLatin1String("Open Sans");
+ if (QFontDatabase::hasFamily(defaultFontFamily)) {
+ const QFont systemFont(QStringList{defaultFontFamily});
+ theme->setFont(QQuickTheme::System, systemFont);
+ }
const QColor accentColor = QColor::fromRgb(0x4fc1e9);
const QColor windowTextColor = QColor::fromRgb(0x434a54);
diff --git a/src/quickcontrols/imagine/qquickimaginetheme_p.h b/src/quickcontrols/imagine/qquickimaginetheme_p.h
index 93b3063b78..522c99727b 100644
--- a/src/quickcontrols/imagine/qquickimaginetheme_p.h
+++ b/src/quickcontrols/imagine/qquickimaginetheme_p.h
@@ -15,13 +15,13 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
+#include <QtQuickControls2Imagine/qtquickcontrols2imagineexports.h>
QT_BEGIN_NAMESPACE
class QQuickTheme;
-class QQuickImagineTheme
+class Q_QUICKCONTROLS2IMAGINE_EXPORT QQuickImagineTheme
{
public:
static void initialize(QQuickTheme *theme);
diff --git a/src/quickcontrols/imagine/qtquickcontrols2imaginestyleplugin.cpp b/src/quickcontrols/imagine/qtquickcontrols2imaginestyleplugin.cpp
index ddf3927562..80949ff648 100644
--- a/src/quickcontrols/imagine/qtquickcontrols2imaginestyleplugin.cpp
+++ b/src/quickcontrols/imagine/qtquickcontrols2imaginestyleplugin.cpp
@@ -7,7 +7,6 @@
#include <QtCore/qloggingcategory.h>
#include <QtQml/qqml.h>
#include <QtQuickControls2/private/qquickstyleplugin_p.h>
-#include <QtQuickTemplates2/private/qquicktheme_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quickcontrols/ios/BusyIndicator.qml b/src/quickcontrols/ios/BusyIndicator.qml
index e9c874d904..3fb08070df 100644
--- a/src/quickcontrols/ios/BusyIndicator.qml
+++ b/src/quickcontrols/ios/BusyIndicator.qml
@@ -27,10 +27,10 @@ T.BusyIndicator {
contentItem: Image {
property int currentImage: 8
source: IOS.url + "busyindicator-frame-0" + currentImage +
- (Qt.styleHints.appearance === Qt.Light ? "-light.png" : "-dark.png")
+ (Qt.styleHints.colorScheme === Qt.Light ? "-light.png" : "-dark.png")
fillMode: Image.PreserveAspectFit
NumberAnimation on currentImage {
- running: control.running
+ running: control.visible && control.running
loops: -1
from: 8
to: 1
diff --git a/src/quickcontrols/ios/Button.qml b/src/quickcontrols/ios/Button.qml
index 6d8a6f8e09..2156b3beda 100644
--- a/src/quickcontrols/ios/Button.qml
+++ b/src/quickcontrols/ios/Button.qml
@@ -20,8 +20,7 @@ T.Button {
icon.width: 17
icon.height: 17
- icon.color: control.flat ? (control.enabled ? (control.down ? control.palette.highlight : control.palette.button)
- : control.palette.mid)
+ icon.color: control.flat ? (control.down ? control.palette.highlight : control.palette.button)
: control.palette.buttonText
contentItem: IconLabel {
@@ -32,8 +31,7 @@ T.Button {
icon: control.icon
text: control.text
font: control.font
- color: control.flat ? (control.enabled ? (control.down ? control.palette.highlight : control.palette.button)
- : control.palette.mid)
+ color: control.flat ? (control.down ? control.palette.highlight : control.palette.button)
: control.palette.buttonText
}
diff --git a/src/quickcontrols/ios/CMakeLists.txt b/src/quickcontrols/ios/CMakeLists.txt
index d20090b601..c4c827677a 100644
--- a/src/quickcontrols/ios/CMakeLists.txt
+++ b/src/quickcontrols/ios/CMakeLists.txt
@@ -45,6 +45,12 @@ set(qml_files
"ComboBox.qml"
"HorizontalHeaderView.qml"
"VerticalHeaderView.qml"
+ "Dialog.qml"
+ "DialogButtonBox.qml"
+ "DelayButton.qml"
+ "SelectionRectangle.qml"
+ "ToolTip.qml"
+ "ToolSeparator.qml"
)
set_source_files_properties(Slider.qml PROPERTIES
@@ -73,10 +79,11 @@ qt_internal_add_qml_module(qtquickcontrols2iosstyleplugin
Qt::CorePrivate
Qt::GuiPrivate
Qt::QmlPrivate
+ Qt::QuickControls2IOSStyleImpl
+ Qt::QuickControls2ImplPrivate
Qt::QuickControls2Private
Qt::QuickPrivate
Qt::QuickTemplates2Private
- Qt::QuickControls2ImplPrivate
)
qt_internal_extend_target(qtquickcontrols2iosstyleplugin CONDITION APPLE AND IOS
diff --git a/src/quickcontrols/ios/CheckBox.qml b/src/quickcontrols/ios/CheckBox.qml
index 2ef8f83114..59f1c8bce4 100644
--- a/src/quickcontrols/ios/CheckBox.qml
+++ b/src/quickcontrols/ios/CheckBox.qml
@@ -27,8 +27,8 @@ T.CheckBox {
states: [
{"checked": control.checkState === Qt.Checked},
{"partially-checked": control.checkState === Qt.PartiallyChecked},
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
@@ -39,7 +39,7 @@ T.CheckBox {
text: control.text
font: control.font
- color: control.palette.windowText
+ color: control.palette.text
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/quickcontrols/ios/CheckDelegate.qml b/src/quickcontrols/ios/CheckDelegate.qml
index fbd2a4ce35..b421430311 100644
--- a/src/quickcontrols/ios/CheckDelegate.qml
+++ b/src/quickcontrols/ios/CheckDelegate.qml
@@ -20,7 +20,7 @@ T.CheckDelegate {
icon.width: 29
icon.height: 29
- icon.color: control.enabled ? control.palette.text : control.palette.mid
+ icon.color: control.palette.text
indicator: Image {
x: control.mirrored ? control.leftPadding : control.width - width - control.rightPadding
@@ -32,8 +32,8 @@ T.CheckDelegate {
states: [
{"checked": control.checkState === Qt.Checked},
{"partially-checked": control.checkState === Qt.PartiallyChecked},
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
@@ -50,12 +50,12 @@ T.CheckDelegate {
icon: control.icon
text: control.text
font: control.font
- color: control.enabled ? control.palette.text : control.palette.mid
+ color: control.palette.text
}
background: Rectangle {
implicitHeight: 44
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
NinePatchImage {
property real offset: control.icon.source.toString() !== "" ? control.icon.width + control.spacing : 0
x: control.down ? 0 : control.leftPadding + offset
@@ -65,8 +65,8 @@ T.CheckDelegate {
source: IOS.url + "itemdelegate-background"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"pressed": control.down}
]
}
diff --git a/src/quickcontrols/ios/ComboBox.qml b/src/quickcontrols/ios/ComboBox.qml
index 491325258d..7c1299db74 100644
--- a/src/quickcontrols/ios/ComboBox.qml
+++ b/src/quickcontrols/ios/ComboBox.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
import QtQuick.Controls.impl
@@ -26,12 +28,15 @@ T.ComboBox {
delegate: MenuItem {
width: ListView.view.width
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ text: model[control.textRole]
palette.text: control.palette.text
palette.highlightedText: control.palette.highlightedText
hoverEnabled: control.hoverEnabled
checked: control.currentIndex === index
+ required property var model
+ required property int index
+
readonly property bool isSingleItem: control.count === 1
readonly property bool isFirstItem: !isSingleItem && index === 0
readonly property bool isLastItem: !isSingleItem && index === control.count - 1
@@ -49,8 +54,8 @@ T.ComboBox {
states: [
{"edge": isFirstItem || isLastItem },
{"single": isSingleItem},
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"pressed": down}
]
}
@@ -65,13 +70,12 @@ T.ComboBox {
opacity: (control.enabled || control.popupButtonStyle) ? 1 : 0.5
source: IOS.url + (control.popupButtonStyle ? "arrow-updown-indicator" : "arrow-indicator")
- color: control.popupButtonStyle ? (control.enabled ? (control.down ? control.palette.highlight : control.palette.button)
- : control.palette.mid)
- : defaultColor
+ color: control.popupButtonStyle ? (control.down ? control.palette.highlight : control.palette.button)
+ : defaultColor
ImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"pressed": control.down}
]
}
@@ -91,9 +95,8 @@ T.ComboBox {
validator: control.validator
selectByMouse: control.selectTextByMouse
- color: control.popupButtonStyle ? (control.enabled ? (control.down ? control.palette.highlight : control.palette.button)
- : control.palette.mid)
- : control.palette.text
+ color: control.popupButtonStyle ? (control.down ? control.palette.highlight : control.palette.button)
+ : control.palette.text
selectionColor: control.palette.highlight
selectedTextColor: control.palette.highlightedText
verticalAlignment: Text.AlignVCenter
diff --git a/src/quickcontrols/ios/DelayButton.qml b/src/quickcontrols/ios/DelayButton.qml
new file mode 100644
index 0000000000..dddeefbedd
--- /dev/null
+++ b/src/quickcontrols/ios/DelayButton.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+
+T.DelayButton {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ bottomPadding: 4
+ topPadding: 4
+ rightPadding: 4
+ leftPadding: 4
+
+ icon.width: 17
+ icon.height: 17
+ icon.color: control.down ? control.palette.highlight : control.palette.button
+
+ transition: Transition {
+ NumberAnimation {
+ duration: control.delay * (control.pressed ? 1.0 - control.progress : 0.3 * control.progress)
+ }
+ }
+
+ contentItem: IconLabel {
+ spacing: control.spacing
+ mirrored: control.mirrored
+ display: control.display
+
+ icon: control.icon
+ text: control.text
+ font: control.font
+ color: control.palette.buttonText
+ }
+
+ background: Rectangle {
+ implicitWidth: 17
+ implicitHeight: 10
+ radius: 4
+
+ color: control.down && control.progress === 1 ? "transparent" : control.palette.disabled.button
+
+ Rectangle {
+ width: control.progress * parent.width
+ height: parent.height
+ radius: 4
+ color: control.down ? control.palette.highlight : control.palette.button
+ }
+ }
+}
diff --git a/src/quickcontrols/ios/Dial.qml b/src/quickcontrols/ios/Dial.qml
index 2a4c9666bc..3fc77035b8 100644
--- a/src/quickcontrols/ios/Dial.qml
+++ b/src/quickcontrols/ios/Dial.qml
@@ -64,8 +64,8 @@ T.Dial {
centerY: control.background.children[0].height / 2
radiusX: control.background.children[0].width / 2 - 2
radiusY: radiusX
- startAngle: -230
- sweepAngle: 140 + control.angle
+ startAngle: control.startAngle - 90
+ sweepAngle: control.angle - control.startAngle
}
}
}
@@ -92,8 +92,8 @@ T.Dial {
source: IOS.url + "slider-handle"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"disabled": !control.enabled}
]
}
diff --git a/src/quickcontrols/ios/Dialog.qml b/src/quickcontrols/ios/Dialog.qml
new file mode 100644
index 0000000000..f8f400daf1
--- /dev/null
+++ b/src/quickcontrols/ios/Dialog.qml
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.Controls.iOS.impl
+
+T.Dialog {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ implicitHeaderWidth,
+ implicitFooterWidth)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+ leftPadding: 16
+ rightPadding: 16
+ bottomPadding: 23
+
+ dim: true
+
+ enter: Transition {
+ NumberAnimation { property: "scale"; from: 1.5; to: 1.0; easing.type: Easing.OutQuint; duration: 220 }
+ NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; easing.type: Easing.OutCubic; duration: 150 }
+ }
+
+ exit: Transition {
+ NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; easing.type: Easing.OutCubic; duration: 150 }
+ }
+
+ background: Item {
+ NinePatchImage {
+ width: parent.width
+ height: parent.height
+ source: IOS.url + "popup-background"
+ NinePatchImageSelector on source {
+ states: [
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
+ ]
+ }
+ }
+ }
+
+ header: Label {
+ text: control.title
+ visible: control.title
+ horizontalAlignment: Text.AlignHCenter
+ topPadding: 23
+ bottomPadding: 7
+ verticalAlignment: Text.AlignVCenter
+ elide: Label.ElideRight
+ font.weight: Font.Medium
+ font.pointSize: 17
+ }
+
+ footer: DialogButtonBox {
+ visible: count > 0
+ }
+
+ T.Overlay.modal: Rectangle {
+ color: Color.transparent("black", 0.5)
+ Behavior on opacity { NumberAnimation { duration: 150 } }
+ }
+
+ T.Overlay.modeless: Rectangle {
+ color: Color.transparent("black", 0.5)
+ Behavior on opacity { NumberAnimation { duration: 150 } }
+ }
+}
diff --git a/src/quickcontrols/ios/DialogButtonBox.qml b/src/quickcontrols/ios/DialogButtonBox.qml
new file mode 100644
index 0000000000..f04088a9d1
--- /dev/null
+++ b/src/quickcontrols/ios/DialogButtonBox.qml
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.Controls.iOS.impl
+
+T.DialogButtonBox {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding)
+
+ readonly property var orientation: (count === 2 || alignment !== 0)
+ ? ListView.Horizontal : ListView.Vertical
+ readonly property bool delegatePressed: count === 2 && (itemAt(0).down || itemAt(1).down)
+
+ spacing: orientation === ListView.Horizontal
+ ? (background && background.separatorLine ? background.separatorLine.width : 0)
+ : 0
+
+ delegate: DialogButtonBoxDelegate {
+ width: control.orientation === ListView.Vertical
+ ? control.width : undefined
+ }
+
+ contentItem: ListView {
+ clip: true
+ implicitWidth: contentWidth
+ implicitHeight: contentHeight
+ model: control.contentModel
+ spacing: control.spacing
+ orientation: control.orientation
+ boundsBehavior: Flickable.StopAtBounds
+ snapMode: ListView.SnapToItem
+ }
+
+ background: Item {
+ implicitHeight: control.orientation === ListView.Horizontal ? 44 : Math.max(contentItem.implicitHeight, 44)
+ implicitWidth: 270
+
+ readonly property NinePatchImage backgroundImage : NinePatchImage {
+ parent: control.background
+ width: parent.width
+ height: parent.height
+ rotation: control.position === DialogButtonBox.Header ? 180 : 0
+ source: IOS.url + "dialogbuttonbox-delegate-vertical-last"
+ NinePatchImageSelector on source {
+ states: [
+ {"pressed": delegate.down},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
+ ]
+ }
+ }
+
+ readonly property NinePatchImage separatorLine : NinePatchImage {
+ parent: control.background.backgroundImage
+ x: control.itemAt(0) ? control.itemAt(0).width : 0
+ height: parent.height
+ rotation: control.position === DialogButtonBox.Header ? 180 : 0
+ visible: control.alignment === 0 && (control.count === 2 && !control.delegatePressed)
+
+ source: IOS.url + "dialogbuttonbox-separator"
+ NinePatchImageSelector on source {
+ states: [
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
+ ]
+ }
+ }
+ }
+}
diff --git a/src/quickcontrols/ios/Drawer.qml b/src/quickcontrols/ios/Drawer.qml
index 52cdb8b372..2ee955e7bc 100644
--- a/src/quickcontrols/ios/Drawer.qml
+++ b/src/quickcontrols/ios/Drawer.qml
@@ -32,8 +32,8 @@ T.Drawer {
source: IOS.url + "drawer-background"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"modal": control.modal}
]
}
diff --git a/src/quickcontrols/ios/Frame.qml b/src/quickcontrols/ios/Frame.qml
index 5c52269a3c..9179366808 100644
--- a/src/quickcontrols/ios/Frame.qml
+++ b/src/quickcontrols/ios/Frame.qml
@@ -19,6 +19,6 @@ T.Frame {
background: Rectangle {
radius: 9
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
}
}
diff --git a/src/quickcontrols/ios/GroupBox.qml b/src/quickcontrols/ios/GroupBox.qml
index 3adc599ddf..d3d14e7df8 100644
--- a/src/quickcontrols/ios/GroupBox.qml
+++ b/src/quickcontrols/ios/GroupBox.qml
@@ -36,6 +36,6 @@ T.GroupBox {
width: parent.width
height: parent.height - control.topPadding + control.bottomPadding
radius: 9
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
}
}
diff --git a/src/quickcontrols/ios/HorizontalHeaderView.qml b/src/quickcontrols/ios/HorizontalHeaderView.qml
index b6abd9e1d9..1a02ff8f05 100644
--- a/src/quickcontrols/ios/HorizontalHeaderView.qml
+++ b/src/quickcontrols/ios/HorizontalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
@@ -16,6 +18,10 @@ T.HorizontalHeaderView {
implicitHeight: Math.max(1, contentHeight)
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
readonly property real cellPadding: 8
implicitWidth: text.implicitWidth + (cellPadding * 2)
@@ -24,11 +30,9 @@ T.HorizontalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/quickcontrols/ios/ItemDelegate.qml b/src/quickcontrols/ios/ItemDelegate.qml
index 28146e51fd..2b27c86b26 100644
--- a/src/quickcontrols/ios/ItemDelegate.qml
+++ b/src/quickcontrols/ios/ItemDelegate.qml
@@ -21,7 +21,7 @@ T.ItemDelegate {
icon.width: 29
icon.height: 29
- icon.color: control.enabled ? control.palette.text : control.palette.mid
+ icon.color: control.palette.text
contentItem: IconLabel {
spacing: control.spacing
@@ -31,12 +31,12 @@ T.ItemDelegate {
icon: control.icon
text: control.text
font: control.font
- color: control.enabled ? control.palette.text : control.palette.mid
+ color: control.palette.text
}
background: Rectangle {
implicitHeight: 44
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
NinePatchImage {
property real offset: control.icon.source.toString() !== "" ? control.icon.width + control.spacing : 0
x: control.down ? 0 : control.leftPadding + offset
@@ -46,8 +46,8 @@ T.ItemDelegate {
source: IOS.url + "itemdelegate-background"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"pressed": control.down}
]
}
diff --git a/src/quickcontrols/ios/Menu.qml b/src/quickcontrols/ios/Menu.qml
index 437731c19e..752737f69b 100644
--- a/src/quickcontrols/ios/Menu.qml
+++ b/src/quickcontrols/ios/Menu.qml
@@ -35,8 +35,8 @@ T.Menu {
implicitHeight: contentHeight
model: control.contentModel
interactive: Window.window
- ? contentHeight + control.topPadding + control.bottomPadding > Window.window.height
- : false
+ ? contentHeight + control.topPadding + control.bottomPadding > control.height
+ : false
clip: true
currentIndex: control.currentIndex
@@ -52,8 +52,8 @@ T.Menu {
source: IOS.url + "menu-background"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
diff --git a/src/quickcontrols/ios/MenuBar.qml b/src/quickcontrols/ios/MenuBar.qml
index 06f549e82a..b38fa12fcb 100644
--- a/src/quickcontrols/ios/MenuBar.qml
+++ b/src/quickcontrols/ios/MenuBar.qml
@@ -24,7 +24,7 @@ T.MenuBar {
background: Rectangle {
opacity: 0.98
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
Rectangle {
height: 1
width: parent.width
diff --git a/src/quickcontrols/ios/MenuBarItem.qml b/src/quickcontrols/ios/MenuBarItem.qml
index 5fe36693e4..93febfa29e 100644
--- a/src/quickcontrols/ios/MenuBarItem.qml
+++ b/src/quickcontrols/ios/MenuBarItem.qml
@@ -19,8 +19,7 @@ T.MenuBarItem {
icon.width: 25
icon.height: 25
- icon.color: control.enabled ? (control.down ? control.palette.highlight : control.palette.button)
- : control.palette.mid
+ icon.color: control.down ? control.palette.highlight : control.palette.button
contentItem: IconLabel {
spacing: control.spacing
@@ -31,8 +30,7 @@ T.MenuBarItem {
icon: control.icon
text: control.text
font: control.font
- color: control.enabled ? (control.down ? control.palette.highlight : control.palette.button)
- : control.palette.mid
+ color: control.down ? control.palette.highlight : control.palette.button
}
background: Item {
diff --git a/src/quickcontrols/ios/MenuItem.qml b/src/quickcontrols/ios/MenuItem.qml
index 91fa865e06..29c2562ca0 100644
--- a/src/quickcontrols/ios/MenuItem.qml
+++ b/src/quickcontrols/ios/MenuItem.qml
@@ -23,7 +23,7 @@ T.MenuItem {
icon.width: 19
icon.height: 19
- icon.color: control.palette.windowText
+ icon.color: control.palette.text
readonly property bool isSingleItem: control.menu && control.menu.count === 1
readonly property bool isFirstItem: !isSingleItem && control.menu && control.menu.itemAt(0) === control ? true : false
@@ -43,7 +43,7 @@ T.MenuItem {
icon: control.icon
text: control.text
font: control.font
- color: control.palette.windowText
+ color: control.palette.text
}
arrow: ColorImage {
@@ -56,7 +56,7 @@ T.MenuItem {
visible: control.subMenu
opacity: control.enabled ? 1 : 0.5
mirror: control.mirrored
- color: control.palette.windowText
+ color: control.palette.text
source: control.subMenu ? IOS.url + "arrow-indicator-light.png" : ""
Behavior on rotation { RotationAnimation { duration: 100 } }
@@ -69,7 +69,7 @@ T.MenuItem {
visible: control.checked
source: control.checked ? IOS.url + "radiodelegate-indicator-light.png" : ""
- color: control.palette.windowText
+ color: control.palette.text
}
background: Item {
@@ -86,8 +86,8 @@ T.MenuItem {
states: [
{"edge": control.isFirstItem || control.isLastItem},
{"single": control.isSingleItem},
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"pressed": control.down}
]
}
diff --git a/src/quickcontrols/ios/MenuSeparator.qml b/src/quickcontrols/ios/MenuSeparator.qml
index ba69005611..790d8f0302 100644
--- a/src/quickcontrols/ios/MenuSeparator.qml
+++ b/src/quickcontrols/ios/MenuSeparator.qml
@@ -18,8 +18,8 @@ T.MenuSeparator {
source: IOS.url + "menuseparator-separator"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
diff --git a/src/quickcontrols/ios/PageIndicator.qml b/src/quickcontrols/ios/PageIndicator.qml
index 036f952ea8..9a059b5df2 100644
--- a/src/quickcontrols/ios/PageIndicator.qml
+++ b/src/quickcontrols/ios/PageIndicator.qml
@@ -18,8 +18,8 @@ T.PageIndicator {
source: IOS.url + "pageindicator-delegate"
ImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"current": index === control.currentIndex},
]
}
diff --git a/src/quickcontrols/ios/Popup.qml b/src/quickcontrols/ios/Popup.qml
index 47a6157f71..ec76a90267 100644
--- a/src/quickcontrols/ios/Popup.qml
+++ b/src/quickcontrols/ios/Popup.qml
@@ -35,8 +35,8 @@ T.Popup {
source: IOS.url + "popup-background"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
diff --git a/src/quickcontrols/ios/ProgressBar.qml b/src/quickcontrols/ios/ProgressBar.qml
index c280d5f94f..20cf4dc265 100644
--- a/src/quickcontrols/ios/ProgressBar.qml
+++ b/src/quickcontrols/ios/ProgressBar.qml
@@ -18,21 +18,36 @@ T.ProgressBar {
contentItem: Item {
parent: control.background
- implicitWidth: progress.width
- implicitHeight: progress.implicitHeight
+ implicitWidth: control.indeterminate ? animatedProgress.implicitWidth : progress.implicitWidth
+ implicitHeight: control.indeterminate ? animatedProgress.implicitHeight : progress.implicitHeight
scale: control.mirrored ? -1 : 1
readonly property NinePatchImage progress: NinePatchImage {
parent: control.contentItem
- visible: control.indeterminate || control.value
+ visible: !control.indeterminate && control.value
y: (parent.height - height) / 2
- width: control.indeterminate ? control.width * 0.4 : control.position * parent.width
+ width: control.position * parent.width
source: IOS.url + "slider-progress"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
+ ]
+ }
+ }
+
+ readonly property NinePatchImage animatedProgress: NinePatchImage {
+ parent: control.contentItem
+ visible: control.indeterminate
+ y: (parent.height - height) / 2
+ width: control.width * 0.4
+
+ source: IOS.url + "slider-progress"
+ NinePatchImageSelector on source {
+ states: [
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
@@ -57,8 +72,8 @@ T.ProgressBar {
width: control.background.width
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
diff --git a/src/quickcontrols/ios/RadioButton.qml b/src/quickcontrols/ios/RadioButton.qml
index 37cf71e1f5..d107d9b490 100644
--- a/src/quickcontrols/ios/RadioButton.qml
+++ b/src/quickcontrols/ios/RadioButton.qml
@@ -26,8 +26,8 @@ T.RadioButton {
ImageSelector on source {
states: [
{"checked": control.checked},
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
@@ -38,7 +38,7 @@ T.RadioButton {
text: control.text
font: control.font
- color: control.palette.windowText
+ color: control.palette.text
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/quickcontrols/ios/RadioDelegate.qml b/src/quickcontrols/ios/RadioDelegate.qml
index 6de6dbb622..91f74b9b4f 100644
--- a/src/quickcontrols/ios/RadioDelegate.qml
+++ b/src/quickcontrols/ios/RadioDelegate.qml
@@ -21,7 +21,7 @@ T.RadioDelegate {
icon.width: 29
icon.height: 29
- icon.color: control.enabled ? control.palette.text : control.palette.mid
+ icon.color: control.palette.text
indicator: Image {
x: control.mirrored ? control.leftPadding : control.width - width - control.rightPadding
@@ -32,8 +32,8 @@ T.RadioDelegate {
source: IOS.url + "radiodelegate-indicator"
ImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
@@ -50,12 +50,12 @@ T.RadioDelegate {
icon: control.icon
text: control.text
font: control.font
- color: control.enabled ? control.palette.text : control.palette.mid
+ color: control.palette.text
}
background: Rectangle {
implicitHeight: 44
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
NinePatchImage {
property real offset: control.icon.source.toString() !== "" ? control.icon.width + control.spacing : 0
x: control.down ? 0 : control.leftPadding + offset
@@ -65,8 +65,8 @@ T.RadioDelegate {
source: IOS.url + "itemdelegate-background"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"pressed": control.down}
]
}
diff --git a/src/quickcontrols/ios/RangeSlider.qml b/src/quickcontrols/ios/RangeSlider.qml
index 966f612ba8..aab3b0a598 100644
--- a/src/quickcontrols/ios/RangeSlider.qml
+++ b/src/quickcontrols/ios/RangeSlider.qml
@@ -28,8 +28,8 @@ T.RangeSlider {
source: IOS.url + "slider-handle"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
]
}
}
@@ -47,8 +47,8 @@ T.RangeSlider {
source: IOS.url + "slider-handle"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
]
}
}
@@ -67,22 +67,24 @@ T.RangeSlider {
width: control.horizontal ? control.background.width : control.background.height
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
]
}
NinePatchImage {
- x: control.first.handle.width / 2 + control.first.position * (parent.width - control.first.handle.width)
+ readonly property real handleWidth: control.first.handle ? control.first.handle.width : 0
+
+ x: handleWidth / 2 + control.first.position * (parent.width - handleWidth)
y: (parent.height - height) / 2
- width: control.second.position * (parent.width - control.first.handle.width) - control.first.position * (parent.width - control.first.handle.width)
+ width: control.second.position * (parent.width - handleWidth) - control.first.position * (parent.width - handleWidth)
height: parent.height
source: IOS.url + "slider-progress"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
]
}
}
diff --git a/src/quickcontrols/ios/ScrollBar.qml b/src/quickcontrols/ios/ScrollBar.qml
index 3bdabe4f08..afd2e737b3 100644
--- a/src/quickcontrols/ios/ScrollBar.qml
+++ b/src/quickcontrols/ios/ScrollBar.qml
@@ -24,8 +24,8 @@ T.ScrollBar {
source: IOS.url + "scrollindicator-handle"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"horizontal": control.horizontal},
{"vertical": control.vertical}
]
diff --git a/src/quickcontrols/ios/ScrollIndicator.qml b/src/quickcontrols/ios/ScrollIndicator.qml
index d6d842196a..5af880ca36 100644
--- a/src/quickcontrols/ios/ScrollIndicator.qml
+++ b/src/quickcontrols/ios/ScrollIndicator.qml
@@ -18,8 +18,8 @@ T.ScrollIndicator {
source: IOS.url + "scrollindicator-handle"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"horizontal": control.horizontal},
{"vertical": control.vertical}
]
diff --git a/src/quickcontrols/ios/SelectionRectangle.qml b/src/quickcontrols/ios/SelectionRectangle.qml
new file mode 100644
index 0000000000..06e540b411
--- /dev/null
+++ b/src/quickcontrols/ios/SelectionRectangle.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.Controls.iOS
+import QtQuick.Controls.iOS.impl
+
+T.SelectionRectangle {
+ id: control
+
+ topLeftHandle: handle
+ bottomRightHandle: handle
+
+ Component {
+ id: handle
+ Image {
+ id: image
+ source: IOS.url + "selectionrectangle-handle"
+ visible: SelectionRectangle.control.active
+ ImageSelector on source {
+ states: [
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
+ ]
+ }
+ }
+ }
+}
diff --git a/src/quickcontrols/ios/Slider.qml b/src/quickcontrols/ios/Slider.qml
index 1f6ca9f242..efa196bc72 100644
--- a/src/quickcontrols/ios/Slider.qml
+++ b/src/quickcontrols/ios/Slider.qml
@@ -26,8 +26,8 @@ T.Slider {
source: IOS.url + "slider-handle"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"disabled": !control.enabled}
]
}
@@ -47,20 +47,22 @@ T.Slider {
width: control.horizontal ? background.width : background.height
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
]
}
NinePatchImage {
- width: control.handle.width / 2 + control.position * (parent.width - control.handle.width)
+ readonly property real handleWidth: control.handle ? control.handle.width : 0
+
+ width: handleWidth / 2 + control.position * (parent.width - handleWidth)
height: parent.height
source: IOS.url + "slider-progress"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
]
}
}
diff --git a/src/quickcontrols/ios/SpinBox.qml b/src/quickcontrols/ios/SpinBox.qml
index 11131a0db1..4168f6b43e 100644
--- a/src/quickcontrols/ios/SpinBox.qml
+++ b/src/quickcontrols/ios/SpinBox.qml
@@ -53,8 +53,8 @@ T.SpinBox {
states: [
{"up": true},
{"pressed": control.up.pressed},
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
@@ -69,8 +69,8 @@ T.SpinBox {
states: [
{"down": true},
{"pressed": control.down.pressed},
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
@@ -87,8 +87,8 @@ T.SpinBox {
y: (parent.height - height) / 2
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
diff --git a/src/quickcontrols/ios/SwipeDelegate.qml b/src/quickcontrols/ios/SwipeDelegate.qml
index ccabfc0cb4..9c6a4c3703 100644
--- a/src/quickcontrols/ios/SwipeDelegate.qml
+++ b/src/quickcontrols/ios/SwipeDelegate.qml
@@ -21,7 +21,7 @@ T.SwipeDelegate {
icon.width: 29
icon.height: 29
- icon.color: control.enabled ? control.palette.text : control.palette.mid
+ icon.color: control.palette.text
swipe.transition: Transition { SmoothedAnimation { velocity: 3; easing.type: Easing.InOutCubic } }
@@ -34,12 +34,12 @@ T.SwipeDelegate {
icon: control.icon
text: control.text
font: control.font
- color: control.enabled ? control.palette.text : control.palette.mid
+ color: control.palette.text
}
background: Rectangle {
implicitHeight: 44
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
NinePatchImage {
property real offset: control.icon.source.toString() !== "" ? control.icon.width + control.spacing : 0
x: control.down ? 0 : control.leftPadding + offset
@@ -49,8 +49,8 @@ T.SwipeDelegate {
source: IOS.url + "itemdelegate-background"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"pressed": control.down}
]
}
diff --git a/src/quickcontrols/ios/Switch.qml b/src/quickcontrols/ios/Switch.qml
index 7a600afc5f..1b219cddf4 100644
--- a/src/quickcontrols/ios/Switch.qml
+++ b/src/quickcontrols/ios/Switch.qml
@@ -32,8 +32,8 @@ T.Switch {
source: IOS.url + "switch-indicator"
ImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"checked": control.checked}
]
}
@@ -53,8 +53,8 @@ T.Switch {
source: IOS.url + "switch-handle"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"disabled": !control.enabled}
]
}
@@ -72,7 +72,7 @@ T.Switch {
text: control.text
font: control.font
- color: control.palette.windowText
+ color: control.palette.text
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/quickcontrols/ios/SwitchDelegate.qml b/src/quickcontrols/ios/SwitchDelegate.qml
index 4cba6dcabf..4b7d8bdccf 100644
--- a/src/quickcontrols/ios/SwitchDelegate.qml
+++ b/src/quickcontrols/ios/SwitchDelegate.qml
@@ -21,7 +21,7 @@ T.SwitchDelegate {
icon.width: 29
icon.height: 29
- icon.color: control.enabled ? control.palette.text : control.palette.mid
+ icon.color: control.palette.text
indicator: Image {
x: control.text ? (control.mirrored ? control.leftPadding : control.width - width - control.rightPadding) : control.leftPadding + (control.availableWidth - width) / 2
@@ -33,8 +33,8 @@ T.SwitchDelegate {
source: IOS.url + "switch-indicator"
ImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"checked": control.checked}
]
}
@@ -54,8 +54,8 @@ T.SwitchDelegate {
source: IOS.url + "switch-handle"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
{"disabled": !control.enabled}
]
}
@@ -79,12 +79,12 @@ T.SwitchDelegate {
icon: control.icon
text: control.text
font: control.font
- color: control.enabled ? control.palette.text : control.palette.mid
+ color: control.palette.text
}
background: Rectangle {
implicitHeight: 44
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
NinePatchImage {
property real offset: control.icon.source.toString() !== "" ? control.icon.width + control.spacing : 0
x: control.leftPadding + offset
@@ -93,8 +93,8 @@ T.SwitchDelegate {
source: IOS.url + "itemdelegate-background"
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
]
}
}
diff --git a/src/quickcontrols/ios/TabBar.qml b/src/quickcontrols/ios/TabBar.qml
index 1d289c71af..0f42ea14e5 100644
--- a/src/quickcontrols/ios/TabBar.qml
+++ b/src/quickcontrols/ios/TabBar.qml
@@ -32,7 +32,7 @@ T.TabBar {
background: Rectangle {
implicitHeight: 49
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
Rectangle {
height: 1
width: parent.width
diff --git a/src/quickcontrols/ios/ToolBar.qml b/src/quickcontrols/ios/ToolBar.qml
index 77b3d8ac0b..28b0029018 100644
--- a/src/quickcontrols/ios/ToolBar.qml
+++ b/src/quickcontrols/ios/ToolBar.qml
@@ -15,7 +15,7 @@ T.ToolBar {
background: Rectangle {
implicitHeight: 49
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.light : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.light : control.palette.base
Rectangle {
height: 1
width: parent.width
diff --git a/src/quickcontrols/ios/ToolButton.qml b/src/quickcontrols/ios/ToolButton.qml
index b18073dc81..863226fdd2 100644
--- a/src/quickcontrols/ios/ToolButton.qml
+++ b/src/quickcontrols/ios/ToolButton.qml
@@ -18,8 +18,7 @@ T.ToolButton {
icon.width: 25
icon.height: 25
- icon.color: control.enabled ? (control.down ? control.palette.highlight : control.palette.button)
- : control.palette.mid
+ icon.color: control.down ? control.palette.highlight : control.palette.button
contentItem: IconLabel {
spacing: control.spacing
@@ -29,8 +28,7 @@ T.ToolButton {
icon: control.icon
text: control.text
font: control.font
- color: control.enabled ? (control.down ? control.palette.highlight : control.palette.button)
- : control.palette.mid
+ color: control.down ? control.palette.highlight : control.palette.button
}
background: Item {
diff --git a/src/quickcontrols/ios/ToolSeparator.qml b/src/quickcontrols/ios/ToolSeparator.qml
new file mode 100644
index 0000000000..10d94f204a
--- /dev/null
+++ b/src/quickcontrols/ios/ToolSeparator.qml
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+
+T.ToolSeparator {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ horizontalPadding: vertical ? 13 : 7
+ verticalPadding: vertical ? 7 : 13
+
+ contentItem: Rectangle {
+ implicitHeight: control.vertical ? 23 : 1
+ implicitWidth: control.vertical ? 1 : 23
+ color: control.palette.mid
+ }
+}
diff --git a/src/quickcontrols/ios/ToolTip.qml b/src/quickcontrols/ios/ToolTip.qml
new file mode 100644
index 0000000000..973e819503
--- /dev/null
+++ b/src/quickcontrols/ios/ToolTip.qml
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.Controls.iOS.impl
+
+T.ToolTip {
+ id: control
+
+ x: parent ? (parent.width - implicitWidth) / 2 : 0 - (background ? background.leftInset : 0)
+ y: -implicitHeight - (background ? background.topInset : 0)
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding)
+
+ topMargin: background ? background.topInset : 0
+ leftMargin: background ? background.leftInset : 0
+ rightMargin: background ? background.rightInset : 0
+ bottomMargin: background ? background.bottomInset : 0
+
+ topPadding: background ? background.topPadding : 0
+ leftPadding: background ? background.leftPadding : 0
+ rightPadding: background ? background.rightPadding : 0
+ bottomPadding: background ? background.bottomPadding : 0
+
+ topInset: background ? -background.topInset || 0 : 0
+ leftInset: background ? -background.leftInset || 0 : 0
+ rightInset: background ? -background.rightInset || 0 : 0
+ bottomInset: background ? -background.bottomInset || 0 : 0
+
+ closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnReleaseOutsideParent
+
+ contentItem: Text {
+ text: control.text
+ wrapMode: Text.Wrap
+ font: control.font
+ color: control.palette.toolTipText
+ }
+
+ background: NinePatchImage {
+ source: IOS.url + "tooltip-background"
+ NinePatchImageSelector on source {
+ states: [
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
+ ]
+ }
+ }
+}
diff --git a/src/quickcontrols/ios/TreeViewDelegate.qml b/src/quickcontrols/ios/TreeViewDelegate.qml
index 89577e297b..5fc16bee07 100644
--- a/src/quickcontrols/ios/TreeViewDelegate.qml
+++ b/src/quickcontrols/ios/TreeViewDelegate.qml
@@ -46,8 +46,8 @@ T.TreeViewDelegate {
source: IOS.url + "arrow-indicator"
ImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
@@ -55,15 +55,15 @@ T.TreeViewDelegate {
background: Rectangle {
implicitHeight: 44
- color: Qt.styleHints.appearance === Qt.Dark ? control.palette.dark : control.palette.base
+ color: Qt.styleHints.colorScheme === Qt.Dark ? control.palette.dark : control.palette.base
NinePatchImage {
height: parent.height
width: parent.width
source: IOS.url + (control.highlighted ? "itemdelegate-background-pressed" : "itemdelegate-background")
NinePatchImageSelector on source {
states: [
- {"light": Qt.styleHints.appearance === Qt.Light},
- {"dark": Qt.styleHints.appearance === Qt.Dark}
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark}
]
}
}
@@ -86,7 +86,7 @@ T.TreeViewDelegate {
readonly property int __role: {
let model = control.treeView.model
- let index = control.treeView.modelIndex(column, row)
+ let index = control.treeView.index(row, column)
let editText = model.data(index, Qt.EditRole)
return editText !== undefined ? Qt.EditRole : Qt.DisplayRole
}
@@ -96,12 +96,12 @@ T.TreeViewDelegate {
x: control.contentItem.x
y: (parent.height - height) / 2
width: control.contentItem.width
- text: control.treeView.model.data(control.treeView.modelIndex(column, row), __role)
+ text: control.treeView.model.data(control.treeView.index(row, column), __role)
focus: true
}
TableView.onCommit: {
- let index = TableView.view.modelIndex(column, row)
+ let index = TableView.view.index(row, column)
TableView.view.model.setData(index, textField.text, __role)
}
diff --git a/src/quickcontrols/ios/VerticalHeaderView.qml b/src/quickcontrols/ios/VerticalHeaderView.qml
index 6015a800b1..179bc35b23 100644
--- a/src/quickcontrols/ios/VerticalHeaderView.qml
+++ b/src/quickcontrols/ios/VerticalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
@@ -16,6 +18,10 @@ T.VerticalHeaderView {
implicitHeight: syncView ? syncView.height : 0
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
readonly property real cellPadding: 8
implicitWidth: Math.max(control.width, text.implicitWidth + (cellPadding * 2))
@@ -24,11 +30,9 @@ T.VerticalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark.9.png
new file mode 100644
index 0000000000..4c752634f2
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark@2x.9.png
new file mode 100644
index 0000000000..a0e7be1fb7
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark@3x.9.png
new file mode 100644
index 0000000000..01ca2e7b19
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-dark@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light.9.png
new file mode 100644
index 0000000000..31f979f17a
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light@2x.9.png
new file mode 100644
index 0000000000..ad45b8c100
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light@3x.9.png
new file mode 100644
index 0000000000..c9414f36be
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-light@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark.9.png
new file mode 100644
index 0000000000..e08c253e2a
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark@2x.9.png
new file mode 100644
index 0000000000..c203f8f6d7
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark@3x.9.png
new file mode 100644
index 0000000000..b651b14bf0
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-dark@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light.9.png
new file mode 100644
index 0000000000..e86f90a095
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light@2x.9.png
new file mode 100644
index 0000000000..a2553b828e
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light@3x.9.png
new file mode 100644
index 0000000000..d832ded229
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-horizontal-pressed-light@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark.9.png
new file mode 100644
index 0000000000..8c966a7f87
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark@2x.9.png
new file mode 100644
index 0000000000..2f65e40b76
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark@3x.9.png
new file mode 100644
index 0000000000..311e1c15bf
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-dark@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark.9.png
new file mode 100644
index 0000000000..bfb6d3a6bf
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark@2x.9.png
new file mode 100644
index 0000000000..50345d347c
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark@3x.9.png
new file mode 100644
index 0000000000..2afa7013bb
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-dark@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light.9.png
new file mode 100644
index 0000000000..f80b82367e
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light@2x.9.png
new file mode 100644
index 0000000000..dcba4c6220
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light@3x.9.png
new file mode 100644
index 0000000000..5adde16f54
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-light@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark.9.png
new file mode 100644
index 0000000000..898cdf1173
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark@2x.9.png
new file mode 100644
index 0000000000..492d319559
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark@3x.9.png
new file mode 100644
index 0000000000..d670e53d32
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-dark@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light.9.png
new file mode 100644
index 0000000000..c3f9486440
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light@2x.9.png
new file mode 100644
index 0000000000..fb63659ceb
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light@3x.9.png
new file mode 100644
index 0000000000..4b2c1f8a21
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-last-pressed-light@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light.9.png
new file mode 100644
index 0000000000..67a28cead3
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light@2x.9.png
new file mode 100644
index 0000000000..d4348cb61a
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light@3x.9.png
new file mode 100644
index 0000000000..c00560ae56
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-light@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark.9.png
new file mode 100644
index 0000000000..0d99bbd993
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark@2x.9.png
new file mode 100644
index 0000000000..7e7e00bfce
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark@3x.9.png
new file mode 100644
index 0000000000..64bd4860a0
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-dark@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light.9.png
new file mode 100644
index 0000000000..094a28ef2f
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light@2x.9.png
new file mode 100644
index 0000000000..bafcb6f623
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light@3x.9.png
new file mode 100644
index 0000000000..8b17a33b57
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-delegate-vertical-pressed-light@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark.9.png
new file mode 100644
index 0000000000..5f00cd512d
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark@2x.9.png
new file mode 100644
index 0000000000..72365ea12b
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark@3x.9.png
new file mode 100644
index 0000000000..3c6b87a691
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-separator-dark@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-separator-light.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-separator-light.9.png
new file mode 100644
index 0000000000..e701f3e7d5
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-separator-light.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-separator-light@2x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-separator-light@2x.9.png
new file mode 100644
index 0000000000..1e151c1bab
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-separator-light@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/dialogbuttonbox-separator-light@3x.9.png b/src/quickcontrols/ios/images/dialogbuttonbox-separator-light@3x.9.png
new file mode 100644
index 0000000000..f9391caa00
--- /dev/null
+++ b/src/quickcontrols/ios/images/dialogbuttonbox-separator-light@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/selectionrectangle-handle-dark.png b/src/quickcontrols/ios/images/selectionrectangle-handle-dark.png
new file mode 100644
index 0000000000..e6581ea767
--- /dev/null
+++ b/src/quickcontrols/ios/images/selectionrectangle-handle-dark.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/selectionrectangle-handle-dark@2x.png b/src/quickcontrols/ios/images/selectionrectangle-handle-dark@2x.png
new file mode 100644
index 0000000000..42f2ef1a1a
--- /dev/null
+++ b/src/quickcontrols/ios/images/selectionrectangle-handle-dark@2x.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/selectionrectangle-handle-dark@3x.png b/src/quickcontrols/ios/images/selectionrectangle-handle-dark@3x.png
new file mode 100644
index 0000000000..01feffb4cf
--- /dev/null
+++ b/src/quickcontrols/ios/images/selectionrectangle-handle-dark@3x.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/selectionrectangle-handle-light.png b/src/quickcontrols/ios/images/selectionrectangle-handle-light.png
new file mode 100644
index 0000000000..f16b0ee6e8
--- /dev/null
+++ b/src/quickcontrols/ios/images/selectionrectangle-handle-light.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/selectionrectangle-handle-light@2x.png b/src/quickcontrols/ios/images/selectionrectangle-handle-light@2x.png
new file mode 100644
index 0000000000..87907decba
--- /dev/null
+++ b/src/quickcontrols/ios/images/selectionrectangle-handle-light@2x.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/selectionrectangle-handle-light@3x.png b/src/quickcontrols/ios/images/selectionrectangle-handle-light@3x.png
new file mode 100644
index 0000000000..4379e4841a
--- /dev/null
+++ b/src/quickcontrols/ios/images/selectionrectangle-handle-light@3x.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/tooltip-background-dark.9.png b/src/quickcontrols/ios/images/tooltip-background-dark.9.png
new file mode 100644
index 0000000000..271a03eae6
--- /dev/null
+++ b/src/quickcontrols/ios/images/tooltip-background-dark.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/tooltip-background-dark@2x.9.png b/src/quickcontrols/ios/images/tooltip-background-dark@2x.9.png
new file mode 100644
index 0000000000..a45eac9cd8
--- /dev/null
+++ b/src/quickcontrols/ios/images/tooltip-background-dark@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/tooltip-background-dark@3x.9.png b/src/quickcontrols/ios/images/tooltip-background-dark@3x.9.png
new file mode 100644
index 0000000000..4d0cd6a7f7
--- /dev/null
+++ b/src/quickcontrols/ios/images/tooltip-background-dark@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/tooltip-background-light.9.png b/src/quickcontrols/ios/images/tooltip-background-light.9.png
new file mode 100644
index 0000000000..76e2f9a4ea
--- /dev/null
+++ b/src/quickcontrols/ios/images/tooltip-background-light.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/tooltip-background-light@2x.9.png b/src/quickcontrols/ios/images/tooltip-background-light@2x.9.png
new file mode 100644
index 0000000000..2be7602f84
--- /dev/null
+++ b/src/quickcontrols/ios/images/tooltip-background-light@2x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/images/tooltip-background-light@3x.9.png b/src/quickcontrols/ios/images/tooltip-background-light@3x.9.png
new file mode 100644
index 0000000000..0563a85195
--- /dev/null
+++ b/src/quickcontrols/ios/images/tooltip-background-light@3x.9.png
Binary files differ
diff --git a/src/quickcontrols/ios/impl/CMakeLists.txt b/src/quickcontrols/ios/impl/CMakeLists.txt
index e298335bb1..a19704ad11 100644
--- a/src/quickcontrols/ios/impl/CMakeLists.txt
+++ b/src/quickcontrols/ios/impl/CMakeLists.txt
@@ -7,13 +7,13 @@
set(qml_files
"CursorDelegate.qml"
+ "DialogButtonBoxDelegate.qml"
)
-qt_internal_add_qml_module(qtquickcontrols2iosstyleimplplugin
+qt_internal_add_qml_module(QuickControls2IOSStyleImpl
URI "QtQuick.Controls.iOS.impl"
VERSION "${PROJECT_VERSION}"
PLUGIN_TARGET qtquickcontrols2iosstyleimplplugin
- NO_PLUGIN_OPTIONAL
SOURCES
qquickiosstyle.cpp qquickiosstyle_p.h
qquickioscursorflashtimer_p.h qquickioscursorflashtimer.cpp
diff --git a/src/quickcontrols/ios/impl/DialogButtonBoxDelegate.qml b/src/quickcontrols/ios/impl/DialogButtonBoxDelegate.qml
new file mode 100644
index 0000000000..2be1f9535e
--- /dev/null
+++ b/src/quickcontrols/ios/impl/DialogButtonBoxDelegate.qml
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.iOS.impl
+
+Button {
+ id: delegate
+
+ implicitHeight: 44
+
+ readonly property var dialogButtonBox: DialogButtonBox.buttonBox
+ readonly property bool hasVerticalLayout: dialogButtonBox && (dialogButtonBox.count !== 2)
+ readonly property bool isLastItem: dialogButtonBox
+ && (dialogButtonBox.contentChildren)
+ && (dialogButtonBox.itemAt(count - 1) == delegate)
+ readonly property int count: dialogButtonBox ? dialogButtonBox.count : 0
+
+ flat: true
+
+ contentItem: IconLabel {
+ readonly property var redColor: Qt.styleHints.colorScheme === Qt.Light ? "#ff3b30" : "#ff453a"
+ text: delegate.text
+ font: delegate.font
+ spacing: delegate.spacing
+ color: delegate.DialogButtonBox.buttonRole === DialogButtonBox.DestructiveRole
+ ? redColor
+ : (delegate.down ? delegate.palette.highlight : delegate.palette.button)
+ }
+
+ background: Item {
+ // The assets below only support the typical iOS Dialog look with buttons
+ // positioned at the bottom and that fill the entire width of the parent.
+ // Don't draw a background if these conditions are not met
+ visible: delegate.dialogButtonBox
+ && delegate.dialogButtonBox.position === DialogButtonBox.Footer
+ && delegate.dialogButtonBox.alignment === 0
+ implicitHeight: 44
+ readonly property bool leftItem: !delegate.hasVerticalLayout && !delegate.isLastItem
+ readonly property bool rightItem: !delegate.hasVerticalLayout && !leftItem
+ readonly property bool flip: delegate.mirrored ? leftItem : rightItem
+
+ NinePatchImage {
+ transform: [
+ // flip
+ Translate { x: (!delegate.background.flip ? 0 : -width) },
+ Scale { xScale: (!delegate.background.flip ? 1 : -1) }
+ ]
+ width: parent.width
+ height: parent.height
+ source: IOS.url + "dialogbuttonbox-delegate"
+ NinePatchImageSelector on source {
+ states: [
+ {"horizontal": !delegate.hasVerticalLayout},
+ {"vertical": delegate.hasVerticalLayout},
+ {"last": delegate.hasVerticalLayout && delegate.isLastItem},
+ {"pressed": delegate.down},
+ {"light": Qt.styleHints.colorScheme === Qt.Light},
+ {"dark": Qt.styleHints.colorScheme === Qt.Dark},
+ ]
+ }
+ }
+
+
+ }
+}
diff --git a/src/quickcontrols/ios/impl/qquickiosstyle_p.h b/src/quickcontrols/ios/impl/qquickiosstyle_p.h
index 03144751ad..4df7c2ac72 100644
--- a/src/quickcontrols/ios/impl/qquickiosstyle_p.h
+++ b/src/quickcontrols/ios/impl/qquickiosstyle_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickIOSStyle : public QObject
{
Q_OBJECT
- Q_PROPERTY(QUrl url READ url CONSTANT)
+ Q_PROPERTY(QUrl url READ url CONSTANT FINAL)
QML_NAMED_ELEMENT(IOS)
QML_SINGLETON
QML_ADDED_IN_VERSION(6, 5)
diff --git a/src/quickcontrols/ios/qquickiostheme.mm b/src/quickcontrols/ios/qquickiostheme.mm
index 2ee9a63d3d..2f7c84ae7e 100644
--- a/src/quickcontrols/ios/qquickiostheme.mm
+++ b/src/quickcontrols/ios/qquickiostheme.mm
@@ -3,7 +3,9 @@
#include "qquickiostheme_p.h"
+#if defined(Q_OS_APPLE)
#include <QtGui/private/qcoregraphics_p.h>
+#endif
#ifdef Q_OS_IOS
#include <UIKit/UIInterface.h>
@@ -19,8 +21,9 @@ void QQuickIOSTheme::initialize(QQuickTheme *theme)
QPalette systemPalette;
QColor window;
- QColor windowText;
- QColor background;
+ QColor base;
+ QColor text;
+ QColor disabledText;
QColor placeholderText;
QColor button;
QColor disabledButton;
@@ -30,8 +33,9 @@ void QQuickIOSTheme::initialize(QQuickTheme *theme)
QColor darkGray;
#ifdef Q_OS_IOS
window = qt_mac_toQColor(UIColor.systemGroupedBackgroundColor.CGColor);
- windowText = qt_mac_toQColor(UIColor.labelColor.CGColor);
- background = qt_mac_toQColor(UIColor.secondarySystemGroupedBackgroundColor.CGColor);
+ base = qt_mac_toQColor(UIColor.secondarySystemGroupedBackgroundColor.CGColor);
+ text = qt_mac_toQColor(UIColor.labelColor.CGColor);
+ disabledText = qt_mac_toQColor(UIColor.tertiaryLabelColor.CGColor);
placeholderText = qt_mac_toQColor(UIColor.placeholderTextColor.CGColor);
button = qt_mac_toQColor(UIColor.systemBlueColor.CGColor);
disabledButton = qt_mac_toQColor(UIColor.tertiarySystemFillColor.CGColor);
@@ -42,9 +46,10 @@ void QQuickIOSTheme::initialize(QQuickTheme *theme)
#else
bool isDarkSystemTheme = QQuickStylePrivate::isDarkSystemTheme();
window = isDarkSystemTheme ? QColor(qRgba(0, 0, 0, 255)) : QColor(qRgba(242, 242, 247, 255));
- windowText = isDarkSystemTheme ? QColor(Qt::white) : QColor(Qt::black);
- background = isDarkSystemTheme ? QColor(qRgba(28, 28, 30, 255)) : QColor(Qt::white);
- placeholderText = isDarkSystemTheme ? QColor(qRgba(235, 235, 245, 77)) : QColor(qRgba(60, 60, 67, 77));
+ base = isDarkSystemTheme ? QColor(qRgba(28, 28, 30, 255)) : QColor(Qt::white);
+ text = isDarkSystemTheme ? QColor(Qt::white) : QColor(Qt::black);
+ disabledText = isDarkSystemTheme ? QColor(qRgba(60, 60, 67, 76)) : QColor(qRgba(235, 235, 245, 76));
+ placeholderText = isDarkSystemTheme ? QColor(qRgba(235, 235, 245, 76)) : QColor(qRgba(60, 60, 67, 77));
button = isDarkSystemTheme ? QColor(qRgba(10, 132, 255, 255)) : QColor(qRgba(0, 122, 255, 255));
disabledButton = isDarkSystemTheme ? QColor(qRgba(118, 118, 128, 61)) : QColor(qRgba(118, 118, 128, 31));
white = QColor(Qt::white);
@@ -53,23 +58,23 @@ void QQuickIOSTheme::initialize(QQuickTheme *theme)
darkGray = QColor(qRgba(142, 142, 147, 255));
#endif
systemPalette.setColor(QPalette::Window, window);
+ systemPalette.setColor(QPalette::Base, base);
- systemPalette.setColor(QPalette::Active, QPalette::WindowText, windowText);
- systemPalette.setColor(QPalette::Disabled, QPalette::WindowText, darkGray);
-
- systemPalette.setColor(QPalette::Base, background);
-
+ systemPalette.setColor(QPalette::WindowText, text);
+ systemPalette.setColor(QPalette::Disabled, QPalette::WindowText, disabledText);
+ systemPalette.setColor(QPalette::Text, text);
+ systemPalette.setColor(QPalette::Disabled, QPalette::Text, disabledText);
systemPalette.setColor(QPalette::PlaceholderText, placeholderText);
systemPalette.setColor(QPalette::Button, button);
systemPalette.setColor(QPalette::Disabled, QPalette::Button, disabledButton);
-
systemPalette.setColor(QPalette::ButtonText, white);
- white.setAlphaF(0.5);
- systemPalette.setColor(QPalette::Disabled, QPalette::ButtonText, white);
+ systemPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledText);
+
+ systemPalette.setColor(QPalette::ToolTipText, text);
+ systemPalette.setColor(QPalette::Disabled, QPalette::ToolTipText, disabledText);
- button.setAlphaF(0.8);
- systemPalette.setColor(QPalette::Highlight, button);
+ systemPalette.setColor(QPalette::Highlight, button.lighter(115));
systemPalette.setColor(QPalette::Light, lightGray);
systemPalette.setColor(QPalette::Mid, gray);
@@ -79,4 +84,3 @@ void QQuickIOSTheme::initialize(QQuickTheme *theme)
}
QT_END_NAMESPACE
-
diff --git a/src/quickcontrols/macos/BusyIndicator.qml b/src/quickcontrols/macos/BusyIndicator.qml
new file mode 100644
index 0000000000..c74e215f36
--- /dev/null
+++ b/src/quickcontrols/macos/BusyIndicator.qml
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.iOS.impl
+import QtQuick.Controls.impl
+
+T.BusyIndicator {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ topPadding: background ? background.topPadding : 0
+ leftPadding: background ? background.leftPadding : 0
+ rightPadding: background ? background.rightPadding : 0
+ bottomPadding: background ? background.bottomPadding : 0
+
+ topInset: background ? -background.topInset || 0 : 0
+ leftInset: background ? -background.leftInset || 0 : 0
+ rightInset: background ? -background.rightInset || 0 : 0
+ bottomInset: background ? -background.bottomInset || 0 : 0
+
+ contentItem: AnimatedImage {
+ source: "qrc:/qt-project.org/imports/QtQuick/Controls/macOS/images/busyindicator-"
+ + (Qt.styleHints.colorScheme === Qt.Light ? "light" : "dark") + ".webp"
+ opacity: control.running ? 1 : 0
+ playing: control.running || opacity > 0
+ visible: control.running || opacity > 0
+
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 250
+ }
+ }
+ }
+}
+
diff --git a/src/quickcontrols/macos/CMakeLists.txt b/src/quickcontrols/macos/CMakeLists.txt
index f0a3dcd3f4..376c093f27 100644
--- a/src/quickcontrols/macos/CMakeLists.txt
+++ b/src/quickcontrols/macos/CMakeLists.txt
@@ -6,23 +6,31 @@
#####################################################################
set(qml_files
+ "BusyIndicator.qml"
"Button.qml"
- "Slider.qml"
- "GroupBox.qml"
"CheckBox.qml"
- "RadioButton.qml"
- "SelectionRectangle.qml"
- "SpinBox.qml"
- "TextField.qml"
- "Frame.qml"
- "TextArea.qml"
+ "CheckDelegate.qml"
"ComboBox.qml"
- "ScrollView.qml"
- "ScrollBar.qml"
- "ProgressBar.qml"
+ "DelayButton.qml"
"Dial.qml"
"Dialog.qml"
"DialogButtonBox.qml"
+ "Frame.qml"
+ "GroupBox.qml"
+ "ItemDelegate.qml"
+ "ProgressBar.qml"
+ "RadioButton.qml"
+ "RadioDelegate.qml"
+ "RangeSlider.qml"
+ "ScrollBar.qml"
+ "ScrollView.qml"
+ "SelectionRectangle.qml"
+ "Slider.qml"
+ "SpinBox.qml"
+ "Switch.qml"
+ "SwitchDelegate.qml"
+ "TextArea.qml"
+ "TextField.qml"
)
if (QT_FEATURE_quick_treeview)
@@ -45,6 +53,13 @@ qt_internal_add_qml_module(qtquickcontrols2macosstyleplugin
qtquickcontrols2macosstyleplugin.cpp
QML_FILES
${qml_files}
+ RESOURCES
+ images/busyindicator-dark.webp
+ images/busyindicator-dark@2x.webp
+ images/busyindicator-dark@3x.webp
+ images/busyindicator-light.webp
+ images/busyindicator-light@2x.webp
+ images/busyindicator-light@3x.webp
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
@@ -52,11 +67,14 @@ qt_internal_add_qml_module(qtquickcontrols2macosstyleplugin
Qt::CorePrivate
Qt::GuiPrivate
Qt::QmlPrivate
+ Qt::QuickControls2MacOSStyleImpl
Qt::QuickControls2Private
Qt::QuickPrivate
Qt::QuickTemplates2Private
)
+add_subdirectory(impl)
+
# Native style is a dependency of the macOS style.
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2macosstyleplugin
qtquickcontrols2nativestyleplugin)
diff --git a/src/quickcontrols/macos/CheckDelegate.qml b/src/quickcontrols/macos/CheckDelegate.qml
new file mode 100644
index 0000000000..6c6d8f14c4
--- /dev/null
+++ b/src/quickcontrols/macos/CheckDelegate.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.NativeStyle as NativeStyle
+
+T.CheckDelegate {
+ id: control
+
+ readonly property bool __nativeIndicator: indicator instanceof NativeStyle.StyleItem
+ readonly property bool __notCustomizable: true
+ readonly property Item __focusFrameTarget: indicator
+ readonly property Item __focusFrameStyleItem: indicator
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ spacing: 6
+ padding: 6
+
+ contentItem: NativeStyle.DefaultItemDelegateIconLabel {
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ indicator: NativeStyle.CheckDelegate {
+ x: control.text ? (control.mirrored ? control.leftPadding : control.width - width - control.rightPadding) : control.leftPadding + (control.availableWidth - width) / 2
+ y: control.topPadding + (control.availableHeight - height) / 2
+ contentWidth: control.implicitContentWidth
+ contentHeight: control.implicitContentHeight
+ useNinePatchImage: false
+ control: control
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 20
+ color: Qt.darker(control.highlighted
+ ? control.palette.highlight : control.palette.button, control.down ? 1.05 : 1)
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+}
diff --git a/src/quickcontrols/macos/DelayButton.qml b/src/quickcontrols/macos/DelayButton.qml
new file mode 100644
index 0000000000..3e0193c2e2
--- /dev/null
+++ b/src/quickcontrols/macos/DelayButton.qml
@@ -0,0 +1,69 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import QtQuick.NativeStyle as NativeStyle
+
+T.DelayButton {
+ id: control
+
+ readonly property bool __nativeBackground: background instanceof NativeStyle.StyleItem
+ readonly property bool __notCustomizable: true
+ readonly property Item __focusFrameTarget: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ leftPadding: __nativeBackground ? background.contentPadding.left : 5
+ rightPadding: __nativeBackground ? background.contentPadding.right : 5
+ topPadding: __nativeBackground ? background.contentPadding.top : 5
+ bottomPadding: __nativeBackground ? background.contentPadding.bottom : 5
+
+ background: NativeStyle.DelayButton {
+ control: control
+ contentWidth: control.contentItem.implicitWidth
+ contentHeight: control.contentItem.implicitHeight
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ icon.width: 24
+ icon.height: 24
+ icon.color: control.palette.buttonText
+
+ transition: Transition {
+ NumberAnimation {
+ duration: control.delay * (control.pressed ? 1.0 - control.progress : 0.3 * control.progress)
+ }
+ }
+
+ contentItem: IconLabel {
+ spacing: control.spacing
+ mirrored: control.mirrored
+ display: control.display
+
+ icon: control.icon
+ text: control.text
+ font: control.font
+ color: control.palette.buttonText
+
+ readonly property bool __ignoreNotCustomizable: true
+
+ // Delay progress bar.
+ Rectangle {
+ x: (parent.width - parent.implicitWidth) / 2
+ y: parent.height + (Qt.styleHints.colorScheme === Qt.Light ? 1 : 0)
+ width: control.progress * parent.implicitWidth
+ // The bar is too thick for the light theme at 2 pixels,
+ // but too thin for the dark theme at 1.
+ height: Qt.styleHints.colorScheme === Qt.Light ? 1 : 2
+ color: control.palette.accent
+ scale: control.mirrored ? -1 : 1
+ }
+ }
+}
diff --git a/src/quickcontrols/macos/Dialog.qml b/src/quickcontrols/macos/Dialog.qml
index effb438733..08154b5504 100644
--- a/src/quickcontrols/macos/Dialog.qml
+++ b/src/quickcontrols/macos/Dialog.qml
@@ -23,15 +23,11 @@ T.Dialog {
padding: 6
- readonly property bool __notCustomizable: true
-
background: Rectangle {
color: control.palette.window
border.color: control.palette.mid
radius: 2
- readonly property bool __ignoreNotCustomizable: true
-
Rectangle {
x: 1
y: 1
@@ -51,8 +47,6 @@ T.Dialog {
font.bold: true
padding: 6
- readonly property bool __ignoreNotCustomizable: true
-
background: Rectangle {
x: 1
y: 1
@@ -65,8 +59,6 @@ T.Dialog {
footer: DialogButtonBox {
visible: count > 0
-
- readonly property bool __ignoreNotCustomizable: true
}
T.Overlay.modal: Rectangle {
diff --git a/src/quickcontrols/macos/DialogButtonBox.qml b/src/quickcontrols/macos/DialogButtonBox.qml
index 6f844dba2f..b99de3354c 100644
--- a/src/quickcontrols/macos/DialogButtonBox.qml
+++ b/src/quickcontrols/macos/DialogButtonBox.qml
@@ -17,8 +17,6 @@ T.DialogButtonBox {
padding: 6
alignment: Qt.AlignRight
- readonly property bool __notCustomizable: true
-
delegate: Button { }
contentItem: ListView {
@@ -28,8 +26,6 @@ T.DialogButtonBox {
orientation: ListView.Horizontal
boundsBehavior: Flickable.StopAtBounds
snapMode: ListView.SnapToItem
-
- readonly property bool __ignoreNotCustomizable: true
}
background: Rectangle {
@@ -40,7 +36,5 @@ T.DialogButtonBox {
height: parent.height - 2
color: control.palette.window
radius: 2
-
- readonly property bool __ignoreNotCustomizable: true
}
}
diff --git a/src/quickcontrols/macos/ItemDelegate.qml b/src/quickcontrols/macos/ItemDelegate.qml
new file mode 100644
index 0000000000..e73f19c12f
--- /dev/null
+++ b/src/quickcontrols/macos/ItemDelegate.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick.NativeStyle as NativeStyle
+
+NativeStyle.DefaultItemDelegate {}
diff --git a/src/quickcontrols/macos/RadioDelegate.qml b/src/quickcontrols/macos/RadioDelegate.qml
new file mode 100644
index 0000000000..d442e7a7dc
--- /dev/null
+++ b/src/quickcontrols/macos/RadioDelegate.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick.NativeStyle as NativeStyle
+
+NativeStyle.DefaultRadioDelegate {}
diff --git a/src/quickcontrols/macos/RangeSlider.qml b/src/quickcontrols/macos/RangeSlider.qml
new file mode 100644
index 0000000000..da3fe83cf2
--- /dev/null
+++ b/src/quickcontrols/macos/RangeSlider.qml
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.Controls.macOS.impl
+
+T.RangeSlider {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ Math.max(first.implicitHandleWidth, second.implicitHandleWidth) + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ Math.max(first.implicitHandleHeight, second.implicitHandleHeight) + topPadding + bottomPadding)
+
+ readonly property bool __notCustomizable: true
+
+ first.handle: SwitchHandle {
+ x: control.leftPadding + Math.round(control.horizontal
+ ? control.first.visualPosition * (control.availableWidth - width)
+ : (control.availableWidth - width) / 2)
+ y: control.topPadding + Math.round(control.horizontal
+ ? (control.availableHeight - height) / 2
+ : control.first.visualPosition * (control.availableHeight - height))
+
+ palette: control.palette
+ down: control.first.pressed
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ second.handle: SwitchHandle {
+ x: control.leftPadding + Math.round(control.horizontal
+ ? control.second.visualPosition * (control.availableWidth - width)
+ : (control.availableWidth - width) / 2)
+ y: control.topPadding + Math.round(control.horizontal
+ ? (control.availableHeight - height) / 2
+ : control.second.visualPosition * (control.availableHeight - height))
+
+ palette: control.palette
+ down: control.second.pressed
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ background: Item {
+ implicitWidth: control.horizontal ? 124 : 24
+ implicitHeight: control.horizontal ? 24 : 124
+
+ readonly property bool __ignoreNotCustomizable: true
+ readonly property int barThickness: 4
+
+ // Groove background.
+ Rectangle {
+ x: control.horizontal ? 0 : (parent.width - width) / 2
+ y: control.horizontal ? (parent.height - height) / 2 : 0
+ width: control.horizontal ? parent.width : parent.barThickness
+ height: control.horizontal ? parent.barThickness : parent.height
+ radius: height / 2
+ color: control.palette.window
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ radius: parent.radius
+ // No border in dark mode, instead we fill.
+ color: Qt.styleHints.colorScheme === Qt.Light
+ ? "transparent" : Qt.lighter(control.palette.window, 1.6)
+ border.color: Qt.styleHints.colorScheme === Qt.Light
+ ? Qt.darker(control.palette.window, 1.1)
+ : "transparent"
+
+ Rectangle {
+ x: 1
+ y: 1
+ width: parent.width - 2
+ height: parent.height - 2
+ radius: parent.radius
+ color: "transparent"
+ border.color: Qt.darker(control.palette.window, 1.05)
+ visible: Qt.styleHints.colorScheme === Qt.Light
+ }
+ }
+ }
+
+ // Progress bar.
+ Rectangle {
+ x: control.horizontal ? control.first.position * parent.width + 3 : (parent.width - width) / 2
+ y: control.horizontal ? (parent.height - height) / 2 : control.second.visualPosition * parent.height + 3
+ width: control.horizontal
+ ? control.second.position * parent.width - control.first.position * parent.width - parent.barThickness
+ : parent.barThickness
+ height: control.horizontal
+ ? parent.barThickness
+ : control.second.position * parent.height - control.first.position * parent.height - parent.barThickness
+ color: control.palette.accent
+ }
+ }
+}
diff --git a/src/quickcontrols/macos/ScrollView.qml b/src/quickcontrols/macos/ScrollView.qml
index 4557da1c0c..86e6bc1a15 100644
--- a/src/quickcontrols/macos/ScrollView.qml
+++ b/src/quickcontrols/macos/ScrollView.qml
@@ -14,8 +14,8 @@ T.ScrollView {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
- rightPadding: ScrollBar.vertical.visible ? ScrollBar.vertical.width : 0
- bottomPadding: ScrollBar.horizontal.visible ? ScrollBar.horizontal.height : 0
+ rightPadding: effectiveScrollBarWidth
+ bottomPadding: effectiveScrollBarHeight
// Don't set __notCustomizable here, because it would require special-casing
// setFlickable's call to setContentItem.
diff --git a/src/quickcontrols/macos/SpinBox.qml b/src/quickcontrols/macos/SpinBox.qml
index f12fc6d2fe..161ddd0231 100644
--- a/src/quickcontrols/macos/SpinBox.qml
+++ b/src/quickcontrols/macos/SpinBox.qml
@@ -58,8 +58,8 @@ T.SpinBox {
}
up.indicator: Item {
- x: parent.width - width
- y: (parent.height / 2) - height
+ x: control.width - width
+ y: (control.height / 2) - height
implicitWidth: upAndDown.width
implicitHeight: upAndDown.height / 2
@@ -67,7 +67,7 @@ T.SpinBox {
}
down.indicator: Item {
- x: parent.width - width
+ x: control.width - width
y: up.indicator.y + upAndDown.height / 2
implicitWidth: upAndDown.width
implicitHeight: upAndDown.height / 2
diff --git a/src/quickcontrols/macos/Switch.qml b/src/quickcontrols/macos/Switch.qml
new file mode 100644
index 0000000000..b96f64bc05
--- /dev/null
+++ b/src/quickcontrols/macos/Switch.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Controls.macOS.impl
+import QtQuick.Templates as T
+
+T.Switch {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ padding: 6
+ spacing: 6
+
+ readonly property Item __focusFrameTarget: indicator
+ readonly property Item __focusFrameStyleItem: indicator
+
+ indicator: SwitchIndicator {
+ x: control.text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
+ y: control.topPadding + (control.availableHeight - height) / 2
+ control: control
+ }
+
+ contentItem: CheckLabel {
+ leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
+ rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
+ text: control.text
+ font: control.font
+ color: control.palette.windowText
+ }
+}
diff --git a/src/quickcontrols/macos/SwitchDelegate.qml b/src/quickcontrols/macos/SwitchDelegate.qml
new file mode 100644
index 0000000000..0dd12ab3b9
--- /dev/null
+++ b/src/quickcontrols/macos/SwitchDelegate.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.Controls.macOS.impl
+
+T.SwitchDelegate {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ padding: 6
+ spacing: 6
+
+ icon.width: 16
+ icon.height: 16
+
+ indicator: SwitchIndicator {
+ x: control.text ? (control.mirrored ? control.leftPadding : control.width - width - control.rightPadding) : control.leftPadding + (control.availableWidth - width) / 2
+ y: control.topPadding + (control.availableHeight - height) / 2
+ control: control
+ }
+
+ contentItem: IconLabel {
+ leftPadding: control.mirrored ? control.indicator.width + control.spacing : 0
+ rightPadding: !control.mirrored ? control.indicator.width + control.spacing : 0
+
+ spacing: control.spacing
+ mirrored: control.mirrored
+ display: control.display
+ alignment: control.display === IconLabel.IconOnly || control.display === IconLabel.TextUnderIcon ? Qt.AlignCenter : Qt.AlignLeft
+
+ icon: control.icon
+ text: control.text
+ font: control.font
+ color: control.palette.text
+ }
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 20
+ color: Qt.darker(control.highlighted ? control.palette.highlight : control.palette.button, control.down ? 1.05 : 1)
+ }
+}
diff --git a/src/quickcontrols/macos/images/busyindicator-dark.webp b/src/quickcontrols/macos/images/busyindicator-dark.webp
new file mode 100644
index 0000000000..9481828b0d
--- /dev/null
+++ b/src/quickcontrols/macos/images/busyindicator-dark.webp
Binary files differ
diff --git a/src/quickcontrols/macos/images/busyindicator-dark@2x.webp b/src/quickcontrols/macos/images/busyindicator-dark@2x.webp
new file mode 100644
index 0000000000..5231b0e373
--- /dev/null
+++ b/src/quickcontrols/macos/images/busyindicator-dark@2x.webp
Binary files differ
diff --git a/src/quickcontrols/macos/images/busyindicator-dark@3x.webp b/src/quickcontrols/macos/images/busyindicator-dark@3x.webp
new file mode 100644
index 0000000000..0346120f4e
--- /dev/null
+++ b/src/quickcontrols/macos/images/busyindicator-dark@3x.webp
Binary files differ
diff --git a/src/quickcontrols/macos/images/busyindicator-light.webp b/src/quickcontrols/macos/images/busyindicator-light.webp
new file mode 100644
index 0000000000..3f19ab63e3
--- /dev/null
+++ b/src/quickcontrols/macos/images/busyindicator-light.webp
Binary files differ
diff --git a/src/quickcontrols/macos/images/busyindicator-light@2x.webp b/src/quickcontrols/macos/images/busyindicator-light@2x.webp
new file mode 100644
index 0000000000..d0481a40a7
--- /dev/null
+++ b/src/quickcontrols/macos/images/busyindicator-light@2x.webp
Binary files differ
diff --git a/src/quickcontrols/macos/images/busyindicator-light@3x.webp b/src/quickcontrols/macos/images/busyindicator-light@3x.webp
new file mode 100644
index 0000000000..4732fe16fd
--- /dev/null
+++ b/src/quickcontrols/macos/images/busyindicator-light@3x.webp
Binary files differ
diff --git a/src/quickcontrols/macos/impl/CMakeLists.txt b/src/quickcontrols/macos/impl/CMakeLists.txt
new file mode 100644
index 0000000000..6d007f4d9f
--- /dev/null
+++ b/src/quickcontrols/macos/impl/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+set(qml_files
+ "SwitchHandle.qml"
+ "SwitchIndicator.qml"
+)
+
+qt_internal_add_qml_module(QuickControls2MacOSStyleImpl
+ URI "QtQuick.Controls.macOS.impl"
+ VERSION "${PROJECT_VERSION}"
+ PAST_MAJOR_VERSIONS 6
+ CLASS_NAME QtQuickControls2macOSStyleImplPlugin
+ DEPENDENCIES
+ QtQuick/auto
+ PLUGIN_TARGET qtquickcontrols2macosstyleimplplugin
+ QML_FILES
+ ${qml_files}
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+)
diff --git a/src/quickcontrols/macos/impl/SwitchHandle.qml b/src/quickcontrols/macos/impl/SwitchHandle.qml
new file mode 100644
index 0000000000..3ef17410e4
--- /dev/null
+++ b/src/quickcontrols/macos/impl/SwitchHandle.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Effects
+
+Rectangle {
+ id: handle
+ width: 20
+ height: 20
+ radius: 10
+ color: Qt.styleHints.colorScheme === Qt.Light
+ ? Qt.darker(palette.base, down ? 1.05 : 1)
+ : Qt.lighter("#cdcbc9", down ? 1.05 : 1)
+
+ required property bool down
+
+ layer.enabled: true
+ layer.effect: MultiEffect {
+ shadowEnabled: true
+ blurMax: 10
+ shadowBlur: 0.2
+ shadowScale: 0.92
+ shadowOpacity: 1
+ }
+}
diff --git a/src/quickcontrols/macos/impl/SwitchIndicator.qml b/src/quickcontrols/macos/impl/SwitchIndicator.qml
new file mode 100644
index 0000000000..33b6f5ca5f
--- /dev/null
+++ b/src/quickcontrols/macos/impl/SwitchIndicator.qml
@@ -0,0 +1,93 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+
+Rectangle {
+ id: indicator
+ implicitWidth: 38
+ implicitHeight: 22
+ radius: implicitHeight / 2
+
+ required property T.AbstractButton control
+ readonly property real downTintFactor: 1.05
+
+ // For QQuickMacFocusFrame.
+ readonly property real __focusFrameRadius: radius
+
+ color: Qt.styleHints.colorScheme === Qt.Light
+ ? Qt.darker(indicator.control.checked
+ ? indicator.palette.accent : "#d9d6d2", indicator.control.down ? indicator.downTintFactor : 1)
+ : Qt.lighter(indicator.control.checked
+ ? indicator.palette.accent : "#454545", indicator.control.down ? indicator.downTintFactor : 1)
+
+ states: [
+ State {
+ name: "checked"
+ when: indicator.control.checked
+
+ // Do a bit of duplication with the bindings here just so that
+ // we can trigger the property change for the transition. We only
+ // the ColorAnimation to happen when changing checked state.
+ PropertyChanges {
+ target: indicator
+ color: Qt.styleHints.colorScheme === Qt.Light
+ ? indicator.control.checked ? indicator.palette.accent : "#d9d6d2"
+ : indicator.control.checked ? indicator.palette.accent : "#454545"
+ }
+ }
+ ]
+
+ transitions: Transition {
+ ColorAnimation {
+ targets: indicator
+ property: "color"
+ // We try to match the speed of x's SmoothedAnimation below,
+ // and 17 pixels (handle travel distance) / 75 pixels a second = 0.226.
+ duration: 226
+ easing.type: Easing.InOutQuad
+ }
+ }
+
+ // Since an equivalent to InnerShadow doesn't exist in Qt 6 (QTBUG-116161),
+ // we approximate it using semi-transparent rectangle borders.
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ radius: height / 2
+ color: "transparent"
+ border.color: Qt.styleHints.colorScheme === Qt.Light
+ ? Qt.darker("#06000000", indicator.control.down ? indicator.downTintFactor : 1)
+ : Qt.lighter("#1affffff", indicator.control.down ? indicator.downTintFactor : 1)
+
+ Rectangle {
+ x: 1
+ y: 1
+ implicitWidth: parent.width - 2
+ implicitHeight: parent.height - 2
+ radius: parent.radius
+ color: "transparent"
+ border.color: Qt.styleHints.colorScheme === Qt.Light
+ ? Qt.darker("#02000000", indicator.control.down ? indicator.downTintFactor : 1)
+ : Qt.lighter("#04ffffff", indicator.control.down ? indicator.downTintFactor : 1)
+ }
+ }
+
+ SwitchHandle {
+ id: handle
+ x: Math.max(1, Math.min(parent.width - width - 1, indicator.control.visualPosition * parent.width - (width / 2)))
+ y: (parent.height - height) / 2
+ down: indicator.control.down
+
+ // We have this here because we don't want this behavior for RangeSlider,
+ // which also uses SwitchHandle.
+ Behavior on x {
+ enabled: !handle.down
+
+ SmoothedAnimation {
+ velocity: 200
+ }
+ }
+ }
+}
diff --git a/src/quickcontrols/material/Button.qml b/src/quickcontrols/material/Button.qml
index e2bbb58687..c02d9f426a 100644
--- a/src/quickcontrols/material/Button.qml
+++ b/src/quickcontrols/material/Button.qml
@@ -17,10 +17,9 @@ T.Button {
topInset: 6
bottomInset: 6
- verticalPadding: 14
- // https://m3.material.io/components/buttons/specs#256326ad-f934-40e7-b05f-0bcb41aa4382
- leftPadding: !flat ? (!hasIcon ? 24 : 16) : 12
- rightPadding: !flat ? 24 : (!hasIcon ? 12 : 16)
+ verticalPadding: Material.buttonVerticalPadding
+ leftPadding: Material.buttonLeftPadding(flat, hasIcon)
+ rightPadding: Material.buttonRightPadding(flat, hasIcon, text !== "")
spacing: 8
icon.width: 24
diff --git a/src/quickcontrols/material/CMakeLists.txt b/src/quickcontrols/material/CMakeLists.txt
index f328e775c3..a427b3711c 100644
--- a/src/quickcontrols/material/CMakeLists.txt
+++ b/src/quickcontrols/material/CMakeLists.txt
@@ -94,7 +94,9 @@ set_source_files_properties(VerticalHeaderView.qml PROPERTIES
QT_QML_SOURCE_VERSIONS "2.15;6.0"
)
-qt_internal_add_qml_module(qtquickcontrols2materialstyleplugin
+add_subdirectory(impl)
+
+qt_internal_add_qml_module(QuickControls2Material
URI "QtQuick.Controls.Material"
VERSION "${PROJECT_VERSION}"
PAST_MAJOR_VERSIONS 2
@@ -109,7 +111,6 @@ qt_internal_add_qml_module(qtquickcontrols2materialstyleplugin
SOURCES
qquickmaterialstyle.cpp qquickmaterialstyle_p.h
qquickmaterialtheme.cpp qquickmaterialtheme_p.h
- qtquickcontrols2materialstyleplugin.cpp
QML_FILES
${qml_files}
DEFINES
@@ -120,12 +121,24 @@ qt_internal_add_qml_module(qtquickcontrols2materialstyleplugin
Qt::GuiPrivate
Qt::QmlPrivate
Qt::QuickControls2ImplPrivate
+ Qt::QuickControls2MaterialStyleImpl
Qt::QuickControls2Private
Qt::QuickPrivate
Qt::QuickTemplates2Private
+ GENERATE_CPP_EXPORTS
+)
+
+target_sources(qtquickcontrols2materialstyleplugin
+ PRIVATE
+ qtquickcontrols2materialstyleplugin.cpp
+)
+
+target_link_libraries(qtquickcontrols2materialstyleplugin
+ PRIVATE
+ Qt::QuickControls2Private
)
-qt_internal_add_resource(qtquickcontrols2materialstyleplugin "qtquickcontrols2materialstyleplugin"
+qt_internal_add_resource(QuickControls2Material "qtquickcontrols2materialstyle"
PREFIX
"/qt-project.org/imports/QtQuick/Controls/Material"
FILES
@@ -141,13 +154,18 @@ qt_internal_add_resource(qtquickcontrols2materialstyleplugin "qtquickcontrols2ma
"images/drop-indicator@2x.png"
"images/drop-indicator@3x.png"
"images/drop-indicator@4x.png"
- "shaders/+glslcore/RectangularGlow.frag"
- "shaders/+hlsl/RectangularGlow.frag"
- "shaders/+qsb/RectangularGlow.frag"
- "shaders/RectangularGlow.frag"
)
-add_subdirectory(impl)
+qt_internal_add_shaders(QuickControls2Material "qtquickcontrols2materialstyle_shaders"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ "/qt-project.org/imports/QtQuick/Controls/Material"
+ FILES
+ "shaders/RectangularGlow.frag"
+)
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2materialstyleplugin quickwindow)
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2materialstyleplugin
diff --git a/src/quickcontrols/material/ComboBox.qml b/src/quickcontrols/material/ComboBox.qml
index 30222c4180..5694aa055c 100644
--- a/src/quickcontrols/material/ComboBox.qml
+++ b/src/quickcontrols/material/ComboBox.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Window
import QtQuick.Controls.impl
@@ -17,20 +19,18 @@ T.ComboBox {
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
- topInset: 6
- bottomInset: 6
-
leftPadding: padding + (!control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
rightPadding: padding + (control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
- Material.elevation: flat ? control.pressed || (enabled && control.hovered) ? 2 : 0
- : control.pressed ? 8 : 2
Material.background: flat ? "transparent" : undefined
Material.foreground: flat ? undefined : Material.primaryTextColor
delegate: MenuItem {
+ required property var model
+ required property int index
+
width: ListView.view.width
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ text: model[control.textRole]
Material.foreground: control.currentIndex === index ? ListView.view.contentItem.Material.accent : ListView.view.contentItem.Material.foreground
highlighted: control.highlightedIndex === index
hoverEnabled: control.hoverEnabled
@@ -44,9 +44,9 @@ T.ComboBox {
}
contentItem: T.TextField {
- padding: 6
- leftPadding: control.editable ? 2 : control.mirrored ? 0 : 12
- rightPadding: control.editable ? 2 : control.mirrored ? 12 : 0
+ leftPadding: Material.textFieldHorizontalPadding
+ topPadding: Material.textFieldVerticalPadding
+ bottomPadding: Material.textFieldVerticalPadding
text: control.editable ? control.editText : control.displayText
@@ -65,46 +65,25 @@ T.ComboBox {
cursorDelegate: CursorDelegate { }
}
- background: Rectangle {
+ background: MaterialTextContainer {
implicitWidth: 120
- implicitHeight: control.Material.buttonHeight
+ implicitHeight: control.Material.textFieldHeight
- radius: control.flat ? 0 : 2
- color: !control.editable ? control.Material.dialogColor : "transparent"
-
- layer.enabled: control.enabled && !control.editable && control.Material.background.a > 0
- layer.effect: ElevationEffect {
- elevation: control.Material.elevation
- }
-
- Rectangle {
- visible: control.editable
- y: parent.y + control.baselineOffset
- width: parent.width
- height: control.activeFocus ? 2 : 1
- color: control.editable && control.activeFocus ? control.Material.accentColor : control.Material.hintTextColor
- }
-
- Ripple {
- clip: control.flat
- clipRadius: control.flat ? 0 : 2
- x: control.editable && control.indicator ? control.indicator.x : 0
- width: control.editable && control.indicator ? control.indicator.width : parent.width
- height: parent.height
- pressed: control.pressed
- anchor: control.editable && control.indicator ? control.indicator : control
- active: enabled && (control.pressed || control.visualFocus || control.hovered)
- color: control.Material.rippleColor
- }
+ outlineColor: (enabled && control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor
+ focusedOutlineColor: control.Material.accentColor
+ controlHasActiveFocus: control.activeFocus
+ controlHasText: true
+ horizontalPadding: control.Material.textFieldHorizontalPadding
}
popup: T.Popup {
y: control.editable ? control.height - 5 : 0
width: control.width
- height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin)
+ height: Math.min(contentItem.implicitHeight + verticalPadding * 2, control.Window.height - topMargin - bottomMargin)
transformOrigin: Item.Top
topMargin: 12
bottomMargin: 12
+ verticalPadding: 8
Material.theme: control.Material.theme
Material.accent: control.Material.accent
@@ -133,12 +112,13 @@ T.ComboBox {
}
background: Rectangle {
- radius: 2
+ radius: 4
color: parent.Material.dialogColor
layer.enabled: control.enabled
- layer.effect: ElevationEffect {
- elevation: 8
+ layer.effect: RoundedElevationEffect {
+ elevation: 4
+ roundedScale: Material.ExtraSmallScale
}
}
}
diff --git a/src/quickcontrols/material/Dial.qml b/src/quickcontrols/material/Dial.qml
index a44938fe20..465c1f3631 100644
--- a/src/quickcontrols/material/Dial.qml
+++ b/src/quickcontrols/material/Dial.qml
@@ -33,12 +33,13 @@ T.Dial {
y: control.background.y + control.background.height / 2 - height / 2
transform: [
Translate {
- y: -control.background.height * 0.4 + control.handle.height / 2
+ y: -control.background.height * 0.4
+ + (control.handle ? control.handle.height / 2 : 0)
},
Rotation {
angle: control.angle
- origin.x: control.handle.width / 2
- origin.y: control.handle.height / 2
+ origin.x: control.handle ? control.handle.width / 2 : 0
+ origin.y: control.handle ? control.handle.height / 2 : 0
}
]
implicitWidth: 10
diff --git a/src/quickcontrols/material/Dialog.qml b/src/quickcontrols/material/Dialog.qml
index 33d8ce97d1..014fcc67c5 100644
--- a/src/quickcontrols/material/Dialog.qml
+++ b/src/quickcontrols/material/Dialog.qml
@@ -28,8 +28,7 @@ T.Dialog {
// https://m3.material.io/components/dialogs/specs#401a48c3-f50c-4fa9-b798-701f5adcf155
// Specs say level 3 (6 dp) is the default, yet the screenshots there show 0. Native Android defaults to non-zero.
Material.elevation: 6
- // https://m3.material.io/components/dialogs/specs#6771d107-624e-47cc-b6d8-2b7b620ba2f1
- Material.roundedScale: Material.ExtraLargeScale
+ Material.roundedScale: Material.dialogRoundedScale
enter: Transition {
// grow_fade_in
@@ -63,7 +62,7 @@ T.Dialog {
bottomPadding: 0
// TODO: QPlatformTheme::TitleBarFont
// https://m3.material.io/components/dialogs/specs#401a48c3-f50c-4fa9-b798-701f5adcf155
- font.pixelSize: 24
+ font.pixelSize: Material.dialogTitleFontPixelSize
background: PaddedRectangle {
radius: control.background.radius
color: control.Material.dialogColor
diff --git a/src/quickcontrols/material/Drawer.qml b/src/quickcontrols/material/Drawer.qml
index ecfe8836b9..fc31a19a84 100644
--- a/src/quickcontrols/material/Drawer.qml
+++ b/src/quickcontrols/material/Drawer.qml
@@ -3,6 +3,7 @@
import QtQuick
import QtQuick.Templates as T
+import QtQuick.Controls.impl
import QtQuick.Controls.Material
import QtQuick.Controls.Material.impl
@@ -16,33 +17,32 @@ T.Drawer {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
- topPadding: !dim && edge === Qt.BottomEdge && Material.elevation === 0
- leftPadding: !dim && edge === Qt.RightEdge && Material.elevation === 0
- rightPadding: !dim && edge === Qt.LeftEdge && Material.elevation === 0
- bottomPadding: !dim && edge === Qt.TopEdge && Material.elevation === 0
+ topPadding: edge !== Qt.TopEdge ? Material.roundedScale : 0
+ bottomPadding: edge !== Qt.BottomEdge ? Material.roundedScale : 0
enter: Transition { SmoothedAnimation { velocity: 5 } }
exit: Transition { SmoothedAnimation { velocity: 5 } }
- Material.elevation: !interactive && !dim ? 0 : 16
+ // https://m3.material.io/components/navigation-drawer/specs#e616dc8f-d61a-4d56-a311-50c68ecda744
+ Material.elevation: !interactive && !dim ? 0 : 1
+ Material.roundedScale: Material.LargeScale
- background: Rectangle {
+ background: PaddedRectangle {
+ // https://m3.material.io/components/navigation-drawer/specs#ce8bfbcf-3dec-45d2-9d8b-5e10af1cf87d
+ implicitWidth: 360
color: control.Material.dialogColor
-
- Rectangle {
- readonly property bool horizontal: control.edge === Qt.LeftEdge || control.edge === Qt.RightEdge
- width: horizontal ? 1 : parent.width
- height: horizontal ? parent.height : 1
- color: control.Material.dividerColor
- x: control.edge === Qt.LeftEdge ? parent.width - 1 : 0
- y: control.edge === Qt.TopEdge ? parent.height - 1 : 0
- visible: !control.dim && control.Material.elevation === 0
- }
-
- layer.enabled: control.position > 0
- layer.effect: ElevationEffect {
+ // FullScale doesn't make sense for Drawer.
+ radius: control.Material.roundedScale
+ leftPadding: edge === Qt.LeftEdge ? -radius : 0
+ rightPadding: edge === Qt.RightEdge ? -radius : 0
+ topPadding: edge === Qt.TopEdge ? -radius : 0
+ bottomPadding: edge === Qt.BottomEdge ? -radius : 0
+ clip: true
+
+ layer.enabled: control.position > 0 && control.Material.elevation > 0
+ layer.effect: RoundedElevationEffect {
elevation: control.Material.elevation
- fullHeight: true
+ roundedScale: control.background.radius
}
}
diff --git a/src/quickcontrols/material/Frame.qml b/src/quickcontrols/material/Frame.qml
index 8b6674e16f..da9cd65581 100644
--- a/src/quickcontrols/material/Frame.qml
+++ b/src/quickcontrols/material/Frame.qml
@@ -17,14 +17,17 @@ T.Frame {
padding: 12
verticalPadding: Material.frameVerticalPadding
+ Material.roundedScale: Material.ExtraSmallScale
+
background: Rectangle {
- radius: 2
+ radius: control.Material.roundedScale
color: control.Material.elevation > 0 ? control.Material.backgroundColor : "transparent"
border.color: control.Material.frameColor
layer.enabled: control.enabled && control.Material.elevation > 0
- layer.effect: ElevationEffect {
+ layer.effect: RoundedElevationEffect {
elevation: control.Material.elevation
+ roundedScale: control.background.radius
}
}
}
diff --git a/src/quickcontrols/material/GroupBox.qml b/src/quickcontrols/material/GroupBox.qml
index be7e6fbadc..f0cee7c854 100644
--- a/src/quickcontrols/material/GroupBox.qml
+++ b/src/quickcontrols/material/GroupBox.qml
@@ -20,8 +20,10 @@ T.GroupBox {
topPadding: Material.frameVerticalPadding + (implicitLabelWidth > 0 ? implicitLabelHeight + spacing : 0)
bottomPadding: Material.frameVerticalPadding
+ Material.roundedScale: Material.ExtraSmallScale
+
label: Text {
- x: control.leftPadding
+ x: Math.max(control.leftPadding, control.Material.roundedScale)
width: control.availableWidth
text: control.title
@@ -36,13 +38,14 @@ T.GroupBox {
width: parent.width
height: parent.height - control.topPadding + control.bottomPadding
- radius: 2
+ radius: control.Material.roundedScale
color: control.Material.elevation > 0 ? control.Material.backgroundColor : "transparent"
border.color: control.Material.frameColor
layer.enabled: control.enabled && control.Material.elevation > 0
- layer.effect: ElevationEffect {
+ layer.effect: RoundedElevationEffect {
elevation: control.Material.elevation
+ roundedScale: control.background.radius
}
}
}
diff --git a/src/quickcontrols/material/HorizontalHeaderView.qml b/src/quickcontrols/material/HorizontalHeaderView.qml
index 0a5fe4f9f5..76060d8e83 100644
--- a/src/quickcontrols/material/HorizontalHeaderView.qml
+++ b/src/quickcontrols/material/HorizontalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
import QtQuick.Controls.Material
@@ -18,6 +20,10 @@ T.HorizontalHeaderView {
implicitHeight: Math.max(1, contentHeight)
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -27,11 +33,9 @@ T.HorizontalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: enabled ? control.Material.foreground : control.Material.hintTextColor
diff --git a/src/quickcontrols/material/Menu.qml b/src/quickcontrols/material/Menu.qml
index 4601772196..641bb7c07b 100644
--- a/src/quickcontrols/material/Menu.qml
+++ b/src/quickcontrols/material/Menu.qml
@@ -10,8 +10,6 @@ import QtQuick.Window
T.Menu {
id: control
- Material.elevation: 8
-
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
@@ -22,6 +20,9 @@ T.Menu {
transformOrigin: !cascade ? Item.Top : (mirrored ? Item.TopRight : Item.TopLeft)
+ Material.elevation: 4
+ Material.roundedScale: Material.ExtraSmallScale
+
delegate: MenuItem { }
enter: Transition {
@@ -41,8 +42,8 @@ T.Menu {
model: control.contentModel
interactive: Window.window
- ? contentHeight + control.topPadding + control.bottomPadding > Window.window.height
- : false
+ ? contentHeight + control.topPadding + control.bottomPadding > control.height
+ : false
clip: true
currentIndex: control.currentIndex
@@ -52,13 +53,14 @@ T.Menu {
background: Rectangle {
implicitWidth: 200
implicitHeight: control.Material.menuItemHeight
-
- radius: 3
+ // FullScale doesn't make sense for Menu.
+ radius: control.Material.roundedScale
color: control.Material.dialogColor
layer.enabled: control.Material.elevation > 0
- layer.effect: ElevationEffect {
+ layer.effect: RoundedElevationEffect {
elevation: control.Material.elevation
+ roundedScale: control.background.radius
}
}
diff --git a/src/quickcontrols/material/Page.qml b/src/quickcontrols/material/Page.qml
index f8b97a2d3e..f8a1804f32 100644
--- a/src/quickcontrols/material/Page.qml
+++ b/src/quickcontrols/material/Page.qml
@@ -4,6 +4,7 @@
import QtQuick
import QtQuick.Templates as T
import QtQuick.Controls.Material
+import QtQuick.Controls.Material.impl
T.Page {
id: control
@@ -19,5 +20,10 @@ T.Page {
background: Rectangle {
color: control.Material.backgroundColor
+
+ layer.enabled: control.enabled && control.Material.elevation > 0
+ layer.effect: ElevationEffect {
+ elevation: control.Material.elevation
+ }
}
}
diff --git a/src/quickcontrols/material/Pane.qml b/src/quickcontrols/material/Pane.qml
index a6e0ae5b33..80385a073f 100644
--- a/src/quickcontrols/material/Pane.qml
+++ b/src/quickcontrols/material/Pane.qml
@@ -15,14 +15,16 @@ T.Pane {
contentHeight + topPadding + bottomPadding)
padding: 12
+ Material.roundedScale: control.Material.elevation > 0 ? Material.ExtraSmallScale : Material.NotRounded
background: Rectangle {
color: control.Material.backgroundColor
- radius: control.Material.elevation > 0 ? 2 : 0
+ radius: control.Material.roundedScale
layer.enabled: control.enabled && control.Material.elevation > 0
- layer.effect: ElevationEffect {
+ layer.effect: RoundedElevationEffect {
elevation: control.Material.elevation
+ roundedScale: control.background.radius
}
}
}
diff --git a/src/quickcontrols/material/Popup.qml b/src/quickcontrols/material/Popup.qml
index 7727ef0d3c..e443a1c2ff 100644
--- a/src/quickcontrols/material/Popup.qml
+++ b/src/quickcontrols/material/Popup.qml
@@ -9,8 +9,6 @@ import QtQuick.Controls.Material.impl
T.Popup {
id: control
- Material.elevation: 24
-
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
@@ -18,6 +16,9 @@ T.Popup {
padding: 12
+ Material.elevation: 4
+ Material.roundedScale: Material.ExtraSmallScale
+
enter: Transition {
// grow_fade_in
NumberAnimation { property: "scale"; from: 0.9; to: 1.0; easing.type: Easing.OutQuint; duration: 220 }
@@ -31,12 +32,14 @@ T.Popup {
}
background: Rectangle {
- radius: 2
+ // FullScale doesn't make sense for Popup.
+ radius: control.Material.roundedScale
color: control.Material.dialogColor
layer.enabled: control.Material.elevation > 0
- layer.effect: ElevationEffect {
+ layer.effect: RoundedElevationEffect {
elevation: control.Material.elevation
+ roundedScale: control.Material.roundedScale
}
}
diff --git a/src/quickcontrols/material/RangeSlider.qml b/src/quickcontrols/material/RangeSlider.qml
index 81507f334d..7547d3d04f 100644
--- a/src/quickcontrols/material/RangeSlider.qml
+++ b/src/quickcontrols/material/RangeSlider.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import QtQuick
@@ -19,6 +19,17 @@ T.RangeSlider {
padding: 6
+ // The RangeSlider is discrete if all of the following requirements are met:
+ // * stepSize is positive
+ // * snapMode is set to SnapAlways
+ // * the difference between to and from is cleanly divisible by the stepSize
+ // * the number of tick marks intended to be rendered is less than the width to height ratio, or vice versa for vertical sliders.
+ readonly property real __steps: Math.abs(to - from) / stepSize
+ readonly property bool __isDiscrete: stepSize >= Number.EPSILON
+ && snapMode === Slider.SnapAlways
+ && Math.abs(Math.round(__steps) - __steps) < Number.EPSILON
+ && Math.floor(__steps) < (horizontal ? background.width / background.height : background.height / background.width)
+
first.handle: SliderHandle {
x: control.leftPadding + (control.horizontal ? control.first.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.first.visualPosition * (control.availableHeight - height))
@@ -37,23 +48,51 @@ T.RangeSlider {
handleHovered: control.second.hovered
}
- background: Rectangle {
+ background: Item {
x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0)
implicitWidth: control.horizontal ? 200 : 48
implicitHeight: control.horizontal ? 48 : 200
width: control.horizontal ? control.availableWidth : 4
height: control.horizontal ? 4 : control.availableHeight
- scale: control.horizontal && control.mirrored ? -1 : 1
- color: control.enabled ? Color.transparent(control.Material.accentColor, 0.33) : control.Material.sliderDisabledColor
Rectangle {
- x: control.horizontal ? control.first.position * parent.width : 0
- y: control.horizontal ? 0 : control.second.visualPosition * parent.height
- width: control.horizontal ? control.second.position * parent.width - control.first.position * parent.width : 4
- height: control.horizontal ? 4 : control.second.position * parent.height - control.first.position * parent.height
+ x: (control.horizontal ? (control.first.implicitHandleWidth / 2) - (control.__isDiscrete ? 2 : 0) : 0)
+ y: (control.horizontal ? 0 : (control.first.implicitHandleHeight / 2) - (control.__isDiscrete ? 2 : 0))
+ width: parent.width - (control.horizontal ? (control.first.implicitHandleWidth - (control.__isDiscrete ? 4 : 0)) : 0)
+ height: parent.height - (control.horizontal ? 0 : (control.first.implicitHandleHeight - (control.__isDiscrete ? 4 : 0)))
+ scale: control.horizontal && control.mirrored ? -1 : 1
+ radius: Math.min(width, height) / 2
+ color: control.enabled ? Color.transparent(control.Material.accentColor, 0.33) : control.Material.sliderDisabledColor
+
+ Rectangle {
+ x: control.horizontal ? control.first.position * parent.width : 0
+ y: control.horizontal ? 0 : control.second.visualPosition * parent.height
+ width: control.horizontal ? control.second.position * parent.width - control.first.position * parent.width : 4
+ height: control.horizontal ? 4 : control.second.position * parent.height - control.first.position * parent.height
+ radius: Math.min(width, height) / 2
+ color: control.enabled ? control.Material.accentColor : control.Material.sliderDisabledColor
+ }
+
+ // Declaring this as a property (in combination with the parent binding below) avoids ids,
+ // which prevent deferred execution.
+ property Repeater repeater: Repeater {
+ parent: control.background.children[0]
+ model: control.__isDiscrete ? Math.floor(control.__steps) + 1 : 0
+ delegate: Rectangle {
+ width: 2
+ height: 2
+ radius: 2
+ x: control.horizontal ? (parent.width - width * 2) * currentPosition + (width / 2) : (parent.width - width) / 2
+ y: control.horizontal ? (parent.height - height) / 2 : (parent.height - height * 2) * currentPosition + (height / 2)
+ color: (control.horizontal && control.first.visualPosition < currentPosition && control.second.visualPosition > currentPosition)
+ || (!control.horizontal && control.first.visualPosition > currentPosition && control.second.visualPosition < currentPosition)
+ ? control.Material.primaryHighlightedTextColor : control.Material.accentColor
- color: control.enabled ? control.Material.accentColor : control.Material.sliderDisabledColor
+ required property int index
+ readonly property real currentPosition: index / (parent.repeater.count - 1)
+ }
+ }
}
}
}
diff --git a/src/quickcontrols/material/Slider.qml b/src/quickcontrols/material/Slider.qml
index 5061ecf03f..817b3022aa 100644
--- a/src/quickcontrols/material/Slider.qml
+++ b/src/quickcontrols/material/Slider.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import QtQuick
@@ -17,6 +17,17 @@ T.Slider {
padding: 6
+ // The Slider is discrete if all of the following requirements are met:
+ // * stepSize is positive
+ // * snapMode is set to SnapAlways
+ // * the difference between to and from is cleanly divisible by the stepSize
+ // * the number of tick marks intended to be rendered is less than the width to height ratio, or vice versa for vertical sliders.
+ readonly property real __steps: Math.abs(to - from) / stepSize
+ readonly property bool __isDiscrete: stepSize >= Number.EPSILON
+ && snapMode === Slider.SnapAlways
+ && Math.abs(Math.round(__steps) - __steps) < Number.EPSILON
+ && Math.floor(__steps) < (horizontal ? background.width / background.height : background.height / background.width)
+
handle: SliderHandle {
x: control.leftPadding + (control.horizontal ? control.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.visualPosition * (control.availableHeight - height))
@@ -26,23 +37,51 @@ T.Slider {
handleHovered: control.hovered
}
- background: Rectangle {
+ background: Item {
x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0)
implicitWidth: control.horizontal ? 200 : 48
implicitHeight: control.horizontal ? 48 : 200
width: control.horizontal ? control.availableWidth : 4
height: control.horizontal ? 4 : control.availableHeight
- scale: control.horizontal && control.mirrored ? -1 : 1
- color: control.enabled ? Color.transparent(control.Material.accentColor, 0.33) : control.Material.sliderDisabledColor
Rectangle {
- x: control.horizontal ? 0 : (parent.width - width) / 2
- y: control.horizontal ? (parent.height - height) / 2 : control.visualPosition * parent.height
- width: control.horizontal ? control.position * parent.width : 4
- height: control.horizontal ? 4 : control.position * parent.height
+ x: (control.horizontal ? (control.implicitHandleWidth / 2) - (control.__isDiscrete ? 2 : 0) : 0)
+ y: (control.horizontal ? 0 : (control.implicitHandleHeight / 2) - (control.__isDiscrete ? 2 : 0))
+ width: parent.width - (control.horizontal ? (control.implicitHandleWidth - (control.__isDiscrete ? 4 : 0)) : 0)
+ height: parent.height - (control.horizontal ? 0 : (control.implicitHandleHeight - (control.__isDiscrete ? 4 : 0)))
+ scale: control.horizontal && control.mirrored ? -1 : 1
+ radius: Math.min(width, height) / 2
+ color: control.enabled ? Color.transparent(control.Material.accentColor, 0.33) : control.Material.sliderDisabledColor
+
+ Rectangle {
+ x: control.horizontal ? 0 : (parent.width - width) / 2
+ y: control.horizontal ? (parent.height - height) / 2 : control.visualPosition * parent.height
+ width: control.horizontal ? control.position * parent.width : 4
+ height: control.horizontal ? 4 : control.position * parent.height
+ radius: Math.min(width, height) / 2
+ color: control.enabled ? control.Material.accentColor : control.Material.sliderDisabledColor
+ }
+
+ // Declaring this as a property (in combination with the parent binding below) avoids ids,
+ // which prevent deferred execution.
+ property Repeater repeater: Repeater {
+ parent: control.background.children[0]
+ model: control.__isDiscrete ? Math.floor(control.__steps) + 1 : 0
+ delegate: Rectangle {
+ width: 2
+ height: 2
+ radius: 2
+ x: control.horizontal ? (parent.width - width * 2) * currentPosition + (width / 2) : (parent.width - width) / 2
+ y: control.horizontal ? (parent.height - height) / 2 : (parent.height - height * 2) * currentPosition + (height / 2)
+ color: (control.horizontal && control.visualPosition > currentPosition)
+ || (!control.horizontal && control.visualPosition <= currentPosition)
+ ? control.Material.primaryHighlightedTextColor : control.Material.accentColor
- color: control.enabled ? control.Material.accentColor : control.Material.sliderDisabledColor
+ required property int index
+ readonly property real currentPosition: index / (parent.repeater.count - 1)
+ }
+ }
}
}
}
diff --git a/src/quickcontrols/material/SpinBox.qml b/src/quickcontrols/material/SpinBox.qml
index 591f5061fa..44fd59f17f 100644
--- a/src/quickcontrols/material/SpinBox.qml
+++ b/src/quickcontrols/material/SpinBox.qml
@@ -17,8 +17,8 @@ T.SpinBox {
up.implicitIndicatorHeight, down.implicitIndicatorHeight)
spacing: 6
- topPadding: 8
- bottomPadding: 16
+ topPadding: Material.textFieldVerticalPadding
+ bottomPadding: Material.textFieldVerticalPadding
leftPadding: control.mirrored ? (up.indicator ? up.indicator.width : 0) : (down.indicator ? down.indicator.width : 0)
rightPadding: control.mirrored ? (down.indicator ? down.indicator.width : 0) : (up.indicator ? up.indicator.width : 0)
@@ -107,16 +107,14 @@ T.SpinBox {
}
}
- background: Item {
- implicitWidth: 192
- implicitHeight: control.Material.touchTarget
+ background: MaterialTextContainer {
+ implicitWidth: 140
+ implicitHeight: control.Material.textFieldHeight
- Rectangle {
- x: parent.width / 2 - width / 2
- y: parent.y + parent.height - height - control.bottomPadding / 2
- width: control.availableWidth
- height: control.activeFocus ? 2 : 1
- color: control.activeFocus ? control.Material.accentColor : control.Material.hintTextColor
- }
+ outlineColor: (enabled && control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor
+ focusedOutlineColor: control.Material.accentColor
+ controlHasActiveFocus: control.activeFocus
+ controlHasText: true
+ horizontalPadding: control.Material.textFieldHorizontalPadding
}
}
diff --git a/src/quickcontrols/material/StackView.qml b/src/quickcontrols/material/StackView.qml
index 241b91f2d7..c3182442a2 100644
--- a/src/quickcontrols/material/StackView.qml
+++ b/src/quickcontrols/material/StackView.qml
@@ -8,39 +8,56 @@ import QtQuick.Controls.Material
T.StackView {
id: control
+ component LineAnimation: NumberAnimation {
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+
+ component FadeIn: LineAnimation {
+ property: "opacity"
+ from: 0.0
+ to: 1.0
+ }
+
+ component FadeOut: LineAnimation {
+ property: "opacity"
+ from: 1.0
+ to: 0.0
+ }
+
popEnter: Transition {
// slide_in_left
- NumberAnimation { property: "x"; from: (control.mirrored ? -0.5 : 0.5) * -control.width; to: 0; duration: 200; easing.type: Easing.OutCubic }
- NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 200; easing.type: Easing.OutCubic }
+ LineAnimation { property: "x"; from: (control.mirrored ? -0.5 : 0.5) * -control.width; to: 0 }
+ FadeIn {}
}
popExit: Transition {
// slide_out_right
- NumberAnimation { property: "x"; from: 0; to: (control.mirrored ? -0.5 : 0.5) * control.width; duration: 200; easing.type: Easing.OutCubic }
- NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 200; easing.type: Easing.OutCubic }
+ LineAnimation { property: "x"; from: 0; to: (control.mirrored ? -0.5 : 0.5) * control.width }
+ FadeOut {}
}
pushEnter: Transition {
// slide_in_right
- NumberAnimation { property: "x"; from: (control.mirrored ? -0.5 : 0.5) * control.width; to: 0; duration: 200; easing.type: Easing.OutCubic }
- NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 200; easing.type: Easing.OutCubic }
+ LineAnimation { property: "x"; from: (control.mirrored ? -0.5 : 0.5) * control.width; to: 0 }
+ FadeIn {}
}
pushExit: Transition {
// slide_out_left
- NumberAnimation { property: "x"; from: 0; to: (control.mirrored ? -0.5 : 0.5) * -control.width; duration: 200; easing.type: Easing.OutCubic }
- NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 200; easing.type: Easing.OutCubic }
+ LineAnimation { property: "x"; from: 0; to: (control.mirrored ? -0.5 : 0.5) * -control.width }
+ FadeOut {}
}
replaceEnter: Transition {
// slide_in_right
- NumberAnimation { property: "x"; from: (control.mirrored ? -0.5 : 0.5) * control.width; to: 0; duration: 200; easing.type: Easing.OutCubic }
- NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 200; easing.type: Easing.OutCubic }
+ LineAnimation { property: "x"; from: (control.mirrored ? -0.5 : 0.5) * control.width; to: 0 }
+ FadeIn {}
}
replaceExit: Transition {
// slide_out_left
- NumberAnimation { property: "x"; from: 0; to: (control.mirrored ? -0.5 : 0.5) * -control.width; duration: 200; easing.type: Easing.OutCubic }
- NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 200; easing.type: Easing.OutCubic }
+ LineAnimation { property: "x"; from: 0; to: (control.mirrored ? -0.5 : 0.5) * -control.width }
+ FadeOut {}
}
}
diff --git a/src/quickcontrols/material/Switch.qml b/src/quickcontrols/material/Switch.qml
index d27621a74f..29a1297684 100644
--- a/src/quickcontrols/material/Switch.qml
+++ b/src/quickcontrols/material/Switch.qml
@@ -34,7 +34,8 @@ T.Switch {
Ripple {
x: parent.handle.x + parent.handle.width / 2 - width / 2
y: parent.handle.y + parent.handle.height / 2 - height / 2
- width: 28; height: 28
+ width: 28
+ height: 28
pressed: control.pressed
active: enabled && (control.down || control.visualFocus || control.hovered)
color: control.checked ? control.Material.highlightedRippleColor : control.Material.rippleColor
diff --git a/src/quickcontrols/material/TextArea.qml b/src/quickcontrols/material/TextArea.qml
index 242cbf691b..99efa222cf 100644
--- a/src/quickcontrols/material/TextArea.qml
+++ b/src/quickcontrols/material/TextArea.qml
@@ -14,37 +14,70 @@ T.TextArea {
implicitBackgroundWidth + leftInset + rightInset,
placeholder.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(contentHeight + topPadding + bottomPadding,
- implicitBackgroundHeight + topInset + bottomInset,
- placeholder.implicitHeight + 1 + topPadding + bottomPadding)
+ implicitBackgroundHeight + topInset + bottomInset)
- topPadding: 8
- bottomPadding: 16
+ // If we're clipped, or we're in a Flickable that's clipped, set our topInset
+ // to half the height of the placeholder text to avoid it being clipped.
+ topInset: clip || (parent?.parent as Flickable && parent?.parent.clip) ? placeholder.largestHeight / 2 : 0
+
+ leftPadding: Material.textFieldHorizontalPadding
+ rightPadding: Material.textFieldHorizontalPadding
+ // Need to account for the placeholder text when it's sitting on top.
+ topPadding: Material.containerStyle === Material.Filled && placeholderText.length > 0 && (activeFocus || length > 0)
+ ? Material.textFieldVerticalPadding + placeholder.largestHeight
+ // When the condition above is not met, the text should always sit in the middle
+ // of a default-height TextArea, which is just near the top for a higher-than-default one.
+ // Account for any topInset as well, otherwise the text will be too close to the background.
+ : ((implicitBackgroundHeight - placeholder.largestHeight) / 2) + topInset
+ bottomPadding: Material.textFieldVerticalPadding
color: enabled ? Material.foreground : Material.hintTextColor
selectionColor: Material.accentColor
selectedTextColor: Material.primaryHighlightedTextColor
- placeholderTextColor: Material.hintTextColor
+ placeholderTextColor: enabled && activeFocus ? Material.accentColor : Material.hintTextColor
+
+ Material.containerStyle: Material.Outlined
+
cursorDelegate: CursorDelegate { }
- PlaceholderText {
+ FloatingPlaceholderText {
id: placeholder
- x: control.leftPadding
- y: control.topPadding
+ // Don't set this to control.leftPadding, because we don't want it to change if the user changes leftPadding.
+ x: control.Material.textFieldHorizontalPadding
width: control.width - (control.leftPadding + control.rightPadding)
- height: control.height - (control.topPadding + control.bottomPadding)
text: control.placeholderText
font: control.font
color: control.placeholderTextColor
- verticalAlignment: control.verticalAlignment
elide: Text.ElideRight
renderType: control.renderType
- visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
+ // When the TextArea is in a Flickable, the background is reparented to it
+ // so that decorations don't move with the content. We need to do the same.
+ // Also allow the background to be set to null; in that case we're just not visible.
+ parent: control.background?.parent ?? null
+
+ filled: control.Material.containerStyle === Material.Filled
+ verticalPadding: control.Material.textFieldVerticalPadding
+ controlHasActiveFocus: control.activeFocus
+ controlHasText: control.length > 0
+ controlImplicitBackgroundHeight: control.implicitBackgroundHeight
+ controlHeight: control.height
}
- background: Rectangle {
- y: parent.height - height - control.bottomPadding / 2
+ background: MaterialTextContainer {
implicitWidth: 120
- height: control.activeFocus ? 2 : 1
- color: control.activeFocus ? control.Material.accentColor : control.Material.hintTextColor
+ implicitHeight: control.Material.textFieldHeight
+
+ filled: control.Material.containerStyle === Material.Filled
+ fillColor: control.Material.textFieldFilledContainerColor
+ outlineColor: (enabled && control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor
+ focusedOutlineColor: control.Material.accentColor
+ // When the control's size is set larger than its implicit size, use whatever size is smaller
+ // so that the gap isn't too big.
+ placeholderTextWidth: Math.min(placeholder.width, placeholder.implicitWidth) * placeholder.scale
+ placeholderTextHAlign: control.effectiveHorizontalAlignment
+ controlHasActiveFocus: control.activeFocus
+ controlHasText: control.length > 0
+ placeholderHasText: placeholder.text.length > 0
+ horizontalPadding: control.Material.textFieldHorizontalPadding
}
}
diff --git a/src/quickcontrols/material/TextField.qml b/src/quickcontrols/material/TextField.qml
index 598f66f938..9294146fac 100644
--- a/src/quickcontrols/material/TextField.qml
+++ b/src/quickcontrols/material/TextField.qml
@@ -13,40 +13,67 @@ T.TextField {
implicitWidth: implicitBackgroundWidth + leftInset + rightInset
|| Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- contentHeight + topPadding + bottomPadding,
- placeholder.implicitHeight + topPadding + bottomPadding)
+ contentHeight + topPadding + bottomPadding)
- topPadding: 8
- bottomPadding: 16
+ // If we're clipped, set topInset to half the height of the placeholder text to avoid it being clipped.
+ topInset: clip ? placeholder.largestHeight / 2 : 0
+
+ leftPadding: Material.textFieldHorizontalPadding
+ rightPadding: Material.textFieldHorizontalPadding
+ // Need to account for the placeholder text when it's sitting on top.
+ topPadding: Material.containerStyle === Material.Filled
+ ? placeholderText.length > 0 && (activeFocus || length > 0)
+ ? Material.textFieldVerticalPadding + placeholder.largestHeight
+ : Material.textFieldVerticalPadding
+ // Account for any topInset (used to avoid floating placeholder text being clipped),
+ // otherwise the text will be too close to the background.
+ : Material.textFieldVerticalPadding + topInset
+ bottomPadding: Material.textFieldVerticalPadding
color: enabled ? Material.foreground : Material.hintTextColor
selectionColor: Material.accentColor
selectedTextColor: Material.primaryHighlightedTextColor
- placeholderTextColor: Material.hintTextColor
+ placeholderTextColor: enabled && activeFocus ? Material.accentColor : Material.hintTextColor
verticalAlignment: TextInput.AlignVCenter
+ Material.containerStyle: Material.Outlined
+
cursorDelegate: CursorDelegate { }
- PlaceholderText {
+ FloatingPlaceholderText {
id: placeholder
- x: control.leftPadding
- y: control.topPadding
+ // Don't set this to control.leftPadding, because we don't want it to change if the user changes leftPadding.
+ x: control.Material.textFieldHorizontalPadding
width: control.width - (control.leftPadding + control.rightPadding)
- height: control.height - (control.topPadding + control.bottomPadding)
text: control.placeholderText
font: control.font
color: control.placeholderTextColor
- verticalAlignment: control.verticalAlignment
elide: Text.ElideRight
renderType: control.renderType
- visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
+
+ filled: control.Material.containerStyle === Material.Filled
+ verticalPadding: control.Material.textFieldVerticalPadding
+ controlHasActiveFocus: control.activeFocus
+ controlHasText: control.length > 0
+ controlImplicitBackgroundHeight: control.implicitBackgroundHeight
+ controlHeight: control.height
}
- background: Rectangle {
- y: control.height - height - control.bottomPadding + 8
+ background: MaterialTextContainer {
implicitWidth: 120
- height: control.activeFocus || (enabled && control.hovered) ? 2 : 1
- color: control.activeFocus ? control.Material.accentColor
- : ((enabled && control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor)
+ implicitHeight: control.Material.textFieldHeight
+
+ filled: control.Material.containerStyle === Material.Filled
+ fillColor: control.Material.textFieldFilledContainerColor
+ outlineColor: (enabled && control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor
+ focusedOutlineColor: control.Material.accentColor
+ // When the control's size is set larger than its implicit size, use whatever size is smaller
+ // so that the gap isn't too big.
+ placeholderTextWidth: Math.min(placeholder.width, placeholder.implicitWidth) * placeholder.scale
+ placeholderTextHAlign: control.effectiveHorizontalAlignment
+ controlHasActiveFocus: control.activeFocus
+ controlHasText: control.length > 0
+ placeholderHasText: placeholder.text.length > 0
+ horizontalPadding: control.Material.textFieldHorizontalPadding
}
}
diff --git a/src/quickcontrols/material/TreeViewDelegate.qml b/src/quickcontrols/material/TreeViewDelegate.qml
index 1a6e35dadc..9f1d444383 100644
--- a/src/quickcontrols/material/TreeViewDelegate.qml
+++ b/src/quickcontrols/material/TreeViewDelegate.qml
@@ -64,6 +64,7 @@ T.TreeViewDelegate {
// The edit delegate is a separate component, and doesn't need
// to follow the same strict rules that are applied to a control.
// qmllint disable attached-property-reuse
+ // qmllint disable controls-attached-property-reuse
// qmllint disable controls-sanity
TableView.editDelegate: FocusScope {
width: parent.width
@@ -71,7 +72,7 @@ T.TreeViewDelegate {
readonly property int __role: {
let model = control.treeView.model
- let index = control.treeView.modelIndex(column, row)
+ let index = control.treeView.index(row, column)
let editText = model.data(index, Qt.EditRole)
return editText !== undefined ? Qt.EditRole : Qt.DisplayRole
}
@@ -81,17 +82,18 @@ T.TreeViewDelegate {
x: control.contentItem.x
y: (parent.height - height) / 2
width: control.contentItem.width
- text: control.treeView.model.data(control.treeView.modelIndex(column, row), __role)
+ text: control.treeView.model.data(control.treeView.index(row, column), __role)
focus: true
}
TableView.onCommit: {
- let index = TableView.view.modelIndex(column, row)
+ let index = TableView.view.index(row, column)
TableView.view.model.setData(index, textField.text, __role)
}
Component.onCompleted: textField.selectAll()
}
// qmllint enable attached-property-reuse
+ // qmllint enable controls-attached-property-reuse
// qmllint enable controls-sanity
}
diff --git a/src/quickcontrols/material/VerticalHeaderView.qml b/src/quickcontrols/material/VerticalHeaderView.qml
index 6b4d9e658b..0646f6135d 100644
--- a/src/quickcontrols/material/VerticalHeaderView.qml
+++ b/src/quickcontrols/material/VerticalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Templates as T
import QtQuick.Controls.Material
@@ -18,6 +20,10 @@ T.VerticalHeaderView {
implicitHeight: syncView ? syncView.height : 0
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -27,11 +33,9 @@ T.VerticalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: enabled ? control.Material.foreground : control.Material.hintTextColor
diff --git a/src/quickcontrols/material/impl/CMakeLists.txt b/src/quickcontrols/material/impl/CMakeLists.txt
index ebf3e1cbf3..774655c2ff 100644
--- a/src/quickcontrols/material/impl/CMakeLists.txt
+++ b/src/quickcontrols/material/impl/CMakeLists.txt
@@ -17,19 +17,21 @@ set(qml_files
"SwitchIndicator.qml"
)
-qt_internal_add_qml_module(qtquickcontrols2materialstyleimplplugin
+qt_internal_add_qml_module(QuickControls2MaterialStyleImpl
URI "QtQuick.Controls.Material.impl"
VERSION "${PROJECT_VERSION}"
PAST_MAJOR_VERSIONS 2
CLASS_NAME QtQuickControls2MaterialStyleImplPlugin
DEPENDENCIES
QtQuick/auto
+ QtQuick.Controls.impl/auto
PLUGIN_TARGET qtquickcontrols2materialstyleimplplugin
- NO_PLUGIN_OPTIONAL
SOURCES
qquickmaterialbusyindicator.cpp qquickmaterialbusyindicator_p.h
+ qquickmaterialplaceholdertext.cpp qquickmaterialplaceholdertext_p.h
qquickmaterialprogressbar.cpp qquickmaterialprogressbar_p.h
qquickmaterialripple.cpp qquickmaterialripple_p.h
+ qquickmaterialtextcontainer.cpp qquickmaterialtextcontainer_p.h
QML_FILES
${qml_files}
DEFINES
@@ -38,8 +40,10 @@ qt_internal_add_qml_module(qtquickcontrols2materialstyleimplplugin
LIBRARIES
Qt::CorePrivate
Qt::Gui
+ Qt::Qml
Qt::QmlPrivate
Qt::QuickControls2ImplPrivate
+ Qt::Quick
Qt::QuickPrivate
Qt::QuickTemplates2Private
)
diff --git a/src/quickcontrols/material/impl/RadioIndicator.qml b/src/quickcontrols/material/impl/RadioIndicator.qml
index 9570065692..2cf46a69cb 100644
--- a/src/quickcontrols/material/impl/RadioIndicator.qml
+++ b/src/quickcontrols/material/impl/RadioIndicator.qml
@@ -12,19 +12,42 @@ Rectangle {
implicitHeight: 20
radius: width / 2
border.width: 2
- border.color: !control.enabled ? control.Material.hintTextColor
- : control.checked || control.down ? control.Material.accentColor : control.Material.secondaryTextColor
+ border.color: targetColor
color: "transparent"
+ // Store the target color in a separate property, because there are two animations that depend on it.
+ readonly property color targetColor: !control.enabled ? control.Material.hintTextColor
+ : control.checked || control.down ? control.Material.accentColor : control.Material.secondaryTextColor
+
property T.AbstractButton control
+ Behavior on border.color {
+ ColorAnimation {
+ duration: 100
+ easing.type: Easing.OutCubic
+ }
+ }
+
Rectangle {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: 10
height: 10
radius: width / 2
- color: parent.border.color
- visible: indicator.control.checked || indicator.control.down
+ color: indicator.targetColor
+ scale: indicator.control.checked || indicator.control.down ? 1 : 0
+
+ Behavior on color {
+ ColorAnimation {
+ duration: 100
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ Behavior on scale {
+ NumberAnimation {
+ duration: 100
+ }
+ }
}
}
diff --git a/src/quickcontrols/material/impl/RectangularGlow.qml b/src/quickcontrols/material/impl/RectangularGlow.qml
index b604049cf9..5fc649c393 100644
--- a/src/quickcontrols/material/impl/RectangularGlow.qml
+++ b/src/quickcontrols/material/impl/RectangularGlow.qml
@@ -202,6 +202,6 @@ Item {
property real spread: rootItem.spread / 2.0
property real cornerRadius: clampedCornerRadius()
- fragmentShader: "qrc:/qt-project.org/imports/QtQuick/Controls/Material/shaders/RectangularGlow.frag"
+ fragmentShader: "qrc:/qt-project.org/imports/QtQuick/Controls/Material/shaders/RectangularGlow.frag.qsb"
}
}
diff --git a/src/quickcontrols/material/impl/SliderHandle.qml b/src/quickcontrols/material/impl/SliderHandle.qml
index ca7912b91d..4681992ab0 100644
--- a/src/quickcontrols/material/impl/SliderHandle.qml
+++ b/src/quickcontrols/material/impl/SliderHandle.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import QtQuick
@@ -22,14 +22,9 @@ Item {
width: parent.width
height: parent.height
radius: width / 2
- scale: root.handlePressed ? 1.5 : 1
- color: root.control.enabled ? root.control.Material.accentColor : root.control.Material.sliderDisabledColor
-
- Behavior on scale {
- NumberAnimation {
- duration: 250
- }
- }
+ color: root.control
+ ? root.control.enabled ? root.control.Material.accentColor : root.control.Material.sliderDisabledColor
+ : "transparent"
}
Ripple {
@@ -38,6 +33,6 @@ Item {
width: 22; height: 22
pressed: root.handlePressed
active: root.handlePressed || root.handleHasFocus || (enabled && root.handleHovered)
- color: root.control.Material.highlightedRippleColor
+ color: root.control ? root.control.Material.highlightedRippleColor : "transparent"
}
}
diff --git a/src/quickcontrols/material/impl/SwitchIndicator.qml b/src/quickcontrols/material/impl/SwitchIndicator.qml
index 2d48302e01..d864f3887a 100644
--- a/src/quickcontrols/material/impl/SwitchIndicator.qml
+++ b/src/quickcontrols/material/impl/SwitchIndicator.qml
@@ -9,8 +9,8 @@ import QtQuick.Controls.Material.impl
Rectangle {
id: indicator
- width: 52
- height: 32
+ width: control.Material.switchIndicatorWidth
+ height: control.Material.switchIndicatorHeight
radius: height / 2
y: parent.height / 2 - height / 2
color: control.enabled
@@ -62,9 +62,9 @@ Rectangle {
scale: indicator.control.down ? 1 : (indicator.control.checked ? checkedSize / largestSize : normalSize / largestSize)
readonly property int offset: 2
- readonly property real normalSize: !hasIcon ? 16 : checkedSize
- readonly property real checkedSize: 24
- readonly property real largestSize: 28
+ readonly property real normalSize: !hasIcon ? indicator.control.Material.switchNormalHandleHeight : checkedSize
+ readonly property real checkedSize: indicator.control.Material.switchCheckedHandleHeight
+ readonly property real largestSize: indicator.control.Material.switchLargestHandleHeight
readonly property real largestScale: largestSize / normalSize
readonly property bool hasIcon: indicator.control.icon.name.length > 0
|| indicator.control.icon.source.toString().length > 0
diff --git a/src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h b/src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h
index b6bbd925c7..c18257127d 100644
--- a/src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h
+++ b/src/quickcontrols/material/impl/qquickmaterialbusyindicator_p.h
@@ -51,6 +51,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMaterialBusyIndicator)
-
#endif // QQUICKMATERIALBUSYINDICATOR_P_H
diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp
new file mode 100644
index 0000000000..2447193d6a
--- /dev/null
+++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp
@@ -0,0 +1,321 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmaterialplaceholdertext_p.h"
+
+#include <QtCore/qpropertyanimation.h>
+#include <QtCore/qparallelanimationgroup.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuickTemplates2/private/qquicktheme_p.h>
+#include <QtQuickTemplates2/private/qquicktextarea_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static const qreal floatingScale = 0.8;
+Q_GLOBAL_STATIC(QEasingCurve, animationEasingCurve, QEasingCurve::OutSine);
+
+/*
+ This class makes it easier to animate the various placeholder text changes
+ for each type of text container (filled, outlined).
+
+ By doing animations in C++, we avoid having a bunch of states, transitions,
+ and animations (which are all QObjects) declared in QML, even if that text
+ control never gets focus and hence never needs them.
+*/
+
+QQuickMaterialPlaceholderText::QQuickMaterialPlaceholderText(QQuickItem *parent)
+ : QQuickPlaceholderText(parent)
+{
+ connect(this, &QQuickMaterialPlaceholderText::effectiveHorizontalAlignmentChanged,
+ this, &QQuickMaterialPlaceholderText::adjustTransformOrigin);
+}
+
+bool QQuickMaterialPlaceholderText::isFilled() const
+{
+ return m_filled;
+}
+
+void QQuickMaterialPlaceholderText::setFilled(bool filled)
+{
+ if (filled == m_filled)
+ return;
+
+ m_filled = filled;
+ update();
+ void filledChanged();
+}
+
+bool QQuickMaterialPlaceholderText::controlHasActiveFocus() const
+{
+ return m_controlHasActiveFocus;
+}
+
+void QQuickMaterialPlaceholderText::setControlHasActiveFocus(bool controlHasActiveFocus)
+{
+ if (m_controlHasActiveFocus == controlHasActiveFocus)
+ return;
+
+ m_controlHasActiveFocus = controlHasActiveFocus;
+ if (m_controlHasActiveFocus)
+ controlGotActiveFocus();
+ else
+ controlLostActiveFocus();
+ emit controlHasActiveFocusChanged();
+}
+
+bool QQuickMaterialPlaceholderText::controlHasText() const
+{
+ return m_controlHasText;
+}
+
+void QQuickMaterialPlaceholderText::setControlHasText(bool controlHasText)
+{
+ if (m_controlHasText == controlHasText)
+ return;
+
+ m_controlHasText = controlHasText;
+ maybeSetFocusAnimationProgress();
+ emit controlHasTextChanged();
+}
+
+/*
+ Placeholder text of outlined text fields should float when:
+ - There is placeholder text, and
+ - The control has active focus, or
+ - The control has text
+*/
+bool QQuickMaterialPlaceholderText::shouldFloat() const
+{
+ const bool controlHasActiveFocusOrText = m_controlHasActiveFocus || m_controlHasText;
+ return m_filled
+ ? controlHasActiveFocusOrText
+ : !text().isEmpty() && controlHasActiveFocusOrText;
+}
+
+bool QQuickMaterialPlaceholderText::shouldAnimate() const
+{
+ return m_filled
+ ? !m_controlHasText
+ : !m_controlHasText && !text().isEmpty();
+}
+
+void QQuickMaterialPlaceholderText::updateY()
+{
+ setY(shouldFloat() ? floatingTargetY() : normalTargetY());
+}
+
+qreal controlTopInset(QQuickItem *textControl)
+{
+ if (const auto textArea = qobject_cast<QQuickTextArea *>(textControl))
+ return textArea->topInset();
+
+ if (const auto textField = qobject_cast<QQuickTextField *>(textControl))
+ return textField->topInset();
+
+ return 0;
+}
+
+qreal QQuickMaterialPlaceholderText::normalTargetY() const
+{
+ auto *textArea = qobject_cast<QQuickTextArea *>(textControl());
+ if (textArea && m_controlHeight >= textArea->implicitHeight()) {
+ // TextArea can be multiple lines in height, and we want the
+ // placeholder text to sit in the middle of its default-height
+ // (one-line) if its explicit height is greater than or equal to its
+ // implicit height - i.e. if it has room for it. If it doesn't have
+ // room, just do what TextField does.
+ // We should also account for any topInset the user might have specified,
+ // which is useful to ensure that the text doesn't get clipped.
+ return ((m_controlImplicitBackgroundHeight - m_largestHeight) / 2.0)
+ + controlTopInset(textControl());
+ }
+
+ // When the placeholder text shouldn't float, it should sit in the middle of the TextField.
+ return (m_controlHeight - height()) / 2.0;
+}
+
+qreal QQuickMaterialPlaceholderText::floatingTargetY() const
+{
+ // For filled text fields, the placeholder text sits just above
+ // the text when floating.
+ if (m_filled)
+ return m_verticalPadding;
+
+ // Outlined text fields have the placeaholder vertically centered
+ // along the outline at the top.
+ return (-m_largestHeight / 2.0) + controlTopInset(textControl());
+}
+
+/*!
+ \internal
+
+ The height of the text at its largest size that we set.
+*/
+int QQuickMaterialPlaceholderText::largestHeight() const
+{
+ return m_largestHeight;
+}
+
+qreal QQuickMaterialPlaceholderText::controlImplicitBackgroundHeight() const
+{
+ return m_controlImplicitBackgroundHeight;
+}
+
+void QQuickMaterialPlaceholderText::setControlImplicitBackgroundHeight(qreal controlImplicitBackgroundHeight)
+{
+ if (qFuzzyCompare(m_controlImplicitBackgroundHeight, controlImplicitBackgroundHeight))
+ return;
+
+ m_controlImplicitBackgroundHeight = controlImplicitBackgroundHeight;
+ updateY();
+ emit controlImplicitBackgroundHeightChanged();
+}
+
+/*!
+ \internal
+
+ Exists so that we can call updateY when the control's height changes,
+ which is necessary for some y position calculations.
+
+ We don't really need it for the actual calculations, since we already
+ have access to the control, from which the property comes, but
+ it's simpler just to use it.
+*/
+qreal QQuickMaterialPlaceholderText::controlHeight() const
+{
+ return m_controlHeight;
+}
+
+void QQuickMaterialPlaceholderText::setControlHeight(qreal controlHeight)
+{
+ if (qFuzzyCompare(m_controlHeight, controlHeight))
+ return;
+
+ m_controlHeight = controlHeight;
+ updateY();
+}
+
+qreal QQuickMaterialPlaceholderText::verticalPadding() const
+{
+ return m_verticalPadding;
+}
+
+void QQuickMaterialPlaceholderText::setVerticalPadding(qreal verticalPadding)
+{
+ if (qFuzzyCompare(m_verticalPadding, verticalPadding))
+ return;
+
+ m_verticalPadding = verticalPadding;
+ emit verticalPaddingChanged();
+}
+
+void QQuickMaterialPlaceholderText::adjustTransformOrigin()
+{
+ switch (effectiveHAlign()) {
+ case QQuickText::AlignLeft:
+ Q_FALLTHROUGH();
+ case QQuickText::AlignJustify:
+ setTransformOrigin(QQuickItem::Left);
+ break;
+ case QQuickText::AlignRight:
+ setTransformOrigin(QQuickItem::Right);
+ break;
+ case QQuickText::AlignHCenter:
+ setTransformOrigin(QQuickItem::Center);
+ break;
+ }
+}
+
+void QQuickMaterialPlaceholderText::controlGotActiveFocus()
+{
+ if (m_focusOutAnimation) {
+ // Focus changes can happen before the animations finish.
+ // In that case, stop the animation, which will eventually delete it.
+ // Until it's deleted, we clear the pointer so that our asserts don't fail
+ // for the wrong reason.
+ m_focusOutAnimation->stop();
+ m_focusOutAnimation.clear();
+ }
+
+ Q_ASSERT(!m_focusInAnimation);
+ if (shouldAnimate()) {
+ m_focusInAnimation = new QParallelAnimationGroup(this);
+
+ QPropertyAnimation *yAnimation = new QPropertyAnimation(this, "y", this);
+ yAnimation->setDuration(300);
+ yAnimation->setStartValue(y());
+ yAnimation->setEndValue(floatingTargetY());
+ yAnimation->setEasingCurve(*animationEasingCurve);
+ m_focusInAnimation->addAnimation(yAnimation);
+
+ auto *scaleAnimation = new QPropertyAnimation(this, "scale", this);
+ scaleAnimation->setDuration(300);
+ scaleAnimation->setStartValue(1);
+ scaleAnimation->setEndValue(floatingScale);
+ yAnimation->setEasingCurve(*animationEasingCurve);
+ m_focusInAnimation->addAnimation(scaleAnimation);
+
+ m_focusInAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+ } else {
+ updateY();
+ }
+}
+
+void QQuickMaterialPlaceholderText::controlLostActiveFocus()
+{
+ if (m_focusInAnimation) {
+ m_focusInAnimation->stop();
+ m_focusInAnimation.clear();
+ }
+
+ Q_ASSERT(!m_focusOutAnimation);
+ if (shouldAnimate()) {
+ m_focusOutAnimation = new QParallelAnimationGroup(this);
+
+ auto *yAnimation = new QPropertyAnimation(this, "y", this);
+ yAnimation->setDuration(300);
+ yAnimation->setStartValue(y());
+ yAnimation->setEndValue(normalTargetY());
+ yAnimation->setEasingCurve(*animationEasingCurve);
+ m_focusOutAnimation->addAnimation(yAnimation);
+
+ auto *scaleAnimation = new QPropertyAnimation(this, "scale", this);
+ scaleAnimation->setDuration(300);
+ scaleAnimation->setStartValue(floatingScale);
+ scaleAnimation->setEndValue(1);
+ yAnimation->setEasingCurve(*animationEasingCurve);
+ m_focusOutAnimation->addAnimation(scaleAnimation);
+
+ m_focusOutAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+ } else {
+ updateY();
+ }
+}
+
+void QQuickMaterialPlaceholderText::maybeSetFocusAnimationProgress()
+{
+ updateY();
+ setScale(shouldFloat() ? floatingScale : 1.0);
+}
+
+void QQuickMaterialPlaceholderText::componentComplete()
+{
+ QQuickPlaceholderText::componentComplete();
+
+ adjustTransformOrigin();
+
+ m_largestHeight = implicitHeight();
+ if (m_largestHeight > 0) {
+ emit largestHeightChanged();
+ } else {
+ qmlWarning(this) << "Expected implicitHeight of placeholder text" << text()
+ << "to be greater than 0 by component completion!";
+ }
+
+ maybeSetFocusAnimationProgress();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h
new file mode 100644
index 0000000000..bf851d367a
--- /dev/null
+++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKMATERIALPLACEHOLDERTEXT_P_H
+#define QQUICKMATERIALPLACEHOLDERTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtGui/qcolor.h>
+#include <QtQuickControls2Impl/private/qquickplaceholdertext_p.h>
+
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QParallelAnimationGroup;
+
+class QQuickMaterialPlaceholderText : public QQuickPlaceholderText
+{
+ Q_OBJECT
+ Q_PROPERTY(bool filled READ isFilled WRITE setFilled NOTIFY filledChanged FINAL)
+ Q_PROPERTY(bool controlHasActiveFocus READ controlHasActiveFocus
+ WRITE setControlHasActiveFocus NOTIFY controlHasActiveFocusChanged FINAL)
+ Q_PROPERTY(bool controlHasText READ controlHasText WRITE setControlHasText NOTIFY controlHasTextChanged FINAL)
+ Q_PROPERTY(int largestHeight READ largestHeight NOTIFY largestHeightChanged FINAL)
+ Q_PROPERTY(qreal verticalPadding READ verticalPadding WRITE setVerticalPadding NOTIFY verticalPaddingChanged FINAL)
+ Q_PROPERTY(qreal controlImplicitBackgroundHeight READ controlImplicitBackgroundHeight
+ WRITE setControlImplicitBackgroundHeight NOTIFY controlImplicitBackgroundHeightChanged FINAL)
+ Q_PROPERTY(qreal controlHeight READ controlHeight WRITE setControlHeight FINAL)
+ QML_NAMED_ELEMENT(FloatingPlaceholderText)
+ QML_ADDED_IN_VERSION(6, 5)
+
+public:
+ explicit QQuickMaterialPlaceholderText(QQuickItem *parent = nullptr);
+
+ bool isFilled() const;
+ void setFilled(bool filled);
+
+ int largestHeight() const;
+
+ bool controlHasActiveFocus() const;
+ void setControlHasActiveFocus(bool controlHasActiveFocus);
+
+ bool controlHasText() const;
+ void setControlHasText(bool controlHasText);
+
+ qreal controlImplicitBackgroundHeight() const;
+ void setControlImplicitBackgroundHeight(qreal controlImplicitBackgroundHeight);
+
+ qreal controlHeight() const;
+ void setControlHeight(qreal controlHeight);
+
+ qreal verticalPadding() const;
+ void setVerticalPadding(qreal verticalPadding);
+
+signals:
+ void filledChanged();
+ void largestHeightChanged();
+ void controlHasActiveFocusChanged();
+ void controlHasTextChanged();
+ void controlImplicitBackgroundHeightChanged();
+ void verticalPaddingChanged();
+
+private slots:
+ void adjustTransformOrigin();
+
+private:
+ bool shouldFloat() const;
+ bool shouldAnimate() const;
+
+ void updateY();
+ qreal normalTargetY() const;
+ qreal floatingTargetY() const;
+
+ void controlGotActiveFocus();
+ void controlLostActiveFocus();
+
+ void maybeSetFocusAnimationProgress();
+
+ void componentComplete() override;
+
+ bool m_filled = false;
+ bool m_controlHasActiveFocus = false;
+ bool m_controlHasText = false;
+ int m_largestHeight = 0;
+ qreal m_verticalPadding = 0;
+ qreal m_controlImplicitBackgroundHeight = 0;
+ qreal m_controlHeight = 0;
+ QPointer<QParallelAnimationGroup> m_focusInAnimation;
+ QPointer<QParallelAnimationGroup> m_focusOutAnimation;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKMATERIALPLACEHOLDERTEXT_P_H
diff --git a/src/quickcontrols/material/impl/qquickmaterialprogressbar.cpp b/src/quickcontrols/material/impl/qquickmaterialprogressbar.cpp
index 67c9f614f5..19e02dc5b7 100644
--- a/src/quickcontrols/material/impl/qquickmaterialprogressbar.cpp
+++ b/src/quickcontrols/material/impl/qquickmaterialprogressbar.cpp
@@ -16,7 +16,7 @@ QT_BEGIN_NAMESPACE
static const int PauseDuration = 520;
static const int SlideDuration = 1240;
-static const int TotalDuration = SlideDuration + PauseDuration;
+static const int QmpbTotalDuration = SlideDuration + PauseDuration;
class QQuickMaterialProgressBarNode : public QQuickAnimatedNode
{
@@ -37,7 +37,7 @@ QQuickMaterialProgressBarNode::QQuickMaterialProgressBarNode(QQuickMaterialProgr
: QQuickAnimatedNode(item)
{
setLoopCount(Infinite);
- setDuration(TotalDuration);
+ setDuration(QmpbTotalDuration);
}
void QQuickMaterialProgressBarNode::updateCurrentTime(int time)
diff --git a/src/quickcontrols/material/impl/qquickmaterialprogressbar_p.h b/src/quickcontrols/material/impl/qquickmaterialprogressbar_p.h
index d250c2f76d..46d5c1b20f 100644
--- a/src/quickcontrols/material/impl/qquickmaterialprogressbar_p.h
+++ b/src/quickcontrols/material/impl/qquickmaterialprogressbar_p.h
@@ -54,6 +54,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMaterialProgressBar)
-
#endif // QQUICKMATERIALPROGRESSBAR_P_H
diff --git a/src/quickcontrols/material/impl/qquickmaterialripple.cpp b/src/quickcontrols/material/impl/qquickmaterialripple.cpp
index 89648182d4..2472b940a3 100644
--- a/src/quickcontrols/material/impl/qquickmaterialripple.cpp
+++ b/src/quickcontrols/material/impl/qquickmaterialripple.cpp
@@ -12,9 +12,7 @@
QT_BEGIN_NAMESPACE
-namespace {
- enum WavePhase { WaveEnter, WaveExit };
-}
+enum WavePhase { WaveEnter, WaveExit };
static const int RIPPLE_ENTER_DELAY = 80;
static const int OPACITY_ENTER_DURATION_FAST = 120;
@@ -90,6 +88,10 @@ void QQuickMaterialRippleWaveNode::updateCurrentTime(int time)
Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType);
rectNode->setRect(QRectF(0, 0, m_value, m_value));
rectNode->setRadius(m_value / 2);
+ rectNode->setTopLeftRadius(-1);
+ rectNode->setTopRightRadius(-1);
+ rectNode->setBottomLeftRadius(-1);
+ rectNode->setBottomRightRadius(-1);
rectNode->update();
}
@@ -176,6 +178,10 @@ void QQuickMaterialRippleBackgroundNode::sync(QQuickItem *item)
rectNode->setRect(QRectF(0, 0, w, h));
rectNode->setRadius(ripple->clipRadius());
}
+ rectNode->setTopLeftRadius(-1);
+ rectNode->setTopRightRadius(-1);
+ rectNode->setBottomLeftRadius(-1);
+ rectNode->setBottomRightRadius(-1);
setMatrix(matrix);
rectNode->setColor(ripple->color());
diff --git a/src/quickcontrols/material/impl/qquickmaterialripple_p.h b/src/quickcontrols/material/impl/qquickmaterialripple_p.h
index cdaaf13b1a..d0772052bd 100644
--- a/src/quickcontrols/material/impl/qquickmaterialripple_p.h
+++ b/src/quickcontrols/material/impl/qquickmaterialripple_p.h
@@ -83,6 +83,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMaterialRipple)
-
#endif // QQUICKMATERIALRIPPLE_P_H
diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
new file mode 100644
index 0000000000..ab07c9d9b2
--- /dev/null
+++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
@@ -0,0 +1,422 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmaterialtextcontainer_p.h"
+
+#include <QtCore/qpropertyanimation.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtQml/qqmlinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ This class exists because:
+
+ - Rectangle doesn't support individual radii for each corner (QTBUG-48774).
+ - We need to draw an interrupted (where the placeholder text is) line for outlined containers.
+ - We need to animate the focus line for filled containers, and we can't use "Behavior on"
+ syntax because we only want to animate activeFocus becoming true, not also false. To do this
+ requires imperative code, and we want to keep the QML declarative.
+
+ focusAnimationProgress has to be a property even though it's only used internally,
+ because we have to use QPropertyAnimation on it.
+
+ An advantage of doing the animation in C++ is that we avoid the memory
+ overhead of an animation instance even when we're not using it, and instead
+ create it on demand and delete it when it's done. I tried doing the animation
+ declaratively with states and transitions, but it was more difficult to implement
+ and would have been harder to maintain, as well as having more overhead.
+*/
+
+QQuickMaterialTextContainer::QQuickMaterialTextContainer(QQuickItem *parent)
+ : QQuickPaintedItem(parent)
+{
+}
+
+bool QQuickMaterialTextContainer::isFilled() const
+{
+ return m_filled;
+}
+
+void QQuickMaterialTextContainer::setFilled(bool filled)
+{
+ if (filled == m_filled)
+ return;
+
+ m_filled = filled;
+ update();
+}
+
+QColor QQuickMaterialTextContainer::fillColor() const
+{
+ return m_fillColor;
+}
+
+void QQuickMaterialTextContainer::setFillColor(const QColor &fillColor)
+{
+ if (fillColor == m_fillColor)
+ return;
+
+ m_fillColor = fillColor;
+ update();
+}
+
+QColor QQuickMaterialTextContainer::outlineColor() const
+{
+ return m_outlineColor;
+}
+
+void QQuickMaterialTextContainer::setOutlineColor(const QColor &outlineColor)
+{
+ if (outlineColor == m_outlineColor)
+ return;
+
+ m_outlineColor = outlineColor;
+ update();
+}
+
+QColor QQuickMaterialTextContainer::focusedOutlineColor() const
+{
+ return m_outlineColor;
+}
+
+void QQuickMaterialTextContainer::setFocusedOutlineColor(const QColor &focusedOutlineColor)
+{
+ if (focusedOutlineColor == m_focusedOutlineColor)
+ return;
+
+ m_focusedOutlineColor = focusedOutlineColor;
+ update();
+}
+
+qreal QQuickMaterialTextContainer::focusAnimationProgress() const
+{
+ return m_focusAnimationProgress;
+}
+
+void QQuickMaterialTextContainer::setFocusAnimationProgress(qreal progress)
+{
+ if (qFuzzyCompare(progress, m_focusAnimationProgress))
+ return;
+
+ m_focusAnimationProgress = progress;
+ update();
+}
+
+qreal QQuickMaterialTextContainer::placeholderTextWidth() const
+{
+ return m_placeholderTextWidth;
+}
+
+void QQuickMaterialTextContainer::setPlaceholderTextWidth(qreal placeholderTextWidth)
+{
+ if (qFuzzyCompare(placeholderTextWidth, m_placeholderTextWidth))
+ return;
+
+ m_placeholderTextWidth = placeholderTextWidth;
+ update();
+}
+
+QQuickMaterialTextContainer::PlaceHolderHAlignment QQuickMaterialTextContainer::placeholderTextHAlign() const
+{
+ return m_placeholderTextHAlign;
+}
+
+void QQuickMaterialTextContainer::setPlaceholderTextHAlign(PlaceHolderHAlignment placeholderTextHAlign)
+{
+ if (m_placeholderTextHAlign == placeholderTextHAlign)
+ return;
+
+ m_placeholderTextHAlign = placeholderTextHAlign;
+ update();
+}
+
+bool QQuickMaterialTextContainer::controlHasActiveFocus() const
+{
+ return m_controlHasActiveFocus;
+}
+
+void QQuickMaterialTextContainer::setControlHasActiveFocus(bool controlHasActiveFocus)
+{
+ if (m_controlHasActiveFocus == controlHasActiveFocus)
+ return;
+
+ m_controlHasActiveFocus = controlHasActiveFocus;
+ if (m_controlHasActiveFocus)
+ controlGotActiveFocus();
+ else
+ controlLostActiveFocus();
+ emit controlHasActiveFocusChanged();
+}
+
+bool QQuickMaterialTextContainer::controlHasText() const
+{
+ return m_controlHasText;
+}
+
+void QQuickMaterialTextContainer::setControlHasText(bool controlHasText)
+{
+ if (m_controlHasText == controlHasText)
+ return;
+
+ m_controlHasText = controlHasText;
+ // TextArea's text length is updated after component completion,
+ // so account for that here and in setPlaceholderHasText().
+ maybeSetFocusAnimationProgress();
+ update();
+ emit controlHasTextChanged();
+}
+
+bool QQuickMaterialTextContainer::placeholderHasText() const
+{
+ return m_placeholderHasText;
+}
+
+void QQuickMaterialTextContainer::setPlaceholderHasText(bool placeholderHasText)
+{
+ if (m_placeholderHasText == placeholderHasText)
+ return;
+
+ m_placeholderHasText = placeholderHasText;
+ maybeSetFocusAnimationProgress();
+ update();
+ emit placeholderHasTextChanged();
+}
+
+int QQuickMaterialTextContainer::horizontalPadding() const
+{
+ return m_horizontalPadding;
+}
+
+/*!
+ \internal
+
+ The text field's horizontal padding.
+
+ We need this to be a property so that the QML can set it, since we can't
+ access QQuickMaterialStyle's C++ API from this plugin.
+*/
+void QQuickMaterialTextContainer::setHorizontalPadding(int horizontalPadding)
+{
+ if (m_horizontalPadding == horizontalPadding)
+ return;
+ m_horizontalPadding = horizontalPadding;
+ update();
+ emit horizontalPaddingChanged();
+}
+
+void QQuickMaterialTextContainer::paint(QPainter *painter)
+{
+ qreal w = width();
+ qreal h = height();
+ if (w <= 0 || h <= 0)
+ return;
+
+ // Account for pen width.
+ const qreal penWidth = m_filled ? 1 : (m_controlHasActiveFocus ? 2 : 1);
+ w -= penWidth;
+ h -= penWidth;
+
+ const qreal cornerRadius = 4;
+ // This is coincidentally the same as cornerRadius, but use different variable names
+ // to keep the code understandable.
+ const qreal gapPadding = 4;
+ // When animating focus on outlined containers, we need to make a gap
+ // at the top left for the placeholder text.
+ // If the text is too wide for the container, it will be elided, so
+ // we shouldn't need to clamp its width here. TODO: check that this is the case for TextArea.
+ const qreal halfPlaceholderWidth = m_placeholderTextWidth / 2;
+ // Take care of different Alignment cases for the placeholder text.
+ qreal gapCenterX;
+ switch (m_placeholderTextHAlign) {
+ case PlaceHolderHAlignment::AlignHCenter:
+ gapCenterX = width() / 2;
+ break;
+ case PlaceHolderHAlignment::AlignRight:
+ gapCenterX = width() - halfPlaceholderWidth - m_horizontalPadding;
+ break;
+ default:
+ gapCenterX = m_horizontalPadding + halfPlaceholderWidth;
+ break;
+ }
+
+ QPainterPath path;
+
+ QPointF startPos;
+
+ // Top-left rounded corner.
+ if (m_filled || m_focusAnimationProgress == 0) {
+ startPos = QPointF(cornerRadius, 0);
+ } else {
+ // Start at the center of the gap and animate outwards towards the left-hand side.
+ // Subtract gapPadding to account for the gap between the line and the placeholder text.
+ // Also subtract the pen width because otherwise it extends by that distance too much to the right.
+ // Changing the cap style to Qt::FlatCap would only fix this by half the pen width,
+ // but it has no effect anyway (perhaps it literally only affects end points and not "start" points?).
+ startPos = QPointF(gapCenterX - (m_focusAnimationProgress * halfPlaceholderWidth) - gapPadding - penWidth, 0);
+ }
+ path.moveTo(startPos);
+ path.arcTo(0, 0, cornerRadius * 2, cornerRadius * 2, 90, 90);
+
+ // Bottom-left corner.
+ if (m_filled) {
+ path.lineTo(0, h);
+ } else {
+ path.lineTo(0, h - cornerRadius * 2);
+ path.arcTo(0, h - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 180, 90);
+ }
+
+ // Bottom-right corner.
+ if (m_filled) {
+ path.lineTo(w, h);
+ } else {
+ path.lineTo(w - cornerRadius * 2, h);
+ path.arcTo(w - cornerRadius * 2, h - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 270, 90);
+ }
+
+ // Top-right rounded corner.
+ path.lineTo(w, cornerRadius);
+ path.arcTo(w - (cornerRadius * 2), 0, cornerRadius * 2, cornerRadius * 2, 0, 90);
+
+ if (m_filled || qFuzzyIsNull(m_focusAnimationProgress)) {
+ // Back to the start.
+ path.lineTo(startPos.x(), startPos.y());
+ } else {
+ path.lineTo(gapCenterX + (m_focusAnimationProgress * halfPlaceholderWidth) + gapPadding, startPos.y());
+ }
+
+ // Account for pen width.
+ painter->translate(penWidth / 2, penWidth / 2);
+
+ painter->setRenderHint(QPainter::Antialiasing, true);
+
+ auto control = textControl();
+ const bool focused = control && control->hasActiveFocus();
+ // We still want to draw the stroke when it's filled, otherwise it will be a pixel
+ // (the pen width) too narrow on either side.
+ QPen pen;
+ pen.setColor(m_filled ? m_fillColor : (focused ? m_focusedOutlineColor : m_outlineColor));
+ pen.setWidthF(penWidth);
+ painter->setPen(pen);
+ if (m_filled)
+ painter->setBrush(QBrush(m_fillColor));
+
+ // Fill or stroke the container's shape.
+ // If not filling, the default brush will be used, which is Qt::NoBrush.
+ painter->drawPath(path);
+
+ // Draw the focus line at the bottom for filled containers.
+ if (m_filled) {
+ if (!qFuzzyCompare(m_focusAnimationProgress, 1.0)) {
+ // Draw the enabled active indicator line (#10) that's at the bottom when it's not focused:
+ // https://m3.material.io/components/text-fields/specs#6d654d1d-262e-4697-858c-9a75e8e7c81d
+ // Don't bother drawing it when the animation has finished, as the focused active indicator
+ // line below will obscure it.
+ pen.setColor(m_outlineColor);
+ painter->setPen(pen);
+ painter->drawLine(0, h, w, h);
+ }
+
+ if (!qFuzzyIsNull(m_focusAnimationProgress)) {
+ // Draw the focused active indicator line (#6) that's at the bottom when it's focused.
+ // Start at the center and expand outwards.
+ const int lineLength = m_focusAnimationProgress * w;
+ const int horizontalCenter = w / 2;
+ pen.setColor(m_focusedOutlineColor);
+ pen.setWidth(2);
+ painter->setPen(pen);
+ painter->drawLine(horizontalCenter - (lineLength / 2), h,
+ horizontalCenter + (lineLength / 2) + pen.width() / 2, h);
+ }
+ }
+}
+
+bool QQuickMaterialTextContainer::shouldAnimateOutline() const
+{
+ return !m_controlHasText && m_placeholderHasText;
+}
+
+/*!
+ \internal
+
+ \sa QQuickPlaceholderText::textControl().
+*/
+QQuickItem *QQuickMaterialTextContainer::textControl() const
+{
+ return qobject_cast<QQuickItem *>(parent());
+}
+
+void QQuickMaterialTextContainer::controlGotActiveFocus()
+{
+ const bool shouldAnimate = m_filled ? !m_controlHasText : shouldAnimateOutline();
+ if (!shouldAnimate) {
+ // It does have focus, but sometimes we don't need to animate anything, just change colors.
+ if (m_filled && m_controlHasText) {
+ // When a filled container has text already entered, we should just immediately change
+ // the color and thickness of the indicator line.
+ m_focusAnimationProgress = 1;
+ }
+ update();
+ return;
+ }
+
+ startFocusAnimation();
+}
+
+void QQuickMaterialTextContainer::controlLostActiveFocus()
+{
+ // We don't want to animate the active indicator line (at the bottom) of filled containers
+ // when the control loses focus, only when it gets it.
+ if (m_filled || !shouldAnimateOutline()) {
+ // Ensure that we set this so that filled containers go back to a non-accent-colored
+ // active indicator line when losing focus.
+ if (m_filled)
+ m_focusAnimationProgress = 0;
+ update();
+ return;
+ }
+
+ QPropertyAnimation *animation = new QPropertyAnimation(this, "focusAnimationProgress", this);
+ animation->setDuration(300);
+ animation->setStartValue(1);
+ animation->setEndValue(0);
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+}
+
+void QQuickMaterialTextContainer::startFocusAnimation()
+{
+ // Each time setFocusAnimationProgress is called by the animation, it'll call update(),
+ // which will cause us to be re-rendered.
+ QPropertyAnimation *animation = new QPropertyAnimation(this, "focusAnimationProgress", this);
+ animation->setDuration(300);
+ animation->setStartValue(0);
+ animation->setEndValue(1);
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+}
+
+void QQuickMaterialTextContainer::maybeSetFocusAnimationProgress()
+{
+ if (m_filled)
+ return;
+
+ if (m_controlHasText && m_placeholderHasText) {
+ // Show the interrupted outline when there is text.
+ setFocusAnimationProgress(1);
+ } else if (!m_controlHasText && !m_controlHasActiveFocus) {
+ // If the text was cleared while it didn't have focus, don't animate, just close the gap.
+ setFocusAnimationProgress(0);
+ }
+}
+
+void QQuickMaterialTextContainer::componentComplete()
+{
+ QQuickPaintedItem::componentComplete();
+
+ if (!parentItem())
+ qmlWarning(this) << "Expected parent item by component completion!";
+
+ maybeSetFocusAnimationProgress();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h b/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h
new file mode 100644
index 0000000000..648e83521f
--- /dev/null
+++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h
@@ -0,0 +1,122 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKMATERIALTEXTCONTAINER_P_H
+#define QQUICKMATERIALTEXTCONTAINER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtGui/qcolor.h>
+#include <QtQuick/qquickpainteditem.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickMaterialTextContainer : public QQuickPaintedItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool filled READ isFilled WRITE setFilled FINAL)
+ Q_PROPERTY(bool controlHasActiveFocus READ controlHasActiveFocus
+ WRITE setControlHasActiveFocus NOTIFY controlHasActiveFocusChanged FINAL)
+ Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor FINAL)
+ Q_PROPERTY(QColor outlineColor READ outlineColor WRITE setOutlineColor FINAL)
+ Q_PROPERTY(QColor focusedOutlineColor READ focusedOutlineColor WRITE setFocusedOutlineColor FINAL)
+ Q_PROPERTY(qreal focusAnimationProgress READ focusAnimationProgress WRITE setFocusAnimationProgress FINAL)
+ Q_PROPERTY(qreal placeholderTextWidth READ placeholderTextWidth WRITE setPlaceholderTextWidth FINAL)
+ Q_PROPERTY(PlaceHolderHAlignment placeholderTextHAlign READ placeholderTextHAlign WRITE setPlaceholderTextHAlign FINAL)
+ Q_PROPERTY(bool controlHasText READ controlHasText WRITE setControlHasText NOTIFY controlHasTextChanged FINAL)
+ Q_PROPERTY(bool placeholderHasText READ placeholderHasText WRITE setPlaceholderHasText NOTIFY placeholderHasTextChanged FINAL)
+ Q_PROPERTY(int horizontalPadding READ horizontalPadding WRITE setHorizontalPadding NOTIFY horizontalPaddingChanged FINAL)
+ QML_NAMED_ELEMENT(MaterialTextContainer)
+ QML_ADDED_IN_VERSION(6, 5)
+
+public:
+ explicit QQuickMaterialTextContainer(QQuickItem *parent = nullptr);
+
+ enum PlaceHolderHAlignment {
+ AlignLeft = Qt::AlignLeft,
+ AlignRight = Qt::AlignRight,
+ AlignHCenter = Qt::AlignHCenter,
+ AlignJustify = Qt::AlignJustify
+ };
+ Q_ENUM(PlaceHolderHAlignment)
+
+ bool isFilled() const;
+ void setFilled(bool filled);
+
+ QColor fillColor() const;
+ void setFillColor(const QColor &fillColor);
+
+ QColor outlineColor() const;
+ void setOutlineColor(const QColor &outlineColor);
+
+ QColor focusedOutlineColor() const;
+ void setFocusedOutlineColor(const QColor &focusedOutlineColor);
+
+ qreal focusAnimationProgress() const;
+ void setFocusAnimationProgress(qreal progress);
+
+ qreal placeholderTextWidth() const;
+ void setPlaceholderTextWidth(qreal placeholderTextWidth);
+
+ bool controlHasActiveFocus() const;
+ void setControlHasActiveFocus(bool controlHasActiveFocus);
+
+ bool controlHasText() const;
+ void setControlHasText(bool controlHasText);
+
+ bool placeholderHasText() const;
+ void setPlaceholderHasText(bool placeholderHasText);
+
+ int horizontalPadding() const;
+ void setHorizontalPadding(int horizontalPadding);
+
+ void paint(QPainter *painter) override;
+
+ PlaceHolderHAlignment placeholderTextHAlign() const;
+ void setPlaceholderTextHAlign(PlaceHolderHAlignment placeHolderTextHAlign);
+
+signals:
+ void animateChanged();
+ void controlHasActiveFocusChanged();
+ void controlHasTextChanged();
+ void placeholderHasTextChanged();
+ void horizontalPaddingChanged();
+
+private:
+ bool shouldAnimateOutline() const;
+
+ QQuickItem *textControl() const;
+ void controlGotActiveFocus();
+ void controlLostActiveFocus();
+ void startFocusAnimation();
+
+ void maybeSetFocusAnimationProgress();
+
+ void componentComplete() override;
+
+ QColor m_fillColor;
+ QColor m_outlineColor;
+ QColor m_focusedOutlineColor;
+ qreal m_focusAnimationProgress = 0;
+ qreal m_placeholderTextWidth = 0;
+ bool m_filled = false;
+ bool m_controlHasActiveFocus = false;
+ bool m_controlHasText = false;
+ bool m_placeholderHasText = false;
+ int m_horizontalPadding = 0;
+ PlaceHolderHAlignment m_placeholderTextHAlign;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKMATERIALTEXTCONTAINER_P_H
diff --git a/src/quickcontrols/material/qquickmaterialstyle.cpp b/src/quickcontrols/material/qquickmaterialstyle.cpp
index d7eb72f830..f8ced5821b 100644
--- a/src/quickcontrols/material/qquickmaterialstyle.cpp
+++ b/src/quickcontrols/material/qquickmaterialstyle.cpp
@@ -4,7 +4,9 @@
#include "qquickmaterialstyle_p.h"
#include <QtCore/qdebug.h>
+#if QT_CONFIG(settings)
#include <QtCore/qsettings.h>
+#endif
#include <QtQml/qqmlinfo.h>
#include <QtQuickControls2/private/qquickstyle_p.h>
@@ -422,6 +424,8 @@ static const QRgb switchDisabledCheckedTrackColorLight = 0x1E1C1B1F;
static const QRgb switchDisabledCheckedTrackColorDark = 0x1EE6E1E5;
static const QRgb switchDisabledUncheckedIconColorLight = 0x611C1B1F;
static const QRgb switchDisabledUncheckedIconColorDark = 0x61E6E1E5;
+static const QRgb textFieldFilledContainerColorLight = 0xFFE7E0EC;
+static const QRgb textFieldFilledContainerColorDark = 0xFF49454F;
static QQuickMaterialStyle::Theme effectiveTheme(QQuickMaterialStyle::Theme theme)
{
@@ -941,10 +945,7 @@ QColor QQuickMaterialStyle::iconDisabledColor() const
QColor QQuickMaterialStyle::buttonColor(Theme theme, const QVariant &background, const QVariant &accent,
bool enabled, bool flat, bool highlighted, bool checked) const
{
- if (flat)
- return Qt::transparent;
-
- if (!enabled) {
+ if (!enabled && !flat) {
return QColor::fromRgba(m_theme == Light
? raisedButtonDisabledColorLight : raisedButtonDisabledColorDark);
}
@@ -970,7 +971,11 @@ QColor QQuickMaterialStyle::buttonColor(Theme theme, const QVariant &background,
// A highlighted + checked button should become darker.
color = accentColor(checked ? Shade100 : shade);
}
- } else {
+ // Flat, highlighted buttons need to have a semi-transparent background,
+ // otherwise the text won't be visible.
+ if (flat)
+ color.setAlphaF(0.25);
+ } else if (!flat) {
// Even if the elevation is zero, it should still have a background if it's not flat.
color = QColor::fromRgba(m_theme == Light ? raisedButtonColorLight
: raisedButtonColorDark);
@@ -1158,6 +1163,11 @@ QColor QQuickMaterialStyle::sliderDisabledColor() const
return QColor::fromRgba(m_theme == Light ? sliderDisabledColorLight : sliderDisabledColorDark);
}
+QColor QQuickMaterialStyle::textFieldFilledContainerColor() const
+{
+ return QColor::fromRgba(m_theme == Light ? textFieldFilledContainerColorLight : textFieldFilledContainerColorDark);
+}
+
QColor QQuickMaterialStyle::color(QQuickMaterialStyle::Color color, QQuickMaterialStyle::Shade shade) const
{
int count = sizeof(colors) / sizeof(colors[0]);
@@ -1259,6 +1269,32 @@ int QQuickMaterialStyle::touchTarget() const
return globalVariant == Dense ? 44 : 48;
}
+int QQuickMaterialStyle::buttonVerticalPadding() const
+{
+ return globalVariant == Dense ? 10 : 14;
+}
+
+// https://m3.material.io/components/buttons/specs#256326ad-f934-40e7-b05f-0bcb41aa4382
+int QQuickMaterialStyle::buttonLeftPadding(bool flat, bool hasIcon) const
+{
+ static const int noIconPadding = globalVariant == Dense ? 12 : 24;
+ static const int iconPadding = globalVariant == Dense ? 8 : 16;
+ static const int flatPadding = globalVariant == Dense ? 6 : 12;
+ return !flat ? (!hasIcon ? noIconPadding : iconPadding) : flatPadding;
+}
+
+int QQuickMaterialStyle::buttonRightPadding(bool flat, bool hasIcon, bool hasText) const
+{
+ static const int noTextPadding = globalVariant == Dense ? 8 : 16;
+ static const int textPadding = globalVariant == Dense ? 12 : 24;
+ static const int flatNoIconPadding = globalVariant == Dense ? 6 : 12;
+ static const int flatNoTextPadding = globalVariant == Dense ? 6 : 12;
+ static const int flatTextPadding = globalVariant == Dense ? 8 : 16;
+ return !flat
+ ? (!hasText ? noTextPadding : textPadding)
+ : (!hasIcon ? flatNoIconPadding : (!hasText ? flatNoTextPadding : flatTextPadding));
+}
+
int QQuickMaterialStyle::buttonHeight() const
{
// https://m3.material.io/components/buttons/specs#256326ad-f934-40e7-b05f-0bcb41aa4382
@@ -1276,6 +1312,19 @@ int QQuickMaterialStyle::dialogButtonBoxHeight() const
return globalVariant == Dense ? 48 : 52;
}
+int QQuickMaterialStyle::dialogTitleFontPixelSize() const
+{
+ return globalVariant == Dense ? 16 : 24;
+}
+
+// https://m3.material.io/components/dialogs/specs#6771d107-624e-47cc-b6d8-2b7b620ba2f1
+QQuickMaterialStyle::RoundedScale QQuickMaterialStyle::dialogRoundedScale() const
+{
+ return globalVariant == Dense
+ ? QQuickMaterialStyle::RoundedScale::LargeScale
+ : QQuickMaterialStyle::RoundedScale::ExtraLargeScale;
+}
+
int QQuickMaterialStyle::frameVerticalPadding() const
{
return globalVariant == Dense ? 8 : 12;
@@ -1292,6 +1341,31 @@ int QQuickMaterialStyle::menuItemVerticalPadding() const
return globalVariant == Dense ? 8 : 12;
}
+int QQuickMaterialStyle::switchIndicatorWidth() const
+{
+ return globalVariant == Dense ? 40 : 52;
+}
+
+int QQuickMaterialStyle::switchIndicatorHeight() const
+{
+ return globalVariant == Dense ? 22 : 32;
+}
+
+int QQuickMaterialStyle::switchNormalHandleHeight() const
+{
+ return globalVariant == Dense ? 10 : 16;
+}
+
+int QQuickMaterialStyle::switchCheckedHandleHeight() const
+{
+ return globalVariant == Dense ? 16 : 24;
+}
+
+int QQuickMaterialStyle::switchLargestHandleHeight() const
+{
+ return globalVariant == Dense ? 18 : 28;
+}
+
int QQuickMaterialStyle::switchDelegateVerticalPadding() const
{
// SwitchDelegate's indicator is much larger than the others due to the shadow,
@@ -1299,6 +1373,21 @@ int QQuickMaterialStyle::switchDelegateVerticalPadding() const
return globalVariant == Dense ? 4 : 8;
}
+int QQuickMaterialStyle::textFieldHeight() const
+{
+ // filled: https://m3.material.io/components/text-fields/specs#8c032848-e442-46df-b25d-28f1315f234b
+ // outlined: https://m3.material.io/components/text-fields/specs#605e24f1-1c1f-4c00-b385-4bf50733a5ef
+ return globalVariant == Dense ? 44 : 56;
+}
+int QQuickMaterialStyle::textFieldHorizontalPadding() const
+{
+ return globalVariant == Dense ? 12 : 16;
+}
+int QQuickMaterialStyle::textFieldVerticalPadding() const
+{
+ return globalVariant == Dense ? 4 : 8;
+}
+
int QQuickMaterialStyle::tooltipHeight() const
{
// https://material.io/guidelines/components/tooltips.html
diff --git a/src/quickcontrols/material/qquickmaterialstyle_p.h b/src/quickcontrols/material/qquickmaterialstyle_p.h
index ab3483f04d..f7c2b256ba 100644
--- a/src/quickcontrols/material/qquickmaterialstyle_p.h
+++ b/src/quickcontrols/material/qquickmaterialstyle_p.h
@@ -18,10 +18,11 @@
#include <QtGui/qcolor.h>
#include <QtQml/qqml.h>
#include <QtQuickControls2/qquickattachedpropertypropagator.h>
+#include <QtQuickControls2Material/qtquickcontrols2materialexports.h>
QT_BEGIN_NAMESPACE
-class QQuickMaterialStyle : public QQuickAttachedPropertyPropagator
+class Q_QUICKCONTROLS2MATERIAL_EXPORT QQuickMaterialStyle : public QQuickAttachedPropertyPropagator
{
Q_OBJECT
Q_PROPERTY(Theme theme READ theme WRITE setTheme RESET resetTheme NOTIFY themeChanged FINAL)
@@ -73,15 +74,27 @@ class QQuickMaterialStyle : public QQuickAttachedPropertyPropagator
Q_PROPERTY(QColor toolTextColor READ toolTextColor NOTIFY toolTextColorChanged FINAL)
Q_PROPERTY(QColor spinBoxDisabledIconColor READ spinBoxDisabledIconColor NOTIFY themeChanged FINAL)
Q_PROPERTY(QColor sliderDisabledColor READ sliderDisabledColor NOTIFY themeChanged FINAL REVISION(2, 15))
+ Q_PROPERTY(QColor textFieldFilledContainerColor READ textFieldFilledContainerColor NOTIFY themeChanged FINAL)
Q_PROPERTY(int touchTarget READ touchTarget CONSTANT FINAL)
+ Q_PROPERTY(int buttonVerticalPadding READ buttonVerticalPadding CONSTANT FINAL)
Q_PROPERTY(int buttonHeight READ buttonHeight CONSTANT FINAL)
Q_PROPERTY(int delegateHeight READ delegateHeight CONSTANT FINAL)
Q_PROPERTY(int dialogButtonBoxHeight READ dialogButtonBoxHeight CONSTANT FINAL)
+ Q_PROPERTY(int dialogTitleFontPixelSize READ dialogTitleFontPixelSize CONSTANT FINAL)
+ Q_PROPERTY(RoundedScale dialogRoundedScale READ dialogRoundedScale CONSTANT FINAL)
Q_PROPERTY(int frameVerticalPadding READ frameVerticalPadding CONSTANT FINAL)
Q_PROPERTY(int menuItemHeight READ menuItemHeight CONSTANT FINAL)
Q_PROPERTY(int menuItemVerticalPadding READ menuItemVerticalPadding CONSTANT FINAL)
+ Q_PROPERTY(int switchIndicatorWidth READ switchIndicatorWidth CONSTANT FINAL)
+ Q_PROPERTY(int switchIndicatorHeight READ switchIndicatorHeight CONSTANT FINAL)
+ Q_PROPERTY(int switchNormalHandleHeight READ switchNormalHandleHeight CONSTANT FINAL)
+ Q_PROPERTY(int switchCheckedHandleHeight READ switchCheckedHandleHeight CONSTANT FINAL)
+ Q_PROPERTY(int switchLargestHandleHeight READ switchLargestHandleHeight CONSTANT FINAL)
Q_PROPERTY(int switchDelegateVerticalPadding READ switchDelegateVerticalPadding CONSTANT FINAL)
+ Q_PROPERTY(int textFieldHeight READ textFieldHeight CONSTANT FINAL)
+ Q_PROPERTY(int textFieldHorizontalPadding READ textFieldHorizontalPadding CONSTANT FINAL)
+ Q_PROPERTY(int textFieldVerticalPadding READ textFieldVerticalPadding CONSTANT FINAL)
Q_PROPERTY(int tooltipHeight READ tooltipHeight CONSTANT FINAL)
QML_NAMED_ELEMENT(Material)
@@ -254,18 +267,32 @@ public:
QColor toolTextColor() const;
QColor spinBoxDisabledIconColor() const;
QColor sliderDisabledColor() const;
+ QColor textFieldFilledContainerColor() const;
Q_INVOKABLE QColor color(Color color, Shade shade = Shade500) const;
Q_INVOKABLE QColor shade(const QColor &color, Shade shade) const;
int touchTarget() const;
+ int buttonVerticalPadding() const;
+ Q_INVOKABLE int buttonLeftPadding(bool flat, bool hasIcon) const;
+ Q_INVOKABLE int buttonRightPadding(bool flat, bool hasIcon, bool hasText) const;
int buttonHeight() const;
int delegateHeight() const;
int dialogButtonBoxHeight() const;
+ int dialogTitleFontPixelSize() const;
+ RoundedScale dialogRoundedScale() const;
int frameVerticalPadding() const;
int menuItemHeight() const;
int menuItemVerticalPadding() const;
+ int switchIndicatorWidth() const;
+ int switchIndicatorHeight() const;
+ int switchNormalHandleHeight() const;
+ int switchCheckedHandleHeight() const;
+ int switchLargestHandleHeight() const;
int switchDelegateVerticalPadding() const;
+ int textFieldHeight() const;
+ int textFieldHorizontalPadding() const;
+ int textFieldVerticalPadding() const;
int tooltipHeight() const;
static void initGlobals();
diff --git a/src/quickcontrols/material/qquickmaterialtheme.cpp b/src/quickcontrols/material/qquickmaterialtheme.cpp
index 629c389be5..d121a1a47e 100644
--- a/src/quickcontrols/material/qquickmaterialtheme.cpp
+++ b/src/quickcontrols/material/qquickmaterialtheme.cpp
@@ -6,7 +6,7 @@
#include <QtGui/qpa/qplatformdialoghelper.h>
#include <QtGui/qfont.h>
-#include <QtGui/qfontinfo.h>
+#include <QtGui/qfontdatabase.h>
#include <QtQuickTemplates2/private/qquicktheme_p.h>
QT_BEGIN_NAMESPACE
@@ -21,17 +21,15 @@ void QQuickMaterialTheme::initialize(QQuickTheme *theme)
QFont menuItemFont;
QFont editorFont;
- QFont font;
- font.setFamilies(QStringList{QLatin1String("Roboto")});
- QString family = QFontInfo(font).family();
-
- if (family != QLatin1String("Roboto")) {
- font.setFamilies(QStringList{QLatin1String("Noto")});
- family = QFontInfo(font).family();
+ auto defaultFontFamily = QLatin1String("Roboto");
+ if (!QFontDatabase::hasFamily(defaultFontFamily)) {
+ defaultFontFamily = QLatin1String("Noto"); // fallback
+ if (!QFontDatabase::hasFamily(defaultFontFamily))
+ defaultFontFamily = {};
}
- if (family == QLatin1String("Roboto") || family == QLatin1String("Noto")) {
- const QStringList families{family};
+ if (!defaultFontFamily.isEmpty()) {
+ const QStringList families{defaultFontFamily};
systemFont.setFamilies(families);
buttonFont.setFamilies(families);
toolTipFont.setFamilies(families);
@@ -47,7 +45,6 @@ void QQuickMaterialTheme::initialize(QQuickTheme *theme)
// https://material.io/guidelines/components/buttons.html#buttons-style
buttonFont.setPixelSize(dense ? 13 : 14);
- buttonFont.setCapitalization(QFont::AllUppercase);
buttonFont.setWeight(QFont::Medium);
theme->setFont(QQuickTheme::Button, buttonFont);
theme->setFont(QQuickTheme::TabBar, buttonFont);
diff --git a/src/quickcontrols/material/qquickmaterialtheme_p.h b/src/quickcontrols/material/qquickmaterialtheme_p.h
index 893f441c86..bdaecd1a87 100644
--- a/src/quickcontrols/material/qquickmaterialtheme_p.h
+++ b/src/quickcontrols/material/qquickmaterialtheme_p.h
@@ -15,13 +15,13 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
+#include <QtQuickControls2Material/qtquickcontrols2materialexports.h>
QT_BEGIN_NAMESPACE
class QQuickTheme;
-class QQuickMaterialTheme
+class Q_QUICKCONTROLS2MATERIAL_EXPORT QQuickMaterialTheme
{
public:
static void initialize(QQuickTheme *theme);
diff --git a/src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp b/src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp
index 10aa64b8cf..4911a3e0f2 100644
--- a/src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp
+++ b/src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp
@@ -5,8 +5,6 @@
#include "qquickmaterialtheme_p.h"
#include <QtQuickControls2/private/qquickstyleplugin_p.h>
-#include <QtQuickControls2Impl/private/qquickpaddedrectangle_p.h>
-#include <QtQuickTemplates2/private/qquicktheme_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quickcontrols/material/shaders/+glslcore/RectangularGlow.frag b/src/quickcontrols/material/shaders/+glslcore/RectangularGlow.frag
deleted file mode 100644
index 432d86b5d5..0000000000
--- a/src/quickcontrols/material/shaders/+glslcore/RectangularGlow.frag
+++ /dev/null
@@ -1,25 +0,0 @@
-#version 150
-
-uniform float qt_Opacity;
-uniform float relativeSizeX;
-uniform float relativeSizeY;
-uniform float spread;
-uniform vec4 color;
-
-in vec2 qt_TexCoord0;
-out vec4 fragColor;
-
-float linearstep(float e0, float e1, float x)
-{
- return clamp((x - e0) / (e1 - e0), 0.0, 1.0);
-}
-
-void main()
-{
- float alpha =
- smoothstep(0.0, relativeSizeX, 0.5 - abs(0.5 - qt_TexCoord0.x)) *
- smoothstep(0.0, relativeSizeY, 0.5 - abs(0.5 - qt_TexCoord0.y));
-
- float spreadMultiplier = linearstep(spread, 1.0 - spread, alpha);
- fragColor = color * qt_Opacity * spreadMultiplier * spreadMultiplier;
-}
diff --git a/src/quickcontrols/material/shaders/+hlsl/RectangularGlow.frag b/src/quickcontrols/material/shaders/+hlsl/RectangularGlow.frag
deleted file mode 100644
index 69d9f852fb..0000000000
--- a/src/quickcontrols/material/shaders/+hlsl/RectangularGlow.frag
+++ /dev/null
@@ -1,21 +0,0 @@
-cbuffer ConstantBuffer : register(b0)
-{
- float4x4 qt_Matrix;
- float qt_Opacity;
- float relativeSizeX;
- float relativeSizeY;
- float spread;
- float4 color;
-}
-
-float linearstep(float e0, float e1, float x) { return clamp((x - e0) / (e1 - e0), 0.0, 1.0); }
-
-float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
-{
- float alpha =
- smoothstep(0.0, relativeSizeX, 0.5 - abs(0.5 - coord.x)) *
- smoothstep(0.0, relativeSizeY, 0.5 - abs(0.5 - coord.y));
-
- float spreadMultiplier = linearstep(spread, 1.0 - spread, alpha);
- return color * qt_Opacity * spreadMultiplier * spreadMultiplier;
-}
diff --git a/src/quickcontrols/material/shaders/+qsb/RectangularGlow.frag b/src/quickcontrols/material/shaders/+qsb/RectangularGlow.frag
deleted file mode 100644
index 5cfa2db6ed..0000000000
--- a/src/quickcontrols/material/shaders/+qsb/RectangularGlow.frag
+++ /dev/null
Binary files differ
diff --git a/src/quickcontrols/material/shaders/RectangularGlow.frag b/src/quickcontrols/material/shaders/RectangularGlow.frag
index 40bab5806c..50188d4516 100644
--- a/src/quickcontrols/material/shaders/RectangularGlow.frag
+++ b/src/quickcontrols/material/shaders/RectangularGlow.frag
@@ -1,19 +1,31 @@
-uniform highp float qt_Opacity;
-uniform mediump float relativeSizeX;
-uniform mediump float relativeSizeY;
-uniform mediump float spread;
-uniform lowp vec4 color;
-varying highp vec2 qt_TexCoord0;
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-highp float linearstep(highp float e0, highp float e1, highp float x) {
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float relativeSizeX;
+ float relativeSizeY;
+ float spread;
+ vec4 color;
+} ubuf;
+
+float linearstep(float e0, float e1, float x)
+{
return clamp((x - e0) / (e1 - e0), 0.0, 1.0);
}
-void main() {
- lowp float alpha =
- smoothstep(0.0, relativeSizeX, 0.5 - abs(0.5 - qt_TexCoord0.x)) *
- smoothstep(0.0, relativeSizeY, 0.5 - abs(0.5 - qt_TexCoord0.y));
+void main()
+{
+ float alpha =
+ smoothstep(0.0, ubuf.relativeSizeX, 0.5 - abs(0.5 - qt_TexCoord0.x)) *
+ smoothstep(0.0, ubuf.relativeSizeY, 0.5 - abs(0.5 - qt_TexCoord0.y));
- highp float spreadMultiplier = linearstep(spread, 1.0 - spread, alpha);
- gl_FragColor = color * qt_Opacity * spreadMultiplier * spreadMultiplier;
+ float spreadMultiplier = linearstep(ubuf.spread, 1.0 - ubuf.spread, alpha);
+ fragColor = ubuf.color * ubuf.qt_Opacity * spreadMultiplier * spreadMultiplier;
}
diff --git a/src/quickcontrols/material/shaders/RectangularGlow_rhi.frag b/src/quickcontrols/material/shaders/RectangularGlow_rhi.frag
deleted file mode 100644
index 3e7d2dfe6f..0000000000
--- a/src/quickcontrols/material/shaders/RectangularGlow_rhi.frag
+++ /dev/null
@@ -1,28 +0,0 @@
-#version 440
-
-layout(location = 0) in vec2 qt_TexCoord0;
-layout(location = 0) out vec4 fragColor;
-
-layout(std140, binding = 0) uniform buf {
- mat4 qt_Matrix;
- float qt_Opacity;
- float relativeSizeX;
- float relativeSizeY;
- float spread;
- vec4 color;
-} ubuf;
-
-float linearstep(float e0, float e1, float x)
-{
- return clamp((x - e0) / (e1 - e0), 0.0, 1.0);
-}
-
-void main()
-{
- float alpha =
- smoothstep(0.0, ubuf.relativeSizeX, 0.5 - abs(0.5 - qt_TexCoord0.x)) *
- smoothstep(0.0, ubuf.relativeSizeY, 0.5 - abs(0.5 - qt_TexCoord0.y));
-
- float spreadMultiplier = linearstep(ubuf.spread, 1.0 - ubuf.spread, alpha);
- fragColor = ubuf.color * ubuf.qt_Opacity * spreadMultiplier * spreadMultiplier;
-}
diff --git a/src/quickcontrols/material/shaders/compile.bat b/src/quickcontrols/material/shaders/compile.bat
deleted file mode 100644
index e0ccbb7ed5..0000000000
--- a/src/quickcontrols/material/shaders/compile.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-:: Copyright (C) 2019 The Qt Company Ltd.
-:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o +qsb/RectangularGlow.frag RectangularGlow_rhi.frag
diff --git a/src/quickcontrols/qquickattachedpropertypropagator.cpp b/src/quickcontrols/qquickattachedpropertypropagator.cpp
index ab6f47d4cd..cf752a4248 100644
--- a/src/quickcontrols/qquickattachedpropertypropagator.cpp
+++ b/src/quickcontrols/qquickattachedpropertypropagator.cpp
@@ -8,9 +8,12 @@
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p.h>
+#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcAttached, "qt.quick.controls.attachedpropertypropagator")
+
/*!
\class QQuickAttachedPropertyPropagator
\brief The QQuickAttachedPropertyPropagator class provides a way to
@@ -41,6 +44,9 @@ QT_BEGIN_NAMESPACE
\li Call \l initialize() in the constructor
\li Define set/inherit/propagate/reset functions for each property as needed
\li Reimplement \l attachedParentChange() to handle property inheritance
+ \li Implement a static \c qmlAttachedProperties function and declare the
+ type as an attached QML type with \l QML_ELEMENT and \l QML_ATTACHED,
+ as detailed in \l {Providing Attached Properties}
\endlist
For an example that demonstrates this in depth, see
@@ -70,31 +76,77 @@ static QQuickAttachedPropertyPropagator *attachedObject(const QMetaObject *type,
*/
static QQuickAttachedPropertyPropagator *findAttachedParent(const QMetaObject *ourAttachedType, QObject *objectWeAreAttachedTo)
{
+ qCDebug(lcAttached).noquote() << "findAttachedParent called with" << ourAttachedType->className() << objectWeAreAttachedTo;
+ /*
+ In the Material ComboBox.qml, we have code like this:
+
+ popup: T.Popup {
+ // ...
+ Material.theme: control.Material.theme
+ // ...
+
+ background: Rectangle {
+ //...
+ color: parent.Material.dialogColor
+
+ The Material attached object has to be accessed this way due to
+ deferred execution limitations (see 3e87695fb4b1a5d503c744046e6d9f43a2ae18a6).
+ However, since parent here refers to QQuickPopupItem and not the popup,
+ the color will actually come from the window. If a dark theme was set on
+ the ComboBox, it will not be respected in the background if we don't have
+ the check below.
+ */
+ auto popupItem = qobject_cast<QQuickPopupItem *>(objectWeAreAttachedTo);
+ if (popupItem) {
+ qCDebug(lcAttached).noquote() << "- attachee belongs to popup item" << popupItem << "- checking if it has an attached object";
+ auto popupItemPrivate = QQuickPopupItemPrivate::get(popupItem);
+ QQuickAttachedPropertyPropagator *popupAttached = attachedObject(ourAttachedType, popupItemPrivate->popup);
+ if (popupAttached) {
+ qCDebug(lcAttached).noquote() << "- popup item has attached object" << popupAttached << "- returning";
+ return popupAttached;
+ } else {
+ qCDebug(lcAttached).noquote() << "- popup item does not have attached object";
+ }
+ } else {
+ qCDebug(lcAttached).noquote() << "- attachee does not belong to a popup";
+ }
+
QQuickItem *item = qobject_cast<QQuickItem *>(objectWeAreAttachedTo);
if (item) {
+ qCDebug(lcAttached).noquote() << "- attachee is an item; checking its parent items and popups";
// lookup parent items and popups
QQuickItem *parent = item->parentItem();
while (parent) {
+ qCDebug(lcAttached).noquote() << " - checking parent item" << parent;
QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, parent);
- if (attached)
+ if (attached) {
+ qCDebug(lcAttached).noquote() << " - parent item has attached object" << attached << "- returning";
return attached;
+ }
QQuickPopup *popup = qobject_cast<QQuickPopup *>(parent->parent());
- if (popup)
+ if (popup) {
+ qCDebug(lcAttached).noquote() << " - parent popup has attached object" << attached << "- returning";
return attachedObject(ourAttachedType, popup);
+ }
parent = parent->parentItem();
}
// fallback to item's window
+ qCDebug(lcAttached).noquote() << "- checking parent window" << item->window();
QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, item->window());
- if (attached)
+ if (attached) {
+ qCDebug(lcAttached).noquote() << "- parent window has attached object" << attached << "- returning";
return attached;
+ }
} else {
// lookup popup's window
QQuickPopup *popup = qobject_cast<QQuickPopup *>(objectWeAreAttachedTo);
- if (popup)
+ if (popup) {
+ qCDebug(lcAttached).noquote() << "- attachee is a popup; checking its window";
return attachedObject(ourAttachedType, popup->popupItem()->window());
+ }
}
// lookup parent window
@@ -102,16 +154,20 @@ static QQuickAttachedPropertyPropagator *findAttachedParent(const QMetaObject *o
if (window) {
// It doesn't seem like a parent window can be anything but transient in Qt Quick.
QQuickWindow *parentWindow = qobject_cast<QQuickWindow *>(window->transientParent());
+ qCDebug(lcAttached).noquote() << "- attachee is a window; checking its parent window" << parentWindow;
if (parentWindow) {
QQuickAttachedPropertyPropagator *attached = attachedObject(ourAttachedType, parentWindow);
- if (attached)
+ if (attached) {
+ qCDebug(lcAttached).noquote() << "- parent window has attached object" << attached << "- returning";
return attached;
+ }
}
}
// fallback to engine (global)
if (objectWeAreAttachedTo) {
QQmlEngine *engine = qmlEngine(objectWeAreAttachedTo);
+ qCDebug(lcAttached).noquote() << "- falling back to engine" << engine;
if (engine) {
QByteArray name = QByteArray("_q_") + ourAttachedType->className();
QQuickAttachedPropertyPropagator *attached = engine->property(name).value<QQuickAttachedPropertyPropagator *>();
@@ -197,6 +253,7 @@ public:
void setAttachedParent(QQuickAttachedPropertyPropagator *parent);
void itemWindowChanged(QQuickWindow *window);
+ void transientParentWindowChanged(QWindow *newTransientParent);
void itemParentChanged(QQuickItem *item, QQuickItem *parent) override;
QList<QQuickAttachedPropertyPropagator *> attachedChildren;
@@ -205,19 +262,23 @@ public:
void QQuickAttachedPropertyPropagatorPrivate::attachTo(QObject *object)
{
- QQuickItem *item = findAttachedItem(object);
- if (item) {
+ if (QQuickItem *item = findAttachedItem(object)) {
connect(item, &QQuickItem::windowChanged, this, &QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged);
QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Parent);
+ } else if (auto *window = qobject_cast<QQuickWindow *>(object)) {
+ QObjectPrivate::connect(window, &QWindow::transientParentChanged, this,
+ &QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged);
}
}
void QQuickAttachedPropertyPropagatorPrivate::detachFrom(QObject *object)
{
- QQuickItem *item = findAttachedItem(object);
- if (item) {
+ if (QQuickItem *item = findAttachedItem(object)) {
disconnect(item, &QQuickItem::windowChanged, this, &QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged);
QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent);
+ } else if (auto *window = qobject_cast<QQuickWindow *>(object)) {
+ QObjectPrivate::disconnect(window, &QWindow::transientParentChanged,
+ this, &QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged);
}
}
@@ -246,31 +307,64 @@ void QQuickAttachedPropertyPropagatorPrivate::setAttachedParent(QQuickAttachedPr
return;
QQuickAttachedPropertyPropagator *oldParent = attachedParent;
- if (attachedParent)
+ qCDebug(lcAttached).noquote() << "setAttachedParent called on" << q << "with parent" << parent;
+ if (attachedParent) {
+ qCDebug(lcAttached).noquote() << "- removing ourselves as an attached child of" << attachedParent;
QQuickAttachedPropertyPropagatorPrivate::get(attachedParent)->attachedChildren.removeOne(q);
+ }
attachedParent = parent;
- if (parent)
+ if (parent) {
+ qCDebug(lcAttached).noquote() << "- adding ourselves as an attached child of" << parent;
QQuickAttachedPropertyPropagatorPrivate::get(parent)->attachedChildren.append(q);
+ }
q->attachedParentChange(parent, oldParent);
}
+/*
+ If there's e.g. code like this:
+
+ Behavior on Material.elevation {}
+
+ The meta type will be something like QQuickMaterialStyle_QML_125,
+ whereas QQmlMetaType::attachedPropertiesFunc only has attached
+ property data for QQuickMaterialStyle (i.e. attached property types
+ created from C++). We work around this by finding the first C++
+ meta object, which works even for attached types created in QML.
+*/
+const QMetaObject *firstCppMetaObject(QQuickAttachedPropertyPropagator *propagator)
+{
+ return QQmlData::ensurePropertyCache(propagator)->firstCppMetaObject();
+}
+
void QQuickAttachedPropertyPropagatorPrivate::itemWindowChanged(QQuickWindow *window)
{
Q_Q(QQuickAttachedPropertyPropagator);
QQuickAttachedPropertyPropagator *attachedParent = nullptr;
- QQuickItem *item = qobject_cast<QQuickItem *>(q->sender());
- if (item)
- attachedParent = findAttachedParent(q->metaObject(), item);
+ qCDebug(lcAttached).noquote() << "window of" << q << "changed to" << window;
+
+ attachedParent = findAttachedParent(firstCppMetaObject(q), q->parent());
+ if (!attachedParent)
+ attachedParent = attachedObject(firstCppMetaObject(q), window);
+ setAttachedParent(attachedParent);
+}
+
+void QQuickAttachedPropertyPropagatorPrivate::transientParentWindowChanged(QWindow *newTransientParent)
+{
+ Q_Q(QQuickAttachedPropertyPropagator);
+ QQuickAttachedPropertyPropagator *attachedParent = nullptr;
+ qCDebug(lcAttached).noquote() << "transient parent window of" << q << "changed to" << newTransientParent;
+ attachedParent = findAttachedParent(firstCppMetaObject(q), q->parent());
if (!attachedParent)
- attachedParent = attachedObject(q->metaObject(), window);
+ attachedParent = attachedObject(firstCppMetaObject(q), newTransientParent);
setAttachedParent(attachedParent);
}
void QQuickAttachedPropertyPropagatorPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
{
Q_Q(QQuickAttachedPropertyPropagator);
+ Q_UNUSED(item);
Q_UNUSED(parent);
- setAttachedParent(findAttachedParent(q->metaObject(), item));
+ setAttachedParent(findAttachedParent(firstCppMetaObject(q), q->parent()));
}
/*!
@@ -348,13 +442,19 @@ QQuickAttachedPropertyPropagator *QQuickAttachedPropertyPropagator::attachedPare
void QQuickAttachedPropertyPropagator::initialize()
{
Q_D(QQuickAttachedPropertyPropagator);
+ qCDebug(lcAttached) << "initialize called for" << parent() << "- looking for attached parent...";
QQuickAttachedPropertyPropagator *attachedParent = findAttachedParent(metaObject(), parent());
if (attachedParent)
d->setAttachedParent(attachedParent);
const QList<QQuickAttachedPropertyPropagator *> attachedChildren = findAttachedChildren(metaObject(), parent());
- for (QQuickAttachedPropertyPropagator *child : attachedChildren)
+ qCDebug(lcAttached) << "- found" << attachedChildren.size() << "attached children:";
+ for (QQuickAttachedPropertyPropagator *child : attachedChildren) {
+ qCDebug(lcAttached) << " -" << child->parent();
QQuickAttachedPropertyPropagatorPrivate::get(child)->setAttachedParent(this);
+ }
+
+ qCDebug(lcAttached) << "... finished initializing";
}
/*!
@@ -375,6 +475,22 @@ void QQuickAttachedPropertyPropagator::attachedParentChange(QQuickAttachedProper
Q_UNUSED(oldParent);
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickAttachedPropertyPropagator *propagator)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace().noquote();
+ if (!propagator) {
+ debug << "QQuickAttachedPropertyPropagator(nullptr)";
+ return debug;
+ }
+
+ // Cast to QObject to avoid recursion.
+ debug << static_cast<const QObject *>(propagator) << " (which is attached to " << propagator->parent() << ')';
+ return debug;
+}
+#endif
+
QT_END_NAMESPACE
#include "moc_qquickattachedpropertypropagator.cpp"
diff --git a/src/quickcontrols/qquickattachedpropertypropagator.h b/src/quickcontrols/qquickattachedpropertypropagator.h
index ec5ff7991a..de3e20bf3f 100644
--- a/src/quickcontrols/qquickattachedpropertypropagator.h
+++ b/src/quickcontrols/qquickattachedpropertypropagator.h
@@ -29,6 +29,10 @@ protected:
virtual void attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent);
private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_QUICKCONTROLS2_EXPORT QDebug operator<<(QDebug debug, const QQuickAttachedPropertyPropagator *propagator);
+#endif
+
Q_DECLARE_PRIVATE(QQuickAttachedPropertyPropagator)
};
diff --git a/src/quickcontrols/qquickstyle.cpp b/src/quickcontrols/qquickstyle.cpp
index 8befd80ea4..a40e9535e2 100644
--- a/src/quickcontrols/qquickstyle.cpp
+++ b/src/quickcontrols/qquickstyle.cpp
@@ -7,7 +7,9 @@
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
#include <QtCore/qdebug.h>
+#if QT_CONFIG(settings)
#include <QtCore/qsettings.h>
+#endif
#include <QtCore/qfileselector.h>
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qloggingcategory.h>
@@ -304,6 +306,7 @@ QSharedPointer<QSettings> QQuickStylePrivate::settings(const QString &group)
return QSharedPointer<QSettings>(settings);
}
#endif // QT_NO_SETTINGS
+ Q_UNUSED(group)
return QSharedPointer<QSettings>();
}
@@ -386,7 +389,7 @@ bool QQuickStylePrivate::isDarkSystemTheme()
{
const bool dark = [](){
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
- return theme->appearance() == Qt::Appearance::Dark;
+ return theme->colorScheme() == Qt::ColorScheme::Dark;
return false;
}();
return dark;
diff --git a/src/quickcontrols/qquickstyleplugin.cpp b/src/quickcontrols/qquickstyleplugin.cpp
index 8f0a71c9ad..caae034970 100644
--- a/src/quickcontrols/qquickstyleplugin.cpp
+++ b/src/quickcontrols/qquickstyleplugin.cpp
@@ -54,18 +54,31 @@ void QQuickStylePlugin::registerTypes(const char *uri)
theme = createTheme(effectiveCurrentStyleName);
}
- if (name() != effectiveCurrentStyleName) {
- qCDebug(lcStylePlugin).nospace() << "theme does not belong to current style ("
- << effectiveCurrentStyleName << "); not calling initializeTheme()";
+ // The primary fallback is the style set by the user. We need to check for that here
+ // so that we can ensure that fallback styles' themes are initialized (QTBUG-117403)
+ // without also allowing the Basic style to be initialized, as it is a secondary fallback
+ // for every built-in style (and only built-in styles can be fallbacks).
+ const bool thisPluginBelongsToCurrentStyle = name() == effectiveCurrentStyleName;
+ const bool isPrimaryFallback = name() == QQuickStylePrivate::fallbackStyle();
+ if (!thisPluginBelongsToCurrentStyle && !isPrimaryFallback) {
+ qCDebug(lcStylePlugin).nospace() << "this style plugin does not belong to the current ("
+ << effectiveCurrentStyleName << ") or fallback (" << QQuickStylePrivate::fallbackStyle()
+ << ") style; not calling initializeTheme()";
return;
}
- qCDebug(lcStylePlugin) << "theme has not yet been initialized; calling initializeTheme()";
+ if (thisPluginBelongsToCurrentStyle) {
+ qCDebug(lcStylePlugin).nospace() << "this style plugin belongs to the current style "
+ << effectiveCurrentStyleName << "; calling initializeTheme()";
+ } else {
+ qCDebug(lcStylePlugin).nospace() << "this style plugin belongs to the fallback style "
+ << QQuickStylePrivate::fallbackStyle() << "; calling initializeTheme()";
+ }
initializeTheme(theme);
- connect(QGuiApplication::styleHints(), &QStyleHints::appearanceChanged,
+ connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged,
this, &QQuickStylePlugin::updateTheme);
- if (!styleName.isEmpty())
+ if (!isPrimaryFallback && !styleName.isEmpty())
QFileSelectorPrivate::addStatics(QStringList() << styleName);
}
@@ -75,7 +88,7 @@ void QQuickStylePlugin::unregisterTypes()
if (!QQuickThemePrivate::instance)
return;
- disconnect(QGuiApplication::styleHints(), &QStyleHints::appearanceChanged,
+ disconnect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged,
this, &QQuickStylePlugin::updateTheme);
// Not every style has a plugin - some styles are QML-only. So, we clean this
diff --git a/src/quickcontrols/qquickstyleplugin_p.h b/src/quickcontrols/qquickstyleplugin_p.h
index d0f690205c..5458892511 100644
--- a/src/quickcontrols/qquickstyleplugin_p.h
+++ b/src/quickcontrols/qquickstyleplugin_p.h
@@ -17,7 +17,6 @@
#include <QtQml/qqmlextensionplugin.h>
#include <QtQuickControls2/qtquickcontrols2global.h>
-#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quickcontrols/universal/CMakeLists.txt b/src/quickcontrols/universal/CMakeLists.txt
index 6f5dc1bb25..b9ab5d9aaa 100644
--- a/src/quickcontrols/universal/CMakeLists.txt
+++ b/src/quickcontrols/universal/CMakeLists.txt
@@ -92,7 +92,9 @@ set_source_files_properties(VerticalHeaderView.qml PROPERTIES
QT_QML_SOURCE_VERSIONS "2.15;6.0"
)
-qt_internal_add_qml_module(qtquickcontrols2universalstyleplugin
+add_subdirectory(impl)
+
+qt_internal_add_qml_module(QuickControls2Universal
URI "QtQuick.Controls.Universal"
VERSION "${PROJECT_VERSION}"
PAST_MAJOR_VERSIONS 2
@@ -107,7 +109,6 @@ qt_internal_add_qml_module(qtquickcontrols2universalstyleplugin
SOURCES
qquickuniversalstyle.cpp qquickuniversalstyle_p.h
qquickuniversaltheme.cpp qquickuniversaltheme_p.h
- qtquickcontrols2universalstyleplugin.cpp
QML_FILES
${qml_files}
DEFINES
@@ -119,11 +120,23 @@ qt_internal_add_qml_module(qtquickcontrols2universalstyleplugin
Qt::QmlPrivate
Qt::QuickControls2ImplPrivate
Qt::QuickControls2Private
+ Qt::QuickControls2UniversalStyleImpl
Qt::QuickPrivate
Qt::QuickTemplates2Private
+ GENERATE_CPP_EXPORTS
+)
+
+target_sources(qtquickcontrols2universalstyleplugin
+ PRIVATE
+ qtquickcontrols2universalstyleplugin.cpp
)
-qt_internal_add_resource(qtquickcontrols2universalstyleplugin "qtquickcontrols2universalstyleplugin"
+target_link_libraries(qtquickcontrols2universalstyleplugin
+ PRIVATE
+ Qt::QuickControls2Private
+)
+
+qt_internal_add_resource(QuickControls2Universal "qtquickcontrols2universalstyle"
PREFIX
"/qt-project.org/imports/QtQuick/Controls/Universal"
FILES
@@ -145,8 +158,6 @@ qt_internal_add_resource(qtquickcontrols2universalstyleplugin "qtquickcontrols2u
"images/rightarrow@4x.png"
)
-add_subdirectory(impl)
-
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2universalstyleplugin quickwindow)
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2universalstyleplugin
qtquickcontrols2universalstyleimplplugin)
diff --git a/src/quickcontrols/universal/ComboBox.qml b/src/quickcontrols/universal/ComboBox.qml
index 9a812388aa..0793416b69 100644
--- a/src/quickcontrols/universal/ComboBox.qml
+++ b/src/quickcontrols/universal/ComboBox.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Window
import QtQuick.Controls.impl
@@ -22,8 +24,11 @@ T.ComboBox {
Universal.theme: editable && activeFocus ? Universal.Light : undefined
delegate: ItemDelegate {
+ required property var model
+ required property int index
+
width: ListView.view.width
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ text: model[control.textRole]
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
highlighted: control.highlightedIndex === index
hoverEnabled: control.hoverEnabled
diff --git a/src/quickcontrols/universal/Dial.qml b/src/quickcontrols/universal/Dial.qml
index 6f86b00722..df52ea1aa5 100644
--- a/src/quickcontrols/universal/Dial.qml
+++ b/src/quickcontrols/universal/Dial.qml
@@ -41,12 +41,13 @@ T.Dial {
transform: [
Translate {
- y: -control.background.height * 0.4 + control.handle.height / 2
+ y: -control.background.height * 0.4
+ + (control.handle ? control.handle.height / 2 : 0)
},
Rotation {
angle: control.angle
- origin.x: control.handle.width / 2
- origin.y: control.handle.height / 2
+ origin.x: control.handle ? control.handle.width / 2 : 0
+ origin.y: control.handle ? control.handle.height / 2 : 0
}
]
}
diff --git a/src/quickcontrols/universal/DialogButtonBox.qml b/src/quickcontrols/universal/DialogButtonBox.qml
index 826a13efd3..4a7e4f0ede 100644
--- a/src/quickcontrols/universal/DialogButtonBox.qml
+++ b/src/quickcontrols/universal/DialogButtonBox.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import QtQuick
@@ -12,7 +12,7 @@ T.DialogButtonBox {
(control.count === 1 ? implicitContentWidth * 2 : implicitContentWidth) + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
- contentWidth: contentItem.contentWidth
+ contentWidth: (contentItem as ListView)?.contentWidth
spacing: 4
padding: 24
diff --git a/src/quickcontrols/universal/HorizontalHeaderView.qml b/src/quickcontrols/universal/HorizontalHeaderView.qml
index c72af49c57..f792a1e690 100644
--- a/src/quickcontrols/universal/HorizontalHeaderView.qml
+++ b/src/quickcontrols/universal/HorizontalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Templates as T
@@ -19,6 +21,10 @@ T.HorizontalHeaderView {
implicitHeight: Math.max(1, contentHeight)
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -28,11 +34,9 @@ T.HorizontalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: Color.transparent(control.Universal.foreground, enabled ? 1.0 : 0.2)
diff --git a/src/quickcontrols/universal/Menu.qml b/src/quickcontrols/universal/Menu.qml
index 64dac0c4b8..03a5b23029 100644
--- a/src/quickcontrols/universal/Menu.qml
+++ b/src/quickcontrols/universal/Menu.qml
@@ -23,8 +23,8 @@ T.Menu {
implicitHeight: contentHeight
model: control.contentModel
interactive: Window.window
- ? contentHeight + control.topPadding + control.bottomPadding > Window.window.height
- : false
+ ? contentHeight + control.topPadding + control.bottomPadding > control.height
+ : false
clip: true
currentIndex: control.currentIndex
diff --git a/src/quickcontrols/universal/VerticalHeaderView.qml b/src/quickcontrols/universal/VerticalHeaderView.qml
index c7223be355..6e4540d66a 100644
--- a/src/quickcontrols/universal/VerticalHeaderView.qml
+++ b/src/quickcontrols/universal/VerticalHeaderView.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Templates as T
@@ -19,6 +21,10 @@ T.VerticalHeaderView {
implicitHeight: syncView ? syncView.height : 0
delegate: Rectangle {
+ id: delegate
+
+ required property var model
+
// Qt6: add cellPadding (and font etc) as public API in headerview
readonly property real cellPadding: 8
@@ -28,11 +34,9 @@ T.VerticalHeaderView {
Label {
id: text
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
- : model[control.textRole])
- : modelData
- width: parent.width
- height: parent.height
+ text: delegate.model[control.textRole]
+ width: delegate.width
+ height: delegate.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: Color.transparent(control.Universal.foreground, enabled ? 1.0 : 0.2)
diff --git a/src/quickcontrols/universal/impl/CMakeLists.txt b/src/quickcontrols/universal/impl/CMakeLists.txt
index 078e42e217..3bbd8e5b21 100644
--- a/src/quickcontrols/universal/impl/CMakeLists.txt
+++ b/src/quickcontrols/universal/impl/CMakeLists.txt
@@ -11,7 +11,7 @@ set(qml_files
"SwitchIndicator.qml"
)
-qt_internal_add_qml_module(qtquickcontrols2universalstyleimplplugin
+qt_internal_add_qml_module(QuickControls2UniversalStyleImpl
URI "QtQuick.Controls.Universal.impl"
VERSION "${PROJECT_VERSION}"
PAST_MAJOR_VERSIONS 2
@@ -19,7 +19,6 @@ qt_internal_add_qml_module(qtquickcontrols2universalstyleimplplugin
DEPENDENCIES
QtQuick/auto
PLUGIN_TARGET qtquickcontrols2universalstyleimplplugin
- NO_PLUGIN_OPTIONAL
SOURCES
qquickuniversalbusyindicator.cpp qquickuniversalbusyindicator_p.h
qquickuniversalfocusrectangle.cpp qquickuniversalfocusrectangle_p.h
diff --git a/src/quickcontrols/universal/impl/qquickuniversalbusyindicator_p.h b/src/quickcontrols/universal/impl/qquickuniversalbusyindicator_p.h
index 1000bbab09..ef785e685e 100644
--- a/src/quickcontrols/universal/impl/qquickuniversalbusyindicator_p.h
+++ b/src/quickcontrols/universal/impl/qquickuniversalbusyindicator_p.h
@@ -52,6 +52,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickUniversalBusyIndicator)
-
#endif // QQUICKUNIVERSALBUSYINDICATOR_P_H
diff --git a/src/quickcontrols/universal/impl/qquickuniversalprogressbar.cpp b/src/quickcontrols/universal/impl/qquickuniversalprogressbar.cpp
index 0ab47e8804..e5548992db 100644
--- a/src/quickcontrols/universal/impl/qquickuniversalprogressbar.cpp
+++ b/src/quickcontrols/universal/impl/qquickuniversalprogressbar.cpp
@@ -12,10 +12,10 @@
QT_BEGIN_NAMESPACE
-static const int PhaseCount = 4;
+static const int QupbPhaseCount = 4;
static const int EllipseCount = 5;
-static const int Interval = 167;
-static const int TotalDuration = 3917;
+static const int QupbInterval = 167;
+static const int QupbTotalDuration = 3917;
static const int VisibleDuration = 3000;
static const qreal EllipseDiameter = 4;
static const qreal EllipseOffset = 4;
@@ -42,15 +42,15 @@ private:
};
bool m_indeterminate = false;
- Phase m_borderPhases[PhaseCount];
- Phase m_ellipsePhases[PhaseCount];
+ Phase m_borderPhases[QupbPhaseCount];
+ Phase m_ellipsePhases[QupbPhaseCount];
};
QQuickUniversalProgressBarNode::QQuickUniversalProgressBarNode(QQuickUniversalProgressBar *item)
: QQuickAnimatedNode(item)
{
setLoopCount(Infinite);
- setDuration(TotalDuration);
+ setDuration(QupbTotalDuration);
m_borderPhases[0] = Phase( 500, -50, 0);
m_borderPhases[1] = Phase(1500, 0, 0);
@@ -79,7 +79,7 @@ void QQuickUniversalProgressBarNode::updateCurrentTime(int time)
{
qreal from = ContainerAnimationStartPosition;
qreal to = from + ContainerAnimationEndPosition * width;
- qreal progress = static_cast<qreal>(time) / TotalDuration;
+ qreal progress = static_cast<qreal>(time) / QupbTotalDuration;
qreal dx = from + (to - from) * progress;
QMatrix4x4 matrix;
@@ -98,8 +98,8 @@ void QQuickUniversalProgressBarNode::updateCurrentTime(int time)
QSGOpacityNode *opacityNode = static_cast<QSGOpacityNode *>(ellipseNode->firstChild());
Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType);
- int begin = nodeIndex * Interval;
- int end = VisibleDuration + nodeIndex * Interval;
+ int begin = nodeIndex * QupbInterval;
+ int end = VisibleDuration + nodeIndex * QupbInterval;
bool visible = time >= begin && time <= end;
opacityNode->setOpacity(visible ? 1.0 : 0.0);
@@ -107,7 +107,7 @@ void QQuickUniversalProgressBarNode::updateCurrentTime(int time)
if (visible) {
{
int phaseIndex, remain = time, elapsed = 0;
- for (phaseIndex = 0; phaseIndex < PhaseCount - 1; ++phaseIndex) {
+ for (phaseIndex = 0; phaseIndex < QupbPhaseCount - 1; ++phaseIndex) {
if (remain <= m_borderPhases[phaseIndex].duration + begin)
break;
remain -= m_borderPhases[phaseIndex].duration;
@@ -130,7 +130,7 @@ void QQuickUniversalProgressBarNode::updateCurrentTime(int time)
curve.addCubicBezierSegment(QPointF(0.4, 0.0), QPointF(0.6, 1.0), QPointF(1.0, 1.0));
int phaseIndex, remain = time, elapsed = 0;
- for (phaseIndex = 0; phaseIndex < PhaseCount - 1; ++phaseIndex) {
+ for (phaseIndex = 0; phaseIndex < QupbPhaseCount - 1; ++phaseIndex) {
if (remain <= m_ellipsePhases[phaseIndex].duration + begin)
break;
remain -= m_ellipsePhases[phaseIndex].duration;
diff --git a/src/quickcontrols/universal/impl/qquickuniversalprogressbar_p.h b/src/quickcontrols/universal/impl/qquickuniversalprogressbar_p.h
index a4bbae8cb5..6def3d2842 100644
--- a/src/quickcontrols/universal/impl/qquickuniversalprogressbar_p.h
+++ b/src/quickcontrols/universal/impl/qquickuniversalprogressbar_p.h
@@ -54,6 +54,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickUniversalProgressBar)
-
#endif // QQUICKUNIVERSALPROGRESSBAR_P_H
diff --git a/src/quickcontrols/universal/qquickuniversalstyle.cpp b/src/quickcontrols/universal/qquickuniversalstyle.cpp
index cacf20a75f..9710bc2a4b 100644
--- a/src/quickcontrols/universal/qquickuniversalstyle.cpp
+++ b/src/quickcontrols/universal/qquickuniversalstyle.cpp
@@ -4,7 +4,9 @@
#include "qquickuniversalstyle_p.h"
#include <QtCore/qdebug.h>
+#if QT_CONFIG(settings)
#include <QtCore/qsettings.h>
+#endif
#include <QtQml/qqmlinfo.h>
#include <QtQuickControls2/private/qquickstyle_p.h>
diff --git a/src/quickcontrols/universal/qquickuniversalstyle_p.h b/src/quickcontrols/universal/qquickuniversalstyle_p.h
index 024fde2192..c97483a5bc 100644
--- a/src/quickcontrols/universal/qquickuniversalstyle_p.h
+++ b/src/quickcontrols/universal/qquickuniversalstyle_p.h
@@ -18,12 +18,13 @@
#include <QtGui/qcolor.h>
#include <QtQml/qqml.h>
#include <QtQuickControls2/qquickattachedpropertypropagator.h>
+#include <QtQuickControls2Universal/qtquickcontrols2universalexports.h>
QT_BEGIN_NAMESPACE
class QQuickUniversalStylePrivate;
-class QQuickUniversalStyle : public QQuickAttachedPropertyPropagator
+class Q_QUICKCONTROLS2UNIVERSAL_EXPORT QQuickUniversalStyle : public QQuickAttachedPropertyPropagator
{
Q_OBJECT
Q_PROPERTY(Theme theme READ theme WRITE setTheme RESET resetTheme NOTIFY themeChanged FINAL)
diff --git a/src/quickcontrols/universal/qquickuniversaltheme.cpp b/src/quickcontrols/universal/qquickuniversaltheme.cpp
index fff5400986..286106d88c 100644
--- a/src/quickcontrols/universal/qquickuniversaltheme.cpp
+++ b/src/quickcontrols/universal/qquickuniversaltheme.cpp
@@ -3,7 +3,7 @@
#include "qquickuniversaltheme_p.h"
-#include <QtGui/qfontinfo.h>
+#include <QtGui/qfontdatabase.h>
#include <QtQuickTemplates2/private/qquicktheme_p.h>
QT_BEGIN_NAMESPACE
@@ -14,8 +14,9 @@ void QQuickUniversalTheme::initialize(QQuickTheme *theme)
QFont groupBoxTitleFont;
QFont tabButtonFont;
- const QFont font(QLatin1String("Segoe UI"));
- if (QFontInfo(font).family() == QLatin1String("Segoe UI")) {
+ const QLatin1String segoeUiFamilyName("Segoe UI");
+ if (QFontDatabase::families().contains(segoeUiFamilyName)) {
+ const QFont font(segoeUiFamilyName);
const QStringList families{font.family()};
systemFont.setFamilies(families);
groupBoxTitleFont.setFamilies(families);
diff --git a/src/quickcontrols/universal/qquickuniversaltheme_p.h b/src/quickcontrols/universal/qquickuniversaltheme_p.h
index 84789c5da1..77373aa6b9 100644
--- a/src/quickcontrols/universal/qquickuniversaltheme_p.h
+++ b/src/quickcontrols/universal/qquickuniversaltheme_p.h
@@ -15,13 +15,13 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
+#include <QtQuickControls2Universal/qtquickcontrols2universalexports.h>
QT_BEGIN_NAMESPACE
class QQuickTheme;
-class QQuickUniversalTheme
+class Q_QUICKCONTROLS2UNIVERSAL_EXPORT QQuickUniversalTheme
{
public:
static void initialize(QQuickTheme *theme);
diff --git a/src/quickcontrols/universal/qtquickcontrols2universalstyleplugin.cpp b/src/quickcontrols/universal/qtquickcontrols2universalstyleplugin.cpp
index 683c966833..4bdb1216b6 100644
--- a/src/quickcontrols/universal/qtquickcontrols2universalstyleplugin.cpp
+++ b/src/quickcontrols/universal/qtquickcontrols2universalstyleplugin.cpp
@@ -5,7 +5,6 @@
#include "qquickuniversaltheme_p.h"
#include <QtQuickControls2/private/qquickstyleplugin_p.h>
-#include <QtQuickTemplates2/private/qquicktheme_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quickcontrols/windows/ApplicationWindow.qml b/src/quickcontrols/windows/ApplicationWindow.qml
new file mode 100644
index 0000000000..dc3094c009
--- /dev/null
+++ b/src/quickcontrols/windows/ApplicationWindow.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick.NativeStyle
+import QtQuick.Templates as T
+
+T.ApplicationWindow {
+ color: palette.window
+}
diff --git a/src/quickcontrols/windows/CMakeLists.txt b/src/quickcontrols/windows/CMakeLists.txt
index b5319fef52..55c57a956c 100644
--- a/src/quickcontrols/windows/CMakeLists.txt
+++ b/src/quickcontrols/windows/CMakeLists.txt
@@ -6,16 +6,24 @@
#####################################################################
set(qml_files
+ "ApplicationWindow.qml"
"Button.qml"
"CheckBox.qml"
+ "CheckDelegate.qml"
"ComboBox.qml"
+ "DelayButton.qml"
"Frame.qml"
"GroupBox.qml"
+ "ItemDelegate.qml"
"ProgressBar.qml"
"RadioButton.qml"
+ "RadioDelegate.qml"
+ "RangeSlider.qml"
"SelectionRectangle.qml"
"Slider.qml"
"SpinBox.qml"
+ "Switch.qml"
+ "SwitchDelegate.qml"
"TextArea.qml"
"TextField.qml"
"ScrollBar.qml"
@@ -44,10 +52,13 @@ qt_internal_add_qml_module(qtquickcontrols2windowsstyleplugin
Qt::GuiPrivate
Qt::QmlPrivate
Qt::QuickControls2Private
+ Qt::QuickControls2WindowsStyleImpl
Qt::QuickPrivate
Qt::QuickTemplates2Private
)
+add_subdirectory(impl)
+
# Native style is a dependency of the Windows style.
_qt_internal_add_qml_static_plugin_dependency(qtquickcontrols2windowsstyleplugin
qtquickcontrols2nativestyleplugin)
diff --git a/src/quickcontrols/windows/CheckDelegate.qml b/src/quickcontrols/windows/CheckDelegate.qml
new file mode 100644
index 0000000000..ce020161c8
--- /dev/null
+++ b/src/quickcontrols/windows/CheckDelegate.qml
@@ -0,0 +1,75 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.NativeStyle as NativeStyle
+
+T.CheckDelegate {
+ id: control
+
+ readonly property bool __nativeIndicator: indicator instanceof NativeStyle.StyleItem
+ readonly property bool __notCustomizable: true
+ readonly property Item __focusFrameTarget: indicator
+ readonly property Item __focusFrameStyleItem: indicator
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ spacing: 6
+ padding: 6
+
+ contentItem: NativeStyle.DefaultItemDelegateIconLabel {
+ color: control.highlighted ? control.palette.button : control.palette.windowText
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ indicator: NativeStyle.CheckDelegate {
+ x: control.text
+ ? (control.mirrored ? control.leftPadding : control.width - width - control.rightPadding)
+ : control.leftPadding + (control.availableWidth - width) / 2
+ // The rendering gets messed up when rendering on sub-pixel positions.
+ y: control.topPadding + Math.round((control.availableHeight - height) / 2)
+ contentWidth: control.implicitContentWidth
+ contentHeight: control.implicitContentHeight
+ control: control
+ useNinePatchImage: false
+ overrideState: NativeStyle.StyleItem.NeverHovered
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ NativeStyle.CheckDelegate {
+ id: hoverCheckDelegate
+ control: control
+ x: control.indicator.x
+ y: control.indicator.y
+ z: control.indicator.z + 1
+ width: control.indicator.width
+ height: control.indicator.height
+ useNinePatchImage: false
+ overrideState: NativeStyle.StyleItem.AlwaysHovered
+ opacity: control.hovered ? 1 : 0
+ visible: opacity !== 0
+ Behavior on opacity {
+ NumberAnimation {
+ duration: hoverCheckDelegate.transitionDuration
+ }
+ }
+ }
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 20
+ color: Qt.darker(control.highlighted
+ ? control.palette.highlight : control.palette.button, control.down ? 1.05 : 1)
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+}
diff --git a/src/quickcontrols/windows/ComboBox.qml b/src/quickcontrols/windows/ComboBox.qml
index 66fffecb46..e8a0ed738d 100644
--- a/src/quickcontrols/windows/ComboBox.qml
+++ b/src/quickcontrols/windows/ComboBox.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Window
import QtQuick.Controls
@@ -56,8 +58,11 @@ T.ComboBox {
}
delegate: ItemDelegate {
+ required property var model
+ required property int index
+
width: ListView.view.width
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ text: model[control.textRole]
palette.text: control.palette.text
palette.highlightedText: control.palette.highlightedText
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
diff --git a/src/quickcontrols/windows/DelayButton.qml b/src/quickcontrols/windows/DelayButton.qml
new file mode 100644
index 0000000000..935e5cc86a
--- /dev/null
+++ b/src/quickcontrols/windows/DelayButton.qml
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import QtQuick.NativeStyle as NativeStyle
+
+T.DelayButton {
+ id: control
+
+ readonly property bool __nativeBackground: background instanceof NativeStyle.StyleItem
+ readonly property bool __notCustomizable: true
+ readonly property Item __focusFrameTarget: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ leftPadding: __nativeBackground ? background.contentPadding.left : 5
+ rightPadding: __nativeBackground ? background.contentPadding.right : 5
+ topPadding: __nativeBackground ? background.contentPadding.top : 5
+ bottomPadding: __nativeBackground ? background.contentPadding.bottom : 5
+
+ icon.width: 24
+ icon.height: 24
+ icon.color: control.palette.buttonText
+
+ transition: Transition {
+ NumberAnimation {
+ duration: control.delay * (control.pressed ? 1.0 - control.progress : 0.3 * control.progress)
+ }
+ }
+
+ background: NativeStyle.DelayButton {
+ control: control
+ contentWidth: contentItem.implicitWidth
+ contentHeight: contentItem.implicitHeight
+ useNinePatchImage: false
+ overrideState: NativeStyle.StyleItem.NeverHovered
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ NativeStyle.DelayButton {
+ id: hoverButton
+ control: control
+ x: background.x
+ y: background.y
+ width: background.width
+ height: background.height
+ useNinePatchImage: false
+ overrideState: NativeStyle.StyleItem.AlwaysHovered
+ opacity: control.hovered ? 1 : 0
+ visible: opacity !== 0
+ Behavior on opacity { NumberAnimation { duration: hoverButton.transitionDuration } }
+ }
+
+ contentItem: IconLabel {
+ spacing: control.spacing
+ mirrored: control.mirrored
+ display: control.display
+
+ icon: control.icon
+ text: control.text
+ font: control.font
+ color: control.palette.buttonText
+
+ readonly property bool __ignoreNotCustomizable: true
+
+ // Delay progress bar.
+ Rectangle {
+ x: (parent.width - parent.implicitWidth) / 2
+ y: parent.height + 1
+ width: control.progress * parent.implicitWidth
+ height: 1
+ color: control.palette.accent
+ scale: control.mirrored ? -1 : 1
+ }
+ }
+}
diff --git a/src/quickcontrols/windows/ItemDelegate.qml b/src/quickcontrols/windows/ItemDelegate.qml
new file mode 100644
index 0000000000..879c4532ec
--- /dev/null
+++ b/src/quickcontrols/windows/ItemDelegate.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick.NativeStyle as NativeStyle
+
+NativeStyle.DefaultItemDelegate {
+ contentItem: NativeStyle.DefaultItemDelegateIconLabel {
+ color: control.highlighted ? control.palette.button : control.palette.windowText
+ }
+}
diff --git a/src/quickcontrols/windows/RadioDelegate.qml b/src/quickcontrols/windows/RadioDelegate.qml
new file mode 100644
index 0000000000..e74e42350b
--- /dev/null
+++ b/src/quickcontrols/windows/RadioDelegate.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick.NativeStyle as NativeStyle
+
+NativeStyle.DefaultRadioDelegate {
+ contentItem: NativeStyle.DefaultItemDelegateIconLabel {
+ color: control.highlighted ? control.palette.button : control.palette.windowText
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+}
diff --git a/src/quickcontrols/windows/RangeSlider.qml b/src/quickcontrols/windows/RangeSlider.qml
new file mode 100644
index 0000000000..a124163e7b
--- /dev/null
+++ b/src/quickcontrols/windows/RangeSlider.qml
@@ -0,0 +1,105 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Templates as T
+
+T.RangeSlider {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ Math.max(first.implicitHandleWidth, second.implicitHandleWidth) + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ Math.max(first.implicitHandleHeight, second.implicitHandleHeight) + topPadding + bottomPadding)
+
+ readonly property bool __notCustomizable: true
+ readonly property Item __focusFrameTarget: control
+
+ component SliderHandle: Rectangle {
+ implicitWidth: control.horizontal ? 11 : 21
+ implicitHeight: control.horizontal ? 21 : 11
+ color: control.palette.highlight
+
+ required property bool pressed
+ }
+
+ first.handle: SliderHandle {
+ x: control.leftPadding + Math.round(control.horizontal
+ ? control.first.visualPosition * (control.availableWidth - width)
+ : (control.availableWidth - width) / 2)
+ y: control.topPadding + Math.round(control.horizontal
+ ? (control.availableHeight - height) / 2
+ : control.first.visualPosition * (control.availableHeight - height))
+ palette: control.palette
+ pressed: control.first.pressed
+
+ // We are the ones that get focus, but we want the control to
+ // be used for the visual focus frame.
+ readonly property Item __focusFrameControl: control
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ second.handle: SliderHandle {
+ x: control.leftPadding + Math.round(control.horizontal
+ ? control.second.visualPosition * (control.availableWidth - width)
+ : (control.availableWidth - width) / 2)
+ y: control.topPadding + Math.round(control.horizontal
+ ? (control.availableHeight - height) / 2
+ : control.second.visualPosition * (control.availableHeight - height))
+ palette: control.palette
+ pressed: control.second.pressed
+
+ readonly property Item __focusFrameControl: control
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ background: Item {
+ implicitWidth: control.horizontal ? 90 : 21
+ implicitHeight: control.horizontal ? 21 : 90
+
+ readonly property real __focusFrameRadius: 1
+ readonly property bool __ignoreNotCustomizable: true
+ readonly property int barThickness: 4
+
+ // Groove background.
+ Rectangle {
+ x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2)
+ y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0)
+ width: control.horizontal ? control.availableWidth : parent.barThickness
+ height: control.horizontal ? parent.barThickness : control.availableHeight
+ color: control.palette.window
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ radius: parent.radius
+ // No border in dark mode, instead we fill.
+ color: Qt.styleHints.colorScheme === Qt.Light
+ ? "transparent" : Qt.lighter(control.palette.window, 1.6)
+ border.color: Qt.styleHints.colorScheme === Qt.Light
+ ? Qt.darker(control.palette.window, 1.1)
+ : "transparent"
+ }
+ }
+
+ // Progress bar.
+ Rectangle {
+ x: control.leftPadding + (control.horizontal
+ ? control.first.position * control.availableWidth
+ : (control.availableWidth - width) / 2)
+ y: control.topPadding + (control.horizontal
+ ? (control.availableHeight - height) / 2
+ : control.second.visualPosition * control.availableHeight)
+
+ width: control.horizontal
+ ? control.second.position * control.availableWidth - control.first.position * control.availableWidth
+ : parent.barThickness
+ height: control.horizontal
+ ? parent.barThickness
+ : control.second.position * control.availableHeight - control.first.position * control.availableHeight
+ color: Qt.rgba(control.palette.highlight.r, control.palette.highlight.g, control.palette.highlight.b, 0.3)
+ }
+ }
+}
diff --git a/src/quickcontrols/windows/ScrollBar.qml b/src/quickcontrols/windows/ScrollBar.qml
index 6906f7dfa2..80fbf74e75 100644
--- a/src/quickcontrols/windows/ScrollBar.qml
+++ b/src/quickcontrols/windows/ScrollBar.qml
@@ -7,6 +7,8 @@ import QtQuick.NativeStyle as NativeStyle
NativeStyle.DefaultScrollBar {
id: controlRoot
+ readonly property bool __notCustomizable: true
+
topPadding: orientation === Qt.Vertical ? controlRoot.__decreaseVisual.indicator.height : 0
bottomPadding: orientation === Qt.Vertical ? controlRoot.__increaseVisual.indicator.height : 0
leftPadding: orientation === Qt.Horizontal ? controlRoot.__decreaseVisual.indicator.width : 0
@@ -15,6 +17,8 @@ NativeStyle.DefaultScrollBar {
contentItem: NativeStyle.ScrollBar {
control: controlRoot
subControl: NativeStyle.ScrollBar.Handle
+
+ readonly property bool __ignoreNotCustomizable: true
}
NativeStyle.ScrollBar {
@@ -29,7 +33,6 @@ NativeStyle.DefaultScrollBar {
subControl: NativeStyle.ScrollBar.Handle
overrideState: NativeStyle.StyleItem.AlwaysHovered
opacity: controlRoot.hovered || control.pressed ? 1 : 0
- visible: contentItem instanceof NativeStyle.StyleItem
Behavior on opacity { NumberAnimation { duration: contentItem.transitionDuration } }
}
@@ -47,6 +50,8 @@ NativeStyle.DefaultScrollBar {
control: controlRoot
subControl: NativeStyle.ScrollBar.Groove
overrideState: NativeStyle.ScrollBar.NeverHovered
+
+ readonly property bool __ignoreNotCustomizable: true
}
__decreaseVisual.indicator: NativeStyle.ScrollBar {
@@ -54,9 +59,10 @@ NativeStyle.DefaultScrollBar {
subControl: NativeStyle.ScrollBar.SubLine
overrideState: NativeStyle.ScrollBar.AlwaysHovered
opacity: controlRoot.__decreaseVisual.hovered ? 1 : 0
- visible: contentItem instanceof NativeStyle.StyleItem
Behavior on opacity { NumberAnimation { duration: contentItem.transitionDuration } }
useNinePatchImage: false
+
+ readonly property bool __ignoreNotCustomizable: true
}
NativeStyle.ScrollBar {
@@ -64,7 +70,6 @@ NativeStyle.DefaultScrollBar {
subControl: NativeStyle.ScrollBar.SubLine
overrideState: NativeStyle.ScrollBar.AlwaysSunken
opacity: controlRoot.__decreaseVisual.pressed ? 1 : 0
- visible: contentItem instanceof NativeStyle.StyleItem
useNinePatchImage: false
z: 1
}
@@ -76,9 +81,10 @@ NativeStyle.DefaultScrollBar {
y: orientation === Qt.Vertical ? controlRoot.height - height : 0
overrideState: NativeStyle.ScrollBar.AlwaysHovered
opacity: controlRoot.__increaseVisual.hovered ? 1 : 0
- visible: contentItem instanceof NativeStyle.StyleItem
Behavior on opacity { NumberAnimation { duration: contentItem.transitionDuration } }
useNinePatchImage: false
+
+ readonly property bool __ignoreNotCustomizable: true
}
NativeStyle.ScrollBar {
@@ -89,7 +95,6 @@ NativeStyle.DefaultScrollBar {
z: 1
overrideState: NativeStyle.ScrollBar.AlwaysSunken
opacity: controlRoot.__increaseVisual.pressed ? 1 : 0
- visible: contentItem instanceof NativeStyle.StyleItem
useNinePatchImage: false
}
}
diff --git a/src/quickcontrols/windows/ScrollView.qml b/src/quickcontrols/windows/ScrollView.qml
index 28f2d8e0fc..65d65899ca 100644
--- a/src/quickcontrols/windows/ScrollView.qml
+++ b/src/quickcontrols/windows/ScrollView.qml
@@ -13,8 +13,8 @@ T.ScrollView {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
- rightPadding: ScrollBar.vertical.visible ? ScrollBar.vertical.width : 0
- bottomPadding: ScrollBar.horizontal.visible ? ScrollBar.horizontal.height : 0
+ rightPadding: effectiveScrollBarWidth
+ bottomPadding: effectiveScrollBarHeight
// Don't set __notCustomizable here, because it would require special-casing
// setFlickable's call to setContentItem.
diff --git a/src/quickcontrols/windows/SpinBox.qml b/src/quickcontrols/windows/SpinBox.qml
index 982a5868a3..dcf75c0b0d 100644
--- a/src/quickcontrols/windows/SpinBox.qml
+++ b/src/quickcontrols/windows/SpinBox.qml
@@ -70,7 +70,7 @@ T.SpinBox {
}
up.indicator: Item {
- x: parent.width - width - 2
+ x: control.width - width - 2
y: 1
height: upAndDown.height >> 1
implicitWidth: upAndDown.implicitWidth
@@ -80,7 +80,7 @@ T.SpinBox {
}
down.indicator: Item {
- x: parent.width - width - 2
+ x: control.width - width - 2
y: up.indicator.y + (upAndDown.height >> 1)
height: upAndDown.height - up.indicator.height
implicitWidth: upAndDown.implicitWidth
diff --git a/src/quickcontrols/windows/Switch.qml b/src/quickcontrols/windows/Switch.qml
new file mode 100644
index 0000000000..4e8fc48396
--- /dev/null
+++ b/src/quickcontrols/windows/Switch.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.Windows.impl
+
+T.Switch {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ padding: 6
+ spacing: 6
+
+ readonly property bool __notCustomizable: true
+ readonly property Item __focusFrameTarget: indicator
+ readonly property Item __focusFrameStyleItem: indicator
+
+ indicator: SwitchIndicator {}
+
+ contentItem: Text {
+ leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
+ rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
+ text: control.text
+ font: control.font
+ color: control.palette.windowText
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+}
diff --git a/src/quickcontrols/windows/SwitchDelegate.qml b/src/quickcontrols/windows/SwitchDelegate.qml
new file mode 100644
index 0000000000..0130a9b372
--- /dev/null
+++ b/src/quickcontrols/windows/SwitchDelegate.qml
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.Windows.impl
+import QtQuick.NativeStyle as NativeStyle
+
+T.SwitchDelegate {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ padding: 6
+ spacing: 6
+
+ readonly property bool __notCustomizable: true
+ readonly property Item __focusFrameTarget: indicator
+ readonly property Item __focusFrameStyleItem: indicator
+
+ indicator: SwitchIndicator {
+ x: control.text
+ ? (control.mirrored ? control.leftPadding : control.width - width - control.rightPadding)
+ : control.leftPadding + (control.availableWidth - width) / 2
+ }
+
+ contentItem: NativeStyle.DefaultItemDelegateIconLabel {
+ color: control.highlighted ? control.palette.button : control.palette.windowText
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 20
+ color: Qt.darker(control.highlighted
+ ? control.palette.highlight : control.palette.button, control.down ? 1.05 : 1)
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+}
diff --git a/src/quickcontrols/windows/impl/CMakeLists.txt b/src/quickcontrols/windows/impl/CMakeLists.txt
new file mode 100644
index 0000000000..fb1b0f2358
--- /dev/null
+++ b/src/quickcontrols/windows/impl/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+set(qml_files
+ "SwitchIndicator.qml"
+)
+
+qt_internal_add_qml_module(QuickControls2WindowsStyleImpl
+ URI "QtQuick.Controls.Windows.impl"
+ VERSION "${PROJECT_VERSION}"
+ PAST_MAJOR_VERSIONS 6
+ CLASS_NAME QtQuickControls2WindowsStyleImplPlugin
+ DEPENDENCIES
+ QtQuick/auto
+ PLUGIN_TARGET qtquickcontrols2windowsstyleimplplugin
+ QML_FILES
+ ${qml_files}
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+)
diff --git a/src/quickcontrols/windows/impl/SwitchIndicator.qml b/src/quickcontrols/windows/impl/SwitchIndicator.qml
new file mode 100644
index 0000000000..0dd80dadf7
--- /dev/null
+++ b/src/quickcontrols/windows/impl/SwitchIndicator.qml
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+
+Rectangle {
+ id: root
+ x: control.text ? (control.mirrored
+ ? control.width - width - control.rightPadding : control.leftPadding)
+ : control.leftPadding + (control.availableWidth - width) / 2
+ y: control.topPadding + (control.availableHeight - height) / 2
+ implicitWidth: 40
+ implicitHeight: 16
+ radius: 3
+ color: Qt.darker(control.palette.button, control.down ? 1.2 : 1.1)
+ border.color: Qt.darker(control.palette.window, 1.4)
+
+ readonly property bool __ignoreNotCustomizable: true
+ readonly property real __focusFrameRadius: 2
+ readonly property T.AbstractButton control: parent as T.AbstractButton
+
+ // Checked indicator.
+ Rectangle {
+ x: root.control.mirrored ? parent.children[1].x : 0
+ width: root.control.mirrored
+ ? parent.width - parent.children[1].x : parent.children[1].x + parent.children[1].width
+ height: parent.height
+ radius: 3
+ color: Qt.darker(root.control.palette.highlight, root.control.down ? 1.1 : 1)
+ border.color: Qt.darker(root.control.palette.highlight, 1.35)
+ border.width: root.control.enabled ? 1 : 0
+ opacity: root.control.checked ? 1 : 0
+
+ Behavior on opacity {
+ enabled: !root.control.down
+ NumberAnimation { duration: 80 }
+ }
+ }
+
+ // Handle.
+ Rectangle {
+ x: Math.max(0, Math.min(parent.width - width,
+ root.control.visualPosition * parent.width - (width / 2)))
+ y: (parent.height - height) / 2
+ width: 20
+ height: 16
+ radius: 3
+ color: Qt.lighter(root.control.palette.button, root.control.down
+ ? 1 : (root.control.hovered ? 1.07 : 1.045))
+ border.width: 1
+ border.color: Qt.darker(root.control.palette.window, 1.4)
+
+ Behavior on x {
+ enabled: !root.control.down
+ SmoothedAnimation { velocity: 200 }
+ }
+ }
+}
diff --git a/src/quickcontrolsimpl/CMakeLists.txt b/src/quickcontrolsimpl/CMakeLists.txt
index acbc91b15e..5b8d932384 100644
--- a/src/quickcontrolsimpl/CMakeLists.txt
+++ b/src/quickcontrolsimpl/CMakeLists.txt
@@ -30,7 +30,6 @@ qt_internal_add_qml_module(QuickControls2Impl
qquickpaddedrectangle.cpp qquickpaddedrectangle_p.h
qquickplaceholdertext.cpp qquickplaceholdertext_p.h
qquickplatformtheme.cpp qquickplatformtheme_p.h
- qtquickcontrols2foreign.cpp qtquickcontrols2foreign_p.h
qtquickcontrols2implglobal_p.h
DEFINES
QT_NO_CAST_FROM_ASCII
@@ -46,8 +45,7 @@ qt_internal_add_qml_module(QuickControls2Impl
Qt::Gui
Qt::Quick
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
qt_internal_extend_target(QuickControls2Impl CONDITION QT_FEATURE_quick_listview AND QT_FEATURE_quick_pathview
SOURCES
diff --git a/src/quickcontrolsimpl/qquickanimatednode_p.h b/src/quickcontrolsimpl/qquickanimatednode_p.h
index a893e7eecc..8a79512865 100644
--- a/src/quickcontrolsimpl/qquickanimatednode_p.h
+++ b/src/quickcontrolsimpl/qquickanimatednode_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickWindow;
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickAnimatedNode : public QObject, public QSGTransformNode
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickAnimatedNode : public QObject, public QSGTransformNode
{
Q_OBJECT
diff --git a/src/quickcontrolsimpl/qquickchecklabel_p.h b/src/quickcontrolsimpl/qquickchecklabel_p.h
index 2419433c68..a9e26e4ddd 100644
--- a/src/quickcontrolsimpl/qquickchecklabel_p.h
+++ b/src/quickcontrolsimpl/qquickchecklabel_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickCheckLabel : public QQuickText
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickCheckLabel : public QQuickText
{
Q_OBJECT
QML_NAMED_ELEMENT(CheckLabel)
@@ -40,6 +40,4 @@ struct QQuickTextForeign
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCheckLabel)
-
#endif // QQUICKCHECKLABEL_P_H
diff --git a/src/quickcontrolsimpl/qquickclippedtext_p.h b/src/quickcontrolsimpl/qquickclippedtext_p.h
index 4ae1c05f3f..53d1eb8aac 100644
--- a/src/quickcontrolsimpl/qquickclippedtext_p.h
+++ b/src/quickcontrolsimpl/qquickclippedtext_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickClippedText : public QQuickText
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickClippedText : public QQuickText
{
Q_OBJECT
Q_PROPERTY(qreal clipX READ clipX WRITE setClipX FINAL)
@@ -60,6 +60,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickClippedText)
-
#endif // QQUICKCLIPPEDTEXT_P_H
diff --git a/src/quickcontrolsimpl/qquickcolor_p.h b/src/quickcontrolsimpl/qquickcolor_p.h
index 994b3e988d..eae1440126 100644
--- a/src/quickcontrolsimpl/qquickcolor_p.h
+++ b/src/quickcontrolsimpl/qquickcolor_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickColor : public QObject
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickColor : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(Color)
diff --git a/src/quickcontrolsimpl/qquickcolorimage_p.h b/src/quickcontrolsimpl/qquickcolorimage_p.h
index c321438d8c..f2506733d6 100644
--- a/src/quickcontrolsimpl/qquickcolorimage_p.h
+++ b/src/quickcontrolsimpl/qquickcolorimage_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickColorImage : public QQuickImage
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickColorImage : public QQuickImage
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor RESET resetColor NOTIFY colorChanged FINAL)
diff --git a/src/quickcontrolsimpl/qquickiconimage.cpp b/src/quickcontrolsimpl/qquickiconimage.cpp
index f5a14f3110..24bb752ebd 100644
--- a/src/quickcontrolsimpl/qquickiconimage.cpp
+++ b/src/quickcontrolsimpl/qquickiconimage.cpp
@@ -50,11 +50,22 @@ void QQuickIconImagePrivate::updateIcon()
const QUrl entryUrl = QUrl::fromLocalFile(entry->filename);
url = context ? context->resolvedUrl(entryUrl) : entryUrl;
isThemeIcon = true;
+ } else if (source.isEmpty()) {
+ std::unique_ptr<QIconEngine> iconEngine(QIconLoader::instance()->iconEngine(icon.iconName));
+ if (iconEngine && !iconEngine->isNull()) {
+ // ### TODO that's the best we can do for now to select different pixmaps based on the
+ // QuickItem's state. QQuickIconImage cannot know about the state of the control that
+ // uses it without adding more properties that are then synced up with the control.
+ const QIcon::Mode mode = q->isEnabled() ? QIcon::Normal : QIcon::Disabled;
+ const QImage image = iconEngine->scaledPixmap(size, mode, QIcon::Off, dpr).toImage();
+ setImage(image);
+ }
} else {
url = source;
isThemeIcon = false;
}
- q->load();
+ if (!url.isEmpty())
+ q->load();
updatingIcon = false;
}
@@ -106,6 +117,8 @@ void QQuickIconImage::setName(const QString &name)
d->icon.entries.clear();
d->icon = QIconLoader::instance()->loadIcon(name);
+ if (d->icon.iconName.isEmpty())
+ d->icon.iconName = name;
if (isComponentComplete())
d->updateIcon();
emit nameChanged();
diff --git a/src/quickcontrolsimpl/qquickiconimage_p.h b/src/quickcontrolsimpl/qquickiconimage_p.h
index d46bbab9f4..18a3c4d831 100644
--- a/src/quickcontrolsimpl/qquickiconimage_p.h
+++ b/src/quickcontrolsimpl/qquickiconimage_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickIconImagePrivate;
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickIconImage : public QQuickImage
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickIconImage : public QQuickImage
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
@@ -58,6 +58,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickIconImage)
-
#endif // QQUICKICONIMAGE_P_H
diff --git a/src/quickcontrolsimpl/qquickiconimage_p_p.h b/src/quickcontrolsimpl/qquickiconimage_p_p.h
index 6b468fa15e..905760ed7b 100644
--- a/src/quickcontrolsimpl/qquickiconimage_p_p.h
+++ b/src/quickcontrolsimpl/qquickiconimage_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickIconImagePrivate : public QQuickImagePrivate
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickIconImagePrivate : public QQuickImagePrivate
{
public:
Q_DECLARE_PUBLIC(QQuickIconImage)
diff --git a/src/quickcontrolsimpl/qquickiconlabel_p.h b/src/quickcontrolsimpl/qquickiconlabel_p.h
index ac3e452c74..2fd129f4ad 100644
--- a/src/quickcontrolsimpl/qquickiconlabel_p.h
+++ b/src/quickcontrolsimpl/qquickiconlabel_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickIconLabelPrivate;
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickIconLabel : public QQuickItem
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickIconLabel : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickIcon icon READ icon WRITE setIcon FINAL)
@@ -104,6 +104,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickIconLabel)
-
#endif // QQUICKICONLABEL_P_H
diff --git a/src/quickcontrolsimpl/qquickimageselector_p.h b/src/quickcontrolsimpl/qquickimageselector_p.h
index 02eab385fb..c5ddf7de50 100644
--- a/src/quickcontrolsimpl/qquickimageselector_p.h
+++ b/src/quickcontrolsimpl/qquickimageselector_p.h
@@ -118,7 +118,4 @@ protected:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickImageSelector)
-QML_DECLARE_TYPE(QQuickAnimatedImageSelector)
-
#endif // QQUICKIMAGESELECTOR_P_H
diff --git a/src/quickcontrolsimpl/qquickitemgroup_p.h b/src/quickcontrolsimpl/qquickitemgroup_p.h
index a1ce320d1c..644c535a43 100644
--- a/src/quickcontrolsimpl/qquickitemgroup_p.h
+++ b/src/quickcontrolsimpl/qquickitemgroup_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickItemGroup : public QQuickImplicitSizeItem, protected QQuickItemChangeListener
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickItemGroup : public QQuickImplicitSizeItem, protected QQuickItemChangeListener
{
Q_OBJECT
QML_NAMED_ELEMENT(ItemGroup)
@@ -47,6 +47,4 @@ protected:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickItemGroup)
-
#endif // QQUICKITEMGROUP_P_H
diff --git a/src/quickcontrolsimpl/qquickmnemoniclabel_p.h b/src/quickcontrolsimpl/qquickmnemoniclabel_p.h
index 825df61e9c..537103bcd4 100644
--- a/src/quickcontrolsimpl/qquickmnemoniclabel_p.h
+++ b/src/quickcontrolsimpl/qquickmnemoniclabel_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickMnemonicLabel : public QQuickText
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickMnemonicLabel : public QQuickText
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText FINAL)
@@ -46,6 +46,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMnemonicLabel)
-
#endif // QQUICKMNEMONICLABEL_P_H
diff --git a/src/quickcontrolsimpl/qquickninepatchimage.cpp b/src/quickcontrolsimpl/qquickninepatchimage.cpp
index 3f8a9e03e9..d6238d2ccd 100644
--- a/src/quickcontrolsimpl/qquickninepatchimage.cpp
+++ b/src/quickcontrolsimpl/qquickninepatchimage.cpp
@@ -401,7 +401,7 @@ void QQuickNinePatchImage::pixmapChange()
d->ninePatch = d->pix.image();
if (d->ninePatch.depth() != 32)
- d->ninePatch = d->ninePatch.convertToFormat(QImage::Format_ARGB32);
+ d->ninePatch = std::move(d->ninePatch).convertToFormat(QImage::Format_ARGB32);
int w = d->ninePatch.width();
int h = d->ninePatch.height();
@@ -443,6 +443,9 @@ QSGNode *QQuickNinePatchImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNode
d->resetNode = false;
}
+ if (d->ninePatch.isNull())
+ return QQuickImage::updatePaintNode(oldNode, data);
+
QSizeF sz = size();
QImage image = d->pix.image();
if (!sz.isValid() || image.isNull()) {
@@ -452,9 +455,6 @@ QSGNode *QQuickNinePatchImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNode
return nullptr;
}
- if (d->ninePatch.isNull())
- return QQuickImage::updatePaintNode(oldNode, data);
-
QQuickNinePatchNode *patchNode = static_cast<QQuickNinePatchNode *>(oldNode);
if (!patchNode)
patchNode = new QQuickNinePatchNode;
diff --git a/src/quickcontrolsimpl/qquickninepatchimage_p.h b/src/quickcontrolsimpl/qquickninepatchimage_p.h
index 0c5777bcfa..7ca5ae951e 100644
--- a/src/quickcontrolsimpl/qquickninepatchimage_p.h
+++ b/src/quickcontrolsimpl/qquickninepatchimage_p.h
@@ -70,6 +70,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickNinePatchImage)
-
#endif // QQUICKNINEPATCHIMAGE_P_H
diff --git a/src/quickcontrolsimpl/qquickpaddedrectangle_p.h b/src/quickcontrolsimpl/qquickpaddedrectangle_p.h
index c7cec6fe52..fff3964a85 100644
--- a/src/quickcontrolsimpl/qquickpaddedrectangle_p.h
+++ b/src/quickcontrolsimpl/qquickpaddedrectangle_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickPaddedRectangle : public QQuickRectangle
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickPaddedRectangle : public QQuickRectangle
{
Q_OBJECT
Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged FINAL)
@@ -83,6 +83,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPaddedRectangle)
-
#endif // QQUICKPADDEDRECTANGLE_P_H
diff --git a/src/quickcontrolsimpl/qquickplaceholdertext.cpp b/src/quickcontrolsimpl/qquickplaceholdertext.cpp
index 5f47a4087d..4a1fb0db00 100644
--- a/src/quickcontrolsimpl/qquickplaceholdertext.cpp
+++ b/src/quickcontrolsimpl/qquickplaceholdertext.cpp
@@ -16,10 +16,25 @@ QQuickPlaceholderText::QQuickPlaceholderText(QQuickItem *parent) : QQuickText(pa
void QQuickPlaceholderText::componentComplete()
{
QQuickText::componentComplete();
- connect(parentItem(), SIGNAL(effectiveHorizontalAlignmentChanged()), this, SLOT(updateAlignment()));
+
+ auto control = textControl();
+ if (control)
+ connect(control, SIGNAL(effectiveHorizontalAlignmentChanged()), this, SLOT(updateAlignment()));
updateAlignment();
}
+/*!
+ \internal
+
+ The control that we're representing. This exists because
+ parentItem() is not always the control - it may be a Flickable
+ in the case of TextArea.
+*/
+QQuickItem *QQuickPlaceholderText::textControl() const
+{
+ return qobject_cast<QQuickItem *>(parent());
+}
+
void QQuickPlaceholderText::updateAlignment()
{
if (QQuickTextInput *input = qobject_cast<QQuickTextInput *>(parentItem())) {
diff --git a/src/quickcontrolsimpl/qquickplaceholdertext_p.h b/src/quickcontrolsimpl/qquickplaceholdertext_p.h
index 81434cc65c..53bd01756c 100644
--- a/src/quickcontrolsimpl/qquickplaceholdertext_p.h
+++ b/src/quickcontrolsimpl/qquickplaceholdertext_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickPlaceholderText : public QQuickText
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickPlaceholderText : public QQuickText
{
Q_OBJECT
QML_NAMED_ELEMENT(PlaceholderText)
@@ -32,12 +32,12 @@ public:
protected:
void componentComplete() override;
+ QQuickItem *textControl() const;
+
private Q_SLOTS:
void updateAlignment();
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPlaceholderText)
-
#endif // QQUICKPLACEHOLDERTEXT_P_H
diff --git a/src/quickcontrolsimpl/qquickplatformtheme_p.h b/src/quickcontrolsimpl/qquickplatformtheme_p.h
index 85cd81f50c..c5284f58bf 100644
--- a/src/quickcontrolsimpl/qquickplatformtheme_p.h
+++ b/src/quickcontrolsimpl/qquickplatformtheme_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickPlatformTheme : public QObject
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickPlatformTheme : public QObject
{
Q_OBJECT
// This exposes the enums in QPlatformTheme to QML. We can't use QML_EXTENDED
diff --git a/src/quickcontrolsimpl/qquicktumblerview_p.h b/src/quickcontrolsimpl/qquicktumblerview_p.h
index f9e01f2821..123511d121 100644
--- a/src/quickcontrolsimpl/qquicktumblerview_p.h
+++ b/src/quickcontrolsimpl/qquicktumblerview_p.h
@@ -26,7 +26,7 @@ class QQuickPathView;
class QQuickTumbler;
-class Q_QUICKCONTROLS2IMPL_PRIVATE_EXPORT QQuickTumblerView : public QQuickItem
+class Q_QUICKCONTROLS2IMPL_EXPORT QQuickTumblerView : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
diff --git a/src/quickcontrolsimpl/qtquickcontrols2foreign.cpp b/src/quickcontrolsimpl/qtquickcontrols2foreign.cpp
deleted file mode 100644
index e55cd304c3..0000000000
--- a/src/quickcontrolsimpl/qtquickcontrols2foreign.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qtquickcontrols2foreign_p.h"
-
-QT_BEGIN_NAMESPACE
-
-QT_END_NAMESPACE
-
-#include "moc_qtquickcontrols2foreign_p.cpp"
diff --git a/src/quickcontrolsimpl/qtquickcontrols2foreign_p.h b/src/quickcontrolsimpl/qtquickcontrols2foreign_p.h
deleted file mode 100644
index b447478cd8..0000000000
--- a/src/quickcontrolsimpl/qtquickcontrols2foreign_p.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QTQUICKCONTROLS2FOREIGN_P_H
-#define QTQUICKCONTROLS2FOREIGN_P_H
-
-#include <QtQml/qqml.h>
-#include <QtQuickTemplates2/private/qquickoverlay_p.h>
-#include <QtQuickTemplates2/private/qquicksplitview_p.h>
-
-QT_BEGIN_NAMESPACE
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-// These are necessary in order to use C++ types in a file where only QtQuick.Controls has been imported.
-// Control types like Button don't need this done for them, as each style module provides a Button type,
-// and QtQuick.Controls is a sort of alias for the active style import.
-
-struct QQuickOverlayAttachedForeign
-{
- Q_GADGET
- QML_NAMED_ELEMENT(Overlay)
- QML_FOREIGN(QQuickOverlay)
- QML_UNCREATABLE("")
- QML_ADDED_IN_VERSION(2, 3)
-};
-
-struct QQuickSplitHandleAttachedForeign
-{
- Q_GADGET
- QML_NAMED_ELEMENT(SplitHandle)
- QML_FOREIGN(QQuickSplitHandleAttached)
- QML_UNCREATABLE("")
- QML_ADDED_IN_VERSION(2, 13)
-};
-
-QT_END_NAMESPACE
-
-#endif // QTQUICKCONTROLS2FOREIGN_P_H
diff --git a/src/quickcontrolsimpl/qtquickcontrols2implglobal_p.h b/src/quickcontrolsimpl/qtquickcontrols2implglobal_p.h
index 45214c88ec..c11bdf4ea2 100644
--- a/src/quickcontrolsimpl/qtquickcontrols2implglobal_p.h
+++ b/src/quickcontrolsimpl/qtquickcontrols2implglobal_p.h
@@ -16,6 +16,6 @@
//
#include <QtCore/qglobal.h>
-#include <QtQuickControls2Impl/private/qtquickcontrols2implexports_p.h>
+#include <QtQuickControls2Impl/qtquickcontrols2implexports.h>
#endif // QTQUICKCONTROLS2IMPLGLOBAL_H
diff --git a/src/quickcontrolstestutils/CMakeLists.txt b/src/quickcontrolstestutils/CMakeLists.txt
index ac3c92a7f9..f44d752ff6 100644
--- a/src/quickcontrolstestutils/CMakeLists.txt
+++ b/src/quickcontrolstestutils/CMakeLists.txt
@@ -8,8 +8,6 @@ qt_internal_add_module(QuickControlsTestUtilsPrivate
SOURCES
controlstestutils.cpp
controlstestutils_p.h
- dialogstestutils.cpp
- dialogstestutils_p.h
qtest_quickcontrols_p.h
DEFINES
QT_BUILD_SHARED_QUICK_CONTROLS_TEST_UTILS_LIB
@@ -20,13 +18,21 @@ qt_internal_add_module(QuickControlsTestUtilsPrivate
Qt::QmlPrivate
Qt::Quick
Qt::QuickControls2
- Qt::QuickDialogs2QuickImplPrivate
Qt::QuickPrivate
Qt::QuickTemplates2
Qt::QuickTemplates2Private
Qt::QuickTestUtilsPrivate
+ NO_GENERATE_CPP_EXPORTS
+)
+qt_internal_extend_target(QuickControlsTestUtilsPrivate CONDITION QT_FEATURE_quicktemplates2_container
+ SOURCES
+ dialogstestutils.cpp
+ dialogstestutils_p.h
+ PUBLIC_LIBRARIES
+ Qt::QuickDialogs2QuickImplPrivate
)
+
# This is used by both C++ and QML tests, so we need it to be a library and a QML plugin,
# hence qt_internal_add_qml_module. We use it in addition to qt_internal_add_module,
# because otherwise syncqt complains that there is no "QtQuickControlsTestUtilsPrivate" module.
diff --git a/src/quickcontrolstestutils/controlstestutils.cpp b/src/quickcontrolstestutils/controlstestutils.cpp
index f565751c0d..5330b8ebbd 100644
--- a/src/quickcontrolstestutils/controlstestutils.cpp
+++ b/src/quickcontrolstestutils/controlstestutils.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "controlstestutils_p.h"
@@ -178,3 +178,8 @@ Q_INVOKABLE QQmlComponent *QQuickControlsTestUtils::ComponentCreator::createComp
qmlWarning(this) << "Failed to create component from the following data:\n" << data;
return component.release();
}
+
+QString QQuickControlsTestUtils::StyleInfo::styleName() const
+{
+ return QQuickStyle::name();
+}
diff --git a/src/quickcontrolstestutils/controlstestutils_p.h b/src/quickcontrolstestutils/controlstestutils_p.h
index 487d3b79fe..e331da69d8 100644
--- a/src/quickcontrolstestutils/controlstestutils_p.h
+++ b/src/quickcontrolstestutils/controlstestutils_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CONTROLSTESTUTILS_P_H
#define CONTROLSTESTUTILS_P_H
@@ -65,6 +65,17 @@ namespace QQuickControlsTestUtils
public:
Q_INVOKABLE QQmlComponent *createComponent(const QByteArray &data);
};
+
+ class StyleInfo : public QObject
+ {
+ Q_OBJECT
+ Q_PROPERTY(QString styleName READ styleName CONSTANT FINAL)
+ QML_ELEMENT
+ QML_SINGLETON
+
+ public:
+ QString styleName() const;
+ };
}
QT_END_NAMESPACE
diff --git a/src/quickcontrolstestutils/dialogstestutils.cpp b/src/quickcontrolstestutils/dialogstestutils.cpp
index 71622fabb0..2b21f674d1 100644
--- a/src/quickcontrolstestutils/dialogstestutils.cpp
+++ b/src/quickcontrolstestutils/dialogstestutils.cpp
@@ -1,21 +1,27 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "dialogstestutils_p.h"
#include <QtTest/qsignalspy.h>
+#include <QtQuick/private/qtquickglobal_p.h>
+#if QT_CONFIG(quick_listview)
#include <QtQuick/private/qquicklistview_p.h>
+#endif
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>
+#if QT_CONFIG(quick_listview)
#include <QtQuickDialogs2QuickImpl/private/qquickfiledialogdelegate_p.h>
#include <QtQuickDialogs2QuickImpl/private/qquickfolderbreadcrumbbar_p.h>
#include <QtQuickDialogs2QuickImpl/private/qquickfolderbreadcrumbbar_p_p.h>
+#endif
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(quick_listview)
bool QQuickDialogTestUtils::verifyFileDialogDelegates(QQuickListView *fileDialogListView,
const QStringList &expectedFiles, QString &failureMessage)
{
@@ -105,6 +111,7 @@ bool QQuickDialogTestUtils::verifyBreadcrumbDelegates(QQuickFolderBreadcrumbBar
return true;
}
+#endif
QQuickAbstractButton *QQuickDialogTestUtils::findDialogButton(QQuickDialogButtonBox *box, const QString &buttonText)
{
diff --git a/src/quickcontrolstestutils/dialogstestutils_p.h b/src/quickcontrolstestutils/dialogstestutils_p.h
index 963eb4d764..dfa46fdd94 100644
--- a/src/quickcontrolstestutils/dialogstestutils_p.h
+++ b/src/quickcontrolstestutils/dialogstestutils_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DIALOGSTESTUTILS_H
#define DIALOGSTESTUTILS_H
diff --git a/src/quickcontrolstestutils/qtest_quickcontrols_p.h b/src/quickcontrolstestutils/qtest_quickcontrols_p.h
index 1294781114..e339425a70 100644
--- a/src/quickcontrolstestutils/qtest_quickcontrols_p.h
+++ b/src/quickcontrolstestutils/qtest_quickcontrols_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QTEST_QUICKCONTROLS_P_H
#define QTEST_QUICKCONTROLS_P_H
diff --git a/src/quickdialogs/quickdialogs/CMakeLists.txt b/src/quickdialogs/quickdialogs/CMakeLists.txt
index 529d021d3c..ec9c37c2f9 100644
--- a/src/quickdialogs/quickdialogs/CMakeLists.txt
+++ b/src/quickdialogs/quickdialogs/CMakeLists.txt
@@ -12,6 +12,7 @@ qt_internal_add_qml_module(QuickDialogs2
PLUGIN_TARGET qtquickdialogsplugin
DEPENDENCIES
QtQuick/auto
+ QtQuick.Dialogs.quickimpl/auto
SOURCES
qquickabstractdialog.cpp
qquickabstractdialog_p.h
@@ -49,8 +50,7 @@ qt_internal_add_qml_module(QuickDialogs2
Qt::Gui
Qt::Quick
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
qt_internal_add_docs(QuickDialogs2
doc/qtquickdialogs.qdocconf
diff --git a/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf b/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf
index 9fd4891d02..b19b96d691 100644
--- a/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf
+++ b/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf
@@ -37,5 +37,5 @@ navigation.qmltypespage = "Qt Quick Dialogs QML Types"
tagfile = qtquickdialogs.tags
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml b/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml
index ab3f33f910..b443a927bc 100644
--- a/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml
+++ b/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [file]
import QtCore
diff --git a/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp b/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp
index 857a75c7bd..1a77b86deb 100644
--- a/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp
+++ b/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp
@@ -150,6 +150,8 @@ QWindow *QQuickAbstractDialog::parentWindow() const
void QQuickAbstractDialog::setParentWindow(QWindow *window)
{
qCDebug(lcDialogs) << "set parent window to" << window;
+ m_parentWindowExplicitlySet = bool(window);
+
if (m_parentWindow == window)
return;
@@ -157,6 +159,17 @@ void QQuickAbstractDialog::setParentWindow(QWindow *window)
emit parentWindowChanged();
}
+void QQuickAbstractDialog::resetParentWindow()
+{
+ m_parentWindowExplicitlySet = false;
+
+ if (!m_parentWindow)
+ return;
+
+ m_parentWindow = nullptr;
+ emit parentWindowChanged();
+}
+
/*!
\qmlproperty string QtQuick.Dialogs::Dialog::title
@@ -248,7 +261,7 @@ void QQuickAbstractDialog::setVisible(bool visible)
}
/*!
- \qmlproperty StandardCode QtQuick.Dialogs::Dialog::result
+ \qmlproperty int QtQuick.Dialogs::Dialog::result
This property holds the result code.
@@ -259,12 +272,12 @@ void QQuickAbstractDialog::setVisible(bool visible)
\note MessageDialog sets the result to the value of the clicked standard
button instead of using the standard result codes.
*/
-QQuickAbstractDialog::StandardCode QQuickAbstractDialog::result() const
+int QQuickAbstractDialog::result() const
{
return m_result;
}
-void QQuickAbstractDialog::setResult(StandardCode result)
+void QQuickAbstractDialog::setResult(int result)
{
if (m_result == result)
return;
@@ -287,7 +300,8 @@ void QQuickAbstractDialog::open()
return;
onShow(m_handle.get());
- m_visible = m_handle->show(m_flags, m_modality, m_parentWindow);
+
+ m_visible = m_handle->show(m_flags, m_modality, windowForOpen());
if (m_visible) {
m_result = Rejected; // in case an accepted dialog gets re-opened, then closed
emit visibleChanged();
@@ -310,11 +324,13 @@ void QQuickAbstractDialog::close()
onHide(m_handle.get());
m_handle->hide();
m_visible = false;
+ if (!m_parentWindowExplicitlySet)
+ m_parentWindow = nullptr;
emit visibleChanged();
- if (m_result == Accepted)
+ if (dialogCode() == Accepted)
emit accepted();
- else // if (m_result == Rejected)
+ else if (dialogCode() == Rejected)
emit rejected();
}
@@ -343,13 +359,13 @@ void QQuickAbstractDialog::reject()
}
/*!
- \qmlmethod void QtQuick.Dialogs::Dialog::done(StandardCode result)
+ \qmlmethod void QtQuick.Dialogs::Dialog::done(int result)
Closes the dialog and sets the \a result.
\sa accept(), reject(), result
*/
-void QQuickAbstractDialog::done(StandardCode result)
+void QQuickAbstractDialog::done(int result)
{
setResult(result);
close();
@@ -364,16 +380,22 @@ void QQuickAbstractDialog::componentComplete()
qCDebug(lcDialogs) << "componentComplete";
m_complete = true;
- if (!m_parentWindow) {
- qCDebug(lcDialogs) << "- no parent window; searching for one";
- setParentWindow(findParentWindow());
- }
+ if (!m_visibleRequested)
+ return;
+
+ m_visibleRequested = false;
- if (m_visibleRequested) {
- qCDebug(lcDialogs) << "visible was bound to true before component completion; opening dialog";
+ if (windowForOpen()) {
open();
- m_visibleRequested = false;
+ return;
}
+
+ // Since visible were set to true by the user, we want the dialog to be open by default.
+ // There is no guarantee that the dialog will work when it exists in a object tree that lacks a window,
+ // and since qml components are sometimes instantiated before they're given a window
+ // (which is the case when using QQuickView), we want to delay the call to open(), until the window is provided.
+ if (const auto parentItem = findParentItem())
+ connect(parentItem, &QQuickItem::windowChanged, this, &QQuickAbstractDialog::deferredOpen, Qt::SingleShotConnection);
}
static const char *qmlTypeName(const QObject *object)
@@ -461,21 +483,35 @@ void QQuickAbstractDialog::onHide(QPlatformDialogHelper *dialog)
Q_UNUSED(dialog);
}
-QWindow *QQuickAbstractDialog::findParentWindow() const
+int QQuickAbstractDialog::dialogCode() const { return m_result; }
+
+QQuickItem *QQuickAbstractDialog::findParentItem() const
{
QObject *obj = parent();
while (obj) {
- QWindow *window = qobject_cast<QWindow *>(obj);
- if (window)
- return window;
QQuickItem *item = qobject_cast<QQuickItem *>(obj);
- if (item && item->window())
- return item->window();
+ if (item)
+ return item;
obj = obj->parent();
}
return nullptr;
}
+QWindow *QQuickAbstractDialog::windowForOpen() const
+{
+ if (m_parentWindowExplicitlySet)
+ return m_parentWindow;
+ else if (auto parentItem = findParentItem())
+ return parentItem->window();
+ return m_parentWindow;
+}
+
+void QQuickAbstractDialog::deferredOpen(QWindow *window)
+{
+ m_parentWindow = window;
+ open();
+}
+
QT_END_NAMESPACE
#include "moc_qquickabstractdialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h b/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h
index f1d046eb89..7926185447 100644
--- a/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h
+++ b/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h
@@ -32,17 +32,17 @@ QT_BEGIN_NAMESPACE
class QWindow;
class QPlatformDialogHelper;
-class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickAbstractDialog : public QObject, public QQmlParserStatus
+class Q_QUICKDIALOGS2_EXPORT QQuickAbstractDialog : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QQmlListProperty<QObject> data READ data FINAL)
- Q_PROPERTY(QWindow *parentWindow READ parentWindow WRITE setParentWindow NOTIFY parentWindowChanged FINAL)
+ Q_PROPERTY(QWindow *parentWindow READ parentWindow WRITE setParentWindow NOTIFY parentWindowChanged RESET resetParentWindow FINAL)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
Q_PROPERTY(Qt::WindowFlags flags READ flags WRITE setFlags NOTIFY flagsChanged FINAL)
Q_PROPERTY(Qt::WindowModality modality READ modality WRITE setModality NOTIFY modalityChanged FINAL)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL)
- Q_PROPERTY(StandardCode result READ result WRITE setResult NOTIFY resultChanged FINAL)
+ Q_PROPERTY(int result READ result WRITE setResult NOTIFY resultChanged FINAL)
Q_CLASSINFO("DefaultProperty", "data")
Q_MOC_INCLUDE(<QtGui/qwindow.h>)
QML_ANONYMOUS
@@ -58,6 +58,7 @@ public:
QWindow *parentWindow() const;
void setParentWindow(QWindow *window);
+ void resetParentWindow();
QString title() const;
void setTitle(const QString &title);
@@ -74,15 +75,15 @@ public:
enum StandardCode { Rejected, Accepted };
Q_ENUM(StandardCode)
- StandardCode result() const;
- void setResult(StandardCode result);
+ int result() const;
+ void setResult(int result);
public Q_SLOTS:
void open();
void close();
virtual void accept();
virtual void reject();
- virtual void done(StandardCode result);
+ virtual void done(int result);
Q_SIGNALS:
void accepted();
@@ -105,14 +106,13 @@ protected:
virtual void onCreate(QPlatformDialogHelper *dialog);
virtual void onShow(QPlatformDialogHelper *dialog);
virtual void onHide(QPlatformDialogHelper *dialog);
+ virtual int dialogCode() const;
- QWindow *findParentWindow() const;
+ QQuickItem *findParentItem() const;
+ QWindow *windowForOpen() const;
+ void deferredOpen(QWindow *window);
- bool m_visibleRequested = false;
- bool m_visible = false;
- bool m_complete = false;
- bool m_firstShow = true;
- StandardCode m_result = Rejected;
+ int m_result = Rejected;
QWindow *m_parentWindow = nullptr;
QString m_title;
Qt::WindowFlags m_flags = Qt::Dialog;
@@ -120,10 +120,13 @@ protected:
QQuickDialogType m_type = QQuickDialogType::FileDialog;
QList<QObject *> m_data;
std::unique_ptr<QPlatformDialogHelper> m_handle;
+ bool m_visibleRequested = false;
+ bool m_visible = false;
+ bool m_complete = false;
+ bool m_parentWindowExplicitlySet = false;
+ bool m_firstShow = true;
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAbstractDialog)
-
#endif // QQUICKABSTRACTDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickcolordialog.cpp b/src/quickdialogs/quickdialogs/qquickcolordialog.cpp
index 55fa073296..08b16f4ab0 100644
--- a/src/quickdialogs/quickdialogs/qquickcolordialog.cpp
+++ b/src/quickdialogs/quickdialogs/qquickcolordialog.cpp
@@ -102,6 +102,7 @@ void QQuickColorDialog::setSelectedColor(const QColor &color)
\value ColorDialog.ShowAlphaChannel Show a slider and additional input fields for the alpha value.
\value ColorDialog.NoButtons Don't display \uicontrol Open and \uicontrol Cancel buttons (useful
for "live dialogs").
+ \value ColorDialog.NoEyeDropperButton Don't display \uicontrol {Eye Dropper} button. This value was added in Qt 6.6.
\value ColorDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation.
*/
diff --git a/src/quickdialogs/quickdialogs/qquickcolordialog_p.h b/src/quickdialogs/quickdialogs/qquickcolordialog_p.h
index 9b424d6b7e..c8ff98531a 100644
--- a/src/quickdialogs/quickdialogs/qquickcolordialog_p.h
+++ b/src/quickdialogs/quickdialogs/qquickcolordialog_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickColorDialog : public QQuickAbstractDialog
+class Q_QUICKDIALOGS2_EXPORT QQuickColorDialog : public QQuickAbstractDialog
{
Q_OBJECT
Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged)
@@ -56,6 +56,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickColorDialog)
-
#endif // QQUICKCOLORDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickfiledialog_p.h b/src/quickdialogs/quickdialogs/qquickfiledialog_p.h
index 7214c907f3..95b239f4fb 100644
--- a/src/quickdialogs/quickdialogs/qquickfiledialog_p.h
+++ b/src/quickdialogs/quickdialogs/qquickfiledialog_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickFileNameFilter;
-class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFileDialog : public QQuickAbstractDialog
+class Q_QUICKDIALOGS2_EXPORT QQuickFileDialog : public QQuickAbstractDialog
{
Q_OBJECT
Q_PROPERTY(FileMode fileMode READ fileMode WRITE setFileMode NOTIFY fileModeChanged FINAL)
@@ -127,6 +127,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFileDialog)
-
#endif // QQUICKFILEDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h b/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h
index e1231ab3a5..b588b0cd3d 100644
--- a/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h
+++ b/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickFileNameFilter;
-class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFolderDialog : public QQuickAbstractDialog
+class Q_QUICKDIALOGS2_EXPORT QQuickFolderDialog : public QQuickAbstractDialog
{
Q_OBJECT
Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged FINAL)
@@ -75,6 +75,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFolderDialog)
-
#endif // QQUICKFOLDERDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickfontdialog_p.h b/src/quickdialogs/quickdialogs/qquickfontdialog_p.h
index f5df6d2a9f..ce3719b75d 100644
--- a/src/quickdialogs/quickdialogs/qquickfontdialog_p.h
+++ b/src/quickdialogs/quickdialogs/qquickfontdialog_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFontDialog : public QQuickAbstractDialog
+class Q_QUICKDIALOGS2_EXPORT QQuickFontDialog : public QQuickAbstractDialog
{
Q_OBJECT
Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont NOTIFY selectedFontChanged)
@@ -62,6 +62,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFontDialog)
-
#endif // QQUICKFONTDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp b/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp
index 075ccd54db..7e16cfb9dd 100644
--- a/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp
+++ b/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp
@@ -218,7 +218,9 @@ void QQuickMessageDialog::setButtons(QPlatformDialogHelper::StandardButtons butt
void QQuickMessageDialog::handleClick(QPlatformDialogHelper::StandardButton button,
QPlatformDialogHelper::ButtonRole role)
{
+ m_roleOfLastButtonPressed = role;
emit buttonClicked(button, role);
+ done(button);
}
void QQuickMessageDialog::onCreate(QPlatformDialogHelper *dialog)
@@ -240,6 +242,20 @@ void QQuickMessageDialog::onShow(QPlatformDialogHelper *dialog)
messageDialog->setOptions(m_options); // setOptions only assigns a member and isn't virtual
}
+int QQuickMessageDialog::dialogCode() const
+{
+ switch (m_roleOfLastButtonPressed) {
+ case QPlatformDialogHelper::AcceptRole:
+ case QPlatformDialogHelper::YesRole:
+ return Accepted;
+ case QPlatformDialogHelper::RejectRole:
+ case QPlatformDialogHelper::NoRole:
+ return Rejected;
+ default:
+ return QQuickAbstractDialog::dialogCode();
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qquickmessagedialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h b/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h
index 40a2b6da8d..ea0b2bbae2 100644
--- a/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h
+++ b/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickMessageDialog : public QQuickAbstractDialog
+class Q_QUICKDIALOGS2_EXPORT QQuickMessageDialog : public QQuickAbstractDialog
{
Q_OBJECT
@@ -63,13 +63,13 @@ private Q_SLOTS:
protected:
void onCreate(QPlatformDialogHelper *dialog) override;
void onShow(QPlatformDialogHelper *dialog) override;
+ int dialogCode() const override;
private:
QSharedPointer<QMessageDialogOptions> m_options;
+ QPlatformDialogHelper::ButtonRole m_roleOfLastButtonPressed = QPlatformDialogHelper::NoRole;
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMessageDialog)
-
#endif // QQUICKMESSAGEDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h b/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h
index 85f9ce6dc4..68a540af12 100644
--- a/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h
+++ b/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h
@@ -17,6 +17,6 @@
#include <QtCore/qglobal.h>
#include <QtQml/private/qqmlglobal_p.h>
-#include <QtQuickDialogs2/private/qtquickdialogs2exports_p.h>
+#include <QtQuickDialogs2/qtquickdialogs2exports.h>
#endif // QTQUICKDIALOGS2GLOBAL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt b/src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt
index c4ccceef39..9191eaa379 100644
--- a/src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt
+++ b/src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt
@@ -63,6 +63,7 @@ qt_internal_add_qml_module(QuickDialogs2QuickImpl
DEPENDENCIES
QtQuick/auto
QtQuick.Templates/auto
+ QtQuick.Layouts/auto
SOURCES
qquickabstractcolorpicker.cpp
qquickabstractcolorpicker_p.h
@@ -76,31 +77,11 @@ qt_internal_add_qml_module(QuickDialogs2QuickImpl
qquickcolorinputs_p.h
qquickdialogimplfactory.cpp
qquickdialogimplfactory_p.h
- qquickfiledialogdelegate.cpp
- qquickfiledialogdelegate_p.h
- qquickfiledialogimpl.cpp
- qquickfiledialogimpl_p.h
- qquickfiledialogimpl_p_p.h
- qquickfolderbreadcrumbbar.cpp
- qquickfolderbreadcrumbbar_p.h
- qquickfolderbreadcrumbbar_p_p.h
- qquickfolderdialogimpl.cpp
- qquickfolderdialogimpl_p.h
- qquickfolderdialogimpl_p_p.h
- qquickfontdialogimpl.cpp
- qquickfontdialogimpl_p.h
- qquickfontdialogimpl_p_p.h
qquickmessagedialogimpl.cpp
qquickmessagedialogimpl_p.h
qquickmessagedialogimpl_p_p.h
qquickplatformcolordialog.cpp
qquickplatformcolordialog_p.h
- qquickplatformfiledialog.cpp
- qquickplatformfiledialog_p.h
- qquickplatformfolderdialog.cpp
- qquickplatformfolderdialog_p.h
- qquickplatformfontdialog.cpp
- qquickplatformfontdialog_p.h
qquickplatformmessagedialog.cpp
qquickplatformmessagedialog_p.h
qquicksaturationlightnesspicker.cpp
@@ -120,18 +101,59 @@ qt_internal_add_qml_module(QuickDialogs2QuickImpl
Qt::CorePrivate
Qt::GuiPrivate
Qt::QmlPrivate
- Qt::QuickPrivate
- Qt::QuickTemplates2
- Qt::QuickTemplates2Private
Qt::QuickControls2ImplPrivate
Qt::QuickDialogs2Utils
Qt::QuickDialogs2UtilsPrivate
+ Qt::QuickPrivate
+ Qt::QuickTemplates2
+ Qt::QuickTemplates2Private
PUBLIC_LIBRARIES
Qt::Core
Qt::Gui
Qt::Quick
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
+)
+
+add_dependencies(QuickDialogs2QuickImpl Qt::QuickControls2Basic)
+
+if(QT_FEATURE_quickcontrols2_fusion)
+ add_dependencies(QuickDialogs2QuickImpl QuickControls2Fusion)
+endif()
+
+if(QT_FEATURE_quickcontrols2_imagine)
+ add_dependencies(QuickDialogs2QuickImpl QuickControls2Imagine)
+endif()
+
+if(QT_FEATURE_quickcontrols2_material)
+ add_dependencies(QuickDialogs2QuickImpl QuickControls2Material)
+endif()
+
+if(QT_FEATURE_quickcontrols2_universal)
+ add_dependencies(QuickDialogs2QuickImpl QuickControls2Universal)
+endif()
+
+qt_internal_extend_target(QuickDialogs2QuickImpl CONDITION QT_FEATURE_quick_listview
+ SOURCES
+ qquickfiledialogdelegate.cpp
+ qquickfiledialogdelegate_p.h
+ qquickfiledialogimpl.cpp
+ qquickfiledialogimpl_p.h
+ qquickfiledialogimpl_p_p.h
+ qquickfolderbreadcrumbbar.cpp
+ qquickfolderbreadcrumbbar_p.h
+ qquickfolderbreadcrumbbar_p_p.h
+ qquickfolderdialogimpl.cpp
+ qquickfolderdialogimpl_p.h
+ qquickfolderdialogimpl_p_p.h
+ qquickfontdialogimpl.cpp
+ qquickfontdialogimpl_p.h
+ qquickfontdialogimpl_p_p.h
+ qquickplatformfiledialog.cpp
+ qquickplatformfiledialog_p.h
+ qquickplatformfolderdialog.cpp
+ qquickplatformfolderdialog_p.h
+ qquickplatformfontdialog.cpp
+ qquickplatformfontdialog_p.h
)
# Resources:
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml
index 591ac46a84..142fecbf7a 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml
@@ -64,6 +64,7 @@ ColorDialogImpl {
padding: 6
Layout.preferredWidth: control.title.length > 0 ? implicitWidth : 0
+ Layout.preferredHeight: control.title.length > 0 ? implicitHeight : 15
Layout.leftMargin: 12
Layout.alignment: Qt.AlignLeft
}
@@ -85,10 +86,10 @@ ColorDialogImpl {
SaturationLightnessPicker {
id: colorPicker
objectName: "colorPicker"
- implicitHeight: width
color: control.color
Layout.fillWidth: true
+ Layout.fillHeight: true
}
Slider {
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml
index a928a4b0cc..965f56bdc7 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml
@@ -31,6 +31,29 @@ FileDialogImpl {
standardButtons: T.Dialog.Open | T.Dialog.Cancel
+ Dialog {
+ id: overwriteConfirmationDialog
+ objectName: "confirmationDialog"
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ dim: true
+ modal: true
+ title: qsTr("“%1†already exists. Do you want to replace it?").arg(control.fileName)
+
+ Label {
+ text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName)
+ }
+
+ footer: DialogButtonBox {
+ alignment: Qt.AlignHCenter
+ standardButtons: DialogButtonBox.Yes | DialogButtonBox.No
+ }
+
+ Overlay.modal: Rectangle {
+ color: Fusion.darkShade
+ }
+ }
+
/*
We use attached properties because we want to handle logic in C++, and:
- We can't assume the footer only contains a DialogButtonBox (which would allow us
@@ -45,6 +68,7 @@ FileDialogImpl {
FileDialogImpl.breadcrumbBar: breadcrumbBar
FileDialogImpl.fileNameLabel: fileNameLabel
FileDialogImpl.fileNameTextField: fileNameTextField
+ FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog
background: Rectangle {
implicitWidth: 600
@@ -124,7 +148,7 @@ FileDialogImpl {
fileDetailRowWidth: nameFiltersComboBox.width
KeyNavigation.backtab: breadcrumbBar
- KeyNavigation.tab: nameFiltersComboBox
+ KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox
}
}
}
@@ -143,7 +167,6 @@ FileDialogImpl {
TextField {
id: fileNameTextField
objectName: "fileNameTextField"
- text: control.fileName
visible: false
Layout.fillWidth: true
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml
index 475528d2ec..20e8421d3f 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml
@@ -34,7 +34,7 @@ DialogsQuickImpl.FileDialogDelegate {
required property int index
required property string fileName
required property url fileUrl
- required property int fileSize
+ required property double fileSize
required property date fileModified
required property bool fileIsDir
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml
index 641453bca5..655b55cf83 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml
@@ -57,6 +57,7 @@ ColorDialogImpl {
header: RowLayout {
Label {
+ objectName: "titleLabel"
text: control.title
elide: Label.ElideRight
font.bold: true
@@ -67,10 +68,12 @@ ColorDialogImpl {
bottomPadding: 16
Layout.preferredWidth: control.title.length > 0 ? implicitWidth : 0
+ Layout.preferredHeight: control.title.length > 0 ? implicitHeight : 15
}
Button {
id: eyeDropperButton
+ objectName: "eyeDropperButton"
icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/eye-dropper.png"
flat: true
topPadding: 16
@@ -86,10 +89,10 @@ ColorDialogImpl {
SaturationLightnessPicker {
id: colorPicker
objectName: "colorPicker"
- implicitHeight: width
color: control.color
Layout.fillWidth: true
+ Layout.fillHeight: true
}
Slider {
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml
index 664965e571..8568be710a 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml
@@ -39,12 +39,34 @@ FileDialogImpl {
standardButtons: T.Dialog.Open | T.Dialog.Cancel
+ Dialog {
+ id: overwriteConfirmationDialog
+ objectName: "confirmationDialog"
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ dim: true
+ modal: true
+ spacing: 12
+ title: qsTr("“%1†already exists. Do you want to replace it?").arg(control.fileName)
+
+ Label {
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName)
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ footer: DialogButtonBox {
+ standardButtons: DialogButtonBox.Yes | DialogButtonBox.No
+ }
+ }
+
FileDialogImpl.buttonBox: buttonBox
FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox
FileDialogImpl.fileDialogListView: fileDialogListView
FileDialogImpl.breadcrumbBar: breadcrumbBar
FileDialogImpl.fileNameLabel: fileNameLabel
FileDialogImpl.fileNameTextField: fileNameTextField
+ FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog
background: NinePatchImage {
source: Imagine.url + "dialog-background"
@@ -115,6 +137,9 @@ FileDialogImpl {
highlighted: ListView.isCurrentItem
dialog: control
fileDetailRowWidth: nameFiltersComboBox.width
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox
}
}
@@ -133,7 +158,6 @@ FileDialogImpl {
TextField {
id: fileNameTextField
objectName: "fileNameTextField"
- text: control.fileName
visible: false
Layout.fillWidth: true
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml
index 30094a7d9d..a4f627d48e 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml
@@ -39,7 +39,7 @@ DialogsQuickImpl.FileDialogDelegate {
required property int index
required property string fileName
required property url fileUrl
- required property int fileSize
+ required property double fileSize
required property date fileModified
required property bool fileIsDir
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml
index 313e8645b4..dd1f887dd9 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml
@@ -50,6 +50,7 @@ ColorDialogImpl {
header: RowLayout {
Label {
+ objectName: "titleLabel"
text: control.title
elide: Label.ElideRight
font.bold: true
@@ -60,10 +61,12 @@ ColorDialogImpl {
bottomPadding: 24
Layout.preferredWidth: control.title.length > 0 ? implicitWidth : 0
+ Layout.preferredHeight: control.title.length > 0 ? implicitHeight : 15
}
Button {
id: eyeDropperButton
+ objectName: "eyeDropperButton"
icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/eye-dropper.png"
flat: true
topPadding: 24
@@ -80,10 +83,10 @@ ColorDialogImpl {
SaturationLightnessPicker {
id: colorPicker
objectName: "colorPicker"
- implicitHeight: width
color: control.color
Layout.fillWidth: true
+ Layout.fillHeight: true
}
Slider {
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml
index cd2c513c2e..9700aeba79 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml
@@ -32,12 +32,32 @@ FileDialogImpl {
Material.elevation: 24
+ Dialog {
+ id: overwriteConfirmationDialog
+ objectName: "confirmationDialog"
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ dim: true
+ modal: true
+ title: qsTr("“%1†already exists. Do you want to replace it?").arg(control.fileName)
+
+ Label {
+ text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName)
+ }
+
+ footer: DialogButtonBox {
+ alignment: Qt.AlignHCenter
+ standardButtons: DialogButtonBox.Yes | DialogButtonBox.No
+ }
+ }
+
FileDialogImpl.buttonBox: buttonBox
FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox
FileDialogImpl.fileDialogListView: fileDialogListView
FileDialogImpl.breadcrumbBar: breadcrumbBar
FileDialogImpl.fileNameLabel: fileNameLabel
FileDialogImpl.fileNameTextField: fileNameTextField
+ FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog
background: Rectangle {
implicitWidth: 600
@@ -97,6 +117,9 @@ FileDialogImpl {
highlighted: ListView.isCurrentItem
dialog: control
fileDetailRowWidth: nameFiltersComboBox.width
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox
}
}
@@ -116,7 +139,6 @@ FileDialogImpl {
TextField {
id: fileNameTextField
objectName: "fileNameTextField"
- text: control.fileName
visible: false
Layout.topMargin: 12
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml
index c96231345d..9ec992b7a6 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml
@@ -31,7 +31,7 @@ DialogsQuickImpl.FileDialogDelegate {
required property int index
required property string fileName
required property url fileUrl
- required property int fileSize
+ required property double fileSize
required property date fileModified
required property bool fileIsDir
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml
index a678503d6c..e336185430 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml
@@ -117,6 +117,8 @@ MessageDialogImpl {
Layout.bottomMargin: 20
background: Rectangle {
+ implicitWidth: 120
+ implicitHeight: control.Material.textFieldHeight
color: Qt.rgba(1,1,1,1)
radius: 3
border.color: Qt.darker(control.palette.light)
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml
index d13b751652..384fa41e7e 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml
@@ -47,6 +47,7 @@ ColorDialogImpl {
spacing: 12
Label {
+ objectName: "titleLabel"
text: control.title
elide: Label.ElideRight
// TODO: QPlatformTheme::TitleBarFont
@@ -63,10 +64,12 @@ ColorDialogImpl {
Layout.leftMargin: 18
Layout.fillWidth: true
Layout.preferredWidth: control.title.length > 0 ? implicitHeight : 0
+ Layout.preferredHeight: control.title.length > 0 ? implicitHeight : 15
}
Button {
id: eyeDropperButton
+ objectName: "eyeDropperButton"
icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/eye-dropper.png"
flat: true
topPadding: 24
@@ -83,10 +86,10 @@ ColorDialogImpl {
SaturationLightnessPicker {
id: colorPicker
objectName: "colorPicker"
- implicitHeight: width
color: control.color
Layout.fillWidth: true
+ Layout.fillHeight: true
}
Slider {
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml
index c029b06293..0d2db8b426 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml
@@ -30,12 +30,35 @@ FileDialogImpl {
standardButtons: T.Dialog.Open | T.Dialog.Cancel
+ Dialog {
+ id: overwriteConfirmationDialog
+ objectName: "confirmationDialog"
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ dim: true
+ modal: true
+ title: qsTr("“%1†already exists. Do you want to replace it?").arg(control.fileName)
+
+ Label {
+ text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName)
+ }
+
+ footer: DialogButtonBox {
+ standardButtons: DialogButtonBox.Yes | DialogButtonBox.No
+ }
+
+ Overlay.modal: Rectangle {
+ color: overwriteConfirmationDialog.Universal.baseMediumColor
+ }
+ }
+
FileDialogImpl.buttonBox: buttonBox
FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox
FileDialogImpl.fileDialogListView: fileDialogListView
FileDialogImpl.breadcrumbBar: breadcrumbBar
FileDialogImpl.fileNameLabel: fileNameLabel
FileDialogImpl.fileNameTextField: fileNameTextField
+ FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog
background: Rectangle {
implicitWidth: 600
@@ -99,6 +122,9 @@ FileDialogImpl {
highlighted: ListView.isCurrentItem
dialog: control
fileDetailRowWidth: nameFiltersComboBox.width
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox
}
}
@@ -117,7 +143,6 @@ FileDialogImpl {
TextField {
id: fileNameTextField
objectName: "fileNameTextField"
- text: control.fileName
visible: false
Layout.fillWidth: true
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml
index 7650ec7a34..dd23f3d1e7 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml
@@ -32,7 +32,7 @@ DialogsQuickImpl.FileDialogDelegate {
required property int index
required property string fileName
required property url fileUrl
- required property int fileSize
+ required property double fileSize
required property date fileModified
required property bool fileIsDir
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml
index ac63d281d1..37284503d2 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml
@@ -115,6 +115,8 @@ MessageDialogImpl {
Layout.bottomMargin: 20
background: Rectangle {
+ implicitWidth: 60 // TextControlThemeMinWidth - 4 (border)
+ implicitHeight: 28 // TextControlThemeMinHeight - 4 (border)
color: Qt.rgba(1,1,1,1)
radius: 3
border.color: Qt.darker(control.palette.light)
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml
index c8395bacd0..a4cbe498f0 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml
@@ -59,6 +59,7 @@ ColorDialogImpl {
font.bold: true
Layout.preferredWidth: control.title.length > 0 ? implicitWidth : 0
+ Layout.preferredHeight: control.title.length > 0 ? implicitHeight : 15
Layout.leftMargin: 12
Layout.alignment: Qt.AlignLeft
}
@@ -81,10 +82,10 @@ ColorDialogImpl {
SaturationLightnessPicker {
id: colorPicker
objectName: "colorPicker"
- implicitHeight: width
color: control.color
Layout.fillWidth: true
+ Layout.fillHeight: true
}
Slider {
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml
index 0f25dee35b..defb7dd4e2 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml
@@ -34,6 +34,25 @@ FileDialogImpl {
standardButtons: T.Dialog.Open | T.Dialog.Cancel
+ Dialog {
+ id: overwriteConfirmationDialog
+ objectName: "confirmationDialog"
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ dim: true
+ modal: true
+ title: qsTr("“%1†already exists. Do you want to replace it?").arg(control.fileName)
+
+ Label {
+ text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName)
+ }
+
+ footer: DialogButtonBox {
+ alignment: Qt.AlignHCenter
+ standardButtons: DialogButtonBox.Yes | DialogButtonBox.No
+ }
+ }
+
/*
We use attached properties because we want to handle logic in C++, and:
- We can't assume the footer only contains a DialogButtonBox (which would allow us
@@ -48,6 +67,7 @@ FileDialogImpl {
FileDialogImpl.breadcrumbBar: breadcrumbBar
FileDialogImpl.fileNameLabel: fileNameLabel
FileDialogImpl.fileNameTextField: fileNameTextField
+ FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog
background: Rectangle {
implicitWidth: 600
@@ -106,7 +126,7 @@ FileDialogImpl {
fileDetailRowWidth: nameFiltersComboBox.width
KeyNavigation.backtab: breadcrumbBar
- KeyNavigation.tab: nameFiltersComboBox
+ KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox
}
}
@@ -135,7 +155,6 @@ FileDialogImpl {
TextField {
id: fileNameTextField
objectName: "fileNameTextField"
- text: control.fileName
visible: false
Layout.fillWidth: true
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml
index 3abe35f43e..eea3ab93d6 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml
@@ -3,7 +3,7 @@
import QtQuick
import QtQuick.Controls
-import QtQuick.Controls.impl as ControlsImpl
+import QtQuick.Controls.impl
import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
DialogsQuickImpl.FileDialogDelegate {
@@ -34,7 +34,7 @@ DialogsQuickImpl.FileDialogDelegate {
required property int index
required property string fileName
required property url fileUrl
- required property int fileSize
+ required property double fileSize
required property date fileModified
required property bool fileIsDir
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml
index ac543e735d..a28d9dea08 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml
@@ -50,7 +50,12 @@ Item {
implicitHeight: childrenRect.height
Label {
- text: locale.formattedDataSize(root.delegate.fileSize)
+ text: {
+ const fileSize = root.delegate.fileSize;
+ return fileSize > Number.MAX_SAFE_INTEGER
+ ? ('>' + locale.formattedDataSize(Number.MAX_SAFE_INTEGER))
+ : locale.formattedDataSize(fileSize);
+ }
font.pixelSize: root.delegate.font.pixelSize * 0.75
color: root.fileDetailRowTextColor
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml
index 750a581a5d..87b826809a 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml
@@ -3,7 +3,7 @@
import QtQuick
import QtQuick.Controls
-import QtQuick.Controls.impl as ControlsImpl
+import QtQuick.Controls.impl
import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
DialogsQuickImpl.FileDialogDelegate {
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp
index 4606c00502..5b5b2abdae 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp
@@ -13,11 +13,6 @@
QQuickAbstractColorPickerPrivate::QQuickAbstractColorPickerPrivate() = default;
-static inline QString handleName()
-{
- return QStringLiteral("handle");
-}
-
bool QQuickAbstractColorPickerPrivate::handlePress(const QPointF &point, ulong timestamp)
{
Q_Q(QQuickAbstractColorPicker);
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h
index b29e488b3a..204bfd77b5 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKABSTRACTCOLORPICKER_P_H
#define QQUICKABSTRACTCOLORPICKER_P_H
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractColorPickerPrivate;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickAbstractColorPicker : public QQuickControl
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickAbstractColorPicker : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp
index 9721bae736..6772f24faa 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp
@@ -9,6 +9,7 @@
#include <QtQuickTemplates2/private/qquickslider_p.h>
#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformservices.h>
#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
@@ -91,7 +92,7 @@ QQuickColorDialogImplAttached *QQuickColorDialogImplPrivate::attachedOrWarn()
void QQuickColorDialogImplPrivate::eyeDropperEnter()
{
- Q_Q(const QQuickColorDialogImpl);
+ Q_Q(QQuickColorDialogImpl);
if (m_eyeDropperMode)
return;
@@ -104,6 +105,19 @@ void QQuickColorDialogImplPrivate::eyeDropperEnter()
m_eyeDropperWindow = window;
}
+ if (auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services();
+ platformServices && platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) {
+ if (auto *colorPickerService = platformServices->colorPicker(m_eyeDropperWindow)) {
+ q->connect(colorPickerService, &QPlatformServiceColorPicker::colorPicked, q,
+ [q, colorPickerService](const QColor &color) {
+ colorPickerService->deleteLater();
+ q->setColor(color);
+ });
+ colorPickerService->pickColor();
+ return;
+ }
+ }
+
m_eyeDropperPreviousColor = q->color();
if (!bool(eyeDropperEventFilter))
@@ -423,9 +437,13 @@ void QQuickColorDialogImpl::setOptions(const QSharedPointer<QColorDialogOptions>
QQuickColorDialogImplAttached *attached = d->attachedOrWarn();
if (attached) {
- const bool screenGrabbingAllowed = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ScreenWindowGrabbing);
+ const auto *integration = QGuiApplicationPrivate::platformIntegration();
+ const bool canSupportEyeDropper =
+ integration->hasCapability(QPlatformIntegration::ScreenWindowGrabbing)
+ || integration->services()->hasCapability(QPlatformServices::Capability::ColorPicking);
const bool offscreen = qgetenv("QT_QPA_PLATFORM").compare(QLatin1String("offscreen"), Qt::CaseInsensitive) == 0;
- attached->eyeDropperButton()->setVisible(screenGrabbingAllowed && !offscreen);
+ const bool noEyeDropperButton = (d->options && d->options->options() & QColorDialogOptions::NoEyeDropperButton);
+ attached->eyeDropperButton()->setVisible(!noEyeDropperButton && canSupportEyeDropper && !offscreen);
if (d->options) {
attached->buttonBox()->setVisible(
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h
index 110298b5f2..a714047c84 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h
@@ -30,7 +30,7 @@ class QQuickColorDialogImplAttached;
class QQuickColorDialogImplAttachedPrivate;
class QQuickColorDialogImplPrivate;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickColorDialogImpl : public QQuickDialog
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickColorDialogImpl : public QQuickDialog
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
@@ -96,7 +96,7 @@ private:
Q_DECLARE_PRIVATE(QQuickColorDialogImpl)
};
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickColorDialogImplAttached : public QObject
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickColorDialogImplAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox WRITE setButtonBox NOTIFY buttonBoxChanged FINAL)
@@ -145,6 +145,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickColorDialogImpl)
-
#endif // QQUICKCOLORDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h
index b7c4dc8e2c..503dff1a77 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h
@@ -24,6 +24,8 @@
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <QtQuickTemplates2/private/qquickslider_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickEyeDropperEventFilter : public QObject
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h
index 4827627d6f..a4e428fba0 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h
@@ -17,16 +17,17 @@
#include <QtGui/qcolor.h>
#include <QtQuick/private/qquickitem_p.h>
-#include <QtQuickTemplates2/private/qquickcombobox_p.h>
#include <QtQuickTemplates2/private/qquicktextfield_p.h>
#include "qtquickdialogs2quickimplglobal_p.h"
#include "qquickcolordialogutils_p.h"
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickColorInputs : public QQuickItem
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickColorInputs : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
index 9a63fb0718..594b148528 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
@@ -5,9 +5,12 @@
#include <QtCore/qloggingcategory.h>
+#include <QtQuick/private/qtquickglobal_p.h>
+#if QT_CONFIG(quick_listview)
#include "qquickplatformfiledialog_p.h"
#include "qquickplatformfolderdialog_p.h"
#include "qquickplatformfontdialog_p.h"
+#endif
#include "qquickplatformcolordialog_p.h"
#include "qquickplatformmessagedialog_p.h"
@@ -29,6 +32,7 @@ std::unique_ptr<QPlatformDialogHelper> QQuickDialogImplFactory::createPlatformDi
dialogHelper.reset(new QQuickPlatformColorDialog(parent));
break;
}
+#if QT_CONFIG(quick_listview)
case QQuickDialogType::FileDialog: {
dialogHelper.reset(new QQuickPlatformFileDialog(parent));
break;
@@ -41,6 +45,7 @@ std::unique_ptr<QPlatformDialogHelper> QQuickDialogImplFactory::createPlatformDi
dialogHelper.reset(new QQuickPlatformFontDialog(parent));
break;
}
+#endif
case QQuickDialogType::MessageDialog: {
dialogHelper.reset(new QQuickPlatformMessageDialog(parent));
break;
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h
index 5072c9e1cb..337c4f175a 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickDialogImplFactory
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickDialogImplFactory
{
public:
static std::unique_ptr<QPlatformDialogHelper> createPlatformDialogHelper(QQuickDialogType type, QObject *parent);
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp
index 71b1fccbc5..369176f844 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp
@@ -11,6 +11,7 @@
#include <QtQuickTemplates2/private/qquickitemdelegate_p_p.h>
#include "qquickfiledialogimpl_p.h"
+#include "qquickfiledialogimpl_p_p.h"
#include "qquickfolderdialogimpl_p.h"
QT_BEGIN_NAMESPACE
@@ -67,6 +68,16 @@ void QQuickFileDialogDelegatePrivate::chooseFile()
Q_ASSERT(fileDialog);
// Otherwise it's a file, so select it and close the dialog.
fileDialog->setSelectedFile(file);
+
+ // Prioritize closing the dialog with QQuickDialogPrivate::handleClick() over QQuickDialog::accept()
+ const QQuickFileDialogImplAttached *attached = QQuickFileDialogImplPrivate::get(fileDialog)->attachedOrWarn();
+ if (Q_LIKELY(attached)) {
+ auto *openButton = attached->buttonBox()->standardButton(QPlatformDialogHelper::Open);
+ if (Q_LIKELY(openButton)) {
+ emit openButton->clicked();
+ return;
+ }
+ }
fileDialog->accept();
}
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h
index 36804497d7..de84b91f50 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickDialog;
class QQuickFileDialogDelegatePrivate;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogDelegate : public QQuickItemDelegate
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFileDialogDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(QQuickDialog *dialog READ dialog WRITE setDialog NOTIFY dialogChanged)
@@ -55,6 +55,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFileDialogDelegate)
-
#endif // QQUICKFILEDIALOGDELEGATE_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp
index 72f2ea25cf..687e899d18 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp
@@ -137,12 +137,24 @@ void QQuickFileDialogImplPrivate::updateSelectedFile(const QString &oldFolderPat
qCDebug(lcUpdateSelectedFile).nospace() << "updateSelectedFile is setting selectedFile to " << newSelectedFileUrl
<< ", newSelectedFileIndex is " << newSelectedFileIndex;
q->setSelectedFile(newSelectedFileUrl);
+ updateFileNameTextEdit();
// If the index is -1, there are no files in the directory, and so fileDialogListView's
// currentIndex will already be -1.
if (newSelectedFileIndex != -1)
tryUpdateFileDialogListViewCurrentIndex(newSelectedFileIndex);
}
+void QQuickFileDialogImplPrivate::updateFileNameTextEdit()
+{
+ QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ if (Q_UNLIKELY(!attached))
+ return;
+
+ const QFileInfo fileInfo(selectedFile.toLocalFile());
+ if (fileInfo.isFile())
+ attached->fileNameTextField()->setText(fileInfo.fileName());
+}
+
QDir::SortFlags QQuickFileDialogImplPrivate::fileListSortFlags()
{
QDir::SortFlags sortFlags = QDir::IgnoreCase;
@@ -238,14 +250,35 @@ void QQuickFileDialogImplPrivate::handleClick(QQuickAbstractButton *button)
// Don't call accept(), because selecting a folder != accepting the dialog.
} else {
// Otherwise it's a file, so select it and close the dialog.
- q->setSelectedFile(selectedFile);
- q->accept();
- QQuickDialogPrivate::handleClick(button);
- emit q->fileSelected(selectedFile);
+
+ lastButtonClicked = button;
+
+ // Unless it already exists...
+ const bool dontConfirmOverride = q->options()->testOption(QFileDialogOptions::DontConfirmOverwrite);
+ const bool isSaveMode = q->options()->fileMode() == QFileDialogOptions::AnyFile;
+ if (QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ attached && fileInfo.exists() && isSaveMode && !dontConfirmOverride) {
+ QQuickDialog *confirmationDialog = attached->overwriteConfirmationDialog();
+ confirmationDialog->open();
+ static_cast<QQuickDialogButtonBox *>(confirmationDialog->footer())->standardButton(QPlatformDialogHelper::Yes)
+ ->forceActiveFocus(Qt::PopupFocusReason);
+ } else {
+ selectFile();
+ }
}
}
}
+void QQuickFileDialogImplPrivate::selectFile()
+{
+ Q_Q(QQuickFileDialogImpl);
+ Q_ASSERT(lastButtonClicked);
+ q->setSelectedFile(selectedFile);
+ q->accept();
+ QQuickDialogPrivate::handleClick(lastButtonClicked);
+ emit q->fileSelected(selectedFile);
+}
+
QQuickFileDialogImpl::QQuickFileDialogImpl(QObject *parent)
: QQuickDialog(*(new QQuickFileDialogImplPrivate), parent)
{
@@ -332,6 +365,7 @@ void QQuickFileDialogImpl::setInitialCurrentFolderAndSelectedFile(const QUrl &fi
qCDebug(lcSelectedFile) << "setting initial currentFolder to" << fileDirUrl << "and selectedFile to" << file;
setCurrentFolder(fileDirUrl, QQuickFileDialogImpl::SetReason::Internal);
setSelectedFile(file);
+ d->updateFileNameTextEdit();
d->setCurrentIndexToInitiallySelectedFile = true;
// If the currentFolder didn't change, the FolderListModel won't change and
@@ -426,8 +460,11 @@ void QQuickFileDialogImpl::setAcceptLabel(const QString &label)
return;
}
+ auto buttonType = (d->options && d->options->acceptMode() == QFileDialogOptions::AcceptSave)
+ ? QPlatformDialogHelper::Save
+ : QPlatformDialogHelper::Open;
acceptButton->setText(!label.isEmpty()
- ? label : QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::Open));
+ ? label : QQuickDialogButtonBoxPrivate::buttonText(buttonType));
}
void QQuickFileDialogImpl::setRejectLabel(const QString &label)
@@ -467,7 +504,15 @@ void QQuickFileDialogImpl::setFileName(const QString &fileName)
if (previous == fileName)
return;
- setSelectedFile(QUrl(currentFolder().path() + u'/' + fileName));
+ QUrl newSelectedFile;
+ newSelectedFile.setScheme(currentFolder().scheme());
+ newSelectedFile.setPath(currentFolder().path() + u'/' + fileName);
+ setSelectedFile(newSelectedFile);
+}
+
+QString QQuickFileDialogImpl::currentFolderName() const
+{
+ return QDir(currentFolder().toLocalFile()).dirName();
}
void QQuickFileDialogImpl::componentComplete()
@@ -561,6 +606,7 @@ void QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged(
auto fileDialogImplPrivate = QQuickFileDialogImplPrivate::get(fileDialogImpl);
if (moveReason != QQuickItemViewPrivate::Other) {
fileDialogImpl->setSelectedFile(fileDialogDelegate->file());
+ fileDialogImplPrivate->updateFileNameTextEdit();
} else if (fileDialogImplPrivate->setCurrentIndexToInitiallySelectedFile) {
// When setting selectedFile before opening the FileDialog,
// we need to ensure that the currentIndex is correct, because the initial change
@@ -573,7 +619,17 @@ void QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged(
}
}
-void QQuickFileDialogImplAttachedPrivate::fileNameChangedByUser()
+void QQuickFileDialogImplAttachedPrivate::fileNameEditedByUser()
+{
+ if (!buttonBox)
+ return;
+ auto openButton = buttonBox->standardButton(QPlatformDialogHelper::Open);
+ if (!openButton || !fileNameTextField)
+ return;
+ openButton->setEnabled(!fileNameTextField->text().isEmpty());
+}
+
+void QQuickFileDialogImplAttachedPrivate::fileNameEditingByUserFinished()
{
auto fileDialogImpl = qobject_cast<QQuickFileDialogImpl *>(parent);
if (!fileDialogImpl)
@@ -740,19 +796,50 @@ void QQuickFileDialogImplAttached::setFileNameTextField(QQuickTextField *fileNam
if (fileNameTextField == d->fileNameTextField)
return;
- if (d->fileNameTextField)
+ if (d->fileNameTextField) {
QObjectPrivate::disconnect(d->fileNameTextField, &QQuickTextField::editingFinished,
- d, &QQuickFileDialogImplAttachedPrivate::fileNameChangedByUser);
+ d, &QQuickFileDialogImplAttachedPrivate::fileNameEditingByUserFinished);
+ QObjectPrivate::disconnect(d->fileNameTextField, &QQuickTextField::textEdited,
+ d, &QQuickFileDialogImplAttachedPrivate::fileNameEditedByUser);
+ }
d->fileNameTextField = fileNameTextField;
- if (d->fileNameTextField)
+ if (d->fileNameTextField) {
QObjectPrivate::connect(d->fileNameTextField, &QQuickTextField::editingFinished,
- d, &QQuickFileDialogImplAttachedPrivate::fileNameChangedByUser);
-
+ d, &QQuickFileDialogImplAttachedPrivate::fileNameEditingByUserFinished);
+ QObjectPrivate::connect(d->fileNameTextField, &QQuickTextField::textEdited,
+ d, &QQuickFileDialogImplAttachedPrivate::fileNameEditedByUser);
+ }
emit fileNameTextFieldChanged();
}
+QQuickDialog *QQuickFileDialogImplAttached::overwriteConfirmationDialog() const
+{
+ Q_D(const QQuickFileDialogImplAttached);
+ return d->overwriteConfirmationDialog;
+}
+
+void QQuickFileDialogImplAttached::setOverwriteConfirmationDialog(QQuickDialog *dialog)
+{
+ Q_D(QQuickFileDialogImplAttached);
+ if (dialog == d->overwriteConfirmationDialog)
+ return;
+
+ QQuickFileDialogImpl *fileDialogImpl = qobject_cast<QQuickFileDialogImpl*>(parent());
+ if (d->overwriteConfirmationDialog && fileDialogImpl)
+ QObjectPrivate::disconnect(d->overwriteConfirmationDialog, &QQuickDialog::accepted,
+ QQuickFileDialogImplPrivate::get(fileDialogImpl), &QQuickFileDialogImplPrivate::selectFile);
+
+ d->overwriteConfirmationDialog = dialog;
+
+ if (d->overwriteConfirmationDialog && fileDialogImpl)
+ QObjectPrivate::connect(d->overwriteConfirmationDialog, &QQuickDialog::accepted,
+ QQuickFileDialogImplPrivate::get(fileDialogImpl), &QQuickFileDialogImplPrivate::selectFile, Qt::QueuedConnection);
+
+ emit overwriteConfirmationDialogChanged();
+}
+
QT_END_NAMESPACE
#include "moc_qquickfiledialogimpl_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h
index 4c64c5e4dd..f024892a97 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h
@@ -33,7 +33,7 @@ class QQuickFileDialogImplPrivate;
class QQuickFileNameFilter;
class QQuickFolderBreadcrumbBar;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImpl : public QQuickDialog
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFileDialogImpl : public QQuickDialog
{
Q_OBJECT
Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged FINAL)
@@ -41,6 +41,7 @@ class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImpl : public QQui
Q_PROPERTY(QStringList nameFilters READ nameFilters NOTIFY nameFiltersChanged FINAL)
Q_PROPERTY(QQuickFileNameFilter *selectedNameFilter READ selectedNameFilter CONSTANT)
Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY selectedFileChanged FINAL)
+ Q_PROPERTY(QString currentFolderName READ currentFolderName NOTIFY selectedFileChanged FINAL)
QML_NAMED_ELEMENT(FileDialogImpl)
QML_ATTACHED(QQuickFileDialogImplAttached)
QML_ADDED_IN_VERSION(6, 2)
@@ -80,6 +81,8 @@ public:
QString fileName() const;
void setFileName(const QString &fileName);
+ QString currentFolderName() const;
+
public Q_SLOTS:
void selectNameFilter(const QString &filter);
@@ -98,7 +101,7 @@ private:
Q_DECLARE_PRIVATE(QQuickFileDialogImpl)
};
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImplAttached : public QObject
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFileDialogImplAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox WRITE setButtonBox NOTIFY buttonBoxChanged FINAL)
@@ -107,6 +110,7 @@ class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImplAttached : pub
Q_PROPERTY(QQuickFolderBreadcrumbBar *breadcrumbBar READ breadcrumbBar WRITE setBreadcrumbBar NOTIFY breadcrumbBarChanged)
Q_PROPERTY(QQuickLabel *fileNameLabel READ fileNameLabel WRITE setFileNameLabel NOTIFY fileNameLabelChanged FINAL)
Q_PROPERTY(QQuickTextField *fileNameTextField READ fileNameTextField WRITE setFileNameTextField NOTIFY fileNameTextFieldChanged FINAL)
+ Q_PROPERTY(QQuickDialog *overwriteConfirmationDialog READ overwriteConfirmationDialog WRITE setOverwriteConfirmationDialog NOTIFY overwriteConfirmationDialogChanged FINAL)
Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>)
Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickcombobox_p.h>)
Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquicktextfield_p.h>)
@@ -136,6 +140,9 @@ public:
QQuickTextField *fileNameTextField() const;
void setFileNameTextField(QQuickTextField *fileNameTextField);
+ QQuickDialog *overwriteConfirmationDialog() const;
+ void setOverwriteConfirmationDialog(QQuickDialog *dialog);
+
Q_SIGNALS:
void buttonBoxChanged();
void nameFiltersComboBoxChanged();
@@ -143,6 +150,7 @@ Q_SIGNALS:
void breadcrumbBarChanged();
void fileNameLabelChanged();
void fileNameTextFieldChanged();
+ void overwriteConfirmationDialogChanged();
private:
Q_DISABLE_COPY(QQuickFileDialogImplAttached)
@@ -151,6 +159,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFileDialogImpl)
-
#endif // QQUICKFILEDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h
index 6f3cc55e86..66a77ab5e0 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h
@@ -23,11 +23,13 @@
#include "qquickfiledialogimpl_p.h"
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickFileNameFilter;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImplPrivate : public QQuickDialogPrivate
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFileDialogImplPrivate : public QQuickDialogPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickFileDialogImpl)
@@ -45,6 +47,7 @@ public:
void updateEnabled();
void updateSelectedFile(const QString &oldFolderPath);
+ void updateFileNameTextEdit();
static QDir::SortFlags fileListSortFlags();
static QFileInfoList fileList(const QDir &dir);
void setFileDialogListViewCurrentIndex(int newCurrentIndex);
@@ -53,10 +56,12 @@ public:
void handleAccept() override;
void handleClick(QQuickAbstractButton *button) override;
+ void selectFile();
QSharedPointer<QFileDialogOptions> options;
QUrl currentFolder;
QUrl selectedFile;
+ QQuickAbstractButton *lastButtonClicked = nullptr;
QStringList nameFilters;
mutable QQuickFileNameFilter *selectedNameFilter = nullptr;
QString acceptLabel;
@@ -70,7 +75,8 @@ class QQuickFileDialogImplAttachedPrivate : public QObjectPrivate
{
void nameFiltersComboBoxItemActivated(int index);
void fileDialogListViewCurrentIndexChanged();
- void fileNameChangedByUser();
+ void fileNameEditedByUser();
+ void fileNameEditingByUserFinished();
public:
Q_DECLARE_PUBLIC(QQuickFileDialogImplAttached)
@@ -81,6 +87,7 @@ public:
QPointer<QQuickFolderBreadcrumbBar> breadcrumbBar;
QPointer<QQuickLabel> fileNameLabel;
QPointer<QQuickTextField> fileNameTextField;
+ QPointer<QQuickDialog> overwriteConfirmationDialog;
};
QT_END_NAMESPACE
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h
index d210112b33..ce952c453b 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickFolderBreadcrumbBarPrivate;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderBreadcrumbBar : public QQuickContainer
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFolderBreadcrumbBar : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(QQuickDialog *dialog READ dialog WRITE setDialog NOTIFY dialogChanged)
@@ -89,6 +89,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFolderBreadcrumbBar)
-
#endif // QQUICKFOLDERBREADCRUMBBAR_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h
index 42c794256d..4ad6c5280a 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h
@@ -27,7 +27,7 @@ class QQuickTextField;
class QQuickFileDialogImpl;
class QQuickFolderDialogImpl;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderBreadcrumbBarPrivate : public QQuickContainerPrivate
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFolderBreadcrumbBarPrivate : public QQuickContainerPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickFolderBreadcrumbBar)
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h
index 021ef5479a..fe92ac1525 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h
@@ -29,7 +29,7 @@ class QQuickFolderDialogImplAttachedPrivate;
class QQuickFolderDialogImplPrivate;
class QQuickFolderBreadcrumbBar;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderDialogImpl : public QQuickDialog
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFolderDialogImpl : public QQuickDialog
{
Q_OBJECT
Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged FINAL)
@@ -68,7 +68,7 @@ private:
Q_DECLARE_PRIVATE(QQuickFolderDialogImpl)
};
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderDialogImplAttached : public QObject
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFolderDialogImplAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickListView *folderDialogListView READ folderDialogListView WRITE setFolderDialogListView NOTIFY folderDialogListViewChanged)
@@ -95,6 +95,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFolderDialogImpl)
-
#endif // QQUICKFOLDERDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h
index b5abda79ad..329391a972 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h
@@ -20,6 +20,8 @@
#include "qquickfolderdialogimpl_p.h"
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickFolderDialogImplPrivate : public QQuickDialogPrivate
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h
index 6c30eb2474..313caf327a 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h
@@ -32,7 +32,7 @@ class QQuickFontDialogImplAttached;
class QQuickFontDialogImplAttachedPrivate;
class QQuickFontDialogImplPrivate;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFontDialogImpl : public QQuickDialog
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFontDialogImpl : public QQuickDialog
{
Q_OBJECT
Q_PROPERTY(QFont currentFont READ currentFont WRITE setCurrentFont NOTIFY currentFontChanged FINAL)
@@ -65,7 +65,7 @@ private:
Q_DECLARE_PRIVATE(QQuickFontDialogImpl)
};
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFontDialogImplAttached : public QObject
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickFontDialogImplAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickListView *familyListView READ familyListView WRITE setFamilyListView
@@ -178,6 +178,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFontDialogImpl)
-
#endif // QQUICKFONTDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h
index cd2ceb49b4..e7bbe9f474 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h
@@ -21,6 +21,8 @@
#include "qquickfontdialogimpl_p.h"
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickFontDialogImplPrivate : public QQuickDialogPrivate
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp
index d80011d127..578da61559 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp
@@ -21,8 +21,6 @@ void QQuickMessageDialogImplPrivate::handleClick(QQuickAbstractButton *button)
emit q->buttonClicked(standardButton, role);
}
-
- QQuickDialogPrivate::handleClick(button);
}
QQuickMessageDialogImplAttached *QQuickMessageDialogImplPrivate::attachedOrWarn()
@@ -125,13 +123,9 @@ void QQuickMessageDialogImplAttached::setButtonBox(QQuickDialogButtonBox *button
QQuickMessageDialogImpl *messageDialogImpl =
qobject_cast<QQuickMessageDialogImpl *>(parent());
if (messageDialogImpl) {
- auto dialogPrivate = QQuickDialogPrivate::get(messageDialogImpl);
- QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::accepted,
- dialogPrivate, &QQuickDialogPrivate::handleAccept);
- QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::rejected,
- dialogPrivate, &QQuickDialogPrivate::handleReject);
+ auto dialogPrivate = QQuickMessageDialogImplPrivate::get(messageDialogImpl);
QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::clicked, dialogPrivate,
- &QQuickDialogPrivate::handleClick);
+ &QQuickMessageDialogImplPrivate::handleClick);
}
}
@@ -141,13 +135,9 @@ void QQuickMessageDialogImplAttached::setButtonBox(QQuickDialogButtonBox *button
QQuickMessageDialogImpl *messageDialogImpl =
qobject_cast<QQuickMessageDialogImpl *>(parent());
if (messageDialogImpl) {
- auto dialogPrivate = QQuickDialogPrivate::get(messageDialogImpl);
- QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::accepted, dialogPrivate,
- &QQuickDialogPrivate::handleAccept);
- QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::rejected, dialogPrivate,
- &QQuickDialogPrivate::handleReject);
+ auto dialogPrivate = QQuickMessageDialogImplPrivate::get(messageDialogImpl);
QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::clicked, dialogPrivate,
- &QQuickDialogPrivate::handleClick);
+ &QQuickMessageDialogImplPrivate::handleClick);
}
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h
index c7871b8d11..317b0e4eda 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h
@@ -29,7 +29,7 @@ class QQuickMessageDialogImplAttached;
class QQuickMessageDialogImplAttachedPrivate;
class QQuickMessageDialogImplPrivate;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickMessageDialogImpl : public QQuickDialog
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickMessageDialogImpl : public QQuickDialog
{
Q_OBJECT
Q_PROPERTY(QString text READ text NOTIFY optionsChanged)
@@ -66,7 +66,7 @@ private:
Q_DECLARE_PRIVATE(QQuickMessageDialogImpl)
};
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickMessageDialogImplAttached : public QObject
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickMessageDialogImplAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox WRITE setButtonBox NOTIFY
@@ -93,6 +93,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMessageDialogImpl)
-
#endif // QQUICKMESSAGEDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h
index dca7ad218c..c2a66eae28 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h
@@ -19,6 +19,8 @@
#include "qquickmessagedialogimpl_p.h"
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickMessageDialogImplPrivate : public QQuickDialogPrivate
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h
index fa4aef9be2..f6fbf83226 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickColorDialogImpl;
class QWindow;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformColorDialog
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickPlatformColorDialog
: public QPlatformColorDialogHelper
{
Q_OBJECT
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h
index 0e0dedf71a..614ec6441a 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickFileDialogImpl;
class QWindow;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformFileDialog : public QPlatformFileDialogHelper
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickPlatformFileDialog : public QPlatformFileDialogHelper
{
Q_OBJECT
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h
index 73e2b2899b..6dc7f31945 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickFileDialogImpl;
class QWindow;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformFolderDialog : public QPlatformFileDialogHelper
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickPlatformFolderDialog : public QPlatformFileDialogHelper
{
Q_OBJECT
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h
index ec7ecc7bfe..31f3639218 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickFontDialogImpl;
class QWindow;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformFontDialog
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickPlatformFontDialog
: public QPlatformFontDialogHelper
{
Q_OBJECT
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h
index 86e44dd85d..77f6fe2b26 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformMessageDialog
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickPlatformMessageDialog
: public QPlatformMessageDialogHelper
{
Q_OBJECT
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h b/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h
index 1263e9173f..d08cbaf934 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickSaturationLightnessPickerPrivate;
-class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickSaturationLightnessPicker
+class Q_QUICKDIALOGS2QUICKIMPL_EXPORT QQuickSaturationLightnessPicker
: public QQuickAbstractColorPicker
{
Q_OBJECT
@@ -41,6 +41,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSaturationLightnessPicker)
-
#endif // QQUICKSATURATIONLIGHTNESSPICKER_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h b/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h
index ec593f048d..53b3bbf510 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h
@@ -17,6 +17,6 @@
#include <QtCore/qglobal.h>
#include <QtQml/private/qqmlglobal_p.h>
-#include <QtQuickDialogs2QuickImpl/private/qtquickdialogs2quickimplexports_p.h>
+#include <QtQuickDialogs2QuickImpl/qtquickdialogs2quickimplexports.h>
#endif // QTQUICKDIALOGS2QUICKIMPLGLOBAL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag b/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag
index eb69d83ca6..ad2b23faa1 100644
--- a/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag
+++ b/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
diff --git a/src/quickdialogs/quickdialogsutils/CMakeLists.txt b/src/quickdialogs/quickdialogsutils/CMakeLists.txt
index 32c175241b..bcf54b279c 100644
--- a/src/quickdialogs/quickdialogsutils/CMakeLists.txt
+++ b/src/quickdialogs/quickdialogsutils/CMakeLists.txt
@@ -25,5 +25,4 @@ qt_internal_add_module(QuickDialogs2Utils
PUBLIC_LIBRARIES
Qt::Core
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
diff --git a/src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h b/src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h
index 20722f01fa..dd3ff80d36 100644
--- a/src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h
+++ b/src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h
@@ -24,7 +24,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKDIALOGS2UTILS_PRIVATE_EXPORT QQuickFileNameFilter : public QObject
+class Q_QUICKDIALOGS2UTILS_EXPORT QQuickFileNameFilter : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged FINAL)
diff --git a/src/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h b/src/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h
index 6a59111d14..02a4edfdb1 100644
--- a/src/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h
+++ b/src/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h
@@ -16,6 +16,6 @@
//
#include <QtCore/qglobal.h>
-#include <QtQuickDialogs2Utils/private/qtquickdialogs2utilsexports_p.h>
+#include <QtQuickDialogs2Utils/qtquickdialogs2utilsexports.h>
#endif // QTQUICKDIALOGS2UTILSUTILSGLOBAL_P_H
diff --git a/src/quicklayouts/CMakeLists.txt b/src/quicklayouts/CMakeLists.txt
index d9b7c4b102..4eeb697461 100644
--- a/src/quicklayouts/CMakeLists.txt
+++ b/src/quicklayouts/CMakeLists.txt
@@ -15,6 +15,7 @@ qt_internal_add_qml_module(QuickLayouts
qquicklayoutstyleinfo.cpp qquicklayoutstyleinfo_p.h
qquicklinearlayout.cpp qquicklinearlayout_p.h
qquickstacklayout.cpp qquickstacklayout_p.h
+ qquicklayoutitemproxy.cpp qquicklayoutitemproxy_p.h
qquicklayoutglobal_p.h
DEFINES
QT_BUILD_QUICKLAYOUTS_LIB
@@ -24,5 +25,4 @@ qt_internal_add_qml_module(QuickLayouts
Qt::QuickPrivate
Qt::Qml
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
diff --git a/src/quicklayouts/qquickgridlayoutengine_p.h b/src/quicklayouts/qquickgridlayoutengine_p.h
index a59679761d..047d214655 100644
--- a/src/quicklayouts/qquickgridlayoutengine_p.h
+++ b/src/quicklayouts/qquickgridlayoutengine_p.h
@@ -86,6 +86,8 @@ public:
}
}
+ inline virtual QString toString() const override { return QDebug::toString(m_item); }
+
QQuickItem *layoutItem() const { return m_item; }
QQuickItem *m_item;
diff --git a/src/quicklayouts/qquicklayout.cpp b/src/quicklayouts/qquicklayout.cpp
index 3699b3fbcf..f38bdfd396 100644
--- a/src/quicklayouts/qquicklayout.cpp
+++ b/src/quicklayouts/qquicklayout.cpp
@@ -85,10 +85,12 @@ QQuickLayoutAttached::QQuickLayoutAttached(QObject *parent)
m_isMaximumWidthSet(false),
m_isMaximumHeightSet(false),
m_changesNotificationEnabled(true),
+ m_isMarginsSet(false),
m_isLeftMarginSet(false),
m_isTopMarginSet(false),
m_isRightMarginSet(false),
m_isBottomMarginSet(false),
+ m_isAlignmentSet(false),
m_horizontalStretch(-1),
m_verticalStretch(-1)
{
@@ -301,15 +303,16 @@ void QQuickLayoutAttached::setMaximumImplicitSize(const QSizeF &sz)
If this property is \c true, the item will be as wide as possible while respecting
the given constraints. If the property is \c false, the item will have a fixed width
set to the preferred width.
- The default is \c false, except for layouts themselves, which default to \c true.
+ The default depends on implicit (built-in) size policy of item.
\sa fillHeight
*/
void QQuickLayoutAttached::setFillWidth(bool fill)
{
+ bool oldFillWidth = fillWidth();
m_isFillWidthSet = true;
- if (m_fillWidth != fill) {
- m_fillWidth = fill;
+ m_fillWidth = fill;
+ if (oldFillWidth != fill) {
invalidateItem();
emit fillWidthChanged();
}
@@ -321,15 +324,16 @@ void QQuickLayoutAttached::setFillWidth(bool fill)
If this property is \c true, the item will be as tall as possible while respecting
the given constraints. If the property is \c false, the item will have a fixed height
set to the preferred height.
- The default is \c false, except for layouts themselves, which default to \c true.
+ The default depends on implicit (built-in) size policy of the item.
\sa fillWidth
*/
void QQuickLayoutAttached::setFillHeight(bool fill)
{
+ bool oldFillHeight = fillHeight();
m_isFillHeightSet = true;
- if (m_fillHeight != fill) {
- m_fillHeight = fill;
+ m_fillHeight = fill;
+ if (oldFillHeight != fill) {
invalidateItem();
emit fillHeightChanged();
}
@@ -403,6 +407,7 @@ void QQuickLayoutAttached::setColumn(int column)
*/
void QQuickLayoutAttached::setAlignment(Qt::Alignment align)
{
+ m_isAlignmentSet = true;
if (align != m_alignment) {
m_alignment = align;
if (QQuickLayout *layout = parentLayout()) {
@@ -434,6 +439,8 @@ void QQuickLayoutAttached::setAlignment(Qt::Alignment align)
\note This requires that Layout::fillWidth is set to true
+ \since Qt 6.5
+
\sa verticalStretchFactor
*/
void QQuickLayoutAttached::setHorizontalStretchFactor(int factor)
@@ -469,6 +476,8 @@ void QQuickLayoutAttached::setHorizontalStretchFactor(int factor)
\note This requires that Layout::fillHeight is set to true
+ \since Qt 6.5
+
\sa horizontalStretchFactor
*/
void QQuickLayoutAttached::setVerticalStretchFactor(int factor)
@@ -514,6 +523,7 @@ void QQuickLayoutAttached::setVerticalStretchFactor(int factor)
*/
void QQuickLayoutAttached::setMargins(qreal m)
{
+ m_isMarginsSet = true;
if (m == m_defaultMargins)
return;
@@ -825,19 +835,16 @@ void QQuickLayout::invalidate(QQuickItem * /*childItem*/)
d->m_dirtyArrangement = true;
if (!qobject_cast<QQuickLayout *>(parentItem())) {
-
- if (m_inUpdatePolish)
- ++m_polishInsideUpdatePolish;
- else
- m_polishInsideUpdatePolish = 0;
-
- if (m_polishInsideUpdatePolish <= 2) {
- // allow at most two consecutive loops in order to respond to height-for-width
- // (e.g QQuickText changes implicitHeight when its width gets changed)
- qCDebug(lcQuickLayouts) << "QQuickLayout::invalidate(), polish()";
- polish();
+ polish();
+
+ if (m_inUpdatePolish) {
+ if (++m_polishInsideUpdatePolish > 2)
+ // allow at most two consecutive loops in order to respond to height-for-width
+ // (e.g QQuickText changes implicitHeight when its width gets changed)
+ qCDebug(lcQuickLayouts) << "Layout polish loop detected for " << this
+ << ". The polish request will still be scheduled.";
} else {
- qmlWarning(this) << "Qt Quick Layouts: Polish loop detected. Aborting after two iterations.";
+ m_polishInsideUpdatePolish = 0;
}
}
}
@@ -916,7 +923,7 @@ void QQuickLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGe
{
Q_D(QQuickLayout);
QQuickItem::geometryChange(newGeometry, oldGeometry);
- if (d->m_disableRearrange || !isReady() || !newGeometry.isValid())
+ if (invalidated() || d->m_disableRearrange || !isReady())
return;
qCDebug(lcQuickLayouts) << "QQuickLayout::geometryChange" << newGeometry << oldGeometry;
@@ -1246,7 +1253,15 @@ void QQuickLayout::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSiz
*/
QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info)
{
- bool fillExtent = false;
+ bool fillExtent([&]{
+ QLayoutPolicy::Policy policy{QLayoutPolicy::Fixed};
+ if (item && QGuiApplication::testAttribute(Qt::AA_QtQuickUseDefaultSizePolicy)) {
+ QLayoutPolicy sizePolicy = QQuickItemPrivate::get(item)->sizePolicy();
+ policy = (orientation == Qt::Horizontal) ? sizePolicy.horizontalPolicy() : sizePolicy.verticalPolicy();
+ }
+ return (policy == QLayoutPolicy::Preferred);
+ }());
+
bool isSet = false;
if (info) {
if (orientation == Qt::Horizontal) {
@@ -1259,8 +1274,8 @@ QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item,
}
if (!isSet && qobject_cast<QQuickLayout*>(item))
fillExtent = true;
- return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed;
+ return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed;
}
void QQuickLayout::_q_dumpLayoutTree() const
diff --git a/src/quicklayouts/qquicklayout_p.h b/src/quicklayouts/qquicklayout_p.h
index df9c4cad97..f0ccc6dbf5 100644
--- a/src/quicklayouts/qquicklayout_p.h
+++ b/src/quicklayouts/qquicklayout_p.h
@@ -23,6 +23,7 @@
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtGui/private/qlayoutpolicy_p.h>
+#include <QtGui/qguiapplication.h>
QT_BEGIN_NAMESPACE
@@ -30,7 +31,7 @@ class QQuickLayoutAttached;
Q_DECLARE_LOGGING_CATEGORY(lcQuickLayouts)
class QQuickLayoutPrivate;
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickLayout : public QQuickItem, public QQuickItemChangeListener
+class Q_QUICKLAYOUTS_EXPORT QQuickLayout : public QQuickItem, public QQuickItemChangeListener
{
Q_OBJECT
@@ -156,60 +157,74 @@ protected:
};
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickLayoutAttached : public QObject
+class Q_QUICKLAYOUTS_EXPORT QQuickLayoutAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged)
- Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged)
- Q_PROPERTY(qreal preferredWidth READ preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged)
- Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged)
- Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged)
- Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged)
- Q_PROPERTY(bool fillHeight READ fillHeight WRITE setFillHeight NOTIFY fillHeightChanged)
- Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged)
- Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged)
- Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged)
- Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan NOTIFY rowSpanChanged)
- Q_PROPERTY(int columnSpan READ columnSpan WRITE setColumnSpan NOTIFY columnSpanChanged)
- Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
- Q_PROPERTY(int horizontalStretchFactor READ horizontalStretchFactor WRITE setHorizontalStretchFactor NOTIFY horizontalStretchFactorChanged)
- Q_PROPERTY(int verticalStretchFactor READ verticalStretchFactor WRITE setVerticalStretchFactor NOTIFY verticalStretchFactorChanged)
-
- Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged)
- Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin RESET resetLeftMargin NOTIFY leftMarginChanged)
- Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin RESET resetTopMargin NOTIFY topMarginChanged)
- Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin RESET resetRightMargin NOTIFY rightMarginChanged)
- Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin RESET resetBottomMargin NOTIFY bottomMarginChanged)
+ Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged FINAL)
+ Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged FINAL)
+ Q_PROPERTY(qreal preferredWidth READ preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged FINAL)
+ Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged FINAL)
+ Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged FINAL)
+ Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged FINAL)
+ Q_PROPERTY(bool fillHeight READ fillHeight WRITE setFillHeight NOTIFY fillHeightChanged FINAL)
+ Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged FINAL)
+ Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged FINAL)
+ Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged FINAL)
+ Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan NOTIFY rowSpanChanged FINAL)
+ Q_PROPERTY(int columnSpan READ columnSpan WRITE setColumnSpan NOTIFY columnSpanChanged FINAL)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged FINAL)
+ Q_PROPERTY(int horizontalStretchFactor READ horizontalStretchFactor WRITE setHorizontalStretchFactor NOTIFY horizontalStretchFactorChanged FINAL)
+ Q_PROPERTY(int verticalStretchFactor READ verticalStretchFactor WRITE setVerticalStretchFactor NOTIFY verticalStretchFactorChanged FINAL)
+
+ Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged FINAL)
+ Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin RESET resetLeftMargin NOTIFY leftMarginChanged FINAL)
+ Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin RESET resetTopMargin NOTIFY topMarginChanged FINAL)
+ Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin RESET resetRightMargin NOTIFY rightMarginChanged FINAL)
+ Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin RESET resetBottomMargin NOTIFY bottomMarginChanged FINAL)
public:
QQuickLayoutAttached(QObject *object);
qreal minimumWidth() const { return !m_isMinimumWidthSet ? sizeHint(Qt::MinimumSize, Qt::Horizontal) : m_minimumWidth; }
void setMinimumWidth(qreal width);
+ bool isMinimumWidthSet() const {return m_isMinimumWidthSet; }
qreal minimumHeight() const { return !m_isMinimumHeightSet ? sizeHint(Qt::MinimumSize, Qt::Vertical) : m_minimumHeight; }
void setMinimumHeight(qreal height);
+ bool isMinimumHeightSet() const {return m_isMinimumHeightSet; }
qreal preferredWidth() const { return m_preferredWidth; }
void setPreferredWidth(qreal width);
+ bool isPreferredWidthSet() const {return m_preferredWidth > -1; }
qreal preferredHeight() const { return m_preferredHeight; }
void setPreferredHeight(qreal width);
+ bool isPreferredHeightSet() const {return m_preferredHeight > -1; }
qreal maximumWidth() const { return !m_isMaximumWidthSet ? sizeHint(Qt::MaximumSize, Qt::Horizontal) : m_maximumWidth; }
void setMaximumWidth(qreal width);
+ bool isMaximumWidthSet() const {return m_isMaximumWidthSet; }
qreal maximumHeight() const { return !m_isMaximumHeightSet ? sizeHint(Qt::MaximumSize, Qt::Vertical) : m_maximumHeight; }
void setMaximumHeight(qreal height);
+ bool isMaximumHeightSet() const {return m_isMaximumHeightSet; }
void setMinimumImplicitSize(const QSizeF &sz);
void setMaximumImplicitSize(const QSizeF &sz);
- bool fillWidth() const { return m_fillWidth; }
+ bool fillWidth() const {
+ if (auto *itemPriv = itemForSizePolicy(m_isFillWidthSet))
+ return (itemPriv->sizePolicy().horizontalPolicy() == QLayoutPolicy::Preferred);
+ return m_fillWidth;
+ }
void setFillWidth(bool fill);
bool isFillWidthSet() const { return m_isFillWidthSet; }
- bool fillHeight() const { return m_fillHeight; }
+ bool fillHeight() const {
+ if (auto *itemPriv = itemForSizePolicy(m_isFillHeightSet))
+ return (itemPriv->sizePolicy().verticalPolicy() == QLayoutPolicy::Preferred);
+ return m_fillHeight;
+ }
void setFillHeight(bool fill);
bool isFillHeightSet() const { return m_isFillHeightSet; }
@@ -227,30 +242,38 @@ public:
Qt::Alignment alignment() const { return m_alignment; }
void setAlignment(Qt::Alignment align);
+ bool isAlignmentSet() const {return m_isAlignmentSet; }
int horizontalStretchFactor() const { return m_horizontalStretch; }
void setHorizontalStretchFactor(int stretchFactor);
+ bool isHorizontalStretchFactorSet() const { return m_horizontalStretch > -1; }
int verticalStretchFactor() const { return m_verticalStretch; }
void setVerticalStretchFactor(int stretchFactor);
+ bool isVerticalStretchFactorSet() const { return m_verticalStretch > -1; }
qreal margins() const { return m_defaultMargins; }
void setMargins(qreal m);
+ bool isMarginsSet() const { return m_isMarginsSet; }
qreal leftMargin() const { return m_isLeftMarginSet ? m_margins.left() : m_defaultMargins; }
void setLeftMargin(qreal m);
void resetLeftMargin();
+ bool isLeftMarginSet() const { return m_isLeftMarginSet; }
qreal topMargin() const { return m_isTopMarginSet ? m_margins.top() : m_defaultMargins; }
void setTopMargin(qreal m);
void resetTopMargin();
+ bool isTopMarginSet() const {return m_isTopMarginSet; }
qreal rightMargin() const { return m_isRightMarginSet ? m_margins.right() : m_defaultMargins; }
void setRightMargin(qreal m);
void resetRightMargin();
+ bool isRightMarginSet() const { return m_isRightMarginSet; }
qreal bottomMargin() const { return m_isBottomMarginSet ? m_margins.bottom() : m_defaultMargins; }
void setBottomMargin(qreal m);
void resetBottomMargin();
+ bool isBottomMarginSet() const { return m_isBottomMarginSet; }
QMarginsF qMargins() const {
return QMarginsF(leftMargin(), topMargin(), rightMargin(), bottomMargin());
@@ -289,6 +312,15 @@ public:
return false;
}
+ QQuickItemPrivate *itemForSizePolicy(bool isFillSet) const
+ {
+ QQuickItemPrivate *itemPriv = nullptr;
+ if (!isFillSet && qobject_cast<QQuickItem *>(item()) &&
+ QGuiApplication::testAttribute(Qt::AA_QtQuickUseDefaultSizePolicy))
+ itemPriv = QQuickItemPrivate::get(item());
+ return itemPriv;
+ }
+
Q_SIGNALS:
void minimumWidthChanged();
void minimumHeightChanged();
@@ -346,10 +378,12 @@ private:
unsigned m_isMaximumWidthSet : 1;
unsigned m_isMaximumHeightSet : 1;
unsigned m_changesNotificationEnabled : 1;
+ unsigned m_isMarginsSet : 1;
unsigned m_isLeftMarginSet : 1;
unsigned m_isTopMarginSet : 1;
unsigned m_isRightMarginSet : 1;
unsigned m_isBottomMarginSet : 1;
+ unsigned m_isAlignmentSet : 1;
Qt::Alignment m_alignment;
int m_horizontalStretch;
int m_verticalStretch;
@@ -363,6 +397,4 @@ inline QQuickLayoutAttached *attachedLayoutObject(QQuickItem *item, bool create
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLayout)
-
#endif // QQUICKLAYOUT_P_H
diff --git a/src/quicklayouts/qquicklayoutglobal_p.h b/src/quicklayouts/qquicklayoutglobal_p.h
index a48bbbe95f..8537aac835 100644
--- a/src/quicklayouts/qquicklayoutglobal_p.h
+++ b/src/quicklayouts/qquicklayoutglobal_p.h
@@ -16,6 +16,6 @@
//
#include <QtCore/qglobal.h>
-#include <QtQuickLayouts/private/qtquicklayoutsexports_p.h>
+#include <QtQuickLayouts/qtquicklayoutsexports.h>
#endif // QQUICKLAYOUTGLOBAL_P_H
diff --git a/src/quicklayouts/qquicklayoutitemproxy.cpp b/src/quicklayouts/qquicklayoutitemproxy.cpp
new file mode 100644
index 0000000000..f690a2eae8
--- /dev/null
+++ b/src/quicklayouts/qquicklayoutitemproxy.cpp
@@ -0,0 +1,532 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qquicklayoutitemproxy_p.h"
+#include "qquicklayout_p.h"
+
+/*!
+ \qmltype LayoutItemProxy
+ \instantiates QQuickLayoutItemProxy
+ \inherits Item
+ \inqmlmodule QtQuick.Layouts
+ \ingroup layouts
+ \since QtQuick.Layouts 6.6
+ \brief The LayoutItemProxy class provides a placeholder for \l{QQuickItem}s
+ in layouts.
+
+ Some responsive layouts require different layout hierarchies for different
+ screen sizes, but the layout hierarchy is the same as the QML structure and
+ can therefore not be changed at runtime. LayoutItemProxy overcomes this
+ limitation by representing a \l{target} item within the layout. The
+ \l{target} item itself can be defined anywhere in the QML hierarchy. This
+ allows declaration of multiple layouts with the same content items. The
+ layouts can be shown and hidden to switch between them.
+
+ The LayoutItemProxy will try to take control of the \l{target} item if it
+ is \l [QML] {Item::}{visible}. Taking control will position and resize the
+ \l{target} item to match the position and size of the LayoutItemProxy.
+ Further, the LayoutItemProxy will set itself as the parent of the
+ \l{target} (to ensure event delivery and useful drawing order) and set the
+ visibility to \c true. Multiple LayoutItemProxies can \l{target} the same
+ item, but only one LayoutItemProxy can control an item at a time. Therefore
+ only one of the proxies targeting the same item should be visible at a
+ time. If multiple proxies target the same item but \e visible is set to
+ false for each proxy, the item will also be invisible.
+
+ All \l{Layout} attached properties of the \l {target}, as well as the
+ \l{QQuickItem::implicitWidth} and \l{QQuickItem::implicitHeight} of the
+ \l{target} are forwarded by the LayoutItemProxy. The LayoutItemProxy will
+ mimic the \l{target} as closely as possible in terms of \l{Layout}
+ properties and size. \l{Layout} attached properties can also be set
+ explicitly on the LayoutItemProxy which will stop the forwarding of the
+ \l {target} properties.
+
+ \section1 Example Usage
+
+ This is a minimalistic example, changing between two layouts using proxies
+ to use the same items in both layouts. The items that populate the layouts
+ can be defined at an arbitrary point in the QML structure.
+
+ \snippet layouts/simpleProxy.qml item definition
+
+ Then we can define the Layouts with LayoutItemProxys
+
+ \snippet layouts/simpleProxy.qml layout definition
+
+ We can switch now between the layouts, depending on a criterion of our
+ choice by toggling the visibility of the layouts on and off.
+
+ \snippet layouts/simpleProxy.qml layout choice
+
+ The two resulting layouts look like this:
+
+ \div {class="float-right"}
+ \inlineimage simpleProxy.png
+ \enddiv
+
+ The LayoutItemProxy can also be used without layouts, e.g. by anchoring it
+ to different items. A mix of real \l {Item}{Items} and proxy items is
+ equally possible, as well as nested structures of layouts and items.
+
+ \warning The LayoutItemProxy will set the parent of its target to itself.
+ Keep this in mind when referring to the parent of the target item.
+
+ \sa Item, GridLayout, RowLayout, ColumnLayout
+*/
+
+Q_LOGGING_CATEGORY(lcLayouts, "qt.quick.layouts")
+
+
+QQuickLayoutItemProxy::QQuickLayoutItemProxy(QQuickItem *parent)
+ : QQuickItem(*new QQuickLayoutItemProxyPrivate, parent)
+{
+
+}
+
+QQuickLayoutItemProxy::~QQuickLayoutItemProxy()
+{
+ Q_D(QQuickLayoutItemProxy);
+
+ if (!d->target)
+ return;
+
+ QQuickLayoutItemProxyAttachedData * attachedData = d->target->property("QQuickLayoutItemProxyAttachedData").value<QQuickLayoutItemProxyAttachedData*>();
+ // De-register this proxy from the proxies controlling the target
+ if (attachedData) {
+ if (attachedData->getControllingProxy() == this) {
+ attachedData->releaseControl(this);
+ d->target->setParentItem(nullptr);
+ }
+ attachedData->releaseProxy(this);
+ }
+ // The target item still has a QObject parent that takes care of its destrctuion.
+ // No need to invoke destruction of the target tiem from here.
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxy::geometryChange Reimplementation of
+ QQuickItem::geometryChange to update the target geometry too.
+*/
+void QQuickLayoutItemProxy::geometryChange(const QRectF &newGeom, const QRectF &oldGeom)
+{
+ QQuickItem::geometryChange(newGeom, oldGeom);
+ if (!isVisible())
+ return;
+
+ const QSizeF sz = newGeom.size();
+ QPointF pos(0., 0.);
+
+ if (QQuickItem *t = effectiveTarget()) {
+ if (QQuickLayoutItemProxyAttachedData * attachedData = target()->property("QQuickLayoutItemProxyAttachedData").value<QQuickLayoutItemProxyAttachedData*>()) {
+ if (attachedData->getControllingProxy() != this)
+ return;
+ }
+
+ // Should normally not be the case, except the user resets the parent
+ // This is a failsave for this case and positions the item correctly
+ if (t->parentItem() != this)
+ pos = t->parentItem()->mapFromGlobal(mapToGlobal(0, 0));
+
+ if (t->size() == sz && t->position() == pos && newGeom == oldGeom)
+ return;
+
+ t->setSize(sz);
+ t->setPosition(pos);
+ }
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxy::itemChange is a reimplementation of
+ QQuickItem::itemChange to react to changes in visibility.
+*/
+void QQuickLayoutItemProxy::itemChange(ItemChange c, const ItemChangeData &d)
+{
+ if (c == QQuickItem::ItemVisibleHasChanged)
+ {
+ maybeTakeControl();
+ }
+ QQuickItem::itemChange(c, d);
+}
+
+// Implementation of the slots to react to changes of the Layout attached properties.
+// If the target Layout propertie change, we change the proxy Layout properties accordingly
+// If the proxy Layout properties have been changed externally, we want to remove this binding.
+// The member variables m_expectProxy##Property##Change help us keep track about who invokes
+// the change of the parameter. If it is invoked by the target we expect a proxy property
+// change and will not remove the connection.
+#define propertyForwarding(property, Property) \
+ void QQuickLayoutItemProxy::target##Property##Changed() { \
+ Q_D(QQuickLayoutItemProxy); \
+ QQuickLayoutAttached *attTarget = attachedLayoutObject(target(), false); \
+ QQuickLayoutAttached *attProxy = attachedLayoutObject(this, false); \
+ if (!attTarget) return; \
+ if (attProxy->property() == attTarget->property()) \
+ return; \
+ d->m_expectProxy##Property##Change = true; \
+ attProxy->set##Property(attTarget->property()); \
+ } \
+ void QQuickLayoutItemProxy::proxy##Property##Changed() { \
+ Q_D(QQuickLayoutItemProxy); \
+ if (d->m_expectProxy##Property##Change) { \
+ d->m_expectProxy##Property##Change = false; \
+ return; \
+ } \
+ QQuickLayoutAttached *attTarget = attachedLayoutObject(target(), false); \
+ if (!attTarget) return; \
+ disconnect(attTarget, &QQuickLayoutAttached::property##Changed, this, &QQuickLayoutItemProxy::target##Property##Changed); \
+ }
+
+propertyForwarding(minimumWidth, MinimumWidth)
+propertyForwarding(minimumHeight, MinimumHeight)
+propertyForwarding(preferredWidth, PreferredWidth)
+propertyForwarding(preferredHeight, PreferredHeight)
+propertyForwarding(maximumWidth, MaximumWidth)
+propertyForwarding(maximumHeight, MaximumHeight)
+propertyForwarding(fillWidth, FillWidth)
+propertyForwarding(fillHeight, FillHeight)
+propertyForwarding(alignment, Alignment)
+propertyForwarding(horizontalStretchFactor, HorizontalStretchFactor)
+propertyForwarding(verticalStretchFactor, VerticalStretchFactor)
+propertyForwarding(margins, Margins)
+propertyForwarding(leftMargin, LeftMargin)
+propertyForwarding(topMargin, TopMargin)
+propertyForwarding(rightMargin, RightMargin)
+propertyForwarding(bottomMargin, BottomMargin)
+
+#undef propertyForwarding
+
+/*!
+ \qmlproperty Item LayoutItemProxy::target
+
+ This property holds the \l Item that the proxy should represent in a
+ \l {Layout} hierarchy.
+*/
+
+/*! \internal
+ \brief QQuickLayoutItemProxy::target
+ \return The target item of the proxy
+*/
+QQuickItem *QQuickLayoutItemProxy::target() const
+{
+ Q_D(const QQuickLayoutItemProxy);
+ return d->target;
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxy::setTarget sets the target
+ \param newTarget The item that the proxy stands in place for.
+
+ All layout properties of the target are connected to the layout properties
+ of the LayoutItemProxy. It the LayoutItemProxy is visible, it will try to
+ take control of the target.
+*/
+void QQuickLayoutItemProxy::setTarget(QQuickItem *newTarget)
+{
+ Q_D(QQuickLayoutItemProxy);
+
+ if (newTarget == d->target)
+ return;
+
+ d->target = newTarget;
+
+ if (newTarget) {
+
+ QQuickLayoutItemProxyAttachedData *attachedData;
+ if (newTarget->property("QQuickLayoutItemProxyAttachedData").isValid()) {
+ attachedData = newTarget->property("QQuickLayoutItemProxyAttachedData").value<QQuickLayoutItemProxyAttachedData*>();
+ } else {
+ attachedData = new QQuickLayoutItemProxyAttachedData(newTarget);
+ QVariant v;
+ v.setValue(attachedData);
+ newTarget->setProperty("QQuickLayoutItemProxyAttachedData", v);
+ }
+ attachedData->registerProxy(this);
+
+ // If there is no other controlling proxy, we will hide the target
+ if (!attachedData->proxyHasControl())
+ newTarget->setVisible(false);
+ // We are calling maybeTakeControl at the end to eventually take
+ // responsibility of showing the target.
+
+ if (QQuickLayoutAttached *attTarget = attachedLayoutObject(newTarget)) {
+ QQuickLayoutAttached *attProxy = attachedLayoutObject(this, true);
+
+ disconnect(attTarget, nullptr, attProxy, nullptr);
+
+ // bind item-specific layout properties:
+
+#define connectPropertyForwarding(property, Property) \
+ if (!attProxy->is##Property##Set()) { \
+ connect(attTarget, &QQuickLayoutAttached::property##Changed, this, &QQuickLayoutItemProxy::target##Property##Changed); \
+ connect(attProxy, &QQuickLayoutAttached::property##Changed, this, &QQuickLayoutItemProxy::proxy##Property##Changed); \
+ target##Property##Changed(); \
+ }
+ connectPropertyForwarding(minimumWidth, MinimumWidth)
+ connectPropertyForwarding(minimumHeight, MinimumHeight)
+ connectPropertyForwarding(preferredWidth, PreferredWidth)
+ connectPropertyForwarding(preferredHeight, PreferredHeight)
+ connectPropertyForwarding(maximumWidth, MaximumWidth)
+ connectPropertyForwarding(maximumHeight, MaximumHeight)
+ connectPropertyForwarding(fillWidth, FillWidth)
+ connectPropertyForwarding(fillHeight, FillHeight)
+ connectPropertyForwarding(alignment, Alignment)
+ connectPropertyForwarding(horizontalStretchFactor, HorizontalStretchFactor)
+ connectPropertyForwarding(verticalStretchFactor, VerticalStretchFactor)
+ connectPropertyForwarding(margins, Margins)
+ connectPropertyForwarding(leftMargin, LeftMargin)
+ connectPropertyForwarding(topMargin, TopMargin)
+ connectPropertyForwarding(rightMargin, RightMargin)
+ connectPropertyForwarding(bottomMargin, BottomMargin)
+#undef connectPropertyForwarding
+
+ // proxy.implicitWidth: target.implicitWidth
+ auto fnBindImplW = [newTarget, this](){ this->setImplicitWidth(newTarget->implicitWidth()); };
+ fnBindImplW();
+ connect(newTarget, &QQuickItem::implicitWidthChanged, fnBindImplW);
+
+ // proxy.implicitHeight: target.implicitHeight
+ auto fnBindImplH = [newTarget, this](){ this->setImplicitHeight(newTarget->implicitHeight()); };
+ fnBindImplH();
+ connect(newTarget, &QQuickItem::implicitHeightChanged, fnBindImplH);
+ }
+ }
+
+ if (isVisible())
+ maybeTakeControl();
+
+ emit targetChanged();
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxy::effectiveTarget
+ \return The target item of the proxy if it is in control, \c null otherwise.
+*/
+QQuickItem *QQuickLayoutItemProxy::effectiveTarget() const
+{
+ if (target() == nullptr)
+ return nullptr;
+
+ QQuickLayoutItemProxyAttachedData * attachedData = target()->property("QQuickLayoutItemProxyAttachedData").value<QQuickLayoutItemProxyAttachedData*>();
+ return (attachedData->getControllingProxy() == this) ? target() : nullptr;
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxy::clearTarget sets the target to null.
+
+ This function is called if the target is destroyed to make sure we do not
+ try to access a non-existing object.
+*/
+void QQuickLayoutItemProxy::clearTarget()
+{
+ setTarget(nullptr);
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxy::maybeTakeControl checks and takes over control
+ of the item.
+
+ If the proxy is visible it will try to take control over the target and set
+ its visibility to true. If the proxy is hidden it will also hide the target
+ and another LayoutItemProxy has to set the visibility to \c true or the
+ target will stay invisible.
+*/
+void QQuickLayoutItemProxy::maybeTakeControl()
+{
+ Q_D(QQuickLayoutItemProxy);
+ if (!d->target)
+ return;
+
+ QQuickLayoutItemProxyAttachedData * attachedData = d->target->property("QQuickLayoutItemProxyAttachedData").value<QQuickLayoutItemProxyAttachedData*>();
+ if (isVisible() && attachedData->getControllingProxy() != this) {
+ if (attachedData->takeControl(this)) {
+ d->target->setVisible(true);
+ d->target->setParentItem(this);
+ updatePos();
+ }
+ }
+ if (!isVisible() && attachedData->getControllingProxy() == this){
+ if (d->target->parentItem() == this) {
+ d->target->setParentItem(nullptr);
+ } else
+ qCDebug(lcLayouts) << "Parent was changed to" << d->target->parentItem() << "while an ItemProxy had control";
+ d->target->setVisible(false);
+ attachedData->releaseControl(this);
+ }
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxy::updatePos sets the geometry of the target to
+ the geometry of the proxy
+*/
+void QQuickLayoutItemProxy::updatePos()
+{
+ if (!isVisible())
+ return;
+ if (target()) {
+ if (QQuickLayoutItemProxyAttachedData * attachedData = target()->property("QQuickLayoutItemProxyAttachedData").value<QQuickLayoutItemProxyAttachedData*>()) {
+ if (attachedData->getControllingProxy() == this)
+ geometryChange(boundingRect(), boundingRect());
+ }
+ }
+}
+
+QQuickLayoutItemProxyPrivate::QQuickLayoutItemProxyPrivate()
+ : QQuickItemPrivate(),
+ m_expectProxyMinimumWidthChange(false),
+ m_expectProxyMinimumHeightChange(false),
+ m_expectProxyPreferredWidthChange(false),
+ m_expectProxyPreferredHeightChange(false),
+ m_expectProxyMaximumWidthChange(false),
+ m_expectProxyMaximumHeightChange(false),
+ m_expectProxyFillWidthChange(false),
+ m_expectProxyFillHeightChange(false),
+ m_expectProxyAlignmentChange(false),
+ m_expectProxyHorizontalStretchFactorChange(false),
+ m_expectProxyVerticalStretchFactorChange(false),
+ m_expectProxyMarginsChange(false),
+ m_expectProxyLeftMarginChange(false),
+ m_expectProxyTopMarginChange(false),
+ m_expectProxyRightMarginChange(false),
+ m_expectProxyBottomMarginChange(false)
+{
+
+}
+
+/*! \internal
+ \class QQuickLayoutItemProxyAttachedData
+ \brief Provides attached properties for items that are managed by one or
+ more LayoutItemProxy.
+
+ It stores all proxies that target the item, and will emit signals when the
+ proxies or the controlling proxy changes. Proxies can listen to the signal
+ and pick up control if they wish to.
+*/
+QQuickLayoutItemProxyAttachedData::QQuickLayoutItemProxyAttachedData(QObject *parent)
+ : QObject(parent), controllingProxy(nullptr)
+{
+
+}
+
+QQuickLayoutItemProxyAttachedData::~QQuickLayoutItemProxyAttachedData()
+{
+ // If this is destroyed, so is the target. Clear the target from the
+ // proxies so they do not try to access a destroyed object
+ for (auto &proxy: std::as_const(proxies))
+ proxy->clearTarget();
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxyAttachedData::registerProxy registers a proxy
+ that manages the item this data is attached to.
+
+ This is required to easily notify proxies when the target is destroyed or
+ when it is free to take over control.
+*/
+void QQuickLayoutItemProxyAttachedData::registerProxy(QQuickLayoutItemProxy *proxy)
+{
+ if (proxies.contains(proxy))
+ return;
+
+ proxies.append(proxy);
+ emit proxiesChanged();
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxyAttachedData::releaseProxy removes a proxy from
+ a list of known proxies that manage the item this data is attached to.
+*/
+void QQuickLayoutItemProxyAttachedData::releaseProxy(QQuickLayoutItemProxy *proxy)
+{
+ if (proxy == controllingProxy)
+ releaseControl(proxy);
+
+ proxies.removeAll(proxy);
+
+ if (proxies.isEmpty())
+ deleteLater();
+
+ emit proxiesChanged();
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxyAttachedData::takeControl is called by
+ LayoutItemProxies when they try to take control over the item this data is
+ attached to.
+ \return \c true if no other proxy controls the item and if control is
+ granted to the proxy, \c false otherwise.
+
+ \param proxy The proxy that tries to take control.
+*/
+bool QQuickLayoutItemProxyAttachedData::takeControl(QQuickLayoutItemProxy *proxy)
+{
+ if (controllingProxy || !proxies.contains(proxy))
+ return false;
+
+ qCDebug(lcLayouts) << proxy
+ << "takes control of"
+ << parent();
+
+ controllingProxy = proxy;
+ emit controlTaken();
+ emit controllingProxyChanged();
+ return true;
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxyAttachedData::releaseControl is called by
+ LayoutItemProxies when they try no longer control the item
+
+ \param proxy The proxy that gives up control.
+*/
+void QQuickLayoutItemProxyAttachedData::releaseControl(QQuickLayoutItemProxy *proxy)
+{
+ if (controllingProxy != proxy)
+ return;
+
+ qCDebug(lcLayouts) << proxy
+ << "no longer controls"
+ << parent();
+
+ controllingProxy = nullptr;
+ emit controlReleased();
+ emit controllingProxyChanged();
+
+ for (auto &otherProxy: std::as_const(proxies)) {
+ if (proxy != otherProxy)
+ otherProxy->maybeTakeControl();
+ }
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxyAttachedData::getControllingProxy
+ \return the proxy that currently controls the item this data is attached to.
+ Returns \c null if no proxy controls the item.
+*/
+QQuickLayoutItemProxy *QQuickLayoutItemProxyAttachedData::getControllingProxy() const
+{
+ return controllingProxy;
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxyAttachedData::getProxies
+ \return a list of all proxies that target the item this data is attached to.
+*/
+QQmlListProperty<QQuickLayoutItemProxy> QQuickLayoutItemProxyAttachedData::getProxies()
+{
+ using Type = QQuickLayoutItemProxy;
+ using Property = QQmlListProperty<Type>;
+
+ return Property(
+ this, &proxies,
+ [](Property *p) { return static_cast<QList<Type *> *>(p->data)->size(); },
+ [](Property *p, qsizetype i) { return static_cast<QList<Type *> *>(p->data)->at(i); }
+ );
+}
+
+/*! \internal
+ \brief QQuickLayoutItemProxyAttachedData::proxyHasControl
+ \return \c true if a proxy is controlling the item, \c false otherwise.
+*/
+bool QQuickLayoutItemProxyAttachedData::proxyHasControl() const
+{
+ return controllingProxy != nullptr;
+}
diff --git a/src/quicklayouts/qquicklayoutitemproxy_p.h b/src/quicklayouts/qquicklayoutitemproxy_p.h
new file mode 100644
index 0000000000..7d900bd2d6
--- /dev/null
+++ b/src/quicklayouts/qquicklayoutitemproxy_p.h
@@ -0,0 +1,147 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKLAYOUTITEMPROXY_P_H
+#define QQUICKLAYOUTITEMPROXY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qquickitem_p.h>
+#include <private/qquickrectangle_p.h>
+
+class QQuickLayoutItemProxyAttachedData;
+class QQuickLayoutItemProxyPrivate;
+class QQuickLayoutItemProxy : public QQuickItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)
+ QML_NAMED_ELEMENT(LayoutItemProxy)
+ QML_ADDED_IN_VERSION(6, 6)
+
+public:
+ QQuickLayoutItemProxy(QQuickItem *parent = nullptr);
+ ~QQuickLayoutItemProxy() override;
+
+ void geometryChange(const QRectF &newGeom, const QRectF &oldGeom) override;
+ void itemChange(ItemChange c, const ItemChangeData &d) override;
+
+ QQuickItem *target() const;
+ void setTarget(QQuickItem *newTarget);
+ Q_INVOKABLE QQuickItem *effectiveTarget() const;
+ void clearTarget();
+
+ void maybeTakeControl();
+public slots:
+ void updatePos();
+
+private slots:
+ // We define some slots to react to changes to the Layout attached properties.
+ // They are all named following the same scheme, which allows us to use a macro.
+#define propertyForwarding(Property) \
+ void target##Property##Changed(); \
+ void proxy##Property##Changed();
+
+ propertyForwarding(MinimumWidth)
+ propertyForwarding(MinimumHeight)
+ propertyForwarding(PreferredWidth)
+ propertyForwarding(PreferredHeight)
+ propertyForwarding(MaximumWidth)
+ propertyForwarding(MaximumHeight)
+ propertyForwarding(FillWidth)
+ propertyForwarding(FillHeight)
+ propertyForwarding(Alignment)
+ propertyForwarding(HorizontalStretchFactor)
+ propertyForwarding(VerticalStretchFactor)
+ propertyForwarding(Margins)
+ propertyForwarding(LeftMargin)
+ propertyForwarding(TopMargin)
+ propertyForwarding(RightMargin)
+ propertyForwarding(BottomMargin)
+#undef propertyForwarding
+
+signals:
+ void targetChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QQuickLayoutItemProxy)
+};
+
+class QQuickLayoutItemProxyPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickLayoutItemProxy)
+
+public:
+ QQuickLayoutItemProxyPrivate();
+
+ // the target of the LayoutItem
+ QQuickItem *target = nullptr;
+
+ // These values are required to know why the Layout property of the proxy changed
+ // If it changed because the target changed we should keep the connection valid
+ // If a Layout property change is not invoked by the target, it was set
+ // explicitly by the application developer and we should disconnect the connection
+ // between target and proxy for this property.
+ unsigned m_expectProxyMinimumWidthChange : 1;
+ unsigned m_expectProxyMinimumHeightChange : 1;
+ unsigned m_expectProxyPreferredWidthChange : 1;
+ unsigned m_expectProxyPreferredHeightChange : 1;
+ unsigned m_expectProxyMaximumWidthChange : 1;
+ unsigned m_expectProxyMaximumHeightChange : 1;
+ unsigned m_expectProxyFillWidthChange : 1;
+ unsigned m_expectProxyFillHeightChange : 1;
+ unsigned m_expectProxyAlignmentChange : 1;
+ unsigned m_expectProxyHorizontalStretchFactorChange : 1;
+ unsigned m_expectProxyVerticalStretchFactorChange : 1;
+ unsigned m_expectProxyMarginsChange : 1;
+ unsigned m_expectProxyLeftMarginChange : 1;
+ unsigned m_expectProxyTopMarginChange : 1;
+ unsigned m_expectProxyRightMarginChange : 1;
+ unsigned m_expectProxyBottomMarginChange : 1;
+
+ friend class QQuickLayoutItemProxy;
+};
+
+class QQuickLayoutItemProxyAttachedData : public QObject
+{
+ Q_OBJECT
+
+ QML_ANONYMOUS
+ Q_PROPERTY(bool proxyHasControl READ proxyHasControl NOTIFY controllingProxyChanged)
+ Q_PROPERTY(QQuickLayoutItemProxy* controllingProxy READ getControllingProxy NOTIFY controllingProxyChanged)
+ Q_PROPERTY(QQmlListProperty<QQuickLayoutItemProxy> proxies READ getProxies NOTIFY proxiesChanged)
+
+public:
+ QQuickLayoutItemProxyAttachedData(QObject *parent);
+ ~QQuickLayoutItemProxyAttachedData() override;
+ void registerProxy(QQuickLayoutItemProxy *proxy);
+ void releaseProxy(QQuickLayoutItemProxy *proxy);
+ bool takeControl(QQuickLayoutItemProxy *proxy);
+ void releaseControl(QQuickLayoutItemProxy *proxy);
+ QQuickLayoutItemProxy *getControllingProxy() const;
+ QQmlListProperty<QQuickLayoutItemProxy> getProxies();
+ bool proxyHasControl() const;
+
+signals:
+ void controlTaken();
+ void controlReleased();
+ void controllingProxyChanged();
+ void proxiesChanged();
+
+private:
+ QList<QQuickLayoutItemProxy*> proxies;
+ QQuickLayoutItemProxy* controllingProxy;
+};
+
+Q_DECLARE_METATYPE(QQuickLayoutItemProxyAttachedData*);
+
+#endif // QQUICKLAYOUTITEMPROXY_P_H
diff --git a/src/quicklayouts/qquicklayoutstyleinfo.cpp b/src/quicklayouts/qquicklayoutstyleinfo.cpp
index 6f0a3a4087..bfd3544b59 100644
--- a/src/quicklayouts/qquicklayoutstyleinfo.cpp
+++ b/src/quicklayouts/qquicklayoutstyleinfo.cpp
@@ -21,7 +21,7 @@ qreal QQuickLayoutStyleInfo::spacing(Qt::Orientation /*orientation*/) const
qreal spacing = 5.0;
#endif
-#ifndef Q_OS_OSX
+#ifndef Q_OS_MACOS
// On OS X the DPI is always 72 so we should not scale it
spacing = qRound(spacing * (qreal(qt_defaultDpiX()) / 96.0));
#endif
diff --git a/src/quicklayouts/qquicklinearlayout.cpp b/src/quicklayouts/qquicklinearlayout.cpp
index c7beb6045e..41f45259ea 100644
--- a/src/quicklayouts/qquicklinearlayout.cpp
+++ b/src/quicklayouts/qquicklinearlayout.cpp
@@ -247,10 +247,8 @@ QSizeF QQuickGridLayoutBase::sizeHint(Qt::SizeHint whichSizeHint) const
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items are laid out from left to right.
- \li Qt.RightToLeft - Items are laid out from right to left.
- \endlist
+ \value Qt.LeftToRight (default) Items are laid out from left to right.
+ \value Qt.RightToLeft Items are laid out from right to left.
\sa RowLayout::layoutDirection, ColumnLayout::layoutDirection
*/
@@ -363,26 +361,24 @@ void QQuickGridLayoutBase::invalidate(QQuickItem *childItem)
if (!isReady())
return;
qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate()" << this << ", invalidated:" << invalidated();
- if (invalidated()) {
- return;
- }
- qCDebug(lcQuickLayouts) << "d->m_rearranging:" << d->m_rearranging;
- if (d->m_rearranging) {
- d->m_invalidateAfterRearrange << childItem;
- return;
- }
-
if (childItem) {
- if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(childItem))
+ if (d->m_rearranging) {
+ if (!d->m_invalidateAfterRearrange.contains(childItem))
+ d->m_invalidateAfterRearrange << childItem;
+ return;
+ }
+ if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(childItem)) {
layoutItem->invalidate();
+ }
}
+
// invalidate engine
d->engine.invalidate();
qCDebug(lcQuickLayouts) << "calling QQuickLayout::invalidate();";
QQuickLayout::invalidate();
- if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem()))
+ if (auto *parentLayout = qobject_cast<QQuickLayout *>(parentItem()))
parentLayout->invalidate(this);
qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate() LEAVING" << this;
}
@@ -445,7 +441,7 @@ void QQuickGridLayoutBase::itemVisibilityChanged(QQuickItem *item)
void QQuickGridLayoutBase::rearrange(const QSizeF &size)
{
Q_D(QQuickGridLayoutBase);
- if (!isReady())
+ if (!isReady() || !size.isValid())
return;
qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this;
@@ -459,6 +455,10 @@ void QQuickGridLayoutBase::rearrange(const QSizeF &size)
return;
}
+ // Should normally not be needed, but there might be an incoming window resize event that we
+ // will process before we process updatePolish()
+ ensureLayoutItemsUpdated(QQuickLayout::ApplySizeHints | QQuickLayout::Recursive);
+
d->m_rearranging = true;
qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size;
Qt::LayoutDirection visualDir = effectiveLayoutDirection();
@@ -477,8 +477,8 @@ void QQuickGridLayoutBase::rearrange(const QSizeF &size)
d->engine.setGeometries(QRectF(QPointF(0,0), size), d->styleInfo);
d->m_rearranging = false;
- for (QQuickItem *invalid : std::as_const(d->m_invalidateAfterRearrange))
- invalidate(invalid);
+ for (auto childItem : std::as_const(d->m_invalidateAfterRearrange))
+ invalidate(childItem);
d->m_invalidateAfterRearrange.clear();
}
@@ -595,12 +595,10 @@ void QQuickGridLayout::setRows(int rows)
Possible values are:
- \list
- \li GridLayout.LeftToRight (default) - Items are positioned next to
- each other, then wrapped to the next line.
- \li GridLayout.TopToBottom - Items are positioned next to each
- other from top to bottom, then wrapped to the next column.
- \endlist
+ \value GridLayout.LeftToRight
+ (default) Items are positioned next to each other, then wrapped to the next line.
+ \value GridLayout.TopToBottom
+ Items are positioned next to each other from top to bottom, then wrapped to the next column.
\sa rows
\sa columns
@@ -622,6 +620,69 @@ void QQuickGridLayout::setFlow(QQuickGridLayout::Flow flow)
emit flowChanged();
}
+/*!
+ \qmlproperty bool GridLayout::uniformCellWidths
+ \since QtQuick.Layouts 6.6
+
+ If this property is set to \c true, the layout will force all cells to have
+ a uniform width. The layout aims to respect
+ \l{Layout::minimumWidth}{Layout.minimumWidth},
+ \l{Layout::preferredWidth}{Layout.preferredWidth} and
+ \l{Layout::maximumWidth}{Layout.maximumWidth} in this mode but might make
+ compromisses to fullfill the requirements of all items.
+
+ Default value is \c false.
+
+ \sa GridLayout::uniformCellHeights, RowLayout::uniformCellSizes, ColumnLayout::uniformCellSizes
+*/
+bool QQuickGridLayout::uniformCellWidths() const
+{
+ Q_D(const QQuickGridLayout);
+ return d->engine.uniformCellWidths();
+}
+
+void QQuickGridLayout::setUniformCellWidths(bool uniformCellWidths)
+{
+ Q_D(QQuickGridLayout);
+ if (d->engine.uniformCellWidths() == uniformCellWidths)
+ return;
+ d->engine.setUniformCellWidths(uniformCellWidths);
+ invalidate();
+ emit uniformCellWidthsChanged();
+}
+
+/*!
+ \qmlproperty bool GridLayout::uniformCellHeights
+ \since QtQuick.Layouts 6.6
+
+ If this property is set to \c true, the layout will force all cells to have an
+ uniform Height. The layout aims to respect
+ \l{Layout::minimumHeight}{Layout.minimumHeight},
+ \l{Layout::preferredHeight}{Layout.preferredHeight} and
+ \l{Layout::maximumHeight}{Layout.maximumHeight} in this mode but might make
+ compromisses to fullfill the requirements of all items.
+
+ Default value is \c false.
+
+ \sa GridLayout::uniformCellWidths, RowLayout::uniformCellSizes, ColumnLayout::uniformCellSizes
+*/
+bool QQuickGridLayout::uniformCellHeights() const
+{
+ Q_D(const QQuickGridLayout);
+ return d->engine.uniformCellHeights();
+}
+
+void QQuickGridLayout::setUniformCellHeights(bool uniformCellHeights)
+{
+ Q_D(QQuickGridLayout);
+ if (d->engine.uniformCellHeights() == uniformCellHeights)
+ return;
+ d->engine.setUniformCellHeights(uniformCellHeights);
+ invalidate();
+ emit uniformCellHeightsChanged();
+}
+
+
void QQuickGridLayout::insertLayoutItems()
{
Q_D(QQuickGridLayout);
@@ -750,6 +811,7 @@ void QQuickGridLayout::insertLayoutItems()
layoutItem->setStretchFactor(hStretch, Qt::Horizontal);
if (vStretch >= 0)
layoutItem->setStretchFactor(vStretch, Qt::Vertical);
+
d->engine.insertItem(layoutItem, -1);
}
}
@@ -775,10 +837,8 @@ QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation,
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items are laid out from left to right.
- \li Qt.RightToLeft - Items are laid out from right to left
- \endlist
+ \value Qt.LeftToRight (default) Items are laid out from left to right.
+ \value Qt.RightToLeft Items are laid out from right to left
\sa GridLayout::layoutDirection, ColumnLayout::layoutDirection
*/
@@ -792,14 +852,49 @@ QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation,
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items are laid out from left to right.
- \li Qt.RightToLeft - Items are laid out from right to left
- \endlist
+ \value Qt.LeftToRight (default) Items are laid out from left to right.
+ \value Qt.RightToLeft Items are laid out from right to left
\sa GridLayout::layoutDirection, RowLayout::layoutDirection
*/
+/*!
+ \qmlproperty bool RowLayout::uniformCellSizes
+ \since QtQuick.Layouts 6.6
+
+ If this property is set to \c true, the layout will force all cells to have
+ a uniform size.
+
+ \sa GridLayout::uniformCellWidths, GridLayout::uniformCellHeights, ColumnLayout::uniformCellSizes
+*/
+/*!
+ \qmlproperty bool ColumnLayout::uniformCellSizes
+ \since QtQuick.Layouts 6.6
+
+ If this property is set to \c true, the layout will force all cells to have
+ a uniform size.
+
+ \sa GridLayout::uniformCellWidths, GridLayout::uniformCellHeights, RowLayout::uniformCellSizes
+*/
+bool QQuickLinearLayout::uniformCellSizes() const
+{
+ Q_D(const QQuickLinearLayout);
+ Q_ASSERT(d->engine.uniformCellWidths() == d->engine.uniformCellHeights());
+ return d->engine.uniformCellWidths();
+}
+
+void QQuickLinearLayout::setUniformCellSizes(bool uniformCellSizes)
+{
+ Q_D(QQuickLinearLayout);
+ Q_ASSERT(d->engine.uniformCellWidths() == d->engine.uniformCellHeights());
+ if (d->engine.uniformCellHeights() == uniformCellSizes)
+ return;
+ d->engine.setUniformCellWidths(uniformCellSizes);
+ d->engine.setUniformCellHeights(uniformCellSizes);
+ invalidate();
+ emit uniformCellSizesChanged();
+}
+
/*!
\qmlproperty real RowLayout::spacing
diff --git a/src/quicklayouts/qquicklinearlayout_p.h b/src/quicklayouts/qquicklinearlayout_p.h
index a29106e8a2..b3b692d13d 100644
--- a/src/quicklayouts/qquicklinearlayout_p.h
+++ b/src/quicklayouts/qquicklinearlayout_p.h
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE
**/
class QQuickGridLayoutBasePrivate;
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickGridLayoutBase : public QQuickLayout
+class Q_QUICKLAYOUTS_EXPORT QQuickGridLayoutBase : public QQuickLayout
{
Q_OBJECT
@@ -111,7 +111,7 @@ public:
**
**/
class QQuickGridLayoutPrivate;
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickGridLayout : public QQuickGridLayoutBase
+class Q_QUICKLAYOUTS_EXPORT QQuickGridLayout : public QQuickGridLayoutBase
{
Q_OBJECT
@@ -120,6 +120,11 @@ class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickGridLayout : public QQuickGridLayoutBa
Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged)
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged)
+ Q_PROPERTY(bool uniformCellWidths READ uniformCellWidths WRITE setUniformCellWidths
+ NOTIFY uniformCellWidthsChanged REVISION(6, 6) FINAL)
+ Q_PROPERTY(bool uniformCellHeights READ uniformCellHeights WRITE setUniformCellHeights
+ NOTIFY uniformCellHeightsChanged REVISION(6, 6) FINAL)
+
QML_NAMED_ELEMENT(GridLayout)
QML_ADDED_IN_VERSION(1, 0)
public:
@@ -139,6 +144,11 @@ public:
Flow flow() const;
void setFlow(Flow flow);
+ bool uniformCellWidths() const;
+ void setUniformCellWidths(bool uniformCellWidths);
+ bool uniformCellHeights() const;
+ void setUniformCellHeights(bool uniformCellHeights);
+
void insertLayoutItems() override;
Q_SIGNALS:
@@ -149,6 +159,9 @@ Q_SIGNALS:
void rowsChanged();
void flowChanged();
+
+ Q_REVISION(6, 6) void uniformCellWidthsChanged();
+ Q_REVISION(6, 6) void uniformCellHeightsChanged();
private:
Q_DECLARE_PRIVATE(QQuickGridLayout)
};
@@ -170,21 +183,27 @@ public:
**
**/
class QQuickLinearLayoutPrivate;
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickLinearLayout : public QQuickGridLayoutBase
+class Q_QUICKLAYOUTS_EXPORT QQuickLinearLayout : public QQuickGridLayoutBase
{
Q_OBJECT
+ QML_ANONYMOUS
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
+ Q_PROPERTY(bool uniformCellSizes READ uniformCellSizes WRITE setUniformCellSizes
+ NOTIFY uniformCellSizesChanged REVISION(6, 6) FINAL)
public:
explicit QQuickLinearLayout(Qt::Orientation orientation,
QQuickItem *parent = nullptr);
void insertLayoutItem(QQuickItem *item);
qreal spacing() const;
void setSpacing(qreal spacing);
+ bool uniformCellSizes() const;
+ void setUniformCellSizes(bool uniformCellSizes);
void insertLayoutItems() override;
Q_SIGNALS:
void spacingChanged();
+ Q_REVISION(6, 6) void uniformCellSizesChanged();
private:
Q_DECLARE_PRIVATE(QQuickLinearLayout)
};
@@ -202,7 +221,7 @@ public:
** QQuickRowLayout
**
**/
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickRowLayout : public QQuickLinearLayout
+class Q_QUICKLAYOUTS_EXPORT QQuickRowLayout : public QQuickLinearLayout
{
Q_OBJECT
QML_NAMED_ELEMENT(RowLayout)
@@ -219,7 +238,7 @@ public:
** QQuickColumnLayout
**
**/
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickColumnLayout : public QQuickLinearLayout
+class Q_QUICKLAYOUTS_EXPORT QQuickColumnLayout : public QQuickLinearLayout
{
Q_OBJECT
QML_NAMED_ELEMENT(ColumnLayout)
diff --git a/src/quicklayouts/qquickstacklayout.cpp b/src/quicklayouts/qquickstacklayout.cpp
index e07a44053b..0f745be9f9 100644
--- a/src/quicklayouts/qquickstacklayout.cpp
+++ b/src/quicklayouts/qquickstacklayout.cpp
@@ -84,6 +84,7 @@ QQuickStackLayout::QQuickStackLayout(QQuickItem *parent) :
/*!
\qmlproperty int StackLayout::count
+ \readonly
This property holds the number of items that belong to the layout.
@@ -100,6 +101,9 @@ int QQuickStackLayout::count() const
This property holds the index of the child item that is currently visible in the StackLayout.
By default it will be \c -1 for an empty layout, otherwise the default is \c 0 (referring to the first item).
+
+ Since 6.5, inserting/removing a new Item at an index less than or equal to the current index
+ will increment/decrement the current index, but keep the current Item.
*/
int QQuickStackLayout::currentIndex() const
{
@@ -159,6 +163,8 @@ void QQuickStackLayout::componentComplete()
void QQuickStackLayout::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
{
QQuickLayout::itemChange(change, value);
+ if (!isReady())
+ return;
if (change == ItemChildRemovedChange) {
QQuickItem *item = value.item;
@@ -169,7 +175,7 @@ void QQuickStackLayout::itemChange(QQuickItem::ItemChange change, const QQuickIt
stackLayoutAttached->setIsCurrentItem(false);
}
m_cachedItemSizeHints.remove(item);
- childItemsChanged();
+ childItemsChanged(AdjustCurrentIndex); // removal; might have to adjust currentIndex
invalidate();
} else if (change == ItemChildAddedChange) {
childItemsChanged();
@@ -272,7 +278,7 @@ void QQuickStackLayout::invalidate(QQuickItem *childItem)
parentLayout->invalidate(this);
}
-void QQuickStackLayout::childItemsChanged()
+void QQuickStackLayout::childItemsChanged(AdjustCurrentIndexPolicy adjustCurrentIndexPolicy)
{
Q_D(QQuickStackLayout);
const int count = itemCount();
@@ -280,6 +286,28 @@ void QQuickStackLayout::childItemsChanged()
if (!d->explicitCurrentIndex)
d->currentIndex = (count > 0 ? 0 : -1);
+ if (adjustCurrentIndexPolicy == AdjustCurrentIndex) {
+ /*
+ * If an item is inserted or deleted at an index less than or equal to the current index it
+ * will affect the current index, but keep the current item. This is consistent with
+ * QStackedLayout, QStackedWidget and TabBar
+ *
+ * Unless the caller is componentComplete(), we can assume that only one of the children
+ * are visible, and we should keep that visible even if the stacking order has changed.
+ * This means that if the sibling order has changed (or an item stacked before the current
+ * item is added/removed), we must update the currentIndex so that it corresponds with the
+ * current visible item.
+ */
+ if (d->currentIndex < d->count) {
+ for (int i = 0; i < count; ++i) {
+ QQuickItem *child = itemAt(i);
+ if (child->isVisible()) {
+ d->currentIndex = i;
+ break;
+ }
+ }
+ }
+ }
if (d->currentIndex != oldIndex)
emit currentIndexChanged();
@@ -362,6 +390,14 @@ bool QQuickStackLayout::shouldIgnoreItem(QQuickItem *item) const
return QQuickItemPrivate::get(item)->isTransparentForPositioner();
}
+void QQuickStackLayout::itemSiblingOrderChanged(QQuickItem *)
+{
+ if (!isReady())
+ return;
+ childItemsChanged(AdjustCurrentIndex);
+ invalidate();
+}
+
QQuickStackLayoutAttached::QQuickStackLayoutAttached(QObject *object)
{
auto item = qobject_cast<QQuickItem*>(object);
@@ -387,10 +423,16 @@ QQuickStackLayoutAttached::QQuickStackLayoutAttached(QObject *object)
setLayout(stackLayout);
setIndex(index);
setIsCurrentItem(stackLayout->currentIndex() == index);
+
+ // In case of lazy loading in loader, attachedProperties are created and updated for the
+ // object after adding the child object to the stack layout, which leads to entries with
+ // same index. Triggering childItemsChanged() resets to right index in the stack layout.
+ stackLayout->childItemsChanged();
}
/*!
\qmlattachedproperty int StackLayout::index
+ \readonly
This attached property holds the index of each child item in the
\l StackLayout.
@@ -415,6 +457,7 @@ void QQuickStackLayoutAttached::setIndex(int index)
/*!
\qmlattachedproperty bool StackLayout::isCurrentItem
+ \readonly
This attached property is \c true if this child is the current item
in the \l StackLayout.
@@ -439,6 +482,7 @@ void QQuickStackLayoutAttached::setIsCurrentItem(bool isCurrentItem)
/*!
\qmlattachedproperty StackLayout StackLayout::layout
+ \readonly
This attached property holds the \l StackLayout that manages this child
item.
diff --git a/src/quicklayouts/qquickstacklayout_p.h b/src/quicklayouts/qquickstacklayout_p.h
index 5ffa3d5c41..0ace1e63a3 100644
--- a/src/quicklayouts/qquickstacklayout_p.h
+++ b/src/quicklayouts/qquickstacklayout_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickStackLayoutPrivate;
class QQuickStackLayoutAttached;
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickStackLayout : public QQuickLayout
+class Q_QUICKLAYOUTS_EXPORT QQuickStackLayout : public QQuickLayout
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
@@ -52,6 +52,9 @@ public:
int itemCount() const override;
int indexOf(QQuickItem *item) const;
+ /* QQuickItemChangeListener */
+ void itemSiblingOrderChanged(QQuickItem *item) override;
+
static QQuickStackLayoutAttached *qmlAttachedProperties(QObject *object);
Q_SIGNALS:
@@ -59,9 +62,14 @@ Q_SIGNALS:
void countChanged();
private:
+ enum AdjustCurrentIndexPolicy {
+ DontAdjustCurrentIndex,
+ AdjustCurrentIndex
+ };
+
static void collectItemSizeHints(QQuickItem *item, QSizeF *sizeHints);
bool shouldIgnoreItem(QQuickItem *item) const;
- void childItemsChanged();
+ void childItemsChanged(AdjustCurrentIndexPolicy adjustCurrentIndexPolicy = DontAdjustCurrentIndex);
Q_DECLARE_PRIVATE(QQuickStackLayout)
friend class QQuickStackLayoutAttached;
@@ -91,12 +99,12 @@ private:
bool explicitCurrentIndex;
};
-class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickStackLayoutAttached : public QObject
+class Q_QUICKLAYOUTS_EXPORT QQuickStackLayoutAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(int index READ index NOTIFY indexChanged)
- Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY isCurrentItemChanged)
- Q_PROPERTY(QQuickStackLayout *layout READ layout NOTIFY layoutChanged)
+ Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
+ Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY isCurrentItemChanged FINAL)
+ Q_PROPERTY(QQuickStackLayout *layout READ layout NOTIFY layoutChanged FINAL)
public:
QQuickStackLayoutAttached(QObject *object);
diff --git a/src/quicknativestyle/CMakeLists.txt b/src/quicknativestyle/CMakeLists.txt
index a1c1739d40..c5056d33b7 100644
--- a/src/quicknativestyle/CMakeLists.txt
+++ b/src/quicknativestyle/CMakeLists.txt
@@ -7,22 +7,27 @@
set(qml_files
"controls/DefaultButton.qml"
- "controls/DefaultSlider.qml"
- "controls/DefaultGroupBox.qml"
"controls/DefaultCheckBox.qml"
+ "controls/DefaultComboBox.qml"
+ "controls/DefaultDial.qml"
+ "controls/DefaultFrame.qml"
+ "controls/DefaultGroupBox.qml"
+ "controls/DefaultItemDelegate.qml"
+ "controls/DefaultItemDelegateIconLabel.qml"
+ "controls/DefaultProgressBar.qml"
"controls/DefaultRadioButton.qml"
+ "controls/DefaultRadioDelegate.qml"
+ "controls/DefaultScrollBar.qml"
+ "controls/DefaultSlider.qml"
"controls/DefaultSpinBox.qml"
- "controls/DefaultTextField.qml"
- "controls/DefaultFrame.qml"
"controls/DefaultTextArea.qml"
- "controls/DefaultComboBox.qml"
- "controls/DefaultScrollBar.qml"
- "controls/DefaultProgressBar.qml"
- "controls/DefaultDial.qml"
+ "controls/DefaultTextField.qml"
)
if(MACOS)
- list(APPEND qml_files "util/FocusFrame.qml")
+ list(APPEND qml_files "util/MacFocusFrame.qml")
+elseif(WIN32)
+ list(APPEND qml_files "util/WindowsFocusFrame.qml")
endif()
if(QT_FEATURE_quick_treeview)
@@ -45,12 +50,14 @@ qt_internal_add_qml_module(qtquickcontrols2nativestyleplugin
items/qquickstyleitem.cpp items/qquickstyleitem.h
items/qquickstyleitembutton.cpp items/qquickstyleitembutton.h
items/qquickstyleitemcheckbox.cpp items/qquickstyleitemcheckbox.h
- items/qquickstyleitemcombobox.cpp items/qquickstyleitemcombobox.h
+ items/qquickstyleitemdelaybutton.cpp items/qquickstyleitemdelaybutton.h
+ items/qquickstyleitemcheckdelegate.cpp items/qquickstyleitemcheckdelegate.h
items/qquickstyleitemdial.cpp items/qquickstyleitemdial.h
items/qquickstyleitemframe.cpp items/qquickstyleitemframe.h
items/qquickstyleitemgroupbox.cpp items/qquickstyleitemgroupbox.h
items/qquickstyleitemprogressbar.cpp items/qquickstyleitemprogressbar.h
items/qquickstyleitemradiobutton.cpp items/qquickstyleitemradiobutton.h
+ items/qquickstyleitemradiodelegate.cpp items/qquickstyleitemradiodelegate.h
items/qquickstyleitemscrollbar.cpp items/qquickstyleitemscrollbar.h
items/qquickstyleitemslider.cpp items/qquickstyleitemslider.h
items/qquickstyleitemspinbox.cpp items/qquickstyleitemspinbox.h
@@ -63,6 +70,7 @@ qt_internal_add_qml_module(qtquickcontrols2nativestyleplugin
qstyle/qquickstylehelper.cpp qstyle/qquickstylehelper_p.h
qstyle/qquickstyleoption.cpp qstyle/qquickstyleoption.h
qtquickcontrols2nativestyleplugin.cpp
+ util/qquickfocusframe.cpp util/qquickfocusframe.h
QML_FILES
${qml_files}
DEFINES
@@ -86,6 +94,11 @@ qt_internal_extend_target(qtquickcontrols2nativestyleplugin CONDITION QT_FEATURE
items/qquickstyleitemtreeindicator.cpp items/qquickstyleitemtreeindicator.h
)
+qt_internal_extend_target(qtquickcontrols2nativestyleplugin CONDITION QT_FEATURE_qml_delegate_model
+ SOURCES
+ items/qquickstyleitemcombobox.cpp items/qquickstyleitemcombobox.h
+)
+
qt_internal_extend_target(qtquickcontrols2nativestyleplugin CONDITION MACOS
SOURCES
items/qquickstyleitemscrollviewcorner.cpp items/qquickstyleitemscrollviewcorner.h
@@ -105,6 +118,7 @@ qt_internal_extend_target(qtquickcontrols2nativestyleplugin CONDITION WIN32
qstyle/windows/qquickwindowsxpstyle.cpp
qstyle/windows/qquickwindowsxpstyle_p.h
qstyle/windows/qquickwindowsxpstyle_p_p.h
+ util/qquickwindowsfocusframe.cpp util/qquickwindowsfocusframe.h
INCLUDE_DIRECTORIES
qstyle/windows
LIBRARIES
diff --git a/src/quicknativestyle/controls/DefaultComboBox.qml b/src/quicknativestyle/controls/DefaultComboBox.qml
index a9a820a2d1..0876c522b3 100644
--- a/src/quicknativestyle/controls/DefaultComboBox.qml
+++ b/src/quicknativestyle/controls/DefaultComboBox.qml
@@ -1,6 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+pragma ComponentBehavior: Bound
+
import QtQuick
import QtQuick.Window
import QtQuick.Controls
@@ -56,8 +58,11 @@ T.ComboBox {
}
delegate: ItemDelegate {
+ required property var model
+ required property int index
+
width: ListView.view.width
- text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ text: model[control.textRole]
palette.text: control.palette.text
palette.highlightedText: control.palette.highlightedText
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
diff --git a/src/quicknativestyle/controls/DefaultItemDelegate.qml b/src/quicknativestyle/controls/DefaultItemDelegate.qml
new file mode 100644
index 0000000000..55c54dbca5
--- /dev/null
+++ b/src/quicknativestyle/controls/DefaultItemDelegate.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.NativeStyle as NativeStyle
+
+T.ItemDelegate {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ padding: 6
+ spacing: 6
+
+ icon.width: 16
+ icon.height: 16
+
+ contentItem: NativeStyle.DefaultItemDelegateIconLabel {}
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 20
+ color: Qt.darker(control.highlighted
+ ? control.palette.highlight : control.palette.button, control.down ? 1.05 : 1)
+ }
+}
diff --git a/src/quicknativestyle/controls/DefaultItemDelegateIconLabel.qml b/src/quicknativestyle/controls/DefaultItemDelegateIconLabel.qml
new file mode 100644
index 0000000000..7ac31e9f3e
--- /dev/null
+++ b/src/quicknativestyle/controls/DefaultItemDelegateIconLabel.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+
+IconLabel {
+ text: control.text
+ font: control.font
+ icon: control.icon
+ color: control.palette.windowText
+ spacing: control.spacing
+ mirrored: control.mirrored
+ display: control.display
+ alignment: control.display === IconLabel.IconOnly || control.display === IconLabel.TextUnderIcon
+ ? Qt.AlignCenter : Qt.AlignLeft
+ leftPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
+ rightPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
+
+ readonly property T.ItemDelegate control: parent as T.ItemDelegate
+}
diff --git a/src/quicknativestyle/controls/DefaultProgressBar.qml b/src/quicknativestyle/controls/DefaultProgressBar.qml
index 77dd4741b1..0b318a5635 100644
--- a/src/quicknativestyle/controls/DefaultProgressBar.qml
+++ b/src/quicknativestyle/controls/DefaultProgressBar.qml
@@ -14,10 +14,9 @@ T.ProgressBar {
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding,
- control.horizontal ? 90 : 0 /* minimum */ )
+ 90)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- implicitContentHeight + topPadding + bottomPadding,
- control.vertical ? 90 : 0 /* minimum */ )
+ implicitContentHeight + topPadding + bottomPadding)
background: NativeStyle.ProgressBar {
control: control
diff --git a/src/quicknativestyle/controls/DefaultRadioDelegate.qml b/src/quicknativestyle/controls/DefaultRadioDelegate.qml
new file mode 100644
index 0000000000..82bc0d26bf
--- /dev/null
+++ b/src/quicknativestyle/controls/DefaultRadioDelegate.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.NativeStyle as NativeStyle
+
+T.RadioDelegate {
+ id: control
+
+ readonly property bool __nativeIndicator: indicator instanceof NativeStyle.StyleItem
+ readonly property bool __notCustomizable: true
+ readonly property Item __focusFrameTarget: indicator
+ readonly property Item __focusFrameStyleItem: indicator
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ spacing: 6
+ padding: 6
+
+ icon.width: 16
+ icon.height: 16
+
+ contentItem: NativeStyle.DefaultItemDelegateIconLabel {
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ indicator: NativeStyle.RadioDelegate {
+ x: control.text
+ ? (control.mirrored ? control.leftPadding : control.width - width - control.rightPadding)
+ : control.leftPadding + (control.availableWidth - width) / 2
+ y: control.topPadding + Math.round((control.availableHeight - height) / 2)
+ contentWidth: control.implicitContentWidth
+ contentHeight: control.implicitContentHeight
+ useNinePatchImage: false
+ control: control
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 20
+ color: Qt.darker(control.highlighted
+ ? control.palette.highlight : control.palette.button, control.down ? 1.05 : 1)
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+}
diff --git a/src/quicknativestyle/controls/DefaultScrollBar.qml b/src/quicknativestyle/controls/DefaultScrollBar.qml
index 7394dd1e74..1fad8e9a41 100644
--- a/src/quicknativestyle/controls/DefaultScrollBar.qml
+++ b/src/quicknativestyle/controls/DefaultScrollBar.qml
@@ -17,7 +17,7 @@ T.ScrollBar {
implicitContentHeight + topPadding + bottomPadding)
visible: policy === T.ScrollBar.AlwaysOn || (policy === T.ScrollBar.AsNeeded && size < 1.0)
- minimumSize: !__nativeContentItem ? 10 : orientation === Qt.Vertical ?
+ minimumSize: !__nativeContentItem ? 0.1 : orientation === Qt.Vertical ?
contentItem.minimumSize.height / height : contentItem.minimumSize.width / width
background: NativeStyle.ScrollBar {
diff --git a/src/quicknativestyle/controls/DefaultTextArea.qml b/src/quicknativestyle/controls/DefaultTextArea.qml
index 4ed9427bab..faab250fae 100644
--- a/src/quicknativestyle/controls/DefaultTextArea.qml
+++ b/src/quicknativestyle/controls/DefaultTextArea.qml
@@ -34,8 +34,8 @@ T.TextArea {
id: placeholder
x: control.leftPadding
y: control.topPadding
- width: control.availableWidth
- height: control.availableHeight
+ width: control.width - (control.leftPadding + control.rightPadding)
+ height: control.height - (control.topPadding + control.bottomPadding)
text: control.placeholderText
font: control.font
color: control.placeholderTextColor
diff --git a/src/quicknativestyle/controls/DefaultTreeViewDelegate.qml b/src/quicknativestyle/controls/DefaultTreeViewDelegate.qml
index a15b4d36f1..89130947bb 100644
--- a/src/quicknativestyle/controls/DefaultTreeViewDelegate.qml
+++ b/src/quicknativestyle/controls/DefaultTreeViewDelegate.qml
@@ -69,6 +69,7 @@ T.TreeViewDelegate {
// The edit delegate is a separate component, and doesn't need
// to follow the same strict rules that are applied to a control.
// qmllint disable attached-property-reuse
+ // qmllint disable controls-attached-property-reuse
// qmllint disable controls-sanity
TableView.editDelegate: FocusScope {
width: parent.width
@@ -76,7 +77,7 @@ T.TreeViewDelegate {
readonly property int __role: {
let model = control.treeView.model
- let index = control.treeView.modelIndex(column, row)
+ let index = control.treeView.index(row, column)
let editText = model.data(index, Qt.EditRole)
return editText !== undefined ? Qt.EditRole : Qt.DisplayRole
}
@@ -86,17 +87,18 @@ T.TreeViewDelegate {
x: control.contentItem.x
y: (parent.height - height) / 2
width: control.contentItem.width
- text: control.treeView.model.data(control.treeView.modelIndex(column, row), __role)
+ text: control.treeView.model.data(control.treeView.index(row, column), __role)
focus: true
}
TableView.onCommit: {
- let index = TableView.view.modelIndex(column, row)
+ let index = TableView.view.index(row, column)
TableView.view.model.setData(index, textField.text, __role)
}
Component.onCompleted: textField.selectAll()
}
// qmllint enable attached-property-reuse
+ // qmllint enable controls-attached-property-reuse
// qmllint enable controls-sanity
}
diff --git a/src/quicknativestyle/items/qquickstyleitem.h b/src/quicknativestyle/items/qquickstyleitem.h
index 1ad8932d23..3d04371b7e 100644
--- a/src/quicknativestyle/items/qquickstyleitem.h
+++ b/src/quicknativestyle/items/qquickstyleitem.h
@@ -14,6 +14,8 @@
#include "qquickstyle.h"
#include "qquickstyleoption.h"
+#include <QtCore/qpointer.h>
+
// Work-around for now, to avoid creator getting confused
// about missing macros. Should eventually be defined
// in qt declarative somewhere I assume.
@@ -274,6 +276,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickStyleItem::DebugFlags)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickStyleItem)
-
#endif // QQUICKSTYLEITEM_H
diff --git a/src/quicknativestyle/items/qquickstyleitembutton.h b/src/quicknativestyle/items/qquickstyleitembutton.h
index 668013ac6f..82dff23048 100644
--- a/src/quicknativestyle/items/qquickstyleitembutton.h
+++ b/src/quicknativestyle/items/qquickstyleitembutton.h
@@ -23,7 +23,7 @@ protected:
StyleItemGeometry calculateGeometry() override;
private:
- void initStyleOption(QStyleOptionButton &styleOption) const;
+ virtual void initStyleOption(QStyleOptionButton &styleOption) const;
};
QT_END_NAMESPACE
diff --git a/src/quicknativestyle/items/qquickstyleitemcheckbox.h b/src/quicknativestyle/items/qquickstyleitemcheckbox.h
index 92cda579ef..f50c621d12 100644
--- a/src/quicknativestyle/items/qquickstyleitemcheckbox.h
+++ b/src/quicknativestyle/items/qquickstyleitemcheckbox.h
@@ -23,7 +23,7 @@ protected:
StyleItemGeometry calculateGeometry() override;
private:
- void initStyleOption(QStyleOptionButton &styleOption) const;
+ virtual void initStyleOption(QStyleOptionButton &styleOption) const;
};
QT_END_NAMESPACE
diff --git a/src/quicknativestyle/items/qquickstyleitemcheckdelegate.cpp b/src/quicknativestyle/items/qquickstyleitemcheckdelegate.cpp
new file mode 100644
index 0000000000..22576c50d2
--- /dev/null
+++ b/src/quicknativestyle/items/qquickstyleitemcheckdelegate.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickstyleitemcheckdelegate.h"
+
+#include <QtQuickTemplates2/private/qquickcheckdelegate_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QQuickStyleItemCheckDelegate::connectToControl() const
+{
+ QQuickStyleItem::connectToControl();
+ auto checkDelegate = control<QQuickCheckDelegate>();
+ connect(checkDelegate, &QQuickCheckDelegate::downChanged, this, &QQuickStyleItem::markImageDirty);
+ connect(checkDelegate, &QQuickCheckDelegate::checkStateChanged, this, &QQuickStyleItem::markImageDirty);
+}
+
+void QQuickStyleItemCheckDelegate::initStyleOption(QStyleOptionButton &styleOption) const
+{
+ initStyleOptionBase(styleOption);
+ auto checkDelegate = control<QQuickCheckDelegate>();
+
+ styleOption.state |= checkDelegate->isDown() ? QStyle::State_Sunken : QStyle::State_Raised;
+ if (checkDelegate->isTristate() && checkDelegate->checkState() == Qt::PartiallyChecked)
+ styleOption.state |= QStyle::State_NoChange;
+ else
+ styleOption.state |= checkDelegate->isChecked() ? QStyle::State_On : QStyle::State_Off;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickstyleitemcheckdelegate.cpp"
diff --git a/src/quicknativestyle/items/qquickstyleitemcheckdelegate.h b/src/quicknativestyle/items/qquickstyleitemcheckdelegate.h
new file mode 100644
index 0000000000..27ff42de25
--- /dev/null
+++ b/src/quicknativestyle/items/qquickstyleitemcheckdelegate.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKSTYLEITEMCHECKDELEGATE_H
+#define QQUICKSTYLEITEMCHECKDELEGATE_H
+
+#include "qquickstyleitemcheckbox.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickStyleItemCheckDelegate : public QQuickStyleItemCheckBox
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(CheckDelegate)
+
+protected:
+ void connectToControl() const override;
+
+private:
+ void initStyleOption(QStyleOptionButton &styleOption) const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSTYLEITEMCHECKDELEGATE_H
diff --git a/src/quicknativestyle/items/qquickstyleitemdelaybutton.cpp b/src/quicknativestyle/items/qquickstyleitemdelaybutton.cpp
new file mode 100644
index 0000000000..19309d11b6
--- /dev/null
+++ b/src/quicknativestyle/items/qquickstyleitemdelaybutton.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickstyleitemdelaybutton.h"
+
+#include <QtQuickTemplates2/private/qquickdelaybutton_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QQuickStyleItemDelayButton::connectToControl() const
+{
+ QQuickStyleItem::connectToControl();
+ auto button = control<QQuickDelayButton>();
+ connect(button, &QQuickButton::downChanged, this, &QQuickStyleItem::markImageDirty);
+ connect(button, &QQuickButton::checkedChanged, this, &QQuickStyleItem::markImageDirty);
+}
+
+void QQuickStyleItemDelayButton::initStyleOption(QStyleOptionButton &styleOption) const
+{
+ initStyleOptionBase(styleOption);
+ auto button = control<QQuickDelayButton>();
+
+ styleOption.state |= button->isDown() ? QStyle::State_Sunken : QStyle::State_Raised;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickstyleitemdelaybutton.cpp"
diff --git a/src/quicknativestyle/items/qquickstyleitemdelaybutton.h b/src/quicknativestyle/items/qquickstyleitemdelaybutton.h
new file mode 100644
index 0000000000..e2a0bf2174
--- /dev/null
+++ b/src/quicknativestyle/items/qquickstyleitemdelaybutton.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKSTYLEITEMDELAYBUTTON_H
+#define QQUICKSTYLEITEMDELAYBUTTON_H
+
+#include "qquickstyleitembutton.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickStyleItemDelayButton : public QQuickStyleItemButton
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(DelayButton)
+
+protected:
+ void connectToControl() const override;
+
+private:
+ void initStyleOption(QStyleOptionButton &styleOption) const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSTYLEITEMDELAYBUTTON_H
diff --git a/src/quicknativestyle/items/qquickstyleitemdial.cpp b/src/quicknativestyle/items/qquickstyleitemdial.cpp
index e2407e9e81..fe36f866e4 100644
--- a/src/quicknativestyle/items/qquickstyleitemdial.cpp
+++ b/src/quicknativestyle/items/qquickstyleitemdial.cpp
@@ -19,6 +19,8 @@ void QQuickStyleItemDial::connectToControl() const
connect(dial, &QQuickDial::positionChanged, this, &QQuickStyleItem::markImageDirty);
connect(dial, &QQuickDial::valueChanged, this, &QQuickStyleItem::markImageDirty);
connect(dial, &QQuickDial::stepSizeChanged, this, &QQuickStyleItem::markImageDirty);
+ connect(dial, &QQuickDial::startAngleChanged, this, &QQuickStyleItem::markImageDirty);
+ connect(dial, &QQuickDial::endAngleChanged, this, &QQuickStyleItem::markImageDirty);
connect(dial, &QQuickDial::pressedChanged, this, &QQuickStyleItem::markImageDirty);
}
@@ -54,6 +56,8 @@ void QQuickStyleItemDial::initStyleOption(QStyleOptionSlider &styleOption) const
styleOption.tickInterval = dial->stepSize();
styleOption.dialWrapping = dial->wrap();
styleOption.upsideDown = true;
+ styleOption.startAngle = dial->startAngle();
+ styleOption.endAngle = dial->endAngle();
if (dial->isPressed())
styleOption.state |= QStyle::State_Sunken;
diff --git a/src/quicknativestyle/items/qquickstyleitemradiobutton.h b/src/quicknativestyle/items/qquickstyleitemradiobutton.h
index 4674098116..209d2cc65f 100644
--- a/src/quicknativestyle/items/qquickstyleitemradiobutton.h
+++ b/src/quicknativestyle/items/qquickstyleitemradiobutton.h
@@ -23,7 +23,7 @@ protected:
StyleItemGeometry calculateGeometry() override;
private:
- void initStyleOption(QStyleOptionButton &styleOption) const;
+ virtual void initStyleOption(QStyleOptionButton &styleOption) const;
};
QT_END_NAMESPACE
diff --git a/src/quicknativestyle/items/qquickstyleitemradiodelegate.cpp b/src/quicknativestyle/items/qquickstyleitemradiodelegate.cpp
new file mode 100644
index 0000000000..a4325cfe15
--- /dev/null
+++ b/src/quicknativestyle/items/qquickstyleitemradiodelegate.cpp
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickstyleitemradiodelegate.h"
+
+#include <QtQuickTemplates2/private/qquickradiodelegate_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QQuickStyleItemRadioDelegate::connectToControl() const
+{
+ QQuickStyleItem::connectToControl();
+ auto radioDelegate = control<QQuickRadioDelegate>();
+ connect(radioDelegate, &QQuickRadioDelegate::downChanged, this, &QQuickStyleItem::markImageDirty);
+ connect(radioDelegate, &QQuickRadioDelegate::checkedChanged, this, &QQuickStyleItem::markImageDirty);
+}
+
+void QQuickStyleItemRadioDelegate::initStyleOption(QStyleOptionButton &styleOption) const
+{
+ initStyleOptionBase(styleOption);
+ auto radioDelegate = control<QQuickRadioDelegate>();
+
+ styleOption.state |= radioDelegate->isDown() ? QStyle::State_Sunken : QStyle::State_Raised;
+ styleOption.state |= radioDelegate->isChecked() ? QStyle::State_On : QStyle::State_Off;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickstyleitemradiodelegate.cpp"
diff --git a/src/quicknativestyle/items/qquickstyleitemradiodelegate.h b/src/quicknativestyle/items/qquickstyleitemradiodelegate.h
new file mode 100644
index 0000000000..3661ce792e
--- /dev/null
+++ b/src/quicknativestyle/items/qquickstyleitemradiodelegate.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKSTYLEITEMRADIODELEGATE_H
+#define QQUICKSTYLEITEMRADIODELEGATE_H
+
+#include "qquickstyleitemradiobutton.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickStyleItemRadioDelegate : public QQuickStyleItemRadioButton
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(RadioDelegate)
+
+protected:
+ void connectToControl() const override;
+
+private:
+ void initStyleOption(QStyleOptionButton &styleOption) const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSTYLEITEMRADIODELEGATE_H
diff --git a/src/quicknativestyle/items/qquickstyleitemscrollbar.cpp b/src/quicknativestyle/items/qquickstyleitemscrollbar.cpp
index c328ab6d1f..097341eb56 100644
--- a/src/quicknativestyle/items/qquickstyleitemscrollbar.cpp
+++ b/src/quicknativestyle/items/qquickstyleitemscrollbar.cpp
@@ -5,6 +5,14 @@
QT_BEGIN_NAMESPACE
+QQuickStyleItemScrollBar::QQuickStyleItemScrollBar(QQuickItem *parent)
+ : QQuickStyleItem(parent)
+{
+#ifdef QT_DEBUG
+ setObjectName("styleItemScrollBar");
+#endif
+}
+
QFont QQuickStyleItemScrollBar::styleFont(QQuickItem *control) const
{
return style()->font(QStyle::CE_ProgressBarLabel, controlSize(control));
diff --git a/src/quicknativestyle/items/qquickstyleitemscrollbar.h b/src/quicknativestyle/items/qquickstyleitemscrollbar.h
index 6ef232420f..57a9c19059 100644
--- a/src/quicknativestyle/items/qquickstyleitemscrollbar.h
+++ b/src/quicknativestyle/items/qquickstyleitemscrollbar.h
@@ -26,6 +26,8 @@ public:
};
Q_ENUM(SubControl)
+ explicit QQuickStyleItemScrollBar(QQuickItem *parent = nullptr);
+
QFont styleFont(QQuickItem *control) const override;
protected:
diff --git a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm b/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm
index eb777ea9f2..b92c380657 100644
--- a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm
+++ b/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm
@@ -166,45 +166,6 @@ const int QMacStylePrivate::PushButtonContentPadding = 6;
QVector<QPointer<QObject> > QMacStylePrivate::scrollBars;
-// Title bar gradient colors for Lion were determined by inspecting PSDs exported
-// using CoreUI's CoreThemeDocument; there is no public API to retrieve them
-
-static QLinearGradient titlebarGradientActive()
-{
- static QLinearGradient darkGradient = [](){
- QLinearGradient gradient;
- // FIXME: colors are chosen somewhat arbitrarily and could be fine-tuned,
- // or ideally determined by calling a native API.
- gradient.setColorAt(0, QColor(47, 47, 47));
- return gradient;
- }();
- static QLinearGradient lightGradient = [](){
- QLinearGradient gradient;
- gradient.setColorAt(0, QColor(235, 235, 235));
- gradient.setColorAt(0.5, QColor(210, 210, 210));
- gradient.setColorAt(0.75, QColor(195, 195, 195));
- gradient.setColorAt(1, QColor(180, 180, 180));
- return gradient;
- }();
- return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient;
-}
-
-static QLinearGradient titlebarGradientInactive()
-{
- static QLinearGradient darkGradient = [](){
- QLinearGradient gradient;
- gradient.setColorAt(1, QColor(42, 42, 42));
- return gradient;
- }();
- static QLinearGradient lightGradient = [](){
- QLinearGradient gradient;
- gradient.setColorAt(0, QColor(250, 250, 250));
- gradient.setColorAt(1, QColor(225, 225, 225));
- return gradient;
- }();
- return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient;
-}
-
static const QColor titlebarSeparatorLineActive(111, 111, 111);
static const QColor titlebarSeparatorLineInactive(131, 131, 131);
static const QColor darkModeSeparatorLine(88, 88, 88);
@@ -2041,20 +2002,21 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt) const
ret = 1;
break;
case PM_ScrollView_ScrollBarOverlap:
- ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay ?
- pixelMetric(PM_ScrollBarExtent, opt) : 0;
+ ret = styleHint(SH_ScrollBar_Transient, opt, nullptr)
+ ? pixelMetric(PM_ScrollBarExtent, opt)
+ : 0;
break;
case PM_PushButtonFocusFrameRadius:
- ret = LargeSmallMini(opt, 8, 7, 5);
+ ret = LargeSmallMini(opt, 5, 4, 2);
break;
case PM_CheckBoxFocusFrameRadius:
- ret = LargeSmallMini(opt, 6, 5, 4);
+ ret = LargeSmallMini(opt, 3, 2, 1);
break;
case PM_ComboBoxFocusFrameRadius:
- ret = LargeSmallMini(opt, 8, 7, 4);
+ ret = LargeSmallMini(opt, 5, 4, 1);
break;
case PM_RadioButtonFocusFrameRadius:
- ret = 10;
+ ret = 7;
break;
case PM_SliderFocusFrameRadius:
// QTBUG-93423: We currently need to skip drawing a focus ring around the handle, since
@@ -2067,7 +2029,7 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt) const
case PM_SpinBoxFocusFrameRadius:
case PM_TextAreaFocusFrameRadius:
case PM_TextFieldFocusFrameRadius:
- ret = 3;
+ ret = 0;
break;
default:
ret = QCommonStyle::pixelMetric(metric, opt);
@@ -2277,7 +2239,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, QStyleHintReturn
++srow;
}
}
- QBitmap qmask = QBitmap::fromImage(img_mask);
+ QBitmap qmask = QBitmap::fromImage(std::move(img_mask));
mask->region = QRegion(qmask);
}
break; }
@@ -2878,20 +2840,10 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
// } break;
//#endif // QT_CONFIG(tabbar)
case PE_PanelStatusBar: {
- // Fill the status bar with the titlebar gradient.
- QLinearGradient linearGrad;
- const bool isMainWindow = qt_macWindowMainWindow(opt->window);
- if (isMainWindow)
- linearGrad = titlebarGradientActive();
- else
- linearGrad = titlebarGradientInactive();
-
- linearGrad.setStart(0, opt->rect.top());
- linearGrad.setFinalStop(0, opt->rect.bottom());
- p->fillRect(opt->rect, linearGrad);
+ p->fillRect(opt->rect, opt->palette.window());
// Draw the black separator line at the top of the status bar.
- if (isMainWindow)
+ if (qt_macWindowMainWindow(opt->window))
p->setPen(titlebarSeparatorLineActive);
else
p->setPen(titlebarSeparatorLineInactive);
@@ -3496,12 +3448,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
}
// fill title bar background
- QLinearGradient linearGrad;
- linearGrad.setStart(QPointF(0, 0));
- linearGrad.setFinalStop(QPointF(0, 2 * effectiveRect.height()));
- linearGrad.setColorAt(0, opt->palette.button().color());
- linearGrad.setColorAt(1, opt->palette.dark().color());
- p->fillRect(effectiveRect, linearGrad);
+ p->fillRect(effectiveRect, opt->palette.window());
// draw horizontal line at bottom
p->setPen(opt->palette.dark().color());
@@ -4859,16 +4806,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
const auto frameAdjust = 1.0 / p->device()->devicePixelRatioF();
const auto innerFrameRect = outerFrameRect.adjusted(frameAdjust, frameAdjust, -frameAdjust, 0);
QPainterPath innerFramePath = d->windowPanelPath(innerFrameRect);
- if (isActive) {
- QLinearGradient g;
- g.setStart(QPointF(0, 0));
- g.setFinalStop(QPointF(0, 2 * opt->rect.height()));
- g.setColorAt(0, opt->palette.button().color());
- g.setColorAt(1, opt->palette.dark().color());
- p->fillPath(innerFramePath, g);
- } else {
- p->fillPath(innerFramePath, opt->palette.button());
- }
+ p->fillPath(innerFramePath, opt->palette.button());
if (titlebar->subControls & (SC_TitleBarCloseButton
| SC_TitleBarMaxButton
diff --git a/src/quicknativestyle/qstyle/qquickcommonstyle.cpp b/src/quicknativestyle/qstyle/qquickcommonstyle.cpp
index d8cac57486..530dc24f84 100644
--- a/src/quicknativestyle/qstyle/qquickcommonstyle.cpp
+++ b/src/quicknativestyle/qstyle/qquickcommonstyle.cpp
@@ -24,7 +24,9 @@
#include <qtextformat.h>
#include <qfileinfo.h>
#include <qdir.h>
+#if QT_CONFIG(settings)
#include <qsettings.h>
+#endif
#include <qvariant.h>
#include <qpixmapcache.h>
#include <qmatrix4x4.h>
@@ -502,6 +504,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
proxy()->drawPrimitive(PE_FrameLineEdit, panel, p);
}
break;
+#if QT_CONFIG(quick_itemview)
case PE_IndicatorColumnViewArrow: {
if (const QStyleOptionViewItem *viewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
bool reverse = (viewOpt->direction == Qt::RightToLeft);
@@ -595,6 +598,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
}
}
break;
+#endif // QT_CONFIG(quick_itemview)
case PE_PanelScrollAreaCorner: {
const QBrush brush(opt->palette.brush(QPalette::Window));
p->fillRect(opt->rect, brush);
@@ -665,7 +669,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
imagePainter.drawPolygon(a);
imagePainter.end();
- pixmap = QPixmap::fromImage(image);
+ pixmap = QPixmap::fromImage(std::move(image));
pixmap.setDevicePixelRatio(pixelRatio);
QPixmapCache::insert(pixmapName, pixmap);
}
@@ -811,6 +815,7 @@ QString QCommonStylePrivate::calculateElidedText(const QString &text, const QTex
return ret;
}
+#if QT_CONFIG(quick_itemview)
QSize QCommonStylePrivate::viewItemSize(const QStyleOptionViewItem *option, int role) const
{
switch (role) {
@@ -1028,6 +1033,7 @@ void QCommonStylePrivate::viewItemLayout(const QStyleOptionViewItem *opt, QRect
*textRect = display;
}
}
+#endif // QT_CONFIG(quick_itemview)
QString QCommonStylePrivate::toolButtonElideText(const QStyleOptionToolButton *option,
const QRect &textRect, int flags) const
@@ -2034,6 +2040,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt,
p->drawLine(line2starting, top, line2starting, bottom);
}
break;
+#if QT_CONFIG(quick_itemview)
case CE_ItemViewItem:
if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
p->save();
@@ -2112,6 +2119,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt,
p->restore();
}
break;
+#endif // QT_CONFIG(quick_itemview)
case CE_ShapedFrame:
if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
int frameShape = f->frameShape;
@@ -2828,6 +2836,7 @@ QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt) const
}
break;
}
+#if QT_CONFIG(quick_itemview)
case SE_ItemViewItemCheckIndicator:
if (!qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
r = subElementRect(SE_CheckBoxIndicator, opt);
@@ -2854,6 +2863,7 @@ QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt) const
r = d->displayRect;
}
break;
+#endif // QT_CONFIG(quick_itemview)
case SE_ToolBarHandle:
if (const QStyleOptionToolBar *tbopt = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
if (tbopt->features & QStyleOptionToolBar::Movable) {
@@ -2898,14 +2908,13 @@ static StaticPolygonF<3> calcArrow(const QStyleOptionSlider *dial, qreal &a)
int r = qMin(width, height) / 2;
int currentSliderPosition = dial->upsideDown ? dial->sliderPosition : (dial->maximum - dial->sliderPosition);
+ qreal startAngle = (90. - dial->startAngle) * Q_PI / 180.;
+ qreal spanAngle = (dial->endAngle - dial->startAngle) * Q_PI / 180.;
if (dial->maximum == dial->minimum)
a = Q_PI / 2;
- else if (dial->dialWrapping)
- a = Q_PI * 3 / 2 - (currentSliderPosition - dial->minimum) * 2 * Q_PI
- / (dial->maximum - dial->minimum);
else
- a = (Q_PI * 8 - (currentSliderPosition - dial->minimum) * 10 * Q_PI
- / (dial->maximum - dial->minimum)) / 6;
+ a = (startAngle - (currentSliderPosition - dial->minimum) * spanAngle
+ / (dial->maximum - dial->minimum));
int xc = width / 2;
int yc = height / 2;
@@ -3865,6 +3874,7 @@ QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex
break;
case SC_SpinBoxFrame:
ret = spinbox->rect;
+ break;
default:
break;
}
@@ -4474,7 +4484,6 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt) const
QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &csz) const
{
- Q_D(const QCommonStyle);
QSize sz(!csz.isEmpty() ? csz : QSize(0,0));
switch (ct) {
@@ -4607,8 +4616,10 @@ QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, c
sz = QSize(1 + 3 * (buttonSize + 1), buttonSize);
}
break;
+#if QT_CONFIG(quick_itemview)
case CT_ItemViewItem:
if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
+ Q_D(const QCommonStyle);
QRect decorationRect, displayRect, checkRect;
d->viewItemLayout(vopt, &checkRect, &decorationRect, &displayRect, true);
sz = (decorationRect|displayRect|checkRect).size();
@@ -4616,6 +4627,7 @@ QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, c
sz.rheight() += 2; // Prevent icons from overlapping.
}
break;
+#endif // QT_CONFIG(quick_itemview)
case CT_SpinBox:
if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
// Add button + frame widths
@@ -4973,9 +4985,11 @@ int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, QStyleHintRet
case SH_Splitter_OpaqueResize:
ret = true;
break;
+#if QT_CONFIG(quick_itemview)
case SH_ItemView_ScrollMode:
ret = QStyleOptionViewItem::ScrollPerItem;
break;
+#endif // QT_CONFIG(quick_itemview)
case SH_TitleBar_ShowToolTipsOnButtons:
ret = true;
break;
@@ -5228,8 +5242,8 @@ QPixmap QCommonStyle::standardPixmap(StandardPixmap sp, const QStyleOption *opti
case SP_ToolBarHorizontalExtensionButton:
if (rtl) {
QImage im(tb_extension_arrow_h_xpm);
- im = im.convertToFormat(QImage::Format_ARGB32).mirrored(true, false);
- return QPixmap::fromImage(im);
+ im = std::move(im).convertToFormat(QImage::Format_ARGB32).mirrored(true, false);
+ return QPixmap::fromImage(std::move(im));
}
return cachedPixmapFromXPM(tb_extension_arrow_h_xpm);
case SP_ToolBarVerticalExtensionButton:
@@ -6024,7 +6038,7 @@ QPixmap QCommonStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &p
}
}
- return QPixmap::fromImage(im);
+ return QPixmap::fromImage(std::move(im));
}
case QIcon::Selected: {
QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
@@ -6034,7 +6048,7 @@ QPixmap QCommonStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &p
painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
painter.fillRect(0, 0, img.width(), img.height(), color);
painter.end();
- return QPixmap::fromImage(img); }
+ return QPixmap::fromImage(std::move(img)); }
case QIcon::Active:
return pixmap;
default:
diff --git a/src/quicknativestyle/qstyle/qquickcommonstyle_p.h b/src/quicknativestyle/qstyle/qquickcommonstyle_p.h
index 83ed85e51d..b597e478cf 100644
--- a/src/quicknativestyle/qstyle/qquickcommonstyle_p.h
+++ b/src/quicknativestyle/qstyle/qquickcommonstyle_p.h
@@ -30,15 +30,18 @@ class QCommonStylePrivate : public QStylePrivate
Q_DECLARE_PUBLIC(QCommonStyle)
public:
+#if QT_CONFIG(quick_itemview)
~QCommonStylePrivate()
{
delete cachedOption;
}
+#endif // QT_CONFIG(quick_itemview)
QString calculateElidedText(const QString &text, const QTextOption &textOption,
const QFont &font, const QRect &textRect, const Qt::Alignment valign,
Qt::TextElideMode textElideMode, int flags,
bool lastVisibleLineShouldBeElided, QPointF *paintStartPosition) const;
+#if QT_CONFIG(quick_itemview)
void viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect) const;
void viewItemLayout(const QStyleOptionViewItem *opt, QRect *checkRect,
QRect *pixmapRect, QRect *textRect, bool sizehint) const;
@@ -62,6 +65,7 @@ public:
&& option.font == cachedOption->font
&& option.viewItemPosition == cachedOption->viewItemPosition;
}
+#endif // QT_CONFIG(quick_itemview)
QString toolButtonElideText(const QStyleOptionToolButton *toolbutton,
const QRect &textRect, int flags) const;
diff --git a/src/quicknativestyle/qstyle/qquickstylehelper.cpp b/src/quicknativestyle/qstyle/qquickstylehelper.cpp
index dab6b041d8..6b73a5fcf3 100644
--- a/src/quicknativestyle/qstyle/qquickstylehelper.cpp
+++ b/src/quicknativestyle/qstyle/qquickstylehelper.cpp
@@ -121,14 +121,13 @@ static QPointF calcRadialPos(const QStyleOptionSlider *dial, qreal offset)
const int r = qMin(width, height) / 2;
const int currentSliderPosition = dial->upsideDown ? dial->sliderPosition : (dial->maximum - dial->sliderPosition);
qreal a = 0;
+ qreal startAngle = (90. - dial->startAngle) * Q_PI / 180.;
+ qreal spanAngle = (dial->endAngle - dial->startAngle) * Q_PI / 180.;
if (dial->maximum == dial->minimum)
a = Q_PI / 2;
- else if (dial->dialWrapping)
- a = Q_PI * 3 / 2 - (currentSliderPosition - dial->minimum) * 2 * Q_PI
- / (dial->maximum - dial->minimum);
else
- a = (Q_PI * 8 - (currentSliderPosition - dial->minimum) * 10 * Q_PI
- / (dial->maximum - dial->minimum)) / 6;
+ a = (startAngle - (currentSliderPosition - dial->minimum) * spanAngle
+ / (dial->maximum - dial->minimum));
qreal xc = width / 2.0;
qreal yc = height / 2.0;
qreal len = r - QStyleHelper::calcBigLineSize(r) - 3;
diff --git a/src/quicknativestyle/qstyle/qquickstyleoption.cpp b/src/quicknativestyle/qstyle/qquickstyleoption.cpp
index c3f5df860a..83afa8e399 100644
--- a/src/quicknativestyle/qstyle/qquickstyleoption.cpp
+++ b/src/quicknativestyle/qstyle/qquickstyleoption.cpp
@@ -385,6 +385,7 @@ QStyleOptionTitleBar::QStyleOptionTitleBar(int versionIn)
{
}
+#if QT_CONFIG(quick_itemview)
/*!
Constructs a QStyleOptionViewItem, initializing the members
variables to their default values.
@@ -406,6 +407,7 @@ QStyleOptionViewItem::QStyleOptionViewItem(int versionIn)
checkState(Qt::Unchecked), viewItemPosition(QStyleOptionViewItem::Invalid)
{
}
+#endif // QT_CONFIG(quick_itemview)
/*!
Constructs a QStyleOptionTabWidgetFrame, initializing the members
diff --git a/src/quicknativestyle/qstyle/qquickstyleoption.h b/src/quicknativestyle/qstyle/qquickstyleoption.h
index e7770cc277..ed430baaca 100644
--- a/src/quicknativestyle/qstyle/qquickstyleoption.h
+++ b/src/quicknativestyle/qstyle/qquickstyleoption.h
@@ -9,11 +9,15 @@
#include <QtCore/qlocale.h>
#include <QtCore/qvariant.h>
#include <QtCore/qdebug.h>
-#include <QtCore/qabstractitemmodel.h>
#include <QtGui/qicon.h>
#include <QtGui/qfontmetrics.h>
+#include <QtQuick/private/qtquick-config_p.h>
+#if QT_CONFIG(quick_itemview)
+#include <QtCore/qabstractitemmodel.h>
+#endif
+
QT_BEGIN_NAMESPACE
class QQuickItem;
@@ -35,7 +39,6 @@ public:
SO_CustomBase = 0xf00,
SO_ComplexCustomBase = 0xf000000
};
- Q_ENUMS(OptionType)
enum StyleOptionType { Type = SO_Default };
enum StyleOptionVersion { Version = 1 };
@@ -376,6 +379,7 @@ protected:
Q_DECL_DEPRECATED typedef QStyleOptionDockWidget QStyleOptionDockWidgetV2;
+#if QT_CONFIG(quick_itemview)
class QStyleOptionViewItem : public QStyleOption
{
public:
@@ -429,6 +433,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionViewItem::ViewItemFeatures)
Q_DECL_DEPRECATED typedef QStyleOptionViewItem QStyleOptionViewItemV2;
Q_DECL_DEPRECATED typedef QStyleOptionViewItem QStyleOptionViewItemV3;
Q_DECL_DEPRECATED typedef QStyleOptionViewItem QStyleOptionViewItemV4;
+#endif // QT_CONFIG(quick_itemview)
class QStyleOptionToolBox : public QStyleOption
{
@@ -514,6 +519,8 @@ public:
int pageStep;
qreal notchTarget;
bool dialWrapping;
+ qreal startAngle;
+ qreal endAngle;
QStyleOptionSlider();
QStyleOptionSlider(const QStyleOptionSlider &other) : QStyleOptionComplex(Version, Type) { *this = other; }
diff --git a/src/quicknativestyle/qstyle/windows/qquickwindowsstyle.cpp b/src/quicknativestyle/qstyle/windows/qquickwindowsstyle.cpp
index 73b2692241..64528c8fb7 100644
--- a/src/quicknativestyle/qstyle/windows/qquickwindowsstyle.cpp
+++ b/src/quicknativestyle/qstyle/windows/qquickwindowsstyle.cpp
@@ -2292,7 +2292,7 @@ void QWindowsStyle::refreshPalette()
QPalette pal;
using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()))
- nativeWindowsApp->lightSystemPalette(pal);
+ nativeWindowsApp->populateLightSystemPalette(pal);
QQuickTheme::instance()->setPalette(QQuickTheme::System, pal);
}
diff --git a/src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp b/src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp
index 4a729b339f..a6b8807ed0 100644
--- a/src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp
+++ b/src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp
@@ -760,7 +760,7 @@ bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeDa
#ifdef DEBUG_XP_STYLE
char buf[25];
- ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h);
+ ::snprintf(buf, sizeof(buf), "+ Pixmap(%3d, %3d) ]", w, h);
printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n",
haveCachedPixmap ? buf : "]-------------------",
qPrintable(themeData.name), themeData.partId, themeData.stateId);
@@ -3879,22 +3879,24 @@ void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h)
static int pCount = 0;
DWORD *bufPix = (DWORD*)bufferPixels;
- char *bufferDump = new char[bufferH * bufferW * 16];
+ const unsigned int bufferSize = bufferH * bufferW * 16;
+ char *bufferDump = new char[bufferSize];
+ char *bufferEndAdress = bufferDump + bufferSize;
char *bufferPos = bufferDump;
memset(bufferDump, 0, sizeof(bufferDump));
- bufferPos += sprintf(bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w);
- bufferPos += sprintf(bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h);
- bufferPos += sprintf(bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount);
+ bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w);
+ bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h);
+ bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount);
for (int iy = 0; iy < h; ++iy) {
- bufferPos += sprintf(bufferPos, "\n ");
+ bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "\n ");
bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4));
for (int ix = 0; ix < w; ++ix) {
- bufferPos += sprintf(bufferPos, "0x%08x, ", *bufPix);
+ bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "0x%08x, ", *bufPix);
++bufPix;
}
}
- bufferPos += sprintf(bufferPos, "\n};\n\n");
+ bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "\n};\n\n");
printf(bufferDump);
delete[] bufferDump;
diff --git a/src/quicknativestyle/qtquickcontrols2nativestyleplugin.cpp b/src/quicknativestyle/qtquickcontrols2nativestyleplugin.cpp
index e0b0e77bf3..f1ff18c74f 100644
--- a/src/quicknativestyle/qtquickcontrols2nativestyleplugin.cpp
+++ b/src/quicknativestyle/qtquickcontrols2nativestyleplugin.cpp
@@ -13,10 +13,11 @@
#include "qquickcommonstyle.h"
#if defined(Q_OS_MACOS)
-#include "qquickmacstyle_mac_p.h"
#include "qquickmacfocusframe.h"
+#include "qquickmacstyle_mac_p.h"
#elif defined(Q_OS_WINDOWS)
-# include "qquickwindowsxpstyle_p.h"
+#include "qquickwindowsfocusframe.h"
+#include "qquickwindowsxpstyle_p.h"
#endif
QT_BEGIN_NAMESPACE
@@ -39,8 +40,8 @@ public:
void initializeTheme(QQuickTheme *theme) override;
QString name() const override;
-#if defined(Q_OS_MACOS)
- QScopedPointer<QQuickMacFocusFrame> m_focusFrame;
+#if defined(Q_OS_MACOS) || defined (Q_OS_WIN)
+ QScopedPointer<QQuickFocusFrame> m_focusFrame;
#endif
};
@@ -111,7 +112,7 @@ void QtQuickControls2NativeStylePlugin::initializeEngine(QQmlEngine *engine, con
style = new QMacStyle;
#elif defined(Q_OS_WINDOWS)
style = new QWindowsXPStyle;
- if (QGuiApplication::styleHints()->appearance() == Qt::Appearance::Dark)
+ if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark)
qobject_cast<QWindowsStyle *>(style)->refreshPalette();
#else
style = new QCommonStyle;
@@ -121,6 +122,8 @@ void QtQuickControls2NativeStylePlugin::initializeEngine(QQmlEngine *engine, con
#if defined(Q_OS_MACOS)
m_focusFrame.reset(new QQuickMacFocusFrame());
+#elif defined(Q_OS_WIN)
+ m_focusFrame.reset(new QQuickWindowsFocusFrame());
#endif
qAddPostRoutine(deleteQStyle);
diff --git a/src/quicknativestyle/util/FocusFrame.qml b/src/quicknativestyle/util/FocusFrame.qml
deleted file mode 100644
index eebee62c31..0000000000
--- a/src/quicknativestyle/util/FocusFrame.qml
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-
-Item {
- id: root
-
- // It's important that this item has a zero size. Otherwise, if the parent of the
- // targetItem is e.g a layout, we will change the layout if we parent this item inside it.
- width: 0
- height: 0
- // Stack on top of all siblings of the targetItem
- z: 100
-
- function moveToItem(item, margins, radius) {
- if (!item) {
- targetItem = null;
- parent = null;
- visible = false;
- return;
- }
- visible = true
- parent = item.parent
- targetItem = item
- leftOffset = margins.left
- rightOffset = margins.right
- topOffset = margins.top
- bottomOffset = margins.bottom
- frameRadius = radius
- animation.restart()
- }
-
- property Item targetItem
- property real leftOffset: 0
- property real rightOffset: 0
- property real topOffset: 0
- property real bottomOffset: 0
- property real frameOpacity: 0
- property real frameSize: 0
- property real frameRadius: 0
-
- // systemFrameColor is set to NSColor.keyboardFocusIndicatorColor from cpp
- property color systemFrameColor
-
- Rectangle {
- id: focusFrame
- z: 10
- x: targetItem ? targetItem.x + leftOffset - frameSize - root.x : 0
- y: targetItem ? targetItem.y + topOffset - frameSize - root.y : 0
- width: targetItem ? targetItem.width - leftOffset - rightOffset + (frameSize * 2) : 0
- height: targetItem ? targetItem.height - topOffset - bottomOffset + (frameSize * 2) : 0
- radius: frameRadius
- visible: targetItem && targetItem.visible
- color: "transparent"
-
- border.color: systemFrameColor
- border.width: frameSize
- }
-
- ParallelAnimation {
- id: animation
- NumberAnimation {
- target: root
- property: "frameSize"
- duration: 300
- from: 15
- to: 2.5
- easing.type: Easing.OutCubic
- }
- NumberAnimation {
- target: focusFrame
- property: "opacity"
- duration: 300
- from: 0
- to: 0.55
- easing.type: Easing.OutCubic
- }
- }
-}
diff --git a/src/quicknativestyle/util/MacFocusFrame.qml b/src/quicknativestyle/util/MacFocusFrame.qml
new file mode 100644
index 0000000000..c281ff9b53
--- /dev/null
+++ b/src/quicknativestyle/util/MacFocusFrame.qml
@@ -0,0 +1,82 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Item {
+ id: root
+
+ // It's important that this item has a zero size. Otherwise, if the parent of the
+ // targetItem is e.g a layout, we will change the layout if we parent this item inside it.
+ width: 0
+ height: 0
+ // Stack on top of all siblings of the targetItem
+ z: 100
+
+ function moveToItem(item, margins, radius) {
+ if (!item) {
+ targetItem = null;
+ parent = null;
+ visible = false;
+ return;
+ }
+ visible = true
+ parent = item.parent
+ targetItem = item
+ leftOffset = margins.left
+ rightOffset = margins.right
+ topOffset = margins.top
+ bottomOffset = margins.bottom
+ frameRadius = radius
+ animation.restart()
+ }
+
+ property Item targetItem
+ property real leftOffset: 0
+ property real rightOffset: 0
+ property real topOffset: 0
+ property real bottomOffset: 0
+ property real frameOpacity: 0
+ property real frameSize: 0
+ property real frameRadius: 0
+
+ // systemFrameColor is set to NSColor.keyboardFocusIndicatorColor from cpp
+ property color systemFrameColor
+
+ Rectangle {
+ id: focusFrame
+ z: 10
+ x: targetItem ? targetItem.x + leftOffset - frameSize - root.x : 0
+ y: targetItem ? targetItem.y + topOffset - frameSize - root.y : 0
+ width: targetItem ? targetItem.width - leftOffset - rightOffset + (frameSize * 2) : 0
+ height: targetItem ? targetItem.height - topOffset - bottomOffset + (frameSize * 2) : 0
+ radius: frameRadius + frameSize
+ visible: targetItem && targetItem.visible
+ color: "transparent"
+
+ border.color: systemFrameColor
+ border.width: frameSize
+ }
+
+ ParallelAnimation {
+ id: animation
+ NumberAnimation {
+ target: root
+ property: "frameSize"
+ duration: 300
+ from: 15
+ to: 3
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ target: focusFrame
+ property: "opacity"
+ duration: 300
+ from: 0
+ to: 0.55
+ easing.type: Easing.OutCubic
+ }
+ }
+}
diff --git a/src/quicknativestyle/util/WindowsFocusFrame.qml b/src/quicknativestyle/util/WindowsFocusFrame.qml
new file mode 100644
index 0000000000..0d23dacf1f
--- /dev/null
+++ b/src/quicknativestyle/util/WindowsFocusFrame.qml
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: root
+
+ // It's important that this item has a zero size. Otherwise, if the parent of the
+ // targetItem is e.g a layout, we will change the layout if we parent this item inside it.
+ width: 0
+ height: 0
+ // Stack on top of all siblings of the targetItem
+ z: 100
+
+ function moveToItem(item, margins, radius) {
+ if (!item) {
+ targetItem = null;
+ parent = null;
+ visible = false;
+ return;
+ }
+ visible = true
+ parent = item.parent
+ targetItem = item
+ leftOffset = margins.left
+ rightOffset = margins.right
+ topOffset = margins.top
+ bottomOffset = margins.bottom
+ frameRadius = radius
+ }
+
+ property Item targetItem
+ property real leftOffset: 0
+ property real rightOffset: 0
+ property real topOffset: 0
+ property real bottomOffset: 0
+ property real frameOpacity: 0
+ property real frameSize: 0
+ property real frameRadius: 0
+
+ Canvas {
+ x: targetItem ? targetItem.x + leftOffset - frameSize - root.x : 0
+ y: targetItem ? targetItem.y + topOffset - frameSize - root.y : 0
+ width: targetItem ? targetItem.width - leftOffset - rightOffset + (frameSize * 2) : 0
+ height: targetItem ? targetItem.height - topOffset - bottomOffset + (frameSize * 2) : 0
+ visible: targetItem && targetItem.visible
+
+ onPaint: {
+ let context = getContext("2d")
+ context.strokeStyle = Qt.rgba(0, 0, 0, 1)
+ context.setLineDash([1, 1])
+ context.beginPath()
+ context.roundedRect(0.5, 0.5, width - 1, height - 1, root.frameRadius, root.frameRadius)
+ context.stroke()
+ }
+ }
+}
diff --git a/src/quicknativestyle/util/qquickfocusframe.cpp b/src/quicknativestyle/util/qquickfocusframe.cpp
new file mode 100644
index 0000000000..bf1ee5a3ea
--- /dev/null
+++ b/src/quicknativestyle/util/qquickfocusframe.cpp
@@ -0,0 +1,118 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickfocusframe.h"
+
+#include <QtCore/qmetaobject.h>
+
+#include <QtGui/qguiapplication.h>
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlcomponent.h>
+
+#include <QtQuick/qquickitem.h>
+
+#include "items/qquickstyleitem.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcFocusFrame, "qt.quick.controls.focusframe")
+
+QQuickFocusFrameDescription QQuickFocusFrameDescription::Invalid = { nullptr, QQuickStyleMargins(), 0 };
+QScopedPointer<QQuickItem> QQuickFocusFrame::m_focusFrame;
+
+QQuickFocusFrame::QQuickFocusFrame()
+{
+ connect(qGuiApp, &QGuiApplication::focusObjectChanged, this, [this]{
+ if (auto item = qobject_cast<QQuickItem *>(qGuiApp->focusObject()))
+ moveToItem(item);
+ });
+}
+
+void QQuickFocusFrame::moveToItem(QQuickItem *item)
+{
+ if (!m_focusFrame) {
+ const auto context = QQmlEngine::contextForObject(item);
+ // In certain cases like QQuickWebEngineView, the item
+ // gets focus even though it has no QQmlEngine associated with its context.
+ // We need the engine for creating the focus frame component.
+ if (!context || !context->engine())
+ return;
+ m_focusFrame.reset(createFocusFrame(context));
+ if (!m_focusFrame) {
+ qWarning() << "Failed to create FocusFrame";
+ return;
+ }
+ }
+
+ const QQuickFocusFrameDescription &config = getDescriptionForItem(item);
+ QMetaObject::invokeMethod(m_focusFrame.data(), "moveToItem",
+ Q_ARG(QVariant, QVariant::fromValue(config.target)),
+ Q_ARG(QVariant, QVariant::fromValue(config.margins)),
+ Q_ARG(QVariant, QVariant::fromValue(config.radius)));
+}
+
+QQuickFocusFrameDescription QQuickFocusFrame::getDescriptionForItem(QQuickItem *focusItem) const
+{
+ qCDebug(lcFocusFrame) << "new focusobject:" << focusItem;
+ const auto parentItem = focusItem->parentItem();
+ if (!parentItem)
+ return QQuickFocusFrameDescription::Invalid;
+
+ // The item that gets active focus can be a child of the control (e.g
+ // editable ComboBox). In that case, resolve the actual control first.
+ const auto proxy = focusItem->property("__focusFrameControl").value<QQuickItem *>();
+ const auto control = proxy ? proxy : focusItem;
+ auto target = control->property("__focusFrameTarget").value<QQuickItem *>();
+ qCDebug(lcFocusFrame) << "target:" << target;
+ qCDebug(lcFocusFrame) << "control:" << control;
+
+ if (!target) {
+ // __focusFrameTarget points to the item in the control that should
+ // get the focus frame. This is usually the control itself, but can
+ // sometimes be a child (CheckBox). We anyway require
+ // this property to be set if we are to show the focus frame around
+ // the control in the first place. So for controls that don't want
+ // a frame (ProgressBar), we simply skip setting it.
+ // Also, we should never show a focus frame around custom controls.
+ // None of the built-in styles do that, so to be consistent, we
+ // shouldn't either. Besides, drawing a focus frame around an unknown
+ // item without any way to turn it off can easily be unwanted. A better
+ // way for custom controls to get a native focus frame is for us to offer
+ // a FocusFrame control (QTBUG-86818).
+ return QQuickFocusFrameDescription::Invalid;
+ }
+
+ // If the control gives us a QQuickStyleItem, we use that to configure the focus frame.
+ // By default we assume that the background delegate is a QQuickStyleItem, but the
+ // control can override this by setting __focusFrameStyleItem.
+ const auto styleItemProperty = control->property("__focusFrameStyleItem");
+ auto item = styleItemProperty.value<QQuickItem *>();
+ if (!item) {
+ const auto styleItemProperty = control->property("background");
+ item = styleItemProperty.value<QQuickItem *>();
+ }
+ qCDebug(lcFocusFrame) << "styleItem:" << item;
+ if (!item)
+ return QQuickFocusFrameDescription::Invalid;
+ if (QQuickStyleItem *styleItem = qobject_cast<QQuickStyleItem *>(item))
+ return { target, QQuickStyleMargins(styleItem->layoutMargins()), styleItem->focusFrameRadius() };
+
+ // Some controls don't have a QQuickStyleItem. But if the __focusFrameStyleItem
+ // has a "__focusFrameRadius" property set, we show a default focus frame using the specified radius instead.
+ const QVariant focusFrameRadiusVariant = item->property("__focusFrameRadius");
+ if (focusFrameRadiusVariant.isValid()) {
+ qCDebug(lcFocusFrame) << "'focusFrameRadius' property found, showing a default focus frame";
+ const QStyleOption opt;
+ const qreal radius = qMax(0.0, focusFrameRadiusVariant.toReal());
+ return { target, QQuickStyleMargins(), radius };
+ }
+
+ // The application has set a custom delegate on the control. In that
+ // case, it's the delegates responsibility to draw a focus frame.
+ qCDebug(lcFocusFrame) << "custom delegates in use, skip showing focus frame";
+ return QQuickFocusFrameDescription::Invalid;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quicknativestyle/util/qquickfocusframe.h b/src/quicknativestyle/util/qquickfocusframe.h
new file mode 100644
index 0000000000..2121d6bdc5
--- /dev/null
+++ b/src/quicknativestyle/util/qquickfocusframe.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKFOCUSFRAME_H
+#define QQUICKFOCUSFRAME_H
+
+#include <QtQuick/qquickitem.h>
+#include "qquickstyleitem.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcFocusFrame)
+
+struct QQuickFocusFrameDescription {
+ QQuickItem *target;
+ QQuickStyleMargins margins;
+ const qreal radius = 3;
+ bool isValid() const { return target != nullptr; }
+ static QQuickFocusFrameDescription Invalid;
+};
+
+class QQuickFocusFrame : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickFocusFrame();
+
+private:
+ static QScopedPointer<QQuickItem> m_focusFrame;
+
+ virtual QQuickItem *createFocusFrame(QQmlContext *context) = 0;
+ void moveToItem(QQuickItem *item);
+ QQuickFocusFrameDescription getDescriptionForItem(QQuickItem *focusItem) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFOCUSFRAME_H
diff --git a/src/quicknativestyle/util/qquickmacfocusframe.h b/src/quicknativestyle/util/qquickmacfocusframe.h
index 88858c7688..e52cc9a300 100644
--- a/src/quicknativestyle/util/qquickmacfocusframe.h
+++ b/src/quicknativestyle/util/qquickmacfocusframe.h
@@ -1,38 +1,22 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKMACFOCUSFRAME_H
#define QQUICKMACFOCUSFRAME_H
-#include <QtQuick/qquickitem.h>
-#include <QtQuick/private/qquicktextedit_p.h>
-#include "qquickstyleitem.h"
+#include "qquickfocusframe.h"
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcFocusFrame)
-
-struct QQuickFocusFrameDescription {
- QQuickItem *target;
- QQuickStyleMargins margins;
- const qreal radius = 3;
- bool isValid() const { return target != nullptr; }
- static QQuickFocusFrameDescription Invalid;
-};
-
-class QQuickMacFocusFrame : public QObject
+class QQuickMacFocusFrame : public QQuickFocusFrame
{
Q_OBJECT
public:
- QQuickMacFocusFrame();
+ QQuickMacFocusFrame() = default;
private:
- static QScopedPointer<QQuickItem> m_focusFrame;
-
- void createFocusFrame(QQmlContext *context);
- void moveToItem(QQuickItem *item);
- QQuickFocusFrameDescription getDescriptionForItem(QQuickItem *focusItem) const;
+ virtual QQuickItem *createFocusFrame(QQmlContext *context) override;
};
QT_END_NAMESPACE
diff --git a/src/quicknativestyle/util/qquickmacfocusframe.mm b/src/quicknativestyle/util/qquickmacfocusframe.mm
index 8ebaf223be..c47d3e1b0a 100644
--- a/src/quicknativestyle/util/qquickmacfocusframe.mm
+++ b/src/quicknativestyle/util/qquickmacfocusframe.mm
@@ -1,141 +1,31 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickmacfocusframe.h"
#include <AppKit/AppKit.h>
-#include <QtCore/qmetaobject.h>
-
#include <QtGui/qguiapplication.h>
#include <QtGui/private/qcoregraphics_p.h>
-#include <QtQml/qqmlengine.h>
-#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlcomponent.h>
-#include <QtQuick/qquickitem.h>
-#include <QtQuick/private/qquicktextinput_p.h>
-#include <QtQuick/private/qquicktextedit_p.h>
-#include <QtQuick/private/qquickflickable_p.h>
-
-#include <QtQuickTemplates2/private/qquickframe_p.h>
-#include <QtQuickTemplates2/private/qquickbutton_p.h>
-#include <QtQuickTemplates2/private/qquickscrollview_p.h>
-#include <QtQuickTemplates2/private/qquickslider_p.h>
-#include <QtQuickTemplates2/private/qquickcombobox_p.h>
-#include <QtQuickTemplates2/private/qquickcheckbox_p.h>
-#include <QtQuickTemplates2/private/qquickradiobutton_p.h>
-#include <QtQuickTemplates2/private/qquickspinbox_p.h>
-#include <QtQuickTemplates2/private/qquicktextfield_p.h>
-#include <QtQuickTemplates2/private/qquicktextarea_p.h>
-
-#include "items/qquickstyleitem.h"
-#include "qquicknativestyle.h"
-
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcFocusFrame, "qt.quick.controls.focusframe")
-
-QQuickFocusFrameDescription QQuickFocusFrameDescription::Invalid = { nullptr, QQuickStyleMargins(), 0 };
-QScopedPointer<QQuickItem> QQuickMacFocusFrame::m_focusFrame;
-
-QQuickMacFocusFrame::QQuickMacFocusFrame()
-{
- connect(qGuiApp, &QGuiApplication::focusObjectChanged, [=]{
- if (auto item = qobject_cast<QQuickItem *>(qGuiApp->focusObject()))
- moveToItem(item);
- });
-}
-
-void QQuickMacFocusFrame::moveToItem(QQuickItem *item)
-{
- if (!m_focusFrame) {
- const auto context = QQmlEngine::contextForObject(item);
- if (!context)
- return;
- createFocusFrame(context);
- }
-
- const QQuickFocusFrameDescription &config = getDescriptionForItem(item);
- QMetaObject::invokeMethod(m_focusFrame.data(), "moveToItem",
- Q_ARG(QVariant, QVariant::fromValue(config.target)),
- Q_ARG(QVariant, QVariant::fromValue(config.margins)),
- Q_ARG(QVariant, QVariant::fromValue(config.radius)));
-}
-
-void QQuickMacFocusFrame::createFocusFrame(QQmlContext *context)
+QQuickItem *QQuickMacFocusFrame::createFocusFrame(QQmlContext *context)
{
QQmlComponent component(
context->engine(),
QUrl(QStringLiteral(
- "qrc:/qt-project.org/imports/QtQuick/NativeStyle/util/FocusFrame.qml")));
- m_focusFrame.reset(qobject_cast<QQuickItem *>(component.create()));
+ "qrc:/qt-project.org/imports/QtQuick/NativeStyle/util/MacFocusFrame.qml")));
+ auto frame = qobject_cast<QQuickItem *>(component.create());
+ if (!frame)
+ return nullptr;
auto indicatorColor = qt_mac_toQColor(NSColor.keyboardFocusIndicatorColor.CGColor);
indicatorColor.setAlpha(255);
- m_focusFrame->setProperty("systemFrameColor", indicatorColor);
-}
-
-QQuickFocusFrameDescription QQuickMacFocusFrame::getDescriptionForItem(QQuickItem *focusItem) const
-{
- qCDebug(lcFocusFrame) << "new focusobject:" << focusItem;
- const auto parentItem = focusItem->parentItem();
- if (!parentItem)
- return QQuickFocusFrameDescription::Invalid;
-
- // The item that gets active focus can be a child of the control (e.g
- // editable ComboBox). In that case, resolve the actual control first.
- const auto proxy = focusItem->property("__focusFrameControl").value<QQuickItem *>();
- const auto control = proxy ? proxy : focusItem;
- auto target = control->property("__focusFrameTarget").value<QQuickItem *>();
- qCDebug(lcFocusFrame) << "target:" << target;
- qCDebug(lcFocusFrame) << "control:" << control;
-
- if (!target) {
- // __focusFrameTarget points to the item in the control that should
- // get the focus frame. This is usually the control itself, but can
- // sometimes be a child (CheckBox). We anyway require
- // this property to be set if we are to show the focus frame around
- // the control in the first place. So for controls that don't want
- // a frame (ProgressBar), we simply skip setting it.
- // Also, we should never show a focus frame around custom controls.
- // None of the built-in styles do that, so to be consistent, we
- // shouldn't either. Besides, drawing a focus frame around an unknown
- // item without any way to turn it off can easily be unwanted. A better
- // way for custom controls to get a native focus frame is for us to offer
- // a FocusFrame control (QTBUG-86818).
- return QQuickFocusFrameDescription::Invalid;
- }
-
- // If the control gives us a QQuickStyleItem, we use that to configure the focus frame.
- // By default we assume that the background delegate is a QQuickStyleItem, but the
- // control can override this by setting __focusFrameStyleItem.
- const auto styleItemProperty = control->property("__focusFrameStyleItem");
- auto item = styleItemProperty.value<QQuickItem *>();
- if (!item) {
- const auto styleItemProperty = control->property("background");
- item = styleItemProperty.value<QQuickItem *>();
- }
- qCDebug(lcFocusFrame) << "styleItem:" << item;
- if (!item)
- return QQuickFocusFrameDescription::Invalid;
- if (QQuickStyleItem *styleItem = qobject_cast<QQuickStyleItem *>(item))
- return { target, QQuickStyleMargins(styleItem->layoutMargins()), styleItem->focusFrameRadius() };
-
- // Some controls don't have a QQuickStyleItem. But if the __focusFrameStyleItem
- // has a "__isDefaultDelegate" property set, we show a default focus frame instead.
- if (item->property("__isDefaultDelegate").toBool() == true) {
- qCDebug(lcFocusFrame) << "'__isDefaultDelegate' property found, showing a default focus frame";
- const QStyleOption opt;
- const qreal radius = QQuickNativeStyle::style()->pixelMetric(QStyle::PM_TextFieldFocusFrameRadius, &opt);
- return { target, QQuickStyleMargins(), radius };
- }
-
- // The application has set a custom delegate on the control. In that
- // case, it's the delegates responsibility to draw a focus frame.
- qCDebug(lcFocusFrame) << "custom delegates in use, skip showing focus frame";
- return QQuickFocusFrameDescription::Invalid;
+ frame->setProperty("systemFrameColor", indicatorColor);
+ return frame;
}
QT_END_NAMESPACE
diff --git a/src/quicknativestyle/util/qquickwindowsfocusframe.cpp b/src/quicknativestyle/util/qquickwindowsfocusframe.cpp
new file mode 100644
index 0000000000..a92af42e21
--- /dev/null
+++ b/src/quicknativestyle/util/qquickwindowsfocusframe.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickwindowsfocusframe.h"
+
+#include <QtQml/qqmlcomponent.h>
+
+QT_BEGIN_NAMESPACE
+
+QQuickItem *QQuickWindowsFocusFrame::createFocusFrame(QQmlContext *context)
+{
+ QQmlComponent component(context->engine(),
+ QUrl(QStringLiteral("qrc:/qt-project.org/imports/QtQuick/NativeStyle/util/WindowsFocusFrame.qml")));
+ return qobject_cast<QQuickItem *>(component.create());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quicknativestyle/util/qquickwindowsfocusframe.h b/src/quicknativestyle/util/qquickwindowsfocusframe.h
new file mode 100644
index 0000000000..5bd60a1343
--- /dev/null
+++ b/src/quicknativestyle/util/qquickwindowsfocusframe.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKWINDOWSFOCUSFRAME_H
+#define QQUICKWINDOWSFOCUSFRAME_H
+
+#include "qquickfocusframe.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickWindowsFocusFrame : public QQuickFocusFrame
+{
+ Q_OBJECT
+
+public:
+ QQuickWindowsFocusFrame() = default;
+
+private:
+ virtual QQuickItem *createFocusFrame(QQmlContext *context) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKWINDOWSFOCUSFRAME_H
diff --git a/src/quickshapes/CMakeLists.txt b/src/quickshapes/CMakeLists.txt
index ba3155e5d9..7b52826751 100644
--- a/src/quickshapes/CMakeLists.txt
+++ b/src/quickshapes/CMakeLists.txt
@@ -20,6 +20,7 @@ qt_internal_add_qml_module(QuickShapesPrivate
qquickshape_p_p.h
qquickshapegenericrenderer.cpp qquickshapegenericrenderer_p.h
qquickshapesglobal.h qquickshapesglobal_p.h
+ qquickshapecurverenderer.cpp qquickshapecurverenderer_p.h qquickshapecurverenderer_p_p.h
qquickshapesoftwarerenderer.cpp qquickshapesoftwarerenderer_p.h
PUBLIC_LIBRARIES
Qt::Core
@@ -27,8 +28,7 @@ qt_internal_add_qml_module(QuickShapesPrivate
Qt::Qml
Qt::QuickPrivate
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
# We need to do additional initialization, so we have to provide our own
# plugin class rather than using the generated one
@@ -37,20 +37,22 @@ qt_internal_extend_target(qmlshapesplugin
LIBRARIES Qt::QuickShapesPrivate
)
-# Resources:
-set(qtquickshapes_resource_files
- "shaders_ng/conicalgradient.frag.qsb"
- "shaders_ng/conicalgradient.vert.qsb"
- "shaders_ng/lineargradient.frag.qsb"
- "shaders_ng/lineargradient.vert.qsb"
- "shaders_ng/radialgradient.frag.qsb"
- "shaders_ng/radialgradient.vert.qsb"
-)
-
-qt_internal_add_resource(QuickShapesPrivate "qtquickshapes"
+qt_internal_add_shaders(QuickShapesPrivate "qtquickshapes_shaders"
+ SILENT
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ MULTIVIEW
PREFIX
"/qt-project.org/shapes"
FILES
- ${qtquickshapes_resource_files}
+ "shaders_ng/lineargradient.vert"
+ "shaders_ng/lineargradient.frag"
+ "shaders_ng/radialgradient.vert"
+ "shaders_ng/radialgradient.frag"
+ "shaders_ng/conicalgradient.vert"
+ "shaders_ng/conicalgradient.frag"
+ "shaders_ng/wireframe.frag"
+ "shaders_ng/wireframe.vert"
)
diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp
index 494047d8e8..96f83b9af6 100644
--- a/src/quickshapes/qquickshape.cpp
+++ b/src/quickshapes/qquickshape.cpp
@@ -5,17 +5,18 @@
#include "qquickshape_p_p.h"
#include "qquickshapegenericrenderer_p.h"
#include "qquickshapesoftwarerenderer_p.h"
+#include "qquickshapecurverenderer_p.h"
#include <private/qsgplaintexture_p.h>
#include <private/qquicksvgparser_p.h>
#include <QtGui/private/qdrawhelper_p.h>
#include <QOpenGLFunctions>
#include <QLoggingCategory>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
static void initResources()
{
#if defined(QT_STATIC)
- Q_INIT_RESOURCE(qtquickshapes);
+ Q_INIT_RESOURCE(qtquickshapes_shaders);
#endif
}
@@ -34,6 +35,35 @@ Q_LOGGING_CATEGORY(QQSHAPE_LOG_TIME_DIRTY_SYNC, "qt.shape.time.sync")
\qml
import QtQuick.Shapes
\endqml
+
+ Qt Quick Shapes provides tools for drawing arbitrary shapes in a Qt Quick scene.
+ \l{Shape}{Shapes} can be constructed from basic building blocks like \l{PathLine}{lines} and
+ \l{PathCubic}{curves} that define sub-shapes. The sub-shapes can then be filled with solid
+ colors or gradients, and an outline stroke can be defined.
+
+ Qt Quick Shapes also supports higher level path element types, such as \l{PathText}{text} and
+ \l{PathSvg}{SVG path descriptions}. The currently supported element types is: PathMove,
+ PathLine, PathQuad, PathCubic, PathArc, PathText and PathSvg.
+
+ Qt Quick Shapes triangulates the shapes and renders the corresponding triangles on the GPU.
+ Therefore, altering the control points of elements will lead to re-triangulation of the
+ affected paths, at some performance cost. In addition, curves are flattened before they are
+ rendered, so applying a very high scale to the shape may show artifacts where it is visible
+ that the curves are represented by a sequence of smaller, straight lines.
+
+ \note By default, Qt Quick Shapes relies on multi-sampling for anti-aliasing. This can be
+ enabled for the entire application or window using the corresponding settings in QSurfaceFormat.
+ It can also be enabled for only the shape, by setting its \l{Item::layer.enabled}{layer.enabled}
+ property to true and then adjusting the \l{Item::layer.samples}{layer.samples} property. In the
+ latter case, multi-sampling will not be applied to the entire scene, but the shape will be
+ rendered via an intermediate off-screen buffer. Alternatively, the
+ \l{QtQuick.Shapes::Shape::preferredRendererType}{preferredRendererType} property can be set
+ to \c{Shape.CurveRenderer}. This has anti-aliasing built in and generally renders the shapes
+ at a higher quality, but at some additional performance cost.
+
+ For further information, the \l{Qt Quick Examples - Shapes}{Shapes example} shows how to
+ implement different types of shapes, fills and strokes, and the \l{Weather Forecast Example}
+ shows examples of different ways shapes might be useful in a user interface.
*/
void QQuickShapes_initializeModule()
@@ -119,7 +149,7 @@ QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams()
\image visualpath-code-example.png
- \sa {Qt Quick Examples - Shapes}, Shape
+ \sa {Qt Quick Examples - Shapes}, {Weather Forecast Example}, Shape
*/
QQuickShapePathPrivate::QQuickShapePathPrivate()
@@ -360,10 +390,8 @@ void QQuickShapePath::setCapStyle(CapStyle style)
This property defines the style of stroking. The default value is
ShapePath.SolidLine.
- \list
- \li ShapePath.SolidLine - A plain line.
- \li ShapePath.DashLine - Dashes separated by a few pixels.
- \endlist
+ \value ShapePath.SolidLine A plain line.
+ \value ShapePath.DashLine Dashes separated by a few pixels.
*/
QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const
@@ -491,6 +519,57 @@ void QQuickShapePath::resetFillGradient()
}
/*!
+ \qmlproperty PathHints QtQuick.Shapes::ShapePath::pathHints
+ \since 6.7
+
+ This property describes characteristics of the shape. If set, these hints may allow
+ optimized rendering. By default, no hints are set. It can be a combination of the following
+ values:
+
+ \value ShapePath.PathLinear
+ The path only has straight lines, no curves.
+ \value ShapePath.PathQuadratic
+ The path does not have any cubic curves: only lines and quadratic Bezier curves.
+ \value ShapePath.PathConvex
+ The path does not have any dents or holes. All straight lines between two points
+ inside the shape will be completely inside the shape.
+ \value ShapePath.PathFillOnRight
+ The path follows the TrueType convention where outlines around solid fill have their
+ control points ordered clockwise, and outlines around holes in the shape have their
+ control points ordered counter-clockwise.
+ \value ShapePath.PathSolid
+ The path has no holes, or mathematically speaking it is \e{simply connected}.
+ \value ShapePath.PathNonIntersecting
+ The path outline does not cross itself.
+ \value ShapePath.PathNonOverlappingControlPointTriangles
+ The triangles defined by the curve control points do not overlap with each other,
+ or with any of the line segments. Also, no line segments intersect.
+ This implies \c PathNonIntersecting.
+
+ Not all hints are logically independent, but the dependencies are not enforced.
+ For example, \c PathIsLinear implies \c PathIsQuadratic, but it is valid to have \c PathIsLinear
+ without \c PathIsQuadratic.
+
+ The pathHints property describes a set of statements known to be true; the absence of a hint
+ does not necessarily mean that the corresponding statement is false.
+*/
+
+QQuickShapePath::PathHints QQuickShapePath::pathHints() const
+{
+ Q_D(const QQuickShapePath);
+ return d->pathHints;
+}
+
+void QQuickShapePath::setPathHints(PathHints newPathHints)
+{
+ Q_D(QQuickShapePath);
+ if (d->pathHints == newPathHints)
+ return;
+ d->pathHints = newPathHints;
+ emit pathHintsChanged();
+}
+
+/*!
\qmltype Shape
//! \instantiates QQuickShape
\inqmlmodule QtQuick.Shapes
@@ -514,7 +593,7 @@ void QQuickShapePath::resetFillGradient()
Shape. However, not all Shape implementations support all path
element types, while some may not make sense for PathView. Shape's
currently supported subset is: PathMove, PathLine, PathQuad, PathCubic,
- PathArc, and PathSvg.
+ PathArc, PathText and PathSvg.
See \l Path for a detailed overview of the supported path elements.
@@ -598,7 +677,7 @@ void QQuickShapePath::resetFillGradient()
\endlist
- \sa {Qt Quick Examples - Shapes}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
+ \sa {Qt Quick Examples - Shapes}, {Weather Forecast Example}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
*/
QQuickShapePrivate::QQuickShapePrivate()
@@ -616,6 +695,9 @@ void QQuickShapePrivate::_q_shapePathChanged()
Q_Q(QQuickShape);
spChanged = true;
q->polish();
+ emit q->boundingRectChanged();
+ auto br = q->boundingRect();
+ q->setImplicitSize(br.right(), br.bottom());
}
void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
@@ -627,6 +709,18 @@ void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
}
}
+qreal QQuickShapePrivate::getImplicitWidth() const
+{
+ Q_Q(const QQuickShape);
+ return q->boundingRect().right();
+}
+
+qreal QQuickShapePrivate::getImplicitHeight() const
+{
+ Q_Q(const QQuickShape);
+ return q->boundingRect().bottom();
+}
+
QQuickShape::QQuickShape(QQuickItem *parent)
: QQuickItem(*(new QQuickShapePrivate), parent)
{
@@ -639,6 +733,7 @@ QQuickShape::~QQuickShape()
/*!
\qmlproperty enumeration QtQuick.Shapes::Shape::rendererType
+ \readonly
This property determines which path rendering backend is active.
@@ -646,15 +741,33 @@ QQuickShape::~QQuickShape()
The renderer is unknown.
\value Shape.GeometryRenderer
- The generic, driver independent solution for OpenGL. Uses the same
+ The generic, driver independent solution for GPU rendering. Uses the same
CPU-based triangulation approach as QPainter's OpenGL 2 paint
- engine. This is the default when the OpenGL Qt Quick scenegraph
+ engine. This is the default when the RHI-based Qt Quick scenegraph
backend is in use.
\value Shape.SoftwareRenderer
Pure QPainter drawing using the raster paint engine. This is the
default, and only, option when the Qt Quick scenegraph is running
with the \c software backend.
+
+ \value Shape.CurveRenderer
+ GPU-based renderer that aims to preserve curvature at any scale.
+ In contrast to \c Shape.GeometryRenderer, curves are not approximated by short straight
+ lines. Instead, curves are rendered using a specialized fragment shader. This improves
+ visual quality and avoids re-tesselation performance hit when zooming. Also,
+ \c Shape.CurveRenderer provides native, high-quality anti-aliasing, without the
+ performance cost of multi- or supersampling.
+
+ By default, \c Shape.GeometryRenderer will be selected unless the Qt Quick scenegraph is running
+ with the \c software backend. In that case, \c Shape.SoftwareRenderer will be used.
+ \c Shape.CurveRenderer may be requested using the \l preferredRendererType property.
+
+ \note The \c Shape.CurveRenderer will approximate cubic curves with quadratic ones and may
+ therefore diverge slightly from the mathematically correct visualization of the shape. In
+ addition, if the shape is being rendered into a Qt Quick 3D scene and the OpenGL backend for
+ RHI is active, the \c GL_OES_standard_derivatives extension to OpenGL is required (this is
+ available by default on OpenGL ES 3 and later, but optional in OpenGL ES 2.)
*/
QQuickShape::RendererType QQuickShape::rendererType() const
@@ -664,12 +777,58 @@ QQuickShape::RendererType QQuickShape::rendererType() const
}
/*!
+ \qmlproperty enumeration QtQuick.Shapes::Shape::preferredRendererType
+ \since 6.6
+
+ Requests a specific backend to use for rendering the shape. The possible values are the same as
+ for \l rendererType. The default is \c Shape.UnknownRenderer, indicating no particular preference.
+
+ If the requested renderer type is not supported for the current Qt Quick backend, the default
+ renderer for that backend will be used instead. This will be reflected in the \l rendererType
+ when the backend is initialized.
+
+ \c Shape.SoftwareRenderer can currently not be selected without running the scenegraph with
+ the \c software backend, in which case it will be selected regardless of the
+ \c preferredRendererType.
+
+ See \l rendererType for more information on the implications.
+*/
+
+QQuickShape::RendererType QQuickShape::preferredRendererType() const
+{
+ Q_D(const QQuickShape);
+ return d->preferredType;
+}
+
+void QQuickShape::setPreferredRendererType(QQuickShape::RendererType preferredType)
+{
+ Q_D(QQuickShape);
+ if (d->preferredType == preferredType)
+ return;
+
+ d->preferredType = preferredType;
+ // (could bail out here if selectRenderType shows no change?)
+
+ for (int i = 0; i < d->sp.size(); ++i) {
+ QQuickShapePath *p = d->sp[i];
+ QQuickShapePathPrivate *pp = QQuickShapePathPrivate::get(p);
+ pp->dirty |= QQuickShapePathPrivate::DirtyAll;
+ }
+ d->spChanged = true;
+ d->_q_shapePathChanged();
+ polish();
+ update();
+
+ emit preferredRendererTypeChanged();
+}
+
+/*!
\qmlproperty bool QtQuick.Shapes::Shape::asynchronous
- When rendererType is \c Shape.GeometryRenderer, the input path is
- triangulated on the CPU during the polishing phase of the Shape. This is
- potentially expensive. To offload this work to separate worker threads,
- set this property to \c true.
+ When rendererType is \c Shape.GeometryRenderer or \c Shape.CurveRenderer, a certain amount of
+ preprocessing of the input path is performed on the CPU during the polishing phase of the
+ Shape. This is potentially expensive. To offload this work to separate worker threads, set this
+ property to \c true.
When enabled, making a Shape visible will not wait for the content to
become available. Instead, the GUI/main thread is not blocked and the
@@ -697,6 +856,26 @@ void QQuickShape::setAsynchronous(bool async)
}
/*!
+ \qmlproperty rect QtQuick.Shapes::Shape::boundingRect
+ \readonly
+ \since 6.6
+
+ Contains the united bounding rect of all sub paths in the shape.
+ */
+QRectF QQuickShape::boundingRect() const
+{
+ Q_D(const QQuickShape);
+ QRectF brect;
+ for (QQuickShapePath *path : d->sp) {
+ qreal pw = path->strokeColor().alpha() ? path->strokeWidth() : 0;
+ qreal d = path->capStyle() == QQuickShapePath::SquareCap ? pw * M_SQRT1_2 : pw / 2;
+ brect = brect.united(path->path().boundingRect().adjusted(-d, -d, d, d));
+ }
+
+ return brect;
+}
+
+/*!
\qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled
This property controls the usage of non-standard OpenGL extensions.
@@ -723,6 +902,7 @@ void QQuickShape::setVendorExtensionsEnabled(bool enable)
/*!
\qmlproperty enumeration QtQuick.Shapes::Shape::status
+ \readonly
This property determines the status of the Shape and is relevant when
Shape.asynchronous is set to \c true.
@@ -799,6 +979,80 @@ bool QQuickShape::contains(const QPointF &point) const
return false;
}
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::Shape::fillMode
+ \since QtQuick.Shapes 6.7
+
+ Set this property to define what happens when the path has a different size
+ than the item.
+
+ \value Shape.NoResize the shape is rendered at its native size, independent of the size of the item. This is the default
+ \value Shape.Stretch the shape is scaled to fit the item, changing the aspect ratio if necessary.
+ Note that non-uniform scaling may cause reduced quality of anti-aliasing when using the curve renderer
+ \value Shape.PreserveAspectFit the shape is scaled uniformly to fit inside the item
+ \value Shape.PreserveAspectCrop the shape is scaled uniformly to fill the item fully, extending outside the item if necessary.
+ Note that this only actually crops the content if \l clip is true
+*/
+
+QQuickShape::FillMode QQuickShape::fillMode() const
+{
+ Q_D(const QQuickShape);
+ return d->fillMode;
+}
+
+void QQuickShape::setFillMode(FillMode newFillMode)
+{
+ Q_D(QQuickShape);
+ if (d->fillMode == newFillMode)
+ return;
+ d->fillMode = newFillMode;
+ emit fillModeChanged();
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::Shape::horizontalAlignment
+ \qmlproperty enumeration QtQuick.Shapes::Shape::verticalAlignment
+ \since 6.7
+
+ Sets the horizontal and vertical alignment of the shape within the item.
+ By default, the shape is aligned with \c{(0,0)} on the top left corner.
+
+ The valid values for \c horizontalAlignment are \c Shape.AlignLeft,
+ \c Shape.AlignRight and \c Shape.AlignHCenter. The valid values for
+ \c verticalAlignment are \c Shape.AlignTop, \c Shape.AlignBottom and
+ \c Shape.AlignVCenter.
+*/
+
+QQuickShape::HAlignment QQuickShape::horizontalAlignment() const
+{
+ Q_D(const QQuickShape);
+ return d->horizontalAlignment;
+}
+
+void QQuickShape::setHorizontalAlignment(HAlignment newHorizontalAlignment)
+{
+ Q_D(QQuickShape);
+ if (d->horizontalAlignment == newHorizontalAlignment)
+ return;
+ d->horizontalAlignment = newHorizontalAlignment;
+ emit horizontalAlignmentChanged();
+}
+
+QQuickShape::VAlignment QQuickShape::verticalAlignment() const
+{
+ Q_D(const QQuickShape);
+ return d->verticalAlignment;
+}
+
+void QQuickShape::setVerticalAlignment(VAlignment newVerticalAlignment)
+{
+ Q_D(QQuickShape);
+ if (d->verticalAlignment == newVerticalAlignment)
+ return;
+ d->verticalAlignment = newVerticalAlignment;
+ emit verticalAlignmentChanged();
+}
+
static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
{
QQuickShape *item = static_cast<QQuickShape *>(property->object);
@@ -879,6 +1133,12 @@ void QQuickShape::updatePolish()
d->spChanged = false;
d->effectRefCount = currentEffectRefCount;
+ QQuickShape::RendererType expectedRenderer = d->selectRendererType();
+ if (d->rendererType != expectedRenderer) {
+ delete d->renderer;
+ d->renderer = nullptr;
+ }
+
if (!d->renderer) {
d->createRenderer();
if (!d->renderer)
@@ -913,38 +1173,108 @@ QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
// Called on the render thread, with the gui thread blocked. We can now
// safely access gui thread data.
-
Q_D(QQuickShape);
- if (d->renderer) {
- if (!node)
+
+ if (d->renderer || d->rendererChanged) {
+ if (!node || d->rendererChanged) {
+ d->rendererChanged = false;
+ delete node;
node = d->createNode();
- d->renderer->updateNode();
+ }
+ if (d->renderer)
+ d->renderer->updateNode();
+
+ // TODO: only add transform node when needed (and then make sure static_cast is safe)
+ QMatrix4x4 fillModeTransform;
+ qreal xScale = 1.0;
+ qreal yScale = 1.0;
+
+ if (d->fillMode != NoResize) {
+ xScale = width() / implicitWidth();
+ yScale = height() / implicitHeight();
+
+ if (d->fillMode == PreserveAspectFit)
+ xScale = yScale = qMin(xScale, yScale);
+ else if (d->fillMode == PreserveAspectCrop)
+ xScale = yScale = qMax(xScale, yScale);
+ fillModeTransform.scale(xScale, yScale);
+ }
+ if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
+ qreal tx = 0;
+ qreal ty = 0;
+ qreal w = xScale * implicitWidth();
+ qreal h = yScale * implicitHeight();
+ if (d->horizontalAlignment == AlignRight)
+ tx = width() - w;
+ else if (d->horizontalAlignment == AlignHCenter)
+ tx = (width() - w) / 2;
+ if (d->verticalAlignment == AlignBottom)
+ ty = height() - h;
+ else if (d->verticalAlignment == AlignVCenter)
+ ty = (height() - h) / 2;
+ fillModeTransform.translate(tx / xScale, ty / yScale);
+ }
+ static_cast<QSGTransformNode *>(node)->setMatrix(fillModeTransform);
}
return node;
}
-// the renderer object lives on the gui thread
-void QQuickShapePrivate::createRenderer()
+QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
{
+ QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
Q_Q(QQuickShape);
QSGRendererInterface *ri = q->window()->rendererInterface();
if (!ri)
- return;
+ return res;
+
+ static const bool environmentPreferCurve =
+ qEnvironmentVariable("QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String("curverenderer");
switch (ri->graphicsApi()) {
case QSGRendererInterface::Software:
- rendererType = QQuickShape::SoftwareRenderer;
- renderer = new QQuickShapeSoftwareRenderer;
+ res = QQuickShape::SoftwareRenderer;
break;
default:
if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
- rendererType = QQuickShape::GeometryRenderer;
- renderer = new QQuickShapeGenericRenderer(q);
+ if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
+ res = QQuickShape::CurveRenderer;
+ } else {
+ res = QQuickShape::GeometryRenderer;
+ }
} else {
qWarning("No path backend for this graphics API yet");
}
break;
}
+
+ return res;
+}
+
+// the renderer object lives on the gui thread
+void QQuickShapePrivate::createRenderer()
+{
+ Q_Q(QQuickShape);
+ QQuickShape::RendererType selectedType = selectRendererType();
+ if (selectedType == QQuickShape::UnknownRenderer)
+ return;
+
+ rendererType = selectedType;
+ rendererChanged = true;
+
+ switch (selectedType) {
+ case QQuickShape::SoftwareRenderer:
+ renderer = new QQuickShapeSoftwareRenderer;
+ break;
+ case QQuickShape::GeometryRenderer:
+ renderer = new QQuickShapeGenericRenderer(q);
+ break;
+ case QQuickShape::CurveRenderer:
+ renderer = new QQuickShapeCurveRenderer(q);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
}
// the node lives on the render thread
@@ -952,29 +1282,39 @@ QSGNode *QQuickShapePrivate::createNode()
{
Q_Q(QQuickShape);
QSGNode *node = nullptr;
- if (!q->window())
+ if (!q->window() || !renderer)
return node;
QSGRendererInterface *ri = q->window()->rendererInterface();
if (!ri)
return node;
+ QSGNode *pathNode = nullptr;
switch (ri->graphicsApi()) {
case QSGRendererInterface::Software:
- node = new QQuickShapeSoftwareRenderNode(q);
+ pathNode = new QQuickShapeSoftwareRenderNode(q);
static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
- static_cast<QQuickShapeSoftwareRenderNode *>(node));
+ static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
break;
default:
if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
- node = new QQuickShapeGenericNode;
- static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
- static_cast<QQuickShapeGenericNode *>(node));
+ if (rendererType == QQuickShape::CurveRenderer) {
+ pathNode = new QSGNode;
+ static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
+ } else {
+ pathNode = new QQuickShapeGenericNode;
+ static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
+ static_cast<QQuickShapeGenericNode *>(pathNode));
+ }
} else {
qWarning("No path backend for this graphics API yet");
}
break;
}
+ // TODO: only create transform node when needed
+ node = new QSGTransformNode;
+ node->appendChildNode(pathNode);
+
return node;
}
@@ -1434,112 +1774,6 @@ void QQuickShapeConicalGradient::setAngle(qreal v)
}
}
-static void generateGradientColorTable(const QQuickShapeGradientCacheKey &gradient,
- uint *colorTable, int size, float opacity)
-{
- int pos = 0;
- const QGradientStops &s = gradient.stops;
- Q_ASSERT(!s.isEmpty());
- const bool colorInterpolation = true;
-
- uint alpha = qRound(opacity * 256);
- uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha);
- qreal incr = 1.0 / qreal(size);
- qreal fpos = 1.5 * incr;
- colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color));
-
- while (fpos <= s.first().first) {
- colorTable[pos] = colorTable[pos - 1];
- pos++;
- fpos += incr;
- }
-
- if (colorInterpolation)
- current_color = qPremultiply(current_color);
-
- const int sLast = s.size() - 1;
- for (int i = 0; i < sLast; ++i) {
- qreal delta = 1/(s[i+1].first - s[i].first);
- uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha);
- if (colorInterpolation)
- next_color = qPremultiply(next_color);
-
- while (fpos < s[i+1].first && pos < size) {
- int dist = int(256 * ((fpos - s[i].first) * delta));
- int idist = 256 - dist;
- if (colorInterpolation)
- colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
- else
- colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
- ++pos;
- fpos += incr;
- }
- current_color = next_color;
- }
-
- uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha)));
- for ( ; pos < size; ++pos)
- colorTable[pos] = last_color;
-
- colorTable[size-1] = last_color;
-}
-
-QQuickShapeGradientCache::~QQuickShapeGradientCache()
-{
- qDeleteAll(m_textures);
-}
-
-QQuickShapeGradientCache *QQuickShapeGradientCache::cacheForRhi(QRhi *rhi)
-{
- static QHash<QRhi *, QQuickShapeGradientCache *> caches;
- auto it = caches.constFind(rhi);
- if (it != caches.constEnd())
- return *it;
-
- QQuickShapeGradientCache *cache = new QQuickShapeGradientCache;
- rhi->addCleanupCallback([cache](QRhi *rhi) {
- caches.remove(rhi);
- delete cache;
- });
- caches.insert(rhi, cache);
- return cache;
-}
-
-QSGTexture *QQuickShapeGradientCache::get(const QQuickShapeGradientCacheKey &grad)
-{
- QSGPlainTexture *tx = m_textures[grad];
- if (!tx) {
- static const int W = 1024; // texture size is 1024x1
- QImage gradTab(W, 1, QImage::Format_RGBA8888_Premultiplied);
- if (!grad.stops.isEmpty())
- generateGradientColorTable(grad, reinterpret_cast<uint *>(gradTab.bits()), W, 1.0f);
- else
- gradTab.fill(Qt::black);
- tx = new QSGPlainTexture;
- tx->setImage(gradTab);
- switch (grad.spread) {
- case QQuickShapeGradient::PadSpread:
- tx->setHorizontalWrapMode(QSGTexture::ClampToEdge);
- tx->setVerticalWrapMode(QSGTexture::ClampToEdge);
- break;
- case QQuickShapeGradient::RepeatSpread:
- tx->setHorizontalWrapMode(QSGTexture::Repeat);
- tx->setVerticalWrapMode(QSGTexture::Repeat);
- break;
- case QQuickShapeGradient::ReflectSpread:
- tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat);
- tx->setVerticalWrapMode(QSGTexture::MirroredRepeat);
- break;
- default:
- qWarning("Unknown gradient spread mode %d", grad.spread);
- break;
- }
- tx->setFiltering(QSGTexture::Linear);
- m_textures[grad] = tx;
- }
- return tx;
-}
-
QT_END_NAMESPACE
#include "moc_qquickshape_p.cpp"
diff --git a/src/quickshapes/qquickshape_p.h b/src/quickshapes/qquickshape_p.h
index f1e2eea546..effbce1f8e 100644
--- a/src/quickshapes/qquickshape_p.h
+++ b/src/quickshapes/qquickshape_p.h
@@ -27,15 +27,15 @@ QT_BEGIN_NAMESPACE
class QQuickShapePathPrivate;
class QQuickShapePrivate;
-void Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapes_initializeModule();
+void Q_QUICKSHAPES_EXPORT QQuickShapes_initializeModule();
-class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapesModule
+class Q_QUICKSHAPES_EXPORT QQuickShapesModule
{
public:
static void defineModule();
};
-class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeGradient : public QQuickGradient
+class Q_QUICKSHAPES_EXPORT QQuickShapeGradient : public QQuickGradient
{
Q_OBJECT
Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged)
@@ -65,7 +65,7 @@ private:
SpreadMode m_spread;
};
-class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeLinearGradient : public QQuickShapeGradient
+class Q_QUICKSHAPES_EXPORT QQuickShapeLinearGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed)
@@ -99,7 +99,7 @@ private:
QPointF m_end;
};
-class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeRadialGradient : public QQuickShapeGradient
+class Q_QUICKSHAPES_EXPORT QQuickShapeRadialGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged)
@@ -148,7 +148,7 @@ private:
qreal m_focalRadius = 0;
};
-class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeConicalGradient : public QQuickShapeGradient
+class Q_QUICKSHAPES_EXPORT QQuickShapeConicalGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged)
@@ -180,7 +180,7 @@ private:
qreal m_angle = 0;
};
-class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapePath : public QQuickPath
+class Q_QUICKSHAPES_EXPORT QQuickShapePath : public QQuickPath
{
Q_OBJECT
@@ -196,6 +196,7 @@ class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapePath : public QQuickPath
Q_PROPERTY(QVector<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged)
Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient)
Q_PROPERTY(QSizeF scale READ scale WRITE setScale NOTIFY scaleChanged REVISION(1, 14))
+ Q_PROPERTY(PathHints pathHints READ pathHints WRITE setPathHints NOTIFY pathHintsChanged REVISION(6, 7) FINAL)
QML_NAMED_ELEMENT(ShapePath)
QML_ADDED_IN_VERSION(1, 0)
@@ -226,6 +227,18 @@ public:
};
Q_ENUM(StrokeStyle)
+ enum PathHint {
+ PathLinear = 0x1,
+ PathQuadratic = 0x2,
+ PathConvex = 0x4,
+ PathFillOnRight = 0x8,
+ PathSolid = 0x10,
+ PathNonIntersecting = 0x20,
+ PathNonOverlappingControlPointTriangles = 0x40
+ };
+ Q_DECLARE_FLAGS(PathHints, PathHint)
+ Q_FLAG(PathHints)
+
QQuickShapePath(QObject *parent = nullptr);
~QQuickShapePath();
@@ -263,6 +276,9 @@ public:
void setFillGradient(QQuickShapeGradient *gradient);
void resetFillGradient();
+ PathHints pathHints() const;
+ void setPathHints(PathHints newPathHints);
+
Q_SIGNALS:
void shapePathChanged();
void strokeColorChanged();
@@ -276,20 +292,29 @@ Q_SIGNALS:
void dashOffsetChanged();
void dashPatternChanged();
+ Q_REVISION(6, 7) void pathHintsChanged();
+
private:
Q_DISABLE_COPY(QQuickShapePath)
Q_DECLARE_PRIVATE(QQuickShapePath)
Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged())
};
-class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShape : public QQuickItem
+class Q_QUICKSHAPES_EXPORT QQuickShape : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(RendererType rendererType READ rendererType NOTIFY rendererChanged)
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged)
+ Q_PROPERTY(RendererType preferredRendererType READ preferredRendererType
+ WRITE setPreferredRendererType NOTIFY preferredRendererTypeChanged REVISION(6, 6) FINAL)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(ContainsMode containsMode READ containsMode WRITE setContainsMode NOTIFY containsModeChanged REVISION(1, 11))
+ Q_PROPERTY(QRectF boundingRect READ boundingRect NOTIFY boundingRectChanged REVISION(6, 6) FINAL)
+ Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged REVISION(6, 7) FINAL)
+ Q_PROPERTY(HAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged REVISION(6, 7) FINAL)
+ Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged REVISION(6, 7) FINAL)
+
Q_PROPERTY(QQmlListProperty<QObject> data READ data)
Q_CLASSINFO("DefaultProperty", "data")
QML_NAMED_ELEMENT(Shape)
@@ -300,7 +325,8 @@ public:
UnknownRenderer,
GeometryRenderer,
NvprRenderer,
- SoftwareRenderer
+ SoftwareRenderer,
+ CurveRenderer
};
Q_ENUM(RendererType)
@@ -317,6 +343,23 @@ public:
};
Q_ENUM(ContainsMode)
+ enum FillMode {
+ NoResize,
+ PreserveAspectFit,
+ PreserveAspectCrop,
+ Stretch
+ };
+ Q_ENUM(FillMode)
+
+ enum HAlignment { AlignLeft = Qt::AlignLeft,
+ AlignRight = Qt::AlignRight,
+ AlignHCenter = Qt::AlignHCenter };
+ Q_ENUM(HAlignment)
+ enum VAlignment { AlignTop = Qt::AlignTop,
+ AlignBottom = Qt::AlignBottom,
+ AlignVCenter = Qt::AlignVCenter };
+ Q_ENUM(VAlignment)
+
QQuickShape(QQuickItem *parent = nullptr);
~QQuickShape();
@@ -325,6 +368,11 @@ public:
bool asynchronous() const;
void setAsynchronous(bool async);
+ Q_REVISION(6, 6) RendererType preferredRendererType() const;
+ Q_REVISION(6, 6) void setPreferredRendererType(RendererType preferredType);
+
+ Q_REVISION(6, 6) QRectF boundingRect() const override;
+
bool vendorExtensionsEnabled() const;
void setVendorExtensionsEnabled(bool enable);
@@ -337,6 +385,15 @@ public:
QQmlListProperty<QObject> data();
+ Q_REVISION(6, 7) FillMode fillMode() const;
+ Q_REVISION(6, 7) void setFillMode(FillMode newFillMode);
+
+ Q_REVISION(6, 7) HAlignment horizontalAlignment() const;
+ Q_REVISION(6, 7) void setHorizontalAlignment(HAlignment newHorizontalAlignment);
+
+ Q_REVISION(6, 7) VAlignment verticalAlignment() const;
+ Q_REVISION(6, 7) void setVerticalAlignment(VAlignment newVerticalAlignment);
+
protected:
QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override;
void updatePolish() override;
@@ -349,8 +406,14 @@ Q_SIGNALS:
void asynchronousChanged();
void vendorExtensionsEnabledChanged();
void statusChanged();
+ Q_REVISION(6, 6) void preferredRendererTypeChanged();
+ Q_REVISION(6, 6) void boundingRectChanged();
Q_REVISION(1, 11) void containsModeChanged();
+ Q_REVISION(6, 7) void fillModeChanged();
+ Q_REVISION(6, 7) void horizontalAlignmentChanged();
+ Q_REVISION(6, 7) void verticalAlignmentChanged();
+
private:
Q_DISABLE_COPY(QQuickShape)
Q_DECLARE_PRIVATE(QQuickShape)
@@ -359,6 +422,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickShape)
-
#endif // QQUICKSHAPE_P_H
diff --git a/src/quickshapes/qquickshape_p_p.h b/src/quickshapes/qquickshape_p_p.h
index 245ba659b2..a36aa1e27f 100644
--- a/src/quickshapes/qquickshape_p_p.h
+++ b/src/quickshapes/qquickshape_p_p.h
@@ -17,7 +17,7 @@
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuickShapes/private/qquickshape_p.h>
-#include <QtQuick/private/qquickitem_p.h>
+#include <private/qquickitem_p.h>
#include <QPainterPath>
#include <QColor>
#include <QBrush>
@@ -37,16 +37,7 @@ public:
SupportsAsync = 0x01
};
Q_DECLARE_FLAGS(Flags, Flag)
-
enum FillGradientType { NoGradient = 0, LinearGradient, RadialGradient, ConicalGradient };
- struct GradientDesc { // can fully describe a linear/radial/conical gradient
- QGradientStops stops;
- QQuickShapeGradient::SpreadMode spread;
- QPointF a; // start (L) or center point (R/C)
- QPointF b; // end (L) or focal point (R)
- qreal v0; // center radius (R) or start angle (C)
- qreal v1; // focal radius (R)
- };
virtual ~QQuickAbstractPathRenderer() { }
@@ -90,7 +81,7 @@ struct QQuickShapeStrokeFillParams
QQuickShapeGradient *fillGradient;
};
-class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapePathPrivate : public QQuickPathPrivate
+class Q_QUICKSHAPES_EXPORT QQuickShapePathPrivate : public QQuickPathPrivate
{
Q_DECLARE_PUBLIC(QQuickShapePath)
@@ -117,6 +108,7 @@ public:
int dirty;
QQuickShapeStrokeFillParams sfp;
+ QQuickShapePath::PathHints pathHints;
};
class QQuickShapePrivate : public QQuickItemPrivate
@@ -127,6 +119,7 @@ public:
QQuickShapePrivate();
~QQuickShapePrivate();
+ QQuickShape::RendererType selectRendererType();
void createRenderer();
QSGNode *createNode();
void sync();
@@ -138,6 +131,9 @@ public:
static void asyncShapeReady(void *data);
+ qreal getImplicitWidth() const override;
+ qreal getImplicitHeight() const override;
+
int effectRefCount;
QVector<QQuickShapePath *> sp;
QElapsedTimer syncTimer;
@@ -146,46 +142,20 @@ public:
int syncTimeCounter = 0;
QQuickShape::Status status = QQuickShape::Null;
QQuickShape::RendererType rendererType = QQuickShape::UnknownRenderer;
+ QQuickShape::RendererType preferredType = QQuickShape::UnknownRenderer;
QQuickShape::ContainsMode containsMode = QQuickShape::BoundingRectContains;
+ QQuickShape::FillMode fillMode = QQuickShape::NoResize;
+ QQuickShape::HAlignment horizontalAlignment = QQuickShape::AlignLeft;
+ QQuickShape::VAlignment verticalAlignment = QQuickShape::AlignTop;
+
bool spChanged = false;
+ bool rendererChanged = false;
bool async = false;
bool enableVendorExts = false;
bool syncTimingActive = false;
qreal triangulationScale = 1.0;
};
-struct QQuickShapeGradientCacheKey
-{
- QQuickShapeGradientCacheKey(const QGradientStops &stops, QQuickShapeGradient::SpreadMode spread)
- : stops(stops), spread(spread)
- { }
- QGradientStops stops;
- QQuickShapeGradient::SpreadMode spread;
- bool operator==(const QQuickShapeGradientCacheKey &other) const
- {
- return spread == other.spread && stops == other.stops;
- }
-};
-
-inline size_t qHash(const QQuickShapeGradientCacheKey &v, size_t seed = 0)
-{
- size_t h = seed + v.spread;
- for (int i = 0; i < 3 && i < v.stops.size(); ++i)
- h += v.stops[i].second.rgba();
- return h;
-}
-
-class QQuickShapeGradientCache
-{
-public:
- ~QQuickShapeGradientCache();
- static QQuickShapeGradientCache *cacheForRhi(QRhi *rhi);
- QSGTexture *get(const QQuickShapeGradientCacheKey &grad);
-
-private:
- QHash<QQuickShapeGradientCacheKey, QSGPlainTexture *> m_textures;
-};
-
QT_END_NAMESPACE
#endif
diff --git a/src/quickshapes/qquickshapecurverenderer.cpp b/src/quickshapes/qquickshapecurverenderer.cpp
new file mode 100644
index 0000000000..856d83fdac
--- /dev/null
+++ b/src/quickshapes/qquickshapecurverenderer.cpp
@@ -0,0 +1,748 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickshapecurverenderer_p.h"
+#include "qquickshapecurverenderer_p_p.h"
+
+#if QT_CONFIG(thread)
+#include <QtCore/qthreadpool.h>
+#endif
+
+#include <QtGui/qvector2d.h>
+#include <QtGui/qvector4d.h>
+#include <QtGui/private/qtriangulator_p.h>
+#include <QtGui/private/qtriangulatingstroker_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+#include <QtQuick/private/qsgcurvefillnode_p.h>
+#include <QtQuick/private/qsgcurvestrokenode_p.h>
+#include <QtQuick/private/qquadpath_p.h>
+#include <QtQuick/private/qsgcurveprocessor_p.h>
+#include <QtQuick/qsgmaterial.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcShapeCurveRenderer, "qt.shape.curverenderer");
+
+namespace {
+
+class QQuickShapeWireFrameMaterialShader : public QSGMaterialShader
+{
+public:
+ QQuickShapeWireFrameMaterialShader(int viewCount)
+ {
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/shapes/shaders_ng/wireframe.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/shapes/shaders_ng/wireframe.frag.qsb"), viewCount);
+ }
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 64);
+ const int matrixCount = qMin(state.projectionMatrixCount(), newMaterial->viewCount());
+
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
+ }
+
+ return changed;
+ }
+};
+
+class QQuickShapeWireFrameMaterial : public QSGMaterial
+{
+public:
+ QQuickShapeWireFrameMaterial()
+ {
+ setFlag(Blending, true);
+ }
+
+ int compare(const QSGMaterial *other) const override
+ {
+ return (type() - other->type());
+ }
+
+protected:
+ QSGMaterialType *type() const override
+ {
+ static QSGMaterialType t;
+ return &t;
+ }
+ QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override
+ {
+ return new QQuickShapeWireFrameMaterialShader(viewCount());
+ }
+
+};
+
+class QQuickShapeWireFrameNode : public QSGCurveAbstractNode
+{
+public:
+ struct WireFrameVertex
+ {
+ float x, y, u, v, w;
+ };
+
+ QQuickShapeWireFrameNode()
+ {
+ setFlag(OwnsGeometry, true);
+ setGeometry(new QSGGeometry(attributes(), 0, 0));
+ activateMaterial();
+ }
+
+ void setColor(QColor col) override
+ {
+ Q_UNUSED(col);
+ }
+
+ void activateMaterial()
+ {
+ m_material.reset(new QQuickShapeWireFrameMaterial);
+ setMaterial(m_material.data());
+ }
+
+ static const QSGGeometry::AttributeSet &attributes()
+ {
+ static QSGGeometry::Attribute data[] = {
+ QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(1, 3, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute),
+ };
+ static QSGGeometry::AttributeSet attrs = { 2, sizeof(WireFrameVertex), data };
+ return attrs;
+ }
+
+ void cookGeometry() override
+ {
+ // Intentionally empty
+ }
+
+protected:
+ QScopedPointer<QQuickShapeWireFrameMaterial> m_material;
+};
+}
+
+QQuickShapeCurveRenderer::~QQuickShapeCurveRenderer()
+{
+ for (const PathData &pd : std::as_const(m_paths)) {
+ if (pd.currentRunner)
+ pd.currentRunner->orphaned = true;
+ }
+}
+
+void QQuickShapeCurveRenderer::beginSync(int totalCount, bool *countChanged)
+{
+ if (countChanged != nullptr && totalCount != m_paths.size())
+ *countChanged = true;
+ m_paths.resize(totalCount);
+}
+
+void QQuickShapeCurveRenderer::setPath(int index, const QQuickPath *path)
+{
+ constexpr QQuickShapePath::PathHints noHints;
+ const auto *shapePath = qobject_cast<const QQuickShapePath *>(path);
+ setPath(index, path->path(), shapePath ? shapePath->pathHints() : noHints);
+}
+
+void QQuickShapeCurveRenderer::setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints)
+{
+ auto &pathData = m_paths[index];
+ pathData.originalPath = path;
+ pathData.pathHints = pathHints;
+ pathData.m_dirty |= PathDirty;
+}
+
+void QQuickShapeCurveRenderer::setStrokeColor(int index, const QColor &color)
+{
+ auto &pathData = m_paths[index];
+ const bool wasVisible = pathData.isStrokeVisible();
+ pathData.pen.setColor(color);
+ if (pathData.isStrokeVisible() != wasVisible)
+ pathData.m_dirty |= StrokeDirty;
+ else
+ pathData.m_dirty |= UniformsDirty;
+}
+
+void QQuickShapeCurveRenderer::setStrokeWidth(int index, qreal w)
+{
+ auto &pathData = m_paths[index];
+ if (w > 0) {
+ pathData.validPenWidth = true;
+ pathData.pen.setWidthF(w);
+ } else {
+ pathData.validPenWidth = false;
+ }
+ pathData.m_dirty |= StrokeDirty;
+}
+
+void QQuickShapeCurveRenderer::setFillColor(int index, const QColor &color)
+{
+ auto &pathData = m_paths[index];
+ const bool wasVisible = pathData.isFillVisible();
+ pathData.fillColor = color;
+ if (pathData.isFillVisible() != wasVisible)
+ pathData.m_dirty |= FillDirty;
+ else
+ pathData.m_dirty |= UniformsDirty;
+}
+
+void QQuickShapeCurveRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
+{
+ auto &pathData = m_paths[index];
+ pathData.fillRule = Qt::FillRule(fillRule);
+ pathData.m_dirty |= PathDirty;
+}
+
+void QQuickShapeCurveRenderer::setJoinStyle(int index,
+ QQuickShapePath::JoinStyle joinStyle,
+ int miterLimit)
+{
+ auto &pathData = m_paths[index];
+ pathData.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
+ pathData.pen.setMiterLimit(miterLimit);
+ pathData.m_dirty |= StrokeDirty;
+}
+
+void QQuickShapeCurveRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle)
+{
+ auto &pathData = m_paths[index];
+ pathData.pen.setCapStyle(Qt::PenCapStyle(capStyle));
+ pathData.m_dirty |= StrokeDirty;
+}
+
+void QQuickShapeCurveRenderer::setStrokeStyle(int index,
+ QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset,
+ const QVector<qreal> &dashPattern)
+{
+ auto &pathData = m_paths[index];
+ pathData.pen.setStyle(Qt::PenStyle(strokeStyle));
+ if (strokeStyle == QQuickShapePath::DashLine) {
+ pathData.pen.setDashPattern(dashPattern);
+ pathData.pen.setDashOffset(dashOffset);
+ }
+ pathData.m_dirty |= StrokeDirty;
+}
+
+void QQuickShapeCurveRenderer::setFillGradient(int index, QQuickShapeGradient *gradient)
+{
+ PathData &pd(m_paths[index]);
+ pd.gradientType = QGradient::NoGradient;
+ if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
+ pd.gradientType = QGradient::LinearGradient;
+ pd.gradient.stops = gradient->gradientStops();
+ pd.gradient.spread = QGradient::Spread(gradient->spread());
+ pd.gradient.a = QPointF(g->x1(), g->y1());
+ pd.gradient.b = QPointF(g->x2(), g->y2());
+ } else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) {
+ pd.gradientType = QGradient::RadialGradient;
+ pd.gradient.a = QPointF(g->centerX(), g->centerY());
+ pd.gradient.b = QPointF(g->focalX(), g->focalY());
+ pd.gradient.v0 = g->centerRadius();
+ pd.gradient.v1 = g->focalRadius();
+ } else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) {
+ pd.gradientType = QGradient::ConicalGradient;
+ pd.gradient.a = QPointF(g->centerX(), g->centerY());
+ pd.gradient.v0 = g->angle();
+ } else
+ if (gradient != nullptr) {
+ static bool warned = false;
+ if (!warned) {
+ warned = true;
+ qCWarning(lcShapeCurveRenderer) << "Unsupported gradient fill";
+ }
+ }
+
+ if (pd.gradientType != QGradient::NoGradient) {
+ pd.gradient.stops = gradient->gradientStops();
+ pd.gradient.spread = QGradient::Spread(gradient->spread());
+ }
+
+ pd.m_dirty |= FillDirty;
+}
+
+void QQuickShapeCurveRenderer::setAsyncCallback(void (*callback)(void *), void *data)
+{
+ m_asyncCallback = callback;
+ m_asyncCallbackData = data;
+}
+
+void QQuickShapeCurveRenderer::endSync(bool async)
+{
+ bool didKickOffAsync = false;
+
+ for (PathData &pathData : m_paths) {
+ if (!pathData.m_dirty)
+ continue;
+
+ if (pathData.m_dirty == UniformsDirty) {
+ // Requires no curve node computation, gets handled directly in updateNode()
+ continue;
+ }
+
+ if (pathData.currentRunner) {
+ // Already performing async computing. New dirty flags will be handled in the next sync
+ // after the current computation is done and the item is updated
+ continue;
+ }
+
+ createRunner(&pathData);
+
+#if QT_CONFIG(thread)
+ if (async) {
+ pathData.currentRunner->isAsync = true;
+ QThreadPool::globalInstance()->start(pathData.currentRunner);
+ didKickOffAsync = true;
+ } else
+#endif
+ {
+ pathData.currentRunner->run();
+ }
+ }
+
+ if (async && !didKickOffAsync && m_asyncCallback)
+ m_asyncCallback(m_asyncCallbackData);
+}
+
+void QQuickShapeCurveRenderer::createRunner(PathData *pathData)
+{
+ Q_ASSERT(!pathData->currentRunner);
+ QQuickShapeCurveRunnable *runner = new QQuickShapeCurveRunnable;
+ runner->setAutoDelete(false);
+ runner->pathData = *pathData;
+ runner->pathData.fillNodes.clear();
+ runner->pathData.strokeNodes.clear();
+ runner->pathData.currentRunner = nullptr;
+
+ pathData->currentRunner = runner;
+ pathData->m_dirty = 0;
+ QObject::connect(runner, &QQuickShapeCurveRunnable::done, qApp,
+ [this](QQuickShapeCurveRunnable *r) {
+ r->isDone = true;
+ if (r->orphaned) {
+ r->deleteLater(); // Renderer was destroyed
+ } else if (r->isAsync) {
+ maybeUpdateAsyncItem();
+ }
+ });
+}
+
+void QQuickShapeCurveRenderer::maybeUpdateAsyncItem()
+{
+ for (const PathData &pd : std::as_const(m_paths)) {
+ if (pd.currentRunner && !pd.currentRunner->isDone)
+ return;
+ }
+ if (m_item)
+ m_item->update();
+ if (m_asyncCallback)
+ m_asyncCallback(m_asyncCallbackData);
+}
+
+void QQuickShapeCurveRunnable::run()
+{
+ QQuickShapeCurveRenderer::processPath(&pathData);
+ emit done(this);
+}
+
+void QQuickShapeCurveRenderer::updateNode()
+{
+ if (!m_rootNode)
+ return;
+
+ auto updateUniforms = [](const PathData &pathData) {
+ for (auto &pathNode : std::as_const(pathData.fillNodes))
+ pathNode->setColor(pathData.fillColor);
+ for (auto &strokeNode : std::as_const(pathData.strokeNodes))
+ strokeNode->setColor(pathData.pen.color());
+ };
+
+ NodeList toBeDeleted;
+
+ for (int i = 0; i < m_paths.size(); i++) {
+ PathData &pathData = m_paths[i];
+ if (pathData.currentRunner) {
+ if (!pathData.currentRunner->isDone)
+ continue;
+ // Find insertion point for new nodes. Default is the first stroke node of this path
+ QSGNode *nextNode = pathData.strokeNodes.value(0);
+ // If that is 0, use the first node (stroke or fill) of later paths, if any
+ for (int j = i + 1; !nextNode && j < m_paths.size(); j++) {
+ const PathData &pd = m_paths[j];
+ nextNode = pd.fillNodes.isEmpty() ? pd.strokeNodes.value(0) : pd.fillNodes.value(0);
+ }
+
+ const PathData &newData = pathData.currentRunner->pathData;
+ if (newData.m_dirty & PathDirty)
+ pathData.path = newData.path;
+ if (newData.m_dirty & FillDirty) {
+ pathData.fillPath = newData.fillPath;
+ for (auto *node : std::as_const(newData.fillNodes)) {
+ if (nextNode)
+ m_rootNode->insertChildNodeBefore(node, nextNode);
+ else
+ m_rootNode->appendChildNode(node);
+ }
+ toBeDeleted += pathData.fillNodes;
+ pathData.fillNodes = newData.fillNodes;
+ }
+ if (newData.m_dirty & StrokeDirty) {
+ for (auto *node : std::as_const(newData.strokeNodes)) {
+ if (nextNode)
+ m_rootNode->insertChildNodeBefore(node, nextNode);
+ else
+ m_rootNode->appendChildNode(node);
+ }
+ toBeDeleted += pathData.strokeNodes;
+ pathData.strokeNodes = newData.strokeNodes;
+ }
+
+ if (newData.m_dirty & UniformsDirty)
+ updateUniforms(pathData);
+
+ // if (pathData.m_dirty && pathData.m_dirty != UniformsDirty && currentRunner.isAsync)
+ // qDebug("### should enqueue a new sync?");
+
+ pathData.currentRunner->deleteLater();
+ pathData.currentRunner = nullptr;
+ }
+
+ if (pathData.m_dirty == UniformsDirty) {
+ // Simple case so no runner was created in endSync(); handle it directly here
+ updateUniforms(pathData);
+ pathData.m_dirty = 0;
+ }
+ }
+ qDeleteAll(toBeDeleted); // also removes them from m_rootNode's child list
+}
+
+void QQuickShapeCurveRenderer::processPath(PathData *pathData)
+{
+ static const bool doOverlapSolving = !qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_OVERLAP_SOLVER");
+ static const bool doIntersetionSolving = !qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_INTERSECTION_SOLVER");
+ static const bool useTriangulatingStroker = qEnvironmentVariableIntValue("QT_QUICKSHAPES_TRIANGULATING_STROKER");
+ static const bool simplifyPath = qEnvironmentVariableIntValue("QT_QUICKSHAPES_SIMPLIFY_PATHS");
+
+ int &dirtyFlags = pathData->m_dirty;
+
+ if (dirtyFlags & PathDirty) {
+ if (simplifyPath)
+ pathData->path = QQuadPath::fromPainterPath(pathData->originalPath.simplified(), QQuadPath::PathLinear | QQuadPath::PathNonIntersecting | QQuadPath::PathNonOverlappingControlPointTriangles);
+ else
+ pathData->path = QQuadPath::fromPainterPath(pathData->originalPath, QQuadPath::PathHints(int(pathData->pathHints)));
+ pathData->path.setFillRule(pathData->fillRule);
+ pathData->fillPath = {};
+ dirtyFlags |= (FillDirty | StrokeDirty);
+ }
+
+ if (dirtyFlags & FillDirty) {
+ if (pathData->isFillVisible()) {
+ if (pathData->fillPath.isEmpty()) {
+ pathData->fillPath = pathData->path.subPathsClosed();
+ if (doIntersetionSolving)
+ QSGCurveProcessor::solveIntersections(pathData->fillPath);
+ pathData->fillPath.addCurvatureData();
+ if (doOverlapSolving)
+ QSGCurveProcessor::solveOverlaps(pathData->fillPath);
+ }
+ pathData->fillNodes = addFillNodes(*pathData);
+ dirtyFlags |= StrokeDirty;
+ }
+ }
+
+ if (dirtyFlags & StrokeDirty) {
+ if (pathData->isStrokeVisible()) {
+ const QPen &pen = pathData->pen;
+ if (pen.style() == Qt::SolidLine)
+ pathData->strokePath = pathData->path;
+ else
+ pathData->strokePath = pathData->path.dashed(pen.widthF(), pen.dashPattern(), pen.dashOffset());
+
+ if (useTriangulatingStroker)
+ pathData->strokeNodes = addTriangulatingStrokerNodes(*pathData);
+ else
+ pathData->strokeNodes = addCurveStrokeNodes(*pathData);
+ }
+ }
+}
+
+QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addFillNodes(const PathData &pathData)
+{
+ auto *node = new QSGCurveFillNode;
+ node->setGradientType(pathData.gradientType);
+ const qsizetype approxDataCount = 20 * pathData.fillPath.elementCount();
+ node->reserve(approxDataCount);
+
+ NodeList ret;
+ const QColor &color = pathData.fillColor;
+ QPainterPath internalHull;
+ internalHull.setFillRule(pathData.fillPath.fillRule());
+
+ bool visualizeDebug = debugVisualization() & DebugCurves;
+ const float dbg = visualizeDebug ? 0.5f : 0.0f;
+ node->setDebug(dbg);
+
+ QVector<QQuickShapeWireFrameNode::WireFrameVertex> wfVertices;
+ wfVertices.reserve(approxDataCount);
+
+ QSGCurveProcessor::processFill(pathData.fillPath,
+ pathData.fillRule,
+ [&wfVertices, &node](const std::array<QVector2D, 3> &v,
+ const std::array<QVector2D, 3> &n,
+ QSGCurveProcessor::uvForPointCallback uvForPoint)
+ {
+ node->appendTriangle(v, n, uvForPoint);
+
+ wfVertices.append({v.at(0).x(), v.at(0).y(), 1.0f, 0.0f, 0.0f}); // 0
+ wfVertices.append({v.at(1).x(), v.at(1).y(), 0.0f, 1.0f, 0.0f}); // 1
+ wfVertices.append({v.at(2).x(), v.at(2).y(), 0.0f, 0.0f, 1.0f}); // 2
+ });
+
+ QVector<quint32> indices = node->uncookedIndexes();
+ if (indices.size() > 0) {
+ node->setColor(color);
+ node->setFillGradient(pathData.gradient);
+
+ node->cookGeometry();
+ ret.append(node);
+ }
+
+ const bool wireFrame = debugVisualization() & DebugWireframe;
+ if (wireFrame) {
+ QQuickShapeWireFrameNode *wfNode = new QQuickShapeWireFrameNode;
+ QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode::attributes(),
+ wfVertices.size(),
+ indices.size(),
+ QSGGeometry::UnsignedIntType);
+ wfNode->setGeometry(wfg);
+
+ wfg->setDrawingMode(QSGGeometry::DrawTriangles);
+ memcpy(wfg->indexData(),
+ indices.data(),
+ indices.size() * wfg->sizeOfIndex());
+ memcpy(wfg->vertexData(),
+ wfVertices.data(),
+ wfg->vertexCount() * wfg->sizeOfVertex());
+
+ ret.append(wfNode);
+ }
+
+ return ret;
+}
+
+QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addTriangulatingStrokerNodes(const PathData &pathData)
+{
+ NodeList ret;
+ const QColor &color = pathData.pen.color();
+
+ QVector<QQuickShapeWireFrameNode::WireFrameVertex> wfVertices;
+
+ QTriangulatingStroker stroker;
+ const auto painterPath = pathData.strokePath.toPainterPath();
+ const QVectorPath &vp = qtVectorPathForPath(painterPath);
+ QPen pen = pathData.pen;
+ stroker.process(vp, pen, {}, {});
+
+ auto *node = new QSGCurveFillNode;
+ node->setGradientType(pathData.gradientType);
+
+ auto uvForPoint = [](QVector2D v1, QVector2D v2, QVector2D p)
+ {
+ double divisor = v1.x() * v2.y() - v2.x() * v1.y();
+
+ float u = (p.x() * v2.y() - p.y() * v2.x()) / divisor;
+ float v = (p.y() * v1.x() - p.x() * v1.y()) / divisor;
+
+ return QVector2D(u, v);
+ };
+
+ // Find uv coordinates for the point p, for a quadratic curve from p0 to p2 with control point p1
+ // also works for a line from p0 to p2, where p1 is on the inside of the path relative to the line
+ auto curveUv = [uvForPoint](QVector2D p0, QVector2D p1, QVector2D p2, QVector2D p)
+ {
+ QVector2D v1 = 2 * (p1 - p0);
+ QVector2D v2 = p2 - v1 - p0;
+ return uvForPoint(v1, v2, p - p0);
+ };
+
+ auto findPointOtherSide = [](const QVector2D &startPoint, const QVector2D &endPoint, const QVector2D &referencePoint){
+
+ QVector2D baseLine = endPoint - startPoint;
+ QVector2D insideVector = referencePoint - startPoint;
+ QVector2D normal = QVector2D(-baseLine.y(), baseLine.x()); // TODO: limit size of triangle
+
+ bool swap = QVector2D::dotProduct(insideVector, normal) < 0;
+
+ return swap ? startPoint + normal : startPoint - normal;
+ };
+
+ static bool disableExtraTriangles = qEnvironmentVariableIntValue("QT_QUICKSHAPES_WIP_DISABLE_EXTRA_STROKE_TRIANGLES");
+
+ auto addStrokeTriangle = [&](const QVector2D &p1, const QVector2D &p2, const QVector2D &p3, bool){
+ if (p1 == p2 || p2 == p3) {
+ return;
+ }
+
+ auto uvForPoint = [&p1, &p2, &p3, curveUv](QVector2D p) {
+ auto uv = curveUv(p1, p2, p3, p);
+ return QVector3D(uv.x(), uv.y(), 0.0f); // Line
+ };
+
+ node->appendTriangle(p1, p2, p3, uvForPoint);
+
+
+ wfVertices.append({p1.x(), p1.y(), 1.0f, 0.0f, 0.0f}); // 0
+ wfVertices.append({p2.x(), p2.y(), 0.0f, 0.1f, 0.0f}); // 1
+ wfVertices.append({p3.x(), p3.y(), 0.0f, 0.0f, 1.0f}); // 2
+
+ if (!disableExtraTriangles) {
+ // Add a triangle on the outer side of the line to get some more AA
+ // The new point replaces p2 (currentVertex+1)
+ QVector2D op = findPointOtherSide(p1, p3, p2);
+ node->appendTriangle(p1, op, p3, uvForPoint);
+
+ wfVertices.append({p1.x(), p1.y(), 1.0f, 0.0f, 0.0f});
+ wfVertices.append({op.x(), op.y(), 0.0f, 1.0f, 0.0f}); // replacing p2
+ wfVertices.append({p3.x(), p3.y(), 0.0f, 0.0f, 1.0f});
+ }
+ };
+
+ const int vertCount = stroker.vertexCount() / 2;
+ const float *verts = stroker.vertices();
+ for (int i = 0; i < vertCount - 2; ++i) {
+ QVector2D p[3];
+ for (int j = 0; j < 3; ++j) {
+ p[j] = QVector2D(verts[(i+j)*2], verts[(i+j)*2 + 1]);
+ }
+ bool isOdd = i % 2;
+ addStrokeTriangle(p[0], p[1], p[2], isOdd);
+ }
+
+ QVector<quint32> indices = node->uncookedIndexes();
+ if (indices.size() > 0) {
+ node->setColor(color);
+ node->setFillGradient(pathData.gradient);
+
+ node->cookGeometry();
+ ret.append(node);
+ }
+ const bool wireFrame = debugVisualization() & DebugWireframe;
+ if (wireFrame) {
+ QQuickShapeWireFrameNode *wfNode = new QQuickShapeWireFrameNode;
+ QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode::attributes(),
+ wfVertices.size(),
+ indices.size(),
+ QSGGeometry::UnsignedIntType);
+ wfNode->setGeometry(wfg);
+
+ wfg->setDrawingMode(QSGGeometry::DrawTriangles);
+ memcpy(wfg->indexData(),
+ indices.data(),
+ indices.size() * wfg->sizeOfIndex());
+ memcpy(wfg->vertexData(),
+ wfVertices.data(),
+ wfg->vertexCount() * wfg->sizeOfVertex());
+
+ ret.append(wfNode);
+ }
+
+ return ret;
+}
+
+void QQuickShapeCurveRenderer::setRootNode(QSGNode *node)
+{
+ m_rootNode = node;
+}
+
+int QQuickShapeCurveRenderer::debugVisualizationFlags = QQuickShapeCurveRenderer::NoDebug;
+
+int QQuickShapeCurveRenderer::debugVisualization()
+{
+ static const int envFlags = qEnvironmentVariableIntValue("QT_QUICKSHAPES_DEBUG");
+ return debugVisualizationFlags | envFlags;
+}
+
+void QQuickShapeCurveRenderer::setDebugVisualization(int options)
+{
+ if (debugVisualizationFlags == options)
+ return;
+ debugVisualizationFlags = options;
+}
+
+QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addCurveStrokeNodes(const PathData &pathData)
+{
+ NodeList ret;
+ const QColor &color = pathData.pen.color();
+
+ const bool debug = debugVisualization() & DebugCurves;
+ auto *node = new QSGCurveStrokeNode;
+ node->setDebug(0.2f * debug);
+ QVector<QQuickShapeWireFrameNode::WireFrameVertex> wfVertices;
+
+ const float miterLimit = pathData.pen.miterLimit();
+ const float penWidth = pathData.pen.widthF();
+
+ static const int subdivisions = qEnvironmentVariable("QT_QUICKSHAPES_STROKE_SUBDIVISIONS", QStringLiteral("3")).toInt();
+
+ QSGCurveProcessor::processStroke(pathData.strokePath,
+ miterLimit,
+ penWidth,
+ pathData.pen.joinStyle(),
+ pathData.pen.capStyle(),
+ [&wfVertices, &node](const std::array<QVector2D, 3> &s,
+ const std::array<QVector2D, 3> &p,
+ const std::array<QVector2D, 3> &n,
+ bool isLine)
+ {
+ const QVector2D &p0 = s.at(0);
+ const QVector2D &p1 = s.at(1);
+ const QVector2D &p2 = s.at(2);
+ if (isLine)
+ node->appendTriangle(s, std::array<QVector2D, 2>{p.at(0), p.at(2)}, n);
+ else
+ node->appendTriangle(s, p, n);
+
+ wfVertices.append({p0.x(), p0.y(), 1.0f, 0.0f, 0.0f});
+ wfVertices.append({p1.x(), p1.y(), 0.0f, 1.0f, 0.0f});
+ wfVertices.append({p2.x(), p2.y(), 0.0f, 0.0f, 1.0f});
+ },
+ subdivisions);
+
+ auto indexCopy = node->uncookedIndexes(); // uncookedIndexes get delete on cooking
+
+ node->setColor(color);
+ node->setStrokeWidth(pathData.pen.widthF());
+ node->cookGeometry();
+ ret.append(node);
+
+ const bool wireFrame = debugVisualization() & DebugWireframe;
+ if (wireFrame) {
+ QQuickShapeWireFrameNode *wfNode = new QQuickShapeWireFrameNode;
+
+ QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode::attributes(),
+ wfVertices.size(),
+ indexCopy.size(),
+ QSGGeometry::UnsignedIntType);
+ wfNode->setGeometry(wfg);
+
+ wfg->setDrawingMode(QSGGeometry::DrawTriangles);
+ memcpy(wfg->indexData(),
+ indexCopy.data(),
+ indexCopy.size() * wfg->sizeOfIndex());
+ memcpy(wfg->vertexData(),
+ wfVertices.data(),
+ wfg->vertexCount() * wfg->sizeOfVertex());
+
+ ret.append(wfNode);
+ }
+
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickshapes/qquickshapecurverenderer_p.h b/src/quickshapes/qquickshapecurverenderer_p.h
new file mode 100644
index 0000000000..f2b10fb44c
--- /dev/null
+++ b/src/quickshapes/qquickshapecurverenderer_p.h
@@ -0,0 +1,154 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKSHAPECURVERENDERER_P_H
+#define QQUICKSHAPECURVERENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
+#include <QtQuickShapes/private/qquickshape_p_p.h>
+#include <QtQuick/private/qquadpath_p.h>
+#include <QtQuick/private/qsgcurveabstractnode_p.h>
+#include <QtQuick/private/qsggradientcache_p.h>
+#include <qsgnode.h>
+#include <qsggeometry.h>
+#include <qsgmaterial.h>
+#include <qsgrendererinterface.h>
+#include <qsgtexture.h>
+#include <QtCore/qrunnable.h>
+#include <QRunnable>
+
+#include <QtGui/private/qtriangulator_p.h>
+#include <QtQuick/private/qsgcurvefillnode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickShapeCurveRunnable;
+
+class Q_QUICKSHAPES_EXPORT QQuickShapeCurveRenderer : public QQuickAbstractPathRenderer
+{
+public:
+ QQuickShapeCurveRenderer(QQuickItem *item)
+ : m_item(item)
+ { }
+ ~QQuickShapeCurveRenderer() override;
+
+ void beginSync(int totalCount, bool *countChanged) override;
+ void setPath(int index, const QQuickPath *path) override;
+ void setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints = {});
+ void setStrokeColor(int index, const QColor &color) override;
+ void setStrokeWidth(int index, qreal w) override;
+ void setFillColor(int index, const QColor &color) override;
+ void setFillRule(int index, QQuickShapePath::FillRule fillRule) override;
+ void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override;
+ void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) override;
+ void setFillGradient(int index, QQuickShapeGradient *gradient) override;
+ void endSync(bool async) override;
+ void setAsyncCallback(void (*)(void *), void *) override;
+ Flags flags() const override { return SupportsAsync; }
+
+ void updateNode() override;
+
+ void setRootNode(QSGNode *node);
+
+ using NodeList = QVector<QSGCurveAbstractNode *>;
+
+ enum DirtyFlag
+ {
+ PathDirty = 0x01,
+ FillDirty = 0x02,
+ StrokeDirty = 0x04,
+ UniformsDirty = 0x08
+ };
+
+ enum DebugVisualizationOption {
+ NoDebug = 0,
+ DebugCurves = 0x01,
+ DebugWireframe = 0x02
+ };
+
+ static int debugVisualization();
+ static void setDebugVisualization(int options);
+
+private:
+ struct PathData {
+
+ bool isFillVisible() const { return fillColor.alpha() > 0 || gradientType != QGradient::NoGradient; }
+
+ bool isStrokeVisible() const
+ {
+ return validPenWidth && pen.color().alpha() > 0 && pen.style() != Qt::NoPen;
+ }
+
+ QGradient::Type gradientType = QGradient::NoGradient;
+ QSGGradientCache::GradientDesc gradient;
+ QColor fillColor;
+ Qt::FillRule fillRule = Qt::OddEvenFill;
+ QPen pen;
+ bool validPenWidth = true;
+ int m_dirty = 0;
+ QQuickShapePath::PathHints pathHints;
+
+ QPainterPath originalPath;
+ QQuadPath path;
+ QQuadPath fillPath;
+ QQuadPath strokePath;
+
+ NodeList fillNodes;
+ NodeList strokeNodes;
+
+ QQuickShapeCurveRunnable *currentRunner = nullptr;
+ };
+
+ void createRunner(PathData *pathData);
+ void maybeUpdateAsyncItem();
+
+ static void processPath(PathData *pathData);
+ static NodeList addFillNodes(const PathData &pathData);
+ static NodeList addTriangulatingStrokerNodes(const PathData &pathData);
+ static NodeList addCurveStrokeNodes(const PathData &pathData);
+
+ void solveIntersections(QQuadPath &path);
+ QQuickItem *m_item;
+ QSGNode *m_rootNode = nullptr;
+ QVector<PathData> m_paths;
+ void (*m_asyncCallback)(void *) = nullptr;
+ void *m_asyncCallbackData = nullptr;
+ static int debugVisualizationFlags;
+
+ friend class QQuickShapeCurveRunnable;
+};
+
+class QQuickShapeCurveRunnable : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ void run() override;
+
+ bool isAsync = false;
+ bool isDone = false;
+ bool orphaned = false;
+
+ // input / output
+ QQuickShapeCurveRenderer::PathData pathData;
+
+Q_SIGNALS:
+ void done(QQuickShapeCurveRunnable *self);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSHAPECURVERENDERER_P_H
diff --git a/src/quickshapes/qquickshapecurverenderer_p_p.h b/src/quickshapes/qquickshapecurverenderer_p_p.h
new file mode 100644
index 0000000000..8829bd89a8
--- /dev/null
+++ b/src/quickshapes/qquickshapecurverenderer_p_p.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QQUICKSHAPECURVERENDERER_P_P_H
+#define QQUICKSHAPECURVERENDERER_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
+#include <QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcShapeCurveRenderer);
+
+QT_END_NAMESPACE
+
+#endif //QQUICKSHAPECURVERENDERER_P_P_H
diff --git a/src/quickshapes/qquickshapegenericrenderer.cpp b/src/quickshapes/qquickshapegenericrenderer.cpp
index b2b6af041f..a18479f776 100644
--- a/src/quickshapes/qquickshapegenericrenderer.cpp
+++ b/src/quickshapes/qquickshapegenericrenderer.cpp
@@ -4,9 +4,11 @@
#include "qquickshapegenericrenderer_p.h"
#include <QtGui/private/qtriangulator_p.h>
#include <QtGui/private/qtriangulatingstroker_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QSGVertexColorMaterial>
+#include <QtQuick/private/qsggradientcache_p.h>
+
#if QT_CONFIG(thread)
#include <QThreadPool>
#endif
@@ -176,7 +178,7 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient
ShapePathData &d(m_sp[index]);
if (gradient) {
d.fillGradient.stops = gradient->gradientStops(); // sorted
- d.fillGradient.spread = gradient->spread();
+ d.fillGradient.spread = QGradient::Spread(gradient->spread());
if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
d.fillGradientActive = LinearGradient;
d.fillGradient.a = QPointF(g->x1(), g->y1());
@@ -701,10 +703,10 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createConicalGradient(QQuickWind
return nullptr;
}
-QQuickShapeLinearGradientRhiShader::QQuickShapeLinearGradientRhiShader()
+QQuickShapeLinearGradientRhiShader::QQuickShapeLinearGradientRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.frag.qsb"), viewCount);
}
bool QQuickShapeLinearGradientRhiShader::updateUniformData(RenderState &state,
@@ -715,11 +717,15 @@ bool QQuickShapeLinearGradientRhiShader::updateUniformData(RenderState &state,
bool changed = false;
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 84);
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
}
QQuickShapeGenericStrokeFillNode *node = m->node();
@@ -727,19 +733,19 @@ bool QQuickShapeLinearGradientRhiShader::updateUniformData(RenderState &state,
if (!oldMaterial || m_gradA.x() != node->m_fillGradient.a.x() || m_gradA.y() != node->m_fillGradient.a.y()) {
m_gradA = QVector2D(node->m_fillGradient.a.x(), node->m_fillGradient.a.y());
Q_ASSERT(sizeof(m_gradA) == 8);
- memcpy(buf->data() + 64, &m_gradA, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &m_gradA, 8);
changed = true;
}
if (!oldMaterial || m_gradB.x() != node->m_fillGradient.b.x() || m_gradB.y() != node->m_fillGradient.b.y()) {
m_gradB = QVector2D(node->m_fillGradient.b.x(), node->m_fillGradient.b.y());
- memcpy(buf->data() + 72, &m_gradB, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &m_gradB, 8);
changed = true;
}
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 80, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 8, &opacity, 4);
changed = true;
}
@@ -754,8 +760,8 @@ void QQuickShapeLinearGradientRhiShader::updateSampledImage(RenderState &state,
QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
QQuickShapeGenericStrokeFillNode *node = m->node();
- const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
- QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
+ const QSGGradientCacheKey cacheKey(node->m_fillGradient.stops, QGradient::Spread(node->m_fillGradient.spread));
+ QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
*texture = t;
}
@@ -777,8 +783,8 @@ int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const
if (a == b)
return 0;
- const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient;
- const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient;
+ const QSGGradientCache::GradientDesc *ga = &a->m_fillGradient;
+ const QSGGradientCache::GradientDesc *gb = &b->m_fillGradient;
if (int d = ga->spread - gb->spread)
return d;
@@ -808,13 +814,13 @@ int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const
QSGMaterialShader *QQuickShapeLinearGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new QQuickShapeLinearGradientRhiShader;
+ return new QQuickShapeLinearGradientRhiShader(viewCount());
}
-QQuickShapeRadialGradientRhiShader::QQuickShapeRadialGradientRhiShader()
+QQuickShapeRadialGradientRhiShader::QQuickShapeRadialGradientRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.frag.qsb"), viewCount);
}
bool QQuickShapeRadialGradientRhiShader::updateUniformData(RenderState &state,
@@ -825,11 +831,15 @@ bool QQuickShapeRadialGradientRhiShader::updateUniformData(RenderState &state,
bool changed = false;
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 92);
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
}
QQuickShapeGenericStrokeFillNode *node = m->node();
@@ -843,32 +853,32 @@ bool QQuickShapeRadialGradientRhiShader::updateUniformData(RenderState &state,
if (!oldMaterial || m_focalPoint.x() != focalPoint.x() || m_focalPoint.y() != focalPoint.y()) {
m_focalPoint = QVector2D(focalPoint.x(), focalPoint.y());
Q_ASSERT(sizeof(m_focalPoint) == 8);
- memcpy(buf->data() + 64, &m_focalPoint, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &m_focalPoint, 8);
changed = true;
}
if (!oldMaterial || m_focalToCenter.x() != focalToCenter.x() || m_focalToCenter.y() != focalToCenter.y()) {
m_focalToCenter = QVector2D(focalToCenter.x(), focalToCenter.y());
Q_ASSERT(sizeof(m_focalToCenter) == 8);
- memcpy(buf->data() + 72, &m_focalToCenter, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &m_focalToCenter, 8);
changed = true;
}
if (!oldMaterial || m_centerRadius != centerRadius) {
m_centerRadius = centerRadius;
- memcpy(buf->data() + 80, &m_centerRadius, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 8, &m_centerRadius, 4);
changed = true;
}
if (!oldMaterial || m_focalRadius != focalRadius) {
m_focalRadius = focalRadius;
- memcpy(buf->data() + 84, &m_focalRadius, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 8 + 4, &m_focalRadius, 4);
changed = true;
}
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 88, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 8 + 4 + 4, &opacity, 4);
changed = true;
}
@@ -883,8 +893,8 @@ void QQuickShapeRadialGradientRhiShader::updateSampledImage(RenderState &state,
QQuickShapeRadialGradientMaterial *m = static_cast<QQuickShapeRadialGradientMaterial *>(newMaterial);
QQuickShapeGenericStrokeFillNode *node = m->node();
- const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
- QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
+ const QSGGradientCacheKey cacheKey(node->m_fillGradient.stops, QGradient::Spread(node->m_fillGradient.spread));
+ QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
*texture = t;
}
@@ -906,8 +916,8 @@ int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const
if (a == b)
return 0;
- const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient;
- const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient;
+ const QSGGradientCache::GradientDesc *ga = &a->m_fillGradient;
+ const QSGGradientCache::GradientDesc *gb = &b->m_fillGradient;
if (int d = ga->spread - gb->spread)
return d;
@@ -942,13 +952,13 @@ int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const
QSGMaterialShader *QQuickShapeRadialGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new QQuickShapeRadialGradientRhiShader;
+ return new QQuickShapeRadialGradientRhiShader(viewCount());
}
-QQuickShapeConicalGradientRhiShader::QQuickShapeConicalGradientRhiShader()
+QQuickShapeConicalGradientRhiShader::QQuickShapeConicalGradientRhiShader(int viewCount)
{
- setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.vert.qsb"));
- setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.frag.qsb"));
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.vert.qsb"), viewCount);
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.frag.qsb"), viewCount);
}
bool QQuickShapeConicalGradientRhiShader::updateUniformData(RenderState &state,
@@ -959,11 +969,15 @@ bool QQuickShapeConicalGradientRhiShader::updateUniformData(RenderState &state,
bool changed = false;
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 80);
+ const int shaderMatrixCount = newMaterial->viewCount();
+ const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
- changed = true;
+ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
+ changed = true;
+ }
}
QQuickShapeGenericStrokeFillNode *node = m->node();
@@ -974,19 +988,19 @@ bool QQuickShapeConicalGradientRhiShader::updateUniformData(RenderState &state,
if (!oldMaterial || m_centerPoint.x() != centerPoint.x() || m_centerPoint.y() != centerPoint.y()) {
m_centerPoint = QVector2D(centerPoint.x(), centerPoint.y());
Q_ASSERT(sizeof(m_centerPoint) == 8);
- memcpy(buf->data() + 64, &m_centerPoint, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount, &m_centerPoint, 8);
changed = true;
}
if (!oldMaterial || m_angle != angle) {
m_angle = angle;
- memcpy(buf->data() + 72, &m_angle, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8, &m_angle, 4);
changed = true;
}
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 76, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 4, &opacity, 4);
changed = true;
}
@@ -1001,8 +1015,8 @@ void QQuickShapeConicalGradientRhiShader::updateSampledImage(RenderState &state,
QQuickShapeConicalGradientMaterial *m = static_cast<QQuickShapeConicalGradientMaterial *>(newMaterial);
QQuickShapeGenericStrokeFillNode *node = m->node();
- const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
- QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
+ const QSGGradientCacheKey cacheKey(node->m_fillGradient.stops, QGradient::Spread(node->m_fillGradient.spread));
+ QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
*texture = t;
}
@@ -1024,8 +1038,8 @@ int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const
if (a == b)
return 0;
- const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient;
- const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient;
+ const QSGGradientCache::GradientDesc *ga = &a->m_fillGradient;
+ const QSGGradientCache::GradientDesc *gb = &b->m_fillGradient;
if (int d = ga->a.x() - gb->a.x())
return d;
@@ -1051,7 +1065,7 @@ int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const
QSGMaterialShader *QQuickShapeConicalGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
{
Q_UNUSED(renderMode);
- return new QQuickShapeConicalGradientRhiShader;
+ return new QQuickShapeConicalGradientRhiShader(viewCount());
}
QT_END_NAMESPACE
diff --git a/src/quickshapes/qquickshapegenericrenderer_p.h b/src/quickshapes/qquickshapegenericrenderer_p.h
index a5ec01329d..6877b674e0 100644
--- a/src/quickshapes/qquickshapegenericrenderer_p.h
+++ b/src/quickshapes/qquickshapegenericrenderer_p.h
@@ -17,6 +17,7 @@
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
+#include <QtQuick/private/qsggradientcache_p.h>
#include <qsgnode.h>
#include <qsggeometry.h>
#include <qsgmaterial.h>
@@ -96,12 +97,12 @@ private:
struct ShapePathData {
float strokeWidth;
QPen pen;
- Color4ub strokeColor;
- Color4ub fillColor;
+ Color4ub strokeColor = { uchar(0), uchar(0), uchar(0), uchar(0) };
+ Color4ub fillColor = { uchar(0), uchar(0), uchar(0), uchar(0) };
Qt::FillRule fillRule;
QPainterPath path;
FillGradientType fillGradientActive;
- GradientDesc fillGradient;
+ QSGGradientCache::GradientDesc fillGradient;
VertexContainerType fillVertices;
IndexContainerType fillIndices;
QSGGeometry::Type indexType;
@@ -188,7 +189,7 @@ public:
void activateMaterial(QQuickWindow *window, Material m);
// shadow data for custom materials
- QQuickAbstractPathRenderer::GradientDesc m_fillGradient;
+ QSGGradientCache::GradientDesc m_fillGradient;
private:
QScopedPointer<QSGMaterial> m_material;
@@ -216,7 +217,7 @@ public:
class QQuickShapeLinearGradientRhiShader : public QSGMaterialShader
{
public:
- QQuickShapeLinearGradientRhiShader();
+ QQuickShapeLinearGradientRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
QSGMaterial *oldMaterial) override;
@@ -255,7 +256,7 @@ private:
class QQuickShapeRadialGradientRhiShader : public QSGMaterialShader
{
public:
- QQuickShapeRadialGradientRhiShader();
+ QQuickShapeRadialGradientRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
QSGMaterial *oldMaterial) override;
@@ -291,7 +292,7 @@ private:
class QQuickShapeConicalGradientRhiShader : public QSGMaterialShader
{
public:
- QQuickShapeConicalGradientRhiShader();
+ QQuickShapeConicalGradientRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
QSGMaterial *oldMaterial) override;
diff --git a/src/quickshapes/qquickshapesglobal_p.h b/src/quickshapes/qquickshapesglobal_p.h
index 0efd1a88ce..eb26c41a20 100644
--- a/src/quickshapes/qquickshapesglobal_p.h
+++ b/src/quickshapes/qquickshapesglobal_p.h
@@ -16,11 +16,11 @@
//
#include "qquickshapesglobal.h"
-#include <QtQuickShapes/private/qtquickshapesexports_p.h>
+#include <QtQuickShapes/qtquickshapesexports.h>
QT_BEGIN_NAMESPACE
-void Q_QUICKSHAPES_PRIVATE_EXPORT qml_register_types_QtQuick_Shapes();
+void Q_QUICKSHAPES_EXPORT qml_register_types_QtQuick_Shapes();
QT_END_NAMESPACE
diff --git a/src/quickshapes/shaders_ng/compile.bat b/src/quickshapes/shaders_ng/compile.bat
deleted file mode 100755
index be21daf62f..0000000000
--- a/src/quickshapes/shaders_ng/compile.bat
+++ /dev/null
@@ -1,15 +0,0 @@
-:: Copyright (C) 2019 The Qt Company Ltd.
-:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-:: For HLSL we invoke fxc.exe (-c argument) and store the resulting intermediate format
-:: instead of HLSL source, so this needs to be run on Windows from a developer command prompt.
-
-:: For SPIR-V the optimizer is requested (-O argument) which means spirv-opt must be
-:: invokable (e.g. because it's in the PATH from the Vulkan SDK)
-
-qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o lineargradient.vert.qsb lineargradient.vert
-qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o lineargradient.frag.qsb lineargradient.frag
-qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o radialgradient.vert.qsb radialgradient.vert
-qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o radialgradient.frag.qsb radialgradient.frag
-qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o conicalgradient.vert.qsb conicalgradient.vert
-qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -O -c -o conicalgradient.frag.qsb conicalgradient.frag
diff --git a/src/quickshapes/shaders_ng/conicalgradient.frag b/src/quickshapes/shaders_ng/conicalgradient.frag
index 0b1e01bae2..7707d55deb 100644
--- a/src/quickshapes/shaders_ng/conicalgradient.frag
+++ b/src/quickshapes/shaders_ng/conicalgradient.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 coord;
@@ -6,7 +9,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D gradTabTexture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 translationPoint;
float angle;
float opacity;
diff --git a/src/quickshapes/shaders_ng/conicalgradient.frag.qsb b/src/quickshapes/shaders_ng/conicalgradient.frag.qsb
deleted file mode 100644
index df58050128..0000000000
--- a/src/quickshapes/shaders_ng/conicalgradient.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/quickshapes/shaders_ng/conicalgradient.vert b/src/quickshapes/shaders_ng/conicalgradient.vert
index 3db027294b..c092198724 100644
--- a/src/quickshapes/shaders_ng/conicalgradient.vert
+++ b/src/quickshapes/shaders_ng/conicalgradient.vert
@@ -6,16 +6,22 @@ layout(location = 1) in vec4 vertexColor;
layout(location = 0) out vec2 coord;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 translationPoint;
float angle;
float opacity;
} ubuf;
-out gl_PerVertex { vec4 gl_Position; };
-
void main()
{
coord = vertexCoord.xy - ubuf.translationPoint;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
+#else
gl_Position = ubuf.matrix * vertexCoord;
+#endif
}
diff --git a/src/quickshapes/shaders_ng/conicalgradient.vert.qsb b/src/quickshapes/shaders_ng/conicalgradient.vert.qsb
deleted file mode 100644
index 958a75e918..0000000000
--- a/src/quickshapes/shaders_ng/conicalgradient.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/quickshapes/shaders_ng/lineargradient.frag b/src/quickshapes/shaders_ng/lineargradient.frag
index 16894fc764..6dc4c7d5ca 100644
--- a/src/quickshapes/shaders_ng/lineargradient.frag
+++ b/src/quickshapes/shaders_ng/lineargradient.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in float gradTabIndex;
@@ -6,7 +9,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D gradTabTexture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 gradStart;
vec2 gradEnd;
float opacity;
diff --git a/src/quickshapes/shaders_ng/lineargradient.frag.qsb b/src/quickshapes/shaders_ng/lineargradient.frag.qsb
deleted file mode 100644
index be3a820afd..0000000000
--- a/src/quickshapes/shaders_ng/lineargradient.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/quickshapes/shaders_ng/lineargradient.vert b/src/quickshapes/shaders_ng/lineargradient.vert
index b4eb868186..b453739230 100644
--- a/src/quickshapes/shaders_ng/lineargradient.vert
+++ b/src/quickshapes/shaders_ng/lineargradient.vert
@@ -6,17 +6,23 @@ layout(location = 1) in vec4 vertexColor;
layout(location = 0) out float gradTabIndex;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 gradStart;
vec2 gradEnd;
float opacity;
} ubuf;
-out gl_PerVertex { vec4 gl_Position; };
-
void main()
{
vec2 gradVec = ubuf.gradEnd - ubuf.gradStart;
gradTabIndex = dot(gradVec, vertexCoord.xy - ubuf.gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
+#else
gl_Position = ubuf.matrix * vertexCoord;
+#endif
}
diff --git a/src/quickshapes/shaders_ng/lineargradient.vert.qsb b/src/quickshapes/shaders_ng/lineargradient.vert.qsb
deleted file mode 100644
index 9e76279ca5..0000000000
--- a/src/quickshapes/shaders_ng/lineargradient.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/quickshapes/shaders_ng/radialgradient.frag b/src/quickshapes/shaders_ng/radialgradient.frag
index 411e589295..bbc68b2ce7 100644
--- a/src/quickshapes/shaders_ng/radialgradient.frag
+++ b/src/quickshapes/shaders_ng/radialgradient.frag
@@ -1,3 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#version 440
layout(location = 0) in vec2 coord;
@@ -6,7 +9,11 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D gradTabTexture;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 translationPoint;
vec2 focalToCenter;
float centerRadius;
diff --git a/src/quickshapes/shaders_ng/radialgradient.frag.qsb b/src/quickshapes/shaders_ng/radialgradient.frag.qsb
deleted file mode 100644
index ecd7f2926d..0000000000
--- a/src/quickshapes/shaders_ng/radialgradient.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/src/quickshapes/shaders_ng/radialgradient.vert b/src/quickshapes/shaders_ng/radialgradient.vert
index 08f15c4f8c..6d3dfce745 100644
--- a/src/quickshapes/shaders_ng/radialgradient.vert
+++ b/src/quickshapes/shaders_ng/radialgradient.vert
@@ -6,7 +6,11 @@ layout(location = 1) in vec4 vertexColor;
layout(location = 0) out vec2 coord;
layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 matrix[QSHADER_VIEW_COUNT];
+#else
mat4 matrix;
+#endif
vec2 translationPoint;
vec2 focalToCenter;
float centerRadius;
@@ -14,10 +18,12 @@ layout(std140, binding = 0) uniform buf {
float opacity;
} ubuf;
-out gl_PerVertex { vec4 gl_Position; };
-
void main()
{
coord = vertexCoord.xy - ubuf.translationPoint;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
+#else
gl_Position = ubuf.matrix * vertexCoord;
+#endif
}
diff --git a/src/quickshapes/shaders_ng/radialgradient.vert.qsb b/src/quickshapes/shaders_ng/radialgradient.vert.qsb
deleted file mode 100644
index 68e0e21f02..0000000000
--- a/src/quickshapes/shaders_ng/radialgradient.vert.qsb
+++ /dev/null
Binary files differ
diff --git a/src/quickshapes/shaders_ng/wireframe.frag b/src/quickshapes/shaders_ng/wireframe.frag
new file mode 100644
index 0000000000..b37ffde6ff
--- /dev/null
+++ b/src/quickshapes/shaders_ng/wireframe.frag
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#version 440
+
+layout(location = 0) out vec4 fragColor;
+layout(location = 0) in vec3 barycentric;
+
+layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
+ mat4 qt_Matrix;
+#endif
+} ubuf;
+
+void main()
+{
+ float f = min(barycentric.x, min(barycentric.y, barycentric.z));
+ float d = fwidth(f * 1.5);
+ float alpha = smoothstep(0.0, d, f);
+
+ //alpha = 1.0 - step(0.5, barycentric.x);
+ fragColor = vec4(1.0, 0.2, 1.0, 1.0) * (1.0 - alpha);
+}
diff --git a/src/quickshapes/shaders_ng/wireframe.vert b/src/quickshapes/shaders_ng/wireframe.vert
new file mode 100644
index 0000000000..69f8872d51
--- /dev/null
+++ b/src/quickshapes/shaders_ng/wireframe.vert
@@ -0,0 +1,23 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+layout(location = 1) in vec3 vertexBarycentric;
+layout(location = 0) out vec3 barycentric;
+
+layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
+ mat4 qt_Matrix;
+#endif
+} ubuf;
+
+void main()
+{
+ barycentric = vertexBarycentric;
+#if QSHADER_VIEW_COUNT >= 2
+ gl_Position = ubuf.qt_Matrix[gl_ViewIndex] * vertexCoord;
+#else
+ gl_Position = ubuf.qt_Matrix * vertexCoord;
+#endif
+}
diff --git a/src/quicktemplates/CMakeLists.txt b/src/quicktemplates/CMakeLists.txt
index 57f4897b53..441649a46a 100644
--- a/src/quicktemplates/CMakeLists.txt
+++ b/src/quicktemplates/CMakeLists.txt
@@ -26,9 +26,6 @@ qt_internal_add_qml_module(QuickTemplates2
qquickbuttongroup.cpp qquickbuttongroup_p.h
qquickcheckbox.cpp qquickcheckbox_p.h
qquickcheckdelegate.cpp qquickcheckdelegate_p.h
- qquickcombobox.cpp qquickcombobox_p.h
- qquickcontainer.cpp qquickcontainer_p.h
- qquickcontainer_p_p.h
qquickcontentitem.cpp qquickcontentitem_p.h
qquickcontrol.cpp qquickcontrol_p.h
qquickcontrol_p_p.h
@@ -37,10 +34,6 @@ qt_internal_add_qml_module(QuickTemplates2
qquickdeferredpointer_p_p.h
qquickdelaybutton.cpp qquickdelaybutton_p.h
qquickdial.cpp qquickdial_p.h
- qquickdialog.cpp qquickdialog_p.h
- qquickdialog_p_p.h
- qquickdialogbuttonbox.cpp qquickdialogbuttonbox_p.h
- qquickdialogbuttonbox_p_p.h
qquickdrawer.cpp qquickdrawer_p.h
qquickdrawer_p_p.h
qquickframe.cpp qquickframe_p.h
@@ -52,14 +45,6 @@ qt_internal_add_qml_module(QuickTemplates2
qquickitemdelegate_p_p.h
qquicklabel.cpp qquicklabel_p.h
qquicklabel_p_p.h
- qquickmenu.cpp qquickmenu_p.h
- qquickmenu_p_p.h
- qquickmenubar.cpp qquickmenubar_p.h
- qquickmenubar_p_p.h
- qquickmenubaritem.cpp qquickmenubaritem_p.h
- qquickmenubaritem_p_p.h
- qquickmenuitem.cpp qquickmenuitem_p.h
- qquickmenuitem_p_p.h
qquickmenuseparator.cpp qquickmenuseparator_p.h
qquickoverlay.cpp qquickoverlay_p.h
qquickoverlay_p_p.h
@@ -91,20 +76,15 @@ qt_internal_add_qml_module(QuickTemplates2
qquickshortcutcontext_p_p.h
qquickslider.cpp qquickslider_p.h
qquickspinbox.cpp qquickspinbox_p.h
- qquicksplitview.cpp qquicksplitview_p.h
qquickstackelement.cpp
qquickstackelement_p_p.h
- qquickstacktransition.cpp
- qquickstacktransition_p_p.h
qquickstackview.cpp qquickstackview_p.cpp qquickstackview_p.h
qquickstackview_p_p.h
qquickswipe_p.h
qquickswipedelegate.cpp qquickswipedelegate_p.h
qquickswipedelegate_p_p.h
- qquickswipeview.cpp qquickswipeview_p.h
qquickswitch.cpp qquickswitch_p.h
qquickswitchdelegate.cpp qquickswitchdelegate_p.h
- qquicktabbar.cpp qquicktabbar_p.h
qquicktabbutton.cpp qquicktabbutton_p.h
qquicktextarea.cpp qquicktextarea_p.h
qquicktextarea_p_p.h
@@ -119,6 +99,8 @@ qt_internal_add_qml_module(QuickTemplates2
qquickvelocitycalculator.cpp
qquickvelocitycalculator_p_p.h
qtquicktemplates2global.cpp qtquicktemplates2global_p.h
+ NO_UNITY_BUILD_SOURCES
+ qquickpopupitem.cpp # redefinition of 'contentItemName' (from qquickcontrol.cpp)
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
@@ -134,8 +116,7 @@ qt_internal_add_qml_module(QuickTemplates2
Qt::Gui
Qt::Quick
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
qt_internal_extend_target(QuickTemplates2 CONDITION TARGET Qt::QmlModels
LIBRARIES
@@ -151,6 +132,31 @@ qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_accessibility
accessible/qaccessiblequickpage.cpp accessible/qaccessiblequickpage_p.h
)
+qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quicktemplates2_container
+ SOURCES
+ qquickcontainer.cpp qquickcontainer_p.h
+ qquickcontainer_p_p.h
+ qquickdialog.cpp qquickdialog_p.h
+ qquickdialog_p_p.h
+ qquickdialogbuttonbox.cpp qquickdialogbuttonbox_p.h
+ qquickdialogbuttonbox_p_p.h
+ qquickmenubar.cpp qquickmenubar_p.h
+ qquickmenubar_p_p.h
+ qquickmenubaritem.cpp qquickmenubaritem_p.h
+ qquickmenubaritem_p_p.h
+ qquicksplitview.cpp qquicksplitview_p.h
+ qquickswipeview.cpp qquickswipeview_p.h
+ qquicktabbar.cpp qquicktabbar_p.h
+)
+
+qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_qml_object_model
+ SOURCES
+ qquickmenu.cpp qquickmenu_p.h
+ qquickmenu_p_p.h
+ qquickmenuitem.cpp qquickmenuitem_p.h
+ qquickmenuitem_p_p.h
+)
+
qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quick_tableview
SOURCES
qquickheaderview.cpp qquickheaderview_p.h
@@ -182,6 +188,18 @@ qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quicktemplates2_c
qquickweeknumbermodel.cpp qquickweeknumbermodel_p.h
)
+qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quick_viewtransitions
+ SOURCES
+ qquickstacktransition.cpp
+ qquickstacktransition_p_p.h
+)
+
+qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_qml_delegate_model
+ SOURCES
+ qquickcombobox.cpp
+ qquickcombobox_p.h
+)
+
qt_internal_extend_Target(qtquicktemplates2plugin
SOURCES
qtquicktemplates2plugin.cpp
diff --git a/src/quicktemplates/configure.cmake b/src/quicktemplates/configure.cmake
index 3b9cb00fc2..77683e8105 100644
--- a/src/quicktemplates/configure.cmake
+++ b/src/quicktemplates/configure.cmake
@@ -31,6 +31,13 @@ qt_feature("quicktemplates2-calendar" PRIVATE
SECTION "Quick Templates 2"
LABEL "Calendar support"
PURPOSE "Provides calendar types."
+ CONDITION QT_FEATURE_itemmodel
+)
+qt_feature("quicktemplates2-container" PRIVATE
+ SECTION "Quick Templates 2"
+ LABEL "Container controls support"
+ PURPOSE "Provides support for Container and its sub-classes."
+ CONDITION QT_FEATURE_qml_object_model
)
qt_configure_add_summary_section(NAME "Qt Quick Templates 2")
qt_configure_add_summary_entry(ARGS "quicktemplates2-hover")
diff --git a/src/quicktemplates/qquickabstractbutton.cpp b/src/quicktemplates/qquickabstractbutton.cpp
index e30c090379..0c1af73e55 100644
--- a/src/quicktemplates/qquickabstractbutton.cpp
+++ b/src/quicktemplates/qquickabstractbutton.cpp
@@ -3,6 +3,7 @@
#include "qquickabstractbutton_p.h"
#include "qquickabstractbutton_p_p.h"
+#include "qquickactiongroup_p.h"
#include "qquickbuttongroup_p.h"
#include "qquickaction_p.h"
#include "qquickaction_p_p.h"
@@ -62,6 +63,8 @@ QT_BEGIN_NAMESPACE
\qmlsignal QtQuick.Controls::AbstractButton::clicked()
This signal is emitted when the button is interactively clicked by the user via touch, mouse, or keyboard.
+
+ \sa {Call a C++ function from QML when a Button is clicked}
*/
/*!
@@ -84,6 +87,30 @@ QT_BEGIN_NAMESPACE
This signal is emitted when the button is interactively double clicked by the user via touch or mouse.
*/
+void QQuickAbstractButtonPrivate::init()
+{
+ Q_Q(QQuickAbstractButton);
+ q->setActiveFocusOnTab(true);
+#ifdef Q_OS_MACOS
+ q->setFocusPolicy(Qt::TabFocus);
+#else
+ q->setFocusPolicy(Qt::StrongFocus);
+#endif
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+#if QT_CONFIG(quicktemplates2_multitouch)
+ q->setAcceptTouchEvents(true);
+#endif
+#if QT_CONFIG(cursor)
+ q->setCursor(Qt::ArrowCursor);
+#endif
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+}
+
+QPointF QQuickAbstractButtonPrivate::centerPressPoint() const
+{
+ return QPointF(qRound(width / 2), qRound(height / 2));
+}
+
void QQuickAbstractButtonPrivate::setPressPoint(const QPointF &point)
{
pressPoint = point;
@@ -362,8 +389,6 @@ void QQuickAbstractButtonPrivate::toggle(bool value)
emit q->toggled();
}
-static inline QString indicatorName() { return QStringLiteral("indicator"); }
-
void QQuickAbstractButtonPrivate::cancelIndicator()
{
Q_Q(QQuickAbstractButton);
@@ -413,7 +438,7 @@ QQuickAbstractButton *QQuickAbstractButtonPrivate::findCheckedButton() const
{
Q_Q(const QQuickAbstractButton);
if (group)
- return qobject_cast<QQuickAbstractButton *>(group->checkedButton());
+ return group->checkedButton();
const QList<QQuickAbstractButton *> buttons = findExclusiveButtons();
// TODO: A singular QRadioButton can be unchecked, which seems logical,
@@ -457,45 +482,29 @@ QList<QQuickAbstractButton *> QQuickAbstractButtonPrivate::findExclusiveButtons(
QQuickAbstractButton::QQuickAbstractButton(QQuickItem *parent)
: QQuickControl(*(new QQuickAbstractButtonPrivate), parent)
{
- setActiveFocusOnTab(true);
-#ifdef Q_OS_MACOS
- setFocusPolicy(Qt::TabFocus);
-#else
- setFocusPolicy(Qt::StrongFocus);
-#endif
- setAcceptedMouseButtons(Qt::LeftButton);
-#if QT_CONFIG(quicktemplates2_multitouch)
- setAcceptTouchEvents(true);
-#endif
-#if QT_CONFIG(cursor)
- setCursor(Qt::ArrowCursor);
-#endif
+ Q_D(QQuickAbstractButton);
+ d->init();
}
QQuickAbstractButton::QQuickAbstractButton(QQuickAbstractButtonPrivate &dd, QQuickItem *parent)
: QQuickControl(dd, parent)
{
- setActiveFocusOnTab(true);
-#ifdef Q_OS_MACOS
- setFocusPolicy(Qt::TabFocus);
-#else
- setFocusPolicy(Qt::StrongFocus);
-#endif
- setAcceptedMouseButtons(Qt::LeftButton);
-#if QT_CONFIG(quicktemplates2_multitouch)
- setAcceptTouchEvents(true);
-#endif
-#if QT_CONFIG(cursor)
- setCursor(Qt::ArrowCursor);
-#endif
+ Q_D(QQuickAbstractButton);
+ d->init();
}
QQuickAbstractButton::~QQuickAbstractButton()
{
Q_D(QQuickAbstractButton);
d->removeImplicitSizeListener(d->indicator);
- if (d->group)
- d->group->removeButton(this);
+ if (d->group) {
+ auto *attached = qobject_cast<QQuickButtonGroupAttached *>(
+ qmlAttachedPropertiesObject<QQuickButtonGroup>(this, false));
+ if (attached)
+ attached->setGroup(nullptr);
+ else
+ d->group->removeButton(this);
+ }
#if QT_CONFIG(shortcut)
d->ungrabShortcut();
#endif
@@ -810,7 +819,7 @@ void QQuickAbstractButton::setIcon(const QQuickIcon &icon)
\header \li Display \li Result
\row \li \c AbstractButton.IconOnly \li \image qtquickcontrols-button-icononly.png
\row \li \c AbstractButton.TextOnly \li \image qtquickcontrols-button-textonly.png
- \row \li \c AbstractButton.TextBesideIcon \li \image qtquickcontrols-button-textbesideicon.png
+ \row \li \c AbstractButton.TextBesideIcon (default) \li \image qtquickcontrols-button-textbesideicon.png
\row \li \c AbstractButton.TextUnderIcon \li \image qtquickcontrols-button-textundericon.png
\endtable
@@ -1084,7 +1093,7 @@ void QQuickAbstractButton::keyPressEvent(QKeyEvent *event)
Q_D(QQuickAbstractButton);
QQuickControl::keyPressEvent(event);
if (d->acceptKeyClick(static_cast<Qt::Key>(event->key()))) {
- d->setPressPoint(QPoint(qRound(width() / 2), qRound(height() / 2)));
+ d->setPressPoint(d->centerPressPoint());
setPressed(true);
if (d->autoRepeat)
@@ -1114,6 +1123,11 @@ void QQuickAbstractButton::keyReleaseEvent(QKeyEvent *event)
void QQuickAbstractButton::mousePressEvent(QMouseEvent *event)
{
+ if (!(event->buttons() & Qt::LeftButton)) {
+ event->ignore();
+ return;
+ }
+
Q_D(QQuickAbstractButton);
d->pressButtons = event->buttons();
QQuickControl::mousePressEvent(event);
@@ -1121,9 +1135,10 @@ void QQuickAbstractButton::mousePressEvent(QMouseEvent *event)
void QQuickAbstractButton::mouseDoubleClickEvent(QMouseEvent *event)
{
+ Q_UNUSED(event);
Q_D(QQuickAbstractButton);
if (d->isDoubleClickConnected()) {
- QQuickControl::mouseDoubleClickEvent(event);
+ // don't call QQuickItem::mouseDoubleClickEvent(): it would ignore()
emit doubleClicked();
d->wasDoubleClick = true;
}
@@ -1188,8 +1203,21 @@ void QQuickAbstractButton::buttonChange(ButtonChange change)
void QQuickAbstractButton::nextCheckState()
{
Q_D(QQuickAbstractButton);
- if (d->checkable && (!d->checked || d->findCheckedButton() != this))
- d->toggle(!d->checked);
+ if (!d->checkable)
+ return;
+
+ if (d->checked) {
+ if (d->findCheckedButton() == this)
+ return;
+ if (d->action) {
+ // For non-exclusive groups checkedAction is null
+ if (const auto group = QQuickActionPrivate::get(d->action)->group)
+ if (group->checkedAction() == d->action)
+ return;
+ }
+ }
+
+ d->toggle(!d->checked);
}
#if QT_CONFIG(accessibility)
diff --git a/src/quicktemplates/qquickabstractbutton_p.h b/src/quicktemplates/qquickabstractbutton_p.h
index 8907cfefd8..0ac27db156 100644
--- a/src/quicktemplates/qquickabstractbutton_p.h
+++ b/src/quicktemplates/qquickabstractbutton_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickAction;
class QQuickAbstractButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickAbstractButton : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickAbstractButton : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText RESET resetText NOTIFY textChanged FINAL)
@@ -188,6 +188,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAbstractButton)
-
#endif // QQUICKABSTRACTBUTTON_P_H
diff --git a/src/quicktemplates/qquickabstractbutton_p_p.h b/src/quicktemplates/qquickabstractbutton_p_p.h
index 9fcb5f5583..ea9a02c99d 100644
--- a/src/quicktemplates/qquickabstractbutton_p_p.h
+++ b/src/quicktemplates/qquickabstractbutton_p_p.h
@@ -21,12 +21,14 @@
# include <QtGui/qkeysequence.h>
#endif
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickAction;
class QQuickButtonGroup;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickAbstractButtonPrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickAbstractButtonPrivate : public QQuickControlPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickAbstractButton)
@@ -36,6 +38,7 @@ public:
return button->d_func();
}
+ QPointF centerPressPoint() const;
void setPressPoint(const QPointF &point);
void setMovePoint(const QPointF &point);
@@ -65,6 +68,7 @@ public:
void actionTextChange();
void setText(const QString &text, bool isExplicit);
+ void init();
void updateEffectiveIcon();
diff --git a/src/quicktemplates/qquickaction.cpp b/src/quicktemplates/qquickaction.cpp
index 466501226d..3c28ae98bc 100644
--- a/src/quicktemplates/qquickaction.cpp
+++ b/src/quicktemplates/qquickaction.cpp
@@ -13,6 +13,8 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
/*!
diff --git a/src/quicktemplates/qquickaction_p.h b/src/quicktemplates/qquickaction_p.h
index c4dd818af6..db985f750d 100644
--- a/src/quicktemplates/qquickaction_p.h
+++ b/src/quicktemplates/qquickaction_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickIcon;
class QQuickActionPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickAction : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickAction : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL)
@@ -92,6 +92,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAction)
-
#endif // QQUICKACTION_P_H
diff --git a/src/quicktemplates/qquickactiongroup.cpp b/src/quicktemplates/qquickactiongroup.cpp
index 5e088f3924..1c41dd5ff0 100644
--- a/src/quicktemplates/qquickactiongroup.cpp
+++ b/src/quicktemplates/qquickactiongroup.cpp
@@ -11,6 +11,8 @@
#include "qquickaction_p.h"
#include "qquickaction_p_p.h"
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
/*!
diff --git a/src/quicktemplates/qquickactiongroup_p.h b/src/quicktemplates/qquickactiongroup_p.h
index 2cd8edbe77..1dd19df1e8 100644
--- a/src/quicktemplates/qquickactiongroup_p.h
+++ b/src/quicktemplates/qquickactiongroup_p.h
@@ -26,7 +26,7 @@ class QQuickActionGroupPrivate;
class QQuickActionGroupAttached;
class QQuickActionGroupAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickActionGroup : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickActionGroup : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickAction *checkedAction READ checkedAction WRITE setCheckedAction NOTIFY checkedActionChanged FINAL)
@@ -73,7 +73,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_updateCurrent())
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickActionGroupAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickActionGroupAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickActionGroup *group READ group WRITE setGroup NOTIFY groupChanged FINAL)
@@ -94,6 +94,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickActionGroup)
-
#endif // QQUICKACTIONGROUP_P_H
diff --git a/src/quicktemplates/qquickapplicationwindow.cpp b/src/quicktemplates/qquickapplicationwindow.cpp
index 582b5dd31a..56e4b5c217 100644
--- a/src/quicktemplates/qquickapplicationwindow.cpp
+++ b/src/quicktemplates/qquickapplicationwindow.cpp
@@ -8,8 +8,11 @@
#include "qquicktextarea_p.h"
#include "qquicktextfield_p.h"
#include "qquicktoolbar_p.h"
+#include <private/qtquicktemplates2-config_p.h>
+#if QT_CONFIG(quicktemplates2_container)
#include "qquicktabbar_p.h"
#include "qquickdialogbuttonbox_p.h"
+#endif
#include "qquickdeferredexecute_p_p.h"
#include "qquickdeferredpointer_p_p.h"
@@ -87,18 +90,13 @@ QT_BEGIN_NAMESPACE
static const QQuickItemPrivate::ChangeTypes ItemChanges = QQuickItemPrivate::Visibility
| QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickApplicationWindowPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickApplicationWindowPrivate
: public QQuickWindowQmlImplPrivate
, public QQuickItemChangeListener
{
Q_DECLARE_PUBLIC(QQuickApplicationWindow)
public:
- QQuickApplicationWindowPrivate()
- {
- complete = true;
- }
-
static QQuickApplicationWindowPrivate *get(QQuickApplicationWindow *window)
{
return window->d_func();
@@ -112,6 +110,7 @@ public:
void itemVisibilityChanged(QQuickItem *item) override;
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ QPalette windowPalette() const override { return defaultPalette(); }
void updateFont(const QFont &f);
inline void setFont_helper(const QFont &f) {
@@ -149,6 +148,8 @@ public:
QLocale locale;
QQuickItem *activeFocusControl = nullptr;
bool insideRelayout = false;
+ bool hasBackgroundWidth = false;
+ bool hasBackgroundHeight = false;
};
static void layoutItem(QQuickItem *item, qreal y, qreal width)
@@ -167,7 +168,7 @@ static void layoutItem(QQuickItem *item, qreal y, qreal width)
void QQuickApplicationWindowPrivate::relayout()
{
Q_Q(QQuickApplicationWindow);
- if (!complete || insideRelayout)
+ if (!componentComplete || insideRelayout)
return;
QScopedValueRollback<bool> guard(insideRelayout, true);
@@ -185,23 +186,25 @@ void QQuickApplicationWindowPrivate::relayout()
layoutItem(footer, content->height(), q->width());
if (background) {
- QQuickItemPrivate *p = QQuickItemPrivate::get(background);
- if (!p->widthValid() && qFuzzyIsNull(background->x())) {
+ if (!hasBackgroundWidth && qFuzzyIsNull(background->x()))
background->setWidth(q->width());
- p->widthValidFlag = false;
- }
- if (!p->heightValid() && qFuzzyIsNull(background->y())) {
+ if (!hasBackgroundHeight && qFuzzyIsNull(background->y()))
background->setHeight(q->height());
- p->heightValidFlag = false;
- }
}
}
void QQuickApplicationWindowPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
{
- Q_UNUSED(item);
- Q_UNUSED(change);
Q_UNUSED(diff);
+
+ if (!insideRelayout && item == background && change.sizeChange()) {
+ // Any time the background is resized (excluding our own resizing),
+ // we should respect it if it's explicit by storing the values of the flags.
+ QQuickItemPrivate *backgroundPrivate = QQuickItemPrivate::get(background);
+ hasBackgroundWidth = backgroundPrivate->widthValid();
+ hasBackgroundHeight = backgroundPrivate->heightValid();
+ }
+
relayout();
}
@@ -280,8 +283,6 @@ void QQuickApplicationWindowPrivate::contentData_append(QQmlListProperty<QObject
QQuickPopupPrivate::get(popup)->setWindow(static_cast<QQuickApplicationWindow *>(prop->data));
}
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickApplicationWindowPrivate::cancelBackground()
{
Q_Q(QQuickApplicationWindow);
@@ -359,12 +360,24 @@ void QQuickApplicationWindow::setBackground(QQuickItem *background)
if (!d->background.isExecuting())
d->cancelBackground();
+ if (d->background) {
+ d->hasBackgroundWidth = false;
+ d->hasBackgroundHeight = false;
+ }
QQuickControlPrivate::hideOldItem(d->background);
+
d->background = background;
+
if (background) {
background->setParentItem(QQuickWindow::contentItem());
+
if (qFuzzyIsNull(background->z()))
background->setZ(-1);
+
+ QQuickItemPrivate *backgroundPrivate = QQuickItemPrivate::get(background);
+ d->hasBackgroundWidth = backgroundPrivate->widthValid();
+ d->hasBackgroundHeight = backgroundPrivate->heightValid();
+
if (isComponentComplete())
d->relayout();
}
@@ -418,10 +431,12 @@ void QQuickApplicationWindow::setHeader(QQuickItem *header)
header->setZ(1);
if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(header))
toolBar->setPosition(QQuickToolBar::Header);
+#if QT_CONFIG(quicktemplates2_container)
else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(header))
tabBar->setPosition(QQuickTabBar::Header);
else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(header))
buttonBox->setPosition(QQuickDialogButtonBox::Header);
+#endif
}
if (isComponentComplete())
d->relayout();
@@ -473,10 +488,12 @@ void QQuickApplicationWindow::setFooter(QQuickItem *footer)
footer->setZ(1);
if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(footer))
toolBar->setPosition(QQuickToolBar::Footer);
+#if QT_CONFIG(quicktemplates2_container)
else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(footer))
tabBar->setPosition(QQuickTabBar::Footer);
else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(footer))
buttonBox->setPosition(QQuickDialogButtonBox::Footer);
+#endif
}
if (isComponentComplete())
d->relayout();
@@ -685,13 +702,13 @@ void QQuickApplicationWindow::setMenuBar(QQuickItem *menuBar)
bool QQuickApplicationWindow::isComponentComplete() const
{
Q_D(const QQuickApplicationWindow);
- return d->complete;
+ return d->componentComplete;
}
void QQuickApplicationWindow::classBegin()
{
Q_D(QQuickApplicationWindow);
- d->complete = false;
+ d->componentComplete = false;
QQuickWindowQmlImpl::classBegin();
d->resolveFont();
}
@@ -699,7 +716,7 @@ void QQuickApplicationWindow::classBegin()
void QQuickApplicationWindow::componentComplete()
{
Q_D(QQuickApplicationWindow);
- d->complete = true;
+ d->componentComplete = true;
d->executeBackground(true);
QQuickWindowQmlImpl::componentComplete();
d->relayout();
diff --git a/src/quicktemplates/qquickapplicationwindow_p.h b/src/quicktemplates/qquickapplicationwindow_p.h
index 283b192b81..23a3a197ac 100644
--- a/src/quicktemplates/qquickapplicationwindow_p.h
+++ b/src/quicktemplates/qquickapplicationwindow_p.h
@@ -27,7 +27,7 @@ class QQuickApplicationWindowPrivate;
class QQuickApplicationWindowAttached;
class QQuickApplicationWindowAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickApplicationWindow : public QQuickWindowQmlImpl
+class Q_QUICKTEMPLATES2_EXPORT QQuickApplicationWindow : public QQuickWindowQmlImpl
{
Q_OBJECT
Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL)
@@ -99,7 +99,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_updateActiveFocus())
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickApplicationWindowAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickApplicationWindowAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickApplicationWindow *window READ window NOTIFY windowChanged FINAL)
@@ -133,32 +133,6 @@ private:
Q_DECLARE_PRIVATE(QQuickApplicationWindowAttached)
};
-struct QWindowForeign2
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QWindow)
- QML_ADDED_IN_VERSION(2, 0)
-};
-
-struct QQuickWindowForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickWindow)
- QML_ADDED_IN_VERSION(2, 0)
-};
-
-struct QQuickWindowQmlImplForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickWindowQmlImpl)
- QML_ADDED_IN_VERSION(2, 2)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickApplicationWindow)
-
#endif // QQUICKAPPLICATIONWINDOW_P_H
diff --git a/src/quicktemplates/qquickbusyindicator_p.h b/src/quicktemplates/qquickbusyindicator_p.h
index 46206b7d78..357d327eb8 100644
--- a/src/quicktemplates/qquickbusyindicator_p.h
+++ b/src/quicktemplates/qquickbusyindicator_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickBusyIndicatorPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickBusyIndicator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickBusyIndicator : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged FINAL)
@@ -53,6 +53,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickBusyIndicator)
-
#endif // QQUICKBUSYINDICATOR_P_H
diff --git a/src/quicktemplates/qquickbutton_p.h b/src/quicktemplates/qquickbutton_p.h
index 1092274ee6..32ebeb563d 100644
--- a/src/quicktemplates/qquickbutton_p.h
+++ b/src/quicktemplates/qquickbutton_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButton : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickButton : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL)
@@ -54,6 +54,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickButton)
-
#endif // QQUICKBUTTON_P_H
diff --git a/src/quicktemplates/qquickbuttongroup.cpp b/src/quicktemplates/qquickbuttongroup.cpp
index 89a64720dc..d57b82c8e7 100644
--- a/src/quicktemplates/qquickbuttongroup.cpp
+++ b/src/quicktemplates/qquickbuttongroup.cpp
@@ -143,9 +143,15 @@ public:
void QQuickButtonGroupPrivate::clear()
{
for (QQuickAbstractButton *button : std::as_const(buttons)) {
- QQuickAbstractButtonPrivate::get(button)->group = nullptr;
- QObjectPrivate::disconnect(button, &QQuickAbstractButton::clicked, this, &QQuickButtonGroupPrivate::buttonClicked);
- QObjectPrivate::disconnect(button, &QQuickAbstractButton::checkedChanged, this, &QQuickButtonGroupPrivate::_q_updateCurrent);
+ auto *attached = qobject_cast<QQuickButtonGroupAttached *>(
+ qmlAttachedPropertiesObject<QQuickButtonGroup>(button, false));
+ if (attached) {
+ attached->setGroup(nullptr);
+ } else {
+ QQuickAbstractButtonPrivate::get(button)->group = nullptr;
+ QObjectPrivate::disconnect(button, &QQuickAbstractButton::clicked, this, &QQuickButtonGroupPrivate::buttonClicked);
+ QObjectPrivate::disconnect(button, &QQuickAbstractButton::checkedChanged, this, &QQuickButtonGroupPrivate::_q_updateCurrent);
+ }
}
buttons.clear();
}
@@ -500,11 +506,12 @@ void QQuickButtonGroupAttached::setGroup(QQuickButtonGroup *group)
if (d->group == group)
return;
+ auto *button = qobject_cast<QQuickAbstractButton *>(parent());
if (d->group)
- d->group->removeButton(qobject_cast<QQuickAbstractButton*>(parent()));
+ d->group->removeButton(button);
d->group = group;
if (group)
- group->addButton(qobject_cast<QQuickAbstractButton*>(parent()));
+ group->addButton(button);
emit groupChanged();
}
diff --git a/src/quicktemplates/qquickbuttongroup_p.h b/src/quicktemplates/qquickbuttongroup_p.h
index 6bdd619c1c..a7ac05b01f 100644
--- a/src/quicktemplates/qquickbuttongroup_p.h
+++ b/src/quicktemplates/qquickbuttongroup_p.h
@@ -27,7 +27,7 @@ class QQuickButtonGroupPrivate;
class QQuickButtonGroupAttached;
class QQuickButtonGroupAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButtonGroup : public QObject, public QQmlParserStatus
+class Q_QUICKTEMPLATES2_EXPORT QQuickButtonGroup : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_PROPERTY(QQuickAbstractButton *checkedButton READ checkedButton WRITE setCheckedButton NOTIFY checkedButtonChanged FINAL)
@@ -84,7 +84,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_updateCurrent())
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButtonGroupAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickButtonGroupAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickButtonGroup *group READ group WRITE setGroup NOTIFY groupChanged FINAL)
@@ -105,6 +105,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickButtonGroup)
-
#endif // QQUICKBUTTONGROUP_P_H
diff --git a/src/quicktemplates/qquickcalendar_p.h b/src/quicktemplates/qquickcalendar_p.h
index 37dac5595a..dbee98a737 100644
--- a/src/quicktemplates/qquickcalendar_p.h
+++ b/src/quicktemplates/qquickcalendar_p.h
@@ -50,6 +50,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCalendar)
-
#endif // QQUICKCALENDAR_P_H
diff --git a/src/quicktemplates/qquickcalendarmodel.cpp b/src/quicktemplates/qquickcalendarmodel.cpp
index 0bac0969e0..bb3e2a7796 100644
--- a/src/quicktemplates/qquickcalendarmodel.cpp
+++ b/src/quicktemplates/qquickcalendarmodel.cpp
@@ -42,9 +42,9 @@ public:
{
}
- static int getCount(const QDate& from, const QDate &to);
+ static int getCount(QDate from, QDate to);
- void populate(const QDate &from, const QDate &to, bool force = false);
+ void populate(QDate from, QDate to, bool force = false);
bool complete;
QDate from;
@@ -52,24 +52,26 @@ public:
int count;
};
-int QQuickCalendarModelPrivate::getCount(const QDate& from, const QDate &to)
+// Returns the number of months we need to display for both from and to to be shown,
+// or zero if from is in a later month than to, or either is invalid.
+int QQuickCalendarModelPrivate::getCount(QDate from, QDate to)
{
if (!from.isValid() || !to.isValid())
return 0;
- QDate f(from.year(), from.month(), 1);
- QDate t(to.year(), to.month(), to.daysInMonth());
- int days = f.daysTo(t);
- if (days < 0)
+ const QCalendar gregorian;
+ Q_ASSERT(gregorian.isGregorian());
+ const QCalendar::YearMonthDay &f = gregorian.partsFromDate(from);
+ const QCalendar::YearMonthDay &t = gregorian.partsFromDate(to);
+ Q_ASSERT(f.isValid() && t.isValid()); // ... because from and to are valid.
+ if (f.year > t.year || (f.year == t.year && f.month > t.month))
return 0;
- QDate r = QDate(1, 1, 1).addDays(days);
- int years = r.year() - 1;
- int months = r.month() - 1;
- return 12 * years + months + (r.day() / t.day());
+ // Count from's month and every subsequent month until to's:
+ return 1 + t.month + 12 * (t.year - f.year) - f.month;
}
-void QQuickCalendarModelPrivate::populate(const QDate &f, const QDate &t, bool force)
+void QQuickCalendarModelPrivate::populate(QDate f, QDate t, bool force)
{
Q_Q(QQuickCalendarModel);
if (!force && f == from && t == to)
@@ -102,7 +104,7 @@ QDate QQuickCalendarModel::from() const
return d->from;
}
-void QQuickCalendarModel::setFrom(const QDate &from)
+void QQuickCalendarModel::setFrom(QDate from)
{
Q_D(QQuickCalendarModel);
if (d->from != from) {
@@ -124,7 +126,7 @@ QDate QQuickCalendarModel::to() const
return d->to;
}
-void QQuickCalendarModel::setTo(const QDate &to)
+void QQuickCalendarModel::setTo(QDate to)
{
Q_D(QQuickCalendarModel);
if (d->to != to) {
@@ -162,7 +164,7 @@ int QQuickCalendarModel::yearAt(int index) const
Returns the model index of the specified \a date.
*/
-int QQuickCalendarModel::indexOf(const QDate &date) const
+int QQuickCalendarModel::indexOf(QDate date) const
{
Q_D(const QQuickCalendarModel);
return d->getCount(d->from, date) - 1;
diff --git a/src/quicktemplates/qquickcalendarmodel_p.h b/src/quicktemplates/qquickcalendarmodel_p.h
index e9dbf6d075..face2b29cf 100644
--- a/src/quicktemplates/qquickcalendarmodel_p.h
+++ b/src/quicktemplates/qquickcalendarmodel_p.h
@@ -39,14 +39,14 @@ public:
explicit QQuickCalendarModel(QObject *parent = nullptr);
QDate from() const;
- void setFrom(const QDate &from);
+ void setFrom(QDate from);
QDate to() const;
- void setTo(const QDate &to);
+ void setTo(QDate to);
Q_INVOKABLE int monthAt(int index) const;
Q_INVOKABLE int yearAt(int index) const;
- Q_INVOKABLE int indexOf(const QDate &date) const;
+ Q_INVOKABLE int indexOf(QDate date) const;
Q_INVOKABLE int indexOf(int year, int month) const;
enum {
@@ -74,6 +74,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCalendarModel)
-
#endif // QQUICKCALENDARMODEL_P_H
diff --git a/src/quicktemplates/qquickcheckbox.cpp b/src/quicktemplates/qquickcheckbox.cpp
index 21038ccdb9..539e89f2d0 100644
--- a/src/quicktemplates/qquickcheckbox.cpp
+++ b/src/quicktemplates/qquickcheckbox.cpp
@@ -69,8 +69,6 @@ class QQuickCheckBoxPrivate : public QQuickAbstractButtonPrivate
Q_DECLARE_PUBLIC(QQuickCheckBox)
public:
- void setNextCheckState(const QJSValue &callback);
-
QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::CheckBox); }
bool tristate = false;
@@ -78,13 +76,6 @@ public:
QJSValue nextCheckState;
};
-void QQuickCheckBoxPrivate::setNextCheckState(const QJSValue &callback)
-{
- Q_Q(QQuickCheckBox);
- nextCheckState = callback;
- emit q->nextCheckStateChanged();
-}
-
QQuickCheckBox::QQuickCheckBox(QQuickItem *parent)
: QQuickAbstractButton(*(new QQuickCheckBoxPrivate), parent)
{
@@ -150,6 +141,19 @@ void QQuickCheckBox::setCheckState(Qt::CheckState state)
emit checkedChanged();
}
+QJSValue QQuickCheckBox::getNextCheckState() const
+{
+ Q_D(const QQuickCheckBox);
+ return d->nextCheckState;
+}
+
+void QQuickCheckBox::setNextCheckState(const QJSValue &callback)
+{
+ Q_D(QQuickCheckBox);
+ d->nextCheckState = callback;
+ emit nextCheckStateChanged();
+}
+
QFont QQuickCheckBox::defaultFont() const
{
return QQuickTheme::font(QQuickTheme::CheckBox);
diff --git a/src/quicktemplates/qquickcheckbox_p.h b/src/quicktemplates/qquickcheckbox_p.h
index 5adeb44ede..b1a779dacd 100644
--- a/src/quicktemplates/qquickcheckbox_p.h
+++ b/src/quicktemplates/qquickcheckbox_p.h
@@ -21,13 +21,14 @@ QT_BEGIN_NAMESPACE
class QQuickCheckBoxPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickCheckBox : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickCheckBox : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool tristate READ isTristate WRITE setTristate NOTIFY tristateChanged FINAL)
Q_PROPERTY(Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL)
// 2.4 (Qt 5.11)
- Q_PRIVATE_PROPERTY(QQuickCheckBox::d_func(), QJSValue nextCheckState MEMBER nextCheckState WRITE setNextCheckState NOTIFY nextCheckStateChanged FINAL REVISION(2, 4))
+ Q_PROPERTY(QJSValue nextCheckState READ getNextCheckState WRITE setNextCheckState NOTIFY
+ nextCheckStateChanged FINAL REVISION(2, 4))
QML_NAMED_ELEMENT(CheckBox)
QML_ADDED_IN_VERSION(2, 0)
@@ -39,6 +40,8 @@ public:
Qt::CheckState checkState() const;
void setCheckState(Qt::CheckState state);
+ QJSValue getNextCheckState() const;
+ void setNextCheckState(const QJSValue &callback);
Q_SIGNALS:
void tristateChanged();
@@ -59,6 +62,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCheckBox)
-
#endif // QQUICKCHECKBOX_P_H
diff --git a/src/quicktemplates/qquickcheckdelegate_p.h b/src/quicktemplates/qquickcheckdelegate_p.h
index 8c7c3e8c74..8e40ef68e7 100644
--- a/src/quicktemplates/qquickcheckdelegate_p.h
+++ b/src/quicktemplates/qquickcheckdelegate_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickCheckDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickCheckDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickCheckDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(bool tristate READ isTristate WRITE setTristate NOTIFY tristateChanged FINAL)
@@ -63,6 +63,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCheckDelegate)
-
#endif // QQUICKCHECKDELEGATE_P_H
diff --git a/src/quicktemplates/qquickcombobox.cpp b/src/quicktemplates/qquickcombobox.cpp
index 31eb97e31d..c55e988b74 100644
--- a/src/quicktemplates/qquickcombobox.cpp
+++ b/src/quicktemplates/qquickcombobox.cpp
@@ -23,7 +23,9 @@
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquicktextinput_p_p.h>
+#if QT_CONFIG(quick_itemview)
#include <QtQuick/private/qquickitemview_p.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -209,6 +211,7 @@ public:
void hidePopup(bool accept);
void togglePopup(bool accept);
void popupVisibleChanged();
+ void popupDestroyed();
void itemClicked();
void itemHovered();
@@ -253,6 +256,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
void setInputMethodHints(Qt::InputMethodHints hints, bool force = false);
@@ -342,14 +346,18 @@ void QQuickComboBoxPrivate::popupVisibleChanged()
if (isPopupVisible())
QGuiApplication::inputMethod()->reset();
+#if QT_CONFIG(quick_itemview)
QQuickItemView *itemView = popup->findChild<QQuickItemView *>();
if (itemView)
itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
+#endif
updateHighlightedIndex();
+#if QT_CONFIG(quick_itemview)
if (itemView)
itemView->positionViewAtIndex(highlightedIndex, QQuickItemView::Beginning);
+#endif
if (!hasDown) {
q->setDown(pressed || isPopupVisible());
@@ -357,6 +365,13 @@ void QQuickComboBoxPrivate::popupVisibleChanged()
}
}
+void QQuickComboBoxPrivate::popupDestroyed()
+{
+ Q_Q(QQuickComboBox);
+ popup = nullptr;
+ emit q->popupChanged();
+}
+
void QQuickComboBoxPrivate::itemClicked()
{
Q_Q(QQuickComboBox);
@@ -381,8 +396,10 @@ void QQuickComboBoxPrivate::itemHovered()
if (index != -1) {
setHighlightedIndex(index, Highlight);
+#if QT_CONFIG(quick_itemview)
if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
itemView->positionViewAtIndex(index, QQuickItemView::Contain);
+#endif
}
}
@@ -767,8 +784,6 @@ void QQuickComboBoxPrivate::handleUngrab()
q->setPressed(false);
}
-static inline QString indicatorName() { return QStringLiteral("indicator"); }
-
void QQuickComboBoxPrivate::cancelIndicator()
{
Q_Q(QQuickComboBox);
@@ -833,6 +848,16 @@ void QQuickComboBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit q->implicitIndicatorHeightChanged();
}
+void QQuickComboBoxPrivate::itemDestroyed(QQuickItem *item)
+{
+ Q_Q(QQuickComboBox);
+ QQuickControlPrivate::itemDestroyed(item);
+ if (item == indicator) {
+ indicator = nullptr;
+ emit q->indicatorChanged();
+ }
+}
+
qreal QQuickComboBoxPrivate::getContentWidth() const
{
if (componentComplete) {
@@ -929,6 +954,7 @@ QQuickComboBox::QQuickComboBox(QQuickItem *parent)
#endif
Q_D(QQuickComboBox);
d->setInputMethodHints(Qt::ImhNoPredictiveText, true);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
QQuickComboBox::~QQuickComboBox()
@@ -1333,6 +1359,7 @@ void QQuickComboBox::setPopup(QQuickPopup *popup)
d->cancelPopup();
if (d->popup) {
+ QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::destroyed, d, &QQuickComboBoxPrivate::popupDestroyed);
QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
QQuickComboBoxPrivate::hideOldPopup(d->popup);
}
@@ -1340,9 +1367,14 @@ void QQuickComboBox::setPopup(QQuickPopup *popup)
QQuickPopupPrivate::get(popup)->allowVerticalFlip = true;
popup->setClosePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent);
QObjectPrivate::connect(popup, &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
+ // QQuickPopup does not derive from QQuickItemChangeListener, so we cannot use
+ // QQuickItemChangeListener::itemDestroyed so we have to use QObject::destroyed
+ QObjectPrivate::connect(popup, &QQuickPopup::destroyed, d, &QQuickComboBoxPrivate::popupDestroyed);
+#if QT_CONFIG(quick_itemview)
if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
+#endif
}
d->popup = popup;
if (!d->popup.isExecuting())
@@ -2056,7 +2088,7 @@ void QQuickComboBox::keyReleaseEvent(QKeyEvent *event)
if (!isEditable()) {
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
if (buttonPressKeys.contains(key)) {
- if (!isEditable())
+ if (!isEditable() && isPressed())
d->togglePopup(true);
setPressed(false);
event->accept();
diff --git a/src/quicktemplates/qquickcombobox_p.h b/src/quicktemplates/qquickcombobox_p.h
index 303eef21b0..2ef5dc776c 100644
--- a/src/quicktemplates/qquickcombobox_p.h
+++ b/src/quicktemplates/qquickcombobox_p.h
@@ -16,8 +16,11 @@
//
#include <QtCore/qloggingcategory.h>
+#include <QtQmlModels/private/qtqmlmodels-config_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
+QT_REQUIRE_CONFIG(qml_delegate_model);
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcItemManagement)
@@ -27,7 +30,7 @@ class QQuickPopup;
class QQmlInstanceModel;
class QQuickComboBoxPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickComboBox : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickComboBox : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged FINAL)
@@ -240,6 +243,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickComboBox)
-
#endif // QQUICKCOMBOBOX_P_H
diff --git a/src/quicktemplates/qquickcontainer.cpp b/src/quicktemplates/qquickcontainer.cpp
index eefc9161e9..115168083c 100644
--- a/src/quicktemplates/qquickcontainer.cpp
+++ b/src/quicktemplates/qquickcontainer.cpp
@@ -116,7 +116,7 @@ QT_BEGIN_NAMESPACE
Container does not provide any default visualization. It is used to implement
such containers as \l SwipeView and \l TabBar. When implementing a custom
container, the most important part of the API is \l contentModel, which provides
- the contained items in a way that it can be used as a delegate model for item
+ the contained items in a way that it can be used as an object model for item
views and repeaters.
\code
@@ -167,6 +167,7 @@ void QQuickContainerPrivate::init()
QObject::connect(contentModel, &QQmlObjectModel::childrenChanged, q, &QQuickContainer::contentChildrenChanged);
connect(q, &QQuickControl::implicitContentWidthChanged, this, &QQuickContainerPrivate::updateContentWidth);
connect(q, &QQuickControl::implicitContentHeightChanged, this, &QQuickContainerPrivate::updateContentHeight);
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
void QQuickContainerPrivate::cleanup()
@@ -256,7 +257,8 @@ void QQuickContainerPrivate::moveItem(int from, int to, QQuickItem *item)
void QQuickContainerPrivate::removeItem(int index, QQuickItem *item)
{
Q_Q(QQuickContainer);
- if (!q->isContent(item))
+ const bool item_inDestructor = QQuickItemPrivate::get(item)->inDestructor;
+ if (!item_inDestructor && !q->isContent(item))
return;
contentData.removeOne(item);
@@ -271,8 +273,11 @@ void QQuickContainerPrivate::removeItem(int index, QQuickItem *item)
currentChanged = true;
}
- QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes);
- item->setParentItem(nullptr);
+ if (!item_inDestructor) {
+ // already handled by ~QQuickItem
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes);
+ item->setParentItem(nullptr);
+ }
contentModel->remove(index);
--count;
diff --git a/src/quicktemplates/qquickcontainer_p.h b/src/quicktemplates/qquickcontainer_p.h
index 7fdf606571..7a71d5bdf6 100644
--- a/src/quicktemplates/qquickcontainer_p.h
+++ b/src/quicktemplates/qquickcontainer_p.h
@@ -18,11 +18,13 @@
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
#include <QtQml/qqmllist.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickContainerPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickContainer : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickContainer : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged FINAL)
@@ -103,6 +105,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickContainer)
-
#endif // QQUICKCONTAINER_P_H
diff --git a/src/quicktemplates/qquickcontainer_p_p.h b/src/quicktemplates/qquickcontainer_p_p.h
index 84fe62a75d..a486e8cba7 100644
--- a/src/quicktemplates/qquickcontainer_p_p.h
+++ b/src/quicktemplates/qquickcontainer_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickContainerPrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickContainerPrivate : public QQuickControlPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickContainer)
diff --git a/src/quicktemplates/qquickcontentitem_p.h b/src/quicktemplates/qquickcontentitem_p.h
index 446489ee70..77f7f14c64 100644
--- a/src/quicktemplates/qquickcontentitem_p.h
+++ b/src/quicktemplates/qquickcontentitem_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickContentItem : public QQuickItem
+class Q_QUICKTEMPLATES2_EXPORT QQuickContentItem : public QQuickItem
{
Q_OBJECT
diff --git a/src/quicktemplates/qquickcontrol.cpp b/src/quicktemplates/qquickcontrol.cpp
index 764b6ca003..72d298d0af 100644
--- a/src/quicktemplates/qquickcontrol.cpp
+++ b/src/quicktemplates/qquickcontrol.cpp
@@ -107,6 +107,8 @@ Q_LOGGING_CATEGORY(lcItemManagement, "qt.quick.controls.control.itemmanagement")
}
\endcode
+ Wheel events are consumed by controls if \l wheelEnabled is \c true.
+
\sa ApplicationWindow, Container, {Using Qt Quick Controls types in
property declarations}
*/
@@ -150,21 +152,8 @@ bool QQuickControlPrivate::acceptTouch(const QTouchEvent::TouchPoint &point)
}
#endif
-static void setActiveFocus(QQuickControl *control, Qt::FocusReason reason)
-{
- QQuickControlPrivate *d = QQuickControlPrivate::get(control);
- if (d->subFocusItem && d->window && d->flags & QQuickItem::ItemIsFocusScope)
- QQuickWindowPrivate::get(d->window)->clearFocusInScope(control, d->subFocusItem, reason);
- control->forceActiveFocus(reason);
-}
-
bool QQuickControlPrivate::handlePress(const QPointF &, ulong)
{
- Q_Q(QQuickControl);
- if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && !QGuiApplication::styleHints()->setFocusOnTouchRelease()) {
- setActiveFocus(q, Qt::MouseFocusReason);
- return true;
- }
return true;
}
@@ -181,14 +170,8 @@ bool QQuickControlPrivate::handleMove(const QPointF &point, ulong)
bool QQuickControlPrivate::handleRelease(const QPointF &, ulong)
{
- Q_Q(QQuickControl);
- bool accepted = true;
- if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && QGuiApplication::styleHints()->setFocusOnTouchRelease()) {
- setActiveFocus(q, Qt::MouseFocusReason);
- accepted = true;
- }
touchId = -1;
- return accepted;
+ return true;
}
void QQuickControlPrivate::handleUngrab()
@@ -352,12 +335,22 @@ void QQuickControlPrivate::resizeBackground()
bool changeHeight = false;
if (((!p->widthValid() || !extra.isAllocated() || !extra->hasBackgroundWidth) && qFuzzyIsNull(background->x()))
|| (extra.isAllocated() && (extra->hasLeftInset || extra->hasRightInset))) {
- background->setX(getLeftInset());
+ const auto leftInset = getLeftInset();
+ if (!qt_is_nan(leftInset) && p->x.valueBypassingBindings() != leftInset) {
+ // We bypass the binding here to prevent it from being removed
+ p->x.setValueBypassingBindings(leftInset);
+ p->dirty(DirtyType::Position);
+ }
changeWidth = !p->width.hasBinding();
}
if (((!p->heightValid() || !extra.isAllocated() || !extra->hasBackgroundHeight) && qFuzzyIsNull(background->y()))
|| (extra.isAllocated() && (extra->hasTopInset || extra->hasBottomInset))) {
- background->setY(getTopInset());
+ const auto topInset = getTopInset();
+ if (!qt_is_nan(topInset) && p->y.valueBypassingBindings() != topInset) {
+ // We bypass the binding here to prevent it from being removed
+ p->y.setValueBypassingBindings(topInset);
+ p->dirty(DirtyType::Position);
+ }
changeHeight = !p->height.hasBinding();
}
if (changeHeight || changeWidth) {
@@ -404,8 +397,7 @@ void QQuickControlPrivate::setContentItem_helper(QQuickItem *item, bool notify)
QQuickItem *oldContentItem = contentItem;
if (oldContentItem) {
disconnect(oldContentItem, &QQuickItem::baselineOffsetChanged, this, &QQuickControlPrivate::updateBaselineOffset);
- if (oldContentItem)
- QQuickItemPrivate::get(oldContentItem)->removeItemChangeListener(this, QQuickControlPrivate::Focus);
+ QQuickItemPrivate::get(oldContentItem)->removeItemChangeListener(this, QQuickControlPrivate::Focus);
removeImplicitSizeListener(oldContentItem);
}
@@ -751,8 +743,6 @@ void QQuickControlPrivate::executeContentItem(bool complete)
quickCompleteDeferred(q, contentItemName(), contentItem);
}
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickControlPrivate::cancelBackground()
{
Q_Q(QQuickControl);
@@ -802,13 +792,25 @@ void QQuickControlPrivate::hideOldItem(QQuickItem *item)
Named "unhide" because it's used for cases where an item
that was previously hidden by \l hideOldItem() wants to be
shown by a control again, such as a ScrollBar in ScrollView.
+
+ \a visibility controls the visibility of \a item, as there
+ may have been bindings that controlled visibility, such as
+ with a typical ScrollBar.qml implementation:
+
+ \code
+ visible: control.policy !== T.ScrollBar.AlwaysOff
+ \endcode
+
+ In the future we could try to save the binding for the visible
+ property (using e.g. QQmlAnyBinding::takeFrom), but for now we
+ keep it simple and just allow restoring an equivalent literal value.
*/
-void QQuickControlPrivate::unhideOldItem(QQuickControl *control, QQuickItem *item)
+void QQuickControlPrivate::unhideOldItem(QQuickControl *control, QQuickItem *item, UnhideVisibility visibility)
{
Q_ASSERT(item);
qCDebug(lcItemManagement) << "unhiding old item" << item;
- item->setVisible(true);
+ item->setVisible(visibility == UnhideVisibility::Show);
item->setParentItem(control);
#if QT_CONFIG(accessibility)
@@ -906,7 +908,7 @@ void QQuickControlPrivate::itemFocusChanged(QQuickItem *item, Qt::FocusReason re
{
Q_Q(QQuickControl);
if (item == contentItem || item == q)
- q->setFocusReason(reason);
+ setLastFocusChangeReason(reason);
}
QQuickControl::QQuickControl(QQuickItem *parent)
@@ -962,7 +964,7 @@ void QQuickControl::itemChange(QQuickItem::ItemChange change, const QQuickItem::
}
break;
case ItemActiveFocusHasChanged:
- if (isKeyFocusReason(d->focusReason))
+ if (isKeyFocusReason(static_cast<Qt::FocusReason>(d->focusReason)))
emit visualFocusChanged();
break;
default:
@@ -1352,58 +1354,38 @@ bool QQuickControl::isMirrored() const
}
/*!
- \qmlproperty enumeration QtQuick.Controls::Control::focusPolicy
-
- This property determines the way the control accepts focus.
-
- \value Qt.TabFocus The control accepts focus by tabbing.
- \value Qt.ClickFocus The control accepts focus by clicking.
- \value Qt.StrongFocus The control accepts focus by both tabbing and clicking.
- \value Qt.WheelFocus The control accepts focus by tabbing, clicking, and using the mouse wheel.
- \value Qt.NoFocus The control does not accept focus.
-*/
-Qt::FocusPolicy QQuickControl::focusPolicy() const
-{
- Q_D(const QQuickControl);
- uint policy = d->focusPolicy;
- if (activeFocusOnTab())
- policy |= Qt::TabFocus;
- return static_cast<Qt::FocusPolicy>(policy);
-}
+ \qmlproperty enumeration QtQuick.Controls::Control::focusReason
+ \readonly
-void QQuickControl::setFocusPolicy(Qt::FocusPolicy policy)
-{
- Q_D(QQuickControl);
- if (d->focusPolicy == policy)
- return;
+ This property holds the reason of the last focus change.
- d->focusPolicy = policy;
- setActiveFocusOnTab(policy & Qt::TabFocus);
- emit focusPolicyChanged();
-}
+ \note This property does not indicate whether the item has \l {Item::activeFocus}
+ {active focus}, but the reason why the item either gained or lost focus.
-/*!
- \qmlproperty enumeration QtQuick.Controls::Control::focusReason
- \readonly
+ \value Qt.MouseFocusReason A mouse action occurred.
+ \value Qt.TabFocusReason The Tab key was pressed.
+ \value Qt.BacktabFocusReason A Backtab occurred. The input for this may include the Shift or Control keys; e.g. Shift+Tab.
+ \value Qt.ActiveWindowFocusReason The window system made this window either active or inactive.
+ \value Qt.PopupFocusReason The application opened/closed a pop-up that grabbed/released the keyboard focus.
+ \value Qt.ShortcutFocusReason The user typed a label's buddy shortcut
+ \value Qt.MenuBarFocusReason The menu bar took focus.
+ \value Qt.OtherFocusReason Another reason, usually application-specific.
- \include qquickcontrol-focusreason.qdocinc
+ \sa Item::activeFocus
\sa visualFocus
*/
Qt::FocusReason QQuickControl::focusReason() const
{
Q_D(const QQuickControl);
- return d->focusReason;
+ return d->lastFocusChangeReason();
}
void QQuickControl::setFocusReason(Qt::FocusReason reason)
{
Q_D(QQuickControl);
- if (d->focusReason == reason)
- return;
-
- Qt::FocusReason oldReason = d->focusReason;
- d->focusReason = reason;
+ Qt::FocusReason oldReason = static_cast<Qt::FocusReason>(d->focusReason);
+ d->setLastFocusChangeReason(reason);
emit focusReasonChanged();
if (isKeyFocusReason(oldReason) != isKeyFocusReason(reason))
emit visualFocusChanged();
@@ -1421,12 +1403,12 @@ void QQuickControl::setFocusReason(Qt::FocusReason reason)
\l Item::activeFocus. This ensures that key focus is only visualized when
interacting with keys - not when interacting via touch or mouse.
- \sa focusReason, Item::activeFocus
+ \sa Item::focusReason, Item::activeFocus
*/
bool QQuickControl::hasVisualFocus() const
{
Q_D(const QQuickControl);
- return d->activeFocus && isKeyFocusReason(d->focusReason);
+ return d->activeFocus && isKeyFocusReason(static_cast<Qt::FocusReason>(d->focusReason));
}
/*!
@@ -1987,14 +1969,22 @@ QFont QQuickControl::defaultFont() const
void QQuickControl::focusInEvent(QFocusEvent *event)
{
+ Q_D(QQuickControl);
+ Qt::FocusReason oldReason = static_cast<Qt::FocusReason>(d->focusReason);
QQuickItem::focusInEvent(event);
- setFocusReason(event->reason());
+ Qt::FocusReason reason = event->reason();
+ if (isKeyFocusReason(oldReason) != isKeyFocusReason(reason))
+ emit visualFocusChanged();
}
void QQuickControl::focusOutEvent(QFocusEvent *event)
{
+ Q_D(QQuickControl);
+ Qt::FocusReason oldReason = static_cast<Qt::FocusReason>(d->focusReason);
QQuickItem::focusOutEvent(event);
- setFocusReason(event->reason());
+ Qt::FocusReason reason = event->reason();
+ if (isKeyFocusReason(oldReason) != isKeyFocusReason(reason))
+ emit visualFocusChanged();
}
#if QT_CONFIG(quicktemplates2_hover)
@@ -2092,9 +2082,6 @@ void QQuickControl::touchUngrabEvent()
void QQuickControl::wheelEvent(QWheelEvent *event)
{
Q_D(QQuickControl);
- if ((d->focusPolicy & Qt::WheelFocus) == Qt::WheelFocus)
- setActiveFocus(this, Qt::MouseFocusReason);
-
event->setAccepted(d->wheelEnabled);
}
#endif
@@ -2175,12 +2162,13 @@ QAccessible::Role QQuickControl::accessibleRole() const
void QQuickControl::accessibilityActiveChanged(bool active)
{
+ Q_D(QQuickControl);
if (!active)
return;
QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(this, true));
Q_ASSERT(accessibleAttached);
- accessibleAttached->setRole(accessibleRole());
+ accessibleAttached->setRole(d->effectiveAccessibleRole());
}
#endif
diff --git a/src/quicktemplates/qquickcontrol_p.h b/src/quicktemplates/qquickcontrol_p.h
index a607a159f3..bf17cf10bc 100644
--- a/src/quicktemplates/qquickcontrol_p.h
+++ b/src/quicktemplates/qquickcontrol_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickControlPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickControl : public QQuickItem
+class Q_QUICKTEMPLATES2_EXPORT QQuickControl : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont RESET resetFont NOTIFY fontChanged FINAL)
@@ -39,7 +39,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickControl : public QQuickItem
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing RESET resetSpacing NOTIFY spacingChanged FINAL)
Q_PROPERTY(QLocale locale READ locale WRITE setLocale RESET resetLocale NOTIFY localeChanged FINAL)
Q_PROPERTY(bool mirrored READ isMirrored NOTIFY mirroredChanged FINAL)
- Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged FINAL)
+ QT6_ONLY(Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged FINAL))
Q_PROPERTY(Qt::FocusReason focusReason READ focusReason WRITE setFocusReason NOTIFY focusReasonChanged FINAL)
Q_PROPERTY(bool visualFocus READ hasVisualFocus NOTIFY visualFocusChanged FINAL)
Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged FINAL)
@@ -104,9 +104,6 @@ public:
bool isMirrored() const;
- Qt::FocusPolicy focusPolicy() const;
- void setFocusPolicy(Qt::FocusPolicy policy);
-
Qt::FocusReason focusReason() const;
void setFocusReason(Qt::FocusReason reason);
@@ -174,9 +171,8 @@ Q_SIGNALS:
void bottomPaddingChanged();
void spacingChanged();
void localeChanged();
- void mirroredChanged();
- void focusPolicyChanged();
void focusReasonChanged();
+ void mirroredChanged();
void visualFocusChanged();
void hoveredChanged();
void hoverEnabledChanged();
@@ -256,32 +252,6 @@ private:
Q_DECLARE_PRIVATE(QQuickControl)
};
-struct QQuickItemForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickItem)
- QML_ADDED_IN_VERSION(2, 3)
-};
-
-struct QQuickColorGroupForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickColorGroup)
- QML_ADDED_IN_VERSION(6, 0)
-};
-
-struct QQuickPaletteForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickPalette)
- QML_ADDED_IN_VERSION(6, 0)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickControl)
-
#endif // QQUICKCONTROL_P_H
diff --git a/src/quicktemplates/qquickcontrol_p_p.h b/src/quicktemplates/qquickcontrol_p_p.h
index d19c76dfe3..c58ee33ede 100644
--- a/src/quicktemplates/qquickcontrol_p_p.h
+++ b/src/quicktemplates/qquickcontrol_p_p.h
@@ -35,7 +35,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcItemManagement)
class QQuickAccessibleAttached;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickControlPrivate : public QQuickItemPrivate, public QQuickItemChangeListener
+class Q_QUICKTEMPLATES2_EXPORT QQuickControlPrivate : public QQuickItemPrivate, public QQuickItemChangeListener
#if QT_CONFIG(accessibility)
, public QAccessible::ActivationObserver
#endif
@@ -130,8 +130,14 @@ public:
virtual void cancelBackground();
virtual void executeBackground(bool complete = false);
+ enum class UnhideVisibility {
+ Show,
+ Hide
+ };
+
static void hideOldItem(QQuickItem *item);
- static void unhideOldItem(QQuickControl *control, QQuickItem *item);
+ static void unhideOldItem(QQuickControl *control, QQuickItem *item,
+ UnhideVisibility visibility = UnhideVisibility::Show);
void updateBaselineOffset();
@@ -200,8 +206,6 @@ public:
qreal spacing = 0;
QLocale locale;
QFont resolvedFont;
- Qt::FocusPolicy focusPolicy = Qt::NoFocus;
- Qt::FocusReason focusReason = Qt::OtherFocusReason;
QQuickDeferredPointer<QQuickItem> background;
QQuickDeferredPointer<QQuickItem> contentItem;
};
diff --git a/src/quicktemplates/qquickdayofweekmodel_p.h b/src/quicktemplates/qquickdayofweekmodel_p.h
index ba80a43077..f3df4edab8 100644
--- a/src/quicktemplates/qquickdayofweekmodel_p.h
+++ b/src/quicktemplates/qquickdayofweekmodel_p.h
@@ -59,6 +59,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDayOfWeekModel)
-
#endif // QQUICKDAYOFWEEKMODEL_P_H
diff --git a/src/quicktemplates/qquickdayofweekrow_p.h b/src/quicktemplates/qquickdayofweekrow_p.h
index 3b5c062196..7c2a2398fe 100644
--- a/src/quicktemplates/qquickdayofweekrow_p.h
+++ b/src/quicktemplates/qquickdayofweekrow_p.h
@@ -56,6 +56,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDayOfWeekRow)
-
#endif // QQUICKDAYOFWEEKROW_P_H
diff --git a/src/quicktemplates/qquickdeferredexecute.cpp b/src/quicktemplates/qquickdeferredexecute.cpp
index 239b0fa6a6..19870a5636 100644
--- a/src/quicktemplates/qquickdeferredexecute.cpp
+++ b/src/quicktemplates/qquickdeferredexecute.cpp
@@ -30,6 +30,9 @@ static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &pro
QQmlData *ddata = QQmlData::get(object);
Q_ASSERT(!ddata->deferredData.isEmpty());
+ if (!ddata->propertyCache)
+ ddata->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
+
int propertyIndex = property.index();
int wasInProgress = enginePriv->inProgressCreations;
diff --git a/src/quicktemplates/qquickdeferredexecute_p_p.h b/src/quicktemplates/qquickdeferredexecute_p_p.h
index beecb3636b..31d0f887cf 100644
--- a/src/quicktemplates/qquickdeferredexecute_p_p.h
+++ b/src/quicktemplates/qquickdeferredexecute_p_p.h
@@ -27,9 +27,9 @@ class QString;
class QObject;
namespace QtQuickPrivate {
- Q_QUICKTEMPLATES2_PRIVATE_EXPORT void beginDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate, bool isOwnState);
- Q_QUICKTEMPLATES2_PRIVATE_EXPORT void cancelDeferred(QObject *object, const QString &property);
- Q_QUICKTEMPLATES2_PRIVATE_EXPORT void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate);
+ Q_QUICKTEMPLATES2_EXPORT void beginDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate, bool isOwnState);
+ Q_QUICKTEMPLATES2_EXPORT void cancelDeferred(QObject *object, const QString &property);
+ Q_QUICKTEMPLATES2_EXPORT void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate);
}
template<typename T>
diff --git a/src/quicktemplates/qquickdeferredpointer_p_p.h b/src/quicktemplates/qquickdeferredpointer_p_p.h
index cd5a87e508..e1fa85e2ee 100644
--- a/src/quicktemplates/qquickdeferredpointer_p_p.h
+++ b/src/quicktemplates/qquickdeferredpointer_p_p.h
@@ -113,10 +113,10 @@ class QQuickDeferredPointer : public QQuickUntypedDeferredPointer
{
Q_DISABLE_COPY_MOVE(QQuickDeferredPointer)
public:
- QQuickDeferredPointer() = default;
+ Q_NODISCARD_CTOR QQuickDeferredPointer() = default;
~QQuickDeferredPointer() = default;
- QQuickDeferredPointer(T *v) : QQuickUntypedDeferredPointer(v) {}
+ Q_NODISCARD_CTOR QQuickDeferredPointer(T *v) : QQuickUntypedDeferredPointer(v) {}
QQuickDeferredPointer<T> &operator=(T *o) {
QQuickUntypedDeferredPointer::operator=(o);
return *this;
diff --git a/src/quicktemplates/qquickdelaybutton_p.h b/src/quicktemplates/qquickdelaybutton_p.h
index ea8a8b6720..3e648c4739 100644
--- a/src/quicktemplates/qquickdelaybutton_p.h
+++ b/src/quicktemplates/qquickdelaybutton_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickTransition;
class QQuickDelayButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDelayButton : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickDelayButton : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY delayChanged FINAL)
@@ -62,6 +62,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDelayButton)
-
#endif // QQUICKDELAYBUTTON_P_H
diff --git a/src/quicktemplates/qquickdial.cpp b/src/quicktemplates/qquickdial.cpp
index 4fd3a15de0..3f95bd901d 100644
--- a/src/quicktemplates/qquickdial.cpp
+++ b/src/quicktemplates/qquickdial.cpp
@@ -57,10 +57,30 @@ QT_BEGIN_NAMESPACE
by the user by either touch, mouse, or keys.
*/
-static const qreal startAngleRadians = (M_PI * 2.0) * (4.0 / 6.0);
-static const qreal startAngle = -140;
-static const qreal endAngleRadians = (M_PI * 2.0) * (5.0 / 6.0);
-static const qreal endAngle = 140;
+/*!
+ \qmlsignal QtQuick.Controls::Dial::wrapped(Dial.WrapDirection direction)
+ \since 6.6
+
+ This signal is emitted when the dial wraps around, i.e. goes beyond its
+ maximum value to its minimum value, or vice versa. It is only emitted when
+ \l wrap is \c true.
+ The \a direction argument specifies the direction of the full rotation and
+ will be one of the following arguments:
+
+ \value Dial.Clockwise The dial wrapped in clockwise direction.
+ \value Dial.CounterClockwise The dial wrapped in counterclockwise direction.
+*/
+
+// The user angle is the clockwise angle between the position and the vertical
+// y-axis (12 o clock position).
+// Using radians for logic (atan2(...)) and degree for user interface
+constexpr qreal toUserAngleDeg(qreal logicAngleRad) {
+ // minus to turn clockwise, add 90 deg clockwise
+ return -logicAngleRad / M_PI * 180. + 90;
+}
+
+static const qreal defaultStartAngle = -140;
+static const qreal defaultEndAngle = 140;
class QQuickDialPrivate : public QQuickControlPrivate
{
@@ -74,7 +94,7 @@ public:
qreal linearPositionAt(const QPointF &point) const;
void setPosition(qreal position);
void updatePosition();
- bool isLargeChange(const QPointF &eventPos, qreal proposedPosition) const;
+ bool isLargeChange(qreal proposedPosition) const;
bool isHorizontalOrVertical() const;
bool handlePress(const QPointF &point, ulong timestamp) override;
@@ -87,10 +107,14 @@ public:
void updateAllValuesAreInteger();
+ void maybeEmitWrapAround(qreal pos);
+
qreal from = 0;
qreal to = 1;
qreal value = 0;
qreal position = 0;
+ qreal startAngle = defaultStartAngle;
+ qreal endAngle = defaultEndAngle;
qreal angle = startAngle;
qreal stepSize = 0;
QPointF pressPoint;
@@ -140,13 +164,34 @@ qreal QQuickDialPrivate::circularPositionAt(const QPointF &point) const
{
qreal yy = height / 2.0 - point.y();
qreal xx = point.x() - width / 2.0;
- qreal angle = (xx || yy) ? std::atan2(yy, xx) : 0;
+ qreal alpha = (xx || yy) ? toUserAngleDeg(std::atan2(yy, xx)) : 0;
+
+ // Move around the circle to reach the interval.
+ if (alpha < startAngle && alpha + 360. < endAngle)
+ alpha += 360.;
+ else if (alpha >= endAngle && alpha - 360. >= startAngle)
+ alpha -= 360.;
+
+ // If wrap is on and we are out of the interval [startAngle, endAngle],
+ // we want to jump to the closest border to make it feel nice and responsive
+ if ((alpha < startAngle || alpha > endAngle) && wrap) {
+ if (abs(alpha - startAngle) > abs(endAngle - alpha - 360.))
+ alpha += 360.;
+ else if (abs(alpha - startAngle - 360.) < abs(endAngle - alpha))
+ alpha -= 360.;
+ }
- if (angle < M_PI / -2)
- angle = angle + M_PI * 2;
+ // If wrap is off,
+ // we want to stay as close as possible to the current angle.
+ // This is important to allow easy setting of boundary values (0,1)
+ if (!wrap) {
+ if (abs(angle - alpha) > abs(angle - (alpha + 360.)))
+ alpha += 360.;
+ if (abs(angle - alpha) > abs(angle - (alpha - 360.)))
+ alpha -= 360.;
+ }
- qreal normalizedAngle = (startAngleRadians - angle) / endAngleRadians;
- return normalizedAngle;
+ return (alpha - startAngle) / (endAngle - startAngle);
}
qreal QQuickDialPrivate::linearPositionAt(const QPointF &point) const
@@ -179,12 +224,13 @@ void QQuickDialPrivate::setPosition(qreal pos)
{
Q_Q(QQuickDial);
pos = qBound<qreal>(qreal(0), pos, qreal(1));
- if (qFuzzyCompare(position, pos))
+ const qreal alpha = startAngle + pos * qAbs(endAngle - startAngle);
+ if (qFuzzyCompare(position, pos) && qFuzzyCompare(angle, alpha))
return;
+ angle = alpha;
position = pos;
- angle = startAngle + position * qAbs(endAngle - startAngle);
emit q->positionChanged();
emit q->angleChanged();
@@ -198,9 +244,11 @@ void QQuickDialPrivate::updatePosition()
setPosition(pos);
}
-bool QQuickDialPrivate::isLargeChange(const QPointF &eventPos, qreal proposedPosition) const
+bool QQuickDialPrivate::isLargeChange(qreal proposedPosition) const
{
- return qAbs(proposedPosition - position) >= qreal(0.5) && eventPos.y() >= height / 2;
+ if (endAngle - startAngle < 180.0)
+ return false;
+ return qAbs(proposedPosition - position) > qreal(0.5);
}
bool QQuickDialPrivate::isHorizontalOrVertical() const
@@ -223,11 +271,13 @@ bool QQuickDialPrivate::handleMove(const QPointF &point, ulong timestamp)
Q_Q(QQuickDial);
QQuickControlPrivate::handleMove(point, timestamp);
const qreal oldPos = position;
- qreal pos = positionAt(point);
+ qreal pos = qBound(0.0, positionAt(point), 1.0);
if (snapMode == QQuickDial::SnapAlways)
pos = snapPosition(pos);
- if (wrap || isHorizontalOrVertical() || !isLargeChange(point, pos)) {
+ maybeEmitWrapAround(pos);
+
+ if (wrap || isHorizontalOrVertical() || !isLargeChange(pos)) {
if (live)
q->setValue(valueAt(pos));
else
@@ -248,7 +298,9 @@ bool QQuickDialPrivate::handleRelease(const QPointF &point, ulong timestamp)
if (snapMode != QQuickDial::NoSnap)
pos = snapPosition(pos);
- if (wrap || isHorizontalOrVertical() || !isLargeChange(point, pos))
+ maybeEmitWrapAround(pos);
+
+ if (wrap || isHorizontalOrVertical() || !isLargeChange(pos))
q->setValue(valueAt(pos));
if (!qFuzzyCompare(pos, oldPos))
emit q->moved();
@@ -272,8 +324,6 @@ void QQuickDialPrivate::handleUngrab()
q->setPressed(false);
}
-static inline QString handleName() { return QStringLiteral("handle"); }
-
void QQuickDialPrivate::cancelHandle()
{
Q_Q(QQuickDial);
@@ -303,6 +353,14 @@ void QQuickDialPrivate::updateAllValuesAreInteger()
allValuesAreInteger = areRepresentableAsInteger(to, from, stepSize) && stepSize != 0.0;
}
+void QQuickDialPrivate::maybeEmitWrapAround(qreal pos)
+{
+ Q_Q(QQuickDial);
+
+ if (wrap && isLargeChange(pos))
+ emit q->wrapped((pos < q->position()) ? QQuickDial::Clockwise : QQuickDial::CounterClockwise);
+}
+
QQuickDial::QQuickDial(QQuickItem *parent)
: QQuickControl(*(new QQuickDialPrivate), parent)
{
@@ -314,6 +372,8 @@ QQuickDial::QQuickDial(QQuickItem *parent)
#if QT_CONFIG(cursor)
setCursor(Qt::ArrowCursor);
#endif
+ Q_D(QQuickDial);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
/*!
@@ -422,11 +482,12 @@ qreal QQuickDial::position() const
\qmlproperty real QtQuick.Controls::Dial::angle
\readonly
- This property holds the angle of the handle.
+ This property holds the clockwise angle of the handle in degrees.
- The range is from \c -140 degrees to \c 140 degrees.
+ The angle is zero at the 12 o'clock position and the range is from
+ \l startAngle to \c endAngle.
- \sa position
+ \sa position, startAngle, endAngle
*/
qreal QQuickDial::angle() const
{
@@ -469,6 +530,129 @@ void QQuickDial::setStepSize(qreal step)
emit stepSizeChanged();
}
+
+/*!
+ \qmlproperty real QtQuick.Controls::Dial::startAngle
+ \since 6.6
+
+ This property holds the starting angle of the dial in degrees.
+
+ This is the \l angle the dial will have for its minimum value, i.e. \l from.
+ The \l startAngle has to be smaller than the \l endAngle, larger than -360
+ and larger or equal to the \l endAngle - 360 degrees.
+
+ \sa endAngle, angle
+*/
+qreal QQuickDial::startAngle() const
+{
+ Q_D(const QQuickDial);
+ return d->startAngle;
+}
+
+void QQuickDial::setStartAngle(qreal startAngle)
+{
+ Q_D(QQuickDial);
+ if (!d->componentComplete) {
+ // Binding evaluation order can cause warnings with certain combinations
+ // of start and end angles, so delay the actual setting until after component completion.
+ // Store the requested value in the existing member to avoid the need for an extra one.
+ d->startAngle = startAngle;
+ return;
+ }
+
+ if (qFuzzyCompare(d->startAngle, startAngle))
+ return;
+
+ // do not allow to change direction
+ if (startAngle >= d->endAngle) {
+ qmlWarning(this) << "startAngle (" << startAngle
+ << ") cannot be greater than or equal to endAngle (" << d->endAngle << ")";
+ return;
+ }
+
+ // Keep the interval around 0
+ if (startAngle <= -360.) {
+ qmlWarning(this) << "startAngle (" << startAngle << ") cannot be less than or equal to -360";
+ return;
+ }
+
+ // keep the interval [startAngle, endAngle] unique
+ if (startAngle < d->endAngle - 360.) {
+ qmlWarning(this) << "Difference between startAngle (" << startAngle
+ << ") and endAngle (" << d->endAngle << ") cannot be greater than 360."
+ << " Changing endAngle to avoid overlaps.";
+ d->endAngle = startAngle + 360.;
+ emit endAngleChanged();
+ }
+
+ d->startAngle = startAngle;
+ // changing the startAngle will change the angle
+ // if the value is kept constant
+ d->updatePosition();
+ emit startAngleChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::Dial::endAngle
+ \since 6.6
+
+ This property holds the end angle of the dial in degrees.
+
+ This is the \l angle the dial will have for its maximum value, i.e. \l to.
+ The \l endAngle has to be bigger than the \l startAngle, smaller than 720
+ and smaller or equal than the \l startAngle + 360 degrees.
+
+ \sa endAngle, angle
+*/
+qreal QQuickDial::endAngle() const
+{
+ Q_D(const QQuickDial);
+ return d->endAngle;
+
+}
+
+void QQuickDial::setEndAngle(qreal endAngle)
+{
+ Q_D(QQuickDial);
+ if (!d->componentComplete) {
+ // Binding evaluation order can cause warnings with certain combinations
+ // of start and end angles, so delay the actual setting until after component completion.
+ // Store the requested value in the existing member to avoid the need for an extra one.
+ d->endAngle = endAngle;
+ return;
+ }
+
+ if (qFuzzyCompare(d->endAngle, endAngle))
+ return;
+
+ if (endAngle <= d->startAngle) {
+ qmlWarning(this) << "endAngle (" << endAngle
+ << ") cannot be less than or equal to startAngle (" << d->startAngle << ")";
+ return;
+ }
+
+ // Keep the interval around 0
+ if (endAngle >= 720.) {
+ qmlWarning(this) << "endAngle (" << endAngle << ") cannot be greater than or equal to 720";
+ return;
+ }
+
+ // keep the interval [startAngle, endAngle] unique
+ if (endAngle > d->startAngle + 360.) {
+ qmlWarning(this) << "Difference between startAngle (" << d->startAngle
+ << ") and endAngle (" << endAngle << ") cannot be greater than 360."
+ << " Changing startAngle to avoid overlaps.";
+ d->startAngle = endAngle - 360.;
+ emit startAngleChanged();
+ }
+
+ d->endAngle = endAngle;
+ // changing the startAngle will change the angle
+ // if the value is kept constant
+ d->updatePosition();
+ emit endAngleChanged();
+}
+
/*!
\qmlproperty enumeration QtQuick.Controls::Dial::snapMode
@@ -808,6 +992,21 @@ void QQuickDial::componentComplete()
Q_D(QQuickDial);
d->executeHandle(true);
QQuickControl::componentComplete();
+
+ // Set the (delayed) start and end angles, if necessary (see the setters for more info).
+ if (!qFuzzyCompare(d->startAngle, defaultStartAngle)) {
+ const qreal startAngle = d->startAngle;
+ // Temporarily set it to something else so that it sees that it has changed.
+ d->startAngle = defaultStartAngle;
+ setStartAngle(startAngle);
+ }
+
+ if (!qFuzzyCompare(d->endAngle, defaultEndAngle)) {
+ const qreal endAngle = d->endAngle;
+ d->endAngle = defaultEndAngle;
+ setEndAngle(endAngle);
+ }
+
setValue(d->value);
d->updatePosition();
}
diff --git a/src/quicktemplates/qquickdial_p.h b/src/quicktemplates/qquickdial_p.h
index c716b9671b..2008261214 100644
--- a/src/quicktemplates/qquickdial_p.h
+++ b/src/quicktemplates/qquickdial_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickDialAttached;
class QQuickDialPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDial : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickDial : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -32,6 +32,8 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDial : public QQuickControl
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL)
Q_PROPERTY(qreal angle READ angle NOTIFY angleChanged FINAL)
+ Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged FINAL REVISION(6, 6))
+ Q_PROPERTY(qreal endAngle READ endAngle WRITE setEndAngle NOTIFY endAngleChanged FINAL REVISION(6, 6))
Q_PROPERTY(qreal stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL)
Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged FINAL)
Q_PROPERTY(bool wrap READ wrap WRITE setWrap NOTIFY wrapChanged FINAL)
@@ -64,6 +66,12 @@ public:
qreal stepSize() const;
void setStepSize(qreal step);
+ qreal startAngle() const;
+ void setStartAngle(qreal startAngle);
+
+ qreal endAngle() const;
+ void setEndAngle(qreal endAngle);
+
enum SnapMode {
NoSnap,
SnapAlways,
@@ -81,6 +89,12 @@ public:
};
Q_ENUM(InputMode)
+ enum WrapDirection {
+ Clockwise,
+ CounterClockwise
+ };
+ Q_ENUM(WrapDirection)
+
bool wrap() const;
void setWrap(bool wrap);
@@ -118,6 +132,9 @@ Q_SIGNALS:
Q_REVISION(2, 2) void liveChanged();
// 2.5 (Qt 5.12)
Q_REVISION(2, 5) void inputModeChanged();
+ Q_REVISION(6, 6) void startAngleChanged();
+ Q_REVISION(6, 6) void endAngleChanged();
+ Q_REVISION(6, 6) void wrapped(WrapDirection);
protected:
void keyPressEvent(QKeyEvent *event) override;
@@ -145,6 +162,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDial)
-
#endif // QQUICKDIAL_P_H
diff --git a/src/quicktemplates/qquickdialog.cpp b/src/quicktemplates/qquickdialog.cpp
index 300ad4301f..30daee5f33 100644
--- a/src/quicktemplates/qquickdialog.cpp
+++ b/src/quicktemplates/qquickdialog.cpp
@@ -25,6 +25,10 @@ QT_BEGIN_NAMESPACE
\image qtquickcontrols-page-wireframe.png
+ The \l {Popup::}{padding} properties only affect the contentItem. Use the
+ \l {Popup::}{spacing} property to affect the space between header,
+ contentItem and footer.
+
By default, Dialogs have \l {QQuickItem::}{focus}.
\section1 Dialog Title and Buttons
diff --git a/src/quicktemplates/qquickdialog_p.h b/src/quicktemplates/qquickdialog_p.h
index a350c5d923..63fac39f97 100644
--- a/src/quicktemplates/qquickdialog_p.h
+++ b/src/quicktemplates/qquickdialog_p.h
@@ -19,11 +19,13 @@
#include <QtQuickTemplates2/private/qquickpopup_p.h>
#include <QtGui/qpa/qplatformdialoghelper.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickDialogPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialog : public QQuickPopup
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialog : public QQuickPopup
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
@@ -111,6 +113,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDialog)
-
#endif // QQUICKDIALOG_P_H
diff --git a/src/quicktemplates/qquickdialog_p_p.h b/src/quicktemplates/qquickdialog_p_p.h
index 2d0121892a..dd4c3fa1f0 100644
--- a/src/quicktemplates/qquickdialog_p_p.h
+++ b/src/quicktemplates/qquickdialog_p_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractButton;
class QQuickDialogButtonBox;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogPrivate : public QQuickPopupPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialogPrivate : public QQuickPopupPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickDialog)
diff --git a/src/quicktemplates/qquickdialogbuttonbox.cpp b/src/quicktemplates/qquickdialogbuttonbox.cpp
index 989c9b4506..b7c486c472 100644
--- a/src/quicktemplates/qquickdialogbuttonbox.cpp
+++ b/src/quicktemplates/qquickdialogbuttonbox.cpp
@@ -383,10 +383,8 @@ QQuickAbstractButton *QQuickDialogButtonBoxPrivate::createStandardButton(QPlatfo
QQmlContext *creationContext = delegate->creationContext();
if (!creationContext)
creationContext = qmlContext(q);
- QQmlContext *context = new QQmlContext(creationContext, q);
- context->setContextObject(q);
- QObject *object = delegate->beginCreate(context);
+ QObject *object = delegate->beginCreate(creationContext);
QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(object);
if (button) {
QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(qmlAttachedPropertiesObject<QQuickDialogButtonBox>(button, true));
@@ -457,6 +455,7 @@ QQuickDialogButtonBox::QQuickDialogButtonBox(QQuickItem *parent)
Q_D(QQuickDialogButtonBox);
d->changeTypes |= QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
d->buttonLayout = platformButtonLayout();
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
QQuickDialogButtonBox::~QQuickDialogButtonBox()
@@ -480,7 +479,7 @@ QQuickDialogButtonBox::~QQuickDialogButtonBox()
Possible values:
\value DialogButtonBox.Header The button box is at the top, as a window or page header.
- \value DialogButtonBox.Footer The button box is at the bottom, as a window or page header.
+ \value DialogButtonBox.Footer The button box is at the bottom, as a window or page footer.
The default value is \c Footer.
@@ -515,6 +514,13 @@ void QQuickDialogButtonBox::setPosition(Position position)
\value Qt.AlignTop The buttons are aligned to the top.
\value Qt.AlignVCenter The buttons are vertically centered.
\value Qt.AlignBottom The buttons are aligned to the bottom.
+
+ The default value is \c undefined.
+
+ \note This property assumes a horizontal layout of the buttons. The
+ DialogButtonBox for the \l {iOS Style}{iOS style} uses a vertical layout
+ when there are more than two buttons, and if set to a value other than
+ \c undefined, the layout of its buttons will be done horizontally.
*/
Qt::Alignment QQuickDialogButtonBox::alignment() const
{
diff --git a/src/quicktemplates/qquickdialogbuttonbox_p.h b/src/quicktemplates/qquickdialogbuttonbox_p.h
index 02cdd3bb63..73b0af9953 100644
--- a/src/quicktemplates/qquickdialogbuttonbox_p.h
+++ b/src/quicktemplates/qquickdialogbuttonbox_p.h
@@ -19,6 +19,8 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
#include <QtGui/qpa/qplatformdialoghelper.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQmlComponent;
@@ -26,7 +28,7 @@ class QQuickDialogButtonBoxPrivate;
class QQuickDialogButtonBoxAttached;
class QQuickDialogButtonBoxAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBox : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialogButtonBox : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(Position position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -35,9 +37,9 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBox : public QQuickCont
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL)
// 2.5 (Qt 5.12)
Q_PROPERTY(QPlatformDialogHelper::ButtonLayout buttonLayout READ buttonLayout WRITE setButtonLayout RESET resetButtonLayout NOTIFY buttonLayoutChanged FINAL REVISION(2, 5))
- Q_FLAGS(QPlatformDialogHelper::StandardButtons)
QML_NAMED_ELEMENT(DialogButtonBox)
QML_ATTACHED(QQuickDialogButtonBoxAttached)
+ QML_EXTENDED_NAMESPACE(QPlatformDialogHelper)
QML_ADDED_IN_VERSION(2, 1)
public:
@@ -66,9 +68,6 @@ public:
static QQuickDialogButtonBoxAttached *qmlAttachedProperties(QObject *object);
- // 2.5 (Qt 5.12)
- Q_ENUMS(QPlatformDialogHelper::ButtonLayout)
-
QPlatformDialogHelper::ButtonLayout buttonLayout() const;
void setButtonLayout(QPlatformDialogHelper::ButtonLayout layout);
void resetButtonLayout();
@@ -108,12 +107,11 @@ private:
Q_DECLARE_PRIVATE(QQuickDialogButtonBox)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBoxAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialogButtonBoxAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox NOTIFY buttonBoxChanged FINAL)
Q_PROPERTY(QPlatformDialogHelper::ButtonRole buttonRole READ buttonRole WRITE setButtonRole NOTIFY buttonRoleChanged FINAL)
- Q_ENUMS(QPlatformDialogHelper::ButtonRole)
public:
explicit QQuickDialogButtonBoxAttached(QObject *parent = nullptr);
@@ -134,6 +132,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDialogButtonBox)
-
#endif // QQUICKDIALOGBUTTONBOX_P_H
diff --git a/src/quicktemplates/qquickdialogbuttonbox_p_p.h b/src/quicktemplates/qquickdialogbuttonbox_p_p.h
index f2d9ff6b2d..b4b8af840f 100644
--- a/src/quicktemplates/qquickdialogbuttonbox_p_p.h
+++ b/src/quicktemplates/qquickdialogbuttonbox_p_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBoxPrivate : public QQuickContainerPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialogButtonBoxPrivate : public QQuickContainerPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickDialogButtonBox)
diff --git a/src/quicktemplates/qquickdrawer.cpp b/src/quicktemplates/qquickdrawer.cpp
index 54b4eedaaa..f67a8ec76a 100644
--- a/src/quicktemplates/qquickdrawer.cpp
+++ b/src/quicktemplates/qquickdrawer.cpp
@@ -12,6 +12,7 @@
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickanimation_p.h>
#include <QtQuick/private/qquicktransition_p.h>
+#include <QtQuickTemplates2/private/qquickoverlay_p.h>
QT_BEGIN_NAMESPACE
@@ -127,7 +128,7 @@ QT_BEGIN_NAMESPACE
Drawer can be configured as a non-closable persistent side panel by
making the Drawer \l {Popup::modal}{non-modal} and \l {interactive}
- {non-interactive}. See the \l {Qt Quick Controls 2 - Side Panel}{Side Panel}
+ {non-interactive}. See the \l {Qt Quick Controls 2 - Gallery}{Gallery}
example for more details.
\note On some platforms, certain edges may be reserved for system
@@ -164,15 +165,25 @@ qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const
if (!window)
return 0;
- switch (edge) {
+ auto size = QSizeF(q->width(), q->height());
+
+ switch (effectiveEdge()) {
case Qt::TopEdge:
- return point.y() / q->height();
+ if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ size.transpose();
+ return point.y() / size.height();
case Qt::LeftEdge:
- return point.x() / q->width();
+ if (edge == Qt::TopEdge || edge == Qt::BottomEdge)
+ size.transpose();
+ return point.x() / size.width();
case Qt::RightEdge:
- return (window->width() - point.x()) / q->width();
+ if (edge == Qt::TopEdge || edge == Qt::BottomEdge)
+ size.transpose();
+ return (window->width() - point.x()) / size.width();
case Qt::BottomEdge:
- return (window->height() - point.y()) / q->height();
+ if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ size.transpose();
+ return (window->height() - point.y()) / size.height();
default:
return 0;
}
@@ -216,22 +227,24 @@ void QQuickDrawerPositioner::reposition()
QQuickPopupPositioner::reposition();
}
-void QQuickDrawerPrivate::showOverlay()
+void QQuickDrawerPrivate::showDimmer()
{
// managed in setPosition()
}
-void QQuickDrawerPrivate::hideOverlay()
+void QQuickDrawerPrivate::hideDimmer()
{
// managed in setPosition()
}
-void QQuickDrawerPrivate::resizeOverlay()
+void QQuickDrawerPrivate::resizeDimmer()
{
if (!dimmer || !window)
return;
- QRectF geometry(0, 0, window->width(), window->height());
+ const QQuickOverlay *overlay = QQuickOverlay::overlay(window);
+
+ QRectF geometry(0, 0, overlay ? overlay->width() : 0, overlay ? overlay->height() : 0);
if (edge == Qt::LeftEdge || edge == Qt::RightEdge) {
geometry.setY(popupItem->y());
@@ -245,17 +258,18 @@ void QQuickDrawerPrivate::resizeOverlay()
dimmer->setSize(geometry.size());
}
-static bool isWithinDragMargin(const QQuickDrawer *drawer, const QPointF &pos)
+bool QQuickDrawerPrivate::isWithinDragMargin(const QPointF &pos) const
{
- switch (drawer->edge()) {
+ Q_Q(const QQuickDrawer);
+ switch (effectiveEdge()) {
case Qt::LeftEdge:
- return pos.x() <= drawer->dragMargin();
+ return pos.x() <= q->dragMargin();
case Qt::RightEdge:
- return pos.x() >= drawer->window()->width() - drawer->dragMargin();
+ return pos.x() >= q->window()->width() - q->dragMargin();
case Qt::TopEdge:
- return pos.y() <= drawer->dragMargin();
+ return pos.y() <= q->dragMargin();
case Qt::BottomEdge:
- return pos.y() >= drawer->window()->height() - drawer->dragMargin();
+ return pos.y() >= q->window()->height() - q->dragMargin();
default:
Q_UNREACHABLE();
break;
@@ -265,14 +279,13 @@ static bool isWithinDragMargin(const QQuickDrawer *drawer, const QPointF &pos)
bool QQuickDrawerPrivate::startDrag(QEvent *event)
{
- Q_Q(QQuickDrawer);
delayedEnterTransition = false;
if (!window || !interactive || dragMargin < 0.0 || qFuzzyIsNull(dragMargin))
return false;
switch (event->type()) {
case QEvent::MouseButtonPress:
- if (QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); isWithinDragMargin(q, mouseEvent->scenePosition())) {
+ if (QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); isWithinDragMargin(mouseEvent->scenePosition())) {
// watch future events and grab the mouse once it has moved
// sufficiently fast or far (in grabMouse).
delayedEnterTransition = true;
@@ -287,7 +300,7 @@ bool QQuickDrawerPrivate::startDrag(QEvent *event)
case QEvent::TouchUpdate: {
auto *touchEvent = static_cast<QTouchEvent *>(event);
for (const QTouchEvent::TouchPoint &point : touchEvent->points()) {
- if (point.state() == QEventPoint::Pressed && isWithinDragMargin(q, point.scenePosition())) {
+ if (point.state() == QEventPoint::Pressed && isWithinDragMargin(point.scenePosition())) {
delayedEnterTransition = true;
touchEvent->addPassiveGrabber(point, popupItem);
handleTouchEvent(window->contentItem(), touchEvent);
@@ -325,10 +338,11 @@ bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event)
// larger threshold to avoid being too eager to steal touch (QTBUG-50045)
const int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5);
bool overThreshold = false;
+ Qt::Edge effEdge = effectiveEdge();
if (position > 0 || dragMargin > 0) {
const bool xOverThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, event, threshold);
const bool yOverThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, event, threshold);
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
overThreshold = xOverThreshold && !yOverThreshold;
else
overThreshold = yOverThreshold && !xOverThreshold;
@@ -336,7 +350,7 @@ bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event)
// Don't be too eager to steal presses outside the drawer (QTBUG-53929)
if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !contains(movePoint)) {
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin;
else
overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin;
@@ -377,10 +391,11 @@ bool QQuickDrawerPrivate::grabTouch(QQuickItem *item, QTouchEvent *event)
// QStyleHints::startDragDistance for dragging. Drawer uses a bit
// larger threshold to avoid being too eager to steal touch (QTBUG-50045)
const int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5);
+ const Qt::Edge effEdge = effectiveEdge();
if (position > 0 || dragMargin > 0) {
const bool xOverThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, &point, threshold);
const bool yOverThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, &point, threshold);
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
overThreshold = xOverThreshold && !yOverThreshold;
else
overThreshold = yOverThreshold && !xOverThreshold;
@@ -388,7 +403,7 @@ bool QQuickDrawerPrivate::grabTouch(QQuickItem *item, QTouchEvent *event)
// Don't be too eager to steal presses outside the drawer (QTBUG-53929)
if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !contains(movePoint)) {
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin;
else
overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin;
@@ -421,8 +436,6 @@ static const qreal openCloseVelocityThreshold = 300;
// interactive control.
bool QQuickDrawerPrivate::blockInput(QQuickItem *item, const QPointF &point) const
{
- Q_Q(const QQuickDrawer);
-
// We want all events, if mouse/touch is already grabbed.
if (popupItem->keepMouseGrab() || popupItem->keepTouchGrab())
return true;
@@ -436,7 +449,7 @@ bool QQuickDrawerPrivate::blockInput(QQuickItem *item, const QPointF &point) con
return false;
// Accept all events within drag area.
- if (isWithinDragMargin(q, point))
+ if (isWithinDragMargin(point))
return true;
// Accept all other events if drawer is modal.
@@ -487,9 +500,9 @@ bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point,
}
velocityCalculator.stopMeasuring(point, timestamp);
-
+ Qt::Edge effEdge = effectiveEdge();
qreal velocity = 0;
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
velocity = velocityCalculator.velocity().x();
else
velocity = velocityCalculator.velocity().y();
@@ -502,7 +515,7 @@ bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point,
// - bottom/right edge: negative velocity opens, positive velocity closes
//
// => invert the velocity for bottom and right edges, for the threshold comparison below
- if (edge == Qt::RightEdge || edge == Qt::BottomEdge)
+ if (effEdge == Qt::RightEdge || effEdge == Qt::BottomEdge)
velocity = -velocity;
if (position > 0.7 || velocity > openCloseVelocityThreshold) {
@@ -510,7 +523,7 @@ bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point,
} else if (position < 0.3 || velocity < -openCloseVelocityThreshold) {
transitionManager.transitionExit();
} else {
- switch (edge) {
+ switch (effEdge) {
case Qt::LeftEdge:
if (point.x() - pressPoint.x() > 0)
transitionManager.transitionEnter();
@@ -641,6 +654,31 @@ Qt::Edge QQuickDrawer::edge() const
return d->edge;
}
+Qt::Edge QQuickDrawerPrivate::effectiveEdge() const
+{
+ auto realEdge = edge;
+ qreal rotation = window->contentItem()->rotation();
+ const bool clockwise = rotation > 0;
+ while (qAbs(rotation) >= 90) {
+ rotation -= clockwise ? 90 : -90;
+ switch (realEdge) {
+ case Qt::LeftEdge:
+ realEdge = clockwise ? Qt::TopEdge : Qt::BottomEdge;
+ break;
+ case Qt::TopEdge:
+ realEdge = clockwise ? Qt::RightEdge : Qt::LeftEdge;
+ break;
+ case Qt::RightEdge:
+ realEdge = clockwise ? Qt::BottomEdge : Qt::TopEdge;
+ break;
+ case Qt::BottomEdge:
+ realEdge = clockwise ? Qt::LeftEdge : Qt::RightEdge;
+ break;
+ }
+ }
+ return realEdge;
+}
+
void QQuickDrawer::setEdge(Qt::Edge edge)
{
Q_D(QQuickDrawer);
@@ -799,7 +837,7 @@ void QQuickDrawer::geometryChange(const QRectF &newGeometry, const QRectF &oldGe
{
Q_D(QQuickDrawer);
QQuickPopup::geometryChange(newGeometry, oldGeometry);
- d->resizeOverlay();
+ d->resizeDimmer();
}
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickdrawer_p.h b/src/quicktemplates/qquickdrawer_p.h
index ec42117f20..a8255e992e 100644
--- a/src/quicktemplates/qquickdrawer_p.h
+++ b/src/quicktemplates/qquickdrawer_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickDrawerPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDrawer : public QQuickPopup
+class Q_QUICKTEMPLATES2_EXPORT QQuickDrawer : public QQuickPopup
{
Q_OBJECT
Q_PROPERTY(Qt::Edge edge READ edge WRITE setEdge NOTIFY edgeChanged FINAL)
@@ -73,6 +73,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDrawer)
-
#endif // QQUICKDRAWER_P_H
diff --git a/src/quicktemplates/qquickdrawer_p_p.h b/src/quicktemplates/qquickdrawer_p_p.h
index 224e557839..7eae26b0cb 100644
--- a/src/quicktemplates/qquickdrawer_p_p.h
+++ b/src/quicktemplates/qquickdrawer_p_p.h
@@ -35,9 +35,9 @@ public:
qreal positionAt(const QPointF &point) const;
QQuickPopupPositioner *getPositioner() override;
- void showOverlay() override;
- void hideOverlay() override;
- void resizeOverlay() override;
+ void showDimmer() override;
+ void hideDimmer() override;
+ void resizeDimmer() override;
bool startDrag(QEvent *event);
bool grabMouse(QQuickItem *item, QMouseEvent *event);
@@ -55,6 +55,8 @@ public:
bool prepareExitTransition() override;
bool setEdge(Qt::Edge edge);
+ Qt::Edge effectiveEdge() const;
+ bool isWithinDragMargin(const QPointF &point) const;
Qt::Edge edge = Qt::LeftEdge;
qreal offset = 0;
diff --git a/src/quicktemplates/qquickframe_p.h b/src/quicktemplates/qquickframe_p.h
index 94c98685f4..6500001fa6 100644
--- a/src/quicktemplates/qquickframe_p.h
+++ b/src/quicktemplates/qquickframe_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickFramePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickFrame : public QQuickPane
+class Q_QUICKTEMPLATES2_EXPORT QQuickFrame : public QQuickPane
{
Q_OBJECT
QML_NAMED_ELEMENT(Frame)
@@ -44,6 +44,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFrame)
-
#endif // QQUICKFRAME_P_H
diff --git a/src/quicktemplates/qquickframe_p_p.h b/src/quicktemplates/qquickframe_p_p.h
index bb7eabf12a..8f882a26e4 100644
--- a/src/quicktemplates/qquickframe_p_p.h
+++ b/src/quicktemplates/qquickframe_p_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickFrame;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickFramePrivate : public QQuickPanePrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickFramePrivate : public QQuickPanePrivate
{
};
diff --git a/src/quicktemplates/qquickgroupbox.cpp b/src/quicktemplates/qquickgroupbox.cpp
index 0fa2d1a6d8..72ce1b42cb 100644
--- a/src/quicktemplates/qquickgroupbox.cpp
+++ b/src/quicktemplates/qquickgroupbox.cpp
@@ -61,6 +61,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::GroupBox); }
@@ -104,6 +105,16 @@ void QQuickGroupBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit q->implicitLabelHeightChanged();
}
+void QQuickGroupBoxPrivate::itemDestroyed(QQuickItem *item)
+{
+ Q_Q(QQuickGroupBox);
+ QQuickFramePrivate::itemDestroyed(item);
+ if (item == label) {
+ label = nullptr;
+ emit q->labelChanged();
+ }
+}
+
QQuickGroupBox::QQuickGroupBox(QQuickItem *parent)
: QQuickFrame(*(new QQuickGroupBoxPrivate), parent)
{
diff --git a/src/quicktemplates/qquickgroupbox_p.h b/src/quicktemplates/qquickgroupbox_p.h
index 5fc6284fea..0b2a5a0e52 100644
--- a/src/quicktemplates/qquickgroupbox_p.h
+++ b/src/quicktemplates/qquickgroupbox_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickGroupBoxPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickGroupBox : public QQuickFrame
+class Q_QUICKTEMPLATES2_EXPORT QQuickGroupBox : public QQuickFrame
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
@@ -71,6 +71,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickGroupBox)
-
#endif // QQUICKGROUPBOX_P_H
diff --git a/src/quicktemplates/qquickheaderview.cpp b/src/quicktemplates/qquickheaderview.cpp
index 0d9bc5588c..b94280865f 100644
--- a/src/quicktemplates/qquickheaderview.cpp
+++ b/src/quicktemplates/qquickheaderview.cpp
@@ -11,19 +11,9 @@
\inherits TableView
\brief Provides a horizontal header view to accompany a \l TableView.
- A HorizontalHeaderView provides labeling of the columns of a \l TableView.
- To add a horizontal header to a TableView, bind the
- \l {HorizontalHeaderView::syncView} {syncView} property to the TableView:
+ \include qquickheaderview.qdocinc {detailed-description} {HorizontalHeaderView}
- \snippet qtquickcontrols-headerview-simple.qml horizontal
-
- The header displays data from the {syncView}'s model by default, but can
- also have its own model. If the model is a QAbstractTableModel, then
- the header will display the model's horizontal headerData(); otherwise,
- the model's data().
-
- A HorizontalHeaderView will have
- \l {resizableColumns}{TableView::resizableColumns} set to \c true by default.
+ \sa VerticalHeaderView
*/
/*!
@@ -31,117 +21,47 @@
\inqmlmodule QtQuick.Controls
\ingroup qtquickcontrols-containers
\inherits TableView
- \brief Provides a vertical header view to accompany a \l TableView.
-
- A VerticalHeaderView provides labeling of the rows of a \l TableView.
- To add a vertical header to a TableView, bind the
- \l {VerticalHeaderView::syncView} {syncView} property to the TableView:
+ \brief Offers a vertical header view to accompany a \l TableView.
- \snippet qtquickcontrols-headerview-simple.qml vertical
+ \include qquickheaderview.qdocinc {detailed-description} {VerticalHeaderView}
- The header displays data from the {syncView}'s model by default, but can
- also have its own model. If the model is a QAbstractTableModel, then
- the header will display the model's vertical headerData(); otherwise,
- the model's data().
-
- A VerticalHeaderView will have
- \l {resizableRows}{TableView::resizableRows} set to \c true by default.
+ \sa HorizontalHeaderView
*/
/*!
- \qmlproperty TableView QtQuick::HorizontalHeaderView::syncView
-
- This property holds the TableView to synchronize with.
-
- Once this property is bound to another TableView, both header and table
- will synchronize with regard to column widths, column spacing, and flicking
- horizontally.
+ \qmlproperty TableView QtQuick.Controls::HorizontalHeaderView::syncView
- If the \l model is not explicitly set, then the header will use the syncView's
- model to label the columns.
-
- \sa model TableView
+ \include qquickheaderview.qdocinc {syncView} {horizontally}
*/
/*!
- \qmlproperty TableView QtQuick::VerticalHeaderView::syncView
-
- This property holds the TableView to synchronize with.
-
- Once this property is bound to another TableView, both header and table
- will synchronize with regard to row heights, row spacing, and flicking
- vertically.
+ \qmlproperty TableView QtQuick.Controls::VerticalHeaderView::syncView
- If the \l model is not explicitly set, then the header will use the syncView's
- model to label the rows.
-
- \sa model TableView
+ \include qquickheaderview.qdocinc {syncView} {vertically}
*/
/*!
- \qmlproperty QVariant QtQuick::HorizontalHeaderView::model
-
- This property holds the model providing data for the horizontal header view.
+ \qmlproperty QVariant QtQuick.Controls::HorizontalHeaderView::model
- When model is not explicitly set, the header will use the syncView's
- model once syncView is set.
-
- If model is a QAbstractTableModel, its horizontal headerData() will
- be accessed.
-
- If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
- will be accessed.
-
- Otherwise, the behavior is same as setting TableView::model.
-
- \sa TableView {TableView::model} {model} QAbstractTableModel
+ \include qquickheaderview.qdocinc {model} {horizontal}
*/
/*!
- \qmlproperty QVariant QtQuick::VerticalHeaderView::model
-
- This property holds the model providing data for the vertical header view.
+ \qmlproperty QVariant QtQuick.Controls::VerticalHeaderView::model
- When model is not explicitly set, it will be synchronized with syncView's model
- once syncView is set.
-
- If model is a QAbstractTableModel, its vertical headerData() will
- be accessed.
-
- If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
- will be accessed.
-
- Otherwise, the behavior is same as setting TableView::model.
-
- \sa TableView {TableView::model} {model} QAbstractTableModel
+ \include qquickheaderview.qdocinc {model} {vertical}
*/
/*!
- \qmlproperty QString QtQuick::HorizontalHeaderView::textRole
-
- This property holds the model role used to display text in each header cell.
+ \qmlproperty QString QtQuick.Controls::HorizontalHeaderView::textRole
- When the model has multiple roles, textRole can be set to determine which
- role should be displayed.
-
- If model is a QAbstractItemModel then it will default to "display"; otherwise
- it is empty.
-
- \sa QAbstractItemModel::roleNames()
+ \include qquickheaderview.qdocinc {textRole}
*/
/*!
- \qmlproperty QString QtQuick::VerticalHeaderView::textRole
-
- This property holds the model role used to display text in each header cell.
-
- When the model has multiple roles, textRole can be set to determine which
- role should be displayed.
-
- If model is a QAbstractItemModel then it will default to "display"; otherwise
- it is empty.
+ \qmlproperty QString QtQuick.Controls::VerticalHeaderView::textRole
- \sa QAbstractItemModel::roleNames()
+ \include qquickheaderview.qdocinc {textRole}
*/
QT_BEGIN_NAMESPACE
@@ -234,36 +154,41 @@ void QQuickHeaderViewBasePrivate::syncModel()
void QQuickHeaderViewBasePrivate::syncSyncView()
{
- Q_Q(QQuickHeaderViewBase);
if (assignedSyncDirection != orientation()) {
qmlWarning(q_func()) << "Setting syncDirection other than Qt::"
<< QVariant::fromValue(orientation()).toString()
<< " is invalid.";
assignedSyncDirection = orientation();
}
- if (assignedSyncView) {
- QBoolBlocker fixupGuard(inUpdateContentSize, true);
- if (orientation() == Qt::Horizontal) {
- q->setLeftMargin(assignedSyncView->leftMargin());
- q->setRightMargin(assignedSyncView->rightMargin());
- } else {
- q->setTopMargin(assignedSyncView->topMargin());
- q->setBottomMargin(assignedSyncView->bottomMargin());
- }
- }
QQuickTableViewPrivate::syncSyncView();
}
+QAbstractItemModel *QQuickHeaderViewBasePrivate::selectionSourceModel()
+{
+ // Our proxy model shares no common model items with HeaderView.model. So
+ // selections done in HeaderView cannot be represented in an ItemSelectionModel
+ // that is shared with the syncView (and for the same reason, the mapping functions
+ // modelIndex(cell) and cellAtIndex(index) have not been overridden either).
+ // Instead, we set the internal proxy model as selection source model.
+ return &m_headerDataProxyModel;
+}
+
QQuickHeaderViewBase::QQuickHeaderViewBase(Qt::Orientation orient, QQuickItem *parent)
: QQuickTableView(*(new QQuickHeaderViewBasePrivate), parent)
{
- d_func()->setOrientation(orient);
+ Q_D(QQuickHeaderViewBase);
+ d->m_headerDataProxyModel.m_headerView = this;
+ d->setSizePolicy(orient == Qt::Horizontal ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed,
+ orient == Qt::Horizontal ? QLayoutPolicy::Fixed : QLayoutPolicy::Preferred);
+ d->setOrientation(orient);
setSyncDirection(orient);
}
QQuickHeaderViewBase::QQuickHeaderViewBase(QQuickHeaderViewBasePrivate &dd, QQuickItem *parent)
: QQuickTableView(dd, parent)
{
+ Q_D(QQuickHeaderViewBase);
+ d->m_headerDataProxyModel.m_headerView = this;
}
QQuickHeaderViewBase::~QQuickHeaderViewBase()
@@ -384,6 +309,24 @@ bool QHeaderDataProxyModel::hasChildren(const QModelIndex &parent) const
return false;
}
+QHash<int, QByteArray> QHeaderDataProxyModel::roleNames() const
+{
+ using namespace Qt::Literals::StringLiterals;
+
+ auto names = m_model ? m_model->roleNames() : QAbstractItemModel::roleNames();
+ if (m_headerView) {
+ QString textRole = m_headerView->textRole();
+ if (textRole.isEmpty())
+ textRole = u"display"_s;
+ if (!names.values().contains(textRole.toUtf8().constData())) {
+ qmlWarning(m_headerView).nospace() << "The 'textRole' property contains a role that doesn't exist in the model: "
+ << textRole << ". Check your model's roleNames() implementation";
+ }
+ }
+
+ return names;
+}
+
QVariant QHeaderDataProxyModel::variantValue() const
{
return QVariant::fromValue(static_cast<QObject *>(const_cast<QHeaderDataProxyModel *>(this)));
diff --git a/src/quicktemplates/qquickheaderview_p.h b/src/quicktemplates/qquickheaderview_p.h
index 8e4ea10a57..5280f563dc 100644
--- a/src/quicktemplates/qquickheaderview_p.h
+++ b/src/quicktemplates/qquickheaderview_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickHeaderViewBase;
class QQuickHeaderViewBasePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickHeaderViewBase : public QQuickTableView
+class Q_QUICKTEMPLATES2_EXPORT QQuickHeaderViewBase : public QQuickTableView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickHeaderViewBase)
@@ -48,7 +48,7 @@ private:
};
class QQuickHorizontalHeaderViewPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickHorizontalHeaderView : public QQuickHeaderViewBase
+class Q_QUICKTEMPLATES2_EXPORT QQuickHorizontalHeaderView : public QQuickHeaderViewBase
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickHorizontalHeaderView)
@@ -67,7 +67,7 @@ private:
};
class QQuickVerticalHeaderViewPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickVerticalHeaderView : public QQuickHeaderViewBase
+class Q_QUICKTEMPLATES2_EXPORT QQuickVerticalHeaderView : public QQuickHeaderViewBase
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickVerticalHeaderView)
@@ -85,17 +85,6 @@ private:
Q_DISABLE_COPY(QQuickVerticalHeaderView)
};
-struct QQuickTableViewForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickTableView)
- QML_ADDED_IN_VERSION(2, 14)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickHorizontalHeaderView)
-QML_DECLARE_TYPE(QQuickVerticalHeaderView)
-
#endif // QQUICKHEADERVIEW_P_H
diff --git a/src/quicktemplates/qquickheaderview_p_p.h b/src/quicktemplates/qquickheaderview_p_p.h
index fe33cb0190..4269d1c1a0 100644
--- a/src/quicktemplates/qquickheaderview_p_p.h
+++ b/src/quicktemplates/qquickheaderview_p_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QHeaderDataProxyModel : public QAbstractItemModel
+class Q_QUICKTEMPLATES2_EXPORT QHeaderDataProxyModel : public QAbstractItemModel
{
Q_OBJECT
Q_DISABLE_COPY(QHeaderDataProxyModel)
@@ -44,11 +44,14 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
+ QHash<int, QByteArray> roleNames() const override;
inline QVariant variantValue() const;
inline Qt::Orientation orientation() const;
inline void setOrientation(Qt::Orientation o);
+ QQuickHeaderViewBase *m_headerView = nullptr;
+
private:
inline void connectToModel();
inline void disconnectFromModel();
@@ -70,6 +73,7 @@ public:
void setModelImpl(const QVariant &newModel) override;
void syncModel() override;
void syncSyncView() override;
+ QAbstractItemModel *selectionSourceModel() override;
protected:
QHeaderDataProxyModel m_headerDataProxyModel;
diff --git a/src/quicktemplates/qquickicon_p.h b/src/quicktemplates/qquickicon_p.h
index 737b28ad89..5f513ea7d3 100644
--- a/src/quicktemplates/qquickicon_p.h
+++ b/src/quicktemplates/qquickicon_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
class QQuickIconPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickIcon
+class Q_QUICKTEMPLATES2_EXPORT QQuickIcon
{
Q_GADGET
Q_PROPERTY(QString name READ name WRITE setName RESET resetName FINAL)
diff --git a/src/quicktemplates/qquickindicatorbutton_p.cpp b/src/quicktemplates/qquickindicatorbutton_p.cpp
index 1d95a9e631..22c13b9e4e 100644
--- a/src/quicktemplates/qquickindicatorbutton_p.cpp
+++ b/src/quicktemplates/qquickindicatorbutton_p.cpp
@@ -8,8 +8,6 @@ QT_BEGIN_NAMESPACE
class QQuickIndicatorButton;
-static inline QString indicatorName() { return QStringLiteral("indicator"); }
-
void QQuickIndicatorButtonPrivate::cancelIndicator()
{
Q_Q(QQuickIndicatorButton);
@@ -33,6 +31,14 @@ QQuickIndicatorButton::QQuickIndicatorButton(QObject *parent)
{
}
+QQuickIndicatorButton::~QQuickIndicatorButton()
+{
+ Q_D(QQuickIndicatorButton);
+ QQuickControl *parentControl = static_cast<QQuickControl *>(parent());
+ if (parentControl)
+ QQuickControlPrivate::get(parentControl)->removeImplicitSizeListener(d->indicator);
+}
+
bool QQuickIndicatorButton::isPressed() const
{
Q_D(const QQuickIndicatorButton);
diff --git a/src/quicktemplates/qquickindicatorbutton_p.h b/src/quicktemplates/qquickindicatorbutton_p.h
index be5259211d..98b08a6030 100644
--- a/src/quicktemplates/qquickindicatorbutton_p.h
+++ b/src/quicktemplates/qquickindicatorbutton_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickIndicatorButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickIndicatorButton : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickIndicatorButton : public QObject
{
Q_OBJECT
Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL)
@@ -38,6 +38,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickIndicatorButton : public QObject
public:
explicit QQuickIndicatorButton(QObject *parent);
+ ~QQuickIndicatorButton() override;
bool isPressed() const;
void setPressed(bool pressed);
diff --git a/src/quicktemplates/qquickitemdelegate.cpp b/src/quicktemplates/qquickitemdelegate.cpp
index 7b0a1c12ad..d4abad9ce2 100644
--- a/src/quicktemplates/qquickitemdelegate.cpp
+++ b/src/quicktemplates/qquickitemdelegate.cpp
@@ -58,8 +58,11 @@ QQuickItemDelegate::QQuickItemDelegate(QQuickItemDelegatePrivate &dd, QQuickItem
id: listView
model: 10
delegate: ItemDelegate {
- text: modelData
+ text: index
highlighted: ListView.isCurrentItem
+
+ required property int index
+
onClicked: listView.currentIndex = index
}
}
diff --git a/src/quicktemplates/qquickitemdelegate_p.h b/src/quicktemplates/qquickitemdelegate_p.h
index 4716edc849..8236410fb3 100644
--- a/src/quicktemplates/qquickitemdelegate_p.h
+++ b/src/quicktemplates/qquickitemdelegate_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickItemDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickItemDelegate : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickItemDelegate : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL)
@@ -54,6 +54,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickItemDelegate)
-
#endif // QQUICKITEMDELEGATE_P_H
diff --git a/src/quicktemplates/qquickitemdelegate_p_p.h b/src/quicktemplates/qquickitemdelegate_p_p.h
index f1af3daf94..cf223e6b8b 100644
--- a/src/quicktemplates/qquickitemdelegate_p_p.h
+++ b/src/quicktemplates/qquickitemdelegate_p_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickItemDelegatePrivate : public QQuickAbstractButtonPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickItemDelegatePrivate : public QQuickAbstractButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickItemDelegate)
diff --git a/src/quicktemplates/qquicklabel.cpp b/src/quicktemplates/qquicklabel.cpp
index 116884a46f..ee8723d755 100644
--- a/src/quicktemplates/qquicklabel.cpp
+++ b/src/quicktemplates/qquicklabel.cpp
@@ -190,7 +190,7 @@ void QQuickLabelPrivate::accessibilityActiveChanged(bool active)
Q_Q(QQuickLabel);
QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
Q_ASSERT(accessibleAttached);
- accessibleAttached->setRole(accessibleRole());
+ accessibleAttached->setRole(effectiveAccessibleRole());
maybeSetAccessibleName(text);
}
@@ -211,8 +211,6 @@ void QQuickLabelPrivate::maybeSetAccessibleName(const QString &name)
}
#endif
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickLabelPrivate::cancelBackground()
{
Q_Q(QQuickLabel);
diff --git a/src/quicktemplates/qquicklabel_p.h b/src/quicktemplates/qquicklabel_p.h
index 5e564baad7..bef65ee8ef 100644
--- a/src/quicktemplates/qquicklabel_p.h
+++ b/src/quicktemplates/qquicklabel_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickLabelPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickLabel : public QQuickText
+class Q_QUICKTEMPLATES2_EXPORT QQuickLabel : public QQuickText
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override
@@ -93,16 +93,6 @@ private:
Q_DECLARE_PRIVATE(QQuickLabel)
};
-struct QQuickTemplatesTextForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickText)
- QML_ADDED_IN_VERSION(2, 3)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabel)
-
#endif // QQUICKLABEL_P_H
diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp
index ea37af1885..707b115983 100644
--- a/src/quicktemplates/qquickmenu.cpp
+++ b/src/quicktemplates/qquickmenu.cpp
@@ -4,8 +4,11 @@
#include "qquickmenu_p.h"
#include "qquickmenu_p_p.h"
#include "qquickmenuitem_p_p.h"
+#include <private/qtquicktemplates2-config_p.h>
+#if QT_CONFIG(quicktemplates2_container)
#include "qquickmenubaritem_p.h"
#include "qquickmenubar_p.h"
+#endif
#include "qquickpopupitem_p_p.h"
#include "qquickpopuppositioner_p_p.h"
#include "qquickaction_p.h"
@@ -26,7 +29,6 @@
#include <private/qqmlobjectmodel_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
-#include <QtQuick/private/qquickitemview_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquickwindow_p.h>
@@ -107,6 +109,16 @@ static const int SUBMENU_DELAY = 225;
}
\endcode
+ If the button should also close the menu when clicked, use the
+ \c Popup.CloseOnPressOutsideParent flag:
+ \code
+ onClicked: menu.visible = !menu.visible
+
+ Menu {
+ // ...
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ \endcode
+
Since QtQuick.Controls 2.3 (Qt 5.10), it is also possible to create sub-menus
and declare Action objects inside Menu:
@@ -571,11 +583,13 @@ void QQuickMenuPrivate::propagateKeyEvent(QKeyEvent *event)
if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(parentItem)) {
if (QQuickMenu *menu = menuItem->menu())
QQuickMenuPrivate::get(menu)->propagateKeyEvent(event);
+#if QT_CONFIG(quicktemplates2_container)
} else if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(parentItem)) {
if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) {
event->accept();
QCoreApplication::sendEvent(menuBar, event);
}
+#endif
}
}
@@ -732,6 +746,15 @@ QQuickMenu::~QQuickMenu()
// been destroyed before that is called.
while (d->contentModel->count() > 0)
d->removeItem(0, d->itemAt(0));
+
+ if (d->contentItem) {
+ QQuickItemPrivate::get(d->contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
+ QQuickItemPrivate::get(d->contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
+
+ const auto children = d->contentItem->childItems();
+ for (QQuickItem *child : std::as_const(children))
+ QQuickItemPrivate::get(child)->removeItemChangeListener(d, QQuickItemPrivate::SiblingOrder);
+ }
}
/*!
@@ -1095,7 +1118,7 @@ QString QQuickMenu::title() const
return d->title;
}
-void QQuickMenu::setTitle(QString &title)
+void QQuickMenu::setTitle(const QString &title)
{
Q_D(QQuickMenu);
if (title == d->title)
@@ -1116,7 +1139,7 @@ void QQuickMenu::setTitle(QString &title)
\include qquickicon.qdocinc grouped-properties
- \sa text, display, {Icons in Qt Quick Controls}
+ \sa AbstractButton::text, AbstractButton::display, {Icons in Qt Quick Controls}
*/
QQuickIcon QQuickMenu::icon() const
@@ -1284,10 +1307,10 @@ void QQuickMenu::popup(QQuickItem *menuItem)
#endif
// As a fallback, center the menu over its parent item.
- if (pos.isNull && d->parentItem)
+ if (!pos.isValid() && d->parentItem)
pos = QPointF((d->parentItem->width() - width()) / 2, (d->parentItem->height() - height()) / 2);
- popup(pos.isNull ? QPointF() : pos.value, menuItem);
+ popup(pos.isValid() ? pos.value() : QPointF(), menuItem);
}
void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
@@ -1343,7 +1366,7 @@ void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
\sa dismiss(), Popup::open()
*/
-void QQuickMenu::popup(QQmlV4Function *args)
+void QQuickMenu::popup(QQmlV4FunctionPtr args)
{
Q_D(QQuickMenu);
const int len = args->length();
@@ -1388,7 +1411,7 @@ void QQuickMenu::popup(QQmlV4Function *args)
pos = QPointF(xArg->asDouble(), yArg->asDouble());
}
- if (pos.isNull && (len >= 2 || (!parentItem && len >= 1))) {
+ if (!pos.isValid() && (len >= 2 || (!parentItem && len >= 1))) {
// point pos
QV4::ScopedValue posArg(scope, (*args)[parentItem ? 1 : 0]);
const QVariant var = QV4::ExecutionEngine::toVariant(posArg, QMetaType {});
@@ -1399,10 +1422,10 @@ void QQuickMenu::popup(QQmlV4Function *args)
if (parentItem)
setParentItem(parentItem);
- if (pos.isNull)
- popup(menuItem);
- else
+ if (pos.isValid())
popup(pos, menuItem);
+ else
+ popup(menuItem);
}
/*!
diff --git a/src/quicktemplates/qquickmenu_p.h b/src/quicktemplates/qquickmenu_p.h
index 351272ed05..0ff8a121ac 100644
--- a/src/quicktemplates/qquickmenu_p.h
+++ b/src/quicktemplates/qquickmenu_p.h
@@ -17,10 +17,13 @@
#include <QtQml/qqmllist.h>
#include <QtQml/qqml.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
#include "qquickpopup_p.h"
#include <QtQuickTemplates2/private/qquickicon_p.h>
+QT_REQUIRE_CONFIG(qml_object_model);
+
QT_BEGIN_NAMESPACE
class QQuickAction;
@@ -28,7 +31,7 @@ class QQmlComponent;
class QQuickMenuItem;
class QQuickMenuPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenu : public QQuickPopup
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenu : public QQuickPopup
{
Q_OBJECT
Q_PROPERTY(QVariant contentModel READ contentModel CONSTANT FINAL)
@@ -60,7 +63,7 @@ public:
QQmlListProperty<QObject> contentData();
QString title() const;
- void setTitle(QString &title);
+ void setTitle(const QString &title);
QQuickIcon icon() const;
void setIcon(const QQuickIcon &icon);
@@ -97,7 +100,7 @@ public:
void popup(QQuickItem *menuItem = nullptr);
void popup(const QPointF &pos, QQuickItem *menuItem = nullptr);
- Q_REVISION(2, 3) Q_INVOKABLE void popup(QQmlV4Function *args);
+ Q_REVISION(2, 3) Q_INVOKABLE void popup(QQmlV4FunctionPtr args);
Q_REVISION(2, 3) Q_INVOKABLE void dismiss();
protected:
@@ -133,6 +136,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenu)
-
#endif // QQUICKMENU_P_H
diff --git a/src/quicktemplates/qquickmenu_p_p.h b/src/quicktemplates/qquickmenu_p_p.h
index 509614d2d1..552f35794a 100644
--- a/src/quicktemplates/qquickmenu_p_p.h
+++ b/src/quicktemplates/qquickmenu_p_p.h
@@ -28,7 +28,7 @@ class QQmlComponent;
class QQmlObjectModel;
class QQuickMenuItem;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuPrivate : public QQuickPopupPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuPrivate : public QQuickPopupPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickMenu)
diff --git a/src/quicktemplates/qquickmenubar.cpp b/src/quicktemplates/qquickmenubar.cpp
index d8ecbb6626..62c7680d0d 100644
--- a/src/quicktemplates/qquickmenubar.cpp
+++ b/src/quicktemplates/qquickmenubar.cpp
@@ -171,7 +171,7 @@ void QQuickMenuBarPrivate::onItemTriggered()
void QQuickMenuBarPrivate::onMenuAboutToHide()
{
- if (triggering || !currentItem || (currentItem->isHovered() && currentItem->isEnabled()) || !currentItem->isHighlighted())
+ if (triggering || !currentItem || !currentItem->isHighlighted())
return;
popupMode = false;
@@ -502,6 +502,7 @@ void QQuickMenuBar::keyPressEvent(QKeyEvent *event)
else
d->activatePreviousItem();
break;
+ // This is triggered when no popup is open but a menu bar item is highlighted and has focus.
case Qt::Key_Escape:
if (d->currentItem) {
d->activateItem(nullptr);
diff --git a/src/quicktemplates/qquickmenubar_p.h b/src/quicktemplates/qquickmenubar_p.h
index 2b206a41b1..daa6bf362b 100644
--- a/src/quicktemplates/qquickmenubar_p.h
+++ b/src/quicktemplates/qquickmenubar_p.h
@@ -17,12 +17,14 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickMenu;
class QQuickMenuBarPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBar : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuBar : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL)
@@ -72,6 +74,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenuBar)
-
#endif // QQUICKMENUBAR_P_H
diff --git a/src/quicktemplates/qquickmenubar_p_p.h b/src/quicktemplates/qquickmenubar_p_p.h
index 2ac3af00ed..f9a8acbd3d 100644
--- a/src/quicktemplates/qquickmenubar_p_p.h
+++ b/src/quicktemplates/qquickmenubar_p_p.h
@@ -18,12 +18,14 @@
#include <QtQuickTemplates2/private/qquickmenubar_p.h>
#include <QtQuickTemplates2/private/qquickcontainer_p_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlComponent;
class QQuickMenuBarItem;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBarPrivate : public QQuickContainerPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuBarPrivate : public QQuickContainerPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickMenuBar)
diff --git a/src/quicktemplates/qquickmenubaritem.cpp b/src/quicktemplates/qquickmenubaritem.cpp
index 03e8c60f03..cc5fb83e40 100644
--- a/src/quicktemplates/qquickmenubaritem.cpp
+++ b/src/quicktemplates/qquickmenubaritem.cpp
@@ -46,6 +46,43 @@ void QQuickMenuBarItemPrivate::setMenuBar(QQuickMenuBar *newMenuBar)
emit q->menuBarChanged();
}
+bool QQuickMenuBarItemPrivate::handlePress(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickMenuBarItem);
+ const bool handled = QQuickAbstractButtonPrivate::handlePress(point, timestamp);
+ if (!handled)
+ return false;
+
+ const bool wasTouchPress = touchId != -1;
+ if (!wasTouchPress) {
+ // Open the menu when it's a mouse press.
+ emit q->triggered();
+ }
+
+ return true;
+}
+
+bool QQuickMenuBarItemPrivate::handleRelease(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickMenuBarItem);
+ const bool wasTouchPress = touchId != -1;
+ const bool handled = QQuickAbstractButtonPrivate::handleRelease(point, timestamp);
+ if (!handled)
+ return false;
+
+ if (wasDoubleClick || !wasTouchPress) {
+ // Don't open the menu on mouse release, as it should be done on press.
+ return handled;
+ }
+
+ if (wasTouchPress) {
+ // Open the menu.
+ emit q->triggered();
+ }
+
+ return true;
+}
+
QPalette QQuickMenuBarItemPrivate::defaultPalette() const
{
return QQuickTheme::palette(QQuickTheme::MenuBar);
@@ -55,7 +92,6 @@ QQuickMenuBarItem::QQuickMenuBarItem(QQuickItem *parent)
: QQuickAbstractButton(*(new QQuickMenuBarItemPrivate), parent)
{
setFocusPolicy(Qt::NoFocus);
- connect(this, &QQuickAbstractButton::clicked, this, &QQuickMenuBarItem::triggered);
}
/*!
@@ -129,6 +165,50 @@ void QQuickMenuBarItem::setHighlighted(bool highlighted)
emit highlightedChanged();
}
+bool QQuickMenuBarItem::event(QEvent *event)
+{
+#if QT_CONFIG(shortcut)
+ Q_D(QQuickMenuBarItem);
+ if (event->type() == QEvent::Shortcut) {
+ auto *shortcutEvent = static_cast<QShortcutEvent *>(event);
+ if (shortcutEvent->shortcutId() == d->shortcutId) {
+ d->trigger();
+ emit triggered();
+ return true;
+ }
+ }
+#endif
+ return QQuickControl::event(event);
+}
+
+void QQuickMenuBarItem::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QQuickMenuBarItem);
+ QQuickControl::keyPressEvent(event);
+ if (d->acceptKeyClick(static_cast<Qt::Key>(event->key()))) {
+ d->setPressPoint(d->centerPressPoint());
+ setPressed(true);
+ emit pressed();
+ event->accept();
+ }
+}
+
+void QQuickMenuBarItem::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QQuickMenuBarItem);
+ QQuickControl::keyReleaseEvent(event);
+ if (d->pressed && d->acceptKeyClick(static_cast<Qt::Key>(event->key()))) {
+ setPressed(false);
+ emit released();
+ d->trigger();
+ // We override these event functions so that we can emit triggered here.
+ // We can't just connect clicked to triggered, because that would cause mouse clicks
+ // to open the menu, when only presses should.
+ emit triggered();
+ event->accept();
+ }
+}
+
void QQuickMenuBarItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickMenuBarItem);
diff --git a/src/quicktemplates/qquickmenubaritem_p.h b/src/quicktemplates/qquickmenubaritem_p.h
index 87faba2e35..c101d1f580 100644
--- a/src/quicktemplates/qquickmenubaritem_p.h
+++ b/src/quicktemplates/qquickmenubaritem_p.h
@@ -17,13 +17,15 @@
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickMenu;
class QQuickMenuBar;
class QQuickMenuBarItemPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBarItem : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuBarItem : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(QQuickMenuBar *menuBar READ menuBar NOTIFY menuBarChanged FINAL)
@@ -50,6 +52,10 @@ Q_SIGNALS:
void highlightedChanged();
protected:
+ bool event(QEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
QFont defaultFont() const override;
@@ -65,6 +71,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenuBarItem)
-
#endif // QQUICKMENUBARITEM_P_H
diff --git a/src/quicktemplates/qquickmenubaritem_p_p.h b/src/quicktemplates/qquickmenubaritem_p_p.h
index c2dcdd4222..b6f70cc274 100644
--- a/src/quicktemplates/qquickmenubaritem_p_p.h
+++ b/src/quicktemplates/qquickmenubaritem_p_p.h
@@ -35,6 +35,9 @@ public:
void setMenuBar(QQuickMenuBar *menuBar);
+ bool handlePress(const QPointF &point, ulong timestamp) override;
+ bool handleRelease(const QPointF &point, ulong timestamp) override;
+
QPalette defaultPalette() const override;
bool highlighted = false;
diff --git a/src/quicktemplates/qquickmenuitem_p.h b/src/quicktemplates/qquickmenuitem_p.h
index f38c437dbd..fb99dc2502 100644
--- a/src/quicktemplates/qquickmenuitem_p.h
+++ b/src/quicktemplates/qquickmenuitem_p.h
@@ -16,13 +16,16 @@
//
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
+
+QT_REQUIRE_CONFIG(qml_object_model);
QT_BEGIN_NAMESPACE
class QQuickMenu;
class QQuickMenuItemPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuItem : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuItem : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL)
@@ -71,6 +74,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenuItem)
-
#endif // QQUICKMENUITEM_P_H
diff --git a/src/quicktemplates/qquickmenuseparator.cpp b/src/quicktemplates/qquickmenuseparator.cpp
index d9aa770ca5..0766151d1c 100644
--- a/src/quicktemplates/qquickmenuseparator.cpp
+++ b/src/quicktemplates/qquickmenuseparator.cpp
@@ -33,7 +33,7 @@ QT_BEGIN_NAMESPACE
\sa {Customizing Menu}, Menu, {Separator Controls}
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuSeparatorPrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuSeparatorPrivate : public QQuickControlPrivate
{
Q_DECLARE_PUBLIC(QQuickMenuSeparator)
diff --git a/src/quicktemplates/qquickmenuseparator_p.h b/src/quicktemplates/qquickmenuseparator_p.h
index abf3221461..ec7517ee90 100644
--- a/src/quicktemplates/qquickmenuseparator_p.h
+++ b/src/quicktemplates/qquickmenuseparator_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickMenuSeparator;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuSeparator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuSeparator : public QQuickControl
{
Q_OBJECT
QML_NAMED_ELEMENT(MenuSeparator)
@@ -44,6 +44,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenuSeparator)
-
#endif // QQUICKMENUSEPARATOR_P_H
diff --git a/src/quicktemplates/qquickmonthgrid.cpp b/src/quicktemplates/qquickmonthgrid.cpp
index a470017676..957c9b8f93 100644
--- a/src/quicktemplates/qquickmonthgrid.cpp
+++ b/src/quicktemplates/qquickmonthgrid.cpp
@@ -92,8 +92,6 @@ public:
bool handleRelease(const QPointF &point, ulong timestamp) override;
void handleUngrab() override;
- static void setContextProperty(QQuickItem *item, const QString &name, const QVariant &value);
-
QString title;
QVariant source;
QDate pressedDate;
@@ -141,7 +139,6 @@ void QQuickMonthGridPrivate::updatePress(const QPointF &pos)
Q_Q(QQuickMonthGrid);
clearPress(false);
pressedItem = cellAt(pos);
- setContextProperty(pressedItem, QStringLiteral("pressed"), true);
pressedDate = dateOf(pressedItem);
if (pressedDate.isValid())
emit q->pressed(pressedDate);
@@ -150,7 +147,6 @@ void QQuickMonthGridPrivate::updatePress(const QPointF &pos)
void QQuickMonthGridPrivate::clearPress(bool clicked)
{
Q_Q(QQuickMonthGrid);
- setContextProperty(pressedItem, QStringLiteral("pressed"), false);
if (pressedDate.isValid()) {
emit q->released(pressedDate);
if (clicked)
@@ -190,16 +186,6 @@ void QQuickMonthGridPrivate::handleUngrab()
clearPress(false);
}
-void QQuickMonthGridPrivate::setContextProperty(QQuickItem *item, const QString &name, const QVariant &value)
-{
- QQmlContext *context = qmlContext(item);
- if (context && context->isValid()) {
- context = context->parentContext();
- if (context && context->isValid())
- context->setContextProperty(name, value);
- }
-}
-
QQuickMonthGrid::QQuickMonthGrid(QQuickItem *parent) :
QQuickControl(*(new QQuickMonthGridPrivate), parent)
{
@@ -357,13 +343,6 @@ void QQuickMonthGrid::componentComplete()
{
Q_D(QQuickMonthGrid);
QQuickControl::componentComplete();
- if (d->contentItem) {
- const auto childItems = d->contentItem->childItems();
- for (QQuickItem *child : childItems) {
- if (!QQuickItemPrivate::get(child)->isTransparentForPositioner())
- d->setContextProperty(child, QStringLiteral("pressed"), false);
- }
- }
d->resizeItems();
}
diff --git a/src/quicktemplates/qquickmonthgrid_p.h b/src/quicktemplates/qquickmonthgrid_p.h
index 9c5ffad030..e7e4421e50 100644
--- a/src/quicktemplates/qquickmonthgrid_p.h
+++ b/src/quicktemplates/qquickmonthgrid_p.h
@@ -58,10 +58,10 @@ Q_SIGNALS:
void titleChanged();
void delegateChanged();
- void pressed(const QDate &date);
- void released(const QDate &date);
- void clicked(const QDate &date);
- void pressAndHold(const QDate &date);
+ void pressed(QDate date);
+ void released(QDate date);
+ void clicked(QDate date);
+ void pressAndHold(QDate date);
protected:
void componentComplete() override;
@@ -79,6 +79,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMonthGrid)
-
#endif // QQUICKMONTHGRID_P_H
diff --git a/src/quicktemplates/qquickmonthmodel.cpp b/src/quicktemplates/qquickmonthmodel.cpp
index 0910ea9be7..60d5e6fce2 100644
--- a/src/quicktemplates/qquickmonthmodel.cpp
+++ b/src/quicktemplates/qquickmonthmodel.cpp
@@ -138,7 +138,7 @@ QDate QQuickMonthModel::dateAt(int index) const
return d->dates.value(index);
}
-int QQuickMonthModel::indexOf(const QDate &date) const
+int QQuickMonthModel::indexOf(QDate date) const
{
Q_D(const QQuickMonthModel);
if (date < d->dates.first() || date > d->dates.last())
diff --git a/src/quicktemplates/qquickmonthmodel_p.h b/src/quicktemplates/qquickmonthmodel_p.h
index 8d1c0fc062..e629a7f8d1 100644
--- a/src/quicktemplates/qquickmonthmodel_p.h
+++ b/src/quicktemplates/qquickmonthmodel_p.h
@@ -50,7 +50,7 @@ public:
void setTitle(const QString &title);
Q_INVOKABLE QDate dateAt(int index) const;
- Q_INVOKABLE int indexOf(const QDate &date) const;
+ Q_INVOKABLE int indexOf(QDate date) const;
enum {
DateRole = Qt::UserRole + 1,
@@ -78,6 +78,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMonthModel)
-
#endif // QQUICKMONTHMODEL_P_H
diff --git a/src/quicktemplates/qquickoverlay.cpp b/src/quicktemplates/qquickoverlay.cpp
index bbbde24602..36fa669d22 100644
--- a/src/quicktemplates/qquickoverlay.cpp
+++ b/src/quicktemplates/qquickoverlay.cpp
@@ -9,6 +9,7 @@
#include "qquickdrawer_p.h"
#include "qquickdrawer_p_p.h"
#include "qquickapplicationwindow_p.h"
+#include <QtGui/qpainterpath.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlproperty.h>
#include <QtQml/qqmlcomponent.h>
@@ -66,6 +67,11 @@ void QQuickOverlayPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChang
updateGeometry();
}
+void QQuickOverlayPrivate::itemRotationChanged(QQuickItem *)
+{
+ updateGeometry();
+}
+
bool QQuickOverlayPrivate::startDrag(QEvent *event, const QPointF &pos)
{
Q_Q(QQuickOverlay);
@@ -256,38 +262,15 @@ void QQuickOverlayPrivate::setMouseGrabberPopup(QQuickPopup *popup)
void QQuickOverlayPrivate::updateGeometry()
{
Q_Q(QQuickOverlay);
- if (!window)
+ if (!window || !window->contentItem())
return;
- QPointF pos;
- QSizeF size = window->size();
- qreal rotation = 0;
-
- switch (window->contentOrientation()) {
- case Qt::PrimaryOrientation:
- case Qt::PortraitOrientation:
- size = window->size();
- break;
- case Qt::LandscapeOrientation:
- rotation = 90;
- pos = QPointF((size.width() - size.height()) / 2, -(size.width() - size.height()) / 2);
- size.transpose();
- break;
- case Qt::InvertedPortraitOrientation:
- rotation = 180;
- break;
- case Qt::InvertedLandscapeOrientation:
- rotation = 270;
- pos = QPointF((size.width() - size.height()) / 2, -(size.width() - size.height()) / 2);
- size.transpose();
- break;
- default:
- break;
- }
+ const QSizeF size = window->contentItem()->size();
+ const QPointF pos(-(size.width() - window->size().width()) / 2,
+ -(size.height() - window->size().height()) / 2);
q->setSize(size);
q->setPosition(pos);
- q->setRotation(rotation);
}
QQuickOverlay::QQuickOverlay(QQuickItem *parent)
@@ -307,7 +290,8 @@ QQuickOverlay::QQuickOverlay(QQuickItem *parent)
QQuickItemPrivate::get(parent)->addItemChangeListener(d, QQuickItemPrivate::Geometry);
if (QQuickWindow *window = parent->window()) {
window->installEventFilter(this);
- QObjectPrivate::connect(window, &QWindow::contentOrientationChanged, d, &QQuickOverlayPrivate::updateGeometry);
+ if (QQuickItem *contentItem = window->contentItem())
+ QQuickItemPrivate::get(contentItem)->addItemChangeListener(d, QQuickItemPrivate::Rotation);
}
}
}
@@ -315,8 +299,13 @@ QQuickOverlay::QQuickOverlay(QQuickItem *parent)
QQuickOverlay::~QQuickOverlay()
{
Q_D(QQuickOverlay);
- if (QQuickItem *parent = parentItem())
+ if (QQuickItem *parent = parentItem()) {
QQuickItemPrivate::get(parent)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
+ if (QQuickWindow *window = parent->window()) {
+ if (QQuickItem *contentItem = window->contentItem())
+ QQuickItemPrivate::get(contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Rotation);
+ }
+ }
}
QQmlComponent *QQuickOverlay::modal() const
@@ -392,7 +381,7 @@ void QQuickOverlay::geometryChange(const QRectF &newGeometry, const QRectF &oldG
Q_D(QQuickOverlay);
QQuickItem::geometryChange(newGeometry, oldGeometry);
for (QQuickPopup *popup : std::as_const(d->allPopups))
- QQuickPopupPrivate::get(popup)->resizeOverlay();
+ QQuickPopupPrivate::get(popup)->resizeDimmer();
}
void QQuickOverlay::mousePressEvent(QMouseEvent *event)
@@ -522,6 +511,12 @@ bool QQuickOverlay::eventFilter(QObject *object, QEvent *event)
// Make sure to accept the touch event in order to receive the consequent
// touch events, to be able to close non-modal popups on release outside.
event->accept();
+ // Since we eat the event, QQuickWindow::event never sees it to clean up the
+ // grabber states. So we have to do so explicitly.
+ if (QQuickWindow *window = parentItem() ? parentItem()->window() : nullptr) {
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
+ d->clearGrabbers(static_cast<QPointerEvent *>(event));
+ }
return true;
#endif
@@ -567,7 +562,18 @@ bool QQuickOverlay::eventFilter(QObject *object, QEvent *event)
if (targetItems.isEmpty())
break;
+ QQuickItem * const dimmerItem = property("_q_dimmerItem").value<QQuickItem *>();
QQuickItem * const topItem = targetItems.first();
+
+ QQuickItem *item = topItem;
+ while ((item = item->parentItem())) {
+ if (qobject_cast<QQuickPopupItem *>(item))
+ break;
+ }
+
+ if (!item && dimmerItem != topItem && isAncestorOf(topItem))
+ break;
+
const auto popups = d->stackingOrderPopups();
// Eat the event if receiver topItem is not a child of a popup before
// the first modal popup.
@@ -575,8 +581,8 @@ bool QQuickOverlay::eventFilter(QObject *object, QEvent *event)
const QQuickItem *popupItem = popup->popupItem();
if (!popupItem)
continue;
- // if we reach a popup that contains the item, deliver the event
- if (popupItem->isAncestorOf(topItem))
+ // if current popup item matches with any popup in stack, deliver the event
+ if (popupItem == item)
break;
// if the popup doesn't contain the item but is modal, eat the event
if (popup->overlayEvent(topItem, we))
diff --git a/src/quicktemplates/qquickoverlay_p.h b/src/quicktemplates/qquickoverlay_p.h
index 47f7e62615..16f0ba09db 100644
--- a/src/quicktemplates/qquickoverlay_p.h
+++ b/src/quicktemplates/qquickoverlay_p.h
@@ -25,7 +25,7 @@ class QQuickOverlayPrivate;
class QQuickOverlayAttached;
class QQuickOverlayAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickOverlay : public QQuickItem
+class Q_QUICKTEMPLATES2_EXPORT QQuickOverlay : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQmlComponent *modal READ modal WRITE setModal NOTIFY modalChanged FINAL)
@@ -76,7 +76,7 @@ private:
Q_DECLARE_PRIVATE(QQuickOverlay)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickOverlayAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickOverlayAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickOverlay *overlay READ overlay NOTIFY overlayChanged FINAL)
@@ -108,6 +108,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickOverlay)
-
#endif // QQUICKOVERLAY_P_H
diff --git a/src/quicktemplates/qquickoverlay_p_p.h b/src/quicktemplates/qquickoverlay_p_p.h
index 38c214804f..3d96540d45 100644
--- a/src/quicktemplates/qquickoverlay_p_p.h
+++ b/src/quicktemplates/qquickoverlay_p_p.h
@@ -20,6 +20,8 @@
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickPopup;
@@ -54,6 +56,7 @@ public:
QList<QQuickPopup *> stackingOrderDrawers() const;
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override;
+ void itemRotationChanged(QQuickItem *item) override;
void updateGeometry();
@@ -64,6 +67,7 @@ public:
// QQuickDrawer by the time removePopup is called.
QList<QQuickPopup *> allDrawers;
QPointer<QQuickPopup> mouseGrabberPopup;
+ QPointer<QQuickItem> lastActiveFocusItem;
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickpage.cpp b/src/quicktemplates/qquickpage.cpp
index 28348c2518..55c6d3a178 100644
--- a/src/quicktemplates/qquickpage.cpp
+++ b/src/quicktemplates/qquickpage.cpp
@@ -3,9 +3,11 @@
#include "qquickpage_p.h"
#include "qquickpage_p_p.h"
-#include "qquicktabbar_p.h"
#include "qquicktoolbar_p.h"
+#if QT_CONFIG(quicktemplates2_container)
+#include "qquicktabbar_p.h"
#include "qquickdialogbuttonbox_p.h"
+#endif
QT_BEGIN_NAMESPACE
@@ -75,23 +77,27 @@ namespace {
Footer
};
+Q_STATIC_ASSERT(int(Header) == int(QQuickToolBar::Header));
+Q_STATIC_ASSERT(int(Footer) == int(QQuickToolBar::Footer));
+
+#if QT_CONFIG(quicktemplates2_container)
Q_STATIC_ASSERT(int(Header) == int(QQuickTabBar::Header));
Q_STATIC_ASSERT(int(Footer) == int(QQuickTabBar::Footer));
- Q_STATIC_ASSERT(int(Header) == int(QQuickToolBar::Header));
- Q_STATIC_ASSERT(int(Footer) == int(QQuickToolBar::Footer));
-
Q_STATIC_ASSERT(int(Header) == int(QQuickDialogButtonBox::Header));
Q_STATIC_ASSERT(int(Footer) == int(QQuickDialogButtonBox::Footer));
+#endif
static void setPos(QQuickItem *item, Position position)
{
if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(item))
toolBar->setPosition(static_cast<QQuickToolBar::Position>(position));
+#if QT_CONFIG(quicktemplates2_container)
else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(item))
tabBar->setPosition(static_cast<QQuickTabBar::Position>(position));
else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(item))
buttonBox->setPosition(static_cast<QQuickDialogButtonBox::Position>(position));
+#endif
}
}
@@ -110,8 +116,10 @@ void QQuickPagePrivate::relayout()
contentItem->setHeight(q->availableHeight() - hh - fh - hsp - fsp);
}
- if (header)
+ if (header) {
+ header->setY(0);
header->setWidth(q->width());
+ }
if (footer) {
footer->setY(q->height() - footer->height());
diff --git a/src/quicktemplates/qquickpage_p.h b/src/quicktemplates/qquickpage_p.h
index b7ce7e9bc9..02725cc7c5 100644
--- a/src/quicktemplates/qquickpage_p.h
+++ b/src/quicktemplates/qquickpage_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickPagePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPage : public QQuickPane
+class Q_QUICKTEMPLATES2_EXPORT QQuickPage : public QQuickPane
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
@@ -85,6 +85,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPage)
-
#endif // QQUICKPAGE_P_H
diff --git a/src/quicktemplates/qquickpageindicator.cpp b/src/quicktemplates/qquickpageindicator.cpp
index b0bc5188fe..8b77b79e9f 100644
--- a/src/quicktemplates/qquickpageindicator.cpp
+++ b/src/quicktemplates/qquickpageindicator.cpp
@@ -177,6 +177,13 @@ QQuickPageIndicator::QQuickPageIndicator(QQuickItem *parent)
{
}
+QQuickPageIndicator::~QQuickPageIndicator()
+{
+ Q_D(QQuickPageIndicator);
+ if (d->contentItem)
+ QQuickItemPrivate::get(d->contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
+}
+
/*!
\qmlproperty int QtQuick.Controls::PageIndicator::count
diff --git a/src/quicktemplates/qquickpageindicator_p.h b/src/quicktemplates/qquickpageindicator_p.h
index d6c648716c..db4d83fad7 100644
--- a/src/quicktemplates/qquickpageindicator_p.h
+++ b/src/quicktemplates/qquickpageindicator_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQmlComponent;
class QQuickPageIndicatorPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPageIndicator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickPageIndicator : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged FINAL)
@@ -34,6 +34,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPageIndicator : public QQuickContro
public:
explicit QQuickPageIndicator(QQuickItem *parent = nullptr);
+ ~QQuickPageIndicator() override;
int count() const;
void setCount(int count);
@@ -71,6 +72,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPageIndicator)
-
#endif // QQUICKPAGEINDICATOR_P_H
diff --git a/src/quicktemplates/qquickpane.cpp b/src/quicktemplates/qquickpane.cpp
index 9f14843db4..02f32e720e 100644
--- a/src/quicktemplates/qquickpane.cpp
+++ b/src/quicktemplates/qquickpane.cpp
@@ -30,6 +30,10 @@ Q_LOGGING_CATEGORY(lcPane, "qt.quick.controls.pane")
Pane's \l[QtQuickControls2]{Control::}{contentItem}. Items created
dynamically need to be explicitly parented to the \c contentItem.
+ As mentioned in \l {Event Handling}, Pane does not let click and touch
+ events through to items beneath it. If \l [QML] {Control::}{wheelEnabled}
+ is \c true, the same applies to mouse wheel events.
+
\section1 Content Sizing
If only a single item is used within a Pane, it will resize to fit the
@@ -104,6 +108,7 @@ void QQuickPanePrivate::init()
#endif
connect(q, &QQuickControl::implicitContentWidthChanged, this, &QQuickPanePrivate::updateContentWidth);
connect(q, &QQuickControl::implicitContentHeightChanged, this, &QQuickPanePrivate::updateContentHeight);
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
QList<QQuickItem *> QQuickPanePrivate::contentChildItems() const
@@ -139,14 +144,25 @@ void QQuickPanePrivate::itemImplicitHeightChanged(QQuickItem *item)
updateImplicitContentHeight();
}
+void QQuickPanePrivate::itemDestroyed(QQuickItem *item)
+{
+ // Do this check before calling the base class implementation, as that clears contentItem.
+ if (item == firstChild)
+ firstChild = nullptr;
+
+ QQuickControlPrivate::itemDestroyed(item);
+}
+
void QQuickPanePrivate::contentChildrenChange()
{
Q_Q(QQuickPane);
- QQuickItem *newFirstChild = contentChildItems().value(0);
+
+ QQuickItem *newFirstChild = getFirstChild();
+
if (newFirstChild != firstChild) {
if (firstChild)
removeImplicitSizeListener(firstChild);
- if (newFirstChild)
+ if (newFirstChild && newFirstChild != contentItem)
addImplicitSizeListener(newFirstChild);
firstChild = newFirstChild;
}
@@ -171,6 +187,16 @@ qreal QQuickPanePrivate::getContentWidth() const
return 0;
}
+QQuickItem* QQuickPanePrivate::getFirstChild() const
+{
+ // The first child varies depending on how the content item is declared.
+ // If it's declared as a child of the Pane, it will be parented to the
+ // default QQuickContentItem. If it's assigned to the contentItem property
+ // directly, QQuickControl::contentItem will be used.
+ return (qobject_cast<QQuickContentItem *>(contentItem)
+ ? contentChildItems().value(0) : contentItem.data());
+}
+
qreal QQuickPanePrivate::getContentHeight() const
{
if (!contentItem)
diff --git a/src/quicktemplates/qquickpane_p.h b/src/quicktemplates/qquickpane_p.h
index d059e629d4..a2a5cbbff6 100644
--- a/src/quicktemplates/qquickpane_p.h
+++ b/src/quicktemplates/qquickpane_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickPanePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPane : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickPane : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth RESET resetContentWidth NOTIFY contentWidthChanged FINAL)
@@ -69,6 +69,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPane)
-
#endif // QQUICKPANE_P_H
diff --git a/src/quicktemplates/qquickpane_p_p.h b/src/quicktemplates/qquickpane_p_p.h
index 7a8e68a068..71da80090a 100644
--- a/src/quicktemplates/qquickpane_p_p.h
+++ b/src/quicktemplates/qquickpane_p_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickPane;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPanePrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickPanePrivate : public QQuickControlPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickPane)
@@ -31,6 +31,7 @@ public:
virtual QQmlListProperty<QObject> contentData();
virtual QQmlListProperty<QQuickItem> contentChildren();
virtual QList<QQuickItem *> contentChildItems() const;
+ virtual QQuickItem *getFirstChild() const;
QQuickItem *getContentItem() override;
@@ -39,6 +40,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
void contentChildrenChange();
diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp
index 5b26789951..6aa88a465e 100644
--- a/src/quicktemplates/qquickpopup.cpp
+++ b/src/quicktemplates/qquickpopup.cpp
@@ -9,11 +9,14 @@
#include "qquickapplicationwindow_p.h"
#include "qquickoverlay_p_p.h"
#include "qquickcontrol_p_p.h"
+#if QT_CONFIG(quicktemplates2_container)
#include "qquickdialog_p.h"
+#endif
#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlinfo.h>
#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <QtQuick/private/qquicktransition_p.h>
#include <QtQuick/private/qquickitem_p.h>
@@ -169,6 +172,36 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
}
\endcode
+ \note The popup's \l{contentItem}{content item} gets parented to the
+ \l{Overlay::overlay}{overlay}, and does not live within the popup's parent.
+ Because of that, a \l{Item::scale}{scale} applied to the tree in which
+ the popup lives does not apply to the visual popup. To make the popup
+ of e.g. a \l{ComboBox} follow the scale of the combobox, apply the same scale
+ to the \l{Overlay::overlay}{overlay} as well:
+
+ \code
+ Window {
+ property double scaleFactor: 2.0
+
+ Scale {
+ id: scale
+ xScale: scaleFactor
+ yScale: scaleFactor
+ }
+ Item {
+ id: scaledContent
+ transform: scale
+
+ ComboBox {
+ id: combobox
+ // ...
+ }
+ }
+
+ Overlay.overlay.transform: scale
+ }
+ \endcode
+
\section1 Popup Positioning
Similar to items in Qt Quick, Popup's \l x and \l y coordinates are
@@ -186,6 +219,47 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
To ensure that the popup is positioned within the bounds of the enclosing
window, the \l margins property can be set to a non-negative value.
+ \section1 Showing Non-Child Items in Front of Popup
+
+ Popup sets its contentItem's
+ \l{qtquick-visualcanvas-visualparent.html}{visual parent}
+ to be the window's \l{Overlay::overlay}{overlay}, in order to ensure that
+ the popup appears in front of everything else in the scene.
+ In some cases, it might be useful to put an item in front of a popup,
+ such as a \l [QML QtVirtualKeyboard] {InputPanel} {virtual keyboard}.
+ This can be done by setting the item's parent to the overlay,
+ and giving the item a positive z value. The same result can also be
+ achieved by waiting until the popup is opened, before re-parenting the item
+ to the overlay.
+
+ \omit
+ This shouldn't be a snippet, since we don't want VKB to be a dependency to controls.
+ \endomit
+ \qml
+ Popup {
+ id: popup
+ visible: true
+ anchors.centerIn: parent
+ margins: 10
+ closePolicy: Popup.CloseOnEscape
+ ColumnLayout {
+ TextField {
+ placeholderText: qsTr("Username")
+ }
+ TextField {
+ placeholderText: qsTr("Password")
+ echoMode: TextInput.Password
+ }
+ }
+ }
+ InputPanel {
+ parent: Overlay.overlay
+ width: parent.width
+ y: popup.y + popup.topMargin + (window.activeFocusItem?.y ?? 0) + (window.activeFocusItem?.height ?? 0)
+ z: 1
+ }
+ \endqml
+
\section1 Popup Transitions
Since Qt 5.15.3 the following properties are restored to their original values from before
@@ -220,6 +294,50 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
\endlist
\sa {Popup Controls}, {Customizing Popup}, ApplicationWindow
+
+ \section1 Property Propagation
+
+ Popup inherits fonts, palettes and attached properties through its parent
+ window, not its \l {Visual Parent}{object or visual parent}:
+
+ \snippet qtquickcontrols-popup-property-propagation.qml file
+
+ \image qtquickcontrols-basic-popup-property-propagation.png
+
+ In addition, popups do not propagate their properties to child popups. This
+ behavior is modelled on Qt Widgets, where a \c Qt::Popup widget is a
+ top-level window. Top-level windows do not propagate their properties to
+ child windows.
+
+ Certain derived types like ComboBox are typically implemented in such a way
+ that the popup is considered an integral part of the control, and as such,
+ may inherit things like attached properties. For example, in the
+ \l {Material Style}{Material style} ComboBox, the theme and other attached
+ properties are explicitly inherited by the Popup from the ComboBox itself:
+
+ \code
+ popup: T.Popup {
+ // ...
+
+ Material.theme: control.Material.theme
+ Material.accent: control.Material.accent
+ Material.primary: control.Material.primary
+ }
+ \endcode
+
+ So, to ensure that a child popup has the same property values as its parent
+ popup, explicitly set those properties:
+
+ \code
+ Popup {
+ id: parentPopup
+ // ...
+
+ Popup {
+ palette: parentPopup.palette
+ }
+ }
+ \endcode
*/
/*!
@@ -279,9 +397,11 @@ void QQuickPopupPrivate::init()
void QQuickPopupPrivate::closeOrReject()
{
Q_Q(QQuickPopup);
+#if QT_CONFIG(quicktemplates2_container)
if (QQuickDialog *dialog = qobject_cast<QQuickDialog*>(q))
dialog->reject();
else
+#endif
q->close();
touchId = -1;
}
@@ -340,7 +460,7 @@ bool QQuickPopupPrivate::blockInput(QQuickItem *item, const QPointF &point) cons
// a) outside a non-modal popup,
// b) to popup children/content, or
// b) outside a modal popups's background dimming
- return modal && !popupItem->isAncestorOf(item) && (!dimmer || dimmer->contains(dimmer->mapFromScene(point)));
+ return modal && ((popupItem != item) && !popupItem->isAncestorOf(item)) && (!dimmer || dimmer->contains(dimmer->mapFromScene(point)));
}
bool QQuickPopupPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp)
@@ -481,7 +601,7 @@ bool QQuickPopupPrivate::prepareEnterTransition()
}
if (dim)
createOverlay();
- showOverlay();
+ showDimmer();
emit q->aboutToShow();
visible = true;
transitionState = EnterTransition;
@@ -489,6 +609,10 @@ bool QQuickPopupPrivate::prepareEnterTransition()
getPositioner()->setParentItem(parentItem);
emit q->visibleChanged();
+ auto *overlayPrivate = QQuickOverlayPrivate::get(overlay);
+ if (overlayPrivate->lastActiveFocusItem.isNull())
+ overlayPrivate->lastActiveFocusItem = window->activeFocusItem();
+
if (focus)
popupItem->setFocus(true, Qt::PopupFocusReason);
}
@@ -501,6 +625,8 @@ bool QQuickPopupPrivate::prepareExitTransition()
if (transitionState == ExitTransition && transitionManager.isRunning())
return false;
+ Q_ASSERT(popupItem);
+
// We need to cache the original scale and opacity values so we can reset it after
// the exit transition is done so they have the original values again
prevScale = popupItem->scale();
@@ -509,12 +635,15 @@ bool QQuickPopupPrivate::prepareExitTransition()
if (transitionState != ExitTransition) {
// The setFocus(false) call below removes any active focus before we're
// able to check it in finalizeExitTransition.
- if (!hadActiveFocusBeforeExitTransition)
- hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus();
+ if (!hadActiveFocusBeforeExitTransition) {
+ const auto *da = QQuickItemPrivate::get(popupItem)->deliveryAgentPrivate();
+ hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus() || (da && da->focusTargetItem() == popupItem);
+ }
+
if (focus)
popupItem->setFocus(false, Qt::PopupFocusReason);
transitionState = ExitTransition;
- hideOverlay();
+ hideDimmer();
emit q->aboutToHide();
emit q->openedChanged();
}
@@ -538,7 +667,7 @@ void QQuickPopupPrivate::finalizeExitTransition()
popupItem->setParentItem(nullptr);
popupItem->setVisible(false);
}
- destroyOverlay();
+ destroyDimmer();
if (hadActiveFocusBeforeExitTransition && window) {
// restore focus to the next popup in chain, or to the window content if there are no other popups open
@@ -556,11 +685,17 @@ void QQuickPopupPrivate::finalizeExitTransition()
if (nextFocusPopup) {
nextFocusPopup->forceActiveFocus(Qt::PopupFocusReason);
} else {
- QQuickApplicationWindow *applicationWindow = qobject_cast<QQuickApplicationWindow*>(window);
- if (applicationWindow)
- applicationWindow->contentItem()->setFocus(true, Qt::PopupFocusReason);
- else
- window->contentItem()->setFocus(true, Qt::PopupFocusReason);
+ auto *appWindow = qobject_cast<QQuickApplicationWindow*>(window);
+ auto *contentItem = appWindow ? appWindow->contentItem() : window->contentItem();
+ auto *overlay = QQuickOverlay::overlay(window);
+ auto *overlayPrivate = QQuickOverlayPrivate::get(overlay);
+ if (!contentItem->scopedFocusItem()
+ && !overlayPrivate->lastActiveFocusItem.isNull()) {
+ overlayPrivate->lastActiveFocusItem->setFocus(true, Qt::OtherFocusReason);
+ } else {
+ contentItem->setFocus(true, Qt::PopupFocusReason);
+ }
+ overlayPrivate->lastActiveFocusItem = nullptr;
}
}
@@ -752,7 +887,6 @@ static QQuickItem *createDimmer(QQmlComponent *component, QQuickPopup *popup, QQ
item = new QQuickItem;
if (item) {
- item->setOpacity(popup->isVisible() ? 1.0 : 0.0);
item->setParentItem(parent);
item->stackBefore(popup->popupItem());
item->setZ(popup->z());
@@ -794,12 +928,24 @@ void QQuickPopupPrivate::createOverlay()
if (!component)
component = modal ? overlay->modal() : overlay->modeless();
- if (!dimmer)
+ if (!dimmer) {
dimmer = createDimmer(component, q, overlay);
- resizeOverlay();
+ if (!dimmer)
+ return;
+ // We cannot update explicitDimmerOpacity when dimmer's opacity changes,
+ // as it is expected to do so when we fade the dimmer in and out in
+ // show/hideDimmer, and any binding of the dimmer's opacity will be
+ // implicitly broken anyway.
+ explicitDimmerOpacity = dimmer->opacity();
+ // initially fully transparent, showDimmer fades the dimmer in.
+ dimmer->setOpacity(0);
+ if (q->isVisible())
+ showDimmer();
+ }
+ resizeDimmer();
}
-void QQuickPopupPrivate::destroyOverlay()
+void QQuickPopupPrivate::destroyDimmer()
{
if (dimmer) {
qCDebug(lcDimmer) << "destroying dimmer" << dimmer;
@@ -815,7 +961,7 @@ void QQuickPopupPrivate::destroyOverlay()
void QQuickPopupPrivate::toggleOverlay()
{
- destroyOverlay();
+ destroyDimmer();
if (dim)
createOverlay();
}
@@ -829,27 +975,29 @@ void QQuickPopupPrivate::updateContentPalettes(const QPalette& parentPalette)
QQuickItemPrivate::get(popupItem)->updateChildrenPalettes(parentPalette);
}
-void QQuickPopupPrivate::showOverlay()
+void QQuickPopupPrivate::showDimmer()
{
// use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors
if (dim && dimmer)
- QQmlProperty::write(dimmer, QStringLiteral("opacity"), 1.0);
+ QQmlProperty::write(dimmer, QStringLiteral("opacity"), explicitDimmerOpacity);
}
-void QQuickPopupPrivate::hideOverlay()
+void QQuickPopupPrivate::hideDimmer()
{
// use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors
if (dim && dimmer)
QQmlProperty::write(dimmer, QStringLiteral("opacity"), 0.0);
}
-void QQuickPopupPrivate::resizeOverlay()
+void QQuickPopupPrivate::resizeDimmer()
{
if (!dimmer)
return;
- qreal w = window ? window->width() : 0;
- qreal h = window ? window->height() : 0;
+ const QQuickOverlay *overlay = QQuickOverlay::overlay(window);
+
+ qreal w = overlay ? overlay->width() : 0;
+ qreal h = overlay ? overlay->height() : 0;
dimmer->setSize(QSizeF(w, h));
}
@@ -896,6 +1044,8 @@ QQuickPopup::QQuickPopup(QObject *parent)
{
Q_D(QQuickPopup);
d->init();
+ // By default, allow popup to move beyond window edges
+ d->relaxEdgeConstraint = true;
}
QQuickPopup::QQuickPopup(QQuickPopupPrivate &dd, QObject *parent)
@@ -909,6 +1059,13 @@ QQuickPopup::~QQuickPopup()
{
Q_D(QQuickPopup);
d->inDestructor = true;
+
+ QQuickItem *currentContentItem = d->popupItem->d_func()->contentItem.data();
+ if (currentContentItem) {
+ disconnect(currentContentItem, &QQuickItem::childrenChanged,
+ this, &QQuickPopup::contentChildrenChanged);
+ }
+
setParentItem(nullptr);
// If the popup is destroyed before the exit transition finishes,
@@ -1682,8 +1839,18 @@ void QQuickPopup::setParentItem(QQuickItem *parent)
if (parent) {
QObjectPrivate::connect(parent, &QQuickItem::windowChanged, d, &QQuickPopupPrivate::setWindow);
QQuickItemPrivate::get(d->parentItem)->addItemChangeListener(d, QQuickItemPrivate::Destroyed);
- } else if (!d->inDestructor) {
- // NOTE: if setParentItem is called from the dtor, this bypasses virtual dispatch and calls QQuickPopup::close() directly
+ } else if (d->inDestructor) {
+ d->destroyDimmer();
+ } else {
+ // Reset transition manager state when its parent window destroyed
+ if (!d->window && d->transitionManager.isRunning()) {
+ if (d->transitionState == QQuickPopupPrivate::EnterTransition)
+ d->finalizeEnterTransition();
+ else if (d->transitionState == QQuickPopupPrivate::ExitTransition)
+ d->finalizeExitTransition();
+ }
+ // NOTE: if setParentItem is called from the dtor, this bypasses virtual dispatch and calls
+ // QQuickPopup::close() directly
close();
}
d->setWindow(parent ? parent->window() : nullptr);
@@ -1760,8 +1927,18 @@ void QQuickPopup::setContentItem(QQuickItem *item)
{
Q_D(QQuickPopup);
// See comment in setBackground for why we do this.
- QQuickControlPrivate::warnIfCustomizationNotSupported(this, item, QStringLiteral("background"));
+ QQuickControlPrivate::warnIfCustomizationNotSupported(this, item, QStringLiteral("contentItem"));
+ QQuickItem *oldContentItem = d->complete ? d->popupItem->d_func()->contentItem.data()
+ : nullptr;
+ if (oldContentItem)
+ disconnect(oldContentItem, &QQuickItem::childrenChanged, this, &QQuickPopup::contentChildrenChanged);
d->popupItem->setContentItem(item);
+ if (d->complete) {
+ QQuickItem *newContentItem = d->popupItem->d_func()->contentItem.data();
+ connect(newContentItem, &QQuickItem::childrenChanged, this, &QQuickPopup::contentChildrenChanged);
+ if (oldContentItem != newContentItem)
+ emit contentChildrenChanged();
+ }
}
/*!
@@ -1977,14 +2154,15 @@ void QQuickPopup::setVisible(bool visible)
if (d->visible == visible && d->transitionState != QQuickPopupPrivate::ExitTransition)
return;
- if (d->complete) {
- if (visible)
- d->transitionManager.transitionEnter();
- else
- d->transitionManager.transitionExit();
- } else {
+ if (!d->complete || (visible && !d->window)) {
d->visible = visible;
+ return;
}
+
+ if (visible)
+ d->transitionManager.transitionEnter();
+ else
+ d->transitionManager.transitionExit();
}
/*!
@@ -2514,6 +2692,11 @@ void QQuickPopup::componentComplete()
d->complete = true;
d->popupItem->componentComplete();
+
+ if (auto currentContentItem = d->popupItem->d_func()->contentItem.data()) {
+ connect(currentContentItem, &QQuickItem::childrenChanged,
+ this, &QQuickPopup::contentChildrenChanged);
+ }
}
bool QQuickPopup::isComponentComplete() const
@@ -2769,6 +2952,19 @@ QFont QQuickPopup::defaultFont() const
}
#if QT_CONFIG(accessibility)
+QAccessible::Role QQuickPopup::effectiveAccessibleRole() const
+{
+ auto *attached = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(this, false);
+
+ auto role = QAccessible::NoRole;
+ if (auto *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(attached))
+ role = accessibleAttached->role();
+ if (role == QAccessible::NoRole)
+ role = accessibleRole();
+
+ return role;
+}
+
QAccessible::Role QQuickPopup::accessibleRole() const
{
return QAccessible::Dialog;
diff --git a/src/quicktemplates/qquickpopup_p.h b/src/quicktemplates/qquickpopup_p.h
index ee9003d627..9ffd94e70a 100644
--- a/src/quicktemplates/qquickpopup_p.h
+++ b/src/quicktemplates/qquickpopup_p.h
@@ -38,7 +38,7 @@ class QQuickPopupAnchors;
class QQuickPopupPrivate;
class QQuickTransition;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopup : public QObject, public QQmlParserStatus
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopup : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
@@ -419,7 +419,10 @@ protected:
virtual QFont defaultFont() const;
#if QT_CONFIG(accessibility)
+ QAccessible::Role effectiveAccessibleRole() const;
+private:
virtual QAccessible::Role accessibleRole() const;
+protected:
virtual void accessibilityActiveChanged(bool active);
#endif
@@ -441,6 +444,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPopup::ClosePolicy)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPopup)
-
#endif // QQUICKPOPUP_P_H
diff --git a/src/quicktemplates/qquickpopup_p_p.h b/src/quicktemplates/qquickpopup_p_p.h
index eabe2df001..5572dd87d1 100644
--- a/src/quicktemplates/qquickpopup_p_p.h
+++ b/src/quicktemplates/qquickpopup_p_p.h
@@ -25,6 +25,8 @@
#include <QtQuick/private/qquicktransitionmanager_p_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickTransition;
@@ -35,7 +37,7 @@ class QQuickPopupItem;
class QQuickPopupPrivate;
class QQuickPopupPositioner;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupTransitionManager : public QQuickTransitionManager
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupTransitionManager : public QQuickTransitionManager
{
public:
QQuickPopupTransitionManager(QQuickPopupPrivate *popup);
@@ -50,7 +52,7 @@ private:
QQuickPopupPrivate *popup = nullptr;
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupPrivate
: public QObjectPrivate
, public QQuickItemChangeListener
, public QQuickPaletteProviderPrivateBase<QQuickPopup, QQuickPopupPrivate>
@@ -93,12 +95,12 @@ public:
void reposition();
void createOverlay();
- void destroyOverlay();
+ void destroyDimmer();
void toggleOverlay();
void updateContentPalettes(const QPalette& parentPalette);
- virtual void showOverlay();
- virtual void hideOverlay();
- virtual void resizeOverlay();
+ virtual void showDimmer();
+ virtual void hideDimmer();
+ virtual void resizeDimmer();
virtual bool prepareEnterTransition();
virtual bool prepareExitTransition();
@@ -154,6 +156,7 @@ public:
bool outsidePressed = false;
bool outsideParentPressed = false;
bool inDestructor = false;
+ bool relaxEdgeConstraint = false;
int touchId = -1;
qreal x = 0;
qreal y = 0;
@@ -178,6 +181,7 @@ public:
QList<QQuickStateAction> exitActions;
QQuickPopupTransitionManager transitionManager;
QQuickPopupAnchors *anchors = nullptr;
+ qreal explicitDimmerOpacity = 0;
qreal prevOpacity = 0;
qreal prevScale = 0;
diff --git a/src/quicktemplates/qquickpopupanchors_p.h b/src/quicktemplates/qquickpopupanchors_p.h
index 589e2d6c8b..db94939673 100644
--- a/src/quicktemplates/qquickpopupanchors_p.h
+++ b/src/quicktemplates/qquickpopupanchors_p.h
@@ -26,10 +26,10 @@ class QQuickItem;
class QQuickPopupAnchorsPrivate;
class QQuickPopup;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupAnchors : public QObject, public QQuickItemChangeListener
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupAnchors : public QObject, public QQuickItemChangeListener
{
Q_OBJECT
- Q_PROPERTY(QQuickItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged)
+ Q_PROPERTY(QQuickItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 5)
@@ -53,6 +53,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPopupAnchors)
-
#endif // QQUICKPOPUPANCHORS_P_H
diff --git a/src/quicktemplates/qquickpopupitem.cpp b/src/quicktemplates/qquickpopupitem.cpp
index 157046ca51..574ea4589a 100644
--- a/src/quicktemplates/qquickpopupitem.cpp
+++ b/src/quicktemplates/qquickpopupitem.cpp
@@ -22,6 +22,11 @@ QQuickPopupItemPrivate::QQuickPopupItemPrivate(QQuickPopup *popup)
isTabFence = true;
}
+QQuickPopupItemPrivate *QQuickPopupItemPrivate::get(QQuickPopupItem *popupItem)
+{
+ return popupItem->d_func();
+}
+
void QQuickPopupItemPrivate::implicitWidthChanged()
{
qCDebug(lcPopupItem).nospace() << "implicitWidthChanged called on " << q_func() << "; new implicitWidth is " << implicitWidth;
@@ -71,8 +76,6 @@ void QQuickPopupItemPrivate::executeContentItem(bool complete)
quickCompleteDeferred(popup, contentItemName(), contentItem);
}
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickPopupItemPrivate::cancelBackground()
{
quickCancelDeferred(popup, backgroundName());
@@ -310,7 +313,7 @@ QFont QQuickPopupItem::defaultFont() const
QAccessible::Role QQuickPopupItem::accessibleRole() const
{
Q_D(const QQuickPopupItem);
- return d->popup->accessibleRole();
+ return d->popup->effectiveAccessibleRole();
}
void QQuickPopupItem::accessibilityActiveChanged(bool active)
diff --git a/src/quicktemplates/qquickpopupitem_p_p.h b/src/quicktemplates/qquickpopupitem_p_p.h
index 77b7695421..c0ef0f4d85 100644
--- a/src/quicktemplates/qquickpopupitem_p_p.h
+++ b/src/quicktemplates/qquickpopupitem_p_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickPopup;
class QQuickPopupItemPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupItem : public QQuickPage
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupItem : public QQuickPage
{
Q_OBJECT
@@ -74,13 +74,15 @@ private:
friend class QQuickPopup;
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupItemPrivate : public QQuickPagePrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupItemPrivate : public QQuickPagePrivate
{
Q_DECLARE_PUBLIC(QQuickPopupItem)
public:
QQuickPopupItemPrivate(QQuickPopup *popup);
+ static QQuickPopupItemPrivate *get(QQuickPopupItem *popupItem);
+
void implicitWidthChanged() override;
void implicitHeightChanged() override;
diff --git a/src/quicktemplates/qquickpopuppositioner.cpp b/src/quicktemplates/qquickpopuppositioner.cpp
index aecbc7373c..f8113a5526 100644
--- a/src/quicktemplates/qquickpopuppositioner.cpp
+++ b/src/quicktemplates/qquickpopuppositioner.cpp
@@ -98,6 +98,7 @@ void QQuickPopupPositioner::reposition()
!centerInParent ? p->allowVerticalMove ? p->y : popupItem->y() : 0,
!p->hasWidth && iw > 0 ? iw : w,
!p->hasHeight && ih > 0 ? ih : h);
+ bool relaxEdgeConstraint = p->relaxEdgeConstraint;
if (m_parentItem) {
// m_parentItem is the parent that the popup should open in,
// and popupItem()->parentItem() is the overlay, so the mapToItem() calls below
@@ -110,6 +111,8 @@ void QQuickPopupPositioner::reposition()
if (centerInOverlay) {
rect.moveCenter(QPointF(qRound(centerInOverlay->width() / 2.0), qRound(centerInOverlay->height() / 2.0)));
+ // Popup cannot be moved outside window bounds when its centered with overlay
+ relaxEdgeConstraint = false;
} else {
const QPointF parentItemCenter = QPointF(qRound(m_parentItem->width() / 2), qRound(m_parentItem->height() / 2));
rect.moveCenter(m_parentItem->mapToItem(popupItem->parentItem(), parentItemCenter));
@@ -124,8 +127,6 @@ void QQuickPopupPositioner::reposition()
qMax<qreal>(0.0, margins.top()),
p->window->width() - qMax<qreal>(0.0, margins.left()) - qMax<qreal>(0.0, margins.right()),
p->window->height() - qMax<qreal>(0.0, margins.top()) - qMax<qreal>(0.0, margins.bottom()));
- if (p->window->contentOrientation() == Qt::LandscapeOrientation || p->window->contentOrientation() == Qt::InvertedLandscapeOrientation)
- bounds = bounds.transposed();
// if the popup doesn't fit horizontally inside the window, try flipping it around (left <-> right)
if (p->allowHorizontalFlip && (rect.left() < bounds.left() || rect.right() > bounds.right())) {
@@ -170,12 +171,17 @@ void QQuickPopupPositioner::reposition()
}
// as a last resort, adjust the width to fit the window
+ // Negative margins don't require resize as popup not pushed within
+ // the boundary. But otherwise, retain existing behavior of resizing
+ // for items, such as menus, which enables flip.
if (p->allowHorizontalResize) {
- if (rect.left() < bounds.left()) {
+ if ((margins.left() >= 0 || !relaxEdgeConstraint)
+ && (rect.left() < bounds.left())) {
rect.setLeft(bounds.left());
widthAdjusted = true;
}
- if (rect.right() > bounds.right()) {
+ if ((margins.right() >= 0 || !relaxEdgeConstraint)
+ && (rect.right() > bounds.right())) {
rect.setRight(bounds.right());
widthAdjusted = true;
}
@@ -198,12 +204,17 @@ void QQuickPopupPositioner::reposition()
}
// as a last resort, adjust the height to fit the window
+ // Negative margins don't require resize as popup not pushed within
+ // the boundary. But otherwise, retain existing behavior of resizing
+ // for items, such as menus, which enables flip.
if (p->allowVerticalResize) {
- if (rect.top() < bounds.top()) {
+ if ((margins.top() >= 0 || !relaxEdgeConstraint)
+ && (rect.top() < bounds.top())) {
rect.setTop(bounds.top());
heightAdjusted = true;
}
- if (rect.bottom() > bounds.bottom()) {
+ if ((margins.bottom() >= 0 || !relaxEdgeConstraint)
+ && (rect.bottom() > bounds.bottom())) {
rect.setBottom(bounds.bottom());
heightAdjusted = true;
}
diff --git a/src/quicktemplates/qquickpopuppositioner_p_p.h b/src/quicktemplates/qquickpopuppositioner_p_p.h
index c8e20b4b28..1ec8234469 100644
--- a/src/quicktemplates/qquickpopuppositioner_p_p.h
+++ b/src/quicktemplates/qquickpopuppositioner_p_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickPopup;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupPositioner : public QQuickItemChangeListener
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupPositioner : public QQuickItemChangeListener
{
public:
explicit QQuickPopupPositioner(QQuickPopup *popup);
diff --git a/src/quicktemplates/qquickpresshandler.cpp b/src/quicktemplates/qquickpresshandler.cpp
index d752bb36f1..fc569a81ca 100644
--- a/src/quicktemplates/qquickpresshandler.cpp
+++ b/src/quicktemplates/qquickpresshandler.cpp
@@ -19,7 +19,7 @@ void QQuickPressHandler::mousePressEvent(QMouseEvent *event)
pressPos = event->position();
if (Qt::LeftButton == (event->buttons() & Qt::LeftButton)) {
timer.start(QGuiApplication::styleHints()->mousePressAndHoldInterval(), control);
- delayedMousePressEvent = new QMouseEvent(event->type(), event->position().toPoint(), event->globalPosition().toPoint(),
+ delayedMousePressEvent = std::make_unique<QMouseEvent>(event->type(), event->position().toPoint(), event->globalPosition().toPoint(),
event->button(), event->buttons(), event->modifiers(), event->pointingDevice());
} else {
timer.stop();
@@ -86,10 +86,7 @@ QT_WARNING_POP
void QQuickPressHandler::clearDelayedMouseEvent()
{
- if (delayedMousePressEvent) {
- delete delayedMousePressEvent;
- delayedMousePressEvent = 0;
- }
+ delayedMousePressEvent.reset();
}
bool QQuickPressHandler::isActive()
diff --git a/src/quicktemplates/qquickpresshandler_p_p.h b/src/quicktemplates/qquickpresshandler_p_p.h
index adbed66a30..72d78aae66 100644
--- a/src/quicktemplates/qquickpresshandler_p_p.h
+++ b/src/quicktemplates/qquickpresshandler_p_p.h
@@ -19,6 +19,8 @@
#include <QtCore/qbasictimer.h>
#include <QtCore/private/qglobal_p.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QQuickItem;
@@ -44,7 +46,7 @@ struct QQuickPressHandler
int pressAndHoldSignalIndex = -1;
int pressedSignalIndex = -1;
int releasedSignalIndex = -1;
- QMouseEvent *delayedMousePressEvent = nullptr;
+ std::unique_ptr<QMouseEvent> delayedMousePressEvent = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickprogressbar.cpp b/src/quicktemplates/qquickprogressbar.cpp
index c42a62b4fe..77be68873c 100644
--- a/src/quicktemplates/qquickprogressbar.cpp
+++ b/src/quicktemplates/qquickprogressbar.cpp
@@ -65,6 +65,8 @@ public:
QQuickProgressBar::QQuickProgressBar(QQuickItem *parent)
: QQuickControl(*(new QQuickProgressBarPrivate), parent)
{
+ Q_D(QQuickProgressBar);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
/*!
diff --git a/src/quicktemplates/qquickprogressbar_p.h b/src/quicktemplates/qquickprogressbar_p.h
index 99e356b525..576515e4ad 100644
--- a/src/quicktemplates/qquickprogressbar_p.h
+++ b/src/quicktemplates/qquickprogressbar_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickProgressBarPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickProgressBar : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickProgressBar : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -74,6 +74,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickProgressBar)
-
#endif // QQUICKPROGRESSBAR_P_H
diff --git a/src/quicktemplates/qquickradiobutton.cpp b/src/quicktemplates/qquickradiobutton.cpp
index a943da9de6..c49bf3b1cd 100644
--- a/src/quicktemplates/qquickradiobutton.cpp
+++ b/src/quicktemplates/qquickradiobutton.cpp
@@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE
\sa ButtonGroup, {Customizing RadioButton}, {Button Controls}, RadioDelegate
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioButtonPrivate : public QQuickAbstractButtonPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickRadioButtonPrivate : public QQuickAbstractButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickRadioButton)
diff --git a/src/quicktemplates/qquickradiobutton_p.h b/src/quicktemplates/qquickradiobutton_p.h
index 8d70290270..d7f4c74c7b 100644
--- a/src/quicktemplates/qquickradiobutton_p.h
+++ b/src/quicktemplates/qquickradiobutton_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickRadioButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioButton : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickRadioButton : public QQuickAbstractButton
{
Q_OBJECT
QML_NAMED_ELEMENT(RadioButton)
@@ -41,6 +41,4 @@ protected:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRadioButton)
-
#endif // QQUICKRADIOBUTTON_P_H
diff --git a/src/quicktemplates/qquickradiodelegate.cpp b/src/quicktemplates/qquickradiodelegate.cpp
index 2c390ad487..7ec22a6301 100644
--- a/src/quicktemplates/qquickradiodelegate.cpp
+++ b/src/quicktemplates/qquickradiodelegate.cpp
@@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE
\sa {Customizing RadioDelegate}, {Delegate Controls}, RadioButton
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioDelegatePrivate : public QQuickItemDelegatePrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickRadioDelegatePrivate : public QQuickItemDelegatePrivate
{
Q_DECLARE_PUBLIC(QQuickRadioDelegate)
diff --git a/src/quicktemplates/qquickradiodelegate_p.h b/src/quicktemplates/qquickradiodelegate_p.h
index 574c87032c..337711c73f 100644
--- a/src/quicktemplates/qquickradiodelegate_p.h
+++ b/src/quicktemplates/qquickradiodelegate_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickRadioDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickRadioDelegate : public QQuickItemDelegate
{
Q_OBJECT
QML_NAMED_ELEMENT(RadioDelegate)
@@ -43,6 +43,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRadioDelegate)
-
#endif // QQUICKRADIODELEGATE_P_H
diff --git a/src/quicktemplates/qquickrangeslider.cpp b/src/quicktemplates/qquickrangeslider.cpp
index ff15a9194e..d97dfdff90 100644
--- a/src/quicktemplates/qquickrangeslider.cpp
+++ b/src/quicktemplates/qquickrangeslider.cpp
@@ -117,8 +117,6 @@ void QQuickRangeSliderNodePrivate::updatePosition(bool ignoreOtherPosition)
setPosition(pos, ignoreOtherPosition);
}
-static inline QString handleName() { return QStringLiteral("handle"); }
-
void QQuickRangeSliderNodePrivate::cancelHandle()
{
Q_Q(QQuickRangeSliderNode);
@@ -227,6 +225,8 @@ void QQuickRangeSliderNode::setHandle(QQuickItem *handle)
if (d->handle == handle)
return;
+ QQuickControlPrivate::warnIfCustomizationNotSupported(d->slider, handle, QStringLiteral("handle"));
+
if (!d->handle.isExecuting())
d->cancelHandle();
@@ -356,6 +356,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
bool live = true;
qreal from = defaultFrom;
@@ -485,8 +486,14 @@ bool QQuickRangeSliderPrivate::handlePress(const QPointF &point, ulong timestamp
if (hitNode) {
hitNode->setPressed(true);
- if (QQuickItem *handle = hitNode->handle())
+ if (QQuickItem *handle = hitNode->handle()) {
handle->setZ(1);
+
+ // A specific handle was hit, so it should get focus, rather than the default
+ // (first handle) that gets focus whenever the RangeSlider itself does - see focusInEvent().
+ if (focusPolicy & Qt::ClickFocus)
+ handle->forceActiveFocus(Qt::MouseFocusReason);
+ }
QQuickRangeSliderNodePrivate::get(hitNode)->touchId = touchId;
}
if (otherNode) {
@@ -528,22 +535,21 @@ bool QQuickRangeSliderPrivate::handleRelease(const QPointF &point, ulong timesta
return true;
QQuickRangeSliderNodePrivate *pressedNodePrivate = QQuickRangeSliderNodePrivate::get(pressedNode);
- if (q->keepMouseGrab() || q->keepTouchGrab()) {
- const qreal oldPos = pressedNode->position();
- qreal pos = positionAt(q, pressedNode->handle(), point);
- if (snapMode != QQuickRangeSlider::NoSnap)
- pos = snapPosition(q, pos);
- qreal val = valueAt(q, pos);
- if (!qFuzzyCompare(val, pressedNode->value()))
- pressedNode->setValue(val);
- else if (snapMode != QQuickRangeSlider::NoSnap)
- pressedNodePrivate->setPosition(pos);
- q->setKeepMouseGrab(false);
- q->setKeepTouchGrab(false);
+ const qreal oldPos = pressedNode->position();
+ qreal pos = positionAt(q, pressedNode->handle(), point);
+ if (snapMode != QQuickRangeSlider::NoSnap)
+ pos = snapPosition(q, pos);
+ qreal val = valueAt(q, pos);
+ if (!qFuzzyCompare(val, pressedNode->value()))
+ pressedNode->setValue(val);
+ else if (snapMode != QQuickRangeSlider::NoSnap)
+ pressedNodePrivate->setPosition(pos);
+ q->setKeepMouseGrab(false);
+ q->setKeepTouchGrab(false);
+
+ if (!qFuzzyCompare(pressedNode->position(), oldPos))
+ emit pressedNode->moved();
- if (!qFuzzyCompare(pressedNode->position(), oldPos))
- emit pressedNode->moved();
- }
pressedNode->setPressed(false);
pressedNodePrivate->touchId = -1;
return true;
@@ -597,14 +603,29 @@ void QQuickRangeSliderPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit second->implicitHandleHeightChanged();
}
+void QQuickRangeSliderPrivate::itemDestroyed(QQuickItem *item)
+{
+ QQuickControlPrivate::itemDestroyed(item);
+ if (item == first->handle())
+ first->setHandle(nullptr);
+ else if (item == second->handle())
+ second->setHandle(nullptr);
+}
+
QQuickRangeSlider::QQuickRangeSlider(QQuickItem *parent)
: QQuickControl(*(new QQuickRangeSliderPrivate), parent)
{
Q_D(QQuickRangeSlider);
d->first = new QQuickRangeSliderNode(0.0, this);
d->second = new QQuickRangeSliderNode(1.0, this);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
setFlag(QQuickItem::ItemIsFocusScope);
+#ifdef Q_OS_MACOS
+ setFocusPolicy(Qt::TabFocus);
+#else
+ setFocusPolicy(Qt::StrongFocus);
+#endif
setAcceptedMouseButtons(Qt::LeftButton);
#if QT_CONFIG(quicktemplates2_multitouch)
setAcceptTouchEvents(true);
@@ -959,6 +980,11 @@ void QQuickRangeSlider::setOrientation(Qt::Orientation orientation)
if (d->orientation == orientation)
return;
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
d->orientation = orientation;
emit orientationChanged();
}
diff --git a/src/quicktemplates/qquickrangeslider_p.h b/src/quicktemplates/qquickrangeslider_p.h
index 577bbe165f..772e4abcda 100644
--- a/src/quicktemplates/qquickrangeslider_p.h
+++ b/src/quicktemplates/qquickrangeslider_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickRangeSliderPrivate;
class QQuickRangeSliderNode;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRangeSlider : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickRangeSlider : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -126,7 +126,7 @@ private:
class QQuickRangeSliderNodePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRangeSliderNode : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickRangeSliderNode : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
@@ -191,6 +191,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRangeSlider)
-
#endif // QQUICKRANGESLIDER_P_H
diff --git a/src/quicktemplates/qquickroundbutton_p.h b/src/quicktemplates/qquickroundbutton_p.h
index bf589b673e..9a30ebe9a8 100644
--- a/src/quicktemplates/qquickroundbutton_p.h
+++ b/src/quicktemplates/qquickroundbutton_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickRoundButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRoundButton : public QQuickButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickRoundButton : public QQuickButton
{
Q_OBJECT
Q_PROPERTY(qreal radius READ radius WRITE setRadius RESET resetRadius NOTIFY radiusChanged FINAL)
@@ -48,6 +48,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRoundButton)
-
#endif // QQUICKROUNDBUTTON_P_H
diff --git a/src/quicktemplates/qquickscrollbar.cpp b/src/quicktemplates/qquickscrollbar.cpp
index 67fc8116a6..75de0363e9 100644
--- a/src/quicktemplates/qquickscrollbar.cpp
+++ b/src/quicktemplates/qquickscrollbar.cpp
@@ -122,9 +122,9 @@ QT_BEGIN_NAMESPACE
\sa ScrollIndicator, ScrollView, {Customizing ScrollBar}, {Indicator Controls}
*/
-static const QQuickItemPrivate::ChangeTypes changeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
-static const QQuickItemPrivate::ChangeTypes horizontalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitHeight;
-static const QQuickItemPrivate::ChangeTypes verticalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitWidth;
+static const QQuickItemPrivate::ChangeTypes QsbChangeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
+static const QQuickItemPrivate::ChangeTypes QsbHorizontalChangeTypes = QsbChangeTypes | QQuickItemPrivate::ImplicitHeight;
+static const QQuickItemPrivate::ChangeTypes QsbVerticalChangeTypes = QsbChangeTypes | QQuickItemPrivate::ImplicitWidth;
QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const
{
@@ -368,6 +368,7 @@ QQuickScrollBar::QQuickScrollBar(QQuickItem *parent)
Q_D(QQuickScrollBar);
d->decreaseVisual = new QQuickIndicatorButton(this);
d->increaseVisual = new QQuickIndicatorButton(this);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
setKeepMouseGrab(true);
setAcceptedMouseButtons(Qt::LeftButton);
#if QT_CONFIG(quicktemplates2_multitouch)
@@ -409,13 +410,11 @@ void QQuickScrollBar::setSize(qreal size)
size = qBound(0.0, size, 1.0);
if (qFuzzyCompare(d->size, size))
return;
- d->size = size;
- auto oldVisualArea = d->visualArea();
- d->size = qBound(0.0, size, 1.0);
+ const auto oldVisualArea = d->visualArea();
+ d->size = size;
if (d->size + d->position > 1.0) {
- setPosition(1.0 - d->size);
- oldVisualArea = d->visualArea();
+ d->setPosition(1.0 - d->size, false);
}
if (isComponentComplete())
@@ -450,15 +449,22 @@ qreal QQuickScrollBar::position() const
void QQuickScrollBar::setPosition(qreal position)
{
Q_D(QQuickScrollBar);
- if (!qt_is_finite(position) || qFuzzyCompare(d->position, position))
+ d->setPosition(position);
+}
+
+void QQuickScrollBarPrivate::setPosition(qreal newPosition, bool notifyVisualChange)
+{
+ Q_Q(QQuickScrollBar);
+ if (!qt_is_finite(newPosition) || qFuzzyCompare(position, newPosition))
return;
- auto oldVisualArea = d->visualArea();
- d->position = position;
- if (isComponentComplete())
- d->resizeContent();
- emit positionChanged();
- d->visualAreaChange(d->visualArea(), oldVisualArea);
+ auto oldVisualArea = visualArea();
+ position = newPosition;
+ if (q->isComponentComplete())
+ resizeContent();
+ emit q->positionChanged();
+ if (notifyVisualChange)
+ visualAreaChange(visualArea(), oldVisualArea);
}
/*!
@@ -567,6 +573,11 @@ void QQuickScrollBar::setOrientation(Qt::Orientation orientation)
if (d->orientation == orientation)
return;
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
d->orientation = orientation;
if (isComponentComplete())
d->resizeContent();
@@ -656,7 +667,17 @@ void QQuickScrollBar::resetInteractive()
The following example keeps the vertical scroll bar always visible:
- \snippet qtquickcontrols-scrollbar-policy.qml 1
+ \snippet qtquickcontrols-scrollbar-policy-alwayson.qml 1
+
+ Styles may use this property in combination with the \l active property
+ in order to implement transient scroll bars. Transient scroll bars are
+ hidden shortly after the last interaction event (hover or press). This
+ is typically done by animating the opacity of the scroll bar. To override
+ this behavior, set the policy to \c ScrollBar.AlwaysOn or
+ \c ScrollBar.AlwaysOff, depending on the size of the content compared to
+ its view. For example, for a vertical \l ListView:
+
+ \snippet qtquickcontrols-scrollbar-policy-alwayson-when-needed.qml 1
*/
QQuickScrollBar::Policy QQuickScrollBar::policy() const
{
@@ -735,6 +756,7 @@ void QQuickScrollBar::setMinimumSize(qreal minimumSize)
/*!
\since QtQuick.Controls 2.4 (Qt 5.11)
\qmlproperty real QtQuick.Controls::ScrollBar::visualSize
+ \readonly
This property holds the effective visual size of the scroll bar,
which may be limited by the \l {minimumSize}{minimum size}.
@@ -750,6 +772,7 @@ qreal QQuickScrollBar::visualSize() const
/*!
\since QtQuick.Controls 2.4 (Qt 5.11)
\qmlproperty real QtQuick.Controls::ScrollBar::visualPosition
+ \readonly
This property holds the effective visual position of the scroll bar,
which may be limited by the \l {minimumSize}{minimum size}.
@@ -942,11 +965,11 @@ void QQuickScrollBarAttachedPrivate::initHorizontal()
// If a scroll bar was previously hidden (due to e.g. setting a new contentItem
// on a ScrollView), we need to make sure that we un-hide it.
- // We don't bother checking if the item is actually the old one, because
- // if it's not, all of the things the function does (setting parent, visibility, etc.)
- // should be no-ops anyway.
- if (auto control = qobject_cast<QQuickControl*>(q_func()->parent()))
- QQuickControlPrivate::unhideOldItem(control, horizontal);
+ if (auto control = qobject_cast<QQuickControl*>(q_func()->parent())) {
+ const auto visibility = horizontal->policy() != QQuickScrollBar::AlwaysOff
+ ? QQuickControlPrivate::UnhideVisibility::Show : QQuickControlPrivate::UnhideVisibility::Hide;
+ QQuickControlPrivate::unhideOldItem(control, horizontal, visibility);
+ }
layoutHorizontal();
horizontal->setSize(area->property("widthRatio").toReal());
@@ -969,8 +992,11 @@ void QQuickScrollBarAttachedPrivate::initVertical()
if (parent && parent == flickable->parentItem())
vertical->stackAfter(flickable);
- if (auto control = qobject_cast<QQuickControl*>(q_func()->parent()))
- QQuickControlPrivate::unhideOldItem(control, vertical);
+ if (auto control = qobject_cast<QQuickControl*>(q_func()->parent())) {
+ const auto visibility = vertical->policy() != QQuickScrollBar::AlwaysOff
+ ? QQuickControlPrivate::UnhideVisibility::Show : QQuickControlPrivate::UnhideVisibility::Hide;
+ QQuickControlPrivate::unhideOldItem(control, vertical, visibility);
+ }
layoutVertical();
vertical->setSize(area->property("heightRatio").toReal());
@@ -1152,11 +1178,11 @@ QQuickScrollBarAttached::~QQuickScrollBarAttached()
{
Q_D(QQuickScrollBarAttached);
if (d->horizontal) {
- QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsbHorizontalChangeTypes);
d->horizontal = nullptr;
}
if (d->vertical) {
- QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsbVerticalChangeTypes);
d->vertical = nullptr;
}
d->setFlickable(nullptr);
@@ -1189,7 +1215,7 @@ void QQuickScrollBarAttached::setHorizontal(QQuickScrollBar *horizontal)
return;
if (d->horizontal) {
- QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsbHorizontalChangeTypes);
QObjectPrivate::disconnect(d->horizontal, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollHorizontal);
if (d->flickable)
@@ -1203,7 +1229,7 @@ void QQuickScrollBarAttached::setHorizontal(QQuickScrollBar *horizontal)
horizontal->setParentItem(qobject_cast<QQuickItem *>(parent()));
horizontal->setOrientation(Qt::Horizontal);
- QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, QsbHorizontalChangeTypes);
QObjectPrivate::connect(horizontal, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollHorizontal);
if (d->flickable)
@@ -1239,7 +1265,7 @@ void QQuickScrollBarAttached::setVertical(QQuickScrollBar *vertical)
return;
if (d->vertical) {
- QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsbVerticalChangeTypes);
QObjectPrivate::disconnect(d->vertical, &QQuickScrollBar::mirroredChanged, d, &QQuickScrollBarAttachedPrivate::mirrorVertical);
QObjectPrivate::disconnect(d->vertical, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollVertical);
@@ -1254,7 +1280,7 @@ void QQuickScrollBarAttached::setVertical(QQuickScrollBar *vertical)
vertical->setParentItem(qobject_cast<QQuickItem *>(parent()));
vertical->setOrientation(Qt::Vertical);
- QQuickItemPrivate::get(vertical)->addItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(vertical)->addItemChangeListener(d, QsbVerticalChangeTypes);
QObjectPrivate::connect(vertical, &QQuickScrollBar::mirroredChanged, d, &QQuickScrollBarAttachedPrivate::mirrorVertical);
QObjectPrivate::connect(vertical, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollVertical);
diff --git a/src/quicktemplates/qquickscrollbar_p.h b/src/quicktemplates/qquickscrollbar_p.h
index 6e4eade1f8..b43cef1734 100644
--- a/src/quicktemplates/qquickscrollbar_p.h
+++ b/src/quicktemplates/qquickscrollbar_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickScrollBarAttached;
class QQuickScrollBarPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollBar : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollBar : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged FINAL)
@@ -156,7 +156,7 @@ private:
class QQuickScrollBarAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollBarAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollBarAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickScrollBar *horizontal READ horizontal WRITE setHorizontal NOTIFY horizontalChanged FINAL)
@@ -183,6 +183,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScrollBar)
-
#endif // QQUICKSCROLLBAR_P_H
diff --git a/src/quicktemplates/qquickscrollbar_p_p.h b/src/quicktemplates/qquickscrollbar_p_p.h
index cad54882d2..2a834ce71e 100644
--- a/src/quicktemplates/qquickscrollbar_p_p.h
+++ b/src/quicktemplates/qquickscrollbar_p_p.h
@@ -45,6 +45,7 @@ public:
qreal logicalPosition(qreal position) const;
+ void setPosition(qreal position, bool notifyVisualChange = true);
qreal snapPosition(qreal position) const;
qreal positionAt(const QPointF &point) const;
void setInteractive(bool interactive);
diff --git a/src/quicktemplates/qquickscrollindicator.cpp b/src/quicktemplates/qquickscrollindicator.cpp
index 3a691852bc..9928503e86 100644
--- a/src/quicktemplates/qquickscrollindicator.cpp
+++ b/src/quicktemplates/qquickscrollindicator.cpp
@@ -93,9 +93,9 @@ QT_BEGIN_NAMESPACE
\sa ScrollBar, {Customizing ScrollIndicator}, {Indicator Controls}
*/
-static const QQuickItemPrivate::ChangeTypes changeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
-static const QQuickItemPrivate::ChangeTypes horizontalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitHeight;
-static const QQuickItemPrivate::ChangeTypes verticalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitWidth;
+static const QQuickItemPrivate::ChangeTypes QsiChangeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
+static const QQuickItemPrivate::ChangeTypes QsiHorizontalChangeTypes = QsiChangeTypes | QQuickItemPrivate::ImplicitHeight;
+static const QQuickItemPrivate::ChangeTypes QsiVerticalChangeTypes = QsiChangeTypes | QQuickItemPrivate::ImplicitWidth;
class QQuickScrollIndicatorPrivate : public QQuickControlPrivate
{
@@ -168,6 +168,8 @@ void QQuickScrollIndicatorPrivate::resizeContent()
QQuickScrollIndicator::QQuickScrollIndicator(QQuickItem *parent)
: QQuickControl(*(new QQuickScrollIndicatorPrivate), parent)
{
+ Q_D(QQuickScrollIndicator);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
QQuickScrollIndicatorAttached *QQuickScrollIndicator::qmlAttachedProperties(QObject *object)
@@ -295,6 +297,11 @@ void QQuickScrollIndicator::setOrientation(Qt::Orientation orientation)
if (d->orientation == orientation)
return;
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
d->orientation = orientation;
if (isComponentComplete())
d->resizeContent();
@@ -496,9 +503,9 @@ QQuickScrollIndicatorAttached::~QQuickScrollIndicatorAttached()
Q_D(QQuickScrollIndicatorAttached);
if (d->flickable) {
if (d->horizontal)
- QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsiHorizontalChangeTypes);
if (d->vertical)
- QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d,verticalChangeTypes);
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsiVerticalChangeTypes);
// NOTE: Use removeItemChangeListener(Geometry) instead of updateOrRemoveGeometryChangeListener(Size).
// The latter doesn't remove the listener but only resets its types. Thus, it leaves behind a dangling
// pointer on destruction.
@@ -533,7 +540,7 @@ void QQuickScrollIndicatorAttached::setHorizontal(QQuickScrollIndicator *horizon
return;
if (d->horizontal && d->flickable) {
- QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsiHorizontalChangeTypes);
QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateHorizontal);
// TODO: export QQuickFlickableVisibleArea
@@ -549,7 +556,7 @@ void QQuickScrollIndicatorAttached::setHorizontal(QQuickScrollIndicator *horizon
horizontal->setParentItem(d->flickable);
horizontal->setOrientation(Qt::Horizontal);
- QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, QsiHorizontalChangeTypes);
QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateHorizontal);
// TODO: export QQuickFlickableVisibleArea
@@ -591,7 +598,7 @@ void QQuickScrollIndicatorAttached::setVertical(QQuickScrollIndicator *vertical)
return;
if (d->vertical && d->flickable) {
- QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsiVerticalChangeTypes);
QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateVertical);
// TODO: export QQuickFlickableVisibleArea
@@ -607,7 +614,7 @@ void QQuickScrollIndicatorAttached::setVertical(QQuickScrollIndicator *vertical)
vertical->setParentItem(d->flickable);
vertical->setOrientation(Qt::Vertical);
- QQuickItemPrivate::get(vertical)->addItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(vertical)->addItemChangeListener(d, QsiVerticalChangeTypes);
QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateVertical);
// TODO: export QQuickFlickableVisibleArea
diff --git a/src/quicktemplates/qquickscrollindicator_p.h b/src/quicktemplates/qquickscrollindicator_p.h
index 1af6407690..e6647c77ab 100644
--- a/src/quicktemplates/qquickscrollindicator_p.h
+++ b/src/quicktemplates/qquickscrollindicator_p.h
@@ -23,7 +23,7 @@ class QQuickFlickable;
class QQuickScrollIndicatorAttached;
class QQuickScrollIndicatorPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollIndicator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollIndicator : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged FINAL)
@@ -96,7 +96,7 @@ private:
class QQuickScrollIndicatorAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollIndicatorAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollIndicatorAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickScrollIndicator *horizontal READ horizontal WRITE setHorizontal NOTIFY horizontalChanged FINAL)
@@ -123,6 +123,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScrollIndicator)
-
#endif // QQUICKSCROLLINDICATOR_P_H
diff --git a/src/quicktemplates/qquickscrollview.cpp b/src/quicktemplates/qquickscrollview.cpp
index 8a352b7f61..5f7c7bf2fd 100644
--- a/src/quicktemplates/qquickscrollview.cpp
+++ b/src/quicktemplates/qquickscrollview.cpp
@@ -98,11 +98,17 @@ public:
QQmlListProperty<QObject> contentData() override;
QQmlListProperty<QQuickItem> contentChildren() override;
QList<QQuickItem *> contentChildItems() const override;
+ QQuickItem* getFirstChild() const override;
QQuickItem *getContentItem() override;
- QQuickFlickable *ensureFlickable(bool content);
- bool setFlickable(QQuickFlickable *flickable, bool content);
+ enum class ContentItemFlag {
+ DoNotSet,
+ Set
+ };
+
+ QQuickFlickable *ensureFlickable(ContentItemFlag contentItemFlag);
+ bool setFlickable(QQuickFlickable *flickable, ContentItemFlag contentItemFlag);
void flickableContentWidthChanged();
void flickableContentHeightChanged();
@@ -127,10 +133,17 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
+ void updateScrollBarWidth();
+ void updateScrollBarHeight();
+
+ void disconnectScrollBarSignals(QQuickScrollBarAttachedPrivate *scrollBar);
bool wasTouched = false;
QQuickFlickable *flickable = nullptr;
bool flickableHasExplicitContentWidth = true;
bool flickableHasExplicitContentHeight = true;
+ bool isUpdatingScrollBar = false;
+ qreal effectiveScrollBarWidth = 0;
+ qreal effectiveScrollBarHeight = 0;
};
QList<QQuickItem *> QQuickScrollViewPrivate::contentChildItems() const
@@ -145,15 +158,23 @@ QQuickItem *QQuickScrollViewPrivate::getContentItem()
{
if (!contentItem)
executeContentItem();
- return ensureFlickable(false);
+ // This function is called by QQuickControl::contentItem() to lazily create
+ // a contentItem, so we don't need to try to set it again.
+ return ensureFlickable(ContentItemFlag::DoNotSet);
}
-QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(bool content)
+QQuickItem* QQuickScrollViewPrivate::getFirstChild() const
+{
+ return contentChildItems().value(0);
+}
+
+QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(ContentItemFlag contentItemFlag)
{
Q_Q(QQuickScrollView);
if (!flickable) {
flickableHasExplicitContentWidth = false;
flickableHasExplicitContentHeight = false;
+ // Pass ourselves as the Flickable's parent item.
auto flickable = new QQuickFlickable(q);
// We almost always want to clip the flickable so that flickable
// contents doesn't show up outside the scrollview. The only time
@@ -163,12 +184,64 @@ QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(bool content)
// child inside the scrollview, and control clipping on it explicit.
flickable->setClip(true);
flickable->setPixelAligned(true);
- setFlickable(flickable, content);
+ setFlickable(flickable, contentItemFlag);
}
return flickable;
}
-bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
+void QQuickScrollViewPrivate::updateScrollBarWidth()
+{
+ Q_Q(QQuickScrollView);
+ qreal oldEffectiveScrollBarWidth = effectiveScrollBarWidth;
+ if (auto *vBar = verticalScrollBar()) {
+ if (vBar->policy() == QQuickScrollBar::AlwaysOff || !vBar->isVisible())
+ effectiveScrollBarWidth = 0;
+ else
+ effectiveScrollBarWidth = vBar->width();
+ }
+ if (effectiveScrollBarWidth != oldEffectiveScrollBarWidth) {
+ if (!isUpdatingScrollBar) {
+ QScopedValueRollback<bool> rollback(isUpdatingScrollBar, true);
+ emit q->effectiveScrollBarWidthChanged();
+ }
+ }
+}
+
+void QQuickScrollViewPrivate::updateScrollBarHeight()
+{
+ Q_Q(QQuickScrollView);
+ qreal oldEffectiveScrollBarHeight = effectiveScrollBarHeight;
+ if (auto *hBar = horizontalScrollBar()) {
+ if (hBar->policy() == QQuickScrollBar::AlwaysOff || !hBar->isVisible())
+ effectiveScrollBarHeight = 0;
+ else
+ effectiveScrollBarHeight = hBar->height();
+ }
+ if (effectiveScrollBarHeight != oldEffectiveScrollBarHeight) {
+ if (!isUpdatingScrollBar) {
+ QScopedValueRollback<bool> rollback(isUpdatingScrollBar, true);
+ emit q->effectiveScrollBarHeightChanged();
+ }
+
+ }
+}
+
+void QQuickScrollViewPrivate::disconnectScrollBarSignals(QQuickScrollBarAttachedPrivate *scrollBar)
+{
+ if (!scrollBar)
+ return;
+
+ if (scrollBar->vertical) {
+ QObjectPrivate::disconnect(scrollBar->vertical, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ QObjectPrivate::disconnect(scrollBar->vertical, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ }
+ if (scrollBar->horizontal) {
+ QObjectPrivate::disconnect(scrollBar->horizontal, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ QObjectPrivate::disconnect(scrollBar->horizontal, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ }
+}
+
+bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, ContentItemFlag contentItemFlag)
{
Q_Q(QQuickScrollView);
if (item == flickable)
@@ -179,8 +252,11 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
if (flickable) {
flickable->removeEventFilter(q);
- if (attached)
- QQuickScrollBarAttachedPrivate::get(attached)->setFlickable(nullptr);
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ scrollBar->setFlickable(nullptr);
+ disconnectScrollBarSignals(scrollBar);
+ }
QObjectPrivate::disconnect(flickable->contentItem(), &QQuickItem::childrenChanged, this, &QQuickPanePrivate::contentChildrenChange);
QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickScrollViewPrivate::flickableContentWidthChanged);
@@ -188,7 +264,7 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
}
flickable = item;
- if (content)
+ if (contentItemFlag == ContentItemFlag::Set)
q->setContentItem(flickable);
if (flickable) {
@@ -202,8 +278,18 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
else
flickableContentHeightChanged();
- if (attached)
- QQuickScrollBarAttachedPrivate::get(attached)->setFlickable(flickable);
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ scrollBar->setFlickable(flickable);
+ if (scrollBar->vertical) {
+ QObjectPrivate::connect(scrollBar->vertical, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ QObjectPrivate::connect(scrollBar->vertical, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ }
+ if (scrollBar->horizontal) {
+ QObjectPrivate::connect(scrollBar->horizontal, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ QObjectPrivate::connect(scrollBar->horizontal, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ }
+ }
QObjectPrivate::connect(flickable->contentItem(), &QQuickItem::childrenChanged, this, &QQuickPanePrivate::contentChildrenChange);
QObjectPrivate::connect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickScrollViewPrivate::flickableContentWidthChanged);
@@ -305,11 +391,14 @@ void QQuickScrollViewPrivate::setScrollBarsInteractive(bool interactive)
void QQuickScrollViewPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
{
QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
- if (!p->flickable && p->setFlickable(qobject_cast<QQuickFlickable *>(obj), true))
+ // If we don't yet have a flickable assigned, and this object is a Flickable,
+ // make it our contentItem.
+ if (!p->flickable && p->setFlickable(qobject_cast<QQuickFlickable *>(obj), ContentItemFlag::Set))
return;
- QQuickFlickable *flickable = p->ensureFlickable(true);
+ QQuickFlickable *flickable = p->ensureFlickable(ContentItemFlag::Set);
Q_ASSERT(flickable);
+ // Add the object that was declared as a child of us as a child object of the Flickable.
QQmlListProperty<QObject> data = flickable->flickableData();
data.append(&data, obj);
}
@@ -348,10 +437,11 @@ void QQuickScrollViewPrivate::contentChildren_append(QQmlListProperty<QQuickItem
{
QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
if (!p->flickable)
- p->setFlickable(qobject_cast<QQuickFlickable *>(item), true);
+ p->setFlickable(qobject_cast<QQuickFlickable *>(item), ContentItemFlag::Set);
- QQuickFlickable *flickable = p->ensureFlickable(true);
+ QQuickFlickable *flickable = p->ensureFlickable(ContentItemFlag::Set);
Q_ASSERT(flickable);
+ // Add the item that was declared as a child of us as a child item of the Flickable's contentItem.
QQmlListProperty<QQuickItem> children = flickable->flickableChildren();
children.append(&children, item);
}
@@ -406,6 +496,48 @@ QQuickScrollView::QQuickScrollView(QQuickItem *parent)
setWheelEnabled(true);
}
+QQuickScrollView::~QQuickScrollView()
+{
+ Q_D(QQuickScrollView);
+ QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(qmlAttachedPropertiesObject<QQuickScrollBar>(this, false));
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ d->disconnectScrollBarSignals(scrollBar);
+ }
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollView::effectiveScrollBarWidth
+ \since 6.6
+
+ This property holds the effective width of the vertical scrollbar.
+ When the scrollbar policy is \c QQuickScrollBar::AlwaysOff or the scrollbar
+ is not visible, this property is \c 0.
+
+ \sa {ScrollBar::policy}
+*/
+qreal QQuickScrollView::effectiveScrollBarWidth()
+{
+ Q_D(QQuickScrollView);
+ return d->effectiveScrollBarWidth;
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollView::effectiveScrollBarHeight
+ \since 6.6
+
+ This property holds the effective height of the horizontal scrollbar.
+ When the scrollbar policy is \c QQuickScrollBar::AlwaysOff or the scrollbar
+ is not visible, this property is \c 0.
+
+ \sa {ScrollBar::policy}
+*/
+qreal QQuickScrollView::effectiveScrollBarHeight()
+{
+ Q_D(QQuickScrollView);
+ return d->effectiveScrollBarHeight;
+}
+
/*!
\qmlproperty list<QtObject> QtQuick.Controls::ScrollView::contentData
\qmldefault
@@ -543,7 +675,7 @@ void QQuickScrollView::componentComplete()
Q_D(QQuickScrollView);
QQuickPane::componentComplete();
if (!d->contentItem)
- d->ensureFlickable(true);
+ d->ensureFlickable(QQuickScrollViewPrivate::ContentItemFlag::Set);
}
void QQuickScrollView::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
@@ -557,7 +689,25 @@ void QQuickScrollView::contentItemChange(QQuickItem *newItem, QQuickItem *oldIte
auto newItemAsFlickable = qobject_cast<QQuickFlickable *>(newItem);
if (newItem && !newItemAsFlickable)
qmlWarning(this) << "ScrollView only supports Flickable types as its contentItem";
- d->setFlickable(newItemAsFlickable, false);
+ // This is called by QQuickControlPrivate::setContentItem_helper, so no need to
+ // try to set it as the contentItem.
+ d->setFlickable(newItemAsFlickable, QQuickScrollViewPrivate::ContentItemFlag::DoNotSet);
+ // We do, however, need to set us as its parent item, as setContentItem_helper will only
+ // do so if the item doesn't already have a parent. If newItem wasn't declared as our
+ // child and was instead imperatively assigned, it may already have a parent item,
+ // which we'll need to override.
+ if (newItem) {
+ newItem->setParentItem(this);
+
+ // Make sure that the scroll bars are stacked in front of the flickable,
+ // otherwise events won't get through to them.
+ QQuickScrollBar *verticalBar = d->verticalScrollBar();
+ if (verticalBar)
+ verticalBar->stackAfter(newItem);
+ QQuickScrollBar *horizontalBar = d->horizontalScrollBar();
+ if (horizontalBar)
+ horizontalBar->stackAfter(newItem);
+ }
}
QQuickPane::contentItemChange(newItem, oldItem);
}
@@ -573,10 +723,14 @@ void QQuickScrollView::contentSizeChange(const QSizeF &newSize, const QSizeF &ol
// exception is if the application has assigned a content size
// directly to the scrollview, which will then win even if the
// application has assigned something else to the flickable.
- if (d->hasContentWidth || !d->flickableHasExplicitContentWidth)
+ if (d->hasContentWidth || !d->flickableHasExplicitContentWidth) {
d->flickable->setContentWidth(newSize.width());
- if (d->hasContentHeight || !d->flickableHasExplicitContentHeight)
+ d->updateScrollBarWidth();
+ }
+ if (d->hasContentHeight || !d->flickableHasExplicitContentHeight) {
d->flickable->setContentHeight(newSize.height());
+ d->updateScrollBarHeight();
+ }
}
}
diff --git a/src/quicktemplates/qquickscrollview_p.h b/src/quicktemplates/qquickscrollview_p.h
index 927de4365c..6cdafd5199 100644
--- a/src/quicktemplates/qquickscrollview_p.h
+++ b/src/quicktemplates/qquickscrollview_p.h
@@ -22,14 +22,19 @@ QT_BEGIN_NAMESPACE
class QQuickScrollViewPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollView : public QQuickPane
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollView : public QQuickPane
{
Q_OBJECT
QML_NAMED_ELEMENT(ScrollView)
QML_ADDED_IN_VERSION(2, 2)
+ Q_PROPERTY(qreal effectiveScrollBarWidth READ effectiveScrollBarWidth NOTIFY effectiveScrollBarWidthChanged FINAL REVISION(6, 6))
+ Q_PROPERTY(qreal effectiveScrollBarHeight READ effectiveScrollBarHeight NOTIFY effectiveScrollBarHeightChanged FINAL REVISION(6, 6))
public:
explicit QQuickScrollView(QQuickItem *parent = nullptr);
+ ~QQuickScrollView();
+ qreal effectiveScrollBarWidth();
+ qreal effectiveScrollBarHeight();
protected:
bool childMouseEventFilter(QQuickItem *item, QEvent *event) override;
@@ -44,6 +49,10 @@ protected:
QAccessible::Role accessibleRole() const override;
#endif
+Q_SIGNALS:
+ Q_REVISION(6, 6) void effectiveScrollBarWidthChanged();
+ Q_REVISION(6, 6) void effectiveScrollBarHeightChanged();
+
private:
Q_DISABLE_COPY(QQuickScrollView)
Q_DECLARE_PRIVATE(QQuickScrollView)
@@ -51,6 +60,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScrollView)
-
#endif // QQUICKSCROLLVIEW_P_H
diff --git a/src/quicktemplates/qquickselectionrectangle.cpp b/src/quicktemplates/qquickselectionrectangle.cpp
index a2adc43dcd..435b12819b 100644
--- a/src/quicktemplates/qquickselectionrectangle.cpp
+++ b/src/quicktemplates/qquickselectionrectangle.cpp
@@ -81,7 +81,7 @@ QT_BEGIN_NAMESPACE
The handle is not hidden by default when a selection is removed.
Instead, this is the responsibility of the delegate, to open up for
custom fade-out animations. The easiest way to ensure that the handle
- ends up hidden, is to simply bind \l visible to the the \l active
+ ends up hidden, is to simply bind \l {Item::}{visible} to the \l active
state of the SelectionRectangle:
\qml
@@ -109,7 +109,7 @@ QT_BEGIN_NAMESPACE
The handle is not hidden by default when a selection is removed.
Instead, this is the responsibility of the delegate, to open up for
custom fade-out animations. The easiest way to ensure that the handle
- ends up hidden, is to simply bind \l visible to the the \l active
+ ends up hidden, is to simply bind \l {Item::}{visible} to the \l active
state of the SelectionRectangle:
\qml
@@ -144,14 +144,14 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \qmlattachedproperty SelectionRectangle QtQuick::SelectionRectangle::control
+ \qmlattachedproperty SelectionRectangle QtQuick.Controls::SelectionRectangle::control
This attached property holds the SelectionRectangle that manages the delegate instance.
It is attached to each handle instance.
*/
/*!
- \qmlattachedproperty bool QtQuick::SelectionRectangle::dragging
+ \qmlattachedproperty bool QtQuick.Controls::SelectionRectangle::dragging
This attached property will be \c true if the user is dragging on the handle.
It is attached to each handle instance.
@@ -176,50 +176,112 @@ QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate()
m_scrollSpeed = QSizeF(qAbs(dist.width() * 0.007), qAbs(dist.height() * 0.007));
});
- QObject::connect(m_tapHandler, &QQuickTapHandler::tapped, [this] {
- updateActiveState(false);
- });
+ QObject::connect(m_tapHandler, &QQuickTapHandler::pressedChanged, [this]() {
+ if (!m_tapHandler->isPressed())
+ return;
+ if (m_effectiveSelectionMode != QQuickSelectionRectangle::Drag)
+ return;
- QObject::connect(m_tapHandler, &QQuickTapHandler::longPressed, [this]() {
const QPointF pos = m_tapHandler->point().pressPosition();
const auto modifiers = m_tapHandler->point().modifiers();
+ if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier))
+ return;
+
+ if (modifiers & Qt::ShiftModifier) {
+ // Extend the selection towards the pressed cell. If there is no
+ // existing selection, start a new selection from the current item
+ // to the pressed item.
+ if (!m_active) {
+ if (!m_selectable->startSelection(pos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(QPoint{-1, -1});
+ }
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ } else if (modifiers & Qt::ControlModifier) {
+ // Select a single cell, but keep the old selection (unless
+ // m_selectable->startSelection(pos. modifiers) returns false, which
+ // it will if selectionMode only allows a single selection).
+ if (handleUnderPos(pos) != nullptr) {
+ // Don't allow press'n'hold to start a new
+ // selection if it started on top of a handle.
+ return;
+ }
+
+ if (!m_selectable->startSelection(pos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(pos);
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ }
+ });
- if (!m_selectable->startSelection(pos))
+ QObject::connect(m_tapHandler, &QQuickTapHandler::longPressed, [this]() {
+ if (m_effectiveSelectionMode != QQuickSelectionRectangle::PressAndHold)
return;
+
+ const QPointF pos = m_tapHandler->point().pressPosition();
+ const auto modifiers = m_tapHandler->point().modifiers();
if (handleUnderPos(pos) != nullptr) {
// Don't allow press'n'hold to start a new
// selection if it started on top of a handle.
return;
}
- if (!m_alwaysAcceptPressAndHold) {
- if (m_selectionMode == QQuickSelectionRectangle::Auto) {
- // In Auto mode, we only accept press and hold from touch
- if (m_tapHandler->point().device()->pointerType() != QPointingDevice::PointerType::Finger)
+
+ if (modifiers == Qt::ShiftModifier) {
+ // Extend the selection towards the pressed cell. If there is no
+ // existing selection, start a new selection from the current item
+ // to the pressed item.
+ if (!m_active) {
+ if (!m_selectable->startSelection(pos, modifiers))
return;
- } else if (m_selectionMode != QQuickSelectionRectangle::PressAndHold) {
- return;
+ m_selectable->setSelectionStartPos(QPoint{-1, -1});
}
- }
-
- if (!modifiers.testFlag(Qt::ShiftModifier))
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ } else if (modifiers == Qt::ControlModifier) {
+ // Select a single cell, but keep the old selection (unless
+ // m_selectable->startSelection(pos, modifiers) returns false, which
+ // it will if selectionMode only allows a single selection).
+ if (!m_selectable->startSelection(pos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(pos);
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ } else if (modifiers == Qt::NoModifier) {
+ // Select a single cell
m_selectable->clearSelection();
- m_selectable->setSelectionStartPos(pos);
- m_selectable->setSelectionEndPos(pos);
- updateHandles();
- updateActiveState(true);
+ if (!m_selectable->startSelection(pos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(pos);
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ }
});
QObject::connect(m_dragHandler, &QQuickDragHandler::activeChanged, [this]() {
+ Q_ASSERT(m_effectiveSelectionMode == QQuickSelectionRectangle::Drag);
const QPointF startPos = m_dragHandler->centroid().pressPosition();
const QPointF dragPos = m_dragHandler->centroid().position();
const auto modifiers = m_dragHandler->centroid().modifiers();
+ if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier))
+ return;
if (m_dragHandler->active()) {
- if (!m_selectable->startSelection(startPos))
- return;
- if (!modifiers.testFlag(Qt::ShiftModifier))
- m_selectable->clearSelection();
- m_selectable->setSelectionStartPos(startPos);
+ // Start a new selection unless there is an active selection
+ // already, and one of the relevant modifiers are being held.
+ // In that case we continue to extend the active selection instead.
+ const bool modifiersHeld = modifiers & (Qt::ControlModifier | Qt::ShiftModifier);
+ if (!m_active || !modifiersHeld) {
+ if (!m_selectable->startSelection(startPos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(startPos);
+ }
m_selectable->setSelectionEndPos(dragPos);
m_draggedHandle = nullptr;
updateHandles();
@@ -409,6 +471,25 @@ void QQuickSelectionRectanglePrivate::connectToTarget()
if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
connect(flickable, &QQuickFlickable::interactiveChanged, this, &QQuickSelectionRectanglePrivate::updateSelectionMode);
}
+
+ // Add a callback function that tells if the selection was
+ // modified outside of the actions taken by SelectionRectangle.
+ m_selectable->setCallback([this](QQuickSelectable::CallBackFlag flag){
+ switch (flag) {
+ case QQuickSelectable::CallBackFlag::CancelSelection:
+ // The selection is either cleared, or can no longer be
+ // represented as a rectangle with two selection handles.
+ updateActiveState(false);
+ break;
+ case QQuickSelectable::CallBackFlag::SelectionRectangleChanged:
+ // The selection has changed, but the selection is still
+ // rectangular and without holes.
+ updateHandles();
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ });
}
void QQuickSelectionRectanglePrivate::updateSelectionMode()
@@ -419,26 +500,33 @@ void QQuickSelectionRectanglePrivate::updateSelectionMode()
m_tapHandler->setEnabled(enabled);
if (m_selectionMode == QQuickSelectionRectangle::Auto) {
- if (qobject_cast<QQuickScrollView *>(m_target->parentItem())) {
+ if (m_target && qobject_cast<QQuickScrollView *>(m_target->parentItem())) {
// ScrollView allows flicking with touch, but not with mouse. So we do
// the same here: you can drag to select with a mouse, but not with touch.
+ m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
m_dragHandler->setEnabled(enabled);
} else if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
- m_dragHandler->setEnabled(enabled && !flickable->isInteractive());
+ if (enabled && !flickable->isInteractive()) {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
+ m_dragHandler->setEnabled(true);
+ } else {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold;
+ m_dragHandler->setEnabled(false);
+ }
} else {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
m_dragHandler->setEnabled(enabled);
}
} else if (m_selectionMode == QQuickSelectionRectangle::Drag) {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::AllDevices);
m_dragHandler->setEnabled(enabled);
} else {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold;
m_dragHandler->setEnabled(false);
}
-
- // If you can't select using a drag, we always accept a PressAndHold
- m_alwaysAcceptPressAndHold = !m_dragHandler->enabled();
}
QQuickSelectionRectangleAttached *QQuickSelectionRectanglePrivate::getAttachedObject(const QObject *object) const
@@ -480,6 +568,7 @@ void QQuickSelectionRectangle::setTarget(QQuickItem *target)
d->m_tapHandler->setParent(this);
d->m_dragHandler->setParent(this);
d->m_target->disconnect(this);
+ d->m_selectable->setCallback(nullptr);
}
d->m_target = target;
diff --git a/src/quicktemplates/qquickselectionrectangle_p.h b/src/quicktemplates/qquickselectionrectangle_p.h
index 2cf3c22062..5a1cf7e041 100644
--- a/src/quicktemplates/qquickselectionrectangle_p.h
+++ b/src/quicktemplates/qquickselectionrectangle_p.h
@@ -18,13 +18,15 @@
#include <QtQuick/qquickitem.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickSelectionRectanglePrivate;
class QQuickSelectable;
class QQuickSelectionRectangleAttached;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSelectionRectangle : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickSelectionRectangle : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged FINAL)
@@ -77,7 +79,7 @@ private:
Q_DECLARE_PRIVATE(QQuickSelectionRectangle)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSelectionRectangleAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSelectionRectangleAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickSelectionRectangle *control READ control NOTIFY controlChanged FINAL)
@@ -105,6 +107,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSelectionRectangle)
-
#endif // QQUICKSELECTIONRECTANGLE_P_H
diff --git a/src/quicktemplates/qquickselectionrectangle_p_p.h b/src/quicktemplates/qquickselectionrectangle_p_p.h
index 4144550aa7..d053b8bf42 100644
--- a/src/quicktemplates/qquickselectionrectangle_p_p.h
+++ b/src/quicktemplates/qquickselectionrectangle_p_p.h
@@ -66,7 +66,7 @@ public:
QSizeF m_scrollSpeed = QSizeF(1, 1);
QQuickSelectionRectangle::SelectionMode m_selectionMode = QQuickSelectionRectangle::Auto;
- bool m_alwaysAcceptPressAndHold = false;
+ QQuickSelectionRectangle::SelectionMode m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
bool m_enabled = true;
bool m_active = false;
diff --git a/src/quicktemplates/qquickshortcutcontext.cpp b/src/quicktemplates/qquickshortcutcontext.cpp
index 2d07eb44f9..503eb270a7 100644
--- a/src/quicktemplates/qquickshortcutcontext.cpp
+++ b/src/quicktemplates/qquickshortcutcontext.cpp
@@ -4,8 +4,11 @@
#include "qquickshortcutcontext_p_p.h"
#include "qquickoverlay_p_p.h"
#include "qquicktooltip_p.h"
+#include <QtQmlModels/private/qtqmlmodels-config_p.h>
+#if QT_CONFIG(qml_object_model)
#include "qquickmenu_p.h"
#include "qquickmenu_p_p.h"
+#endif
#include "qquickpopup_p.h"
#include <QtCore/qloggingcategory.h>
@@ -27,6 +30,9 @@ static bool isBlockedByPopup(QQuickItem *item)
if (qobject_cast<QQuickToolTip *>(popup))
continue; // ignore tooltips (QTBUG-60492)
if (popup->isModal() || popup->closePolicy() & QQuickPopup::CloseOnEscape) {
+ qCDebug(lcContextMatcher) << popup << "is modal or has a CloseOnEscape policy;"
+ << "if the following are both true," << item << "will be blocked by it:"
+ << (item != popup->popupItem()) << !popup->popupItem()->isAncestorOf(item);
return item != popup->popupItem() && !popup->popupItem()->isAncestorOf(item);
}
}
@@ -50,6 +56,7 @@ bool QQuickShortcutContext::matcher(QObject *obj, Qt::ShortcutContext context)
obj = popup->window();
item = popup->popupItem();
+#if QT_CONFIG(qml_object_model)
if (!obj) {
// The popup has no associated window (yet). However, sub-menus,
// unlike top-level menus, will not have an associated window
@@ -61,15 +68,16 @@ bool QQuickShortcutContext::matcher(QObject *obj, Qt::ShortcutContext context)
obj = parentMenu->window();
}
}
+#endif
break;
}
obj = obj->parent();
}
if (QWindow *renderWindow = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow *>(obj)))
obj = renderWindow;
- qCDebug(lcContextMatcher) << "obj" << obj << "focusWindow" << QGuiApplication::focusWindow()
+ qCDebug(lcContextMatcher) << "obj" << obj << "item" << item << "focusWindow" << QGuiApplication::focusWindow()
<< "!isBlockedByPopup(item)" << !isBlockedByPopup(item);
- return obj && obj == QGuiApplication::focusWindow() && !isBlockedByPopup(item);
+ return obj && qobject_cast<QWindow*>(obj)->isActive() && !isBlockedByPopup(item);
default:
return false;
}
diff --git a/src/quicktemplates/qquickshortcutcontext_p_p.h b/src/quicktemplates/qquickshortcutcontext_p_p.h
index efeb541dda..4dcd96d54f 100644
--- a/src/quicktemplates/qquickshortcutcontext_p_p.h
+++ b/src/quicktemplates/qquickshortcutcontext_p_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QObject;
-struct Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickShortcutContext
+struct Q_QUICKTEMPLATES2_EXPORT QQuickShortcutContext
{
static bool matcher(QObject *object, Qt::ShortcutContext context);
};
diff --git a/src/quicktemplates/qquickslider.cpp b/src/quicktemplates/qquickslider.cpp
index 70317cd231..04b5589524 100644
--- a/src/quicktemplates/qquickslider.cpp
+++ b/src/quicktemplates/qquickslider.cpp
@@ -74,6 +74,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
qreal from = 0;
qreal to = 1;
@@ -202,8 +203,6 @@ void QQuickSliderPrivate::handleUngrab()
q->setPressed(false);
}
-static inline QString handleName() { return QStringLiteral("handle"); }
-
void QQuickSliderPrivate::cancelHandle()
{
Q_Q(QQuickSlider);
@@ -238,9 +237,21 @@ void QQuickSliderPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit q->implicitHandleHeightChanged();
}
+void QQuickSliderPrivate::itemDestroyed(QQuickItem *item)
+{
+ Q_Q(QQuickSlider);
+ QQuickControlPrivate::itemDestroyed(item);
+ if (item == handle) {
+ handle = nullptr;
+ emit q->handleChanged();
+ }
+}
+
QQuickSlider::QQuickSlider(QQuickItem *parent)
: QQuickControl(*(new QQuickSliderPrivate), parent)
{
+ Q_D(QQuickSlider);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
setActiveFocusOnTab(true);
#ifdef Q_OS_MACOS
setFocusPolicy(Qt::TabFocus);
@@ -524,6 +535,11 @@ void QQuickSlider::setOrientation(Qt::Orientation orientation)
if (d->orientation == orientation)
return;
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
d->orientation = orientation;
emit orientationChanged();
}
diff --git a/src/quicktemplates/qquickslider_p.h b/src/quicktemplates/qquickslider_p.h
index 8452d4dad9..4374a6b05d 100644
--- a/src/quicktemplates/qquickslider_p.h
+++ b/src/quicktemplates/qquickslider_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickSliderPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSlider : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickSlider : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -152,6 +152,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSlider)
-
#endif // QQUICKSLIDER_P_H
diff --git a/src/quicktemplates/qquickspinbox.cpp b/src/quicktemplates/qquickspinbox.cpp
index c825724fc8..2a68edac33 100644
--- a/src/quicktemplates/qquickspinbox.cpp
+++ b/src/quicktemplates/qquickspinbox.cpp
@@ -10,7 +10,9 @@
#include <QtGui/qstylehints.h>
#include <QtQml/qqmlinfo.h>
+#if QT_CONFIG(qml_locale)
#include <QtQml/private/qqmllocale_p.h>
+#endif
#include <QtQml/private/qqmlengine_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
@@ -60,6 +62,10 @@ static const int AUTO_REPEAT_INTERVAL = 100;
\snippet qtquickcontrols-spinbox-double.qml 1
+ A prefix and suffix can be added using regular expressions:
+
+ \snippet qtquickcontrols-spinbox-prefix.qml 1
+
\sa Tumbler, {Customizing SpinBox}, {Focus Management in Qt Quick Controls}
*/
@@ -88,8 +94,9 @@ public:
int effectiveStepSize() const;
- void updateDisplayText(bool modified = false);
- void setDisplayText(const QString &displayText, bool modified = false);
+ void updateDisplayText();
+ void setDisplayText(const QString &displayText);
+ void contentItemTextChanged();
bool upEnabled() const;
void updateUpEnabled();
@@ -108,10 +115,15 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
+
+ QString evaluateTextFromValue(int val) const;
+ int evaluateValueFromText(const QString &text) const;
QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::SpinBox); }
bool editable = false;
+ bool live = false;
bool wrap = false;
int from = 0;
int to = 99;
@@ -148,20 +160,10 @@ int QQuickSpinBoxPrivate::boundValue(int value, bool wrap) const
void QQuickSpinBoxPrivate::updateValue()
{
- Q_Q(QQuickSpinBox);
if (contentItem) {
QVariant text = contentItem->property("text");
if (text.isValid()) {
- int val = 0;
- QQmlEngine *engine = qmlEngine(q);
- if (engine && valueFromText.isCallable()) {
- QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
- QJSValue loc = QJSValuePrivate::fromReturnedValue(QQmlLocale::wrap(v4, locale));
- val = valueFromText.call(QJSValueList() << text.toString() << loc).toInt();
- } else {
- val = locale.toInt(text.toString());
- }
- setValue(val, /* allowWrap = */ false, /* modified = */ true);
+ setValue(evaluateValueFromText(text.toString()), /* allowWrap = */ false, /* modified = */ true);
}
}
}
@@ -183,7 +185,7 @@ bool QQuickSpinBoxPrivate::setValue(int newValue, bool allowWrap, bool modified)
const bool emitSignals = (value != correctedValue);
value = correctedValue;
- updateDisplayText(modified);
+ updateDisplayText();
updateUpEnabled();
updateDownEnabled();
@@ -217,32 +219,49 @@ int QQuickSpinBoxPrivate::effectiveStepSize() const
return from > to ? -1 * stepSize : stepSize;
}
-void QQuickSpinBoxPrivate::updateDisplayText(bool modified)
+void QQuickSpinBoxPrivate::updateDisplayText()
{
- Q_Q(QQuickSpinBox);
- QString text;
- QQmlEngine *engine = qmlEngine(q);
- if (engine && textFromValue.isCallable()) {
- QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
- QJSValue loc = QJSValuePrivate::fromReturnedValue(QQmlLocale::wrap(v4, locale));
- text = textFromValue.call(QJSValueList() << value << loc).toString();
- } else {
- text = locale.toString(value);
- }
- setDisplayText(text, modified);
+ setDisplayText(evaluateTextFromValue(value));
}
-void QQuickSpinBoxPrivate::setDisplayText(const QString &text, bool modified)
+void QQuickSpinBoxPrivate::setDisplayText(const QString &text)
{
Q_Q(QQuickSpinBox);
- if (!modified && displayText == text)
+ if (displayText == text)
return;
displayText = text;
emit q->displayTextChanged();
}
+void QQuickSpinBoxPrivate::contentItemTextChanged()
+{
+ Q_Q(QQuickSpinBox);
+
+ QQuickTextInput *inputTextItem = qobject_cast<QQuickTextInput *>(q->contentItem());
+ if (!inputTextItem)
+ return;
+ QString text = inputTextItem->text();
+#if QT_CONFIG(validator)
+ if (validator)
+ validator->fixup(text);
+#endif
+
+ if (live) {
+ const int enteredVal = evaluateValueFromText(text);
+ const int correctedValue = boundValue(enteredVal, false);
+ if (correctedValue == enteredVal && correctedValue != value) {
+ // If live is true and the text is valid change the value
+ // setValue will set the displayText for us.
+ q->setValue(correctedValue);
+ return;
+ }
+ }
+ // If live is false or the value is not valid, just set the displayText
+ setDisplayText(text);
+}
+
bool QQuickSpinBoxPrivate::upEnabled() const
{
const QQuickItem *upIndicator = up->indicator();
@@ -352,13 +371,15 @@ bool QQuickSpinBoxPrivate::handleRelease(const QPointF &point, ulong timestamp)
int oldValue = value;
if (up->isPressed()) {
- up->setPressed(false);
if (repeatTimer <= 0 && ui && ui->contains(ui->mapFromItem(q, point)))
q->increase();
+ // Retain pressed state until after increasing is done in case user code binds stepSize
+ // to up/down.pressed.
+ up->setPressed(false);
} else if (down->isPressed()) {
- down->setPressed(false);
if (repeatTimer <= 0 && di && di->contains(di->mapFromItem(q, point)))
q->decrease();
+ down->setPressed(false);
}
if (value != oldValue)
emit q->valueModified();
@@ -397,12 +418,62 @@ void QQuickSpinBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit down->implicitIndicatorHeightChanged();
}
+void QQuickSpinBoxPrivate::itemDestroyed(QQuickItem *item)
+{
+ QQuickControlPrivate::itemDestroyed(item);
+ if (item == up->indicator())
+ up->setIndicator(nullptr);
+ else if (item == down->indicator())
+ down->setIndicator(nullptr);
+}
+
+
+QString QQuickSpinBoxPrivate::evaluateTextFromValue(int val) const
+{
+ Q_Q(const QQuickSpinBox);
+
+ QString text;
+ QQmlEngine *engine = qmlEngine(q);
+ if (engine && textFromValue.isCallable()) {
+ QJSValue loc;
+#if QT_CONFIG(qml_locale)
+ QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
+ loc = QJSValuePrivate::fromReturnedValue(
+ v4->fromData(QMetaType::fromType<QLocale>(), &locale));
+#endif
+ text = textFromValue.call(QJSValueList() << val << loc).toString();
+ } else {
+ text = locale.toString(val);
+ }
+ return text;
+}
+
+int QQuickSpinBoxPrivate::evaluateValueFromText(const QString &text) const
+{
+ Q_Q(const QQuickSpinBox);
+ int value;
+ QQmlEngine *engine = qmlEngine(q);
+ if (engine && valueFromText.isCallable()) {
+ QJSValue loc;
+#if QT_CONFIG(qml_locale)
+ QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
+ loc = QJSValuePrivate::fromReturnedValue(
+ v4->fromData(QMetaType::fromType<QLocale>(), &locale));
+#endif
+ value = valueFromText.call(QJSValueList() << text << loc).toInt();
+ } else {
+ value = locale.toInt(text);
+ }
+ return value;
+}
+
QQuickSpinBox::QQuickSpinBox(QQuickItem *parent)
: QQuickControl(*(new QQuickSpinBoxPrivate), parent)
{
Q_D(QQuickSpinBox);
d->up = new QQuickIndicatorButton(this);
d->down = new QQuickIndicatorButton(this);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
setFlag(ItemIsFocusScope);
setFiltersChildMouseEvents(true);
@@ -550,6 +621,41 @@ void QQuickSpinBox::setEditable(bool editable)
emit editableChanged();
}
+/*!
+ \qmlproperty bool QtQuick.Controls::SpinBox::live
+ \since 6.6
+
+ This property holds whether the \l value is updated when the user edits the
+ \l displayText. The default value is \c false. If this property is \c true and
+ the value entered by the user is valid and within the bounds of the spinbox
+ [\l from, \l to], the value of the SpinBox will be set. If this property is
+ \c false or the value entered by the user is outside the boundaries, the
+ value will not be updated until the enter or return keys are pressed, or the
+ input field loses focus.
+
+ \sa editable, displayText
+*/
+bool QQuickSpinBox::isLive() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->live;
+}
+
+void QQuickSpinBox::setLive(bool live)
+{
+ Q_D(QQuickSpinBox);
+ if (d->live == live)
+ return;
+
+ d->live = live;
+
+ //make sure to update the value when changing to live
+ if (live)
+ d->contentItemTextChanged();
+
+ emit liveChanged();
+}
+
#if QT_CONFIG(validator)
/*!
\qmlproperty Validator QtQuick.Controls::SpinBox::validator
@@ -890,16 +996,18 @@ void QQuickSpinBox::keyPressEvent(QKeyEvent *event)
switch (event->key()) {
case Qt::Key_Up:
if (d->upEnabled()) {
- d->increase(true);
+ // Update the pressed state before increasing/decreasing in case user code binds
+ // stepSize to up/down.pressed.
d->up->setPressed(true);
+ d->increase(true);
event->accept();
}
break;
case Qt::Key_Down:
if (d->downEnabled()) {
- d->decrease(true);
d->down->setPressed(true);
+ d->decrease(true);
event->accept();
}
break;
@@ -988,20 +1096,24 @@ void QQuickSpinBox::itemChange(ItemChange change, const ItemChangeData &value)
void QQuickSpinBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
{
Q_D(QQuickSpinBox);
- if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem))
+ if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
disconnect(oldInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickSpinBox::inputMethodComposingChanged);
+ QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d, &QQuickSpinBoxPrivate::contentItemTextChanged);
+ }
if (newItem) {
newItem->setActiveFocusOnTab(true);
if (d->activeFocus)
- newItem->forceActiveFocus(d->focusReason);
+ newItem->forceActiveFocus(static_cast<Qt::FocusReason>(d->focusReason));
#if QT_CONFIG(cursor)
if (d->editable)
newItem->setCursor(Qt::IBeamCursor);
#endif
- if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem))
+ if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
connect(newInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickSpinBox::inputMethodComposingChanged);
+ QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d, &QQuickSpinBoxPrivate::contentItemTextChanged);
+ }
}
}
diff --git a/src/quicktemplates/qquickspinbox_p.h b/src/quicktemplates/qquickspinbox_p.h
index a50e77c171..c9547f405b 100644
--- a/src/quicktemplates/qquickspinbox_p.h
+++ b/src/quicktemplates/qquickspinbox_p.h
@@ -24,7 +24,7 @@ class QValidator;
class QQuickSpinBoxPrivate;
class QQuickIndicatorButton;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSpinBox : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickSpinBox : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(int from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -32,6 +32,8 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSpinBox : public QQuickControl
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged FINAL)
Q_PROPERTY(int stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL)
Q_PROPERTY(bool editable READ isEditable WRITE setEditable NOTIFY editableChanged FINAL)
+ Q_PROPERTY(bool live READ isLive WRITE setLive NOTIFY liveChanged FINAL REVISION(6, 6))
+
#if QT_CONFIG(validator)
Q_PROPERTY(QValidator *validator READ validator WRITE setValidator NOTIFY validatorChanged FINAL)
#endif
@@ -68,6 +70,9 @@ public:
bool isEditable() const;
void setEditable(bool editable);
+ bool isLive() const;
+ void setLive(bool live);
+
#if QT_CONFIG(validator)
QValidator *validator() const;
void setValidator(QValidator *validator);
@@ -105,6 +110,7 @@ Q_SIGNALS:
void valueChanged();
void stepSizeChanged();
void editableChanged();
+ Q_REVISION(6, 6) void liveChanged();
#if QT_CONFIG(validator)
void validatorChanged();
#endif
@@ -151,6 +157,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSpinBox)
-
#endif // QQUICKSPINBOX_P_H
diff --git a/src/quicktemplates/qquicksplitview.cpp b/src/quicktemplates/qquicksplitview.cpp
index 0bf1b79cac..1d41016f94 100644
--- a/src/quicktemplates/qquicksplitview.cpp
+++ b/src/quicktemplates/qquicksplitview.cpp
@@ -225,6 +225,9 @@ Q_LOGGING_CATEGORY(qlcQQuickSplitView, "qt.quick.controls.splitview")
Q_LOGGING_CATEGORY(qlcQQuickSplitViewPointer, "qt.quick.controls.splitview.pointer")
Q_LOGGING_CATEGORY(qlcQQuickSplitViewState, "qt.quick.controls.splitview.state")
+/*
+ Updates m_fillIndex to be between 0 .. (item count - 1).
+*/
void QQuickSplitViewPrivate::updateFillIndex()
{
const int count = contentModel->count();
@@ -232,10 +235,9 @@ void QQuickSplitViewPrivate::updateFillIndex()
qCDebug(qlcQQuickSplitView) << "looking for fillWidth/Height item amongst" << count << "items";
- m_fillIndex = -1;
- int i = 0;
+ int fillIndex = -1;
int lastVisibleIndex = -1;
- for (; i < count; ++i) {
+ for (int i = 0; i < count; ++i) {
QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
if (!item->isVisible())
continue;
@@ -248,19 +250,21 @@ void QQuickSplitViewPrivate::updateFillIndex()
continue;
if ((horizontal && attached->fillWidth()) || (!horizontal && attached->fillHeight())) {
- m_fillIndex = i;
- qCDebug(qlcQQuickSplitView) << "found fillWidth/Height item at index" << m_fillIndex;
+ fillIndex = i;
+ qCDebug(qlcQQuickSplitView) << "found fillWidth/Height item at index" << fillIndex;
break;
}
}
- if (m_fillIndex == -1) {
- // If there was no item with fillWidth/fillHeight set, m_fillIndex will be -1,
- // and we'll set it to the last visible item.
+ if (fillIndex == -1) {
+ // If there was no item with fillWidth/fillHeight set, fillIndex will be -1,
+ // and we'll set m_fillIndex to the last visible item.
// If there was an item with fillWidth/fillHeight set, we were already done and this will be skipped.
- m_fillIndex = lastVisibleIndex != -1 ? lastVisibleIndex : count - 1;
- qCDebug(qlcQQuickSplitView) << "found no fillWidth/Height item; using last item at index" << m_fillIndex;
+ fillIndex = lastVisibleIndex != -1 ? lastVisibleIndex : count - 1;
+ qCDebug(qlcQQuickSplitView) << "found no fillWidth/Height item; using last item at index" << fillIndex;
}
+ // Take new fillIndex into use.
+ m_fillIndex = fillIndex;
}
/*
@@ -325,11 +329,14 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
// The handle shouldn't cross other handles, so use the right edge of
// the first handle to the left as the left edge.
qreal leftEdge = 0;
- if (m_pressedHandleIndex - 1 >= 0) {
- const QQuickItem *leftHandle = m_handleItems.at(m_pressedHandleIndex - 1);
- leftEdge = horizontal
- ? leftHandle->x() + leftHandle->width()
- : leftHandle->y() + leftHandle->height();
+ for (int i = m_pressedHandleIndex - 1; i >= 0; --i) {
+ const QQuickItem *nextHandleToTheLeft = m_handleItems.at(i);
+ if (nextHandleToTheLeft->isVisible()) {
+ leftEdge = horizontal
+ ? nextHandleToTheLeft->x() + nextHandleToTheLeft->width()
+ : nextHandleToTheLeft->y() + nextHandleToTheLeft->height();
+ break;
+ }
}
// The mouse can be clicked anywhere in the handle, and if we don't account for
@@ -344,35 +351,9 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
const qreal newItemSize = newHandlePos - leftEdge;
- // Modify the preferredWidth, otherwise the original implicitWidth/preferredWidth
- // will be used on the next layout (when it's no longer being resized).
- if (!attached) {
- // Force the attached object to be created since we rely on it.
- attached = qobject_cast<QQuickSplitViewAttached*>(
- qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
- }
-
- /*
- Users could conceivably respond to size changes in items by setting attached
- SplitView properties:
-
- onWidthChanged: if (width < 10) secondItem.SplitView.preferredWidth = 100
-
- We handle this by doing another layout after the current layout if the
- attached/implicit size properties are set during this layout. However, we also
- need to set preferredWidth/Height here (for reasons mentioned in the comment above),
- but we don't want this to count as a request for a delayed layout, so we guard against it.
- */
- m_ignoreNextLayoutRequest = true;
-
- if (horizontal)
- attached->setPreferredWidth(newItemSize);
- else
- attached->setPreferredHeight(newItemSize);
-
- // We still need to use requestedWidth in the setWidth() call below,
+ // We still need to use requestedSize in the width/height call below,
// because sizeData has already been calculated and now contains an old
- // effectivePreferredWidth value.
+ // effectivePreferredWidth/Height value.
requestedSize = newItemSize;
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized (dragged) " << item
@@ -405,22 +386,7 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
const qreal newItemSize = rightEdge - (newHandlePos + pressedHandleSize);
- // Modify the preferredWidth, otherwise the original implicitWidth/preferredWidth
- // will be used on the next layout (when it's no longer being resized).
- if (!attached) {
- // Force the attached object to be created since we rely on it.
- attached = qobject_cast<QQuickSplitViewAttached*>(
- qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
- }
-
- m_ignoreNextLayoutRequest = true;
-
- if (horizontal)
- attached->setPreferredWidth(newItemSize);
- else
- attached->setPreferredHeight(newItemSize);
-
- // We still need to use requestedSize in the setWidth()/setHeight() call below,
+ // We still need to use requestedSize in the width/height call below,
// because sizeData has already been calculated and now contains an old
// effectivePreferredWidth/Height value.
requestedSize = newItemSize;
@@ -443,34 +409,43 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
}
if (index != m_fillIndex) {
+ LayoutData layoutData;
if (horizontal) {
- item->setWidth(qBound(
- sizeData.effectiveMinimumWidth,
- requestedSize,
- sizeData.effectiveMaximumWidth));
- item->setHeight(height);
+ layoutData.width = qBound(
+ sizeData.effectiveMinimumWidth,
+ requestedSize,
+ sizeData.effectiveMaximumWidth);
+ layoutData.height = height;
} else {
- item->setWidth(width);
- item->setHeight(qBound(
- sizeData.effectiveMinimumHeight,
- requestedSize,
- sizeData.effectiveMaximumHeight));
+ layoutData.width = width;
+ layoutData.height = qBound(
+ sizeData.effectiveMinimumHeight,
+ requestedSize,
+ sizeData.effectiveMaximumHeight);
}
- qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized split item " << item
- << " (effective"
- << " minW=" << sizeData.effectiveMinimumWidth
- << ", minH=" << sizeData.effectiveMinimumHeight
- << ", prfW=" << sizeData.effectivePreferredWidth
- << ", prfH=" << sizeData.effectivePreferredHeight
- << ", maxW=" << sizeData.effectiveMaximumWidth
- << ", maxH=" << sizeData.effectiveMaximumHeight << ")";
+ // Mark that this item has been manually resized. After this
+ // we can override the preferredWidth & preferredHeight
+ if (isBeingResized)
+ layoutData.wasResizedByHandle = true;
+
+ m_layoutData.insert(item, layoutData);
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": calculated the following size data for split item " << item
+ << ": eminW=" << sizeData.effectiveMinimumWidth
+ << ", eminH=" << sizeData.effectiveMinimumHeight
+ << ", eprfW=" << sizeData.effectivePreferredWidth
+ << ", eprfH=" << sizeData.effectivePreferredHeight
+ << ", emaxW=" << sizeData.effectiveMaximumWidth
+ << ", emaxH=" << sizeData.effectiveMaximumHeight
+ << ", w=" << layoutData.width
+ << ", h=" << layoutData.height << "";
// Keep track of how much space has been used so far.
if (horizontal)
- usedWidth += item->width();
+ usedWidth += layoutData.width;
else
- usedHeight += item->height();
+ usedHeight += layoutData.height;
} else if (indexBeingResizedDueToDrag != m_fillIndex) {
// The fill item is resized afterwards, outside of the loop.
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": skipping fill item as we resize it last";
@@ -526,20 +501,26 @@ void QQuickSplitViewPrivate::layoutResizeFillItem(QQuickItem *fillItem,
const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
qmlAttachedPropertiesObject<QQuickSplitView>(fillItem, false));
const auto fillSizeData = effectiveSizeData(fillItemPrivate, attached);
+
+ LayoutData layoutData;
if (isHorizontal()) {
- fillItem->setWidth(qBound(
- fillSizeData.effectiveMinimumWidth,
- width - usedWidth,
- fillSizeData.effectiveMaximumWidth));
- fillItem->setHeight(height);
+ layoutData.width = qBound(
+ fillSizeData.effectiveMinimumWidth,
+ width - usedWidth,
+ fillSizeData.effectiveMaximumWidth);
+ layoutData.height = height;
+ usedWidth += layoutData.width;
} else {
- fillItem->setWidth(width);
- fillItem->setHeight(qBound(
- fillSizeData.effectiveMinimumHeight,
- height - usedHeight,
- fillSizeData.effectiveMaximumHeight));
+ layoutData.width = width;
+ layoutData.height = qBound(
+ fillSizeData.effectiveMinimumHeight,
+ height - usedHeight,
+ fillSizeData.effectiveMaximumHeight);
+ usedHeight += layoutData.height;
}
+ m_layoutData.insert(fillItem, layoutData);
+
qCDebug(qlcQQuickSplitView).nospace() << " - " << m_fillIndex
<< ": resized split fill item " << fillItem << " (effective"
<< " minW=" << fillSizeData.effectiveMinimumWidth
@@ -549,6 +530,100 @@ void QQuickSplitViewPrivate::layoutResizeFillItem(QQuickItem *fillItem,
}
/*
+ Limit the sizes if needed and apply them into items.
+*/
+void QQuickSplitViewPrivate::limitAndApplySizes(qreal usedWidth, qreal usedHeight)
+{
+ const int count = contentModel->count();
+ const bool horizontal = isHorizontal();
+
+ const qreal maxSize = horizontal ? width : height;
+ const qreal usedSize = horizontal ? usedWidth : usedHeight;
+ if (usedSize > maxSize) {
+ qCDebug(qlcQQuickSplitView).nospace() << "usedSize " << usedSize << " is greater than maxSize "
+ << maxSize << "; reducing size of non-filled items from right to left / bottom to top";
+
+ // If items don't fit, reduce the size of non-filled items from
+ // right to left / bottom to top. At this point filled item is
+ // already at its minimum size or usedSize wouldn't be > maxSize.
+ qreal delta = usedSize - maxSize;
+ for (int index = count - 1; index >= 0; --index) {
+ if (index == m_fillIndex)
+ continue;
+ QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(index));
+ if (!item->isVisible())
+ continue;
+
+ const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ const auto sizeData = effectiveSizeData(itemPrivate, attached);
+ const qreal maxReduce = horizontal ?
+ m_layoutData[item].width - sizeData.effectiveMinimumWidth :
+ m_layoutData[item].height - sizeData.effectiveMinimumHeight;
+
+ const qreal reduce = std::min(maxReduce, delta);
+ if (horizontal)
+ m_layoutData[item].width -= reduce;
+ else
+ m_layoutData[item].height -= reduce;
+
+ delta -= reduce;
+ if (delta <= 0) {
+ // Now all the items fit, so continue
+ break;
+ }
+ }
+ }
+
+ qCDebug(qlcQQuickSplitView).nospace() << " applying new sizes to " << count << " items (excluding hidden items)";
+
+ // Apply the new sizes into items
+ for (int index = 0; index < count; ++index) {
+ QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(index));
+ if (!item->isVisible())
+ continue;
+
+ QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ LayoutData layoutData = m_layoutData.value(item);
+ if (layoutData.wasResizedByHandle) {
+ // Modify the preferredWidth/Height, otherwise the original implicit/preferred size
+ // will be used on the next layout (when it's no longer being resized).
+ if (!attached) {
+ // Force the attached object to be created since we rely on it.
+ attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
+ }
+ /*
+ Users could conceivably respond to size changes in items by setting attached
+ SplitView properties:
+
+ onWidthChanged: if (width < 10) secondItem.SplitView.preferredWidth = 100
+
+ We handle this by doing another layout after the current layout if the
+ attached/implicit size properties are set during this layout. However, we also
+ need to set preferredWidth/Height here, otherwise the original implicit/preferred sizes
+ will be used on the next layout (when it's no longer being resized).
+ But we don't want this to count as a request for a delayed layout, so we guard against it.
+ */
+ m_ignoreNextLayoutRequest = true;
+ if (horizontal)
+ attached->setPreferredWidth(layoutData.width);
+ else
+ attached->setPreferredHeight(layoutData.height);
+ }
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized item " << item << " from "
+ << item->width() << "x" << item->height() << " to "
+ << layoutData.width << "x" << layoutData.height;
+
+ item->setWidth(layoutData.width);
+ item->setHeight(layoutData.height);
+ }
+}
+
+/*
Positions items by laying them out in a row or column.
Items that aren't visible are skipped.
@@ -621,6 +696,19 @@ void QQuickSplitViewPrivate::requestLayout()
q->polish();
}
+/*
+ Layout steps are (horizontal SplitView as an example):
+ 1) layoutResizeSplitItems: Gives each non-filled item its preferredWidth
+ or if not set, implicitWidth. Sizes are kept between effectiveMinimumWidth
+ and effectiveMaximumWidth and stored into layoutData for now.
+ 2) layoutResizeFillItem: Gives filled item all the remaining space. Size is
+ kept between effectiveMinimumWidth and effectiveMaximumWidth and stored
+ into layoutData for now.
+ 3) limitAndApplySizes: If we have used more space than SplitView item has,
+ start reducing non-filled item sizes from right-to-left. Reduce them up
+ to minimumWidth or until SplitView item width is reached. Finally set the
+ new item sizes from layoutData.
+*/
void QQuickSplitViewPrivate::layout()
{
if (!componentComplete)
@@ -648,13 +736,15 @@ void QQuickSplitViewPrivate::layout()
qCDebug(qlcQQuickSplitView) << "laying out" << count << "split items"
<< (horizontal ? "horizontally" : "vertically") << "in SplitView" << q_func();
+ // Total sizes of items used during the layout operation.
qreal usedWidth = 0;
qreal usedHeight = 0;
int indexBeingResizedDueToDrag = -1;
+ m_layoutData.clear();
qCDebug(qlcQQuickSplitView) << " resizing:";
- // First, resize the items. We need to do this first because otherwise fill
+ // First, resize the non-filled items. We need to do this first because otherwise fill
// items would take up all of the remaining space as soon as they are encountered.
layoutResizeSplitItems(usedWidth, usedHeight, indexBeingResizedDueToDrag);
@@ -666,6 +756,9 @@ void QQuickSplitViewPrivate::layout()
QQuickItem *fillItem = qobject_cast<QQuickItem*>(contentModel->object(m_fillIndex));
layoutResizeFillItem(fillItem, usedWidth, usedHeight, indexBeingResizedDueToDrag);
+ // Reduce the sizes still if needed and apply them into items.
+ limitAndApplySizes(usedWidth, usedHeight);
+
qCDebug(qlcQQuickSplitView) << " positioning:";
// Position the items.
@@ -1129,13 +1222,19 @@ void QQuickSplitView::setOrientation(Qt::Orientation orientation)
return;
d->m_orientation = orientation;
- d->resizeHandles();
+
#if QT_CONFIG(cursor)
for (QQuickItem *handleItem : d->m_handleItems)
d->updateCursorHandle(handleItem);
#endif
- d->requestLayout();
emit orientationChanged();
+
+ // Do this after emitting orientationChanged so that the bindings in QML
+ // update the implicit size in time.
+ d->resizeHandles();
+ // This is queued (via polish) anyway, but to make our intentions clear,
+ // do it afterwards too.
+ d->requestLayout();
}
/*!
@@ -1359,7 +1458,6 @@ void QQuickSplitView::componentComplete()
{
Q_D(QQuickSplitView);
QQuickControl::componentComplete();
- d->resizeHandles();
d->updateFillIndex();
d->updatePolish();
}
@@ -1912,9 +2010,10 @@ void QQuickSplitViewAttached::resetMaximumHeight()
This attached property controls whether the item takes the remaining space
in the split view after all other items have been laid out.
- By default, the last visible child of the split view will have this set,
+ By default, the last visible child of the split view will fill the view,
but it can be changed by explicitly setting \c fillWidth to \c true on
- another item.
+ another item. If multiple items have \c fillWidth set to \c true, the
+ left-most item will fill the view.
The width of a split item with \c fillWidth set to \c true is still
restricted within its \l minimumWidth and \l maximumWidth.
@@ -1947,9 +2046,10 @@ void QQuickSplitViewAttached::setFillWidth(bool fill)
This attached property controls whether the item takes the remaining space
in the split view after all other items have been laid out.
- By default, the last visible child of the split view will have this set,
+ By default, the last visible child of the split view will fill the view,
but it can be changed by explicitly setting \c fillHeight to \c true on
- another item.
+ another item. If multiple items have \c fillHeight set to \c true, the
+ top-most item will fill the view.
The height of a split item with \c fillHeight set to \c true is still
restricted within its \l minimumHeight and \l maximumHeight.
diff --git a/src/quicktemplates/qquicksplitview_p.h b/src/quicktemplates/qquicksplitview_p.h
index 12bba3a856..0828142a4e 100644
--- a/src/quicktemplates/qquicksplitview_p.h
+++ b/src/quicktemplates/qquicksplitview_p.h
@@ -18,6 +18,8 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
#include <QtQml/qqmllist.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickSplitViewPrivate;
@@ -26,7 +28,7 @@ class QQuickSplitViewAttachedPrivate;
class QQuickSplitHandleAttached;
class QQuickSplitHandleAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitView : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickSplitView : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL)
@@ -86,7 +88,7 @@ private:
Q_DECLARE_PRIVATE(QQuickSplitView)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitViewAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSplitViewAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickSplitView *view READ view NOTIFY viewChanged FINAL)
@@ -156,7 +158,7 @@ private:
Q_DECLARE_PRIVATE(QQuickSplitViewAttached)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitHandleAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSplitHandleAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged FINAL)
@@ -185,8 +187,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSplitView)
-
-QML_DECLARE_TYPE(QQuickSplitHandleAttached)
-
#endif // QQUICKSPLITVIEW_P_H
diff --git a/src/quicktemplates/qquicksplitview_p_p.h b/src/quicktemplates/qquicksplitview_p_p.h
index 86316ecfe9..d2b0c35668 100644
--- a/src/quicktemplates/qquicksplitview_p_p.h
+++ b/src/quicktemplates/qquicksplitview_p_p.h
@@ -23,7 +23,7 @@ class QQuickSplitView;
class QQuickSplitViewAttached;
class QQuickSplitHandleAttached;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitViewPrivate : public QQuickContainerPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickSplitViewPrivate : public QQuickContainerPrivate
{
Q_DECLARE_PUBLIC(QQuickSplitView)
@@ -31,6 +31,7 @@ public:
void updateFillIndex();
void layoutResizeSplitItems(qreal &usedWidth, qreal &usedHeight, int &indexBeingResizedDueToDrag);
void layoutResizeFillItem(QQuickItem *fillItem, qreal &usedWidth, qreal &usedHeight, int indexBeingResizedDueToDrag);
+ void limitAndApplySizes(qreal usedWidth, qreal usedHeight);
void layoutPositionItems(const QQuickItem *fillItem);
void requestLayout();
void layout();
@@ -59,6 +60,13 @@ public:
qreal effectiveMaximumHeight;
};
+ // Used during the layout
+ struct LayoutData {
+ qreal width = 0;
+ qreal height = 0;
+ bool wasResizedByHandle = false;
+ };
+
EffectiveSizeData effectiveSizeData(const QQuickItemPrivate *itemPrivate,
const QQuickSplitViewAttached *attached) const;
@@ -80,6 +88,7 @@ public:
Qt::Orientation m_orientation = Qt::Horizontal;
QQmlComponent *m_handle = nullptr;
QList<QQuickItem*> m_handleItems;
+ QHash<QQuickItem*, LayoutData> m_layoutData;
int m_hoveredHandleIndex = -1;
int m_pressedHandleIndex = -1;
int m_nextVisibleIndexAfterPressedHandle = -1;
diff --git a/src/quicktemplates/qquickstackelement.cpp b/src/quicktemplates/qquickstackelement.cpp
index f8001a2980..f6a5ffdd74 100644
--- a/src/quicktemplates/qquickstackelement.cpp
+++ b/src/quicktemplates/qquickstackelement.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickstackelement_p_p.h"
+#include "qquickstackview_p.h"
#include "qquickstackview_p_p.h"
#include <QtQml/qqmlinfo.h>
@@ -15,6 +16,7 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(quick_viewtransitions)
static QQuickStackViewAttached *attachedStackObject(QQuickStackElement *element)
{
QQuickStackViewAttached *attached = qobject_cast<QQuickStackViewAttached *>(qmlAttachedPropertiesObject<QQuickStackView>(element->item, false));
@@ -22,6 +24,7 @@ static QQuickStackViewAttached *attachedStackObject(QQuickStackElement *element)
QQuickStackViewAttachedPrivate::get(attached)->element = element;
return attached;
}
+#endif
class QQuickStackIncubator : public QQmlIncubator
{
@@ -44,18 +47,23 @@ private:
};
QQuickStackElement::QQuickStackElement()
+#if QT_CONFIG(quick_viewtransitions)
: QQuickItemViewTransitionableItem(nullptr)
+#endif
{
}
QQuickStackElement::~QQuickStackElement()
{
+#if QT_CONFIG(quick_viewtransitions)
if (item)
QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed);
+#endif
if (ownComponent)
delete component;
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
if (item) {
if (ownItem) {
@@ -79,6 +87,7 @@ QQuickStackElement::~QQuickStackElement()
if (attached)
emit attached->removed();
+#endif
}
QQuickStackElement *QQuickStackElement::fromString(const QString &str, QQuickStackView *view, QString *error)
@@ -110,9 +119,36 @@ QQuickStackElement *QQuickStackElement::fromObject(QObject *object, QQuickStackV
QQuickStackElement *element = new QQuickStackElement;
element->component = qobject_cast<QQmlComponent *>(object);
+#if QT_CONFIG(quick_viewtransitions)
element->item = qobject_cast<QQuickItem *>(object);
if (element->item)
element->originalParent = element->item->parentItem();
+#endif
+ return element;
+}
+
+QQuickStackElement *QQuickStackElement::fromStackViewArg(QQuickStackView *view, QQuickStackViewArg arg)
+{
+ QQuickStackElement *element = new QQuickStackElement;
+#if QT_CONFIG(quick_viewtransitions)
+ element->item = arg.mItem;
+ if (element->item) {
+ element->originalParent = element->item->parentItem();
+
+ Q_ASSERT(!arg.mComponent);
+ Q_ASSERT(!arg.mUrl.isValid());
+ } else
+#endif
+ if (arg.mComponent) {
+ element->component = arg.mComponent;
+
+ Q_ASSERT(!arg.mUrl.isValid());
+ } else if (arg.mUrl.isValid()) {
+ element->component = new QQmlComponent(qmlEngine(view), arg.mUrl, view);
+ element->ownComponent = true;
+ } else {
+ qFatal("No Item, Component or URL set on arg passed to fromStrictArg");
+ }
return element;
}
@@ -177,7 +213,9 @@ void QQuickStackElement::initialize(RequiredProperties *requiredProperties)
QV4::ScopedValue ipv(scope, properties.value());
QV4::Scoped<QV4::QmlContext> qmlContext(scope, qmlCallingContext.value());
QV4::ScopedValue qmlObject(scope, QV4::QObjectWrapper::wrap(v4, item));
- QQmlComponentPrivate::setInitialProperties(v4, qmlContext, qmlObject, ipv, requiredProperties, item);
+ QQmlComponentPrivate::setInitialProperties(
+ v4, qmlContext, qmlObject, ipv, requiredProperties, item,
+ component ? QQmlComponentPrivate::get(component)->state.creator() : nullptr);
properties.clear();
}
@@ -202,9 +240,11 @@ void QQuickStackElement::setIndex(int value)
return;
index = value;
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
if (attached)
emit attached->indexChanged();
+#endif
}
void QQuickStackElement::setView(QQuickStackView *value)
@@ -213,9 +253,11 @@ void QQuickStackElement::setView(QQuickStackView *value)
return;
view = value;
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
if (attached)
emit attached->viewChanged();
+#endif
}
void QQuickStackElement::setStatus(QQuickStackView::Status value)
@@ -224,6 +266,7 @@ void QQuickStackElement::setStatus(QQuickStackView::Status value)
return;
status = value;
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
if (!attached)
return;
@@ -247,17 +290,25 @@ void QQuickStackElement::setStatus(QQuickStackView::Status value)
}
emit attached->statusChanged();
+#endif
}
void QQuickStackElement::setVisible(bool visible)
{
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
- if (!item || (attached && QQuickStackViewAttachedPrivate::get(attached)->explicitVisible))
+#endif
+ if (!item
+#if QT_CONFIG(quick_viewtransitions)
+ || (attached && QQuickStackViewAttachedPrivate::get(attached)->explicitVisible)
+#endif
+ )
return;
item->setVisible(visible);
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickStackElement::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
{
if (transitioner)
@@ -294,10 +345,13 @@ void QQuickStackElement::completeTransition(QQuickTransition *quickTransition)
{
QQuickItemViewTransitionableItem::completeTransition(quickTransition);
}
+#endif
void QQuickStackElement::itemDestroyed(QQuickItem *)
{
+#if QT_CONFIG(quick_viewtransitions)
item = nullptr;
+#endif
}
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickstackelement_p_p.h b/src/quicktemplates/qquickstackelement_p_p.h
index 00b150805d..5af8149d91 100644
--- a/src/quicktemplates/qquickstackelement_p_p.h
+++ b/src/quicktemplates/qquickstackelement_p_p.h
@@ -17,10 +17,14 @@
#include <QtQuickTemplates2/private/qquickstackview_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
+#if QT_CONFIG(quick_viewtransitions)
#include <QtQuick/private/qquickitemviewtransition_p.h>
+#endif
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQml/private/qv4persistent_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlContext;
@@ -28,7 +32,11 @@ class QQmlComponent;
struct QQuickStackTransition;
class RequiredProperties;
-class QQuickStackElement : public QQuickItemViewTransitionableItem, public QQuickItemChangeListener
+class QQuickStackElement :
+#if QT_CONFIG(quick_viewtransitions)
+ public QQuickItemViewTransitionableItem,
+#endif
+ public QQuickItemChangeListener
{
QQuickStackElement();
@@ -37,6 +45,7 @@ public:
static QQuickStackElement *fromString(const QString &str, QQuickStackView *view, QString *error);
static QQuickStackElement *fromObject(QObject *object, QQuickStackView *view, QString *error);
+ static QQuickStackElement *fromStackViewArg(QQuickStackView *view, QQuickStackViewArg arg);
bool load(QQuickStackView *parent);
void incubate(QObject *object, RequiredProperties *requiredProperties);
@@ -47,10 +56,12 @@ public:
void setStatus(QQuickStackView::Status status);
void setVisible(bool visible);
+#if QT_CONFIG(quick_viewtransitions)
void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
void startTransition(QQuickItemViewTransitioner *transitioner, QQuickStackView::Status status);
void completeTransition(QQuickTransition *quickTransition);
+#endif
void itemDestroyed(QQuickItem *item) override;
@@ -67,6 +78,9 @@ public:
QQuickStackView::Status status = QQuickStackView::Inactive;
QV4::PersistentValue properties;
QV4::PersistentValue qmlCallingContext;
+#if !QT_CONFIG(quick_viewtransitions)
+ QQuickItem *item;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickstacktransition_p_p.h b/src/quicktemplates/qquickstacktransition_p_p.h
index e68f1bb7a1..d42e1981c0 100644
--- a/src/quicktemplates/qquickstacktransition_p_p.h
+++ b/src/quicktemplates/qquickstacktransition_p_p.h
@@ -15,6 +15,10 @@
// We mean it.
//
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_viewtransitions);
+
#include <QtQuickTemplates2/private/qquickstackview_p.h>
#include <QtQuick/private/qquickitemviewtransition_p.h>
diff --git a/src/quicktemplates/qquickstackview.cpp b/src/quicktemplates/qquickstackview.cpp
index d09f55f6a4..2e0109863a 100644
--- a/src/quicktemplates/qquickstackview.cpp
+++ b/src/quicktemplates/qquickstackview.cpp
@@ -4,7 +4,9 @@
#include "qquickstackview_p.h"
#include "qquickstackview_p_p.h"
#include "qquickstackelement_p_p.h"
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickstacktransition_p_p.h"
+#endif
#include <QtCore/qscopedvaluerollback.h>
#include <QtQml/qjsvalue.h>
@@ -16,6 +18,39 @@
QT_BEGIN_NAMESPACE
+QQuickStackViewArg::QQuickStackViewArg(QQuickItem *item)
+ : mItem(item)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(const QUrl &url)
+ : mUrl(url)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(QQmlComponent *component)
+ : mComponent(component)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(const QVariantMap &properties)
+ : mProperties(properties)
+{
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickStackViewArg &arg)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "QQuickStackViewArg("
+ << "mItem=" << arg.mItem
+ << " mComponent=" << arg.mComponent
+ << " mUrl=" << arg.mUrl
+ << ")";
+ return debug;
+}
+#endif
+
/*!
\qmltype StackView
\inherits Control
@@ -349,15 +384,20 @@ QQuickStackView::QQuickStackView(QQuickItem *parent)
: QQuickControl(*(new QQuickStackViewPrivate), parent)
{
setFlag(ItemIsFocusScope);
+
+ Q_D(QQuickStackView);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
QQuickStackView::~QQuickStackView()
{
Q_D(QQuickStackView);
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
d->transitioner->setChangeListener(nullptr);
delete d->transitioner;
}
+#endif
qDeleteAll(d->removing);
qDeleteAll(d->removed);
qDeleteAll(d->elements);
@@ -517,13 +557,17 @@ QQuickItem *QQuickStackView::find(const QJSValue &callback, LoadBehavior behavio
\value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
\value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
- If no operation is provided, \c PushTransition will be used.
+ If no operation is provided, \c Immediate will be used if the stack is
+ empty, and \c PushTransition otherwise.
\note Items that already exist in the stack are not pushed.
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l pushItem or \l pushItems functions instead.
+
\sa initialItem, {Pushing Items}
*/
-void QQuickStackView::push(QQmlV4Function *args)
+void QQuickStackView::push(QQmlV4FunctionPtr args)
{
Q_D(QQuickStackView);
const QString operationName = QStringLiteral("push");
@@ -543,21 +587,23 @@ void QQuickStackView::push(QQmlV4Function *args)
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
+#if QT_CONFIG(quick_viewtransitions)
Operation operation = d->elements.isEmpty() ? Immediate : PushTransition;
QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
if (lastArg->isInt32())
operation = static_cast<Operation>(lastArg->toInt32());
+#endif
QStringList errors;
QList<QQuickStackElement *> elements = d->parseElements(0, args, &errors);
// Remove any items that are already in the stack, as they can't be in two places at once.
- for (int i = 0; i < elements.size(); ) {
- QQuickStackElement *element = elements.at(i);
- if (element->item && d->findElement(element->item))
- elements.removeAt(i);
- else
- ++i;
- }
+ // not using erase_if, as we have to delete the elements first
+ auto removeIt = std::remove_if(elements.begin(), elements.end(), [&](QQuickStackElement *element) {
+ return element->item && d->findElement(element->item);
+ });
+ for (auto it = removeIt, end = elements.end(); it != end; ++it)
+ delete *it;
+ elements.erase(removeIt, elements.end());
if (!errors.isEmpty() || elements.isEmpty()) {
if (!errors.isEmpty()) {
@@ -570,17 +616,21 @@ void QQuickStackView::push(QQmlV4Function *args)
return;
}
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackElement *exit = nullptr;
if (!d->elements.isEmpty())
exit = d->elements.top();
+#endif
int oldDepth = d->elements.size();
if (d->pushElements(elements)) {
d->depthChange(d->elements.size(), oldDepth);
QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
d->startTransition(QQuickStackTransition::pushEnter(operation, enter, this),
QQuickStackTransition::pushExit(operation, exit, this),
operation == Immediate);
+#endif
d->setCurrentItem(enter);
}
@@ -625,9 +675,13 @@ void QQuickStackView::push(QQmlV4Function *args)
stackView.pop(null)
\endcode
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l popToItem, \l popToIndex or \l popCurrentItem functions
+ instead.
+
\sa clear(), {Popping Items}, {Unwinding Items via Pop}
*/
-void QQuickStackView::pop(QQmlV4Function *args)
+void QQuickStackView::pop(QQmlV4FunctionPtr args)
{
Q_D(QQuickStackView);
const QString operationName = QStringLiteral("pop");
@@ -663,7 +717,7 @@ void QQuickStackView::pop(QQmlV4Function *args)
enter = d->findElement(item);
if (!enter) {
if (item != d->currentItem)
- d->warn(QStringLiteral("unknown argument: ") + value->toQString()); // TODO: safe?
+ d->warn(QStringLiteral("can't find item to pop: ") + value->toQString());
args->setReturnValue(QV4::Encode::null());
d->elements.push(exit); // restore
return;
@@ -671,14 +725,16 @@ void QQuickStackView::pop(QQmlV4Function *args)
}
}
+#if QT_CONFIG(quick_viewtransitions)
Operation operation = PopTransition;
if (argc > 0) {
QV4::ScopedValue lastArg(scope, (*args)[argc - 1]);
if (lastArg->isInt32())
operation = static_cast<Operation>(lastArg->toInt32());
}
+#endif
- QQuickItem *previousItem = nullptr;
+ QPointer<QQuickItem> previousItem;
if (d->popElements(enter)) {
if (exit) {
@@ -687,9 +743,11 @@ void QQuickStackView::pop(QQmlV4Function *args)
previousItem = exit->item;
}
d->depthChange(d->elements.size(), oldDepth);
+#if QT_CONFIG(quick_viewtransitions)
d->startTransition(QQuickStackTransition::popExit(operation, exit, this),
QQuickStackTransition::popEnter(operation, enter, this),
operation == Immediate);
+#endif
d->setCurrentItem(enter);
}
@@ -759,7 +817,8 @@ void QQuickStackView::pop(QQmlV4Function *args)
\value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
\value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
- If no operation is provided, \c ReplaceTransition will be used.
+ If no operation is provided, \c Immediate will be used if the stack is
+ empty, and \c ReplaceTransition otherwise.
The following example illustrates the use of push and pop transitions with replace().
@@ -789,9 +848,12 @@ void QQuickStackView::pop(QQmlV4Function *args)
}
\endcode
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l replaceCurrentItem functions instead.
+
\sa push(), {Replacing Items}
*/
-void QQuickStackView::replace(QQmlV4Function *args)
+void QQuickStackView::replace(QQmlV4FunctionPtr args)
{
Q_D(QQuickStackView);
const QString operationName = QStringLiteral("replace");
@@ -812,10 +874,12 @@ void QQuickStackView::replace(QQmlV4Function *args)
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
+#if QT_CONFIG(quick_viewtransitions)
Operation operation = d->elements.isEmpty() ? Immediate : ReplaceTransition;
QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
if (lastArg->isInt32())
operation = static_cast<Operation>(lastArg->toInt32());
+#endif
QQuickStackElement *target = nullptr;
QV4::ScopedValue firstArg(scope, (*args)[0]);
@@ -849,9 +913,11 @@ void QQuickStackView::replace(QQmlV4Function *args)
d->removing.insert(exit);
}
QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
d->startTransition(QQuickStackTransition::replaceExit(operation, exit, this),
QQuickStackTransition::replaceEnter(operation, enter, this),
operation == Immediate);
+#endif
d->setCurrentItem(enter);
}
@@ -864,6 +930,422 @@ void QQuickStackView::replace(QQmlV4Function *args)
}
/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItems(items, operation)
+ \since 6.7
+
+ Pushes \a items onto the stack using an optional \a operation, and
+ optionally applies a set of properties on each element. \a items is an array
+ of elements. Each element can be
+ an \l Item, \l Component, or \l [QML] url and can be followed by an optional
+ properties argument (see below). Returns the item that became
+ current (the last item).
+
+ StackView creates an instance automatically if the pushed element is a
+ \l Component or \l [QML] url, and the instance will be destroyed when it is
+ popped off the stack. See \l {Item Ownership} for more information.
+
+ \include qquickstackview.qdocinc optional-properties-after-each-item
+
+ \code
+ stackView.push([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
+
+ // With properties:
+ stackView.pushItems([
+ item, { "color": "red" },
+ rectComponent, { "color": "green" },
+ Qt.resolvedUrl("MyItem.qml"), { "color": "blue" }
+ ])
+
+ // With properties for only some items:
+ stackView.pushItems([
+ item, { "color": "yellow" },
+ rectComponent
+ ])
+ \endcode
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push a single item, use the relevant \c pushItem function:
+ \list
+ \li \l {stackview-pushitem-item-overload}
+ {pushItem}(item, properties, operation)
+ \li \l {stackview-pushitem-component-overload}
+ {pushItem}(component, properties, operation)
+ \li \l {stackview-pushitem-url-overload}
+ {pushItem}(url, properties, operation)
+ \endlist
+
+ \note Items that already exist in the stack are not pushed.
+
+ \sa initialItem, pushItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItems(QList<QQuickStackViewArg> args, Operation operation)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("pushItem");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ return nullptr;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+
+ const QList<QQuickStackElement *> stackElements = d->parseElements(args);
+
+#if QT_CONFIG(quick_viewtransitions)
+ QQuickStackElement *exit = nullptr;
+ if (!d->elements.isEmpty())
+ exit = d->elements.top();
+#endif
+
+ const int oldDepth = d->elements.size();
+ if (d->pushElements(stackElements)) {
+ d->depthChange(d->elements.size(), oldDepth);
+ QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::pushEnter(operation, enter, this),
+ QQuickStackTransition::pushExit(operation, exit, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ return d->currentItem;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(item, properties, operation)
+ \keyword stackview-pushitem-item-overload
+ \since 6.7
+
+ Pushes an \a item onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(QQuickItem *item, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ item, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(component, properties, operation)
+ \overload pushItem()
+ \keyword stackview-pushitem-component-overload
+ \since 6.7
+
+ Pushes a \a component onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(QQmlComponent *component, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ component, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(url, properties, operation)
+ \overload pushItem()
+ \keyword stackview-pushitem-url-overload
+ \since 6.7
+
+ Pushes a \a url onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(const QUrl &url, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ url, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popToItem(item, operation)
+ \since 6.7
+
+ Pops all items down to (but not including) \a item. Returns the last item
+ removed from the stack.
+
+ If \a item is \c null, a warning is produced and \c null is returned.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ \code
+ stackView.popToItem(someItem, StackView.Immediate)
+ \endcode
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popToItem(QQuickItem *item, Operation operation)
+{
+ Q_D(QQuickStackView);
+ return d->popToItem(item, operation, QQuickStackViewPrivate::CurrentItemPolicy::DoNotPop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popToIndex(index, operation)
+ \since 6.7
+
+ Pops all items down to (but not including) \a index. Returns the last item
+ removed from the stack.
+
+ If \a index is out of bounds, a warning is produced and \c null is
+ returned.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ \code
+ stackView.popToIndex(stackView.depth - 2, StackView.Immediate)
+ \endcode
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popToIndex(int index, Operation operation)
+{
+ Q_D(QQuickStackView);
+ if (index < 0 || index >= d->elements.size()) {
+ d->warn(QString::fromLatin1("popToIndex: index %1 is out of bounds (%2 item(s))")
+ .arg(index).arg(d->elements.size()));
+ return nullptr;
+ }
+
+ if (index == d->elements.size() - 1) {
+ // This would pop down to the current item, which is a no-op.
+ return nullptr;
+ }
+
+ QQuickStackElement *element = d->elements.at(index);
+ element->load(this);
+ return d->popToItem(element->item, operation, QQuickStackViewPrivate::CurrentItemPolicy::Pop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popCurrentItem(operation)
+ \since 6.7
+
+ Pops \l currentItem from the stack. Returns the last item removed from the
+ stack, or \c null if \l depth was \c 1.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ This function is equivalent to \c popToIndex(stackView.currentIndex - 1).
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popCurrentItem(Operation operation)
+{
+ Q_D(QQuickStackView);
+ if (d->elements.size() == 1) {
+ auto lastItemRemoved = d->elements.last()->item;
+ clear(operation);
+ return lastItemRemoved;
+ }
+ return d->popToItem(d->currentItem, operation, QQuickStackViewPrivate::CurrentItemPolicy::Pop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(items, operation)
+ \keyword stackview-replacecurrentitem-items-overload
+ \since 6.7
+
+ Pops \l currentItem from the stack and pushes \a items. If the optional
+ \a operation is specified, the relevant transition will be used. Each item
+ can be followed by an optional set of properties that will be applied to
+ that item. Returns the item that became current.
+
+ \include qquickstackview.qdocinc optional-properties-after-each-item
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ \code
+ stackView.replaceCurrentItem([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
+
+ // With properties:
+ stackView.replaceCurrentItem([
+ item, { "color": "red" },
+ rectComponent, { "color": "green" },
+ Qt.resolvedUrl("MyItem.qml"), { "color": "blue" }
+ ])
+ \endcode
+
+ To push a single item, use the relevant overload:
+ \list
+ \li \l {stackview-replacecurrentitem-item-overload}
+ {replaceCurrentItem}(item, properties, operation)
+ \li \l {stackview-replacecurrentitem-component-overload}
+ {replaceCurrentItem}(component, properties, operation)
+ \li \l {stackview-replacecurrentitem-url-overload}
+ {replaceCurrentItem}(url, properties, operation)
+ \endlist
+
+ \sa push(), {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(const QList<QQuickStackViewArg> &args,
+ Operation operation)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("replace");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ return nullptr;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+
+ QQuickStackElement *currentElement = !d->elements.isEmpty() ? d->elements.top() : nullptr;
+
+ const QList<QQuickStackElement *> stackElements = d->parseElements(args);
+
+ int oldDepth = d->elements.size();
+ QQuickStackElement* exit = nullptr;
+ if (!d->elements.isEmpty())
+ exit = d->elements.pop();
+
+ const bool successfullyReplaced = exit != currentElement
+ ? d->replaceElements(currentElement, stackElements)
+ : d->pushElements(stackElements);
+ if (successfullyReplaced) {
+ d->depthChange(d->elements.size(), oldDepth);
+ if (exit) {
+ exit->removal = true;
+ d->removing.insert(exit);
+ }
+ QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::replaceExit(operation, exit, this),
+ QQuickStackTransition::replaceEnter(operation, enter, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ return d->currentItem;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(item, properties, operation)
+ \overload replaceCurrentItem()
+ \keyword stackview-replacecurrentitem-item-overload
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {item}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(QQuickItem *item, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(item), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(component, properties, operation)
+ \overload replaceCurrentItem()
+ \keyword stackview-replacecurrentitem-component-overload
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {component}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(QQmlComponent *component, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(component), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(url, properties, operation)
+ \keyword stackview-replacecurrentitem-url-overload
+ \overload replaceCurrentItem()
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {url}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(const QUrl &url, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(url), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
\since QtQuick.Controls 2.3 (Qt 5.10)
\qmlproperty bool QtQuick.Controls::StackView::empty
\readonly
@@ -894,6 +1376,9 @@ bool QQuickStackView::isEmpty() const
*/
void QQuickStackView::clear(Operation operation)
{
+#if !QT_CONFIG(quick_viewtransitions)
+ Q_UNUSED(operation)
+#endif
Q_D(QQuickStackView);
if (d->elements.isEmpty())
return;
@@ -904,8 +1389,11 @@ void QQuickStackView::clear(Operation operation)
return;
}
+ const int oldDepth = d->elements.size();
+
QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+#if QT_CONFIG(quick_viewtransitions)
if (operation != Immediate) {
QQuickStackElement *exit = d->elements.pop();
exit->removal = true;
@@ -913,8 +1401,8 @@ void QQuickStackView::clear(Operation operation)
d->startTransition(QQuickStackTransition::popExit(operation, exit, this),
QQuickStackTransition::popEnter(operation, nullptr, this), false);
}
+#endif
- int oldDepth = d->elements.size();
d->setCurrentItem(nullptr);
qDeleteAll(d->elements);
d->elements.clear();
@@ -945,6 +1433,7 @@ void QQuickStackView::setInitialItem(const QJSValue &item)
d->initialItem = item;
}
+#if QT_CONFIG(quick_viewtransitions)
/*!
\qmlproperty Transition QtQuick.Controls::StackView::popEnter
@@ -1106,6 +1595,7 @@ void QQuickStackView::setReplaceExit(QQuickTransition *exit)
d->transitioner->moveDisplacedTransition = exit;
emit replaceExitChanged();
}
+#endif
void QQuickStackView::componentComplete()
{
diff --git a/src/quicktemplates/qquickstackview_p.cpp b/src/quicktemplates/qquickstackview_p.cpp
index 99127d84b6..0288ff1f4b 100644
--- a/src/quicktemplates/qquickstackview_p.cpp
+++ b/src/quicktemplates/qquickstackview_p.cpp
@@ -3,8 +3,11 @@
#include "qquickstackview_p_p.h"
#include "qquickstackelement_p_p.h"
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickstacktransition_p_p.h"
+#endif
+#include <QtCore/qscopedvaluerollback.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmllist.h>
#include <QtQml/private/qv4qmlcontext_p.h>
@@ -46,7 +49,7 @@ void QQuickStackViewPrivate::setCurrentItem(QQuickStackElement *element)
emit q->currentItemChanged();
}
-static bool initProperties(QQuickStackElement *element, const QV4::Value &props, QQmlV4Function *args)
+static bool initProperties(QQuickStackElement *element, const QV4::Value &props, QQmlV4FunctionPtr args)
{
if (props.isObject()) {
const QV4::QObjectWrapper *wrapper = props.as<QV4::QObjectWrapper>();
@@ -60,7 +63,7 @@ static bool initProperties(QQuickStackElement *element, const QV4::Value &props,
return false;
}
-QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(int from, QQmlV4Function *args, QStringList *errors)
+QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(int from, QQmlV4FunctionPtr args, QStringList *errors)
{
QV4::ExecutionEngine *v4 = args->v4engine();
auto context = v4->callingQmlContext();
@@ -106,6 +109,47 @@ QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(int from, QQml
return elements;
}
+QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(const QList<QQuickStackViewArg> &args)
+{
+ Q_Q(QQuickStackView);
+ QList<QQuickStackElement *> stackElements;
+ for (int i = 0; i < args.size(); ++i) {
+ const QQuickStackViewArg &arg = args.at(i);
+ QVariantMap properties;
+ // Look ahead at the next arg in case it contains properties for this
+ // Item/Component/URL.
+ if (i < args.size() - 1) {
+ const QQuickStackViewArg &nextArg = args.at(i + 1);
+ // If mProperties isn't empty, the user passed properties.
+ // If it is empty, but mItem, mComponent and mUrl also are,
+ // then they passed an empty property map.
+ if (!nextArg.mProperties.isEmpty()
+ || (!nextArg.mItem && !nextArg.mComponent && !nextArg.mUrl.isValid())) {
+ properties = nextArg.mProperties;
+ ++i;
+ }
+ }
+
+ // Remove any items that are already in the stack, as they can't be in two places at once.
+ if (findElement(arg.mItem))
+ continue;
+
+ // We look ahead one index for each Item/Component/URL, so if this arg is
+ // a property map, the user has passed two or more in a row.
+ if (!arg.mProperties.isEmpty()) {
+ qmlWarning(q) << "Properties must come after an Item, Component or URL";
+ return {};
+ }
+
+ QQuickStackElement *element = QQuickStackElement::fromStackViewArg(q, arg);
+ QV4::ExecutionEngine *v4Engine = qmlEngine(q)->handle();
+ element->properties.set(v4Engine, v4Engine->fromVariant(properties));
+ element->qmlCallingContext.set(v4Engine, v4Engine->qmlContext());
+ stackElements.append(element);
+ }
+ return stackElements;
+}
+
QQuickStackElement *QQuickStackViewPrivate::findElement(QQuickItem *item) const
{
if (item) {
@@ -204,6 +248,80 @@ bool QQuickStackViewPrivate::replaceElements(QQuickStackElement *target, const Q
return pushElements(elems);
}
+QQuickItem *QQuickStackViewPrivate::popToItem(QQuickItem *item, QQuickStackView::Operation operation, CurrentItemPolicy currentItemPolicy)
+{
+ const QString operationName = QStringLiteral("pop");
+ if (modifyingElements) {
+ warnOfInterruption(operationName);
+ return nullptr;
+ }
+
+ QScopedValueRollback<bool> modifyingElementsRollback(modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(this->operation, operationName);
+ if (elements.isEmpty()) {
+ warn(QStringLiteral("no items to pop"));
+ return nullptr;
+ }
+
+ if (!item) {
+ warn(QStringLiteral("item cannot be null"));
+ return nullptr;
+ }
+
+ const int oldDepth = elements.size();
+ QQuickStackElement *exit = elements.pop();
+ // top() here will be the item below the previously current item, since we just popped above.
+ QQuickStackElement *enter = elements.top();
+
+ bool nothingToDo = false;
+ if (item != currentItem) {
+ if (!item) {
+ // Popping down to the first item.
+ enter = elements.value(0);
+ } else {
+ // Popping down to an arbitrary item.
+ enter = findElement(item);
+ if (!enter) {
+ warn(QStringLiteral("can't find item to pop: ") + QDebug::toString(item));
+ nothingToDo = true;
+ }
+ }
+ } else {
+ if (currentItemPolicy == CurrentItemPolicy::DoNotPop) {
+ // popToItem was called with the currentItem, which means there are no items
+ // to pop because it's already at the top.
+ nothingToDo = true;
+ }
+ // else: popToItem was called by popCurrentItem, and so we _should_ pop.
+ }
+ if (nothingToDo) {
+ // Restore the element we popped earlier.
+ elements.push(exit);
+ return nullptr;
+ }
+
+ QQuickItem *previousItem = nullptr;
+ if (popElements(enter)) {
+ if (exit) {
+ exit->removal = true;
+ removing.insert(exit);
+ previousItem = exit->item;
+ }
+ depthChange(elements.size(), oldDepth);
+#if QT_CONFIG(quick_viewtransitions)
+ Q_Q(QQuickStackView);
+ startTransition(QQuickStackTransition::popExit(operation, exit, q),
+ QQuickStackTransition::popEnter(operation, enter, q),
+ operation == QQuickStackView::Immediate);
+#else
+ Q_UNUSED(operation);
+#endif
+ setCurrentItem(enter);
+ }
+ return previousItem;
+}
+
+#if QT_CONFIG(quick_viewtransitions)
void QQuickStackViewPrivate::ensureTransitioner()
{
if (!transitioner) {
@@ -301,6 +419,7 @@ void QQuickStackViewPrivate::viewItemTransitionFinished(QQuickItemViewTransition
removing.remove(element);
}
+#endif
void QQuickStackViewPrivate::setBusy(bool b)
{
diff --git a/src/quicktemplates/qquickstackview_p.h b/src/quicktemplates/qquickstackview_p.h
index b36976a395..8606759a7c 100644
--- a/src/quicktemplates/qquickstackview_p.h
+++ b/src/quicktemplates/qquickstackview_p.h
@@ -15,30 +15,67 @@
// We mean it.
//
+#include <QtCore/qdebug.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
QT_BEGIN_NAMESPACE
-class QQmlV4Function;
class QQuickTransition;
class QQuickStackElement;
class QQuickStackViewPrivate;
class QQuickStackViewAttached;
class QQuickStackViewAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickStackView : public QQuickControl
+/*!
+ \internal
+
+ Input from the user that is turned into QQuickStackElements.
+
+ This was added to support the QML-compiler-friendly pushElement[s]
+ functions.
+*/
+class QQuickStackViewArg
+{
+ Q_GADGET
+ QML_CONSTRUCTIBLE_VALUE
+ QML_ANONYMOUS
+
+public:
+ QQuickStackViewArg() = default;
+ Q_INVOKABLE QQuickStackViewArg(QQuickItem *item);
+ Q_INVOKABLE QQuickStackViewArg(const QUrl &url);
+ Q_INVOKABLE QQuickStackViewArg(QQmlComponent *component);
+ Q_INVOKABLE QQuickStackViewArg(const QVariantMap &properties);
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend QDebug operator<<(QDebug debug, const QQuickStackViewArg &arg);
+#endif
+
+private:
+ friend class QQuickStackViewPrivate;
+ friend class QQuickStackElement;
+
+ QQuickItem *mItem = nullptr;
+ QQmlComponent *mComponent = nullptr;
+ QUrl mUrl;
+ QVariantMap mProperties;
+};
+
+class Q_QUICKTEMPLATES2_EXPORT QQuickStackView : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(bool busy READ isBusy NOTIFY busyChanged FINAL)
Q_PROPERTY(int depth READ depth NOTIFY depthChanged FINAL)
Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged FINAL)
Q_PROPERTY(QJSValue initialItem READ initialItem WRITE setInitialItem FINAL)
+#if QT_CONFIG(quick_viewtransitions)
Q_PROPERTY(QQuickTransition *popEnter READ popEnter WRITE setPopEnter NOTIFY popEnterChanged FINAL)
Q_PROPERTY(QQuickTransition *popExit READ popExit WRITE setPopExit NOTIFY popExitChanged FINAL)
Q_PROPERTY(QQuickTransition *pushEnter READ pushEnter WRITE setPushEnter NOTIFY pushEnterChanged FINAL)
Q_PROPERTY(QQuickTransition *pushExit READ pushExit WRITE setPushExit NOTIFY pushExitChanged FINAL)
Q_PROPERTY(QQuickTransition *replaceEnter READ replaceEnter WRITE setReplaceEnter NOTIFY replaceEnterChanged FINAL)
Q_PROPERTY(QQuickTransition *replaceExit READ replaceExit WRITE setReplaceExit NOTIFY replaceExitChanged FINAL)
+#endif
// 2.3 (Qt 5.10)
Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged FINAL REVISION(2, 3))
QML_NAMED_ELEMENT(StackView)
@@ -66,6 +103,7 @@ public:
QJSValue initialItem() const;
void setInitialItem(const QJSValue &item);
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *popEnter() const;
void setPopEnter(QQuickTransition *enter);
@@ -83,6 +121,7 @@ public:
QQuickTransition *replaceExit() const;
void setReplaceExit(QQuickTransition *exit);
+#endif
enum LoadBehavior {
DontLoad,
@@ -90,8 +129,8 @@ public:
};
Q_ENUM(LoadBehavior)
- Q_INVOKABLE QQuickItem *get(int index, LoadBehavior behavior = DontLoad);
- Q_INVOKABLE QQuickItem *find(const QJSValue &callback, LoadBehavior behavior = DontLoad);
+ Q_INVOKABLE QQuickItem *get(int index, QQuickStackView::LoadBehavior behavior = DontLoad);
+ Q_INVOKABLE QQuickItem *find(const QJSValue &callback, QQuickStackView::LoadBehavior behavior = DontLoad);
enum Operation {
Transition = -1, // ### Deprecated in Qt 6; remove in Qt 7.
@@ -102,9 +141,31 @@ public:
};
Q_ENUM(Operation)
- Q_INVOKABLE void push(QQmlV4Function *args);
- Q_INVOKABLE void pop(QQmlV4Function *args);
- Q_INVOKABLE void replace(QQmlV4Function *args);
+ Q_INVOKABLE void push(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void pop(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void replace(QQmlV4FunctionPtr args);
+
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *pushItems(QList<QQuickStackViewArg> args,
+ Operation operation = Immediate);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *pushItem(QQuickItem *item, const QVariantMap &properties = {},
+ Operation operation = Immediate);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *pushItem(QQmlComponent *component, const QVariantMap &properties = {},
+ Operation operation = Immediate);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *pushItem(const QUrl &url, const QVariantMap &properties = {},
+ Operation operation = Immediate);
+
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *popToItem(QQuickItem *item, Operation operation = PopTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *popToIndex(int index, Operation operation = PopTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *popCurrentItem(Operation operation = PopTransition);
+
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(const QList<QQuickStackViewArg> &args,
+ Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(QQuickItem *item,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(QQmlComponent *component,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(const QUrl &url,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
// 2.3 (Qt 5.10)
bool isEmpty() const;
@@ -116,12 +177,14 @@ Q_SIGNALS:
void busyChanged();
void depthChanged();
void currentItemChanged();
+#if QT_CONFIG(quick_viewtransitions)
void popEnterChanged();
void popExitChanged();
void pushEnterChanged();
void pushExitChanged();
void replaceEnterChanged();
void replaceExitChanged();
+#endif
// 2.3 (Qt 5.10)
Q_REVISION(2, 3) void emptyChanged();
@@ -143,7 +206,7 @@ private:
Q_DECLARE_PRIVATE(QQuickStackView)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickStackViewAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickStackViewAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
@@ -185,6 +248,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickStackView)
-
#endif // QQUICKSTACKVIEW_P_H
diff --git a/src/quicktemplates/qquickstackview_p_p.h b/src/quicktemplates/qquickstackview_p_p.h
index 2b86458edc..7d6ee50b1b 100644
--- a/src/quicktemplates/qquickstackview_p_p.h
+++ b/src/quicktemplates/qquickstackview_p_p.h
@@ -17,7 +17,9 @@
#include <QtQuickTemplates2/private/qquickstackview_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
+#if QT_CONFIG(quick_viewtransitions)
#include <QtQuick/private/qquickitemviewtransition_p.h>
+#endif
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQml/private/qv4value_p.h>
#include <QtQml/private/qqmlcontextdata_p.h>
@@ -29,7 +31,10 @@ QT_BEGIN_NAMESPACE
class QQuickStackElement;
struct QQuickStackTransition;
-class QQuickStackViewPrivate : public QQuickControlPrivate, public QQuickItemViewTransitionChangeListener
+class QQuickStackViewPrivate : public QQuickControlPrivate
+#if QT_CONFIG(quick_viewtransitions)
+ , public QQuickItemViewTransitionChangeListener
+#endif
{
Q_DECLARE_PUBLIC(QQuickStackView)
@@ -44,7 +49,8 @@ public:
void setCurrentItem(QQuickStackElement *element);
- QList<QQuickStackElement *> parseElements(int from, QQmlV4Function *args, QStringList *errors);
+ QList<QQuickStackElement *> parseElements(int from, QQmlV4FunctionPtr args, QStringList *errors);
+ QList<QQuickStackElement *> parseElements(const QList<QQuickStackViewArg> &args);
QQuickStackElement *findElement(QQuickItem *item) const;
QQuickStackElement *findElement(const QV4::Value &value) const;
QQuickStackElement *createElement(const QV4::Value &value, const QQmlRefPointer<QQmlContextData> &context, QString *error);
@@ -53,11 +59,19 @@ public:
bool popElements(QQuickStackElement *element);
bool replaceElements(QQuickStackElement *element, const QList<QQuickStackElement *> &elements);
+ enum class CurrentItemPolicy {
+ DoNotPop,
+ Pop
+ };
+ QQuickItem *popToItem(QQuickItem *item, QQuickStackView::Operation operation, CurrentItemPolicy currentItemPolicy);
+
+#if QT_CONFIG(quick_viewtransitions)
void ensureTransitioner();
void startTransition(const QQuickStackTransition &first, const QQuickStackTransition &second, bool immediate);
void completeTransition(QQuickStackElement *element, QQuickTransition *transition, QQuickStackView::Status status);
void viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) override;
+#endif
void setBusy(bool busy);
void depthChange(int newDepth, int oldDepth);
@@ -69,10 +83,15 @@ public:
QSet<QQuickStackElement*> removing;
QList<QQuickStackElement*> removed;
QStack<QQuickStackElement *> elements;
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner *transitioner = nullptr;
+#endif
};
-class QQuickStackViewAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener
+class QQuickStackViewAttachedPrivate : public QObjectPrivate
+//#if QT_CONFIG(quick_viewtransitions)
+ , public QQuickItemChangeListener
+//#endif
{
Q_DECLARE_PUBLIC(QQuickStackViewAttached)
@@ -82,7 +101,9 @@ public:
return attached->d_func();
}
+//#if QT_CONFIG(quick_viewtransitions)
void itemParentChanged(QQuickItem *item, QQuickItem *parent) override;
+//#endif
bool explicitVisible = false;
QQuickStackElement *element = nullptr;
diff --git a/src/quicktemplates/qquickswipe_p.h b/src/quicktemplates/qquickswipe_p.h
index 4694948763..44eadcb584 100644
--- a/src/quicktemplates/qquickswipe_p.h
+++ b/src/quicktemplates/qquickswipe_p.h
@@ -26,7 +26,7 @@ class QQuickItem;
class QQuickTransition;
class QQuickSwipePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipe : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipe : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL)
diff --git a/src/quicktemplates/qquickswipedelegate.cpp b/src/quicktemplates/qquickswipedelegate.cpp
index 24ca772b06..36782fee8c 100644
--- a/src/quicktemplates/qquickswipedelegate.cpp
+++ b/src/quicktemplates/qquickswipedelegate.cpp
@@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE
\image qtquickcontrols-swipedelegate-behind.gif
- \sa {Customizing SwipeDelegate}, {Delegate Controls}, {Qt Quick Controls 2 - Swipe to Remove}{Swipe to Remove Example}
+ \sa {Customizing SwipeDelegate}, {Delegate Controls}, {Qt Quick Controls 2 - Gallery}{Gallery Example}
*/
namespace {
@@ -194,6 +194,7 @@ QQuickItem *QQuickSwipePrivate::createDelegateItem(QQmlComponent *component)
if (item) {
item->setParentItem(control);
component->completeCreate();
+ QJSEngine::setObjectOwnership(item, QJSEngine::JavaScriptOwnership);
}
return item;
}
@@ -770,12 +771,18 @@ bool QQuickSwipeDelegatePrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEv
if (!swipePrivate->left && !swipePrivate->right && !swipePrivate->behind)
return false;
+ if (item != q && swipePrivate->complete) {
+ // If the delegate is swiped open, send the event to the exposed item,
+ // in case it's an interactive child (like a Button).
+ const auto posInItem = item->mapToItem(q, event->position().toPoint());
+ forwardMouseEvent(event, item, posInItem);
+ }
+
// Don't handle move events for the control if it wasn't pressed.
if (item == q && !pressed)
return false;
- const QPointF mappedEventPos = item->mapToItem(q, event->position().toPoint());
- const qreal distance = (mappedEventPos - pressPoint).x();
+ const qreal distance = (event->globalPosition() - event->points().first().globalPressPosition()).x();
if (!q->keepMouseGrab()) {
// We used to use the custom threshold that QQuickDrawerPrivate::grabMouse used,
// but since it's larger than what Flickable uses, it results in Flickable
@@ -998,6 +1005,33 @@ QPalette QQuickSwipeDelegatePrivate::defaultPalette() const
return QQuickTheme::palette(QQuickTheme::ListView);
}
+/*! \internal
+ Recursively search right and/or left item tree of swipe delegate for any item that
+ contains the \a event position.
+
+ Returns the first such item found, otherwise \c nullptr.
+*/
+QQuickItem *QQuickSwipeDelegatePrivate::getPressedItem(QQuickItem *childItem, QMouseEvent *event) const
+{
+ if (!childItem || !event)
+ return nullptr;
+
+ QQuickItem *item = nullptr;
+
+ if (childItem->acceptedMouseButtons() &&
+ childItem->contains(childItem->mapFromScene(event->scenePosition()))) {
+ item = childItem;
+ } else {
+ const auto &childItems = childItem->childItems();
+ for (const auto &child: childItems) {
+ if ((item = getPressedItem(child, event)))
+ break;
+ }
+ }
+
+ return item;
+}
+
QQuickSwipeDelegate::QQuickSwipeDelegate(QQuickItem *parent)
: QQuickItemDelegate(*(new QQuickSwipeDelegatePrivate(this)), parent)
{
@@ -1240,17 +1274,11 @@ void QQuickSwipeDelegate::mousePressEvent(QMouseEvent *event)
swipePrivate->velocityCalculator.startMeasuring(event->position().toPoint(), event->timestamp());
if (swipePrivate->complete) {
- auto item = d->swipe.rightItem();
- if (item && item->contains(item->mapFromScene(event->scenePosition()))) {
- d->pressedItem = item;
- d->handleMousePressEvent(item, event);
- } else {
- item = d->swipe.leftItem();
- if (item && item->contains(item->mapFromScene(event->scenePosition()))) {
- d->pressedItem = item;
- d->handleMousePressEvent(item, event);
- }
- }
+ d->pressedItem = d->getPressedItem(d->swipe.rightItem(), event);
+ if (!d->pressedItem)
+ d->pressedItem = d->getPressedItem(d->swipe.leftItem(), event);
+ if (d->pressedItem)
+ d->handleMousePressEvent(d->pressedItem, event);
}
}
diff --git a/src/quicktemplates/qquickswipedelegate_p.h b/src/quicktemplates/qquickswipedelegate_p.h
index 2fadabe09f..fae6c6136d 100644
--- a/src/quicktemplates/qquickswipedelegate_p.h
+++ b/src/quicktemplates/qquickswipedelegate_p.h
@@ -24,7 +24,7 @@ class QQuickSwipeDelegatePrivate;
class QQuickSwipeDelegateAttached;
class QQuickSwipeDelegateAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipeDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(QQuickSwipe *swipe READ swipe CONSTANT FINAL)
@@ -64,7 +64,7 @@ private:
Q_DECLARE_PRIVATE(QQuickSwipeDelegate)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeDelegateAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipeDelegateAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged FINAL)
@@ -86,7 +86,6 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSwipeDelegate)
Q_DECLARE_METATYPE(QQuickSwipeDelegate::Side)
#endif // QQUICKSWIPEDELEGATE_P_H
diff --git a/src/quicktemplates/qquickswipedelegate_p_p.h b/src/quicktemplates/qquickswipedelegate_p_p.h
index 4963bb0ff7..78034badd3 100644
--- a/src/quicktemplates/qquickswipedelegate_p_p.h
+++ b/src/quicktemplates/qquickswipedelegate_p_p.h
@@ -34,6 +34,7 @@ public:
bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event);
void forwardMouseEvent(QMouseEvent *event, QQuickItem *destination, QPointF localPos);
bool attachedObjectsSetPressed(QQuickItem *item, QPointF scenePos, bool pressed, bool cancel = false);
+ QQuickItem *getPressedItem(QQuickItem *childItem, QMouseEvent *event) const;
void resizeContent() override;
void resizeBackground() override;
diff --git a/src/quicktemplates/qquickswipeview.cpp b/src/quicktemplates/qquickswipeview.cpp
index fe647f5764..425f7a5e1a 100644
--- a/src/quicktemplates/qquickswipeview.cpp
+++ b/src/quicktemplates/qquickswipeview.cpp
@@ -77,7 +77,7 @@ class QQuickSwipeViewPrivate : public QQuickContainerPrivate
Q_DECLARE_PUBLIC(QQuickSwipeView)
public:
- void resizeItem(QQuickItem *item);
+ void resizeItem(int index, QQuickItem *item);
void resizeItems();
static QQuickSwipeViewPrivate *get(QQuickSwipeView *view);
@@ -111,25 +111,29 @@ public:
int currentIndex = -1;
};
+void QQuickSwipeViewPrivate::resizeItem(int index, QQuickItem *item)
+{
+ QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
+ // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
+ if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) {
+ qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
+ item->setProperty("_q_QQuickSwipeView_warned", true);
+ }
+ if (orientation == Qt::Horizontal)
+ item->setPosition({index * (contentItem->width() + spacing), 0});
+ else
+ item->setPosition({0, index * (contentItem->height() + spacing)});
+ item->setSize(QSizeF(contentItem->width(), contentItem->height()));
+}
+
void QQuickSwipeViewPrivate::resizeItems()
{
Q_Q(QQuickSwipeView);
const int count = q->count();
for (int i = 0; i < count; ++i) {
QQuickItem *item = itemAt(i);
- if (item) {
- QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
- // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
- if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) {
- qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
- item->setProperty("_q_QQuickSwipeView_warned", true);
- }
- if (orientation == Qt::Horizontal)
- item->setPosition({i * (contentItem->width() + spacing), 0});
- else
- item->setPosition({0, i * (contentItem->height() + spacing)});
- item->setSize(QSizeF(contentItem->width(), contentItem->height()));
- }
+ if (item)
+ resizeItem(i, item);
}
}
@@ -268,6 +272,13 @@ QQuickSwipeViewAttached *QQuickSwipeView::qmlAttachedProperties(QObject *object)
return new QQuickSwipeViewAttached(object);
}
+void QQuickSwipeView::componentComplete()
+{
+ Q_D(QQuickSwipeView);
+ QQuickContainer::componentComplete();
+ d->resizeItems();
+}
+
void QQuickSwipeView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickSwipeView);
@@ -279,7 +290,7 @@ void QQuickSwipeView::itemAdded(int index, QQuickItem *item)
{
Q_D(QQuickSwipeView);
if (isComponentComplete())
- item->setSize(QSizeF(d->contentItem->width(), d->contentItem->height()));
+ d->resizeItem(index, item);
QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(qmlAttachedPropertiesObject<QQuickSwipeView>(item));
if (attached)
QQuickSwipeViewAttachedPrivate::get(attached)->update(this, index);
diff --git a/src/quicktemplates/qquickswipeview_p.h b/src/quicktemplates/qquickswipeview_p.h
index 84e4b51b2a..758de23ffe 100644
--- a/src/quicktemplates/qquickswipeview_p.h
+++ b/src/quicktemplates/qquickswipeview_p.h
@@ -17,12 +17,14 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickSwipeViewAttached;
class QQuickSwipeViewPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeView : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipeView : public QQuickContainer
{
Q_OBJECT
// 2.1 (Qt 5.8)
@@ -60,6 +62,7 @@ Q_SIGNALS:
Q_REVISION(2, 2) void orientationChanged();
protected:
+ void componentComplete() override;
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void itemAdded(int index, QQuickItem *item) override;
void itemMoved(int index, QQuickItem *item) override;
@@ -76,7 +79,7 @@ private:
class QQuickSwipeViewAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeViewAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipeViewAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
@@ -112,6 +115,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSwipeView)
-
#endif // QQUICKSWIPEVIEW_P_H
diff --git a/src/quicktemplates/qquickswitch_p.h b/src/quicktemplates/qquickswitch_p.h
index 9044b066b9..4ee45e8d50 100644
--- a/src/quicktemplates/qquickswitch_p.h
+++ b/src/quicktemplates/qquickswitch_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickSwitchPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwitch : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwitch : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -61,6 +61,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSwitch)
-
#endif // QQUICKSWITCH_P_H
diff --git a/src/quicktemplates/qquickswitchdelegate_p.h b/src/quicktemplates/qquickswitchdelegate_p.h
index d6a6c830b3..26823c3fa8 100644
--- a/src/quicktemplates/qquickswitchdelegate_p.h
+++ b/src/quicktemplates/qquickswitchdelegate_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickSwitchDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwitchDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwitchDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -61,6 +61,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSwitchDelegate)
-
#endif // QQUICKSWITCHDELEGATE_P_H
diff --git a/src/quicktemplates/qquicktabbar_p.h b/src/quicktemplates/qquicktabbar_p.h
index f062a01b1a..f0b7ebb12f 100644
--- a/src/quicktemplates/qquicktabbar_p.h
+++ b/src/quicktemplates/qquicktabbar_p.h
@@ -17,13 +17,15 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickTabBarPrivate;
class QQuickTabBarAttached;
class QQuickTabBarAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabBar : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickTabBar : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(Position position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -71,7 +73,7 @@ private:
Q_DECLARE_PRIVATE(QQuickTabBar)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabBarAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickTabBarAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
@@ -97,6 +99,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTabBar)
-
#endif // QQUICKTABBAR_P_H
diff --git a/src/quicktemplates/qquicktabbutton.cpp b/src/quicktemplates/qquicktabbutton.cpp
index 500ee0e81b..1c8402fd23 100644
--- a/src/quicktemplates/qquicktabbutton.cpp
+++ b/src/quicktemplates/qquicktabbutton.cpp
@@ -31,7 +31,7 @@ QT_BEGIN_NAMESPACE
\sa TabBar, {Customizing TabButton}, {Button Controls}, {Navigation Controls}
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabButtonPrivate : public QQuickAbstractButtonPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickTabButtonPrivate : public QQuickAbstractButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickTabButton)
diff --git a/src/quicktemplates/qquicktabbutton_p.h b/src/quicktemplates/qquicktabbutton_p.h
index 7b2fbdb2fe..1ba7ac5f5a 100644
--- a/src/quicktemplates/qquicktabbutton_p.h
+++ b/src/quicktemplates/qquicktabbutton_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickTabButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabButton : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickTabButton : public QQuickAbstractButton
{
Q_OBJECT
QML_NAMED_ELEMENT(TabButton)
@@ -43,6 +43,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTabButton)
-
#endif // QQUICKTABBUTTON_P_H
diff --git a/src/quicktemplates/qquicktextarea.cpp b/src/quicktemplates/qquicktextarea.cpp
index d43f78a5a3..fb4702928e 100644
--- a/src/quicktemplates/qquicktextarea.cpp
+++ b/src/quicktemplates/qquicktextarea.cpp
@@ -121,16 +121,10 @@ using namespace Qt::StringLiterals;
QQuickTextAreaPrivate::QQuickTextAreaPrivate()
{
-#if QT_CONFIG(accessibility)
- QAccessible::installActivationObserver(this);
-#endif
}
QQuickTextAreaPrivate::~QQuickTextAreaPrivate()
{
-#if QT_CONFIG(accessibility)
- QAccessible::removeActivationObserver(this);
-#endif
}
void QQuickTextAreaPrivate::setTopInset(qreal value, bool reset)
@@ -373,8 +367,8 @@ void QQuickTextAreaPrivate::resizeFlickableContent()
if (!flickable)
return;
- flickable->setContentWidth(q->contentWidth() + q->leftPadding() + q->rightPadding());
- flickable->setContentHeight(q->contentHeight() + q->topPadding() + q->bottomPadding());
+ flickable->setContentWidth(q->implicitWidth());
+ flickable->setContentHeight(q->implicitHeight());
}
void QQuickTextAreaPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
@@ -420,37 +414,15 @@ void QQuickTextAreaPrivate::implicitHeightChanged()
emit q->implicitHeightChanged3();
}
-void QQuickTextAreaPrivate::readOnlyChanged(bool isReadOnly)
-{
- Q_UNUSED(isReadOnly);
-#if QT_CONFIG(accessibility)
- if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(q_func()))
- accessibleAttached->set_readOnly(isReadOnly);
-#endif
-}
-
#if QT_CONFIG(accessibility)
void QQuickTextAreaPrivate::accessibilityActiveChanged(bool active)
{
- if (!active)
- return;
-
- Q_Q(QQuickTextArea);
- QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
- Q_ASSERT(accessibleAttached);
- accessibleAttached->setRole(accessibleRole());
- accessibleAttached->set_readOnly(q->isReadOnly());
- accessibleAttached->setDescription(placeholder);
-}
-
-QAccessible::Role QQuickTextAreaPrivate::accessibleRole() const
-{
- return QAccessible::EditableText;
+ QQuickTextEditPrivate::accessibilityActiveChanged(active);
+ if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(q_func()))
+ accessibleAttached->setDescription(placeholder);
}
#endif
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickTextAreaPrivate::cancelBackground()
{
Q_Q(QQuickTextArea);
@@ -509,8 +481,6 @@ QQuickTextArea::QQuickTextArea(QQuickItem *parent)
d->setImplicitResizeEnabled(false);
d->pressHandler.control = this;
- QObjectPrivate::connect(this, &QQuickTextEdit::readOnlyChanged,
- d, &QQuickTextAreaPrivate::readOnlyChanged);
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
if (qEnvironmentVariable("QT_QUICK_CONTROLS_TEXT_SELECTION_BEHAVIOR") == u"old"_s)
QQuickTextEdit::setOldSelectionDefault();
@@ -667,24 +637,36 @@ void QQuickTextArea::setPlaceholderTextColor(const QColor &color)
/*!
\qmlproperty enumeration QtQuick.Controls::TextArea::focusReason
- \include qquickcontrol-focusreason.qdocinc
+ This property holds the reason of the last focus change.
+
+ \note This property does not indicate whether the item has \l {Item::activeFocus}
+ {active focus}, but the reason why the item either gained or lost focus.
+
+ \value Qt.MouseFocusReason A mouse action occurred.
+ \value Qt.TabFocusReason The Tab key was pressed.
+ \value Qt.BacktabFocusReason A Backtab occurred. The input for this may include the Shift or Control keys; e.g. Shift+Tab.
+ \value Qt.ActiveWindowFocusReason The window system made this window either active or inactive.
+ \value Qt.PopupFocusReason The application opened/closed a pop-up that grabbed/released the keyboard focus.
+ \value Qt.ShortcutFocusReason The user typed a label's buddy shortcut
+ \value Qt.MenuBarFocusReason The menu bar took focus.
+ \value Qt.OtherFocusReason Another reason, usually application-specific.
+
+ \note Prefer \l {Item::focusReason} to this property.
*/
Qt::FocusReason QQuickTextArea::focusReason() const
{
Q_D(const QQuickTextArea);
- return d->focusReason;
+ return d->lastFocusChangeReason();
}
void QQuickTextArea::setFocusReason(Qt::FocusReason reason)
{
Q_D(QQuickTextArea);
- if (d->focusReason == reason)
- return;
-
- d->focusReason = reason;
+ d->setLastFocusChangeReason(reason);
emit focusReasonChanged();
}
+
bool QQuickTextArea::contains(const QPointF &point) const
{
Q_D(const QQuickTextArea);
@@ -928,10 +910,6 @@ void QQuickTextArea::componentComplete()
if (!d->explicitHoverEnabled)
setAcceptHoverEvents(QQuickControlPrivate::calcHoverEnabled(d->parentItem));
#endif
-#if QT_CONFIG(accessibility)
- if (QAccessible::isActive())
- d->accessibilityActiveChanged(true);
-#endif
}
void QQuickTextArea::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
@@ -1014,13 +992,11 @@ QSGNode *QQuickTextArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
void QQuickTextArea::focusInEvent(QFocusEvent *event)
{
QQuickTextEdit::focusInEvent(event);
- setFocusReason(event->reason());
}
void QQuickTextArea::focusOutEvent(QFocusEvent *event)
{
QQuickTextEdit::focusOutEvent(event);
- setFocusReason(event->reason());
}
#if QT_CONFIG(quicktemplates2_hover)
@@ -1046,7 +1022,7 @@ void QQuickTextArea::mousePressEvent(QMouseEvent *event)
d->pressHandler.mousePressEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
// Calling the base class implementation will result in QQuickTextControl's
@@ -1064,7 +1040,7 @@ void QQuickTextArea::mouseMoveEvent(QMouseEvent *event)
d->pressHandler.mouseMoveEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
QQuickTextEdit::mouseMoveEvent(event);
@@ -1077,7 +1053,7 @@ void QQuickTextArea::mouseReleaseEvent(QMouseEvent *event)
d->pressHandler.mouseReleaseEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
QQuickTextEdit::mouseReleaseEvent(event);
@@ -1088,7 +1064,7 @@ void QQuickTextArea::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_D(QQuickTextArea);
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
QQuickTextEdit::mouseDoubleClickEvent(event);
diff --git a/src/quicktemplates/qquicktextarea_p.h b/src/quicktemplates/qquicktextarea_p.h
index 5bd10af21e..d09f300a9f 100644
--- a/src/quicktemplates/qquicktextarea_p.h
+++ b/src/quicktemplates/qquicktextarea_p.h
@@ -26,7 +26,7 @@ class QQuickText;
class QQuickTextAreaPrivate;
class QQuickTextAreaAttached;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextArea : public QQuickTextEdit
+class Q_QUICKTEMPLATES2_EXPORT QQuickTextArea : public QQuickTextEdit
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override
@@ -155,7 +155,7 @@ private:
class QQuickTextAreaAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextAreaAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickTextAreaAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickTextArea *flickable READ flickable WRITE setFlickable NOTIFY flickableChanged FINAL)
@@ -174,16 +174,6 @@ private:
Q_DECLARE_PRIVATE(QQuickTextAreaAttached)
};
-struct QQuickTextEditForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickTextEdit)
- QML_ADDED_IN_VERSION(2, 3)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextArea)
-
#endif // QQUICKTEXTAREA_P_H
diff --git a/src/quicktemplates/qquicktextarea_p_p.h b/src/quicktemplates/qquicktextarea_p_p.h
index 243b350866..5e406d869e 100644
--- a/src/quicktemplates/qquicktextarea_p_p.h
+++ b/src/quicktemplates/qquicktextarea_p_p.h
@@ -24,18 +24,11 @@
#include <QtQuickTemplates2/private/qquicktextarea_p.h>
-#if QT_CONFIG(accessibility)
-#include <QtGui/qaccessible.h>
-#endif
-
QT_BEGIN_NAMESPACE
class QQuickFlickable;
class QQuickTextAreaPrivate : public QQuickTextEditPrivate, public QQuickItemChangeListener
-#if QT_CONFIG(accessibility)
- , public QAccessible::ActivationObserver
-#endif
{
public:
Q_DECLARE_PUBLIC(QQuickTextArea)
@@ -92,7 +85,6 @@ public:
#if QT_CONFIG(accessibility)
void accessibilityActiveChanged(bool active) override;
- QAccessible::Role accessibleRole() const override;
#endif
void cancelBackground();
@@ -130,7 +122,6 @@ public:
QQuickDeferredPointer<QQuickItem> background;
QString placeholder;
QColor placeholderColor;
- Qt::FocusReason focusReason = Qt::OtherFocusReason;
QQuickPressHandler pressHandler;
QQuickFlickable *flickable = nullptr;
};
diff --git a/src/quicktemplates/qquicktextfield.cpp b/src/quicktemplates/qquicktextfield.cpp
index bb62c15ed1..9513be3f3b 100644
--- a/src/quicktemplates/qquicktextfield.cpp
+++ b/src/quicktemplates/qquicktextfield.cpp
@@ -288,7 +288,7 @@ void QQuickTextFieldPrivate::accessibilityActiveChanged(bool active)
Q_Q(QQuickTextField);
QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
Q_ASSERT(accessibleAttached);
- accessibleAttached->setRole(accessibleRole());
+ accessibleAttached->setRole(effectiveAccessibleRole());
accessibleAttached->set_readOnly(m_readOnly);
accessibleAttached->set_passwordEdit((m_echoMode == QQuickTextField::Password || m_echoMode == QQuickTextField::PasswordEchoOnEdit) ? true : false);
accessibleAttached->setDescription(placeholder);
@@ -300,8 +300,6 @@ QAccessible::Role QQuickTextFieldPrivate::accessibleRole() const
}
#endif
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickTextFieldPrivate::cancelBackground()
{
Q_Q(QQuickTextField);
@@ -525,21 +523,32 @@ void QQuickTextField::setPlaceholderTextColor(const QColor &color)
/*!
\qmlproperty enumeration QtQuick.Controls::TextField::focusReason
- \include qquickcontrol-focusreason.qdocinc
+ This property holds the reason of the last focus change.
+
+ \note This property does not indicate whether the item has \l {Item::activeFocus}
+ {active focus}, but the reason why the item either gained or lost focus.
+
+ \value Qt.MouseFocusReason A mouse action occurred.
+ \value Qt.TabFocusReason The Tab key was pressed.
+ \value Qt.BacktabFocusReason A Backtab occurred. The input for this may include the Shift or Control keys; e.g. Shift+Tab.
+ \value Qt.ActiveWindowFocusReason The window system made this window either active or inactive.
+ \value Qt.PopupFocusReason The application opened/closed a pop-up that grabbed/released the keyboard focus.
+ \value Qt.ShortcutFocusReason The user typed a label's buddy shortcut
+ \value Qt.MenuBarFocusReason The menu bar took focus.
+ \value Qt.OtherFocusReason Another reason, usually application-specific.
+
+ \note Prefer \l {Item::focusReason} to this property.
*/
Qt::FocusReason QQuickTextField::focusReason() const
{
Q_D(const QQuickTextField);
- return d->focusReason;
+ return d->lastFocusChangeReason();
}
void QQuickTextField::setFocusReason(Qt::FocusReason reason)
{
Q_D(QQuickTextField);
- if (d->focusReason == reason)
- return;
-
- d->focusReason = reason;
+ d->setLastFocusChangeReason(reason);
emit focusReasonChanged();
}
@@ -839,13 +848,11 @@ QSGNode *QQuickTextField::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
void QQuickTextField::focusInEvent(QFocusEvent *event)
{
QQuickTextInput::focusInEvent(event);
- setFocusReason(event->reason());
}
void QQuickTextField::focusOutEvent(QFocusEvent *event)
{
QQuickTextInput::focusOutEvent(event);
- setFocusReason(event->reason());
}
#if QT_CONFIG(quicktemplates2_hover)
@@ -871,7 +878,7 @@ void QQuickTextField::mousePressEvent(QMouseEvent *event)
d->pressHandler.mousePressEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
if (event->buttons() != Qt::RightButton)
@@ -885,7 +892,7 @@ void QQuickTextField::mouseMoveEvent(QMouseEvent *event)
d->pressHandler.mouseMoveEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event)
@@ -904,7 +911,7 @@ void QQuickTextField::mouseReleaseEvent(QMouseEvent *event)
d->pressHandler.mouseReleaseEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
if (event->buttons() != Qt::RightButton)
@@ -916,7 +923,7 @@ void QQuickTextField::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_D(QQuickTextField);
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
if (event->buttons() != Qt::RightButton)
diff --git a/src/quicktemplates/qquicktextfield_p.h b/src/quicktemplates/qquicktextfield_p.h
index c5bc9ce0bd..4c38c098ba 100644
--- a/src/quicktemplates/qquicktextfield_p.h
+++ b/src/quicktemplates/qquicktextfield_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickTextFieldPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextField : public QQuickTextInput
+class Q_QUICKTEMPLATES2_EXPORT QQuickTextField : public QQuickTextInput
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override
@@ -146,16 +146,6 @@ private:
Q_DECLARE_PRIVATE(QQuickTextField)
};
-struct QQuickTextFieldForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickTextInput)
- QML_ADDED_IN_VERSION(2, 2)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextField)
-
#endif // QQUICKTEXTFIELD_P_H
diff --git a/src/quicktemplates/qquicktextfield_p_p.h b/src/quicktemplates/qquicktextfield_p_p.h
index 8c6752776d..c8afee8ba3 100644
--- a/src/quicktemplates/qquicktextfield_p_p.h
+++ b/src/quicktemplates/qquicktextfield_p_p.h
@@ -118,7 +118,6 @@ public:
QQuickDeferredPointer<QQuickItem> background;
QString placeholder;
QColor placeholderColor;
- Qt::FocusReason focusReason = Qt::OtherFocusReason;
QQuickPressHandler pressHandler;
};
diff --git a/src/quicktemplates/qquicktheme_p.h b/src/quicktemplates/qquicktheme_p.h
index da6254e9bf..96cbd2c0ad 100644
--- a/src/quicktemplates/qquicktheme_p.h
+++ b/src/quicktemplates/qquicktheme_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickThemePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTheme
+class Q_QUICKTEMPLATES2_EXPORT QQuickTheme
{
public:
QQuickTheme();
diff --git a/src/quicktemplates/qquicktheme_p_p.h b/src/quicktemplates/qquicktheme_p_p.h
index a18957bd0d..f6c9cc39fb 100644
--- a/src/quicktemplates/qquicktheme_p_p.h
+++ b/src/quicktemplates/qquicktheme_p_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickThemePrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickThemePrivate
{
public:
static QQuickThemePrivate *get(QQuickTheme *theme)
diff --git a/src/quicktemplates/qquicktoolbar_p.h b/src/quicktemplates/qquicktoolbar_p.h
index f896361ea0..3079bc78f0 100644
--- a/src/quicktemplates/qquicktoolbar_p.h
+++ b/src/quicktemplates/qquicktoolbar_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickToolBarPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolBar : public QQuickPane
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolBar : public QQuickPane
{
Q_OBJECT
Q_PROPERTY(Position position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -57,6 +57,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickToolBar)
-
#endif // QQUICKTOOLBAR_P_H
diff --git a/src/quicktemplates/qquicktoolbutton.cpp b/src/quicktemplates/qquicktoolbutton.cpp
index 3acf8782a1..d857b8ab40 100644
--- a/src/quicktemplates/qquicktoolbutton.cpp
+++ b/src/quicktemplates/qquicktoolbutton.cpp
@@ -32,7 +32,7 @@ QT_BEGIN_NAMESPACE
\sa ToolBar, {Customizing ToolButton}, {Button Controls}
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolPrivate : public QQuickButtonPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolPrivate : public QQuickButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickToolButton)
diff --git a/src/quicktemplates/qquicktoolbutton_p.h b/src/quicktemplates/qquicktoolbutton_p.h
index e470ae816d..2ddd9087c6 100644
--- a/src/quicktemplates/qquicktoolbutton_p.h
+++ b/src/quicktemplates/qquicktoolbutton_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickToolButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolButton : public QQuickButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolButton : public QQuickButton
{
Q_OBJECT
QML_NAMED_ELEMENT(ToolButton)
@@ -39,6 +39,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickToolButton)
-
#endif // QQUICKTOOLBUTTON_P_H
diff --git a/src/quicktemplates/qquicktoolseparator_p.h b/src/quicktemplates/qquicktoolseparator_p.h
index 85930a3506..04d941566c 100644
--- a/src/quicktemplates/qquicktoolseparator_p.h
+++ b/src/quicktemplates/qquicktoolseparator_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickToolSeparatorPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolSeparator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolSeparator : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL)
@@ -56,6 +56,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickToolSeparator)
-
#endif // QQUICKTOOLSEPARATOR_P_H
diff --git a/src/quicktemplates/qquicktooltip_p.h b/src/quicktemplates/qquicktooltip_p.h
index 979df38a31..1ac89d8efb 100644
--- a/src/quicktemplates/qquicktooltip_p.h
+++ b/src/quicktemplates/qquicktooltip_p.h
@@ -23,7 +23,7 @@ class QQuickToolTipPrivate;
class QQuickToolTipAttached;
class QQuickToolTipAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolTip : public QQuickPopup
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolTip : public QQuickPopup
{
Q_OBJECT
Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY delayChanged FINAL)
@@ -74,7 +74,7 @@ private:
Q_DECLARE_PRIVATE(QQuickToolTip)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolTipAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolTipAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL)
@@ -117,6 +117,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickToolTip)
-
#endif // QQUICKTOOLTIP_P_H
diff --git a/src/quicktemplates/qquicktreeviewdelegate.cpp b/src/quicktemplates/qquicktreeviewdelegate.cpp
index ba1a26cb34..32d51975be 100644
--- a/src/quicktemplates/qquicktreeviewdelegate.cpp
+++ b/src/quicktemplates/qquicktreeviewdelegate.cpp
@@ -7,6 +7,8 @@
#include <QtQuick/private/qquicktaphandler_p.h>
#include <QtQuick/private/qquicktreeview_p_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
/*!
@@ -99,14 +101,14 @@ QT_BEGIN_NAMESPACE
\note If you want to disable the default behavior that occurs when the
user clicks on the delegate (like changing the current index), you can set
- \l {QQuickTableView::pointerNavigationEnabled}{pointerNavigationEnabled} to \c false.
+ \l {TableView::pointerNavigationEnabled}{pointerNavigationEnabled} to \c false.
\section2 Editing nodes in the tree
TreeViewDelegate has a default \l {TableView::editDelegate}{edit delegate}
assigned. If \l TreeView has \l {TableView::editTriggers}{edit triggers}
set, and the \l {TableView::model}{model} has support for
\l {Editing cells} {editing model items}, then the user can activate any of
- the edit triggers to edit the text of the \l {TableView::current}{current}
+ the edit triggers to edit the text of the \l {TreeViewDelegate::current}{current}
tree node.
The default edit delegate will try to use the \c {Qt.EditRole} to read and
@@ -138,7 +140,7 @@ QT_BEGIN_NAMESPACE
\qmlproperty bool QtQuick.Controls::TreeViewDelegate::isTreeNode
This property is \c true if the delegate item draws a node in the tree.
- Only one column in the view will be used to to draw the tree, and
+ Only one column in the view will be used to draw the tree, and
therefore, only delegate items in that column will have this
property set to \c true.
diff --git a/src/quicktemplates/qquicktreeviewdelegate_p.h b/src/quicktemplates/qquicktreeviewdelegate_p.h
index aa39b17c10..e56057301e 100644
--- a/src/quicktemplates/qquicktreeviewdelegate_p.h
+++ b/src/quicktemplates/qquicktreeviewdelegate_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickTreeViewDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTreeViewDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickTreeViewDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(qreal indentation READ indentation WRITE setIndentation NOTIFY indentationChanged FINAL)
diff --git a/src/quicktemplates/qquicktumbler.cpp b/src/quicktemplates/qquicktumbler.cpp
index e7f6606de3..71766cc698 100644
--- a/src/quicktemplates/qquicktumbler.cpp
+++ b/src/quicktemplates/qquicktumbler.cpp
@@ -259,6 +259,9 @@ QPalette QQuickTumblerPrivate::defaultPalette() const
QQuickTumbler::QQuickTumbler(QQuickItem *parent)
: QQuickControl(*(new QQuickTumblerPrivate), parent)
{
+ Q_D(QQuickTumbler);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
+
setActiveFocusOnTab(true);
connect(this, SIGNAL(leftPaddingChanged()), this, SLOT(_q_updateItemWidths()));
diff --git a/src/quicktemplates/qquicktumbler_p.h b/src/quicktemplates/qquicktumbler_p.h
index 02ea3049fd..d2f980aad0 100644
--- a/src/quicktemplates/qquicktumbler_p.h
+++ b/src/quicktemplates/qquicktumbler_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickTumblerAttached;
class QQuickTumblerPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumbler : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickTumbler : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged FINAL)
@@ -118,7 +118,7 @@ private:
class QQuickTumblerAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumblerAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickTumblerAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickTumbler *tumbler READ tumbler CONSTANT FINAL)
@@ -140,6 +140,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTumbler)
-
#endif // QQUICKTUMBLER_P_H
diff --git a/src/quicktemplates/qquicktumbler_p_p.h b/src/quicktemplates/qquicktumbler_p_p.h
index e53381c470..70a611bf0f 100644
--- a/src/quicktemplates/qquicktumbler_p_p.h
+++ b/src/quicktemplates/qquicktumbler_p_p.h
@@ -18,9 +18,11 @@
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
#include <QtQuickTemplates2/private/qquicktumbler_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumblerPrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickTumblerPrivate : public QQuickControlPrivate
{
Q_DECLARE_PUBLIC(QQuickTumbler)
diff --git a/src/quicktemplates/qquickweeknumbercolumn_p.h b/src/quicktemplates/qquickweeknumbercolumn_p.h
index 7ea6255319..6db01a0923 100644
--- a/src/quicktemplates/qquickweeknumbercolumn_p.h
+++ b/src/quicktemplates/qquickweeknumbercolumn_p.h
@@ -66,6 +66,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickWeekNumberColumn)
-
#endif // QQUICKWEEKNUMBERCOLUMN_P_H
diff --git a/src/quicktemplates/qquickweeknumbermodel_p.h b/src/quicktemplates/qquickweeknumbermodel_p.h
index f00fd5a073..ba12f2be16 100644
--- a/src/quicktemplates/qquickweeknumbermodel_p.h
+++ b/src/quicktemplates/qquickweeknumbermodel_p.h
@@ -67,6 +67,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickWeekNumberModel)
-
#endif // QQUICKWEEKNUMBERMODEL_P_H
diff --git a/src/quicktemplates/qtquicktemplates2global_p.h b/src/quicktemplates/qtquicktemplates2global_p.h
index bca84663c6..27b5947994 100644
--- a/src/quicktemplates/qtquicktemplates2global_p.h
+++ b/src/quicktemplates/qtquicktemplates2global_p.h
@@ -18,12 +18,16 @@
#include <QtCore/qglobal.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <QtQuickTemplates2/private/qtquicktemplates2-config_p.h>
-#include <QtQuickTemplates2/private/qtquicktemplates2exports_p.h>
+#include <QtQuickTemplates2/qtquicktemplates2exports.h>
QT_BEGIN_NAMESPACE
-Q_QUICKTEMPLATES2_PRIVATE_EXPORT void QQuickTemplates_initializeModule();
-Q_QUICKTEMPLATES2_PRIVATE_EXPORT void qml_register_types_QtQuick_Templates();
+Q_QUICKTEMPLATES2_EXPORT void QQuickTemplates_initializeModule();
+Q_QUICKTEMPLATES2_EXPORT void qml_register_types_QtQuick_Templates();
+
+[[maybe_unused]] static inline QString backgroundName() { return QStringLiteral("background"); }
+[[maybe_unused]] static inline QString handleName() { return QStringLiteral("handle"); }
+[[maybe_unused]] static inline QString indicatorName() { return QStringLiteral("indicator"); }
QT_END_NAMESPACE
diff --git a/src/quicktestutils/CMakeLists.txt b/src/quicktestutils/CMakeLists.txt
index 25f8f38204..da1936542c 100644
--- a/src/quicktestutils/CMakeLists.txt
+++ b/src/quicktestutils/CMakeLists.txt
@@ -34,6 +34,7 @@ qt_internal_add_module(QuickTestUtilsPrivate
Qt::Network
Qt::Qml
Qt::QmlPrivate
+ NO_GENERATE_CPP_EXPORTS
)
if(TARGET Qt::Quick)
diff --git a/src/quicktestutils/qml/platforminputcontext_p.h b/src/quicktestutils/qml/platforminputcontext_p.h
index 8067ad876e..0f2af1e105 100644
--- a/src/quicktestutils/qml/platforminputcontext_p.h
+++ b/src/quicktestutils/qml/platforminputcontext_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PLATFORMINPUTCONTEXT_P_H
#define PLATFORMINPUTCONTEXT_P_H
diff --git a/src/quicktestutils/qml/platformquirks_p.h b/src/quicktestutils/qml/platformquirks_p.h
index beaf664f5d..afcadeacee 100644
--- a/src/quicktestutils/qml/platformquirks_p.h
+++ b/src/quicktestutils/qml/platformquirks_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PLATFORMQUIRKS_P_H
#define PLATFORMQUIRKS_P_H
@@ -17,7 +17,7 @@
#include <private/qglobal_p.h>
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
#include <Carbon/Carbon.h>
#endif
@@ -29,14 +29,26 @@ struct PlatformQuirks
{
#if !QT_CONFIG(clipboard)
return false;
-#elif defined(Q_OS_OSX)
+#elif defined(Q_OS_MACOS)
PasteboardRef pasteboard;
OSStatus status = PasteboardCreate(0, &pasteboard);
if (status == noErr)
CFRelease(pasteboard);
return status == noErr;
#else
- return true;
+ if (QGuiApplication::platformName() != QLatin1StringView("xcb"))
+ return true;
+
+ // On XCB a clipboard may be dysfunctional due to platform restrictions
+ QClipboard *clipBoard = QGuiApplication::clipboard();
+ if (!clipBoard)
+ return false;
+ const QString &oldText = clipBoard->text();
+ QScopeGuard guard([&](){ clipBoard->setText(oldText); });
+ const QLatin1StringView prefix("Something to prefix ");
+ const QString newText = prefix + oldText;
+ clipBoard->setText(newText);
+ return QTest::qWaitFor([&](){ return clipBoard->text() == newText; });
#endif
}
};
diff --git a/src/quicktestutils/qml/qmlutils.cpp b/src/quicktestutils/qml/qmlutils.cpp
index 05d94b68ae..08750790b3 100644
--- a/src/quicktestutils/qml/qmlutils.cpp
+++ b/src/quicktestutils/qml/qmlutils.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qmlutils_p.h"
@@ -7,6 +7,7 @@
#include <QtCore/QMutexLocker>
#include <QtQml/QQmlComponent>
#include <QtQml/QQmlEngine>
+#include <private/qqmlengine_p.h>
QT_BEGIN_NAMESPACE
@@ -109,6 +110,34 @@ QQmlTestMessageHandler::~QQmlTestMessageHandler()
QQmlTestMessageHandler::m_instance = nullptr;
}
+
+bool gcDone(const QV4::ExecutionEngine *engine) {
+ return !engine->memoryManager->gcStateMachine->inProgress();
+}
+
+void gc(QV4::ExecutionEngine &engine, GCFlags flags)
+{
+ engine.memoryManager->runGC();
+ while (!gcDone(&engine))
+ engine.memoryManager->gcStateMachine->step();
+ if (int(GCFlags::DontSendPostedEvents) & int(flags))
+ return;
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+}
+
+bool gcDone(QQmlEngine *engine) {
+ auto priv = QQmlEnginePrivate::get(engine);
+ return gcDone(priv->v4engine());
+}
+
+void gc(QQmlEngine &engine, GCFlags flags)
+{
+ auto priv = QQmlEnginePrivate::get(&engine);
+ gc(*priv->v4engine(), flags);
+}
+
+
QT_END_NAMESPACE
#include "moc_qmlutils_p.cpp"
diff --git a/src/quicktestutils/qml/qmlutils_p.h b/src/quicktestutils/qml/qmlutils_p.h
index 47825a3793..d8d28256ac 100644
--- a/src/quicktestutils/qml/qmlutils_p.h
+++ b/src/quicktestutils/qml/qmlutils_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QQMLTESTUTILS_P_H
#define QQMLTESTUTILS_P_H
@@ -106,6 +106,22 @@ private:
bool m_includeCategories;
};
+class QQmlEngine;
+
+namespace QV4 {
+struct ExecutionEngine;
+}
+
+enum class GCFlags {
+ None = 0,
+ DontSendPostedEvents = 1
+};
+
+bool gcDone(const QV4::ExecutionEngine *engine);
+void gc(QV4::ExecutionEngine &engine, GCFlags flags = GCFlags::None);
+bool gcDone(QQmlEngine *engine);
+void gc(QQmlEngine &engine, GCFlags flags = GCFlags::None);
+
QT_END_NAMESPACE
#endif // QQMLTESTUTILS_P_H
diff --git a/src/quicktestutils/qml/testhttpserver.cpp b/src/quicktestutils/qml/testhttpserver.cpp
index 9873cd1827..4dfaf5acba 100644
--- a/src/quicktestutils/qml/testhttpserver.cpp
+++ b/src/quicktestutils/qml/testhttpserver.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testhttpserver_p.h"
#include <QTcpSocket>
@@ -158,12 +158,10 @@ bool TestHTTPServer::wait(const QUrl &expect, const QUrl &reply, const QUrl &bod
bool headers_done = false;
while (!(line = expectFile.readLine()).isEmpty()) {
line.replace('\r', "");
- if (line.at(0) == '\n') {
- headers_done = true;
- continue;
- }
if (headers_done) {
m_waitData.body.append(line);
+ } else if (line.at(0) == '\n') {
+ headers_done = true;
} else if (line.endsWith("{{Ignore}}\n")) {
m_waitData.headerPrefixes.append(line.left(line.size() - strlen("{{Ignore}}\n")));
} else {
diff --git a/src/quicktestutils/qml/testhttpserver_p.h b/src/quicktestutils/qml/testhttpserver_p.h
index 0620aa4e72..eb5571646a 100644
--- a/src/quicktestutils/qml/testhttpserver_p.h
+++ b/src/quicktestutils/qml/testhttpserver_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTHTTPSERVER_P_H
#define TESTHTTPSERVER_P_H
diff --git a/src/quicktestutils/quick/geometrytestutils.cpp b/src/quicktestutils/quick/geometrytestutils.cpp
index 2fe1138fbd..fd1bbf356c 100644
--- a/src/quicktestutils/quick/geometrytestutils.cpp
+++ b/src/quicktestutils/quick/geometrytestutils.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "geometrytestutils_p.h"
diff --git a/src/quicktestutils/quick/geometrytestutils_p.h b/src/quicktestutils/quick/geometrytestutils_p.h
index 605dfd9027..9dc5987bf4 100644
--- a/src/quicktestutils/quick/geometrytestutils_p.h
+++ b/src/quicktestutils/quick/geometrytestutils_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QQUICKGEOMETRYTESTUTIL_P_H
#define QQUICKGEOMETRYTESTUTIL_P_H
diff --git a/src/quicktestutils/quick/viewtestutils.cpp b/src/quicktestutils/quick/viewtestutils.cpp
index e8e5847548..053e864660 100644
--- a/src/quicktestutils/quick/viewtestutils.cpp
+++ b/src/quicktestutils/quick/viewtestutils.cpp
@@ -1,9 +1,10 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "viewtestutils_p.h"
#include <QtCore/QRandomGenerator>
+#include <QtCore/QTimer>
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickView>
#include <QtGui/QScreen>
@@ -12,7 +13,9 @@
#include <QtTest/QTest>
#include <QtQuick/private/qquickdeliveryagent_p_p.h>
+#if QT_CONFIG(quick_itemview)
#include <QtQuick/private/qquickitemview_p_p.h>
+#endif
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
@@ -391,6 +394,7 @@ void QQuickViewTestUtils::StressTestModel::updateModel()
}
}
+#if QT_CONFIG(quick_itemview) && defined(QT_BUILD_INTERNAL)
bool QQuickViewTestUtils::testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx)
{
QHash<QQuickItem*, int> uniqueItems;
@@ -427,6 +431,7 @@ bool QQuickViewTestUtils::testVisibleItems(const QQuickItemViewPrivate *priv, bo
return true;
}
+#endif
namespace QQuickTouchUtils {
diff --git a/src/quicktestutils/quick/viewtestutils_p.h b/src/quicktestutils/quick/viewtestutils_p.h
index ff8905afa6..beb524949d 100644
--- a/src/quicktestutils/quick/viewtestutils_p.h
+++ b/src/quicktestutils/quick/viewtestutils_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QQUICKVIEWTESTUTILS_P_H
#define QQUICKVIEWTESTUTILS_P_H
@@ -19,6 +19,7 @@
#include <QtQml/QQmlExpression>
#include <QtQuick/QQuickItem>
#include <QtCore/private/qglobal_p.h>
+#include <QtQuick/private/qtquickglobal_p.h>
QT_FORWARD_DECLARE_CLASS(QQuickView)
QT_FORWARD_DECLARE_CLASS(QQuickItemViewPrivate)
@@ -167,8 +168,10 @@ namespace QQuickViewTestUtils
int m_rowCount;
};
+#if QT_CONFIG(quick_itemview) && defined(QT_BUILD_INTERNAL)
[[nodiscard]] bool testVisibleItems(const QQuickItemViewPrivate *priv,
bool *nonUnique, FxViewItem **failItem, int *expectedIdx);
+#endif
}
namespace QQuickTouchUtils {
diff --git a/src/quicktestutils/quick/visualtestutils.cpp b/src/quicktestutils/quick/visualtestutils.cpp
index 12da74d519..eb04bf086b 100644
--- a/src/quicktestutils/quick/visualtestutils.cpp
+++ b/src/quicktestutils/quick/visualtestutils.cpp
@@ -1,13 +1,17 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "visualtestutils_p.h"
#include <QtCore/QCoreApplication>
+#include <QtCore/private/qvariantanimation_p.h>
#include <QtCore/QDebug>
#include <QtQuick/QQuickItem>
+#if QT_CONFIG(quick_itemview)
#include <QtQuick/private/qquickitemview_p.h>
+#endif
#include <QtQuickTest/QtQuickTest>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
QT_BEGIN_NAMESPACE
@@ -58,7 +62,62 @@ void QQuickVisualTestUtils::centerOnScreen(QQuickWindow *window)
window->setFramePosition(screenGeometry.center() - offset);
}
-bool QQuickVisualTestUtils::delegateVisible(QQuickItem *item)
+QPoint QQuickVisualTestUtils::lerpPoints(const QPoint &point1, const QPoint &point2, qreal t)
+{
+ return QPoint(_q_interpolate(point1.x(), point2.x(), t), _q_interpolate(point1.y(), point2.y(), t));
+};
+
+/*!
+ \internal
+
+ Convenience class to linearly interpolate between two pointer move points.
+
+ \code
+ PointLerper pointLerper(window);
+ // Lerps from {0, 0} to {15, 15}.
+ pointLerper.move(15, 15);
+ QVERIFY(parentButton->isHovered());
+
+ // Lerps from {15, 15} to {25, 25}.
+ pointLerper.move(25, 25);
+ QVERIFY(childButton->isHovered());
+ \endcode
+*/
+QQuickVisualTestUtils::PointLerper::PointLerper(QQuickWindow *window, const QPointingDevice *pointingDevice)
+ : mWindow(window)
+ , mPointingDevice(pointingDevice)
+{
+}
+
+/*!
+ \internal
+
+ Moves from the last pos (or {0, 0} if there have been no calls
+ to this function yet) to \a pos using linear interpolation
+ over 10 (default value) steps with 1 ms (default value) delays
+ between each step.
+*/
+void QQuickVisualTestUtils::PointLerper::move(const QPoint &pos, int steps, int delayInMilliseconds)
+{
+ forEachStep(steps, [&](qreal progress) {
+ QQuickTest::pointerMove(mPointingDevice, mWindow, 0, lerpPoints(mFrom, pos, progress));
+ QTest::qWait(delayInMilliseconds);
+ });
+ mFrom = pos;
+};
+
+void QQuickVisualTestUtils::PointLerper::move(int x, int y, int steps, int delayInMilliseconds)
+{
+ move(QPoint(x, y), steps, delayInMilliseconds);
+};
+
+/*!
+ \internal
+
+ Returns \c true if \c {item->isVisible()} returns \c true, and
+ the item is not culled.
+*/
+bool QQuickVisualTestUtils::isDelegateVisible(QQuickItem *item)
{
return item->isVisible() && !QQuickItemPrivate::get(item)->culled;
}
@@ -111,6 +170,7 @@ bool QQuickVisualTestUtils::compareImages(const QImage &ia, const QImage &ib, QS
return true;
}
+#if QT_CONFIG(quick_itemview)
/*!
\internal
@@ -139,6 +199,7 @@ QQuickItem *QQuickVisualTestUtils::findViewDelegateItem(QQuickItemView *itemView
return itemView->itemAtIndex(index);
}
+#endif
QQuickVisualTestUtils::QQuickApplicationHelper::QQuickApplicationHelper(QQmlDataTest *testCase,
const QString &testFilePath, const QVariantMap &initialProperties, const QStringList &qmlImportPaths)
@@ -198,6 +259,21 @@ void QQuickVisualTestUtils::MnemonicKeySimulator::click(Qt::Key key)
release(key);
}
+QPoint QQuickVisualTestUtils::mapCenterToWindow(const QQuickItem *item)
+{
+ return item->mapToScene(QPointF(item->width() / 2, item->height() / 2)).toPoint();
+}
+
+QPoint QQuickVisualTestUtils::mapToWindow(const QQuickItem *item, qreal relativeX, qreal relativeY)
+{
+ return item->mapToScene(QPointF(relativeX, relativeY)).toPoint();
+}
+
+QPoint QQuickVisualTestUtils::mapToWindow(const QQuickItem *item, const QPointF &relativePos)
+{
+ return mapToWindow(item, relativePos.x(), relativePos.y());
+}
+
QT_END_NAMESPACE
#include "moc_visualtestutils_p.cpp"
diff --git a/src/quicktestutils/quick/visualtestutils_p.h b/src/quicktestutils/quick/visualtestutils_p.h
index ae618a04e0..48f8b2d8f9 100644
--- a/src/quicktestutils/quick/visualtestutils_p.h
+++ b/src/quicktestutils/quick/visualtestutils_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QQUICKVISUALTESTUTILS_P_H
#define QQUICKVISUALTESTUTILS_P_H
@@ -20,6 +20,8 @@
#include <private/qmlutils_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickItemView;
@@ -34,7 +36,33 @@ namespace QQuickVisualTestUtils
void moveMouseAway(QQuickWindow *window);
void centerOnScreen(QQuickWindow *window);
- [[nodiscard]] bool delegateVisible(QQuickItem *item);
+ template<typename F>
+ void forEachStep(int steps, F &&func)
+ {
+ for (int i = 0; i < steps; ++i) {
+ const qreal progress = qreal(i) / steps;
+ func(progress);
+ }
+ }
+
+ [[nodiscard]] QPoint lerpPoints(const QPoint &point1, const QPoint &point2, qreal t);
+
+ class [[nodiscard]] PointLerper
+ {
+ public:
+ PointLerper(QQuickWindow *window,
+ const QPointingDevice *pointingDevice = QPointingDevice::primaryPointingDevice());
+
+ void move(const QPoint &pos, int steps = 10, int delayInMilliseconds = 1);
+ void move(int x, int y, int steps = 10, int delayInMilliseconds = 1);
+
+ private:
+ QQuickWindow *mWindow = nullptr;
+ const QPointingDevice *mPointingDevice = nullptr;
+ QPoint mFrom;
+ };
+
+ [[nodiscard]] bool isDelegateVisible(QQuickItem *item);
/*
Find an item with the specified objectName. If index is supplied then the
@@ -131,8 +159,10 @@ namespace QQuickVisualTestUtils
};
Q_DECLARE_FLAGS(FindViewDelegateItemFlags, FindViewDelegateItemFlag)
+#if QT_CONFIG(quick_itemview)
QQuickItem* findViewDelegateItem(QQuickItemView *itemView, int index,
FindViewDelegateItemFlags flags = FindViewDelegateItemFlag::PositionViewAtIndex);
+#endif
/*!
\internal
@@ -187,6 +217,10 @@ namespace QQuickVisualTestUtils
QPointer<QWindow> m_window;
Qt::KeyboardModifiers m_modifiers;
};
+
+ QPoint mapCenterToWindow(const QQuickItem *item);
+ QPoint mapToWindow(const QQuickItem *item, qreal relativeX, qreal relativeY);
+ QPoint mapToWindow(const QQuickItem *item, const QPointF &relativePos);
}
#define QQUICK_VERIFY_POLISH(item) \
diff --git a/src/quickvectorimage/CMakeLists.txt b/src/quickvectorimage/CMakeLists.txt
new file mode 100644
index 0000000000..ecca0432e5
--- /dev/null
+++ b/src/quickvectorimage/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## QuickVectorGraphics Module:
+#####################################################################
+
+qt_internal_add_module(QuickVectorImageGeneratorPrivate
+ INTERNAL_MODULE
+ SOURCES
+ generator/qsvgvisitorimpl_p.h generator/qsvgvisitorimpl.cpp
+ generator/qquickgenerator_p.h generator/qquickgenerator.cpp
+ generator/qquickitemgenerator_p.h generator/qquickitemgenerator.cpp
+ generator/qquickqmlgenerator_p.h generator/qquickqmlgenerator.cpp
+ generator/qquicknodeinfo_p.h
+ generator/utils_p.h
+ qquickvectorimageglobal_p.h
+ LIBRARIES
+ Qt::Core
+ Qt::QuickPrivate
+ Qt::QuickShapesPrivate
+ Qt::SvgPrivate
+ GENERATE_CPP_EXPORTS
+)
+
+qt_internal_add_qml_module(QuickVectorImage
+ URI "QtQuick.VectorImage"
+ VERSION "${PROJECT_VERSION}"
+ PLUGIN_TARGET qquickvectorimageplugin
+ NO_PLUGIN_OPTIONAL
+ CLASS_NAME QtQuickVectorImagePlugin
+ SOURCES
+ qquickvectorimage_p.h qquickvectorimage.cpp
+ qquickvectorimage_p_p.h
+ LIBRARIES
+ Qt::QuickPrivate
+ Qt::QuickVectorImageGeneratorPrivate
+ Qt::SvgPrivate
+ GENERATE_CPP_EXPORTS
+)
diff --git a/src/quickvectorimage/generator/qquickgenerator.cpp b/src/quickvectorimage/generator/qquickgenerator.cpp
new file mode 100644
index 0000000000..505b10f919
--- /dev/null
+++ b/src/quickvectorimage/generator/qquickgenerator.cpp
@@ -0,0 +1,80 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickgenerator_p.h"
+#include "qsvgvisitorimpl_p.h"
+#include "qquicknodeinfo_p.h"
+
+#include <private/qsgcurveprocessor_p.h>
+#include <private/qquickshape_p.h>
+#include <private/qquadpath_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qquickimagebase_p_p.h>
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQuickVectorImage, "qt.quick.vectorimage", QtWarningMsg)
+
+QQuickGenerator::QQuickGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags)
+ : m_flags(flags)
+ , m_fileName(fileName)
+ , m_loader(nullptr)
+{
+}
+
+QQuickGenerator::~QQuickGenerator()
+{
+ delete m_loader;
+}
+
+void QQuickGenerator::setGeneratorFlags(QQuickVectorImageGenerator::GeneratorFlags flags)
+{
+ m_flags = flags;
+}
+
+QQuickVectorImageGenerator::GeneratorFlags QQuickGenerator::generatorFlags()
+{
+ return m_flags;
+}
+
+void QQuickGenerator::generate()
+{
+ m_loader = new QSvgVisitorImpl(m_fileName, this);
+ m_loader->traverse();
+}
+
+void QQuickGenerator::optimizePaths(const PathNodeInfo &info)
+{
+ QPainterPath pathCopy = info.painterPath;
+ pathCopy.setFillRule(info.fillRule);
+
+ if (m_flags.testFlag(QQuickVectorImageGenerator::GeneratorFlag::OptimizePaths)) {
+ QQuadPath strokePath = QQuadPath::fromPainterPath(pathCopy);
+ bool fillPathNeededClose;
+ QQuadPath fillPath = strokePath.subPathsClosed(&fillPathNeededClose);
+ const bool intersectionsFound = QSGCurveProcessor::solveIntersections(fillPath, false);
+ fillPath.addCurvatureData();
+ QSGCurveProcessor::solveOverlaps(fillPath);
+ const bool compatibleStrokeAndFill = !fillPathNeededClose && !intersectionsFound;
+ if (compatibleStrokeAndFill || m_flags.testFlag(QQuickVectorImageGenerator::GeneratorFlag::OutlineStrokeMode)) {
+ outputShapePath(info, nullptr, &fillPath, QQuickVectorImageGenerator::FillAndStroke, pathCopy.boundingRect());
+ } else {
+ outputShapePath(info, nullptr, &fillPath, QQuickVectorImageGenerator::FillPath, pathCopy.boundingRect());
+ outputShapePath(info, nullptr, &strokePath, QQuickVectorImageGenerator::StrokePath, pathCopy.boundingRect());
+ }
+ } else {
+ outputShapePath(info, &pathCopy, nullptr, QQuickVectorImageGenerator::FillAndStroke, pathCopy.boundingRect());
+ }
+}
+
+bool QQuickGenerator::isNodeVisible(const NodeInfo &info)
+{
+ if (!info.isVisible || !info.isDisplayed)
+ return false;
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickvectorimage/generator/qquickgenerator_p.h b/src/quickvectorimage/generator/qquickgenerator_p.h
new file mode 100644
index 0000000000..c951ad800c
--- /dev/null
+++ b/src/quickvectorimage/generator/qquickgenerator_p.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKGENERATOR_P_H
+#define QQUICKGENERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qquickvectorimageglobal_p.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSvgVisitorImpl;
+class QPainterPath;
+class QGradient;
+class QQuickShapePath;
+class QQuadPath;
+class QQuickItem;
+class QQuickShape;
+class QRectF;
+
+struct NodeInfo;
+struct ImageNodeInfo;
+struct PathNodeInfo;
+struct TextNodeInfo;
+struct UseNodeInfo;
+struct StructureNodeInfo;
+
+class Q_QUICKVECTORIMAGEGENERATOR_EXPORT QQuickGenerator
+{
+public:
+ QQuickGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags);
+ virtual ~QQuickGenerator();
+
+ void setGeneratorFlags(QQuickVectorImageGenerator::GeneratorFlags flags);
+ QQuickVectorImageGenerator::GeneratorFlags generatorFlags();
+
+ void generate();
+
+protected:
+ virtual void generateNodeBase(const NodeInfo &info) = 0;
+ virtual bool generateDefsNode(const NodeInfo &info) = 0;
+ virtual void generateImageNode(const ImageNodeInfo &info) = 0;
+ virtual void generatePath(const PathNodeInfo &info) = 0;
+ virtual void generateNode(const NodeInfo &info) = 0;
+ virtual void generateTextNode(const TextNodeInfo &info) = 0;
+ virtual void generateUseNode(const UseNodeInfo &info) = 0;
+ virtual bool generateStructureNode(const StructureNodeInfo &info) = 0;
+ virtual bool generateRootNode(const StructureNodeInfo &info) = 0;
+ virtual void outputShapePath(const PathNodeInfo &info, const QPainterPath *path, const QQuadPath *quadPath, QQuickVectorImageGenerator::PathSelector pathSelector, const QRectF &boundingRect) = 0;
+ void optimizePaths(const PathNodeInfo &info);
+ bool isNodeVisible(const NodeInfo &info);
+
+protected:
+ QQuickVectorImageGenerator::GeneratorFlags m_flags;
+
+private:
+ QString m_fileName;
+ QSvgVisitorImpl *m_loader;
+ friend class QSvgVisitorImpl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKGENERATOR_P_H
diff --git a/src/quickvectorimage/generator/qquickitemgenerator.cpp b/src/quickvectorimage/generator/qquickitemgenerator.cpp
new file mode 100644
index 0000000000..171cb6889f
--- /dev/null
+++ b/src/quickvectorimage/generator/qquickitemgenerator.cpp
@@ -0,0 +1,393 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickitemgenerator_p.h"
+#include "utils_p.h"
+#include "qquicknodeinfo_p.h"
+
+#include <private/qsgcurveprocessor_p.h>
+#include <private/qquickshape_p.h>
+#include <private/qquadpath_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qquickimagebase_p_p.h>
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcQuickVectorImage)
+
+QQuickItemGenerator::QQuickItemGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags, QQuickItem *parentItem)
+ :QQuickGenerator(fileName, flags)
+{
+ Q_ASSERT(parentItem);
+ m_items.push(parentItem);
+ m_parentItem = parentItem;
+}
+
+QQuickItemGenerator::~QQuickItemGenerator()
+{
+}
+
+void QQuickItemGenerator::generateNodeBase(const NodeInfo &info)
+{
+ if (!info.isDefaultTransform) {
+ auto sx = info.transform.m11();
+ auto sy = info.transform.m22();
+ auto x = info.transform.m31();
+ auto y = info.transform.m32();
+
+ auto xformProp = currentItem()->transform();
+ if (info.transform.type() == QTransform::TxTranslate) {
+ auto *translate = new QQuickTranslate;
+ translate->setX(x);
+ translate->setY(y);
+ xformProp.append(&xformProp, translate);
+ } else if (info.transform.type() == QTransform::TxScale && !x && !y) {
+ auto scale = new QQuickScale;
+ scale->setParent(currentItem());
+ scale->setXScale(sx);
+ scale->setYScale(sy);
+ xformProp.append(&xformProp, scale);
+ } else {
+ const QMatrix4x4 m(info.transform);
+ auto xform = new QQuickMatrix4x4;
+ xform->setMatrix(m);
+ xformProp.append(&xformProp, xform);
+ }
+ }
+ if (!info.isDefaultOpacity) {
+ currentItem()->setOpacity(info.opacity);
+ }
+}
+
+bool QQuickItemGenerator::generateDefsNode(const NodeInfo &info)
+{
+ Q_UNUSED(info)
+
+ return false;
+}
+
+void QQuickItemGenerator::generateImageNode(const ImageNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ auto *imageItem = new QQuickImage;
+ auto *imagePriv = static_cast<QQuickImageBasePrivate*>(QQuickItemPrivate::get(imageItem));
+ imagePriv->pix.setImage(info.image);
+
+ imageItem->setX(info.rect.x());
+ imageItem->setY(info.rect.y());
+ imageItem->setWidth(info.rect.width());
+ imageItem->setHeight(info.rect.height());
+
+ generateNodeBase(info);
+
+ addCurrentItem(imageItem, info);
+ m_items.pop();
+}
+
+void QQuickItemGenerator::generatePath(const PathNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ if (m_inShapeItem) {
+ if (!info.isDefaultTransform)
+ qCWarning(lcQuickVectorImage) << "Skipped transform for node" << info.nodeId << "type" << info.typeName << "(this is not supposed to happen)";
+ optimizePaths(info);
+ } else {
+ auto *shapeItem = new QQuickShape;
+ if (m_flags.testFlag(QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer))
+ shapeItem->setPreferredRendererType(QQuickShape::CurveRenderer);
+ shapeItem->setContainsMode(QQuickShape::ContainsMode::FillContains); // TODO: configurable?
+ addCurrentItem(shapeItem, info);
+ m_parentShapeItem = shapeItem;
+ m_inShapeItem = true;
+
+ generateNodeBase(info);
+
+ optimizePaths(info);
+ //qCDebug(lcQuickVectorGraphics) << *node->qpath();
+ m_items.pop();
+ m_inShapeItem = false;
+ m_parentShapeItem = nullptr;
+ }
+}
+
+void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPainterPath *painterPath, const QQuadPath *quadPath, QQuickVectorImageGenerator::PathSelector pathSelector, const QRectF &boundingRect)
+{
+ Q_UNUSED(pathSelector)
+ Q_ASSERT(painterPath || quadPath);
+
+ const bool noPen = info.strokeStyle.color == QColorConstants::Transparent;
+ if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
+ return;
+
+ const bool noFill = info.grad.type() == QGradient::NoGradient && info.fillColor == QColorConstants::Transparent;
+
+ if (pathSelector == QQuickVectorImageGenerator::FillPath && noFill)
+ return;
+
+ QQuickShapePath::FillRule fillRule = QQuickShapePath::FillRule(painterPath ? painterPath->fillRule() : quadPath->fillRule());
+
+ QQuickShapePath *shapePath = new QQuickShapePath;
+ Q_ASSERT(shapePath);
+
+ if (!info.nodeId.isEmpty())
+ shapePath->setObjectName(QStringLiteral("svg_path:") + info.nodeId);
+
+ if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
+ shapePath->setStrokeColor(Qt::transparent);
+ } else {
+ shapePath->setStrokeColor(info.strokeStyle.color);
+ shapePath->setStrokeWidth(info.strokeStyle.width);
+ shapePath->setCapStyle(QQuickShapePath::CapStyle(info.strokeStyle.lineCapStyle));
+ shapePath->setJoinStyle(QQuickShapePath::JoinStyle(info.strokeStyle.lineJoinStyle));
+ shapePath->setMiterLimit(info.strokeStyle.miterLimit);
+ if (info.strokeStyle.dashArray.length() != 0) {
+ shapePath->setStrokeStyle(QQuickShapePath::DashLine);
+ shapePath->setDashPattern(info.strokeStyle.dashArray.toVector());
+ shapePath->setDashOffset(info.strokeStyle.dashOffset);
+ }
+ }
+
+ if (!(pathSelector & QQuickVectorImageGenerator::FillPath))
+ shapePath->setFillColor(Qt::transparent);
+ else if (info.grad.type() != QGradient::NoGradient)
+ generateGradient(&info.grad, shapePath, boundingRect);
+ else
+ shapePath->setFillColor(info.fillColor);
+
+ shapePath->setFillRule(fillRule);
+
+ QString svgPathString = painterPath ? QQuickVectorImageGenerator::Utils::toSvgString(*painterPath) : QQuickVectorImageGenerator::Utils::toSvgString(*quadPath);
+
+ auto *pathSvg = new QQuickPathSvg;
+ pathSvg->setPath(svgPathString);
+ pathSvg->setParent(shapePath);
+
+ auto pathElementProp = shapePath->pathElements();
+ pathElementProp.append(&pathElementProp, pathSvg);
+
+ shapePath->setParent(currentItem());
+ auto shapeDataProp = m_parentShapeItem->data();
+ shapeDataProp.append(&shapeDataProp, shapePath);
+}
+
+void QQuickItemGenerator::generateGradient(const QGradient *grad, QQuickShapePath *shapePath, const QRectF &boundingRect)
+{
+ if (!shapePath)
+ return;
+
+ auto setStops = [=](QQuickShapeGradient *quickGrad, const QGradientStops &stops) {
+ auto stopsProp = quickGrad->stops();
+ for (auto &stop : stops) {
+ auto *stopObj = new QQuickGradientStop(quickGrad);
+ stopObj->setPosition(stop.first);
+ stopObj->setColor(stop.second);
+ stopsProp.append(&stopsProp, stopObj);
+ }
+ };
+
+ if (grad->type() == QGradient::LinearGradient) {
+ auto *linGrad = static_cast<const QLinearGradient *>(grad);
+
+ QRectF gradRect(linGrad->start(), linGrad->finalStop());
+ QRectF logRect = linGrad->coordinateMode() == QGradient::LogicalMode ? gradRect : QQuickVectorImageGenerator::Utils::mapToQtLogicalMode(gradRect, boundingRect);
+
+ auto *quickGrad = new QQuickShapeLinearGradient(shapePath);
+ quickGrad->setX1(logRect.left());
+ quickGrad->setY1(logRect.top());
+ quickGrad->setX2(logRect.right());
+ quickGrad->setY2(logRect.bottom());
+ setStops(quickGrad, linGrad->stops());
+
+ shapePath->setFillGradient(quickGrad);
+ } else if (grad->type() == QGradient::RadialGradient) {
+ auto *radGrad = static_cast<const QRadialGradient*>(grad);
+ auto *quickGrad = new QQuickShapeRadialGradient(shapePath);
+ quickGrad->setCenterX(radGrad->center().x());
+ quickGrad->setCenterY(radGrad->center().y());
+ quickGrad->setCenterRadius(radGrad->radius());
+ quickGrad->setFocalX(radGrad->focalPoint().x());
+ quickGrad->setFocalY(radGrad->focalPoint().y());
+ setStops(quickGrad, radGrad->stops());
+
+ shapePath->setFillGradient(quickGrad);
+ }
+}
+
+void QQuickItemGenerator::generateNode(const NodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ qCWarning(lcQuickVectorImage) << "SVG NODE NOT IMPLEMENTED: "
+ << info.nodeId
+ << " type: " << info.typeName;
+}
+
+void QQuickItemGenerator::generateTextNode(const TextNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ QQuickItem *alignItem = nullptr;
+ QQuickText *textItem = nullptr;
+
+ QQuickItem *containerItem = new QQuickItem(currentItem());
+ addCurrentItem(containerItem, info);
+
+ generateNodeBase(info);
+
+ if (!info.isTextArea) {
+ alignItem = new QQuickItem(currentItem());
+ alignItem->setX(info.position.x());
+ alignItem->setY(info.position.y());
+ }
+
+ textItem = new QQuickText(containerItem);
+ addCurrentItem(textItem, info);
+
+ if (info.isTextArea) {
+ textItem->setX(info.position.x());
+ textItem->setY(info.position.y());
+ if (info.size.width() > 0)
+ textItem->setWidth(info.size.width());
+ if (info.size.height() > 0)
+ textItem->setHeight(info.size.height());
+ textItem->setWrapMode(QQuickText::Wrap);
+ textItem->setClip(true);
+ } else {
+ auto *anchors = QQuickItemPrivate::get(textItem)->anchors();
+ auto *alignPrivate = QQuickItemPrivate::get(alignItem);
+ anchors->setBaseline(alignPrivate->top());
+
+ switch (info.alignment) {
+ case Qt::AlignHCenter:
+ anchors->setHorizontalCenter(alignPrivate->left());
+ break;
+ case Qt::AlignRight:
+ anchors->setRight(alignPrivate->left());
+ break;
+ default:
+ qCDebug(lcQuickVectorImage) << "Unexpected text alignment" << info.alignment;
+ Q_FALLTHROUGH();
+ case Qt::AlignLeft:
+ anchors->setLeft(alignPrivate->left());
+ break;
+ }
+ }
+
+ textItem->setColor(info.fillColor);
+ textItem->setTextFormat(info.needsRichText ? QQuickText::RichText : QQuickText::StyledText);
+ textItem->setText(info.text);
+ textItem->setFont(info.font);
+
+ if (info.strokeColor != QColorConstants::Transparent) {
+ textItem->setStyleColor(info.strokeColor);
+ textItem->setStyle(QQuickText::Outline);
+ }
+
+ m_items.pop(); m_items.pop();
+}
+
+void QQuickItemGenerator::generateUseNode(const UseNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ if (info.stage == StructureNodeStage::Start) {
+ QQuickItem *item = new QQuickItem();
+ item->setPosition(info.startPos);
+ addCurrentItem(item, info);
+ generateNodeBase(info);
+ } else {
+ m_items.pop();
+ }
+
+}
+
+bool QQuickItemGenerator::generateStructureNode(const StructureNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return false;
+
+ if (info.stage == StructureNodeStage::Start) {
+ if (!info.forceSeparatePaths && info.isPathContainer) {
+ m_inShapeItem = true;
+ auto *shapeItem = new QQuickShape;
+ if (m_flags.testFlag(QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer))
+ shapeItem->setPreferredRendererType(QQuickShape::CurveRenderer);
+ m_parentShapeItem = shapeItem;
+ addCurrentItem(shapeItem, info);
+ } else {
+ QQuickItem *item = !info.viewBox.isEmpty() ? new QQuickVectorImageGenerator::Utils::ViewBoxItem(info.viewBox) : new QQuickItem;
+ addCurrentItem(item, info);
+ }
+
+ generateNodeBase(info);
+ } else {
+ m_inShapeItem = false;
+ m_parentShapeItem = nullptr;
+ m_items.pop();
+ }
+
+ return true;
+}
+
+bool QQuickItemGenerator::generateRootNode(const StructureNodeInfo &info)
+{
+ if (!isNodeVisible(info)) {
+ QQuickItem *item = new QQuickItem();
+ item->setParentItem(m_parentItem);
+
+ if (info.size.width() > 0)
+ m_parentItem->setImplicitWidth(info.size.width());
+
+ if (info.size.height() > 0)
+ m_parentItem->setImplicitHeight(info.size.height());
+
+ item->setWidth(m_parentItem->implicitWidth());
+ item->setHeight(m_parentItem->implicitHeight());
+
+ return false;
+ }
+
+ if (info.stage == StructureNodeStage::Start) {
+ QQuickItem *item = !info.viewBox.isEmpty() ? new QQuickVectorImageGenerator::Utils::ViewBoxItem(info.viewBox) : new QQuickItem;
+ addCurrentItem(item, info);
+ if (info.size.width() > 0)
+ m_parentItem->setImplicitWidth(info.size.width());
+
+ if (info.size.height() > 0)
+ m_parentItem->setImplicitHeight(info.size.height());
+
+ item->setWidth(m_parentItem->implicitWidth());
+ item->setHeight(m_parentItem->implicitHeight());
+ generateNodeBase(info);
+ } else {
+ m_inShapeItem = false;
+ m_parentShapeItem = nullptr;
+ m_items.pop();
+ }
+
+ return true;
+}
+
+QQuickItem *QQuickItemGenerator::currentItem()
+{
+ return m_items.top();
+}
+
+void QQuickItemGenerator::addCurrentItem(QQuickItem *item, const NodeInfo &info)
+{
+ item->setParentItem(currentItem());
+ m_items.push(item);
+ QStringView name = !info.nodeId.isEmpty() ? info.nodeId : info.typeName;
+ item->setObjectName(name);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickvectorimage/generator/qquickitemgenerator_p.h b/src/quickvectorimage/generator/qquickitemgenerator_p.h
new file mode 100644
index 0000000000..e96812ec30
--- /dev/null
+++ b/src/quickvectorimage/generator/qquickitemgenerator_p.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKITEMGENERATOR_P_H
+#define QQUICKITEMGENERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickgenerator_p.h"
+#include <QStack>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKVECTORIMAGEGENERATOR_EXPORT QQuickItemGenerator : public QQuickGenerator
+{
+public:
+ QQuickItemGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags, QQuickItem *parentItem);
+ ~QQuickItemGenerator();
+
+protected:
+ void generateNodeBase(const NodeInfo &info) override;
+ bool generateDefsNode(const NodeInfo &info) override;
+ void generateImageNode(const ImageNodeInfo &info) override;
+ void generatePath(const PathNodeInfo &info) override;
+ void generateNode(const NodeInfo &info) override;
+ void generateTextNode(const TextNodeInfo &info) override;
+ void generateUseNode(const UseNodeInfo &info) override;
+ bool generateStructureNode(const StructureNodeInfo &info) override;
+ bool generateRootNode(const StructureNodeInfo &info) override;
+ void outputShapePath(const PathNodeInfo &info, const QPainterPath *path, const QQuadPath *quadPath, QQuickVectorImageGenerator::PathSelector pathSelector, const QRectF &boundingRect) override;
+
+private:
+ void generateGradient(const QGradient *grad, QQuickShapePath *shapePath, const QRectF &boundingRect);
+ QQuickItem *currentItem();
+ void addCurrentItem(QQuickItem *item, const NodeInfo &info);
+
+ bool m_inShapeItem = false;
+ QQuickShape *m_parentShapeItem = nullptr;
+
+ QStack<QQuickItem *> m_items;
+
+ QQuickItem *m_parentItem = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKITEMGENERATOR_P_H
diff --git a/src/quickvectorimage/generator/qquicknodeinfo_p.h b/src/quickvectorimage/generator/qquicknodeinfo_p.h
new file mode 100644
index 0000000000..9af3d0b03c
--- /dev/null
+++ b/src/quickvectorimage/generator/qquicknodeinfo_p.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKNODEINFO_P_H
+#define QQUICKNODEINFO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QString>
+#include <QPainter>
+#include <QPainterPath>
+#include <QMatrix4x4>
+#include <QQuickItem>
+#include <private/qquicktext_p.h>
+#include <private/qquicktranslate_p.h>
+#include <private/qquickimage_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct NodeInfo
+{
+ QString nodeId;
+ QString typeName;
+ QTransform transform;
+ qreal opacity;
+ bool isDefaultTransform;
+ bool isDefaultOpacity;
+ bool isVisible;
+ bool isDisplayed; // TODO: Map to display enum in QtSvg
+};
+
+struct ImageNodeInfo : NodeInfo
+{
+ QImage image;
+ QRectF rect;
+ QString externalFileReference;
+};
+
+struct StrokeStyle
+{
+ Qt::PenCapStyle lineCapStyle = Qt::SquareCap;
+ Qt::PenJoinStyle lineJoinStyle = Qt::MiterJoin;
+ qreal miterLimit = 4;
+ qreal dashOffset = 0;
+ QList<qreal> dashArray;
+ QColor color = QColorConstants::Transparent;
+ qreal width = 1.0;
+};
+
+struct PathNodeInfo : NodeInfo
+{
+ QPainterPath painterPath;
+ Qt::FillRule fillRule = Qt::FillRule::WindingFill;
+ QColor fillColor;
+ StrokeStyle strokeStyle;
+ QGradient grad;
+};
+
+struct TextNodeInfo : NodeInfo
+{
+ bool isTextArea;
+ bool needsRichText;
+ QPointF position;
+ QSizeF size;
+ QString text;
+ QFont font;
+ Qt::Alignment alignment;
+ QColor fillColor;
+ QColor strokeColor;
+};
+
+enum class StructureNodeStage
+{
+ Start,
+ End
+};
+
+struct UseNodeInfo : NodeInfo
+{
+ QPointF startPos;
+ StructureNodeStage stage;
+};
+
+struct StructureNodeInfo : NodeInfo
+{
+ StructureNodeStage stage;
+ bool forceSeparatePaths;
+ QRectF viewBox;
+ QSize size;
+ bool isPathContainer;
+};
+
+
+QT_END_NAMESPACE
+
+#endif //QQUICKNODEINFO_P_H
diff --git a/src/quickvectorimage/generator/qquickqmlgenerator.cpp b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
new file mode 100644
index 0000000000..fdb59c7ef4
--- /dev/null
+++ b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
@@ -0,0 +1,578 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickqmlgenerator_p.h"
+#include "qquicknodeinfo_p.h"
+#include "utils_p.h"
+
+#include <private/qsgcurveprocessor_p.h>
+#include <private/qquickshape_p.h>
+#include <private/qquadpath_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qquickimagebase_p_p.h>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qdir.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcQuickVectorImage)
+
+class GeneratorStream
+{
+public:
+ explicit GeneratorStream(QTextStream *result)
+ : m_array(new QByteArray())
+ , m_stream(new QTextStream(m_array, QIODeviceBase::ReadWrite))
+ , m_resultStream(result)
+ {}
+
+ ~GeneratorStream()
+ {
+ if (m_stream) {
+ m_stream->flush();
+ delete m_stream;
+ }
+
+ if (m_resultStream && m_array && !m_array->isEmpty())
+ *m_resultStream << *m_array << Qt::endl;
+
+ delete m_array;
+ }
+
+ GeneratorStream(GeneratorStream &&other) noexcept
+ : m_array(std::exchange(other.m_array, nullptr))
+ , m_stream(std::exchange(other.m_stream, nullptr))
+ , m_resultStream(std::exchange(other.m_resultStream, nullptr))
+ {}
+ GeneratorStream &operator=(GeneratorStream &&other) noexcept
+ {
+ std::swap(m_resultStream, other.m_resultStream);
+ std::swap(m_stream, other.m_stream);
+ std::swap(m_array, other.m_array);
+
+ return *this;
+ }
+
+ Q_DISABLE_COPY(GeneratorStream)
+private:
+ template<typename T>
+ friend const GeneratorStream &operator<<(const GeneratorStream& str, T val);
+ QByteArray *m_array = nullptr;
+ QTextStream *m_stream = nullptr;
+ QTextStream *m_resultStream = nullptr;
+};
+
+template<typename T>
+const GeneratorStream &operator<<(const GeneratorStream& str, T val)
+{
+ *str.m_stream << val;
+ return str;
+}
+
+QQuickQmlGenerator::QQuickQmlGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags, const QString &outFileName)
+ : QQuickGenerator(fileName, flags)
+ , outputFileName(outFileName)
+{
+ m_stream = new QTextStream(&result);
+}
+
+QQuickQmlGenerator::~QQuickQmlGenerator()
+{
+ if (!outputFileName.isEmpty()) {
+ QFile outFile(outputFileName);
+ outFile.open(QIODevice::WriteOnly);
+ outFile.write(result);
+ outFile.close();
+ }
+
+ if (lcQuickVectorImage().isDebugEnabled()) {
+ result.truncate(300);
+ qCDebug(lcQuickVectorImage).noquote() << result;
+ }
+}
+
+void QQuickQmlGenerator::setShapeTypeName(const QString &name)
+{
+ m_shapeTypeName = name.toLatin1();
+}
+
+QString QQuickQmlGenerator::shapeTypeName() const
+{
+ return QString::fromLatin1(m_shapeTypeName);
+}
+
+void QQuickQmlGenerator::setCommentString(const QString commentString)
+{
+ m_commentString = commentString;
+}
+
+QString QQuickQmlGenerator::commentString() const
+{
+ return m_commentString;
+}
+
+void QQuickQmlGenerator::generateNodeBase(const NodeInfo &info)
+{
+ m_indentLevel++;
+ if (!info.nodeId.isEmpty())
+ stream() << "objectName: \"" << info.nodeId << "\"";
+ if (!info.isDefaultTransform) {
+ auto sx = info.transform.m11();
+ auto sy = info.transform.m22();
+ auto x = info.transform.m31();
+ auto y = info.transform.m32();
+ if (info.transform.type() == QTransform::TxTranslate) {
+ stream() << "transform: Translate { " << "x: " << x << "; y: " << y << " }";
+ } else if (info.transform.type() == QTransform::TxScale && !x && !y) {
+ stream() << "transform: Scale { xScale: " << sx << "; yScale: " << sy << " }";
+ } else {
+ const QMatrix4x4 m(info.transform);
+ {
+ stream() << "transform: [ Matrix4x4 { matrix: Qt.matrix4x4 (";
+ m_indentLevel += 3;
+ const auto *data = m.data();
+ for (int i = 0; i < 4; i++) {
+ stream() << data[i] << ", " << data[i+4] << ", " << data[i+8] << ", " << data[i+12] << ", ";
+ }
+ stream() << ") } ]";
+ m_indentLevel -= 3;
+ }
+ }
+ }
+ if (!info.isDefaultOpacity) {
+ stream() << "opacity: " << info.opacity;
+ }
+ m_indentLevel--;
+}
+
+bool QQuickQmlGenerator::generateDefsNode(const NodeInfo &info)
+{
+ Q_UNUSED(info)
+
+ return false;
+}
+
+void QQuickQmlGenerator::generateImageNode(const ImageNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ const QFileInfo outputFileInfo(outputFileName);
+ const QDir outputDir(outputFileInfo.absolutePath());
+
+ QString filePath;
+
+ if (!m_retainFilePaths || info.externalFileReference.isEmpty()) {
+ filePath = m_assetFileDirectory;
+ if (filePath.isEmpty())
+ filePath = outputDir.absolutePath();
+
+ if (!filePath.isEmpty() && !filePath.endsWith(u'/'))
+ filePath += u'/';
+
+ QDir fileDir(filePath);
+ if (!fileDir.exists()) {
+ if (!fileDir.mkpath(QStringLiteral(".")))
+ qCWarning(lcQuickVectorImage) << "Failed to create image resource directory:" << filePath;
+ }
+
+ filePath += QStringLiteral("%1%2.png").arg(m_assetFilePrefix.isEmpty()
+ ? QStringLiteral("svg_asset_")
+ : m_assetFilePrefix)
+ .arg(info.image.cacheKey());
+
+ if (!info.image.save(filePath))
+ qCWarning(lcQuickVectorImage) << "Unabled to save image resource" << filePath;
+ qCDebug(lcQuickVectorImage) << "Saving copy of IMAGE" << filePath;
+ } else {
+ filePath = info.externalFileReference;
+ }
+
+ const QFileInfo assetFileInfo(filePath);
+
+ // TODO: this requires proper asset management.
+ stream() << "Image {";
+ m_indentLevel++;
+
+ generateNodeBase(info);
+ stream() << "x: " << info.rect.x();
+ stream() << "y: " << info.rect.y();
+ stream() << "width: " << info.rect.width();
+ stream() << "height: " << info.rect.height();
+ stream() << "source: \"" << outputDir.relativeFilePath(assetFileInfo.absoluteFilePath()) <<"\"";
+
+ m_indentLevel--;
+
+ stream() << "}";
+}
+
+void QQuickQmlGenerator::generatePath(const PathNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ if (m_inShapeItem) {
+ if (!info.isDefaultTransform)
+ qWarning() << "Skipped transform for node" << info.nodeId << "type" << info.typeName << "(this is not supposed to happen)";
+ optimizePaths(info);
+ } else {
+ m_inShapeItem = true;
+ stream() << shapeName() << " {";
+
+ // Check ??
+ generateNodeBase(info);
+
+ m_indentLevel++;
+ if (m_flags.testFlag(QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer))
+ stream() << "preferredRendererType: Shape.CurveRenderer";
+ optimizePaths(info);
+ //qCDebug(lcQuickVectorGraphics) << *node->qpath();
+ m_indentLevel--;
+ stream() << "}";
+ m_inShapeItem = false;
+ }
+}
+
+void QQuickQmlGenerator::generateGradient(const QGradient *grad, const QRectF &boundingRect)
+{
+ if (grad->type() == QGradient::LinearGradient) {
+ auto *linGrad = static_cast<const QLinearGradient *>(grad);
+ stream() << "fillGradient: LinearGradient {";
+ m_indentLevel++;
+
+ QRectF gradRect(linGrad->start(), linGrad->finalStop());
+ QRectF logRect = linGrad->coordinateMode() == QGradient::LogicalMode ? gradRect : QQuickVectorImageGenerator::Utils::mapToQtLogicalMode(gradRect, boundingRect);
+
+ stream() << "x1: " << logRect.left();
+ stream() << "y1: " << logRect.top();
+ stream() << "x2: " << logRect.right();
+ stream() << "y2: " << logRect.bottom();
+ for (auto &stop : linGrad->stops())
+ stream() << "GradientStop { position: " << stop.first << "; color: \"" << stop.second.name(QColor::HexArgb) << "\" }";
+ m_indentLevel--;
+ stream() << "}";
+ } else if (grad->type() == QGradient::RadialGradient) {
+ auto *radGrad = static_cast<const QRadialGradient*>(grad);
+ stream() << "fillGradient: RadialGradient {";
+ m_indentLevel++;
+
+ stream() << "centerX: " << radGrad->center().x();
+ stream() << "centerY: " << radGrad->center().y();
+ stream() << "centerRadius: " << radGrad->radius();
+ stream() << "focalX:" << radGrad->focalPoint().x();
+ stream() << "focalY:" << radGrad->focalPoint().y();
+ for (auto &stop : radGrad->stops())
+ stream() << "GradientStop { position: " << stop.first << "; color: \"" << stop.second.name(QColor::HexArgb) << "\" }";
+ m_indentLevel--;
+ stream() << "}";
+ }
+}
+
+void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainterPath *painterPath, const QQuadPath *quadPath, QQuickVectorImageGenerator::PathSelector pathSelector, const QRectF &boundingRect)
+{
+ Q_UNUSED(pathSelector)
+ Q_ASSERT(painterPath || quadPath);
+
+ const bool noPen = info.strokeStyle.color == QColorConstants::Transparent;
+ if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
+ return;
+
+ const bool noFill = info.grad.type() == QGradient::NoGradient && info.fillColor == QColorConstants::Transparent;
+
+ if (pathSelector == QQuickVectorImageGenerator::FillPath && noFill)
+ return;
+
+ auto fillRule = QQuickShapePath::FillRule(painterPath ? painterPath->fillRule() : quadPath->fillRule());
+ stream() << "ShapePath {";
+ m_indentLevel++;
+ if (!info.nodeId.isEmpty()) {
+ switch (pathSelector) {
+ case QQuickVectorImageGenerator::FillPath:
+ stream() << "objectName: \"svg_fill_path:" << info.nodeId << "\"";
+ break;
+ case QQuickVectorImageGenerator::StrokePath:
+ stream() << "objectName: \"svg_stroke_path:" << info.nodeId << "\"";
+ break;
+ case QQuickVectorImageGenerator::FillAndStroke:
+ stream() << "objectName: \"svg_path:" << info.nodeId << "\"";
+ break;
+ }
+ }
+
+ if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
+ stream() << "strokeColor: \"transparent\"";
+ } else {
+ stream() << "strokeColor: \"" << info.strokeStyle.color.name(QColor::HexArgb) << "\"";
+ stream() << "strokeWidth: " << info.strokeStyle.width;
+ stream() << "capStyle: " << QQuickVectorImageGenerator::Utils::strokeCapStyleString(info.strokeStyle.lineCapStyle);
+ stream() << "joinStyle: " << QQuickVectorImageGenerator::Utils::strokeJoinStyleString(info.strokeStyle.lineJoinStyle);
+ stream() << "miterLimit: " << info.strokeStyle.miterLimit;
+ if (info.strokeStyle.dashArray.length() != 0) {
+ stream() << "strokeStyle: " << "ShapePath.DashLine";
+ stream() << "dashPattern: " << QQuickVectorImageGenerator::Utils::listString(info.strokeStyle.dashArray);
+ stream() << "dashOffset: " << info.strokeStyle.dashOffset;
+ }
+ }
+
+ if (!(pathSelector & QQuickVectorImageGenerator::FillPath)) {
+ stream() << "fillColor: \"transparent\"";
+ } else if (info.grad.type() != QGradient::NoGradient) {
+ generateGradient(&info.grad, boundingRect);
+ } else {
+ stream() << "fillColor: \"" << info.fillColor.name(QColor::HexArgb) << "\"";
+ }
+ if (fillRule == QQuickShapePath::WindingFill)
+ stream() << "fillRule: ShapePath.WindingFill";
+ else
+ stream() << "fillRule: ShapePath.OddEvenFill";
+
+ QString hintStr;
+ if (quadPath)
+ hintStr = QQuickVectorImageGenerator::Utils::pathHintString(*quadPath);
+ if (!hintStr.isEmpty())
+ stream() << hintStr;
+
+
+ QString svgPathString = painterPath ? QQuickVectorImageGenerator::Utils::toSvgString(*painterPath) : QQuickVectorImageGenerator::Utils::toSvgString(*quadPath);
+ stream() << "PathSvg { path: \"" << svgPathString << "\" }";
+
+ m_indentLevel--;
+ stream() << "}";
+}
+
+void QQuickQmlGenerator::generateNode(const NodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ stream() << "// Missing Implementation for SVG Node: " << info.typeName;
+ stream() << "// Adding an empty Item and skipping";
+ stream() << "Item {";
+ generateNodeBase(info);
+ stream() << "}";
+}
+
+void QQuickQmlGenerator::generateTextNode(const TextNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ static int counter = 0;
+ stream() << "Item {";
+ generateNodeBase(info);
+ m_indentLevel++;
+
+ if (!info.isTextArea)
+ stream() << "Item { id: textAlignItem_" << counter << "; x: " << info.position.x() << "; y: " << info.position.y() << "}";
+
+ stream() << "Text {";
+
+ m_indentLevel++;
+
+ if (info.isTextArea) {
+ stream() << "x: " << info.position.x();
+ stream() << "y: " << info.position.y();
+ if (info.size.width() > 0)
+ stream() << "width: " << info.size.width();
+ if (info.size.height() > 0)
+ stream() << "height: " << info.size.height();
+ stream() << "wrapMode: Text.Wrap"; // ### WordWrap? verify with SVG standard
+ stream() << "clip: true"; //### Not exactly correct: should clip on the text level, not the pixel level
+ } else {
+ QString hAlign = QStringLiteral("left");
+ stream() << "anchors.baseline: textAlignItem_" << counter << ".top";
+ switch (info.alignment) {
+ case Qt::AlignHCenter:
+ hAlign = QStringLiteral("horizontalCenter");
+ break;
+ case Qt::AlignRight:
+ hAlign = QStringLiteral("right");
+ break;
+ default:
+ qCDebug(lcQuickVectorImage) << "Unexpected text alignment" << info.alignment;
+ Q_FALLTHROUGH();
+ case Qt::AlignLeft:
+ break;
+ }
+ stream() << "anchors." << hAlign << ": textAlignItem_" << counter << ".left";
+ }
+ counter++;
+
+ stream() << "color: \"" << info.fillColor.name(QColor::HexArgb) << "\"";
+ stream() << "textFormat:" << (info.needsRichText ? "Text.RichText" : "Text.StyledText");
+
+ QString s = info.text;
+ s.replace(QLatin1Char('"'), QLatin1String("\\\""));
+ stream() << "text: \"" << s << "\"";
+ stream() << "font.family: \"" << info.font.family() << "\"";
+ if (info.font.pixelSize() > 0)
+ stream() << "font.pixelSize:" << info.font.pixelSize();
+ else if (info.font.pointSize() > 0)
+ stream() << "font.pixelSize:" << info.font.pointSizeF();
+ if (info.font.underline())
+ stream() << "font.underline: true";
+ if (info.font.weight() != QFont::Normal)
+ stream() << "font.weight: " << int(info.font.weight());
+ if (info.font.italic())
+ stream() << "font.italic: true";
+
+ if (info.strokeColor != QColorConstants::Transparent) {
+ stream() << "styleColor: \"" << info.strokeColor.name(QColor::HexArgb) << "\"";
+ stream() << "style: Text.Outline";
+ }
+
+ m_indentLevel--;
+ stream() << "}";
+
+ m_indentLevel--;
+ stream() << "}";
+}
+
+void QQuickQmlGenerator::generateUseNode(const UseNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ if (info.stage == StructureNodeStage::Start) {
+ stream() << "Item {";
+ generateNodeBase(info);
+ m_indentLevel++;
+ stream() << "x: " << info.startPos.x();
+ stream() << "y: " << info.startPos.y();
+ } else {
+ m_indentLevel--;
+ stream() << "}";
+ }
+}
+
+bool QQuickQmlGenerator::generateStructureNode(const StructureNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return false;
+
+ if (info.stage == StructureNodeStage::Start) {
+ if (!info.forceSeparatePaths && info.isPathContainer) {
+ stream() << shapeName() <<" {";
+ m_indentLevel++;
+ if (m_flags.testFlag(QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer))
+ stream() << "preferredRendererType: Shape.CurveRenderer";
+ m_indentLevel--;
+
+ m_inShapeItem = true;
+ } else {
+ stream() << "Item {";
+ }
+
+ if (!info.viewBox.isEmpty()) {
+ m_indentLevel++;
+ stream() << "transform: [";
+ m_indentLevel++;
+ bool translate = !qFuzzyIsNull(info.viewBox.x()) || !qFuzzyIsNull(info.viewBox.y());
+ if (translate)
+ stream() << "Translate { x: " << -info.viewBox.x() << "; y: " << -info.viewBox.y() << " },";
+ stream() << "Scale { xScale: width / " << info.viewBox.width() << "; yScale: height / " << info.viewBox.height() << " }";
+ m_indentLevel--;
+ stream() << "]";
+ m_indentLevel--;
+ }
+
+ generateNodeBase(info);
+ m_indentLevel++;
+ } else {
+ m_indentLevel--;
+ stream() << "}";
+ m_inShapeItem = false;
+ }
+
+ return true;
+}
+
+bool QQuickQmlGenerator::generateRootNode(const StructureNodeInfo &info)
+{
+ m_indentLevel = 0;
+ const QStringList comments = m_commentString.split(u'\n');
+
+ if (!isNodeVisible(info)) {
+ if (comments.isEmpty()) {
+ stream() << "// Generated from SVG";
+ } else {
+ for (const auto &comment : comments)
+ stream() << "// " << comment;
+ }
+
+ stream() << "import QtQuick";
+ stream() << "import QtQuick.Shapes" << Qt::endl;
+ stream() << "Item {";
+ m_indentLevel++;
+
+ double w = info.size.width();
+ double h = info.size.height();
+ if (w > 0)
+ stream() << "implicitWidth: " << w;
+ if (h > 0)
+ stream() << "implicitHeight: " << h;
+
+ m_indentLevel--;
+ stream() << "}";
+
+ return false;
+ }
+
+ if (info.stage == StructureNodeStage::Start) {
+ if (comments.isEmpty())
+ stream() << "// Generated from SVG";
+ else
+ for (const auto &comment : comments)
+ stream() << "// " << comment;
+
+ stream() << "import QtQuick";
+ stream() << "import QtQuick.Shapes" << Qt::endl;
+ stream() << "Item {";
+ m_indentLevel++;
+
+ double w = info.size.width();
+ double h = info.size.height();
+ if (w > 0)
+ stream() << "implicitWidth: " << w;
+ if (h > 0)
+ stream() << "implicitHeight: " << h;
+
+ if (!info.viewBox.isEmpty()) {
+ stream() << "transform: [";
+ m_indentLevel++;
+ bool translate = !qFuzzyIsNull(info.viewBox.x()) || !qFuzzyIsNull(info.viewBox.y());
+ if (translate)
+ stream() << "Translate { x: " << -info.viewBox.x() << "; y: " << -info.viewBox.y() << " },";
+ stream() << "Scale { xScale: width / " << info.viewBox.width() << "; yScale: height / " << info.viewBox.height() << " }";
+ m_indentLevel--;
+ stream() << "]";;
+ }
+
+ generateNodeBase(info);
+ } else {
+ stream() << "}";
+ m_inShapeItem = false;
+ }
+
+ return true;
+}
+
+QString QQuickQmlGenerator::indent()
+{
+ return QString().fill(QLatin1Char(' '), m_indentLevel * 4);
+}
+
+GeneratorStream QQuickQmlGenerator::stream()
+{
+ GeneratorStream strm(m_stream);
+ strm << indent();
+ return strm;
+}
+
+const char *QQuickQmlGenerator::shapeName() const
+{
+ return m_shapeTypeName.isEmpty() ? "Shape" : m_shapeTypeName.constData();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickvectorimage/generator/qquickqmlgenerator_p.h b/src/quickvectorimage/generator/qquickqmlgenerator_p.h
new file mode 100644
index 0000000000..2bef58b054
--- /dev/null
+++ b/src/quickvectorimage/generator/qquickqmlgenerator_p.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKQMLGENERATOR_P_H
+#define QQUICKQMLGENERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickgenerator_p.h"
+
+#include <QtCore/qtextstream.h>
+
+QT_BEGIN_NAMESPACE
+
+class GeneratorStream;
+
+class Q_QUICKVECTORIMAGEGENERATOR_EXPORT QQuickQmlGenerator : public QQuickGenerator
+{
+public:
+ QQuickQmlGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags, const QString &outFileName);
+ ~QQuickQmlGenerator();
+
+ void setShapeTypeName(const QString &name);
+ QString shapeTypeName() const;
+
+ void setCommentString(const QString commentString);
+ QString commentString() const;
+
+ void setRetainFilePaths(bool retainFilePaths)
+ {
+ m_retainFilePaths = retainFilePaths;
+ }
+
+ bool retainFilePaths() const
+ {
+ return m_retainFilePaths;
+ }
+
+ void setAssetFileDirectory(const QString &assetFileDirectory)
+ {
+ m_assetFileDirectory = assetFileDirectory;
+ }
+
+ QString assetFileDirectory() const
+ {
+ return m_assetFileDirectory;
+ }
+
+ void setAssetFilePrefix(const QString &assetFilePrefix)
+ {
+ m_assetFilePrefix = assetFilePrefix;
+ }
+
+ QString assetFilePrefix() const
+ {
+ return m_assetFilePrefix;
+ }
+
+protected:
+ void generateNodeBase(const NodeInfo &info) override;
+ bool generateDefsNode(const NodeInfo &info) override;
+ void generateImageNode(const ImageNodeInfo &info) override;
+ void generatePath(const PathNodeInfo &info) override;
+ void generateNode(const NodeInfo &info) override;
+ void generateTextNode(const TextNodeInfo &info) override;
+ void generateUseNode(const UseNodeInfo &info) override;
+ bool generateStructureNode(const StructureNodeInfo &info) override;
+ bool generateRootNode(const StructureNodeInfo &info) override;
+ void outputShapePath(const PathNodeInfo &info, const QPainterPath *path, const QQuadPath *quadPath, QQuickVectorImageGenerator::PathSelector pathSelector, const QRectF &boundingRect) override;
+
+private:
+ void generateGradient(const QGradient *grad, const QRectF &boundingRect);
+ QString indent();
+ GeneratorStream stream();
+ const char *shapeName() const;
+
+private:
+ int m_indentLevel = 0;
+ QTextStream *m_stream;
+ QByteArray result;
+ QString outputFileName;
+ bool m_inShapeItem = false;
+ QByteArray m_shapeTypeName;
+ QString m_commentString;
+ bool m_retainFilePaths = false;
+ QString m_assetFileDirectory;
+ QString m_assetFilePrefix;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKQMLGENERATOR_P_H
diff --git a/src/quickvectorimage/generator/qsvgvisitorimpl.cpp b/src/quickvectorimage/generator/qsvgvisitorimpl.cpp
new file mode 100644
index 0000000000..ed6dfdf5ea
--- /dev/null
+++ b/src/quickvectorimage/generator/qsvgvisitorimpl.cpp
@@ -0,0 +1,776 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsvgvisitorimpl_p.h"
+#include "qquickgenerator_p.h"
+#include "qquicknodeinfo_p.h"
+
+#include <private/qsvgvisitor_p.h>
+
+#include <QString>
+#include <QPainter>
+#include <QTextDocument>
+#include <QTextLayout>
+#include <QMatrix4x4>
+#include <QQuickItem>
+
+#include <private/qquickshape_p.h>
+#include <private/qquicktext_p.h>
+#include <private/qquicktranslate_p.h>
+#include <private/qquickitem_p.h>
+
+#include <private/qquickimagebase_p_p.h>
+#include <private/qquickimage_p.h>
+#include <private/qsgcurveprocessor_p.h>
+
+#include <private/qquadpath_p.h>
+
+#include "utils_p.h"
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+Q_DECLARE_LOGGING_CATEGORY(lcQuickVectorImage)
+
+class QSvgStyleResolver
+{
+public:
+ QSvgStyleResolver()
+ {
+ m_dummyImage = QImage(1, 1, QImage::Format_RGB32);
+ m_dummyPainter.begin(&m_dummyImage);
+ QPen defaultPen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
+ defaultPen.setMiterLimit(4);
+ m_dummyPainter.setPen(defaultPen);
+ m_dummyPainter.setBrush(Qt::black);
+ }
+
+ ~QSvgStyleResolver()
+ {
+ m_dummyPainter.end();
+ }
+
+ QPainter& painter() { return m_dummyPainter; }
+ QSvgExtraStates& states() { return m_svgState; }
+
+ QColor currentFillColor() const
+ {
+ if (m_dummyPainter.brush().style() == Qt::NoBrush ||
+ m_dummyPainter.brush().color() == QColorConstants::Transparent) {
+ return QColor(QColorConstants::Transparent);
+ }
+
+ QColor fillColor;
+ fillColor = m_dummyPainter.brush().color();
+ fillColor.setAlphaF(m_svgState.fillOpacity);
+
+ return fillColor;
+ }
+
+ qreal currentFillOpacity() const
+ {
+ return m_svgState.fillOpacity;
+ }
+
+ const QGradient *currentFillGradient() const
+ {
+ if (m_dummyPainter.brush().style() == Qt::LinearGradientPattern || m_dummyPainter.brush().style() == Qt::RadialGradientPattern || m_dummyPainter.brush().style() == Qt::ConicalGradientPattern )
+ return m_dummyPainter.brush().gradient();
+ return nullptr;
+ }
+
+ QColor currentStrokeColor() const
+ {
+ if (m_dummyPainter.pen().brush().style() == Qt::NoBrush ||
+ m_dummyPainter.pen().brush().color() == QColorConstants::Transparent) {
+ return QColor(QColorConstants::Transparent);
+ }
+
+ QColor strokeColor;
+ strokeColor = m_dummyPainter.pen().brush().color();
+ strokeColor.setAlphaF(m_svgState.strokeOpacity);
+
+ return strokeColor;
+ }
+
+ static QGradient applyOpacityToGradient(const QGradient &gradient, float opacity)
+ {
+ QGradient grad = gradient;
+ QGradientStops stops;
+ for (auto &stop : grad.stops()) {
+ stop.second.setAlphaF(stop.second.alphaF() * opacity);
+ stops.append(stop);
+ }
+
+ grad.setStops(stops);
+
+ return grad;
+ }
+
+ float currentStrokeWidth() const
+ {
+ float penWidth = m_dummyPainter.pen().widthF();
+ return penWidth ? penWidth : 1;
+ }
+
+ QPen currentStroke() const
+ {
+ return m_dummyPainter.pen();
+ }
+
+protected:
+ QPainter m_dummyPainter;
+ QImage m_dummyImage;
+ QSvgExtraStates m_svgState;
+};
+
+Q_GLOBAL_STATIC(QSvgStyleResolver, styleResolver)
+
+namespace {
+inline bool isPathContainer(const QSvgStructureNode *node)
+{
+ bool foundPath = false;
+ for (const auto *child : node->renderers()) {
+ switch (child->type()) {
+ // nodes that shouldn't go inside Shape{}
+ case QSvgNode::Switch:
+ case QSvgNode::Doc:
+ case QSvgNode::Group:
+ case QSvgNode::Animation:
+ case QSvgNode::Use:
+ case QSvgNode::Video:
+ //qCDebug(lcQuickVectorGraphics) << "NOT path container because" << node->typeName() ;
+ return false;
+
+ // nodes that could go inside Shape{}
+ case QSvgNode::Defs:
+ case QSvgNode::Image:
+ case QSvgNode::Textarea:
+ case QSvgNode::Text:
+ case QSvgNode::Tspan:
+ break;
+
+ // nodes that are done as pure ShapePath{}
+ case QSvgNode::Rect:
+ case QSvgNode::Circle:
+ case QSvgNode::Ellipse:
+ case QSvgNode::Line:
+ case QSvgNode::Path:
+ case QSvgNode::Polygon:
+ case QSvgNode::Polyline:
+ if (!child->style().transform.isDefault()) {
+ //qCDebug(lcQuickVectorGraphics) << "NOT path container because local transform";
+ return false;
+ }
+ foundPath = true;
+ break;
+ default:
+ qCDebug(lcQuickVectorImage) << "Unhandled type in switch" << child->type();
+ break;
+ }
+ }
+ //qCDebug(lcQuickVectorGraphics) << "Container" << node->nodeId() << node->typeName() << "is" << foundPath;
+ return foundPath;
+}
+
+void populateStrokeStyle(StrokeStyle &srokeStyle)
+{
+ QPen p = styleResolver->currentStroke();
+ srokeStyle.lineCapStyle = p.capStyle();
+ srokeStyle.lineJoinStyle = p.joinStyle() == Qt::SvgMiterJoin ? Qt::MiterJoin : p.joinStyle(); //TODO support SvgMiterJoin
+ srokeStyle.miterLimit = p.miterLimit();
+ srokeStyle.dashOffset = p.dashOffset();
+ srokeStyle.dashArray = p.dashPattern();
+ srokeStyle.color = styleResolver->currentStrokeColor();
+ srokeStyle.width = p.widthF();
+}
+
+};
+
+QSvgVisitorImpl::QSvgVisitorImpl(const QString svgFileName, QQuickGenerator *generator)
+ : m_svgFileName(svgFileName)
+ , m_generator(generator)
+{
+}
+
+void QSvgVisitorImpl::traverse()
+{
+ if (!m_generator) {
+ qCDebug(lcQuickVectorImage) << "No valid QQuickGenerator is set. Genration will stop";
+ return;
+ }
+
+ auto *doc = QSvgTinyDocument::load(m_svgFileName);
+ if (!doc) {
+ qCDebug(lcQuickVectorImage) << "Not a valid Svg File : " << m_svgFileName;
+ return;
+ }
+
+ QSvgVisitor::traverse(doc);
+}
+
+void QSvgVisitorImpl::visitNode(const QSvgNode *node)
+{
+ handleBaseNodeSetup(node);
+
+ NodeInfo info;
+ fillCommonNodeInfo(node, info);
+
+ m_generator->generateNode(info);
+
+ handleBaseNodeEnd(node);
+}
+
+void QSvgVisitorImpl::visitImageNode(const QSvgImage *node)
+{
+ // TODO: this requires proper asset management.
+ handleBaseNodeSetup(node);
+
+ ImageNodeInfo info;
+ fillCommonNodeInfo(node, info);
+ info.image = node->image();
+ info.rect = node->rect();
+ info.externalFileReference = node->filename();
+
+ m_generator->generateImageNode(info);
+
+ handleBaseNodeEnd(node);
+}
+
+void QSvgVisitorImpl::visitRectNode(const QSvgRect *node)
+{
+ QRectF rect = node->rect();
+ QPointF rads = node->radius();
+ // This is using Qt::RelativeSize semantics: percentage of half rect size
+ qreal x1 = rect.left();
+ qreal x2 = rect.right();
+ qreal y1 = rect.top();
+ qreal y2 = rect.bottom();
+
+ qreal rx = rads.x() * rect.width() / 200;
+ qreal ry = rads.y() * rect.height() / 200;
+ QPainterPath p;
+
+ p.moveTo(x1 + rx, y1);
+ p.lineTo(x2 - rx, y1);
+ // qCDebug(lcQuickVectorGraphics) << "Line1" << x2 - rx << y1;
+ p.arcTo(x2 - rx * 2, y1, rx * 2, ry * 2, 90, -90); // ARC to x2, y1 + ry
+ // qCDebug(lcQuickVectorGraphics) << "p1" << p;
+
+ p.lineTo(x2, y2 - ry);
+ p.arcTo(x2 - rx * 2, y2 - ry * 2, rx * 2, ry * 2, 0, -90); // ARC to x2 - rx, y2
+
+ p.lineTo(x1 + rx, y2);
+ p.arcTo(x1, y2 - ry * 2, rx * 2, ry * 2, 270, -90); // ARC to x1, y2 - ry
+
+ p.lineTo(x1, y1 + ry);
+ p.arcTo(x1, y1, rx * 2, ry * 2, 180, -90); // ARC to x1 + rx, y1
+
+ handlePathNode(node, p);
+}
+
+void QSvgVisitorImpl::visitEllipseNode(const QSvgEllipse *node)
+{
+ QRectF rect = node->rect();
+
+ QPainterPath p;
+ p.addEllipse(rect);
+
+ handlePathNode(node, p);
+}
+
+void QSvgVisitorImpl::visitPathNode(const QSvgPath *node)
+{
+ handlePathNode(node, node->path());
+}
+
+void QSvgVisitorImpl::visitLineNode(const QSvgLine *node)
+{
+ QPainterPath p;
+ p.moveTo(node->line().p1());
+ p.lineTo(node->line().p2());
+ handlePathNode(node, p);
+}
+
+void QSvgVisitorImpl::visitPolygonNode(const QSvgPolygon *node)
+{
+ QPainterPath p = QQuickVectorImageGenerator::Utils::polygonToPath(node->polygon(), true);
+ handlePathNode(node, p);
+}
+
+void QSvgVisitorImpl::visitPolylineNode(const QSvgPolyline *node)
+{
+ QPainterPath p = QQuickVectorImageGenerator::Utils::polygonToPath(node->polygon(), false);
+ handlePathNode(node, p);
+}
+
+QString QSvgVisitorImpl::gradientCssDescription(const QGradient *gradient)
+{
+ QString cssDescription;
+ if (gradient->type() == QGradient::LinearGradient) {
+ const QLinearGradient *linearGradient = static_cast<const QLinearGradient *>(gradient);
+
+ cssDescription += " -qt-foreground: qlineargradient("_L1;
+ cssDescription += "x1:"_L1 + QString::number(linearGradient->start().x()) + u',';
+ cssDescription += "y1:"_L1 + QString::number(linearGradient->start().y()) + u',';
+ cssDescription += "x2:"_L1 + QString::number(linearGradient->finalStop().x()) + u',';
+ cssDescription += "y2:"_L1 + QString::number(linearGradient->finalStop().y()) + u',';
+ } else if (gradient->type() == QGradient::RadialGradient) {
+ const QRadialGradient *radialGradient = static_cast<const QRadialGradient *>(gradient);
+
+ cssDescription += " -qt-foreground: qradialgradient("_L1;
+ cssDescription += "cx:"_L1 + QString::number(radialGradient->center().x()) + u',';
+ cssDescription += "cy:"_L1 + QString::number(radialGradient->center().y()) + u',';
+ cssDescription += "fx:"_L1 + QString::number(radialGradient->focalPoint().x()) + u',';
+ cssDescription += "fy:"_L1 + QString::number(radialGradient->focalPoint().y()) + u',';
+ cssDescription += "radius:"_L1 + QString::number(radialGradient->radius()) + u',';
+ } else {
+ const QConicalGradient *conicalGradient = static_cast<const QConicalGradient *>(gradient);
+
+ cssDescription += " -qt-foreground: qconicalgradient("_L1;
+ cssDescription += "cx:"_L1 + QString::number(conicalGradient->center().x()) + u',';
+ cssDescription += "cy:"_L1 + QString::number(conicalGradient->center().y()) + u',';
+ cssDescription += "angle:"_L1 + QString::number(conicalGradient->angle()) + u',';
+ }
+
+ const QStringList coordinateModes = { "logical"_L1, "stretchtodevice"_L1, "objectbounding"_L1, "object"_L1 };
+ cssDescription += "coordinatemode:"_L1;
+ cssDescription += coordinateModes.at(int(gradient->coordinateMode()));
+ cssDescription += u',';
+
+ const QStringList spreads = { "pad"_L1, "reflect"_L1, "repeat"_L1 };
+ cssDescription += "spread:"_L1;
+ cssDescription += spreads.at(int(gradient->spread()));
+
+ for (const QGradientStop &stop : gradient->stops()) {
+ cssDescription += ",stop:"_L1;
+ cssDescription += QString::number(stop.first);
+ cssDescription += u' ';
+ cssDescription += stop.second.name(QColor::HexArgb);
+ }
+
+ cssDescription += ");"_L1;
+
+ return cssDescription;
+}
+
+QString QSvgVisitorImpl::colorCssDescription(QColor color)
+{
+ QString cssDescription;
+ cssDescription += QStringLiteral("rgba(");
+ cssDescription += QString::number(color.red()) + QStringLiteral(",");
+ cssDescription += QString::number(color.blue()) + QStringLiteral(",");
+ cssDescription += QString::number(color.green()) + QStringLiteral(",");
+ cssDescription += QString::number(color.alphaF()) + QStringLiteral(")");
+
+ return cssDescription;
+}
+
+void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
+{
+ handleBaseNodeSetup(node);
+ const bool isTextArea = node->type() == QSvgNode::Textarea;
+
+ QString text;
+ bool needsRichText = false;
+ bool preserveWhiteSpace = node->whitespaceMode() == QSvgText::Preserve;
+ const QGradient *mainGradient = styleResolver->currentFillGradient();
+#if QT_CONFIG(texthtmlparser)
+ bool needsPathNode = mainGradient != nullptr;
+#endif
+ for (const auto *tspan : node->tspans()) {
+ if (!tspan) {
+ text += QStringLiteral("<br>");
+ continue;
+ }
+
+ // Note: We cannot get the font directly from the style, since this does
+ // not apply the weight, since this is relative and depends on current state.
+ handleBaseNodeSetup(tspan);
+ QFont font = styleResolver->painter().font();
+
+ QString styleTagContent;
+
+ if ((font.resolveMask() & QFont::FamilyResolved)
+ || (font.resolveMask() & QFont::FamiliesResolved)) {
+ styleTagContent += QStringLiteral("font-family: %1;").arg(font.family());
+ }
+
+ if (font.resolveMask() & QFont::WeightResolved
+ && font.weight() != QFont::Normal
+ && font.weight() != QFont::Bold) {
+ styleTagContent += QStringLiteral("font-weight: %1;").arg(int(font.weight()));
+ }
+
+ if (font.resolveMask() & QFont::SizeResolved) {
+ // Pixel size stored as point size in SVG parser
+ styleTagContent += QStringLiteral("font-size: %1px;").arg(int(font.pointSizeF()));
+ }
+
+ if (font.resolveMask() & QFont::CapitalizationResolved
+ && font.capitalization() == QFont::SmallCaps) {
+ styleTagContent += QStringLiteral("font-variant: small-caps;");
+ }
+
+ if (styleResolver->currentFillGradient() != nullptr
+ && styleResolver->currentFillGradient() != mainGradient) {
+ const QGradient grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
+ styleTagContent += gradientCssDescription(&grad) + u';';
+#if QT_CONFIG(texthtmlparser)
+ needsPathNode = true;
+#endif
+ }
+
+ QString strokeColor = colorCssDescription(styleResolver->currentStrokeColor());
+ if (!strokeColor.isEmpty()) {
+ styleTagContent += QStringLiteral("-qt-stroke-color:%1;").arg(strokeColor);
+ styleTagContent += QStringLiteral("-qt-stroke-width:%1;").arg(styleResolver->currentStrokeWidth());
+#if QT_CONFIG(texthtmlparser)
+ needsPathNode = true;
+#endif
+ }
+
+ if (tspan->whitespaceMode() == QSvgText::Preserve && !preserveWhiteSpace)
+ styleTagContent += QStringLiteral("white-space: pre-wrap;");
+
+ QString content = tspan->text().toHtmlEscaped();
+ content.replace(QLatin1Char('\t'), QLatin1Char(' '));
+ content.replace(QLatin1Char('\n'), QLatin1Char(' '));
+
+ bool fontTag = false;
+ if (!tspan->style().fill.isDefault()) {
+ auto &b = tspan->style().fill->qbrush();
+ qCDebug(lcQuickVectorImage) << "tspan FILL:" << b;
+ if (b.style() != Qt::NoBrush)
+ {
+ if (qFuzzyCompare(b.color().alphaF() + 1.0, 2.0))
+ {
+ QString spanColor = b.color().name();
+ fontTag = !spanColor.isEmpty();
+ if (fontTag)
+ text += QStringLiteral("<font color=\"%1\">").arg(spanColor);
+ } else {
+ QString spanColor = colorCssDescription(b.color());
+ styleTagContent += QStringLiteral("color:%1").arg(spanColor);
+ }
+ }
+ }
+
+ needsRichText = needsRichText || !styleTagContent.isEmpty();
+ if (!styleTagContent.isEmpty())
+ text += QStringLiteral("<span style=\"%1\">").arg(styleTagContent);
+
+ if (font.resolveMask() & QFont::WeightResolved && font.bold())
+ text += QStringLiteral("<b>");
+
+ if (font.resolveMask() & QFont::StyleResolved && font.italic())
+ text += QStringLiteral("<i>");
+
+
+ if (font.resolveMask() & QFont::CapitalizationResolved) {
+ switch (font.capitalization()) {
+ case QFont::AllLowercase:
+ content = content.toLower();
+ break;
+ case QFont::AllUppercase:
+ content = content.toUpper();
+ break;
+ case QFont::Capitalize:
+ // ### We need to iterate over the string and do the title case conversion,
+ // since this is not part of QString.
+ qCWarning(lcQuickVectorImage) << "Title case not implemented for tspan";
+ break;
+ default:
+ break;
+ }
+ }
+ text += content;
+ if (fontTag)
+ text += QStringLiteral("</font>");
+
+ if (font.resolveMask() & QFont::StyleResolved && font.italic())
+ text += QStringLiteral("</i>");
+
+ if (font.resolveMask() & QFont::WeightResolved && font.bold())
+ text += QStringLiteral("</b>");
+
+ if (!styleTagContent.isEmpty())
+ text += QStringLiteral("</span>");
+
+ handleBaseNodeEnd(tspan);
+ }
+
+ if (preserveWhiteSpace && (needsRichText || styleResolver->currentFillGradient() != nullptr))
+ text = QStringLiteral("<span style=\"white-space: pre-wrap\">") + text + QStringLiteral("</span>");
+
+ QFont font = styleResolver->painter().font();
+ if (font.pixelSize() <= 0 && font.pointSize() > 0)
+ font.setPixelSize(font.pointSize()); // Pixel size stored as point size by SVG parser
+
+#if QT_CONFIG(texthtmlparser)
+ if (needsPathNode) {
+ QTextDocument document;
+ document.setHtml(text);
+ if (isTextArea && node->size().width() > 0)
+ document.setTextWidth(node->size().width());
+ document.setDefaultFont(font);
+ document.pageCount(); // Force layout
+
+ QTextBlock block = document.firstBlock();
+ while (block.isValid()) {
+ QTextLayout *lout = block.layout();
+
+ if (lout != nullptr) {
+ auto addPathForFormat = [&](QPainterPath p, QTextCharFormat fmt) {
+ PathNodeInfo info;
+ fillCommonNodeInfo(node, info);
+ auto fillStyle = node->style().fill;
+ if (fillStyle)
+ info.fillRule = fillStyle->fillRule();
+
+ if (fmt.hasProperty(QTextCharFormat::ForegroundBrush)) {
+ info.fillColor = fmt.foreground().color();
+ if (fmt.foreground().gradient() != nullptr && fmt.foreground().gradient()->type() != QGradient::NoGradient)
+ info.grad = *fmt.foreground().gradient();
+ } else {
+ info.fillColor = styleResolver->currentFillColor();
+ }
+
+ info.painterPath = p;
+
+ if (fmt.hasProperty(QTextCharFormat::TextOutline)) {
+ info.strokeStyle.width = fmt.textOutline().widthF();
+ info.strokeStyle.color = fmt.textOutline().color();
+ } else {
+ info.strokeStyle.color = styleResolver->currentStrokeColor();
+ info.strokeStyle.width = styleResolver->currentStrokeWidth();
+ }
+
+ if (info.grad.type() == QGradient::NoGradient && styleResolver->currentFillGradient() != nullptr)
+ info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
+
+ m_generator->generatePath(info);
+ };
+
+ qreal baselineOffset = -QFontMetricsF(font).ascent();
+ if (lout->lineCount() > 0 && lout->lineAt(0).isValid())
+ baselineOffset = -lout->lineAt(0).ascent();
+
+ const QPointF baselineTranslation(0.0, baselineOffset);
+ auto glyphsToPath = [&](QList<QGlyphRun> glyphRuns) {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+ for (const QGlyphRun &glyphRun : glyphRuns) {
+ QRawFont font = glyphRun.rawFont();
+ QList<quint32> glyphIndexes = glyphRun.glyphIndexes();
+ QList<QPointF> positions = glyphRun.positions();
+
+ for (qsizetype j = 0; j < glyphIndexes.size(); ++j) {
+ quint32 glyphIndex = glyphIndexes.at(j);
+ const QPointF &pos = positions.at(j);
+
+ QPainterPath p = font.pathForGlyph(glyphIndex);
+ p.translate(pos + node->position() + baselineTranslation);
+ path.addPath(p);
+ }
+ }
+
+ return path;
+ };
+
+ QList<QTextLayout::FormatRange> formats = block.textFormats();
+ for (int i = 0; i < formats.size(); ++i) {
+ QTextLayout::FormatRange range = formats.at(i);
+
+ // If we hit a "multi" anchor, it means we have additional formats to apply
+ // for both this and the subsequent range, so we merge them.
+ if (!range.format.anchorNames().isEmpty()
+ && range.format.anchorNames().first().startsWith(QStringLiteral("multi"))
+ && i < formats.size() - 1) {
+ QTextLayout::FormatRange nextRange = formats.at(++i);
+ range.length += nextRange.length;
+ range.format.merge(nextRange.format);
+ }
+ QList<QGlyphRun> glyphRuns = lout->glyphRuns(range.start, range.length);
+ QPainterPath path = glyphsToPath(glyphRuns);
+ addPathForFormat(path, range.format);
+ }
+ }
+
+ block = block.next();
+ }
+ } else
+#endif
+ {
+ TextNodeInfo info;
+ fillCommonNodeInfo(node, info);
+
+ info.position = node->position();
+ info.size = node->size();
+ info.font = font;
+ info.text = text;
+ info.isTextArea = isTextArea;
+ info.needsRichText = needsRichText;
+ info.fillColor = styleResolver->currentFillColor();
+ info.alignment = styleResolver->states().textAnchor;
+ info.strokeColor = styleResolver->currentStrokeColor();
+
+ m_generator->generateTextNode(info);
+ }
+
+ handleBaseNodeEnd(node);
+}
+
+void QSvgVisitorImpl::visitUseNode(const QSvgUse *node)
+{
+ QSvgNode *link = node->link();
+ if (!link)
+ return;
+
+ handleBaseNodeSetup(node);
+ UseNodeInfo info;
+ fillCommonNodeInfo(node, info);
+
+ info.stage = StructureNodeStage::Start;
+ info.startPos = node->start();
+
+ m_generator->generateUseNode(info);
+
+ QSvgVisitor::traverse(link);
+
+ info.stage = StructureNodeStage::End;
+ m_generator->generateUseNode(info);
+ handleBaseNodeEnd(node);
+}
+
+bool QSvgVisitorImpl::visitDefsNodeStart(const QSvgDefs *node)
+{
+ Q_UNUSED(node)
+
+ return m_generator->generateDefsNode(NodeInfo{});
+}
+
+bool QSvgVisitorImpl::visitStructureNodeStart(const QSvgStructureNode *node)
+{
+ constexpr bool forceSeparatePaths = false;
+ handleBaseNodeSetup(node);
+
+ StructureNodeInfo info;
+
+ fillCommonNodeInfo(node, info);
+ info.forceSeparatePaths = forceSeparatePaths;
+ info.isPathContainer = isPathContainer(node);
+ info.stage = StructureNodeStage::Start;
+
+ return m_generator->generateStructureNode(info);;
+}
+
+void QSvgVisitorImpl::visitStructureNodeEnd(const QSvgStructureNode *node)
+{
+ handleBaseNodeEnd(node);
+ // qCDebug(lcQuickVectorGraphics) << "REVERT" << node->nodeId() << node->type() << (m_styleResolver->painter().pen().style() != Qt::NoPen) << m_styleResolver->painter().pen().color().name()
+ // << (m_styleResolver->painter().pen().brush().style() != Qt::NoBrush) << m_styleResolver->painter().pen().brush().color().name();
+
+ StructureNodeInfo info;
+ fillCommonNodeInfo(node, info);
+ info.stage = StructureNodeStage::End;
+
+ m_generator->generateStructureNode(info);
+}
+
+bool QSvgVisitorImpl::visitDocumentNodeStart(const QSvgTinyDocument *node)
+{
+ handleBaseNodeSetup(node);
+
+ StructureNodeInfo info;
+ fillCommonNodeInfo(node, info);
+
+ const QSvgTinyDocument *doc = static_cast<const QSvgTinyDocument *>(node);
+ info.size = doc->size();
+ info.viewBox = doc->viewBox();
+ info.isPathContainer = isPathContainer(node);
+ info.stage = StructureNodeStage::Start;
+
+ return m_generator->generateRootNode(info);;
+}
+
+void QSvgVisitorImpl::visitDocumentNodeEnd(const QSvgTinyDocument *node)
+{
+ handleBaseNodeEnd(node);
+ qCDebug(lcQuickVectorImage) << "REVERT" << node->nodeId() << node->type() << (styleResolver->painter().pen().style() != Qt::NoPen)
+ << styleResolver->painter().pen().color().name() << (styleResolver->painter().pen().brush().style() != Qt::NoBrush)
+ << styleResolver->painter().pen().brush().color().name();
+
+ StructureNodeInfo info;
+ fillCommonNodeInfo(node, info);
+ info.stage = StructureNodeStage::End;
+
+ m_generator->generateRootNode(info);
+}
+
+void QSvgVisitorImpl::fillCommonNodeInfo(const QSvgNode *node, NodeInfo &info)
+{
+ info.nodeId = node->nodeId();
+ info.typeName = node->typeName();
+ info.isDefaultTransform = node->style().transform.isDefault();
+ info.transform = !info.isDefaultTransform ? node->style().transform->qtransform() : QTransform();
+ info.isDefaultOpacity = node->style().opacity.isDefault();
+ info.opacity = !info.isDefaultOpacity ? node->style().opacity->opacity() : 1.0;
+ info.isVisible = node->isVisible();
+ info.isDisplayed = node->displayMode() != QSvgNode::DisplayMode::NoneMode;
+}
+
+void QSvgVisitorImpl::handleBaseNodeSetup(const QSvgNode *node)
+{
+ qCDebug(lcQuickVectorImage) << "Before SETUP" << node << "fill" << styleResolver->currentFillColor()
+ << "stroke" << styleResolver->currentStrokeColor() << styleResolver->currentStrokeWidth()
+ << node->nodeId() << " type: " << node->typeName() << " " << node->type();
+
+ node->applyStyle(&styleResolver->painter(), styleResolver->states());
+
+ qCDebug(lcQuickVectorImage) << "After SETUP" << node << "fill" << styleResolver->currentFillColor()
+ << "stroke" << styleResolver->currentStrokeColor()
+ << styleResolver->currentStrokeWidth() << node->nodeId();
+}
+
+void QSvgVisitorImpl::handleBaseNode(const QSvgNode *node)
+{
+ NodeInfo info;
+ fillCommonNodeInfo(node, info);
+
+ m_generator->generateNodeBase(info);
+}
+
+void QSvgVisitorImpl::handleBaseNodeEnd(const QSvgNode *node)
+{
+ node->revertStyle(&styleResolver->painter(), styleResolver->states());
+
+ qCDebug(lcQuickVectorImage) << "After END" << node << "fill" << styleResolver->currentFillColor()
+ << "stroke" << styleResolver->currentStrokeColor() << styleResolver->currentStrokeWidth()
+ << node->nodeId();
+}
+
+void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &path)
+{
+ handleBaseNodeSetup(node);
+
+ PathNodeInfo info;
+ fillCommonNodeInfo(node, info);
+ auto fillStyle = node->style().fill;
+ if (fillStyle)
+ info.fillRule = fillStyle->fillRule();
+
+ info.painterPath = path;
+ info.fillColor = styleResolver->currentFillColor();
+ populateStrokeStyle(info.strokeStyle);
+ if (styleResolver->currentFillGradient() != nullptr)
+ info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
+
+ m_generator->generatePath(info);
+
+ handleBaseNodeEnd(node);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickvectorimage/generator/qsvgvisitorimpl_p.h b/src/quickvectorimage/generator/qsvgvisitorimpl_p.h
new file mode 100644
index 0000000000..af8a8d386e
--- /dev/null
+++ b/src/quickvectorimage/generator/qsvgvisitorimpl_p.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSVGVISITORIMPL_P_H
+#define QSVGVISITORIMPL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtSvg/private/qsvgvisitor_p.h>
+#include "qquickgenerator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextStream;
+class QSvgTinyDocument;
+class QString;
+class QQuickItem;
+
+class QSvgVisitorImpl : public QSvgVisitor
+{
+public:
+ QSvgVisitorImpl(const QString svgFileName, QQuickGenerator *generator);
+ void traverse();
+
+protected:
+ void visitNode(const QSvgNode *node) override;
+ void visitImageNode(const QSvgImage *node) override;
+ void visitRectNode(const QSvgRect *node) override;
+ void visitEllipseNode(const QSvgEllipse *node) override;
+ void visitPathNode(const QSvgPath *node) override;
+ void visitLineNode(const QSvgLine *node) override;
+ void visitPolygonNode(const QSvgPolygon *node) override;
+ void visitPolylineNode(const QSvgPolyline *node) override;
+ void visitTextNode(const QSvgText *node) override;
+ void visitUseNode(const QSvgUse *node) override;
+ bool visitDefsNodeStart(const QSvgDefs *node) override;
+ bool visitStructureNodeStart(const QSvgStructureNode *node) override;
+ void visitStructureNodeEnd(const QSvgStructureNode *node) override;
+
+ bool visitDocumentNodeStart(const QSvgTinyDocument *node) override;
+ void visitDocumentNodeEnd(const QSvgTinyDocument *node) override;
+
+private:
+ void fillCommonNodeInfo(const QSvgNode *node, NodeInfo &info);
+ void handleBaseNodeSetup(const QSvgNode *node);
+ void handleBaseNode(const QSvgNode *node);
+ void handleBaseNodeEnd(const QSvgNode *node);
+ void handlePathNode(const QSvgNode *node, const QPainterPath &path);
+ void outputShapePath(QPainterPath pathCopy, const PathNodeInfo &info);
+ static QString gradientCssDescription(const QGradient *gradient);
+ static QString colorCssDescription(QColor color);
+
+private:
+ QString m_svgFileName;
+ QQuickGenerator *m_generator;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSVGVISITORIMPL_P_H
diff --git a/src/quickvectorimage/generator/utils_p.h b/src/quickvectorimage/generator/utils_p.h
new file mode 100644
index 0000000000..bb65ee2b69
--- /dev/null
+++ b/src/quickvectorimage/generator/utils_p.h
@@ -0,0 +1,264 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef UTILS_P_H
+#define UTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qquicktranslate_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qsvgnode_p.h>
+
+#include <private/qquadpath_p.h>
+#include <private/qsvgvisitor_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQuickVectorImageGenerator::Utils
+{
+
+class ViewBoxItem : public QQuickItem
+{
+public:
+ ViewBoxItem(const QRectF viewBox, QQuickItem *parent = nullptr) : QQuickItem(parent), m_viewBox(viewBox) { setXForm(); }
+
+protected:
+ void geometryChange(const QRectF &/*newGeometry*/, const QRectF &/*oldGeometry*/) override
+ {
+ setXForm();
+ }
+
+private:
+ void setXForm()
+ {
+ auto xformProp = transform();
+ xformProp.clear(&xformProp);
+ bool translate = !qFuzzyIsNull(m_viewBox.x()) || !qFuzzyIsNull(m_viewBox.y());
+ if (translate) {
+ auto *tr = new QQuickTranslate(this);
+ tr->setX(-m_viewBox.x());
+ tr->setY(-m_viewBox.y());
+ xformProp.append(&xformProp, tr);
+ }
+ if (!m_viewBox.isEmpty() && width() && height()) {
+ auto *scale = new QQuickScale(this);
+ qreal sx = width() / m_viewBox.width();
+ qreal sy = height() / m_viewBox.height();
+
+ scale->setXScale(sx);
+ scale->setYScale(sy);
+ xformProp.append(&xformProp, scale);
+ }
+ }
+ QRectF m_viewBox;
+};
+
+inline QPainterPath polygonToPath(const QPolygonF &poly, bool closed)
+{
+ QPainterPath path;
+ if (poly.isEmpty())
+ return path;
+ bool first = true;
+ for (const auto &p : poly) {
+ if (first)
+ path.moveTo(p);
+ else
+ path.lineTo(p);
+ first = false;
+ }
+ if (closed)
+ path.closeSubpath();
+ return path;
+}
+
+inline QString pathHintString(const QQuadPath &qp)
+{
+ QString res;
+ QTextStream str(&res);
+ auto flags = qp.pathHints();
+ if (!flags)
+ return res;
+ str << "pathHints:";
+ bool first = true;
+
+#define CHECK_PATH_HINT(flagName) \
+ if (flags.testFlag(QQuadPath::flagName)) { \
+ if (!first) \
+ str << " |"; \
+ first = false; \
+ str << " ShapePath." #flagName; \
+ }
+
+ CHECK_PATH_HINT(PathLinear)
+ CHECK_PATH_HINT(PathQuadratic)
+ CHECK_PATH_HINT(PathConvex)
+ CHECK_PATH_HINT(PathFillOnRight)
+ CHECK_PATH_HINT(PathSolid)
+ CHECK_PATH_HINT(PathNonIntersecting)
+ CHECK_PATH_HINT(PathNonOverlappingControlPointTriangles)
+
+ return res;
+}
+
+// Find the square that gives the same gradient in QGradient::LogicalMode as
+// objModeRect does in QGradient::ObjectMode
+
+// When the object's bounding box is not square, the stripes that are conceptually
+// perpendicular to the gradient vector within object bounding box space shall render
+// non-perpendicular relative to the gradient vector in user space due to application
+// of the non-uniform scaling transformation from bounding box space to user space.
+inline QRectF mapToQtLogicalMode(const QRectF &objModeRect, const QRectF &boundingRect)
+{
+
+ QRect pixelRect(objModeRect.x() * boundingRect.width() + boundingRect.left(),
+ objModeRect.y() * boundingRect.height() + boundingRect.top(),
+ objModeRect.width() * boundingRect.width(),
+ objModeRect.height() * boundingRect.height());
+
+ if (pixelRect.isEmpty()) // pure horizontal/vertical gradient
+ return pixelRect;
+
+ double w = boundingRect.width();
+ double h = boundingRect.height();
+ double objModeSlope = objModeRect.height() / objModeRect.width();
+ double a = objModeSlope * w / h;
+
+ // do calculation with origin == pixelRect.topLeft
+ double x2 = pixelRect.width();
+ double y2 = pixelRect.height();
+ double x = (x2 + a * y2) / (1 + a * a);
+ double y = y2 - (x - x2)/a;
+
+ return QRectF(pixelRect.topLeft(), QSizeF(x,y));
+}
+
+inline QString toSvgString(const QPainterPath &path)
+{
+ QString svgPathString;
+ QTextStream strm(&svgPathString);
+
+ for (int i = 0; i < path.elementCount(); ++i) {
+ QPainterPath::Element element = path.elementAt(i);
+ if (element.isMoveTo()) {
+ strm << "M " << element.x << " " << element.y << " ";
+ } else if (element.isLineTo()) {
+ strm << "L " << element.x << " " << element.y << " ";
+ } else if (element.isCurveTo()) {
+ QPointF c1(element.x, element.y);
+ ++i;
+ element = path.elementAt(i);
+
+ QPointF c2(element.x, element.y);
+ ++i;
+ element = path.elementAt(i);
+ QPointF ep(element.x, element.y);
+
+ strm << "C "
+ << c1.x() << " "
+ << c1.y() << " "
+ << c2.x() << " "
+ << c2.y() << " "
+ << ep.x() << " "
+ << ep.y() << " ";
+ }
+ }
+
+ return svgPathString;
+}
+
+inline QString toSvgString(const QQuadPath &path)
+{
+ QString svgPathString;
+ QTextStream strm(&svgPathString);
+ path.iterateElements([&](const QQuadPath::Element &e, int) {
+ if (e.isSubpathStart())
+ strm << "M " << e.startPoint().x() << " " << e.startPoint().y() << " ";
+ if (e.isLine())
+ strm << "L " << e.endPoint().x() << " " << e.endPoint().y() << " ";
+ else
+ strm << "Q " << e.controlPoint().x() << " " << e.controlPoint().y() << " "
+ << e.endPoint().x() << " " << e.endPoint().y() << " ";
+ });
+
+ return svgPathString;
+}
+
+inline QString strokeCapStyleString(Qt::PenCapStyle strokeCapStyle)
+{
+ QString capStyle;
+ switch (strokeCapStyle) {
+ case Qt::FlatCap:
+ capStyle = QStringLiteral("ShapePath.FlatCap");
+ break;
+ case Qt::SquareCap:
+ capStyle = QStringLiteral("ShapePath.SquareCap");
+ break;
+ case Qt::RoundCap:
+ capStyle = QStringLiteral("ShapePath.RoundCap");
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ return capStyle;
+}
+
+inline QString strokeJoinStyleString(Qt::PenJoinStyle strokeJoinStyle)
+{
+ QString joinStyle;
+ switch (strokeJoinStyle) {
+ case Qt::MiterJoin:
+ joinStyle = QStringLiteral("ShapePath.MiterJoin");
+ break;
+ case Qt::BevelJoin:
+ joinStyle = QStringLiteral("ShapePath.BevelJoin");
+ break;
+ case Qt::RoundJoin:
+ joinStyle = QStringLiteral("ShapePath.RoundJoin");
+ break;
+ default:
+ //TODO: Add support for SvgMiter case
+ Q_UNREACHABLE();
+ break;
+ }
+
+ return joinStyle;
+}
+
+template<typename T>
+inline QString listString(QList<T> list)
+{
+ if (list.isEmpty())
+ return QStringLiteral("[]");
+
+ QString listString;
+ QTextStream stream(&listString);
+ stream << "[";
+
+ if (list.length() > 1) {
+ for (int i = 0; i < list.length() - 1; i++) {
+ T v = list[i];
+ stream << v << ", ";
+ }
+ }
+
+ stream << list.last() << "]";
+ return listString;
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif // UTILS_P_H
diff --git a/src/quickvectorimage/qquickvectorimage.cpp b/src/quickvectorimage/qquickvectorimage.cpp
new file mode 100644
index 0000000000..b8d93c7691
--- /dev/null
+++ b/src/quickvectorimage/qquickvectorimage.cpp
@@ -0,0 +1,135 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickvectorimage_p.h"
+#include "qquickvectorimage_p_p.h"
+#include <QtQuickVectorImageGenerator/private/qquickitemgenerator_p.h>
+#include <QtQuickVectorImageGenerator/private/qquickvectorimageglobal_p.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule QtQuick.VectorImage
+ \title Qt Quick Vector Image QML Types
+ \ingroup qmlmodules
+ \brief Provides QML types for displaying vector image files.
+
+ To use the types in this module, import the module with the following line:
+
+ \qml
+ import QtQuick.VectorImage
+ \endqml
+
+ Qt Quick Vector Image provides support for displaying vector image files in a Qt Quick
+ scene.
+
+ It currently supports the \c SVG file format.
+
+ Qt supports multiple options for displaying SVG files. For an overview and comparison of the
+ different ones, see the documentation of the \l{svgtoqml} tool.
+
+ \section1 QML Types
+*/
+
+Q_LOGGING_CATEGORY(lcQuickVectorImage, "qt.quick.vectorimage", QtWarningMsg)
+
+void QQuickVectorImagePrivate::setSource(const QUrl &source)
+{
+ Q_Q(QQuickVectorImage);
+ if (sourceFile == source)
+ return;
+
+ sourceFile = source;
+ loadSvg();
+ emit q->sourceChanged();
+}
+
+void QQuickVectorImagePrivate::loadSvg()
+{
+ Q_Q(QQuickVectorImage);
+
+ QUrl resolvedUrl = qmlContext(q)->resolvedUrl(sourceFile);
+ QString localFile = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
+
+ if (localFile.isEmpty())
+ return;
+
+ QQuickVectorImagePrivate::Format fileFormat = formatFromFilePath(localFile);
+
+ if (fileFormat != QQuickVectorImagePrivate::Format::Svg) {
+ qCWarning(lcQuickVectorImage) << "Unsupported file format";
+ return;
+ }
+
+ if (svgItem)
+ svgItem->deleteLater();
+
+ svgItem = new QQuickItem(q);
+
+ QQuickVectorImageGenerator::GeneratorFlags flags;
+ flags.setFlag(QQuickVectorImageGenerator::CurveRenderer);
+ QQuickItemGenerator generator(localFile, flags, svgItem);
+ generator.generate();
+
+ svgItem->setParentItem(q);
+ q->setImplicitWidth(svgItem->width());
+ q->setImplicitHeight(svgItem->height());
+ q->update();
+}
+
+QQuickVectorImagePrivate::Format QQuickVectorImagePrivate::formatFromFilePath(const QString &filePath)
+{
+ Q_UNUSED(filePath)
+
+ QQuickVectorImagePrivate::Format res = QQuickVectorImagePrivate::Format::Unknown;
+
+ if (filePath.endsWith(QLatin1String(".svg")) || filePath.endsWith(QLatin1String(".svgz"))
+ || filePath.endsWith(QLatin1String(".svg.gz"))) {
+ res = QQuickVectorImagePrivate::Format::Svg;
+ }
+
+ return res;
+}
+
+/*!
+ \qmltype VectorImage
+ \inqmlmodule QtQuick.VectorImage
+ \brief Loads a vector image file and displays it in a Qt Quick scene.
+ \since 6.8
+
+ The VectorImage can be used to load a vector image file and display this as an item in a Qt
+ Quick scene. It currently supports the \c SVG file format.
+
+ \note This complements the approach of loading the vector image file through an \l Image
+ element: \l Image creates a raster version of the image at the requested size. VectorImage
+ builds a Qt Quick scene that represents the image. This means the resulting item can be scaled
+ and rotated without losing quality, and it will typically consume less memory than the
+ rasterized version.
+*/
+QQuickVectorImage::QQuickVectorImage(QQuickItem *parent)
+ : QQuickItem(*(new QQuickVectorImagePrivate), parent)
+{
+ setFlag(QQuickItem::ItemHasContents, true);
+}
+
+/*!
+ \qmlproperty url QtQuick.VectorImage::VectorImage::source
+
+ This property holds the URL of the vector image file to load.
+
+ VectorImage currently only supports the \c SVG file format.
+*/
+QUrl QQuickVectorImage::source() const
+{
+ Q_D(const QQuickVectorImage);
+ return d->sourceFile;
+}
+
+void QQuickVectorImage::setSource(const QUrl &source)
+{
+ Q_D(QQuickVectorImage);
+ d->setSource(source);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickvectorimage/qquickvectorimage_p.h b/src/quickvectorimage/qquickvectorimage_p.h
new file mode 100644
index 0000000000..68ccc561a1
--- /dev/null
+++ b/src/quickvectorimage/qquickvectorimage_p.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKVECTORIMAGE_P_H
+#define QQUICKVECTORIMAGE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QQuickItem>
+#include <QtQuickVectorImage/qtquickvectorimageexports.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickVectorImagePrivate;
+
+class Q_QUICKVECTORIMAGE_EXPORT QQuickVectorImage : public QQuickItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ QML_NAMED_ELEMENT(VectorImage)
+
+public:
+ QQuickVectorImage(QQuickItem *parent = nullptr);
+
+ QUrl source() const;
+ void setSource(const QUrl &source);
+
+signals:
+ void sourceChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickVectorImage)
+ Q_DECLARE_PRIVATE(QQuickVectorImage)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKVECTORIMAGE_P_H
+
diff --git a/src/quickvectorimage/qquickvectorimage_p_p.h b/src/quickvectorimage/qquickvectorimage_p_p.h
new file mode 100644
index 0000000000..689a5e9b69
--- /dev/null
+++ b/src/quickvectorimage/qquickvectorimage_p_p.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKVECTORIMAGE_P_P_H
+#define QQUICKVECTORIMAGE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QQuickPaintedItem>
+#include <QSvgRenderer>
+#include <private/qquickitem_p.h>
+#include "qquickvectorimage_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickVectorImagePrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickVectorImage)
+public:
+ QQuickVectorImagePrivate() = default;
+ virtual ~QQuickVectorImagePrivate() = default;
+
+ void setSource(const QUrl &source);
+ void loadSvg();
+
+ enum Format {
+ Unknown,
+ Svg
+ };
+ QQuickVectorImagePrivate::Format formatFromFilePath(const QString &filePath);
+
+ QUrl sourceFile;
+ QQuickItem *svgItem = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKVECTORIMAGE_P_P_H
diff --git a/src/quickvectorimage/qquickvectorimageglobal_p.h b/src/quickvectorimage/qquickvectorimageglobal_p.h
new file mode 100644
index 0000000000..d6c636bc84
--- /dev/null
+++ b/src/quickvectorimage/qquickvectorimageglobal_p.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTQUICKVECTORIMAGEGLOBAL_P_H
+#define QTQUICKVECTORIMAGEGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtQuickVectorImageGenerator/qtquickvectorimagegeneratorexports.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQuickVectorImageGenerator
+{
+ enum PathSelector {
+ FillPath = 0x1,
+ StrokePath = 0x2,
+ FillAndStroke = 0x3
+ };
+
+ enum GeneratorFlag {
+ OptimizePaths = 0x01,
+ CurveRenderer = 0x02,
+ OutlineStrokeMode = 0x04
+ };
+
+ Q_DECLARE_FLAGS(GeneratorFlags, GeneratorFlag);
+ Q_DECLARE_OPERATORS_FOR_FLAGS(GeneratorFlags);
+}
+
+QT_END_NAMESPACE
+
+#endif //QTQUICKVECTORIMAGEGLOBAL_P_H
diff --git a/src/quickwidgets/qaccessiblequickwidget.cpp b/src/quickwidgets/qaccessiblequickwidget.cpp
index f67d3eecab..40b8f8daf2 100644
--- a/src/quickwidgets/qaccessiblequickwidget.cpp
+++ b/src/quickwidgets/qaccessiblequickwidget.cpp
@@ -11,32 +11,52 @@ QT_BEGIN_NAMESPACE
QAccessibleQuickWidget::QAccessibleQuickWidget(QQuickWidget* widget)
: QAccessibleWidget(widget)
-, m_accessibleWindow(QQuickWidgetPrivate::get(widget)->offscreenWindow)
{
// NOTE: m_accessibleWindow is a QAccessibleQuickWindow, and not a
// QAccessibleQuickWidgetOffscreenWindow (defined below). This means
// it will return the Quick item child interfaces, which is what's needed here
// (unlike QAccessibleQuickWidgetOffscreenWindow, which will report 0 children).
+ repairWindow();
+}
+
+QAccessibleQuickWidget::~QAccessibleQuickWidget()
+{
+ QObject::disconnect(m_connection);
+}
+
+void QAccessibleQuickWidget::repairWindow()
+{
+ if (!m_accessibleWindow || !m_accessibleWindow->object()) {
+ QQuickWidget *theWidget = static_cast<QQuickWidget *>(object());
+ QQuickWindow *newOffscreen = QQuickWidgetPrivate::get(theWidget)->offscreenWindow;
+ // We use the qobject_cast here to detect that the newOffscreen is
+ // not the one getting destroyed right now.
+ if (qobject_cast<QQuickWindow *>(newOffscreen)) {
+ m_accessibleWindow.reset(new QAccessibleQuickWindow(newOffscreen));
+ m_connection = QObject::connect(newOffscreen, &QObject::destroyed, theWidget,
+ [this] { repairWindow(); });
+ }
+ }
}
QAccessibleInterface *QAccessibleQuickWidget::child(int index) const
{
- return m_accessibleWindow.child(index);
+ return m_accessibleWindow->child(index);
}
int QAccessibleQuickWidget::childCount() const
{
- return m_accessibleWindow.childCount();
+ return m_accessibleWindow->childCount();
}
int QAccessibleQuickWidget::indexOfChild(const QAccessibleInterface *iface) const
{
- return m_accessibleWindow.indexOfChild(iface);
+ return m_accessibleWindow->indexOfChild(iface);
}
QAccessibleInterface *QAccessibleQuickWidget::childAt(int x, int y) const
{
- return m_accessibleWindow.childAt(x, y);
+ return m_accessibleWindow->childAt(x, y);
}
QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window)
diff --git a/src/quickwidgets/qaccessiblequickwidget_p.h b/src/quickwidgets/qaccessiblequickwidget_p.h
index ea86433c41..a0c99b7d97 100644
--- a/src/quickwidgets/qaccessiblequickwidget_p.h
+++ b/src/quickwidgets/qaccessiblequickwidget_p.h
@@ -31,6 +31,7 @@ class QAccessibleQuickWidget: public QAccessibleWidget
{
public:
QAccessibleQuickWidget(QQuickWidget* widget);
+ ~QAccessibleQuickWidget();
QAccessibleInterface *child(int index) const override;
int childCount() const override;
@@ -38,7 +39,10 @@ public:
QAccessibleInterface *childAt(int x, int y) const override;
private:
- QAccessibleQuickWindow m_accessibleWindow;
+ void repairWindow();
+
+ std::unique_ptr<QAccessibleQuickWindow> m_accessibleWindow;
+ QMetaObject::Connection m_connection;
Q_DISABLE_COPY(QAccessibleQuickWidget)
};
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index e914d8d6b3..c782dc74b1 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -47,6 +47,10 @@
#include <QtWidgets/qgraphicsview.h>
#endif
+#if QT_CONFIG(vulkan)
+#include <QtGui/private/qvulkandefaultinstance_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control)
@@ -196,6 +200,27 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
if (!engine.isNull() && !engine.data()->incubationController())
engine.data()->setIncubationController(offscreenWindow->incubationController());
+ q->setMouseTracking(true);
+ q->setFocusPolicy(Qt::StrongFocus);
+#ifndef Q_OS_MACOS
+ /*
+ Usually, a QTouchEvent comes from a touchscreen, and we want those
+ touch events in Qt Quick. But on macOS, there are no touchscreens, and
+ WA_AcceptTouchEvents has a different meaning: QApplication::notify()
+ calls the native-integration function registertouchwindow() to change
+ NSView::allowedTouchTypes to include NSTouchTypeMaskIndirect when the
+ trackpad cursor enters the window, and removes that mask when the
+ cursor exits. In other words, WA_AcceptTouchEvents enables getting
+ discrete touchpoints from the trackpad. We rather prefer to get mouse,
+ wheel and native gesture events from the trackpad (because those
+ provide more of a "native feel"). The only exception is for
+ MultiPointTouchArea, and it takes care of that for itself. So don't
+ automatically set WA_AcceptTouchEvents on macOS. The user can still do
+ it, but we don't recommend it.
+ */
+ q->setAttribute(Qt::WA_AcceptTouchEvents);
+#endif
+
#if QT_CONFIG(quick_draganddrop)
q->setAcceptDrops(true);
#endif
@@ -241,7 +266,7 @@ void QQuickWidgetPrivate::handleWindowChange()
// must be recreated because its RHI will contain a dangling pointer to
// the context.
- delete offscreenWindow;
+ QScopedPointer<QQuickWindow> oldOffScreenWindow(offscreenWindow); // Do not delete before reparenting sgItem
offscreenWindow = nullptr;
delete renderControl;
@@ -335,8 +360,10 @@ void QQuickWidgetPrivate::render(bool needsSync)
q->createFramebufferObject();
}
- if (!rhi)
+ if (!rhi) {
+ qWarning("QQuickWidget: Attempted to render scene with no rhi");
return;
+ }
// createFramebufferObject() bails out when the size is empty. In this case
// we cannot render either.
@@ -396,11 +423,6 @@ void QQuickWidgetPrivate::renderSceneGraph()
if (!q->isVisible() || fakeHidden)
return;
- if (!useSoftwareRenderer && !rhi) {
- qWarning("QQuickWidget: Attempted to render scene with no rhi");
- return;
- }
-
render(true);
#if QT_CONFIG(graphicsview)
@@ -606,24 +628,22 @@ QImage QQuickWidgetPrivate::grabFramebuffer()
*/
/*!
- Constructs a QQuickWidget with the given \a parent.
- The default value of \a parent is 0.
+ Constructs a QQuickWidget with a default QML engine as a child of \a parent.
+ The default value of \a parent is \c nullptr.
*/
QQuickWidget::QQuickWidget(QWidget *parent)
: QWidget(*(new QQuickWidgetPrivate), parent, {})
{
- setMouseTracking(true);
- setFocusPolicy(Qt::StrongFocus);
- setAttribute(Qt::WA_AcceptTouchEvents);
d_func()->init();
}
/*!
- Constructs a QQuickWidget with the given QML \a source and \a parent.
- The default value of \a parent is 0.
+ Constructs a QQuickWidget with a default QML engine and the given QML \a source
+ as a child of \a parent.
-*/
+ The default value of \a parent is \c nullptr.
+ */
QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
: QQuickWidget(parent)
{
@@ -631,19 +651,15 @@ QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
}
/*!
- Constructs a QQuickWidget with the given QML \a engine and \a parent.
+ Constructs a QQuickWidget with the given QML \a engine as a child of \a parent.
- Note: In this case, the QQuickWidget does not own the given \a engine object;
+ \note The QQuickWidget does not take ownership of the given \a engine object;
it is the caller's responsibility to destroy the engine. If the \a engine is deleted
- before the view, status() will return QQuickWidget::Error.
-
- \sa Status, status(), errors()
+ before the view, \l status() will return \l QQuickWidget::Error.
*/
QQuickWidget::QQuickWidget(QQmlEngine* engine, QWidget *parent)
: QWidget(*(new QQuickWidgetPrivate), parent, {})
{
- setMouseTracking(true);
- setFocusPolicy(Qt::StrongFocus);
d_func()->init(engine);
}
@@ -658,6 +674,9 @@ QQuickWidget::~QQuickWidget()
delete d->root;
d->root = nullptr;
+ if (d->rhi)
+ d->rhi->removeCleanupCallback(this);
+
// NB! resetting graphics resources must be done from this destructor,
// *not* from the private class' destructor. This is due to how destruction
// works and due to the QWidget dtor (for toplevels) destroying the repaint
@@ -992,6 +1011,8 @@ static inline QPlatformBackingStoreRhiConfig::Api graphicsApiToBackingStoreRhiAp
return QPlatformBackingStoreRhiConfig::Vulkan;
case QSGRendererInterface::Direct3D11:
return QPlatformBackingStoreRhiConfig::D3D11;
+ case QSGRendererInterface::Direct3D12:
+ return QPlatformBackingStoreRhiConfig::D3D12;
case QSGRendererInterface::Metal:
return QPlatformBackingStoreRhiConfig::Metal;
default:
@@ -1004,13 +1025,10 @@ void QQuickWidgetPrivate::initializeWithRhi()
{
Q_Q(QQuickWidget);
- QWidgetPrivate *tlwd = QWidgetPrivate::get(q->window());
// when reparenting, the rhi may suddenly be different
if (rhi) {
- QRhi *tlwRhi = nullptr;
- if (QWidgetRepaintManager *repaintManager = tlwd->maybeRepaintManager())
- tlwRhi = repaintManager->rhi();
- if (tlwRhi && rhi != tlwRhi)
+ QRhi *backingStoreRhi = QWidgetPrivate::rhi();
+ if (backingStoreRhi && rhi != backingStoreRhi)
rhi = nullptr;
}
@@ -1022,8 +1040,17 @@ void QQuickWidgetPrivate::initializeWithRhi()
if (rhi)
return;
- if (QWidgetRepaintManager *repaintManager = tlwd->maybeRepaintManager())
- rhi = repaintManager->rhi();
+ if (QRhi *backingStoreRhi = QWidgetPrivate::rhi()) {
+ rhi = backingStoreRhi;
+ // We don't own the RHI, so make sure we clean up if it goes away
+ rhi->addCleanupCallback(q, [this](QRhi *rhi) {
+ if (this->rhi == rhi) {
+ invalidateRenderControl();
+ deviceLost = true;
+ this->rhi = nullptr;
+ }
+ });
+ }
if (!rhi) {
// The widget (and its parent chain, if any) may not be shown at
@@ -1054,6 +1081,8 @@ void QQuickWidgetPrivate::initializeWithRhi()
#if QT_CONFIG(vulkan)
if (QWindow *w = q->window()->windowHandle())
offscreenWindow->setVulkanInstance(w->vulkanInstance());
+ else if (rhi == offscreenRenderer.rhi())
+ offscreenWindow->setVulkanInstance(QVulkanDefaultInstance::instance());
#endif
renderControl->initialize();
}
@@ -1100,7 +1129,7 @@ void QQuickWidget::createFramebufferObject()
// Could be a simple hide - show, in which case the previous texture is just fine.
if (!d->outputTexture) {
- d->outputTexture = d->rhi->newTexture(QRhiTexture::RGBA8, fboSize, 1, QRhiTexture::RenderTarget);
+ d->outputTexture = d->rhi->newTexture(QRhiTexture::RGBA8, fboSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
if (!d->outputTexture->create()) {
qWarning("QQuickWidget: failed to create output texture of size %dx%d",
fboSize.width(), fboSize.height());
@@ -1462,6 +1491,8 @@ void QQuickWidget::mouseMoveEvent(QMouseEvent *e)
// top-level window always.
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ // It's not just the timestamp but also the globalPressPosition, velocity etc.
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1477,10 +1508,12 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
// See QTBUG-25831
QMouseEvent pressEvent(QEvent::MouseButtonPress, e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ pressEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent);
e->setAccepted(pressEvent.isAccepted());
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
}
@@ -1540,6 +1573,7 @@ void QQuickWidget::mousePressEvent(QMouseEvent *e)
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1553,6 +1587,7 @@ void QQuickWidget::mouseReleaseEvent(QMouseEvent *e)
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1646,7 +1681,7 @@ bool QQuickWidget::event(QEvent *e)
QPointerEvent *pointerEvent = static_cast<QPointerEvent *>(e);
auto deliveredPoints = pointerEvent->points();
for (auto &point : deliveredPoints) {
- if (pointerEvent->exclusiveGrabber(point))
+ if (pointerEvent->exclusiveGrabber(point) || !pointerEvent->passiveGrabbers(point).isEmpty())
point.setAccepted(true);
}
}
@@ -1670,7 +1705,10 @@ bool QQuickWidget::event(QEvent *e)
}
case QEvent::WindowAboutToChangeInternal:
+ if (d->rhi)
+ d->rhi->removeCleanupCallback(this);
d->invalidateRenderControl();
+ d->deviceLost = true;
d->rhi = nullptr;
break;
@@ -1683,15 +1721,20 @@ bool QQuickWidget::event(QEvent *e)
QScreen *newScreen = screen();
if (d->offscreenWindow)
d->offscreenWindow->setScreen(newScreen);
-
+ break;
+ }
+ case QEvent::DevicePixelRatioChange:
if (d->useSoftwareRenderer || d->outputTexture) {
// This will check the size taking the devicePixelRatio into account
// and recreate if needed.
createFramebufferObject();
d->render(true);
}
+ if (d->offscreenWindow) {
+ QEvent dprChangeEvent(QEvent::DevicePixelRatioChange);
+ QGuiApplication::sendEvent(d->offscreenWindow, &dprChangeEvent);
+ }
break;
- }
case QEvent::Show:
case QEvent::Move:
d->updatePosition();
diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h
index 7167f4fdea..181fbd8d92 100644
--- a/src/quickwidgets/qquickwidget_p.h
+++ b/src/quickwidgets/qquickwidget_p.h
@@ -17,7 +17,7 @@
#include "qquickwidget.h"
#include <private/qwidget_p.h>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <private/qbackingstorerhisupport_p.h>
#include <QtCore/qurl.h>
@@ -67,7 +67,7 @@ public:
QPlatformTextureList::Flags textureListFlags() override;
QImage grabFramebuffer() override;
- void init(QQmlEngine* e = 0);
+ void init(QQmlEngine* e = nullptr);
void ensureBackingScene();
void initOffscreenWindow();
void ensureEngine() const;
diff --git a/sync.profile b/sync.profile
deleted file mode 100644
index d35bb5f440..0000000000
--- a/sync.profile
+++ /dev/null
@@ -1,37 +0,0 @@
-%modules = ( # path to module name map
- "QtQmlIntegration" => "$basedir/src/qmlintegration",
- "QtQml" => "$basedir/src/qml",
- "QtQmlCore" => "$basedir/src/core",
- "QtQuick" => "$basedir/src/quick",
- "QtQuickShapes" => "$basedir/src/quickshapes",
- "QtQuickWidgets" => "$basedir/src/quickwidgets",
- "QtQuickParticles" => "$basedir/src/particles",
- "QtQuickTest" => "$basedir/src/qmltest",
- "QtQuickTestUtils" => "$basedir/src/quicktestutils",
- "QtPacketProtocol" => "$basedir/src/plugins/qmltooling/packetprotocol",
- "QtQmlDebug" => "$basedir/src/qmldebug",
- "QtQmlModels" => "$basedir/src/qmlmodels",
- "QtQmlWorkerScript" => "$basedir/src/qmlworkerscript",
- "QtQmlCompiler" => "$basedir/src/qmlcompiler",
- "QtQmlTypeRegistrar" => "$basedir/src/qmltyperegistrar/",
- "QtQmlDom" => "$basedir/src/qmldom",
- "QtQuickLayouts" => "$basedir/src/quicklayouts",
- "QtQmlLocalStorage" => "$basedir/src/qmllocalstorage",
- "QtLabsSettings" => "$basedir/src/labs/settings",
- "QtLabsFolderListModel" => "$basedir/src/labs/folderlistmodel",
- "QtLabsAnimation" => "$basedir/src/labs/animation",
- "QtLabsWavefrontMesh" => "$basedir/src/labs/wavefrontmesh",
- "QtLabsQmlModels" => "$basedir/src/labs/models",
- "QtLabsSharedImage" => "$basedir/src/labs/sharedimage",
- "QtQmlXmlListModel" => "$basedir/src/qmlxmllistmodel",
- "QtQuickControls2" => "$basedir/src/quickcontrols",
- "QtQuickControls2Impl" => "$basedir/src/quickcontrolsimpl",
- "QtQuickControlsTestUtils" => "$basedir/src/quickcontrolstestutils",
- "QtQuickDialogs2" => "$basedir/src/quickdialogs/quickdialogs",
- "QtQuickDialogs2Utils" => "$basedir/src/quickdialogs/quickdialogsutils",
- "QtQuickDialogs2QuickImpl" => "$basedir/src/quickdialogs/quickdialogsquickimpl",
- "QtQuickTemplates2" => "$basedir/src/quicktemplates"
-);
-%inject_headers = (
- "$basedir/src/qml" => [ "^qqmljsgrammar_p.h", "^qqmljsparser_p.h", "^qml_compile_hash_p.h" ],
-);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 800bc265b0..e32cbf04a0 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -6,5 +6,6 @@
if(QT_BUILD_STANDALONE_TESTS)
# Add qt_find_package calls for extra dependencies that need to be found when building
# the standalone tests here.
+ qt_find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS QmlLSPrivate)
endif()
qt_build_tests()
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
index 74c4ee86d6..b47bd318d5 100644
--- a/tests/auto/CMakeLists.txt
+++ b/tests/auto/CMakeLists.txt
@@ -26,6 +26,7 @@ if(TARGET Qt::Quick)
endif()
if(TARGET Qt::QuickTest)
add_subdirectory(core)
+ add_subdirectory(qmlnetwork)
endif()
add_subdirectory(toolsupport)
if(NOT UIKIT AND NOT ANDROID AND NOT QNX) # FIXME: QTBUG-92591 QTBUG-100202
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index fd2053176a..0e168cf6ad 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -93,6 +93,12 @@ if(TARGET Qt::Quick)
)
endif()
+if(TARGET Qt::QuickControls2)
+ list(APPEND module_includes
+ QuickControls2 QQuickStyle
+ )
+endif()
+
_qt_internal_test_module_includes(${module_includes})
if(TARGET Qt::Qml)
@@ -110,6 +116,15 @@ if(TARGET Qt::Qml)
If(NOT ANDROID) # QML only project cannot run on Android with C++ enty point
_qt_internal_test_expect_pass(qmlquery)
endif()
+ _qt_internal_test_expect_fail(test_internal_singleton)
+
+ # Make sure that we don't attempt to run binary when crosscompiling
+ if(NOT CMAKE_CROSSCOMPILING)
+ set(test_static_qml_module_extra_args BINARY test_static_qml_module)
+ else()
+ set(test_static_qml_module_extra_args "")
+ endif()
+ _qt_internal_test_expect_pass(test_static_qml_module ${test_static_qml_module_extra_args})
endif()
if(TARGET Qt::Quick)
@@ -121,6 +136,12 @@ if(TARGET Qt::Quick)
BINARY cmake_test
)
endif()
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.19")
+ _qt_internal_test_expect_pass(test_generate_qmlls_ini BINARY tst_generate_qmlls_ini)
+ endif()
+ _qt_internal_test_expect_pass(
+ test_link_qml_module_without_target
+ BINARY test_link_qml_module_without_target)
endif()
if(NOT QT6_IS_SHARED_LIBS_BUILD)
_qt_internal_test_expect_pass(test_import_static_shapes_plugin_resources
@@ -135,4 +156,3 @@ if(TARGET Qt::Quick)
endif()
endif()
endif()
-
diff --git a/tests/auto/cmake/empty_qmldir/enemy.cpp b/tests/auto/cmake/empty_qmldir/enemy.cpp
index 5365d75e05..2821cef07c 100644
--- a/tests/auto/cmake/empty_qmldir/enemy.cpp
+++ b/tests/auto/cmake/empty_qmldir/enemy.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "enemy.h"
diff --git a/tests/auto/cmake/empty_qmldir/enemy.h b/tests/auto/cmake/empty_qmldir/enemy.h
index 051176c8f6..96b3516884 100644
--- a/tests/auto/cmake/empty_qmldir/enemy.h
+++ b/tests/auto/cmake/empty_qmldir/enemy.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef ENEMY_H
#define ENEMY_H
diff --git a/tests/auto/cmake/empty_qmldir/main.cpp b/tests/auto/cmake/empty_qmldir/main.cpp
index e5a2176253..8bb5981bd9 100644
--- a/tests/auto/cmake/empty_qmldir/main.cpp
+++ b/tests/auto/cmake/empty_qmldir/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qobject.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/cmake/qmlimportscanner/main.cpp b/tests/auto/cmake/qmlimportscanner/main.cpp
index 8b6cc65c87..7dc3c46ca1 100644
--- a/tests/auto/cmake/qmlimportscanner/main.cpp
+++ b/tests/auto/cmake/qmlimportscanner/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtQml>
diff --git a/tests/auto/cmake/qmlquery/CMakeLists.txt b/tests/auto/cmake/qmlquery/CMakeLists.txt
index 5baac4f967..bafabe70f2 100644
--- a/tests/auto/cmake/qmlquery/CMakeLists.txt
+++ b/tests/auto/cmake/qmlquery/CMakeLists.txt
@@ -8,3 +8,4 @@ find_package(Qt6 REQUIRED COMPONENTS Qml)
set(CMAKE_AUTOMOC TRUE)
add_subdirectory(My/Things)
+add_subdirectory(My/OtherThings)
diff --git a/tests/auto/cmake/qmlquery/My/OtherThings/CMakeLists.txt b/tests/auto/cmake/qmlquery/My/OtherThings/CMakeLists.txt
new file mode 100644
index 0000000000..5d2bbc4fdd
--- /dev/null
+++ b/tests/auto/cmake/qmlquery/My/OtherThings/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_policy(SET QTP0001 NEW)
+
+function(verify_result keyword expected actual)
+ if(NOT "${actual}" STREQUAL "${expected}")
+ message(SEND_ERROR
+ " Expected ${keyword}: ${expected}\n"
+ " Actual ${keyword}: ${actual}"
+ )
+ endif()
+endfunction()
+
+qt_add_qml_module(MyOtherThings
+ URI My.OtherThings
+ SOURCES
+ test.h test.cpp
+)
+
+qt_query_qml_module(MyOtherThings
+ QML_FILES qml_files2
+ RESOURCES resources2
+)
+
+# empty resources and files
+verify_result(RESOURCES "${resources2}" "")
+verify_result(QML_FILES "${qml_files2}" "")
diff --git a/tests/auto/cmake/qmlquery/My/OtherThings/test.cpp b/tests/auto/cmake/qmlquery/My/OtherThings/test.cpp
new file mode 100644
index 0000000000..b9dde77775
--- /dev/null
+++ b/tests/auto/cmake/qmlquery/My/OtherThings/test.cpp
@@ -0,0 +1,3 @@
+#include "test.h"
+
+Test::Test() {}
diff --git a/tests/auto/cmake/qmlquery/My/OtherThings/test.h b/tests/auto/cmake/qmlquery/My/OtherThings/test.h
new file mode 100644
index 0000000000..93f71766a8
--- /dev/null
+++ b/tests/auto/cmake/qmlquery/My/OtherThings/test.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TEST_H
+#define TEST_H
+
+#include <QtQml/qqml.h>
+#include <QObject>
+
+class Test : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ Test();
+};
+
+#endif // TEST_H
diff --git a/tests/auto/cmake/qtquickcompiler/main.cpp b/tests/auto/cmake/qtquickcompiler/main.cpp
index 4cfc0bab2e..6780374dd3 100644
--- a/tests/auto/cmake/qtquickcompiler/main.cpp
+++ b/tests/auto/cmake/qtquickcompiler/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtQml>
diff --git a/tests/auto/cmake/test_common_import_path/CMakeLists.txt b/tests/auto/cmake/test_common_import_path/CMakeLists.txt
index 6071128010..13af36846b 100644
--- a/tests/auto/cmake/test_common_import_path/CMakeLists.txt
+++ b/tests/auto/cmake/test_common_import_path/CMakeLists.txt
@@ -13,6 +13,11 @@ set(CMAKE_AUTOMOC ON)
# Simplify finding the backing targets' DLLs on Windows
if(WIN32)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+else()
+ add_compile_options (
+ -Werror # Treat all warnings as errors
+ -Wshadow # Warn on shadowing
+ )
endif()
add_subdirectory(duck/tick)
diff --git a/tests/auto/cmake/test_common_import_path/main.cpp b/tests/auto/cmake/test_common_import_path/main.cpp
index 5cc81b1573..9430c94f31 100644
--- a/tests/auto/cmake/test_common_import_path/main.cpp
+++ b/tests/auto/cmake/test_common_import_path/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt
new file mode 100644
index 0000000000..f0fa09a66a
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.19)
+project(tst_generate_qmlls_ini)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Qml Test)
+
+qt_standard_project_setup()
+
+qt_add_executable(tst_generate_qmlls_ini main.cpp)
+target_link_libraries(tst_generate_qmlls_ini PRIVATE Qt6::Test)
+
+set(QT_QML_GENERATE_QMLLS_INI ON CACHE BOOL "" FORCE)
+
+add_subdirectory(SomeSubfolder)
+add_subdirectory(Dotted)
+
+qt_add_qml_module(tst_generate_qmlls_ini
+ URI MainModule
+ VERSION 1.0
+ NO_RESOURCE_TARGET_PATH
+ SOURCES
+ main.cpp
+ QML_FILES
+ Main.qml
+)
+target_compile_definitions(tst_generate_qmlls_ini
+ PRIVATE
+ "SOURCE_DIRECTORY=u\"${CMAKE_CURRENT_SOURCE_DIR}\"_s"
+ "BUILD_DIRECTORY=u\"${CMAKE_CURRENT_BINARY_DIR}\"_s"
+)
+
+qt_add_qml_module(Module
+ URI Module
+ VERSION 1.0
+ QML_FILES
+ Main.qml
+ OUTPUT_DIRECTORY ./qml/hello/subfolders/Module
+)
+
+# Ensure linting runs when building the default "all" target
+set_target_properties(all_qmllint PROPERTIES EXCLUDE_FROM_ALL FALSE)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/CMakeLists.txt
new file mode 100644
index 0000000000..e3d4518aae
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(Uri)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/CMakeLists.txt
new file mode 100644
index 0000000000..a2edbc25ce
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_add_qml_module(DottedUri
+ URI Dotted.Uri
+ QML_FILES Main.qml
+)
+
+add_subdirectory(Hello)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/CMakeLists.txt
new file mode 100644
index 0000000000..ecceec7e73
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(World)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/CMakeLists.txt
new file mode 100644
index 0000000000..338674bc93
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_add_qml_module(DottedUri2
+ URI Dotted.Uri.Hello.World
+ QML_FILES Main.qml
+)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/Main.qml b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/Main.qml
new file mode 100644
index 0000000000..f97cbcf115
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Hello/World/Main.qml
@@ -0,0 +1,4 @@
+import QtQuick
+
+Item {
+}
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Main.qml b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Main.qml
new file mode 100644
index 0000000000..f97cbcf115
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Dotted/Uri/Main.qml
@@ -0,0 +1,4 @@
+import QtQuick
+
+Item {
+}
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/Main.qml b/tests/auto/cmake/test_generate_qmlls_ini/Main.qml
new file mode 100644
index 0000000000..68c21087cb
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/Main.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+
+}
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/SomeSubfolder/CMakeLists.txt b/tests/auto/cmake/test_generate_qmlls_ini/SomeSubfolder/CMakeLists.txt
new file mode 100644
index 0000000000..ae6fb009f8
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/SomeSubfolder/CMakeLists.txt
@@ -0,0 +1,13 @@
+qt_add_qml_module(ModuleA
+ URI ModuleA
+ VERSION 1.0
+ QML_FILES Main.qml
+ OUTPUT_DIRECTORY ./qml/Some/Sub/Folder/ModuleA
+)
+
+qt_add_qml_module(ModuleB
+ URI ModuleB
+ VERSION 1.0
+ QML_FILES Main.qml
+ OUTPUT_DIRECTORY ./qml/Some/Sub/Folder/ModuleB
+)
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/SomeSubfolder/Main.qml b/tests/auto/cmake/test_generate_qmlls_ini/SomeSubfolder/Main.qml
new file mode 100644
index 0000000000..68c21087cb
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/SomeSubfolder/Main.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+
+}
diff --git a/tests/auto/cmake/test_generate_qmlls_ini/main.cpp b/tests/auto/cmake/test_generate_qmlls_ini/main.cpp
new file mode 100644
index 0000000000..a7bdbf1e18
--- /dev/null
+++ b/tests/auto/cmake/test_generate_qmlls_ini/main.cpp
@@ -0,0 +1,96 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtQml/qqml.h>
+#include <QtTest/qtest.h>
+
+class tst_generate_qmlls_ini : public QObject
+{
+ Q_OBJECT
+private slots:
+ void qmllsIniAreCorrect();
+};
+
+using namespace Qt::StringLiterals;
+
+#ifndef SOURCE_DIRECTORY
+# define SOURCE_DIRECTORY u"invalid_source_directory"_s
+#endif
+#ifndef BUILD_DIRECTORY
+# define BUILD_DIRECTORY u"invalid_build_directory"_s
+#endif
+
+void tst_generate_qmlls_ini::qmllsIniAreCorrect()
+{
+ const QString qmllsIniName = u".qmlls.ini"_s;
+ QDir source(SOURCE_DIRECTORY);
+ QDir build(BUILD_DIRECTORY);
+ if (!source.exists())
+ QSKIP(u"Cannot find source directory '%1', skipping test..."_s.arg(SOURCE_DIRECTORY)
+ .toLatin1());
+
+ {
+ auto file = QFile(source.absoluteFilePath(qmllsIniName));
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QFile::ReadOnly | QFile::Text));
+ const auto fileContent = QString::fromUtf8(file.readAll());
+ auto secondFolder = QDir(build.absolutePath().append(u"/qml/hello/subfolders"_s));
+ QVERIFY(secondFolder.exists());
+ QCOMPARE(fileContent,
+ u"[General]\nbuildDir=%1%2%3\nno-cmake-calls=false\n"_s.arg(build.absolutePath(), QDir::listSeparator(),
+ secondFolder.absolutePath()));
+ }
+
+ {
+ QDir sourceSubfolder = source;
+ QVERIFY(sourceSubfolder.cd(u"SomeSubfolder"_s));
+ QDir buildSubfolder(build.absolutePath().append(u"/SomeSubfolder/qml/Some/Sub/Folder"_s));
+ {
+ auto file = QFile(sourceSubfolder.absoluteFilePath(qmllsIniName));
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QFile::ReadOnly | QFile::Text));
+ const auto fileContent = QString::fromUtf8(file.readAll());
+ QCOMPARE(fileContent,
+ u"[General]\nbuildDir=%1\nno-cmake-calls=false\n"_s.arg(buildSubfolder.absolutePath()));
+ }
+ }
+
+ {
+ QDir dottedUriSubfolder = source;
+ QVERIFY(dottedUriSubfolder.cd(u"Dotted"_s));
+ QVERIFY(dottedUriSubfolder.cd(u"Uri"_s));
+ {
+ auto file = QFile(dottedUriSubfolder.absoluteFilePath(qmllsIniName));
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QFile::ReadOnly | QFile::Text));
+ const auto fileContent = QString::fromUtf8(file.readAll());
+ QCOMPARE(
+ fileContent,
+ u"[General]\nbuildDir=%1\nno-cmake-calls=false\n"_s.arg(build.absolutePath()));
+ }
+ }
+ {
+ QDir dottedUriSubfolder = source;
+ QVERIFY(dottedUriSubfolder.cd(u"Dotted"_s));
+ QVERIFY(dottedUriSubfolder.cd(u"Uri"_s));
+ QVERIFY(dottedUriSubfolder.cd(u"Hello"_s));
+ QVERIFY(dottedUriSubfolder.cd(u"World"_s));
+ {
+ auto file = QFile(dottedUriSubfolder.absoluteFilePath(qmllsIniName));
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QFile::ReadOnly | QFile::Text));
+ const auto fileContent = QString::fromUtf8(file.readAll());
+ QCOMPARE(
+ fileContent,
+ u"[General]\nbuildDir=%1\nno-cmake-calls=false\n"_s.arg(build.absolutePath()));
+ }
+ }
+}
+
+QTEST_MAIN(tst_generate_qmlls_ini)
+
+#include "main.moc"
diff --git a/tests/auto/cmake/test_import_static_shapes_plugin_resources/app.qml b/tests/auto/cmake/test_import_static_shapes_plugin_resources/app.qml
index e470b2e240..20a0d1db78 100644
--- a/tests/auto/cmake/test_import_static_shapes_plugin_resources/app.qml
+++ b/tests/auto/cmake/test_import_static_shapes_plugin_resources/app.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Shapes
diff --git a/tests/auto/cmake/test_import_static_shapes_plugin_resources/tst_main.cpp b/tests/auto/cmake/test_import_static_shapes_plugin_resources/tst_main.cpp
index 4d6265a9e6..5b99d8f22e 100644
--- a/tests/auto/cmake/test_import_static_shapes_plugin_resources/tst_main.cpp
+++ b/tests/auto/cmake/test_import_static_shapes_plugin_resources/tst_main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QtGlobal>
#include <QtCore/QScopeGuard>
diff --git a/tests/auto/cmake/test_internal_singleton/CMakeLists.txt b/tests/auto/cmake/test_internal_singleton/CMakeLists.txt
new file mode 100644
index 0000000000..f1db46a0f9
--- /dev/null
+++ b/tests/auto/cmake/test_internal_singleton/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+cmake_minimum_required(VERSION 3.19)
+
+project(test_internal_singleton)
+
+find_package(Qt6 REQUIRED COMPONENTS Qml)
+
+qt_standard_project_setup()
+
+set_source_files_properties(Test.qml PROPERTIES
+ QT_QML_SINGLETON_TYPE TRUE
+ QT_QML_INTERNAL_TYPE TRUE
+)
+
+qt_add_qml_module(test_internal_singleton
+ URI Controls
+ VERSION 1.0
+ QML_FILES
+ Test.qml
+)
diff --git a/tests/auto/cmake/test_internal_singleton/Test.qml b/tests/auto/cmake/test_internal_singleton/Test.qml
new file mode 100644
index 0000000000..a2eb03bd4e
--- /dev/null
+++ b/tests/auto/cmake/test_internal_singleton/Test.qml
@@ -0,0 +1,4 @@
+pragma singleton
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/Base/Base.cpp b/tests/auto/cmake/test_link_qml_module_without_target/Base/Base.cpp
new file mode 100644
index 0000000000..765c14383f
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/Base/Base.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "Base.h"
+#include <QDebug>
+
+Base::Base(QObject *parent) : QObject(parent)
+{
+ qDebug() << Q_FUNC_INFO << "Base library loaded";
+}
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/Base/Base.h b/tests/auto/cmake/test_link_qml_module_without_target/Base/Base.h
new file mode 100644
index 0000000000..9835131a57
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/Base/Base.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef BASE_H
+#define BASE_H
+
+#include <QObject>
+
+class Base : public QObject
+{
+ Q_OBJECT
+public:
+ explicit Base(QObject *parent = nullptr);
+};
+
+#endif // BASE_H
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/Base/CMakeLists.txt b/tests/auto/cmake/test_link_qml_module_without_target/Base/CMakeLists.txt
new file mode 100644
index 0000000000..1d94c8ea21
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/Base/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_add_library(Base STATIC)
+qt_add_qml_module(Base
+ URI "Base"
+ QML_FILES
+ qml/Red.qml
+ SOURCES
+ Base.cpp
+)
+
+target_link_libraries(Base PRIVATE Qt6::Quick)
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/Base/qml/Red.qml b/tests/auto/cmake/test_link_qml_module_without_target/Base/qml/Red.qml
new file mode 100644
index 0000000000..c75f13d7a8
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/Base/qml/Red.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ width: 320
+ height: 240
+ color: "red"
+}
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/CMakeLists.txt b/tests/auto/cmake/test_link_qml_module_without_target/CMakeLists.txt
new file mode 100644
index 0000000000..86c43aa605
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/CMakeLists.txt
@@ -0,0 +1,59 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+project(test_link_qml_module_without_target)
+
+find_package(Qt6 COMPONENTS REQUIRED
+ Core
+ Test
+ Quick
+ QuickLayouts
+ QuickTestUtilsPrivate
+)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+add_subdirectory(Base)
+add_subdirectory(Derived)
+
+qt6_add_executable(test_link_qml_module_without_target main.cpp)
+
+target_link_libraries(test_link_qml_module_without_target PRIVATE
+ Qt6::Core
+ Qt6::Test
+ Qt6::QuickPrivate
+ Qt6::QuickTestUtilsPrivate
+)
+
+# Suppress running qmlimportscanner and auto linking of static qml plugins, to ensure we don't
+# automatically link to the project-created qml module object library initializers.
+# Doing that would be at odds with what the test is trying to test.
+set_target_properties(test_link_qml_module_without_target PROPERTIES _QT_QML_PLUGINS_IMPORTED TRUE)
+
+# Because we disable qmlimportscanner auto-linking above, we need to manually link to the Qt
+# provided qml plugins used by the project. Note that we link to the plugins, and not the
+# backing libraries, to ensure we link to the initializer object libraries as well
+# (which are dependencies of the plugin targets).
+if(NOT QT6_IS_SHARED_LIBS_BUILD)
+ target_link_libraries(test_link_qml_module_without_target PRIVATE
+ Qt6::qtquick2plugin
+ Qt6::qquicklayoutsplugin
+ )
+endif()
+
+# Make sure we build the libraries before we link to them.
+add_dependencies(test_link_qml_module_without_target Base Baseplugin Derived Derivedplugin)
+
+# Link against the library files and not the targets, so that we can confirm
+# the ability to manually initialize the qml modules and their resources from inside main.cpp
+# using Q_IMPORT_QML_PLUGIN calls.
+# The order of libraries is important, the plugins need to come before the backing libraries
+# so that linkers like 'Linux ld' don't discard symbols that haven't yet been marked as required.
+target_link_libraries(test_link_qml_module_without_target PRIVATE
+ $<TARGET_FILE:Baseplugin>
+ $<TARGET_FILE:Base>
+ $<TARGET_FILE:Derivedplugin>
+ $<TARGET_FILE:Derived>
+)
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/Derived/CMakeLists.txt b/tests/auto/cmake/test_link_qml_module_without_target/Derived/CMakeLists.txt
new file mode 100644
index 0000000000..08f1155e11
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/Derived/CMakeLists.txt
@@ -0,0 +1,42 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# See comment below why we do this conditionally.
+set(main_qml "qml/main.qml")
+if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.19")
+ set(qml_extra_files "")
+else()
+ set(qml_extra_files "${main_qml}")
+endif()
+
+qt_add_library(Derived STATIC)
+qt_add_qml_module(Derived
+ URI "Derived"
+ QML_FILES
+ qml/Blue.qml
+ ${qml_extra_files}
+)
+
+# The library has no cpp sources, prevent 'ranlib: has no symbols' warnings
+set_target_properties(Derived PROPERTIES AUTOMOC OFF)
+
+# Add one more qml source in a separate qt_target_qml_sources call,
+# to ensure we correctly create an additional resource for the same
+# qml module and that linking to the qml module plugin + calling
+# Q_IMPORT_PLUGIN will initialize the resource.
+# We can only do it with CMake 3.19+, otherwise we get a configure time
+# error in qt6_target_qml_sources.
+if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.19")
+ qt6_target_qml_sources(Derived
+ QML_FILES
+ ${main_qml}
+ )
+endif()
+
+# Make sure we build the libraries before we link to them.
+add_dependencies(Derived Base Baseplugin)
+
+# Manually link to the library files, to avoid bringing in any
+# object library dependencies.
+target_link_libraries(Derived PRIVATE $<TARGET_FILE:Base>)
+target_link_libraries(Derived PRIVATE $<TARGET_FILE:Baseplugin>)
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/Derived/qml/Blue.qml b/tests/auto/cmake/test_link_qml_module_without_target/Derived/qml/Blue.qml
new file mode 100644
index 0000000000..10668894c2
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/Derived/qml/Blue.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ width: 320
+ height: 240
+ color: "blue"
+}
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/Derived/qml/main.qml b/tests/auto/cmake/test_link_qml_module_without_target/Derived/qml/main.qml
new file mode 100644
index 0000000000..7c797cf2ef
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/Derived/qml/main.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Layouts
+import Base
+import Derived
+
+RowLayout {
+ width: 200
+ height: 200
+
+ Red {
+ id: red
+ width: 100
+ height: 100
+ }
+
+ Blue {
+ width: 100
+ height: 100
+ }
+}
diff --git a/tests/auto/cmake/test_link_qml_module_without_target/main.cpp b/tests/auto/cmake/test_link_qml_module_without_target/main.cpp
new file mode 100644
index 0000000000..a0795cb20d
--- /dev/null
+++ b/tests/auto/cmake/test_link_qml_module_without_target/main.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/QDirIterator>
+#include <QtGui/QGuiApplication>
+#include <QtTest/QtTest>
+#include <QtQml/QQmlApplicationEngine>
+#include <QtQml/QQmlContext>
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQuick/QQuickView>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
+
+// Manually import the qml plugins so that the qml modules and their resources
+// are initialized.
+Q_IMPORT_QML_PLUGIN(BasePlugin)
+Q_IMPORT_QML_PLUGIN(DerivedPlugin)
+
+class TestLinkStaticQmlModule : public QObject
+{
+ Q_OBJECT
+private slots:
+ void canRun();
+};
+
+void TestLinkStaticQmlModule::canRun()
+{
+ // Show all the non-Qt loaded resources.
+ QDirIterator it(":", QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ const auto path = it.next();
+ if (!path.startsWith(u":/qt-project.org"_qs)) {
+ qDebug() << path;
+ }
+ }
+
+ QQuickView view;
+ QVERIFY(QFile::exists(":/qt/qml/Base/qml/Red.qml"));
+ QVERIFY(QFile::exists(":/qt/qml/Derived/qml/Blue.qml"));
+ QVERIFY(QQuickTest::showView(view, QUrl(u"qrc:/qt/qml/Derived/qml/main.qml"_qs)));
+}
+
+QTEST_MAIN(TestLinkStaticQmlModule)
+#include "main.moc"
diff --git a/tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.cpp b/tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.cpp
index fda229bbd8..ea99971d37 100644
--- a/tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.cpp
+++ b/tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "ellipseitem.h"
#include <QPainter>
diff --git a/tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.h b/tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.h
index 88ce76a641..b2aa94b3f3 100644
--- a/tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.h
+++ b/tests/auto/cmake/test_qml_app_deployment/Shapes/EllipseShape/ellipseitem.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef ELLIPSEITEM_H
#define ELLIPSEITEM_H
diff --git a/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/FunkyItemQml.qml b/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/FunkyItemQml.qml
index 48794ff428..99af8c58fd 100644
--- a/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/FunkyItemQml.qml
+++ b/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/FunkyItemQml.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Shapes
diff --git a/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.cpp b/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.cpp
index b19232e624..3c6b2321e2 100644
--- a/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.cpp
+++ b/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "funkyitem.h"
#include <QPainter>
diff --git a/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.h b/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.h
index ea0338763d..6899cd41e9 100644
--- a/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.h
+++ b/tests/auto/cmake/test_qml_app_deployment/Shapes/FunkyShape/funkyitem.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef FunkyItem_H
#define FunkyItem_H
diff --git a/tests/auto/cmake/test_qml_app_deployment/main.cpp b/tests/auto/cmake/test_qml_app_deployment/main.cpp
index 00ee6cf8eb..5af318ea14 100644
--- a/tests/auto/cmake/test_qml_app_deployment/main.cpp
+++ b/tests/auto/cmake/test_qml_app_deployment/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/QQmlEngine>
#include <QtQuick/QQuickView>
#include <QtTest/QTest>
diff --git a/tests/auto/cmake/test_qml_app_deployment/main.qml b/tests/auto/cmake/test_qml_app_deployment/main.qml
index 5df35a0592..abac994370 100644
--- a/tests/auto/cmake/test_qml_app_deployment/main.qml
+++ b/tests/auto/cmake/test_qml_app_deployment/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import Shapes
diff --git a/tests/auto/cmake/test_static_qml_module/CMakeLists.txt b/tests/auto/cmake/test_static_qml_module/CMakeLists.txt
new file mode 100644
index 0000000000..83d6b74daa
--- /dev/null
+++ b/tests/auto/cmake/test_static_qml_module/CMakeLists.txt
@@ -0,0 +1,38 @@
+cmake_minimum_required(VERSION 3.16)
+project(test_static_qml_module LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick Test)
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(test_static_qml_module
+ main.cpp
+)
+
+qt_add_qml_module(test_static_qml_module
+ URI MyAppUri
+ VERSION 1.0
+ QML_FILES
+ main.qml
+ IMPORT_PATH
+ ${CMAKE_CURRENT_BINARY_DIR}/qml
+)
+
+target_link_libraries(test_static_qml_module
+ PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+ Qt::Test
+)
+
+qt_add_qml_module(test_static_qml_module_lib
+ STATIC
+ URI MyUri
+ OUTPUT_DIRECTORY qml/MyUri
+ VERSION 1.0
+ SOURCES
+ MyElement.cpp MyElement.h
+)
+
+qt_import_qml_plugins(test_static_qml_module)
diff --git a/tests/auto/cmake/test_static_qml_module/MyElement.cpp b/tests/auto/cmake/test_static_qml_module/MyElement.cpp
new file mode 100644
index 0000000000..6e3cb8cf2e
--- /dev/null
+++ b/tests/auto/cmake/test_static_qml_module/MyElement.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "MyElement.h"
diff --git a/tests/auto/cmake/test_static_qml_module/MyElement.h b/tests/auto/cmake/test_static_qml_module/MyElement.h
new file mode 100644
index 0000000000..118606aa33
--- /dev/null
+++ b/tests/auto/cmake/test_static_qml_module/MyElement.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#pragma once
+
+#include <QObject>
+#include <QtQml>
+
+class MyElement : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QString someProp MEMBER m_someProp)
+public:
+ MyElement() = default;
+ ~MyElement() = default;
+
+private:
+ QString m_someProp;
+};
diff --git a/tests/auto/cmake/test_static_qml_module/main.cpp b/tests/auto/cmake/test_static_qml_module/main.cpp
new file mode 100644
index 0000000000..87d690004a
--- /dev/null
+++ b/tests/auto/cmake/test_static_qml_module/main.cpp
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QObject>
+#include <QString>
+#include <QTest>
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <QQmlExtensionPlugin>
+#include <QUrl>
+
+Q_IMPORT_QML_PLUGIN(MyUriPlugin)
+
+using namespace Qt::StringLiterals;
+
+class TestStaticQmlPlugin : public QObject
+{
+ Q_OBJECT
+private slots:
+ void ElementFromPluginAvailable();
+};
+
+void TestStaticQmlPlugin::ElementFromPluginAvailable()
+{
+ QQmlEngine engine;
+ engine.addImportPath(QStringLiteral(":/qt/qml"));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/MyAppUri/main.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
+
+QTEST_MAIN(TestStaticQmlPlugin)
+
+#include "main.moc"
diff --git a/tests/auto/cmake/test_static_qml_module/main.qml b/tests/auto/cmake/test_static_qml_module/main.qml
new file mode 100644
index 0000000000..29720ebae1
--- /dev/null
+++ b/tests/auto/cmake/test_static_qml_module/main.qml
@@ -0,0 +1,7 @@
+import QtQuick
+import MyUri
+
+MyElement {
+ id: el
+ someProp: "My window title"
+}
diff --git a/tests/auto/cmake/tooling_imports/First/Neighbor.cpp b/tests/auto/cmake/tooling_imports/First/Neighbor.cpp
index d6adca4823..920394cf6e 100644
--- a/tests/auto/cmake/tooling_imports/First/Neighbor.cpp
+++ b/tests/auto/cmake/tooling_imports/First/Neighbor.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "Neighbor.h"
diff --git a/tests/auto/cmake/tooling_imports/First/Neighbor.h b/tests/auto/cmake/tooling_imports/First/Neighbor.h
index e5119bc4c4..05e18d23d7 100644
--- a/tests/auto/cmake/tooling_imports/First/Neighbor.h
+++ b/tests/auto/cmake/tooling_imports/First/Neighbor.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef NEIGHBOR_H
#define NEIGHBOR_H
@@ -15,4 +15,11 @@ public:
Neighbor(QObject *parent = nullptr);
};
+class Absent : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_UNAVAILABLE
+};
+
#endif // NEIGHBOR_H
diff --git a/tests/auto/cmake/tooling_imports/main.cpp b/tests/auto/cmake/tooling_imports/main.cpp
index a1c8dd9daf..92693a832b 100644
--- a/tests/auto/cmake/tooling_imports/main.cpp
+++ b/tests/auto/cmake/tooling_imports/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtQml>
diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt
index fefa6ff1c1..8a5e039673 100644
--- a/tests/auto/core/CMakeLists.txt
+++ b/tests/auto/core/CMakeLists.txt
@@ -3,4 +3,6 @@
add_subdirectory(qqmlstandardpaths)
add_subdirectory(qqmlsysteminformation)
-add_subdirectory(qqmlsettings)
+if(QT_FEATURE_settings)
+ add_subdirectory(qqmlsettings)
+endif()
diff --git a/tests/auto/core/qqmlsettings/CMakeLists.txt b/tests/auto/core/qqmlsettings/CMakeLists.txt
index 0f8b04bbef..2a61b758dd 100644
--- a/tests/auto/core/qqmlsettings/CMakeLists.txt
+++ b/tests/auto/core/qqmlsettings/CMakeLists.txt
@@ -4,7 +4,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qqmlsettings LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
# Collect test data
@@ -23,6 +23,13 @@ qt_internal_add_test(tst_qqmlsettings
TESTDATA ${test_data}
)
+qt_add_resources(tst_qqmlsettings "test_settings"
+ PREFIX
+ "/"
+ FILES
+ "test_settings.ini"
+)
+
qt_internal_extend_target(tst_qqmlsettings CONDITION ANDROID OR IOS
DEFINES
QT_QMLTEST_DATADIR=":/data"
diff --git a/tests/auto/core/qqmlsettings/data/aliases.qml b/tests/auto/core/qqmlsettings/data/aliases.qml
index 547b4d5c13..de05370804 100644
--- a/tests/auto/core/qqmlsettings/data/aliases.qml
+++ b/tests/auto/core/qqmlsettings/data/aliases.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtCore
import QtQuick
diff --git a/tests/auto/core/qqmlsettings/data/basic.qml b/tests/auto/core/qqmlsettings/data/basic.qml
index 2c395b56e3..f23284e199 100644
--- a/tests/auto/core/qqmlsettings/data/basic.qml
+++ b/tests/auto/core/qqmlsettings/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtCore
diff --git a/tests/auto/core/qqmlsettings/data/categories.qml b/tests/auto/core/qqmlsettings/data/categories.qml
index 54cd22a8b1..bac1eb5ebb 100644
--- a/tests/auto/core/qqmlsettings/data/categories.qml
+++ b/tests/auto/core/qqmlsettings/data/categories.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtCore
diff --git a/tests/auto/core/qqmlsettings/data/cpp-aliases.qml b/tests/auto/core/qqmlsettings/data/cpp-aliases.qml
index 22d7338986..146feb7c80 100644
--- a/tests/auto/core/qqmlsettings/data/cpp-aliases.qml
+++ b/tests/auto/core/qqmlsettings/data/cpp-aliases.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtCore
import QtQuick
diff --git a/tests/auto/core/qqmlsettings/data/resources.qml b/tests/auto/core/qqmlsettings/data/resources.qml
new file mode 100644
index 0000000000..84cf89fbf2
--- /dev/null
+++ b/tests/auto/core/qqmlsettings/data/resources.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQml
+import QtCore
+
+Settings {
+ category: "test"
+ location: "qrc:/test_settings.ini"
+ property string text
+}
diff --git a/tests/auto/core/qqmlsettings/data/siblings.qml b/tests/auto/core/qqmlsettings/data/siblings.qml
index e483623d82..f26312ec67 100644
--- a/tests/auto/core/qqmlsettings/data/siblings.qml
+++ b/tests/auto/core/qqmlsettings/data/siblings.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtCore
import QtQuick
diff --git a/tests/auto/core/qqmlsettings/data/types.qml b/tests/auto/core/qqmlsettings/data/types.qml
index 3f38594323..990a908d02 100644
--- a/tests/auto/core/qqmlsettings/data/types.qml
+++ b/tests/auto/core/qqmlsettings/data/types.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtCore
import QtQuick
diff --git a/tests/auto/core/qqmlsettings/test_settings.ini b/tests/auto/core/qqmlsettings/test_settings.ini
new file mode 100644
index 0000000000..7fc7334438
--- /dev/null
+++ b/tests/auto/core/qqmlsettings/test_settings.ini
@@ -0,0 +1,3 @@
+# ini file in resources
+[test]
+text=from resource
diff --git a/tests/auto/core/qqmlsettings/tst_qqmlsettings.cpp b/tests/auto/core/qqmlsettings/tst_qqmlsettings.cpp
index 1b6ab3f68b..236ce4b3e9 100644
--- a/tests/auto/core/qqmlsettings/tst_qqmlsettings.cpp
+++ b/tests/auto/core/qqmlsettings/tst_qqmlsettings.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/QCoreApplication>
@@ -33,6 +33,7 @@ private slots:
void siblings();
void initial();
void noApplicationIdentifiersSet();
+ void fromResources();
};
// ### Replace keyValueMap("foo", "bar") with QVariantMap({{"foo", "bar"}})
@@ -493,6 +494,17 @@ void tst_QQmlSettings::noApplicationIdentifiersSet()
QVERIFY(!settings.value("success").toBool());
}
+void tst_QQmlSettings::fromResources()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("resources.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+
+ QCOMPARE(root->property("text").toString(), QLatin1String("from resource"));
+}
+
QTEST_MAIN(tst_QQmlSettings)
#include "tst_qqmlsettings.moc"
diff --git a/tests/auto/core/qqmlstandardpaths/CMakeLists.txt b/tests/auto/core/qqmlstandardpaths/CMakeLists.txt
index 6078c10755..2ae79515a4 100644
--- a/tests/auto/core/qqmlstandardpaths/CMakeLists.txt
+++ b/tests/auto/core/qqmlstandardpaths/CMakeLists.txt
@@ -4,7 +4,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qqmlstandardpaths LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
# Collect test data
diff --git a/tests/auto/core/qqmlstandardpaths/data/tst_standardpaths.qml b/tests/auto/core/qqmlstandardpaths/data/tst_standardpaths.qml
index afc196e92a..5b88d424ca 100644
--- a/tests/auto/core/qqmlstandardpaths/data/tst_standardpaths.qml
+++ b/tests/auto/core/qqmlstandardpaths/data/tst_standardpaths.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtCore
diff --git a/tests/auto/core/qqmlstandardpaths/tst_qqmlstandardpaths.cpp b/tests/auto/core/qqmlstandardpaths/tst_qqmlstandardpaths.cpp
index b8f7878b41..352ce21f8b 100644
--- a/tests/auto/core/qqmlstandardpaths/tst_qqmlstandardpaths.cpp
+++ b/tests/auto/core/qqmlstandardpaths/tst_qqmlstandardpaths.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QQmlEngine>
#include <QQmlComponent>
@@ -113,9 +113,7 @@ void tst_qqmlstandardpaths::standardLocations()
#endif
QVERIFY2(!path.isEmpty(), qPrintable(QString::fromLatin1(
"Path for %1 received from QStandardPaths::standardLocations is empty").arg(locationName)));
- QUrl url = QUrl(path);
- if (url.scheme().isEmpty())
- url = QUrl::fromLocalFile(path);
+ QUrl url = QUrl::fromLocalFile(path);
QVERIFY(!url.isEmpty());
QVERIFY(url.isValid());
standardLocationUrls.append(url);
diff --git a/tests/auto/core/qqmlsysteminformation/CMakeLists.txt b/tests/auto/core/qqmlsysteminformation/CMakeLists.txt
index c69564634e..b9ccebf1e7 100644
--- a/tests/auto/core/qqmlsysteminformation/CMakeLists.txt
+++ b/tests/auto/core/qqmlsysteminformation/CMakeLists.txt
@@ -4,7 +4,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qqmlsysteminformation LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
# Collect test data
diff --git a/tests/auto/core/qqmlsysteminformation/data/tst_systeminformation.qml b/tests/auto/core/qqmlsysteminformation/data/tst_systeminformation.qml
index afc53dde54..ced47ff328 100644
--- a/tests/auto/core/qqmlsysteminformation/data/tst_systeminformation.qml
+++ b/tests/auto/core/qqmlsysteminformation/data/tst_systeminformation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtCore
diff --git a/tests/auto/core/qqmlsysteminformation/tst_qqmlsysteminformation.cpp b/tests/auto/core/qqmlsysteminformation/tst_qqmlsysteminformation.cpp
index 5a743dee0c..2f9374f5b1 100644
--- a/tests/auto/core/qqmlsysteminformation/tst_qqmlsysteminformation.cpp
+++ b/tests/auto/core/qqmlsysteminformation/tst_qqmlsysteminformation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QQmlEngine>
#include <QQmlComponent>
diff --git a/tests/auto/guiapplauncher/examples.txt b/tests/auto/guiapplauncher/examples.txt
index 40c41e59fb..88ff180a17 100644
--- a/tests/auto/guiapplauncher/examples.txt
+++ b/tests/auto/guiapplauncher/examples.txt
@@ -2,11 +2,8 @@
"quick/models/stringlistmodel Example", "examples/quick/models/stringlistmodel", "stringlistmodel", 0, -1
"quick/models/objectlistmodel Example", "examples/quick/models/objectlistmodel", "objectlistmodel", 0, -1
"quick/models/abstractitemmodel Example", "examples/quick/models/abstractitemmodel", "abstractitemmodel", 0, -1
-"quick/righttoleft Example", "examples/quick/righttoleft", "righttoleft", 0, -1
-"quick/externaldraganddrop Example", "examples/quick/externaldraganddrop", "externaldraganddrop", 0, -1
"quick/customitems/maskedmousearea Example", "examples/quick/customitems/maskedmousearea", "maskedmousearea", 0, -1
"quick/imageelements Example", "examples/quick/imageelements", "imageelements", 0, -1
-"quick/threading Example", "examples/quick/threading", "threading", 0, -1
"quick/scenegraph/openglunderqml Example", "examples/quick/scenegraph/openglunderqml", "openglunderqml", 0, -1
"quick/scenegraph/threadedanimation Example", "examples/quick/scenegraph/threadedanimation", "threadedanimation", 0, -1
"quick/scenegraph/twotextureproviders Example", "examples/quick/scenegraph/twotextureproviders", "twotextureproviders", 0, -1
@@ -30,23 +27,11 @@
"quick/positioners Example", "examples/quick/positioners", "positioners", 0, -1
"quick/shadereffects Example", "examples/quick/shadereffects", "shadereffects", 0, -1
"quick/mousearea Example", "examples/quick/mousearea", "mousearea", 0, -1
-"quick/touchinteraction Example", "examples/quick/touchinteraction", "touchinteraction", 0, -1
"quick/canvas Example", "examples/quick/canvas", "canvas", 0, -1
"quick/text Example", "examples/quick/text", "text", 0, -1
"quick/quickwidgets/qquickviewcomparison Example", "examples/quick/quickwidgets/qquickviewcomparison", "qquickviewcomparison", 0, -1
"quick/quickwidgets/quickwidget Example", "examples/quick/quickwidgets/quickwidget", "quickwidget", 0, -1
"qml/shell Example", "examples/qml/shell", "shell", 10, -1
-"qml/referenceexamples/extended Example", "examples/qml/referenceexamples/extended", "extended", 0, -1
-"qml/referenceexamples/binding Example", "examples/qml/referenceexamples/binding", "binding", 10, -1
-"qml/referenceexamples/adding Example", "examples/qml/referenceexamples/adding", "adding", 10, -1
-"qml/referenceexamples/attached Example", "examples/qml/referenceexamples/attached", "attached", 10, -1
-"qml/referenceexamples/default Example", "examples/qml/referenceexamples/default", "default", 10, -1
-"qml/referenceexamples/signal Example", "examples/qml/referenceexamples/signal", "signal", 10, -1
-"qml/referenceexamples/coercion Example", "examples/qml/referenceexamples/coercion", "coercion", 10, -1
-"qml/referenceexamples/grouped Example", "examples/qml/referenceexamples/grouped", "grouped", 10, -1
-"qml/referenceexamples/valuesource Example", "examples/qml/referenceexamples/valuesource", "valuesource", 10, -1
-"qml/referenceexamples/properties Example", "examples/qml/referenceexamples/properties", "properties", 10, -1
-"qml/referenceexamples/methods Example", "examples/qml/referenceexamples/methods", "methods", 10, -1
"qml/networkaccessmanagerfactory Example", "examples/qml/networkaccessmanagerfactory", "networkaccessmanagerfactory", 10, -1
"qml/xmlhttprequest Example", "examples/qml/xmlhttprequest", "xmlhttprequest", 0, -1
"qmltest/qmltest Example", "examples/qmltest/qmltest", "tst_qmltestexample", 10, -1
diff --git a/tests/auto/particles/qquickage/CMakeLists.txt b/tests/auto/particles/qquickage/CMakeLists.txt
index 0873359a5c..0f941dd421 100644
--- a/tests/auto/particles/qquickage/CMakeLists.txt
+++ b/tests/auto/particles/qquickage/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickage/data/jump.qml b/tests/auto/particles/qquickage/data/jump.qml
index f8bb2c6c99..ebef068da8 100644
--- a/tests/auto/particles/qquickage/data/jump.qml
+++ b/tests/auto/particles/qquickage/data/jump.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickage/data/kill.qml b/tests/auto/particles/qquickage/data/kill.qml
index 7d7a5c47bd..64e71feb08 100644
--- a/tests/auto/particles/qquickage/data/kill.qml
+++ b/tests/auto/particles/qquickage/data/kill.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickage/data/onceoff.qml b/tests/auto/particles/qquickage/data/onceoff.qml
index bed3e53dad..7233fb819a 100644
--- a/tests/auto/particles/qquickage/data/onceoff.qml
+++ b/tests/auto/particles/qquickage/data/onceoff.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickage/data/sustained.qml b/tests/auto/particles/qquickage/data/sustained.qml
index 0f6a6261eb..45cc46a642 100644
--- a/tests/auto/particles/qquickage/data/sustained.qml
+++ b/tests/auto/particles/qquickage/data/sustained.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickage/tst_qquickage.cpp b/tests/auto/particles/qquickage/tst_qquickage.cpp
index 21b79d8921..cff4ab9cba 100644
--- a/tests/auto/particles/qquickage/tst_qquickage.cpp
+++ b/tests/auto/particles/qquickage/tst_qquickage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickangleddirection/CMakeLists.txt b/tests/auto/particles/qquickangleddirection/CMakeLists.txt
index c2914f20ff..5895974c75 100644
--- a/tests/auto/particles/qquickangleddirection/CMakeLists.txt
+++ b/tests/auto/particles/qquickangleddirection/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickangleddirection Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickangleddirection LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickangleddirection/data/basic.qml b/tests/auto/particles/qquickangleddirection/data/basic.qml
index 1c284b2c73..b763bc65d1 100644
--- a/tests/auto/particles/qquickangleddirection/data/basic.qml
+++ b/tests/auto/particles/qquickangleddirection/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp b/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp
index 8b2cb417cb..ff38dabc76 100644
--- a/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp
+++ b/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <qmath.h>
diff --git a/tests/auto/particles/qquickcumulativedirection/CMakeLists.txt b/tests/auto/particles/qquickcumulativedirection/CMakeLists.txt
index 564baccdfd..784d557ae6 100644
--- a/tests/auto/particles/qquickcumulativedirection/CMakeLists.txt
+++ b/tests/auto/particles/qquickcumulativedirection/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickcumulativedirection Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickcumulativedirection LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickcumulativedirection/data/basic.qml b/tests/auto/particles/qquickcumulativedirection/data/basic.qml
index 9cb93ba24e..49538b99c0 100644
--- a/tests/auto/particles/qquickcumulativedirection/data/basic.qml
+++ b/tests/auto/particles/qquickcumulativedirection/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp b/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp
index df427600c5..d510a44eb2 100644
--- a/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp
+++ b/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickcustomaffector/CMakeLists.txt b/tests/auto/particles/qquickcustomaffector/CMakeLists.txt
index 85aabd4c3a..34dc258c8b 100644
--- a/tests/auto/particles/qquickcustomaffector/CMakeLists.txt
+++ b/tests/auto/particles/qquickcustomaffector/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickcustomaffector Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickcustomaffector LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickcustomaffector/data/affectedSignal.qml b/tests/auto/particles/qquickcustomaffector/data/affectedSignal.qml
index b5216be5b2..965479f305 100644
--- a/tests/auto/particles/qquickcustomaffector/data/affectedSignal.qml
+++ b/tests/auto/particles/qquickcustomaffector/data/affectedSignal.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickcustomaffector/data/basic.qml b/tests/auto/particles/qquickcustomaffector/data/basic.qml
index 879e7d3ecf..4748fc45d2 100644
--- a/tests/auto/particles/qquickcustomaffector/data/basic.qml
+++ b/tests/auto/particles/qquickcustomaffector/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickcustomaffector/data/move.qml b/tests/auto/particles/qquickcustomaffector/data/move.qml
index 33671d52e2..078bff7814 100644
--- a/tests/auto/particles/qquickcustomaffector/data/move.qml
+++ b/tests/auto/particles/qquickcustomaffector/data/move.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp b/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp
index e10885cf84..b4468905f1 100644
--- a/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp
+++ b/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickellipseextruder/CMakeLists.txt b/tests/auto/particles/qquickellipseextruder/CMakeLists.txt
index 2930acfd4d..634fd49a63 100644
--- a/tests/auto/particles/qquickellipseextruder/CMakeLists.txt
+++ b/tests/auto/particles/qquickellipseextruder/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickellipseextruder Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickellipseextruder LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickellipseextruder/data/basic.qml b/tests/auto/particles/qquickellipseextruder/data/basic.qml
index b3d904b382..d03d3175e3 100644
--- a/tests/auto/particles/qquickellipseextruder/data/basic.qml
+++ b/tests/auto/particles/qquickellipseextruder/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp b/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp
index 98139e8271..78875ac62b 100644
--- a/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp
+++ b/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qmath.h>
#include <QtTest/QtTest>
diff --git a/tests/auto/particles/qquickfriction/CMakeLists.txt b/tests/auto/particles/qquickfriction/CMakeLists.txt
index 7a9b1daaa8..ade4635990 100644
--- a/tests/auto/particles/qquickfriction/CMakeLists.txt
+++ b/tests/auto/particles/qquickfriction/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickfriction Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickfriction LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickfriction/data/basic.qml b/tests/auto/particles/qquickfriction/data/basic.qml
index d5911535b0..da6f779d22 100644
--- a/tests/auto/particles/qquickfriction/data/basic.qml
+++ b/tests/auto/particles/qquickfriction/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickfriction/data/threshold.qml b/tests/auto/particles/qquickfriction/data/threshold.qml
index 64b9bc7117..2d836671ad 100644
--- a/tests/auto/particles/qquickfriction/data/threshold.qml
+++ b/tests/auto/particles/qquickfriction/data/threshold.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp b/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp
index edc3770f85..579e68355b 100644
--- a/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp
+++ b/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickgravity/CMakeLists.txt b/tests/auto/particles/qquickgravity/CMakeLists.txt
index 69a5484b24..d4b97b6516 100644
--- a/tests/auto/particles/qquickgravity/CMakeLists.txt
+++ b/tests/auto/particles/qquickgravity/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickgravity Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickgravity LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickgravity/data/basic.qml b/tests/auto/particles/qquickgravity/data/basic.qml
index dd5e1c90d3..6cc950eea0 100644
--- a/tests/auto/particles/qquickgravity/data/basic.qml
+++ b/tests/auto/particles/qquickgravity/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp b/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp
index d95bb19827..8590308bc5 100644
--- a/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp
+++ b/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickgroupgoal/CMakeLists.txt b/tests/auto/particles/qquickgroupgoal/CMakeLists.txt
index 6b123882ec..84bbac6bf4 100644
--- a/tests/auto/particles/qquickgroupgoal/CMakeLists.txt
+++ b/tests/auto/particles/qquickgroupgoal/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickgroupgoal Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickgroupgoal LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickgroupgoal/data/basic.qml b/tests/auto/particles/qquickgroupgoal/data/basic.qml
index 737bf275dc..8bf5d8e995 100644
--- a/tests/auto/particles/qquickgroupgoal/data/basic.qml
+++ b/tests/auto/particles/qquickgroupgoal/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp b/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp
index 87a0c9f21e..2e18e4e9a5 100644
--- a/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp
+++ b/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickimageparticle/CMakeLists.txt b/tests/auto/particles/qquickimageparticle/CMakeLists.txt
index 49e1a74227..f104aefce7 100644
--- a/tests/auto/particles/qquickimageparticle/CMakeLists.txt
+++ b/tests/auto/particles/qquickimageparticle/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickimageparticle Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickimageparticle LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickimageparticle/data/basic.qml b/tests/auto/particles/qquickimageparticle/data/basic.qml
index 32a8483975..0b97fb3f3d 100644
--- a/tests/auto/particles/qquickimageparticle/data/basic.qml
+++ b/tests/auto/particles/qquickimageparticle/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickimageparticle/data/colorVariance.qml b/tests/auto/particles/qquickimageparticle/data/colorVariance.qml
index 572577715f..18bafdb8f3 100644
--- a/tests/auto/particles/qquickimageparticle/data/colorVariance.qml
+++ b/tests/auto/particles/qquickimageparticle/data/colorVariance.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickimageparticle/data/colored.qml b/tests/auto/particles/qquickimageparticle/data/colored.qml
index 6707a50f16..cb4baa1974 100644
--- a/tests/auto/particles/qquickimageparticle/data/colored.qml
+++ b/tests/auto/particles/qquickimageparticle/data/colored.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickimageparticle/data/deformed.qml b/tests/auto/particles/qquickimageparticle/data/deformed.qml
index 487d1366a5..d54874f1e9 100644
--- a/tests/auto/particles/qquickimageparticle/data/deformed.qml
+++ b/tests/auto/particles/qquickimageparticle/data/deformed.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickimageparticle/data/sprite.qml b/tests/auto/particles/qquickimageparticle/data/sprite.qml
index 2906dae813..0f9b10b898 100644
--- a/tests/auto/particles/qquickimageparticle/data/sprite.qml
+++ b/tests/auto/particles/qquickimageparticle/data/sprite.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickimageparticle/data/tabled.qml b/tests/auto/particles/qquickimageparticle/data/tabled.qml
index 2dcf0f5c54..ce91ca15b6 100644
--- a/tests/auto/particles/qquickimageparticle/data/tabled.qml
+++ b/tests/auto/particles/qquickimageparticle/data/tabled.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp b/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
index f4b6794cc2..b9bce20a24 100644
--- a/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
+++ b/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/QtMath>
diff --git a/tests/auto/particles/qquickitemparticle/CMakeLists.txt b/tests/auto/particles/qquickitemparticle/CMakeLists.txt
index 8e7283c51e..835a98ae08 100644
--- a/tests/auto/particles/qquickitemparticle/CMakeLists.txt
+++ b/tests/auto/particles/qquickitemparticle/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickitemparticle Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickitemparticle LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickitemparticle/data/basic.qml b/tests/auto/particles/qquickitemparticle/data/basic.qml
index 2f25de4a19..60923589e8 100644
--- a/tests/auto/particles/qquickitemparticle/data/basic.qml
+++ b/tests/auto/particles/qquickitemparticle/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickitemparticle/data/loader.qml b/tests/auto/particles/qquickitemparticle/data/loader.qml
index f6a90d6a24..8711bd4868 100644
--- a/tests/auto/particles/qquickitemparticle/data/loader.qml
+++ b/tests/auto/particles/qquickitemparticle/data/loader.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickitemparticle/data/managed.qml b/tests/auto/particles/qquickitemparticle/data/managed.qml
index 00405bd519..a1f34fe213 100644
--- a/tests/auto/particles/qquickitemparticle/data/managed.qml
+++ b/tests/auto/particles/qquickitemparticle/data/managed.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickitemparticle/data/takeGive.qml b/tests/auto/particles/qquickitemparticle/data/takeGive.qml
index 1b6b42d0cb..f1bfd8355f 100644
--- a/tests/auto/particles/qquickitemparticle/data/takeGive.qml
+++ b/tests/auto/particles/qquickitemparticle/data/takeGive.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickitemparticle/data/unmanaged.qml b/tests/auto/particles/qquickitemparticle/data/unmanaged.qml
index 63b0943574..082b6e22ca 100644
--- a/tests/auto/particles/qquickitemparticle/data/unmanaged.qml
+++ b/tests/auto/particles/qquickitemparticle/data/unmanaged.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
index 8e82f91a92..4620c26574 100644
--- a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
+++ b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquicklineextruder/CMakeLists.txt b/tests/auto/particles/qquicklineextruder/CMakeLists.txt
index 8391a07fc1..b48478e4f1 100644
--- a/tests/auto/particles/qquicklineextruder/CMakeLists.txt
+++ b/tests/auto/particles/qquicklineextruder/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicklineextruder Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicklineextruder LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquicklineextruder/data/basic.qml b/tests/auto/particles/qquicklineextruder/data/basic.qml
index 83e1ead18a..9ed70921c9 100644
--- a/tests/auto/particles/qquicklineextruder/data/basic.qml
+++ b/tests/auto/particles/qquicklineextruder/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp b/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp
index 98e300d90a..3d65886b3a 100644
--- a/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp
+++ b/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickmaskextruder/CMakeLists.txt b/tests/auto/particles/qquickmaskextruder/CMakeLists.txt
index b876532612..6b447af949 100644
--- a/tests/auto/particles/qquickmaskextruder/CMakeLists.txt
+++ b/tests/auto/particles/qquickmaskextruder/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickmaskextruder Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickmaskextruder LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickmaskextruder/data/basic.qml b/tests/auto/particles/qquickmaskextruder/data/basic.qml
index a2d6520f66..6ec6f2ac23 100644
--- a/tests/auto/particles/qquickmaskextruder/data/basic.qml
+++ b/tests/auto/particles/qquickmaskextruder/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp b/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp
index 6c4bb44a07..a5fafd384c 100644
--- a/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp
+++ b/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickparticlegroup/CMakeLists.txt b/tests/auto/particles/qquickparticlegroup/CMakeLists.txt
index 6837565c08..a7bc94fb9e 100644
--- a/tests/auto/particles/qquickparticlegroup/CMakeLists.txt
+++ b/tests/auto/particles/qquickparticlegroup/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickparticlegroup Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickparticlegroup LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickparticlegroup/data/basic.qml b/tests/auto/particles/qquickparticlegroup/data/basic.qml
index 76eb1bc546..8eda2235bb 100644
--- a/tests/auto/particles/qquickparticlegroup/data/basic.qml
+++ b/tests/auto/particles/qquickparticlegroup/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp b/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp
index 75c2e72c34..bc1183a24c 100644
--- a/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp
+++ b/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickparticlesystem/CMakeLists.txt b/tests/auto/particles/qquickparticlesystem/CMakeLists.txt
index 0a55dd1b4a..b3f826e0d9 100644
--- a/tests/auto/particles/qquickparticlesystem/CMakeLists.txt
+++ b/tests/auto/particles/qquickparticlesystem/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickparticlesystem Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickparticlesystem LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickparticlesystem/data/basic.qml b/tests/auto/particles/qquickparticlesystem/data/basic.qml
index 32a8483975..0b97fb3f3d 100644
--- a/tests/auto/particles/qquickparticlesystem/data/basic.qml
+++ b/tests/auto/particles/qquickparticlesystem/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickparticlesystem/data/crashaffectors.qml b/tests/auto/particles/qquickparticlesystem/data/crashaffectors.qml
index ae483686a5..96a4038378 100644
--- a/tests/auto/particles/qquickparticlesystem/data/crashaffectors.qml
+++ b/tests/auto/particles/qquickparticlesystem/data/crashaffectors.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 reMarkable A/S
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.6
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp b/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp
index cadeac66f5..51404e5321 100644
--- a/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp
+++ b/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickpointattractor/CMakeLists.txt b/tests/auto/particles/qquickpointattractor/CMakeLists.txt
index f8a9d83d4e..ccf2babc78 100644
--- a/tests/auto/particles/qquickpointattractor/CMakeLists.txt
+++ b/tests/auto/particles/qquickpointattractor/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpointattractor Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpointattractor LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickpointattractor/data/basic.qml b/tests/auto/particles/qquickpointattractor/data/basic.qml
index 71682a7f1d..41d1a1c8ab 100644
--- a/tests/auto/particles/qquickpointattractor/data/basic.qml
+++ b/tests/auto/particles/qquickpointattractor/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp b/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp
index e5822c1b88..2afaff634a 100644
--- a/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp
+++ b/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickpointdirection/CMakeLists.txt b/tests/auto/particles/qquickpointdirection/CMakeLists.txt
index e394058ef3..fdaa071077 100644
--- a/tests/auto/particles/qquickpointdirection/CMakeLists.txt
+++ b/tests/auto/particles/qquickpointdirection/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpointdirection Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpointdirection LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickpointdirection/data/basic.qml b/tests/auto/particles/qquickpointdirection/data/basic.qml
index 6a18ced3fe..70d38bcead 100644
--- a/tests/auto/particles/qquickpointdirection/data/basic.qml
+++ b/tests/auto/particles/qquickpointdirection/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp b/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp
index 8c39bad812..ba4949f095 100644
--- a/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp
+++ b/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickrectangleextruder/CMakeLists.txt b/tests/auto/particles/qquickrectangleextruder/CMakeLists.txt
index 70c192dd77..79edac296a 100644
--- a/tests/auto/particles/qquickrectangleextruder/CMakeLists.txt
+++ b/tests/auto/particles/qquickrectangleextruder/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickrectangleextruder Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickrectangleextruder LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickrectangleextruder/data/basic.qml b/tests/auto/particles/qquickrectangleextruder/data/basic.qml
index 5aae3e5378..a3e2eab7f7 100644
--- a/tests/auto/particles/qquickrectangleextruder/data/basic.qml
+++ b/tests/auto/particles/qquickrectangleextruder/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp b/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp
index 99cbeba35a..1486ff9e1c 100644
--- a/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp
+++ b/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickspritegoal/CMakeLists.txt b/tests/auto/particles/qquickspritegoal/CMakeLists.txt
index 9f3b7b4520..c85662b889 100644
--- a/tests/auto/particles/qquickspritegoal/CMakeLists.txt
+++ b/tests/auto/particles/qquickspritegoal/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickspritegoal Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickspritegoal LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickspritegoal/data/basic.qml b/tests/auto/particles/qquickspritegoal/data/basic.qml
index 89e9675e6a..5984007104 100644
--- a/tests/auto/particles/qquickspritegoal/data/basic.qml
+++ b/tests/auto/particles/qquickspritegoal/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp b/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp
index 4dd965aff7..327c443c9c 100644
--- a/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp
+++ b/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquicktargetdirection/CMakeLists.txt b/tests/auto/particles/qquicktargetdirection/CMakeLists.txt
index 3febb4efc9..4c2b68d01b 100644
--- a/tests/auto/particles/qquicktargetdirection/CMakeLists.txt
+++ b/tests/auto/particles/qquicktargetdirection/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktargetdirection Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktargetdirection LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquicktargetdirection/data/basic.qml b/tests/auto/particles/qquicktargetdirection/data/basic.qml
index 59e848841d..6ff5ba5e56 100644
--- a/tests/auto/particles/qquicktargetdirection/data/basic.qml
+++ b/tests/auto/particles/qquicktargetdirection/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp b/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp
index 6e695440a0..61777f4836 100644
--- a/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp
+++ b/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquicktrailemitter/CMakeLists.txt b/tests/auto/particles/qquicktrailemitter/CMakeLists.txt
index 74f2026635..05fc453274 100644
--- a/tests/auto/particles/qquicktrailemitter/CMakeLists.txt
+++ b/tests/auto/particles/qquicktrailemitter/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktrailemitter Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktrailemitter LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquicktrailemitter/data/basic.qml b/tests/auto/particles/qquicktrailemitter/data/basic.qml
index d6324a2a77..f44008f5ef 100644
--- a/tests/auto/particles/qquicktrailemitter/data/basic.qml
+++ b/tests/auto/particles/qquicktrailemitter/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp b/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp
index 230c666c8d..7233e57224 100644
--- a/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp
+++ b/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickturbulence/CMakeLists.txt b/tests/auto/particles/qquickturbulence/CMakeLists.txt
index 411c7c20dd..13a971cb71 100644
--- a/tests/auto/particles/qquickturbulence/CMakeLists.txt
+++ b/tests/auto/particles/qquickturbulence/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickturbulence Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickturbulence LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickturbulence/data/basic.qml b/tests/auto/particles/qquickturbulence/data/basic.qml
index 3eae9c807d..3b387fcc00 100644
--- a/tests/auto/particles/qquickturbulence/data/basic.qml
+++ b/tests/auto/particles/qquickturbulence/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp b/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp
index d90dd17199..99340993cf 100644
--- a/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp
+++ b/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/qquickwander/CMakeLists.txt b/tests/auto/particles/qquickwander/CMakeLists.txt
index e4d2f9488c..f1ef2737a9 100644
--- a/tests/auto/particles/qquickwander/CMakeLists.txt
+++ b/tests/auto/particles/qquickwander/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickwander Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickwander LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/particles/qquickwander/data/basic.qml b/tests/auto/particles/qquickwander/data/basic.qml
index 8d95cce1ad..8fd94d0e44 100644
--- a/tests/auto/particles/qquickwander/data/basic.qml
+++ b/tests/auto/particles/qquickwander/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/particles/qquickwander/tst_qquickwander.cpp b/tests/auto/particles/qquickwander/tst_qquickwander.cpp
index 71a8ee1f97..3099323919 100644
--- a/tests/auto/particles/qquickwander/tst_qquickwander.cpp
+++ b/tests/auto/particles/qquickwander/tst_qquickwander.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include "../shared/particlestestsshared.h"
diff --git a/tests/auto/particles/shared/particlestestsshared.h b/tests/auto/particles/shared/particlestestsshared.h
index 22c7cd70df..36b27fab64 100644
--- a/tests/auto/particles/shared/particlestestsshared.h
+++ b/tests/auto/particles/shared/particlestestsshared.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PARTICLES_TESTS_SHARED
#define PARTICLES_TESTS_SHARED
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index 77663f350d..6b81f4c616 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -17,8 +17,9 @@ add_subdirectory(qjsonbinding)
add_subdirectory(qqmlfile)
if(NOT INTEGRITY)
- # The INTEGRITY linker tends to crash on tst_qqmlfileselector
+ # The INTEGRITY linker tends to crash on tst_qqmlfileselector and tst_qml_common
add_subdirectory(qqmlfileselector)
+ add_subdirectory(common)
endif()
# Limit set of tests to run for static Qt builds.
@@ -33,7 +34,9 @@ add_subdirectory(qqmlerror)
add_subdirectory(qqmlincubator)
add_subdirectory(qqmlinfo)
add_subdirectory(qqmllistreference)
-add_subdirectory(qqmllocale)
+if(QT_FEATURE_qml_locale)
+ add_subdirectory(qqmllocale)
+endif()
add_subdirectory(qqmlmetaobject)
if(NOT ANDROID) # QTBUG-100003
add_subdirectory(qqmlmoduleplugin)
@@ -45,7 +48,9 @@ add_subdirectory(qqmlpromise)
add_subdirectory(qtqmlmodules)
add_subdirectory(qquickfolderlistmodel)
add_subdirectory(qqmlapplicationengine)
-add_subdirectory(qqmlsettings)
+if(QT_FEATURE_settings)
+ add_subdirectory(qqmlsettings)
+endif()
if(NOT INTEGRITY)
# There's no mounted filesystem on INTEGRITY therefore skipping qmldiskcache
@@ -55,7 +60,7 @@ endif()
add_subdirectory(qqmlmetatype)
if(TARGET Qt::Quick)
add_subdirectory(qmltc_manual)
- # QML Type Compiler tests cannot be run on webOS due to missing support for multiple
+ # QML type compiler tests cannot be run on webOS due to missing support for multiple
# consecutive window creation from a single QtWayland client
if(NOT WEBOS)
add_subdirectory(qmltc)
@@ -76,6 +81,7 @@ if(TARGET Qt::Widgets)
add_subdirectory(qjsengine)
add_subdirectory(qjsvalue)
add_subdirectory(qjsmanagedvalue)
+ add_subdirectory(qwidgetsinqml)
endif()
if(QT_FEATURE_process AND QT_FEATURE_qml_debug)
add_subdirectory(debugger)
@@ -140,9 +146,14 @@ if(QT_FEATURE_private_tests)
add_subdirectory(qqmltreemodeltotablemodel)
add_subdirectory(qv4assembler)
add_subdirectory(qv4mm)
+ add_subdirectory(qv4estable)
add_subdirectory(qv4identifiertable)
add_subdirectory(qv4regexp)
- if(QT_FEATURE_process AND NOT QNX)
+ add_subdirectory(qv4urlobject)
+ if(QT_FEATURE_process AND NOT QNX AND NOT
+ (ANDROID AND CLANG AND
+ CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "14.0.0" AND
+ CMAKE_CXX_COMPILER_VERSION VERSION_LESS "15.0.0"))
add_subdirectory(ecmascripttests)
endif()
add_subdirectory(bindingdependencyapi)
diff --git a/tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt b/tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt
index 03099ef616..9e6ba7fcb6 100644
--- a/tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt
+++ b/tests/auto/qml/animation/qabstractanimationjob/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qabstractanimationjob Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qabstractanimationjob LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qabstractanimationjob
SOURCES
tst_qabstractanimationjob.cpp
diff --git a/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp b/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp
index e2e5e59e95..40eb8a6910 100644
--- a/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp
+++ b/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/private/qabstractanimationjob_p.h>
@@ -53,8 +53,7 @@ void tst_QAbstractAnimationJob::construction()
void tst_QAbstractAnimationJob::destruction()
{
- TestableQAbstractAnimation *anim = new TestableQAbstractAnimation;
- delete anim;
+ std::unique_ptr<TestableQAbstractAnimation> anim = std::make_unique<TestableQAbstractAnimation>();
}
void tst_QAbstractAnimationJob::currentLoop()
diff --git a/tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt b/tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt
index 286bdeed4a..db46ef71dc 100644
--- a/tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt
+++ b/tests/auto/qml/animation/qanimationgroupjob/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qanimationgroupjob Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qanimationgroupjob LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qanimationgroupjob
SOURCES
tst_qanimationgroupjob.cpp
diff --git a/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp b/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp
index cac9581803..a3fe188b65 100644
--- a/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp
+++ b/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -236,7 +236,7 @@ void tst_QAnimationGroupJob::addChildTwice()
{
QAbstractAnimationJob *subGroup;
QAbstractAnimationJob *subGroup2;
- auto *parent = new QSequentialAnimationGroupJob();
+ auto parent = std::make_unique<QSequentialAnimationGroupJob>();
subGroup = new QAbstractAnimationJob;
parent->appendAnimation(subGroup);
@@ -266,7 +266,6 @@ void tst_QAnimationGroupJob::addChildTwice()
QCOMPARE(parent->children()->first(), subGroup2);
QCOMPARE(parent->children()->last(), subGroup);
- delete parent;
}
QTEST_MAIN(tst_QAnimationGroupJob)
diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt b/tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt
index 74e3b3fed4..0a275aaaa1 100644
--- a/tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt
+++ b/tests/auto/qml/animation/qparallelanimationgroupjob/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qparallelanimationgroupjob Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qparallelanimationgroupjob LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qparallelanimationgroupjob
SOURCES
tst_qparallelanimationgroupjob.cpp
diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
index a11ae75033..91c47586c1 100644
--- a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
+++ b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -395,8 +395,8 @@ void tst_QParallelAnimationGroupJob::deleteChildrenWithRunningGroup()
// test if children can be activated when their group is stopped
QParallelAnimationGroupJob group;
- TestAnimation *anim1 = new TestAnimation(200);
- group.appendAnimation(anim1);
+ std::unique_ptr<TestAnimation> anim1 = std::make_unique<TestAnimation>(200);
+ group.appendAnimation(anim1.get());
QCOMPARE(group.duration(), anim1->duration());
@@ -406,7 +406,7 @@ void tst_QParallelAnimationGroupJob::deleteChildrenWithRunningGroup()
QTRY_VERIFY(group.currentLoopTime() > 0);
- delete anim1;
+ anim1.reset();
QVERIFY(group.children()->isEmpty());
QCOMPARE(group.duration(), 0);
QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
@@ -793,34 +793,34 @@ void tst_QParallelAnimationGroupJob::addAndRemoveDuration()
{
QParallelAnimationGroupJob group;
QCOMPARE(group.duration(), 0);
- TestAnimation *test = new TestAnimation(250); // 0, duration = 250;
- group.appendAnimation(test);
+ std::unique_ptr<TestAnimation> test = std::make_unique<TestAnimation>(250); // 0, duration = 250;
+ group.appendAnimation(test.get());
QCOMPARE(test->group(), static_cast<QAnimationGroupJob*>(&group));
QCOMPARE(test->duration(), 250);
QCOMPARE(group.duration(), 250);
- TestAnimation *test2 = new TestAnimation(750); // 1
- group.appendAnimation(test2);
+ std::unique_ptr<TestAnimation> test2 = std::make_unique<TestAnimation>(750); // 1
+ group.appendAnimation(test2.get());
QCOMPARE(test2->group(), static_cast<QAnimationGroupJob*>(&group));
QCOMPARE(group.duration(), 750);
- TestAnimation *test3 = new TestAnimation(500); // 2
- group.appendAnimation(test3);
+ std::unique_ptr<TestAnimation> test3 = std::make_unique<TestAnimation>(500); // 2
+ group.appendAnimation(test3.get());
QCOMPARE(test3->group(), static_cast<QAnimationGroupJob*>(&group));
QCOMPARE(group.duration(), 750);
- group.removeAnimation(test2); // remove the one with duration = 750
- delete test2;
+ group.removeAnimation(test2.get()); // remove the one with duration = 750
+ test2.reset();
QCOMPARE(group.duration(), 500);
- group.removeAnimation(test3); // remove the one with duration = 500
- delete test3;
+ group.removeAnimation(test3.get()); // remove the one with duration = 500
+ test3.reset();
QCOMPARE(group.duration(), 250);
- group.removeAnimation(test); // remove the last one (with duration = 250)
+ group.removeAnimation(test.get()); // remove the last one (with duration = 250)
QCOMPARE(test->group(), static_cast<QAnimationGroupJob*>(nullptr));
QCOMPARE(group.duration(), 0);
- delete test;
+ test.reset();
}
void tst_QParallelAnimationGroupJob::pauseResume()
@@ -878,16 +878,16 @@ void tst_QParallelAnimationGroupJob::pauseResume()
void tst_QParallelAnimationGroupJob::crashWhenRemovingUncontrolledAnimation()
{
QParallelAnimationGroupJob group;
- TestAnimation *anim = new TestAnimation;
+ std::unique_ptr<TestAnimation> anim = std::make_unique<TestAnimation>();
anim->setLoopCount(-1);
- TestAnimation *anim2 = new TestAnimation;
+ std::unique_ptr<TestAnimation> anim2 = std::make_unique<TestAnimation>();
anim2->setLoopCount(-1);
- group.appendAnimation(anim);
- group.appendAnimation(anim2);
+ group.appendAnimation(anim.get());
+ group.appendAnimation(anim2.get());
group.start();
- delete anim;
+ anim.reset();
// it would crash here because the internals of the group would still have a reference to anim
- delete anim2;
+ anim2.reset();
}
void tst_QParallelAnimationGroupJob::uncontrolledWithLoops()
diff --git a/tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt b/tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt
index 5f67083ea4..2dc9e42f90 100644
--- a/tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt
+++ b/tests/auto/qml/animation/qpauseanimationjob/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qpauseanimationjob Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qpauseanimationjob LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qpauseanimationjob
SOURCES
tst_qpauseanimationjob.cpp
diff --git a/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp b/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp
index db433af8c6..c658f590c9 100644
--- a/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp
+++ b/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt b/tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt
index e9a680ace5..fa7ad06a03 100644
--- a/tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt
+++ b/tests/auto/qml/animation/qsequentialanimationgroupjob/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qsequentialanimationgroupjob Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qsequentialanimationgroupjob LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qsequentialanimationgroupjob
SOURCES
tst_qsequentialanimationgroupjob.cpp
diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
index cc67df420c..71aa2240ff 100644
--- a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
+++ b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQml/private/qsequentialanimationgroupjob_p.h>
@@ -110,7 +110,7 @@ public:
{
states << newState;
if (beEvil) {
- delete job->group();
+ delete job->group(); //manual delete intentional
groupDeleted = true;
}
}
@@ -1090,8 +1090,8 @@ void tst_QSequentialAnimationGroupJob::deleteChildrenWithRunningGroup()
// test if children can be activated when their group is stopped
QSequentialAnimationGroupJob group;
- TestAnimation *anim1 = new TestAnimation(200);
- group.appendAnimation(anim1);
+ std::unique_ptr<TestAnimation> anim1 = std::make_unique<TestAnimation>(200);
+ group.appendAnimation(anim1.get());
QCOMPARE(group.duration(), anim1->duration());
@@ -1102,7 +1102,7 @@ void tst_QSequentialAnimationGroupJob::deleteChildrenWithRunningGroup()
QTest::qWait(100);
QTRY_VERIFY(group.currentLoopTime() > 0);
- delete anim1;
+ anim1.reset();
QVERIFY(group.children()->isEmpty());
QCOMPARE(group.duration(), 0);
QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
diff --git a/tests/auto/qml/bindingdependencyapi/CMakeLists.txt b/tests/auto/qml/bindingdependencyapi/CMakeLists.txt
index e8ab157548..febc0bcfdd 100644
--- a/tests/auto/qml/bindingdependencyapi/CMakeLists.txt
+++ b/tests/auto/qml/bindingdependencyapi/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_bindingdependencyapi Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_bindingdependencyapi LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp b/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp
index 5fbf28b15b..6b1f4e1622 100644
--- a/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp
+++ b/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -104,12 +104,12 @@ void tst_bindingdependencyapi::testSingleDep()
QQmlEngine engine;
QQmlComponent c(&engine);
c.setData(code, QUrl());
- QObject *rect = c.create();
+ std::unique_ptr<QObject> rect { c.create() };
QTest::qWait(10);
- QVERIFY(rect != nullptr);
+ QVERIFY(rect.get() != nullptr);
QObject *text = rect->findChildren<QQuickText *>().front();
- QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(referencedObjectName);
+ QObject *referencedObject = rect->objectName() == referencedObjectName ? rect.get() : rect->findChild<QObject *>(referencedObjectName);
auto data = QQmlData::get(text);
QVERIFY(data);
@@ -125,8 +125,6 @@ void tst_bindingdependencyapi::testSingleDep()
QCOMPARE(dependency.property().name(), "labelText");
QCOMPARE(dependency.read().toString(), QStringLiteral("Hello world!"));
QCOMPARE(dependency, QQmlProperty(referencedObject, "labelText"));
-
- delete rect;
}
bool tst_bindingdependencyapi::findProperties(const QVector<QQmlProperty> &properties, QObject *obj, const QString &propertyName, const QVariant &value)
@@ -183,12 +181,12 @@ void tst_bindingdependencyapi::testManyDeps()
QQmlEngine engine;
QQmlComponent c(&engine);
c.setData(code, QUrl());
- QObject *rect = c.create();
+ std::unique_ptr<QObject> rect { c.create() };
if (c.isError()) {
qWarning() << c.errorString();
}
QTest::qWait(100);
- QVERIFY(rect != nullptr);
+ QVERIFY(rect.get() != nullptr);
QObject *text = rect->findChildren<QQuickText *>().front();
QObject *configObj = rect->findChild<QObject *>("config");
@@ -201,11 +199,9 @@ void tst_bindingdependencyapi::testManyDeps()
auto dependencies = binding->dependencies();
QCOMPARE(dependencies.size(), 3);
- QVERIFY(findProperties(dependencies, rect, "name", "world"));
+ QVERIFY(findProperties(dependencies, rect.get(), "name", "world"));
QVERIFY(findProperties(dependencies, text, "greeting", "Hello"));
QVERIFY(findProperties(dependencies, configObj, "helloWorldTemplate", "%1 %2!"));
-
- delete rect;
}
void tst_bindingdependencyapi::testConditionalDependencies_data()
@@ -263,12 +259,12 @@ void tst_bindingdependencyapi::testConditionalDependencies()
QQmlEngine engine;
QQmlComponent c(&engine);
c.setData(code, QUrl());
- QObject *rect = c.create();
+ std::unique_ptr<QObject> rect { c.create() };
QTest::qWait(10);
- QVERIFY(rect != nullptr);
+ QVERIFY(rect.get() != nullptr);
QObject *text = rect->findChildren<QQuickText *>().front();
- QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(referencedObjectName);
+ QObject *referencedObject = rect->objectName() == referencedObjectName ? rect.get() : rect->findChild<QObject *>(referencedObjectName);
auto data = QQmlData::get(text);
QVERIFY(data);
@@ -290,8 +286,6 @@ void tst_bindingdependencyapi::testConditionalDependencies()
dependencies = binding->dependencies();
QCOMPARE(dependencies.size(), 1);
QVERIFY(findProperties(dependencies, referencedObject, "haveDep", false));
-
- delete rect;
}
void tst_bindingdependencyapi::testBindingLoop()
@@ -306,12 +300,12 @@ void tst_bindingdependencyapi::testBindingLoop()
"text: labelText\n"
"}\n"
"}"), QUrl());
- QObject *rect = c.create();
+ std::unique_ptr<QObject> rect { c.create() };
if (c.isError()) {
qWarning() << c.errorString();
}
QTest::qWait(100);
- QVERIFY(rect != nullptr);
+ QVERIFY(rect.get() != nullptr);
QObject *text = rect->findChildren<QQuickText *>().front();
auto data = QQmlData::get(text);
@@ -324,10 +318,8 @@ void tst_bindingdependencyapi::testBindingLoop()
QCOMPARE(dependencies.size(), 1);
auto dependency = dependencies.front();
QVERIFY(dependency.isValid());
- QCOMPARE(quintptr(dependency.object()), quintptr(rect));
+ QCOMPARE(quintptr(dependency.object()), quintptr(rect.get()));
QCOMPARE(dependency.property().name(), "labelText");
-
- delete rect;
}
void tst_bindingdependencyapi::testQproperty()
diff --git a/tests/auto/qml/common/CMakeLists.txt b/tests/auto/qml/common/CMakeLists.txt
new file mode 100644
index 0000000000..4b7714a11d
--- /dev/null
+++ b/tests/auto/qml/common/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_policy(SET QTP0001 NEW)
+
+qt_internal_add_test(tst_qml_common
+ SOURCES
+ tst_qml_common.cpp tst_qml_common.h
+ LIBRARIES
+ Qt::QmlPrivate
+)
diff --git a/tests/auto/qml/common/tst_qml_common.cpp b/tests/auto/qml/common/tst_qml_common.cpp
new file mode 100644
index 0000000000..e9baae5d53
--- /dev/null
+++ b/tests/auto/qml/common/tst_qml_common.cpp
@@ -0,0 +1,168 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qml_common.h"
+
+using namespace Qt::StringLiterals;
+
+void tst_qml_common::tst_propertyNameToChangedSignalName_data()
+{
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<QString>("expected");
+
+ QTest::addRow("normalProperty") << u"helloWorld"_s << u"helloWorldChanged"_s;
+ QTest::addRow("changedProperty") << u"changed"_s << u"changedChanged"_s;
+ QTest::addRow("chängedProperty") << u"chänged"_s << u"chängedChanged"_s;
+}
+void tst_qml_common::tst_propertyNameToChangedSignalName()
+{
+ QFETCH(QString, property);
+ QFETCH(QString, expected);
+
+ QVERIFY(QQmlSignalNames::isChangedSignalName(expected));
+ QCOMPARE(QQmlSignalNames::propertyNameToChangedSignalName(property), expected);
+ QCOMPARE(QQmlSignalNames::changedSignalNameToPropertyName(expected).value(), property);
+}
+
+void tst_qml_common::tst_propertyNameToChangedHandlerName_data()
+{
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<QString>("expected");
+
+ QTest::addRow("normalProperty") << u"helloWorld"_s << u"onHelloWorldChanged"_s;
+ QTest::addRow("changedProperty") << u"changed"_s << u"onChangedChanged"_s;
+ QTest::addRow("chängedProperty") << u"chänged"_s << u"onChängedChanged"_s;
+ QTest::addRow("äProperty") << u"ä"_s << u"onÄChanged"_s;
+ QTest::addRow("_Property") << u"_"_s << u"on_Changed"_s;
+ QTest::addRow("___123aProperty") << u"___123a"_s << u"on___123AChanged"_s;
+ QTest::addRow("___123Property") << u"___123"_s << u"on___123Changed"_s;
+ QTest::addRow("AProperty") << u"A"_s << u"onAChanged"_s;
+ QTest::addRow("_Property") << u"_"_s << u"on_Changed"_s;
+ QTest::addRow("$Property") << u"$"_s << u"on$Changed"_s;
+}
+void tst_qml_common::tst_propertyNameToChangedHandlerName()
+{
+ QFETCH(QString, property);
+ QFETCH(QString, expected);
+
+ QVERIFY(QQmlSignalNames::isChangedHandlerName(expected));
+ QCOMPARE(QQmlSignalNames::propertyNameToChangedHandlerName(property), expected);
+ auto reverse = QQmlSignalNames::changedHandlerNameToPropertyName(expected);
+ QVERIFY(reverse);
+ QEXPECT_FAIL("AProperty",
+ "Cannot distinguish between property names starting with upper case"
+ " from properties starting with a lower case letter.",
+ Continue);
+ QCOMPARE(reverse.value(), property);
+}
+
+void tst_qml_common::tst_signalNameToHandlerName_data()
+{
+ QTest::addColumn<QString>("signalName");
+ QTest::addColumn<QString>("expected");
+
+ QTest::addRow("normalProperty") << u"helloWorld"_s << u"onHelloWorld"_s;
+ QTest::addRow("changedProperty") << u"changed"_s << u"onChanged"_s;
+ QTest::addRow("chängedProperty") << u"chänged"_s << u"onChänged"_s;
+ QTest::addRow("äProperty") << u"ä"_s << u"onÄ"_s;
+ QTest::addRow("_Property") << u"_"_s << u"on_"_s;
+ QTest::addRow("___123aProperty") << u"___123a"_s << u"on___123A"_s;
+ QTest::addRow("___123Property") << u"___123"_s << u"on___123"_s;
+ QTest::addRow("AProperty") << u"A"_s << u"onA"_s;
+ QTest::addRow("_Property") << u"_"_s << u"on_"_s;
+ QTest::addRow("$Property") << u"$"_s << u"on$"_s;
+}
+
+void tst_qml_common::tst_signalNameToHandlerName()
+{
+ QFETCH(QString, signalName);
+ QFETCH(QString, expected);
+
+ QVERIFY(QQmlSignalNames::isHandlerName(expected));
+ QCOMPARE(QQmlSignalNames::signalNameToHandlerName(signalName), expected);
+
+ auto result = QQmlSignalNames::handlerNameToSignalName(expected);
+ QVERIFY(result.has_value());
+
+ QEXPECT_FAIL("AProperty",
+ "Cannot distinguish between signal names starting with upper case"
+ " from signal names starting with a lower case letter.",
+ Continue);
+
+ QCOMPARE(result.value(), signalName);
+}
+
+void tst_qml_common::tst_changedSignalNameToPropertyName_data()
+{
+ // only test when it should return nothing, see also tst_propertyNameToChangedSignalName.
+ QTest::addColumn<QString>("changedSignalName");
+
+ QTest::addRow("normalProperty") << u"helloWorld"_s;
+ QTest::addRow("Changed") << u"Changed"_s;
+ QTest::addRow("empty") << u""_s;
+}
+
+void tst_qml_common::tst_changedSignalNameToPropertyName()
+{
+ QFETCH(QString, changedSignalName);
+
+ QVERIFY(!QQmlSignalNames::changedSignalNameToPropertyName(changedSignalName).has_value());
+}
+
+void tst_qml_common::tst_changedHandlerNameToPropertyName_data()
+{
+ // only test when it should return nothing, see also tst_propertyNameToChangedHandler.
+ QTest::addColumn<QString>("changedHandler");
+
+ QTest::addRow("normalProperty") << u"helloWorld"_s;
+ QTest::addRow("Changed") << u"Changed"_s;
+ QTest::addRow("empty") << u""_s;
+ QTest::addRow("empty2") << u"onChanged"_s;
+ QTest::addRow("on") << u"on"_s;
+}
+void tst_qml_common::tst_changedHandlerNameToPropertyName()
+{
+ QFETCH(QString, changedHandler);
+
+ QVERIFY(!QQmlSignalNames::changedHandlerNameToPropertyName(changedHandler).has_value());
+}
+
+void tst_qml_common::tst_handlerNameToSignalName_data()
+{
+ // only test when it should return nothing, see also tst_signalNameToHandlerName.
+ QTest::addColumn<QString>("handler");
+
+ QTest::addRow("normalProperty") << u"helloWorld"_s;
+ QTest::addRow("Changed") << u"Changed"_s;
+ QTest::addRow("empty") << u""_s;
+ QTest::addRow("on") << u"on"_s;
+}
+void tst_qml_common::tst_handlerNameToSignalName()
+{
+ QFETCH(QString, handler);
+
+ QVERIFY(!QQmlSignalNames::handlerNameToSignalName(handler).has_value());
+}
+
+void tst_qml_common::tst_isChangedHandlerName_data()
+{
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<bool>("expected");
+
+ QTest::addRow("normalProperty") << u"helloWorld"_s << false;
+ QTest::addRow("Changed") << u"Changed"_s << false;
+ QTest::addRow("empty") << u""_s << false;
+ QTest::addRow("empty2") << u"onChanged"_s << false;
+ QTest::addRow("on") << u"on"_s << false;
+ QTest::addRow("on_Changed") << u"on_Changed"_s << true;
+ QTest::addRow("on$Changed") << u"on$Changed"_s << true;
+}
+void tst_qml_common::tst_isChangedHandlerName()
+{
+ QFETCH(QString, name);
+ QFETCH(bool, expected);
+
+ QCOMPARE(QQmlSignalNames::isChangedHandlerName(name), expected);
+}
+
+QTEST_MAIN(tst_qml_common)
diff --git a/tests/auto/qml/common/tst_qml_common.h b/tests/auto/qml/common/tst_qml_common.h
new file mode 100644
index 0000000000..98e462ded6
--- /dev/null
+++ b/tests/auto/qml/common/tst_qml_common.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QML_COMMON_H
+#define TST_QML_COMMON_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtTest/qtest.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+
+class tst_qml_common : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void tst_propertyNameToChangedSignalName_data();
+ void tst_propertyNameToChangedSignalName();
+ void tst_propertyNameToChangedHandlerName();
+ void tst_propertyNameToChangedHandlerName_data();
+ void tst_signalNameToHandlerName();
+ void tst_signalNameToHandlerName_data();
+
+ void tst_changedSignalNameToPropertyName();
+ void tst_changedSignalNameToPropertyName_data();
+ void tst_changedHandlerNameToPropertyName();
+ void tst_changedHandlerNameToPropertyName_data();
+ void tst_handlerNameToSignalName();
+ void tst_handlerNameToSignalName_data();
+
+ void tst_isChangedHandlerName();
+ void tst_isChangedHandlerName_data();
+};
+
+#endif // TST_QML_COMMON_H
diff --git a/tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt b/tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt
index d3df19bc40..07b6e26544 100644
--- a/tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qdebugmessageservice/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qdebugmessageservice Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qdebugmessageservice LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml b/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml
index b8134b37e6..bdb91d08e5 100644
--- a/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml
+++ b/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp
index 11c4203429..cd39b51b25 100644
--- a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp
+++ b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//QQmlDebugTest
#include "../shared/debugutil_p.h"
diff --git a/tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt b/tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt
index 146999b9bd..91941de381 100644
--- a/tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qpacketprotocol/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qpacketprotocol Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qpacketprotocol LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qpacketprotocol
SOURCES
../shared/debugutil.cpp ../shared/debugutil_p.h
diff --git a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp
index 81d8694406..2ce062b0cc 100644
--- a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp
+++ b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QSignalSpy>
#include <QTimer>
@@ -18,9 +18,9 @@ class tst_QPacketProtocol : public QObject
Q_OBJECT
private:
- QTcpServer *m_server;
- QTcpSocket *m_client;
- QTcpSocket *m_serverConn;
+ std::unique_ptr<QTcpServer> m_server;
+ std::unique_ptr<QTcpSocket> m_client;
+ std::unique_ptr<QTcpSocket> m_serverConn;
private slots:
void init();
@@ -34,34 +34,34 @@ private slots:
void tst_QPacketProtocol::init()
{
- m_server = new QTcpServer(this);
+ m_server = std::make_unique<QTcpServer>(this);
m_serverConn = nullptr;
QVERIFY(m_server->listen(QHostAddress("127.0.0.1")));
- m_client = new QTcpSocket(this);
+ m_client = std::make_unique<QTcpSocket>(this);
- QSignalSpy serverSpy(m_server, SIGNAL(newConnection()));
- QSignalSpy clientSpy(m_client, SIGNAL(connected()));
+ QSignalSpy serverSpy(m_server.get(), SIGNAL(newConnection()));
+ QSignalSpy clientSpy(m_client.get(), SIGNAL(connected()));
m_client->connectToHost(m_server->serverAddress(), m_server->serverPort());
QVERIFY(clientSpy.size() > 0 || clientSpy.wait());
QVERIFY(serverSpy.size() > 0 || serverSpy.wait());
- m_serverConn = m_server->nextPendingConnection();
+ m_serverConn.reset(m_server->nextPendingConnection());
}
void tst_QPacketProtocol::cleanup()
{
- delete m_client;
- delete m_serverConn;
- delete m_server;
+ m_client.reset();
+ m_serverConn.reset();
+ m_server.reset();
}
void tst_QPacketProtocol::send()
{
- QPacketProtocol in(m_client);
- QPacketProtocol out(m_serverConn);
+ QPacketProtocol in(m_client.get());
+ QPacketProtocol out(m_serverConn.get());
QByteArray ba;
int num;
@@ -82,8 +82,8 @@ void tst_QPacketProtocol::packetsAvailable()
{
QFETCH(int, packetCount);
- QPacketProtocol out(m_client);
- QPacketProtocol in(m_serverConn);
+ QPacketProtocol out(m_client.get());
+ QPacketProtocol in(m_serverConn.get());
QCOMPARE(out.packetsAvailable(), qint64(0));
QCOMPARE(in.packetsAvailable(), qint64(0));
@@ -109,8 +109,8 @@ void tst_QPacketProtocol::packetsAvailable_data()
void tst_QPacketProtocol::read()
{
- QPacketProtocol in(m_client);
- QPacketProtocol out(m_serverConn);
+ QPacketProtocol in(m_client.get());
+ QPacketProtocol out(m_serverConn.get());
QVERIFY(in.read().isEmpty());
diff --git a/tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt
index 6b33ee0024..d0a63440da 100644
--- a/tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmldebugclient/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmldebugclient Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldebugclient LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmldebugclient
SOURCES
../shared/debugutil.cpp ../shared/debugutil_p.h
diff --git a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp
index 509791b96d..d0e9c22f2c 100644
--- a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp
+++ b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "debugutil_p.h"
#include "qqmldebugtestservice.h"
@@ -44,11 +44,16 @@ void tst_QQmlDebugClient::initTestCase()
m_service = new QQmlDebugTestService("tst_QQmlDebugClient::handshake()");
- foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices())
+ const QStringList debuggerServices = QQmlDebuggingEnabler::debuggerServices();
+ for (const QString &service : debuggerServices)
QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr);
- foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices())
+
+ const QStringList inspectorServices = QQmlDebuggingEnabler::inspectorServices();
+ for (const QString &service : inspectorServices)
QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr);
- foreach (const QString &service, QQmlDebuggingEnabler::profilerServices())
+
+ const QStringList profilerServices = QQmlDebuggingEnabler::profilerServices();
+ for (const QString &service : profilerServices)
QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr);
const QString waitingMsg = QString("QML Debugger: Waiting for connection on port %1...").arg(PORT);
diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt
index b73c2aa218..53b1e9c683 100644
--- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmldebuggingenabler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldebuggingenabler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmldebuggingenabler
SOURCES
../../shared/debugutil.cpp ../../shared/debugutil_p.h
diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml
index 9bd5130e71..2474016705 100644
--- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml
+++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/data/test.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp
index ab599e9a23..03a65f56ca 100644
--- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp
+++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "debugutil_p.h"
#include "qqmldebugprocess_p.h"
@@ -41,14 +41,14 @@ void tst_QQmlDebuggingEnabler::data()
QTest::addColumn<bool>("blockMode");
QTest::addColumn<QStringList>("services");
- QStringList connectors({
+ const QStringList connectors({
QLatin1String("QQmlDebugServer"),
QLatin1String("QQmlNativeDebugConnector")
});
- QList<bool> blockModes({ true, false });
+ const QList<bool> blockModes({ true, false });
- QList<QStringList> serviceLists({
+ const QList<QStringList> serviceLists({
QStringList(),
QQmlDebuggingEnabler::nativeDebuggerServices(),
QQmlDebuggingEnabler::debuggerServices(),
@@ -57,9 +57,9 @@ void tst_QQmlDebuggingEnabler::data()
QQmlDebuggingEnabler::debuggerServices() + QQmlDebuggingEnabler::inspectorServices()
});
- foreach (const QString &connector, connectors) {
- foreach (bool blockMode, blockModes) {
- foreach (const QStringList &serviceList, serviceLists) {
+ for (const QString &connector : connectors) {
+ for (bool blockMode : blockModes) {
+ for (const QStringList &serviceList : serviceLists) {
QString name = connector + QLatin1Char(',')
+ QLatin1String(blockMode ? "block" : "noblock") + QLatin1Char(',')
+ serviceList.join(QLatin1Char('-'));
@@ -103,7 +103,7 @@ void tst_QQmlDebuggingEnabler::qmlscene()
m_clients = QQmlDebugTest::createOtherClients(m_connection);
m_connection->connectToHost("127.0.0.1", m_process->debugPort());
QVERIFY(m_connection->waitForConnected());
- foreach (QQmlDebugClient *client, m_clients)
+ for (QQmlDebugClient *client : std::as_const(m_clients))
QCOMPARE(client->state(), (services.isEmpty() || services.contains(client->name())) ?
QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable);
}
diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp
index 33bb0d6d37..6baaf8f430 100644
--- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp
+++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenablerserver/qqmldebuggingenablerserver.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qcoreapplication.h>
#include <QtCore/qlibraryinfo.h>
diff --git a/tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt
index e48f8e965d..3d65442ebf 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmldebugjs/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmldebugjs Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldebugjs LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml
index 03bc85f5a4..6650096b55 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.11
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml
index 3a846c0eeb..254924743e 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml
index 2d747c28a5..3ebe44ea76 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml
index a4952f79cc..0f02f62750 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml
index e67713ba67..f58b474876 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml
index dc3be646ce..807735573e 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml
index 0af1188015..bcd8f4e9d5 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import "test.js" as Script
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml
index 6cf2d7c030..4cc13a7952 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml b/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml
index 026911d1cc..788cd86cce 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml b/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml
index 4f21b53ff4..ab8566b77c 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import "quit.js" as Quit;
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml
index f442a9c491..1de3078f19 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/test.js b/tests/auto/qml/debugger/qqmldebugjs/data/test.js
index 7a28dafdd2..f631b1657b 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/test.js
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
function printMessage(msg)
{
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/test.qml b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml
index 687955dffc..bc84f7b3af 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/test.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml
index 923acb72ad..a823a9a407 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
index ac607df0a0..35a94aae78 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
+++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "debugutil_p.h"
#include "qqmldebugprocess_p.h"
@@ -140,8 +140,8 @@ private slots:
void letConstLocals();
private:
- ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE),
- bool blockMode = true, bool restrictServices = false);
+ ConnectResult runAndConnect(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE),
+ bool blockMode = true, bool restrictServices = false);
QList<QQmlDebugClient *> createClients() override;
QPointer<QV4DebugClient> m_client;
@@ -163,7 +163,7 @@ void tst_QQmlDebugJS::initTestCase()
QQmlDebugTest::initTestCase();
}
#include <iostream>
-QQmlDebugTest::ConnectResult tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile,
+QQmlDebugTest::ConnectResult tst_QQmlDebugJS::runAndConnect(bool qmlscene, const QString &qmlFile,
bool blockMode, bool restrictServices)
{
const QString executable = qmlscene
@@ -195,7 +195,7 @@ void tst_QQmlDebugJS::connect()
QFETCH(bool, restrictMode);
QFETCH(bool, qmlscene);
- QCOMPARE(init(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode), ConnectSuccess);
m_client->connect();
QVERIFY(waitForClientSignal(SIGNAL(connected())));
}
@@ -205,7 +205,7 @@ void tst_QQmlDebugJS::interrupt()
//void connect()
QFETCH(bool, qmlscene);
- QCOMPARE(init(qmlscene), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene), ConnectSuccess);
m_client->connect();
m_client->interrupt();
@@ -217,7 +217,7 @@ void tst_QQmlDebugJS::getVersion()
//void version()
QFETCH(bool, qmlscene);
- QCOMPARE(init(qmlscene), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene), ConnectSuccess);
m_client->connect();
QVERIFY(waitForClientSignal(SIGNAL(connected())));
@@ -231,7 +231,7 @@ void tst_QQmlDebugJS::getVersionWhenAttaching()
//void version()
QFETCH(bool, qmlscene);
- QCOMPARE(init(qmlscene, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess);
m_client->connect();
m_client->version();
@@ -244,7 +244,7 @@ void tst_QQmlDebugJS::disconnect()
//void disconnect()
QFETCH(bool, qmlscene);
- QCOMPARE(init(qmlscene), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene), ConnectSuccess);
m_client->connect();
m_client->disconnect();
@@ -257,7 +257,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted()
QFETCH(bool, qmlscene);
int sourceLine = 9;
- QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -276,7 +276,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated()
QFETCH(bool, qmlscene);
int sourceLine = 9;
- QCOMPARE(init(qmlscene, CREATECOMPONENT_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, CREATECOMPONENT_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -294,7 +294,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback()
QFETCH(bool, qmlscene);
int sourceLine = 10;
- QCOMPARE(init(qmlscene, TIMER_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, TIMER_QMLFILE), ConnectSuccess);
m_client->connect();
//We can set the breakpoint after connect() here because the timer is repeating and if we miss
@@ -315,7 +315,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile()
QFETCH(bool, qmlscene);
int sourceLine = 6;
- QCOMPARE(init(qmlscene, LOADJSFILE_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, LOADJSFILE_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true);
m_client->connect();
@@ -335,7 +335,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComment()
int sourceLine = 9;
int actualLine = 11;
- QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -356,7 +356,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine()
int sourceLine = 10;
int actualLine = 11;
- QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -376,7 +376,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding()
QFETCH(bool, qmlscene);
int sourceLine = 14;
- QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -395,7 +395,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptWithCondition()
int out = 10;
int sourceLine = 12;
- QCOMPARE(init(qmlscene, CONDITION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, CONDITION_QMLFILE), ConnectSuccess);
m_client->connect();
//The breakpoint is in a timer loop so we can set it after connect().
@@ -425,7 +425,7 @@ void tst_QQmlDebugJS::setBreakpointInScriptThatQuits()
{
QFETCH(bool, qmlscene);
- QCOMPARE(init(qmlscene, QUIT_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, QUIT_QMLFILE), ConnectSuccess);
int sourceLine = 11;
@@ -465,7 +465,7 @@ void tst_QQmlDebugJS::setBreakpointInJavaScript()
QTRY_COMPARE(process.state(), QProcess::NotRunning);
}
- QCOMPARE(init(qmlscene, QUITINJS_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, QUITINJS_QMLFILE), ConnectSuccess);
const int sourceLine = 2;
@@ -488,7 +488,7 @@ void tst_QQmlDebugJS::setBreakpointInJavaScript()
void tst_QQmlDebugJS::setBreakpointWhenAttaching()
{
int sourceLine = 10;
- QCOMPARE(init(true, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess);
+ QCOMPARE(runAndConnect(true, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess);
m_client->connect();
@@ -509,7 +509,7 @@ void tst_QQmlDebugJS::clearBreakpoint()
int sourceLine1 = 12;
int sourceLine2 = 13;
- QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess);
m_client->connect();
//The breakpoints are in a timer loop so we can set them after connect().
@@ -556,7 +556,7 @@ void tst_QQmlDebugJS::changeBreakpoint()
int sourceLine2 = 12;
int sourceLine1 = 13;
const QString file = QLatin1String(CHANGEBREAKPOINT_QMLFILE);
- QCOMPARE(init(qmlscene, file), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, file), ConnectSuccess);
bool isStopped = false;
QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { isStopped = true; });
@@ -629,7 +629,7 @@ void tst_QQmlDebugJS::setExceptionBreak()
//void setExceptionBreak(QString type, bool enabled = false);
QFETCH(bool, qmlscene);
- QCOMPARE(init(qmlscene, EXCEPTION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, EXCEPTION_QMLFILE), ConnectSuccess);
m_client->setExceptionBreak(QV4DebugClient::All,true);
m_client->connect();
QVERIFY(waitForClientSignal(SIGNAL(stopped())));
@@ -641,7 +641,7 @@ void tst_QQmlDebugJS::stepNext()
QFETCH(bool, qmlscene);
int sourceLine = 12;
- QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -669,7 +669,7 @@ void tst_QQmlDebugJS::stepIn()
int sourceLine = 16;
int actualLine = 11;
- QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true);
m_client->connect();
@@ -691,7 +691,7 @@ void tst_QQmlDebugJS::stepOut()
int sourceLine = 12;
int actualLine = 16;
- QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -713,7 +713,7 @@ void tst_QQmlDebugJS::continueDebugging()
int sourceLine1 = 16;
int sourceLine2 = 13;
- QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true);
m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true);
@@ -736,7 +736,7 @@ void tst_QQmlDebugJS::backtrace()
QFETCH(bool, qmlscene);
int sourceLine = 9;
- QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -752,7 +752,7 @@ void tst_QQmlDebugJS::getFrameDetails()
QFETCH(bool, qmlscene);
int sourceLine = 9;
- QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -768,7 +768,7 @@ void tst_QQmlDebugJS::getScopeDetails()
QFETCH(bool, qmlscene);
int sourceLine = 9;
- QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -781,7 +781,7 @@ void tst_QQmlDebugJS::getScopeDetails()
void tst_QQmlDebugJS::evaluateInGlobalScope()
{
//void evaluate(QString expr, int frame = -1);
- QCOMPARE(init(true), ConnectSuccess);
+ QCOMPARE(runAndConnect(true), ConnectSuccess);
m_client->connect();
@@ -802,7 +802,7 @@ void tst_QQmlDebugJS::evaluateInLocalScope()
QFETCH(bool, qmlscene);
int sourceLine = 9;
- QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true);
m_client->connect();
@@ -889,7 +889,7 @@ void tst_QQmlDebugJS::getScripts()
//void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant());
QFETCH(bool, qmlscene);
- QCOMPARE(init(qmlscene), ConnectSuccess);
+ QCOMPARE(runAndConnect(qmlscene), ConnectSuccess);
m_client->setBreakpoint(QString(TEST_QMLFILE), 10, -1, true);
m_client->connect();
@@ -908,7 +908,7 @@ void tst_QQmlDebugJS::getScripts()
void tst_QQmlDebugJS::encodeQmlScope()
{
QString file(ENCODEQMLSCOPE_QMLFILE);
- QCOMPARE(init(true, file), ConnectSuccess);
+ QCOMPARE(runAndConnect(true, file), ConnectSuccess);
int numFrames = 0;
int numExpectedScopes = 0;
@@ -968,7 +968,7 @@ void tst_QQmlDebugJS::encodeQmlScope()
void tst_QQmlDebugJS::breakOnAnchor()
{
QString file(BREAKONANCHOR_QMLFILE);
- QCOMPARE(init(true, file), ConnectSuccess);
+ QCOMPARE(runAndConnect(true, file), ConnectSuccess);
int breaks = 0;
bool stopped = false;
@@ -1005,7 +1005,7 @@ void tst_QQmlDebugJS::breakOnAnchor()
void tst_QQmlDebugJS::breakPointIds()
{
QString file(BREAKPOINTIDS_QMLFILE);
- QCOMPARE(init(true, file), ConnectSuccess);
+ QCOMPARE(runAndConnect(true, file), ConnectSuccess);
int breaks = 0;
int breakPointIds[] = { -1, -1, -1, -1, -1, -1};
@@ -1038,7 +1038,7 @@ void tst_QQmlDebugJS::breakPointIds()
void tst_QQmlDebugJS::letConstLocals()
{
QString file(LETCONSTLOCALS_QMLFILE);
- QCOMPARE(init(true, file), ConnectSuccess);
+ QCOMPARE(runAndConnect(true, file), ConnectSuccess);
QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() {
m_client->frame();
diff --git a/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp
index b24e4c4445..4b3d7dc6fa 100644
--- a/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp
+++ b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui/qguiapplication.h>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt
index 471336139d..03dcb24b3f 100644
--- a/tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmldebuglocal/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmldebuglocal Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldebuglocal LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmldebuglocal
SOURCES
../shared/debugutil.cpp ../shared/debugutil_p.h
diff --git a/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp b/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp
index 7449062332..bca70b7273 100644
--- a/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp
+++ b/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qqmldebugtestservice.h"
#include "debugutil_p.h"
diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt
index ea07333ec7..54fe36b7d5 100644
--- a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmldebugprocess Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldebugprocess LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmldebugprocess
SOURCES
../../shared/qqmldebugprocess.cpp ../../shared/qqmldebugprocess_p.h
diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp
index 7b1faa1131..2a403fa361 100644
--- a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp
+++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qqmldebugprocess_p.h>
#include <QtTest>
diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp
index 595fe70e58..f275c2e836 100644
--- a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp
+++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qdebug.h>
#include <QtCore/qcoreapplication.h>
diff --git a/tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt
index 33b7ba1c2f..2169cb8e0e 100644
--- a/tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmldebugservice/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmldebugservice Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldebugservice LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmldebugservice/data/test.qml b/tests/auto/qml/debugger/qqmldebugservice/data/test.qml
index 687955dffc..bc84f7b3af 100644
--- a/tests/auto/qml/debugger/qqmldebugservice/data/test.qml
+++ b/tests/auto/qml/debugger/qqmldebugservice/data/test.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp
index 7dbff4279e..462caf930f 100644
--- a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp
+++ b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp
@@ -1,6 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qqmldebugtestservice.h"
#include "debugutil_p.h"
@@ -30,7 +29,7 @@ public:
tst_QQmlDebugService();
private:
- QQmlDebugConnection *m_conn;
+ std::unique_ptr<QQmlDebugConnection> m_conn;
QQmlDebugTestService *m_service;
private slots:
@@ -60,20 +59,23 @@ void tst_QQmlDebugService::initTestCase()
<< QStringLiteral("tst_QQmlDebugService"));
m_service = new QQmlDebugTestService("tst_QQmlDebugService", 2);
- foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices())
+ const QStringList debuggerServices = QQmlDebuggingEnabler::debuggerServices();
+ for (const QString &service : debuggerServices)
QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr);
- foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices())
+
+ const QStringList inspectorServices = QQmlDebuggingEnabler::inspectorServices();
+ for (const QString &service : inspectorServices)
QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr);
- foreach (const QString &service, QQmlDebuggingEnabler::profilerServices())
+
+ const QStringList profilerServices = QQmlDebuggingEnabler::profilerServices();
+ for (const QString &service : profilerServices)
QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr);
const QString waitingMsg = QString("QML Debugger: Waiting for connection on port %1...").arg(PORT);
QTest::ignoreMessage(QtDebugMsg, waitingMsg.toLatin1().constData());
QQmlDebuggingEnabler::startTcpDebugServer(PORT);
- new QQmlEngine(this);
-
- m_conn = new QQmlDebugConnection(this);
+ m_conn = std::make_unique<QQmlDebugConnection>(this);
for (int i = 0; i < 50; ++i) {
// try for 5 seconds ...
@@ -136,7 +138,7 @@ void tst_QQmlDebugService::state()
QCOMPARE(m_service->state(), QQmlDebugService::Unavailable);
{
- QQmlDebugTestClient client("tst_QQmlDebugService", m_conn);
+ QQmlDebugTestClient client("tst_QQmlDebugService", m_conn.get());
QTRY_COMPARE(client.state(), QQmlDebugClient::Enabled);
QTRY_COMPARE(m_service->state(), QQmlDebugService::Enabled);
}
@@ -154,7 +156,7 @@ void tst_QQmlDebugService::state()
void tst_QQmlDebugService::sendMessage()
{
- QQmlDebugTestClient client("tst_QQmlDebugService", m_conn);
+ QQmlDebugTestClient client("tst_QQmlDebugService", m_conn.get());
QByteArray msg = "hello!";
@@ -175,7 +177,7 @@ void tst_QQmlDebugService::sendMessage()
void tst_QQmlDebugService::checkSupportForDataStreamVersion()
{
- QQmlDebugTestClient client("tst_QQmlDebugService", m_conn);
+ QQmlDebugTestClient client("tst_QQmlDebugService", m_conn.get());
QByteArray msg = "hello!";
@@ -192,22 +194,19 @@ void tst_QQmlDebugService::idForObject()
{
QCOMPARE(QQmlDebugService::idForObject(nullptr), -1);
- QObject *objA = new QObject;
+ std::unique_ptr<QObject> objA = std::make_unique<QObject>();
- int idA = QQmlDebugService::idForObject(objA);
+ int idA = QQmlDebugService::idForObject(objA.get());
QVERIFY(idA >= 0);
- QCOMPARE(QQmlDebugService::objectForId(idA), objA);
+ QCOMPARE(QQmlDebugService::objectForId(idA), objA.get());
- int idAA = QQmlDebugService::idForObject(objA);
+ int idAA = QQmlDebugService::idForObject(objA.get());
QCOMPARE(idAA, idA);
- QObject *objB = new QObject;
- int idB = QQmlDebugService::idForObject(objB);
+ std::unique_ptr<QObject> objB = std::make_unique<QObject>();
+ int idB = QQmlDebugService::idForObject(objB.get());
QVERIFY(idB != idA);
- QCOMPARE(QQmlDebugService::objectForId(idB), objB);
-
- delete objA;
- delete objB;
+ QCOMPARE(QQmlDebugService::objectForId(idB), objB.get());
}
void tst_QQmlDebugService::objectForId()
@@ -215,19 +214,18 @@ void tst_QQmlDebugService::objectForId()
QCOMPARE(QQmlDebugService::objectForId(-1), static_cast<QObject*>(nullptr));
QCOMPARE(QQmlDebugService::objectForId(1), static_cast<QObject*>(nullptr));
- QObject *obj = new QObject;
- int id = QQmlDebugService::idForObject(obj);
- QCOMPARE(QQmlDebugService::objectForId(id), obj);
+ std::unique_ptr<QObject> obj = std::make_unique<QObject>();
+ int id = QQmlDebugService::idForObject(obj.get());
+ QCOMPARE(QQmlDebugService::objectForId(id), obj.get());
- delete obj;
+ obj.reset();
QCOMPARE(QQmlDebugService::objectForId(id), static_cast<QObject*>(nullptr));
}
void tst_QQmlDebugService::checkSupportForOldDataStreamVersion()
{
//create a new connection;
- delete m_conn;
- m_conn = new QQmlDebugConnection(this);
+ m_conn = std::make_unique<QQmlDebugConnection>(this);
m_conn->setMaximumDataStreamVersion(QDataStream::Qt_5_0);
for (int i = 0; i < 50; ++i) {
// try for 5 seconds ...
@@ -238,7 +236,7 @@ void tst_QQmlDebugService::checkSupportForOldDataStreamVersion()
}
QVERIFY(m_conn->isConnected());
- QQmlDebugTestClient client("tst_QQmlDebugService", m_conn);
+ QQmlDebugTestClient client("tst_QQmlDebugService", m_conn.get());
QByteArray msg = "hello!";
diff --git a/tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt
index fd44b5d32f..56c2e8d88a 100644
--- a/tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmldebugtranslationclient/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qqmldebugtranslationclient Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldebugtranslationclient LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml b/tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml
index 5b4784bbff..e90f9a5a80 100644
--- a/tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml
+++ b/tests/auto/qml/debugger/qqmldebugtranslationclient/data/test.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp b/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp
index 4ad9699cb9..068ab69d4c 100644
--- a/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp
+++ b/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//QQmlDebugTest
#include <debugutil_p.h>
@@ -105,9 +105,7 @@ private:
if (newCurrentOutputLine > m_currentOutputLine) {
// lets wait a little bit more to not cut anything
int triggeredCount = 0;
- int debugCounter = 0;
do {
- debugCounter++;
triggeredCount = m_process->output().size();
QTest::qWait(updateTimeOut);
newCurrentOutputLine = m_process->output().size();
diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt b/tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt
index 7f971df399..5081f51557 100644
--- a/tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qqmldebugtranslationservice Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldebugtranslationservice LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml b/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml
index 1212fa100a..5d3957bffa 100644
--- a/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml
+++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp b/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp
index 0e0340b672..88d7bd39fd 100644
--- a/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp
+++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//QQmlDebugTest
#include <debugutil_p.h>
diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt b/tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt
index 39435fe894..12c5b4ad1f 100644
--- a/tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmlenginecontrol/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlenginecontrol Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlenginecontrol LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp
index 638389eb3c..bd01c69163 100644
--- a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp
+++ b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "debugutil_p.h"
#include <QtQuickTestUtils/private/qmlutils_p.h>
diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt
index 8b4fc93594..bb36f678c3 100644
--- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlenginedebuginspectorintegration Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlenginedebuginspectorintegration LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp
index 890f4eeef6..7bbc8830b1 100644
--- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp
+++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/debugutil_p.h"
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -24,7 +24,7 @@ public:
tst_QQmlEngineDebugInspectorIntegration();
private:
- ConnectResult init(bool restrictServices);
+ ConnectResult runAndConnect(bool restrictServices);
QList<QQmlDebugClient *> createClients() override;
QQmlEngineDebugObjectReference findRootObject();
@@ -68,7 +68,7 @@ tst_QQmlEngineDebugInspectorIntegration::tst_QQmlEngineDebugInspectorIntegration
{
}
-QQmlDebugTest::ConnectResult tst_QQmlEngineDebugInspectorIntegration::init(bool restrictServices)
+QQmlDebugTest::ConnectResult tst_QQmlEngineDebugInspectorIntegration::runAndConnect(bool restrictServices)
{
return QQmlDebugTest::connectTo(
QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qml",
@@ -96,15 +96,15 @@ void tst_QQmlEngineDebugInspectorIntegration::connect_data()
void tst_QQmlEngineDebugInspectorIntegration::connect()
{
QFETCH(bool, restrictMode);
- QCOMPARE(init(restrictMode), ConnectSuccess);
+ QCOMPARE(runAndConnect(restrictMode), ConnectSuccess);
}
void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup()
{
- QCOMPARE(init(true), ConnectSuccess);
+ QCOMPARE(runAndConnect(true), ConnectSuccess);
bool success = false;
- QQmlEngineDebugObjectReference rootObject = findRootObject();
+ const QQmlEngineDebugObjectReference rootObject = findRootObject();
QVERIFY(rootObject.debugId != -1);
const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName();
int lineNumber = rootObject.source.lineNumber;
@@ -114,7 +114,7 @@ void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup()
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result())));
- foreach (QQmlEngineDebugObjectReference child, rootObject.children) {
+ for (const QQmlEngineDebugObjectReference &child : rootObject.children) {
success = false;
lineNumber = child.source.lineNumber;
columnNumber = child.source.columnNumber;
@@ -127,12 +127,12 @@ void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup()
void tst_QQmlEngineDebugInspectorIntegration::select()
{
- QCOMPARE(init(true), ConnectSuccess);
+ QCOMPARE(runAndConnect(true), ConnectSuccess);
- QQmlEngineDebugObjectReference rootObject = findRootObject();
+ const QQmlEngineDebugObjectReference rootObject = findRootObject();
QList<int> childIds;
int requestId = 0;
- foreach (const QQmlEngineDebugObjectReference &child, rootObject.children) {
+ for (const QQmlEngineDebugObjectReference &child : rootObject.children) {
requestId = m_inspectorClient->select(QList<int>() << child.debugId);
QTRY_COMPARE(m_recipient->lastResponseId, requestId);
QVERIFY(m_recipient->lastResult);
@@ -145,7 +145,7 @@ void tst_QQmlEngineDebugInspectorIntegration::select()
void tst_QQmlEngineDebugInspectorIntegration::createObject()
{
- QCOMPARE(init(true), ConnectSuccess);
+ QCOMPARE(runAndConnect(true), ConnectSuccess);
QString qml = QLatin1String("Rectangle {\n"
" id: xxxyxxx\n"
@@ -172,7 +172,7 @@ void tst_QQmlEngineDebugInspectorIntegration::createObject()
void tst_QQmlEngineDebugInspectorIntegration::moveObject()
{
- QCOMPARE(init(true), ConnectSuccess);
+ QCOMPARE(runAndConnect(true), ConnectSuccess);
QCOMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled);
QQmlEngineDebugObjectReference rootObject = findRootObject();
@@ -197,7 +197,7 @@ void tst_QQmlEngineDebugInspectorIntegration::moveObject()
void tst_QQmlEngineDebugInspectorIntegration::destroyObject()
{
- QCOMPARE(init(true), ConnectSuccess);
+ QCOMPARE(runAndConnect(true), ConnectSuccess);
QCOMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled);
QQmlEngineDebugObjectReference rootObject = findRootObject();
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt b/tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt
index 4d4a939da5..d412368767 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlenginedebugservice Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlenginedebugservice LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml
index 9e4546bdac..bda1673b18 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/complexItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Test 1.0
@@ -7,7 +7,7 @@ Item {
id: root
width: 10; height: 20; scale: blueRect.scale;
Rectangle { id: blueRect; width: 500; height: 600; color: "blue"; }
- Text { font.bold: true; color: blueRect.color; }
+ Text { id: blueText; font.bold: true; color: blueRect.color; }
MouseArea {
onEntered: { console.log('hello') }
}
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml
index a472988772..3203cc5bfc 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/customTypes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Backend 1.0
CustomTypes {
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml
index 0532b44726..994b2f82f1 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Controls 2.5
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml
index ebbf3f85e3..ded2b620ce 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/emptyItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
Item {
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml
index ea0c3ff8c0..0a2e68f786 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/fetchValueType.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml
index 118b13f2d7..e107ed5259 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/itemWithFunctions.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
Item {
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml
index eb0c65b42f..27f6ffcc92 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/jsonTest.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import JsonTest 1.0
JsonTest {
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml
index fee3f969b8..d8fe7f98c3 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/rectangleWithTransitions.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
Rectangle {
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
index e6e21cbb9e..a63c690cf0 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "debugutil_p.h"
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -22,6 +22,7 @@
#include <QtQml/qqmlproperty.h>
#include <QtQml/qqmlincubator.h>
#include <QtQml/qqmlapplicationengine.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <QtQuick/qquickitem.h>
#include <QtNetwork/qhostaddress.h>
@@ -244,7 +245,7 @@ void tst_QQmlEngineDebugService::recursiveObjectTest(
QCOMPARE(p.objectDebugId, QQmlDebugService::idForObject(o));
// signal properties are fake - they are generated from QQmlAbstractBoundSignal children
- if (p.name.startsWith("on") && p.name.size() > 2 && p.name[2].isUpper()) {
+ if (QQmlSignalNames::isHandlerName(p.name)) {
QString signal = p.value.toString();
QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(QQmlProperty(o, p.name));
QVERIFY(expr && expr->expression() == signal);
@@ -414,10 +415,10 @@ void tst_QQmlEngineDebugService::watch_property()
bool success;
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
unconnected->addWatch(prop, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
m_dbg->addWatch(QQmlEngineDebugPropertyReference(), &success);
QVERIFY(success);
@@ -458,10 +459,10 @@ void tst_QQmlEngineDebugService::watch_object()
bool success;
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
unconnected->addWatch(obj, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
m_dbg->addWatch(QQmlEngineDebugObjectReference(), &success);
QVERIFY(success);
@@ -525,10 +526,10 @@ void tst_QQmlEngineDebugService::watch_expression()
bool success;
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
unconnected->addWatch(obj, expr, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
m_dbg->addWatch(QQmlEngineDebugObjectReference(), expr, &success);
QVERIFY(success);
@@ -600,10 +601,10 @@ void tst_QQmlEngineDebugService::queryAvailableEngines()
{
bool success;
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
unconnected->queryAvailableEngines(&success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
m_dbg->queryAvailableEngines(&success);
QVERIFY(success);
@@ -628,10 +629,10 @@ void tst_QQmlEngineDebugService::queryRootContexts()
QVERIFY(m_dbg->engines().size());
const QQmlEngineDebugEngineReference engine = m_dbg->engines()[0];
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
unconnected->queryRootContexts(engine, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
m_dbg->queryRootContexts(engine, &success);
QVERIFY(success);
@@ -659,10 +660,10 @@ void tst_QQmlEngineDebugService::queryObject()
QQmlEngineDebugObjectReference rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
recursive ? unconnected->queryObjectRecursive(rootObject, &success) : unconnected->queryObject(rootObject, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
recursive ? m_dbg->queryObjectRecursive(rootObject, &success) : m_dbg->queryObject(rootObject, &success);
QVERIFY(success);
@@ -690,9 +691,9 @@ void tst_QQmlEngineDebugService::queryObject()
QQmlEngineDebugObjectReference text;
for (const QQmlEngineDebugObjectReference &child : obj.children) {
QVERIFY(!child.className.isEmpty());
- if (child.className == "Rectangle")
+ if (child.idString == "blueRect")
rect = child;
- else if (child.className == "Text")
+ else if (child.idString == "blueText")
text = child;
}
@@ -731,13 +732,13 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation()
int lineNumber = rootObject.source.lineNumber;
int columnNumber = rootObject.source.columnNumber;
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
recursive ? unconnected->queryObjectsForLocationRecursive(fileName, lineNumber,
columnNumber, &success)
: unconnected->queryObjectsForLocation(fileName, lineNumber,
columnNumber, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
recursive ? m_dbg->queryObjectsForLocationRecursive(fileName, lineNumber,
columnNumber, &success)
@@ -769,9 +770,9 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation()
QQmlEngineDebugObjectReference text;
for (const QQmlEngineDebugObjectReference &child : obj.children) {
QVERIFY(!child.className.isEmpty());
- if (child.className == "Rectangle")
+ if (child.idString == "blueRect")
rect = child;
- else if (child.className == "Text")
+ else if (child.idString == "blueText")
text = child;
}
@@ -813,9 +814,9 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451()
"text: \"test\"\n"
"}");
component.setData(content, rootObject.source.url);
- QObject *object = component.create(context);
+ std::unique_ptr<QObject> object { component.create(context) };
QVERIFY(object);
- int idNew = QQmlDebugService::idForObject(object);
+ int idNew = QQmlDebugService::idForObject(object.get());
QVERIFY(idNew >= 0);
const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName();
@@ -839,7 +840,7 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451()
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
}
- delete object;
+ object.reset();
QObject *deleted = QQmlDebugService::objectForId(idNew);
QVERIFY(!deleted);
@@ -870,10 +871,10 @@ void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes()
QQmlEngineDebugObjectReference rootObject = findRootObject(4, true);
QVERIFY(!rootObject.className.isEmpty());
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
unconnected->queryObject(rootObject, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
m_dbg->queryObject(rootObject, &success);
QVERIFY(success);
@@ -914,10 +915,10 @@ void tst_QQmlEngineDebugService::queryExpressionResult()
bool success;
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
unconnected->queryExpressionResult(objectId, expr, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
m_dbg->queryExpressionResult(objectId, expr, &success);
QVERIFY(success);
@@ -967,10 +968,10 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC()
bool success;
- QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
+ std::unique_ptr<QQmlEngineDebugClient> unconnected = std::make_unique<QQmlEngineDebugClient>(nullptr);
unconnected->queryExpressionResultBC(objectId, expr, &success);
QVERIFY(!success);
- delete unconnected;
+ unconnected.reset();
m_dbg->queryExpressionResultBC(objectId, expr, &success);
QVERIFY(success);
diff --git a/tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt b/tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt
index 7e6fb3559d..34a9b7a393 100644
--- a/tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmlinspector/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlinspector Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlinspector LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp
index 897d8c8688..7f4e5eb73f 100644
--- a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp
+++ b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/debugutil_p.h"
#include "../shared/qqmldebugprocess_p.h"
diff --git a/tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt b/tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt
index 705361c3be..f6d8988ec2 100644
--- a/tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmlnativeconnector/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlnativeconnector Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlnativeconnector LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmlnativeconnector
SOURCES
tst_qqmlnativeconnector.cpp
diff --git a/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp b/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp
index 55a9aeb3f5..dd9789cdc5 100644
--- a/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp
+++ b/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
diff --git a/tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt b/tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt
index 3eaf14b35d..83d33a617f 100644
--- a/tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmlpreview/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlpreview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlpreview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
list(APPEND test_data "data/window.qml")
list(APPEND test_data "data/qtquick2.qml")
diff --git a/tests/auto/qml/debugger/qqmlpreview/data/broken.qml b/tests/auto/qml/debugger/qqmlpreview/data/broken.qml
index 75387c42aa..e8f82461a0 100644
--- a/tests/auto/qml/debugger/qqmlpreview/data/broken.qml
+++ b/tests/auto/qml/debugger/qqmlpreview/data/broken.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml b/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml
index 4013dfc9ff..d49b94fc7e 100644
--- a/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml
+++ b/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window.qml b/tests/auto/qml/debugger/qqmlpreview/data/window.qml
index 951b9a7f10..1141292a2c 100644
--- a/tests/auto/qml/debugger/qqmlpreview/data/window.qml
+++ b/tests/auto/qml/debugger/qqmlpreview/data/window.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.0
diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window1.qml b/tests/auto/qml/debugger/qqmlpreview/data/window1.qml
index 4a24041dea..20c7ab04ea 100644
--- a/tests/auto/qml/debugger/qqmlpreview/data/window1.qml
+++ b/tests/auto/qml/debugger/qqmlpreview/data/window1.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.3
diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window2.qml b/tests/auto/qml/debugger/qqmlpreview/data/window2.qml
index 0de5dc36c8..d144cfb033 100644
--- a/tests/auto/qml/debugger/qqmlpreview/data/window2.qml
+++ b/tests/auto/qml/debugger/qqmlpreview/data/window2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.3
diff --git a/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml b/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml
index 86445a2fdc..18712a7e2d 100644
--- a/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml
+++ b/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
index 0c4fd568a9..5874100ebd 100644
--- a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
+++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qqmldebugprocess_p.h>
#include <debugutil_p.h>
@@ -303,7 +303,7 @@ void tst_QQmlPreview::zoom()
for (auto testZoomFactor : {2.0f, 1.5f, 0.5f}) {
m_client->triggerZoom(testZoomFactor);
- verifyZoomFactor(m_process, testZoomFactor);
+ verifyZoomFactor(m_process, testZoomFactor * baseZoomFactor);
}
m_client->triggerZoom(-1.0f);
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt b/tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt
index 1e6c4c1753..d770f1fb27 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlprofilerservice Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlprofilerservice LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
index 3a7c093fa5..6e16eaa7b1 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.0
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml
index 026911d1cc..788cd86cce 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
index 53971d3e8b..2088f958ae 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "debugutil_p.h"
#include "qqmldebugprocess_p.h"
@@ -80,7 +80,7 @@ void QQmlProfilerTestClient::addEvent(const QQmlProfilerEvent &event)
const QQmlProfilerEventType &type = types[typeIndex];
- QVERIFY(event.timestamp() >= lastTimestamp);
+ const qint64 oldTimestamp = lastTimestamp;
lastTimestamp = event.timestamp();
switch (type.message()) {
@@ -149,6 +149,8 @@ void QQmlProfilerTestClient::addEvent(const QQmlProfilerEvent &event)
}
break;
}
+
+ QCOMPARE_GE(lastTimestamp, oldTimestamp);
}
class tst_QQmlProfilerService : public QQmlDebugTest
@@ -287,7 +289,7 @@ void tst_QQmlProfilerService::checkJsHeap()
qint64 allocated = 0;
qint64 used = 0;
qint64 lastTimestamp = -1;
- foreach (const QQmlProfilerEvent &message, m_client->jsHeapMessages) {
+ for (const QQmlProfilerEvent &message : std::as_const(m_client->jsHeapMessages)) {
const auto amount = message.number<qint64>(0);
const QQmlProfilerEventType &type = m_client->types.at(message.typeIndex());
switch (type.detailType()) {
@@ -440,7 +442,7 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty
return true;
} while (++position < target->size() && target->at(position).timestamp() == timestamp);
- foreach (const QString &message, warnings)
+ for (const QString &message : std::as_const(warnings))
qWarning() << message.toLocal8Bit().constData();
return false;
@@ -580,7 +582,7 @@ void tst_QQmlProfilerService::scenegraphData()
QCOMPARE(connectTo(true, "scenegraphTest.qml"), ConnectSuccess);
while (!m_process->output().contains(QLatin1String("tick")))
- QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput())));
+ QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()), 30000));
m_client->client->setRecording(false);
checkTraceReceived();
@@ -594,7 +596,7 @@ void tst_QQmlProfilerService::scenegraphData()
// interleaved. Also, events could carry the same time stamps and be sorted in an unexpected way
// if the clocks are acting up.
qint64 renderFrameTime = -1;
- foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) {
+ for (const QQmlProfilerEvent &msg : std::as_const(m_client->asynchronousMessages)) {
const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex());
if (type.detailType() == SceneGraphRendererFrame) {
renderFrameTime = msg.timestamp();
diff --git a/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt b/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt
index 0b60a0092b..42f6cf931a 100644
--- a/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt
+++ b/tests/auto/qml/debugger/qv4debugger/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qv4debugger Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4debugger LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -17,11 +23,12 @@ qt_add_library(testCppTypes STATIC)
qt_autogen_tools_initial_setup(testCppTypes)
target_link_libraries(testCppTypes PRIVATE Qt::Qml Qt::QmlPrivate Qt::Quick)
+qt_policy(SET QTP0001 NEW)
+
qt6_add_qml_module(testCppTypes
URI TestTypes
SOURCES
commontypes.h
- AUTO_RESOURCE_PREFIX
OUTPUT_DIRECTORY TestTypes
)
qt_autogen_tools_initial_setup(testCppTypesplugin)
diff --git a/tests/auto/qml/debugger/qv4debugger/commontypes.h b/tests/auto/qml/debugger/qv4debugger/commontypes.h
index 01b2125ae3..b63059b0e6 100644
--- a/tests/auto/qml/debugger/qv4debugger/commontypes.h
+++ b/tests/auto/qml/debugger/qv4debugger/commontypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef COMMONTYPES_H
#define COMMONTYPES_H
@@ -14,7 +14,7 @@ class MyType : public QQuickItem
QML_ELEMENT
public:
MyType(QQuickItem *parent = nullptr) : QQuickItem(parent) {}
- Q_INVOKABLE void name(QQmlV4Function*) const {}
+ Q_INVOKABLE void name(QQmlV4FunctionPtr) const {}
};
#endif // COMMONTYPES_H
diff --git a/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml b/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml
new file mode 100644
index 0000000000..2582a23ec5
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml
@@ -0,0 +1,4 @@
+import QtQml 2.15
+import "module1.js" as Module1
+
+QtObject {}
diff --git a/tests/auto/qml/debugger/qv4debugger/data/module1.js b/tests/auto/qml/debugger/qv4debugger/data/module1.js
new file mode 100644
index 0000000000..9ce1f1e6b1
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/module1.js
@@ -0,0 +1,5 @@
+.pragma library
+
+.import "module2.mjs" as Module2
+
+Module2.crashMe();
diff --git a/tests/auto/qml/debugger/qv4debugger/data/module2.mjs b/tests/auto/qml/debugger/qv4debugger/data/module2.mjs
new file mode 100644
index 0000000000..80f82af953
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/module2.mjs
@@ -0,0 +1,7 @@
+import * as Module3 from "module3.mjs"
+import * as Module4 from "module4.mjs"
+
+export function crashMe()
+{
+ console.log("Hello world!");
+}
diff --git a/src/plugins/scenegraph/openvg/openvg.tracepoints b/tests/auto/qml/debugger/qv4debugger/data/module3.mjs
index e69de29bb2..e69de29bb2 100644
--- a/src/plugins/scenegraph/openvg/openvg.tracepoints
+++ b/tests/auto/qml/debugger/qv4debugger/data/module3.mjs
diff --git a/tests/auto/qml/debugger/qv4debugger/data/module4.mjs b/tests/auto/qml/debugger/qv4debugger/data/module4.mjs
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/module4.mjs
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index 6f147446f0..3da401b5bb 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtTest/QtTest>
@@ -167,7 +168,7 @@ public slots:
m_thrownValue = job.exceptionValue();
}
- foreach (const TestBreakPoint &bp, m_breakPointsToAddWhenPaused)
+ for (const TestBreakPoint &bp : std::as_const(m_breakPointsToAddWhenPaused))
debugger->addBreakPoint(bp.fileName, bp.lineNumber);
m_breakPointsToAddWhenPaused.clear();
@@ -216,10 +217,14 @@ public:
QJsonArray scopes = frameObj.value(QLatin1String("scopes")).toArray();
int nscopes = scopes.size();
int s = 0;
- for (s = 0; s < nscopes; ++s) {
- QJsonObject o = scopes.at(s).toObject();
- if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext
- break;
+ if (m_targetScope != -1) {
+ s = m_targetScope;
+ } else {
+ for (s = 0; s < nscopes; ++s) {
+ QJsonObject o = scopes.at(s).toObject();
+ if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext
+ break;
+ }
}
if (s == nscopes)
return;
@@ -249,6 +254,7 @@ public:
bool m_wasPaused;
QV4Debugger::PauseReason m_pauseReason;
bool m_captureContextInfo;
+ int m_targetScope = -1;
QList<QV4Debugger::ExecutionState> m_statesWhenPaused;
QList<TestBreakPoint> m_breakPointsToAddWhenPaused;
QVector<QV4::StackFrame> m_stackTrace;
@@ -274,9 +280,10 @@ public:
void dumpStackTrace() const
{
qDebug() << "Stack depth:" << m_stackTrace.size();
- foreach (const QV4::StackFrame &frame, m_stackTrace)
+ for (const QV4::StackFrame &frame : m_stackTrace) {
qDebug("\t%s (%s:%d:%d)", qPrintable(frame.function), qPrintable(frame.source),
- frame.line, frame.column);
+ qAbs(frame.line), frame.column);
+ }
}
};
@@ -322,6 +329,9 @@ private slots:
void readThis();
void signalParameters();
void debuggerNoCrash();
+
+ void breakPointInJSModule();
+
private:
QV4Debugger *debugger() const
{
@@ -329,27 +339,27 @@ private:
}
void evaluateJavaScript(const QString &script, const QString &fileName, int lineNumber = 1)
{
- QMetaObject::invokeMethod(m_engine, "evaluate", Qt::QueuedConnection,
+ QMetaObject::invokeMethod(m_engine.get(), "evaluate", Qt::QueuedConnection,
Q_ARG(QString, script), Q_ARG(QString, fileName),
Q_ARG(int, lineNumber));
- waitForSignal(m_engine, SIGNAL(evaluateFinished()), /*timeout*/0);
+ waitForSignal(m_engine.get(), SIGNAL(evaluateFinished()), /*timeout*/0);
}
- TestEngine *m_engine;
+ std::unique_ptr<TestEngine> m_engine;
QV4::ExecutionEngine *m_v4;
- TestAgent *m_debuggerAgent;
- QThread *m_javaScriptThread;
+ std::unique_ptr<TestAgent> m_debuggerAgent;
+ std::unique_ptr<QThread> m_javaScriptThread;
};
void tst_qv4debugger::init()
{
- m_javaScriptThread = new QThread;
- m_engine = new TestEngine;
+ m_javaScriptThread = std::make_unique<QThread>();
+ m_engine = std::make_unique<TestEngine>();
m_v4 = m_engine->v4Engine();
m_v4->setDebugger(new QV4Debugger(m_v4));
- m_engine->moveToThread(m_javaScriptThread);
+ m_engine->moveToThread(m_javaScriptThread.get());
m_javaScriptThread->start();
- m_debuggerAgent = new TestAgent(m_v4);
+ m_debuggerAgent = std::make_unique<TestAgent>(m_v4);
m_debuggerAgent->addDebugger(debugger());
}
@@ -357,11 +367,11 @@ void tst_qv4debugger::cleanup()
{
m_javaScriptThread->exit();
m_javaScriptThread->wait();
- delete m_engine;
- delete m_javaScriptThread;
+ m_engine.reset();
+ m_javaScriptThread.reset();
m_engine = nullptr;
m_v4 = nullptr;
- delete m_debuggerAgent;
+ m_debuggerAgent.reset();
m_debuggerAgent = nullptr;
}
@@ -453,7 +463,7 @@ void tst_qv4debugger::removeBreakPointForNextInstruction()
"someCall();\n"
"var i = 42;";
- QMetaObject::invokeMethod(m_engine, "injectFunction", Qt::BlockingQueuedConnection,
+ QMetaObject::invokeMethod(m_engine.get(), "injectFunction", Qt::BlockingQueuedConnection,
Q_ARG(QString, "someCall"), Q_ARG(InjectedFunction, someCall));
debugger()->addBreakPoint("removeBreakPointForNextInstruction", 2);
@@ -967,6 +977,35 @@ void tst_qv4debugger::debuggerNoCrash()
debugThread->wait();
}
+void tst_qv4debugger::breakPointInJSModule()
+{
+ QQmlEngine engine;
+ QV4::ExecutionEngine *v4 = engine.handle();
+ QPointer<QV4Debugger> v4Debugger = new QV4Debugger(v4);
+ v4->setDebugger(v4Debugger.data());
+
+ QScopedPointer<QThread> debugThread(new QThread);
+ debugThread->start();
+ QScopedPointer<TestAgent> debuggerAgent(new TestAgent(v4));
+ debuggerAgent->addDebugger(v4Debugger);
+ debuggerAgent->moveToThread(debugThread.data());
+
+ QQmlComponent component(&engine, testFileUrl("breakPointInJSModule.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ debuggerAgent->m_captureContextInfo = true;
+ debuggerAgent->m_targetScope = 1;
+ v4Debugger->addBreakPoint("module2.mjs", 6);
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+
+ QVERIFY(!debuggerAgent->m_capturedScope.isEmpty());
+
+ debugThread->quit();
+ debugThread->wait();
+}
+
tst_qv4debugger::tst_qv4debugger() : QQmlDataTest(QT_QMLTEST_DATADIR) { }
QTEST_MAIN(tst_qv4debugger)
diff --git a/tests/auto/qml/debugger/shared/debugutil.cpp b/tests/auto/qml/debugger/shared/debugutil.cpp
index bce2c28378..28d40d768d 100644
--- a/tests/auto/qml/debugger/shared/debugutil.cpp
+++ b/tests/auto/qml/debugger/shared/debugutil.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "debugutil_p.h"
#include "qqmldebugprocess_p.h"
diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h
index 0303aa2da9..188cc14c32 100644
--- a/tests/auto/qml/debugger/shared/debugutil_p.h
+++ b/tests/auto/qml/debugger/shared/debugutil_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DEBUGUTIL_P_H
#define DEBUGUTIL_P_H
diff --git a/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp b/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp
index 4944ba2e15..9e5bc17623 100644
--- a/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp
+++ b/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qqmldebugprocess_p.h"
diff --git a/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h b/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h
index 56ed9b9830..94554928a5 100644
--- a/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h
+++ b/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QQMLDEBUGPROCESS_P_H
#define QQMLDEBUGPROCESS_P_H
diff --git a/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp b/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp
index daafa8c26e..4884c9d400 100644
--- a/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp
+++ b/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qqmldebugtestservice.h"
#include <QThread>
diff --git a/tests/auto/qml/debugger/shared/qqmldebugtestservice.h b/tests/auto/qml/debugger/shared/qqmldebugtestservice.h
index 150ded9b1b..a2a3ce6161 100644
--- a/tests/auto/qml/debugger/shared/qqmldebugtestservice.h
+++ b/tests/auto/qml/debugger/shared/qqmldebugtestservice.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QQMLDEBUGTESTSERVICE_H
#define QQMLDEBUGTESTSERVICE_H
diff --git a/tests/auto/qml/ecmascripttests/CMakeLists.txt b/tests/auto/qml/ecmascripttests/CMakeLists.txt
index d3da3adb53..1ee70cb101 100644
--- a/tests/auto/qml/ecmascripttests/CMakeLists.txt
+++ b/tests/auto/qml/ecmascripttests/CMakeLists.txt
@@ -5,15 +5,22 @@
## tst_ecmascripttests Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_ecmascripttests LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
test262/*)
list(APPEND test_data ${test_data_glob})
+list(FILTER test_data EXCLUDE REGEX ".git")
qt_internal_add_test(tst_ecmascripttests
SOURCES
- qjstest/test262runner.cpp qjstest/test262runner.h
+ test262runner.cpp test262runner.h
tst_ecmascripttests.cpp
LIBRARIES
Qt::QmlPrivate
@@ -40,7 +47,3 @@ else()
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/test262"
)
endif()
-
-if(NOT CMAKE_CROSSCOMPILING)
- add_subdirectory(qjstest)
-endif()
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index 75fdd1cb0c..2e96de7819 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -18,6 +18,12 @@ language/statements/labeled/let-identifier-with-newline.js sloppyFails
language/statements/while/let-identifier-with-newline.js sloppyFails
language/statements/with/let-identifier-with-newline.js sloppyFails
+# These failures are a defect in the Yarr regexp engine we are using.
+# They all amount to some variation of: /\udf06/u.exec('\ud834\udf06')
+built-ins/RegExp/prototype/Symbol.match/builtin-infer-unicode.js
+built-ins/RegExp/prototype/Symbol.search/u-lastindex-advance.js
+built-ins/RegExp/prototype/exec/u-lastindex-adv.js
+
# The ES6/7 spec says that [[DefineOwnProperty]] on the module namespace exotic object
# always returns false. This was changed in https://github.com/tc39/ecma262/pull/858
# but it's not in the published spec yet.
@@ -211,7 +217,6 @@ built-ins/Promise/prototype/then/ctor-throws.js fails
built-ins/Promise/race/ctx-ctor.js fails
built-ins/Proxy/ownKeys/return-duplicate-entries-throws.js fails
built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws.js fails
-built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js fails
built-ins/RegExp/prototype/Symbol.split/species-ctor.js fails
built-ins/RegExp/prototype/exec/S15.10.6.2_A5_T3.js fails
built-ins/RegExp/prototype/exec/failure-lastindex-access.js fails
diff --git a/tests/auto/qml/ecmascripttests/qjstest/CMakeLists.txt b/tests/auto/qml/ecmascripttests/qjstest/CMakeLists.txt
deleted file mode 100644
index 86ca5f97a3..0000000000
--- a/tests/auto/qml/ecmascripttests/qjstest/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-# Generated from qjstest.pro.
-
-#####################################################################
-## qjstest Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qjstest)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "Javascript test runner"
- SOURCES
- main.cpp
- test262runner.cpp test262runner.h
- DEFINES
- QT_DEPRECATED_WARNINGS
- INCLUDE_DIRECTORIES
- .
- LIBRARIES
- Qt::Gui
- Qt::QmlPrivate
-)
-qt_internal_return_unless_building_tools()
-
-#### Keys ignored in scope 1:.:.:qjstest.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "Javascript" "test" "runner"
-# TEMPLATE = "app"
diff --git a/tests/auto/qml/ecmascripttests/qjstest/main.cpp b/tests/auto/qml/ecmascripttests/qjstest/main.cpp
deleted file mode 100644
index 7bffedae81..0000000000
--- a/tests/auto/qml/ecmascripttests/qjstest/main.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <QJSEngine>
-#include <QCoreApplication>
-#include <QCommandLineParser>
-#include <qdebug.h>
-#include <stdlib.h>
-
-#include "test262runner.h"
-
-int main(int argc, char **argv)
-{
- QCoreApplication app(argc, argv);
-
-
- QCommandLineParser parser;
- parser.addHelpOption();
- parser.addVersionOption();
- QCommandLineOption verbose("verbose", "Verbose output");
- parser.addOption(verbose);
- QCommandLineOption commandOption("command", "Javascript command line interpreter", "command");
- parser.addOption(commandOption);
- QCommandLineOption testDir("tests", "path to the tests", "tests", "test262");
- parser.addOption(testDir);
- QCommandLineOption cat("cat", "Print packaged test code that would be run");
- parser.addOption(cat);
- QCommandLineOption parallel("parallel", "Run tests in parallel");
- parser.addOption(parallel);
- QCommandLineOption jit("jit", "JIT all code");
- parser.addOption(jit);
- QCommandLineOption bytecode("interpret", "Run using the bytecode interpreter");
- parser.addOption(bytecode);
- QCommandLineOption withExpectations("with-test-expectations", "Parse TestExpectations to deal with known failures");
- parser.addOption(withExpectations);
- QCommandLineOption updateExpectations("update-expectations", "Update TestExpectations to remove unexepected passes");
- parser.addOption(updateExpectations);
- QCommandLineOption writeExpectations("write-expectations", "Generate a new TestExpectations file based on the results of the run");
- parser.addOption(writeExpectations);
- parser.addPositionalArgument("[filter]", "Only run tests that contain filter in their name");
-
- parser.process(app);
-
- Test262Runner testRunner(parser.value(commandOption), parser.value(testDir), QStringLiteral("TestExpectations"));
-
- QStringList otherArgs = parser.positionalArguments();
- if (otherArgs.size() > 1) {
- qWarning() << "too many arguments";
- return 1;
- } else if (otherArgs.size()) {
- testRunner.setFilter(otherArgs.at(0));
- }
-
- if (parser.isSet(cat)) {
- testRunner.cat();
- return 0;
- }
-
- if (parser.isSet(updateExpectations) && parser.isSet(writeExpectations)) {
- qWarning() << "Can only specify one of --update-expectations and --write-expectations.";
- exit(1);
- }
-
- if (parser.isSet(jit) && parser.isSet(bytecode)) {
- qWarning() << "Can only specify one of --jit and --interpret.";
- exit(1);
- }
-
- int flags = 0;
- if (parser.isSet(verbose))
-
- flags |= Test262Runner::Verbose;
- if (parser.isSet(parallel))
- flags |= Test262Runner::Parallel;
- if (parser.isSet(jit))
- flags |= Test262Runner::ForceJIT;
- if (parser.isSet(bytecode))
- flags |= Test262Runner::ForceBytecode;
- if (parser.isSet(withExpectations))
- flags |= Test262Runner::WithTestExpectations;
- if (parser.isSet(updateExpectations))
- flags |= Test262Runner::UpdateTestExpectations;
- if (parser.isSet(writeExpectations))
- flags |= Test262Runner::WriteTestExpectations;
- testRunner.setFlags(flags);
-
- if (testRunner.run())
- return EXIT_SUCCESS;
- else
- return EXIT_FAILURE;
-}
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
deleted file mode 100644
index fc09182f19..0000000000
--- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
+++ /dev/null
@@ -1,849 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "test262runner.h"
-
-#include <qfile.h>
-#include <qdir.h>
-#include <qdiriterator.h>
-#include <qdebug.h>
-#include <qprocess.h>
-#include <qtemporaryfile.h>
-
-#include <private/qv4script_p.h>
-#include "private/qv4globalobject_p.h"
-#include "private/qqmlbuiltinfunctions_p.h"
-#include "private/qv4arraybuffer_p.h"
-#include <QtCore/QLoggingCategory>
-
-#include "qrunnable.h"
-
-static const char *excludedFeatures[] = {
- "BigInt",
- "class-fields-public",
- "class-fields-private",
- "Promise.prototype.finally",
- "async-iteration",
- "Symbol.asyncIterator",
- "object-rest",
- "object-spread",
- "optional-catch-binding",
- "regexp-dotall",
- "regexp-lookbehind",
- "regexp-named-groups",
- "regexp-unicode-property-escapes",
- "Atomics",
- "SharedArrayBuffer",
- "Array.prototype.flatten",
- "Array.prototype.flatMap",
- "string-trimming",
- "String.prototype.trimEnd",
- "String.prototype.trimStart",
- "numeric-separator-literal",
-
- // optional features, not supported by us
- "caller",
- nullptr
-};
-
-static const char *excludedFilePatterns[] = {
- "realm",
- nullptr
-};
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-
-static ReturnedValue method_detachArrayBuffer(const FunctionObject *f, const Value *, const Value *argv, int argc)
-{
- Scope scope(f);
- if (!argc)
- return scope.engine->throwTypeError();
- Scoped<ArrayBuffer> a(scope, argv[0]);
- if (!a)
- return scope.engine->throwTypeError();
-
- if (a->hasSharedArrayData())
- return scope.engine->throwTypeError();
-
- a->d()->detachArrayData();
-
- return Encode::null();
-}
-
-static void initD262(ExecutionEngine *e)
-{
- Scope scope(e);
- ScopedObject d262(scope, e->newObject());
-
- d262->defineDefaultProperty(QStringLiteral("detachArrayBuffer"), method_detachArrayBuffer, 1);
-
- ScopedString s(scope, e->newString(QStringLiteral("$262")));
- e->globalObject->put(s, d262);
-}
-
-}
-
-QT_END_NAMESPACE
-
-Q_DECLARE_LOGGING_CATEGORY(lcJsTest);
-Q_LOGGING_CATEGORY(lcJsTest, "qt.v4.ecma262.tests", QtWarningMsg);
-
-Test262Runner::Test262Runner(const QString &command, const QString &dir, const QString &expectationsFile)
- : command(command), testDir(dir), expectationsFile(expectationsFile)
-{
- if (testDir.endsWith(QLatin1Char('/')))
- testDir = testDir.chopped(1);
-}
-
-Test262Runner::~Test262Runner()
-{
- delete threadPool;
-}
-
-void Test262Runner::cat()
-{
- if (!loadTests())
- return;
-
- if (testCases.size() != 1)
- qWarning() << "test262 --cat: Ambiguous test case, using" << testCases.begin().key();
- TestData data = getTestData(testCases.begin().value());
- printf("%s", data.content.constData());
-}
-
-bool Test262Runner::run()
-{
- if (!loadTests())
- return false;
-
- if (flags & Parallel) {
- threadPool = new QThreadPool;
- threadPool->setStackSize(16*1024*1024);
- if (flags & Verbose)
- qDebug() << "Running in parallel with" << QThread::idealThreadCount() << "threads.";
- }
-
- if (flags & ForceJIT)
- qputenv("QV4_JIT_CALL_THRESHOLD", QByteArray("0"));
- else if (flags & ForceBytecode)
- qputenv("QV4_FORCE_INTERPRETER", QByteArray("1"));
-
- if (flags & WithTestExpectations)
- loadTestExpectations();
-
- for (auto it = testCases.constBegin(); it != testCases.constEnd(); ++it) {
- auto c = it.value();
- if (!c.skipTestCase) {
- int result = runSingleTest(c);
- if (result == -2)
- return false;
- }
- }
-
- if (threadPool)
- threadPool->waitForDone();
-
- const bool testsOk = report();
-
- if (flags & WriteTestExpectations)
- writeTestExpectations();
- else if (flags & UpdateTestExpectations)
- updateTestExpectations();
-
- return testsOk;
-}
-
-bool Test262Runner::report()
-{
- qDebug() << "Test execution summary:";
- qDebug() << " Executed" << testCases.size() << "test cases.";
- QStringList crashes;
- QStringList unexpectedFailures;
- QStringList unexpectedPasses;
- for (auto it = testCases.constBegin(); it != testCases.constEnd(); ++it) {
- const auto c = it.value();
- if (c.strictResult.state == c.strictExpectation.state
- && c.sloppyResult.state == c.sloppyExpectation.state)
- continue;
- auto report = [&](TestCase::Result expected, TestCase::Result result, const char *s) {
- if (result.state == TestCase::Crashes)
- crashes << (it.key() + " crashed in " + s + " mode");
- if (result.state == TestCase::Fails && expected.state == TestCase::Passes)
- unexpectedFailures << (it.key() + " failed in " + s
- + " mode with error message: " + result.errorMessage);
- if (result.state == TestCase::Passes && expected.state == TestCase::Fails)
- unexpectedPasses << (it.key() + " unexpectedly passed in " + s + " mode");
- };
- report(c.strictExpectation, c.strictResult, "strict");
- report(c.sloppyExpectation, c.sloppyResult, "sloppy");
- }
- if (!crashes.isEmpty()) {
- qDebug() << " Encountered" << crashes.size() << "crashes in the following files:";
- for (const QString &f : std::as_const(crashes))
- qDebug() << " " << f;
- }
- if (!unexpectedFailures.isEmpty()) {
- qDebug() << " Encountered" << unexpectedFailures.size() << "unexpected failures in the following files:";
- for (const QString &f : std::as_const(unexpectedFailures))
- qDebug() << " " << f;
- }
- if (!unexpectedPasses.isEmpty()) {
- qDebug() << " Encountered" << unexpectedPasses.size() << "unexpected passes in the following files:";
- for (const QString &f : std::as_const(unexpectedPasses))
- qDebug() << " " << f;
- }
- return crashes.isEmpty() && unexpectedFailures.isEmpty() && unexpectedPasses.isEmpty();
-}
-
-bool Test262Runner::loadTests()
-{
- QDir dir(testDir + "/test");
- if (!dir.exists()) {
- qWarning() << "Could not load tests," << dir.path() << "does not exist.";
- return false;
- }
-
- QString annexB = "annexB";
- QString harness = "harness";
- QString intl402 = "intl402";
-
- int pathlen = dir.path().size() + 1;
- QDirIterator it(dir, QDirIterator::Subdirectories);
- while (it.hasNext()) {
- QString file = it.next().mid(pathlen);
- if (!file.endsWith(".js"))
- continue;
- if (file.endsWith("_FIXTURE.js"))
- continue;
- if (!filter.isEmpty() && !file.contains(filter))
- continue;
- if (file.startsWith(annexB) || file.startsWith(harness) || file.startsWith(intl402))
- continue;
- const char **excluded = excludedFilePatterns;
- bool skip = false;
- while (*excluded) {
- if (file.contains(QLatin1String(*excluded)))
- skip = true;
- ++excluded;
- }
- if (skip)
- continue;
-
- testCases.insert(file, TestCase{ file });
- }
- if (testCases.isEmpty()) {
- qWarning() << "No tests to run.";
- return false;
- }
-
- return true;
-}
-
-
-struct TestExpectationLine {
- TestExpectationLine(const QByteArray &line);
- enum State {
- Fails,
- SloppyFails,
- StrictFails,
- Skip,
- Passes
- } state;
- QString testCase;
-
- QByteArray toLine() const;
- void update(const TestCase &testCase);
-
- static TestExpectationLine fromTestCase(const TestCase &testCase);
-private:
- TestExpectationLine() = default;
- static State stateFromTestCase(const TestCase &testCase);
-};
-
-TestExpectationLine::TestExpectationLine(const QByteArray &line)
-{
- int space = line.indexOf(' ');
-
- testCase = QString::fromUtf8(space > 0 ? line.left(space) : line);
- if (!testCase.endsWith(".js"))
- testCase += ".js";
-
- state = Fails;
- if (space < 0)
- return;
- QByteArray qualifier = line.mid(space + 1);
- if (qualifier == "skip")
- state = Skip;
- else if (qualifier == "strictFails")
- state = StrictFails;
- else if (qualifier == "sloppyFails")
- state = SloppyFails;
- else if (qualifier == "fails")
- state = Fails;
- else
- qWarning() << "illegal format in TestExpectations, line" << line;
-}
-
-QByteArray TestExpectationLine::toLine() const {
- const char *res = nullptr;
- switch (state) {
- case Fails:
- res = " fails\n";
- break;
- case SloppyFails:
- res = " sloppyFails\n";
- break;
- case StrictFails:
- res = " strictFails\n";
- break;
- case Skip:
- res = " skip\n";
- break;
- case Passes:
- // no need for an entry
- return QByteArray();
- }
- QByteArray result = testCase.toUtf8() + res;
- return result;
-}
-
-void TestExpectationLine::update(const TestCase &testCase)
-{
- Q_ASSERT(testCase.test == this->testCase);
-
- State resultState = stateFromTestCase(testCase);
- switch (resultState) {
- case Fails:
- // no improvement, don't update
- break;
- case SloppyFails:
- if (state == Fails)
- state = SloppyFails;
- else if (state == StrictFails)
- // we have a regression in sloppy mode, but strict now passes
- state = Passes;
- break;
- case StrictFails:
- if (state == Fails)
- state = StrictFails;
- else if (state == SloppyFails)
- // we have a regression in strict mode, but sloppy now passes
- state = Passes;
- break;
- case Skip:
- Q_ASSERT(state == Skip);
- // nothing to do
- break;
- case Passes:
- state = Passes;
- }
-}
-
-TestExpectationLine TestExpectationLine::fromTestCase(const TestCase &testCase)
-{
- TestExpectationLine l;
- l.testCase = testCase.test;
- l.state = stateFromTestCase(testCase);
- return l;
-}
-
-TestExpectationLine::State TestExpectationLine::stateFromTestCase(const TestCase &testCase)
-{
- // keep skipped tests
- if (testCase.skipTestCase)
- return Skip;
-
- bool strictFails = (testCase.strictResult.state == TestCase::Crashes
- || testCase.strictResult.state == TestCase::Fails);
- bool sloppyFails = (testCase.sloppyResult.state == TestCase::Crashes
- || testCase.sloppyResult.state == TestCase::Fails);
- if (strictFails && sloppyFails)
- return Fails;
- if (strictFails)
- return StrictFails;
- if (sloppyFails)
- return SloppyFails;
- return Passes;
-}
-
-
-void Test262Runner::loadTestExpectations()
-{
- QFile file(expectationsFile);
- if (!file.open(QFile::ReadOnly)) {
- qWarning() << "Could not open TestExpectations file at" << expectationsFile;
- return;
- }
-
- while (!file.atEnd()) {
- QByteArray line = file.readLine().trimmed();
- if (line.startsWith('#') || line.isEmpty())
- continue;
- TestExpectationLine expectation(line);
- if (!filter.isEmpty() && !expectation.testCase.contains(filter))
- continue;
-
- if (!testCases.contains(expectation.testCase))
- qWarning() << "Unknown test case" << expectation.testCase << "in TestExpectations file.";
- //qDebug() << "TestExpectations:" << expectation.testCase << expectation.state;
- TestCase &s = testCases[expectation.testCase];
- switch (expectation.state) {
- case TestExpectationLine::Fails:
- s.strictExpectation.state = TestCase::Fails;
- s.sloppyExpectation.state = TestCase::Fails;
- break;
- case TestExpectationLine::SloppyFails:
- s.strictExpectation.state = TestCase::Passes;
- s.sloppyExpectation.state = TestCase::Fails;
- break;
- case TestExpectationLine::StrictFails:
- s.strictExpectation.state = TestCase::Fails;
- s.sloppyExpectation.state = TestCase::Passes;
- break;
- case TestExpectationLine::Skip:
- s.skipTestCase = true;
- break;
- case TestExpectationLine::Passes:
- Q_UNREACHABLE();
- }
- }
-}
-
-void Test262Runner::updateTestExpectations()
-{
- QFile file(expectationsFile);
- if (!file.open(QFile::ReadOnly)) {
- qWarning() << "Could not open TestExpectations file at" << expectationsFile;
- return;
- }
-
- QTemporaryFile updatedExpectations;
- updatedExpectations.open();
-
- while (!file.atEnd()) {
- QByteArray originalLine = file.readLine();
- QByteArray line = originalLine.trimmed();
- if (line.startsWith('#') || line.isEmpty()) {
- updatedExpectations.write(originalLine);
- continue;
- }
-
- TestExpectationLine expectation(line);
-// qDebug() << "checking: " << expectation.testCase;
- if (!testCases.contains(expectation.testCase)) {
- updatedExpectations.write(originalLine);
- continue;
- }
- const TestCase &testcase = testCases.value(expectation.testCase);
- expectation.update(testcase);
-
- line = expectation.toLine();
-// qDebug() << "updated line:" << line;
- updatedExpectations.write(line);
- }
- file.close();
- updatedExpectations.close();
- if (!file.remove())
- qWarning() << "Could not remove old TestExpectations file at" << expectationsFile;
- if (updatedExpectations.copy(file.fileName()))
- qDebug() << "Updated TestExpectations file written!";
- else
- qWarning() << "Could not write new TestExpectations file at" << expectationsFile;
-}
-
-void Test262Runner::writeTestExpectations()
-{
- QFile file(expectationsFile);
-
- QTemporaryFile expectations;
- expectations.open();
-
- for (auto c : std::as_const(testCases)) {
- TestExpectationLine line = TestExpectationLine::fromTestCase(c);
- expectations.write(line.toLine());
- }
-
- expectations.close();
- if (file.exists() && !file.remove())
- qWarning() << "Could not remove old TestExpectations file at" << expectationsFile;
- if (expectations.copy(file.fileName()))
- qDebug() << "new TestExpectations file written!";
- else
- qWarning() << "Could not write new TestExpectations file at" << expectationsFile;
-}
-
-static TestCase::Result executeTest(const QByteArray &data, bool runAsModule = false,
- const QString &testCasePath = QString(),
- const QByteArray &harnessForModules = QByteArray())
-{
- QString testData = QString::fromUtf8(data.constData(), data.size());
-
- QV4::ExecutionEngine vm;
-
- QV4::Scope scope(&vm);
-
- QV4::GlobalExtensions::init(vm.globalObject, QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension);
- QV4::initD262(&vm);
-
- if (runAsModule) {
- const QUrl rootModuleUrl = QUrl::fromLocalFile(testCasePath);
- // inject all modules with the harness
- QVector<QUrl> modulesToLoad = { rootModuleUrl };
- while (!modulesToLoad.isEmpty()) {
- QUrl url = modulesToLoad.takeFirst();
- QQmlRefPointer<QV4::ExecutableCompilationUnit> module;
-
- QFile f(url.toLocalFile());
- if (f.open(QIODevice::ReadOnly)) {
- QByteArray content = harnessForModules + f.readAll();
- module = vm.compileModule(url.toString(), QString::fromUtf8(content.constData(), content.size()), QFileInfo(f).lastModified());
- if (vm.hasException)
- break;
- vm.injectCompiledModule(module);
- } else {
- vm.throwError(QStringLiteral("Could not load module"));
- break;
- }
-
- for (const QString &request: module->moduleRequests()) {
- const QUrl absoluteRequest = module->finalUrl().resolved(QUrl(request));
- const auto module = vm.moduleForUrl(absoluteRequest);
- if (module.native == nullptr && module.compiled == nullptr)
- modulesToLoad << absoluteRequest;
- }
- }
-
- if (!vm.hasException) {
- const auto rootModule = vm.loadModule(rootModuleUrl);
- if (rootModule.compiled && rootModule.compiled->instantiate(&vm))
- rootModule.compiled->evaluate();
- }
- } else {
- QV4::ScopedContext ctx(scope, vm.rootContext());
-
- QV4::Script script(ctx, QV4::Compiler::ContextType::Global, testData);
- script.parse();
-
- if (!vm.hasException)
- script.run();
- }
-
- if (vm.hasException) {
- QV4::Scope scope(&vm);
- QV4::ScopedValue val(scope, vm.catchException());
- return TestCase::Result(TestCase::Fails, val->toQString());
- }
- return TestCase::Result(TestCase::Passes);
-}
-
-class SingleTest : public QRunnable
-{
-public:
- SingleTest(Test262Runner *runner, const TestData &data)
- : runner(runner), data(data)
- {
- command = runner->command;
- }
- void run() override;
-
- void runExternalTest();
-
- QString command;
- Test262Runner *runner;
- TestData data;
-};
-
-void SingleTest::run()
-{
- if (!command.isEmpty()) {
- runExternalTest();
- return;
- }
-
- if (data.runInSloppyMode) {
- TestCase::Result ok = ::executeTest(data.content);
- if (data.negative)
- ok.negateResult();
-
- data.sloppyResult = ok;
- } else {
- data.sloppyResult = TestCase::Result(TestCase::Skipped);
- }
- if (data.runInStrictMode) {
- const QString testCasePath = QFileInfo(runner->testDir + "/test/" + data.test).absoluteFilePath();
- QByteArray c = "'use strict';\n" + data.content;
- TestCase::Result ok = ::executeTest(c, data.runAsModuleCode, testCasePath, data.harness);
- if (data.negative)
- ok.negateResult();
-
- data.strictResult = ok;
- } else {
- data.strictResult = TestCase::Result(TestCase::Skipped);
- }
- runner->addResult(data);
-}
-
-void SingleTest::runExternalTest()
-{
- auto runTest = [this] (const char *header, TestCase::Result *result) {
- QTemporaryFile tempFile;
- tempFile.open();
- tempFile.write(header);
- tempFile.write(data.content);
- tempFile.close();
-
- QProcess process;
-// if (flags & Verbose)
-// process.setReadChannelMode(QProcess::ForwardedChannels);
-
- process.start(command, QStringList(tempFile.fileName()));
- if (!process.waitForFinished(-1) || process.error() == QProcess::FailedToStart) {
- qWarning() << "Could not execute" << command;
- *result = TestCase::Result(TestCase::Crashes);
- }
- if (process.exitStatus() != QProcess::NormalExit) {
- *result = TestCase::Result(TestCase::Crashes);
- }
- bool ok = (process.exitCode() == EXIT_SUCCESS);
- if (data.negative)
- ok = !ok;
- *result = ok ? TestCase::Result(TestCase::Passes)
- : TestCase::Result(TestCase::Fails, process.readAllStandardError());
- };
-
- if (data.runInSloppyMode)
- runTest("", &data.sloppyResult);
- if (data.runInStrictMode)
- runTest("'use strict';\n", &data.strictResult);
-
- runner->addResult(data);
-}
-
-int Test262Runner::runSingleTest(TestCase testCase)
-{
- TestData data = getTestData(testCase);
-// qDebug() << "starting test" << data.test;
-
- if (data.isExcluded || data.async)
- return 0;
-
- if (threadPool) {
- SingleTest *test = new SingleTest(this, data);
- threadPool->start(test);
- return 0;
- }
- SingleTest test(this, data);
- test.run();
- return 0;
-}
-
-void Test262Runner::addResult(TestCase result)
-{
- {
- QMutexLocker locker(&mutex);
- Q_ASSERT(result.strictExpectation.state == testCases[result.test].strictExpectation.state);
- Q_ASSERT(result.sloppyExpectation.state == testCases[result.test].sloppyExpectation.state);
- testCases[result.test] = result;
- }
-
- if (!(flags & Verbose))
- return;
-
- QString test = result.test;
- if (result.strictResult.state == TestCase::Skipped) {
- ;
- } else if (result.strictResult.state == TestCase::Crashes) {
- qDebug() << "FAIL:" << test << "crashed in strict mode!";
- } else if (result.strictResult.state == TestCase::Fails
- && result.strictExpectation.state == TestCase::Fails) {
- qCDebug(lcJsTest) << "PASS:" << test << "failed in strict mode as expected";
- } else if ((result.strictResult.state == TestCase::Passes)
- == (result.strictExpectation.state == TestCase::Passes)) {
- qCDebug(lcJsTest) << "PASS:" << test << "passed in strict mode";
- } else if (!(result.strictExpectation.state == TestCase::Fails)) {
- qDebug() << "FAIL:" << test << "failed in strict mode with error message:\n"
- << result.strictResult.errorMessage;
- } else {
- qDebug() << "XPASS:" << test << "unexpectedly passed in strict mode";
- }
-
- if (result.sloppyResult.state == TestCase::Skipped) {
- ;
- } else if (result.sloppyResult.state == TestCase::Crashes) {
- qDebug() << "FAIL:" << test << "crashed in sloppy mode!";
- } else if (result.sloppyResult.state == TestCase::Fails
- && result.sloppyExpectation.state == TestCase::Fails) {
- qCDebug(lcJsTest) << "PASS:" << test << "failed in sloppy mode as expected";
- } else if ((result.sloppyResult.state == TestCase::Passes)
- == (result.sloppyExpectation.state == TestCase::Passes)) {
- qCDebug(lcJsTest) << "PASS:" << test << "passed in sloppy mode";
- } else if (!(result.sloppyExpectation.state == TestCase::Fails)) {
- qDebug() << "FAIL:" << test << "failed in sloppy mode with error message:\n"
- << result.sloppyResult.errorMessage;
- } else {
- qDebug() << "XPASS:" << test << "unexpectedly passed in sloppy mode";
- }
-}
-
-TestData Test262Runner::getTestData(const TestCase &testCase)
-{
- QFile testFile(testDir + "/test/" + testCase.test);
- if (!testFile.open(QFile::ReadOnly)) {
- qWarning() << "wrong test file" << testCase.test;
- exit(1);
- }
- QByteArray content = testFile.readAll();
- content.replace(QByteArrayLiteral("\r\n"), "\n");
-
- qCDebug(lcJsTest) << "parsing test file" << testCase.test;
-
- TestData data(testCase);
- parseYaml(content, &data);
-
- data.harness += harness("assert.js");
- data.harness += harness("sta.js");
-
- for (QByteArray inc : std::as_const(data.includes)) {
- inc = inc.trimmed();
- data.harness += harness(inc);
- }
-
- if (data.async)
- data.harness += harness("doneprintHandle.js");
-
- data.content = data.harness + content;
-
- return data;
-}
-
-struct YamlSection {
- YamlSection(const QByteArray &yaml, const char *sectionName);
-
- bool contains(const char *keyword) const;
- QList<QByteArray> keywords() const;
-
- QByteArray yaml;
- int start = -1;
- int length = 0;
- bool shortSection = false;
-};
-
-YamlSection::YamlSection(const QByteArray &yaml, const char *sectionName)
- : yaml(yaml)
-{
- start = yaml.indexOf(sectionName);
- if (start < 0)
- return;
- start += static_cast<int>(strlen(sectionName));
- int end = yaml.indexOf('\n', start + 1);
- if (end < 0)
- end = yaml.size();
-
- int s = yaml.indexOf('[', start);
- if (s > 0 && s < end) {
- shortSection = true;
- start = s + 1;
- end = yaml.indexOf(']', s);
- } else {
- while (end < yaml.size() - 1 && yaml.at(end + 1) == ' ')
- end = yaml.indexOf('\n', end + 1);
- }
- length = end - start;
-}
-
-bool YamlSection::contains(const char *keyword) const
-{
- if (start < 0)
- return false;
- int idx = yaml.indexOf(keyword, start);
- if (idx >= start && idx < start + length)
- return true;
- return false;
-}
-
-QList<QByteArray> YamlSection::keywords() const
-{
- if (start < 0)
- return QList<QByteArray>();
-
- QByteArray content = yaml.mid(start, length);
- QList<QByteArray> keywords;
- if (shortSection) {
- keywords = content.split(',');
- } else {
- const QList<QByteArray> list = content.split('\n');
- for (const QByteArray &l : list) {
- int i = 0;
- while (i < l.size() && (l.at(i) == ' ' || l.at(i) == '-'))
- ++i;
- QByteArray entry = l.mid(i);
- if (!entry.isEmpty())
- keywords.append(entry);
- }
- }
-// qDebug() << "keywords:" << keywords;
- return keywords;
-}
-
-
-void Test262Runner::parseYaml(const QByteArray &content, TestData *data)
-{
- int start = content.indexOf("/*---");
- if (start < 0)
- return;
- start += sizeof("/*---");
-
- int end = content.indexOf("---*/");
- if (end < 0)
- return;
-
- QByteArray yaml = content.mid(start, end - start);
-
- if (yaml.contains("negative:"))
- data->negative = true;
-
- YamlSection flags(yaml, "flags:");
- data->runInSloppyMode = !flags.contains("onlyStrict");
- data->runInStrictMode = !flags.contains("noStrict") && !flags.contains("raw");
- data->runAsModuleCode = flags.contains("module");
- data->async = flags.contains("async");
-
- if (data->runAsModuleCode) {
- data->runInStrictMode = true;
- data->runInSloppyMode = false;
- }
-
- YamlSection includes(yaml, "includes:");
- data->includes = includes.keywords();
-
- YamlSection features = YamlSection(yaml, "features:");
-
- const char **f = excludedFeatures;
- while (*f) {
- if (features.contains(*f)) {
- data->isExcluded = true;
- break;
- }
- ++f;
- }
-
-// qDebug() << "Yaml:\n" << yaml;
-}
-
-QByteArray Test262Runner::harness(const QByteArray &name)
-{
- if (harnessFiles.contains(name))
- return harnessFiles.value(name);
-
- QFile h(testDir + QLatin1String("/harness/") + name);
- if (!h.open(QFile::ReadOnly)) {
- qWarning() << "Illegal test harness file" << name;
- exit(1);
- }
-
- QByteArray content = h.readAll();
- harnessFiles.insert(name, content);
- return content;
-}
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.h b/tests/auto/qml/ecmascripttests/qjstest/test262runner.h
deleted file mode 100644
index e2bf26296f..0000000000
--- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef TEST262RUNNER_H
-#define TEST262RUNNER_H
-#include <qstring.h>
-#include <qstringlist.h>
-#include <qset.h>
-#include <qmap.h>
-#include <qmutex.h>
-#include <qthreadpool.h>
-
-struct TestCase {
- TestCase() = default;
- TestCase(const QString &test)
- : test(test) {}
-
- enum State { Skipped, Passes, Fails, Crashes };
-
- struct Result
- {
- State state;
- QString errorMessage;
-
- Result(State state, QString errorMessage = "")
- : state(state), errorMessage(errorMessage) { }
-
- void negateResult()
- {
- switch (state) {
- case TestCase::Passes:
- state = TestCase::Fails;
- break;
- case TestCase::Fails:
- state = TestCase::Passes;
- break;
- case TestCase::Skipped:
- case TestCase::Crashes:
- break;
- }
- }
- };
-
- bool skipTestCase = false;
- Result strictExpectation = Result(Passes);
- Result sloppyExpectation = Result(Passes);
- Result strictResult = Result(Skipped);
- Result sloppyResult = Result(Skipped);
-
- QString test;
-};
-
-struct TestData : TestCase {
- TestData(const TestCase &testCase)
- : TestCase(testCase) {}
- // flags
- bool negative = false;
- bool runInStrictMode = true;
- bool runInSloppyMode = true;
- bool runAsModuleCode = false;
- bool async = false;
-
- bool isExcluded = false;
-
- QList<QByteArray> includes;
-
- QByteArray harness;
- QByteArray content;
-};
-
-class Test262Runner
-{
-public:
- Test262Runner(const QString &command, const QString &testDir, const QString &expectationsFile);
- ~Test262Runner();
-
- enum Mode {
- Sloppy = 0,
- Strict = 1
- };
-
- enum Flags {
- Verbose = 0x1,
- Parallel = 0x2,
- ForceBytecode = 0x4,
- ForceJIT = 0x8,
- WithTestExpectations = 0x10,
- UpdateTestExpectations = 0x20,
- WriteTestExpectations = 0x40,
- };
- void setFlags(int f) { flags = f; }
-
- void setFilter(const QString &f) { filter = f; }
-
- void cat();
- bool run();
-
- bool report();
-
-private:
- friend class SingleTest;
- bool loadTests();
- void loadTestExpectations();
- void updateTestExpectations();
- void writeTestExpectations();
- int runSingleTest(TestCase testCase);
-
- TestData getTestData(const TestCase &testCase);
- void parseYaml(const QByteArray &content, TestData *data);
-
- QByteArray harness(const QByteArray &name);
-
- void addResult(TestCase result);
-
- QString command;
- QString testDir;
- QString expectationsFile;
- int flags = 0;
-
- QMutex mutex;
- QString filter;
-
- QMap<QString, TestCase> testCases;
- QHash<QByteArray, QByteArray> harnessFiles;
-
- QThreadPool *threadPool = nullptr;
-};
-
-
-#endif
diff --git a/tests/auto/qml/ecmascripttests/test262.py b/tests/auto/qml/ecmascripttests/test262.py
deleted file mode 100755
index 01c990950c..0000000000
--- a/tests/auto/qml/ecmascripttests/test262.py
+++ /dev/null
@@ -1,611 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2017 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-# Copyright 2009 the Sputnik authors. All rights reserved.
-# This code is governed by the BSD license found in the LICENSE file.
-
-# This is derived from sputnik.py, the Sputnik console test runner,
-# with elements from packager.py, which is separately
-# copyrighted. TODO: Refactor so there is less duplication between
-# test262.py and packager.py.
-
-import sys
-from os import path
-rootDir = path.dirname(path.realpath(__file__))
-sys.path.insert(0, path.abspath(rootDir + "/test262/tools/packaging"))
-
-import logging
-import optparse
-import os
-import platform
-import re
-import subprocess
-import tempfile
-import time
-import xml.dom.minidom
-import datetime
-import shutil
-import json
-import stat
-import multiprocessing
-import signal
-
-
-from parseTestRecord import parseTestRecord, stripHeader
-
-from packagerConfig import *
-
-# excluded features that are still experimental and not part of any official standard
-# see also the features.txt file in test262/
-excludedFeatures = [
- "BigInt",
- "class-fields-public",
- "class-fields-private",
- "Promise.prototype.finally",
- "async-iteration",
- "Symbol.asyncIterator",
- "object-rest",
- "object-spread",
- "optional-catch-binding",
- "regexp-dotall",
- "regexp-lookbehind",
- "regexp-named-groups",
- "regexp-unicode-property-escapes",
- "Atomics",
- "SharedArrayBuffer",
- "Array.prototype.flatten",
- "Array.prototype.flatMap",
- "string-trimming",
- "String.prototype.trimEnd",
- "String.prototype.trimStart",
- "numeric-separator-literal",
-
- # optional features, not supported by us
- "caller"
-]
-
-# ############# Helpers needed for parallel multi-process test execution ############
-
-def runTest(case, args):
- return case.Run(args)
-
-def runTestVarArgs(args):
- return runTest(*args)
-
-def initWorkerProcess():
- signal.signal(signal.SIGINT, signal.SIG_IGN)
-
-# #############
-
-class Test262Error(Exception):
- def __init__(self, message):
- self.message = message
-
-def ReportError(s):
- raise Test262Error(s)
-
-
-class TestExpectations:
- def __init__(self, enabled):
- self.testsToSkip = []
- self.failingTests = []
- f = open(rootDir + "/TestExpectations")
- if not enabled:
- return
- for line in f.read().splitlines():
- line = line.strip()
- if len(line) == 0 or line[0] == "#":
- continue
- record = line.split()
- if len(record) == 1:
- self.failingTests.append(record[0])
- else:
- test = record[0]
- expectation = record[1]
- if expectation == "skip":
- self.testsToSkip.append(test)
- f.close()
-
- def update(self, progress):
- unexpectedPasses = [c.case.name for c in progress.failed_tests if c.case.IsNegative()]
-
- # If a test fails that we expected to fail, then it actually passed unexpectedly.
- failures = [c.case.name for c in progress.failed_tests if not c.case.IsNegative()]
- for failure in failures:
- if failure in self.failingTests:
- unexpectedPasses.append(failure)
-
- f = open(rootDir + "/TestExpectations")
- lines = f.read().splitlines()
- oldLen = len(lines)
- for result in unexpectedPasses:
- expectationLine = result
- try:
- lines.remove(expectationLine)
- except ValueError:
- pass
-
- f.close()
- if len(lines) != oldLen:
- f = open(rootDir + "/TestExpectations", "w")
- f.write("\n".join(lines))
- f.close()
- print "Changes to TestExpectations written!"
-
-
-if not os.path.exists(EXCLUDED_FILENAME):
- print "Cannot generate (JSON) test262 tests without a file," + \
- " %s, showing which tests have been disabled!" % EXCLUDED_FILENAME
- sys.exit(1)
-EXCLUDE_LIST = xml.dom.minidom.parse(EXCLUDED_FILENAME)
-EXCLUDE_LIST = EXCLUDE_LIST.getElementsByTagName("test")
-EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST]
-
-
-def BuildOptions():
- result = optparse.OptionParser()
- result.add_option("--command", default="qmljs", help="The command-line to run")
- result.add_option("--tests", default=path.abspath(rootDir + '/test262'),
- help="Path to the tests")
- result.add_option("--cat", default=False, action="store_true",
- help="Print packaged test code that would be run")
- result.add_option("--summary", default=True, action="store_true",
- help="Print summary after running tests")
- result.add_option("--full-summary", default=False, action="store_true",
- help="Print summary and test output after running tests")
- result.add_option("--strict_only", default=False, action="store_true",
- help="Test only strict mode")
- result.add_option("--non_strict_only", default=False, action="store_true",
- help="Test only non-strict mode")
- result.add_option("--parallel", default=False, action="store_true",
- help="Run tests in parallel")
- result.add_option("--with-test-expectations", default=False, action="store_true",
- help="Parse TestExpectations to deal with tests known to fail")
- result.add_option("--update-expectations", default=False, action="store_true",
- help="Update test expectations fail when a test passes that was expected to fail")
- # TODO: Once enough tests are made strict compat, change the default
- # to "both"
- result.add_option("--unmarked_default", default="non_strict",
- help="default mode for tests of unspecified strictness")
- return result
-
-
-def ValidateOptions(options):
- if not options.command:
- ReportError("A --command must be specified.")
- if not path.exists(options.tests):
- ReportError("Couldn't find test path '%s'" % options.tests)
-
-
-placeHolderPattern = re.compile(r"\{\{(\w+)\}\}")
-
-
-def IsWindows():
- p = platform.system()
- return (p == 'Windows') or (p == 'Microsoft')
-
-
-class TempFile(object):
-
- def __init__(self, suffix="", prefix="tmp", text=False):
- self.suffix = suffix
- self.prefix = prefix
- self.text = text
- self.fd = None
- self.name = None
- self.is_closed = False
- self.Open()
-
- def Open(self):
- (self.fd, self.name) = tempfile.mkstemp(
- suffix = self.suffix,
- prefix = self.prefix,
- text = self.text)
-
- def Write(self, str):
- os.write(self.fd, str)
-
- def Read(self):
- f = file(self.name)
- result = f.read()
- f.close()
- return result
-
- def Close(self):
- if not self.is_closed:
- self.is_closed = True
- os.close(self.fd)
-
- def Dispose(self):
- try:
- self.Close()
- os.unlink(self.name)
- except OSError, e:
- logging.error("Error disposing temp file: %s", str(e))
-
-
-class TestResult(object):
-
- def __init__(self, exit_code, stdout, stderr, case):
- self.exit_code = exit_code
- self.stdout = stdout
- self.stderr = stderr
- self.case = case
-
- def ReportOutcome(self, long_format):
- name = self.case.GetName()
- mode = self.case.GetMode()
- if self.HasUnexpectedOutcome():
- if self.case.IsNegative():
- print "=== %s was expected to fail in %s, but didn't ===" % (name, mode)
- else:
- if long_format:
- print "=== %s failed in %s ===" % (name, mode)
- else:
- print "%s in %s: " % (name, mode)
- out = self.stdout.strip()
- if len(out) > 0:
- print "--- output ---"
- print out
- err = self.stderr.strip()
- if len(err) > 0:
- print "--- errors ---"
- print err
- if long_format:
- print "==="
- elif self.case.IsNegative():
- print "%s failed in %s as expected" % (name, mode)
- else:
- print "%s passed in %s" % (name, mode)
-
- def HasFailed(self):
- return self.exit_code != 0
-
- def HasUnexpectedOutcome(self):
- if self.case.IsNegative():
- return not self.HasFailed()
- else:
- return self.HasFailed()
-
-
-class TestCase(object):
-
- def __init__(self, suite, name, full_path, strict_mode):
- self.suite = suite
- self.name = name
- self.full_path = full_path
- self.strict_mode = strict_mode
- f = open(self.full_path)
- self.contents = f.read()
- f.close()
- testRecord = parseTestRecord(self.contents, name)
- self.test = testRecord["test"]
- if 'features' in testRecord:
- self.features = testRecord["features"];
- else:
- self.features = []
- del testRecord["test"]
- del testRecord["header"]
- self.testRecord = testRecord;
-
-
- def GetName(self):
- return self.name
-
- def GetMode(self):
- if self.strict_mode:
- return "strict mode"
- else:
- return "non-strict mode"
-
- def GetPath(self):
- return self.name
-
- def NegateResult(self):
- if self.IsNegative():
- del self.testRecord['negative']
- else:
- self.testRecord['negative'] = "Some failure";
-
- def IsNegative(self):
- return 'negative' in self.testRecord
-
- def IsOnlyStrict(self):
- return 'onlyStrict' in self.testRecord
-
- def IsNoStrict(self):
- return 'noStrict' in self.testRecord
-
- def IsExperimental(self):
- for f in self.features:
- if excludedFeatures.count(f) >= 1:
- return True;
- return False
-
- def GetSource(self):
- # "var testDescrip = " + str(self.testRecord) + ';\n\n' + \
- source = self.suite.GetInclude("assert.js") + \
- self.suite.GetInclude("sta.js") + \
- self.test + '\n'
- if 'includes' in self.testRecord:
- for inc in self.testRecord['includes']:
- source += self.suite.GetInclude(inc);
-
- if self.strict_mode:
- source = '"use strict";\nvar strict_mode = true;\n' + source
- else:
- source = "var strict_mode = false; \n" + source
- return source
-
- def InstantiateTemplate(self, template, params):
- def GetParameter(match):
- key = match.group(1)
- return params.get(key, match.group(0))
- return placeHolderPattern.sub(GetParameter, template)
-
- def Execute(self, command):
- if IsWindows():
- args = '%s' % command
- else:
- args = command.split(" ")
- stdout = TempFile(prefix="test262-out-")
- stderr = TempFile(prefix="test262-err-")
- try:
- logging.info("exec: %s", str(args))
- process = subprocess.Popen(
- args,
- shell = IsWindows(),
- stdout = stdout.fd,
- stderr = stderr.fd
- )
- code = process.wait()
- out = stdout.Read()
- err = stderr.Read()
- finally:
- stdout.Dispose()
- stderr.Dispose()
- return (code, out, err)
-
- def RunTestIn(self, command_template, tmp):
- tmp.Write(self.GetSource())
- tmp.Close()
- command = self.InstantiateTemplate(command_template, {
- 'path': tmp.name
- })
- (code, out, err) = self.Execute(command)
- return TestResult(code, out, err, self)
-
- def Run(self, command_template):
- tmp = TempFile(suffix=".js", prefix="test262-", text=True)
- try:
- result = self.RunTestIn(command_template, tmp)
- finally:
- tmp.Dispose()
- return result
-
- def Print(self):
- print self.GetSource()
-
-
-class ProgressIndicator(object):
-
- def __init__(self, count):
- self.count = count
- self.succeeded = 0
- self.failed = 0
- self.failed_tests = []
-
- def HasRun(self, result):
- result.ReportOutcome(True)
- if result.HasUnexpectedOutcome():
- self.failed += 1
- self.failed_tests.append(result)
- else:
- self.succeeded += 1
-
-
-def MakePlural(n):
- if (n == 1):
- return (n, "")
- else:
- return (n, "s")
-
-
-class TestSuite(object):
-
- def __init__(self, root, strict_only, non_strict_only, unmarked_default, load_expectations):
- # TODO: derive from packagerConfig.py
- self.test_root = path.join(root, 'test')
- self.lib_root = path.join(root, 'harness')
- self.strict_only = strict_only
- self.non_strict_only = non_strict_only
- self.unmarked_default = unmarked_default
- self.include_cache = { }
- self.expectations = TestExpectations(load_expectations)
-
- def IsExcludedTest(self, path):
- if path.startswith('annexB'):
- return True;
- if path.startswith('harness'):
- return True;
- if path.startswith('intl402'):
- return True;
- return False;
-
- def Validate(self):
- if not path.exists(self.test_root):
- ReportError("No test repository found")
- if not path.exists(self.lib_root):
- ReportError("No test library found")
-
- def IsHidden(self, path):
- return path.startswith('.') or path == 'CVS'
-
- def IsTestCase(self, path):
- return path.endswith('.js')
-
- def ShouldRun(self, rel_path, tests):
- if len(tests) == 0:
- return True
- for test in tests:
- if test in rel_path:
- return True
- return False
-
- def GetInclude(self, name):
- if not name in self.include_cache:
- static = path.join(self.lib_root, name)
- if path.exists(static):
- f = open(static)
- contents = stripHeader(f.read())
- contents = re.sub(r'\r\n', '\n', contents)
- self.include_cache[name] = contents + "\n"
- f.close()
- else:
- ReportError("Can't find: " + static)
- return self.include_cache[name]
-
- def EnumerateTests(self, tests):
- logging.info("Listing tests in %s", self.test_root)
- cases = []
- for root, dirs, files in os.walk(self.test_root):
- for f in [x for x in dirs if self.IsHidden(x)]:
- dirs.remove(f)
- dirs.sort()
- for f in sorted(files):
- if self.IsTestCase(f):
- full_path = path.join(root, f)
- if full_path.startswith(self.test_root):
- rel_path = full_path[len(self.test_root)+1:]
- else:
- logging.warning("Unexpected path %s", full_path)
- rel_path = full_path
- if self.ShouldRun(rel_path, tests) and not self.IsExcludedTest(rel_path):
- basename = path.basename(full_path)[:-3]
- name = rel_path.replace('.js', '')
- if EXCLUDE_LIST.count(basename) >= 1 or self.expectations.testsToSkip.count(name) >= 1:
- print 'Excluded: ' + rel_path
- else:
- if not self.non_strict_only:
- strict_case = TestCase(self, name, full_path, True)
- if self.expectations.failingTests.count(name) >= 1:
- strict_case.NegateResult()
- if not strict_case.IsNoStrict() and not strict_case.IsExperimental():
- if strict_case.IsOnlyStrict() or \
- self.unmarked_default in ['both', 'strict']:
- cases.append(strict_case)
- if not self.strict_only:
- non_strict_case = TestCase(self, name, full_path, False)
- if self.expectations.failingTests.count(name) >= 1:
- non_strict_case.NegateResult()
- if not non_strict_case.IsOnlyStrict() and not non_strict_case.IsExperimental():
- if non_strict_case.IsNoStrict() or \
- self.unmarked_default in ['both', 'non_strict']:
- cases.append(non_strict_case)
- logging.info("Done listing tests")
- return cases
-
- def PrintSummary(self, progress):
- print
- print "=== Summary ==="
- count = progress.count
- succeeded = progress.succeeded
- failed = progress.failed
- print " - Ran %i test%s" % MakePlural(count)
- if progress.failed == 0:
- print " - All tests succeeded"
- else:
- percent = ((100.0 * succeeded) / count,)
- print " - Passed %i test%s (%.1f%%)" % (MakePlural(succeeded) + percent)
- percent = ((100.0 * failed) / count,)
- print " - Failed %i test%s (%.1f%%)" % (MakePlural(failed) + percent)
- positive = [c for c in progress.failed_tests if not c.case.IsNegative()]
- negative = [c for c in progress.failed_tests if c.case.IsNegative()]
- if len(positive) > 0:
- print
- print "Failed tests"
- for result in positive:
- print " %s in %s" % (result.case.GetName(), result.case.GetMode())
- if len(negative) > 0:
- print
- print "Expected to fail but passed ---"
- for result in negative:
- print " %s in %s" % (result.case.GetName(), result.case.GetMode())
-
- def PrintFailureOutput(self, progress):
- for result in progress.failed_tests:
- print
- result.ReportOutcome(False)
-
- def Run(self, command_template, tests, print_summary, full_summary, parallel, update_expectations):
- if not "{{path}}" in command_template:
- command_template += " {{path}}"
- cases = self.EnumerateTests(tests)
- if len(cases) == 0:
- ReportError("No tests to run")
- progress = ProgressIndicator(len(cases))
-
- if parallel:
- pool = multiprocessing.Pool(processes=multiprocessing.cpu_count(), initializer=initWorkerProcess)
- results = pool.imap_unordered(func=runTestVarArgs, iterable=[(case, command_template) for case in cases], chunksize=multiprocessing.cpu_count() * 8)
- for result in results:
- progress.HasRun(result)
- else:
- for case in cases:
- result = case.Run(command_template)
- progress.HasRun(result)
- if print_summary:
- self.PrintSummary(progress)
- if full_summary:
- self.PrintFailureOutput(progress)
- else:
- print
- print "Use --full-summary to see output from failed tests"
- print
- if update_expectations:
- self.expectations.update(progress)
- return progress.failed == 0
-
- def Print(self, tests):
- cases = self.EnumerateTests(tests)
- if len(cases) > 0:
- cases[0].Print()
-
-
-def Main():
- # Uncomment the next line for more logging info.
- #logging.basicConfig(level=logging.DEBUG)
- # Some date tests rely on being run in pacific time and the USA's locale:
- os.environ["TZ"] = "America/Los_Angeles" # it *matters* that this is (7m8s) *East* of PST's nominal meridian !
- os.environ["LANG"] = "en_US.UTF-8"
- os.environ["LC_TIME"] = "en_US.UTF-8"
- parser = BuildOptions()
- (options, args) = parser.parse_args()
- ValidateOptions(options)
- test_suite = TestSuite(options.tests,
- options.strict_only,
- options.non_strict_only,
- options.unmarked_default,
- options.with_test_expectations)
- test_suite.Validate()
- if options.cat:
- test_suite.Print(args)
- return 0
- else:
- if test_suite.Run(options.command, args,
- options.summary or options.full_summary,
- options.full_summary,
- options.parallel,
- options.update_expectations):
- return 0
- else:
- return 1
-
-
-if __name__ == '__main__':
- try:
- sys.exit(Main())
- except Test262Error, e:
- print "Error: %s" % e.message
- sys.exit(1)
diff --git a/tests/auto/qml/ecmascripttests/test262runner.cpp b/tests/auto/qml/ecmascripttests/test262runner.cpp
new file mode 100644
index 0000000000..d87a8a9552
--- /dev/null
+++ b/tests/auto/qml/ecmascripttests/test262runner.cpp
@@ -0,0 +1,1010 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "test262runner.h"
+
+#include <qdebug.h>
+#include <qdir.h>
+#include <qdiriterator.h>
+#include <qfile.h>
+#include <qjsondocument.h>
+#include <qjsonobject.h>
+#include <qlibraryinfo.h>
+#include <qprocess.h>
+#include <qtemporaryfile.h>
+#include <qthread.h>
+
+#include "private/qqmlbuiltinfunctions_p.h"
+#include "private/qv4arraybuffer_p.h"
+#include "private/qv4globalobject_p.h"
+#include <QtCore/QLoggingCategory>
+#include <private/qv4script_p.h>
+
+using namespace Qt::StringLiterals;
+
+static const char *excludedFeatures[] = {
+ "BigInt",
+ "class-fields-public",
+ "class-fields-private",
+ "Promise.prototype.finally",
+ "async-iteration",
+ "Symbol.asyncIterator",
+ "object-rest",
+ "object-spread",
+ "optional-catch-binding",
+ "regexp-dotall",
+ "regexp-lookbehind",
+ "regexp-named-groups",
+ "regexp-unicode-property-escapes",
+ "Atomics",
+ "SharedArrayBuffer",
+ "Array.prototype.flatten",
+ "Array.prototype.flatMap",
+ "string-trimming",
+ "String.prototype.trimEnd",
+ "String.prototype.trimStart",
+ "numeric-separator-literal",
+
+ // optional features, not supported by us
+ "caller",
+ nullptr
+};
+
+static const char *excludedFilePatterns[] = {
+ "realm",
+ nullptr
+};
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+static ReturnedValue method_detachArrayBuffer(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+ Scoped<ArrayBuffer> a(scope, argv[0]);
+ if (!a)
+ return scope.engine->throwTypeError();
+
+ if (a->hasSharedArrayData())
+ return scope.engine->throwTypeError();
+
+ a->d()->detachArrayData();
+
+ return Encode::null();
+}
+
+void initD262(ExecutionEngine *e)
+{
+ Scope scope(e);
+ ScopedObject d262(scope, e->newObject());
+
+ d262->defineDefaultProperty(QStringLiteral("detachArrayBuffer"), method_detachArrayBuffer, 1);
+
+ ScopedString s(scope, e->newString(QStringLiteral("$262")));
+ e->globalObject->put(s, d262);
+}
+
+}
+
+
+Q_DECLARE_LOGGING_CATEGORY(lcJsTest);
+Q_LOGGING_CATEGORY(lcJsTest, "qt.v4.ecma262.tests", QtWarningMsg);
+
+Test262Runner::Test262Runner(const QString &command, const QString &dir, const QString &expectationsFile)
+ : command(command), testDir(dir), expectationsFile(expectationsFile)
+{
+ if (testDir.endsWith(QLatin1Char('/')))
+ testDir = testDir.chopped(1);
+}
+
+Test262Runner::~Test262Runner()
+{
+ if (threadPool)
+ delete threadPool;
+}
+
+void Test262Runner::cat()
+{
+ if (!loadTests())
+ return;
+
+ if (testCases.size() != 1)
+ qWarning() << "test262 --cat: Ambiguous test case, using" << testCases.begin().key();
+ TestData data = getTestData(testCases.begin().value());
+ printf("%s", data.content.constData());
+}
+
+void Test262Runner::assignTaskOrTerminate(int processIndex)
+{
+ if (tasks.isEmpty()) {
+ sendDone(processIndex);
+ return;
+ }
+
+ currentTasks[processIndex] = tasks.dequeue();
+ TestData &task = currentTasks[processIndex];
+
+ // Sloppy run + maybe strict run later
+ if (task.runInSloppyMode) {
+ if (task.runInStrictMode)
+ task.stillNeedStrictRun = true;
+ assignSloppy(processIndex);
+ return;
+ }
+
+ // Only strict run
+ if (task.runInStrictMode) {
+ assignStrict(processIndex);
+ return;
+ }
+
+ // TODO: Start a timer for timeouts?
+}
+
+void Test262Runner::assignSloppy(int processIndex)
+{
+ QProcess &p = *processes[processIndex];
+ TestData &task = currentTasks[processIndex];
+
+ QJsonObject json;
+ json.insert("mode", "sloppy");
+ json.insert("testData", QString::fromUtf8(task.content));
+ json.insert("runAsModule", false);
+ json.insert("testCasePath", "");
+ json.insert("harnessForModules", "");
+ p.write(QJsonDocument(json).toJson(QJsonDocument::Compact));
+ p.write("\r\n");
+}
+
+void Test262Runner::assignStrict(int processIndex)
+{
+ QProcess &p = *processes[processIndex];
+ TestData &task = currentTasks[processIndex];
+
+ QJsonObject json;
+ json.insert("mode", "strict");
+ QString strictContent = "'use strict';\n" + QString::fromUtf8(task.content);
+ json.insert("testData", strictContent);
+ json.insert("runAsModule", task.runAsModuleCode);
+ json.insert("testCasePath", QFileInfo(testDir + "/test/" + task.test).absoluteFilePath());
+ json.insert("harnessForModules", QString::fromUtf8(task.harness));
+ p.write(QJsonDocument(json).toJson(QJsonDocument::Compact));
+ p.write("\r\n");
+}
+
+void Test262Runner::sendDone(int processIndex)
+{
+ QProcess &p = *processes[processIndex];
+
+ QJsonObject json;
+ json.insert("done", true);
+ p.write(QJsonDocument(json).toJson(QJsonDocument::Compact));
+ p.write("\r\n");
+}
+
+void Test262Runner::createProcesses()
+{
+ const int processCount = QThread::idealThreadCount();
+ qDebug() << "Running in parallel with" << processCount << "processes";
+ for (int i = 0; i < processCount; ++i) {
+ processes.emplace_back(std::make_unique<QProcess>());
+ QProcess &p = *processes[i];
+ QProcess::connect(&p, &QProcess::started, this, [&, i]() {
+ assignTaskOrTerminate(i);
+ });
+
+ QProcess::connect(&p, &QIODevice::readyRead, this, [&, i]() {
+ QProcess &p = *processes[i];
+ QString output;
+ while (output.isEmpty())
+ output = p.readLine();
+ QJsonDocument response = QJsonDocument::fromJson(output.toUtf8());
+
+ TestData &testData(currentTasks[i]);
+ auto mode = response["mode"].toString();
+ auto state = TestCase::State(response["resultState"].toInt(int(TestCase::State::Fails)));
+ auto errorMessage = response["resultErrorMessage"].toString();
+
+ auto &result = mode == "strict" ? testData.strictResult : testData.sloppyResult;
+ result = TestCase::Result(state, errorMessage);
+ if (testData.negative)
+ result.negateResult();
+
+ if (testData.stillNeedStrictRun) {
+ testData.stillNeedStrictRun = false;
+ assignStrict(i);
+ } else {
+ addResult(testData);
+ assignTaskOrTerminate(i);
+ }
+ });
+
+ QObject::connect(&p, &QProcess::finished, this,
+ [this, processCount, i](int exitCode, QProcess::ExitStatus status) {
+ if (status != QProcess::NormalExit || exitCode != 0) {
+ TestData &testData(currentTasks[i]);
+
+ auto &result = testData.stillNeedStrictRun
+ ? testData.sloppyResult
+ : testData.strictResult;
+ result = TestCase::Result(
+ TestCase::Crashes,
+ QStringLiteral("Process %1 of %2 exited with a non-normal status")
+ .arg(i).arg(processCount - 1));
+
+ addResult(testData);
+ }
+
+ --runningCount;
+ if (runningCount == 0)
+ loop.exit();
+ });
+
+ p.setProgram(QCoreApplication::applicationFilePath());
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert(u"runnerProcess"_s, u"1"_s);
+ p.setProcessEnvironment(env);
+ ++runningCount;
+ p.start();
+ }
+}
+
+class SingleTest : public QRunnable
+{
+public:
+ SingleTest(Test262Runner *runner, const TestData &data)
+ : runner(runner), data(data)
+ {}
+ void run() override;
+
+ Test262Runner *runner;
+ TestData data;
+};
+
+TestCase::Result getTestExecutionResult(QV4::ExecutionEngine &vm)
+{
+ TestCase::State state;
+ QString errorMessage;
+ if (vm.hasException) {
+ state = TestCase::State::Fails;
+ QV4::Scope scope(&vm);
+ QV4::ScopedValue val(scope, vm.catchException());
+ errorMessage = val->toQString();
+ } else {
+ state = TestCase::State::Passes;
+ }
+ return TestCase::Result(state, errorMessage);
+}
+
+void SingleTest::run()
+{
+ if (data.runInSloppyMode) {
+ QV4::ExecutionEngine vm;
+ Test262Runner::executeTest(vm, data.content);
+ TestCase::Result ok = getTestExecutionResult(vm);
+
+ if (data.negative)
+ ok.negateResult();
+
+ data.sloppyResult = ok;
+ } else {
+ data.sloppyResult = TestCase::Result(TestCase::Skipped);
+ }
+ if (data.runInStrictMode) {
+ QString testCasePath = QFileInfo(runner->testDirectory() + "/test/" + data.test).absoluteFilePath();
+ QByteArray c = "'use strict';\n" + data.content;
+
+ QV4::ExecutionEngine vm;
+ Test262Runner::executeTest(vm, c, testCasePath, data.harness, data.runAsModuleCode);
+ TestCase::Result ok = getTestExecutionResult(vm);
+
+ if (data.negative)
+ ok.negateResult();
+
+ data.strictResult = ok;
+ } else {
+ data.strictResult = TestCase::Result(TestCase::Skipped);
+ }
+ runner->addResult(data);
+}
+
+void Test262Runner::executeTest(QV4::ExecutionEngine &vm, const QString &testData,
+ const QString &testCasePath, const QString &harnessForModules,
+ bool runAsModule)
+{
+ QV4::Scope scope(&vm);
+ QV4::GlobalExtensions::init(vm.globalObject,
+ QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension);
+ QV4::initD262(&vm);
+
+ if (runAsModule) {
+ const QUrl rootModuleUrl = QUrl::fromLocalFile(testCasePath);
+ // inject all modules with the harness
+ QVector<QUrl> modulesToLoad = { rootModuleUrl };
+ while (!modulesToLoad.isEmpty()) {
+ QUrl url = modulesToLoad.takeFirst();
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> module;
+
+ QFile f(url.toLocalFile());
+ if (f.open(QIODevice::ReadOnly)) {
+ QByteArray content = harnessForModules.toLocal8Bit() + f.readAll();
+ module = vm.compileModule(url.toString(),
+ QString::fromUtf8(content.constData(),content.size()),
+ QFileInfo(f).lastModified());
+ if (vm.hasException)
+ break;
+ } else {
+ vm.throwError(QStringLiteral("Could not load module"));
+ break;
+ }
+
+ const QStringList moduleRequests = module->baseCompilationUnit()->moduleRequests();
+ for (const QString &request: moduleRequests) {
+ const QUrl absoluteRequest = module->finalUrl().resolved(QUrl(request));
+ const auto module = vm.moduleForUrl(absoluteRequest);
+ if (module.native == nullptr && module.compiled == nullptr)
+ modulesToLoad << absoluteRequest;
+ }
+ }
+
+ if (!vm.hasException) {
+ const auto rootModule = vm.loadModule(rootModuleUrl);
+ if (rootModule.compiled && rootModule.compiled->instantiate())
+ rootModule.compiled->evaluate();
+ }
+ } else {
+ QV4::ScopedContext ctx(scope, vm.rootContext());
+
+ QV4::Script script(ctx, QV4::Compiler::ContextType::Global, testData);
+ script.parse();
+
+ if (!vm.hasException)
+ script.run();
+ }
+}
+
+void Test262Runner::runWithThreadPool()
+{
+ threadPool = new QThreadPool();
+ threadPool->setStackSize(16*1024*1024);
+ qDebug() << "Running in parallel with" << QThread::idealThreadCount() << "threads";
+
+ for (const TestCase &testCase : std::as_const(testCases)) {
+ TestData testData = getTestData(testCase);
+ if (testData.isExcluded || testData.async)
+ continue;
+ SingleTest *test = new SingleTest(this, testData);
+ threadPool->start(test);
+ }
+
+ while (!threadPool->waitForDone(10'000)) {
+ if (lcJsTest().isEnabled(QtDebugMsg)) {
+ // heartbeat, only needed when there is no other debug output
+ qDebug("test262: in progress...");
+ }
+ }
+}
+
+bool Test262Runner::run()
+{
+ if (!loadTests())
+ return false;
+
+ if (flags & ForceJIT)
+ qputenv("QV4_JIT_CALL_THRESHOLD", QByteArray("0"));
+ else if (flags & ForceBytecode)
+ qputenv("QV4_FORCE_INTERPRETER", QByteArray("1"));
+
+ if (flags & WithTestExpectations)
+ loadTestExpectations();
+
+ for (auto it = testCases.constBegin(); it != testCases.constEnd(); ++it) {
+ auto c = it.value();
+ if (!c.skipTestCase) {
+ TestData data = getTestData(c);
+ if (data.isExcluded || data.async)
+ continue;
+
+ tasks.append(data);
+ }
+ }
+
+ if (command.isEmpty()) {
+#if QT_CONFIG(process)
+ createProcesses();
+ loop.exec();
+#else
+ runWithThreadPool();
+#endif
+ } else {
+ runAsExternalTests();
+ }
+
+ const bool testsOk = report();
+
+ if (flags & WriteTestExpectations)
+ writeTestExpectations();
+ else if (flags & UpdateTestExpectations)
+ updateTestExpectations();
+
+ return testsOk;
+}
+
+bool Test262Runner::report()
+{
+ qDebug() << "Test execution summary:";
+ qDebug() << " Executed" << testCases.size() << "test cases.";
+ QStringList crashes;
+ QStringList unexpectedFailures;
+ QStringList unexpectedPasses;
+ for (auto it = testCases.constBegin(); it != testCases.constEnd(); ++it) {
+ const auto c = it.value();
+ if (c.strictResult.state == c.strictExpectation.state
+ && c.sloppyResult.state == c.sloppyExpectation.state)
+ continue;
+ auto report = [&](const TestCase::Result &expected, const TestCase::Result &result, const char *s) {
+ if (result.state == TestCase::Crashes)
+ crashes << (it.key() + " crashed in " + s + " mode");
+ if (result.state == TestCase::Fails && expected.state == TestCase::Passes)
+ unexpectedFailures << (it.key() + " failed in " + s
+ + " mode with error message: " + result.errorMessage);
+ if (result.state == TestCase::Passes && expected.state == TestCase::Fails)
+ unexpectedPasses << (it.key() + " unexpectedly passed in " + s + " mode");
+ };
+ report(c.strictExpectation, c.strictResult, "strict");
+ report(c.sloppyExpectation, c.sloppyResult, "sloppy");
+ }
+ if (!crashes.isEmpty()) {
+ qDebug() << " Encountered" << crashes.size() << "crashes in the following files:";
+ for (const QString &f : std::as_const(crashes))
+ qDebug() << " " << f;
+ }
+ if (!unexpectedFailures.isEmpty()) {
+ qDebug() << " Encountered" << unexpectedFailures.size() << "unexpected failures in the following files:";
+ for (const QString &f : std::as_const(unexpectedFailures))
+ qDebug() << " " << f;
+ }
+ if (!unexpectedPasses.isEmpty()) {
+ qDebug() << " Encountered" << unexpectedPasses.size() << "unexpected passes in the following files:";
+ for (const QString &f : std::as_const(unexpectedPasses))
+ qDebug() << " " << f;
+ }
+ return crashes.isEmpty() && unexpectedFailures.isEmpty() && unexpectedPasses.isEmpty();
+}
+
+bool Test262Runner::loadTests()
+{
+ QDir dir(testDir + "/test");
+ if (!dir.exists()) {
+ qWarning() << "Could not load tests," << dir.path() << "does not exist.";
+ return false;
+ }
+
+ QString annexB = "annexB";
+ QString harness = "harness";
+ QString intl402 = "intl402";
+
+ int pathlen = dir.path().size() + 1;
+ QDirIterator it(dir, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QString file = it.next().mid(pathlen);
+ if (!file.endsWith(".js"))
+ continue;
+ if (file.endsWith("_FIXTURE.js"))
+ continue;
+ if (!filter.isEmpty() && !file.contains(filter))
+ continue;
+ if (file.startsWith(annexB) || file.startsWith(harness) || file.startsWith(intl402))
+ continue;
+ const char **excluded = excludedFilePatterns;
+ bool skip = false;
+ while (*excluded) {
+ if (file.contains(QLatin1String(*excluded)))
+ skip = true;
+ ++excluded;
+ }
+ if (skip)
+ continue;
+
+ testCases.insert(file, TestCase{ file });
+ }
+ if (testCases.isEmpty()) {
+ qWarning() << "No tests to run.";
+ return false;
+ }
+
+ return true;
+}
+
+
+struct TestExpectationLine {
+ TestExpectationLine(const QByteArray &line);
+ enum State {
+ Fails,
+ SloppyFails,
+ StrictFails,
+ Skip,
+ Passes
+ } state;
+ QString testCase;
+
+ QByteArray toLine() const;
+ void update(const TestCase &testCase);
+
+ static TestExpectationLine fromTestCase(const TestCase &testCase);
+private:
+ TestExpectationLine() = default;
+ static State stateFromTestCase(const TestCase &testCase);
+};
+
+TestExpectationLine::TestExpectationLine(const QByteArray &line)
+{
+ int space = line.indexOf(' ');
+
+ testCase = QString::fromUtf8(space > 0 ? line.left(space) : line);
+ if (!testCase.endsWith(".js"))
+ testCase += ".js";
+
+ state = Fails;
+ if (space < 0)
+ return;
+ QByteArray qualifier = line.mid(space + 1);
+ if (qualifier == "skip")
+ state = Skip;
+ else if (qualifier == "strictFails")
+ state = StrictFails;
+ else if (qualifier == "sloppyFails")
+ state = SloppyFails;
+ else if (qualifier == "fails")
+ state = Fails;
+ else
+ qWarning() << "illegal format in TestExpectations, line" << line;
+}
+
+QByteArray TestExpectationLine::toLine() const {
+ const char *res = nullptr;
+ switch (state) {
+ case Fails:
+ res = " fails\n";
+ break;
+ case SloppyFails:
+ res = " sloppyFails\n";
+ break;
+ case StrictFails:
+ res = " strictFails\n";
+ break;
+ case Skip:
+ res = " skip\n";
+ break;
+ case Passes:
+ // no need for an entry
+ return QByteArray();
+ }
+ QByteArray result = testCase.toUtf8() + res;
+ return result;
+}
+
+void TestExpectationLine::update(const TestCase &testCase)
+{
+ Q_ASSERT(testCase.test == this->testCase);
+
+ State resultState = stateFromTestCase(testCase);
+ switch (resultState) {
+ case Fails:
+ // no improvement, don't update
+ break;
+ case SloppyFails:
+ if (state == Fails)
+ state = SloppyFails;
+ else if (state == StrictFails)
+ // we have a regression in sloppy mode, but strict now passes
+ state = Passes;
+ break;
+ case StrictFails:
+ if (state == Fails)
+ state = StrictFails;
+ else if (state == SloppyFails)
+ // we have a regression in strict mode, but sloppy now passes
+ state = Passes;
+ break;
+ case Skip:
+ Q_ASSERT(state == Skip);
+ // nothing to do
+ break;
+ case Passes:
+ state = Passes;
+ }
+}
+
+TestExpectationLine TestExpectationLine::fromTestCase(const TestCase &testCase)
+{
+ TestExpectationLine l;
+ l.testCase = testCase.test;
+ l.state = stateFromTestCase(testCase);
+ return l;
+}
+
+TestExpectationLine::State TestExpectationLine::stateFromTestCase(const TestCase &testCase)
+{
+ // keep skipped tests
+ if (testCase.skipTestCase)
+ return Skip;
+
+ bool strictFails = (testCase.strictResult.state == TestCase::Crashes
+ || testCase.strictResult.state == TestCase::Fails);
+ bool sloppyFails = (testCase.sloppyResult.state == TestCase::Crashes
+ || testCase.sloppyResult.state == TestCase::Fails);
+ if (strictFails && sloppyFails)
+ return Fails;
+ if (strictFails)
+ return StrictFails;
+ if (sloppyFails)
+ return SloppyFails;
+ return Passes;
+}
+
+
+void Test262Runner::loadTestExpectations()
+{
+ QFile file(expectationsFile);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning() << "Could not open TestExpectations file at" << expectationsFile;
+ return;
+ }
+
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine().trimmed();
+ if (line.startsWith('#') || line.isEmpty())
+ continue;
+ TestExpectationLine expectation(line);
+ if (!filter.isEmpty() && !expectation.testCase.contains(filter))
+ continue;
+
+ if (!testCases.contains(expectation.testCase))
+ qWarning() << "Unknown test case" << expectation.testCase << "in TestExpectations file.";
+ //qDebug() << "TestExpectations:" << expectation.testCase << expectation.state;
+ TestCase &s = testCases[expectation.testCase];
+ switch (expectation.state) {
+ case TestExpectationLine::Fails:
+ s.strictExpectation.state = TestCase::Fails;
+ s.sloppyExpectation.state = TestCase::Fails;
+ break;
+ case TestExpectationLine::SloppyFails:
+ s.strictExpectation.state = TestCase::Passes;
+ s.sloppyExpectation.state = TestCase::Fails;
+ break;
+ case TestExpectationLine::StrictFails:
+ s.strictExpectation.state = TestCase::Fails;
+ s.sloppyExpectation.state = TestCase::Passes;
+ break;
+ case TestExpectationLine::Skip:
+ s.skipTestCase = true;
+ break;
+ case TestExpectationLine::Passes:
+ Q_UNREACHABLE();
+ }
+ }
+}
+
+void Test262Runner::updateTestExpectations()
+{
+ QFile file(expectationsFile);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning() << "Could not open TestExpectations file at" << expectationsFile;
+ return;
+ }
+
+ QTemporaryFile updatedExpectations;
+ if (!updatedExpectations.open()) {
+ qFatal("Could not open temporary TestExpectations file: %s",
+ qPrintable(updatedExpectations.errorString()));
+ }
+
+ while (!file.atEnd()) {
+ QByteArray originalLine = file.readLine();
+ QByteArray line = originalLine.trimmed();
+ if (line.startsWith('#') || line.isEmpty()) {
+ updatedExpectations.write(originalLine);
+ continue;
+ }
+
+ TestExpectationLine expectation(line);
+// qDebug() << "checking: " << expectation.testCase;
+ if (!testCases.contains(expectation.testCase)) {
+ updatedExpectations.write(originalLine);
+ continue;
+ }
+ const TestCase &testcase = testCases.value(expectation.testCase);
+ expectation.update(testcase);
+
+ line = expectation.toLine();
+// qDebug() << "updated line:" << line;
+ updatedExpectations.write(line);
+ }
+ file.close();
+ updatedExpectations.close();
+ if (!file.remove())
+ qWarning() << "Could not remove old TestExpectations file at" << expectationsFile;
+ if (updatedExpectations.copy(file.fileName()))
+ qDebug() << "Updated TestExpectations file written!";
+ else
+ qWarning() << "Could not write new TestExpectations file at" << expectationsFile;
+}
+
+void Test262Runner::writeTestExpectations()
+{
+ QFile file(expectationsFile);
+
+ QTemporaryFile expectations;
+ if (!expectations.open()) {
+ qFatal("Could not open temporary TestExpectations file: %s",
+ qPrintable(expectations.errorString()));
+ }
+
+ for (const auto &c : std::as_const(testCases)) {
+ TestExpectationLine line = TestExpectationLine::fromTestCase(c);
+ expectations.write(line.toLine());
+ }
+
+ expectations.close();
+ if (file.exists() && !file.remove())
+ qWarning() << "Could not remove old TestExpectations file at" << expectationsFile;
+ if (expectations.copy(file.fileName()))
+ qDebug() << "new TestExpectations file written!";
+ else
+ qWarning() << "Could not write new TestExpectations file at" << expectationsFile;
+}
+
+void Test262Runner::runAsExternalTests()
+{
+ for (TestData &testData : tasks) {
+ auto runTest = [&] (const char *header, TestCase::Result *result) {
+ QTemporaryFile tempFile;
+ if (!tempFile.open()) {
+ qFatal("Could not open temporary test data file: %s",
+ qPrintable(tempFile.errorString()));
+ }
+ tempFile.write(header);
+ tempFile.write(testData.content);
+ tempFile.close();
+
+ QProcess process;
+ process.start(command, QStringList(tempFile.fileName()));
+ if (!process.waitForFinished(-1) || process.error() == QProcess::FailedToStart) {
+ qWarning() << "Could not execute" << command;
+ *result = TestCase::Result(TestCase::Crashes);
+ }
+ if (process.exitStatus() != QProcess::NormalExit) {
+ *result = TestCase::Result(TestCase::Crashes);
+ }
+ bool ok = (process.exitCode() == EXIT_SUCCESS);
+ if (testData.negative)
+ ok = !ok;
+ *result = ok ? TestCase::Result(TestCase::Passes)
+ : TestCase::Result(TestCase::Fails, process.readAllStandardError());
+ };
+
+ if (testData.runInSloppyMode)
+ runTest("", &testData.sloppyResult);
+ if (testData.runInStrictMode)
+ runTest("'use strict';\n", &testData.strictResult);
+
+ addResult(testData);
+ }
+}
+
+void Test262Runner::addResult(TestCase result)
+{
+ {
+#if !QT_CONFIG(process)
+ QMutexLocker locker(&mutex);
+#endif
+ Q_ASSERT(result.strictExpectation.state == testCases[result.test].strictExpectation.state);
+ Q_ASSERT(result.sloppyExpectation.state == testCases[result.test].sloppyExpectation.state);
+ testCases[result.test] = result;
+ }
+
+ if (!(flags & Verbose))
+ return;
+
+ QString test = result.test;
+ if (result.strictResult.state == TestCase::Skipped) {
+ ;
+ } else if (result.strictResult.state == TestCase::Crashes) {
+ qDebug() << "FAIL:" << test << "crashed in strict mode!";
+ } else if (result.strictResult.state == TestCase::Fails
+ && result.strictExpectation.state == TestCase::Fails) {
+ qCDebug(lcJsTest) << "PASS:" << test << "failed in strict mode as expected";
+ } else if ((result.strictResult.state == TestCase::Passes)
+ == (result.strictExpectation.state == TestCase::Passes)) {
+ qCDebug(lcJsTest) << "PASS:" << test << "passed in strict mode";
+ } else if (!(result.strictExpectation.state == TestCase::Fails)) {
+ qDebug() << "FAIL:" << test << "failed in strict mode with error message:\n"
+ << result.strictResult.errorMessage;
+ } else {
+ qDebug() << "XPASS:" << test << "unexpectedly passed in strict mode";
+ }
+
+ if (result.sloppyResult.state == TestCase::Skipped) {
+ ;
+ } else if (result.sloppyResult.state == TestCase::Crashes) {
+ qDebug() << "FAIL:" << test << "crashed in sloppy mode!";
+ } else if (result.sloppyResult.state == TestCase::Fails
+ && result.sloppyExpectation.state == TestCase::Fails) {
+ qCDebug(lcJsTest) << "PASS:" << test << "failed in sloppy mode as expected";
+ } else if ((result.sloppyResult.state == TestCase::Passes)
+ == (result.sloppyExpectation.state == TestCase::Passes)) {
+ qCDebug(lcJsTest) << "PASS:" << test << "passed in sloppy mode";
+ } else if (!(result.sloppyExpectation.state == TestCase::Fails)) {
+ qDebug() << "FAIL:" << test << "failed in sloppy mode with error message:\n"
+ << result.sloppyResult.errorMessage;
+ } else {
+ qDebug() << "XPASS:" << test << "unexpectedly passed in sloppy mode";
+ }
+}
+
+TestData Test262Runner::getTestData(const TestCase &testCase)
+{
+ QFile testFile(testDir + "/test/" + testCase.test);
+ if (!testFile.open(QFile::ReadOnly)) {
+ qWarning() << "wrong test file" << testCase.test;
+ exit(1);
+ }
+ QByteArray content = testFile.readAll();
+ content.replace(QByteArrayLiteral("\r\n"), "\n");
+
+ qCDebug(lcJsTest) << "parsing test file" << testCase.test;
+
+ TestData data(testCase);
+ parseYaml(content, &data);
+
+ data.harness += harness("assert.js");
+ data.harness += harness("sta.js");
+
+ for (QByteArray inc : std::as_const(data.includes)) {
+ inc = inc.trimmed();
+ data.harness += harness(inc);
+ }
+
+ if (data.async)
+ data.harness += harness("doneprintHandle.js");
+
+ data.content = data.harness + content;
+
+ return data;
+}
+
+struct YamlSection {
+ YamlSection(const QByteArray &yaml, const char *sectionName);
+
+ bool contains(const char *keyword) const;
+ QList<QByteArray> keywords() const;
+
+ QByteArray yaml;
+ int start = -1;
+ int length = 0;
+ bool shortSection = false;
+};
+
+YamlSection::YamlSection(const QByteArray &yaml, const char *sectionName)
+ : yaml(yaml)
+{
+ start = yaml.indexOf(sectionName);
+ if (start < 0)
+ return;
+ start += static_cast<int>(strlen(sectionName));
+ int end = yaml.indexOf('\n', start + 1);
+ if (end < 0)
+ end = yaml.size();
+
+ int s = yaml.indexOf('[', start);
+ if (s > 0 && s < end) {
+ shortSection = true;
+ start = s + 1;
+ end = yaml.indexOf(']', s);
+ } else {
+ while (end < yaml.size() - 1 && yaml.at(end + 1) == ' ')
+ end = yaml.indexOf('\n', end + 1);
+ }
+ length = end - start;
+}
+
+bool YamlSection::contains(const char *keyword) const
+{
+ if (start < 0)
+ return false;
+ int idx = yaml.indexOf(keyword, start);
+ if (idx >= start && idx < start + length)
+ return true;
+ return false;
+}
+
+QList<QByteArray> YamlSection::keywords() const
+{
+ if (start < 0)
+ return QList<QByteArray>();
+
+ QByteArray content = yaml.mid(start, length);
+ QList<QByteArray> keywords;
+ if (shortSection) {
+ keywords = content.split(',');
+ } else {
+ const QList<QByteArray> list = content.split('\n');
+ for (const QByteArray &l : list) {
+ int i = 0;
+ while (i < l.size() && (l.at(i) == ' ' || l.at(i) == '-'))
+ ++i;
+ QByteArray entry = l.mid(i);
+ if (!entry.isEmpty())
+ keywords.append(entry);
+ }
+ }
+// qDebug() << "keywords:" << keywords;
+ return keywords;
+}
+
+
+void Test262Runner::parseYaml(const QByteArray &content, TestData *data)
+{
+ int start = content.indexOf("/*---");
+ if (start < 0)
+ return;
+ start += sizeof("/*---");
+
+ int end = content.indexOf("---*/");
+ if (end < 0)
+ return;
+
+ QByteArray yaml = content.mid(start, end - start);
+
+ if (yaml.contains("negative:"))
+ data->negative = true;
+
+ YamlSection flags(yaml, "flags:");
+ data->runInSloppyMode = !flags.contains("onlyStrict");
+ data->runInStrictMode = !flags.contains("noStrict") && !flags.contains("raw");
+ data->runAsModuleCode = flags.contains("module");
+ data->async = flags.contains("async");
+
+ if (data->runAsModuleCode) {
+ data->runInStrictMode = true;
+ data->runInSloppyMode = false;
+ }
+
+ YamlSection includes(yaml, "includes:");
+ data->includes = includes.keywords();
+
+ YamlSection features = YamlSection(yaml, "features:");
+
+ const char **f = excludedFeatures;
+ while (*f) {
+ if (features.contains(*f)) {
+ data->isExcluded = true;
+ break;
+ }
+ ++f;
+ }
+
+// qDebug() << "Yaml:\n" << yaml;
+}
+
+QByteArray Test262Runner::harness(const QByteArray &name)
+{
+ if (harnessFiles.contains(name))
+ return harnessFiles.value(name);
+
+ QFile h(testDir + QLatin1String("/harness/") + name);
+ if (!h.open(QFile::ReadOnly)) {
+ qWarning() << "Illegal test harness file" << name;
+ exit(1);
+ }
+
+ QByteArray content = h.readAll();
+ harnessFiles.insert(name, content);
+ return content;
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/qml/ecmascripttests/test262runner.h b/tests/auto/qml/ecmascripttests/test262runner.h
new file mode 100644
index 0000000000..a989ac5188
--- /dev/null
+++ b/tests/auto/qml/ecmascripttests/test262runner.h
@@ -0,0 +1,167 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TEST262RUNNER_H
+#define TEST262RUNNER_H
+
+#include <qeventloop.h>
+#include <qmap.h>
+#include <qmutex.h>
+#include <qprocess.h>
+#include <qqueue.h>
+#include <qset.h>
+#include <qthreadpool.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+struct ExecutionEngine;
+void initD262(ExecutionEngine *e);
+}
+
+struct TestCase {
+ TestCase() = default;
+ TestCase(const QString &test)
+ : test(test) {}
+
+ enum State { Skipped, Passes, Fails, Crashes };
+
+ struct Result
+ {
+ State state;
+ QString errorMessage;
+
+ Result(State state, QString errorMessage = "")
+ : state(state), errorMessage(errorMessage) { }
+
+ void negateResult()
+ {
+ switch (state) {
+ case TestCase::Passes:
+ state = TestCase::Fails;
+ break;
+ case TestCase::Fails:
+ state = TestCase::Passes;
+ break;
+ case TestCase::Skipped:
+ case TestCase::Crashes:
+ break;
+ }
+ }
+ };
+
+ Result strictExpectation = Result(Passes);
+ Result sloppyExpectation = Result(Passes);
+ Result strictResult = Result(Skipped);
+ Result sloppyResult = Result(Skipped);
+ bool skipTestCase = false;
+ bool stillNeedStrictRun = false;
+
+ QString test;
+};
+
+struct TestData : TestCase {
+ TestData() = default;
+ TestData(const TestCase &testCase)
+ : TestCase(testCase) {}
+ // flags
+ bool negative = false;
+ bool runInStrictMode = true;
+ bool runInSloppyMode = true;
+ bool runAsModuleCode = false;
+ bool async = false;
+
+ bool isExcluded = false;
+
+ QList<QByteArray> includes;
+
+ QByteArray harness;
+ QByteArray content;
+};
+
+class SingleTest;
+
+class Test262Runner : public QObject
+{
+ Q_OBJECT
+
+public:
+ Test262Runner(const QString &command, const QString &testDir, const QString &expectationsFile);
+ ~Test262Runner();
+
+ enum Mode {
+ Sloppy = 0,
+ Strict = 1
+ };
+
+ enum Flags {
+ Verbose = 0x1,
+ Parallel = 0x2,
+ ForceBytecode = 0x4,
+ ForceJIT = 0x8,
+ WithTestExpectations = 0x10,
+ UpdateTestExpectations = 0x20,
+ WriteTestExpectations = 0x40,
+ };
+ void setFlags(int f) { flags = f; }
+
+ void setFilter(const QString &f) { filter = f; }
+
+ void cat();
+ bool run();
+
+ bool report();
+ QString testDirectory() const { return testDir; }
+
+ static void executeTest(QV4::ExecutionEngine &vm, const QString &testData,
+ const QString &testCasePath = QString(),
+ const QString &harnessForModules = QString(),
+ bool runAsModule = false);
+
+private:
+ friend class SingleTest;
+ bool loadTests();
+ void loadTestExpectations();
+ void updateTestExpectations();
+ void writeTestExpectations();
+
+ void runWithThreadPool();
+
+ void runAsExternalTests();
+ void createProcesses();
+ void assignTaskOrTerminate(int processIndex);
+ void assignSloppy(int processIndex);
+ void assignStrict(int processIndex);
+ void sendDone(int processIndex);
+ QString readUntilNull(QProcess &p);
+
+ TestData getTestData(const TestCase &testCase);
+ void parseYaml(const QByteArray &content, TestData *data);
+
+ QByteArray harness(const QByteArray &name);
+
+ void addResult(TestCase result);
+
+ QString command;
+ QString testDir;
+ QString expectationsFile;
+ int flags = 0;
+
+ QString filter;
+
+ QMap<QString, TestCase> testCases;
+ QHash<QByteArray, QByteArray> harnessFiles;
+
+ QThreadPool *threadPool = nullptr;
+ QMutex mutex;
+
+ QEventLoop loop;
+ std::vector<std::unique_ptr<QProcess>> processes;
+ int runningCount = 0;
+ QQueue<TestData> tasks;
+ QHash<int, TestData> currentTasks;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp
index 03c5b18474..11d724f795 100644
--- a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp
+++ b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp
@@ -1,11 +1,22 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include <QtTest/QtTest>
-#include <QProcess>
+#include <QFileInfo>
+#include <QJSEngine>
+#include <QJsonDocument>
+#include <QJsonObject>
#include <QLibraryInfo>
-#include <qjstest/test262runner.h>
+#include <QProcess>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtTest/QtTest>
+
+#include "test262runner.h"
+#include "private/qqmlbuiltinfunctions_p.h"
+#include "private/qv4arraybuffer_p.h"
+#include "private/qv4globalobject_p.h"
+#include "private/qv4script_p.h"
+
+#include <stdio.h>
class tst_EcmaScriptTests : public QQmlDataTest
{
@@ -40,7 +51,7 @@ static inline bool isNoise(QByteArrayView name)
#ifdef QT_V4_WANT_ES262_WARNINGS
return false;
#else
- const QByteArrayView noisy("qt.qml.compiler");
+ const QByteArrayView noisy("qt.qml.usedbeforedeclared");
return name.startsWith(noisy) && (name.size() <= noisy.size() || name[noisy.size()] == '.');
#endif
}
@@ -59,7 +70,7 @@ void tst_EcmaScriptTests::filterCategories(QLoggingCategory *category)
void tst_EcmaScriptTests::initTestCase()
{
QQmlDataTest::initTestCase();
- /* Suppress lcQmlCompiler's "qt.qml.compiler" warnings; we aren't in a
+ /* Suppress lcQmlCompiler's "qt.qml.usedbeforedeclared" warnings; we aren't in a
position to fix test262's many warnings and they flood messages so we
didn't get to see actual failures unless we passed -maxwarnings with a
huge value on the command-line (resulting in huge log output).
@@ -94,7 +105,74 @@ void tst_EcmaScriptTests::runJitted()
QVERIFY(result);
}
-QTEST_GUILESS_MAIN(tst_EcmaScriptTests)
+//// v RUNNER PROCESS MODE v ////
-#include "tst_ecmascripttests.moc"
+void readInput(bool &done, QString &mode, QString &testData, QString &testCasePath,
+ QString &harnessForModules, bool &runAsModule)
+{
+ QTextStream in(stdin);
+ QString input;
+ while (input.isEmpty())
+ input = in.readLine();
+
+ QJsonDocument json = QJsonDocument::fromJson(input.toUtf8());
+ done = json["done"].toBool(false);
+ mode = json["mode"].toString();
+ testData = json["testData"].toString();
+ testCasePath = json["testCasePath"].toString();
+ harnessForModules = json["harnessForModules"].toString();
+ runAsModule = json["runAsModule"].toBool(false);
+}
+
+void printResult(QV4::ExecutionEngine &vm, const QString &mode)
+{
+ QJsonObject result;
+ result.insert("mode", mode);
+ if (vm.hasException) {
+ QV4::Scope scope(&vm);
+ QV4::ScopedValue val(scope, vm.catchException());
+
+ result.insert("resultState", int(TestCase::State::Fails));
+ result.insert("resultErrorMessage", val->toQString());
+ } else {
+ result.insert("resultState", int(TestCase::State::Passes));
+ }
+ QTextStream(stdout) << QJsonDocument(result).toJson(QJsonDocument::Compact) << "\r\n";
+}
+
+void doRunnerProcess()
+{
+ bool done = false;
+ QString mode;
+ QString testData;
+ QString testCasePath;
+ QString harnessForModules;
+ bool runAsModule = false;
+
+ while (!done) {
+ QV4::ExecutionEngine vm;
+ readInput(done, mode, testData, testCasePath, harnessForModules, runAsModule);
+ if (done)
+ break;
+ Test262Runner::executeTest(vm, testData, testCasePath, harnessForModules, runAsModule);
+ printResult(vm, mode);
+ }
+}
+
+//// ^ RUNNER PROCESS MODE ^ ////
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ if (qEnvironmentVariableIntValue("runnerProcess") == 1) {
+ doRunnerProcess();
+ } else {
+ tst_EcmaScriptTests tc;
+ QTEST_SET_MAIN_SOURCE_PATH
+ return QTest::qExec(&tc, argc, argv);
+ }
+}
+
+#include "tst_ecmascripttests.moc"
diff --git a/tests/auto/qml/linebylinelex/CMakeLists.txt b/tests/auto/qml/linebylinelex/CMakeLists.txt
index 868d13d7a7..8b05ca0527 100644
--- a/tests/auto/qml/linebylinelex/CMakeLists.txt
+++ b/tests/auto/qml/linebylinelex/CMakeLists.txt
@@ -2,6 +2,12 @@
## tst_linebylinelex Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_linebylinelex LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect linebyline test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
diff --git a/tests/auto/qml/linebylinelex/tst_linebylinelex.cpp b/tests/auto/qml/linebylinelex/tst_linebylinelex.cpp
index 02ce1f33f3..d94f325020 100644
--- a/tests/auto/qml/linebylinelex/tst_linebylinelex.cpp
+++ b/tests/auto/qml/linebylinelex/tst_linebylinelex.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTestUtils/private/qmlutils_p.h>
diff --git a/tests/auto/qml/parserstress/CMakeLists.txt b/tests/auto/qml/parserstress/CMakeLists.txt
index 876e8e30ee..5a2751504b 100644
--- a/tests/auto/qml/parserstress/CMakeLists.txt
+++ b/tests/auto/qml/parserstress/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_parserstress Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_parserstress LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/parserstress/tst_parserstress.cpp b/tests/auto/qml/parserstress/tst_parserstress.cpp
index 99af0247a8..a881d12294 100644
--- a/tests/auto/qml/parserstress/tst_parserstress.cpp
+++ b/tests/auto/qml/parserstress/tst_parserstress.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
@@ -41,9 +41,9 @@ QFileInfoList tst_parserstress::findJSFiles(const QDir &d)
rv << fileInfo;
}
- QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
+ const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
QDir::NoSymLinks);
- foreach (const QString &dir, dirs) {
+ for (const QString &dir : dirs) {
QDir sub = d;
sub.cd(dir);
rv << findJSFiles(sub);
diff --git a/tests/auto/qml/qjsengine/CMakeLists.txt b/tests/auto/qml/qjsengine/CMakeLists.txt
index 84c738c34f..452eafa3ad 100644
--- a/tests/auto/qml/qjsengine/CMakeLists.txt
+++ b/tests/auto/qml/qjsengine/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qjsengine Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjsengine LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -28,6 +34,7 @@ qt_internal_add_test(tst_qjsengine
Qt::GuiPrivate
Qt::Qml
Qt::QmlPrivate
+ Qt::QuickTestUtilsPrivate
LIBRARIES # special case
Threads::Threads # special case
TESTDATA ${test_data} "dummy_imports.qml"
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 21f4ff033b..daa16eba72 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -17,6 +17,9 @@
#include <QScopeGuard>
#include <QUrl>
#include <QModelIndex>
+#include <QtQml/qqmllist.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <private/qv4functionobject_p.h>
#ifdef Q_CC_MSVC
#define NO_INLINE __declspec(noinline)
@@ -107,6 +110,8 @@ private slots:
void collectGarbageNestedWrappersTwoEngines();
void gcWithNestedDataStructure();
void stacktrace();
+ void unshiftAndSort();
+ void unshiftAndPushAndSort();
void numberParsing_data();
void numberParsing();
void automaticSemicolonInsertion();
@@ -136,7 +141,10 @@ private slots:
void reentrancy_Array();
void reentrancy_objectCreation();
void jsIncDecNonObjectProperty();
- void JSONparse();
+ void JSON_Parse();
+ void JSON_Stringify_data();
+ void JSON_Stringify();
+ void JSON_Stringify_WithReplacer_QTBUG_95324();
void arraySort();
void lookupOnDisappearingProperty();
void arrayConcat();
@@ -290,6 +298,8 @@ private slots:
void staticInNestedClasses();
void callElement();
+ void functionCtorGeneratedCUIsNotCollectedByGc();
+
void tdzViolations_data();
void tdzViolations();
@@ -301,6 +311,15 @@ private slots:
void callWithSpreadOnElement();
void spreadNoOverflow();
+ void symbolToVariant();
+
+ void garbageCollectedObjectMethodBase();
+
+ void optionalChainWithElementLookup();
+
+ void deleteDefineCycle();
+ void deleteFromSparseArray();
+
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@@ -377,7 +396,7 @@ void tst_QJSEngine::callQObjectSlot()
}
{
- QSignalSpy spy(&dummy, SIGNAL(slotWithArgumentsCalled(QString, QString, QString)));
+ QSignalSpy spy(&dummy, SIGNAL(slotWithArgumentsCalled(QString,QString,QString)));
eng.evaluate("dummy.slotToCall('arg', 'arg2');");
QCOMPARE(spy.size(), 1);
@@ -388,7 +407,7 @@ void tst_QJSEngine::callQObjectSlot()
}
{
- QSignalSpy spy(&dummy, SIGNAL(slotWithArgumentsCalled(QString, QString, QString)));
+ QSignalSpy spy(&dummy, SIGNAL(slotWithArgumentsCalled(QString,QString,QString)));
eng.evaluate("dummy.slotToCall('arg', 'arg2', 'arg3');");
QCOMPARE(spy.size(), 1);
@@ -399,7 +418,7 @@ void tst_QJSEngine::callQObjectSlot()
}
{
- QSignalSpy spy(&dummy, SIGNAL(slotWithOverloadedArgumentsCalled(QString, Qt::KeyboardModifier, Qt::KeyboardModifiers)));
+ QSignalSpy spy(&dummy, SIGNAL(slotWithOverloadedArgumentsCalled(QString,Qt::KeyboardModifier,Qt::KeyboardModifiers)));
eng.evaluate(QStringLiteral("dummy.slotToCall('arg', %1);").arg(QString::number(Qt::ControlModifier)));
QCOMPARE(spy.size(), 1);
@@ -411,7 +430,7 @@ void tst_QJSEngine::callQObjectSlot()
}
{
- QSignalSpy spy(&dummy, SIGNAL(slotWithTwoOverloadedArgumentsCalled(QString, Qt::KeyboardModifiers, Qt::KeyboardModifier)));
+ QSignalSpy spy(&dummy, SIGNAL(slotWithTwoOverloadedArgumentsCalled(QString,Qt::KeyboardModifiers,Qt::KeyboardModifier)));
QJSValue v = eng.evaluate(QStringLiteral("dummy.slotToCallTwoDefault('arg', %1);").arg(QString::number(Qt::MetaModifier | Qt::KeypadModifier)));
QCOMPARE(spy.size(), 1);
@@ -432,7 +451,7 @@ void tst_QJSEngine::callQObjectSlot()
eng.globalObject().setProperty(QStringLiteral("Qt"), value);
{
- QSignalSpy spy(&dummy, SIGNAL(slotWithOverloadedArgumentsCalled(QString, Qt::KeyboardModifier, Qt::KeyboardModifiers)));
+ QSignalSpy spy(&dummy, SIGNAL(slotWithOverloadedArgumentsCalled(QString,Qt::KeyboardModifier,Qt::KeyboardModifiers)));
QJSValue v = eng.evaluate(QStringLiteral("dummy.slotToCall('arg', Qt.ControlModifier);"));
QCOMPARE(spy.size(), 1);
@@ -443,7 +462,7 @@ void tst_QJSEngine::callQObjectSlot()
}
{
- QSignalSpy spy(&dummy, SIGNAL(slotWithTwoOverloadedArgumentsCalled(QString, Qt::KeyboardModifiers, Qt::KeyboardModifier)));
+ QSignalSpy spy(&dummy, SIGNAL(slotWithTwoOverloadedArgumentsCalled(QString,Qt::KeyboardModifiers,Qt::KeyboardModifier)));
QJSValue v = eng.evaluate(QStringLiteral("dummy.slotToCallTwoDefault('arg', Qt.MetaModifier | Qt.KeypadModifier);"));
QCOMPARE(spy.size(), 1);
@@ -905,7 +924,7 @@ void tst_QJSEngine::newQObjectRace()
{
int newObjectCount = 1000;
#if defined(Q_OS_QNX)
- newObjectCount = 256;
+ newObjectCount = 128;
#endif
for (int i=0;i<newObjectCount;++i)
{
@@ -932,7 +951,7 @@ void tst_QJSEngine::newQObject_ownership()
{
QJSValue v = eng.newQObject(ptr);
}
- eng.collectGarbage();
+ gc(*eng.handle(), GCFlags::DontSendPostedEvents);
if (ptr)
QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
QVERIFY(ptr.isNull());
@@ -944,16 +963,16 @@ void tst_QJSEngine::newQObject_ownership()
QJSValue v = eng.newQObject(ptr);
}
QObject *before = ptr;
- eng.collectGarbage();
+ gc(*eng.handle(), GCFlags::DontSendPostedEvents);
QCOMPARE(ptr.data(), before);
delete ptr;
}
{
- QObject *parent = new QObject();
- QObject *child = new QObject(parent);
+ std::unique_ptr<QObject> parent = std::make_unique<QObject>();
+ QObject *child = new QObject(parent.get());
QJSValue v = eng.newQObject(child);
QCOMPARE(v.toQObject(), child);
- delete parent;
+ parent.reset();
QCOMPARE(v.toQObject(), (QObject *)nullptr);
}
{
@@ -962,23 +981,22 @@ void tst_QJSEngine::newQObject_ownership()
{
QJSValue v = eng.newQObject(ptr);
}
- eng.collectGarbage();
+ gc(*eng.handle(), GCFlags::DontSendPostedEvents);
// no parent, so it should be like ScriptOwnership
if (ptr)
QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
QVERIFY(ptr.isNull());
}
{
- QObject *parent = new QObject();
- QPointer<QObject> child = new QObject(parent);
+ std::unique_ptr<QObject> parent = std::make_unique<QObject>();
+ QPointer<QObject> child = new QObject(parent.get());
QVERIFY(child != nullptr);
{
QJSValue v = eng.newQObject(child);
}
- eng.collectGarbage();
+ gc(*eng.handle(), GCFlags::DontSendPostedEvents);
// has parent, so it should be like QtOwnership
QVERIFY(child != nullptr);
- delete parent;
}
{
QPointer<QObject> ptr = new QObject();
@@ -987,7 +1005,7 @@ void tst_QJSEngine::newQObject_ownership()
QQmlEngine::setObjectOwnership(ptr.data(), QQmlEngine::CppOwnership);
QJSValue v = eng.newQObject(ptr);
}
- eng.collectGarbage();
+ gc(*eng.handle(), GCFlags::DontSendPostedEvents);
if (ptr)
QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
QVERIFY(!ptr.isNull());
@@ -1038,6 +1056,17 @@ private:
int m_called = 1;
};
+class TestQMetaObject2 : public QObject
+{
+ Q_OBJECT
+public:
+ Q_INVOKABLE TestQMetaObject2(int a) : m_called(a) {}
+ int called() const { return m_called; }
+
+private:
+ int m_called = 1;
+};
+
void tst_QJSEngine::newQObjectPropertyCache()
{
QScopedPointer<QObject> obj(new QObject);
@@ -1113,6 +1142,18 @@ void tst_QJSEngine::newQMetaObject() {
QCOMPARE(metaObject.property("C").toInt(), 2);
}
+ {
+ QJSEngine engine;
+ const QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject2::staticMetaObject);
+ engine.globalObject().setProperty("Example"_L1, metaObject);
+
+ const QJSValue invalid = engine.evaluate("new Example()"_L1);
+ QVERIFY(invalid.isError());
+ QCOMPARE(invalid.toString(), "Error: Insufficient arguments"_L1);
+
+ const QJSValue valid = engine.evaluate("new Example(123)"_L1);
+ QCOMPARE(qjsvalue_cast<TestQMetaObject2 *>(valid)->called(), 123);
+ }
}
void tst_QJSEngine::exceptionInSlot()
@@ -1642,6 +1683,8 @@ void tst_QJSEngine::valueConversion_basic()
QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
QCOMPARE(eng.fromScriptValue<float>(num), float(123));
QCOMPARE(eng.fromScriptValue<double>(num), double(123));
+ QCOMPARE(eng.fromScriptValue<long>(num), long(123));
+ QCOMPARE(eng.fromScriptValue<ulong>(num), ulong(123));
QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
}
@@ -1653,6 +1696,8 @@ void tst_QJSEngine::valueConversion_basic()
QCOMPARE(eng.fromScriptValue<unsigned short>(num), (unsigned short)(123));
QCOMPARE(eng.fromScriptValue<float>(num), float(123));
QCOMPARE(eng.fromScriptValue<double>(num), double(123));
+ QCOMPARE(eng.fromScriptValue<long>(num), long(123));
+ QCOMPARE(eng.fromScriptValue<ulong>(num), ulong(123));
QCOMPARE(eng.fromScriptValue<qlonglong>(num), qlonglong(123));
QCOMPARE(eng.fromScriptValue<qulonglong>(num), qulonglong(123));
}
@@ -1672,11 +1717,20 @@ void tst_QJSEngine::valueConversion_basic()
QCOMPARE(eng.fromScriptValue<QChar>(eng.toScriptValue(c)), c);
}
+ {
+ QList<QObject *> list = {this};
+ QQmlListProperty<QObject> prop(this, &list);
+ QJSValue jsVal = eng.toScriptValue(prop);
+ QCOMPARE(eng.fromScriptValue<QQmlListProperty<QObject>>(jsVal), prop);
+ }
+
QVERIFY(eng.toScriptValue(static_cast<void *>(nullptr)).isNull());
}
void tst_QJSEngine::valueConversion_QVariant()
{
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_DEPRECATED
QJSEngine eng;
// qScriptValueFromValue() should be "smart" when the argument is a QVariant
{
@@ -1686,6 +1740,8 @@ void tst_QJSEngine::valueConversion_QVariant()
}
// Checking nested QVariants
{
+ // ### Qt 7: QVariant nesting is evil; we should check if we can get rid of it
+ // main use case for it was QSignalSpy
QVariant tmp1;
QVariant tmp2(QMetaType::fromType<QVariant>(), &tmp1);
QCOMPARE(QMetaType::Type(tmp2.userType()), QMetaType::QVariant);
@@ -1764,6 +1820,7 @@ void tst_QJSEngine::valueConversion_QVariant()
QVERIFY(val.isObject());
QCOMPARE(val.property(42).toString(), map.value(QStringLiteral("42")).toString());
}
+ QT_WARNING_POP
}
void tst_QJSEngine::valueConversion_basic2()
@@ -1884,7 +1941,7 @@ void tst_QJSEngine::collectGarbage()
QPointer<QObject> ptr = new QObject();
QVERIFY(ptr != nullptr);
(void)eng.newQObject(ptr);
- eng.collectGarbage();
+ gc(*eng.handle(), GCFlags::DontSendPostedEvents);
if (ptr)
QGuiApplication::sendPostedEvents(ptr, QEvent::DeferredDelete);
QVERIFY(ptr.isNull());
@@ -1919,8 +1976,8 @@ void tst_QJSEngine::collectGarbageNestedWrappersTwoEngines()
QCOMPARE(engine1.evaluate("foobar.dummy.baz").toInt(), 42);
QCOMPARE(engine2.evaluate("foobar.dummy.baz").toInt(), 43);
- engine1.collectGarbage();
- engine2.collectGarbage();
+ gc(*engine1.handle());
+ gc(*engine2.handle());
// The GC should not collect dummy object wrappers neither in engine1 nor engine2, we
// verify that by checking whether the baz property still has its previous value.
@@ -2058,6 +2115,81 @@ void tst_QJSEngine::stacktrace()
}
}
+void tst_QJSEngine::unshiftAndSort()
+{
+ QJSEngine engine;
+ QJSValue func = engine.evaluate(R"""(
+ (function (objectArr, currIdx) {
+ objectArr.unshift({"sortIndex": currIdx});
+ objectArr.sort(function(a, b) {
+ if (a.sortIndex > b.sortIndex)
+ return 1;
+ if (a.sortIndex < b.sortIndex)
+ return -1;
+ return 0;
+ });
+ return objectArr;
+ })
+ )""");
+ QVERIFY(func.isCallable());
+ QJSValue objectArr = engine.newArray();
+
+ for (int i = 0; i < 5; ++i) {
+ objectArr = func.call({objectArr, i});
+ QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString()));
+ const int length = objectArr.property("length").toInt();
+
+ // It did add one element
+ QCOMPARE(length, i + 1);
+
+ for (int x = 0; x < length; ++x) {
+ // We didn't sort cruft into the array.
+ QVERIFY(!objectArr.property(x).isUndefined());
+
+ // The array is actually sorted.
+ QCOMPARE(objectArr.property(x).property("sortIndex").toInt(), x);
+ }
+ }
+}
+
+void tst_QJSEngine::unshiftAndPushAndSort()
+{
+ QJSEngine engine;
+ QJSValue func = engine.evaluate(R"""(
+ (function (objectArr, currIdx) {
+ objectArr.unshift({"sortIndex": currIdx});
+ objectArr.push({"sortIndex": currIdx + 1});
+ objectArr.sort(function(a, b) {
+ if (a.sortIndex > b.sortIndex)
+ return 1;
+ if (a.sortIndex < b.sortIndex)
+ return -1;
+ return 0;
+ });
+ return objectArr;
+ })
+ )""");
+ QVERIFY(func.isCallable());
+ QJSValue objectArr = engine.newArray();
+
+ for (int i = 0; i < 20; i += 2) {
+ objectArr = func.call({objectArr, i});
+ QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString()));
+ const int length = objectArr.property("length").toInt();
+
+ // It did add 2 elements
+ QCOMPARE(length, i + 2);
+
+ for (int x = 0; x < length; ++x) {
+ // We didn't sort cruft into the array.
+ QVERIFY(!objectArr.property(x).isUndefined());
+
+ // The array is actually sorted.
+ QCOMPARE(objectArr.property(x).property("sortIndex").toInt(), x);
+ }
+ }
+}
+
void tst_QJSEngine::numberParsing_data()
{
QTest::addColumn<QString>("string");
@@ -3305,13 +3437,65 @@ void tst_QJSEngine::jsIncDecNonObjectProperty()
}
}
-void tst_QJSEngine::JSONparse()
+void tst_QJSEngine::JSON_Parse()
{
QJSEngine eng;
QJSValue ret = eng.evaluate("var json=\"{\\\"1\\\": null}\"; JSON.parse(json);");
QVERIFY(ret.isObject());
}
+void tst_QJSEngine::JSON_Stringify_data()
+{
+ QTest::addColumn<QString>("object");
+ QTest::addColumn<QString>("json");
+
+ // Basic "smoke" test. More tests are provided by test262 suite.
+ // Don't test with multiple key-value pairs on the same level,
+ // because serialization order might not be deterministic.
+ // Note: parenthesis are required, otherwise objects will be interpretted as code blocks.
+ QTest::newRow("empty") << "({})" << "{}";
+ QTest::newRow("string") << "({a: 'b'})" << "{\"a\":\"b\"}";
+ QTest::newRow("number") << "({c: 42})" << "{\"c\":42}";
+ QTest::newRow("boolean") << "({d: true})" << "{\"d\":true}";
+ QTest::newRow("key is array") << "({[[12, 34]]: 56})" << "{\"12,34\":56}";
+ QTest::newRow("value is date") << "({d: new Date('2000-01-20T12:00:00.000Z')})" << "{\"d\":\"2000-01-20T12:00:00.000Z\"}";
+}
+
+void tst_QJSEngine::JSON_Stringify()
+{
+ QFETCH(QString, object);
+ QFETCH(QString, json);
+
+ QJSEngine eng;
+
+ QJSValue obj = eng.evaluate(object);
+ QVERIFY(obj.isObject());
+
+ QJSValue func = eng.evaluate("(function(obj) { return JSON.stringify(obj); })");
+ QVERIFY(func.isCallable());
+
+ QJSValue ret = func.call(QJSValueList{obj});
+ QVERIFY(ret.isString());
+ QCOMPARE(ret.toString(), json);
+}
+
+void tst_QJSEngine::JSON_Stringify_WithReplacer_QTBUG_95324()
+{
+ QJSEngine eng;
+ QJSValue json = eng.evaluate(R"(
+ function replacer(k, v) {
+ if (this[k] instanceof Date) {
+ return Math.floor(this[k].getTime() / 1000.0);
+ }
+ return v;
+ }
+ const obj = {d: new Date('2000-01-20T12:00:00.000Z')};
+ JSON.stringify(obj, replacer);
+ )");
+ QVERIFY(json.isString());
+ QCOMPARE(json.toString(), QString::fromLatin1("{\"d\":948369600}"));
+}
+
void tst_QJSEngine::arraySort()
{
// tests that calling Array.sort with a bad sort function doesn't cause issues
@@ -3480,9 +3664,6 @@ void tst_QJSEngine::qRegularExpressionExport()
// effect at a given date (QTBUG-9770).
void tst_QJSEngine::dateRoundtripJSQtJS()
{
-#ifdef Q_OS_WIN
- QSKIP("This test fails on Windows due to a bug in QDateTime.");
-#endif
qint64 secs = QDate(2009, 1, 1).startOfDay(QTimeZone::UTC).toSecsSinceEpoch();
QJSEngine eng;
for (int i = 0; i < 8000; ++i) {
@@ -3497,9 +3678,6 @@ void tst_QJSEngine::dateRoundtripJSQtJS()
void tst_QJSEngine::dateRoundtripQtJSQt()
{
-#ifdef Q_OS_WIN
- QSKIP("This test fails on Windows due to a bug in QDateTime.");
-#endif
QDateTime qtDate = QDate(2009, 1, 1).startOfDay();
QJSEngine eng;
for (int i = 0; i < 8000; ++i) {
@@ -3513,9 +3691,6 @@ void tst_QJSEngine::dateRoundtripQtJSQt()
void tst_QJSEngine::dateConversionJSQt()
{
-#ifdef Q_OS_WIN
- QSKIP("This test fails on Windows due to a bug in QDateTime.");
-#endif
qint64 secs = QDate(2009, 1, 1).startOfDay(QTimeZone::UTC).toSecsSinceEpoch();
QJSEngine eng;
for (int i = 0; i < 8000; ++i) {
@@ -3740,7 +3915,7 @@ void tst_QJSEngine::prototypeChainGc()
QJSValue factory = engine.evaluate("(function() { return Object.create(Object.create({})); })");
QVERIFY(factory.isCallable());
QJSValue obj = factory.call();
- engine.collectGarbage();
+ gc(*engine.handle());
QJSValue proto = getProto.call(QJSValueList() << obj);
proto = getProto.call(QJSValueList() << proto);
@@ -3759,7 +3934,7 @@ void tst_QJSEngine::prototypeChainGc_QTBUG38299()
"delete mapping.prop1\n"
"\n");
// Don't hang!
- engine.collectGarbage();
+ gc(*engine.handle());
}
void tst_QJSEngine::dynamicProperties()
@@ -4403,6 +4578,12 @@ void tst_QJSEngine::tracing()
QTest::ignoreMessage(QtDebugMsg, "a (:1)\nb (:1)\nc (:1)\n%entry (:1)");
engine.evaluate("function a() { console.trace(); } function b() { a(); } function c() { b(); }");
engine.evaluate("c()");
+
+ QQmlTestMessageHandler messageHandler;
+ messageHandler.setIncludeCategoriesEnabled(true);
+ engine.evaluate("c()");
+ QCOMPARE(messageHandler.messageString(),
+ QLatin1String("js: a (:1)\nb (:1)\nc (:1)\n%entry (:1)"));
}
void tst_QJSEngine::asserts()
@@ -4497,7 +4678,6 @@ void tst_QJSEngine::privateMethods()
}
QVERIFY(privateMethods.contains("myPrivateMethod"));
- QVERIFY(privateMethods.contains("_q_reregisterTimers"));
privateMethods << QStringLiteral("deleteLater") << QStringLiteral("destroyed");
QJSValueIterator it(jsWrapper);
@@ -4571,7 +4751,7 @@ public:
bool called = false;
- Q_INVOKABLE void callMe(QQmlV4Function *) {
+ Q_INVOKABLE void callMe(QQmlV4FunctionPtr) {
called = true;
}
};
@@ -5749,6 +5929,38 @@ void tst_QJSEngine::callElement()
QCOMPARE(engine.evaluate(program).toString(), u"a"_s);
}
+void tst_QJSEngine::functionCtorGeneratedCUIsNotCollectedByGc()
+{
+ QJSEngine engine;
+ auto v4 = engine.handle();
+ QVERIFY(!v4->isGCOngoing);
+
+ // run gc until roots are collected
+ // we run the gc steps manually, so use "Forever" as the dealine to avoid interference
+ v4->memoryManager->gcStateMachine->deadline = QDeadlineTimer(QDeadlineTimer::Forever);
+ auto sm = v4->memoryManager->gcStateMachine.get();
+ sm->reset();
+ while (sm->state != QV4::GCState::InitMarkPersistentValues) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+
+ const QString program = "new Function('a', 'b', 'let x = \"Hello\"; return a + b');";
+ auto sumFunc = engine.evaluate(program);
+ QVERIFY(sumFunc.isCallable());
+ auto *function = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&sumFunc);
+ auto *cu = function->d()->function->executableCompilationUnit();
+ QVERIFY(cu->runtimeStrings); // should exist for "Hello"
+ QVERIFY(cu->runtimeStrings[0]->isMarked());
+ while (sm->state != QV4::GCState::Invalid) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+
+ auto sum = sumFunc.call({QJSValue(12), QJSValue(13)});
+ QCOMPARE(sum.toInt(), 25);
+}
+
void tst_QJSEngine::tdzViolations_data()
{
QTest::addColumn<QString>("type");
@@ -6028,6 +6240,184 @@ void tst_QJSEngine::spreadNoOverflow()
QCOMPARE(result.errorType(), QJSValue::RangeError);
}
+void tst_QJSEngine::symbolToVariant()
+{
+ QJSEngine engine;
+ const QJSValue val = engine.newSymbol("asymbol");
+ QCOMPARE(val.toVariant(), QStringLiteral("Symbol(asymbol)"));
+
+ const QVariant retained = val.toVariant(QJSValue::RetainJSObjects);
+ QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>());
+ QVERIFY(retained.value<QJSValue>().strictlyEquals(val));
+
+ QCOMPARE(val.toVariant(QJSValue::ConvertJSObjects), QStringLiteral("Symbol(asymbol)"));
+}
+
+class PACHelper : public QObject {
+ Q_OBJECT
+public:
+ Q_INVOKABLE bool shExpMatch(const QString &, const QString &) { return false; }
+ Q_INVOKABLE QString dnsResolve(const QString &) { return QString{}; }
+};
+
+class ProxyAutoConf {
+public:
+ void exposeQObjectMethodsAsGlobal(QJSEngine *engine, QObject *object)
+ {
+ QJSValue helper = engine->newQObject(object);
+ QJSValue g = engine->globalObject();
+ QJSValueIterator it(helper);
+ while (it.hasNext()) {
+ it.next();
+ if (!it.value().isCallable())
+ continue;
+ g.setProperty(it.name(), it.value());
+ }
+ }
+
+ bool parse(const QString & pacBytes)
+ {
+ jsFindProxyForURL = QJSValue();
+ engine = std::make_unique<QJSEngine>();
+ exposeQObjectMethodsAsGlobal(engine.get(), new PACHelper);
+ engine->evaluate(pacBytes);
+ jsFindProxyForURL = engine->globalObject().property(QStringLiteral("FindProxyForURL"));
+ return true;
+ }
+
+ QString findProxyForUrl(const QString &url, const QString &host)
+ {
+ QJSValueList args;
+ args << url << host;
+ gc(*engine->handle(), GCFlags::DontSendPostedEvents);
+ QJSValue callResult = jsFindProxyForURL.call(args);
+ return callResult.toString().trimmed();
+ }
+
+private:
+ std::unique_ptr<QJSEngine> engine;
+ QJSValue jsFindProxyForURL;
+};
+
+QString const pacstring = R"js(
+function FindProxyForURL(host) {
+ list_split_all = Array(
+ "oneoneoneoneo.oneo.oneo.oneoneo.one",
+ "twotwotwotwotw.otwo.twot.wotwotw.otw",
+ "threethreethr.eeth.reet.hreethr.eet",
+ "fourfourfourfo.urfo.urfo.urfourf.our",
+ "fivefivefivef.ivef.ivef.ivefive.fiv",
+ "sixsixsixsixsi.xsix.sixs.ixsixsi.xsi",
+ "sevensevenseve.nsev.ense.venseve.nse",
+ "eight.eighteigh.tei",
+ "*.nin.eninen.ine"
+ )
+ list_myip_direct =
+ "10.254.0.0/255.255.0.0"
+ for (i = 0; i < list_split_all.length; ++i)
+ for (j = 0; j < list_myip_direct.length; ++j)
+ shExpMatch(host, list_split_all)
+ shExpMatch()
+ dnsResolve()}
+)js";
+
+void tst_QJSEngine::garbageCollectedObjectMethodBase()
+{
+ ProxyAutoConf proxyConf;
+ bool pac_read = false;
+
+ const auto processUrl = [&](QString const &url, QString const &host)
+ {
+ if (!pac_read) {
+ proxyConf.parse(pacstring);
+ pac_read = true;
+ }
+ return proxyConf.findProxyForUrl(url, host);
+ };
+
+ const QString url = QStringLiteral("https://servername.domain.test");
+ const QString host = QStringLiteral("servername.domain.test");
+
+ for (size_t i = 0; i < 5; ++i) {
+ auto future = std::async(processUrl, url, host);
+ QCOMPARE(future.get(), QLatin1String("Error: Insufficient arguments"));
+ }
+}
+
+void tst_QJSEngine::optionalChainWithElementLookup()
+{
+ QJSEngine engine;
+
+ const QString program = R"js(
+ (function(xxx) { return xxx?.title["en"] ?? "A" })
+ )js";
+
+ QJSManagedValue func = QJSManagedValue(engine.evaluate(program), &engine);
+ QVERIFY(func.isFunction());
+
+ QCOMPARE(func.call({QJSValue::NullValue}).toString(), "A");
+ QCOMPARE(func.call({QJSValue::UndefinedValue}).toString(), "A");
+
+ const QJSValue nice
+ = engine.toScriptValue(QVariantMap { {"title", QVariantMap { {"en", "B"} } } });
+ QCOMPARE(func.call({nice}).toString(), "B");
+
+ const QJSValue naughty1
+ = engine.toScriptValue(QVariantMap { {"title", QVariantMap { {"fr", "B"} } } });
+ QCOMPARE(func.call({naughty1}).toString(), "A");
+
+ const QJSValue naughty2
+ = engine.toScriptValue(QVariantMap { {"foos", QVariantMap { {"en", "B"} } } });
+ QVERIFY(func.call({naughty2}).isUndefined());
+ QVERIFY(engine.hasError());
+ QCOMPARE(engine.catchError().toString(), "TypeError: Cannot read property 'en' of undefined");
+ QVERIFY(!engine.hasError());
+
+ QVERIFY(func.call({ QJSValue(4) }).isUndefined());
+ QVERIFY(engine.hasError());
+ QCOMPARE(engine.catchError().toString(), "TypeError: Cannot read property 'en' of undefined");
+ QVERIFY(!engine.hasError());
+}
+
+void tst_QJSEngine::deleteDefineCycle()
+{
+ QJSEngine engine;
+ QStringList stackTrace;
+
+ QJSValue result = engine.evaluate(QString::fromLatin1(R"(
+ let global = ({})
+
+ for (let j = 0; j < 1000; j++) {
+ for (let i = 0; i < 2; i++) {
+ const name = "test" + i
+ delete global[name]
+ Object.defineProperty(global, name, { get() { return 0 }, configurable: true })
+ }
+ }
+ )"), {}, 1, &stackTrace);
+ QVERIFY(stackTrace.isEmpty());
+}
+
+void tst_QJSEngine::deleteFromSparseArray()
+{
+ QJSEngine engine;
+
+ // Should not crash
+ const QJSValue result = engine.evaluate(QLatin1String(R"((function() {
+ let o = [];
+ o[10000] = 10;
+ o[20000] = 20;
+ for (let k in o)
+ delete o[k];
+ return o;
+ })())"));
+
+ QVERIFY(result.isArray());
+ QCOMPARE(result.property("length").toNumber(), 20001);
+ QVERIFY(result.property(10000).isUndefined());
+ QVERIFY(result.property(20000).isUndefined());
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt b/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt
index 7400bc5f1f..3bc5c39bf9 100644
--- a/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt
+++ b/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qjsmanagedvalue Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjsmanagedvalue LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qjsmanagedvalue
SOURCES
tst_qjsmanagedvalue.cpp tst_qjsmanagedvalue.h
diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp
index f11ae73a59..35f5bbadc4 100644
--- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp
+++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qjsmanagedvalue.h"
@@ -542,7 +542,7 @@ void tst_QJSManagedValue::toVariant()
}
- // array
+ // variant list
{
auto handler = qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &, const QString &) {
if (type == QtMsgType::QtWarningMsg)
@@ -553,12 +553,10 @@ void tst_QJSManagedValue::toVariant()
QVariantList listIn;
listIn << 123 << QStringLiteral("hello");
QJSManagedValue array(eng.toManagedValue(listIn));
- QVERIFY(array.isArray());
QCOMPARE(array.property(QStringLiteral("length")).toInt(), 2);
QVariant retained = array.toVariant();
- QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>());
- QVERIFY(QJSManagedValue(retained.value<QJSValue>(), &eng).strictlyEquals(array));
+ QCOMPARE(retained.metaType(), QMetaType::fromType<QVariantList>());
QVariantList listOut = retained.toList();
QCOMPARE(listOut.size(), listIn.size());
@@ -566,7 +564,6 @@ void tst_QJSManagedValue::toVariant()
QCOMPARE(listOut.at(i), listIn.at(i));
// round-trip conversion
QJSManagedValue array2(eng.toManagedValue(retained));
- QVERIFY(array2.isArray());
QCOMPARE(array2.property(QStringLiteral("length")).toInt(), array.property(QStringLiteral("length")).toInt());
for (int i = 0; i < array.property(QStringLiteral("length")).toInt(); ++i)
QVERIFY(array2.property(i).strictlyEquals(array.property(i)));
@@ -1525,8 +1522,6 @@ void tst_QJSManagedValue::strictlyEquals()
{
QJSManagedValue var1(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
QJSManagedValue var2(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
- QVERIFY(var1.isArray());
- QVERIFY(var2.isArray());
QVERIFY(!var1.strictlyEquals(var2));
}
{
@@ -1582,22 +1577,22 @@ void tst_QJSManagedValue::castToPointer()
void tst_QJSManagedValue::engineDeleted()
{
- QJSEngine *eng = new QJSEngine;
+ std::unique_ptr<QJSEngine> eng = std::make_unique<QJSEngine>();
QObject *temp = new QObject(); // Owned by JS engine, as newQObject() sets JS ownership explicitly
QJSManagedValue v1(eng->toManagedValue(123));
QCOMPARE(v1.type(), QJSManagedValue::Number);
QJSManagedValue v2(eng->toManagedValue(QStringLiteral("ciao")));
QCOMPARE(v2.type(), QJSManagedValue::String);
- QJSManagedValue v3(eng->newObject(), eng);
+ QJSManagedValue v3(eng->newObject(), eng.get());
QCOMPARE(v3.type(), QJSManagedValue::Object);
QVERIFY(!v3.isNull());
- QJSManagedValue v4(eng->newQObject(temp), eng);
+ QJSManagedValue v4(eng->newQObject(temp), eng.get());
QCOMPARE(v4.type(), QJSManagedValue::Object);
QVERIFY(!v4.isNull());
- QJSManagedValue v5(QStringLiteral("Hello"), eng);
+ QJSManagedValue v5(QStringLiteral("Hello"), eng.get());
QCOMPARE(v2.type(), QJSManagedValue::String);
- delete eng;
+ eng.reset();
// You can still check the type, but anything involving the engine is obviously prohibited.
QCOMPARE(v1.type(), QJSManagedValue::Undefined);
@@ -1742,7 +1737,7 @@ void tst_QJSManagedValue::stringByIndex()
const QString testString = QStringLiteral("foobar");
QJSManagedValue str(testString, &engine);
- for (uint i = 0; i < testString.size(); ++i) {
+ for (qsizetype i = 0; i < testString.size(); ++i) {
QVERIFY(str.hasOwnProperty(i));
QVERIFY(str.hasProperty(i));
diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h
index 6f6add433e..bb5a6be3ca 100644
--- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h
+++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QJSMANAGEDVALUE_H
#define TST_QJSMANAGEDVALUE_H
diff --git a/tests/auto/qml/qjsonbinding/CMakeLists.txt b/tests/auto/qml/qjsonbinding/CMakeLists.txt
index 66f3e94e4c..fa80e764ef 100644
--- a/tests/auto/qml/qjsonbinding/CMakeLists.txt
+++ b/tests/auto/qml/qjsonbinding/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qjsonbinding Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjsonbinding LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp b/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
index 6235ff376d..450560833f 100644
--- a/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
+++ b/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQml/QtQml>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -67,7 +67,8 @@ private:
QByteArray tst_qjsonbinding::readAsUtf8(const QString &fileName)
{
QFile file(testFile(fileName));
- file.open(QIODevice::ReadOnly);
+ if (!file.open(QIODevice::ReadOnly))
+ qFatal("Cannot open file %s", qPrintable(fileName));
QTextStream stream(&file);
return stream.readAll().trimmed().toUtf8();
}
@@ -144,7 +145,11 @@ void tst_qjsonbinding::cppJsConversion()
{
QJSValue jsValue = eng.toScriptValue(jsonValue);
+#if QT_DEPRECATED_SINCE(6, 9)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
QVERIFY(!jsValue.isVariant());
+ QT_WARNING_POP
+#endif
switch (jsonValue.type()) {
case QJsonValue::Null:
QVERIFY(jsValue.isNull());
@@ -193,7 +198,6 @@ void tst_qjsonbinding::cppJsConversion()
if (jsonValue.isObject()) {
QJsonObject jsonObject = jsonValue.toObject();
QJSValue jsObject = eng.toScriptValue(jsonObject);
- QVERIFY(!jsObject.isVariant());
QVERIFY(jsObject.isObject());
QJSValue stringified = stringify.call(QJSValueList() << jsObject);
@@ -205,7 +209,6 @@ void tst_qjsonbinding::cppJsConversion()
} else if (jsonValue.isArray()) {
QJsonArray jsonArray = jsonValue.toArray();
QJSValue jsArray = eng.toScriptValue(jsonArray);
- QVERIFY(!jsArray.isVariant());
QVERIFY(jsArray.isArray());
QJSValue stringified = stringify.call(QJSValueList() << jsArray);
diff --git a/tests/auto/qml/qjsprimitivevalue/CMakeLists.txt b/tests/auto/qml/qjsprimitivevalue/CMakeLists.txt
index 18b3ee938c..c709a5aa56 100644
--- a/tests/auto/qml/qjsprimitivevalue/CMakeLists.txt
+++ b/tests/auto/qml/qjsprimitivevalue/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qjsprimitivevalue Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjsprimitivevalue LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qjsprimitivevalue
SOURCES
tst_qjsprimitivevalue.cpp
diff --git a/tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp b/tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp
index 7609842f17..232200e633 100644
--- a/tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp
+++ b/tests/auto/qml/qjsprimitivevalue/tst_qjsprimitivevalue.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qobject.h>
#include <QtQml/qjsengine.h>
@@ -19,6 +19,7 @@ private slots:
void unaryOperators();
void toFromVariant();
+ void coercion();
void ctor_invalid();
void ctor_undefinedWithEngine();
@@ -313,25 +314,37 @@ void tst_QJSPrimitiveValue::toFromVariant()
switch (operand.type()) {
case QJSPrimitiveValue::Undefined:
QVERIFY(!var.isValid());
+ QCOMPARE(operand.metaType(), QMetaType());
+ QCOMPARE(operand.data(), nullptr);
break;
case QJSPrimitiveValue::Null:
QCOMPARE(var.typeId(), QMetaType::Nullptr);
+ QCOMPARE(operand.metaType(), QMetaType::fromType<std::nullptr_t>());
+ QCOMPARE(operand.data(), nullptr);
break;
case QJSPrimitiveValue::Boolean:
QCOMPARE(var.typeId(), QMetaType::Bool);
QCOMPARE(var.toBool(), operand.toBoolean());
+ QCOMPARE(operand.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(*static_cast<const bool *>(operand.data()), operand.toBoolean());
break;
case QJSPrimitiveValue::Integer:
QCOMPARE(var.typeId(), QMetaType::Int);
QCOMPARE(var.toInt(), operand.toInteger());
+ QCOMPARE(operand.metaType(), QMetaType::fromType<int>());
+ QCOMPARE(*static_cast<const int *>(operand.data()), operand.toInteger());
break;
case QJSPrimitiveValue::Double:
QCOMPARE(var.typeId(), QMetaType::Double);
QCOMPARE(var.toDouble(), operand.toDouble());
+ QCOMPARE(operand.metaType(), QMetaType::fromType<double>());
+ QCOMPARE(*static_cast<const double *>(operand.data()), operand.toDouble());
break;
case QJSPrimitiveValue::String:
QCOMPARE(var.typeId(), QMetaType::QString);
QCOMPARE(var.toString(), operand.toString());
+ QCOMPARE(operand.metaType(), QMetaType::fromType<QString>());
+ QCOMPARE(*static_cast<const QString *>(operand.data()), operand.toString());
break;
}
@@ -344,6 +357,25 @@ void tst_QJSPrimitiveValue::toFromVariant()
}
}
+void tst_QJSPrimitiveValue::coercion()
+{
+ for (const QJSPrimitiveValue &operand : operands) {
+ QCOMPARE(operand.to<QJSPrimitiveValue::Undefined>(), QJSPrimitiveUndefined());
+ QCOMPARE(operand.to<QJSPrimitiveValue::Null>(), QJSPrimitiveNull());
+ QCOMPARE(operand.to<QJSPrimitiveValue::Boolean>(), operand.toBoolean());
+ QCOMPARE(operand.to<QJSPrimitiveValue::Integer>(), operand.toInteger());
+ QCOMPARE(operand.to<QJSPrimitiveValue::String>(), operand.toString());
+
+ const QJSPrimitiveValue lhs = operand.to<QJSPrimitiveValue::Double>();
+ QCOMPARE(lhs.type(), QJSPrimitiveValue::Double);
+ const double rhs = operand.toDouble();
+ if (std::isnan(rhs))
+ QVERIFY(std::isnan(lhs.toDouble()));
+ else
+ QCOMPARE(lhs.toDouble(), rhs);
+ }
+}
+
void tst_QJSPrimitiveValue::ctor_invalid()
{
QJSPrimitiveValue v;
diff --git a/tests/auto/qml/qjsvalue/CMakeLists.txt b/tests/auto/qml/qjsvalue/CMakeLists.txt
index fcd51fff44..212109c878 100644
--- a/tests/auto/qml/qjsvalue/CMakeLists.txt
+++ b/tests/auto/qml/qjsvalue/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qjsvalue Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjsvalue LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# special case begin
# TODO: Prepare for removal, once Platform brings in Threads.
if(NOT TARGET Threads::Threads)
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index 516abed9e8..6a10b5910d 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qjsvalue.h"
@@ -19,11 +19,6 @@ tst_QJSValue::tst_QJSValue()
{
}
-tst_QJSValue::~tst_QJSValue()
-{
- delete engine;
-}
-
void tst_QJSValue::ctor_invalid()
{
QJSEngine eng;
@@ -369,10 +364,11 @@ void tst_QJSValue::toString()
// variant should use internal valueOf(), then fall back to QVariant::toString(),
// then fall back to "QVariant(typename)"
QJSValue variant = eng.toScriptValue(QPoint(10, 20));
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
QVERIFY(!variant.isVariant());
+ QT_WARNING_POP
QCOMPARE(variant.toString(), QString::fromLatin1("QPoint(10, 20)"));
variant = eng.toScriptValue(QUrl());
- QVERIFY(!variant.isVariant());
QVERIFY(variant.isUrl());
QVERIFY(variant.toString().isEmpty());
@@ -1085,13 +1081,8 @@ void tst_QJSValue::toVariant()
QVariantList listIn;
listIn << 123 << "hello";
QJSValue array = eng.toScriptValue(listIn);
- QVERIFY(array.isArray());
QCOMPARE(array.property("length").toInt(), 2);
- QVariant retained = array.toVariant(QJSValue::RetainJSObjects);
- QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>());
- QVERIFY(retained.value<QJSValue>().strictlyEquals(array));
-
QVariant ret = array.toVariant();
QCOMPARE(ret.typeId(), QMetaType::QVariantList);
QVariantList listOut = ret.toList();
@@ -1100,7 +1091,6 @@ void tst_QJSValue::toVariant()
QCOMPARE(listOut.at(i), listIn.at(i));
// round-trip conversion
QJSValue array2 = eng.toScriptValue(ret);
- QVERIFY(array2.isArray());
QCOMPARE(array2.property("length").toInt(), array.property("length").toInt());
for (int i = 0; i < array.property("length").toInt(); ++i)
QVERIFY(array2.property(i).strictlyEquals(array.property(i)));
@@ -1124,6 +1114,25 @@ void tst_QJSValue::toVariant()
QCOMPARE(func.toVariant().metaType(), QMetaType::fromType<QJSValue>());
}
+
+ // object with custom prototype
+ {
+ QJSValue object = eng.evaluate(R"js(
+ (function(){
+ function Person(firstName, lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+ return new Person("John", "Doe");
+ })();
+ )js");
+ QVERIFY(object.isObject());
+ auto asVariant = object.toVariant();
+ QCOMPARE(asVariant.metaType(), QMetaType::fromType<QVariantMap>());
+ auto variantMap = asVariant.value<QVariantMap>();
+ QVERIFY(variantMap.contains("firstName"));
+ QCOMPARE(variantMap["firstName"].toString(), "John");
+ }
}
void tst_QJSValue::toPrimitive_data()
@@ -2345,8 +2354,6 @@ void tst_QJSValue::strictlyEquals()
{
QJSValue var1 = eng.toScriptValue(QVariant(QStringList() << "a"));
QJSValue var2 = eng.toScriptValue(QVariant(QStringList() << "a"));
- QVERIFY(var1.isArray());
- QVERIFY(var2.isArray());
QVERIFY(!var1.strictlyEquals(var2));
}
{
@@ -2586,7 +2593,7 @@ void tst_QJSValue::prettyPrinter()
void tst_QJSValue::engineDeleted()
{
- QJSEngine *eng = new QJSEngine;
+ std::unique_ptr<QJSEngine> eng = std::make_unique<QJSEngine>();
QObject *temp = new QObject(); // Owned by JS engine, as newQObject() sets JS ownership explicitly
QJSValue v1 = eng->toScriptValue(123);
QVERIFY(v1.isNumber());
@@ -2599,7 +2606,7 @@ void tst_QJSValue::engineDeleted()
QJSValue v5 = "Hello";
QVERIFY(v2.isString());
- delete eng;
+ eng.reset();
QVERIFY(!v1.isUndefined()); // Primitive value is stored inline
QVERIFY(v2.isUndefined());
@@ -2810,6 +2817,7 @@ void tst_QJSValue::deleteFromDifferentThread()
thread->start();
condition.wait(&mutex);
QTRY_VERIFY(thread->isFinished());
+ storage.clearFreePageHint();
QTRY_COMPARE(storage.firstPage, nullptr);
#endif
}
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
index 5b77bc2e15..459c0535c0 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QJSVALUE_H
#define TST_QJSVALUE_H
@@ -16,7 +16,6 @@ class tst_QJSValue : public QObject
public:
tst_QJSValue();
- virtual ~tst_QJSValue();
private slots:
void ctor_invalid();
@@ -129,11 +128,9 @@ private slots:
private:
void newEngine()
{
- if (engine)
- delete engine;
- engine = new QJSEngine();
+ engine = std::make_unique<QJSEngine>();
}
- QJSEngine *engine;
+ std::unique_ptr<QJSEngine> engine;
};
#endif
diff --git a/tests/auto/qml/qjsvalueiterator/CMakeLists.txt b/tests/auto/qml/qjsvalueiterator/CMakeLists.txt
index 85ac6c7a58..961674e50d 100644
--- a/tests/auto/qml/qjsvalueiterator/CMakeLists.txt
+++ b/tests/auto/qml/qjsvalueiterator/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qjsvalueiterator Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjsvalueiterator LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qjsvalueiterator
SOURCES
tst_qjsvalueiterator.cpp
diff --git a/tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp b/tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp
index e329cf948f..a671bf42e2 100644
--- a/tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp
+++ b/tests/auto/qml/qjsvalueiterator/tst_qjsvalueiterator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -447,7 +447,7 @@ void tst_QJSValueIterator::iterateNonObject()
void tst_QJSValueIterator::iterateOverObjectFromDeletedEngine()
{
- QJSEngine *engine = new QJSEngine;
+ std::unique_ptr<QJSEngine> engine = std::make_unique<QJSEngine>();
QJSValue objet = engine->newObject();
// populate object with properties
@@ -465,7 +465,7 @@ void tst_QJSValueIterator::iterateOverObjectFromDeletedEngine()
it.next();
QVERIFY(properties.contains(it.name()));
- delete engine;
+ engine.reset();
QVERIFY(objet.isUndefined());
QVERIFY(it.name().isEmpty());
diff --git a/tests/auto/qml/qml/CMakeLists.txt b/tests/auto/qml/qml/CMakeLists.txt
index a696d80c4d..39056384de 100644
--- a/tests/auto/qml/qml/CMakeLists.txt
+++ b/tests/auto/qml/qml/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qml LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -15,4 +21,6 @@ qt_internal_add_test(tst_qml
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
LIBRARIES
Qt::QuickTestUtilsPrivate
+ Qt::Gui
+ Qt::GuiPrivate
)
diff --git a/tests/auto/qml/qml/data/resizeItem.qml b/tests/auto/qml/qml/data/resizeItem.qml
new file mode 100644
index 0000000000..c416451ccc
--- /dev/null
+++ b/tests/auto/qml/qml/data/resizeItem.qml
@@ -0,0 +1,28 @@
+import QtQuick
+
+Rectangle {
+ id: rect
+ color: "green"
+
+ Timer {
+ id: exitTimer
+ running: false
+ onTriggered: Qt.quit()
+ }
+
+ Timer {
+ id: resizeTimer
+ running: false
+ onTriggered: {
+ rect.width = 100
+ rect.height = 50
+ exitTimer.start()
+ }
+ }
+
+ Window.onHeightChanged: {
+ if (rect.Window.width > 0)
+ console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height)
+ resizeTimer.start()
+ }
+}
diff --git a/tests/auto/qml/qml/data/sizedItem.qml b/tests/auto/qml/qml/data/sizedItem.qml
new file mode 100644
index 0000000000..edcb0e8629
--- /dev/null
+++ b/tests/auto/qml/qml/data/sizedItem.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Rectangle {
+ id: rect
+ color: "blue"
+ width: 200; height: 150
+
+ Timer {
+ id: exitTimer
+ running: false
+ onTriggered: Qt.quit()
+ }
+
+ Window.onHeightChanged: {
+ if (rect.Window.width > 0)
+ console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height)
+ exitTimer.restart()
+ }
+}
diff --git a/tests/auto/qml/qml/data/unsizedItem.qml b/tests/auto/qml/qml/data/unsizedItem.qml
new file mode 100644
index 0000000000..e32784762f
--- /dev/null
+++ b/tests/auto/qml/qml/data/unsizedItem.qml
@@ -0,0 +1,18 @@
+import QtQuick
+
+Rectangle {
+ id: rect
+ color: "green"
+
+ Timer {
+ id: exitTimer
+ running: false
+ onTriggered: Qt.quit()
+ }
+
+ Window.onHeightChanged: {
+ if (rect.Window.width > 0)
+ console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height)
+ exitTimer.restart()
+ }
+}
diff --git a/tests/auto/qml/qml/tst_qml.cpp b/tests/auto/qml/qml/tst_qml.cpp
index de884d8257..befc68d443 100644
--- a/tests/auto/qml/qml/tst_qml.cpp
+++ b/tests/auto/qml/qml/tst_qml.cpp
@@ -1,11 +1,16 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qprocess.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformintegration.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+Q_LOGGING_CATEGORY(lcQml, "qt.qml.tests");
+
class tst_qml : public QQmlDataTest
{
Q_OBJECT
@@ -15,6 +20,8 @@ public:
private slots:
void initTestCase() override;
void nonWindow();
+ void itemAndWindowGeometry_data();
+ void itemAndWindowGeometry();
private:
QString qmlPath;
@@ -42,6 +49,124 @@ void tst_qml::nonWindow()
QCOMPARE(qml.exitCode(), 0); // Should not exit with code 2
}
+void tst_qml::itemAndWindowGeometry_data()
+{
+ QTest::addColumn<QString>("config");
+ QTest::addColumn<QString>("geometry");
+ QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<QSize>("expectedWindowSize");
+ QTest::addColumn<QSize>("expectedContentSize");
+
+ const QString none; // empty string
+
+ auto sizeOrInvalid = [](int w, int h) {
+ static const bool wm = QGuiApplicationPrivate::platformIntegration()->
+ hasCapability(QPlatformIntegration::WindowManagement);
+ return wm ? QSize(w, h) : QSize();
+ };
+
+ QTest::newRow("default: unsized")
+ << none << none << "unsizedItem.qml"
+ << QSize() << QSize(); // default size depends on window system
+ QTest::newRow("default: unsized with geometry")
+ << none << "100x100+50+50" << "unsizedItem.qml"
+ << sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100);
+ QTest::newRow("resizeToItem: unsized")
+ << "resizeToItem" << none << "unsizedItem.qml"
+ << QSize() << QSize(0, 0);
+ QTest::newRow("resizeToItem: unsized with geometry")
+ << "resizeToItem" << "100x100+50+50" << "unsizedItem.qml"
+ << sizeOrInvalid(100, 100) << QSize(0, 0);
+
+ QTest::newRow("default: sized")
+ << none << none << "sizedItem.qml"
+ << QSize() << QSize();
+ QTest::newRow("default: sized with geometry")
+ << none << "100x100+50+50" << "sizedItem.qml"
+ << sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100);
+ QTest::newRow("resizeToItem: sized")
+ << "resizeToItem" << none << "sizedItem.qml"
+ << sizeOrInvalid(200, 150) << sizeOrInvalid(200, 150);
+ QTest::newRow("resizeToItem: sized with geometry")
+ << "resizeToItem" << "320x240+50+50" << "sizedItem.qml"
+ << sizeOrInvalid(320, 240) << QSize(200, 150);
+
+ QTest::newRow("default: resizing")
+ << none << none << "resizeItem.qml"
+ << QSize() << QSize();
+ QTest::newRow("default: resizing with geometry")
+ << none << "100x100+50+50" << "resizeItem.qml"
+ << sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100);
+ QTest::newRow("resizeToItem: resizing")
+ << "resizeToItem" << none << "resizeItem.qml"
+ << sizeOrInvalid(100, 50) << sizeOrInvalid(100, 50);
+ QTest::newRow("resizeToItem: resizing with geometry")
+ << "resizeToItem" << "320x240+50+50" << "resizeItem.qml"
+ << sizeOrInvalid(100, 50) << sizeOrInvalid(100, 50);
+}
+
+/*
+ - A root Item will get put into a Window depending on config (implementations in
+ tools/qml/ResizeItemToWindow.qml and ResizeWindowToItem.qml).
+ - The window system will enforce a minimum size.
+ - In the default configuration, the root Item should then get resized to fit
+ (QTBUG-114068 / QTBUG-116753).
+ - In resizeToItem configuration, if the item width/height are not set, the window would
+ try to be 0x0, but the window system won't allow it.
+ - This also tests the `--qwindowgeometry` argument: with the default config, the
+ item should be resized to fit, but not with `-c resizeToItem`.
+*/
+void tst_qml::itemAndWindowGeometry()
+{
+#ifdef Q_OS_WIN
+ QSKIP("console.info does not go to stderr on Windows.");
+#endif
+
+ QFETCH(QString, config);
+ QFETCH(QString, geometry);
+ QFETCH(QString, qmlfile);
+ QFETCH(QSize, expectedWindowSize);
+ QFETCH(QSize, expectedContentSize);
+
+ QStringList args;
+ if (!config.isEmpty())
+ args << "-c" << config;
+ if (!geometry.isEmpty())
+ args << "--qwindowgeometry" << geometry;
+ args << testFile(qmlfile);
+ QProcess qml;
+ qml.start(qmlPath, args);
+ QVERIFY(qml.waitForFinished());
+ QCOMPARE(qml.exitStatus(), QProcess::NormalExit);
+ const QByteArray output = qml.readAllStandardError();
+ const auto sizeLineIndex = output.lastIndexOf("window");
+ QCOMPARE_GE(sizeLineIndex, 0);
+ const auto newlineIndex = output.indexOf('\n', sizeLineIndex);
+ QCOMPARE_GT(newlineIndex, sizeLineIndex);
+ // expect a line like "window 120 120 content 120 120"
+ const auto sizes = output.sliced(sizeLineIndex, newlineIndex - sizeLineIndex).split(' ');
+ QCOMPARE_GE(sizes.size(), 6);
+ QCOMPARE(sizes[0], "window");
+ QCOMPARE(sizes[3], "content");
+ const QSize windowSize(sizes[1].toInt(), sizes[2].toInt());
+ const QSize contentSize(sizes[4].toInt(), sizes[5].toInt());
+ qCDebug(lcQml) << sizes
+ << "window" << windowSize << "expect" << expectedWindowSize
+ << "content" << contentSize << "expect" << expectedContentSize;
+ QVERIFY(!windowSize.isEmpty());
+ if (config != "resizeToItem") {
+ // default config:
+ // ResizeItemToWindow.qml should have resized the item to its window
+ QCOMPARE(contentSize, windowSize);
+ }
+ // windowSize can be off-by-one on hidpi (e.g. QT_SCALE_FACTOR=2 on xcb);
+ // perhaps that's a bug somewhere, but so far we aren't testing hidpi on CI
+ if (expectedWindowSize.isValid())
+ QCOMPARE(windowSize, expectedWindowSize);
+ if (expectedContentSize.isValid())
+ QCOMPARE(contentSize, expectedContentSize);
+}
+
QTEST_MAIN(tst_qml)
#include <tst_qml.moc>
diff --git a/tests/auto/qml/qmlbasicapp/CMakeLists.txt b/tests/auto/qml/qmlbasicapp/CMakeLists.txt
index 8328a6b3f4..add4c560a8 100644
--- a/tests/auto/qml/qmlbasicapp/CMakeLists.txt
+++ b/tests/auto/qml/qmlbasicapp/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlbasicapp LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmlbasicapp
SOURCES
tst_qmlbasicapp.cpp
diff --git a/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp b/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp
index 7f2736a76b..f3846569cb 100644
--- a/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp
+++ b/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "timemodel.h"
diff --git a/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h b/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h
index b42542be31..2cc8f2e756 100644
--- a/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h
+++ b/tests/auto/qml/qmlbasicapp/TimeExample2/timemodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TIMEMODEL_H
#define TIMEMODEL_H
diff --git a/tests/auto/qml/qmlbasicapp/main.qml b/tests/auto/qml/qmlbasicapp/main.qml
index 37598a5235..8c10b0881f 100644
--- a/tests/auto/qml/qmlbasicapp/main.qml
+++ b/tests/auto/qml/qmlbasicapp/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import TimeExample2 // import types from the plugin
import BasicExtension
diff --git a/tests/auto/qml/qmlbasicapp/manual_imports.cpp b/tests/auto/qml/qmlbasicapp/manual_imports.cpp
index 61df826af4..57adb572dd 100644
--- a/tests/auto/qml/qmlbasicapp/manual_imports.cpp
+++ b/tests/auto/qml/qmlbasicapp/manual_imports.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlextensionplugin.h>
diff --git a/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp b/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp
index fd72a0910f..3b9028154c 100644
--- a/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp
+++ b/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QQmlEngine>
#include <QQmlComponent>
diff --git a/tests/auto/qml/qmlcachegen/CMakeLists.txt b/tests/auto/qml/qmlcachegen/CMakeLists.txt
index d92d5e6166..2dacff35b5 100644
--- a/tests/auto/qml/qmlcachegen/CMakeLists.txt
+++ b/tests/auto/qml/qmlcachegen/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmlcachegen Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlcachegen LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qmlcachegen/data/truncateTest.qml b/tests/auto/qml/qmlcachegen/data/truncateTest.qml
new file mode 100644
index 0000000000..fee768cf77
--- /dev/null
+++ b/tests/auto/qml/qmlcachegen/data/truncateTest.qml
@@ -0,0 +1,764 @@
+import QtQuick 2.15
+import QtQuick.Layouts 2.15
+
+Item {
+ width: 400
+ height: 400
+
+ component Button : Item {
+ property string text
+ property Item background
+ }
+
+ ColumnLayout {
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+ Button {
+ text: "A Special Button"
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ color: "red"
+ border.color: "#26282a"
+ border.width: 1
+ radius: 4
+ }
+ }
+
+ }
+}
diff --git a/tests/auto/qml/qmlcachegen/scriptstringprops.h b/tests/auto/qml/qmlcachegen/scriptstringprops.h
index 521455c098..96c86f1c59 100644
--- a/tests/auto/qml/qmlcachegen/scriptstringprops.h
+++ b/tests/auto/qml/qmlcachegen/scriptstringprops.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef SCRIPT_STRING_PROPS_H
#define SCRIPT_STRING_PROPS_H
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index 60d4a5df32..17a914c1dd 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
@@ -63,6 +63,8 @@ private slots:
void inlineComponent();
void posthocRequired();
+ void gracefullyHandleTruncatedCacheFile();
+
void scriptStringCachegenInteraction();
void saveableUnitPointer();
};
@@ -349,7 +351,7 @@ void tst_qmlcachegen::signalHandlerParameters()
};
QVERIFY(isStringIndexInStringTable(compilationUnit->objectAt(0)->signalAt(0)->parameterAt(0)->nameIndex));
- QVERIFY(!compilationUnit->dynamicStrings.isEmpty());
+ QVERIFY(!compilationUnit->baseCompilationUnit()->dynamicStrings.isEmpty());
}
}
@@ -714,7 +716,8 @@ void tst_qmlcachegen::moduleScriptImport()
{
auto componentPrivate = QQmlComponentPrivate::get(&component);
QVERIFY(componentPrivate);
- auto compilationUnit = componentPrivate->compilationUnit->dependentScripts.first()->compilationUnit();
+ auto compilationUnit = componentPrivate->compilationUnit->dependentScriptsPtr()
+ ->first()->compilationUnit();
QVERIFY(compilationUnit);
auto unitData = compilationUnit->unitData();
QVERIFY(unitData);
@@ -839,6 +842,23 @@ void tst_qmlcachegen::posthocRequired()
qPrintable(component.errorString()));
}
+void tst_qmlcachegen::gracefullyHandleTruncatedCacheFile()
+{
+#if defined(QTEST_CROSS_COMPILED)
+ QSKIP("Cannot call qmlcachegen on cross-compiled target.");
+#endif
+
+ bool ok = generateCache(testFile("truncateTest.qml"));
+ QVERIFY(ok);
+ const QString qmlcFile = testFile("truncateTest.qmlc");
+ QVERIFY(QFile::exists(qmlcFile));
+ QFile::resize(qmlcFile, QFileInfo(qmlcFile).size() / 2);
+ QQmlEngine engine;
+ CleanlyLoadingComponent component(&engine, testFileUrl("truncateTest.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+}
+
void tst_qmlcachegen::scriptStringCachegenInteraction()
{
#if defined(QTEST_CROSS_COMPILED)
diff --git a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
index 8644e00cde..42ad6d23d6 100644
--- a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlcppcodegen LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
add_subdirectory(data)
qt_internal_add_test(tst_qmlcppcodegen
@@ -8,9 +14,11 @@ qt_internal_add_test(tst_qmlcppcodegen
tst_qmlcppcodegen.cpp
LIBRARIES
Qt::QmlPrivate
- Qt::Gui
+ Qt::GuiPrivate
codegen_test_module
codegen_test_moduleplugin
+ codegen_test_hidden
+ codegen_test_hiddenplugin
)
qt_internal_add_test(tst_qmlcppcodegen_interpreted
@@ -18,9 +26,11 @@ qt_internal_add_test(tst_qmlcppcodegen_interpreted
tst_qmlcppcodegen.cpp
LIBRARIES
Qt::QmlPrivate
- Qt::Gui
+ Qt::GuiPrivate
codegen_test_module
codegen_test_moduleplugin
+ codegen_test_hidden
+ codegen_test_hiddenplugin
DEFINES
QT_TEST_FORCE_INTERPRETER
)
diff --git a/tests/auto/qml/qmlcppcodegen/data/Action.qml b/tests/auto/qml/qmlcppcodegen/data/Action.qml
new file mode 100644
index 0000000000..99b86fb31c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Action.qml
@@ -0,0 +1,7 @@
+import QtQuick
+import QtQuick.Controls as QQC2
+import QtQuick.Templates as T
+
+QQC2.Action {
+ property bool visible: true
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/B.qml b/tests/auto/qml/qmlcppcodegen/data/B.qml
new file mode 100644
index 0000000000..97e895ecad
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/B.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ property rect r
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml b/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml
new file mode 100644
index 0000000000..2615778f6a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/BaseConstraint.qml
@@ -0,0 +1,9 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property int satisfaction: Satisfaction.NONE
+ property QtObject output
+
+ function inputsKnown(mark: int) : bool { return true }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index 0737d77005..8c5449d192 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -4,41 +4,61 @@
set(cpp_sources
ambiguous.h
birthdayparty.cpp birthdayparty.h
+ convertQJSPrimitiveValueToIntegral.h
cppbaseclass.h
druggeljug.h
+ dummyobjekt.h
dynamicmeta.h
enumproblems.h
enumProperty.h
+ getOptionalLookup.h
gadgetwithenum.h
invisible.h
+ listprovider.h
multiforeign.h
objectwithmethod.h
person.cpp person.h
+ resettable.h
+ sequenceToIterable.h
sequencetypeexample.cpp sequencetypeexample.h
state.h
theme.cpp theme.h
timelinetheme.cpp timelinetheme.h
+ variantMapLookup.h
+ variantreturn.h
+ weathermoduleurl.h
wrapwithvariant.h
withlength.h
)
set(qml_files
AccessModelMethodsFromOutside.qml
+ Action.qml
ArraySequenceLengthInterop.qml
+ B.qml
BadType.qml
+ BaseConstraint.qml
BaseMember.qml
BindingExpression.qml
+ CxxTypeFromDir.qml
+ CxxTypeFromImplicit.qml
Cycle1.qml
Cycle2.qml
Cycle3.qml
- CxxTypeFromDir.qml
- CxxTypeFromImplicit.qml
+ CppMethodListReturnType.qml
Dummy.qml
+ Dummy2.qml
+ EditConstraint.qml
Enums.qml
Foozle.qml
+ GetOptionalLookupOnQJSValueNonStrict.qml
+ GetOptionalLookupShadowed.qml
Loopy.qml
+ NotificationItem.qml
+ NotificationsUtils.js
OkType.qml
Panel.qml
+ Planner.qml
ProgressBar/Keyframe.qml
ProgressBar/KeyframeGroup.qml
ProgressBar/ProgressBar.ui.qml
@@ -46,98 +66,145 @@ set(qml_files
ProgressBar/Timeline.qml
ProgressBar/TimelineAnimation.qml
RootWithoutId.qml
+ Satisfaction.qml
SelectionRectangle.qml
+ ShadowedObjectName.qml
+ ShadowedObjectNameDerived.qml
+ StoreMetaEnum.qml
Test.qml
TestCase.qml
+ Variable.qml
WindowDerived.qml
aliasLookup.qml
ambiguous1/Ambiguous.qml
ambiguous2/Ambiguous.qml
+ ambiguousAs.qml
ambiguousSignals.qml
anchorsFill.qml
argumentConversion.qml
array.qml
+ arrayCtor.qml
asCast.qml
attachedBaseEnum.qml
badSequence.qml
+ basicBlocksWithBackJump.qml
+ basicBlocksWithBackJump_infinite.qml
+ basicDTZ.qml
bindToValueType.qml
blockComments.qml
+ boolCoercions.qml
+ boolPointerMerge.qml
boundComponents.qml
callContextPropertyLookupResult.qml
callWithSpread.qml
childobject.qml
colorAsVariant.qml
colorString.qml
- consoleObject.qml
+ compareOriginals.qml
+ comparisonTypes.qml
componentReturnType.qml
compositeTypeMethod.qml
compositesingleton.qml
+ consoleObject.qml
+ consoleTrace.qml
construct.qml
contextParam.qml
conversionDecrement.qml
+ conversionInDeadCode.qml
conversions.qml
conversions2.qml
+ convertPrimitiveToVar.qml
+ convertQJSPrimitiveValueToIntegral.qml
+ convertToOriginalReadAcumulatorForUnaryOperators.qml
curlygrouped.qml
cycleHead.qml
+ dateConstruction.qml
dateConversions.qml
deadShoeSize.qml
deadStoreLoop.qml
dialog.qml
+ dialogButtonBox.qml
dynamicscene.qml
+ enforceSignature.qml
enumConversion.qml
+ enumFromBadSingleton.qml
enumInvalid.qml
enumLookup.qml
+ enumMarkedAsFlag.qml
enumProblems.qml
enumScope.qml
enumsInOtherObject.qml
enumsUser.qml
equalityQObjects.qml
+ equalityQUrl.qml
+ equalityTestsWithNullOrUndefined.qml
equalityVarAndNonStorable.qml
equalsUndefined.qml
+ exceptionFromInner.qml
excessiveParameters.qml
extendedTypes.qml
+ extra/extra.qml
failures.qml
fallbacklookups.qml
+ fallbackresettable.qml
fileDialog.qml
+ flagEnum.qml
fromBoolValue.qml
- functionLookup.qml
funcWithParams.qml
+ functionLookup.qml
functionReturningVoid.qml
functionTakingVar.qml
+ getOptionalLookup.qml
globals.qml
- hidden/Main.qml
- hidden/Style.qml
idAccess.qml
+ ignoredFunctionReturn.qml
immediateQuit.qml
imports/QmlBench/Globals.qml
importsFromImportPath.qml
+ indirectlyShadowable.qml
infinities.qml
infinitiesToInt.qml
- invisibleBase.qml
- invisibleTypes.qml
- invisibleListElementType.qml
intEnumCompare.qml
intOverflow.qml
+ intToEnum.qml
interactive.qml
interceptor.qml
+ internalConversion.qml
+ invisibleBase.qml
+ invisibleListElementType.qml
+ invisibleTypes.qml
isnan.qml
+ iteration.qml
javaScriptArgument.qml
+ jsArrayMethods.qml
+ jsArrayMethodsUntyped.qml
+ jsArrayMethodsWithParams.qml
+ jsArrayMethodsWithParamsUntyped.qml
jsMathObject.qml
jsimport.qml
jsmoduleimport.qml
layouts.qml
- library.js
letAndConst.qml
+ library.js
listAsArgument.qml
+ listConversion.qml
listIndices.qml
+ listOfInvisible.qml
listPropertyAsModel.qml
+ listToString.qml
listlength.qml
math.qml
+ mathMinMax.qml
mathOperations.qml
+ mathStaticProperties.qml
+ mergedObjectRead.qml
+ mergedObjectWrite.qml
+ methodOnListLookup.qml
methods.qml
modulePrefix.qml
moveRegVoid.qml
multiforeign.qml
+ multipleCtors.qml
namespaceWithEnum.qml
noBindingLoop.qml
noQQmlData.qml
@@ -146,9 +213,14 @@ set(qml_files
notEqualsInt.qml
notNotString.qml
nullAccess.qml
+ nullAccessInsideSignalHandler.qml
nullComparison.qml
+ nullishCoalescing.qml
numbersInJsPrimitive.qml
objectInVar.qml
+ objectLookupOnListElement.qml
+ objectWithStringListMethod.qml
+ optionalComparison.qml
outOfBounds.qml
overriddenMember.qml
ownProperty.qml
@@ -157,48 +229,76 @@ set(qml_files
popContextAfterRet.qml
prefixedMetaType.qml
pressAndHoldButton.qml
- registerelimination.qml
+ qtbug113150.qml
+ reduceWithNullThis.qml
+ readEnumFromInstance.qml
+ readonlyListProperty.qml
registerPropagation.qml
+ registerelimination.qml
+ renameAdjust.qml
+ resettable.qml
+ returnAfterReject.qml
revisions.qml
+ scopeIdLookup.qml
scopeVsObject.qml
+ scopedEnum.qml
script.js
script.mjs
+ sequenceToIterable.qml
+ setLookupConversion.qml
+ setLookupOriginalScope.qml
+ shadowedAsCasts.qml
+ shadowedMethod.qml
+ shadowedPrimitiveCmpEqNull.qml
shared/Slider.qml
shifts.qml
signal.qml
signalHandler.qml
signalIndexMismatch.qml
+ signalsWithLists.qml
signatureIgnored.qml
specificParent.qml
storeElementSideEffects.qml
stringArg.qml
stringLength.qml
stringToByteArray.qml
+ structuredValueType.qml
testlogger.js
text.qml
themerbad.qml
themergood.qml
+ thisObject.qml
throwObjectName.qml
toString.qml
+ topLevelComponent.qml
translation.qml
+ trigraphs.qml
trivialSignalHandler.qml
typePropagationLoop.qml
typePropertyClash.qml
typedArray.qml
undefinedResets.qml
+ undefinedToDouble.qml
unknownAttached.qml
unknownParameter.qml
unstoredUndefined.qml
unusedAttached.qml
urlString.qml
usingCxxTypesFromFileImports.qml
+ valueTypeCast.qml
valueTypeCopy.qml
+ valueTypeDefault.qml
valueTypeLists.qml
valueTypeProperty.qml
valueTypeReference.qml
+ variantMap.qml
+ variantMapLookup.qml
+ variantReturn.qml
variantlist.qml
versionmismatch.qml
+ voidConversion.qml
voidfunction.qml
+ writeback.qml
dummy_imports.qml
)
@@ -214,21 +314,61 @@ set_source_files_properties("shared/Slider.qml"
set_source_files_properties("hidden/Style.qml"
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
+qt_policy(SET QTP0001 NEW)
+
+# Add the module for the hidden files before setting QTP0004, so that we don't add a qmldir in
+# "hidden". That would defeat the purpose.
+
+qt_add_library(codegen_test_hidden STATIC)
+qt_autogen_tools_initial_setup(codegen_test_hidden)
+
+set_target_properties(codegen_test_hidden PROPERTIES
+ # We really want qmlcachegen here, even if qmlsc is available
+ QT_QMLCACHEGEN_EXECUTABLE qmlcachegen
+ QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks
+)
+
+target_compile_definitions(codegen_test_hidden PUBLIC
+ -DGENERATED_CPP_FOLDER="${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache"
+)
+
+qt_policy(SET QTP0004 OLD)
+qt6_add_qml_module(codegen_test_hidden
+ URI HiddenTestTypes
+ QML_FILES
+ hidden/Main.qml
+ hidden/Style.qml
+ OUTPUT_DIRECTORY HiddenTestTypes
+ __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE
+)
+
+add_dependencies(codegen_test_hidden Qt::Quick)
+
+qt_autogen_tools_initial_setup(codegen_test_hiddenplugin)
+
+
qt_add_library(codegen_test_module STATIC)
qt_autogen_tools_initial_setup(codegen_test_module)
set_target_properties(codegen_test_module PROPERTIES
# We really want qmlcachegen here, even if qmlsc is available
QT_QMLCACHEGEN_EXECUTABLE qmlcachegen
+ QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks
+)
+
+qt_policy(SET QTP0004 NEW)
+
+target_compile_definitions(codegen_test_module PUBLIC
+ -DGENERATED_CPP_FOLDER="${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache"
)
qt6_add_qml_module(codegen_test_module
VERSION 1.5
URI TestTypes
IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/imports/"
- AUTO_RESOURCE_PREFIX
DEPENDENCIES
QtQuick
+ QtQuick.Controls
QtQuick.Templates
QtQuick.Shapes
SOURCES
@@ -238,8 +378,96 @@ qt6_add_qml_module(codegen_test_module
RESOURCES
${resource_files}
OUTPUT_DIRECTORY TestTypes # Make sure tst_qmlcachegen doesn't see our output
+ __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE
)
+if(${CMAKE_VERSION} GREATER_EQUAL "3.19.0")
+ qt_target_qml_sources(codegen_test_module
+ QML_FILES extra2/extra.qml
+ )
+else()
+ target_compile_definitions(codegen_test_module PUBLIC
+ -DVERY_OLD_CMAKE=1
+ )
+endif()
+
add_dependencies(codegen_test_module Qt::Quick Qt::QuickTemplates2 Qt::QuickShapesPrivate)
qt_autogen_tools_initial_setup(codegen_test_moduleplugin)
+
+
+qt_add_library(codegen_test_module_verify STATIC)
+qt_autogen_tools_initial_setup(codegen_test_module_verify)
+
+set_target_properties(codegen_test_module_verify PROPERTIES
+ # We really want qmlcachegen here, even if qmlsc is available
+ QT_QMLCACHEGEN_EXECUTABLE qmlcachegen
+ QT_QMLCACHEGEN_ARGUMENTS --validate-basic-blocks
+)
+
+
+qt6_add_qml_module(codegen_test_module_verify
+ VERSION 1.5
+ URI TestTypes
+ IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/imports/"
+ DEPENDENCIES
+ QtQuick
+ QtQuick.Controls
+ QtQuick.Templates
+ QtQuick.Shapes
+ SOURCES
+ ${cpp_sources}
+ QML_FILES
+ ${qml_files}
+ RESOURCES
+ ${resource_files}
+ OUTPUT_DIRECTORY verify/TestTypes # Make sure tst_qmlcachegen doesn't see our output
+ TARGET_PATH verify/TestTypes # Different target path to avoid resource file name clashes
+ __QT_INTERNAL_DISAMBIGUATE_QMLDIR_RESOURCE
+)
+
+add_dependencies(codegen_test_module_verify Qt::Quick Qt::QuickTemplates2 Qt::QuickShapesPrivate)
+
+qt_autogen_tools_initial_setup(codegen_test_module_verifyplugin)
+
+
+qt_internal_add_test(tst_qmlcppcodegen_verify
+ SOURCES
+ tst_qmlcppcodegen_verify.cpp
+)
+
+add_dependencies(tst_qmlcppcodegen_verify codegen_test_module codegen_test_module_verify)
+
+set(a_files "")
+set(b_files "")
+
+foreach(qml_file IN LISTS qml_files)
+ string(REGEX REPLACE "\\.(js|mjs|qml)$" "_\\1" compiled_file ${qml_file})
+ string(REGEX REPLACE "[$#?]+" "_" compiled_file ${compiled_file})
+
+ list(APPEND
+ a_files
+ "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/codegen_test_module_${compiled_file}.cpp")
+
+ list(APPEND
+ b_files
+ "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/codegen_test_module_verify_${compiled_file}.cpp")
+endforeach()
+
+qt_add_resources(tst_qmlcppcodegen_verify "a"
+ PREFIX
+ "/a"
+ FILES
+ ${a_files}
+ BASE
+ "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/"
+)
+
+qt_add_resources(tst_qmlcppcodegen_verify "b"
+ PREFIX
+ "/b"
+ FILES
+ ${b_files}
+ BASE
+ "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmlcache/"
+)
diff --git a/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml b/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml
new file mode 100644
index 0000000000..9c3ce4e877
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml
@@ -0,0 +1,12 @@
+pragma Strict
+
+import QtQuick
+import TestTypes
+
+Item {
+ ListProvider {
+ id: listProvider
+ }
+
+ property var list: listProvider.intList()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml b/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml
new file mode 100644
index 0000000000..a3bbef1888
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Dummy2.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+
+import QtQml
+
+QtObject {
+ property int value
+ property Dummy2 child
+ property int dummyEnum
+
+ signal triggered()
+ signal signalWithArg(int one, bool two)
+ property real onValue
+ property real offValue
+
+ function someFunction(a: int, b: bool, c: Dummy2, d: real, e: int) : int { return 42 }
+ property string strProp
+ function concat(a: string, b: string) : string { return a + b }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml b/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml
new file mode 100644
index 0000000000..ce278fbdf9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/EditConstraint.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property Variable myOutput
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml
new file mode 100644
index 0000000000..5a89e996b4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupOnQJSValueNonStrict.qml
@@ -0,0 +1,7 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ property Action action: Action { }
+ property bool b: action?.visible
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml
new file mode 100644
index 0000000000..eacefc3017
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/GetOptionalLookupShadowed.qml
@@ -0,0 +1,19 @@
+pragma Strict
+
+import QtQml
+import QtQuick
+
+QtObject {
+ id: root
+
+ component Base : QtObject {
+ property int i: 1
+ }
+
+ component Derived : Base {
+ property string i: "a"
+ }
+
+ property Base base: Derived { }
+ property var res: root.base?.i
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml b/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml
new file mode 100644
index 0000000000..fba4df6453
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/NotificationItem.qml
@@ -0,0 +1,7 @@
+import QtQml
+import TestTypes as MobileShell
+
+QtObject {
+ id: notificationItem
+ objectName: MobileShell.NotificationsUtils.determineNotificationHeadingText(notificationItem)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js b/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js
new file mode 100644
index 0000000000..079270e1b9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/NotificationsUtils.js
@@ -0,0 +1,3 @@
+function determineNotificationHeadingText(notificationItem) {
+ return "heading";
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Panel.qml b/tests/auto/qml/qmlcppcodegen/data/Panel.qml
index 84e926b8d2..7f589d23e8 100644
--- a/tests/auto/qml/qmlcppcodegen/data/Panel.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/Panel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qmlcppcodegen/data/Planner.qml b/tests/auto/qml/qmlcppcodegen/data/Planner.qml
new file mode 100644
index 0000000000..ddb2fff053
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Planner.qml
@@ -0,0 +1,50 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: planner
+ property Variable last: Variable { value: 10 }
+
+ function newMark() : int {
+ return 5;
+ }
+
+ function addPropagate(i: int) : bool {
+ return false;
+ }
+
+ function typeErasedRemoveOne(v: QtObject) { removeOne(v as Variable) }
+
+ // Work with various shadowable members and return values.
+ function removeOne(v: Variable) {
+ let vDeterminedBy = v.determinedBy;
+ for (let i = 0, length = v.length(); i < length; ++i) {
+ let next = v.constraint(i) as BaseConstraint;
+ if (next.satisfaction === Satisfaction.NONE)
+ objectName += "n"
+ else if (next !== vDeterminedBy)
+ objectName += "d"
+ else
+ objectName += "x"
+ }
+ }
+
+ function typeErasedRun(c: QtObject) { run(c as BaseConstraint) }
+
+ function run(initial: BaseConstraint) {
+ let mark = planner.newMark();
+ let c = initial;
+
+ let output = c.output as Variable;
+ if (output.mark !== mark && c.inputsKnown(mark)) {
+ output.mark = mark;
+ }
+ }
+
+ function verify(i: int) {
+ if (last.value !== i)
+ console.error("failed", last.value, i);
+ else
+ console.log("success")
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml
index 21a3832366..a44af04a50 100644
--- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/ProgressBar.ui.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
import QtQuick.Window 2.3
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml
index 707f0d9be9..43ff1b62b1 100644
--- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/Root.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
diff --git a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml
index b97e8956bf..75ad2245f2 100644
--- a/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/ProgressBar/TimelineAnimation.qml
@@ -2,5 +2,5 @@ import QtQuick
NumberAnimation {
property bool pingPong
- signal finished()
+ signal finishedEvil()
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml b/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml
new file mode 100644
index 0000000000..74968c65ea
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Satisfaction.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ enum Value {
+ NONE = 0,
+ FORWARD = 1,
+ BACKWARD = 2
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml
new file mode 100644
index 0000000000..f079f4a94e
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectName.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property int objectName: 12
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml
new file mode 100644
index 0000000000..f988cd6bc9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ShadowedObjectNameDerived.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+ShadowedObjectName {
+ property double objectName: 17.4
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml b/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml
new file mode 100644
index 0000000000..eb3da15a3a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/StoreMetaEnum.qml
@@ -0,0 +1,12 @@
+import QtQml
+
+QtObject {
+ enum Foo {
+ Bar,
+ Baz
+ }
+
+ property var eF: StoreMetaEnum.Foo
+ property int bar: eF.Bar
+ property int baz: eF.Baz
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/Variable.qml b/tests/auto/qml/qmlcppcodegen/data/Variable.qml
new file mode 100644
index 0000000000..f5af97757f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/Variable.qml
@@ -0,0 +1,22 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: variable
+ property int value: 0
+ property int mark: 0
+ property BaseConstraint determinedBy: null
+ property list<BaseConstraint> constraints: [
+ BaseConstraint {
+ satisfaction: variable.value == 0 ? Satisfaction.NONE : Satisfaction.FORWARD
+ }
+ ]
+
+ function length(): int {
+ return constraints.length
+ }
+
+ function constraint(i: int) : BaseConstraint {
+ return constraints[i];
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ambiguous.h b/tests/auto/qml/qmlcppcodegen/data/ambiguous.h
index 5ea20dbcd1..8f964d593c 100644
--- a/tests/auto/qml/qmlcppcodegen/data/ambiguous.h
+++ b/tests/auto/qml/qmlcppcodegen/data/ambiguous.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef AMBIGUOUS_H
#define AMBIGUOUS_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml b/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml
new file mode 100644
index 0000000000..2b7cbd593d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ambiguousAs.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: self
+ property bool useSelf: true
+ property QtObject other: {
+ var a;
+ if (useSelf)
+ a = self
+ else
+ a = 15
+ return a as QtObject
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml b/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml
new file mode 100644
index 0000000000..9886a14cb1
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/arrayCtor.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import QML
+
+QtObject {
+ property list<int> defaultCtor: new Array()
+ property list<int> oneArgCtor: new Array(5)
+ property list<int> multiArgCtor: new Array(2, 3, 3, 4)
+ property list<bool> arrayTrue: new Array(true)
+ property list<bool> arrayFalse: new Array(false)
+ property list<real> arrayNegative: new Array(-14)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/asCast.qml b/tests/auto/qml/qmlcppcodegen/data/asCast.qml
index 1befc08d0a..cb8155ca6c 100644
--- a/tests/auto/qml/qmlcppcodegen/data/asCast.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/asCast.qml
@@ -28,4 +28,14 @@ Item {
property QtObject dummyAsItem: dummy as Item
property QtObject dummyAsRectangle: dummy as Rectangle
property QtObject dummyAsDummy: dummy as Dummy
+
+ property QtObject nullAsObject: null as QtObject
+ property QtObject nullAsItem: null as Item
+ property QtObject nullAsRectangle: null as Rectangle
+ property QtObject nullAsDummy: null as Dummy
+
+ property QtObject undefinedAsObject: undefined as QtObject
+ property QtObject undefinedAsItem: undefined as Item
+ property QtObject undefinedAsRectangle: undefined as Rectangle
+ property QtObject undefinedAsDummy: undefined as Dummy
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml
new file mode 100644
index 0000000000..5b254fe494
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump.qml
@@ -0,0 +1,33 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ function t1() {
+ let i = 0
+ let foo = false
+ if (true) {
+ for (i = 0; i < 42 ; ++i) {}
+ } else {
+ console.log(foo)
+ }
+ }
+
+ function t2() {
+ let foo = false
+ if (false) {
+ while(Math.random() < 0.5) {}
+ } else {
+ console.log(foo)
+ }
+ }
+
+ function t3() {
+ let foo = false
+ if (Math.random() < 0.5) {
+ console.log(foo)
+ } else {
+ while(Math.random() < 0.5) {}
+ console.log(foo)
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml
new file mode 100644
index 0000000000..997ff68736
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/basicBlocksWithBackJump_infinite.qml
@@ -0,0 +1,13 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ function infinite() {
+ let foo = false
+ if (true) {
+ while (true) {}
+ } else {
+ console.log(foo)
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml b/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml
new file mode 100644
index 0000000000..bc9506a533
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/basicDTZ.qml
@@ -0,0 +1,40 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: win
+ property real width: 640
+ property real height: 480
+ property string title: "none"
+
+ function t1(): void {
+ const w = win.width
+ const h = win.height
+
+ if (w > 0 && h > 0) {
+ const wByH = h / 3.0 * 4.0
+ }
+ }
+
+ function t2(): void {
+ let i = 42
+ win.title = "Foo " + i
+
+ for (let j = 0; j < 10; j++) {
+ win.title = "Bar " + j
+ }
+
+ for (let k = 0; k < i; k++) {
+ win.title = "Baz " + k
+ }
+ }
+
+ function t3(): void {
+ let v1 = 1
+ let v2 = 2
+
+ if (true) {
+ v1 = v2
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp
index 048459f03a..10a2c90b38 100644
--- a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp
+++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "birthdayparty.h"
@@ -36,6 +36,23 @@ Person *BirthdayParty::guest(int index) const
return m_guests.at(index);
}
+QStringList BirthdayParty::guestNames() const
+{
+ QStringList names;
+ for (Person *guest: m_guests)
+ names.append(guest->name());
+ return names;
+}
+
+QVariantList BirthdayParty::stuffs() const
+{
+ return QVariantList({
+ QVariant::fromValue(objectName()),
+ QVariant::fromValue(m_guests.size()),
+ QVariant::fromValue(m_host)
+ });
+}
+
void BirthdayParty::invite(const QString &name)
{
auto *person = new Person(this);
diff --git a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
index a6871c39b1..8dd640c67f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
+++ b/tests/auto/qml/qmlcppcodegen/data/birthdayparty.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef BIRTHDAYPARTY_H
#define BIRTHDAYPARTY_H
@@ -52,7 +52,6 @@ private:
};
struct Foozle {
- Q_GADGET
int foo = 1;
};
@@ -61,6 +60,8 @@ class BirthdayParty : public QObject
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
+ Q_PROPERTY(QStringList guestNames READ guestNames FINAL)
+ Q_PROPERTY(QVariantList stuffs READ stuffs FINAL)
QML_ELEMENT
QML_ATTACHED(BirthdayPartyAttached)
QML_EXTENDED(BirthDayPartyExtended)
@@ -74,6 +75,9 @@ public:
int guestCount() const;
Person *guest(int) const;
+ QStringList guestNames() const;
+ QVariantList stuffs() const;
+
Q_INVOKABLE void invite(const QString &name);
static BirthdayPartyAttached *qmlAttachedProperties(QObject *object);
diff --git a/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml b/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml
new file mode 100644
index 0000000000..6b8ebb3f38
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/boolCoercions.qml
@@ -0,0 +1,45 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property rect a
+ property bool t1: a
+
+ property int c: 1
+ property bool t2: c
+
+ property url e: "qrc:/ab/c.txt"
+ property bool t3: e
+
+ property string f: "a"
+ property bool t4: f
+
+ property var j: 1
+ property bool t5: j
+
+ id: k
+ property bool t6: k
+
+ property date l
+ property bool t7: l
+
+ property url m
+ property bool t8: m
+
+
+
+ property int b: 0
+ property bool f1: b
+
+ property QtObject d: null
+ property bool f2: d
+
+ property string g
+ property bool f3: g
+
+ property var h: undefined
+ property bool f4: h
+
+ property var i: null
+ property bool f5: i
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml b/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml
new file mode 100644
index 0000000000..b2d4d7c5d0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/boolPointerMerge.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import TestTypes
+import QtQuick
+
+Loader {
+ id: self
+ source: "BaseMember.qml"
+ property int ppp: -99
+
+ onItemChanged: {
+ var base = item as BaseMember;
+ if (base)
+ base.ppp = ppp
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/childobject.qml b/tests/auto/qml/qmlcppcodegen/data/childobject.qml
index 3775ee16bc..76ad8fbbb2 100644
--- a/tests/auto/qml/qmlcppcodegen/data/childobject.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/childobject.qml
@@ -4,6 +4,19 @@ import TestTypes
QtObject {
property ObjectWithMethod child: ObjectWithMethod {
objectName: "kraut"
+
+ function doString() { overloaded("string"); }
+ function doNumber() { overloaded(5.2); }
+ function doArray() { overloaded({a: 2, b: 3, c: 3}); }
+
+ function doString2() { overloaded2("string"); }
+ function doNumber2() { overloaded2(5.2); }
+
+ // Artificially pass an extra argument to avoid choosing the "string" overload.
+ // Unfortunately this is still order-dependent on the metaobject level.
+ function doArray2() { overloaded2({a: 2, b: 3, c: 3}, 1); }
+
+ function doFoo() { foo(this); }
}
objectName: child.objectName
property int doneThing: child.doThing()
diff --git a/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml b/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml
new file mode 100644
index 0000000000..3d40ffee62
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/compareOriginals.qml
@@ -0,0 +1,39 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ component Variable: QtObject {
+ property int value: 4
+ }
+
+ property Variable first: Variable {}
+ property Variable last: Variable {
+ id: last
+ }
+
+ property int compareOriginals: {
+ var matches = 0;
+ for (var i = 0; i < 6; i++) {
+ first.value = i; // do a shadowed assignment
+ if (last.value != i)
+ ++matches
+ }
+ return matches;
+ }
+
+ property bool optionalThis: {
+ var a
+ if (2 == 2)
+ a = this
+ else
+ a = undefined
+
+ var b
+ if (2 == 2)
+ b = this
+ else
+ b = undefined
+
+ return a === b
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml b/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml
new file mode 100644
index 0000000000..423cd55ed4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/comparisonTypes.qml
@@ -0,0 +1,54 @@
+import QtQml
+
+QtObject {
+ component Variable: QtObject {
+ property int value: 4
+ }
+
+ component VariableShadow: Variable {
+ property string value: "1"
+ }
+
+ property Variable last: VariableShadow {}
+
+ function find(n: int) : int {
+ var found = 0
+ for (var i = 0; i < n; i++) {
+ if (last.value == i)
+ ++found
+ }
+ return found;
+ }
+
+ function findStrict(n: int) : int {
+ var found = 0
+ for (var i = 0; i < n; i++) {
+ if (last.value === i)
+ ++found
+ }
+ return found;
+ }
+
+ function findNot(n: int) : int {
+ var found = 0
+ for (var i = 0; i < n; i++) {
+ if (last.value != i)
+ ++found
+ }
+ return found;
+ }
+
+ function findNotStrict(n: int) : int {
+ var found = 0
+ for (var i = 0; i < n; i++) {
+ if (last.value !== i)
+ ++found
+ }
+ return found;
+ }
+
+ property int found: find(3)
+ property int foundStrict: findStrict(10)
+ property int foundNot: findNot(3)
+ property int foundNotStrict: findNotStrict(10)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml b/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml
new file mode 100644
index 0000000000..a80af89ddd
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ function a() { b() }
+ function b() { c() }
+ function c() { console.trace() }
+ Component.onCompleted: a()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml b/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml
new file mode 100644
index 0000000000..b2e7b40c00
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/conversionInDeadCode.qml
@@ -0,0 +1,32 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ // This does not look like dead code, but each access to 'result' generates a
+ // DeadTemoralZoneCheck instruction that we ignore when compiling to C++
+ // after checking statically that 'result' is alive throughout the function.
+ // Therefore, this function is a torture test for the dead code elimination.
+ function calc(a: int, b: int) : int {
+ let result = a;
+ if (b < 0) {
+ if (b < -1)
+ result -= b;
+ if (b < -2)
+ result /= b;
+ } else {
+ if (b > 1)
+ result *= b;
+ if (b > 2)
+ result += b;
+ }
+ return result;
+ }
+
+ property int a: calc(10, -3);
+ property int b: calc(10, -2);
+ property int c: calc(10, -1);
+ property int d: calc(10, 0);
+ property int e: calc(10, 1);
+ property int f: calc(10, 2);
+ property int g: calc(10, 3);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
index c3a9414ae2..d0d1fc9b8f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml
@@ -94,6 +94,8 @@ Item {
}
function qtest_signalHandlerName(sn) {
+ // Warning: to not test for signal handlers like this in actual code.
+ // Use the helper methods in QQmlSignalNames instead.
if (sn.substr(0, 2) === "on" && sn[2] === sn[2].toUpperCase())
return sn
return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1)
diff --git a/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml b/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml
new file mode 100644
index 0000000000..f7c2cc4058
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/convertPrimitiveToVar.qml
@@ -0,0 +1,18 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: foo
+
+ property int offsetValue
+
+ function send(data : variant) {
+ }
+
+ Component.onCompleted: () => {
+ let deltaOffset = 42
+ deltaOffset -= 1
+ foo.offsetValue = deltaOffset
+ foo.send({offset: deltaOffset})
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h
new file mode 100644
index 0000000000..459dd62374
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H
+#define CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+class Moo485 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(int uid READ uid CONSTANT FINAL)
+
+public:
+ explicit Moo485(QObject *parent = nullptr) : QObject(parent) { }
+ int uid() const { return 4711; }
+};
+
+class Foo485 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(quint16 uid MEMBER m_uid FINAL)
+
+public:
+ explicit Foo485(QObject *parent = nullptr) : QObject(parent) { }
+ quint16 m_uid = 0;
+};
+
+#endif // CONVERTQJSPRIMITIVEVALUETOINTEGRAL_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml
new file mode 100644
index 0000000000..6a15d6f775
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/convertQJSPrimitiveValueToIntegral.qml
@@ -0,0 +1,13 @@
+import QtQuick
+import TestTypes
+
+
+Item {
+ id: root
+
+ property Moo485 moo
+
+ readonly property Foo485 foo: Foo485 {
+ uid: root.moo.uid ?? 0xFFFF
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml b/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml
new file mode 100644
index 0000000000..6eb14bba57
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/convertToOriginalReadAcumulatorForUnaryOperators.qml
@@ -0,0 +1,13 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: self
+ property int i: 0
+ property Planner planner: null
+
+ function satisfy(mark: int) {
+ planner.addPropagate(mark);
+ i = +mark;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h b/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h
index eecc3d968e..812415ae24 100644
--- a/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h
+++ b/tests/auto/qml/qmlcppcodegen/data/cppbaseclass.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CPPBASECLASS_H
#define CPPBASECLASS_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml b/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml
new file mode 100644
index 0000000000..fc8a34da71
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dateConstruction.qml
@@ -0,0 +1,20 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property date now: new Date()
+ property date now2: new Date(now)
+ property date fromString: new Date("1995-12-17T03:24:00")
+ property date fromNumber: new Date(777)
+ property date fromPrimitive: new Date(objectName.length === 0 ? 57 : "1997-02-13T13:04:12")
+ property date from2: new Date(1996, 1)
+ property date from3: new Date(1996, 2, 3)
+ property date from4: new Date(1996, 3, 4, 5)
+ property date from5: new Date(1996, 4, 5, 6, 7)
+ property date from6: new Date(1996, 5, 6, 7, 8, 9)
+ property date from7: new Date(1996, 6, 7, 8, 9, 10, 11)
+ property date from8: new Date(1996, 7, 8, 9, 10, 11, 12, 13)
+
+ property date withUnderflow: new Date(-4, -5, -6, -7, -8, -9, -10)
+ property date invalid: new Date("foo", "bar")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml b/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml
index 38a34f7487..5c0a426466 100644
--- a/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/dateConversions.qml
@@ -9,12 +9,17 @@ QtObject {
property string dateString: date
property string timeString: time
+ property real dateNumber: date
+ property real timeNumber: time
+
function shuffle() {
Druggeljug.myDate = date;
Druggeljug.myTime = time;
dateString = Druggeljug.myDate;
timeString = Druggeljug.myTime;
+ dateNumber = Druggeljug.myDate;
+ timeNumber = Druggeljug.myTime;
}
function fool() {
@@ -22,4 +27,9 @@ QtObject {
Druggeljug.myTime = Druggeljug.myDate;
Druggeljug.myDate = tmp;
}
+
+ function invalidate() {
+ date = new Date("foo", "bar");
+ time = new Date("bar", "foo");
+ }
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml b/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml
new file mode 100644
index 0000000000..b30e0124c8
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dialogButtonBox.qml
@@ -0,0 +1,8 @@
+pragma Strict
+import QtQuick.Controls.Basic
+
+ApplicationWindow {
+ footer: DialogButtonBox {
+ standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h b/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h
new file mode 100644
index 0000000000..ace319f91f
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/dummyobjekt.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef DUMMYOBJEKT_H
+#define DUMMYOBJEKT_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+#if QT_DEPRECATED_SINCE(6, 4)
+class DummyObjekt : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ enum Test {
+ TestA = 1,
+ TestB
+ };
+ Q_ENUM(Test)
+
+ // Deliberately not default constructible
+ explicit DummyObjekt(QObject *parent) : QObject(parent) {}
+};
+#endif
+
+#endif // DUMMYOBJEKT_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h
index 64c2850bda..d8358b6a9c 100644
--- a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h
+++ b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h
@@ -1,38 +1,48 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DYNAMICMETA_H
#define DYNAMICMETA_H
#include <private/qobject_p.h>
+#include <private/qmetaobjectbuilder_p.h>
#include <QtQmlIntegration/qqmlintegration.h>
-struct FreeDeleter {
- void operator()(QMetaObject *meta) { free(meta); }
-};
-
template<typename T>
class MetaObjectData : public QDynamicMetaObjectData
{
Q_DISABLE_COPY_MOVE(MetaObjectData)
public:
- MetaObjectData() = default;
- ~MetaObjectData() = default;
+ MetaObjectData()
+ {
+ QMetaObjectBuilder builder;
+ builder.setSuperClass(&T::staticMetaObject);
+ builder.setFlags(builder.flags() | DynamicMetaObject);
+ metaObject = builder.toMetaObject();
+ };
+
+ ~MetaObjectData() {
+ free(metaObject);
+ };
QMetaObject *toDynamicMetaObject(QObject *) override
{
- return const_cast<QMetaObject *>(&T::staticMetaObject);
+ return metaObject;
}
int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override
{
return o->qt_metacall(call, idx, argv);
}
+
+ QMetaObject *metaObject = nullptr;
};
class DynamicMeta : public QObject
{
Q_OBJECT
Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged FINAL)
+ Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue NOTIFY valueChanged FINAL)
+ Q_PROPERTY(qreal shadowable READ shadowable CONSTANT)
QML_ELEMENT
public:
@@ -54,11 +64,26 @@ public:
Q_INVOKABLE int bar(int baz) { return baz + 12; }
+ qreal value() const { return m_value; }
+ qreal shadowable() const { return 25; }
+
+public slots:
+ void resetValue() { setValue(0); }
+ void setValue(qreal value)
+ {
+ if (m_value == value)
+ return;
+ m_value = value;
+ emit valueChanged();
+ }
+
Q_SIGNALS:
void fooChanged();
+ void valueChanged();
private:
int m_foo = 0;
+ qreal m_value = 0;
};
class DynamicMetaSingleton : public DynamicMeta
diff --git a/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml
new file mode 100644
index 0000000000..571a000199
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+QtObject {
+ id: mainItem
+
+ function arg(item: Binding) : QtObject { return item }
+ function ret(item: QtObject) : Binding { return item }
+
+ property QtObject a: arg(mainItem);
+ property QtObject b: ret(mainItem);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml b/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml
index fee2c50e15..61ddd2162d 100644
--- a/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/enumConversion.qml
@@ -1,7 +1,12 @@
+pragma Strict
import TestTypes
MyType {
id: root
+
+ property alias status: root.a
+
property int test: myEnumType.type
property bool test_1: myEnumType.type
+ objectName: root.status + "m"
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml b/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml
new file mode 100644
index 0000000000..3176fde315
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enumFromBadSingleton.qml
@@ -0,0 +1,6 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ objectName: "Dummy: " + DummyObjekt.TestA
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml b/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml
new file mode 100644
index 0000000000..2ef37cbdf0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enumMarkedAsFlag.qml
@@ -0,0 +1,6 @@
+import QML
+import TestTypes
+
+QtObject {
+ property int flagValue: ControlFlags.Both
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml b/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml
index f9a4eb144b..6a57b0e64a 100644
--- a/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/enumProblems.qml
@@ -11,4 +11,9 @@ QtObject {
readonly property FooThing fighter: root.f.get(Foo.Fighter)
readonly property FooThing bar: root.f.get(Foo.Component)
}
+
+ property int a: FooFactory.B
+ property int b: f.t8
+ property int c: FooFactory.D
+ property int d: f.t16
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumProperty.h b/tests/auto/qml/qmlcppcodegen/data/enumProperty.h
index 8c13e860a3..8d6405a059 100644
--- a/tests/auto/qml/qmlcppcodegen/data/enumProperty.h
+++ b/tests/auto/qml/qmlcppcodegen/data/enumProperty.h
@@ -15,20 +15,87 @@ public:
Tri = 0x04,
};
Q_ENUM(MyEnum)
- Q_PROPERTY(MyEnum type READ type)
+ Q_PROPERTY(MyEnum type READ type CONSTANT)
MyEnum type() const { return MyEnum::Tri; }
};
class MyType : public QObject
{
Q_OBJECT
- Q_PROPERTY(MyEnumType myEnumType READ myEnumType)
+ Q_PROPERTY(MyEnumType myEnumType READ myEnumType CONSTANT)
+ Q_PROPERTY(A a READ a WRITE setA NOTIFY aChanged FINAL)
QML_ELEMENT
public:
+ enum A { B, C, D };
+ Q_ENUM(A)
+
MyEnumType myEnumType() const { return m_type; }
+ A a() const { return m_a; }
+ void setA(A newA)
+ {
+ if (m_a == newA)
+ return;
+ m_a = newA;
+ emit aChanged();
+ }
+
+ Q_INVOKABLE int method(quint16, const QString &) { return 24; }
+ Q_INVOKABLE int method(quint16, MyType::A a) { return int(a); }
+
+Q_SIGNALS:
+ void aChanged();
+
private:
MyEnumType m_type;
+ A m_a = B;
+};
+
+class CommunicationPermission
+{
+ Q_GADGET
+public:
+ enum CommunicationMode : quint8 {
+ Access = 0x01,
+ Advertise = 0x02,
+ Default = Access | Advertise,
+ };
+ Q_DECLARE_FLAGS(CommunicationModes, CommunicationMode)
+ Q_FLAG(CommunicationModes)
+
+ void setCommunicationModes(CommunicationModes modes) { m_modes = modes; }
+ CommunicationModes communicationModes() const { return m_modes; }
+
+private:
+ CommunicationModes m_modes;
+};
+
+struct QQmlCommunicationPermission : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(CommunicationPermission)
+ QML_EXTENDED_NAMESPACE(CommunicationPermission)
+ Q_PROPERTY(CommunicationPermission::CommunicationModes communicationModes READ communicationModes WRITE setCommunicationmodes NOTIFY communicationModesChanged)
+
+public:
+ CommunicationPermission::CommunicationModes communicationModes() const
+ {
+ return m_permission.communicationModes();
+ }
+
+ void setCommunicationmodes(const CommunicationPermission::CommunicationModes &newCommunicationModes)
+ {
+ if (communicationModes() == newCommunicationModes)
+ return;
+ m_permission.setCommunicationModes(newCommunicationModes);
+ emit communicationModesChanged();
+ }
+
+signals:
+ void communicationModesChanged();
+
+private:
+ CommunicationPermission m_permission;
};
#endif // ENUMPROPERTY_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/enumproblems.h b/tests/auto/qml/qmlcppcodegen/data/enumproblems.h
index 08a00acf7e..36f97bec5a 100644
--- a/tests/auto/qml/qmlcppcodegen/data/enumproblems.h
+++ b/tests/auto/qml/qmlcppcodegen/data/enumproblems.h
@@ -1,10 +1,11 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef ENUMPROBLEMS_H
#define ENUMPROBLEMS_H
#include <QObject>
+#include <QtCore/qflags.h>
#include <QtQml/qqml.h>
#include <QtQml/qqmlregistration.h>
@@ -45,9 +46,74 @@ class FooThingWrapper {
class FooFactory : public QObject {
Q_OBJECT
QML_ELEMENT
+ Q_PROPERTY(T8 t8 READ t8 CONSTANT FINAL)
+ Q_PROPERTY(T16 t16 READ t16 CONSTANT FINAL)
public:
+ enum T8: qint8 {
+ A, B, C
+ };
+ Q_ENUM(T8)
+
+ enum T16: qint16 {
+ D = 500, E, F
+ };
+ Q_ENUM(T16)
+
+ T8 t8() const { return C; }
+ T16 t16() const { return E; }
+
Q_INVOKABLE Foo* get(Foo::Type type) const { return new Foo(type); }
};
+class ControlFlags : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+ QML_UNCREATABLE("Flag Container Class")
+public:
+
+ enum Option {
+ ControlA = 0x1,
+ ControlB = 0x2,
+ Both = ControlA | ControlB
+ };
+
+ Q_DECLARE_FLAGS(Options, Option)
+ Q_FLAG(Option)
+};
+
+class ScopedEnum : public QObject {
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Data)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
+
+public:
+ enum class DType {
+ A = 27, B
+ };
+ Q_ENUM(DType)
+
+ enum EType {
+ C = 7, D
+ };
+ Q_ENUM(EType)
+};
+
+class UnscopedEnum : public QObject {
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Data2)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "true")
+
+public:
+ enum class DType {
+ A = 26, B
+ };
+ Q_ENUM(DType)
+
+ enum EType {
+ C = 6, D
+ };
+ Q_ENUM(EType)
+};
+
#endif // ENUMPROBLEMS_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml b/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml
new file mode 100644
index 0000000000..55ac68592c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/equalityQUrl.qml
@@ -0,0 +1,16 @@
+pragma Strict
+import QtQuick
+
+Item {
+ property url emptyUrl: ""
+ property url sourceUrl: "some/path/file.png"
+
+ property bool emptyUrlStrict: emptyUrl === Qt.resolvedUrl("")
+ property bool emptyUrlWeak: emptyUrl == Qt.resolvedUrl("")
+
+ property bool sourceUrlStrict: sourceUrl === Qt.url("some/path/file.png");
+ property bool sourceUrlWeak: sourceUrl == Qt.url("some/path/file.png");
+
+ property bool sourceIsNotEmptyStrict: sourceUrl !== emptyUrl
+ property bool sourceIsNotEmptyWeak: sourceUrl != emptyUrl
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml b/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml
new file mode 100644
index 0000000000..cd0c433ea9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/equalityTestsWithNullOrUndefined.qml
@@ -0,0 +1,14 @@
+pragma Strict
+
+import QtQml
+import QtQuick
+
+Window {
+ property var foo
+ Component.onCompleted: {
+ console.log(foo !== null)
+ console.log(foo === null)
+ console.log(foo !== undefined)
+ console.log(foo === undefined)
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml b/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml
new file mode 100644
index 0000000000..13855356f2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/exceptionFromInner.qml
@@ -0,0 +1,10 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property QtObject theNull: null
+
+ function doFail() : string { return theNull.objectName }
+ function delegateFail() : string { doFail() }
+ function disbelieveFail() : string { delegateFail() }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml b/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml
new file mode 100644
index 0000000000..e8f51984b8
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/extra/extra.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+B {
+ r: ({x: 4, y: 6, width: 8, height: 10})
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml b/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml
new file mode 100644
index 0000000000..e8f51984b8
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/extra2/extra.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+B {
+ r: ({x: 4, y: 6, width: 8, height: 10})
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/failures.qml b/tests/auto/qml/qmlcppcodegen/data/failures.qml
index f90fb44fe1..3b0e4908ab 100644
--- a/tests/auto/qml/qmlcppcodegen/data/failures.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/failures.qml
@@ -9,6 +9,7 @@ QtObject {
property string attachedForNasty: Nasty.objectName
property Nasty nasty: Nasty {
+ id: theNasty
objectName: Component.objectName
}
@@ -30,18 +31,10 @@ QtObject {
Component.onCompleted: doesNotExist()
- property string aString: self + "a"
-
property BirthdayParty party: BirthdayParty {
onPartyStarted: (foozle) => { objectName = foozle }
}
- signal foo()
- signal bar()
-
- // Cannot assign potential undefined
- onFoo: objectName = self.bar()
-
property int enumFromGadget1: GadgetWithEnum.CONNECTED + 1
property int enumFromGadget2: TT2.GadgetWithEnum.CONNECTED + 1
@@ -62,4 +55,55 @@ QtObject {
let a;
return a;
}
+
+ function getText(myArr: list<string>): string {
+ myArr.shiftss()
+ }
+
+ function readTracks(metadataList : list<badType>): int {
+ return metadataList.length
+ }
+
+ function dtzFail() : int {
+ for (var a = 10; a < 20; ++a) {
+ switch (a) {
+ case 11:
+ let b = 5;
+ break;
+ case 10:
+ console.log(b);
+ break;
+ }
+ }
+ return a;
+ }
+
+ // TODO: Drop these once we can manipulate QVariant-wrapped lists.
+ property list<withLength> withLengths
+ property int l: withLengths.length
+ property withLength w: withLengths[10]
+
+ property unconstructibleWithLength uwl: 12 + 1
+
+ // Cannot generate code for getters
+ property rect r3: ({ get x() { return 42; }, y: 4 })
+
+ property int nonIterable: {
+ var result = 1;
+ for (var a in Component)
+ ++result;
+ return result;
+ }
+
+ property alias selfself: self
+ property alias nastyBad: theNasty.bad
+ function writeToUnknown() : int {
+ self.selfself.nastyBad = undefined;
+ return 5;
+ }
+
+ readonly property int someNumber: 10
+ function writeToReadonly() { someNumber = 20 }
+
+ property var silly: [,0]
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml b/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml
new file mode 100644
index 0000000000..44b55e245a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml
@@ -0,0 +1,23 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+DynamicMeta {
+ id: self
+ value: 999
+
+ property double notResettable: 10
+ property double notResettable2: { return undefined }
+
+ property DynamicMeta shadowing: DynamicMeta {
+ property var shadowable: undefined
+ }
+
+ function doReset() { self.value = undefined }
+ function doReset2() { self.value = shadowing.shadowable }
+ function doNotReset() { self.notResettable = undefined }
+
+ signal aaa()
+ signal bbb()
+ onAaa: objectName = self.bbb()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml b/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml
index b8bd466717..6634982de2 100644
--- a/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/fileDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml b/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml
new file mode 100644
index 0000000000..3ea5cf98db
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/flagEnum.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import TestTypes
+
+CommunicationPermission {
+ communicationModes: CommunicationPermission.Access
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h
index 839e026b77..3c81cd2e7f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h
+++ b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef GADGETWITHENUM_H
#define GADGETWITHENUM_H
@@ -26,4 +26,40 @@ namespace GadgetWithEnumWrapper {
QML_NAMED_ELEMENT(NamespaceWithEnum)
};
+struct Gadget
+{
+ Q_GADGET
+ QML_VALUE_TYPE(gadget)
+
+public:
+ enum class Prop1 { High, low, VeryHigh, VeryLow };
+ Q_ENUM(Prop1)
+
+ enum class Prop2 { VeryHigh, High, low, VeryLow };
+ Q_ENUM(Prop2)
+};
+
+class Backend : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+ Q_PROPERTY(prop priority READ priority FINAL CONSTANT)
+ Q_PROPERTY(Gadget gadget READ gadget FINAL CONSTANT)
+ Q_CLASSINFO("RegisterEnumsFromRelatedTypes", "false")
+
+public:
+ enum prop { High, low, VeryHigh, VeryLow };
+ Q_ENUM(prop)
+
+ explicit Backend(QObject *parent = nullptr) : QObject(parent) {}
+
+ prop priority() const { return m_priority; }
+ Gadget gadget() const { return m_gadget; }
+
+private:
+ prop m_priority = low;
+ Gadget m_gadget;
+};
+
#endif // GADGETWITHENUM_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h
new file mode 100644
index 0000000000..e8a24cd707
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.h
@@ -0,0 +1,42 @@
+#ifndef GETOPTIONALLOOKUP_H
+#define GETOPTIONALLOOKUP_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class GOL_Object : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(int i READ i CONSTANT FINAL)
+ Q_PROPERTY(QString s READ s CONSTANT FINAL)
+ Q_PROPERTY(GOL_Object *childA READ childA WRITE setChildA NOTIFY childAChanged FINAL)
+ Q_PROPERTY(Enum e READ e CONSTANT FINAL)
+
+public:
+ GOL_Object(QObject *parent = nullptr) : QObject(parent) { }
+
+ int i() const { return m_i; }
+ void setI(int i) { m_i = i; }
+
+ QString s() const { return m_s; }
+ void setS(QString s) { m_s = s; }
+
+ GOL_Object *childA() const { return m_childA; }
+ void setChildA(GOL_Object *a) { m_childA = a; }
+
+ enum Enum { V1, V2 };
+ Q_ENUM(Enum)
+ Enum e() const { return Enum::V2; }
+
+signals:
+ void childAChanged();
+
+private:
+ int m_i = 5;
+ QString m_s = "6";
+ GOL_Object *m_childA = nullptr;
+};
+
+#endif // GETOPTIONALLOOKUP_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml
new file mode 100644
index 0000000000..ee360d7142
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml
@@ -0,0 +1,34 @@
+pragma Strict
+pragma ValueTypeBehavior: Addressable
+
+import QtQuick
+
+GOL_Object {
+ id: root
+
+ property rect r: Qt.rect(0, 0, 20, 50)
+ property point p: Qt.point(0, -10)
+ property var v: Qt.point(5, 5)
+ property var u: undefined
+
+ property int to1: root?.i
+ property string to2: root?.s
+ property GOL_Object to3: root?.childA
+ property var to4: root.childA?.i
+ property var to5: (undefined as GOL_Object)?.childA
+ property int to6: (root as GOL_Object)?.s.length
+
+ property int tv1: root.r?.bottom
+ property int tv2: root.p?.y
+
+ property int te1: root?.e
+ property int te2: GOL_Object?.V2
+ property bool te3: root?.e === GOL_Object?.V1
+ property bool te4: root?.e === GOL_Object?.V2
+
+ property int tc1: root?.p.y
+ property int tc2: root.r?.x
+
+ property var tc4: root?.childA?.s
+ property var tc5: root.childA?.s
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/idAccess.qml b/tests/auto/qml/qmlcppcodegen/data/idAccess.qml
index 2090926872..6ca1f7f66b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/idAccess.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/idAccess.qml
@@ -1,3 +1,4 @@
+pragma Strict
import QtQuick
Item {
@@ -11,5 +12,9 @@ Item {
Text {
id: ttt
+ onTextChanged: {
+ root.objectName = "dead"
+ ttt.objectName = "context"
+ }
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml b/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml
new file mode 100644
index 0000000000..640e2bc22a
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/ignoredFunctionReturn.qml
@@ -0,0 +1,14 @@
+import QtQuick
+
+Item {
+ id: root
+
+ Component {
+ id: comp
+ Rectangle {
+ color: "blue"
+ }
+ }
+
+ Component.onCompleted: comp.createObject(root, {"width": 200, "height": 200})
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml b/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml
new file mode 100644
index 0000000000..de31527e5b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/indirectlyShadowable.qml
@@ -0,0 +1,39 @@
+import QtQml
+
+QtObject {
+ id: self
+ objectName: "self"
+
+ component Inner : QtObject {
+ property QtObject shadowable: QtObject {
+ objectName: "shadowable"
+ }
+ }
+
+ component Outer : QtObject {
+ property Inner inner: Inner {}
+ }
+
+ component Evil : Outer {
+ property string inner: "evil"
+ }
+
+ property Outer outer: Outer {}
+ property Outer evil: Evil {}
+
+ property QtObject notShadowable: QtObject {
+ objectName: "notShadowable"
+ }
+
+ function getInnerShadowable() {
+ notShadowable = outer.inner.shadowable;
+ }
+
+ function setInnerShadowable() {
+ outer.inner.shadowable = self;
+ }
+
+ function turnEvil() {
+ outer = evil;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml b/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml
new file mode 100644
index 0000000000..e255f4e8f4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/intToEnum.qml
@@ -0,0 +1,7 @@
+pragma Strict
+import TestTypes
+
+MyType {
+ a: myEnumType.type === 4 ? 2 : 1
+ property int b: method("12", "hh")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/interactive.qml b/tests/auto/qml/qmlcppcodegen/data/interactive.qml
index e857df96e7..be5e5f0d40 100644
--- a/tests/auto/qml/qmlcppcodegen/data/interactive.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/interactive.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
pragma Strict
import QtQuick 2.9
diff --git a/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml b/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml
new file mode 100644
index 0000000000..7304b7a6b9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/internalConversion.qml
@@ -0,0 +1,16 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property QtObject offset: QtObject {
+ id: a
+ property string mark
+ }
+
+ function markInputs(mark: string) {
+ offset.objectName = mark;
+ a.mark = mark;
+ }
+
+ Component.onCompleted: markInputs("hello")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/invisible.h b/tests/auto/qml/qmlcppcodegen/data/invisible.h
index a385ee975f..4f4ebb87ad 100644
--- a/tests/auto/qml/qmlcppcodegen/data/invisible.h
+++ b/tests/auto/qml/qmlcppcodegen/data/invisible.h
@@ -1,11 +1,12 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef INVISIBLE_H
#define INVISIBLE_H
#include <QtCore/qobject.h>
#include <QtQmlIntegration/qqmlintegration.h>
+#include <QtQml/qqmllist.h>
class Invisible : public QObject
{
@@ -45,6 +46,39 @@ class DerivedFromInvisible : public Invisible
{
Q_OBJECT
QML_ELEMENT
+ Q_PROPERTY(double implicitWidth MEMBER m_implicitWidth NOTIFY implicitWidthChanged FINAL)
+public:
+ DerivedFromInvisible(QObject *parent = nullptr) : Invisible(parent) {}
+
+signals:
+ void implicitWidthChanged();
+
+private:
+ double m_implicitWidth = 27;
+};
+
+class WithListPropertyOfDerivedFromInvisible : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QQmlListProperty<DerivedFromInvisible> children READ children NOTIFY childrenChanged FINAL)
+
+public:
+ WithListPropertyOfDerivedFromInvisible(QObject *parent = nullptr) : QObject(parent)
+ {
+ m_children.append(new DerivedFromInvisible(this));
+ }
+
+ QQmlListProperty<DerivedFromInvisible> children()
+ {
+ return QQmlListProperty<DerivedFromInvisible>(this, &m_children);
+ }
+
+signals:
+ void childrenChanged();
+
+private:
+ QList<DerivedFromInvisible *> m_children;
};
#endif // INVISIBLE_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/iteration.qml b/tests/auto/qml/qmlcppcodegen/data/iteration.qml
new file mode 100644
index 0000000000..8632eefa1b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/iteration.qml
@@ -0,0 +1,20 @@
+pragma Strict
+
+import QtQml
+
+QtObject {
+ property list<int> ints: [3, 4, 5]
+ property list<QtObject> objects: [
+ QtObject { objectName: "a" },
+ QtObject { objectName: "b" },
+ QtObject { objectName: "c" }
+ ]
+
+ Component.onCompleted: {
+ for (var a in objects) {
+ objectName += objects[a].objectName;
+ for (var b in ints)
+ objectName += ints[b];
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml
new file mode 100644
index 0000000000..ff372bca45
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethods.qml
@@ -0,0 +1,28 @@
+pragma Strict
+import QML
+
+QtObject {
+ id: self
+
+ property QtObject l1: QtObject { objectName: "klaus" }
+ property QtObject l2: QtObject { function toString(): string { return "teil" } }
+ property QtObject l3: QtObject { }
+
+ function jsArray() : list<var> { return [l1, l2, l3, l1, l2, l3] }
+ property list<QtObject> listProperty: [l1, l2, l3, l1, l2, l3]
+
+ property string jsArrayToString: jsArray().toString()
+ property string listPropertyToString: listProperty.toString()
+
+ property bool listPropertyIncludes: listProperty.includes(l3)
+ property bool jsArrayIncludes: jsArray().includes(l3)
+
+ property string listPropertyJoin: listProperty.join()
+ property string jsArrayJoin: jsArray().join()
+
+ property int listPropertyIndexOf: listProperty.indexOf(l2)
+ property int jsArrayIndexOf: jsArray().indexOf(l2)
+
+ property int listPropertyLastIndexOf: listProperty.lastIndexOf(l3)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml
new file mode 100644
index 0000000000..7426c692fe
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsUntyped.qml
@@ -0,0 +1,17 @@
+import QML
+
+QtObject {
+ id: self
+
+ property QtObject l1
+ property QtObject l2
+ property QtObject l3
+
+ function jsArray() { return [l1, l2, l3, l1, l2, l3] }
+
+ property string jsArrayToString: jsArray().toString()
+ property bool jsArrayIncludes: jsArray().includes(l3)
+ property string jsArrayJoin: jsArray().join()
+ property int jsArrayIndexOf: jsArray().indexOf(l2)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml
new file mode 100644
index 0000000000..293e7cbda5
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParams.qml
@@ -0,0 +1,26 @@
+pragma Strict
+import QML
+
+QtObject {
+ id: self
+
+ required property int i
+ required property int j
+ required property int k
+
+ property QtObject l1: QtObject { objectName: "klaus" }
+ property QtObject l2: QtObject { function toString(): string { return "teil" } }
+ property QtObject l3: QtObject { }
+
+ function jsArray() : list<var> { return [l1, l2, l3, l1, l2, l3] }
+ property list<QtObject> listProperty: [l1, l2, l3, l1, l2, l3]
+
+ property list<QtObject> listPropertySlice: listProperty.slice(i, j)
+ property list<var> jsArraySlice: jsArray().slice(i, j)
+
+ property int listPropertyIndexOf: listProperty.indexOf(l2, i)
+ property int jsArrayIndexOf: jsArray().indexOf(l2, i)
+
+ property int listPropertyLastIndexOf: listProperty.lastIndexOf(l3, i)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3, i)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml
new file mode 100644
index 0000000000..9e928bd6f6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/jsArrayMethodsWithParamsUntyped.qml
@@ -0,0 +1,18 @@
+import QML
+
+QtObject {
+ id: self
+
+ required property int i
+ required property int j
+ required property int k
+
+ property QtObject l1
+ property QtObject l2
+ property QtObject l3
+
+ function jsArray() { return [l1, l2, l3, l1, l2, l3] }
+ property var jsArraySlice: jsArray().slice(i, j)
+ property int jsArrayIndexOf: jsArray().indexOf(l2, i)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(l3, i)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listConversion.qml b/tests/auto/qml/qmlcppcodegen/data/listConversion.qml
new file mode 100644
index 0000000000..ca86d9a1d6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listConversion.qml
@@ -0,0 +1,17 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+BirthdayParty {
+ id: self
+
+ guests: [
+ Person { name: "Horst 1" },
+ Person { name: "Horst 2" },
+ Person { name: "Horst 3" }
+ ]
+
+ property list<QtObject> o: self.guests
+ property list<string> s: self.guestNames
+ property list<var> v: self.stuffs
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml b/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml
new file mode 100644
index 0000000000..f3698d78ab
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listOfInvisible.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import TestTypes
+
+WithListPropertyOfDerivedFromInvisible {
+ property real width: children[0].implicitWidth
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listToString.qml b/tests/auto/qml/qmlcppcodegen/data/listToString.qml
new file mode 100644
index 0000000000..e9e4b85956
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listToString.qml
@@ -0,0 +1,25 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property list<string> stringList: ["one", "two"]
+ property list<int> intList: [1, 2]
+ property list<QtObject> objectList: [this, this]
+
+ Component.onCompleted: {
+ console.log(stringList)
+ console.log(stringList + "")
+
+ console.log(intList)
+ console.log(intList + "")
+
+ console.log(objectList)
+ console.log(objectList + "")
+
+ console.log(["a", "b"]);
+
+ // TODO: Cannot do this, yet, because we cannot coerce a list to string on the fly.
+ // We need to store it as list first.
+ // console.log(["a", "b"] + "");
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listprovider.h b/tests/auto/qml/qmlcppcodegen/data/listprovider.h
new file mode 100644
index 0000000000..076944b586
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listprovider.h
@@ -0,0 +1,24 @@
+#ifndef QLISTPROVIDER_H
+#define QLISTPROVIDER_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class QListProvider : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(ListProvider)
+
+public:
+ explicit QListProvider(QObject *parent = nullptr) : QObject(parent) { }
+
+ Q_INVOKABLE QList<int> intList() const
+ {
+ QList<int> list;
+ for (int i = 0; i < 3; ++i)
+ list.append(i);
+ return list;
+ }
+};
+
+#endif // QLISTPROVIDER_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/math.qml b/tests/auto/qml/qmlcppcodegen/data/math.qml
index cc6cd3741a..ad6303e682 100644
--- a/tests/auto/qml/qmlcppcodegen/data/math.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/math.qml
@@ -3,4 +3,5 @@ import QML
QtObject {
property int a: Math.max(5, 7, 9, -111)
property var b: 50 / 22
+ property real c: Math.PI * 2
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml b/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml
new file mode 100644
index 0000000000..654b699918
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mathMinMax.qml
@@ -0,0 +1,59 @@
+pragma Strict
+import QtQml
+import QtQuick
+
+Rectangle {
+ Component.onCompleted: {
+ // Math.max()
+ console.log(Math.max(1, 1));
+ console.log(Math.max(1, 2));
+ console.log(Math.max(2, 1));
+ console.log(Math.max(0, 0));
+ console.log(Math.max(-1, 0));
+ console.log(Math.max(0, -1));
+ console.log(Math.max(-1, -1));
+
+ console.log(Math.max(0, 0, 0));
+ console.log(Math.max(0, 0, 1, 0, 0, 0));
+ console.log(Math.max(-2, -1, 0, 1, 2));
+ console.log(Math.max(2, 1, 0, -1, -2));
+ console.log(Math.max(9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+ console.log(Math.max(0.0, 0.0, 0.0))
+ console.log(Math.max(-0.001, 0.001, 0.002))
+ console.log(Math.max(5.4, 1, 0.002))
+ console.log(Math.max(null, 0, -1, 8E-2, NaN, undefined, true, false, Infinity))
+ console.log(Math.max(0, -1, 8E-2, true, false, Infinity))
+ console.log(Math.max(0, -1, 8E-2, true, false))
+ console.log(Math.max(0, -1, 8E-2, false))
+ console.log(Math.max(0, -1, 8E-2, true, false, Infinity))
+ console.log(Math.max(-1, -8, null))
+ console.log(Math.max(undefined, 20, 70))
+
+ // Math.min()
+ console.log(Math.min(1, 1));
+ console.log(Math.min(1, +2));
+ console.log(Math.min(2, 1));
+ console.log(Math.min(0, 0));
+ console.log(Math.min(-1, 0));
+ console.log(Math.min(0, -1));
+ console.log(Math.min(-1, -1));
+
+ console.log(Math.min(0, 0, 0));
+ console.log(Math.min(0, 0, 1, 0, 0, 0));
+ console.log(Math.min(-2, -1, 0, 1, 2));
+ console.log(Math.min(2, 1, 0, -1, -2));
+ console.log(Math.min(9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+ console.log(Math.min(0.0, 0.0, 0.0))
+ console.log(Math.min(-0.001, 0.001, 0.002))
+ console.log(Math.min(5.4, 1, 0.002))
+ console.log(Math.min(null, 0, -1, 8E-2, NaN, undefined, true, false, Infinity))
+ console.log(Math.min(0, -1, 8E-2, true, false, Infinity))
+ console.log(Math.min(0, -1, 8E-2, true, false))
+ console.log(Math.min(0, -1, 8E-2, false))
+ console.log(Math.min(0, -1, 8E-2, true, false, Infinity))
+ console.log(Math.min(-1, -8, null))
+ console.log(Math.min(undefined, 20, 70))
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml b/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml
new file mode 100644
index 0000000000..fad74a28bd
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mathStaticProperties.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+
+import QML
+
+QtObject {
+ property double e: Math.E
+ property double ln10: Math.LN10
+ property double ln2: Math.LN2
+ property double log10e: Math.LOG10E
+ property double log2e: Math.LOG2E
+ property double pi: Math.PI
+ property double sqrt1_2: Math.SQRT1_2
+ property double sqrt2: Math.SQRT2
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml b/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml
new file mode 100644
index 0000000000..161e21e643
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mergedObjectRead.qml
@@ -0,0 +1,14 @@
+pragma Strict
+import QtQuick
+
+Item {
+ objectName: "a"
+
+ function f(arg: Item) : string {
+ // Read arg as QtObject and Item, merged into QtObject.
+ console.log(arg)
+ return arg.x
+ }
+
+ Component.onCompleted: objectName = f(null)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml b/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml
new file mode 100644
index 0000000000..5f4bb4ff87
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/mergedObjectWrite.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import QtQuick
+
+Item {
+ objectName: "a"
+
+ function f(arg: Item) : string {
+ // Write arg as Item, read it as QtObject.
+ arg.x = 5
+ console.log(arg)
+ return arg.objectName
+ }
+
+ Component.onCompleted: objectName = f(null)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml b/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml
new file mode 100644
index 0000000000..b6b7179438
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/methodOnListLookup.qml
@@ -0,0 +1,16 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+QtObject {
+ objectName: people[0].getName()
+ property list<Person> people: [
+ Person {
+ name: "no one"
+ }
+ ]
+
+ function boom() : string {
+ return people[1].getName()
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/methods.qml b/tests/auto/qml/qmlcppcodegen/data/methods.qml
index 3abd14c9c1..c045c2249b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/methods.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/methods.qml
@@ -35,6 +35,8 @@ BirthdayParty {
}
function stuff(sn) {
+ // Warning: to not test for signal handlers like this in actual code.
+ // Use the helper methods in QQmlSignalNames instead.
if (sn.substr(0, 2) === "on" && sn[2] === sn[2].toUpperCase())
return sn
return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1)
diff --git a/tests/auto/qml/qmlcppcodegen/data/multiforeign.h b/tests/auto/qml/qmlcppcodegen/data/multiforeign.h
index 290b6370f5..6c46d5ad86 100644
--- a/tests/auto/qml/qmlcppcodegen/data/multiforeign.h
+++ b/tests/auto/qml/qmlcppcodegen/data/multiforeign.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MULTIFOREIGN_H
#define MULTIFOREIGN_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml b/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml
new file mode 100644
index 0000000000..61dfdb7ca5
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/multipleCtors.qml
@@ -0,0 +1,13 @@
+pragma Strict
+
+import TestTypes
+import QtQml
+
+QtObject {
+ property rect r: Qt.rect(1, 2, 3, 4)
+ property point p: Qt.point(5, 6);
+
+ property withLength wr: r
+ property withLength wp: p
+ property withLength wi: 17
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml b/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml
new file mode 100644
index 0000000000..8fe47b7296
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/nullAccessInsideSignalHandler.qml
@@ -0,0 +1,33 @@
+import QtQuick
+
+Item {
+ id: root
+ visible: true
+
+ property var speaker
+ signal say_hello()
+
+ Component{
+ id: speakerComp
+ Text {
+ text: "HELLO"
+ function say_hello() {
+ console.log(text)
+ }
+ }
+ }
+
+ Timer {
+ interval: 1; running: true; repeat: false
+ onTriggered: root.say_hello();
+ }
+
+ Component.onCompleted:
+ {
+ root.speaker = speakerComp.createObject(root);
+
+ root.say_hello.connect(root.speaker.say_hello);
+
+ root.speaker.destroy();
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml b/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml
index 1f9af7169b..53b3697c9b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/nullComparison.qml
@@ -6,6 +6,7 @@ QtObject {
property int w: 1
property int x: 1
property int y: 1
+ property int z: 1
Component.onCompleted: {
var g = null;
if (g !== null) {
@@ -22,5 +23,15 @@ QtObject {
if (h === undefined) {
y = 5;
}
+
+ var o = this;
+ if (o != null)
+ z += 7;
+ if (o == null)
+ z += 6;
+ if (g == null)
+ z += 10;
+ if (g != null)
+ z += 20;
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml b/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml
new file mode 100644
index 0000000000..f84f93c5d2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/nullishCoalescing.qml
@@ -0,0 +1,37 @@
+pragma Strict
+pragma ValueTypeBehavior: Addressable
+
+import QtQuick
+
+GOL_Object {
+ id: root
+
+ property int p1: 5 ?? -1
+ property string p2: "6" ?? "-1"
+
+ property var p3: undefined ?? undefined
+ property var p4: undefined ?? null
+ property var p5: undefined ?? -1
+ property var p6: undefined ?? "-1"
+
+ property var p7: null ?? undefined
+ property var p8: null ?? null
+ property var p9: null ?? -1
+ property var p10: null ?? "-1"
+
+ property int p11: GOL_Object.V2 ?? "-1"
+
+ property int p12: 1 ?? 2 ?? 3
+ property int p13: "1" ?? "2" ?? "3"
+ property var p14: undefined ?? "2" ?? undefined
+ property var p15: undefined ?? undefined ?? 1
+
+ property var p16
+ property var p17
+
+ Component.onCompleted: {
+ p16 = (root.childA as GOL_Object)?.i ?? -1
+ root.childA = root
+ p17 = (root.childA as GOL_Object)?.i ?? -1
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml b/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml
index f12d3f5ea2..ec848429e8 100644
--- a/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml
@@ -4,23 +4,57 @@ import TestTypes
QtObject {
function writeValues() {
+ Druggeljug.myInt8 = 35
+ Druggeljug.myUint8 = 36
+ Druggeljug.myInt16 = 37
+ Druggeljug.myUint16 = 38
Druggeljug.myInt = 39
Druggeljug.myUint = 40
Druggeljug.myInt32 = 41
Druggeljug.myUint32 = 42
}
+ function negateValues() {
+ Druggeljug.myInt8 = -Druggeljug.myInt8;
+ Druggeljug.myUint8 = -Druggeljug.myUint8;
+ Druggeljug.myInt16 = -Druggeljug.myInt16;
+ Druggeljug.myUint16 = -Druggeljug.myUint16;
+ Druggeljug.myInt = -Druggeljug.myInt;
+ Druggeljug.myUint = -Druggeljug.myUint;
+ Druggeljug.myInt32 = -Druggeljug.myInt32;
+ Druggeljug.myUint32 = -Druggeljug.myUint32;
+ }
+
+ function shuffleValues() {
+ Druggeljug.myInt8 = Druggeljug.myUint8;
+ Druggeljug.myUint8 = Druggeljug.myInt16;
+ Druggeljug.myInt16 = Druggeljug.myUint16;
+ Druggeljug.myUint16 = Druggeljug.myInt;
+ Druggeljug.myInt = Druggeljug.myUint;
+ Druggeljug.myUint = Druggeljug.myInt32;
+ Druggeljug.myInt32 = Druggeljug.myUint32;
+ Druggeljug.myUint32 = Druggeljug.myInt8;
+ }
+
function readValueAsString(i: int) : string {
switch (i) {
- case 0: return Druggeljug.myInt;
- case 1: return Druggeljug.myUint;
- case 2: return Druggeljug.myInt32;
- case 3: return Druggeljug.myUint32;
+ case 0: return Druggeljug.myInt8;
+ case 1: return Druggeljug.myUint8;
+ case 2: return Druggeljug.myInt16;
+ case 3: return Druggeljug.myUint16;
+ case 4: return Druggeljug.myInt;
+ case 5: return Druggeljug.myUint;
+ case 6: return Druggeljug.myInt32;
+ case 7: return Druggeljug.myUint32;
default: return "";
}
}
function storeValues() {
+ Druggeljug.storeMyInt8(1330)
+ Druggeljug.storeMyUint8(1331)
+ Druggeljug.storeMyInt16(1332)
+ Druggeljug.storeMyUint16(1333)
Druggeljug.storeMyInt(1334)
Druggeljug.storeMyUint(1335)
Druggeljug.storeMyInt32(1336)
diff --git a/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml b/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml
new file mode 100644
index 0000000000..4804921b02
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/objectLookupOnListElement.qml
@@ -0,0 +1,34 @@
+pragma Strict
+import QtQuick
+
+Item {
+ id: stack
+
+ property int current: 0
+
+ onCurrentChanged: setZOrders()
+ Component.onCompleted: setZOrders()
+
+ function setZOrders() {
+ for (var i = 0; i < Math.max(stack.children.length, 3); ++i) {
+ stack.children[i].z = (i == current ? 1 : 0)
+ stack.children[i].enabled = (i == current)
+ }
+ }
+
+ function zOrders() : list<int> {
+ return [
+ stack.children[0].z,
+ stack.children[1].z,
+ stack.children[2].z
+ ]
+ }
+
+ function clearChildren() {
+ children.length = 0;
+ }
+
+ Item {}
+ Item {}
+ Item {}
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml b/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml
new file mode 100644
index 0000000000..14f84c57d0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/objectWithStringListMethod.qml
@@ -0,0 +1,7 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ readonly property ObjectWithStringListMethod foo: ObjectFactory.getFoo()
+ Component.onCompleted: console.log(foo ? foo.names().length : "-")
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h b/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h
index 348862985f..f43a0d5531 100644
--- a/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h
+++ b/tests/auto/qml/qmlcppcodegen/data/objectwithmethod.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef OBJECTWITHMETOD_H
#define OBJECTWITHMETOD_H
@@ -7,6 +7,7 @@
#include <QtCore/qobject.h>
#include <QtCore/qproperty.h>
#include <QtQml/qqml.h>
+#include <QtQml/private/qv4engine_p.h>
// Make objectName available. It doesn't exist on the builtin QtObject type
struct QObjectForeignForObjectName {
@@ -27,6 +28,20 @@ public:
Q_INVOKABLE int doThing() const { return theThing; }
QProperty<int> theThing;
QBindable<int> theThingBindable() { return QBindable<int>(&theThing); }
+
+ // The meta methods are populated back to front.
+ // The V4Function flag should not bleed into the others in either case.
+
+ Q_INVOKABLE void overloaded(QQmlV4FunctionPtr) { setObjectName(QStringLiteral("javaScript")); }
+ Q_INVOKABLE void overloaded(double) { setObjectName(QStringLiteral("double")); }
+ Q_INVOKABLE void overloaded(const QString &) { setObjectName(QStringLiteral("string")); }
+
+ Q_INVOKABLE void foo(const QString &bla) { setObjectName(bla); }
+ Q_INVOKABLE void foo(ObjectWithMethod *) { setObjectName(QStringLiteral("ObjectWithMethod")); }
+
+ Q_INVOKABLE void overloaded2(double) { setObjectName(QStringLiteral("double")); }
+ Q_INVOKABLE void overloaded2(const QString &) { setObjectName(QStringLiteral("string")); }
+ Q_INVOKABLE void overloaded2(QQmlV4FunctionPtr) { setObjectName(QStringLiteral("javaScript")); }
};
class OverriddenObjectName : public ObjectWithMethod
@@ -56,4 +71,40 @@ private:
QProperty<QString> m_objectName;
};
+class ObjectWithStringListMethod : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ explicit ObjectWithStringListMethod(QObject *parent = nullptr) : QObject(parent)
+ {
+ m_names.append("One");
+ m_names.append("Two");
+ }
+
+ Q_INVOKABLE QStringList names() const { return m_names; }
+
+private:
+ QStringList m_names;
+};
+
+class ObjectFactory : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ explicit ObjectFactory(QObject *parent = nullptr) : QObject(parent) {}
+ Q_INVOKABLE ObjectWithStringListMethod *getFoo()
+ {
+ if (!m_foo)
+ m_foo = new ObjectWithStringListMethod(this);
+ return m_foo;
+ }
+
+private:
+ ObjectWithStringListMethod *m_foo = nullptr;
+};
+
#endif // OBJECTWITHMETHOD_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml b/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml
new file mode 100644
index 0000000000..4dbc541721
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/optionalComparison.qml
@@ -0,0 +1,77 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property list<QtObject> elms: [this, null, null]
+
+ property int found: find(this)
+ property int foundNot: findNot(this)
+ property int foundStrict: findStrict(this)
+ property int foundStrictNot: findStrictNot(this)
+
+ function find(elm : QtObject) : int {
+ let found = 0;
+ for (var i = 0; i < elms.length; i++) {
+ var value = elms[i];
+ if (value == elm)
+ ++found;
+ }
+ return found;
+ }
+
+ function findNot(elm : QtObject) : int {
+ let found = 0;
+ for (var i = 0; i < elms.length; i++) {
+ var value = elms[i];
+ if (value != elm)
+ ++found;
+ }
+ return found;
+ }
+
+ function findStrict(elm : QtObject) : int {
+ let found = 0;
+ for (var i = 0; i < elms.length; i++) {
+ var value = elms[i];
+ if (value === elm)
+ ++found;
+ }
+ return found;
+ }
+
+ function findStrictNot(elm : QtObject) : int {
+ let found = 0;
+ for (var i = 0; i < elms.length; i++) {
+ var value = elms[i];
+ if (value !== elm)
+ ++found;
+ }
+ return found;
+ }
+
+ property bool optionalNull: {
+ let a // Produces a QJsPrimitiveValue we can compare to null below
+ if (objectName.length === 0)
+ a = null
+ else
+ a = undefined
+
+ return a === null
+ }
+
+ property int undefinedEqualsUndefined: {
+ var matches = 0;
+
+ // Overrun the array so that we get some undefined !== undefined.
+ for (var i = 0; i < 4; i++) {
+ var val1 = elms[i]
+ for (var j = 0; j < 4; j++) {
+ var val2 = elms[j]
+ if (!(val1 !== val2))
+ ++matches
+ }
+ }
+
+ return matches;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/person.cpp b/tests/auto/qml/qmlcppcodegen/data/person.cpp
index 4dcd6fd56f..946dfbcdaa 100644
--- a/tests/auto/qml/qmlcppcodegen/data/person.cpp
+++ b/tests/auto/qml/qmlcppcodegen/data/person.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "person.h"
@@ -98,3 +98,21 @@ void Person::setCousins(const QList<Person *> &newCousins)
m_cousins = newCousins;
emit cousinsChanged();
}
+
+QRectF Person::area() const
+{
+ return m_area;
+}
+
+void Person::setArea(const QRectF &newArea)
+{
+ if (m_area.valueBypassingBindings() == newArea)
+ return;
+ m_area = newArea;
+ emit areaChanged();
+}
+
+QBindable<QRectF> Person::areaBindable()
+{
+ return QBindable<QRectF>(&m_area);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/person.h b/tests/auto/qml/qmlcppcodegen/data/person.h
index fba4a9e9a5..c46aa757a7 100644
--- a/tests/auto/qml/qmlcppcodegen/data/person.h
+++ b/tests/auto/qml/qmlcppcodegen/data/person.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PERSON_H
#define PERSON_H
@@ -8,6 +8,35 @@
#include <QtQml/qqml.h>
#include <QtQml/qqmlengine.h>
#include <QtCore/qproperty.h>
+#include <QtCore/qrect.h>
+
+struct Inner
+{
+ Q_GADGET
+ QML_VALUE_TYPE(inner)
+ QML_STRUCTURED_VALUE
+ Q_PROPERTY(int i MEMBER i)
+
+private:
+ friend bool operator==(const Inner &lhs, const Inner &rhs) { return lhs.i == rhs.i; }
+ friend bool operator!=(const Inner &lhs, const Inner &rhs) { return !(lhs == rhs); }
+
+ int i = 11;
+};
+
+struct Outer
+{
+ Q_GADGET
+ QML_VALUE_TYPE(outer)
+ QML_STRUCTURED_VALUE
+ Q_PROPERTY(Inner inner MEMBER inner)
+
+private:
+ friend bool operator==(const Outer &lhs, const Outer &rhs) { return lhs.inner == rhs.inner; }
+ friend bool operator!=(const Outer &lhs, const Outer &rhs) { return !(lhs == rhs); }
+
+ Inner inner;
+};
// Intentionally opaque type
class Barzle : public QObject {};
@@ -22,6 +51,8 @@ class Person : public QObject
Q_PROPERTY(QList<Barzle *> barzles READ barzles WRITE setBarzles NOTIFY barzlesChanged FINAL)
Q_PROPERTY(QList<Person *> cousins READ cousins WRITE setCousins NOTIFY cousinsChanged FINAL)
Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged FINAL)
+ Q_PROPERTY(QRectF area READ area WRITE setArea NOTIFY areaChanged) // not FINAL
+ Q_PROPERTY(QRectF area2 READ area WRITE setArea NOTIFY areaChanged BINDABLE areaBindable FINAL)
QML_ELEMENT
public:
Person(QObject *parent = nullptr);
@@ -52,6 +83,12 @@ public:
QList<Person *> cousins() const;
void setCousins(const QList<Person *> &newCousins);
+ QRectF area() const;
+ void setArea(const QRectF &newArea);
+ QBindable<QRectF> areaBindable();
+
+ Q_INVOKABLE QString getName() const { return m_name; }
+
signals:
void nameChanged();
void shoeSizeChanged();
@@ -62,6 +99,10 @@ signals:
void ambiguous(int a = 9);
void cousinsChanged();
+ void objectListHappened(const QList<QObject *> &);
+ void variantListHappened(const QList<QVariant> &);
+
+ void areaChanged();
private:
QString m_name;
@@ -70,6 +111,7 @@ private:
QList<Barzle *> m_barzles;
QList<Person *> m_cousins;
QProperty<QByteArray> m_data;
+ QProperty<QRectF> m_area;
};
class BarzleListRegistration
diff --git a/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml b/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml
new file mode 100644
index 0000000000..c7103eaf05
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/qtbug113150.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+
+import QtQuick
+
+Window {
+ // If static properties of the Math global object are not directly
+ // supported, a warning should be issued in turn failing the build
+ // due to `pragma Strict`.
+ width: 200 * Math.PI
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml b/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml
new file mode 100644
index 0000000000..d0176e6b15
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/readEnumFromInstance.qml
@@ -0,0 +1,16 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ id: root
+
+ property int priority: Backend.gadget.VeryHigh
+ property int prop2: Backend.priority
+
+ property bool priorityIsVeryHigh: root.priority == Backend.VeryHigh
+
+ function cyclePriority() : int {
+ root.priority = Backend.gadget.High;
+ return root.priority;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml b/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml
new file mode 100644
index 0000000000..149638283b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/readonlyListProperty.qml
@@ -0,0 +1,17 @@
+import QtQml
+
+QtObject {
+ id: testObj
+
+ // "readonly" means the identity of the list cannot be changed.
+ // Its contents can be changed.
+ readonly default property list<QtObject> theList
+
+ Component.onCompleted: {
+ for (var i = 0; i < 4; i++)
+ testObj.theList.push(testObj)
+ }
+
+ property int l: theList.length
+}
+
diff --git a/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml b/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml
new file mode 100644
index 0000000000..c6fda8c739
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml
@@ -0,0 +1,18 @@
+import QtQml
+
+QtObject {
+ id: mainItem
+ property int topPadding: 12
+ property int bottomPadding: 12
+
+ property int preferredHeight: mainItem.children.reduce(maximumImplicitHeightReducer, 0) + topPadding + bottomPadding
+ function maximumImplicitHeightReducer(accumulator: real, item: Binding): real {
+ return Math.max(accumulator, (item.objectName + "b").length);
+ }
+
+ property int preferredHeight2: mainItem.children.reduce((accumulator, item) => {
+ return Math.max(accumulator, (item.objectName + "b").length);
+ }, 0) + topPadding + bottomPadding
+
+ property list<Binding> children: [ Binding { objectName: "aaa" } ]
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml b/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml
new file mode 100644
index 0000000000..9352163ba7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml
@@ -0,0 +1,19 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: last
+ property int value: 10
+
+ function verify(i: int) {
+ if (last.value !== i)
+ console.error("failed", last.value, i);
+ else
+ console.log("success")
+ }
+
+ Component.onCompleted: {
+ verify(10)
+ verify(11)
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/resettable.h b/tests/auto/qml/qmlcppcodegen/data/resettable.h
new file mode 100644
index 0000000000..755c8de237
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/resettable.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef RESETTABLE_H
+#define RESETTABLE_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+class ResettableProperty : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Resettable)
+ Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue NOTIFY valueChanged FINAL)
+ Q_PROPERTY(qreal shadowable READ shadowable CONSTANT)
+
+public:
+ explicit ResettableProperty(QObject *parent = nullptr) : QObject(parent) {}
+ qreal value() const { return m_value; }
+ qreal shadowable() const { return 25; }
+
+public slots:
+ void resetValue() { setValue(0); }
+ void setValue(qreal value)
+ {
+ if (m_value == value)
+ return;
+ m_value = value;
+ emit valueChanged();
+ }
+
+signals:
+ void valueChanged();
+
+private:
+ qreal m_value = 0;
+};
+
+#endif // RESETTABLE_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/resettable.qml b/tests/auto/qml/qmlcppcodegen/data/resettable.qml
new file mode 100644
index 0000000000..561655032d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/resettable.qml
@@ -0,0 +1,23 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+Resettable {
+ id: self
+ value: 999
+
+ property double notResettable: 10
+ property double notResettable2: { return undefined }
+
+ property Resettable shadowing: Resettable {
+ property var shadowable: undefined
+ }
+
+ function doReset() { self.value = undefined }
+ function doReset2() { self.value = shadowing.shadowable }
+ function doNotReset() { self.notResettable = undefined }
+
+ signal aaa()
+ signal bbb()
+ onAaa: objectName = self.bbb()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml b/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml
new file mode 100644
index 0000000000..e0b85eb270
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/returnAfterReject.qml
@@ -0,0 +1,15 @@
+import QtQml
+
+QtObject {
+ id: remaining
+
+ property int bar: 0
+
+ Component.onCompleted: {
+ let remainingTime = 123
+ if (remainingTime < 0) {
+ remainingTime += 24 * 60 * 60
+ }
+ remaining.bar = isNaN(remainingTime) ? 0 : remainingTime
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml b/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml
new file mode 100644
index 0000000000..e23f180598
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml
@@ -0,0 +1,20 @@
+pragma ComponentBehavior: Bound
+
+import QtQml
+
+QtObject {
+ id: root
+
+ property QtObject b: QtObject {
+ id: bar
+ objectName: "outer"
+ }
+
+ property Instantiator i: Instantiator {
+ model: 1
+ delegate: QtObject {
+ property QtObject bar: QtObject { objectName: "inner" }
+ Component.onCompleted: root.objectName = bar.objectName
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml b/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml
new file mode 100644
index 0000000000..8b9f161b06
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/scopedEnum.qml
@@ -0,0 +1,19 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ property int good: Data.DType.A
+ property int bad: Data.A
+
+ property int wrong: Data.EType.C
+ property int right: Data.C
+
+ property int notgood: Data2.DType.A
+ property int notbad: Data2.A
+
+ property int notwrong: Data2.EType.C
+ property int notright: Data2.C
+
+ property int passable: Enums.AppState.Blue
+ property int wild: Enums.Green
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h
new file mode 100644
index 0000000000..76c72fff36
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SEQUENCETOITERABLE_H
+#define SEQUENCETOITERABLE_H
+
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+class Entry : public QObject {
+ Q_OBJECT
+
+public:
+ explicit Entry(const QString &name, QObject *parent = nullptr)
+ : QObject(parent), m_name(name)
+ {
+ setObjectName(name);
+ }
+
+private:
+ QString m_name;
+};
+
+class EntryWrapper {
+ Q_GADGET
+ QML_FOREIGN(Entry)
+ QML_NAMED_ELEMENT(Entry)
+ QML_UNCREATABLE("These are my Entry objects")
+};
+
+class EntryListRegistration
+{
+ Q_GADGET
+ QML_FOREIGN(QList<Entry*>)
+ QML_ANONYMOUS
+ QML_SEQUENTIAL_CONTAINER(Entry*)
+};
+
+class EntrySource : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ explicit EntrySource(QObject* parent = nullptr) : QObject(parent) {
+ for (int i = 0; i < 10; i++) {
+ m_entries.push_back(new Entry(QString("Item %1").arg(i), this));
+ }
+ }
+ Q_INVOKABLE QList<Entry*> getEntries() const { return m_entries; }
+
+private:
+ QList<Entry*> m_entries;
+};
+
+#endif // SEQUENCETOITERABLE_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml
new file mode 100644
index 0000000000..23e2645128
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/sequenceToIterable.qml
@@ -0,0 +1,20 @@
+pragma Strict
+import QtQuick
+import TestTypes
+
+Item {
+ Component.onCompleted: () => {
+ repeater.model = EntrySource.getEntries()
+ }
+
+ Repeater {
+ id: repeater
+ Item {
+ required property int index
+ required property QtObject modelData
+ objectName: modelData + ": " + index
+ }
+ }
+
+ property int c: children.length
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml b/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml
new file mode 100644
index 0000000000..404ee8653b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/setLookupConversion.qml
@@ -0,0 +1,17 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: first
+ property int value
+
+ objectName: a.objectName
+ property QtObject a: QtObject {}
+ function t() { a.objectName = "a" }
+
+ Component.onCompleted: {
+ for (let i = 0; i < 10; ++i) {
+ first.value = i;
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml b/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml
new file mode 100644
index 0000000000..9975bfdfa4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/setLookupOriginalScope.qml
@@ -0,0 +1,17 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property Component newEditConstraint: EditConstraint {}
+ property Variable variable: Variable {}
+ property EditConstraint edit
+
+ function trigger() {
+ change(variable, 55);
+ }
+
+ function change(v: Variable, newValue: int) {
+ edit = newEditConstraint.createObject(null, {myOutput: v}) as EditConstraint;
+ v.value = newValue;
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml
new file mode 100644
index 0000000000..ccb50f4934
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/shadowedAsCasts.qml
@@ -0,0 +1,31 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property ShadowedObjectName shadowed1: ShadowedObjectName {}
+ property ShadowedObjectName shadowed2: ShadowedObjectName {}
+ property QtObject shadowed3: ShadowedObjectNameDerived {}
+
+ function returnShadowed2() : QtObject { return shadowed2 }
+
+ function a(mark: int) {
+ // as-cast can be optimized out if we're clever.
+ (shadowed1 as QtObject).objectName = mark;
+ }
+
+ function b(mark: int) {
+ // method return values can contain shadowed properties!
+ returnShadowed2().objectName = mark;
+ }
+
+ function c(mark: int) {
+ // Has to do an actual as-cast, but results in ShadowedObjectNameDerived!
+ (shadowed3 as ShadowedObjectName).objectName = mark;
+ }
+
+ Component.onCompleted: {
+ a(43);
+ b(42);
+ c(41);
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml
new file mode 100644
index 0000000000..590fb40b17
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/shadowedMethod.qml
@@ -0,0 +1,35 @@
+pragma Strict
+import QtQuick
+
+Item {
+ component B: Item {
+ function contains(point: point) : string {
+ return "b"
+ }
+ }
+
+
+ component C: Item {
+ function contains(point: point) : string {
+ return "c"
+ }
+ }
+
+ property Item a: Item {}
+ property B b: B {}
+ property C c: C {}
+
+ function doThing() : var { return a.contains(Qt.point(0, 0)) }
+
+ property var athing;
+ property var bthing;
+ property var cthing;
+
+ Component.onCompleted: {
+ athing = doThing();
+ a = b;
+ bthing = doThing();
+ a = c;
+ cthing = doThing();
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml b/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml
new file mode 100644
index 0000000000..0e130b9afc
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/shadowedPrimitiveCmpEqNull.qml
@@ -0,0 +1,16 @@
+import QtQuick
+
+QtObject {
+ id: win
+
+ component Foo: QtObject {
+ property int progress: 0
+ }
+
+ property int progress: 0
+ readonly property Foo configuring: Foo {}
+
+ Component.onCompleted: {
+ win.configuring.progress = win?.progress ?? 0
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml b/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml
index 58a567f4db..3a0349966d 100644
--- a/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/shared/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml b/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml
new file mode 100644
index 0000000000..9a07b206d4
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/signalsWithLists.qml
@@ -0,0 +1,18 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+Person {
+ property list<var> varlist: [1, "foo", this, undefined, true]
+ property list<QtObject> objlist: [this, null, this]
+
+ function sendSignals() {
+ variantListHappened(varlist);
+ objectListHappened(objlist);
+ }
+
+ property int happening: 0
+
+ onObjectListHappened: (objects) => { happening += objects.length }
+ onVariantListHappened: (variants) => { happening += variants.length }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/state.h b/tests/auto/qml/qmlcppcodegen/data/state.h
index 1afcebc4ea..708e681781 100644
--- a/tests/auto/qml/qmlcppcodegen/data/state.h
+++ b/tests/auto/qml/qmlcppcodegen/data/state.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef STATE_H
#define STATE_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml b/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml
new file mode 100644
index 0000000000..158dff2d0b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/structuredValueType.qml
@@ -0,0 +1,8 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property rect r: ({x: 1, y: 2, width: 3, height: 4})
+ property rect r2: { var x = 42; return {x}; }
+ property weatherModelUrl w: ({ strings: ["one", "two", "three"] })
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/theme.cpp b/tests/auto/qml/qmlcppcodegen/data/theme.cpp
index c8a2612753..10302645c0 100644
--- a/tests/auto/qml/qmlcppcodegen/data/theme.cpp
+++ b/tests/auto/qml/qmlcppcodegen/data/theme.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "theme.h"
diff --git a/tests/auto/qml/qmlcppcodegen/data/theme.h b/tests/auto/qml/qmlcppcodegen/data/theme.h
index f70a3b440e..3e56ec1f9f 100644
--- a/tests/auto/qml/qmlcppcodegen/data/theme.h
+++ b/tests/auto/qml/qmlcppcodegen/data/theme.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef THEME_H
#define THEME_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/thisObject.qml b/tests/auto/qml/qmlcppcodegen/data/thisObject.qml
new file mode 100644
index 0000000000..50664eced2
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/thisObject.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property QtObject warned
+
+ function f(arg: QtObject) { warned = arg }
+ function warn() { f(this) }
+
+ Component.onCompleted: warn()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp
index 1853d2dc12..04e1797c7c 100644
--- a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp
+++ b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "timelinetheme.h"
diff --git a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h
index 335e43b570..3e7da77cc9 100644
--- a/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h
+++ b/tests/auto/qml/qmlcppcodegen/data/timelinetheme.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TIMELINETHEME_H
#define TIMELINETHEME_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml b/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml
new file mode 100644
index 0000000000..a73e28f642
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/topLevelComponent.qml
@@ -0,0 +1,14 @@
+pragma Strict
+import QtQml
+
+Component {
+ QtObject {
+ id: root
+
+ function myOpen() {
+ root.objectName = "foo"
+ }
+
+ Component.onCompleted: myOpen()
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml b/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml
new file mode 100644
index 0000000000..e7bf5ccec9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/trigraphs.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ objectName: "??= ??/ ??' ??( ??) ??! ??< ??> ??-"
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp b/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp
new file mode 100644
index 0000000000..02629ad7f6
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/tst_qmlcppcodegen_verify.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+
+#include <QtTest>
+#include <QtCore/qobject.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qbytearray.h>
+
+class tst_QmlCppCodegenVerify : public QObject
+{
+ Q_OBJECT
+private slots:
+ void verifyGeneratedSources_data();
+ void verifyGeneratedSources();
+};
+
+void tst_QmlCppCodegenVerify::verifyGeneratedSources_data()
+{
+ QTest::addColumn<QString>("file");
+
+ QDir a(":/a");
+ const QStringList entries = a.entryList(QDir::Files);
+ for (const QString &entry : entries)
+ QTest::addRow("%s", entry.toUtf8().constData()) << entry;
+}
+
+void tst_QmlCppCodegenVerify::verifyGeneratedSources()
+{
+ QFETCH(QString, file);
+ QFile a(":/a/" + file);
+ QFile b(":/b/" + file.replace("codegen_test_module", "codegen_test_module_verify"));
+
+ QVERIFY(a.open(QIODevice::ReadOnly));
+ QVERIFY(b.open(QIODevice::ReadOnly));
+
+ const QByteArray aData = a.readAll();
+ const QByteArray bData = b.readAll()
+ .replace("verify/TestTypes", "TestTypes")
+ .replace("verify_TestTypes", "TestTypes");
+
+ QCOMPARE(aData, bData);
+}
+
+QTEST_MAIN(tst_QmlCppCodegenVerify)
+
+#include "tst_qmlcppcodegen_verify.moc"
diff --git a/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml b/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml
new file mode 100644
index 0000000000..e76443a2e0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property double d: Math.max(undefined, 40)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/urlString.qml b/tests/auto/qml/qmlcppcodegen/data/urlString.qml
index 511c54532c..a83855ebdb 100644
--- a/tests/auto/qml/qmlcppcodegen/data/urlString.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/urlString.qml
@@ -9,5 +9,12 @@ QtObject {
Component.onCompleted: {
c = "http://dddddd.com";
self.d = "http://aaaaaa.com";
+ myUrlChanged(c)
+ }
+
+ signal myUrlChanged(urlParam: url)
+
+ onMyUrlChanged: (urlParam) => {
+ objectName = urlParam;
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml
new file mode 100644
index 0000000000..a775773dda
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml
@@ -0,0 +1,43 @@
+pragma ValueTypeBehavior: Addressable
+import QtQml
+
+QtObject {
+ id: root
+ property rect r: Qt.rect(10, 20, 3, 4)
+ property var v: r
+ property real x: (v as rect).x
+
+ function f(input: bool) : var {
+ if (input)
+ return 0
+ return Qt.point(2, 2)
+ }
+
+ property var vv: Qt.point(5, 5)
+ property var uu: undefined
+
+ property int tv3: (root.vv as point)?.x
+ property var tv4: (root.uu as rect)?.x
+ property int tc3: (root?.vv as point)?.y
+ property var tc6: (root?.uu as rect)?.height
+ property var tc7: (f(true) as point)?.x
+ property var tc8: (f(false) as point)?.x
+
+ property string greeting1
+ property string greeting2
+
+ readonly property string defaultGreeting: "Default Greeting"
+ property QtObject o: QtObject {
+ id: o
+ property var customGreeting
+ function greet() : string {
+ return (o.customGreeting as string) ?? root.defaultGreeting
+ }
+ }
+
+ Component.onCompleted: {
+ root.greeting1 = o.greet()
+ o.customGreeting = "Custom Greeting"
+ root.greeting2 = o.greet()
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml
index cca634753d..d33133bb6b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeCopy.qml
@@ -1,4 +1,4 @@
-pragma ValueTypeBehavior: Copy
+pragma ValueTypeBehavior: Copy, Addressable
import QtQml
QtObject {
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml
new file mode 100644
index 0000000000..42a55e832d
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeDefault.qml
@@ -0,0 +1,34 @@
+import QtQml
+
+
+QtObject {
+ id: root
+
+ property list<double> numbers: {
+ var result = [];
+ for (var i = 0; i < 10; ++i)
+ result[i] = i;
+ return result;
+ }
+
+ property rect r: ({x: 1, y: 2, width: 3, height: 4})
+
+ function evil() : double {
+ var numbers = root.numbers;
+ root.numbers = [];
+ var a = 0;
+ for (var j = 0; j < 10; ++j) {
+ a += numbers[j];
+ }
+ return a;
+ }
+
+ function fvil() : double {
+ var r = root.r;
+ root.r = {x: 5, y: 6, width: 7, height: 8};
+ return r.x;
+ }
+
+ property double e: evil()
+ property double f: fvil()
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml
index 568f39820c..a87f88ecf9 100644
--- a/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeReference.qml
@@ -1,4 +1,4 @@
-pragma ValueTypeBehavior: Reference
+pragma ValueTypeBehavior: Reference, Inaddressable
import QtQml
QtObject {
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMap.qml b/tests/auto/qml/qmlcppcodegen/data/variantMap.qml
new file mode 100644
index 0000000000..d7147ec5fc
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantMap.qml
@@ -0,0 +1,27 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property Component shadowable: QtObject {}
+ property B b: B { id: theB }
+ property rect r: theB.r
+ property var v: { "1": null, "25": undefined, "19": "19" }
+
+ property Component c: Component {
+ id: unshadowable
+ QtObject {}
+ }
+
+ // We need this extra function in order to coerce the result of the shadowable
+ // method call back to QtObject
+ function createShadowable() : QtObject {
+ return shadowable.createObject(this, {objectName: "a"})
+ }
+
+ objectName: {
+ return createShadowable().objectName
+ + " " + unshadowable.createObject(this, {objectName: "b"}).objectName
+ }
+
+ Component.onCompleted: b.r = { x: 12, y: 13, width: 14, height: 15 }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h
new file mode 100644
index 0000000000..61d38228b0
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.h
@@ -0,0 +1,17 @@
+#pragma once
+#include <QObject>
+#include <QVariantMap>
+#include <QtQml/qqmlregistration.h>
+
+class VariantMapLookupFoo : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QVariantMap data READ data CONSTANT)
+
+public:
+ VariantMapLookupFoo(QObject *parent = nullptr) : QObject(parent) { }
+
+private:
+ QVariantMap data() const { return { { "value", 42 } }; }
+};
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml
new file mode 100644
index 0000000000..4e56cb9448
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantMapLookup.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import TestTypes
+import QtQuick
+
+Item {
+ property int i: moo.data.value
+
+ VariantMapLookupFoo {
+ id: moo
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml b/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml
new file mode 100644
index 0000000000..cca26265c9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantReturn.qml
@@ -0,0 +1,15 @@
+pragma Strict
+import QtQml
+import TestTypes
+
+QtObject {
+ property DirectBindable a: DirectBindable {
+ id: aId
+ x: WeatherModelUrlUtils.url(1)
+ }
+
+ property IndirectBindable b: IndirectBindable {
+ id: bId
+ y: aId.x
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/variantreturn.h b/tests/auto/qml/qmlcppcodegen/data/variantreturn.h
new file mode 100644
index 0000000000..87718aaef3
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/variantreturn.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef VARIANTERETURN_H
+#define VARIANTERETURN_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQml/qqmlregistration.h>
+
+#include "weathermoduleurl.h"
+
+class DirectBindable : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(WeatherModelUrl x READ x WRITE setX NOTIFY xChanged BINDABLE bindableX)
+
+public:
+ explicit DirectBindable(QObject *parent = nullptr) : QObject(parent) {}
+
+ WeatherModelUrl x() const { return m_x.value(); }
+ void setX(const WeatherModelUrl& newX) { m_x.setValue(newX);}
+ QBindable<WeatherModelUrl> bindableX() { return QBindable<WeatherModelUrl>(&m_x); }
+
+Q_SIGNALS:
+ void xChanged();
+
+private:
+ Q_OBJECT_BINDABLE_PROPERTY(DirectBindable, WeatherModelUrl, m_x, &DirectBindable::xChanged)
+};
+
+class IndirectBindable : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(WeatherModelUrl y READ y WRITE setY NOTIFY yChanged BINDABLE bindableY)
+ Q_PROPERTY(int z READ z NOTIFY zChanged)
+
+public:
+ explicit IndirectBindable(QObject *parent = nullptr) : QObject(parent) {
+ m_z.setBinding([this]()->int {
+ return m_y.value().timeIndex() * 2;
+ });
+ }
+
+ WeatherModelUrl y() const { return m_y.value(); }
+ void setY(const WeatherModelUrl& newY) { m_y.setValue(newY); }
+ QBindable<WeatherModelUrl> bindableY() { return QBindable<WeatherModelUrl>(&m_y); }
+
+ int z() const { return m_z.value(); }
+ QBindable<int> bindableZ() const { return QBindable<int>(&m_z); }
+
+Q_SIGNALS:
+ void yChanged();
+ void zChanged();
+
+private:
+ Q_OBJECT_BINDABLE_PROPERTY(IndirectBindable, WeatherModelUrl, m_y, &IndirectBindable::yChanged)
+ Q_OBJECT_BINDABLE_PROPERTY(IndirectBindable, int, m_z, &IndirectBindable::zChanged)
+};
+
+#endif // VARIANTRETURN_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml b/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml
new file mode 100644
index 0000000000..f5826d9e48
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/voidConversion.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ id: item
+ property point p: Qt.point(20, 10)
+
+ Component.onCompleted: {
+ item.p = undefined
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h b/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h
new file mode 100644
index 0000000000..998118dc5b
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/weathermoduleurl.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef WEATHERMODELURL_H
+#define WEATHERMODELURL_H
+
+#include <QtQml/qqmlregistration.h>
+#include <QtCore/qobject.h>
+
+class WeatherModelUrl
+{
+ Q_GADGET
+ QML_STRUCTURED_VALUE
+ QML_VALUE_TYPE(weatherModelUrl)
+ Q_PROPERTY(qsizetype timeIndex READ timeIndex CONSTANT)
+ Q_PROPERTY(QStringList strings READ strings WRITE setStrings)
+
+public:
+ WeatherModelUrl() : m_timeIndex(-1) {}
+ WeatherModelUrl(qsizetype timeIdx) : m_timeIndex(timeIdx) {}
+
+ qsizetype timeIndex() const { return m_timeIndex; }
+
+ QStringList strings() const { return m_strings; }
+ void setStrings(const QStringList &newStrings)
+ {
+ if (m_strings != newStrings)
+ m_strings = newStrings;
+ }
+
+private:
+ friend bool operator==(const WeatherModelUrl &a, const WeatherModelUrl &b)
+ {
+ return a.m_timeIndex == b.m_timeIndex;
+ }
+
+ friend bool operator!=(const WeatherModelUrl &a, const WeatherModelUrl &b)
+ {
+ return !(a == b);
+ }
+
+ qsizetype m_timeIndex;
+ QStringList m_strings;
+};
+
+class WeatherModelUrlUtils : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ WeatherModelUrlUtils(QObject *parent = nullptr) : QObject(parent) {}
+
+ Q_INVOKABLE static WeatherModelUrl url(int timeIdx)
+ {
+ return WeatherModelUrl(timeIdx);
+ }
+};
+
+#endif // WEATHERMODELURL_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/withlength.h b/tests/auto/qml/qmlcppcodegen/data/withlength.h
index ba95522c53..26c6307f2b 100644
--- a/tests/auto/qml/qmlcppcodegen/data/withlength.h
+++ b/tests/auto/qml/qmlcppcodegen/data/withlength.h
@@ -1,11 +1,13 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WITHLENGTH_H
#define WITHLENGTH_H
#include <QtCore/qobject.h>
-#include <QtQmlIntegration/qqmlintegration.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+#include <QtQml/qqml.h>
struct ValueTypeWithLength
{
@@ -18,6 +20,8 @@ struct ValueTypeWithLength
public:
ValueTypeWithLength() = default;
Q_INVOKABLE ValueTypeWithLength(int length) : m_length(length) {}
+ Q_INVOKABLE ValueTypeWithLength(QPointF point) : m_length(point.manhattanLength()) {}
+ Q_INVOKABLE ValueTypeWithLength(QRectF rect) : m_length(rect.width()) {}
Q_INVOKABLE QString toString() const { return QStringLiteral("no"); }
int length() const { return m_length; }
@@ -26,4 +30,24 @@ private:
int m_length = 19;
};
+struct InnerWithLength {
+ int m_length;
+};
+
+struct UnconstructibleWithLength
+{
+ Q_GADGET
+ QML_VALUE_TYPE(unconstructibleWithLength)
+
+ QML_FOREIGN(InnerWithLength)
+ QML_EXTENDED(UnconstructibleWithLength)
+
+public:
+ UnconstructibleWithLength() = default;
+ Q_INVOKABLE UnconstructibleWithLength(int length) : v{length} {}
+
+private:
+ InnerWithLength v;
+};
+
#endif // WITHLENGTH_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h b/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h
index dce78fa9c9..7de49f095a 100644
--- a/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h
+++ b/tests/auto/qml/qmlcppcodegen/data/wrapwithvariant.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WRAPWITHVARIANT_H
#define WRAPWITHVARIANT_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/writeback.qml b/tests/auto/qml/qmlcppcodegen/data/writeback.qml
new file mode 100644
index 0000000000..359f00efb7
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/writeback.qml
@@ -0,0 +1,43 @@
+pragma Strict
+
+import TestTypes
+import QtQml
+
+Person {
+ id: self
+
+ area {
+ width: 19
+ height: 199
+ }
+
+ property list<int> ints: [4, 3, 2, 1]
+
+ property outer recursive
+ property Person shadowable: Person {
+ id: notShadowable
+ area.width: self.area.width
+ area2.height: self.area2.height
+ }
+
+ Component.onCompleted: {
+ area.width = 16
+ area2.height = 17
+
+ self.area.x = 4
+ self.area2.y = 5
+
+ // You cannot do this on the shadowable Person because
+ // shadowable.area may not actually be a QRectF anymore.
+ notShadowable.area.x = 40
+ notShadowable.area2.y = 50
+
+ self.recursive.inner.i = 99;
+
+ self.ints[0] = 12;
+ ints[1] = 22;
+ ints[6] = 33;
+ }
+
+ property int inner: recursive.inner.i
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 400075c08d..9b66143f62 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -1,10 +1,16 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include "data/druggeljug.h"
#include <data/birthdayparty.h>
#include <data/cppbaseclass.h>
+#include <data/druggeljug.h>
+#include <data/enumProperty.h>
#include <data/enumproblems.h>
+#include <data/getOptionalLookup.h>
#include <data/objectwithmethod.h>
+#include <data/resettable.h>
+#include <data/weathermoduleurl.h>
+#include <data/withlength.h>
#include <QtQml/private/qqmlengine_p.h>
#include <QtQml/private/qqmlpropertycachecreator_p.h>
@@ -12,6 +18,7 @@
#include <QtTest>
#include <QtQml>
#include <QtGui/qcolor.h>
+#include <QtGui/qpa/qplatformdialoghelper.h>
#if QT_CONFIG(process)
#include <QtCore/qprocess.h>
@@ -26,146 +33,459 @@ class tst_QmlCppCodegen : public QObject
Q_OBJECT
private slots:
void initTestCase();
+ void cleanupTestCase();
- void simpleBinding();
- void cppValueTypeList();
+ void accessModelMethodFromOutSide();
+ void aliasLookup();
+ void ambiguousAs();
+ void ambiguousSignals();
void anchorsFill();
- void signalHandler();
- void idAccess();
- void globals();
- void multiLookup();
- void enums();
- void funcWithParams();
- void intOverflow();
- void stringLength();
- void scopeVsObject();
- void compositeTypeMethod();
- void excessiveParameters();
- void jsImport();
- void jsmoduleImport();
- void methods();
- void math();
- void unknownParameter();
+ void argumentConversion();
void array();
- void equalsUndefined();
- void conversions();
- void interestingFiles_data();
- void interestingFiles();
- void extendedTypes();
- void construct();
- void contextParam();
- void attachedType();
- void componentReturnType();
- void onAssignment();
- void failures();
- void enumScope();
- void unusedAttached();
+ void arrayCtor();
+ void asCast();
void attachedBaseEnum();
- void nullAccess();
- void interceptor();
- void nonNotifyable();
- void importsFromImportPath();
- void aliasLookup();
- void outOfBoundsArray();
- void compositeSingleton();
- void lotsOfRegisters();
- void inPlaceDecrement();
- void shifts();
- void valueTypeProperty();
- void propertyOfParent();
- void accessModelMethodFromOutSide();
- void functionArguments();
+ void attachedSelf();
+ void attachedType();
+ void badSequence();
+ void basicBlocksWithBackJump();
+ void basicBlocksWithBackJump_infinite();
+ void basicDTZ();
+ void bindToValueType();
void bindingExpression();
- void voidFunction();
- void overriddenProperty();
- void listLength();
- void parentProperty();
- void registerElimination();
- void asCast();
- void noQQmlData();
- void scopeObjectDestruction();
+ void blockComments();
+ void boolCoercions();
+ void boolPointerMerge();
+ void boundComponents();
+ void callContextPropertyLookupResult();
+ void callWithSpread();
void colorAsVariant();
- void bindToValueType();
- void undefinedResets();
- void innerObjectNonShadowable();
- void ownPropertiesNonShadowable();
- void modulePrefix();
void colorString();
- void urlString();
- void callContextPropertyLookupResult();
+ void compareOriginals();
+ void comparisonTypes();
+ void componentReturnType();
+ void compositeSingleton();
+ void compositeTypeMethod();
+ void consoleObject();
+ void consoleTrace();
+ void construct();
+ void contextParam();
+ void conversionDecrement();
+ void conversionInDeadCode();
+ void conversions();
+ void convertPrimitiveToVar();
+ void convertQJSPrimitiveValueToIntegral();
+ void convertToOriginalReadAcumulatorForUnaryOperators();
+ void cppMethodListReturnType();
+ void cppValueTypeList();
+ void dateConstruction();
+ void dateConversions();
void deadShoeSize();
- void listIndices();
- void jsMathObject();
- void intEnumCompare();
- void attachedSelf();
- void functionReturningVoid();
- void functionCallOnNamespaced();
+ void dialogButtonBox();
+ void enumConversion();
+ void enumFromBadSingleton();
+ void enumLookup();
+ void enumMarkedAsFlag();
+ void enumProblems();
+ void enumScope();
+ void enums();
+ void enforceSignature();
+ void enumsInOtherObject();
+ void equalityQObjects();
+ void equalityQUrl();
+ void equalityTestsWithNullOrUndefined();
+ void equalityVarAndNonStorable();
+ void equalityVarAndStorable();
+ void equalsUndefined();
+ void evadingAmbiguity();
+ void exceptionFromInner();
+ void excessiveParameters();
+ void extendedTypes();
+ void failures();
+ void fallbackLookups();
+ void fileImportsContainCxxTypes();
+ void flagEnum();
void flushBeforeCapture();
- void unknownAttached();
- void variantlist();
- void popContextAfterRet();
- void revisions();
- void invisibleBase();
- void notEqualsInt();
- void infinities();
- void blockComments();
+ void fromBoolValue();
+ void funcWithParams();
+ void functionArguments();
+ void functionCallOnNamespaced();
void functionLookup();
- void objectInVar();
+ void functionReturningVoid();
void functionTakingVar();
- void testIsnan();
- void fallbackLookups();
- void typedArray();
- void prefixedType();
- void evadingAmbiguity();
- void fromBoolValue();
- void invisibleTypes();
+ void getLookupOfScript();
+ void getOptionalLookup();
+ void getOptionalLookup_data();
+ void getOptionalLookupOnQJSValueNonStrict();
+ void getOptionalLookupShadowed();
+ void globals();
+ void idAccess();
+ void ignoredFunctionReturn();
+ void importsFromImportPath();
+ void inPlaceDecrement();
+ void inaccessibleProperty();
+ void indirectlyShadowable();
+ void infinities();
+ void infinitiesToInt();
+ void innerObjectNonShadowable();
+ void intEnumCompare();
+ void intOverflow();
+ void intToEnum();
+ void interceptor();
+ void interestingFiles();
+ void interestingFiles_data();
+ void internalConversion();
void invalidPropertyType();
- void valueTypeLists();
- void boundComponents();
+ void invisibleBase();
void invisibleListElementType();
- void typePropertyClash();
- void objectToString();
- void throwObjectName();
+ void invisibleSingleton();
+ void invisibleTypes();
+ void iteration();
void javaScriptArgument();
- void translation();
- void stringArg();
- void conversionDecrement();
- void unstoredUndefined();
- void registerPropagation();
- void argumentConversion();
- void badSequence();
- void enumLookup();
- void trivialSignalHandler();
- void stringToByteArray();
+ void jsArrayMethods();
+ void jsArrayMethodsWithParams();
+ void jsArrayMethodsWithParams_data();
+ void jsImport();
+ void jsMathObject();
+ void jsmoduleImport();
+ void lengthAccessArraySequenceCompat();
+ void letAndConst();
+ void listAsArgument();
+ void listConversion();
+ void listIndices();
+ void listLength();
+ void listOfInvisible();
void listPropertyAsModel();
- void notNotString();
+ void listToString();
+ void lotsOfRegisters();
+ void math();
+ void mathMinMax();
void mathOperations();
- void inaccessibleProperty();
- void typePropagationLoop();
- void signatureIgnored();
- void listAsArgument();
- void letAndConst();
- void signalIndexMismatch();
- void callWithSpread();
- void nullComparison();
- void consoleObject();
+ void mathStaticProperties();
+ void mergedObjectReadWrite();
+ void methodOnListLookup();
+ void methods();
+ void modulePrefix();
+ void multiDirectory_data();
+ void multiDirectory();
void multiForeign();
+ void multiLookup();
+ void multipleCtors();
void namespaceWithEnum();
- void enumProblems();
- void enumConversion();
- void ambiguousSignals();
- void fileImportsContainCxxTypes();
- void lengthAccessArraySequenceCompat();
- void storeElementSideEffects();
+ void noQQmlData();
+ void nonNotifyable();
+ void notEqualsInt();
+ void notNotString();
+ void nullAccess();
+ void nullAccessInsideSignalHandler();
+ void nullComparison();
+ void nullishCoalescing();
+ void nullishCoalescing_data();
void numbersInJsPrimitive();
- void infinitiesToInt();
- void equalityVarAndNonStorable();
- void equalityQObjects();
- void dateConversions();
+ void objectInVar();
+ void objectLookupOnListElement();
+ void objectToString();
+ void objectWithStringListMethod();
+ void onAssignment();
+ void optionalComparison();
+ void outOfBoundsArray();
+ void overriddenProperty();
+ void ownPropertiesNonShadowable();
+ void parentProperty();
+ void popContextAfterRet();
+ void prefixedType();
+ void propertyOfParent();
+ void reduceWithNullThis();
+ void readEnumFromInstance();
+ void readonlyListProperty();
+ void registerElimination();
+ void registerPropagation();
+ void renameAdjust();
+
+ void resettableProperty();
+ void resettableProperty_data();
+
+ void returnAfterReject();
+ void revisions();
+ void scopeIdLookup();
+ void scopeObjectDestruction();
+ void scopeVsObject();
+ void scopedEnum();
+ void sequenceToIterable();
+ void setLookupConversion();
+ void setLookupOriginalScope();
+ void shadowedAsCasts();
+ void shadowedMethod();
+ void shadowedPrimitiveCmpEqNull();
+ void shifts();
+ void signalHandler();
+ void signalIndexMismatch();
+ void signalsWithLists();
+ void signatureIgnored();
+ void simpleBinding();
+ void storeElementSideEffects();
+ void storeMetaEnum();
+ void stringArg();
+ void stringLength();
+ void stringToByteArray();
+ void structuredValueType();
+ void testIsnan();
+ void thisObject();
+ void throwObjectName();
+ void topLevelComponent();
+ void translation();
+ void trigraphs();
+ void trivialSignalHandler();
+ void typePropagationLoop();
+ void typePropertyClash();
+ void typedArray();
+ void undefinedResets();
+ void undefinedToDouble();
+ void unknownAttached();
+ void unknownParameter();
+ void unstoredUndefined();
+ void unusedAttached();
+ void urlString();
void valueTypeBehavior();
- void invisibleSingleton();
+ void valueTypeLists();
+ void valueTypeProperty();
+ void variantMapLookup();
+ void variantReturn();
+ void variantlist();
+ void variantMap();
+ void voidConversion();
+ void voidFunction();
+ void writeBack();
};
+static QByteArray arg1()
+{
+ const QStringList args = QCoreApplication::instance()->arguments();
+ return args.size() > 1 ? args[1].toUtf8() : QByteArray("undefined");
+}
+
+namespace QmlCacheGeneratedCode {
+namespace _qt_qml_TestTypes_failures_qml {
+extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];
+}
+}
+
+static void checkColorProperties(QQmlComponent *component)
+{
+ QVERIFY2(component->isReady(), qPrintable(component->errorString()));
+ QScopedPointer<QObject> rootObject(component->create());
+ QVERIFY(rootObject);
+
+ const QMetaObject *mo = QMetaType::fromName("QQuickIcon").metaObject();
+ QVERIFY(mo != nullptr);
+
+ const QMetaProperty prop = mo->property(mo->indexOfProperty("color"));
+ QVERIFY(prop.isValid());
+
+ const QVariant a = rootObject->property("a");
+ QVERIFY(a.isValid());
+
+ const QVariant iconColor = prop.readOnGadget(rootObject->property("icon").data());
+ QVERIFY(iconColor.isValid());
+
+ const QMetaType colorType = QMetaType::fromName("QColor");
+ QVERIFY(colorType.isValid());
+
+ QCOMPARE(a.metaType(), colorType);
+ QCOMPARE(iconColor.metaType(), colorType);
+
+ QCOMPARE(iconColor, a);
+}
+
+static const double numbers[] = {
+ qQNaN(), -qInf(),
+ std::numeric_limits<double>::min(),
+ std::numeric_limits<float>::min(),
+ std::numeric_limits<qint32>::min(),
+ -1000.2, -100, -2, -1.333, -1, -0.84, -0.5,
+
+ // -0 and 0 are not different on the QML side. Therefore, don't keep them adjacent.
+ // Otherwise the bindings won't get re-evaluated.
+ std::copysign(0.0, -1), 1, 0.0,
+
+ 0.5, 0.77, 1.4545, 2, 199, 2002.13,
+ std::numeric_limits<qint32>::max(),
+ std::numeric_limits<quint32>::max(),
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<double>::max(),
+ qInf()
+};
+
+class MyCppType : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool useListDelegate
+ READ useListDelegate
+ WRITE setUseListDelegate
+ NOTIFY useListDelegateChanged)
+public:
+ explicit MyCppType(QObject * parent = nullptr) : QObject(parent) {}
+
+ bool useListDelegate() const { return m_useListDelegate; }
+ void setUseListDelegate(bool useListDelegate)
+ {
+ if (useListDelegate != m_useListDelegate) {
+ m_useListDelegate = useListDelegate;
+ emit useListDelegateChanged();
+ }
+ }
+
+signals:
+ void useListDelegateChanged();
+
+private:
+ bool m_useListDelegate = false;
+};
+
+class InvisibleListElementType : public QObject
+{
+ Q_OBJECT
+public:
+ InvisibleListElementType(QObject *parent = nullptr) : QObject(parent) {}
+};
+
+template<typename T>
+QString toOperand(double arg);
+
+template<>
+QString toOperand<double>(double arg)
+{
+ if (qIsNull(arg))
+ return std::signbit(arg) ? QStringLiteral("(-0)") : QStringLiteral("(0)");
+
+ return u'(' + QJSPrimitiveValue(arg).toString() + u')';
+}
+
+template<>
+QString toOperand<int>(double arg)
+{
+ const int iArg = QJSPrimitiveValue(arg).toInteger();
+ return u'(' + QJSPrimitiveValue(iArg).toString() + u')';
+}
+
+template<>
+QString toOperand<bool>(double arg)
+{
+ const bool bArg = QJSPrimitiveValue(arg).toBoolean();
+ return u'(' + QJSPrimitiveValue(bArg).toString() + u')';
+}
+
+template<typename T1, typename T2>
+double jsEval(double arg1, double arg2, const QString &op, QJSEngine *engine)
+{
+ auto evalBinary = [&](const QString &jsOp) {
+ return engine->evaluate(toOperand<T1>(arg1) + jsOp + toOperand<T2>(arg2)).toNumber();
+ };
+
+ auto evalBinaryConst = [&](const QString &jsOp) {
+ return engine->evaluate(toOperand<T1>(arg1) + jsOp + u'9').toNumber();
+ };
+
+ auto evalUnary = [&](const QString &jsOp) {
+ return engine->evaluate(jsOp + toOperand<T1>(arg1)).toNumber();
+ };
+
+ auto evalInPlace = [&](const QString &jsOp) {
+ return engine->evaluate(
+ u"(function() {var a = "_s + toOperand<T1>(arg1)+ u"; return "_s
+ + jsOp + u"a;})()"_s).toNumber();
+ };
+
+ if (op == u"unot")
+ return evalUnary(u"!"_s);
+ if (op == u"uplus")
+ return evalUnary(u"+"_s);
+ if (op == u"uminus")
+ return evalUnary(u"-"_s);
+ if (op == u"ucompl")
+ return evalUnary(u"~"_s);
+
+ if (op == u"increment")
+ return evalInPlace(u"++"_s);
+ if (op == u"decrement")
+ return evalInPlace(u"--"_s);
+
+ if (op == u"add")
+ return evalBinary(u"+"_s);
+ if (op == u"sub")
+ return evalBinary(u"-"_s);
+ if (op == u"mul")
+ return evalBinary(u"*"_s);
+ if (op == u"div")
+ return evalBinary(u"/"_s);
+ if (op == u"exp")
+ return evalBinary(u"**"_s);
+ if (op == u"mod")
+ return evalBinary(u"%"_s);
+
+ if (op == u"bitAnd")
+ return evalBinary(u"&"_s);
+ if (op == u"bitOr")
+ return evalBinary(u"|"_s);
+ if (op == u"bitXor")
+ return evalBinary(u"^"_s);
+
+ if (op == u"bitAndConst")
+ return evalBinaryConst(u"&"_s);
+ if (op == u"bitOrConst")
+ return evalBinaryConst(u"|"_s);
+ if (op == u"bitXorConst")
+ return evalBinaryConst(u"^"_s);
+
+ if (op == u"ushr")
+ return evalBinary(u">>>"_s);
+ if (op == u"shr")
+ return evalBinary(u">>"_s);
+ if (op == u"shl")
+ return evalBinary(u"<<"_s);
+
+ if (op == u"ushrConst")
+ return evalBinaryConst(u">>>"_s);
+ if (op == u"shrConst")
+ return evalBinaryConst(u">>"_s);
+ if (op == u"shlConst")
+ return evalBinaryConst(u"<<"_s);
+
+ qDebug() << op;
+ Q_UNREACHABLE_RETURN(0);
+}
+
+static QList<QString> convertToStrings(const QList<qint64> &ints)
+{
+ QList<QString> strings;
+ for (qint64 i : ints)
+ strings.append(QString::number(i));
+ return strings;
+}
+
+static QRegularExpression bindingLoopMessage(const QUrl &url, char var)
+{
+ // The actual string depends on how many times QObject* was registered with what parameters.
+ return QRegularExpression(
+ "%1:4:1: QML [^:]+: Binding loop detected for property \"%2\""_L1
+ .arg(url.toString()).arg(QLatin1Char(var)));
+}
+
+static void listsEqual(QObject *listProp, QObject *array, const char *method)
+{
+ const QByteArray listPropertyPropertyName = QByteArray("listProperty") + method;
+ const QByteArray jsArrayPropertyName = QByteArray("jsArray") + method;
+
+ const QQmlListReference listPropertyProperty(listProp, listPropertyPropertyName.constData());
+ const QVariantList jsArrayProperty = array->property(jsArrayPropertyName.constData()).toList();
+
+ const qsizetype listPropertyCount = listPropertyProperty.count();
+ QCOMPARE(listPropertyCount, jsArrayProperty.count());
+
+ for (qsizetype i = 0; i < listPropertyCount; ++i)
+ QCOMPARE(listPropertyProperty.at(i), jsArrayProperty.at(i).value<QObject *>());
+}
+
void tst_QmlCppCodegen::initTestCase()
{
#ifdef QT_TEST_FORCE_INTERPRETER
@@ -173,40 +493,91 @@ void tst_QmlCppCodegen::initTestCase()
#endif
}
-void tst_QmlCppCodegen::simpleBinding()
+void tst_QmlCppCodegen::cleanupTestCase()
+{
+ // This code checks for basic blocks validation failures in the tests
+ QStringList expectedFailures = {
+ "codegen_test_module_basicBlocksWithBackJump_infinite_qml.cpp",
+ "codegen_test_module_verify_basicBlocksWithBackJump_infinite_qml.cpp",
+ };
+
+ QString generatedCppFolder = GENERATED_CPP_FOLDER;
+ QDirIterator dirIterator(generatedCppFolder, { "*.cpp" }, QDir::Files);
+ while (dirIterator.hasNext()) {
+ QFile file(dirIterator.next());
+ if (!file.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text)) {
+ qDebug() << "Couldn't open generated file";
+ continue;
+ }
+
+ const auto content = file.readAll();
+ if (bool validationFailed = content.contains("// QV4_BASIC_BLOCK_VALIDATION_FAILED:"_L1)) {
+ if (expectedFailures.contains(dirIterator.fileInfo().fileName())) {
+ QEXPECT_FAIL("", "Expected failure", Continue);
+ }
+ const auto message = file.fileName() + ": Basic blocks validation failed.";
+ QVERIFY2(!validationFailed, message.toStdString().c_str());
+ }
+ }
+}
+
+void tst_QmlCppCodegen::accessModelMethodFromOutSide()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/AccessModelMethodsFromOutside.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtDebugMsg, "3");
+ QTest::ignoreMessage(QtDebugMsg, "Apple");
QScopedPointer<QObject> object(component.create());
- QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
- QCOMPARE(object->property("foo").toInt(), int(3));
- {
- CppBaseClass *base = qobject_cast<CppBaseClass *>(object.data());
- Q_ASSERT(base);
- QVERIFY(!base->cppProp.hasBinding());
- QCOMPARE(base->cppProp.value(), 7);
- QVERIFY(base->cppProp2.hasBinding());
- QCOMPARE(base->cppProp2.value(), 14);
- base->cppProp.setValue(9);
- QCOMPARE(base->cppProp.value(), 9);
- QCOMPARE(base->cppProp2.value(), 18);
- }
+ QCOMPARE(object->property("cost1").toDouble(), 3);
+ QCOMPARE(object->property("name1").toString(), u"Orange"_s);
+ QCOMPARE(object->property("cost2").toDouble(), 1.95);
+ QCOMPARE(object->property("name2").toString(), u"Banana"_s);
}
-void tst_QmlCppCodegen::cppValueTypeList()
+void tst_QmlCppCodegen::aliasLookup()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/aliasLookup.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
- QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
- QCOMPARE(object->property("a").toInt(), 16);
- QMetaObject::invokeMethod(object.data(), "incA");
- QCOMPARE(object->property("a").toInt(), 17);
+ QVERIFY(!object.isNull());
- QCOMPARE(object->property("b").toDouble(), 0.25);
- QMetaObject::invokeMethod(object.data(), "incB");
- QCOMPARE(object->property("b").toDouble(), 13.5);
+ const QVariant t = object->property("t");
+ QCOMPARE(t.metaType(), QMetaType::fromType<QString>());
+ QCOMPARE(t.toString(), u"12"_s);
+}
+
+void tst_QmlCppCodegen::ambiguousAs()
+{
+ QQmlEngine e;
+ const QUrl url(u"qrc:/qt/qml/TestTypes/ambiguousAs.qml"_s);
+ QQmlComponent c(&e, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("other").value<QObject *>(), o.data());
+ o->setProperty("useSelf", QVariant::fromValue(false));
+ QCOMPARE(o->property("other").value<QObject *>(), nullptr);
+}
+
+void tst_QmlCppCodegen::ambiguousSignals()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguousSignals.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"tomorrow"_s);
+ Person *p = qobject_cast<Person *>(o.data());
+ QVERIFY(p);
+ emit p->ambiguous(12);
+ QCOMPARE(o->objectName(), u"12foo"_s);
+ emit p->ambiguous();
+ QCOMPARE(o->objectName(), u"9foo"_s);
}
void tst_QmlCppCodegen::anchorsFill()
@@ -231,342 +602,629 @@ void tst_QmlCppCodegen::anchorsFill()
QCOMPARE(child->property("width").toInt(), 47);
}
-void tst_QmlCppCodegen::signalHandler()
+void tst_QmlCppCodegen::argumentConversion()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signal.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/argumentConversion.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ auto checkNaN = [&](const char *propName) {
+ const QVariant prop = o->property(propName);
+ QCOMPARE(prop.metaType(), QMetaType::fromType<double>());
+ QVERIFY(qIsNaN(prop.toDouble()));
+ };
+
+ checkNaN("a");
+ checkNaN("b");
+ checkNaN("e");
+
+ QCOMPARE(o->property("c").toDouble(), 3.0);
+ QCOMPARE(o->property("d").toDouble(), -1.0);
+ QCOMPARE(o->property("f").toDouble(), 10.0);
+}
+
+void tst_QmlCppCodegen::array()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/array.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->objectName(), QString());
- QCOMPARE(object->property("ff").toInt(), 4);
+ const QJSValue value1 = object->property("values1").value<QJSValue>();
+ QVERIFY(value1.isArray());
+ QCOMPARE(value1.property(u"length"_s).toInt(), 3);
+ QCOMPARE(value1.property(0).toInt(), 1);
+ QCOMPARE(value1.property(1).toInt(), 2);
+ QCOMPARE(value1.property(2).toInt(), 3);
- object->setObjectName(u"foo"_s);
- QCOMPARE(object->property("ff").toInt(), 12);
+ const QJSValue value2 = object->property("values2").value<QJSValue>();
+ QVERIFY(value2.isArray());
+ QCOMPARE(value2.property(u"length"_s).toInt(), 0);
}
-void tst_QmlCppCodegen::idAccess()
+void tst_QmlCppCodegen::arrayCtor()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/idAccess.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/arrayCtor.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QVERIFY(object->property("y").toInt() != 48);
- QCOMPARE(object->property("y").toInt(), 12);
- object->setProperty("z", 13);
- QCOMPARE(object->property("y").toInt(), 13);
- object->setProperty("x", QVariant::fromValue(333));
- QCOMPARE(object->property("y").toInt(), 48);
+ QCOMPARE(object->property("defaultCtor"), QVariant::fromValue(QList<int>()));
+ QCOMPARE(object->property("oneArgCtor"), QVariant::fromValue(QList<int>(5)));
+ QCOMPARE(object->property("multiArgCtor"), QVariant::fromValue(QList<int>({2, 3, 3, 4})));
+ QCOMPARE(object->property("arrayTrue"), QVariant::fromValue(QList<bool>({true})));
+ QCOMPARE(object->property("arrayFalse"), QVariant::fromValue(QList<bool>({false})));
+ QCOMPARE(object->property("arrayNegative"), QVariant::fromValue(QList<double>()));
+}
- // The binding was broken by setting the property
- object->setProperty("z", 14);
- QCOMPARE(object->property("y").toInt(), 48);
+void tst_QmlCppCodegen::asCast()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/asCast.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
- QObject *ttt = qmlContext(object.data())->objectForName(u"ttt"_s);
- QFont f = qvariant_cast<QFont>(ttt->property("font"));
- QCOMPARE(f.pointSize(), 22);
+ QQmlContext *context = qmlContext(root.data());
+ const QObject *object = context->objectForName(u"object"_s);
+ const QObject *item = context->objectForName(u"item"_s);
+ const QObject *rectangle = context->objectForName(u"rectangle"_s);
+ const QObject *dummy = context->objectForName(u"dummy"_s);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsObject")), object);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsItem")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsDummy")), nullptr);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsObject")), item);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsItem")), item);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsDummy")), nullptr);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsObject")), rectangle);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsItem")), rectangle);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsRectangle")), rectangle);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsDummy")), nullptr);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsObject")), dummy);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsItem")), dummy);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsDummy")), dummy);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsObject")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsItem")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("nullAsDummy")), nullptr);
+
+ QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsObject")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsItem")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsRectangle")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(root->property("undefinedAsDummy")), nullptr);
}
-static QByteArray arg1()
+void tst_QmlCppCodegen::attachedBaseEnum()
{
- const QStringList args = QCoreApplication::instance()->arguments();
- return args.size() > 1 ? args[1].toUtf8() : QByteArray("undefined");
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/attachedBaseEnum.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QObject *drag = qvariant_cast<QObject *>(object->property("drag"));
+ QVERIFY(drag);
+
+ // Drag.YAxis is 2, but we cannot #include it here.
+ bool ok = false;
+ QCOMPARE(drag->property("axis").toInt(&ok), 2);
+ QVERIFY(ok);
}
-void tst_QmlCppCodegen::globals()
+void tst_QmlCppCodegen::attachedSelf()
{
QQmlEngine engine;
- int exitCode = -1;
- QObject::connect(&engine, &QQmlEngine::exit, [&](int code) { exitCode = code; });
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/globals.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/SelectionRectangle.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- const QByteArray message = QByteArray("Start 2 ") + arg1();
- QTest::ignoreMessage(QtDebugMsg, message.constData());
+ QObject *handle = qvariant_cast<QObject *>(o->property("aa"));
+ QVERIFY(handle);
+ QVERIFY(qvariant_cast<QObject *>(handle->property("rect")) != nullptr);
+}
+void tst_QmlCppCodegen::attachedType()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/text.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QTRY_COMPARE(exitCode, 0);
+ QCOMPARE(object->property("dayz").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
+ QCOMPARE(object->property("oParty").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
- QObject *application = qvariant_cast<QObject *>(object->property("application"));
- QVERIFY(application);
- QCOMPARE(QString::fromUtf8(application->metaObject()->className()),
- u"QQuickApplication"_s);
+ QObject *party = qvariant_cast<QObject *>(object->property("party"));
+ QVERIFY(party);
+ QCOMPARE(party->property("eee").toInt(), 21);
+ QCOMPARE(party->property("fff").toInt(), 33);
+ QCOMPARE(object->property("ggg").toInt(), 37);
+}
- QTest::ignoreMessage(QtDebugMsg, "End");
- QMetaObject::invokeMethod(application, "aboutToQuit");
+void tst_QmlCppCodegen::badSequence()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/badSequence.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- const QVariant somewhere = object->property("somewhere");
- QCOMPARE(somewhere.userType(), QMetaType::QUrl);
- QCOMPARE(qvariant_cast<QUrl>(somewhere).toString(), u"qrc:/somewhere/else.qml"_s);
+ Person *self = qobject_cast<Person *>(o.data());
+ QVERIFY(self);
+ QVERIFY(self->barzles().isEmpty());
+ QVERIFY(self->cousins().isEmpty());
- const QVariant somewhereString = object->property("somewhereString");
- QCOMPARE(somewhereString.userType(), QMetaType::QString);
- QCOMPARE(somewhereString.toString(), u"qrc:/somewhere/else.qml"_s);
+ Person *other = o->property("other").value<Person *>();
+ QVERIFY(other);
- const QVariant plain = object->property("plain");
- QCOMPARE(plain.userType(), QMetaType::QUrl);
- QCOMPARE(qvariant_cast<QUrl>(plain).toString(), u"/not/here.qml"_s);
+ QVERIFY(other->barzles().isEmpty());
+ QVERIFY(other->cousins().isEmpty());
+
+ Barzle f1;
+ Barzle f2;
+ const QList<Barzle *> barzles { &f1, &f2 };
+ const QList<Person *> cousins { self, other };
+
+ other->setBarzles(barzles);
+ QCOMPARE(self->barzles(), barzles);
+ QCOMPARE(self->property("l").toInt(), 2);
+
+ other->setCousins(cousins);
+ QCOMPARE(self->cousins(), cousins);
+ QCOMPARE(self->property("m").toInt(), 2);
+
+ QQmlListProperty<Person> others
+ = self->property("others").value<QQmlListProperty<Person>>();
+ QCOMPARE(others.count(&others), 2);
+ QCOMPARE(others.at(&others, 0), cousins[0]);
+ QCOMPARE(others.at(&others, 1), cousins[1]);
+
+ QQmlListProperty<Person> momsCousins
+ = self->property("momsCousins").value<QQmlListProperty<Person>>();
+ QCOMPARE(momsCousins.count(&momsCousins), 2);
+ QCOMPARE(momsCousins.at(&momsCousins, 0), cousins[0]);
+ QCOMPARE(momsCousins.at(&momsCousins, 1), cousins[1]);
+
+ QQmlListProperty<Person> dadsCousins
+ = self->property("dadsCousins").value<QQmlListProperty<Person>>();
+ QCOMPARE(dadsCousins.count(&dadsCousins), 1);
+ QCOMPARE(dadsCousins.at(&dadsCousins, 0), other);
}
-void tst_QmlCppCodegen::multiLookup()
+static bool expectingMessage = false;
+static void handler(QtMsgType type, const QMessageLogContext &, const QString &message)
+{
+ QVERIFY(expectingMessage);
+ QCOMPARE(type, QtDebugMsg);
+ QCOMPARE(message, u"false");
+ expectingMessage = false;
+}
+
+void tst_QmlCppCodegen::basicBlocksWithBackJump()
{
- // Multiple lookups of singletons (Qt in this case) don't clash with one another.
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/immediateQuit.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicBlocksWithBackJump.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
- const QByteArray message = QByteArray("End: ") + arg1();
- QTest::ignoreMessage(QtDebugMsg, message.constData());
+ const auto oldHandler = qInstallMessageHandler(&handler);
+ const auto guard = qScopeGuard([oldHandler]() { qInstallMessageHandler(oldHandler); });
- QSignalSpy quitSpy(&engine, &QQmlEngine::quit);
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(quitSpy.size(), 1);
+ // t1 does not log anything
+ QMetaObject::invokeMethod(o.data(), "t1");
+
+ // t2 logs "false" exactly once
+ expectingMessage = true;
+ QMetaObject::invokeMethod(o.data(), "t2");
+ QVERIFY(!expectingMessage);
+
+ // t3 logs "false" exactly once
+ expectingMessage = true;
+ QMetaObject::invokeMethod(o.data(), "t3");
+ QVERIFY(!expectingMessage);
}
-void tst_QmlCppCodegen::enums()
+void tst_QmlCppCodegen::basicBlocksWithBackJump_infinite()
{
QQmlEngine engine;
- {
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Enums.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicBlocksWithBackJump_infinite.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+}
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/Enums.qml:4:1: "
- "QML Enums: Layout must be attached to Item elements");
- QScopedPointer<QObject> object(component.create());
+void tst_QmlCppCodegen::basicDTZ()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/basicDTZ.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
- QVERIFY(!object.isNull());
- bool ok = false;
- QCOMPARE(object->property("appState").toInt(&ok), 2);
- QVERIFY(ok);
- QCOMPARE(object->property("color").toString(), u"blue"_s);
+ QCOMPARE(o->property("title").toString(), u"none");
- QTRY_COMPARE(object->property("appState").toInt(&ok), 1);
- QVERIFY(ok);
- QCOMPARE(object->property("color").toString(), u"green"_s);
+ QMetaObject::invokeMethod(o.data(), "t1");
+ QMetaObject::invokeMethod(o.data(), "t2");
+ QMetaObject::invokeMethod(o.data(), "t3");
+
+ QCOMPARE(o->property("title").toString(), u"Baz 41");
+}
+
+void tst_QmlCppCodegen::bindToValueType()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/bindToValueType.qml"_s));
+ checkColorProperties(&component);
+}
+
+void tst_QmlCppCodegen::bindingExpression()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BindingExpression.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+
+ QObject *child = qmlContext(object.data())->objectForName(u"child"_s);
+
+ double width = 200;
+ double y = 10;
+ for (int i = 0; i < 10; ++i) {
+ QCOMPARE(object->property("width").toDouble(), width);
+ QCOMPARE(object->property("height").toDouble(), width);
+ QCOMPARE(object->property("y").toDouble(), y);
- const auto func = qmlAttachedPropertiesFunction(
- object.data(), QMetaType::fromName("QQuickLayout*").metaObject());
+ const double childY = y + (width - 100) / 2;
+ QCOMPARE(child->property("y").toDouble(), childY);
+ QCOMPARE(object->property("mass"), childY > 100 ? u"heavy"_s : u"light"_s);
+ QCOMPARE(object->property("test_division").toDouble(), width / 1000 + 50);
+ QCOMPARE(object->property("test_ternary").toDouble(), 2.2);
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/enumsInOtherObject.qml:4:25: "
- "QML Enums: Layout must be attached to Item elements");
- QObject *attached = qmlAttachedPropertiesObject(object.data(), func);
+ const int test_switch = object->property("test_switch").toInt();
+ switch (int(width) % 3) {
+ case 0:
+ QCOMPARE(test_switch, 130);
+ break;
+ case 1:
+ QCOMPARE(test_switch, 380);
+ break;
+ case 2:
+ QCOMPARE(test_switch, 630);
+ break;
+ }
- const QVariant prop = attached->property("alignment");
- QVERIFY(prop.isValid());
- QCOMPARE(qvariant_cast<Qt::Alignment>(prop), Qt::AlignCenter);
+ width = 200 * i;
+ y = 10 + i;
+ object->setProperty("width", width);
+ object->setProperty("y", y);
}
- {
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumsInOtherObject.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("color").toString(), u"blue"_s);
- QTRY_COMPARE(object->property("color").toString(), u"green"_s);
+}
+
+void tst_QmlCppCodegen::blockComments()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/blockComments.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("implicitHeight").toDouble(), 8.0);
+}
+
+void tst_QmlCppCodegen::boolCoercions()
+{
+ QQmlEngine e;
+ const QUrl url(u"qrc:/qt/qml/TestTypes/boolCoercions.qml"_s);
+ QQmlComponent c(&e, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + ":41:5: Unable to assign [undefined] to bool"_L1));
+ QScopedPointer<QObject> o(c.create());
+
+ for (char p = '1'; p <= '8'; ++p) {
+ const QVariant t = o->property(qPrintable(QLatin1String("t%1").arg(p)));
+ QCOMPARE(t.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(t.toBool());
+ }
+
+ for (char p = '1'; p <= '5'; ++p) {
+ const QVariant f = o->property(qPrintable(QLatin1String("f%1").arg(p)));
+ QCOMPARE(f.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(!f.toBool());
}
}
-void tst_QmlCppCodegen::funcWithParams()
+void tst_QmlCppCodegen::boolPointerMerge()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/boolPointerMerge.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QObject *item = o->property("item").value<QObject *>();
+ QVERIFY(item);
+ QCOMPARE(item->property("ppp").toInt(), -99);
+}
+
+void tst_QmlCppCodegen::boundComponents()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/funcWithParams.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("bar").toInt(), 30);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/boundComponents.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QObject *c1o = o->property("o").value<QObject *>();
+ QVERIFY(c1o != nullptr);
+ QCOMPARE(c1o->objectName(), u"bar"_s);
+
+ QObject *c2o = c1o->property("o").value<QObject *>();
+ QVERIFY(c2o != nullptr);
+ QCOMPARE(c2o->objectName(), u"bar12"_s);
}
-void tst_QmlCppCodegen::intOverflow()
+void tst_QmlCppCodegen::callContextPropertyLookupResult()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intOverflow.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("a").toDouble(), 1.09951162778e+12);
- QCOMPARE(object->property("b").toInt(), 5);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callContextPropertyLookupResult.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QVERIFY(qvariant_cast<QQmlComponent *>(o->property("c")) != nullptr);
}
-void tst_QmlCppCodegen::stringLength()
+void tst_QmlCppCodegen::callWithSpread()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringLength.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("stringLength").toInt(), 8);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callWithSpread.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtCriticalMsg, "That is great!");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
}
-void tst_QmlCppCodegen::scopeVsObject()
+void tst_QmlCppCodegen::colorAsVariant()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeVsObject.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorAsVariant.qml"_s));
+ checkColorProperties(&component);
+}
+
+void tst_QmlCppCodegen::colorString()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorString.qml"_s));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(qvariant_cast<QColor>(rootObject->property("c")), QColor::fromRgb(0xdd, 0xdd, 0xdd));
+ QCOMPARE(qvariant_cast<QColor>(rootObject->property("d")), QColor::fromRgb(0xaa, 0xaa, 0xaa));
+ QCOMPARE(qvariant_cast<QColor>(rootObject->property("e")), QColor::fromRgb(0x11, 0x22, 0x33));
+}
+
+void tst_QmlCppCodegen::compareOriginals()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compareOriginals.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->property("objectName").toString(), u"foobar"_s);
+
+ QCOMPARE(object->property("compareOriginals").toInt(), 5);
+ QVERIFY(object->property("optionalThis").toBool());
}
-void tst_QmlCppCodegen::compositeTypeMethod()
+void tst_QmlCppCodegen::comparisonTypes()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositeTypeMethod.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/comparisonTypes.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QSignalSpy spy(object.data(), SIGNAL(foo()));
- QTRY_VERIFY(spy.size() > 0);
+
+ QCOMPARE(object->property("found").toInt(), 1);
+ QCOMPARE(object->property("foundStrict").toInt(), 0);
+
+ QCOMPARE(object->property("foundNot").toInt(), 2);
+ QCOMPARE(object->property("foundNotStrict").toInt(), 10);
}
-void tst_QmlCppCodegen::excessiveParameters()
+void tst_QmlCppCodegen::componentReturnType()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/excessiveParameters.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/componentReturnType.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QSignalSpy spy(object.data(), SIGNAL(foo()));
- QTRY_VERIFY(spy.size() > 0);
+
+ QCOMPARE(object->property("count").toInt(), 10);
+ QCOMPARE(QQmlListReference(object.data(), "children").count(), 11);
}
-void tst_QmlCppCodegen::jsImport()
+void tst_QmlCppCodegen::compositeSingleton()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsimport.qml"_s));
+ engine.addImportPath(u":/qt/qml/TestTypes/imports/"_s);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositesingleton.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("value").toInt(), 42);
+ QScopedPointer<QObject> o(component.create());
+ QCOMPARE(o->property("x").toDouble(), 4.5);
+ QCOMPARE(o->property("y").toDouble(), 10.0);
+ QCOMPARE(o->property("smooth").toBool(), true);
}
-void tst_QmlCppCodegen::jsmoduleImport()
+void tst_QmlCppCodegen::compositeTypeMethod()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsmoduleimport.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositeTypeMethod.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->property("ok").toBool(), true);
- QVariant okFunc = object->property("okFunc");
- QCOMPARE(okFunc.metaType(), QMetaType::fromType<QJSValue>());
- QJSValue val = engine.toScriptValue(okFunc);
- QJSValue result = val.call();
- QVERIFY(result.isBool());
- QVERIFY(result.toBool());
+ QSignalSpy spy(object.data(), SIGNAL(foo()));
+ QTRY_VERIFY(spy.size() > 0);
}
-void tst_QmlCppCodegen::methods()
+void tst_QmlCppCodegen::consoleObject()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/methods.qml"_s));
- QVERIFY(component.isReady());
-
- QTest::ignoreMessage(QtDebugMsg, "The Bar");
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"TypeError: .* is not a function"_s));
- QScopedPointer<QObject> obj(component.create());
- QVERIFY(obj);
- BirthdayParty *party(qobject_cast<BirthdayParty *>(obj.data()));
-
- QVERIFY(party && party->host());
- QCOMPARE(party->guestCount(), 5);
+ static const QString urlString = u"qrc:/qt/qml/TestTypes/consoleObject.qml"_s;
+ QQmlComponent c(&engine, QUrl(urlString));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- bool foundGreen = false;
- bool foundFoo = false;
- for (int ii = 0; ii < party->guestCount(); ++ii) {
- if (party->guest(ii)->name() == u"William Green"_s)
- foundGreen = true;
- if (party->guest(ii)->name() == u"The Foo"_s)
- foundFoo = true;
- }
+ QTest::ignoreMessage(QtDebugMsg, "b 4.55");
+ QTest::ignoreMessage(QtDebugMsg, "b 4.55");
+ QTest::ignoreMessage(QtInfoMsg, "b 4.55");
+ QTest::ignoreMessage(QtWarningMsg, "b 4.55");
+ QTest::ignoreMessage(QtCriticalMsg, "b 4.55");
- QVERIFY(foundGreen);
- QVERIFY(foundFoo);
+ // Unfortunately we cannot check the logging category with QTest::ignoreMessage
+ QTest::ignoreMessage(QtDebugMsg, "b 4.55");
+ QTest::ignoreMessage(QtDebugMsg, "b 4.55");
+ QTest::ignoreMessage(QtInfoMsg, "b 4.55");
+ QTest::ignoreMessage(QtWarningMsg, "b 4.55");
+ QTest::ignoreMessage(QtCriticalMsg, "b 4.55");
- QCOMPARE(obj->property("n1").toString(), u"onGurk"_s);
- QCOMPARE(obj->property("n2").toString(), u"onSemmeln"_s);
- QCOMPARE(obj->property("n3"), QVariant());
+ const QRegularExpression re(u"QQmlComponentAttached\\(0x[0-9a-f]+\\) b 4\\.55"_s);
+ QTest::ignoreMessage(QtDebugMsg, re);
+ QTest::ignoreMessage(QtDebugMsg, re);
+ QTest::ignoreMessage(QtInfoMsg, re);
+ QTest::ignoreMessage(QtWarningMsg, re);
+ QTest::ignoreMessage(QtCriticalMsg, re);
- {
- QVariant ret;
- obj->metaObject()->invokeMethod(obj.data(), "retrieveVar", Q_RETURN_ARG(QVariant, ret));
- QCOMPARE(ret.typeId(), QMetaType::QString);
- QCOMPARE(ret.toString(), u"Jack Smith"_s);
- }
+ QTest::ignoreMessage(QtDebugMsg, "a undefined b false null 7");
+ QTest::ignoreMessage(QtDebugMsg, "");
+ QTest::ignoreMessage(QtDebugMsg, "4");
+ QTest::ignoreMessage(QtDebugMsg, "");
- {
- QString ret;
- obj->metaObject()->invokeMethod(obj.data(), "retrieveString", Q_RETURN_ARG(QString, ret));
- QCOMPARE(ret, u"Jack Smith"_s);
- }
+ const QRegularExpression re2(u"QQmlComponentAttached\\(0x[0-9a-f]+\\)"_s);
+ QTest::ignoreMessage(QtDebugMsg, re2);
- QCOMPARE(party->host()->shoeSize(), 12);
- obj->metaObject()->invokeMethod(obj.data(), "storeElement");
- QCOMPARE(party->host()->shoeSize(), 13);
- QJSManagedValue v = engine.toManagedValue(obj->property("dresses"));
- QVERIFY(v.isArray());
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QJSManagedValue inner(v.property(2), &engine);
- QVERIFY(inner.isArray());
- QCOMPARE(inner.property(0).toInt(), 1);
- QCOMPARE(inner.property(1).toInt(), 2);
- QCOMPARE(inner.property(2).toInt(), 3);
+ auto oldHandler = qInstallMessageHandler(
+ [](QtMsgType, const QMessageLogContext &ctxt, const QString &) {
+ QCOMPARE(ctxt.file, urlString.toUtf8());
+ QCOMPARE(ctxt.function, QByteArray("expression for onCompleted"));
+ QVERIFY(ctxt.line > 0);
+ });
+ const auto guard = qScopeGuard([oldHandler]() { qInstallMessageHandler(oldHandler); });
- QCOMPARE(obj->property("enumValue").toInt(), 2);
+ QScopedPointer<QObject> p(c.create());
+ QVERIFY(!p.isNull());
}
-void tst_QmlCppCodegen::math()
+void tst_QmlCppCodegen::consoleTrace()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/math.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/consoleTrace.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+#if !defined(QT_NO_DEBUG) || defined(QT_TEST_FORCE_INTERPRETER)
+ // All line numbers in debug mode or when interpreting
+
+ QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6)
+b (qrc:/qt/qml/TestTypes/consoleTrace.qml:5)
+a (qrc:/qt/qml/TestTypes/consoleTrace.qml:4)
+expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml:7))");
+#else
+ // Only top-most line number otherwise
+
+ QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6)
+b (qrc:/qt/qml/TestTypes/consoleTrace.qml)
+a (qrc:/qt/qml/TestTypes/consoleTrace.qml)
+expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml))");
+#endif
+
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->property("a").toInt(), 9);
- QCOMPARE(object->property("b").toDouble(), 50.0 / 22.0);
}
-void tst_QmlCppCodegen::unknownParameter()
+void tst_QmlCppCodegen::construct()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownParameter.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/construct.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- QCOMPARE(object->property("cppProp").toInt(), 18);
+
+ const QJSManagedValue v = engine.toManagedValue(object->property("foo"));
+ QVERIFY(v.isError());
+ QCOMPARE(v.toString(), u"Error: bar"_s);
+
+ QCOMPARE(object->property("aaa").toInt(), 12);
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/construct.qml:9: Error: ouch");
+ object->metaObject()->invokeMethod(object.data(), "ouch");
+ QCOMPARE(object->property("aaa").toInt(), 13);
}
-void tst_QmlCppCodegen::array()
+void tst_QmlCppCodegen::contextParam()
{
+ // The compiler cannot resolve context parameters.
+ // Make sure the binding is interpreted.
+
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/array.qml"_s));
+
+ QVariantMap m;
+ m.insert(u"foo"_s, 10);
+ engine.rootContext()->setContextProperty(u"contextParam"_s, m);
+
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/contextParam.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- const QJSValue value1 = object->property("values1").value<QJSValue>();
- QVERIFY(value1.isArray());
- QCOMPARE(value1.property(u"length"_s).toInt(), 3);
- QCOMPARE(value1.property(0).toInt(), 1);
- QCOMPARE(value1.property(1).toInt(), 2);
- QCOMPARE(value1.property(2).toInt(), 3);
- const QJSValue value2 = object->property("values2").value<QJSValue>();
- QVERIFY(value2.isArray());
- QCOMPARE(value2.property(u"length"_s).toInt(), 0);
+ QCOMPARE(object->property("foo").toInt(), 10);
}
-void tst_QmlCppCodegen::equalsUndefined()
+void tst_QmlCppCodegen::conversionDecrement()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalsUndefined.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionDecrement.qml"_s));
- QCOMPARE(object->property("a").toInt(), 50);
- QCOMPARE(object->property("b").toInt(), 5000);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("currentPageIndex").toInt(), 0);
+ o->setProperty("pages", 5);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 3);
+ o->setProperty("pages", 4);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 0);
+ o->setProperty("pages", 6);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 4);
+ o->setProperty("pages", 60);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 3);
+}
+
+void tst_QmlCppCodegen::conversionInDeadCode()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionInDeadCode.qml"_s));
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("a").toInt(), -4);
+ QCOMPARE(o->property("b").toInt(), 12);
+ QCOMPARE(o->property("c").toInt(), 10);
+ QCOMPARE(o->property("d").toInt(), 10);
+ QCOMPARE(o->property("e").toInt(), 10);
+ QCOMPARE(o->property("f").toInt(), 20);
+ QCOMPARE(o->property("g").toInt(), 33);
}
void tst_QmlCppCodegen::conversions()
@@ -699,157 +1357,260 @@ void tst_QmlCppCodegen::conversions()
QVERIFY(!undef.isValid());
}
-void tst_QmlCppCodegen::interestingFiles_data()
+void tst_QmlCppCodegen::convertPrimitiveToVar()
{
- QTest::addColumn<QString>("file");
- QTest::addColumn<bool>("isValid");
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertPrimitiveToVar.qml"_s));
- QTest::addRow("conversions2") << u"conversions2.qml"_s << true;
- QTest::addRow("TestCase") << u"TestCase.qml"_s << true;
- QTest::addRow("layouts") << u"layouts.qml"_s << true;
- QTest::addRow("interactive") << u"interactive.qml"_s << true;
- QTest::addRow("Panel") << u"Panel.qml"_s << true;
- QTest::addRow("ProgressBar") << u"ProgressBar/ProgressBar.ui.qml"_s << true;
- QTest::addRow("Root") << u"ProgressBar/Root.qml"_s << true;
- QTest::addRow("noscope") << u"noscope.qml"_s << true;
- QTest::addRow("dynamicscene") << u"dynamicscene.qml"_s << true;
- QTest::addRow("curlygrouped") << u"curlygrouped.qml"_s << true;
- QTest::addRow("cycleHead") << u"cycleHead.qml"_s << false;
- QTest::addRow("deadStoreLoop") << u"deadStoreLoop.qml"_s << true;
- QTest::addRow("moveRegVoid") << u"moveRegVoid.qml"_s << true;
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("offsetValue").toInt(), 41);
}
-void tst_QmlCppCodegen::interestingFiles()
+void tst_QmlCppCodegen::convertQJSPrimitiveValueToIntegral()
{
- QFETCH(QString, file);
- QFETCH(bool, isValid);
-
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/%1"_s.arg(file)));
- if (isValid) {
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- } else {
- QVERIFY(component.isError());
- }
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertQJSPrimitiveValueToIntegral.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
}
-void tst_QmlCppCodegen::extendedTypes()
+void tst_QmlCppCodegen::convertToOriginalReadAcumulatorForUnaryOperators()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/extendedTypes.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
-
- QTest::ignoreMessage(QtDebugMsg, "6 QSizeF(10, 20) 30");
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/convertToOriginalReadAcumulatorForUnaryOperators.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
- QCOMPARE(object->property("a").toInt(), 6);
- QCOMPARE(qvariant_cast<QSizeF>(object->property("b")), QSizeF(10, 20));
- QCOMPARE(object->property("c").toInt(), 30);
- QCOMPARE(object->property("d").toString(), u"QSizeF(10, 20)"_s);
+void tst_QmlCppCodegen::cppMethodListReturnType()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/CppMethodListReturnType.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QCOMPARE(object->property("e").toInt(), 2);
+ QCOMPARE(o->property("list").toList()[2].toInt(), 2);
}
-void tst_QmlCppCodegen::construct()
+void tst_QmlCppCodegen::cppValueTypeList()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/construct.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s));
QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
-
- const QJSManagedValue v = engine.toManagedValue(object->property("foo"));
- QVERIFY(v.isError());
- QCOMPARE(v.toString(), u"Error: bar"_s);
+ QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
+ QCOMPARE(object->property("a").toInt(), 16);
+ QMetaObject::invokeMethod(object.data(), "incA");
+ QCOMPARE(object->property("a").toInt(), 17);
- QCOMPARE(object->property("aaa").toInt(), 12);
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/construct.qml:9: Error: ouch");
- object->metaObject()->invokeMethod(object.data(), "ouch");
- QCOMPARE(object->property("aaa").toInt(), 13);
+ QCOMPARE(object->property("b").toDouble(), 0.25);
+ QMetaObject::invokeMethod(object.data(), "incB");
+ QCOMPARE(object->property("b").toDouble(), 13.5);
}
-void tst_QmlCppCodegen::contextParam()
+void tst_QmlCppCodegen::dateConstruction()
{
- // The compiler cannot resolve context parameters.
- // Make sure the binding is interpreted.
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConstruction.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QDateTime now = QDateTime::currentDateTime();
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(o->property("now").value<QDateTime>().toMSecsSinceEpoch() >= now.toMSecsSinceEpoch());
+ QCOMPARE(o->property("now2"), o->property("now"));
+ QCOMPARE(o->property("fromString").value<QDateTime>(),
+ QDateTime(QDate(1995, 12, 17), QTime(3, 24), QTimeZone::LocalTime));
+ QCOMPARE(o->property("fromNumber").value<QDateTime>().toMSecsSinceEpoch(), 777);
+ QCOMPARE(o->property("fromPrimitive").value<QDateTime>().toMSecsSinceEpoch(), 57);
+ o->setObjectName("foo"_L1);
+ QCOMPARE(o->property("fromPrimitive").value<QDateTime>(),
+ QDateTime(QDate(1997, 2, 13), QTime(13, 4, 12), QTimeZone::LocalTime));
+
+ QCOMPARE(o->property("from2").value<QDateTime>(),
+ QDateTime(QDate(1996, 2, 1), QTime(), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from3").value<QDateTime>(),
+ QDateTime(QDate(1996, 3, 3), QTime(), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from4").value<QDateTime>(),
+ QDateTime(QDate(1996, 4, 4), QTime(5, 0), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from5").value<QDateTime>(),
+ QDateTime(QDate(1996, 5, 5), QTime(6, 7), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from6").value<QDateTime>(),
+ QDateTime(QDate(1996, 6, 6), QTime(7, 8, 9), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from7").value<QDateTime>(),
+ QDateTime(QDate(1996, 7, 7), QTime(8, 9, 10, 11), QTimeZone::LocalTime));
+ QCOMPARE(o->property("from8").value<QDateTime>(),
+ QDateTime(QDate(1996, 8, 8), QTime(9, 10, 11, 12), QTimeZone::LocalTime));
+
+ QCOMPARE(o->property("withUnderflow").value<QDateTime>(),
+ QDateTime(QDate(-6, 7, 24), QTime(16, 51, 50, 990), QTimeZone::LocalTime));
+ QCOMPARE(o->property("invalid").value<QDateTime>(), QDateTime());
+}
+void tst_QmlCppCodegen::dateConversions()
+{
QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConversions.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- QVariantMap m;
- m.insert(u"foo"_s, 10);
- engine.rootContext()->setContextProperty(u"contextParam"_s, m);
+ Druggeljug *ref = engine.singletonInstance<Druggeljug *>("TestTypes", "Druggeljug");
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/contextParam.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
+ const QDateTime refDate = engine.coerceValue<QDate, QDateTime>(ref->myDate());
+ const QDateTime refTime = engine.coerceValue<QTime, QDateTime>(ref->myTime());
+
+ QCOMPARE(o->property("date").value<QDateTime>(), refDate);
+ QCOMPARE(o->property("time").value<QDateTime>(), refTime);
+
+ QCOMPARE(o->property("dateString").toString(),
+ (engine.coerceValue<QDateTime, QString>(refDate)));
+ QCOMPARE(o->property("dateNumber").toDouble(),
+ (engine.coerceValue<QDateTime, double>(refDate)));
+ QCOMPARE(o->property("timeString").toString(),
+ (engine.coerceValue<QDateTime, QString>(refTime)));
+ QCOMPARE(o->property("timeNumber").toDouble(),
+ (engine.coerceValue<QDateTime, double>(refTime)));
+
+ QMetaObject::invokeMethod(o.data(), "shuffle");
+
+ QCOMPARE(ref->myDate(), (engine.coerceValue<QDateTime, QDate>(refDate)));
+ QCOMPARE(ref->myTime(), (engine.coerceValue<QDateTime, QTime>(refTime)));
+
+ const QDate date = ref->myDate();
+ const QTime time = ref->myTime();
+
+ QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDate, QString>(date)));
+ QCOMPARE(o->property("dateNumber").toDouble(), (engine.coerceValue<QDate, double>(date)));
+ QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QTime, QString>(time)));
+ QCOMPARE(o->property("timeNumber").toDouble(), (engine.coerceValue<QTime, double>(time)));
+
+ QMetaObject::invokeMethod(o.data(), "fool");
+
+ QCOMPARE(ref->myDate(), (engine.coerceValue<QTime, QDate>(time)));
+ QCOMPARE(ref->myTime(), (engine.coerceValue<QDate, QTime>(date)));
+
+ QMetaObject::invokeMethod(o.data(), "invalidate");
+ QMetaObject::invokeMethod(o.data(), "shuffle");
+
+ QCOMPARE(o->property("dateString").toString(), "Invalid Date"_L1);
+ QVERIFY(qIsNaN(o->property("dateNumber").toDouble()));
+ QCOMPARE(o->property("timeString").toString(), "Invalid Date"_L1);
+ QVERIFY(qIsNaN(o->property("timeNumber").toDouble()));
- QCOMPARE(object->property("foo").toInt(), 10);
}
-void tst_QmlCppCodegen::attachedType()
+void tst_QmlCppCodegen::deadShoeSize()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/text.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("dayz").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
- QCOMPARE(object->property("oParty").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
-
- QObject *party = qvariant_cast<QObject *>(object->property("party"));
- QVERIFY(party);
- QCOMPARE(party->property("eee").toInt(), 21);
- QCOMPARE(party->property("fff").toInt(), 33);
- QCOMPARE(object->property("ggg").toInt(), 37);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/deadShoeSize.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/deadShoeSize.qml:5: Error: ouch");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("shoeSize").toInt(), 0);
}
-void tst_QmlCppCodegen::componentReturnType()
+void tst_QmlCppCodegen::dialogButtonBox()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/componentReturnType.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
+ const QUrl copy(u"qrc:/qt/qml/TestTypes/dialogButtonBox.qml"_s);
+ QQmlComponent c(&engine, copy);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QObject *footer = o->property("footer").value<QObject *>();
+ QVERIFY(footer);
- QCOMPARE(object->property("count").toInt(), 10);
- QCOMPARE(QQmlListReference(object.data(), "children").count(), 11);
+ QCOMPARE(footer->property("standardButtons").value<QPlatformDialogHelper::StandardButton>(),
+ QPlatformDialogHelper::Ok | QPlatformDialogHelper::Cancel);
}
-void tst_QmlCppCodegen::onAssignment()
+void tst_QmlCppCodegen::enumConversion()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/pressAndHoldButton.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QCOMPARE(object->property("pressed").toBool(), false);
- QCOMPARE(object->property("scale").toDouble(), 1.0);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumConversion.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- object->metaObject()->invokeMethod(object.data(), "press");
- QTRY_COMPARE(object->property("pressed").toBool(), true);
- QCOMPARE(object->property("scale").toDouble(), 0.9);
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("test").toInt(), 0x04);
+ QCOMPARE(o->property("test_1").toBool(), true);
+ QCOMPARE(o->objectName(), u"0m"_s);
+}
- object->metaObject()->invokeMethod(object.data(), "release");
- QCOMPARE(object->property("pressed").toBool(), false);
- QCOMPARE(object->property("scale").toDouble(), 1.0);
+void tst_QmlCppCodegen::enumFromBadSingleton()
+{
+ QQmlEngine e;
+ const QUrl url(u"qrc:/qt/qml/TestTypes/enumFromBadSingleton.qml"_s);
+ QQmlComponent c(&e, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+#if QT_DEPRECATED_SINCE(6,4)
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(
+ url.toString()
+ + u":5:5: TypeError: Cannot read property 'TestA' of undefined"_s));
+#else
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(
+ url.toString()
+ + u":5:5: ReferenceError: DummyObjekt is not defined"_s));
+#endif
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QVERIFY(o->objectName().isEmpty());
}
-namespace QmlCacheGeneratedCode {
-namespace _qt_qml_TestTypes_failures_qml {
-extern const QQmlPrivate::TypedFunction aotBuiltFunctions[];
+void tst_QmlCppCodegen::enumLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumLookup.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QCOMPARE(o->property("ready").toBool(), true);
}
+
+void tst_QmlCppCodegen::enumMarkedAsFlag()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumMarkedAsFlag.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QCOMPARE(o->property("flagValue").toInt(), 3);
}
-void tst_QmlCppCodegen::failures()
+void tst_QmlCppCodegen::enumProblems()
{
- const auto &aotFailure
- = QmlCacheGeneratedCode::_qt_qml_TestTypes_failures_qml::aotBuiltFunctions[0];
- QVERIFY(aotFailure.argumentTypes.isEmpty());
- QVERIFY(!aotFailure.functionPtr);
- QCOMPARE(aotFailure.extraData, 0);
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumProblems.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> outer(c.create());
+ QVERIFY(!outer.isNull());
+ QObject *inner = outer->property("o").value<QObject *>();
+ QVERIFY(inner);
+
+ Foo *bar = inner->property("bar").value<Foo *>();
+ QVERIFY(bar);
+ QCOMPARE(bar->type(), Foo::Component);
+
+ Foo *fighter = inner->property("fighter").value<Foo *>();
+ QVERIFY(fighter);
+ QCOMPARE(fighter->type(), Foo::Fighter);
+
+ QCOMPARE(outer->property("a").toInt(), FooFactory::B);
+ QCOMPARE(outer->property("b").toInt(), FooFactory::C);
+ QCOMPARE(outer->property("c").toInt(), FooFactory::D);
+ QCOMPARE(outer->property("d").toInt(), FooFactory::E);
}
void tst_QmlCppCodegen::enumScope()
@@ -861,283 +1622,370 @@ void tst_QmlCppCodegen::enumScope()
QCOMPARE(object->property("flow").toInt(), 1);
}
-void tst_QmlCppCodegen::unusedAttached()
+void tst_QmlCppCodegen::enums()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unusedAttached.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Enums.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/Enums.qml:4:1: "
+ "QML Enums: Layout must be attached to Item elements");
QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ bool ok = false;
+ QCOMPARE(object->property("appState").toInt(&ok), 2);
+ QVERIFY(ok);
+ QCOMPARE(object->property("color").toString(), u"blue"_s);
+
+ QTRY_COMPARE(object->property("appState").toInt(&ok), 1);
+ QVERIFY(ok);
+ QCOMPARE(object->property("color").toString(), u"green"_s);
+
const auto func = qmlAttachedPropertiesFunction(
- object.data(), QMetaType::fromName("QQuickKeyNavigationAttached*").metaObject());
+ object.data(), QMetaType::fromName("QQuickLayout*").metaObject());
+
QObject *attached = qmlAttachedPropertiesObject(object.data(), func);
- const QVariant prop = attached->property("priority");
+
+ const QVariant prop = attached->property("alignment");
QVERIFY(prop.isValid());
- QCOMPARE(QByteArray(prop.metaType().name()), "QQuickKeyNavigationAttached::Priority");
- bool ok = false;
- QCOMPARE(prop.toInt(&ok), 0);
- QVERIFY(ok);
+ QCOMPARE(qvariant_cast<Qt::Alignment>(prop), Qt::AlignCenter);
+
}
-void tst_QmlCppCodegen::attachedBaseEnum()
+void tst_QmlCppCodegen::enforceSignature()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/attachedBaseEnum.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enforceSignature.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QObject *drag = qvariant_cast<QObject *>(object->property("drag"));
- QVERIFY(drag);
+ const QVariant a = object->property("a");
+ QCOMPARE(a.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(a.value<QObject *>(), nullptr);
- // Drag.YAxis is 2, but we cannot #include it here.
- bool ok = false;
- QCOMPARE(drag->property("axis").toInt(&ok), 2);
- QVERIFY(ok);
+ const QVariant b = object->property("b");
+ QCOMPARE(b.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(b.value<QObject *>(), nullptr);
}
-void tst_QmlCppCodegen::nullAccess()
+void tst_QmlCppCodegen::enumsInOtherObject()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccess.qml"_s));
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/enumsInOtherObject.qml:4:25: "
+ "QML Enums: Layout must be attached to Item elements");
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumsInOtherObject.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
-
- QTest::ignoreMessage(QtWarningMsg,
- "qrc:/qt/qml/TestTypes/nullAccess.qml:4:5: TypeError: "
- "Cannot read property 'width' of null");
- QTest::ignoreMessage(QtWarningMsg,
- "qrc:/qt/qml/TestTypes/nullAccess.qml:5:5: TypeError: "
- "Cannot read property 'height' of null");
- QTest::ignoreMessage(QtWarningMsg,
- "qrc:/qt/qml/TestTypes/nullAccess.qml:6: TypeError: Value is null and "
- "could not be converted to an object");
QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("color").toString(), u"blue"_s);
+ QTRY_COMPARE(object->property("color").toString(), u"green"_s);
+}
- QCOMPARE(object->property("width").toDouble(), 0.0);
- QCOMPARE(object->property("height").toDouble(), 0.0);
+void tst_QmlCppCodegen::equalityQObjects()
+{
+ QQmlEngine engine;
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQObjects.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+ QScopedPointer<QObject> object(c1.create());
+ QVERIFY(!object.isNull() && !c1.isError());
+
+ QVERIFY(object->property("derivedIsNotNull").toBool());
+ QVERIFY(object->property("nullObjectIsNull").toBool());
+ QVERIFY(object->property("nonNullObjectIsNotNull").toBool());
+ QVERIFY(object->property("compareSameObjects").toBool());
+ QVERIFY(object->property("compareDifferentObjects").toBool());
+ QVERIFY(object->property("compareObjectWithNullObject").toBool());
+
+ QVERIFY(object->property("nonStrict_derivedIsNotNull").toBool());
+ QVERIFY(object->property("nonStrict_nullObjectIsNull").toBool());
+ QVERIFY(object->property("nonStrict_nonNullObjectIsNotNull").toBool());
+ QVERIFY(object->property("nonStrict_compareSameObjects").toBool());
+ QVERIFY(object->property("nonStrict_compareDifferentObjects").toBool());
+ QVERIFY(object->property("nonStrict_compareObjectWithNullObject").toBool());
}
-void tst_QmlCppCodegen::interceptor()
+void tst_QmlCppCodegen::equalityQUrl()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/interceptor.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("x").toInt(), 100);
- QCOMPARE(object->property("y").toInt(), 100);
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQUrl.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QVERIFY(object->property("width").toInt() != 200);
- QVERIFY(object->property("height").toInt() != 200);
- QVERIFY(object->property("qProperty1").toInt() != 300);
- QVERIFY(object->property("qProperty2").toInt() != 300);
- QTRY_COMPARE(object->property("width").toInt(), 200);
- QTRY_COMPARE(object->property("height").toInt(), 200);
- QTRY_COMPARE(object->property("qProperty1").toInt(), 300);
- QTRY_COMPARE(object->property("qProperty2").toInt(), 300);
+ QScopedPointer<QObject> object(c1.create());
+ QVERIFY(!object.isNull() && !c1.isError());
+ QVERIFY(object->property("emptyUrlStrict").toBool());
+ QVERIFY(object->property("emptyUrlWeak").toBool());
+ QVERIFY(object->property("sourceUrlStrict").toBool());
+ QVERIFY(object->property("sourceUrlWeak").toBool());
+ QVERIFY(object->property("sourceIsNotEmptyStrict").toBool());
+ QVERIFY(object->property("sourceIsNotEmptyWeak").toBool());
}
-void tst_QmlCppCodegen::nonNotifyable()
+void tst_QmlCppCodegen::equalityTestsWithNullOrUndefined()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nonNotifyable.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityTestsWithNullOrUndefined.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o);
+}
- QCOMPARE(qvariant_cast<QDateTime>(object->property("dayz")),
- QDateTime(QDate(2121, 1, 12), QTime()));
- QCOMPARE(qvariant_cast<QDateTime>(object->property("oParty")),
- QDateTime(QDate(2111, 12, 11), QTime()));
+void tst_QmlCppCodegen::equalityVarAndNonStorable()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityVarAndNonStorable.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+
+ QScopedPointer<QObject> object(c1.create());
+ QVERIFY(!object.isNull() && !c1.isError());
+ QVERIFY(!object->property("aIsNull").toBool());
+ QVERIFY(object->property("aIsNotNull").toBool());
+ QVERIFY(object->property("aIsNotUndefined").toBool());
+ QVERIFY(object->property("objectIsNotNull").toBool());
+ QVERIFY(!object->property("typedArrayIsNull").toBool());
+ QVERIFY(object->property("isUndefined").toBool());
+ QVERIFY(!object->property("derivedIsNull").toBool());
+
+ QVERIFY(object->property("primitiveIsNull").toBool());
+ QVERIFY(object->property("primitiveIsDefined").toBool());
+ QVERIFY(object->property("primitiveIsUndefined").toBool());
+
+ QVERIFY(object->property("jsValueIsNull").toBool());
+ QVERIFY(object->property("jsValueIsDefined").toBool());
+ QVERIFY(object->property("jsValueIsUndefined").toBool());
+
+ QVERIFY(object->property("nullVarIsUndefined").toBool());
+ QVERIFY(object->property("nullIsUndefined").toBool());
+ QVERIFY(object->property("nullVarIsNull").toBool());
+ QVERIFY(object->property("nullIsNotUndefined").toBool());
}
-void tst_QmlCppCodegen::importsFromImportPath()
+void tst_QmlCppCodegen::equalityVarAndStorable()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml"_s));
+ QQmlComponent planner(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Planner.qml"_s));
+ QVERIFY2(!planner.isError(), qPrintable(planner.errorString()));
+ QScopedPointer<QObject> p(planner.create());
+ QVERIFY(!p.isNull());
- // We might propagate the import path, eventually, but for now instantiating is not important.
- // If the compiler accepts the file, it's probably fine.
- QVERIFY(component.isError());
- QCOMPARE(component.errorString(),
- u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml:1 module \"Module\" is not installed\n"_s);
+ QQmlComponent variable(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Variable.qml"_s));
+ QVERIFY2(!variable.isError(), qPrintable(variable.errorString()));
+ QScopedPointer<QObject> v(variable.create());
+ QVERIFY(!v.isNull());
+
+ QVERIFY(p->objectName().isEmpty());
+ QMetaObject::invokeMethod(p.data(), "typeErasedRemoveOne", v.data());
+ QCOMPARE(p->objectName(), u"n");
+
+ v->setProperty("value", 1);
+ QMetaObject::invokeMethod(p.data(), "typeErasedRemoveOne", v.data());
+ QCOMPARE(p->objectName(), u"nd");
+
+ QQmlComponent constraint(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BaseConstraint.qml"_s));
+ QVERIFY2(!constraint.isError(), qPrintable(constraint.errorString()));
+ QScopedPointer<QObject> c(constraint.create());
+ QVERIFY(!c.isNull());
+
+ c->setProperty("output", QVariant::fromValue(v.data()));
+ QCOMPARE(v->property("mark").toInt(), 0);
+ QMetaObject::invokeMethod(p.data(), "typeErasedRun", c.data());
+ QCOMPARE(v->property("mark").toInt(), 5);
+
+ QTest::ignoreMessage(QtDebugMsg, "success");
+ QMetaObject::invokeMethod(p.data(), "verify", 10);
+
+ QTest::ignoreMessage(QtCriticalMsg, "failed 10 11");
+ QMetaObject::invokeMethod(p.data(), "verify", 11);
}
-void tst_QmlCppCodegen::aliasLookup()
+void tst_QmlCppCodegen::equalsUndefined()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/aliasLookup.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalsUndefined.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
- const QVariant t = object->property("t");
- QCOMPARE(t.metaType(), QMetaType::fromType<QString>());
- QCOMPARE(t.toString(), u"12"_s);
+ QCOMPARE(object->property("a").toInt(), 50);
+ QCOMPARE(object->property("b").toInt(), 5000);
}
-void tst_QmlCppCodegen::outOfBoundsArray()
+void tst_QmlCppCodegen::evadingAmbiguity()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/outOfBounds.qml"_s));
- QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QTest::ignoreMessage(QtDebugMsg, "oob undefined");
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QVERIFY(object->metaObject()->indexOfProperty("oob") > 0);
- QVERIFY(!object->property("oob").isValid());
- const QVariant oob2 = object->property("oob2");
- QCOMPARE(oob2.metaType(), QMetaType::fromType<QObject *>());
- QCOMPARE(oob2.value<QObject *>(), nullptr);
+ // We need to add an import path here because we cannot namespace the implicit import.
+ // The implicit import is what we use for all the other tests, even if we explicitly
+ // import TestTypes. That is because the TestTypes module is in a subdirectory "data".
+ engine.addImportPath(u":/"_s);
+
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous1/Ambiguous.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+ QScopedPointer<QObject> o1(c1.create());
+ QCOMPARE(o1->objectName(), QStringLiteral("Ambiguous"));
+ QCOMPARE(o1->property("i").toString(), QStringLiteral("Ambiguous1"));
+
+ QQmlComponent c2(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous2/Ambiguous.qml"_s));
+ QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+ QScopedPointer<QObject> o2(c2.create());
+ QCOMPARE(o2->objectName(), QStringLiteral("Ambiguous"));
+ QCOMPARE(o2->property("i").toString(), QStringLiteral("Ambiguous2"));
}
-void tst_QmlCppCodegen::compositeSingleton()
+void tst_QmlCppCodegen::exceptionFromInner()
{
QQmlEngine engine;
- engine.addImportPath(u":/qt/qml/TestTypes/imports/"_s);
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/compositesingleton.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/exceptionFromInner.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QScopedPointer<QObject> o(component.create());
- QCOMPARE(o->property("x").toDouble(), 4.5);
- QCOMPARE(o->property("y").toDouble(), 10.0);
- QCOMPARE(o->property("smooth").toBool(), true);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/exceptionFromInner.qml:7: TypeError: "
+ "Cannot read property 'objectName' of null");
+ QMetaObject::invokeMethod(object.data(), "disbelieveFail");
}
-void tst_QmlCppCodegen::lotsOfRegisters()
+void tst_QmlCppCodegen::excessiveParameters()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/page.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/excessiveParameters.qml"_s));
QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
+ QSignalSpy spy(object.data(), SIGNAL(foo()));
+ QTRY_VERIFY(spy.size() > 0);
+}
- const auto compare = [&]() {
- const qreal implicitBackgroundWidth = object->property("implicitBackgroundWidth").toDouble();
- const qreal leftInset = object->property("leftInset").toDouble();
- const qreal rightInset = object->property("rightInset").toDouble();
- const qreal contentWidth = object->property("contentWidth").toDouble();
- const qreal leftPadding = object->property("leftPadding").toDouble();
- const qreal rightPadding = object->property("rightPadding").toDouble();
- const qreal implicitFooterWidth = object->property("implicitFooterWidth").toDouble();
- const qreal implicitHeaderWidth = object->property("implicitHeaderWidth").toDouble();
+void tst_QmlCppCodegen::extendedTypes()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/extendedTypes.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- const qreal implicitWidth = object->property("implicitWidth").toDouble();
- QCOMPARE(implicitWidth, qMax(qMax(implicitBackgroundWidth + leftInset + rightInset,
- contentWidth + leftPadding + rightPadding),
- qMax(implicitHeaderWidth, implicitFooterWidth)));
- };
+ QTest::ignoreMessage(QtDebugMsg, "6 QSizeF(10, 20) 30");
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- compare();
+ QCOMPARE(object->property("a").toInt(), 6);
+ QCOMPARE(qvariant_cast<QSizeF>(object->property("b")), QSizeF(10, 20));
+ QCOMPARE(object->property("c").toInt(), 30);
+ QCOMPARE(object->property("d").toString(), u"QSizeF(10, 20)"_s);
- const QList<const char *> props = {
- "leftInset", "rightInset", "contentWidth", "leftPadding", "rightPadding"
- };
+ QCOMPARE(object->property("e").toInt(), 2);
+}
- for (int i = 0; i < 100; ++i) {
- QVERIFY(object->setProperty(props[i % props.size()], (i * 17) % 512));
- compare();
- }
+void tst_QmlCppCodegen::failures()
+{
+ const auto &aotFailure
+ = QmlCacheGeneratedCode::_qt_qml_TestTypes_failures_qml::aotBuiltFunctions[0];
+ QVERIFY(!aotFailure.functionPtr);
+ QCOMPARE(aotFailure.functionIndex, 0);
}
-void tst_QmlCppCodegen::inPlaceDecrement()
+void tst_QmlCppCodegen::fallbackLookups()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dialog.qml"_s));
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QObject *header = qvariant_cast<QObject *>(object->property("header"));
- QVERIFY(header);
- QObject *background = qvariant_cast<QObject *>(header->property("background"));
- QObject *parent = qvariant_cast<QObject *>(background->property("parent"));
+ const QUrl document(u"qrc:/qt/qml/TestTypes/fallbacklookups.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
- QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
+ QCOMPARE(o->objectName(), QString());
+ int result = 0;
- QVERIFY(object->setProperty("width", QVariant::fromValue(17)));
- QVERIFY(parent->property("width").toInt() > 0);
- QVERIFY(object->setProperty("height", QVariant::fromValue(53)));
- QVERIFY(parent->property("height").toInt() > 0);
+ QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 16);
+ QCOMPARE(o->objectName(), QStringLiteral("aa93"));
- QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
- QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
+ QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 17);
+ QCOMPARE(o->objectName(), QStringLiteral("bb94"));
- QCOMPARE(object->property("a").toInt(), 1024);
+ QObject *singleton = nullptr;
+ QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton));
+ QVERIFY(singleton);
+
+ QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 18);
+ QCOMPARE(singleton->objectName(), QStringLiteral("cc95"));
+
+ QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 19);
+ QCOMPARE(singleton->objectName(), QStringLiteral("dd96"));
}
-void tst_QmlCppCodegen::shifts()
+void tst_QmlCppCodegen::fileImportsContainCxxTypes()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/shifts.qml"_s));
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
-
- QCOMPARE(object->property("a").toInt(), 9728);
- QCOMPARE(object->property("b").toInt(), 4864);
- QCOMPARE(object->property("c").toInt(), 19448);
- QCOMPARE(object->property("d").toInt(), 9731);
- QCOMPARE(object->property("e").toInt(), 0);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/usingCxxTypesFromFileImports.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"horst guenther"_s);
}
-void tst_QmlCppCodegen::valueTypeProperty()
+void tst_QmlCppCodegen::flagEnum()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeProperty.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/flagEnum.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QFont font = qvariant_cast<QFont>(object->property("font"));
- QCOMPARE(object->property("foo").toString(), font.family());
- font.setFamily(u"Bar"_s);
- object->setProperty("font", QVariant::fromValue(font));
- QCOMPARE(object->property("foo").toString(), u"Bar"_s);
+ QQmlCommunicationPermission *p = qobject_cast<QQmlCommunicationPermission *>(o.data());
+ QCOMPARE(p->communicationModes(), CommunicationPermission::Access);
}
-void tst_QmlCppCodegen::propertyOfParent()
+void tst_QmlCppCodegen::flushBeforeCapture()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/RootWithoutId.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
-
- QObject *child = qmlContext(object.data())->objectForName(u"item"_s);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noBindingLoop.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- bool expected = false;
+ QCOMPARE(o->property("deviation").toDouble(), 9.0 / 3.3333);
+ QCOMPARE(o->property("samples").toInt(), 16);
+ QCOMPARE(o->property("radius").toDouble(), 8.0);
+}
- for (int i = 0; i < 3; ++i) {
- const QVariant foo = object->property("foo");
- QCOMPARE(foo.metaType(), QMetaType::fromType<bool>());
- QCOMPARE(foo.toBool(), expected);
+void tst_QmlCppCodegen::fromBoolValue()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fromBoolValue.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->property("a").toBool(), true);
+ o->setProperty("x", 100);
+ QCOMPARE(o->property("a").toBool(), false);
- const QVariant bar = object->property("bar");
- QCOMPARE(bar.metaType(), QMetaType::fromType<bool>());
- QCOMPARE(bar.toBool(), expected);
+ QCOMPARE(o->property("width").toInt(), 100);
+ QCOMPARE(o->property("b").toBool(), false);
- const QVariant visible = child->property("visible");
- QCOMPARE(visible.metaType(), QMetaType::fromType<bool>());
- QCOMPARE(visible.toBool(), expected);
+ QScopedPointer<QObject> parent(c.create());
+ o->setProperty("parent", QVariant::fromValue(parent.data()));
+ QCOMPARE(o->property("width").toInt(), 100);
+ QCOMPARE(o->property("b").toBool(), false);
- expected = !expected;
- object->setProperty("foo", expected);
- }
+ o->setProperty("state", QVariant::fromValue(u"foo"_s));
+ QCOMPARE(o->property("width").toInt(), 0);
+ QCOMPARE(o->property("b").toBool(), false);
}
-void tst_QmlCppCodegen::accessModelMethodFromOutSide()
+void tst_QmlCppCodegen::funcWithParams()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/AccessModelMethodsFromOutside.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
-
- QTest::ignoreMessage(QtDebugMsg, "3");
- QTest::ignoreMessage(QtDebugMsg, "Apple");
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/funcWithParams.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
-
- QCOMPARE(object->property("cost1").toDouble(), 3);
- QCOMPARE(object->property("name1").toString(), u"Orange"_s);
- QCOMPARE(object->property("cost2").toDouble(), 1.95);
- QCOMPARE(object->property("name2").toString(), u"Banana"_s);
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("bar").toInt(), 30);
}
// QML-generated types have no C++ names, but we want to call a method that
@@ -1150,42 +1998,55 @@ void tst_QmlCppCodegen::accessModelMethodFromOutSide()
// the metatype of the argument we pass does not match the metatype of the
// argument the method expects. In order to work around it, we specialize
// qMetaTypeInterfaceForType() and produce a "correct" metatype this way.
-class Dummy_QMLTYPE_0;
+class Dummy2_QMLTYPE_0;
// We set this to the actual value retrieved from an actual instance of the QML
// type before retrieving the metatype interface for the first time.
static const QtPrivate::QMetaTypeInterface *dummyMetaTypeInterface = nullptr;
template<>
-const QtPrivate::QMetaTypeInterface *QtPrivate::qMetaTypeInterfaceForType<Dummy_QMLTYPE_0 *>() {
+const QtPrivate::QMetaTypeInterface *QtPrivate::qMetaTypeInterfaceForType<Dummy2_QMLTYPE_0 *>() {
return dummyMetaTypeInterface;
}
void tst_QmlCppCodegen::functionArguments()
{
+ qmlClearTypeRegistrations();
+
QQmlEngine engine;
+ QQmlComponent preheat(&engine);
+ preheat.setData(R"(
+ import QtQml
+ import TestTypes
+ QtObject {
+ objectName: Style.objectName
+ }
+ )", QUrl(u"qrc:/qt/qml/TestTypes/preheat.qml"_s));
+ QVERIFY2(preheat.isReady(), qPrintable(preheat.errorString()));
+ QScopedPointer<QObject> hot(preheat.create());
+ QVERIFY(!hot.isNull());
- // Ensure that Dummy gets counter value 0. Don't do that at home
+ // Ensure that Dummy gets counter value 1. Don't do that at home
QScopedValueRollback<QAtomicInt> rb(QQmlPropertyCacheCreatorBase::classIndexCounter, 0);
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Dummy.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Dummy2.qml"_s));
QVERIFY2(component.isReady(), component.errorString().toUtf8());
QScopedPointer<QObject> object(component.create());
const QMetaObject *metaObject = object->metaObject();
dummyMetaTypeInterface = metaObject->metaType().iface();
const QByteArray className = QByteArray(metaObject->className());
- QCOMPARE(className, "Dummy_QMLTYPE_0");
+ QCOMPARE(className, "Dummy2_QMLTYPE_0");
int result;
int a = 1;
bool b = false;
- Dummy_QMLTYPE_0 *c = nullptr;
+ Dummy2_QMLTYPE_0 *c = nullptr;
double d = -1.2;
int e = 3;
metaObject->invokeMethod(
object.data(), "someFunction", Q_RETURN_ARG(int, result),
- Q_ARG(int, a), Q_ARG(bool, b), Q_ARG(Dummy_QMLTYPE_0 *, c),
+ Q_ARG(int, a), Q_ARG(bool, b), Q_ARG(Dummy2_QMLTYPE_0 *, c),
Q_ARG(double, d), Q_ARG(int, e));
QCOMPARE(result, 42);
@@ -1198,473 +2059,780 @@ void tst_QmlCppCodegen::functionArguments()
QCOMPARE(concatenated, u"foobar"_s);
}
-void tst_QmlCppCodegen::bindingExpression()
+void tst_QmlCppCodegen::functionCallOnNamespaced()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BindingExpression.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
-
- QObject *child = qmlContext(object.data())->objectForName(u"child"_s);
-
- double width = 200;
- double y = 10;
- for (int i = 0; i < 10; ++i) {
- QCOMPARE(object->property("width").toDouble(), width);
- QCOMPARE(object->property("height").toDouble(), width);
- QCOMPARE(object->property("y").toDouble(), y);
-
- const double childY = y + (width - 100) / 2;
- QCOMPARE(child->property("y").toDouble(), childY);
- QCOMPARE(object->property("mass"), childY > 100 ? u"heavy"_s : u"light"_s);
- QCOMPARE(object->property("test_division").toDouble(), width / 1000 + 50);
- QCOMPARE(object->property("test_ternary").toDouble(), 2.2);
-
- const int test_switch = object->property("test_switch").toInt();
- switch (int(width) % 3) {
- case 0:
- QCOMPARE(test_switch, 130);
- break;
- case 1:
- QCOMPARE(test_switch, 380);
- break;
- case 2:
- QCOMPARE(test_switch, 630);
- break;
- }
+ {
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themergood.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("i").toInt(), 12);
+ }
- width = 200 * i;
- y = 10 + i;
- object->setProperty("width", width);
- object->setProperty("y", y);
+ {
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themerbad.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(5.0, 10.0, 1.0, 1.0)));
}
}
-void tst_QmlCppCodegen::voidFunction()
+void tst_QmlCppCodegen::functionLookup()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/voidfunction.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QVERIFY(object->objectName().isEmpty());
- object->metaObject()->invokeMethod(object.data(), "doesNotReturnValue");
- QCOMPARE(object->objectName(), u"barbar"_s);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionLookup.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ const QVariant foo = o->property("bar");
+ QCOMPARE(foo.metaType(), QMetaType::fromType<QJSValue>());
+ const QJSManagedValue method(engine.toScriptValue(foo), &engine);
+ QVERIFY(method.isFunction());
+ const QJSValue result = method.call();
+ QVERIFY(result.isString());
+ QCOMPARE(result.toString(), QStringLiteral("a99"));
}
-void tst_QmlCppCodegen::overriddenProperty()
+void tst_QmlCppCodegen::functionReturningVoid()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/childobject.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->objectName(), u"kraut"_s);
- QCOMPARE(object->property("doneThing").toInt(), 5);
- QCOMPARE(object->property("usingFinal").toInt(), 5);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionReturningVoid.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- auto checkAssignment = [&]() {
- const QString newName = u"worscht"_s;
- QMetaObject::invokeMethod(object.data(), "setChildObjectName", Q_ARG(QString, newName));
- QCOMPARE(object->objectName(), newName);
- };
- checkAssignment();
+ // It should be able to call the methods and wrap the void values into invalid QVariants,
+ // without crashing.
+ QVERIFY(o->metaObject()->indexOfProperty("aa") >= 0);
+ QVERIFY(o->metaObject()->indexOfProperty("bb") >= 0);
+ QVERIFY(!o->property("aa").isValid());
+ QVERIFY(!o->property("bb").isValid());
+}
- ObjectWithMethod *benign = new ObjectWithMethod(object.data());
- benign->theThing = 10;
- benign->setObjectName(u"cabbage"_s);
- object->setProperty("child", QVariant::fromValue(benign));
- QCOMPARE(object->objectName(), u"cabbage"_s);
- checkAssignment();
- QCOMPARE(object->property("doneThing").toInt(), 10);
- QCOMPARE(object->property("usingFinal").toInt(), 10);
+void tst_QmlCppCodegen::functionTakingVar()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/functionTakingVar.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- OverriddenObjectName *evil = new OverriddenObjectName(object.data());
- QTest::ignoreMessage(QtWarningMsg,
- "Final member fff is overridden in class OverriddenObjectName. "
- "The override won't be used.");
- object->setProperty("child", QVariant::fromValue(evil));
+ QVERIFY(!o->property("c").isValid());
- QCOMPARE(object->objectName(), u"borschtsch"_s);
+ 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);
- checkAssignment();
- QCOMPARE(object->property("doneThing").toInt(), 7);
- QCOMPARE(object->property("usingFinal").toInt(), 5);
+ QCOMPARE(o->property("c"), QVariant::fromValue<int>(11));
}
-void tst_QmlCppCodegen::listLength()
+void tst_QmlCppCodegen::getLookupOfScript()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listlength.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("l").toInt(), 2);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/NotificationItem.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"heading"_s);
}
-void tst_QmlCppCodegen::parentProperty()
+void tst_QmlCppCodegen::getOptionalLookup_data()
{
- QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/parentProp.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> object(component.create());
- QVERIFY(!object.isNull());
- QCOMPARE(object->property("c").toInt(), 11);
- QCOMPARE(object->property("i").toInt(), 22);
- object->setProperty("a", QVariant::fromValue(22));
- QCOMPARE(object->property("c").toInt(), 28);
- object->setProperty("implicitWidth", QVariant::fromValue(14));
- QCOMPARE(object->property("i").toInt(), 26);
+ QTest::addColumn<QString>("propertyName");
+ QTest::addColumn<QVariant>("expected");
- QObject *child = qmlContext(object.data())->objectForName(u"child"_s);
- QObject *sibling = qmlContext(object.data())->objectForName(u"sibling"_s);
- QObject *evil = qmlContext(object.data())->objectForName(u"evil"_s);
+ // Objects
+ QTest::addRow("int on object") << u"to1"_s << QVariant(5);
+ QTest::addRow("string on object") << u"to2"_s << QVariant("6");
+ QTest::addRow("object on object") << u"to3"_s << QVariant::fromValue<QObject *>(nullptr);
+ QTest::addRow("int on null") << u"to4"_s << QVariant(); // undefined
+ QTest::addRow("any on undefined as object") << u"to5"_s << QVariant(); // undefined
+ QTest::addRow("int on string on object") << u"to6"_s << QVariant(1);
- child->setProperty("parent", QVariant::fromValue(sibling));
+ // Value Types
+ QTest::addRow("int on rect") << u"tv1"_s << QVariant(50);
+ QTest::addRow("int on point") << u"tv2"_s << QVariant(-10);
+ QTest::addRow("int on undefined as point") << u"tv4"_s << QVariant(); // undefined
- QCOMPARE(child->property("b").toInt(), 0);
- QCOMPARE(child->property("i").toInt(), 28);
- QCOMPARE(object->property("i").toInt(), 56);
+ // Enums
+ QTest::addRow("enum on object") << u"te1"_s << QVariant(1);
+ QTest::addRow("enum on type") << u"te2"_s << QVariant(1);
+ QTest::addRow("enums comparison 1") << u"te3"_s << QVariant(false);
+ QTest::addRow("enums comparison 2") << u"te4"_s << QVariant(true);
- child->setProperty("parent", QVariant::fromValue(evil));
+ // Complex chains
+ QTest::addRow("mixed 1") << u"tc1"_s << QVariant(-10);
+ QTest::addRow("mixed 2") << u"tc2"_s << QVariant(0);
+ QTest::addRow("early out 1") << u"tc4"_s << QVariant(); // undefined
+ QTest::addRow("early out 2") << u"tc5"_s << QVariant(); // undefined
+}
- QCOMPARE(child->property("b").toInt(), 5994);
- QCOMPARE(object->property("c").toInt(), 5996);
+void tst_QmlCppCodegen::getOptionalLookup()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/getOptionalLookup.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(child->property("i").toInt(), 443);
- QCOMPARE(object->property("i").toInt(), 886);
+ QFETCH(QString, propertyName);
+ QFETCH(QVariant, expected);
- {
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/specificParent.qml"_s));
+ QVariant actual = o->property(propertyName.toLocal8Bit());
+ QCOMPARE(actual, expected);
+}
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+void tst_QmlCppCodegen::getOptionalLookupOnQJSValueNonStrict()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/GetOptionalLookupOnQJSValueNonStrict.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(rootObject->property("a").toReal(), 77.0);
- }
+ QVERIFY(o->property("b").toBool());
}
-void tst_QmlCppCodegen::registerElimination()
+void tst_QmlCppCodegen::getOptionalLookupShadowed()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/registerelimination.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ const QUrl document(u"qrc:/qt/qml/TestTypes/GetOptionalLookupShadowed.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QCOMPARE(o->property("res").toString(), "a");
+}
+
+void tst_QmlCppCodegen::globals()
+{
+ QQmlEngine engine;
+ int exitCode = -1;
+ QObject::connect(&engine, &QQmlEngine::exit, [&](int code) { exitCode = code; });
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/globals.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+
+ const QByteArray message = QByteArray("Start 2 ") + arg1();
+ QTest::ignoreMessage(QtDebugMsg, message.constData());
+
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
+ QTRY_COMPARE(exitCode, 0);
- // Increment of 23 hits both 0 and 460
- for (int input = -23; input < 700; input += 23) {
- object->setProperty("input", input);
- if (input <= 0 || input >= 460)
- QCOMPARE(object->property("output").toInt(), 459);
- else
- QCOMPARE(object->property("output").toInt(), input);
- }
+ QObject *application = qvariant_cast<QObject *>(object->property("application"));
+ QVERIFY(application);
+ QCOMPARE(QString::fromUtf8(application->metaObject()->className()),
+ u"QQuickApplication"_s);
+
+ QTest::ignoreMessage(QtDebugMsg, "End");
+ QMetaObject::invokeMethod(application, "aboutToQuit");
+
+ const QVariant somewhere = object->property("somewhere");
+ QCOMPARE(somewhere.userType(), QMetaType::QUrl);
+ QCOMPARE(qvariant_cast<QUrl>(somewhere).toString(), u"qrc:/somewhere/else.qml"_s);
+
+ const QVariant somewhereString = object->property("somewhereString");
+ QCOMPARE(somewhereString.userType(), QMetaType::QString);
+ QCOMPARE(somewhereString.toString(), u"qrc:/somewhere/else.qml"_s);
+
+ const QVariant plain = object->property("plain");
+ QCOMPARE(plain.userType(), QMetaType::QUrl);
+ QCOMPARE(qvariant_cast<QUrl>(plain).toString(), u"/not/here.qml"_s);
}
-void tst_QmlCppCodegen::asCast()
+void tst_QmlCppCodegen::idAccess()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/asCast.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QScopedPointer<QObject> root(component.create());
- QVERIFY(!root.isNull());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/idAccess.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QQmlContext *context = qmlContext(root.data());
- const QObject *object = context->objectForName(u"object"_s);
- const QObject *item = context->objectForName(u"item"_s);
- const QObject *rectangle = context->objectForName(u"rectangle"_s);
- const QObject *dummy = context->objectForName(u"dummy"_s);
+ QVERIFY(object->property("y").toInt() != 48);
+ QCOMPARE(object->property("y").toInt(), 12);
+ object->setProperty("z", 13);
+ QCOMPARE(object->property("y").toInt(), 13);
+ object->setProperty("x", QVariant::fromValue(333));
+ QCOMPARE(object->property("y").toInt(), 48);
- QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsObject")), object);
- QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsItem")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsRectangle")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(root->property("objectAsDummy")), nullptr);
+ // The binding was broken by setting the property
+ object->setProperty("z", 14);
+ QCOMPARE(object->property("y").toInt(), 48);
- QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsObject")), item);
- QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsItem")), item);
- QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsRectangle")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(root->property("itemAsDummy")), nullptr);
+ QObject *ttt = qmlContext(object.data())->objectForName(u"ttt"_s);
+ QFont f = qvariant_cast<QFont>(ttt->property("font"));
+ QCOMPARE(f.pointSize(), 22);
- QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsObject")), rectangle);
- QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsItem")), rectangle);
- QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsRectangle")), rectangle);
- QCOMPARE(qvariant_cast<QObject *>(root->property("rectangleAsDummy")), nullptr);
+ QObject::connect(object.data(), &QObject::objectNameChanged, ttt, [&](){
+ ttt->setParent(nullptr);
+ QJSEngine::setObjectOwnership(ttt, QJSEngine::CppOwnership);
+ object.reset(ttt);
+ });
- QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsObject")), dummy);
- QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsItem")), dummy);
- QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsRectangle")), nullptr);
- QCOMPARE(qvariant_cast<QObject *>(root->property("dummyAsDummy")), dummy);
+ QVERIFY(object->objectName().isEmpty());
+ QVERIFY(ttt->objectName().isEmpty());
+ ttt->setProperty("text", u"kill"_s);
+ QCOMPARE(object.data(), ttt);
+ QCOMPARE(ttt->objectName(), u"context"_s);
}
-void tst_QmlCppCodegen::noQQmlData()
+void tst_QmlCppCodegen::ignoredFunctionReturn()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noQQmlData.qml"_s));
- QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ignoredFunctionReturn.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+}
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: "
- "Cannot read property 'name' of null");
- QScopedPointer<QObject> root(component.create());
- QVERIFY(!root.isNull());
+void tst_QmlCppCodegen::importsFromImportPath()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml"_s));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(root.data());
- QVERIFY(party != nullptr);
+ // We might propagate the import path, eventually, but for now instantiating is not important.
+ // If the compiler accepts the file, it's probably fine.
+ QVERIFY(component.isError());
+ QCOMPARE(component.errorString(),
+ u"qrc:/qt/qml/TestTypes/importsFromImportPath.qml:1 module \"Module\" is not installed\n"_s);
+}
- QCOMPARE(party->host(), nullptr);
- QCOMPARE(party->property("n").toString(), QString());
+void tst_QmlCppCodegen::inPlaceDecrement()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dialog.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QObject *header = qvariant_cast<QObject *>(object->property("header"));
+ QVERIFY(header);
+ QObject *background = qvariant_cast<QObject *>(header->property("background"));
+ QObject *parent = qvariant_cast<QObject *>(background->property("parent"));
- Person *host1 = new Person(party);
- party->setHost(host1);
- QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s);
- host1->setName(u"Marge"_s);
- QCOMPARE(party->property("n").toString(), u"Marge in da house!"_s);
+ QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
+ QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: "
- "Cannot read property 'name' of null");
+ QVERIFY(object->setProperty("width", QVariant::fromValue(17)));
+ QVERIFY(parent->property("width").toInt() > 0);
+ QVERIFY(object->setProperty("height", QVariant::fromValue(53)));
+ QVERIFY(parent->property("height").toInt() > 0);
- // Doesn't crash
- party->setHost(nullptr);
+ QCOMPARE(background->property("width").toInt(), parent->property("width").toInt() + 1);
+ QCOMPARE(background->property("height").toInt(), parent->property("height").toInt() - 1);
- // Lookups are initialized now, and we introduce an object without QQmlData
- Person *host2 = new Person(party);
- party->setHost(host2);
- QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s);
- host2->setName(u"Homer"_s);
- QCOMPARE(party->property("n").toString(), u"Homer in da house!"_s);
+ QCOMPARE(object->property("a").toInt(), 1024);
+}
- QMetaObject::invokeMethod(party, "burn");
- engine.collectGarbage();
+void tst_QmlCppCodegen::inaccessibleProperty()
+{
+ QQmlEngine engine;
- // Does not crash
- party->setProperty("inDaHouse", u" burns!"_s);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/versionmismatch.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- // Mr Burns may or may not burn, depending on whether we use lookups.
- // If using lookups, the binding is aborted when we find the isQueuedForDeletion flag.
- // If reading the property directly, we don't have to care about it.
- QVERIFY(party->property("n").toString().startsWith(u"Mr Burns"_s));
+ QCOMPARE(o->property("c").toInt(), 5);
}
-void tst_QmlCppCodegen::scopeObjectDestruction()
+void tst_QmlCppCodegen::indirectlyShadowable()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fileDialog.qml"_s));
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+ const QString url = u"qrc:/qt/qml/TestTypes/indirectlyShadowable.qml"_s;
+ QQmlComponent c(&engine, QUrl(url));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QObject *dialog = rootObject->property("dialog").value<QObject *>();
- QVERIFY(dialog);
+ const auto verifyShadowable = [&](const QString &objectName) {
+ QObject *outer = o->property("outer").value<QObject *>();
+ QVERIFY(outer);
+ QObject *inner = outer->property("inner").value<QObject *>();
+ QVERIFY(inner);
+ QObject *shadowable = inner->property("shadowable").value<QObject *>();
+ QVERIFY(shadowable);
+ QCOMPARE(shadowable->objectName(), objectName);
+ };
- // We cannot check the warning messages. The AOT compiled code complains about reading the
- // "parent" property of an object scheduled for deletion. The runtime silently returns undefined
- // at that point and then complains about not being able to read a property on undefined.
+ const auto verifyNotShadowable = [&](const QString &objectName) {
+ QObject *notShadowable = o->property("notShadowable").value<QObject *>();
+ QCOMPARE(notShadowable->objectName(), objectName);
+ };
- // Doesn't crash, even though it triggers bindings on scope objects scheduled for deletion.
- QMetaObject::invokeMethod(dialog, "open");
-}
+ const auto verifyEvil = [&]() {
+ QObject *outer = o->property("outer").value<QObject *>();
+ QVERIFY(outer);
+ QCOMPARE(outer->property("inner").toString(), u"evil"_s);
+ };
-static void checkColorProperties(QQmlComponent *component)
-{
- QVERIFY2(component->isReady(), qPrintable(component->errorString()));
- QScopedPointer<QObject> rootObject(component->create());
- QVERIFY(rootObject);
+ verifyShadowable(u"shadowable"_s);
+ verifyNotShadowable(u"notShadowable"_s);
- const QMetaObject *mo = QMetaType::fromName("QQuickIcon").metaObject();
- QVERIFY(mo != nullptr);
+ QMetaObject::invokeMethod(o.data(), "setInnerShadowable");
- const QMetaProperty prop = mo->property(mo->indexOfProperty("color"));
- QVERIFY(prop.isValid());
+ verifyShadowable(u"self"_s);
+ verifyNotShadowable(u"notShadowable"_s);
- const QVariant a = rootObject->property("a");
- QVERIFY(a.isValid());
+ QMetaObject::invokeMethod(o.data(), "getInnerShadowable");
- const QVariant iconColor = prop.readOnGadget(rootObject->property("icon").data());
- QVERIFY(iconColor.isValid());
+ verifyShadowable(u"self"_s);
+ verifyNotShadowable(u"self"_s);
- const QMetaType colorType = QMetaType::fromName("QColor");
- QVERIFY(colorType.isValid());
+ QMetaObject::invokeMethod(o.data(), "turnEvil");
- QCOMPARE(a.metaType(), colorType);
- QCOMPARE(iconColor.metaType(), colorType);
+ verifyEvil();
+ verifyNotShadowable(u"self"_s);
- QCOMPARE(iconColor, a);
+ // Does not produce an error message because JavaScript.
+ QMetaObject::invokeMethod(o.data(), "setInnerShadowable");
+
+ verifyEvil();
+ verifyNotShadowable(u"self"_s);
+
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + u":29: Error: Cannot assign [undefined] to QObject*"_s));
+ QMetaObject::invokeMethod(o.data(), "getInnerShadowable");
+
+ verifyEvil();
+ verifyNotShadowable(u"self"_s);
}
-void tst_QmlCppCodegen::colorAsVariant()
+void tst_QmlCppCodegen::infinities()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorAsVariant.qml"_s));
- checkColorProperties(&component);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinities.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QCOMPARE(o->property("positiveInfinity").toDouble(), std::numeric_limits<double>::infinity());
+ QCOMPARE(o->property("negativeInfinity").toDouble(), -std::numeric_limits<double>::infinity());
+
+ const double positiveZero = o->property("positiveZero").toDouble();
+ QCOMPARE(positiveZero, 0.0);
+ QVERIFY(!std::signbit(positiveZero));
+
+ const double negativeZero = o->property("negativeZero").toDouble();
+ QCOMPARE(negativeZero, -0.0);
+ QVERIFY(std::signbit(negativeZero));
+
+ QVERIFY(qIsNaN(o->property("naN").toDouble()));
}
-void tst_QmlCppCodegen::bindToValueType()
+void tst_QmlCppCodegen::infinitiesToInt()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/bindToValueType.qml"_s));
- checkColorProperties(&component);
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinitiesToInt.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ const char *props[] = {"a", "b", "c"};
+ for (const char *prop : props) {
+ const QVariant i = o->property(prop);
+ QCOMPARE(i.metaType(), QMetaType::fromType<int>());
+ bool ok = false;
+ QCOMPARE(i.toInt(&ok), 0);
+ QVERIFY(ok);
+ }
}
-void tst_QmlCppCodegen::undefinedResets()
+void tst_QmlCppCodegen::innerObjectNonShadowable()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedResets.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ownProperty.qml"_s));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> rootObject(component.create());
QVERIFY(rootObject);
- Person *person = qobject_cast<Person *>(rootObject.data());
- QVERIFY(person);
- QCOMPARE(person->shoeSize(), 0);
- QCOMPARE(person->name(), u"Marge"_s);
-
- person->setShoeSize(11);
+ QCOMPARE(rootObject->objectName(), u"foo"_s);
+}
- QCOMPARE(person->shoeSize(), 11);
- QCOMPARE(person->name(), u"Bart"_s);
+void tst_QmlCppCodegen::intEnumCompare()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intEnumCompare.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("a").toBool(), true);
+ QCOMPARE(o->property("b").toBool(), false);
+ QCOMPARE(o->property("c").toBool(), true);
+ QCOMPARE(o->property("d").toBool(), false);
+ }
- person->setShoeSize(10);
- QCOMPARE(person->shoeSize(), 10);
- QCOMPARE(person->name(), u"Marge"_s);
+ {
+ // We cannot use Qt.red in QML because it's lower case.
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumInvalid.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("c").toBool(), true);
+ QCOMPARE(o->property("d").toBool(), false);
+ }
+}
- person->setName(u"no one"_s);
- QCOMPARE(person->name(), u"no one"_s);
+void tst_QmlCppCodegen::intOverflow()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intOverflow.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("a").toDouble(), 1.09951162778e+12);
+ QCOMPARE(object->property("b").toInt(), 5);
+}
- person->setObjectName(u"the one"_s);
- QCOMPARE(person->name(), u"Bart"_s);
+void tst_QmlCppCodegen::intToEnum()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intToEnum.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ MyType *m = qobject_cast<MyType *>(o.data());
+ QCOMPARE(m->a(), MyType::D);
+ QCOMPARE(m->property("b").toInt(), 24);
}
-void tst_QmlCppCodegen::innerObjectNonShadowable()
+void tst_QmlCppCodegen::interceptor()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ownProperty.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/interceptor.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+ QCOMPARE(object->property("x").toInt(), 100);
+ QCOMPARE(object->property("y").toInt(), 100);
- QCOMPARE(rootObject->objectName(), u"foo"_s);
+ QVERIFY(object->property("width").toInt() != 200);
+ QVERIFY(object->property("height").toInt() != 200);
+ QVERIFY(object->property("qProperty1").toInt() != 300);
+ QVERIFY(object->property("qProperty2").toInt() != 300);
+ QTRY_COMPARE(object->property("width").toInt(), 200);
+ QTRY_COMPARE(object->property("height").toInt(), 200);
+ QTRY_COMPARE(object->property("qProperty1").toInt(), 300);
+ QTRY_COMPARE(object->property("qProperty2").toInt(), 300);
}
-void tst_QmlCppCodegen::ownPropertiesNonShadowable()
+void tst_QmlCppCodegen::interestingFiles()
{
+ QFETCH(QString, file);
+ QFETCH(bool, isValid);
+
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/overriddenMember.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/%1"_s.arg(file)));
+ if (isValid) {
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ } else {
+ QVERIFY(component.isError());
+ }
+}
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+void tst_QmlCppCodegen::interestingFiles_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<bool>("isValid");
- QCOMPARE(rootObject->property("ppp").toInt(), 16);
- QCOMPARE(rootObject->property("ppp2").toInt(), 9);
- QCOMPARE(rootObject->property("ppp3").toInt(), 12);
+ QTest::addRow("conversions2") << u"conversions2.qml"_s << true;
+ QTest::addRow("TestCase") << u"TestCase.qml"_s << true;
+ QTest::addRow("layouts") << u"layouts.qml"_s << true;
+ QTest::addRow("interactive") << u"interactive.qml"_s << true;
+ QTest::addRow("Panel") << u"Panel.qml"_s << true;
+ QTest::addRow("ProgressBar") << u"ProgressBar/ProgressBar.ui.qml"_s << true;
+ QTest::addRow("Root") << u"ProgressBar/Root.qml"_s << true;
+ QTest::addRow("noscope") << u"noscope.qml"_s << true;
+ QTest::addRow("dynamicscene") << u"dynamicscene.qml"_s << true;
+ QTest::addRow("curlygrouped") << u"curlygrouped.qml"_s << true;
+ QTest::addRow("cycleHead") << u"cycleHead.qml"_s << false;
+ QTest::addRow("deadStoreLoop") << u"deadStoreLoop.qml"_s << true;
+ QTest::addRow("moveRegVoid") << u"moveRegVoid.qml"_s << true;
}
-void tst_QmlCppCodegen::modulePrefix()
+void tst_QmlCppCodegen::internalConversion()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/modulePrefix.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/internalConversion.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+ QObject *offset = obj->property("offset").value<QObject *>();
+ QVERIFY(offset);
- QCOMPARE(rootObject->property("foo").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
- QCOMPARE(rootObject->property("bar").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
- QCOMPARE(rootObject->property("baz").toString(), QStringLiteral("ItIsTheSingleton"));
+ QCOMPARE(offset->objectName(), "hello"_L1);
+ QCOMPARE(offset->property("mark").toString(), "hello"_L1);
}
-void tst_QmlCppCodegen::colorString()
+void tst_QmlCppCodegen::invalidPropertyType()
{
+ // Invisible on purpose
+ qmlRegisterType<MyCppType>("App", 1, 0, "MyCppType");
+
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/colorString.qml"_s));
+ QQmlComponent okComponent(&engine, QUrl(u"qrc:/qt/qml/TestTypes/OkType.qml"_s));
+ QVERIFY2(okComponent.isReady(), qPrintable(okComponent.errorString()));
+ QScopedPointer<QObject> picker(okComponent.create());
+ QVERIFY2(!picker.isNull(), qPrintable(okComponent.errorString()));
+ QObject *inner = qmlContext(picker.data())->objectForName(u"inner"_s);
+ QVERIFY(inner);
+ MyCppType *myCppType = qobject_cast<MyCppType *>(inner);
+ QVERIFY(myCppType);
+ QVERIFY(!myCppType->useListDelegate());
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BadType.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.createWithInitialProperties(
+ QVariantMap {{u"picker"_s, QVariant::fromValue(picker.data())}}));
+ QVERIFY2(!o.isNull(), qPrintable(c.errorString()));
+ QVERIFY(!myCppType->useListDelegate());
- QCOMPARE(qvariant_cast<QColor>(rootObject->property("c")), QColor::fromRgb(0xdd, 0xdd, 0xdd));
- QCOMPARE(qvariant_cast<QColor>(rootObject->property("d")), QColor::fromRgb(0xaa, 0xaa, 0xaa));
- QCOMPARE(qvariant_cast<QColor>(rootObject->property("e")), QColor::fromRgb(0x11, 0x22, 0x33));
+ o->setProperty("useListDelegate", QVariant::fromValue<bool>(true));
+ QVERIFY(myCppType->useListDelegate());
}
-void tst_QmlCppCodegen::urlString()
+void tst_QmlCppCodegen::invisibleBase()
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/urlString.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleBase.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("n")), o.data());
+}
- QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QScopedPointer<QObject> rootObject(component.create());
- QVERIFY(rootObject);
+void tst_QmlCppCodegen::invisibleListElementType()
+{
+ qmlRegisterType<InvisibleListElementType>("Invisible", 1, 0, "InvisibleListElement");
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleListElementType.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QCOMPARE(qvariant_cast<QUrl>(rootObject->property("c")), QUrl(u"http://dddddd.com"_s));
- QCOMPARE(qvariant_cast<QUrl>(rootObject->property("d")), QUrl(u"http://aaaaaa.com"_s));
- QCOMPARE(qvariant_cast<QUrl>(rootObject->property("e")), QUrl(u"http://a112233.de"_s));
+ QObject *a = o->property("a").value<QObject *>();
+ QVERIFY(a);
+
+ const QVariant x = a->property("x");
+ QCOMPARE(x.metaType(), QMetaType::fromType<QQmlListReference>());
+ const QQmlListReference ref = x.value<QQmlListReference>();
+ QVERIFY(ref.isValid());
+ QCOMPARE(ref.size(), 0);
}
+void tst_QmlCppCodegen::invisibleSingleton()
+{
+ // We may have seen Style.qml as singleton before, which would make the assignment pass.
+ // So let's flush the type registry.
+ qmlClearTypeRegistrations();
-void tst_QmlCppCodegen::callContextPropertyLookupResult()
+ QQmlEngine engine;
+ const QUrl copy(u"qrc:/qt/qml/HiddenTestTypes/hidden/Main.qml"_s);
+ QQmlComponent c(&engine, copy);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "qrc:/qt/qml/HiddenTestTypes/hidden/Main.qml:4:5: "
+ "Unable to assign [undefined] to QColor");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("c"), QVariant(QMetaType::fromName("QColor")));
+}
+
+void tst_QmlCppCodegen::invisibleTypes()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callContextPropertyLookupResult.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleTypes.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QVERIFY(qvariant_cast<QQmlComponent *>(o->property("c")) != nullptr);
+ QObject *singleton = qvariant_cast<QObject *>(o->property("singleton"));
+ QVERIFY(singleton != nullptr);
+ QCOMPARE(singleton->metaObject()->className(), "SingletonModel");
+
+ QObject *attached = qvariant_cast<QObject *>(o->property("attached"));
+ QVERIFY(attached != nullptr);
+ QCOMPARE(attached->metaObject()->className(), "AttachedAttached");
+
+// TODO: This doesn't work in interpreted mode:
+// const QMetaObject *meta = qvariant_cast<const QMetaObject *>(o->property("metaobject"));
+// QVERIFY(meta != nullptr);
+// QCOMPARE(meta->className(), "DerivedFromInvisible");
}
-void tst_QmlCppCodegen::deadShoeSize()
+void tst_QmlCppCodegen::iteration()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/deadShoeSize.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/iteration.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/deadShoeSize.qml:5: Error: ouch");
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("shoeSize").toInt(), 0);
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->objectName(), "a345b345c345"_L1);
}
-void tst_QmlCppCodegen::listIndices()
+void tst_QmlCppCodegen::javaScriptArgument()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listIndices.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/javaScriptArgument.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QVERIFY(!o.isNull());
- QQmlListReference list(o.data(), "items");
- QCOMPARE(list.count(), 3);
- 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);
+ QCOMPARE(o->property("a").toDouble(), 4.0);
+ QCOMPARE(o->property("b").toDouble(), 9.0);
+ QCOMPARE(o->property("c").toString(), u"5t-1"_s);
+ QCOMPARE(o->property("d").toString(), u"9"_s);
+ QCOMPARE(o->property("e").toString(), u"10"_s);
+ QCOMPARE(o->property("f").toString(), u"-10"_s);
+
+ const QStringList scales {
+ "0 ", "1 ", "10 ", "100 ", "1000 ", "9.77k", "97.7k", "977k", "9.54M", "95.4M", "954M",
+ "9.31G", "93.1G", "931G", "9.09T", "-1 ", "-10 ", "-100 ", "-1000 ", "-9.77k", "-97.7k",
+ "-977k", "-9.54M", "-95.4M", "-954M", "-9.31G", "-93.1G", "-931G", "-9.09T"
+ };
+
+ QCOMPARE(o->property("scales").value<QStringList>(), scales);
+
+ double thing = 12.0;
+ QString result;
+ QMetaObject::invokeMethod(
+ o.data(), "forwardArg", Q_RETURN_ARG(QString, result), Q_ARG(double, thing));
+ QCOMPARE(result, u"12 ");
}
-static const double numbers[] = {
- qQNaN(), -qInf(),
- std::numeric_limits<double>::min(),
- std::numeric_limits<float>::min(),
- std::numeric_limits<qint32>::min(),
- -1000.2, -100, -2, -1.333, -1, -0.84, -0.5,
+void tst_QmlCppCodegen::jsArrayMethods()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethods.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- // -0 and 0 are not different on the QML side. Therefore, don't keep them adjacent.
- // Otherwise the bindings won't get re-evaluated.
- std::copysign(0.0, -1), 1, 0.0,
+ QQmlComponent untyped(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsUntyped.qml"_s));
+ QVERIFY2(untyped.isReady(), qPrintable(untyped.errorString()));
+ QScopedPointer<QObject> check(untyped.create());
+ QVERIFY(!check.isNull());
- 0.5, 0.77, 1.4545, 2, 199, 2002.13,
- std::numeric_limits<qint32>::max(),
- std::numeric_limits<quint32>::max(),
- std::numeric_limits<float>::max(),
- std::numeric_limits<double>::max(),
- qInf()
-};
+ check->setProperty("l1", object->property("l1"));
+ check->setProperty("l2", object->property("l2"));
+ check->setProperty("l3", object->property("l3"));
+
+ QCOMPARE(object->property("listPropertyToString"), object->property("jsArrayToString"));
+ QCOMPARE(object->property("listPropertyToString"), check->property("jsArrayToString"));
+
+ QCOMPARE(object->property("listPropertyIncludes"), object->property("jsArrayIncludes"));
+ QVERIFY(object->property("listPropertyIncludes").toBool());
+
+ QCOMPARE(object->property("listPropertyJoin"), object->property("jsArrayJoin"));
+ QCOMPARE(object->property("listPropertyJoin"), check->property("jsArrayJoin"));
+ QVERIFY(object->property("listPropertyJoin").toString().contains(QStringLiteral("klaus")));
+
+ QCOMPARE(object->property("listPropertyIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("listPropertyIndexOf").toInt(), 1);
+
+ QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf"));
+ QCOMPARE(object->property("listPropertyLastIndexOf").toInt(), 5);
+}
+
+void tst_QmlCppCodegen::jsArrayMethodsWithParams()
+{
+ QFETCH(int, i);
+ QFETCH(int, j);
+ QFETCH(int, k);
+ QQmlEngine engine;
+ QQmlComponent component
+ (&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsWithParams.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QQmlComponent untyped(
+ &engine, QUrl(u"qrc:/qt/qml/TestTypes/jsArrayMethodsWithParamsUntyped.qml"_s));
+ QVERIFY2(untyped.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.createWithInitialProperties({
+ {QStringLiteral("i"), i},
+ {QStringLiteral("j"), j},
+ {QStringLiteral("k"), k}
+ }));
+ QVERIFY(!object.isNull());
+ QScopedPointer<QObject> check(untyped.createWithInitialProperties({
+ {QStringLiteral("i"), i},
+ {QStringLiteral("j"), j},
+ {QStringLiteral("k"), k}
+ }));
+ QVERIFY(!check.isNull());
+ check->setProperty("l1", object->property("l1"));
+ check->setProperty("l2", object->property("l2"));
+ check->setProperty("l3", object->property("l3"));
+
+ listsEqual(object.data(), object.data(), "Slice");
+ listsEqual(object.data(), check.data(), "Slice");
+ QCOMPARE(object->property("listPropertyIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("listPropertyIndexOf"), check->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf"));
+ QCOMPARE(object->property("listPropertyLastIndexOf"), check->property("jsArrayLastIndexOf"));
+}
+
+void tst_QmlCppCodegen::jsArrayMethodsWithParams_data()
+{
+ QTest::addColumn<int>("i");
+ QTest::addColumn<int>("j");
+ QTest::addColumn<int>("k");
+
+ const int indices[] = {
+ std::numeric_limits<int>::min(),
+ -10, -3, -2, -1, 0, 1, 2, 3, 10,
+ std::numeric_limits<int>::max(),
+ };
+
+ // We cannot test the full cross product. So, take a random sample instead.
+ const qsizetype numIndices = sizeof(indices) / sizeof(int);
+ qsizetype seed = QRandomGenerator::global()->generate();
+ const int numSamples = 4;
+ for (int i = 0; i < numSamples; ++i) {
+ seed = qHash(i, seed);
+ const int vi = indices[qAbs(seed) % numIndices];
+ for (int j = 0; j < numSamples; ++j) {
+ seed = qHash(j, seed);
+ const int vj = indices[qAbs(seed) % numIndices];
+ for (int k = 0; k < numSamples; ++k) {
+ seed = qHash(k, seed);
+ const int vk = indices[qAbs(seed) % numIndices];
+ const QString tag = QLatin1String("%1/%2/%3").arg(
+ QString::number(vi), QString::number(vj), QString::number(vk));
+ QTest::newRow(qPrintable(tag)) << vi << vj << vk;
+
+ // output all the tags so that we can find out
+ // what combination caused a test to hang.
+ qDebug().noquote() << "scheduling" << tag;
+ }
+ }
+ }
+}
+
+void tst_QmlCppCodegen::jsImport()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsimport.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("value").toInt(), 42);
+}
void tst_QmlCppCodegen::jsMathObject()
{
@@ -1707,579 +2875,904 @@ void tst_QmlCppCodegen::jsMathObject()
qDebug() << name << "failed.";
}
-void tst_QmlCppCodegen::intEnumCompare()
+void tst_QmlCppCodegen::jsmoduleImport()
{
QQmlEngine engine;
- {
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/intEnumCompare.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("a").toBool(), true);
- QCOMPARE(o->property("b").toBool(), false);
- QCOMPARE(o->property("c").toBool(), true);
- QCOMPARE(o->property("d").toBool(), false);
- }
-
- {
- // We cannot use Qt.red in QML because it's lower case.
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumInvalid.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("c").toBool(), true);
- QCOMPARE(o->property("d").toBool(), false);
- }
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/jsmoduleimport.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("ok").toBool(), true);
+ QVariant okFunc = object->property("okFunc");
+ QCOMPARE(okFunc.metaType(), QMetaType::fromType<QJSValue>());
+ QJSValue val = engine.toScriptValue(okFunc);
+ QJSValue result = val.call();
+ QVERIFY(result.isBool());
+ QVERIFY(result.toBool());
}
-void tst_QmlCppCodegen::attachedSelf()
+void tst_QmlCppCodegen::lengthAccessArraySequenceCompat()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/SelectionRectangle.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ArraySequenceLengthInterop.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
-
- QObject *handle = qvariant_cast<QObject *>(o->property("aa"));
- QVERIFY(handle);
- QVERIFY(qvariant_cast<QObject *>(handle->property("rect")) != nullptr);
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("length").toInt(), 100);
}
-void tst_QmlCppCodegen::functionReturningVoid()
+void tst_QmlCppCodegen::letAndConst()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionReturningVoid.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/letAndConst.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
-
- // It should be able to call the methods and wrap the void values into invalid QVariants,
- // without crashing.
- QVERIFY(o->metaObject()->indexOfProperty("aa") >= 0);
- QVERIFY(o->metaObject()->indexOfProperty("bb") >= 0);
- QVERIFY(!o->property("aa").isValid());
- QVERIFY(!o->property("bb").isValid());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"ab"_s);
}
-void tst_QmlCppCodegen::functionCallOnNamespaced()
+void tst_QmlCppCodegen::listAsArgument()
{
QQmlEngine engine;
- {
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themergood.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("i").toInt(), 12);
- }
- {
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/themerbad.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(5.0, 10.0, 1.0, 1.0)));
- }
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listAsArgument.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->property("i").toInt(), 4);
+ QCOMPARE(o->property("j").toInt(), 2);
+ QCOMPARE(o->property("i1").toInt(), 2);
+ QCOMPARE(o->property("i2").toInt(), 4);
+ QCOMPARE(o->property("d").value<QObject *>()->objectName(), u"this one"_s);
+
+ int singleInt = 0;
+ QList<int> moreInts;
+ QMetaObject::invokeMethod(o.data(), "returnInts1", Q_RETURN_ARG(QList<int>, moreInts));
+ QCOMPARE(moreInts, QList<int>({5, 4, 3, 2, 1}));
+ QMetaObject::invokeMethod(o.data(), "selectSecondInt", Q_RETURN_ARG(int, singleInt), Q_ARG(QList<int>, moreInts));
+ QCOMPARE(singleInt, 4);
}
-void tst_QmlCppCodegen::flushBeforeCapture()
+void tst_QmlCppCodegen::listConversion()
{
- QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noBindingLoop.qml"_s));
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/listConversion.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QVERIFY(!o.isNull());
- QCOMPARE(o->property("deviation").toDouble(), 9.0 / 3.3333);
- QCOMPARE(o->property("samples").toInt(), 16);
- QCOMPARE(o->property("radius").toDouble(), 8.0);
-}
+ QQmlListProperty<QObject> list = o->property("o").value<QQmlListProperty<QObject>>();
+ QCOMPARE(list.count(&list), 3);
+ for (int i = 0; i < 3; ++i) {
+ QObject *entry = list.at(&list, i);
+ Person *person = qobject_cast<Person *>(entry);
+ QVERIFY(person);
+ QCOMPARE(person->name(), u"Horst %1"_s.arg(i + 1));
+ }
-void tst_QmlCppCodegen::unknownAttached()
-{
- QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownAttached.qml"_s));
- QVERIFY(c.isError());
+ QStringList strings = o->property("s").value<QStringList>();
+ QCOMPARE(strings, QStringList({u"Horst 1"_s, u"Horst 2"_s, u"Horst 3"_s}));
+
+ QVariantList vars = o->property("v").toList();
+ QCOMPARE(vars, QVariantList({
+ QString(),
+ QVariant::fromValue<qsizetype>(3),
+ QVariant::fromValue<Person *>(nullptr)
+ }));
}
-void tst_QmlCppCodegen::variantlist()
+void tst_QmlCppCodegen::listIndices()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantlist.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listIndices.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(o);
- const QVariantList things = qvariant_cast<QVariantList>(o->property("things"));
- QCOMPARE(things.size(), 2);
- QCOMPARE(things[0].toString(), u"thing"_s);
- QCOMPARE(things[1].toInt(), 30);
+ QQmlListReference list(o.data(), "items");
+ QCOMPARE(list.count(), 3);
+ 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::popContextAfterRet()
+void tst_QmlCppCodegen::listLength()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/popContextAfterRet.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
-
- QCOMPARE(o->objectName(), QString());
- o->setProperty("stackViewDepth", 1);
- QCOMPARE(o->objectName(), u"backgroundImage"_s);
- o->setProperty("stackViewDepth", 2);
- QCOMPARE(o->objectName(), u"backgroundBlur"_s);
- o->setProperty("stackViewDepth", 1);
- QCOMPARE(o->objectName(), u"backgroundImage"_s);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listlength.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("l").toInt(), 2);
}
-void tst_QmlCppCodegen::revisions()
+void tst_QmlCppCodegen::listOfInvisible()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/revisions.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
-
- QCOMPARE(o->property("delayed").toBool(), true);
- QCOMPARE(o->property("gotten").toInt(), 5);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listOfInvisible.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("width").toDouble(), 27.0);
}
-void tst_QmlCppCodegen::invisibleBase()
+void tst_QmlCppCodegen::listPropertyAsModel()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleBase.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listPropertyAsModel.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(qvariant_cast<QObject *>(o->property("n")), o.data());
+
+ QQmlListReference children(o.data(), "children");
+ QCOMPARE(children.count(), 5);
}
-void tst_QmlCppCodegen::notEqualsInt()
+void tst_QmlCppCodegen::listToString()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notEqualsInt.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listToString.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(QtDebugMsg, "[one,two]");
+ QTest::ignoreMessage(QtDebugMsg, "one,two");
+ QTest::ignoreMessage(QtDebugMsg, "[1,2]");
+ QTest::ignoreMessage(QtDebugMsg, "1,2");
+ QTest::ignoreMessage(
+ QtDebugMsg,
+ QRegularExpression("\\[QObject_QML_[0-9]+\\(0x[0-9a-f]+\\),"
+ "QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)\\]"));
+ QTest::ignoreMessage(
+ QtDebugMsg,
+ QRegularExpression("QObject_QML_[0-9]+\\(0x[0-9a-f]+\\),"
+ "QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)"));
+
+ QTest::ignoreMessage(QtDebugMsg, "[a,b]");
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QObject *t = qmlContext(o.data())->objectForName(u"t"_s);
- QVERIFY(t);
- QCOMPARE(t->property("text").toString(), u"Foo"_s);
- QMetaObject::invokeMethod(o.data(), "foo");
- QCOMPARE(t->property("text").toString(), u"Bar"_s);
}
-void tst_QmlCppCodegen::infinities()
+void tst_QmlCppCodegen::lotsOfRegisters()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinities.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/page.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QCOMPARE(o->property("positiveInfinity").toDouble(), std::numeric_limits<double>::infinity());
- QCOMPARE(o->property("negativeInfinity").toDouble(), -std::numeric_limits<double>::infinity());
+ const auto compare = [&]() {
+ const qreal implicitBackgroundWidth = object->property("implicitBackgroundWidth").toDouble();
+ const qreal leftInset = object->property("leftInset").toDouble();
+ const qreal rightInset = object->property("rightInset").toDouble();
+ const qreal contentWidth = object->property("contentWidth").toDouble();
+ const qreal leftPadding = object->property("leftPadding").toDouble();
+ const qreal rightPadding = object->property("rightPadding").toDouble();
+ const qreal implicitFooterWidth = object->property("implicitFooterWidth").toDouble();
+ const qreal implicitHeaderWidth = object->property("implicitHeaderWidth").toDouble();
- const double positiveZero = o->property("positiveZero").toDouble();
- QCOMPARE(positiveZero, 0.0);
- QVERIFY(!std::signbit(positiveZero));
+ const qreal implicitWidth = object->property("implicitWidth").toDouble();
+ QCOMPARE(implicitWidth, qMax(qMax(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding),
+ qMax(implicitHeaderWidth, implicitFooterWidth)));
+ };
- const double negativeZero = o->property("negativeZero").toDouble();
- QCOMPARE(negativeZero, -0.0);
- QVERIFY(std::signbit(negativeZero));
+ compare();
- QVERIFY(qIsNaN(o->property("naN").toDouble()));
+ const QList<const char *> props = {
+ "leftInset", "rightInset", "contentWidth", "leftPadding", "rightPadding"
+ };
+
+ for (int i = 0; i < 100; ++i) {
+ QVERIFY(object->setProperty(props[i % props.size()], (i * 17) % 512));
+ compare();
+ }
}
-void tst_QmlCppCodegen::blockComments()
+void tst_QmlCppCodegen::math()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/blockComments.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("implicitHeight").toDouble(), 8.0);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/math.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("a").toInt(), 9);
+ QCOMPARE(object->property("b").toDouble(), 50.0 / 22.0);
+ QCOMPARE(object->property("c").toDouble(), std::atan(1.0) * 8.0);
}
-void tst_QmlCppCodegen::functionLookup()
+void tst_QmlCppCodegen::mathMinMax()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/functionLookup.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathMinMax.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ // Math.max()
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "9");
+
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "0.002");
+ QTest::ignoreMessage(QtDebugMsg, "5.4");
+ QTest::ignoreMessage(QtDebugMsg, "NaN");
+ QTest::ignoreMessage(QtDebugMsg, "Infinity");
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "0.08");
+ QTest::ignoreMessage(QtDebugMsg, "Infinity");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "NaN");
+
+ // Math.min()
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "-2");
+ QTest::ignoreMessage(QtDebugMsg, "-2");
+ QTest::ignoreMessage(QtDebugMsg, "0");
+
+ QTest::ignoreMessage(QtDebugMsg, "0");
+ QTest::ignoreMessage(QtDebugMsg, "-0.001");
+ QTest::ignoreMessage(QtDebugMsg, "0.002");
+ QTest::ignoreMessage(QtDebugMsg, "NaN");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-1");
+ QTest::ignoreMessage(QtDebugMsg, "-8");
+ QTest::ignoreMessage(QtDebugMsg, "NaN");
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- const QVariant foo = o->property("bar");
- QCOMPARE(foo.metaType(), QMetaType::fromType<QJSValue>());
- const QJSManagedValue method(engine.toScriptValue(foo), &engine);
- QVERIFY(method.isFunction());
- const QJSValue result = method.call();
- QVERIFY(result.isString());
- QCOMPARE(result.toString(), QStringLiteral("a99"));
+ QVERIFY(!o.isNull());
}
-void tst_QmlCppCodegen::objectInVar()
+void tst_QmlCppCodegen::mathOperations()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectInVar.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathOperations.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data());
- bool result = false;
- QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
- QVERIFY(result);
+ const QMetaObject *metaObject = o->metaObject();
- o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr));
- QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
- QVERIFY(!result);
+ char t1;
+ char t2;
+ QString name;
+ const auto guard = qScopeGuard([&]() {
+ if (QTest::currentTestFailed()) {
+ qDebug() << t1 << t2 << name << "failed on:";
+ qDebug() << "doubles" << o->property("a").toDouble() << o->property("b").toDouble();
+ qDebug() << "integers" << o->property("ia").toInt() << o->property("ib").toInt();
+ qDebug() << "booleans" << o->property("ba").toBool() << o->property("bb").toBool();
+ }
+ });
+
+ for (double a : numbers) {
+ for (double b : numbers) {
+ o->setProperty("a", a);
+ o->setProperty("b", b);
+ for (int i = 0, end = metaObject->propertyCount(); i != end; ++i) {
+ const QMetaProperty prop = metaObject->property(i);
+ const QByteArray propName = prop.name();
+
+ if (propName.size() < 3 || propName == "objectName")
+ continue;
+
+ t1 = propName[0];
+ t2 = propName[1];
+ name = QString::fromUtf8(propName.mid(2));
+
+ double expected;
+
+ switch (t2) {
+ case 'd':
+ case '_':
+ switch (t1) {
+ case 'd':
+ expected = jsEval<double, double>(a, b, name, &engine);
+ break;
+ case 'i':
+ expected = jsEval<int, double>(a, b, name, &engine);
+ break;
+ case 'b':
+ expected = jsEval<bool, double>(a, b, name, &engine);
+ break;
+ }
+ break;
+ case 'i':
+ switch (t1) {
+ case 'd':
+ expected = jsEval<double, int>(a, b, name, &engine);
+ break;
+ case 'i':
+ expected = jsEval<int, int>(a, b, name, &engine);
+ break;
+ case 'b':
+ expected = jsEval<bool, int>(a, b, name, &engine);
+ break;
+ }
+ break;
+ case 'b':
+ switch (t1) {
+ case 'd':
+ expected = jsEval<double, bool>(a, b, name, &engine);
+ break;
+ case 'i':
+ expected = jsEval<int, bool>(a, b, name, &engine);
+ break;
+ case 'b':
+ expected = jsEval<bool, bool>(a, b, name, &engine);
+ break;
+ }
+ break;
+ }
+
+ const double result = prop.read(o.data()).toDouble();
+ QCOMPARE(result, expected);
+ }
+ }
+ }
}
-void tst_QmlCppCodegen::functionTakingVar()
+void tst_QmlCppCodegen::mathStaticProperties()
{
QQmlEngine engine;
- const QUrl document(u"qrc:/qt/qml/TestTypes/functionTakingVar.qml"_s);
- QQmlComponent c(&engine, document);
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathStaticProperties.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QVERIFY(!o->property("c").isValid());
+ // Approximate values based on
+ // https://262.ecma-international.org/14.0/#sec-value-properties-of-the-math-object
+ QCOMPARE(object->property("e").toDouble(), 2.7182818284590452354);
+ QCOMPARE(object->property("ln10").toDouble(), 2.302585092994046);
+ QCOMPARE(object->property("ln2").toDouble(), 0.6931471805599453);
+ QCOMPARE(object->property("log10e").toDouble(), 0.4342944819032518);
+ QCOMPARE(object->property("log2e").toDouble(), 1.4426950408889634);
+ QCOMPARE(object->property("pi").toDouble(), 3.1415926535897932);
+ QCOMPARE(object->property("sqrt1_2").toDouble(), 0.7071067811865476);
+ QCOMPARE(object->property("sqrt2").toDouble(), 1.4142135623730951);
+}
- 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);
+void tst_QmlCppCodegen::mergedObjectReadWrite()
+{
+ QQmlEngine e;
+ {
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/mergedObjectRead.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtDebugMsg, "null");
+ QTest::ignoreMessage(
+ QtWarningMsg, QRegularExpression("TypeError: Cannot read property 'x' of null"));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ }
- QCOMPARE(o->property("c"), QVariant::fromValue<int>(11));
+ {
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/mergedObjectWrite.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(
+ "TypeError: Value is null and could not be converted to an object"));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ }
}
-void tst_QmlCppCodegen::testIsnan()
+void tst_QmlCppCodegen::methodOnListLookup()
{
QQmlEngine engine;
- const QUrl document(u"qrc:/qt/qml/TestTypes/isnan.qml"_s);
- QQmlComponent c(&engine, document);
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ const QUrl url(u"qrc:/qt/qml/TestTypes/methodOnListLookup.qml"_s);
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QCOMPARE(o->property("good").toDouble(), 10.1);
- QVERIFY(qIsNaN(o->property("bad").toDouble()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
- const QVariant a = o->property("a");
- QCOMPARE(a.metaType(), QMetaType::fromType<bool>());
- QVERIFY(!a.toBool());
+ QCOMPARE(o->objectName(), u"no one");
- const QVariant b = o->property("b");
- QCOMPARE(b.metaType(), QMetaType::fromType<bool>());
- QVERIFY(b.toBool());
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString()
+ + ":14: TypeError: Cannot call method 'getName' of undefined"_L1));
+ QMetaObject::invokeMethod(o.data(), "boom");
}
-void tst_QmlCppCodegen::fallbackLookups()
+void tst_QmlCppCodegen::methods()
{
QQmlEngine engine;
- const QUrl document(u"qrc:/qt/qml/TestTypes/fallbacklookups.qml"_s);
- QQmlComponent c(&engine, document);
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/methods.qml"_s));
+ QVERIFY(component.isReady());
- QCOMPARE(o->objectName(), QString());
- int result = 0;
+ QTest::ignoreMessage(QtDebugMsg, "The Bar");
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(u"TypeError: .* is not a function"_s));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj);
+ BirthdayParty *party(qobject_cast<BirthdayParty *>(obj.data()));
- QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 16);
- QCOMPARE(o->objectName(), QStringLiteral("aa93"));
+ QVERIFY(party && party->host());
+ QCOMPARE(party->guestCount(), 5);
- QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 17);
- QCOMPARE(o->objectName(), QStringLiteral("bb94"));
+ bool foundGreen = false;
+ bool foundFoo = false;
+ for (int ii = 0; ii < party->guestCount(); ++ii) {
+ if (party->guest(ii)->name() == u"William Green"_s)
+ foundGreen = true;
+ if (party->guest(ii)->name() == u"The Foo"_s)
+ foundFoo = true;
+ }
- QObject *singleton = nullptr;
- QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton));
- QVERIFY(singleton);
+ QVERIFY(foundGreen);
+ QVERIFY(foundFoo);
- QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 18);
- QCOMPARE(singleton->objectName(), QStringLiteral("cc95"));
+ QCOMPARE(obj->property("n1").toString(), u"onGurk"_s);
+ QCOMPARE(obj->property("n2").toString(), u"onSemmeln"_s);
+ QCOMPARE(obj->property("n3"), QVariant());
- QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 19);
- QCOMPARE(singleton->objectName(), QStringLiteral("dd96"));
+ {
+ QVariant ret;
+ obj->metaObject()->invokeMethod(obj.data(), "retrieveVar", Q_RETURN_ARG(QVariant, ret));
+ QCOMPARE(ret.typeId(), QMetaType::QString);
+ QCOMPARE(ret.toString(), u"Jack Smith"_s);
+ }
+
+ {
+ QString ret;
+ obj->metaObject()->invokeMethod(obj.data(), "retrieveString", Q_RETURN_ARG(QString, ret));
+ QCOMPARE(ret, u"Jack Smith"_s);
+ }
+
+ QCOMPARE(party->host()->shoeSize(), 12);
+ obj->metaObject()->invokeMethod(obj.data(), "storeElement");
+ QCOMPARE(party->host()->shoeSize(), 13);
+ QJSManagedValue v = engine.toManagedValue(obj->property("dresses"));
+ QVERIFY(v.isArray());
+
+ QJSManagedValue inner(v.property(2), &engine);
+ QVERIFY(inner.isArray());
+ QCOMPARE(inner.property(0).toInt(), 1);
+ QCOMPARE(inner.property(1).toInt(), 2);
+ QCOMPARE(inner.property(2).toInt(), 3);
+
+ QCOMPARE(obj->property("enumValue").toInt(), 2);
}
-void tst_QmlCppCodegen::typedArray()
+void tst_QmlCppCodegen::modulePrefix()
{
QQmlEngine engine;
- const QUrl document(u"qrc:/qt/qml/TestTypes/typedArray.qml"_s);
- QQmlComponent c(&engine, document);
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QDateTime date;
- QVERIFY(qvariant_cast<QList<int>>(o->property("values2")).isEmpty());
- QCOMPARE(qvariant_cast<QList<int>>(o->property("values3")),
- QList<int>({1, 2, 3, 4}));
- QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
- QList<QDateTime>({date, date, date}));
- QCOMPARE(qvariant_cast<QList<double>>(o->property("values5")),
- QList<double>({1, 2, 3.4, 30, 0, 0}));
- date = QDateTime::currentDateTime();
- o->setProperty("aDate", date);
- QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
- QList<QDateTime>({date, date, date}));
-
- QQmlListProperty<QObject> values6
- = qvariant_cast<QQmlListProperty<QObject>>(o->property("values6"));
- QCOMPARE(values6.count(&values6), 3);
- for (int i = 0; i < 3; ++i)
- QCOMPARE(values6.at(&values6, i), o.data());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/modulePrefix.qml"_s));
- QCOMPARE(o->property("inIntList").toInt(), 2);
- QCOMPARE(qvariant_cast<QDateTime>(o->property("inDateList")), date);
- QCOMPARE(o->property("inRealList").toDouble(), 30.0);
- QCOMPARE(o->property("inCharList").toString(), QStringLiteral("f"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- const QMetaObject *metaObject = o->metaObject();
- QMetaMethod method = metaObject->method(metaObject->indexOfMethod("stringAt10(QString)"));
- QVERIFY(method.isValid());
+ QCOMPARE(rootObject->property("foo").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
+ QCOMPARE(rootObject->property("bar").toDateTime(), QDateTime(QDate(1911, 3, 4), QTime()));
+ QCOMPARE(rootObject->property("baz").toString(), QStringLiteral("ItIsTheSingleton"));
+}
- // If LoadElement threw an exception the function would certainly return neither 10 nor 20.
- int result = 0;
- method.invoke(
- o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("a")));
- QCOMPARE(result, 10);
- method.invoke(
- o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("aaaaaaaaaaa")));
- QCOMPARE(result, 20);
+void tst_QmlCppCodegen::multiDirectory_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::addRow("from qt_add_qml_module")
+ << QUrl(u"qrc:/qt/qml/TestTypes/extra/extra.qml"_s);
+#ifndef VERY_OLD_CMAKE
+ QTest::addRow("from qt_target_qml_sources")
+ << QUrl(u"qrc:/qt/qml/TestTypes/extra2/extra.qml"_s);
+#endif
}
-void tst_QmlCppCodegen::prefixedType()
+void tst_QmlCppCodegen::multiDirectory()
{
+ QFETCH(QUrl, url);
QQmlEngine engine;
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- // We need to add an import path here because we cannot namespace the implicit import.
- // The implicit import is what we use for all the other tests, even if we explicitly
- // import TestTypes. That is because the TestTypes module is in a subdirectory "data".
- engine.addImportPath(u":/"_s);
+ QCOMPARE(rootObject->property("r").value<QRectF>(), QRectF(4, 6, 8, 10));
+}
- const QUrl document(u"qrc:/qt/qml/TestTypes/prefixedMetaType.qml"_s);
- QQmlComponent c(&engine, document);
+void tst_QmlCppCodegen::multiForeign()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multiforeign.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
-
- QCOMPARE(o->property("state").toInt(), 2);
- QVERIFY(qvariant_cast<QObject *>(o->property("a")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("b")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("c")) == nullptr);
-
- QVERIFY(qvariant_cast<QObject *>(o->property("d")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("e")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("f")) == nullptr);
-
- QVERIFY(qvariant_cast<QObject *>(o->property("g")) != nullptr);
- QVERIFY(qvariant_cast<QObject *>(o->property("h")) != nullptr);
-
- QCOMPARE(o->property("countG").toInt(), 11);
- QCOMPARE(o->property("countH").toInt(), 11);
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), u"not here and not there"_s);
}
-void tst_QmlCppCodegen::evadingAmbiguity()
+void tst_QmlCppCodegen::multiLookup()
{
+ // Multiple lookups of singletons (Qt in this case) don't clash with one another.
QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/immediateQuit.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- // We need to add an import path here because we cannot namespace the implicit import.
- // The implicit import is what we use for all the other tests, even if we explicitly
- // import TestTypes. That is because the TestTypes module is in a subdirectory "data".
- engine.addImportPath(u":/"_s);
-
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous1/Ambiguous.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QScopedPointer<QObject> o1(c1.create());
- QCOMPARE(o1->objectName(), QStringLiteral("Ambiguous"));
- QCOMPARE(o1->property("i").toString(), QStringLiteral("Ambiguous1"));
+ const QByteArray message = QByteArray("End: ") + arg1();
+ QTest::ignoreMessage(QtDebugMsg, message.constData());
- QQmlComponent c2(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguous2/Ambiguous.qml"_s));
- QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
- QScopedPointer<QObject> o2(c2.create());
- QCOMPARE(o2->objectName(), QStringLiteral("Ambiguous"));
- QCOMPARE(o2->property("i").toString(), QStringLiteral("Ambiguous2"));
+ QSignalSpy quitSpy(&engine, &QQmlEngine::quit);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(quitSpy.size(), 1);
}
-void tst_QmlCppCodegen::fromBoolValue()
+void tst_QmlCppCodegen::multipleCtors()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fromBoolValue.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multipleCtors.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QCOMPARE(o->property("a").toBool(), true);
- o->setProperty("x", 100);
- QCOMPARE(o->property("a").toBool(), false);
-
- QCOMPARE(o->property("width").toInt(), 100);
- QCOMPARE(o->property("b").toBool(), false);
-
- QScopedPointer<QObject> parent(c.create());
- o->setProperty("parent", QVariant::fromValue(parent.data()));
- QCOMPARE(o->property("width").toInt(), 100);
- QCOMPARE(o->property("b").toBool(), false);
-
- o->setProperty("state", QVariant::fromValue(u"foo"_s));
- QCOMPARE(o->property("width").toInt(), 0);
- QCOMPARE(o->property("b").toBool(), false);
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("wr").value<ValueTypeWithLength>().length(), 3);
+ QCOMPARE(o->property("wp").value<ValueTypeWithLength>().length(), 11);
+ QCOMPARE(o->property("wi").value<ValueTypeWithLength>().length(), 17);
}
-void tst_QmlCppCodegen::invisibleTypes()
+void tst_QmlCppCodegen::namespaceWithEnum()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleTypes.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/namespaceWithEnum.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("i").toInt(), 2);
+}
- QObject *singleton = qvariant_cast<QObject *>(o->property("singleton"));
- QVERIFY(singleton != nullptr);
- QCOMPARE(singleton->metaObject()->className(), "SingletonModel");
+void tst_QmlCppCodegen::noQQmlData()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/noQQmlData.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
- QObject *attached = qvariant_cast<QObject *>(o->property("attached"));
- QVERIFY(attached != nullptr);
- QCOMPARE(attached->metaObject()->className(), "AttachedAttached");
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: "
+ "Cannot read property 'name' of null");
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
-// TODO: This doesn't work in interpreted mode:
-// const QMetaObject *meta = qvariant_cast<const QMetaObject *>(o->property("metaobject"));
-// QVERIFY(meta != nullptr);
-// QCOMPARE(meta->className(), "DerivedFromInvisible");
-}
+ BirthdayParty *party = qobject_cast<BirthdayParty *>(root.data());
+ QVERIFY(party != nullptr);
+ QCOMPARE(party->host(), nullptr);
+ QCOMPARE(party->property("n").toString(), QString());
-class MyCppType : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(bool useListDelegate
- READ useListDelegate
- WRITE setUseListDelegate
- NOTIFY useListDelegateChanged)
-public:
- explicit MyCppType(QObject * parent = nullptr) : QObject(parent) {}
+ Person *host1 = new Person(party);
+ party->setHost(host1);
+ QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s);
+ host1->setName(u"Marge"_s);
+ QCOMPARE(party->property("n").toString(), u"Marge in da house!"_s);
- bool useListDelegate() const { return m_useListDelegate; }
- void setUseListDelegate(bool useListDelegate)
- {
- if (useListDelegate != m_useListDelegate) {
- m_useListDelegate = useListDelegate;
- emit useListDelegateChanged();
- }
- }
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/noQQmlData.qml:7: TypeError: "
+ "Cannot read property 'name' of null");
-signals:
- void useListDelegateChanged();
+ // Doesn't crash
+ party->setHost(nullptr);
-private:
- bool m_useListDelegate = false;
-};
+ // Lookups are initialized now, and we introduce an object without QQmlData
+ Person *host2 = new Person(party);
+ party->setHost(host2);
+ QCOMPARE(party->property("n").toString(), u"Bart in da house!"_s);
+ host2->setName(u"Homer"_s);
+ QCOMPARE(party->property("n").toString(), u"Homer in da house!"_s);
-void tst_QmlCppCodegen::invalidPropertyType()
-{
- // Invisible on purpose
- qmlRegisterType<MyCppType>("App", 1, 0, "MyCppType");
+ QMetaObject::invokeMethod(party, "burn");
+ engine.collectGarbage();
+ // Does not crash
+ party->setProperty("inDaHouse", u" burns!"_s);
+
+ // Mr Burns may or may not burn, depending on whether we use lookups.
+ // If using lookups, the binding is aborted when we find the isQueuedForDeletion flag.
+ // If reading the property directly, we don't have to care about it.
+ QVERIFY(party->property("n").toString().startsWith(u"Mr Burns"_s));
+}
+
+void tst_QmlCppCodegen::nonNotifyable()
+{
QQmlEngine engine;
- QQmlComponent okComponent(&engine, QUrl(u"qrc:/qt/qml/TestTypes/OkType.qml"_s));
- QVERIFY2(okComponent.isReady(), qPrintable(okComponent.errorString()));
- QScopedPointer<QObject> picker(okComponent.create());
- QVERIFY2(!picker.isNull(), qPrintable(okComponent.errorString()));
- QObject *inner = qmlContext(picker.data())->objectForName(u"inner"_s);
- QVERIFY(inner);
- MyCppType *myCppType = qobject_cast<MyCppType *>(inner);
- QVERIFY(myCppType);
- QVERIFY(!myCppType->useListDelegate());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nonNotifyable.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/BadType.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.createWithInitialProperties(
- QVariantMap {{u"picker"_s, QVariant::fromValue(picker.data())}}));
- QVERIFY2(!o.isNull(), qPrintable(c.errorString()));
- QVERIFY(!myCppType->useListDelegate());
+ QCOMPARE(qvariant_cast<QDateTime>(object->property("dayz")),
+ QDateTime(QDate(2121, 1, 12), QTime()));
+ QCOMPARE(qvariant_cast<QDateTime>(object->property("oParty")),
+ QDateTime(QDate(2111, 12, 11), QTime()));
+}
- o->setProperty("useListDelegate", QVariant::fromValue<bool>(true));
- QVERIFY(myCppType->useListDelegate());
+void tst_QmlCppCodegen::notEqualsInt()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notEqualsInt.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QObject *t = qmlContext(o.data())->objectForName(u"t"_s);
+ QVERIFY(t);
+ QCOMPARE(t->property("text").toString(), u"Foo"_s);
+ QMetaObject::invokeMethod(o.data(), "foo");
+ QCOMPARE(t->property("text").toString(), u"Bar"_s);
}
-void tst_QmlCppCodegen::valueTypeLists()
+void tst_QmlCppCodegen::notNotString()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeLists.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notNotString.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QCOMPARE(qvariant_cast<QRectF>(o->property("rectInBounds")), QRectF(1, 2, 3, 4));
- QVERIFY(o->metaObject()->indexOfProperty("rectOutOfBounds") > 0);
- QVERIFY(!o->property("rectOutOfBounds").isValid());
+ QCOMPARE(o->property("notNotString").value<bool>(), false);
+ o->setObjectName(u"a"_s);
+ QCOMPARE(o->property("notNotString").value<bool>(), true);
+}
- QCOMPARE(qvariant_cast<QString>(o->property("stringInBounds")), QStringLiteral("bbb"));
- QVERIFY(o->metaObject()->indexOfProperty("stringOutOfBounds") > 0);
- QVERIFY(!o->property("stringOutOfBounds").isValid());
+void tst_QmlCppCodegen::nullAccess()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccess.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QCOMPARE(qvariant_cast<int>(o->property("intInBounds")), 7);
- QVERIFY(o->metaObject()->indexOfProperty("intOutOfBounds") > 0);
- QVERIFY(!o->property("intOutOfBounds").isValid());
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/nullAccess.qml:4:5: TypeError: "
+ "Cannot read property 'width' of null");
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/nullAccess.qml:5:5: TypeError: "
+ "Cannot read property 'height' of null");
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/nullAccess.qml:6: TypeError: Value is null and "
+ "could not be converted to an object");
+ QScopedPointer<QObject> object(component.create());
- QCOMPARE(qvariant_cast<QString>(o->property("charInBounds")), QStringLiteral("d"));
- QVERIFY(o->metaObject()->indexOfProperty("charOutOfBounds") > 0);
- QVERIFY(!o->property("charOutOfBounds").isValid());
+ QCOMPARE(object->property("width").toDouble(), 0.0);
+ QCOMPARE(object->property("height").toDouble(), 0.0);
}
-void tst_QmlCppCodegen::boundComponents()
+void tst_QmlCppCodegen::nullAccessInsideSignalHandler()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/boundComponents.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullAccessInsideSignalHandler.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QTest::ignoreMessage(QtWarningMsg,
+ "qrc:/qt/qml/TestTypes/nullAccessInsideSignalHandler.qml:15: ReferenceError: "
+ "text is not defined");
+ QScopedPointer<QObject> object(component.create());
+ QSignalSpy spy(object.data(), SIGNAL(say_hello()));
+ QTRY_VERIFY(spy.size() > 0);
+}
+
+
+void tst_QmlCppCodegen::nullComparison()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullComparison.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QObject *c1o = o->property("o").value<QObject *>();
- QVERIFY(c1o != nullptr);
- QCOMPARE(c1o->objectName(), u"bar"_s);
+ QCOMPARE(o->property("v").toInt(), 1);
+ QCOMPARE(o->property("w").toInt(), 3);
+ QCOMPARE(o->property("x").toInt(), 1);
+ QCOMPARE(o->property("y").toInt(), 5);
+ QCOMPARE(o->property("z").toInt(), 18);
+}
- QObject *c2o = c1o->property("o").value<QObject *>();
- QVERIFY(c2o != nullptr);
- QCOMPARE(c2o->objectName(), u"bar12"_s);
+void tst_QmlCppCodegen::nullishCoalescing_data()
+{
+ QTest::addColumn<QString>("propertyName");
+ QTest::addColumn<QVariant>("expected");
+
+ const auto undefinedValue = QVariant();
+ const auto nullValue = QVariant::fromMetaType(QMetaType::fromType<std::nullptr_t>(), nullptr);
+
+ QTest::addRow("trivial-good-int") << "p1" << QVariant(5);
+ QTest::addRow("trivial-good-string") << "p2" << QVariant("6");
+
+ QTest::addRow("trivial-bad-undefined-undefined") << "p3" << undefinedValue;
+ QTest::addRow("trivial-bad-undefined-null") << "p4" << nullValue;
+ QTest::addRow("trivial-bad-undefined-int") << "p5" << QVariant(-1);
+ QTest::addRow("trivial-bad-undefined-string") << "p6" << QVariant("-1");
+
+ QTest::addRow("trivial-bad-null-undefined") << "p7" << undefinedValue;
+ QTest::addRow("trivial-bad-null-null") << "p8" << nullValue;
+ QTest::addRow("trivial-bad-null-int") << "p9" << QVariant(-1);
+ QTest::addRow("trivial-bad-null-string") << "p10" << QVariant("-1");
+
+ QTest::addRow("enum1") << "p11" << QVariant(1);
+
+ QTest::addRow("multiple ?? int") << "p12" << QVariant(1);
+ QTest::addRow("multiple ?? string") << "p13" << QVariant("1");
+ QTest::addRow("multiple ?? mixed2") << "p14" << QVariant("2");
+ QTest::addRow("multiple ?? mixed3") << "p15" << QVariant(1);
+
+ QTest::addRow("optional + nullish bad") << "p16" << QVariant(-1);
+ QTest::addRow("optional + nullish good") << "p17" << QVariant(5);
}
-class InvisibleListElementType : public QObject
+void tst_QmlCppCodegen::nullishCoalescing()
{
- Q_OBJECT
-public:
- InvisibleListElementType(QObject *parent = nullptr) : QObject(parent) {}
-};
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/qt/qml/TestTypes/nullishCoalescing.qml"_s);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
-void tst_QmlCppCodegen::invisibleListElementType()
+ QFETCH(QString, propertyName);
+ QFETCH(QVariant, expected);
+
+ QVariant actual = o->property(propertyName.toLocal8Bit());
+ QCOMPARE(actual, expected);
+}
+
+void tst_QmlCppCodegen::numbersInJsPrimitive()
{
- qmlRegisterType<InvisibleListElementType>("Invisible", 1, 0, "InvisibleListElement");
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/invisibleListElementType.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/numbersInJsPrimitive.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QObject *a = o->property("a").value<QObject *>();
- QVERIFY(a);
+ const QList<qint64> zeroes
+ = {0, 0, 0, 0, 0, 0, 0, 0};
+ const QList<qint64> written
+ = {35, 36, 37, 38, 39, 40, 41, 42};
+ const QList<qint64> writtenNegative
+ = {-35, 220, -37, 65498, -39, 4294967256, -41, 4294967254};
+ const QList<QList<qint64>> writtenShuffled = {
+ { -36, 219, -38, 65497, -40, 4294967255, -42, 4294967260 },
+ { -37, 218, -39, 65496, -41, 4294967254, -36, 4294967259 },
+ { -38, 217, -40, 65495, -42, 4294967260, -37, 4294967258 },
+ { -39, 216, -41, 65494, -36, 4294967259, -38, 4294967257 },
+ { -40, 215, -42, 65500, -37, 4294967258, -39, 4294967256 },
+ { -41, 214, -36, 65499, -38, 4294967257, -40, 4294967255 },
+ { -42, 220, -37, 65498, -39, 4294967256, -41, 4294967254 },
+ { -36, 219, -38, 65497, -40, 4294967255, -42, 4294967260 },
+ };
- const QVariant x = a->property("x");
- QCOMPARE(x.metaType(), QMetaType::fromType<QQmlListReference>());
- const QQmlListReference ref = x.value<QQmlListReference>();
- QVERIFY(ref.isValid());
- QCOMPARE(ref.size(), 0);
+ const QList<qint64> stored
+ = {50, 51, 1332, 1333, 1334, 1335, 1336, 1337};
+ const QList<qint64> storedNegative
+ = {-50, 205, -1332, 64203, -1334, 4294965961, -1336, 4294965959};
+ const QList<QList<qint64>> storedShuffled = {
+ { -51, 204, -1333, 64202, -1335, 4294965960, -1337, 4294967245 },
+ { -52, 203, -1334, 64201, -1336, 4294965959, -51, 4294967244 },
+ { -53, 202, -1335, 64200, -1337, 4294967245, -52, 4294967243 },
+ { -54, 201, -1336, 64199, -51, 4294967244, -53, 4294967242 },
+ { -55, 200, -1337, 65485, -52, 4294967243, -54, 4294967241 },
+ { -56, 199, -51, 65484, -53, 4294967242, -55, 4294967240 },
+ { -57, 205, -52, 65483, -54, 4294967241, -56, 4294967239 },
+ { -51, 204, -53, 65482, -55, 4294967240, -57, 4294967245 },
+ };
+
+ QStringList asStrings(8);
+
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(zeroes));
+
+ QMetaObject::invokeMethod(o.data(), "writeValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(written));
+
+ QMetaObject::invokeMethod(o.data(), "negateValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(writtenNegative));
+
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(o.data(), "shuffleValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(writtenShuffled[i]));
+ }
+
+ QMetaObject::invokeMethod(o.data(), "storeValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(stored));
+
+ QMetaObject::invokeMethod(o.data(), "negateValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(storedNegative));
+
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(o.data(), "shuffleValues");
+ for (int i = 0; i < 8; ++i) {
+ QMetaObject::invokeMethod(
+ o.data(), "readValueAsString",
+ Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
+ }
+ QCOMPARE(asStrings, convertToStrings(storedShuffled[i]));
+ }
}
-void tst_QmlCppCodegen::typePropertyClash()
+void tst_QmlCppCodegen::objectInVar()
{
QQmlEngine engine;
- engine.rootContext()->setContextProperty(u"size"_s, 5);
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropertyClash.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectInVar.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"Size: 5"_s);
+ QVERIFY(o);
+ QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data());
+
+ bool result = false;
+ QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
+ QVERIFY(result);
+
+ o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr));
+ QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result)));
+ QVERIFY(!result);
+}
+
+void tst_QmlCppCodegen::objectLookupOnListElement()
+{
+ QQmlEngine engine;
+
+ const QUrl url(u"qrc:/qt/qml/TestTypes/objectLookupOnListElement.qml"_s);
+ QQmlComponent c1(&engine, url);
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+
+ QScopedPointer<QObject> object(c1.create());
+ QVERIFY(!object.isNull());
+
+ QList<int> zOrders;
+ QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders));
+ QCOMPARE(zOrders, (QList<int>{1, 0, 0}));
+ object->setProperty("current", 1);
+ QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders));
+ QCOMPARE(zOrders, (QList<int>{0, 1, 0}));
+
+ QMetaObject::invokeMethod(object.data(), "clearChildren");
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString()
+ + u":21: TypeError: Cannot read property 'z' of undefined"_s));
+ QMetaObject::invokeMethod(object.data(), "zOrders", Q_RETURN_ARG(QList<int>, zOrders));
+ QCOMPARE(zOrders, (QList<int>()));
}
void tst_QmlCppCodegen::objectToString()
@@ -2296,490 +3789,677 @@ void tst_QmlCppCodegen::objectToString()
QCOMPARE(o->property("no").toString(), u" no"_s); // throws, but that is ignored
}
-void tst_QmlCppCodegen::throwObjectName()
+void tst_QmlCppCodegen::objectWithStringListMethod()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/throwObjectName.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/objectWithStringListMethod.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
-
- QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/throwObjectName.qml:5:5: ouch");
+ QTest::ignoreMessage(QtDebugMsg, "2");
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QVERIFY(o->objectName().isEmpty());
}
-void tst_QmlCppCodegen::javaScriptArgument()
+void tst_QmlCppCodegen::onAssignment()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/javaScriptArgument.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/pressAndHoldButton.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ QCOMPARE(object->property("pressed").toBool(), false);
+ QCOMPARE(object->property("scale").toDouble(), 1.0);
- QCOMPARE(o->property("a").toDouble(), 4.0);
- QCOMPARE(o->property("b").toDouble(), 9.0);
- QCOMPARE(o->property("c").toString(), u"5t-1"_s);
- QCOMPARE(o->property("d").toString(), u"9"_s);
- QCOMPARE(o->property("e").toString(), u"10"_s);
- QCOMPARE(o->property("f").toString(), u"-10"_s);
+ object->metaObject()->invokeMethod(object.data(), "press");
+ QTRY_COMPARE(object->property("pressed").toBool(), true);
+ QCOMPARE(object->property("scale").toDouble(), 0.9);
- const QStringList scales {
- "0 ", "1 ", "10 ", "100 ", "1000 ", "9.77k", "97.7k", "977k", "9.54M", "95.4M", "954M",
- "9.31G", "93.1G", "931G", "9.09T", "-1 ", "-10 ", "-100 ", "-1000 ", "-9.77k", "-97.7k",
- "-977k", "-9.54M", "-95.4M", "-954M", "-9.31G", "-93.1G", "-931G", "-9.09T"
- };
+ object->metaObject()->invokeMethod(object.data(), "release");
+ QCOMPARE(object->property("pressed").toBool(), false);
+ QCOMPARE(object->property("scale").toDouble(), 1.0);
+}
- QCOMPARE(o->property("scales").value<QStringList>(), scales);
+void tst_QmlCppCodegen::optionalComparison()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/optionalComparison.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- double thing = 12.0;
- QString result;
- QMetaObject::invokeMethod(
- o.data(), "forwardArg", Q_RETURN_ARG(QString, result), Q_ARG(double, thing));
- QCOMPARE(result, u"12 ");
+ QCOMPARE(object->property("found").toInt(), 1);
+ QCOMPARE(object->property("foundStrict").toInt(), 1);
+ QCOMPARE(object->property("foundNot").toInt(), 2);
+ QCOMPARE(object->property("foundStrictNot").toInt(), 2);
+
+ // this === this, null === null (x4), undefined === undefined
+ QCOMPARE(object->property("undefinedEqualsUndefined").toInt(), 6);
+
+ QCOMPARE(object->property("optionalNull").toBool(), true);
+ object->setObjectName("foo"_L1);
+ QCOMPARE(object->property("optionalNull").toBool(), false);
}
-void tst_QmlCppCodegen::translation()
+void tst_QmlCppCodegen::outOfBoundsArray()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/translation.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/outOfBounds.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- QCOMPARE(o->property("translate2"), u"s"_s);
- QCOMPARE(o->property("translate3"), u"s"_s);
- QCOMPARE(o->property("translate4"), u"s"_s);
+ QTest::ignoreMessage(QtDebugMsg, "oob undefined");
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QVERIFY(object->metaObject()->indexOfProperty("oob") > 0);
+ QVERIFY(!object->property("oob").isValid());
+ const QVariant oob2 = object->property("oob2");
+ QCOMPARE(oob2.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(oob2.value<QObject *>(), nullptr);
+}
- QCOMPARE(o->property("translateNoop2"), u"s"_s);
- QCOMPARE(o->property("translateNoop3"), u"s"_s);
+void tst_QmlCppCodegen::overriddenProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/childobject.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QCOMPARE(o->property("tr1"), u"s"_s);
- QCOMPARE(o->property("tr2"), u"s"_s);
- QCOMPARE(o->property("tr3"), u"s"_s);
+ QObject *child = object->property("child").value<QObject *>();
+ QVERIFY(child);
- QCOMPARE(o->property("trNoop1"), u"s"_s);
- QCOMPARE(o->property("trNoop2"), u"s"_s);
+ QCOMPARE(object->objectName(), u"kraut"_s);
+ QCOMPARE(object->property("doneThing").toInt(), 5);
+ QCOMPARE(object->property("usingFinal").toInt(), 5);
- QCOMPARE(o->property("trId1"), u"s"_s);
- QCOMPARE(o->property("trId2"), u"s"_s);
+ auto checkAssignment = [&]() {
+ const QString newName = u"worscht"_s;
+ QMetaObject::invokeMethod(object.data(), "setChildObjectName", Q_ARG(QString, newName));
+ QCOMPARE(object->objectName(), newName);
+ };
+ checkAssignment();
- QCOMPARE(o->property("trIdNoop1"), u"s"_s);
+ QMetaObject::invokeMethod(child, "doString");
+ QCOMPARE(child->objectName(), u"string"_s);
+ QMetaObject::invokeMethod(child, "doNumber");
+ QCOMPARE(child->objectName(), u"double"_s);
+ QMetaObject::invokeMethod(child, "doArray");
+ QCOMPARE(child->objectName(), u"javaScript"_s);
+
+ QMetaObject::invokeMethod(child, "doString2");
+ QCOMPARE(child->objectName(), u"string"_s);
+ QMetaObject::invokeMethod(child, "doNumber2");
+ QCOMPARE(child->objectName(), u"double"_s);
+ QMetaObject::invokeMethod(child, "doArray2");
+ QCOMPARE(child->objectName(), u"javaScript"_s);
+
+ QMetaObject::invokeMethod(child, "doFoo");
+ QCOMPARE(child->objectName(), u"ObjectWithMethod"_s);
+
+ ObjectWithMethod *benign = new ObjectWithMethod(object.data());
+ benign->theThing = 10;
+ benign->setObjectName(u"cabbage"_s);
+ object->setProperty("child", QVariant::fromValue(benign));
+ QCOMPARE(object->objectName(), u"cabbage"_s);
+ checkAssignment();
+ QCOMPARE(object->property("doneThing").toInt(), 10);
+ QCOMPARE(object->property("usingFinal").toInt(), 10);
+
+ OverriddenObjectName *evil = new OverriddenObjectName(object.data());
+ QTest::ignoreMessage(QtWarningMsg,
+ "Final member fff is overridden in class OverriddenObjectName. "
+ "The override won't be used.");
+ object->setProperty("child", QVariant::fromValue(evil));
+
+ QCOMPARE(object->objectName(), u"borschtsch"_s);
+
+ checkAssignment();
+ QCOMPARE(object->property("doneThing").toInt(), 7);
+ QCOMPARE(object->property("usingFinal").toInt(), 5);
}
-void tst_QmlCppCodegen::stringArg()
+void tst_QmlCppCodegen::ownPropertiesNonShadowable()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringArg.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/overriddenMember.qml"_s));
- QCOMPARE(o->property("stringArg"), u"a foozly thing"_s);
- QCOMPARE(o->property("falseArg"), u"a 0 thing"_s);
- QCOMPARE(o->property("trueArg"), u"a 1 thing"_s);
- QCOMPARE(o->property("zeroArg"), u"a 0 thing"_s);
- QCOMPARE(o->property("intArg"), u"a 11 thing"_s);
- QCOMPARE(o->property("realArg"), u"a 12.25 thing"_s);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(rootObject->property("ppp").toInt(), 16);
+ QCOMPARE(rootObject->property("ppp2").toInt(), 9);
+ QCOMPARE(rootObject->property("ppp3").toInt(), 12);
}
-void tst_QmlCppCodegen::conversionDecrement()
+void tst_QmlCppCodegen::parentProperty()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/conversionDecrement.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/parentProp.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("c").toInt(), 11);
+ QCOMPARE(object->property("i").toInt(), 22);
+ object->setProperty("a", QVariant::fromValue(22));
+ QCOMPARE(object->property("c").toInt(), 28);
+ object->setProperty("implicitWidth", QVariant::fromValue(14));
+ QCOMPARE(object->property("i").toInt(), 26);
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QObject *child = qmlContext(object.data())->objectForName(u"child"_s);
+ QObject *sibling = qmlContext(object.data())->objectForName(u"sibling"_s);
+ QObject *evil = qmlContext(object.data())->objectForName(u"evil"_s);
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ child->setProperty("parent", QVariant::fromValue(sibling));
- QCOMPARE(o->property("currentPageIndex").toInt(), 0);
- o->setProperty("pages", 5);
- QCOMPARE(o->property("currentPageIndex").toInt(), 3);
- o->setProperty("pages", 4);
- QCOMPARE(o->property("currentPageIndex").toInt(), 0);
- o->setProperty("pages", 6);
- QCOMPARE(o->property("currentPageIndex").toInt(), 4);
- o->setProperty("pages", 60);
- QCOMPARE(o->property("currentPageIndex").toInt(), 3);
+ QCOMPARE(child->property("b").toInt(), 0);
+ QCOMPARE(child->property("i").toInt(), 28);
+ QCOMPARE(object->property("i").toInt(), 56);
+
+ child->setProperty("parent", QVariant::fromValue(evil));
+
+ QCOMPARE(child->property("b").toInt(), 5994);
+ QCOMPARE(object->property("c").toInt(), 5996);
+
+ QCOMPARE(child->property("i").toInt(), 443);
+ QCOMPARE(object->property("i").toInt(), 886);
+
+ {
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/specificParent.qml"_s));
+
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
+
+ QCOMPARE(rootObject->property("a").toReal(), 77.0);
+ }
}
-void tst_QmlCppCodegen::unstoredUndefined()
+void tst_QmlCppCodegen::popContextAfterRet()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unstoredUndefined.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/popContextAfterRet.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QCOMPARE(o->objectName(), u"NaN"_s);
+ QVERIFY(o);
+
+ QCOMPARE(o->objectName(), QString());
+ o->setProperty("stackViewDepth", 1);
+ QCOMPARE(o->objectName(), u"backgroundImage"_s);
+ o->setProperty("stackViewDepth", 2);
+ QCOMPARE(o->objectName(), u"backgroundBlur"_s);
+ o->setProperty("stackViewDepth", 1);
+ QCOMPARE(o->objectName(), u"backgroundImage"_s);
}
-void tst_QmlCppCodegen::registerPropagation()
+void tst_QmlCppCodegen::prefixedType()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/registerPropagation.qml"_s));
+
+ // We need to add an import path here because we cannot namespace the implicit import.
+ // The implicit import is what we use for all the other tests, even if we explicitly
+ // import TestTypes. That is because the TestTypes module is in a subdirectory "data".
+ engine.addImportPath(u":/"_s);
+
+ const QUrl document(u"qrc:/qt/qml/TestTypes/prefixedMetaType.qml"_s);
+ QQmlComponent c(&engine, document);
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- int result = 0;
- QMetaObject::invokeMethod(o.data(), "test", Q_RETURN_ARG(int, result));
- QCOMPARE(result, 1);
+ QCOMPARE(o->property("state").toInt(), 2);
+ QVERIFY(qvariant_cast<QObject *>(o->property("a")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("b")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("c")) == nullptr);
- QString undefined;
- QMetaObject::invokeMethod(o.data(), "produceUndefined1", Q_RETURN_ARG(QString, undefined));
- QCOMPARE(undefined, u"undefined"_s);
+ QVERIFY(qvariant_cast<QObject *>(o->property("d")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("e")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("f")) == nullptr);
- undefined.clear();
- QMetaObject::invokeMethod(o.data(), "produceUndefined2", Q_RETURN_ARG(QString, undefined));
- QCOMPARE(undefined, u"undefined"_s);
+ QVERIFY(qvariant_cast<QObject *>(o->property("g")) != nullptr);
+ QVERIFY(qvariant_cast<QObject *>(o->property("h")) != nullptr);
+
+ QCOMPARE(o->property("countG").toInt(), 11);
+ QCOMPARE(o->property("countH").toInt(), 11);
}
-void tst_QmlCppCodegen::argumentConversion()
+void tst_QmlCppCodegen::propertyOfParent()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/argumentConversion.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/RootWithoutId.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
- auto checkNaN = [&](const char *propName) {
- const QVariant prop = o->property(propName);
- QCOMPARE(prop.metaType(), QMetaType::fromType<double>());
- QVERIFY(qIsNaN(prop.toDouble()));
- };
+ QObject *child = qmlContext(object.data())->objectForName(u"item"_s);
- checkNaN("a");
- checkNaN("b");
- checkNaN("e");
+ bool expected = false;
- QCOMPARE(o->property("c").toDouble(), 3.0);
- QCOMPARE(o->property("d").toDouble(), -1.0);
- QCOMPARE(o->property("f").toDouble(), 10.0);
+ for (int i = 0; i < 3; ++i) {
+ const QVariant foo = object->property("foo");
+ QCOMPARE(foo.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(foo.toBool(), expected);
+
+ const QVariant bar = object->property("bar");
+ QCOMPARE(bar.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(bar.toBool(), expected);
+
+ const QVariant visible = child->property("visible");
+ QCOMPARE(visible.metaType(), QMetaType::fromType<bool>());
+ QCOMPARE(visible.toBool(), expected);
+
+ expected = !expected;
+ object->setProperty("foo", expected);
+ }
}
-void tst_QmlCppCodegen::badSequence()
+void tst_QmlCppCodegen::reduceWithNullThis()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/badSequence.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/reduceWithNullThis.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- Person *self = qobject_cast<Person *>(o.data());
- QVERIFY(self);
- QVERIFY(self->barzles().isEmpty());
- QVERIFY(self->cousins().isEmpty());
+ QCOMPARE(object->property("preferredHeight").toDouble(), 28.0);
+ QCOMPARE(object->property("preferredHeight2").toDouble(), 28.0);
+}
- Person *other = o->property("other").value<Person *>();
- QVERIFY(other);
+void tst_QmlCppCodegen::readEnumFromInstance()
+{
+ QQmlEngine engine;
- QVERIFY(other->barzles().isEmpty());
- QVERIFY(other->cousins().isEmpty());
+ const QString url = u"qrc:/qt/qml/TestTypes/readEnumFromInstance.qml"_s;
- Barzle f1;
- Barzle f2;
- const QList<Barzle *> barzles { &f1, &f2 };
- const QList<Person *> cousins { self, other };
+ QQmlComponent component(&engine, QUrl(url));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
- other->setBarzles(barzles);
- QCOMPARE(self->barzles(), barzles);
- QCOMPARE(self->property("l").toInt(), 2);
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + ":7:5: Unable to assign [undefined] to int"_L1));
- other->setCousins(cousins);
- QCOMPARE(self->cousins(), cousins);
- QCOMPARE(self->property("m").toInt(), 2);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QQmlListProperty<Person> others
- = self->property("others").value<QQmlListProperty<Person>>();
- QCOMPARE(others.count(&others), 2);
- QCOMPARE(others.at(&others, 0), cousins[0]);
- QCOMPARE(others.at(&others, 1), cousins[1]);
+ QCOMPARE(object->property("priority"), QVariant::fromValue<int>(0));
+ QCOMPARE(object->property("prop2"), QVariant::fromValue<int>(1));
+ QCOMPARE(object->property("priorityIsVeryHigh"), QVariant::fromValue<bool>(false));
- QQmlListProperty<Person> momsCousins
- = self->property("momsCousins").value<QQmlListProperty<Person>>();
- QCOMPARE(momsCousins.count(&momsCousins), 2);
- QCOMPARE(momsCousins.at(&momsCousins, 0), cousins[0]);
- QCOMPARE(momsCousins.at(&momsCousins, 1), cousins[1]);
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + ":13: Error: Cannot assign [undefined] to int"_L1));
- QQmlListProperty<Person> dadsCousins
- = self->property("dadsCousins").value<QQmlListProperty<Person>>();
- QCOMPARE(dadsCousins.count(&dadsCousins), 1);
- QCOMPARE(dadsCousins.at(&dadsCousins, 0), other);
+ int result = 0;
+ QMetaObject::invokeMethod(object.data(), "cyclePriority", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 0);
}
-void tst_QmlCppCodegen::enumLookup()
+void tst_QmlCppCodegen::readonlyListProperty()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumLookup.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/readonlyListProperty.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- QCOMPARE(o->property("ready").toBool(), true);
+ QCOMPARE(object->property("l").toInt(), 4);
}
-void tst_QmlCppCodegen::trivialSignalHandler()
+void tst_QmlCppCodegen::registerElimination()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/trivialSignalHandler.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
-
- QCOMPARE(o->property("a").toString(), u"no"_s);
- QCOMPARE(o->property("b").toInt(), -1);
- QCOMPARE(o->property("b").toDouble(), -1.0);
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/registerelimination.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- o->setObjectName(u"yes"_s);
- QCOMPARE(o->property("a").toString(), u"yes"_s);
- QCOMPARE(o->property("b").toInt(), 5);
- QCOMPARE(o->property("c").toDouble(), 2.5);
+ // Increment of 23 hits both 0 and 460
+ for (int input = -23; input < 700; input += 23) {
+ object->setProperty("input", input);
+ if (input <= 0 || input >= 460)
+ QCOMPARE(object->property("output").toInt(), 459);
+ else
+ QCOMPARE(object->property("output").toInt(), input);
+ }
}
-void tst_QmlCppCodegen::stringToByteArray()
+void tst_QmlCppCodegen::registerPropagation()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringToByteArray.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/registerPropagation.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- Person *person = qobject_cast<Person *>(o.data());
- QVERIFY(person);
+ int result = 0;
+ QMetaObject::invokeMethod(o.data(), "test", Q_RETURN_ARG(int, result));
+ QCOMPARE(result, 1);
- QCOMPARE(person->dataBindable().value(), QByteArray("some data"));
- QCOMPARE(person->name(), u"some data"_s);
+ QString undefined;
+ QMetaObject::invokeMethod(o.data(), "produceUndefined1", Q_RETURN_ARG(QString, undefined));
+ QCOMPARE(undefined, u"undefined"_s);
+
+ undefined.clear();
+ QMetaObject::invokeMethod(o.data(), "produceUndefined2", Q_RETURN_ARG(QString, undefined));
+ QCOMPARE(undefined, u"undefined"_s);
}
-void tst_QmlCppCodegen::listPropertyAsModel()
+void tst_QmlCppCodegen::renameAdjust()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listPropertyAsModel.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/renameAdjust.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QQmlListReference children(o.data(), "children");
- QCOMPARE(children.count(), 5);
+ QTest::ignoreMessage(QtDebugMsg, "success");
+ QTest::ignoreMessage(QtCriticalMsg, "failed 10 11");
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
}
-void tst_QmlCppCodegen::notNotString()
+void tst_QmlCppCodegen::resettableProperty()
{
+ QFETCH(QString, url);
+
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/notNotString.qml"_s));
+ QQmlComponent c(&engine, QUrl(url));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + u":10:5: Unable to assign [undefined] to double"_s));
+
QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QCOMPARE(o->property("notNotString").value<bool>(), false);
- o->setObjectName(u"a"_s);
- QCOMPARE(o->property("notNotString").value<bool>(), true);
-}
+ QCOMPARE(o->property("value").toDouble(), 999);
+ QMetaObject::invokeMethod(o.data(), "doReset");
+ QCOMPARE(o->property("value").toDouble(), 0);
-template<typename T>
-QString toOperand(double arg);
+ o->setProperty("value", double(82));
+ QCOMPARE(o->property("value").toDouble(), 82);
+ QMetaObject::invokeMethod(o.data(), "doReset2");
+ QCOMPARE(o->property("value").toDouble(), 0);
-template<>
-QString toOperand<double>(double arg)
-{
- if (qIsNull(arg))
- return std::signbit(arg) ? QStringLiteral("(-0)") : QStringLiteral("(0)");
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + u":18: Error: Cannot assign [undefined] to double"_s));
+ QCOMPARE(o->property("notResettable").toDouble(), 10);
+ QMetaObject::invokeMethod(o.data(), "doNotReset");
+ QCOMPARE(o->property("notResettable").toDouble(), 10);
+ QCOMPARE(o->property("notResettable2").toDouble(), 0); // not NaN
- return u'(' + QJSPrimitiveValue(arg).toString() + u')';
+ o->setObjectName(u"namename"_s);
+ QTest::ignoreMessage(
+ QtWarningMsg, qPrintable(url + u":22: Error: Cannot assign [undefined] to QString"_s));
+ QMetaObject::invokeMethod(o.data(), "aaa");
+ QCOMPARE(o->objectName(), u"namename"_s);
}
-template<>
-QString toOperand<int>(double arg)
+void tst_QmlCppCodegen::resettableProperty_data()
{
- const int iArg = QJSPrimitiveValue(arg).toInteger();
- return u'(' + QJSPrimitiveValue(iArg).toString() + u')';
+ QTest::addColumn<QString>("url");
+ QTest::addRow("object lookups") << u"qrc:/qt/qml/TestTypes/resettable.qml"_s;
+ QTest::addRow("fallback lookups") << u"qrc:/qt/qml/TestTypes/fallbackresettable.qml"_s;
}
-template<>
-QString toOperand<bool>(double arg)
+void tst_QmlCppCodegen::returnAfterReject()
{
- const bool bArg = QJSPrimitiveValue(arg).toBoolean();
- return u'(' + QJSPrimitiveValue(bArg).toString() + u')';
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/returnAfterReject.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("bar").toInt(), 123);
}
-template<typename T1, typename T2>
-double jsEval(double arg1, double arg2, const QString &op, QJSEngine *engine)
+void tst_QmlCppCodegen::revisions()
{
- auto evalBinary = [&](const QString &jsOp) {
- return engine->evaluate(toOperand<T1>(arg1) + jsOp + toOperand<T2>(arg2)).toNumber();
- };
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/revisions.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- auto evalBinaryConst = [&](const QString &jsOp) {
- return engine->evaluate(toOperand<T1>(arg1) + jsOp + u'9').toNumber();
- };
+ QCOMPARE(o->property("delayed").toBool(), true);
+ QCOMPARE(o->property("gotten").toInt(), 5);
+}
- auto evalUnary = [&](const QString &jsOp) {
- return engine->evaluate(jsOp + toOperand<T1>(arg1)).toNumber();
- };
+void tst_QmlCppCodegen::scopeIdLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeIdLookup.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("objectName").toString(), u"outer"_s);
+}
- auto evalInPlace = [&](const QString &jsOp) {
- return engine->evaluate(
- u"(function() {var a = "_s + toOperand<T1>(arg1)+ u"; return "_s
- + jsOp + u"a;})()"_s).toNumber();
- };
+void tst_QmlCppCodegen::scopeObjectDestruction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/fileDialog.qml"_s));
- if (op == u"unot")
- return evalUnary(u"!"_s);
- if (op == u"uplus")
- return evalUnary(u"+"_s);
- if (op == u"uminus")
- return evalUnary(u"-"_s);
- if (op == u"ucompl")
- return evalUnary(u"~"_s);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- if (op == u"increment")
- return evalInPlace(u"++"_s);
- if (op == u"decrement")
- return evalInPlace(u"--"_s);
+ QObject *dialog = rootObject->property("dialog").value<QObject *>();
+ QVERIFY(dialog);
- if (op == u"add")
- return evalBinary(u"+"_s);
- if (op == u"sub")
- return evalBinary(u"-"_s);
- if (op == u"mul")
- return evalBinary(u"*"_s);
- if (op == u"div")
- return evalBinary(u"/"_s);
- if (op == u"exp")
- return evalBinary(u"**"_s);
- if (op == u"mod")
- return evalBinary(u"%"_s);
+ // We cannot check the warning messages. The AOT compiled code complains about reading the
+ // "parent" property of an object scheduled for deletion. The runtime silently returns undefined
+ // at that point and then complains about not being able to read a property on undefined.
- if (op == u"bitAnd")
- return evalBinary(u"&"_s);
- if (op == u"bitOr")
- return evalBinary(u"|"_s);
- if (op == u"bitXor")
- return evalBinary(u"^"_s);
+ // Doesn't crash, even though it triggers bindings on scope objects scheduled for deletion.
+ QMetaObject::invokeMethod(dialog, "open");
+}
- if (op == u"bitAndConst")
- return evalBinaryConst(u"&"_s);
- if (op == u"bitOrConst")
- return evalBinaryConst(u"|"_s);
- if (op == u"bitXorConst")
- return evalBinaryConst(u"^"_s);
+void tst_QmlCppCodegen::scopeVsObject()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeVsObject.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("objectName").toString(), u"foobar"_s);
+}
- if (op == u"ushr")
- return evalBinary(u">>>"_s);
- if (op == u"shr")
- return evalBinary(u">>"_s);
- if (op == u"shl")
- return evalBinary(u"<<"_s);
+void tst_QmlCppCodegen::scopedEnum()
+{
+ QQmlEngine engine;
+ const QString url = u"qrc:/qt/qml/TestTypes/scopedEnum.qml"_s;
+ QQmlComponent component(&engine, QUrl(url));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
- if (op == u"ushrConst")
- return evalBinaryConst(u">>>"_s);
- if (op == u"shrConst")
- return evalBinaryConst(u">>"_s);
- if (op == u"shlConst")
- return evalBinaryConst(u"<<"_s);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url + u":6:5: Unable to assign [undefined] to int"_s));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url + u":8: TypeError: Cannot read property 'C' of undefined"_s));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url + u":14: TypeError: Cannot read property 'C' of undefined"_s));
- qDebug() << op;
- Q_UNREACHABLE_RETURN(0);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("good").toInt(), 27);
+ QCOMPARE(object->property("bad").toInt(), 0);
+ QCOMPARE(object->property("wrong").toInt(), 0);
+ QCOMPARE(object->property("right").toInt(), 7);
+
+ QCOMPARE(object->property("notgood").toInt(), 26);
+ QCOMPARE(object->property("notbad").toInt(), 26);
+ QCOMPARE(object->property("notwrong").toInt(), 0);
+ QCOMPARE(object->property("notright").toInt(), 6);
+
+ QCOMPARE(object->property("passable").toInt(), 2);
+ QCOMPARE(object->property("wild").toInt(), 1);
}
-void tst_QmlCppCodegen::mathOperations()
+void tst_QmlCppCodegen::sequenceToIterable()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/mathOperations.qml"_s));
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/sequenceToIterable.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("c").toInt(), 11);
+
+ QQmlListReference children(object.data(), "children");
+ QCOMPARE(children.count(), 11);
+ static const QRegularExpression name("Entry\\(0x[0-9a-f]+, \"Item ([0-9])\"\\): ([0-9])");
+ for (int i = 0; i < 10; ++i) {
+ const auto match = name.match(children.at(i)->objectName());
+ QVERIFY(match.hasMatch());
+ QCOMPARE(match.captured(1), QString::number(i));
+ }
+}
+
+void tst_QmlCppCodegen::setLookupConversion()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/setLookupConversion.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(o->objectName().isEmpty());
+ QMetaObject::invokeMethod(o.data(), "t");
+ QCOMPARE(o->objectName(), u"a"_s);
+ QCOMPARE(o->property("value").toInt(), 9);
+}
- const QMetaObject *metaObject = o->metaObject();
+void tst_QmlCppCodegen::setLookupOriginalScope()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/setLookupOriginalScope.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- char t1;
- char t2;
- QString name;
- const auto guard = qScopeGuard([&]() {
- if (QTest::currentTestFailed()) {
- qDebug() << t1 << t2 << name << "failed on:";
- qDebug() << "doubles" << o->property("a").toDouble() << o->property("b").toDouble();
- qDebug() << "integers" << o->property("ia").toInt() << o->property("ib").toInt();
- qDebug() << "booleans" << o->property("ba").toBool() << o->property("bb").toBool();
- }
- });
+ QObject *v = o->property("variable").value<QObject *>();
+ QCOMPARE(v->property("value").toInt(), 0);
- for (double a : numbers) {
- for (double b : numbers) {
- o->setProperty("a", a);
- o->setProperty("b", b);
- for (int i = 0, end = metaObject->propertyCount(); i != end; ++i) {
- const QMetaProperty prop = metaObject->property(i);
- const QByteArray propName = prop.name();
+ QMetaObject::invokeMethod(o.data(), "trigger");
+ QObject *edit = o->property("edit").value<QObject *>();
+ QVERIFY(edit);
+ QCOMPARE(edit->property("myOutput").value<QObject *>(), v);
+ QCOMPARE(v->property("value").toInt(), 55);
+}
- if (propName.size() < 3 || propName == "objectName")
- continue;
+void tst_QmlCppCodegen::shadowedAsCasts()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedAsCasts.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
+
+ QObject *shadowed1 = obj->property("shadowed1").value<QObject *>();
+ QVERIFY(shadowed1);
+ QVERIFY(shadowed1->objectName().isEmpty());
+ const QVariant name1 = shadowed1->property("objectName");
+ QCOMPARE(name1.metaType(), QMetaType::fromType<int>());
+ QCOMPARE(name1.toInt(), 43);
+
+ QObject *shadowed2 = obj->property("shadowed2").value<QObject *>();
+ QVERIFY(shadowed2);
+ QVERIFY(shadowed2->objectName().isEmpty());
+ const QVariant name2 = shadowed2->property("objectName");
+ QCOMPARE(name2.metaType(), QMetaType::fromType<int>());
+ QCOMPARE(name2.toInt(), 42);
+
+ QObject *shadowed3 = obj->property("shadowed3").value<QObject *>();
+ QVERIFY(shadowed3);
+ QVERIFY(shadowed3->objectName().isEmpty());
+ const QVariant name3 = shadowed3->property("objectName");
+ QCOMPARE(name3.metaType(), QMetaType::fromType<double>());
+ QCOMPARE(name3.toDouble(), 41.0);
+}
+
+void tst_QmlCppCodegen::shadowedMethod()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedMethod.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("athing"), QVariant::fromValue<bool>(false));
+ QCOMPARE(o->property("bthing"), QVariant::fromValue(u"b"_s));
+ QCOMPARE(o->property("cthing"), QVariant::fromValue(u"c"_s));
+}
- t1 = propName[0];
- t2 = propName[1];
- name = QString::fromUtf8(propName.mid(2));
+void tst_QmlCppCodegen::shadowedPrimitiveCmpEqNull()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedPrimitiveCmpEqNull.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
- double expected;
+void tst_QmlCppCodegen::shifts()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/shifts.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
- switch (t2) {
- case 'd':
- case '_':
- switch (t1) {
- case 'd':
- expected = jsEval<double, double>(a, b, name, &engine);
- break;
- case 'i':
- expected = jsEval<int, double>(a, b, name, &engine);
- break;
- case 'b':
- expected = jsEval<bool, double>(a, b, name, &engine);
- break;
- }
- break;
- case 'i':
- switch (t1) {
- case 'd':
- expected = jsEval<double, int>(a, b, name, &engine);
- break;
- case 'i':
- expected = jsEval<int, int>(a, b, name, &engine);
- break;
- case 'b':
- expected = jsEval<bool, int>(a, b, name, &engine);
- break;
- }
- break;
- case 'b':
- switch (t1) {
- case 'd':
- expected = jsEval<double, bool>(a, b, name, &engine);
- break;
- case 'i':
- expected = jsEval<int, bool>(a, b, name, &engine);
- break;
- case 'b':
- expected = jsEval<bool, bool>(a, b, name, &engine);
- break;
- }
- break;
- }
+ QCOMPARE(object->property("a").toInt(), 9728);
+ QCOMPARE(object->property("b").toInt(), 4864);
+ QCOMPARE(object->property("c").toInt(), 19448);
+ QCOMPARE(object->property("d").toInt(), 9731);
+ QCOMPARE(object->property("e").toInt(), 0);
+}
- const double result = prop.read(o.data()).toDouble();
- QCOMPARE(result, expected);
- }
- }
- }
+void tst_QmlCppCodegen::signalHandler()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signal.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->objectName(), QString());
+ QCOMPARE(object->property("ff").toInt(), 4);
+
+ object->setObjectName(u"foo"_s);
+ QCOMPARE(object->property("ff").toInt(), 12);
}
-void tst_QmlCppCodegen::inaccessibleProperty()
+void tst_QmlCppCodegen::signalIndexMismatch()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/versionmismatch.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
+ QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalIndexMismatch.qml"_s));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QCOMPARE(o->property("c").toInt(), 5);
+ QScopedPointer<QObject> item(c1.create());
+ const auto visualIndexBeforeMoveList = item->property("visualIndexBeforeMove").toList();
+ const auto visualIndexAfterMoveList = item->property("visualIndexAfterMove").toList();
+
+ QCOMPARE(visualIndexBeforeMoveList, QList<QVariant>({ 0, 1, 2 }));
+ QCOMPARE(visualIndexAfterMoveList, QList<QVariant>({ 0, 1, 2 }));
}
-void tst_QmlCppCodegen::typePropagationLoop()
+void tst_QmlCppCodegen::signalsWithLists()
{
QQmlEngine engine;
-
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropagationLoop.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalsWithLists.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QCOMPARE(o->property("j").toInt(), 3);
+ QVariantList varlist = o->property("varlist").toList();
+ QCOMPARE(varlist.size(), 5);
+ QCOMPARE(varlist[0], QVariant::fromValue(1));
+ QCOMPARE(varlist[1], QVariant::fromValue(u"foo"_s));
+ QCOMPARE(varlist[2], QVariant::fromValue(o.data()));
+ QCOMPARE(varlist[3], QVariant());
+ QCOMPARE(varlist[4], QVariant::fromValue(true));
+
+ QQmlListProperty<QObject> objlist = o->property("objlist").value<QQmlListProperty<QObject>>();
+ QCOMPARE(objlist.count(&objlist), 3);
+ QCOMPARE(objlist.at(&objlist, 0), o.data());
+ QCOMPARE(objlist.at(&objlist, 1), nullptr);
+ QCOMPARE(objlist.at(&objlist, 2), o.data());
+
+ QCOMPARE(o->property("happening").toInt(), 0);
+ o->metaObject()->invokeMethod(o.data(), "sendSignals");
+ QCOMPARE(o->property("happening").toInt(), 8);
}
void tst_QmlCppCodegen::signatureIgnored()
@@ -2795,420 +4475,636 @@ void tst_QmlCppCodegen::signatureIgnored()
QCOMPARE(ignored->property("n").toInt(), 67);
}
-void tst_QmlCppCodegen::listAsArgument()
+void tst_QmlCppCodegen::simpleBinding()
{
QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/Test.qml"_s));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY2(!object.isNull(), component.errorString().toUtf8().constData());
+ QCOMPARE(object->property("foo").toInt(), int(3));
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/listAsArgument.qml"_s));
+ {
+ CppBaseClass *base = qobject_cast<CppBaseClass *>(object.data());
+ Q_ASSERT(base);
+ QVERIFY(!base->cppProp.hasBinding());
+ QCOMPARE(base->cppProp.value(), 7);
+ QVERIFY(base->cppProp2.hasBinding());
+ QCOMPARE(base->cppProp2.value(), 14);
+ base->cppProp.setValue(9);
+ QCOMPARE(base->cppProp.value(), 9);
+ QCOMPARE(base->cppProp2.value(), 18);
+ }
+}
+
+void tst_QmlCppCodegen::storeElementSideEffects()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/storeElementSideEffects.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QCOMPARE(o->property("i").toInt(), 4);
- QCOMPARE(o->property("j").toInt(), 2);
- QCOMPARE(o->property("i1").toInt(), 2);
- QCOMPARE(o->property("i2").toInt(), 4);
- QCOMPARE(o->property("d").value<QObject *>()->objectName(), u"this one"_s);
+ QVERIFY(o);
- int singleInt = 0;
- QList<int> moreInts;
- QMetaObject::invokeMethod(o.data(), "returnInts1", Q_RETURN_ARG(QList<int>, moreInts));
- QCOMPARE(moreInts, QList<int>({5, 4, 3, 2, 1}));
- QMetaObject::invokeMethod(o.data(), "selectSecondInt", Q_RETURN_ARG(int, singleInt), Q_ARG(QList<int>, moreInts));
- QCOMPARE(singleInt, 4);
+ const QJSValue prop = o->property("myItem").value<QJSValue>();
+ QVERIFY(prop.isArray());
+ QCOMPARE(prop.property(0).toInt(), 10);
}
-void tst_QmlCppCodegen::letAndConst()
+void tst_QmlCppCodegen::storeMetaEnum()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/letAndConst.qml"_s));
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/StoreMetaEnum.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"ab"_s);
+ QVERIFY(o);
+
+ QCOMPARE(o->property("bar").toInt(), 0);
+ QCOMPARE(o->property("baz").toInt(), 1);
}
-void tst_QmlCppCodegen::signalIndexMismatch()
+void tst_QmlCppCodegen::stringArg()
{
QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringArg.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/signalIndexMismatch.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
-
- QScopedPointer<QObject> item(c1.create());
- const auto visualIndexBeforeMoveList = item->property("visualIndexBeforeMove").toList();
- const auto visualIndexAfterMoveList = item->property("visualIndexAfterMove").toList();
+ QCOMPARE(o->property("stringArg"), u"a foozly thing"_s);
+ QCOMPARE(o->property("falseArg"), u"a 0 thing"_s);
+ QCOMPARE(o->property("trueArg"), u"a 1 thing"_s);
+ QCOMPARE(o->property("zeroArg"), u"a 0 thing"_s);
+ QCOMPARE(o->property("intArg"), u"a 11 thing"_s);
+ QCOMPARE(o->property("realArg"), u"a 12.25 thing"_s);
+}
- QCOMPARE(visualIndexBeforeMoveList, QList<QVariant>({ 0, 1, 2 }));
- QCOMPARE(visualIndexAfterMoveList, QList<QVariant>({ 0, 1, 2 }));
+void tst_QmlCppCodegen::stringLength()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringLength.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("stringLength").toInt(), 8);
}
-void tst_QmlCppCodegen::callWithSpread()
+void tst_QmlCppCodegen::stringToByteArray()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/callWithSpread.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/stringToByteArray.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QTest::ignoreMessage(QtCriticalMsg, "That is great!");
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+
+ Person *person = qobject_cast<Person *>(o.data());
+ QVERIFY(person);
+
+ QCOMPARE(person->dataBindable().value(), QByteArray("some data"));
+ QCOMPARE(person->name(), u"some data"_s);
}
-void tst_QmlCppCodegen::nullComparison()
+void tst_QmlCppCodegen::structuredValueType()
{
QQmlEngine engine;
-
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/nullComparison.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/structuredValueType.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QCOMPARE(o->property("v").toInt(), 1);
- QCOMPARE(o->property("w").toInt(), 3);
- QCOMPARE(o->property("x").toInt(), 1);
- QCOMPARE(o->property("y").toInt(), 5);
+ QCOMPARE(o->property("r").value<QRectF>(), QRectF(1, 2, 3, 4));
+ QCOMPARE(o->property("r2").value<QRectF>(), QRectF(42, 0, 0, 0));
+
+ WeatherModelUrl w;
+ w.setStrings(QStringList({"one", "two", "three"}));
+
+ QCOMPARE(o->property("w").value<WeatherModelUrl>(), w);
}
-void tst_QmlCppCodegen::consoleObject()
+void tst_QmlCppCodegen::testIsnan()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/consoleObject.qml"_s));
+ const QUrl document(u"qrc:/qt/qml/TestTypes/isnan.qml"_s);
+ QQmlComponent c(&engine, document);
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- QTest::ignoreMessage(QtDebugMsg, "b 4.55");
- QTest::ignoreMessage(QtDebugMsg, "b 4.55");
- QTest::ignoreMessage(QtInfoMsg, "b 4.55");
- QTest::ignoreMessage(QtWarningMsg, "b 4.55");
- QTest::ignoreMessage(QtCriticalMsg, "b 4.55");
-
- // Unfortunately we cannot check the logging category with QTest::ignoreMessage
- QTest::ignoreMessage(QtDebugMsg, "b 4.55");
- QTest::ignoreMessage(QtDebugMsg, "b 4.55");
- QTest::ignoreMessage(QtInfoMsg, "b 4.55");
- QTest::ignoreMessage(QtWarningMsg, "b 4.55");
- QTest::ignoreMessage(QtCriticalMsg, "b 4.55");
-
- const QRegularExpression re(u"QQmlComponentAttached\\(0x[0-9a-f]+\\) b 4\\.55"_s);
- QTest::ignoreMessage(QtDebugMsg, re);
- QTest::ignoreMessage(QtDebugMsg, re);
- QTest::ignoreMessage(QtInfoMsg, re);
- QTest::ignoreMessage(QtWarningMsg, re);
- QTest::ignoreMessage(QtCriticalMsg, re);
+ QCOMPARE(o->property("good").toDouble(), 10.1);
+ QVERIFY(qIsNaN(o->property("bad").toDouble()));
- QTest::ignoreMessage(QtDebugMsg, "a undefined b false null 7");
- QTest::ignoreMessage(QtDebugMsg, "");
- QTest::ignoreMessage(QtDebugMsg, "4");
- QTest::ignoreMessage(QtDebugMsg, "");
+ const QVariant a = o->property("a");
+ QCOMPARE(a.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(!a.toBool());
- const QRegularExpression re2(u"QQmlComponentAttached\\(0x[0-9a-f]+\\)"_s);
- QTest::ignoreMessage(QtDebugMsg, re2);
+ const QVariant b = o->property("b");
+ QCOMPARE(b.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(b.toBool());
+}
+void tst_QmlCppCodegen::thisObject()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/thisObject.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
+ QCOMPARE(o->property("warned").value<QObject *>(), o.data());
}
-void tst_QmlCppCodegen::multiForeign()
+void tst_QmlCppCodegen::throwObjectName()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multiforeign.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/throwObjectName.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/qt/qml/TestTypes/throwObjectName.qml:5:5: ouch");
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"not here and not there"_s);
+ QVERIFY(o->objectName().isEmpty());
}
-void tst_QmlCppCodegen::namespaceWithEnum()
+void tst_QmlCppCodegen::topLevelComponent()
{
- QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/namespaceWithEnum.qml"_s));
+ // TODO: Once we stop accepting top level Component elements, this test can be removed.
+
+ QQmlEngine e;
+
+ const QUrl url(u"qrc:/qt/qml/TestTypes/topLevelComponent.qml"_s);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + u":4:1: Using a Component as the root of a QML document "
+ "is deprecated: types defined in qml documents are "
+ "automatically wrapped into Components when needed."_s));
+
+ QQmlComponent c(&e, url);
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QCOMPARE(o->property("i").toInt(), 2);
+
+ QQmlComponent *inner = qobject_cast<QQmlComponent *>(o.data());
+ QVERIFY(inner);
+
+ QScopedPointer<QObject> o2(inner->create());
+ QCOMPARE(o2->objectName(), u"foo"_s);
}
-void tst_QmlCppCodegen::enumProblems()
+void tst_QmlCppCodegen::translation()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumProblems.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/translation.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> outer(c.create());
- QVERIFY(!outer.isNull());
- QObject *inner = outer->property("o").value<QObject *>();
- QVERIFY(inner);
+ QScopedPointer<QObject> o(c.create());
- Foo *bar = inner->property("bar").value<Foo *>();
- QVERIFY(bar);
- QCOMPARE(bar->type(), Foo::Component);
+ QCOMPARE(o->property("translate2"), u"s"_s);
+ QCOMPARE(o->property("translate3"), u"s"_s);
+ QCOMPARE(o->property("translate4"), u"s"_s);
- Foo *fighter = inner->property("fighter").value<Foo *>();
- QVERIFY(fighter);
- QCOMPARE(fighter->type(), Foo::Fighter);
+ QCOMPARE(o->property("translateNoop2"), u"s"_s);
+ QCOMPARE(o->property("translateNoop3"), u"s"_s);
+
+ QCOMPARE(o->property("tr1"), u"s"_s);
+ QCOMPARE(o->property("tr2"), u"s"_s);
+ QCOMPARE(o->property("tr3"), u"s"_s);
+
+ QCOMPARE(o->property("trNoop1"), u"s"_s);
+ QCOMPARE(o->property("trNoop2"), u"s"_s);
+
+ QCOMPARE(o->property("trId1"), u"s"_s);
+ QCOMPARE(o->property("trId2"), u"s"_s);
+
+ QCOMPARE(o->property("trIdNoop1"), u"s"_s);
}
-void tst_QmlCppCodegen::enumConversion()
+void tst_QmlCppCodegen::trigraphs()
{
- QQmlEngine engine;
-
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enumConversion.qml"_s));
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/trigraphs.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
-
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- QCOMPARE(o->property("test").toInt(), 0x04);
- QCOMPARE(o->property("test_1").toBool(), true);
+ QCOMPARE(o->objectName(), u"?""?= ?""?/ ?""?' ?""?( ?""?) ?""?! ?""?< ?""?> ?""?-"_s);
}
-void tst_QmlCppCodegen::storeElementSideEffects()
+void tst_QmlCppCodegen::trivialSignalHandler()
{
QQmlEngine engine;
-
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/storeElementSideEffects.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/trivialSignalHandler.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
-
QScopedPointer<QObject> o(c.create());
- QVERIFY(o);
- const QJSValue prop = o->property("myItem").value<QJSValue>();
- QVERIFY(prop.isArray());
- QCOMPARE(prop.property(0).toInt(), 10);
-};
+ QCOMPARE(o->property("a").toString(), u"no"_s);
+ QCOMPARE(o->property("b").toInt(), -1);
+ QCOMPARE(o->property("b").toDouble(), -1.0);
-void tst_QmlCppCodegen::ambiguousSignals()
+ o->setObjectName(u"yes"_s);
+ QCOMPARE(o->property("a").toString(), u"yes"_s);
+ QCOMPARE(o->property("b").toInt(), 5);
+ QCOMPARE(o->property("c").toDouble(), 2.5);
+}
+
+void tst_QmlCppCodegen::typePropagationLoop()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ambiguousSignals.qml"_s));
+
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropagationLoop.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"tomorrow"_s);
- Person *p = qobject_cast<Person *>(o.data());
- QVERIFY(p);
- emit p->ambiguous(12);
- QCOMPARE(o->objectName(), u"12foo"_s);
- emit p->ambiguous();
- QCOMPARE(o->objectName(), u"9foo"_s);
+
+ QCOMPARE(o->property("j").toInt(), 3);
}
-void tst_QmlCppCodegen::fileImportsContainCxxTypes()
+void tst_QmlCppCodegen::typePropertyClash()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/usingCxxTypesFromFileImports.qml"_s));
+ engine.rootContext()->setContextProperty(u"size"_s, 5);
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/typePropertyClash.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
- QCOMPARE(o->objectName(), u"horst guenther"_s);
+ QCOMPARE(o->objectName(), u"Size: 5"_s);
}
-void tst_QmlCppCodegen::lengthAccessArraySequenceCompat()
+void tst_QmlCppCodegen::typedArray()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/ArraySequenceLengthInterop.qml"_s));
+ const QUrl document(u"qrc:/qt/qml/TestTypes/typedArray.qml"_s);
+ QQmlComponent c(&engine, document);
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->property("length").toInt(), 100);
-}
+ QVERIFY(o);
+ QDateTime date;
+ QVERIFY(qvariant_cast<QList<int>>(o->property("values2")).isEmpty());
+ QCOMPARE(qvariant_cast<QList<int>>(o->property("values3")),
+ QList<int>({1, 2, 3, 4}));
+ QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
+ QList<QDateTime>({date, date, date}));
+ {
+ const QList<double> actual
+ = qvariant_cast<QList<double>>(o->property("values5"));
+ const QList<double> expected
+ = QList<double>({1, 2, 3.4, 30, std::numeric_limits<double>::quiet_NaN(), 0});
+ QCOMPARE(actual.size(), expected.size());
+ for (qsizetype i = 0, end = actual.size(); i != end; ++i) {
+ if (std::isnan(expected[i]))
+ QVERIFY(std::isnan(actual[i]));
+ else
+ QCOMPARE(actual[i], expected[i]);
+ }
+ }
+ date = QDateTime::currentDateTime();
+ o->setProperty("aDate", date);
+ QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")),
+ QList<QDateTime>({date, date, date}));
-static QList<QString> convertToStrings(const QList<int> &ints)
-{
- QList<QString> strings;
- for (int i : ints)
- strings.append(QString::number(i));
- return strings;
+ QQmlListProperty<QObject> values6
+ = qvariant_cast<QQmlListProperty<QObject>>(o->property("values6"));
+ QCOMPARE(values6.count(&values6), 3);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values6.at(&values6, i), o.data());
+
+ QCOMPARE(o->property("inIntList").toInt(), 2);
+ QCOMPARE(qvariant_cast<QDateTime>(o->property("inDateList")), date);
+ QCOMPARE(o->property("inRealList").toDouble(), 30.0);
+ QCOMPARE(o->property("inCharList").toString(), QStringLiteral("f"));
+
+ const QMetaObject *metaObject = o->metaObject();
+ QMetaMethod method = metaObject->method(metaObject->indexOfMethod("stringAt10(QString)"));
+ QVERIFY(method.isValid());
+
+ // If LoadElement threw an exception the function would certainly return neither 10 nor 20.
+ int result = 0;
+ method.invoke(
+ o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("a")));
+ QCOMPARE(result, 10);
+ method.invoke(
+ o.data(), Q_RETURN_ARG(int, result), Q_ARG(QString, QStringLiteral("aaaaaaaaaaa")));
+ QCOMPARE(result, 20);
}
-void tst_QmlCppCodegen::numbersInJsPrimitive()
+void tst_QmlCppCodegen::undefinedResets()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/numbersInJsPrimitive.qml"_s));
- QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedResets.qml"_s));
- const QList<int> zeroes = {0, 0, 0, 0};
- const QList<int> written = {39, 40, 41, 42};
- const QList<int> stored = {1334, 1335, 1336, 1337};
- QStringList asStrings(4);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- for (int i = 0; i < 4; ++i) {
- QMetaObject::invokeMethod(
- o.data(), "readValueAsString",
- Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
- }
- QCOMPARE(asStrings, convertToStrings(zeroes));
+ Person *person = qobject_cast<Person *>(rootObject.data());
+ QVERIFY(person);
+ QCOMPARE(person->shoeSize(), 0);
+ QCOMPARE(person->name(), u"Marge"_s);
- QMetaObject::invokeMethod(o.data(), "writeValues");
- for (int i = 0; i < 4; ++i) {
- QMetaObject::invokeMethod(
- o.data(), "readValueAsString",
- Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
- }
- QCOMPARE(asStrings, convertToStrings(written));
+ person->setShoeSize(11);
- QMetaObject::invokeMethod(o.data(), "storeValues");
- for (int i = 0; i < 4; ++i) {
- QMetaObject::invokeMethod(
- o.data(), "readValueAsString",
- Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i));
- }
- QCOMPARE(asStrings, convertToStrings(stored));
+ QCOMPARE(person->shoeSize(), 11);
+ QCOMPARE(person->name(), u"Bart"_s);
+
+ person->setShoeSize(10);
+ QCOMPARE(person->shoeSize(), 10);
+ QCOMPARE(person->name(), u"Marge"_s);
+
+ person->setName(u"no one"_s);
+ QCOMPARE(person->name(), u"no one"_s);
+
+ person->setObjectName(u"the one"_s);
+ QCOMPARE(person->name(), u"Bart"_s);
}
-void tst_QmlCppCodegen::infinitiesToInt()
+void tst_QmlCppCodegen::undefinedToDouble()
{
QQmlEngine engine;
-
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/infinitiesToInt.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/undefinedToDouble.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
+ const QVariant d = o->property("d");
+ QCOMPARE(d.metaType(), QMetaType::fromType<double>());
+ QVERIFY(std::isnan(d.toDouble()));
+}
- const char *props[] = {"a", "b", "c"};
- for (const char *prop : props) {
- const QVariant i = o->property(prop);
- QCOMPARE(i.metaType(), QMetaType::fromType<int>());
- bool ok = false;
- QCOMPARE(i.toInt(&ok), 0);
- QVERIFY(ok);
- }
+void tst_QmlCppCodegen::unknownAttached()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownAttached.qml"_s));
+ QVERIFY(c.isError());
}
-void tst_QmlCppCodegen::equalityVarAndNonStorable()
+void tst_QmlCppCodegen::unknownParameter()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unknownParameter.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("cppProp").toInt(), 18);
+}
+
+void tst_QmlCppCodegen::unstoredUndefined()
{
QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unstoredUndefined.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->objectName(), u"NaN"_s);
+}
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityVarAndNonStorable.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+void tst_QmlCppCodegen::unusedAttached()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/unusedAttached.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
- QScopedPointer<QObject> object(c1.create());
- QVERIFY(!object.isNull() && !c1.isError());
- QVERIFY(!object->property("aIsNull").toBool());
- QVERIFY(object->property("aIsNotNull").toBool());
- QVERIFY(object->property("aIsNotUndefined").toBool());
- QVERIFY(object->property("objectIsNotNull").toBool());
- QVERIFY(!object->property("typedArrayIsNull").toBool());
- QVERIFY(object->property("isUndefined").toBool());
- QVERIFY(!object->property("derivedIsNull").toBool());
+ const auto func = qmlAttachedPropertiesFunction(
+ object.data(), QMetaType::fromName("QQuickKeyNavigationAttached*").metaObject());
+ QObject *attached = qmlAttachedPropertiesObject(object.data(), func);
+ const QVariant prop = attached->property("priority");
+ QVERIFY(prop.isValid());
+ QCOMPARE(QByteArray(prop.metaType().name()), "QQuickKeyNavigationAttached::Priority");
+ bool ok = false;
+ QCOMPARE(prop.toInt(&ok), 0);
+ QVERIFY(ok);
+}
- QVERIFY(object->property("primitiveIsNull").toBool());
- QVERIFY(object->property("primitiveIsDefined").toBool());
- QVERIFY(object->property("primitiveIsUndefined").toBool());
+void tst_QmlCppCodegen::urlString()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/urlString.qml"_s));
- QVERIFY(object->property("jsValueIsNull").toBool());
- QVERIFY(object->property("jsValueIsDefined").toBool());
- QVERIFY(object->property("jsValueIsUndefined").toBool());
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject);
- QVERIFY(object->property("nullVarIsUndefined").toBool());
- QVERIFY(object->property("nullIsUndefined").toBool());
- QVERIFY(object->property("nullVarIsNull").toBool());
- QVERIFY(object->property("nullIsNotUndefined").toBool());
-};
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("c")), QUrl(u"http://dddddd.com"_s));
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("d")), QUrl(u"http://aaaaaa.com"_s));
+ QCOMPARE(qvariant_cast<QUrl>(rootObject->property("e")), QUrl(u"http://a112233.de"_s));
+ QCOMPARE(rootObject->objectName(), QLatin1String("http://dddddd.com"));
+}
-void tst_QmlCppCodegen::equalityQObjects()
+void tst_QmlCppCodegen::valueTypeBehavior()
{
QQmlEngine engine;
- QQmlComponent c1(&engine, QUrl(u"qrc:/qt/qml/TestTypes/equalityQObjects.qml"_s));
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QScopedPointer<QObject> object(c1.create());
- QVERIFY(!object.isNull() && !c1.isError());
- QVERIFY(object->property("derivedIsNotNull").toBool());
- QVERIFY(object->property("nullObjectIsNull").toBool());
- QVERIFY(object->property("nonNullObjectIsNotNull").toBool());
- QVERIFY(object->property("compareSameObjects").toBool());
- QVERIFY(object->property("compareDifferentObjects").toBool());
- QVERIFY(object->property("compareObjectWithNullObject").toBool());
+ {
+ const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeCopy.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e'));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f'));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("e").toDouble(), 45.0);
+ QCOMPARE(o->property("f").toDouble(), 1.0);
+ }
- QVERIFY(object->property("nonStrict_derivedIsNotNull").toBool());
- QVERIFY(object->property("nonStrict_nullObjectIsNull").toBool());
- QVERIFY(object->property("nonStrict_nonNullObjectIsNotNull").toBool());
- QVERIFY(object->property("nonStrict_compareSameObjects").toBool());
- QVERIFY(object->property("nonStrict_compareDifferentObjects").toBool());
- QVERIFY(object->property("nonStrict_compareObjectWithNullObject").toBool());
+ {
+ const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeReference.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e'));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f'));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(qIsNaN(o->property("e").toDouble()));
+ QCOMPARE(o->property("f").toDouble(), 5.0);
+ }
+
+ {
+ const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeDefault.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'e'));
+ QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(url, 'f'));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(qIsNaN(o->property("e").toDouble()));
+ QCOMPARE(o->property("f").toDouble(), 5.0);
+ }
+
+ {
+ const QUrl url(u"qrc:/qt/qml/TestTypes/valueTypeCast.qml"_s);
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("x"), 10);
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString()
+ + u":8: TypeError: Cannot read property 'x' of undefined"_s));
+ o->setProperty("v", QLatin1String("not a rect"));
+
+ // If the binding throws an exception, the value doesn't change.
+ QCOMPARE(o->property("x"), 10);
+
+ QCOMPARE(o->property("tv3"), 5);
+ QCOMPARE(o->property("tc3"), 5);
+ QCOMPARE(o->property("tc6"), QVariant());
+ QCOMPARE(o->property("tc7"), QVariant());
+ QCOMPARE(o->property("tc8"), 2);
+
+ // The default greeting is never applied because undefined can be coerced to string
+ QCOMPARE(o->property("greeting1"), QLatin1String("undefined"));
+ QCOMPARE(o->property("greeting2"), QLatin1String("Custom Greeting"));
+ }
}
-void tst_QmlCppCodegen::dateConversions()
+void tst_QmlCppCodegen::valueTypeLists()
{
QQmlEngine engine;
- QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConversions.qml"_s));
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeLists.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
- Druggeljug *ref = engine.singletonInstance<Druggeljug *>("TestTypes", "Druggeljug");
+ QCOMPARE(qvariant_cast<QRectF>(o->property("rectInBounds")), QRectF(1, 2, 3, 4));
+ QVERIFY(o->metaObject()->indexOfProperty("rectOutOfBounds") > 0);
+ QVERIFY(!o->property("rectOutOfBounds").isValid());
- const QDateTime refDate = engine.coerceValue<QDate, QDateTime>(ref->myDate());
- const QDateTime refTime = engine.coerceValue<QTime, QDateTime>(ref->myTime());
+ QCOMPARE(qvariant_cast<QString>(o->property("stringInBounds")), QStringLiteral("bbb"));
+ QVERIFY(o->metaObject()->indexOfProperty("stringOutOfBounds") > 0);
+ QVERIFY(!o->property("stringOutOfBounds").isValid());
- QCOMPARE(o->property("date").value<QDateTime>(), refDate);
- QCOMPARE(o->property("time").value<QDateTime>(), refTime);
+ QCOMPARE(qvariant_cast<int>(o->property("intInBounds")), 7);
+ QVERIFY(o->metaObject()->indexOfProperty("intOutOfBounds") > 0);
+ QVERIFY(!o->property("intOutOfBounds").isValid());
- QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDateTime, QString>(refDate)));
- QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QDateTime, QString>(refTime)));
+ QCOMPARE(qvariant_cast<QString>(o->property("charInBounds")), QStringLiteral("d"));
+ QVERIFY(o->metaObject()->indexOfProperty("charOutOfBounds") > 0);
+ QVERIFY(!o->property("charOutOfBounds").isValid());
+}
- QMetaObject::invokeMethod(o.data(), "shuffle");
+void tst_QmlCppCodegen::valueTypeProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/valueTypeProperty.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
- QCOMPARE(ref->myDate(), (engine.coerceValue<QDateTime, QDate>(refDate)));
- QCOMPARE(ref->myTime(), (engine.coerceValue<QDateTime, QTime>(refTime)));
+ QFont font = qvariant_cast<QFont>(object->property("font"));
+ QCOMPARE(object->property("foo").toString(), font.family());
+ font.setFamily(u"Bar"_s);
+ object->setProperty("font", QVariant::fromValue(font));
+ QCOMPARE(object->property("foo").toString(), u"Bar"_s);
+}
- const QDate date = ref->myDate();
- const QTime time = ref->myTime();
+void tst_QmlCppCodegen::variantMapLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantMapLookup.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("i"), 42);
+}
- QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDate, QString>(date)));
- QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QTime, QString>(time)));
+void tst_QmlCppCodegen::variantReturn()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/variantReturn.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
- QMetaObject::invokeMethod(o.data(), "fool");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
- QCOMPARE(ref->myDate(), (engine.coerceValue<QTime, QDate>(time)));
- QCOMPARE(ref->myTime(), (engine.coerceValue<QDate, QTime>(date)));
+ QObject *a = o->property("a").value<QObject *>();
+ QVERIFY(a);
+ const QVariant x = a->property("x");
+ const QMetaObject *meta = x.metaType().metaObject();
+ QVERIFY(meta);
+ const QMetaProperty property = meta->property(meta->indexOfProperty("timeIndex"));
+ QVERIFY(property.isValid());
+ const QVariant timeIndex = property.readOnGadget(x.data());
+ QCOMPARE(timeIndex.metaType(), QMetaType::fromType<qsizetype>());
+ QCOMPARE(timeIndex.value<qsizetype>(), qsizetype(1));
+
+ QObject *b = o->property("b").value<QObject *>();
+ QVERIFY(b);
+ QCOMPARE(b->property("z").toInt(), 2);
}
-static QRegularExpression bindingLoopMessage(const QUrl &url, char var)
+void tst_QmlCppCodegen::variantlist()
{
- // The actual string depends on how many times QObject* was registered with what parameters.
- return QRegularExpression(
- "%1:4:1: QML [^:]+: Binding loop detected for property \"%2\""_L1
- .arg(url.toString()).arg(QLatin1Char(var)));
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantlist.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ const QVariantList things = qvariant_cast<QVariantList>(o->property("things"));
+ QCOMPARE(things.size(), 2);
+ QCOMPARE(things[0].toString(), u"thing"_s);
+ QCOMPARE(things[1].toInt(), 30);
}
-void tst_QmlCppCodegen::valueTypeBehavior()
+void tst_QmlCppCodegen::variantMap()
{
QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/variantMap.qml"_s));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
- const QUrl copy(u"qrc:/qt/qml/TestTypes/valueTypeCopy.qml"_s);
+ QCOMPARE(o->objectName(), "a b"_L1);
+ QCOMPARE(o->property("r"), QVariant::fromValue(QRectF(12, 13, 14, 15)));
- QQmlComponent c1(&engine, copy);
- QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
- QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(copy, 'e'));
- QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(copy, 'f'));
- QScopedPointer<QObject> o1(c1.create());
- QVERIFY(!o1.isNull());
- QCOMPARE(o1->property("e").toDouble(), 45.0);
- QCOMPARE(o1->property("f").toDouble(), 1.0);
+ const QVariantMap expected = QVariantMap {
+ { u"1"_s, QVariant::fromValue<std::nullptr_t>(nullptr) },
+ { u"19"_s, QVariant::fromValue(u"19"_s) },
+ { u"25"_s, QVariant() }
+ };
- const QUrl reference(u"qrc:/qt/qml/TestTypes/valueTypeReference.qml"_s);
- QQmlComponent c2(&engine, reference);
- QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
- QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(reference, 'e'));
- QTest::ignoreMessage(QtWarningMsg, bindingLoopMessage(reference, 'f'));
- QScopedPointer<QObject> o2(c2.create());
- QVERIFY(!o2.isNull());
- QVERIFY(qIsNaN(o2->property("e").toDouble()));
- QCOMPARE(o2->property("f").toDouble(), 5.0);
+ QCOMPARE(o->property("v").toMap(), expected);
}
-void tst_QmlCppCodegen::invisibleSingleton()
+void tst_QmlCppCodegen::voidConversion()
{
QQmlEngine engine;
- const QUrl copy(u"qrc:/qt/qml/TestTypes/hidden/Main.qml"_s);
- QQmlComponent c(&engine, copy);
+ const QUrl url(u"qrc:/qt/qml/TestTypes/voidConversion.qml"_s);
+ QQmlComponent c(&engine, url);
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QTest::ignoreMessage(
- QtWarningMsg,
- "qrc:/qt/qml/TestTypes/hidden/Main.qml:4:5: "
- "Unable to assign [undefined] to QColor");
+ QtWarningMsg,
+ qPrintable(url.toString() + u":8: Error: Cannot assign [undefined] to QPointF"_s));
+
QScopedPointer<QObject> o(c.create());
- QVERIFY(!o.isNull());
- QCOMPARE(o->property("c"), QVariant(QMetaType::fromName("QColor")));
+ QVERIFY(o);
+
+ QCOMPARE(o->property("p"), QPointF(20, 10));
+}
+
+void tst_QmlCppCodegen::voidFunction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/voidfunction.qml"_s));
+ QVERIFY2(component.isReady(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QVERIFY(object->objectName().isEmpty());
+ object->metaObject()->invokeMethod(object.data(), "doesNotReturnValue");
+ QCOMPARE(object->objectName(), u"barbar"_s);
+}
+
+void tst_QmlCppCodegen::writeBack()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/writeback.qml"_s));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ Person *person = qobject_cast<Person *>(object.data());
+ QVERIFY(person);
+ QCOMPARE(person->area(), QRectF(4, 5, 16, 17));
+ QCOMPARE(person->property("inner").toInt(), 99);
+
+ Person *shadowable = person->property("shadowable").value<Person *>();
+ QVERIFY(shadowable);
+ QCOMPARE(shadowable->area(), QRectF(40, 50, 16, 17));
+
+ QCOMPARE(person->property("ints"), QVariant::fromValue(QList<int>({12, 22, 2, 1, 0, 0, 33})));
}
QTEST_MAIN(tst_QmlCppCodegen)
diff --git a/tests/auto/qml/qmldiskcache/CMakeLists.txt b/tests/auto/qml/qmldiskcache/CMakeLists.txt
index b6fd92d653..e47d2a7f75 100644
--- a/tests/auto/qml/qmldiskcache/CMakeLists.txt
+++ b/tests/auto/qml/qmldiskcache/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmldiskcache Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldiskcache LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmldiskcache
SOURCES
tst_qmldiskcache.cpp
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 19a6731ff7..810fdecafd 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
@@ -34,12 +34,18 @@ private slots:
void recompileAfterDirectoryChange();
void fileSelectors();
void localAliases();
+ void aliasToAlias();
void cacheResources();
void stableOrderOfDependentCompositeTypes();
void singletonDependency();
void cppRegisteredSingletonDependency();
void cacheModuleScripts();
void reuseStaticMappings();
+ void invalidateSaveLoadCache();
+ void duplicateIdsInInlineComponents();
+
+ void inlineComponentDoesNotCauseConstantInvalidation_data();
+ void inlineComponentDoesNotCauseConstantInvalidation();
private:
QDir m_qmlCacheDirectory;
@@ -98,7 +104,7 @@ struct TestCompiler
{
closeMapping();
testFilePath = baseDirectory + QStringLiteral("/test.qml");
- cacheFilePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ cacheFilePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(
QUrl::fromLocalFile(testFilePath));
mappedFile.setFileName(cacheFilePath);
}
@@ -170,7 +176,7 @@ struct TestCompiler
return false;
}
- const QString targetCacheFilePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ const QString targetCacheFilePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(
QUrl::fromLocalFile(targetTestFilePath));
QFile source(cacheFilePath);
@@ -197,16 +203,14 @@ struct TestCompiler
{
const QString path = fileName.isEmpty() ? testFilePath : tempDir.path() + "/" + fileName;
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
- = QV4::ExecutableCompilationUnit::create();
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
return unit->loadFromDisk(QUrl::fromLocalFile(path),
QFileInfo(path).lastModified(), &lastErrorString);
}
quintptr unitData()
{
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
- = QV4::ExecutableCompilationUnit::create();
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath),
QFileInfo(testFilePath).lastModified(), &lastErrorString)
? quintptr(unit->unitData())
@@ -289,12 +293,12 @@ void tst_qmldiskcache::loadLocalAsFallback()
f.write(reinterpret_cast<const char *>(&unit), sizeof(unit));
}
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create();
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath),
QFileInfo(testCompiler.testFilePath).lastModified(),
&testCompiler.lastErrorString);
QVERIFY2(loaded, qPrintable(testCompiler.lastErrorString));
- QCOMPARE(unit->objectCount(), 1);
+ QCOMPARE(unit->qmlData->nObjects, 1u);
}
void tst_qmldiskcache::regenerateAfterChange()
@@ -606,7 +610,7 @@ void tst_qmldiskcache::fileSelectors()
QVERIFY(!obj.isNull());
QCOMPARE(obj->property("value").toInt(), 42);
- QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(
QUrl::fromLocalFile(testFilePath)));
QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName()));
}
@@ -622,7 +626,7 @@ void tst_qmldiskcache::fileSelectors()
QVERIFY(!obj.isNull());
QCOMPARE(obj->property("value").toInt(), 100);
- QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(
QUrl::fromLocalFile(selectedTestFilePath)));
QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName()));
}
@@ -671,6 +675,55 @@ void tst_qmldiskcache::localAliases()
}
}
+void tst_qmldiskcache::aliasToAlias()
+{
+ QQmlEngine engine;
+
+ TestCompiler testCompiler(&engine);
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ const QByteArray contents = QByteArrayLiteral(R"(
+ import QML
+ QtObject {
+ id: foo
+ readonly property alias myAlias: bar.prop
+
+ property QtObject o: QtObject {
+ id: bar
+
+ property QtObject o: QtObject {
+ id: baz
+ readonly property int value: 100
+ }
+
+ readonly property alias prop: baz.value
+ }
+ }
+ )");
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+ QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString));
+ }
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("myAlias").toInt(), 100);
+ }
+
+ engine.clearComponentCache();
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("myAlias").toInt(), 100);
+ }
+}
+
static QSet<QString> entrySet(const QDir &dir)
{
const auto &list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
@@ -782,7 +835,7 @@ void tst_qmldiskcache::stableOrderOfDependentCompositeTypes()
QVERIFY2(firstDependentTypeClassName.contains("QMLTYPE"), firstDependentTypeClassName.constData());
QVERIFY2(secondDependentTypeClassName.contains("QMLTYPE"), secondDependentTypeClassName.constData());
- const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(
QUrl::fromLocalFile(testFilePath));
QVERIFY(QFile::exists(testFileCachePath));
QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
@@ -861,7 +914,7 @@ void tst_qmldiskcache::singletonDependency()
QCOMPARE(obj->property("value").toInt(), 42);
}
- const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(
QUrl::fromLocalFile(testFilePath));
QVERIFY(QFile::exists(testFileCachePath));
QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
@@ -919,7 +972,7 @@ void tst_qmldiskcache::cppRegisteredSingletonDependency()
QCOMPARE(value.toInt(), 42);
}
- const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(
QUrl::fromLocalFile(testFilePath));
QVERIFY(QFile::exists(testFileCachePath));
QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
@@ -961,7 +1014,8 @@ void tst_qmldiskcache::cacheModuleScripts()
auto componentPrivate = QQmlComponentPrivate::get(&component);
QVERIFY(componentPrivate);
- auto compilationUnit = componentPrivate->compilationUnit->dependentScripts.first()->compilationUnit();
+ auto compilationUnit = componentPrivate->compilationUnit->dependentScriptsPtr()
+ ->first()->compilationUnit();
QVERIFY(compilationUnit);
auto unitData = compilationUnit->unitData();
QVERIFY(unitData);
@@ -1007,6 +1061,326 @@ void tst_qmldiskcache::reuseStaticMappings()
QCOMPARE(testCompiler.unitData(), data1);
}
+class AParent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int x MEMBER x)
+public:
+ AParent(QObject *parent = nullptr) : QObject(parent) {}
+ int x = 25;
+};
+
+class BParent : public QObject
+{
+ Q_OBJECT
+
+ // Insert y before x, to change the property index of x
+ Q_PROPERTY(int y MEMBER y)
+
+ Q_PROPERTY(int x MEMBER x)
+public:
+ BParent(QObject *parent = nullptr) : QObject(parent) {}
+ int y = 13;
+ int x = 25;
+};
+
+static QString writeTempFile(
+ const QTemporaryDir &tempDir, const QString &fileName, const char *contents) {
+ QFile f(tempDir.path() + '/' + fileName);
+ const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ Q_ASSERT(ok);
+ f.write(contents);
+ return f.fileName();
+};
+
+void tst_qmldiskcache::invalidateSaveLoadCache()
+{
+ qmlRegisterType<AParent>("Base", 1, 0, "Parent");
+ std::unique_ptr<QQmlEngine> e = std::make_unique<QQmlEngine>();
+
+ // If you store a CU to a .qmlc file at run time, the .qmlc file will contain
+ // alias entries with the encodedMetaPropertyIndex pre-resolved. That's in
+ // contrast to .qmlc files generated ahead of time. Exploit that to cause
+ // a need to recompile the file.
+
+ QTemporaryDir tempDir;
+ writeTempFile(
+ tempDir, QLatin1String("B.qml"),
+ R"(
+ import QML
+ QtObject {
+ component C: QtObject {}
+ }
+ )");
+
+ const QString fileName = writeTempFile(
+ tempDir, QLatin1String("a.qml"),
+ R"(
+ import Base
+ Parent {
+ id: self
+ property alias z: self.x
+ component C: Parent {}
+ property C c: C {}
+ property B.C d: B.C {}
+ }
+ )");
+ const QUrl url = QUrl::fromLocalFile(fileName);
+ waitForFileSystem();
+
+ {
+ QQmlComponent a(e.get(), url);
+ QVERIFY2(a.isReady(), qPrintable(a.errorString()));
+ QScopedPointer<QObject> ao(a.create());
+ QVERIFY(!ao.isNull());
+ AParent *ap = qobject_cast<AParent *>(ao.data());
+ QCOMPARE(ap->property("z").toInt(), ap->x);
+ }
+
+ QString errorString;
+ auto oldUnit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
+ QVERIFY2(oldUnit->loadFromDisk(url, QFileInfo(fileName).lastModified(), &errorString), qPrintable(errorString));
+
+ // Produce a checksum mismatch.
+ e->clearComponentCache();
+ qmlClearTypeRegistrations();
+ qmlRegisterType<BParent>("Base", 1, 0, "Parent");
+ e = std::make_unique<QQmlEngine>();
+
+ {
+ QQmlComponent b(e.get(), url);
+ QVERIFY2(b.isReady(), qPrintable(b.errorString()));
+ QScopedPointer<QObject> bo(b.create());
+ QVERIFY(!bo.isNull());
+ BParent *bp = qobject_cast<BParent *>(bo.data());
+ QCOMPARE(bp->property("z").toInt(), bp->x);
+ }
+
+ // Make it recompile again. If we ever get rid of the metaobject indices in compilation units,
+ // the above test will not test the save/load cache anymore. Therefore, in order to make really
+ // sure that we get a new CU that invalidates the save/load cache, modify the file in place.
+
+ e->clearComponentCache();
+ {
+ QFile file(fileName);
+ QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Append));
+ file.write(" ");
+ }
+ waitForFileSystem();
+
+ {
+ QQmlComponent b(e.get(), url);
+ QVERIFY2(b.isReady(), qPrintable(b.errorString()));
+ QScopedPointer<QObject> bo(b.create());
+ QVERIFY(!bo.isNull());
+ BParent *bp = qobject_cast<BParent *>(bo.data());
+ QCOMPARE(bp->property("z").toInt(), bp->x);
+ }
+
+ // Verify that the mapped unit data is actually different now.
+ // The cache should have been invalidated after all.
+ // So, now we should be able to load a freshly written CU.
+
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
+ QVERIFY2(unit->loadFromDisk(url, QFileInfo(fileName).lastModified(), &errorString), qPrintable(errorString));
+
+ QVERIFY(unit->unitData() != oldUnit->unitData());
+}
+
+void tst_qmldiskcache::duplicateIdsInInlineComponents()
+{
+ // Exercise the case of loading strange generalized group properties from .qmlc.
+
+ QQmlEngine engine;
+
+ TestCompiler testCompiler(&engine);
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ const QByteArray contents = QByteArrayLiteral(R"(
+ import QtQml
+ QtObject {
+ component First : QtObject {
+ property QtObject aa: QtObject {
+ id: a
+ }
+ property Binding bb: Binding {
+ a.objectName: "test1"
+ }
+ }
+
+ component Second : QtObject {
+ property QtObject aa: QtObject {
+ id: a
+ }
+ property Binding bb: Binding {
+ a.objectName: "test2"
+ }
+
+ property Component cc: QtObject {
+ property QtObject aa: QtObject {
+ id: a
+ }
+ property Binding bb: Binding {
+ a.objectName: "test3"
+ }
+ }
+ }
+
+ property First first: First {}
+ property Second second: Second {}
+ property QtObject third: second.cc.createObject();
+
+ objectName: first.aa.objectName + second.aa.objectName + third.aa.objectName;
+ }
+ )");
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+ QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString));
+ }
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->objectName(), "test1test2test3");
+ }
+
+ engine.clearComponentCache();
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->objectName(), "test1test2test3");
+ }
+}
+
+void tst_qmldiskcache::inlineComponentDoesNotCauseConstantInvalidation_data()
+{
+ QTest::addColumn<QByteArray>("code");
+
+ QTest::addRow("simple") << QByteArray(R"(
+ import QtQml
+ QtObject {
+ component Test: QtObject {
+ property int i: 28
+ }
+ property Test test: Test {
+ objectName: "foobar"
+ }
+ property int k: test.i
+ }
+ )");
+
+ QTest::addRow("with function") << QByteArray(R"(
+ import QtQml
+ QtObject {
+ component Test : QtObject {
+ id: self
+ property int i: 2
+ property alias j: self.i
+ }
+ property Test test: Test {
+ function updateValue() {}
+ objectName: 'foobar'
+ j: 28
+ }
+ property int k: test.j
+ }
+ )");
+
+ QTest::addRow("in nested") << QByteArray(R"(
+ import QtQuick
+ Item {
+ Item {
+ component Line: Item {
+ property alias endY: pathLine.y
+ Item {
+ Item {
+ id: pathLine
+ }
+ }
+ }
+ }
+ Line {
+ id: primaryLine
+ endY: 28
+ }
+ property int k: primaryLine.endY
+ }
+ )");
+
+ QTest::addRow("with revision") << QByteArray(R"(
+ import QtQuick
+ ListView {
+ Item {
+ id: scrollBar
+ }
+ delegate: Image {
+ mipmap: true
+ }
+ Item {
+ id: refreshNodesIndicator
+ }
+ property int k: delegate.createObject().mipmap ? 28 : 4
+ }
+ )");
+}
+
+void tst_qmldiskcache::inlineComponentDoesNotCauseConstantInvalidation()
+{
+ QFETCH(QByteArray, code);
+
+ QQmlEngine engine;
+
+ TestCompiler testCompiler(&engine);
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ auto check = [&](){
+ QQmlComponent c(&engine, QUrl::fromLocalFile(testCompiler.testFilePath));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("k"), QVariant::fromValue<int>(28));
+ };
+
+ testCompiler.reset();
+ QVERIFY(testCompiler.writeTestFile(code));
+
+ QVERIFY(testCompiler.loadTestFile());
+
+ const quintptr data1 = testCompiler.unitData();
+ QVERIFY(data1 != 0);
+ QCOMPARE(testCompiler.unitData(), data1);
+ check();
+
+ engine.clearComponentCache();
+
+ // inline component does not invalidate cache
+ QVERIFY(testCompiler.loadTestFile());
+ QCOMPARE(testCompiler.unitData(), data1);
+ check();
+
+ testCompiler.reset();
+ QVERIFY(testCompiler.writeTestFile(R"(
+ import QtQml
+ QtObject {
+ component Test : QtObject {
+ property double d: 2
+ }
+ property Test test: Test {
+ objectName: 'foobar'
+ }
+ })"));
+ QVERIFY(testCompiler.loadTestFile());
+ const quintptr data2 = testCompiler.unitData();
+ QVERIFY(data2);
+ QVERIFY(data1 != data2);
+}
+
QTEST_MAIN(tst_qmldiskcache)
#include "tst_qmldiskcache.moc"
diff --git a/tests/auto/qml/qmlformat/CMakeLists.txt b/tests/auto/qml/qmlformat/CMakeLists.txt
index 2c908a9190..76923d66de 100644
--- a/tests/auto/qml/qmlformat/CMakeLists.txt
+++ b/tests/auto/qml/qmlformat/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmlformat Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlformat LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -24,8 +30,11 @@ qt_internal_add_test(tst_qmlformat
Qt::TestPrivate
Qt::QuickTestUtilsPrivate
TESTDATA ${test_data}
+ TIMEOUT 3000
)
+add_dependencies(tst_qmlformat Qt::qmlformat)
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qmlformat/data/Annotations.formatted.qml b/tests/auto/qml/qmlformat/data/Annotations.formatted.qml
index 92cbeb55f7..019b7f0141 100644
--- a/tests/auto/qml/qmlformat/data/Annotations.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/Annotations.formatted.qml
@@ -10,17 +10,14 @@ import QtCharts 2.0
@Pippo {
atg1: 3
}
-@Annotation2 {
-}
+@Annotation2 {}
Item {
- @Annotate {
- }
+ @Annotate {}
anchors.fill: parent
@AnnotateMore {
property int x: 5
}
- @AnnotateALot {
- }
+ @AnnotateALot {}
property variant othersSlice: 0
//![1]
@@ -79,19 +76,17 @@ Item {
@BindingAnn {
bType: 2
}
- val2: Item {
- }
+ val2: Item {}
@BindingAnn {
bType: 3
}
val3: [
- Item {
- }
+ Item {}
]
@BindingAnn {
bType: 4
}
- Animation on val {
+ Animation on val {
duration: 34
}
}
diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml
index 8b181f607e..b1662e8898 100644
--- a/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml
+++ b/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml
@@ -43,7 +43,7 @@ Item {
property bool some_bool: false
// This comment is related to the property animation
- PropertyAnimation on x {
+ PropertyAnimation on x {
id: foo2
x: 3
y: x + 3
@@ -54,6 +54,7 @@ Item {
// Another orphan
// More orphans
+
property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
property bool something_computed: function (x) {
const PI = 3, DAYS_PER_YEAR = 365.25;
@@ -124,6 +125,7 @@ Item {
}
// Another orphan inside something_computed
+
return "foobar";
}()
@@ -133,11 +135,9 @@ Item {
// This is an orphan
// This is a cool text
- Text {
- },
+ Text {},
// This is a cool rectangle
- Rectangle {
- }
+ Rectangle {}
]
// some_read_only_bool
diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.qml
index cb8865d94e..c4aef78924 100644
--- a/tests/auto/qml/qmlformat/data/Example1.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/Example1.formatted.qml
@@ -43,7 +43,7 @@ Item {
property bool some_bool: false
// This comment is related to the property animation
- PropertyAnimation on x {
+ PropertyAnimation on x {
id: foo2
x: 3
y: x + 3
@@ -54,6 +54,7 @@ Item {
// Another orphan
// More orphans
+
property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
property bool something_computed: function (x) {
const PI = 3, DAYS_PER_YEAR = 365.25;
@@ -124,6 +125,7 @@ Item {
}
// Another orphan inside something_computed
+
return "foobar";
}()
@@ -133,11 +135,9 @@ Item {
// This is an orphan
// This is a cool text
- Text {
- },
+ Text {},
// This is a cool rectangle
- Rectangle {
- }
+ Rectangle {}
]
// some_read_only_bool
diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml
index ef964f6f60..7cea50213a 100644
--- a/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml
+++ b/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml
@@ -43,7 +43,7 @@ Item {
property bool some_bool: false
// This comment is related to the property animation
- PropertyAnimation on x {
+ PropertyAnimation on x {
id: foo2
x: 3
y: x + 3
@@ -54,6 +54,7 @@ Item {
// Another orphan
// More orphans
+
property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
property bool something_computed: function (x) {
const PI = 3, DAYS_PER_YEAR = 365.25;
@@ -124,6 +125,7 @@ Item {
}
// Another orphan inside something_computed
+
return "foobar";
}()
@@ -133,11 +135,9 @@ Item {
// This is an orphan
// This is a cool text
- Text {
- },
+ Text {},
// This is a cool rectangle
- Rectangle {
- }
+ Rectangle {}
]
// some_read_only_bool
diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted2.qml b/tests/auto/qml/qmlformat/data/Example1.formatted2.qml
index aa662a3d5f..7049686900 100644
--- a/tests/auto/qml/qmlformat/data/Example1.formatted2.qml
+++ b/tests/auto/qml/qmlformat/data/Example1.formatted2.qml
@@ -39,6 +39,7 @@ Item {
// Another orphan
// More orphans
+
property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
property bool some_bool: false
default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool
@@ -114,6 +115,7 @@ Item {
}
// Another orphan inside something_computed
+
return "foobar";
}()
@@ -140,14 +142,12 @@ Item {
// This is an orphan
// This is a cool text
- Text {
- },
+ Text {},
// This is a cool rectangle
- Rectangle {
- }
+ Rectangle {}
]
// This comment is related to the property animation
- PropertyAnimation on x {
+ PropertyAnimation on x {
id: foo2
x: 3
y: x + 3
diff --git a/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml
index 7dfe435ac0..95b4fcf4a3 100644
--- a/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml
@@ -1,4 +1,3 @@
// This comment should be directly above Item after formatting
-Item {
-}
+Item {}
diff --git a/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml b/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml
new file mode 100644
index 0000000000..8ae4dd7c88
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml
@@ -0,0 +1,4 @@
+Item {
+ // should keep its comma
+ property var some_array_literal: [30, 20, 0.3,]
+}
diff --git a/tests/auto/qml/qmlformat/data/arrayEndComma.qml b/tests/auto/qml/qmlformat/data/arrayEndComma.qml
new file mode 100644
index 0000000000..1aec09515c
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/arrayEndComma.qml
@@ -0,0 +1,8 @@
+Item {
+ // should keep its comma
+ property var some_array_literal: [
+ 30,
+ 20,
+ 0.3,
+ ]
+}
diff --git a/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml
new file mode 100644
index 0000000000..ac4cf97881
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ Component.onCompleted: {
+ let f = ([]) => {};
+ let g = ([a]) => {};
+ let h = ([a, b]) => {};
+ let i = ([a, ...b]) => {};
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml
new file mode 100644
index 0000000000..2a47b2f152
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item { Component.onCompleted: { let f = ([]) => {};
+ let g = ([a]) => {};
+ let h = ([a, b]) => {};
+ let i = ([a, ...b]) => {};
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml
new file mode 100644
index 0000000000..071e3bc69f
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml
@@ -0,0 +1,12 @@
+/* comment with spaces */
+
+/*
+ another comment
+ */
+//
+
+// another comment /*test */
+
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qmlformat/data/blanklinesAfterComment.qml b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.qml
new file mode 100644
index 0000000000..c99415aeed
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.qml
@@ -0,0 +1,15 @@
+/* comment with spaces */
+
+/*
+ another comment
+ */
+//
+
+
+// another comment /*test */
+
+
+
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qmlformat/data/class.formatted.js b/tests/auto/qml/qmlformat/data/class.formatted.js
new file mode 100644
index 0000000000..eeb1faf64e
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/class.formatted.js
@@ -0,0 +1,5 @@
+class Person {
+ constructor(name) {
+ this._name = name;
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/class.js b/tests/auto/qml/qmlformat/data/class.js
new file mode 100644
index 0000000000..116061d515
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/class.js
@@ -0,0 +1 @@
+class Person{constructor(name){this._name = name;}}
diff --git a/tests/auto/qml/qmlformat/data/commentInEnum.formatted.qml b/tests/auto/qml/qmlformat/data/commentInEnum.formatted.qml
new file mode 100644
index 0000000000..583c315c4b
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/commentInEnum.formatted.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+QtObject {
+ // This to enum
+ enum Foo {
+ A = 3, // This is A
+ B, // This is B
+ C = 4, // This is C
+ D // This is D
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/commentInEnum.qml b/tests/auto/qml/qmlformat/data/commentInEnum.qml
new file mode 100644
index 0000000000..3a1b15d278
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/commentInEnum.qml
@@ -0,0 +1,12 @@
+import QtQml
+
+QtObject {
+ // This to enum
+ enum Foo {
+ A = 3, // This is A
+ B, // This is B
+ C = 4, // This is C
+ D // This is D
+ }
+
+}
diff --git a/tests/auto/qml/qmlformat/data/commentInQmlObject.formatted.qml b/tests/auto/qml/qmlformat/data/commentInQmlObject.formatted.qml
new file mode 100644
index 0000000000..749dc65b7c
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/commentInQmlObject.formatted.qml
@@ -0,0 +1,4 @@
+import QtQml
+
+// hello world
+QtObject {}
diff --git a/tests/auto/qml/qmlformat/data/commentInQmlObject.qml b/tests/auto/qml/qmlformat/data/commentInQmlObject.qml
new file mode 100644
index 0000000000..9aca637e4f
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/commentInQmlObject.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ // hello world
+}
diff --git a/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml
new file mode 100644
index 0000000000..0bfcc58179
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml
@@ -0,0 +1,24 @@
+import QtQml
+
+QtObject {
+
+ function evil({
+ hello = "world",
+ x = 42
+ }, [n = 42, m = 43, o = 44], {
+ destructuring,
+ is = {
+ a,
+ lot,
+ of
+ },
+ fun = 42
+ } = {
+ destructuring: 123,
+ is: {
+ x: 123
+ },
+ fun: 456
+ }) {
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.qml b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.qml
new file mode 100644
index 0000000000..f41661ed67
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+
+ function evil({ hello = "world", x = 42 },
+ [n = 42, m = 43, o = 44],
+ { destructuring, is = {a, lot, of}, fun = 42 } = {destructuring : 123, is : {x : 123}, fun : 456}) {
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/directives.formatted.js b/tests/auto/qml/qmlformat/data/directives.formatted.js
new file mode 100644
index 0000000000..3d0a4db544
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/directives.formatted.js
@@ -0,0 +1,8 @@
+.pragma library
+.import "fun.js" as Fun
+.import Qt.test 1.0 as JsQtTest
+.import Qt.Quick as Test
+
+Fun.begin();
+Test.do_stuff();
+JsQtTest.do_stuff();
diff --git a/tests/auto/qml/qmlformat/data/directives.js b/tests/auto/qml/qmlformat/data/directives.js
new file mode 100644
index 0000000000..b8e1b785ac
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/directives.js
@@ -0,0 +1,9 @@
+
+.pragma library
+
+.import "fun.js" as Fun
+.import Qt.test 1.0 as JsQtTest
+
+
+.import Qt.Quick as Test
+Fun.begin();Test.do_stuff();JsQtTest.do_stuff();
diff --git a/tests/auto/qml/qmlformat/data/directivesWithComments.formatted.js b/tests/auto/qml/qmlformat/data/directivesWithComments.formatted.js
new file mode 100644
index 0000000000..c130f72453
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/directivesWithComments.formatted.js
@@ -0,0 +1,6 @@
+.pragma library // use as library
+/*
+ Fun is necessary
+ */
+.import "fun.js" as Fun
+.import Qt.test 1.0 as JsQtTest // yet another comment not to be lost
diff --git a/tests/auto/qml/qmlformat/data/directivesWithComments.js b/tests/auto/qml/qmlformat/data/directivesWithComments.js
new file mode 100644
index 0000000000..7206bb27ed
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/directivesWithComments.js
@@ -0,0 +1,8 @@
+
+.pragma library // use as library
+
+/*
+ Fun is necessary
+ */
+.import "fun.js" as Fun
+.import Qt.test 1.0 as JsQtTest // yet another comment not to be lost
diff --git a/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml b/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml
index 0c7a2829c9..8eaac71178 100644
--- a/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml
@@ -1,13 +1,15 @@
Item {
- property var test: [{
+ property var test: [
+ {
// Testing
"foo": "bar"
- }]
+ }
+ ]
onTestChanged: {
fooBar(test, {
- // Testing
- "foo": "bar"
- });
+ // Testing
+ "foo": "bar"
+ });
}
}
diff --git a/tests/auto/qml/qmlformat/data/dontRemoveComments.qml b/tests/auto/qml/qmlformat/data/dontRemoveComments.qml
index 1797834879..2d2b4b6705 100644
--- a/tests/auto/qml/qmlformat/data/dontRemoveComments.qml
+++ b/tests/auto/qml/qmlformat/data/dontRemoveComments.qml
@@ -1,8 +1,10 @@
Item {
- property var test: [{
-// Testing
+ property var test: [
+ {
+ // Testing
"foo": "bar"
- }]
+ }
+ ]
onTestChanged: {
fooBar(test, {
diff --git a/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml b/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml
index edbb12c6e6..de6a23f9c7 100644
--- a/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.formatted.qml
@@ -4,11 +4,13 @@ Item {
function f() {
var count = 0;
+
class Person {
constructor(name){
this._name = name;
}
}
+
class Employee extends Person {
constructor(name, age){
super(name);
diff --git a/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.formatted.qml b/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.formatted.qml
new file mode 100644
index 0000000000..f41942e054
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.formatted.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+QtObject {
+
+ function patron(a, ...b) {
+ }
+
+ function patron1(a, ...[b, ...args]) {
+ }
+
+ function patron2(...{}) {
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.qml b/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.qml
new file mode 100644
index 0000000000..28364f29c9
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/ellipsisFunctionArgument.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+QtObject {
+
+ function patron(a, ...b) {
+ }
+
+ function patron1(a, ...[b, ...args]) {}
+
+ function patron2(...{}) {}
+} \ No newline at end of file
diff --git a/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml b/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml
new file mode 100644
index 0000000000..3f6807834f
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml
@@ -0,0 +1,16 @@
+import QtQuick
+
+Item {
+ x: {
+ const s = "\"";
+ let a = {
+ "\"": "\\"
+ };
+
+ let patron = {
+ "\\\"\n\n": "\?\?\\\"",
+ "": "",
+ "\'\"\n": 1
+ };
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/escapeChars.qml b/tests/auto/qml/qmlformat/data/escapeChars.qml
new file mode 100644
index 0000000000..c82ff3119e
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/escapeChars.qml
@@ -0,0 +1,16 @@
+import QtQuick
+
+Item {
+ x: {
+ const s = "\""
+ let a = {
+ "\"": "\\"
+ };
+
+ let patron = {
+ "\\\"\n\n" : "\?\?\\\"","": "", "\'\"\n":1
+ };
+
+
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/esm.formatted.mjs b/tests/auto/qml/qmlformat/data/esm.formatted.mjs
new file mode 100644
index 0000000000..f0b3fd7753
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/esm.formatted.mjs
@@ -0,0 +1,44 @@
+//Imports
+import defaultExport from "module-name";
+import * as name from "module-name";
+import { export1 } from "module-name";
+import { export1 as alias1 } from "module-name";
+import { default as alias } from "module-name";
+import { export1, export2 } from "module-name";
+import { export1, export2 as alias2 } from "module-name";
+import defaultExport, { export1, a } from "module-name";
+import defaultExport, * as name from "module-name";
+import "module-name";
+
+// Exporting declarations
+export let name1, name2; // also var
+export const name1 = 1, name2 = 2; // also var, let
+export function functionName() {}
+export class ClassName {
+ constructor(h){
+ this.h = h;
+ }
+}
+export function* generatorFunctionName() {}
+export const {
+ name1,
+ name2: bar
+} = o;
+export const [name1, name2] = array;
+
+// Export list
+export { name1, nameN };
+export { variable1 as name1, variable2 as name2, nameN };
+export { name1 as default };
+
+// Default exports
+export default function* generatorFunctionName() {
+ return 1;
+}
+
+// Aggregating modules
+export * from "module-name";
+export { name1, nameN } from "module-name";
+export { import1 as name1, import2 as name2, nameN } from "module-name";
+export { default } from "module-name";
+export { default as name1 } from "module-name";
diff --git a/tests/auto/qml/qmlformat/data/esm.mjs b/tests/auto/qml/qmlformat/data/esm.mjs
new file mode 100644
index 0000000000..83e22fdedd
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/esm.mjs
@@ -0,0 +1,33 @@
+//Imports
+import defaultExport from "module-name";
+import * as name from "module-name"
+import {export1} from "module-name";
+import { export1 as alias1 } from "module-name";
+import { default as alias } from "module-name"
+import { export1, export2 } from "module-name";
+import {export1,export2 as alias2} from "module-name";
+import defaultExport,{export1,a} from "module-name"
+import defaultExport, * as name from "module-name";
+import "module-name";
+
+
+// Exporting declarations
+export let name1,name2; // also var
+export const name1=1,name2=2 // also var, let
+export function functionName() {}
+export class ClassName{constructor(h){this.h=h;}}
+export function* generatorFunctionName() {}
+export const {name1, name2: bar}=o;export const [name1,name2]=array
+
+// Export list
+export {name1,nameN};export {variable1 as name1,variable2 as name2,nameN }
+export {name1 as default};
+
+// Default exports
+export default function* generatorFunctionName() {return 1;}
+
+// Aggregating modules
+export * from "module-name";
+export { name1,nameN} from "module-name"
+export { import1 as name1, import2 as name2,nameN } from "module-name";export { default, } from "module-name";
+export { default as name1 } from "module-name";
diff --git a/tests/auto/qml/qmlformat/data/filesOption/valid1.formatted.qml b/tests/auto/qml/qmlformat/data/filesOption/valid1.formatted.qml
new file mode 100644
index 0000000000..a995f0479f
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/filesOption/valid1.formatted.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ property int a
+ function aa() {
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/filesOption/valid1.qml b/tests/auto/qml/qmlformat/data/filesOption/valid1.qml
new file mode 100644
index 0000000000..8aef366bd9
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/filesOption/valid1.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+property int a
+function aa(){}
+} \ No newline at end of file
diff --git a/tests/auto/qml/qmlformat/data/filesOption/valid2.formatted.qml b/tests/auto/qml/qmlformat/data/filesOption/valid2.formatted.qml
new file mode 100644
index 0000000000..a995f0479f
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/filesOption/valid2.formatted.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ property int a
+ function aa() {
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/filesOption/valid2.qml b/tests/auto/qml/qmlformat/data/filesOption/valid2.qml
new file mode 100644
index 0000000000..f2cb636c00
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/filesOption/valid2.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ property int a
+ function aa() {}
+}
diff --git a/tests/auto/qml/qmlformat/data/forOf.formatted.qml b/tests/auto/qml/qmlformat/data/forOf.formatted.qml
index fa9ff9c631..0cc4f9fecd 100644
--- a/tests/auto/qml/qmlformat/data/forOf.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/forOf.formatted.qml
@@ -3,6 +3,7 @@ import QtQml 2.0
QtObject {
Component.onCompleted: {
var list = [[1, 2], [3, 4], [5, 6]];
+
for (const [x, y] of list)
console.log("X: " + x + "; Y: " + y);
for (let [x, y] of list)
diff --git a/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml b/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml
index d452ba2b8c..91f520b5fc 100644
--- a/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml
@@ -6,8 +6,7 @@ Item {
}
function test2() {
}
- Button {
- }
+ Button {}
function test4() {
}
diff --git a/tests/auto/qml/qmlformat/data/importStatements.formatted.qml b/tests/auto/qml/qmlformat/data/importStatements.formatted.qml
new file mode 100644
index 0000000000..6613becaca
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/importStatements.formatted.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+import QtQuick
+import QtQuick.Controls
+
+import org.test.module
+
+QtObject {}
diff --git a/tests/auto/qml/qmlformat/data/importStatements.qml b/tests/auto/qml/qmlformat/data/importStatements.qml
new file mode 100644
index 0000000000..efe1872e93
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/importStatements.qml
@@ -0,0 +1,12 @@
+import QtQml
+
+
+import QtQuick
+import QtQuick.Controls
+
+import org.test.module
+
+
+
+QtObject {
+}
diff --git a/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml b/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml
new file mode 100644
index 0000000000..09ab9454e1
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml
@@ -0,0 +1,6 @@
+Item {
+ block1: {
+ console.log("Hello, world!");
+ }
+ emptyBlock: {}
+}
diff --git a/tests/auto/qml/qmlformat/data/javascriptBlock.qml b/tests/auto/qml/qmlformat/data/javascriptBlock.qml
new file mode 100644
index 0000000000..1665c81f42
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/javascriptBlock.qml
@@ -0,0 +1,5 @@
+Item {
+ block1: {console.log("Hello, world!");}
+ emptyBlock: {
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.formatted.js b/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.formatted.js
new file mode 100644
index 0000000000..62a5e36f5e
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.formatted.js
@@ -0,0 +1,6 @@
+var b = function () {
+ var a = 1;
+ for (var ii = 1; ii < 10; ++ii) {
+ a = a * ii;
+ }
+};
diff --git a/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.js b/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.js
new file mode 100644
index 0000000000..0155cff7f9
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/lambdaFunctionWithLoop.js
@@ -0,0 +1 @@
+var b=function(){var a=1;for(var ii=1;ii<10;++ii){a=a*ii;}}
diff --git a/tests/auto/qml/qmlformat/data/lambdaWithIfElse.formatted.js b/tests/auto/qml/qmlformat/data/lambdaWithIfElse.formatted.js
new file mode 100644
index 0000000000..3bc7f683c4
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/lambdaWithIfElse.formatted.js
@@ -0,0 +1,7 @@
+var f = function () {
+ if (true) {
+ console.log("true");
+ } else {
+ console.log("false");
+ }
+};
diff --git a/tests/auto/qml/qmlformat/data/lambdaWithIfElse.js b/tests/auto/qml/qmlformat/data/lambdaWithIfElse.js
new file mode 100644
index 0000000000..aaf731e5f0
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/lambdaWithIfElse.js
@@ -0,0 +1 @@
+var f = function(){if(true){ console.log("true");} else {console.log("false");}}
diff --git a/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.formatted.js b/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.formatted.js
new file mode 100644
index 0000000000..99db6fb372
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.formatted.js
@@ -0,0 +1,9 @@
+var l = function () {
+ var nl = function () {
+ if (true) {
+ console.log("true");
+ } else {
+ console.log("false");
+ }
+ };
+};
diff --git a/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.js b/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.js
new file mode 100644
index 0000000000..23ae087b04
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/lambdaWithIfElseInsideLambda.js
@@ -0,0 +1 @@
+var l = function(){var nl = function(){if(true){console.log("true");}else{console.log("false");}};};
diff --git a/tests/auto/qml/qmlformat/data/messyIfStatement.formatted.js b/tests/auto/qml/qmlformat/data/messyIfStatement.formatted.js
new file mode 100644
index 0000000000..cd7d75314c
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/messyIfStatement.formatted.js
@@ -0,0 +1,4 @@
+if (((typeof ezJsu !== 'undefined') && ezJsu === true) || ((typeof _ez_sa !== 'undefined') && _ez_sa === true)) {
+ a.defaultStoreUrl = '//g.ezoic.net/ezoic/imp.gif';
+ a.defaultStoreA = '//g.ezoic.net/ezoic/i.gif';// Single Line Comments here
+}
diff --git a/tests/auto/qml/qmlformat/data/messyIfStatement.js b/tests/auto/qml/qmlformat/data/messyIfStatement.js
new file mode 100644
index 0000000000..cdd4c7bd9d
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/messyIfStatement.js
@@ -0,0 +1,2 @@
+if (((typeof ezJsu!=='undefined')&&ezJsu===true)||((typeof _ez_sa !=='undefined')&&_ez_sa === true)) {a.defaultStoreUrl='//g.ezoic.net/ezoic/imp.gif';a.defaultStoreA ='//g.ezoic.net/ezoic/i.gif';// Single Line Comments here
+}
diff --git a/tests/auto/qml/qmlformat/data/mini_esm.formattedTabs.mjs b/tests/auto/qml/qmlformat/data/mini_esm.formattedTabs.mjs
new file mode 100644
index 0000000000..37f8ccb698
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/mini_esm.formattedTabs.mjs
@@ -0,0 +1,20 @@
+//Imports
+import defaultExport from "module-name";
+import "module-name";
+export class ClassName {
+ constructor(h){
+ this.h = h;
+ }
+}
+export const {
+ n1,
+ n3,
+ n4,
+ name2: bar
+} = o;
+export const [name1, name2] = array;
+function stuff() {
+ var l = () => {
+ 1 + 1;
+ };
+}
diff --git a/tests/auto/qml/qmlformat/data/mini_esm.mjs b/tests/auto/qml/qmlformat/data/mini_esm.mjs
new file mode 100644
index 0000000000..9d6c2406dc
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/mini_esm.mjs
@@ -0,0 +1,3 @@
+//Imports
+import defaultExport from "module-name";import "module-name";export class ClassName{constructor(h){this.h=h;}}
+export const {n1,n3,n4,name2:bar}=o;export const [name1,name2]=array;function stuff(){var l=()=>{1+1};};
diff --git a/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml b/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml
index 45d04c5887..46e3e963ea 100644
--- a/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml
@@ -1,11 +1,9 @@
Item {
- Item {
- }
+ Item {}
/* This is a multiline comment.
it should stay attached to Commented instead of getting orphaned.
*/
// This should also stick to Commented
- Commented {
- }
+ Commented {}
}
diff --git a/tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml b/tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml
index 5536ecf513..fc1915f647 100644
--- a/tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/nestedFunctions.formatted.qml
@@ -1,11 +1,13 @@
Item {
function a() {
function nested() {}
+
foo();
}
function b() {
function nested() {}
+
bar();
}
}
diff --git a/tests/auto/qml/qmlformat/data/nestedIf.formatted.qml b/tests/auto/qml/qmlformat/data/nestedIf.formatted.qml
index 4ff5a40a23..ebb125f36d 100644
--- a/tests/auto/qml/qmlformat/data/nestedIf.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/nestedIf.formatted.qml
@@ -26,6 +26,7 @@ Item {
x();
}
}
+
if (x && y)
if (x < y)
return 0;
diff --git a/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml b/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml
new file mode 100644
index 0000000000..94e97076b1
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml
@@ -0,0 +1,137 @@
+import QtQml
+
+QtObject {
+
+ function set1() {
+ const array = [1, 2, 3, 4];
+ const [a, b] = [1, 2];
+ const [aa, , bb] = array;
+ const [aaa = 23, bbb] = array;
+ const [a1, b1, ...rest1] = array;
+ const [a2, , b2, ...rest2] = array;
+ const [a3, b3, ...{
+ pop,
+ push
+ }] = array;
+ const [a4, b4, ...[c, d]] = array;
+
+ const obj = {
+ _a: 1,
+ _b: 2
+ };
+ const {
+ a5,
+ b5
+ } = obj;
+ const {
+ a6: a_,
+ b6: b1_
+ } = obj;
+ const {
+ a7: a11 = 4,
+ b11 = 34,
+ c1: b111,
+ d1
+ } = obj;
+ let key = a;
+ const {
+ [key]: a___
+ } = obj;
+ }
+
+ function set2() {
+ // declare first
+ let a, b, a1, b1, c, d, rest, pop, push;
+ const array = [1, 2, 3, 4];
+ [a, b] = array;
+ [a, , b] = array;
+ [a = aDefault, b] = array;
+ [a, b, ...rest] = array;
+ [a, , b, ...rest] = array;
+ [a, b, ...{
+ pop,
+ push
+ }] = array;
+ [a, b, ...[c, d]] = array;
+
+ const obj = {
+ _a: 1,
+ _b: 2
+ };
+ ({
+ a,
+ b
+ } = obj); // brackets are required
+ ({
+ a: a1,
+ b: b1
+ } = obj);
+
+ const complicatedObject = {
+ a: 1,
+ b: {
+ c: 2,
+ d: {
+ e: 3,
+ f: [4, 5, 6]
+ }
+ },
+ g: [7, 8, 9]
+ };
+
+ const {
+ patron,
+ b: {
+ mafik,
+ d: {
+ e,
+ f: [, secondF, ...restF]
+ }
+ },
+ g: [firstG, ...restG]
+ } = complicatedObject;
+ }
+
+ Component.onCompleted: {
+ const myFunction = myLambda => {
+ const myObject = {
+ a: 1,
+ b: {
+ c: 2,
+ d: [3, 4, 5]
+ },
+ e: {
+ f: 6,
+ g: {
+ h: 7,
+ i: [8, 9, 10]
+ }
+ }
+ };
+
+ myLambda(myObject);
+ };
+
+ myFunction(({
+ a,
+ b: {
+ c,
+ d: [firstD]
+ },
+ e: {
+ f,
+ g: {
+ h,
+ i: [, secondI]
+ }
+ }
+ }) => {
+ console.log(a); // 1
+ console.log(c); // 2
+ console.log(firstD); // 3
+ console.log(f); // 6
+ console.log(h); // 7
+ console.log(secondI); // 9
+ });
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/objectDestructuring.qml b/tests/auto/qml/qmlformat/data/objectDestructuring.qml
new file mode 100644
index 0000000000..0487153125
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/objectDestructuring.qml
@@ -0,0 +1,77 @@
+import QtQml
+
+QtObject {
+
+ function set1() {
+ const array = [1,2,3,4];
+ const [a, b] = [1,2];
+ const [aa, , bb] = array;
+ const [aaa = 23, bbb] = array;
+ const [a1, b1, ...rest1] = array;
+ const [a2, , b2, ...rest2] = array;
+ const [a3, b3, ...{ pop, push }] = array;
+ const [a4, b4, ...[c, d]] = array;
+
+ const obj = {_a:1,_b:2};
+ const { a5, b5 } = obj;
+ const { a6: a_, b6: b1_ } = obj;
+ const { a7: a11 = 4, b11 = 34, c1: b111, d1 } = obj;
+ let key = a;
+ const { [key]: a___ } = obj;
+ }
+
+ function set2() {
+ // declare first
+ let a, b, a1, b1, c, d, rest, pop, push;
+ const array = [1,2,3,4];
+ [a, b] = array;
+ [a, , b] = array;
+ [a = aDefault, b] = array;
+ [a, b, ...rest] = array;
+ [a, , b, ...rest] = array;
+ [a, b, ...{ pop, push }] = array;
+ [a, b, ...[c, d]] = array;
+
+ const obj = {_a:1,_b:2};
+ ({ a, b } = obj); // brackets are required
+ ({ a: a1, b: b1 } = obj);
+
+ const complicatedObject = {
+ a: 1,
+ b: {
+ c: 2,
+ d: {
+ e: 3,
+ f: [4, 5, 6]
+ }
+ },
+ g: [7, 8, 9]
+ };
+
+ const { patron, b: { mafik, d: { e, f: [ , secondF, ...restF ] } }, g: [ firstG, ...restG ] } = complicatedObject;
+ }
+
+ Component.onCompleted: {
+ const myFunction = (myLambda) => {
+ const myObject = {
+ a: 1,
+ b: {c: 2, d: [3, 4, 5] }, e: {
+ f: 6,
+ g: { h: 7, i: [8, 9, 10]
+ }
+ }
+ };
+
+ myLambda(myObject);
+ };
+
+ myFunction(({ a, b: { c, d: [ firstD ] }, e: { f, g: { h, i: [ , secondI ] } } }) => {
+ console.log(a); // 1
+ console.log(c); // 2
+ console.log(firstD); // 3
+ console.log(f); // 6
+ console.log(h); // 7
+ console.log(secondI); // 9
+ });
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml b/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml
index bd0406e595..df26a9b599 100644
--- a/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml
@@ -1,6 +1,9 @@
Item {
+ Button {}
+
Button {
+ id: foo
}
height: 360
diff --git a/tests/auto/qml/qmlformat/data/objectsSpacing.qml b/tests/auto/qml/qmlformat/data/objectsSpacing.qml
index 6adc89778c..0239b05145 100644
--- a/tests/auto/qml/qmlformat/data/objectsSpacing.qml
+++ b/tests/auto/qml/qmlformat/data/objectsSpacing.qml
@@ -3,6 +3,10 @@ Item {
Button {
}
+ Button {
+ id: foo
+ }
+
height: 360
width: 360
diff --git a/tests/auto/qml/qmlformat/data/pragma.formatted.js b/tests/auto/qml/qmlformat/data/pragma.formatted.js
new file mode 100644
index 0000000000..3f57e537ac
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/pragma.formatted.js
@@ -0,0 +1,4 @@
+"use strict";
+{
+ function f() {}
+}
diff --git a/tests/auto/qml/qmlformat/data/pragma.formatted.qml b/tests/auto/qml/qmlformat/data/pragma.formatted.qml
new file mode 100644
index 0000000000..143db39888
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/pragma.formatted.qml
@@ -0,0 +1,8 @@
+pragma Singleton
+pragma ComponentBehavior: Bound
+pragma FunctionSignatureBehavior: Enforced
+pragma ValueTypeBehavior: Copy, Addressable
+
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qmlformat/data/pragma.js b/tests/auto/qml/qmlformat/data/pragma.js
new file mode 100644
index 0000000000..b81d52d853
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/pragma.js
@@ -0,0 +1 @@
+"use strict";{function f(){}}
diff --git a/tests/auto/qml/qmlformat/data/pragma.qml b/tests/auto/qml/qmlformat/data/pragma.qml
new file mode 100644
index 0000000000..7469277395
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/pragma.qml
@@ -0,0 +1,8 @@
+pragma Singleton
+ pragma ComponentBehavior: Bound
+ pragma FunctionSignatureBehavior: Enforced
+pragma ValueTypeBehavior: Copy, Addressable
+
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml b/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml
index 94b5877957..9214014889 100644
--- a/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml
@@ -3,11 +3,11 @@ Item {
var copiedItem = "copied value";
var computedItem = "computedName";
var obj = {
- "identifierName": "identifier value",
+ identifierName: "identifier value",
"string name": "string value",
- "Infinity": "numeric value",
+ Infinity: "numeric value",
[computedItem]: "computed value",
- "copiedItem": copiedItem
+ copiedItem
};
}
}
diff --git a/tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml b/tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml
index b9bcc34e8c..767bfe7e61 100644
--- a/tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml
+++ b/tests/auto/qml/qmlformat/data/settings/Example1.formatted_mac_cr.qml
@@ -1 +1 @@
-/* This file is licensed under the not a license license 1. You may not comply 2. Goodbye */ // Importing this is very important import QtQuick 5.15 // Muddling the waters! import QtQuick.Models 3.14 as muddle // Importing that is important too import Z import That import This // THIS IS VERY IMPORTANT! import Y import X.Z import X.Y import A.LLOHA import A.B.B.A // This comment is related to Item Item { // This to id // Also id. (line 2) // This is the third id // fourth id comment id: foo // This to enum enum Foo { A = 3, // This is A B, // This is B C = 4, // This is C D // This is D } // Orphan comment // Another orphan // More orphans property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3] property bool some_bool: false default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool // some_read_only_bool readonly property bool some_read_only_bool: Math.sin(3) && (aFunc()[30] + 5) | 2 != 0 property bool something_computed: function (x) { const PI = 3, DAYS_PER_YEAR = 365.25; var x = 3 + 2; x["bla"] = 50; // This is an orphan inside something_computed // Are these getting duplicated? // This one to var few! var few = new WhatEver(); x += Math.sin(3); x--; --x; x++; ++x; for (var x = 0; x < 100; x++) { x++; console.log("Foo"); } for (var x in [3, 2, 1]) { y++; console.log("Bar"); } while (true) { console.log("Wee"); } with (foo) { bar; x += 5; } // This is related to with! x3: do { console.log("Hello"); } while (3 == 0) try { dangerous(); } catch (e) { console.log(e); } finally { console.log("What else?"); } switch (x) { case 0: x = 1; break; case 1: x = 5; break; case 4: x = 100; break; } if (x == 50) { console.log("true"); } else if (x == 50) { console.log("other thing"); } else { console.log("false"); } if (x == 50) { console.log("true"); } else if (x == 50) { console.log("other thing"); x--; } else { console.log("false"); } // Another orphan inside something_computed return "foobar"; }() signal say(string name, bool caps) // This one to aFunc() function aFunc() { var x = 3; return x; } x: 3 // Very cool myFavouriteThings: [ // This is an orphan // This is a cool text Text { }, // This is a cool rectangle Rectangle { } ] // This comment is related to the property animation PropertyAnimation on x { id: foo2 x: 3 y: x + 3 } Component.onCompleted: console.log("Foo!") Text { required property string batman signal boo(int count, int times, real duration) text: "Bla" } } \ No newline at end of file
+/* This file is licensed under the not a license license 1. You may not comply 2. Goodbye */ // Importing this is very important import QtQuick 5.15 // Muddling the waters! import QtQuick.Models 3.14 as muddle // Importing that is important too import Z import That import This // THIS IS VERY IMPORTANT! import Y import X.Z import X.Y import A.LLOHA import A.B.B.A // This comment is related to Item Item { // This to id // Also id. (line 2) // This is the third id // fourth id comment id: foo // This to enum enum Foo { A = 3, // This is A B, // This is B C = 4, // This is C D // This is D } // Orphan comment // Another orphan // More orphans property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3] property bool some_bool: false default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool // some_read_only_bool readonly property bool some_read_only_bool: Math.sin(3) && (aFunc()[30] + 5) | 2 != 0 property bool something_computed: function (x) { const PI = 3, DAYS_PER_YEAR = 365.25; var x = 3 + 2; x["bla"] = 50; // This is an orphan inside something_computed // Are these getting duplicated? // This one to var few! var few = new WhatEver(); x += Math.sin(3); x--; --x; x++; ++x; for (var x = 0; x < 100; x++) { x++; console.log("Foo"); } for (var x in [3, 2, 1]) { y++; console.log("Bar"); } while (true) { console.log("Wee"); } with (foo) { bar; x += 5; } // This is related to with! x3: do { console.log("Hello"); } while (3 == 0) try { dangerous(); } catch (e) { console.log(e); } finally { console.log("What else?"); } switch (x) { case 0: x = 1; break; case 1: x = 5; break; case 4: x = 100; break; } if (x == 50) { console.log("true"); } else if (x == 50) { console.log("other thing"); } else { console.log("false"); } if (x == 50) { console.log("true"); } else if (x == 50) { console.log("other thing"); x--; } else { console.log("false"); } // Another orphan inside something_computed return "foobar"; }() signal say(string name, bool caps) // This one to aFunc() function aFunc() { var x = 3; return x; } x: 3 // Very cool myFavouriteThings: [ // This is an orphan // This is a cool text Text { }, // This is a cool rectangle Rectangle { } ] // This comment is related to the property animation PropertyAnimation on x { id: foo2 x: 3 y: x + 3 } Component.onCompleted: console.log("Foo!") Text { required property string batman signal boo(int count, int times, real duration) text: "Bla" } } \ No newline at end of file
diff --git a/tests/auto/qml/qmlformat/data/simpleJSStatement.formatted.js b/tests/auto/qml/qmlformat/data/simpleJSStatement.formatted.js
new file mode 100644
index 0000000000..fbdec1038c
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/simpleJSStatement.formatted.js
@@ -0,0 +1 @@
+let v = 1;
diff --git a/tests/auto/qml/qmlformat/data/simpleJSStatement.js b/tests/auto/qml/qmlformat/data/simpleJSStatement.js
new file mode 100644
index 0000000000..d954a87fc0
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/simpleJSStatement.js
@@ -0,0 +1 @@
+let v=1;
diff --git a/tests/auto/qml/qmlformat/data/simpleLoop.formatted.js b/tests/auto/qml/qmlformat/data/simpleLoop.formatted.js
new file mode 100644
index 0000000000..435fc145bf
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/simpleLoop.formatted.js
@@ -0,0 +1,4 @@
+var a = 1;
+for (var ii = 1; ii < 10; ++ii) {
+ a = a * ii;
+}
diff --git a/tests/auto/qml/qmlformat/data/simpleLoop.js b/tests/auto/qml/qmlformat/data/simpleLoop.js
new file mode 100644
index 0000000000..08b172991f
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/simpleLoop.js
@@ -0,0 +1 @@
+var a=1;for(var ii=1;ii<10;++ii){a=a*ii;}
diff --git a/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.formatted.js b/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.formatted.js
new file mode 100644
index 0000000000..d5b34291e6
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.formatted.js
@@ -0,0 +1,4 @@
+function f() {
+ let a = 0;
+ return a;
+}
diff --git a/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.js b/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.js
new file mode 100644
index 0000000000..c49be5e2bd
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/simpleOnelinerJSFunc.js
@@ -0,0 +1 @@
+function f(){let a=0;return a; }
diff --git a/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml b/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml
index 40cf5068da..923f0642d7 100644
--- a/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml
+++ b/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml
@@ -2,15 +2,12 @@ QtObject {
id: foo
states: [
- State {
- }
+ State {}
]
transitions: [
- Transition {
- }
+ Transition {}
]
// This needs to be *before* states and transitions after formatting
- Item {
- }
+ Item {}
}
diff --git a/tests/auto/qml/qmlformat/data/threeFunctions.formattedFuncSpacing.js b/tests/auto/qml/qmlformat/data/threeFunctions.formattedFuncSpacing.js
new file mode 100644
index 0000000000..592db7a02b
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/threeFunctions.formattedFuncSpacing.js
@@ -0,0 +1,11 @@
+function one() {
+ var a=1;
+}
+
+function two(a, b) {
+ console.log(a,b);
+}
+
+function three(c) {
+ var a=c;
+}
diff --git a/tests/auto/qml/qmlformat/data/threeFunctions.formattedTabs.js b/tests/auto/qml/qmlformat/data/threeFunctions.formattedTabs.js
new file mode 100644
index 0000000000..6575a9fbd8
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/threeFunctions.formattedTabs.js
@@ -0,0 +1,9 @@
+function one() {
+ var a = 1;
+}
+function two(a, b) {
+ console.log(a, b);
+}
+function three(c) {
+ var a = c;
+}
diff --git a/tests/auto/qml/qmlformat/data/threeFunctions.formattedW2.js b/tests/auto/qml/qmlformat/data/threeFunctions.formattedW2.js
new file mode 100644
index 0000000000..afcf8acf02
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/threeFunctions.formattedW2.js
@@ -0,0 +1,9 @@
+function one() {
+ var a = 1;
+}
+function two(a, b) {
+ console.log(a, b);
+}
+function three(c) {
+ var a = c;
+}
diff --git a/tests/auto/qml/qmlformat/data/threeFunctionsOneLine.js b/tests/auto/qml/qmlformat/data/threeFunctionsOneLine.js
new file mode 100644
index 0000000000..648591f221
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/threeFunctionsOneLine.js
@@ -0,0 +1 @@
+function one() {var a=1;}function two(a, b) {console.log(a,b);}function three(c){var a=c;}
diff --git a/tests/auto/qml/qmlformat/data/twoFunctions.formatted.js b/tests/auto/qml/qmlformat/data/twoFunctions.formatted.js
new file mode 100644
index 0000000000..b7414de053
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/twoFunctions.formatted.js
@@ -0,0 +1,12 @@
+function one() {
+ var a = 1;
+ if (a === true) {
+ a = 5;
+ }
+}
+
+function two(a, b) {
+ for (; b < 5; ++b) {
+ a = a * b;
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/twoFunctions.js b/tests/auto/qml/qmlformat/data/twoFunctions.js
new file mode 100644
index 0000000000..b410ebd167
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/twoFunctions.js
@@ -0,0 +1,3 @@
+function one(){var a=1;if (a===true){a=5;}}
+
+function two(a,b){for(;b<5;++b){a=a*b;}}
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index c14dd794fc..da3ebc69a2 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDir>
@@ -16,6 +16,25 @@
using namespace QQmlJS::Dom;
+// TODO refactor extension helpers
+const QString QML_EXT = ".qml";
+const QString JS_EXT = ".js";
+const QString MJS_EXT = ".mjs";
+
+static QStringView fileExt(QStringView filename)
+{
+ if (filename.endsWith(QML_EXT)) {
+ return QML_EXT;
+ }
+ if (filename.endsWith(JS_EXT)) {
+ return JS_EXT;
+ }
+ if (filename.endsWith(MJS_EXT)) {
+ return MJS_EXT;
+ }
+ Q_UNREACHABLE();
+};
+
class TestQmlformat: public QQmlDataTest
{
Q_OBJECT
@@ -27,6 +46,7 @@ public:
private Q_SLOTS:
void initTestCase() override;
+ //actually testFormat tests CLI of qmlformat
void testFormat();
void testFormat_data();
@@ -40,10 +60,19 @@ private Q_SLOTS:
void testBackupFileLimit();
+ void testFilesOption_data();
+ void testFilesOption();
+
+ void plainJS_data();
+ void plainJS();
+
+ void ecmascriptModule();
+
private:
QString readTestFile(const QString &path);
+ //TODO(QTBUG-117849) refactor this helper function
QString runQmlformat(const QString &fileToFormat, QStringList args, bool shouldSucceed = true,
- RunOption rOption = RunOption::OnCopy);
+ RunOption rOption = RunOption::OnCopy, QStringView ext = QML_EXT);
QString formatInMemory(const QString &fileToFormat, bool *didSucceed = nullptr,
LineWriterOptions options = LineWriterOptions(),
WriteOutChecks extraChecks = WriteOutCheck::ReparseCompare,
@@ -52,9 +81,11 @@ private:
QString m_qmlformatPath;
QStringList m_excludedDirs;
QStringList m_invalidFiles;
+ QStringList m_ignoreFiles;
QStringList findFiles(const QDir &);
bool isInvalidFile(const QFileInfo &fileName) const;
+ bool isIgnoredFile(const QFileInfo &fileName) const;
};
// Don't fail on warnings because we read a lot of QML files that might intentionally be malformed.
@@ -85,6 +116,7 @@ void TestQmlformat::initTestCase()
m_excludedDirs << "doc/src/snippets/qtquick1/qtbinding";
m_excludedDirs << "doc/src/snippets/qtquick1/imports";
m_excludedDirs << "tests/manual/v4";
+ m_excludedDirs << "tests/manual/qmllsformatter";
m_excludedDirs << "tests/auto/qml/ecmascripttests";
m_excludedDirs << "tests/auto/qml/qmllint";
@@ -126,6 +158,15 @@ void TestQmlformat::initTestCase()
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml";
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml";
m_invalidFiles << "tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/emptyFile.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/afterDots.qml";
+ m_invalidFiles << "tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/qualifiedModule.qml";
// Files that get changed:
// rewrite of import "bla/bla/.." to import "bla"
@@ -145,6 +186,10 @@ void TestQmlformat::initTestCase()
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml";
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml";
+
+ // These files are too big
+ m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/longQmlFile.qml";
+ m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/deeplyNested.qml";
}
QStringList TestQmlformat::findFiles(const QDir &d)
@@ -157,15 +202,17 @@ QStringList TestQmlformat::findFiles(const QDir &d)
QStringList rv;
- QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
- QDir::Files);
- foreach (const QString &file, files) {
- rv << d.absoluteFilePath(file);
+ const QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
+ QDir::Files);
+ for (const QString &file: files) {
+ QString absoluteFilePath = d.absoluteFilePath(file);
+ if (!isIgnoredFile(QFileInfo(absoluteFilePath)))
+ rv << absoluteFilePath;
}
- QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
- QDir::NoSymLinks);
- foreach (const QString &dir, dirs) {
+ const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
+ QDir::NoSymLinks);
+ for (const QString &dir: dirs) {
QDir sub = d;
sub.cd(dir);
rv << findFiles(sub);
@@ -183,6 +230,15 @@ bool TestQmlformat::isInvalidFile(const QFileInfo &fileName) const
return false;
}
+bool TestQmlformat::isIgnoredFile(const QFileInfo &fileName) const
+{
+ for (const QString &file : m_ignoreFiles) {
+ if (fileName.absoluteFilePath().endsWith(file))
+ return true;
+ }
+ return false;
+}
+
QString TestQmlformat::readTestFile(const QString &path)
{
QFile file(testFile(path));
@@ -304,6 +360,58 @@ void TestQmlformat::testFormat_data()
QTest::newRow("ecmaScriptClassInQml")
<< "ecmaScriptClassInQml.qml"
<< "ecmaScriptClassInQml.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("arrowFunctionWithBinding")
+ << "arrowFunctionWithBinding.qml"
+ << "arrowFunctionWithBinding.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("blanklinesAfterComment")
+ << "blanklinesAfterComment.qml"
+ << "blanklinesAfterComment.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("pragmaValueList")
+ << "pragma.qml"
+ << "pragma.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("objectDestructuring")
+ << "objectDestructuring.qml"
+ << "objectDestructuring.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("destructuringFunctionParameter")
+ << "destructuringFunctionParameter.qml"
+ << "destructuringFunctionParameter.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("ellipsisFunctionArgument")
+ << "ellipsisFunctionArgument.qml"
+ << "ellipsisFunctionArgument.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("importStatements")
+ << "importStatements.qml"
+ << "importStatements.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("arrayEndComma")
+ << "arrayEndComma.qml"
+ << "arrayEndComma.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("escapeChars")
+ << "escapeChars.qml"
+ << "escapeChars.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("javascriptBlock")
+ << "javascriptBlock.qml"
+ << "javascriptBlock.formatted.qml" << QStringList{} << RunOption::OnCopy;
+
+ //plainJS
+ QTest::newRow("nestedLambdaWithIfElse")
+ << "lambdaWithIfElseInsideLambda.js"
+ << "lambdaWithIfElseInsideLambda.formatted.js" << QStringList{} << RunOption::OnCopy;
+
+ QTest::newRow("indentEquals2")
+ << "threeFunctionsOneLine.js"
+ << "threeFunctions.formattedW2.js" << QStringList{"-w=2"} << RunOption::OnCopy;
+
+ QTest::newRow("tabIndents")
+ << "threeFunctionsOneLine.js"
+ << "threeFunctions.formattedTabs.js" << QStringList{"-t"} << RunOption::OnCopy;
+
+ QTest::newRow("normalizedFunctionSpacing")
+ << "threeFunctionsOneLine.js"
+ << "threeFunctions.formattedFuncSpacing.js"
+ << QStringList{ "-n", "--functions-spacing" } << RunOption::OnCopy;
+
+ QTest::newRow("esm_tabIndents")
+ << "mini_esm.mjs"
+ << "mini_esm.formattedTabs.mjs" << QStringList{ "-t" } << RunOption::OnCopy;
}
void TestQmlformat::testFormat()
@@ -313,7 +421,82 @@ void TestQmlformat::testFormat()
QFETCH(QStringList, args);
QFETCH(RunOption, runOption);
- QCOMPARE(runQmlformat(testFile(file), args, true, runOption), readTestFile(fileFormatted));
+ auto formatted = runQmlformat(testFile(file), args, true, runOption, fileExt(file));
+ QEXPECT_FAIL("normalizedFunctionSpacing",
+ "Normalize && function spacing are not yet supported for JS", Abort);
+ auto exp = readTestFile(fileFormatted);
+ QCOMPARE(formatted, exp);
+}
+
+void TestQmlformat::plainJS_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QString>("fileFormatted");
+
+ QTest::newRow("simpleStatement") << "simpleJSStatement.js"
+ << "simpleJSStatement.formatted.js";
+ QTest::newRow("simpleFunction") << "simpleOnelinerJSFunc.js"
+ << "simpleOnelinerJSFunc.formatted.js";
+ QTest::newRow("simpleLoop") << "simpleLoop.js"
+ << "simpleLoop.formatted.js";
+ QTest::newRow("messyIfStatement") << "messyIfStatement.js"
+ << "messyIfStatement.formatted.js";
+ QTest::newRow("lambdaFunctionWithLoop") << "lambdaFunctionWithLoop.js"
+ << "lambdaFunctionWithLoop.formatted.js";
+ QTest::newRow("lambdaWithIfElse") << "lambdaWithIfElse.js"
+ << "lambdaWithIfElse.formatted.js";
+ QTest::newRow("nestedLambdaWithIfElse") << "lambdaWithIfElseInsideLambda.js"
+ << "lambdaWithIfElseInsideLambda.formatted.js";
+ QTest::newRow("twoFunctions") << "twoFunctions.js"
+ << "twoFunctions.formatted.js";
+ QTest::newRow("pragma") << "pragma.js"
+ << "pragma.formatted.js";
+ QTest::newRow("classConstructor") << "class.js"
+ << "class.formatted.js";
+ QTest::newRow("legacyDirectives") << "directives.js"
+ << "directives.formatted.js";
+ QTest::newRow("legacyDirectivesWithComments") << "directivesWithComments.js"
+ << "directivesWithComments.formatted.js";
+}
+
+void TestQmlformat::plainJS()
+{
+ QFETCH(QString, file);
+ QFETCH(QString, fileFormatted);
+
+ bool wasSuccessful;
+ LineWriterOptions opts;
+#ifdef Q_OS_WIN
+ opts.lineEndings = QQmlJS::Dom::LineWriterOptions::LineEndings::Windows;
+#endif
+ QString output = formatInMemory(testFile(file), &wasSuccessful, opts, WriteOutCheck::None);
+
+ QVERIFY(wasSuccessful && !output.isEmpty());
+
+ // TODO(QTBUG-119404)
+ QEXPECT_FAIL("classConstructor", "see QTBUG-119404", Abort);
+ // TODO(QTBUG-119770)
+ QEXPECT_FAIL("legacyDirectivesWithComments", "see QTBUG-119770", Abort);
+ auto exp = readTestFile(fileFormatted);
+ QCOMPARE(output, exp);
+}
+
+void TestQmlformat::ecmascriptModule()
+{
+ QString file("esm.mjs");
+ QString formattedFile("esm.formatted.mjs");
+
+ bool wasSuccessful;
+ LineWriterOptions opts;
+#ifdef Q_OS_WIN
+ opts.lineEndings = QQmlJS::Dom::LineWriterOptions::LineEndings::Windows;
+#endif
+ QString output = formatInMemory(testFile(file), &wasSuccessful, opts, WriteOutCheck::None);
+
+ QVERIFY(wasSuccessful && !output.isEmpty());
+
+ auto exp = readTestFile(formattedFile);
+ QCOMPARE(output, readTestFile(formattedFile));
}
#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
@@ -326,9 +509,23 @@ void TestQmlformat::testExample_data()
QString examples = QLatin1String(SRCDIR) + "/../../../../examples/";
QString tests = QLatin1String(SRCDIR) + "/../../../../tests/";
+ QStringList exampleFiles;
+ QStringList testFiles;
QStringList files;
- files << findFiles(QDir(examples));
- files << findFiles(QDir(tests));
+ exampleFiles << findFiles(QDir(examples));
+ testFiles << findFiles(QDir(tests));
+
+ // Actually this test is an e2e test and not the unit test.
+ // At the moment of writing, CI lacks providing instruments for the automated tests
+ // which might be time-consuming, as for example this one.
+ // Therefore as part of QTBUG-122990 this test was copied to the /manual/e2e/qml/qmlformat
+ // however very small fraction of the test data is still preserved here for the sake of
+ // testing automatically at least a small part of the examples
+ const int nBatch = 10;
+ files << exampleFiles.mid(0, nBatch) << exampleFiles.mid(exampleFiles.size() / 2, nBatch)
+ << exampleFiles.mid(exampleFiles.size() - nBatch, nBatch);
+ files << testFiles.mid(0, nBatch) << testFiles.mid(exampleFiles.size() / 2, nBatch)
+ << testFiles.mid(exampleFiles.size() - nBatch, nBatch);
for (const QString &file : files)
QTest::newRow(qPrintable(file)) << file;
@@ -417,12 +614,79 @@ void TestQmlformat::testBackupFileLimit()
};
}
+void TestQmlformat::testFilesOption_data()
+{
+ QTest::addColumn<QString>("containerFile");
+ QTest::addColumn<QStringList>("individualFiles");
+
+ QTest::newRow("initial") << "fileListToFormat"
+ << QStringList{"valid1.qml", "invalidEntry:cannot be parsed", "valid2.qml"};
+}
+
+void TestQmlformat::testFilesOption()
+{
+ QFETCH(QString, containerFile);
+ QFETCH(QStringList, individualFiles);
+
+ // Create a temporary directory
+ QTemporaryDir tempDir;
+ tempDir.setAutoRemove(false);
+ QStringList actualFormattedFilesPath;
+
+ // Iterate through files in the source directory and copy them to the temporary directory
+ const auto sourceDir = dataDirectory() + QDir::separator() + "filesOption";
+
+ // Create a file that contains the list of files to be formatted
+ const QString tempFilePath = tempDir.path() + QDir::separator() + containerFile;
+ QFile container(tempFilePath);
+ if (container.open(QIODevice::Text | QIODevice::WriteOnly)) {
+ QTextStream out(&container);
+
+ for (const auto &file : individualFiles) {
+ QString destinationFilePath = tempDir.path() + QDir::separator() + file;
+ if (QFile::copy(sourceDir + QDir::separator() + file, destinationFilePath))
+ actualFormattedFilesPath << destinationFilePath;
+ out << destinationFilePath << "\n";
+ }
+
+ container.close();
+ } else {
+ QFAIL("Cannot create temp test file\n");
+ return;
+ }
+
+ {
+ QProcess process;
+ process.start(m_qmlformatPath, QStringList{"-F", tempFilePath});
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+ }
+
+ const auto readFile = [](const QString &filePath){
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "Error on opening the file " << filePath;
+ return QByteArray{};
+ }
+
+ return file.readAll();
+ };
+
+ for (const auto &filePath : actualFormattedFilesPath) {
+ auto expectedFormattedFile = QFileInfo(filePath).fileName();
+ const auto expectedFormattedFilePath = sourceDir + QDir::separator() +
+ expectedFormattedFile.replace(".qml", ".formatted.qml");
+
+ QCOMPARE(readFile(filePath), readFile(expectedFormattedFilePath));
+ }
+}
+
QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList args,
- bool shouldSucceed, RunOption rOptions)
+ bool shouldSucceed, RunOption rOptions, QStringView ext)
{
// Copy test file to temporary location
QTemporaryDir tempDir;
- const QString tempFile = tempDir.path() + QDir::separator() + "to_format.qml";
+ const QString tempFile = (tempDir.path() + QDir::separator() + "to_format") % ext;
if (rOptions == RunOption::OnCopy) {
QFile::copy(fileToFormat, tempFile);
@@ -446,7 +710,8 @@ QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList arg
QFile temp(tempFile);
- temp.open(QIODevice::ReadOnly);
+ if (!temp.open(QIODevice::ReadOnly))
+ qFatal("Could not open %s", qPrintable(tempFile));
QString formatted = QString::fromUtf8(temp.readAll());
return formatted;
@@ -456,19 +721,17 @@ QString TestQmlformat::formatInMemory(const QString &fileToFormat, bool *didSucc
LineWriterOptions options, WriteOutChecks extraChecks,
WriteOutChecks largeChecks)
{
- DomItem env = DomEnvironment::create(
+ auto env = DomEnvironment::create(
QStringList(), // as we load no dependencies we do not need any paths
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
DomItem tFile;
- env.loadFile(
- fileToFormat, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ env->loadFile(FileToLoad::fromFileSystem(env, fileToFormat),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ env->loadPendingDependencies();
MutableDomItem myFile = tFile.field(Fields::currentItem);
- DomItem writtenOut;
+ bool writtenOut;
QString resultStr;
if (myFile.field(Fields::isValid).value().toBool()) {
WriteOutChecks checks = extraChecks;
@@ -486,7 +749,7 @@ QString TestQmlformat::formatInMemory(const QString &fileToFormat, bool *didSucc
res.flush();
}
if (didSucceed)
- *didSucceed = bool(writtenOut);
+ *didSucceed = writtenOut;
return resultStr;
}
diff --git a/tests/auto/qml/qmlimportscanner/CMakeLists.txt b/tests/auto/qml/qmlimportscanner/CMakeLists.txt
index 042e8fd3d5..a1a22d7309 100644
--- a/tests/auto/qml/qmlimportscanner/CMakeLists.txt
+++ b/tests/auto/qml/qmlimportscanner/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmlimportscanner Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlimportscanner LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json
index a688511ed0..028685f566 100644
--- a/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json
@@ -53,5 +53,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json
index 084e4354b3..77faf99e6c 100644
--- a/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json
@@ -43,5 +43,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json
index c50fe5ab51..cf446c33bd 100644
--- a/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json
@@ -53,5 +53,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json b/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json
index 447c664785..0a885f058e 100644
--- a/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json
+++ b/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json
@@ -38,5 +38,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/Imports.json b/tests/auto/qml/qmlimportscanner/data/Imports.json
index 5e23e7b1ad..20b9c524c4 100644
--- a/tests/auto/qml/qmlimportscanner/data/Imports.json
+++ b/tests/auto/qml/qmlimportscanner/data/Imports.json
@@ -53,5 +53,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json b/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json
index 06df9295fe..f07b7e8494 100644
--- a/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json
+++ b/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json
@@ -54,5 +54,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json b/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json
index 213798d04d..3aa7bc8282 100644
--- a/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json
+++ b/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json
@@ -48,5 +48,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/Simple.qml.json b/tests/auto/qml/qmlimportscanner/data/Simple.qml.json
index 213798d04d..3aa7bc8282 100644
--- a/tests/auto/qml/qmlimportscanner/data/Simple.qml.json
+++ b/tests/auto/qml/qmlimportscanner/data/Simple.qml.json
@@ -48,5 +48,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/Singleton.json b/tests/auto/qml/qmlimportscanner/data/Singleton.json
index cb24e57f60..90f0ff19ad 100644
--- a/tests/auto/qml/qmlimportscanner/data/Singleton.json
+++ b/tests/auto/qml/qmlimportscanner/data/Singleton.json
@@ -53,5 +53,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/Things.json b/tests/auto/qml/qmlimportscanner/data/Things.json
index ee5caecfda..7782dd7c5f 100644
--- a/tests/auto/qml/qmlimportscanner/data/Things.json
+++ b/tests/auto/qml/qmlimportscanner/data/Things.json
@@ -54,5 +54,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json b/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json
index 2d2b602fb1..8fe2da0078 100644
--- a/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json
+++ b/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json
@@ -42,5 +42,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/localImport.qml.json b/tests/auto/qml/qmlimportscanner/data/localImport.qml.json
index ee5caecfda..7782dd7c5f 100644
--- a/tests/auto/qml/qmlimportscanner/data/localImport.qml.json
+++ b/tests/auto/qml/qmlimportscanner/data/localImport.qml.json
@@ -54,5 +54,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json b/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json
index 213798d04d..3aa7bc8282 100644
--- a/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json
+++ b/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json
@@ -48,5 +48,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json b/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json
index ee5caecfda..7782dd7c5f 100644
--- a/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json
+++ b/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json
@@ -54,5 +54,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json b/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json
index 447c664785..0a885f058e 100644
--- a/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json
+++ b/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json
@@ -38,5 +38,9 @@
"relativePath": "QtQml/WorkerScript",
"type": "module",
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
+ },
+ {
+ "name": "QML",
+ "type": "module"
}
]
diff --git a/tests/auto/qml/qmlimportscanner/data/rootPath.json b/tests/auto/qml/qmlimportscanner/data/rootPath.json
index 942c8d6000..b468a8acb1 100644
--- a/tests/auto/qml/qmlimportscanner/data/rootPath.json
+++ b/tests/auto/qml/qmlimportscanner/data/rootPath.json
@@ -50,6 +50,10 @@
"prefer": ":/qt-project.org/imports/QtQml/WorkerScript/"
},
{
+ "name": "QML",
+ "type": "module"
+ },
+ {
"name": "QTBUG-45916.js",
"type": "javascript"
},
diff --git a/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp b/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp
index 02cebc349c..49b3418401 100644
--- a/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp
+++ b/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QProcess>
@@ -149,7 +149,7 @@ void TestQmlimportscanner::runQmlimportscanner(const QString &mode, const QStrin
QVERIFY(generated.isArray());
QFile imports(resultFile);
- imports.open(QIODevice::ReadOnly);
+ QVERIFY(imports.open(QIODevice::ReadOnly));
QJsonDocument expected = QJsonDocument::fromJson(imports.readAll(), &error);
QCOMPARE(error.error, QJsonParseError::NoError);
QVERIFY(expected.isArray());
diff --git a/tests/auto/qml/qmllint/CMakeLists.txt b/tests/auto/qml/qmllint/CMakeLists.txt
index 422e7a08b3..5a3e2d9c0d 100644
--- a/tests/auto/qml/qmllint/CMakeLists.txt
+++ b/tests/auto/qml/qmllint/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmllint Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmllint LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -42,9 +48,15 @@ qt_internal_extend_target(tst_qmllint CONDITION NOT ANDROID AND NOT IOS
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
)
+if (TARGET qmllint)
+ add_dependencies(tst_qmllint Qt::qmllint)
+endif()
+
if (TARGET qmljsrootgen)
qt_internal_extend_target(tst_qmllint
DEFINES
QT_QMLJSROOTGEN_PRESENT
)
+
+ add_dependencies(tst_qmllint Qt::qmljsrootgen)
endif()
diff --git a/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml
new file mode 100644
index 0000000000..4141884af9
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string myProperty
+}
diff --git a/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir
new file mode 100644
index 0000000000..b6e958d657
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir
@@ -0,0 +1,2 @@
+module ModuleInImportPath
+A 1.0 A.qml
diff --git a/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml b/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml
new file mode 100644
index 0000000000..8b20fe9ae9
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml
@@ -0,0 +1,14 @@
+import QtQuick
+
+Item {
+ id: item
+ visible: true
+
+ enum Mode {
+ Hours,
+ Minutes
+ }
+
+ property int mode: item.Mode.Hours
+ property string s: item.mode === IsNotAnEntryOfEnum.Mode.Hour ? "green" : "tomato"
+}
diff --git a/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes b/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes
new file mode 100644
index 0000000000..b5baa22357
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes
@@ -0,0 +1,31 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by qmltyperegistrar.
+
+Module {
+ Component {
+ file: "AppManager.h"
+ name: "AppManager"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["LocaleTest/AppManager 1.0"]
+ isCreatable: false
+ isSingleton: true
+ exportMetaObjectRevisions: [256]
+ Property {
+ name: "primaryLocale"
+ type: "QLocale"
+ read: "getPrimaryLocale"
+ notify: "primaryLocaleChanged"
+ index: 0
+ isReadonly: true
+ }
+ Signal {
+ name: "primaryLocaleChanged"
+ Parameter { type: "QLocale" }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/LocaleTest/qmldir b/tests/auto/qml/qmllint/data/LocaleTest/qmldir
new file mode 100644
index 0000000000..2dc0799d5b
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/LocaleTest/qmldir
@@ -0,0 +1,3 @@
+module LocaleTest
+typeinfo localeTest.qmltypes
+depends QtQml
diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml b/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml
new file mode 100644
index 0000000000..5dda9a4d12
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ property int o: 12
+}
diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc b/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc
new file mode 100644
index 0000000000..b6df21ec4c
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc
@@ -0,0 +1,10 @@
+<RCC>
+ <qresource prefix="/qt/qml/MultiDirectory">
+ <file>qmldir</file>
+ <file>Outer.qml</file>
+ <file>qml/qmldir</file>
+ <file>qml/Inner.qml</file>
+ <file>qml/pages/qmldir</file>
+ <file>qml/pages/Page.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml b/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml
new file mode 100644
index 0000000000..01b7c331d9
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+Outer {
+ o: 25
+}
diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml
new file mode 100644
index 0000000000..7efc889fb1
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+Inner {
+ o: 32
+}
diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir
new file mode 100644
index 0000000000..56ef19d41b
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir
@@ -0,0 +1 @@
+prefer :/qt/qml/MultiDirectory/
diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir
new file mode 100644
index 0000000000..56ef19d41b
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir
@@ -0,0 +1 @@
+prefer :/qt/qml/MultiDirectory/
diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qmldir
new file mode 100644
index 0000000000..699132dc4f
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MultiDirectory/qmldir
@@ -0,0 +1,5 @@
+module MultiDirectory
+prefer :/qt/qml/MultiDirectory
+Outer 1.0 Outer.qml
+Inner 1.0 qml/Inner.qml
+Page 1.0 qml/pages/Page.qml
diff --git a/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes b/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes
new file mode 100644
index 0000000000..92c1b0953e
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes
@@ -0,0 +1,112 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by qmltyperegistrar.
+
+Module {
+ Component {
+ file: "mystyle.h"
+ name: "MyStyle"
+ accessSemantics: "reference"
+ prototype: "QQuickAttachedPropertyPropagator"
+ exports: ["MyStyle/MyStyle 1.0", "MyStyle/MyStyle 254.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [256, 65024]
+ attachedType: "MyStyle"
+ Enum {
+ name: "Theme"
+ values: ["Light", "Dark"]
+ }
+ Property {
+ name: "theme"
+ type: "Theme"
+ read: "theme"
+ write: "setTheme"
+ reset: "resetTheme"
+ notify: "themeChanged"
+ index: 0
+ isFinal: true
+ }
+ Property {
+ name: "windowColor"
+ type: "QColor"
+ read: "windowColor"
+ notify: "themeChanged"
+ index: 1
+ isReadonly: true
+ isFinal: true
+ }
+ Property {
+ name: "windowTextColor"
+ type: "QColor"
+ read: "windowTextColor"
+ notify: "themeChanged"
+ index: 2
+ isReadonly: true
+ isFinal: true
+ }
+ Property {
+ name: "buttonColor"
+ type: "QColor"
+ read: "buttonColor"
+ notify: "themeChanged"
+ index: 3
+ isReadonly: true
+ isFinal: true
+ }
+ Property {
+ name: "buttonTextColor"
+ type: "QColor"
+ read: "buttonTextColor"
+ notify: "themeChanged"
+ index: 4
+ isReadonly: true
+ isFinal: true
+ }
+ Property {
+ name: "toolBarColor"
+ type: "QColor"
+ read: "toolBarColor"
+ notify: "themeChanged"
+ index: 5
+ isReadonly: true
+ isFinal: true
+ }
+ Property {
+ name: "popupColor"
+ type: "QColor"
+ read: "popupColor"
+ notify: "themeChanged"
+ index: 6
+ isReadonly: true
+ isFinal: true
+ }
+ Property {
+ name: "popupBorderColor"
+ type: "QColor"
+ read: "popupBorderColor"
+ notify: "themeChanged"
+ index: 7
+ isReadonly: true
+ isFinal: true
+ }
+ Property {
+ name: "backgroundDimColor"
+ type: "QColor"
+ read: "backgroundDimColor"
+ notify: "themeChanged"
+ index: 8
+ isReadonly: true
+ isFinal: true
+ }
+ Signal { name: "themeChanged" }
+ }
+ Component {
+ file: "qquickattachedpropertypropagator.h"
+ name: "QQuickAttachedPropertyPropagator"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml b/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml
new file mode 100644
index 0000000000..5920797d9b
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml
@@ -0,0 +1,12 @@
+import QtQuick
+import QtQuick.Templates as T
+import MyStyle
+
+T.ToolBar {
+ id: control
+
+ property color c: MyStyle.toolBarColor
+ background: Rectangle {
+ color: MyStyle.toolBarColor
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/MyStyle/qmldir b/tests/auto/qml/qmllint/data/MyStyle/qmldir
new file mode 100644
index 0000000000..8cc4246f7c
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/MyStyle/qmldir
@@ -0,0 +1,4 @@
+module MyStyle
+typeinfo MyStyle.qmltypes
+ToolBar 254.0 ToolBar.qml
+import QtQuick.Controls.Material
diff --git a/tests/auto/qml/qmllint/data/NeedImportPath.qml b/tests/auto/qml/qmllint/data/NeedImportPath.qml
new file mode 100644
index 0000000000..0a63b58f7c
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/NeedImportPath.qml
@@ -0,0 +1,5 @@
+import ModuleInImportPath
+
+A {
+ myProperty: "Hello World"
+}
diff --git a/tests/auto/qml/qmllint/data/Qtbug111015/qmldir b/tests/auto/qml/qmllint/data/Qtbug111015/qmldir
new file mode 100644
index 0000000000..3bf1d48e13
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/Qtbug111015/qmldir
@@ -0,0 +1,3 @@
+module Qtbug111015
+typeinfo qtbug111015.qmltypes
+import QtQml
diff --git a/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes b/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes
new file mode 100644
index 0000000000..cad6d88cc3
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes
@@ -0,0 +1,12 @@
+import QtQuick.tooling 1.2
+
+Module {
+ Component {
+ file: "typewithjsonobjectlist.h"
+ name: "TypeWithJsonObjectList"
+ exports: ["QmlLintTestLib/TypeWithJsonObjectList 1.0"]
+ accessSemantics: "reference"
+ prototype: "QObject"
+ Property { name: "jsonObjectList"; type: "QJsonObject"; isList: true; read: "getJsonObjectList"; index: 0; isReadonly: true }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/SharedFunctions.js b/tests/auto/qml/qmllint/data/SharedFunctions.js
new file mode 100644
index 0000000000..3398d2d6d7
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/SharedFunctions.js
@@ -0,0 +1,5 @@
+.pragma library
+
+function setColorAlpha(color, alpha) {
+ return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
+}
diff --git a/tests/auto/qml/qmllint/data/StringToDateTime/qmldir b/tests/auto/qml/qmllint/data/StringToDateTime/qmldir
new file mode 100644
index 0000000000..761f613496
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/StringToDateTime/qmldir
@@ -0,0 +1,3 @@
+module StringToDateTime
+typeinfo stringToDateTime.qmltypes
+import QtQml
diff --git a/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes b/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes
new file mode 100644
index 0000000000..e0f13fa2ec
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes
@@ -0,0 +1,14 @@
+import QtQuick.tooling 1.2
+Module {
+ Component {
+ file: "stringToQDateTime.h"
+ name: "StringToDateTimeComponent"
+ exports: ["QmlLintTestLib/StringToDateTime 1.0"]
+ exportMetaObjectRevisions: [256]
+ accessSemantics: "reference"
+ prototype: "QObject"
+ Property { name: "aDate"; type: "QDate"; read: "getADate"; write: "setADate" }
+ Property { name: "aTime"; type: "QTime"; read: "getATime"; write: "setATime" }
+ Property { name: "aDateTime"; type: "QDateTime"; read: "getADateTime"; write: "setADateTime" }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes b/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes
index 04ca30c184..77ad6a3cef 100644
--- a/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes
+++ b/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes
@@ -178,4 +178,24 @@ Module {
exportMetaObjectRevisions: [256]
Method { name: "createObject"; isJavaScriptFunction: true }
}
+ Component {
+ file: "variantMapLookup.h"
+ name: "VariantMapLookupFoo"
+ prototype: "QObject"
+ exports: ["TestTypes/VariantMapLookupFoo 1.0"]
+ exportMetaObjectRevisions: [256]
+ Property {
+ name: "data"
+ type: "QVariantMap"
+ read: "data"
+ index: 0
+ }
+ }
+ Component {
+ file: "foo.h"
+ name: "Foo"
+ prototype: "QObject"
+ exports: ["TestTypes/Foo 1.0"]
+ Method { name: "print" }
+ }
}
diff --git a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes
index 9d36d91a90..47ae34cc00 100644
--- a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes
+++ b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes
@@ -12,6 +12,7 @@ Module {
exports: ["Things/SomethingEntirelyStrange 1.0"]
Enum {
name: "AnEnum"
+ isScoped: true
values: {
"AAA": 0,
"BBB": 1,
@@ -20,7 +21,7 @@ Module {
}
Enum {
name: "TheEnum"
- scoped: false
+ isScoped: false
values: {
"V1": 0,
"V2": 1
@@ -96,4 +97,21 @@ Module {
Property { name: "foo"; type: "string" }
hasCustomParser: true
}
+ Component {
+ file: "mediaplayer-qml.h"
+ name: "MediaPlayerStateMachine"
+ accessSemantics: "value"
+ exports: ["Mediaplayer/MediaPlayerStateMachine 1.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [256]
+ }
+ Component {
+ file: "constinvokable.h"
+ name: "ConstInvokable"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["Things/ConstInvokable 1.0"]
+ exportMetaObjectRevisions: [256]
+ Method { name: "getObject"; type: "QObject"; isPointer: true; isConstant: true }
+ }
}
diff --git a/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml b/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml
new file mode 100644
index 0000000000..197b74fa60
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ Rectangle {
+ property real divisor: 0
+
+ Timer {
+ onTriggered: divisor = 1
+ }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml b/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml
new file mode 100644
index 0000000000..198e2146f5
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ Rectangle {
+ property real divisor: 0
+
+ Timer {
+ onTriggered: function() {"use strict"; divisor = 1 }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/addressableValue.qml b/tests/auto/qml/qmllint/data/addressableValue.qml
new file mode 100644
index 0000000000..9b93728ed8
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/addressableValue.qml
@@ -0,0 +1,8 @@
+pragma ValueTypeBehavior: Addressable
+
+import QtQml
+import scripts
+
+QtObject {
+ property var v: "red" as vvv
+}
diff --git a/tests/auto/qml/qmllint/data/attachedImportUse.qml b/tests/auto/qml/qmllint/data/attachedImportUse.qml
new file mode 100644
index 0000000000..56b4e2bf7c
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/attachedImportUse.qml
@@ -0,0 +1,7 @@
+import QtQml
+import TestTypes
+
+QtObject {
+ id: control
+ objectName: control.BirthdayParty.objectName
+}
diff --git a/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml b/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml
new file mode 100644
index 0000000000..cdc2b28c07
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+Item {
+
+ property alias innerObj: {
+ id: inner
+ }
+
+}
diff --git a/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes
new file mode 100644
index 0000000000..96f98dd14e
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes
@@ -0,0 +1,513 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -builtins'
+
+Module {
+ dependencies: []
+ Component {
+ name: "Qt"
+ Enum {
+ name: "GlobalColor"
+ values: {
+ "color0": 0,
+ "color1": 1,
+ "black": 2,
+ "white": 3,
+ "darkGray": 4,
+ "gray": 5,
+ "lightGray": 6,
+ "red": 7,
+ "green": 8,
+ "blue": 9,
+ "cyan": 10,
+ "magenta": 11,
+ "yellow": 12,
+ "darkRed": 13,
+ "darkGreen": 14,
+ "darkBlue": 15,
+ "darkCyan": 16,
+ "darkMagenta": 17,
+ "darkYellow": 18,
+ "transparent": 19
+ }
+ }
+ Enum {
+ name: "KeyboardModifiers"
+ values: {
+ "NoModifier": 0,
+ "ShiftModifier": 33554432,
+ "ControlModifier": 67108864,
+ "AltModifier": 134217728,
+ "MetaModifier": 268435456,
+ "KeypadModifier": 536870912,
+ "GroupSwitchModifier": 1073741824,
+ "KeyboardModifierMask": -33554432
+ }
+ }
+ Enum {
+ name: "MouseButtons"
+ values: {
+ "NoButton": 0,
+ "LeftButton": 1,
+ "RightButton": 2,
+ "MidButton": 4, // For backwards compatibility
+ "MiddleButton": 4,
+ "BackButton": 8,
+ "XButton1": 8,
+ "ExtraButton1": 8,
+ "ForwardButton": 16,
+ "XButton2": 16,
+ "ExtraButton2": 16,
+ "TaskButton": 32,
+ "ExtraButton3": 32,
+ "ExtraButton4": 64,
+ "ExtraButton5": 128,
+ "ExtraButton6": 256,
+ "ExtraButton7": 512,
+ "ExtraButton8": 1024,
+ "ExtraButton9": 2048,
+ "ExtraButton10": 4096,
+ "ExtraButton11": 8192,
+ "ExtraButton12": 16384,
+ "ExtraButton13": 32768,
+ "ExtraButton14": 65536,
+ "ExtraButton15": 131072,
+ "ExtraButton16": 262144,
+ "ExtraButton17": 524288,
+ "ExtraButton18": 1048576,
+ "ExtraButton19": 2097152,
+ "ExtraButton20": 4194304,
+ "ExtraButton21": 8388608,
+ "ExtraButton22": 16777216,
+ "ExtraButton23": 33554432,
+ "ExtraButton24": 67108864,
+ "AllButtons": 134217727,
+ "MaxMouseButton": 67108864,
+ "MouseButtonMask": -1
+ }
+ }
+ Enum {
+ name: "Orientation"
+ values: {
+ "Horizontal": 1,
+ "Vertical": 2
+ }
+ }
+ Enum {
+ name: "Orientations"
+ values: {
+ "Horizontal": 1,
+ "Vertical": 2
+ }
+ }
+ Enum {
+ name: "FocusPolicy"
+ values: {
+ "NoFocus": 0,
+ "TabFocus": 1,
+ "ClickFocus": 2,
+ "StrongFocus": 11,
+ "WheelFocus": 15
+ }
+ }
+ Enum {
+ name: "TabFocusBehavior"
+ values: {
+ "NoTabFocus": 0,
+ "TabFocusTextControls": 1,
+ "TabFocusListControls": 2,
+ "TabFocusAllControls": 255
+ }
+ }
+ Enum {
+ name: "SortOrder"
+ values: {
+ "AscendingOrder": 0,
+ "DescendingOrder": 1
+ }
+ }
+ Enum {
+ name: "SplitBehavior"
+ values: {
+ "KeepEmptyParts": 0,
+ "SkipEmptyParts": 1
+ }
+ }
+ Enum {
+ name: "Alignment"
+ values: {
+ "AlignLeft": 1,
+ "AlignLeading": 1,
+ "AlignRight": 2,
+ "AlignTrailing": 2,
+ "AlignHCenter": 4,
+ "AlignJustify": 8,
+ "AlignAbsolute": 16,
+ "AlignHorizontal_Mask": 31,
+ "AlignTop": 32,
+ "AlignBottom": 64,
+ "AlignVCenter": 128,
+ "AlignBaseline": 256,
+ "AlignVertical_Mask": 480,
+ "AlignCenter": 132
+ }
+ }
+ Enum {
+ name: "TextFlag"
+ values: {
+ "TextSingleLine": 256,
+ "TextDontClip": 512,
+ "TextExpandTabs": 1024,
+ "TextShowMnemonic": 2048,
+ "TextWordWrap": 4096,
+ "TextWrapAnywhere": 8192,
+ "TextDontPrint": 16384,
+ "TextIncludeTrailingSpaces": 134217728,
+ "TextHideMnemonic": 32768,
+ "TextJustificationForced": 65536,
+ "TextForceLeftToRight": 131072,
+ "TextForceRightToLeft": 262144,
+ "TextLongestVariant": 524288,
+ "TextBypassShaping": 1048576
+ }
+ }
+ Enum {
+ name: "TextElideMode"
+ values: {
+ "ElideLeft": 0,
+ "ElideRight": 1,
+ "ElideMiddle": 2,
+ "ElideNone": 3
+ }
+ }
+ Enum {
+ name: "WindowType"
+ values: {
+ "Widget": 0,
+ "Window": 1,
+ "Dialog": 3,
+ "Sheet": 5,
+ "Drawer": 7,
+ "Popup": 9,
+ "Tool": 11,
+ "ToolTip": 13,
+ "SplashScreen": 15,
+ "Desktop": 17,
+ "SubWindow": 18,
+ "ForeignWindow": 33,
+ "CoverWindow": 65,
+ "WindowType_Mask": 255,
+ "MSWindowsFixedSizeDialogHint": 256,
+ "MSWindowsOwnDC": 512,
+ "BypassWindowManagerHint": 1024,
+ "X11BypassWindowManagerHint": 1024,
+ "FramelessWindowHint": 2048,
+ "WindowTitleHint": 4096,
+ "WindowSystemMenuHint": 8192,
+ "WindowMinimizeButtonHint": 16384,
+ "WindowMaximizeButtonHint": 32768,
+ "WindowMinMaxButtonsHint": 49152,
+ "WindowContextHelpButtonHint": 65536,
+ "WindowShadeButtonHint": 131072,
+ "WindowStaysOnTopHint": 262144,
+ "WindowTransparentForInput": 524288,
+ "WindowOverridesSystemGestures": 1048576,
+ "WindowDoesNotAcceptFocus": 2097152,
+ "MaximizeUsingFullscreenGeometryHint": 4194304,
+ "CustomizeWindowHint": 33554432,
+ "WindowStaysOnBottomHint": 67108864,
+ "WindowCloseButtonHint": 134217728,
+ "MacWindowToolBarButtonHint": 268435456,
+ "BypassGraphicsProxyWidget": 536870912,
+ "NoDropShadowWindowHint": 1073741824,
+ "WindowFullscreenButtonHint": -2147483648
+ }
+ }
+ Enum {
+ name: "WindowFlags"
+ values: {
+ "Widget": 0,
+ "Window": 1,
+ "Dialog": 3,
+ "Sheet": 5,
+ "Drawer": 7,
+ "Popup": 9,
+ "Tool": 11,
+ "ToolTip": 13,
+ "SplashScreen": 15,
+ "Desktop": 17,
+ "SubWindow": 18,
+ "ForeignWindow": 33,
+ "CoverWindow": 65,
+ "WindowType_Mask": 255,
+ "MSWindowsFixedSizeDialogHint": 256,
+ "MSWindowsOwnDC": 512,
+ "BypassWindowManagerHint": 1024,
+ "X11BypassWindowManagerHint": 1024,
+ "FramelessWindowHint": 2048,
+ "WindowTitleHint": 4096,
+ "WindowSystemMenuHint": 8192,
+ "WindowMinimizeButtonHint": 16384,
+ "WindowMaximizeButtonHint": 32768,
+ "WindowMinMaxButtonsHint": 49152,
+ "WindowContextHelpButtonHint": 65536,
+ "WindowShadeButtonHint": 131072,
+ "WindowStaysOnTopHint": 262144,
+ "WindowTransparentForInput": 524288,
+ "WindowOverridesSystemGestures": 1048576,
+ "WindowDoesNotAcceptFocus": 2097152,
+ "MaximizeUsingFullscreenGeometryHint": 4194304,
+ "CustomizeWindowHint": 33554432,
+ "WindowStaysOnBottomHint": 67108864,
+ "WindowCloseButtonHint": 134217728,
+ "MacWindowToolBarButtonHint": 268435456,
+ "BypassGraphicsProxyWidget": 536870912,
+ "NoDropShadowWindowHint": 1073741824,
+ "WindowFullscreenButtonHint": -2147483648
+ }
+ }
+ Enum {
+ name: "WindowState"
+ values: {
+ "WindowNoState": 0,
+ "WindowMinimized": 1,
+ "WindowMaximized": 2,
+ "WindowFullScreen": 4,
+ "WindowActive": 8
+ }
+ }
+ Enum {
+ name: "WindowStates"
+ values: {
+ "WindowNoState": 0,
+ "WindowMinimized": 1,
+ "WindowMaximized": 2,
+ "WindowFullScreen": 4,
+ "WindowActive": 8
+ }
+ }
+ Enum {
+ name: "ApplicationState"
+ values: {
+ "ApplicationSuspended": 0,
+ "ApplicationHidden": 1,
+ "ApplicationInactive": 2,
+ "ApplicationActive": 4
+ }
+ }
+ Enum {
+ name: "ScreenOrientation"
+ values: {
+ "PrimaryOrientation": 0,
+ "PortraitOrientation": 1,
+ "LandscapeOrientation": 2,
+ "InvertedPortraitOrientation": 4,
+ "InvertedLandscapeOrientation": 8
+ }
+ }
+ Enum {
+ name: "ScreenOrientations"
+ values: {
+ "PrimaryOrientation": 0,
+ "PortraitOrientation": 1,
+ "LandscapeOrientation": 2,
+ "InvertedPortraitOrientation": 4,
+ "InvertedLandscapeOrientation": 8
+ }
+ }
+ Enum {
+ name: "WidgetAttribute"
+ values: {
+ "WA_Disabled": 0,
+ "WA_UnderMouse": 1,
+ "WA_MouseTracking": 2,
+ "WA_ContentsPropagated": 3,
+ "WA_OpaquePaintEvent": 4,
+ "WA_NoBackground": 4,
+ "WA_StaticContents": 5,
+ "WA_LaidOut": 7,
+ "WA_PaintOnScreen": 8,
+ "WA_NoSystemBackground": 9,
+ "WA_UpdatesDisabled": 10,
+ "WA_Mapped": 11,
+ "WA_MacNoClickThrough": 12,
+ "WA_InputMethodEnabled": 14,
+ "WA_WState_Visible": 15,
+ "WA_WState_Hidden": 16,
+ "WA_ForceDisabled": 32,
+ "WA_KeyCompression": 33,
+ "WA_PendingMoveEvent": 34,
+ "WA_PendingResizeEvent": 35,
+ "WA_SetPalette": 36,
+ "WA_SetFont": 37,
+ "WA_SetCursor": 38,
+ "WA_NoChildEventsFromChildren": 39,
+ "WA_WindowModified": 41,
+ "WA_Resized": 42,
+ "WA_Moved": 43,
+ "WA_PendingUpdate": 44,
+ "WA_InvalidSize": 45,
+ "WA_MacBrushedMetal": 46,
+ "WA_MacMetalStyle": 46,
+ "WA_CustomWhatsThis": 47,
+ "WA_LayoutOnEntireRect": 48,
+ "WA_OutsideWSRange": 49,
+ "WA_GrabbedShortcut": 50,
+ "WA_TransparentForMouseEvents": 51,
+ "WA_PaintUnclipped": 52,
+ "WA_SetWindowIcon": 53,
+ "WA_NoMouseReplay": 54,
+ "WA_DeleteOnClose": 55,
+ "WA_RightToLeft": 56,
+ "WA_SetLayoutDirection": 57,
+ "WA_NoChildEventsForParent": 58,
+ "WA_ForceUpdatesDisabled": 59,
+ "WA_WState_Created": 60,
+ "WA_WState_CompressKeys": 61,
+ "WA_WState_InPaintEvent": 62,
+ "WA_WState_Reparented": 63,
+ "WA_WState_ConfigPending": 64,
+ "WA_WState_Polished": 66,
+ "WA_WState_DND": 67,
+ "WA_WState_OwnSizePolicy": 68,
+ "WA_WState_ExplicitShowHide": 69,
+ "WA_ShowModal": 70,
+ "WA_MouseNoMask": 71,
+ "WA_GroupLeader": 72,
+ "WA_NoMousePropagation": 73,
+ "WA_Hover": 74,
+ "WA_InputMethodTransparent": 75,
+ "WA_QuitOnClose": 76,
+ "WA_KeyboardFocusChange": 77,
+ "WA_AcceptDrops": 78,
+ "WA_DropSiteRegistered": 79,
+ "WA_ForceAcceptDrops": 79,
+ "WA_WindowPropagation": 80,
+ "WA_NoX11EventCompression": 81,
+ "WA_TintedBackground": 82,
+ "WA_X11OpenGLOverlay": 83,
+ "WA_AlwaysShowToolTips": 84,
+ "WA_MacOpaqueSizeGrip": 85,
+ "WA_SetStyle": 86,
+ "WA_SetLocale": 87,
+ "WA_MacShowFocusRect": 88,
+ "WA_MacNormalSize": 89,
+ "WA_MacSmallSize": 90,
+ "WA_MacMiniSize": 91,
+ "WA_LayoutUsesWidgetRect": 92,
+ "WA_StyledBackground": 93,
+ "WA_MSWindowsUseDirect3D": 94,
+ "WA_CanHostQMdiSubWindowTitleBar": 95,
+ "WA_MacAlwaysShowToolWindow": 96,
+ "WA_StyleSheet": 97,
+ "WA_ShowWithoutActivating": 98,
+ "WA_X11BypassTransientForHint": 99,
+ "WA_NativeWindow": 100,
+ "WA_DontCreateNativeAncestors": 101,
+ "WA_MacVariableSize": 102,
+ "WA_DontShowOnScreen": 103,
+ "WA_X11NetWmWindowTypeDesktop": 104,
+ "WA_X11NetWmWindowTypeDock": 105,
+ "WA_X11NetWmWindowTypeToolBar": 106,
+ "WA_X11NetWmWindowTypeMenu": 107,
+ "WA_X11NetWmWindowTypeUtility": 108,
+ "WA_X11NetWmWindowTypeSplash": 109,
+ "WA_X11NetWmWindowTypeDialog": 110,
+ "WA_X11NetWmWindowTypeDropDownMenu": 111,
+ "WA_X11NetWmWindowTypePopupMenu": 112,
+ "WA_X11NetWmWindowTypeToolTip": 113,
+ "WA_X11NetWmWindowTypeNotification": 114,
+ "WA_X11NetWmWindowTypeCombo": 115,
+ "WA_X11NetWmWindowTypeDND": 116,
+ "WA_MacFrameworkScaled": 117,
+ "WA_SetWindowModality": 118,
+ "WA_WState_WindowOpacitySet": 119,
+ "WA_TranslucentBackground": 120,
+ "WA_AcceptTouchEvents": 121,
+ "WA_WState_AcceptedTouchBeginEvent": 122,
+ "WA_TouchPadAcceptSingleTouchEvents": 123,
+ "WA_X11DoNotAcceptFocus": 126,
+ "WA_MacNoShadow": 127,
+ "WA_AlwaysStackOnTop": 128,
+ "WA_TabletTracking": 129,
+ "WA_ContentsMarginsRespectsSafeArea": 130,
+ "WA_StyleSheetTarget": 131,
+ "WA_AttributeCount": 132
+ }
+ }
+ Enum {
+ name: "ApplicationAttribute"
+ values: {
+ "AA_ImmediateWidgetCreation": 0,
+ "AA_MSWindowsUseDirect3DByDefault": 1,
+ "AA_DontShowIconsInMenus": 2,
+ "AA_NativeWindows": 3,
+ "AA_DontCreateNativeWidgetSiblings": 4,
+ "AA_PluginApplication": 5,
+ "AA_MacPluginApplication": 5,
+ "AA_DontUseNativeMenuBar": 6,
+ "AA_MacDontSwapCtrlAndMeta": 7,
+ "AA_Use96Dpi": 8,
+ "AA_X11InitThreads": 10,
+ "AA_SynthesizeTouchForUnhandledMouseEvents": 11,
+ "AA_SynthesizeMouseForUnhandledTouchEvents": 12,
+ "AA_UseHighDpiPixmaps": 13,
+ "AA_ForceRasterWidgets": 14,
+ "AA_UseDesktopOpenGL": 15,
+ "AA_UseOpenGLES": 16,
+ "AA_UseSoftwareOpenGL": 17,
+ "AA_ShareOpenGLContexts": 18,
+ "AA_SetPalette": 19,
+ "AA_EnableHighDpiScaling": 20,
+ "AA_DisableHighDpiScaling": 21,
+ "AA_UseStyleSheetPropagationInWidgetStyles": 22,
+ "AA_DontUseNativeDialogs": 23,
+ "AA_SynthesizeMouseForUnhandledTabletEvents": 24,
+ "AA_CompressHighFrequencyEvents": 25,
+ "AA_DontCheckOpenGLContextThreadAffinity": 26,
+ "AA_DisableShaderDiskCache": 27,
+ "AA_DontShowShortcutsInContextMenus": 28,
+ "AA_CompressTabletEvents": 29,
+ "AA_DisableWindowContextHelpButton": 30,
+ "AA_DisableSessionManager": 31,
+ "AA_AttributeCount": 32
+ }
+ }
+ Enum {
+ name: "ImageConversionFlags"
+ values: {
+ "ColorMode_Mask": 3,
+ "AutoColor": 0,
+ "ColorOnly": 3,
+ "MonoOnly": 2,
+ "AlphaDither_Mask": 12,
+ "ThresholdAlphaDither": 0,
+ "OrderedAlphaDither": 4,
+ "DiffuseAlphaDither": 8,
+ "NoAlpha": 12,
+ "Dither_Mask": 48,
+ "DiffuseDither": 0,
+ "OrderedDither": 16,
+ "ThresholdDither": 32,
+ "DitherMode_Mask": 192,
+ "AutoDither": 0,
+ "PreferDither": 64,
+ "AvoidDither": 128,
+ "NoOpaqueDetection": 256,
+ "NoFormatConversion": 512
+ }
+ }
+ Enum {
+ name: "BGMode"
+ values: {
+ "TransparentMode": 0,
+ "OpaqueMode": 1
+ }
+ }
+ }
+ Component { name: "QEasingCurve"; prototype: "QQmlEasingValueType" }
+}
diff --git a/tests/auto/qml/qmllint/data/coercetovoid.qml b/tests/auto/qml/qmllint/data/coercetovoid.qml
new file mode 100644
index 0000000000..f1b593ea3a
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/coercetovoid.qml
@@ -0,0 +1,8 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ function touchThisAndReturnSomething(x: int) {
+ return x + 1;
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/customParser.qml b/tests/auto/qml/qmllint/data/customParser.qml
index a83ae7e823..324ca20953 100644
--- a/tests/auto/qml/qmllint/data/customParser.qml
+++ b/tests/auto/qml/qmllint/data/customParser.qml
@@ -7,11 +7,11 @@ Rectangle {
states: [
State {
name: "red_color"
- PropertyChanges { target: root; color: "red" }
+ PropertyChanges { root.color: "red" }
},
State {
name: "blue_color"
- PropertyChanges { target: root; color: "blue" }
+ PropertyChanges { root.color: "blue" }
}
]
}
diff --git a/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml b/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml
new file mode 100644
index 0000000000..dc582936b9
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+import "SharedFunctions.js" as Functions
+
+Item {
+ Rectangle {
+ color: Functions.setColorAlpha(Qt.color("orange"), 0.15)
+ x: Functions.setColorAlpha.asdfg
+ y: Functions.asdfg
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/findMemberPrint.qml b/tests/auto/qml/qmllint/data/findMemberPrint.qml
new file mode 100644
index 0000000000..69146eb06b
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/findMemberPrint.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+import TestTypes
+
+QtObject {
+ property var foooooooo: Foo {
+ id: foo
+ }
+
+ function f(): void {
+ foo.print()
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/generalizedGroupHint.qml b/tests/auto/qml/qmllint/data/generalizedGroupHint.qml
new file mode 100644
index 0000000000..5fec03f7f3
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/generalizedGroupHint.qml
@@ -0,0 +1,23 @@
+import QtQuick
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ title: 'Resolve my color type'
+
+ Item {
+ id: foo
+
+ states: [
+ State {
+ PropertyChanges {
+ target: foo
+ myColor: Qt.rgba(0.5, 0.5, 0.5, 0.16)
+ }
+ }
+ ]
+
+ property color myColor: 'black'
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml b/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml
new file mode 100644
index 0000000000..7cfe98d4f8
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml
@@ -0,0 +1,20 @@
+import QtQuick
+import QtQuick.Layouts
+
+Window {
+ id: root
+
+ Rectangle {
+ id: redRect
+ }
+
+ Item {
+ states: [
+ State {
+ PropertyChanges {
+ redRect.Layout.fillWidth: true
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml
new file mode 100644
index 0000000000..d018110b86
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml
@@ -0,0 +1,11 @@
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ width: 640; height: 480
+ visible: true
+
+ Button {
+ text: "a"
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir
new file mode 100644
index 0000000000..acb456ffd9
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir
@@ -0,0 +1,3 @@
+module moduleWithQrc
+prefer :/qt/qml/moduleWithQrc/
+
diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc
new file mode 100644
index 0000000000..43d3ec805c
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/qt/qml/qmllint65/">
+ <file alias="main.qml">moduleWithQrc/main.qml</file>
+ </qresource>
+</RCC>
+
diff --git a/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc b/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc
new file mode 100644
index 0000000000..cb362e2d55
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/qt/qml/qmllint65">
+ <file alias="qmldir">moduleWithQrc/qmldir</file>
+ </qresource>
+</RCC>
+
diff --git a/tests/auto/qml/qmllint/data/importNonexistentFile.qml b/tests/auto/qml/qmllint/data/importNonexistentFile.qml
new file mode 100644
index 0000000000..847023936a
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/importNonexistentFile.qml
@@ -0,0 +1,3 @@
+import "¯\(ツ)/¯:/invalid/url"
+
+QtObject {}
diff --git a/tests/auto/qml/qmllint/data/importNullDevice.qml b/tests/auto/qml/qmllint/data/importNullDevice.qml
new file mode 100644
index 0000000000..5ff3090d75
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/importNullDevice.qml
@@ -0,0 +1,3 @@
+import "/dev/null"
+
+QtObject {}
diff --git a/tests/auto/qml/qmllint/data/initReadonly.qml b/tests/auto/qml/qmllint/data/initReadonly.qml
index 5a3cafff23..a9a2a0016b 100644
--- a/tests/auto/qml/qmllint/data/initReadonly.qml
+++ b/tests/auto/qml/qmllint/data/initReadonly.qml
@@ -2,4 +2,5 @@ import QtQml
QtObject {
readonly property int i: 14
+ readonly property int j: i + 20
}
diff --git a/tests/auto/qml/qmllint/data/inlineComponent.qml b/tests/auto/qml/qmllint/data/inlineComponent.qml
index ce6998a980..364d5319de 100644
--- a/tests/auto/qml/qmllint/data/inlineComponent.qml
+++ b/tests/auto/qml/qmllint/data/inlineComponent.qml
@@ -1,6 +1,7 @@
import QtQuick 2.0
Item {
+ component MyIC: IC {}
component IC : QtObject {}
QtObject {
component IC2: QtObject {}
diff --git a/tests/auto/qml/qmllint/data/invalidIdLookup.qml b/tests/auto/qml/qmllint/data/invalidIdLookup.qml
new file mode 100644
index 0000000000..b351e5cfea
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/invalidIdLookup.qml
@@ -0,0 +1,10 @@
+import Things
+import QtQml
+
+QtObject {
+ property MediaPlayerStateMachine m: MediaPlayerStateMachine {
+ id: stateMachine
+ }
+
+ objectName: stateMachine.objectName
+}
diff --git a/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml b/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml
new file mode 100644
index 0000000000..0a96fa9f92
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml
@@ -0,0 +1,8 @@
+import QtQuick
+import Qtbug111015 1.0
+
+Item {
+ TypeWithJsonObjectList {
+ jsonObjectList: []
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/locale.qml b/tests/auto/qml/qmllint/data/locale.qml
new file mode 100644
index 0000000000..4ff9e6392c
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/locale.qml
@@ -0,0 +1,6 @@
+import QtQml
+import LocaleTest
+
+QtObject {
+ property int monday: AppManager.primaryLocale.firstDayOfWeek
+}
diff --git a/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml
new file mode 100644
index 0000000000..14c716c35d
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml
@@ -0,0 +1,9 @@
+import QtQuick as test
+
+test.Rectangle { // crashed qqmljsimportvisitor
+ id: mainRect
+ width: 100
+ height: 100
+ visible: true
+ rotation: 11
+}
diff --git a/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml
new file mode 100644
index 0000000000..4e03d8091d
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml
@@ -0,0 +1,9 @@
+import QtQuick as test
+
+test.Item {
+ property test.color c
+
+ property var p: test.Grid {}
+
+ component IC: test.Rectangle {}
+}
diff --git a/tests/auto/qml/qmllint/data/multifix.fixed.qml b/tests/auto/qml/qmllint/data/multifix.fixed.qml
new file mode 100644
index 0000000000..d2188f2318
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/multifix.fixed.qml
@@ -0,0 +1,14 @@
+pragma ComponentBehavior: Bound
+import QtQml
+
+QtObject {
+ id: root
+
+ property Component cursorDelegate: QtObject {
+ objectName: root.objectName
+ }
+
+ property Component background: QtObject {
+ objectName: root.objectName
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/multifix.qml b/tests/auto/qml/qmllint/data/multifix.qml
new file mode 100644
index 0000000000..5f05ae7e62
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/multifix.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+QtObject {
+ id: root
+
+ property Component cursorDelegate: QtObject {
+ objectName: root.objectName
+ }
+
+ property Component background: QtObject {
+ objectName: root.objectName
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/notQmlRootMethods.qml b/tests/auto/qml/qmllint/data/notQmlRootMethods.qml
new file mode 100644
index 0000000000..6d067d572d
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/notQmlRootMethods.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ id: self
+
+ function a() { self.deleteLater(); }
+ function b() { self.destroyed(); }
+}
diff --git a/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml b/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml
index 2a51cceac3..b6c0f59c7f 100644
--- a/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml
+++ b/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml
@@ -5,5 +5,6 @@ Item {
anchors.horizontalCenter: undefined
anchors.verticalCenter: undefined
anchors.baseline: undefined
+ Component.onCompleted: anchors.bottom = undefined
}
}
diff --git a/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml
new file mode 100644
index 0000000000..fa7d4ef1ac
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+Rectangle {
+ State {
+ name: "test"
+ PropertyChanges { target: root; color: "blue" }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml
new file mode 100644
index 0000000000..94873463f1
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Window {
+ Item {
+ id: foo
+ property color myColor: 'black'
+
+ states: [
+ State {
+ PropertyChanges {
+ target: foo
+ myColor: Qt.rgba(0.5, 0.5, 0.5, 0.16)
+ notThere: "a"
+ }
+ }
+ ]
+
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/qEventPoint.qml b/tests/auto/qml/qmllint/data/qEventPoint.qml
new file mode 100644
index 0000000000..086e710b48
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/qEventPoint.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+TapHandler {
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onSingleTapped: (eventPoint, button) => {
+ console.log("Single tap at", eventPoint, "with button", button)
+ }
+ onTapped: console.log("tapped")
+}
diff --git a/tests/auto/qml/qmllint/data/qmlRootMethods.qml b/tests/auto/qml/qmllint/data/qmlRootMethods.qml
new file mode 100644
index 0000000000..5541de3a32
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/qmlRootMethods.qml
@@ -0,0 +1,12 @@
+import QtQml
+
+QtObject {
+ id: self
+
+ objectName: self.toString()
+
+ Component.onCompleted: {
+ self.destroy();
+ self.destroy(25);
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml b/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml
index 4847fc9196..ad88f1c58c 100644
--- a/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml
+++ b/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml
@@ -2,5 +2,5 @@ import Things 1.0
Something {
property var a: SomethingEntirelyStrange {}
- property var b: SomethingEntirelyStrange.AAA
+ property var b: SomethingEntirelyStrange.AnEnum.AAA
}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml
new file mode 100644
index 0000000000..de142337b4
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml
@@ -0,0 +1,9 @@
+import QtQml
+import QtQuick
+
+QtObject {
+ id: root
+ component Comp : Item { }
+ property Comp c: Comp{ }
+ function comp() { return root.c }
+}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml
new file mode 100644
index 0000000000..0585ceceb2
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+QtObject {
+ function enumeration() { return Text.AlignRight }
+}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml
new file mode 100644
index 0000000000..dc03311e73
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ function f() { }
+ function method() { return f }
+}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml
new file mode 100644
index 0000000000..bb79978d85
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml
@@ -0,0 +1,7 @@
+import QtQml
+
+QtObject {
+ id: root
+ property int i: 1
+ function prop() { return root.i }
+}
diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml
new file mode 100644
index 0000000000..78f02a8b67
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ function type() { return 1 + 1 }
+}
diff --git a/tests/auto/qml/qmllint/data/scriptInTemplate.qml b/tests/auto/qml/qmllint/data/scriptInTemplate.qml
new file mode 100644
index 0000000000..ba333dcd3e
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/scriptInTemplate.qml
@@ -0,0 +1,6 @@
+import QtQml
+import scripts
+
+QtObject {
+ objectName: `Result: ${Foo.getText()}`
+}
diff --git a/tests/auto/qml/qmllint/data/scripts/Foo.js b/tests/auto/qml/qmllint/data/scripts/Foo.js
new file mode 100644
index 0000000000..1d5106f733
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/scripts/Foo.js
@@ -0,0 +1,6 @@
+.pragma library
+
+function getText() {
+ return "whohoooooo"
+}
+
diff --git a/tests/auto/qml/qmllint/data/scripts/qmldir b/tests/auto/qml/qmllint/data/scripts/qmldir
new file mode 100644
index 0000000000..b4bf844348
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/scripts/qmldir
@@ -0,0 +1,4 @@
+module scripts
+typeinfo scripts.qmltypes
+Foo 1.0 Foo.js
+
diff --git a/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes b/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes
new file mode 100644
index 0000000000..ebbfc41eb2
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes
@@ -0,0 +1,22 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by qmltyperegistrar.
+
+Module {
+ Component {
+ file: "value.h"
+ name: "ValueType"
+ accessSemantics: "value"
+ exports: ["scripts/vvv 1.0"]
+ exportMetaObjectRevisions: [256]
+ Method {
+ name: "ValueType"
+ isConstructor: true
+ Parameter { name: "v"; type: "QString" }
+ }
+ Method { name: "ValueType"; isCloned: true; isConstructor: true }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/scriptstring.qml b/tests/auto/qml/qmllint/data/scriptstring.qml
new file mode 100644
index 0000000000..733434e924
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/scriptstring.qml
@@ -0,0 +1,73 @@
+import QtQuick
+
+Window {
+ id: root
+
+ Rectangle {
+ id: main
+
+ MouseArea {
+ id: mouse
+ property int clickCount: 0
+ onClicked: {
+ clickCount++
+
+ switch ( clickCount % 3 ) {
+ case 1 :
+ main.state = "middleState"
+ break
+ case 2 :
+ main.state = "rightState"
+ break
+ default :
+ main.state = "leftState"
+ }
+ }
+ }
+
+ Rectangle {
+ id: mover
+ anchors {
+ left: undefined
+ right: undefined
+ horizontalCenter: undefined
+ top: main.top
+ bottom: main.bottom
+ }
+ }
+
+ states: [
+ State {
+ name: "leftState"
+ AnchorChanges {
+ target: mover
+ anchors.left: main.left
+ anchors.right: undefined
+ anchors.horizontalCenter: undefined
+ }
+ },
+ State {
+ name: "middleState"
+ AnchorChanges {
+ target: mover
+ anchors {
+ left: undefined
+ right: undefined
+ horizontalCenter: main.horizontalCenter
+ }
+ }
+ },
+ State {
+ name: "rightState"
+ AnchorChanges {
+ target: mover
+ anchors {
+ left: undefined
+ right: main.right
+ horizontalCenter: undefined
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini b/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini
new file mode 100644
index 0000000000..a72e0e29e2
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini
@@ -0,0 +1,2 @@
+[Warnings]
+TestWarning=disable
diff --git a/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml b/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml
new file mode 100644
index 0000000000..b9250a2d11
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ Rectangle {
+ radius: 5
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/storeNameMethod.qml b/tests/auto/qml/qmllint/data/storeNameMethod.qml
new file mode 100644
index 0000000000..fca02b288f
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/storeNameMethod.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ Rectangle {
+
+ Timer {
+ function foo() {}
+ onTriggered: foo = 1
+ }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/stringToDateTime.qml b/tests/auto/qml/qmllint/data/stringToDateTime.qml
new file mode 100644
index 0000000000..dc4bd6cda5
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/stringToDateTime.qml
@@ -0,0 +1,7 @@
+import StringToDateTime
+
+StringToDateTime {
+ aDate: "2023-03-29"
+ aTime: "15:10:41"
+ aDateTime: "2023-03-29 15:10:41"
+}
diff --git a/tests/auto/qml/qmllint/data/untitled/components/Foo.qml b/tests/auto/qml/qmllint/data/untitled/components/Foo.qml
new file mode 100644
index 0000000000..10e5741900
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/untitled/components/Foo.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Text {
+ text: "Here I am!"
+}
diff --git a/tests/auto/qml/qmllint/data/untitled/main.qml b/tests/auto/qml/qmllint/data/untitled/main.qml
new file mode 100644
index 0000000000..cf8980ab55
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/untitled/main.qml
@@ -0,0 +1,9 @@
+pragma Strict
+
+import QtQuick
+import 'qrc:/untitled/components' as C
+
+Window {
+ id: root
+ C.Foo {}
+}
diff --git a/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc b/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc
new file mode 100644
index 0000000000..0ad0d57fbb
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/untitled/">
+ <file alias="main.qml">main.qml</file>
+ <file alias="components/Foo.qml">components/Foo.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/qml/qmllint/data/useConstInvokable.qml b/tests/auto/qml/qmllint/data/useConstInvokable.qml
new file mode 100644
index 0000000000..4f89e89918
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/useConstInvokable.qml
@@ -0,0 +1,5 @@
+import Things
+
+ConstInvokable {
+ objectName: getObject().objectName
+}
diff --git a/tests/auto/qml/qmllint/data/validLiterals.qml b/tests/auto/qml/qmllint/data/validLiterals.qml
index 4f8c575cd3..55792eaae2 100644
--- a/tests/auto/qml/qmllint/data/validLiterals.qml
+++ b/tests/auto/qml/qmllint/data/validLiterals.qml
@@ -29,9 +29,9 @@ QtObject {
property date date1: "2021-08-13T14:16:21.435Z"
- property point point1: "1,2"
+ property point point1: ({ x: 1, y: 2 })
- property size size1: "50x50"
+ property size size1: ({ width: 50, height: 50 })
- property rect rect1: "10,20,30x30"
+ property rect rect1: ({ x: 10, y: 20, width: 30, height: 30 })
}
diff --git a/tests/auto/qml/qmllint/data/valueTypesFromString.qml b/tests/auto/qml/qmllint/data/valueTypesFromString.qml
new file mode 100644
index 0000000000..fc013f858f
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/valueTypesFromString.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ property point p: "30,50"
+ property rect p3: "10, 20, 30x50"
+ property size p4: "30x50"
+}
diff --git a/tests/auto/qml/qmllint/data/variantMapLookup.qml b/tests/auto/qml/qmllint/data/variantMapLookup.qml
new file mode 100644
index 0000000000..7f50879932
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/variantMapLookup.qml
@@ -0,0 +1,13 @@
+pragma Strict
+import TestTypes
+import QtQuick
+
+Item {
+ VariantMapLookupFoo {
+ id: moo
+ }
+ Component.onCompleted: {
+ moo.data.value = 5
+ moo.data["value"] = 6
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/writeListProperty.qml b/tests/auto/qml/qmllint/data/writeListProperty.qml
new file mode 100644
index 0000000000..8015fdf51b
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/writeListProperty.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ id: self
+ property Item a: Item { id: a }
+ Component.onCompleted: self.data = [ a ]
+}
diff --git a/tests/auto/qml/qmllint/lintplugin.cpp b/tests/auto/qml/qmllint/lintplugin.cpp
index 47279adcaa..65795c103c 100644
--- a/tests/auto/qml/qmllint/lintplugin.cpp
+++ b/tests/auto/qml/qmllint/lintplugin.cpp
@@ -1,11 +1,11 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "lintplugin.h"
using namespace Qt::StringLiterals;
-static constexpr LoggerWarningId plugin { "testPlugin.test" };
+static constexpr QQmlSA::LoggerWarningId plugin{ "testPlugin.test" };
class ElementTest : public QQmlSA::ElementPass
{
@@ -17,25 +17,25 @@ public:
bool shouldRun(const QQmlSA::Element &element) override
{
- return element->baseType() == m_rectangle;
+ return element.baseType() == m_rectangle;
}
void run(const QQmlSA::Element &element) override
{
- auto property = element->property(u"radius"_s);
- if (!property.isValid() || element->property(u"radius"_s).typeName() != u"double") {
- emitWarning(u"Failed to verify radius property", plugin, element->sourceLocation());
+ auto property = element.property(u"radius"_s);
+ if (!property.isValid() || element.property(u"radius"_s).typeName() != u"qreal") {
+ emitWarning(u"Failed to verify radius property", plugin, element.sourceLocation());
return;
}
- auto bindings = element->propertyBindings(u"radius"_s);
+ auto bindings = element.propertyBindings(u"radius"_s);
if (bindings.isEmpty() || bindings.constFirst().numberValue() != 5) {
emitWarning(u"Failed to verify radius property binding", plugin,
- element->sourceLocation());
+ element.sourceLocation());
return;
}
- emitWarning(u"ElementTest OK", plugin, element->sourceLocation());
+ emitWarning(u"ElementTest OK", plugin, element.sourceLocation());
}
private:
@@ -48,37 +48,37 @@ public:
PropertyTest(QQmlSA::PassManager *manager) : QQmlSA::PropertyPass(manager) { }
void onBinding(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope,
+ const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope,
const QQmlSA::Element &value) override
{
emitWarning(u"Saw binding on %1 property %2 with value %3 (and type %4) in scope %5"_s
- .arg(element->baseTypeName(), propertyName,
+ .arg(element.baseTypeName(), propertyName,
value.isNull()
? u"NULL"_s
- : (value->internalName().isNull() ? value->baseTypeName()
- : value->baseTypeName()))
- .arg(binding.bindingType())
- .arg(bindingScope->baseTypeName()),
- plugin, bindingScope->sourceLocation());
+ : (value.name().isNull() ? value.baseTypeName()
+ : value.name()))
+ .arg(qToUnderlying(binding.bindingType()))
+ .arg(bindingScope.baseTypeName()),
+ plugin, bindingScope.sourceLocation());
}
void onRead(const QQmlSA::Element &element, const QString &propertyName,
- const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override
+ const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override
{
emitWarning(u"Saw read on %1 property %2 in scope %3"_s.arg(
- element->baseTypeName(), propertyName, readScope->baseTypeName()),
+ element.baseTypeName(), propertyName, readScope.baseTypeName()),
plugin, location);
}
void onWrite(const QQmlSA::Element &element, const QString &propertyName,
const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
- QQmlJS::SourceLocation location) override
+ QQmlSA::SourceLocation location) override
{
emitWarning(u"Saw write on %1 property %2 with value %3 in scope %4"_s.arg(
- element->baseTypeName(), propertyName,
- (value->internalName().isNull() ? value->baseTypeName()
- : value->internalName()),
- writeScope->baseTypeName()),
+ element.baseTypeName(), propertyName,
+ (value.name().isNull() ? value.baseTypeName()
+ : value.name()),
+ writeScope.baseTypeName()),
plugin, location);
}
};
@@ -109,7 +109,7 @@ private:
void LintPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement)
{
- if (!rootElement->filePath().endsWith(u"_pluginTest.qml"))
+ if (!rootElement.filePath().endsWith(u"_pluginTest.qml"))
return;
manager->registerElementPass(std::make_unique<ElementTest>(manager));
diff --git a/tests/auto/qml/qmllint/lintplugin.h b/tests/auto/qml/qmllint/lintplugin.h
index 76733ca7a7..c121657456 100644
--- a/tests/auto/qml/qmllint/lintplugin.h
+++ b/tests/auto/qml/qmllint/lintplugin.h
@@ -1,12 +1,12 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef LINTPLUGIN_H
#define LINTPLUGIN_H
#include <QtPlugin>
#include <QtCore/qobject.h>
-#include <QtQmlCompiler/private/qqmlsa_p.h>
+#include <QtQmlCompiler/qqmlsa.h>
class LintPlugin : public QObject, public QQmlSA::LintPlugin
{
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index a5e906ce9a..a9b5f8d732 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -1,6 +1,6 @@
// Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com>
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QProcess>
@@ -43,6 +43,11 @@ public:
Flags flags = {};
};
+ struct Environment : public QList<QPair<QString, QString>>
+ {
+ using QList<QPair<QString, QString>>::QList;
+ };
+
private Q_SLOTS:
void initTestCase() override;
@@ -73,12 +78,19 @@ private Q_SLOTS:
void autoqmltypes();
void resources();
+ void multiDirectory();
+
void requiredProperty();
void settingsFile();
void additionalImplicitImport();
+ void qrcUrlImport();
+
+ void incorrectImportFromHost_data();
+ void incorrectImportFromHost();
+
void attachedPropertyReuse();
void missingBuiltinsNoCrash();
@@ -90,6 +102,12 @@ private Q_SLOTS:
void lintModule();
void testLineEndings();
+ void valueTypesFromString();
+
+ void ignoreSettingsNotCommandLineOptions();
+
+ void environment_data();
+ void environment();
#if QT_CONFIG(library)
void testPlugin();
@@ -107,15 +125,17 @@ private:
QString runQmllint(const QString &fileToLint, std::function<void(QProcess &)> handleResult,
const QStringList &extraArgs = QStringList(), bool ignoreSettings = true,
- bool addImportDirs = true, bool absolutePath = true);
+ bool addImportDirs = true, bool absolutePath = true,
+ const Environment &env = {});
QString runQmllint(const QString &fileToLint, bool shouldSucceed,
const QStringList &extraArgs = QStringList(), bool ignoreSettings = true,
- bool addImportDirs = true, bool absolutePath = true);
+ bool addImportDirs = true, bool absolutePath = true,
+ const Environment &env = {});
void callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings = nullptr,
QStringList importDirs = {}, QStringList qmltypesFiles = {},
QStringList resources = {},
DefaultImportOption defaultImports = UseDefaultImports,
- QList<QQmlJSLogger::Category> *categories = nullptr, bool autoFixable = false,
+ QList<QQmlJS::LoggerCategory> *categories = nullptr, bool autoFixable = false,
LintType type = LintFile);
void searchWarnings(const QJsonArray &warnings, const QString &string,
@@ -137,7 +157,7 @@ private:
void runTest(const QString &testFile, const Result &result, QStringList importDirs = {},
QStringList qmltypesFiles = {}, QStringList resources = {},
DefaultImportOption defaultImports = UseDefaultImports,
- QList<QQmlJSLogger::Category> *categories = nullptr);
+ QList<QQmlJS::LoggerCategory> *categories = nullptr);
QString m_qmllintPath;
QString m_qmljsrootgenPath;
@@ -258,6 +278,12 @@ void TestQmllint::testUnqualified_data()
Message {
QStringLiteral("index is implicitly injected into this delegate. "
"Add a required property instead.") } } };
+ QTest::newRow("storeSloppy")
+ << QStringLiteral("UnqualifiedInStoreSloppy.qml")
+ << Result{ { Message{ QStringLiteral("Unqualified access"), 9, 26} } };
+ QTest::newRow("storeStrict")
+ << QStringLiteral("UnqualifiedInStoreStrict.qml")
+ << Result{ { Message{ QStringLiteral("Unqualified access"), 9, 52} } };
}
void TestQmllint::testUnknownCausesFail()
@@ -355,12 +381,12 @@ void TestQmllint::verifyJsRoot()
QString currentJsRootContent, generatedJsRootContent;
QFile currentJsRoot(currentJsRootPath);
- QVERIFY(currentJsRoot.open(QFile::ReadOnly));
+ QVERIFY(currentJsRoot.open(QFile::ReadOnly | QIODevice::Text));
currentJsRootContent = QString::fromUtf8(currentJsRoot.readAll());
currentJsRoot.close();
QFile generatedJsRoot(dir.path() + QDir::separator() + "jsroot.qmltypes");
- QVERIFY(generatedJsRoot.open(QFile::ReadOnly));
+ QVERIFY(generatedJsRoot.open(QFile::ReadOnly | QIODevice::Text));
generatedJsRootContent = QString::fromUtf8(generatedJsRoot.readAll());
generatedJsRoot.close();
@@ -393,6 +419,21 @@ void TestQmllint::autoqmltypes()
QVERIFY(process.readAllStandardError()
.contains("is not a qmldir file. Assuming qmltypes"));
QVERIFY(process.readAllStandardOutput().isEmpty());
+
+ {
+ QProcess bare;
+ bare.setWorkingDirectory(testFile("autoqmltypes"));
+ bare.start(m_qmllintPath, { QStringLiteral("--bare"), QStringLiteral("test.qml") });
+ bare.waitForFinished();
+
+ const QByteArray errors = bare.readAllStandardError();
+ QVERIFY(!errors.contains("is not a qmldir file. Assuming qmltypes"));
+ QVERIFY(errors.contains("Failed to import TestTest."));
+ QVERIFY(bare.readAllStandardOutput().isEmpty());
+
+ QCOMPARE(bare.exitStatus(), QProcess::NormalExit);
+ QVERIFY(bare.exitCode() != 0);
+ }
}
void TestQmllint::resources()
@@ -422,6 +463,17 @@ void TestQmllint::resources()
}
}
+void TestQmllint::multiDirectory()
+{
+ callQmllint(
+ testFile("MultiDirectory/qml/Inner.qml"), true, nullptr,
+ {}, {}, { testFile("MultiDirectory/multi.qrc") });
+
+ callQmllint(
+ testFile("MultiDirectory/qml/pages/Page.qml"), true, nullptr,
+ {}, {}, { testFile("MultiDirectory/multi.qrc") });
+}
+
void TestQmllint::dirtyQmlCode_data()
{
QTest::addColumn<QString>("filename");
@@ -444,12 +496,12 @@ void TestQmllint::dirtyQmlCode_data()
QTest::newRow("MemberNotFound")
<< QStringLiteral("memberNotFound.qml")
<< Result { { Message {
- QStringLiteral("Property \"foo\" not found on type \"QtObject\""), 6,
+ QStringLiteral("Member \"foo\" not found on type \"QtObject\""), 6,
31 } } };
QTest::newRow("UnknownJavascriptMethd")
<< QStringLiteral("unknownJavascriptMethod.qml")
<< Result { { Message {
- QStringLiteral("Property \"foo2\" not found on type \"Methods\""), 5,
+ QStringLiteral("Member \"foo2\" not found on type \"Methods\""), 5,
25 } } };
QTest::newRow("badAlias")
<< QStringLiteral("badAlias.qml")
@@ -463,6 +515,12 @@ void TestQmllint::dirtyQmlCode_data()
QStringLiteral("Invalid alias expression. Only IDs and field member "
"expressions can be aliased"),
5, 26 } } };
+ QTest::newRow("badAliasNotAnExpression")
+ << QStringLiteral("badAliasNotAnExpression.qml")
+ << Result { { Message {
+ QStringLiteral("Invalid alias expression. Only IDs and field member "
+ "expressions can be aliased"),
+ 4, 30 } } };
QTest::newRow("aliasCycle1") << QStringLiteral("aliasCycle.qml")
<< Result { { Message {
QStringLiteral("Alias \"b\" is part of an alias cycle"),
@@ -485,17 +543,17 @@ void TestQmllint::dirtyQmlCode_data()
9, 34 } } };
QTest::newRow("badParent")
<< QStringLiteral("badParent.qml")
- << Result { { Message { QStringLiteral("Property \"rrr\" not found on type \"Item\""),
+ << Result { { Message { QStringLiteral("Member \"rrr\" not found on type \"Item\""),
5, 34 } } };
QTest::newRow("parentIsComponent")
<< QStringLiteral("parentIsComponent.qml")
<< Result { { Message {
- QStringLiteral("Property \"progress\" not found on type \"QQuickItem\""), 7,
+ QStringLiteral("Member \"progress\" not found on type \"QQuickItem\""), 7,
39 } } };
QTest::newRow("badTypeAssertion")
<< QStringLiteral("badTypeAssertion.qml")
<< Result { { Message {
- QStringLiteral("Property \"rrr\" not found on type \"QQuickItem\""), 5,
+ QStringLiteral("Member \"rrr\" not found on type \"QQuickItem\""), 5,
39 } } };
QTest::newRow("incompleteQmltypes")
<< QStringLiteral("incompleteQmltypes.qml")
@@ -504,7 +562,7 @@ void TestQmllint::dirtyQmlCode_data()
26 } } };
QTest::newRow("incompleteQmltypes2")
<< QStringLiteral("incompleteQmltypes2.qml")
- << Result { { Message { QStringLiteral("Property \"weDontKnowIt\" "
+ << Result { { Message { QStringLiteral("Member \"weDontKnowIt\" "
"not found on type \"CustomPalette\""),
5, 35 } } };
QTest::newRow("incompleteQmltypes3")
@@ -526,11 +584,11 @@ void TestQmllint::dirtyQmlCode_data()
QTest::newRow("javascriptMethodsInModule")
<< QStringLiteral("javascriptMethodsInModuleBad.qml")
<< Result { { Message {
- QStringLiteral("Property \"unknownFunc\" not found on type \"Foo\""), 5,
+ QStringLiteral("Member \"unknownFunc\" not found on type \"Foo\""), 5,
21 } } };
QTest::newRow("badEnumFromQtQml")
<< QStringLiteral("badEnumFromQtQml.qml")
- << Result { { Message { QStringLiteral("Property \"Linear123\" not "
+ << Result { { Message { QStringLiteral("Member \"Linear123\" not "
"found on type \"QQmlEasingEnums\""),
4, 30 } } };
QTest::newRow("anchors3")
@@ -548,13 +606,13 @@ void TestQmllint::dirtyQmlCode_data()
"unknown grouped property scope nanchors.") } } };
QTest::newRow("badAliasObject")
<< QStringLiteral("badAliasObject.qml")
- << Result { { Message { QStringLiteral("Property \"wrongwrongwrong\" not "
+ << Result { { Message { QStringLiteral("Member \"wrongwrongwrong\" not "
"found on type \"QtObject\""),
8, 40 } } };
QTest::newRow("badScript") << QStringLiteral("badScript.qml")
<< Result { { Message {
QStringLiteral(
- "Property \"stuff\" not found on type \"Empty\""),
+ "Member \"stuff\" not found on type \"Empty\""),
5, 21 } } };
QTest::newRow("badScriptOnAttachedProperty")
<< QStringLiteral("badScript.attached.qml")
@@ -565,7 +623,7 @@ void TestQmllint::dirtyQmlCode_data()
QTest::newRow("segFault (bad)")
<< QStringLiteral("SegFault.bad.qml")
<< Result { { Message { QStringLiteral(
- "Property \"foobar\" not found on type \"QQuickScreenAttached\"") } } };
+ "Member \"foobar\" not found on type \"QQuickScreenAttached\"") } } };
QTest::newRow("VariableUsedBeforeDeclaration")
<< QStringLiteral("useBeforeDeclaration.qml")
<< Result { { Message {
@@ -585,7 +643,7 @@ void TestQmllint::dirtyQmlCode_data()
"than the signal it handles.") } } };
QTest::newRow("OnAssignment") << QStringLiteral("onAssignment.qml")
<< Result { { Message { QStringLiteral(
- "Property \"loops\" not found on type \"bool\"") } } };
+ "Member \"loops\" not found on type \"bool\"") } } };
QTest::newRow("BadAttached") << QStringLiteral("badAttached.qml")
<< Result { { Message { QStringLiteral(
"unknown attached property scope WrongAttached.") } } };
@@ -702,13 +760,13 @@ void TestQmllint::dirtyQmlCode_data()
QTest::newRow("badAttachedProperty")
<< QStringLiteral("badAttachedProperty.qml")
<< Result { { Message {
- QStringLiteral("Property \"progress\" not found on type \"TestType\"") } } };
+ QStringLiteral("Member \"progress\" not found on type \"TestType\"") } } };
QTest::newRow("badAttachedPropertyNested")
<< QStringLiteral("badAttachedPropertyNested.qml")
<< Result { { Message { QStringLiteral(
- "Property \"progress\" not found on type \"QObject\""),
+ "Member \"progress\" not found on type \"QObject\""),
12, 41 } },
- { Message { QString("Property \"progress\" not found on type \"QObject\""),
+ { Message { QString("Member \"progress\" not found on type \"QObject\""),
6, 37 } } };
QTest::newRow("badAttachedPropertyTypeString")
<< QStringLiteral("badAttachedPropertyTypeString.qml")
@@ -780,6 +838,14 @@ singleTicks: ' \\' \\\\'
expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
16, 27 } },
{ Result::ExitsNormally, Result::AutoFixable } };
+ QTest::addRow("multifix")
+ << QStringLiteral("multifix.qml")
+ << Result { {
+ Message { QStringLiteral("Unqualified access"), 7, 19, QtWarningMsg},
+ Message { QStringLiteral("Unqualified access"), 11, 19, QtWarningMsg},
+ }, {}, {
+ Message { QStringLiteral("pragma ComponentBehavior: Bound\n"), 1, 1 }
+ }, { Result::AutoFixable }};
QTest::newRow("unresolvedType")
<< QStringLiteral("unresolvedType.qml")
<< Result { { Message { QStringLiteral(
@@ -812,7 +878,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
QTest::newRow("QtQuick.Window 2.0")
<< QStringLiteral("qtquickWindow20.qml")
<< Result { { Message { QStringLiteral(
- "Property \"window\" not found on type \"QQuickWindow\"") } } };
+ "Member \"window\" not found on type \"QQuickWindow\"") } } };
QTest::newRow("unresolvedAttachedType")
<< QStringLiteral("unresolvedAttachedType.qml")
<< Result { { Message { QStringLiteral(
@@ -879,22 +945,22 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
QTest::newRow("enumInvalid")
<< QStringLiteral("enumInvalid.qml")
<< Result { { Message {
- QStringLiteral("Property \"red\" not found on type \"QtObject\"") } } };
+ QStringLiteral("Member \"red\" not found on type \"QtObject\"") } } };
QTest::newRow("inaccessibleId")
<< QStringLiteral("inaccessibleId.qml")
<< Result { { Message {
- QStringLiteral("Property \"objectName\" not found on type \"int\"") } } };
+ QStringLiteral("Member \"objectName\" not found on type \"int\"") } } };
QTest::newRow("inaccessibleId2")
<< QStringLiteral("inaccessibleId2.qml")
<< Result { { Message {
- QStringLiteral("Property \"objectName\" not found on type \"int\"") } } };
+ QStringLiteral("Member \"objectName\" not found on type \"int\"") } } };
QTest::newRow("unknownTypeCustomParser")
<< QStringLiteral("unknownTypeCustomParser.qml")
<< Result { { Message { QStringLiteral("TypeDoesNotExist was not found.") } } };
QTest::newRow("nonNullStored")
<< QStringLiteral("nonNullStored.qml")
<< Result { { Message { QStringLiteral(
- "Property \"objectName\" not found on type \"Foozle\"") } },
+ "Member \"objectName\" not found on type \"Foozle\"") } },
{ Message { QStringLiteral("Unqualified access") } } };
QTest::newRow("cppPropertyChangeHandlers-wrong-parameters-size-bindable")
<< QStringLiteral("badCppPropertyChangeHandlers1.qml")
@@ -936,13 +1002,13 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
QTest::newRow("didYouMean(property)")
<< QStringLiteral("didYouMeanProperty.qml")
<< Result { { Message { QStringLiteral(
- "Property \"hoight\" not found on type \"Rectangle\"") },
+ "Member \"hoight\" not found on type \"Rectangle\"") },
{},
{ Message { QStringLiteral("height") } } } };
QTest::newRow("didYouMean(propertyCall)")
<< QStringLiteral("didYouMeanPropertyCall.qml")
<< Result {
- { Message { QStringLiteral("Property \"lgg\" not found on type \"Console\"") },
+ { Message { QStringLiteral("Member \"lgg\" not found on type \"Console\"") },
{},
{ Message { QStringLiteral("log") } } }
};
@@ -955,12 +1021,12 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
QTest::newRow("didYouMean(enum)")
<< QStringLiteral("didYouMeanEnum.qml")
<< Result { { Message { QStringLiteral(
- "Property \"Readx\" not found on type \"QQuickImage\"") },
+ "Member \"Readx\" not found on type \"QQuickImage\"") },
{},
{ Message { QStringLiteral("Ready") } } } };
QTest::newRow("nullBinding") << QStringLiteral("nullBinding.qml")
<< Result{ { Message{ QStringLiteral(
- "Cannot assign literal of type null to double") } } };
+ "Cannot assign literal of type null to qreal") } } };
QTest::newRow("missingRequiredAlias")
<< QStringLiteral("missingRequiredAlias.qml")
<< Result { { Message {
@@ -1020,9 +1086,8 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
QTest::newRow("NotScopedEnumCpp")
<< QStringLiteral("NotScopedEnumCpp.qml")
<< Result{ { Message{
- QStringLiteral(
- "Type is an unscoped enum. You cannot access \"V1\" from here."),
- 5, 57 } } };
+ QStringLiteral("You cannot access unscoped enum \"TheEnum\" from here."), 5,
+ 49 } } };
QTest::newRow("unresolvedArrayBinding")
<< QStringLiteral("unresolvedArrayBinding.qml")
@@ -1044,6 +1109,56 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
"in nested components."), 0, 0, QtInfoMsg } },
Result::AutoFixable
};
+ QTest::newRow("IsNotAnEntryOfEnum")
+ << QStringLiteral("IsNotAnEntryOfEnum.qml")
+ << Result{ {
+ Message {
+ QStringLiteral("Member \"Mode\" not found on type \"Item\""), 12,
+ 29, QtWarningMsg },
+ Message{
+ QStringLiteral("\"Hour\" is not an entry of enum \"Mode\"."), 13,
+ 62, QtInfoMsg }
+ },
+ {},
+ { Message{ QStringLiteral("Hours") } }
+ };
+
+ QTest::newRow("StoreNameMethod")
+ << QStringLiteral("storeNameMethod.qml")
+ << Result { { Message { QStringLiteral("Cannot assign to method foo") } } };
+
+ QTest::newRow("CoerceToVoid")
+ << QStringLiteral("coercetovoid.qml")
+ << Result { { Message {
+ QStringLiteral("Function without return type annotation returns double")
+ } } };
+
+ QTest::newRow("lowerCaseQualifiedImport")
+ << QStringLiteral("lowerCaseQualifiedImport.qml")
+ << Result{ {
+ Message{ u"Import qualifier 'test' must start with a capital letter."_s },
+ Message{
+ u"Namespace 'test' of 'test.Rectangle' must start with an upper case letter."_s },
+ } };
+ QTest::newRow("lowerCaseQualifiedImport2")
+ << QStringLiteral("lowerCaseQualifiedImport2.qml")
+ << Result{ {
+ Message{ u"Import qualifier 'test' must start with a capital letter."_s },
+ Message{
+ u"Namespace 'test' of 'test.Item' must start with an upper case letter."_s },
+ Message{
+ u"Namespace 'test' of 'test.Rectangle' must start with an upper case letter."_s },
+ Message{
+ u"Namespace 'test' of 'test.color' must start with an upper case letter."_s },
+ Message{
+ u"Namespace 'test' of 'test.Grid' must start with an upper case letter."_s },
+ } };
+ QTest::newRow("notQmlRootMethods")
+ << QStringLiteral("notQmlRootMethods.qml")
+ << Result{ {
+ Message{ u"Member \"deleteLater\" not found on type \"QtObject\""_s },
+ Message{ u"Member \"destroyed\" not found on type \"QtObject\""_s },
+ } };
}
void TestQmllint::dirtyQmlCode()
@@ -1167,6 +1282,7 @@ void TestQmllint::cleanQmlCode_data()
QTest::newRow("QVariant") << QStringLiteral("qvariant.qml");
QTest::newRow("Accessible") << QStringLiteral("accessible.qml");
QTest::newRow("qjsroot") << QStringLiteral("qjsroot.qml");
+ QTest::newRow("qmlRootMethods") << QStringLiteral("qmlRootMethods.qml");
QTest::newRow("InlineComponent") << QStringLiteral("inlineComponent.qml");
QTest::newRow("InlineComponentWithComponents") << QStringLiteral("inlineComponentWithComponents.qml");
QTest::newRow("InlineComponentsChained") << QStringLiteral("inlineComponentsChained.qml");
@@ -1216,6 +1332,20 @@ void TestQmllint::cleanQmlCode_data()
QTest::newRow("propertyWithOn") << QStringLiteral("switcher.qml");
QTest::newRow("constructorProperty") << QStringLiteral("constructorProperty.qml");
QTest::newRow("onlyMajorVersion") << QStringLiteral("onlyMajorVersion.qml");
+ QTest::newRow("attachedImportUse") << QStringLiteral("attachedImportUse.qml");
+ QTest::newRow("VariantMapGetPropertyLookup") << QStringLiteral("variantMapLookup.qml");
+ QTest::newRow("StringToDateTime") << QStringLiteral("stringToDateTime.qml");
+ QTest::newRow("ScriptInTemplate") << QStringLiteral("scriptInTemplate.qml");
+ QTest::newRow("AddressableValue") << QStringLiteral("addressableValue.qml");
+ QTest::newRow("WriteListProperty") << QStringLiteral("writeListProperty.qml");
+ QTest::newRow("dontConfuseMemberPrintWithGlobalPrint") << QStringLiteral("findMemberPrint.qml");
+ QTest::newRow("groupedAttachedLayout") << QStringLiteral("groupedAttachedLayout.qml");
+ QTest::newRow("QQmlScriptString") << QStringLiteral("scriptstring.qml");
+ QTest::newRow("QEventPoint") << QStringLiteral("qEventPoint.qml");
+ QTest::newRow("locale") << QStringLiteral("locale.qml");
+ QTest::newRow("constInvokable") << QStringLiteral("useConstInvokable.qml");
+ QTest::newRow("dontCheckJSTypes") << QStringLiteral("dontCheckJSTypes.qml");
+ QTest::newRow("jsonObjectIsRecognized") << QStringLiteral("jsonObjectIsRecognized.qml");
}
void TestQmllint::cleanQmlCode()
@@ -1239,10 +1369,11 @@ void TestQmllint::compilerWarnings_data()
QTest::newRow("qQmlV4Function") << QStringLiteral("varargs.qml") << Result::clean() << true;
QTest::newRow("multiGrouped") << QStringLiteral("multiGrouped.qml") << Result::clean() << true;
- QTest::newRow("shadowable") << QStringLiteral("shadowable.qml")
- << Result { { Message { QStringLiteral(
- "with type NotSoSimple can be shadowed") } } }
- << true;
+ QTest::newRow("shadowable")
+ << QStringLiteral("shadowable.qml")
+ << Result { { Message {QStringLiteral(
+ "with type NotSoSimple (stored as QQuickItem) can be shadowed") } } }
+ << true;
QTest::newRow("tooFewParameters")
<< QStringLiteral("tooFewParams.qml")
<< Result { { Message { QStringLiteral("No matching override found") } } } << true;
@@ -1261,6 +1392,51 @@ void TestQmllint::compilerWarnings_data()
<< Result { { { QStringLiteral(
"Functions without type annotations won't be compiled") } } }
<< true;
+ QTest::newRow("generalizedGroupHint")
+ << QStringLiteral("generalizedGroupHint.qml")
+ << Result { { { QStringLiteral(
+ "Cannot resolve property type for binding on myColor. "
+ "You may want use ID-based grouped properties here.") } } }
+ << true;
+ QTest::newRow("invalidIdLookup")
+ << QStringLiteral("invalidIdLookup.qml")
+ << Result { { {
+ QStringLiteral("Cannot retrieve a non-object type by ID: stateMachine")
+ } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-component")
+ << QStringLiteral("returnTypeAnnotation_component.qml")
+ << Result{ { { "Could not compile function comp: function without return type "
+ "annotation returns (component in" },
+ { "returnTypeAnnotation_component.qml)::c with type Comp (stored as "
+ "QQuickItem). This may prevent proper compilation to Cpp." } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-enum")
+ << QStringLiteral("returnTypeAnnotation_enum.qml")
+ << Result{ { { "Could not compile function enumeration: function without return type "
+ "annotation returns QQuickText::HAlignment::AlignRight (stored as int). "
+ "This may prevent proper compilation to Cpp." } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-method")
+ << QStringLiteral("returnTypeAnnotation_method.qml")
+ << Result{ { { "Could not compile function method: function without return type "
+ "annotation returns (component in " }, // Don't check the build folder path
+ { "returnTypeAnnotation_method.qml)::f(...) (stored as QJSValue). This may "
+ "prevent proper compilation to Cpp." } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-property")
+ << QStringLiteral("returnTypeAnnotation_property.qml")
+ << Result{ { { "Could not compile function prop: function without return type "
+ "annotation returns (component in " }, // Don't check the build folder path
+ { "returnTypeAnnotation_property.qml)::i with type int. This may prevent "
+ "proper compilation to Cpp." } } }
+ << true;
+ QTest::newRow("returnTypeAnnotation-type")
+ << QStringLiteral("returnTypeAnnotation_type.qml")
+ << Result{ { { "Could not compile function type: function without return type "
+ "annotation returns double. This may prevent proper compilation to "
+ "Cpp." } } }
+ << true;
}
void TestQmllint::compilerWarnings()
@@ -1273,11 +1449,15 @@ void TestQmllint::compilerWarnings()
auto categories = QQmlJSLogger::defaultCategories();
- auto category = std::find(categories.begin(), categories.end(), qmlCompiler);
+ auto category = std::find_if(categories.begin(), categories.end(), [](const QQmlJS::LoggerCategory& category) {
+ return category.id() == qmlCompiler;
+ });
Q_ASSERT(category != categories.end());
- if (enableCompilerWarnings)
- category->setLevel(u"warning"_s);
+ if (enableCompilerWarnings) {
+ category->setLevel(QtWarningMsg);
+ category->setIgnored(false);
+ }
runTest(filename, result, {}, {}, {}, UseDefaultImports, &categories);
}
@@ -1285,7 +1465,7 @@ void TestQmllint::compilerWarnings()
QString TestQmllint::runQmllint(const QString &fileToLint,
std::function<void(QProcess &)> handleResult,
const QStringList &extraArgs, bool ignoreSettings,
- bool addImportDirs, bool absolutePath)
+ bool addImportDirs, bool absolutePath, const Environment &env)
{
auto qmlImportDir = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
QStringList args;
@@ -1311,6 +1491,11 @@ QString TestQmllint::runQmllint(const QString &fileToLint,
QString errors;
auto verify = [&](bool isSilent) {
QProcess process;
+ QProcessEnvironment processEnv = QProcessEnvironment::systemEnvironment();
+ for (const auto &entry : env)
+ processEnv.insert(entry.first, entry.second);
+
+ process.setProcessEnvironment(processEnv);
process.setWorkingDirectory(QFileInfo(absoluteFilePath).absolutePath());
process.start(m_qmllintPath, args);
handleResult(process);
@@ -1353,7 +1538,7 @@ QString TestQmllint::runQmllint(const QString &fileToLint,
QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed,
const QStringList &extraArgs, bool ignoreSettings,
- bool addImportDirs, bool absolutePath)
+ bool addImportDirs, bool absolutePath, const Environment &env)
{
return runQmllint(
fileToLint,
@@ -1366,13 +1551,13 @@ QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed,
else
QVERIFY(process.exitCode() != 0);
},
- extraArgs, ignoreSettings, addImportDirs, absolutePath);
+ extraArgs, ignoreSettings, addImportDirs, absolutePath, env);
}
void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings,
QStringList importPaths, QStringList qmldirFiles,
QStringList resources, DefaultImportOption defaultImports,
- QList<QQmlJSLogger::Category> *categories, bool autoFixable,
+ QList<QQmlJS::LoggerCategory> *categories, bool autoFixable,
LintType type)
{
QJsonArray jsonOutput;
@@ -1382,18 +1567,22 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs
QQmlJSLinter::LintResult lintResult;
+ const QStringList resolvedImportPaths = defaultImports == UseDefaultImports
+ ? m_defaultImportPaths + importPaths
+ : importPaths;
if (type == LintFile) {
+ const QList<QQmlJS::LoggerCategory> resolvedCategories =
+ categories != nullptr ? *categories : QQmlJSLogger::defaultCategories();
lintResult = m_linter.lintFile(
- lintedFile, nullptr, true, &jsonOutput,
- defaultImports == UseDefaultImports ? m_defaultImportPaths + importPaths
- : importPaths,
- qmldirFiles, resources,
- categories != nullptr ? *categories : QQmlJSLogger::defaultCategories());
+ lintedFile, nullptr, true, &jsonOutput, resolvedImportPaths, qmldirFiles,
+ resources, resolvedCategories);
} else {
- lintResult = m_linter.lintModule(fileToLint, true, &jsonOutput);
+ lintResult =
+ m_linter.lintModule(fileToLint, true, &jsonOutput, resolvedImportPaths, resources);
}
bool success = lintResult == QQmlJSLinter::LintSuccess;
+ QEXPECT_FAIL("qtquickdialog", "Will fail until QTBUG-104091 is implemented", Abort);
QVERIFY2(success == shouldSucceed, QJsonDocument(jsonOutput).toJson());
if (warnings) {
@@ -1425,7 +1614,7 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs
if (QFileInfo(fixedPath).exists()) {
QFile fixedFile(fixedPath);
- fixedFile.open(QFile::ReadOnly);
+ QVERIFY(fixedFile.open(QFile::ReadOnly));
QString fixedFileContents = QString::fromUtf8(fixedFile.readAll());
#ifdef Q_OS_WIN
fixedCode = fixedCode.replace(u"\r\n"_s, u"\n"_s);
@@ -1447,7 +1636,7 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs
void TestQmllint::runTest(const QString &testFile, const Result &result, QStringList importDirs,
QStringList qmltypesFiles, QStringList resources,
DefaultImportOption defaultImports,
- QList<QQmlJSLogger::Category> *categories)
+ QList<QQmlJS::LoggerCategory> *categories)
{
QJsonArray warnings;
callQmllint(testFile, result.flags.testFlag(Result::Flag::ExitsNormally), &warnings, importDirs,
@@ -1623,17 +1812,56 @@ void TestQmllint::additionalImplicitImport()
const auto guard = qScopeGuard([this]() {m_linter.clearCache(); });
runTest("additionalImplicitImport.qml", Result::clean(), {}, {},
{ testFile("implicitImportResource.qrc") });
+}
+
+void TestQmllint::qrcUrlImport()
+{
+ const auto guard = qScopeGuard([this]() { m_linter.clearCache(); });
+
+ QJsonArray warnings;
+ callQmllint(testFile("untitled/main.qml"), true, &warnings, {}, {},
+ { testFile("untitled/qrcUrlImport.qrc") });
+ checkResult(warnings, Result::clean());
+}
+
+void TestQmllint::incorrectImportFromHost_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<Result>("result");
+ QTest::newRow("NonexistentFile")
+ << QStringLiteral("importNonexistentFile.qml")
+ << Result{ { Message{
+ QStringLiteral("File or directory you are trying to import does not exist"),
+ 1, 1 } } };
+#ifndef Q_OS_WIN
+ // there is no /dev/null device on Win
+ QTest::newRow("NullDevice")
+ << QStringLiteral("importNullDevice.qml")
+ << Result{ { Message{ QStringLiteral("is neither a file nor a directory. Are sure the "
+ "import path is correct?"),
+ 1, 1 } } };
+#endif
}
-void TestQmllint::attachedPropertyReuse()
+void TestQmllint::incorrectImportFromHost()
{
+ QFETCH(QString, filename);
+ QFETCH(Result, result);
+ runTest(filename, result);
+}
+
+void TestQmllint::attachedPropertyReuse()
+{
auto categories = QQmlJSLogger::defaultCategories();
- auto category = std::find(categories.begin(), categories.end(), qmlAttachedPropertyReuse);
+ auto category = std::find_if(categories.begin(), categories.end(), [](const QQmlJS::LoggerCategory& category) {
+ return category.id() == qmlAttachedPropertyReuse;
+ });
Q_ASSERT(category != categories.end());
- category->setLevel(u"warning"_s);
+ category->setLevel(QtWarningMsg);
+ category->setIgnored(false);
runTest("attachedPropNotReused.qml",
Result { { Message { QStringLiteral("Using attached type QQuickKeyNavigationAttached "
"already initialized in a parent "
@@ -1641,6 +1869,24 @@ void TestQmllint::attachedPropertyReuse()
{}, {}, {}, UseDefaultImports, &categories);
runTest("attachedPropEnum.qml", Result::clean(), {}, {}, {}, UseDefaultImports, &categories);
+ runTest("MyStyle/ToolBar.qml", Result {
+ {
+ Message {
+ "Using attached type MyStyle already initialized in a parent scope"_L1,
+ 10,
+ 16
+ }
+ },
+ {},
+ {
+ Message {
+ "Reference it by id instead"_L1,
+ 10,
+ 16
+ }
+ },
+ Result::AutoFixable
+ });
}
void TestQmllint::missingBuiltinsNoCrash()
@@ -1683,10 +1929,14 @@ void TestQmllint::importMultipartUri()
void TestQmllint::lintModule_data()
{
QTest::addColumn<QString>("module");
+ QTest::addColumn<QStringList>("importPaths");
+ QTest::addColumn<QStringList>("resources");
QTest::addColumn<Result>("result");
QTest::addRow("Things")
<< u"Things"_s
+ << QStringList()
+ << QStringList()
<< Result {
{ Message {
u"Type \"QPalette\" not found. Used in SomethingEntirelyStrange.palette"_s,
@@ -1696,16 +1946,30 @@ void TestQmllint::lintModule_data()
};
QTest::addRow("missingQmltypes")
<< u"Fake5Compat.GraphicalEffects.private"_s
+ << QStringList()
+ << QStringList()
<< Result { { Message { u"QML types file does not exist"_s } } };
+
+ QTest::addRow("moduleWithQrc")
+ << u"moduleWithQrc"_s
+ << QStringList({ testFile("hidden") })
+ << QStringList({
+ testFile("hidden/qmake_moduleWithQrc.qrc"),
+ testFile("hidden/moduleWithQrc_raw_qml_0.qrc")
+ })
+ << Result::clean();
}
void TestQmllint::lintModule()
{
QFETCH(QString, module);
+ QFETCH(QStringList, importPaths);
+ QFETCH(QStringList, resources);
QFETCH(Result, result);
QJsonArray warnings;
- callQmllint(module, false, &warnings, {}, {}, {}, {}, nullptr, false, LintModule);
+ callQmllint(module, result.flags & Result::ExitsNormally, &warnings, importPaths, {}, resources,
+ UseDefaultImports, nullptr, false, LintModule);
checkResult(warnings, result);
}
@@ -1739,6 +2003,25 @@ void TestQmllint::testLineEndings()
}
}
+void TestQmllint::valueTypesFromString()
+{
+ runTest("valueTypesFromString.qml",
+ Result{ {
+ Message{
+ u"Binding is not supported: Type QSizeF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s },
+ Message{
+ u"Binding is not supported: Type QRectF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s },
+ Message{
+ u"Binding is not supported: Type QPointF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s },
+ },
+ { /*bad messages */ },
+ {
+ Message{ u"({ width: 30, height: 50 })"_s },
+ Message{ u"({ x: 10, y: 20, width: 30, height: 50 })"_s },
+ Message{ u"({ x: 30, y: 50 })"_s },
+ } });
+}
+
#if QT_CONFIG(library)
void TestQmllint::testPlugin()
{
@@ -1780,6 +2063,9 @@ void TestQmllint::testPlugin()
Result { { Message { u"QtQuick.Controls and NO QtQuick present"_s } } });
// Verify that none of the passes do anything when they're not supposed to
runTest("nothing_pluginTest.qml", Result::clean());
+
+ QVERIFY(runQmllint("settings/plugin/elemenpass_pluginSettingTest.qml", true, QStringList(), false)
+ .isEmpty());
}
// TODO: Eventually tests for (real) plugins need to be moved into a separate file
@@ -1885,8 +2171,87 @@ void TestQmllint::quickPlugin()
runTest("pluginQuick_attachedClean.qml", Result::clean());
runTest("pluginQuick_attachedIgnore.qml", Result::clean());
runTest("pluginQuick_noCrashOnUneresolved.qml", Result {}); // we don't care about the specific warnings
+
+ runTest("pluginQuick_propertyChangesParsed.qml",
+ Result { {
+ Message {
+ u"Property \"myColor\" is custom-parsed in PropertyChanges. "
+ "You should phrase this binding as \"foo.myColor: Qt.rgba(0.5, ...\""_s,
+ 12, 30
+ },
+ Message {
+ u"You should remove any bindings on the \"target\" property and avoid "
+ "custom-parsed bindings in PropertyChanges."_s,
+ 11, 29
+ },
+ Message {
+ u"Unknown property \"notThere\" in PropertyChanges."_s,
+ 13, 31
+ }
+ } });
+ runTest("pluginQuick_propertyChangesInvalidTarget.qml", Result {}); // we don't care about the specific warnings
}
+
+void TestQmllint::environment_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<bool>("shouldSucceed");
+ QTest::addColumn<QStringList>("extraArgs");
+ QTest::addColumn<Environment>("env");
+ QTest::addColumn<QString>("expectedWarning");
+
+ const QString fileThatNeedsImportPath = testFile(u"NeedImportPath.qml"_s);
+ const QString importPath = testFile(u"ImportPath"_s);
+ const QString invalidImportPath = testFile(u"ImportPathThatDoesNotExist"_s);
+ const QString noWarningExpected;
+
+ QTest::addRow("missing-import-dir")
+ << fileThatNeedsImportPath << false << QStringList{}
+ << Environment{ { u"QML_IMPORT_PATH"_s, importPath } } << noWarningExpected;
+
+ QTest::addRow("import-dir-via-arg")
+ << fileThatNeedsImportPath << true << QStringList{ u"-I"_s, importPath }
+ << Environment{ { u"QML_IMPORT_PATH"_s, invalidImportPath } } << noWarningExpected;
+
+ QTest::addRow("import-dir-via-env")
+ << fileThatNeedsImportPath << true << QStringList{ u"-E"_s }
+ << Environment{ { u"QML_IMPORT_PATH"_s, importPath } }
+ << u"Using import directories passed from environment variable \"QML_IMPORT_PATH\": \"%1\"."_s
+ .arg(importPath);
+
+ QTest::addRow("import-dir-via-env2")
+ << fileThatNeedsImportPath << true << QStringList{ u"-E"_s }
+ << Environment{ { u"QML2_IMPORT_PATH"_s, importPath } }
+ << u"Using import directories passed from the deprecated environment variable \"QML2_IMPORT_PATH\": \"%1\"."_s
+ .arg(importPath);
+}
+
+void TestQmllint::environment()
+{
+ QFETCH(QString, file);
+ QFETCH(bool, shouldSucceed);
+ QFETCH(QStringList, extraArgs);
+ QFETCH(Environment, env);
+ QFETCH(QString, expectedWarning);
+
+ const QString output = runQmllint(file, shouldSucceed, extraArgs, false, true, false, env);
+ if (!expectedWarning.isEmpty()) {
+ QVERIFY(output.contains(expectedWarning));
+ }
+}
+
#endif
-QTEST_MAIN(TestQmllint)
+void TestQmllint::ignoreSettingsNotCommandLineOptions()
+{
+ const QString importPath = testFile(u"ImportPath"_s);
+ // makes sure that ignore settings only ignores settings and not command line options like
+ // "-I".
+ const QString output = runQmllint(testFile(u"NeedImportPath.qml"_s), true,
+ QStringList{ u"-I"_s, importPath }, true);
+ // should not complain about not finding the module that is in importPath
+ QCOMPARE(output, QString());
+}
+
+QTEST_GUILESS_MAIN(TestQmllint)
#include "tst_qmllint.moc"
diff --git a/tests/auto/qml/qmlplugindump/CMakeLists.txt b/tests/auto/qml/qmlplugindump/CMakeLists.txt
index 9153660ef0..053268bcfb 100644
--- a/tests/auto/qml/qmlplugindump/CMakeLists.txt
+++ b/tests/auto/qml/qmlplugindump/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmlplugindump Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlplugindump LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmlplugindump
SOURCES
tst_qmlplugindump.cpp
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp
index 171a185ad7..793191695d 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "dummy.h"
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h
index 0fb354d6c4..b72814df82 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DUMMY_H
#define DUMMY_H
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp
index eec547cd29..1409876ec6 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "dummy_plugin.h"
#include "dummy.h"
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h
index 2c3f25ae61..45437e0500 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DUMMY_PLUGIN_H
#define DUMMY_PLUGIN_H
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp
index e84355eb11..4c8da8aeab 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp
+++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "plugin.h"
#include "types.h"
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h
index f16177351d..02c9d42022 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h
+++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PLUGIN_H
#define PLUGIN_H
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h
index 5fdc5fa1e4..7a1b73ac90 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h
+++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TYPES_H
#define TYPES_H
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp
index 04fcfc5edd..9ba1365d40 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "imports.h"
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h
index 386cce9f4d..e88deda2f9 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef IMPORTS_H
#define IMPORTS_H
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp
index b71719fb6f..c215917c7d 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "imports_plugin.h"
#include "imports.h"
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h
index 17855d5a22..1353bbd24a 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef IMPORTS_PLUGIN_H
#define IMPORTS_PLUGIN_H
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp
index 117b6db82f..6295ec2201 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "versions.h"
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h
index 991823aaf3..b5afc16c2a 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef VERSIONS_H
#define VERSIONS_H
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp
index 6a554c596d..9c1a2d4a4e 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "versions_plugin.h"
#include "versions.h"
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h
index 9bc70ca473..aba5f1fac7 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef VERSIONS_PLUGIN_H
#define VERSIONS_PLUGIN_H
diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp
index 50af75380d..2e7b704413 100644
--- a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp
+++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QLibraryInfo>
@@ -152,10 +152,10 @@ void tst_qmlplugindump::plugin()
QVERIFY2(dumper.waitForStarted(), qPrintable(dumper.errorString()));
QVERIFY2(dumper.waitForFinished(), qPrintable(dumper.errorString()));
- const QString &result = dumper.readAllStandardOutput();
+ const QByteArray result = dumper.readAllStandardOutput();
QFile expectedFile(expectedPath);
QVERIFY2(expectedFile.open(QIODevice::ReadOnly), qPrintable(expectedFile.errorString()));
- const QString expected = expectedFile.readAll();
+ const QByteArray expected = expectedFile.readAll();
QCOMPARE(result, expected);
}
diff --git a/tests/auto/qml/qmlsplitlib/CMakeLists.txt b/tests/auto/qml/qmlsplitlib/CMakeLists.txt
index 523ab5cbfd..7c77d5fbae 100644
--- a/tests/auto/qml/qmlsplitlib/CMakeLists.txt
+++ b/tests/auto/qml/qmlsplitlib/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlsplitlib LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
include(GenerateExportHeader)
qt_add_library(tst_qmlsplitlib_library
@@ -13,6 +19,17 @@ generate_export_header(tst_qmlsplitlib_library)
target_link_libraries(tst_qmlsplitlib_library Qt::Core Qt::QmlIntegration)
+qt_add_library(tst-qmlsplitlib-library-2
+ lib2.h
+ lib2.cpp
+)
+qt_autogen_tools_initial_setup(tst-qmlsplitlib-library-2)
+target_include_directories(tst-qmlsplitlib-library-2 PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # find autogenerated header
+generate_export_header(tst-qmlsplitlib-library-2)
+
+target_link_libraries(tst-qmlsplitlib-library-2 Qt::Core Qt::QmlIntegration)
+
+
qt_internal_add_test(tst_qmlsplitlib
SOURCES
tst_qmlsplitlib.cpp
@@ -23,12 +40,15 @@ qt_internal_add_test(tst_qmlsplitlib
qt_autogen_tools_initial_setup(tst_qmlsplitlib)
qt6_generate_foreign_qml_types(tst_qmlsplitlib_library tst_qmlsplitlib)
+qt6_generate_foreign_qml_types(tst-qmlsplitlib-library-2 tst_qmlsplitlib)
+
+qt_policy(SET QTP0001 NEW)
qt6_add_qml_module(tst_qmlsplitlib
URI "SplitLib"
- AUTO_RESOURCE_PREFIX
QML_FILES
main.qml
+ main2.qml
)
-target_link_libraries(tst_qmlsplitlib PRIVATE tst_qmlsplitlib_library)
+target_link_libraries(tst_qmlsplitlib PRIVATE tst_qmlsplitlib_library tst-qmlsplitlib-library-2)
diff --git a/tests/auto/qml/qmlsplitlib/lib.h b/tests/auto/qml/qmlsplitlib/lib.h
index 02fc88894d..b6c394a3b6 100644
--- a/tests/auto/qml/qmlsplitlib/lib.h
+++ b/tests/auto/qml/qmlsplitlib/lib.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef SPLITLIB_LIB_H
#define SPLITLIB_LIB_H
diff --git a/tests/auto/qml/qmlsplitlib/lib2.cpp b/tests/auto/qml/qmlsplitlib/lib2.cpp
new file mode 100644
index 0000000000..d4489f5c77
--- /dev/null
+++ b/tests/auto/qml/qmlsplitlib/lib2.cpp
@@ -0,0 +1,5 @@
+#include "lib2.h"
+
+bool SplitLib2::transmogrify() { return true; }
+
+#include "moc_lib2.cpp"
diff --git a/tests/auto/qml/qmlsplitlib/lib2.h b/tests/auto/qml/qmlsplitlib/lib2.h
new file mode 100644
index 0000000000..6d6aaddda5
--- /dev/null
+++ b/tests/auto/qml/qmlsplitlib/lib2.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SPLITLIB_LIB2_H
+#define SPLITLIB_LIB2_H
+#include "tst-qmlsplitlib-library-2_export.h"
+
+#include <QtQmlIntegration/qqmlintegration.h>
+#include <QtCore/qglobal.h>
+#include <QObject>
+
+class TST_QMLSPLITLIB_LIBRARY_2_EXPORT SplitLib2 : public QObject
+{
+public:
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_INVOKABLE bool transmogrify();
+};
+
+
+class TST_QMLSPLITLIB_LIBRARY_2_EXPORT Foo2 : public QObject
+{
+public:
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Bar2)
+ QML_SINGLETON
+};
+
+#endif
diff --git a/tests/auto/qml/qmlsplitlib/main.qml b/tests/auto/qml/qmlsplitlib/main.qml
index 2309a2e8c3..47be48acc4 100644
--- a/tests/auto/qml/qmlsplitlib/main.qml
+++ b/tests/auto/qml/qmlsplitlib/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import SplitLib
diff --git a/tests/auto/qml/qmlsplitlib/main2.qml b/tests/auto/qml/qmlsplitlib/main2.qml
new file mode 100644
index 0000000000..c5081c6627
--- /dev/null
+++ b/tests/auto/qml/qmlsplitlib/main2.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import SplitLib
+
+SplitLib2 {
+ property string s: Bar2.objectName
+}
diff --git a/tests/auto/qml/qmlsplitlib/manual_imports.cpp b/tests/auto/qml/qmlsplitlib/manual_imports.cpp
index f1b30f8352..dd1c8dbe89 100644
--- a/tests/auto/qml/qmlsplitlib/manual_imports.cpp
+++ b/tests/auto/qml/qmlsplitlib/manual_imports.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlextensionplugin.h>
diff --git a/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp b/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp
index 893ed77ff7..d04a1c6845 100644
--- a/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp
+++ b/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp
@@ -1,17 +1,19 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QQmlEngine>
#include <QQmlComponent>
#include <QObject>
#include <qtest.h>
#include "lib.h"
+#include "lib2.h"
class tst_splitlib : public QObject
{
Q_OBJECT
private slots:
void verifyComponent();
+ void verifyComponent2();
};
void tst_splitlib::verifyComponent()
@@ -25,5 +27,16 @@ void tst_splitlib::verifyComponent()
QVERIFY(lib);
}
+void tst_splitlib::verifyComponent2()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QStringLiteral("qrc:/qt/qml/SplitLib/main2.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer o(c.create());
+ QVERIFY(!o.isNull());
+ auto lib = qobject_cast<SplitLib2 *>(o.get());
+ QVERIFY(lib);
+}
+
QTEST_MAIN(tst_splitlib)
#include "tst_qmlsplitlib.moc"
diff --git a/tests/auto/qml/qmltc/BLACKLIST b/tests/auto/qml/qmltc/BLACKLIST
deleted file mode 100644
index 004fbeb8d9..0000000000
--- a/tests/auto/qml/qmltc/BLACKLIST
+++ /dev/null
@@ -1,5 +0,0 @@
-[listView]
-qnx ci
-# QTBUG-101342
-[listView]
-android
diff --git a/tests/auto/qml/qmltc/CMakeLists.txt b/tests/auto/qml/qmltc/CMakeLists.txt
index 4b86a6c018..1e6c6b9e30 100644
--- a/tests/auto/qml/qmltc/CMakeLists.txt
+++ b/tests/auto/qml/qmltc/CMakeLists.txt
@@ -1,8 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmltc LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
add_subdirectory(QmltcTests)
add_subdirectory(NamespaceTest/Subfolder)
+add_subdirectory(QmltcExportedTests)
+add_subdirectory(QmltcExportedNoFileNameTest)
set(test_sources
nameconflict.h nameconflict.cpp
@@ -18,6 +26,11 @@ set(qmltc_module_libs
# automatic type registration that comes from the plugin)
qmltc_test_moduleplugin
qmltc_namespace_test_module
+ qmltc_namespace_test_moduleplugin
+ qmltc_exported_tests_module
+ qmltc_exported_tests_moduleplugin
+ qmltc_exported_no_file_name_test_module
+ qmltc_exported_no_file_name_test_moduleplugin
)
qt_internal_add_test(tst_qmltc_diskcache
SOURCES ${test_sources}
@@ -25,6 +38,7 @@ qt_internal_add_test(tst_qmltc_diskcache
)
target_compile_definitions(tst_qmltc_diskcache PRIVATE
QMLTC_TESTS_DISABLE_CACHE=0
+ QT_NO_URL_CAST_FROM_STRING #QTBUG-113875
)
qt_internal_add_test(tst_qmltc_nodiskcache
@@ -33,6 +47,7 @@ qt_internal_add_test(tst_qmltc_nodiskcache
)
target_compile_definitions(tst_qmltc_nodiskcache PRIVATE
QMLTC_TESTS_DISABLE_CACHE=1
+ QT_NO_URL_CAST_FROM_STRING #QTBUG-113875
)
# Add qmltc documentation example to the tests. This is not beautiful but allows
diff --git a/tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt b/tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt
index f661cdea1e..daf1139a8f 100644
--- a/tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt
+++ b/tests/auto/qml/qmltc/NamespaceTest/Subfolder/CMakeLists.txt
@@ -24,9 +24,10 @@ qt_autogen_tools_initial_setup(qmltc_namespace_test_module)
target_link_libraries(qmltc_namespace_test_module PUBLIC ${common_libraries})
+qt_policy(SET QTP0001 NEW)
+
qt6_add_qml_module(qmltc_namespace_test_module
URI NamespaceTest.Subfolder
- AUTO_RESOURCE_PREFIX
SOURCES
${cpp_sources}
QML_FILES
diff --git a/tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml b/tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml
index 101e9f69a1..3576353b02 100644
--- a/tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml
+++ b/tests/auto/qml/qmltc/NamespaceTest/Subfolder/Type.qml
@@ -1,5 +1,5 @@
import QtQuick
Item {
- property string data
+ property string data: "Hello from namespace"
}
diff --git a/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/CMakeLists.txt
new file mode 100644
index 0000000000..47f920129a
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_add_library(qmltc_exported_no_file_name_test_module STATIC)
+qt_autogen_tools_initial_setup(qmltc_exported_no_file_name_test_module)
+
+set(common_libraries
+ Qt::QuickPrivate
+)
+
+target_link_libraries(qmltc_exported_no_file_name_test_module PUBLIC ${common_libraries})
+
+qt_policy(SET QTP0001 NEW)
+
+qt6_add_qml_module(qmltc_exported_no_file_name_test_module
+ URI QmltcExportedNoFileNameTest
+ QML_FILES
+ HelloExportedWorldNoFileName.qml
+ DEPENDENCIES
+ Qt::Quick
+ ENABLE_TYPE_COMPILER
+ QMLTC_EXPORT_DIRECTIVE "Q_DECL_EXPORT"
+ # explicitly omitting QMLTC_EXPORT_FILE_NAME
+)
+
+target_include_directories(qmltc_exported_no_file_name_test_module PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
+
+qt_autogen_tools_initial_setup(qmltc_exported_no_file_name_test_moduleplugin)
diff --git a/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/HelloExportedWorldNoFileName.qml b/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/HelloExportedWorldNoFileName.qml
new file mode 100644
index 0000000000..89986df5a6
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcExportedNoFileNameTest/HelloExportedWorldNoFileName.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ property string myString: "Hello! I should be exported by qmltc"
+}
diff --git a/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt
new file mode 100644
index 0000000000..709297bed4
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcExportedTests/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_add_library(qmltc_exported_tests_module STATIC)
+qt_autogen_tools_initial_setup(qmltc_exported_tests_module)
+
+include(GenerateExportHeader)
+generate_export_header(qmltc_exported_tests_module)
+
+set(common_libraries
+ Qt::QuickPrivate
+)
+
+target_link_libraries(qmltc_exported_tests_module PUBLIC ${common_libraries})
+
+qt_policy(SET QTP0001 NEW)
+
+qt6_add_qml_module(qmltc_exported_tests_module
+ URI QmltcExportedTests
+ QML_FILES
+ HelloExportedWorld.qml
+ DEPENDENCIES
+ Qt::Quick
+ ENABLE_TYPE_COMPILER
+ QMLTC_EXPORT_DIRECTIVE "QMLTC_EXPORTED_TESTS_MODULE_EXPORT"
+ QMLTC_EXPORT_FILE_NAME "qmltc_exported_tests_module_export.h"
+)
+
+target_include_directories(qmltc_exported_tests_module PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
+
+qt_autogen_tools_initial_setup(qmltc_exported_tests_moduleplugin)
diff --git a/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml b/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml
new file mode 100644
index 0000000000..5e6886bced
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcExportedTests/HelloExportedWorld.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string myString: "Hello! I should be exported by qmltc"
+}
diff --git a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt
index 870d21f61d..4c472ec4ab 100644
--- a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt
+++ b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt
@@ -22,6 +22,8 @@ set(cpp_sources
cpptypes/singletontype.h cpptypes/singletontype.cpp
cpptypes/typewithnamespace.h cpptypes/typewithnamespace.cpp
cpptypes/typewithsignal.h
+ cpptypes/custominitialization.h
+ cpptypes/typewithrequiredproperties.h
)
set(qml_sources
@@ -44,11 +46,16 @@ set(qml_sources
regexpBindings.qml
AliasBase.qml
aliasAssignments.qml
+ Connections.qml
qtbug103956/SubComponent.qml
qtbug103956/MainComponent.qml
qtbug103956/qtbug103956_main.qml
+ qtbug120700_main.qml
+
+ qtbug123476.qml
+
signalHandlers.qml
javaScriptFunctions.qml
changingBindings.qml
@@ -108,6 +115,9 @@ set(qml_sources
inlineComponentsFromDifferentFiles.qml
singletons.qml
mySignals.qml
+ stringToUrl.qml
+ myCheckBox.qml
+ signalConnections.qml
# support types:
DefaultPropertySingleChild.qml
@@ -125,6 +135,8 @@ set(qml_sources
NamespacedTypes.qml
badFile.qml
+
+ requiredProperties.qml
)
set(js_sources
@@ -135,10 +147,16 @@ set(common_libraries
Qt::Core
Qt::QmlPrivate
Qt::QuickPrivate
+ Qt::QuickTemplates2Private
Qt::TestPrivate
Qt::Gui # QColor, QMatrix4x4, ...
)
+if (QT_FEATURE_qml_table_model)
+ list(APPEND qml_sources QmlTableModel.qml)
+ list(APPEND common_libraries Qt::LabsQmlModelsPrivate)
+endif()
+
set_source_files_properties(NameConflict.qml PROPERTIES
QT_QMLTC_FILE_BASENAME ResolvedNameConflict)
@@ -166,10 +184,11 @@ qt_internal_add_resource(qmltc_test_module "qmake_immediate"
${qmake_immediate_resource_files}
)
+qt_policy(SET QTP0001 NEW)
+
qt6_add_qml_module(qmltc_test_module
VERSION 1.0
URI QmltcTests
- AUTO_RESOURCE_PREFIX
SOURCES
${cpp_sources}
QML_FILES
diff --git a/tests/auto/qml/qmltc/QmltcTests/Connections.qml b/tests/auto/qml/qmltc/QmltcTests/Connections.qml
new file mode 100644
index 0000000000..d7fc0d87c6
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/Connections.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Rectangle {
+ property string hello
+
+ id: root
+
+ Connections {
+ target: root
+ function onHelloChanged(argument) {}
+ }
+}
diff --git a/tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml b/tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml
index 9dbe9d6fbe..d3c1cfe293 100644
--- a/tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/InlineComponentProvider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/qml/qmltc/QmltcTests/QmlTableModel.qml b/tests/auto/qml/qmltc/QmltcTests/QmlTableModel.qml
new file mode 100644
index 0000000000..696c2b40bb
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/QmlTableModel.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.12
+import QtQuick.Window 2.12
+import Qt.labs.qmlmodels 1.0
+
+TableView {
+ anchors.fill: parent
+ columnSpacing: 1
+ rowSpacing: 1
+ boundsBehavior: Flickable.StopAtBounds
+
+ model: TableModel {
+ property string testName: "MyTableModel"
+ TableModelColumn { display: "checked" }
+ TableModelColumn { display: "amount" }
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitName" }
+ TableModelColumn { display: "fruitPrice" }
+
+ // Each row is one type of fruit that can be ordered
+ rows: [
+ {
+ // Each property is one cell/column.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ {
+ checked: true,
+ amount: 4,
+ fruitType: "Orange",
+ fruitName: "Navel",
+ fruitPrice: 2.50
+ },
+ {
+ checked: false,
+ amount: 1,
+ fruitType: "Banana",
+ fruitName: "Cavendish",
+ fruitPrice: 3.50
+ }
+ ]
+ }
+ delegate: TextInput {
+ text: model.display
+ padding: 12
+ selectByMouse: true
+
+ onAccepted: model.display = text
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#efefef"
+ z : -1
+ }
+ }
+}
+
diff --git a/tests/auto/qml/qmltc/QmltcTests/aliases.qml b/tests/auto/qml/qmltc/QmltcTests/aliases.qml
index 9f13f7b17a..41c34b4187 100644
--- a/tests/auto/qml/qmltc/QmltcTests/aliases.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/aliases.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml b/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml
index 680e297edb..70636c0a2e 100644
--- a/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QmltcTests
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/custominitialization.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/custominitialization.h
new file mode 100644
index 0000000000..7546f344d2
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/custominitialization.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef CUSTOMINITIALIAZATION_H_
+#define CUSTOMINITIALIAZATION_H_
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQml/qqmllist.h>
+#include <QtQml/qqmlregistration.h>
+
+class ExtensionType : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+ Q_PROPERTY(double propertyFromExtension READ getPropertyFromExtension WRITE
+ setPropertyFromExtension)
+ Q_PROPERTY(QQmlListProperty<QQuickItem> extensionObjectList READ getExtensionObjectList)
+
+ QProperty<double> m_propertyFromExtension{ 0 };
+ QList<QQuickItem *> m_extensionObjectList;
+
+public:
+ ExtensionType(QObject *parent = nullptr) : QObject(parent) { }
+
+ double getPropertyFromExtension() const { return m_propertyFromExtension; }
+ void setPropertyFromExtension(double v) { m_propertyFromExtension = v; }
+
+ QQmlListProperty<QQuickItem> getExtensionObjectList()
+ {
+ return QQmlListProperty<QQuickItem>(this, &m_extensionObjectList);
+ }
+};
+
+class TypeForCustomInitialization : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(QQmlListProperty<QQuickItem> cppObjectList READ getCppObjectList)
+ Q_PROPERTY(
+ double defaultedBindable BINDABLE bindableDefaultedBindable READ default WRITE default)
+
+ QML_EXTENDED(ExtensionType)
+
+ QList<QQuickItem *> m_cppObjectList;
+ QProperty<double> m_defaultedBindable;
+
+public:
+ TypeForCustomInitialization(QObject *parent = nullptr) : QObject(parent) { }
+
+ QQmlListProperty<QQuickItem> getCppObjectList()
+ {
+ return QQmlListProperty<QQuickItem>(this, &m_cppObjectList);
+ }
+
+ QBindable<double> bindableDefaultedBindable()
+ {
+ return QBindable<double>(&m_defaultedBindable);
+ }
+};
+
+#endif // CUSTOMINITIALIAZATION_H_
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp
index 422e0021f1..1487dbcf15 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "deferredpropertytypes.h"
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h
index d9aa801c77..1701d6ee91 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/deferredpropertytypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DEFERREDPROPERTYTYPES_H
#define DEFERREDPROPERTYTYPES_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp
index 16f555c0b7..b6ed76eed4 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "extensiontypes.h"
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h
index 667e2952ce..080d3b4380 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef EXTENSIONTYPES_H
#define EXTENSIONTYPES_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h
index 31cb385cfd..3135f4e3d3 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/private/testprivateproperty_p.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// NB: fake private header for testing purposes
#ifndef TESTPRIVATEPROPERTY_P_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h
index 293aece065..a2e1518fc3 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/singletontype.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef SINGLETONTYPE_H
#define SINGLETONTYPE_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp
index fd70c0a3f4..b44945fba0 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testattachedtype.h"
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h
index b7cc6348aa..2e3ef97009 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testattachedtype.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTATTACHEDTYPE_H
#define TESTATTACHEDTYPE_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp
index 410aae1528..45d6c3e322 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testgroupedtype.h"
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h
index f895f19531..cef875545c 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testgroupedtype.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTGROUPEDTYPE_H
#define TESTGROUPEDTYPE_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp
index c1d101653c..f24728b597 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "private/testprivateproperty_p.h"
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h
index 230c86affd..22863ba52a 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/testprivateproperty.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTPRIVATEPROPERTY_H
#define TESTPRIVATEPROPERTY_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h
index 873c38771e..b0f1a17adc 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PROPERTYALIASATTRIBUTES_H
#define PROPERTYALIASATTRIBUTES_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp
index fbfaf447da..ae4aaa5092 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "typewithnamespace.h"
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h
index 7b8bc7803a..173e8624ff 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithnamespace.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TYPEWITHNAMESPACE_H
#define TYPEWITHNAMESPACE_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp
index 80557209a5..eb0dae7465 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "typewithproperties.h"
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h
index e06f9079f9..d7a0d03405 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithproperties.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TYPEWITHPROPERTIES_H
#define TYPEWITHPROPERTIES_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithrequiredproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithrequiredproperties.h
new file mode 100644
index 0000000000..cc1ded9f74
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithrequiredproperties.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQml/qqmllist.h>
+#include <QtQml/qqmlregistration.h>
+
+#ifndef TYPEWITHREQUIREDPROPERTIES_H_
+# define TYPEWITHREQUIREDPROPERTIES_H_
+
+class ExtensionTypeWithRequiredProperties : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+ Q_PROPERTY(double requiredPropertyFromExtension READ getRequiredPropertyFromExtension WRITE
+ setRequiredPropertyFromExtension REQUIRED)
+
+ QProperty<double> m_requiredPropertyFromExtension{};
+
+public:
+ ExtensionTypeWithRequiredProperties(QObject *parent = nullptr) : QObject(parent) { }
+
+ double getRequiredPropertyFromExtension() const { return m_requiredPropertyFromExtension; }
+ void setRequiredPropertyFromExtension(double v) { m_requiredPropertyFromExtension = v; }
+};
+
+class TypeWithRequiredProperties : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(QQuickItem *inheritedRequiredProperty READ getInheritedRequiredProperty WRITE
+ setInheritedRequiredProperty REQUIRED)
+ Q_PROPERTY(int inheritedRequiredPropertyThatWillBeBound READ
+ getInheritedRequiredPropertyThatWillBeBound WRITE
+ setInheritedRequiredPropertyThatWillBeBound REQUIRED)
+ Q_PROPERTY(int nonRequiredInheritedPropertyThatWillBeMarkedRequired READ
+ getNonRequiredInheritedPropertyThatWillBeMarkedRequired WRITE
+ setNonRequiredInheritedPropertyThatWillBeMarkedRequired REQUIRED)
+
+ QML_EXTENDED(ExtensionTypeWithRequiredProperties)
+
+ QProperty<QQuickItem *> m_inheritedRequiredProperty{};
+ QProperty<int> m_inheritedRequiredPropertyThatWillBeBound{};
+ QProperty<int> m_nonRequiredInheritedPropertyThatWillBeMarkedRequired{};
+
+public:
+ TypeWithRequiredProperties(QObject *parent = nullptr) : QObject(parent) { }
+
+ QQuickItem *getInheritedRequiredProperty() const { return m_inheritedRequiredProperty; }
+ void setInheritedRequiredProperty(QQuickItem *v) { m_inheritedRequiredProperty = v; }
+
+ int getInheritedRequiredPropertyThatWillBeBound() const
+ {
+ return m_inheritedRequiredPropertyThatWillBeBound;
+ }
+ void setInheritedRequiredPropertyThatWillBeBound(int v)
+ {
+ m_inheritedRequiredPropertyThatWillBeBound = v;
+ }
+
+ int getNonRequiredInheritedPropertyThatWillBeMarkedRequired() const
+ {
+ return m_nonRequiredInheritedPropertyThatWillBeMarkedRequired;
+ }
+ void setNonRequiredInheritedPropertyThatWillBeMarkedRequired(int v)
+ {
+ m_nonRequiredInheritedPropertyThatWillBeMarkedRequired = v;
+ }
+};
+
+#endif // TYPEWITHREQUIREDPROPERTIES_H_
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h
index 139b431a40..6bb94ae051 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithsignal.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TYPEWITHSIGNAL_H
#define TYPEWITHSIGNAL_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h
index 9d68275b1e..8bf068ba35 100644
--- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h
+++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithspecialproperties.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TYPEWITHSPECIALPROPERTIES_H
#define TYPEWITHSPECIALPROPERTIES_H
diff --git a/tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml b/tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml
index a037776a52..2c21298c35 100644
--- a/tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/generalizedGroupedProperty.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtQuick
diff --git a/tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml b/tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml
index f9146083f6..284a8f5034 100644
--- a/tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/inlineComponents.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick as MyQtQuick
diff --git a/tests/auto/qml/qmltc/QmltcTests/myCheckBox.qml b/tests/auto/qml/qmltc/QmltcTests/myCheckBox.qml
new file mode 100644
index 0000000000..c5a2f5a7b8
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/myCheckBox.qml
@@ -0,0 +1,5 @@
+import QtQuick.Templates
+
+CheckBox {
+
+}
diff --git a/tests/auto/qml/qmltc/QmltcTests/mySignals.qml b/tests/auto/qml/qmltc/QmltcTests/mySignals.qml
index c79a0518c2..0348a0af54 100644
--- a/tests/auto/qml/qmltc/QmltcTests/mySignals.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/mySignals.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QmltcTests
import QtQuick
diff --git a/tests/auto/qml/qmltc/QmltcTests/properties.qml b/tests/auto/qml/qmltc/QmltcTests/properties.qml
index ca827519d5..f42b14a356 100644
--- a/tests/auto/qml/qmltc/QmltcTests/properties.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/properties.qml
@@ -26,13 +26,13 @@ QtObject {
property date dateP
property font fontP
property matrix4x4 matrix4x4P
- property point pointP
- property quaternion quatP
- property rect rectP
- property size sizeP
- property vector2d vec2dP
- property vector3d vec3dP
- property vector4d vec4dP
+ property point pointP: ({ x: 100, y: 200 })
+ property quaternion quatP: ({ x: 100, y: 200, z: 300, scalar: 400 })
+ property rect rectP: ({ x: 100, y: 200, width: 300, height: 400 })
+ property size sizeP: ({ width: 100, height: 200 })
+ property vector2d vec2dP : ({ x: 100, y: 200 })
+ property vector3d vec3dP: ({ x: 100, y: 200, z: 300 })
+ property vector4d vec4dP: ({ x: 100, y: 200, z: 300, w: 400 })
default property QtObject defaultObjP
readonly property string readonlyStringP: "foobar"
diff --git a/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml b/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml
index 66cd948ac4..fb57a2bbc8 100644
--- a/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QmltcTests
diff --git a/tests/auto/qml/qmltc/QmltcTests/qtbug120700_main.qml b/tests/auto/qml/qmltc/QmltcTests/qtbug120700_main.qml
new file mode 100644
index 0000000000..0796d00732
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/qtbug120700_main.qml
@@ -0,0 +1,25 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QmltcTests 1.0
+
+TypeForCustomInitialization {
+ id: myWindow
+ required property int someValue
+
+ property alias someValueAlias: myWindow.someValue
+ property int someValueBinding: someValue + 1
+
+ property bool wasSomeValueChanged: false
+
+ property int someComplexValueThatWillBeSet: { return 5 }
+ property int someComplexValueThatWillNotBeSet: { return 5 }
+
+ property list<int> valueTypeList : []
+ property list<Item> objectTypeList : []
+
+ //QTBUG-114403: onValueChanged should not trigger when setting
+ //the initial values.
+ onSomeValueChanged: { wasSomeValueChanged = true; }
+}
diff --git a/tests/auto/qml/qmltc/QmltcTests/qtbug123476.qml b/tests/auto/qml/qmltc/QmltcTests/qtbug123476.qml
new file mode 100644
index 0000000000..79aa178de4
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/qtbug123476.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ // qmltc should see that rectangle is used as the type argument
+ // for the list and produce an import for QQuickRectangle.
+ // Failure to do so will produce code that cannot compile and the
+ // test will fail at build time.
+ property list<Rectangle> listWithUniqueReferenceToType : []
+}
diff --git a/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml b/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml
index 7c4dad5a4e..d4eb72786b 100644
--- a/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/qml/qmltc/QmltcTests/requiredProperties.qml b/tests/auto/qml/qmltc/QmltcTests/requiredProperties.qml
new file mode 100644
index 0000000000..77e9c88b9b
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/requiredProperties.qml
@@ -0,0 +1,61 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QmltcTests 1.0
+
+TypeWithRequiredProperties {
+ id: self
+ required property int primitiveType
+ required property list<int> valueList
+ required property list<Item> objectList
+
+ property int propertyThatWillBeMarkedRequired
+
+ // This is already bound so it should not appear as part of the
+ // bundle.
+ inheritedRequiredPropertyThatWillBeBound : 10
+
+ // This should be ignored as it alias a required property we are
+ // already going to consider. It should thus not appear as part of
+ // the bundle.
+ property alias aliasToRequiredProperty : self.primitiveType
+
+ required propertyThatWillBeMarkedRequired
+ required nonRequiredInheritedPropertyThatWillBeMarkedRequired
+
+ property alias aliasToRequiredInner: inner.requiredInner
+
+ // This should be ignored as the underlying property is already bound.
+ property alias aliasToRequiredBoundedInner: inner.requiredBoundedInner
+
+ property alias aliasToInnerThatWillBeMarkedRequired: inner.nonRequiredInner
+ required aliasToInnerThatWillBeMarkedRequired
+
+ property int notRequired
+ property alias requiredAliasToUnrequiredProperty : self.notRequired
+ required requiredAliasToUnrequiredProperty
+
+ // When we have an alias to a required property in the same scope
+ // we exclude the alias in favor of setting the property directly.
+ // See for example aliasToRequiredProperty in this file.
+ //
+ // The following alias should instead be picked up, as it point to
+ // an inner scope.
+ // Nonetheless, an initial implementation had a bug that would
+ // discard the alias as long as a property with the same name as
+ // the target was present in the same scope.
+ //
+ // The following alias tests this initially failing case.
+ property alias aliasToPropertyThatShadows: inner.primitiveType
+
+ property Item children : Item {
+ id: inner
+ required property int requiredInner
+ property int nonRequiredInner
+ required property int requiredBoundedInner
+ requiredBoundedInner: 43
+
+ required property int primitiveType
+ }
+}
diff --git a/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml b/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml
new file mode 100644
index 0000000000..d88e3920c3
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ id: root
+
+ property bool cycleEnabled: false
+ property bool cycleFirst: false
+ property bool cycleSecond: false
+
+ property Timer enableTimer: Timer {
+ running: root.cycleEnabled
+ interval: 1
+ onTriggered: {
+ conn.enabled = !conn.enabled;
+ root.cycleEnabled = false;
+ }
+ }
+
+ property Timer firstTimer: Timer {
+ id: firstTimer
+ objectName: "first"
+ running: root.cycleFirst
+ interval: 1
+ onTriggered: root.cycleFirst = false
+ }
+
+ property Timer secondTimer: Timer {
+ objectName: "second"
+ running: root.cycleSecond
+ interval: 1
+ onTriggered: conn.target = this;
+ repeat: true
+ }
+
+ property Connections conn: Connections {
+ id: conn
+ target: firstTimer
+ function onTriggered(m) {
+ root.objectName = target.objectName
+ root.cycleSecond = false;
+ }
+ }
+}
diff --git a/tests/auto/qml/qmltc/QmltcTests/singletons.qml b/tests/auto/qml/qmltc/QmltcTests/singletons.qml
index a45e502bc0..d30a50dd6b 100644
--- a/tests/auto/qml/qmltc/QmltcTests/singletons.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/singletons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QmltcTests
diff --git a/tests/auto/qml/qmltc/QmltcTests/stringToUrl.qml b/tests/auto/qml/qmltc/QmltcTests/stringToUrl.qml
new file mode 100644
index 0000000000..1172c42032
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/stringToUrl.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.12
+
+QtObject {
+ readonly property FontLoader iconLoader: FontLoader {
+ source: "qrc:/qt/qml/path/to/font.ttf"
+ }
+ property string myUrl: "qrc:/qt/qml/path/to/font2.ttf"
+ readonly property FontLoader iconLoader2: FontLoader {
+ source: myUrl
+ }
+}
+
diff --git a/tests/auto/qml/qmltc/QmltcTests/subfolder/code.js b/tests/auto/qml/qmltc/QmltcTests/subfolder/code.js
index a248743d29..7b552e7f67 100644
--- a/tests/auto/qml/qmltc/QmltcTests/subfolder/code.js
+++ b/tests/auto/qml/qmltc/QmltcTests/subfolder/code.js
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
function isGood(value)
{
diff --git a/tests/auto/qml/qmltc/QmltcTests/translations.qml b/tests/auto/qml/qmltc/QmltcTests/translations.qml
index e00d9f5b86..71e9298fd1 100644
--- a/tests/auto/qml/qmltc/QmltcTests/translations.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/translations.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/qml/qmltc/QmltcTests/translationsById.qml b/tests/auto/qml/qmltc/QmltcTests/translationsById.qml
index e3606d9b0f..98fd5cb846 100644
--- a/tests/auto/qml/qmltc/QmltcTests/translationsById.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/translationsById.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml b/tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml
index 7226abbb2e..300af9d31f 100644
--- a/tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml
+++ b/tests/auto/qml/qmltc/QmltcTests/valueTypeListProperty.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtQuick
diff --git a/tests/auto/qml/qmltc/nameconflict.cpp b/tests/auto/qml/qmltc/nameconflict.cpp
index e95272913f..b1fb21977b 100644
--- a/tests/auto/qml/qmltc/nameconflict.cpp
+++ b/tests/auto/qml/qmltc/nameconflict.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "nameconflict.h"
#include <QtCore/qobject.h>
diff --git a/tests/auto/qml/qmltc/nameconflict.h b/tests/auto/qml/qmltc/nameconflict.h
index ea971b5159..64297af4d4 100644
--- a/tests/auto/qml/qmltc/nameconflict.h
+++ b/tests/auto/qml/qmltc/nameconflict.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef NAME_CONFLICT_FLAG
#define NAME_CONFLICT_FLAG
diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp
index 1fa4030642..ef33cebc00 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.cpp
+++ b/tests/auto/qml/qmltc/tst_qmltc.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmltc.h"
@@ -21,10 +21,12 @@
#include "qjsvalueassignments.h"
#include "extensiontypebindings.h"
#include "qtbug103956_main.h"
+#include "qtbug120700_main.h"
#include "nonstandardinclude.h"
#include "specialproperties.h"
#include "regexpbindings.h"
#include "aliasassignments.h"
+#include "connections.h"
#include "signalhandlers.h"
#include "javascriptfunctions.h"
@@ -77,12 +79,18 @@
#include "repeatercrash.h"
#include "aliases.h"
#include "inlinecomponentsfromdifferentfiles.h"
+#include "helloexportedworld.h"
+#include "helloexportedworldnofilename.h"
#include "testprivateproperty.h"
#include "singletons.h"
#include "mysignals.h"
#include "namespacedtypes.h"
#include "type.h"
+#include "qmltablemodel.h"
+#include "stringtourl.h"
+#include "signalconnections.h"
+#include "requiredproperties.h"
// Qt:
#include <QtCore/qstring.h>
@@ -147,11 +155,14 @@ void tst_qmltc::initTestCase()
QUrl("qrc:/qt/qml/QmltcTests/regexpBindings.qml"),
QUrl("qrc:/qt/qml/QmltcTests/AliasBase.qml"),
QUrl("qrc:/qt/qml/QmltcTests/aliasAssignments.qml"),
+ QUrl("qrc:/qt/qml/QmltcTests/Connections.qml"),
QUrl("qrc:/qt/qml/QmltcTests/qtbug103956/SubComponent.qml"),
QUrl("qrc:/qt/qml/QmltcTests/qtbug103956/MainComponent.qml"),
QUrl("qrc:/qt/qml/QmltcTests/qtbug103956/qtbug103956_main.qml"),
+ QUrl("qrc:/qt/qml/QmltcTests/qtbug120700_main.qml"),
+
QUrl("qrc:/qt/qml/QmltcTests/signalHandlers.qml"),
QUrl("qrc:/qt/qml/QmltcTests/javaScriptFunctions.qml"),
QUrl("qrc:/qt/qml/QmltcTests/changingBindings.qml"),
@@ -192,6 +203,7 @@ void tst_qmltc::initTestCase()
QUrl("qrc:/qt/qml/QmltcTests/calqlatrBits.qml"),
QUrl("qrc:/qt/qml/QmltcTests/valueTypeListProperty.qml"),
QUrl("qrc:/qt/qml/QmltcTests/appendToQQmlListProperty.qml"),
+ QUrl("qrc:/qt/qml/QmltcTests/requiredProperties.qml"),
};
QQmlEngine e;
@@ -374,7 +386,14 @@ void tst_qmltc::properties()
QCOMPARE(created.intP(), 42);
QCOMPARE(created.realP(), 2.32);
QCOMPARE(created.stringP(), u"hello, world"_s);
- QCOMPARE(created.urlP(), u"https://www.qt.io/"_s);
+ QCOMPARE(created.urlP(), QUrl(u"https://www.qt.io/"_s));
+ QCOMPARE(created.pointP(), QPoint(100, 200));
+ QCOMPARE(created.quatP(), QQuaternion(400, 100, 200, 300));
+ QCOMPARE(created.rectP(), QRectF(100, 200, 300, 400));
+ QCOMPARE(created.sizeP(), QSizeF(100, 200));
+ QCOMPARE(created.vec2dP(), QVector2D(100, 200));
+ QCOMPARE(created.vec3dP(), QVector3D(100, 200, 300));
+ QCOMPARE(created.vec4dP(), QVector4D(100, 200, 300, 400));
QCOMPARE(created.varP(), 42.42);
QCOMPARE(created.boolP(), true);
@@ -818,6 +837,103 @@ void tst_qmltc::visibleAliasMethods()
QCOMPARE(created.firstComponent()->setMe(), true);
}
+// QTBUG-120700
+void tst_qmltc::customInitialization()
+{
+ int valueToTest = 10;
+
+ QQuickItem firstItem;
+ QQuickItem secondItem;
+
+ QQmlEngine e;
+ PREPEND_NAMESPACE(qtbug120700_main)
+ created(&e, {valueToTest} ,nullptr, [valueToTest, &firstItem, &secondItem](auto& component) {
+ component.setSomeComplexValueThatWillBeSet(valueToTest);
+ component.setPropertyFromExtension(static_cast<double>(valueToTest));
+ component.setDefaultedBindable(static_cast<double>(valueToTest));
+ component.setValueTypeList({1, 2, 3, 4});
+ component.setObjectTypeList({&firstItem, &secondItem});
+ component.setExtensionObjectList({&firstItem, &secondItem});
+ component.setCppObjectList({&firstItem, &secondItem});
+ });
+
+ // QTBUG-114403: onValueChanged should have not been triggered
+ // when setting the initial value for the property.
+ // If this is true then the handler was called.
+ QCOMPARE(created.wasSomeValueChanged(), false);
+
+ // someComplexValueThatWillBeSet is set through a binding in the
+ // QML code, but is initialized when the instance is created.
+ // The bindings, which is generally set after the custom
+ // initialization was perfomed, should not overwrite the initial
+ // value that the user provided.
+ // On the other side, someComplexValueThatWillNotBeSet should
+ // still respect the original binding as an initial value for it
+ // was not provided.
+ QCOMPARE(created.someComplexValueThatWillBeSet(), valueToTest);
+ QCOMPARE(created.someComplexValueThatWillNotBeSet(), 5);
+
+ QCOMPARE(created.someValue(), valueToTest);
+ QCOMPARE(created.someValueAlias(), valueToTest);
+ QCOMPARE(created.someValueBinding(), valueToTest + 1);
+ QCOMPARE(created.property("propertyFromExtension").toDouble(), static_cast<double>(valueToTest));
+ QCOMPARE(created.bindableDefaultedBindable().value(), static_cast<double>(valueToTest));
+ QCOMPARE(created.valueTypeList(), QList({1, 2, 3, 4}));
+ QCOMPARE(created.objectTypeList().toList<QList<QQuickItem*>>(), QList({&firstItem, &secondItem}));
+ QCOMPARE(
+ created.property("extensionObjectList").value<QQmlListProperty<QQuickItem>>().toList<QList<QQuickItem*>>(),
+ QList({&firstItem, &secondItem})
+ );
+ QCOMPARE(created.getCppObjectList().toList<QList<QQuickItem*>>(), QList({&firstItem, &secondItem}));
+}
+
+void tst_qmltc::requiredPropertiesInitialization()
+{
+ QQuickItem item{};
+
+ int aliasToInnerThatWillBeMarkedRequired = 10;
+ int aliasToPropertyThatShadows = 42;
+ int aliasToRequiredInner = 11;
+ QQuickItem inheritedRequiredProperty{};
+ int nonRequiredInheritedPropertyThatWillBeMarkedRequired = 12;
+ QList<QQuickItem*> objectList{&item};
+ int primitiveType = 13;
+ int propertyThatWillBeMarkedRequired = 14;
+ int requiredAliasToUnrequiredProperty = 15;
+ double requiredPropertyFromExtension = 16.0;
+ QList<int> valueList{1, 2, 3, 4};
+
+ QQmlEngine e;
+ PREPEND_NAMESPACE(requiredProperties) created(
+ &e,
+ {
+ aliasToInnerThatWillBeMarkedRequired,
+ aliasToPropertyThatShadows,
+ aliasToRequiredInner,
+ &inheritedRequiredProperty,
+ nonRequiredInheritedPropertyThatWillBeMarkedRequired,
+ objectList,
+ primitiveType,
+ propertyThatWillBeMarkedRequired,
+ requiredAliasToUnrequiredProperty,
+ requiredPropertyFromExtension,
+ valueList
+ }
+ );
+
+ QCOMPARE(created.aliasToInnerThatWillBeMarkedRequired(), aliasToInnerThatWillBeMarkedRequired);
+ QCOMPARE(created.aliasToPropertyThatShadows(), aliasToPropertyThatShadows);
+ QCOMPARE(created.aliasToRequiredInner(), aliasToRequiredInner);
+ QCOMPARE(created.getInheritedRequiredProperty(), &inheritedRequiredProperty);
+ QCOMPARE(created.getNonRequiredInheritedPropertyThatWillBeMarkedRequired(), nonRequiredInheritedPropertyThatWillBeMarkedRequired);
+ QCOMPARE(created.objectList().toList<QList<QQuickItem*>>(), objectList);
+ QCOMPARE(created.primitiveType(), primitiveType);
+ QCOMPARE(created.propertyThatWillBeMarkedRequired(), propertyThatWillBeMarkedRequired);
+ QCOMPARE(created.requiredAliasToUnrequiredProperty(), requiredAliasToUnrequiredProperty);
+ QCOMPARE(created.property("requiredPropertyFromExtension").toDouble(), requiredPropertyFromExtension);
+ QCOMPARE(created.valueList(), valueList);
+}
+
// QTBUG-104094
void tst_qmltc::nonStandardIncludesInsideModule()
{
@@ -887,6 +1003,12 @@ void tst_qmltc::aliasAssignments()
}
}
+void tst_qmltc::connections()
+{
+ QQmlEngine e;
+ PREPEND_NAMESPACE(Connections) created(&e);
+}
+
void tst_qmltc::signalHandlers()
{
QQmlEngine e;
@@ -1058,7 +1180,7 @@ void tst_qmltc::propertyAlias_external()
void tst_qmltc::propertyAliasAttribute()
{
QQmlEngine e;
- PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e);
+ PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e, {""});
QQmlComponent c(&e);
c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/propertyAliasAttributes.qml"));
@@ -3204,8 +3326,74 @@ void tst_qmltc::namespacedName()
{
// cmake script should be able to auto-fill the namespace of the generated modules, and to
// replace . with ::
- NamespaceTest::Subfolder::Type *t;
- Q_UNUSED(t);
+ QQmlEngine e;
+ NamespaceTest::Subfolder::Type t(&e);
+ QCOMPARE(t.data(), u"Hello from namespace"_s);
+}
+
+void tst_qmltc::checkExportsAreCompiling()
+{
+ QQmlEngine e;
+ QmltcExportedTests::HelloExportedWorld w(&e);
+ QCOMPARE(w.myString(), u"Hello! I should be exported by qmltc"_s);
+}
+
+void tst_qmltc::checkExportsNoFileName()
+{
+ QQmlEngine e;
+ QmltcExportedNoFileNameTest::HelloExportedWorldNoFileName w(&e);
+ QCOMPARE(w.myString(), u"Hello! I should be exported by qmltc"_s);
+}
+
+#if QT_CONFIG(qml_table_model)
+void tst_qmltc::qmlTableModel()
+{
+ QQmlEngine e;
+ PREPEND_NAMESPACE(QmlTableModel) createdByQmltc(&e);
+ // check that the tableModel is not default constructed
+ QVariant model = createdByQmltc.model();
+ QVERIFY(model.isValid());
+ QQmlTableModel *tableModel = model.value<QQmlTableModel *>();
+ QVERIFY(tableModel);
+ QCOMPARE(tableModel->property("testName").toString(), u"MyTableModel"_s);
+}
+#endif
+
+void tst_qmltc::urlToString()
+{
+ QQmlEngine e;
+ PREPEND_NAMESPACE(stringToUrl) createdByQmltc(&e);
+ // check that the tableModel is not default constructed
+ QUrl first = createdByQmltc.iconLoader()->source();
+ QUrl second = createdByQmltc.iconLoader2()->source();
+ QCOMPARE(first, QUrl("qrc:/qt/qml/path/to/font.ttf"));
+ QCOMPARE(second, QUrl("qrc:/qt/qml/path/to/font2.ttf"));
+}
+
+void tst_qmltc::signalConnections()
+{
+ QQmlEngine e;
+ PREPEND_NAMESPACE(signalConnections) createdByQmltc(&e);
+
+ QVERIFY(createdByQmltc.objectName().isEmpty());
+ createdByQmltc.setCycleFirst(true);
+ QTRY_VERIFY(!createdByQmltc.cycleFirst());
+ QCOMPARE(createdByQmltc.objectName(), QLatin1String("first"));
+
+ createdByQmltc.setObjectName(QLatin1String("none"));
+ createdByQmltc.setCycleEnabled(true);
+ QTRY_VERIFY(!createdByQmltc.cycleEnabled());
+
+ createdByQmltc.setCycleFirst(true);
+ QTRY_VERIFY(!createdByQmltc.cycleFirst());
+ QCOMPARE(createdByQmltc.objectName(), QLatin1String("none"));
+
+ createdByQmltc.setCycleEnabled(true);
+ QTRY_VERIFY(!createdByQmltc.cycleEnabled());
+
+ createdByQmltc.setCycleSecond(true);
+ QTRY_VERIFY(!createdByQmltc.cycleSecond());
+ QCOMPARE(createdByQmltc.objectName(), QLatin1String("second"));
}
QTEST_MAIN(tst_qmltc)
diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h
index af084dcc01..ede6d551a0 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.h
+++ b/tests/auto/qml/qmltc/tst_qmltc.h
@@ -1,7 +1,8 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
+#include <private/qtqmlmodelsglobal_p.h>
using namespace Qt::StringLiterals;
@@ -34,10 +35,13 @@ private slots:
void jsvalueAssignments();
void extensionTypeBindings();
void visibleAliasMethods(); // QTBUG-103956
+ void customInitialization(); // QTBUG-120700
+ void requiredPropertiesInitialization();
void nonStandardIncludesInsideModule(); // QTBUG-104094
void specialProperties();
void regexpBindings();
void aliasAssignments();
+ void connections();
void signalHandlers();
void jsFunctions();
@@ -93,4 +97,12 @@ private slots:
void constSignalParameters();
void cppNamespaces();
void namespacedName();
+ void checkExportsAreCompiling();
+ void checkExportsNoFileName();
+
+#if QT_CONFIG(qml_table_model)
+ void qmlTableModel();
+#endif
+ void urlToString();
+ void signalConnections();
};
diff --git a/tests/auto/qml/qmltc_manual/CMakeLists.txt b/tests/auto/qml/qmltc_manual/CMakeLists.txt
index 8101bcbcea..6f1b065f99 100644
--- a/tests/auto/qml/qmltc_manual/CMakeLists.txt
+++ b/tests/auto/qml/qmltc_manual/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmltc_manual LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -19,9 +25,10 @@ qt_internal_add_test(tst_qmltc_manual
TESTDATA ${test_data}
)
+qt_policy(SET QTP0001 NEW)
+
qt6_add_qml_module(tst_qmltc_manual
URI "QmltcManualTests"
- AUTO_RESOURCE_PREFIX
QML_FILES
${test_data}
)
diff --git a/tests/auto/qml/qmltc_manual/testclasses.h b/tests/auto/qml/qmltc_manual/testclasses.h
index a3fc9cfc21..879b815f16 100644
--- a/tests/auto/qml/qmltc_manual/testclasses.h
+++ b/tests/auto/qml/qmltc_manual/testclasses.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTCLASSES_H
#define TESTCLASSES_H
diff --git a/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp b/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp
index ca8e0895d7..6e41ff3baa 100644
--- a/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp
+++ b/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
diff --git a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
index bac445dec8..85c01d0a5f 100644
--- a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
+++ b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmltc_qprocess LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmltc_qprocess
SOURCES
tst_qmltc_qprocess.cpp
@@ -14,12 +20,14 @@ qt_internal_add_test(tst_qmltc_qprocess
set_source_files_properties(data/SingletonThing.qml data/singletonUncreatable.qml
PROPERTIES QT_QML_SINGLETON_TYPE true)
+qt_policy(SET QTP0001 NEW)
+
qt6_add_qml_module(tst_qmltc_qprocess
VERSION 1.0
URI QmltcQProcessTests
- AUTO_RESOURCE_PREFIX
SOURCES
cpptypes/testtype.h
+ cpptypes/typewithrequiredproperty.h
DEPENDENCIES
QtQuick/auto
QML_FILES
@@ -33,6 +41,13 @@ qt6_add_qml_module(tst_qmltc_qprocess
data/singletonUncreatable.qml
data/uncreatable.qml
data/invalidSignalHandlers.qml
+ data/QmlBaseFromAnotherModule.qml
+ data/invalidTypeAnnotation.qml
+ data/constructFromString.qml
+ data/unboundRequiredPropertyInInlineComponent.qml
+ data/componentDefinitionInnerRequiredProperty.qml
+ data/componentDefinitionInnerRequiredPropertyFromOutside.qml
+ data/innerLevelRequiredProperty.qml
)
set(common_libraries
diff --git a/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h b/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h
index 81c5f00c84..401a63accc 100644
--- a/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h
+++ b/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPE_H
#define TESTTYPE_H
diff --git a/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h b/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h
new file mode 100644
index 0000000000..1b0c69efaa
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/cpptypes/typewithrequiredproperty.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TYPEWITHREQUIREDPROPERTY_H_
+#define TYPEWITHREQUIREDPROPERTY_H_
+
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+#include <QtQml/qqmlregistration.h>
+
+class TypeWithRequiredProperty : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(QString requiredProperty READ requiredProperty WRITE setRequiredProperty REQUIRED)
+
+ QProperty<QString> m_requiredProperty;
+
+public:
+ TypeWithRequiredProperty(QObject *parent = nullptr) : QObject(parent) { }
+
+ QString requiredProperty() const { return m_requiredProperty; }
+ void setRequiredProperty(const QString &s) { m_requiredProperty = s; }
+};
+
+#endif // TYPEWITHREQUIREDPROPERTY_H_
diff --git a/tests/auto/qml/qmltc_qprocess/data/QmlBaseFromAnotherModule.qml b/tests/auto/qml/qmltc_qprocess/data/QmlBaseFromAnotherModule.qml
new file mode 100644
index 0000000000..3c62716105
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/QmlBaseFromAnotherModule.qml
@@ -0,0 +1,13 @@
+import QtQml
+import QtQuick
+import QtQuick.Controls.Basic
+import QtQuick.Window
+
+Item {
+ property ScrollBar myBar: ScrollBar {}
+ function f(a: ScrollBar): ScrollBar {}
+
+ // C++ defined QML types from other modules are fine
+ property Item myItem: Item {}
+ function g(a: Item): Item {}
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml
new file mode 100644
index 0000000000..638cf9fa1e
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredProperty.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ Component {
+ id: mycomp
+
+ Item {
+ Rectangle {
+ // Inner required properties cannot be set so this
+ // should produce an error
+ required property bool bar
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml
new file mode 100644
index 0000000000..56f00edbe9
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/componentDefinitionInnerRequiredPropertyFromOutside.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QmltcQProcessTests
+
+Item {
+ Component {
+ id: mycomp
+
+ Item {
+ // This introduces an inner required property
+ // without a binding that cannot be set later and should
+ // thus block the compilation.
+ TypeWithRequiredProperty {}
+ }
+ }
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/constructFromString.qml b/tests/auto/qml/qmltc_qprocess/data/constructFromString.qml
new file mode 100644
index 0000000000..76ec189f3d
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/constructFromString.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ property point p: "30,50"
+ property rect p3: "10, 20, 30x50"
+ property size p4: "30x50"
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml b/tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml
new file mode 100644
index 0000000000..8b28418670
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/innerLevelRequiredProperty.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ Item {
+ required property int foo
+ }
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml b/tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml
index 55e3e73c31..44111430f7 100644
--- a/tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml
+++ b/tests/auto/qml/qmltc_qprocess/data/invalidAliasRevision.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QmltcQProcessTests
diff --git a/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml b/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml
new file mode 100644
index 0000000000..fb8c8eb198
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+
+Item {
+ function f(): Qt.point {
+
+ }
+
+ function g() {
+
+ }
+
+ function h(a: int, b: Item) {
+
+ }
+
+ function alpha(a: int, b) {}
+ function beta(a: int, b): Item {}
+ function gamma(a: Qt.point, b) {}
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml b/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml
new file mode 100644
index 0000000000..3955a228d8
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/unboundRequiredPropertyInInlineComponent.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ component InlineComponent: Item { required property int foo }
+
+ InlineComponent {}
+}
diff --git a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
index 7f12f29342..31c27c3cd7 100644
--- a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
+++ b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
@@ -51,6 +51,14 @@ private slots:
void topLevelComponent();
void dashesInFilename();
void invalidSignalHandlers();
+ void exports();
+ void qmlBaseFromAnotherModule();
+ void invalidTypeAnnotation();
+ void constructFromString();
+ void unboundRequiredPropertyInInlineComponent();
+ void componentDefinitionInnerRequiredProperty();
+ void componentDefinitionInnerRequiredPropertyFromOutside();
+ void innerLevelRequiredProperty();
};
#ifndef TST_QMLTC_QPROCESS_RESOURCES
@@ -92,6 +100,7 @@ QString tst_qmltc_qprocess::runQmltc(const QString &inputFile,
args << u"--resource"_s << resource;
args << u"--header"_s << (m_tmpPath + u"/"_s + QFileInfo(inputFile).baseName() + u".h"_s);
args << u"--impl"_s << (m_tmpPath + u"/"_s + QFileInfo(inputFile).baseName() + u".cpp"_s);
+ args << u"--module"_s << u"QmltcQProcessTestModule"_s;
args << extraArgs;
QString errors;
@@ -261,5 +270,120 @@ void tst_qmltc_qprocess::invalidSignalHandlers()
}
}
+void tst_qmltc_qprocess::qmlBaseFromAnotherModule()
+{
+ {
+ const auto errors = runQmltc(u"QmlBaseFromAnotherModule.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"QmlBaseFromAnotherModule.qml:6:1: Can't compile the QML property type \"ScrollBar\" to C++ because it lives in \"QtQuick.Controls.Basic\" instead of the current file's \"QmltcQProcessTestModule\" QML module."_s));
+ QVERIFY(errors.contains(
+ u"QmlBaseFromAnotherModule.qml:6:1: Can't compile the QML method return type \"ScrollBar\" to C++ because it lives in \"QtQuick.Controls.Basic\" instead of the current file's \"QmltcQProcessTestModule\" QML module."_s));
+ QVERIFY(errors.contains(
+ u"QmlBaseFromAnotherModule.qml:6:1: Can't compile the QML parameter type \"ScrollBar\" to C++ because it lives in \"QtQuick.Controls.Basic\" instead of the current file's \"QmltcQProcessTestModule\" QML module."_s));
+ // it should not complain about the usages of Item, a C++ defined QML element from another
+ // module
+ QVERIFY(!errors.contains(u"\"Item\""_s));
+ }
+}
+
+void tst_qmltc_qprocess::invalidTypeAnnotation()
+{
+ {
+ const auto errors = runQmltc(u"invalidTypeAnnotation.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"invalidTypeAnnotation.qml:5:17: \"Qt.point\" was not found for the return type of method \"f\"."_s));
+ QVERIFY(errors.contains(
+ u"invalidTypeAnnotation.qml:19:21: \"Qt.point\" was not found for the type of parameter \"a\" in method \"gamma\"."_s));
+ QVERIFY(!errors.contains(u"\"var\""_s));
+ QVERIFY(!errors.contains(u"\"void\""_s));
+ }
+}
+
+static QString fileToString(const QString &path)
+{
+ QFile f(path);
+ if (f.open(QIODevice::ReadOnly))
+ return QString::fromLatin1(f.readAll());
+ return QString();
+}
+
+void tst_qmltc_qprocess::exports()
+{
+ const QString fileName = u"dummy.qml"_s;
+ QStringList extraArgs;
+ extraArgs << "--export"
+ << "MYLIB_EXPORT_MACRO"
+ << "--exportInclude"
+ << "exportheader.h";
+ const auto errors = runQmltc(fileName, true, extraArgs);
+
+ const QString headerName = m_tmpPath + u"/"_s + QFileInfo(fileName).baseName() + u".h"_s;
+ const QString header = fileToString(headerName);
+ const QString implementationName =
+ m_tmpPath + u"/"_s + QFileInfo(fileName).baseName() + u".cpp"_s;
+ const QString implementation = fileToString(implementationName);
+
+ QCOMPARE(errors.size(), 0);
+
+ QVERIFY(header.contains(u"class MYLIB_EXPORT_MACRO dummy : public QObject\n"_s));
+ QVERIFY(!implementation.contains(u"MYLIB_EXPORT_MACRO"_s));
+
+ QVERIFY(header.contains(u"#include \"exportheader.h\"\n"_s));
+ QVERIFY(!implementation.contains(u"exportheader.h"_s));
+}
+
+void tst_qmltc_qprocess::constructFromString()
+{
+ const auto errors = runQmltc(u"constructFromString.qml"_s, false);
+ const QString warningMessage =
+ u"constructFromString.qml:%1:%2: Binding is not supported: Type %3 should be"
+ u" constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a"
+ u" string."_s;
+ QVERIFY(errors.contains(warningMessage.arg(4).arg(23).arg(u"QPointF")));
+ QVERIFY(errors.contains(warningMessage.arg(5).arg(23).arg(u"QRectF")));
+ QVERIFY(errors.contains(warningMessage.arg(6).arg(23).arg(u"QSizeF")));
+}
+
+void tst_qmltc_qprocess::unboundRequiredPropertyInInlineComponent()
+{
+ {
+ const auto errors = runQmltc(u"unboundRequiredPropertyInInlineComponent.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"unboundRequiredPropertyInInlineComponent.qml:9:5: Component is missing required property foo from InlineComponent [required]"_s
+ ));
+ }
+}
+
+void tst_qmltc_qprocess::componentDefinitionInnerRequiredProperty()
+{
+ {
+ const auto errors = runQmltc(u"componentDefinitionInnerRequiredProperty.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"componentDefinitionInnerRequiredProperty.qml:11:13: Component is missing required property bar from here [required]"
+ ));
+ }
+}
+
+void tst_qmltc_qprocess::componentDefinitionInnerRequiredPropertyFromOutside()
+{
+ {
+ const auto errors =
+ runQmltc(u"componentDefinitionInnerRequiredPropertyFromOutside.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"componentDefinitionInnerRequiredPropertyFromOutside.qml:15:13: Component is missing required property requiredProperty from TypeWithRequiredProperty [required]"
+ ));
+ }
+}
+
+void tst_qmltc_qprocess::innerLevelRequiredProperty()
+{
+ {
+ const auto errors = runQmltc(u"innerLevelRequiredProperty.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"innerLevelRequiredProperty.qml:7:5: Component is missing required property foo from here [required]"
+ ));
+ }
+}
+
QTEST_MAIN(tst_qmltc_qprocess)
#include "tst_qmltc_qprocess.moc"
diff --git a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
index 039f1647db..45669769e4 100644
--- a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
@@ -7,12 +7,18 @@
## tst_qmltyperegistrar Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmltyperegistrar LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
add_subdirectory(VersionZero)
add_subdirectory(UnregisteredTypes)
add_subdirectory(foreign)
qt_manual_moc(moc_files OUTPUT_MOC_JSON_FILES json_list noextheader
- INCLUDE_DIRECTORY_TARGETS Qt::Qml)
+ TARGETS Qt6::Qml)
# Dummy target to pass --private-includes to qmltyperegistrar for tst_qmltyperegistrar.
# We want to test that it expects files named foo_p.h appearing in foreign metatypes
@@ -76,9 +82,11 @@ qt_add_library(tst-qmltyperegistrar-with-dashes)
qt_autogen_tools_initial_setup(tst-qmltyperegistrar-with-dashes)
target_link_libraries(tst-qmltyperegistrar-with-dashes PRIVATE Qt::Core Qt::Qml)
qt_enable_autogen_tool(tst-qmltyperegistrar-with-dashes "moc" ON)
+
+qt_policy(SET QTP0001 NEW)
+
qt_add_qml_module(tst-qmltyperegistrar-with-dashes
URI Module-With-Dashes
- AUTO_RESOURCE_PREFIX
SOURCES
foo.cpp foo.h
OUTPUT_DIRECTORY Module-With-Dashes
@@ -90,4 +98,20 @@ qt_internal_add_resource(tst_qmltyperegistrar "resources"
"/"
FILES
duplicatedExports.json
+ missingTypes.json
)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+qt_add_library(tst-qmltyperegistrar-enum-foreign STATIC enum.cpp)
+qt_autogen_tools_initial_setup(tst-qmltyperegistrar-enum-foreign)
+qt_enable_autogen_tool(tst-qmltyperegistrar-enum-foreign "moc" ON)
+target_link_libraries(tst-qmltyperegistrar-enum-foreign PRIVATE Qt::QmlIntegration)
+
+qt_add_library(tst-qmltyperegistrar-enum STATIC)
+qt_autogen_tools_initial_setup(tst-qmltyperegistrar-enum)
+qt_enable_autogen_tool(tst-qmltyperegistrar-enum "moc" ON)
+target_link_libraries(tst-qmltyperegistrar-enum PRIVATE Qt::Qml tst-qmltyperegistrar-enum-foreign)
+
+qt_add_qml_module(tst-qmltyperegistrar-enum URI TstEnum OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/TstEnum)
+qt_autogen_tools_initial_setup(tst-qmltyperegistrar-enumplugin)
+qt_generate_foreign_qml_types(tst-qmltyperegistrar-enum-foreign tst-qmltyperegistrar-enum)
diff --git a/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt
index cb020e95e0..d469f1e97f 100644
--- a/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/CMakeLists.txt
@@ -2,13 +2,15 @@
# SPDX-License-Identifier: BSD-3-Clause
# Use NO_GENERATE_QMLTYPES to avoid static asserts during compilation of the types to be tested, same for NO_PLUGIN
+
+qt_policy(SET QTP0001 NEW)
+
qt_add_qml_module(UnregisteredTypes
STATIC
URI UnregisteredTypes
NO_GENERATE_QMLTYPES
NO_PLUGIN
SOURCES uncreatable.h
- AUTO_RESOURCE_PREFIX
)
qt_enable_autogen_tool(UnregisteredTypes "moc" ON)
diff --git a/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h b/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h
index 9726f4683c..2274f87e3d 100644
--- a/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h
+++ b/tests/auto/qml/qmltyperegistrar/UnregisteredTypes/uncreatable.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef UNCREATABLE_H
#define UNCREATABLE_H
@@ -39,6 +39,39 @@ public:
static SingletonCreatable3 *create(QQmlEngine *, QJSEngine *) { return nullptr; }
};
+class SingletonForeign : public QObject
+{
+ Q_OBJECT
+ SingletonForeign() = delete;
+};
+
+class SingletonLocalCreatable
+{
+ Q_GADGET
+ QML_FOREIGN(SingletonForeign)
+ QML_ELEMENT
+ QML_SINGLETON
+public:
+ static SingletonForeign *create(QQmlEngine *, QJSEngine *) { return nullptr; }
+};
+
+class SingletonLocalUncreatable1
+{
+ Q_GADGET
+ QML_FOREIGN(SingletonForeign)
+ QML_ELEMENT
+ QML_SINGLETON
+ static SingletonForeign *create(QQmlEngine *, QJSEngine *) { return nullptr; }
+};
+
+class SingletonLocalUncreatable2
+{
+ Q_GADGET
+ QML_FOREIGN(SingletonForeign)
+ QML_ELEMENT
+ QML_SINGLETON
+};
+
class SingletonIncreatable : public QObject
{
Q_OBJECT
@@ -184,4 +217,20 @@ class FixingBadUncreatable
QML_FOREIGN(BadUncreatable)
QML_UNCREATABLE("")
};
+
+class SingletonVesion0 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+ // is default constructible
+};
+
+class SingletonVesion1 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+ // is default constructible
+};
#endif // UNCREATABLE_H
diff --git a/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h b/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h
index a3277e6ab3..0ad477c3cb 100644
--- a/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h
+++ b/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef VERSION_ZERO_TYPE_H
#define VERSION_ZERO_TYPE_H
diff --git a/tests/auto/qml/qmltyperegistrar/duplicatedExports.h b/tests/auto/qml/qmltyperegistrar/duplicatedExports.h
index 5ae2e77551..e72a2f19c9 100644
--- a/tests/auto/qml/qmltyperegistrar/duplicatedExports.h
+++ b/tests/auto/qml/qmltyperegistrar/duplicatedExports.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DUPLICATEDEXPORTS_H
#define DUPLICATEDEXPORTS_H
diff --git a/tests/auto/qml/qmltyperegistrar/enum.cpp b/tests/auto/qml/qmltyperegistrar/enum.cpp
new file mode 100644
index 0000000000..34d2e00ffa
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/enum.cpp
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "enum.h"
+#include "moc_enum.cpp"
diff --git a/tests/auto/qml/qmltyperegistrar/enum.h b/tests/auto/qml/qmltyperegistrar/enum.h
new file mode 100644
index 0000000000..653c48c79f
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/enum.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef ENUM_NS_HELLO_H
+#define ENUM_NS_HELLO_H
+
+#include <QObject>
+#include <QtQmlIntegration/qqmlintegration.h>
+
+namespace Hello {
+ Q_NAMESPACE
+ QML_NAMED_ELEMENT(World)
+ enum class World {
+ Europe = 2024,
+ };
+ Q_ENUM_NS(World)
+}
+
+namespace Universe {
+ namespace Galaxy {
+ Q_NAMESPACE
+ QML_NAMED_ELEMENT(Solar)
+ enum class Solar {
+ Earth,
+ };
+ Q_ENUM_NS(Solar)
+ }
+
+ class Blackhole {
+ Q_GADGET
+ QML_ELEMENT
+ public:
+ enum SagittariusA {
+ Singularity
+ };
+ Q_ENUM(SagittariusA)
+ };
+}
+
+#endif // ENUM_NS_HELLO_H
diff --git a/tests/auto/qml/qmltyperegistrar/foo.cpp b/tests/auto/qml/qmltyperegistrar/foo.cpp
index ba95235bd6..c5ef8313dc 100644
--- a/tests/auto/qml/qmltyperegistrar/foo.cpp
+++ b/tests/auto/qml/qmltyperegistrar/foo.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "foo.h"
#include <QDebug>
diff --git a/tests/auto/qml/qmltyperegistrar/foo.h b/tests/auto/qml/qmltyperegistrar/foo.h
index 7872c35c17..5af2e11eaa 100644
--- a/tests/auto/qml/qmltyperegistrar/foo.h
+++ b/tests/auto/qml/qmltyperegistrar/foo.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef FOO_H
#define FOO_H
diff --git a/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt
index 5334225692..68223ae6a5 100644
--- a/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/foreign/CMakeLists.txt
@@ -10,7 +10,8 @@
qt_internal_add_cmake_library(foreign
STATIC
SOURCES
- foreign.cpp foreign.h foreign_p.h
+ foreign.cpp foreign.h
+ private/foreign_p.h
PUBLIC_LIBRARIES
Qt::Core
)
diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp b/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp
index f78dd47237..1691f5396a 100644
--- a/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp
+++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "foreign.h"
diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.h b/tests/auto/qml/qmltyperegistrar/foreign/foreign.h
index ea78a58432..79ac8074cf 100644
--- a/tests/auto/qml/qmltyperegistrar/foreign/foreign.h
+++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef FOREIGN_H
#define FOREIGN_H
diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h b/tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h
deleted file mode 100644
index fe7986471f..0000000000
--- a/tests/auto/qml/qmltyperegistrar/foreign/foreign_p.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef FOREIGN_P_H
-#define FOREIGN_P_H
-
-#include <QtCore/qobject.h>
-
-// qmltyperegistrar will assume this file is reachable under <private/foreign_p.h>
-// It's not true, but this is how it works on actual private headers in Qt.
-// See the trick in tst_qmltyperegistrar's CMakeLists.txt to turn on the --private-includes option.
-class ForeignPrivate : public QObject
-{
- Q_OBJECT
-Q_SIGNALS:
- void happens();
-};
-
-#endif // FOREIGN_P_H
diff --git a/tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h b/tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h
new file mode 100644
index 0000000000..ed23d78284
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/foreign/private/foreign_p.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef FOREIGN_P_H
+#define FOREIGN_P_H
+
+#include <QtCore/qobject.h>
+
+// qmltyperegistrar will assume this file is reachable under <private/foreign_p.h>
+// See the trick in tst_qmltyperegistrar's CMakeLists.txt to turn on the --private-includes option.
+class ForeignPrivate : public QObject
+{
+ Q_OBJECT
+Q_SIGNALS:
+ void happens();
+};
+
+#endif // FOREIGN_P_H
diff --git a/tests/auto/qml/qmltyperegistrar/hppheader.hpp b/tests/auto/qml/qmltyperegistrar/hppheader.hpp
index dc82fc8530..4278601a9e 100644
--- a/tests/auto/qml/qmltyperegistrar/hppheader.hpp
+++ b/tests/auto/qml/qmltyperegistrar/hppheader.hpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef HPPHEADER_HPP
#define HPPHEADER_HPP
diff --git a/tests/auto/qml/qmltyperegistrar/missingTypes.json b/tests/auto/qml/qmltyperegistrar/missingTypes.json
new file mode 100644
index 0000000000..dacec11c4c
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/missingTypes.json
@@ -0,0 +1,167 @@
+[
+ {
+ "classes": [
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "int"
+ },
+ {
+ "name": "QML.Extended",
+ "value": "QQmlIntForeign"
+ },
+ {
+ "name": "QML.Foreign",
+ "value": "int"
+ }
+ ],
+ "className": "QQmlIntForeign",
+ "gadget": true,
+ "qualifiedClassName": "QQmlIntForeign"
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "auto"
+ }
+ ],
+ "className": "ExcessiveVersion",
+ "object": true,
+ "properties": [
+ {
+ "constant": false,
+ "designable": true,
+ "final": false,
+ "index": 0,
+ "name": "palette",
+ "notify": "paletteChanged",
+ "read": "palette",
+ "required": false,
+ "revision": 1536,
+ "scriptable": true,
+ "stored": true,
+ "type": "int",
+ "user": false,
+ "write": "setPalette"
+ }
+ ],
+ "enums": [
+ {
+ "isClass": false,
+ "isFlag": false,
+ "name": "RestorationMode",
+ "type": "NotAnUnderlyingType",
+ "values": [
+ "RestoreNone",
+ "RestoreBinding",
+ "RestoreValue",
+ "RestoreBindingOrValue"
+ ]
+ }
+ ],
+ "qualifiedClassName": "ExcessiveVersion",
+ "signals": [
+ {
+ "access": "public",
+ "name": "paletteChanged",
+ "returnType": "void",
+ "revision": 1536
+ }
+ ],
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "NotQObject"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "Versioned"
+ },
+ {
+ "name": "QML.AddedInVersion",
+ "value": "264"
+ }
+ ],
+ "className": "AddedInLateVersion",
+ "object": true,
+ "properties": [
+ {
+ "constant": true,
+ "designable": true,
+ "final": false,
+ "index": 0,
+ "name": "revisioned",
+ "read": "revisioned",
+ "required": false,
+ "revision": 260,
+ "scriptable": true,
+ "stored": true,
+ "type": "NotAPropertyType",
+ "user": false
+ }
+ ],
+ "methods": [
+ {
+ "access": "public",
+ "arguments": [
+ {
+ "type": "NotAnArgumentType"
+ }
+ ],
+ "name": "createAThing",
+ "returnType": "NotAReturnType"
+ }
+ ],
+ "qualifiedClassName": "AddedInLateVersion",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "NotQObject"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Foreign",
+ "value": "Invisible"
+ },
+ {
+ "name": "QML.Element",
+ "value": "Invisible"
+ }
+ ],
+ "className": "InvisibleForeign",
+ "gadget": true,
+ "qualifiedClassName": "InvisibleForeign"
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "anonymous"
+ },
+ {
+ "name": "QML.Sequence",
+ "value": "NotQByteArray"
+ },
+ {
+ "name": "QML.Foreign",
+ "value": "std::vector<NotQByteArray>"
+ }
+ ],
+ "className": "NotQByteArrayStdVectorForeign",
+ "gadget": true,
+ "qualifiedClassName": "NotQByteArrayStdVectorForeign"
+ }
+ ],
+ "inputFile": "tst_qmltyperegistrar.h",
+ "outputRevision": 68
+ }
+]
diff --git a/tests/auto/qml/qmltyperegistrar/noextheader b/tests/auto/qml/qmltyperegistrar/noextheader
index 1bad168044..2b3579a34b 100644
--- a/tests/auto/qml/qmltyperegistrar/noextheader
+++ b/tests/auto/qml/qmltyperegistrar/noextheader
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef NOEXTHEADER
#define NOEXTHEADER
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
index a0f9a92b5c..822caea0d0 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmltyperegistrar.h"
#include <QtTest/qtest.h>
@@ -116,8 +116,8 @@ void tst_qmltyperegistrar::pastMajorVersions()
void tst_qmltyperegistrar::implementsInterfaces()
{
- QVERIFY(qmltypesData.contains("interfaces: [\"Interface\"]"));
- QVERIFY(qmltypesData.contains("interfaces: [\"Interface\", \"Interface2\"]"));
+ QVERIFY(qmltypesData.contains("interfaces: [\"Interface1\"]"));
+ QVERIFY(qmltypesData.contains("interfaces: [\"Interface1\", \"Interface2\"]"));
}
void tst_qmltyperegistrar::namespacedElement()
@@ -145,9 +145,9 @@ void tst_qmltyperegistrar::metaTypesRegistered()
auto verifyMetaType = [](const char *name, const char *className) {
const auto foundMetaType = QMetaType::fromName(name);
- QVERIFY(foundMetaType.isValid());
+ QVERIFY2(foundMetaType.isValid(), name);
QCOMPARE(foundMetaType.name(), name);
- QVERIFY(foundMetaType.metaObject());
+ QVERIFY2(foundMetaType.metaObject(), name);
QCOMPARE(foundMetaType.metaObject()->className(), className);
};
@@ -356,6 +356,12 @@ void tst_qmltyperegistrar::addRemoveVersion()
QCOMPARE(o->property("thing").toInt(), thingAccessible ? 24 : 0);
}
+void tst_qmltyperegistrar::addInMinorVersion()
+{
+ QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/MinorVersioned 1.5\"]"));
+ QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/MinorVersioned 1.2\"]"));
+}
+
#ifdef QT_QUICK_LIB
void tst_qmltyperegistrar::foreignRevisionedProperty()
{
@@ -398,23 +404,83 @@ void tst_qmltyperegistrar::duplicateExportWarnings()
MetaTypesJsonProcessor processor(true);
QVERIFY(processor.processTypes({ ":/duplicatedExports.json" }));
processor.postProcessTypes();
- QVector<QJsonObject> types = processor.types();
- QVector<QJsonObject> typesforeign = processor.foreignTypes();
+ QVector<MetaType> types = processor.types();
+ QVector<MetaType> typesforeign = processor.foreignTypes();
r.setTypes(types, typesforeign);
- auto expectWarning = [](QString message) {
- QTest::ignoreMessage(QtWarningMsg, qPrintable(message));
+ const auto expectWarning = [](const char *message) {
+ QTest::ignoreMessage(QtWarningMsg, message);
};
- expectWarning("Warning: ExportedQmlElement was registered multiple times by following Cpp "
- "classes: ExportedQmlElement, ExportedQmlElement2 (added in 1.2), "
- "ExportedQmlElementDifferentVersion (added in 1.0) (removed in 1.7)");
- expectWarning("Warning: SameNameSameExport was registered multiple times by following Cpp "
- "classes: SameNameSameExport, SameNameSameExport2 (added in 1.2), "
- "SameNameSameExportDifferentVersion (added in 1.0)");
+ expectWarning("Warning: duplicatedExports.h:: ExportedQmlElement is registered multiple times "
+ "by the following C++ classes: ExportedQmlElement, ExportedQmlElement2 "
+ "(added in 1.2), ExportedQmlElementDifferentVersion (added in 1.0) "
+ "(removed in 1.7)");
+ expectWarning("Warning: duplicatedExports.h:: SameNameSameExport is registered multiple times "
+ "by the following C++ classes: SameNameSameExport, SameNameSameExport2 "
+ "(added in 1.2), SameNameSameExportDifferentVersion (added in 1.0)");
QString outputData;
QTextStream output(&outputData, QIODeviceBase::ReadWrite);
- r.write(output);
+ r.write(output, "tst_qmltyperegistrar_qmltyperegistrations.cpp");
+}
+
+void tst_qmltyperegistrar::consistencyWarnings()
+{
+ QmlTypeRegistrar r;
+ r.setModuleVersions(QTypeRevision::fromVersion(1, 1), {}, false);
+ QString moduleName = "tstmodule";
+ QString targetNamespace = "tstnamespace";
+ r.setModuleNameAndNamespace(moduleName, targetNamespace);
+
+ MetaTypesJsonProcessor processor(true);
+
+ QVERIFY(processor.processTypes({ ":/missingTypes.json" }));
+ processor.postProcessTypes();
+
+ const auto expectWarning = [](const char *message) {
+ QTest::ignoreMessage(QtWarningMsg, message);
+ };
+
+ expectWarning("Warning: tst_qmltyperegistrar.h:: "
+ "NotQObject is used as base type but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotQObject is used as base type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: Invisible is declared as foreign type, "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotQByteArray is used as sequence value type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAPropertyType is used as property type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAnArgumentType is used as argument type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAReturnType is used as return type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAnUnderlyingType is used as enum type "
+ "but cannot be found.");
+
+ processor.postProcessForeignTypes();
+
+ QVector<MetaType> types = processor.types();
+ QVector<MetaType> typesforeign = processor.foreignTypes();
+ r.setTypes(types, typesforeign);
+
+ QString outputData;
+ QTextStream output(&outputData, QIODeviceBase::ReadWrite);
+
+ expectWarning("Warning: tst_qmltyperegistrar.h:: AddedInLateVersion is trying to register "
+ "property revisioned with future version 1.4 when module version is only 1.1");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: ExcessiveVersion is trying to register "
+ "property palette with future version 6.0 when module version is only 1.1");
+
+ r.write(output, "tst_qmltyperegistrar_qmltyperegistrations.cpp");
+
+ QTemporaryFile pluginTypes;
+ QVERIFY(pluginTypes.open());
+
+ expectWarning("Warning: tst_qmltyperegistrar.h:: Refusing to generate non-lowercase name "
+ "Invisible for unknown foreign type");
+
+ r.generatePluginTypes(pluginTypes.fileName());
}
void tst_qmltyperegistrar::clonedSignal()
@@ -450,70 +516,101 @@ void tst_qmltyperegistrar::hasIsConstantInParameters()
void tst_qmltyperegistrar::uncreatable()
{
+ using namespace QQmlPrivate;
+
// "normal" constructible types
- QVERIFY(QQmlPrivate::QmlMetaType<Creatable>::hasAcceptableCtors());
- QVERIFY(QQmlPrivate::QmlMetaType<Creatable2>::hasAcceptableCtors());
+ QVERIFY(QmlMetaType<Creatable>::hasAcceptableCtors());
+ QVERIFY(QmlMetaType<Creatable2>::hasAcceptableCtors());
// good singletons
- QVERIFY(QQmlPrivate::QmlMetaType<SingletonCreatable>::hasAcceptableSingletonCtors());
- QVERIFY(QQmlPrivate::QmlMetaType<SingletonCreatable2>::hasAcceptableSingletonCtors());
- QVERIFY(QQmlPrivate::QmlMetaType<SingletonCreatable3>::hasAcceptableSingletonCtors());
+ QCOMPARE((singletonConstructionMode<SingletonCreatable, SingletonCreatable>()),
+ SingletonConstructionMode::Factory);
+ QCOMPARE((singletonConstructionMode<SingletonCreatable2, SingletonCreatable2>()),
+ SingletonConstructionMode::Constructor);
+ QCOMPARE((singletonConstructionMode<SingletonCreatable2, SingletonCreatable2>()),
+ SingletonConstructionMode::Constructor);
+ QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalCreatable>()),
+ SingletonConstructionMode::FactoryWrapper);
// bad singletons
- QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatable>::hasAcceptableSingletonCtors());
- QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatable2>::hasAcceptableSingletonCtors());
- QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatable3>::hasAcceptableSingletonCtors());
- QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatable4>::hasAcceptableSingletonCtors());
- QVERIFY(!QQmlPrivate::QmlMetaType<SingletonIncreatableExtended>::hasAcceptableSingletonCtors());
+ QCOMPARE((singletonConstructionMode<SingletonIncreatable, SingletonIncreatable>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonIncreatable2, SingletonIncreatable2>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonIncreatable3, SingletonIncreatable3>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonIncreatable4, SingletonIncreatable4>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonIncreatableExtended,
+ SingletonIncreatableExtended>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalUncreatable1>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalUncreatable2>()),
+ SingletonConstructionMode::None);
#if QT_DEPRECATED_SINCE(6, 4)
QTest::ignoreMessage(
QtWarningMsg,
- "Singleton SingletonIncreatable needs either a default constructor or, "
- "when adding a default constructor is infeasible, a public static "
- "create(QQmlEngine *, QJSEngine *) method.");
+ "Singleton SingletonIncreatable needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
qmlRegisterTypesAndRevisions<SingletonIncreatable>("A", 1);
QTest::ignoreMessage(
QtWarningMsg,
- "Singleton SingletonIncreatable2 needs either a default constructor or, "
- "when adding a default constructor is infeasible, a public static "
- "create(QQmlEngine *, QJSEngine *) method.");
+ "Singleton SingletonIncreatable2 needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
qmlRegisterTypesAndRevisions<SingletonIncreatable2>("A", 1);
QTest::ignoreMessage(
QtWarningMsg,
- "Singleton SingletonIncreatable3 needs either a default constructor or, "
- "when adding a default constructor is infeasible, a public static "
- "create(QQmlEngine *, QJSEngine *) method.");
+ "Singleton SingletonIncreatable3 needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
qmlRegisterTypesAndRevisions<SingletonIncreatable3>("A", 1);
QTest::ignoreMessage(
QtWarningMsg,
- "Singleton SingletonIncreatable4 needs either a default constructor or, "
- "when adding a default constructor is infeasible, a public static "
- "create(QQmlEngine *, QJSEngine *) method.");
+ "Singleton SingletonIncreatable4 needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
qmlRegisterTypesAndRevisions<SingletonIncreatable4>("A", 1);
QTest::ignoreMessage(
QtWarningMsg,
- "Singleton SingletonIncreatableExtended needs either a default constructor or, "
- "when adding a default constructor is infeasible, a public static "
- "create(QQmlEngine *, QJSEngine *) method.");
+ "Singleton SingletonIncreatableExtended needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
qmlRegisterTypesAndRevisions<SingletonIncreatableExtended>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonForeign needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonLocalUncreatable1>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonForeign needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonLocalUncreatable2>("A", 1);
#endif
// QML_UNCREATABLE types
- QVERIFY(!QQmlPrivate::QmlMetaType<BadUncreatable>::hasAcceptableCtors());
- QVERIFY(!QQmlPrivate::QmlMetaType<BadUncreatableExtended>::hasAcceptableCtors());
- QVERIFY(!QQmlPrivate::QmlMetaType<GoodUncreatable>::hasAcceptableCtors());
- QVERIFY(!QQmlPrivate::QmlMetaType<UncreatableNeedsForeign>::hasAcceptableCtors());
- QVERIFY(!QQmlPrivate::QmlMetaType<GoodUncreatableExtended>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<BadUncreatable>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<BadUncreatableExtended>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<GoodUncreatable>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<UncreatableNeedsForeign>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<GoodUncreatableExtended>::hasAcceptableCtors());
#if QT_DEPRECATED_SINCE(6, 4)
QTest::ignoreMessage(
QtWarningMsg,
- "BadUncreatable is neither a QObject, nor default- and copy-constructible, "
- "nor uncreatable. You should not use it as a QML type.");
+ "BadUncreatable is neither a default constructible QObject, nor a default- "
+ "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
+ "You should not use it as a QML type.");
qmlRegisterTypesAndRevisions<BadUncreatable>("A", 1);
QTest::ignoreMessage(
QtWarningMsg,
- "BadUncreatableExtended is neither a QObject, nor default- and copy-constructible, "
- "nor uncreatable. You should not use it as a QML type.");
+ "BadUncreatableExtended is neither a default constructible QObject, nor a default- "
+ "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
+ "You should not use it as a QML type.");
qmlRegisterTypesAndRevisions<BadUncreatableExtended>("A", 1);
#endif
@@ -537,6 +634,26 @@ void tst_qmltyperegistrar::uncreatable()
qmlRegisterTypesAndRevisions<GoodUncreatableExtended>("A", 1);
}
+void tst_qmltyperegistrar::singletonVersions()
+{
+ QQmlEngine engine;
+ qmlRegisterTypesAndRevisions<SingletonVesion0>("A", 0);
+ qmlRegisterTypesAndRevisions<SingletonVesion1>("B", 1);
+
+ QQmlComponent c(&engine);
+ c.setData("import QtQuick\n"
+ "import A\n"
+ "import B\n"
+ "QtObject {\n"
+ " property QtObject v0: SingletonVesion0\n"
+ " property QtObject v1: SingletonVesion1\n"
+ "}", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY2(!obj->property("v0").isNull(), "Singleton version 0 is not registered");
+ QVERIFY2(!obj->property("v1").isNull(), "Singleton version 1 is not registered");
+}
+
void tst_qmltyperegistrar::baseVersionInQmltypes()
{
// Since it has no QML_ADDED_IN_VERSION, WithMethod was added in .0 of the current version.
@@ -544,4 +661,366 @@ void tst_qmltyperegistrar::baseVersionInQmltypes()
QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/WithMethod 1.0\"]"));
}
+void tst_qmltyperegistrar::unconstructibleValueType()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Unconstructible"
+ accessSemantics: "value"
+ exports: ["QmlTypeRegistrarTest/unconstructible 1.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [256]
+ })"));
+}
+
+void tst_qmltyperegistrar::constructibleValueType()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Constructible"
+ accessSemantics: "value"
+ exports: ["QmlTypeRegistrarTest/constructible 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Method {
+ name: "Constructible"
+ isConstructor: true
+ Parameter { name: "i"; type: "int" }
+ }
+ Method { name: "Constructible"; isCloned: true; isConstructor: true }
+ })"));
+}
+
+void tst_qmltyperegistrar::structuredValueType()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Structured"
+ accessSemantics: "value"
+ exports: ["QmlTypeRegistrarTest/structured 1.0"]
+ isCreatable: true
+ isStructured: true
+ exportMetaObjectRevisions: [256]
+ Property { name: "i"; type: "int"; index: 0; isFinal: true }
+ })"));
+}
+
+void tst_qmltyperegistrar::anonymousAndUncreatable()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "AnonymousAndUncreatable"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ })"));
+}
+
+void tst_qmltyperegistrar::omitInvisible()
+{
+ // If it cannot resolve the type a QML_FOREIGN refers to, it should not generate anything.
+ QVERIFY(qmltypesData.contains(
+ R"(Component { file: "tst_qmltyperegistrar.h"; name: "Invisible"; accessSemantics: "none" })"));
+}
+
+void tst_qmltyperegistrar::typedEnum()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "TypedEnum"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["QmlTypeRegistrarTest/TypedEnum 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Enum {
+ name: "UChar"
+ type: "uchar"
+ values: ["V0"]
+ }
+ Enum {
+ name: "Int8_T"
+ type: "int8_t"
+ values: ["V1"]
+ }
+ Enum {
+ name: "UInt8_T"
+ type: "uint8_t"
+ values: ["V2"]
+ }
+ Enum {
+ name: "Int16_T"
+ type: "int16_t"
+ values: ["V3"]
+ }
+ Enum {
+ name: "UInt16_T"
+ type: "uint16_t"
+ values: ["V4"]
+ }
+ Enum {
+ name: "Int32_T"
+ type: "int32_t"
+ values: ["V5"]
+ }
+ Enum {
+ name: "UInt32_T"
+ type: "uint32_t"
+ values: ["V6"]
+ }
+ Enum {
+ name: "S"
+ type: "qint16"
+ values: ["A", "B", "C"]
+ }
+ Enum {
+ name: "T"
+ type: "quint16"
+ values: ["D", "E", "F"]
+ }
+ Enum {
+ name: "U"
+ type: "qint8"
+ values: ["G", "H", "I"]
+ }
+ Enum {
+ name: "V"
+ type: "quint8"
+ values: ["J", "K", "L"]
+ }
+ })"));
+}
+
+void tst_qmltyperegistrar::listSignal()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "ListSignal"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ Signal {
+ name: "objectListHappened"
+ Parameter { type: "QList<QObject*>" }
+ }
+ })"));
+}
+
+void tst_qmltyperegistrar::withNamespace()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Bar"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ Property {
+ name: "outerBarProp"
+ type: "int"
+ read: "bar"
+ index: 0
+ isReadonly: true
+ isConstant: true
+ }
+ })"));
+
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Testing::Bar"
+ accessSemantics: "reference"
+ prototype: "Testing::Foo"
+ exports: ["QmlTypeRegistrarTest/Bar 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Property { name: "barProp"; type: "int"; read: "bar"; index: 0; isReadonly: true; isConstant: true }
+ })"));
+
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Testing::Foo"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ Property { name: "fooProp"; type: "int"; read: "foo"; index: 0; isReadonly: true; isConstant: true }
+ })"));
+
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Testing::Inner::Baz"
+ accessSemantics: "reference"
+ prototype: "Testing::Bar"
+ extension: "Bar"
+ exports: ["QmlTypeRegistrarTest/Baz 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ attachedType: "Testing::Foo"
+ })"));
+}
+
+void tst_qmltyperegistrar::sequenceRegistration()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "std::vector<QByteArray>"
+ accessSemantics: "sequence"
+ valueType: "QByteArray"
+ })"));
+}
+
+void tst_qmltyperegistrar::valueTypeSelfReference()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "QPersistentModelIndex"
+ accessSemantics: "value"
+ extension: "QPersistentModelIndexValueType"
+ })"));
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "QPersistentModelIndexValueType"
+ accessSemantics: "value"
+ Property { name: "row"; type: "int"; read: "row"; index: 0; isReadonly: true; isFinal: true }
+ })"));
+}
+
+void tst_qmltyperegistrar::foreignNamespaceFromGadget()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral(R"(
+ import QtQml
+ import QmlTypeRegistrarTest
+ QtObject {
+ objectName: 'b' + NetworkManager.B
+ }
+ )").toUtf8(), QUrl("foreignNamespaceFromGadget.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->objectName(), QStringLiteral("b1"));
+ }
+
+ {
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral(R"(
+ import QtQml
+ import QmlTypeRegistrarTest
+ QtObject {
+ objectName: 'b' + NotNamespaceForeign.B
+ }
+ )").toUtf8(), QUrl("foreignNamespaceFromGadget2.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->objectName(), QStringLiteral("b1"));
+ }
+}
+
+void tst_qmltyperegistrar::nameExplosion_data()
+{
+ QTest::addColumn<QByteArray>("qml");
+ QTest::addRow("Name1") << QByteArray("import QmlTypeRegistrarTest\nName1{}");
+ QTest::addRow("Name2") << QByteArray("import QmlTypeRegistrarTest\nName2{}");
+ QTest::addRow("NameExplosion") << QByteArray("import QmlTypeRegistrarTest\nNameExplosion{}");
+}
+
+void tst_qmltyperegistrar::nameExplosion()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "NameExplosion"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: [
+ "QmlTypeRegistrarTest/Name1 1.0",
+ "QmlTypeRegistrarTest/Name2 1.0",
+ "QmlTypeRegistrarTest/NameExplosion 1.0"
+ ]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ })"));
+
+ QFETCH(QByteArray, qml);
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+
+ c.setData(qml, QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
+
+void tst_qmltyperegistrar::javaScriptExtension()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "JavaScriptExtension"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ extension: "SymbolPrototype"
+ extensionIsJavaScript: true
+ exports: ["QmlTypeRegistrarTest/JavaScriptExtension 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ })"));
+}
+
+void tst_qmltyperegistrar::relatedAddedInVersion()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "AddedIn1_0"
+ accessSemantics: "reference"
+ prototype: "AddedIn1_5"
+ exports: [
+ "QmlTypeRegistrarTest/AddedIn1_0 1.0",
+ "QmlTypeRegistrarTest/AddedIn1_0 1.5"
+ ]
+ isCreatable: true
+ exportMetaObjectRevisions: [256, 261]
+ })"));
+}
+
+void tst_qmltyperegistrar::longNumberTypes()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "LongNumberTypes"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["QmlTypeRegistrarTest/LongNumberTypes 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Property { name: "a"; type: "qint64"; index: 0 }
+ Property { name: "b"; type: "int64_t"; index: 1 }
+ Property { name: "c"; type: "quint64"; index: 2 }
+ Property { name: "d"; type: "uint64_t"; index: 3 }
+ })"));
+}
+
+void tst_qmltyperegistrar::enumList() {
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "QList<NetworkManager::NM>"
+ accessSemantics: "sequence"
+ valueType: "NetworkManager::NM"
+ })"));
+}
+
+void tst_qmltyperegistrar::constReturnType()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "ConstInvokable"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["QmlTypeRegistrarTest/ConstInvokable 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Method { name: "getObject"; type: "QObject"; isPointer: true; isConstant: true }
+ })"));
+}
+
QTEST_MAIN(tst_qmltyperegistrar)
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
index df755472d7..371fb840d1 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
@@ -1,46 +1,49 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLTYPEREGISTRAR_H
#define TST_QMLTYPEREGISTRAR_H
#include "foreign.h"
-#include "foreign_p.h"
+#include "private/foreign_p.h"
-#include <QtQml/qqml.h>
-#include <QtQml/qqmlcomponent.h>
-#include <QtCore/qproperty.h>
-#include <QtCore/qtimeline.h>
-#include <QtCore/qrect.h>
#include <QtQmlTypeRegistrar/private/qqmltyperegistrar_p.h>
-#include <QtCore/qtemporaryfile.h>
#ifdef QT_QUICK_LIB
# include <QtQuick/qquickitem.h>
#endif
-class Interface {};
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlcomponent.h>
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qproperty.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qtemporaryfile.h>
+#include <QtCore/qtimeline.h>
+
+class Interface1 {};
class Interface2 {};
class Interface3 {};
QT_BEGIN_NAMESPACE
-Q_DECLARE_INTERFACE(Interface, "io.qt.bugreports.Interface");
+Q_DECLARE_INTERFACE(Interface1, "io.qt.bugreports.Interface1");
Q_DECLARE_INTERFACE(Interface2, "io.qt.bugreports.Interface2");
Q_DECLARE_INTERFACE(Interface3, "io.qt.bugreports.Interface3");
QT_END_NAMESPACE
-class ImplementsInterfaces : public QObject, public Interface
+class ImplementsInterfaces : public QObject, public Interface1
{
Q_OBJECT
QML_ELEMENT
- QML_IMPLEMENTS_INTERFACES(Interface)
+ QML_IMPLEMENTS_INTERFACES(Interface1)
};
-class ImplementsInterfaces2 : public QObject, public Interface, public Interface2
+class ImplementsInterfaces2 : public QObject, public Interface1, public Interface2
{
Q_OBJECT
QML_ELEMENT
- QML_IMPLEMENTS_INTERFACES(Interface Interface2)
+ QML_IMPLEMENTS_INTERFACES(Interface1 Interface2)
};
class ExcessiveVersion : public QObject
@@ -458,6 +461,29 @@ public:
int revisioned() const { return 24; }
};
+class AddedInLateMinorVersion : public QObject
+{
+ Q_OBJECT
+ QML_ADDED_IN_VERSION(1, 5)
+ Q_PROPERTY(int revisioned READ revisioned CONSTANT)
+ QML_NAMED_ELEMENT(MinorVersioned)
+public:
+ AddedInLateMinorVersion(QObject *parent = nullptr) : QObject(parent) {}
+ int revisioned() const { return 123; }
+};
+
+class RemovedInLateMinorVersion : public QObject
+{
+ Q_OBJECT
+ QML_ADDED_IN_VERSION(1, 2)
+ QML_REMOVED_IN_VERSION(1, 4)
+ Q_PROPERTY(int revisioned READ revisioned CONSTANT)
+ QML_NAMED_ELEMENT(MinorVersioned)
+public:
+ RemovedInLateMinorVersion(QObject *parent = nullptr) : QObject(parent) { }
+ int revisioned() const { return 456; }
+};
+
class RemovedInEarlyVersion : public AddedInLateVersion
{
Q_OBJECT
@@ -468,6 +494,23 @@ public:
RemovedInEarlyVersion(QObject *parent = nullptr) : AddedInLateVersion(parent) {}
};
+class AddedIn1_5 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 5)
+};
+
+// Slightly absurd. The reason for such a thing may be a change in the versioning
+// scheme of the base class. We still have to retain all of the version information
+// so that you can at least use version 1.5.
+class AddedIn1_0 : public AddedIn1_5
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 0)
+};
+
class HasResettableProperty : public QObject
{
Q_OBJECT
@@ -501,6 +544,254 @@ signals:
void clonedSignal(int i = 7);
};
+class Unconstructible
+{
+ Q_GADGET
+ QML_VALUE_TYPE(unconstructible)
+ int m_i = 11;
+};
+
+class Constructible
+{
+ Q_GADGET
+ QML_VALUE_TYPE(constructible)
+ QML_CONSTRUCTIBLE_VALUE
+public:
+ Q_INVOKABLE Constructible(int i = 12) : m_i(i) {}
+
+private:
+ int m_i;
+};
+
+class Structured
+{
+ Q_GADGET
+ QML_VALUE_TYPE(structured)
+ QML_STRUCTURED_VALUE
+ Q_PROPERTY(int i MEMBER m_i FINAL)
+
+private:
+ int m_i;
+};
+
+class AnonymousAndUncreatable : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+ QML_UNCREATABLE("Pointless uncreatable message")
+};
+
+class Invisible : public QObject
+{
+};
+
+struct InvisibleForeign
+{
+ Q_GADGET
+ QML_FOREIGN(Invisible)
+ QML_NAMED_ELEMENT(Invisible)
+};
+
+class TypedEnum : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ enum UChar: uchar { V0 = 41 };
+ Q_ENUM(UChar)
+ enum Int8_T: int8_t { V1 = 42 };
+ Q_ENUM(Int8_T)
+ enum UInt8_T: uint8_t { V2 = 43 };
+ Q_ENUM(UInt8_T)
+ enum Int16_T: int16_t { V3 = 44 };
+ Q_ENUM(Int16_T)
+ enum UInt16_T: uint16_t { V4 = 45 };
+ Q_ENUM(UInt16_T)
+ enum Int32_T: int32_t { V5 = 46 };
+ Q_ENUM(Int32_T)
+ enum UInt32_T: uint32_t { V6 = 47 };
+ Q_ENUM(UInt32_T)
+
+ // TODO: We cannot handle 64bit numbers as underlying types for enums.
+ // Luckily, moc generates bad code for those. So we don't have to, for now.
+
+ enum S: qint16 {
+ A, B, C
+ };
+ Q_ENUM(S)
+
+ enum T: quint16 {
+ D, E, F
+ };
+ Q_ENUM(T)
+
+ enum U: qint8 {
+ G, H, I
+ };
+ Q_ENUM(U)
+
+ enum V: quint8 {
+ J, K, L
+ };
+ Q_ENUM(V)
+};
+
+class ListSignal : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+Q_SIGNALS:
+ void objectListHappened(const QList<QObject *> &);
+};
+
+class Bar : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int outerBarProp READ bar CONSTANT)
+public:
+ Bar(QObject *parent = nullptr) : QObject(parent) {}
+ int bar() const { return 44; }
+};
+
+namespace Testing {
+
+class Foo : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int fooProp READ foo CONSTANT)
+
+public:
+ int foo() const { return 42; }
+};
+
+class Bar : public Foo
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int barProp READ bar CONSTANT)
+
+public:
+ int bar() const { return 43; }
+};
+
+namespace Inner {
+
+class Baz : public Bar
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ QML_EXTENDED(::Bar)
+ QML_ATTACHED(Foo)
+
+public:
+ static Foo *qmlAttachedProperties(QObject *) { return new Foo; }
+};
+
+} // namespace Inner
+} // namespace Testing
+
+struct QByteArrayStdVectorForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_SEQUENTIAL_CONTAINER(QByteArray)
+ QML_FOREIGN(std::vector<QByteArray>)
+};
+
+// Anonymous value type for an unknown foreign type
+struct QPersistentModelIndexValueType
+{
+ QPersistentModelIndex v;
+ Q_PROPERTY(int row READ row FINAL)
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_EXTENDED(QPersistentModelIndexValueType)
+ QML_FOREIGN(QPersistentModelIndex)
+
+public:
+ inline int row() const { return v.row(); }
+};
+
+
+namespace NetworkManager {
+Q_NAMESPACE
+
+enum NM { A, B, C};
+Q_ENUM_NS(NM)
+}
+
+struct NMForeign
+{
+ Q_GADGET
+ QML_NAMED_ELEMENT(NetworkManager)
+ QML_FOREIGN_NAMESPACE(NetworkManager)
+};
+
+struct NotNamespace {
+ Q_GADGET
+public:
+ enum Abc {
+ A, B, C, D
+ };
+ Q_ENUM(Abc);
+};
+
+struct NotNamespaceForeign {
+ Q_GADGET
+ QML_FOREIGN_NAMESPACE(NotNamespace)
+ QML_ELEMENT
+};
+
+class NameExplosion : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Name1)
+ QML_NAMED_ELEMENT(Name2)
+ QML_ELEMENT
+ QML_ANONYMOUS
+};
+
+class JavaScriptExtension : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_CLASSINFO("QML.Extended", "SymbolPrototype")
+ Q_CLASSINFO("QML.ExtensionIsJavaScript", "true")
+};
+
+class LongNumberTypes : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(qint64 a MEMBER m_a)
+ Q_PROPERTY(int64_t b MEMBER m_b)
+ Q_PROPERTY(quint64 c MEMBER m_c)
+ Q_PROPERTY(uint64_t d MEMBER m_d)
+
+ qint64 m_a = 1;
+ int64_t m_b = 2;
+ quint64 m_c = 3;
+ uint64_t m_d = 4;
+};
+
+struct EnumList
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QList<NetworkManager::NM>)
+ QML_SEQUENTIAL_CONTAINER(NetworkManager::NM)
+};
+
+class ConstInvokable : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ Q_INVOKABLE const QObject *getObject() { return nullptr; }
+};
+
class tst_qmltyperegistrar : public QObject
{
Q_OBJECT
@@ -537,6 +828,7 @@ private slots:
void methodReturnType();
void hasIsConstantInParameters();
void uncreatable();
+ void singletonVersions();
#ifdef QT_QUICK_LIB
void foreignRevisionedProperty();
@@ -544,11 +836,34 @@ private slots:
void addRemoveVersion_data();
void addRemoveVersion();
+ void addInMinorVersion();
void typeInModuleMajorVersionZero();
void resettableProperty();
void duplicateExportWarnings();
void clonedSignal();
void baseVersionInQmltypes();
+ void unconstructibleValueType();
+ void constructibleValueType();
+ void structuredValueType();
+ void anonymousAndUncreatable();
+ void omitInvisible();
+ void typedEnum();
+ void listSignal();
+ void withNamespace();
+ void sequenceRegistration();
+ void valueTypeSelfReference();
+ void foreignNamespaceFromGadget();
+
+ void nameExplosion_data();
+ void nameExplosion();
+
+ void javaScriptExtension();
+
+ void consistencyWarnings();
+ void relatedAddedInVersion();
+ void longNumberTypes();
+ void enumList();
+ void constReturnType();
private:
QByteArray qmltypesData;
diff --git a/tests/auto/qml/qqmlanybinding/CMakeLists.txt b/tests/auto/qml/qqmlanybinding/CMakeLists.txt
index 8cdecae3a8..f80acd8187 100644
--- a/tests/auto/qml/qqmlanybinding/CMakeLists.txt
+++ b/tests/auto/qml/qqmlanybinding/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qqmlanybinding Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlanybinding LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp b/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp
index 9e2ba24969..7e0a1c659a 100644
--- a/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp
+++ b/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlComponent>
#include <QtCore/QScopedPointer>
@@ -39,7 +39,7 @@ static int getRefCount(const QQmlAnyBinding &binding)
} else {
// this temporarily adds a refcount because we construc a new untypedpropertybinding
// thus -1
- return QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding())->ref - 1;
+ return QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding())->refCount() - 1;
}
}
diff --git a/tests/auto/qml/qqmlanybinding/withbindable.h b/tests/auto/qml/qqmlanybinding/withbindable.h
index 0672532344..322459f991 100644
--- a/tests/auto/qml/qqmlanybinding/withbindable.h
+++ b/tests/auto/qml/qqmlanybinding/withbindable.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WITH_BINDABLE_H
#define WITH_BINDABLE_H
diff --git a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
index 0570b9a95c..3b02ed09ef 100644
--- a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
+++ b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlapplicationengine Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlapplicationengine LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -57,5 +63,12 @@ qt_internal_extend_target(tst_qqmlapplicationengine CONDITION NOT ANDROID AND NO
DEFINES
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
)
+
add_subdirectory(testapp)
add_subdirectory(androidassets)
+add_dependencies(tst_qqmlapplicationengine testapp)
+
+add_subdirectory(loadFromModuleTranslationsQmlType)
+add_subdirectory(loadFromModuleTranslationsCppType)
+add_dependencies(tst_qqmlapplicationengine i18nLoadFromModuleQmlType)
+add_dependencies(tst_qqmlapplicationengine i18nLoadFromModuleCppType)
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt
index c5d42ed9c4..eca18010ee 100644
--- a/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_androidassets LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_androidassets
SOURCES
tst_androidassets.cpp
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp b/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp
index 7a6774c268..ed55afc693 100644
--- a/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlapplicationengine.h>
#include <QtTest/qsignalspy.h>
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/CMakeLists.txt
new file mode 100644
index 0000000000..f6fa23e010
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_standard_project_setup(REQUIRES 6.5 I18N_LANGUAGES es)
+
+qt_internal_add_executable(i18nLoadFromModuleCppType
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+)
+
+qt_add_qml_module(i18nLoadFromModuleCppType
+ URI TranslatedCpp
+ QML_FILES Main.qml
+)
+
+qt_internal_extend_target(i18nLoadFromModuleCppType
+ ENABLE_AUTOGEN_TOOLS
+ uic
+)
+
+qt_add_resources(i18nLoadFromModuleCppType "loadFromModuleCppTypeQmFile"
+ PREFIX
+ /qt/qml/TranslatedCpp/
+ FILES
+ i18n/qml_es.qm
+)
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/Main.qml b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/Main.qml
new file mode 100644
index 0000000000..f1d2010837
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/Main.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ Component.onCompleted: Qt.exit(0)
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.qm b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.qm
new file mode 100644
index 0000000000..e35ee63f89
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.qm
Binary files differ
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.ts b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.ts
new file mode 100644
index 0000000000..f8d478f056
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/i18n/qml_es.ts
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="es_ES">
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../main.cpp" line="17"/>
+ <source>Hello</source>
+ <translation>Hola</translation>
+ </message>
+</context>
+</TS>
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/main.cpp b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/main.cpp
new file mode 100644
index 0000000000..326a1397dc
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsCppType/main.cpp
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QLocale::setDefault(QLocale(QLocale::Language(qEnvironmentVariableIntValue("qtlang"))));
+ QGuiApplication app(argc, argv);
+ QQmlApplicationEngine engine;
+
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
+ &app, []() { QCoreApplication::exit(-1); },
+ Qt::QueuedConnection);
+ engine.loadFromModule("TranslatedCpp", "Main");
+ app.exec();
+
+ QString expected = qgetenv("LOADFROMMODULE_TEST_EXPECTED_OUTPUT");
+ QString actual = QObject::tr("Hello");
+
+ if (actual == expected)
+ return 0;
+
+ return actual[0].toLatin1();
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/CMakeLists.txt
new file mode 100644
index 0000000000..088648040d
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_standard_project_setup(REQUIRES 6.5 I18N_LANGUAGES fr)
+
+qt_internal_add_executable(i18nLoadFromModuleQmlType
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+)
+
+qt_add_qml_module(i18nLoadFromModuleQmlType
+ URI TranslatedQml
+ QML_FILES Main.qml
+)
+
+qt_internal_extend_target(i18nLoadFromModuleQmlType
+ ENABLE_AUTOGEN_TOOLS
+ uic
+)
+
+qt_add_resources(i18nLoadFromModuleQmlType "loadFromModuleQmlTypeQmFile"
+ PREFIX
+ /qt/qml/TranslatedQml/
+ FILES
+ i18n/qml_fr.qm
+)
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/Main.qml b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/Main.qml
new file mode 100644
index 0000000000..9f8e1984e9
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/Main.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+QtObject {
+ property string expected: "placeholder"
+ property string actual: qsTr("Hello")
+
+ function f() {
+ if (expected === actual)
+ Qt.exit(0)
+ else
+ Qt.exit(actual.charCodeAt(0))
+ }
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.qm b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.qm
new file mode 100644
index 0000000000..a53cf121a2
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.qm
Binary files differ
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.ts b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.ts
new file mode 100644
index 0000000000..87b46be9ca
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/i18n/qml_fr.ts
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="fr_FR">
+<context>
+ <name>Main</name>
+ <message>
+ <location filename="../Main.qml" line="5"/>
+ <source>Hello</source>
+ <translation>Salut</translation>
+ </message>
+</context>
+</TS>
diff --git a/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/main.cpp b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/main.cpp
new file mode 100644
index 0000000000..6f28ec8148
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/loadFromModuleTranslationsQmlType/main.cpp
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QLocale::setDefault(QLocale(QLocale::Language(qEnvironmentVariableIntValue("qtlang"))));
+ QGuiApplication app(argc, argv);
+ QQmlApplicationEngine engine;
+
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
+ &app, []() { QCoreApplication::exit(-1); },
+ Qt::QueuedConnection);
+ engine.loadFromModule("TranslatedQml", "Main");
+
+ QString expected = qgetenv("LOADFROMMODULE_TEST_EXPECTED_OUTPUT");
+ auto *root = engine.rootObjects().first();
+ root->setProperty("expected", expected);
+ root->metaObject()->invokeMethod(root, "f");
+ return app.exec();
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp
index 468c8428ba..6ccd256cca 100644
--- a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp
+++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
index 5774b68c32..3bf83d81e1 100644
--- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
+++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QQmlApplicationEngine>
#include <QScopedPointer>
@@ -28,6 +28,8 @@ private slots:
void removeObjectsWhenDestroyed();
void loadTranslation_data();
void loadTranslation();
+ void loadFromModuleTranslation_data();
+ void loadFromModuleTranslation();
void translationChange();
void setInitialProperties();
void failureToLoadTriggersWarningSignal();
@@ -144,7 +146,7 @@ void tst_qqmlapplicationengine::application()
#if QT_CONFIG(process)
QDir::setCurrent(buildDir);
- QProcess *testProcess = new QProcess(this);
+ std::unique_ptr<QProcess> testProcess = std::make_unique<QProcess>(this);
#ifdef Q_OS_QNX
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("QT_FORCE_STDERR_LOGGING", "1"); // QTBUG-76546
@@ -164,7 +166,7 @@ void tst_qqmlapplicationengine::application()
QVERIFY2(QString(testStdErr).endsWith(QString(expectedStdErr)),
QByteArray("\nExpected ending:\n") + expectedStdErr
+ QByteArray("\nActual output:\n") + testStdErr);
- delete testProcess;
+ testProcess.reset();
QDir::setCurrent(srcDir);
#else // process
QSKIP("No process support");
@@ -191,7 +193,7 @@ void tst_qqmlapplicationengine::applicationProperties()
QCoreApplication::setOrganizationName(firstOrganization);
QCoreApplication::setOrganizationDomain(firstDomain);
- QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("applicationTest.qml"));
+ std::unique_ptr<QQmlApplicationEngine> test = std::make_unique<QQmlApplicationEngine>(testFileUrl("applicationTest.qml"));
QObject* root = test->rootObjects().at(0);
QVERIFY(root);
QCOMPARE(root->property("originalName").toString(), firstName);
@@ -223,8 +225,6 @@ void tst_qqmlapplicationengine::applicationProperties()
QCOMPARE(versionChanged.size(), 1);
QCOMPARE(organizationChanged.size(), 1);
QCOMPARE(domainChanged.size(), 1);
-
- delete test;
}
void tst_qqmlapplicationengine::removeObjectsWhenDestroyed()
@@ -268,6 +268,53 @@ void tst_qqmlapplicationengine::loadTranslation()
QCOMPARE(rootObject->property("translation").toString(), translation);
}
+void tst_qqmlapplicationengine::loadFromModuleTranslation_data()
+{
+ QTest::addColumn<QString>("executable");
+ QTest::addColumn<QLocale::Language>("LANG");
+ QTest::addColumn<QString>("output");
+
+ QString qmlTypeExecutable = "loadFromModuleTranslationsQmlType/i18nLoadFromModuleQmlType";
+ QString cppTypeExecutable = "loadFromModuleTranslationsCppType/i18nLoadFromModuleCppType";
+
+ QTest::newRow("Qml: en -> en") << qmlTypeExecutable << QLocale::English << "Hello";
+ QTest::newRow("Qml: en -> fr") << qmlTypeExecutable << QLocale::French << "Salut";
+ QTest::newRow("Cpp: en -> en") << cppTypeExecutable << QLocale::English << "Hello";
+ QTest::newRow("Cpp: en -> es") << cppTypeExecutable << QLocale::Spanish << "Hola";
+}
+
+void tst_qqmlapplicationengine::loadFromModuleTranslation()
+{
+#if defined(Q_OS_ANDROID)
+ QSKIP("Test doesn't currently run on Android");
+ return;
+#endif
+
+#if QT_CONFIG(process)
+ QFETCH(QString, executable);
+ QFETCH(QLocale::Language, LANG);
+ QFETCH(QString, output);
+
+ QDir::setCurrent(buildDir);
+ QProcess app;
+ auto env = QProcessEnvironment::systemEnvironment();
+ env.insert("qtlang", QString::number(int(LANG)));
+ env.insert("LOADFROMMODULE_TEST_EXPECTED_OUTPUT", output);
+ app.setProcessEnvironment(env);
+ app.start(executable);
+ QVERIFY(app.waitForStarted());
+ QVERIFY(app.waitForFinished());
+
+ auto status = app.exitStatus();
+ auto code = app.exitCode();
+ QVERIFY2(code == 0,
+ QStringLiteral("status: %1, exitCode: %2").arg(status).arg(code).toStdString().c_str());
+ app.kill();
+#else
+ QSKIP("No process support");
+#endif
+}
+
void tst_qqmlapplicationengine::translationChange()
{
if (QLocale().language() == QLocale::SwissGerman) {
diff --git a/tests/auto/qml/qqmlbinding/CMakeLists.txt b/tests/auto/qml/qqmlbinding/CMakeLists.txt
index bcddd51730..0419240921 100644
--- a/tests/auto/qml/qqmlbinding/CMakeLists.txt
+++ b/tests/auto/qml/qqmlbinding/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlbinding Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlbinding LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -27,6 +33,22 @@ qt_internal_add_test(tst_qqmlbinding
TESTDATA ${test_data}
)
+qt_internal_add_test(tst_qqmlbinding_no_deferred_properties
+ SOURCES
+ tst_qqmlbinding.cpp
+ WithBindableProperties.h
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickPrivate
+ Qt::QuickTestUtilsPrivate
+ TESTDATA ${test_data}
+ DEFINES
+ QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES
+)
+
set_target_properties(tst_qqmlbinding PROPERTIES
QT_QML_MODULE_URI "test"
QT_QML_MODULE_VERSION 1.0
@@ -34,9 +56,13 @@ set_target_properties(tst_qqmlbinding PROPERTIES
_qt_internal_qml_type_registration(tst_qqmlbinding)
+set_target_properties(tst_qqmlbinding_no_deferred_properties PROPERTIES
+ QT_QML_MODULE_URI "test"
+ QT_QML_MODULE_VERSION 1.0
+)
+
+_qt_internal_qml_type_registration(tst_qqmlbinding_no_deferred_properties)
-## Scopes:
-#####################################################################
qt_internal_extend_target(tst_qqmlbinding CONDITION ANDROID OR IOS
DEFINES
@@ -47,3 +73,13 @@ qt_internal_extend_target(tst_qqmlbinding CONDITION NOT ANDROID AND NOT IOS
DEFINES
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
)
+
+qt_internal_extend_target(tst_qqmlbinding_no_deferred_properties CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qqmlbinding_no_deferred_properties CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
diff --git a/tests/auto/qml/qqmlbinding/WithBindableProperties.h b/tests/auto/qml/qqmlbinding/WithBindableProperties.h
index 17fa3cd9b3..8837098d81 100644
--- a/tests/auto/qml/qqmlbinding/WithBindableProperties.h
+++ b/tests/auto/qml/qqmlbinding/WithBindableProperties.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef WithBindableProperties_H
#define WithBindableProperties_H
diff --git a/tests/auto/qml/qqmlbinding/data/bindingOverwriting2.qml b/tests/auto/qml/qqmlbinding/data/bindingOverwriting2.qml
new file mode 100644
index 0000000000..8d15185506
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/bindingOverwriting2.qml
@@ -0,0 +1,21 @@
+pragma ComponentBehavior: Bound
+import QtQuick
+
+ListView {
+ id: list
+ property int i: 0
+
+ model: 1
+ delegate: Item {
+ id: cellRootID
+ required property int index
+ Timer {
+ interval: 1
+ running: true
+ onTriggered: {
+ cellRootID.index = index + 123
+ list.i = cellRootID.index
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml b/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml
new file mode 100644
index 0000000000..98b3aa6606
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml
@@ -0,0 +1,18 @@
+import QtQml.Models
+import QtQuick
+
+Instantiator {
+ id: inst
+ model: 1
+ property int check: 0
+
+ delegate: Binding {
+ ListView.delayRemove: true
+ Component.onCompleted: inst.check += 1
+ }
+
+ Component.onCompleted: {
+ if (inst.objectAt(0))
+ inst.check += 2
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml
index 8d89989613..40d4806b8c 100644
--- a/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml
+++ b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQml 2.14
diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml
index 9fd5fc77d0..5742a849b9 100644
--- a/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml
+++ b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQml 2.14
diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml
index ee183a0d10..e9d90b5e8b 100644
--- a/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml
+++ b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQml 2.14
diff --git a/tests/auto/qml/qqmlbinding/data/toggleEnableProperlyRemembersValues.qml b/tests/auto/qml/qqmlbinding/data/toggleEnableProperlyRemembersValues.qml
new file mode 100644
index 0000000000..251e71f771
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/toggleEnableProperlyRemembersValues.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+QtObject {
+ id: root
+ property bool enabled: false
+ property var func: function() { return 1 }
+ property var arr: [1, 2]
+ property Binding b: Binding {
+ root.func: function() { return 2 };
+ root.arr: [1, 2, 3]
+ when: root.enabled
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/whenEvaluatedEarlyEnough.qml b/tests/auto/qml/qqmlbinding/data/whenEvaluatedEarlyEnough.qml
new file mode 100644
index 0000000000..6245270e14
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/whenEvaluatedEarlyEnough.qml
@@ -0,0 +1,23 @@
+import QtQuick
+
+Item {
+ id: root
+ property bool toggle: true
+ property bool forceEnable: false
+
+ Item {
+ id: item1
+ property int i
+ }
+
+ Item {
+ id: item2
+ }
+
+ Binding {
+ target: root.toggle ? item1 : item2
+ when: root.forceEnable || (root.toggle ? item1 : item2).hasOwnProperty("i")
+ property: "i"
+ value: 42
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
index c78763cf5f..494d765798 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -38,6 +38,9 @@ private slots:
void intOverflow();
void generalizedGroupedProperties();
void localSignalHandler();
+ void whenEvaluatedEarlyEnough();
+ void propertiesAttachedToBindingItself();
+ void toggleEnableProperlyRemembersValues();
private:
QQmlEngine engine;
@@ -46,6 +49,9 @@ private:
tst_qqmlbinding::tst_qqmlbinding()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
+#ifdef QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES
+ qputenv("QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES", "1");
+#endif
}
void tst_qqmlbinding::binding()
@@ -365,10 +371,6 @@ void tst_qqmlbinding::disabledOnReadonlyProperty()
void tst_qqmlbinding::delayed()
{
-#ifdef Q_OS_ANDROID
- QSKIP("This test crashes on Android. QTBUG-103310");
-#endif
-
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("delayed.qml"));
QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())};
@@ -455,9 +457,15 @@ void tst_qqmlbinding::bindingOverwriting()
QQmlComponent c(&engine, testFileUrl("bindingOverwriting.qml"));
QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())};
QVERIFY(item);
+ QCOMPARE(messageHandler.messages().size(), 2);
+
+ QQmlComponent c2(&engine, testFileUrl("bindingOverwriting2.qml"));
+ QScopedPointer<QObject> o(c2.create());
+ QVERIFY(o);
+ QTRY_COMPARE(o->property("i").toInt(), 123);
+ QCOMPARE(messageHandler.messages().size(), 3);
QLoggingCategory::setFilterRules(QString());
- QCOMPARE(messageHandler.messages().size(), 2);
}
void tst_qqmlbinding::bindToQmlComponent()
@@ -593,6 +601,55 @@ void tst_qqmlbinding::localSignalHandler()
QCOMPARE(o->property("output").toString(), QStringLiteral("abc"));
}
+void tst_qqmlbinding::whenEvaluatedEarlyEnough()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("whenEvaluatedEarlyEnough.qml"));
+ QTest::failOnWarning(QRegularExpression(".*"));
+ std::unique_ptr<QObject> root { c.create() };
+ root->setProperty("toggle", false); // should not cause warnings
+ // until "when" is actually true
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg,
+ QRegularExpression(".*QML Binding: Property 'i' does not exist on Item.*"));
+ root->setProperty("forceEnable", true);
+}
+
+void tst_qqmlbinding::propertiesAttachedToBindingItself()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("propertiesAttachedToBindingItself.qml"));
+ QTest::failOnWarning(QRegularExpression(".*"));
+ std::unique_ptr<QObject> root { c.create() };
+ QVERIFY2(root, qPrintable(c.errorString()));
+ // 0 => everything broken; 1 => normal attached properties broken;
+ // 2 => Component.onCompleted broken, 3 => everything works
+ QTRY_COMPARE(root->property("check").toInt(), 3);
+}
+
+void tst_qqmlbinding::toggleEnableProperlyRemembersValues()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("toggleEnableProperlyRemembersValues.qml"));
+ std::unique_ptr<QObject> root { c.create() };
+ QVERIFY2(root, qPrintable(c.errorString()));
+ for (int i = 0; i < 3; ++i) {
+ {
+ QJSManagedValue arr(root->property("arr"), &e);
+ QJSManagedValue func(root->property("func"), &e);
+ QCOMPARE(arr.property("length").toInt(), 2);
+ QCOMPARE(func.call().toInt(), 1);
+ }
+ root->setProperty("enabled", true);
+ {
+ QJSManagedValue arr(root->property("arr"), &e);
+ QJSManagedValue func(root->property("func"), &e);
+ QCOMPARE(arr.property("length").toInt(), 3);
+ QCOMPARE(func.call().toInt(), 2);
+ }
+ root->setProperty("enabled", false);
+ }
+}
+
QTEST_MAIN(tst_qqmlbinding)
#include "tst_qqmlbinding.moc"
diff --git a/tests/auto/qml/qqmlchangeset/CMakeLists.txt b/tests/auto/qml/qqmlchangeset/CMakeLists.txt
index c4038b0eaf..32a20d7edf 100644
--- a/tests/auto/qml/qqmlchangeset/CMakeLists.txt
+++ b/tests/auto/qml/qqmlchangeset/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlchangeset Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlchangeset LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmlchangeset
SOURCES
tst_qqmlchangeset.cpp
diff --git a/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp b/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp
index 1aec5dcac3..8712d4953d 100644
--- a/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp
+++ b/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <qrandom.h>
#include <private/qqmlchangeset_p.h>
@@ -94,7 +95,7 @@ public:
bool applyChanges(QVector<int> &list, const QVector<QVector<Signal> > &changes)
{
- foreach (const SignalList &sl, changes) {
+ for (const SignalList &sl : changes) {
if (!applyChanges(list, sl))
return false;
}
@@ -104,7 +105,7 @@ public:
bool applyChanges(QVector<int> &list, const QVector<Signal> &changes)
{
QHash<QQmlChangeSet::MoveKey, int> removedValues;
- foreach (const Signal &signal, changes) {
+ for (const Signal &signal : changes) {
if (signal.isInsert()) {
if (signal.index < 0 || signal.index > list.size()) {
qDebug() << "insert out of range" << signal.index << list.size();
@@ -1133,7 +1134,7 @@ void tst_qqmlchangeset::sequence()
QQmlChangeSet set;
- foreach (const Signal &signal, input) {
+ for (const Signal &signal : std::as_const(input)) {
if (signal.isRemove())
set.remove(signal.index, signal.count);
else if (signal.isInsert())
@@ -1145,11 +1146,11 @@ void tst_qqmlchangeset::sequence()
}
SignalList changes;
- foreach (const QQmlChangeSet::Change &remove, set.removes())
+ for (const QQmlChangeSet::Change &remove : set.removes())
changes << Remove(remove.index, remove.count, remove.moveId, remove.offset);
- foreach (const QQmlChangeSet::Change &insert, set.inserts())
+ for (const QQmlChangeSet::Change &insert : set.inserts())
changes << Insert(insert.index, insert.count, insert.moveId, insert.offset);
- foreach (const QQmlChangeSet::Change &change, set.changes())
+ for (const QQmlChangeSet::Change &change : set.changes())
changes << Change(change.index, change.count);
VERIFY_EXPECTED_OUTPUT
@@ -1267,9 +1268,9 @@ void tst_qqmlchangeset::apply()
QQmlChangeSet set;
QQmlChangeSet linearSet;
- foreach (const SignalList &list, input) {
+ for (const SignalList &list : std::as_const(input)) {
QQmlChangeSet intermediateSet;
- foreach (const Signal &signal, list) {
+ for (const Signal &signal : list) {
if (signal.isRemove()) {
intermediateSet.remove(signal.index, signal.count);
linearSet.remove(signal.index, signal.count);
@@ -1285,15 +1286,15 @@ void tst_qqmlchangeset::apply()
}
SignalList changes;
- foreach (const QQmlChangeSet::Change &remove, set.removes())
+ for (const QQmlChangeSet::Change &remove : set.removes())
changes << Remove(remove.index, remove.count, remove.moveId, remove.offset);
- foreach (const QQmlChangeSet::Change &insert, set.inserts())
+ for (const QQmlChangeSet::Change &insert : set.inserts())
changes << Insert(insert.index, insert.count, insert.moveId, insert.offset);
SignalList linearChanges;
- foreach (const QQmlChangeSet::Change &remove, linearSet.removes())
+ for (const QQmlChangeSet::Change &remove : linearSet.removes())
linearChanges << Remove(remove.index, remove.count, remove.moveId, remove.offset);
- foreach (const QQmlChangeSet::Change &insert, linearSet.inserts())
+ for (const QQmlChangeSet::Change &insert : linearSet.inserts())
linearChanges << Insert(insert.index, insert.count, insert.moveId, insert.offset);
// The output in the failing tests isn't incorrect, merely sub-optimal.
@@ -1328,7 +1329,7 @@ void tst_qqmlchangeset::removeConsecutive()
QFETCH(SignalList, output);
QVector<QQmlChangeSet::Change> removes;
- foreach (const Signal &signal, input) {
+ for (const Signal &signal : std::as_const(input)) {
QVERIFY(signal.isRemove());
removes.append(QQmlChangeSet::Change(signal.index, signal.count, signal.moveId, signal.offset));
}
@@ -1337,7 +1338,7 @@ void tst_qqmlchangeset::removeConsecutive()
set.remove(removes);
SignalList changes;
- foreach (const QQmlChangeSet::Change &remove, set.removes())
+ for (const QQmlChangeSet::Change &remove : set.removes())
changes << Remove(remove.index, remove.count, remove.moveId, remove.offset);
QVERIFY(set.inserts().isEmpty());
QVERIFY(set.changes().isEmpty());
@@ -1368,7 +1369,7 @@ void tst_qqmlchangeset::insertConsecutive()
QFETCH(SignalList, output);
QVector<QQmlChangeSet::Change> inserts;
- foreach (const Signal &signal, input) {
+ for (const Signal &signal : std::as_const(input)) {
QVERIFY(signal.isInsert());
inserts.append(QQmlChangeSet::Change(signal.index, signal.count, signal.moveId, signal.offset));
}
@@ -1377,7 +1378,7 @@ void tst_qqmlchangeset::insertConsecutive()
set.insert(inserts);
SignalList changes;
- foreach (const QQmlChangeSet::Change &insert, set.inserts())
+ for (const QQmlChangeSet::Change &insert : set.inserts())
changes << Insert(insert.index, insert.count, insert.moveId, insert.offset);
QVERIFY(set.removes().isEmpty());
QVERIFY(set.changes().isEmpty());
@@ -1496,9 +1497,9 @@ void tst_qqmlchangeset::random()
}
SignalList output;
- foreach (const QQmlChangeSet::Change &remove, accumulatedSet.removes())
+ for (const QQmlChangeSet::Change &remove : accumulatedSet.removes())
output << Remove(remove.index, remove.count, remove.moveId, remove.offset);
- foreach (const QQmlChangeSet::Change &insert, accumulatedSet.inserts())
+ for (const QQmlChangeSet::Change &insert : accumulatedSet.inserts())
output << Insert(insert.index, insert.count, insert.moveId, insert.offset);
QVector<int> inputList;
diff --git a/tests/auto/qml/qqmlcomponent/CMakeLists.txt b/tests/auto/qml/qqmlcomponent/CMakeLists.txt
index cab87ac08d..874c7f0cc3 100644
--- a/tests/auto/qml/qqmlcomponent/CMakeLists.txt
+++ b/tests/auto/qml/qqmlcomponent/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlcomponent Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlcomponent LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -36,10 +42,13 @@ if(TARGET Qt::QuickControls2)
target_compile_definitions(tst_qqmlcomponent PRIVATE HAS_CONTROLS)
endif()
+qt_policy(SET QTP0001 NEW)
+
qt_add_qml_module(
tst_qqmlcomponent
+ SOURCES
+ lifecyclewatcher.h
URI test
- AUTO_RESOURCE_PREFIX
QML_FILES
"data/TestComponentWithIC.qml"
"data/withAot.qml"
diff --git a/tests/auto/qml/qqmlcomponent/data/complexObjectArgument.qml b/tests/auto/qml/qqmlcomponent/data/complexObjectArgument.qml
new file mode 100644
index 0000000000..71676a4415
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/complexObjectArgument.qml
@@ -0,0 +1,28 @@
+import QtQml 2.15
+
+QtObject {
+ id: root
+ Component.onCompleted: {
+ function WithPrototype(refMsgSeqNr) {
+ this.init(refMsgSeqNr)
+ };
+
+ WithPrototype.prototype = {
+ init: function(refMsgSeqNr) {
+ this.testObj = {
+ has: function(a) { return a === refMsgSeqNr }
+ }
+
+ this.protocolSubTypeID = 2
+ this.messageControl = 0
+ this.referredMsgSequenceNumber = refMsgSeqNr
+ }
+ };
+
+ let comp = Qt.createComponent("dynamic.qml");
+ let inst1 = comp.createObject(root, { testObj: new Set(), });
+ let inst2 = comp.createObject(root, new WithPrototype(1));
+
+ objectName = inst1.use() + " - " + inst2.use();
+ }
+}
diff --git a/tests/auto/qml/qqmlcomponent/data/createObject.qml b/tests/auto/qml/qqmlcomponent/data/createObject.qml
index afd9e71229..c9ca605f8f 100644
--- a/tests/auto/qml/qqmlcomponent/data/createObject.qml
+++ b/tests/auto/qml/qqmlcomponent/data/createObject.qml
@@ -1,5 +1,4 @@
-import QtQuick 2.0
-import QtQuick.Window 2.0
+import QtQuick
Item {
property QtObject qtobjectParent: QtObject { }
diff --git a/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml b/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml
index 3391b3a266..63aeb9415e 100644
--- a/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml
+++ b/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml
@@ -11,6 +11,9 @@ Item{
property QtObject badRequired: null
property QtObject goodRequired: null
+ property QtObject bindingAsInitial: null
+ property bool bindingUsed: false
+
Component{
id: a
Rectangle {
@@ -21,7 +24,7 @@ Item{
id: b
Item{
property bool testBool: false
- property int testInt: null
+ property int testInt: { return null; }
property QtObject testObject: null
}
}
@@ -43,6 +46,11 @@ Item{
}
}
+ Component {
+ id: e
+ Rectangle {}
+ }
+
Component.onCompleted: {
root.declarativerectangle = a.createObject(root, {"x":17,"y":17, "color":"white", "border.width":3, "innerRect.border.width": 20});
root.declarativeitem = b.createObject(root, {"x":17,"y":17,"testBool":true,"testInt":17,"testObject":root});
@@ -52,5 +60,9 @@ Item{
root.badRequired = d.createObject(root, { "not_i": 42 });
root.goodRequired = d.createObject(root, { "i": 42 });
+
+ root.bindingAsInitial = e.createObject(root, {color: Qt.binding(() => {
+ root.bindingUsed = true; return '#ff0000'
+ })});
}
}
diff --git a/tests/auto/qml/qqmlcomponent/data/createQmlObject.qml b/tests/auto/qml/qqmlcomponent/data/createQmlObject.qml
index 282ab509f0..480835a5b1 100644
--- a/tests/auto/qml/qqmlcomponent/data/createQmlObject.qml
+++ b/tests/auto/qml/qqmlcomponent/data/createQmlObject.qml
@@ -1,5 +1,4 @@
-import QtQuick 2.0
-import QtQuick.Window 2.0
+import QtQuick
Item {
property QtObject qtobjectParent: QtObject { }
@@ -19,14 +18,14 @@ Item {
property QtObject window_window : null
Component.onCompleted: {
- qtobject_qtobject = Qt.createQmlObject("import QtQuick 2.0; QtObject{}", qtobjectParent);
- qtobject_item = Qt.createQmlObject("import QtQuick 2.0; Item{}", qtobjectParent);
- qtobject_window = Qt.createQmlObject("import QtQuick.Window 2.0; Window{}", qtobjectParent);
- item_qtobject = Qt.createQmlObject("import QtQuick 2.0; QtObject{}", itemParent);
- item_item = Qt.createQmlObject("import QtQuick 2.0; Item{}", itemParent);
- item_window = Qt.createQmlObject("import QtQuick.Window 2.0; Window{}", itemParent);
- window_qtobject = Qt.createQmlObject("import QtQuick 2.0; QtObject{}", windowParent);
- window_item = Qt.createQmlObject("import QtQuick 2.0; Item{}", windowParent);
- window_window = Qt.createQmlObject("import QtQuick.Window 2.0; Window{}", windowParent);
+ qtobject_qtobject = Qt.createQmlObject("import QtQuick; QtObject{}", qtobjectParent);
+ qtobject_item = Qt.createQmlObject("import QtQuick; Item{}", qtobjectParent);
+ qtobject_window = Qt.createQmlObject("import QtQuick; Window{}", qtobjectParent);
+ item_qtobject = Qt.createQmlObject("import QtQuick; QtObject{}", itemParent);
+ item_item = Qt.createQmlObject("import QtQuick; Item{}", itemParent);
+ item_window = Qt.createQmlObject("import QtQuick; Window{}", itemParent);
+ window_qtobject = Qt.createQmlObject("import QtQuick; QtObject{}", windowParent);
+ window_item = Qt.createQmlObject("import QtQuick; Item{}", windowParent);
+ window_window = Qt.createQmlObject("import QtQuick; Window{}", windowParent);
}
}
diff --git a/tests/auto/qml/qqmlcomponent/data/dynamic.qml b/tests/auto/qml/qqmlcomponent/data/dynamic.qml
new file mode 100644
index 0000000000..b9a54d53ff
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/dynamic.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ property var testObj
+ function use() { return testObj.has(1) ? 25 : 26; }
+}
diff --git a/tests/auto/qml/qqmlcomponent/data/removeBinding.qml b/tests/auto/qml/qqmlcomponent/data/removeBinding.qml
new file mode 100644
index 0000000000..091f6991be
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/removeBinding.qml
@@ -0,0 +1,32 @@
+import QtQml 2.15
+
+QtObject {
+ id: root
+ objectName: "400"
+
+ property Component c: Component {
+ id: customItem
+ QtObject {
+ objectName: root.objectName
+ }
+ }
+
+ property string result: {
+ const properties = {
+ "objectName": "42",
+ }
+ const item = customItem.createObject(root, properties)
+ return item.objectName;
+ }
+
+ property string result2: {
+ const properties = {
+ "objectName": "43",
+ }
+
+ // add some junk argument to trigger the QQmlV4Function overload
+ const item = customItem.createObject(root, properties, 13)
+
+ return item.objectName;
+ }
+}
diff --git a/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h b/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h
new file mode 100644
index 0000000000..e974681d25
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef LIFECYCLEWATCHER_H
+#define LIFECYCLEWATCHER_H
+
+#include <QtQml/qqmlparserstatus.h>
+#include <private/qqmlfinalizer_p.h>
+#include <QtCore/qobject.h>
+#include <QtQml/qqml.h>
+
+class LifeCycleWatcher : public QObject, public QQmlParserStatus, public QQmlFinalizerHook
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_INTERFACES(QQmlParserStatus)
+ Q_INTERFACES(QQmlFinalizerHook)
+public:
+ void classBegin() override {states.push_back(1); }
+ void componentComplete() override {states.push_back(2);};
+ void componentFinalized() override { states.push_back(3); }
+ QList<int> states;
+};
+#endif
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 5203ba9615..ea06a11006 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
@@ -21,7 +21,7 @@
#include <private/qv4executablecompilationunit_p.h>
#include <qcolor.h>
#include <qsignalspy.h>
-
+#include "lifecyclewatcher.h"
#include <algorithm>
using namespace Qt::StringLiterals;
@@ -90,13 +90,6 @@ public slots:
}
};
-static void gc(QQmlEngine &engine)
-{
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
-}
-
class tst_qqmlcomponent : public QQmlDataTest
{
Q_OBJECT
@@ -140,11 +133,16 @@ private slots:
void boundComponent();
void loadFromModule_data();
void loadFromModule();
+ void loadFromModuleLifecycle();
void loadFromModuleThenCreateWithIncubator();
void loadFromModuleFailures_data();
void loadFromModuleFailures();
void loadFromModuleRequired();
void loadFromQrc();
+ void removeBinding();
+ void complexObjectArgument();
+ void bindingEvaluationOrder();
+ void compilationUnitsWithSameUrl();
private:
QQmlEngine engine;
@@ -182,14 +180,12 @@ void tst_qqmlcomponent::loadEmptyUrl()
void tst_qqmlcomponent::qmlIncubateObject()
{
QQmlComponent component(&engine, testFileUrl("incubateObject.qml"));
- QObject *object = component.create();
+ std::unique_ptr<QObject> object { component.create() };
QVERIFY(object != nullptr);
QCOMPARE(object->property("test1").toBool(), true);
QCOMPARE(object->property("test2").toBool(), false);
QTRY_VERIFY(object->property("test2").toBool());
-
- delete object;
}
void tst_qqmlcomponent::qmlCreateWindow()
@@ -282,7 +278,7 @@ void tst_qqmlcomponent::qmlCreateObjectWithProperties()
QTest::ignoreMessage(
QtMsgType::QtWarningMsg,
QRegularExpression(
- ".*createObjectWithScript.qml:42:13: Required property i was not initialized"));
+ ".*createObjectWithScript.qml:45:13: Required property i was not initialized"));
QQmlComponent component(&engine, testFileUrl("createObjectWithScript.qml"));
QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8());
@@ -342,6 +338,12 @@ void tst_qqmlcomponent::qmlCreateObjectWithProperties()
QCOMPARE(goodRequired->parent(), object.data());
QCOMPARE(goodRequired->property("i").value<int>(), 42);
}
+
+ {
+ QScopedPointer<QObject> bindingAsInitial(object->property("bindingAsInitial").value<QObject *>());
+ QVERIFY(bindingAsInitial);
+ QVERIFY(object->property("bindingUsed").toBool());
+ }
}
void tst_qqmlcomponent::qmlCreateObjectClean()
@@ -390,11 +392,11 @@ void tst_qqmlcomponent::qmlCreateParentReference()
QQmlComponent component(&engine, testFileUrl("createParentReference.qml"));
QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8());
- QObject *object = component.create();
+ std::unique_ptr<QObject> object { component.create() };
QVERIFY(object != nullptr);
- QVERIFY(QMetaObject::invokeMethod(object, "createChild"));
- delete object;
+ QVERIFY(QMetaObject::invokeMethod(object.get(), "createChild"));
+ object.reset();
engine.setOutputWarningsToStandardError(false);
QCOMPARE(engine.outputWarningsToStandardError(), false);
@@ -416,10 +418,8 @@ void tst_qqmlcomponent::async()
QCOMPARE(watcher.ready, 1);
QCOMPARE(watcher.error, 0);
- QObject *object = component.create();
+ std::unique_ptr<QObject> object { component.create() };
QVERIFY(object != nullptr);
-
- delete object;
}
void tst_qqmlcomponent::asyncHierarchy()
@@ -437,7 +437,7 @@ void tst_qqmlcomponent::asyncHierarchy()
QCOMPARE(watcher.ready, 1);
QCOMPARE(watcher.error, 0);
- QObject *root = component.create();
+ std::unique_ptr<QObject> root { component.create() };
QVERIFY(root != nullptr);
// ensure that the parent-child relationship hierarchy is correct
@@ -461,8 +461,6 @@ void tst_qqmlcomponent::asyncHierarchy()
// ensure that values and bindings are assigned correctly
QVERIFY(root->property("success").toBool());
-
- delete root;
}
void tst_qqmlcomponent::asyncForceSync()
@@ -1245,12 +1243,14 @@ void tst_qqmlcomponent::boundComponent()
{
QQmlComponent component(&engine, testFileUrl("nestedBoundComponent.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QVERIFY(component.isBound());
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
QQmlComponent *nestedComponent = o->property("c").value<QQmlComponent *>();
QVERIFY(nestedComponent != nullptr);
+ QVERIFY(nestedComponent->isBound());
QObject *nestedObject = o->property("o").value<QObject *>();
QVERIFY(nestedObject != nullptr);
@@ -1269,6 +1269,7 @@ void tst_qqmlcomponent::boundComponent()
{
QQmlComponent component(&engine, testFileUrl("BoundInlineComponent.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QVERIFY(component.isBound());
QScopedPointer<QObject> o(component.create());
QVERIFY2(!o.isNull(), qPrintable(component.errorString()));
@@ -1282,11 +1283,22 @@ void tst_qqmlcomponent::boundComponent()
{
QQmlComponent component(&engine, testFileUrl("boundInlineComponentUser.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QVERIFY(!component.isBound());
QScopedPointer<QObject> o(component.create());
QVERIFY(o.isNull());
QVERIFY(component.errorString().contains(
QLatin1String("Cannot instantiate bound inline component in different file")));
+
+ }
+
+ {
+ QQmlComponent component(&engine);
+ QVERIFY(!component.isBound());
+
+ component.setData("pragma ComponentBehavior: Bound\nsyntax error", QUrl());
+ QCOMPARE(component.errorString(), ":2 Syntax error\n"_L1);
+ QVERIFY(!component.isBound());
}
}
@@ -1334,6 +1346,34 @@ void tst_qqmlcomponent::loadFromModule()
name);
}
+void tst_qqmlcomponent::loadFromModuleLifecycle()
+{
+ QQmlEngine engine;
+ QList<int> loadFromModuleOrder;
+ QList<int> plainLoadOrder;
+ const QList<int> expected {1, 2, 3};
+ {
+ QQmlComponent component(&engine);
+ component.loadFromModule("test", "LifeCycleWatcher");
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ std::unique_ptr<QObject> root{ component.create() };
+ LifeCycleWatcher *watcher = qobject_cast<LifeCycleWatcher *>(root.get());
+ QVERIFY(watcher);
+ loadFromModuleOrder = watcher->states;
+ QCOMPARE(loadFromModuleOrder, expected);
+ }
+ {
+ QQmlComponent component(&engine);
+ component.setData("import test; LifeCycleWatcher {}", {});
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ std::unique_ptr<QObject> root{ component.create() };
+ LifeCycleWatcher *watcher = qobject_cast<LifeCycleWatcher *>(root.get());
+ QVERIFY(watcher);
+ plainLoadOrder = watcher->states;
+ }
+ QCOMPARE(loadFromModuleOrder, plainLoadOrder);
+}
+
struct CallVerifyingIncubtor : QQmlIncubator
{
void setInitialState(QObject *) override { setInitialStateCalled = true; }
@@ -1373,6 +1413,10 @@ void tst_qqmlcomponent::loadFromModuleFailures_data()
QTest::addRow("CppSingleton") << u"QtQuick"_s
<< u"Application"_s
<< u"Application is a singleton, and cannot be loaded"_s;
+ QTest::addRow("passedFileName") << "plainqml"
+ << "Plain.qml"
+ << R"(Type "Plain" from module "plainqml" contains no inline component named "qml". )"
+ R"(To load the type "Plain", drop the ".qml" extension.)";
}
void tst_qqmlcomponent::loadFromModuleFailures()
@@ -1384,7 +1428,11 @@ void tst_qqmlcomponent::loadFromModuleFailures()
QQmlEngine engine;
QQmlComponent component(&engine);
QSignalSpy errorSpy(&component, &QQmlComponent::statusChanged);
+ QSignalSpy progressSpy(&component, &QQmlComponent::progressChanged);
component.loadFromModule(uri, typeName);
+ // verify that we changed the progress correctly to 1
+ QTRY_VERIFY(!progressSpy.isEmpty());
+ QTRY_COMPARE(progressSpy.last().at(0).toDouble(), 1.0);
QVERIFY(!errorSpy.isEmpty());
QCOMPARE(errorSpy.first().first().value<QQmlComponent::Status>(),
QQmlComponent::Error);
@@ -1422,7 +1470,99 @@ void tst_qqmlcomponent::loadFromQrc()
QQmlComponentPrivate *p = QQmlComponentPrivate::get(&component);
QVERIFY(p);
QVERIFY(p->compilationUnit);
- QVERIFY(p->compilationUnit->aotCompiledFunctions);
+ QVERIFY(p->compilationUnit->baseCompilationUnit()->aotCompiledFunctions);
+}
+
+void tst_qqmlcomponent::removeBinding()
+{
+ QQmlEngine e;
+ const QUrl url = testFileUrl("removeBinding.qml");
+ QQmlComponent c(&e, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + QStringLiteral(":7:27: QML Component: Unsuitable arguments "
+ "passed to createObject(). The first argument "
+ "should be a QObject* or null, and the second "
+ "argument should be a JavaScript object or a "
+ "QVariantMap")));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("result"), QStringLiteral("42"));
+ QCOMPARE(o->property("result2"), QStringLiteral("43"));
+}
+
+void tst_qqmlcomponent::complexObjectArgument()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("complexObjectArgument.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), QStringLiteral("26 - 25"));
+}
+
+void tst_qqmlcomponent::bindingEvaluationOrder()
+{
+ // Note: This test explicitly tests the order in which bindings are
+ // evaluated, which is generally unspecified. This, however, exists
+ // as a regression test for QQmlObjectCreator code that is supposed
+ // to *not* mess with the QmlIR given to it.
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(R"(
+ import QtQml
+ QtObject {
+ property var myList: ["dummy"]
+ property int p1: { myList.push("p1"); return 0; }
+ property int p2: { myList.push("p2"); return 0; }
+ })", QUrl());
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+
+ const QList<QVariant> myList = o->property("myList").toList();
+ QCOMPARE(myList.size(), 3);
+ QCOMPARE(myList[0].toString(), u"dummy"_s);
+ QCOMPARE(myList[1].toString(), u"p1"_s);
+ QCOMPARE(myList[2].toString(), u"p2"_s);
+}
+
+void tst_qqmlcomponent::compilationUnitsWithSameUrl()
+{
+ QQmlEngine engine;
+ engine.setUiLanguage("de_CH");
+
+ std::vector<std::unique_ptr<QObject>> objects;
+ for (int i = 0; i < 10; ++i) {
+ QQmlComponent component(&engine);
+ component.setData(R"(
+ import QtQml
+ QtObject {
+ function returnThing() : string { return Qt.uiLanguage }
+ }
+ )", QUrl("duplicate.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ std::unique_ptr<QObject> o(component.create());
+ QVERIFY(o.get());
+
+ QString result;
+ QMetaObject::invokeMethod(o.get(), "returnThing", Q_RETURN_ARG(QString, result));
+ QCOMPARE(result, "de_CH");
+
+ objects.push_back(std::move(o));
+ }
+
+ gc(engine);
+
+ for (const auto &o: objects) {
+ QString result;
+ QMetaObject::invokeMethod(o.get(), "returnThing", Q_RETURN_ARG(QString, result));
+ QCOMPARE(result, "de_CH");
+ }
}
QTEST_MAIN(tst_qqmlcomponent)
diff --git a/tests/auto/qml/qqmlconnections/CMakeLists.txt b/tests/auto/qml/qqmlconnections/CMakeLists.txt
index d75c86f417..dd1814fed1 100644
--- a/tests/auto/qml/qqmlconnections/CMakeLists.txt
+++ b/tests/auto/qml/qqmlconnections/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlconnections Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlconnections LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlconnections/data/badSignalHandlerName.qml b/tests/auto/qml/qqmlconnections/data/badSignalHandlerName.qml
new file mode 100644
index 0000000000..921787aa36
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/badSignalHandlerName.qml
@@ -0,0 +1,15 @@
+import QtQml
+
+QtObject {
+ id: root
+ signal _foo
+
+ property int handled: 0
+
+ property Connections c: Connections {
+ target: root
+ function on_Foo() { root.handled += 1 }
+ function on_foo() { root.handled += 2 }
+ }
+}
+
diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
index f23c474907..88441e9dac 100644
--- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
+++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -55,6 +55,7 @@ private slots:
void bindToPropertyWithUnderscoreChangeHandler();
void invalidTarget();
+ void badSignalHandlerName();
private:
QQmlEngine engine;
void prefixes();
@@ -76,26 +77,22 @@ void tst_qqmlconnections::defaultValues()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("test-connection3.qml"));
- QQmlConnections *item = qobject_cast<QQmlConnections*>(c.create());
+ std::unique_ptr<QQmlConnections> item { qobject_cast<QQmlConnections*>(c.create()) };
QVERIFY(item != nullptr);
QVERIFY(!item->target());
-
- delete item;
}
void tst_qqmlconnections::properties()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("test-connection2.qml"));
- QQmlConnections *item = qobject_cast<QQmlConnections*>(c.create());
+ std::unique_ptr<QQmlConnections> item { qobject_cast<QQmlConnections*>(c.create()) };
QVERIFY(item != nullptr);
QVERIFY(item != nullptr);
- QCOMPARE(item->target(), item);
-
- delete item;
+ QCOMPARE(item->target(), item.get());
}
void tst_qqmlconnections::connection()
@@ -103,7 +100,7 @@ void tst_qqmlconnections::connection()
QFETCH(QString, prefix);
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(c.create()) };
QVERIFY(item != nullptr);
@@ -112,8 +109,6 @@ void tst_qqmlconnections::connection()
emit item->setWidth(100.);
QCOMPARE(item->width(), 100.);
QCOMPARE(item->property("tested").toBool(), true);
-
- delete item;
}
void tst_qqmlconnections::trimming()
@@ -121,20 +116,18 @@ void tst_qqmlconnections::trimming()
QFETCH(QString, prefix);
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl(prefix + "/trimming.qml"));
- QObject *object = c.create();
+ std::unique_ptr<QObject> object { c.create() };
QVERIFY(object != nullptr);
QCOMPARE(object->property("tested").toString(), QString(""));
int index = object->metaObject()->indexOfSignal("testMe(int,QString)");
QMetaMethod method = object->metaObject()->method(index);
- method.invoke(object,
+ method.invoke(object.get(),
Qt::DirectConnection,
Q_ARG(int, 5),
Q_ARG(QString, "worked"));
QCOMPARE(object->property("tested").toString(), QString("worked5"));
-
- delete object;
}
// Confirm that target can be changed by one of our signal handlers
@@ -143,7 +136,7 @@ void tst_qqmlconnections::targetChanged()
QFETCH(QString, prefix);
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl(prefix + "/connection-targetchange.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(c.create()) };
QVERIFY(item != nullptr);
QQmlConnections *connections = item->findChild<QQmlConnections*>("connections");
@@ -159,8 +152,6 @@ void tst_qqmlconnections::targetChanged()
QCOMPARE(connections->target(), item2);
// If we don't crash then we're OK
-
- delete item;
}
void tst_qqmlconnections::unknownSignals_data()
@@ -193,7 +184,7 @@ void tst_qqmlconnections::unknownSignals()
QQmlEngine engine;
QQmlComponent c(&engine, url);
- QObject *object = c.create();
+ std::unique_ptr<QObject> object { c.create() };
QVERIFY(object != nullptr);
// check that connection is created (they are all runtime errors)
@@ -202,8 +193,6 @@ void tst_qqmlconnections::unknownSignals()
if (file == "connection-unknownsignals-ignored.qml")
QVERIFY(connections->ignoreUnknownSignals());
-
- delete object;
}
void tst_qqmlconnections::errors_data()
@@ -260,25 +249,21 @@ void tst_qqmlconnections::rewriteErrors()
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl(prefix + "/rewriteError-unnamed.qml"));
QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal uses unnamed parameter followed by named parameter.").toLatin1());
- TestObject *obj = qobject_cast<TestObject*>(c.create());
+ std::unique_ptr<TestObject> obj { qobject_cast<TestObject*>(c.create()) };
QVERIFY(obj != nullptr);
obj->unnamedArgumentSignal(1, .5, "hello");
QCOMPARE(obj->ran(), false);
-
- delete obj;
}
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl(prefix + "/rewriteError-global.qml"));
QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal parameter \"parseInt\" hides global variable.").toLatin1());
- TestObject *obj = qobject_cast<TestObject*>(c.create());
+ std::unique_ptr<TestObject> obj { qobject_cast<TestObject*>(c.create()) };
QVERIFY(obj != nullptr);
obj->signalWithGlobalName(10);
QCOMPARE(obj->ran(), false);
-
- delete obj;
}
}
@@ -324,26 +309,24 @@ void tst_qqmlconnections::singletonTypeTarget()
QFETCH(QString, prefix);
qmlRegisterSingletonType<MyTestSingletonType>("MyTestSingletonType", 1, 0, "Api", module_api_factory);
QQmlComponent component(&engine, testFileUrl(prefix + "/singletontype-target.qml"));
- QObject *object = component.create();
+ std::unique_ptr<QObject> object { component.create() };
QVERIFY(object != nullptr);
QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 0);
QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0);
- QMetaObject::invokeMethod(object, "setModuleIntProp");
+ QMetaObject::invokeMethod(object.get(), "setModuleIntProp");
QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 1);
QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0);
- QMetaObject::invokeMethod(object, "setModuleIntProp");
+ QMetaObject::invokeMethod(object.get(), "setModuleIntProp");
QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 2);
QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0);
// the singleton Type emits otherSignal every 3 times the int property changes.
- QMetaObject::invokeMethod(object, "setModuleIntProp");
+ QMetaObject::invokeMethod(object.get(), "setModuleIntProp");
QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 3);
QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 1);
-
- delete object;
}
void tst_qqmlconnections::enableDisable_QTBUG_36350()
@@ -351,7 +334,7 @@ void tst_qqmlconnections::enableDisable_QTBUG_36350()
QFETCH(QString, prefix);
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(c.create()) };
QVERIFY(item != nullptr);
QQmlConnections *connections = item->findChild<QQmlConnections*>("connections");
@@ -370,8 +353,6 @@ void tst_qqmlconnections::enableDisable_QTBUG_36350()
emit item->setWidth(50.);
QCOMPARE(item->width(), 50.);
QCOMPARE(item->property("tested").toBool(), true); //Should have received signal to change property
-
- delete item;
}
void tst_qqmlconnections::disabledAtStart()
@@ -379,17 +360,15 @@ void tst_qqmlconnections::disabledAtStart()
QFETCH(QString, prefix);
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl(prefix + "/disabled-at-start.qml"));
- QObject * const object = c.create();
+ std::unique_ptr<QObject> object { c.create() };
QVERIFY(object != nullptr);
QCOMPARE(object->property("tested").toBool(), false);
const int index = object->metaObject()->indexOfSignal("testMe()");
const QMetaMethod method = object->metaObject()->method(index);
- method.invoke(object, Qt::DirectConnection);
+ method.invoke(object.get(), Qt::DirectConnection);
QCOMPARE(object->property("tested").toBool(), false);
-
- delete object;
}
//QTBUG-56499
@@ -398,7 +377,7 @@ void tst_qqmlconnections::clearImplicitTarget()
QFETCH(QString, prefix);
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection-implicit.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(c.create()) };
QVERIFY(item != nullptr);
@@ -415,8 +394,6 @@ void tst_qqmlconnections::clearImplicitTarget()
// target cleared: no longer fire Connections
item->setWidth(150.);
QCOMPARE(item->property("tested").toBool(), false);
-
- delete item;
}
void tst_qqmlconnections::onWithoutASignal()
@@ -485,6 +462,26 @@ void tst_qqmlconnections::invalidTarget()
QTRY_VERIFY(root->objectName().isEmpty());
}
+void tst_qqmlconnections::badSignalHandlerName()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("badSignalHandlerName.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "\"on_foo\" is not a properly capitalized signal handler name. "
+ "\"on_Foo\" would be correct.");
+
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+
+ QCOMPARE(root->property("handled").toInt(), 0);
+ QMetaObject::invokeMethod(root.data(), "_foo");
+ QCOMPARE(root->property("handled").toInt(), 3);
+}
+
+
QTEST_MAIN(tst_qqmlconnections)
#include "tst_qqmlconnections.moc"
diff --git a/tests/auto/qml/qqmlconsole/CMakeLists.txt b/tests/auto/qml/qqmlconsole/CMakeLists.txt
index a4eb33f1df..121b0863d0 100644
--- a/tests/auto/qml/qqmlconsole/CMakeLists.txt
+++ b/tests/auto/qml/qqmlconsole/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlconsole Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlconsole LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlconsole/data/assert.qml b/tests/auto/qml/qqmlconsole/data/assert.qml
index 38a103e233..04ea18a3e5 100644
--- a/tests/auto/qml/qqmlconsole/data/assert.qml
+++ b/tests/auto/qml/qqmlconsole/data/assert.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml
index 9593abbaa9..dd03c0c773 100644
--- a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml
+++ b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Pelagicore AG
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/qml/qqmlconsole/data/exception.qml b/tests/auto/qml/qqmlconsole/data/exception.qml
index 769b196c10..d3d022ca22 100644
--- a/tests/auto/qml/qqmlconsole/data/exception.qml
+++ b/tests/auto/qml/qqmlconsole/data/exception.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlconsole/data/logging.qml b/tests/auto/qml/qqmlconsole/data/logging.qml
index af1c97ee01..309534615e 100644
--- a/tests/auto/qml/qqmlconsole/data/logging.qml
+++ b/tests/auto/qml/qqmlconsole/data/logging.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlconsole/data/profiling.qml b/tests/auto/qml/qqmlconsole/data/profiling.qml
index fc655ab26c..528726b268 100644
--- a/tests/auto/qml/qqmlconsole/data/profiling.qml
+++ b/tests/auto/qml/qqmlconsole/data/profiling.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlconsole/data/tracing.qml b/tests/auto/qml/qqmlconsole/data/tracing.qml
index 750e4f5688..52be9d84de 100644
--- a/tests/auto/qml/qqmlconsole/data/tracing.qml
+++ b/tests/auto/qml/qqmlconsole/data/tracing.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
index 4bffdb0dd7..40d849969c 100644
--- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
+++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
#include <QQmlEngine>
@@ -95,7 +95,7 @@ void tst_qqmlconsole::categorized_logging()
QVERIFY(testCategory.isCriticalEnabled());
QQmlComponent component(&engine, testUrl);
- QObject *object = component.create();
+ std::unique_ptr<QObject> object { component.create() };
QVERIFY2(object != nullptr, component.errorString().toUtf8());
QVERIFY(messageHandler.messages().contains("qt.test: console.info"));
@@ -131,8 +131,6 @@ void tst_qqmlconsole::categorized_logging()
QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(42) +
"Error: A QmlLoggingCatgory was provided without a valid name";
QVERIFY(messageHandler.messages().contains(useEmptyCategory));
-
- delete object;
}
void tst_qqmlconsole::tracing()
@@ -147,9 +145,17 @@ void tst_qqmlconsole::tracing()
QTest::ignoreMessage(QtDebugMsg, qPrintable(traceText));
QQmlComponent component(&engine, testUrl);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
- delete object;
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
+
+ const QString traceText2
+ = QLatin1String("qml: tracing (%1:%2)\nexpression for onCompleted (%1:%3)")
+ .arg(testUrl.toString()).arg(12).arg(16);
+
+ QQmlTestMessageHandler messageHandler;
+ messageHandler.setIncludeCategoriesEnabled(true);
+ std::unique_ptr<QObject> object2 { component.create() };
+ QCOMPARE(messageHandler.messageString(), traceText2);
}
void tst_qqmlconsole::profiling()
@@ -161,9 +167,8 @@ void tst_qqmlconsole::profiling()
QTest::ignoreMessage(QtWarningMsg, "Ignoring console.profileEnd(): the debug service is disabled.");
QQmlComponent component(&engine, testUrl);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
- delete object;
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
}
void tst_qqmlconsole::testAssert()
@@ -186,9 +191,8 @@ void tst_qqmlconsole::testAssert()
QTest::ignoreMessage(QtCriticalMsg, qPrintable(assert2));
QQmlComponent component(&engine, testUrl);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
- delete object;
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
}
void tst_qqmlconsole::exception()
@@ -211,9 +215,8 @@ void tst_qqmlconsole::exception()
QTest::ignoreMessage(QtCriticalMsg, qPrintable(exception2));
QQmlComponent component(&engine, testUrl);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
- delete object;
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
}
QTEST_MAIN(tst_qqmlconsole)
diff --git a/tests/auto/qml/qqmlcontext/CMakeLists.txt b/tests/auto/qml/qqmlcontext/CMakeLists.txt
index c8d5ec6810..b50e3ff7fe 100644
--- a/tests/auto/qml/qqmlcontext/CMakeLists.txt
+++ b/tests/auto/qml/qqmlcontext/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlcontext Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlcontext LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlcontext/data/A.qml b/tests/auto/qml/qqmlcontext/data/A.qml
new file mode 100644
index 0000000000..1a44f005af
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/A.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+B {
+ id: b
+ property int y: 2
+}
diff --git a/tests/auto/qml/qqmlcontext/data/B.qml b/tests/auto/qml/qqmlcontext/data/B.qml
new file mode 100644
index 0000000000..7754728304
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/B.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+C {
+ id: z
+ property int z: 3
+}
diff --git a/tests/auto/qml/qqmlcontext/data/C.qml b/tests/auto/qml/qqmlcontext/data/C.qml
new file mode 100644
index 0000000000..6afd23aa6c
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/C.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ id: outer
+ objectName: "the" + "C"
+}
diff --git a/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml b/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml
new file mode 100644
index 0000000000..7b1f46d7d1
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ property A a: A {}
+}
diff --git a/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml b/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml
new file mode 100644
index 0000000000..a478a587df
--- /dev/null
+++ b/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml
@@ -0,0 +1,5 @@
+import QtQml
+QtObject {
+ property Component c: MyItem {}
+ property QtObject o: c.createObject()
+}
diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
index 8398f8b9d7..8e726857cc 100644
--- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
+++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
@@ -49,6 +49,10 @@ private slots:
void outerContextObject();
void contextObjectHierarchy();
void destroyContextProperty();
+ void destroyContextObject();
+
+ void numericContextProperty();
+ void gcDeletesContextObject();
private:
QQmlEngine engine;
@@ -92,13 +96,13 @@ void tst_qqmlcontext::resolvedUrl()
// Relative to a deleted parent
{
- QQmlContext *ctxt = new QQmlContext(&engine);
+ std::unique_ptr<QQmlContext> ctxt = std::make_unique<QQmlContext>(&engine);
ctxt->setBaseUrl(QUrl("http://www.qt-project.org/"));
- QQmlContext ctxt2(ctxt);
+ QQmlContext ctxt2(ctxt.get());
QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.qt-project.org/main2.qml"));
- delete ctxt; ctxt = nullptr;
+ ctxt.reset();
QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl());
}
@@ -114,58 +118,58 @@ void tst_qqmlcontext::resolvedUrl()
void tst_qqmlcontext::engineMethod()
{
- QQmlEngine *engine = new QQmlEngine;
+ std::unique_ptr<QQmlEngine> engine = std::make_unique<QQmlEngine>();
- QQmlContext ctxt(engine);
+ QQmlContext ctxt(engine.get());
QQmlContext ctxt2(&ctxt);
QQmlContext ctxt3(&ctxt2);
QQmlContext ctxt4(&ctxt2);
- QCOMPARE(ctxt.engine(), engine);
- QCOMPARE(ctxt2.engine(), engine);
- QCOMPARE(ctxt3.engine(), engine);
- QCOMPARE(ctxt4.engine(), engine);
+ QCOMPARE(ctxt.engine(), engine.get());
+ QCOMPARE(ctxt2.engine(), engine.get());
+ QCOMPARE(ctxt3.engine(), engine.get());
+ QCOMPARE(ctxt4.engine(), engine.get());
- delete engine; engine = nullptr;
+ engine.reset();
- QCOMPARE(ctxt.engine(), engine);
- QCOMPARE(ctxt2.engine(), engine);
- QCOMPARE(ctxt3.engine(), engine);
- QCOMPARE(ctxt4.engine(), engine);
+ QCOMPARE(ctxt.engine(), engine.get());
+ QCOMPARE(ctxt2.engine(), engine.get());
+ QCOMPARE(ctxt3.engine(), engine.get());
+ QCOMPARE(ctxt4.engine(), engine.get());
}
void tst_qqmlcontext::parentContext()
{
- QQmlEngine *engine = new QQmlEngine;
+ std::unique_ptr<QQmlEngine> engine = std::make_unique<QQmlEngine>();
QCOMPARE(engine->rootContext()->parentContext(), (QQmlContext *)nullptr);
- QQmlContext *ctxt = new QQmlContext(engine);
- QQmlContext *ctxt2 = new QQmlContext(ctxt);
- QQmlContext *ctxt3 = new QQmlContext(ctxt2);
- QQmlContext *ctxt4 = new QQmlContext(ctxt2);
- QQmlContext *ctxt5 = new QQmlContext(ctxt);
- QQmlContext *ctxt6 = new QQmlContext(engine);
- QQmlContext *ctxt7 = new QQmlContext(engine->rootContext());
+ std::unique_ptr<QQmlContext> ctxt = std::make_unique<QQmlContext>(engine.get());
+ std::unique_ptr<QQmlContext> ctxt2 = std::make_unique<QQmlContext>(ctxt.get());
+ std::unique_ptr<QQmlContext> ctxt3 = std::make_unique<QQmlContext>(ctxt2.get());
+ std::unique_ptr<QQmlContext> ctxt4 = std::make_unique<QQmlContext>(ctxt2.get());
+ std::unique_ptr<QQmlContext> ctxt5 = std::make_unique<QQmlContext>(ctxt.get());
+ std::unique_ptr<QQmlContext> ctxt6 = std::make_unique<QQmlContext>(engine.get());
+ std::unique_ptr<QQmlContext> ctxt7 = std::make_unique<QQmlContext>(engine->rootContext());
QCOMPARE(ctxt->parentContext(), engine->rootContext());
- QCOMPARE(ctxt2->parentContext(), ctxt);
- QCOMPARE(ctxt3->parentContext(), ctxt2);
- QCOMPARE(ctxt4->parentContext(), ctxt2);
- QCOMPARE(ctxt5->parentContext(), ctxt);
+ QCOMPARE(ctxt2->parentContext(), ctxt.get());
+ QCOMPARE(ctxt3->parentContext(), ctxt2.get());
+ QCOMPARE(ctxt4->parentContext(), ctxt2.get());
+ QCOMPARE(ctxt5->parentContext(), ctxt.get());
QCOMPARE(ctxt6->parentContext(), engine->rootContext());
QCOMPARE(ctxt7->parentContext(), engine->rootContext());
- delete ctxt2; ctxt2 = nullptr;
+ ctxt2.reset();
QCOMPARE(ctxt->parentContext(), engine->rootContext());
QCOMPARE(ctxt3->parentContext(), (QQmlContext *)nullptr);
QCOMPARE(ctxt4->parentContext(), (QQmlContext *)nullptr);
- QCOMPARE(ctxt5->parentContext(), ctxt);
+ QCOMPARE(ctxt5->parentContext(), ctxt.get());
QCOMPARE(ctxt6->parentContext(), engine->rootContext());
QCOMPARE(ctxt7->parentContext(), engine->rootContext());
- delete engine; engine = nullptr;
+ engine.reset();
QCOMPARE(ctxt->parentContext(), (QQmlContext *)nullptr);
QCOMPARE(ctxt3->parentContext(), (QQmlContext *)nullptr);
@@ -173,13 +177,6 @@ void tst_qqmlcontext::parentContext()
QCOMPARE(ctxt5->parentContext(), (QQmlContext *)nullptr);
QCOMPARE(ctxt6->parentContext(), (QQmlContext *)nullptr);
QCOMPARE(ctxt7->parentContext(), (QQmlContext *)nullptr);
-
- delete ctxt7;
- delete ctxt6;
- delete ctxt5;
- delete ctxt4;
- delete ctxt3;
- delete ctxt;
}
class TestObject : public QObject
@@ -229,11 +226,9 @@ private:
QQmlComponent component(&engine); \
component.setData("import QtQuick 2.0; QtObject { property variant test: " #name " }", QUrl()); \
\
- QObject *obj = component.create(ctxt); \
+ std::unique_ptr<QObject> obj { component.create(ctxt) }; \
\
QCOMPARE(obj->property("test"), value); \
-\
- delete obj; \
}
void tst_qqmlcontext::setContextProperty()
@@ -279,39 +274,33 @@ void tst_qqmlcontext::setContextProperty()
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl());
- QObject *obj = component.create(&ctxt2);
+ std::unique_ptr<QObject> obj { component.create(&ctxt2) };
QCOMPARE(obj->property("test"), QVariant(13));
ctxt.setContextProperty("a", QVariant(19));
QCOMPARE(obj->property("test"), QVariant(19));
-
- delete obj;
}
{
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { property variant test: b }", QUrl());
- QObject *obj = component.create(&ctxt2);
+ std::unique_ptr<QObject> obj { component.create(&ctxt2) };
QCOMPARE(obj->property("test"), QVariant(8));
ctxt.setContextProperty("b", QVariant(5));
QCOMPARE(obj->property("test"), QVariant(8));
ctxt2.setContextProperty("b", QVariant(1912));
QCOMPARE(obj->property("test"), QVariant(1912));
-
- delete obj;
}
{
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { property variant test: e.a }", QUrl());
- QObject *obj = component.create(&ctxt2);
+ std::unique_ptr<QObject> obj { component.create(&ctxt2) };
QCOMPARE(obj->property("test"), QVariant(12));
obj1.setA(13);
QCOMPARE(obj->property("test"), QVariant(13));
-
- delete obj;
}
// New context properties
@@ -319,13 +308,11 @@ void tst_qqmlcontext::setContextProperty()
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl());
- QObject *obj = component.create(&ctxt2);
+ std::unique_ptr<QObject> obj { component.create(&ctxt2) };
QCOMPARE(obj->property("test"), QVariant(19));
ctxt2.setContextProperty("a", QVariant(1945));
QCOMPARE(obj->property("test"), QVariant(1945));
-
- delete obj;
}
// Setting an object-variant context property
@@ -337,15 +324,13 @@ void tst_qqmlcontext::setContextProperty()
ctxt.setContextProperty("ctxtProp", QVariant());
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1: TypeError: Cannot read property 'a' of undefined");
- QObject *obj = component.create(&ctxt);
+ std::unique_ptr<QObject> obj { component.create(&ctxt) };
QVariant v = obj->property("obj");
ctxt.setContextProperty("ctxtProp", v);
QCOMPARE(obj->property("test"), QVariant(10));
-
- delete obj;
}
}
@@ -407,13 +392,11 @@ void tst_qqmlcontext::setContextObject()
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl());
- QObject *obj = component.create(&ctxt);
+ std::unique_ptr<QObject> obj { component.create(&ctxt) };
QCOMPARE(obj->property("test"), QVariant(12));
to.setA(14);
QCOMPARE(obj->property("test"), QVariant(14));
-
- delete obj;
}
// Change of context object
@@ -431,19 +414,19 @@ void tst_qqmlcontext::setContextObject()
void tst_qqmlcontext::destruction()
{
- QQmlContext *ctxt = new QQmlContext(&engine);
+ std::unique_ptr<QQmlContext> ctxt = std::make_unique<QQmlContext>(&engine);
QObject obj;
- QQmlEngine::setContextForObject(&obj, ctxt);
- QQmlExpression expr(ctxt, nullptr, "a");
+ QQmlEngine::setContextForObject(&obj, ctxt.get());
+ QQmlExpression expr(ctxt.get(), nullptr, "a");
- QCOMPARE(ctxt, QQmlEngine::contextForObject(&obj));
- QCOMPARE(ctxt, expr.context());
+ QCOMPARE(ctxt.get(), QQmlEngine::contextForObject(&obj));
+ QCOMPARE(ctxt.get(), expr.context());
- delete ctxt; ctxt = nullptr;
+ ctxt.reset();
- QCOMPARE(ctxt, QQmlEngine::contextForObject(&obj));
- QCOMPARE(ctxt, expr.context());
+ QCOMPARE(ctxt.get(), QQmlEngine::contextForObject(&obj));
+ QCOMPARE(ctxt.get(), expr.context());
}
void tst_qqmlcontext::idAsContextProperty()
@@ -451,18 +434,16 @@ void tst_qqmlcontext::idAsContextProperty()
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { property variant a; a: QtObject { id: myObject } }", QUrl());
- QObject *obj = component.create();
- QVERIFY(obj);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj.get());
QVariant a = obj->property("a");
QCOMPARE(a.userType(), int(QMetaType::QObjectStar));
- QVariant ctxt = qmlContext(obj)->contextProperty("myObject");
+ QVariant ctxt = qmlContext(obj.get())->contextProperty("myObject");
QCOMPARE(ctxt.userType(), int(QMetaType::QObjectStar));
QCOMPARE(a, ctxt);
-
- delete obj;
}
// Internal contexts should be read-only
@@ -471,28 +452,26 @@ void tst_qqmlcontext::readOnlyContexts()
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { id: me }", QUrl());
- QObject *obj = component.create();
- QVERIFY(obj);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj.get());
- QQmlContext *context = qmlContext(obj);
+ QQmlContext *context = qmlContext(obj.get());
QVERIFY(context);
- QCOMPARE(qvariant_cast<QObject*>(context->contextProperty("me")), obj);
- QCOMPARE(context->contextObject(), obj);
+ QCOMPARE(qvariant_cast<QObject*>(context->contextProperty("me")), obj.get());
+ QCOMPARE(context->contextObject(), obj.get());
QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set property on internal context.");
context->setContextProperty("hello", 12);
QCOMPARE(context->contextProperty("hello"), QVariant());
QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set property on internal context.");
- context->setContextProperty("hello", obj);
+ context->setContextProperty("hello", obj.get());
QCOMPARE(context->contextProperty("hello"), QVariant());
QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set context object for internal context.");
context->setContextObject(nullptr);
- QCOMPARE(context->contextObject(), obj);
-
- delete obj;
+ QCOMPARE(context->contextObject(), obj.get());
}
void tst_qqmlcontext::objectsAndNames()
@@ -624,14 +603,12 @@ void tst_qqmlcontext::refreshExpressionsCrash()
component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
QVERIFY(component.isReady());
- QObject *o1 = component.create(&ctxt);
+ std::unique_ptr<QObject> o1 { component.create(&ctxt) };
QObject *o2 = component.create(&ctxt);
command.object = o2;
QQmlContextData::get(&ctxt)->refreshExpressions();
-
- delete o1;
}
{
QQmlEngine engine;
@@ -647,13 +624,11 @@ void tst_qqmlcontext::refreshExpressionsCrash()
QVERIFY(component.isReady());
QObject *o1 = component.create(&ctxt);
- QObject *o2 = component.create(&ctxt);
+ std::unique_ptr<QObject> o2 { component.create(&ctxt) };
command.object = o1;
QQmlContextData::get(&ctxt)->refreshExpressions();
-
- delete o2;
}
}
@@ -685,19 +660,15 @@ void tst_qqmlcontext::refreshExpressions()
QQmlContext context(engine.rootContext());
QQmlContext context2(&context);
- QObject *o1 = component.create(&context);
- QObject *o2 = component.create(&context2);
- QObject *o3 = component2.create(&context);
+ std::unique_ptr<QObject> o1 { component.create(&context) };
+ std::unique_ptr<QObject> o2 { component.create(&context2) };
+ std::unique_ptr<QObject> o3 { component2.create(&context) };
QCOMPARE(command.count, 5);
QQmlContextData::get(&context)->refreshExpressions();
QCOMPARE(command.count, 10);
-
- delete o3;
- delete o2;
- delete o1;
}
// Test that updating the root context, only causes expressions in contexts with an
@@ -717,10 +688,10 @@ void tst_qqmlcontext::refreshExpressionsRootContext()
QString warning = component2.url().toString() + QLatin1String(":4: ReferenceError: unresolvedName is not defined");
- QObject *o1 = component.create(&context);
+ std::unique_ptr<QObject> o1 { component.create(&context) };
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
- QObject *o2 = component2.create(&context2);
+ std::unique_ptr<QObject> o2 { component2.create(&context2) };
QCOMPARE(command.count, 3);
@@ -728,9 +699,6 @@ void tst_qqmlcontext::refreshExpressionsRootContext()
QQmlContextData::get(engine.rootContext())->refreshExpressions();
QCOMPARE(command.count, 4);
-
- delete o2;
- delete o1;
}
void tst_qqmlcontext::skipExpressionRefresh_qtbug_53431()
@@ -752,10 +720,9 @@ void tst_qqmlcontext::qtbug_22535()
QQmlComponent component(&engine, testFileUrl("qtbug_22535.qml"));
QQmlContext context(engine.rootContext());
- QObject *o = component.create(&context);
+ std::unique_ptr<QObject> o { component.create(&context) };
// Don't crash!
- delete o;
}
void tst_qqmlcontext::evalAfterInvalidate()
@@ -858,7 +825,7 @@ void tst_qqmlcontext::contextLeak()
scriptContext = scriptContextWrapper->as<QV4::QQmlContextWrapper>()->getContext();
}
- engine.collectGarbage();
+ gc(engine);
// Each time a JS file (non-pragma-shared) is imported, we create a QQmlContext(Data) for it.
// Make sure that context does not leak.
@@ -979,6 +946,66 @@ void tst_qqmlcontext::destroyContextProperty()
// TODO: Or are we?
}
+void tst_qqmlcontext::destroyContextObject()
+{
+ QQmlEngine engine;
+ QList<QQmlRefPointer<QQmlContextData>> contexts;
+ QQmlComponent component(&engine, testFileUrl("destroyContextObject.qml"));
+ QScopedPointer<QObject> root(component.create());
+
+ QPointer<QObject> a = root->property("a").value<QObject *>();
+ QVERIFY(a);
+
+ for (QQmlRefPointer<QQmlContextData> context = QQmlData::get(a)->ownContext;
+ context; context = context->parent()) {
+ contexts.append(context);
+ }
+
+ QObject *deleted = a.data();
+ root.reset();
+
+ QVERIFY(a.isNull());
+
+ for (const auto &context : contexts)
+ QVERIFY(context->contextObject() != deleted);
+}
+
+void tst_qqmlcontext::numericContextProperty()
+{
+ QQmlEngine engine;
+ auto context = engine.rootContext();
+ QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Using numbers as context properties will be disallowed in a future Qt version.");
+ context->setContextProperty(QLatin1String("11"), 42);
+ QCOMPARE(context->contextProperty(QLatin1String("11")).toInt(), 42);
+}
+
+void tst_qqmlcontext::gcDeletesContextObject()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("gcDeletesContextObject.qml"));
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QVERIFY(!o.isNull());
+
+ QPointer<QObject> contextObject = o->property("o").value<QObject *>();
+ QVERIFY(contextObject != nullptr);
+
+ QQmlData *data = QQmlData::get(contextObject);
+ QVERIFY(data);
+ QQmlRefPointer<QQmlContextData> context = data->ownContext;
+ QVERIFY(context);
+ QCOMPARE(context->contextObject(), contextObject);
+
+ o->setProperty("o", QVariant::fromValue<QObject *>(nullptr));
+ QCOMPARE(o->property("o").value<QObject *>(), nullptr);
+ engine.collectGarbage();
+
+ QTRY_VERIFY(contextObject.isNull());
+ QCOMPARE(context->contextObject(), nullptr);
+}
+
QTEST_MAIN(tst_qqmlcontext)
#include "tst_qqmlcontext.moc"
diff --git a/tests/auto/qml/qqmlcpputils/CMakeLists.txt b/tests/auto/qml/qqmlcpputils/CMakeLists.txt
index 3eb58640c7..ee809e09b5 100644
--- a/tests/auto/qml/qqmlcpputils/CMakeLists.txt
+++ b/tests/auto/qml/qqmlcpputils/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlcpputils Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlcpputils LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmlcpputils
SOURCES
tst_qqmlcpputils.cpp
diff --git a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp
index 11d79707d0..d858fd2ff0 100644
--- a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp
+++ b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <qsignalspy.h>
@@ -35,13 +35,11 @@ public slots:
void tst_qqmlcpputils::fastConnect()
{
{
- MyObject *obj = new MyObject;
- qmlobject_connect(obj, MyObject, SIGNAL(signal1()), obj, MyObject, SLOT(slot1()));
+ std::unique_ptr<MyObject> obj = std::make_unique<MyObject>();
+ qmlobject_connect(obj.get(), MyObject, SIGNAL(signal1()), obj.get(), MyObject, SLOT(slot1()));
obj->signal1();
QCOMPARE(obj->slotCount, 1);
-
- delete obj;
}
{
@@ -53,27 +51,24 @@ void tst_qqmlcpputils::fastConnect()
}
{
- MyObject *obj = new MyObject;
- QSignalSpy spy(obj, SIGNAL(signal2()));
- qmlobject_connect(obj, MyObject, SIGNAL(signal1()), obj, MyObject, SIGNAL(signal2()));
+ std::unique_ptr<MyObject> obj = std::make_unique<MyObject>();
+ QSignalSpy spy(obj.get(), SIGNAL(signal2()));
+ qmlobject_connect(obj.get(), MyObject, SIGNAL(signal1()), obj.get(), MyObject, SIGNAL(signal2()));
obj->signal1();
QCOMPARE(spy.size(), 1);
-
- delete obj;
}
}
void tst_qqmlcpputils::fastCast()
{
{
- QObject *myObj = new MyObject;
- MyObject *obj = qmlobject_cast<MyObject*>(myObj);
+ std::unique_ptr<QObject> myObj = std::make_unique<MyObject>();
+ MyObject *obj = qmlobject_cast<MyObject*>(myObj.get());
QVERIFY(obj);
QCOMPARE(obj->metaObject(), myObj->metaObject());
obj->slot1();
QCOMPARE(obj->slotCount, 1);
- delete myObj;
}
{
diff --git a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
index 705ee6f357..8d8a90e0a7 100644
--- a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
+++ b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmldelegatemodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldelegatemodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml b/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml
index a61e94ea6d..5ef502601c 100644
--- a/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml
+++ b/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml.Models 2.15
import QtQuick 2.15
diff --git a/tests/auto/qml/qqmldelegatemodel/data/clearCacheDuringInsertion.qml b/tests/auto/qml/qqmldelegatemodel/data/clearCacheDuringInsertion.qml
new file mode 100644
index 0000000000..37e508302e
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/clearCacheDuringInsertion.qml
@@ -0,0 +1,138 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQml.Models
+
+Window {
+ id: root
+ width: 640
+ height: 480
+ visible: true
+ color: "#111111"
+
+ Column {
+ spacing: 1
+ Repeater {
+ model: 1000
+
+ Rectangle {
+ width: 100
+ height: 100
+ color: "grey"
+ DelegateModel {
+ id: delegateModel
+ delegate: Rectangle {
+ width: 100
+ height: 20
+ color: "black"
+ Text {
+ anchors.centerIn: parent
+ text: "Name: " + model.name
+ color: "white"
+ }
+ }
+
+ property int length: 0
+ property var filterAcceptsItem: function(item) { return true; }
+
+ model: ListModel {
+ id: myModel
+
+ ListElement {
+ name: "tomato"
+ classifications: [
+ ListElement { classification: "fruit" },
+ ListElement { classification: "veg" }
+ ]
+ nutritionFacts: [
+ ListElement {
+ calories: "45"
+ }
+ ]
+ }
+
+ ListElement {
+ name: "apple"
+ classifications: [
+ ListElement { classification: "fruit" }
+ ]
+ nutritionFacts: [
+ ListElement {
+ calories: "87"
+ }
+ ]
+ }
+
+ ListElement {
+ name: "broccoli"
+ classifications: [
+ ListElement { classification: "veg" }
+ ]
+ nutritionFacts: [
+ ListElement {
+ calories: "12"
+ }
+ ]
+ }
+
+ ListElement {
+ name: "squash"
+ classifications: [
+ ListElement { classification: "veg" }
+ ]
+ nutritionFacts: [
+ ListElement {
+ calories: "112"
+ }
+ ]
+ }
+ }
+
+ groups: [
+ DelegateModelGroup { id: visibleItems; name: "visible" },
+ DelegateModelGroup { name: "veg" },
+ DelegateModelGroup { name: "fruit"; includeByDefault: true }
+ ]
+
+ function update() {
+
+ // Step 1: Filter items
+ var visible = [];
+ for (var i = 0; i < items.count; ++i) {
+ var item = items.get(i);
+ if (filterAcceptsItem(item.model)) {
+ visible.push(item);
+ }
+ }
+
+ // Step 2: Add all items to the visible group:
+ for (i = 0; i < visible.length; ++i) {
+ items.insert(visible[i], delegateModel.filterOnGroup)
+ }
+ delegateModel.length = visible.length
+ }
+
+ items.onChanged: update()
+ onFilterAcceptsItemChanged: update()
+
+ filterOnGroup: "visible"
+ Component.onCompleted: {
+ for(var i = 0; i < myModel.count; i++) {
+ var temp = 0;
+ var entry = myModel.get(i);
+
+ for (var j = 0; j < entry.classifications.count; j++) {
+ temp = entry.classifications.get(j)
+ items.insert(entry, temp.classification)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml b/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml
new file mode 100644
index 0000000000..b01b14f7b8
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml
@@ -0,0 +1,50 @@
+import QtQuick
+import QtQml.Models
+
+Item {
+ DelegateModel {
+ id: delegateModel
+ model: ListModel {
+ id: sourceModel
+
+ ListElement { title: "foo" }
+ ListElement { title: "bar" }
+
+ function clear() {
+ if (count > 0)
+ remove(0, count);
+ }
+ }
+
+ groups: [
+ DelegateModelGroup { name: "selectedItems" }
+ ]
+
+ delegate: Text {
+ height: DelegateModel.inSelectedItems ? implicitHeight * 2 : implicitHeight
+ Component.onCompleted: {
+ if (index === 0)
+ DelegateModel.inSelectedItems = true;
+ }
+ }
+
+ Component.onCompleted: {
+ items.create(0)
+ items.create(1)
+ }
+ }
+
+ ListView {
+ anchors.fill: parent
+ model: delegateModel
+ }
+
+ Timer {
+ running: true
+ interval: 10
+ onTriggered: sourceModel.clear()
+ }
+
+ property int count: delegateModel.items.count
+}
+
diff --git a/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml b/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml
index 42bf882fda..bcc7a2786a 100644
--- a/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml
+++ b/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml.Models 2.15
import QtQuick 2.15
diff --git a/tests/auto/qml/qqmldelegatemodel/data/listModel.qml b/tests/auto/qml/qqmldelegatemodel/data/listModel.qml
index f74abb2e9a..b4ea7a283f 100644
--- a/tests/auto/qml/qqmldelegatemodel/data/listModel.qml
+++ b/tests/auto/qml/qqmldelegatemodel/data/listModel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml.Models 2.15
import QtQuick 2.15
diff --git a/tests/auto/qml/qqmldelegatemodel/data/modifyObjectUnderConstruction.qml b/tests/auto/qml/qqmldelegatemodel/data/modifyObjectUnderConstruction.qml
new file mode 100644
index 0000000000..f21577e395
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/modifyObjectUnderConstruction.qml
@@ -0,0 +1,18 @@
+import QtQuick
+
+Window {
+ id: window
+ width: 640
+ height: 480
+ visible: true
+ property alias testModel: repeater.model
+
+ Repeater {
+ id: repeater
+ model: 1
+ delegate: Item {
+ Component.onCompleted: repeater.model = 0
+ }
+ }
+}
+
diff --git a/tests/auto/qml/qqmldelegatemodel/data/overriddenModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/overriddenModelData.qml
new file mode 100644
index 0000000000..e392b2e5c9
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/overriddenModelData.qml
@@ -0,0 +1,55 @@
+import QtQml
+
+DelegateModel {
+ id: root
+
+ property ListModel listModel: ListModel {
+ ListElement {
+ modelData: "a"
+ row: "b"
+ column: "c"
+ model: "d"
+ hasModelChildren: "e"
+ index: "f"
+ }
+ }
+
+ property var array: [{
+ modelData: "a",
+ row: "b",
+ column: "c",
+ model: "d",
+ hasModelChildren: "e",
+ index: "f"
+ }]
+
+ property QtObject object: QtObject {
+ property string modelData: "a"
+ property string row: "b"
+ property string column: "c"
+ property string model: "d"
+ property string hasModelChildren: "e"
+ property string index: "f"
+ }
+
+ property int n: -1
+
+ model: {
+ switch (n) {
+ case 0: return listModel
+ case 1: return array
+ case 2: return object
+ }
+ return undefined;
+ }
+
+ delegate: QtObject {
+ required property string modelData
+ required property string row
+ required property string column
+ required property string model
+ required property string hasModelChildren
+ required property string index
+ objectName: [modelData, row, column, model, hasModelChildren, index].join(" ")
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/persistedItemsCache.qml b/tests/auto/qml/qqmldelegatemodel/data/persistedItemsCache.qml
new file mode 100644
index 0000000000..5ae2038e1f
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/persistedItemsCache.qml
@@ -0,0 +1,62 @@
+import QtQuick
+import QtQuick.Window
+import QtQml.Models
+
+Window {
+ id: win
+ visible: true
+ width: 640
+ height: 480
+ property int destroyCount : 0;
+ property int createCount : 0;
+ property alias testListModel: mdl
+
+ DelegateModel {
+ id: visualModel
+ model: ListModel {
+ id: mdl
+ ListElement {
+ name: "a"
+ hidden: false
+ }
+ ListElement {
+ name: "b"
+ hidden: true
+ }
+ ListElement {
+ name: "c"
+ hidden: false
+ }
+ }
+
+ filterOnGroup: "selected"
+
+ groups: [
+ DelegateModelGroup {
+ name: "selected"
+ includeByDefault: true
+ }
+ ]
+
+ delegate: Text {
+ visible: DelegateModel.inSelected
+ property var idx
+ Component.onCompleted: {
+ ++createCount
+ idx = index
+ DelegateModel.inPersistedItems = true
+ DelegateModel.inSelected = !model.hidden
+ }
+ Component.onDestruction: ++destroyCount
+ text: model.name
+ }
+ }
+
+ ListView {
+ id: listView
+ model: visualModel
+ anchors.fill: parent
+ focus: true
+ }
+
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/requiredModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/requiredModelData.qml
new file mode 100644
index 0000000000..467d60dff8
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/requiredModelData.qml
@@ -0,0 +1,50 @@
+import QtQml
+
+DelegateModel {
+ id: root
+
+ property ListModel singularModel: ListModel {
+ ListElement {
+ a: "a"
+ }
+ ListElement {
+ a: "a"
+ }
+ }
+
+ property ListModel listModel: ListModel {
+ ListElement {
+ a: "a"
+ b: "b"
+ }
+ ListElement {
+ a: "a"
+ b: "b"
+ }
+ }
+
+ property var array: [
+ {a: "a", b: "b"}, {a: "b", b: "b"}
+ ]
+
+ property QtObject object: QtObject {
+ property string a: "a"
+ property string b: "b"
+ }
+
+ property int n: -1
+
+ model: {
+ switch (n) {
+ case 0: return singularModel
+ case 1: return listModel
+ case 2: return array
+ case 3: return object
+ }
+ return undefined;
+ }
+
+ delegate: QtObject {
+ required property string a
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/resetModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/resetModelData.qml
new file mode 100644
index 0000000000..1fe6168c79
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/resetModelData.qml
@@ -0,0 +1,16 @@
+import QtQuick
+
+ListView {
+ id: root
+ anchors.fill: parent
+ property bool success: (currentItem?.mydata ?? 0) === 42
+ height: 300
+ width: 200
+
+ delegate: Rectangle {
+ required property var model
+ implicitWidth: 100
+ implicitHeight: 50
+ property var mydata: model?.foo ?? model.bar
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml
new file mode 100644
index 0000000000..08f1c7d68e
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml
@@ -0,0 +1,64 @@
+import QtQml
+
+DelegateModel {
+ id: root
+
+ // useful object as model, int as modelData
+ property ListModel singularModel: ListModel {
+ ListElement {
+ x: 11
+ }
+ ListElement {
+ x: 12
+ }
+ }
+
+ // same, useful, object as model and modelData
+ property ListModel listModel: ListModel {
+ ListElement {
+ x: 13
+ y: 14
+ }
+ ListElement {
+ x: 15
+ y: 16
+ }
+ }
+
+ // useful but different objects as modelData and model
+ // This is how the array accessor works. We can live with it.
+ property var array: [
+ {x: 17, y: 18}, {x: 19, y: 20}
+ ]
+
+ // useful but different objects as modelData and model
+ // This is how the object accessor works. We can live with it.
+ property QtObject object: QtObject {
+ property int x: 21
+ property int y: 22
+ }
+
+ property int n: -1
+
+ model: {
+ switch (n) {
+ case 0: return singularModel
+ case 1: return listModel
+ case 2: return array
+ case 3: return object
+ }
+ return undefined;
+ }
+
+ delegate: QtObject {
+ required property point modelData
+ required property QtObject model
+
+ property real modelX: model.x
+ property real modelDataX: modelData.x
+ property point modelSelf: model
+ property point modelDataSelf: modelData
+ property point modelModelData: model.modelData
+ property point modelAnonymous: model[""]
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/universalModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/universalModelData.qml
new file mode 100644
index 0000000000..f24009f873
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/universalModelData.qml
@@ -0,0 +1,72 @@
+import QtQml
+
+DelegateModel {
+ id: root
+
+ // useful object as model, string as modelData
+ property ListModel singularModel: ListModel {
+ ListElement {
+ a: "a"
+ }
+ ListElement {
+ a: "b"
+ }
+ }
+
+ // same, useful, object as model and modelData
+ property ListModel listModel: ListModel {
+ ListElement {
+ a: "a"
+ b: "a"
+ }
+ ListElement {
+ a: "b"
+ b: "b"
+ }
+ }
+
+ // useful but different objects as modelData and model
+ // This is how the array accessor works. We can live with it.
+ property var array: [
+ {a: "a", b: "a"}, {a: "b", b: "a"}
+ ]
+
+ // string as modelData
+ // object with anonymous string property as model.
+ property var stringList: ["a", "b"]
+
+ // useful but different objects as modelData and model
+ // This is how the object accessor works. We can live with it.
+ property QtObject object: QtObject {
+ property string a: "a"
+ property string b: "a"
+ }
+
+ // number as modelData
+ // object with anonymous number property as model
+ property int n: -1
+
+ model: {
+ switch (n) {
+ case 0: return singularModel
+ case 1: return listModel
+ case 2: return array
+ case 3: return stringList
+ case 4: return object
+ case 5: return n
+ }
+ return undefined;
+ }
+
+ delegate: QtObject {
+ required property var modelData
+ required property var model
+
+ property var modelA: model.a
+ property var modelDataA: modelData.a
+ property var modelSelf: model
+ property var modelDataSelf: modelData
+ property var modelModelData: model.modelData
+ property var modelAnonymous: model[""]
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/viewUpdatedOnDelegateChoiceAffectingRoleChange.qml b/tests/auto/qml/qqmldelegatemodel/data/viewUpdatedOnDelegateChoiceAffectingRoleChange.qml
new file mode 100644
index 0000000000..b2367b3a6e
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/viewUpdatedOnDelegateChoiceAffectingRoleChange.qml
@@ -0,0 +1,93 @@
+import QtQuick
+import Qt.labs.qmlmodels
+import QtQml.Models
+
+Item {
+ id: root
+ property bool triggered: false
+ onTriggeredChanged: {
+ rootLM.setProperty(1, "currentRole", "first");
+ }
+ width: 800
+ height: 680
+
+ function verify(): bool {
+ rootLV.currentIndex = 1; // needed for itemAtIndex to work
+ if (root.triggered)
+ return rootLV.itemAtIndex(0).isFirst && rootLV.itemAtIndex(1).isFirst;
+ else
+ return rootLV.itemAtIndex(0).isFirst && !rootLV.itemAtIndex(1).isFirst;
+ }
+
+ ListModel {
+ id: rootLM
+ ListElement {
+ currentRole: "first"
+ firstText: "TEXT_FIRST_1"
+ secondText: "TEXT_SECOND_1"
+ }
+ ListElement {
+ currentRole: "second"
+ firstText: "TEXT_FIRST_2"
+ secondText: "TEXT_SECOND_2"
+ }
+ }
+
+ DelegateModel {
+ id: delModel
+ model: rootLM
+ delegate: DelegateChooser {
+ id: delegateChooser
+ role: "currentRole"
+ DelegateChoice {
+ roleValue: "first"
+ Rectangle {
+ property bool isFirst: true
+ height: 30
+ width: rootLV.width
+ color: "yellow"
+ Text {
+ anchors.centerIn: parent
+ text: firstText + " " + currentRole
+ }
+ }
+ }
+ DelegateChoice {
+ roleValue: "second"
+ Rectangle {
+ property bool isFirst: false
+ height: 30
+ width: rootLV.width
+ color: "red"
+ Text {
+ anchors.centerIn: parent
+ text: secondText + " " + currentRole
+ }
+ }
+ }
+ }
+ }
+
+ TapHandler {
+ // for manual testing
+ onTapped: root.triggered = true
+ }
+
+ Rectangle {
+ width: 200
+ height: 300
+ anchors.centerIn: parent
+ border.color: "black"
+ border.width: 1
+ color: "blue"
+
+ ListView {
+ id: rootLV
+ objectName: "listview"
+ anchors.margins: 30
+ anchors.fill: parent
+ cacheBuffer: 0
+ model: delModel
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
index ca66ddb618..2cacda5513 100644
--- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
+++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
@@ -1,16 +1,21 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
+#include <QtCore/qjsonobject.h>
#include <QtCore/QConcatenateTablesProxyModel>
#include <QtGui/QStandardItemModel>
#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlapplicationengine.h>
#include <QtQmlModels/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtTest/QSignalSpy>
+#include <forward_list>
+
class tst_QQmlDelegateModel : public QQmlDataTest
{
Q_OBJECT
@@ -19,6 +24,7 @@ public:
tst_QQmlDelegateModel();
private slots:
+ void resettingRolesRespected();
void valueWithoutCallingObjectFirst_data();
void valueWithoutCallingObjectFirst();
void qtbug_86017();
@@ -26,6 +32,16 @@ private slots:
void contextAccessedByHandler();
void redrawUponColumnChange();
void nestedDelegates();
+ void universalModelData();
+ void typedModelData();
+ void requiredModelData();
+ void overriddenModelData();
+ void deleteRace();
+ void persistedItemsStayInCache();
+ void unknownContainersAsModel();
+ void doNotUnrefObjectUnderConstruction();
+ void clearCacheDuringInsertion();
+ void viewUpdatedOnDelegateChoiceAffectingRoleChange();
};
class AbstractItemModel : public QAbstractItemModel
@@ -85,6 +101,61 @@ tst_QQmlDelegateModel::tst_QQmlDelegateModel()
qmlRegisterType<AbstractItemModel>("Test", 1, 0, "AbstractItemModel");
}
+class TableModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ int rowCount(const QModelIndex & = QModelIndex()) const override
+ {
+ return 1;
+ }
+
+ int columnCount(const QModelIndex & = QModelIndex()) const override
+ {
+ return 1;
+ }
+
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ switch (role) {
+ case 0:
+ return QString("foo: %1, %2").arg(index.column()).arg(index.row());
+ case 1:
+ return 42;
+ default:
+ break;
+ }
+
+ return QVariant();
+ }
+
+ Q_INVOKABLE void change() { beginResetModel(); toggle = !toggle; endResetModel(); }
+
+ QHash<int, QByteArray> roleNames() const override
+ {
+ if (toggle)
+ return { {0, "foo"} };
+ else
+ return { {1, "bar"} };
+ }
+
+ bool toggle = true;
+};
+
+void tst_QQmlDelegateModel::resettingRolesRespected()
+{
+ auto model = std::make_unique<TableModel>();
+ QQmlApplicationEngine engine;
+ engine.setInitialProperties({ {"model", QVariant::fromValue(model.get()) }} );
+ engine.load(testFileUrl("resetModelData.qml"));
+ QTRY_VERIFY(!engine.rootObjects().isEmpty());
+ QObject *root = engine.rootObjects().constFirst();
+ QVERIFY(!root->property("success").toBool());
+ model->change();
+ QTRY_VERIFY(root->property("success").toBool());
+}
+
void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst_data()
{
QTest::addColumn<QUrl>("qmlFileUrl");
@@ -207,6 +278,344 @@ void tst_QQmlDelegateModel::nestedDelegates()
QFAIL("Loader not found");
}
+void tst_QQmlDelegateModel::universalModelData()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("universalModelData.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data());
+ QVERIFY(delegateModel);
+
+ for (int i = 0; i < 6; ++i) {
+ delegateModel->setProperty("n", i);
+ QObject *delegate = delegateModel->object(0);
+ QObject *modelItem = delegate->property("modelSelf").value<QObject *>();
+ QVERIFY(modelItem != nullptr);
+ switch (i) {
+ case 0: {
+ // list model with 1 role
+ QCOMPARE(delegate->property("modelA"), QStringLiteral("a"));
+ QVERIFY(!delegate->property("modelDataA").isValid());
+ QCOMPARE(delegate->property("modelDataSelf"), QStringLiteral("a"));
+ QCOMPARE(delegate->property("modelModelData"), QStringLiteral("a"));
+ QCOMPARE(delegate->property("modelAnonymous"), QStringLiteral("a"));
+ break;
+ }
+ case 1: {
+ // list model with 2 roles
+ QCOMPARE(delegate->property("modelA"), QStringLiteral("a"));
+ QCOMPARE(delegate->property("modelDataA"), QStringLiteral("a"));
+ QCOMPARE(delegate->property("modelDataSelf"), QVariant::fromValue(modelItem));
+ QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelItem));
+ QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelItem));
+ break;
+ }
+ case 2: {
+ // JS array of objects
+ QCOMPARE(delegate->property("modelA"), QStringLiteral("a"));
+ QCOMPARE(delegate->property("modelDataA"), QStringLiteral("a"));
+
+ // Do the comparison in QVariantMap. The values get converted back and forth a
+ // few times, making any JavaScript equality comparison impossible.
+ // This is only due to test setup, though.
+ const QVariantMap modelData = delegate->property("modelDataSelf").value<QVariantMap>();
+ QVERIFY(!modelData.isEmpty());
+ QCOMPARE(delegate->property("modelModelData").value<QVariantMap>(), modelData);
+ QCOMPARE(delegate->property("modelAnonymous").value<QVariantMap>(), modelData);
+ break;
+ }
+ case 3: {
+ // string list
+ QVERIFY(!delegate->property("modelA").isValid());
+ QVERIFY(!delegate->property("modelDataA").isValid());
+ QCOMPARE(delegate->property("modelDataSelf"), QStringLiteral("a"));
+ QCOMPARE(delegate->property("modelModelData"), QStringLiteral("a"));
+ QCOMPARE(delegate->property("modelAnonymous"), QStringLiteral("a"));
+ break;
+ }
+ case 4: {
+ // single object
+ QCOMPARE(delegate->property("modelA"), QStringLiteral("a"));
+ QCOMPARE(delegate->property("modelDataA"), QStringLiteral("a"));
+ QObject *modelData = delegate->property("modelDataSelf").value<QObject *>();
+ QVERIFY(modelData != nullptr);
+ QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelData));
+ QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelData));
+ break;
+ }
+ case 5: {
+ // a number
+ QVERIFY(!delegate->property("modelA").isValid());
+ QVERIFY(!delegate->property("modelDataA").isValid());
+ const QVariant modelData = delegate->property("modelDataSelf");
+
+ // This is int on 32bit systems because qsizetype fits into int there.
+ // On 64bit systems it's double because qsizetype doesn't fit into int.
+ if (sizeof(qsizetype) > sizeof(int))
+ QCOMPARE(modelData.metaType(), QMetaType::fromType<double>());
+ else
+ QCOMPARE(modelData.metaType(), QMetaType::fromType<int>());
+
+ QCOMPARE(modelData.value<int>(), 0);
+ QCOMPARE(delegate->property("modelModelData"), modelData);
+ QCOMPARE(delegate->property("modelAnonymous"), modelData);
+ break;
+ }
+ default:
+ QFAIL("wrong model number");
+ break;
+ }
+ }
+
+}
+
+void tst_QQmlDelegateModel::typedModelData()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("typedModelData.qml");
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data());
+ QVERIFY(delegateModel);
+
+ for (int i = 0; i < 4; ++i) {
+ if (i == 0) {
+ for (int j = 0; j < 3; ++j) {
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Could not find any constructor for value type QQmlPointFValueType "
+ "to call with value QVariant(double, 11)");
+ }
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + ":62:9: Unable to assign double to QPointF"));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + ":61:9: Unable to assign double to QPointF"));
+ }
+
+ delegateModel->setProperty("n", i);
+ QObject *delegate = delegateModel->object(0);
+ QVERIFY(delegate);
+ const QPointF modelItem = delegate->property("modelSelf").value<QPointF>();
+ switch (i) {
+ case 0: {
+ // list model with 1 role.
+ // Does not work, for the most part, because the model is singular
+ QCOMPARE(delegate->property("modelX"), 11.0);
+ QCOMPARE(delegate->property("modelDataX"), 0.0);
+ QCOMPARE(delegate->property("modelSelf"), QPointF(11.0, 0.0));
+ QCOMPARE(delegate->property("modelDataSelf"), QPointF());
+ QCOMPARE(delegate->property("modelModelData"), QPointF());
+ QCOMPARE(delegate->property("modelAnonymous"), QPointF());
+ break;
+ }
+ case 1: {
+ // list model with 2 roles
+ QCOMPARE(delegate->property("modelX"), 13.0);
+ QCOMPARE(delegate->property("modelDataX"), 13.0);
+ QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelItem));
+ QCOMPARE(delegate->property("modelDataSelf"), QVariant::fromValue(modelItem));
+ QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelItem));
+ QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelItem));
+ break;
+ }
+ case 2: {
+ // JS array of objects
+ QCOMPARE(delegate->property("modelX"), 17.0);
+ QCOMPARE(delegate->property("modelDataX"), 17.0);
+
+ const QPointF modelData = delegate->property("modelDataSelf").value<QPointF>();
+ QCOMPARE(modelData, QPointF(17, 18));
+ QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelData));
+ QCOMPARE(delegate->property("modelModelData").value<QPointF>(), modelData);
+ QCOMPARE(delegate->property("modelAnonymous").value<QPointF>(), modelData);
+ break;
+ }
+ case 3: {
+ // single object
+ QCOMPARE(delegate->property("modelX"), 21);
+ QCOMPARE(delegate->property("modelDataX"), 21);
+ const QPointF modelData = delegate->property("modelDataSelf").value<QPointF>();
+ QCOMPARE(modelData, QPointF(21, 22));
+ QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelData));
+ QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelData));
+ QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelData));
+ break;
+ }
+ default:
+ QFAIL("wrong model number");
+ break;
+ }
+ }
+
+}
+
+void tst_QQmlDelegateModel::requiredModelData()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("requiredModelData.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data());
+ QVERIFY(delegateModel);
+
+ for (int i = 0; i < 4; ++i) {
+ delegateModel->setProperty("n", i);
+ QObject *delegate = delegateModel->object(0);
+ QVERIFY(delegate);
+ const QVariant a = delegate->property("a");
+ QCOMPARE(a.metaType(), QMetaType::fromType<QString>());
+ QCOMPARE(a.toString(), QLatin1String("a"));
+ }
+}
+
+void tst_QQmlDelegateModel::overriddenModelData()
+{
+ QTest::failOnWarning(QRegularExpression(
+ "Final member [^ ]+ is overridden in class [^\\.]+. The override won't be used."));
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("overriddenModelData.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data());
+ QVERIFY(delegateModel);
+
+ for (int i = 0; i < 3; ++i) {
+ delegateModel->setProperty("n", i);
+ QObject *delegate = delegateModel->object(0);
+ QVERIFY(delegate);
+
+ if (i == 1 || i == 2) {
+ // You can actually not override if the model is a QObject or a JavaScript array.
+ // Someone is certainly relying on this.
+ // We need to find a migration mechanism to fix it.
+ QCOMPARE(delegate->objectName(), QLatin1String(" 0 0 e 0"));
+ } else {
+ QCOMPARE(delegate->objectName(), QLatin1String("a b c d e f"));
+ }
+ }
+}
+
+void tst_QQmlDelegateModel::deleteRace()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("deleteRace.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QTRY_COMPARE(o->property("count").toInt(), 2);
+ QTRY_COMPARE(o->property("count").toInt(), 0);
+}
+
+void tst_QQmlDelegateModel::persistedItemsStayInCache()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("persistedItemsCache.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ std::unique_ptr<QObject> object(component.create());
+ QVERIFY(object);
+ const QVariant properyListModel = object->property("testListModel");
+ QQmlListModel *listModel = qvariant_cast<QQmlListModel *>(properyListModel);
+ QVERIFY(listModel);
+ QTRY_COMPARE(object->property("createCount").toInt(), 3);
+ listModel->clear();
+ QTRY_COMPARE(object->property("destroyCount").toInt(), 3);
+}
+
+Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::forward_list)
+void tst_QQmlDelegateModel::unknownContainersAsModel()
+{
+ QQmlEngine engine;
+
+ QQmlComponent modelComponent(&engine);
+ modelComponent.setData("import QtQml.Models\nDelegateModel {}\n", QUrl());
+ QCOMPARE(modelComponent.status(), QQmlComponent::Ready);
+
+ QScopedPointer<QObject> o(modelComponent.create());
+ QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(o.data());
+ QVERIFY(delegateModel);
+
+ QQmlComponent delegateComponent(&engine);
+ delegateComponent.setData("import QtQml\nQtObject { objectName: modelData }\n", QUrl());
+ QCOMPARE(delegateComponent.status(), QQmlComponent::Ready);
+
+ delegateModel->setDelegate(&delegateComponent);
+
+ QList<QJsonObject> json;
+ for (int i = 0; i < 10; ++i)
+ json.append(QJsonObject({{"foo", i}}));
+
+ // Recognized as list
+ delegateModel->setModel(QVariant::fromValue(json));
+ QObject *obj = delegateModel->object(0, QQmlIncubator::Synchronous);
+ QVERIFY(obj);
+ QCOMPARE(delegateModel->count(), 10);
+ QCOMPARE(delegateModel->model().metaType(), QMetaType::fromType<QList<QJsonObject>>());
+
+ QVERIFY(qMetaTypeId<std::forward_list<int>>() > 0);
+ std::forward_list<int> mess;
+ mess.push_front(4);
+ mess.push_front(5);
+ mess.push_front(6);
+
+ // Converted into QVariantList
+ delegateModel->setModel(QVariant::fromValue(mess));
+ obj = delegateModel->object(0, QQmlIncubator::Synchronous);
+ QVERIFY(obj);
+ QCOMPARE(delegateModel->count(), 3);
+ QCOMPARE(delegateModel->model().metaType(), QMetaType::fromType<QVariantList>());
+}
+
+void tst_QQmlDelegateModel::doNotUnrefObjectUnderConstruction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("modifyObjectUnderConstruction.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ std::unique_ptr<QObject> object(component.create());
+ QVERIFY(object);
+ QTRY_COMPARE(object->property("testModel").toInt(), 0);
+}
+
+void tst_QQmlDelegateModel::clearCacheDuringInsertion()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("clearCacheDuringInsertion.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ std::unique_ptr<QObject> object(component.create());
+ QVERIFY(object);
+ QTRY_COMPARE(object->property("testModel").toInt(), 0);
+}
+
+void tst_QQmlDelegateModel::viewUpdatedOnDelegateChoiceAffectingRoleChange()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("viewUpdatedOnDelegateChoiceAffectingRoleChange.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ std::unique_ptr<QObject> object(component.create());
+ QVERIFY(object);
+ QQuickItem *listview = object->findChild<QQuickItem *>("listview");
+ QVERIFY(listview);
+ QTRY_VERIFY(listview->property("count").toInt() > 0);
+ bool returnedValue = false;
+ QMetaObject::invokeMethod(object.get(), "verify", Q_RETURN_ARG(bool, returnedValue));
+ QVERIFY(returnedValue);
+ returnedValue = false;
+
+ object->setProperty("triggered", "true");
+ QTRY_VERIFY(listview->property("count").toInt() > 0);
+ QMetaObject::invokeMethod(object.get(), "verify", Q_RETURN_ARG(bool, returnedValue));
+ QVERIFY(returnedValue);
+}
+
QTEST_MAIN(tst_QQmlDelegateModel)
#include "tst_qqmldelegatemodel.moc"
diff --git a/tests/auto/qml/qqmldirparser/CMakeLists.txt b/tests/auto/qml/qqmldirparser/CMakeLists.txt
index 0ed28b63ee..d7738c08f8 100644
--- a/tests/auto/qml/qqmldirparser/CMakeLists.txt
+++ b/tests/auto/qml/qqmldirparser/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmldirparser Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmldirparser LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmldirparser/data/versioned-internal/qmldir b/tests/auto/qml/qqmldirparser/data/versioned-internal/qmldir
new file mode 100644
index 0000000000..3a3e606099
--- /dev/null
+++ b/tests/auto/qml/qqmldirparser/data/versioned-internal/qmldir
@@ -0,0 +1,3 @@
+module MyModule
+internal InternalType 1.0 InternalType.qml
+
diff --git a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
index 55b776c6a2..4cbb7aaf47 100644
--- a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
+++ b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QObject>
@@ -60,7 +60,7 @@ namespace {
{
QStringList rv;
- foreach (const QQmlDirParser::Plugin &p, plugins)
+ for (const QQmlDirParser::Plugin &p : plugins)
rv.append(toString(p));
return rv;
@@ -86,7 +86,8 @@ namespace {
{
QStringList rv;
- foreach (const QQmlDirParser::Component &c, components.values())
+ const auto values = components.values();
+ for (const QQmlDirParser::Component &c : values)
rv.append(toString(c));
std::sort(rv.begin(), rv.end());
@@ -97,7 +98,7 @@ namespace {
{
QStringList rv;
- foreach (const QQmlDirParser::Import &c, components)
+ for (const QQmlDirParser::Import &c : components)
rv.append(toString(c));
std::sort(rv.begin(), rv.end());
@@ -115,7 +116,7 @@ namespace {
{
QStringList rv;
- foreach (const QQmlDirParser::Script &s, scripts)
+ for (const QQmlDirParser::Script &s : scripts)
rv.append(toString(s));
return rv;
@@ -421,6 +422,17 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< false;
+
+ QTest::newRow("versioned-internal")
+ << "versioned-internal/qmldir"
+ << QString()
+ << QStringList()
+ << QStringList()
+ << QStringList()
+ << QStringList({"InternalType|InternalType.qml|1|0|true"})
+ << QStringList()
+ << QStringList()
+ << false;
}
void tst_qqmldirparser::parse()
@@ -435,7 +447,7 @@ void tst_qqmldirparser::parse()
QFETCH(bool, designerSupported);
QFile f(testFile(file));
- f.open(QIODevice::ReadOnly);
+ QVERIFY(f.open(QIODevice::ReadOnly));
QQmlDirParser p;
p.parse(f.readAll());
diff --git a/tests/auto/qml/qqmlecmascript/CMakeLists.txt b/tests/auto/qml/qqmlecmascript/CMakeLists.txt
index f4b4169c82..12cab47a36 100644
--- a/tests/auto/qml/qqmlecmascript/CMakeLists.txt
+++ b/tests/auto/qml/qqmlecmascript/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlecmascript Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlecmascript LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -24,6 +30,7 @@ qt_internal_add_test(tst_qqmlecmascript
Qt::Network
Qt::QmlPrivate
Qt::QuickTestUtilsPrivate
+ Qt::QuickPrivate
TESTDATA ${test_data}
)
diff --git a/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml b/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml
new file mode 100644
index 0000000000..f8ee283f1e
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+import Qt.test
+
+QtObject {
+ property listPropertyAssignment_Gadget gadget
+ property ListPropertyAssignment_Object object: ListPropertyAssignment_Object { }
+
+ Component.onCompleted: {
+ gadget.gadgetStringList = ["Element1", "Element2", "Element3"]
+ gadget.gadgetVariantList = [1, "foo", null, true]
+ object.qobjectStringList = ["Element1", "Element2", "Element3"]
+
+ gadget.gadgetStringList[0] = "Completely new Element"
+ gadget.gadgetVariantList[0] = "Completely new Element"
+ object.qobjectStringList[0] = "Completely new Element"
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml
index 9273a52f54..ff7fe4434c 100644
--- a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml
+++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml
@@ -22,7 +22,6 @@ Rectangle {
if (component.status == Component.Ready) {
text.vp = component.createObject(null); // has JavaScript ownership
}
- gc();
}
}
}
diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml
index f4307081c5..4e8da872f5 100644
--- a/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml
+++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml
@@ -25,7 +25,6 @@ Rectangle {
if (component.status == Component.Ready) {
textTwo.vp = component.createObject(null); // has JavaScript ownership
}
- gc();
}
function deassignVp() {
diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml
deleted file mode 100644
index d611e0fe30..0000000000
--- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml
+++ /dev/null
@@ -1,12 +0,0 @@
-import QtQuick 2.0
-
-Item {
- property int changeCount: 0
-
- // invalid property name - we don't allow $
- property bool $nameWithDollarsign: false
-
- on$NameWithDollarsignChanged: {
- changeCount = changeCount + 4;
- }
-}
diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml
deleted file mode 100644
index a6862517c6..0000000000
--- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml
+++ /dev/null
@@ -1,12 +0,0 @@
-import QtQuick 2.0
-
-Item {
- property int changeCount: 0
-
- property bool _6nameWithUnderscoreNumber: false
-
- // invalid property name - the first character after an underscore must be a letter
- on_6NameWithUnderscoreNumberChanged: {
- changeCount = changeCount + 3;
- }
-}
diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml
index f91fb71f1f..9a3141e15a 100644
--- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml
+++ b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml
@@ -6,6 +6,8 @@ Item {
property bool normalName: false
property bool _nameWithUnderscore: false
property bool ____nameWithUnderscores: false
+ property bool _6nameWithUnderscoreNumber: false
+ property bool $nameWithDollarsign: false
onNormalNameChanged: {
changeCount = changeCount + 1;
@@ -19,9 +21,20 @@ Item {
changeCount = changeCount + 3;
}
+ on$NameWithDollarsignChanged: {
+ changeCount = changeCount + 4;
+ }
+
+ on_6NameWithUnderscoreNumberChanged: {
+ changeCount = changeCount + 5;
+ }
+
Component.onCompleted: {
normalName = true;
_nameWithUnderscore = true;
____nameWithUnderscores = true;
+ $nameWithDollarsign = true;
+ _6nameWithUnderscoreNumber = true;
}
+
}
diff --git a/tests/auto/qml/qqmlecmascript/data/date.qml b/tests/auto/qml/qqmlecmascript/data/date.qml
index 8e190b1f8f..33644b604d 100644
--- a/tests/auto/qml/qqmlecmascript/data/date.qml
+++ b/tests/auto/qml/qqmlecmascript/data/date.qml
@@ -26,8 +26,12 @@ Item {
function check_value(date, tag, qdt) {
var result = true;
+ if (isNaN(date)) {
+ console.warn("Invalid Date");
+ return false;
+ }
if (date.getFullYear() != 2014) {
- console.warn("Wrong year (" + tag + "):", date.getFullYear(), "!= 2014")
+ console.warn("Wrong year (" + tag + "):", date.getFullYear(), "!= 2014");
result = false;
}
// July; JS's months are Jan 0 to 11 Dec, vs. Qt's 1 to 12.
diff --git a/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml b/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml
new file mode 100644
index 0000000000..51c321684e
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml
@@ -0,0 +1,30 @@
+import QtQml
+import test
+
+QtObject {
+ id: root
+
+ property FrozenObjects a: FrozenObjects { objectName: "a" }
+ property FrozenObjects b: FrozenObjects { objectName: "b" }
+
+ // Create wrappers and immediately discard them
+ objectName: a.getConst().objectName + "/" + b.getNonConst().objectName
+
+ // Create a non-const wrapper and retain it
+ property var objNonConst: a.getNonConst()
+
+ // Create a const wrapper and retain it
+ property var objConst: b.getConst()
+
+ property int gcs: 0
+
+ property Timer t: Timer {
+ interval: 1
+ running: true
+ repeat: true
+ onTriggered: {
+ gc();
+ ++root.gcs;
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/getThis.qml b/tests/auto/qml/qqmlecmascript/data/getThis.qml
index db9854e872..90c51caf4d 100644
--- a/tests/auto/qml/qqmlecmascript/data/getThis.qml
+++ b/tests/auto/qml/qqmlecmascript/data/getThis.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.12
diff --git a/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml b/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml
new file mode 100644
index 0000000000..9d2ee433fd
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml
@@ -0,0 +1,6 @@
+import Qt.test
+import QtQml
+
+QtObject {
+ Component.onCompleted: SingletonInheritanceTest.trackPage("test", {x: 42})
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml
index 6f5094de27..5f017c4880 100644
--- a/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml
@@ -16,7 +16,6 @@ Item {
function assignVarProp() {
vp = constructGarbage();
- gc();
}
function deassignVarProp() {
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml
index ecc4892334..144fcc8a93 100644
--- a/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml
@@ -16,7 +16,6 @@ Item {
function assignCircular() {
vp = constructGarbage();
- gc();
}
function deassignCircular() {
diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml
new file mode 100644
index 0000000000..a50c8e0f46
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml
@@ -0,0 +1,12 @@
+import QtQml
+import Qt.test.singletonWithEnum
+
+QtObject {
+ id: root
+ required property QtObject invokableObject
+
+ Component.onCompleted: {
+ root.invokableObject.method_typeWrapper(Component)
+ root.invokableObject.method_typeWrapper(SingletonWithEnum)
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml
new file mode 100644
index 0000000000..490fec2dc8
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ property var val: undefined
+ property var observes: width
+ width: val
+ implicitWidth: 200
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/resetGadget.qml b/tests/auto/qml/qqmlecmascript/data/resetGadget.qml
new file mode 100644
index 0000000000..2bc196da34
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/resetGadget.qml
@@ -0,0 +1,9 @@
+import Qt.test
+
+ResettableGadgetHolder {
+ id: root
+ property bool trigger: false
+ onTriggerChanged: {
+ root.g.value = undefined
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml
new file mode 100644
index 0000000000..2933d9b4d5
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+Item {
+ height: undefined
+ implicitHeight: 30
+ property int steps: 0
+
+ Behavior on height {
+ NumberAnimation {
+ duration: 500
+ }
+ }
+
+ onHeightChanged: ++steps
+
+ Component.onCompleted: {
+ height = Qt.binding(() => implicitHeight);
+ implicitHeight = 60;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml
new file mode 100644
index 0000000000..7d43aa6c05
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml
@@ -0,0 +1,21 @@
+import Qt.test
+import QtQuick
+
+Item {
+ id: root
+ property int count: 0
+ signal someSignal
+
+ property Item item: Item {
+ id: contextItem
+ function test() {
+ count++;
+ }
+ }
+
+ function itemDestroy() {
+ contextItem.destroy()
+ }
+
+ Component.onCompleted: root.someSignal.connect(contextItem, contextItem.test);
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml
new file mode 100644
index 0000000000..1123edf3f7
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml
@@ -0,0 +1,30 @@
+import Qt.test
+import QtQuick
+
+MyQmlObject {
+ id: root
+ property int a: 0
+
+ signal someSignal
+
+ function disconnectSignal() {
+ root.someSignal.disconnect(other.MyQmlObject, root.test)
+ }
+
+ function destroyObj() {
+ other.destroy()
+ }
+
+ function test() {
+ other.MyQmlObject.value2++
+ root.a = other.MyQmlObject.value2
+ }
+
+ property MyQmlObject obj
+ obj: MyQmlObject {
+ id: other
+ MyQmlObject.value2: 0
+ }
+
+ Component.onCompleted: root.someSignal.connect(other.MyQmlObject, root.test)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml
new file mode 100644
index 0000000000..efbbc9fedc
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml
@@ -0,0 +1,36 @@
+import Qt.test
+import QtQml
+
+QtObject {
+ id: root
+
+ property int a: 0
+ property int b: 0
+
+ signal someSignal
+
+ function destroyObj() {
+ obj.destroy()
+ }
+
+ function test() {
+ ++a
+ }
+
+ component DestructionReceiver: QtObject {
+ // Has its own context and therefore can receive Component.onDestruction
+ }
+
+ property QtObject obj: QtObject {
+ property QtObject inner: DestructionReceiver {
+ Component.onDestruction: {
+ // The outer obj is already queued for deletion.
+ // We don't want to see this signal delivered.
+ root.someSignal();
+ ++root.b
+ }
+ }
+ }
+
+ Component.onCompleted: someSignal.connect(obj, test)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml
new file mode 100644
index 0000000000..f666945b33
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml
@@ -0,0 +1,21 @@
+import QtQuick
+import Test
+
+Item {
+ id: root
+
+ property int a: 0
+ signal mySignal
+
+ function test() {
+ MyInheritedQmlObjectSingleton.value++
+ root.a = MyInheritedQmlObjectSingleton.value
+ }
+
+ function disconnectSingleton() {
+ root.mySignal.disconnect(MyInheritedQmlObjectSingleton, root.test)
+ }
+
+ Component.onCompleted: root.mySignal.connect(MyInheritedQmlObjectSingleton,
+ root.test)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml
new file mode 100644
index 0000000000..9d24fa85ae
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml
@@ -0,0 +1,19 @@
+import Qt.test
+import QtQuick
+
+Item {
+ id: root
+ property int count: 0
+ signal someSignal
+ signal disconnectSignal
+
+ property Item item: Item {
+ id: contextItem
+ function test() {
+ count++;
+ }
+ }
+
+ Component.onCompleted: root.someSignal.connect(contextItem, contextItem.test);
+ onDisconnectSignal: { root.someSignal.disconnect(contextItem, contextItem.test); }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/singletonTest.qml b/tests/auto/qml/qqmlecmascript/data/singletonTest.qml
index b0e951b89a..f4bdcdbed3 100644
--- a/tests/auto/qml/qqmlecmascript/data/singletonTest.qml
+++ b/tests/auto/qml/qqmlecmascript/data/singletonTest.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Test 1.0
diff --git a/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml b/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml
index 5a5bad5b8c..5d6af4d67c 100644
--- a/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml
+++ b/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Test 1.0
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp
index 40f5e5cf5c..5f7713392b 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.cpp
+++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include "testtypes.h"
#ifndef QT_NO_WIDGETS
# include <QWidget>
@@ -8,6 +9,7 @@
#include <QQmlEngine>
#include <QJSEngine>
#include <QThread>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
class BaseExtensionObject : public QObject
{
@@ -104,7 +106,7 @@ public:
void setWidth(int) { }
};
-void MyQmlObject::v8function(QQmlV4Function *function)
+void MyQmlObject::v8function(QQmlV4FunctionPtr function)
{
function->v4engine()->throwError(QStringLiteral("Exception thrown from within QObject slot"));
}
@@ -393,9 +395,7 @@ void QObjectContainer::children_append(QQmlListProperty<QObject> *prop, QObject
if (that->gcOnAppend) {
QQmlEngine *engine = qmlEngine(that);
- engine->collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(*engine);
}
}
@@ -412,7 +412,7 @@ QObject *QObjectContainer::children_at(QQmlListProperty<QObject> *prop, qsizetyp
void QObjectContainer::children_clear(QQmlListProperty<QObject> *prop)
{
QObjectContainer *that = static_cast<QObjectContainer*>(prop->object);
- foreach (QObject *c, that->dataChildren)
+ for (QObject *c : std::as_const(that->dataChildren))
QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*)));
that->dataChildren.clear();
}
@@ -436,6 +436,44 @@ void ClassWithQProperty2::callback()
// Q_UNUSED(this->value.value()); // force evaluation
}
+ListPropertyAssignment_Gadget::ListPropertyAssignment_Gadget() { }
+
+QStringList ListPropertyAssignment_Gadget::gadgetStringList() const
+{
+ return m_gadgetStringList;
+}
+
+void ListPropertyAssignment_Gadget::setGadgetStringList(const QStringList &list)
+{
+ if (m_gadgetStringList == list)
+ return;
+ m_gadgetStringList = list;
+}
+
+QVariantList ListPropertyAssignment_Gadget::gadgetVariantList() const
+{
+ return m_gadgetVariantList;
+}
+
+void ListPropertyAssignment_Gadget::setGadgetVariantList(const QVariantList &list)
+{
+ if (m_gadgetVariantList == list)
+ return;
+ m_gadgetVariantList = list;
+}
+
+ListPropertyAssignment_Object::ListPropertyAssignment_Object(QObject *parent)
+ : QObject{ parent } { }
+
+void ListPropertyAssignment_Object::setQobjectStringList(const QStringList &newList)
+{
+ if (m_qobjectStringList == newList)
+ return;
+ m_qobjectStringList = newList;
+}
+
+bool MetaCallInterceptor::didGetObjectDestroyedCallback = false;
+
void registerTypes()
{
qmlRegisterType<MyQmlObject>("Qt.test", 1,0, "MyQmlObjectAlias");
@@ -542,6 +580,15 @@ void registerTypes()
qmlRegisterType<Receiver>("Qt.test", 1,0, "Receiver");
qmlRegisterType<Sender>("Qt.test", 1,0, "Sender");
qmlRegisterTypesAndRevisions<ReadOnlyBindable>("Qt.test", 1);
+ qmlRegisterTypesAndRevisions<ResettableGadgetHolder>("Qt.test", 1);
+
+ qmlRegisterTypesAndRevisions<ListPropertyAssignment_Gadget>("Qt.test", 1);
+ qmlRegisterTypesAndRevisions<ListPropertyAssignment_Object>("Qt.test", 1);
+
+ qmlRegisterTypesAndRevisions<SingletonRegistrationWrapper>("Qt.test", 1);
+
+ qmlRegisterExtendedType<TypeWithCustomMetaObject, TypeToTriggerProxyMetaObject>(
+ "Qt.test", 1,0, "TypeWithCustomMetaObject");
}
#include "testtypes.moc"
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index ff9dda36d1..cc20437fff 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
@@ -31,6 +31,7 @@
#include <private/qqmlengine_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qqmlcomponentattached_p.h>
class MyQmlAttachedObject : public QObject
{
@@ -243,7 +244,7 @@ public slots:
void myinvokable(MyQmlObject *o) { myinvokableObject = o; }
void variantMethod(const QVariant &v) { m_variant = v; }
void qjsvalueMethod(const QJSValue &v) { m_qjsvalue = v; }
- void v8function(QQmlV4Function*);
+ void v8function(QQmlV4FunctionPtr);
void registeredFlagMethod(Qt::MouseButtons v) { m_buttons = v; }
QString slotWithReturnValue(const QString &arg) { return arg; }
int resetCount() { return m_resetCount; }
@@ -862,6 +863,17 @@ struct NonRegisteredType
struct CompletelyUnknown;
+class SingletonWithEnum : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(TestEnum)
+public:
+ enum TestEnum {
+ TestValue = 42,
+ TestValue_MinusOne = -1
+ };
+};
+
class MyInvokableObject : public MyInvokableBaseObject
{
Q_OBJECT
@@ -919,7 +931,7 @@ public:
Q_INVOKABLE void method_unknown(NonRegisteredType) { invoke(28); }
- Q_INVOKABLE void method_overload2(QQmlV4Function *v)
+ Q_INVOKABLE void method_overload2(QQmlV4FunctionPtr v)
{
invoke(31);
QV4::Scope scope(v->v4engine());
@@ -955,12 +967,43 @@ public:
invoke(40);
m_actuals << f;
}
+
Q_INVOKABLE void method_qobject(QObject *o)
{
invoke(41);
m_actuals << QVariant::fromValue(o);
}
+ Q_INVOKABLE QQmlComponent *someComponent() { return &m_someComponent; }
+ Q_INVOKABLE void method_component(QQmlComponent *c)
+ {
+ invoke(42);
+ m_actuals << QVariant::fromValue(c);
+ }
+
+ Q_INVOKABLE MyTypeObject *someTypeObject() { return &m_someTypeObject; }
+ Q_INVOKABLE void method_component(MyTypeObject *c)
+ {
+ invoke(43);
+ m_actuals << QVariant::fromValue(c);
+ }
+
+ Q_INVOKABLE void method_component(const QUrl &c)
+ {
+ invoke(44);
+ m_actuals << QVariant::fromValue(c);
+ }
+
+ Q_INVOKABLE void method_typeWrapper(QQmlComponentAttached *attached)
+ {
+ m_actuals << QVariant::fromValue(attached);
+ }
+
+ Q_INVOKABLE void method_typeWrapper(SingletonWithEnum *singleton)
+ {
+ m_actuals << QVariant::fromValue(singleton);
+ }
+
private:
friend class MyInvokableBaseObject;
void invoke(int idx) { if (m_invoked != -1) m_invokedError = true; m_invoked = idx;}
@@ -969,6 +1012,8 @@ private:
QVariantList m_actuals;
QFont m_someFont;
+ QQmlComponent m_someComponent;
+ MyTypeObject m_someTypeObject;
public:
Q_SIGNALS:
@@ -1807,17 +1852,6 @@ public:
QML_DECLARE_TYPEINFO(FallbackBindingsTypeObject, QML_HAS_ATTACHED_PROPERTIES)
QML_DECLARE_TYPEINFO(FallbackBindingsTypeDerived, QML_HAS_ATTACHED_PROPERTIES)
-class SingletonWithEnum : public QObject
-{
- Q_OBJECT
- Q_ENUMS(TestEnum)
-public:
- enum TestEnum {
- TestValue = 42,
- TestValue_MinusOne = -1
- };
-};
-
// Like QtObject, but with default property
class QObjectContainer : public QObject
{
@@ -2000,6 +2034,161 @@ public:
QBindable<int> bindableX() const { return &_xProp; }
};
+class ResettableGadget
+{
+ Q_GADGET
+ Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue)
+
+ qreal m_value = 0;
+
+public:
+ qreal value() const { return m_value; }
+ void setValue(qreal val) { m_value = val; }
+ void resetValue() { m_value = 42; }
+};
+
+class ResettableGadgetHolder : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(ResettableGadget g READ g WRITE setG NOTIFY gChanged)
+ ResettableGadget m_g;
+
+signals:
+ void gChanged();
+
+public:
+ ResettableGadget g() const { return m_g; }
+ void setG(ResettableGadget newG)
+ {
+ if (m_g.value() == newG.value())
+ return;
+ m_g = newG;
+ Q_EMIT gChanged();
+ }
+};
+
+class ListPropertyAssignment_Gadget
+{
+ Q_GADGET
+ Q_PROPERTY(QStringList gadgetStringList READ gadgetStringList WRITE setGadgetStringList)
+ Q_PROPERTY(QVariantList gadgetVariantList READ gadgetVariantList WRITE setGadgetVariantList)
+ QML_VALUE_TYPE(listPropertyAssignment_Gadget)
+public:
+ ListPropertyAssignment_Gadget();
+ QStringList gadgetStringList() const;
+ void setGadgetStringList(const QStringList &list);
+
+ QVariantList gadgetVariantList() const;
+ void setGadgetVariantList(const QVariantList &list);
+
+private:
+ QStringList m_gadgetStringList;
+ QVariantList m_gadgetVariantList;
+};
+
+class ListPropertyAssignment_Object : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QStringList qobjectStringList READ qobjectStringList WRITE setQobjectStringList)
+public:
+ explicit ListPropertyAssignment_Object(QObject *parent = nullptr);
+
+ QStringList qobjectStringList() const { return m_qobjectStringList; }
+
+ void setQobjectStringList(const QStringList &newList);
+
+private:
+ QStringList m_qobjectStringList;
+};
+
+class SingletonBase : public QObject {
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE virtual void trackPage(const QString&) {}
+ Q_INVOKABLE virtual void trackPage(const QString&, const QVariantMap&) {}
+
+ bool m_okay = false;
+};
+
+class SingletonImpl : public SingletonBase {
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE virtual void trackPage(const QString&) override {}
+ Q_INVOKABLE virtual void trackPage(const QString&, const QVariantMap&) override
+ {
+ m_okay = true;
+ }
+};
+
+class SingletonRegistrationWrapper {
+ Q_GADGET
+ QML_FOREIGN(SingletonBase)
+ QML_NAMED_ELEMENT(SingletonInheritanceTest)
+ QML_SINGLETON
+
+public:
+ static SingletonBase* create(QQmlEngine*, QJSEngine*) {
+ return new SingletonImpl();
+ }
+
+private:
+ SingletonRegistrationWrapper() = default;
+};
+
+class MetaCallInterceptor : public QObject, public QDynamicMetaObjectData
+{
+ Q_OBJECT
+public:
+ MetaCallInterceptor()
+ {
+ didGetObjectDestroyedCallback = false;
+ }
+
+ void objectDestroyed(QObject *object) override
+ {
+ didGetObjectDestroyedCallback = true;
+
+ // Deletes this meta object
+ QDynamicMetaObjectData::objectDestroyed(object);
+ }
+
+ QMetaObject *toDynamicMetaObject(QObject *) override
+ {
+ return const_cast<QMetaObject *>(&MetaCallInterceptor::staticMetaObject);
+ }
+
+ int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override
+ {
+ return o->qt_metacall(call, idx, argv);
+ }
+
+ static bool didGetObjectDestroyedCallback;
+};
+
+struct TypeToTriggerProxyMetaObject
+{
+ Q_GADGET
+};
+
+class TypeWithCustomMetaObject : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(TypeWithCustomMetaObject)
+ QML_EXTENDED_NAMESPACE(TypeToTriggerProxyMetaObject)
+
+public:
+ TypeWithCustomMetaObject()
+ {
+ auto *p = QObjectPrivate::get(this);
+ Q_ASSERT(!p->metaObject);
+ p->metaObject = new MetaCallInterceptor;
+ }
+};
+
void registerTypes();
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index edb1e9ba80..bd60093a7b 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -1,6 +1,7 @@
// Copyright (C) 2017 Crimson AS <info@crimson.no>
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <QtTest/QtTest>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlengine.h>
@@ -27,6 +28,8 @@
#include <private/qqmlabstractbinding_p.h>
#include <private/qqmlvaluetypeproxybinding_p.h>
#include <QtCore/private/qproperty_p.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/private/qquickitem_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
@@ -376,6 +379,8 @@ private slots:
void qpropertyBindingHandlesUndefinedCorrectly();
void qpropertyBindingHandlesUndefinedWithoutResetCorrectly_data();
void qpropertyBindingHandlesUndefinedWithoutResetCorrectly();
+ void qpropertyBindingRestoresObserverAfterReset();
+ void qpropertyBindingObserverCorrectlyLinkedAfterReset();
void hugeRegexpQuantifiers();
void singletonTypeWrapperLookup();
void getThisObject();
@@ -416,6 +421,13 @@ private slots:
void doNotCrashOnReadOnlyBindable();
+ void resetGadget();
+ void assignListPropertyByIndexOnGadget();
+
+ void methodCallOnDerivedSingleton();
+
+ void proxyMetaObject();
+
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt);
@@ -432,14 +444,6 @@ private:
}
};
-static void gc(QQmlEngine &engine)
-{
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
-}
-
-
tst_qqmlecmascript::tst_qqmlecmascript()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
@@ -3127,11 +3131,14 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->invoked(), -1);
QCOMPARE(o->actuals().size(), 0);
- o->reset();
- QVERIFY(EVALUATE_ERROR("object.method_QPointF(object)"));
- QCOMPARE(o->error(), false);
- QCOMPARE(o->invoked(), -1);
- QCOMPARE(o->actuals().size(), 0);
+ // This fails if the QtQml module is loaded but works if it's not.
+ // If QtQml is loaded, QPointF is a structured value type that can be created from any object.
+ //
+ // o->reset();
+ // QVERIFY(EVALUATE_ERROR("object.method_QPointF(object)"));
+ // QCOMPARE(o->error(), false);
+ // QCOMPARE(o->invoked(), -1);
+ // QCOMPARE(o->actuals().size(), 0);
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", QV4::Primitive::undefinedValue()));
@@ -3186,6 +3193,17 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->invoked(), -1); // no function got called due to incompatible arguments
}
+ {
+ o->reset();
+ QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs3.qml"));
+ QScopedPointer<QObject> root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) };
+ QVERIFY(root);
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->actuals().size(), 2);
+ QCOMPARE(o->actuals().at(0).metaType(), QMetaType::fromType<QQmlComponentAttached *>());
+ QCOMPARE(o->actuals().at(1).metaType(), QMetaType::fromType<SingletonWithEnum *>());
+ }
+
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
@@ -3517,6 +3535,27 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), -1);
QCOMPARE(o->actuals(), QVariantList());
+
+ o->reset();
+ QVERIFY(EVALUATE_VALUE("object.method_component(object.someComponent())",
+ QV4::Primitive::undefinedValue()));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 42);
+ QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(o->someComponent()));
+
+ o->reset();
+ QVERIFY(EVALUATE_VALUE("object.method_component(object.someTypeObject())",
+ QV4::Primitive::undefinedValue()));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 43);
+ QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(o->someTypeObject()));
+
+ o->reset();
+ QVERIFY(EVALUATE_VALUE("object.method_component('qrc:/somewhere/else')",
+ QV4::Primitive::undefinedValue()));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 44);
+ QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(QUrl("qrc:/somewhere/else")));
}
void tst_qqmlecmascript::resolveClashingProperties()
@@ -3815,6 +3854,82 @@ void tst_qqmlecmascript::scriptConnect()
QScopedPointer<QObject> root { component.create() };
QVERIFY2(root, qPrintable(component.errorString()));
}
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.8.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ QCOMPARE(obj.data()->property("count"), 0);
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj.data()->property("count"), 1);
+
+ QMetaObject::invokeMethod(obj.data(), "itemDestroy");
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj.data()->property("count"), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.9.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ MyQmlObject *object = qobject_cast<MyQmlObject *>(obj.data());
+
+ QCOMPARE(object->property("a"), 0);
+
+ QMetaObject::invokeMethod(object, "someSignal");
+ QCOMPARE(object->property("a"), 1);
+
+ QMetaObject::invokeMethod(object, "destroyObj", Qt::DirectConnection);
+ QApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QApplication::processEvents();
+
+ QMetaObject::invokeMethod(object, "someSignal");
+
+ QCOMPARE(object->property("a"), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnectSingleton.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
+ QCOMPARE(obj.data()->property("a").toInt(), 1);
+ engine.clearSingletons();
+ QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
+ QCOMPARE(obj.data()->property("a").toInt(), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.deletion.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(!obj.isNull());
+
+ QCOMPARE(obj->property("a"), 0);
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj->property("a"), 1);
+
+ QCOMPARE(obj->property("b"), 0);
+ QMetaObject::invokeMethod(obj.data(), "destroyObj", Qt::DirectConnection);
+
+ QTRY_COMPARE(obj->property("b"), 1);
+ QCOMPARE(obj->property("a"), 1);
+ }
}
void tst_qqmlecmascript::scriptDisconnect()
@@ -3895,6 +4010,60 @@ void tst_qqmlecmascript::scriptDisconnect()
emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
QCOMPARE(object->property("test").toInt(), 3);
}
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptDisconnect.5.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ QCOMPARE(obj.data()->property("count"), 0);
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj.data()->property("count"), 1);
+
+ QMetaObject::invokeMethod(obj.data(), "disconnectSignal");
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj.data()->property("count"), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.9.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ MyQmlObject *object = qobject_cast<MyQmlObject *>(obj.data());
+
+ QCOMPARE(object->property("a"), 0);
+
+ QMetaObject::invokeMethod(object, "someSignal");
+ QCOMPARE(object->property("a"), 1);
+
+ QMetaObject::invokeMethod(object, "disconnectSignal", Qt::DirectConnection);
+
+ QMetaObject::invokeMethod(object, "someSignal");
+
+ QCOMPARE(object->property("a"), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnectSingleton.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
+ QCOMPARE(obj.data()->property("a").toInt(), 1);
+
+ QMetaObject::invokeMethod(obj.data(), "disconnectSingleton", Qt::DirectConnection);
+ QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
+ QCOMPARE(obj.data()->property("a").toInt(), 1);
+ }
}
class OwnershipObject : public QObject
@@ -3923,10 +4092,7 @@ void tst_qqmlecmascript::ownership()
QScopedPointer<QObject> object(component.create(context.data()));
- engine.collectGarbage();
-
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(own.object.isNull());
}
@@ -3940,10 +4106,7 @@ void tst_qqmlecmascript::ownership()
QScopedPointer<QObject> object(component.create(context.data()));
- engine.collectGarbage();
-
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(own.object != nullptr);
}
@@ -4019,9 +4182,7 @@ void tst_qqmlecmascript::ownershipCustomReturnValue()
QVERIFY(source.value != nullptr);
}
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(source.value.isNull());
}
@@ -4052,10 +4213,7 @@ void tst_qqmlecmascript::ownershipRootObject()
QScopedPointer<QObject> object(component.create(context.data()));
QVERIFY2(object, qPrintable(component.errorString()));
- engine.collectGarbage();
-
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(own.object != nullptr);
}
@@ -4080,10 +4238,7 @@ void tst_qqmlecmascript::ownershipConsistency()
QScopedPointer<QObject> object(component.create(context.data()));
QVERIFY2(object, qPrintable(component.errorString()));
- engine.collectGarbage();
-
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(own.object != nullptr);
}
@@ -4613,6 +4768,7 @@ void tst_qqmlecmascript::verifyContextLifetime(const QQmlRefPointer<QQmlContextD
}
ctxt->engine()->collectGarbage();
+ QTRY_VERIFY(gcDone(ctxt->engine()));
qml = scripts->get(i);
newContext = qml ? qml->getContext() : nullptr;
QCOMPARE(scriptContext.data(), newContext.data());
@@ -5257,6 +5413,7 @@ void tst_qqmlecmascript::propertyChangeSlots()
QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
+ QCOMPARE(object->property("changeCount"), 15);
// ensure that invalid property names fail properly.
QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
@@ -5272,20 +5429,6 @@ void tst_qqmlecmascript::propertyChangeSlots()
QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
object.reset(e2.create());
QVERIFY(!object);
-
- QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
- QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
- expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
- QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
- object.reset(e3.create());
- QVERIFY(!object);
-
- QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
- QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
- expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
- QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
- object.reset(e4.create());
- QVERIFY(!object);
}
void tst_qqmlecmascript::propertyVar_data()
@@ -5453,7 +5596,9 @@ void tst_qqmlecmascript::propertyVarOwnership()
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
QMetaObject::invokeMethod(object.data(), "createComponent");
- engine.collectGarbage();
+ // This test only works if we don't deliver the pending delete later event
+ // that collectGarbage will post before calling runTest
+ gc(engine, GCFlags::DontSendPostedEvents);
QMetaObject::invokeMethod(object.data(), "runTest");
QCOMPARE(object->property("test").toBool(), true);
}
@@ -5469,8 +5614,7 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership()
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
QMetaObject::invokeMethod(object.data(), "assignCircular");
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
- QCoreApplication::processEvents();
+ gc(engine);
QObject *rootObject = object->property("vp").value<QObject*>();
QVERIFY(rootObject != nullptr);
QObject *childObject = rootObject->findChild<QObject*>("text");
@@ -5479,6 +5623,8 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership()
QCOMPARE(childObject->property("textCanary").toInt(), 10);
// Creates a reference to a constructed QObject:
QMetaObject::invokeMethod(childObject, "constructQObject");
+ // Don't send delete later events yet, we do it manually later
+ gc(engine, GCFlags::DontSendPostedEvents);
QPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
QVERIFY(!qobjectGuard.isNull());
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
@@ -5497,8 +5643,7 @@ void tst_qqmlecmascript::propertyVarReparent()
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
QMetaObject::invokeMethod(object.data(), "assignVarProp");
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
- QCoreApplication::processEvents();
+ gc(engine);
QObject *rect = object->property("vp").value<QObject*>();
QObject *text = rect->findChild<QObject*>("textOne");
QObject *text2 = rect->findChild<QObject*>("textTwo");
@@ -5512,6 +5657,7 @@ void tst_qqmlecmascript::propertyVarReparent()
QCOMPARE(text2->property("textCanary").toInt(), 12);
// now construct an image which we will reparent.
QMetaObject::invokeMethod(text2, "constructQObject");
+ gc(engine, GCFlags::DontSendPostedEvents);
QObject *image = text2->property("vp").value<QObject*>();
QPointer<QObject> imageGuard(image);
QVERIFY(!imageGuard.isNull());
@@ -5539,8 +5685,7 @@ void tst_qqmlecmascript::propertyVarReparentNullContext()
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
QMetaObject::invokeMethod(object.data(), "assignVarProp");
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
- QCoreApplication::processEvents();
+ gc(engine);
QObject *rect = object->property("vp").value<QObject*>();
QObject *text = rect->findChild<QObject*>("textOne");
QObject *text2 = rect->findChild<QObject*>("textTwo");
@@ -5554,6 +5699,7 @@ void tst_qqmlecmascript::propertyVarReparentNullContext()
QCOMPARE(text2->property("textCanary").toInt(), 12);
// now construct an image which we will reparent.
QMetaObject::invokeMethod(text2, "constructQObject");
+ gc(engine);
QObject *image = text2->property("vp").value<QObject*>();
QPointer<QObject> imageGuard(image);
QVERIFY(!imageGuard.isNull());
@@ -5780,9 +5926,7 @@ void tst_qqmlecmascript::handleReferenceManagement()
gc(hrmEngine);
QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
object.reset();
- hrmEngine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(hrmEngine);
QCOMPARE(dtorCount, 3);
}
@@ -5799,9 +5943,7 @@ void tst_qqmlecmascript::handleReferenceManagement()
gc(hrmEngine);
QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
object.reset();
- hrmEngine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(hrmEngine);
QCOMPARE(dtorCount, 3);
}
@@ -7980,12 +8122,9 @@ void tst_qqmlecmascript::onDestructionViaGC()
v4->memoryManager->allocate<QV4::WeakReferenceSentinel>(weakRef.data(), &sentinelResult);
}
gc(engine);
-
+ QVERIFY2(weakRef->isNullOrUndefined(), "The weak value was not cleared");
QVERIFY2(mutatorResult, "We failed to re-assign the weak reference a new value during GC");
- QVERIFY2(!sentinelResult, "The weak value was cleared on first GC run");
- QVERIFY2(!weakRef->isNullOrUndefined(), "The weak value was cleared on first GC run");
- gc(engine);
- QVERIFY2(weakRef->isNullOrUndefined(), "The weak value was not cleared on second gc run");
+ QVERIFY2(sentinelResult, "The weak reference was not cleared properly");
}
struct EventProcessor : public QObject
@@ -8078,7 +8217,9 @@ void tst_qqmlecmascript::qqmldataDestroyed()
QVERIFY2(object, qPrintable(c.errorString()));
// now gc causing the collection of the dynamically constructed object.
engine.collectGarbage();
+ QTRY_VERIFY(gcDone(&engine));
engine.collectGarbage();
+ QTRY_VERIFY(gcDone(&engine));
// now process events to allow deletion (calling qqmldata::destroyed())
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
QCoreApplication::processEvents();
@@ -9389,6 +9530,32 @@ void tst_qqmlecmascript::qpropertyBindingHandlesUndefinedWithoutResetCorrectly()
QCOMPARE(root->property("value2").toInt(), 2);
}
+void tst_qqmlecmascript::qpropertyBindingRestoresObserverAfterReset()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restoreObserverAfterReset.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QTRY_COMPARE(o->property("height").toDouble(), 60.0);
+ QVERIFY(o->property("steps").toInt() > 3);
+}
+
+void tst_qqmlecmascript::qpropertyBindingObserverCorrectlyLinkedAfterReset()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("qpropertyResetCorrectlyLinked.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ std::unique_ptr<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("width"), 200);
+ auto item = qobject_cast<QQuickItem *>(o.get());
+ auto itemPriv = QQuickItemPrivate::get(item);
+ QBindingStorage *storage = qGetBindingStorage(itemPriv);
+ QPropertyBindingDataPointer ptr { storage->bindingData(&itemPriv->width) };
+ QCOMPARE(ptr.observerCount(), 1);
+}
+
void tst_qqmlecmascript::hugeRegexpQuantifiers()
{
QJSEngine engine;
@@ -10027,6 +10194,9 @@ public:
Q_INVOKABLE void triggerSignal() { emit fooMember2Emitted(&m_fooMember2); }
+ Q_INVOKABLE const FrozenFoo *getConst() { return createFloating(); }
+ Q_INVOKABLE FrozenFoo *getNonConst() { return createFloating(); }
+
FrozenFoo *fooMember() { return &m_fooMember; }
FrozenFoo *fooMember2() { return &m_fooMember2; }
@@ -10036,6 +10206,16 @@ signals:
private:
const FrozenFoo *fooMemberConst() const { return &m_fooMember; }
+ FrozenFoo *createFloating()
+ {
+ if (!m_floating) {
+ m_floating = new FrozenFoo;
+ m_floating->setObjectName(objectName());
+ }
+ return m_floating;
+ }
+
+ FrozenFoo *m_floating = nullptr;
FrozenFoo m_fooMember;
FrozenFoo m_fooMember2;
};
@@ -10058,6 +10238,17 @@ void tst_qqmlecmascript::frozenQObject()
QVERIFY(frozenObjects->property("caughtSignal").toBool());
QCOMPARE(frozenObjects->fooMember()->name(), QStringLiteral("Jane"));
QCOMPARE(frozenObjects->fooMember2()->name(), QStringLiteral("Jane"));
+
+ QQmlComponent component3(&engine, testFileUrl("frozenQObject3.qml"));
+ QScopedPointer<QObject> root3(component3.create());
+ QCOMPARE(root3->objectName(), QLatin1String("a/b"));
+ QVERIFY(root3->property("objConst").value<QObject *>());
+ QVERIFY(root3->property("objNonConst").value<QObject *>());
+
+ QTRY_VERIFY(root3->property("gcs").toInt() > 8);
+
+ QVERIFY(root3->property("objConst").value<QObject *>());
+ QVERIFY(root3->property("objNonConst").value<QObject *>());
}
struct ConstPointer : QObject
@@ -10373,6 +10564,80 @@ void tst_qqmlecmascript::doNotCrashOnReadOnlyBindable()
QCOMPARE(o->property("x").toInt(), 7);
}
+void tst_qqmlecmascript::resetGadget()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("resetGadget.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ auto resettableGadgetHolder = qobject_cast<ResettableGadgetHolder *>(o.get());
+ QVERIFY(resettableGadgetHolder);
+ QCOMPARE(resettableGadgetHolder->g().value(), 0);
+ resettableGadgetHolder->setProperty("trigger", QVariant::fromValue(true));
+ QCOMPARE(resettableGadgetHolder->g().value(), 42);
+}
+
+void tst_qqmlecmascript::assignListPropertyByIndexOnGadget()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFile("AssignListPropertyByIndexOnGadget.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ const auto &gadget = o->property("gadget").value<ListPropertyAssignment_Gadget>();
+ const auto *object = o->property("object").value<ListPropertyAssignment_Object *>();
+ QVERIFY(object);
+
+ QStringList expected{ "Completely new Element", "Element2", "Element3" };
+ QVariantList variants {
+ u"Completely new Element"_s,
+ u"foo"_s,
+ QVariant::fromValue<std::nullptr_t>(nullptr),
+ QVariant::fromValue<bool>(true)
+ };
+
+ QCOMPARE(gadget.gadgetStringList(), expected);
+ QCOMPARE(gadget.gadgetVariantList(), variants);
+ QCOMPARE(object->qobjectStringList(), expected);
+}
+
+void tst_qqmlecmascript::methodCallOnDerivedSingleton()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFile("methodCallOnDerivedSingleton.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ auto singleton = engine.singletonInstance<SingletonBase *>("Qt.test", "SingletonInheritanceTest");
+ QVERIFY(singleton);
+ QVERIFY(singleton->m_okay);
+}
+
+void tst_qqmlecmascript::proxyMetaObject()
+{
+ // Verify that TypeWithCustomMetaObject, that extends another type,
+ // thereby triggering a QQmlProxyMetaObject, is still proxied the
+ // QDynamicMetaObjectData::objectDestroyed callback.
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(R"(
+ import QtQuick
+ import QtQml
+ import Qt.test
+ Rectangle {
+ TypeWithCustomMetaObject {}
+ }
+ )", QUrl("testData"));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o);
+ QVERIFY(!MetaCallInterceptor::didGetObjectDestroyedCallback);
+ o.reset(nullptr);
+ QVERIFY(MetaCallInterceptor::didGetObjectDestroyedCallback);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmlengine/CMakeLists.txt b/tests/auto/qml/qqmlengine/CMakeLists.txt
index 0869fe7ccc..9745f31bdb 100644
--- a/tests/auto/qml/qqmlengine/CMakeLists.txt
+++ b/tests/auto/qml/qqmlengine/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlengine Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlengine LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -34,6 +40,7 @@ qt_add_qml_module(tst_qqmlengine_qml
SOURCES
"declarativelyregistered.h"
"declarativelyregistered.cpp"
+ "variantlistQJsonConversion.h"
RESOURCE_PREFIX
"/"
OUTPUT_DIRECTORY
diff --git a/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml b/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml
new file mode 100644
index 0000000000..596ab10ee7
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Item {
+ visible: false
+
+ property int test: 1
+
+ Component {
+ id: comp
+ Item {
+ width: { width = test * 100 }
+ }
+ }
+
+ Loader {
+ sourceComponent: comp
+ width: 100
+ }
+}
diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/Main.qml b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/Main.qml
new file mode 100644
index 0000000000..3be706ee29
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/Main.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string s: SingletonA.name
+}
diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonA.qml b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonA.qml
new file mode 100644
index 0000000000..dc3bfd23fd
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonA.qml
@@ -0,0 +1,11 @@
+pragma Singleton
+import QtQuick
+
+Item {
+ readonly property string name: "SingletonA"
+
+ readonly property TestItem itemA: TestItem{}
+
+ property TestItem crossRef: SingletonB.itemB
+ property int testItemInt: crossRef.i
+}
diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonB.qml b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonB.qml
new file mode 100644
index 0000000000..7f0335ee89
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/SingletonB.qml
@@ -0,0 +1,11 @@
+pragma Singleton
+import QtQuick
+
+Item {
+ readonly property string name: "SingletonB"
+
+ readonly property TestItem itemB: TestItem{}
+
+ property TestItem crossRef: SingletonA.itemA
+ property int testItemInt: crossRef.i
+}
diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/TestItem.qml b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/TestItem.qml
new file mode 100644
index 0000000000..81d931f14c
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/TestItem.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int i: 3
+}
diff --git a/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/qmldir b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/qmldir
new file mode 100644
index 0000000000..8bf691d10d
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/crossReferencingSingletonsDeletion/Module/qmldir
@@ -0,0 +1,3 @@
+module Module
+singleton SingletonA 1.0 SingletonA.qml
+singleton SingletonB 1.0 SingletonB.qml
diff --git a/tests/auto/qml/qqmlengine/data/markCurrentFunctionAsTranslationBinding.qml b/tests/auto/qml/qqmlengine/data/markCurrentFunctionAsTranslationBinding.qml
new file mode 100644
index 0000000000..a16e9c483f
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/markCurrentFunctionAsTranslationBinding.qml
@@ -0,0 +1,7 @@
+import QtQml
+import i18ntest
+
+QtObject {
+ property I18nAware aware: I18nAware {}
+ property string result: aware.text
+}
diff --git a/tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs b/tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs
index fd1080fccb..8f5bdde82a 100644
--- a/tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs
+++ b/tests/auto/qml/qqmlengine/data/nativeModuleImport.mjs
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import { name } from "info.mjs";
diff --git a/tests/auto/qml/qqmlengine/data/nativeModuleImport.qml b/tests/auto/qml/qqmlengine/data/nativeModuleImport.qml
index ae51210944..ffcb0e42ce 100644
--- a/tests/auto/qml/qqmlengine/data/nativeModuleImport.qml
+++ b/tests/auto/qml/qqmlengine/data/nativeModuleImport.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
diff --git a/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml b/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml
new file mode 100644
index 0000000000..fd0820a3c5
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/data/variantListQJsonConversion.qml
@@ -0,0 +1,18 @@
+import QtQuick
+import OnlyDeclarative
+
+Item {
+
+ MiscUtils {
+ id: miscUtils
+ }
+
+ Component.onCompleted: {
+ const varlist = miscUtils.createVariantList();
+ const obj = { test: varlist };
+ const listProperty = miscUtils.createQmlListProperty();
+ miscUtils.logArray(varlist);
+ miscUtils.logObject(obj);
+ miscUtils.logArray(listProperty);
+ }
+}
diff --git a/tests/auto/qml/qqmlengine/declarativelyregistered.cpp b/tests/auto/qml/qqmlengine/declarativelyregistered.cpp
index 9893fa0311..4a469c4795 100644
--- a/tests/auto/qml/qqmlengine/declarativelyregistered.cpp
+++ b/tests/auto/qml/qqmlengine/declarativelyregistered.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "declarativelyregistered.h"
diff --git a/tests/auto/qml/qqmlengine/declarativelyregistered.h b/tests/auto/qml/qqmlengine/declarativelyregistered.h
index 98f7250d76..edb783e3aa 100644
--- a/tests/auto/qml/qqmlengine/declarativelyregistered.h
+++ b/tests/auto/qml/qqmlengine/declarativelyregistered.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DECLARATIVELYREGISTERED_H
#define DECLARATIVELYREGISTERED_H
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index 895513d822..3c25d29dfb 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QQmlEngine>
#include <QQmlContext>
@@ -36,8 +36,10 @@ public:
private slots:
void initTestCase() override;
void rootContext();
+#if QT_CONFIG(qml_network)
void networkAccessManager();
void synchronousNetworkAccessManager();
+#endif
void baseUrl();
void contextForObject();
void offlineStoragePath();
@@ -67,6 +69,7 @@ private slots:
void cachedGetterLookup_qtbug_75335();
void createComponentOnSingletonDestruction();
void uiLanguage();
+ void markCurrentFunctionAsTranslationBinding();
void executeRuntimeFunction();
void captureQProperty();
void listWrapperAsListReference();
@@ -77,6 +80,9 @@ private slots:
void qtNamespaceInQtObject();
void nativeModuleImport();
void lockedRootObject();
+ void crossReferencingSingletonsDeletion();
+ void bindingInstallUseAfterFree();
+ void variantListQJsonConversion();
public slots:
QObject *createAQObjectForOwnershipTest ()
@@ -149,6 +155,7 @@ void tst_qqmlengine::rootContext()
QVERIFY(!engine.rootContext()->parentContext());
}
+#if QT_CONFIG(qml_network)
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
{
public:
@@ -164,21 +171,19 @@ public:
void tst_qqmlengine::networkAccessManager()
{
- QQmlEngine *engine = new QQmlEngine;
+ std::unique_ptr<QQmlEngine> engine = std::make_unique<QQmlEngine>();
// Test QQmlEngine created manager
QPointer<QNetworkAccessManager> manager = engine->networkAccessManager();
QVERIFY(manager != nullptr);
- delete engine;
// Test factory created manager
- engine = new QQmlEngine;
+ engine.reset(new QQmlEngine);
NetworkAccessManagerFactory factory;
engine->setNetworkAccessManagerFactory(&factory);
QCOMPARE(engine->networkAccessManagerFactory(), &factory);
QNetworkAccessManager *engineNam = engine->networkAccessManager(); // calls NetworkAccessManagerFactory::create()
QCOMPARE(engineNam, factory.manager);
- delete engine;
}
class ImmediateReply : public QNetworkReply {
@@ -226,7 +231,7 @@ void tst_qqmlengine::synchronousNetworkAccessManager()
// reply is finished, so should not be in loading state.
QVERIFY(!c.isLoading());
}
-
+#endif
void tst_qqmlengine::baseUrl()
{
@@ -267,7 +272,7 @@ void tst_qqmlengine::baseUrl()
void tst_qqmlengine::contextForObject()
{
- QQmlEngine *engine = new QQmlEngine;
+ std::unique_ptr<QQmlEngine> engine = std::make_unique<QQmlEngine>();
// Test null-object
QVERIFY(!QQmlEngine::contextForObject(nullptr));
@@ -294,7 +299,7 @@ void tst_qqmlengine::contextForObject()
QCOMPARE(QQmlEngine::contextForObject(&object), engine->rootContext());
// Delete context
- delete engine; engine = nullptr;
+ engine.reset();
QVERIFY(!QQmlEngine::contextForObject(&object));
}
@@ -372,10 +377,9 @@ void tst_qqmlengine::clearComponentCache()
// Test "test" property
{
QQmlComponent component(&engine, fileUrl);
- QObject *obj = component.create();
- QVERIFY(obj != nullptr);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj.get() != nullptr);
QCOMPARE(obj->property("test").toInt(), 10);
- delete obj;
}
// Modify qml file
@@ -395,10 +399,9 @@ void tst_qqmlengine::clearComponentCache()
// Test cache hit
{
QQmlComponent component(&engine, fileUrl);
- QObject *obj = component.create();
- QVERIFY(obj != nullptr);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj.get() != nullptr);
QCOMPARE(obj->property("test").toInt(), 10);
- delete obj;
}
// Clear cache
@@ -407,10 +410,9 @@ void tst_qqmlengine::clearComponentCache()
// Test cache refresh
{
QQmlComponent component(&engine, fileUrl);
- QObject *obj = component.create();
- QVERIFY(obj != nullptr);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj.get() != nullptr);
QCOMPARE(obj->property("test").toInt(), 11);
- delete obj;
}
// Regular Synchronous loading will leave us with an event posted
@@ -437,7 +439,7 @@ public:
// There might be JS function objects around that hold a last ref to the compilation unit that's
// keeping the type compilation data (CompilationUnit) around. Let's collect them as well so that
// trim works well.
- engine->collectGarbage();
+ gc(*engine);
engine->trimComponentCache();
}
@@ -496,22 +498,25 @@ void tst_qqmlengine::trimComponentCache_data()
// empty apart from their inherited elements, and those that define new properties.
// For each there are five types of composition: extension, aggregation,
// aggregation via component, property and object-created-via-transient-component.
- foreach (const QString &test, (QStringList() << "EmptyComponent"
- << "VMEComponent"
- << "EmptyExtendEmptyComponent"
- << "VMEExtendEmptyComponent"
- << "EmptyExtendVMEComponent"
- << "VMEExtendVMEComponent"
- << "EmptyAggregateEmptyComponent"
- << "VMEAggregateEmptyComponent"
- << "EmptyAggregateVMEComponent"
- << "VMEAggregateVMEComponent"
- << "EmptyPropertyEmptyComponent"
- << "VMEPropertyEmptyComponent"
- << "EmptyPropertyVMEComponent"
- << "VMEPropertyVMEComponent"
- << "VMETransientEmptyComponent"
- << "VMETransientVMEComponent")) {
+ const QStringList components = {
+ "EmptyComponent",
+ "VMEComponent",
+ "EmptyExtendEmptyComponent",
+ "VMEExtendEmptyComponent",
+ "EmptyExtendVMEComponent",
+ "VMEExtendVMEComponent",
+ "EmptyAggregateEmptyComponent",
+ "VMEAggregateEmptyComponent",
+ "EmptyAggregateVMEComponent",
+ "VMEAggregateVMEComponent",
+ "EmptyPropertyEmptyComponent",
+ "VMEPropertyEmptyComponent",
+ "EmptyPropertyVMEComponent",
+ "VMEPropertyVMEComponent",
+ "VMETransientEmptyComponent",
+ "VMETransientVMEComponent",
+ };
+ for (const QString &test : components) {
// For these cases, we first test that the component instance keeps the components
// referenced, and then that the instantiated object keeps the components referenced
for (int i = 1; i <= 2; ++i) {
@@ -903,7 +908,7 @@ void tst_qqmlengine::qtqmlModule()
QFETCH(QString, expectedError);
QFETCH(QStringList, expectedWarnings);
- foreach (const QString &w, expectedWarnings)
+ for (const QString &w : std::as_const(expectedWarnings))
QTest::ignoreMessage(QtWarningMsg, qPrintable(w));
QQmlEngine e;
@@ -1272,9 +1277,9 @@ void tst_qqmlengine::singletonInstance()
{
// deleted object
- auto dayfly = new Dayfly{};
- auto id = qmlRegisterSingletonInstance("Vanity", 1, 0, "Dayfly", dayfly);
- delete dayfly;
+ auto dayfly = std::make_unique<Dayfly>();
+ auto id = qmlRegisterSingletonInstance("Vanity", 1, 0, "Dayfly", dayfly.get());
+ dayfly.reset();
QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: The registered singleton has already been deleted. Ensure that it outlives the engine.");
QObject *instance = engine.singletonInstance<QObject*>(id);
QVERIFY(!instance);
@@ -1378,6 +1383,35 @@ void tst_qqmlengine::uiLanguage()
}
}
+class I18nAwareClass : public QObject {
+ Q_OBJECT
+ QML_NAMED_ELEMENT(I18nAware)
+
+ Q_PROPERTY(QString text READ text NOTIFY textChanged)
+signals:
+ void textChanged();
+public:
+ int counter = 0;
+
+ QString text()
+ {
+ if (auto engine = qmlEngine(this))
+ engine->markCurrentFunctionAsTranslationBinding();
+ return QLatin1String("Hello, %1").arg(QString::number(counter++));
+ }
+};
+
+void tst_qqmlengine::markCurrentFunctionAsTranslationBinding()
+{
+ QQmlEngine engine;
+ qmlRegisterTypesAndRevisions<I18nAwareClass>("i18ntest", 1);
+ QQmlComponent comp(&engine, testFileUrl("markCurrentFunctionAsTranslationBinding.qml"));
+ std::unique_ptr<QObject> root { comp.create() };
+ QCOMPARE(root->property("result"), "Hello, 0");
+ engine.retranslate();
+ QCOMPARE(root->property("result"), "Hello, 1");
+}
+
void tst_qqmlengine::executeRuntimeFunction()
{
QQmlEngine engine;
@@ -1683,6 +1717,42 @@ void tst_qqmlengine::lockedRootObject()
QCOMPARE(o->property("defineProperty2").toBool(), false);
}
+void tst_qqmlengine::crossReferencingSingletonsDeletion()
+{
+ QQmlEngine engine;
+ engine.addImportPath(testFileUrl("crossReferencingSingletonsDeletion").url());
+ QQmlComponent c(&engine, testFileUrl("crossReferencingSingletonsDeletion/Module/Main.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ std::unique_ptr<QObject> o{ c.create() };
+ QVERIFY(o);
+ QCOMPARE(o->property("s").toString(), "SingletonA");
+}
+
+void tst_qqmlengine::bindingInstallUseAfterFree()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("bindingInstallUseAfterFree.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ std::unique_ptr<QObject> o{ c.create() };
+ QVERIFY(o);
+}
+
+void tst_qqmlengine::variantListQJsonConversion()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("variantListQJsonConversion.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(QtMsgType::QtDebugMsg, R"(["cpp","variant","list"])");
+ QTest::ignoreMessage(QtMsgType::QtDebugMsg, R"({"test":["cpp","variant","list"]})");
+ QTest::ignoreMessage(QtMsgType::QtDebugMsg,
+ R"([{"objectName":"o0"},{"objectName":"o1"},{"objectName":"o2"}])");
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+}
+
QTEST_MAIN(tst_qqmlengine)
#include "tst_qqmlengine.moc"
diff --git a/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h b/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h
new file mode 100644
index 0000000000..edf2174a18
--- /dev/null
+++ b/tests/auto/qml/qqmlengine/variantlistQJsonConversion.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef VARIANTLIST_QJSON_CONVERSION_HPP
+#define VARIANTLIST_QJSON_CONVERSION_HPP
+
+#include "qqmlintegration.h"
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QObject>
+#include <QJsonDocument>
+#include <QDebug>
+#include <private/qjsvalue_p.h>
+#include <private/qqmllistwrapper_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4jsonobject_p.h>
+
+class MiscUtils : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ Q_INVOKABLE QVariantList createVariantList() const
+ {
+ return { QString("cpp"), QString("variant"), QString("list") };
+ }
+
+ Q_INVOKABLE QQmlListProperty<QObject> createQmlListProperty()
+ {
+ QV4::ExecutionEngine engine(qmlEngine(this));
+ static QObject objects[] = { QObject{}, QObject{}, QObject{} };
+ objects[0].setObjectName("o0");
+ objects[1].setObjectName("o1");
+ objects[2].setObjectName("o2");
+ static QList<QObject *> list{ &objects[0], &objects[1], &objects[2] };
+ return QQmlListProperty<QObject>(this, &list);
+ }
+
+ Q_INVOKABLE void logArray(const QJsonArray &arr) const
+ {
+ const auto str = QString(QJsonDocument(arr).toJson(QJsonDocument::Compact));
+ qDebug().noquote() << str;
+ }
+
+ Q_INVOKABLE void logObject(const QJsonObject &obj) const
+ {
+ const auto str = QString(QJsonDocument(obj).toJson(QJsonDocument::Compact));
+ qDebug().noquote() << str;
+ }
+};
+
+#endif // VARIANTLIST_QJSON_CONVERSION_HPP
diff --git a/tests/auto/qml/qqmlenginecleanup/CMakeLists.txt b/tests/auto/qml/qqmlenginecleanup/CMakeLists.txt
index d91fc98f8a..87b0874774 100644
--- a/tests/auto/qml/qqmlenginecleanup/CMakeLists.txt
+++ b/tests/auto/qml/qqmlenginecleanup/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlenginecleanup Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlenginecleanup LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml b/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml
index 6dfe0d52e8..083e8f2e7c 100644
--- a/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml
+++ b/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/ModuleType.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.12
diff --git a/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp b/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp
index c53f9e674b..52ab367d10 100644
--- a/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp
+++ b/tests/auto/qml/qqmlenginecleanup/CustomModuleImport/moduleplugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/qml/qqmlenginecleanup/data/TestType.qml b/tests/auto/qml/qqmlenginecleanup/data/TestType.qml
index 9f98ec971f..0699ac894f 100644
--- a/tests/auto/qml/qqmlenginecleanup/data/TestType.qml
+++ b/tests/auto/qml/qqmlenginecleanup/data/TestType.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.0
QtObject{property int notJustAStandardQtObject: 10 }
diff --git a/tests/auto/qml/qqmlenginecleanup/data/testFile1.qml b/tests/auto/qml/qqmlenginecleanup/data/testFile1.qml
index 5298dea961..233684633f 100644
--- a/tests/auto/qml/qqmlenginecleanup/data/testFile1.qml
+++ b/tests/auto/qml/qqmlenginecleanup/data/testFile1.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlenginecleanup/data/testFile2.qml b/tests/auto/qml/qqmlenginecleanup/data/testFile2.qml
index 0bd2c54e16..02d231d375 100644
--- a/tests/auto/qml/qqmlenginecleanup/data/testFile2.qml
+++ b/tests/auto/qml/qqmlenginecleanup/data/testFile2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlenginecleanup/data/testFile3.qml b/tests/auto/qml/qqmlenginecleanup/data/testFile3.qml
index be95e2ff74..10e2b2888a 100644
--- a/tests/auto/qml/qqmlenginecleanup/data/testFile3.qml
+++ b/tests/auto/qml/qqmlenginecleanup/data/testFile3.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlenginecleanup/data/types.qml b/tests/auto/qml/qqmlenginecleanup/data/types.qml
index f726a37404..4de1e6e43d 100644
--- a/tests/auto/qml/qqmlenginecleanup/data/types.qml
+++ b/tests/auto/qml/qqmlenginecleanup/data/types.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.0
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp
index c9049c7ca4..04e8389c87 100644
--- a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp
+++ b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QObject>
#include <QtQml/qqml.h>
@@ -47,8 +47,8 @@ public:
void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations()
{
//Test for preventing memory leaks is in tests/manual/qmltypememory
- QQmlEngine* engine;
- CleanlyLoadingComponent* component;
+ std::unique_ptr<QQmlEngine> engine;
+ std::unique_ptr<CleanlyLoadingComponent> component;
QUrl testFile = testFileUrl("types.qml");
const auto qmlTypeForTestType = []() {
@@ -60,12 +60,12 @@ void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations()
qmlRegisterType<QObject>("Test", 2, 0, "TestTypeCpp");
QVERIFY(qmlTypeForTestType().isValid());
- engine = new QQmlEngine;
- component = new CleanlyLoadingComponent(engine, testFile);
+ engine = std::make_unique<QQmlEngine>();
+ component = std::make_unique<CleanlyLoadingComponent>(engine.get(), testFile);
QVERIFY(component->isReady());
- delete component;
- delete engine;
+ component.reset();
+ engine.reset();
{
auto cppType = qmlTypeForTestType();
@@ -81,24 +81,21 @@ void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations()
//2nd run verifies that types can reload after a qmlClearTypeRegistrations
qmlRegisterType<QObject>("Test", 2, 0, "TestTypeCpp");
QVERIFY(qmlTypeForTestType().isValid());
- engine = new QQmlEngine;
- component = new CleanlyLoadingComponent(engine, testFile);
+ engine = std::make_unique<QQmlEngine>();
+ component = std::make_unique<CleanlyLoadingComponent>(engine.get(), testFile);
QVERIFY(component->isReady());
- delete component;
- delete engine;
+ component.reset();
+ engine.reset();
qmlClearTypeRegistrations();
QVERIFY(!qmlTypeForTestType().isValid());
//3nd run verifies that TestTypeCpp is no longer registered
- engine = new QQmlEngine;
- component = new CleanlyLoadingComponent(engine, testFile);
+ engine = std::make_unique<QQmlEngine>();
+ component = std::make_unique<CleanlyLoadingComponent>(engine.get(), testFile);
QVERIFY(component->isError());
QCOMPARE(component->errorString(),
testFile.toString() +":8 module \"Test\" is not installed\n");
-
- delete component;
- delete engine;
}
static void cleanState(QQmlEngine **e)
diff --git a/tests/auto/qml/qqmlerror/CMakeLists.txt b/tests/auto/qml/qqmlerror/CMakeLists.txt
index be85347051..11b084e88d 100644
--- a/tests/auto/qml/qqmlerror/CMakeLists.txt
+++ b/tests/auto/qml/qqmlerror/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlerror Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlerror LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
index b0e8846e59..d4d483fb27 100644
--- a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
+++ b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlError>
diff --git a/tests/auto/qml/qqmlexpression/CMakeLists.txt b/tests/auto/qml/qqmlexpression/CMakeLists.txt
index 79dccd484d..6adb01b3b1 100644
--- a/tests/auto/qml/qqmlexpression/CMakeLists.txt
+++ b/tests/auto/qml/qqmlexpression/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlexpression Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlexpression LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
index 71c186f3e4..59023391cd 100644
--- a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
+++ b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
@@ -1,11 +1,12 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
-#include <QtQml/qqmlengine.h>
-#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlexpression.h>
+#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlscriptstring.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -20,6 +21,7 @@ private slots:
void syntaxError();
void exception();
void expressionFromDataComponent();
+ void emptyScriptString();
};
class TestObject : public QObject
@@ -121,6 +123,36 @@ void tst_qqmlexpression::expressionFromDataComponent()
QCOMPARE(result.toString(), QStringLiteral("success"));
}
+void tst_qqmlexpression::emptyScriptString()
+{
+ QQmlEngine engine;
+ QQmlContext *context = engine.rootContext();
+ QVERIFY(context);
+ QVERIFY(context->isValid());
+
+ QQmlScriptString empty;
+ QVERIFY(empty.isEmpty());
+
+ QQmlExpression expression(empty, context, this);
+ QCOMPARE(expression.context(), context);
+ QCOMPARE(expression.scopeObject(), this);
+ QCOMPARE(expression.expression(), QString());
+
+ const QVariant result = expression.evaluate();
+ QVERIFY(!result.isValid());
+
+ QQmlComponent c(&engine, testFileUrl("scriptString.qml"));
+ std::unique_ptr<QObject> root { c.create() };
+ TestObject *testObj = qobject_cast<TestObject*>(root.get());
+ QVERIFY(testObj != nullptr);
+
+ QQmlScriptString script = testObj->scriptString();
+ QVERIFY(!script.isEmpty());
+
+ // verify that comparing against an empty script string does not crash
+ QVERIFY(script != empty);
+}
+
QTEST_MAIN(tst_qqmlexpression)
#include "tst_qqmlexpression.moc"
diff --git a/tests/auto/qml/qqmlextensionplugin/CMakeLists.txt b/tests/auto/qml/qqmlextensionplugin/CMakeLists.txt
index de108af085..4a32e3a0e4 100644
--- a/tests/auto/qml/qqmlextensionplugin/CMakeLists.txt
+++ b/tests/auto/qml/qqmlextensionplugin/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlextensionplugin Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlextensionplugin LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmlextensionplugin
SOURCES
tst_qqmlextensionplugin.cpp
diff --git a/tests/auto/qml/qqmlextensionplugin/data/dummy.qml b/tests/auto/qml/qqmlextensionplugin/data/dummy.qml
index f8169bd6aa..e555405535 100644
--- a/tests/auto/qml/qqmlextensionplugin/data/dummy.qml
+++ b/tests/auto/qml/qqmlextensionplugin/data/dummy.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
index b8e0b01f6d..5f299f7b91 100644
--- a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
+++ b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtTest>
@@ -75,7 +75,7 @@ void tst_qqmlextensionplugin::iidCheck_data()
files = removeDuplicates(std::move(files));
QTest::addColumn<QString>("filePath");
- foreach (const QString &file, files) {
+ for (const QString &file: std::as_const(files)) {
QFileInfo fileInfo(file);
QTest::newRow(fileInfo.baseName().toLatin1().data()) << fileInfo.absoluteFilePath();
}
diff --git a/tests/auto/qml/qqmlfile/CMakeLists.txt b/tests/auto/qml/qqmlfile/CMakeLists.txt
index c573b53b1a..d8e23f410c 100644
--- a/tests/auto/qml/qqmlfile/CMakeLists.txt
+++ b/tests/auto/qml/qqmlfile/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlfile Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlfile LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmlfile
SOURCES
tst_qqmlfile.cpp
diff --git a/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
index 3f75a14bf6..3ce87ce048 100644
--- a/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
+++ b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore>
#include <QtTest>
diff --git a/tests/auto/qml/qqmlfileselector/CMakeLists.txt b/tests/auto/qml/qqmlfileselector/CMakeLists.txt
index d169ffc607..b3dec0ff3d 100644
--- a/tests/auto/qml/qqmlfileselector/CMakeLists.txt
+++ b/tests/auto/qml/qqmlfileselector/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlfileselector Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlfileselector LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml
index d6dd2c9b90..4e09798a84 100644
--- a/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/main.qml
@@ -1,10 +1,8 @@
import QtQuick
import qmldirtest
-Window {
- width: 640
- height: 480
- visible: true
+Item {
+ objectName: Name.name
property color color: mybutton.color
MyButton {
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/Name.js
new file mode 100644
index 0000000000..91ca4f129d
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+linux/Name.js
@@ -0,0 +1 @@
+var name = "linux"
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/Name.js
new file mode 100644
index 0000000000..12e5058285
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/+macos/Name.js
@@ -0,0 +1 @@
+var name = "macos"
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/Name.js
new file mode 100644
index 0000000000..916a232eb4
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qml/Name.js
@@ -0,0 +1 @@
+var name = "base"
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir
index ac68d9097d..a2efdbf27d 100644
--- a/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest/qmldir
@@ -2,4 +2,7 @@ module qmldirtest
MyButton 1.0 qml/MyButton.qml
MyButton 1.0 qml/+linux/MyButton.qml
MyButton 1.0 qml/+macos/MyButton.qml
+Name 1.0 qml/Name.js
+Name 1.0 qml/+linux/Name.js
+Name 1.0 qml/+macos/Name.js
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/MyButton.qml
new file mode 100644
index 0000000000..5bf632c48d
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/MyButton.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 50
+ color: "yellow"
+}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/Name.js
new file mode 100644
index 0000000000..8591795d37
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+bar/Name.js
@@ -0,0 +1 @@
+var name = "bar"
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/MyButton.qml
new file mode 100644
index 0000000000..cc6eb967da
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/MyButton.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 50
+ color: "blue"
+}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/Name.js
new file mode 100644
index 0000000000..b224ed15ec
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/+foo/Name.js
@@ -0,0 +1 @@
+var name = "foo"
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/MyButton.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/MyButton.qml
new file mode 100644
index 0000000000..32db428c4f
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/MyButton.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 50
+ color: "green"
+}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/Name.js b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/Name.js
new file mode 100644
index 0000000000..916a232eb4
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/Name.js
@@ -0,0 +1 @@
+var name = "base"
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/main.qml b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/main.qml
new file mode 100644
index 0000000000..5fb8afc660
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/main.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ objectName: Name.name
+ property color color: mybutton.color
+ MyButton {
+ id: mybutton
+ }
+}
diff --git a/tests/auto/qml/qqmlfileselector/data/qmldirtest2/qmldir b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/qmldir
new file mode 100644
index 0000000000..92fefb9806
--- /dev/null
+++ b/tests/auto/qml/qqmlfileselector/data/qmldirtest2/qmldir
@@ -0,0 +1,5 @@
+module qmldirtest2
+MyButton 1.0 +foo/MyButton.qml
+MyButton 1.0 MyButton.qml
+MyButton 1.0 +bar/MyButton.qml
+Name 1.0 Name.js
diff --git a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
index 46df20378c..98774ffe64 100644
--- a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
+++ b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
@@ -32,11 +32,9 @@ void tst_qqmlfileselector::basicTest()
selector.setExtraSelectors(QStringList() << "basic");
QQmlComponent component(&engine, testFileUrl("basicTest.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
QCOMPARE(object->property("value").toString(), QString("selected"));
-
- delete object;
}
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
@@ -72,19 +70,38 @@ void tst_qqmlfileselector::applicationEngineTest()
void tst_qqmlfileselector::qmldirCompatibility()
{
- QQmlApplicationEngine engine;
- engine.addImportPath(dataDirectory());
- engine.load(testFileUrl("qmldirtest/main.qml"));
- QVERIFY(!engine.rootObjects().isEmpty());
- QObject *object = engine.rootObjects().at(0);
- auto color = object->property("color").value<QColor>();
+ {
+ // No error for multiple files with different selectors, and the matching one is chosen
+ // for +macos and +linux selectors.
+ QQmlApplicationEngine engine;
+ engine.addImportPath(dataDirectory());
+ engine.load(testFileUrl("qmldirtest/main.qml"));
+ QVERIFY(!engine.rootObjects().isEmpty());
+ QObject *object = engine.rootObjects().at(0);
+ auto color = object->property("color").value<QColor>();
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
- QCOMPARE(color, QColorConstants::Svg::blue);
+ QCOMPARE(object->objectName(), "linux");
+ QCOMPARE(color, QColorConstants::Svg::blue);
#elif defined(Q_OS_DARWIN)
- QCOMPARE(color, QColorConstants::Svg::yellow);
+ QCOMPARE(object->objectName(), "macos");
+ QCOMPARE(color, QColorConstants::Svg::yellow);
#else
- QCOMPARE(color, QColorConstants::Svg::green);
+ QCOMPARE(object->objectName(), "base");
+ QCOMPARE(color, QColorConstants::Svg::green);
#endif
+ }
+
+ {
+ // If nothing matches, the _base_ file is chosen, not the first or the last one.
+ // This also holds when using the implicit import.
+ QQmlApplicationEngine engine;
+ engine.addImportPath(dataDirectory());
+ engine.load(testFileUrl("qmldirtest2/main.qml"));
+ QVERIFY(!engine.rootObjects().isEmpty());
+ QObject *object = engine.rootObjects().at(0);
+ QCOMPARE(object->property("color").value<QColor>(), QColorConstants::Svg::green);
+ QCOMPARE(object->objectName(), "base");
+ }
}
QTEST_MAIN(tst_qqmlfileselector)
diff --git a/tests/auto/qml/qqmlglobal/CMakeLists.txt b/tests/auto/qml/qqmlglobal/CMakeLists.txt
index ff57005dc3..ce600c7607 100644
--- a/tests/auto/qml/qqmlglobal/CMakeLists.txt
+++ b/tests/auto/qml/qqmlglobal/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlglobal Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlglobal LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmlglobal
SOURCES
tst_qqmlglobal.cpp
diff --git a/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp b/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp
index 81d61976a3..884ea180a9 100644
--- a/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp
+++ b/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <qqml.h>
diff --git a/tests/auto/qml/qqmlimport/CMakeLists.txt b/tests/auto/qml/qqmlimport/CMakeLists.txt
index 32aeec0966..803234787b 100644
--- a/tests/auto/qml/qqmlimport/CMakeLists.txt
+++ b/tests/auto/qml/qqmlimport/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlimport Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlimport LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -44,6 +50,14 @@ qt_internal_add_resource(tst_qqmlimport "preferred"
"Preferred.qml"
)
+qt_internal_add_resource(tst_qqmlimport "preferred2"
+ PREFIX
+ "/qqmlimport/ModuleWithPrefer2/"
+ FILES
+ "Preferred.qml"
+ "qmldir"
+)
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml b/tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml
index 943ce3309f..960b9b474b 100644
--- a/tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml
+++ b/tests/auto/qml/qqmlimport/MyPluginSupported/MyItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml b/tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml
index 943ce3309f..960b9b474b 100644
--- a/tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml
+++ b/tests/auto/qml/qqmlimport/MyPluginUnsupported/MyItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/Preferred.qml b/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/Preferred.qml
new file mode 100644
index 0000000000..a4af255b56
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/Preferred.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ objectName: "wrong"
+}
diff --git a/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/qmldir b/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/qmldir
new file mode 100644
index 0000000000..6b04c0eb5f
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/ModuleWithPrefer2/qmldir
@@ -0,0 +1,3 @@
+module ModuleWithPrefer2
+prefer :/qqmlimport/ModuleWithPrefer2/
+Preferred 1.0 Preferred.qml
diff --git a/tests/auto/qml/qqmlimport/data/MyModuleName/Font.js b/tests/auto/qml/qqmlimport/data/MyModuleName/Font.js
new file mode 100644
index 0000000000..7036c0a739
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/MyModuleName/Font.js
@@ -0,0 +1,4 @@
+.pragma library
+.import QtQuick as QtQuick
+
+var exampleVar = 12;
diff --git a/tests/auto/qml/qqmlimport/data/MyModuleName/qmldir b/tests/auto/qml/qqmlimport/data/MyModuleName/qmldir
new file mode 100644
index 0000000000..43add34163
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/MyModuleName/qmldir
@@ -0,0 +1,2 @@
+module MyModuleName
+Font 0.1 Font.js
diff --git a/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml b/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml
new file mode 100644
index 0000000000..83dbd566f3
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml
@@ -0,0 +1,6 @@
+import QtQml
+import 'file://./MyModuleName' as MyModuleName
+
+QtObject {
+ objectName: MyModuleName.Font.exampleVar
+}
diff --git a/tests/auto/qml/qqmlimport/data/noimport/Main.qml b/tests/auto/qml/qqmlimport/data/noimport/Main.qml
new file mode 100644
index 0000000000..d473b43d7b
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/noimport/Main.qml
@@ -0,0 +1,5 @@
+pragma Strict
+
+TheThing {
+ width: 640
+}
diff --git a/tests/auto/qml/qqmlimport/data/noimport/qmldir b/tests/auto/qml/qqmlimport/data/noimport/qmldir
new file mode 100644
index 0000000000..3034edba51
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/noimport/qmldir
@@ -0,0 +1,3 @@
+module noimport
+Main 1.0 Main.qml
+
diff --git a/tests/auto/qml/qqmlimport/data/prefer2.qml b/tests/auto/qml/qqmlimport/data/prefer2.qml
new file mode 100644
index 0000000000..c0727dc13d
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/prefer2.qml
@@ -0,0 +1,3 @@
+import ModuleWithPrefer2
+
+Pickles {}
diff --git a/tests/auto/qml/qqmlimport/data/qualifiedScriptImport.qml b/tests/auto/qml/qqmlimport/data/qualifiedScriptImport.qml
new file mode 100644
index 0000000000..de4b02afbb
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/qualifiedScriptImport.qml
@@ -0,0 +1,8 @@
+import QtQuick
+import 'MyModuleName' as MyModuleName
+
+Item {
+ property var a: MyModuleName.Font.exampleVar
+ property var b: Font.SmallCaps
+ property var c: Font.exampleVar
+}
diff --git a/tests/auto/qml/qqmlimport/data/testfile_supported.qml b/tests/auto/qml/qqmlimport/data/testfile_supported.qml
index eb4995bacf..dc20a91d06 100644
--- a/tests/auto/qml/qqmlimport/data/testfile_supported.qml
+++ b/tests/auto/qml/qqmlimport/data/testfile_supported.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import MyPluginSupported 1.0
diff --git a/tests/auto/qml/qqmlimport/data/testfile_unsupported.qml b/tests/auto/qml/qqmlimport/data/testfile_unsupported.qml
index 81156f1624..38ad7ad258 100644
--- a/tests/auto/qml/qqmlimport/data/testfile_unsupported.qml
+++ b/tests/auto/qml/qqmlimport/data/testfile_unsupported.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import MyPluginUnsupported 1.0
diff --git a/tests/auto/qml/qqmlimport/qmldir b/tests/auto/qml/qqmlimport/qmldir
new file mode 100644
index 0000000000..ca29b16cfb
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/qmldir
@@ -0,0 +1,2 @@
+module ModuleWithPrefer2
+Pickles 1.0 Preferred.qml
diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
index 38970a3b99..ff1513d0d6 100644
--- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
+++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
@@ -1,23 +1,49 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <private/qmlutils_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlimport_p.h>
-#include <QtCore/qscopeguard.h>
-#include <QtTest/QtTest>
-#include <QQmlApplicationEngine>
-#include <QQmlAbstractUrlInterceptor>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
-#include <private/qqmlimport_p.h>
-#include <private/qqmlengine_p.h>
-#include <QtQuickTestUtils/private/qmlutils_p.h>
-#include <QQmlComponent>
+
+#include <QtTest/qsignalspy.h>
+
+#include <QtQml/qqmlabstracturlinterceptor.h>
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlmoduleregistration.h>
+
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qlibraryinfo.h>
+
+class TheThing : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int width MEMBER m_width FINAL)
+
+public:
+ int m_width = 12;
+};
+
+void qml_register_types_noimport()
+{
+ qmlRegisterTypesAndRevisions<TheThing>("noimport", 1);
+ qmlRegisterModule("noimport", 1, 0);
+}
class tst_QQmlImport : public QQmlDataTest
{
Q_OBJECT
public:
- tst_QQmlImport();
+ tst_QQmlImport()
+ : QQmlDataTest(QT_QMLTEST_DATADIR)
+ , noimportRegistration("noimport", qml_register_types_noimport)
+ {
+ }
private slots:
void importPathOrder();
@@ -34,10 +60,17 @@ private slots:
void importDependenciesPrecedence();
void cleanup();
void envResourceImportPath();
+ void preferResourcePath_data();
void preferResourcePath();
void invalidFileImport_data();
void invalidFileImport();
void implicitWithDependencies();
+ void qualifiedScriptImport();
+ void invalidImportUrl();
+ void registerTypesFromImplicitImport();
+
+private:
+ QQmlModuleRegistration noimportRegistration;
};
void tst_QQmlImport::cleanup()
@@ -72,12 +105,21 @@ void tst_QQmlImport::envResourceImportPath()
QVERIFY((importPaths.contains(path.startsWith(u':') ? QLatin1String("qrc") + path : path)));
}
+void tst_QQmlImport::preferResourcePath_data()
+{
+ QTest::addColumn<QUrl>("file");
+ QTest::addRow("without qmldir") << testFileUrl("prefer.qml");
+ QTest::addRow("with qmldir") << testFileUrl("prefer2.qml");
+}
+
void tst_QQmlImport::preferResourcePath()
{
+ QFETCH(QUrl, file);
+
QQmlEngine engine;
engine.addImportPath(dataDirectory());
- QQmlComponent component(&engine, testFileUrl("prefer.qml"));
+ QQmlComponent component(&engine, file);
QVERIFY2(component.isReady(), component.errorString().toUtf8());
QScopedPointer<QObject> o(component.create());
QCOMPARE(o->objectName(), "right");
@@ -123,9 +165,46 @@ void tst_QQmlImport::implicitWithDependencies()
QCOMPARE(o->objectName(), QStringLiteral("notARectangle"));
}
+void tst_QQmlImport::qualifiedScriptImport()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("qualifiedScriptImport.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("a"), QVariant::fromValue<double>(12));
+ QCOMPARE(o->property("b"), QVariant::fromValue<int>(3));
+ QCOMPARE(o->property("c"), QVariant());
+}
+
+void tst_QQmlImport::invalidImportUrl()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("fileDotSlashImport.qml");
+ QQmlComponent component(&engine, url);
+ QVERIFY(component.isError());
+ QCOMPARE(
+ component.errorString(),
+ url.toString() + QLatin1String(
+ ":2 Cannot resolve URL for import \"file://./MyModuleName\"\n"));
+}
+
+void tst_QQmlImport::registerTypesFromImplicitImport()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("noimport/Main.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ TheThing *t = qobject_cast<TheThing *>(o.data());
+ QVERIFY(t);
+ QCOMPARE(t->m_width, 640);
+}
+
void tst_QQmlImport::testDesignerSupported()
{
- QQuickView *window = new QQuickView();
+ std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>();
window->engine()->addImportPath(directory());
window->setSource(testFileUrl("testfile_supported.qml"));
@@ -137,8 +216,7 @@ void tst_QQmlImport::testDesignerSupported()
QQmlImports::setDesignerSupportRequired(true);
//imports are cached so we create a new window
- delete window;
- window = new QQuickView();
+ window = std::make_unique<QQuickView>();
window->engine()->addImportPath(directory());
window->engine()->clearComponentCache();
@@ -154,21 +232,19 @@ void tst_QQmlImport::testDesignerSupported()
QTest::ignoreMessage(QtWarningMsg, warningString.toLocal8Bit());
window->setSource(testFileUrl("testfile_unsupported.qml"));
QVERIFY(!window->errors().isEmpty());
-
- delete window;
}
void tst_QQmlImport::uiFormatLoading()
{
int size = 0;
- QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("TestForm.ui.qml"));
+ std::unique_ptr<QQmlApplicationEngine> test = std::make_unique<QQmlApplicationEngine>(testFileUrl("TestForm.ui.qml"));
test->addImportPath(directory());
QCOMPARE(test->rootObjects().size(), ++size);
QVERIFY(test->rootObjects()[size -1]);
QVERIFY(test->rootObjects()[size -1]->property("success").toBool());
- QSignalSpy objectCreated(test, SIGNAL(objectCreated(QObject*,QUrl)));
+ QSignalSpy objectCreated(test.get(), SIGNAL(objectCreated(QObject*,QUrl)));
test->load(testFileUrl("TestForm.ui.qml"));
QCOMPARE(objectCreated.size(), size);//one less than rootObjects().size() because we missed the first one
QCOMPARE(test->rootObjects().size(), ++size);
@@ -193,13 +269,6 @@ void tst_QQmlImport::uiFormatLoading()
QCOMPARE(test->rootObjects().size(), ++size);
QVERIFY(test->rootObjects()[size -1]);
QVERIFY(test->rootObjects()[size -1]->property("success").toBool());
-
- delete test;
-}
-
-tst_QQmlImport::tst_QQmlImport()
- : QQmlDataTest(QT_QMLTEST_DATADIR)
-{
}
void tst_QQmlImport::importPathOrder()
@@ -503,6 +572,25 @@ void tst_QQmlImport::registerModuleImport()
qmlUnregisterModuleImport("MyPluginSupported", 2, "QtQuick", 2);
qmlUnregisterModuleImport("MyPluginSupported", 2, "ShadowQuick", 1);
+
+ qmlRegisterTypesAndRevisions<NotItem>("NoQmldir", 2);
+ qmlRegisterModuleImport("NoQmldir", QQmlModuleImportModuleAny, "QtQml", QQmlModuleImportAuto);
+
+ {
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(R"(
+ import NoQmldir 2.0
+ QtObject {
+ property Item item: Item {}
+ }
+ )", QUrl::fromLocalFile(""));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
+ NotItem *item = object->property("item").value<NotItem *>();
+ QVERIFY(item);
+ }
}
void tst_QQmlImport::importDependenciesPrecedence()
diff --git a/tests/auto/qml/qqmlincubator/CMakeLists.txt b/tests/auto/qml/qqmlincubator/CMakeLists.txt
index 230609bb1d..3a38868e76 100644
--- a/tests/auto/qml/qqmlincubator/CMakeLists.txt
+++ b/tests/auto/qml/qqmlincubator/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlincubator Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlincubator LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml b/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml
new file mode 100644
index 0000000000..b5ba531ede
--- /dev/null
+++ b/tests/auto/qml/qqmlincubator/data/garbageCollection2.qml
@@ -0,0 +1,12 @@
+import QtQml
+QtObject {
+ property Component comp: Component {
+ QtObject {}
+ }
+
+ property QtObject incubated: {
+ var i = comp.incubateObject(null, {}, Qt.Synchronous);
+ gc();
+ return i.object
+ }
+}
diff --git a/tests/auto/qml/qqmlincubator/testtypes.cpp b/tests/auto/qml/qqmlincubator/testtypes.cpp
index c94ef73c20..4e449a1b5a 100644
--- a/tests/auto/qml/qqmlincubator/testtypes.cpp
+++ b/tests/auto/qml/qqmlincubator/testtypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlincubator/testtypes.h b/tests/auto/qml/qqmlincubator/testtypes.h
index e4c48d9d13..4942957986 100644
--- a/tests/auto/qml/qqmlincubator/testtypes.h
+++ b/tests/auto/qml/qqmlincubator/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
diff --git a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
index 1baf61574e..948dc66e71 100644
--- a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
+++ b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
#include <QUrl>
@@ -1148,7 +1148,7 @@ void tst_qqmlincubator::garbageCollection()
QQmlComponent component(&engine, testFileUrl("garbageCollection.qml"));
QScopedPointer<QObject> obj(component.create());
- engine.collectGarbage();
+ gc(engine);
std::atomic<bool> b{true};
controller.incubateWhile(&b);
@@ -1166,8 +1166,14 @@ void tst_qqmlincubator::garbageCollection()
incubatorVariant.clear();
// verify incubator is correctly collected now that incubation is complete and all references are gone
- engine.collectGarbage();
+ gc(engine);
QVERIFY(weakIncubatorRef.isNullOrUndefined());
+
+ QQmlComponent component2(&engine, testFileUrl("garbageCollection2.qml"));
+ QVERIFY2(component2.isReady(), qPrintable(component2.errorString()));
+ QScopedPointer<QObject> obj2(component2.create());
+ QVERIFY(!obj2.isNull());
+ QVERIFY(obj2->property("incubated").value<QObject *>() != nullptr);
}
void tst_qqmlincubator::requiredProperties()
diff --git a/tests/auto/qml/qqmlinfo/CMakeLists.txt b/tests/auto/qml/qqmlinfo/CMakeLists.txt
index 32e6c5ebd9..db00af9ea8 100644
--- a/tests/auto/qml/qqmlinfo/CMakeLists.txt
+++ b/tests/auto/qml/qqmlinfo/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlinfo Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlinfo LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlinfo/attached.cpp b/tests/auto/qml/qqmlinfo/attached.cpp
index 5ced27f3c9..88ad5d88dd 100644
--- a/tests/auto/qml/qqmlinfo/attached.cpp
+++ b/tests/auto/qml/qqmlinfo/attached.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "attached.h"
diff --git a/tests/auto/qml/qqmlinfo/attached.h b/tests/auto/qml/qqmlinfo/attached.h
index c719da8bcc..8d4018f88d 100644
--- a/tests/auto/qml/qqmlinfo/attached.h
+++ b/tests/auto/qml/qqmlinfo/attached.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp
index 4908ca210b..ebe1416c7d 100644
--- a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp
+++ b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/qsignalspy.h>
@@ -213,7 +213,7 @@ void tst_qqmlinfo::attachedObject()
{
QQmlComponent component(&engine, testFileUrl("AttachedObject.qml"));
- QSignalSpy warningSpy(&engine, SIGNAL(warnings(const QList<QQmlError> &)));
+ QSignalSpy warningSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
QVERIFY(warningSpy.isValid());
const QString qmlBindingLoopMessage = "QML Rectangle: Binding loop detected for property \"width\"";
diff --git a/tests/auto/qml/qqmlinstantiator/CMakeLists.txt b/tests/auto/qml/qqmlinstantiator/CMakeLists.txt
index 59280f8c7d..7e453b9671 100644
--- a/tests/auto/qml/qqmlinstantiator/CMakeLists.txt
+++ b/tests/auto/qml/qqmlinstantiator/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlinstantiator Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlinstantiator LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlinstantiator/data/listDataDestruction.qml b/tests/auto/qml/qqmlinstantiator/data/listDataDestruction.qml
new file mode 100644
index 0000000000..82d707af37
--- /dev/null
+++ b/tests/auto/qml/qqmlinstantiator/data/listDataDestruction.qml
@@ -0,0 +1,20 @@
+import QtQml
+
+QtObject {
+ id: menu
+
+ Component.onCompleted: dt = new Date();
+ property date dt
+ property Instantiator i: Instantiator {
+ model: {
+ var model = [];
+ var d = menu.dt;
+ model.push({text: "A"});
+ return model;
+ }
+ delegate: QtObject {
+ objectName: modelData.text
+ Component.onCompleted: menu.objectName = objectName
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlinstantiator/data/removeDuringModelChange.qml b/tests/auto/qml/qqmlinstantiator/data/removeDuringModelChange.qml
new file mode 100644
index 0000000000..079e376549
--- /dev/null
+++ b/tests/auto/qml/qqmlinstantiator/data/removeDuringModelChange.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.15
+import QtQml.Models 2.15
+
+Instantiator {
+ delegate: QtObject {
+ function deactivate() {
+ model.active = false;
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlinstantiator/stringmodel.h b/tests/auto/qml/qqmlinstantiator/stringmodel.h
index d9676b9460..2b0c725e05 100644
--- a/tests/auto/qml/qqmlinstantiator/stringmodel.h
+++ b/tests/auto/qml/qqmlinstantiator/stringmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Dmitrii Kosarev aka Kakadu <kakadu.hafanana@gmail.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef STRINGMODEL_H
#define STRINGMODEL_H
diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
index 1097c65f02..de7c6645a9 100644
--- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
+++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
@@ -1,7 +1,8 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QSignalSpy>
+#include <QSortFilterProxyModel>
#include <QDebug>
#include <QtQml/qqmlengine.h>
@@ -28,12 +29,15 @@ private slots:
void activeModelChangeInteraction();
void intModelChange();
void createAndRemove();
+ void removeDuringModelChange();
void asynchronous_data();
void asynchronous();
void handlerWithParent();
void boundDelegateComponent();
+
+ void listDataDestruction();
};
tst_qqmlinstantiator::tst_qqmlinstantiator()
@@ -306,6 +310,103 @@ void tst_qqmlinstantiator::boundDelegateComponent()
QCOMPARE(b->objectAt(2)->objectName(), QStringLiteral("root3"));
}
+void tst_qqmlinstantiator::listDataDestruction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("listDataDestruction.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), QLatin1String("A"));
+}
+
+class SingleBoolItemModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ SingleBoolItemModel(QObject *parent = nullptr) : QAbstractListModel(parent) {}
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override
+ {
+ if (parent.isValid())
+ return 0;
+ return 1;
+ }
+
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ if (index.parent().isValid() || index.row() != 0 || index.column() != 0
+ || role != Qt::UserRole)
+ return QVariant();
+
+ return m_active;
+ }
+
+ bool setData(const QModelIndex &index, const QVariant &value,
+ int role) override {
+ if (index.parent().isValid() || index.row() != 0 || index.column() != 0
+ || role != Qt::UserRole || m_active == value.toBool())
+ return false;
+
+ m_active = value.toBool();
+ Q_EMIT dataChanged(index, index, QList<int>{Qt::UserRole});
+ return true;
+ }
+
+ QHash<int, QByteArray> roleNames() const override
+ {
+ return { {Qt::UserRole, "active"} };
+ }
+
+private:
+ bool m_active = true;
+};
+
+class FilterBoolRoleProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ FilterBoolRoleProxyModel(QObject *parent = nullptr)
+ : QSortFilterProxyModel(parent) {}
+
+ bool filterAcceptsRow(int source_row,
+ const QModelIndex &source_parent) const override
+ {
+ return sourceModel()->index(source_row, 0, source_parent).data(Qt::UserRole).toBool();
+ }
+};
+
+void tst_qqmlinstantiator::removeDuringModelChange()
+{
+ SingleBoolItemModel model;
+
+ FilterBoolRoleProxyModel proxyModel;
+ proxyModel.setSourceModel(&model);
+ proxyModel.setFilterRole(Qt::UserRole);
+ QCOMPARE(proxyModel.rowCount(), 1);
+
+ QQmlEngine engine;
+ const QUrl url(testFileUrl("removeDuringModelChange.qml"));
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY2(!o.isNull(), qPrintable(component.errorString()));
+
+ QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator *>(o.data());
+
+ instantiator->setModel(QVariant::fromValue(&proxyModel));
+
+ QSignalSpy removedSpy(instantiator, &QQmlInstantiator::objectRemoved);
+ QMetaObject::invokeMethod(instantiator->object(), "deactivate");
+
+ // We should still be alive at this point.
+ QCOMPARE(removedSpy.size(), 1);
+ QCOMPARE(proxyModel.rowCount(), 0);
+}
+
QTEST_MAIN(tst_qqmlinstantiator)
#include "tst_qqmlinstantiator.moc"
diff --git a/tests/auto/qml/qqmlitemmodels/CMakeLists.txt b/tests/auto/qml/qqmlitemmodels/CMakeLists.txt
index 632e98206b..80f210f668 100644
--- a/tests/auto/qml/qqmlitemmodels/CMakeLists.txt
+++ b/tests/auto/qml/qqmlitemmodels/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlitemmodels Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlitemmodels LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml
index 2756f04120..7bf8bfb5a2 100644
--- a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml
+++ b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml
@@ -8,6 +8,7 @@ ItemModelsTest {
property var parent: modelIndex.parent
property var model: modelIndex.model
property var internalId: modelIndex.internalId
+ property var displayData: modelIndex.data(Qt.DisplayRole)
onSignalWithModelIndex: {
isValid = index.valid
@@ -16,5 +17,6 @@ ItemModelsTest {
parent = index.parent
model = index.model
internalId = index.internalId
+ displayData = index.data(Qt.DisplayRole)
}
}
diff --git a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml
index 85987bdcac..aa43252100 100644
--- a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml
+++ b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml
@@ -8,6 +8,7 @@ ItemModelsTest {
property var parent: persistentModelIndex.parent
property var model: persistentModelIndex.model
property var internalId: persistentModelIndex.internalId
+ property var displayData: persistentModelIndex.data(Qt.DisplayRole)
property var pmi
@@ -18,6 +19,7 @@ ItemModelsTest {
parent = index.parent
model = index.model
internalId = index.internalId
+ displayData = index.data(Qt.DisplayRole)
pmi = createPersistentModelIndex(model.index(0, 0))
}
diff --git a/tests/auto/qml/qqmlitemmodels/qtestmodel.h b/tests/auto/qml/qqmlitemmodels/qtestmodel.h
index 6cbec533b1..6c442b06b7 100644
--- a/tests/auto/qml/qqmlitemmodels/qtestmodel.h
+++ b/tests/auto/qml/qqmlitemmodels/qtestmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef Q_TEST_MODEL_H
#define Q_TEST_MODEL_H
@@ -251,6 +251,8 @@ public:
mutable bool wrongIndex;
struct Node {
+ Q_DISABLE_COPY_MOVE(Node)
+
Node *parent;
QVector<Node *> children;
@@ -261,8 +263,7 @@ public:
~Node()
{
- foreach (Node *n, children)
- delete n;
+ qDeleteAll(children);
}
void addRows(int row, int count)
diff --git a/tests/auto/qml/qqmlitemmodels/testtypes.h b/tests/auto/qml/qqmlitemmodels/testtypes.h
index 57ac9c12d1..3bbd566cd1 100644
--- a/tests/auto/qml/qqmlitemmodels/testtypes.h
+++ b/tests/auto/qml/qqmlitemmodels/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
diff --git a/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp b/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp
index 339a61f996..1182759519 100644
--- a/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp
+++ b/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
@@ -55,6 +55,7 @@ void tst_qqmlitemmodels::modelIndex()
QCOMPARE(object->property("parent").toModelIndex(), index.parent());
QCOMPARE(object->property("model").value<QAbstractItemModel *>(), index.model());
QCOMPARE(object->property("internalId").toULongLong(), index.internalId());
+ QCOMPARE(object->property("displayData"), index.data(Qt::DisplayRole));
if (i < 3) {
index = model.index(2 + i, 4 - i, index);
@@ -79,6 +80,7 @@ void tst_qqmlitemmodels::persistentModelIndex()
QCOMPARE(object->property("parent").toModelIndex(), index.parent());
QCOMPARE(object->property("model").value<QAbstractItemModel *>(), index.model());
QCOMPARE(object->property("internalId").toULongLong(), index.internalId());
+ QCOMPARE(object->property("displayData"), index.data(Qt::DisplayRole));
if (i < 2) {
index = model.index(2 + i, 4 - i, index);
diff --git a/tests/auto/qml/qqmljsscope/CMakeLists.txt b/tests/auto/qml/qqmljsscope/CMakeLists.txt
index 542d217d8d..be8e136fc8 100644
--- a/tests/auto/qml/qqmljsscope/CMakeLists.txt
+++ b/tests/auto/qml/qqmljsscope/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmljsscope LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt
index 3431398f47..5215159373 100644
--- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt
+++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt
@@ -8,9 +8,10 @@ qt_autogen_tools_initial_setup(qqmljsscope_test_module)
target_include_directories(qqmljsscope_test_module PUBLIC cpptypes/)
target_link_libraries(qqmljsscope_test_module PUBLIC Qt::Core Qt::Qml Qt::Gui)
+qt_policy(SET QTP0001 NEW)
+
qt6_add_qml_module(qqmljsscope_test_module
URI QQmlJSScopeTests
- AUTO_RESOURCE_PREFIX
SOURCES
singleton.h singleton.cpp
extensiontypes.h
diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h
index f5fbde1b27..82fc3b58d2 100644
--- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h
+++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef EXTENSIONTYPES_H
#define EXTENSIONTYPES_H
@@ -18,6 +18,17 @@ public:
Extension(QObject *parent = nullptr) : QObject(parent) { }
int getCount() const { return 42; }
void setCount(int) { }
+
+ enum EnumFromExtension {
+ ThisIsTheEnumFromExtension,
+ };
+ Q_ENUM(EnumFromExtension)
+ enum FlagFromExtension {
+ ThisIsTheFlagFromExtension,
+ };
+ Q_DECLARE_FLAGS(FlagsFromExtension, FlagFromExtension)
+ Q_FLAG(FlagsFromExtension)
+
Q_SIGNALS:
void countChanged();
};
@@ -41,6 +52,16 @@ public:
Extended(QObject *parent = nullptr) : QObject(parent) { }
double getCount() const { return 0.0; }
void setCount(double) { }
+
+ enum EnumFromExtended {
+ ThisIsTheEnumFromExtended,
+ };
+ Q_ENUM(EnumFromExtended)
+ enum FlagFromExtended {
+ ThisIsTheFlagFromExtended,
+ };
+ Q_DECLARE_FLAGS(FlagsFromExtended, FlagFromExtended)
+ Q_FLAG(FlagsFromExtended)
Q_SIGNALS:
void countChanged();
};
diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp
index 280ff6163e..56efc324d3 100644
--- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp
+++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "singleton.h"
diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h
index 78aa6466d7..a78b85e1d9 100644
--- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h
+++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/singleton.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef SINGLETON_H
#define SINGLETON_H
diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h
index 2c812a6d9d..9126cf8f69 100644
--- a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h
+++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/typewithproperties.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TYPEWITHPROPERTIES_H
#define TYPEWITHPROPERTIES_H
diff --git a/tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h b/tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h
index c323645f73..514a98881d 100644
--- a/tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h
+++ b/tests/auto/qml/qqmljsscope/QualifiedNamesTests/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
diff --git a/tests/auto/qml/qqmljsscope/data/attachedTypeResolution.qml b/tests/auto/qml/qqmljsscope/data/attachedTypeResolution.qml
new file mode 100644
index 0000000000..d6062be1f6
--- /dev/null
+++ b/tests/auto/qml/qqmljsscope/data/attachedTypeResolution.qml
@@ -0,0 +1,6 @@
+import QtQml
+import QtQuick
+
+Window {
+
+}
diff --git a/tests/auto/qml/qqmljsscope/data/methodAndSignalSourceLocation.qml b/tests/auto/qml/qqmljsscope/data/methodAndSignalSourceLocation.qml
new file mode 100644
index 0000000000..52841e323c
--- /dev/null
+++ b/tests/auto/qml/qqmljsscope/data/methodAndSignalSourceLocation.qml
@@ -0,0 +1,16 @@
+import QtQml
+
+QtObject {
+ function f1() { }
+ function f2(a) { }
+ function f3(a: int) { }
+ function f4(a, b) { }
+ function f5(a, b): void { }
+ function f6(a, b, c): void {
+ // Nothing
+ }
+
+ signal s1()
+ signal s2(a: int)
+ signal s3(a: int, b: string)
+}
diff --git a/tests/auto/qml/qqmljsscope/data/ownModuleName.qml b/tests/auto/qml/qqmljsscope/data/ownModuleName.qml
new file mode 100644
index 0000000000..6e43ce6b05
--- /dev/null
+++ b/tests/auto/qml/qqmljsscope/data/ownModuleName.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ Item { id: child }
+ component IC: Item {
+ Item {
+ id: childInIC
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmljsscope/data/requiredAlias.qml b/tests/auto/qml/qqmljsscope/data/requiredAlias.qml
new file mode 100644
index 0000000000..77f70dea2b
--- /dev/null
+++ b/tests/auto/qml/qqmljsscope/data/requiredAlias.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: self
+
+ property int nonRequired
+ property alias sameScopeAlias: self.nonRequired
+ required sameScopeAlias
+
+ property alias innerScopeAlias: inner.nonRequiredInner
+ required innerScopeAlias
+
+ Item {
+ id: inner
+ property int nonRequiredInner
+ }
+}
diff --git a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
index 09c6601858..4dacc17f94 100644
--- a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
+++ b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -35,9 +35,9 @@ class tst_qqmljsscope : public QQmlDataTest
{
const QFileInfo fi(url);
QFile f(fi.absoluteFilePath());
- f.open(QIODevice::ReadOnly);
- QByteArray data(fi.size(), Qt::Uninitialized);
- f.read(data.data(), data.size());
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("Could not open file %s", qPrintable(url));
+ QByteArray data = f.readAll();
return QString::fromUtf8(data);
}
@@ -71,6 +71,7 @@ class tst_qqmljsscope : public QQmlDataTest
logger.setCode(sourceCode);
logger.setSilent(expectErrorsOrWarnings);
QQmlJSScope::Ptr target = QQmlJSScope::create();
+ target->setOwnModuleName(u"HelloModule"_s);
QQmlJSImportVisitor visitor(target, &m_importer, &logger, dataDirectory());
QQmlJSTypeResolver typeResolver { &m_importer };
typeResolver.init(&visitor, document->program);
@@ -92,6 +93,7 @@ private Q_SLOTS:
void signalCreationDifferences();
void allTypesAvailable();
void shadowing();
+ void requiredAlias();
#ifdef LABS_QML_MODELS_PRESENT
void componentWrappedObjects();
@@ -106,9 +108,15 @@ private Q_SLOTS:
void scriptIndices();
void extensions();
void emptyBlockBinding();
- void qualifiedName();
+ void hasOwnEnumerationKeys();
+ void ownModuleName();
void resolvedNonUniqueScopes();
void compilationUnitsAreCompatible();
+ void attachedTypeResolution_data();
+ void attachedTypeResolution();
+ void builtinTypeResolution_data();
+ void builtinTypeResolution();
+ void methodAndSignalSourceLocation();
public:
tst_qqmljsscope()
@@ -160,14 +168,14 @@ void tst_qqmljsscope::orderedBindings()
QCOMPARE(std::distance(pBindingsBegin, pBindingsEnd), 2);
// check that the bindings are properly ordered
- QCOMPARE(pBindingsBegin->bindingType(), QQmlJSMetaPropertyBinding::Object);
- QCOMPARE(std::next(pBindingsBegin)->bindingType(), QQmlJSMetaPropertyBinding::Interceptor);
+ QCOMPARE(pBindingsBegin->bindingType(), QQmlSA::BindingType::Object);
+ QCOMPARE(std::next(pBindingsBegin)->bindingType(), QQmlSA::BindingType::Interceptor);
auto [itemsBindingsBegin, itemsBindingsEnd] = root->ownPropertyBindings(u"items"_s);
QCOMPARE(std::distance(itemsBindingsBegin, itemsBindingsEnd), 2);
- QCOMPARE(itemsBindingsBegin->bindingType(), QQmlJSMetaPropertyBinding::Object);
- QCOMPARE(std::next(itemsBindingsBegin)->bindingType(), QQmlJSMetaPropertyBinding::Object);
+ QCOMPARE(itemsBindingsBegin->bindingType(), QQmlSA::BindingType::Object);
+ QCOMPARE(std::next(itemsBindingsBegin)->bindingType(), QQmlSA::BindingType::Object);
QCOMPARE(itemsBindingsBegin->objectType()->baseTypeName(), u"Item"_s);
QCOMPARE(std::next(itemsBindingsBegin)->objectType()->baseTypeName(), u"Text"_s);
@@ -184,8 +192,8 @@ void tst_qqmljsscope::signalCreationDifferences()
const auto conflicting = root->ownMethods(u"conflictingPropertyChanged"_s);
QCOMPARE(conflicting.size(), 2);
- QCOMPARE(conflicting[0].methodType(), QQmlJSMetaMethod::Signal);
- QCOMPARE(conflicting[1].methodType(), QQmlJSMetaMethod::Signal);
+ QCOMPARE(conflicting[0].methodType(), QQmlJSMetaMethodType::Signal);
+ QCOMPARE(conflicting[1].methodType(), QQmlJSMetaMethodType::Signal);
const QQmlJSMetaMethod *explicitMethod = nullptr;
if (conflicting[0].isImplicitQmlPropertyChangeSignal())
@@ -204,7 +212,7 @@ void tst_qqmljsscope::allTypesAvailable()
QQmlJSImporter importer { importPaths, /* resource file mapper */ nullptr };
const auto imported = importer.importModule(u"QtQml"_s);
- QCOMPARE(imported.context(), QQmlJSScope::ContextualTypes::QML);
+ QCOMPARE(imported.context(), QQmlJS::ContextualTypes::QML);
const auto types = imported.types();
QVERIFY(types.contains(u"$internal$.QObject"_s));
QVERIFY(types.contains(u"QtObject"_s));
@@ -235,6 +243,16 @@ void tst_qqmljsscope::shadowing()
QCOMPARE(methods[u"method_shadowed"_s].parameterNames().size(), 0);
}
+void tst_qqmljsscope::requiredAlias()
+{
+ QQmlJSScope::ConstPtr root = run(u"requiredAlias.qml"_s);
+ QVERIFY(root);
+
+ // Check whether aliases marked as required are required
+ QVERIFY(root->isPropertyRequired("sameScopeAlias"));
+ QVERIFY(root->isPropertyRequired("innerScopeAlias"));
+}
+
#ifdef LABS_QML_MODELS_PRESENT
void tst_qqmljsscope::componentWrappedObjects()
{
@@ -302,7 +320,7 @@ void tst_qqmljsscope::groupedProperties()
const auto getBindingsWithinGroup =
[&](QMultiHash<QString, QQmlJSMetaPropertyBinding> *bindings, qsizetype index) -> void {
const auto &binding = anchorBindings[index];
- QCOMPARE(binding.bindingType(), QQmlJSMetaPropertyBinding::GroupProperty);
+ QCOMPARE(binding.bindingType(), QQmlSA::BindingType::GroupProperty);
auto anchorScope = binding.groupType();
QVERIFY(anchorScope);
*bindings = anchorScope->ownPropertyBindings();
@@ -316,14 +334,14 @@ void tst_qqmljsscope::groupedProperties()
QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfType;
getBindingsWithinGroup(&bindingsOfType, 0);
QCOMPARE(bindingsOfType.size(), 2);
- QCOMPARE(value(bindingsOfType, u"left"_s).bindingType(), QQmlJSMetaPropertyBinding::Script);
+ QCOMPARE(value(bindingsOfType, u"left"_s).bindingType(), QQmlSA::BindingType::Script);
QCOMPARE(value(bindingsOfType, u"leftMargin"_s).bindingType(),
- QQmlJSMetaPropertyBinding::NumberLiteral);
+ QQmlSA::BindingType::NumberLiteral);
QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfBaseType;
getBindingsWithinGroup(&bindingsOfBaseType, 1);
QCOMPARE(bindingsOfBaseType.size(), 1);
- QCOMPARE(value(bindingsOfBaseType, u"top"_s).bindingType(), QQmlJSMetaPropertyBinding::Script);
+ QCOMPARE(value(bindingsOfBaseType, u"top"_s).bindingType(), QQmlSA::BindingType::Script);
}
void tst_qqmljsscope::descriptiveNameOfNull()
@@ -337,8 +355,10 @@ void tst_qqmljsscope::descriptiveNameOfNull()
property.setPropertyName(u"foo"_s);
property.setTypeName(u"baz"_s);
QQmlJSRegisterContent unscoped = QQmlJSRegisterContent::create(
- stored, property, QQmlJSRegisterContent::ScopeProperty, QQmlJSScope::ConstPtr());
- QCOMPARE(unscoped.descriptiveName(), u"bar of (invalid type)::foo with type baz"_s);
+ stored, property, QQmlJSRegisterContent::InvalidLookupIndex,
+ QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ScopeProperty,
+ QQmlJSScope::ConstPtr());
+ QCOMPARE(unscoped.descriptiveName(), u"(invalid type)::foo with type baz (stored as bar)"_s);
}
void tst_qqmljsscope::groupedPropertiesConsistency()
@@ -363,8 +383,8 @@ void tst_qqmljsscope::groupedPropertiesConsistency()
// The binding order in QQmlJSScope case is "reversed": first come
// bindings on the leaf type, followed by the bindings on the base type
- QCOMPARE(fontBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty);
- QCOMPARE(fontBindings[1].bindingType(), QQmlJSMetaPropertyBinding::Script);
+ QCOMPARE(fontBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty);
+ QCOMPARE(fontBindings[1].bindingType(), QQmlSA::BindingType::Script);
}
}
@@ -378,7 +398,7 @@ void tst_qqmljsscope::groupedPropertySyntax()
// The binding order in QQmlJSScope case is "reversed": first come
// bindings on the leaf type, followed by the bindings on the base type
- QCOMPARE(fontBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty);
+ QCOMPARE(fontBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty);
auto fontScope = fontBindings[0].groupType();
QVERIFY(fontScope);
QCOMPARE(fontScope->accessSemantics(), QQmlJSScope::AccessSemantics::Value);
@@ -390,9 +410,8 @@ void tst_qqmljsscope::groupedPropertySyntax()
return bindings.value(key, QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {}));
};
- QCOMPARE(value(subbindings, u"pixelSize"_s).bindingType(),
- QQmlJSMetaPropertyBinding::NumberLiteral);
- QCOMPARE(value(subbindings, u"bold"_s).bindingType(), QQmlJSMetaPropertyBinding::BoolLiteral);
+ QCOMPARE(value(subbindings, u"pixelSize"_s).bindingType(), QQmlSA::BindingType::NumberLiteral);
+ QCOMPARE(value(subbindings, u"bold"_s).bindingType(), QQmlSA::BindingType::BoolLiteral);
}
void tst_qqmljsscope::attachedProperties()
@@ -407,7 +426,7 @@ void tst_qqmljsscope::attachedProperties()
const auto getBindingsWithinAttached =
[&](QMultiHash<QString, QQmlJSMetaPropertyBinding> *bindings, qsizetype index) -> void {
const auto &binding = keysBindings[index];
- QCOMPARE(binding.bindingType(), QQmlJSMetaPropertyBinding::AttachedProperty);
+ QCOMPARE(binding.bindingType(), QQmlSA::BindingType::AttachedProperty);
auto keysScope = binding.attachingType();
QVERIFY(keysScope);
QCOMPARE(keysScope->accessSemantics(), QQmlJSScope::AccessSemantics::Reference);
@@ -422,23 +441,21 @@ void tst_qqmljsscope::attachedProperties()
QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfType;
getBindingsWithinAttached(&bindingsOfType, 0);
QCOMPARE(bindingsOfType.size(), 2);
- QCOMPARE(value(bindingsOfType, u"enabled"_s).bindingType(),
- QQmlJSMetaPropertyBinding::BoolLiteral);
- QCOMPARE(value(bindingsOfType, u"forwardTo"_s).bindingType(),
- QQmlJSMetaPropertyBinding::Script);
+ QCOMPARE(value(bindingsOfType, u"enabled"_s).bindingType(), QQmlSA::BindingType::BoolLiteral);
+ QCOMPARE(value(bindingsOfType, u"forwardTo"_s).bindingType(), QQmlSA::BindingType::Script);
QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfBaseType;
getBindingsWithinAttached(&bindingsOfBaseType, 1);
QCOMPARE(bindingsOfBaseType.size(), 1);
- QCOMPARE(value(bindingsOfBaseType, u"priority"_s).bindingType(),
- QQmlJSMetaPropertyBinding::Script);
+ QCOMPARE(value(bindingsOfBaseType, u"priority"_s).bindingType(), QQmlSA::BindingType::Script);
}
inline QString getScopeName(const QQmlJSScope::ConstPtr &scope)
{
Q_ASSERT(scope);
QQmlJSScope::ScopeType type = scope->scopeType();
- if (type == QQmlJSScope::GroupedPropertyScope || type == QQmlJSScope::AttachedPropertyScope)
+ if (type == QQmlSA::ScopeType::GroupedPropertyScope
+ || type == QQmlSA::ScopeType::AttachedPropertyScope)
return scope->internalName();
return scope->baseTypeName();
}
@@ -507,7 +524,7 @@ void tst_qqmljsscope::scriptIndices()
QmlIR::Document document(false); // we need QmlIR information here
QQmlJSScope::ConstPtr root = run(u"functionAndBindingIndices.qml"_s, &document);
QVERIFY(root);
- QVERIFY(document.javaScriptCompilationUnit.unitData());
+ QVERIFY(document.javaScriptCompilationUnit->unitData());
// compare QQmlJSScope and QmlIR:
@@ -537,8 +554,9 @@ void tst_qqmljsscope::scriptIndices()
const auto suitableScope = [](const QQmlJSScope::ConstPtr &scope) {
const auto type = scope->scopeType();
- return type == QQmlJSScope::QMLScope || type == QQmlJSScope::GroupedPropertyScope
- || type == QQmlJSScope::AttachedPropertyScope;
+ return type == QQmlSA::ScopeType::QMLScope
+ || type == QQmlSA::ScopeType::GroupedPropertyScope
+ || type == QQmlSA::ScopeType::AttachedPropertyScope;
};
QList<QQmlJSScope::ConstPtr> queue;
@@ -550,7 +568,7 @@ void tst_qqmljsscope::scriptIndices()
if (suitableScope(current)) {
const auto methods = current->ownMethods();
for (const auto &method : methods) {
- if (method.methodType() == QQmlJSMetaMethod::Signal)
+ if (method.methodType() == QQmlJSMetaMethodType::Signal)
continue;
QString name = method.methodName();
auto relativeIndex = method.jsFunctionIndex();
@@ -563,7 +581,7 @@ void tst_qqmljsscope::scriptIndices()
const auto bindings = current->ownPropertyBindings();
for (const auto &binding : bindings) {
- if (binding.bindingType() != QQmlJSMetaPropertyBinding::Script)
+ if (binding.bindingType() != QQmlSA::BindingType::Script)
continue;
QString name = binding.propertyName();
auto relativeIndex = binding.scriptIndex();
@@ -674,28 +692,50 @@ void tst_qqmljsscope::emptyBlockBinding()
QVERIFY(root->hasOwnPropertyBindings(u"y"_s));
}
-void tst_qqmljsscope::qualifiedName()
+void tst_qqmljsscope::hasOwnEnumerationKeys()
{
- QQmlJSScope::ConstPtr root = run(u"qualifiedName.qml"_s);
+ QQmlJSScope::ConstPtr root = run(u"extensions.qml"_s);
QVERIFY(root);
+ QQmlJSScope::ConstPtr extendedDerived = root->childScopes().front();
+ QVERIFY(extendedDerived);
+ // test that enumeration keys from base cannot be found
+ QVERIFY(!extendedDerived->hasOwnEnumerationKey(u"ThisIsTheEnumFromExtended"_s));
+ QVERIFY(!extendedDerived->hasOwnEnumerationKey(u"ThisIsTheFlagFromExtended"_s));
+
+ QQmlJSScope::ConstPtr extended = extendedDerived->baseType();
+ QVERIFY(extended);
+
+ QVERIFY(extended->hasOwnEnumerationKey(u"ThisIsTheEnumFromExtended"_s));
+ QVERIFY(extended->hasOwnEnumerationKey(u"ThisIsTheFlagFromExtended"_s));
+ QVERIFY(!extended->hasOwnEnumerationKey(u"ThisIsTheEnumFromExtension"_s));
+ QVERIFY(!extended->hasOwnEnumerationKey(u"ThisIsTheFlagFromExtension"_s));
+}
- auto qualifiedNameOf = [](const QQmlJSScope::ConstPtr &ptr) -> QString {
- if (ptr->baseType())
- return ptr->baseType()->qualifiedName();
- else
- return u""_s;
- };
-
- QCOMPARE(root->childScopes().size(), 4);
- QQmlJSScope::ConstPtr b = root->childScopes()[0];
- QQmlJSScope::ConstPtr d = root->childScopes()[1];
- QQmlJSScope::ConstPtr qualifiedA = root->childScopes()[2];
- QQmlJSScope::ConstPtr qualifiedB = root->childScopes()[3];
-
- QCOMPARE(qualifiedNameOf(b), "QualifiedNamesTests/B 5.0-6.0");
- QCOMPARE(qualifiedNameOf(d), "QualifiedNamesTests/D 6.0");
- QCOMPARE(qualifiedNameOf(qualifiedA), "QualifiedNamesTests/A 5.0");
- QCOMPARE(qualifiedNameOf(qualifiedB), "QualifiedNamesTests/B 5.0-6.0");
+void tst_qqmljsscope::ownModuleName()
+{
+ const QString moduleName = u"HelloModule"_s;
+ QQmlJSScope::ConstPtr root = run(u"ownModuleName.qml"_s);
+ QVERIFY(root);
+ QCOMPARE(root->moduleName(), moduleName);
+ QCOMPARE(root->ownModuleName(), moduleName);
+
+ QCOMPARE(root->childScopes().size(), 2);
+ QQmlJSScope::ConstPtr child = root->childScopes().front();
+ QVERIFY(child);
+ // only root and inline components have own module names, but the child should be able to query
+ // its component's module Name via moduleName()
+ QCOMPARE(child->ownModuleName(), QString());
+ QCOMPARE(child->moduleName(), moduleName);
+
+ QQmlJSScope::ConstPtr ic = root->childScopes()[1];
+ QVERIFY(ic);
+ QCOMPARE(ic->ownModuleName(), moduleName);
+ QCOMPARE(ic->moduleName(), moduleName);
+
+ QQmlJSScope::ConstPtr icChild = ic->childScopes().front();
+ QVERIFY(icChild);
+ QCOMPARE(icChild->ownModuleName(), QString());
+ QCOMPARE(icChild->moduleName(), moduleName);
}
void tst_qqmljsscope::resolvedNonUniqueScopes()
@@ -711,32 +751,32 @@ void tst_qqmljsscope::resolvedNonUniqueScopes()
{
auto topLevelBindings = root->propertyBindings(u"Component"_s);
QCOMPARE(topLevelBindings.size(), 1);
- QCOMPARE(topLevelBindings[0].bindingType(), QQmlJSMetaPropertyBinding::AttachedProperty);
+ QCOMPARE(topLevelBindings[0].bindingType(), QQmlSA::BindingType::AttachedProperty);
auto componentScope = topLevelBindings[0].attachingType();
auto componentBindings = componentScope->ownPropertyBindings();
QCOMPARE(componentBindings.size(), 2);
auto onCompletedBinding = value(componentBindings, u"onCompleted"_s);
QVERIFY(onCompletedBinding.isValid());
- QCOMPARE(onCompletedBinding.bindingType(), QQmlJSMetaPropertyBinding::Script);
- QCOMPARE(onCompletedBinding.scriptKind(), QQmlJSMetaPropertyBinding::Script_SignalHandler);
+ QCOMPARE(onCompletedBinding.bindingType(), QQmlSA::BindingType::Script);
+ QCOMPARE(onCompletedBinding.scriptKind(), QQmlSA::ScriptBindingKind::SignalHandler);
auto onDestructionBinding = value(componentBindings, u"onDestruction"_s);
QVERIFY(onDestructionBinding.isValid());
- QCOMPARE(onDestructionBinding.bindingType(), QQmlJSMetaPropertyBinding::Script);
+ QCOMPARE(onDestructionBinding.bindingType(), QQmlSA::BindingType::Script);
QCOMPARE(onDestructionBinding.scriptKind(),
- QQmlJSMetaPropertyBinding::Script_SignalHandler);
+ QQmlSA::ScriptBindingKind::SignalHandler);
}
{
auto topLevelBindings = root->propertyBindings(u"p"_s);
QCOMPARE(topLevelBindings.size(), 1);
- QCOMPARE(topLevelBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty);
+ QCOMPARE(topLevelBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty);
auto pScope = topLevelBindings[0].groupType();
auto pBindings = pScope->ownPropertyBindings();
QCOMPARE(pBindings.size(), 1);
auto onXChangedBinding = value(pBindings, u"onXChanged"_s);
QVERIFY(onXChangedBinding.isValid());
- QCOMPARE(onXChangedBinding.bindingType(), QQmlJSMetaPropertyBinding::Script);
- QCOMPARE(onXChangedBinding.scriptKind(), QQmlJSMetaPropertyBinding::Script_SignalHandler);
+ QCOMPARE(onXChangedBinding.bindingType(), QQmlSA::BindingType::Script);
+ QCOMPARE(onXChangedBinding.scriptKind(), QQmlSA::ScriptBindingKind::SignalHandler);
}
}
@@ -777,8 +817,8 @@ void tst_qqmljsscope::compilationUnitsAreCompatible()
QmlIR::Document document(false); // we need QmlIR information here
QVERIFY(run(url, &document));
- QVERIFY(document.javaScriptCompilationUnit.unitData());
- getRuntimeInfoFromCompilationUnit(document.javaScriptCompilationUnit.unitData(),
+ QVERIFY(document.javaScriptCompilationUnit->unitData());
+ getRuntimeInfoFromCompilationUnit(document.javaScriptCompilationUnit->unitData(),
cachegenFunctions);
if (QTest::currentTestFailed())
return;
@@ -791,5 +831,162 @@ void tst_qqmljsscope::compilationUnitsAreCompatible()
QCOMPARE(uint(cachegenFunctions[i]->nameIndex), uint(componentFunctions[i]->nameIndex));
}
+void tst_qqmljsscope::attachedTypeResolution_data()
+{
+ QTest::addColumn<bool>("creatable");
+ QTest::addColumn<QString>("moduleName");
+ QTest::addColumn<QString>("typeName");
+ QTest::addColumn<QString>("attachedTypeName");
+ QTest::addColumn<QString>("propertyOnSelf");
+ QTest::addColumn<QString>("propertyOnAttached");
+
+ QTest::addRow("ListView") << true
+ << "QtQuick"
+ << "ListView"
+ << "QQuickListViewAttached"
+ << "orientation"
+ << "";
+ QTest::addRow("Keys") << false
+ << "QtQuick"
+ << "Keys"
+ << "QQuickKeysAttached"
+ << "priority"
+ << "priority";
+}
+
+class TestPass : public QQmlSA::ElementPass
+{
+public:
+ TestPass(QQmlSA::PassManager *manager) : QQmlSA::ElementPass(manager) { }
+ bool shouldRun(const QQmlSA::Element &) override { return true; }
+ void run(const QQmlSA::Element &) override { }
+};
+
+using PassManagerPtr = std::unique_ptr<
+ QQmlSA::PassManager, decltype(&QQmlSA::PassManagerPrivate::deletePassManager)>;
+
+void tst_qqmljsscope::attachedTypeResolution()
+{
+ QFETCH(bool, creatable);
+ QFETCH(QString, moduleName);
+ QFETCH(QString, typeName);
+ QFETCH(QString, attachedTypeName);
+ QFETCH(QString, propertyOnSelf);
+ QFETCH(QString, propertyOnAttached);
+
+ std::unique_ptr<QQmlJSLogger> logger = std::make_unique<QQmlJSLogger>();
+ QFile qmlFile("data/attachedTypeResolution.qml");
+ if (!qmlFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ QSKIP("Unable to open qml file");
+
+ logger->setCode(qmlFile.readAll());
+ logger->setFileName(QString(qmlFile.filesystemFileName().string().c_str()));
+ QQmlJSImporter importer{ { "data" }, nullptr, true };
+ QStringList defaultImportPaths =
+ QStringList{ QLibraryInfo::path(QLibraryInfo::QmlImportsPath) };
+ importer.setImportPaths(defaultImportPaths);
+ QQmlJSTypeResolver resolver(&importer);
+ const auto &implicitImportDirectory = QQmlJSImportVisitor::implicitImportDirectory(
+ logger->fileName(), importer.resourceFileMapper());
+ QQmlJSImportVisitor v{
+ QQmlJSScope::create(), &importer, logger.get(), implicitImportDirectory, {}
+ };
+
+ PassManagerPtr manager(
+ QQmlSA::PassManagerPrivate::createPassManager(&v, &resolver),
+ &QQmlSA::PassManagerPrivate::deletePassManager);
+
+ TestPass pass{ manager.get() };
+ const auto &resolved = pass.resolveType(moduleName, typeName);
+
+ QVERIFY(!resolved.isNull());
+ const auto &attachedType = pass.resolveAttached(moduleName, typeName);
+ QVERIFY(!attachedType.isNull());
+ QCOMPARE(attachedType.name(), attachedTypeName);
+
+ if (propertyOnAttached != "") {
+ QEXPECT_FAIL("Keys", "Keys and QQuickKeysAttached have the same properties", Continue);
+ QVERIFY(!resolved.hasProperty(propertyOnAttached));
+ QVERIFY(attachedType.hasProperty(propertyOnAttached));
+ }
+ if (propertyOnSelf != "") {
+ QEXPECT_FAIL("Keys", "Keys and QQuickKeysAttached have the same properties", Continue);
+ QVERIFY(!attachedType.hasProperty(propertyOnSelf));
+ }
+
+ if (creatable)
+ QVERIFY(resolved.hasProperty(propertyOnSelf));
+}
+
+
+
+void tst_qqmljsscope::builtinTypeResolution_data()
+{
+ QTest::addColumn<bool>("valid");
+ QTest::addColumn<QString>("typeName");
+
+ QTest::addRow("global_QtObject") << true << "Qt";
+ QTest::addRow("function") << true << "function";
+ QTest::addRow("Array") << true << "Array";
+ QTest::addRow("invalid") << false << "foobar";
+ QTest::addRow("Number") << true << "Number";
+ QTest::addRow("bool") << true << "bool";
+ QTest::addRow("QString") << true << "QString";
+}
+
+void tst_qqmljsscope::builtinTypeResolution()
+{
+ QFETCH(bool, valid);
+ QFETCH(QString, typeName);
+
+ QQmlJSImporter importer{ { "data" }, nullptr, true };
+ QStringList defaultImportPaths =
+ QStringList{ QLibraryInfo::path(QLibraryInfo::QmlImportsPath) };
+ importer.setImportPaths(defaultImportPaths);
+ QQmlJSTypeResolver resolver(&importer);
+ const auto &implicitImportDirectory = QQmlJSImportVisitor::implicitImportDirectory({}, nullptr);
+ QQmlJSLogger logger;
+ QQmlJSImportVisitor v{
+ QQmlJSScope::create(), &importer, &logger, implicitImportDirectory, {}
+ };
+
+ PassManagerPtr manager(
+ QQmlSA::PassManagerPrivate::createPassManager(&v, &resolver),
+ &QQmlSA::PassManagerPrivate::deletePassManager);
+
+ TestPass pass{ manager.get() };
+ auto element = pass.resolveBuiltinType(typeName);
+ QCOMPARE(element.isNull(), !valid);
+}
+
+void tst_qqmljsscope::methodAndSignalSourceLocation()
+{
+ QmlIR::Document document(false);
+ auto jsscope = run(u"methodAndSignalSourceLocation.qml"_s, false);
+
+ std::array<std::array<int, 9>, 2> offsetsByLineEnding = {
+ std::array{ 29, 51, 74, 102, 128, 160, 219, 235, 257 }, // 1 char line endings
+ std::array{ 32, 55, 79, 108, 135, 168, 231, 248, 271 } // 2 char line endinds
+ };
+
+ // Try to detect the size of line endings as they lead to different source locations
+ auto offset1 = jsscope->methods("f1")[0].sourceLocation().offset;
+ QVERIFY(offset1 == 29 || offset1 == 32);
+ bool oneCharEndings = offset1 == 29;
+ std::array<int, 9> &offsets = oneCharEndings ? offsetsByLineEnding[0] : offsetsByLineEnding[1];
+
+ using namespace QQmlJS;
+ QCOMPARE(jsscope->methods("f1")[0].sourceLocation(), SourceLocation(offsets[0], 17, 4, 5));
+ QCOMPARE(jsscope->methods("f2")[0].sourceLocation(), SourceLocation(offsets[1], 18, 5, 5));
+ QCOMPARE(jsscope->methods("f3")[0].sourceLocation(), SourceLocation(offsets[2], 23, 6, 5));
+ QCOMPARE(jsscope->methods("f4")[0].sourceLocation(), SourceLocation(offsets[3], 21, 7, 5));
+ QCOMPARE(jsscope->methods("f5")[0].sourceLocation(), SourceLocation(offsets[4], 27, 8, 5));
+ QCOMPARE(jsscope->methods("f6")[0].sourceLocation(), SourceLocation(offsets[5], oneCharEndings ? 53 : 55, 9, 5));
+
+ QCOMPARE(jsscope->methods("s1")[0].sourceLocation(), SourceLocation(offsets[6], 11, 13, 5));
+ QCOMPARE(jsscope->methods("s2")[0].sourceLocation(), SourceLocation(offsets[7], 17, 14, 5));
+ QCOMPARE(jsscope->methods("s3")[0].sourceLocation(), SourceLocation(offsets[8], 28, 15, 5));
+}
+
QTEST_MAIN(tst_qqmljsscope)
#include "tst_qqmljsscope.moc"
diff --git a/tests/auto/qml/qqmllanguage/CMakeLists.txt b/tests/auto/qml/qqmllanguage/CMakeLists.txt
index e07f741bf6..cdab786a2f 100644
--- a/tests/auto/qml/qqmllanguage/CMakeLists.txt
+++ b/tests/auto/qml/qqmllanguage/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmllanguage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmllanguage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmllanguage/data/AliasHolder.qml b/tests/auto/qml/qqmllanguage/data/AliasHolder.qml
new file mode 100644
index 0000000000..42aed6ed26
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/AliasHolder.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ property alias strokeStyle: path.restoreMode
+ property Binding p: Binding { id: path }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnumSelfReference.qml b/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnumSelfReference.qml
new file mode 100644
index 0000000000..0ec43bf7aa
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/CompositeTypeWithEnumSelfReference.qml
@@ -0,0 +1,8 @@
+import QtQml
+import Test
+
+QtObject {
+ enum A { B, C, D }
+ property int e: CompositeTypeWithEnumSelfReference.C
+ property int f: CompositeTypeWithEnumSelfReference.A.D
+}
diff --git a/tests/auto/qml/qqmllanguage/data/Comps/IconPropertiesGroup.qml b/tests/auto/qml/qqmllanguage/data/Comps/IconPropertiesGroup.qml
new file mode 100644
index 0000000000..232c755bfb
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/Comps/IconPropertiesGroup.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+QtObject {
+ function dothing() { console.log("do") }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/Comps/OverlayDrawer.qml b/tests/auto/qml/qqmllanguage/data/Comps/OverlayDrawer.qml
new file mode 100644
index 0000000000..713c760a04
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/Comps/OverlayDrawer.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property IconPropertiesGroup handleOpenIcon: IconPropertiesGroup {}
+}
diff --git a/tests/auto/qml/qqmllanguage/data/Comps/qmldir b/tests/auto/qml/qqmllanguage/data/Comps/qmldir
new file mode 100644
index 0000000000..0a68a376f6
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/Comps/qmldir
@@ -0,0 +1,4 @@
+module Comps
+OverlayDrawer 254.0 OverlayDrawer.qml
+IconPropertiesGroup 254.0 IconPropertiesGroup.qml
+
diff --git a/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml b/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml
new file mode 100644
index 0000000000..134eacf913
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml
@@ -0,0 +1,27 @@
+import QtQml
+
+QtObject {
+ id: root
+ objectName: "theRoot"
+
+ component ObjectWithColor: QtObject {
+ property string color
+ property var varvar
+ }
+
+ property ObjectWithColor border: ObjectWithColor {
+ objectName: root.objectName
+ color: root.trueBorderColor
+ varvar: root.trueBorderVarvar
+ }
+
+ readonly property rect readonlyRect: ({x: 12, y: 13, width: 14, height: 15})
+
+ property alias borderObjectName: root.border.objectName
+ property alias borderColor: root.border.color
+ property alias borderVarvar: root.border.varvar
+ property alias readonlyRectX: root.readonlyRect.x
+
+ property string trueBorderColor: "green"
+ property var trueBorderVarvar: 1234
+}
diff --git a/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle1.qml b/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle1.qml
new file mode 100644
index 0000000000..6186faa00b
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle1.qml
@@ -0,0 +1,15 @@
+import QtQml
+
+QtObject {
+ id: self
+ property QtObject b
+ property Component c
+ function a() : TypeAnnotationCycle2 { return c.createObject() as TypeAnnotationCycle2 }
+
+ Component.onCompleted: {
+ c = Qt.createComponent("TypeAnnotationCycle2.qml");
+ let v = a();
+ v.addTypeAnnotationCycle1(self as TypeAnnotationCycle1);
+ b = v.b;
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle2.qml b/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle2.qml
new file mode 100644
index 0000000000..9e3ffa86d2
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/TypeAnnotationCycle2.qml
@@ -0,0 +1,6 @@
+import QtQml
+
+QtObject {
+ property QtObject b
+ function addTypeAnnotationCycle1(c: TypeAnnotationCycle1) { b = c; }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/UIToolBar.qml b/tests/auto/qml/qqmllanguage/data/UIToolBar.qml
new file mode 100644
index 0000000000..08a22d2492
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/UIToolBar.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ id: root
+ signal doneClicked()
+ signal foo()
+
+ onObjectNameChanged: foo()
+ Component.onCompleted: root.foo.connect(root.doneClicked)
+}
diff --git a/tests/auto/qml/qqmllanguage/data/alias.16.qml b/tests/auto/qml/qqmllanguage/data/alias.16.qml
index 4637aec58f..335d240003 100644
--- a/tests/auto/qml/qqmllanguage/data/alias.16.qml
+++ b/tests/auto/qml/qqmllanguage/data/alias.16.qml
@@ -2,7 +2,7 @@ import QtQuick 2.0
import QtQuick.Window 2.0
Window {
- visible: true
+ visible: false
property alias list: repeater.model
diff --git a/tests/auto/qml/qqmllanguage/data/aliasWriter.qml b/tests/auto/qml/qqmllanguage/data/aliasWriter.qml
new file mode 100644
index 0000000000..4001c2af34
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/aliasWriter.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+AliasHolder {
+ strokeStyle: 1
+}
diff --git a/tests/auto/qml/qqmllanguage/data/ambiguousComponents.qml b/tests/auto/qml/qqmllanguage/data/ambiguousComponents.qml
new file mode 100644
index 0000000000..64c31b46d6
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/ambiguousComponents.qml
@@ -0,0 +1,16 @@
+import QtQuick
+import Comps as Comps
+
+Comps.OverlayDrawer {
+ id: self
+
+ property var dothing
+
+ Component.onCompleted: dothing = handleOpenIcon.dothing
+
+ function dodo() { dothing() }
+
+ function testInstanceOf() : bool {
+ return self instanceof Comps.OverlayDrawer
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/asCastToInlineComponent.qml b/tests/auto/qml/qqmllanguage/data/asCastToInlineComponent.qml
new file mode 100644
index 0000000000..428ccd5eef
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/asCastToInlineComponent.qml
@@ -0,0 +1,16 @@
+import QtQml
+
+QtObject {
+ id: root
+
+ component MyItem: QtObject {
+ property int value: 10
+ onValueChanged: root.objectName = "value: " + value
+ }
+
+ property Instantiator i: Instantiator {
+ id: loader
+ delegate: MyItem {}
+ onObjectChanged: (loader.object as MyItem).value = 20
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/asValueType.qml b/tests/auto/qml/qqmllanguage/data/asValueType.qml
new file mode 100644
index 0000000000..6a5500e344
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/asValueType.qml
@@ -0,0 +1,13 @@
+pragma ValueTypeBehavior: Addressable
+import QtQml
+import StaticTest
+
+QtObject {
+ property var a
+ property rect b: a as rect
+ property bool c: a instanceof rect
+ property bool d: ({x: 10, y: 20}) instanceof point
+ property var e: ({x: 10, y: 20}) as point
+ property var f: "red" as withString
+ property var g: "green" as string
+}
diff --git a/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml b/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml
new file mode 100644
index 0000000000..6f0db53f2a
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml
@@ -0,0 +1,25 @@
+import QtQml
+
+QtObject {
+ id: self
+
+ function doStuff(status: Binding.NotAnInlineComponent) : int {
+ return status
+ }
+
+ function doStuff2(status: InlineComponentBase.IC) : QtObject {
+ return status
+ }
+
+ function doStuff3(status: InlineComponentBase.NotIC) : QtObject {
+ return status
+ }
+
+ property InlineComponentBase.IC ic: InlineComponentBase.IC {}
+
+ property int a: doStuff(5)
+ property QtObject b: doStuff2(ic)
+ property QtObject c: doStuff3(ic)
+ property QtObject d: doStuff2(self)
+}
+
diff --git a/tests/auto/qml/qqmllanguage/data/SingletonTest.qml b/tests/auto/qml/qqmllanguage/data/badSingleton/SingletonTest.qml
index 70e1671754..70e1671754 100644
--- a/tests/auto/qml/qqmllanguage/data/SingletonTest.qml
+++ b/tests/auto/qml/qqmllanguage/data/badSingleton/SingletonTest.qml
diff --git a/tests/auto/qml/qqmllanguage/data/qmldir b/tests/auto/qml/qqmllanguage/data/badSingleton/qmldir
index c946de657c..c946de657c 100644
--- a/tests/auto/qml/qqmllanguage/data/qmldir
+++ b/tests/auto/qml/qqmllanguage/data/badSingleton/qmldir
diff --git a/tests/auto/qml/qqmllanguage/data/qtbug_85932.qml b/tests/auto/qml/qqmllanguage/data/badSingleton/qtbug_85932.qml
index aa21558220..aa21558220 100644
--- a/tests/auto/qml/qqmllanguage/data/qtbug_85932.qml
+++ b/tests/auto/qml/qqmllanguage/data/badSingleton/qtbug_85932.qml
diff --git a/tests/auto/qml/qqmllanguage/data/corpseInQmlList.qml b/tests/auto/qml/qqmllanguage/data/corpseInQmlList.qml
new file mode 100644
index 0000000000..dc0e145064
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/corpseInQmlList.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+QtObject {
+ property var b;
+
+ function returnList(a: QtObject) : list<QtObject> {
+ return [a]
+ }
+
+ function setB(a: QtObject) {
+ b = { b: returnList(a) }
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml b/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml
new file mode 100644
index 0000000000..50eaa7c3e2
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+DeepAliasOnIC {
+ borderObjectName: "theLeaf"
+ borderColor: "black"
+ borderVarvar: "mauve"
+}
+
+
diff --git a/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml b/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml
new file mode 100644
index 0000000000..f5ae62406b
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+DeepAliasOnIC {
+ readonlyRectX: 55
+}
diff --git a/tests/auto/qml/qqmllanguage/data/derivedFromUnexposedBase.qml b/tests/auto/qml/qqmllanguage/data/derivedFromUnexposedBase.qml
new file mode 100644
index 0000000000..b508474a36
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/derivedFromUnexposedBase.qml
@@ -0,0 +1,6 @@
+import Test
+
+DerivedFromUnexposedBase {
+ group.value: 42
+ groupGadget.value: 42
+}
diff --git a/tests/auto/qml/qqmllanguage/data/dynamicGroupPropertyRejected.qml b/tests/auto/qml/qqmllanguage/data/dynamicGroupPropertyRejected.qml
new file mode 100644
index 0000000000..2fffea25c6
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/dynamicGroupPropertyRejected.qml
@@ -0,0 +1,5 @@
+import Test
+
+DerivedFromUnexposedBase {
+ dynamic.value: "This should fail"
+}
diff --git a/tests/auto/qml/qqmllanguage/data/enumPropsManyUnderlyingTypes.qml b/tests/auto/qml/qqmllanguage/data/enumPropsManyUnderlyingTypes.qml
new file mode 100644
index 0000000000..b713d2aa24
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/enumPropsManyUnderlyingTypes.qml
@@ -0,0 +1,10 @@
+import Test
+
+EnumPropsManyUnderlyingTypes {
+ si8prop: EnumPropsManyUnderlyingTypes.ResolvedValue
+ ui8prop: EnumPropsManyUnderlyingTypes.ResolvedValue
+ si16prop: EnumPropsManyUnderlyingTypes.ResolvedValue
+ ui16prop: EnumPropsManyUnderlyingTypes.ResolvedValue
+ si64prop: EnumPropsManyUnderlyingTypes.ResolvedValue
+ ui64prop: EnumPropsManyUnderlyingTypes.ResolvedValue
+}
diff --git a/tests/auto/qml/qqmllanguage/data/enumScopes.qml b/tests/auto/qml/qqmllanguage/data/enumScopes.qml
new file mode 100644
index 0000000000..c71872387f
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/enumScopes.qml
@@ -0,0 +1,16 @@
+import QtQml 2.15
+import EnumScopeTest 1.0
+
+QtObject {
+ property NonSingleton n: NonSingleton {
+ id: nonSingleton
+ }
+
+ property bool singletonUnscoped: Singleton.enumProperty === Singleton.EnumValue2
+ property bool singletonScoped: Singleton.enumProperty === Singleton.EnumType.EnumValue2
+ property bool nonSingletonUnscoped: nonSingleton.enumProperty === NonSingleton.EnumValue2
+ property bool nonSingletonScoped: nonSingleton.enumProperty === NonSingleton.EnumType.EnumValue2
+
+ property int singletonScopedValue: EnumProviderSingleton.Expected.Value
+ property int singletonUnscopedValue: EnumProviderSingleton.Value
+}
diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml
new file mode 100644
index 0000000000..902dfb501d
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/inlineComponentWithImplicitComponent.qml
@@ -0,0 +1,27 @@
+import QtQml
+
+QtObject {
+ component C1: QtObject {
+ property Component comp: null
+ }
+
+ component C2: C1 {
+ comp: QtObject {
+ objectName: "green"
+ }
+ }
+
+ component C3: C1 {
+ comp: Component {
+ QtObject {
+ objectName: "blue"
+ }
+ }
+ }
+
+ property QtObject c1: C1 {}
+ property QtObject c2: C2 {}
+ property QtObject c3: C3 {}
+
+ objectName: c2.comp.createObject().objectName + " " + c3.comp.createObject().objectName
+}
diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt
index d76f18ba89..5ddb8a2e6d 100644
--- a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt
@@ -1 +1 @@
-5:5:Invalid grouped property access: Property "o" with type "QVariant", which is not a value type
+5:7:Cannot assign to non-existent property "blah"
diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt
index 9a0422753f..ced96fba83 100644
--- a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt
@@ -1 +1 @@
-4:5:Invalid grouped property access: Property "customType" with type "MyCustomVariantType", which is not a value type
+4:5:Invalid grouped property access: Property "customType" with type "MyCustomVariantType", which is neither a value nor an object type
diff --git a/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_interpreter.qml b/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_interpreter.qml
new file mode 100644
index 0000000000..460d4667f8
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_interpreter.qml
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ id: self
+ property int a: 3
+ property var result
+ Component.onCompleted: {
+ var sum = 0
+ let f = function() {
+ return self.notthere ?? self.a
+ }
+
+ // Not enough times for the jit to kick in (should run on the interpreter)
+ for (let i = 0; i < 1; i++) {
+ sum = sum + f()
+ }
+ result = sum
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_jit.qml b/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_jit.qml
new file mode 100644
index 0000000000..43c82f436c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/isNullOrUndefined_jit.qml
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ id: self
+ property int a: 3
+ property int result
+ Component.onCompleted: {
+ var sum = 0
+ let f = function() {
+ return self.notthere ?? self.a
+ }
+
+ // Enough times for the jit to kick in (should run on the jit)
+ for (let i = 0; i < 50; i++) {
+ sum = sum + f()
+ }
+ result = sum
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/jitExceptions.qml b/tests/auto/qml/qqmllanguage/data/jitExceptions.qml
new file mode 100644
index 0000000000..c5e11af31d
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/jitExceptions.qml
@@ -0,0 +1,16 @@
+import QtQml
+
+QtObject {
+ function burn() {
+ return control.font
+ }
+
+ Component.onCompleted: {
+ for (var a = 0; a < 10; ++a) {
+ try { burn() } catch(e) {}
+ }
+
+ burn();
+ }
+
+}
diff --git a/tests/auto/qml/qqmllanguage/data/longConversion.qml b/tests/auto/qml/qqmllanguage/data/longConversion.qml
new file mode 100644
index 0000000000..fd9a9518ee
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/longConversion.qml
@@ -0,0 +1,28 @@
+import QtQml
+import Test
+
+QtObject {
+ property bool testProp: GetterObject.getFalse()
+ property bool testQProp: GetterObject.getQFalse()
+
+ property bool fromLocal
+ property bool fromQLocal
+
+ property bool fromBoolean
+ property bool fromQBoolean
+
+ Component.onCompleted: {
+ let l = GetterObject.getFalse();
+ fromLocal = l;
+
+ let b = Boolean(l);
+ fromBoolean = b;
+
+ let ql = GetterObject.getQFalse();
+ fromQLocal = ql;
+
+
+ let qb = Boolean(ql);
+ fromQBoolean = qb;
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/manuallyCallSignalHandler.qml b/tests/auto/qml/qqmllanguage/data/manuallyCallSignalHandler.qml
new file mode 100644
index 0000000000..1ee71e5fd2
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/manuallyCallSignalHandler.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+QtObject {
+ Component.onDestruction: {
+ console.log("evil!");
+ }
+
+ Component.onCompleted: {
+ Component.onDestruction()
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml
index acd5463a3c..ac5622f9fb 100644
--- a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml
+++ b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.1.qml
@@ -10,7 +10,7 @@ Item {
}
}
- property bool expectNull: null
+ property bool expectNull: { return null; }
function setExpectNull(b) {
success = false;
diff --git a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml
index ed0e0d10f0..3c18739c32 100644
--- a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml
+++ b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.2.qml
@@ -10,7 +10,7 @@ Item {
}
}
- property bool expectNull: null
+ property bool expectNull: { return null; }
function setExpectNull(b) {
success = false;
diff --git a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml
index f5e94ba715..e2e560199f 100644
--- a/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml
+++ b/tests/auto/qml/qqmllanguage/data/objectDeletionNotify.3.qml
@@ -10,7 +10,7 @@ Item {
}
}
- property bool expectNull: null
+ property bool expectNull: { return null; }
function setExpectNull(b) {
success = false;
diff --git a/tests/auto/qml/qqmllanguage/data/objectInList.qml b/tests/auto/qml/qqmllanguage/data/objectInList.qml
new file mode 100644
index 0000000000..53c8c3cdd1
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/objectInList.qml
@@ -0,0 +1,17 @@
+import QtQml
+
+QtObject {
+ objectName: "parent"
+ property list<QtObject> child
+ property Component c: QtObject { objectName: "child" }
+
+ function doCreate() {
+ child.push(c.createObject(null));
+ }
+
+ Component.onCompleted: {
+ // Extra function call so that the created object cannot be on the stack
+ doCreate();
+ gc();
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/objectMethodClone.qml b/tests/auto/qml/qqmllanguage/data/objectMethodClone.qml
new file mode 100644
index 0000000000..e21179ea14
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/objectMethodClone.qml
@@ -0,0 +1,23 @@
+import QtQml
+
+QtObject {
+ id: window
+
+ property int doneClicks: 0
+
+ property UIToolBar t1: UIToolBar {
+ objectName: window.objectName
+ onDoneClicked: window.doneClicks++
+ }
+
+ property UIToolBar t2: UIToolBar {
+ objectName: window.objectName
+ onDoneClicked: window.doneClicks++
+ }
+
+ property Timer timer: Timer {
+ interval: 10
+ running: true
+ onTriggered: window.objectName = "bar"
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/optionalChainCallOnNullProperty.qml b/tests/auto/qml/qqmllanguage/data/optionalChainCallOnNullProperty.qml
new file mode 100644
index 0000000000..00029e3953
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/optionalChainCallOnNullProperty.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ id: root
+ property QtObject target: null
+
+ Component.onCompleted: {
+ target?.destroy( )
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml b/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml
new file mode 100644
index 0000000000..69f9316c51
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property list<var> data: []
+ Item {}
+}
diff --git a/tests/auto/qml/qqmllanguage/data/retainThis.qml b/tests/auto/qml/qqmllanguage/data/retainThis.qml
new file mode 100644
index 0000000000..7a372ee236
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/retainThis.qml
@@ -0,0 +1,49 @@
+import QmlOtherThis
+import QtQml
+
+QtObject {
+ property var cppMethod: objA.greet
+ property var cppMethod2: objA.sum
+
+ property Greeter a: Greeter { id: objA; objectName: "objA" }
+ property Greeter b: Greeter { id: objB; objectName: "objB" }
+
+ function doCall() {
+ cppMethod.call(objB)
+ cppMethod2(5, 6)
+ }
+
+ property var cppMethod3;
+ function doRetrieve(g) {
+ cppMethod3 = g.greet;
+ }
+
+ function doCall2() {
+ cppMethod3();
+ }
+
+ property Greeter c: Greeter {
+ id: objC
+ objectName: "objC"
+
+ property var cppMethod: objC.sum
+
+ function doCall() {
+ cppMethod(7, 7)
+ }
+ }
+
+ Component.onCompleted: {
+ doCall();
+ doCall();
+
+ doRetrieve(objA);
+ doCall2();
+ doRetrieve(objB);
+ doCall2();
+
+ objC.doCall();
+ objC.cppMethod = objB.sum;
+ objC.doCall();
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/signatureEnforced.qml b/tests/auto/qml/qqmllanguage/data/signatureEnforced.qml
index e2ddd75b55..ec00d5d2b3 100644
--- a/tests/auto/qml/qqmllanguage/data/signatureEnforced.qml
+++ b/tests/auto/qml/qqmllanguage/data/signatureEnforced.qml
@@ -8,7 +8,7 @@ QtObject {
property withLength withLength: 5
function a(r: rect) {
- r.x = 77 // does not write back
+ r.x = 77 // does write back, but this is an evil thing to do.
}
function b(s: string) : int {
@@ -28,8 +28,11 @@ QtObject {
property int n: c(99) // creates a withLength
property int o: rect.y
+ function bad(b: int) { return b }
+
Component.onCompleted: {
a(rect)
d(rect)
+ bad(15)
}
}
diff --git a/tests/auto/qml/qqmllanguage/data/signatureIgnored.qml b/tests/auto/qml/qqmllanguage/data/signatureIgnored.qml
index 0e9d0f42dc..bdb373040d 100644
--- a/tests/auto/qml/qqmllanguage/data/signatureIgnored.qml
+++ b/tests/auto/qml/qqmllanguage/data/signatureIgnored.qml
@@ -1,6 +1,6 @@
+pragma FunctionSignatureBehavior: Ignored
import StaticTest
import QtQml
-
QtObject {
property rect rect: ({ x: 12, y: 13 })
property withLength withLength: 5
diff --git a/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml b/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml
index 5359157b31..b86477e40a 100644
--- a/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml
+++ b/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
pragma Singleton
diff --git a/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js b/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js
index 6e28624bb0..50ed2b0e66 100644
--- a/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js
+++ b/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js
@@ -1,5 +1,5 @@
// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
.pragma library
diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest17.qml b/tests/auto/qml/qqmllanguage/data/singletonTest17.qml
index 4a987e31c0..7c7bffa4ac 100644
--- a/tests/auto/qml/qqmllanguage/data/singletonTest17.qml
+++ b/tests/auto/qml/qqmllanguage/data/singletonTest17.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import org.qtproject.Test 1.0
diff --git a/tests/auto/qml/qqmllanguage/data/typedObjectList.qml b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
new file mode 100644
index 0000000000..7e6f6e8dd9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ property var b;
+ property Component c: QtObject {}
+
+ function returnList(a: Component) : list<Component> { return [a] }
+
+ Component.onCompleted: b = { b: returnList(c) }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/unregisteredValueTypeConversion.qml b/tests/auto/qml/qqmllanguage/data/unregisteredValueTypeConversion.qml
new file mode 100644
index 0000000000..70484d3f0e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/unregisteredValueTypeConversion.qml
@@ -0,0 +1,10 @@
+import QtQml
+import Test
+
+UnregisteredValueTypeHandler {
+ Component.onCompleted: {
+ consume(produce())
+ consume(produceDerived())
+ consume(produceGadgeted())
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/variantObjectList.qml b/tests/auto/qml/qqmllanguage/data/variantObjectList.qml
new file mode 100644
index 0000000000..9ec7d4f90f
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/variantObjectList.qml
@@ -0,0 +1,17 @@
+import QtQml
+import People
+
+QtObject {
+ id: root
+
+ property QtObject b: QtObject { id: g1; objectName: "Leo Hodges" }
+ property QtObject c: QtObject { id: g2; objectName: "Jack Smith" }
+ property QtObject d: QtObject { id: g3; objectName: "Anne Brown" }
+
+ property Component pc: Component {
+ id: partyComp
+ BirthdayParty {}
+ }
+
+ property BirthdayParty q: partyComp.createObject(root, { guests: [g1, g2, g3] })
+}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index f571429b07..ffff0a6979 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include "testtypes.h"
#include <private/qv4qmlcontext_p.h>
@@ -55,6 +56,8 @@ void registerTypes()
qmlRegisterTypeNotAvailable("Test",1,0,"UnavailableType", "UnavailableType is unavailable for testing");
+ qmlRegisterTypesAndRevisions<DerivedFromUnexposedBase>("Test", 1);
+
qmlRegisterType<MyQmlObject>("Test.Version",1,0,"MyQmlObject");
qmlRegisterType<MyTypeObject>("Test.Version",1,0,"MyTypeObject");
qmlRegisterType<MyTypeObject>("Test.Version",2,0,"MyTypeObject");
@@ -92,6 +95,8 @@ void registerTypes()
qmlRegisterType<MyArrayBufferTestClass>("Test", 1, 0, "MyArrayBufferTestClass");
+ qmlRegisterTypesAndRevisions<EnumPropsManyUnderlyingTypes>("Test", 1);
+
qmlRegisterType<LazyDeferredSubObject>("Test", 1, 0, "LazyDeferredSubObject");
qmlRegisterType<DeferredProperties>("Test", 1, 0, "DeferredProperties");
qmlRegisterType<ImmediateProperties>("Test", 1, 0, "ImmediateProperties");
@@ -148,6 +153,27 @@ void registerTypes()
qmlRegisterTypesAndRevisions<BaseValueType>("ValueTypes", 1);
qmlRegisterTypesAndRevisions<DerivedValueType>("ValueTypes", 1);
+ qmlRegisterTypesAndRevisions<GetterObject>("Test", 1);
+
+ qmlRegisterNamespaceAndRevisions(&TypedEnums::staticMetaObject, "TypedEnums", 1);
+ qmlRegisterTypesAndRevisions<ObjectWithEnums>("TypedEnums", 1);
+ qmlRegisterTypesAndRevisions<GadgetWithEnums>("TypedEnums", 1);
+
+ QMetaType::registerConverter<UnregisteredValueDerivedType, UnregisteredValueBaseType>();
+ qmlRegisterTypesAndRevisions<UnregisteredValueTypeHandler>("Test", 1);
+
+ qmlRegisterTypesAndRevisions<Greeter>("QmlOtherThis", 1);
+ qmlRegisterTypesAndRevisions<BirthdayParty>("People", 1);
+ qmlRegisterTypesAndRevisions<AttachedInCtor>("Test", 1);
+
+ qmlRegisterTypesAndRevisions<ByteArrayReceiver>("Test", 1);
+
+ qmlRegisterTypesAndRevisions<Counter>("Test", 1);
+
+ qmlRegisterTypesAndRevisions<Singleton>("EnumScopeTest", 1);
+ qmlRegisterTypesAndRevisions<NonSingleton>("EnumScopeTest", 1);
+ qmlRegisterTypesAndRevisions<EnumProviderSingletonQml>("EnumScopeTest", 1);
+
}
QVariant myCustomVariantTypeConverter(const QString &data)
@@ -170,7 +196,7 @@ void CustomBinding::componentComplete()
{
Q_ASSERT(m_target);
- foreach (const QV4::CompiledData::Binding *binding, bindings) {
+ for (const QV4::CompiledData::Binding *binding : std::as_const(bindings)) {
QString name = compilationUnit->stringAt(binding->propertyNameIndex);
int bindingId = binding->value.compiledScriptIndex;
@@ -248,3 +274,19 @@ UncreatableSingleton *UncreatableSingleton::instance()
static UncreatableSingleton instance;
return &instance;
}
+
+QT_BEGIN_NAMESPACE
+const QMetaObject *QtPrivate::MetaObjectForType<FakeDynamicObject *, void>::metaObjectFunction(const QMetaTypeInterface *)
+{
+ static auto ptr = []{
+ QMetaObjectBuilder builder(&FakeDynamicObject::staticMetaObject);
+ builder.setFlags(DynamicMetaObject);
+ auto mo = builder.toMetaObject();
+ QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, [mo]() {
+ delete mo;
+ });
+ return mo;
+ }();
+ return ptr;
+}
+QT_END_NAMESPACE
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index 0777ed103d..bcf02c1cf9 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
@@ -18,6 +18,8 @@
#include <QtQml/qqmlpropertyvaluesource.h>
#include <QtQml/qqmlscriptstring.h>
#include <QtQml/qqmlproperty.h>
+
+#include <private/qqmlcomponentattached_p.h>
#include <private/qqmlcustomparser_p.h>
QVariant myCustomVariantTypeConverter(const QString &data);
@@ -42,6 +44,77 @@ struct MyCustomVariantType
};
Q_DECLARE_METATYPE(MyCustomVariantType);
+
+class Group : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int value MEMBER value)
+
+public:
+ Group(QObject *parent = nullptr) : QObject(parent) {}
+ int value = 0;
+};
+
+
+struct GroupGadget
+{
+ Q_GADGET
+
+ Q_PROPERTY(int value MEMBER value)
+
+public:
+ friend bool operator==(GroupGadget g1, GroupGadget g2) { return g1.value == g2.value; }
+ friend bool operator!=(GroupGadget g1, GroupGadget g2) { return !(g1 == g2); }
+ int value = 0;
+};
+
+struct FakeDynamicObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString value MEMBER value)
+
+public:
+ FakeDynamicObject() {}
+ QString value;
+};
+
+QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+// don't do this at home – we override the meta-object which QMetaType collects for
+// FakeDynamicObject* properties
+template<>
+struct MetaObjectForType<FakeDynamicObject *, void>
+{
+ static const QMetaObject *metaObjectFunction(const QMetaTypeInterface *);
+};
+}
+QT_END_NAMESPACE
+
+class UnexposedBase : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(Group *group MEMBER group)
+ Q_PROPERTY(GroupGadget groupGadget MEMBER groupGadget)
+ Q_PROPERTY(FakeDynamicObject *dynamic MEMBER dynamic)
+public:
+ UnexposedBase(QObject *parent = nullptr) : QObject(parent)
+ {
+ group = new Group(this);
+ }
+ Group *group;
+ GroupGadget groupGadget;
+ FakeDynamicObject *dynamic = nullptr;
+};
+
+class DerivedFromUnexposedBase : public UnexposedBase
+{
+ Q_OBJECT
+ QML_ELEMENT
+};
+
+
class MyAttachedObject : public QObject
{
Q_OBJECT
@@ -1292,6 +1365,40 @@ public:
}
};
+class EnumPropsManyUnderlyingTypes : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ enum si8 : qint8 { ResolvedValue = 1};
+ enum ui8 : quint8 {};
+ enum si16 : qint16 {};
+ enum ui16 : quint16 {};
+ enum ui64 : qint64 {};
+ enum si64 : quint64 {};
+ Q_ENUM(si8)
+ Q_ENUM(ui8)
+ Q_ENUM(si16)
+ Q_ENUM(ui16)
+ Q_ENUM(si64)
+ Q_ENUM(ui64)
+
+
+ Q_PROPERTY(si8 si8prop MEMBER si8prop)
+ Q_PROPERTY(ui8 ui8prop MEMBER ui8prop)
+ Q_PROPERTY(si16 si16prop MEMBER si16prop)
+ Q_PROPERTY(ui16 ui16prop MEMBER ui16prop)
+ Q_PROPERTY(si64 si64prop MEMBER si64prop)
+ Q_PROPERTY(ui64 ui64prop MEMBER ui64prop)
+
+ si8 si8prop = si8(0);
+ ui8 ui8prop = ui8(0);
+ si16 si16prop = si16(0);
+ ui16 ui16prop = ui16(0);
+ si64 si64prop = si64(0);
+ ui64 ui64prop = ui64(0);
+};
+
Q_DECLARE_METATYPE(MyEnum2Class::EnumB)
Q_DECLARE_METATYPE(MyEnum1Class::EnumA)
Q_DECLARE_METATYPE(Qt::TextFormat)
@@ -1547,6 +1654,7 @@ class BareSingleton : public QObject
Q_OBJECT
QML_SINGLETON
QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 0)
public:
BareSingleton(QObject *parent = nullptr) : QObject(parent)
@@ -1560,6 +1668,7 @@ class UncreatableSingleton : public QObject
Q_OBJECT
QML_SINGLETON
QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 0)
public:
static UncreatableSingleton *instance();
@@ -2386,6 +2495,32 @@ public:
}
};
+
+struct ForeignNamespace
+{
+ Q_GADGET
+public:
+ enum Abc { A, B, C, D };
+ Q_ENUM(Abc)
+};
+
+class ForeignNamespaceForeign
+{
+ Q_GADGET
+ QML_ELEMENT
+ QML_FOREIGN_NAMESPACE(ForeignNamespace)
+};
+
+class LeakingForeignNamespaceForeign : public QObject, public ForeignNamespaceForeign
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ enum AnotherAbc { D, C, B, A };
+ Q_ENUM(AnotherAbc)
+};
+
struct ValueTypeWithLength
{
Q_GADGET
@@ -2405,4 +2540,406 @@ private:
int m_length = 19;
};
+struct ValueTypeWithString
+{
+ Q_GADGET
+ QML_VALUE_TYPE(withString)
+ QML_CONSTRUCTIBLE_VALUE
+
+public:
+ Q_INVOKABLE ValueTypeWithString(const QString &v = QString()) : m_string(v) {}
+ QString toString() const { return m_string; }
+
+private:
+ QString m_string;
+};
+
+class GetterObject : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+public:
+ explicit GetterObject(QObject *parent = nullptr) : QObject{parent} {}
+
+ // always returns a 0 as uint64_t
+ Q_INVOKABLE uint64_t getFalse() const { return 0; }
+ Q_INVOKABLE uint64_t getTrue() const { return 1; }
+
+ Q_INVOKABLE quint64 getQFalse() const { return 0; }
+ Q_INVOKABLE quint64 getQTrue() const { return 1; }
+};
+
+class EnumProviderSingleton : public QObject {
+ Q_OBJECT
+
+public:
+ enum class Expected {
+ Value = 42
+ };
+ Q_ENUM(Expected)
+
+ EnumProviderSingleton(QObject* parent = nullptr) : QObject(parent) {}
+};
+
+class EnumProviderSingletonQml {
+ Q_GADGET
+ QML_FOREIGN(EnumProviderSingleton)
+ QML_NAMED_ELEMENT(EnumProviderSingleton)
+ QML_SINGLETON
+
+public:
+ static EnumProviderSingleton* create(QQmlEngine*, QJSEngine*) {
+ return new EnumProviderSingleton();
+ }
+
+private:
+ EnumProviderSingletonQml() = default;
+};
+
+
+
+namespace TypedEnums {
+Q_NAMESPACE
+QML_ELEMENT
+
+enum E8S : qint8 {
+ E8SA = std::numeric_limits<qint8>::min(),
+ E8SB = -5,
+ E8SC = -1,
+ E8SD = 0,
+ E8SE = 1,
+ E8SF = 5,
+ E8SG = std::numeric_limits<qint8>::max(),
+};
+Q_ENUM_NS(E8S);
+
+enum E8U : quint8 {
+ E8UA = 0,
+ E8UB = 1,
+ E8UC = 5,
+ E8UD = 1 << 7,
+ E8UE = std::numeric_limits<quint8>::max(),
+};
+Q_ENUM_NS(E8U);
+
+enum E16S : qint16 {
+ E16SA = std::numeric_limits<qint16>::min(),
+ E16SB = -5,
+ E16SC = -1,
+ E16SD = 0,
+ E16SE = 1,
+ E16SF = 5,
+ E16SG = std::numeric_limits<qint16>::max(),
+};
+Q_ENUM_NS(E16S);
+
+enum E16U : quint16 {
+ E16UA = 0,
+ E16UB = 1,
+ E16UC = 5,
+ E16UD = 1 << 15,
+ E16UE = std::numeric_limits<quint16>::max(),
+};
+Q_ENUM_NS(E16U);
+
+enum E32S : qint32 {
+ E32SA = std::numeric_limits<qint32>::min(),
+ E32SB = -5,
+ E32SC = -1,
+ E32SD = 0,
+ E32SE = 1,
+ E32SF = 5,
+ E32SG = std::numeric_limits<qint32>::max(),
+};
+Q_ENUM_NS(E32S);
+
+enum E32U : quint32 {
+ E32UA = 0,
+ E32UB = 1,
+ E32UC = 5,
+ E32UD = 1u << 31,
+ E32UE = std::numeric_limits<quint32>::max(),
+};
+Q_ENUM_NS(E32U);
+
+enum E64S : qint64 {
+ E64SA = std::numeric_limits<qint64>::min(),
+ E64SB = -5,
+ E64SC = -1,
+ E64SD = 0,
+ E64SE = 1,
+ E64SF = 5,
+ E64SG = std::numeric_limits<qint64>::max(),
+};
+Q_ENUM_NS(E64S);
+
+enum E64U : quint64 {
+ E64UA = 0,
+ E64UB = 1,
+ E64UC = 5,
+ E64UD = 1ull << 63,
+ E64UE = std::numeric_limits<quint64>::max(),
+};
+Q_ENUM_NS(E64U);
+}
+
+class GadgetWithEnums
+{
+ Q_GADGET
+ QML_VALUE_TYPE(gadgetWithEnums)
+ Q_PROPERTY(TypedEnums::E8S e8s MEMBER m_e8s);
+ Q_PROPERTY(TypedEnums::E8U e8u MEMBER m_e8u);
+ Q_PROPERTY(TypedEnums::E16S e16s MEMBER m_e16s);
+ Q_PROPERTY(TypedEnums::E16U e16u MEMBER m_e16u);
+ Q_PROPERTY(TypedEnums::E32S e32s MEMBER m_e32s);
+ Q_PROPERTY(TypedEnums::E32U e32u MEMBER m_e32u);
+ Q_PROPERTY(TypedEnums::E64S e64s MEMBER m_e64s);
+ Q_PROPERTY(TypedEnums::E64U e64u MEMBER m_e64u);
+public:
+ TypedEnums::E8S m_e8s = {};
+ TypedEnums::E8U m_e8u = {};
+ TypedEnums::E16S m_e16s = {};
+ TypedEnums::E16U m_e16u = {};
+ TypedEnums::E32S m_e32s = {};
+ TypedEnums::E32U m_e32u = {};
+ TypedEnums::E64S m_e64s = {};
+ TypedEnums::E64U m_e64u = {};
+private:
+ friend bool operator==(const GadgetWithEnums &a, const GadgetWithEnums &b)
+ {
+ return a.m_e8s == b.m_e8s && a.m_e8u == b.m_e8u && a.m_e16s == b.m_e16s
+ && a.m_e16u == b.m_e16u && a.m_e32s == b.m_e32s && a.m_e32u == b.m_e32u
+ && a.m_e64s == b.m_e64s && a.m_e64u == b.m_e64u;
+ }
+ friend bool operator!=(const GadgetWithEnums &a, const GadgetWithEnums &b)
+ {
+ return !(a == b);
+ }
+};
+
+class ObjectWithEnums : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(TypedEnums::E8S e8s MEMBER m_e8s NOTIFY changed);
+ Q_PROPERTY(TypedEnums::E8U e8u MEMBER m_e8u NOTIFY changed);
+ Q_PROPERTY(TypedEnums::E16S e16s MEMBER m_e16s NOTIFY changed);
+ Q_PROPERTY(TypedEnums::E16U e16u MEMBER m_e16u NOTIFY changed);
+ Q_PROPERTY(TypedEnums::E32S e32s MEMBER m_e32s NOTIFY changed);
+ Q_PROPERTY(TypedEnums::E32U e32u MEMBER m_e32u NOTIFY changed);
+ Q_PROPERTY(TypedEnums::E64S e64s MEMBER m_e64s NOTIFY changed);
+ Q_PROPERTY(TypedEnums::E64U e64u MEMBER m_e64u NOTIFY changed);
+ Q_PROPERTY(GadgetWithEnums g MEMBER m_g NOTIFY changed);
+public:
+ ObjectWithEnums(QObject *parent = nullptr) : QObject(parent) {}
+ TypedEnums::E8S m_e8s = {};
+ TypedEnums::E8U m_e8u = {};
+ TypedEnums::E16S m_e16s = {};
+ TypedEnums::E16U m_e16u = {};
+ TypedEnums::E32S m_e32s = {};
+ TypedEnums::E32U m_e32u = {};
+ TypedEnums::E64S m_e64s = {};
+ TypedEnums::E64U m_e64u = {};
+ GadgetWithEnums m_g;
+Q_SIGNALS:
+ void changed();
+};
+
+struct UnregisteredValueBaseType
+{
+ int foo = 12;
+};
+
+struct UnregisteredValueDerivedType: public UnregisteredValueBaseType
+{
+ int bar = 13;
+};
+
+struct GadgetedValueBaseType
+{
+ Q_GADGET
+ int foo = 12;
+};
+
+struct GadgetedValueDerivedType: public GadgetedValueBaseType
+{
+ Q_GADGET
+ int bar = 13;
+};
+
+class UnregisteredValueTypeHandler: public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ int consumed = 0;
+ int gadgeted = 0;
+
+public slots:
+ UnregisteredValueBaseType produce() { return UnregisteredValueBaseType(); }
+ UnregisteredValueDerivedType produceDerived() { return UnregisteredValueDerivedType(); }
+ void consume(UnregisteredValueBaseType) { ++consumed; }
+
+ GadgetedValueDerivedType produceGadgeted() { return GadgetedValueDerivedType(); }
+ void consume(GadgetedValueBaseType) { ++gadgeted; }
+};
+
+class Greeter : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ Greeter(QObject *parent = nullptr) : QObject(parent) {}
+
+ Q_INVOKABLE void greet()
+ {
+ qDebug().noquote() << objectName() << "says hello";
+ }
+
+ Q_INVOKABLE void sum(int a, int b)
+ {
+ qDebug().noquote() << objectName() << QString("says %1 + %2 = %3").arg(a).arg(b).arg(a + b);
+ }
+};
+
+class Attachment : public QObject {
+ Q_OBJECT
+public:
+ Attachment(QObject *parent = nullptr) : QObject(parent) {}
+};
+
+class AttachedInCtor : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_ATTACHED(Attachment)
+
+public:
+ AttachedInCtor(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ attached = qmlAttachedPropertiesObject<AttachedInCtor>(this, true);
+ }
+
+ static Attachment *qmlAttachedProperties(QObject *object) {
+ return new Attachment(object);
+ }
+
+ QObject *attached = nullptr;
+};
+
+class BirthdayParty : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<QObject> guests READ guests)
+ Q_CLASSINFO("DefaultProperty", "guests")
+ QML_ELEMENT
+
+public:
+ using QObject::QObject;
+ QQmlListProperty<QObject> guests() { return {this, &m_guests}; }
+ qsizetype guestCount() const { return m_guests.count(); }
+ QObject *guest(qsizetype i) const { return m_guests.at(i); }
+
+private:
+ QList<QObject *> m_guests;
+};
+
+class ByteArrayReceiver : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ QList<QByteArray> byteArrays;
+
+ Q_INVOKABLE void byteArrayTest(const QByteArray &ba)
+ {
+ byteArrays.push_back(ba);
+ }
+};
+
+class CounterAttachedBaseType: public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+ Q_PROPERTY (int value READ value NOTIFY valueChanged)
+
+public:
+ CounterAttachedBaseType(QObject *parent = nullptr) : QObject(parent) {}
+
+ int value() { return m_value; }
+ Q_SIGNAL void valueChanged();
+
+protected:
+ int m_value = 98;
+};
+
+
+class CounterAttachedType: public CounterAttachedBaseType
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+public:
+ CounterAttachedType(QObject *parent = nullptr) : CounterAttachedBaseType(parent) {}
+
+ Q_INVOKABLE void increase() {
+ ++m_value;
+ Q_EMIT valueChanged();
+ }
+};
+
+class Counter : public QObject
+{
+ Q_OBJECT
+ QML_ATTACHED(CounterAttachedBaseType)
+ QML_ELEMENT
+
+public:
+ static CounterAttachedBaseType *qmlAttachedProperties(QObject *o)
+ {
+ return new CounterAttachedType(o);
+ }
+};
+
+
+class Singleton: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(EnumType enumProperty READ enumProperty CONSTANT)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
+ QML_ELEMENT
+ QML_SINGLETON
+public:
+ explicit Singleton(QObject* parent = nullptr) : QObject(parent) {}
+ enum class EnumType {
+ EnumValue1,
+ EnumValue2
+ };
+ Q_ENUM(EnumType);
+ EnumType enumProperty() const {
+ return EnumType::EnumValue2;
+ }
+};
+
+class NonSingleton: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(EnumType enumProperty READ enumProperty CONSTANT)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
+ QML_ELEMENT
+public:
+ explicit NonSingleton(QObject* parent = nullptr) : QObject(parent) {}
+ enum class EnumType {
+ EnumValue1,
+ EnumValue2
+ };
+ Q_ENUM(EnumType);
+ EnumType enumProperty() const {
+ return EnumType::EnumValue2;
+ }
+};
+
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index fee65fcb17..2f382e8d8e 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -316,6 +317,7 @@ private slots:
void inlineComponentFoundBeforeOtherImports();
void inlineComponentDuplicateNameError();
void inlineComponentWithAliasInstantiatedWithNewProperties();
+ void inlineComponentWithImplicitComponent();
void selfReference();
void selfReferencingSingleton();
@@ -358,6 +360,8 @@ private slots:
void hangOnWarning();
+ void groupPropertyFromNonExposedBaseClass();
+
void listEnumConversion();
void deepInlineComponentScriptBinding();
@@ -402,9 +406,53 @@ private slots:
void importPrecedence();
void nullIsNull();
void multiRequired();
+ void isNullOrUndefined();
void objectAndGadgetMethodCallsRejectThisObject();
void objectAndGadgetMethodCallsAcceptThisObject();
+ void asValueType();
+
+ void longConversion();
+
+ void enumPropsManyUnderylingTypes();
+
+ void typedEnums_data();
+ void typedEnums();
+
+ void objectMethodClone();
+ void unregisteredValueTypeConversion();
+ void retainThis();
+
+ void variantObjectList();
+ void jitExceptions();
+
+ void attachedInCtor();
+ void byteArrayConversion();
+ void propertySignalNames_data();
+ void propertySignalNames();
+ void signalNames_data();
+ void signalNames();
+
+ void callMethodOfAttachedDerived();
+
+ void multiVersionSingletons();
+ void typeAnnotationCycle();
+ void corpseInQmlList();
+ void objectInQmlListAndGc();
+ void asCastToInlineComponent();
+ void deepAliasOnICOrReadonly();
+
+ void optionalChainCallOnNullProperty();
+
+ void ambiguousComponents();
+
+ void writeNumberToEnumAlias();
+ void badInlineComponentAnnotation();
+ void manuallyCallSignalHandler();
+ void overrideDefaultProperty();
+ void enumScopes();
+
+ void typedObjectList();
private:
QQmlEngine engine;
@@ -492,11 +540,11 @@ void tst_qqmllanguage::insertedSemicolon()
QQmlComponent component(&engine, testFileUrl(file));
- QScopedPointer<QObject> object;
+ std::unique_ptr<QObject> object;
if(create) {
object.reset(component.create());
- QVERIFY(object.isNull());
+ QVERIFY(object.get());
}
VERIFY_ERRORS(errorFile.toLatin1().constData());
@@ -1381,13 +1429,13 @@ void tst_qqmllanguage::rootItemIsComponent()
QtWarningMsg,
QRegularExpression(
".*/rootItemIsComponent\\.qml:3:1: Using a Component as the root of "
- "a qmldocument is deprecated: types defined in qml documents are automatically "
- "wrapped into Components when needed\\."));
+ "a QML document is deprecated: types defined in qml documents are "
+ "automatically wrapped into Components when needed\\."));
QTest::ignoreMessage(
QtWarningMsg,
QRegularExpression(
".*/EvilComponentType\\.qml:3:1: Using a Component as the root of a "
- "qmldocument is deprecated: types defined in qml documents are automatically "
+ "QML document is deprecated: types defined in qml documents are automatically "
"wrapped into Components when needed\\."));
QTest::ignoreMessage(
QtWarningMsg,
@@ -1707,8 +1755,8 @@ void tst_qqmllanguage::propertyValueSource()
QVERIFY(object != nullptr);
QList<QObject *> valueSources;
- QObjectList allChildren = object->findChildren<QObject*>();
- foreach (QObject *child, allChildren) {
+ const QObjectList allChildren = object->findChildren<QObject*>();
+ for (QObject *child : allChildren) {
if (qobject_cast<QQmlPropertyValueSource *>(child))
valueSources.append(child);
}
@@ -1728,8 +1776,8 @@ void tst_qqmllanguage::propertyValueSource()
QVERIFY(object != nullptr);
QList<QObject *> valueSources;
- QObjectList allChildren = object->findChildren<QObject*>();
- foreach (QObject *child, allChildren) {
+ const QObjectList allChildren = object->findChildren<QObject*>();
+ for (QObject *child : allChildren) {
if (qobject_cast<QQmlPropertyValueSource *>(child))
valueSources.append(child);
}
@@ -2039,7 +2087,7 @@ void tst_qqmllanguage::aliasProperties()
MyQmlObject *o = qvariant_cast<MyQmlObject*>(v);
QCOMPARE(o->value(), 10);
- delete o;
+ delete o; //intentional delete
v = object->property("otherAlias");
QCOMPARE(v.typeId(), qMetaTypeId<MyQmlObject *>());
@@ -2074,7 +2122,7 @@ void tst_qqmllanguage::aliasProperties()
QObject *alias = qvariant_cast<QObject *>(object->property("aliasedObject"));
QCOMPARE(alias, object2);
- delete object1;
+ delete object1; //intentional delete
QObject *alias2 = object.data(); // "Random" start value
int status = -1;
@@ -2583,7 +2631,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode()
Q_ASSERT(td);
QVERIFY(!td->backupSourceCode().isValid());
- QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = td->compilationUnit();
readOnlyQmlUnit.reset(compilationUnit->unitData());
Q_ASSERT(readOnlyQmlUnit);
QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize));
@@ -2863,7 +2911,8 @@ void tst_qqmllanguage::testType(const QString& qml, const QString& type, const Q
if (type.isEmpty()) {
QVERIFY(component.isError());
QString actualerror;
- foreach (const QQmlError e, component.errors()) {
+ const auto errors = component.errors();
+ for (const QQmlError &e : errors) {
if (!actualerror.isEmpty())
actualerror.append("; ");
actualerror.append(e.description());
@@ -3861,6 +3910,7 @@ void tst_qqmllanguage::initTestCase()
qmlRegisterType(testFileUrl("invalidRoot.1.qml"), "Test", 1, 0, "RegisteredCompositeType3");
qmlRegisterType(testFileUrl("CompositeTypeWithEnum.qml"), "Test", 1, 0, "RegisteredCompositeTypeWithEnum");
qmlRegisterType(testFileUrl("CompositeTypeWithAttachedProperty.qml"), "Test", 1, 0, "RegisteredCompositeTypeWithAttachedProperty");
+ qmlRegisterType(testFileUrl("CompositeTypeWithEnumSelfReference.qml"), "Test", 1, 0, "CompositeTypeWithEnumSelfReference");
// Registering the TestType class in other modules should have no adverse effects
qmlRegisterType<TestType>("org.qtproject.TestPre", 1, 0, "Test");
@@ -3889,6 +3939,7 @@ void tst_qqmllanguage::initTestCase()
// Register a Composite Singleton.
qmlRegisterSingletonType(testFileUrl("singleton/RegisteredCompositeSingletonType.qml"), "org.qtproject.Test", 1, 0, "RegisteredSingleton");
+ qmlRegisterType(testFileUrl("Comps/OverlayDrawer.qml"), "Comps", 2, 0, "OverlayDrawer");
}
void tst_qqmllanguage::aliasPropertyChangeSignals()
@@ -4036,6 +4087,17 @@ void tst_qqmllanguage::registeredCompositeTypeWithEnum()
QCOMPARE(o->property("enumValue0").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue0));
QCOMPARE(o->property("enumValue42").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue42));
QCOMPARE(o->property("enumValue15").toInt(), static_cast<int>(MyCompositeBaseType::ScopedCompositeEnum::EnumValue15));
+
+ {
+ QQmlComponent component(&engine);
+ component.setData("import Test\nCompositeTypeWithEnumSelfReference {}", QUrl());
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o != nullptr);
+
+ QCOMPARE(o->property("e").toInt(), 1);
+ QCOMPARE(o->property("f").toInt(), 2);
+ }
}
// QTBUG-43581
@@ -4212,7 +4274,8 @@ void tst_qqmllanguage::lowercaseEnumRuntime()
QQmlComponent component(&engine, testFileUrl(file));
VERIFY_ERRORS(0);
- delete component.create();
+ std::unique_ptr<QObject> root { component.create() };
+ QVERIFY(root);
}
void tst_qqmllanguage::lowercaseEnumCompileTime_data()
@@ -4229,7 +4292,8 @@ void tst_qqmllanguage::lowercaseEnumCompileTime()
QQmlComponent component(&engine, testFileUrl(file));
VERIFY_ERRORS(0);
- delete component.create();
+ std::unique_ptr<QObject> root { component.create() };
+ QVERIFY(root);
}
void tst_qqmllanguage::scopedEnum()
@@ -4473,7 +4537,6 @@ void tst_qqmllanguage::groupAssignmentFailure()
{
auto ep = std::make_unique<QQmlEngine>();
QTest::failOnWarning("QQmlComponent: Component destroyed while completion pending");
- 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());
QVERIFY(!o);
@@ -5749,7 +5812,7 @@ void tst_qqmllanguage::selfReference()
const QMetaObject *metaObject = o->metaObject();
QMetaProperty selfProperty = metaObject->property(metaObject->indexOfProperty("self"));
- QCOMPARE(selfProperty.metaType().id(), compilationUnit->typeIds.id.id());
+ QCOMPARE(selfProperty.metaType().id(), compilationUnit->metaType().id());
QByteArray typeName = selfProperty.typeName();
QVERIFY(typeName.endsWith('*'));
@@ -5758,7 +5821,7 @@ void tst_qqmllanguage::selfReference()
QMetaMethod selfFunction = metaObject->method(metaObject->indexOfMethod("returnSelf()"));
QVERIFY(selfFunction.isValid());
- QCOMPARE(selfFunction.returnType(), compilationUnit->typeIds.id.id());
+ QCOMPARE(selfFunction.returnType(), compilationUnit->metaType().id());
QMetaMethod selfSignal;
@@ -5772,7 +5835,7 @@ void tst_qqmllanguage::selfReference()
QVERIFY(selfSignal.isValid());
QCOMPARE(selfSignal.parameterCount(), 1);
- QCOMPARE(selfSignal.parameterType(0), compilationUnit->typeIds.id.id());
+ QCOMPARE(selfSignal.parameterType(0), compilationUnit->metaType().id());
}
void tst_qqmllanguage::selfReferencingSingleton()
@@ -5809,10 +5872,10 @@ void tst_qqmllanguage::listContainingDeletedObject()
QVERIFY(root);
auto cmp = root->property("a").value<QQmlComponent*>();
- auto o = cmp->create();
+ std::unique_ptr<QObject> o { cmp->create() };
- QMetaObject::invokeMethod(root.get(), "doAssign", Q_ARG(QVariant, QVariant::fromValue(o)));
- delete o;
+ QMetaObject::invokeMethod(root.get(), "doAssign", Q_ARG(QVariant, QVariant::fromValue(o.get())));
+ o.reset();
QMetaObject::invokeMethod(root.get(), "use");
}
@@ -6096,6 +6159,17 @@ void tst_qqmllanguage::inlineComponentWithAliasInstantiatedWithNewProperties()
QCOMPARE(root->property("result").toString(), "Bar");
}
+void tst_qqmllanguage::inlineComponentWithImplicitComponent()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("inlineComponentWithImplicitComponent.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+
+ QCOMPARE(root->objectName(), "green blue"_L1);
+}
+
struct QJSValueConvertible {
Q_GADGET
@@ -6392,23 +6466,15 @@ void tst_qqmllanguage::extendedSingleton()
void tst_qqmllanguage::qtbug_85932()
{
- QString warning1 = QLatin1String("%1:10:9: id is not unique").arg(testFileUrl("SingletonTest.qml").toString());
- QString warning2 = QLatin1String("%1:4: Error: Due to the preceding error(s), Singleton \"SingletonTest\" could not be loaded.").arg(testFileUrl("qtbug_85932.qml").toString());
-
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(warning1));
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, qPrintable(warning2));
-
QQmlEngine engine;
- QList<QQmlError> allWarnings;
- QObject::connect(&engine, &QQmlEngine::warnings, [&allWarnings](const QList<QQmlError> &warnings) {
- allWarnings.append(warnings);
- });
- QQmlComponent c(&engine, testFileUrl("qtbug_85932.qml"));
- QScopedPointer<QObject> obj(c.create());
- QTRY_COMPARE(allWarnings.size(), 2);
- QCOMPARE(allWarnings.at(0).toString(), warning1);
- QCOMPARE(allWarnings.at(1).toString(), warning2);
+ QQmlComponent c(&engine, testFileUrl("badSingleton/qtbug_85932.qml"));
+ QVERIFY(c.isError());
+
+ const QString error = c.errorString();
+ QVERIFY(error.contains(QLatin1String("Type SingletonTest unavailable")));
+ QVERIFY(error.contains(QLatin1String("%1:10 id is not unique")
+ .arg(testFileUrl("badSingleton/SingletonTest.qml").toString())));
}
void tst_qqmllanguage::multiExtension()
@@ -6758,15 +6824,22 @@ void tst_qqmllanguage::bareInlineComponent()
if (type.elementName() == QStringLiteral("Tab1")) {
QVERIFY(type.module().isEmpty());
tab1Found = true;
- const auto ics = type.priv()->objectIdToICType;
- QVERIFY(ics.size() > 0);
- for (const QQmlType &ic : ics)
- QVERIFY(ic.containingType() == type);
+
+ const QQmlType leftTab = QQmlMetaType::inlineComponentType(type, "LeftTab");
+ QUrl leftUrl = leftTab.sourceUrl();
+ leftUrl.setFragment(QString());
+ QCOMPARE(leftUrl, type.sourceUrl());
+
+ const QQmlType rightTab = QQmlMetaType::inlineComponentType(type, "RightTab");
+ QUrl rightUrl = rightTab.sourceUrl();
+ rightUrl.setFragment(QString());
+ QCOMPARE(rightUrl, type.sourceUrl());
}
}
QVERIFY(tab1Found);
}
+#if QT_CONFIG(qml_debug)
struct DummyDebugger : public QV4::Debugging::Debugger
{
bool pauseAtNextOpportunity() const final { return false; }
@@ -6775,6 +6848,9 @@ struct DummyDebugger : public QV4::Debugging::Debugger
void leavingFunction(const QV4::ReturnedValue &) final { }
void aboutToThrow() final { }
};
+#else
+using DummyDebugger = QV4::Debugging::Debugger; // it's already dummy
+#endif
void tst_qqmllanguage::hangOnWarning()
{
@@ -6792,6 +6868,25 @@ void tst_qqmllanguage::hangOnWarning()
QVERIFY(object != nullptr);
}
+void tst_qqmllanguage::groupPropertyFromNonExposedBaseClass()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("derivedFromUnexposedBase.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ auto root = qobject_cast<DerivedFromUnexposedBase *>(o.get());
+ QVERIFY(root);
+ QVERIFY(root->group);
+ QCOMPARE(root->group->value, 42);
+ QCOMPARE(root->groupGadget.value, 42);
+
+ c.loadUrl(testFileUrl("dynamicGroupPropertyRejected.qml"));
+ QVERIFY(c.isError());
+ QVERIFY2(c.errorString().contains("Unsupported grouped property access"), qPrintable(c.errorString()));
+}
+
void tst_qqmllanguage::listEnumConversion()
{
QQmlEngine e;
@@ -7424,6 +7519,33 @@ LeakingForeignerForeign {
QVERIFY(o->property("anotherAbc").isValid());
QVERIFY(!o->property("abc").isValid());
}
+
+ {
+ QQmlComponent c(&engine);
+ c.setData(R"(
+import StaticTest
+import QtQml
+QtObject {
+ objectName: 'b' + ForeignNamespaceForeign.B
+})", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->objectName(), "b1");
+ }
+ {
+ QQmlComponent c(&engine);
+ c.setData(R"(
+import StaticTest
+import QtQml
+QtObject {
+ objectName: 'b' + LeakingForeignNamespaceForeign.B
+})", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->objectName(), "b2");
+ }
}
void tst_qqmllanguage::attachedOwnProperties()
@@ -7711,12 +7833,20 @@ void tst_qqmllanguage::functionSignatureEnforcement()
QCOMPARE(ignored->property("m").toInt(), 77);
QCOMPARE(ignored->property("n").toInt(), 67);
- QQmlComponent c2(&engine, testFileUrl("signatureEnforced.qml"));
+ const QUrl url2 = testFileUrl("signatureEnforced.qml");
+ QQmlComponent c2(&engine, url2);
QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+ QTest::ignoreMessage(
+ QtCriticalMsg,
+ qPrintable(url2.toString() + u":36: 15 should be coerced to void because the function "
+ "called is insufficiently annotated. The original value "
+ "is retained. "
+ "This will change in a future version of Qt."_s));
+
QScopedPointer<QObject> enforced(c2.create());
QCOMPARE(enforced->property("l").toInt(), 2); // strlen("no")
- QCOMPARE(enforced->property("m").toInt(), 12);
+ QCOMPARE(enforced->property("m").toInt(), 77);
QCOMPARE(enforced->property("n").toInt(), 99);
QCOMPARE(enforced->property("o").toInt(), 77);
}
@@ -7766,6 +7896,30 @@ void tst_qqmllanguage::multiRequired()
qPrintable(url.toString() + ":5 Required property description was not initialized\n"));
}
+// QTBUG-111088
+void tst_qqmllanguage::isNullOrUndefined()
+{
+ {
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("isNullOrUndefined_interpreter.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVariant result = o.data()->property("result");
+ QVERIFY(result.isValid());
+ QCOMPARE(result.toInt(), 3);
+ }
+
+ {
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("isNullOrUndefined_jit.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVariant result = o.data()->property("result");
+ QVERIFY(result.isValid());
+ QCOMPARE(result.toInt(), 150);
+ }
+}
+
void tst_qqmllanguage::objectAndGadgetMethodCallsRejectThisObject()
{
QQmlEngine engine;
@@ -7810,6 +7964,15 @@ void tst_qqmllanguage::objectAndGadgetMethodCallsAcceptThisObject()
QQmlComponent c(&engine, testFileUrl("objectAndGadgetMethodCallsAcceptThisObject.qml"));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ // Explicitly retrieve the metaobject for the Qt singleton so that the proxy data is created.
+ // This way the inheritance analysis we do when figuring out what toString() means is somewhat
+ // more interesting. Also, we get a deterministic result for Qt.toString().
+ const QQmlType qtType = QQmlMetaType::qmlType(QStringLiteral("Qt"), QString(), QTypeRevision());
+ QVERIFY(qtType.isValid());
+ const QMetaObject *qtMeta = qtType.metaObject();
+ QVERIFY(qtMeta);
+ QCOMPARE(QString::fromUtf8(qtMeta->className()), QLatin1String("Qt"));
+
QTest::ignoreMessage(
QtWarningMsg, QRegularExpression(
"objectAndGadgetMethodCallsAcceptThisObject.qml:16: Error: "
@@ -7840,7 +8003,7 @@ void tst_qqmllanguage::objectAndGadgetMethodCallsAcceptThisObject()
QCOMPARE(o->property("goodString2"), QStringLiteral("27"));
QCOMPARE(o->property("goodString3"), QStringLiteral("28"));
- QVERIFY(o->property("goodString4").value<QString>().startsWith("QtObject"_L1));
+ QVERIFY(o->property("goodString4").value<QString>().startsWith("Qt("_L1));
QCOMPARE(o->property("badString2"), QString());
QCOMPARE(o->property("badInt"), 0);
@@ -7849,6 +8012,653 @@ void tst_qqmllanguage::objectAndGadgetMethodCallsAcceptThisObject()
QCOMPARE(o->property("goodInt3"), 5);
}
+void tst_qqmllanguage::longConversion()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("longConversion.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ for (const char *prop : {
+ "testProp",
+ "testQProp",
+ "fromLocal",
+ "fromQLocal",
+ "fromBoolean",
+ "fromQBoolean"}) {
+ const QVariant val = o->property(prop);
+ QVERIFY(val.isValid());
+ QCOMPARE(val.metaType(), QMetaType::fromType<bool>());
+ QVERIFY(!val.toBool());
+ }
+}
+
+void tst_qqmllanguage::enumPropsManyUnderylingTypes()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("enumPropsManyUnderlyingTypes.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ auto *enumObject = qobject_cast<EnumPropsManyUnderlyingTypes *>(o.get());
+ QCOMPARE(enumObject->si8prop, EnumPropsManyUnderlyingTypes::ResolvedValue);
+ QCOMPARE(enumObject->ui8prop, EnumPropsManyUnderlyingTypes::ResolvedValue);
+ QCOMPARE(enumObject->si16prop, EnumPropsManyUnderlyingTypes::ResolvedValue);
+ QCOMPARE(enumObject->ui16prop, EnumPropsManyUnderlyingTypes::ResolvedValue);
+ QCOMPARE(enumObject->si64prop, EnumPropsManyUnderlyingTypes::ResolvedValue);
+ QCOMPARE(enumObject->ui64prop, EnumPropsManyUnderlyingTypes::ResolvedValue);
+}
+
+void tst_qqmllanguage::asValueType()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("asValueType.qml");
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + ":7:5: Unable to assign [undefined] to QRectF"_L1));
+ QScopedPointer<QObject> o(c.create());
+
+ QCOMPARE(o->property("a"), QVariant());
+ QCOMPARE(o->property("b").value<QRectF>(), QRectF());
+ QVERIFY(!o->property("c").toBool());
+
+ const QRectF rect(1, 2, 3, 4);
+ o->setProperty("a", QVariant(rect));
+ QCOMPARE(o->property("b").value<QRectF>(), rect);
+ QVERIFY(o->property("c").toBool());
+
+ QVERIFY(!o->property("d").toBool());
+ const QPointF point = o->property("e").value<QPointF>();
+ QCOMPARE(point.x(), 10.0);
+ QCOMPARE(point.y(), 20.0);
+
+ const ValueTypeWithString withString = o->property("f").value<ValueTypeWithString>();
+ QCOMPARE(withString.toString(), u"red");
+
+ const QVariant string = o->property("g");
+ QCOMPARE(string.metaType(), QMetaType::fromType<QString>());
+ QCOMPARE(string.toString(), u"green");
+}
+
+void tst_qqmllanguage::typedEnums_data()
+{
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<double>("value");
+ const QMetaObject *mo = &TypedEnums::staticMetaObject;
+ for (int i = 0, end = mo->enumeratorCount(); i != end; ++i) {
+ const QMetaEnum e = mo->enumerator(i);
+ for (int k = 0, end = e.keyCount(); k != end; ++k) {
+ QTest::addRow("%s::%s", e.name(), e.key(k))
+ << QString::fromLatin1(e.name()).toLower()
+ << double(e.value(k));
+ }
+ }
+}
+void tst_qqmllanguage::typedEnums()
+{
+ QFETCH(QString, property);
+ QFETCH(double, value);
+ QQmlEngine e;
+ const QString qml = QLatin1String(R"(
+ import QtQml
+ import TypedEnums
+ ObjectWithEnums {
+ property real input: %2
+ %1: input
+ g.%1: input
+ property real output1: %1
+ property real output2: g.%1
+ }
+ )").arg(property).arg(value, 0, 'f');
+ QQmlComponent c(&engine);
+ c.setData(qml.toUtf8(), QUrl("enums.qml"_L1));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ // TODO: This silently fails for quint32, qint64 and quint64 because QMetaEnum cannot encode
+ // such values either. For the 64bit values we'll also need a better type than double
+ // inside QML.
+ QEXPECT_FAIL("E32U::E32UD", "Not supported", Abort);
+ QEXPECT_FAIL("E32U::E32UE", "Not supported", Abort);
+ QEXPECT_FAIL("E64U::E64UE", "Not supported", Abort);
+
+ QCOMPARE(o->property("output1").toDouble(), value);
+ QCOMPARE(o->property("output2").toDouble(), value);
+}
+
+void tst_qqmllanguage::objectMethodClone()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("objectMethodClone.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QTRY_COMPARE(o->property("doneClicks").toInt(), 2);
+}
+
+void tst_qqmllanguage::unregisteredValueTypeConversion()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("unregisteredValueTypeConversion.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ UnregisteredValueTypeHandler *handler = qobject_cast<UnregisteredValueTypeHandler *>(o.data());
+ Q_ASSERT(handler);
+ QCOMPARE(handler->consumed, 2);
+ QCOMPARE(handler->gadgeted, 1);
+}
+
+void tst_qqmllanguage::retainThis()
+{
+ QQmlEngine e;
+ const QUrl url = testFileUrl("retainThis.qml");
+ QQmlComponent c(&e, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ const QString warning = u"Calling C++ methods with 'this' objects different "
+ "from the one they were retrieved from is broken, due to "
+ "historical reasons. The original object is used as 'this' "
+ "object. You can allow the given 'this' object to be used "
+ "by setting 'pragma NativeMethodBehavior: AcceptThisObject'"_s;
+
+ // Both cases objA because we retain the thisObject.
+ for (int i = 0; i < 2; ++i) {
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":12: "_s + warning));
+ QTest::ignoreMessage(QtDebugMsg, "objA says hello");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":13: "_s + warning));
+ QTest::ignoreMessage(QtDebugMsg, "objA says 5 + 6 = 11");
+ }
+
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":22: "_s + warning));
+ QTest::ignoreMessage(QtDebugMsg, "objA says hello");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":22: "_s + warning));
+ QTest::ignoreMessage(QtDebugMsg, "objB says hello");
+
+ QTest::ignoreMessage(QtDebugMsg, "objC says 7 + 7 = 14");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + u":32: "_s + warning));
+ QTest::ignoreMessage(QtDebugMsg, "objB says 7 + 7 = 14");
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
+
+void tst_qqmllanguage::variantObjectList()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("variantObjectList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ BirthdayParty *party = o->property("q").value<BirthdayParty *>();
+ QCOMPARE(party->guestCount(), 3);
+ QCOMPARE(party->guest(0)->objectName(), "Leo Hodges");
+ QCOMPARE(party->guest(1)->objectName(), "Jack Smith");
+ QCOMPARE(party->guest(2)->objectName(), "Anne Brown");
+}
+
+void tst_qqmllanguage::jitExceptions()
+{
+ QQmlEngine e;
+ const QUrl url = testFileUrl("jitExceptions.qml");
+ QQmlComponent c(&e, testFileUrl("jitExceptions.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + u":5: ReferenceError: control is not defined"_s));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
+
+void tst_qqmllanguage::attachedInCtor()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e);
+ c.setData(R"(
+ import Test
+ AttachedInCtor {}
+ )", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ AttachedInCtor *a = qobject_cast<AttachedInCtor *>(o.data());
+ QVERIFY(a->attached);
+ QCOMPARE(a->attached, qmlAttachedPropertiesObject<AttachedInCtor>(a, false));
+}
+
+void tst_qqmllanguage::byteArrayConversion()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e);
+ c.setData(R"(
+ import Test
+ import QtQml
+ ByteArrayReceiver {
+ Component.onCompleted: {
+ byteArrayTest([1, 2, 3]);
+ byteArrayTest(Array.from('456'));
+ }
+ }
+ )", QUrl());
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ ByteArrayReceiver *receiver = qobject_cast<ByteArrayReceiver *>(o.data());
+ QVERIFY(receiver);
+ QCOMPARE(receiver->byteArrays.length(), 2);
+ QCOMPARE(receiver->byteArrays[0], QByteArray("\1\2\3"));
+ QCOMPARE(receiver->byteArrays[1], QByteArray("\4\5\6"));
+}
+void tst_qqmllanguage::propertySignalNames_data()
+{
+ QTest::addColumn<QString>("propertyName");
+ QTest::addColumn<QString>("propertyChangedSignal");
+ QTest::addColumn<QString>("propertyChangedHandler");
+ QTest::addRow("helloWorld") << u"helloWorld"_s << u"helloWorldChanged"_s
+ << u"onHelloWorldChanged"_s;
+ QTest::addRow("$helloWorld") << u"$helloWorld"_s << u"$helloWorldChanged"_s
+ << u"on$HelloWorldChanged"_s;
+ QTest::addRow("_helloWorld") << u"_helloWorld"_s << u"_helloWorldChanged"_s
+ << u"on_HelloWorldChanged"_s;
+ QTest::addRow("_") << u"_"_s << u"_Changed"_s << u"on_Changed"_s;
+ QTest::addRow("$") << u"$"_s << u"$Changed"_s << u"on$Changed"_s;
+ QTest::addRow("ä") << u"ä"_s << u"äChanged"_s << u"onÄChanged"_s;
+ QTest::addRow("___123a") << u"___123a"_s << u"___123aChanged"_s << u"on___123AChanged"_s;
+}
+void tst_qqmllanguage::propertySignalNames()
+{
+ QFETCH(QString, propertyName);
+ QFETCH(QString, propertyChangedSignal);
+ QFETCH(QString, propertyChangedHandler);
+ QQmlEngine e;
+ QQmlComponent c(&e);
+ c.setData(uR"(
+import QtQuick
+Item {
+ property int %1: 456
+ property bool success: false
+ function f() { %1 = 123; }
+ function g() { %2(); }
+ %3: success = true
+})"_s.arg(propertyName, propertyChangedSignal, propertyChangedHandler)
+ .toUtf8(),
+ QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o != nullptr);
+ const QMetaObject *metaObject = o->metaObject();
+ auto signalIndex =
+ metaObject->indexOfSignal(propertyChangedSignal.append("()").toStdString().c_str());
+ QVERIFY(signalIndex > -1);
+ auto signal = metaObject->method(signalIndex);
+ QVERIFY(signal.isValid());
+ QSignalSpy changeSignal(o.data(), signal);
+ QMetaObject::invokeMethod(o.data(), "f");
+ QCOMPARE(o->property(propertyName.toStdString().c_str()), 123);
+ QVERIFY(changeSignal.size() == 1);
+ QCOMPARE(o->property("success"), true);
+ QMetaObject::invokeMethod(o.data(), "g");
+ QVERIFY(changeSignal.size() == 2);
+}
+void tst_qqmllanguage::signalNames_data()
+{
+ QTest::addColumn<QString>("signalName");
+ QTest::addColumn<QString>("handlerName");
+ QTest::addRow("helloWorld") << u"helloWorld"_s << u"onHelloWorld"_s;
+ QTest::addRow("$helloWorld") << u"$helloWorld"_s << u"on$HelloWorld"_s;
+ QTest::addRow("_helloWorld") << u"_helloWorld"_s << u"on_HelloWorld"_s;
+ QTest::addRow("_") << u"_"_s << u"on_"_s;
+ QTest::addRow("aUmlaut") << u"ä"_s << u"onÄ"_s;
+ QTest::addRow("___123a") << u"___123a"_s << u"on___123A"_s;
+}
+void tst_qqmllanguage::signalNames()
+{
+ QFETCH(QString, signalName);
+ QFETCH(QString, handlerName);
+ QQmlEngine e;
+ QQmlComponent c(&e);
+ c.setData(uR"(
+import QtQuick
+Item {
+ signal %1()
+ property bool success: false
+ function f() { %1(); }
+ %2: success = true
+})"_s.arg(signalName, handlerName)
+ .toUtf8(),
+ QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o != nullptr);
+ const QMetaObject *metaObject = o->metaObject();
+ auto signalIndex = metaObject->indexOfSignal(signalName.append("()").toStdString().c_str());
+ QVERIFY(signalIndex > -1);
+ auto signal = metaObject->method(signalIndex);
+ QVERIFY(signal.isValid());
+ QSignalSpy changeSignal(o.data(), signal);
+ signal.invoke(o.data());
+ QVERIFY(changeSignal.size() == 1);
+ QCOMPARE(o->property("success"), true);
+ QMetaObject::invokeMethod(o.data(), "f");
+ QVERIFY(changeSignal.size() == 2);
+}
+
+void tst_qqmllanguage::callMethodOfAttachedDerived()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData(R"(
+ import QtQml
+ import Test
+
+ QtObject {
+ Component.onCompleted: Counter.increase()
+ property int v: Counter.value
+ }
+ )", QUrl());
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("v").toInt(), 99);
+}
+
+void tst_qqmllanguage::multiVersionSingletons()
+{
+ qmlRegisterTypesAndRevisions<BareSingleton>("MultiVersionSingletons", 11);
+ qmlRegisterTypesAndRevisions<UncreatableSingleton>("MultiVersionSingletons", 11);
+ QQmlEngine engine;
+
+ for (const char *name : { "BareSingleton", "UncreatableSingleton"}) {
+ const int id1 = qmlTypeId("MultiVersionSingletons", 1, 0, name);
+ const int id2 = qmlTypeId("MultiVersionSingletons", 11, 0, name);
+ QVERIFY(id1 != id2);
+ const QJSValue value1 = engine.singletonInstance<QJSValue>(id1);
+ const QJSValue value2 = engine.singletonInstance<QJSValue>(id2);
+ QVERIFY(value1.strictlyEquals(value2));
+ }
+}
+
+void tst_qqmllanguage::typeAnnotationCycle()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("TypeAnnotationCycle1.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("b").value<QObject*>(), o.data());
+}
+
+void tst_qqmllanguage::corpseInQmlList()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("corpseInQmlList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QScopedPointer<QObject> a(new QObject);
+ QMetaObject::invokeMethod(o.data(), "setB", Q_ARG(QObject *, a.data()));
+
+ QJSValue b = o->property("b").value<QJSValue>();
+ QQmlListProperty<QObject> list
+ = qjsvalue_cast<QQmlListProperty<QObject>>(b.property(QStringLiteral("b")));
+
+ QCOMPARE(list.count(&list), 1);
+ QCOMPARE(list.at(&list, 0), a.data());
+
+ a.reset();
+
+ b = o->property("b").value<QJSValue>();
+ list = qjsvalue_cast<QQmlListProperty<QObject>>(b.property(QStringLiteral("b")));
+
+ QCOMPARE(list.count(&list), 1);
+ QCOMPARE(list.at(&list, 0), nullptr);
+
+ // The list itself is still alive:
+
+ list.append(&list, o.data());
+ QCOMPARE(list.count(&list), 2);
+ QCOMPARE(list.at(&list, 0), nullptr);
+ QCOMPARE(list.at(&list, 1), o.data());
+
+ list.replace(&list, 0, o.data());
+ QCOMPARE(list.count(&list), 2);
+ QCOMPARE(list.at(&list, 0), o.data());
+ QCOMPARE(list.at(&list, 1), o.data());
+
+ list.removeLast(&list);
+ QCOMPARE(list.count(&list), 1);
+ QCOMPARE(list.at(&list, 0), o.data());
+
+ list.clear(&list);
+ QCOMPARE(list.count(&list), 0);
+}
+
+void tst_qqmllanguage::objectInQmlListAndGc()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("objectInList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ // Process the deletion event
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+
+ QQmlListProperty<QObject> children = o->property("child").value<QQmlListProperty<QObject>>();
+ QCOMPARE(children.count(&children), 1);
+ QObject *child = children.at(&children, 0);
+ QVERIFY(child);
+ QCOMPARE(child->objectName(), QLatin1String("child"));
+}
+
+void tst_qqmllanguage::asCastToInlineComponent()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("asCastToInlineComponent.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->objectName(), QLatin1String("value: 20"));
+}
+
+void tst_qqmllanguage::deepAliasOnICOrReadonly()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("deepAliasOnICUser.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("borderColor").toString(), QLatin1String("black"));
+ QCOMPARE(o->property("borderObjectName").toString(), QLatin1String("theLeaf"));
+
+ const QVariant var = o->property("borderVarvar");
+ QCOMPARE(var.metaType(), QMetaType::fromType<QString>());
+ QCOMPARE(var.toString(), QLatin1String("mauve"));
+
+ QQmlComponent c2(&engine, testFileUrl("deepAliasOnReadonly.qml"));
+ QVERIFY(c2.isError());
+ QVERIFY(c2.errorString().contains(
+ QLatin1String(
+ "Invalid property assignment: \"readonlyRectX\" is a read-only property")));
+}
+
+void tst_qqmllanguage::optionalChainCallOnNullProperty()
+{
+ QTest::failOnWarning(QRegularExpression(".*Cannot call method 'destroy' of null.*"));
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("optionalChainCallOnNullProperty.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
+
+void tst_qqmllanguage::ambiguousComponents()
+{
+ auto e1 = std::make_unique<QQmlEngine>();
+ e1->addImportPath(dataDirectory());
+ bool isInstanceOf = false;
+
+ {
+ QQmlComponent c(e1.get());
+ c.loadUrl(testFileUrl("ambiguousComponents.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QTest::ignoreMessage(QtDebugMsg, "do");
+ QMetaObject::invokeMethod(o.data(), "dodo");
+
+ QMetaObject::invokeMethod(o.data(), "testInstanceOf", Q_RETURN_ARG(bool, isInstanceOf));
+ QVERIFY(isInstanceOf);
+ }
+
+ QQmlEngine e2;
+ e2.addImportPath(dataDirectory());
+ QQmlComponent c2(&e2);
+ c2.loadUrl(testFileUrl("ambiguousComponents.qml"));
+ QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+
+ QScopedPointer<QObject> o2(c2.create());
+ QTest::ignoreMessage(QtDebugMsg, "do");
+ QMetaObject::invokeMethod(o2.data(), "dodo");
+
+ isInstanceOf = false;
+ QMetaObject::invokeMethod(o2.data(), "testInstanceOf", Q_RETURN_ARG(bool, isInstanceOf));
+ QVERIFY(isInstanceOf);
+
+ e1.reset();
+
+ // We can still invoke the function. This means its CU belongs to e2.
+ QTest::ignoreMessage(QtDebugMsg, "do");
+ QMetaObject::invokeMethod(o2.data(), "dodo");
+
+ isInstanceOf = false;
+ QMetaObject::invokeMethod(o2.data(), "testInstanceOf", Q_RETURN_ARG(bool, isInstanceOf));
+ QVERIFY(isInstanceOf);
+}
+
+void tst_qqmllanguage::writeNumberToEnumAlias()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("aliasWriter.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("strokeStyle").toInt(), 1);
+}
+
+void tst_qqmllanguage::badInlineComponentAnnotation()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("badICAnnotation.qml");
+ QQmlComponent c(&engine, testFileUrl("badICAnnotation.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(
+ QtCriticalMsg,
+ qPrintable(url.toString() + ":20: 5 should be coerced to void because the function "
+ "called is insufficiently annotated. The original "
+ "value is retained. This will change in a future "
+ "version of Qt."));
+ QTest::ignoreMessage(
+ QtCriticalMsg,
+ QRegularExpression(":22: IC\\([^\\)]+\\) should be coerced to void because the "
+ "function called is insufficiently annotated. The original "
+ "value is retained. This will change in a future version of "
+ "Qt\\."));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("a").toInt(), 5);
+
+ QObject *ic = o->property("ic").value<QObject *>();
+ QVERIFY(ic);
+
+ QCOMPARE(o->property("b").value<QObject *>(), ic);
+ QCOMPARE(o->property("c").value<QObject *>(), ic);
+ QCOMPARE(o->property("d").value<QObject *>(), nullptr);
+}
+
+void tst_qqmllanguage::manuallyCallSignalHandler()
+{
+ // TODO: This test verifies the absence of regression legacy behavior. See QTBUG-120573
+ // Once we can get rid of the legacy behavior, delete this test!
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("manuallyCallSignalHandler.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ for (int i = 0; i < 10; ++i) {
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ "Property 'onDestruction' of object QQmlComponentAttached\\(0x[0-9a-f]+\\) is a signal "
+ "handler\\. You should not call it directly\\. Make it a proper function and call that "
+ "or emit the signal\\."));
+ QTest::ignoreMessage(QtDebugMsg, "evil!");
+ QScopedPointer<QObject> o(c.create());
+ QTest::ignoreMessage(QtDebugMsg, "evil!");
+ }
+}
+
+void tst_qqmllanguage::overrideDefaultProperty()
+{
+ QQmlEngine e;
+ const QUrl url = testFileUrl("overrideDefaultProperty.qml");
+
+ // Should not crash here!
+
+ QQmlComponent c(&e, url);
+ QVERIFY(c.isError());
+ QCOMPARE(c.errorString(),
+ url.toString() + QLatin1String(":5 Cannot assign object to list property \"data\"\n"));
+}
+
+void tst_qqmllanguage::enumScopes()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("enumScopes.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("singletonUnscoped"), false);
+ QCOMPARE(o->property("singletonScoped"), true);
+ QCOMPARE(o->property("nonSingletonUnscoped"), false);
+ QCOMPARE(o->property("nonSingletonScoped"), true);
+
+ QCOMPARE(o->property("singletonScopedValue").toInt(), int(EnumProviderSingleton::Expected::Value));
+ QCOMPARE(o->property("singletonUnscopedValue").toInt(), int(EnumProviderSingleton::Expected::Value));
+}
+
+void tst_qqmllanguage::typedObjectList()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("typedObjectList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QJSValue b = o->property("b").value<QJSValue>();
+ auto list = qjsvalue_cast<QQmlListProperty<QQmlComponent>>(b.property(QStringLiteral("b")));
+
+ QCOMPARE(list.count(&list), 1);
+ QVERIFY(list.at(&list, 0) != nullptr);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllistcompositor/CMakeLists.txt b/tests/auto/qml/qqmllistcompositor/CMakeLists.txt
index c05b97f6d2..b6999900ba 100644
--- a/tests/auto/qml/qqmllistcompositor/CMakeLists.txt
+++ b/tests/auto/qml/qqmllistcompositor/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmllistcompositor Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmllistcompositor LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmllistcompositor
SOURCES
tst_qqmllistcompositor.cpp
diff --git a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp
index 2d02cac9f8..c10ceafeae 100644
--- a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp
+++ b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <private/qqmllistcompositor_p.h>
@@ -179,7 +180,7 @@ void tst_qqmllistcompositor::find()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
compositor.find(startGroup, startIndex);
@@ -241,7 +242,7 @@ void tst_qqmllistcompositor::findInsertPosition()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
QQmlListCompositor::insert_iterator it = compositor.findInsertPosition(group, index);
@@ -499,7 +500,7 @@ void tst_qqmllistcompositor::clearFlags()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
QVector<C::Remove> removes;
@@ -681,7 +682,7 @@ void tst_qqmllistcompositor::setFlags()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
QVector<C::Insert> inserts;
@@ -968,7 +969,7 @@ void tst_qqmllistcompositor::move()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
QVector<C::Remove> removes;
@@ -1202,7 +1203,7 @@ void tst_qqmllistcompositor::listItemsInserted()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
QVector<C::Insert> inserts;
@@ -1338,7 +1339,7 @@ void tst_qqmllistcompositor::listItemsRemoved()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
QVector<C::Remove> removes;
@@ -1532,7 +1533,7 @@ void tst_qqmllistcompositor::listItemsMoved()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
QVector<C::Remove> removes;
@@ -1612,7 +1613,7 @@ void tst_qqmllistcompositor::listItemsChanged()
compositor.setGroupCount(4);
compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
- foreach (const Range &range, ranges)
+ for (const Range &range : std::as_const(ranges))
compositor.append(range.list, range.index, range.count, range.flags);
QVector<C::Change> changes;
diff --git a/tests/auto/qml/qqmllistmodel/CMakeLists.txt b/tests/auto/qml/qqmllistmodel/CMakeLists.txt
index 7c689e643c..133a2dce5d 100644
--- a/tests/auto/qml/qqmllistmodel/CMakeLists.txt
+++ b/tests/auto/qml/qqmllistmodel/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmllistmodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmllistmodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmllistmodel/data/nestedLists.qml b/tests/auto/qml/qqmllistmodel/data/nestedLists.qml
new file mode 100644
index 0000000000..4aea6f1ae1
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodel/data/nestedLists.qml
@@ -0,0 +1,37 @@
+import QtQuick
+
+Item {
+ id: mainWindow
+
+ function load(data) {
+ model.clear()
+ for (var i = 0; i < data.length; i++)
+ model.append(data[i])
+ }
+
+ ListModel {
+ id: model
+ }
+
+ Repeater {
+ objectName: "topLevel"
+ model: model
+ Item {
+ objectName: _headline
+ Repeater {
+ objectName: "month"
+ model: _weeks
+ Item {
+ objectName: index
+ Repeater {
+ objectName: "week"
+ model: _week
+ Item {
+ objectName: _day
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index e522f280a3..d738a7f68d 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -1,13 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickanimation_p.h>
#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuick/private/qquickrepeater_p.h>
#include <QtQml/private/qqmlengine_p.h>
#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qqmlexpression_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <QQmlComponent>
#include <QtCore/qtimer.h>
@@ -119,6 +121,7 @@ private slots:
void objectOwnershipFlip();
void enumsInListElement();
void protectQObjectFromGC();
+ void nestedLists();
};
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@@ -248,8 +251,8 @@ void tst_qqmllistmodel::static_types()
QVERIFY(!component.isError());
- QObject *obj = component.create();
- QVERIFY(obj != nullptr);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj);
if (error.isEmpty()) {
QVariant actual = obj->property("test");
@@ -257,8 +260,6 @@ void tst_qqmllistmodel::static_types()
QCOMPARE(actual, value);
QCOMPARE(actual.toString(), value.toString());
}
-
- delete obj;
}
void tst_qqmllistmodel::static_i18n_data()
@@ -319,15 +320,13 @@ void tst_qqmllistmodel::static_i18n()
QVERIFY(!component.isError());
- QObject *obj = component.create();
- QVERIFY(obj != nullptr);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj);
QVariant actual = obj->property("test");
QCOMPARE(actual, value);
QCOMPARE(actual.toString(), value.toString());
-
- delete obj;
}
void tst_qqmllistmodel::dynamic_i18n_data()
@@ -368,16 +367,15 @@ void tst_qqmllistmodel::dynamic_i18n()
QVERIFY(!component.isError());
- QObject *obj = component.create();
- QVERIFY(obj != nullptr);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj);
QVariant actual = obj->property("test");
QCOMPARE(actual, value);
QCOMPARE(actual.toString(), value.toString());
-
- delete obj;
}
+
void tst_qqmllistmodel::static_nestedElements()
{
QFETCH(int, elementCount);
@@ -406,14 +404,12 @@ void tst_qqmllistmodel::static_nestedElements()
QQmlComponent component(&engine);
component.setData(componentStr.toUtf8(), QUrl::fromLocalFile(""));
- QObject *obj = component.create();
- QVERIFY(obj != nullptr);
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj);
QVariant count = obj->property("count");
QCOMPARE(count.typeId(), QMetaType::Int);
QCOMPARE(count.toInt(), elementCount);
-
- delete obj;
}
void tst_qqmllistmodel::static_nestedElements_data()
@@ -606,8 +602,8 @@ void tst_qqmllistmodel::enumerate()
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("enumerate.qml"));
QVERIFY(!component.isError());
- QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(component.create()) };
+ QVERIFY(item);
QLatin1String expectedStrings[] = {
QLatin1String("val1=1Y"),
@@ -638,8 +634,6 @@ void tst_qqmllistmodel::enumerate()
}
QCOMPARE(matchCount, expectedStringCount);
-
- delete item;
}
void tst_qqmllistmodel::error_data()
@@ -779,9 +773,9 @@ void tst_qqmllistmodel::get()
component.setData(
"import QtQuick 2.0\n"
"ListModel {}\n", QUrl());
- QQmlListModel *model = qobject_cast<QQmlListModel*>(component.create());
+ std::unique_ptr<QQmlListModel> model { qobject_cast<QQmlListModel*>(component.create()) };
model->setDynamicRoles(dynamicRoles);
- engine.rootContext()->setContextProperty("model", model);
+ engine.rootContext()->setContextProperty("model", model.get());
RUNEXPR("model.append({roleA: 100})");
RUNEXPR("model.append({roleA: 200, roleB: 400})");
@@ -789,12 +783,12 @@ void tst_qqmllistmodel::get()
RUNEXPR("model.append({roleC: {} })");
RUNEXPR("model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })");
- QSignalSpy spy(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
- QQmlExpression expr(engine.rootContext(), model, expression);
+ QSignalSpy spy(model.get(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)));
+ QQmlExpression expr(engine.rootContext(), model.get(), expression);
expr.evaluate();
QVERIFY(!expr.hasError());
- int role = roleFromName(model, roleName);
+ int role = roleFromName(model.get(), roleName);
QVERIFY(role >= 0);
if (roleValue.typeId() == QMetaType::QVariantList) {
@@ -810,8 +804,6 @@ void tst_qqmllistmodel::get()
QCOMPARE(spyResult.at(0).value<QModelIndex>(), model->index(index, 0, QModelIndex()));
QCOMPARE(spyResult.at(1).value<QModelIndex>(), model->index(index, 0, QModelIndex())); // only 1 item is modified at a time
QCOMPARE(spyResult.at(2).value<QVector<int> >(), (QVector<int>() << role));
-
- delete model;
}
void tst_qqmllistmodel::get_data()
@@ -857,11 +849,11 @@ void tst_qqmllistmodel::get_nested()
component.setData(
"import QtQuick 2.0\n"
"ListModel {}", QUrl());
- QQmlListModel *model = qobject_cast<QQmlListModel*>(component.create());
+ std::unique_ptr<QQmlListModel> model { qobject_cast<QQmlListModel*>(component.create()) };
model->setDynamicRoles(dynamicRoles);
QVERIFY(component.errorString().isEmpty());
QQmlListModel *childModel;
- engine.rootContext()->setContextProperty("model", model);
+ engine.rootContext()->setContextProperty("model", model.get());
RUNEXPR("model.append({ listRoleA: [\n"
"{ roleA: 100 },\n"
@@ -908,16 +900,16 @@ void tst_qqmllistmodel::get_nested()
for (int i=0; i<testData.size(); i++) {
int outerListIndex = testData[i].first;
QString outerListRoleName = testData[i].second;
- int outerListRole = roleFromName(model, outerListRoleName);
+ int outerListRole = roleFromName(model.get(), outerListRoleName);
QVERIFY(outerListRole >= 0);
childModel = qobject_cast<QQmlListModel*>(model->data(outerListIndex, outerListRole).value<QObject*>());
QVERIFY(childModel);
QString extendedExpression = QString("get(%1).%2.%3").arg(outerListIndex).arg(outerListRoleName).arg(expression);
- QQmlExpression expr(engine.rootContext(), model, extendedExpression);
+ QQmlExpression expr(engine.rootContext(), model.get(), extendedExpression);
- QSignalSpy spy(childModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ QSignalSpy spy(childModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)));
expr.evaluate();
QVERIFY(!expr.hasError());
@@ -935,8 +927,6 @@ void tst_qqmllistmodel::get_nested()
QCOMPARE(spyResult.at(1).value<QModelIndex>(), childModel->index(index, 0, QModelIndex())); // only 1 item is modified at a time
QCOMPARE(spyResult.at(2).value<QVector<int> >(), (QVector<int>() << role));
}
-
- delete model;
}
void tst_qqmllistmodel::get_nested_data()
@@ -949,16 +939,14 @@ void tst_qqmllistmodel::crash_model_with_multiple_roles()
{
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("multipleroles.qml"));
- QObject *rootItem = component.create();
+ std::unique_ptr<QObject> rootItem { component.create() };
QVERIFY(component.errorString().isEmpty());
- QVERIFY(rootItem != nullptr);
+ QVERIFY(rootItem);
QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel");
QVERIFY(model != nullptr);
// used to cause a crash
model->setProperty(0, "black", true);
-
- delete rootItem;
}
void tst_qqmllistmodel::crash_model_with_unknown_roles()
@@ -983,22 +971,19 @@ void tst_qqmllistmodel::crash_model_with_dynamic_roles()
// setting a dynamic role to a QObject value, then triggering dtor
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("dynamicroles.qml"));
- QObject *rootItem = component.create();
+ std::unique_ptr<QObject> rootItem { component.create() };
qWarning() << component.errorString();
QVERIFY(component.errorString().isEmpty());
- QVERIFY(rootItem != 0);
+ QVERIFY(rootItem.get() != 0);
QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel");
QVERIFY(model != 0);
QMetaObject::invokeMethod(model, "appendNewElement");
- QObject *testObj = new QObject;
- model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj));
- delete testObj;
+ model->setProperty(0, "obj", QVariant::fromValue<QObject*>(std::make_unique<QObject>().get()));
- // delete the root item, which will cause the model dtor to run
- // previously, this would crash as it attempted to delete testObj.
- delete rootItem;
+ // Let root item go out of scope to let the model dtor run.
+ // Previously, this would crash as it attempted to delete the already-deleted temporary QObject.
}
{
@@ -1006,22 +991,18 @@ void tst_qqmllistmodel::crash_model_with_dynamic_roles()
// DynamicRoleModelNode::updateValues() to trigger unsafe qobject_cast
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("dynamicroles.qml"));
- QObject *rootItem = component.create();
+ std::unique_ptr<QObject> rootItem { component.create() };
qWarning() << component.errorString();
QVERIFY(component.errorString().isEmpty());
- QVERIFY(rootItem != 0);
+ QVERIFY(rootItem.get() != 0);
QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel");
QVERIFY(model != 0);
QMetaObject::invokeMethod(model, "appendNewElement");
- QObject *testObj = new QObject;
- model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj));
- delete testObj;
+ model->setProperty(0, "obj", QVariant::fromValue<QObject*>(std::make_unique<QObject>().get()));
QMetaObject::invokeMethod(model, "setElementAgain");
-
- delete rootItem;
}
{
@@ -1098,21 +1079,21 @@ void tst_qqmllistmodel::property_changes()
expr.evaluate();
QVERIFY2(!expr.hasError(), qPrintable(expr.error().toString()));
- QString signalHandler = "on" + QString(roleName[0].toUpper()) + roleName.mid(1, roleName.size()) + "Changed:";
+ QString signalHandler = QQmlSignalNames::propertyNameToChangedHandlerName(roleName);
QString qml = "import QtQuick 2.0\n"
"Connections {\n"
"property bool gotSignal: false\n"
"target: model.get(" + QString::number(listIndex) + ")\n"
- + signalHandler + " gotSignal = true\n"
+ + signalHandler + ": gotSignal = true\n"
"}\n";
QQmlComponent component(&engine);
component.setData(qml.toUtf8(), QUrl::fromLocalFile(""));
engine.rootContext()->setContextProperty("model", &model);
- QObject *connectionsObject = component.create();
+ std::unique_ptr<QObject> connectionsObject { component.create() };
QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
- QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)));
expr.setExpression(script_change);
expr.evaluate();
@@ -1132,8 +1113,6 @@ void tst_qqmllistmodel::property_changes()
expr.setExpression(testExpression);
QCOMPARE(expr.evaluate().toBool(), true);
-
- delete connectionsObject;
}
void tst_qqmllistmodel::property_changes_data()
@@ -1354,10 +1333,8 @@ void tst_qqmllistmodel::empty_element_warning()
component.setData(qml.toUtf8(), QUrl::fromLocalFile(QString("dummy.qml")));
QVERIFY(!component.isError());
- QObject *obj = component.create();
- QVERIFY(obj != nullptr);
-
- delete obj;
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj);
}
void tst_qqmllistmodel::datetime_data()
@@ -1549,6 +1526,8 @@ void tst_qqmllistmodel::modify_through_delegate()
" ListElement { name: \"Doe\"; age: 33 }\n"
" }\n"
" ListView {\n"
+ " height: 100\n" \
+ " width: 100\n" \
" model: testModel\n"
" delegate: Item {\n"
" Component.onCompleted: model.age = 18;\n"
@@ -1929,6 +1908,154 @@ void tst_qqmllistmodel::protectQObjectFromGC()
}
}
+static QVariantList createLast7Days()
+{
+ QVariantList last7DaysList;
+ for (int i = 0; i < 7; i++) {
+ QVariantMap map;
+ map.insert("_day", i);
+ last7DaysList.append(map);
+ }
+ return last7DaysList;
+}
+
+static QVariantList createWeekChartModels()
+{
+ QVariantList list;
+ for (int i = 0; i < 4; i++) {
+ QVariantMap map;
+ map.insert("_week", createLast7Days());
+ list.append(map);
+ }
+ return list;
+}
+
+static QVariantList createVariantModel()
+{
+ QVariantMap element1;
+ element1.insert("_headline", "Element 1");
+ element1.insert("_weeks", createWeekChartModels());
+
+ QVariantMap element2;
+ element2.insert("_headline", "Element 2");
+ element2.insert("_weeks", createWeekChartModels());
+
+ QVariantMap element3;
+ element3.insert("_headline", "Element 3");
+ element3.insert("_weeks", createWeekChartModels());
+
+ QVariantList list;
+ list.append(element1);
+ list.append(element2);
+ list.append(element3);
+
+ return list;
+}
+
+class Day : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int _day READ _day CONSTANT)
+public:
+ Day(int day, QObject *parent = nullptr) : QObject(parent), day(day) {}
+ int _day() const { return day; }
+private:
+ int day = 0;
+};
+
+class Week : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<Day> _week READ _week)
+public:
+ Week(QObject *parent = nullptr) : QObject(parent)
+ {
+ for (int i = 0; i < 7; ++i)
+ week.append(new Day(i, this));
+ }
+
+ QQmlListProperty<Day> _week() { return QQmlListProperty<Day>(this, &week); }
+
+private:
+ QList<Day *> week;
+};
+
+class Month : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<Week> _weeks READ _weeks)
+ Q_PROPERTY(QString _headline READ _headline CONSTANT)
+public:
+
+ Month(int i, QObject *parent = nullptr)
+ : QObject(parent)
+ , headline(QLatin1String("Element ") + QString::number(i))
+ {
+ for (int i = 0; i < 4; ++i)
+ weeks.append(new Week(this));
+ }
+
+ QQmlListProperty<Week> _weeks() { return QQmlListProperty<Week>(this, &weeks); }
+ QString _headline() const { return headline; }
+
+private:
+ QList<Week *> weeks;
+ QString headline;
+};
+
+static void verifyLists(const QVariantList &list, QQuickRepeater *topLevel)
+{
+ QVERIFY(topLevel);
+ QCOMPARE(topLevel->count(), 3);
+
+ for (int month = 0; month < 3; ++month) {
+ const QVariantMap monthData = list[month].toMap();
+ const QQuickItem *monthItem = topLevel->itemAt(month);
+ QCOMPARE(monthItem->objectName(), monthData["_headline"].toString());
+ const QQuickRepeater *monthRepeater = monthItem->findChild<QQuickRepeater *>("month");
+ QVERIFY(monthRepeater);
+ QCOMPARE(monthRepeater->count(), 4);
+ const QVariantList weekList = monthData["_weeks"].toList();
+ for (int week = 0; week < 4; ++week) {
+ const QVariantList weekData = weekList[week].toMap()["_week"].toList();
+ const QQuickItem *weekItem = monthRepeater->itemAt(week);
+ QCOMPARE(weekItem->objectName(), QString::number(week));
+ const QQuickRepeater *weekRepeater = weekItem->findChild<QQuickRepeater *>("week");
+ QVERIFY(weekRepeater);
+ QCOMPARE(weekRepeater->count(), 7);
+ for (int day = 0; day < 7; ++day) {
+ const QVariantMap dayData = weekData[day].toMap();
+ const QQuickItem *dayItem = weekRepeater->itemAt(day);
+ QCOMPARE(dayItem->objectName(), dayData["_day"]);
+ }
+ }
+ }
+}
+
+void tst_qqmllistmodel::nestedLists()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("nestedLists.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+
+ QQuickRepeater *topLevel = o->findChild<QQuickRepeater *>("topLevel");
+
+ const QVariantList list = createVariantModel();
+ QMetaObject::invokeMethod(o.data(), "load", Q_ARG(QVariant, QVariant::fromValue(list)));
+ verifyLists(list, topLevel);
+
+ const QObjectList objects {
+ new Month(1, o.data()),
+ new Month(2, o.data()),
+ new Month(3, o.data())
+ };
+
+ QMetaObject::invokeMethod(o.data(), "load", Q_ARG(QVariant, QVariant::fromValue(objects)));
+ verifyLists(list, topLevel);
+}
+
QTEST_MAIN(tst_qqmllistmodel)
#include "tst_qqmllistmodel.moc"
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt b/tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt
index ac77f65b68..93d597e687 100644
--- a/tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt
+++ b/tests/auto/qml/qqmllistmodelworkerscript/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmllistmodelworkerscript Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmllistmodelworkerscript LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
index 4875602314..93f18ea8d5 100644
--- a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
+++ b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquicktext_p.h>
@@ -55,7 +56,7 @@ public:
private:
int roleFromName(const QQmlListModel *model, const QString &roleName);
- QQuickItem *createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model);
+ std::unique_ptr<QQuickItem> createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model);
void waitForWorker(QQuickItem *item);
static bool compareVariantList(const QVariantList &testList, QVariant object);
@@ -134,9 +135,9 @@ int tst_qqmllistmodelworkerscript::roleFromName(const QQmlListModel *model, cons
return model->roleNames().key(roleName.toUtf8(), -1);
}
-QQuickItem *tst_qqmllistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model)
+std::unique_ptr<QQuickItem> tst_qqmllistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model)
{
- QQuickItem *item = qobject_cast<QQuickItem*>(component->create());
+ std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(component->create()) };
QQmlEngine::setContextForObject(model, eng->rootContext());
if (item)
item->setProperty("model", QVariant::fromValue(model));
@@ -326,15 +327,16 @@ void tst_qqmllistmodelworkerscript::dynamic_worker()
model.setDynamicRoles(dynamicRoles);
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("model.qml"));
- QQuickItem *item = createWorkerTest(&eng, &component, &model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model);
+ QVERIFY(item);
QSignalSpy spyCount(&model, SIGNAL(countChanged()));
if (script[0] == QLatin1Char('{') && script[script.size()-1] == QLatin1Char('}'))
script = script.mid(1, script.size() - 2);
QVariantList operations;
- foreach (const QString &s, script.split(';')) {
+ const QStringList statements = script.split(';');
+ for (const QString &s : statements) {
if (!s.isEmpty())
operations << s;
}
@@ -342,15 +344,15 @@ void tst_qqmllistmodelworkerscript::dynamic_worker()
if (isValidErrorMessage(warning, dynamicRoles))
QTest::ignoreMessage(QtWarningMsg, warning.toLatin1());
- QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker",
Q_ARG(QVariant, operations)));
- waitForWorker(item);
- QCOMPARE(QQmlProperty(item, "result").read().toInt(), result);
+ waitForWorker(item.get());
+ QCOMPARE(QQmlProperty(item.get(), "result").read().toInt(), result);
if (model.count() > 0)
QVERIFY(spyCount.size() > 0);
- delete item;
+ item.reset();
qApp->processEvents();
}
@@ -377,13 +379,14 @@ void tst_qqmllistmodelworkerscript::dynamic_worker_sync()
model.setDynamicRoles(dynamicRoles);
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("model.qml"));
- QQuickItem *item = createWorkerTest(&eng, &component, &model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model);
+ QVERIFY(item);
if (script[0] == QLatin1Char('{') && script[script.size()-1] == QLatin1Char('}'))
script = script.mid(1, script.size() - 2);
QVariantList operations;
- foreach (const QString &s, script.split(';')) {
+ const QStringList statements = script.split(';');
+ for (const QString &s : statements) {
if (!s.isEmpty())
operations << s;
}
@@ -393,14 +396,14 @@ void tst_qqmllistmodelworkerscript::dynamic_worker_sync()
// execute a set of commands on the worker list model, then check the
// changes are reflected in the list model in the main thread
- QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker",
Q_ARG(QVariant, operations.mid(0, operations.size()-1))));
- waitForWorker(item);
+ waitForWorker(item.get());
QQmlExpression e(eng.rootContext(), &model, operations.last().toString());
QCOMPARE(e.evaluate().toInt(), result);
- delete item;
+ item.reset();
qApp->processEvents();
}
@@ -440,25 +443,25 @@ void tst_qqmllistmodelworkerscript::get_worker()
model.setDynamicRoles(dynamicRoles);
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("model.qml"));
- QQuickItem *item = createWorkerTest(&eng, &component, &model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model);
+ QVERIFY(item);
// Add some values like get() test
- RUNEVAL(item, "model.append({roleA: 100})");
- RUNEVAL(item, "model.append({roleA: 200, roleB: 400})");
- RUNEVAL(item, "model.append({roleA: 200, roleB: 400})");
- RUNEVAL(item, "model.append({roleC: {} })");
- RUNEVAL(item, "model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })");
+ RUNEVAL(item.get(), "model.append({roleA: 100})");
+ RUNEVAL(item.get(), "model.append({roleA: 200, roleB: 400})");
+ RUNEVAL(item.get(), "model.append({roleA: 200, roleB: 400})");
+ RUNEVAL(item.get(), "model.append({roleC: {} })");
+ RUNEVAL(item.get(), "model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })");
int role = roleFromName(&model, roleName);
QVERIFY(role >= 0);
- QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)));
// in the worker thread, change the model data and call sync()
- QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker",
Q_ARG(QVariant, QStringList(expression))));
- waitForWorker(item);
+ waitForWorker(item.get());
// see if we receive the model changes in the main thread's model
if (roleValue.typeId() == QMetaType::QVariantList) {
@@ -474,8 +477,6 @@ void tst_qqmllistmodelworkerscript::get_worker()
QCOMPARE(spyResult.at(0).value<QModelIndex>(), model.index(index, 0, QModelIndex()));
QCOMPARE(spyResult.at(1).value<QModelIndex>(), model.index(index, 0, QModelIndex())); // only 1 item is modified at a time
QVERIFY(spyResult.at(2).value<QVector<int> >().contains(role));
-
- delete item;
}
void tst_qqmllistmodelworkerscript::get_worker_data()
@@ -570,18 +571,18 @@ void tst_qqmllistmodelworkerscript::property_changes_worker()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("model.qml"));
QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8());
- QQuickItem *item = createWorkerTest(&engine, &component, &model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&engine, &component, &model);
+ QVERIFY(item);
QQmlExpression expr(engine.rootContext(), &model, script_setup);
expr.evaluate();
- QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString()));
+ QVERIFY2(!expr.hasError(), qPrintable(expr.error().toString()));
- QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)));
- QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker",
Q_ARG(QVariant, QStringList(script_change))));
- waitForWorker(item);
+ waitForWorker(item.get());
// test itemsChanged() is emitted correctly
if (itemsChanged) {
@@ -592,7 +593,7 @@ void tst_qqmllistmodelworkerscript::property_changes_worker()
QCOMPARE(spyItemsChanged.size(), 0);
}
- delete item;
+ item.reset();
qApp->processEvents();
}
@@ -617,12 +618,12 @@ void tst_qqmllistmodelworkerscript::worker_sync()
model.setDynamicRoles(dynamicRoles);
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("workersync.qml"));
- QQuickItem *item = createWorkerTest(&eng, &component, &model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model);
+ QVERIFY(item);
QCOMPARE(model.count(), 0);
- QVERIFY(QMetaObject::invokeMethod(item, "addItem0"));
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "addItem0"));
QCOMPARE(model.count(), 2);
QVariant childData = model.data(0, 0);
@@ -633,39 +634,39 @@ void tst_qqmllistmodelworkerscript::worker_sync()
QSignalSpy spyModelInserted(&model, SIGNAL(rowsInserted(QModelIndex,int,int)));
QSignalSpy spyChildInserted(childModel, SIGNAL(rowsInserted(QModelIndex,int,int)));
- QVERIFY(QMetaObject::invokeMethod(item, "addItemViaWorker"));
- waitForWorker(item);
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "addItemViaWorker"));
+ waitForWorker(item.get());
QCOMPARE(model.count(), 2);
QCOMPARE(childModel->count(), 1);
QCOMPARE(spyModelInserted.size(), 0);
QCOMPARE(spyChildInserted.size(), 0);
- QVERIFY(QMetaObject::invokeMethod(item, "doSync"));
- waitForWorker(item);
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync"));
+ waitForWorker(item.get());
QCOMPARE(model.count(), 2);
QCOMPARE(childModel->count(), 2);
QCOMPARE(spyModelInserted.size(), 0);
QCOMPARE(spyChildInserted.size(), 1);
- QVERIFY(QMetaObject::invokeMethod(item, "addItemViaWorker"));
- waitForWorker(item);
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "addItemViaWorker"));
+ waitForWorker(item.get());
QCOMPARE(model.count(), 2);
QCOMPARE(childModel->count(), 2);
QCOMPARE(spyModelInserted.size(), 0);
QCOMPARE(spyChildInserted.size(), 1);
- QVERIFY(QMetaObject::invokeMethod(item, "doSync"));
- waitForWorker(item);
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync"));
+ waitForWorker(item.get());
QCOMPARE(model.count(), 2);
QCOMPARE(childModel->count(), 3);
QCOMPARE(spyModelInserted.size(), 0);
QCOMPARE(spyChildInserted.size(), 2);
- delete item;
+ item.reset();
qApp->processEvents();
}
@@ -682,53 +683,51 @@ void tst_qqmllistmodelworkerscript::worker_remove_element()
model.setDynamicRoles(dynamicRoles);
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml"));
- QQuickItem *item = createWorkerTest(&eng, &component, &model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model);
+ QVERIFY(item);
QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int)));
QCOMPARE(model.count(), 0);
QCOMPARE(spyModelRemoved.size(), 0);
- QVERIFY(QMetaObject::invokeMethod(item, "addItem"));
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "addItem"));
QCOMPARE(model.count(), 1);
- QVERIFY(QMetaObject::invokeMethod(item, "removeItemViaWorker"));
- waitForWorker(item);
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "removeItemViaWorker"));
+ waitForWorker(item.get());
QCOMPARE(model.count(), 1);
QCOMPARE(spyModelRemoved.size(), 0);
- QVERIFY(QMetaObject::invokeMethod(item, "doSync"));
- waitForWorker(item);
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync"));
+ waitForWorker(item.get());
QCOMPARE(model.count(), 0);
QCOMPARE(spyModelRemoved.size(), 1);
- delete item;
+ item.reset();
qApp->processEvents();
{
//don't crash if model was deleted earlier
- QQmlListModel* model = new QQmlListModel;
+ std::unique_ptr<QQmlListModel> model = std::make_unique<QQmlListModel>();
model->setDynamicRoles(dynamicRoles);
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml"));
- QQuickItem *item = createWorkerTest(&eng, &component, model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, model.get());
+ QVERIFY(item);
- QVERIFY(QMetaObject::invokeMethod(item, "addItem"));
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "addItem"));
QCOMPARE(model->count(), 1);
- QVERIFY(QMetaObject::invokeMethod(item, "removeItemViaWorker"));
- QVERIFY(QMetaObject::invokeMethod(item, "doSync"));
- delete model;
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "removeItemViaWorker"));
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync"));
+ model.reset();
qApp->processEvents(); //must not crash here
- waitForWorker(item);
-
- delete item;
+ waitForWorker(item.get());
}
}
@@ -745,31 +744,31 @@ void tst_qqmllistmodelworkerscript::worker_remove_list()
model.setDynamicRoles(dynamicRoles);
QQmlEngine eng;
QQmlComponent component(&eng, testFileUrl("workerremovelist.qml"));
- QQuickItem *item = createWorkerTest(&eng, &component, &model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&eng, &component, &model);
+ QVERIFY(item);
QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int)));
QCOMPARE(model.count(), 0);
QCOMPARE(spyModelRemoved.size(), 0);
- QVERIFY(QMetaObject::invokeMethod(item, "addList"));
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "addList"));
QCOMPARE(model.count(), 1);
- QVERIFY(QMetaObject::invokeMethod(item, "removeListViaWorker"));
- waitForWorker(item);
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "removeListViaWorker"));
+ waitForWorker(item.get());
QCOMPARE(model.count(), 1);
QCOMPARE(spyModelRemoved.size(), 0);
- QVERIFY(QMetaObject::invokeMethod(item, "doSync"));
- waitForWorker(item);
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "doSync"));
+ waitForWorker(item.get());
QCOMPARE(model.count(), 0);
QCOMPARE(spyModelRemoved.size(), 1);
- delete item;
+ item.reset();
qApp->processEvents();
}
@@ -792,8 +791,8 @@ void tst_qqmllistmodelworkerscript::dynamic_role()
model.setDynamicRoles(true);
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("model.qml"));
- QQuickItem *item = createWorkerTest(&engine, &component, &model);
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item = createWorkerTest(&engine, &component, &model);
+ QVERIFY(item);
QQmlExpression preExp(engine.rootContext(), &model, preamble);
QCOMPARE(preExp.evaluate().toInt(), 0);
@@ -801,21 +800,22 @@ void tst_qqmllistmodelworkerscript::dynamic_role()
if (script[0] == QLatin1Char('{') && script[script.size()-1] == QLatin1Char('}'))
script = script.mid(1, script.size() - 2);
QVariantList operations;
- foreach (const QString &s, script.split(';')) {
+ const QStringList statements = script.split(';');
+ for (const QString &s : statements) {
if (!s.isEmpty())
operations << s;
}
// execute a set of commands on the worker list model, then check the
// changes are reflected in the list model in the main thread
- QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
+ QVERIFY(QMetaObject::invokeMethod(item.get(), "evalExpressionViaWorker",
Q_ARG(QVariant, operations.mid(0, operations.size()-1))));
- waitForWorker(item);
+ waitForWorker(item.get());
QQmlExpression e(engine.rootContext(), &model, operations.last().toString());
QCOMPARE(e.evaluate().toInt(), result);
- delete item;
+ item.reset();
qApp->processEvents();
}
diff --git a/tests/auto/qml/qqmllistreference/CMakeLists.txt b/tests/auto/qml/qqmllistreference/CMakeLists.txt
index 3286531a45..0fa70259b2 100644
--- a/tests/auto/qml/qqmllistreference/CMakeLists.txt
+++ b/tests/auto/qml/qqmllistreference/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmllistreference Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmllistreference LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmllistreference/data/consoleLogSyntheticList.qml b/tests/auto/qml/qqmllistreference/data/consoleLogSyntheticList.qml
new file mode 100644
index 0000000000..9d2fd3e0b2
--- /dev/null
+++ b/tests/auto/qml/qqmllistreference/data/consoleLogSyntheticList.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ id: self
+
+ function createList() : list<QtObject> { return [self] }
+ Component.onCompleted: console.log(createList())
+}
diff --git a/tests/auto/qml/qqmllistreference/data/listIgnoresNull.qml b/tests/auto/qml/qqmllistreference/data/listIgnoresNull.qml
new file mode 100644
index 0000000000..35c8d66dad
--- /dev/null
+++ b/tests/auto/qml/qqmllistreference/data/listIgnoresNull.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.7
+import Test
+
+Item {
+ id: root
+
+ TestItem {
+ id: testItem
+ }
+
+ Component.onCompleted : {
+ testItem.data.push(null);
+ testItem.data.length = 5;
+ testItem.data.unshift(null);
+
+ var state = Qt.createQmlObject( "import QtQuick 2.7; State{ name: 'MyState' }", root, "dynamicState" );
+ root.states.length = 5;
+ root.states.push(null);
+ root.states.unshift(state);
+ }
+}
diff --git a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
index 049ffb2d72..76711a00e6 100644
--- a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
+++ b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QUrl>
@@ -58,6 +58,9 @@ private slots:
void jsArrayMethods();
void jsArrayMethodsWithParams_data();
void jsArrayMethodsWithParams();
+ void listIgnoresNull_data() { modeData(); }
+ void listIgnoresNull();
+ void consoleLogSyntheticList();
};
class TestType : public QObject
@@ -73,12 +76,18 @@ public:
SyntheticClearAndReplace,
SyntheticRemoveLast,
SyntheticRemoveLastAndReplace,
- AutomaticPointer
+ AutomaticPointer,
+ IgnoreNullValues,
};
static void append(QQmlListProperty<TestType> *p, TestType *v) {
reinterpret_cast<QList<TestType *> *>(p->data)->append(v);
}
+ static void appendNoNullValues(QQmlListProperty<TestType> *p, TestType *v) {
+ if (!v)
+ return;
+ reinterpret_cast<QList<TestType *> *>(p->data)->append(v);
+ }
static qsizetype count(QQmlListProperty<TestType> *p) {
return reinterpret_cast<QList<TestType *> *>(p->data)->size();
}
@@ -121,6 +130,10 @@ public:
case AutomaticPointer:
property = QQmlListProperty<TestType>(this, &data);
break;
+ case IgnoreNullValues:
+ property = QQmlListProperty<TestType>(this, &data, appendNoNullValues, count, at, clear,
+ replace, removeLast);
+ break;
}
}
@@ -142,6 +155,7 @@ void tst_qqmllistreference::modeData()
QTest::addRow("SyntheticClearAndReplace") << TestType::SyntheticClearAndReplace;
QTest::addRow("SyntheticRemoveLast") << TestType::SyntheticRemoveLast;
QTest::addRow("SyntheticRemoveLastAndReplace") << TestType::SyntheticRemoveLastAndReplace;
+ QTest::addRow("IgnoreNullValues") << TestType::IgnoreNullValues;
}
void tst_qqmllistreference::initTestCase()
@@ -1036,6 +1050,50 @@ void tst_qqmllistreference::jsArrayMethodsWithParams()
QCOMPARE(object->property("listPropertyLastIndexOf"), object->property("jsArrayLastIndexOf"));
}
+/*!
+ Some of our list implementations ignore attempts to append a null object.
+ This should result in warnings or type errors, and not crash our wrapper
+ code.
+*/
+void tst_qqmllistreference::listIgnoresNull()
+{
+ QFETCH(const TestType::Mode, mode);
+ static TestType::Mode globalMode;
+ globalMode = mode;
+ struct TestItem : public TestType
+ {
+ TestItem() : TestType(globalMode) {}
+ };
+
+ const auto id = qmlRegisterType<TestItem>("Test", 1, 0, "TestItem");
+ const auto unregister = qScopeGuard([id]{
+ QQmlPrivate::qmlunregister(QQmlPrivate::TypeRegistration, id);
+ });
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("listIgnoresNull.qml"));
+
+ // For lists that don't append null values, creating the component shouldn't crash
+ // in the onCompleted handler, but generate type errors and warnings.
+ if (mode == TestType::IgnoreNullValues) {
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".* QML TestItem: List didn't append all objects$"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".* TypeError: List doesn't append null objects$"));
+ }
+ QScopedPointer<QObject> object( component.create() );
+ QVERIFY(object != nullptr);
+}
+
+void tst_qqmllistreference::consoleLogSyntheticList()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("consoleLogSyntheticList.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QTest::ignoreMessage(
+ QtDebugMsg, QRegularExpression("\\[QObject_QML_[0-9]+\\(0x[0-9a-f]+\\)\\]"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+}
+
QTEST_MAIN(tst_qqmllistreference)
#include "tst_qqmllistreference.moc"
diff --git a/tests/auto/qml/qqmllocale/CMakeLists.txt b/tests/auto/qml/qqmllocale/CMakeLists.txt
index 8603fe454d..beca01f955 100644
--- a/tests/auto/qml/qqmllocale/CMakeLists.txt
+++ b/tests/auto/qml/qqmllocale/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmllocale Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmllocale LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
index 3349f5f9b6..a7ddf79ad5 100644
--- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
+++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
@@ -1,9 +1,11 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
+#include <private/qqmllocale_p.h>
+
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlcomponent.h>
@@ -457,9 +459,10 @@ void tst_qqmllocale::toString_data()
const QDateTime midnight2000(QDate(2000, 1, 1), QTime(0, 0));
// 12 AM might not exist in this timezone (some timezones have transitions at midnight).
if (midnight2000.isValid()) {
- functionCallScript = "locale.toString(new Date(2000, 1, 1))";
- QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript
- << QLatin1String("Tuesday, 1 February 2000 12:00:00 AM ") + midnight2000.timeZoneAbbreviation() << QString();
+ functionCallScript = "locale.toString(new Date(2000, 0, 1))";
+ const QLocale locale("en_AU");
+ QTest::newRow(qPrintable(functionCallScript)) << locale.name() << functionCallScript
+ << locale.toString(midnight2000, QLocale::LongFormat) << QString();
}
functionCallScript = "locale.toString(new Date(2022, 7, 16), [])";
@@ -475,10 +478,12 @@ void tst_qqmllocale::toString_data()
QTest::newRow(qPrintable(functionCallScript)) << "ar" << functionCallScript << "١٦" << QString();
functionCallScript = "locale.toString(new Date(2022, 7, 16), Locale.ShortFormat)";
- QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript << "16/8/22 12:00 AM" << QString();
+ QTest::newRow(qPrintable(functionCallScript))
+ << "en_AU" << functionCallScript << "16/8/22 12:00 am" << QString();
functionCallScript = "locale.toString(new Date(2022, 7, 16, 1, 23, 4), Locale.ShortFormat)";
- QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript << "16/8/22 1:23 AM" << QString();
+ QTest::newRow(qPrintable(functionCallScript))
+ << "en_AU" << functionCallScript << "16/8/22 1:23 am" << QString();
}
void tst_qqmllocale::toString()
@@ -544,7 +549,7 @@ void tst_qqmllocale::weekDays()
Q_ARG(QVariant, QVariant(locale)));
QVariant val = obj->property("weekDays");
- QCOMPARE(val.userType(), qMetaTypeId<QJSValue>());
+ QCOMPARE(val.metaType(), QMetaType::fromType<QList<QQmlLocale::DayOfWeek>>());
QList<QVariant> qmlDays = val.toList();
QList<Qt::DayOfWeek> days = QLocale(locale).weekdays();
@@ -584,7 +589,7 @@ void tst_qqmllocale::uiLanguages()
Q_ARG(QVariant, QVariant(locale)));
QVariant val = obj->property("uiLanguages");
- QCOMPARE(val.userType(), qMetaTypeId<QJSValue>());
+ QCOMPARE(val.metaType(), QMetaType::fromType<QStringList>());
QList<QVariant> qmlLangs = val.toList();
QStringList langs = QLocale(locale).uiLanguages();
@@ -763,7 +768,15 @@ void tst_qqmllocale::testFunctionCall()
STOP_ON_FAILURE
QVERIFY(evaluationResult.canConvert<QString>());
STOP_ON_FAILURE
- QCOMPARE(evaluationResult.toString(), expectedResult);
+
+ // We're not interested in whether the spaces in the date/time format
+ // are breaking or non-breaking.
+ const QString resultWithBreakingSpaces
+ = evaluationResult.toString().replace(u'\u202f', u' ');
+ const QString expectedWithBreakingSpaces
+ = expectedResult.replace(u'\u202f', u' ');
+
+ QCOMPARE(resultWithBreakingSpaces, expectedWithBreakingSpaces);
STOP_ON_FAILURE
} else {
QVERIFY(qmlExpression.hasError());
diff --git a/tests/auto/qml/qqmlmetaobject/CMakeLists.txt b/tests/auto/qml/qqmlmetaobject/CMakeLists.txt
index 9895bb04fb..6f77e030e6 100644
--- a/tests/auto/qml/qqmlmetaobject/CMakeLists.txt
+++ b/tests/auto/qml/qqmlmetaobject/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlmetaobject Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlmetaobject LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp
index d945f460eb..0516140667 100644
--- a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp
+++ b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/QScopedPointer>
diff --git a/tests/auto/qml/qqmlmetatype/CMakeLists.txt b/tests/auto/qml/qqmlmetatype/CMakeLists.txt
index 7a5b47b571..2ab974ca91 100644
--- a/tests/auto/qml/qqmlmetatype/CMakeLists.txt
+++ b/tests/auto/qml/qqmlmetatype/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlmetatype Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlmetatype LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlmetatype/data/Components/App.qml b/tests/auto/qml/qqmlmetatype/data/Components/App.qml
index 57b2b4520a..693cffea40 100644
--- a/tests/auto/qml/qqmlmetatype/data/Components/App.qml
+++ b/tests/auto/qml/qqmlmetatype/data/Components/App.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.0
diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
index c1bac33d87..04c2a5bfdb 100644
--- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
+++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qstandardpaths.h>
#include <qtest.h>
@@ -51,6 +51,9 @@ private slots:
void enumsInRecursiveImport();
void revertValueTypeAnimation();
+
+ void clearPropertyCaches();
+ void builtins();
};
class TestType : public QObject
@@ -724,6 +727,115 @@ void tst_qqmlmetatype::revertValueTypeAnimation()
QCOMPARE(o->property("pointSize").toDouble(), 12.0);
}
+void tst_qqmlmetatype::clearPropertyCaches()
+{
+ qmlClearTypeRegistrations();
+ qmlRegisterType<TestType>("ClearPropertyCaches", 1, 0, "A");
+ QQmlPropertyCache::ConstPtr oldCache = QQmlMetaType::propertyCache(&TestType::staticMetaObject);
+ QVERIFY(oldCache);
+ qmlClearTypeRegistrations();
+ qmlRegisterType<TestType>("ClearPropertyCaches", 1, 0, "B");
+ QQmlPropertyCache::ConstPtr newCache = QQmlMetaType::propertyCache(&TestType::staticMetaObject);
+ QVERIFY(oldCache.data() != newCache.data());
+}
+
+template<typename T>
+void checkBuiltinBaseType()
+{
+ const QQmlType type = QQmlMetaType::qmlType(QMetaType::fromType<T>());
+ QVERIFY(type.isValid());
+ QCOMPARE(type.typeId(), QMetaType::fromType<T>());
+ QCOMPARE(type.qListTypeId(), QMetaType::fromType<QList<T>>());
+}
+
+template<typename T>
+void checkBuiltinListType()
+{
+ const QQmlType listType = QQmlMetaType::qmlListType(QMetaType::fromType<QList<T>>());
+ QVERIFY(listType.isValid());
+ QVERIFY(listType.isSequentialContainer());
+ QCOMPARE(listType.typeId(), QMetaType::fromType<T>());
+ QCOMPARE(listType.qListTypeId(), QMetaType::fromType<QList<T>>());
+ QCOMPARE(listType.listMetaSequence().valueMetaType(), QMetaType::fromType<T>());
+}
+
+template<typename... T>
+void checkBuiltinTypes()
+{
+ (checkBuiltinBaseType<T>(), ...);
+ (checkBuiltinListType<T>(), ...);
+}
+
+template<typename T>
+void checkNamedBuiltin(const QString &name)
+{
+ const QQmlType expected = QQmlMetaType::qmlType(QMetaType::fromType<T>());
+ const QQmlType actual = QQmlMetaType::qmlType("QML/" + name, QTypeRevision::fromVersion(1, 0));
+ if (actual != expected) {
+ qWarning() << Q_FUNC_INFO << "looking for" << name;
+ qWarning() << "found" << actual.module() << actual.elementName() << actual.version()
+ << actual.typeId();
+ qWarning() << "expected" << expected.module() << expected.elementName()
+ << expected.version() << expected.typeId();
+ QFAIL("mismatch");
+ }
+}
+
+template<typename T>
+void checkObjectBuiltin(const QString &name)
+{
+ const QQmlType objectType = QQmlMetaType::qmlType(QMetaType::fromType<T *>());
+ QVERIFY(objectType.isValid());
+ QCOMPARE(objectType.typeId(), QMetaType::fromType<T *>());
+ QCOMPARE(objectType.qListTypeId(), QMetaType::fromType<QQmlListProperty<T>>());
+
+ const QQmlType listType = QQmlMetaType::qmlListType(QMetaType::fromType<QQmlListProperty<T>>());
+ QVERIFY(listType.isValid());
+ QCOMPARE(listType.typeId(), QMetaType::fromType<T *>());
+ QCOMPARE(listType.qListTypeId(), QMetaType::fromType<QQmlListProperty<T>>());
+
+ checkNamedBuiltin<T *>(name);
+}
+
+void tst_qqmlmetatype::builtins()
+{
+ qmlClearTypeRegistrations();
+ QQmlEngine engine; // registers the builtins
+
+ checkBuiltinTypes<
+ QVariant, QJSValue, qint8, quint8, short, ushort, int, uint, qlonglong, qulonglong, float,
+ double, QChar, QString, bool, QDateTime, QDate, QTime, QUrl, QByteArray>();
+
+ checkNamedBuiltin<QVariant>("var");
+ checkNamedBuiltin<QVariant>("variant");
+ checkNamedBuiltin<int>("int");
+ checkNamedBuiltin<double>("double");
+ checkNamedBuiltin<double>("real");
+ checkNamedBuiltin<QString>("string");
+ checkNamedBuiltin<bool>("bool");
+ checkNamedBuiltin<QDateTime>("date");
+ checkNamedBuiltin<QUrl>("url");
+
+#if QT_CONFIG(regularexpression)
+ checkBuiltinBaseType<QRegularExpression>();
+ checkBuiltinListType<QRegularExpression>();
+ checkNamedBuiltin<QRegularExpression>("regexp");
+#endif
+
+ // Can't retrieve this one by metatype
+ const QQmlType voidType = QQmlMetaType::qmlType("QML/void", QTypeRevision::fromVersion(1, 0));
+ QVERIFY(voidType.isValid());
+ QCOMPARE(voidType.typeId(), QMetaType());
+ QCOMPARE(voidType.qListTypeId(), QMetaType());
+
+ // No separate list types
+ checkBuiltinBaseType<std::nullptr_t>();
+ checkBuiltinBaseType<QVariantMap>();
+
+ checkObjectBuiltin<QObject>("QtObject");
+ checkObjectBuiltin<QQmlComponent>("Component");
+}
+
QTEST_MAIN(tst_qqmlmetatype)
#include "tst_qqmlmetatype.moc"
diff --git a/tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt b/tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt
index 1d536afd24..4508ae3986 100644
--- a/tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt
+++ b/tests/auto/qml/qqmlmoduleplugin/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlmoduleplugin Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlmoduleplugin LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt b/tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt
index ee68ebdfe0..56373ba3f4 100644
--- a/tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt
+++ b/tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt
@@ -1,2 +1,2 @@
1:-1:a component declaration requires two or three arguments, but 4 were provided
-2:-1:internal types require 2 arguments, but 3 were provided
+2:-1:invalid version bar, expected <major>.<minor>
diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp
index b97fdf7cff..c60161f424 100644
--- a/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp
index cecc074946..000a75375f 100644
--- a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp
index 5922ca34f5..5ffd28dcbb 100644
--- a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
index 4400c11996..858b2b4704 100644
--- a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp
index 8f20900838..00be65b599 100644
--- a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp b/tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp
index 9d8c110db6..519b567c1d 100644
--- a/tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/optionalPlugin/optionalPlugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp
index 9b54f3946a..7061c5a8c1 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp
index eaa76e08d4..0ca3603267 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp
index d5906611b7..c97824112e 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp
index db158e6b6d..1ac6e8902f 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp
index f1043b4b72..4dbc3c15bb 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp
index 410d2c483a..3e5ae31d62 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp
index 2142ac604d..dcf9ed676a 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp
index c9cd28fc83..84b795d165 100644
--- a/tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/pluginMixed/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp
index 698e5f2692..26d9977f7d 100644
--- a/tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/pluginVersion/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp
index e49d1c1a28..c8620cbe5e 100644
--- a/tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/pluginWithQmlFile/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp
index 96d01191fd..3e4987f9b1 100644
--- a/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp
index 4383325d93..40eebfe3bb 100644
--- a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp
index 3e851c1df3..644719cebf 100644
--- a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp
index d8de79dcfd..590f10dced 100644
--- a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
#include <QDebug>
diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp
index ef55650a31..160f8f5ac6 100644
--- a/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp
index d50de5242c..39f7f86acf 100644
--- a/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QStringList>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
index a4901aebad..e57eb1b65a 100644
--- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <qdir.h>
#include <QtQml/qqmlengine.h>
@@ -130,7 +131,9 @@ void registerStaticPlugin(const char *uri)
PluginType::metaData.append(char(QT_VERSION_MAJOR));
PluginType::metaData.append(char(QT_VERSION_MINOR));
PluginType::metaData.append(char(qPluginArchRequirements()));
+#if QT_CONFIG(cborstreamwriter)
PluginType::metaData.append(QCborValue(QCborMap::fromJsonObject(md)).toCbor());
+#endif
auto rawMetaDataFunctor = []() -> QPluginMetaData {
return {reinterpret_cast<const uchar *>(PluginType::metaData.constData()), size_t(PluginType::metaData.size())};
@@ -207,13 +210,13 @@ void tst_qqmlmoduleplugin::importsPlugin()
QTest::ignoreMessage(QtWarningMsg, qPrintable(QString("import%1 worked").arg(suffix)));
QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlPluginType' does not contain a module identifier directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFileUrl(qmlFile));
- foreach (QQmlError err, component.errors())
+ const auto errors = component.errors();
+ for (const QQmlError &err : errors)
qWarning() << err;
VERIFY_ERRORS(0);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
QCOMPARE(object->property("value").toInt(),123);
- delete object;
}
void tst_qqmlmoduleplugin::importsPlugin_data()
@@ -282,12 +285,12 @@ void tst_qqmlmoduleplugin::importPluginWithQmlFile()
QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestPluginWithQmlFile' does not contain a module identifier directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFileUrl(QStringLiteral("pluginWithQmlFile.qml")));
- foreach (QQmlError err, component.errors())
+ const auto errors = component.errors();
+ for (const QQmlError &err : errors)
qWarning() << err;
VERIFY_ERRORS(0);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
- delete object;
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
}
void tst_qqmlmoduleplugin::remoteImportWithQuotedUrl()
@@ -300,12 +303,12 @@ void tst_qqmlmoduleplugin::remoteImportWithQuotedUrl()
component.setData(qml.toUtf8(), QUrl());
QTRY_COMPARE(component.status(), QQmlComponent::Ready);
- QObject *object = component.create();
+ std::unique_ptr<QObject> object { component.create() };
QCOMPARE(object->property("width").toInt(), 300);
- QVERIFY(object != nullptr);
- delete object;
+ QVERIFY(object.get() != nullptr);
- foreach (QQmlError err, component.errors())
+ const auto errors = component.errors();
+ for (const QQmlError &err : errors)
qWarning() << err;
VERIFY_ERRORS(0);
}
@@ -323,12 +326,12 @@ void tst_qqmlmoduleplugin::remoteImportWithUnquotedUri()
QTRY_COMPARE(component.status(), QQmlComponent::Ready);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
QCOMPARE(object->property("width").toInt(), 300);
- delete object;
- foreach (QQmlError err, component.errors())
+ const auto errors = component.errors();
+ for (const QQmlError &err : errors)
qWarning() << err;
VERIFY_ERRORS(0);
}
@@ -368,20 +371,18 @@ void tst_qqmlmoduleplugin::importsMixedQmlCppPlugin()
{
QQmlComponent component(&engine, testFileUrl(QStringLiteral("importsMixedQmlCppPlugin.qml")));
- QObject *o = component.create();
- QVERIFY2(o != nullptr, msgComponentError(component, &engine));
+ std::unique_ptr<QObject> o { component.create() };
+ QVERIFY2(o.get() != nullptr, msgComponentError(component, &engine));
QCOMPARE(o->property("test").toBool(), true);
- delete o;
}
{
QQmlComponent component(&engine, testFileUrl(QStringLiteral("importsMixedQmlCppPlugin.2.qml")));
- QObject *o = component.create();
- QVERIFY2(o != nullptr, msgComponentError(component, &engine));
+ std::unique_ptr<QObject> o { component.create() };
+ QVERIFY2(o.get() != nullptr, msgComponentError(component, &engine));
QCOMPARE(o->property("test").toBool(), true);
QCOMPARE(o->property("test2").toBool(), true);
- delete o;
}
@@ -444,9 +445,8 @@ void tst_qqmlmoduleplugin::implicitQmldir()
QList<QQmlError> errors = component.errors();
VERIFY_ERRORS(errorFileName.toLatin1().constData());
QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
- QObject *obj = component.create();
- QVERIFY(!obj);
- delete obj;
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(!obj.get());
}
void tst_qqmlmoduleplugin::importsNested_data()
@@ -481,17 +481,17 @@ void tst_qqmlmoduleplugin::importsNested()
QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlNestedPluginType' does not contain a module identifier directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFile(file));
- QObject *obj = component.create();
+ std::unique_ptr<QObject> obj { component.create() };
if (errorFile.isEmpty()) {
if (qgetenv("DEBUG") != "" && !component.errors().isEmpty())
qWarning() << "Unexpected Errors:" << component.errors();
- QVERIFY(obj);
- delete obj;
+ QVERIFY(obj.get());
+ obj.reset();
} else {
QList<QQmlError> errors = component.errors();
VERIFY_ERRORS(errorFile.toLatin1().constData());
- QVERIFY(!obj);
+ QVERIFY(!obj.get());
}
}
@@ -684,13 +684,13 @@ void tst_qqmlmoduleplugin::importsChildPlugin()
QTest::ignoreMessage(QtWarningMsg, "child import worked");
QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlPluginType.ChildPlugin' does not contain a module identifier directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFileUrl(QStringLiteral("child.qml")));
- foreach (QQmlError err, component.errors())
+ const auto errors = component.errors();
+ for (const QQmlError &err : errors)
qWarning() << err;
VERIFY_ERRORS(0);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
QCOMPARE(object->property("value").toInt(),123);
- delete object;
}
void tst_qqmlmoduleplugin::importsChildPlugin2()
@@ -701,13 +701,13 @@ void tst_qqmlmoduleplugin::importsChildPlugin2()
QTest::ignoreMessage(QtWarningMsg, "child import2 worked");
QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlPluginType.ChildPlugin' does not contain a module identifier directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFileUrl(QStringLiteral("child2.qml")));
- foreach (QQmlError err, component.errors())
+ const auto errors = component.errors();
+ for (const QQmlError &err : errors)
qWarning() << err;
VERIFY_ERRORS(0);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
QCOMPARE(object->property("value").toInt(),123);
- delete object;
}
void tst_qqmlmoduleplugin::importsChildPlugin21()
@@ -718,13 +718,13 @@ void tst_qqmlmoduleplugin::importsChildPlugin21()
QTest::ignoreMessage(QtWarningMsg, "child import2.1 worked");
QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlPluginType.ChildPlugin' does not contain a module identifier directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFileUrl(QStringLiteral("child21.qml")));
- foreach (QQmlError err, component.errors())
+ const auto errors = component.errors();
+ for (const QQmlError &err : errors)
qWarning() << err;
VERIFY_ERRORS(0);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
QCOMPARE(object->property("value").toInt(),123);
- delete object;
}
void tst_qqmlmoduleplugin::parallelPluginImport()
@@ -780,10 +780,9 @@ void tst_qqmlmoduleplugin::multiSingleton()
engine.addImportPath(m_importsDirectory);
QQmlComponent component(&engine, testFileUrl("multiSingleton.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object.get() != nullptr);
QCOMPARE(obj.objectName(), QLatin1String("first"));
- delete object;
}
void tst_qqmlmoduleplugin::optionalPlugin()
diff --git a/tests/auto/qml/qqmlnotifier/CMakeLists.txt b/tests/auto/qml/qqmlnotifier/CMakeLists.txt
index 44bd62ed9c..f91c1411e3 100644
--- a/tests/auto/qml/qqmlnotifier/CMakeLists.txt
+++ b/tests/auto/qml/qqmlnotifier/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlnotifier Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlnotifier LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
index e3171496d8..501aa472bc 100644
--- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
+++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
#include <QQmlEngine>
diff --git a/tests/auto/qml/qqmlobjectmodel/CMakeLists.txt b/tests/auto/qml/qqmlobjectmodel/CMakeLists.txt
index c54e195b26..aea2cae6dd 100644
--- a/tests/auto/qml/qqmlobjectmodel/CMakeLists.txt
+++ b/tests/auto/qml/qqmlobjectmodel/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlobjectmodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlobjectmodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmlobjectmodel
SOURCES
tst_qqmlobjectmodel.cpp
diff --git a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
index d007f4d024..95a05ebf44 100644
--- a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
+++ b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQmlModels/private/qqmlobjectmodel_p.h>
#include <QtQmlModels/private/qqmlchangeset_p.h>
#include <QtTest/qsignalspy.h>
diff --git a/tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt b/tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt
index f01d281ed3..b6ecc6bc0f 100644
--- a/tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt
+++ b/tests/auto/qml/qqmlopenmetaobject/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlopenmetaobject Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlopenmetaobject LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmlopenmetaobject
SOURCES
tst_qqmlopenmetaobject.cpp
diff --git a/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp b/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp
index 2432282f8c..a3ccda0a1a 100644
--- a/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp
+++ b/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <private/qqmlopenmetaobject_p.h>
diff --git a/tests/auto/qml/qqmlparser/CMakeLists.txt b/tests/auto/qml/qqmlparser/CMakeLists.txt
index 48e1a97f44..faa241bb5e 100644
--- a/tests/auto/qml/qqmlparser/CMakeLists.txt
+++ b/tests/auto/qml/qqmlparser/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlparser Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlparser LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
index adad96b864..0a8411ddcf 100644
--- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
+++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <private/qqmljsengine_p.h>
#include <private/qqmljsparser_p.h>
@@ -34,6 +34,10 @@ private slots:
void codeLocationsWithContinuationStringLiteral_data();
void noSubstitutionTemplateLiteral();
void templateLiteral();
+ void numericSeparator_data();
+ void numericSeparator();
+ void invalidNumericSeparator_data();
+ void invalidNumericSeparator();
void leadingSemicolonInClass();
void templatedReadonlyProperty();
void qmlImportInJS();
@@ -243,15 +247,13 @@ QStringList tst_qqmlparser::findFiles(const QDir &d)
QStringList rv;
- QStringList files = d.entryList(QStringList() << QLatin1String("*.qml") << QLatin1String("*.js"),
- QDir::Files);
- foreach (const QString &file, files) {
+ const QStringList files = d.entryList(
+ QStringList() << QLatin1String("*.qml") << QLatin1String("*.js"), QDir::Files);
+ for (const QString &file : files)
rv << d.absoluteFilePath(file);
- }
- QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
- QDir::NoSymLinks);
- foreach (const QString &dir, dirs) {
+ const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
+ for (const QString &dir : dirs) {
QDir sub = d;
sub.cd(dir);
rv << findFiles(sub);
@@ -277,7 +279,7 @@ void tst_qqmlparser::qmlParser_data()
files << findFiles(QDir(examples));
files << findFiles(QDir(tests));
- foreach (const QString &file, files)
+ for (const QString &file : std::as_const(files))
QTest::newRow(qPrintable(file)) << file;
}
#endif
@@ -498,6 +500,74 @@ void tst_qqmlparser::templateLiteral()
QVERIFY(e);
}
+void tst_qqmlparser::numericSeparator_data() {
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<double>("expected_value");
+
+ QTest::newRow("Separator in decimal literal") << "1_000_000_000" << 1000000000.0;
+ QTest::newRow("Separator in fractional part") << "1000.22_33" << 1000.2233;
+ QTest::newRow("Separator in exponent part") << "1e1_0_0" << std::pow(10, 100);
+ QTest::newRow("Separator in positive exponent part") << "1e+1_0_0" << 1e100;
+ QTest::newRow("Separator in negative exponent part") << "1e-1_0_0" << 1e-100;
+ QTest::newRow("Separator in binary literal with b prefix") << "0b1010_0001_1000_0101" << static_cast<double>(0b1010000110000101);
+ QTest::newRow("Separator in binary literal with B prefix") << "0B01_10_01_10" << static_cast<double>(0b01100110);
+ QTest::newRow("Separator in octal literal with o prefix") << "0o1234_5670" << static_cast<double>(012345670);
+ QTest::newRow("Separator in octal literal with O prefix") << "0O7777_0000" << static_cast<double>(077770000);
+ QTest::newRow("Separator in hex literal with x prefix") << "0xA0_B0_C0" << static_cast<double>(0xA0B0C0);
+ QTest::newRow("Separator in hex literal with X prefix") << "0X1000_AAAA" << static_cast<double>(0x1000AAAA);
+}
+
+void tst_qqmlparser::numericSeparator() {
+ using namespace QQmlJS;
+
+ QFETCH(QString, code);
+ QFETCH(double, expected_value);
+
+ QQmlJS::Engine engine;
+
+ QQmlJS::Lexer lexer(&engine);
+ lexer.setCode(code, 1);
+
+ QQmlJS::Parser parser(&engine);
+ QVERIFY(parser.parseExpression());
+
+ AST::ExpressionNode *expression = parser.expression();
+ QVERIFY(expression);
+
+ auto *literal = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expression);
+ QVERIFY(literal);
+
+ QCOMPARE(literal->value, expected_value);
+ QCOMPARE(literal->firstSourceLocation().begin(), 0u);
+ QCOMPARE(literal->lastSourceLocation().end(), quint32(code.size()));
+}
+
+void tst_qqmlparser::invalidNumericSeparator_data() {
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("Trailing numeric separator") << "1_" << "A trailing numeric separator is not allowed in numeric literals";
+ QTest::newRow("Multiple numeric separators") << "1__2" << "There can be at most one numeric separator beetwen digits";
+}
+
+void tst_qqmlparser::invalidNumericSeparator() {
+ using namespace QQmlJS;
+
+ QFETCH(QString, code);
+ QFETCH(QString, error);
+
+ QQmlJS::Engine engine;
+
+ QQmlJS::Lexer lexer(&engine);
+ lexer.setCode(code, 1);
+
+ QQmlJS::Parser parser(&engine);
+ QVERIFY(!parser.parseExpression());
+
+ QVERIFY(lexer.errorCode() != Lexer::NoError);
+ QCOMPARE(lexer.errorMessage(), error);
+}
+
void tst_qqmlparser::leadingSemicolonInClass()
{
QQmlJS::Engine engine;
diff --git a/tests/auto/qml/qqmlpromise/CMakeLists.txt b/tests/auto/qml/qqmlpromise/CMakeLists.txt
index b12da35bab..749963fc12 100644
--- a/tests/auto/qml/qqmlpromise/CMakeLists.txt
+++ b/tests/auto/qml/qqmlpromise/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlpromise Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlpromise LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml b/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml
index 18e141f787..9846777d97 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml b/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml
index be6651a3c3..103263aca6 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml b/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml
index 1345fbc6d7..8f0ce78006 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml
index 380026c485..eef8065713 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml
index e4bee826fa..b3be71243e 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml b/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml
index 7088aa935c..d6c37bee1d 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml
index c924b476d9..9a5f2d0331 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml
index c7d9536bdb..7767aa49eb 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml
index e494983ae6..f8e5a3c32c 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml
index 541b884303..321506c483 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml
index baafee07a1..800db1144c 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml
index 806d276f51..22f066b2bd 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-get-length.qml b/tests/auto/qml/qqmlpromise/data/promise-get-length.qml
index 96d48b42fb..cf0648025b 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-get-length.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-get-length.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml b/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml
index 454930f06b..c6e9dce72b 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml
index 84f85f19dc..76d344eb3b 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml
index b537af12a3..847911cc9f 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml
index 498c3fa778..d7d5fbd06e 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml b/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml
index 5dc5abcb27..003c2ef102 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml
index 6d09642842..30a08d5004 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml
index 46700fe77e..88005b62e6 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml
index 383c827741..1327d91e5f 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml
index 7d31972633..f71000d0dd 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml
index aefe25bfc1..1571b3a837 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml
index 7ba5352431..62b70828fd 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml
index 52842dde76..08cb4d62df 100644
--- a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml
+++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml b/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml
index 07d607ca99..eaa5c89717 100644
--- a/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml
+++ b/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml b/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml
index 876a953b7e..be31f1a4f2 100644
--- a/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml
+++ b/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml b/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml
index 37c046d823..908149b1e6 100644
--- a/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml
+++ b/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml b/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml
index bb525fab7b..780e75660f 100644
--- a/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml
+++ b/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml b/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml
index 7075fc8a41..778bd085d8 100644
--- a/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml
+++ b/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
QtObject {
diff --git a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
index 2e00729dbc..c6c7767d44 100644
--- a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
+++ b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QString>
#include <QtTest>
#include <QQmlEngine>
diff --git a/tests/auto/qml/qqmlproperty/CMakeLists.txt b/tests/auto/qml/qqmlproperty/CMakeLists.txt
index 0d41159fb3..7148289318 100644
--- a/tests/auto/qml/qqmlproperty/CMakeLists.txt
+++ b/tests/auto/qml/qqmlproperty/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlproperty Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlproperty LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml b/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml
new file mode 100644
index 0000000000..bfa832c1c8
--- /dev/null
+++ b/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml
@@ -0,0 +1,50 @@
+import QtQml
+
+QtObject {
+ id: root
+ objectName: column.text
+
+ property Component c: Component {
+ id: comp
+ QtObject { }
+ }
+
+ property QtObject rectItem: null
+
+ property bool running: false
+
+ property Timer t: Timer {
+ id: column
+ interval: 200
+ running: root.running
+ repeat: true
+
+ property string text: {
+ let item = root.rectItem
+ let result = rectItem ? rectItem.objectName : "Create Object"
+ return result
+ }
+
+ onTriggered: {
+ let rectItem = root.rectItem
+
+ // If rectItem exists destory it.
+ if (rectItem) {
+ rectItem.destroy()
+ return
+ }
+
+ // Otherwise create a new object
+ let newRectItem = comp.createObject(column, {})
+
+
+ // Setting the objectName before setting root.rectItem seems to work.
+ // newRectItem.width = 1200
+ root.rectItem = newRectItem
+
+ // But setting the objectName after setting root.rectItem seems to
+ // cause the issue.
+ newRectItem.objectName = "1300"
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlproperty/data/listAssignmentSignals.qml b/tests/auto/qml/qqmlproperty/data/listAssignmentSignals.qml
new file mode 100644
index 0000000000..8a2c68ab5d
--- /dev/null
+++ b/tests/auto/qml/qqmlproperty/data/listAssignmentSignals.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.0
+
+Item {
+ property int signalCounter: 0
+ property list<QtObject> sourceList: [ QtObject{}, QtObject{}, QtObject{} ]
+ property list<QtObject> targetList1: sourceList
+
+ onTargetList1Changed: signalCounter++
+
+ function assignList() {
+ targetList1 = sourceList
+ }
+}
diff --git a/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml b/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml
new file mode 100644
index 0000000000..0ced54a9ca
--- /dev/null
+++ b/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+ id: root
+ property int onlineStatus
+ property Binding b: Binding {
+ root.onlineStatus: 12
+ }
+}
diff --git a/tests/auto/qml/qqmlproperty/interfaces.h b/tests/auto/qml/qqmlproperty/interfaces.h
index 7ca15214d4..e8f08d06fc 100644
--- a/tests/auto/qml/qqmlproperty/interfaces.h
+++ b/tests/auto/qml/qqmlproperty/interfaces.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef INTERFACES_H
#define INTERFACES_H
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 88c34f0e22..2635af705b 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "interfaces.h"
#include <qtest.h>
@@ -30,8 +30,15 @@ class MyQmlObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QPoint pointProperty MEMBER m_point)
+ Q_PROPERTY(QString a READ objectName NOTIFY somethingHappened)
+ Q_PROPERTY(QString b READ objectName NOTIFY somethingHappened)
public:
- MyQmlObject(QObject *parent = nullptr) : QObject(parent) {}
+ MyQmlObject(QObject *parent = nullptr) : QObject(parent) {
+ connect(this, &QObject::objectNameChanged, this, &MyQmlObject::somethingHappened);
+ }
+
+signals:
+ void somethingHappened();
private:
QPoint m_point;
@@ -217,6 +224,13 @@ private slots:
void bindToNonQObjectTarget();
void assignVariantList();
+
+ void listAssignmentSignals();
+
+ void invalidateQPropertyChangeTriggers();
+
+ void propertyStartsWithOn();
+
private:
QQmlEngine engine;
};
@@ -355,6 +369,7 @@ void tst_qqmlproperty::registeredCompositeTypeProperty()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeProperty.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> obj(component.create());
QVERIFY(obj);
@@ -2416,9 +2431,13 @@ void tst_qqmlproperty::initFlags_data()
const QString names[] = {
QStringLiteral("foo"),
+ QStringLiteral("aChanged"),
QStringLiteral("self.foo"),
+ QStringLiteral("self.aChanged"),
QStringLiteral("onFoo"),
+ QStringLiteral("onAChanged"),
QStringLiteral("self.onFoo"),
+ QStringLiteral("self.onAChanged"),
QStringLiteral("bar"),
QStringLiteral("self.bar"),
QStringLiteral("abar"),
@@ -2453,15 +2472,15 @@ void tst_qqmlproperty::initFlags()
QQmlEngine engine;
QQmlComponent c(&engine);
c.setData(R"(
- import QtQml
- QtObject {
+ import Test
+ MyQmlObject {
id: self
signal foo()
property int bar: 12
property alias abar: self.bar
}
)", QUrl());
- QVERIFY(c.isReady());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
@@ -2471,7 +2490,9 @@ void tst_qqmlproperty::initFlags()
passObject ? o.data() : nullptr, name, context, flags);
const bool usesId = name.startsWith(QStringLiteral("self."));
- const bool hasSignal = name.endsWith(QStringLiteral("foo"));
+ const bool hasSignal = name.endsWith(QStringLiteral("foo"))
+ || name.endsWith(QStringLiteral("aChanged"));
+
if (!passObject && !usesId) {
QVERIFY(!property.isValid());
} else if (passObject && usesId) {
@@ -2486,10 +2507,14 @@ void tst_qqmlproperty::initFlags()
QVERIFY(property.isProperty());
QCOMPARE(property.name(), usesId ? name.mid(strlen("self.")) : name);
QCOMPARE(property.propertyMetaType(), QMetaType::fromType<int>());
- } else {
+ } else if (name.endsWith(QStringLiteral("oo"))) { // 'onFoo' or 'foo'
QVERIFY(property.isSignalProperty());
QCOMPARE(property.name(), QStringLiteral("onFoo"));
QVERIFY(!property.propertyMetaType().isValid());
+ } else {
+ QVERIFY(property.isSignalProperty());
+ QCOMPARE(property.name(), QStringLiteral("onSomethingHappened"));
+ QVERIFY(!property.propertyMetaType().isValid());
}
}
@@ -2544,6 +2569,58 @@ void tst_qqmlproperty::assignVariantList()
QCOMPARE(holder->doubleList(), doubleList);
}
+void tst_qqmlproperty::listAssignmentSignals()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("listAssignmentSignals.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+
+ QCOMPARE(root->property("signalCounter").toInt(), 1);
+ QMetaObject::invokeMethod(root.get(), "assignList");
+ QCOMPARE(root->property("signalCounter").toInt(), 2);
+}
+
+void tst_qqmlproperty::invalidateQPropertyChangeTriggers()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("invalidateQPropertyChangeTriggers.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+
+ QStringList names;
+ QObject::connect(root.data(), &QObject::objectNameChanged, [&](const QString &name) {
+ if (names.length() == 10)
+ root->setProperty("running", false);
+ else
+ names.append(name);
+ });
+
+ root->setProperty("running", true);
+ QTRY_VERIFY(!root->property("running").toBool());
+
+ QCOMPARE(names, (QStringList {
+ u""_s, u"1300"_s, u"Create Object"_s,
+ u""_s, u"1300"_s, u"Create Object"_s,
+ u""_s, u"1300"_s, u"Create Object"_s,
+ u""_s
+ }));
+}
+
+void tst_qqmlproperty::propertyStartsWithOn()
+{
+ QTest::failOnWarning("\"onlineStatus\" is not a properly capitalized signal handler name. "
+ "\"onLineStatus\" would be correct.");
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("propertyStartsWithOn.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(!root.isNull());
+ QCOMPARE(root->property("onlineStatus").toInt(), 12);
+}
+
QTEST_MAIN(tst_qqmlproperty)
#include "tst_qqmlproperty.moc"
diff --git a/tests/auto/qml/qqmlpropertycache/CMakeLists.txt b/tests/auto/qml/qqmlpropertycache/CMakeLists.txt
index 8a7ffd2d9d..6dcb042dab 100644
--- a/tests/auto/qml/qqmlpropertycache/CMakeLists.txt
+++ b/tests/auto/qml/qqmlpropertycache/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlpropertycache Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlpropertycache LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlpropertycache/data/duplicateIdsAndGeneralizedGroupProperties.qml b/tests/auto/qml/qqmlpropertycache/data/duplicateIdsAndGeneralizedGroupProperties.qml
new file mode 100644
index 0000000000..2dd2cd8e21
--- /dev/null
+++ b/tests/auto/qml/qqmlpropertycache/data/duplicateIdsAndGeneralizedGroupProperties.qml
@@ -0,0 +1,64 @@
+import QtQuick 2.15
+
+Item {
+ component First : Item {
+ Item {
+ id: a
+ }
+
+ states: [
+ State {
+ name: "test1"
+
+ PropertyChanges {
+ a.enabled: false
+ }
+ }
+ ]
+ }
+
+ component Second : Item {
+ QtObject {
+ id: a
+ property bool enabled: true
+ }
+
+ states: [
+ State {
+ name: "test2"
+
+ PropertyChanges {
+ a.enabled: false
+ }
+ }
+ ]
+
+ property Component cc: Item {
+ Item { id: a }
+
+ states: [
+ State {
+ name: "test3"
+
+ PropertyChanges {
+ a.enabled: false
+ }
+ }
+ ]
+ }
+ }
+
+ First { id: first }
+ Second { id: second }
+ property Item third: second.cc.createObject();
+
+ Component.onCompleted: {
+ console.log(1, first.data[0].enabled, second.data[0].enabled, third.data[0].enabled);
+ first.state = "test1";
+ console.log(2, first.data[0].enabled, second.data[0].enabled, third.data[0].enabled);
+ second.state = "test2";
+ console.log(3, first.data[0].enabled, second.data[0].enabled, third.data[0].enabled);
+ third.state = "test3";
+ console.log(4, first.data[0].enabled, second.data[0].enabled, third.data[0].enabled);
+ }
+}
diff --git a/tests/auto/qml/qqmlpropertycache/data/overriddenSignal.qml b/tests/auto/qml/qqmlpropertycache/data/overriddenSignal.qml
new file mode 100644
index 0000000000..c948d47a1b
--- /dev/null
+++ b/tests/auto/qml/qqmlpropertycache/data/overriddenSignal.qml
@@ -0,0 +1,36 @@
+import QtQml
+import Test.PropertyCache
+
+QtObject {
+ id: root
+
+ property BaseObject obj: null
+ property Connections connection: Connections {
+ target: obj
+ function onPropertyAChanged() { ++root.a }
+ }
+ onObjChanged: {
+ connection.target = obj // Make sure this takes effect before sending the signal
+ obj.propertyAChanged()
+ }
+
+ property BaseObject obj2: null
+ property Connections connection2: Connections {
+ target: obj2
+ function onSignalA() { ++root.b }
+ }
+ onObj2Changed: {
+ connection2.target = obj2 // Make sure this takes effect before sending the signal
+ obj2.signalA();
+ }
+
+ property BaseObject theObj: BaseObject {}
+ Component.onCompleted: {
+ // Make sure the change signals are triggered also initially
+ obj = theObj;
+ obj2 = theObj;
+ }
+
+ property int a: 0
+ property int b: 0
+}
diff --git a/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal.qml b/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal.qml
new file mode 100644
index 0000000000..b8fee08978
--- /dev/null
+++ b/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal.qml
@@ -0,0 +1,8 @@
+import QtQml
+import Test.PropertyCache
+
+BaseObject {
+ property int callCount: 0
+ function signalA() { ++callCount }
+ Component.onCompleted: signalA()
+}
diff --git a/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal2.qml b/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal2.qml
new file mode 100644
index 0000000000..cbf1cfe037
--- /dev/null
+++ b/tests/auto/qml/qqmlpropertycache/data/qmlOverriddenSignal2.qml
@@ -0,0 +1,6 @@
+import QtQml
+import Test.PropertyCache
+
+BaseObject {
+ signal propertyAChanged
+}
diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
index cfea7c0da1..6af2a3e371 100644
--- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
+++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <private/qqmlpropertycache_p.h>
@@ -8,6 +8,7 @@
#include <QtQml/qqmlcomponent.h>
#include <private/qmetaobjectbuilder_p.h>
#include <private/qqmlcontextdata_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
#include <QCryptographicHash>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -34,6 +35,8 @@ private slots:
void derivedGadgetMethod();
void restrictRegistrationVersion();
void rejectOverriddenFinal();
+ void overriddenSignals();
+ void duplicateIdsAndGeneralizedGroupProperties();
private:
QQmlEngine engine;
@@ -164,6 +167,19 @@ Q_SIGNALS:
void signalB();
};
+class OverriddenSignal : public BaseObject
+{
+ Q_OBJECT
+public:
+ OverriddenSignal(QObject *parent = nullptr) : BaseObject(parent) {}
+
+ Q_INVOKABLE void propertyAChanged() { ++propertyAChangedCalled; }
+ int propertyAChangedCalled = 0;
+
+Q_SIGNALS:
+ void signalA();
+};
+
const QQmlPropertyData *cacheProperty(const QQmlPropertyCache::ConstPtr &cache, const char *name)
{
return cache->property(QLatin1String(name), nullptr, nullptr);
@@ -707,4 +723,70 @@ void tst_qqmlpropertycache::rejectOverriddenFinal()
QCOMPARE(o->property("c").toInt(), 0);
}
+void tst_qqmlpropertycache::overriddenSignals()
+{
+ qmlRegisterTypesAndRevisions<BaseObject>("Test.PropertyCache", 3);
+ QQmlEngine engine;
+
+ QQmlComponent c1(&engine, testFileUrl("overriddenSignal.qml"));
+ QVERIFY2(!c1.isError(), qPrintable(c1.errorString()));
+
+ QScopedPointer<QObject> o(c1.create());
+
+ // the propertyAChanged _signal_ is sent once (initially).
+ QCOMPARE(o->property("a").toInt(), 1);
+
+ // signalA() is invoked once as signal, and the other time as method since both are C++.
+ QCOMPARE(o->property("b").toInt(), 1);
+
+ OverriddenSignal *derived = new OverriddenSignal(o.data());
+
+ // Does call our overridden method, since that is defined in C++
+ QCOMPARE(derived->propertyAChangedCalled, 0);
+ o->setProperty("obj", QVariant::fromValue(derived));
+ QCOMPARE(derived->propertyAChangedCalled, 1);
+
+ o->setProperty("obj2", QVariant::fromValue(derived));
+
+ // the propertyAChanged _signal_ is sent once (initially).
+ QCOMPARE(o->property("a").toInt(), 1);
+
+ // We get to receive both signalA() signals since we only match by name.
+ QCOMPARE(o->property("b").toInt(), 2);
+
+ // We shouldn't be allowed to define such things in QML, though.
+
+ const QUrl c2Url = testFileUrl("qmlOverriddenSignal.qml");
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(c2Url.toString() + QLatin1String(
+ ":6:14: Duplicate method name: "
+ "invalid override of property change signal or superclass signal")));
+ QQmlComponent c2(&engine, c2Url);
+ // Should be an error, but we can't enforce it yet.
+
+ const QUrl c3Url = testFileUrl("qmlOverriddenSignal2.qml");
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(c3Url.toString() + QLatin1String(
+ ":5:12: Duplicate signal name: "
+ "invalid override of property change signal or superclass signal")));
+ QQmlComponent c3(&engine, c3Url);
+ // Should be an error, but we can't enforce it yet.
+}
+
+void tst_qqmlpropertycache::duplicateIdsAndGeneralizedGroupProperties()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("duplicateIdsAndGeneralizedGroupProperties.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(QtDebugMsg, "1 true true true");
+ QTest::ignoreMessage(QtDebugMsg, "2 false true true");
+ QTest::ignoreMessage(QtDebugMsg, "3 false false true");
+ QTest::ignoreMessage(QtDebugMsg, "4 false false false");
+
+ QScopedPointer<QObject> o(c.create());
+}
+
QTEST_MAIN(tst_qqmlpropertycache)
diff --git a/tests/auto/qml/qqmlpropertymap/CMakeLists.txt b/tests/auto/qml/qqmlpropertymap/CMakeLists.txt
index bb314ba006..2a7bd742fa 100644
--- a/tests/auto/qml/qqmlpropertymap/CMakeLists.txt
+++ b/tests/auto/qml/qqmlpropertymap/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlpropertymap Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlpropertymap LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp
index 689202346c..2d91a8502d 100644
--- a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp
+++ b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQml/qqmlengine.h>
@@ -346,9 +346,8 @@ void tst_QQmlPropertyMap::controlledWrite()
component.setData(qmlSource, QUrl::fromLocalFile(""));
QVERIFY(component.isReady());
- QObject *obj = component.create();
- QVERIFY(obj);
- delete obj;
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj.get());
QCOMPARE(map.value(QLatin1String("key1")), QVariant("HELLO WORLD"));
QCOMPARE(map.value(QLatin1String("key2")), QVariant("Goodbye"));
@@ -364,8 +363,7 @@ void tst_QQmlPropertyMap::crashBug()
QQmlComponent c(&engine);
c.setData("import QtQuick 2.0\nBinding { target: map; property: \"myProp\"; value: 10 + 23 }",QUrl());
- QObject *obj = c.create(&context);
- delete obj;
+ std::unique_ptr<QObject> obj { c.create(&context) };
}
void tst_QQmlPropertyMap::QTBUG_17868()
@@ -378,10 +376,9 @@ void tst_QQmlPropertyMap::QTBUG_17868()
map.insert("key", 1);
QQmlComponent c(&engine);
c.setData("import QtQuick 2.0\nItem {property bool error:false; Component.onCompleted: {try{ map.keys(); }catch(e) {error=true;}}}",QUrl());
- QObject *obj = c.create(&context);
- QVERIFY(obj);
+ std::unique_ptr<QObject> obj { c.create(&context) };
+ QVERIFY(obj.get());
QVERIFY(!obj->property("error").toBool());
- delete obj;
}
@@ -434,17 +431,14 @@ void tst_QQmlPropertyMap::QTBUG_31226()
" Timer { interval: 5; running: true; onTriggered: { myProp = qmlPropertyMap.greeting; } }\n"
"}",
QUrl());
- QObject *obj = c.create(&context);
- QVERIFY(obj);
+ std::unique_ptr<QObject> obj { c.create(&context) };
+ QVERIFY(obj.get());
QQmlPropertyMap *qmlPropertyMap = obj->findChild<QQmlPropertyMap*>(QString("qmlPropertyMap"));
QVERIFY(qmlPropertyMap);
qmlPropertyMap->insert("greeting", QString("Hello world!"));
QTRY_COMPARE(obj->property("myProp").toString(), QString("Hello world!"));
-
- delete obj;
-
}
void tst_QQmlPropertyMap::QTBUG_29836()
@@ -461,13 +455,10 @@ void tst_QQmlPropertyMap::QTBUG_29836()
" Timer { interval: 5; running: true; onTriggered: enhancedMap.testSlot() }\n"
"}",
QUrl());
- QObject *obj = c.create(&context);
- QVERIFY(obj);
+ std::unique_ptr<QObject> obj { c.create(&context) };
+ QVERIFY(obj.get());
QTRY_COMPARE(map.testSlotCalled(), true);
-
- delete obj;
-
}
void tst_QQmlPropertyMap::QTBUG_35233()
diff --git a/tests/auto/qml/qqmlqt/CMakeLists.txt b/tests/auto/qml/qqmlqt/CMakeLists.txt
index f43c12d907..31f86d8877 100644
--- a/tests/auto/qml/qqmlqt/CMakeLists.txt
+++ b/tests/auto/qml/qqmlqt/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlqt Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlqt LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
index 5ba1f6f848..9fea41104d 100644
--- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
+++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <private/qqmlengine_p.h>
@@ -27,7 +27,8 @@
#include <QTimeZone>
#include <QtQuickTestUtils/private/qmlutils_p.h>
-#include <private/qglobal_p.h>
+#include <private/qtenvironmentvariables_p.h> // for qTzSet()
+#include <private/qqmlengine_p.h>
class tst_qqmlqt : public QQmlDataTest
{
@@ -56,8 +57,10 @@ private slots:
void alpha();
void tint();
void color();
+#if QT_CONFIG(desktopservices)
void openUrlExternally();
void openUrlExternally_pragmaLibrary();
+#endif
void md5();
void createComponent();
void createComponent_pragmaLibrary();
@@ -612,6 +615,7 @@ public slots:
void noteCall(const QUrl &url) { called++; last = url; }
};
+#if QT_CONFIG(desktopservices)
void tst_qqmlqt::openUrlExternally()
{
MyUrlHandler handler;
@@ -658,6 +662,7 @@ void tst_qqmlqt::openUrlExternally_pragmaLibrary()
QCOMPARE(handler.called,2);
QCOMPARE(handler.last, htmlTestFile);
}
+#endif
void tst_qqmlqt::md5()
{
@@ -805,7 +810,7 @@ void tst_qqmlqt::dateTimeFormatting()
<< component.url().toString() + ":40: TypeError: Passing incompatible arguments to C++ functions from JavaScript is not allowed."
<< component.url().toString() + ":43: TypeError: Passing incompatible arguments to C++ functions from JavaScript is not allowed.";
- foreach (const QString &warning, warnings)
+ for (const QString &warning : std::as_const(warnings))
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
warnings.clear();
@@ -820,7 +825,7 @@ void tst_qqmlqt::dateTimeFormatting()
<< "Could not convert argument 1 at"
<< "expression for err_dateTime2@";
- foreach (const QString &warning, warnings)
+ for (const QString &warning : std::as_const(warnings))
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(warning));
QScopedPointer<QObject> object(component.createWithInitialProperties({
@@ -833,7 +838,7 @@ void tst_qqmlqt::dateTimeFormatting()
QVERIFY(inputProperties.size() > 0);
QVariant result;
- foreach(const QString &prop, inputProperties) {
+ for (const QString &prop : std::as_const(inputProperties)) {
QVERIFY(QMetaObject::invokeMethod(object.data(), method.toUtf8().constData(),
Q_RETURN_ARG(QVariant, result),
Q_ARG(QVariant, prop)));
@@ -1280,12 +1285,13 @@ void tst_qqmlqt::later_data()
void tst_qqmlqt::later()
{
+ QQmlEngine engine;
QFETCH(QString, function);
QFETCH(QStringList, expectedWarnings);
QFETCH(QStringList, propNames);
QFETCH(QVariantList, values);
- foreach (const QString &w, expectedWarnings)
+ for (const QString &w : std::as_const(expectedWarnings))
QTest::ignoreMessage(QtWarningMsg, qPrintable(w));
QQmlComponent component(&engine, testFileUrl("later.qml"));
@@ -1300,7 +1306,7 @@ void tst_qqmlqt::later()
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
QCoreApplication::processEvents();
} else if (propNames.at(i) == QLatin1String("collectGarbage")) {
- engine.collectGarbage();
+ gc(engine, GCFlags::DontSendPostedEvents);
} else {
QCOMPARE(root->property(qPrintable(propNames.at(i))), values.at(i));
}
diff --git a/tests/auto/qml/qqmlsettings/CMakeLists.txt b/tests/auto/qml/qqmlsettings/CMakeLists.txt
index fab4502c65..a444324120 100644
--- a/tests/auto/qml/qqmlsettings/CMakeLists.txt
+++ b/tests/auto/qml/qqmlsettings/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlsettings_labs Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlsettings_labs LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlsettings/data/aliases.qml b/tests/auto/qml/qqmlsettings/data/aliases.qml
index 25ca431a6d..21bc558799 100644
--- a/tests/auto/qml/qqmlsettings/data/aliases.qml
+++ b/tests/auto/qml/qqmlsettings/data/aliases.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.1
import QtQuick 2.2
import Qt.labs.settings 1.0
diff --git a/tests/auto/qml/qqmlsettings/data/basic.qml b/tests/auto/qml/qqmlsettings/data/basic.qml
index ce02d65a87..64520b33f2 100644
--- a/tests/auto/qml/qqmlsettings/data/basic.qml
+++ b/tests/auto/qml/qqmlsettings/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.labs.settings 1.0
Settings {
diff --git a/tests/auto/qml/qqmlsettings/data/categories.qml b/tests/auto/qml/qqmlsettings/data/categories.qml
index c333ec17eb..a6407f9cb2 100644
--- a/tests/auto/qml/qqmlsettings/data/categories.qml
+++ b/tests/auto/qml/qqmlsettings/data/categories.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.labs.settings 1.0
Settings {
diff --git a/tests/auto/qml/qqmlsettings/data/cpp-aliases.qml b/tests/auto/qml/qqmlsettings/data/cpp-aliases.qml
index 0ee95641c4..84f8923705 100644
--- a/tests/auto/qml/qqmlsettings/data/cpp-aliases.qml
+++ b/tests/auto/qml/qqmlsettings/data/cpp-aliases.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.1
import QtQuick 2.2
import Qt.labs.settings 1.0
diff --git a/tests/auto/qml/qqmlsettings/data/siblings.qml b/tests/auto/qml/qqmlsettings/data/siblings.qml
index 548a0869da..962cbc128b 100644
--- a/tests/auto/qml/qqmlsettings/data/siblings.qml
+++ b/tests/auto/qml/qqmlsettings/data/siblings.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import Qt.labs.settings 1.0
diff --git a/tests/auto/qml/qqmlsettings/data/types.qml b/tests/auto/qml/qqmlsettings/data/types.qml
index fc2209375d..eec4bb709b 100644
--- a/tests/auto/qml/qqmlsettings/data/types.qml
+++ b/tests/auto/qml/qqmlsettings/data/types.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.1
import QtQuick 2.2
import Qt.labs.settings 1.0
diff --git a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
index 88d8c58e2c..46ab2be3c9 100644
--- a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
+++ b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/QCoreApplication>
#include <QtCore/QScopedPointer>
diff --git a/tests/auto/qml/qqmlsqldatabase/CMakeLists.txt b/tests/auto/qml/qqmlsqldatabase/CMakeLists.txt
index 7537a92803..92809d0963 100644
--- a/tests/auto/qml/qqmlsqldatabase/CMakeLists.txt
+++ b/tests/auto/qml/qqmlsqldatabase/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlsqldatabase Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlsqldatabase LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml b/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml
index fbd770740d..74226808f2 100644
--- a/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml
+++ b/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.2
import QtQuick.LocalStorage 2.0
diff --git a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp
index 67c44684a4..0269756225 100644
--- a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp
+++ b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -164,9 +165,9 @@ void tst_qqmlsqldatabase::testQml_cleanopen()
engine->collectGarbage();
- foreach (QString dbname, QSqlDatabase::connectionNames()) {
+ const QStringList connectionNames = QSqlDatabase::connectionNames();
+ for (const QString &dbname : connectionNames)
QSqlDatabase::removeDatabase(dbname);
- }
}
void tst_qqmlsqldatabase::totalDatabases()
diff --git a/tests/auto/qml/qqmltablemodel/CMakeLists.txt b/tests/auto/qml/qqmltablemodel/CMakeLists.txt
index ebd3e94835..8edd052e3c 100644
--- a/tests/auto/qml/qqmltablemodel/CMakeLists.txt
+++ b/tests/auto/qml/qqmltablemodel/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmltablemodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmltablemodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
index a862af35d2..2b41837bac 100644
--- a/tests/auto/qml/qqmltablemodel/data/TestModel.qml
+++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/data/TestUtils.js b/tests/auto/qml/qqmltablemodel/data/TestUtils.js
index 83ac1b80a8..f6a5d4bb20 100644
--- a/tests/auto/qml/qqmltablemodel/data/TestUtils.js
+++ b/tests/auto/qml/qqmltablemodel/data/TestUtils.js
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
function testModelRoleDataProvider(index, role, cellData) {
switch (role) {
diff --git a/tests/auto/qml/qqmltablemodel/data/common.qml b/tests/auto/qml/qqmltablemodel/data/common.qml
index 7e9f46dd6f..1b3b8b5846 100644
--- a/tests/auto/qml/qqmltablemodel/data/common.qml
+++ b/tests/auto/qml/qqmltablemodel/data/common.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.13
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/data/complex.qml b/tests/auto/qml/qqmltablemodel/data/complex.qml
index 9075a6a26f..41c69d44f4 100644
--- a/tests/auto/qml/qqmltablemodel/data/complex.qml
+++ b/tests/auto/qml/qqmltablemodel/data/complex.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.13
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
index a1d9463665..dcdfd42a28 100644
--- a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
+++ b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/data/empty.qml b/tests/auto/qml/qqmltablemodel/data/empty.qml
index 77c0021ee6..164a38b9d1 100644
--- a/tests/auto/qml/qqmltablemodel/data/empty.qml
+++ b/tests/auto/qml/qqmltablemodel/data/empty.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/data/intAndDouble.qml b/tests/auto/qml/qqmltablemodel/data/intAndDouble.qml
index c08525bc95..d20650f3be 100644
--- a/tests/auto/qml/qqmltablemodel/data/intAndDouble.qml
+++ b/tests/auto/qml/qqmltablemodel/data/intAndDouble.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
index bb278ff9d1..60f502d4b1 100644
--- a/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
+++ b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
index 04c76098dd..847a89667f 100644
--- a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
+++ b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
index 884427d24b..f5ece4bcf3 100644
--- a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
+++ b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import Qt.labs.qmlmodels 1.0
diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
index cd6eb3e9c5..f2eec6e79c 100644
--- a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
+++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
diff --git a/tests/auto/qml/qqmltimer/CMakeLists.txt b/tests/auto/qml/qqmltimer/CMakeLists.txt
index 1853a545d7..f66e054dc6 100644
--- a/tests/auto/qml/qqmltimer/CMakeLists.txt
+++ b/tests/auto/qml/qqmltimer/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmltimer Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmltimer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qqmltimer
SOURCES
tst_qqmltimer.cpp
diff --git a/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp b/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp
index 736907d5f0..a6c61abd57 100644
--- a/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp
+++ b/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QSignalSpy>
#include <qtest.h>
#include <QtQml/qqmlengine.h>
@@ -69,7 +69,8 @@ void tst_qqmltimer::notRepeating()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
+ std::unique_ptr<QObject> o { component.create() };
+ QQmlTimer *timer = qobject_cast<QQmlTimer*>(o.get());
QVERIFY(timer != nullptr);
QVERIFY(timer->isRunning());
QVERIFY(!timer->isRepeating());
@@ -91,12 +92,12 @@ void tst_qqmltimer::notRepeatingStart()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100 }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
- QVERIFY(timer != nullptr);
+ std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) };
+ QVERIFY(timer.get());
QVERIFY(!timer->isRunning());
TimerHelper helper;
- connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
+ connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout()));
consistentWait(200);
QCOMPARE(helper.count, 0);
@@ -107,8 +108,6 @@ void tst_qqmltimer::notRepeatingStart()
consistentWait(200);
QCOMPARE(helper.count, 1);
QVERIFY(!timer->isRunning());
-
- delete timer;
}
void tst_qqmltimer::repeat()
@@ -116,11 +115,11 @@ void tst_qqmltimer::repeat()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; repeat: true; running: true }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
- QVERIFY(timer != nullptr);
+ std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) };
+ QVERIFY(timer);
TimerHelper helper;
- connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
+ connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout()));
QCOMPARE(helper.count, 0);
consistentWait(200);
@@ -138,7 +137,7 @@ void tst_qqmltimer::repeat()
QCOMPARE(helper.count, oldCount);
QVERIFY(!timer->isRunning());
- QSignalSpy spy(timer, SIGNAL(repeatChanged()));
+ QSignalSpy spy(timer.get(), SIGNAL(repeatChanged()));
timer->setRepeating(false);
QVERIFY(!timer->isRepeating());
@@ -149,8 +148,6 @@ void tst_qqmltimer::repeat()
timer->setRepeating(true);
QCOMPARE(spy.size(),2);
-
- delete timer;
}
void tst_qqmltimer::triggeredOnStart()
@@ -158,12 +155,12 @@ void tst_qqmltimer::triggeredOnStart()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true; triggeredOnStart: true }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
- QVERIFY(timer != nullptr);
+ std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) };
+ QVERIFY(timer);
QVERIFY(timer->triggeredOnStart());
TimerHelper helper;
- connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
+ connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout()));
consistentWait(1);
QCOMPARE(helper.count, 1);
consistentWait(200);
@@ -172,7 +169,7 @@ void tst_qqmltimer::triggeredOnStart()
QCOMPARE(helper.count, 2);
QVERIFY(!timer->isRunning());
- QSignalSpy spy(timer, SIGNAL(triggeredOnStartChanged()));
+ QSignalSpy spy(timer.get(), SIGNAL(triggeredOnStartChanged()));
timer->setTriggeredOnStart(false);
QVERIFY(!timer->triggeredOnStart());
@@ -183,8 +180,6 @@ void tst_qqmltimer::triggeredOnStart()
timer->setTriggeredOnStart(true);
QCOMPARE(spy.size(),2);
-
- delete timer;
}
void tst_qqmltimer::triggeredOnStartRepeat()
@@ -192,11 +187,11 @@ void tst_qqmltimer::triggeredOnStartRepeat()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true; triggeredOnStart: true; repeat: true }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
- QVERIFY(timer != nullptr);
+ std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) };
+ QVERIFY(timer);
TimerHelper helper;
- connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
+ connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout()));
consistentWait(1);
QCOMPARE(helper.count, 1);
@@ -206,8 +201,6 @@ void tst_qqmltimer::triggeredOnStartRepeat()
consistentWait(200);
QVERIFY(helper.count > oldCount);
QVERIFY(timer->isRunning());
-
- delete timer;
}
void tst_qqmltimer::noTriggerIfNotRunning()
@@ -221,12 +214,10 @@ void tst_qqmltimer::noTriggerIfNotRunning()
"property Timer timer2: Timer { interval: 10; running: true; onTriggered: t1.running=false }"
"}"
), QUrl::fromLocalFile(""));
- QObject *item = component.create();
- QVERIFY(item != nullptr);
+ std::unique_ptr<QObject> item { component.create() };
+ QVERIFY(item);
consistentWait(200);
QCOMPARE(item->property("ok").toBool(), true);
-
- delete item;
}
void tst_qqmltimer::changeDuration()
@@ -234,11 +225,11 @@ void tst_qqmltimer::changeDuration()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; repeat: true; running: true }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
- QVERIFY(timer != nullptr);
+ std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) };
+ QVERIFY(timer);
TimerHelper helper;
- connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
+ connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout()));
QCOMPARE(helper.count, 0);
consistentWait(500);
@@ -250,7 +241,7 @@ void tst_qqmltimer::changeDuration()
QCOMPARE(helper.count, 3);
QVERIFY(timer->isRunning());
- QSignalSpy spy(timer, SIGNAL(intervalChanged()));
+ QSignalSpy spy(timer.get(), SIGNAL(intervalChanged()));
timer->setInterval(200);
QCOMPARE(timer->interval(), 200);
@@ -261,8 +252,6 @@ void tst_qqmltimer::changeDuration()
timer->setInterval(300);
QCOMPARE(spy.size(),2);
-
- delete timer;
}
void tst_qqmltimer::restart()
@@ -270,11 +259,11 @@ void tst_qqmltimer::restart()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 1000; repeat: true; running: true }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
- QVERIFY(timer != nullptr);
+ std::unique_ptr<QQmlTimer> timer { qobject_cast<QQmlTimer*>(component.create()) };
+ QVERIFY(timer);
TimerHelper helper;
- connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
+ connect(timer.get(), SIGNAL(triggered()), &helper, SLOT(timeout()));
QCOMPARE(helper.count, 0);
consistentWait(1200);
@@ -290,8 +279,6 @@ void tst_qqmltimer::restart()
QCOMPARE(helper.count, 2);
QVERIFY(timer->isRunning());
-
- delete timer;
}
void tst_qqmltimer::restartFromTriggered()
@@ -356,14 +343,12 @@ void tst_qqmltimer::parentProperty()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQuick 2.0\nItem { Timer { objectName: \"timer\"; running: parent.visible } }"), QUrl::fromLocalFile(""));
- QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
- QVERIFY(item != nullptr);
+ std::unique_ptr<QQuickItem> item { qobject_cast<QQuickItem*>(component.create()) };
+ QVERIFY(item);
QQmlTimer *timer = item->findChild<QQmlTimer*>("timer");
QVERIFY(timer != nullptr);
QVERIFY(timer->isRunning());
-
- delete timer;
}
void tst_qqmltimer::stopWhenEventPosted()
@@ -371,7 +356,8 @@ void tst_qqmltimer::stopWhenEventPosted()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; running: true }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
+ std::unique_ptr<QObject> o { component.create() };
+ QQmlTimer *timer = qobject_cast<QQmlTimer*>(o.get());
TimerHelper helper;
connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
@@ -395,7 +381,8 @@ void tst_qqmltimer::restartWhenEventPosted()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; running: true }"), QUrl::fromLocalFile(""));
- QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
+ std::unique_ptr<QObject> o { component.create() };
+ QQmlTimer *timer = qobject_cast<QQmlTimer*>(o.get());
TimerHelper helper;
connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
diff --git a/tests/auto/qml/qqmltranslation/CMakeLists.txt b/tests/auto/qml/qqmltranslation/CMakeLists.txt
index 86198e7867..12d98b95a9 100644
--- a/tests/auto/qml/qqmltranslation/CMakeLists.txt
+++ b/tests/auto/qml/qqmltranslation/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmltranslation Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmltranslation LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmltranslation/data/jstranslation.qml b/tests/auto/qml/qqmltranslation/data/jstranslation.qml
index 7adde019fa..cca8bfa9e8 100644
--- a/tests/auto/qml/qqmltranslation/data/jstranslation.qml
+++ b/tests/auto/qml/qqmltranslation/data/jstranslation.qml
@@ -17,4 +17,5 @@ QtObject {
property string singular2: Js.singular2()
property string plural: Js.plural()
property string plural2: Js.plural2()
+ property string emptyContext: Js.emptyContext()
}
diff --git a/tests/auto/qml/qqmltranslation/data/pragmacontext.qml b/tests/auto/qml/qqmltranslation/data/pragmacontext.qml
new file mode 100644
index 0000000000..e158cfe4bf
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/pragmacontext.qml
@@ -0,0 +1,8 @@
+import QtQml 2.12
+
+pragma Translator: contextSetWithPragma
+
+QtObject {
+ property string german1: qsTr("English in translation")
+ property string german2: qsTranslate("setContext","English in translation")
+}
diff --git a/tests/auto/qml/qqmltranslation/data/pragmacontextstringliteral.qml b/tests/auto/qml/qqmltranslation/data/pragmacontextstringliteral.qml
new file mode 100644
index 0000000000..31a3d0e009
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/pragmacontextstringliteral.qml
@@ -0,0 +1,8 @@
+import QtQml 2.12
+
+pragma Translator: "contextSetWithPragmaStringLiteral"
+
+QtObject {
+ property string german1: qsTr("English in translation")
+ property string german2: qsTranslate("setContext","English in translation")
+}
diff --git a/tests/auto/qml/qqmltranslation/data/translation.js b/tests/auto/qml/qqmltranslation/data/translation.js
index 11117fd36e..5269926604 100644
--- a/tests/auto/qml/qqmltranslation/data/translation.js
+++ b/tests/auto/qml/qqmltranslation/data/translation.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
function basic() {
return qsTr("hello")
@@ -58,3 +58,7 @@ function plural2() {
return qsTr("%n duck(s)", "", 2)
return "";
}
+
+function emptyContext() {
+ return qsTranslate("", "hello")
+}
diff --git a/tests/auto/qml/qqmltranslation/data/translation.qml b/tests/auto/qml/qqmltranslation/data/translation.qml
index b96471c9dd..127b5cb5b4 100644
--- a/tests/auto/qml/qqmltranslation/data/translation.qml
+++ b/tests/auto/qml/qqmltranslation/data/translation.qml
@@ -1,8 +1,8 @@
import QtQuick 2.0
-
+// The translation data starts at line == 4. QCoreApplication::translate() tolerates this.
QtObject {
objectName: "test"
-
+ property string emptyContext: qsTranslate("", "hello")
property string basic: qsTr("hello")
property string basic2: qsTranslate("CustomContext", "goodbye")
property string basic3: if (objectName.length > 0) qsTr("hello")
diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
index f78bd3ac76..69dbd6179b 100644
--- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
+++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
@@ -24,6 +24,8 @@ private slots:
void idTranslation();
void translationChange();
void preferJSContext();
+ void pragmaContext();
+ void pragmaContextStringLiteral();
void listModel();
};
@@ -50,11 +52,11 @@ void tst_qqmltranslation::translation()
QQmlEngine engine;
QQmlComponent component(&engine, testFile);
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
if (verifyCompiledData) {
- QQmlContext *context = qmlContext(object);
+ QQmlContext *context = qmlContext(object.get());
QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine());
QQmlRefPointer<QQmlTypeData> typeData = engine->typeLoader.getType(context->baseUrl());
QVERIFY(!typeData->backupSourceCode().isValid());
@@ -66,7 +68,9 @@ void tst_qqmltranslation::translation()
<< QStringLiteral("basic2")
<< QStringLiteral("disambiguation")
<< QStringLiteral("disambiguation2")
- << QStringLiteral("singular") << QStringLiteral("plural");
+ << QStringLiteral("singular")
+ << QStringLiteral("plural")
+ << QStringLiteral("emptyContext");
const QV4::CompiledData::Object *rootObject
= compilationUnit->qmlData->objectAt(/*root object*/0);
@@ -100,9 +104,9 @@ void tst_qqmltranslation::translation()
QCOMPARE(object->property("singular2").toString(), QLatin1String("1 canard"));
QCOMPARE(object->property("plural").toString(), QLatin1String("2 canards"));
QCOMPARE(object->property("plural2").toString(), QLatin1String("2 canards"));
+ QCOMPARE(object->property("emptyContext").toString(), QLatin1String("hello"));
QCoreApplication::removeTranslator(&translator);
- delete object;
}
void tst_qqmltranslation::idTranslation()
@@ -113,11 +117,11 @@ void tst_qqmltranslation::idTranslation()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("idtranslation.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
{
- QQmlContext *context = qmlContext(object);
+ QQmlContext *context = qmlContext(object.get());
QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine());
QQmlRefPointer<QQmlTypeData> typeData = engine->typeLoader.getType(context->baseUrl());
QVERIFY(!typeData->backupSourceCode().isValid());
@@ -144,7 +148,6 @@ void tst_qqmltranslation::idTranslation()
QCOMPARE(object->property("idTranslation3").toString(), QLatin1String("bonjour tout le monde"));
QCoreApplication::removeTranslator(&translator);
- delete object;
}
class CppTranslationBase : public QQuickItem
@@ -174,6 +177,12 @@ class DummyTranslator : public QTranslator
return QString::fromUtf8("Deutsch in mylibrary");
if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "nested_js_translation"))
return QString::fromUtf8("Deutsch in Setzung");
+ if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "contextSetWithPragma"))
+ return QString::fromUtf8("Deutsch in Setzung pragma");
+ if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "contextSetWithPragmaStringLiteral"))
+ return QString::fromUtf8("Deutsch in Setzung pragma string literal");
+ if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "setContext"))
+ return QString::fromUtf8("Deutsch in Setzung set");
if (!qstrcmp(sourceText, "soup"))
return QString::fromUtf8("Suppe");
if (!qstrcmp(sourceText, "fish"))
@@ -245,6 +254,42 @@ void tst_qqmltranslation::preferJSContext()
QCoreApplication::removeTranslator(&translator);
}
+void tst_qqmltranslation::pragmaContext()
+{
+ DummyTranslator translator;
+ QCoreApplication::installTranslator(&translator);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("pragmacontext.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("german1").toString(),
+ QStringLiteral("Deutsch in Setzung pragma"));
+ QCOMPARE(object->property("german2").toString(),
+ QStringLiteral("Deutsch in Setzung set"));
+
+ QCoreApplication::removeTranslator(&translator);
+}
+
+void tst_qqmltranslation::pragmaContextStringLiteral()
+{
+ DummyTranslator translator;
+ QCoreApplication::installTranslator(&translator);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("pragmacontextstringliteral.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("german1").toString(),
+ QStringLiteral("Deutsch in Setzung pragma string literal"));
+ QCOMPARE(object->property("german2").toString(),
+ QStringLiteral("Deutsch in Setzung set"));
+
+ QCoreApplication::removeTranslator(&translator);
+}
+
void tst_qqmltranslation::listModel()
{
QQmlEngine engine;
diff --git a/tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt b/tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt
index 7c55146ddc..f260ca8287 100644
--- a/tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt
+++ b/tests/auto/qml/qqmltreemodeltotablemodel/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qqmltreemodeltotablemodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmltreemodeltotablemodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp b/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp
index 58587da79c..7cf077f265 100644
--- a/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp
+++ b/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testmodel.h"
@@ -129,3 +129,31 @@ bool TestModel::insertRows(int position, int rows, const QModelIndex &parent)
endInsertRows();
return true;
}
+
+void insertColumnsRecursive(TreeItem *item, int cols)
+{
+ int pos = item->m_entries.size();
+ for (int col = 0; col < cols; col++)
+ item->m_entries << QVariant(QString("%1, %2 (inserted)").arg(pos + col).arg(col));
+ for (auto child : item->m_childItems)
+ insertColumnsRecursive(child, cols);
+}
+
+bool TestModel::insertColumns(int position, int cols, const QModelIndex &parent)
+{
+ if (!parent.isValid()) {
+ qWarning() << "Cannot insert columns on an invalid parent!";
+ return false;
+ }
+
+ beginInsertColumns(parent, position, position + cols - 1);
+ TreeItem *parentItem = treeItem(parent);
+
+ TreeItem *item = m_rootItem.data();
+
+ insertColumnsRecursive(item, cols);
+ m_columnCount += cols;
+
+ endInsertColumns();
+ return true;
+}
diff --git a/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h b/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h
index f886c56d4a..c67410d5f1 100644
--- a/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h
+++ b/tests/auto/qml/qqmltreemodeltotablemodel/testmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTMODEL_H
#define TESTMODEL_H
@@ -38,6 +38,7 @@ public:
QModelIndex parent(const QModelIndex &index) const override;
bool insertRows(int position, int rows, const QModelIndex &parent) override;
+ bool insertColumns(int position, int rows, const QModelIndex &parent) override;
private:
QScopedPointer<TreeItem> m_rootItem;
diff --git a/tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp b/tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp
index cd9aed4b30..47bbf1440b 100644
--- a/tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp
+++ b/tests/auto/qml/qqmltreemodeltotablemodel/tst_qqmltreemodeltotablemodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QAbstractItemModelTester>
diff --git a/tests/auto/qml/qqmltypeloader/CMakeLists.txt b/tests/auto/qml/qqmltypeloader/CMakeLists.txt
index d20449579e..1bf4da34f9 100644
--- a/tests/auto/qml/qqmltypeloader/CMakeLists.txt
+++ b/tests/auto/qml/qqmltypeloader/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmltypeloader Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmltypeloader LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp b/tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp
index e0ff775cb5..b1726f190f 100644
--- a/tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp
+++ b/tests/auto/qml/qqmltypeloader/SlowImport/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "plugin.h"
diff --git a/tests/auto/qml/qqmltypeloader/SlowImport/plugin.h b/tests/auto/qml/qqmltypeloader/SlowImport/plugin.h
index dfaafe278f..2a5a1b2a4e 100644
--- a/tests/auto/qml/qqmltypeloader/SlowImport/plugin.h
+++ b/tests/auto/qml/qqmltypeloader/SlowImport/plugin.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef SLOW_PLUGIN_H
#define SLOW_PLUGIN_H
diff --git a/tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp b/tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp
index 910f3eaeb6..c17da49e93 100644
--- a/tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp
+++ b/tests/auto/qml/qqmltypeloader/SlowImport/slow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "slow.h"
diff --git a/tests/auto/qml/qqmltypeloader/SlowImport/slow.h b/tests/auto/qml/qqmltypeloader/SlowImport/slow.h
index e9f6341e5a..c3ec73bc53 100644
--- a/tests/auto/qml/qqmltypeloader/SlowImport/slow.h
+++ b/tests/auto/qml/qqmltypeloader/SlowImport/slow.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef SLOWSTUFF_H
#define SLOWSTUFF_H
diff --git a/tests/auto/qml/qqmltypeloader/data/GenericView.qml b/tests/auto/qml/qqmltypeloader/data/GenericView.qml
index 2a708d425d..0f90ef26da 100644
--- a/tests/auto/qml/qqmltypeloader/data/GenericView.qml
+++ b/tests/auto/qml/qqmltypeloader/data/GenericView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Slow 1.0
diff --git a/tests/auto/qml/qqmltypeloader/data/Intercept.qml b/tests/auto/qml/qqmltypeloader/data/Intercept.qml
index 57290832ea..d0de7e04ec 100644
--- a/tests/auto/qml/qqmltypeloader/data/Intercept.qml
+++ b/tests/auto/qml/qqmltypeloader/data/Intercept.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Fast 1.0
diff --git a/tests/auto/qml/qqmltypeloader/data/NiceView.qml b/tests/auto/qml/qqmltypeloader/data/NiceView.qml
index cb73650551..4f1782000a 100644
--- a/tests/auto/qml/qqmltypeloader/data/NiceView.qml
+++ b/tests/auto/qml/qqmltypeloader/data/NiceView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmltypeloader/data/doesExist.qml b/tests/auto/qml/qqmltypeloader/data/doesExist.qml
new file mode 100644
index 0000000000..54531c4bdc
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/doesExist.qml
@@ -0,0 +1,2 @@
+import QtQml
+QtObject {}
diff --git a/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/a.qml b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/a.qml
new file mode 100644
index 0000000000..8fc36a40da
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/a.qml
@@ -0,0 +1,3 @@
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml b/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml
index 10e1784ff1..c22a880fbd 100644
--- a/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml
+++ b/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.2
diff --git a/tests/auto/qml/qqmltypeloader/data/test_intercept.qml b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml
index cd6a726f9b..930d481f7b 100644
--- a/tests/auto/qml/qqmltypeloader/data/test_intercept.qml
+++ b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmltypeloader/data/test_load_complete.qml b/tests/auto/qml/qqmltypeloader/data/test_load_complete.qml
index 6cba31c434..95439b8308 100644
--- a/tests/auto/qml/qqmltypeloader/data/test_load_complete.qml
+++ b/tests/auto/qml/qqmltypeloader/data/test_load_complete.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmltypeloader/declarativetesttype.h b/tests/auto/qml/qqmltypeloader/declarativetesttype.h
index 5a33f25be5..b767342951 100644
--- a/tests/auto/qml/qqmltypeloader/declarativetesttype.h
+++ b/tests/auto/qml/qqmltypeloader/declarativetesttype.h
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DECLARATIVETESTTYPE_H
#define DECLARATIVETESTTYPE_H
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index 89e208f441..7828a869e1 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
@@ -38,6 +38,7 @@ private slots:
void redirect();
void qmlSingletonWithinModule();
void multiSingletonModule();
+ void multiSingletonModuleNoWarning();
void implicitComponentModule();
void customDiskCachePath();
void qrcRootPathUrl();
@@ -47,6 +48,7 @@ private slots:
void circularDependency();
void declarativeCppAndQmlDir();
void signalHandlersAreCompatible();
+ void loadTypeOnShutdown();
private:
void checkSingleton(const QString & dataDirectory);
@@ -62,19 +64,18 @@ void tst_QQMLTypeLoader::testLoadComplete()
#ifdef Q_OS_ANDROID
QSKIP("Loading dynamic plugins does not work on Android");
#endif
- QQuickView *window = new QQuickView();
+ std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>();
window->engine()->addImportPath(QT_TESTCASE_BUILDDIR);
qDebug() << window->engine()->importPathList();
window->setGeometry(0,0,240,320);
window->setSource(testFileUrl("test_load_complete.qml"));
window->show();
- QVERIFY(QTest::qWaitForWindowExposed(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window.get()));
QObject *rootObject = window->rootObject();
QTRY_VERIFY(rootObject != nullptr);
QTRY_COMPARE(rootObject->property("created").toInt(), 2);
QTRY_COMPARE(rootObject->property("loaded").toInt(), 2);
- delete window;
}
void tst_QQMLTypeLoader::loadComponentSynchronously()
@@ -92,7 +93,7 @@ void tst_QQMLTypeLoader::trimCache()
QQmlEngine engine;
QQmlTypeLoader &loader = QQmlEnginePrivate::get(&engine)->typeLoader;
QVector<QQmlTypeData *> releaseLater;
- QVector<QV4::ExecutableCompilationUnit *> releaseCompilationUnitLater;
+ QVector<QV4::CompiledData::CompilationUnit *> releaseCompilationUnitLater;
for (int i = 0; i < 256; ++i) {
QUrl url = testFileUrl("trim_cache.qml");
url.setQuery(QString::number(i));
@@ -534,6 +535,18 @@ void tst_QQMLTypeLoader::multiSingletonModule()
checkCleanCacheLoad(QLatin1String("multiSingletonModule"));
}
+void tst_QQMLTypeLoader::multiSingletonModuleNoWarning()
+{
+ // Should not warn about a "cyclic" dependency between the singletons
+ QTest::failOnWarning(QRegularExpression(".*"));
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("imports/multisingletonmodule/a.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+}
+
void tst_QQMLTypeLoader::implicitComponentModule()
{
#ifdef Q_OS_ANDROID
@@ -673,7 +686,7 @@ static void getCompilationUnitAndRuntimeInfo(QQmlRefPointer<QV4::ExecutableCompi
QVERIFY(!typeData->isError()); // this returns
}
- unit = typeData->compilationUnit();
+ unit = engine->handle()->executableCompilationUnit(typeData->compilationUnit());
QVERIFY(unit);
// the QmlIR::Document is deleted once loader.getType() is complete, so
@@ -724,6 +737,49 @@ void tst_QQMLTypeLoader::signalHandlersAreCompatible()
QVERIFY(unitFromCachegen->url() != unitFromTypeCompiler->url());
}
+void tst_QQMLTypeLoader::loadTypeOnShutdown()
+{
+ bool dead1 = false;
+ bool dead2 = false;
+
+ {
+ QQmlEngine engine;
+ auto good = new QQmlComponent(
+ &engine, testFileUrl("doesExist.qml"),
+ QQmlComponent::CompilationMode::Asynchronous, &engine);
+ QObject::connect(
+ good, &QQmlComponent::statusChanged, &engine,
+ [&](QQmlComponent::Status) {
+
+ // Must not call this if the engine is already dead.
+ QVERIFY(engine.rootContext());
+
+ });
+
+ QObject::connect(good, &QQmlComponent::destroyed, good, [&]() { dead1 = true; });
+ QVERIFY(good->isLoading());
+
+ auto bad = new QQmlComponent(
+ &engine, testFileUrl("doesNotExist.qml"),
+ QQmlComponent::CompilationMode::Asynchronous, &engine);
+ QObject::connect(
+ bad, &QQmlComponent::statusChanged, &engine,
+ [&](QQmlComponent::Status) {
+
+ // Must not call this if the engine is already dead.
+ // Must also not leak memory from the events the error produces.
+ QVERIFY(engine.rootContext());
+
+ });
+
+ QObject::connect(bad, &QQmlComponent::destroyed, bad, [&]() { dead2 = true; });
+ QVERIFY(bad->isLoading());
+ }
+
+ QVERIFY(dead1);
+ QVERIFY(dead2);
+}
+
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"
diff --git a/tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt b/tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt
index 156022bac1..93460d72a9 100644
--- a/tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt
+++ b/tests/auto/qml/qqmlvaluetypeproviders/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlvaluetypeproviders Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlvaluetypeproviders LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml b/tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml
index 95a588d1bd..6081d460b8 100644
--- a/tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml
+++ b/tests/auto/qml/qqmlvaluetypeproviders/data/changedSignal.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml b/tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml
index d6707e37cd..e2c39d2d72 100644
--- a/tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml
+++ b/tests/auto/qml/qqmlvaluetypeproviders/data/recursiveWriteBack.qml
@@ -3,11 +3,16 @@ import Test
MyTypeObject {
property list<structured> l: [{i : 21}, {c: 22}, {p: {x: 199, y: 222}}]
+ property int aa: 5
Component.onCompleted: {
l[2].i = 4
l[1].p.x = 88
l[0].sizes[1].width = 19
structured.p.x = 76
+
+ var sizesDetached = l[0].sizesDetached();
+ sizesDetached[1].width = 12;
+ aa = sizesDetached[1].width;
}
}
diff --git a/tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml b/tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml
index 6a42a4f1a8..abd6f6af08 100644
--- a/tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml
+++ b/tests/auto/qml/qqmlvaluetypeproviders/data/structuredValueTypes.qml
@@ -40,7 +40,22 @@ MyTypeObject {
r2 = {x: 9, y: 10, width: 11, height: 12};
c3 = 99;
b2 = {i: 11, c: 15, p: {x: 4} }
-
+ acceptConstructible(object)
c4 = {foo: 11};
}
+
+ barren: ({i: 17})
+ function changeBarren() { barren = "foos" }
+
+ property QtObject object: QtObject {}
+ property constructible fromObject: object
+ property int listResult: acceptConstructibles([Qt.resolvedUrl("foo/bar.baz"), fromObject, 12])
+
+ property var insanity: ({})
+ property structured fromInsanity: acceptStructured(insanity)
+
+ property rect newItemPadding
+ function updatePadding() {
+ setEffectPadding(newItemPadding);
+ }
}
diff --git a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp
index 1746957872..fd85c117e1 100644
--- a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp
@@ -1,11 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
+QList<Padding::LogEntry> Padding::log;
+
void registerTypes()
{
qmlRegisterType<MyTypeObject>("Test", 1, 0, "MyTypeObject");
qmlRegisterTypesAndRevisions<ConstructibleValueType>("Test", 1);
qmlRegisterTypesAndRevisions<ConstructibleFromQReal>("Test", 1);
qmlRegisterTypesAndRevisions<StructuredValueType>("Test", 1);
+ qmlRegisterTypesAndRevisions<ForeignAnonymousStructuredValueType>("Test", 1);
+ qmlRegisterTypesAndRevisions<Padding>("Test", 1);
+ qmlRegisterTypesAndRevisions<MyItem>("Test", 1);
}
diff --git a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h
index ca1d1b2b7a..8130ea2912 100644
--- a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h
+++ b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
@@ -33,6 +33,8 @@ struct ConstructibleValueType
public:
ConstructibleValueType() = default;
Q_INVOKABLE ConstructibleValueType(int foo) : m_foo(foo) {}
+ Q_INVOKABLE ConstructibleValueType(QObject *) : m_foo(67) {}
+ Q_INVOKABLE ConstructibleValueType(const QUrl &) : m_foo(68) {}
int foo() const { return m_foo; }
@@ -96,6 +98,8 @@ public:
const QList<QSizeF> &sizes() const { return m_sizes; }
void setSizes(const QList<QSizeF> &sizes) { m_sizes = sizes; }
+ Q_INVOKABLE QList<QSizeF> sizesDetached() const { return m_sizes; }
+
private:
friend bool operator==(const StructuredValueType &a, const StructuredValueType &b)
@@ -109,6 +113,35 @@ private:
QList<QSizeF> m_sizes = { QSizeF(1, 1), QSizeF(2, 2) };
};
+struct BarrenValueType
+{
+ Q_GADGET
+ Q_PROPERTY(int i READ i WRITE setI)
+
+public:
+ BarrenValueType() = default;
+ Q_INVOKABLE BarrenValueType(const QString &) : m_i(25) {}
+
+ int i() const { return m_i; }
+ void setI(int newI) { m_i = newI; }
+
+private:
+ friend bool operator==(const BarrenValueType &a, const BarrenValueType &b)
+ {
+ return a.m_i == b.m_i;
+ }
+
+ int m_i = 0;
+};
+
+struct ForeignAnonymousStructuredValueType
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(BarrenValueType)
+ QML_STRUCTURED_VALUE
+};
+
class MyTypeObject : public QObject
{
Q_OBJECT
@@ -133,6 +166,7 @@ class MyTypeObject : public QObject
Q_PROPERTY(QVariant variant READ variant NOTIFY changed)
Q_PROPERTY(ConstructibleValueType constructible READ constructible WRITE setConstructible NOTIFY constructibleChanged)
Q_PROPERTY(StructuredValueType structured READ structured WRITE setStructured NOTIFY structuredChanged)
+ Q_PROPERTY(BarrenValueType barren READ barren WRITE setBarren NOTIFY barrenChanged)
Q_PROPERTY(QDateTime aDateTime READ aDateTime WRITE setADateTime NOTIFY aDateTimeChanged)
Q_PROPERTY(QDate aDate READ aDate WRITE setADate NOTIFY aDateChanged)
@@ -275,7 +309,7 @@ public:
{
return m_aDate;
}
- void setADate(const QDate &newADate)
+ void setADate(QDate newADate)
{
if (m_aDate == newADate)
return;
@@ -287,7 +321,7 @@ public:
{
return m_aTime;
}
- void setATime(const QTime &newATime)
+ void setATime(QTime newATime)
{
if (m_aTime == newATime)
return;
@@ -307,6 +341,47 @@ public:
emit aVariantChanged();
}
+ BarrenValueType barren() const
+ {
+ return m_barren;
+ }
+
+ void setBarren(const BarrenValueType &newBarren)
+ {
+ if (m_barren == newBarren)
+ return;
+ m_barren = newBarren;
+ emit barrenChanged();
+ }
+
+ Q_INVOKABLE void acceptConstructible(const ConstructibleValueType &a)
+ {
+ setAVariant(QVariant::fromValue(a));
+ }
+
+ Q_INVOKABLE int acceptConstructibles(const QList<ConstructibleValueType> &constructibles)
+ {
+ int result = 0;
+ for (const auto &c: constructibles) {
+ result += c.foo();
+ }
+ return result;
+ }
+
+ Q_INVOKABLE StructuredValueType acceptStructured(const StructuredValueType &a)
+ {
+ return a;
+ }
+
+ Q_INVOKABLE void setEffectPadding(const QRect &r)
+ {
+ m_hasEffectPadding = true;
+ m_effectPadding = r;
+ }
+
+ bool hasEffectPadding() const { return m_hasEffectPadding; }
+ QRectF effectPadding() const { return m_effectPadding; }
+
signals:
void changed();
void runScript();
@@ -319,6 +394,8 @@ signals:
void aTimeChanged();
void aVariantChanged();
+ void barrenChanged();
+
public slots:
QSize method() { return QSize(13, 14); }
private:
@@ -329,6 +406,139 @@ private:
QDate m_aDate;
QTime m_aTime;
QVariant m_aVariant;
+ BarrenValueType m_barren;
+ QRectF m_effectPadding;
+ bool m_hasEffectPadding = false;
+};
+
+class Padding
+{
+ Q_GADGET
+
+ Q_PROPERTY(int left READ left WRITE setLeft)
+ Q_PROPERTY(int right READ right WRITE setRight)
+
+ QML_VALUE_TYPE(padding)
+ QML_STRUCTURED_VALUE
+
+public:
+ enum LogType {
+ DefaultCtor,
+ CopyCtor,
+ MoveCtor,
+ CopyAssign,
+ MoveAssign,
+ InvokableCtor,
+ CustomCtor,
+ Invalid,
+ };
+
+ Q_ENUM(LogType);
+
+ struct LogEntry {
+ LogType type = Invalid;
+ int left = 0;
+ int right = 0;
+
+ friend QDebug operator<<(QDebug &debug, const LogEntry &self)
+ {
+ return debug << self.type << " " << self.left << " " << self.right;
+ }
+ };
+
+ static QList<LogEntry> log;
+
+ void doLog(LogType type) {
+ log.append({
+ type, m_left, m_right
+ });
+ }
+
+ Padding()
+ {
+ doLog(DefaultCtor);
+ }
+
+ Padding(const Padding &other)
+ : m_left(other.m_left)
+ , m_right(other.m_right)
+ {
+ doLog(CopyCtor);
+ }
+
+ Padding(Padding &&other)
+ : m_left(other.m_left)
+ , m_right(other.m_right)
+ {
+ doLog(MoveCtor);
+ }
+
+ Padding(int left, int right)
+ : m_left( left )
+ , m_right( right )
+ {
+ doLog(CustomCtor);
+ }
+
+ Padding &operator=(const Padding &other) {
+ if (this != &other) {
+ m_left = other.m_left;
+ m_right = other.m_right;
+ }
+ doLog(CopyAssign);
+ return *this;
+ }
+
+ Padding &operator=(Padding &&other) {
+ if (this != &other) {
+ m_left = other.m_left;
+ m_right = other.m_right;
+ }
+ doLog(MoveAssign);
+ return *this;
+ }
+
+ Q_INVOKABLE Padding(int padding )
+ : m_left( padding )
+ , m_right( padding )
+ {
+ doLog(InvokableCtor);
+ }
+
+ void setLeft(int padding) { m_left = padding; }
+ int left() const { return m_left; }
+
+ void setRight(int padding) { m_right = padding; }
+ int right() const { return m_right; }
+
+private:
+ int m_left = 0;
+ int m_right = 0;
+};
+
+class MyItem : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Padding padding READ padding WRITE setPadding NOTIFY paddingChanged)
+ QML_ELEMENT
+
+public:
+ void setPadding(const Padding &padding)
+ {
+ if (padding.left() == m_padding.left() && padding.right() == m_padding.right())
+ return;
+
+ m_padding = padding;
+ emit paddingChanged();
+ }
+
+ const Padding &padding() const{ return m_padding; }
+
+signals:
+ void paddingChanged();
+
+private:
+ Padding m_padding{ 17, 17 };
};
void registerTypes();
diff --git a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp
index 5a36c6edd3..da7e14411d 100644
--- a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp
+++ b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
@@ -41,6 +41,7 @@ private slots:
void structured();
void recursive();
void date();
+ void constructors();
};
void tst_qqmlvaluetypeproviders::initTestCase()
@@ -253,7 +254,6 @@ void tst_qqmlvaluetypeproviders::structured()
" with value [object Object]",
"Could not find any constructor for value type ConstructibleValueType to call"
" with value QVariant(QJSValue, )",
- "Could not convert QLocale(English, Latin, Australia) to double for property y",
"Could not find any constructor for value type ConstructibleValueType to call"
" with value [object Object]",
"Could not find any constructor for value type ConstructibleValueType to call"
@@ -292,8 +292,16 @@ void tst_qqmlvaluetypeproviders::structured()
QCOMPARE(o->property("c3").value<ConstructibleValueType>(), ConstructibleValueType(99));
QCOMPARE(o->property("c4").value<ConstructibleValueType>(), ConstructibleValueType(0));
- QCOMPARE(o->property("ps").value<QList<QPointF>>(),
- QList<QPointF>({QPointF(1, 2), QPointF(3, 4), QPointF(55, 0)}));
+ const QList<QPointF> actual = o->property("ps").value<QList<QPointF>>();
+ const QList<QPointF> expected = {
+ QPointF(1, 2), QPointF(3, 4), QPointF(55, std::numeric_limits<double>::quiet_NaN())
+ };
+ QCOMPARE(actual.size(), expected.size());
+ QCOMPARE(actual[0], expected[0]);
+ QCOMPARE(actual[1], expected[1]);
+ QCOMPARE(actual[2].x(), expected[2].x());
+ QVERIFY(std::isnan(actual[2].y()));
+
QCOMPARE(o->property("ss").value<QList<QSizeF>>(),
QList<QSizeF>({QSizeF(5, 6), QSizeF(7, 8), QSizeF(-1, 99)}));
QCOMPARE(o->property("cs").value<QList<ConstructibleValueType>>(),
@@ -343,6 +351,112 @@ void tst_qqmlvaluetypeproviders::structured()
ConstructibleFromQReal(-112.5));
QCOMPARE(o->property("cr7").value<ConstructibleFromQReal>(),
ConstructibleFromQReal(50));
+
+ BarrenValueType barren;
+ barren.setI(17);
+ QCOMPARE(o->property("barren").value<BarrenValueType>(), barren);
+
+ QMetaObject::invokeMethod(o.data(), "changeBarren");
+ QCOMPARE(o->property("barren").value<BarrenValueType>(), BarrenValueType(QString()));
+
+ QCOMPARE(o->property("fromObject").value<ConstructibleValueType>(),
+ ConstructibleValueType(nullptr));
+ QCOMPARE(o->property("aVariant").value<ConstructibleValueType>(),
+ ConstructibleValueType(nullptr));
+
+ QCOMPARE(o->property("listResult").toInt(), 12 + 67 + 68);
+
+
+ // You can store all kinds of insanity in a VariantObject, but we generally don't.
+ // Since we cannot rule out the possibility of there being such VariantObjects, we need to test
+ // their conversions.
+
+
+ QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), StructuredValueType());
+
+ QV4::Scope scope(e.handle());
+ QV4::ScopedString name(scope, scope.engine->newString("insanity"));
+
+ QObject *po = o.data();
+ QV4::ScopedObject js(
+ scope, scope.engine->metaTypeToJS(QMetaType::fromType<MyTypeObject *>(), &po));
+
+ const QVariantHash hash {
+ {"i", 12},
+ {"c", QUrl("http://example.com")},
+ {"p", QVariantMap {
+ {"x", 17},
+ {"y", 18}
+ }}
+ };
+ QV4::ScopedValue hashValue(
+ scope, e.handle()->newVariantObject(QMetaType::fromType<QVariantHash>(), &hash));
+
+ js->put(name, hashValue);
+
+ StructuredValueType fromHash;
+ fromHash.setI(12);
+ fromHash.setC(ConstructibleValueType(QUrl()));
+ fromHash.setP(QPointF(17, 18));
+
+ QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), fromHash);
+
+ const QVariantMap map {
+ {"i", 13},
+ {"c", QVariant::fromValue(po) },
+ {"p", QVariantMap {
+ {"x", 19},
+ {"y", 20}
+ }}
+ };
+ QV4::ScopedValue mapValue(
+ scope, e.handle()->newVariantObject(QMetaType::fromType<QVariantMap>(), &map));
+ js->put(name, mapValue);
+
+ StructuredValueType fromMap;
+ fromMap.setI(13);
+ fromMap.setC(ConstructibleValueType(po));
+ fromMap.setP(QPointF(19, 20));
+
+ QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), fromMap);
+
+ BarrenValueType immediate;
+ immediate.setI(14);
+ QV4::ScopedValue immediateValue(
+ scope, e.handle()->newVariantObject(QMetaType::fromType<BarrenValueType>(), &immediate));
+ js->put(name, immediateValue);
+
+ StructuredValueType fromImmediate;
+ fromImmediate.setI(14);
+
+ QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), fromImmediate);
+
+ QQmlComponent c2(&e);
+ c2.setData(
+ "import QtQml; QtObject { property int i: 99; property point p: ({x: 3, y: 4}) }", QUrl());
+ QVERIFY(c2.isReady());
+ QScopedPointer<QObject> o2(c2.create());
+ QVERIFY(!o2.isNull());
+ QObject *object = o2.data();
+ QV4::ScopedValue objectValue(
+ scope, e.handle()->newVariantObject(QMetaType::fromType<QObject *>(), &object));
+ js->put(name, objectValue);
+
+ StructuredValueType fromObject;
+ fromObject.setI(99);
+ fromObject.setP(QPointF(3, 4));
+
+ QCOMPARE(o->property("fromInsanity").value<StructuredValueType>(), fromObject);
+
+ const MyTypeObject *m = static_cast<const MyTypeObject *>(po);
+ QVERIFY(!m->hasEffectPadding());
+ QMetaObject::invokeMethod(po, "updatePadding");
+ QVERIFY(m->hasEffectPadding());
+ QCOMPARE(m->effectPadding(), QRectF());
+ po->setProperty("newItemPadding", QRectF(1, 2, 3, 4));
+ QMetaObject::invokeMethod(po, "updatePadding");
+ QVERIFY(m->hasEffectPadding());
+ QCOMPARE(m->effectPadding(), QRectF(1, 2, 3, 4));
}
void tst_qqmlvaluetypeproviders::recursive()
@@ -363,6 +477,9 @@ void tst_qqmlvaluetypeproviders::recursive()
MyTypeObject *m = qobject_cast<MyTypeObject *>(o.data());
QCOMPARE(m->structured().p().x(), 76);
+
+ // Recursive write back into a list detached from the property.
+ QCOMPARE(m->property("aa").toInt(), 12);
}
void tst_qqmlvaluetypeproviders::date()
@@ -379,6 +496,72 @@ void tst_qqmlvaluetypeproviders::date()
QCOMPARE(o->property("aVariant").value<QDateTime>().time().minute(), 44);
}
+void tst_qqmlvaluetypeproviders::constructors()
+{
+
+ QQmlEngine e;
+
+ {
+ const auto guard = qScopeGuard([]() { Padding::log.clear(); });
+ QQmlComponent component(&e);
+ component.setData("import Test\nMyItem { padding : 50 }", QUrl());
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+ MyItem *item = qobject_cast<MyItem *>(o.data());
+ QVERIFY(item);
+ QCOMPARE(item->padding().left(), 50);
+ QCOMPARE(item->padding().right(), 50);
+
+ QCOMPARE(Padding::log.length(), 3);
+
+ // Created by default ctor of MyItem
+ QCOMPARE(Padding::log[0].type, Padding::CustomCtor);
+ QCOMPARE(Padding::log[0].left, 17);
+ QCOMPARE(Padding::log[0].right, 17);
+
+ // Created by assignment of integer
+ QCOMPARE(Padding::log[1].type, Padding::InvokableCtor);
+ QCOMPARE(Padding::log[1].left, 50);
+ QCOMPARE(Padding::log[1].right, 50);
+
+ // In MyItem::setPadding()
+ QCOMPARE(Padding::log[2].type, Padding::CopyAssign);
+ QCOMPARE(Padding::log[2].left, 50);
+ QCOMPARE(Padding::log[2].right, 50);
+ }
+
+ {
+ const auto guard = qScopeGuard([]() { Padding::log.clear(); });
+ QQmlComponent component(&e);
+ component.setData("import Test\nMyItem { padding: ({ left: 10, right: 20 }) }", QUrl());
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+ MyItem *item = qobject_cast<MyItem *>(o.data());
+ QVERIFY(item);
+ QCOMPARE(item->padding().left(), 10);
+ QCOMPARE(item->padding().right(), 20);
+
+ QCOMPARE(Padding::log.length(), 3);
+
+ // Created by default ctor of MyItem
+ QCOMPARE(Padding::log[0].type, Padding::CustomCtor);
+ QCOMPARE(Padding::log[0].left, 17);
+ QCOMPARE(Padding::log[0].right, 17);
+
+ // Preparing for setting properties of structured value
+ QCOMPARE(Padding::log[1].type, Padding::DefaultCtor);
+ QCOMPARE(Padding::log[1].left, 0);
+ QCOMPARE(Padding::log[1].right, 0);
+
+ // In MyItem::setPadding()
+ QCOMPARE(Padding::log[2].type, Padding::CopyAssign);
+ QCOMPARE(Padding::log[2].left, 10);
+ QCOMPARE(Padding::log[2].right, 20);
+ }
+}
+
QTEST_MAIN(tst_qqmlvaluetypeproviders)
#include "tst_qqmlvaluetypeproviders.moc"
diff --git a/tests/auto/qml/qqmlvaluetypes/CMakeLists.txt b/tests/auto/qml/qqmlvaluetypes/CMakeLists.txt
index fd4bbc1c3f..e167aade13 100644
--- a/tests/auto/qml/qqmlvaluetypes/CMakeLists.txt
+++ b/tests/auto/qml/qqmlvaluetypes/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlvaluetypes Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlvaluetypes LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.cpp b/tests/auto/qml/qqmlvaluetypes/testtypes.cpp
index 4344445514..d6304bf086 100644
--- a/tests/auto/qml/qqmlvaluetypes/testtypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/testtypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
void registerTypes()
diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.h b/tests/auto/qml/qqmlvaluetypes/testtypes.h
index 2c431c64af..19cfc25d23 100644
--- a/tests/auto/qml/qqmlvaluetypes/testtypes.h
+++ b/tests/auto/qml/qqmlvaluetypes/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 1f9dfb7fd4..2634044238 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
@@ -93,30 +93,26 @@ void tst_qqmlvaluetypes::point()
{
{
QQmlComponent component(&engine, testFileUrl("point_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->property("p_x").toInt(), 10);
QCOMPARE(object->property("p_y").toInt(), 4);
QCOMPARE(object->property("copy"), QVariant(QPoint(10, 4)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("point_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->point(), QPoint(11, 12));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("point_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QPoint(10, 4)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -129,8 +125,6 @@ void tst_qqmlvaluetypes::point()
QCOMPARE(object->property("equalsSelf").toBool(), true);
QCOMPARE(object->property("equalsOther").toBool(), false);
QCOMPARE(object->property("pointEqualsPointf").toBool(), true);
-
- delete object;
}
}
@@ -138,30 +132,26 @@ void tst_qqmlvaluetypes::pointf()
{
{
QQmlComponent component(&engine, testFileUrl("pointf_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(float(object->property("p_x").toDouble()), float(11.3));
QCOMPARE(float(object->property("p_y").toDouble()), float(-10.9));
QCOMPARE(object->property("copy"), QVariant(QPointF(11.3, -10.9)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("pointf_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->pointf(), QPointF(6.8, 9.3));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("pointf_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QPointF(11.3, -10.9)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -174,8 +164,6 @@ void tst_qqmlvaluetypes::pointf()
QCOMPARE(object->property("equalsSelf").toBool(), true);
QCOMPARE(object->property("equalsOther").toBool(), false);
QCOMPARE(object->property("pointfEqualsPoint").toBool(), true);
-
- delete object;
}
}
@@ -183,30 +171,26 @@ void tst_qqmlvaluetypes::size()
{
{
QQmlComponent component(&engine, testFileUrl("size_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->property("s_width").toInt(), 1912);
QCOMPARE(object->property("s_height").toInt(), 1913);
QCOMPARE(object->property("copy"), QVariant(QSize(1912, 1913)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("size_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->size(), QSize(13, 88));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("size_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QSize(1912, 1913)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -219,8 +203,6 @@ void tst_qqmlvaluetypes::size()
QCOMPARE(object->property("equalsSelf").toBool(), true);
QCOMPARE(object->property("equalsOther").toBool(), false);
QCOMPARE(object->property("sizeEqualsSizef").toBool(), true);
-
- delete object;
}
}
@@ -228,30 +210,26 @@ void tst_qqmlvaluetypes::sizef()
{
{
QQmlComponent component(&engine, testFileUrl("sizef_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(float(object->property("s_width").toDouble()), float(0.1));
QCOMPARE(float(object->property("s_height").toDouble()), float(100923.2));
QCOMPARE(object->property("copy"), QVariant(QSizeF(0.1, 100923.2)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("sizef_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->sizef(), QSizeF(44.3, 92.8));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("sizef_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QSizeF(0.1, 100923)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -264,8 +242,6 @@ void tst_qqmlvaluetypes::sizef()
QCOMPARE(object->property("equalsSelf").toBool(), true);
QCOMPARE(object->property("equalsOther").toBool(), false);
QCOMPARE(object->property("sizefEqualsSize").toBool(), true);
-
- delete object;
}
}
@@ -299,9 +275,9 @@ void tst_qqmlvaluetypes::locale()
QCOMPARE(object->property("textDirection").toInt(), int(locale.textDirection()));
QCOMPARE(object->property("uiLanguages").toStringList(), locale.uiLanguages());
QList<Qt::DayOfWeek> weekDays;
- foreach (const QVariant &weekDay, object->property("weekDays").toList()) {
+ const QVariantList weekDaysProperty = object->property("weekDays").toList();
+ for (const QVariant &weekDay : weekDaysProperty)
weekDays.append(Qt::DayOfWeek(weekDay.toInt()));
- }
QCOMPARE(weekDays, locale.weekdays());
QCOMPARE(object->property("zeroDigit").toString().at(0), locale.zeroDigit());
#endif // im
@@ -311,29 +287,25 @@ void tst_qqmlvaluetypes::locale()
void tst_qqmlvaluetypes::qmlproperty()
{
QQmlComponent component(&engine, testFileUrl("qmlproperty_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
- QCOMPARE(object->property("colorPropertyObject").value<QObject *>(), object);
+ QCOMPARE(object->property("colorPropertyObject").value<QObject *>(), object.get());
QCOMPARE(object->property("colorPropertyName").toString(), "color");
QCOMPARE(object->property("invalidPropertyObject").value<QObject *>(), nullptr);
QCOMPARE(object->property("invalidPropertyName").toString(), "");
-
- delete object;
}
void tst_qqmlvaluetypes::sizereadonly()
{
{
QQmlComponent component(&engine, testFileUrl("sizereadonly_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->property("s_width").toInt(), 1912);
QCOMPARE(object->property("s_height").toInt(), 1913);
QCOMPARE(object->property("copy"), QVariant(QSize(1912, 1913)));
-
- delete object;
}
{
@@ -357,12 +329,10 @@ void tst_qqmlvaluetypes::sizereadonly()
{
QQmlComponent component(&engine, testFileUrl("sizereadonly_writeerror4.qml"));
- QObject *object = component.create();
+ std::unique_ptr<QObject> object { component.create() };
QVERIFY(object);
QCOMPARE(object->property("sizereadonly").toSize(), QSize(1912, 1913));
-
- delete object;
}
}
@@ -370,8 +340,8 @@ void tst_qqmlvaluetypes::rect()
{
{
QQmlComponent component(&engine, testFileUrl("rect_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->property("r_x").toInt(), 2);
QCOMPARE(object->property("r_y").toInt(), 3);
@@ -382,24 +352,20 @@ void tst_qqmlvaluetypes::rect()
QCOMPARE(object->property("r_top").toInt(), 3);
QCOMPARE(object->property("r_bottom").toInt(), 104);
QCOMPARE(object->property("copy"), QVariant(QRect(2, 3, 109, 102)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("rect_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect(), QRect(1234, 7, 56, 63));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("rect_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QRect(2, 3, 109, 102)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -412,8 +378,6 @@ void tst_qqmlvaluetypes::rect()
QCOMPARE(object->property("equalsSelf").toBool(), true);
QCOMPARE(object->property("equalsOther").toBool(), false);
QCOMPARE(object->property("rectEqualsRectf").toBool(), true);
-
- delete object;
}
}
@@ -421,8 +385,8 @@ void tst_qqmlvaluetypes::rectf()
{
{
QQmlComponent component(&engine, testFileUrl("rectf_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(float(object->property("r_x").toDouble()), float(103.8));
QCOMPARE(float(object->property("r_y").toDouble()), float(99.2));
@@ -433,24 +397,20 @@ void tst_qqmlvaluetypes::rectf()
QCOMPARE(float(object->property("r_top").toDouble()), float(99.2));
QCOMPARE(float(object->property("r_bottom").toDouble()), float(176.8));
QCOMPARE(object->property("copy"), QVariant(QRectF(103.8, 99.2, 88.1, 77.6)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("rectf_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rectf(), QRectF(70.1, -113.2, 80924.8, 99.2));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("rectf_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QRectF(103.8, 99.2, 88.1, 77.6)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -463,8 +423,6 @@ void tst_qqmlvaluetypes::rectf()
QCOMPARE(object->property("equalsSelf").toBool(), true);
QCOMPARE(object->property("equalsOther").toBool(), false);
QCOMPARE(object->property("rectfEqualsRect").toBool(), true);
-
- delete object;
}
}
@@ -472,30 +430,26 @@ void tst_qqmlvaluetypes::vector2d()
{
{
QQmlComponent component(&engine, testFileUrl("vector2d_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE((float)object->property("v_x").toDouble(), (float)32.88);
QCOMPARE((float)object->property("v_y").toDouble(), (float)1.3);
QCOMPARE(object->property("copy"), QVariant(QVector2D(32.88f, 1.3f)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector2d_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->vector2(), QVector2D(-0.3f, -12.9f));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector2d_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QVector2D(32.88, 1.3)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -506,16 +460,13 @@ void tst_qqmlvaluetypes::vector2d()
QCOMPARE(object->property("equalsPoint").toBool(), false);
QCOMPARE(object->property("equalsRect").toBool(), false);
QCOMPARE(object->property("equalsSelf").toBool(), true);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector2d_invokables.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QVERIFY(object->property("success").toBool());
- delete object;
}
}
@@ -523,31 +474,27 @@ void tst_qqmlvaluetypes::vector3d()
{
{
QQmlComponent component(&engine, testFileUrl("vector3d_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE((float)object->property("v_x").toDouble(), (float)23.88);
QCOMPARE((float)object->property("v_y").toDouble(), (float)3.1);
QCOMPARE((float)object->property("v_z").toDouble(), (float)4.3);
QCOMPARE(object->property("copy"), QVariant(QVector3D(23.88f, 3.1f, 4.3f)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector3d_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->vector(), QVector3D(-0.3f, -12.9f, 907.4f));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector3d_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QVector3D(23.88, 3.1, 4.3)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -559,16 +506,13 @@ void tst_qqmlvaluetypes::vector3d()
QCOMPARE(object->property("equalsRect").toBool(), false);
QCOMPARE(object->property("equalsSelf").toBool(), true);
QCOMPARE(object->property("equalsOther").toBool(), false);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector3d_invokables.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QVERIFY(object->property("success").toBool());
- delete object;
}
}
@@ -576,32 +520,28 @@ void tst_qqmlvaluetypes::vector4d()
{
{
QQmlComponent component(&engine, testFileUrl("vector4d_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE((float)object->property("v_x").toDouble(), (float)54.2);
QCOMPARE((float)object->property("v_y").toDouble(), (float)23.88);
QCOMPARE((float)object->property("v_z").toDouble(), (float)3.1);
QCOMPARE((float)object->property("v_w").toDouble(), (float)4.3);
QCOMPARE(object->property("copy"), QVariant(QVector4D(54.2f, 23.88f, 3.1f, 4.3f)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector4d_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->vector4(), QVector4D(-0.3f, -12.9f, 907.4f, 88.5f));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector4d_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QVector4D(54.2, 23.88, 3.1, 4.3)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -612,16 +552,13 @@ void tst_qqmlvaluetypes::vector4d()
QCOMPARE(object->property("equalsPoint").toBool(), false);
QCOMPARE(object->property("equalsRect").toBool(), false);
QCOMPARE(object->property("equalsSelf").toBool(), true);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("vector4d_invokables.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QVERIFY(object->property("success").toBool());
- delete object;
}
}
@@ -629,32 +566,28 @@ void tst_qqmlvaluetypes::quaternion()
{
{
QQmlComponent component(&engine, testFileUrl("quaternion_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE((float)object->property("v_scalar").toDouble(), (float)4.3);
QCOMPARE((float)object->property("v_x").toDouble(), (float)54.2);
QCOMPARE((float)object->property("v_y").toDouble(), (float)23.88);
QCOMPARE((float)object->property("v_z").toDouble(), (float)3.1);
QCOMPARE(object->property("copy"), QVariant(QQuaternion(4.3f, 54.2f, 23.88f, 3.1f)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("quaternion_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->quaternion(), QQuaternion(88.5f, -0.3f, -12.9f, 907.4f));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("quaternion_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QQuaternion(4.3, 54.2, 23.88, 3.1)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -665,16 +598,13 @@ void tst_qqmlvaluetypes::quaternion()
QCOMPARE(object->property("equalsPoint").toBool(), false);
QCOMPARE(object->property("equalsRect").toBool(), false);
QCOMPARE(object->property("equalsSelf").toBool(), true);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("quaternion_invokables.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QVERIFY(object->property("success").toBool());
- delete object;
}
}
@@ -682,8 +612,8 @@ void tst_qqmlvaluetypes::matrix4x4()
{
{
QQmlComponent component(&engine, testFileUrl("matrix4x4_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE((float)object->property("v_m11").toDouble(), (float)1);
QCOMPARE((float)object->property("v_m12").toDouble(), (float)2);
@@ -706,27 +636,23 @@ void tst_qqmlvaluetypes::matrix4x4()
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16)));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("matrix4x4_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->matrix(), QMatrix4x4(11, 12, 13, 14,
21, 22, 23, 24,
31, 32, 33, 34,
41, 42, 43, 44));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("matrix4x4_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QMatrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)");
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -737,16 +663,13 @@ void tst_qqmlvaluetypes::matrix4x4()
QCOMPARE(object->property("equalsPoint").toBool(), false);
QCOMPARE(object->property("equalsRect").toBool(), false);
QCOMPARE(object->property("equalsSelf").toBool(), true);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("matrix4x4_invokables.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("success").toBool(), true);
- delete object;
}
}
@@ -754,9 +677,9 @@ void tst_qqmlvaluetypes::font()
{
{
QQmlComponent component(&engine, testFileUrl("font_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
- QVERIFY(object != nullptr);
+ QVERIFY(object);
QCOMPARE(object->property("f_family").toString(), object->font().family());
QCOMPARE(object->property("f_bold").toBool(), object->font().bold());
@@ -783,14 +706,12 @@ void tst_qqmlvaluetypes::font()
QCOMPARE(object->property("f_wordSpacing").toDouble(), object->font().wordSpacing());
QCOMPARE(object->property("copy"), QVariant(object->font()));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("font_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QFont font;
font.setFamily("Helvetica");
@@ -815,46 +736,38 @@ void tst_qqmlvaluetypes::font()
QCOMPARE(f.capitalization(), font.capitalization());
QCOMPARE(f.letterSpacing(), font.letterSpacing());
QCOMPARE(f.wordSpacing(), font.wordSpacing());
-
- delete object;
}
// Test pixelSize
{
QQmlComponent component(&engine, testFileUrl("font_write.2.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->font().pixelSize(), 10);
-
- delete object;
}
// Test pixelSize and pointSize
{
QQmlComponent component(&engine, testFileUrl("font_write.3.qml"));
QTest::ignoreMessage(QtWarningMsg, "Both point size and pixel size set. Using pixel size.");
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->font().pixelSize(), 10);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("font_write.4.qml"));
QTest::ignoreMessage(QtWarningMsg, "Both point size and pixel size set. Using pixel size.");
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->font().pixelSize(), 10);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("font_write.5.qml"));
- QObject *object = qobject_cast<QObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { qobject_cast<QObject *>(component.create()) };
+ QVERIFY(object);
MyTypeObject *object1 = object->findChild<MyTypeObject *>("object1");
QVERIFY(object1 != nullptr);
MyTypeObject *object2 = object->findChild<MyTypeObject *>("object2");
@@ -862,14 +775,12 @@ void tst_qqmlvaluetypes::font()
QCOMPARE(object1->font().pixelSize(), 19);
QCOMPARE(object2->font().pointSize(), 14);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("font_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QString tostring = QLatin1String("QFont(") + object->font().toString() + QLatin1Char(')');
QCOMPARE(object->property("tostring").toString(), tostring);
@@ -880,8 +791,6 @@ void tst_qqmlvaluetypes::font()
QCOMPARE(object->property("equalsPoint").toBool(), false);
QCOMPARE(object->property("equalsRect").toBool(), false);
QCOMPARE(object->property("equalsSelf").toBool(), true);
-
- delete object;
}
}
@@ -889,8 +798,8 @@ void tst_qqmlvaluetypes::color()
{
{
QQmlComponent component(&engine, testFileUrl("color_read.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(float(object->property("v_r").toDouble()), 0.2f);
QCOMPARE(float(object->property("v_g").toDouble()), 0.88f);
@@ -916,14 +825,12 @@ void tst_qqmlvaluetypes::color()
comparison.setBlueF(0.6f);
comparison.setAlphaF(0.34f);
QCOMPARE(object->property("copy"), QVariant(comparison));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("color_write.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QColor newColor;
newColor.setRedF(0.5f);
@@ -931,38 +838,32 @@ void tst_qqmlvaluetypes::color()
newColor.setBlueF(0.3f);
newColor.setAlphaF(0.7f);
QCOMPARE(object->color(), newColor);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("color_write_HSV.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QColor newColor;
newColor.setHsvF(0.43f, 0.77f, 0.88f, 0.7f);
QCOMPARE(object->color(), newColor);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("color_write_HSL.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QColor newColor;
newColor.setHslF(0.43f, 0.74f, 0.54f, 0.7f);
QCOMPARE(object->color(), newColor);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("color_compare.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QColor comparison;
comparison.setRedF(0.2f);
comparison.setGreenF(0.88f);
@@ -990,8 +891,6 @@ void tst_qqmlvaluetypes::color()
QCOMPARE(object->property("equalsColorRHS").toBool(), object->property("equalsColor").toBool());
QCOMPARE(object->property("colorEqualsCopy").toBool(), true);
QCOMPARE(object->property("copyEqualsColor").toBool(), object->property("colorEqualsCopy").toBool());
-
- delete object;
}
}
@@ -1001,8 +900,8 @@ void tst_qqmlvaluetypes::bindingAssignment()
// binding declaration
{
QQmlComponent component(&engine, testFileUrl("bindingAssignment.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect().x(), 10);
QCOMPARE(object->rect().y(), 15);
@@ -1011,8 +910,6 @@ void tst_qqmlvaluetypes::bindingAssignment()
QCOMPARE(object->rect().x(), 92);
QCOMPARE(object->rect().y(), 97);
-
- delete object;
}
// function assignment should fail without crashing
@@ -1022,12 +919,11 @@ void tst_qqmlvaluetypes::bindingAssignment()
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
QQmlComponent component(&engine, testFileUrl("bindingAssignment.2.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect().x(), 5);
object->setProperty("value", QVariant(92));
QCOMPARE(object->rect().x(), 5);
- delete object;
}
}
@@ -1035,42 +931,36 @@ void tst_qqmlvaluetypes::bindingAssignment()
void tst_qqmlvaluetypes::bindingRead()
{
QQmlComponent component(&engine, testFileUrl("bindingRead.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->property("value").toInt(), 2);
object->setRect(QRect(19, 3, 88, 2));
QCOMPARE(object->property("value").toInt(), 19);
-
- delete object;
}
// Test static values can assign to value types
void tst_qqmlvaluetypes::staticAssignment()
{
QQmlComponent component(&engine, testFileUrl("staticAssignment.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect().x(), 9);
-
- delete object;
}
// Test scripts can read/write value types
void tst_qqmlvaluetypes::scriptAccess()
{
QQmlComponent component(&engine, testFileUrl("scriptAccess.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->property("valuePre").toInt(), 2);
QCOMPARE(object->rect().x(), 19);
QCOMPARE(object->property("valuePost").toInt(), 19);
-
- delete object;
}
// Test that assigning a constant from script removes any binding
@@ -1078,8 +968,8 @@ void tst_qqmlvaluetypes::autoBindingRemoval()
{
{
QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect().x(), 10);
@@ -1094,14 +984,12 @@ void tst_qqmlvaluetypes::autoBindingRemoval()
object->setProperty("value", QVariant(92));
QCOMPARE(object->rect().x(), 42);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.2.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect().x(), 10);
@@ -1116,16 +1004,14 @@ void tst_qqmlvaluetypes::autoBindingRemoval()
object->setProperty("value", QVariant(92));
QCOMPARE(object->rect(), QRect(10, 10, 10, 10));
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.3.qml"));
QString warning = component.url().toString() + ":6:5: Unable to assign [undefined] to QRect";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
object->setProperty("value", QVariant(QRect(9, 22, 33, 44)));
@@ -1138,8 +1024,6 @@ void tst_qqmlvaluetypes::autoBindingRemoval()
object->setProperty("value", QVariant(QRect(19, 3, 4, 8)));
QCOMPARE(object->rect(), QRect(44, 22, 33, 44));
-
- delete object;
}
}
@@ -1147,12 +1031,10 @@ void tst_qqmlvaluetypes::autoBindingRemoval()
void tst_qqmlvaluetypes::valueSources()
{
QQmlComponent component(&engine, testFileUrl("valueSources.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect().x(), 3345);
-
- delete object;
}
static void checkNoErrors(QQmlComponent& component)
@@ -1170,17 +1052,15 @@ static void checkNoErrors(QQmlComponent& component)
void tst_qqmlvaluetypes::valueInterceptors()
{
QQmlComponent component(&engine, testFileUrl("valueInterceptors.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
checkNoErrors(component);
- QVERIFY(object != nullptr);
+ QVERIFY(object);
QCOMPARE(object->rect().x(), 13);
object->setProperty("value", 99);
QCOMPARE(object->rect().x(), 112);
-
- delete object;
}
// Test that you can't assign a binding to the "root" value type, and a sub-property
@@ -1192,11 +1072,10 @@ void tst_qqmlvaluetypes::bindingConflict()
#define CPP_TEST(type, v) \
{ \
- type *t = new type; \
+ std::unique_ptr<type> t = std::make_unique<type>(); \
QVariant value(v); \
t->setValue(value); \
QCOMPARE(t->value(), value); \
- delete t; \
}
// Test that accessing a reference to a valuetype after the owning object is deleted
@@ -1205,87 +1084,76 @@ void tst_qqmlvaluetypes::deletedObject()
{
QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
QTest::ignoreMessage(QtDebugMsg, "Test: 2");
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
- QObject *dObject = qvariant_cast<QObject *>(object->property("object"));
- QVERIFY(dObject != nullptr);
- delete dObject;
+ std::unique_ptr<QObject> dObject { qvariant_cast<QObject *>(object->property("object")) };
+ QVERIFY(dObject);
+ dObject.reset();
QTest::ignoreMessage(QtDebugMsg, "Test: undefined");
object->emitRunScript();
-
- delete object;
}
// Test that value types can be assigned to another value type property in a binding
void tst_qqmlvaluetypes::bindingVariantCopy()
{
QQmlComponent component(&engine, testFileUrl("bindingVariantCopy.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect(), QRect(19, 33, 5, 99));
-
- delete object;
}
// Test that value types can be assigned to another value type property in script
void tst_qqmlvaluetypes::scriptVariantCopy()
{
QQmlComponent component(&engine, testFileUrl("scriptVariantCopy.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->rect(), QRect(2, 3, 109, 102));
object->emitRunScript();
QCOMPARE(object->rect(), QRect(19, 33, 5, 99));
-
- delete object;
}
void tst_qqmlvaluetypes::enums()
{
{
QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("enums.4.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("enums.5.qml"));
- MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
- QVERIFY(object != nullptr);
+ std::unique_ptr<MyTypeObject> object { qobject_cast<MyTypeObject *>(component.create()) };
+ QVERIFY(object);
QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
- delete object;
}
}
@@ -1295,83 +1163,73 @@ void tst_qqmlvaluetypes::conflictingBindings()
{
{
QQmlComponent component(&engine, testFileUrl("conflicting.1.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
- QMetaObject::invokeMethod(object, "toggle");
+ QMetaObject::invokeMethod(object.get(), "toggle");
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6);
- QMetaObject::invokeMethod(object, "toggle");
+ QMetaObject::invokeMethod(object.get(), "toggle");
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("conflicting.2.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6);
- QMetaObject::invokeMethod(object, "toggle");
+ QMetaObject::invokeMethod(object.get(), "toggle");
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
- QMetaObject::invokeMethod(object, "toggle");
+ QMetaObject::invokeMethod(object.get(), "toggle");
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("conflicting.3.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
- QMetaObject::invokeMethod(object, "toggle");
+ QMetaObject::invokeMethod(object.get(), "toggle");
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 24);
- QMetaObject::invokeMethod(object, "toggle");
+ QMetaObject::invokeMethod(object.get(), "toggle");
QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
-
- delete object;
}
}
void tst_qqmlvaluetypes::returnValues()
{
QQmlComponent component(&engine, testFileUrl("returnValues.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("test1").toBool(), true);
QCOMPARE(object->property("test2").toBool(), true);
QCOMPARE(object->property("size").toSize(), QSize(13, 14));
-
- delete object;
}
void tst_qqmlvaluetypes::varAssignment()
{
QQmlComponent component(&engine, testFileUrl("varAssignment.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("x").toInt(), 1);
QCOMPARE(object->property("y").toInt(), 2);
QCOMPARE(object->property("z").toInt(), 3);
-
- delete object;
}
// Test bindings splice together correctly
@@ -1379,77 +1237,63 @@ void tst_qqmlvaluetypes::bindingsSpliceCorrectly()
{
{
QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.1.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("test").toBool(), true);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.2.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("test").toBool(), true);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.3.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("test").toBool(), true);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.4.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("test").toBool(), true);
-
- delete object;
}
{
QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.5.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("test").toBool(), true);
-
- delete object;
}
}
void tst_qqmlvaluetypes::nonValueTypeComparison()
{
QQmlComponent component(&engine, testFileUrl("nonValueTypeComparison.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("test1").toBool(), true);
QCOMPARE(object->property("test2").toBool(), true);
-
- delete object;
}
void tst_qqmlvaluetypes::initializeByWrite()
{
QQmlComponent component(&engine, testFileUrl("initializeByWrite.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QCOMPARE(object->property("test").toBool(), true);
-
- delete object;
}
void tst_qqmlvaluetypes::groupedInterceptors_data()
@@ -1483,8 +1327,8 @@ void tst_qqmlvaluetypes::groupedInterceptors()
QFETCH(QColor, expectedFinalColor);
QQmlComponent component(&engine, testFileUrl(qmlfile));
- QObject *object = component.create();
- QVERIFY2(object != nullptr, qPrintable(component.errorString()));
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY2(object.get(), qPrintable(component.errorString()));
QColor initialColor = object->property("color").value<QColor>();
QVERIFY(fuzzyCompare(initialColor.redF(), expectedInitialColor.redF()));
@@ -1499,8 +1343,6 @@ void tst_qqmlvaluetypes::groupedInterceptors()
QVERIFY(fuzzyCompare(finalColor.greenF(), expectedFinalColor.greenF()));
QVERIFY(fuzzyCompare(finalColor.blueF(), expectedFinalColor.blueF()));
QVERIFY(fuzzyCompare(finalColor.alphaF(), expectedFinalColor.alphaF()));
-
- delete object;
}
struct MyDesk
diff --git a/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt b/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt
index 529840c484..3d93585812 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt
+++ b/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlxmlhttprequest Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlxmlhttprequest LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect
index 6dba52d2de..15274c5e58 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.collection.allprop.expect
@@ -1,13 +1,13 @@
PROPFIND /container/ HTTP/1.1
-Depth: 1
-Content-Length: 95
-Connection: Keep-Alive, Upgrade, HTTP2-Settings
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-Accept-Language: en-US,*
-Content-type:i application/xml; charset="utf-8"
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+depth: 1
+content-length: 95
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+accept-language: {{Ignore}}
+content-type:i application/xml; charset="utf-8"
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect
index dc7217d45c..f7bb57ad89 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/propfind.file.expect
@@ -1,12 +1,12 @@
PROPFIND /file HTTP/1.1
-Content-Length: 192
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-Accept-Language: en-US,*
-Content-type: text/xml; charset="utf-8"
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+content-length: 192
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+accept-language: {{Ignore}}
+content-type: text/xml; charset="utf-8"
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml
index 5c47a27420..624cdc4a41 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.collection.allprop.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml
index dbb57b3b1b..5249ad84c1 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.response.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml
index ecc3627060..eb1dd53a97 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/WebDAV/sendPropfind.responseXML.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/abort.expect b/tests/auto/qml/qqmlxmlhttprequest/data/abort.expect
index 97c1661851..90e15895a6 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/abort.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/abort.expect
@@ -1,11 +1,11 @@
PUT /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Content-Type: text/plain;charset=UTF-8
-Content-Length: 10
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+content-type: text/plain;charset=UTF-8
+content-length: 10
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
Test Data
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/abort.qml b/tests/auto/qml/qqmlxmlhttprequest/data/abort.qml
index c8d8382c8f..f07ba2dbc6 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/abort.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/abort.qml
@@ -13,32 +13,25 @@ QtObject {
var x = new XMLHttpRequest;
x.open("GET", urlDummy);
x.setRequestHeader("Test-header", "TestValue");
- x.setRequestHeader("Accept-Language", "en-US");
x.send();
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
seenDone = true;
- } else if (x.readyState == XMLHttpRequest.UNSENT) {
+ else if (x.readyState == XMLHttpRequest.UNSENT)
didNotSeeUnsent = false;
- }
}
x.abort();
- if (x.readyState == XMLHttpRequest.UNSENT) {
+ if (x.readyState == XMLHttpRequest.UNSENT)
endStateUnsent = true;
- }
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.open("PUT", url);
- x.setRequestHeader("Accept-Language", "en-US");
x.send("Test Data\n");
}
}
-
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml b/tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml
index 7fcbc9367a..5933c3062d 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/abort_opened.qml
@@ -21,14 +21,10 @@ QtObject {
readyState = true;
x.open("PUT", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
x.abort();
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
- if (x.readyState == XMLHttpRequest.OPENED)
+ if (x.readyState == XMLHttpRequest.OPENED)
openedState = true;
try {
@@ -53,8 +49,6 @@ QtObject {
}
}
-
x.send()
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml b/tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml
index 0a85c6b16f..36febc41dc 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/abort_unsent.qml
@@ -21,9 +21,7 @@ QtObject {
readyState = true;
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
- if (x.readyState == XMLHttpRequest.OPENED)
+ if (x.readyState == XMLHttpRequest.OPENED)
openedState = true;
try {
@@ -43,13 +41,10 @@ QtObject {
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
-
x.send()
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml b/tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml
index a9066093d4..cf38ca22df 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/getAllResponseHeaders.qml
@@ -31,9 +31,7 @@ QtObject {
readyState = true;
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
- if (x.readyState == XMLHttpRequest.OPENED)
+ if (x.readyState == XMLHttpRequest.OPENED)
openedState = true;
try {
@@ -43,7 +41,11 @@ QtObject {
openedException = true;
}
- var headers = "connection: close\r\ncontent-type: text/html; charset=UTF-8\r\ntest-header: TestValue\r\nmultitest-header: TestValue, SecondTestValue\r\ncontent-length: 11";
+ var headers = [ "connection: close",
+ "content-type: text/html; charset=UTF-8",
+ "test-header: TestValue",
+ "multitest-header: TestValue, SecondTestValue",
+ "content-length: 11" ].join("\r\n");
// Test to the end
x.onreadystatechange = function() {
@@ -52,7 +54,7 @@ QtObject {
headersReceivedHeader = (x.getAllResponseHeaders() == headers);
} else if (x.readyState == XMLHttpRequest.DONE) {
- doneState = headersReceivedState && true;
+ doneState = headersReceivedState;
doneHeader = (x.getAllResponseHeaders() == headers);
dataOK = (x.responseText == "QML Rocks!\n");
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect b/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect
index 2270e7f3aa..299bfd4689 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.expect
@@ -1,8 +1,8 @@
GET /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml b/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml
index 0219d7b2fc..02c1b6ef47 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/getResponseHeader.qml
@@ -37,9 +37,7 @@ QtObject {
readyState = true;
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
- if (x.readyState == XMLHttpRequest.OPENED)
+ if (x.readyState == XMLHttpRequest.OPENED)
openedState = true;
try {
@@ -56,15 +54,20 @@ QtObject {
headersReceivedNullHeader = (x.getResponseHeader("Nonexistant-header") == "");
headersReceivedValidHeader = (x.getResponseHeader("Test-HEAder") == "TestValue");
- headersReceivedMultiValidHeader = (x.getResponseHeader("MultiTest-HEAder") == "TestValue, SecondTestValue");
- headersReceivedCookieHeader = (x.getResponseHeader("Set-Cookie") == "" && x.getResponseHeader("Set-Cookie2") == "");
+ headersReceivedMultiValidHeader =
+ (x.getResponseHeader("MultiTest-HEAder") == "TestValue, SecondTestValue");
+ headersReceivedCookieHeader =
+ (x.getResponseHeader("Set-Cookie") == ""
+ && x.getResponseHeader("Set-Cookie2") == "");
} else if (x.readyState == XMLHttpRequest.DONE) {
- doneState = headersReceivedState && true;
+ doneState = headersReceivedState;
doneNullHeader = (x.getResponseHeader("Nonexistant-header") == "");
doneValidHeader = (x.getResponseHeader("Test-HEAder") == "TestValue");
- doneMultiValidHeader = (x.getResponseHeader("MultiTest-HEAder") == "TestValue, SecondTestValue");
- doneCookieHeader = (x.getResponseHeader("Set-Cookie") == "" && x.getResponseHeader("Set-Cookie2") == "");
+ doneMultiValidHeader =
+ (x.getResponseHeader("MultiTest-HEAder") == "TestValue, SecondTestValue");
+ doneCookieHeader = (x.getResponseHeader("Set-Cookie") == ""
+ && x.getResponseHeader("Set-Cookie2") == "");
dataOK = (x.responseText == "QML Rocks!\n");
}
}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js b/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js
index adb7269310..4c592bc8de 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js
@@ -1,11 +1,9 @@
(function(url, resultCollector) {
var x = new XMLHttpRequest;
x.open("GET", url);
- x.setRequestHeader("Accept-Language","en-US");
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
resultCollector.responseText = x.responseText
- }
}
x.send()
})
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open.qml
index 6e7681dfb4..26934fabe9 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/open.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/open.qml
@@ -20,9 +20,7 @@ QtObject {
readyState = true;
x.open("GET", url);
- x.setRequestHeader("Accept-Language","en-US");
-
- if (x.readyState == XMLHttpRequest.OPENED)
+ if (x.readyState == XMLHttpRequest.OPENED)
openedState = true;
try {
@@ -42,13 +40,10 @@ QtObject {
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
-
x.send()
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect b/tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect
index 2270e7f3aa..299bfd4689 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_network.expect
@@ -1,8 +1,8 @@
GET /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml
index 3c73141954..24711de2b3 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml
@@ -7,7 +7,6 @@ QtObject {
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("GET", url, false);
- x.setRequestHeader("Accept-Language", "en-US");
x.send();
responseText = x.responseText;
}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml
index 4eaef536b3..f22118a1b0 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_user.qml
@@ -20,9 +20,7 @@ QtObject {
readyState = true;
x.open("GET", url, true, "username", "password");
- x.setRequestHeader("Accept-Language","en-US");
-
- if (x.readyState == XMLHttpRequest.OPENED)
+ if (x.readyState == XMLHttpRequest.OPENED)
openedState = true;
try {
@@ -42,13 +40,10 @@ QtObject {
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
-
x.send()
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml
index b8ce5361f3..b62b09097e 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_username.qml
@@ -21,7 +21,7 @@ QtObject {
x.open("GET", url, true, "sampleusername", "password");
- if (x.readyState == XMLHttpRequest.OPENED)
+ if (x.readyState == XMLHttpRequest.OPENED)
openedState = true;
try {
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.qml b/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.qml
new file mode 100644
index 0000000000..922147bc4e
--- /dev/null
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+QtObject {
+ property string url
+ property bool dataOK: false
+
+ Component.onCompleted: {
+ let xhr = new XMLHttpRequest;
+ xhr.open("GET", url);
+ xhr.overrideMimeType('text/xml');
+
+ // Test to the end
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === XMLHttpRequest.DONE)
+ dataOK = xhr.responseXML !== null;
+ }
+
+ xhr.send();
+ }
+}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.reply b/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.reply
new file mode 100644
index 0000000000..fed050b72d
--- /dev/null
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/overrideMimeType.reply
@@ -0,0 +1,3 @@
+HTTP/1.1 200 OK
+Connection: close
+Content-Type: text/plain
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml b/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml
index b9f0ab6e66..15503d1bc6 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml
@@ -4,6 +4,7 @@ QtObject {
property string url
property int readSize: 0
property int status: 0
+ id: object
Component.onCompleted: {
@@ -13,7 +14,7 @@ QtObject {
request.onreadystatechange = function() {
if (request.readyState == XMLHttpRequest.DONE) {
- status = request.status;
+ object.status = request.status;
var arrayBuffer = request.response;
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect b/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect
index 35d28ec76d..d18a95c7dd 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect
@@ -1,8 +1,8 @@
GET /gml_logo.png HTTP/1.1
-Accept-Language: en-US,*
-Content-Type: image/png
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+content-type: image/png
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect b/tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect
index 9784ba4a80..0df27ead2c 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/receive_json_data.expect
@@ -1,8 +1,8 @@
GET /json.data HTTP/1.1
-Accept-Language: en-US,*
-Content-Type: application/jsonrequest
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+content-type: application/jsonrequest
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml b/tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml
index 4b216d9c85..cf06b6fda6 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/responseText.qml
@@ -22,8 +22,6 @@ QtObject {
unsent = (x.responseText == "");
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
opened = (x.responseText == "");
// Test to the end
@@ -40,8 +38,6 @@ QtObject {
dataOK = (x.responseText == expectedText);
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
reset = (x.responseText == "");
}
}
@@ -51,4 +47,3 @@ QtObject {
sent = (x.responseText == "");
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml b/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml
new file mode 100644
index 0000000000..3a35dcc882
--- /dev/null
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+QtObject {
+ property string url
+ property string expectedURL
+
+ property bool dataOK: false
+
+ Component.onCompleted: {
+ var x = new XMLHttpRequest;
+ x.open("GET", url);
+
+ // Test to the end
+ x.onreadystatechange = function() {
+ if (x.readyState === XMLHttpRequest.DONE)
+ dataOK = (x.responseURL === expectedURL);
+ }
+
+ x.send()
+ }
+}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml
index 65ce90853d..1a3a5285c0 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_alreadySent.qml
@@ -7,13 +7,11 @@ QtObject {
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("GET", "testdocument.html");
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.send();
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect
index 64bd2b3818..708aa1a7f0 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.expect
@@ -1,11 +1,11 @@
POST /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Content-Type: text/plain;charset=UTF-8
-Content-Length: 13
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+content-type: text/plain;charset=UTF-8
+content-length: 13
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
My Sent Data
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml
index 57203433c0..2f794650ba 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.1.qml
@@ -2,19 +2,16 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("POST", url);
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.send("My Sent Data\n");
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect
index 2bf3b5a081..bb82e8ba74 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.10.expect
@@ -1,11 +1,11 @@
OPTIONS /testdocument.html HTTP/1.1
-Accept-Language: en-US,*
-Content-Type: text/plain;charset=UTF-8
-Content-Length: 13
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+content-type: text/plain;charset=UTF-8
+content-length: 13
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
My Sent Data
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect
index 7132819c72..341fb36f2a 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect
Binary files differ
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml
index 25584a662b..136243bbb2 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml
@@ -2,19 +2,16 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("POST", url);
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
var data = new Uint8Array([1, 2, 3, 0, 3, 2, 1, 10])
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml
index e63d58fdb6..e3d55889d6 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.2.qml
@@ -2,23 +2,19 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("POST", url);
x.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.send("My Sent Data\n");
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml
index cfd67a6a72..eaf5a057ae 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.3.qml
@@ -2,23 +2,19 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("POST", url);
x.setRequestHeader("Content-Type", "text/plain;charset=latin1");
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.send("My Sent Data\n");
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect
index 270ee838d9..75e6758f14 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.expect
@@ -1,11 +1,11 @@
POST /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Content-Type: charset=UTF-8;text/plain
-Content-Length: 13
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+content-type: charset=UTF-8;text/plain
+content-length: 13
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
My Sent Data
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml
index 808fb3a18d..e65982e6cf 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.4.qml
@@ -2,23 +2,19 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("POST", url);
x.setRequestHeader("Content-Type", "charset=UTF-8;text/plain");
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.send("My Sent Data\n");
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml
index c8735173d3..b3c24a5328 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.5.qml
@@ -2,23 +2,19 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("POST", url);
x.setRequestHeader("Content-Type", "charset=latin1;text/plain");
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.send("My Sent Data\n");
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect
index f6c8083875..02062e905b 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.expect
@@ -1,11 +1,11 @@
PUT /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Content-Type: text/plain;charset=UTF-8
-Content-Length: 13
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+content-type: text/plain;charset=UTF-8
+content-length: 13
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
My Sent Data
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml
index ee6a9caa06..345d486669 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.6.qml
@@ -2,19 +2,16 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("PUT", url);
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.send("My Sent Data\n");
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml
index 003a912818..a378624803 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.7.qml
@@ -2,23 +2,19 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("POST", url);
x.setRequestHeader("Content-Type", "text/plain");
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
x.send("My Sent Data\n");
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect
index bcd7e79fc1..c580208eca 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.8.expect
@@ -1,8 +1,8 @@
OPTIONS / HTTP/1.1
-Content-Length: 0
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-Accept-Language: en-US,*
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+content-length: 0
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+accept-language: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect
index 2d4f2c3146..3a7ae3730d 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.9.expect
@@ -1,8 +1,8 @@
OPTIONS /testdocument.html HTTP/1.1
-Content-Length: 0
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-Accept-Language: en-US,*
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+content-length: 0
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+accept-language: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml
index 336971c919..38fd3800d8 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData.qml
@@ -9,19 +9,14 @@ QtObject {
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open(reqType, url);
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
- if (reqType == "HEAD" || reqType == "DELETE")
- dataOK = (x.responseText == "");
- else
- dataOK = (x.responseText == "QML Rocks!\n");
- }
+ let expect = reqType == "HEAD" || reqType == "DELETE" ? "" : "QML Rocks!\n";
+ if (x.readyState == XMLHttpRequest.DONE)
+ dataOK = (x.responseText == expect);
}
x.send("Data To Ignore");
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect
index 7d96c00101..feca475b75 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_DELETE.expect
@@ -1,8 +1,8 @@
DELETE /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect
index 2270e7f3aa..299bfd4689 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_GET.expect
@@ -1,8 +1,8 @@
GET /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect
index 064cf1b6b7..fa757aeac3 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_ignoreData_HEAD.expect
@@ -1,8 +1,8 @@
HEAD /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect
index 55f7f7dceb..d6ff9854a5 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect
@@ -1,13 +1,13 @@
PATCH /qqmlxmlhttprequest.cpp HTTP/1.1
-Accept-Language: en-US
-If-Match: "ETagNumber"
-Content-Type: application/example
-Content-Length: 247
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+if-match: "ETagNumber"
+content-type: application/example
+content-length: 247
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
--- a/qqmlxmlhttprequest.cpp
+++ b/qqmlxmlhttprequest.cpp
@@ -15,4 +15,3 @@ Host: {{ServerHostUrl}}
- } else if (m_method == QLatin1String("OPTIONS")) {
+ } else if (m_method == QLatin1String("OPTIONS") ||
+ (m_method == QLatin1String("PATCH"))) {
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml
index 35a629fc92..d7d57b445c 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
@@ -12,7 +12,6 @@ QtObject {
Component.onCompleted: {
var x = new XMLHttpRequest;
x.open("PATCH", url);
- x.setRequestHeader("Accept-Language","en-US");
x.setRequestHeader("If-Match","\"ETagNumber\"");
// Test to the end
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect
index 003e54f33c..29d2d22030 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.expect
@@ -1,10 +1,10 @@
GET /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Test-header: value
-Test-header2: value,value2
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+test-header: value
+test-header2: value,value2
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml
index 4229584af2..1490d0f108 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader.qml
@@ -2,14 +2,11 @@ import QtQuick 2.0
QtObject {
property string url
-
property bool dataOK: false
Component.onCompleted: {
var x = new XMLHttpRequest;
-
x.open("GET", url);
- x.setRequestHeader("Accept-Language","en-US");
x.setRequestHeader("Test-header", "value");
x.setRequestHeader("Test-header2", "value");
@@ -25,5 +22,3 @@ QtObject {
x.send();
}
}
-
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml
index e03f73431a..015682b5d5 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_caseInsensitive.qml
@@ -9,12 +9,10 @@ QtObject {
var x = new XMLHttpRequest;
x.open("GET", url);
- x.setRequestHeader("Accept-Language","en-US");
-
x.setRequestHeader("Test-header", "value");
- //Setting headers with just different cases
- //will be treated as the same header, and accepted
- //as the last setting.
+
+ // Setting headers differing only in case will treat them as one header,
+ // and the values are joined with a comma.
x.setRequestHeader("Test-hEADEr2", "value");
x.setRequestHeader("Test-header2", "value2");
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml
index cd047cf8dd..23a3c42fcc 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_illegalName.qml
@@ -21,11 +21,9 @@ QtObject {
readyState = true;
x.open("GET", url);
- x.setRequestHeader("Accept-Language","en-US");
-
x.setRequestHeader(header, "Value");
- if (x.readyState == XMLHttpRequest.OPENED)
+ if (x.readyState == XMLHttpRequest.OPENED)
openedState = true;
try {
@@ -45,14 +43,10 @@ QtObject {
// Test to the end
x.onreadystatechange = function() {
- if (x.readyState == XMLHttpRequest.DONE) {
+ if (x.readyState == XMLHttpRequest.DONE)
dataOK = (x.responseText == "QML Rocks!\n");
- }
}
-
x.send()
}
}
-
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml
index 49888fdac8..2f37006de9 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/setRequestHeader_sent.qml
@@ -8,9 +8,7 @@ QtObject {
Component.onCompleted: {
var x = new XMLHttpRequest;
-
x.open("GET", url);
- x.setRequestHeader("Accept-Language","en-US");
// Test to the end
x.onreadystatechange = function() {
@@ -29,4 +27,3 @@ QtObject {
}
}
}
-
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/status.expect b/tests/auto/qml/qqmlxmlhttprequest/data/status.expect
index 2270e7f3aa..299bfd4689 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/status.expect
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/status.expect
@@ -1,8 +1,8 @@
GET /testdocument.html HTTP/1.1
-Accept-Language: en-US
-Connection: Keep-Alive{{Ignore}}
-HTTP2-Settings: {{Ignore}}
-Accept-Encoding: {{Ignore}}
-User-Agent: Mozilla/5.0
-Host: {{ServerHostUrl}}
+accept-language: {{Ignore}}
+connection: Keep-Alive{{Ignore}}
+http2-settings: {{Ignore}}
+accept-encoding: {{Ignore}}
+user-agent: Mozilla/5.0
+host: {{ServerHostUrl}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/status.qml b/tests/auto/qml/qqmlxmlhttprequest/data/status.qml
index 94908f63c0..ed7268d6b3 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/status.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/status.qml
@@ -30,8 +30,6 @@ QtObject {
}
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
try {
var a = x.status;
} catch (e) {
@@ -54,8 +52,6 @@ QtObject {
dataOK = (x.responseText == "QML Rocks!\n");
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
try {
var a = x.status;
} catch (e) {
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml b/tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml
index b47a0f1af0..cbc15b47f0 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/statusText.qml
@@ -27,8 +27,6 @@ QtObject {
}
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
try {
var a = x.statusText;
} catch (e) {
@@ -51,8 +49,6 @@ QtObject {
dataOK = (x.responseText == "QML Rocks!\n") && (x.response == "QML Rocks!\n");
x.open("GET", url);
- x.setRequestHeader("Accept-Language", "en-US");
-
try {
var a = x.statusText;
} catch (e) {
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/text.expect b/tests/auto/qml/qqmlxmlhttprequest/data/text.expect
new file mode 100644
index 0000000000..2b4314273f
--- /dev/null
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/text.expect
@@ -0,0 +1,6 @@
+GET /text.xml HTTP/1.1
+host: {{ServerHostUrl}}
+connection: Keep-Alive{{Ignore}}
+accept-encoding: {{Ignore}}
+accept-language: {{Ignore}}
+user-agent: {{Ignore}}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
index 6517c58670..f908633193 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
+++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
@@ -75,6 +75,7 @@ private slots:
void statusText_data();
void responseText();
void responseText_data();
+ void responseURL();
void responseXML_invalid();
void invalidMethodUsage();
void redirects();
@@ -89,6 +90,8 @@ private slots:
void sendFileRequestNoRead();
#endif
+ void overrideMime();
+
// WebDAV
void sendPropfind();
void sendPropfind_data();
@@ -637,8 +640,6 @@ void tst_qqmlxmlhttprequest::send_options()
void tst_qqmlxmlhttprequest::send_options_data()
{
- if (QLocale::system() != QLocale(QLocale::English, QLocale::UnitedStates))
- QSKIP("Test is locale dependent");
QTest::addColumn<QString>("url_suffix");
QTest::addColumn<QString>("file_expected");
QTest::addColumn<QString>("file_qml");
@@ -863,8 +864,6 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders_args()
void tst_qqmlxmlhttprequest::getBinaryData()
{
- if (QLocale::system() != QLocale(QLocale::English, QLocale::UnitedStates))
- QSKIP("Test is locale dependent");
TestHTTPServer server;
QVERIFY2(server.listen(), qPrintable(server.errorString()));
QVERIFY(server.wait(testFileUrl("receive_binary_data.expect"),
@@ -877,15 +876,13 @@ void tst_qqmlxmlhttprequest::getBinaryData()
object->setProperty("url", server.urlString("/gml_logo.png"));
component.completeCreate();
- QFileInfo fileInfo("data/qml_logo.png");
+ const QFileInfo fileInfo(testFile("qml_logo.png"));
QTRY_COMPARE(object->property("readSize").toInt(), fileInfo.size());
QCOMPARE(object->property("status").toInt(), 200);
}
void tst_qqmlxmlhttprequest::getJsonData()
{
- if (QLocale::system() != QLocale(QLocale::English, QLocale::UnitedStates))
- QSKIP("Test is locale dependent");
TestHTTPServer server;
QVERIFY2(server.listen(), qPrintable(server.errorString()));
QVERIFY(server.wait(testFileUrl("receive_json_data.expect"),
@@ -1025,6 +1022,64 @@ void tst_qqmlxmlhttprequest::responseText_data()
QTest::newRow("Internal server error") << testFileUrl("status.500.reply") << testFileUrl("testdocument.html") << "QML Rocks!\n";
}
+
+void tst_qqmlxmlhttprequest::responseURL()
+{
+ // 200 OK
+ {
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ QVERIFY(server.wait(testFileUrl("status.expect"),
+ testFileUrl("status.200.reply"),
+ testFileUrl("testdocument.html")));
+
+ QQmlComponent component(engine.get(), testFileUrl("responseURL.qml"));
+ QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext()));
+ QVERIFY(!object.isNull());
+ object->setProperty("url", server.urlString("/testdocument.html"));
+ object->setProperty("expectedURL", server.urlString("/testdocument.html"));
+ component.completeCreate();
+
+ QTRY_VERIFY(object->property("dataOK").toBool());
+ }
+
+ // 200 OK with the exclude fragment flag set
+ {
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ QVERIFY(server.wait(testFileUrl("status.expect"),
+ testFileUrl("status.200.reply"),
+ testFileUrl("testdocument.html")));
+
+ QQmlComponent component(engine.get(), testFileUrl("responseURL.qml"));
+ QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext()));
+ QVERIFY(!object.isNull());
+ object->setProperty("url", server.urlString("/testdocument.html#fragment"));
+ object->setProperty("expectedURL", server.urlString("/testdocument.html"));
+ component.completeCreate();
+
+ QTRY_VERIFY(object->property("dataOK").toBool());
+ }
+
+ // 302 Found
+ {
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ server.addRedirect("redirect.html", server.urlString("/redirecttarget.html"));
+ server.serveDirectory(dataDirectory());
+
+ QQmlComponent component(engine.get(), testFileUrl("responseURL.qml"));
+ QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext()));
+ QVERIFY(!object.isNull());
+ object->setProperty("url", server.urlString("/redirect.html"));
+ object->setProperty("expectedURL", server.urlString("/redirecttarget.html"));
+ component.completeCreate();
+
+ QTRY_VERIFY(object->property("dataOK").toBool());
+ }
+}
+
+
void tst_qqmlxmlhttprequest::nonUtf8()
{
QFETCH(QString, fileName);
@@ -1076,7 +1131,7 @@ void tst_qqmlxmlhttprequest::doFileRequest(std::function<void(QObject *component
QTemporaryFile writeFile;
QTemporaryFile readFile;
- writeFile.open();
+ QVERIFY(writeFile.open());
writeFile.close();
QVERIFY(readFile.open());
@@ -1242,8 +1297,6 @@ void tst_qqmlxmlhttprequest::sendFileRequestNoRead() {
void tst_qqmlxmlhttprequest::sendPropfind()
{
- if (QLocale::system() != QLocale(QLocale::English, QLocale::UnitedStates))
- QSKIP("Test is locale dependent");
const QString prefix = "WebDAV//";
QFETCH(QString, qml);
@@ -1277,14 +1330,17 @@ void tst_qqmlxmlhttprequest::sendPropfind_data()
QTest::addColumn<QString>("replyHeader");
QTest::addColumn<QString>("replyBody");
- QTest::newRow("Send PROPFIND for file (bigbox, author, DingALing, Random properties). Get response with responseXML.")
+ QTest::newRow("Send PROPFIND for file (bigbox, author, DingALing, Random properties). "
+ "Get response with responseXML.")
<< "sendPropfind.responseXML.qml" << "/file" << "propfind.file.expect"
<< "propfind.file.reply.header" << "propfind.file.reply.body";
- QTest::newRow("Send PROPFIND for file (bigbox, author, DingALing, Random properties). Get response with response.")
+ QTest::newRow("Send PROPFIND for file (bigbox, author, DingALing, Random properties). "
+ "Get response with response.")
<< "sendPropfind.response.qml" << "/file" << "propfind.file.expect"
<< "propfind.file.reply.header" << "propfind.file.reply.body";
QTest::newRow("Send PROPFIND \"allprop\" request for collection.")
- << "sendPropfind.collection.allprop.qml" << "/container/" << "propfind.collection.allprop.expect"
+ << "sendPropfind.collection.allprop.qml" << "/container/"
+ << "propfind.collection.allprop.expect"
<< "propfind.file.reply.header" << "propfind.collection.allprop.reply.body";
}
@@ -1323,7 +1379,6 @@ void tst_qqmlxmlhttprequest::redirects()
QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext()));
QVERIFY(!object.isNull());
object->setProperty("url", server.urlString("/redirect.html"));
- object->setProperty("expectedText", "");
component.completeCreate();
QTRY_VERIFY(object->property("done").toBool());
@@ -1340,7 +1395,6 @@ void tst_qqmlxmlhttprequest::redirects()
QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext()));
QVERIFY(!object.isNull());
object->setProperty("url", server.urlString("/redirect.html"));
- object->setProperty("expectedText", "");
component.completeCreate();
QTRY_VERIFY(object->property("done").toBool());
@@ -1357,7 +1411,6 @@ void tst_qqmlxmlhttprequest::redirects()
QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext()));
QVERIFY(!object.isNull());
object->setProperty("url", server.urlString("/redirect.html"));
- object->setProperty("expectedText", "");
component.completeCreate();
for (int ii = 0; ii < 60; ++ii) {
@@ -1489,6 +1542,26 @@ void tst_qqmlxmlhttprequest::stateChangeCallingContext()
QTRY_VERIFY(object->property("success").toBool());
}
+void tst_qqmlxmlhttprequest::overrideMime()
+{
+ // overrideMimeType.reply sets the Content-Type to text/plain
+ // overrideMimeType.qml overrides it to text/xml and checks the responseXML property.
+
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ QVERIFY(server.wait(testFileUrl("text.expect"),
+ testFileUrl("overrideMimeType.reply"),
+ testFileUrl("text.xml")));
+
+ QQmlComponent component(engine.get(), testFileUrl("overrideMimeType.qml"));
+ QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext()));
+ QVERIFY(!object.isNull());
+ object->setProperty("url", server.urlString("/text.xml"));
+ component.completeCreate();
+
+ QTRY_VERIFY(object->property("dataOK").toBool());
+}
+
QTEST_MAIN(tst_qqmlxmlhttprequest)
#include "tst_qqmlxmlhttprequest.moc"
diff --git a/tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt b/tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt
index d0a8270faa..96ae64cd8d 100644
--- a/tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt
+++ b/tests/auto/qml/qqmlxmllistmodel/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlxmllistmodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
data/*)
diff --git a/tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp b/tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp
index 0745853e02..bb6e59cb17 100644
--- a/tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp
+++ b/tests/auto/qml/qqmlxmllistmodel/tst_qqmlxmllistmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQmlXmlListModel/private/qqmlxmllistmodel_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -321,14 +321,12 @@ void tst_QQmlXmlListModel::headers()
QTRY_COMPARE_WITH_TIMEOUT(qvariant_cast<QQmlXmlListModel::Status>(model->property("status")),
QQmlXmlListModel::Error, 10000);
- QVariantMap expectedHeaders;
- expectedHeaders["Accept"] = "application/xml,*/*";
+ QLatin1String expectedAcceptHeader = "application/xml,*/*"_L1;
- QCOMPARE(factory.lastSentHeaders.size(), expectedHeaders.size());
- for (auto it = expectedHeaders.cbegin(), end = expectedHeaders.cend(); it != end; ++it) {
- QVERIFY(factory.lastSentHeaders.contains(it.key()));
- QCOMPARE(factory.lastSentHeaders[it.key()].toString(), it.value().toString());
- }
+ QCOMPARE(factory.lastSentHeaders.size(), 1);
+ QVariant acceptHeader = factory.lastSentHeaders["accept"];
+ QVERIFY(acceptHeader.isValid());
+ QCOMPARE(acceptHeader.toString(), expectedAcceptHeader);
}
void tst_QQmlXmlListModel::source()
@@ -435,8 +433,8 @@ void tst_QQmlXmlListModel::reload()
QVERIFY(model != nullptr);
QTRY_COMPARE(model->rowCount(), 9);
- QSignalSpy spyInsert(model.get(), SIGNAL(rowsInserted(QModelIndex, int, int)));
- QSignalSpy spyRemove(model.get(), SIGNAL(rowsRemoved(QModelIndex, int, int)));
+ QSignalSpy spyInsert(model.get(), SIGNAL(rowsInserted(QModelIndex,int,int)));
+ QSignalSpy spyRemove(model.get(), SIGNAL(rowsRemoved(QModelIndex,int,int)));
QSignalSpy spyCount(model.get(), SIGNAL(countChanged()));
// reload multiple times to test the xml query aborting
QMetaObject::invokeMethod(model.get(), "reload");
diff --git a/tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt b/tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt
index 37bd47de3a..39abeacbdf 100644
--- a/tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt
+++ b/tests/auto/qml/qquickfolderlistmodel/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickfolderlistmodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickfolderlistmodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
index 6209e62a8f..f246e1af6d 100644
--- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
+++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/qml/qquickworkerscript/CMakeLists.txt b/tests/auto/qml/qquickworkerscript/CMakeLists.txt
index 9ba3446659..e96fc74300 100644
--- a/tests/auto/qml/qquickworkerscript/CMakeLists.txt
+++ b/tests/auto/qml/qquickworkerscript/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickworkerscript Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickworkerscript LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
index 966aac6aaf..e209c3cdde 100644
--- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
+++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtCore/qdebug.h>
#include <QtCore/qtimer.h>
@@ -102,21 +102,20 @@ void tst_QQuickWorkerScript::messaging()
QFETCH(QVariant, value);
QQmlComponent component(&m_engine, testFileUrl("worker.qml"));
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
- waitForEchoMessage(worker);
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value)));
+ waitForEchoMessage(worker.get());
const QMetaObject *mo = worker->metaObject();
- QVariant response = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>();
+ QVariant response = mo->property(mo->indexOfProperty("response")).read(worker.get()).value<QVariant>();
if (response.userType() == qMetaTypeId<QJSValue>())
response = response.value<QJSValue>().toVariant();
QCOMPARE(response, value);
qApp->processEvents();
- delete worker;
}
void tst_QQuickWorkerScript::messaging_data()
@@ -142,29 +141,28 @@ void tst_QQuickWorkerScript::messaging_sendQObjectList()
// js values.
QQmlComponent component(&m_engine, testFileUrl("worker.qml"));
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
QVariantList objects;
for (int i=0; i<3; i++)
objects << QVariant::fromValue(new QObject(this));
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(objects))));
- waitForEchoMessage(worker);
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, QVariant::fromValue(objects))));
+ waitForEchoMessage(worker.get());
const QMetaObject *mo = worker->metaObject();
- QVariantList result = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariantList>();
+ QVariantList result = mo->property(mo->indexOfProperty("response")).read(worker.get()).value<QVariantList>();
QCOMPARE(result, (QVariantList() << QVariant() << QVariant() << QVariant()));
qApp->processEvents();
- delete worker;
}
void tst_QQuickWorkerScript::messaging_sendJsObject()
{
QQmlComponent component(&m_engine, testFileUrl("worker.qml"));
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
// Properties are in alphabetical order to enable string-based comparison after
// QVariant roundtrip, since the properties will be stored in a QVariantMap.
@@ -175,26 +173,24 @@ void tst_QQuickWorkerScript::messaging_sendJsObject()
map.insert("name", "zyz");
map.insert("spell power", 3101);
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(map))));
- waitForEchoMessage(worker);
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, QVariant::fromValue(map))));
+ waitForEchoMessage(worker.get());
QVariant result = QVariant::fromValue(false);
- QVERIFY(QMetaObject::invokeMethod(worker, "compareLiteralResponse", Qt::DirectConnection,
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "compareLiteralResponse", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, jsObject)));
QVERIFY(result.toBool());
qApp->processEvents();
- delete worker;
}
void tst_QQuickWorkerScript::messaging_sendExternalObject()
{
QQmlComponent component(&m_engine, testFileUrl("externalObjectWorker.qml"));
- QObject *obj = component.create();
- QVERIFY(obj);
- QMetaObject::invokeMethod(obj, "testExternalObject");
+ std::unique_ptr<QObject> obj { component.create() };
+ QVERIFY(obj.get());
+ QMetaObject::invokeMethod(obj.get(), "testExternalObject");
QTest::qWait(100); // shouldn't crash.
- delete obj;
}
void tst_QQuickWorkerScript::script_with_pragma()
@@ -202,35 +198,33 @@ void tst_QQuickWorkerScript::script_with_pragma()
QVariant value(100);
QQmlComponent component(&m_engine, testFileUrl("worker_pragma.qml"));
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
- waitForEchoMessage(worker);
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value)));
+ waitForEchoMessage(worker.get());
const QMetaObject *mo = worker->metaObject();
- QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(), value);
+ QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.get()).value<QVariant>(), value);
qApp->processEvents();
- delete worker;
}
void tst_QQuickWorkerScript::script_included()
{
QQmlComponent component(&m_engine, testFileUrl("worker_include.qml"));
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
QString value("Hello");
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
- waitForEchoMessage(worker);
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value)));
+ waitForEchoMessage(worker.get());
const QMetaObject *mo = worker->metaObject();
- QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).toString(), value + " World");
+ QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.get()).toString(), value + " World");
qApp->processEvents();
- delete worker;
}
static QString qquickworkerscript_lastWarning;
@@ -245,69 +239,65 @@ void tst_QQuickWorkerScript::scriptError_onLoad()
QQmlComponent component(&m_engine, testFileUrl("worker_error_onLoad.qml"));
QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler);
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
QTRY_COMPARE(qquickworkerscript_lastWarning,
testFileUrl("script_error_onLoad.js").toString() + QLatin1String(":3:10: SyntaxError: Expected token `,'"));
qInstallMessageHandler(previousMsgHandler);
qApp->processEvents();
- delete worker;
}
void tst_QQuickWorkerScript::scriptError_onCall()
{
QQmlComponent component(&m_engine, testFileUrl("worker_error_onCall.qml"));
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler);
QVariant value;
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value)));
QTRY_COMPARE(qquickworkerscript_lastWarning,
testFileUrl("script_error_onCall.js").toString() + QLatin1String(":4: ReferenceError: getData is not defined"));
qInstallMessageHandler(previousMsgHandler);
qApp->processEvents();
- delete worker;
}
void tst_QQuickWorkerScript::script_function()
{
QQmlComponent component(&m_engine, testFileUrl("worker_function.qml"));
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
QString value("Hello");
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
- waitForEchoMessage(worker);
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value)));
+ waitForEchoMessage(worker.get());
const QMetaObject *mo = worker->metaObject();
- QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).toString(), value + " World");
+ QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.get()).toString(), value + " World");
qApp->processEvents();
- delete worker;
}
void tst_QQuickWorkerScript::script_var()
{
QQmlComponent component(&m_engine, testFileUrl("worker_var.qml"));
- QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create());
- QVERIFY(worker != nullptr);
+ std::unique_ptr<QQuickWorkerScript> worker { qobject_cast<QQuickWorkerScript*>(component.create()) };
+ QVERIFY(worker);
QString value("Hello");
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value)));
- waitForEchoMessage(worker);
+ QVERIFY(QMetaObject::invokeMethod(worker.get(), "testSend", Q_ARG(QVariant, value)));
+ waitForEchoMessage(worker.get());
const QMetaObject *mo = worker->metaObject();
- QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).toString(), value + " World");
+ QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.get()).toString(), value + " World");
qApp->processEvents();
- delete worker;
}
// Rapidly create and destroy worker scripts to test resources are being disposed
@@ -317,9 +307,8 @@ void tst_QQuickWorkerScript::stressDispose()
for (int ii = 0; ii < 100; ++ii) {
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("stressDispose.qml"));
- QObject *o = component.create();
- QVERIFY(o);
- delete o;
+ std::unique_ptr<QObject> o { component.create() };
+ QVERIFY(o.get());
}
}
diff --git a/tests/auto/qml/qrcqml/CMakeLists.txt b/tests/auto/qml/qrcqml/CMakeLists.txt
index de081545cb..c793f1363c 100644
--- a/tests/auto/qml/qrcqml/CMakeLists.txt
+++ b/tests/auto/qml/qrcqml/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qrcqml Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qrcqml LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qrcqml
SOURCES
tst_qrcqml.cpp
diff --git a/tests/auto/qml/qrcqml/tst_qrcqml.cpp b/tests/auto/qml/qrcqml/tst_qrcqml.cpp
index 5f77083c4e..a9be307c23 100644
--- a/tests/auto/qml/qrcqml/tst_qrcqml.cpp
+++ b/tests/auto/qml/qrcqml/tst_qrcqml.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QObject>
@@ -59,10 +59,9 @@ void tst_qrcqml::basicLoad()
QQmlEngine e;
QQmlComponent c(&e, QUrl(url));
QVERIFY(c.isReady());
- QObject* o = c.create();
- QVERIFY(o);
+ std::unique_ptr<QObject> o { c.create() };
+ QVERIFY(o.get());
QCOMPARE(o->property("tokenProperty").toString(), token);
- delete o;
}
void tst_qrcqml::qrcImport_data()
@@ -88,10 +87,9 @@ void tst_qrcqml::qrcImport()
e.addImportPath(importPath);
QQmlComponent c(&e, QUrl("qrc:///importtest.qml"));
QVERIFY(c.isReady());
- QObject *o = c.create();
- QVERIFY(o);
+ std::unique_ptr<QObject> o { c.create() };
+ QVERIFY(o.get());
QCOMPARE(o->property("tokenProperty").toString(), token);
- delete o;
}
QTEST_MAIN(tst_qrcqml)
diff --git a/tests/auto/qml/qtqmlmodules/CMakeLists.txt b/tests/auto/qml/qtqmlmodules/CMakeLists.txt
index 59033ae690..a6ae2ec9f7 100644
--- a/tests/auto/qml/qtqmlmodules/CMakeLists.txt
+++ b/tests/auto/qml/qtqmlmodules/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qtqmlmodules Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qtqmlmodules LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp b/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp
index 88fd0e6bc0..62637c97ff 100644
--- a/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp
+++ b/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research in Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
@@ -23,33 +23,27 @@ void tst_qtqmlmodules::baseTypes()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("base.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QVERIFY(object->property("success").toBool());
-
- delete object;
}
void tst_qtqmlmodules::modelsTypes()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("models.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QVERIFY(object->property("success").toBool());
-
- delete object;
}
void tst_qtqmlmodules::unavailableTypes()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("unavailable.qml"));
- QObject *object = component.create();
- QVERIFY(object != nullptr);
+ std::unique_ptr<QObject> object { component.create() };
+ QVERIFY(object);
QVERIFY(object->property("success").toBool());
-
- delete object;
}
QTEST_MAIN(tst_qtqmlmodules)
diff --git a/tests/auto/qml/qv4assembler/CMakeLists.txt b/tests/auto/qml/qv4assembler/CMakeLists.txt
index dfac16468a..8ae3c891d5 100644
--- a/tests/auto/qml/qv4assembler/CMakeLists.txt
+++ b/tests/auto/qml/qv4assembler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qv4assembler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4assembler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qv4assembler/data/crash.qml b/tests/auto/qml/qv4assembler/data/crash.qml
index ffae69b321..b083497a70 100644
--- a/tests/auto/qml/qv4assembler/data/crash.qml
+++ b/tests/auto/qml/qv4assembler/data/crash.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.2
import Crash 1.0
diff --git a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
index 7e9e070fa6..4be0acd73e 100644
--- a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
+++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#if QT_CONFIG(process)
diff --git a/tests/auto/qml/qv4estable/CMakeLists.txt b/tests/auto/qml/qv4estable/CMakeLists.txt
new file mode 100644
index 0000000000..01d2663a04
--- /dev/null
+++ b/tests/auto/qml/qv4estable/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qv4estable Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4estable LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qv4estable
+ SOURCES
+ tst_qv4estable.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::QmlPrivate
+)
+
+## Scopes:
+#####################################################################
diff --git a/tests/auto/qml/qv4estable/tst_qv4estable.cpp b/tests/auto/qml/qv4estable/tst_qv4estable.cpp
new file mode 100644
index 0000000000..45df62b23e
--- /dev/null
+++ b/tests/auto/qml/qv4estable/tst_qv4estable.cpp
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <qtest.h>
+#include <private/qv4estable_p.h>
+
+class tst_qv4estable : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void checkRemoveAvoidsHeapBufferOverflow();
+};
+
+// QTBUG-123999
+void tst_qv4estable::checkRemoveAvoidsHeapBufferOverflow()
+{
+ QV4::ESTable estable;
+
+ // Fill the ESTable with values so it is at max capacity.
+ QCOMPARE_EQ(estable.m_capacity, 8);
+ for (uint i = 0; i < estable.m_capacity; ++i) {
+ estable.set(QV4::Value::fromUInt32(i), QV4::Value::fromUInt32(i));
+ }
+ // Our |m_keys| array should now contain eight values.
+ // > [v0, v1, v2, v3, v4, v5, v6, v7]
+ for (uint i = 0; i < estable.m_capacity; ++i) {
+ QVERIFY(estable.m_keys[i].sameValueZero(QV4::Value::fromUInt32(i)));
+ }
+ QCOMPARE_EQ(estable.m_capacity, 8);
+ QCOMPARE_EQ(estable.m_size, 8);
+
+ // Remove the first item from the set to verify that asan does not trip.
+ // Relies on the CI platform propagating asan flag to all tests.
+ estable.remove(QV4::Value::fromUInt32(0));
+}
+
+QTEST_MAIN(tst_qv4estable)
+
+#include "tst_qv4estable.moc"
diff --git a/tests/auto/qml/qv4identifiertable/CMakeLists.txt b/tests/auto/qml/qv4identifiertable/CMakeLists.txt
index 5c785b0b74..c0350d6ef7 100644
--- a/tests/auto/qml/qv4identifiertable/CMakeLists.txt
+++ b/tests/auto/qml/qv4identifiertable/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qv4identifiertable Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4identifiertable LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qv4identifiertable
SOURCES
tst_qv4identifiertable.cpp
diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
index a3f5c4bb70..6222f20c10 100644
--- a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
+++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
@@ -1,6 +1,6 @@
// Copyright (C) 2019 The Qt Company Ltd.
// Copyright (C) 2016 basysKom GmbH.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
diff --git a/tests/auto/qml/qv4mm/CMakeLists.txt b/tests/auto/qml/qv4mm/CMakeLists.txt
index bfafd5819c..7c8a52038e 100644
--- a/tests/auto/qml/qv4mm/CMakeLists.txt
+++ b/tests/auto/qml/qv4mm/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qv4mm Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4mm LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/qml/qv4mm/data/createdestroy.qml b/tests/auto/qml/qv4mm/data/createdestroy.qml
index fd8a8cb2a2..5cc689b1ea 100644
--- a/tests/auto/qml/qv4mm/data/createdestroy.qml
+++ b/tests/auto/qml/qv4mm/data/createdestroy.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.2
diff --git a/tests/auto/qml/qv4mm/data/createobjects.qml b/tests/auto/qml/qv4mm/data/createobjects.qml
index 4163d34d34..bd1897e04e 100644
--- a/tests/auto/qml/qv4mm/data/createobjects.qml
+++ b/tests/auto/qml/qv4mm/data/createobjects.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.2
diff --git a/tests/auto/qml/qv4mm/data/simpleObject.qml b/tests/auto/qml/qv4mm/data/simpleObject.qml
new file mode 100644
index 0000000000..8fc36a40da
--- /dev/null
+++ b/tests/auto/qml/qv4mm/data/simpleObject.qml
@@ -0,0 +1,3 @@
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
index e5f8951825..5bcdcd4624 100644
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 basysKom GmbH.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
@@ -9,6 +9,10 @@
#include <private/qv4mm_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qjsvalue_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4arraydata_p.h>
+#include <private/qqmlcomponentattached_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -23,22 +27,151 @@ public:
private slots:
void gcStats();
+ void arrayDataWriteBarrierInteraction();
+ void persistentValueMarking_data();
+ void persistentValueMarking();
void multiWrappedQObjects();
void accessParentOnDestruction();
void cleanInternalClasses();
void createObjectsOnDestruction();
+ void sharedInternalClassDataMarking();
+ void gcTriggeredInOnDestroyed();
+ void weakValuesAssignedAfterThePhaseThatShouldHandleWeakValues();
};
tst_qv4mm::tst_qv4mm()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
+ QV4::ExecutionEngine engine;
+ QV4::Scope scope(engine.rootContext());
}
void tst_qv4mm::gcStats()
{
QLoggingCategory::setFilterRules("qt.qml.gc.*=true");
QQmlEngine engine;
- engine.collectGarbage();
+ gc(engine);
+ QLoggingCategory::setFilterRules("qt.qml.gc.*=false");
+}
+
+void tst_qv4mm::arrayDataWriteBarrierInteraction()
+{
+ QV4::ExecutionEngine engine;
+ QCOMPARE(engine.memoryManager->gcBlocked, QV4::MemoryManager::Unblocked);
+ engine.memoryManager->gcBlocked = QV4::MemoryManager::InCriticalSection;
+ QV4::Heap::Object *unprotectedObject = engine.newObject();
+ QV4::Scope scope(&engine);
+ QV4::ScopedArrayObject array(scope, engine.newArrayObject());
+ constexpr int initialCapacity = 8; // compare qv4arraydata.cpp
+ for (int i = 0; i < initialCapacity; ++i) {
+ array->push_back(unprotectedObject->asReturnedValue());
+ }
+ QVERIFY(!unprotectedObject->isMarked());
+ engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked;
+
+ // initialize gc
+ auto sm = engine.memoryManager->gcStateMachine.get();
+ sm->reset();
+ while (sm->state != QV4::GCState::MarkGlobalObject) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+
+ array->push_back(QV4::Value::fromUInt32(42));
+ QVERIFY(!unprotectedObject->isMarked());
+ // we should have pushed the new arraydata on the mark stack
+ // so if we call drain...
+ engine.memoryManager->markStack()->drain();
+ // the unprotectedObject should have been marked
+ QVERIFY(unprotectedObject->isMarked());
+}
+
+enum PVSetOption {
+ CopyCtor,
+ ValueCtor,
+ ObjectCtor,
+ ReturnedValueCtor,
+ WeakValueAssign,
+ ObjectAssign,
+};
+
+void tst_qv4mm::persistentValueMarking_data()
+{
+ QTest::addColumn<PVSetOption>("setOption");
+
+ QTest::addRow("copy") << CopyCtor;
+ QTest::addRow("valueCtor") << ValueCtor;
+ QTest::addRow("ObjectCtor") << ObjectCtor;
+ QTest::addRow("ReturnedValueCtor") << ReturnedValueCtor;
+ QTest::addRow("WeakValueAssign") << WeakValueAssign;
+ QTest::addRow("ObjectAssign") << ObjectAssign;
+}
+
+void tst_qv4mm::persistentValueMarking()
+{
+ QFETCH(PVSetOption, setOption);
+ QV4::ExecutionEngine engine;
+ QV4::PersistentValue persistentOrigin; // used for copy ctor
+ QV4::Heap::Object *unprotectedObject = engine.newObject();
+ {
+ QV4::Scope scope(engine.rootContext());
+ QV4::ScopedObject object {scope, unprotectedObject};
+ persistentOrigin.set(&engine, object);
+ QVERIFY(!unprotectedObject->isMarked());
+ }
+ auto sm = engine.memoryManager->gcStateMachine.get();
+ sm->reset();
+ while (sm->state != QV4::GCState::MarkGlobalObject) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ QVERIFY(engine.isGCOngoing);
+ QVERIFY(!unprotectedObject->isMarked());
+ switch (setOption) {
+ case CopyCtor: {
+ QV4::PersistentValue persistentCopy(persistentOrigin);
+ QVERIFY(unprotectedObject->isMarked());
+ break;
+ }
+ case ValueCtor: {
+ QV4::Value val = QV4::Value::fromHeapObject(unprotectedObject);
+ QV4::PersistentValue persistent(&engine, val);
+ QVERIFY(unprotectedObject->isMarked());
+ break;
+ }
+ case ObjectCtor: {
+ QV4::Scope scope(&engine);
+ QV4::ScopedObject o(scope, unprotectedObject);
+ // scoped object without scan shouldn't result in marking
+ QVERIFY(!unprotectedObject->isMarked());
+ QV4::PersistentValue persistent(&engine, o.getPointer());
+ QVERIFY(unprotectedObject->isMarked());
+ break;
+ }
+ case ReturnedValueCtor: {
+ QV4::PersistentValue persistent(&engine, unprotectedObject->asReturnedValue());
+ QVERIFY(unprotectedObject->isMarked());
+ break;
+ }
+ case WeakValueAssign: {
+ QV4::WeakValue wv;
+ wv.set(&engine, unprotectedObject);
+ QVERIFY(!unprotectedObject->isMarked());
+ QV4::PersistentValue persistent;
+ persistent = wv;
+ break;
+ }
+ case ObjectAssign: {
+ QV4::Scope scope(&engine);
+ QV4::ScopedObject o(scope, unprotectedObject);
+ // scoped object without scan shouldn't result in marking
+ QVERIFY(!unprotectedObject->isMarked());
+ QV4::PersistentValue persistent;
+ persistent = o;
+ QVERIFY(unprotectedObject->isMarked());
+ break;
+ }
+ }
}
void tst_qv4mm::multiWrappedQObjects()
@@ -48,7 +181,7 @@ void tst_qv4mm::multiWrappedQObjects()
{
QObject object;
for (int i = 0; i < 10; ++i)
- QV4::QObjectWrapper::wrap(i % 2 ? &engine1 : &engine2, &object);
+ QV4::QObjectWrapper::ensureWrapper(i % 2 ? &engine1 : &engine2, &object);
QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0);
QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0);
@@ -62,27 +195,27 @@ void tst_qv4mm::multiWrappedQObjects()
// The additional WeakValue from m_multiplyWrappedQObjects hasn't been moved
// to m_pendingFreedObjectWrapperValue yet. It's still alive after all.
- engine1.memoryManager->runGC();
+ gc(engine1);
QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1);
// engine2 doesn't own the object as engine1 was the first to wrap it above.
// Therefore, no effect here.
- engine2.memoryManager->runGC();
+ gc(engine2);
QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0);
}
// Clears m_pendingFreedObjectWrapperValue. Now it's really dead.
- engine1.memoryManager->runGC();
+ gc(engine1);
QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0);
- engine2.memoryManager->runGC();
+ gc(engine2);
QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0);
}
void tst_qv4mm::accessParentOnDestruction()
{
- QLoggingCategory::setFilterRules("qt.qml.gc.*=false");
QQmlEngine engine;
+
QQmlComponent component(&engine, testFileUrl("createdestroy.qml"));
std::unique_ptr<QObject> obj(component.create());
QVERIFY(obj);
@@ -91,6 +224,9 @@ void tst_qv4mm::accessParentOnDestruction()
QTRY_VERIFY(!timer->property("running").toBool());
QCOMPARE(obj->property("iterations").toInt(), 100);
QCOMPARE(obj->property("creations").toInt(), 100);
+ gc(engine); // ensure incremental gc has finished, and collected all objects
+ // TODO: investigaet whether we really need two gc rounds for incremental gc
+ gc(engine); // ensure incremental gc has finished, and collected all objects
QCOMPARE(obj->property("destructions").toInt(), 100);
}
@@ -181,7 +317,11 @@ void tst_qv4mm::cleanInternalClasses()
}
// Make sure that all dangling ICs are actually gone.
- scope.engine->memoryManager->runGC();
+ gc(engine);
+ // NOTE: If we allocate new ICs during gc (potentially triggered on alloc),
+ // then they will survive the previous gc call
+ // run gc again to ensure that a full gc cycle happens
+ gc(engine);
// Now the GC has removed the ICs we originally added by adding properties.
QVERIFY(prevIC->d()->transitions.empty() || prevIC->d()->transitions.front().lookup == nullptr);
@@ -197,8 +337,8 @@ void tst_qv4mm::cleanInternalClasses()
void tst_qv4mm::createObjectsOnDestruction()
{
- QLoggingCategory::setFilterRules("qt.qml.gc.*=false");
QQmlEngine engine;
+
QQmlComponent component(&engine, testFileUrl("createobjects.qml"));
std::unique_ptr<QObject> obj(component.create());
QVERIFY(obj);
@@ -206,6 +346,162 @@ void tst_qv4mm::createObjectsOnDestruction()
QCOMPARE(obj->property("ok").toBool(), true);
}
+void tst_qv4mm::sharedInternalClassDataMarking()
+{
+ QV4::ExecutionEngine engine;
+ QV4::Scope scope(engine.rootContext());
+ QV4::ScopedObject object(scope, engine.newObject());
+ QVERIFY(!engine.memoryManager->gcBlocked);
+ // no scoped classes, as that would defeat the point of the test
+ // we block the gc instead so that the allocation can't trigger the gc
+ engine.memoryManager->gcBlocked = QV4::MemoryManager::InCriticalSection;
+ QV4::Heap::String *s = engine.newString(QString::fromLatin1("test"));
+ QV4::PropertyKey id = engine.identifierTable->asPropertyKeyImpl(s);
+ engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked;
+ QVERIFY(!id.asStringOrSymbol()->isMarked());
+
+ auto sm = engine.memoryManager->gcStateMachine.get();
+ sm->reset();
+ while (sm->state != QV4::GCState::MarkGlobalObject) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+
+ // simulate partial marking caused by drain due mark stack running out of space
+ // and running out of time during drain phase for complete marking
+ // the last part is necessary for us to find not-already marked name/value pair to put into
+ // the object
+
+ QVERIFY(engine.memoryManager->markStack()->isEmpty());
+ QVERIFY(!id.asStringOrSymbol()->isMarked());
+ {
+
+ // for simplcity's sake we create a new PropertyKey - if gc were actually ongoing that would
+ // already mark it. In practice we would need to retrieve an existing one from an unmarked
+ // object, and then make that object unreachable afterwards.
+ object->put(id, QV4::Value::fromUInt32(42));
+ engine.memoryManager->markStack()->drain();
+ QVERIFY(id.asStringOrSymbol()->isMarked());
+ }
+ gc(engine);
+ // sanity check that we still can lookup the value
+ QV4::ScopedString s2(scope, engine.newString(QString::fromLatin1("test")));
+ auto val = QV4::Value::fromReturnedValue(object->get(s2->toPropertyKey()));
+ QCOMPARE(val.toUInt32(), 42u);
+}
+
+void tst_qv4mm::gcTriggeredInOnDestroyed()
+{
+ QQmlEngine engine;
+ QV4::ExecutionEngine &v4 = *engine.handle();
+
+ QPointer<QObject> testObject = new QObject; // unparented, will be deleted
+ auto cleanup = qScopeGuard([&]() {
+ if (testObject)
+ testObject->deleteLater();
+ });
+
+ QQmlComponent component(&engine, testFileUrl("simpleObject.qml"));
+ auto toBeCollected = component.create();
+ QVERIFY(toBeCollected);
+ QJSEngine::setObjectOwnership(toBeCollected, QJSEngine::JavaScriptOwnership);
+ QV4::QObjectWrapper::ensureWrapper(&v4, toBeCollected);
+ QVERIFY(qmlEngine(toBeCollected));
+ QQmlComponentAttached *attached = QQmlComponent::qmlAttachedProperties(toBeCollected);
+ QVERIFY(attached);
+
+
+ QV4::Scope scope(v4.rootContext());
+ QCOMPARE(v4.memoryManager->gcBlocked, QV4::MemoryManager::Unblocked);
+
+
+
+ // let the gc run up to CallDestroyObjects
+ auto sm = v4.memoryManager->gcStateMachine.get();
+ sm->reset();
+ v4.memoryManager->gcBlocked = QV4::MemoryManager::NormalBlocked;
+ while (sm->state != QV4::GCState::CallDestroyObjects && sm->state != QV4::GCState::Invalid) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ QCOMPARE(sm->state, QV4::GCState::CallDestroyObjects);
+
+ QV4::ScopedValue val(scope);
+ bool calledOnDestroyed = false;
+ auto con = connect(attached, &QQmlComponentAttached::destruction, this, [&]() {
+ calledOnDestroyed = true;
+ // we trigger uncommon code paths:
+ // create ObjectWrapper in destroyed hadnler
+ auto ddata = QQmlData::get(testObject.get(), false);
+ QVERIFY(!ddata); // we don't have ddata yet (otherwise we'd already have an object wrapper)
+ val = QV4::QObjectWrapper::wrap(&v4, testObject.get());
+ QJSEngine::setObjectOwnership(testObject, QJSEngine::JavaScriptOwnership);
+
+ // and also try to trigger a force gc completion
+ bool gcComplete = v4.memoryManager->tryForceGCCompletion();
+ QVERIFY(!gcComplete);
+ });
+ while (!calledOnDestroyed && sm->state != QV4::GCState::Invalid) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ QVERIFY(!QTest::currentTestFailed());
+ QObject::disconnect(con);
+ QVERIFY(calledOnDestroyed);
+
+ bool gcComplete = v4.memoryManager->tryForceGCCompletion();
+ QVERIFY(gcComplete);
+ val = QV4::Value::undefinedValue(); // no longer keep a reference on the stack
+ QCOMPARE(sm->state, QV4::GCState::Invalid);
+ QVERIFY(testObject); // must not have be deleted, referenced by val
+
+ gc(v4); // run another gc cycle
+ QVERIFY(!testObject); // now collcted by gc
+}
+void tst_qv4mm::weakValuesAssignedAfterThePhaseThatShouldHandleWeakValues()
+{
+ QObject testObject;
+ QV4::ExecutionEngine v4;
+
+ QCOMPARE(v4.memoryManager->gcBlocked, QV4::MemoryManager::Unblocked);
+
+
+
+ // let the gc run up to CallDestroyObjects
+ auto sm = v4.memoryManager->gcStateMachine.get();
+ sm->reset();
+ v4.memoryManager->gcBlocked = QV4::MemoryManager::NormalBlocked;
+
+
+ // run just before the sweeping face
+ while (sm->state != QV4::GCState::DoSweep && sm->state != QV4::GCState::Invalid) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ QCOMPARE(sm->state, QV4::GCState::DoSweep);
+
+ {
+ // simulate code accessing the object wrapper for an object
+ QV4::Scope scope(v4.rootContext());
+ QV4::ScopedValue value(scope);
+ value = QV4::QObjectWrapper::wrap(&v4, &testObject);
+ // let it go out of scope before any stack re-scanning could happen
+ }
+
+ bool gcComplete = v4.memoryManager->tryForceGCCompletion();
+ QVERIFY(gcComplete);
+
+ auto ddata = QQmlData::get(&testObject);
+ QVERIFY(ddata);
+ if (ddata->jsWrapper.isUndefined()) {
+ // it's in principle valid for the wrapper to be reset, though the current
+ // implementation doesn't do it, and it requires some care
+ qWarning("Double-check the handling of weak values and object wrappers in the gc");
+ return;
+ }
+ QVERIFY(ddata->jsWrapper.valueRef()->heapObject()->inUse());
+}
+
QTEST_MAIN(tst_qv4mm)
#include "tst_qv4mm.moc"
diff --git a/tests/auto/qml/qv4regexp/CMakeLists.txt b/tests/auto/qml/qv4regexp/CMakeLists.txt
index bdaf5e52e4..6f2c47c4b2 100644
--- a/tests/auto/qml/qv4regexp/CMakeLists.txt
+++ b/tests/auto/qml/qv4regexp/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qv4regexp Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4regexp LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qv4regexp
SOURCES
tst_qv4regexp.cpp
diff --git a/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp b/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp
index 534fa53956..99a91a9b0f 100644
--- a/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp
+++ b/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qjsengine.h>
diff --git a/tests/auto/qml/qv4urlobject/CMakeLists.txt b/tests/auto/qml/qv4urlobject/CMakeLists.txt
new file mode 100644
index 0000000000..f255413d84
--- /dev/null
+++ b/tests/auto/qml/qv4urlobject/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Generated from qv4urlobject.pro.
+
+#####################################################################
+## tst_qv4urlobject Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4urlobject LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qv4urlobject
+ SOURCES
+ tst_qv4urlobject.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::QmlPrivate
+)
+
+## Scopes:
+#####################################################################
diff --git a/tests/auto/qml/qv4urlobject/tst_qv4urlobject.cpp b/tests/auto/qml/qv4urlobject/tst_qv4urlobject.cpp
new file mode 100644
index 0000000000..992d1ef808
--- /dev/null
+++ b/tests/auto/qml/qv4urlobject/tst_qv4urlobject.cpp
@@ -0,0 +1,153 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <qtest.h>
+#include <QtQml/qjsengine.h>
+
+class tst_urlobject : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void searchParams_set();
+ void searchParams_nullUrlPointer();
+ void urlObject_search();
+ void urlObject_search_data();
+ void urlObject_href();
+ void urlObject_href_data();
+};
+
+void tst_urlobject::searchParams_set()
+{
+ QJSEngine engine;
+ QJSValue result =
+ engine.evaluate(QLatin1String("var url = new URL(\"http://www.google.com/search\");"
+ "url.href"));
+ QVERIFY(!result.isError());
+ QCOMPARE(result.toString(), "http://www.google.com/search");
+
+ result = engine.evaluate(QLatin1String("url.toString()"));
+ QVERIFY(!result.isError());
+ QCOMPARE(result.toString(), "http://www.google.com/search");
+
+ result = engine.evaluate(QLatin1String("url.searchParams.set(\"q\", \"value\");"
+ "url.href;"));
+ QVERIFY(!result.isError());
+ QCOMPARE(result.toString(), "http://www.google.com/search?q=value");
+
+ result = engine.evaluate(QLatin1String("url.toString()"));
+ QVERIFY(!result.isError());
+ QCOMPARE(result.toString(), "http://www.google.com/search?q=value");
+
+ result = engine.evaluate(QLatin1String("url.searchParams.set(\"t\", \"otherValue\");"
+ "url.href;"));
+ QVERIFY(!result.isError());
+ QCOMPARE(result.toString(), "http://www.google.com/search?q=value&t=otherValue");
+
+ result = engine.evaluate(QLatin1String("url.toString()"));
+ QVERIFY(!result.isError());
+ QCOMPARE(result.toString(), "http://www.google.com/search?q=value&t=otherValue");
+}
+
+void tst_urlobject::searchParams_nullUrlPointer()
+{
+ QJSEngine engine;
+ QJSValue result = engine.evaluate(QLatin1String("let params = new URLSearchParams();"
+ "params.set(\"foo\", \"bar\");"));
+ QVERIFY(!result.isError());
+}
+
+void tst_urlobject::urlObject_search()
+{
+ QFETCH(QString, test);
+ QFETCH(QString, expected);
+
+ QJSEngine engine;
+
+ QCOMPARE(engine.evaluate(test).toString(), expected);
+}
+
+void tst_urlobject::urlObject_search_data()
+{
+ QTest::addColumn<QString>("test");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("base case")
+ << "var url = new URL(\"http://www.google.com/search?q=123\");"
+ "url.search"
+ << "?q=123";
+ QTest::newRow("space")
+ << "var url = new URL(\"http://www.google.com/search?a=b ~\");"
+ "url.search"
+ << "?a=b%20~";
+ QTest::newRow("empty search")
+ << "var url = new URL(\"http://www.google.com/search?\");"
+ "url.search"
+ << "";
+ QTest::newRow("no search")
+ << "var url = new URL(\"http://www.google.com/search\");"
+ "url.search"
+ << "";
+ QTest::newRow("Question mark")
+ // the embedded ""'s break trigraph sequences:
+ << "var url = new URL(\"http://www.google.com/search?""?=?\");"
+ "url.search"
+ << "?""?=?";
+ QTest::newRow("equal sign")
+ << "var url = new URL(\"http://www.google.com/search?a==&b=!\");"
+ "url.search"
+ << "?a==&b=!";
+ QTest::newRow("percent sign")
+ << "var url = new URL(\"http://www.google.com/search?a=%20\");"
+ "url.search"
+ << "?a=%20";
+ QTest::newRow("multiple key-value pairs")
+ << "var url = new URL(\"http://www.google.com/search?a=b&c=d\");"
+ "url.search"
+ << "?a=b&c=d";
+ QTest::newRow("unreserved")
+ << "var url = new URL(\"http://www.google.com/search?a=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~\");"
+ "url.search"
+ << "?a=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~";
+ QTest::newRow("reserved + illegal")
+ << "var url = new URL(\"http://google.com/search/?a=!*();:@&=+$,/?#[]\");"
+ "url.search"
+ << "?a=!*();:@&=+$,/?";
+ QTest::newRow("unicode (U+327D)")
+ << "var url = new URL(\"http://google.com/search/?a=㉽\");"
+ "url.search"
+ << "?a=%E3%89%BD";
+ QTest::newRow("backslash")
+ // The JS string in the C++ source ends in 4 backslashes.
+ // The C++ compiler turns that into 2 backslashes.
+ // QV4 receives source code containing a string literal ending in two backslashes.
+ // The resulting JS string ends in a single backslash.
+ << "var url = new URL('http://google.com/search/?q=\\\\');"
+ "url.search"
+ << "?q=\\";
+}
+
+void tst_urlobject::urlObject_href()
+{
+ QFETCH(QString, test);
+ QFETCH(QString, expected);
+
+ QJSEngine engine;
+
+ QCOMPARE(engine.evaluate(test).toString(), expected);
+}
+
+void tst_urlobject::urlObject_href_data()
+{
+ QTest::addColumn<QString>("test");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("QTBUG-110454")
+ << "var url = new URL(\"https://example.com/?a=b ~\");"
+ "url.href"
+ << "https://example.com/?a=b%20~";
+}
+
+QTEST_MAIN(tst_urlobject)
+
+#include "tst_qv4urlobject.moc"
diff --git a/tests/auto/qml/qwidgetsinqml/CMakeLists.txt b/tests/auto/qml/qwidgetsinqml/CMakeLists.txt
index 4daa98889a..d2374997f2 100644
--- a/tests/auto/qml/qwidgetsinqml/CMakeLists.txt
+++ b/tests/auto/qml/qwidgetsinqml/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qwidgetsinqml Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qwidgetsinqml LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qwidgetsinqml
SOURCES
tst_qwidgetsinqml.cpp
@@ -15,6 +21,8 @@ qt_internal_add_test(tst_qwidgetsinqml
Qt::GuiPrivate
Qt::Qml
Qt::Widgets
+ Qt::QuickTestUtilsPrivate
+ TESTDATA "dummy_imports.qml"
)
## Scopes:
diff --git a/tests/auto/qml/qwidgetsinqml/dummy_imports.qml b/tests/auto/qml/qwidgetsinqml/dummy_imports.qml
new file mode 100644
index 0000000000..afe2b33adf
--- /dev/null
+++ b/tests/auto/qml/qwidgetsinqml/dummy_imports.qml
@@ -0,0 +1,7 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++.
+
+import QtQml
+
+QtObject { }
diff --git a/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp
index 15236c20d8..12058fbba2 100644
--- a/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp
+++ b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp
@@ -1,10 +1,11 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QQmlEngine>
#include <QtQml>
#include <QWidget>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
class tst_QWidgetsInQml : public QObject
{
@@ -20,13 +21,6 @@ private slots:
void widgetAsDefaultPropertyKeptDuringCreation();
};
-static void gc(QQmlEngine &engine)
-{
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
-}
-
// Like QtObject, but with default property
class QObjectContainer : public QObject
{
@@ -40,7 +34,8 @@ public:
{}
QQmlListProperty<QObject> data() {
- return QQmlListProperty<QObject>(this, 0, children_append, children_count, children_at, children_clear);
+ return QQmlListProperty<QObject>(
+ this, nullptr, children_append, children_count, children_at, children_clear);
}
static void children_append(QQmlListProperty<QObject> *prop, QObject *o)
@@ -58,12 +53,12 @@ public:
}
}
- static int children_count(QQmlListProperty<QObject> *prop)
+ static qsizetype children_count(QQmlListProperty<QObject> *prop)
{
return static_cast<QObjectContainer*>(prop->object)->dataChildren.count();
}
- static QObject *children_at(QQmlListProperty<QObject> *prop, int index)
+ static QObject *children_at(QQmlListProperty<QObject> *prop, qsizetype index)
{
return static_cast<QObjectContainer*>(prop->object)->dataChildren.at(index);
}
@@ -71,7 +66,7 @@ public:
static void children_clear(QQmlListProperty<QObject> *prop)
{
QObjectContainer *that = static_cast<QObjectContainer*>(prop->object);
- foreach (QObject *c, that->dataChildren)
+ for (QObject *c : std::as_const(that->dataChildren))
QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*)));
that->dataChildren.clear();
}
diff --git a/tests/auto/qml/registrationmacros/CMakeLists.txt b/tests/auto/qml/registrationmacros/CMakeLists.txt
index 3f2b7411ac..4cae044adf 100644
--- a/tests/auto/qml/registrationmacros/CMakeLists.txt
+++ b/tests/auto/qml/registrationmacros/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_registrationmacros LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_registrationmacros
SOURCES
tst_registrationmacros.cpp
diff --git a/tests/auto/qml/registrationmacros/tst_registrationmacros.cpp b/tests/auto/qml/registrationmacros/tst_registrationmacros.cpp
index 77798af24f..a94e045277 100644
--- a/tests/auto/qml/registrationmacros/tst_registrationmacros.cpp
+++ b/tests/auto/qml/registrationmacros/tst_registrationmacros.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QObject>
diff --git a/tests/auto/qml/registrationmacros/types.cpp b/tests/auto/qml/registrationmacros/types.cpp
index 4ca801e7f7..57d565c525 100644
--- a/tests/auto/qml/registrationmacros/types.cpp
+++ b/tests/auto/qml/registrationmacros/types.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "types.h"
bool Test::check() const
diff --git a/tests/auto/qml/registrationmacros/types.h b/tests/auto/qml/registrationmacros/types.h
index 3c365cc2fd..5579edd2d3 100644
--- a/tests/auto/qml/registrationmacros/types.h
+++ b/tests/auto/qml/registrationmacros/types.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TYPES_H
#define TYPES_H
diff --git a/tests/auto/qml/v4misc/CMakeLists.txt b/tests/auto/qml/v4misc/CMakeLists.txt
index 81ae948cf1..1390fe9e0f 100644
--- a/tests/auto/qml/v4misc/CMakeLists.txt
+++ b/tests/auto/qml/v4misc/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_v4misc Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_v4misc LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_v4misc
SOURCES
tst_v4misc.cpp
diff --git a/tests/auto/qml/v4misc/tst_v4misc.cpp b/tests/auto/qml/v4misc/tst_v4misc.cpp
index 201bdb2f95..956b87d5f2 100644
--- a/tests/auto/qml/v4misc/tst_v4misc.cpp
+++ b/tests/auto/qml/v4misc/tst_v4misc.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <private/qv4instr_moth_p.h>
diff --git a/tests/auto/qmldom/CMakeLists.txt b/tests/auto/qmldom/CMakeLists.txt
index 7e2a0b885f..f3186e2263 100644
--- a/tests/auto/qmldom/CMakeLists.txt
+++ b/tests/auto/qmldom/CMakeLists.txt
@@ -10,4 +10,3 @@ add_subdirectory(stringdumper)
add_subdirectory(merging)
add_subdirectory(reformatter)
add_subdirectory(combined)
-add_subdirectory(standalone)
diff --git a/tests/auto/qmldom/combined/CMakeLists.txt b/tests/auto/qmldom/combined/CMakeLists.txt
index 6903f89eff..add44acf0b 100644
--- a/tests/auto/qmldom/combined/CMakeLists.txt
+++ b/tests/auto/qmldom/combined/CMakeLists.txt
@@ -5,6 +5,13 @@
## tst_dom_all Binary executing all tests together
## (simpler to verify coverage)
#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_dom_all LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
@@ -20,7 +27,6 @@ qt_internal_add_test(tst_dom_all
../domitem/tst_qmldomitem.h
../merging/tst_dommerging.h
../reformatter/tst_reformatter.h
- ../standalone/tst_standalone.h
INCLUDE_DIRECTORIES
..
DEFINES
diff --git a/tests/auto/qmldom/combined/tst_dom_all.cpp b/tests/auto/qmldom/combined/tst_dom_all.cpp
index b3920f8e41..6e3e0bd851 100644
--- a/tests/auto/qmldom/combined/tst_dom_all.cpp
+++ b/tests/auto/qmldom/combined/tst_dom_all.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "stringdumper/tst_qmldomstringdumper.h"
#include "errormessage/tst_qmldomerrormessage.h"
@@ -7,7 +7,6 @@
#include "merging/tst_dommerging.h"
#include "path/tst_qmldompath.h"
#include "reformatter/tst_reformatter.h"
-#include "standalone/tst_standalone.h"
#include <QtCore/qdebug.h>
@@ -38,10 +37,6 @@ int main(int argc, char *argv[])
QQmlJS::Dom::TestReformatter test;
status |= QTest::qExec(&test, argc, argv);
}
- {
- QQmlJS::Dom::TestStandalone test;
- status |= QTest::qExec(&test, argc, argv);
- }
if (status)
qWarning() << "Combined test failed!";
return status;
diff --git a/tests/auto/qmldom/domdata/domitem/Base.qml b/tests/auto/qmldom/domdata/domitem/Base.qml
new file mode 100644
index 0000000000..919619ffe6
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/Base.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int a
+}
diff --git a/tests/auto/qmldom/domdata/domitem/Derived.qml b/tests/auto/qmldom/domdata/domitem/Derived.qml
new file mode 100644
index 0000000000..8a6ce158c3
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/Derived.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Base {
+ property int b
+}
diff --git a/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml b/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml
new file mode 100644
index 0000000000..169d162469
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/WithImplicitImport.qml b/tests/auto/qmldom/domdata/domitem/WithImplicitImport.qml
new file mode 100644
index 0000000000..5560aee727
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/WithImplicitImport.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/Yyy.qml b/tests/auto/qmldom/domdata/domitem/Yyy.qml
new file mode 100644
index 0000000000..b2235ff9cc
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/Yyy.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.0
+import QtQuick as QQ
+
+Zzz {
+ id: root
+ width: height
+ Rectangle {
+ color: "green"
+ anchors.fill: parent
+ height: root.foo.height
+ width: root.height
+
+ }
+
+ function lala() {}
+ property Rectangle foo: Rectangle{ height: 200 }
+ function longfunction(a, b, c = "c", d = "d"): string {
+ return "hehe: " + c + d
+ }
+
+ // documentedFunction: is documented
+ // returns 'Good'
+ function documentedFunction(arg1, arg2 = "Qt"): string {
+ return "Good"
+ }
+ QQ.Rectangle {
+ color:"red"
+ }
+
+ component IC: Zzz { property SomeBase data }
+ property SomeBase mySomeBase
+}
diff --git a/tests/auto/qmldom/domdata/domitem/aliasProperties.qml b/tests/auto/qmldom/domdata/domitem/aliasProperties.qml
index c8653e3446..e2521b7afe 100644
--- a/tests/auto/qmldom/domdata/domitem/aliasProperties.qml
+++ b/tests/auto/qmldom/domdata/domitem/aliasProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
Item {
diff --git a/tests/auto/qmldom/domdata/domitem/astComments.qml b/tests/auto/qmldom/domdata/domitem/astComments.qml
new file mode 100644
index 0000000000..6fca4c69e5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/astComments.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function ninja() {
+ // tst comment
+ const patron = 34;
+/*Ast Comment*/ const ppp = 23;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/attachedOrGroupedProperties.qml b/tests/auto/qmldom/domdata/domitem/attachedOrGroupedProperties.qml
new file mode 100644
index 0000000000..ff7720506c
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/attachedOrGroupedProperties.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ id: grouped
+
+ // Dot notation
+ Binding {
+ grouped.font.family: "mono"
+ }
+
+ // Group notation
+ Test {
+ id: test
+ myText {
+ font {
+ pixelSize: 12
+ }
+ }
+ }
+
+ component Test : Rectangle {
+ property Text myText: text1
+ Text {
+ id: text1
+ }
+ }
+
+ Keys.onPressed: (event)=> {
+
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/breakStatement.qml b/tests/auto/qmldom/domdata/domitem/breakStatement.qml
new file mode 100644
index 0000000000..212fe095c0
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/breakStatement.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ function f() {
+ break helloWorld;
+ break;
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/callExpressions.qml b/tests/auto/qmldom/domdata/domitem/callExpressions.qml
new file mode 100644
index 0000000000..d0de350fae
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/callExpressions.qml
@@ -0,0 +1,61 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property var p: f()
+ // crash if arguments are wrongly collected, e.g. because they are stolen from other unimplemented scriptelements
+ property var p2: f(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
+
+ // dummy test: if these ones fail, then the deconstructing parameters cannot possibly work (they share some
+ // common code).
+ function deconstruct() {
+ let { a } = { a: 32, b: 42}, { b, c } = { b: 32, c: 42}, [ d, e, f ] = [ 111, 222, 333 ];
+ let x = [,,,,[1,2,3],,];
+ }
+
+ function f(q,w,e,r,t,y) {
+ let helloF = 32
+ return 42
+ }
+
+ function fWithDefault(q = 1, w = 2, e, r = 4, t, y = 6) {
+ let helloFWithDefault = {}
+ return 42
+ }
+
+ function marmelade(...onTheBread) {
+ let helloMarmelade = 123
+ return 42
+ }
+
+ function marmelade2(spread, it,...onTheBread) {
+ let helloMarmelade2 = 123
+ return 42
+ }
+
+ // check if nothing crashes for empty stuff
+ function empty({}, []) {
+ let {} = {};
+ let [] = [];
+ }
+
+ component MyType: Item{}
+
+ function withTypes(a: int, b: MyType) {}
+ function empty() {}
+ signal mySignal()
+
+
+ property var p3: evil({ hello: "World", y: "yyy"}, [1,2,3], { is: {a: 111, lot: 222, of: 333, fun: 444, really: ["!",]}})
+
+ function evil({ hello = "world", x = 42 },
+ [n = 42, m = 43, o = 44],
+ { destructuring, is = {a, lot, of}, fun = 42 } = {destructuring : 123, is : {x : 123}, fun : 456}) {
+ const helloEvil = "asdf"
+ return 42
+ }
+
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/checkScopes.qml b/tests/auto/qmldom/domdata/domitem/checkScopes.qml
new file mode 100644
index 0000000000..39903e9d90
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/checkScopes.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Item {
+ id: root
+ property int myInt
+ property int myInt2
+
+ myInt: 42
+ myInt2: 123
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/commaExpression.qml b/tests/auto/qmldom/domdata/domitem/commaExpression.qml
new file mode 100644
index 0000000000..c6576b5dad
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/commaExpression.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ function f(a, b, c) {
+ a, b, c;
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/conditionalExpression.qml b/tests/auto/qmldom/domdata/domitem/conditionalExpression.qml
new file mode 100644
index 0000000000..027078b0ef
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/conditionalExpression.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ function f(a, b, c) {
+ a?b:c;
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/continueStatement.qml b/tests/auto/qmldom/domdata/domitem/continueStatement.qml
new file mode 100644
index 0000000000..735ded4bda
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/continueStatement.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ function f() {
+ continue helloWorld;
+ continue;
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/crashes/lambda.qml b/tests/auto/qmldom/domdata/domitem/crashes/lambda.qml
new file mode 100644
index 0000000000..cab0fec143
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/crashes/lambda.qml
@@ -0,0 +1,7 @@
+import QtQuick.Controls
+
+Action {
+ onTriggered: foo(Bla.Bar, function() {
+ console.log("Hello")
+ })
+}
diff --git a/tests/auto/qmldom/domdata/domitem/crashes/templateStrings.qml b/tests/auto/qmldom/domdata/domitem/crashes/templateStrings.qml
new file mode 100644
index 0000000000..feb5646496
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/crashes/templateStrings.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+
+Item {
+ property string verbatim1: 'A "verbatim" string!'
+ property string verbatim2: "A 'verbatim' string\u2757"
+ property string verbatim3: `400 + 300 is ${400 + 300}.
+
+mutliline`
+}
diff --git a/tests/auto/qmldom/domdata/domitem/ecmaScriptClass.qml b/tests/auto/qmldom/domdata/domitem/ecmaScriptClass.qml
new file mode 100644
index 0000000000..e86ea737e5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/ecmaScriptClass.qml
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+
+function f() {
+
+var count = 0;
+
+class Person {
+ constructor(name) {
+ this._name = name
+ }
+}
+
+class Employee extends Person{
+
+
+ constructor(name, age) {
+ super(name);
+ this._name = name;
+ this._age = age;
+ ++count;
+ }
+
+ get /* do we get the comment? */ name() {
+ return this._name.toUpperCase();
+ }
+
+ set name(newName){
+ if (newName) {
+ this._name = newName;
+ }
+ }
+
+ static get count() { return count;}
+}
+
+
+}
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/emptyMethodBody.qml b/tests/auto/qmldom/domdata/domitem/emptyMethodBody.qml
new file mode 100644
index 0000000000..d987d3e649
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/emptyMethodBody.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ function f(x) {}
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/enumDeclarations.qml b/tests/auto/qmldom/domdata/domitem/enumDeclarations.qml
new file mode 100644
index 0000000000..260f079fd6
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/enumDeclarations.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+Item {
+ enum Cats { Patron, Mafya, Kivrik = -1 }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fieldMemberExpression.qml b/tests/auto/qmldom/domdata/domitem/fieldMemberExpression.qml
new file mode 100644
index 0000000000..8890a0c8cb
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fieldMemberExpression.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: root
+
+ property int p: 42
+
+ property Item property1: Item {
+ property bool p: 44
+ property Item property2: Item {
+ property string p: "Hello World"
+ property Item property3: Item {
+ property real p: 123
+ }
+ }
+ }
+
+ property var p1: p
+ property var p1Qualified: root.p
+ property var p1Bracket: root["p"]
+ property var p1Index: root[42]
+ property var p1Key: root[p]
+
+ property var p2: property1.p
+ property var p2Qualified: root.property1.p
+
+ property var p3: property1.property2.p
+ property var p3Qualified: root.property1.property2.p
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegion.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegion.qml
new file mode 100644
index 0000000000..f441efb5e2
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegion.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ property int helloWorld
+
+ // before helloWorld binding
+ helloWorld: 42 // after helloWorld binding
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/comments.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/comments.qml
new file mode 100644
index 0000000000..a69505c544
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/comments.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+/*
+splitting
+multiline
+*/
+// single line
+/* another comment */
+QtObject {}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/enums.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/enums.qml
new file mode 100644
index 0000000000..e6e0f7a205
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/enums.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ enum OSC {
+ sin = 1,
+ saw,
+ tri = 16
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/functions.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/functions.qml
new file mode 100644
index 0000000000..e045e5a4cc
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/functions.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function a() : int {}
+
+ component A : Item {
+ function b(k: int) : int {}
+ }
+
+ signal k(int a)
+ signal kk(a: int)
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/imports.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/imports.qml
new file mode 100644
index 0000000000..ccf012bc63
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/imports.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQml 2.15
+import QtQuick.Controls as Patron
+import "../cats"
+
+Item {
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/pragmas.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/pragmas.qml
new file mode 100644
index 0000000000..5f34f9ae33
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/pragmas.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Singleton
+pragma ComponentBehavior: Bound
+pragma FunctionSignatureBehavior: Enforced
+pragma ValueTypeBehavior: Copy,Addressable
+
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qmldom/domdata/domitem/finalizeScriptExpressions.qml b/tests/auto/qmldom/domdata/domitem/finalizeScriptExpressions.qml
new file mode 100644
index 0000000000..5a0c811396
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/finalizeScriptExpressions.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property var binding
+ binding: 42
+
+ property var bindingInPropertyDefinition: 123
+
+ function return42(aa: Item = 33, bb: string = "Hello", cc = binding): int {
+ return 42
+ }
+ function empty(aa: Item, bb: string, cc) {}
+ function full(aa: Item, bb: string, cc)
+ {
+ const x = 42;
+ const formula = (x + 5) * 2 + 10 - x
+ return formula;
+ }
+
+ id: idBinding
+
+ property var arrayBinding
+ arrayBinding:[]
+ property var objectBinding
+ objectBinding: Item {}
+}
diff --git a/tests/auto/qmldom/domdata/domitem/forStatements.qml b/tests/auto/qmldom/domdata/domitem/forStatements.qml
new file mode 100644
index 0000000000..6c06098309
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/forStatements.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f() {
+ let sum = 0, helloWorld = "hello"
+ for (let i = 0; i < 100; i = i + 1) {
+ sum = sum + 1
+ for (;;)
+ i = 42
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/ifStatements.qml b/tests/auto/qmldom/domdata/domitem/ifStatements.qml
new file mode 100644
index 0000000000..90d1bd3ac8
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/ifStatements.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ function conditional() {
+ let i = 5
+ if (i)
+ i = 42
+
+ if (i == 55)
+ i = 32
+ else
+ i = i - 1
+
+ if (i == 42) {
+ i = 111
+ }
+
+ if (i == 746) {
+ i = 123
+ } else {
+ i = 456
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/import.js b/tests/auto/qmldom/domdata/domitem/import.js
new file mode 100644
index 0000000000..e13db4c1a4
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/import.js
@@ -0,0 +1,2 @@
+.import "main.js" as Main
+console.log(Main.a);
diff --git a/tests/auto/qmldom/domdata/domitem/inactiveVisitorMarkerCrash.qml b/tests/auto/qmldom/domdata/domitem/inactiveVisitorMarkerCrash.qml
new file mode 100644
index 0000000000..c706251e47
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/inactiveVisitorMarkerCrash.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ title: qsTr("Hello World")
+
+ HelloWorld { myP: 55; myPPP: 55 }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/inlineComponents.qml b/tests/auto/qmldom/domdata/domitem/inlineComponents.qml
new file mode 100644
index 0000000000..5ae0af4eb1
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/inlineComponents.qml
@@ -0,0 +1,17 @@
+import QtQuick
+
+Item {
+ id: mainComponent
+
+ component IC1: Item { property string firstIC }
+ component IC2: Item { property string secondIC }
+
+ Item {
+ Item {
+ Item {
+ component IC3: Item { property string thirdIC }
+ }
+ }
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/inlineObject.qml b/tests/auto/qmldom/domdata/domitem/inlineObject.qml
new file mode 100644
index 0000000000..2f5941f4da
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/inlineObject.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ component IC: Item {}
+ property var myItem: Item {}
+ property var myItem2: IC {}
+}
diff --git a/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml b/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml
index 266e4139b7..7998642ef5 100644
--- a/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml
+++ b/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
Item {
diff --git a/tests/auto/qmldom/domdata/domitem/iterationStatements.qml b/tests/auto/qmldom/domdata/domitem/iterationStatements.qml
new file mode 100644
index 0000000000..e75eeb2d7d
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/iterationStatements.qml
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+
+ function whileStatement() {
+ const i = 10;
+ while (i > 0) {
+ i = i -1;
+ while ( i > 100) {}
+ }
+
+ while (i > 0) i = i-1
+ }
+
+ function doWhile() {
+ let a = 7;
+ do {
+ const b = a;
+ a = a - 1;
+ } while (a > 0)
+
+ do a = a-1; while (a > 0)
+ }
+
+ function forOf() {
+ const iterable = [[1,2], [3,4],]
+ for (var [a,b] of iterable) {
+
+ let t;
+ for (const [a1, , a2, ...rest] of array) {
+
+ }
+ for (const k of [1,2,3,4,,,]) {
+ t += k;
+ }
+ for (t of a) {
+ {}
+ }
+ for (t of a) t += k
+ }
+ }
+
+ function forIn() {
+ const enumerable = {
+ list: [1, 2, 3, 4, 5],
+ name: 'John',
+ age: 25
+ };
+
+ for (var [a,b,c,d] in enumerable) {
+ let t;
+ for (t in enumerable) {
+ {}
+ }
+ for (const [a1, , a2, ...rest] in enumerable.list) {
+
+ }
+ for (let t in enumerable) t += k
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/module.mjs b/tests/auto/qmldom/domdata/domitem/module.mjs
new file mode 100644
index 0000000000..6838766329
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/module.mjs
@@ -0,0 +1,6 @@
+
+import { helper } from "utils.mjs"
+
+export function entry() {
+ return helper()
+}
diff --git a/tests/auto/qmldom/domdata/domitem/nullStatements.qml b/tests/auto/qmldom/domdata/domitem/nullStatements.qml
new file mode 100644
index 0000000000..f4be9b30de
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/nullStatements.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function testForNull() {
+ for (;;)
+ {}
+ for (;;)
+ x
+ {} {} {} {}
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/objectBindings.qml b/tests/auto/qmldom/domdata/domitem/objectBindings.qml
new file mode 100644
index 0000000000..c02a7113a5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/objectBindings.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick as QQ
+
+Item {
+ id: root
+ Item {}
+ QQ.Item {}
+ property var x: root.
+ QQ.Drag {}
+}
diff --git a/tests/auto/qmldom/domdata/domitem/propertyBindings.qml b/tests/auto/qmldom/domdata/domitem/propertyBindings.qml
new file mode 100644
index 0000000000..3c2931cb5d
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/propertyBindings.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int a: 42
+ property int b
+
+ b: a
+
+ id: root
+}
diff --git a/tests/auto/qmldom/domdata/domitem/returnStatements.qml b/tests/auto/qmldom/domdata/domitem/returnStatements.qml
new file mode 100644
index 0000000000..4f503244fb
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/returnStatements.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ // TODO: Add a test for returning void
+ function returningFunction(i) {
+ if (i)
+ return 123;
+ else
+ return 1 + 2;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/simplestJSStatement.js b/tests/auto/qmldom/domdata/domitem/simplestJSStatement.js
new file mode 100644
index 0000000000..d954a87fc0
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/simplestJSStatement.js
@@ -0,0 +1 @@
+let v=1;
diff --git a/tests/auto/qmldom/domdata/domitem/simplestJSmodule.mjs b/tests/auto/qmldom/domdata/domitem/simplestJSmodule.mjs
new file mode 100644
index 0000000000..401b25314b
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/simplestJSmodule.mjs
@@ -0,0 +1 @@
+export function entry() {}
diff --git a/tests/auto/qmldom/domdata/domitem/switchStatement.qml b/tests/auto/qmldom/domdata/domitem/switchStatement.qml
new file mode 100644
index 0000000000..b2bacec8c0
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/switchStatement.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ function switchStatement(){
+ const animals = "cat";
+ const no = 0;
+ switch (animals) {
+ case "cat":
+ switch (no) {
+ case 0: return "patron";
+ case 1: return "mafik";
+ default: return "none";
+ }
+ case "dog": {
+ // check if qqmljsscope is created for this case
+ const name = "tekila";
+ let another = "mutantx";
+ return name;
+ }
+ default: return "monster";
+ case "moreCases!":
+ return "moreCaseClauses?"
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/tryStatements.qml b/tests/auto/qmldom/domdata/domitem/tryStatements.qml
new file mode 100644
index 0000000000..5bd29db058
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/tryStatements.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ function f() {
+ try { insideTry; } catch(catchExpression) { insideCatch; } finally { insideFinally; }
+ try { insideTry; } catch(catchExpression) { insideCatch; }
+ try { insideTry; } finally { insideFinally; }
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/decrement.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/decrement.qml
new file mode 100644
index 0000000000..fb9fe6f252
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/decrement.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ --a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/delete.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/delete.qml
new file mode 100644
index 0000000000..39063b8c2f
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/delete.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ delete a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/increment.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/increment.qml
new file mode 100644
index 0000000000..e7acd3dfe5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/increment.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ ++a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/not.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/not.qml
new file mode 100644
index 0000000000..6c3a1aa035
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/not.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ !a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/postDecrement.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/postDecrement.qml
new file mode 100644
index 0000000000..52d170fae0
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/postDecrement.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ a--;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/postIncrement.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/postIncrement.qml
new file mode 100644
index 0000000000..30b4da8c8f
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/postIncrement.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ a++;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/tilde.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/tilde.qml
new file mode 100644
index 0000000000..b35a00bd79
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/tilde.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ ~a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/typeof.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/typeof.qml
new file mode 100644
index 0000000000..1696c5bd27
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/typeof.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ typeof a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryMinus.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryMinus.qml
new file mode 100644
index 0000000000..e711c4d2f5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryMinus.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ -a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryPlus.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryPlus.qml
new file mode 100644
index 0000000000..1b9ecbf5b5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryPlus.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ +a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/void.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/void.qml
new file mode 100644
index 0000000000..d9396e1b39
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/void.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ void a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/variableDeclarations.qml b/tests/auto/qmldom/domdata/domitem/variableDeclarations.qml
new file mode 100644
index 0000000000..7d5d2ac9a5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/variableDeclarations.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function count() {
+ let one = 1, two = 2, three = 3, four = 4, five = 5, six = 6
+ let testMe = 123
+ }
+ // for now only numeric literal and string literal is supported
+ function f() {
+ let sum = 0, helloWorld = "hello"
+ const a = 3;
+ const b = "patron";
+ var aa = helloWorld, bb = aa;
+
+ const bool1 = true;
+ let bool2 = false;
+ var nullVar = null;
+ return sum;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/visitTreeFilter.qml b/tests/auto/qmldom/domdata/domitem/visitTreeFilter.qml
new file mode 100644
index 0000000000..5d0b06f91f
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/visitTreeFilter.qml
@@ -0,0 +1,4 @@
+
+QtObject {
+ property int helloProperty
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml
index 0911f0e102..ab722f94be 100644
--- a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml
+++ b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml
@@ -26,8 +26,10 @@ Item {
// before zz
zz - // after z + zz
/*before (a b)*/(/* before a */ a * /* after a */ b * /*after b*/ c); // after (a * b * c)
+
if (y == 6) // if comment
console.log("pippo");
+
a + b; // comment
// footer
diff --git a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml
index 522cbbc777..c6e33f9389 100644
--- a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml
+++ b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml
@@ -26,8 +26,10 @@ Item {
// before zz
zz - // after z + zz
/*before (a b)*/(/* before a */ a * /* after a */ b * /*after b*/ c); // after (a * b * c)
+
if (y == 6) // if comment
console.log("pippo");
+
a + b; // comment
// footer
diff --git a/tests/auto/qmldom/domdata/reformatter/file1.qml b/tests/auto/qmldom/domdata/reformatter/file1.qml
index 36819b9998..e0382bf57c 100644
--- a/tests/auto/qmldom/domdata/reformatter/file1.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file1.qml
@@ -14,6 +14,8 @@ Window {
Rectangle {
anchors.fill: parent
+ Behavior on opacity {}
+
ListView {
width: parent.width
model: {
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml b/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml
index 361d054f9c..6a24f907d1 100644
--- a/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml
@@ -14,6 +14,9 @@ Window {
Rectangle {
anchors.fill: parent
+ Behavior on opacity {
+ }
+
ListView {
model: {
MySingleton.mySignal();
@@ -38,7 +41,7 @@ Window {
function f(v = 4) {
let c = 0;
return {
- "a": function () {
+ a: function () {
if (b == 0)
c += 78 * 5 * v;
}()
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml b/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml
index 18c719020c..2e7483f453 100644
--- a/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml
@@ -14,6 +14,8 @@ Window {
Rectangle {
anchors.fill: parent
+ Behavior on opacity {}
+
ListView {
width: parent.width
model: {
@@ -32,7 +34,7 @@ Window {
function f(v = 4) {
let c = 0;
return {
- "a": function () {
+ a: function () {
if (b == 0)
c += 78 * 5 * v;
}()
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml b/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml
index 224f1c1dce..4085b91e6e 100644
--- a/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml
@@ -14,6 +14,8 @@ property var arrTrailingComma: [1,2,3,]
Rectangle {
anchors.fill: parent
+Behavior on opacity {}
+
ListView {
width: parent.width
model: {
diff --git a/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml b/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml
index 48a961930d..3ed3aa208c 100644
--- a/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml
@@ -52,7 +52,7 @@ Window {
function f(v) {
let c = 0;
return {
- "a": function () {
+ a: function () {
if (b == 0)
c += 78 * 5 * v;
}()
diff --git a/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml b/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml
index cb84168307..7473283605 100644
--- a/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml
+++ b/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml
@@ -8,7 +8,7 @@ Item {
function foo() {
theItem.foo("The issue is exacerbated if the object literal is wrapped onto the next line like so:", {
- "foo": theFoo
- });
+ "foo": theFoo
+ });
}
}
diff --git a/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml b/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml
index b01eca88d3..fde8ffd686 100644
--- a/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml
+++ b/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml
@@ -4,7 +4,7 @@ Item {
function foo() {
iterableObj = [1, 2];
obj = {
- "a": 42
+ a: 42
};
let x = (console.log("bla\n"), 3);
myFunction(...iterableObj); // pass all elements of iterableObj as arguments to function myFunction
diff --git a/tests/auto/qmldom/domitem/CMakeLists.txt b/tests/auto/qmldom/domitem/CMakeLists.txt
index bf12ad866c..e6f8117902 100644
--- a/tests/auto/qmldom/domitem/CMakeLists.txt
+++ b/tests/auto/qmldom/domitem/CMakeLists.txt
@@ -6,6 +6,13 @@
#####################################################################
## tst_qmldomitem Test:
#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldomitem LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
diff --git a/tests/auto/qmldom/domitem/tst_qmldomitem.cpp b/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
index ea450e6969..97b7dcc3ed 100644
--- a/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
+++ b/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmldomitem.h"
QTEST_MAIN(QQmlJS::Dom::TestDomItem)
diff --git a/tests/auto/qmldom/domitem/tst_qmldomitem.h b/tests/auto/qmldom/domitem/tst_qmldomitem.h
index 433a92a2b2..97cc26bbd7 100644
--- a/tests/auto/qmldom/domitem/tst_qmldomitem.h
+++ b/tests/auto/qmldom/domitem/tst_qmldomitem.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLDOMITEM_H
#define TST_QMLDOMITEM_H
@@ -9,6 +9,7 @@
#include <QtQmlDom/private/qqmldommock_p.h>
#include <QtQmlDom/private/qqmldomcompare_p.h>
#include <QtQmlDom/private/qqmldomfieldfilter_p.h>
+#include <QtQmlDom/private/qqmldomscriptelements_p.h>
#include <QtTest/QtTest>
#include <QtCore/QCborValue>
@@ -16,14 +17,18 @@
#include <QtCore/QLibraryInfo>
#include <QtCore/QFileInfo>
+#include <deque>
#include <memory>
+#include <utility>
+#include <variant>
+#include <vector>
QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-inline DomItem wrapInt(DomItem &self, const PathEls::PathComponent &p, const int &i)
+inline DomItem wrapInt(const DomItem &self, const PathEls::PathComponent &p, const int &i)
{
return self.subDataItem(p, i);
}
@@ -51,8 +56,9 @@ private slots:
qmltypeDirs = QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::QmlImportsPath) });
universePtr =
std::shared_ptr<DomUniverse>(new DomUniverse(QStringLiteral(u"dummyUniverse")));
- envPtr = std::shared_ptr<DomEnvironment>(new DomEnvironment(
- QStringList(), DomEnvironment::Option::SingleThreaded, universePtr));
+ envPtr = std::shared_ptr<DomEnvironment>(
+ new DomEnvironment(QStringList(), DomEnvironment::Option::SingleThreaded,
+ DomCreationOption::None, universePtr));
env = DomItem(envPtr);
testOwnerPtr = std::shared_ptr<MockOwner>(new MockOwner(
Path::Root(u"env").field(u"testOwner"), 0,
@@ -363,7 +369,7 @@ private slots:
auto tOwner3 = tOwner.path(u"$env.testOwner");
QCOMPARE(tOwner3.internalKind(), DomType::MockOwner);
QList<qint64> values;
- tOwner.visitTree(Path(), [&values](Path p, DomItem i, bool) {
+ tOwner.visitTree(Path(), [&values](const Path &p, DomItem i, bool) {
if (i.pathFromOwner() != p)
myErrors()
.error(QStringLiteral(u"unexpected path %1 %2")
@@ -403,18 +409,15 @@ private slots:
qmltypeDirs,
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
- univPtr));
+ DomCreationOption::None, univPtr));
QQmlJS::Dom::DomItem env(envPtr);
QVERIFY(env);
QString testFile1 = baseDir + QLatin1String("/test1.qml");
DomItem tFile;
- // env.loadBuiltins();
- env.loadFile(
- testFile1, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadFile(baseDir, QString(), {}, LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir), {});
+ envPtr->loadPendingDependencies();
QVERIFY(tFile);
tFile = tFile.field(Fields::currentItem);
@@ -424,7 +427,7 @@ private slots:
DomItem obj1 = comp1.field(Fields::objects).index(0);
QVERIFY(obj1);
- tFile.visitTree(Path(), [&tFile](Path p, DomItem i, bool) {
+ tFile.visitTree(Path(), [&tFile](const Path &p, DomItem i, bool) {
if (!(i == i.path(i.canonicalPath()))) {
DomItem i2 = i.path(i.canonicalPath());
qDebug() << p << i.canonicalPath() << i.internalKindStr() << i2.internalKindStr()
@@ -489,18 +492,16 @@ private slots:
auto univPtr = std::shared_ptr<QQmlJS::Dom::DomUniverse>(
new QQmlJS::Dom::DomUniverse(QLatin1String("univ1")));
auto envPtr = std::shared_ptr<QQmlJS::Dom::DomEnvironment>(new QQmlJS::Dom::DomEnvironment(
- qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, univPtr));
+ qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, {}, univPtr));
QQmlJS::Dom::DomItem env(envPtr);
QVERIFY(env);
QString testFile1 = baseDir + QLatin1String("/test1.qml");
DomItem tFile;
- env.loadBuiltins();
- env.loadFile(
- testFile1, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadFile(baseDir, QString(), {}, LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir), {});
+ envPtr->loadPendingDependencies();
QVERIFY(tFile);
tFile = tFile.field(Fields::currentItem);
@@ -531,13 +532,13 @@ private slots:
QList<DomItem> rects;
obj1.resolve(
Path::Current(PathCurrent::Lookup).field(Fields::type).key(u"Rectangle"_s),
- [&rects](Path, DomItem &el) {
+ [&rects](Path, const DomItem &el) {
rects.append(el);
return true;
},
{});
QVERIFY(rects.size() == 1);
- for (DomItem &el : rects) {
+ for (const DomItem &el : rects) {
QCOMPARE(rect.first(), el);
}
}
@@ -548,6 +549,13 @@ private slots:
DomItem f2 = env.path(p2);
QVERIFY2(f2, "Directory dependencies did not load MySingleton.qml");
}
+ {
+ QString fPath = tFile.canonicalFilePath();
+ QString fPath2 = fPath.mid(0, fPath.lastIndexOf(u'/')) % u"/ImportMeImplicitly.ui.qml";
+ Path p2 = Paths::qmlFileObjectPath(fPath2);
+ DomItem f2 = env.path(p2);
+ QVERIFY2(f2, "Directory dependencies did not load .ui.qml file!");
+ }
}
void testImports()
@@ -558,17 +566,17 @@ private slots:
using namespace Qt::StringLiterals;
QString testFile1 = baseDir + QLatin1String("/TestImports.qml");
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
QStringList(),
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
DomItem tFile;
- env.loadFile(
- testFile1, QString(),
- [&tFile](Path, DomItem &, DomItem &newIt) { tFile = newIt.fileObject(); },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
QVERIFY(tFile);
QList<QmlUri> importedModules;
@@ -601,17 +609,15 @@ private slots:
{
QString testFile = baseDir + QLatin1String("/test1.qml");
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
qmltypeDirs,
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
DomItem tFile; // place where to store the loaded file
- env.loadFile(
- testFile, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadPendingDependencies();
DomItem f = tFile.fileObject();
QString dump1;
f.dump([&dump1](QStringView v) { dump1.append(v); });
@@ -626,6 +632,7 @@ private slots:
if (!diffs.isEmpty())
qDebug() << "testDeepCopy.diffs:" << diffs;
QVERIFY(diffs.isEmpty());
+ DomItem env(envPtr);
DomItem univFile = env.universe().path(f.canonicalPath());
MutableDomItem univFileCopy = univFile.makeCopy();
QStringList univFileDiffs =
@@ -645,11 +652,12 @@ private slots:
void testInMemory()
{
DomItem res = DomItem::fromCode("MyItem{}");
+ QCOMPARE(res.qmlObject(GoTo::Strict), DomItem());
DomItem obj = res.qmlObject(GoTo::MostLikely);
QCOMPARE(obj.name(), u"MyItem");
}
- static void checkAliases(DomItem &qmlObj)
+ static void checkAliases(const DomItem &qmlObj)
{
using namespace Qt::StringLiterals;
@@ -714,7 +722,7 @@ private slots:
}
++i;
}
- for (DomItem obj : qmlObj.children().values()) {
+ for (const DomItem &obj : qmlObj.children().values()) {
if (obj.as<QmlObject>())
checkAliases(obj);
}
@@ -734,22 +742,2709 @@ private slots:
QFETCH(QString, inFile);
QString testFile1 = baseDir + u"/"_s + inFile;
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
QStringList(),
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
DomItem tFile;
- env.loadFile(
- testFile1, QString(),
- [&tFile](Path, DomItem &, DomItem &newIt) { tFile = newIt.fileObject(); },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
DomItem rootObj = tFile.qmlObject(GoTo::MostLikely);
checkAliases(rootObj);
}
+ void inlineComponents()
+ {
+ using namespace Qt::StringLiterals;
+
+ QString testFile = baseDir + u"/inlineComponents.qml"_s;
+
+ DomCreationOptions options{ DomCreationOption::WithScriptExpressions };
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies, options);
+
+ DomItem tFile;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ auto rootQmlObject = tFile.rootQmlObject(GoTo::MostLikely);
+
+ // check if the lookup can find the inline components correctly, to see if the
+ // visitScopeChain also visit them.
+ auto ic3 = rootQmlObject.lookup("IC3", LookupType::Type, LookupOption::Normal,
+ [](const ErrorMessage &) {});
+
+ QCOMPARE(ic3.size(), 1);
+ QCOMPARE(ic3.front().name(), "inlineComponents.IC3");
+ QCOMPARE(ic3.front().field(Fields::nameIdentifiers).internalKind(), DomType::ScriptType);
+ QCOMPARE(ic3.front()
+ .field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .value()
+ .toString(),
+ u"IC3"_s);
+
+ auto ic1 = rootQmlObject.lookup("IC1", LookupType::Type, LookupOption::Normal,
+ [](const ErrorMessage &) {});
+
+ QCOMPARE(ic1.size(), 1);
+ QCOMPARE(ic1.front().name(), "inlineComponents.IC1");
+ }
+
+ void inlineObject()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/inlineObject.qml"_s;
+
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ DomItem tFile;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ auto rootQmlObject = tFile.rootQmlObject(GoTo::MostLikely);
+
+ // check that the inline objects have their prototypes set.
+
+ {
+ auto prototypes = rootQmlObject.propertyInfos()
+ .key(u"myItem"_s)
+ .field(Fields::bindings)
+ .index(0)
+ .field(Fields::value)
+ .field(Fields::prototypes);
+ QVERIFY(prototypes.internalKind() != DomType::Empty);
+ QCOMPARE(prototypes.indexes(), 1);
+ QCOMPARE(prototypes.index(0)
+ .field(Fields::referredObjectPath)
+ .as<ConstantData>()
+ ->value()
+ .toString(),
+ u"@lookup.type[\"Item\"]"_s);
+ }
+
+ {
+ auto prototypes2 = rootQmlObject.propertyInfos()
+ .key(u"myItem2"_s)
+ .field(Fields::bindings)
+ .index(0)
+ .field(Fields::value)
+ .field(Fields::prototypes);
+ QVERIFY(prototypes2.internalKind() != DomType::Empty);
+ QCOMPARE(prototypes2.indexes(), 1);
+ QCOMPARE(prototypes2.index(0)
+ .field(Fields::referredObjectPath)
+ .as<ConstantData>()
+ ->value()
+ .toString(),
+ u"@lookup.type[\"IC\"]"_s);
+ }
+ }
+
+ void scopesInDom()
+ {
+ QString fileName = baseDir + u"/checkScopes.qml"_s;
+
+ const QStringList importPaths = {
+ QLibraryInfo::path(QLibraryInfo::QmlImportsPath),
+ };
+
+ DomItem tFile;
+
+ auto envPtr = DomEnvironment::create(
+ importPaths,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ WithSemanticAnalysis);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, fileName),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ auto root = tFile.rootQmlObject(GoTo::MostLikely);
+
+ {
+ auto rootQmlObject = root.as<QmlObject>();
+ QVERIFY(rootQmlObject);
+ auto rootScope = rootQmlObject->semanticScope();
+ QVERIFY(rootScope);
+ QVERIFY(rootScope->hasOwnProperty("myInt"));
+ QVERIFY(rootScope->hasOwnProperty("myInt2"));
+ QVERIFY(rootScope->hasOwnPropertyBindings("myInt"));
+ QVERIFY(rootScope->hasOwnPropertyBindings("myInt2"));
+ }
+ }
+
+ void propertyBindings()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/propertyBindings.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ // check the binding to a and b
+ DomItem a = rootQmlObject.path(".bindings[\"a\"][0].value.scriptElement.value");
+ QCOMPARE(a.value().toDouble(), 42);
+
+ DomItem b = rootQmlObject.path(".bindings[\"b\"][0].value.scriptElement.identifier");
+ QCOMPARE(b.value().toString(), "a");
+
+ DomItem root = rootQmlObject.path(".idStr");
+ QCOMPARE(root.value().toString(), "root");
+
+ DomItem componentIds = rootQmlObject.component().path(".ids");
+ QCOMPARE(componentIds.keys(), QSet<QString>({ "root" }));
+
+ DomItem idScriptExpression =
+ componentIds.key("root").index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(idScriptExpression.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(idScriptExpression.field(Fields::identifier).value().toString(), "root");
+ }
+
+ void variableDeclarations()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/variableDeclarations.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement");
+
+ // check that variabledeclarationlists and statement lists are correctly collected
+ {
+ // let one = 1, two = 2, three = 3, four = 4, five = 5, six = 6
+ std::vector<QString> data = { u"one"_s, u"two"_s, u"three"_s,
+ u"four"_s, u"five"_s, u"six"_s };
+ DomItem block =
+ rootQmlObject.path(".methods[\"count\"][0].body.scriptElement.statements");
+ DomItem variableDeclaration = block.index(0).field(Fields::declarations);
+ QCOMPARE((size_t)variableDeclaration.indexes(), data.size());
+ for (size_t i = 0; i < data.size(); ++i) {
+ DomItem variableDeclarationEntry = variableDeclaration.index(i);
+ QCOMPARE(variableDeclarationEntry.field(Fields::identifier).value().toString(),
+ data[i]);
+ QCOMPARE((size_t)variableDeclarationEntry.field(Fields::initializer)
+ .value()
+ .toInteger(),
+ i + 1);
+ }
+ // let testMe = 123
+ DomItem testDeclaration = block.index(1).field(Fields::declarations);
+ QCOMPARE(testDeclaration.indexes(), 1);
+ QCOMPARE(testDeclaration.index(0).field(Fields::identifier).value().toString(),
+ u"testMe"_s);
+ QCOMPARE(testDeclaration.index(0).field(Fields::initializer).value().toInteger(),
+ 123ll);
+ }
+
+ // if there is a failure in the following line, then thats because either the
+ // variabledeclarationlist or the statement list was wrongly collected and the
+ // domcreator gave up on creating the Dom (unexpected DomType: DomType::ScriptExpression
+ // means that no Dom was constructed for f's body)
+
+ // This block should have a semantic scope that defines sum and helloWorld
+ auto blockSemanticScope = block.semanticScope();
+ QVERIFY(blockSemanticScope);
+ QVERIFY(blockSemanticScope);
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"sum"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"helloWorld"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"a"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"b"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"aa"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"bb"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"bool1"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"bool2"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"nullVar"_s));
+ DomItem statements = block.field(Fields::statements);
+ QCOMPARE(statements.indexes(), 8);
+
+ // avoid that toInteger calls return 0 on error, which sometimes is the correct response.
+ const int invalidEnumValue = 9999;
+
+ {
+ // let sum = 0, helloWorld = "hello"
+ DomItem variableDeclaration = statements.index(0).field(Fields::declarations);
+ QCOMPARE(variableDeclaration.indexes(), 2);
+ DomItem sumInitialization = variableDeclaration.index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(sumInitialization.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Let);
+ QCOMPARE(sumInitialization.field(Fields::identifier).value().toString(), "sum");
+ QCOMPARE(sumInitialization.field(Fields::initializer)
+ .field(Fields::value)
+ .value()
+ .toDouble(),
+ 0);
+
+ DomItem helloWorldInitialization = variableDeclaration.index(1);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(helloWorldInitialization.field(Fields::scopeType)
+ .value()
+ .toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Let);
+ QCOMPARE(helloWorldInitialization.field(Fields::identifier).value().toString(),
+ "helloWorld");
+ QCOMPARE(helloWorldInitialization.field(Fields::initializer)
+ .field(Fields::value)
+ .value()
+ .toString(),
+ "hello");
+ }
+ {
+ // const a = 3
+ DomItem a = statements.index(1).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(a.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Const);
+ QCOMPARE(a.field(Fields::identifier).value().toString(), "a");
+ QCOMPARE(a.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(a.field(Fields::initializer).field(Fields::value).value().toInteger(), 3);
+ }
+ {
+ // const b = "patron"
+ DomItem b = statements.index(2).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(b.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Const);
+ QCOMPARE(b.field(Fields::identifier).value().toString(), "b");
+ QCOMPARE(b.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(b.field(Fields::initializer).field(Fields::value).value().toString(),
+ "patron");
+ }
+ {
+ // var aa = helloWorld
+ DomItem aa = statements.index(3).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(aa.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Var);
+ QCOMPARE(aa.field(Fields::identifier).value().toString(), "aa");
+ QCOMPARE(aa.field(Fields::initializer).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(aa.field(Fields::initializer).field(Fields::identifier).value().toString(),
+ "helloWorld");
+ // var bb = aa
+ DomItem bb = statements.index(3).field(Fields::declarations).index(1);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(bb.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Var);
+ QCOMPARE(bb.field(Fields::identifier).value().toString(), "bb");
+ QCOMPARE(bb.field(Fields::initializer).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(bb.field(Fields::initializer).field(Fields::identifier).value().toString(),
+ "aa");
+ }
+ {
+ // const bool1 = true
+ DomItem bool1 = statements.index(4).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(bool1.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Const);
+ QCOMPARE(bool1.field(Fields::identifier).value().toString(), "bool1");
+ QCOMPARE(bool1.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QVERIFY(bool1.field(Fields::initializer).field(Fields::value).value().isTrue());
+ }
+ {
+ // let bool2 = false
+ DomItem bool2 = statements.index(5).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(bool2.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Let);
+ QCOMPARE(bool2.field(Fields::identifier).value().toString(), "bool2");
+ QCOMPARE(bool2.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QVERIFY(bool2.field(Fields::initializer).field(Fields::value).value().isFalse());
+ }
+ {
+ // var nullVar = null
+ DomItem nullVar = statements.index(6).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(nullVar.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Var);
+ QCOMPARE(nullVar.field(Fields::identifier).value().toString(), "nullVar");
+ QCOMPARE(nullVar.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QVERIFY(nullVar.field(Fields::initializer).field(Fields::value).value().isNull());
+ }
+ }
+
+ void ifStatements()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/ifStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.path(".methods[\"conditional\"][0].body.scriptElement");
+ auto blockSemanticScope = block.semanticScope();
+ QVERIFY(blockSemanticScope);
+
+ DomItem statements = block.field(Fields::statements);
+ QCOMPARE(statements.indexes(), 5);
+
+ // let i = 5
+ DomItem iDeclaration = statements.index(0);
+ QCOMPARE(iDeclaration.internalKind(), DomType::ScriptVariableDeclaration);
+
+ {
+ // if (i)
+ // i = 42
+ DomItem conditional = statements.index(1);
+ DomItem condition = conditional.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(condition.field(Fields::identifier).value().toString(), u"i"_s);
+
+ DomItem consequence = conditional.field(Fields::consequence);
+ auto nonBlockSemanticScope = consequence.semanticScope();
+ QVERIFY(!nonBlockSemanticScope); // because there is no block
+
+ QCOMPARE(consequence.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(consequence.field(Fields::left).field(Fields::identifier).value().toString(),
+ u"i"_s);
+ QCOMPARE(consequence.field(Fields::right).field(Fields::value).value().toDouble(), 42);
+
+ QCOMPARE(conditional.field(Fields::alternative).internalKind(), DomType::Empty);
+ }
+ {
+ // if (i == 55)
+ // i = 32
+ // else
+ // i = i - 1
+ DomItem conditional = statements.index(2);
+ DomItem condition = conditional.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(condition.field(Fields::right).field(Fields::value).value().toDouble(), 55);
+
+ DomItem consequence = conditional.field(Fields::consequence);
+ auto nonBlockSemanticScope = consequence.semanticScope();
+ QVERIFY(!nonBlockSemanticScope);
+
+ QCOMPARE(consequence.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(consequence.field(Fields::left).field(Fields::identifier).value().toString(),
+ u"i"_s);
+ QCOMPARE(consequence.field(Fields::right).field(Fields::value).value().toDouble(), 32);
+
+ DomItem alternative = conditional.field(Fields::alternative);
+ QCOMPARE(alternative.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(alternative.field(Fields::left).field(Fields::identifier).value().toString(),
+ u"i"_s);
+ QCOMPARE(alternative.field(Fields::right).internalKind(),
+ DomType::ScriptBinaryExpression);
+ }
+ {
+ // if (i == 42) {
+ // i = 111
+ // }
+ DomItem conditional = statements.index(3);
+ DomItem condition = conditional.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(condition.field(Fields::right).field(Fields::value).value().toDouble(), 42);
+
+ DomItem consequence = conditional.field(Fields::consequence);
+ auto blockSemanticScope = consequence.semanticScope();
+ QVERIFY(blockSemanticScope);
+
+ QCOMPARE(consequence.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(consequence.field(Fields::statements).indexes(), 1);
+ DomItem consequence1 = consequence.field(Fields::statements).index(0);
+ QCOMPARE(consequence1.field(Fields::left).field(Fields::identifier).value().toString(),
+ u"i"_s);
+ QCOMPARE(consequence1.field(Fields::right).field(Fields::value).value().toDouble(),
+ 111);
+
+ QCOMPARE(conditional.field(Fields::alternative).internalKind(), DomType::Empty);
+ }
+ {
+ // if (i == 746) {
+ // i = 123
+ // } else {
+ // i = 456
+ // }
+
+ DomItem conditional = statements.index(4);
+ DomItem condition = conditional.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(condition.field(Fields::right).field(Fields::value).value().toDouble(), 746);
+
+ {
+ DomItem consequence = conditional.field(Fields::consequence);
+ QCOMPARE(consequence.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(consequence.field(Fields::statements).indexes(), 1);
+ DomItem consequence1 = consequence.field(Fields::statements).index(0);
+ QCOMPARE(consequence1.field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"i"_s);
+ QCOMPARE(consequence1.field(Fields::right).field(Fields::value).value().toDouble(),
+ 123);
+ }
+
+ {
+ DomItem alternative = conditional.field(Fields::alternative);
+ auto blockSemanticScope = alternative.semanticScope();
+ QVERIFY(blockSemanticScope);
+
+ QCOMPARE(alternative.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(alternative.field(Fields::statements).indexes(), 1);
+ DomItem alternative1 = alternative.field(Fields::statements).index(0);
+ QCOMPARE(alternative1.field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"i"_s);
+ QCOMPARE(alternative1.field(Fields::right).field(Fields::value).value().toDouble(),
+ 456);
+ }
+ }
+ }
+
+ void returnStatement()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/returnStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.path(".methods[\"returningFunction\"][0].body.scriptElement");
+ QCOMPARE(block.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(block.field(Fields::statements).indexes(), 1);
+ DomItem conditional = block.field(Fields::statements).index(0);
+ DomItem consequence = conditional.field(Fields::consequence);
+ QCOMPARE(consequence.internalKind(), DomType::ScriptReturnStatement);
+ {
+ DomItem returnValue = consequence.field(Fields::expression);
+ QCOMPARE(returnValue.internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(returnValue.field(Fields::value).value().toDouble(), 123);
+ }
+ DomItem alternative = conditional.field(Fields::alternative);
+ QCOMPARE(alternative.internalKind(), DomType::ScriptReturnStatement);
+ {
+ DomItem returnValue = alternative.field(Fields::expression);
+ QCOMPARE(returnValue.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(returnValue.field(Fields::left).field(Fields::value).value().toDouble(), 1);
+ QCOMPARE(returnValue.field(Fields::right).field(Fields::value).value().toDouble(), 2);
+ }
+ }
+
+ void forStatements()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/forStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement");
+ DomItem statements = block.field(Fields::statements);
+ DomItem forLoop = statements.index(1);
+ {
+ // for ( >> let i = 0 << ; i < 100; i = i + 1) {
+ DomItem declarationList =
+ forLoop.field(Fields::declarations).field(Fields::declarations);
+ QCOMPARE(declarationList.internalKind(), DomType::List);
+
+ QCOMPARE(declarationList.indexes(), 1);
+ DomItem declaration = declarationList.index(0);
+
+ QCOMPARE(declaration.internalKind(), DomType::ScriptVariableDeclarationEntry);
+ QCOMPARE(declaration.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+
+ QCOMPARE(declaration.field(Fields::identifier).value().toString(), "i");
+ QCOMPARE(declaration.field(Fields::initializer).field(Fields::value).value().toDouble(),
+ 0);
+ }
+ {
+ // for ( let i = 0; >> i < 100 <<; i = i + 1) {
+ DomItem condition = forLoop.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptBinaryExpression);
+
+ QCOMPARE(condition.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(condition.field(Fields::left).field(Fields::identifier).value().toString(),
+ "i");
+
+ QCOMPARE(condition.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(condition.field(Fields::right).field(Fields::value).value().toDouble(), 100);
+ }
+ {
+ // for ( let i = 0; i < 100; >> i = i + 1 << ) {
+ DomItem expression = forLoop.field(Fields::expression);
+ QCOMPARE(expression.internalKind(), DomType::ScriptBinaryExpression);
+ DomItem left = expression.field(Fields::left);
+ QCOMPARE(left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left.field(Fields::identifier).value().toString(), "i");
+
+ // for ( let i = 0; i < 100; i = >> i + 1 << ) {
+ DomItem right = expression.field(Fields::right);
+ QCOMPARE(right.internalKind(), DomType::ScriptBinaryExpression);
+ DomItem left2 = right.field(Fields::left);
+ QCOMPARE(left2.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left2.field(Fields::identifier).value().toString(), "i");
+
+ DomItem right2 = right.field(Fields::right);
+ QCOMPARE(right2.internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(right2.field(Fields::value).value().toDouble(), 1);
+ }
+ {
+ // test the body of the for-loop
+ DomItem body = forLoop.field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptBlockStatement);
+ auto blockSemanticScope = body.semanticScope();
+ QVERIFY(blockSemanticScope);
+
+ DomItem statementList = body.field(Fields::statements);
+ QCOMPARE(statementList.indexes(), 2);
+ {
+ // >> sum = sum + 1 <<
+ DomItem binaryExpression = statementList.index(0);
+ QCOMPARE(binaryExpression.internalKind(), DomType::ScriptBinaryExpression);
+
+ DomItem left = binaryExpression.field(Fields::left);
+ QCOMPARE(left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left.field(Fields::identifier).value().toString(), "sum");
+
+ // sum = >> sum + 1 <<
+ DomItem right = binaryExpression.field(Fields::right);
+ QCOMPARE(right.internalKind(), DomType::ScriptBinaryExpression);
+
+ DomItem left2 = right.field(Fields::left);
+ QCOMPARE(left2.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left2.field(Fields::identifier).value().toString(), "sum");
+
+ DomItem right2 = right.field(Fields::right);
+ QCOMPARE(right2.internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(right2.field(Fields::value).value().toDouble(), 1);
+ }
+
+ {
+ // >> for (;;) <<
+ // i = 42
+ DomItem innerForLoop = statementList.index(1);
+ QCOMPARE(innerForLoop.internalKind(), DomType::ScriptForStatement);
+ QCOMPARE(innerForLoop.field(Fields::declarations).indexes(), 0);
+ QVERIFY(!innerForLoop.field(Fields::initializer));
+ QVERIFY(!innerForLoop.field(Fields::condition));
+ QVERIFY(!innerForLoop.field(Fields::expression));
+ QVERIFY(innerForLoop.field(Fields::body));
+
+ // for (;;)
+ // >> i = 42 <<
+ DomItem expression = innerForLoop.field(Fields::body);
+ QCOMPARE(expression.internalKind(), DomType::ScriptBinaryExpression);
+
+ DomItem left = expression.field(Fields::left);
+ QCOMPARE(left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left.field(Fields::identifier).value().toString(), "i");
+
+ DomItem right = expression.field(Fields::right);
+ QCOMPARE(right.internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(right.field(Fields::value).value().toDouble(), 42);
+ }
+ }
+ }
+
+ void nullStatements()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/nullStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.methods()
+ .key(u"testForNull"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement);
+
+ QVERIFY(block);
+ // First for
+ DomItem statements = block.field(Fields::statements);
+ QCOMPARE(statements.internalKind(), DomType::List);
+ QVERIFY(statements.index(0).field(Fields::body).field(Fields::statements).internalKind()
+ != DomType::Empty);
+ QCOMPARE(statements.index(0).field(Fields::body).field(Fields::statements).length(), 0);
+
+ // Second for
+ DomItem secondFor = statements.index(1).field(Fields::body);
+ QVERIFY(secondFor.internalKind() == DomType::ScriptIdentifierExpression);
+ QCOMPARE(secondFor.field(Fields::identifier).value().toString(), u"x"_s);
+
+ // Empty block
+ QVERIFY(statements.index(3).field(Fields::statements).internalKind() != DomType::Empty);
+ QCOMPARE(statements.index(3).field(Fields::statements).length(), 0);
+ }
+
+ void deconstruction()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/callExpressions.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"deconstruct"_s).index(0);
+ DomItem statement = method.field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(0);
+ QCOMPARE(statement.internalKind(), DomType::ScriptVariableDeclaration);
+ QCOMPARE(statement.field(Fields::declarations).indexes(), 3);
+
+ {
+ DomItem entry = statement.field(Fields::declarations).index(0);
+ QVERIFY(!entry.field(Fields::identifier));
+ DomItem deconstructedProperty =
+ entry.field(Fields::bindingElement).field(Fields::properties);
+
+ QCOMPARE(deconstructedProperty.indexes(), 1);
+ QCOMPARE(deconstructedProperty.index(0).field(Fields::name).value().toString(), u"a"_s);
+
+ DomItem initializer = entry.field(Fields::initializer).field(Fields::properties);
+ QCOMPARE(initializer.indexes(), 2);
+
+ DomItem a32 = initializer.index(0);
+ QCOMPARE(a32.field(Fields::initializer).value().toInteger(), 32);
+ QCOMPARE(a32.field(Fields::name).value().toString(), "a");
+
+ DomItem b42 = initializer.index(1);
+ QCOMPARE(b42.field(Fields::initializer).value().toInteger(), 42);
+ QCOMPARE(b42.field(Fields::name).value().toString(), "b");
+ }
+ {
+ DomItem entry = statement.field(Fields::declarations).index(1);
+ QVERIFY(!entry.field(Fields::identifier));
+ DomItem deconstructedProperty =
+ entry.field(Fields::bindingElement).field(Fields::properties);
+
+ QCOMPARE(deconstructedProperty.indexes(), 2);
+ QCOMPARE(deconstructedProperty.index(0).field(Fields::name).value().toString(), u"b"_s);
+ QCOMPARE(deconstructedProperty.index(1).field(Fields::name).value().toString(), u"c"_s);
+
+ DomItem initializer = entry.field(Fields::initializer).field(Fields::properties);
+ QCOMPARE(initializer.indexes(), 2);
+
+ DomItem a32 = initializer.index(0);
+ QCOMPARE(a32.field(Fields::initializer).value().toInteger(), 32);
+ QCOMPARE(a32.field(Fields::name).value().toString(), "b");
+
+ DomItem b42 = initializer.index(1);
+ QCOMPARE(b42.field(Fields::initializer).value().toInteger(), 42);
+ QCOMPARE(b42.field(Fields::name).value().toString(), "c");
+ }
+ {
+ DomItem entry = statement.field(Fields::declarations).index(2);
+ QVERIFY(!entry.field(Fields::identifier));
+ DomItem deconstructedProperty =
+ entry.field(Fields::bindingElement).field(Fields::elements);
+
+ QCOMPARE(deconstructedProperty.indexes(), 3);
+ QCOMPARE(deconstructedProperty.index(0).field(Fields::identifier).value().toString(),
+ u"d"_s);
+ QCOMPARE(deconstructedProperty.index(1).field(Fields::identifier).value().toString(),
+ u"e"_s);
+ QCOMPARE(deconstructedProperty.index(2).field(Fields::identifier).value().toString(),
+ u"f"_s);
+
+ DomItem initializer = entry.field(Fields::initializer).field(Fields::elements);
+ QCOMPARE(initializer.indexes(), 3);
+
+ for (int i = 0; i < initializer.indexes(); ++i) {
+ QCOMPARE(initializer.index(i).field(Fields::initializer).value().toInteger(),
+ (i + 1) * 111);
+ }
+ }
+ {
+ DomItem statement = method.field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(1);
+ DomItem listWithElision = statement.field(Fields::declarations)
+ .index(0)
+ .field(Fields::initializer)
+ .field(Fields::elements);
+ const int listElements = 6;
+ const int elisionCount = 4;
+ QCOMPARE(listWithElision.indexes(), listElements);
+ for (int i = 0; i < elisionCount; ++i) {
+ QCOMPARE(listWithElision.index(i).internalKind(), DomType::ScriptElision);
+ }
+ QCOMPARE(listWithElision.index(elisionCount).internalKind(), DomType::ScriptArrayEntry);
+ QCOMPARE(listWithElision.index(elisionCount + 1).internalKind(),
+ DomType::ScriptElision);
+ }
+ }
+
+ void callExpressions()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/callExpressions.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ {
+ DomItem p1 = rootQmlObject.path(".bindings[\"p\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptCallExpression);
+ QCOMPARE(p1.field(Fields::callee).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::callee).field(Fields::identifier).value().toString(), "f");
+ QCOMPARE(p1.field(Fields::arguments).internalKind(), DomType::List);
+ QCOMPARE(p1.field(Fields::arguments).indexes(), 0);
+ }
+
+ {
+ DomItem p2 = rootQmlObject.path(".bindings[\"p2\"][0].value.scriptElement");
+ QCOMPARE(p2.internalKind(), DomType::ScriptCallExpression);
+ QCOMPARE(p2.field(Fields::callee).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p2.field(Fields::callee).field(Fields::identifier).value().toString(), "f");
+
+ DomItem p2List = p2.field(Fields::arguments);
+ QCOMPARE(p2List.indexes(), 20);
+ for (int i = 0; i < p2List.indexes(); ++i) {
+ QCOMPARE(p2List.index(i).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(p2List.index(i).field(Fields::value).value().toInteger(), i + 1);
+ }
+ }
+
+ {
+ DomItem p3 = rootQmlObject.path(".bindings[\"p3\"][0].value.scriptElement");
+ QCOMPARE(p3.internalKind(), DomType::ScriptCallExpression);
+ QCOMPARE(p3.field(Fields::callee).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p3.field(Fields::callee).field(Fields::identifier).value().toString(), "evil");
+
+ DomItem p3List = p3.field(Fields::arguments);
+ QCOMPARE(p3List.indexes(), 3);
+
+ DomItem firstArg = p3List.index(0);
+ QCOMPARE(firstArg.internalKind(), DomType::ScriptObject);
+
+ {
+ DomItem helloWorld = firstArg.field(Fields::properties).index(0);
+ QCOMPARE(helloWorld.internalKind(), DomType::ScriptProperty);
+ QCOMPARE(helloWorld.field(Fields::initializer).value().toString(), "World");
+ QCOMPARE(helloWorld.field(Fields::name).value().toString(), "hello");
+ }
+
+ {
+ DomItem yyyy = firstArg.field(Fields::properties).index(1);
+ QCOMPARE(yyyy.internalKind(), DomType::ScriptProperty);
+ QCOMPARE(yyyy.field(Fields::initializer).value().toString(), "yyy");
+ QCOMPARE(yyyy.field(Fields::name).value().toString(), "y");
+ }
+
+ DomItem secondArg = p3List.index(1);
+ QCOMPARE(secondArg.internalKind(), DomType::ScriptArray);
+ {
+ for (int i = 0; i < 3; ++i) {
+ DomItem current = secondArg.field(Fields::elements).index(i);
+ QCOMPARE(current.internalKind(), DomType::ScriptArrayEntry);
+ QCOMPARE(current.field(Fields::initializer).value().toInteger(), i + 1);
+ }
+ }
+
+ DomItem thirdArg = p3List.index(2);
+ QCOMPARE(thirdArg.internalKind(), DomType::ScriptObject);
+ {
+ DomItem is = thirdArg.field(Fields::properties).index(0);
+ QCOMPARE(is.internalKind(), DomType::ScriptProperty);
+ QCOMPARE(is.field(Fields::name).value().toString(), "is");
+ DomItem initializer = is.field(Fields::initializer).field(Fields::properties);
+
+ QCOMPARE(initializer.index(0).field(Fields::name).value().toString(), "a");
+ QCOMPARE(initializer.index(0).field(Fields::initializer).value().toInteger(), 111);
+
+ QCOMPARE(initializer.index(1).field(Fields::name).value().toString(), "lot");
+ QCOMPARE(initializer.index(1).field(Fields::initializer).value().toInteger(), 222);
+
+ QCOMPARE(initializer.index(2).field(Fields::name).value().toString(), "of");
+ QCOMPARE(initializer.index(2).field(Fields::initializer).value().toInteger(), 333);
+
+ QCOMPARE(initializer.index(3).field(Fields::name).value().toString(), "fun");
+ QCOMPARE(initializer.index(3).field(Fields::initializer).value().toInteger(), 444);
+
+ QCOMPARE(initializer.index(4).field(Fields::name).value().toString(), "really");
+ QCOMPARE(initializer.index(4)
+ .field(Fields::initializer)
+ .field(Fields::elements)
+ .index(0)
+ .field(Fields::initializer)
+ .value()
+ .toString(),
+ "!");
+ }
+ }
+
+ {
+ DomItem functionFs = rootQmlObject.field(Fields::methods).key(u"f");
+ QCOMPARE(functionFs.indexes(), 1);
+ DomItem functionF = functionFs.index(0);
+
+ QVERIFY(!functionF.semanticScope().isNull());
+ QVERIFY(functionF.semanticScope()->ownJSIdentifier("helloF").has_value());
+ DomItem parameters = functionF.field(Fields::parameters);
+ std::vector<QString> parameterNames = {
+ u"q"_s, u"w"_s, u"e"_s, u"r"_s, u"t"_s, u"y"_s
+ };
+ QCOMPARE(parameterNames.size(), (size_t)parameters.indexes());
+
+ for (size_t i = 0; i < parameterNames.size(); ++i) {
+ DomItem currentParameter = parameters.index(i);
+ QCOMPARE(currentParameter.field(Fields::name).value().toString(),
+ parameterNames[i]);
+ }
+ }
+
+ {
+ DomItem functionFWithDefaults =
+ rootQmlObject.field(Fields::methods).key(u"fWithDefault");
+ QCOMPARE(functionFWithDefaults.indexes(), 1);
+ DomItem functionFWithDefault = functionFWithDefaults.index(0);
+ QVERIFY(!functionFWithDefault.semanticScope().isNull());
+ QVERIFY(functionFWithDefault.semanticScope()->ownJSIdentifier(u"helloFWithDefault"_s));
+ DomItem parameters = functionFWithDefault.field(Fields::parameters);
+
+ const std::vector<QString> parameterNames = { u"q"_s, u"w"_s, u"e"_s,
+ u"r"_s, u"t"_s, u"y"_s };
+ const QSet<QString> parameterWithoutDefault = { u"e"_s, u"t"_s };
+
+ QCOMPARE(parameterNames.size(), (size_t)parameters.indexes());
+
+ for (size_t i = 0; i < parameterNames.size(); ++i) {
+ DomItem currentParameter = parameters.index(i);
+ QCOMPARE(currentParameter.field(Fields::name).value().toString(),
+ parameterNames[i]);
+
+ DomItem scriptElement =
+ currentParameter.field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(scriptElement.internalKind(), DomType::ScriptFormalParameter);
+ QCOMPARE(scriptElement.field(Fields::identifier).value().toString(),
+ parameterNames[i]);
+ if (!parameterWithoutDefault.contains(parameterNames[i])) {
+ QCOMPARE((size_t)scriptElement.field(Fields::initializer).value().toInteger(),
+ i + 1);
+ }
+ }
+ }
+
+ {
+ // note: no need to check the inside of ScriptObject and ScriptArray as those are
+ // already tested in deconstruction().
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"evil"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QVERIFY(method.semanticScope()->ownJSIdentifier("helloEvil").has_value());
+ DomItem parameters = method.field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 3);
+
+ {
+ DomItem parameter1 =
+ parameters.index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter1.internalKind(), DomType::ScriptFormalParameter);
+
+ DomItem objectPattern = parameter1.field(Fields::bindingElement);
+ QCOMPARE(objectPattern.internalKind(), DomType::ScriptObject);
+ QCOMPARE(objectPattern.field(Fields::properties).indexes(), 2);
+ }
+ {
+ DomItem parameter2 =
+ parameters.index(1).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter2.internalKind(), DomType::ScriptFormalParameter);
+
+ DomItem objectPattern = parameter2.field(Fields::bindingElement);
+ QCOMPARE(objectPattern.internalKind(), DomType::ScriptArray);
+ QCOMPARE(objectPattern.field(Fields::elements).indexes(), 3);
+ }
+ {
+ DomItem parameter3 =
+ parameters.index(2).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter3.internalKind(), DomType::ScriptFormalParameter);
+
+ DomItem objectPattern = parameter3.field(Fields::bindingElement);
+ QCOMPARE(objectPattern.internalKind(), DomType::ScriptObject);
+ QCOMPARE(objectPattern.field(Fields::properties).indexes(), 3);
+
+ DomItem parameter3DefaultValue = parameter3.field(Fields::initializer);
+ QCOMPARE(parameter3DefaultValue.internalKind(), DomType::ScriptObject);
+
+ DomItem objectPatternDefaultValue =
+ parameter3DefaultValue.field(Fields::properties);
+ QCOMPARE(objectPatternDefaultValue.indexes(), 3);
+ }
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"marmelade"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QVERIFY(method.semanticScope()->ownJSIdentifier(u"helloMarmelade"_s));
+ DomItem parameters = method.field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 1);
+ {
+ DomItem parameter1 =
+ parameters.index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter1.internalKind(), DomType::ScriptFormalParameter);
+ QCOMPARE(parameter1.field(Fields::identifier).value().toString(), "onTheBread");
+ }
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"marmelade2"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QVERIFY(method.semanticScope()->ownJSIdentifier(u"helloMarmelade2"_s));
+ DomItem parameters = method.field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 3);
+ {
+ DomItem parameter3 =
+ parameters.index(2).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter3.internalKind(), DomType::ScriptFormalParameter);
+ QCOMPARE(parameter3.field(Fields::identifier).value().toString(), "onTheBread");
+ }
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"withTypes"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QCOMPARE(method.semanticScope()->scopeType(),
+ QQmlJSScope::ScopeType::JSFunctionScope);
+ DomItem parameters = method.field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 2);
+ QCOMPARE(parameters.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .field(Fields::type)
+ .field(Fields::typeName)
+ .value()
+ .toString(),
+ "int");
+ QCOMPARE(parameters.index(1)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .field(Fields::type)
+ .field(Fields::typeName)
+ .value()
+ .toString(),
+ "MyType");
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"empty"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QCOMPARE(method.semanticScope()->scopeType(),
+ QQmlJSScope::ScopeType::JSFunctionScope);
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"mySignal"_s).index(0);
+ // signals contain the scope of the QML object owning them
+ QVERIFY(!method.semanticScope().isNull());
+ QCOMPARE(method.semanticScope()->scopeType(), QQmlJSScope::ScopeType::QMLScope);
+ }
+ }
+
+ void fieldMemberExpression()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/fieldMemberExpression.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ {
+ DomItem p1 = rootQmlObject.path(".bindings[\"p1\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::identifier).value().toString(), "p");
+ }
+
+ {
+ DomItem p1Qualified =
+ rootQmlObject.path(".bindings[\"p1Qualified\"][0].value.scriptElement");
+ fieldMemberExpressionHelper(p1Qualified, QStringList{ u"p"_s });
+ QCOMPARE(p1Qualified.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1Qualified.field(Fields::left).field(Fields::identifier).value().toString(),
+ "root");
+ }
+ {
+ // property var p1Bracket: root["p"]
+ DomItem p1 = rootQmlObject.path(".bindings[\"p1Bracket\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(p1.field(Fields::operation).value().toInteger(),
+ ScriptElements::BinaryExpression::ArrayMemberAccess);
+
+ QCOMPARE(p1.field(Fields::left).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::left).field(Fields::identifier).value().toString(), "root");
+
+ QCOMPARE(p1.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(p1.field(Fields::right).field(Fields::value).value().toString(), "p");
+ }
+ {
+ // property var p1Index: root[42]
+ DomItem p1 = rootQmlObject.path(".bindings[\"p1Index\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(p1.field(Fields::operation).value().toInteger(),
+ ScriptElements::BinaryExpression::ArrayMemberAccess);
+
+ QCOMPARE(p1.field(Fields::left).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::left).field(Fields::identifier).value().toString(), "root");
+
+ QCOMPARE(p1.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(p1.field(Fields::right).field(Fields::value).value().toInteger(), 42);
+ }
+ {
+ // property var p1Key: root[p]
+ DomItem p1 = rootQmlObject.path(".bindings[\"p1Key\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(p1.field(Fields::operation).value().toInteger(),
+ ScriptElements::BinaryExpression::ArrayMemberAccess);
+
+ QCOMPARE(p1.field(Fields::left).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::left).field(Fields::identifier).value().toString(), "root");
+
+ QCOMPARE(p1.field(Fields::right).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::right).field(Fields::identifier).value().toString(), "p");
+ }
+ {
+ DomItem p1Qualified =
+ rootQmlObject.path(".bindings[\"p1Qualified\"][0].value.scriptElement");
+ fieldMemberExpressionHelper(p1Qualified, QStringList{ u"p"_s });
+ QCOMPARE(p1Qualified.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1Qualified.field(Fields::left).field(Fields::identifier).value().toString(),
+ "root");
+ }
+
+ {
+ DomItem p3 = rootQmlObject.path(".bindings[\"p3\"][0].value.scriptElement");
+ fieldMemberExpressionHelper(p3, QStringList{ u"property2"_s, u"p"_s });
+ DomItem p3Left = p3.field(Fields::left).field(Fields::left);
+ QCOMPARE(p3Left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p3Left.field(Fields::identifier).value().toString(), "property1");
+ }
+
+ {
+ DomItem p3Qualified =
+ rootQmlObject.path(".bindings[\"p3Qualified\"][0].value.scriptElement");
+ fieldMemberExpressionHelper(p3Qualified,
+ QStringList{ u"property1"_s, u"property2"_s, u"p"_s });
+ DomItem p3Left =
+ p3Qualified.field(Fields::left).field(Fields::left).field(Fields::left);
+ QCOMPARE(p3Left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p3Left.field(Fields::identifier).value().toString(), "root");
+ }
+ }
+
+ void switchStatement()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/switchStatement.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+
+ {
+ DomItem statements = rootQmlObject.methods()
+ .key(u"switchStatement")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements);
+ QVERIFY(statements);
+ QCOMPARE(statements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ QCOMPARE(statements.index(1).internalKind(), DomType::ScriptVariableDeclaration);
+
+ DomItem firstSwitch = statements.index(2);
+ QVERIFY(firstSwitch);
+ QCOMPARE(firstSwitch.internalKind(), DomType::ScriptSwitchStatement);
+ QCOMPARE(firstSwitch.field(Fields::caseBlock).internalKind(), DomType::ScriptCaseBlock);
+ QCOMPARE(firstSwitch.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(firstSwitch.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"animals");
+
+ DomItem caseClauses = firstSwitch.field(Fields::caseBlock).field(Fields::caseClauses);
+ QVERIFY(caseClauses);
+ QCOMPARE(caseClauses.index(0).internalKind(), DomType::ScriptCaseClause);
+ QCOMPARE(caseClauses.index(0).field(Fields::expression).internalKind(),
+ DomType::ScriptLiteral);
+ QCOMPARE(caseClauses.index(0).field(Fields::expression).value().toString(), u"cat");
+
+ DomItem innerSwitch = caseClauses.index(0).field(Fields::statements).index(0);
+ QVERIFY(innerSwitch);
+ QCOMPARE(innerSwitch.internalKind(), DomType::ScriptSwitchStatement);
+ QCOMPARE(innerSwitch.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(innerSwitch.field(Fields::expression).value().toString(), u"no");
+
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(0)
+ .internalKind(),
+ DomType::ScriptCaseClause);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(0)
+ .field(Fields::expression)
+ .internalKind(),
+ DomType::ScriptLiteral);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toInteger(),
+ 0);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(0)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toString(),
+ u"patron");
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(1)
+ .internalKind(),
+ DomType::ScriptCaseClause);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(1)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toString(),
+ u"mafik");
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::defaultClause)
+ .internalKind(),
+ DomType::ScriptDefaultClause);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::defaultClause)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toString(),
+ u"none");
+
+ // case "dog"
+ DomItem caseDogBlock = firstSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(1)
+ .field(Fields::statements)
+ .index(0);
+ QVERIFY(caseDogBlock);
+ // Check if semantic scope is correctly created for the CaseClause
+ auto blockSemanticScope = caseDogBlock.semanticScope();
+ QVERIFY(blockSemanticScope);
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"name"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"another"_s));
+
+ // Default clause
+ DomItem defaultClause =
+ firstSwitch.field(Fields::caseBlock).field(Fields::defaultClause);
+ QVERIFY(defaultClause);
+ QCOMPARE(defaultClause.internalKind(), DomType::ScriptDefaultClause);
+ QCOMPARE(defaultClause.field(Fields::statements).index(0).internalKind(),
+ DomType::ScriptReturnStatement);
+ QCOMPARE(defaultClause.field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .internalKind(),
+ DomType::ScriptLiteral);
+ QCOMPARE(defaultClause.field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toString(),
+ u"monster");
+
+ // case "moreCases!"
+ const DomItem moreCases = firstSwitch.field(Fields::caseBlock)
+ .field(Fields::moreCaseClauses)
+ .index(0)
+ .field(Fields::statements)
+ .index(0);
+
+ QCOMPARE(moreCases.internalKind(),
+ DomType::ScriptReturnStatement);
+ QCOMPARE(moreCases.field(Fields::expression).value().toString(), u"moreCaseClauses?");
+ }
+ }
+
+ void iterationStatements()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/iterationStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ QVERIFY(rootQmlObject);
+ DomItem methods = rootQmlObject.methods();
+ QVERIFY(methods);
+
+ {
+ // while statement
+ DomItem whileTest = methods.key("whileStatement")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(1);
+ QVERIFY(whileTest);
+ QCOMPARE(whileTest.internalKind(), DomType::ScriptWhileStatement);
+ auto whileScope = whileTest.semanticScope();
+ QVERIFY(whileScope);
+ DomItem whileBody = whileTest.field(Fields::body);
+ QVERIFY(whileBody);
+ QCOMPARE(whileBody.internalKind(), DomType::ScriptBlockStatement);
+ auto scope = whileBody.semanticScope();
+ QVERIFY(scope);
+ QCOMPARE(whileBody.field(Fields::statements).index(0).internalKind(),
+ DomType::ScriptBinaryExpression); // i = i - 1
+ QCOMPARE(
+ whileBody.field(Fields::statements).index(0).field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(whileBody.field(Fields::statements)
+ .index(0)
+ .field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"i");
+ DomItem whileExpression = whileTest.field(Fields::expression);
+ QVERIFY(whileExpression);
+ QCOMPARE(whileExpression.internalKind(), DomType::ScriptBinaryExpression); // i > 0
+ QCOMPARE(whileExpression.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(whileExpression.field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"i");
+ QCOMPARE(whileExpression.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(whileExpression.field(Fields::right)
+ .field(Fields::identifier)
+ .value()
+ .toInteger(),
+ 0);
+
+ DomItem singleLineWhile = methods.key("whileStatement")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(2);
+ QVERIFY(singleLineWhile);
+ QCOMPARE(singleLineWhile.internalKind(), DomType::ScriptWhileStatement);
+ DomItem singleLineWhileBody = singleLineWhile.field(Fields::body);
+ QVERIFY(singleLineWhileBody);
+ QCOMPARE(singleLineWhileBody.internalKind(), DomType::ScriptBinaryExpression);
+ auto singleLineWhileScope = singleLineWhile.semanticScope();
+ QVERIFY(singleLineWhileScope);
+ QVERIFY(singleLineWhileScope->jsIdentifier("i")); // i is in the parent scope
+ }
+
+ {
+ // do-while statement
+ DomItem doWhile = methods.key("doWhile")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(1);
+ QVERIFY(doWhile);
+ QCOMPARE(doWhile.internalKind(), DomType::ScriptDoWhileStatement);
+
+ auto doWhileScope = doWhile.semanticScope();
+ QVERIFY(doWhileScope);
+ DomItem doWhileBody = doWhile.field(Fields::body);
+ QVERIFY(doWhileBody);
+ auto doWhileBodyScope = doWhileBody.semanticScope();
+ QVERIFY(doWhileBodyScope);
+ QVERIFY(doWhileBodyScope->ownJSIdentifier("b")); // const b = a
+ QCOMPARE(doWhileBody.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(doWhileBody.field(Fields::statements).index(0).internalKind(),
+ DomType::ScriptVariableDeclaration); // const b = a
+ QCOMPARE(doWhileBody.field(Fields::statements).index(1).internalKind(),
+ DomType::ScriptBinaryExpression); // a = a - 1
+
+ DomItem doWhileExpression = doWhile.field(Fields::expression);
+ QVERIFY(doWhileExpression);
+ QCOMPARE(doWhileExpression.internalKind(), DomType::ScriptBinaryExpression); // a > 0
+ QCOMPARE(doWhileExpression.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(doWhileExpression.field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a");
+ QCOMPARE(doWhileExpression.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(doWhileExpression.field(Fields::right)
+ .field(Fields::identifier)
+ .value()
+ .toInteger(),
+ 0);
+ DomItem singleDoWhile = methods.key("doWhile")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(2);
+ auto singleDoWhileScope = singleDoWhile.semanticScope();
+ QVERIFY(singleDoWhileScope);
+ QVERIFY(singleDoWhileScope->jsIdentifier("a")); // a = a - 1
+ }
+
+ {
+ // for..of
+ DomItem statements = methods.key("forOf")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements);
+ QVERIFY(statements);
+ QCOMPARE(statements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ DomItem outerForEach = statements.index(1);
+ QVERIFY(outerForEach);
+ QCOMPARE(outerForEach.internalKind(), DomType::ScriptForEachStatement);
+ DomItem bindingElements =
+ outerForEach.field(Fields::bindingElement).field(Fields::bindingElement);
+ QVERIFY(bindingElements);
+ QCOMPARE(bindingElements.internalKind(), DomType::ScriptArray);
+ QCOMPARE(bindingElements.field(Fields::elements).length(), 2);
+ QCOMPARE(bindingElements.field(Fields::elements)
+ .index(1)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "b");
+ QCOMPARE(outerForEach.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(outerForEach.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"iterable");
+ DomItem forEachStatements = outerForEach.field(Fields::body).field(Fields::statements);
+ QCOMPARE(forEachStatements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ QCOMPARE(forEachStatements.index(1).internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(forEachStatements.index(2).internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(forEachStatements.index(3).internalKind(), DomType::ScriptForEachStatement);
+ DomItem firstForEach = forEachStatements.index(1);
+ QVERIFY(firstForEach);
+ DomItem bindingElement =
+ firstForEach.field(Fields::bindingElement).field(Fields::bindingElement);
+ QCOMPARE(bindingElement.internalKind(), DomType::ScriptArray);
+
+ QCOMPARE(bindingElement.field(Fields::elements).length(), 4);
+ QCOMPARE(bindingElement.field(Fields::elements)
+ .index(0)
+ .field(Fields::identifier)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "a1");
+ DomItem secondForEach = forEachStatements.index(2);
+ QCOMPARE(secondForEach.field(Fields::bindingElement).internalKind(),
+ DomType::ScriptPattern);
+ QCOMPARE(secondForEach.field(Fields::bindingElement)
+ .field(Fields::identifier)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "k");
+ QCOMPARE(secondForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(secondForEach.field(Fields::expression).internalKind(), DomType::ScriptArray);
+ QVERIFY(secondForEach.field(Fields::body));
+ QCOMPARE(secondForEach.field(Fields::body).internalKind(),
+ DomType::ScriptBlockStatement);
+ DomItem thirdForEach = forEachStatements.index(3);
+ QCOMPARE(thirdForEach.field(Fields::bindingElement).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(thirdForEach.field(Fields::bindingElement).value().toString(), "t");
+
+ QCOMPARE(thirdForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(thirdForEach.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(thirdForEach.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a");
+ QVERIFY(thirdForEach.field(Fields::body));
+ QCOMPARE(thirdForEach.field(Fields::body).internalKind(),
+ DomType::ScriptBlockStatement);
+
+ DomItem forthForEach = forEachStatements.index(3);
+ QVERIFY(forthForEach);
+ auto forthForEachScope = forthForEach.semanticScope();
+ QVERIFY(forthForEachScope);
+ QVERIFY(forthForEachScope->jsIdentifier("t")); // t lives in parent scope
+ }
+
+ {
+ // for..in
+ DomItem statements = methods.key("forIn")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements);
+ QVERIFY(statements);
+ QCOMPARE(statements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ DomItem outerForEach = statements.index(1);
+ QVERIFY(outerForEach);
+ auto outerForEachScope = outerForEach.semanticScope();
+ QVERIFY(outerForEachScope);
+ QVERIFY(outerForEachScope->jsIdentifier("a")); // var [a,b,c,d]
+ QVERIFY(outerForEachScope->jsIdentifier("b"));
+ QVERIFY(outerForEachScope->jsIdentifier("c"));
+ QVERIFY(outerForEachScope->jsIdentifier("d"));
+ QCOMPARE(outerForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(statements.index(1).internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(outerForEach.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "enumerable");
+ auto outerForEachBodyScope = outerForEach.field(Fields::body).semanticScope();
+ QVERIFY(outerForEachBodyScope);
+ QVERIFY(outerForEachBodyScope->ownJSIdentifier("t")); // let t
+ DomItem forInStatements = outerForEach.field(Fields::body).field(Fields::statements);
+ QCOMPARE(forInStatements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ QCOMPARE(forInStatements.index(1).internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(forInStatements.index(2).internalKind(), DomType::ScriptForEachStatement);
+ DomItem firstForEach = forInStatements.index(1);
+ QVERIFY(firstForEach);
+ auto firstForEachScope = firstForEach.semanticScope();
+ QVERIFY(firstForEachScope);
+ QVERIFY(firstForEachScope->jsIdentifier("t"));
+ QCOMPARE(firstForEach.field(Fields::bindingElement).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(firstForEach.field(Fields::bindingElement)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "t");
+ QCOMPARE(firstForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(firstForEach.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(firstForEach.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "enumerable");
+ QVERIFY(firstForEach.field(Fields::body));
+ QCOMPARE(firstForEach.field(Fields::body).internalKind(),
+ DomType::ScriptBlockStatement);
+
+ DomItem secondForEach = forInStatements.index(2);
+ QVERIFY(secondForEach);
+ auto secondForEachScope = secondForEach.semanticScope();
+ QVERIFY(secondForEachScope);
+ QVERIFY(secondForEachScope->ownJSIdentifier("a1")); // const [a1,,a2,...rest]
+ QVERIFY(secondForEachScope->ownJSIdentifier("a2"));
+ QVERIFY(secondForEachScope->ownJSIdentifier("rest"));
+
+ DomItem bindingElement =
+ secondForEach.field(Fields::bindingElement).field(Fields::bindingElement);
+ QCOMPARE(bindingElement.internalKind(), DomType::ScriptArray);
+ QCOMPARE(bindingElement.field(Fields::elements)
+ .index(3)
+ .field(Fields::identifier)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "rest");
+ QCOMPARE(secondForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(secondForEach.field(Fields::expression).internalKind(),
+ DomType::ScriptBinaryExpression);
+ QVERIFY(secondForEach.field(Fields::body));
+ QCOMPARE(secondForEach.field(Fields::body).internalKind(),
+ DomType::ScriptBlockStatement);
+ DomItem thirdForEach = forInStatements.index(3);
+ QVERIFY(thirdForEach);
+ auto thirdForEachScope = thirdForEach.semanticScope();
+ QVERIFY(thirdForEachScope);
+ QVERIFY(thirdForEachScope->ownJSIdentifier("t"));
+ }
+ }
+
+ void doNotCrashEcmaScriptClass()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/ecmaScriptClass.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+ }
+
+ void bindingAttachedOrGroupedProperties()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/attachedOrGroupedProperties.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+
+ DomItem dotNotation = rootQmlObject.path(u".children[0].bindings[\"grouped.font.family\"][0].bindingIdentifiers");
+ QVERIFY(dotNotation);
+ QCOMPARE(dotNotation.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(dotNotation.field(Fields::left).internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(dotNotation.field(Fields::right).field(Fields::identifier).value().toString(), u"family");
+ QCOMPARE(dotNotation.field(Fields::right).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(dotNotation.field(Fields::left).field(Fields::left).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(dotNotation.field(Fields::left).field(Fields::left).field(Fields::identifier).value().toString(), u"grouped");
+ QCOMPARE(dotNotation.field(Fields::left).field(Fields::right).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(dotNotation.field(Fields::left).field(Fields::right).field(Fields::identifier).value().toString(), u"font");
+ auto dotNotationScope = dotNotation.semanticScope();
+ QVERIFY(!dotNotationScope);
+
+ DomItem groupNotationChild1 = rootQmlObject.path(u".children[1].children[0]");
+ QVERIFY(groupNotationChild1);
+ QCOMPARE(groupNotationChild1.internalKind(), DomType::QmlObject);
+ QCOMPARE(groupNotationChild1.field(Fields::name).value().toString(), u"myText");
+ auto myTextScope = groupNotationChild1.semanticScope();
+ QVERIFY(myTextScope);
+ QCOMPARE(myTextScope->scopeType(), QQmlJSScope::ScopeType::GroupedPropertyScope);
+ QVERIFY(myTextScope->hasProperty("font"));
+
+ DomItem groupNotationChild2 = groupNotationChild1.path(u".children[0]");
+ QCOMPARE(groupNotationChild2.internalKind(), DomType::QmlObject);
+ QCOMPARE(groupNotationChild2.field(Fields::name).value().toString(), u"font");
+
+ auto fontScope = groupNotationChild2.semanticScope();
+ QVERIFY(fontScope);
+ QCOMPARE(fontScope->scopeType(), QQmlJSScope::ScopeType::GroupedPropertyScope);
+ QVERIFY(fontScope->hasProperty("pixelSize"));
+
+ DomItem pixelSize = groupNotationChild2.path(u".bindings[\"pixelSize\"][0].bindingIdentifiers");
+ QCOMPARE(pixelSize.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(pixelSize.field(Fields::identifier).value().toString(), u"pixelSize");
+
+ DomItem attached = rootQmlObject.path(u".bindings[\"Keys.onPressed\"][0].bindingIdentifiers");
+ QVERIFY(attached);
+ QCOMPARE(attached.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(attached.field(Fields::left).field(Fields::identifier).value().toString(), u"Keys");
+ QCOMPARE(attached.field(Fields::right).field(Fields::identifier).value().toString(), u"onPressed");
+ }
+
+ void enumDeclarations()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/enumDeclarations.qml"_s;
+ DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ QVERIFY(fileObject);
+ DomItem enums = fileObject.path(u".components[\"\"][0].enumerations");
+ QVERIFY(enums);
+
+ DomItem catsEnum = enums.key("Cats").index(0);
+ QVERIFY(catsEnum);
+ QCOMPARE(catsEnum.internalKind(), DomType::EnumDecl);
+ QCOMPARE(catsEnum.name(), u"Cats");
+
+ auto values = catsEnum.field(Fields::values);
+ QCOMPARE(values.length(), 3);
+ QCOMPARE(values.index(0).internalKind(), DomType::EnumItem);
+ QCOMPARE(values.index(0).name(), u"Patron");
+ QCOMPARE(values.index(0).field(Fields::value).value().toInteger(), 0);
+ QCOMPARE(values.index(1).internalKind(), DomType::EnumItem);
+ QCOMPARE(values.index(1).name(), u"Mafya");
+ QCOMPARE(values.index(1).field(Fields::value).value().toInteger(), 1);
+ QCOMPARE(values.index(2).internalKind(), DomType::EnumItem);
+ QCOMPARE(values.index(2).name(), u"Kivrik");
+ QCOMPARE(values.index(2).field(Fields::value).value().toInteger(), -1);
+ }
+
+ void tryStatements()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/tryStatements.qml"_s;
+ const DomItem root = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ QVERIFY(root);
+ const DomItem statements = root.path(u".methods[\"f\"][0].body.scriptElement.statements");
+ QCOMPARE(statements.indexes(), 3);
+
+ // test the try blocks
+ for (int i = 0; i < 3; ++i) {
+ const DomItem statement = statements.index(i).field(Fields::block);
+ QVERIFY(statement);
+ QCOMPARE(statement.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(statement.field(Fields::statements)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"insideTry"_s);
+ }
+
+ // test the catch blocks
+ for (int i = 0; i < 3; ++i) {
+ const DomItem statement = statements.index(i).field(Fields::catchBlock);
+ if (i == 2) {
+ QVERIFY(!statement); // no catch in last statement
+ continue;
+ }
+
+ QVERIFY(statement);
+ QCOMPARE(statement.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(statement.field(Fields::statements)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"insideCatch"_s);
+
+ const DomItem expression = statements.index(i).field(Fields::catchParameter);
+ QVERIFY(expression);
+ QCOMPARE(expression.field(Fields::identifier)
+ .value()
+ .toString(),
+ u"catchExpression"_s);
+ }
+
+ // test the finally blocks
+ for (int i = 0; i < 3; ++i) {
+ const DomItem statement = statements.index(i).field(Fields::finallyBlock);
+ if (i == 1) {
+ QVERIFY(!statement); // no finally in last statement
+ continue;
+ }
+
+ QVERIFY(statement);
+ QCOMPARE(statement.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(statement.field(Fields::statements)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"insideFinally"_s);
+ }
+ }
+
+ void plainJSDOM_data()
+ {
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<QString>("content");
+
+ QTest::newRow("simplestJSStatement")
+ << "simplestJSStatement.js" << QString(u"let v=1;\n"_s);
+ QTest::newRow("import")
+ << "import.js"
+ << QString(u".import \"main.js\" as Main\nconsole.log(Main.a);\n"_s);
+ QTest::newRow("simplestJSmodule")
+ << "simplestJSmodule.mjs" << QString(u"export function entry() {}\n"_s);
+ }
+
+ // Verifies that DOM can load .js and .mjs files and
+ // parse / store the content inside the ScriptExpression
+ void plainJSDOM()
+ {
+ using namespace Qt::StringLiterals;
+ QFETCH(QString, filename);
+ QFETCH(QString, content);
+
+ QString testFile = baseDir + "/" + filename;
+ auto dom = parse(testFile, qmltypeDirs);
+ QVERIFY(dom);
+ QCOMPARE(dom.internalKind(), DomType::JsFile);
+ auto filePtr = dom.fileObject().ownerAs<JsFile>();
+ QVERIFY(filePtr && filePtr->isValid());
+ auto exprAsString = dom.field(Fields::expression)
+ .field(Fields::code)
+ .value()
+ .toString();
+ QVERIFY(!exprAsString.isEmpty());
+ exprAsString.replace("\r\n", "\n");
+ QCOMPARE(exprAsString, content);
+ }
+
+private:
+ struct DomItemWithLocation
+ {
+ DomItem item;
+ FileLocations::Tree tree;
+ };
+private slots:
+
+ void fileLocations_data()
+ {
+ QTest::addColumn<QString>("fileName");
+ QDir dir(baseDir);
+ for (const QString &file : dir.entryList(QDir::Files, QDir::Name)) {
+ if (!file.endsWith(".qml"))
+ continue;
+ QTest::addRow("%s", file.toStdString().c_str()) << baseDir + QDir::separator() + file;
+ }
+ }
+
+ /*!
+ \internal
+ Check if finalizeScriptExpression() was called with the correct FileLocations::Tree
+ argument when this test fails.
+ */
+ void fileLocations()
+ {
+ QFETCH(QString, fileName);
+
+ DomItem rootQmlObject = rootQmlObjectFromFile(fileName, qmltypeDirs);
+ std::deque<DomItemWithLocation> queue;
+
+ DomItem fileDomItem = rootQmlObject.containingFile();
+ std::shared_ptr<QmlFile> file = fileDomItem.ownerAs<QmlFile>();
+ QVERIFY(file);
+
+ DomItemWithLocation root{ fileDomItem, file->fileLocationsTree() };
+ queue.push_back(root);
+
+ while (!queue.empty()) {
+ DomItemWithLocation current = queue.front();
+ queue.pop_front();
+
+ auto subEls = current.tree->subItems();
+ for (auto it = subEls.begin(); it != subEls.end(); ++it) {
+ DomItem childItem = current.item.path(it.key());
+ FileLocations::Tree childTree =
+ std::static_pointer_cast<AttachedInfoT<FileLocations>>(it.value());
+ if (!childItem) {
+ const auto attachedInfo = FileLocations::findAttachedInfo(current.item);
+ const DomItem treeItem = current.item.path(attachedInfo.foundTreePath);
+ qDebug() << current.item.internalKindStr()
+ << "has incorrect FileLocations! Make sure that "
+ "finalizeScriptExpression is called with the right arguments. It "
+ "should print out some debugging information with the "
+ "qt.qmldom.astcreator.debug logging rule.";
+ qDebug() << "Current FileLocations has keys:"
+ << treeItem.field(Fields::subItems).keys()
+ << "but current Item of type" << current.item.internalKindStr()
+ << "has fields: " << current.item.fields()
+ << "and keys: " << current.item.keys()
+ << "and indexes: " << current.item.indexes();
+ }
+ QVERIFY(childItem.internalKind() != DomType::Empty);
+ queue.push_back({ childItem, childTree });
+ }
+ }
+ }
+
+private slots:
+ void finalizeScriptExpressions()
+ {
+ QString fileName = baseDir + u"/finalizeScriptExpressions.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(fileName, qmltypeDirs);
+
+ /*
+ Check if the path obtained by the filelocations is the same as the DomItem path. Both
+ need to be equal to find DomItem's from their location in the source code.
+ */
+ auto compareFileLocationsPathWithCanonicalPath = [](const DomItem &item) {
+ Path canonical = item.canonicalPath();
+ QVERIFY(canonical.length() > 0);
+ if (canonical.length() > 0)
+ QCOMPARE(canonical.toString(),
+ FileLocations::treeOf(item)->canonicalPathForTesting());
+ };
+
+ /*
+ for all places, where scriptelements are attached to Qml elements in the Dom, check if:
+ a) scriptelement is accessible from the DomItem (is it correclty attached?)
+ b) scriptelement has the correct path (is its pathFromOwner the path where it was
+ attached?)
+
+ For bindings to objects, arrays and scripts, check that the bindingIdentifiers are correctly
+ attached in the Dom.
+ */
+
+ {
+ DomItem binding = rootQmlObject.field(Fields::bindings).key("binding");
+ QCOMPARE(binding.indexes(), 1);
+
+ QCOMPARE(binding.index(0).field(Fields::value).internalKind(),
+ DomType::ScriptExpression);
+ QCOMPARE(binding.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .internalKind(),
+ DomType::ScriptLiteral);
+ // Fields::value is in the path of the owner, and therefore should not be in
+ // pathFromOwner!
+ DomItem scriptElement =
+ binding.index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+
+ {
+ DomItem bindingInPropertyDefinition =
+ rootQmlObject.field(Fields::bindings).key("bindingInPropertyDefinition");
+ QCOMPARE(bindingInPropertyDefinition.indexes(), 1);
+
+ QCOMPARE(bindingInPropertyDefinition.index(0).field(Fields::value).internalKind(),
+ DomType::ScriptExpression);
+ QCOMPARE(bindingInPropertyDefinition.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .internalKind(),
+ DomType::ScriptLiteral);
+ // Fields::value is in the path of the owner, and therefore should not be in
+ // pathFromOwner!
+ DomItem scriptElement = bindingInPropertyDefinition.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+ // check the parameters + returnType of the method
+ {
+ DomItem return42 = rootQmlObject.field(Fields::methods).key("return42");
+ QCOMPARE(return42.indexes(), 1);
+
+ DomItem typeAnnotation =
+ return42.index(0).field(Fields::returnType).field(Fields::scriptElement);
+ QCOMPARE(typeAnnotation.internalKind(), DomType::ScriptType);
+ compareFileLocationsPathWithCanonicalPath(typeAnnotation);
+
+ DomItem parameters = return42.index(0).field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 3);
+ for (int i = 0; i < 3; ++i) {
+ QCOMPARE(parameters.index(i).field(Fields::defaultValue).internalKind(),
+ DomType::ScriptExpression);
+ DomItem scriptElement =
+ parameters.index(i).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(scriptElement.internalKind(), DomType::ScriptFormalParameter);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+ }
+ // check the body of the methods
+ QList<QString> methodNames = { "full", "return42" };
+ for (QString &methodName : methodNames) {
+ DomItem method = rootQmlObject.field(Fields::methods).key(methodName);
+ DomItem body = method.index(0).field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptExpression);
+ DomItem scriptElement = body.field(Fields::scriptElement);
+ QCOMPARE(scriptElement.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+
+ {
+ DomItem binding = rootQmlObject.field(Fields::bindings).key("arrayBinding");
+ QCOMPARE(binding.indexes(), 1);
+ QCOMPARE(binding.index(0).field(Fields::value).internalKind(),
+ DomType::ScriptExpression);
+ QCOMPARE(binding.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .internalKind(),
+ DomType::ScriptArray);
+ // Fields::value is in the path of the owner, and therefore should not be in
+ // pathFromOwner!
+ DomItem scriptElement =
+ binding.index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ // also check that the left hand side of the binding is correctly attached to the Dom:
+ scriptElement = binding.index(0).field(Fields::bindingIdentifiers);
+ QCOMPARE(scriptElement.pathFromOwner(),
+ Path::fromString(u".components[\"\"][0].objects[0].bindings[\"arrayBinding\"]["
+ u"0].bindingIdentifiers"));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+ {
+ DomItem binding = rootQmlObject.field(Fields::bindings).key("objectBinding");
+ QCOMPARE(binding.indexes(), 1);
+ QCOMPARE(binding.index(0).field(Fields::value).internalKind(), DomType::QmlObject);
+ // check that the left hand side of the binding is correctly attached to the Dom:
+ DomItem scriptElement = binding.index(0).field(Fields::bindingIdentifiers);
+ QCOMPARE(scriptElement.pathFromOwner(),
+ Path::fromString(u".components[\"\"][0].objects[0].bindings[\"objectBinding\"]["
+ u"0].bindingIdentifiers"));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+ }
+
+ void goToFile()
+ {
+ using namespace Qt::StringLiterals;
+ const QString filePathA = baseDir + u"/nullStatements.qml"_s;
+ const QString filePathB = baseDir + u"/propertyBindings.qml"_s;
+ const QString canonicalFilePathB = QFileInfo(filePathB).canonicalFilePath();
+ QVERIFY(!canonicalFilePathB.isEmpty());
+
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ options);
+
+ DomItem fileA;
+ DomItem fileB;
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filePathA),
+ [&fileA](Path, const DomItem &, const DomItem &newIt) {
+ fileA = newIt.fileObject();
+ });
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filePathB),
+ [&fileB](Path, const DomItem &, const DomItem &newIt) {
+ fileB = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ QCOMPARE(fileA.goToFile(canonicalFilePathB), fileB);
+ }
+
+ void goUp()
+ {
+ using namespace Qt::StringLiterals;
+ const QString filePath = baseDir + u"/nullStatements.qml"_s;
+ const QString canonicalFilePathB = QFileInfo(filePath).canonicalFilePath();
+ QVERIFY(!canonicalFilePathB.isEmpty());
+
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ options);
+
+ DomItem fileA;
+ DomItem fileB;
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filePath),
+ [&fileA](Path, const DomItem &, const DomItem &newIt) {
+ fileA = newIt.fileObject();
+ });
+
+ envPtr->loadPendingDependencies();
+
+ QCOMPARE(fileA.top().goUp(1), DomItem());
+ QCOMPARE(fileA.top().directParent(), DomItem());
+
+ DomItem component = fileA.field(Fields::components).key(QString()).index(0);
+
+ DomItem forStatement = component.field(Fields::objects)
+ .index(0)
+ .field(Fields::methods)
+ .key(u"testForNull"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::body);
+
+ DomItem forStatementBlock = forStatement.field(Fields::statements);
+
+ QCOMPARE(forStatementBlock.directParent(), forStatement);
+ QCOMPARE(forStatementBlock.goUp(1), forStatement);
+ QCOMPARE(forStatementBlock.goUp(11), component);
+
+ QCOMPARE(forStatement.component(GoTo::Strict), component);
+ }
+
+private:
+ static DomItem parse(const QString &path, const QStringList &qmltypeDirs)
+ {
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ options);
+
+ DomItem fileItem;
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, path),
+ [&fileItem](Path, const DomItem &, const DomItem &newIt) {
+ fileItem = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+ return fileItem;
+ }
+
+ static DomItem rootQmlObjectFromFile(const QString &path, const QStringList &qmltypeDirs)
+ {
+ auto dom = parse(path, qmltypeDirs);
+ return dom.rootQmlObject(GoTo::MostLikely);
+ }
+
+ void fieldMemberExpressionHelper(const DomItem &actual, const QStringList &expected)
+ {
+ Q_ASSERT(!expected.isEmpty());
+ auto currentString = expected.rbegin();
+ auto endString = expected.rend();
+ DomItem current = actual;
+
+ for (; currentString != endString; ++currentString, current = current.field(Fields::left)) {
+ QCOMPARE(current.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(current.field(Fields::operation).value().toInteger(),
+ ScriptElements::BinaryExpression::FieldMemberAccess);
+ QCOMPARE(current.field(Fields::right).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(current.field(Fields::right).field(Fields::identifier).value().toString(),
+ *currentString);
+ }
+ }
+
+private slots:
+ void mapsKeyedByFileLocationRegion()
+ {
+ using namespace Qt::StringLiterals;
+ const QString filePath = baseDir + u"/fileLocationRegion.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(filePath, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+
+ // test if preComments map works correctly with DomItem interface
+ const DomItem binding = rootQmlObject.field(Fields::bindings).key(u"helloWorld"_s).index(0);
+ const DomItem bindingRegionComments =
+ binding.field(Fields::comments).field(Fields::regionComments);
+ const DomItem preComments =
+ bindingRegionComments.key(fileLocationRegionName(FileLocationRegion::IdentifierRegion))
+ .field(Fields::preComments);
+
+ QCOMPARE(preComments.indexes(), 1);
+ QString rawPreComment = preComments.index(0).field(Fields::rawComment).value().toString();
+ QCOMPARE(preComments.index(0)
+ .field(Fields::rawComment)
+ .value()
+ .toString()
+ // replace weird newlines by \n
+ .replace("\r\n", "\n")
+ .replace("\r", "\n"),
+ u" // before helloWorld binding\n "_s);
+
+ // test if postComments map works correctly with DomItem interface
+ const DomItem postComments =
+ bindingRegionComments
+ .key(fileLocationRegionName(FileLocationRegion::MainRegion))
+ .field(Fields::postComments);
+ QCOMPARE(postComments.indexes(), 1);
+ QCOMPARE(postComments.index(0)
+ .field(Fields::rawComment)
+ .value()
+ .toString()
+ // replace the windows newlines by \n
+ .replace("\r\n", "\n")
+ .replace("\r", "\n"),
+ u" // after helloWorld binding\n"_s);
+
+ const auto fileLocations = FileLocations::findAttachedInfo(binding);
+ const DomItem bindingFileLocation =
+ rootQmlObject.path(fileLocations.foundTreePath).field(Fields::infoItem);
+
+ // test if FileLocation Tree map works correctly with DomItem interface
+ QCOMPARE(bindingFileLocation.field(Fields::fullRegion).value(),
+ bindingFileLocation.field(Fields::regions)
+ .key(fileLocationRegionName(FileLocationRegion::MainRegion))
+ .value());
+
+ QCOMPARE(bindingFileLocation.field(Fields::fullRegion).value(),
+ sourceLocationToQCborValue(fileLocations.foundTree->info().fullRegion));
+
+ QCOMPARE(bindingFileLocation.field(Fields::regions)
+ .key(fileLocationRegionName(FileLocationRegion::MainRegion))
+ .value(),
+ sourceLocationToQCborValue(fileLocations.foundTree->info().regions[MainRegion]));
+
+ QCOMPARE(bindingFileLocation.field(Fields::regions)
+ .key(fileLocationRegionName(FileLocationRegion::ColonTokenRegion))
+ .value(),
+ sourceLocationToQCborValue(fileLocations.foundTree->info().regions[ColonTokenRegion]));
+ }
+
+ // add qml files here that should not crash the dom construction
+ void crashes_data()
+ {
+ QTest::addColumn<QString>("filePath");
+
+ QTest::addRow("inactiveVisitorMarkerCrash")
+ << baseDir + u"/inactiveVisitorMarkerCrash.qml"_s;
+
+ QTest::addRow("templateStrings")
+ << baseDir + u"/crashes/templateStrings.qml"_s;
+
+ QTest::addRow("lambda")
+ << baseDir + u"/crashes/lambda.qml"_s;
+ }
+ void crashes()
+ {
+ QFETCH(QString, filePath);
+
+ const DomItem rootQmlObject = rootQmlObjectFromFile(filePath, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+ }
+
+ void continueStatement()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/continueStatement.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem block =
+ rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements");
+
+ const DomItem firstContinue = block.index(0);
+ QCOMPARE(firstContinue.internalKind(), DomType::ScriptContinueStatement);
+ QCOMPARE(firstContinue.field(Fields::label).value().toString("UNEXISTING"),
+ u"helloWorld"_s);
+
+ const DomItem secondContinue = block.index(1);
+ QCOMPARE(secondContinue.internalKind(), DomType::ScriptContinueStatement);
+ QCOMPARE(secondContinue.field(Fields::label).internalKind(), DomType::Empty);
+ }
+
+ void breakStatement()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/breakStatement.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem block =
+ rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements");
+
+ const DomItem firstContinue = block.index(0);
+ QCOMPARE(firstContinue.internalKind(), DomType::ScriptBreakStatement);
+ QCOMPARE(firstContinue.field(Fields::label).value().toString("UNEXISTING"),
+ u"helloWorld"_s);
+
+ const DomItem secondContinue = block.index(1);
+ QCOMPARE(secondContinue.internalKind(), DomType::ScriptBreakStatement);
+ QCOMPARE(secondContinue.field(Fields::label).internalKind(), DomType::Empty);
+ }
+
+ void emptyMethodBody()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/emptyMethodBody.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem block = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement");
+
+ QCOMPARE(block.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(block.field(Fields::statements).indexes(), 0);
+ }
+
+ void commaExpression()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/commaExpression.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem commaExpression = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements[0]");
+
+ QCOMPARE(commaExpression.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(commaExpression.field(Fields::right)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"c"_s);
+ QCOMPARE(commaExpression.field(Fields::left)
+ .field(Fields::right)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"b"_s);
+ QCOMPARE(commaExpression.field(Fields::left)
+ .field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a"_s);
+ }
+
+ void conditionalExpression()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/conditionalExpression.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem commaExpression = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements[0]");
+
+ QCOMPARE(commaExpression.internalKind(), DomType::ScriptConditionalExpression);
+ QCOMPARE(commaExpression.field(Fields::condition)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a"_s);
+ QCOMPARE(commaExpression.field(Fields::consequence)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"b"_s);
+ QCOMPARE(commaExpression.field(Fields::alternative)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"c"_s);
+ }
+
+ void unaryExpression_data()
+ {
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<DomType>("type");
+
+ const QString folder = baseDir + u"/unaryExpressions/"_s;
+
+ QTest::addRow("minus") << folder + u"unaryMinus.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("plus") << folder + u"unaryPlus.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("tilde") << folder + u"tilde.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("not") << folder + u"not.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("typeof") << folder + u"typeof.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("delete") << folder + u"delete.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("void") << folder + u"void.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("increment") << folder + u"increment.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("decrement") << folder + u"decrement.qml"_s << DomType::ScriptUnaryExpression;
+
+ // post stuff
+ QTest::addRow("postIncrement")
+ << folder + u"postIncrement.qml"_s << DomType::ScriptPostExpression;
+ QTest::addRow("postDecrement")
+ << folder + u"postDecrement.qml"_s << DomType::ScriptPostExpression;
+ }
+
+ void unaryExpression()
+ {
+ using namespace Qt::StringLiterals;
+ QFETCH(QString, fileName);
+ QFETCH(DomType, type);
+ const DomItem rootQmlObject = rootQmlObjectFromFile(fileName, qmltypeDirs);
+ const DomItem firstStatement =
+ rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements[0]");
+
+ QCOMPARE(firstStatement.internalKind(), type);
+ QCOMPARE(firstStatement.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a"_s);
+ }
+
+ void objectBindings()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/objectBindings.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ const DomItem xBinding = rootQmlObject.path(".bindings[\"x\"][0].value");
+ QCOMPARE(xBinding.field(Fields::name).value().toString(), u"root.QQ.Drag");
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers).internalKind(),
+ DomType::ScriptType);
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers).field(Fields::typeName).internalKind(),
+ DomType::ScriptBinaryExpression);
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::operation)
+ .value()
+ .toInteger(-1),
+ ScriptElements::BinaryExpression::FieldMemberAccess);
+
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers).field(Fields::typeName).field(Fields::left).internalKind(),
+ DomType::ScriptBinaryExpression);
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::left)
+ .field(Fields::right)
+ .value()
+ .toString(),
+ u"QQ");
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::left)
+ .field(Fields::left)
+ .value()
+ .toString(),
+ u"root");
+
+ const DomItem item = rootQmlObject.path(".children[0]");
+ QCOMPARE(item.field(Fields::nameIdentifiers).field(Fields::typeName).value().toString(),
+ u"Item");
+
+ const DomItem qqItem = rootQmlObject.path(".children[1]");
+ QCOMPARE(qqItem.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::operation)
+ .value()
+ .toInteger(-1),
+ ScriptElements::BinaryExpression::FieldMemberAccess);
+ QCOMPARE(qqItem.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::right)
+ .value()
+ .toString(),
+ u"Item");
+ QCOMPARE(qqItem.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::left)
+ .value()
+ .toString(),
+ u"QQ");
+ }
+
+ void scriptExpression()
+ {
+ // verifying support of ECMA script modules by ScriptExpression
+ const ScriptExpression esmExport("export function a(){}",
+ ScriptExpression::ExpressionType::ESMCode);
+ QVERIFY(esmExport.localErrors().empty());
+ }
+
+ void semanticAnalysis()
+ {
+
+ DomItem baseItem;
+ DomItem derivedItem;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr =
+ DomEnvironment::create(qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option{}, options);
+
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, baseDir + u"/Base.qml"_s),
+ [&baseItem](Path, const DomItem &, const DomItem &newIt) {
+ baseItem = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, baseDir + u"/Derived.qml"_s),
+ [&derivedItem](Path, const DomItem &, const DomItem &newIt) {
+ derivedItem = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtr->loadPendingDependencies();
+
+ const auto baseScope = baseItem.semanticScope();
+ const auto derivedScope = derivedItem.semanticScope();
+
+ QCOMPARE_NE(baseScope, QQmlJSScope::ConstPtr{});
+ QCOMPARE(baseScope, derivedScope->baseType());
+ }
+
+ void propertyDefinitionScopes()
+ {
+ DomItem qmlObject;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr =
+ DomEnvironment::create(qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option{}, options);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir + u"/propertyBindings.qml"_s),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtr->loadPendingDependencies();
+
+ {
+ const auto a = qmlObject.field(Fields::propertyDefs).key(u"a").index(0);
+ const auto scopeA = a.semanticScope();
+ QCOMPARE_NE(scopeA, QQmlJSScope::ConstPtr{});
+ QCOMPARE(scopeA->scopeType(), QQmlSA::ScopeType::QMLScope);
+ }
+
+ {
+ const auto b = qmlObject.field(Fields::propertyDefs).key(u"b").index(0);
+ const auto scopeB = b.semanticScope();
+ QCOMPARE_NE(scopeB, QQmlJSScope::ConstPtr{});
+ QCOMPARE(scopeB->scopeType(), QQmlSA::ScopeType::QMLScope);
+ }
+ }
+
+ // simulate qmlls loading the same file twice like in QTBUG-123591
+ void loadFileTwice()
+ {
+ DomItem qmlObject;
+ DomItem qmlObject2;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+ options.setFlag(DomCreationOption::WithRecovery);
+
+ std::shared_ptr<DomEnvironment> envPtr = DomEnvironment::create(
+ qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, options);
+
+ const QString fileName{ baseDir + u"/propertyBindings.qml"_s };
+ QFile file(fileName);
+ QVERIFY(file.open(QFile::ReadOnly));
+ const QString content = file.readAll();
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir + u"/propertyBindings.qml"_s),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtr->loadPendingDependencies();
+
+ // should not assert when loading the same file again
+ auto envPtrChild = envPtr->makeCopy(DomItem(envPtr));
+ envPtrChild->loadFile(
+ FileToLoad::fromMemory(envPtr, baseDir + u"/propertyBindings.qml"_s, content),
+ [&qmlObject2](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject2 = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtrChild->loadPendingDependencies();
+ }
+
+ void visitTreeFilter()
+ {
+ DomItem qmlObject;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr =
+ DomEnvironment::create(qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option{}, options);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir + u"/visitTreeFilter.qml"_s),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtr->loadPendingDependencies();
+
+ FieldFilter filter({}, { { QString(), QString::fromUtf16(Fields::propertyDefs) } });
+
+ // check if propertyDefs is visited without the filter
+ bool success = false;
+ qmlObject.visitTree(
+ Path(), emptyChildrenVisitor, VisitOption::Recurse | VisitOption::VisitSelf,
+ [&success](const Path &p, const DomItem &, bool) {
+ const QString pathString = p.toString();
+ if (p && p.checkHeadName(Fields::propertyDefs)) {
+ success = true;
+ }
+ return true;
+ },
+ emptyChildrenVisitor);
+ QVERIFY(success);
+
+ // check that propertyDefs is not visited with the filter
+ success = true;
+ qmlObject.visitTree(
+ Path(), emptyChildrenVisitor, VisitOption::Recurse | VisitOption::VisitSelf,
+ [&success](const Path &p, const DomItem &, bool) {
+ if (p && p.checkHeadName(Fields::propertyDefs)) {
+ qWarning() << "Filter did not filter propertyDefs at path" << p;
+ success = false;
+ }
+ return true;
+ },
+ emptyChildrenVisitor, filter);
+ QVERIFY(success);
+ }
+
+ void fileLocationRegions_data()
+ {
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<FileLocationRegion>("region");
+ QTest::addColumn<QSet<QQmlJS::SourceLocation>>("expectedLocs");
+
+ QTest::newRow("import") << baseDir + u"/fileLocationRegions/imports.qml"_s << ImportTokenRegion <<
+ QSet {
+ QQmlJS::SourceLocation{112, 6, 4, 1},
+ QQmlJS::SourceLocation{127, 6, 5, 1},
+ };
+ QTest::newRow("importUri") << baseDir + u"/fileLocationRegions/imports.qml"_s << ImportUriRegion <<
+ QSet {
+ QQmlJS::SourceLocation{119, 7, 4, 8},
+ QQmlJS::SourceLocation{152, 16, 6, 8},
+ QQmlJS::SourceLocation{186, 9, 7, 8}
+ };
+ QTest::newRow("asToken") << baseDir + u"/fileLocationRegions/imports.qml"_s << AsTokenRegion <<
+ QSet {
+ QQmlJS::SourceLocation{169, 2, 6, 25}
+ };
+ QTest::newRow("version") << baseDir + u"/fileLocationRegions/imports.qml"_s << VersionRegion <<
+ QSet {
+ QQmlJS::SourceLocation{140, 4, 5, 14}
+ };
+ QTest::newRow("namespace") << baseDir + u"/fileLocationRegions/imports.qml"_s << IdNameRegion <<
+ QSet {
+ QQmlJS::SourceLocation{172, 6, 6, 28}
+ };
+
+ QTest::newRow("function") << baseDir + u"/fileLocationRegions/functions.qml"_s
+ << FunctionKeywordRegion
+ << QSet{ QQmlJS::SourceLocation{ 139, 9, 7, 5 },
+ QQmlJS::SourceLocation{ 195, 9, 10, 9 } };
+
+ QTest::newRow("signal") << baseDir + u"/fileLocationRegions/functions.qml"_s
+ << SignalKeywordRegion
+ << QSet{ QQmlJS::SourceLocation{ 234, 6, 13, 5 },
+ QQmlJS::SourceLocation{ 254, 6, 14, 5 } };
+ QTest::newRow("return-type-identifier")
+ << baseDir + u"/fileLocationRegions/functions.qml"_s << TypeIdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 154, 3, 7, 20 },
+ QQmlJS::SourceLocation{ 216, 3, 10, 30 } };
+ QTest::newRow("function-parameter-type-identifier")
+ << baseDir + u"/fileLocationRegions/functions.qml"_s << TypeIdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 209, 3, 10, 23 } };
+ QTest::newRow("signal-parameter-type-identifier")
+ << baseDir + u"/fileLocationRegions/functions.qml"_s << TypeIdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 243, 3, 13, 14 },
+ QQmlJS::SourceLocation{ 267, 3, 14, 18 } };
+ QTest::newRow("signal-parameter-identifier")
+ << baseDir + u"/fileLocationRegions/functions.qml"_s << IdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 247, 1, 13, 18 },
+ QQmlJS::SourceLocation{ 264, 1, 14, 15 } };
+
+ QTest::newRow("pragma-keyword")
+ << baseDir + u"/fileLocationRegions/pragmas.qml"_s << PragmaKeywordRegion
+ << QSet{ QQmlJS::SourceLocation{ 112, 6, 4, 1 },
+ QQmlJS::SourceLocation{ 129, 6, 5, 1 },
+ QQmlJS::SourceLocation{ 161, 6, 6, 1 },
+ QQmlJS::SourceLocation{ 204, 6, 7, 1 }};
+ QTest::newRow("pragmaId")
+ << baseDir + u"/fileLocationRegions/pragmas.qml"_s << IdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 119, 9, 4, 8 },
+ QQmlJS::SourceLocation{ 136, 17, 5, 8 },
+ QQmlJS::SourceLocation{ 168, 25, 6, 8 },
+ QQmlJS::SourceLocation{ 211, 17, 7, 8 }};
+ QTest::newRow("pragmaValues")
+ << baseDir + u"/fileLocationRegions/pragmas.qml"_s << PragmaValuesRegion
+ << QSet{ QQmlJS::SourceLocation{ 155, 5, 5, 27 },
+ QQmlJS::SourceLocation{ 195, 8, 6, 35 },
+ QQmlJS::SourceLocation{ 230, 4, 7, 27 },
+ QQmlJS::SourceLocation{ 235, 11, 7, 32 }};
+
+ QTest::newRow("enum-keyword")
+ << baseDir + u"/fileLocationRegions/enums.qml"_s << EnumKeywordRegion
+ << QSet{ QQmlJS::SourceLocation{ 139, 4, 7, 5 }};
+ QTest::newRow("enum-id")
+ << baseDir + u"/fileLocationRegions/enums.qml"_s << IdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 144, 3, 7, 10 }};
+ QTest::newRow("enum-member")
+ << baseDir + u"/fileLocationRegions/enums.qml"_s << IdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 158, 3, 8, 9 },
+ QQmlJS::SourceLocation{ 175, 3, 9, 9 },
+ QQmlJS::SourceLocation{ 188, 3, 10, 9 }};
+ QTest::newRow("enum-value")
+ << baseDir + u"/fileLocationRegions/enums.qml"_s << EnumValueRegion
+ << QSet{ QQmlJS::SourceLocation{ 164, 1, 8, 15 },
+ QQmlJS::SourceLocation{ 194, 2, 10, 15 }};
+ }
+
+ void fileLocationRegions()
+ {
+ QFETCH(QString, filePath);
+ QFETCH(FileLocationRegion, region);
+ QFETCH(QSet<QQmlJS::SourceLocation>, expectedLocs);
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ QFile f(filePath);
+ QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text));
+ QString code = f.readAll();
+ DomItem file;
+ envPtr->loadFile(FileToLoad::fromMemory(envPtr, filePath, code),
+ [&file](Path, const DomItem &, const DomItem &newIt) {
+ file = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ const auto tree = FileLocations::treeOf(file);
+ using AttachedInfo = AttachedInfoT<FileLocations>;
+ QSet<QQmlJS::SourceLocation> locs;
+ auto visitor = [&](const Path &currentPath, const AttachedInfo::Ptr &attachedInfo){
+ Q_UNUSED(currentPath);
+ const auto regions = attachedInfo->info().regions;
+ if (regions.contains(region)) {
+ locs << regions.value(region);
+ }
+ return true;
+ };
+ AttachedInfo::visitTree(tree, visitor, Path());
+ [&] {
+ QVERIFY(locs.contains(expectedLocs));
+ }();
+
+ if (QTest::currentTestFailed()) {
+ qDebug() << "Got:\n";
+ for (auto &x : locs) {
+ qDebug() << "Offset: " << x.offset
+ << ", Length:" << x.length
+ << ", Startline: " << x.startLine
+ << ", StartColumn: " << x.startColumn;
+ }
+ qDebug() << "But expected: \n";
+ for (auto &x : expectedLocs) {
+ qDebug() << "Offset: " << x.offset
+ << ", Length:" << x.length
+ << ", Startline: " << x.startLine
+ << ", StartColumn: " << x.startColumn;
+ }
+ }
+ }
+
+ void doNotCrashAtAstComments()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/astComments.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem astComments = fileObject.path(".astComments");
+
+ // Visiting astComment element shouldn't fail
+ QSet<QStringView> comments;
+ astComments.visitTree(
+ Path(),
+ [&comments](const Path &, const DomItem &item, bool) {
+ if (item.internalKind() == DomType::Comment) {
+ auto comment = item.as<Comment>();
+ comments << comment->rawComment();
+ }
+ return true;
+ }
+ );
+
+ QVERIFY(comments.contains(u"/*Ast Comment*/ "_s));
+ }
+
+ void commentLocations()
+ {
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ const auto filePath = baseDir + u"/fileLocationRegions/comments.qml"_s;
+ QFile f(filePath);
+ QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text));
+ QString code = f.readAll();
+ DomItem file;
+ envPtr->loadFile(FileToLoad::fromMemory(envPtr, filePath, code),
+ [&file](Path, const DomItem &, const DomItem &newIt) {
+ file = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ const auto expctedCommentLocations = QSet {
+ QQmlJS::SourceLocation(0, 41, 1, 1),
+ QQmlJS::SourceLocation(42,68, 2, 1),
+ QQmlJS::SourceLocation(126,25, 6, 1),
+ QQmlJS::SourceLocation(152,14, 10, 1),
+ QQmlJS::SourceLocation(167,21, 11, 1)
+ };
+
+ QSet<SourceLocation> locs;
+ file.fileObject(GoTo::MostLikely).visitTree(Path(), [&locs](Path, const DomItem &item, bool){
+ if (item.internalKind() == DomType::Comment) {
+ const auto comment = item.as<Comment>();
+ if (comment) {
+ locs << comment->info().sourceLocation();
+ }
+ }
+ return true;
+ }, VisitOption::Default, emptyChildrenVisitor, emptyChildrenVisitor);
+
+
+ QCOMPARE(locs, expctedCommentLocations);
+ }
+
private:
QString baseDir;
QStringList qmltypeDirs;
diff --git a/tests/auto/qmldom/errormessage/CMakeLists.txt b/tests/auto/qmldom/errormessage/CMakeLists.txt
index 5a61ebcb29..7c82876827 100644
--- a/tests/auto/qmldom/errormessage/CMakeLists.txt
+++ b/tests/auto/qmldom/errormessage/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmldomerrormessage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldomerrormessage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmldomerrormessage
SOURCES
tst_qmldomerrormessage.cpp tst_qmldomerrormessage.h
diff --git a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
index 0ca1091a18..2c2007c119 100644
--- a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
+++ b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmldomerrormessage.h"
#include <QtQmlDom/private/qqmldomerrormessage_p.h>
diff --git a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h
index f7d3a291b6..31628ef52d 100644
--- a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h
+++ b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQmlDom/qqmldom_global.h>
#include <QtTest/QtTest>
diff --git a/tests/auto/qmldom/merging/CMakeLists.txt b/tests/auto/qmldom/merging/CMakeLists.txt
index 2eaf7b9615..a33df96216 100644
--- a/tests/auto/qmldom/merging/CMakeLists.txt
+++ b/tests/auto/qmldom/merging/CMakeLists.txt
@@ -6,6 +6,13 @@
#####################################################################
## tst_qmldomitem Binary:
#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_dommerging LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
diff --git a/tests/auto/qmldom/merging/tst_dommerging.cpp b/tests/auto/qmldom/merging/tst_dommerging.cpp
index 2dc1570e32..1fa994b1a3 100644
--- a/tests/auto/qmldom/merging/tst_dommerging.cpp
+++ b/tests/auto/qmldom/merging/tst_dommerging.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_dommerging.h"
QTEST_MAIN(QQmlJS::Dom::TestDomMerging)
diff --git a/tests/auto/qmldom/merging/tst_dommerging.h b/tests/auto/qmldom/merging/tst_dommerging.h
index 01d20014bf..59937b279e 100644
--- a/tests/auto/qmldom/merging/tst_dommerging.h
+++ b/tests/auto/qmldom/merging/tst_dommerging.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_DOMMERGING_H
#define TST_DOMMERGING_H
@@ -38,16 +38,13 @@ private slots:
auto envPtr = std::shared_ptr<QQmlJS::Dom::DomEnvironment>(new QQmlJS::Dom::DomEnvironment(
qmltypeDirs,
DomEnvironment::Option::SingleThreaded | DomEnvironment::Option::NoDependencies));
- QQmlJS::Dom::DomItem env(envPtr);
- QVERIFY(env);
QString testFile1 = baseDir + QLatin1String("/test1.qml");
- env.loadFile(
- testFile1, QString(),
- [this](Path, const DomItem &, const DomItem &newIt) { this->tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadFile(baseDir, QString(), {}, LoadOption::DefaultLoad);
- envPtr->loadPendingDependencies(env);
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, testFile1),
+ [this](Path, const DomItem &, const DomItem &newIt) { this->tFile = newIt; });
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir), {});
+ envPtr->loadPendingDependencies();
QVERIFY(tFile);
tFile = tFile.field(Fields::currentItem);
diff --git a/tests/auto/qmldom/path/CMakeLists.txt b/tests/auto/qmldom/path/CMakeLists.txt
index dbffe714d9..bf3e5d20f5 100644
--- a/tests/auto/qmldom/path/CMakeLists.txt
+++ b/tests/auto/qmldom/path/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmldompath Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldompath LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmldompath
SOURCES
tst_qmldompath.cpp tst_qmldompath.h
diff --git a/tests/auto/qmldom/path/tst_qmldompath.cpp b/tests/auto/qmldom/path/tst_qmldompath.cpp
index 86ef15e035..54a634e476 100644
--- a/tests/auto/qmldom/path/tst_qmldompath.cpp
+++ b/tests/auto/qmldom/path/tst_qmldompath.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmldompath.h"
QTEST_MAIN(QQmlJS::Dom::PathEls::TestPaths)
diff --git a/tests/auto/qmldom/path/tst_qmldompath.h b/tests/auto/qmldom/path/tst_qmldompath.h
index 24d8c30bcf..f463b93164 100644
--- a/tests/auto/qmldom/path/tst_qmldompath.h
+++ b/tests/auto/qmldom/path/tst_qmldompath.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLDOMPATH_H
#define TST_QMLDOMPATH_H
@@ -16,7 +16,7 @@ namespace PathEls {
class TestPaths: public QObject {
Q_OBJECT
public:
- void testPathInternals(Path p1)
+ void testPathInternals(const Path &p1)
{
QCOMPARE(p1.component(0).kind(), Kind::Root);
QCOMPARE(p1.component(1).kind(), Kind::Current);
@@ -85,11 +85,11 @@ private slots:
auto c10=PathComponent(Any());
QCOMPARE(c10.kind(), Kind::Any);
QVERIFY(c9!=c10);
- auto c11=PathComponent(Filter([](DomItem){ return true; }));
+ auto c11=PathComponent(Filter([](const DomItem &){ return true; }));
auto c12=c11;
- auto c13=PathComponent(Filter([](DomItem){ return false; }));
- auto c14=PathComponent(Filter([](DomItem){ return false; }, u"skipAll"));
- auto c15=PathComponent(Filter([](DomItem){ return true; }, u"skipAll"));
+ auto c13=PathComponent(Filter([](const DomItem &){ return false; }));
+ auto c14=PathComponent(Filter([](const DomItem &){ return false; }, u"skipAll"));
+ auto c15=PathComponent(Filter([](const DomItem &){ return true; }, u"skipAll"));
QCOMPARE(c11.kind(), Kind::Filter);
QCOMPARE(c11, c11);
QVERIFY(c11 != c12); // native code assumed to be non comparable and different even if they are the same
@@ -132,7 +132,7 @@ private slots:
QCOMPARE(p6[6].headKind(), Kind::Empty);
auto rString = u"$.@.aa[4][\"bla\"][*].";
QCOMPARE(p6.toString(), rString);
- auto p7 = p6.filter([](DomItem){ return true; }, u"true");
+ auto p7 = p6.filter([](const DomItem &){ return true; }, u"true");
auto p7Str = p7.toString();
QCOMPARE(p7Str, u"$.@.aa[4][\"bla\"][*].[?(true)]");
auto p8 = p7.dropTail();
@@ -161,7 +161,7 @@ private slots:
void testPathSplit()
{
- QList<Path> paths({Path(),
+ const QList<Path> paths({Path(),
Path::Root(PathRoot::Env).field(u"pippo").key(u"pluto").index(4),
Path::Root(PathRoot::Env).field(u"pippo").key(u"pluto"),
Path::Root(PathRoot::Env).field(u"pippo"),
@@ -175,7 +175,7 @@ private slots:
Path::Index(4),
Path::Key(u"zz")
});
- foreach (Path p, paths) {
+ for (const Path &p : paths) {
Source s = p.split();
QCOMPARE(p, s.pathToSource.path(s.pathFromSource));
if (!s.pathFromSource)
diff --git a/tests/auto/qmldom/reformatter/CMakeLists.txt b/tests/auto/qmldom/reformatter/CMakeLists.txt
index 7b7f7e4708..1b8cfb0d8a 100644
--- a/tests/auto/qmldom/reformatter/CMakeLists.txt
+++ b/tests/auto/qmldom/reformatter/CMakeLists.txt
@@ -4,6 +4,13 @@
#####################################################################
## tst_reformatter:
#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_reformatter LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
diff --git a/tests/auto/qmldom/reformatter/tst_reformatter.cpp b/tests/auto/qmldom/reformatter/tst_reformatter.cpp
index 75f8b445e7..00d26bc55b 100644
--- a/tests/auto/qmldom/reformatter/tst_reformatter.cpp
+++ b/tests/auto/qmldom/reformatter/tst_reformatter.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_reformatter.h"
QTEST_MAIN(QQmlJS::Dom::TestReformatter)
diff --git a/tests/auto/qmldom/reformatter/tst_reformatter.h b/tests/auto/qmldom/reformatter/tst_reformatter.h
index 908c2f66a9..31d80097c1 100644
--- a/tests/auto/qmldom/reformatter/tst_reformatter.h
+++ b/tests/auto/qmldom/reformatter/tst_reformatter.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLDOMCODEFORMATTER_H
#define TST_QMLDOMCODEFORMATTER_H
@@ -8,6 +8,7 @@
#include <QtQmlDom/private/qqmldomoutwriter_p.h>
#include <QtQmlDom/private/qqmldomitem_p.h>
#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomreformatter_p.h>
#include <QtTest/QtTest>
#include <QCborValue>
@@ -24,6 +25,69 @@ class TestReformatter : public QObject
{
Q_OBJECT
public:
+private:
+ // TODO Move to a dedicated LineWriter factory / LineWriter API ?
+ enum class LineWriterType { Default, Indenting };
+ std::unique_ptr<LineWriter> getLineWriter(const SinkF &innerSink,
+ const LineWriterOptions &lwOptions)
+ {
+ return lwOptions.maxLineLength > 0
+ ? getLineWriter(LineWriterType::Indenting, innerSink, lwOptions)
+ : getLineWriter(LineWriterType::Default, innerSink, lwOptions);
+ }
+
+ std::unique_ptr<LineWriter> getLineWriter(LineWriterType type, const SinkF &innerSink,
+ const LineWriterOptions &lwOptions)
+ {
+ switch (type) {
+ case LineWriterType::Indenting:
+ return std::make_unique<IndentingLineWriter>(innerSink, QLatin1String("*testStream*"),
+ lwOptions);
+ default:
+ return std::make_unique<LineWriter>(innerSink, QLatin1String("*testStream*"),
+ lwOptions);
+ }
+ Q_UNREACHABLE_RETURN(nullptr);
+ }
+
+ // "Unix" LineWriter (with '\n' line endings) is used by default,
+ // under the assumption that line endings are properly tested in lineWriter() test.
+ static LineWriterOptions defaultLineWriterOptions()
+ {
+ LineWriterOptions opts;
+ opts.lineEndings = LineWriterOptions::LineEndings::Unix;
+ return opts;
+ }
+
+ QString formatJSCode(const QString &jsCode,
+ const LineWriterOptions &lwOptions = defaultLineWriterOptions())
+ {
+ return formatPlainJS(jsCode, ScriptExpression::ExpressionType::JSCode, lwOptions);
+ }
+
+ QString formatJSModuleCode(const QString &jsCode,
+ const LineWriterOptions &lwOptions = defaultLineWriterOptions())
+ {
+ return formatPlainJS(jsCode, ScriptExpression::ExpressionType::ESMCode, lwOptions);
+ }
+
+ QString formatPlainJS(const QString &jsCode, ScriptExpression::ExpressionType exprType,
+ const LineWriterOptions &lwOptions = defaultLineWriterOptions())
+ {
+ QString resultStr;
+ QTextStream res(&resultStr);
+ auto lwPtr = getLineWriter([&res](QStringView s) { res << s; }, lwOptions);
+ assert(lwPtr);
+ OutWriter ow(*lwPtr);
+
+ const ScriptExpression scriptItem(jsCode, exprType);
+ scriptItem.writeOut(DomItem(), ow);
+
+ lwPtr->flush(); // flush instead of eof to ignore line endings
+ res.flush();
+ return resultStr;
+ }
+
private slots:
void reindent_data()
{
@@ -145,18 +209,16 @@ private slots:
QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
QStringList qmltypeDirs =
QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
qmltypeDirs,
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
QString testFilePath = baseDir + QLatin1Char('/') + inFile;
DomItem tFile;
- env.loadBuiltins();
- env.loadFile(
- testFilePath, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFilePath),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadPendingDependencies();
MutableDomItem myFile = tFile.field(Fields::currentItem);
@@ -252,20 +314,16 @@ private slots:
QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
QStringList qmltypeDirs =
QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
qmltypeDirs,
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
QString testFilePath = baseDir + QLatin1Char('/') + inFile;
DomItem tFile;
- env.loadBuiltins();
- env.loadFile(
- testFilePath, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
-
- MutableDomItem myFile = tFile.field(Fields::currentItem);
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFilePath),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadPendingDependencies();
QString resultStr;
QTextStream res(&resultStr);
@@ -388,6 +446,324 @@ private slots:
}
}
+ void hoistableDeclaration_data()
+ {
+ QTest::addColumn<QString>("declarationToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedDeclaration");
+
+ QTest::newRow("Function") << QStringLiteral(u"function a(a,b){}")
+ << QStringLiteral(u"function a(a, b) {}");
+ QTest::newRow("AnonymousFunction") << QStringLiteral(u"let f=function (a,b){}")
+ << QStringLiteral(u"let f = function (a, b) {}");
+ QTest::newRow("Generator_lhs_star")
+ << QStringLiteral(u"function* g(a,b){}") << QStringLiteral(u"function* g(a, b) {}");
+ QTest::newRow("Generator_rhs_star")
+ << QStringLiteral(u"function *g(a,b){}") << QStringLiteral(u"function* g(a, b) {}");
+ QTest::newRow("AnonymousGenerator") << QStringLiteral(u"let g=function * (a,b){}")
+ << QStringLiteral(u"let g = function* (a, b) {}");
+ }
+
+ // https://262.ecma-international.org/7.0/#prod-HoistableDeclaration
+ void hoistableDeclaration()
+ {
+ QFETCH(QString, declarationToBeFormatted);
+ QFETCH(QString, expectedFormattedDeclaration);
+
+ QString formattedDeclaration = formatJSCode(declarationToBeFormatted);
+
+ QCOMPARE(formattedDeclaration, expectedFormattedDeclaration);
+ }
+
+ void exportDeclarations_data()
+ {
+ QTest::addColumn<QString>("exportToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedExport");
+ // not exhaustive list of ExportDeclarations as per
+ // https://262.ecma-international.org/7.0/#prod-ExportDeclaration
+
+ // LexicalDeclaration
+ QTest::newRow("LexicalDeclaration_let_Binding")
+ << QStringLiteral(u"export let name") << QStringLiteral(u"export let name;");
+ QTest::newRow("LexicalDeclaration_const_BindingList")
+ << QStringLiteral(u"export const "
+ u"n1=1,n2=2,n3=3,n4=4,n5=5")
+ << QStringLiteral(u"export const "
+ u"n1 = 1, n2 = 2, n3 = 3, n4 = 4, n5 = 5;");
+ QTest::newRow("LexicalDeclaration_const_ArrayBinding")
+ << QStringLiteral(u"export const "
+ u"[a,b]=a_and_b")
+ << QStringLiteral(u"export const "
+ u"[a, b] = a_and_b;");
+ QTest::newRow("LexicalDeclaration_let_ObjectBinding")
+ << QStringLiteral(u"export let "
+ u"{a,b:c}=a_and_b")
+ << QStringLiteral(u"export let "
+ u"{\na,\nb: c\n} = a_and_b;");
+
+ // ClassDeclaration
+ QTest::newRow("ClassDeclaration") << QStringLiteral(u"export "
+ u"class A extends B{}")
+ << QStringLiteral(u"export "
+ u"class A extends B {}");
+
+ // HoistableDeclaration
+ QTest::newRow("HoistableDeclaration_FunctionDeclaration")
+ << QStringLiteral(u"export "
+ u"function a(a,b){}")
+ << QStringLiteral(u"export "
+ u"function a(a, b) {}");
+ QTest::newRow("HoistableDeclaration_GeneratorDeclaration")
+ << QStringLiteral(u"export "
+ u"function * g(a,b){}")
+ << QStringLiteral(u"export "
+ u"function* g(a, b) {}");
+
+ // export ExportClause ;
+ QTest::newRow("ExportClause_Empty")
+ << QStringLiteral(u"export{}") << QStringLiteral(u"export {};");
+ QTest::newRow("ExportClause_1Specifier")
+ << QStringLiteral(u"export{one}") << QStringLiteral(u"export { one };");
+ QTest::newRow("ExportClause_Specifier_as")
+ << QStringLiteral(u"export{one as o}") << QStringLiteral(u"export { one as o };");
+ QTest::newRow("ExportClause_Specifier_as_StringLiteral")
+ << QStringLiteral(u"export{one as \"s\"}")
+ << QStringLiteral(u"export { one as \"s\" };");
+ QTest::newRow("ExportClause_ExportsList")
+ << QStringLiteral(u"export{one,two,three,four as fo,five}")
+ << QStringLiteral(u"export { one, two, three, four as fo, five };");
+
+ // export * FromClause ;
+ QTest::newRow("star") << QStringLiteral(u"export * from \"design\"")
+ << QStringLiteral(u"export * from \"design\";");
+ QTest::newRow("star_as_Specifier") << QStringLiteral(u"export * as star from \"design\"")
+ << QStringLiteral(u"export * as star from \"design\";");
+
+ // export ExportClause FromClause ;
+ QTest::newRow("ExportClause")
+ << QStringLiteral(u"export {i1 as n1,i2 as n2,nN} from \"M\"")
+ << QStringLiteral(u"export { i1 as n1, i2 as n2, nN } from \"M\";");
+
+ // export default HoistableDeclaration
+ QTest::newRow("Default_AnonymousFunction")
+ << QStringLiteral(u"export default function(a,b){}")
+ << QStringLiteral(u"export default function (a, b) {}");
+ QTest::newRow("Default_AnonymousGenerator")
+ << QStringLiteral(u"export default function * (a,b){}")
+ << QStringLiteral(u"export default function* (a, b) {}");
+ QTest::newRow("Default_Function") << QStringLiteral(u"export default function a(a,b){}")
+ << QStringLiteral(u"export default function a(a, b) {}");
+
+ // export default ClassDeclaration
+ QTest::newRow("Default_Class") << QStringLiteral(u"export default class A{}")
+ << QStringLiteral(u"export default class A {}");
+ QTest::newRow("Default_AnonymousClass")
+ << QStringLiteral(u"export default class extends A{}")
+ << QStringLiteral(u"export default class extends A{}");
+
+ // export default Expression
+ QTest::newRow("Default_Expression") << QStringLiteral(u"export default 1+1")
+ << QStringLiteral(u"export default 1 + 1;");
+ QTest::newRow("Default_ArrowFunctionExpression")
+ << QStringLiteral(u"export default(x,y)=> x+2")
+ << QStringLiteral(u"export default (x, y) => x + 2;");
+ }
+
+ // https://262.ecma-international.org/7.0/#prod-ExportDeclaration
+ void exportDeclarations()
+ {
+ QFETCH(QString, exportToBeFormatted);
+ QFETCH(QString, expectedFormattedExport);
+
+ QString formattedExport = formatJSModuleCode(exportToBeFormatted);
+
+ QEXPECT_FAIL("ExportClause_Specifier_as_StringLiteral",
+ "export {a as \"string name\"} declaration is not supported yet", Abort);
+ QEXPECT_FAIL("star_as_Specifier", "export * as star declaration is not supported yet",
+ Abort);
+ QEXPECT_FAIL("Default_AnonymousClass", "QTBUG-122291", Abort);
+ QEXPECT_FAIL("Default_AnonymousFunction", "QTBUG-122291", Abort);
+ QEXPECT_FAIL("Default_AnonymousGenerator", "QTBUG-122291", Abort);
+ QCOMPARE(formattedExport, expectedFormattedExport);
+ }
+
+ void carryoverMJS_data()
+ {
+ QTest::addColumn<QString>("codeToBeFormatted");
+ QTest::addColumn<int>("maxLineLength");
+ QTest::addColumn<QString>("expectedFormattedCode");
+
+ QTest::newRow("LongExportList_NoMaxLineLength")
+ << QStringLiteral(u"export const n1=1,n2=2,n3=3,n4=4,n5=5") << -1
+ << QStringLiteral(u"export const n1 = 1, n2 = 2, n3 = 3, n4 = 4, n5 = 5;");
+ QTest::newRow("LongExportList_MaxLineLength20")
+ << QStringLiteral(u"export const n1=1,n2=2,n3=3,n4=4,n5=5") << 20
+ << QStringLiteral(u"export const n1 = 1,\n"
+ u" n2 = 2, n3 = 3,\n"
+ u" n4 = 4, n5 = 5;");
+ }
+
+ void carryoverMJS()
+ {
+ QFETCH(QString, codeToBeFormatted);
+ QFETCH(int, maxLineLength);
+ QFETCH(QString, expectedFormattedCode);
+
+ LineWriterOptions lwOptions;
+ lwOptions.maxLineLength = maxLineLength;
+ // TODO maybe fetch this
+ lwOptions.formatOptions.indentSize = 2;
+ QString formattedCode = formatJSModuleCode(codeToBeFormatted, lwOptions);
+
+ QEXPECT_FAIL("LongExportList_MaxLineLength20", "QTBUG-122260", Abort);
+ QCOMPARE(formattedCode, expectedFormattedCode);
+ }
+
+ void importDeclarations_data()
+ {
+ QTest::addColumn<QString>("importToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedImport");
+ // not exhaustive list of ExportDeclarations as per
+ // https://262.ecma-international.org/7.0/#prod-ImportDeclaration
+
+ // import ModuleSpecifier;
+ QTest::newRow("ModuleSpecifier")
+ << QStringLiteral(u"import \"Module\"") << QStringLiteral(u"import \"Module\";");
+
+ // import ImportClause FromClause ;
+ QTest::newRow("NameSpaceImport") << QStringLiteral(u"import * as d from \"design\";")
+ << QStringLiteral(u"import * as d from \"design\";");
+
+ QTest::newRow("NamedImports") << QStringLiteral(u"import {b,cd as c,d} from \"M\";")
+ << QStringLiteral(u"import { b, cd as c, d } from \"M\";");
+
+ QTest::newRow("DefaultBindung") << QStringLiteral(u"import defaultExport from \"M\"")
+ << QStringLiteral(u"import defaultExport from \"M\";");
+ QTest::newRow("DefaultBindung_NameSpaceImport")
+ << QStringLiteral(u"import defaultExport, * as m from \"M\";")
+ << QStringLiteral(u"import defaultExport, * as m from \"M\";");
+ QTest::newRow("DefaultBinding_NamedImports")
+ << QStringLiteral(u"import defaultExport,{b,cd as c,d} from \"M\";")
+ << QStringLiteral(u"import defaultExport, { b, cd as c, d } from \"M\";");
+
+ QTest::newRow("ImportClause_Specifier_as_StringLiteral")
+ << QStringLiteral(u"import{\"s\" as s} from \"M\"")
+ << QStringLiteral(u"import { \"s\" as s } from \"M\";");
+ }
+
+ // https://262.ecma-international.org/7.0/#prod-ImportDeclaration
+ void importDeclarations()
+ {
+ QFETCH(QString, importToBeFormatted);
+ QFETCH(QString, expectedFormattedImport);
+
+ QString formattedImport = formatJSModuleCode(importToBeFormatted);
+
+ QEXPECT_FAIL(
+ "ImportClause_Specifier_as_StringLiteral",
+ "import {\"string literal export\" as alias } declaration is not supported yet",
+ Abort);
+ QCOMPARE(formattedImport, expectedFormattedImport);
+ }
+
+ void methodDefinitions_data()
+ {
+ QTest::addColumn<QString>("methodToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedMethod");
+
+ // ObjectInitializer
+ QTest::newRow("ObjGetter") << QStringLiteral(u"const o={get a(){},}")
+ << QStringLiteral(u"const o = {\nget a(){}\n}");
+ QTest::newRow("ObjSetter") << QStringLiteral(u"const o={set a(a){},}")
+ << QStringLiteral(u"const o = {\nset a(a){}\n}");
+ QTest::newRow("ComputedObjPropertyGetter")
+ << QStringLiteral(u"const o={get [a+b](){},}")
+ << QStringLiteral(u"const o = {\nget [a + b](){}\n}");
+
+ // Generator
+ QTest::newRow("ObjPropertyGenerator")
+ << QStringLiteral(u"const o={*a(){1+1;},}")
+ << QStringLiteral(u"const o = {\n*a(){\n1 + 1;\n}\n}");
+ QTest::newRow("ComputedClassPropertyGenerator")
+ << QStringLiteral(u"class A{*[a+b](){}}")
+ << QStringLiteral(u"class A {\n*[a + b](){}\n}");
+
+ // ClassDefinitions
+ QTest::newRow("ClassGetter") << QStringLiteral(u"class A{get a(){}}")
+ << QStringLiteral(u"class A {\nget a(){}\n}");
+ QTest::newRow("ClassSetter") << QStringLiteral(u"class A{set a(a){}}")
+ << QStringLiteral(u"class A {\nset a(a){}\n}");
+ }
+
+ // https://262.ecma-international.org/7.0/#sec-method-definitions
+ void methodDefinitions()
+ {
+ QFETCH(QString, methodToBeFormatted);
+ QFETCH(QString, expectedFormattedMethod);
+
+ QString formattedMethod = formatJSCode(methodToBeFormatted);
+
+ QCOMPARE(formattedMethod, expectedFormattedMethod);
+ }
+
+ void statementList_data()
+ {
+ QTest::addColumn<QString>("codeToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedCode");
+
+ QTest::newRow("StatementsOnTheSameLine")
+ << QStringLiteral(u"a=1;b=1;") << QStringLiteral(u"a = 1;\nb = 1;");
+
+ QTest::newRow("StatementsOnSuccessiveLines")
+ << QStringLiteral(u"a=1;\nb=1;") << QStringLiteral(u"a = 1;\nb = 1;");
+
+ QTest::newRow("EmptyLineBetweenStatements")
+ << QStringLiteral(u"a=1;\n\nb=1;") << QStringLiteral(u"a = 1;\n\nb = 1;");
+
+ QTest::newRow("MultipleEmptyLinesBetweenStatements")
+ << QStringLiteral(u"a=1;\n\n\n\n\n\nb=1;") << QStringLiteral(u"a = 1;\n\nb = 1;");
+
+ QTest::newRow("MultilineStatementWithStatementOnTheFollowingLine")
+ << QStringLiteral(u"console.log(\n\n);\nb = 1;")
+ << QStringLiteral(u"console.log();\nb = 1;");
+
+ QTest::newRow("StatementWithPostCommentAndStatementOnTheFollowingLine")
+ << QStringLiteral(u"a=1;//\nb=1;") << QStringLiteral(u"a = 1;//\nb = 1;");
+
+ QTest::newRow("StatementWithPostCommentAndEmptyLineToNextStatement")
+ << QStringLiteral(u"a=1;//\n\nb=1;") << QStringLiteral(u"a = 1;//\n\nb = 1;");
+
+ QTest::newRow("StatementWithPostCommentAndMultipleEmptyLinesToNextStatement")
+ << QStringLiteral(u"a=1;//\n\n\n\n\nb=1;") << QStringLiteral(u"a = 1;//\n\nb = 1;");
+
+ QTest::newRow("StatementsWithCommentInBetweenThem")
+ << QStringLiteral(u"a=1;\n//\nb=1;") << QStringLiteral(u"a = 1;\n//\nb = 1;");
+
+ QTest::newRow("StatementsWithCommentAndSingleEmptyLineInBetweenThem")
+ << QStringLiteral(u"a=1;\n\n//\n\nb=1;")
+ << QStringLiteral(u"a = 1;\n\n//\n\nb = 1;");
+
+ QTest::newRow("StatementsWithCommentAndMultipleEmptyLinesInBetweenThem")
+ << QStringLiteral(u"a=1;\n\n\n\n//\n\n\nb=1;")
+ << QStringLiteral(u"a = 1;\n\n//\n\nb = 1;");
+
+ QTest::newRow("StatementWithSingleEmptyLineAndPreCommentOnNextStatement")
+ << QStringLiteral(u"a=1;\n\n//\nb=1;") << QStringLiteral(u"a = 1;\n\n//\nb = 1;");
+
+ QTest::newRow("StatementWithMultipleEmptyLinesAndPreCommentOnNextStatement")
+ << QStringLiteral(u"a=1;\n\n\n\n\n\n\n\n//\nb=1;")
+ << QStringLiteral(u"a = 1;\n\n//\nb = 1;");
+ }
+
+ void statementList()
+ {
+ QFETCH(QString, codeToBeFormatted);
+ QFETCH(QString, expectedFormattedCode);
+
+ QString formattedCode = formatJSCode(codeToBeFormatted);
+
+ QCOMPARE(formattedCode, expectedFormattedCode);
+ }
+
private:
};
diff --git a/tests/auto/qmldom/standalone/CMakeLists.txt b/tests/auto/qmldom/standalone/CMakeLists.txt
deleted file mode 100644
index e141ec0835..0000000000
--- a/tests/auto/qmldom/standalone/CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-#####################################################################
-## tst_dom_all Binary executing all tests together
-## (simpler to verify coverage)
-#####################################################################
-# Collect test data
-file(GLOB_RECURSE test_data_glob
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
- domdata/*)
-list(APPEND test_data ${test_data_glob})
-
-set(QMLDOM_EXTERNAL_BUILD OFF CACHE BOOL "If the build is against an external Qt, and not tested inside a build of this Qt" FORCE)
-add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/qmldom/standalone qmldomlib)
-
-if(MSVC)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
-elseif (MINGW)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
-endif()
-
-qt_internal_add_test(tst_standalone
- SOURCES
- tst_standalone.cpp
- DEFINES
- QT_DEPRECATED_WARNINGS
- QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../domdata"
- LIBRARIES
- Qt::Core
- Qt::QmlPrivate
- qmldomlib
- TESTDATA ${test_data}
-)
-
-qt_internal_extend_target(tst_standalone CONDITION ANDROID OR IOS
- DEFINES
- QT_QMLTEST_DATADIR=":/domdata"
-)
diff --git a/tests/auto/qmldom/standalone/tst_standalone.cpp b/tests/auto/qmldom/standalone/tst_standalone.cpp
deleted file mode 100644
index 14b6424525..0000000000
--- a/tests/auto/qmldom/standalone/tst_standalone.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "tst_standalone.h"
-QTEST_MAIN(QQmlJS::Dom::TestStandalone)
diff --git a/tests/auto/qmldom/standalone/tst_standalone.h b/tests/auto/qmldom/standalone/tst_standalone.h
deleted file mode 100644
index c403c660e6..0000000000
--- a/tests/auto/qmldom/standalone/tst_standalone.h
+++ /dev/null
@@ -1,634 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include <QtTest/QtTest>
-#ifdef QMLDOM_STANDALONE
-# include "qmldom/qqmldom_global.h"
-// common declarations
-# include "qmldom/qqmldomitem_p.h"
-// comparisons of two DomItems
-# include "qmldom/qqmldomcompare_p.h"
-// field filters to compare only selected fields (ignore for example location changes)
-# include "qmldom/qqmldomfieldfilter_p.h"
-// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
-# include "qmldom/qqmldomelements_p.h"
-// cast of the top level items (DomEnvironments,...)
-# include "qmldom/qqmldomtop_p.h"
-#else
-# include <QtQmlDom/qqmldom_global.h>
-// common declarations
-# include <QtQmlDom/private/qqmldomitem_p.h>
-// comparisons of two DomItems
-# include <QtQmlDom/private/qqmldomcompare_p.h>
-// field filters to compare only selected fields (ignore for example location changes)
-# include <QtQmlDom/private/qqmldomfieldfilter_p.h>
-// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
-# include <QtQmlDom/private/qqmldomelements_p.h>
-// cast of the top level items (DomEnvironments,...)
-# include <QtQmlDom/private/qqmldomtop_p.h>
-#endif
-
-#include <QDebug>
-#include <QLatin1String>
-#include <QLatin1Char>
-#include <QLibraryInfo>
-#include <QDir>
-
-QT_BEGIN_NAMESPACE
-namespace QQmlJS {
-namespace Dom {
-
-class TestStandalone : public QObject
-{
- Q_OBJECT
-private slots:
- void testLoadEditSave()
- {
- QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
- QStringList qmltypeDirs =
- QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
-
- // qDebug() << "Creating an environment loading qml from the directories" << qmltypeDirs;
- // qDebug() << "single threaded, no dependencies";
- DomItem env = DomEnvironment::create(
- qmltypeDirs,
- QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
- | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
-
- QString testFilePath = baseDir + QLatin1String("/file1.qml");
- DomItem tFile; // place where to store the loaded file
-
- // qDebug() << "loading the file" << testFilePath;
- env.loadFile(
- testFilePath, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) {
- tFile = newIt; // callback called when everything is loaded that receives the
- // loaded external file pair (path, oldValue, newValue)
- },
- LoadOption::DefaultLoad);
-
- // trigger the load
- env.loadPendingDependencies();
-
- // # Read only API: DomItem is a generic pointer for read only access to Dom Itmes :)
- {
- // ## declarative json like API
- DomItem qmlFile = tFile.field(Fields::currentItem);
- DomItem imports = qmlFile.field(Fields::imports);
- DomItem qmlObj = qmlFile.field(Fields::components)
- .key(QString())
- .index(0)
- .field(Fields::objects)
- .index(0);
-
- // ### Dump
- // any DomItem can be dumped
- // qDebug() << "writing to QDebug dumps that element:" << imports;
- // often the dump is too verbose, and one might want it to a separate file
- QString dumpFilePath = QDir(QDir::tempPath())
- .filePath(QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- qmlFile.dump(dumpFilePath, FieldFilter::defaultFilter());
- // qDebug() << "dumped file to" << dumpFilePath;
-
- // ### Paths
- // qDebug() << "To identify a DomItem a canonical path can be used:"
- // << imports.canonicalPath();
- // a path can be converted to/from strings
- QString pString = imports.canonicalPath().toString();
- Path importsPath = Path::fromString(pString);
- // and loaded again using the .path(somePath) method
- DomItem imports2 = env.path(importsPath);
- QCOMPARE(imports, imports2);
- // the canonical path is absolute, but you can have relative paths
- Path first = Path::Index(0);
- DomItem firstImport = imports.path(first);
- // an existing path can also be extended
- Path firstImportPath = importsPath.index(0);
- QCOMPARE(firstImportPath, firstImport.canonicalPath());
- // the normal elements of a path are index, key, field
- // Uppercase static method creates one, lowercase appends to an existing path.
- Path mainComponentPath = Path::Field(Fields::components).key("").index(0);
- DomItem mainComponent = qmlFile.path(mainComponentPath);
- // DomItems have the same methods to access their elements
- DomItem mainComponent2 = qmlFile.field(Fields::components).key("").index(0);
- // two other special ements are root (root element for absolute paths)
- Path topPath = Path::Root(PathRoot::Top);
- QCOMPARE(topPath, importsPath[0]);
- // the current element performs an operation (tipically a lookup or iteration) at the
- // current path location (not handled here)
- Path lookupPath = Path::Current(PathCurrent::Lookup);
-
- // there are various visit methods to iterate/visit DomItems in particular visitTree
- // which is quite flexible.
- // They normally use callbacks that can return false to stop the iteration.
- // Still often the DomKind specific for loop presentated later are clearer and more
- // convenient
- {
- QString s;
- QTextStream dbg(&s);
- imports.visitTree(
- Path(),
- [&dbg](Path p, const DomItem &el, bool) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "*"
- << p.last().toString() << " " << domKindToString(el.domKind())
- << "(" << el.internalKindStr() << ")\n";
- // returning false here stops the whole iteration
- return true;
- },
- VisitOption::Default, // we want a recursive visit visiting also the top and
- // adopted
- [&dbg](Path p, const DomItem &, bool canonicalChild) {
- // returning false here skips that branch
- if (!canonicalChild) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "+"
- << p.last().toString() << " (adopted, will not recurse)\n";
- } else if (p && p.headIndex(0) % 2 == 1) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "-"
- << p.last().toString() << " *recursive visit skipped*\n";
- return false; // we skip odd entries in lists;
- } else {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "+"
- << p.last().toString() << "\n";
- }
- return true;
- },
- [&dbg](Path p, const DomItem &, bool) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "="
- << p.last().toString() << "\n";
- return true;
- });
- dbg.flush();
- QCOMPARE(s,
- QStringLiteral("* List(List)\n"
- "+\n"
- " *[0] Object(Import)\n"
- " +[0]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.implicit Value(ConstantData)\n"
- " +.implicit\n"
- " =.implicit\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[0]\n"
- " *[1] Object(Import)\n"
- " -[1] *recursive visit skipped*\n"
- " *[2] Object(Import)\n"
- " +[2]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.implicit Value(ConstantData)\n"
- " +.implicit\n"
- " =.implicit\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[2]\n"
- " *[3] Object(Import)\n"
- " -[3] *recursive visit skipped*\n"
- " *[4] Object(Import)\n"
- " +[4]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[4]\n"
- "=\n"));
- }
-
- // ### DomKind
- // any DomItem belongs to 5 basic types
-
- // 1. Object (a C++ object)
- QCOMPARE(qmlFile.domKind(), DomKind::Object);
- // The underlying type of the c++ object can be found with .internalKind()
- QCOMPARE(qmlFile.internalKind(), DomType::QmlFile);
- // .initernalKindStr() is a convenience string version of it
- QCOMPARE(qmlFile.internalKindStr(), u"QmlFile");
- // the object attributes (fields) can be reached using .field(u"filedName")
- // normally one should not use a string, but the Fields:: constant
- DomItem qmlFile2 = tFile.field(Fields::currentItem);
- // all the available fields can be listed via fields()
- // qDebug() << "The" << qmlObj.internalKindStr() << "at" << qmlObj.canonicalPath()
- // << "has the following fields:" << qmlObj.fields();
- // we can access the underlying C++ object with as<>
- // if (const QmlFile *qmlFilePtr = qmlFile.as<QmlFile>())
- // qDebug() << "The QmlFile lives at the address" << qmlFilePtr;
- //// We can get the shared pointer of the owner type (which for the file is the QmlFile
- /// itself
- // if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
- // qDebug() << "QmlFile uses shared pointers as ownership method, the underlying
- // address "
- // "is the same"
- // << qmlFilePtr.get();
-
- // 2. a (Cbor-) Value, i.e a string, number,...
- DomItem fPath = qmlFile.field(Fields::canonicalFilePath);
- QCOMPARE(fPath.domKind(), DomKind::Value);
- // the Cbor representation of a value can be extracted with .value(), and in this case
- // we can then call toString
- // qDebug() << "The filePath DomItem is " << fPath << " and it still 'knows' its path "
- // << fPath.canonicalPath() << " but can have it also as value:" <<
- // fPath.value()
- // << "or even better as string." <<
- // fPath.value().toString(QLatin1String("*none*"));
- // a DomItem might have a valid value() even if it is not of type DomKind::Value, indeed
- // CBor maps and lists are mapped to DomKind::Map and DomKind::List, and can be
- // traversed thought that but also have a valid value().
-
- // 3. a list
- QCOMPARE(imports.domKind(), DomKind::List);
- // the number of elements can be sound with .indexes() and with .index(n) we access each
- // element
- // qDebug() << "We have " << imports.indexes() << " imports, and the first is "
- // << imports.index(0);
- // If we want to just loop on the elements .values() is the most convenient way
- // technically values *always* works even for objects and maps, iterating on the values
- // for (DomItem import : imports.values()) {
- // if (const Import *importPtr = import.as<Import>()) {
- // if (importPtr->implicit)
- // qDebug() << importPtr->uri << importPtr->version.stringValue();
- // }
- //}
-
- // 4. a map
- DomItem bindings = qmlObj.field(Fields::bindings);
- QCOMPARE(bindings.domKind(), DomKind::Map);
- // The keys of the map can be reached either with .keys() or .sortedKeys(), each element
- // with .key(k)
- // qDebug() << "bindings";
- // for (QString k : bindings.sortedKeys()) {
- // for (DomItem b : bindings.key(k).values()) {
- // qDebug() << k << ":" << b;
- // }
- //}
-
- // 5 The empty element
- DomItem empty;
- QCOMPARE(empty.domKind(), DomKind::Empty);
- // The empty element is the only DomItem that casted to bool returns false, so checking
- // for it can be just an implicit cast to bool
- QVERIFY(bindings && !empty);
- // the empty element supports all the previus operations so that one can traverse a non
- // existing path without checking at every element, but only check the result
- DomItem nonExisting = qmlFile.field(u"no-existing").key(u"a").index(0);
- QVERIFY(!nonExisting);
-
- // the index operator [] can be used instead of .index/.key/.field, it might be slightly
- // less efficient but works
-
- // find type
- // access type
-
- // ### write out
- // it is possible to write out a qmlFile (actually also parts of it), which will
- // automatically reformat it
- QString reformattedFilePath =
- QDir(QDir::tempPath())
- .filePath(QFileInfo(testFilePath).baseName() + QLatin1String(".qml"));
- DomItem newFile = qmlFile.writeOut(reformattedFilePath);
- // qDebug() << "reformatted written at " << reformattedFilePath;
-
- // ## Jumping around
- // ### Generic Methods
- // from a DomItem you do no have just deeper in the tree, you can also go up the
- // hierarch toward the root .container() just goes up one step in the canonicalPath of
- // the object
- QCOMPARE(imports, firstImport.container());
- // .containingObject() goes up to the containing DomKind::Object, skipping over all Maps
- // and Lists
- QCOMPARE(qmlFile, firstImport.containingObject());
- // .owner() returns the shared pointer based "owner" object, qmlFile and
- // ScriptExpression are owningItems
- QCOMPARE(qmlFile, bindings.owner());
- // .top() goes to the top of the tree, i.e the environment (or the universe)
- QCOMPARE(env, bindings.top());
- // environment is normally the same as top, but making sure it is a actually a
- // DomEnvironment
- QCOMPARE(env, bindings.environment());
- // the universe is a cache of loaded files which for each file keeps two versions: the
- // latest and the latest valid it can be reached with .universe(), from the universe you
- // cannot get back to the environment.
- QCOMPARE(env.universe().internalKind(), DomType::DomUniverse);
-
- // ## QML Oriented Methods
- // The Dom model is not for generic json-like structures, so there are methods tailored
- // for Qml and its structure The methods can succeed if there is a clearly defined
- // unique result. sometime there is an obivious, but not necessarily unique choice
- // (tipically going up the hierarchy), for example given a qml file the obvious choice
- // for a component is the root component, but the file might contain other inline
- // components, and for an object with different version exposed (C++ property
- // versioning) the latest version is the natural choice, but other might be available.
- // In these case passing GoTo::MostLikely as argument makes the method to this obivious
- // choice (or possibly even only choice if no other versions/components are actually
- // defined), instead of refusing any potentially ambiguous situation and returning the
- // empty element.
-
- // .fileObject() goes to the object representing the whole file
- // (from either the external object returned by load or from inside the file)
- DomItem fileObject = tFile.fileObject();
- DomItem fileObject2 = imports.fileObject();
- QCOMPARE(fileObject, fileObject2);
- QCOMPARE(fileObject.internalKind(), DomType::QmlFile);
- // .component() goes to the component object.
- QCOMPARE(qmlObj.component(), qmlFile.component(GoTo::MostLikely));
- // .pragmas gives access to the pragmas of the current component
- QCOMPARE(qmlFile.pragmas(), qmlFile.field(Fields::pragmas));
-
- // QmlObject
- // QmlObject if the main to represent the type information (methods, bindings,
- // properties,...) of qml. Please note that QmlObject -> component operation is
- // potentially lossy, when multiple version are exposed, so we represent a type through
- // its root object, not through a component.
-
- // .qmlObject() goes to the current QmlObject
- QCOMPARE(qmlObj, bindings.qmlObject());
-
- // Given the centrality of QmlObject several of its attributes have convenience methods
- // to access them:
-
- // .children() makes subObjects contained inside a QmlObject accessible
- // note that it is possible to add objects also by directly binding the children or data
- // attribute, those children are not listed here, this accesses only those listed inside
- // the QmlObject
- QCOMPARE(qmlObj.children(), qmlObj.field(Fields::children));
- DomItem subObj0 = qmlObj.children().index(0);
- // .child(<i>) is a shortcut for .children.index(<i>)
- QCOMPARE(subObj0, qmlObj.child(0));
- // rootQmlObject goes to the root qmlObject (unless one reaches an empty element)
- QVERIFY(bool(subObj0));
- QCOMPARE(subObj0.rootQmlObject(), qmlObj);
- // .bindings() returns the bindings defined in the current object
- QCOMPARE(bindings, qmlObj.bindings());
- DomItem mCompObj = qmlObj.child(0)
- .child(0)
- .bindings()
- .key(u"delegate")
- .index(0)
- .field(Fields::value)
- .child(1);
- // .methods() gives methods definitions and signals
- DomItem methods = mCompObj.methods();
- // qDebug() << "mCompObj methods:";
- for (QString methodName : methods.sortedKeys()) {
- for (DomItem method : methods.key(methodName).values()) {
- if (const MethodInfo *methodPtr = method.as<MethodInfo>()) {
- QCOMPARE(methodName, methodPtr->name);
- // qDebug() << " " << methodPtr->name << methodPtr->methodType;
- }
- }
- }
- // qDebug() << "mCompObj propertyDefs:";
- // .propertyDefs() returns the properties defined in the current object
- DomItem pDefs = mCompObj.propertyDefs();
- for (QString pDefName : pDefs.sortedKeys()) {
- for (DomItem pDef : pDefs.key(pDefName).values()) {
- if (const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>()) {
- QCOMPARE(pDefName, pDefPtr->name);
- // qDebug() << " " << pDefPtr->name << pDefPtr->typeName;
- }
- }
- }
- // binding and property definitions are about the ones defined in the current object
- // often one is interested also to the inherited properties.
- // Here PropertyInfo helps, it list all the definitions and bindings for a given
- // property in the inheritance order (local definitions, parent definitions, parent
- // parent definitions,...) .propertyInfos() gives access in the usual way (through a
- // DomItem)
- DomItem propertyInfos = mCompObj.propertyInfos();
- // .propertyInfoWithName(<name>) directly accesses one
- PropertyInfo pInfo = mCompObj.propertyInfoWithName(QStringLiteral(u"a"));
- // qDebug() << "bindings" << pInfo.bindings;
- // .propertyInfoNames() gives the names of the properties
- QCOMPARE(propertyInfos.keys(), mCompObj.propertyInfoNames());
-
- // .globalScope() goes to the globa scope object
- QCOMPARE(qmlObj.globalScope().internalKind(), DomType::GlobalScope);
- // and scope to the containing scope
- QCOMPARE(bindings.scope(), qmlObj);
- }
- // mutate & edit
- {
- // DomItem handles read-only access, but if one wants to change something it cannot be
- // used. MutableDomItem can be initialized with a DomItem, and provides also the methods
- // to modify the item. It keeps the OwningItem and the path to the current item.
- // Mutability can invalidate pointers to non owning items (and thus DomItem).
- // For this reason one should not modify something that other code can have a DomItem
- // pointer to, the best practice is to make shared object immutable and never change
- // them. One should modify only a copy that is used only by a single thread, and do not
- // shared untils all modifications are done. A MutableItem stays valid (or becomes
- // Empty), but stays safe to use
- //
- // Assuming one guarantees that editing is ok, doing it in practice is just about using
- // MutableDomItem instead of DomItem
- // It is possible to simply initialize a mutable item with a DomItem
- DomItem origFile = tFile.fileObject();
- MutableDomItem myFile0(origFile);
- // Normally it is better to have a separate environment. Is possible to avoid re-reading
- // the files already read by sharing the Universe between two environments.
- // But normally it is better and just as safe to work on a copy, so that one can be sure
- // that no DomItem is kept by other code gets invalidated. The .makeCopy creates a deep
- // copy, and by default (DomItem::CopyOption::EnvConnected) creates an environment which
- // to takes all non local elements from the current environment (its parent environment)
- // but replaces the file object with the copy. When finished one can replace the file
- // object of the parent with the new one using .commitToBase().
- MutableDomItem myFile = origFile.makeCopy();
- QVERIFY(myFile.ownerAs<QmlFile>()
- && myFile.ownerAs<QmlFile>() != myFile0.ownerAs<QmlFile>());
- QVERIFY(myFile.environment().ownerAs<DomEnvironment>()
- && myFile.environment().ownerAs<DomEnvironment>()
- != myFile0.environment().ownerAs<DomEnvironment>());
- // we can check that the two files are really identical (.item() give back the DomItem
- // of a MutableDomItem
- Q_ASSERT(domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
- // MutableDomItem has the same methods as DomItem
- MutableDomItem qmlObj = myFile.qmlObject(GoTo::MostLikely);
- MutableDomItem qmlObj2 = myFile.field(Fields::components)
- .key(QString())
- .index(0)
- .field(Fields::objects)
- .index(0);
- QVERIFY(bool(qmlObj));
- QCOMPARE(qmlObj, qmlObj2);
- // qDebug() << "mutable qmlObj has canonicalPath " << qmlObj.canonicalPath();
- // but it adds methods to add
- // * new PropertyDefinitions
- PropertyDefinition b;
- b.name = QLatin1String("xx");
- b.typeName = QLatin1String("int");
- // if we make t true we also have to give a value...
- MutableDomItem addedPDef = qmlObj.addPropertyDef(b);
- // qDebug() << "added property definition at:" << addedPDef.pathFromOwner();
- // * new bindings
- MutableDomItem addedBinding0 = qmlObj.addBinding(
- Binding("height",
- std::shared_ptr<ScriptExpression>(new ScriptExpression(
- QStringLiteral(u"243"),
- ScriptExpression::ExpressionType::BindingExpression))));
- // by default addBinding, addPropertyDef and addMethod have the AddOption::Override
- // to make it more difficult to create invalid documents, so that only the
- // following binding remains (where we use the convenience constructor that constucts
- // the ScriptExpression internally
- MutableDomItem addedBinding =
- qmlObj.addBinding(Binding("height", QStringLiteral(u"242")));
- // qDebug() << "added binding at:" << addedBinding.pathFromOwner();
- // * new methods
- MethodInfo mInfo;
- mInfo.name = QLatin1String("foo2");
- MethodParameter param;
- param.name = QLatin1String("x");
- mInfo.parameters.append(param);
- mInfo.setCode(QLatin1String("return 4*10+2 - x"));
- // we can change the added binding
- addedBinding.setCode(QLatin1String("245"));
- MutableDomItem addedMethod = qmlObj.addMethod(mInfo);
- // qDebug() << "added method at:" << addedMethod.pathFromOwner();
- // * new QmlObjects
- QmlObject subObj;
- subObj.setName(QLatin1String("Item"));
- MutableDomItem addedSubObj = qmlObj.addChild(subObj);
- // qDebug() << "added subObject at:" << addedMethod.pathFromOwner();
- // It is possible to modify the content of objects, using the mutableAs method
- if (PropertyDefinition *addedPDefPtr = addedPDef.mutableAs<PropertyDefinition>()) {
- addedPDefPtr->isRequired = true;
- }
- MutableDomItem firstChild = qmlObj.child(0);
- // qDebug() << "firstChild:" << firstChild;
- // It is possible remove objects
- if (QmlObject *qmlObjPtr = qmlObj.mutableAs<QmlObject>()) {
- QList<QmlObject> children = qmlObjPtr->children();
- children.removeAt(0);
- qmlObjPtr->setChildren(children);
- }
- // But as MutableDomItem does not keep the identity, just the same position, the
- // addedSubObj becomes invalid (and firstChild changes)
- // qDebug() << "after removal firstChild:" << firstChild;
- // qDebug() << "addedSubObj becomes invalid:" << addedSubObj;
- // qDebug() << "But the last object is the added one:"
- // << qmlObj.child(qmlObj.children().indexes() - 1);
-
- // now origFile are different
- Q_ASSERT(!domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
- // and we can look at the places where they differ
- // qDebug().noquote().nospace()
- // << "Edits introduced the following diffs (ignoring file locations"
- // << " and thus whitespace/reformatting changes):\n"
- // << domCompareStrList(origFile, myFile, FieldFilter::noLocationFilter(),
- // DomCompareStrList::AllDiffs)
- // .join(QString());
-
- QString reformattedFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".qml"));
- Q_ASSERT(myFile.as<QmlFile>()
- && myFile.as<QmlFile>() == myFile.fileObject().as<QmlFile>());
- MutableDomItem reformattedEditedFile = myFile.writeOut(reformattedFilePath);
- // the reformatted edited file might be different from the edited file
- // but the differences are just in file location/formatting
- Q_ASSERT(domCompareStrList(myFile, reformattedEditedFile,
- FieldFilter::noLocationFilter())
- .isEmpty());
-
- // qDebug() << "The edited file was written at " << reformattedFilePath;
- QString dumpFilePath = QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited0")
- + QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- myFile.dump(dumpFilePath);
- // qDebug() << "The non reformatted edited file was dumped at " << dumpFilePath;
- QString reformattedDumpFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- reformattedEditedFile.dump(reformattedDumpFilePath);
- // qDebug() << "The edited file was dumped at " << reformattedDumpFilePath;
- // The top environment still contains the original loaded file
- Q_ASSERT(origFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>());
- QCOMPARE(tFile.fileObject().refreshed().ownerAs<QmlFile>(),
- origFile.ownerAs<QmlFile>());
- QCOMPARE(tFile.fileObject().ownerAs<QmlFile>(), origFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
- != reformattedEditedFile.ownerAs<QmlFile>());
- // we can commit the reformatted file
- if (!reformattedEditedFile.commitToBase()) {
- qWarning() << "No reformatted file to commit";
- }
- // myFile might not be the same (If and updated check is requested, not the case here)
- if (myFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>()
- && !myFile.commitToBase()) {
- qWarning() << "Could not commit edited file";
- }
- // but refreshing it (looking up its canonical path) we always find the updated file
- QCOMPARE(myFile.refreshed().ownerAs<QmlFile>(),
- reformattedEditedFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
- == reformattedEditedFile.ownerAs<QmlFile>());
- }
- }
-};
-
-} // Dom
-} // QQmlJS
-QT_END_NAMESPACE
diff --git a/tests/auto/qmldom/stringdumper/CMakeLists.txt b/tests/auto/qmldom/stringdumper/CMakeLists.txt
index a67041d433..2b09281302 100644
--- a/tests/auto/qmldom/stringdumper/CMakeLists.txt
+++ b/tests/auto/qmldom/stringdumper/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qmldomstringdumper Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldomstringdumper LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmldomstringdumper
SOURCES
tst_qmldomstringdumper.cpp tst_qmldomstringdumper.h
diff --git a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
index bc25070838..fab3bc5be8 100644
--- a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
+++ b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmldomstringdumper.h"
QTEST_MAIN(QQmlJS::Dom::TestStringDumper)
diff --git a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h
index 9d72b589c1..28e844db73 100644
--- a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h
+++ b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLDOMSTRINGDUMPER_H
#define TST_QMLDOMSTRINGDUMPER_H
diff --git a/tests/auto/qmlls/CMakeLists.txt b/tests/auto/qmlls/CMakeLists.txt
index bcb8d121d3..daf7d64cd4 100644
--- a/tests/auto/qmlls/CMakeLists.txt
+++ b/tests/auto/qmlls/CMakeLists.txt
@@ -1,8 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-if (TARGET Qt::LanguageServerPrivate)
- add_subdirectory(qmlls)
+if (TARGET Qt::QmlLSPrivate)
add_subdirectory(lifecycle)
- add_subdirectory(completions)
+ add_subdirectory(utils)
+ add_subdirectory(qqmlcodemodel)
+ add_subdirectory(qmlls)
+ add_subdirectory(modules)
+ add_subdirectory(cli)
endif()
+
diff --git a/tests/auto/qmlls/cli/CMakeLists.txt b/tests/auto/qmlls/cli/CMakeLists.txt
new file mode 100644
index 0000000000..1ac8016f66
--- /dev/null
+++ b/tests/auto/qmlls/cli/CMakeLists.txt
@@ -0,0 +1,34 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls_modules LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+
+qt_internal_add_test(tst_qmlls_cli
+ SOURCES
+ tst_qmlls_cli.cpp
+ tst_qmlls_cli.h
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+if (TARGET qmlls)
+ # standalone test builds do not know the qmlls target
+ # but if TARGET qmlls is known it should be built before this test
+ add_dependencies(tst_qmlls_cli qmlls)
+endif()
diff --git a/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/A.qml b/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/A.qml
new file mode 100644
index 0000000000..5468cae5e1
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/A.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string helloSomeModule
+}
diff --git a/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/qmldir b/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/qmldir
new file mode 100644
index 0000000000..6e8de8bc53
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/ImportPath1/SomeModule/qmldir
@@ -0,0 +1,2 @@
+module SomeModule
+A 254.0 A.qml
diff --git a/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/B.qml b/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/B.qml
new file mode 100644
index 0000000000..33af59274a
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/B.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property string helloAnotherModule
+}
diff --git a/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/qmldir b/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/qmldir
new file mode 100644
index 0000000000..aa4ef803b1
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/ImportPath2/AnotherModule/qmldir
@@ -0,0 +1,2 @@
+module AnotherModule
+B 254.0 B.qml
diff --git a/tests/auto/qmlls/cli/data/sourceFolder/ImportFromBothPaths.qml b/tests/auto/qmlls/cli/data/sourceFolder/ImportFromBothPaths.qml
new file mode 100644
index 0000000000..4a2775ce29
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/sourceFolder/ImportFromBothPaths.qml
@@ -0,0 +1,10 @@
+import SomeModule
+import AnotherModule
+
+A {
+ helloSomeModule: "hello!"
+
+ B {
+ helloAnotherModule: "World!"
+ }
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/cli/data/sourceFolder/ImportFromImportPath1.qml b/tests/auto/qmlls/cli/data/sourceFolder/ImportFromImportPath1.qml
new file mode 100644
index 0000000000..3417701352
--- /dev/null
+++ b/tests/auto/qmlls/cli/data/sourceFolder/ImportFromImportPath1.qml
@@ -0,0 +1,5 @@
+import SomeModule
+
+A {
+ helloSomeModule: "hello!"
+}
diff --git a/tests/auto/qmlls/cli/tst_qmlls_cli.cpp b/tests/auto/qmlls/cli/tst_qmlls_cli.cpp
new file mode 100644
index 0000000000..344ed48e64
--- /dev/null
+++ b/tests/auto/qmlls/cli/tst_qmlls_cli.cpp
@@ -0,0 +1,309 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_cli.h"
+
+using namespace Qt::StringLiterals;
+
+void tst_qmlls_cli::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+
+ m_qmllsPath = QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlls");
+#ifdef Q_OS_WIN
+ m_qmllsPath += QLatin1String(".exe");
+#endif
+ // allow overriding of the executable, to be able to use a qmlEcho script (as described in
+ // qmllanguageservertool.cpp)
+ m_qmllsPath = qEnvironmentVariable("QMLLS", m_qmllsPath);
+ m_server.setProgram(m_qmllsPath);
+}
+
+void tst_qmlls_cli::cleanup()
+{
+ m_server.closeWriteChannel();
+ m_server.waitForFinished();
+ QTRY_COMPARE(m_server.state(), QProcess::NotRunning);
+ QCOMPARE(m_server.exitStatus(), QProcess::NormalExit);
+}
+
+// Helper structs to avoid confusions between expected and unexpected messages and between expected
+// and unexpected diagnostics.
+struct ExpectedMessages : public QStringList
+{
+ using QStringList::QStringList;
+};
+struct UnexpectedMessages : public QStringList
+{
+ using QStringList::QStringList;
+};
+struct ExpectedDiagnostics : public QStringList
+{
+ using QStringList::QStringList;
+};
+struct UnexpectedDiagnostics : public QStringList
+{
+ using QStringList::QStringList;
+};
+
+// Extra environment variables to be added to qmlls's environment.
+struct Environment : public QList<QPair<QString, QString>>
+{
+ using QList<QPair<QString, QString>>::QList;
+};
+
+void tst_qmlls_cli::warnings_data()
+{
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<Environment>("environment");
+ QTest::addColumn<QString>("filePath");
+ // messages are printed to stderr and not shown in editor:
+ QTest::addColumn<ExpectedMessages>("expectedMessages");
+ QTest::addColumn<UnexpectedMessages>("unexpectedMessages");
+ // diagnostics are passed via LSP to be shown in editor:
+ QTest::addColumn<ExpectedDiagnostics>("expectedDiagnostics");
+ QTest::addColumn<UnexpectedDiagnostics>("unexpectedDiagnostics");
+
+ const Environment defaultEnv;
+ const QString dir1 = testFile(u"ImportPath1"_s);
+ const QString dir2 = testFile(u"ImportPath2"_s);
+ const QString notDir = testFile(u"ImportPath1/SomeModule/qmldir"_s);
+ const QString wrongDir = testFile(u"ImportPathInexistent"_s);
+
+ const QString fileImportingDir1 = testFile(u"sourceFolder/ImportFromImportPath1.qml"_s);
+ const QString fileImportingBothDirs = testFile(u"sourceFolder/ImportFromBothPaths.qml"_s);
+
+ const QString importWarningDir1 = u"Warnings occurred while importing module \"SomeModule\""_s;
+ const QString importWarningDir2 = u"Warnings occurred while importing module \"AnotherModule\""_s;
+
+ const UnexpectedMessages noUnexpectedMessages;
+ const QString warnAboutQmllsIniFiles{
+ u"Using the build directories found in the .qmlls.ini file. Your build folder might not be found if no .qmlls.ini files are present in the root source folder."_s
+ };
+
+ QTest::addRow("2-build-dirs")
+ << QStringList{ u"--build-dir"_s, dir1, u"-b"_s, dir2 } << defaultEnv
+ << fileImportingDir1
+ << ExpectedMessages{ u"Using build directories passed by -b: \"%1\", \"%2\"."_s.arg(
+ dir1, dir2) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles } << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1 };
+
+ QTest::addRow("build-dir-not-dir")
+ << QStringList{ u"--build-dir"_s, notDir, u"-b"_s, dir2 } << defaultEnv
+ << fileImportingBothDirs
+ << ExpectedMessages{ u"Argument \"%1\" passed to -b is not a directory."_s.arg(notDir) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles }
+ << ExpectedDiagnostics{ importWarningDir1 }
+ << UnexpectedDiagnostics{ importWarningDir2 };
+
+ QTest::addRow("build-dir-not-existing")
+ << QStringList{ u"--build-dir"_s, wrongDir, u"-b"_s, dir2 } << defaultEnv
+ << fileImportingBothDirs
+ << ExpectedMessages{ u"Argument \"%1\" passed to -b does not exist."_s.arg(wrongDir) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles } << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{};
+
+ QTest::addRow("build-dir-from-environment")
+ << QStringList{}
+ << Environment{ { u"QMLLS_BUILD_DIRS"_s,
+ u"%1%2%3"_s.arg(dir1, QDir::listSeparator(), dir2) } }
+ << fileImportingBothDirs
+ << ExpectedMessages{ u"Using build directories passed from environment variable \"QMLLS_BUILD_DIRS\": \"%1\", \"%2\"."_s
+ .arg(dir1, dir2) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles } << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+
+ QTest::addRow("build-dir-from-environment-not-existing")
+ << QStringList{}
+ << Environment{ { u"QMLLS_BUILD_DIRS"_s,
+ QStringList{ dir1, wrongDir, notDir }.join(QDir::listSeparator()) } }
+ << fileImportingDir1
+ << ExpectedMessages{ u"Argument \"%1\" from environment variable \"QMLLS_BUILD_DIRS\" does not exist."_s
+ .arg(wrongDir),
+ u"Argument \"%1\" from environment variable \"QMLLS_BUILD_DIRS\" is not a directory."_s
+ .arg(notDir) }
+ << UnexpectedMessages{ warnAboutQmllsIniFiles } << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+
+ QTest::addRow("ignore-environment-with-option")
+ << QStringList{ u"--build-dir"_s, dir1 }
+ << Environment{ { u"QMLLS_BUILD_DIRS"_s, dir2 } } << fileImportingBothDirs
+ << ExpectedMessages{ u"Using build directories passed by -b: \"%1\"."_s.arg(dir1) }
+ << UnexpectedMessages{ dir2, warnAboutQmllsIniFiles }
+ << ExpectedDiagnostics{ importWarningDir2 }
+ << UnexpectedDiagnostics{ importWarningDir1 };
+
+ QTest::addRow("loadFromConfigFile")
+ << QStringList{} << Environment{} << fileImportingDir1
+ << ExpectedMessages{ warnAboutQmllsIniFiles } << UnexpectedMessages{}
+ << ExpectedDiagnostics{ importWarningDir1 } << UnexpectedDiagnostics{};
+
+ QTest::addRow("2-import-paths")
+ << QStringList{ u"-I"_s, dir1, u"-I"_s, dir2 } << Environment{} << fileImportingBothDirs
+ << ExpectedMessages{ u"Using import directories passed by -I: \"%1\", \"%2\"."_s.arg(
+ dir1, dir2) }
+ << UnexpectedMessages{} << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+
+ QTest::addRow("import-paths-ignore-env")
+ << QStringList{ u"-I"_s, dir1, } << Environment{ { u"QML_IMPORT_PATH"_s, dir2 } }
+ << fileImportingBothDirs
+ << ExpectedMessages{ u"Using import directories passed by -I: \"%1\"."_s.arg(dir1) }
+ << UnexpectedMessages{ u"Using import directories passed from environment variable \"QML_IMPORT_PATH\": \"%1\"."_s.arg(dir2)}
+ << ExpectedDiagnostics{importWarningDir2} << UnexpectedDiagnostics{ importWarningDir1 };
+
+ QTest::addRow("2-import-paths-mixed")
+ << QStringList{ u"-I"_s, dir1, u"-E"_s }
+ << Environment{ { u"QML_IMPORT_PATH"_s, dir2 } } << fileImportingBothDirs
+ << ExpectedMessages{ u"Using import directories passed by -I: \"%1\"."_s.arg(dir1),
+ u"Using import directories passed from environment variable \"QML_IMPORT_PATH\": \"%1\"."_s
+ .arg(dir2) }
+ << UnexpectedMessages{} << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+
+ QTest::addRow("2-import-paths-deprecated")
+ << QStringList{ u"-I"_s, dir1, u"-E"_s }
+ << Environment{ { u"QML2_IMPORT_PATH"_s, dir2 } } << fileImportingBothDirs
+ << ExpectedMessages{ u"Using import directories passed by -I: \"%1\"."_s.arg(dir1),
+ u"Using import directories passed from the deprecated environment variable \"QML2_IMPORT_PATH\": \"%1\"."_s
+ .arg(dir2) }
+ << UnexpectedMessages{} << ExpectedDiagnostics{}
+ << UnexpectedDiagnostics{ importWarningDir1, importWarningDir2 };
+}
+
+auto tst_qmlls_cli::startServerRAII()
+{
+ startServerImpl();
+ return qScopeGuard([this]() { this->stopServerImpl(); });
+}
+
+void tst_qmlls_cli::startServerImpl()
+{
+ m_protocol = std::make_unique<QLanguageServerProtocol>(
+ [this](const QByteArray &data) { m_server.write(data); });
+
+ connect(&m_server, &QProcess::readyReadStandardOutput, this, [this]() {
+ QByteArray data = m_server.readAllStandardOutput();
+ m_protocol->receiveData(data);
+ });
+
+ m_server.start();
+
+ QLspSpecification::InitializeParams clientInfo;
+ clientInfo.rootUri = QUrl::fromLocalFile(dataDirectory() + "/default").toString().toUtf8();
+
+ QLspSpecification::TextDocumentClientCapabilities tDoc;
+ tDoc.typeDefinition = QLspSpecification::TypeDefinitionClientCapabilities{ false, false };
+
+ QLspSpecification::PublishDiagnosticsClientCapabilities pDiag;
+ tDoc.publishDiagnostics = pDiag;
+ pDiag.versionSupport = true;
+ clientInfo.capabilities.textDocument = tDoc;
+ bool didInit = false;
+ m_protocol->requestInitialize(
+ clientInfo, [this, &didInit](const QLspSpecification::InitializeResult &serverInfo) {
+ Q_UNUSED(serverInfo);
+ m_protocol->notifyInitialized(QLspSpecification::InitializedParams());
+ didInit = true;
+ });
+ QTRY_COMPARE_WITH_TIMEOUT(didInit, true, 10000);
+}
+
+void tst_qmlls_cli::stopServerImpl()
+{
+ m_server.closeWriteChannel();
+ m_server.waitForFinished();
+ QTRY_COMPARE(m_server.state(), QProcess::NotRunning);
+ QCOMPARE(m_server.exitStatus(), QProcess::NormalExit);
+}
+
+void tst_qmlls_cli::warnings()
+{
+ QFETCH(QStringList, args);
+ QFETCH(Environment, environment);
+ QFETCH(ExpectedMessages, expectedMessages);
+ QFETCH(UnexpectedMessages, unexpectedMessages);
+ QFETCH(QString, filePath);
+ QFETCH(ExpectedDiagnostics, expectedDiagnostics);
+ QFETCH(UnexpectedDiagnostics, unexpectedDiagnostics);
+
+ QProcessEnvironment processEnvironment = QProcessEnvironment::systemEnvironment();
+ for (const auto &entry : environment)
+ processEnvironment.insert(entry.first, entry.second);
+ m_server.setProcessEnvironment(processEnvironment);
+ m_server.setArguments(args);
+
+ QList<int> countExpectedMessages(expectedMessages.size(), 0);
+ QList<int> countUnexpectedMessages(unexpectedMessages.size(), 0);
+ QList<int> countExpectedDiagnostics(expectedDiagnostics.size(), 0);
+ QList<int> countUnexpectedDiagnostics(unexpectedDiagnostics.size(), 0);
+
+ auto guard = qScopeGuard([this]() {
+ // note: the lambda used in the "connect"-call references local variables, so disconnect the
+ // lambda via QScopedGuard to avoid its captured references to dangle
+ disconnect(&m_server, &QProcess::readyReadStandardOutput, nullptr, nullptr);
+ });
+ connect(&m_server, &QProcess::readyReadStandardError, this,
+ [this, &expectedMessages, &countExpectedMessages, &unexpectedMessages,
+ &countUnexpectedMessages]() {
+ const auto data = QString::fromUtf8(m_server.readAllStandardError());
+ if (data.isEmpty())
+ return;
+
+ for (int i = 0; i < expectedMessages.size(); ++i) {
+ if (data.contains(expectedMessages[i]))
+ ++countExpectedMessages[i];
+ }
+ for (int i = 0; i < unexpectedMessages.size(); ++i) {
+ if (data.contains(unexpectedMessages[i]))
+ ++countUnexpectedMessages[i];
+ }
+ });
+
+ auto guard2 = startServerRAII();
+
+ // each expected message should appear exactly one time
+ QTRY_COMPARE_WITH_TIMEOUT(countExpectedMessages, QList<int>(expectedMessages.size(), 1), 500);
+ // each unexpected message should appear exactly zero times
+ QCOMPARE(countUnexpectedMessages, QList<int>(unexpectedMessages.size(), 0));
+
+ bool diagnosticOk = false;
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [&diagnosticOk, &expectedDiagnostics, &countExpectedDiagnostics, &unexpectedDiagnostics,
+ &countUnexpectedDiagnostics](const QByteArray &,
+ const QLspSpecification::PublishDiagnosticsParams &p) {
+ for (const auto &d : p.diagnostics) {
+ const QString message = QString::fromUtf8(d.message);
+ for (int i = 0; i < expectedDiagnostics.size(); ++i) {
+ if (message.contains(expectedDiagnostics[i]))
+ ++countExpectedDiagnostics[i];
+ }
+ for (int i = 0; i < unexpectedDiagnostics.size(); ++i) {
+ if (message.contains(unexpectedDiagnostics[i]))
+ ++countUnexpectedDiagnostics[i];
+ }
+ }
+ diagnosticOk = true;
+ });
+
+ QFile file(filePath);
+ QVERIFY(file.open(QIODevice::ReadOnly));
+
+ QLspSpecification::DidOpenTextDocumentParams oParams;
+ QLspSpecification::TextDocumentItem textDocument;
+ QByteArray uri = QUrl::fromLocalFile(filePath).toEncoded();
+ textDocument.uri = uri;
+ textDocument.text = file.readAll();
+ oParams.textDocument = textDocument;
+ m_protocol->notifyDidOpenTextDocument(oParams);
+
+ QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk, 3000);
+ // each expected diagnostic should appear exactly one time
+ QCOMPARE(countExpectedDiagnostics, QList<int>(expectedDiagnostics.size(), 1));
+ // each unexpected diagnostic should appear exactly zero times
+ QCOMPARE(countUnexpectedDiagnostics, QList<int>(unexpectedDiagnostics.size(), 0));
+
+}
+
+QTEST_MAIN(tst_qmlls_cli)
diff --git a/tests/auto/qmlls/cli/tst_qmlls_cli.h b/tests/auto/qmlls/cli/tst_qmlls_cli.h
new file mode 100644
index 0000000000..3eab8b5d1b
--- /dev/null
+++ b/tests/auto/qmlls/cli/tst_qmlls_cli.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_CLI_H
+#define TST_QMLLS_CLI_H
+
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlibraryinfo.h>
+
+#include <QtTest/qtest.h>
+
+class tst_qmlls_cli: public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qmlls_cli() : QQmlDataTest(QT_QMLTEST_DATADIR) { }
+ [[nodiscard]] auto startServerRAII();
+ void startServerImpl();
+ void stopServerImpl();
+
+private slots:
+ void initTestCase();
+ void cleanup();
+ void warnings_data();
+ void warnings();
+
+public:
+ QProcess m_server;
+ QString m_qmllsPath;
+ std::unique_ptr<QLanguageServerProtocol> m_protocol;
+};
+
+#endif // TST_QMLLS_CLI_H
diff --git a/tests/auto/qmlls/completions/CMakeLists.txt b/tests/auto/qmlls/completions/CMakeLists.txt
deleted file mode 100644
index 7b9a6111b2..0000000000
--- a/tests/auto/qmlls/completions/CMakeLists.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-file(GLOB_RECURSE test_data_glob
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
- data)
-list(APPEND test_data ${test_data_glob})
-
-qt_internal_add_test(tst_qmllscompletions
- SOURCES
- tst_qmllscompletions.cpp
- DEFINES
- QT_DEPRECATED_WARNINGS
- QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
- LIBRARIES
- Qt::Core
- Qt::QmlDomPrivate
- Qt::LanguageServerPrivate
- Qt::Test
- Qt::QuickTestUtilsPrivate
- TESTDATA ${test_data}
-)
-
-qt_internal_extend_target(tst_qmllscompletions CONDITION ANDROID OR IOS
- DEFINES
- QT_QMLTEST_DATADIR=":/domdata"
-)
diff --git a/tests/auto/qmlls/completions/data/buildDir/BuildDir/qmldir b/tests/auto/qmlls/completions/data/buildDir/BuildDir/qmldir
deleted file mode 100644
index 18d90059da..0000000000
--- a/tests/auto/qmlls/completions/data/buildDir/BuildDir/qmldir
+++ /dev/null
@@ -1,2 +0,0 @@
-module BuildDir
-BuildDirType 1.0 BuildDirType.qml
diff --git a/tests/auto/qmlls/completions/data/completions/Yyy.qml b/tests/auto/qmlls/completions/data/completions/Yyy.qml
deleted file mode 100644
index 23bbd057f7..0000000000
--- a/tests/auto/qmlls/completions/data/completions/Yyy.qml
+++ /dev/null
@@ -1,29 +0,0 @@
-import QtQuick 2.0
-import QtQuick as QQ
-
-Zzz {
- id: root
- width: height
- Rectangle {
- color: "green"
- anchors.fill: parent
- width: root.height
- height: root.foo.height
-
- }
-
- function lala() {}
- property Rectangle foo: Rectangle{ height: 200 }
- function longfunction(a, b, c = "c", d = "d"): string {
- return "hehe: " + c + d
- }
-
- // documentedFunction: is documented
- // returns 'Good'
- function documentedFunction(arg1, arg2 = "Qt"): string {
- return "Good"
- }
- QQ.Rectangle {
- color:"red"
- }
-}
diff --git a/tests/auto/qmlls/completions/data/completions/fromBuildDir.qml b/tests/auto/qmlls/completions/data/completions/fromBuildDir.qml
deleted file mode 100644
index 4a572a3950..0000000000
--- a/tests/auto/qmlls/completions/data/completions/fromBuildDir.qml
+++ /dev/null
@@ -1,9 +0,0 @@
-import BuildDir
-import QtQuick
-BuildDirType {
- Rectangle {
- color: "green"
- width: 250
- height: 10
- }
-}
diff --git a/tests/auto/qmlls/completions/tst_qmllscompletions.cpp b/tests/auto/qmlls/completions/tst_qmllscompletions.cpp
deleted file mode 100644
index e5d955a337..0000000000
--- a/tests/auto/qmlls/completions/tst_qmllscompletions.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
-#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
-#include <QtQuickTestUtils/private/qmlutils_p.h>
-#include <QtCore/private/qduplicatetracker_p.h>
-
-#include <QtCore/qobject.h>
-#include <QtCore/qprocess.h>
-#include <QtCore/qlibraryinfo.h>
-#include <QtCore/qstringlist.h>
-
-#include <QtTest/qtest.h>
-#include "../../../../tools/qmlls/lspcustomtypes.h"
-
-#include <iostream>
-#include <variant>
-
-// Check if QTest already has a QTEST_CHECKED macro
-#ifndef QTEST_CHECKED
-#define QTEST_CHECKED(...) \
-do { \
- __VA_ARGS__; \
- if (QTest::currentTestFailed()) \
- return; \
-} while (false)
-#endif
-
-QT_USE_NAMESPACE
-using namespace Qt::StringLiterals;
-using namespace QLspSpecification;
-
-class tst_QmllsCompletions : public QQmlDataTest
-{
- using ExpectedCompletion = QPair<QString, CompletionItemKind>;
- using ExpectedCompletions = QList<ExpectedCompletion>;
-
- using ExpectedDocumentation = std::tuple<QString, QString, QString>;
- using ExpectedDocumentations = QList<ExpectedDocumentation>;
-
- Q_OBJECT
-public:
- tst_QmllsCompletions();
- void checkCompletions(QByteArray uri, int lineNr, int character, ExpectedCompletions expected,
- QStringList notExpected);
-private slots:
- void initTestCase() final;
- void completions_data();
- void completions();
- void function_documentations_data();
- void function_documentations();
- void buildDir();
- void cleanupTestCase();
-
-private:
- QProcess m_server;
- QLanguageServerProtocol m_protocol;
- QString m_qmllsPath;
- QList<QByteArray> m_uriToClose;
-};
-
-tst_QmllsCompletions::tst_QmllsCompletions()
- : QQmlDataTest(QT_QMLTEST_DATADIR),
- m_protocol([this](const QByteArray &data) { m_server.write(data); })
-{
- connect(&m_server, &QProcess::readyReadStandardOutput, this, [this]() {
- QByteArray data = m_server.readAllStandardOutput();
- m_protocol.receiveData(data);
- });
-
- connect(&m_server, &QProcess::readyReadStandardError, this,
- [this]() {
- QProcess::ProcessChannel tmp = m_server.readChannel();
- m_server.setReadChannel(QProcess::StandardError);
- while (m_server.canReadLine())
- std::cerr << m_server.readLine().constData();
- m_server.setReadChannel(tmp);
- });
-
- m_qmllsPath =
- QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlls");
-#ifdef Q_OS_WIN
- m_qmllsPath += QLatin1String(".exe");
-#endif
- // allow overriding of the executable, to be able to use a qmlEcho script (as described in
- // qmllanguageservertool.cpp)
- m_qmllsPath = qEnvironmentVariable("QMLLS", m_qmllsPath);
- m_server.setProgram(m_qmllsPath);
- m_protocol.registerPublishDiagnosticsNotificationHandler([](const QByteArray &, auto) {
- // ignoring qmlint notifications
- });
-}
-
-void tst_QmllsCompletions::initTestCase()
-{
- QQmlDataTest::initTestCase();
- if (!QFileInfo::exists(m_qmllsPath)) {
- QString message =
- QStringLiteral("qmlls executable not found (looked for %0)").arg(m_qmllsPath);
- QSKIP(qPrintable(message)); // until we add a feature for this we avoid failing here
- }
- m_server.start();
- InitializeParams clientInfo;
- clientInfo.rootUri = QUrl::fromLocalFile(dataDirectory() + "/default").toString().toUtf8();
- TextDocumentClientCapabilities tDoc;
- PublishDiagnosticsClientCapabilities pDiag;
- tDoc.publishDiagnostics = pDiag;
- pDiag.versionSupport = true;
- clientInfo.capabilities.textDocument = tDoc;
- bool didInit = false;
- m_protocol.requestInitialize(clientInfo, [this, &didInit](const InitializeResult &serverInfo) {
- Q_UNUSED(serverInfo);
- m_protocol.notifyInitialized(InitializedParams());
- didInit = true;
- });
- QTRY_COMPARE_WITH_TIMEOUT(didInit, true, 10000);
-
- for (const QString &filePath :
- QStringList({ u"completions/Yyy.qml"_s, u"completions/fromBuildDir.qml"_s })) {
- QFile file(testFile(filePath));
- QVERIFY(file.open(QIODevice::ReadOnly));
- DidOpenTextDocumentParams oParams;
- TextDocumentItem textDocument;
- QByteArray uri = testFileUrl(filePath).toString().toUtf8();
- textDocument.uri = uri;
- textDocument.text = file.readAll();
- oParams.textDocument = textDocument;
- m_protocol.notifyDidOpenTextDocument(oParams);
- m_uriToClose.append(uri);
- }
-}
-
-void tst_QmllsCompletions::completions_data()
-{
- QTest::addColumn<QByteArray>("uri");
- QTest::addColumn<int>("lineNr");
- QTest::addColumn<int>("character");
- QTest::addColumn<ExpectedCompletions>("expected");
- QTest::addColumn<QStringList>("notExpected");
-
- QByteArray uri = testFileUrl("completions/Yyy.qml").toString().toUtf8();
-
- QTest::newRow("objEmptyLine") << uri << 8 << 0
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"property"_s, CompletionItemKind::Keyword },
- { u"width"_s, CompletionItemKind::Property },
- { u"function"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"QtQuick"_s, u"vector4d"_s });
-
- QTest::newRow("inBindingLabel") << uri << 5 << 9
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"property"_s, CompletionItemKind::Keyword },
- { u"width"_s, CompletionItemKind::Property },
- })
- << QStringList({ u"QtQuick"_s, u"vector4d"_s });
-
- QTest::newRow("afterBinding") << uri << 5 << 10
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Field },
- { u"width"_s, CompletionItemKind::Field },
- { u"vector4d"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"QtQuick"_s, u"property"_s });
-
- // suppress?
- QTest::newRow("afterId") << uri << 4 << 7
- << ExpectedCompletions({
- { u"import"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"QtQuick"_s, u"property"_s, u"Rectangle"_s,
- u"width"_s, u"vector4d"_s });
-
- QTest::newRow("fileStart") << uri << 0 << 0
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"import"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s });
-
- QTest::newRow("importImport") << uri << 0 << 3
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"import"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s });
-
- QTest::newRow("importModuleStart")
- << uri << 0 << 7
- << ExpectedCompletions({
- { u"QtQuick"_s, CompletionItemKind::Module },
- })
- << QStringList({ u"vector4d"_s, u"width"_s, u"Rectangle"_s, u"import"_s });
-
- QTest::newRow("importVersionStart")
- << uri << 0 << 15
- << ExpectedCompletions({
- { u"2"_s, CompletionItemKind::Constant },
- { u"as"_s, CompletionItemKind::Keyword },
- })
- << QStringList({ u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
-
- // QTest::newRow("importVersionMinor")
- // << uri << 0 << 17
- // << ExpectedCompletions({
- // { u"15"_s, CompletionItemKind::Constant },
- // })
- // << QStringList({ u"as"_s, u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
-
- QTest::newRow("inScript") << uri << 6 << 14
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Field },
- { u"vector4d"_s, CompletionItemKind::Field },
- { u"lala()"_s, CompletionItemKind::Function },
- { u"longfunction()"_s, CompletionItemKind::Function },
- { u"documentedFunction()"_s,
- CompletionItemKind::Function },
- { u"lala()"_s, CompletionItemKind { 0 } },
- { u"width"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"import"_s });
-
- QTest::newRow("expandBase1") << uri << 9 << 23
- << ExpectedCompletions({
- { u"width"_s, CompletionItemKind::Field },
- { u"foo"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"import"_s, u"Rectangle"_s });
-
- QTest::newRow("expandBase2") << uri << 10 << 29
- << ExpectedCompletions({
- { u"width"_s, CompletionItemKind::Field },
- { u"color"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"foo"_s, u"import"_s, u"Rectangle"_s });
-
- QTest::newRow("asCompletions")
- << uri << 25 << 8
- << ExpectedCompletions({
- { u"Rectangle"_s, CompletionItemKind::Field },
- })
- << QStringList({ u"foo"_s, u"import"_s, u"lala()"_s, u"width"_s });
-}
-
-void tst_QmllsCompletions::checkCompletions(QByteArray uri, int lineNr, int character,
- ExpectedCompletions expected, QStringList notExpected)
-{
- CompletionParams cParams;
- cParams.position.line = lineNr;
- cParams.position.character = character;
- cParams.textDocument.uri = uri;
- std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
- auto clean = [didFinish]() { *didFinish = true; };
-
- m_protocol.requestCompletion(
- cParams,
- [clean, uri, expected, notExpected](auto res) {
- QScopeGuard cleanup(clean);
- const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
-
- if (!cItems) {
- return;
- }
-
- QSet<QString> labels;
- QDuplicateTracker<QByteArray> modulesTracker;
- QDuplicateTracker<QByteArray> keywordsTracker;
- QDuplicateTracker<QByteArray> classesTracker;
- QDuplicateTracker<QByteArray> fieldsTracker;
- QDuplicateTracker<QByteArray> propertiesTracker;
-
- for (const CompletionItem &c : *cItems) {
- if (c.kind->toInt() == int(CompletionItemKind::Module)) {
- QVERIFY2(!modulesTracker.hasSeen(c.label), "Duplicate module: " + c.label);
- } else if (c.kind->toInt() == int(CompletionItemKind::Keyword)) {
- QVERIFY2(!keywordsTracker.hasSeen(c.label),
- "Duplicate keyword: " + c.label);
- } else if (c.kind->toInt() == int(CompletionItemKind::Class)) {
- QVERIFY2(!classesTracker.hasSeen(c.label), "Duplicate class: " + c.label);
- } else if (c.kind->toInt() == int(CompletionItemKind::Field)) {
- QVERIFY2(!fieldsTracker.hasSeen(c.label), "Duplicate field: " + c.label);
- } else if (c.kind->toInt() == int(CompletionItemKind::Property)) {
- QVERIFY2(!propertiesTracker.hasSeen(c.label),
- "Duplicate property: " + c.label);
- QVERIFY2(c.insertText == c.label + u": "_s,
- "a property should end with a colon with a space for "
- "'insertText', for better coding experience");
- }
- labels << c.label;
- }
-
- for (const ExpectedCompletion &exp : expected) {
- QVERIFY2(labels.contains(exp.first),
- u"no %1 in %2"_s
- .arg(exp.first,
- QStringList(labels.begin(), labels.end()).join(u", "_s))
- .toUtf8());
- if (labels.contains(exp.first)) {
- for (const CompletionItem &c : *cItems) {
- const auto kind = static_cast<CompletionItemKind>(c.kind->toInt());
-
- bool foundEntry = false;
- bool hasCorrectKind = false;
- for (const ExpectedCompletion &e : expected) {
- if (c.label == e.first) {
- foundEntry = true;
- hasCorrectKind |= kind == e.second;
- }
- }
-
- // Ignore QVERIFY for those completions not in the expected list.
- if (!foundEntry)
- continue;
-
- QVERIFY2(hasCorrectKind,
- qPrintable(
- QString::fromLatin1(
- "Completion item '%1' has wrong kind '%2'")
- .arg(c.label)
- .arg(QMetaEnum::fromType<CompletionItemKind>()
- .valueToKey(int(kind)))));
- }
- }
- }
- for (const QString &nexp : notExpected) {
- QVERIFY2(!labels.contains(nexp),
- u"found unexpected completion %1"_s.arg(nexp).toUtf8());
- }
- },
- [clean](const ResponseError &err) {
- QScopeGuard cleanup(clean);
- ProtocolBase::defaultResponseErrorHandler(err);
- QVERIFY2(false, "error computing the completion");
- });
- QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
-}
-
-void tst_QmllsCompletions::completions()
-{
- QFETCH(QByteArray, uri);
- QFETCH(int, lineNr);
- QFETCH(int, character);
- QFETCH(ExpectedCompletions, expected);
- QFETCH(QStringList, notExpected);
-
- QTEST_CHECKED(checkCompletions(uri, lineNr, character, expected, notExpected));
-}
-
-void tst_QmllsCompletions::function_documentations_data()
-{
- QTest::addColumn<QByteArray>("uri");
- QTest::addColumn<int>("lineNr");
- QTest::addColumn<int>("character");
- QTest::addColumn<ExpectedDocumentations>("expectedDocs");
-
- QByteArray uri = testFileUrl("completions/Yyy.qml").toString().toUtf8();
-
- QTest::newRow("longfunction")
- << uri << 5 << 14
- << ExpectedDocumentations{
- std::make_tuple(u"lala()"_s, u"returns void"_s, u"lala()"_s),
- std::make_tuple(u"longfunction()"_s, u"returns string"_s,
- uR"(longfunction(a, b, c = "c", d = "d"))"_s),
- std::make_tuple(u"documentedFunction()"_s, u"returns string"_s,
- uR"(// documentedFunction: is documented
-// returns 'Good'
-documentedFunction(arg1, arg2 = "Qt"))"_s),
- };
-}
-
-void tst_QmllsCompletions::function_documentations()
-{
- QFETCH(QByteArray, uri);
- QFETCH(int, lineNr);
- QFETCH(int, character);
- QFETCH(ExpectedDocumentations, expectedDocs);
-
- CompletionParams cParams;
- cParams.position.line = lineNr;
- cParams.position.character = character;
- cParams.textDocument.uri = uri;
- std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
- auto clean = [didFinish]() { *didFinish = true; };
-
- m_protocol.requestCompletion(
- cParams,
- [clean, uri, expectedDocs](auto res) {
- const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
-
- if (!cItems) {
- return;
- }
-
- for (const ExpectedDocumentation &exp : expectedDocs) {
- bool hasFoundExpected = false;
- const auto expectedLabel = std::get<0>(exp);
- for (const CompletionItem &c : *cItems) {
- if (c.kind->toInt() != int(CompletionItemKind::Function)) {
- // Only check functions.
- continue;
- }
-
- if (c.label == expectedLabel) {
- hasFoundExpected = true;
- }
- }
-
- QVERIFY2(hasFoundExpected,
- qPrintable(u"expected completion label '%1' wasn't found"_s.arg(
- expectedLabel)));
- }
-
- for (const CompletionItem &c : *cItems) {
- if (c.kind->toInt() != int(CompletionItemKind::Function)) {
- // Only check functions.
- continue;
- }
-
- QVERIFY(c.documentation != std::nullopt);
- // We currently don't support 'MarkupContent', change this when we do.
- QVERIFY(c.documentation->index() == 0);
- const QByteArray cDoc = std::get<0>(*c.documentation);
-
- for (const ExpectedDocumentation &exp : expectedDocs) {
- const auto &[label, details, docs] = exp;
-
- if (c.label != label)
- continue;
-
- QVERIFY2(c.detail == details,
- qPrintable(u"Completion item '%1' has wrong details '%2'"_s
- .arg(label).arg(*c.detail)));
- QVERIFY2(cDoc == docs,
- qPrintable(u"Completion item '%1' has wrong documentation '%2'"_s
- .arg(label).arg(cDoc)));
- }
- }
- clean();
- },
- [clean](const ResponseError &err) {
- ProtocolBase::defaultResponseErrorHandler(err);
- QVERIFY2(false, "error computing the completion");
- clean();
- });
- QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
-}
-
-void tst_QmllsCompletions::buildDir()
-{
- QString filePath = u"completions/fromBuildDir.qml"_s;
- QByteArray uri = testFileUrl(filePath).toString().toUtf8();
- QTEST_CHECKED(checkCompletions(uri, 3, 0,
- ExpectedCompletions({
- { u"property"_s, CompletionItemKind::Keyword },
- { u"function"_s, CompletionItemKind::Keyword },
- { u"Rectangle"_s, CompletionItemKind::Class },
- }),
- QStringList({ u"BuildDirType"_s, u"QtQuick"_s, u"width"_s, u"vector4d"_s })));
- Notifications::AddBuildDirsParams bDirs;
- UriToBuildDirs ub;
- ub.baseUri = uri;
- ub.buildDirs.append(testFile("buildDir").toUtf8());
- bDirs.buildDirsToSet.append(ub);
- m_protocol.typedRpc()->sendNotification(QByteArray(Notifications::AddBuildDirsMethod), bDirs);
- DidChangeTextDocumentParams didChange;
- didChange.textDocument.uri = uri;
- didChange.textDocument.version = 2;
- TextDocumentContentChangeEvent change;
- QFile file(testFile(filePath));
- QVERIFY(file.open(QIODevice::ReadOnly));
- change.text = file.readAll();
- didChange.contentChanges.append(change);
- m_protocol.notifyDidChangeTextDocument(didChange);
- QTEST_CHECKED(checkCompletions(uri, 3, 0,
- ExpectedCompletions({
- { u"BuildDirType"_s, CompletionItemKind::Class },
- { u"Rectangle"_s, CompletionItemKind::Class },
- { u"property"_s, CompletionItemKind::Keyword },
- { u"width"_s, CompletionItemKind::Property },
- { u"function"_s, CompletionItemKind::Keyword },
- }),
- QStringList({ u"QtQuick"_s, u"vector4d"_s })));
-}
-void tst_QmllsCompletions::cleanupTestCase()
-{
- for (const QByteArray &uri : m_uriToClose) {
- DidCloseTextDocumentParams closeP;
- closeP.textDocument.uri = uri;
- m_protocol.notifyDidCloseTextDocument(closeP);
- }
- m_server.closeWriteChannel();
- QTRY_COMPARE(m_server.state(), QProcess::NotRunning);
- QCOMPARE(m_server.exitStatus(), QProcess::NormalExit);
-}
-
-QTEST_MAIN(tst_QmllsCompletions)
-
-#include <tst_qmllscompletions.moc>
diff --git a/tests/auto/qmlls/lifecycle/CMakeLists.txt b/tests/auto/qmlls/lifecycle/CMakeLists.txt
index ec12884e98..fa97aca132 100644
--- a/tests/auto/qmlls/lifecycle/CMakeLists.txt
+++ b/tests/auto/qmlls/lifecycle/CMakeLists.txt
@@ -5,18 +5,22 @@
## tst_lifecycle Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_lifecycle LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_lifecycle
SOURCES
- ../../../../tools/qmlls/qlanguageserver.h ../../../../tools/qmlls/qlanguageserver.cpp
tst_lifecycle.cpp
qiopipe.h qiopipe.cpp
- INCLUDE_DIRECTORIES
- ../../../../tools/qmlls
DEFINES
QT_DEPRECATED_WARNINGS
LIBRARIES
Qt::CorePrivate
Qt::LanguageServerPrivate
Qt::Test
+ Qt::QmlLSPrivate
TESTDATA ${test_data}
)
diff --git a/tests/auto/qmlls/lifecycle/qiopipe.cpp b/tests/auto/qmlls/lifecycle/qiopipe.cpp
index 416fe8257d..44f34e6580 100644
--- a/tests/auto/qmlls/lifecycle/qiopipe.cpp
+++ b/tests/auto/qmlls/lifecycle/qiopipe.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qiopipe.h"
diff --git a/tests/auto/qmlls/lifecycle/qiopipe.h b/tests/auto/qmlls/lifecycle/qiopipe.h
index e6c6d8b9a4..1722e2e465 100644
--- a/tests/auto/qmlls/lifecycle/qiopipe.h
+++ b/tests/auto/qmlls/lifecycle/qiopipe.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QECHODEVICE_H
#define QECHODEVICE_H
diff --git a/tests/auto/qmlls/lifecycle/tst_lifecycle.cpp b/tests/auto/qmlls/lifecycle/tst_lifecycle.cpp
index 6c960f414e..efbdf10c37 100644
--- a/tests/auto/qmlls/lifecycle/tst_lifecycle.cpp
+++ b/tests/auto/qmlls/lifecycle/tst_lifecycle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qiopipe.h"
@@ -8,7 +8,7 @@
#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
-#include "qlanguageserver.h"
+#include <QtQmlLS/private/qlanguageserver_p.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonobject.h>
diff --git a/tests/auto/qmlls/modules/CMakeLists.txt b/tests/auto/qmlls/modules/CMakeLists.txt
new file mode 100644
index 0000000000..565c8fb62a
--- /dev/null
+++ b/tests/auto/qmlls/modules/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls_modules LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+file(GLOB_RECURSE test_data_glob_qmlformat
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/../../qml/qmlformat/
+ data)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qmlls_modules
+ SOURCES
+ tst_qmlls_modules.cpp
+ tst_qmlls_modules.h
+ DEFINES
+ QT_DEPRECATED_WARNINGS
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ QT_QMLFORMATTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../../qml/qmlformat/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qmlls_modules CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/domdata"
+)
+
+if (TARGET qmlls)
+ # standalone test builds do not know the qmlls target
+ # but if TARGET qmlls is known it should be built before this test
+ add_dependencies(tst_qmlls_modules qmlls)
+endif()
diff --git a/tests/auto/qmlls/completions/data/buildDir/BuildDir/BuildDirType.qml b/tests/auto/qmlls/modules/data/buildDir/BuildDir/BuildDirType.qml
index 416ec33253..416ec33253 100644
--- a/tests/auto/qmlls/completions/data/buildDir/BuildDir/BuildDirType.qml
+++ b/tests/auto/qmlls/modules/data/buildDir/BuildDir/BuildDirType.qml
diff --git a/tests/auto/qmlls/modules/data/buildDir/BuildDir/qmldir b/tests/auto/qmlls/modules/data/buildDir/BuildDir/qmldir
new file mode 100644
index 0000000000..df2320f1f0
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/buildDir/BuildDir/qmldir
@@ -0,0 +1,3 @@
+module BuildDir
+BuildDirType 1.0 BuildDirType.qml
+import QtQuick.Controls.Basic
diff --git a/tests/auto/qmlls/modules/data/completions/SomeBase.qml b/tests/auto/qmlls/modules/data/completions/SomeBase.qml
new file mode 100644
index 0000000000..9c36e13c5b
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/completions/SomeBase.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/modules/data/completions/Yyy.qml b/tests/auto/qmlls/modules/data/completions/Yyy.qml
new file mode 100644
index 0000000000..9ac6f9968d
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/completions/Yyy.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.0
+import QtQuick as QQ
+
+Zzz {
+ id: root
+ width: height
+ Rectangle {
+ color: "green"
+ anchors.fill: parent
+ width: root.height
+ height: root.foo.height
+
+ }
+
+ function lala() {}
+ property Rectangle foo: Rectangle{ height: 200 }
+ function longfunction(a, b, c = "c", d = "d"): string {
+ return "hehe: " + c + d
+ }
+
+ // documentedFunction: is documented
+ // returns 'Good'
+ function documentedFunction(arg1, arg2 = "Qt"): string {
+ return "Good"
+ }
+ QQ.Rectangle {
+ color:"red"
+ }
+
+ component IC: Zzz { property SomeBase data }
+ property SomeBase mySomeBase
+}
diff --git a/tests/auto/qmlls/completions/data/completions/Zzz.qml b/tests/auto/qmlls/modules/data/completions/Zzz.qml
index 165ea46394..165ea46394 100644
--- a/tests/auto/qmlls/completions/data/completions/Zzz.qml
+++ b/tests/auto/qmlls/modules/data/completions/Zzz.qml
diff --git a/tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml b/tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml
new file mode 100644
index 0000000000..2a8694342b
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+QtObject {
+ id: root
+ property int good
+ property var i: Item {
+ property int bad
+ property int myP: root.
+ Item { }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml b/tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml
new file mode 100644
index 0000000000..4cf47b6f31
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+QtObject {
+ id: root
+ property int good
+ property var i: Item {
+ property int bad
+ property int myP2: root.
+ bad: 43
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/completions/fromBuildDir.qml b/tests/auto/qmlls/modules/data/completions/fromBuildDir.qml
new file mode 100644
index 0000000000..2c35f5b864
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/completions/fromBuildDir.qml
@@ -0,0 +1,10 @@
+import BuildDir
+import QtQuick
+BuildDirType {
+ Rectangle {
+ color: "green"
+ width: 250
+ height: 10
+ }
+ Button {}
+}
diff --git a/tests/auto/qmlls/modules/data/findDefinition/jsDefinitions.qml b/tests/auto/qmlls/modules/data/findDefinition/jsDefinitions.qml
new file mode 100644
index 0000000000..3243030466
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/findDefinition/jsDefinitions.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+ id: rootId
+
+ property int i // (1)
+ function f(a /*(2)*/ , b) {return a /* go to definition on a leads to (2) */ > b} // (4)
+
+ Component.onCompleted: {
+ let x = 42 // (3)
+ f(x, i) // goto definition on f goes to 4, on x goes to (3) and on i goes to (1)
+ f(x, rootId.i) // goto definition on f goes to 4, on x goes to (3) and on i goes to (1)
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/findUsages/jsIdentifierUsages.qml b/tests/auto/qmlls/modules/data/findUsages/jsIdentifierUsages.qml
new file mode 100644
index 0000000000..6c099edc9e
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/findUsages/jsIdentifierUsages.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/blanklines.formatted.qml b/tests/auto/qmlls/modules/data/formatting/blanklines.formatted.qml
new file mode 100644
index 0000000000..19548e9023
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/blanklines.formatted.qml
@@ -0,0 +1,4 @@
+// leading and trailing spaces
+import QtQuick
+
+Item {}
diff --git a/tests/auto/qmlls/modules/data/formatting/blanklines.qml b/tests/auto/qmlls/modules/data/formatting/blanklines.qml
new file mode 100644
index 0000000000..ae1a14daed
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/blanklines.qml
@@ -0,0 +1,15 @@
+
+
+
+
+// leading and trailing spaces
+import QtQuick
+
+
+Item {
+
+
+ }
+
+
+
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted1.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted1.qml
new file mode 100644
index 0000000000..e167f829b2
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted1.qml
@@ -0,0 +1,23 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+ // Testing
+ "foo": "bar"
+ }]
+
+ Item {
+
+ function test() {
+var patron = "ðŸˆtestðŸˆ";
+ let kivrik = 1;
+
+
+
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted2.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted2.qml
new file mode 100644
index 0000000000..ebd68dbde2
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted2.qml
@@ -0,0 +1,20 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+// Testing
+ "foo": "bar"
+}]
+
+ Item {
+
+ function test() {
+ var patron = "ðŸˆtestðŸˆ";
+ let kivrik = 1;
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted3.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted3.qml
new file mode 100644
index 0000000000..83f91c6af5
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted3.qml
@@ -0,0 +1,23 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+// Testing
+ "foo": "bar"
+}]
+
+ Item {
+
+ function test() {
+var patron = "ðŸˆtestðŸˆ";
+ let kivrik = 1;
+
+
+
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted4.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted4.qml
new file mode 100644
index 0000000000..d6d7227588
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted4.qml
@@ -0,0 +1,20 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+ // Testing
+ "foo": "bar"
+ }]
+
+ Item {
+
+ function test() {
+ var patron = "ðŸˆtestðŸˆ";
+ let kivrik = 1;
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted5.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted5.qml
new file mode 100644
index 0000000000..8b3ddf8df1
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.formatted5.qml
@@ -0,0 +1,20 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+// Testing
+ "foo": "bar"
+}]
+
+ Item {
+
+ function test() {
+ var patron = "ðŸˆtestðŸˆ";
+ let kivrik = 1;
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/formatting/rangeFormatting.qml b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.qml
new file mode 100644
index 0000000000..1431fb10c3
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/formatting/rangeFormatting.qml
@@ -0,0 +1,23 @@
+pragma Strict
+
+import QtQuick
+
+Item {
+ property var test: [{
+// Testing
+ "foo": "bar"
+}]
+
+ Item {
+
+ function test() {
+var patron = "ðŸˆtestðŸˆ";
+ let kivrik = 1;
+
+
+
+
+ const yumyum = 1;
+ }
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/highlighting/basic.qml b/tests/auto/qmlls/modules/data/highlighting/basic.qml
new file mode 100644
index 0000000000..264f553b22
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/highlighting/basic.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ function a() {
+
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/highlighting/bigFile.qml b/tests/auto/qmlls/modules/data/highlighting/bigFile.qml
new file mode 100644
index 0000000000..9832e8e98a
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/highlighting/bigFile.qml
@@ -0,0 +1,351 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ function f() {
+ let sum = 0, sum2 = 0;
+ for (let i = 1; i < 42; i = i + 2) {
+ sum = sum + i;
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ }
+
+ readonly property int helloProperty: 0
+ property int p2: 1
+
+ function withProperty() {
+ let sum = 0, sum2 = 0;
+ for (const i = 1; i < 42; i = i + 2) {
+ sum = sum + i;
+ helloProperty = helloProperty + sum - i * p2;
+ {
+ let helloProperty = "evil";
+ }
+ }
+ }
+ Item {
+ function f() {
+ return helloProperty + p2;
+ }
+ property string helloProperty
+ }
+ component IC: Item {
+ property var helloProperty
+ function f() {
+ return helloProperty + p2;
+ }
+ }
+ component NestedComponent: Item {
+ property NestedComponent2 inner: NestedComponent2 {}
+ property int p2
+ }
+ component NestedComponent2: Item {
+ property NestedComponent3 inner
+ property int p2
+ inner: NestedComponent3 {}
+ }
+ component NestedComponent3: Item {
+ property NestedComponent4 inner
+ property int p2
+ inner: NestedComponent4 {}
+ }
+ component NestedComponent4: Item {
+ property int helloProperty
+ property int p2
+ }
+ NestedComponent {
+ id: myNested
+ }
+ function nestedUsages() {
+ let x = myNested.inner.inner.inner.helloProperty + helloProperty;
+ let a = myNested.p2 + p2;
+ let b = myNested.inner.p2 + p2;
+ let c = myNested.inner.inner.p2 + p2;
+ let d = myNested.inner.inner.inner.p2 + p2;
+ }
+
+ function recursive(n: int): int {
+ if (n > 3)
+ return 1 + recursive(recursive(x - 1) + recursive(x - 2) - recursive(x - 3));
+ else
+ return recursive(0);
+ }
+
+ property int helloRecursive: recursive(42)
+ Rectangle {
+ function f() {
+ return rootId.recursive(123);
+ }
+ }
+
+ signal helloSignal
+
+ function callSignals() {
+ helloSignal();
+ if (false) {
+ helloSignal();
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ helloSignal();
+ }
+ }
+ function callSignals2() {
+ helloSignal();
+ if (false) {
+ widthChanged();
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ widthChanged();
+ rootId.widthChanged();
+ }
+ }
+ Item {
+ function callSignalsInChild() {
+ widthChanged();
+ rootId.widthChanged();
+ }
+ }
+
+ function myHelloHandler() {
+ let x = 32;
+ }
+ onHelloSignal: myHelloHandler
+
+ property int helloPropertyBinding
+ helloPropertyBinding: 123
+
+ property int checkHandlers
+ onCheckHandlersChanged: myHelloHandler
+ onChildrenChanged: myHelloHandler
+ function callChanged() {
+ checkHandlersChanged();
+ childrenChanged();
+ }
+ property int _: 48
+ property int ______42: 48
+ property int _123a: 48
+ on_Changed: myHelloHandler
+ on______42Changed: myHelloHandler
+ on_123AChanged: myHelloHandler
+ function weirdPropertynames() {
+ _Changed();
+ ______42Changed();
+ _123aChanged();
+ }
+
+ TapHandler {
+ onTapped: myHelloHandler
+ function f() {
+ tapped();
+ }
+ }
+
+ function anotherF() {
+ helloPropertyChanged();
+ }
+ onHelloPropertyChanged: myHelloHandler
+ // Type {}
+ function foo(mouse) {
+ }
+
+ MouseArea {
+ id: area1
+ onClicked: foo
+ property int insideMouseArea1
+ }
+
+ MouseArea {
+ id: area2
+ Connections {
+ function onClicked(mouse) {
+ area1.clicked();
+ area3.clicked();
+ }
+ }
+ property int insideMouseArea2
+
+ MouseArea {
+ id: area3
+ }
+ }
+
+ property Connections c: Connections {
+ target: area3
+ onClicked: function (mouse) {}
+ }
+ function useMouseAreas() {
+ area1.clicked();
+ area2.clicked();
+ area3.clicked();
+ }
+
+ function checkParameters(a: int, b: double, {
+ x,
+ y = {},
+ z = [x, y]
+ }) {
+ return a + b + c + x + y + z;
+ }
+
+ function deconstructingUsages(xxx) {
+ let {
+ a,
+ b
+ } = xxx;
+ let c = a + b;
+ }
+
+ function k() {
+ }
+
+ function mafik() {
+ var patron = 34;
+ const upperLimit = 42;
+ do {
+ ++patron;
+ if (patron < 2)
+ continue;
+ else
+ ++patron;
+ } while (patron < upperLimit)
+ switch (patron) {
+ case 1:
+ return 23;
+ default:
+ break;
+ }
+ try {
+ {}
+ } catch (error) {
+ {}
+ } finally {}
+ for (const a in [1, 2, 3]) {
+ throw 2;
+ }
+ }
+
+ enum Test {
+ LOG
+ }
+
+ readonly property int t: 34
+ signal tt
+ required property int k
+
+ signal kkk(string a)
+ signal yyy(string a)
+
+ function ttt() {
+ }
+
+ function createComplexExpression(...objects) {
+ // Create an object that holds some data
+ let data = {
+ a: 5,
+ b: 10,
+ c: 3
+ };
+
+ // Create a complex expression using the data object
+ let expression = ((data.a + data.b * data.c) / (data.a - data.b)) ** data.c;
+
+ return expression;
+ }
+
+ function set1() {
+ const array = [1, 2, 3, 4];
+ const [a, b] = [1, 2];
+ const [aa, , bb] = array;
+ const [aaa = 23, bbb] = array;
+ const [a1, b1, ...rest1] = array;
+ const [a2, , b2, ...rest2] = array;
+ const [a3, b3, ...{
+ pop,
+ push
+ }] = array;
+ const [a4, b4, ...[c, d]] = array;
+
+ const obj = {
+ _a: 1,
+ _b: 2
+ };
+ const {
+ a5,
+ b5
+ } = obj;
+ const {
+ a6: a_,
+ b6: b1_
+ } = obj;
+ const {
+ a7: a11 = 4,
+ b11 = 34,
+ c1: b111,
+ d1
+ } = obj;
+ let key = a;
+ const {
+ [key]: a___
+ } = obj;
+ }
+
+ function set2(s: int): int {
+ // declare first
+ let a, b, a1, b1, c, d, rest, pop, push;
+ const array = [1, 2, 3, 4];
+ [a, b] = array;
+ [a, , b] = array;
+ [a = aDefault, b] = array;
+ [a, b, ...rest] = array;
+ [a, , b, ...rest] = array;
+ [a, b, ...{
+ pop,
+ push
+ }] = array;
+ [a, b, ...[c, d]] = array;
+
+ const obj = {
+ _a: 1,
+ _b: 2
+ };
+ ({
+ a,
+ b
+ } = obj); // brackets are required
+ ({
+ a: a1,
+ b: b1
+ } = obj);
+
+ const complicatedObject = {
+ a: 1,
+ b: {
+ c: 2,
+ d: {
+ e: 3,
+ f: [4, 5, 6]
+ }
+ },
+ g: [7, 8, 9]
+ };
+
+ const {
+ patron,
+ b: {
+ mafik,
+ d: {
+ e,
+ f: [, secondF, ...restF]
+ }
+ },
+ g: [firstG, ...restG]
+ } = complicatedObject;
+ }
+}
diff --git a/tests/auto/qmlls/modules/data/hover/test.qml b/tests/auto/qmlls/modules/data/hover/test.qml
new file mode 100644
index 0000000000..dd1d5abcb0
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/hover/test.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+QtObject {
+ readonly property int t: ""
+}
diff --git a/tests/auto/qmlls/modules/data/linting/SimpleItem.qml b/tests/auto/qmlls/modules/data/linting/SimpleItem.qml
new file mode 100644
index 0000000000..b7b7fc5021
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/linting/SimpleItem.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ id: helloItem
+
+}
diff --git a/tests/auto/qmlls/modules/data/quickfixes/INeedAQuickFix.qml b/tests/auto/qmlls/modules/data/quickfixes/INeedAQuickFix.qml
new file mode 100644
index 0000000000..ccd2a55383
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/quickfixes/INeedAQuickFix.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Item {
+ id: hello
+
+ signal s(xxx: string)
+ property int i: 42
+
+ // fix me! add '(xxx) =>' in front of console.log()
+ onS: console.log(xxx)
+
+ Item {
+ // fix me! prepend 'hello.' to 'i'!
+ function f() {
+ return i
+ }
+ }
+
+}
diff --git a/tests/auto/qmlls/modules/data/sourceDir/A.qml b/tests/auto/qmlls/modules/data/sourceDir/A.qml
new file mode 100644
index 0000000000..5560aee727
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/sourceDir/A.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/modules/data/sourceDir/Main.qml b/tests/auto/qmlls/modules/data/sourceDir/Main.qml
new file mode 100644
index 0000000000..e9fc9d4298
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/sourceDir/Main.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ B {}
+ Button {}
+}
diff --git a/tests/auto/qmlls/modules/data/sourceDir/qmldir b/tests/auto/qmlls/modules/data/sourceDir/qmldir
new file mode 100644
index 0000000000..2dd55288e3
--- /dev/null
+++ b/tests/auto/qmlls/modules/data/sourceDir/qmldir
@@ -0,0 +1,3 @@
+module SourceDir
+B 1.0 A.qml
+import QtQuick.Controls.Basic
diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
new file mode 100644
index 0000000000..d0851f15ff
--- /dev/null
+++ b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp
@@ -0,0 +1,1696 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_modules.h"
+#include "QtQmlLS/private/qqmllsutils_p.h"
+#include "QtQmlLS/private/qqmlsemantictokens_p.h"
+#include <algorithm>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <variant>
+
+// Check if QTest already has a QTEST_CHECKED macro
+#ifndef QTEST_CHECKED
+#define QTEST_CHECKED(...) \
+do { \
+ __VA_ARGS__; \
+ if (QTest::currentTestFailed()) \
+ return; \
+} while (false)
+#endif
+
+QT_USE_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace QLspSpecification;
+
+static constexpr bool enable_debug_output = false;
+
+tst_qmlls_modules::tst_qmlls_modules() : QQmlDataTest(QT_QMLTEST_DATADIR)
+{
+ m_qmllsPath =
+ QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlls");
+#ifdef Q_OS_WIN
+ m_qmllsPath += QLatin1String(".exe");
+#endif
+ // allow overriding of the executable, to be able to use a qmlEcho script (as described in
+ // qmllanguageservertool.cpp)
+ m_qmllsPath = qEnvironmentVariable("QMLLS", m_qmllsPath);
+ // qputenv("QT_LOGGING_RULES",
+ // "qt.languageserver.codemodel.debug=true;qt.languageserver.codemodel.warning=true"); // helps
+ qputenv("QT_LOGGING_RULES", "*.debug=true;*.warning=true");
+ // when using EditingRecorder
+ m_server.setProgram(m_qmllsPath);
+ // m_server.setArguments(QStringList() << u"-v"_s << u"-w"_s << u"7"_s);
+}
+
+void tst_qmlls_modules::init()
+{
+ QQmlDataTest::init();
+
+ m_protocol = std::make_unique<QLanguageServerProtocol>(
+ [this](const QByteArray &data) { m_server.write(data); });
+
+ connect(&m_server, &QProcess::readyReadStandardOutput, this, [this]() {
+ QByteArray data = m_server.readAllStandardOutput();
+ m_protocol->receiveData(data);
+ });
+
+ if constexpr (enable_debug_output) {
+ connect(&m_server, &QProcess::readyReadStandardError, this, [this]() {
+ QProcess::ProcessChannel tmp = m_server.readChannel();
+ m_server.setReadChannel(QProcess::StandardError);
+ while (m_server.canReadLine())
+ qDebug() << m_server.readLine();
+ m_server.setReadChannel(tmp);
+ });
+ }
+
+ m_server.start();
+
+ InitializeParams clientInfo;
+ clientInfo.rootUri = QUrl::fromLocalFile(dataDirectory() + "/default").toString().toUtf8();
+
+ TextDocumentClientCapabilities tDoc;
+ tDoc.typeDefinition = TypeDefinitionClientCapabilities{ false, false };
+
+ PublishDiagnosticsClientCapabilities pDiag;
+ tDoc.publishDiagnostics = pDiag;
+ pDiag.versionSupport = true;
+ clientInfo.capabilities.textDocument = tDoc;
+ bool didInit = false;
+ m_protocol->requestInitialize(clientInfo, [this, &didInit](const InitializeResult &serverInfo) {
+ Q_UNUSED(serverInfo);
+ m_protocol->notifyInitialized(InitializedParams());
+ didInit = true;
+ });
+ QTRY_COMPARE_WITH_TIMEOUT(didInit, true, 10000);
+}
+
+void tst_qmlls_modules::cleanup()
+{
+ for (const QByteArray &uri : m_uriToClose) {
+ DidCloseTextDocumentParams closeP;
+ closeP.textDocument.uri = uri;
+ m_protocol->notifyDidCloseTextDocument(closeP);
+ }
+ m_uriToClose.clear();
+
+ disconnect(&m_server, nullptr, this, nullptr);
+ m_server.closeWriteChannel();
+ m_server.waitForFinished();
+ QTRY_COMPARE(m_server.state(), QProcess::NotRunning);
+ QCOMPARE(m_server.exitStatus(), QProcess::NormalExit);
+}
+
+/*!
+\internal
+Opens a file from a relative filePath, and returns the loaded uri.
+Returns an empty option when the file could not be found.
+*/
+std::optional<QByteArray> tst_qmlls_modules::openFile(const QString &filePath)
+{
+ return openFileFromAbsolutePath(testFile(filePath));
+}
+
+/*!
+\internal
+Opens a file from an absolute filePath, and returns the loaded uri.
+Returns an empty option when the file could not be found.
+*/
+std::optional<QByteArray> tst_qmlls_modules::openFileFromAbsolutePath(const QString &filePath)
+{
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly))
+ return {};
+ DidOpenTextDocumentParams oParams;
+ TextDocumentItem textDocument;
+ QByteArray uri = QUrl::fromLocalFile(filePath).toEncoded();
+ textDocument.uri = uri;
+ textDocument.text = file.readAll();
+ oParams.textDocument = textDocument;
+ m_protocol->notifyDidOpenTextDocument(oParams);
+ m_uriToClose.append(uri);
+ return uri;
+}
+
+/*!
+\internal
+Ignore qmllint warnings, when not needed for the test.
+*/
+void tst_qmlls_modules::ignoreDiagnostics()
+{
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [](const QByteArray &, const PublishDiagnosticsParams &) {});
+}
+
+void tst_qmlls_modules::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ if (!QFileInfo::exists(m_qmllsPath)) {
+ QString message =
+ QStringLiteral("qmlls executable not found (looked for %0)").arg(m_qmllsPath);
+ QSKIP(qPrintable(message)); // until we add a feature for this we avoid failing here
+ }
+}
+
+void tst_qmlls_modules::checkCompletions(const QByteArray &uri, int lineNr, int character,
+ ExpectedCompletions expected, QStringList notExpected)
+{
+ CompletionParams cParams;
+ cParams.position.line = lineNr;
+ cParams.position.character = character;
+ cParams.textDocument.uri = uri;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+
+ m_protocol->requestCompletion(
+ cParams,
+ [clean, uri, expected, notExpected](auto res) {
+ QScopeGuard cleanup(clean);
+ const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
+
+ if (!cItems) {
+ return;
+ }
+
+ QSet<QString> labels;
+ QDuplicateTracker<QByteArray> modulesTracker;
+ QDuplicateTracker<QByteArray> keywordsTracker;
+ QDuplicateTracker<QByteArray> classesTracker;
+ QDuplicateTracker<QByteArray> fieldsTracker;
+ QDuplicateTracker<QByteArray> propertiesTracker;
+
+ for (const CompletionItem &c : *cItems) {
+ if (c.kind->toInt() == int(CompletionItemKind::Module)) {
+ QVERIFY2(!modulesTracker.hasSeen(c.label), "Duplicate module: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Keyword)) {
+ QVERIFY2(!keywordsTracker.hasSeen(c.label),
+ "Duplicate keyword: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Class)) {
+ QVERIFY2(!classesTracker.hasSeen(c.label), "Duplicate class: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Field)) {
+ QVERIFY2(!fieldsTracker.hasSeen(c.label), "Duplicate field: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Property)) {
+ QVERIFY2(!propertiesTracker.hasSeen(c.label),
+ "Duplicate property: " + c.label);
+ }
+ labels << c.label;
+ }
+
+ for (const ExpectedCompletion &exp : expected) {
+ QVERIFY2(labels.contains(exp.first),
+ u"no %1 in %2"_s
+ .arg(exp.first,
+ QStringList(labels.begin(), labels.end()).join(u", "_s))
+ .toUtf8());
+ if (labels.contains(exp.first)) {
+ for (const CompletionItem &c : *cItems) {
+ const auto kind = static_cast<CompletionItemKind>(c.kind->toInt());
+
+ bool foundEntry = false;
+ bool hasCorrectKind = false;
+ for (const ExpectedCompletion &e : expected) {
+ if (c.label == e.first) {
+ foundEntry = true;
+ hasCorrectKind |= kind == e.second;
+ }
+ }
+
+ // Ignore QVERIFY for those completions not in the expected list.
+ if (!foundEntry)
+ continue;
+
+ QVERIFY2(hasCorrectKind,
+ qPrintable(
+ QString::fromLatin1(
+ "Completion item '%1' has wrong kind '%2'")
+ .arg(c.label)
+ .arg(QMetaEnum::fromType<CompletionItemKind>()
+ .valueToKey(int(kind)))));
+ }
+ }
+ }
+ for (const QString &nexp : notExpected) {
+ QVERIFY2(!labels.contains(nexp),
+ u"found unexpected completion %1"_s.arg(nexp).toUtf8());
+ }
+ },
+ [clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
+}
+
+void tst_qmlls_modules::function_documentations_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("lineNr");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<ExpectedDocumentations>("expectedDocs");
+
+ const QString filePath = u"completions/Yyy.qml"_s;
+
+ QTest::newRow("longfunction")
+ << filePath << 5 << 14
+ << ExpectedDocumentations{
+ std::make_tuple(u"lala"_s, u"returns void"_s, u"lala()"_s),
+ std::make_tuple(u"longfunction"_s, u"returns string"_s,
+ uR"(longfunction(a, b, c = "c", d = "d"))"_s),
+ std::make_tuple(u"documentedFunction"_s, u"returns string"_s,
+ uR"(// documentedFunction: is documented
+// returns 'Good'
+documentedFunction(arg1, arg2 = "Qt"))"_s),
+ };
+}
+
+void tst_qmlls_modules::function_documentations()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, lineNr);
+ QFETCH(int, character);
+ QFETCH(ExpectedDocumentations, expectedDocs);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ CompletionParams cParams;
+ cParams.position.line = lineNr;
+ cParams.position.character = character;
+ cParams.textDocument.uri = *uri;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+
+ m_protocol->requestCompletion(
+ cParams,
+ [clean, uri, expectedDocs](auto res) {
+ const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
+
+ if (!cItems) {
+ return;
+ }
+
+ for (const ExpectedDocumentation &exp : expectedDocs) {
+ bool hasFoundExpected = false;
+ const auto expectedLabel = std::get<0>(exp);
+ for (const CompletionItem &c : *cItems) {
+ if (c.kind->toInt() != int(CompletionItemKind::Method)) {
+ // Only check functions.
+ continue;
+ }
+
+ if (c.label == expectedLabel) {
+ hasFoundExpected = true;
+ }
+ }
+
+ QVERIFY2(hasFoundExpected,
+ qPrintable(u"expected completion label '%1' wasn't found"_s.arg(
+ expectedLabel)));
+ }
+
+ for (const CompletionItem &c : *cItems) {
+ if (c.kind->toInt() != int(CompletionItemKind::Function)) {
+ // Only check functions.
+ continue;
+ }
+
+ QVERIFY(c.documentation != std::nullopt);
+ // We currently don't support 'MarkupContent', change this when we do.
+ QVERIFY(c.documentation->index() == 0);
+ const QByteArray cDoc = std::get<0>(*c.documentation);
+
+ for (const ExpectedDocumentation &exp : expectedDocs) {
+ const auto &[label, details, docs] = exp;
+
+ if (c.label != label)
+ continue;
+
+ QVERIFY2(c.detail == details,
+ qPrintable(u"Completion item '%1' has wrong details '%2'"_s
+ .arg(label).arg(*c.detail)));
+ QVERIFY2(cDoc == docs,
+ qPrintable(u"Completion item '%1' has wrong documentation '%2'"_s
+ .arg(label).arg(cDoc)));
+ }
+ }
+ clean();
+ },
+ [clean](const ResponseError &err) {
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ clean();
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 3000);
+}
+
+void tst_qmlls_modules::buildDir()
+{
+ ignoreDiagnostics();
+ const QString filePath = u"completions/fromBuildDir.qml"_s;
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QTEST_CHECKED(checkCompletions(
+ *uri, 3, 0,
+ ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ }),
+ QStringList({ u"BuildDirType"_s, u"QtQuick"_s, u"width"_s, u"vector4d"_s })));
+ Notifications::AddBuildDirsParams bDirs;
+ UriToBuildDirs ub;
+ ub.baseUri = *uri;
+ ub.buildDirs.append(testFile("buildDir").toUtf8());
+ bDirs.buildDirsToSet.append(ub);
+ m_protocol->typedRpc()->sendNotification(QByteArray(Notifications::AddBuildDirsMethod), bDirs);
+
+ DidChangeTextDocumentParams didChange;
+ didChange.textDocument.uri = *uri;
+ didChange.textDocument.version = 2;
+
+ // change the file content to force qqml— to recreate a new DomItem
+ // if it reuses the old DomItem then it will not know about the added build directory
+ TextDocumentContentChangeEvent change;
+ change.range = Range{ Position{ 4, 0 }, Position{ 4, 0 } };
+ change.text = "\n";
+
+ didChange.contentChanges.append(change);
+ m_protocol->notifyDidChangeTextDocument(didChange);
+
+ QTEST_CHECKED(checkCompletions(*uri, 3, 0,
+ ExpectedCompletions({
+ { u"BuildDirType"_s, CompletionItemKind::Constructor },
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"width"_s, CompletionItemKind::Property },
+ }),
+ QStringList({ u"QtQuick"_s, u"vector4d"_s })));
+}
+
+void tst_qmlls_modules::automaticSemicolonInsertionForCompletions_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("row");
+ QTest::addColumn<int>("column");
+
+ QTest::addRow("bindingAfterDot") << u"completions/bindingAfterDot.qml"_s << 11 << 32;
+ QTest::addRow("defaultBindingAfterDot")
+ << u"completions/defaultBindingAfterDot.qml"_s << 11 << 32;
+}
+
+void tst_qmlls_modules::automaticSemicolonInsertionForCompletions()
+{
+ ignoreDiagnostics();
+ QFETCH(QString, filePath);
+ QFETCH(int, row);
+ QFETCH(int, column);
+ row--;
+ column--;
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ QTEST_CHECKED(checkCompletions(
+ *uri, row, column,
+ ExpectedCompletions({
+ { u"good"_s, CompletionItemKind::Property },
+ }),
+ QStringList({ u"bad"_s, u"BuildDirType"_s, u"QtQuick"_s, u"width"_s, u"vector4d"_s })));
+}
+
+void tst_qmlls_modules::checkQuickSnippets()
+{
+ ignoreDiagnostics();
+ const auto uri = openFile(u"completions/Yyy.qml"_s);
+ QVERIFY(uri);
+
+ // if at least one snippet is there, then the pluginloading works. To test the plugin itself,
+ // add tests in tst_qmlls_utils instead.
+ QTEST_CHECKED(checkCompletions(
+ *uri, 4, 3,
+ ExpectedCompletions({
+ { u"BorderImage snippet"_s, CompletionItemKind::Snippet },
+ }),
+ QStringList({})));
+}
+
+void tst_qmlls_modules::goToTypeDefinition_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QString>("expectedFilePath");
+ QTest::addColumn<int>("expectedStartLine");
+ QTest::addColumn<int>("expectedStartCharacter");
+ QTest::addColumn<int>("expectedEndLine");
+ QTest::addColumn<int>("expectedEndCharacter");
+
+ const QString yyyPath = u"completions/Yyy.qml"_s;
+ const QString zzzPath = u"completions/Zzz.qml"_s;
+ const QString someBasePath = u"completions/SomeBase.qml"_s;
+
+ QTest::newRow("BaseOfYyy") << yyyPath << 3 << 1 << zzzPath << 2 << 0 << 9 << 1;
+ QTest::newRow("BaseOfIC") << yyyPath << 29 << 19 << zzzPath << 2 << 0 << 9 << 1;
+
+ QTest::newRow("PropertyType") << yyyPath << 30 << 14 << someBasePath << 2 << 0 << 4 << 1;
+
+ QTest::newRow("TypeInIC") << yyyPath << 29 << 36 << someBasePath << 2 << 0 << 4 << 1;
+ QTest::newRow("ICTypeDefinition") << yyyPath << 29 << 15 << yyyPath << 29 << 14 << 29 << 16;
+}
+
+void tst_qmlls_modules::goToTypeDefinition()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, expectedFilePath);
+ QFETCH(int, expectedStartLine);
+ QFETCH(int, expectedStartCharacter);
+ QFETCH(int, expectedEndLine);
+ QFETCH(int, expectedEndCharacter);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QVERIFY(uri->startsWith("file://"_ba));
+
+ // note: do not call openFile(expectedFilePath), the definition should be found even if
+ // the qmlls user did not open the qml file with the definition yet.
+ const auto expectedUri = testFileUrl(expectedFilePath).toEncoded();
+
+ // TODO
+ TypeDefinitionParams params;
+ params.position.line = line;
+ params.position.character = character;
+ params.textDocument.uri = *uri;
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+
+ m_protocol->requestTypeDefinition(
+ params,
+ [&](auto res) {
+ QScopeGuard cleanup(clean);
+ auto *result = std::get_if<QList<Location>>(&res);
+
+ QVERIFY(result);
+
+ QCOMPARE(result->size(), 1);
+
+ Location l = result->front();
+ QCOMPARE(l.uri, expectedUri);
+ QCOMPARE(l.range.start.line, expectedStartLine);
+ QCOMPARE(l.range.start.character, expectedStartCharacter);
+ QCOMPARE(l.range.end.line, expectedEndLine);
+ QCOMPARE(l.range.end.character, expectedEndCharacter);
+ },
+ [clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
+}
+
+void tst_qmlls_modules::goToDefinition_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+
+ QTest::addColumn<QByteArray>("expectedUri");
+ // set to -1 when unchanged from above line and character. 0-based.
+ QTest::addColumn<int>("expectedStartLine");
+ QTest::addColumn<int>("expectedStartCharacter");
+ QTest::addColumn<int>("expectedEndLine");
+ QTest::addColumn<size_t>("expectedEndCharacter");
+
+ const QByteArray JSDefinitionsQml =
+ testFileUrl(u"findDefinition/jsDefinitions.qml"_s).toEncoded();
+ const QString JSDefinitionsQmlPath = u"findDefinition/jsDefinitions.qml"_s;
+ const QByteArray noResultExpected;
+
+ QTest::addRow("JSIdentifierX") << JSDefinitionsQmlPath << 14 << 11 << JSDefinitionsQml << 13
+ << 13 << 13 << 13 + strlen("x");
+ QTest::addRow("propertyI") << JSDefinitionsQmlPath << 14 << 14 << JSDefinitionsQml << 9 << 18
+ << 9 << 18 + strlen("i");
+ QTest::addRow("qualifiedPropertyI") << JSDefinitionsQmlPath << 15 << 21 << JSDefinitionsQml << 9
+ << 18 << 9 << 18 + strlen("i");
+ QTest::addRow("id") << JSDefinitionsQmlPath << 15 << 17 << JSDefinitionsQml << 7 << 9 << 7
+ << 9 + strlen("rootId");
+
+ QTest::addRow("comment") << JSDefinitionsQmlPath << 10 << 21 << noResultExpected << -1 << -1
+ << -1 << size_t{};
+}
+
+void tst_qmlls_modules::goToDefinition()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QByteArray, expectedUri);
+ QFETCH(int, expectedStartLine);
+ QFETCH(int, expectedStartCharacter);
+ QFETCH(int, expectedEndLine);
+ QFETCH(size_t, expectedEndCharacter);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QVERIFY(uri->startsWith("file://"_ba));
+
+ DefinitionParams params;
+ params.position.line = line - 1;
+ params.position.character = character - 1;
+ params.textDocument.uri = *uri;
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+
+ m_protocol->requestDefinition(
+ params,
+ [&](auto res) {
+ QScopeGuard cleanup(clean);
+ auto *result = std::get_if<QList<Location>>(&res);
+ const QByteArray noResultExpected;
+
+ QVERIFY(result);
+ if (expectedUri == noResultExpected) {
+ QCOMPARE(result->size(), 0);
+ } else {
+ QCOMPARE(result->size(), 1);
+
+ Location l = result->front();
+ QCOMPARE(l.uri, expectedUri);
+ QCOMPARE(l.range.start.line, expectedStartLine - 1);
+ QCOMPARE(l.range.start.character, expectedStartCharacter - 1);
+ QCOMPARE(l.range.end.line, expectedEndLine - 1);
+ QCOMPARE(l.range.end.character, (int)(expectedEndCharacter - 1));
+ }
+ },
+ [clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 30000);
+}
+
+// startLine and startCharacter start at 1, not 0
+static QLspSpecification::Range rangeFrom(const QString &code, quint32 startLine,
+ quint32 startCharacter, quint32 length)
+{
+ QLspSpecification::Range range;
+
+ // the LSP works with lines and characters starting at 0
+ range.start.line = startLine - 1;
+ range.start.character = startCharacter - 1;
+
+ quint32 startOffset = QQmlLSUtils::textOffsetFrom(code, startLine - 1, startCharacter - 1);
+ auto end = QQmlLSUtils::textRowAndColumnFrom(code, startOffset + length);
+ range.end.line = end.line;
+ range.end.character = end.character;
+
+ return range;
+}
+
+// startLine and startCharacter start at 1, not 0
+static QLspSpecification::Location locationFrom(const QByteArray fileName, const QString &code,
+ quint32 startLine, quint32 startCharacter,
+ quint32 length)
+{
+ QLspSpecification::Location location;
+ location.uri = QQmlLSUtils::qmlUrlToLspUri(fileName);
+ location.range = rangeFrom(code, startLine, startCharacter, length);
+ return location;
+}
+
+void tst_qmlls_modules::findUsages_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QList<QLspSpecification::Location>>("expectedUsages");
+
+ const QByteArray jsIdentifierUsagesUri =
+ testFileUrl("findUsages/jsIdentifierUsages.qml").toEncoded();
+ const QString jsIdentifierUsagesPath = u"findUsages/jsIdentifierUsages.qml"_s;
+
+ QString jsIdentifierUsagesContent;
+ {
+ QFile file(testFile("findUsages/jsIdentifierUsages.qml").toUtf8());
+ QVERIFY(file.open(QIODeviceBase::ReadOnly));
+ jsIdentifierUsagesContent = QString::fromUtf8(file.readAll());
+ }
+
+ // line and character start at 1!
+ const QList<QLspSpecification::Location> sumUsages = {
+ locationFrom(jsIdentifierUsagesUri, jsIdentifierUsagesContent, 8, 13, strlen("sum")),
+ locationFrom(jsIdentifierUsagesUri, jsIdentifierUsagesContent, 10, 13, strlen("sum")),
+ locationFrom(jsIdentifierUsagesUri, jsIdentifierUsagesContent, 10, 19, strlen("sum")),
+ };
+ QVERIFY(sumUsages.front().uri.startsWith("file://"_ba));
+
+ // line and character start at 1!
+ QTest::addRow("sumUsagesFromUsage") << jsIdentifierUsagesPath << 10 << 14 << sumUsages;
+ QTest::addRow("sumUsagesFromUsage2") << jsIdentifierUsagesPath << 10 << 20 << sumUsages;
+ QTest::addRow("sumUsagesFromDefinition") << jsIdentifierUsagesPath << 8 << 14 << sumUsages;
+}
+
+static bool locationsAreEqual(const QLspSpecification::Location &a,
+ const QLspSpecification::Location &b)
+{
+ return std::tie(a.uri, a.range.start.character, a.range.start.line, a.range.end.character,
+ a.range.end.line)
+ == std::tie(b.uri, b.range.start.character, b.range.start.line, b.range.end.character,
+ b.range.end.line);
+}
+
+static bool locationListsAreEqual(const QList<QLspSpecification::Location> &a,
+ const QList<QLspSpecification::Location> &b)
+{
+ return std::equal(a.cbegin(), a.cend(), b.cbegin(), b.cend(), locationsAreEqual);
+}
+
+static QString locationToString(const QLspSpecification::Location &l)
+{
+ QString s = u"%1: (%2, %3) - (%4, %5)"_s.arg(l.uri)
+ .arg(l.range.start.line)
+ .arg(l.range.start.character)
+ .arg(l.range.end.line)
+ .arg(l.range.end.character);
+ return s;
+}
+
+void tst_qmlls_modules::findUsages()
+{
+ QFETCH(QString, filePath);
+ // line and character start at 1!
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QList<QLspSpecification::Location>, expectedUsages);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QVERIFY(uri->startsWith("file://"_ba));
+
+ ReferenceParams params;
+ params.position.line = line - 1;
+ params.position.character = character - 1;
+ params.textDocument.uri = *uri;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+ m_protocol->requestReference(
+ params,
+ [&](auto res) {
+ QScopeGuard cleanup(clean);
+ auto *result = std::get_if<QList<Location>>(&res);
+
+ QVERIFY(result);
+ if constexpr (enable_debug_output) {
+ if (!locationListsAreEqual(*result, expectedUsages)) {
+ qDebug() << "Got following locations:";
+ for (auto &x : *result) {
+ qDebug() << locationToString(x);
+ }
+ qDebug() << "But expected:";
+ for (auto &x : expectedUsages) {
+ qDebug() << locationToString(x);
+ }
+ }
+ } else {
+ // dont get warning on unused function when enable_debug_output is false
+ Q_UNUSED(locationToString);
+ }
+
+ QVERIFY(locationListsAreEqual(*result, expectedUsages));
+ },
+ [clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 3000);
+}
+
+void tst_qmlls_modules::documentFormatting_data()
+{
+ QTest::addColumn<QString>("originalFile");
+ QTest::addColumn<QString>("expectedFile");
+
+ QDir directory(QT_QMLFORMATTEST_DATADIR);
+
+ // Exclude some test files which require options support
+ QStringList excludedFiles;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/checkIdsNewline.qml"_s;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/normalizedFunctionsSpacing.qml"_s;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/normalizedObjectsSpacing.qml"_s;
+
+ // excluded because it crashes Dom construction
+ // TODO: fix QQMLDomAstConstructor to not crash on these files, see QTBUG-116392
+ excludedFiles << u"tests/auto/qml/qmlformat/data/ecmaScriptClassInQml.qml"_s;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/Example1.qml"_s;
+ excludedFiles << u"tests/auto/qml/qmlformat/data/nestedFunctions.qml"_s;
+
+ const auto shouldSkip = [&excludedFiles](const QString &fileName) {
+ for (const QString &file : excludedFiles) {
+ if (fileName.endsWith(file))
+ return true;
+ }
+ return false;
+ };
+
+ // Filter to include files contain .formatted.
+ const auto formattedFilesInfo =
+ directory.entryInfoList(QStringList{ { "*.formatted.qml" } }, QDir::Files);
+ for (const auto &formattedFileInfo : formattedFilesInfo) {
+ const QFileInfo unformattedFileInfo(directory, formattedFileInfo.fileName().remove(".formatted"));
+ const auto unformattedFilePath = unformattedFileInfo.canonicalFilePath();
+ if (shouldSkip(unformattedFilePath))
+ continue;
+
+ QTest::newRow(qPrintable(unformattedFileInfo.fileName()))
+ << unformattedFilePath << formattedFileInfo.canonicalFilePath();
+ }
+
+ // Extra tests
+ QTest::newRow("leading-and-trailing-blanklines")
+ << testFile("formatting/blanklines.qml")
+ << testFile("formatting/blanklines.formatted.qml");
+}
+
+void tst_qmlls_modules::documentFormatting()
+{
+ QFETCH(QString, originalFile);
+ QFETCH(QString, expectedFile);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFileFromAbsolutePath(originalFile);
+ QVERIFY(uri);
+
+ DocumentFormattingParams params;
+ params.textDocument.uri = *uri;
+
+ const auto lineCount = [](const QString &filePath) {
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "Error while opening the file " << filePath;
+ return -1;
+ }
+ int lineCount = 0;
+ QString line;
+ while (!file.atEnd()) {
+ line = file.readLine();
+ ++lineCount;
+ }
+ if (line.endsWith('\n'))
+ ++lineCount;
+ return lineCount;
+ };
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+ auto &&responseHandler = [&](auto response) {
+ QScopeGuard cleanup(clean);
+ if (std::holds_alternative<QList<TextEdit>>(response)) {
+ const auto results = std::get<QList<TextEdit>>(response);
+ QVERIFY(results.size() == 1);
+ QFile file(expectedFile);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "Error while opening the file " << expectedFile;
+ return;
+ }
+
+ const auto &textEdit = results.first();
+ QCOMPARE(textEdit.range.start.line, 0);
+ QCOMPARE(textEdit.range.start.character, 0);
+ QCOMPARE(textEdit.range.end.line, lineCount(originalFile));
+ QCOMPARE(textEdit.range.end.character, 0);
+ QCOMPARE(textEdit.newText, file.readAll());
+ }
+ };
+
+ auto &&errorHandler = [&clean](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ QVERIFY2(false, "error computing the completion");
+ };
+ m_protocol->requestDocumentFormatting(params, std::move(responseHandler),
+ std::move(errorHandler));
+
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 50000);
+}
+
+void tst_qmlls_modules::renameUsages_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QString>("newName");
+ QTest::addColumn<QLspSpecification::WorkspaceEdit>("expectedEdit");
+ QTest::addColumn<QLspSpecification::ResponseError>("expectedError");
+
+ QLspSpecification::ResponseError noError;
+
+ const QString jsIdentifierUsagesPath = u"findUsages/jsIdentifierUsages.qml"_s;
+ const QByteArray jsIdentifierUsagesUri =
+ testFileUrl("findUsages/jsIdentifierUsages.qml").toEncoded();
+
+ QString jsIdentifierUsagesContent;
+ {
+ QFile file(testFile("findUsages/jsIdentifierUsages.qml").toUtf8());
+ QVERIFY(file.open(QIODeviceBase::ReadOnly));
+ jsIdentifierUsagesContent = QString::fromUtf8(file.readAll());
+ }
+
+ // TODO: create workspace edit for the tests
+ QLspSpecification::WorkspaceEdit sumRenames{
+ std::nullopt, // TODO
+ QList<QLspSpecification::WorkspaceEdit::DocumentChange>{
+ TextDocumentEdit{
+ OptionalVersionedTextDocumentIdentifier{ { jsIdentifierUsagesUri } },
+ {
+ TextEdit{
+ rangeFrom(jsIdentifierUsagesContent, 8, 13, strlen("sum")),
+ "specialSum" },
+ TextEdit{
+ rangeFrom(jsIdentifierUsagesContent, 10, 13, strlen("sum")),
+ "specialSum" },
+ TextEdit{
+ rangeFrom(jsIdentifierUsagesContent, 10, 19, strlen("sum")),
+ "specialSum" },
+ } },
+ }
+ };
+
+ // line and character start at 1!
+ QTest::addRow("sumRenameFromUsage")
+ << jsIdentifierUsagesPath << 10 << 14 << u"specialSum"_s << sumRenames << noError;
+ QTest::addRow("sumRenameFromUsage2")
+ << jsIdentifierUsagesPath << 10 << 20 << u"specialSum"_s << sumRenames << noError;
+ QTest::addRow("sumRenameFromDefinition")
+ << jsIdentifierUsagesPath << 8 << 14 << u"specialSum"_s << sumRenames << noError;
+ QTest::addRow("invalidSumRenameFromDefinition")
+ << jsIdentifierUsagesPath << 8 << 14 << u"function"_s << sumRenames
+ << QLspSpecification::ResponseError{
+ 0,
+ "Invalid EcmaScript identifier!",
+ std::nullopt,
+ };
+}
+
+void tst_qmlls_modules::compareQTextDocumentEdit(const TextDocumentEdit &a,
+ const TextDocumentEdit &b)
+{
+
+ QCOMPARE(a.textDocument.uri, b.textDocument.uri);
+ QVERIFY(a.textDocument.uri.startsWith("file://"));
+ QCOMPARE(a.textDocument.version, b.textDocument.version);
+ QCOMPARE(a.edits.size(), b.edits.size());
+
+ for (qsizetype j = 0; j < a.edits.size(); ++j) {
+ std::visit(
+ [](auto &&textEdit, auto &&expectedTextEdit) {
+ using U = std::decay_t<decltype(textEdit)>;
+ using V = std::decay_t<decltype(expectedTextEdit)>;
+
+ if constexpr (std::conjunction_v<std::is_same<U, V>,
+ std::is_same<U, TextEdit>>) {
+ QCOMPARE(textEdit.range.start.line, expectedTextEdit.range.start.line);
+ QCOMPARE(textEdit.range.start.character,
+ expectedTextEdit.range.start.character);
+ QCOMPARE(textEdit.range.end.line, expectedTextEdit.range.end.line);
+ QCOMPARE(textEdit.range.end.character,
+ expectedTextEdit.range.end.character);
+ QCOMPARE(textEdit.newText, expectedTextEdit.newText);
+ } else {
+ QFAIL("Comparison not implemented");
+ }
+ },
+ a.edits[j], b.edits[j]);
+ }
+}
+
+void tst_qmlls_modules::renameUsages()
+{
+ QFETCH(QString, filePath);
+ // line and character start at 1!
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, newName);
+ QFETCH(QLspSpecification::WorkspaceEdit, expectedEdit);
+ QFETCH(QLspSpecification::ResponseError, expectedError);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QVERIFY(uri->startsWith("file://"_ba));
+
+ RenameParams params;
+ params.position.line = line - 1;
+ params.position.character = character - 1;
+ params.textDocument.uri = *uri;
+ params.newName = newName.toUtf8();
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ auto clean = [didFinish]() { *didFinish = true; };
+ m_protocol->requestRename(
+ params,
+ [&](auto res) {
+ QScopeGuard cleanup(clean);
+ auto *result = std::get_if<QLspSpecification::WorkspaceEdit>(&res);
+
+ QVERIFY(result);
+ QCOMPARE(result->changes.has_value(), expectedEdit.changes.has_value());
+ QCOMPARE(result->changeAnnotations.has_value(),
+ expectedEdit.changeAnnotations.has_value());
+ QCOMPARE(result->documentChanges.has_value(),
+ expectedEdit.documentChanges.has_value());
+
+ auto &documentChanges = *result->documentChanges;
+ auto &expectedDocumentChanges = *expectedEdit.documentChanges;
+
+ if (!expectedError.message.isEmpty())
+ QVERIFY2(false, "No expected error was thrown.");
+
+ QCOMPARE(documentChanges.size(), expectedDocumentChanges.size());
+
+ for (qsizetype i = 0; i < expectedDocumentChanges.size(); ++i) {
+ QCOMPARE(documentChanges[i].index(), expectedDocumentChanges[i].index());
+ if (std::holds_alternative<TextDocumentEdit>(documentChanges[i])) {
+ compareQTextDocumentEdit(
+ std::get<TextDocumentEdit>(documentChanges[i]),
+ std::get<TextDocumentEdit>(expectedDocumentChanges[i]));
+ } else {
+ QFAIL("TODO: implement me!");
+ }
+ }
+ },
+ [clean, &expectedError](const ResponseError &err) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(err);
+ if (expectedError.message.isEmpty())
+ QVERIFY2(false, "unexpected error computing the completion");
+ QCOMPARE(err.code, expectedError.code);
+ QCOMPARE(err.message, expectedError.message);
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 3000);
+}
+
+struct EditingRecorder
+{
+ QList<DidChangeTextDocumentParams> actions;
+ QHash<int, QString> diagnosticsPerFileVersions;
+
+ /*!
+ All the indexes passed here must start at 1!
+
+ If you want to make sure that your own written changes make sense, use
+ \code
+ qputenv("QT_LOGGING_RULES",
+ "qt.languageserver.codemodel.debug=true;qt.languageserver.codemodel.warning=true"); \endcode
+ before starting qmlls. It will print the differences between the different versions, and helps
+ when some indices are off.
+ */
+ void changeText(int startLine, int startCharacter, int endLine, int endCharacter,
+ QString newText)
+ {
+ // The LSP starts at 0
+ QVERIFY(startLine > 0);
+ QVERIFY(startCharacter > 0);
+ QVERIFY(endLine > 0);
+ QVERIFY(endCharacter > 0);
+
+ --startLine;
+ --startCharacter;
+ --endLine;
+ --endCharacter;
+
+ DidChangeTextDocumentParams params;
+ params.textDocument = VersionedTextDocumentIdentifier{ { lastFileUri }, ++version };
+ params.contentChanges.append({
+ Range{ Position{ startLine, startCharacter }, Position{ endLine, endCharacter } },
+ std::nullopt, // deprecated range length
+ newText.toUtf8(),
+ });
+ actions.append(params);
+ }
+
+ void setFile(const QString &filePath) { lastFilePath = filePath; }
+
+ void setCurrentExpectedDiagnostic(const QString &diagnostic)
+ {
+ Q_ASSERT(diagnosticsPerFileVersions.find(version) == diagnosticsPerFileVersions.end());
+ diagnosticsPerFileVersions[version] = diagnostic;
+ }
+
+ QString lastFilePath;
+ QByteArray lastFileUri;
+ int version = 0;
+};
+
+static constexpr int characterAfter(const char *line)
+{
+ return std::char_traits<char>::length(line) + 1;
+}
+
+static EditingRecorder propertyTypoScenario(const QByteArray &fileUri)
+{
+ EditingRecorder propertyTypo;
+ propertyTypo.lastFileUri = fileUri;
+
+ propertyTypo.changeText(5, 1, 5, 1, u" property int t"_s);
+
+ // replace property by propertyt and expect a complaint from the parser
+ propertyTypo.changeText(5, characterAfter(" property"), 5, characterAfter(" property"),
+ u"t"_s);
+ propertyTypo.setCurrentExpectedDiagnostic(u"Expected token"_s);
+
+ // replace propertyt back to property and expect no complaint from the parser
+ propertyTypo.changeText(5, characterAfter(" property"), 5, characterAfter(" propertyt"),
+ u""_s);
+
+ // replace property by propertyt and expect a complaint from the parser
+ propertyTypo.changeText(5, characterAfter(" property"), 5, characterAfter(" property"),
+ u"t"_s);
+ propertyTypo.setCurrentExpectedDiagnostic(u"Expected token"_s);
+
+ // now, simulate some slow typing and expect the previous warning to not disappear
+ const QString data = u"Item {}\n"_s;
+ for (int i = 0; i < data.size(); ++i) {
+ propertyTypo.changeText(6, i + 1, 6, i + 1, data[i]);
+ propertyTypo.setCurrentExpectedDiagnostic(u"Expected token"_s);
+ }
+
+ // replace propertyt back to property and expect no complaint from the parser
+ propertyTypo.changeText(5, characterAfter(" property"), 5, characterAfter(" propertyt"),
+ u""_s);
+
+ return propertyTypo;
+}
+
+void tst_qmlls_modules::linting_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<EditingRecorder>("recorder");
+
+ QTest::addRow("property-typo")
+ << u"linting/SimpleItem.qml"_s
+ << propertyTypoScenario(testFileUrl(u"linting/SimpleItem.qml"_s).toEncoded());
+}
+
+void tst_qmlls_modules::linting()
+{
+ QFETCH(QString, filePath);
+ QFETCH(EditingRecorder, recorder);
+ bool diagnosticOk = false;
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ recorder.lastFileUri = *uri;
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [&recorder, &diagnosticOk, &uri](const QByteArray &,
+ const PublishDiagnosticsParams &p) {
+ if (p.uri != *uri || !p.version)
+ return;
+ auto expectedMessage = recorder.diagnosticsPerFileVersions.find(*p.version);
+ if (expectedMessage == recorder.diagnosticsPerFileVersions.end()) {
+ if constexpr (enable_debug_output) {
+ if (p.diagnostics.size() > 0)
+ qDebug() << "Did not expect message" << p.diagnostics.front().message;
+ }
+
+ QVERIFY(p.diagnostics.size() == 0);
+ diagnosticOk = true;
+ return;
+ }
+ QVERIFY(p.diagnostics.size() > 0);
+ if constexpr (enable_debug_output) {
+ if (!p.diagnostics.front().message.contains(expectedMessage->toUtf8())) {
+ qDebug() << "expected a message with" << *expectedMessage << "but got"
+ << p.diagnostics.front().message;
+ }
+ }
+ QVERIFY(p.diagnostics.front().message.contains(expectedMessage->toUtf8()));
+ diagnosticOk = true;
+ });
+ for (const auto &action : recorder.actions) {
+ m_protocol->notifyDidChangeTextDocument(action);
+ QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk, 5000);
+ diagnosticOk = false;
+ }
+}
+
+void tst_qmlls_modules::rangeFormatting_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QLspSpecification::Range>("selectedRange");
+ QTest::addColumn<QLspSpecification::Range>("expectedRange");
+ QTest::addColumn<QString>("expectedAfterFormat");
+
+ const QString filePath = u"formatting/rangeFormatting.qml"_s;
+
+ {
+ QLspSpecification::Range selectedRange = { { 5, 0 }, { 9, 0 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectRegion1") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted1.qml"_s;
+ }
+
+ {
+ QLspSpecification::Range selectedRange = { { 10, 25 }, { 23, 0 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectRegion2") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted2.qml"_s;
+ }
+
+ {
+ QLspSpecification::Range selectedRange = { { 14, 36 }, { 14, 45 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectSingleLine") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted3.qml"_s;
+ }
+
+ {
+ QLspSpecification::Range selectedRange = { { 0, 0 }, { 24, 0 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectEntireFile") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted4.qml"_s;
+ }
+
+ {
+ QLspSpecification::Range selectedRange = { { 10, 3 }, { 20, 4 } };
+ QLspSpecification::Range expectedRange = { { 0, 0 }, { 24, 0 } };
+ QTest::addRow("selectUnbalanced") << filePath << selectedRange << expectedRange
+ << u"formatting/rangeFormatting.formatted5.qml"_s;
+ }
+}
+
+void tst_qmlls_modules::rangeFormatting()
+{
+ QFETCH(QString, filePath);
+ QFETCH(QLspSpecification::Range, selectedRange);
+ QFETCH(QLspSpecification::Range, expectedRange);
+ QFETCH(QString, expectedAfterFormat);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ QLspSpecification::DocumentRangeFormattingParams params;
+ params.textDocument.uri = *uri;
+ params.range = selectedRange;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto clean = [didFinish]() { *didFinish = true; };
+
+ auto &&responseHandler = [&](auto res) {
+ Q_UNUSED(res);
+ QScopeGuard cleanup(clean);
+ auto result = std::get_if<QList<TextEdit>>(&res);
+ QVERIFY(result);
+
+ QFile file(testFile(expectedAfterFormat));
+ if (!file.open(QIODevice::ReadOnly))
+ QFAIL("Error while opening the file ");
+
+ const auto text = result->first();
+ QCOMPARE(text.range.start.line, expectedRange.start.line);
+ QCOMPARE(text.range.start.character, expectedRange.start.character);
+ QCOMPARE(text.range.end.line, expectedRange.end.line);
+ QCOMPARE(text.range.end.character, expectedRange.end.character);
+ QCOMPARE(text.newText, file.readAll());
+ };
+
+ auto &&errorHandler = [&clean](auto &error) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred while range formatting");
+ };
+
+ m_protocol->requestDocumentRangeFormatting(params, std::move(responseHandler),
+ std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+}
+
+void tst_qmlls_modules::hover_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QLspSpecification::Position>("hoveredPosition");
+ QTest::addColumn<QLspSpecification::MarkupContent>("expectedResult");
+
+ const QString filePath = u"hover/test.qml"_s;
+ {
+ QLspSpecification::MarkupContent content{ MarkupKind::PlainText, "should fail" };
+ QTest::addRow("hover") << filePath << QLspSpecification::Position{ 7, 24 } << content;
+ }
+}
+
+void tst_qmlls_modules::hover()
+{
+ QFETCH(QString, filePath);
+ QFETCH(QLspSpecification::Position, hoveredPosition);
+ QFETCH(QLspSpecification::MarkupContent, expectedResult);
+
+ ignoreDiagnostics();
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ QLspSpecification::HoverParams params;
+ params.textDocument.uri = *uri;
+ params.position = hoveredPosition;
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto clean = [didFinish]() { *didFinish = true; };
+
+ auto &&responseHandler = [&](auto res) {
+ QScopeGuard cleanup(clean);
+ const auto *const result = std::get_if<QLspSpecification::Hover>(&res);
+ QVERIFY(result);
+
+ const auto *const markupContent =
+ std::get_if<QLspSpecification::MarkupContent>(&result->contents);
+ QVERIFY(markupContent);
+
+ QEXPECT_FAIL("hover", "Should fail until we get the actual documentation for hovered items",
+ Continue);
+ QCOMPARE(markupContent->value, expectedResult.value);
+ QCOMPARE(markupContent->kind, expectedResult.kind);
+ };
+
+ auto &&errorHandler = [&clean](auto &error) {
+ QScopeGuard cleanup(clean);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred on hovering");
+ };
+
+ m_protocol->requestHover(params, std::move(responseHandler), std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+}
+
+enum AddBuildDirOption : bool { AddBuildDir, DoNotAddBuildDir };
+
+void tst_qmlls_modules::qmldirImports_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<AddBuildDirOption>("addBuildDirectory");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QString>("expectedCompletion");
+
+ QTest::addRow("fromBuildFolder")
+ << u"completions/fromBuildDir.qml"_s << AddBuildDir << 3 << 1 << u"BuildDirType"_s;
+ QTest::addRow("fromSourceFolder")
+ << u"sourceDir/Main.qml"_s << DoNotAddBuildDir << 3 << 1 << u"Button"_s;
+}
+
+void tst_qmlls_modules::qmldirImports()
+{
+ QFETCH(QString, filePath);
+ QFETCH(AddBuildDirOption, addBuildDirectory);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, expectedCompletion);
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ if (addBuildDirectory == AddBuildDir) {
+ Notifications::AddBuildDirsParams bDirs;
+ UriToBuildDirs ub;
+ ub.baseUri = *uri;
+ ub.buildDirs.append(testFile("buildDir").toUtf8());
+ bDirs.buildDirsToSet.append(ub);
+ m_protocol->typedRpc()->sendNotification(QByteArray(Notifications::AddBuildDirsMethod), bDirs);
+ }
+
+ bool diagnosticOk = false;
+ bool completionOk = false;
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [&diagnosticOk, &uri](const QByteArray &, const PublishDiagnosticsParams &p) {
+ if (p.uri != *uri)
+ return;
+
+ if constexpr (enable_debug_output) {
+ for (const auto &x : p.diagnostics) {
+ qDebug() << x.message;
+ }
+ }
+ QCOMPARE(p.diagnostics.size(), 0);
+ diagnosticOk = true;
+ });
+
+ // Currently, the Dom is created twice in qmlls: once for the linting and once for all other
+ // features. Therefore, also test that this second dom also uses the right resource files.
+ CompletionParams cParams;
+ cParams.position.line = line - 1; // LSP is 0 based
+ cParams.position.character = character - 1; // LSP is 0 based
+ cParams.textDocument.uri = *uri;
+
+ m_protocol->requestCompletion(cParams, [&completionOk, &expectedCompletion](auto res) {
+ const QList<CompletionItem> *cItems = std::get_if<QList<CompletionItem>>(&res);
+
+ QSet<QString> labels;
+ for (const CompletionItem &c : *cItems) {
+ labels << c.label;
+ }
+ QVERIFY(labels.contains(expectedCompletion));
+ completionOk = true;
+ });
+
+ QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk && completionOk, 5000);
+}
+
+void tst_qmlls_modules::quickFixes_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<CodeActionParams>("codeActionParams");
+ QTest::addColumn<int>("diagnosticIndex");
+ QTest::addColumn<Range>("replacementRange");
+ QTest::addColumn<QString>("replacementText");
+
+ const QString filePath = u"quickfixes/INeedAQuickFix.qml"_s;
+
+ QString fileContent;
+ {
+ QFile file(testFile(filePath));
+ QVERIFY(file.open(QFile::Text | QFile::ReadOnly));
+ fileContent = file.readAll();
+ }
+
+ CodeActionParams firstCodeAction;
+ firstCodeAction.range = rangeFrom(fileContent, 10, 23, 1);
+ firstCodeAction.textDocument.uri = testFileUrl(filePath).toEncoded();
+
+ const Range firstRange = rangeFrom(fileContent, 10, 10, 0);
+ const QString firstReplacement = u"(xxx) => "_s;
+
+ QTest::addRow("injectedParameters")
+ << filePath << firstCodeAction << 0 << firstRange << firstReplacement;
+
+ CodeActionParams secondCodeAction;
+ secondCodeAction.textDocument.uri = testFileUrl(filePath).toEncoded();
+ secondCodeAction.range = rangeFrom(fileContent, 15, 20, 1);
+
+ const Range secondRange = rangeFrom(fileContent, 15, 20, 0);
+ const QString secondReplacement = u"hello."_s;
+
+ QTest::addRow("parentProperty")
+ << filePath << secondCodeAction << 1 << secondRange << secondReplacement;
+}
+
+std::tuple<int, int, int, int> rangeAsTuple(const Range &range)
+{
+ return std::make_tuple(range.start.line, range.start.character, range.end.line,
+ range.end.character);
+}
+
+void tst_qmlls_modules::quickFixes()
+{
+ QFETCH(QString, filePath);
+ QFETCH(CodeActionParams, codeActionParams);
+ // The index of the diagnostic that the quickFix belongs to.
+ // diagnostics are sorted by their range (= text position in the current file).
+ QFETCH(int, diagnosticIndex);
+ QFETCH(Range, replacementRange);
+ QFETCH(QString, replacementText);
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+
+ bool diagnosticOk = false;
+ QList<Diagnostic> diagnostics;
+
+ // run first the diagnostic that proposes a quickfix
+ m_protocol->registerPublishDiagnosticsNotificationHandler(
+ [&diagnosticOk, &uri, &diagnostics,
+ &diagnosticIndex](const QByteArray &, const PublishDiagnosticsParams &p) {
+ if (p.uri != *uri)
+ return;
+
+ if constexpr (enable_debug_output) {
+ for (const auto &x : p.diagnostics) {
+ qDebug() << x.message;
+ }
+ }
+ QCOMPARE_GE(p.diagnostics.size(), diagnosticIndex);
+
+ QList<Diagnostic> partially_sorted{ p.diagnostics };
+ std::nth_element(partially_sorted.begin(),
+ std::next(partially_sorted.begin(), diagnosticIndex),
+ partially_sorted.end(),
+ [](const Diagnostic &a, const Diagnostic &b) {
+ return rangeAsTuple(a.range) < rangeAsTuple(b.range);
+ });
+ diagnostics.append(partially_sorted[diagnosticIndex]);
+
+ diagnosticOk = true;
+ });
+
+ QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk, 5000);
+
+ codeActionParams.context.diagnostics = diagnostics;
+
+ using InnerT = QList<std::variant<Command, CodeAction>>;
+ using T = std::variant<InnerT, std::nullptr_t>;
+
+ bool codeActionOk = false;
+
+ // request a quickfix with the obtained diagnostic
+ m_protocol->requestCodeAction(codeActionParams, [&](const T &result) {
+ QVERIFY(std::holds_alternative<InnerT>(result));
+ InnerT inner = std::get<InnerT>(result);
+ QCOMPARE(inner.size(), 1);
+ QVERIFY(std::holds_alternative<CodeAction>(inner.front()));
+ CodeAction codeAction = std::get<CodeAction>(inner.front());
+
+ QCOMPARE(codeAction.kind, "quickfix"); // everything else is ignored by QtC, VS Code, ...
+
+ QVERIFY(codeAction.edit);
+ QVERIFY(codeAction.edit->documentChanges);
+ const auto &edits = *codeAction.edit->documentChanges;
+ QCOMPARE(edits.size(), 1);
+ const auto& firstEdit = std::get<TextDocumentEdit>(edits.front());
+ QCOMPARE(firstEdit.edits.size(), 1);
+ QVERIFY(std::holds_alternative<TextEdit>(firstEdit.edits.front()));
+ auto textEdit = std::get<TextEdit>(firstEdit.edits.front());
+
+ // make sure that the quick fix does something
+ QCOMPARE(textEdit.newText, replacementText);
+ QCOMPARE(rangeAsTuple(textEdit.range), rangeAsTuple(replacementRange));
+
+ codeActionOk = true;
+ });
+
+ QTRY_VERIFY_WITH_TIMEOUT(codeActionOk, 5000);
+}
+
+static QQmlJS::Dom::DomItem fileObject(const QString &filePath)
+{
+ QFile f(filePath);
+ DomItem file;
+ if (!f.open(QIODevice::ReadOnly))
+ return file;
+ QString code = f.readAll();
+ QQmlJS::Dom::DomCreationOptions options;
+ options.setFlag(QQmlJS::Dom::DomCreationOption::WithScriptExpressions);
+ options.setFlag(QQmlJS::Dom::DomCreationOption::WithSemanticAnalysis);
+ options.setFlag(QQmlJS::Dom::DomCreationOption::WithRecovery);
+
+ QStringList dirs = {QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)};
+ auto envPtr = QQmlJS::Dom::DomEnvironment::create(dirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies, options);
+ envPtr->loadBuiltins();
+ envPtr->loadFile(QQmlJS::Dom::FileToLoad::fromMemory(envPtr, filePath, code),
+ [&file](QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &, const QQmlJS::Dom::DomItem &newIt) {
+ file = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+ return file;
+};
+
+void tst_qmlls_modules::semanticHighlightingFull_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addRow("bigfile") << u"highlighting/bigFile.qml"_s;
+}
+
+void tst_qmlls_modules::semanticHighlightingFull()
+{
+ QFETCH(QString, filePath);
+ const auto item = fileObject(testFile(filePath));
+ Highlights highlights;
+ const auto expectedData = highlights.collectTokens(item, std::nullopt);
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QLspSpecification::SemanticTokensParams params;
+ params.textDocument.uri = *uri;
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto cleanup = [didFinish]() { *didFinish = true; };
+
+ auto &&responseHandler = [&](auto res) {
+ QScopeGuard callAtExit(cleanup);
+ const auto *const result = std::get_if<QLspSpecification::SemanticTokens>(&res);
+ QVERIFY(result);
+ QList<int> data = result->data;
+ QCOMPARE(data.size(), expectedData.size());
+ QCOMPARE(data, expectedData);
+ };
+
+ auto &&errorHandler = [&](auto &error) {
+ QScopeGuard callAtExit(cleanup);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred on full semantic tokens");
+ };
+
+ m_protocol->requestSemanticTokens(params, std::move(responseHandler), std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+}
+
+void tst_qmlls_modules::semanticHighlightingRange_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QLspSpecification::Range>("range");
+ QTest::addRow("bigfile") << u"highlighting/bigFile.qml"_s
+ << QLspSpecification::Range{ { 6, 0 }, { 15, 0 } };
+}
+
+void tst_qmlls_modules::semanticHighlightingRange()
+{
+ QFETCH(QString, filePath);
+ QFETCH(QLspSpecification::Range, range);
+
+ const auto item = fileObject(testFile(filePath));
+ Highlights highlights;
+ const auto qmlFile = item.as<QQmlJS::Dom::QmlFile>();
+ const auto code = qmlFile->code();
+ const int startOffset = int(QQmlLSUtils::textOffsetFrom(code, range.start.line, range.end.character));
+ const int endOffset = int(QQmlLSUtils::textOffsetFrom(code, range.end.line, range.end.character));
+ const auto expectedData = highlights.collectTokens(item, HighlightsRange{startOffset, endOffset});
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ QLspSpecification::SemanticTokensRangeParams params;
+ params.textDocument.uri = *uri;
+ params.range = range;
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto cleanup = [didFinish]() { *didFinish = true; };
+
+ auto &&responseHandler = [&](auto res) {
+ QScopeGuard callAtExit(cleanup);
+ const auto *const result = std::get_if<QLspSpecification::SemanticTokens>(&res);
+ QVERIFY(result);
+ QList<int> data = result->data;
+ QCOMPARE(data.size(), expectedData.size());
+ QCOMPARE(data, expectedData);
+ };
+
+ auto &&errorHandler = [&](auto &error) {
+ QScopeGuard callAtExit(cleanup);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred on full semantic tokens");
+ };
+
+ m_protocol->requestSemanticTokensRange(params, std::move(responseHandler),
+ std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+}
+
+void tst_qmlls_modules::semanticHighlightingDelta_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addRow("basicDelta") << u"highlighting/basic.qml"_s;
+}
+
+void tst_qmlls_modules::semanticHighlightingDelta()
+{
+ QSKIP("This test should be skipped until QTBUG-124870 is fixed");
+ QFETCH(QString, filePath);
+ QFETCH(QString, deltaFilePath);
+
+ const auto fileItem = fileObject(testFile(filePath));
+ const auto deltaFileItem = fileObject(testFile(deltaFilePath));
+ Highlights highlights;
+ auto fullDocumentSemanticTokensData = highlights.collectTokens(fileItem, std::nullopt);
+ auto editedDocumentSemanticTokensData = highlights.collectTokens(deltaFileItem, std::nullopt);
+ const auto expectedEdits = HighlightingUtils::computeDiff(fullDocumentSemanticTokensData, editedDocumentSemanticTokensData);
+
+ const auto uri = openFile(filePath);
+ QVERIFY(uri);
+ const auto deltaUri = openFile(deltaFilePath);
+ QVERIFY(deltaUri);
+
+ std::shared_ptr<bool> didFinish = std::make_shared<bool>(false);
+ const auto cleanup = [didFinish]() { *didFinish = true; };
+
+ QLspSpecification::SemanticTokensDeltaParams params;
+ QLspSpecification::Responses::SemanticTokensDeltaResultType result;
+
+ auto &&errorHandler = [&](auto &error) {
+ QScopeGuard callAtExit(cleanup);
+ ProtocolBase::defaultResponseErrorHandler(error);
+ QVERIFY2(false, "error occurred on semantic tokens/delta");
+ };
+
+ QLspSpecification::SemanticTokensParams fullParams;
+ fullParams.textDocument.uri = *uri;
+ m_protocol->requestSemanticTokens(fullParams,
+ [&](auto res) {
+ QScopeGuard callAtExit(cleanup);
+ if (auto r = std::get_if<QLspSpecification::SemanticTokens>(&res)) {
+ params.previousResultId = r->resultId.value();
+ fullDocumentSemanticTokensData = r->data;
+ }
+ }, errorHandler);
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+
+ // Change the file
+ DidChangeTextDocumentParams didChange;
+ didChange.textDocument.uri = *uri;
+ didChange.textDocument.version = 2;
+
+ TextDocumentContentChangeEvent change;
+ change.range = Range{ Position{ 8, 4 }, Position{ 8, 4 } };
+ change.text = "const Patron = 42";
+
+ didChange.contentChanges.append(change);
+ m_protocol->notifyDidChangeTextDocument(didChange);
+
+ *didFinish = false;
+ params.textDocument.uri = *uri;
+ m_protocol->requestSemanticTokensDelta(params,
+ [&](auto res) {
+ QScopeGuard callAtExit(cleanup);
+ result = res;
+ }, std::move(errorHandler));
+ QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
+
+ if (const auto *const delta = std::get_if<QLspSpecification::SemanticTokensDelta>(&result)) {
+ QVERIFY(delta);
+ const auto data = delta->edits.front().data;
+ const auto start = delta->edits.front().start;
+ const auto deleteCount = delta->edits.front().deleteCount;
+ QCOMPARE(start, expectedEdits.front().start);
+ QCOMPARE(deleteCount, expectedEdits.front().deleteCount);
+ QCOMPARE(data, expectedEdits.front().data);
+ } else {
+ const auto *const full = std::get_if<QLspSpecification::SemanticTokens>(&result);
+ QVERIFY(full);
+ QCOMPARE(full->data, expectedEdits.front().data);
+ }
+}
+
+QTEST_MAIN(tst_qmlls_modules)
diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.h b/tests/auto/qmlls/modules/tst_qmlls_modules.h
new file mode 100644
index 0000000000..d7b601cf5a
--- /dev/null
+++ b/tests/auto/qmlls/modules/tst_qmlls_modules.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLSMODULES_H
+#define TST_QMLLSMODULES_H
+
+#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtCore/private/qduplicatetracker_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qstringlist.h>
+
+#include <QtTest/qtest.h>
+#include <QtQmlLS/private/qlspcustomtypes_p.h>
+
+#include <iostream>
+#include <variant>
+
+
+class tst_qmlls_modules : public QQmlDataTest
+{
+ using ExpectedCompletion = QPair<QString, QLspSpecification::CompletionItemKind>;
+ using ExpectedCompletions = QList<ExpectedCompletion>;
+
+ using ExpectedDocumentation = std::tuple<QString, QString, QString>;
+ using ExpectedDocumentations = QList<ExpectedDocumentation>;
+
+ Q_OBJECT
+public:
+ tst_qmlls_modules();
+ void checkCompletions(const QByteArray &filePath, int lineNr, int character,
+ ExpectedCompletions expected, QStringList notExpected);
+ std::optional<QByteArray> openFile(const QString &uri);
+ std::optional<QByteArray> openFileFromAbsolutePath(const QString &uri);
+ void ignoreDiagnostics();
+ void compareQTextDocumentEdit(const QLspSpecification::TextDocumentEdit &a,
+ const QLspSpecification::TextDocumentEdit &b);
+private slots:
+ void init() final;
+ void cleanup();
+ void initTestCase() final;
+ void function_documentations_data();
+ void function_documentations();
+ void buildDir();
+ void goToTypeDefinition_data();
+ void goToTypeDefinition();
+ void goToDefinition_data();
+ void goToDefinition();
+ void findUsages_data();
+ void findUsages();
+ void documentFormatting_data();
+ void documentFormatting();
+ void renameUsages_data();
+ void renameUsages();
+ void linting_data();
+ void linting();
+ void rangeFormatting_data();
+ void rangeFormatting();
+ void qmldirImports_data();
+ void qmldirImports();
+ void quickFixes_data();
+ void quickFixes();
+ void automaticSemicolonInsertionForCompletions_data();
+ void automaticSemicolonInsertionForCompletions();
+ void hover_data();
+ void hover();
+ void checkQuickSnippets();
+ void semanticHighlightingFull_data();
+ void semanticHighlightingFull();
+ void semanticHighlightingRange_data();
+ void semanticHighlightingRange();
+ void semanticHighlightingDelta_data();
+ void semanticHighlightingDelta();
+private:
+ QProcess m_server;
+ std::unique_ptr<QLanguageServerProtocol> m_protocol;
+ QString m_qmllsPath;
+ QList<QByteArray> m_uriToClose;
+};
+
+#endif // TST_QMLLSMODULES_H
diff --git a/tests/auto/qmlls/qmlls/CMakeLists.txt b/tests/auto/qmlls/qmlls/CMakeLists.txt
index e5f4bf822c..b3580c74e7 100644
--- a/tests/auto/qmlls/qmlls/CMakeLists.txt
+++ b/tests/auto/qmlls/qmlls/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
data)
@@ -25,3 +31,9 @@ qt_internal_extend_target(tst_qmlls CONDITION ANDROID OR IOS
DEFINES
QT_QMLTEST_DATADIR=":/domdata"
)
+
+if (TARGET qmlls)
+ # in a standalone build, the qmlls target won't exist
+ # but we assume that it is available if you manually set up the test
+ add_dependencies(tst_qmlls qmlls)
+endif()
diff --git a/tests/auto/qmlls/qmlls/tst_qmlls.cpp b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
index 49d2de0583..9e057992c4 100644
--- a/tests/auto/qmlls/qmlls/tst_qmlls.cpp
+++ b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -214,18 +214,17 @@ void tst_Qmlls::didOpenTextDocument()
QString title = QString::fromUtf8(action.title);
QVERIFY(action.kind.has_value());
- QCOMPARE(QString::fromUtf8(action.kind.value()),
- QLatin1StringView("refactor.rewrite"));
+ QCOMPARE(QString::fromUtf8(action.kind.value()), QLatin1StringView("quickfix"));
QVERIFY(action.edit.has_value());
WorkspaceEdit edit = action.edit.value();
QVERIFY(edit.documentChanges.has_value());
- auto docChangeVariant = edit.documentChanges.value();
- QVERIFY(std::holds_alternative<QList<TextDocumentEdit>>(docChangeVariant));
- auto documentChanges = std::get<QList<TextDocumentEdit>>(docChangeVariant);
+ auto documentChanges = edit.documentChanges.value();
QCOMPARE(documentChanges.size(), 1);
- TextDocumentEdit textDocEdit = documentChanges.first();
+ QVERIFY(std::holds_alternative<TextDocumentEdit>(documentChanges.first()));
+ TextDocumentEdit textDocEdit
+ = std::get<TextDocumentEdit>(documentChanges.first());
QCOMPARE(textDocEdit.textDocument.uri, textDocument.uri);
QVERIFY(std::holds_alternative<int>(textDocEdit.textDocument.version));
diff --git a/tests/auto/qmlls/qqmlcodemodel/CMakeLists.txt b/tests/auto/qmlls/qqmlcodemodel/CMakeLists.txt
new file mode 100644
index 0000000000..21b14a62a8
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls_qqmlcodemodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qmlls_qqmlcodemodel
+ SOURCES
+ tst_qmlls_qqmlcodemodel.cpp tst_qmlls_qqmlcodemodel.h
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+ DEFINES
+ QT_QQMLCODEMODEL_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
+
+qt_internal_extend_target(tst_qmlls_qqmlcodemodel CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QQMLCODEMODEL_DATADIR=":/data"
+)
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/Main.qml b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/Main.qml
new file mode 100644
index 0000000000..6406952c27
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/Main.qml
@@ -0,0 +1,5 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+
+HelloWorld { myP: 55; myPPP: 55 }
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/mycppmodule.qmltypes b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/mycppmodule.qmltypes
new file mode 100644
index 0000000000..8a2a1bc714
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/mycppmodule.qmltypes
@@ -0,0 +1,36 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by qmltyperegistrar.
+
+Module {
+ Component {
+ file: "helloworld.h"
+ name: "HelloWorld"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["MyCppModule/HelloWorld 1.0"]
+ exportMetaObjectRevisions: [256]
+ Property {
+ name: "myP"
+ type: "int"
+ read: "myP"
+ write: "setMyP"
+ notify: "myPChanged"
+ index: 0
+ isFinal: true
+ }
+ Property {
+ name: "myPPP"
+ type: "int"
+ read: "myP"
+ write: "setMyP"
+ notify: "myPChanged"
+ index: 1
+ isFinal: true
+ }
+ Signal { name: "myPChanged" }
+ }
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/qmldir b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/qmldir
new file mode 100644
index 0000000000..741dd5cbf8
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/MyCppModule/qmldir
@@ -0,0 +1,5 @@
+module MyCppModule
+typeinfo mycppmodule.qmltypes
+prefer :/MyCppModule/
+Main 1.0 Main.qml
+
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/Main.qml b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/Main.qml
new file mode 100644
index 0000000000..dbe18d54a6
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/Main.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import autoGenCMake
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ title: qsTr("Hello World")
+ HelloWorld { myP: 55; myPPP: 55 }
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.cpp
new file mode 100644
index 0000000000..445103c228
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.cpp
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "helloworld.h"
+
+HelloWorld::HelloWorld(QObject *parent)
+ : QObject{parent}
+{}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.h
new file mode 100644
index 0000000000..0a13344476
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/MyCppModule/helloworld.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef HELLOWORLD_H
+#define HELLOWORLD_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class HelloWorld : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int myP READ myP WRITE setMyP NOTIFY myPChanged FINAL)
+ Q_PROPERTY(int myPPP READ myP WRITE setMyP NOTIFY myPChanged FINAL)
+
+public:
+ explicit HelloWorld(QObject *parent = nullptr);
+
+ int myP() { return m_myP; }
+ void setMyP(int p) { m_myP = p; }
+private:
+ int m_myP;
+
+signals:
+ void myPChanged();
+};
+
+#endif // HELLOWORLD_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.cpp
new file mode 100644
index 0000000000..604e42f8b1
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "myqmlelement.h"
+
+MyQmlElement::MyQmlElement(QObject *parent)
+ : QObject{parent}
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.h
new file mode 100644
index 0000000000..dd4e2e2cc4
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MYQMLELEMENT_H
+#define MYQMLELEMENT_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class MyQmlElement : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit MyQmlElement(QObject *parent = nullptr);
+
+signals:
+
+};
+
+#endif // MYQMLELEMENT_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.qml b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.qml
new file mode 100644
index 0000000000..226927e792
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.cpp
new file mode 100644
index 0000000000..abeeb8f3b1
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "myqmlelement2.hpp"
+
+myQmlElement2::myQmlElement2(QObject *parent)
+ : QObject{parent}
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.hpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.hpp
new file mode 100644
index 0000000000..c7adf50e2e
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/myqmlelement2.hpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MYQMLELEMENT2_H
+#define MYQMLELEMENT2_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class myQmlElement2 : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit myQmlElement2(QObject *parent = nullptr);
+};
+
+#endif // MYQMLELEMENT2_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.cpp
new file mode 100644
index 0000000000..a3a73413eb
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.cpp
@@ -0,0 +1,9 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "somecppclass.h"
+
+SomeCppClass::SomeCppClass()
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.h
new file mode 100644
index 0000000000..06943515d8
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/somecppclass.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SOMECPPCLASS_H
+#define SOMECPPCLASS_H
+
+
+class SomeCppClass
+{
+public:
+ SomeCppClass();
+};
+
+#endif // SOMECPPCLASS_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.cpp
new file mode 100644
index 0000000000..9a89751eb8
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "anotherqmlelement.h"
+
+AnotherQmlElement::AnotherQmlElement(QObject *parent)
+ : QObject{parent}
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.h
new file mode 100644
index 0000000000..1ddba88c6b
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/anotherqmlelement.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef ANOTHERQMLELEMENT_H
+#define ANOTHERQMLELEMENT_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class AnotherQmlElement : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit AnotherQmlElement(QObject *parent = nullptr);
+};
+
+#endif // ANOTHERQMLELEMENT_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.cpp b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.cpp
new file mode 100644
index 0000000000..c448ea76f0
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "myqmlelementx.h"
+
+MyQmlElementX::MyQmlElementX(QObject *parent)
+ : QObject{parent}
+{
+
+}
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.h b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.h
new file mode 100644
index 0000000000..f32756c7b1
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MYQMLELEMENTX_H
+#define MYQMLELEMENTX_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class MyQmlElementX : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit MyQmlElementX(QObject *parent = nullptr);
+
+signals:
+
+};
+
+#endif // MYQMLELEMENTX_H
diff --git a/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.txt b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/data/sourceFolder/subSourceFolder/subsubSourceFolder/myqmlelement.txt
diff --git a/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.cpp b/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.cpp
new file mode 100644
index 0000000000..f56839d99a
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.cpp
@@ -0,0 +1,131 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_qqmlcodemodel.h"
+
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+#include <QtQmlLS/private/qqmlcodemodel_p.h>
+#include <QtQmlLS/private/qqmllsutils_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+tst_qmlls_qqmlcodemodel::tst_qmlls_qqmlcodemodel() : QQmlDataTest(QT_QQMLCODEMODEL_DATADIR) { }
+
+void tst_qmlls_qqmlcodemodel::buildPathsForFileUrl_data()
+{
+ QTest::addColumn<QString>("pathFromIniFile");
+ QTest::addColumn<QString>("pathFromEnvironmentVariable");
+ QTest::addColumn<QString>("pathFromCommandLine");
+ QTest::addColumn<QString>("expectedPath");
+
+ const QString path1 = u"/Users/helloWorld/build-myProject"_s;
+ const QString path2 = u"/Users/helloWorld/build-custom"_s;
+ const QString path3 = u"/Users/helloWorld/build-12345678"_s;
+
+ QTest::addRow("justCommandLine") << QString() << QString() << path1 << path1;
+ QTest::addRow("all3") << path1 << path2 << path3 << path3;
+
+ QTest::addRow("commandLineOverridesEnvironmentVariable")
+ << QString() << path2 << path3 << path3;
+ QTest::addRow("commandLineOverridesIniFile") << path2 << QString() << path3 << path3;
+
+ QTest::addRow("EnvironmentVariableOverridesIniFile") << path1 << path2 << QString() << path2;
+ QTest::addRow("iniFile") << path1 << QString() << QString() << path1;
+ QTest::addRow("environmentVariable") << QString() << path3 << QString() << path3;
+}
+
+void tst_qmlls_qqmlcodemodel::buildPathsForFileUrl()
+{
+ QFETCH(QString, pathFromIniFile);
+ QFETCH(QString, pathFromEnvironmentVariable);
+ QFETCH(QString, pathFromCommandLine);
+ QFETCH(QString, expectedPath);
+
+ QQmlToolingSettings settings(u"qmlls"_s);
+ if (!pathFromIniFile.isEmpty())
+ settings.addOption("buildDir", pathFromIniFile);
+
+ constexpr char environmentVariable[] = "QMLLS_BUILD_DIRS";
+ qunsetenv(environmentVariable);
+ if (!pathFromEnvironmentVariable.isEmpty()) {
+ qputenv(environmentVariable, pathFromEnvironmentVariable.toUtf8());
+ }
+
+ QmlLsp::QQmlCodeModel model(nullptr, &settings);
+ if (!pathFromCommandLine.isEmpty())
+ model.setBuildPathsForRootUrl(QByteArray(), QStringList{ pathFromCommandLine });
+
+ // use nonexistent path to avoid loading random .qmlls.ini files that might be laying around.
+ // in this case, it should abort the search and the standard value we set in the settings
+ const QByteArray nonExistentUrl =
+ QUrl::fromLocalFile(u"./___thispathdoesnotexist123___/abcdefghijklmnop"_s).toEncoded();
+
+ QStringList result = model.buildPathsForFileUrl(nonExistentUrl);
+ QCOMPARE(result.size(), 1);
+ QCOMPARE(result.front(), expectedPath);
+}
+
+void tst_qmlls_qqmlcodemodel::findFilePathsFromFileNames_data()
+{
+ QTest::addColumn<QStringList>("fileNames");
+ QTest::addColumn<QStringList>("expectedPaths");
+
+ const QString folder = testFile("sourceFolder");
+ const QString subfolder = testFile("sourceFolder/subSourceFolder/subsubSourceFolder");
+
+ QTest::addRow("notExistingFile") << QStringList{ u"notExistingFile.h"_s } << QStringList{};
+
+ QTest::addRow("myqmlelement") << QStringList{ u"myqmlelement.h"_s }
+ << QStringList{ folder + u"/myqmlelement.h"_s,
+ subfolder + u"/myqmlelement.h"_s };
+
+ QTest::addRow("myqmlelement2") << QStringList{ u"myqmlelement2.hpp"_s }
+ << QStringList{ folder + u"/myqmlelement2.hpp"_s };
+
+ QTest::addRow("anotherqmlelement") << QStringList{ u"anotherqmlelement.cpp"_s }
+ << QStringList{ subfolder + u"/anotherqmlelement.cpp"_s };
+}
+
+void tst_qmlls_qqmlcodemodel::findFilePathsFromFileNames()
+{
+ QFETCH(QStringList, fileNames);
+ QFETCH(QStringList, expectedPaths);
+
+ QmlLsp::QQmlCodeModel model;
+ model.setRootUrls({ testFileUrl(u"sourceFolder"_s).toEncoded() });
+
+ auto result = model.findFilePathsFromFileNames(fileNames);
+ // the order only is required for the QCOMPARE
+ std::sort(result.begin(), result.end());
+ std::sort(expectedPaths.begin(), expectedPaths.end());
+
+ QCOMPARE(result, expectedPaths);
+}
+
+using namespace QQmlJS::Dom;
+
+void tst_qmlls_qqmlcodemodel::fileNamesToWatch()
+{
+ DomItem qmlFile;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr = DomEnvironment::create(QStringList(),
+ DomEnvironment::Option::SingleThreaded
+ | DomEnvironment::Option::NoDependencies, options);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile("MyCppModule/Main.qml")),
+ [&qmlFile](Path, const DomItem &, const DomItem &newIt) {
+ qmlFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ const auto fileNames = QmlLsp::QQmlCodeModel::fileNamesToWatch(qmlFile);
+
+ // fileNames also contains some builtins it seems, like:
+ // QSet("qqmlcomponentattached_p.h", "qqmlcomponent.h", "qobject.h", "qqmllist.h",
+ // "helloworld.h", "qqmlengine_p.h")
+ QVERIFY(fileNames.contains(u"helloworld.h"_s));
+}
+
+QTEST_MAIN(tst_qmlls_qqmlcodemodel)
diff --git a/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.h b/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.h
new file mode 100644
index 0000000000..45c88d908e
--- /dev/null
+++ b/tests/auto/qmlls/qqmlcodemodel/tst_qmlls_qqmlcodemodel.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_QQMLCODEMODEL_H
+#define TST_QMLLS_QQMLCODEMODEL_H
+
+#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qlibraryinfo.h>
+
+#include <QtTest/qtest.h>
+
+#include <iostream>
+
+using namespace Qt::StringLiterals;
+
+class tst_qmlls_qqmlcodemodel : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qmlls_qqmlcodemodel();
+
+private slots:
+ void buildPathsForFileUrl_data();
+ void buildPathsForFileUrl();
+ void fileNamesToWatch();
+ void findFilePathsFromFileNames_data();
+ void findFilePathsFromFileNames();
+};
+
+#endif // TST_QMLLS_QQMLCODEMODEL_H
diff --git a/tests/auto/qmlls/utils/CMakeLists.txt b/tests/auto/qmlls/utils/CMakeLists.txt
new file mode 100644
index 0000000000..ca4a26b051
--- /dev/null
+++ b/tests/auto/qmlls/utils/CMakeLists.txt
@@ -0,0 +1,48 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlls_utils LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qmlls_utils
+ SOURCES
+ tst_qmlls_utils.cpp
+ DEFINES
+ QT_QMLLS_UTILS_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_add_test(tst_qmlls_highlighting
+ SOURCES
+ tst_qmlls_highlighting.cpp
+ DEFINES
+ QT_QMLLS_HIGHLIGHTS_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::LanguageServerPrivate
+ Qt::Test
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlLSPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qmlls_utils CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLLS_UTILS_DATADIR=":/domdata"
+)
diff --git a/tests/auto/qmlls/utils/data/BaseType.qml b/tests/auto/qmlls/utils/data/BaseType.qml
new file mode 100644
index 0000000000..168630f8ef
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/BaseType.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+ property string inBaseTypeDotQml: "Hello from BaseType!"
+
+ component MyBaseInlineComponent: Item {
+ id: baseIC
+ }
+
+ Item {
+ id: child
+
+ Item {
+ id: nestedChild
+
+ component MyNestedInlineComponent: Item {
+ property string inMyNestedInlineComponent: "world"
+ }
+ }
+ }
+ property int helloProperty: 123
+ function helloFunction() {
+ return helloProperty
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/JSDefinitions.qml b/tests/auto/qmlls/utils/data/JSDefinitions.qml
new file mode 100644
index 0000000000..20a1d34fe2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/JSDefinitions.qml
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+ id: rootId
+
+ property int i // (1)
+ function f(a /*(2)*/ , b) {return a /* go to definition on a leads to (2) */ > b} // (4)
+
+ Component.onCompleted: {
+ let x = 42 // (3)
+ f(x, i) // goto definition on f goes to 4, on x goes to (3) and on i goes to (1)
+ f(x, rootId.i) // goto definition on f goes to 4, on x goes to (3) and on i goes to (1)
+ }
+
+ function ffff() {
+ let scoped = 42;
+ {
+ let scoped = 666;
+ f(scoped, i);
+ {
+ let a = 12345, i = 32;
+ f(scoped, i);
+ }
+ }
+ f(scoped, i);
+ }
+
+ Rectangle {
+ id: nested
+
+ property int i
+
+ function f(n: int): int {
+ let x = i, y = nested.i, z = rootId.i;
+ if (x > 3)
+ return 1 + f(f(x-1) + f(x-2) - f(x-3));
+ else
+ return f(0);
+ }
+ function fff(n: int, m: int): int {
+ return f(n + m) / 42 + ffff()
+ }
+ }
+ function abc() {
+ return nested.f(42);
+ }
+
+ component MyIC: Rectangle {
+ id: helloIC
+
+ property int data: 42
+ Item {
+ property int data: helloIC.data
+ }
+ }
+
+ property MyIC ic: MyIC {}
+ function icProperty() {
+ return ic.data
+ }
+ property int propertyInBinding: i
+ property int propertyInBinding2: i * 42
+ property int propertyInBinding3: abc()[rootId.i ** 42 - 7]
+
+ property BaseType bt: BaseType {}
+ property int helloProperty: 1234567890 // BaseType also has a property helloProperty
+ function helloFunction() {} // BaseType also has a method helloFunction
+ function fromDifferentFiles() {
+ let x = bt.helloProperty + bt.helloFunction()
+ }
+
+ property BaseType.MyBaseInlineComponent inlineCompFromDifferentFile
+}
diff --git a/tests/auto/qmlls/utils/data/JSUsages.qml b/tests/auto/qmlls/utils/data/JSUsages.qml
new file mode 100644
index 0000000000..ce0cd1d046
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/JSUsages.qml
@@ -0,0 +1,196 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ }
+
+ property int helloProperty: 0
+ property int p2: 1
+
+ function withProperty() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ helloProperty = helloProperty + sum - i * p2;
+ {
+ let helloProperty = "evil"
+ }
+ }
+ }
+ Item {
+ function f() {
+ return helloProperty + p2
+ }
+ property string helloProperty
+ }
+ component IC: Item {
+ property var helloProperty
+ function f() {
+ return helloProperty + p2
+ }
+ }
+ component NestedComponent: Item {
+ property NestedComponent2 inner: NestedComponent2 {}
+ property int p2
+ }
+ component NestedComponent2: Item {
+ property NestedComponent3 inner
+ property int p2
+ inner: NestedComponent3 {}
+ }
+ component NestedComponent3: Item {
+ property NestedComponent4 inner
+ property int p2
+ inner: NestedComponent4 {}
+
+ }
+ component NestedComponent4: Item {
+ property int helloProperty
+ property int p2
+ }
+ NestedComponent {
+ id: myNested
+ }
+ function nestedUsages() {
+ let x = myNested.inner.inner.inner.helloProperty + helloProperty;
+ let a = myNested.p2 + p2
+ let b = myNested.inner.p2 + p2
+ let c = myNested.inner.inner.p2 + p2
+ let d = myNested.inner.inner.inner.p2 + p2
+ }
+
+ function recursive(n: int): int {
+ if (n > 3)
+ return 1 + recursive(recursive(x-1) + recursive(x-2) - recursive(x-3));
+ else
+ return recursive(0);
+ }
+
+ property int helloRecursive: recursive(42)
+
+ id: rootId
+ Rectangle {
+ function f() {
+ return rootId.recursive(123)
+ }
+ }
+
+ signal helloSignal()
+
+ function callSignals() {
+ helloSignal()
+ if (false) {
+ helloSignal()
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ helloSignal()
+ }
+ }
+ function callSignals2() {
+ helloSignal()
+ if (false) {
+ widthChanged()
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ widthChanged()
+ rootId.widthChanged()
+ }
+ }
+ Item {
+ function callSignalsInChild() {
+ widthChanged()
+ rootId.widthChanged()
+ }
+ }
+
+ function myHelloHandler() { let x = 32; }
+ onHelloSignal: myHelloHandler
+
+ property int helloPropertyBinding
+ helloPropertyBinding: 123
+
+ property int checkHandlers
+ onCheckHandlersChanged: myHelloHandler
+ onChildrenChanged: myHelloHandler
+ function callChanged() {
+ checkHandlersChanged()
+ childrenChanged()
+ }
+ property int _: 48
+ property int ______42: 48
+ property int _123a: 48
+ on_Changed: myHelloHandler
+ on______42Changed: myHelloHandler
+ on_123AChanged: myHelloHandler
+ function weirdPropertynames() {
+ _Changed()
+ ______42Changed()
+ _123aChanged()
+ }
+
+ TapHandler {
+ onTapped: myHelloHandler
+ function f() {
+ tapped()
+ }
+ }
+
+ function anotherF() {
+ helloPropertyChanged()
+ }
+ onHelloPropertyChanged: myHelloHandler
+ Type {}
+ function foo(mouse) {}
+
+ MouseArea {
+ id: area1
+ onClicked: foo
+ property int insideMouseArea1
+ }
+
+ MouseArea {
+ id: area2
+ Connections {
+ function onClicked(mouse) {
+ area1.clicked()
+ area3.clicked()
+ }
+ }
+ property int insideMouseArea2
+
+ MouseArea {id: area3}
+ }
+
+ property Connections c: Connections {
+ target: area3
+ onClicked: function(mouse) {
+ //
+ }
+ }
+ function useMouseAreas() {
+ area1.clicked()
+ area2.clicked()
+ area3.clicked()
+ }
+
+ function checkParameters(a: int, b: double, {x, y={}, z=[x,y]}) {
+ return a + b + c + x + y + z
+ }
+
+ function deconstructingUsages(xxx) {
+ let {a, b} = xxx;
+ let c = a + b;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/JSUsagesFromAnotherFile.qml b/tests/auto/qmlls/utils/data/JSUsagesFromAnotherFile.qml
new file mode 100644
index 0000000000..eb0cbf70be
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/JSUsagesFromAnotherFile.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+JSUsages {
+ function f() {
+ // sanity check: reuse variable names from function f in JSUsages. Those should not appear
+ // as usages of the sum of JSUsages.
+ let sum = 0;
+ sum += 1;
+ sum += helloProperty + 32 // valid usage of JSUsages's helloProperty
+ return sum
+ }
+
+}
diff --git a/tests/auto/qmlls/utils/data/Type.qml b/tests/auto/qmlls/utils/data/Type.qml
new file mode 100644
index 0000000000..2d0a32cfb4
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/Type.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+BaseType {
+ id: derived
+ property int inTypeDotQml
+
+ component MyInlineComponent: BaseType {
+ id: derivedInIC
+ property int inMyInlineComponent
+ }
+
+ property BaseType inlineType: BaseType {
+ id: derivedInline
+ }
+
+ property MyInlineComponent icType: MyInlineComponent {
+ id:derivedInIcInline
+ }
+
+ property var icType2: BaseType.MyBaseInlineComponent {
+ id:derivedInIcInline2
+ }
+
+ property var nestedIcType: BaseType.MyNestedInlineComponent {
+ id:derivedInIcInline3
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/Yyy.qml b/tests/auto/qmlls/utils/data/Yyy.qml
new file mode 100644
index 0000000000..6c3886c0a4
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/Yyy.qml
@@ -0,0 +1,143 @@
+import QtQuick 2.0
+import QtQuick as QQ
+import QtCore
+Zzz {
+ id: root
+ width: height
+ Rectangle {
+ color: "green"
+ anchors.fill: parent
+ width: root.height
+ height: root.foo.height
+
+ }
+ component MyRectangle: Rectangle {}
+ function lala() {}
+ property Rectangle foo: Rectangle{ height: 200 }
+ function longfunction(a, b, c = "c", d = "d"): string {
+ return "hehe: " + c + d
+ }
+
+ // documentedFunction: is documented
+ // returns 'Good'
+ function documentedFunction(arg1, arg2 = "Qt"): string {
+ return "Good"
+ }
+ QQ.Rectangle {
+ color:"red"
+ }
+
+ Item {
+ id: someItem
+ property int helloProperty
+ }
+
+ function parameterCompletion(helloWorld, helloMe: int) {
+ let helloVar = 42;
+ let result = someItem.helloProperty + helloWorld;
+ return result;
+ }
+
+ component Base: QtObject {
+ property int propertyInBase
+ function functionInBase(jsParameterInBase) {
+ let jsIdentifierInBase;
+ return jsIdentifierInBase;
+ }
+ }
+
+ Base {
+ property int propertyInDerived
+ function functionInDerived(jsParameterInDerived) {
+ let jsIdentifierInDerived;
+ return jsIdentifierInDerived;
+ }
+
+ property Base child: Base {
+ property int propertyInChild
+ function functionInChild(jsParameterInChild) {
+ let jsIdentifierInChild;
+ return someItem.helloProperty;
+ }
+ }
+ }
+ function test1() {
+ {
+ var helloVarVariable = 42;
+ }
+ // this is fine, var has no block scope
+ console.log(helloVarVariable);
+ }
+ function test2() {
+ {
+ let helloLetVariable = 42;
+ }
+ // this is not fine, let variables have block scope
+ console.log(helloLetVariable);
+ }
+ property var testSingleton: SystemInformation.byteOrder
+
+
+
+ enum Hello { World }
+ enum MyEnum { ValueOne, ValueTwo }
+
+
+ property var testEnums: Yyy.World
+ property var testEnums2: Yyy.Hello.World
+
+ Component.onCompleted: {}
+ property var anything: Rectangle{ height: 200 }
+ function createRectangle(): Rectangle {}
+ function createItem(): Item {}
+ function createAnything() {}
+ function helloJSStatements() {
+ let x = 3;
+ }
+ required property int requiredProperty
+ readonly property int readonlyProperty: 456
+ default property int defaultProperty
+ property int builtin: Math.abs(43)
+ signal handleMe()
+ function helloForStatement() {
+ for(let i = 0; i < 5; ++i) {
+
+ }
+ for(let j = 0; j < 5; ++j)
+ helloForStatement()
+ }
+ function helloIfStatement(hello) {
+ if (hello)
+ hello = !hello
+ else
+ hello = hello
+ if (hello == !hello) {
+ hello = hello / hello
+ } else {
+ hello += hello
+ }
+ if (hello)
+ hello = hello
+ else if (hello)
+ hello = hello + hello / 2
+ }
+ function helloReturnStatement(hello) {
+ return hello
+ }
+ function helloWhileStatement(hello) {
+ while (hello) --hello
+ }
+ function helloDoWhileStatement(hello) {
+ do --hello; while (hello);
+ }
+ function helloForEachStatement(hello) {
+ for(variable in hello) ++hello;
+ for(element of hello) ++hello;
+ }
+ function qualifiedScriptIdentifiers() {
+ console.l()
+ }
+ QtObject {
+
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/Zzz.qml b/tests/auto/qmlls/utils/data/Zzz.qml
new file mode 100644
index 0000000000..fa0edf69dc
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/Zzz.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.11
+
+Item {
+ id: zzz
+ height: 333
+
+ Rectangle {
+ width: zzz.height
+ }
+
+ property int propertyOfZZZ
+}
diff --git a/tests/auto/qmlls/utils/data/completions/afterDots.qml b/tests/auto/qmlls/utils/data/completions/afterDots.qml
new file mode 100644
index 0000000000..0659425195
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/afterDots.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+QtObject {
+ id: root
+ property int good
+ property var i: Item {
+ property int bad
+ property int myP: root.
+ Item { }
+ property int myP2: root.;
+ bad: 43
+ function f() {
+ root.;
+ for (;;) {}
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/attachedAndGroupedProperty.qml b/tests/auto/qmlls/utils/data/completions/attachedAndGroupedProperty.qml
new file mode 100644
index 0000000000..5736938d73
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/attachedAndGroupedProperty.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int bad
+ Component.a: {}
+ Text {
+ font.f: ""
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml b/tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml
new file mode 100644
index 0000000000..38aca7ef7a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ Component.on
+}
diff --git a/tests/auto/qmlls/utils/data/completions/boundComponents.qml b/tests/auto/qmlls/utils/data/completions/boundComponents.qml
new file mode 100644
index 0000000000..343e7a3c7b
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/boundComponents.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ property int inRoot
+
+ DelegateModel {
+ delegate: Item {
+ id: childId
+
+ property int myInt: rootId.inRoot
+ property int inChild
+
+ }
+ }
+
+ property int myInt: childId.inChild
+}
diff --git a/tests/auto/qmlls/utils/data/completions/commaExpression.qml b/tests/auto/qmlls/utils/data/completions/commaExpression.qml
new file mode 100644
index 0000000000..de74d07c1d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/commaExpression.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ function f(a,b,c) {
+ f(a,a,a), b += 55,c *= 24;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/conditionalExpression.qml b/tests/auto/qmlls/utils/data/completions/conditionalExpression.qml
new file mode 100644
index 0000000000..933c71d26c
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/conditionalExpression.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ function f(a,b,c) {
+ a == b ? b == c ? c : b + 3 : 42
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/continueAndBreakStatement.qml b/tests/auto/qmlls/utils/data/completions/continueAndBreakStatement.qml
new file mode 100644
index 0000000000..243f206db9
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/continueAndBreakStatement.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ label1: f(f(x))
+
+ nestedLabel1: for (let i = 0; i < 3; ++i) {
+ nestedLabel2: for (let j = 0; j < 3; ++j) {
+ continue nestedLabel1;
+ break nestedLabel2;
+ }
+ }
+
+ multiLabel1:
+ multiLabel2: {
+ f(1 + f(x))
+ continue multiLabel1
+ break multiLabel2
+ }
+
+ for(;;) {
+ continue ;
+ break ;
+ }
+
+ return x + y
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/functionBody.qml b/tests/auto/qmlls/utils/data/completions/functionBody.qml
new file mode 100644
index 0000000000..755f136d7d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/functionBody.qml
@@ -0,0 +1,13 @@
+import QtQuick
+
+Item {
+ function f(x) {
+
+ }
+
+ property int badProperty
+ component IC: Item { property int helloProperty }
+ function g(x: IC) {
+ x.helloProperty
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml b/tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml
new file mode 100644
index 0000000000..44fbd022ba
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ font.fa
+}
diff --git a/tests/auto/qmlls/utils/data/completions/labelledStatement.qml b/tests/auto/qmlls/utils/data/completions/labelledStatement.qml
new file mode 100644
index 0000000000..e223d6465b
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/labelledStatement.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+Item {
+ function f(x) {
+ label1: f(f(x))
+
+ nestedLabel1: for (let i = 0; i < 3; ++i) {
+ nestedLabel2: for (let j = 0; j < 3; ++j) {
+ if (i === 1 && j === 1) {
+ continue nestedLabel1;
+ }
+ }
+ }
+
+ multilabel1:
+ multilabel2: f(1 + f(x))
+
+ return x + y
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml b/tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml
new file mode 100644
index 0000000000..ce594d9f16
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ function f() {
+ let x = root.
+ let y = root.
+ for(;;) {}
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/missingRHS.qml b/tests/auto/qmlls/utils/data/completions/missingRHS.qml
new file mode 100644
index 0000000000..1423f5c17e
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/missingRHS.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+ import QtQuick
+
+ Item {
+ id: root
+ property int good
+ Item {
+ property int bad
+ function f() {
+ return root.
+ }
+ property int boom: root.
+ Item {
+ property int helloSubItem
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/parenthesizedExpression.qml b/tests/auto/qmlls/utils/data/completions/parenthesizedExpression.qml
new file mode 100644
index 0000000000..567b543154
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/parenthesizedExpression.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ (x + 1)
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml b/tests/auto/qmlls/utils/data/completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml
new file mode 100644
index 0000000000..50bc0976c8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Templates as T
+
+Item {
+ T.Button {}
+ function f(x: T.Button) {}
+}
diff --git a/tests/auto/qmlls/utils/data/completions/returnStatement.qml b/tests/auto/qmlls/utils/data/completions/returnStatement.qml
new file mode 100644
index 0000000000..e5203652b7
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/returnStatement.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ return
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/suggestContinueAndBreak.qml b/tests/auto/qmlls/utils/data/completions/suggestContinueAndBreak.qml
new file mode 100644
index 0000000000..89855e1584
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/suggestContinueAndBreak.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ // sanity check: no break or continue allowed in function body
+
+ for(let i = 0; i < 5; ++i) {
+ // break and continue allowed in loop
+ }
+
+ switch(x) {
+ // no break allowed here
+ case 3:
+ // break allowed in case
+ default:
+ // break allowed in default
+ case f("helloWorld"):
+ // break allowed in moreCase
+ }
+
+ helloLabel: {
+ // break allowed in labelledstatement
+ }
+
+ // combinations:
+ combiLabel: {
+ // break allowed in labelledstatement
+ for(let i = 0; i < 5; ++i) {
+ // break and continue allowed in loop
+
+ switch(x) {
+ default:
+ // break allowed in default + continue for loop
+ }
+ }
+
+ switch(x) {
+ case 3:
+ default:
+ case f("helloWorld"):
+ // no continue allowed here
+
+ for(let i = 0; i < 5; ++i) {
+ // break and continue allowed in loop
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/tests/auto/qmlls/utils/data/completions/switchStatements.qml b/tests/auto/qmlls/utils/data/completions/switchStatements.qml
new file mode 100644
index 0000000000..f9ddf3f2a1
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/switchStatements.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int myProperty;
+ function g(x) { return x + 1; }
+ function f(x) {
+ switch(x) {
+
+ case 1:
+ return 0;
+ case g(x) + 3: {
+ return 1;
+ }
+ }
+
+ switch(x) {
+ case 42:
+ myProperty = x + f(x)
+ myProperty = myProperty * 0.33
+ default:
+ return 123456
+ case 666:
+ for(;;) {
+ g(x)
+ }
+ myProperty = "hello"
+ }
+ switch(x) {
+ default:
+ break;
+ }
+ }
+
+}
diff --git a/tests/auto/qmlls/utils/data/completions/throwStatement.qml b/tests/auto/qmlls/utils/data/completions/throwStatement.qml
new file mode 100644
index 0000000000..927c885f54
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/throwStatement.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f(x) {
+ throw 1;
+ throw { x: "myError" };
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/tryStatements.qml b/tests/auto/qmlls/utils/data/completions/tryStatements.qml
new file mode 100644
index 0000000000..5fb261a388
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/tryStatements.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ function f() {
+ try { } catch(x) { } finally { }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/unaryExpression.qml b/tests/auto/qmlls/utils/data/completions/unaryExpression.qml
new file mode 100644
index 0000000000..f2ba099a7a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/unaryExpression.qml
@@ -0,0 +1,17 @@
+import QtQuick
+
+Item {
+ function f(x) {
+ -x;
+ +x;
+ ~x;
+ !x;
+ typeof x;
+ delete x;
+ void x;
+ x--;
+ x++;
+ --x;
+ ++x;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/completions/variableDeclaration.qml b/tests/auto/qmlls/utils/data/completions/variableDeclaration.qml
new file mode 100644
index 0000000000..2a32c2ff44
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/completions/variableDeclaration.qml
@@ -0,0 +1,36 @@
+import QtQuick
+
+Item {
+ function data() { return 42; }
+
+ function f(x) {
+ let letStatement = data();
+ const constStatement = data();
+ var varStatement = data(); // bad?
+ }
+
+ function objects(x) {
+ let { deconstructed } = data();
+ let { deconstructedAloneWithInitializer = 55 } = data();
+ let { deconstructedWithInitializer1 = 55, deconstructedWithInitializer2 = 66 } = data(), { unused: deconstructedWithInitializer3 = 77, } = data() ;
+ }
+
+ function arrays(x) {
+ let [ deconstructed ] = data();
+ let [ deconstructedAloneWithInitializer = 55 ] = data();
+ let [ deconstructedWithInitializer1 = 55, deconstructedWithInitializer2 = 66 ] = data(), [ deconstructedWithInitializer3 = 77, ] = data() ;
+ }
+
+ function oneArrayingToRuleThemAll(x) {
+ let [ head, [headOfSecond = 44, secondOfSecond = 55], [_ = "useless", secondOfThird = g()], [ [ headOfHeadOfFourth = g() + 1] ] ] = data();
+ }
+
+ function needleInTheHarraystack(x) {
+ let [ head, [headOfSecond = 44, secondOfSecond = 55], [_ = "useless", secondOfThird = g(), { needle = "x" }], [ [ headOfHeadOfFourth = g() + 1] ] ] = data();
+ }
+
+ function arrayInTheObject(x) {
+ let { p: [first, second] } = { p: [1,2] };
+ }
+
+}
diff --git a/tests/auto/qmlls/utils/data/emptyFile.qml b/tests/auto/qmlls/utils/data/emptyFile.qml
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/emptyFile.qml
diff --git a/tests/auto/qmlls/utils/data/file1.qml b/tests/auto/qmlls/utils/data/file1.qml
new file mode 100644
index 0000000000..c069a62a43
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/file1.qml
@@ -0,0 +1,55 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Item {
+ component C: Item {}
+
+ property int a
+ property bool b
+ property C c
+ property var d
+ property list<int> e
+
+ component D: Item { id: icid }
+ C {id: firstC }D{id: firstD }
+ C { id: secondC } D{ id: secondD}
+ C {
+ C{}
+ C{
+ C {}
+ C {}
+ C {}
+ }
+ C{}
+ }
+
+ component IC: Item { property C myC }
+
+ a: 43
+ d: 123 + 7
+
+ function f(a: int, b: Item, c: C) : C {
+ return c;
+ }
+ function lala() {}
+
+ Rectangle {
+ color: "green"
+ anchors.fill: parent
+ width: root.height
+ height: root.foo.height
+ }
+ property Rectangle foo: Rectangle{ height: 200 }
+
+ c: C{}
+ property C ccc: c
+ property C cccc
+ cccc: c
+
+ function fff(a: int, b: Item, c: C) : C {
+ let x = c.children;
+ return x;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/attachedPropertyUsage/attachedPropertyUsage.qml b/tests/auto/qmlls/utils/data/findUsages/attachedPropertyUsage/attachedPropertyUsage.qml
new file mode 100644
index 0000000000..897d153e84
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/attachedPropertyUsage/attachedPropertyUsage.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: root
+
+ Keys.onPressed: {}
+
+ MouseArea {
+ onClicked: root.Keys.enabled = false
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/binding/binding.qml b/tests/auto/qmlls/utils/data/findUsages/binding/binding.qml
new file mode 100644
index 0000000000..c0a06dd6fd
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/binding/binding.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ id: textRoot
+
+ property int helloPropertyBinding
+ helloPropertyBinding: 123
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/bindings/bindings.qml b/tests/auto/qmlls/utils/data/findUsages/bindings/bindings.qml
new file mode 100644
index 0000000000..f4d4c74aff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/bindings/bindings.qml
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+
+Item {
+ id: root
+ property bool patronChanged // shouldn't be found
+
+ Item {
+ id: inner
+ property bool patronChanged
+
+ Binding on patronChanged {
+ value: !inner.patronChanged
+ when: root.patronChanged // // shouldn't be found
+ }
+
+ Binding {
+ target: inner
+ property: "patronChanged"
+ value: !inner.patronChanged
+ when: root.patronChanged // shouldn't be found
+ }
+
+ // generalized dot
+ Binding {
+ inner.patronChanged: !inner.patronChanged
+ when: root.patronChanged // // shouldn't be found
+ }
+
+ // generalized block
+ Binding {
+ inner {
+ patronChanged: false
+ }
+ when: root.patronChanged // // shouldn't be found
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/connections/connections.qml b/tests/auto/qmlls/utils/data/findUsages/connections/connections.qml
new file mode 100644
index 0000000000..c933faca09
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/connections/connections.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+Item {
+
+ MouseArea {
+ id: area1
+ onClicked: foo
+ property int insideMouseArea1
+ }
+
+ MouseArea {
+ id: area2
+ Connections {
+ function onClicked(mouse) {
+ area1.clicked()
+ area3.clicked()
+ }
+ }
+ property int insideMouseArea2
+
+ MouseArea {id: area3}
+ }
+
+ property Connections c: Connections {
+ target: area3
+ onClicked: function(mouse) {
+ //
+ }
+ }
+ function useMouseAreas() {
+ area1.clicked()
+ area2.clicked()
+ area3.clicked()
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/enums/Enums.qml b/tests/auto/qmlls/utils/data/findUsages/enums/Enums.qml
new file mode 100644
index 0000000000..a60bb38053
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/enums/Enums.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+
+ enum Cats {
+ Patron = 8,
+ Mafik = 7,
+ Kivrik = 2
+ }
+
+ property var inner: QtObject {
+ enum Cats {
+ Patron = -8, // Shouldn't be found
+ Mafik = -7,
+ Kivrik = -2
+ }
+ }
+
+ property int main: Enums.Cats.Patron
+ property int innerVal: Enums.Patron
+ property int illegal1: Cats.Patron // Shouldn't be found
+ property int illegal2: Patron // Shouldn't be found
+ property int alien: EnumsFromAnotherFile.FromAnotherUniverse
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/enums/EnumsFromAnotherFile.qml b/tests/auto/qmlls/utils/data/findUsages/enums/EnumsFromAnotherFile.qml
new file mode 100644
index 0000000000..9f5bf491fd
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/enums/EnumsFromAnotherFile.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+ enum FromAnotherFile { FromAnotherWorld, FromAnotherDimension, FromAnotherUniverse }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/fontFamilyUsage.qml b/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/fontFamilyUsage.qml
new file mode 100644
index 0000000000..61880d13ff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/fontFamilyUsage.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property font myFont
+ property var family: myFont.family
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/groupPropertyUsage.qml b/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/groupPropertyUsage.qml
new file mode 100644
index 0000000000..167ccbb30e
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/groupPropertyUsage/groupPropertyUsage.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ id: textRoot
+
+ Test {
+ id: test
+ myText {
+ font {
+ pixelSize: 12
+ family: "serif"
+ }
+ }
+ }
+
+ component Test : Text{
+ property Text myText
+ }
+
+ font.family: test.myText.font.family
+ font {
+ pixelSize: 12
+ }
+
+ Item {
+ property var family // should not be in groupPropertyUsages1
+ property int font // should not be in groupPropertyUsages2
+
+ property var realFont: textRoot.font // should be in groupPropertyUsages2
+ property var realFamily: textRoot.font.family // should be in groupPropertyUsages1
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/idUsages/idUsages.qml b/tests/auto/qmlls/utils/data/findUsages/idUsages/idUsages.qml
new file mode 100644
index 0000000000..14506b2202
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/idUsages/idUsages.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ Rectangle {
+ function f() {
+ if (widthChanged())
+ rootId.widthChanged();
+ return rootId.x
+ }
+ }
+
+ function t() {
+ rootId.widthChanged();
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/inlineComponents/InlineComponentProvider.qml b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/InlineComponentProvider.qml
new file mode 100644
index 0000000000..bfe8d99020
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/InlineComponentProvider.qml
@@ -0,0 +1,23 @@
+import QtQuick
+
+Item {
+ component IC1: Item { property int inIc1: 123 }
+ component IC2: Item { property IC1 inIc2 }
+
+ IC1 {
+ id: firstUsage
+ property int inFirstUsage
+ }
+ IC2 {
+ id: secondUsage
+ property int inSecondUsage
+ }
+ Item {
+ Item {
+ IC1 {
+ id: thirdUsage
+ property int inThirdUsage
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents.qml b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents.qml
new file mode 100644
index 0000000000..6631dc3448
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+
+ component Patron: QtObject {
+ property int foo
+ Component.onCompleted: console.log(foo)
+ }
+
+ component Mafik: Patron {
+ property int bar: foo
+ }
+
+ property int foo // should not be in inlineUsages
+ property var realFoo: Mafik {
+ function f() {
+ return foo; // should be in inlineUsages
+ }
+ }
+
+ property InlineComponentProvider fromAnotherFile: InlineComponentProvider {}
+ property InlineComponentProvider.IC1 fromAnotherFile2: InlineComponentProvider.IC1 {}
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents2.qml b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents2.qml
new file mode 100644
index 0000000000..49430d4b7d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/inlineComponents/inlineComponents2.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ component MyIC: Item {}
+ MyIC { MyIC{} MyIC {}}
+ function f(x: MyIC): MyIC {
+ return x;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/jsIdentifier/jsIdentifier.qml b/tests/auto/qmlls/utils/data/findUsages/jsIdentifier/jsIdentifier.qml
new file mode 100644
index 0000000000..9d844eab3b
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/jsIdentifier/jsIdentifier.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml b/tests/auto/qmlls/utils/data/findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml
new file mode 100644
index 0000000000..566b8648bb
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+
+ function checkParameters(a: int, b: double, {x, y={}, z=[x,y]}) {
+ return a + b + c + x + y + z
+ }
+
+ function deconstructingUsages(xxx) {
+ let {a, b} = xxx;
+ let c = a + b;
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/property/PropertyFromAnotherFile.qml b/tests/auto/qmlls/utils/data/findUsages/property/PropertyFromAnotherFile.qml
new file mode 100644
index 0000000000..b9197def63
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/property/PropertyFromAnotherFile.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/property/property.qml b/tests/auto/qmlls/utils/data/findUsages/property/property.qml
new file mode 100644
index 0000000000..25280c31f1
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/property/property.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int p2: 1
+ property int helloProperty: 0
+ function withProperty() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = sum + i
+ helloProperty = helloProperty + sum - i * p2;
+ {
+ let helloProperty = "evil"
+ }
+ }
+ }
+ function signalEmitter() {
+ helloProperty = 23;
+ helloPropertyChanged()
+ }
+ onHelloPropertyChanged: {}
+
+ // inline component
+ component IC: Item {
+ property var helloProperty
+ function f() {
+ return helloProperty + p2
+ }
+ }
+
+ //sub item
+ Item {
+ function f() {
+ return helloProperty + p2
+ }
+ property string helloProperty
+ }
+
+ PropertyFromAnotherFile {
+ helloProperty: 42
+ function f() {
+ return helloProperty + 53;
+ }
+ onHelloPropertyChanged: f()
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyChanges/propertyChanges.qml b/tests/auto/qmlls/utils/data/findUsages/propertyChanges/propertyChanges.qml
new file mode 100644
index 0000000000..255f1c7ede
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyChanges/propertyChanges.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+Item {
+ MouseArea {
+ id: mouse11
+ onClicked: doSomething()
+ property rect r
+ }
+
+ states: [
+ State {
+ PropertyChanges {
+ mouse11 { // block notation
+ onClicked: doSomethingElse()
+ r : 34
+ }
+ mouse11.onClicked: doSomething(); // dot notation
+ }
+
+ // with target property
+ PropertyChanges {
+ target: mouse11
+ onClicked: doSomethingElse()
+ r: 45
+ }
+ }
+ ]
+ function doSomething() {}
+ function doSomethingElse() {}
+
+ property rect r // shouldn't be found
+ property int clicked // shouldn't be gound
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile.qml
new file mode 100644
index 0000000000..c11802ffc2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property NestedComponentInFile2 inner
+ property int p2
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile2.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile2.qml
new file mode 100644
index 0000000000..ece2913758
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile2.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property NestedComponentInFile3 inner
+ property int p2
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile3.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile3.qml
new file mode 100644
index 0000000000..4cddcb5cfe
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile3.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property NestedComponentInFile4 inner
+ property int p2
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile4.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile4.qml
new file mode 100644
index 0000000000..2dee832ff8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/NestedComponentInFile4.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+ property int p2
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/propertyInNested/propertyInNested.qml b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/propertyInNested.qml
new file mode 100644
index 0000000000..0eb493886a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/propertyInNested/propertyInNested.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int p2: 1
+
+ component NestedComponent: Item {
+ property NestedComponent2 inner: NestedComponent2 {}
+ property int p2
+ }
+ component NestedComponent2: Item {
+ property NestedComponent3 inner
+ property int p2
+ inner: NestedComponent3 {}
+ }
+ component NestedComponent3: Item {
+ property NestedComponent4 inner
+ property int p2
+ inner: NestedComponent4 {}
+
+ }
+ component NestedComponent4: Item {
+ property int helloProperty
+ property int p2
+ }
+ NestedComponent {
+ id: myNested
+ }
+ function nestedUsages() {
+ let x = myNested.inner.inner.inner.helloProperty;
+ let a = myNested.p2 + p2
+ let b = myNested.inner.p2 + p2
+ let c = myNested.inner.inner.p2 + p2
+ let d = myNested.inner.inner.inner.p2 + p2
+ }
+
+ function f() {
+ {
+ let _p2 = 34;
+ return _p2 + p2
+ }
+ }
+
+ NestedComponentInFile {
+ id: myNestedInFile
+ }
+ function nestedUsagesInFile() {
+ let x = myNestedInFile.inner.inner.inner.helloProperty;
+ let a = myNestedInFile.p2
+ let b = myNestedInFile.inner.p2
+ let c = myNestedInFile.inner.inner.p2
+ let d = myNestedInFile.inner.inner.inner.p2
+ }
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/findUsages/recursive/RecursiveInOtherFile.qml b/tests/auto/qmlls/utils/data/findUsages/recursive/RecursiveInOtherFile.qml
new file mode 100644
index 0000000000..8a7f2d04ff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/recursive/RecursiveInOtherFile.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ function recursive(n: int): int {
+ if (n > 3)
+ return 1 + recursive(recursive(x-1) + recursive(x-2) - recursive(x-3));
+ else
+ return recursive(0);
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/recursive/recursive.qml b/tests/auto/qmlls/utils/data/findUsages/recursive/recursive.qml
new file mode 100644
index 0000000000..45939bc89a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/recursive/recursive.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ function recursive(n: int): int {
+ if (n > 3)
+ return 1 + recursive(recursive(x-1) + recursive(x-2) - recursive(x-3));
+ else
+ return recursive(0);
+ }
+
+ property int helloRecursive: recursive(42)
+
+ Rectangle {
+ function f() {
+ return rootId.recursive(123)
+ }
+ }
+
+ RecursiveInOtherFile {
+ id: fromOtherFile
+ }
+
+ property int helloRecursiveFromOtherFile: fromOtherFile.recursive(42)
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalAndHandlers2.qml b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalAndHandlers2.qml
new file mode 100644
index 0000000000..d10768dd42
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalAndHandlers2.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function myHelloHandler() { let x = 32; }
+ onHelloSignal: myHelloHandler
+
+ property int helloPropertyBinding
+ helloPropertyBinding: 123
+
+ property int checkHandlers
+ onCheckHandlersChanged: myHelloHandler
+ onChildrenChanged: myHelloHandler
+ function callChanged() {
+ checkHandlersChanged()
+ childrenChanged()
+ }
+ property int _: 48
+ property int ______42: 48
+ property int _123a: 48
+ on_Changed: myHelloHandler
+ on______42Changed: myHelloHandler
+ on_123AChanged: myHelloHandler
+ function weirdPropertynames() {
+ _Changed()
+ ______42Changed()
+ _123aChanged()
+ }
+
+ TapHandler {
+ onTapped: myHelloHandler
+ function f() {
+ tapped()
+ }
+ }
+
+ function anotherF() {
+ helloPropertyChanged()
+ }
+ onHelloPropertyChanged: myHelloHandler
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalsAndHandlers.qml b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalsAndHandlers.qml
new file mode 100644
index 0000000000..47280bca46
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/signalsAndHandlers.qml
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: rootId
+ signal helloSignal()
+
+ function callSignals() {
+ helloSignal()
+ if (false) {
+ helloSignal()
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ helloSignal()
+ }
+ }
+ function callSignals2() {
+ helloSignal()
+ if (false) {
+ widthChanged()
+ } else {
+ // helloSignal() // not a usage btw
+ if (true)
+ widthChanged()
+ rootId.widthChanged()
+ }
+ }
+ Item {
+ function callSignalsInChild() {
+ widthChanged()
+ rootId.widthChanged()
+ }
+ }
+
+ function myHelloHandler() { let x = 32; }
+ onHelloSignal: myHelloHandler
+}
diff --git a/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml
new file mode 100644
index 0000000000..aecee2437a
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ function f() {}
+ onWidthChanged: f
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/Identifiers.qml b/tests/auto/qmlls/utils/data/highlights/Identifiers.qml
new file mode 100644
index 0000000000..7725b6d5e4
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/Identifiers.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ readonly property int test: 34
+ signal pressed()
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = test + i
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ // signal and property changed
+ testChanged();
+ pressed();
+ }
+
+ // attached
+ Keys.onPressed: {
+ }
+
+ // propertychanged handler
+ onTestChanged: {
+ f(); // method identifier
+ }
+
+ // signal handler
+ onPressed: {}
+
+ enum K { Plus}
+ property int tt: Identifiers.Plus // component and enum value
+
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/bindings.qml b/tests/auto/qmlls/utils/data/highlights/bindings.qml
new file mode 100644
index 0000000000..ac1592e778
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/bindings.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int x: 45
+
+ Behavior on width {}
+
+ x: width
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/comments.qml b/tests/auto/qmlls/utils/data/highlights/comments.qml
new file mode 100644
index 0000000000..351aaee36c
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/comments.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+Item {
+/*
+ multiline comment
+*/
+
+/* single line comment */
+// another
+
+ function inc() {
+ // in
+
+ /*
+ inside js
+ */
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/enums.qml b/tests/auto/qmlls/utils/data/highlights/enums.qml
new file mode 100644
index 0000000000..22183bf37f
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/enums.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ enum Osc {
+ Sin,
+ Saw = 1
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/identifiers.qml b/tests/auto/qmlls/utils/data/highlights/identifiers.qml
new file mode 100644
index 0000000000..7725b6d5e4
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/identifiers.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ readonly property int test: 34
+ signal pressed()
+ function f() {
+ let sum = 0, sum2 = 0
+ for(let i = 1; i < 42; i = i + 2) {
+ sum = test + i
+ {
+ let sum = 42; // another unrelated sum
+ }
+ }
+ // signal and property changed
+ testChanged();
+ pressed();
+ }
+
+ // attached
+ Keys.onPressed: {
+ }
+
+ // propertychanged handler
+ onTestChanged: {
+ f(); // method identifier
+ }
+
+ // signal handler
+ onPressed: {}
+
+ enum K { Plus}
+ property int tt: Identifiers.Plus // component and enum value
+
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/imports.qml b/tests/auto/qmlls/utils/data/highlights/imports.qml
new file mode 100644
index 0000000000..1e69077070
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/imports.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQml 2.15
+import "X" as Patron
+
+Item {
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/literals.qml b/tests/auto/qmlls/utils/data/highlights/literals.qml
new file mode 100644
index 0000000000..520ed5d2ef
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/literals.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int a: 123
+ property string b: "single"
+ property string c: "multi
+ line string";
+ property bool d: true
+ property var e: null
+
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/methodAndSignal.qml b/tests/auto/qmlls/utils/data/highlights/methodAndSignal.qml
new file mode 100644
index 0000000000..4e8319f049
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/methodAndSignal.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ signal p()
+ signal q(int a)
+ signal r(a: int)
+ function a(b: int) : int {}
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/objectAndComponent.qml b/tests/auto/qmlls/utils/data/highlights/objectAndComponent.qml
new file mode 100644
index 0000000000..9165e4b1b5
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/objectAndComponent.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ component Patron: Item {}
+ Item {
+ id: inner
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/pragmas.qml b/tests/auto/qmlls/utils/data/highlights/pragmas.qml
new file mode 100644
index 0000000000..cf99c93584
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/pragmas.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Singleton
+pragma FunctionSignatureBehavior: Enforced
+pragma ValueTypeBehavior: Copy,Addressable
+
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qmlls/utils/data/highlights/properties.qml b/tests/auto/qmlls/utils/data/highlights/properties.qml
new file mode 100644
index 0000000000..bde60915ca
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/properties.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ Item {
+ property int k
+ readonly property int kk
+ required property int kkk
+ default property int kkkk
+ }
+}
diff --git a/tests/auto/qmlls/utils/data/highlights/scriptExpressions.qml b/tests/auto/qmlls/utils/data/highlights/scriptExpressions.qml
new file mode 100644
index 0000000000..ee0b4ff5f8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/highlights/scriptExpressions.qml
@@ -0,0 +1,116 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function k() {
+ }
+
+ function mafik() {
+ var patron = 34;
+ const upperLimit = 42;
+ do {
+ ++patron;
+ if (patron < 2)
+ continue;
+ else
+ ++patron;
+ } while (patron < upperLimit)
+ switch (patron) {
+ case 1:
+ return 23;
+ default:
+ break;
+ }
+ try {
+ {}
+ } catch (error) {
+ {}
+ } finally {}
+ for (const a in [1, 2, 3]) {
+ throw 2;
+ }
+ }
+
+ enum Test {
+ LOG
+ }
+
+ readonly property int t: 34
+ signal tt
+ required property int k
+
+ signal kkk(string a)
+ signal yyy(a: string)
+
+ function ttt() {
+
+ }
+
+function createComplexExpression(...objects) {
+ // Create an object that holds some data
+ let data = {
+ a: 5,
+ b: 10,
+ c: 3
+ };
+
+ // Create a complex expression using the data object
+ let expression = ((data.a + data.b * data.c) / (data.a - data.b)) ** data.c;
+
+ return expression;
+}
+
+ function set1() {
+ const array = [1,2,3,4];
+ const [a, b] = [1,2];
+ const [aa, , bb] = array;
+ const [aaa = 23, bbb] = array;
+ const [a1, b1, ...rest1] = array;
+ const [a2, , b2, ...rest2] = array;
+ const [a3, b3, ...{ pop, push }] = array;
+ const [a4, b4, ...[c, d]] = array;
+
+ const obj = {_a:1,_b:2};
+ const { a5, b5 } = obj;
+ const { a6: a_, b6: b1_ } = obj;
+ const { a7: a11 = 4, b11 = 34, c1: b111, d1 } = obj;
+ let key = a;
+ const { [key]: a___ } = obj;
+ }
+
+ function set2(s : int) : int {
+ // declare first
+ let a, b, a1, b1, c, d, rest, pop, push;
+ const array = [1,2,3,4];
+ [a, b] = array;
+ [a, , b] = array;
+ [a = aDefault, b] = array;
+ [a, b, ...rest] = array;
+ [a, , b, ...rest] = array;
+ [a, b, ...{ pop, push }] = array;
+ [a, b, ...[c, d]] = array;
+
+ const obj = {_a:1,_b:2};
+ ({ a, b } = obj); // brackets are required
+ ({ a: a1, b: b1 } = obj);
+
+ const complicatedObject = {
+ a: 1,
+ b: {
+ c: 2,
+ d: {
+ e: 3,
+ f: [4, 5, 6]
+ }
+ },
+ g: [7, 8, 9]
+ };
+
+ const { patron, b: { mafik, d: { e, f: [ , secondF, ...restF ] } }, g: [ firstG, ...restG ] } = complicatedObject;
+ }
+
+
+}
+
diff --git a/tests/auto/qmlls/utils/data/pragmas.qml b/tests/auto/qmlls/utils/data/pragmas.qml
new file mode 100644
index 0000000000..bcb73cf10b
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/pragmas.qml
@@ -0,0 +1,8 @@
+pragma Singleton
+pragma NativeMethodBehavior: AcceptThisObject;
+pragma ListPropertyAssignBehavior: Append, Replace;
+import QtQuick 2.15
+
+Item {
+
+}
diff --git a/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject-qt-5.html b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject-qt-5.html
new file mode 100644
index 0000000000..9c3ce4e0f2
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject-qt-5.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qqmlengine.cpp -->
+ <title>QtObject QML Type | Qt QML 5.15.16</title>
+ <link rel="stylesheet" type="text/css" href="style/offline-simple.css" />
+ <script type="text/javascript">
+ document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css");
+ // loading style sheet breaks anchors that were jumped to before
+ // so force jumping to anchor again
+ setTimeout(function() {
+ var anchor = location.hash;
+ // need to jump to different anchor first (e.g. none)
+ location.hash = "#";
+ setTimeout(function() {
+ location.hash = anchor;
+ }, 0);
+ }, 0);
+ </script>
+</head>
+<body>
+<div class="header" id="qtdocheader">
+ <div class="main">
+ <div class="main-rounded">
+ <div class="navigationbar">
+ <ul>
+<li><a href="../qtdoc/index.html" translate="no">Qt 5.15</a></li>
+<li><a href="qtqml-index.html" translate="no">Qt QML</a></li>
+<li><a href="qtqml-qmlmodule.html" translate="no">QML Types</a></li>
+<li>QtObject QML Type</li>
+<li id="buildversion"><a href="qtqml-index.html" translate="no">Qt 5.15.16 Reference Documentation</a></li>
+ </ul>
+ </div>
+</div>
+<div class="content">
+<div class="line">
+<div class="content mainContent">
+<div class="sidebar">
+<div class="toc">
+<h3><a name="toc">Contents</a></h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QtObject QML Type</h1>
+<span class="subtitle" translate="no"></span>
+<!-- $$$QtObject-brief -->
+<p>A basic QML type. <a href="#details">More...</a></p>
+<!-- @@@QtObject -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QtQml 2.15</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Instantiates:</td><td class="memItemRight bottomAlign"> <a href="qml-qtqml-qtobject.html" translate="no"><a href="../qtcore/qobject.html" translate="no">QObject</a></td></tr></table></div><ul>
+<li><a href="qml-qtqml-qtobject-members.html">List of all members, including inherited members</a></li>
+</ul>
+<a name="properties"></a>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><b><a href="qml-qtqml-qtobject.html#objectName-prop" translate="no">objectName</a></b></b> : string</li>
+</ul>
+<!-- $$$QtObject-description -->
+<a name="details"></a>
+<h2 id="details">Detailed Description</h2>
+<p>The QtObject type is a non-visual element which contains only the <a href="qml-qtqml-qtobject.html#objectName-prop" translate="no">objectName</a> property.</p>
+<p>It can be useful to create a QtObject if you need an extremely lightweight type to enclose a set of custom properties:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="../qtquick/qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtqml-qtobject.html" translate="no">QtObject</a></span> {
+ <span class="name">id</span>: <span class="name">attributes</span>
+ property <span class="type"><a href="qml-string.html" translate="no">string</a></span> <span class="name">name</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">size</span>
+ property <span class="type"><a href="qml-variant.html" translate="no">variant</a></span> <span class="name">attributes</span>
+ }
+
+ <span class="type"><a href="../qtquick/qml-qtquick-text.html" translate="no">Text</a></span> { <span class="name">text</span>: <span class="name">attributes</span>.<span class="name">name</span> }
+ }
+</pre>
+<p>It can also be useful for C++ integration, as it is just a plain <a href="../qtcore/qobject.html" translate="no">QObject</a>. See the <a href="../qtcore/qobject.html" translate="no">QObject</a> documentation for further details.</p>
+<!-- @@@QtObject -->
+<h2>Property Documentation</h2>
+<!-- $$$objectName -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="objectName-prop">
+<td class="tblQmlPropNode"><p>
+<a name="objectName-prop"></a><span class="name">objectName</span> : <span class="type"><a href="qml-string.html" translate="no">string</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the <a href="../qtcore/qobject.html#objectName-prop" translate="no">QObject::objectName</a> for this specific object instance.</p>
+<p>This allows a C++ application to locate an item within a QML component using the <a href="../qtcore/qobject.html#findChild" translate="no">QObject::findChild()</a> method. For example, the following C++ application locates the child <a href="../qtquick/qml-qtquick-rectangle.html" translate="no">Rectangle</a> item and dynamically changes its <code translate="no">color</code> value:</p>
+<pre class="qml" translate="no">
+ <span class="comment">// MyRect.qml</span>
+
+ import QtQuick 2.0
+
+ <span class="type"><a href="../qtquick/qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">width</span>: <span class="number">200</span>; <span class="name">height</span>: <span class="number">200</span>
+
+ <span class="type"><a href="../qtquick/qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">objectName</span>: <span class="string">&quot;myRect&quot;</span>
+ }
+ }
+</pre>
+<pre class="cpp" translate="no">
+ <span class="comment">// main.cpp</span>
+
+ <span class="type">QQuickView</span> view;
+ view<span class="operator">.</span>setSource(<span class="type">QUrl</span><span class="operator">::</span>fromLocalFile(<span class="string">&quot;MyRect.qml&quot;</span>));
+ view<span class="operator">.</span>show();
+
+ <span class="type">QQuickItem</span> <span class="operator">*</span>item <span class="operator">=</span> view<span class="operator">.</span>rootObject()<span class="operator">-</span><span class="operator">&gt;</span>findChild<span class="operator">&lt;</span><span class="type">QQuickItem</span><span class="operator">*</span><span class="operator">&gt;</span>(<span class="string">&quot;myRect&quot;</span>);
+ <span class="keyword">if</span> (item)
+ item<span class="operator">-</span><span class="operator">&gt;</span>setProperty(<span class="string">&quot;color&quot;</span><span class="operator">,</span> <span class="type">QColor</span>(<span class="type"><a href="qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">::</span>yellow));
+</pre>
+</div></div><!-- @@@objectName -->
+<br/>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="footer">
+ <p>
+ <acronym title="Copyright">&copy;</acronym> 2023 The Qt Company Ltd.
+ Documentation contributions included herein are the copyrights of
+ their respective owners.<br/> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation.<br/> Qt and respective logos are <a href="https://doc.qt.io/qt/trademarks.html"> trademarks</a> of The Qt Company Ltd. in Finland and/or other countries
+ worldwide. All other trademarks are property of their respective owners. </p>
+</div>
+</body>
+</html>
diff --git a/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject.html b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject.html
new file mode 100644
index 0000000000..16c218d2ff
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtqml-qtobject.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qqmlengine.cpp -->
+ <meta name="description" content="A basic QML type.">
+ <link rel="stylesheet" type="text/css" href="style/offline-simple.css" />
+ <script type="text/javascript">
+ document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css");
+ // loading style sheet breaks anchors that were jumped to before
+ // so force jumping to anchor again
+ setTimeout(function() {
+ var anchor = location.hash;
+ // need to jump to different anchor first (e.g. none)
+ location.hash = "#";
+ setTimeout(function() {
+ location.hash = anchor;
+ }, 0);
+ }, 0);
+ </script>
+</head>
+<body>
+<div class="header" id="qtdocheader">
+ <div class="main">
+ <div class="main-rounded">
+ <div class="navigationbar">
+ <ul>
+<li><a href="qtqml-index.html" translate="no">Qt QML</a></li>
+<li><a href="qtqml-qmlmodule.html" translate="no">QML Types</a></li>
+<li>QtObject</li>
+ </ul>
+ </div>
+</div>
+<div class="content">
+<div class="line">
+<div class="content mainContent">
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QtObject QML Type</h1>
+<!-- $$$QtObject-brief -->
+<p>A basic QML type. <a href="#details">More...</a></p>
+<!-- @@@QtObject -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QtQml</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Instantiates:</td><td class="memItemRight bottomAlign"> <a href="../qtcore/qobject.html" translate="no">QObject</a></td></tr></table></div><ul>
+<li><a href="qml-qtqml-qtobject-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qtqml-qtobject.html#objectName-prop" translate="no">objectName</a></b> : string</li>
+</ul>
+<!-- $$$QtObject-description -->
+<h2 id="details">Detailed Description</h2>
+<p>The QtObject type is a non-visual element which contains only the <a href="qml-qtqml-qtobject.html#objectName-prop" translate="no">objectName</a> property.</p>
+<p>It can be useful to create a QtObject if you need an extremely lightweight type to enclose a set of custom properties:</p>
+<pre class="qml" translate="no">
+ import QtQuick
+
+ <span class="type"><a href="../qtquick/qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtqml-qtobject.html" translate="no">QtObject</a></span> {
+ <span class="name">id</span>: <span class="name">attributes</span>
+ property <span class="type"><a href="qml-string.html" translate="no">string</a></span> <span class="name">name</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">size</span>
+ property <span class="type"><a href="qml-variant.html" translate="no">variant</a></span> <span class="name">attributes</span>
+ }
+
+ <span class="type"><a href="../qtquick/qml-qtquick-text.html" translate="no">Text</a></span> { <span class="name">text</span>: <span class="name">attributes</span>.<span class="name">name</span> }
+ }
+</pre>
+<p>It can also be useful for C++ integration, as it is just a plain <a href="../qtcore/qobject.html" translate="no">QObject</a>. See the <a href="../qtcore/qobject.html" translate="no">QObject</a> documentation for further details.</p>
+<!-- @@@QtObject -->
+<h2>Property Documentation</h2>
+<!-- $$$objectName -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="objectName-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">objectName</span> : <span class="type"><a href="qml-string.html" translate="no">string</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the <a href="../qtcore/qobject.html#objectName-prop" translate="no">QObject::objectName</a> for this specific object instance.</p>
+<p>This allows a C++ application to locate an item within a QML component using the <a href="../qtcore/qobject.html#findChild" translate="no">QObject::findChild</a>() method. For example, the following C++ application locates the child <a href="../qtquick/qml-qtquick-rectangle.html" translate="no">Rectangle</a> item and dynamically changes its <code translate="no">color</code> value:</p>
+<pre class="qml" translate="no">
+ <span class="comment">// MyRect.qml</span>
+
+ import QtQuick 2.0
+
+ <span class="type"><a href="../qtquick/qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">width</span>: <span class="number">200</span>; <span class="name">height</span>: <span class="number">200</span>
+
+ <span class="type"><a href="../qtquick/qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">objectName</span>: <span class="string">&quot;myRect&quot;</span>
+ }
+ }
+</pre>
+<pre class="cpp" translate="no">
+ <span class="comment">// main.cpp</span>
+
+ <span class="type">QQuickView</span> view;
+ view<span class="operator">.</span>setSource(<span class="type">QUrl</span><span class="operator">::</span>fromLocalFile(<span class="string">&quot;MyRect.qml&quot;</span>));
+ view<span class="operator">.</span>show();
+
+ <span class="type">QQuickItem</span> <span class="operator">*</span>item <span class="operator">=</span> view<span class="operator">.</span>rootObject()<span class="operator">-</span><span class="operator">&gt;</span>findChild<span class="operator">&lt;</span><span class="type">QQuickItem</span><span class="operator">*</span><span class="operator">&gt;</span>(<span class="string">&quot;myRect&quot;</span>);
+ <span class="keyword">if</span> (item)
+ item<span class="operator">-</span><span class="operator">&gt;</span>setProperty(<span class="string">&quot;color&quot;</span><span class="operator">,</span> <span class="type">QColor</span>(<span class="type"><a href="qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">::</span>yellow));
+</pre>
+</div></div><!-- @@@objectName -->
+<br/>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="footer">
+ <p>
+ <acronym title="Copyright">&copy;</acronym> 2023 The Qt Company Ltd.
+ Documentation contributions included herein are the copyrights of
+ their respective owners.<br/> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation.<br/> Qt and respective logos are <a href="https://doc.qt.io/qt/trademarks.html"> trademarks</a> of The Qt Company Ltd. in Finland and/or other countries
+ worldwide. All other trademarks are property of their respective owners. </p>
+</div>
+</body>
+</html>
diff --git a/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-item.html b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-item.html
new file mode 100644
index 0000000000..fc34b2d5db
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-item.html
@@ -0,0 +1,1486 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qquickitem.cpp -->
+ <meta name="description" content="A basic visual QML type.">
+ <title>Item QML Type | Qt Quick 6.8.0</title>
+ <link rel="stylesheet" type="text/css" href="style/offline-simple.css" />
+ <script type="text/javascript">
+ document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css");
+ // loading style sheet breaks anchors that were jumped to before
+ // so force jumping to anchor again
+ setTimeout(function() {
+ var anchor = location.hash;
+ // need to jump to different anchor first (e.g. none)
+ location.hash = "#";
+ setTimeout(function() {
+ location.hash = anchor;
+ }, 0);
+ }, 0);
+ </script>
+</head>
+<body>
+<div class="header" id="qtdocheader">
+ <div class="main">
+ <div class="main-rounded">
+ <div class="navigationbar">
+ <ul>
+<li><a href="../qtdoc/index.html" translate="no">Qt 6.8</a></li>
+<li><a href="qtquick-index.html" translate="no">Qt Quick</a></li>
+<li><a href="qtquick-qmlmodule.html" translate="no">QML Types</a></li>
+<li>Item</li>
+<li id="buildversion"><a href="qtquick-index.html" translate="no">Qt 6.8.0 Reference Documentation</a></li>
+ </ul>
+ </div>
+</div>
+<div class="content">
+<div class="line">
+<div class="content mainContent">
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#item-layers">Item Layers</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Item QML Type</h1>
+<!-- $$$Item-brief -->
+<p>A basic visual QML type. <a href="#details">More...</a></p>
+<!-- @@@Item -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QtQuick</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Instantiates:</td><td class="memItemRight bottomAlign"> <a href="qquickitem.html" translate="no">QQuickItem</a></td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <p><a href="../qtqml/qml-qtqml-qtobject.html" translate="no">QtObject</a></p>
+</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="qml-qtquick-animatedsprite.html" translate="no">AnimatedSprite</a>, <a href="qml-qtquick-borderimage.html" translate="no">BorderImage</a>, <a href="qml-qtquick-canvas.html" translate="no">Canvas</a>, <a href="qml-qtquick-column.html" translate="no">Column</a>, <a href="qml-qtquick-layouts-columnlayout.html" translate="no">ColumnLayout</a>, <a href="qml-qtquick-droparea.html" translate="no">DropArea</a>, <a href="qml-qtquick-flickable.html" translate="no">Flickable</a>, <a href="qml-qtquick-flipable.html" translate="no">Flipable</a>, <a href="qml-qtquick-flow.html" translate="no">Flow</a>, <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a>, <a href="qml-qtquick-grid.html" translate="no">Grid</a>, <a href="qml-qtquick-layouts-gridlayout.html" translate="no">GridLayout</a>, <a href="qml-qtquick-image.html" translate="no">Image</a>, <a href="qml-qtquick-layouts-layoutitemproxy.html" translate="no">LayoutItemProxy</a>, <a href="qml-qtquick-loader.html" translate="no">Loader</a>, <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>, <a href="qml-qtquick-effects-multieffect.html" translate="no">MultiEffect</a>, <a href="qml-qtquick-multipointtoucharea.html" translate="no">MultiPointTouchArea</a>, <a href="qml-qtquick-particles-particlepainter.html" translate="no">ParticlePainter</a>, <a href="qml-qtquick-pathview.html" translate="no">PathView</a>, <a href="qml-qtquick-pincharea.html" translate="no">PinchArea</a>, <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a>, <a href="qml-qtquick-repeater.html" translate="no">Repeater</a>, <a href="qml-qtquick-row.html" translate="no">Row</a>, <a href="qml-qtquick-layouts-rowlayout.html" translate="no">RowLayout</a>, <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a>, <a href="qml-qtquick-shadereffectsource.html" translate="no">ShaderEffectSource</a>, <a href="qml-qtquick-shapes-shape.html" translate="no">Shape</a>, <a href="qml-qtquick-spritesequence.html" translate="no">SpriteSequence</a>, <a href="qml-qtquick-layouts-stacklayout.html" translate="no">StackLayout</a>, <a href="qml-qtquick-text.html" translate="no">Text</a>, <a href="qml-qtquick-textedit.html" translate="no">TextEdit</a>, <a href="qml-qtquick-textinput.html" translate="no">TextInput</a>, and <a href="qml-qtquick-windowcontainer.html" translate="no">WindowContainer</a></p>
+</td></tr></table></div><ul>
+<li><a href="qml-qtquick-item-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#activeFocusOnTab-prop" translate="no">activeFocusOnTab</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors-prop" translate="no">anchors</a></b><ul>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.alignWhenCentered-prop" translate="no">anchors.alignWhenCentered</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.baseline-prop" translate="no">anchors.baseline</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.baselineOffset-prop" translate="no">anchors.baselineOffset</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.bottom-prop" translate="no">anchors.bottom</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.bottomMargin-prop" translate="no">anchors.bottomMargin</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.centerIn-prop" translate="no">anchors.centerIn</a></b> : Item</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.fill-prop" translate="no">anchors.fill</a></b> : Item</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.horizontalCenter-prop" translate="no">anchors.horizontalCenter</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.horizontalCenterOffset-prop" translate="no">anchors.horizontalCenterOffset</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.left-prop" translate="no">anchors.left</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.leftMargin-prop" translate="no">anchors.leftMargin</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.margins-prop" translate="no">anchors.margins</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.right-prop" translate="no">anchors.right</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.rightMargin-prop" translate="no">anchors.rightMargin</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.top-prop" translate="no">anchors.top</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.topMargin-prop" translate="no">anchors.topMargin</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.verticalCenter-prop" translate="no">anchors.verticalCenter</a></b> : AnchorLine</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#anchors.verticalCenterOffset-prop" translate="no">anchors.verticalCenterOffset</a></b> : real</li>
+</ul>
+</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#antialiasing-prop" translate="no">antialiasing</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#baselineOffset-prop" translate="no">baselineOffset</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#children-prop" translate="no">children</a></b> : list&lt;Item&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect-prop" translate="no">childrenRect</a></b><ul>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect.height-prop" translate="no">childrenRect.height</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect.width-prop" translate="no">childrenRect.width</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect.x-prop" translate="no">childrenRect.x</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childrenRect.y-prop" translate="no">childrenRect.y</a></b> : real</li>
+</ul>
+</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#clip-prop" translate="no">clip</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#containmentMask-prop" translate="no">containmentMask</a></b> : QObject*</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#data-prop" translate="no">data</a></b> : list&lt;QtObject&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#enabled-prop" translate="no">enabled</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#focusPolicy-prop" translate="no">focusPolicy</a></b> : enumeration <code class="summary extra" translate="no">(since 6.7)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#height-prop" translate="no">height</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#implicitHeight-prop" translate="no">implicitHeight</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#implicitWidth-prop" translate="no">implicitWidth</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.effect-prop" translate="no">layer.effect</a></b> : Component</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layer.enabled</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.format-prop" translate="no">layer.format</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.live-prop" translate="no">layer.live</a></b> : bool <code class="summary extra" translate="no">(since 6.5)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.mipmap-prop" translate="no">layer.mipmap</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.samplerName-prop" translate="no">layer.samplerName</a></b> : string</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.samples-prop" translate="no">layer.samples</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.smooth-prop" translate="no">layer.smooth</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.sourceRect-prop" translate="no">layer.sourceRect</a></b> : rect</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.textureMirroring-prop" translate="no">layer.textureMirroring</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.textureSize-prop" translate="no">layer.textureSize</a></b> : size</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#layer.wrapMode-prop" translate="no">layer.wrapMode</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#palette-prop" translate="no">palette</a></b> : Palette <code class="summary extra" translate="no">(since 6.0)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#parent-prop" translate="no">parent</a></b> : Item</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#resources-prop" translate="no">resources</a></b> : list&lt;QtObject&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#smooth-prop" translate="no">smooth</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#state-prop" translate="no">state</a></b> : string</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#states-prop" translate="no">states</a></b> : list&lt;State&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a></b> : list&lt;Transform&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#transitions-prop" translate="no">transitions</a></b> : list&lt;Transition&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#visible-prop" translate="no">visible</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#visibleChildren-prop" translate="no">visibleChildren</a></b> : list&lt;Item&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#width-prop" translate="no">width</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#x-prop" translate="no">x</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#y-prop" translate="no">y</a></b> : real</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#z-prop" translate="no">z</a></b> : real</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#childAt-method" translate="no">childAt</a></b>(real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">bool <b><a href="qml-qtquick-item.html#contains-method" translate="no">contains</a></b>(point <i>point</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#dumpItemTree-method" translate="no">dumpItemTree</a></b>() <code class="summary extra" translate="no">(since 6.3)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#forceActiveFocus-method" translate="no">forceActiveFocus</a></b>()</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#forceActiveFocus-method-1" translate="no">forceActiveFocus</a></b>(Qt::FocusReason <i>reason</i>)</li>
+<li class="fn" translate="no">bool <b><a href="qml-qtquick-item.html#grabToImage-method" translate="no">grabToImage</a></b>(<i>callback</i>, <i>targetSize</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapFromGlobal-method" translate="no">mapFromGlobal</a></b>(real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapFromItem-method" translate="no">mapFromItem</a></b>(Item <i>item</i>, real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapFromItem-method-1" translate="no">mapFromItem</a></b>(Item <i>item</i>, point <i>p</i>)</li>
+<li class="fn" translate="no">rect <b><a href="qml-qtquick-item.html#mapFromItem-method-2" translate="no">mapFromItem</a></b>(Item <i>item</i>, real <i>x</i>, real <i>y</i>, real <i>width</i>, real <i>height</i>)</li>
+<li class="fn" translate="no">rect <b><a href="qml-qtquick-item.html#mapFromItem-method-3" translate="no">mapFromItem</a></b>(Item <i>item</i>, rect <i>r</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapToGlobal-method" translate="no">mapToGlobal</a></b>(real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapToItem-method" translate="no">mapToItem</a></b>(Item <i>item</i>, real <i>x</i>, real <i>y</i>)</li>
+<li class="fn" translate="no">point <b><a href="qml-qtquick-item.html#mapToItem-method-1" translate="no">mapToItem</a></b>(Item <i>item</i>, point <i>p</i>)</li>
+<li class="fn" translate="no">rect <b><a href="qml-qtquick-item.html#mapToItem-method-2" translate="no">mapToItem</a></b>(Item <i>item</i>, real <i>x</i>, real <i>y</i>, real <i>width</i>, real <i>height</i>)</li>
+<li class="fn" translate="no">rect <b><a href="qml-qtquick-item.html#mapToItem-method-3" translate="no">mapToItem</a></b>(Item <i>item</i>, rect <i>r</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qtquick-item.html#nextItemInFocusChain-method" translate="no">nextItemInFocusChain</a></b>(bool <i>forward</i>)</li>
+</ul>
+<!-- $$$Item-description -->
+<h2 id="details">Detailed Description</h2>
+<p>The Item type is the base type for all visual items in Qt Quick.</p>
+<p>All visual items in Qt Quick inherit from Item. Although an Item object has no visual appearance, it defines all the attributes that are common across visual items, such as x and y position, width and height, <a href="qtquick-positioning-anchors.html" translate="no">anchoring</a> and key handling support.</p>
+<p>The Item type can be useful for grouping several items under a single root visual item. For example:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">source</span>: <span class="string">&quot;tile.png&quot;</span>
+ }
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">x</span>: <span class="number">80</span>
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">source</span>: <span class="string">&quot;tile.png&quot;</span>
+ }
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">x</span>: <span class="number">190</span>
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">fillMode</span>: <span class="name">Image</span>.<span class="name">Tile</span>
+ <span class="name">source</span>: <span class="string">&quot;tile.png&quot;</span>
+ }
+ }
+</pre>
+<h3 id="event-handling">Event Handling</h3>
+<p>All Item-based visual types can use <a href="qtquickhandlers-index.html" translate="no">Input Handlers</a> to handle incoming input events (subclasses of <a href="../qtgui/qinputevent.html" translate="no">QInputEvent</a>), such as mouse, touch and key events. This is the preferred declarative way to handle events.</p>
+<p>An alternative way to handle touch events is to subclass <a href="qquickitem.html" translate="no">QQuickItem</a>, call setAcceptTouchEvents() in the constructor, and override touchEvent(). <a href="../qtcore/qevent.html#accepted-prop" translate="no">Accept</a> the entire event to stop delivery to items underneath, and to exclusively grab for all the event's touch points. Use <a href="../qtgui/qpointerevent.html#setExclusiveGrabber" translate="no">QPointerEvent::setExclusiveGrabber</a>() to grab only certain touchpoints, and allow the event to be delivered further.</p>
+<p>Likewise, a <a href="qquickitem.html" translate="no">QQuickItem</a> subclass can call setAcceptedMouseButtons() to register to receive mouse button events, setAcceptHoverEvents() to receive hover events (mouse movements while no button is pressed), and override the virtual functions mousePressEvent(), mouseMoveEvent(), and mouseReleaseEvent(). Those can also accept the event to prevent further delivery and get an implicit grab at the same time; or explicitly <a href="../qtgui/qpointerevent.html#setExclusiveGrabber" translate="no">grab</a> the single <a href="../qtgui/qeventpoint.html" translate="no">QEventPoint</a> that the <a href="../qtgui/qmouseevent.html" translate="no">QMouseEvent</a> carries.</p>
+<p>Key handling is available to all Item-based visual types via the <a href="qml-qtquick-keys.html" translate="no">Keys</a> attached property. The <i>Keys</i> attached property provides basic signals such as <a href="qml-qtquick-keys.html#pressed-signal" translate="no">pressed</a> and <a href="qml-qtquick-keys.html#released-signal" translate="no">released</a>, as well as signals for specific keys, such as <a href="qml-qtquick-keys.html#spacePressed-signal" translate="no">spacePressed</a>. The example below assigns <a href="qtquick-input-focus.html" translate="no">keyboard focus</a> to the item and handles the left key via the general <code translate="no">onPressed</code> handler and the return key via the <code translate="no">onReturnPressed</code> handler:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">focus</span>: <span class="number">true</span>
+ <span class="name">Keys</span>.onPressed: (<span class="keyword"></span>event)=&gt; {
+ <span class="keyword">if</span> (<span class="name">event</span>.<span class="name">key</span> <span class="operator">==</span> <span class="name">Qt</span>.<span class="name">Key_Left</span>) {
+ <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;move left&quot;</span>);
+ <span class="name">event</span>.<span class="name">accepted</span> <span class="operator">=</span> <span class="number">true</span>;
+ }
+ }
+ <span class="name">Keys</span>.onReturnPressed: <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;Pressed return&quot;</span>);
+ }
+</pre>
+<p>See the <a href="qml-qtquick-keys.html" translate="no">Keys</a> attached property for detailed documentation.</p>
+<h3 id="layout-mirroring">Layout Mirroring</h3>
+<p>Item layouts can be mirrored using the <a href="qml-qtquick-layoutmirroring.html" translate="no">LayoutMirroring</a> attached property. This causes <a href="qml-qtquick-item.html#anchors.top-prop" translate="no">anchors</a> to be horizontally reversed, and also causes items that lay out or position their children (such as <a href="qml-qtquick-listview.html" translate="no">ListView</a> or <a href="qml-qtquick-row.html" translate="no">Row</a>) to horizontally reverse the direction of their layouts.</p>
+<p>See <a href="qml-qtquick-layoutmirroring.html" translate="no">LayoutMirroring</a> for more details.</p>
+<h2 id="item-layers">Item Layers</h2>
+<p>An Item will normally be rendered directly into the window it belongs to. However, by setting <a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layer.enabled</a>, it is possible to delegate the item and its entire subtree into an offscreen surface. Only the offscreen surface, a texture, will be then drawn into the window.</p>
+<p>If it is desired to have a texture size different from that of the item, this is possible using <a href="qml-qtquick-item.html#layer.textureSize-prop" translate="no">layer.textureSize</a>. To render only a section of the item into the texture, use <a href="qml-qtquick-item.html#layer.sourceRect-prop" translate="no">layer.sourceRect</a>. It is also possible to specify <a href="qml-qtquick-item.html#layer.sourceRect-prop" translate="no">layer.sourceRect</a> so it extends beyond the bounds of the item. In this case, the exterior will be padded with transparent pixels.</p>
+<p>The item will use linear interpolation for scaling if <a href="qml-qtquick-item.html#layer.smooth-prop" translate="no">layer.smooth</a> is set to <code translate="no">true</code> and will use mipmap for downsampling if <a href="qml-qtquick-item.html#layer.mipmap-prop" translate="no">layer.mipmap</a> is set to <code translate="no">true</code>. Mipmapping may improve visual quality of downscaled items. For mipmapping of single Image items, prefer <a href="qml-qtquick-image.html#mipmap-prop" translate="no">Image::mipmap</a>.</p>
+<h3 id="layer-opacity-vs-item-opacity">Layer Opacity vs Item Opacity</h3>
+<p>When applying <a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a> to an item hierarchy the opacity is applied to each item individually. This can lead to undesired visual results when the opacity is applied to a subtree. Consider the following example:</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><img src="images/qml-blending-nonlayered.png" alt="" /></td><td ><b>Non-layered Opacity</b><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">id</span>: <span class="name">nonLayered</span>
+
+ <span class="name">opacity</span>: <span class="number">0.5</span>
+
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> { <span class="name">width</span>: <span class="number">80</span>; <span class="name">height</span>: <span class="number">80</span>; <span class="name">border</span>.width: <span class="number">1</span> }
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> { <span class="name">x</span>: <span class="number">20</span>; <span class="name">y</span>: <span class="number">20</span>; <span class="name">width</span>: <span class="number">80</span>; <span class="name">height</span>: <span class="number">80</span>; <span class="name">border</span>.width: <span class="number">1</span> }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p>A layer is rendered with the root item's opacity being 1, and then the root item's opacity is applied to the texture when it is drawn. This means that fading in a large item hierarchy from transparent to opaque, or vice versa, can be done without the overlap artifacts that the normal item by item alpha blending has. Here is the same example with layer enabled:</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/qml-blending-layered.png" alt="" /></p></td><td ><b>Layered Opacity</b><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">id</span>: <span class="name">layered</span>
+
+ <span class="name">opacity</span>: <span class="number">0.5</span>
+
+ <span class="name">layer</span>.enabled: <span class="number">true</span>
+
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> { <span class="name">width</span>: <span class="number">80</span>; <span class="name">height</span>: <span class="number">80</span>; <span class="name">border</span>.width: <span class="number">1</span> }
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> { <span class="name">x</span>: <span class="number">20</span>; <span class="name">y</span>: <span class="number">20</span>; <span class="name">width</span>: <span class="number">80</span>; <span class="name">height</span>: <span class="number">80</span>; <span class="name">border</span>.width: <span class="number">1</span> }
+ }
+</pre>
+</td></tr>
+</table></div>
+<h3 id="combined-with-shadereffects">Combined with ShaderEffects</h3>
+<p>Setting <a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layer.enabled</a> to true will turn the item into a <a href="qquickitem.html#isTextureProvider" translate="no">texture provider</a>, making it possible to use the item directly as a texture, for instance in combination with the <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a> type.</p>
+<p>It is possible to apply an effect on a layer at runtime using layer.effect:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">id</span>: <span class="name">layerRoot</span>
+ <span class="name">layer</span>.enabled: <span class="number">true</span>
+ <span class="name">layer</span>.effect: <span class="name">ShaderEffect</span> {
+ <span class="name">fragmentShader</span>: <span class="string">&quot;effect.frag.qsb&quot;</span>
+ }
+ }
+</pre>
+<p>See <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a> for more information about using effects.</p>
+<div class="admonition note">
+<p><b>Note: </b><a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layer.enabled</a> is actually just a more convenient way of using <a href="qml-qtquick-shadereffectsource.html" translate="no">ShaderEffectSource</a>.</p>
+</div>
+<h3 id="memory-and-performance">Memory and Performance</h3>
+<p>When an item's layer is enabled, the scene graph will allocate memory in the GPU equal to <code translate="no">width x height x 4</code>. In memory constrained configurations, large layers should be used with care.</p>
+<p>In the <a href="../qtgui/qpainter.html" translate="no">QPainter</a> / <a href="../qtwidgets/qwidget.html" translate="no">QWidget</a> world, it is sometimes favorable to cache complex content in a pixmap, image or texture. In Qt Quick, because of the techniques already applied by the <a href="qtquick-visualcanvas-scenegraph-renderer.html" translate="no">scene graph renderer</a>, this will in most cases not be the case. Excessive draw calls are already reduced because of batching and a cache will in most cases end up blending more pixels than the original content. The overhead of rendering to an offscreen and the blending involved with drawing the resulting texture is therefore often more costly than simply letting the item and its children be drawn normally.</p>
+<p>Also, an item using a layer can not be <a href="qtquick-visualcanvas-scenegraph-renderer.html#batching" translate="no">batched</a> during rendering. This means that a scene with many layered items may have performance problems.</p>
+<p>Layering can be convenient and useful for visual effects, but should in most cases be enabled for the duration of the effect and disabled afterwards.</p>
+<!-- @@@Item -->
+<h2>Property Documentation</h2>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="children-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">children</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span>&gt;</p></td></tr>
+<tr valign="top" class="odd" id="resources-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">resources</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="../qtqml/qml-qtqml-qtobject.html" translate="no">QtObject</a></span>&gt;</p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>The children property contains the list of visual children of this item. The resources property contains non-visual resources that you want to reference by name.</p>
+<p>It is not generally necessary to refer to these properties when adding child items or resources, as the default <a href="qml-qtquick-item.html#data-prop" translate="no">data</a> property will automatically assign child objects to the <code translate="no">children</code> and <code translate="no">resources</code> properties as appropriate. See the <a href="qml-qtquick-item.html#data-prop" translate="no">data</a> documentation for details.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="height-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">height</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="width-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">width</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="x-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">x</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="y-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">y</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Defines the item's position and size. The default value is <code translate="no">0</code>.</p>
+<p>The (x,y) position is relative to the <a href="qml-qtquick-item.html#parent-prop" translate="no">parent</a>.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> { <span class="name">x</span>: <span class="number">100</span>; <span class="name">y</span>: <span class="number">100</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span> }
+</pre>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="implicitHeight-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">implicitHeight</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="implicitWidth-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">implicitWidth</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Defines the preferred width or height of the Item.</p>
+<p>If <a href="qml-qtquick-item.html#width-prop" translate="no">width</a> or <a href="qml-qtquick-item.html#height-prop" translate="no">height</a> is not specified, an item's effective size will be determined by its <a href="qml-qtquick-item.html#implicitWidth-prop" translate="no">implicitWidth</a> or <a href="qml-qtquick-item.html#implicitHeight-prop" translate="no">implicitHeight</a>.</p>
+<p>However, if an item is the child of a <a href="qtquicklayouts-index.html" translate="no">layout</a>, the layout will determine the item's preferred size using its implicit size. In such a scenario, the explicit <a href="qml-qtquick-item.html#width-prop" translate="no">width</a> or <a href="qml-qtquick-item.html#height-prop" translate="no">height</a> will be ignored.</p>
+<p>The default implicit size for most items is 0x0, however some items have an inherent implicit size which cannot be overridden, for example, <a href="qml-qtquick-image.html" translate="no">Image</a> and <a href="qml-qtquick-text.html" translate="no">Text</a>.</p>
+<p>Setting the implicit size is useful for defining components that have a preferred size based on their content, for example:</p>
+<pre class="qml" translate="no">
+ <span class="comment">// Label.qml</span>
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ property <span class="type">alias</span> <span class="name">icon</span>: <span class="name">image</span>.<span class="name">source</span>
+ property <span class="type">alias</span> <span class="name">label</span>: <span class="name">text</span>.<span class="name">text</span>
+ <span class="name">implicitWidth</span>: <span class="name">text</span>.<span class="name">implicitWidth</span> <span class="operator">+</span> <span class="name">image</span>.<span class="name">implicitWidth</span>
+ <span class="name">implicitHeight</span>: <span class="name">Math</span>.<span class="name">max</span>(<span class="name">text</span>.<span class="name">implicitHeight</span>, <span class="name">image</span>.<span class="name">implicitHeight</span>)
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> { <span class="name">id</span>: <span class="name">image</span> }
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {
+ <span class="name">id</span>: <span class="name">text</span>
+ <span class="name">wrapMode</span>: <span class="name">Text</span>.<span class="name">Wrap</span>
+ <span class="name">anchors</span>.left: <span class="name">image</span>.<span class="name">right</span>; <span class="name">anchors</span>.right: <span class="name">parent</span>.<span class="name">right</span>
+ <span class="name">anchors</span>.verticalCenter: <span class="name">parent</span>.<span class="name">verticalCenter</span>
+ }
+ }
+</pre>
+<div class="admonition note">
+<p><b>Note: </b>Using <a href="qml-qtquick-item.html#implicitWidth-prop" translate="no">implicitWidth</a> of <a href="qml-qtquick-text.html" translate="no">Text</a> or <a href="qml-qtquick-textedit.html" translate="no">TextEdit</a> and setting the width explicitly incurs a performance penalty as the text must be laid out twice.</p>
+</div>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$activeFocus -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="activeFocus-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">activeFocus</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This read-only property indicates whether the item has active focus.</p>
+<p>If activeFocus is true, either this item is the one that currently receives keyboard input, or it is a <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> ancestor of the item that currently receives keyboard input.</p>
+<p>Usually, activeFocus is gained by setting <a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a> on an item and its enclosing <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> objects. In the following example, the <code translate="no">input</code> and <code translate="no">focusScope</code> objects will have active focus, while the root rectangle object will not.</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a></span> {
+ <span class="name">id</span>: <span class="name">focusScope</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+
+ <span class="type"><a href="qml-qtquick-textinput.html" translate="no">TextInput</a></span> {
+ <span class="name">id</span>: <span class="name">input</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+ }
+ }
+ }
+</pre>
+<p><b>See also </b><a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a> and <a href="qtquick-input-focus.html" translate="no">Keyboard Focus in Qt Quick</a>.</p>
+</div></div><!-- @@@activeFocus -->
+<br/>
+<!-- $$$activeFocusOnTab -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="activeFocusOnTab-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">activeFocusOnTab</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item wants to be in the tab focus chain. By default, this is set to <code translate="no">false</code>.</p>
+<p>The tab focus chain traverses elements by first visiting the parent, and then its children in the order they occur in the children property. Pressing the tab key on an item in the tab focus chain will move keyboard focus to the next item in the chain. Pressing BackTab (normally Shift+Tab) will move focus to the previous item.</p>
+<p>To set up a manual tab focus chain, see <a href="qml-qtquick-keynavigation.html" translate="no">KeyNavigation</a>. Tab key events used by Keys or <a href="qml-qtquick-keynavigation.html" translate="no">KeyNavigation</a> have precedence over focus chain behavior; ignore the events in other key handlers to allow it to propagate.</p>
+</div></div><!-- @@@activeFocusOnTab -->
+<br/>
+<!-- $$$anchors -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="even" id="anchors-prop"><th class="centerAlign"><p><b>anchors group</b></p></th></tr>
+<tr valign="top" class="odd" id="anchors.alignWhenCentered-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.alignWhenCentered</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.baseline-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.baseline</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.baselineOffset-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.baselineOffset</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.bottom-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.bottom</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.bottomMargin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.bottomMargin</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.centerIn-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.centerIn</span> : <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.fill-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.fill</span> : <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.horizontalCenter-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.horizontalCenter</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.horizontalCenterOffset-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.horizontalCenterOffset</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.left-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.left</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.leftMargin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.leftMargin</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.margins-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.margins</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.right-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.right</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.rightMargin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.rightMargin</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.top-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.top</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.topMargin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.topMargin</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.verticalCenter-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.verticalCenter</span> : <span class="type">AnchorLine</span></p></td></tr>
+<tr valign="top" class="odd" id="anchors.verticalCenterOffset-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">anchors.verticalCenterOffset</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Anchors provide a way to position an item by specifying its relationship with other items.</p>
+<p>Margins apply to top, bottom, left, right, and fill anchors. The <a href="qml-qtquick-item.html#anchors.margins-prop" translate="no">anchors.margins</a> property can be used to set all of the various margins at once, to the same value. It will not override a specific margin that has been previously set; to clear an explicit margin set its value to <code translate="no">undefined</code>. Note that margins are anchor-specific and are not applied if an item does not use anchors.</p>
+<p>Offsets apply for horizontal center, vertical center, and baseline anchors.</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-anchors_example.png" alt="" /></p></td><td >Text anchored to Image, horizontally centered and vertically below, with a margin.<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">id</span>: <span class="name">pic</span>
+ <span class="comment">// ...</span>
+ }
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {
+ <span class="name">id</span>: <span class="name">label</span>
+ <span class="name">anchors</span>.horizontalCenter: <span class="name">pic</span>.<span class="name">horizontalCenter</span>
+ <span class="name">anchors</span>.top: <span class="name">pic</span>.<span class="name">bottom</span>
+ <span class="name">anchors</span>.topMargin: <span class="number">5</span>
+ <span class="comment">// ...</span>
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="even"><td ><p class="centerAlign"><img src="images/declarative-anchors_example2.png" alt="" /></p></td><td >Left of Text anchored to right of Image, with a margin. The y property of both defaults to 0.<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">id</span>: <span class="name">pic</span>
+ <span class="comment">// ...</span>
+ }
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {
+ <span class="name">id</span>: <span class="name">label</span>
+ <span class="name">anchors</span>.left: <span class="name">pic</span>.<span class="name">right</span>
+ <span class="name">anchors</span>.leftMargin: <span class="number">5</span>
+ <span class="comment">// ...</span>
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p><a href="qml-qtquick-item.html#anchors.fill-prop" translate="no">anchors.fill</a> provides a convenient way for one item to have the same geometry as another item, and is equivalent to connecting all four directional anchors.</p>
+<p>To clear an anchor value, set it to <code translate="no">undefined</code>.</p>
+<p><a href="qml-qtquick-item.html#anchors.alignWhenCentered-prop" translate="no">anchors.alignWhenCentered</a> (default <code translate="no">true</code>) forces centered anchors to align to a whole pixel; if the item being centered has an odd <a href="qml-qtquick-item.html#width-prop" translate="no">width</a> or <a href="qml-qtquick-item.html#height-prop" translate="no">height</a>, the item will be positioned on a whole pixel rather than being placed on a half-pixel. This ensures the item is painted crisply. There are cases where this is not desirable, for example when rotating the item jitters may be apparent as the center is rounded.</p>
+<div class="admonition note">
+<p><b>Note: </b>You can only anchor an item to siblings or a parent.</p>
+</div>
+<p>For more information see <a href="qtquick-positioning-anchors.html" translate="no">Anchor Layouts</a>.</p>
+</div></div><!-- @@@anchors -->
+<br/>
+<!-- $$$antialiasing -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="antialiasing-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">antialiasing</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Used by visual elements to decide if the item should use antialiasing or not. In some cases items with antialiasing require more memory and are potentially slower to render (see <a href="qtquick-visualcanvas-scenegraph-renderer.html#antialiasing" translate="no">Antialiasing</a> for more details).</p>
+<p>The default is false, but may be overridden by derived elements.</p>
+</div></div><!-- @@@antialiasing -->
+<br/>
+<!-- $$$baselineOffset -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="baselineOffset-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">baselineOffset</span> : <span class="type"><a href="../qtqml/qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Specifies the position of the item's baseline in local coordinates.</p>
+<p>The baseline of a <a href="qml-qtquick-text.html" translate="no">Text</a> item is the imaginary line on which the text sits. Controls containing text usually set their baseline to the baseline of their text.</p>
+<p>For non-text items, a default baseline offset of 0 is used.</p>
+</div></div><!-- @@@baselineOffset -->
+<br/>
+<!-- $$$childrenRect -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="even" id="childrenRect-prop"><th class="centerAlign"><p><b>childrenRect group</b></p></th></tr>
+<tr valign="top" class="odd" id="childrenRect.height-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">childrenRect.height</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+<tr valign="top" class="odd" id="childrenRect.width-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">childrenRect.width</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+<tr valign="top" class="odd" id="childrenRect.x-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">childrenRect.x</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+<tr valign="top" class="odd" id="childrenRect.y-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">childrenRect.y</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This read-only property holds the collective position and size of the item's children.</p>
+<p>This property is useful if you need to access the collective geometry of an item's children in order to correctly size the item.</p>
+<p>The geometry that is returned is local to the item. For example:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">x</span>: <span class="number">50</span>
+ <span class="name">y</span>: <span class="number">100</span>
+
+ <span class="comment">// prints: QRectF(-10, -20, 30, 40)</span>
+ <span class="name">Component</span>.onCompleted: <span class="name">print</span>(<span class="name">childrenRect</span>)
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">x</span>: -<span class="number">10</span>
+ <span class="name">y</span>: -<span class="number">20</span>
+ <span class="name">width</span>: <span class="number">30</span>
+ <span class="name">height</span>: <span class="number">40</span>
+ }
+ }
+</pre>
+</div></div><!-- @@@childrenRect -->
+<br/>
+<!-- $$$clip -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="clip-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">clip</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether clipping is enabled. The default clip value is <code translate="no">false</code>.</p>
+<p>If clipping is enabled, an item will clip its own painting, as well as the painting of its children, to its bounding rectangle.</p>
+<div class="admonition note">
+<p><b>Note: </b>Clipping can affect rendering performance. See <a href="qtquick-visualcanvas-scenegraph-renderer.html#clipping" translate="no">Clipping</a> for more information.</p>
+</div>
+</div></div><!-- @@@clip -->
+<br/>
+<!-- $$$containmentMask -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="containmentMask-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">containmentMask</span> : <span class="type">QObject</span>*</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds an optional mask for the Item to be used in the <a href="qml-qtquick-item.html#contains-method" translate="no">contains</a>() method. Its main use is currently to determine whether a <a href="../qtgui/qpointerevent.html" translate="no">pointer event</a> has landed into the item or not.</p>
+<p>By default the <code translate="no">contains()</code> method will return true for any point within the Item's bounding box. <code translate="no">containmentMask</code> allows for more fine-grained control. For example, if a custom C++ <a href="qquickitem.html" translate="no">QQuickItem</a> subclass with a specialized <a href="qml-qtquick-item.html#contains-method" translate="no">contains</a>() method is used as containmentMask:</p>
+<pre class="cpp" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> { <span class="name">id</span>: <span class="name">item</span>; <span class="name">containmentMask</span>: <span class="name">AnotherItem</span> { <span class="name">id</span>: <span class="name">anotherItem</span> } }
+</pre>
+<p><i>item</i>'s contains method would then return <code translate="no">true</code> only if <i>anotherItem</i>'s <a href="qml-qtquick-item.html#contains-method" translate="no">contains</a>() implementation returns <code translate="no">true</code>.</p>
+<p>A <a href="qml-qtquick-shapes-shape.html" translate="no">Shape</a> can be used as a mask, to make an item react to <a href="../qtgui/qpointerevent.html" translate="no">pointer events</a> only within a non-rectangular region:</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/containmentMask-shape.gif" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">90</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">color</span>: <span class="name">hoverHandler</span>.<span class="name">hovered</span> ? <span class="string">&quot;wheat&quot;</span> : <span class="string">&quot;lightgray&quot;</span>
+ <span class="name">containmentMask</span>: <span class="name">shape</span>
+
+ <span class="type"><a href="qml-qtquick-hoverhandler.html" translate="no">HoverHandler</a></span> { <span class="name">id</span>: <span class="name">hoverHandler</span> }
+
+ <span class="type"><a href="qml-qtquick-shapes-shape.html" translate="no">Shape</a></span> {
+ <span class="name">id</span>: <span class="name">shape</span>
+ <span class="name">containsMode</span>: <span class="name">Shape</span>.<span class="name">FillContains</span>
+
+ <span class="type"><a href="qml-qtquick-shapes-shapepath.html" translate="no">ShapePath</a></span> {
+ <span class="name">fillColor</span>: <span class="string">&quot;lightsteelblue&quot;</span>
+ <span class="name">startX</span>: <span class="number">10</span>; <span class="name">startY</span>: <span class="number">20</span>
+ <span class="type"><a href="qml-qtquick-patharc.html" translate="no">PathArc</a></span> {
+ <span class="name">x</span>: <span class="number">10</span>; <span class="name">y</span>: <span class="number">80</span>
+ <span class="name">radiusX</span>: <span class="number">40</span>; <span class="name">radiusY</span>: <span class="number">40</span>
+ <span class="name">useLargeArc</span>: <span class="number">true</span>
+ }
+ <span class="type"><a href="qml-qtquick-pathline.html" translate="no">PathLine</a></span> {
+ <span class="name">x</span>: <span class="number">10</span>; <span class="name">y</span>: <span class="number">20</span>
+ }
+ }
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p>It is also possible to define the contains method in QML. For example, to create a circular item that only responds to events within its actual bounds:</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/containmentMask-circle.gif" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">circle</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="name">width</span>
+ <span class="name">radius</span>: <span class="name">width</span> <span class="operator">/</span> <span class="number">2</span>
+ <span class="name">color</span>: <span class="name">tapHandler</span>.<span class="name">pressed</span> ? <span class="string">&quot;tomato&quot;</span> : <span class="name">hoverHandler</span>.<span class="name">hovered</span> ? <span class="string">&quot;darkgray&quot;</span> : <span class="string">&quot;lightgray&quot;</span>
+
+ <span class="type"><a href="qml-qtquick-taphandler.html" translate="no">TapHandler</a></span> { <span class="name">id</span>: <span class="name">tapHandler</span> }
+ <span class="type"><a href="qml-qtquick-hoverhandler.html" translate="no">HoverHandler</a></span> { <span class="name">id</span>: <span class="name">hoverHandler</span> }
+
+ <span class="name">containmentMask</span>: <span class="name">QtObject</span> {
+ property <span class="type">alias</span> <span class="name">radius</span>: <span class="name">circle</span>.<span class="name">radius</span>
+ <span class="keyword">function </span><span class="name">contains</span>(point: <span class="name">point</span>) : bool {
+ <span class="keyword">return</span> (<span class="name">Math</span>.<span class="name">pow</span>(<span class="name">point</span>.<span class="name">x</span> <span class="operator">-</span> <span class="name">radius</span>, <span class="number">2</span>) <span class="operator">+</span> <span class="name">Math</span>.<span class="name">pow</span>(<span class="name">point</span>.<span class="name">y</span> <span class="operator">-</span> <span class="name">radius</span>, <span class="number">2</span>)) <span class="operator">&lt;</span> <span class="name">Math</span>.<span class="name">pow</span>(<span class="name">radius</span>, <span class="number">2</span>)
+ }
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p><b>See also </b><a href="qtquick-shapes-example.html" translate="no">Qt Quick Examples - Shapes</a>.</p>
+</div></div><!-- @@@containmentMask -->
+<br/>
+<!-- $$$data -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="data-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">data</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="../qtqml/qml-qtqml-qtobject.html" translate="no">QtObject</a></span>&gt; <code class="details extra" translate="no">[default]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>The data property allows you to freely mix visual children and resources in an item. If you assign a visual item to the data list it becomes a child and if you assign any other object type, it is added as a resource.</p>
+<p>So you can write:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {}
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {}
+ <span class="type"><a href="../qtqml/qml-qtqml-timer.html" translate="no">Timer</a></span> {}
+ }
+</pre>
+<p>instead of:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">children</span>: [
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {},
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {}
+ ]
+ <span class="name">resources</span>: [
+ <span class="type"><a href="../qtqml/qml-qtqml-timer.html" translate="no">Timer</a></span> {}
+ ]
+ }
+</pre>
+<p>It should not generally be necessary to refer to the <code translate="no">data</code> property, as it is the default property for Item and thus all child items are automatically assigned to this property.</p>
+</div></div><!-- @@@data -->
+<br/>
+<!-- $$$enabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="enabled-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">enabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item receives mouse and keyboard events. By default this is true.</p>
+<p>Setting this property directly affects the <code translate="no">enabled</code> value of child items. When set to <code translate="no">false</code>, the <code translate="no">enabled</code> values of all child items also become <code translate="no">false</code>. When set to <code translate="no">true</code>, the <code translate="no">enabled</code> values of child items are returned to <code translate="no">true</code>, unless they have explicitly been set to <code translate="no">false</code>.</p>
+<p>Setting this property to <code translate="no">false</code> automatically causes <a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a> to be set to <code translate="no">false</code>, and this item will no longer receive keyboard events.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#visible-prop" translate="no">visible</a>.</p>
+</div></div><!-- @@@enabled -->
+<br/>
+<!-- $$$focus -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="focus-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">focus</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item has focus within the enclosing <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a>. If true, this item will gain active focus when the enclosing <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> gains active focus.</p>
+<p>In the following example, <code translate="no">input</code> will be given active focus when <code translate="no">scope</code> gains active focus:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a></span> {
+ <span class="name">id</span>: <span class="name">scope</span>
+
+ <span class="type"><a href="qml-qtquick-textinput.html" translate="no">TextInput</a></span> {
+ <span class="name">id</span>: <span class="name">input</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+ }
+ }
+ }
+</pre>
+<p>For the purposes of this property, the scene as a whole is assumed to act like a focus scope. On a practical level, that means the following QML will give active focus to <code translate="no">input</code> on startup.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-textinput.html" translate="no">TextInput</a></span> {
+ <span class="name">id</span>: <span class="name">input</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+ }
+ }
+</pre>
+<p><b>See also </b><a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a> and <a href="qtquick-input-focus.html" translate="no">Keyboard Focus in Qt Quick</a>.</p>
+</div></div><!-- @@@focus -->
+<br/>
+<!-- $$$focusPolicy -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="focusPolicy-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">focusPolicy</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span> <code class="details extra" translate="no">[since 6.7]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property determines the way the item accepts focus.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">Qt.TabFocus</code></td><td class="topAlign">The item accepts focus by tabbing.</td></tr>
+<tr><td class="topAlign"><code translate="no">Qt.ClickFocus</code></td><td class="topAlign">The item accepts focus by clicking.</td></tr>
+<tr><td class="topAlign"><code translate="no">Qt.StrongFocus</code></td><td class="topAlign">The item accepts focus by both tabbing and clicking.</td></tr>
+<tr><td class="topAlign"><code translate="no">Qt.WheelFocus</code></td><td class="topAlign">The item accepts focus by tabbing, clicking, and using the mouse wheel.</td></tr>
+<tr><td class="topAlign"><code translate="no">Qt.NoFocus</code></td><td class="topAlign">The item does not accept focus.</td></tr>
+</table></div>
+<div class="admonition note">
+<p><b>Note: </b>This property was a member of Control until Qt 6.7.</p>
+</div>
+<p>This property was introduced in Qt 6.7.</p>
+</div></div><!-- @@@focusPolicy -->
+<br/>
+<!-- $$$layer.effect -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.effect-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.effect</span> : <span class="type">Component</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Holds the effect that is applied to this layer.</p>
+<p>The effect is typically a <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a> component, although any <a href="qml-qtquick-item.html" translate="no">Item</a> component can be assigned. The effect should have a source texture property with a name matching <a href="qml-qtquick-item.html#layer.samplerName-prop" translate="no">layer.samplerName</a>.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#layer.samplerName-prop" translate="no">layer.samplerName</a> and <a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.effect -->
+<br/>
+<!-- $$$layer.enabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.enabled-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.enabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Holds whether the item is layered or not. Layering is disabled by default.</p>
+<p>A layered item is rendered into an offscreen surface and cached until it is changed. Enabling layering for complex QML item hierarchies can sometimes be an optimization.</p>
+<p>None of the other layer properties have any effect when the layer is disabled.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.enabled -->
+<br/>
+<!-- $$$layer.format -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.format-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.format</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property defines the format of the backing texture. Modifying this property makes most sense when the <i translate="no">layer.effect</i> is also specified.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGBA8</code></td><td class="topAlign">&nbsp;</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGBA16F</code></td><td class="topAlign">&nbsp;</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGBA32F</code></td><td class="topAlign">&nbsp;</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.Alpha</code></td><td class="topAlign">Starting with Qt 6.0, this value is not in use and has the same effect as <code translate="no">RGBA8</code> in practice.</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGB</code></td><td class="topAlign">Starting with Qt 6.0, this value is not in use and has the same effect as <code translate="no">RGBA8</code> in practice.</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RGBA</code></td><td class="topAlign">Starting with Qt 6.0, this value is not in use and has the same effect as <code translate="no">RGBA8</code> in practice.</td></tr>
+</table></div>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.format -->
+<br/>
+<!-- $$$layer.live -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.live-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.live</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <code class="details extra" translate="no">[since 6.5]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>When this property is true the layer texture is updated whenever the item updates. Otherwise it will always be a frozen image.</p>
+<p>By default, this property is set to <code translate="no">true</code>.</p>
+<p>This property was introduced in Qt 6.5.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.live -->
+<br/>
+<!-- $$$layer.mipmap -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.mipmap-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.mipmap</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>If this property is true, mipmaps are generated for the texture.</p>
+<div class="admonition note">
+<p><b>Note: </b>Some OpenGL ES 2 implementations do not support mipmapping of non-power-of-two textures.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.mipmap -->
+<br/>
+<!-- $$$layer.samplerName -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.samplerName-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.samplerName</span> : <span class="type"><a href="../qtqml/qml-string.html" translate="no">string</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Holds the name of the effect's source texture property.</p>
+<p>This value must match the name of the effect's source texture property so that the Item can pass the layer's offscreen surface to the effect correctly.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#layer.effect-prop" translate="no">layer.effect</a>, <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a>, and <a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.samplerName -->
+<br/>
+<!-- $$$layer.samples -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.samples-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.samples</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property allows requesting multisampled rendering in the layer.</p>
+<p>By default multisampling is enabled whenever multisampling is enabled for the entire window, assuming the scenegraph renderer in use and the underlying graphics API supports this.</p>
+<p>By setting the value to 2, 4, etc. multisampled rendering can be requested for a part of the scene without enabling multisampling for the entire scene. This way multisampling is applied only to a given subtree, which can lead to significant performance gains since multisampling is not applied to other parts of the scene.</p>
+<div class="admonition note">
+<p><b>Note: </b>Enabling multisampling can be potentially expensive regardless of the layer's size, as it incurs a hardware and driver dependent performance and memory cost.</p>
+</div>
+<div class="admonition note">
+<p><b>Note: </b>This property is only functional when support for multisample renderbuffers and framebuffer blits is available. Otherwise the value is silently ignored.</p>
+</div>
+</div></div><!-- @@@layer.samples -->
+<br/>
+<!-- $$$layer.smooth -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.smooth-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.smooth</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Holds whether the layer is smoothly transformed. When enabled, sampling the layer's texture is performed using <code translate="no">linear</code> interpolation, while non-smooth results in using the <code translate="no">nearest</code> filtering mode.</p>
+<p>By default, this property is set to <code translate="no">false</code>.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.smooth -->
+<br/>
+<!-- $$$layer.sourceRect -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.sourceRect-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.sourceRect</span> : <span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property defines the rectangular area of the item that should be rendered into the texture. The source rectangle can be larger than the item itself. If the rectangle is null, which is the default, then the whole item is rendered to the texture.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.sourceRect -->
+<br/>
+<!-- $$$layer.textureMirroring -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.textureMirroring-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.textureMirroring</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property defines how the generated texture should be mirrored. The default value is <code translate="no">ShaderEffectSource.MirrorVertically</code>. Custom mirroring can be useful if the generated texture is directly accessed by custom shaders, such as those specified by <a href="qml-qtquick-shadereffect.html" translate="no">ShaderEffect</a>. If no effect is specified for the layered item, mirroring has no effect on the UI representation of the item.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.NoMirroring</code></td><td class="topAlign">No mirroring</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.MirrorHorizontally</code></td><td class="topAlign">The generated texture is flipped along X-axis.</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.MirrorVertically</code></td><td class="topAlign">The generated texture is flipped along Y-axis.</td></tr>
+</table></div>
+</div></div><!-- @@@layer.textureMirroring -->
+<br/>
+<!-- $$$layer.textureSize -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.textureSize-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.textureSize</span> : <span class="type"><a href="../qtqml/qml-size.html" translate="no">size</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the requested pixel size of the layers texture. If it is empty, which is the default, the size of the item is used.</p>
+<div class="admonition note">
+<p><b>Note: </b>Some platforms have a limit on how small framebuffer objects can be, which means the actual texture size might be larger than the requested size.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.textureSize -->
+<br/>
+<!-- $$$layer.wrapMode -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="layer.wrapMode-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">layer.wrapMode</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property defines the wrap modes associated with the texture. Modifying this property makes most sense when the <i translate="no">layer.effect</i> is specified.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.ClampToEdge</code></td><td class="topAlign">GL_CLAMP_TO_EDGE both horizontally and vertically</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RepeatHorizontally</code></td><td class="topAlign">GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.RepeatVertically</code></td><td class="topAlign">GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically</td></tr>
+<tr><td class="topAlign"><code translate="no">ShaderEffectSource.Repeat</code></td><td class="topAlign">GL_REPEAT both horizontally and vertically</td></tr>
+</table></div>
+<div class="admonition note">
+<p><b>Note: </b>Some OpenGL ES 2 implementations do not support the GL_REPEAT wrap mode with non-power-of-two textures.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-item.html#item-layers" translate="no">Item Layers</a>.</p>
+</div></div><!-- @@@layer.wrapMode -->
+<br/>
+<!-- $$$opacity -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="opacity-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">opacity</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the opacity of the item. Opacity is specified as a number between 0.0 (fully transparent) and 1.0 (fully opaque). The default value is 1.0.</p>
+<p>When this property is set, the specified opacity is also applied individually to child items. This may have an unintended effect in some circumstances. For example in the second set of rectangles below, the red rectangle has specified an opacity of 0.5, which affects the opacity of its blue child rectangle even though the child has not specified an opacity.</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-item_opacity1.png" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="even"><td ><p class="centerAlign"><img src="images/declarative-item_opacity2.png" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">opacity</span>: <span class="number">0.5</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p>Changing an item's opacity does not affect whether the item receives user input events. (In contrast, setting <a href="qml-qtquick-item.html#visible-prop" translate="no">visible</a> property to <code translate="no">false</code> stops mouse events, and setting the <a href="qml-qtquick-item.html#enabled-prop" translate="no">enabled</a> property to <code translate="no">false</code> stops mouse and keyboard events, and also removes active focus from the item.)</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#visible-prop" translate="no">visible</a>.</p>
+</div></div><!-- @@@opacity -->
+<br/>
+<!-- $$$palette -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="palette-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">palette</span> : <span class="type"><a href="qml-qtquick-palette.html" translate="no">Palette</a></span> <code class="details extra" translate="no">[since 6.0]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the palette currently set for the item.</p>
+<p>This property describes the item's requested palette. The palette is used by the item's style when rendering all controls, and is available as a means to ensure that custom controls can maintain consistency with the native platform's native look and feel. It's common that different platforms, or different styles, define different palettes for an application.</p>
+<p>The default palette depends on the system environment. <a href="../qtquickcontrols/qml-qtquick-controls-applicationwindow.html" translate="no">ApplicationWindow</a> maintains a system/theme palette which serves as a default for all controls. There may also be special palette defaults for certain types of controls. You can also set the default palette for controls by either:</p>
+<ul>
+<li>passing a custom palette to <a href="../qtgui/qguiapplication.html#setPalette" translate="no">QGuiApplication::setPalette</a>(), before loading any QML; or</li>
+<li>specifying the colors in the <a href="../qtquickcontrols/qtquickcontrols-configuration.html" translate="no">qtquickcontrols2.conf file</a>.</li>
+</ul>
+<p>Items propagate explicit palette properties from parents to children. If you change a specific property on a items's palette, that property propagates to all of the item's children, overriding any system defaults for that property.</p>
+<pre class="cpp" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type">palette</span> {
+ <span class="name">buttonText</span>: <span class="string">&quot;maroon&quot;</span>
+ <span class="name">button</span>: <span class="string">&quot;lavender&quot;</span>
+ }
+
+ <span class="type"><a href="../qtquickcontrols/qml-qtquick-controls-button.html" translate="no">Button</a></span> {
+ <span class="name">text</span>: <span class="string">&quot;Click Me&quot;</span>
+ }
+ }
+</pre>
+<p>This property was introduced in Qt 6.0.</p>
+<p><b>See also </b><a href="qml-qtquick-window.html#palette-prop" translate="no">Window::palette</a>, <a href="../qtquickcontrols/qml-qtquick-controls-popup.html#palette-prop" translate="no">Popup::palette</a>, <a href="qml-qtquick-colorgroup.html" translate="no">ColorGroup</a>, <a href="qml-qtquick-palette.html" translate="no">Palette</a>, and <a href="qml-qtquick-systempalette.html" translate="no">SystemPalette</a>.</p>
+</div></div><!-- @@@palette -->
+<br/>
+<!-- $$$parent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="parent-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">parent</span> : <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the visual parent of the item.</p>
+<div class="admonition note">
+<p><b>Note: </b>The concept of the <i>visual parent</i> differs from that of the <i><a href="../qtcore/qobject.html" translate="no">QObject</a> parent</i>. An item's visual parent may not necessarily be the same as its object parent. See <a href="qtquick-visualcanvas-visualparent.html" translate="no">Concepts - Visual Parent in Qt Quick</a> for more details.</p>
+</div>
+</div></div><!-- @@@parent -->
+<br/>
+<!-- $$$rotation -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="rotation-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">rotation</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the rotation of the item in degrees clockwise around its <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>.</p>
+<p>The default value is 0 degrees (that is, no rotation).</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-rotation.png" alt="" /></p></td><td ><pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">x</span>: <span class="number">25</span>; <span class="name">y</span>: <span class="number">25</span>; <span class="name">width</span>: <span class="number">50</span>; <span class="name">height</span>: <span class="number">50</span>
+ <span class="name">rotation</span>: <span class="number">30</span>
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p><b>See also </b><a href="qml-qtquick-transform.html" translate="no">Transform</a> and <a href="qml-qtquick-rotation.html" translate="no">Rotation</a>.</p>
+</div></div><!-- @@@rotation -->
+<br/>
+<!-- $$$scale -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="scale-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">scale</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the scale factor for this item.</p>
+<p>A scale of less than 1.0 causes the item to be rendered at a smaller size, and a scale greater than 1.0 renders the item at a larger size. A negative scale causes the item to be mirrored when rendered.</p>
+<p>The default value is 1.0.</p>
+<p>Scaling is applied from the <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>.</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-scale.png" alt="" /></p></td><td ><pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;green&quot;</span>
+ <span class="name">width</span>: <span class="number">25</span>; <span class="name">height</span>: <span class="number">25</span>
+ }
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">x</span>: <span class="number">25</span>; <span class="name">y</span>: <span class="number">25</span>; <span class="name">width</span>: <span class="number">50</span>; <span class="name">height</span>: <span class="number">50</span>
+ <span class="name">scale</span>: <span class="number">1.4</span>
+ <span class="name">transformOrigin</span>: <span class="name">Item</span>.<span class="name">TopLeft</span>
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+<p><b>See also </b><a href="qml-qtquick-transform.html" translate="no">Transform</a> and <a href="qml-qtquick-scale.html" translate="no">Scale</a>.</p>
+</div></div><!-- @@@scale -->
+<br/>
+<!-- $$$smooth -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="smooth-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">smooth</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Primarily used in image based items to decide if the item should use smooth sampling or not. Smooth sampling is performed using linear interpolation, while non-smooth is performed using nearest neighbor.</p>
+<p>In Qt Quick 2.0, this property has minimal impact on performance.</p>
+<p>By default, this property is set to <code translate="no">true</code>.</p>
+</div></div><!-- @@@smooth -->
+<br/>
+<!-- $$$state -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="state-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">state</span> : <span class="type"><a href="../qtqml/qml-string.html" translate="no">string</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the name of the current state of the item.</p>
+<p>If the item is in its default state, that is, no explicit state has been set, then this property holds an empty string. Likewise, you can return an item to its default state by setting this property to an empty string.</p>
+<p><b>See also </b><a href="qtquick-statesanimations-states.html" translate="no">Qt Quick States</a>.</p>
+</div></div><!-- @@@state -->
+<br/>
+<!-- $$$states -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="states-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">states</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-state.html" translate="no">State</a></span>&gt;</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the list of possible states for this item. To change the state of this item, set the <a href="qml-qtquick-item.html#state-prop" translate="no">state</a> property to one of these states, or set the <a href="qml-qtquick-item.html#state-prop" translate="no">state</a> property to an empty string to revert the item to its default state.</p>
+<p>This property is specified as a list of <a href="qml-qtquick-state.html" translate="no">State</a> objects. For example, below is an item with &quot;red_color&quot; and &quot;blue_color&quot; states:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">root</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="name">states</span>: [
+ <span class="type"><a href="qml-qtquick-state.html" translate="no">State</a></span> {
+ <span class="name">name</span>: <span class="string">&quot;red_color&quot;</span>
+ <span class="type"><a href="qml-qtquick-propertychanges.html" translate="no">PropertyChanges</a></span> { <span class="name">root</span>.color: <span class="string">&quot;red&quot;</span> }
+ },
+ <span class="type"><a href="qml-qtquick-state.html" translate="no">State</a></span> {
+ <span class="name">name</span>: <span class="string">&quot;blue_color&quot;</span>
+ <span class="type"><a href="qml-qtquick-propertychanges.html" translate="no">PropertyChanges</a></span> { <span class="name">root</span>.color: <span class="string">&quot;blue&quot;</span> }
+ }
+ ]
+ }
+</pre>
+<p>See <a href="qtquick-statesanimations-states.html" translate="no">Qt Quick States</a> and <a href="qtquick-statesanimations-animations.html" translate="no">Animation and Transitions in Qt Quick</a> for more details on using states and transitions.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#transitions-prop" translate="no">transitions</a>.</p>
+</div></div><!-- @@@states -->
+<br/>
+<!-- $$$transform -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="transform-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">transform</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-transform.html" translate="no">Transform</a></span>&gt; <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the list of transformations to apply.</p>
+<p>For more information see <a href="qml-qtquick-transform.html" translate="no">Transform</a>.</p>
+</div></div><!-- @@@transform -->
+<br/>
+<!-- $$$transformOrigin -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="transformOrigin-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">transformOrigin</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the origin point around which scale and rotation transform.</p>
+<p>Nine transform origins are available, as shown in the image below. The default transform origin is <code translate="no">Item.Center</code>.</p>
+<p class="centerAlign"><img src="images/declarative-transformorigin.png" alt="" /></p><p>This example rotates an image around its bottom-right corner.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">source</span>: <span class="string">&quot;myimage.png&quot;</span>
+ <span class="name">transformOrigin</span>: <span class="name">Item</span>.<span class="name">BottomRight</span>
+ <span class="name">rotation</span>: <span class="number">45</span>
+ }
+</pre>
+<p>To set an arbitrary transform origin point use the <a href="qml-qtquick-scale.html" translate="no">Scale</a> or <a href="qml-qtquick-rotation.html" translate="no">Rotation</a> transform types with <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+</div></div><!-- @@@transformOrigin -->
+<br/>
+<!-- $$$transitions -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="transitions-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">transitions</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-transition.html" translate="no">Transition</a></span>&gt;</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the list of transitions for this item. These define the transitions to be applied to the item whenever it changes its <a href="qml-qtquick-item.html#state-prop" translate="no">state</a>.</p>
+<p>This property is specified as a list of <a href="qml-qtquick-transition.html" translate="no">Transition</a> objects. For example:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="name">transitions</span>: [
+ <span class="type"><a href="qml-qtquick-transition.html" translate="no">Transition</a></span> {
+ <span class="comment">//...</span>
+ },
+ <span class="type"><a href="qml-qtquick-transition.html" translate="no">Transition</a></span> {
+ <span class="comment">//...</span>
+ }
+ ]
+ }
+</pre>
+<p>See <a href="qtquick-statesanimations-states.html" translate="no">Qt Quick States</a> and <a href="qtquick-statesanimations-animations.html" translate="no">Animation and Transitions in Qt Quick</a> for more details on using states and transitions.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#states-prop" translate="no">states</a>.</p>
+</div></div><!-- @@@transitions -->
+<br/>
+<!-- $$$visible -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="visible-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">visible</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item is visible. By default this is true.</p>
+<p>Setting this property directly affects the <code translate="no">visible</code> value of child items. When set to <code translate="no">false</code>, the <code translate="no">visible</code> values of all child items also become <code translate="no">false</code>. When set to <code translate="no">true</code>, the <code translate="no">visible</code> values of child items are returned to <code translate="no">true</code>, unless they have explicitly been set to <code translate="no">false</code>.</p>
+<p>(Because of this flow-on behavior, using the <code translate="no">visible</code> property may not have the intended effect if a property binding should only respond to explicit property changes. In such cases it may be better to use the <a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a> property instead.)</p>
+<p>If this property is set to <code translate="no">false</code>, the item will no longer receive mouse events, but will continue to receive key events and will retain the keyboard <a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a> if it has been set. (In contrast, setting the <a href="qml-qtquick-item.html#enabled-prop" translate="no">enabled</a> property to <code translate="no">false</code> stops both mouse and keyboard events, and also removes focus from the item.)</p>
+<div class="admonition note">
+<p><b>Note: </b>This property's value is only affected by changes to this property or the parent's <code translate="no">visible</code> property. It does not change, for example, if this item moves off-screen, or if the <a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a> changes to 0.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-item.html#opacity-prop" translate="no">opacity</a> and <a href="qml-qtquick-item.html#enabled-prop" translate="no">enabled</a>.</p>
+</div></div><!-- @@@visible -->
+<br/>
+<!-- $$$visibleChildren -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="visibleChildren-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">visibleChildren</span> : <span class="type"><a href="../qtqml/qml-list.html" translate="no">list</a></span>&lt;<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span>&gt;</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This read-only property lists all of the item's children that are currently visible. Note that a child's visibility may have changed explicitly, or because the visibility of this (it's parent) item or another grandparent changed.</p>
+</div></div><!-- @@@visibleChildren -->
+<br/>
+<!-- $$$z -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="z-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">z</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Sets the stacking order of sibling items. By default the stacking order is 0.</p>
+<p>Items with a higher stacking value are drawn on top of siblings with a lower stacking order. Items with the same stacking value are drawn bottom up in the order they appear. Items with a negative stacking value are drawn under their parent's content.</p>
+<p>The following example shows the various effects of stacking order.</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-item_stacking1.png" alt="" /></p></td><td >Same <code translate="no">z</code> - later children above earlier children:<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="even"><td ><p class="centerAlign"><img src="images/declarative-item_stacking2.png" alt="" /></p></td><td >Higher <code translate="no">z</code> on top:<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">z</span>: <span class="number">1</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="odd"><td ><p class="centerAlign"><img src="images/declarative-item_stacking3.png" alt="" /></p></td><td >Same <code translate="no">z</code> - children above parents:<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+ }
+</pre>
+</td></tr>
+<tr valign="top" class="even"><td ><p class="centerAlign"><img src="images/declarative-item_stacking4.png" alt="" /></p></td><td >Lower <code translate="no">z</code> below:<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> {
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">z</span>: -<span class="number">1</span>
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span>: <span class="number">50</span>; <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ }
+ }
+ }
+</pre>
+</td></tr>
+</table></div>
+</div></div><!-- @@@z -->
+<br/>
+<h2>Method Documentation</h2>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mapFromItem-method-1">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapFromItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <i>p</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapFromItem-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapFromItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapFromItem-method-2">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <span class="name">mapFromItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>width</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>height</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapFromItem-method-3">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <span class="name">mapFromItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <i>r</i>)</p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Maps the point (<i translate="no">x</i>, <i translate="no">y</i>) or rect (<i translate="no">x</i>, <i translate="no">y</i>, <i translate="no">width</i>, <i translate="no">height</i>), which is in <i translate="no">item</i>'s coordinate system, to this item's coordinate system, and returns a <a href="../qtqml/qml-point.html" translate="no">point</a> or <a href="../qtqml/qml-rect.html" translate="no">rect</a> matching the mapped coordinate.</p>
+<p>The following properties of the item are used in the mapping: <a href="qml-qtquick-item.html#x-prop" translate="no">x</a>, <a href="qml-qtquick-item.html#y-prop" translate="no">y</a>, <a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a>, <a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a>, <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>, and <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+<p>If the items are part of different scenes, the mapping includes the relative position of the two scenes.</p>
+<p>If <i translate="no">item</i> is a <code translate="no">null</code> value, this maps the point or rect from the coordinate system of the <a href="qtquick-visualcanvas-coordinates.html#scene-coordinates" translate="no">scene</a>.</p>
+<p>The versions accepting point and rect are since Qt 5.15.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mapToItem-method-1">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapToItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <i>p</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapToItem-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapToItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapToItem-method-2">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <span class="name">mapToItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>width</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>height</i>)</p></td></tr>
+<tr valign="top" class="odd" id="mapToItem-method-3">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <span class="name">mapToItem</span>(<span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span> <i>item</i>, <span class="type"><a href="../qtqml/qml-rect.html" translate="no">rect</a></span> <i>r</i>)</p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Maps the point (<i translate="no">x</i>, <i translate="no">y</i>) or rect (<i translate="no">x</i>, <i translate="no">y</i>, <i translate="no">width</i>, <i translate="no">height</i>), which is in this item's coordinate system, to <i translate="no">item</i>'s coordinate system, and returns a <a href="../qtqml/qml-point.html" translate="no">point</a> or <a href="../qtqml/qml-rect.html" translate="no">rect</a> matching the mapped coordinate.</p>
+<p>The following properties of the item are used in the mapping: <a href="qml-qtquick-item.html#x-prop" translate="no">x</a>, <a href="qml-qtquick-item.html#y-prop" translate="no">y</a>, <a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a>, <a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a>, <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>, and <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+<p>If the items are part of different scenes, the mapping includes the relative position of the two scenes.</p>
+<p>If <i translate="no">item</i> is a <code translate="no">null</code> value, this maps the point or rect to the coordinate system of the <a href="qtquick-visualcanvas-coordinates.html#scene-coordinates" translate="no">scene</a>.</p>
+<p>The versions accepting point and rect are since Qt 5.15.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$childAt[overload1]$$$childAtrealreal -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="childAt-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">childAt</span>(<span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Returns the first visible child item found at point (<i translate="no">x</i>, <i translate="no">y</i>) within the coordinate system of this item.</p>
+<p>Returns <code translate="no">null</code> if there is no such item.</p>
+</div></div><!-- @@@childAt -->
+<br/>
+<!-- $$$contains[overload1]$$$containspoint -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="contains-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <span class="name">contains</span>(<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <i>point</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Returns <code translate="no">true</code> if this item contains <i translate="no">point</i>, which is in local coordinates; returns <code translate="no">false</code> otherwise. This is the same check that is used for hit-testing a <a href="../qtgui/qeventpoint.html" translate="no">QEventPoint</a> during event delivery, and is affected by <a href="qml-qtquick-item.html#containmentMask-prop" translate="no">containmentMask</a> if it is set.</p>
+</div></div><!-- @@@contains -->
+<br/>
+<!-- $$$dumpItemTree[overload1]$$$dumpItemTree -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="dumpItemTree-method">
+<td class="tblQmlFuncNode"><p>
+<code class="details extra" translate="no">[since 6.3]</code> <span class="name">dumpItemTree</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Dumps some details about the <a href="qtquick-visualcanvas-visualparent.html" translate="no">visual tree of Items</a> starting with this item and its children, recursively.</p>
+<p>The output looks similar to that of this QML code:</p>
+<pre class="qml" translate="no">
+ function dump(object, indent) {
+ console.log(indent + object)
+ for (const i in object.children)
+ dump(object.children[i], indent + &quot; &quot;)
+ }
+
+ dump(myItem, &quot;&quot;)
+</pre>
+<p>So if you want more details, you can implement your own function and add extra output to the console.log, such as values of specific properties.</p>
+<p>This method was introduced in Qt 6.3.</p>
+<p><b>See also </b><a href="../qtcore/qobject.html#dumpObjectTree" translate="no">QObject::dumpObjectTree</a>().</p>
+</div></div><!-- @@@dumpItemTree -->
+<br/>
+<!-- $$$forceActiveFocus[overload1]$$$forceActiveFocus -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="forceActiveFocus-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">forceActiveFocus</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Forces active focus on the item.</p>
+<p>This method sets focus on the item and ensures that all ancestor <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> objects in the object hierarchy are also given <a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a>.</p>
+<p>The reason for the focus change will be <a href="../qtcore/qt.html#FocusReason-enum" translate="no">Qt::OtherFocusReason</a>. Use the overloaded method to specify the focus reason to enable better handling of the focus change.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a>.</p>
+</div></div><!-- @@@forceActiveFocus -->
+<br/>
+<!-- $$$forceActiveFocus$$$forceActiveFocusQt::FocusReason -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="forceActiveFocus-method-1">
+<td class="tblQmlFuncNode"><p>
+<span class="name">forceActiveFocus</span>(<span class="type">Qt::FocusReason</span> <i>reason</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This is an overloaded function.</p>
+<p>Forces active focus on the item with the given <i translate="no">reason</i>.</p>
+<p>This method sets focus on the item and ensures that all ancestor <a href="qml-qtquick-focusscope.html" translate="no">FocusScope</a> objects in the object hierarchy are also given <a href="qml-qtquick-item.html#focus-prop" translate="no">focus</a>.</p>
+<p><b>See also </b><a href="qml-qtquick-item.html#activeFocus-prop" translate="no">activeFocus</a> and <a href="../qtcore/qt.html#FocusReason-enum" translate="no">Qt::FocusReason</a>.</p>
+</div></div><!-- @@@forceActiveFocus -->
+<br/>
+<!-- $$$grabToImage[overload1]$$$grabToImage -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="grabToImage-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <span class="name">grabToImage</span>(<i>callback</i>, <i>targetSize</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Grabs the item into an in-memory image.</p>
+<p>The grab happens asynchronously and the JavaScript function <i translate="no">callback</i> is invoked when the grab is completed. The callback takes one argument, which is the result of the grab operation; an <a href="qml-qtquick-itemgrabresult.html" translate="no">ItemGrabResult</a> object.</p>
+<p>Use <i translate="no">targetSize</i> to specify the size of the target image. By default, the result will have the same size as the item.</p>
+<p>If the grab could not be initiated, the function returns <code translate="no">false</code>.</p>
+<p>The following snippet shows how to grab an item and store the results in a file:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">sourceRectangle</span>
+ <span class="name">width</span>: <span class="number">100</span>
+ <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">focus</span>: <span class="number">true</span>
+ <span class="name">gradient</span>: <span class="name">Gradient</span> {
+ <span class="type"><a href="qml-qtquick-gradientstop.html" translate="no">GradientStop</a></span> { <span class="name">position</span>: <span class="number">0</span>; <span class="name">color</span>: <span class="string">&quot;steelblue&quot;</span> }
+ <span class="type"><a href="qml-qtquick-gradientstop.html" translate="no">GradientStop</a></span> { <span class="name">position</span>: <span class="number">1</span>; <span class="name">color</span>: <span class="string">&quot;black&quot;</span> }
+ }
+
+ <span class="name">Keys</span>.onSpacePressed: {
+ <span class="name">sourceRectangle</span>.<span class="name">grabToImage</span>(<span class="keyword">function</span>(result) {
+ <span class="name">result</span>.<span class="name">saveToFile</span>(<span class="string">&quot;something.png&quot;</span>)
+ })
+ }
+ }
+</pre>
+<p>The following snippet shows how to grab an item and use the results in another image element:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-image.html" translate="no">Image</a></span> {
+ <span class="name">id</span>: <span class="name">image</span>
+ }
+
+ <span class="name">Keys</span>.onSpacePressed: {
+ <span class="name">sourceRectangle</span>.<span class="name">grabToImage</span>(<span class="keyword">function</span>(result) {
+ <span class="name">image</span>.<span class="name">source</span> <span class="operator">=</span> <span class="name">result</span>.<span class="name">url</span>
+ }, <span class="name">Qt</span>.<span class="name">size</span>(<span class="number">50</span>, <span class="number">50</span>))
+ }
+</pre>
+<div class="admonition note">
+<p><b>Note: </b>This function will render the item to an offscreen surface and copy that surface from the GPU's memory into the CPU's memory, which can be quite costly. For &quot;live&quot; preview, use <a href="qml-qtquick-item.html#layer.enabled-prop" translate="no">layers</a> or <a href="qml-qtquick-shadereffectsource.html" translate="no">ShaderEffectSource</a>.</p>
+</div>
+</div></div><!-- @@@grabToImage -->
+<br/>
+<!-- $$$mapFromGlobal[overload1]$$$mapFromGlobalrealreal -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mapFromGlobal-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapFromGlobal</span>(<span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Maps the point (<i translate="no">x</i>, <i translate="no">y</i>), which is in the global coordinate system, to the item's coordinate system, and returns a <a href="../qtqml/qml-point.html" translate="no">point</a> matching the mapped coordinate.</p>
+<p>The following properties of the item are used in the mapping: <a href="qml-qtquick-item.html#x-prop" translate="no">x</a>, <a href="qml-qtquick-item.html#y-prop" translate="no">y</a>, <a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a>, <a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a>, <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>, and <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+<p>If the items are part of different scenes, the mapping includes the relative position of the two scenes.</p>
+</div></div><!-- @@@mapFromGlobal -->
+<br/>
+<!-- $$$mapToGlobal[overload1]$$$mapToGlobalrealreal -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mapToGlobal-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="../qtqml/qml-point.html" translate="no">point</a></span> <span class="name">mapToGlobal</span>(<span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>x</i>, <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span> <i>y</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Maps the point (<i translate="no">x</i>, <i translate="no">y</i>), which is in this item's coordinate system, to the global coordinate system, and returns a <a href="../qtqml/qml-point.html" translate="no">point</a> matching the mapped coordinate.</p>
+<p>The following properties of the item are used in the mapping: <a href="qml-qtquick-item.html#x-prop" translate="no">x</a>, <a href="qml-qtquick-item.html#y-prop" translate="no">y</a>, <a href="qml-qtquick-item.html#scale-prop" translate="no">scale</a>, <a href="qml-qtquick-item.html#rotation-prop" translate="no">rotation</a>, <a href="qml-qtquick-item.html#transformOrigin-prop" translate="no">transformOrigin</a>, and <a href="qml-qtquick-item.html#transform-prop" translate="no">transform</a>.</p>
+<p>If the items are part of different scenes, the mapping includes the relative position of the two scenes.</p>
+</div></div><!-- @@@mapToGlobal -->
+<br/>
+<!-- $$$nextItemInFocusChain[overload1]$$$nextItemInFocusChainbool -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="nextItemInFocusChain-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">nextItemInFocusChain</span>(<span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span> <i>forward</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Returns the item in the focus chain which is next to this item. If <i translate="no">forward</i> is <code translate="no">true</code>, or not supplied, it is the next item in the forwards direction. If <i translate="no">forward</i> is <code translate="no">false</code>, it is the next item in the backwards direction.</p>
+</div></div><!-- @@@nextItemInFocusChain -->
+<br/>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="footer">
+ <p>
+ <acronym title="Copyright">&copy;</acronym> 2024 <span translate="no">The Qt Company Ltd.</span>
+ Documentation contributions included herein are the copyrights of
+ their respective owners.<br/> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the <span translate="no">Free Software Foundation</span>.<br/> <span translate="no">Qt</span> and respective logos are <a href="https://doc.qt.io/qt/trademarks.html"> trademarks</a> of <span translate="no">The Qt Company Ltd.</span> in Finland and/or other countries
+ worldwide. All other trademarks are property of their respective owners. </p>
+</div>
+</body>
+</html>
diff --git a/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-mousearea.html b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-mousearea.html
new file mode 100644
index 0000000000..bbdd6345e8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qdochtmlparser/qml-qtquick-mousearea.html
@@ -0,0 +1,676 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qquickmousearea.cpp -->
+ <title>MouseArea QML Type | Qt Quick 5.15.16</title>
+ <link rel="stylesheet" type="text/css" href="style/offline-simple.css" />
+ <script type="text/javascript">
+ document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css");
+ // loading style sheet breaks anchors that were jumped to before
+ // so force jumping to anchor again
+ setTimeout(function() {
+ var anchor = location.hash;
+ // need to jump to different anchor first (e.g. none)
+ location.hash = "#";
+ setTimeout(function() {
+ location.hash = anchor;
+ }, 0);
+ }, 0);
+ </script>
+</head>
+<body>
+<div class="header" id="qtdocheader">
+ <div class="main">
+ <div class="main-rounded">
+ <div class="navigationbar">
+ <ul>
+<li><a href="../qtdoc/index.html" translate="no">Qt 5.15</a></li>
+<li><a href="qtquick-index.html" translate="no">Qt Quick</a></li>
+<li><a href="qtquick-qmlmodule.html" translate="no">QML Types</a></li>
+<li>MouseArea QML Type</li>
+<li id="buildversion"><a href="qtquick-index.html" translate="no">Qt 5.15.16 Reference Documentation</a></li>
+ </ul>
+ </div>
+</div>
+<div class="content">
+<div class="line">
+<div class="content mainContent">
+<div class="sidebar">
+<div class="toc">
+<h3><a name="toc">Contents</a></h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#signals">Signals</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#example-usage">Example Usage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">MouseArea QML Type</h1>
+<span class="subtitle" translate="no"></span>
+<!-- $$$MouseArea-brief -->
+<p>Enables simple mouse handling. <a href="#details">More...</a></p>
+<!-- @@@MouseArea -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QtQuick 2.15</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <p><a href="qml-qtquick-item.html" translate="no">Item</a></p>
+</td></tr></table></div><ul>
+<li><a href="qml-qtquick-mousearea-members.html">List of all members, including inherited members</a></li>
+</ul>
+<a name="properties"></a>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a></b></b> : Qt::MouseButtons</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#containsPress-prop" translate="no">containsPress</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#cursorShape-prop" translate="no">cursorShape</a></b></b> : Qt::CursorShape</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag-prop" translate="no">drag</a></b></b><ul>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.active-prop" translate="no">drag.active</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.axis-prop" translate="no">drag.axis</a></b></b> : enumeration</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.filterChildren-prop" translate="no">drag.filterChildren</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.maximumX-prop" translate="no">drag.maximumX</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.maximumY-prop" translate="no">drag.maximumY</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.minimumX-prop" translate="no">drag.minimumX</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.minimumY-prop" translate="no">drag.minimumY</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.smoothed-prop" translate="no">drag.smoothed</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.target-prop" translate="no">drag.target</a></b></b> : Item</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#drag.threshold-prop" translate="no">drag.threshold</a></b></b> : real</li>
+</ul>
+</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#enabled-prop" translate="no">enabled</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#mouseX-prop" translate="no">mouseX</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#mouseY-prop" translate="no">mouseY</a></b></b> : real</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressAndHoldInterval-prop" translate="no">pressAndHoldInterval</a></b></b> : int</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressed-prop" translate="no">pressed</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressedButtons-prop" translate="no">pressedButtons</a></b></b> : MouseButtons</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#preventStealing-prop" translate="no">preventStealing</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a></b></b> : bool</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#scrollGestureEnabled-prop" translate="no">scrollGestureEnabled</a></b></b> : bool</li>
+</ul>
+<a name="signals"></a>
+<h2 id="signals">Signals</h2>
+<ul>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#canceled-signal" translate="no">canceled</a></b></b>()</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#clicked-signal" translate="no">clicked</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#doubleClicked-signal" translate="no">doubleClicked</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#entered-signal" translate="no">entered</a></b></b>()</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#exited-signal" translate="no">exited</a></b></b>()</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#positionChanged-signal" translate="no">positionChanged</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressAndHold-signal" translate="no">pressAndHold</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#pressed-signal" translate="no">pressed</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#released-signal" translate="no">released</a></b></b>(MouseEvent <i>mouse</i>)</li>
+<li class="fn" translate="no"><b><b><a href="qml-qtquick-mousearea.html#wheel-signal" translate="no">wheel</a></b></b>(WheelEvent <i>wheel</i>)</li>
+</ul>
+<!-- $$$MouseArea-description -->
+<a name="details"></a>
+<h2 id="details">Detailed Description</h2>
+<p>A MouseArea is an invisible item that is typically used in conjunction with a visible item in order to provide mouse handling for that item. By effectively acting as a proxy, the logic for mouse handling can be contained within a MouseArea item.</p>
+<p>The <a href="qml-qtquick-mousearea.html#enabled-prop" translate="no">enabled</a> property is used to enable and disable mouse handling for the proxied item. When disabled, the mouse area becomes transparent to mouse events.</p>
+<p>MouseArea is an invisible Item, but it has a visible property. When set to false, the mouse area becomes transparent to mouse events.</p>
+<p>The <a href="qml-qtquick-mousearea.html#pressed-signal" translate="no">pressed</a> read-only property indicates whether or not the user is holding down a mouse button over the mouse area. This property is often used in bindings between properties in a user interface. The <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> read-only property indicates the presence of the mouse cursor over the mouse area but, by default, only when a mouse button is held down; see the <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> documentation for details.</p>
+<p>Information about the mouse position and button clicks are provided via signals for which event handler properties are defined. The most commonly used involved handling mouse presses and clicks: onClicked, onDoubleClicked, onPressed, onReleased and onPressAndHold. It's also possible to handle mouse wheel events via the onWheel signal.</p>
+<p>If a MouseArea overlaps with the area of other MouseArea items, you can choose to propagate <code translate="no">clicked</code>, <code translate="no">doubleClicked</code> and <code translate="no">pressAndHold</code> events to these other items by setting <a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a> to true and rejecting events that should be propagated. See the <a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a> documentation for details.</p>
+<p>By default, MouseArea items only report mouse clicks and not changes to the position of the mouse cursor. Setting the <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> property ensures that handlers defined for onPositionChanged, onEntered and onExited are used and that the <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> property is updated even when no mouse buttons are pressed.</p>
+<a name="example-usage"></a>
+<h2 id="example-usage">Example Usage</h2>
+<div class="float-right"><p><img src="images/qml-mousearea-snippet.png" alt="" /></p>
+</div><p>The following example uses a MouseArea in a <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a> that changes the <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a> color to red when clicked:</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">color</span>: <span class="string">&quot;green&quot;</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">onClicked</span>: { <span class="name">parent</span>.<span class="name">color</span> <span class="operator">=</span> <span class="string">'red'</span> }
+ }
+ }
+</pre>
+<br style="clear: both" /><p>Many MouseArea signals pass a <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter that contains additional information about the mouse event, such as the position, button, and any key modifiers.</p>
+<p>Here is an extension of the previous example that produces a different color when the area is right clicked:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">color</span>: <span class="string">&quot;green&quot;</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">acceptedButtons</span>: <span class="name">Qt</span>.<span class="name">LeftButton</span> <span class="operator">|</span> <span class="name">Qt</span>.<span class="name">RightButton</span>
+ <span class="name">onClicked</span>: {
+ <span class="keyword">if</span> (<span class="name">mouse</span>.<span class="name">button</span> <span class="operator">==</span> <span class="name">Qt</span>.<span class="name">RightButton</span>)
+ <span class="name">parent</span>.<span class="name">color</span> <span class="operator">=</span> <span class="string">'blue'</span>;
+ <span class="keyword">else</span>
+ <span class="name">parent</span>.<span class="name">color</span> <span class="operator">=</span> <span class="string">'red'</span>;
+ }
+ }
+ }
+</pre>
+<p><b>See also </b><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a>, <a href="qtquick-mousearea-example.html" translate="no">MouseArea example</a>, and <a href="qtquick-input-topic.html" translate="no">Important Concepts In Qt Quick - User Input</a>.</p>
+<!-- @@@MouseArea -->
+<h2>Property Documentation</h2>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="mouseX-prop">
+<td class="tblQmlPropNode"><p>
+<a name="mouseX-prop"></a><span class="name">mouseX</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="mouseY-prop">
+<td class="tblQmlPropNode"><p>
+<a name="mouseY-prop"></a><span class="name">mouseY</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>These properties hold the coordinates of the mouse cursor.</p>
+<p>If the <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> property is false then these properties will only be valid while a button is pressed, and will remain valid as long as the button is held down even if the mouse is moved outside the area.</p>
+<p>By default, this property is false.</p>
+<p>If <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> is true then these properties will be valid when:</p>
+<ul>
+<li>no button is pressed, but the mouse is within the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> (<a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> is true).</li>
+<li>a button is pressed and held, even if it has since moved out of the area.</li>
+</ul>
+<p>The coordinates are relative to the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$acceptedButtons -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="acceptedButtons-prop">
+<td class="tblQmlPropNode"><p>
+<a name="acceptedButtons-prop"></a><span class="name">acceptedButtons</span> : <span class="type">Qt::MouseButtons</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the mouse buttons that the mouse area reacts to.</p>
+<p>To specify that the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> will react to multiple buttons, <a href="../qtcore/qt.html#MouseButton-enum" translate="no">Qt::MouseButtons</a> flag values are combined using the &quot;|&quot; (or) operator:</p>
+<pre class="cpp" translate="no">
+ MouseArea { acceptedButtons: <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>LeftButton <span class="operator">|</span> <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>RightButton }
+</pre>
+<p>To indicate that all possible mouse buttons are to be accepted, the special value 'Qt.AllButtons' may be used:</p>
+<pre class="cpp" translate="no">
+ MouseArea { acceptedButtons: <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>AllButtons }
+</pre>
+<p>The default value is <code translate="no">Qt.LeftButton</code>.</p>
+</div></div><!-- @@@acceptedButtons -->
+<br/>
+<!-- $$$containsMouse -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="containsMouse-prop">
+<td class="tblQmlPropNode"><p>
+<a name="containsMouse-prop"></a><span class="name">containsMouse</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the mouse is currently inside the mouse area.</p>
+<div class="admonition warning">
+<p><b>Warning: </b>If <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> is false, containsMouse will only be valid when the mouse is pressed while the mouse cursor is inside the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>.</p>
+</div>
+</div></div><!-- @@@containsMouse -->
+<br/>
+<!-- $$$containsPress -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="containsPress-prop">
+<td class="tblQmlPropNode"><p>
+<a name="containsPress-prop"></a><span class="name">containsPress</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This is a convenience property equivalent to <code translate="no">pressed &amp;&amp; containsMouse</code>, i.e&#x2e; it holds whether any of the <a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a> are currently pressed and the mouse is currently within the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>.</p>
+<p>This property is particularly useful for highlighting an item while the mouse is pressed within its bounds.</p>
+<p>This property was introduced in Qt 5.4.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#pressed-signal" translate="no">pressed</a> and <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a>.</p>
+</div></div><!-- @@@containsPress -->
+<br/>
+<!-- $$$cursorShape -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="cursorShape-prop">
+<td class="tblQmlPropNode"><p>
+<a name="cursorShape-prop"></a><span class="name">cursorShape</span> : <span class="type">Qt::CursorShape</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the cursor shape for this mouse area. Note that on platforms that do not display a mouse cursor this may have no effect.</p>
+<p>The available cursor shapes are:</p>
+<ul>
+<li>Qt.ArrowCursor</li>
+<li>Qt.UpArrowCursor</li>
+<li>Qt.CrossCursor</li>
+<li>Qt.WaitCursor</li>
+<li>Qt.IBeamCursor</li>
+<li>Qt.SizeVerCursor</li>
+<li>Qt.SizeHorCursor</li>
+<li>Qt.SizeBDiagCursor</li>
+<li>Qt.SizeFDiagCursor</li>
+<li>Qt.SizeAllCursor</li>
+<li>Qt.BlankCursor</li>
+<li>Qt.SplitVCursor</li>
+<li>Qt.SplitHCursor</li>
+<li>Qt.PointingHandCursor</li>
+<li>Qt.ForbiddenCursor</li>
+<li>Qt.WhatsThisCursor</li>
+<li>Qt.BusyCursor</li>
+<li>Qt.OpenHandCursor</li>
+<li>Qt.ClosedHandCursor</li>
+<li>Qt.DragCopyCursor</li>
+<li>Qt.DragMoveCursor</li>
+<li>Qt.DragLinkCursor</li>
+</ul>
+<p>In order to only set a mouse cursor shape for a region without reacting to mouse events set the <a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a> to none:</p>
+<pre class="cpp" translate="no">
+ MouseArea { cursorShape: <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>IBeamCursor; acceptedButtons: <span class="type"><a href="../qtqml/qml-qtqml-qt.html" translate="no">Qt</a></span><span class="operator">.</span>NoButton }
+</pre>
+<p>The default value is <code translate="no">Qt.ArrowCursor</code>.</p>
+<p><b>See also </b><a href="../qtcore/qt.html#CursorShape-enum" translate="no">Qt::CursorShape</a>.</p>
+</div></div><!-- @@@cursorShape -->
+<br/>
+<!-- $$$drag -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="even" id="drag-prop"><th class="centerAlign"><p><a name="drag-prop"></a><b>drag group</b></p></th></tr>
+<tr valign="top" class="odd" id="drag.active-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.active-prop"></a><span class="name">drag.active</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.axis-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.axis-prop"></a><span class="name">drag.axis</span> : <span class="type"><a href="../qtqml/qml-enumeration.html" translate="no">enumeration</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.filterChildren-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.filterChildren-prop"></a><span class="name">drag.filterChildren</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.maximumX-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.maximumX-prop"></a><span class="name">drag.maximumX</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.maximumY-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.maximumY-prop"></a><span class="name">drag.maximumY</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.minimumX-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.minimumX-prop"></a><span class="name">drag.minimumX</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.minimumY-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.minimumY-prop"></a><span class="name">drag.minimumY</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.smoothed-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.smoothed-prop"></a><span class="name">drag.smoothed</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.target-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.target-prop"></a><span class="name">drag.target</span> : <span class="type"><a href="qml-qtquick-item.html" translate="no">Item</a></span></p></td></tr>
+<tr valign="top" class="odd" id="drag.threshold-prop">
+<td class="tblQmlPropNode"><p>
+<a name="drag.threshold-prop"></a><span class="name">drag.threshold</span> : <span class="type"><a href="../qtqml/qml-real.html" translate="no">real</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p><code translate="no">drag</code> provides a convenient way to make an item draggable.</p>
+<ul>
+<li><code translate="no">drag.target</code> specifies the id of the item to drag.</li>
+<li><code translate="no">drag.active</code> specifies if the target item is currently being dragged.</li>
+<li><code translate="no">drag.axis</code> specifies whether dragging can be done horizontally (<code translate="no">Drag.XAxis</code>), vertically (<code translate="no">Drag.YAxis</code>), or both (<code translate="no">Drag.XAndYAxis</code>)</li>
+<li><code translate="no">drag.minimum</code> and <code translate="no">drag.maximum</code> limit how far the target can be dragged along the corresponding axes.</li>
+</ul>
+<p>The following example displays a <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a> that can be dragged along the X-axis. The opacity of the rectangle is reduced when it is dragged to the right.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">container</span>
+ <span class="name">width</span>: <span class="number">600</span>; <span class="name">height</span>: <span class="number">200</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">id</span>: <span class="name">rect</span>
+ <span class="name">width</span>: <span class="number">50</span>; <span class="name">height</span>: <span class="number">50</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="name">opacity</span>: (<span class="number">600.0</span> <span class="operator">-</span> <span class="name">rect</span>.<span class="name">x</span>) <span class="operator">/</span> <span class="number">600</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">drag</span>.target: <span class="name">rect</span>
+ <span class="name">drag</span>.axis: <span class="name">Drag</span>.<span class="name">XAxis</span>
+ <span class="name">drag</span>.minimumX: <span class="number">0</span>
+ <span class="name">drag</span>.maximumX: <span class="name">container</span>.<span class="name">width</span> <span class="operator">-</span> <span class="name">rect</span>.<span class="name">width</span>
+ }
+ }
+ }
+</pre>
+<div class="admonition note">
+<p><b>Note: </b>Items cannot be dragged if they are anchored for the requested <code translate="no">drag.axis</code>. For example, if <code translate="no">anchors.left</code> or <code translate="no">anchors.right</code> was set for <code translate="no">rect</code> in the above example, it cannot be dragged along the X-axis. This can be avoided by settng the anchor value to <code translate="no">undefined</code> in an <a href="qml-qtquick-mousearea.html#pressed-signal" translate="no">onPressed</a> handler.</p>
+</div>
+<p>If <code translate="no">drag.filterChildren</code> is set to true, a drag can override descendant MouseAreas. This enables a parent <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> to handle drags, for example, while descendants handle clicks:</p>
+<p><code translate="no">drag.threshold</code> determines the threshold in pixels of when the drag operation should start. By default this is bound to a platform dependent value. This property was added in Qt Quick 2.2&#x2e;</p>
+<p>If <code translate="no">drag.smoothed</code> is <code translate="no">true</code>, the target will be moved only after the drag operation has started. If set to <code translate="no">false</code>, the target will be moved straight to the current mouse position. By default, this property is <code translate="no">true</code>. This property was added in Qt Quick 2.4</p>
+<p>See the <a href="qml-qtquick-drag.html" translate="no">Drag</a> attached property and <a href="qml-qtquick-droparea.html" translate="no">DropArea</a> if you want to make a drop.</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">480</span>
+ <span class="name">height</span>: <span class="number">320</span>
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">x</span>: <span class="number">30</span>; <span class="name">y</span>: <span class="number">30</span>
+ <span class="name">width</span>: <span class="number">300</span>; <span class="name">height</span>: <span class="number">240</span>
+ <span class="name">color</span>: <span class="string">&quot;lightsteelblue&quot;</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">drag</span>.target: <span class="name">parent</span>;
+ <span class="name">drag</span>.axis: <span class="string">&quot;XAxis&quot;</span>
+ <span class="name">drag</span>.minimumX: <span class="number">30</span>
+ <span class="name">drag</span>.maximumX: <span class="number">150</span>
+ <span class="name">drag</span>.filterChildren: <span class="number">true</span>
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;yellow&quot;</span>
+ <span class="name">x</span>: <span class="number">50</span>; <span class="name">y</span> : <span class="number">50</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">onClicked</span>: <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;Clicked&quot;</span>)
+ }
+ }
+ }
+ }
+ }
+</pre>
+</div></div><!-- @@@drag -->
+<br/>
+<!-- $$$enabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="enabled-prop">
+<td class="tblQmlPropNode"><p>
+<a name="enabled-prop"></a><span class="name">enabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the item accepts mouse events.</p>
+<div class="admonition note">
+<p><b>Note: </b>Due to historical reasons, this property is not equivalent to Item.enabled. It only affects mouse events, and its effect does not propagate to child items.</p>
+</div>
+<p>By default, this property is true.</p>
+</div></div><!-- @@@enabled -->
+<br/>
+<!-- $$$hoverEnabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="hoverEnabled-prop">
+<td class="tblQmlPropNode"><p>
+<a name="hoverEnabled-prop"></a><span class="name">hoverEnabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether hover events are handled.</p>
+<p>By default, mouse events are only handled in response to a button event, or when a button is pressed. Hover enables handling of all mouse events even when no mouse button is pressed.</p>
+<p>This property affects the <a href="qml-qtquick-mousearea.html#containsMouse-prop" translate="no">containsMouse</a> property and the onEntered, onExited and onPositionChanged signals.</p>
+</div></div><!-- @@@hoverEnabled -->
+<br/>
+<!-- $$$pressAndHoldInterval -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressAndHoldInterval-prop">
+<td class="tblQmlPropNode"><p>
+<a name="pressAndHoldInterval-prop"></a><span class="name">pressAndHoldInterval</span> : <span class="type"><a href="../qtqml/qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property overrides the elapsed time in milliseconds before <code translate="no">pressAndHold</code> is emitted.</p>
+<p>If not explicitly set -- or after reset -- the value follows <code translate="no">QStyleHints::mousePressAndHoldInterval</code>.</p>
+<p>Typically it's sufficient to set this property globally using the application style hint. This property should be used when varying intervals are needed for certain MouseAreas.</p>
+<p>This property was introduced in Qt 5.9.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#pressAndHold-signal" translate="no">pressAndHold</a>.</p>
+</div></div><!-- @@@pressAndHoldInterval -->
+<br/>
+<!-- $$$pressed -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressed-prop">
+<td class="tblQmlPropNode"><p>
+<a name="pressed-prop"></a><span class="name">pressed</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether any of the <a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a> are currently pressed.</p>
+</div></div><!-- @@@pressed -->
+<br/>
+<!-- $$$pressedButtons -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressedButtons-prop">
+<td class="tblQmlPropNode"><p>
+<a name="pressedButtons-prop"></a><span class="name">pressedButtons</span> : <span class="type">MouseButtons</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds the mouse buttons currently pressed.</p>
+<p>It contains a bitwise combination of:</p>
+<ul>
+<li>Qt.LeftButton</li>
+<li>Qt.RightButton</li>
+<li>Qt.MiddleButton</li>
+</ul>
+<p>The code below displays &quot;right&quot; when the right mouse buttons is pressed:</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-text.html" translate="no">Text</a></span> {
+ <span class="name">text</span>: <span class="name">mouseArea</span>.<span class="name">pressedButtons</span> <span class="operator">&amp;</span> <span class="name">Qt</span>.<span class="name">RightButton</span> ? <span class="string">&quot;right&quot;</span> : <span class="string">&quot;&quot;</span>
+ <span class="name">horizontalAlignment</span>: <span class="name">Text</span>.<span class="name">AlignHCenter</span>
+ <span class="name">verticalAlignment</span>: <span class="name">Text</span>.<span class="name">AlignVCenter</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">id</span>: <span class="name">mouseArea</span>
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">acceptedButtons</span>: <span class="name">Qt</span>.<span class="name">LeftButton</span> <span class="operator">|</span> <span class="name">Qt</span>.<span class="name">RightButton</span>
+ }
+ }
+</pre>
+<div class="admonition note">
+<p><b>Note: </b>this property only handles buttons specified in <a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a>.</p>
+</div>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#acceptedButtons-prop" translate="no">acceptedButtons</a>.</p>
+</div></div><!-- @@@pressedButtons -->
+<br/>
+<!-- $$$preventStealing -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="preventStealing-prop">
+<td class="tblQmlPropNode"><p>
+<a name="preventStealing-prop"></a><span class="name">preventStealing</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether the mouse events may be stolen from this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>.</p>
+<p>If a <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> is placed within an item that filters child mouse events, such as Flickable, the mouse events may be stolen from the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> if a gesture is recognized by the parent item, e.g&#x2e; a flick gesture. If preventStealing is set to true, no item will steal the mouse events.</p>
+<p>Note that setting preventStealing to true once an item has started stealing events will have no effect until the next press event.</p>
+<p>By default this property is false.</p>
+</div></div><!-- @@@preventStealing -->
+<br/>
+<!-- $$$propagateComposedEvents -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="propagateComposedEvents-prop">
+<td class="tblQmlPropNode"><p>
+<a name="propagateComposedEvents-prop"></a><span class="name">propagateComposedEvents</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property holds whether composed mouse events will automatically propagate to other MouseAreas that overlap with this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> but are lower in the visual stacking order. By default, this property is false.</p>
+<p><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> contains several composed events: <code translate="no">clicked</code>, <code translate="no">doubleClicked</code> and <code translate="no">pressAndHold</code>. These are composed of basic mouse events, like <code translate="no">pressed</code>, and can be propagated differently in comparison to basic events.</p>
+<p>If propagateComposedEvents is set to true, then composed events will be automatically propagated to other MouseAreas in the same location in the scene. Each event is propagated to the next <a href="qml-qtquick-mousearea.html#enabled-prop" translate="no">enabled</a> <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> beneath it in the stacking order, propagating down this visual hierarchy until a <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> accepts the event. Unlike <code translate="no">pressed</code> events, composed events will not be automatically accepted if no handler is present.</p>
+<p>For example, below is a yellow <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a> that contains a blue <a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a>. The blue rectangle is the top-most item in the hierarchy of the visual stacking order; it will visually rendered above the yellow rectangle. Since the blue rectangle sets propagateComposedEvents to true, and also sets <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">MouseEvent::accepted</a> to false for all received <code translate="no">clicked</code> events, any <code translate="no">clicked</code> events it receives are propagated to the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> of the yellow rectangle beneath it.</p>
+<pre class="qml" translate="no">
+ import QtQuick 2.0
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;yellow&quot;</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">onClicked</span>: <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;clicked yellow&quot;</span>)
+ }
+
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="name">width</span>: <span class="number">50</span>; <span class="name">height</span>: <span class="number">50</span>
+
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">propagateComposedEvents</span>: <span class="number">true</span>
+ <span class="name">onClicked</span>: {
+ <span class="name">console</span>.<span class="name">log</span>(<span class="string">&quot;clicked blue&quot;</span>)
+ <span class="name">mouse</span>.<span class="name">accepted</span> <span class="operator">=</span> <span class="number">false</span>
+ }
+ }
+ }
+ }
+</pre>
+<p>Clicking on the blue rectangle will cause the <code translate="no">onClicked</code> handler of its child <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> to be invoked; the event will then be propagated to the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> of the yellow rectangle, causing its own <code translate="no">onClicked</code> handler to be invoked.</p>
+<p>This property greatly simplifies the usecase of when you want to have overlapping MouseAreas handling the composed events together. For example: if you want one <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> to handle <code translate="no">clicked</code> signals and the other to handle <code translate="no">pressAndHold</code>, or if you want one <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> to handle <code translate="no">clicked</code> most of the time, but pass it through when certain conditions are met.</p>
+</div></div><!-- @@@propagateComposedEvents -->
+<br/>
+<!-- $$$scrollGestureEnabled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="scrollGestureEnabled-prop">
+<td class="tblQmlPropNode"><p>
+<a name="scrollGestureEnabled-prop"></a><span class="name">scrollGestureEnabled</span> : <span class="type"><a href="../qtqml/qml-bool.html" translate="no">bool</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This property controls whether this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> responds to scroll gestures from non-mouse devices, such as the 2-finger flick gesture on a trackpad. If set to false, the <a href="qml-qtquick-mousearea.html#wheel-signal" translate="no">wheel</a> signal be emitted only when the wheel event comes from an actual mouse with a wheel, while scroll gesture events will pass through to any other Item that will handle them. For example, the user might perform a flick gesture while the cursor is over an item containing a <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>, intending to interact with a Flickable which is underneath. Setting this property to false will allow the <a href="qml-qtquick-pincharea.html" translate="no">PinchArea</a> to handle the mouse wheel or the pinch gesture, while the Flickable handles the flick gesture.</p>
+<p>By default, this property is true.</p>
+<p>This property was introduced in Qt 5.5.</p>
+</div></div><!-- @@@scrollGestureEnabled -->
+<br/>
+<h2>Signal Documentation</h2>
+<!-- $$$canceled[overload1]$$$canceled -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="canceled-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="canceled-signal"></a><span class="name">canceled</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when mouse events have been canceled, because another item stole the mouse event handling.</p>
+<p>This signal is for advanced use: it is useful when there is more than one <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> that is handling input, or when there is a <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> inside a <a href="qml-qtquick-flickable.html" translate="no">Flickable</a>. In the latter case, if you execute some logic in the <code translate="no">onPressed</code> signal handler and then start dragging, the <a href="qml-qtquick-flickable.html" translate="no">Flickable</a> will steal the mouse handling from the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>. In these cases, to reset the logic when the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> has lost the mouse handling to the <a href="qml-qtquick-flickable.html" translate="no">Flickable</a>, <code translate="no">canceled</code> should be handled in addition to <a href="qml-qtquick-mousearea.html#released-signal" translate="no">released</a>.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onCanceled</code>.</p>
+</div></div><!-- @@@canceled -->
+<br/>
+<!-- $$$clicked[overload1]$$$clickedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="clicked-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="clicked-signal"></a><span class="name">clicked</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a click. A click is defined as a press followed by a release, both inside the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> (pressing, moving outside the <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a>, and then moving back inside and releasing is also considered a click).</p>
+<p>The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the click, including the x and y position of the release of the click, and whether the click was held.</p>
+<p>When handling this signal, changing the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter has no effect, unless the <a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a> property is <code translate="no">true</code>.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onClicked</code>.</p>
+</div></div><!-- @@@clicked -->
+<br/>
+<!-- $$$doubleClicked[overload1]$$$doubleClickedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="doubleClicked-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="doubleClicked-signal"></a><span class="name">doubleClicked</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a double-click (a press followed by a release followed by a press). The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the click, including the x and y position of the release of the click, and whether the click was held.</p>
+<p>When handling this signal, if the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter is set to false, the pressed/released/clicked signals will be emitted for the second click; otherwise they are suppressed. The <code translate="no">accepted</code> property defaults to true.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onDoubleClicked</code>.</p>
+</div></div><!-- @@@doubleClicked -->
+<br/>
+<!-- $$$entered[overload1]$$$entered -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="entered-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="entered-signal"></a><span class="name">entered</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when the mouse enters the mouse area.</p>
+<p>By default this signal is only emitted if a button is currently pressed. Set <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> to true to emit this signal even when no mouse button is pressed.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onEntered</code>.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a>.</p>
+</div></div><!-- @@@entered -->
+<br/>
+<!-- $$$exited[overload1]$$$exited -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="exited-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="exited-signal"></a><span class="name">exited</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when the mouse exits the mouse area.</p>
+<p>By default this signal is only emitted if a button is currently pressed. Set <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> to true to emit this signal even when no mouse button is pressed.</p>
+<p>The example below shows a fairly typical relationship between two MouseAreas, with <code translate="no">mouseArea2</code> on top of <code translate="no">mouseArea1</code>. Moving the mouse into <code translate="no">mouseArea2</code> from <code translate="no">mouseArea1</code> will cause <code translate="no">mouseArea1</code> to emit the <code translate="no">exited</code> signal.</p>
+<pre class="qml" translate="no">
+ <span class="type"><a href="qml-qtquick-rectangle.html" translate="no">Rectangle</a></span> {
+ <span class="name">width</span>: <span class="number">400</span>; <span class="name">height</span>: <span class="number">400</span>
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">id</span>: <span class="name">mouseArea1</span>
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">hoverEnabled</span>: <span class="number">true</span>
+ }
+ <span class="type"><a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a></span> {
+ <span class="name">id</span>: <span class="name">mouseArea2</span>
+ <span class="name">width</span>: <span class="number">100</span>; <span class="name">height</span>: <span class="number">100</span>
+ <span class="name">anchors</span>.centerIn: <span class="name">parent</span>
+ <span class="name">hoverEnabled</span>: <span class="number">true</span>
+ }
+ }
+</pre>
+<p>If instead you give the two MouseAreas a parent-child relationship, moving the mouse into <code translate="no">mouseArea2</code> from <code translate="no">mouseArea1</code> will <b>not</b> cause <code translate="no">mouseArea1</code> to emit <code translate="no">exited</code>. Instead, they will both be considered to be simultaneously hovered.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onExited</code>.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a>.</p>
+</div></div><!-- @@@exited -->
+<br/>
+<!-- $$$positionChanged[overload1]$$$positionChangedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="positionChanged-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="positionChanged-signal"></a><span class="name">positionChanged</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when the mouse position changes.</p>
+<p>The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the mouse, including the x and y position, and any buttons currently pressed.</p>
+<p>By default this signal is only emitted if a button is currently pressed. Set <a href="qml-qtquick-mousearea.html#hoverEnabled-prop" translate="no">hoverEnabled</a> to true to emit this signal even when no mouse button is pressed.</p>
+<p>When handling this signal, changing the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter has no effect.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onPositionChanged</code>.</p>
+</div></div><!-- @@@positionChanged -->
+<br/>
+<!-- $$$pressAndHold[overload1]$$$pressAndHoldMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressAndHold-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="pressAndHold-signal"></a><span class="name">pressAndHold</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a long press (currently 800ms). The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the press, including the x and y position of the press, and which button is pressed.</p>
+<p>When handling this signal, changing the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter has no effect, unless the <a href="qml-qtquick-mousearea.html#propagateComposedEvents-prop" translate="no">propagateComposedEvents</a> property is <code translate="no">true</code>.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onPressAndHold</code>.</p>
+</div></div><!-- @@@pressAndHold -->
+<br/>
+<!-- $$$pressed[overload1]$$$pressedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="pressed-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="pressed-signal"></a><span class="name">pressed</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a press. The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the press, including the x and y position and which button was pressed.</p>
+<p>When handling this signal, use the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter to control whether this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> handles the press and all future mouse events until release. The default is to accept the event and not allow other MouseAreas beneath this one to handle the event. If <i>accepted</i> is set to false, no further events will be sent to this <a href="qml-qtquick-mousearea.html" translate="no">MouseArea</a> until the button is next pressed.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onPressed</code>.</p>
+</div></div><!-- @@@pressed -->
+<br/>
+<!-- $$$released[overload1]$$$releasedMouseEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="released-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="released-signal"></a><span class="name">released</span>(<span class="type"><a href="qml-qtquick-mouseevent.html" translate="no">MouseEvent</a></span> <i>mouse</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when there is a release. The <a href="qml-qtquick-mouseevent.html" translate="no">mouse</a> parameter provides information about the click, including the x and y position of the release of the click, and whether the click was held.</p>
+<p>When handling this signal, changing the <a href="qml-qtquick-mouseevent.html#accepted-prop" translate="no">accepted</a> property of the <i translate="no">mouse</i> parameter has no effect.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onReleased</code>.</p>
+<p><b>See also </b><a href="qml-qtquick-mousearea.html#canceled-signal" translate="no">canceled</a>.</p>
+</div></div><!-- @@@released -->
+<br/>
+<!-- $$$wheel[overload1]$$$wheelWheelEvent -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="wheel-signal">
+<td class="tblQmlFuncNode"><p>
+<a name="wheel-signal"></a><span class="name">wheel</span>(<span class="type"><a href="qml-qtquick-wheelevent.html" translate="no">WheelEvent</a></span> <i>wheel</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted in response to both mouse wheel and trackpad scroll gestures.</p>
+<p>The <i translate="no">wheel</i> parameter provides information about the event, including the x and y position, any buttons currently pressed, and information about the wheel movement, including angleDelta and pixelDelta.</p>
+<p><b>Note: </b>The corresponding handler is <code translate="no">onWheel</code>.</p>
+</div></div><!-- @@@wheel -->
+<br/>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="footer">
+ <p>
+ <acronym title="Copyright">&copy;</acronym> 2023 The Qt Company Ltd.
+ Documentation contributions included herein are the copyrights of
+ their respective owners.<br/> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation.<br/> Qt and respective logos are <a href="https://doc.qt.io/qt/trademarks.html"> trademarks</a> of The Qt Company Ltd. in Finland and/or other countries
+ worldwide. All other trademarks are property of their respective owners. </p>
+</div>
+</body>
+</html>
diff --git a/tests/auto/qmlls/utils/data/qualifiedModule.qml b/tests/auto/qmlls/utils/data/qualifiedModule.qml
new file mode 100644
index 0000000000..4695f57d3f
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/qualifiedModule.qml
@@ -0,0 +1,7 @@
+import QtQuick as QQ
+
+QQ.Item {
+ property Item helloWorld: QQ.Item {}
+ QQ.
+
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/BaseType.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/BaseType.qml
new file mode 100644
index 0000000000..210f55519d
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/BaseType.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function helloMethod() {}
+ property int helloProperty
+ signal helloSignal
+ enum HelloEnum { HelloEnumValue1, HelloEnumValue2, HelloEnumValue3 }
+ property font helloFont
+}
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/BindingsOnDeferred.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/BindingsOnDeferred.qml
new file mode 100644
index 0000000000..f0d82f3318
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/BindingsOnDeferred.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls.Basic
+
+Item {
+ property var c: Control {
+ id: inner
+
+ property var myBinding: Binding {
+ inner.background: Item {}
+
+ inner {
+ background: Item {}
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/Derived1.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/Derived1.qml
new file mode 100644
index 0000000000..687c190094
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/Derived1.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+BaseType {
+
+}
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/Derived2.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/Derived2.qml
new file mode 100644
index 0000000000..3208d97778
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/Derived2.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+Derived1 {
+
+}
diff --git a/tests/auto/qmlls/utils/data/resolveExpressionType/DerivedType.qml b/tests/auto/qmlls/utils/data/resolveExpressionType/DerivedType.qml
new file mode 100644
index 0000000000..ab0b542ce8
--- /dev/null
+++ b/tests/auto/qmlls/utils/data/resolveExpressionType/DerivedType.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Derived2 {
+ id: self
+ function f() {
+ helloMethod()
+ let x = helloProperty;
+ helloSignal()
+ let y = HelloEnum.HelloEnumValue1
+ }
+
+ property var someMethod: helloMethod()
+ property int someProperty: helloProperty
+ property var someEnum: HelloEnum.HelloEnumValue2
+ function someHandler() { helloSignal(); }
+ onHelloSignal: someHandler
+
+ Item {
+ property var someMethod: self.helloMethod()
+ property int someProperty: self.helloProperty
+ function f() {
+ self.helloSignal()
+ }
+ }
+ helloFont.family: "helloFamily"
+ Keys.onBackPressed: someHandler
+
+ helloProperty: 42
+}
diff --git a/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp b/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp
new file mode 100644
index 0000000000..f21de11990
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp
@@ -0,0 +1,650 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_highlighting.h"
+
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlLS/private/qqmlsemantictokens_p.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
+
+#include <qlist.h>
+
+using namespace QLspSpecification;
+
+tst_qmlls_highlighting::tst_qmlls_highlighting()
+ : QQmlDataTest(QT_QMLLS_HIGHLIGHTS_DATADIR) , m_highlightingDataDir(QT_QMLLS_HIGHLIGHTS_DATADIR + "/highlights"_L1)
+{
+}
+
+// Token encoding as in:
+// https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#textDocument_semanticTokens
+void tst_qmlls_highlighting::encodeSemanticTokens_data()
+{
+ QTest::addColumn<Highlights>("highlights");
+ QTest::addColumn<QList<int>>("expectedMemoryLayout");
+
+ {
+ Highlights c;
+ c.highlights().insert(0, Token());
+ QTest::addRow("empty-token-single") << c << QList {0, 0, 0, 0, 0};
+ }
+ {
+ Highlights c;
+ QQmlJS::SourceLocation loc(0, 1, 1, 1);
+ c.highlights().insert(0, Token(loc, 0, 0));
+ QTest::addRow("single-token") << c << QList {0, 0, 1, 0, 0};
+ }
+ {
+ Highlights c;
+ Token t1(QQmlJS::SourceLocation(0, 1, 1, 1), 0, 0);
+ Token t2(QQmlJS::SourceLocation(1, 1, 3, 3), 0, 0);
+ c.highlights().insert(t1.offset, t1);
+ c.highlights().insert(t2.offset, t2);
+ QTest::addRow("different-lines") << c << QList {0, 0, 1, 0, 0, 2, 2, 1, 0, 0};
+ }
+ {
+ Highlights c;
+ Token t1(QQmlJS::SourceLocation(0, 1, 1, 1), 0, 0);
+ Token t2(QQmlJS::SourceLocation(1, 1, 1, 3), 0, 0);
+ c.highlights().insert(t1.offset, t1);
+ c.highlights().insert(t2.offset, t2);
+ QTest::addRow("same-line-different-column") << c << QList {0, 0, 1, 0, 0, 0, 2, 1, 0, 0};
+ }
+ {
+ Highlights c;
+ Token t1(QQmlJS::SourceLocation(0, 1, 1, 1), 1, 0);
+ c.highlights().insert(t1.offset, t1);
+ QTest::addRow("token-type") << c << QList {0, 0, 1, 1, 0};
+ }
+ {
+ Highlights c;
+ Token t1(QQmlJS::SourceLocation(0, 1, 1, 1), 1, 1);
+ c.highlights().insert(t1.offset, t1);
+ QTest::addRow("token-modifier") << c << QList {0, 0, 1, 1, 1};
+ }
+}
+
+void tst_qmlls_highlighting::encodeSemanticTokens()
+{
+ QFETCH(Highlights, highlights);
+ QFETCH(QList<int>, expectedMemoryLayout);
+ const auto encoded = HighlightingUtils::encodeSemanticTokens(highlights);
+ QCOMPARE(encoded, expectedMemoryLayout);
+}
+
+struct LineLength
+{
+ quint32 startLine;
+ quint32 length;
+};
+
+void tst_qmlls_highlighting::sourceLocationsFromMultiLineToken_data()
+{
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QList<LineLength>>("expectedLines");
+
+ QTest::addRow("multilineComment1") << R"("line 1
+line 2
+line 3 ")" << QList{ LineLength{ 1, 7 }, LineLength{ 2, 6 }, LineLength{ 3, 8 } };
+
+ QTest::addRow("prePostNewlines") <<
+ R"("
+
+")" << QList{ LineLength{ 1, 1 }, LineLength{ 2, 0 }, LineLength{ 3, 1 } };
+ QTest::addRow("windows-newline")
+ << QString::fromUtf8("\"test\r\nwindows\r\nnewline\"")
+ << QList{ LineLength{ 1, 5 }, LineLength{ 2, 7 }, LineLength{ 3, 8 } };
+}
+
+void tst_qmlls_highlighting::sourceLocationsFromMultiLineToken()
+{
+ QFETCH(QString, source);
+ QFETCH(QList<LineLength>, expectedLines);
+ using namespace QQmlJS::AST;
+
+ QQmlJS::Engine jsEngine;
+ QQmlJS::Lexer lexer(&jsEngine);
+ lexer.setCode(source, 1, true);
+ QQmlJS::Parser parser(&jsEngine);
+ parser.parseExpression();
+ const auto expression = parser.expression();
+
+ auto *literal = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expression);
+ const auto locs =
+ HighlightingUtils::sourceLocationsFromMultiLineToken(source, literal->literalToken);
+
+ [&]() {
+ QCOMPARE(locs.size(), expectedLines.size());
+
+ for (auto i = 0; i < locs.size(); ++i) {
+ QCOMPARE(locs[i].startLine, expectedLines[i].startLine);
+ QCOMPARE(locs[i].length, expectedLines[i].length);
+ }
+ }();
+
+ if (QTest::currentTestFailed()) {
+
+ qDebug() << "Actual locations";
+ for (auto i = 0; i < locs.size(); ++i) {
+ qDebug() << "Startline :" << locs[i].startLine << "Length " << locs[i].length;
+ }
+
+ qDebug() << "Expected locations";
+ for (auto i = 0; i < expectedLines.size(); ++i) {
+ qDebug() << "Startline :" << expectedLines[i].startLine
+ << "Length :" << expectedLines[i].length;
+ }
+ }
+}
+
+void tst_qmlls_highlighting::highlights_data()
+{
+ using namespace QQmlJS::Dom;
+ QTest::addColumn<DomItem>("fileItem");
+ QTest::addColumn<Token>("expectedHighlightedToken");
+
+ const auto fileObject = [](const QString &filePath){
+ QFile f(filePath);
+ DomItem file;
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ return file;
+ QString code = f.readAll();
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+ options.setFlag(DomCreationOption::WithRecovery);
+
+ QStringList dirs = {QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)};
+ auto envPtr = DomEnvironment::create(dirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies, options);
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromMemory(envPtr, filePath, code),
+ [&file](Path, const DomItem &, const DomItem &newIt) {
+ file = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+ return file;
+ };
+
+ { // Comments
+ const auto filePath = m_highlightingDataDir + "/comments.qml";
+ const auto fileItem = fileObject(filePath);
+ // Copyright (C) 2023 The Qt Company Ltd.
+ QTest::addRow("single-line-1")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(0, 41, 1, 1), int(SemanticTokenTypes::Comment), 0);
+
+ /* single line comment */
+ QTest::addRow("single-line-2") << fileItem
+ << Token(QQmlJS::SourceLocation(162, 28, 9, 1),
+ int(SemanticTokenTypes::Comment), 0);
+
+ // Multiline comments are split into multiple locations
+ QTest::addRow("multiline-first-line")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(133, 2, 5, 1), int(SemanticTokenTypes::Comment), 0);
+ QTest::addRow("multiline-second-line") << fileItem
+ << Token(QQmlJS::SourceLocation(136, 21, 6, 1),
+ int(SemanticTokenTypes::Comment), 0);
+ QTest::addRow("multiline-third-line")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(158, 2, 7, 1), int(SemanticTokenTypes::Comment), 0);
+
+ // Comments Inside Js blocks
+ QTest::addRow("inside-js") << fileItem
+ << Token(QQmlJS::SourceLocation(232, 5, 13, 9),
+ int(SemanticTokenTypes::Comment), 0);
+ }
+ { // Imports
+ const auto filePath = m_highlightingDataDir + "/imports.qml";
+ const auto fileItem = fileObject(filePath);
+ QTest::addRow("import-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(112, 6, 4, 1), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("module-uri") << fileItem
+ << Token(QQmlJS::SourceLocation(119, 7, 4, 8),
+ int(SemanticTokenTypes::Namespace), 0);
+ QTest::addRow("directory-uri")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(152, 3, 6, 8), int(SemanticTokenTypes::String), 0);
+ QTest::addRow("as-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(156, 2, 6, 12),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("version-number")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(140, 4, 5, 14), int(SemanticTokenTypes::Number), 0);
+ QTest::addRow("qualified-namespace") << fileItem
+ << Token(QQmlJS::SourceLocation(159, 6, 6, 15),
+ int(SemanticTokenTypes::Namespace), 0);
+ }
+ { // Bindings
+ const auto filePath = m_highlightingDataDir + "/bindings.qml";
+ const auto fileItem = fileObject(filePath);
+
+ // normal binding
+ QTest::addRow("normalBinding") << fileItem
+ << Token(QQmlJS::SourceLocation(189, 1, 11, 5),
+ int(SemanticTokenTypes::Property), 0);
+ // on binding
+ QTest::addRow("on-binding") << fileItem
+ << Token(QQmlJS::SourceLocation(175, 5, 9, 17),
+ int(SemanticTokenTypes::Property), 0);
+ QTest::addRow("on-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(172, 2, 9, 14),
+ int(SemanticTokenTypes::Keyword), 0);
+ }
+ { // Pragmas
+ const auto filePath = m_highlightingDataDir + "/pragmas.qml";
+ const auto fileItem = fileObject(filePath);
+ QTest::addRow("pragma-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(112, 6, 4, 1), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("pragma-name") << fileItem
+ << Token(QQmlJS::SourceLocation(136, 25, 5, 8),
+ int(SemanticTokenTypes::Variable), 0);
+ QTest::addRow("pragma-value") << fileItem
+ << Token(QQmlJS::SourceLocation(198, 4, 6, 27),
+ int(SemanticTokenTypes::Variable), 0);
+ }
+ { // Enums
+ const auto filePath = m_highlightingDataDir + "/enums.qml";
+ const auto fileItem = fileObject(filePath);
+ QTest::addRow("enum-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(141, 4, 7, 5), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("enum-name")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(146, 3, 7, 10), int(SemanticTokenTypes::Enum), 0);
+ QTest::addRow("enum-item") << fileItem
+ << Token(QQmlJS::SourceLocation(160, 3, 8, 9),
+ int(SemanticTokenTypes::EnumMember), 0);
+ QTest::addRow("enum-value")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(179, 1, 9, 15), int(SemanticTokenTypes::Number), 0);
+ }
+ { // objects and inline components
+ const auto filePath = m_highlightingDataDir + "/objectAndComponent.qml";
+ const auto fileItem = fileObject(filePath);
+
+ // object
+ QTest::addRow("object-identifier")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(169, 4, 8, 5), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("object-id-property") << fileItem
+ << Token(QQmlJS::SourceLocation(184, 2, 9, 9),
+ int(SemanticTokenTypes::Property), 0);
+ QTest::addRow("object-id-name") << fileItem
+ << Token(QQmlJS::SourceLocation(188, 5, 9, 13),
+ int(SemanticTokenTypes::Variable), 0);
+
+ // component
+ QTest::addRow("component-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(139, 9, 7, 5), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("component-name")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(149, 6, 7, 15), int(SemanticTokenTypes::Type), 0);
+ }
+ { // property definition
+ const auto filePath = m_highlightingDataDir + "/properties.qml";
+ const auto fileItem = fileObject(filePath);
+
+ int definitionModifier = 1 << int(SemanticTokenModifiers::Definition);
+ QTest::addRow("property-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(154, 8, 8, 9), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("property-type")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(163, 3, 8, 18), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("property-name")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(167, 1, 8, 22), int(SemanticTokenTypes::Property),
+ definitionModifier);
+ int readOnlyModifier = definitionModifier | (1 << int(SemanticTokenModifiers::Readonly));
+ QTest::addRow("readonly-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(177, 8, 9, 9), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("readonly-modifier")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(199, 2, 9, 31), int(SemanticTokenTypes::Property),
+ readOnlyModifier);
+ int requiredModifier = definitionModifier | (1 << int(SemanticTokenModifiers::Abstract));
+ QTest::addRow("required-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(210, 8, 10, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("required-modifier")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(232, 3, 10, 31), int(SemanticTokenTypes::Property),
+ requiredModifier);
+ int defaultModifier =
+ definitionModifier | (1 << int(SemanticTokenModifiers::DefaultLibrary));
+ QTest::addRow("default-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(244, 7, 11, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("default-modifier")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(265, 4, 11, 30), int(SemanticTokenTypes::Property),
+ defaultModifier);
+ }
+ {
+ // methods and signals
+ const auto filePath = m_highlightingDataDir + "/methodAndSignal.qml";
+ const auto fileItem = fileObject(filePath);
+
+ QTest::addRow("signal-keyword")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(139, 6, 7, 5), int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("signal-name")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(146, 1, 7, 12), int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("signal-type")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(163, 3, 8, 14), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("signal-type-2")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(186, 3, 9, 17), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("function-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(195, 9, 10, 5),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("function-name") << fileItem
+ << Token(QQmlJS::SourceLocation(204, 1, 10, 14),
+ int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("function-prm-type")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(209, 3, 10, 19), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("function-prm-name") << fileItem
+ << Token(QQmlJS::SourceLocation(206, 1, 10, 16),
+ int(SemanticTokenTypes::Parameter), 0);
+ QTest::addRow("function-rtn-type")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(216, 3, 10, 26), int(SemanticTokenTypes::Type), 0);
+ }
+ { // literals
+ const auto filePath = m_highlightingDataDir + "/literals.qml";
+ const auto fileItem = fileObject(filePath);
+
+ QTest::addRow("number") << fileItem
+ << Token(QQmlJS::SourceLocation(155, 3, 7, 21),
+ int(SemanticTokenTypes::Number), 0);
+ QTest::addRow("singleline-string")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(182, 8, 8, 24), int(SemanticTokenTypes::String), 0);
+ QTest::addRow("multiline-string-first")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(214, 6, 9, 24), int(SemanticTokenTypes::String), 0);
+ QTest::addRow("multiline-string-second") << fileItem
+ << Token(QQmlJS::SourceLocation(221, 16, 10, 1),
+ int(SemanticTokenTypes::String), 0);
+ QTest::addRow("boolean") << fileItem
+ << Token(QQmlJS::SourceLocation(260, 4, 11, 22),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("null") << fileItem
+ << Token(QQmlJS::SourceLocation(285, 4, 12, 21),
+ int(SemanticTokenTypes::Keyword), 0);
+ }
+ { // identifiers
+ const auto filePath = m_highlightingDataDir + "/Identifiers.qml";
+ const auto fileItem = fileObject(filePath);
+ QTest::addRow("js-property") << fileItem
+ << Token(QQmlJS::SourceLocation(222, 3, 10, 13),
+ int(SemanticTokenTypes::Variable), 0);
+ QTest::addRow("property-id")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(302, 4, 12, 19), int(SemanticTokenTypes::Property),
+ (1 << int(SemanticTokenModifiers::Readonly)));
+ QTest::addRow("property-changed") << fileItem
+ << Token(QQmlJS::SourceLocation(451, 11, 18, 9),
+ int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("signal") << fileItem
+ << Token(QQmlJS::SourceLocation(474, 7, 19, 9),
+ int(SemanticTokenTypes::Method), 0);
+
+ QTest::addRow("attached-id")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(512, 4, 23, 5), int(SemanticTokenTypes::Type), 0);
+ QTest::addRow("attached-signalhandler") << fileItem
+ << Token(QQmlJS::SourceLocation(517, 9, 23, 10),
+ int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("propchanged-handler") << fileItem
+ << Token(QQmlJS::SourceLocation(572, 13, 27, 5),
+ int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("method-id")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(597, 1, 28, 9), int(SemanticTokenTypes::Method), 0);
+ QTest::addRow("signal-handler")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(656, 9, 32, 5), int(SemanticTokenTypes::Method), 0);
+ }
+ { // script expressions
+ const auto filePath = m_highlightingDataDir + "/scriptExpressions.qml";
+ const auto fileItem = fileObject(filePath);
+
+ QTest::addRow("var-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(192, 3, 11, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("const-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(217, 5, 12, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ const auto modifier = (1 << int(SemanticTokenModifiers::Readonly));
+ QTest::addRow("const-name") << fileItem
+ << Token(QQmlJS::SourceLocation(223, 10, 12, 15),
+ int(SemanticTokenTypes::Variable), modifier);
+ QTest::addRow("do-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(248, 2, 13, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("if-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(287, 2, 15, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("continue-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(319, 8, 16, 17),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("else-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(341, 4, 17, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("while-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(382, 5, 19, 11),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("switch-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(418, 6, 20, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("case-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(444, 4, 21, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("return-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(464, 6, 22, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("default-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(483, 7, 23, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("break-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(504, 5, 24, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("try-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(529, 3, 26, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("catch-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(560, 5, 28, 11),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("finally-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(601, 7, 30, 11),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("for-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(620, 3, 31, 9),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("throw-keyword") << fileItem
+ << Token(QQmlJS::SourceLocation(661, 5, 32, 13),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("for-declaration") << fileItem
+ << Token(QQmlJS::SourceLocation(625, 5, 31, 14),
+ int(SemanticTokenTypes::Keyword), 0);
+ QTest::addRow("destructuring")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(1511, 2, 73, 16), int(SemanticTokenTypes::Variable),
+ (1 << int(SemanticTokenModifiers::Readonly)));
+ QTest::addRow("obj-destructuring")
+ << fileItem
+ << Token(QQmlJS::SourceLocation(1589, 2, 76, 17), int(SemanticTokenTypes::Variable),
+ (1 << int(SemanticTokenModifiers::Readonly)));
+ }
+}
+
+void tst_qmlls_highlighting::highlights()
+{
+ using namespace QQmlJS::Dom;
+ QFETCH(DomItem, fileItem);
+ QFETCH(Token, expectedHighlightedToken);
+
+ Highlights h;
+ HighlightingVisitor hv(h, std::nullopt);
+
+ fileItem.visitTree(QQmlJS::Dom::Path(), hv, VisitOption::Default, emptyChildrenVisitor,
+ emptyChildrenVisitor);
+
+ const auto highlights = h.highlights();
+ QVERIFY(highlights.contains(expectedHighlightedToken.offset));
+ QCOMPARE(highlights.value(expectedHighlightedToken.offset), expectedHighlightedToken);
+}
+
+void tst_qmlls_highlighting::rangeOverlapsWithSourceLocation_data()
+{
+ QTest::addColumn<QQmlJS::SourceLocation>("sourceLocation");
+ QTest::addColumn<HighlightsRange>("range");
+ QTest::addColumn<bool>("overlaps");
+
+ QTest::addRow("sl-inside-range")
+ << QQmlJS::SourceLocation(5, 1, 1, 1) << HighlightsRange{ 0, 100 } << true;
+ QTest::addRow("sl-exceeds-rightBoundRange")
+ << QQmlJS::SourceLocation(5, 1000, 1, 1) << HighlightsRange{ 0, 100 } << true;
+ QTest::addRow("sl-exceeds-leftRightBoundRange")
+ << QQmlJS::SourceLocation(5, 1000, 1, 1) << HighlightsRange{ 8, 100 } << true;
+ QTest::addRow("sl-exceeds-leftBoundRange")
+ << QQmlJS::SourceLocation(5, 100, 1, 1) << HighlightsRange{ 8, 1000 } << true;
+ QTest::addRow("no-overlaps") << QQmlJS::SourceLocation(5, 100, 1, 1)
+ << HighlightsRange{ 8000, 100000 } << false;
+}
+
+void tst_qmlls_highlighting::rangeOverlapsWithSourceLocation()
+{
+ QFETCH(QQmlJS::SourceLocation, sourceLocation);
+ QFETCH(HighlightsRange, range);
+ QFETCH(bool, overlaps);
+ QVERIFY(overlaps == HighlightingUtils::rangeOverlapsWithSourceLocation(sourceLocation, range));
+}
+
+void tst_qmlls_highlighting::updateResultID_data()
+{
+ QTest::addColumn<QByteArray>("currentId");
+ QTest::addColumn<QByteArray>("expectedNextId");
+
+ QTest::addRow("zero-to-one") << QByteArray("0") << QByteArray("1");
+ QTest::addRow("nine-to-ten") << QByteArray("9") << QByteArray("10");
+ QTest::addRow("nineteen-to-twenty") << QByteArray("19") << QByteArray("20");
+ QTest::addRow("twodigit-to-threedigit") << QByteArray("99") << QByteArray("100");
+}
+
+void tst_qmlls_highlighting::updateResultID()
+{
+ QFETCH(QByteArray, currentId);
+ QFETCH(QByteArray, expectedNextId);
+
+ HighlightingUtils::updateResultID(currentId);
+ QCOMPARE(currentId, expectedNextId);
+}
+
+void tst_qmlls_highlighting::computeDiff_data()
+{
+ QTest::addColumn<QList<int>>("oldData");
+ QTest::addColumn<QList<int>>("newData");
+ QTest::addColumn<QList<SemanticTokensEdit>>("expected");
+
+ {
+ QList<int> oldData { 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0};
+ QList<int> newData { 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0};
+ SemanticTokensEdit expected;
+ expected.start = 0;
+ expected.deleteCount = 1;
+ expected.data = QList{3};
+ QTest::addRow("simple") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0};
+ QList<int> newData { 3, 3, 3, 3, 3, 0, 0, 5, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 0;
+ expected.deleteCount = 0;
+ expected.data = QList{3, 3, 3, 3, 3};
+ QTest::addRow("prepend") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 3, 3, 3, 3, 3, 0, 0, 5, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 0;
+ expected.deleteCount = 5;
+ expected.data = {};
+ QTest::addRow("remove-front") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0, 1, 0, 23, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 5;
+ expected.deleteCount = 0;
+ expected.data = QList{1, 0, 23, 5, 0};
+ QTest::addRow("append") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0, 1, 0, 23, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 5;
+ expected.deleteCount = 5;
+ expected.data = {};
+ QTest::addRow("remove-back") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0, 1, 0, 23, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0, 3, 3, 3, 3, 3, 1, 0, 23, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 5;
+ expected.deleteCount = 0;
+ expected.data = QList{3, 3, 3, 3, 3};
+ QTest::addRow("insert-middle") << oldData << newData << QList{expected};
+ }
+ {
+ QList<int> oldData { 0, 0, 5, 5, 0, 3, 3, 3, 3, 3, 1, 0, 23, 5, 0};
+ QList<int> newData { 0, 0, 5, 5, 0, 1, 0, 23, 5, 0};
+ SemanticTokensEdit expected;
+ expected.start = 5;
+ expected.deleteCount = 5;
+ expected.data = {};
+ QTest::addRow("remove-middle") << oldData << newData << QList{expected};
+ }
+}
+
+void tst_qmlls_highlighting::computeDiff()
+{
+ QFETCH(QList<int>, oldData);
+ QFETCH(QList<int>, newData);
+ QFETCH(QList<SemanticTokensEdit>, expected);
+
+ const auto edits = HighlightingUtils::computeDiff(oldData, newData);
+ QCOMPARE(edits.size(), expected.size());
+
+ qsizetype i = 0;
+ for (const auto &edit : edits) {
+ QCOMPARE(edit.start, expected.at(i).start);
+ QCOMPARE(edit.deleteCount, expected.at(i).deleteCount);
+ QCOMPARE(edit.data, expected.at(i).data);
+ ++i;
+ }
+}
+
+
+QTEST_MAIN(tst_qmlls_highlighting)
diff --git a/tests/auto/qmlls/utils/tst_qmlls_highlighting.h b/tests/auto/qmlls/utils/tst_qmlls_highlighting.h
new file mode 100644
index 0000000000..a1d0e3c9b1
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_highlighting.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_HIGHLIGHTING_H
+#define TST_QMLLS_HIGHLIGHTING_H
+
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtCore/qobject.h>
+#include <QtTest/qtest.h>
+
+class tst_qmlls_highlighting : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qmlls_highlighting();
+private slots:
+ void encodeSemanticTokens_data();
+ void encodeSemanticTokens();
+ void sourceLocationsFromMultiLineToken_data();
+ void sourceLocationsFromMultiLineToken();
+
+ void highlights_data();
+ void highlights();
+
+ void rangeOverlapsWithSourceLocation_data();
+ void rangeOverlapsWithSourceLocation();
+
+ void updateResultID_data();
+ void updateResultID();
+
+ void computeDiff_data();
+ void computeDiff();
+private:
+ QString m_highlightingDataDir;
+};
+
+#endif // TST_QMLLS_HIGHLIGHTING_H
diff --git a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
new file mode 100644
index 0000000000..c8808e2d7c
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
@@ -0,0 +1,4026 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmlls_utils.h"
+#include <algorithm>
+#include <optional>
+
+#include <QtCore/private/qduplicatetracker_p.h>
+#include <QtQmlLS/private/qdochtmlparser_p.h>
+
+// some helper constants for the tests
+const static int positionAfterOneIndent = 5;
+const static QString noResultExpected;
+// constants for resultIndex
+const static int firstResult = 0;
+const static int secondResult = 1;
+// constants for expectedItemsCount
+const static int outOfOne = 1;
+const static int outOfTwo = 2;
+
+// enable/disable additional debug output
+constexpr static bool enable_debug_output = true;
+
+static QString printSet(const QSet<QString> &s)
+{
+ const QString r = QStringList(s.begin(), s.end()).join(u", "_s);
+ return r;
+}
+
+static QString readFileContent(const QString &testFileName) {
+ QFile file(testFileName);
+ if (file.open(QIODeviceBase::ReadOnly))
+ return QString::fromUtf8(file.readAll());
+ return QString{};
+};
+
+std::tuple<QQmlJS::Dom::DomItem, QQmlJS::Dom::DomItem>
+tst_qmlls_utils::createEnvironmentAndLoadFile(const QString &filePath)
+{
+ CacheKey cacheKey = QDir::cleanPath(filePath + u"/.."_s);
+ if (auto entry = cache.find(cacheKey); entry != cache.end()) {
+ DomItem env{ *entry };
+ return { env, env.field(QQmlJS::Dom::Fields::qmlFileWithPath).key(filePath) };
+ };
+
+ QStringList qmltypeDirs =
+ QStringList({ dataDirectory(), QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
+
+ // This should be exactly the same options as qmlls uses in qqmlcodemodel.
+ // Otherwise, this test will not test the codepaths also used by qmlls and will be useless.
+ const QQmlJS::Dom::DomCreationOptions options = QQmlJS::Dom::DomCreationOptions{}
+ | QQmlJS::Dom::DomCreationOption::WithSemanticAnalysis
+ | QQmlJS::Dom::DomCreationOption::WithScriptExpressions
+ | QQmlJS::Dom::DomCreationOption::WithRecovery;
+
+ auto envPtr = QQmlJS::Dom::DomEnvironment::create(
+ qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, options);
+
+ QQmlJS::Dom::DomItem file;
+ QQmlJS::Dom::DomItem env(envPtr);
+ envPtr->loadFile(QQmlJS::Dom::FileToLoad::fromFileSystem(envPtr, filePath),
+ [&file](QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &,
+ const QQmlJS::Dom::DomItem &newIt) { file = newIt; });
+
+ envPtr->loadPendingDependencies();
+ envPtr->loadBuiltins();
+
+ cache[cacheKey] = envPtr;
+ return std::make_tuple(env, file);
+}
+
+void tst_qmlls_utils::textOffsetRowColumnConversions_data()
+{
+ QTest::addColumn<QString>("code");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<qsizetype>("expectedOffset");
+ QTest::addColumn<QChar>("expectedChar");
+ // in case they differ from line and character, e.g. when accessing non-existing line or rows
+ // set to -1 when same as before
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+
+ QTest::newRow("oneline") << u"Hello World!"_s << 0 << 6 << 6ll << QChar('W') << -1 << -1;
+ QTest::newRow("multi-line") << u"Hello World!\n How are you? \n Bye!\n"_s << 0 << 6 << 6ll
+ << QChar('W') << -1 << -1;
+ QTest::newRow("multi-line2") << u"Hello World!\n How are you? \n Bye!\n"_s << 1 << 5 << 18ll
+ << QChar('a') << -1 << -1;
+ QTest::newRow("multi-line3") << u"Hello World!\n How are you? \n Bye!\n"_s << 2 << 1 << 29ll
+ << QChar('B') << -1 << -1;
+
+ QTest::newRow("newlines") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 0 << 0 << 0ll << QChar('A') << -1
+ << -1;
+ QTest::newRow("newlines2") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 0 << 2ll << QChar('B') << -1
+ << -1;
+
+ // try to access '\r'
+ QTest::newRow("newlines3") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 1 << 3ll << QChar('\r') << -1
+ << -1;
+ // try to access '\n', should return the last character of the line (which is '\r' in this case)
+ QTest::newRow("newlines4") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 2 << 3ll << QChar('\r') << -1
+ << 1;
+ // try to access after the end of the line, should return the last character of the line (which
+ // is '\r' in this case)
+ QTest::newRow("afterLineEnd") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 42 << 3ll << QChar('\r')
+ << -1 << 1;
+
+ // try to access an inexisting column, seems to return the last character of the last line.
+ QTest::newRow("afterColumnEnd")
+ << u"A\nB\r\nC\n\r\nD\r\n\rAX"_s << 42 << 0 << 15ll << QChar('X') << 5 << 2;
+}
+
+void tst_qmlls_utils::textOffsetRowColumnConversions()
+{
+ QFETCH(QString, code);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(qsizetype, expectedOffset);
+ QFETCH(QChar, expectedChar);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+
+ qsizetype offset = QQmlLSUtils::textOffsetFrom(code, line, character);
+
+ QCOMPARE(offset, expectedOffset);
+ if (offset < code.size())
+ QCOMPARE(code[offset], expectedChar);
+
+ auto [computedRow, computedColumn] = QQmlLSUtils::textRowAndColumnFrom(code, expectedOffset);
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ QCOMPARE(computedRow, expectedLine);
+ QCOMPARE(computedColumn, expectedCharacter);
+}
+
+void tst_qmlls_utils::findItemFromLocation_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ // in case there are multiple items to be found (e.g. for a location between two objects), the
+ // item to be checked against
+ QTest::addColumn<int>("resultIndex");
+ QTest::addColumn<int>("expectedItemsCount");
+ QTest::addColumn<QQmlJS::Dom::DomType>("expectedType");
+ // set to -1 when unchanged from above line and character, starts at 1
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+
+ const QString file1Qml = testFile(u"file1.qml"_s);
+
+ QTest::addRow("findIntProperty") << file1Qml << 9 << 18 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "a" property
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("findIntProperty2") << file1Qml << 9 << 10 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "a" property
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("findIntBinding")
+ << file1Qml << 30 << positionAfterOneIndent << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression
+ // start of the a identifier of the "a" binding
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("findIntBinding2") << file1Qml << 30 << 8 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptLiteral
+ << -1 << 8;
+
+ QTest::addRow("colorBinding") << file1Qml << 39 << 13 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1
+ << 2 * positionAfterOneIndent - 1;
+
+ QTest::addRow("findVarProperty") << file1Qml << 12 << 12 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "d" property
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("findVarBinding") << file1Qml << 31 << 8 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptLiteral << -1 << 8;
+ QTest::addRow("beforeEProperty")
+ << file1Qml << 13 << positionAfterOneIndent << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "e" property
+ << -1 << -1;
+ QTest::addRow("onEProperty") << file1Qml << 13 << 24 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "e" property
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("afterEProperty") << file1Qml << 13 << 25 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition
+ // start of the "property"-token of the "e" property
+ << -1 << positionAfterOneIndent;
+
+ QTest::addRow("property-in-ic") << file1Qml << 28 << 38 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::PropertyDefinition << -1 << 26;
+
+ QTest::addRow("onCChild") << file1Qml << 16 << positionAfterOneIndent << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1
+ << positionAfterOneIndent;
+
+ // check for off-by-one/overlapping items
+ QTest::addRow("closingBraceOfC")
+ << file1Qml << 16 << 19 << firstResult << outOfOne << QQmlJS::Dom::DomType::QmlObject
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("beforeClosingBraceOfC")
+ << file1Qml << 16 << 18 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 12;
+ QTest::addRow("firstBetweenCandD")
+ << file1Qml << 16 << 20 << secondResult << outOfTwo << QQmlJS::Dom::DomType::QmlObject
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("secondBetweenCandD")
+ << file1Qml << 16 << 20 << firstResult << outOfTwo
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << -1;
+
+ QTest::addRow("afterD") << file1Qml << 16 << 21 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 20;
+
+ // check what happens between items (it should not crash)
+
+ QTest::addRow("onWhitespaceBeforeC")
+ << file1Qml << 16 << 1 << firstResult << outOfOne << QQmlJS::Dom::DomType::Map << 9
+ << positionAfterOneIndent;
+
+ QTest::addRow("onWhitespaceAfterC")
+ << file1Qml << 17 << 8 << firstResult << outOfOne << QQmlJS::Dom::DomType::QmlObject
+ << -1 << positionAfterOneIndent;
+
+ QTest::addRow("onWhitespaceBetweenCAndD") << file1Qml << 17 << 23 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::Map << 16 << 8;
+ QTest::addRow("onWhitespaceBetweenCAndD2") << file1Qml << 17 << 24 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::Map << 16 << 8;
+
+ QTest::addRow("ic") << file1Qml << 15 << 5 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::QmlComponent << -1 << 5;
+ QTest::addRow("ic2") << file1Qml << 15 << 20 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 18;
+ QTest::addRow("ic3") << file1Qml << 15 << 33 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 29;
+
+ QTest::addRow("function") << file1Qml << 33 << 5 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::MethodInfo << -1 << positionAfterOneIndent;
+ QTest::addRow("function-parameter")
+ << file1Qml << 33 << 20 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 19;
+ // The return type of a function has no own DomItem. Instead, the return type of a function
+ // is saved into the MethodInfo.
+ QTest::addRow("function-return")
+ << file1Qml << 33 << 41 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 41;
+ QTest::addRow("function2") << file1Qml << 36 << 17 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::MethodInfo << -1
+ << positionAfterOneIndent;
+
+ // check rectangle property
+ QTest::addRow("rectangle-property")
+ << file1Qml << 44 << 31 << firstResult << outOfOne
+ << QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 29;
+}
+
+void tst_qmlls_utils::findItemFromLocation()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(int, resultIndex);
+ QFETCH(int, expectedItemsCount);
+ QFETCH(QQmlJS::Dom::DomType, expectedType);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+ Q_ASSERT(expectedLine > 0);
+ Q_ASSERT(expectedCharacter > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QVERIFY(resultIndex < locations.size());
+ QCOMPARE(locations.size(), expectedItemsCount);
+
+ QQmlJS::Dom::DomItem itemToTest = locations[resultIndex].domItem;
+ // ask for the type in the args
+ if constexpr (enable_debug_output) {
+ if (itemToTest.internalKind() != expectedType) {
+ qDebug() << itemToTest.internalKindStr() << " has not the expected kind "
+ << expectedType << " for item " << itemToTest.toString();
+ }
+ }
+ QCOMPARE(itemToTest.internalKind(), expectedType);
+
+ QQmlJS::Dom::FileLocations::Tree locationToTest = locations[resultIndex].fileLocation;
+ QCOMPARE(locationToTest->info().fullRegion.startLine, quint32(expectedLine));
+ QCOMPARE(locationToTest->info().fullRegion.startColumn, quint32(expectedCharacter));
+}
+
+void tst_qmlls_utils::findTypeDefinitionFromLocation_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ // in case there are multiple items to be found (e.g. for a location between two objects), the
+ // item to be checked against
+ QTest::addColumn<int>("resultIndex");
+ QTest::addColumn<int>("expectedItemsCount");
+ QTest::addColumn<QString>("expectedFilePath");
+ // set to -1 when unchanged from above line and character. 0-based.
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+
+ const QString file1Qml = testFile(u"file1.qml"_s);
+ const QString TypeQml = testFile(u"Type.qml"_s);
+ // pass this as file when no result is expected, e.g. for type definition of "var".
+
+ QTest::addRow("onCProperty") << file1Qml << 11 << 16 << firstResult << outOfOne << file1Qml << 7
+ << positionAfterOneIndent;
+
+ QTest::addRow("onCProperty2") << file1Qml << 28 << 37 << firstResult << outOfOne << file1Qml
+ << 7 << positionAfterOneIndent;
+
+ QTest::addRow("onCProperty3") << file1Qml << 28 << 35 << firstResult << outOfOne << file1Qml
+ << 7 << positionAfterOneIndent;
+
+ QTest::addRow("onCBinding") << file1Qml << 46 << 8 << firstResult << outOfOne << file1Qml << 7
+ << positionAfterOneIndent;
+
+ QTest::addRow("onDefaultBinding") << file1Qml << 16 << positionAfterOneIndent << firstResult
+ << outOfOne << file1Qml << 7 << positionAfterOneIndent;
+
+ QTest::addRow("onDefaultBindingId")
+ << file1Qml << 16 << 28 << firstResult << outOfOne << file1Qml << 16 << 20;
+
+ QTest::addRow("findIntProperty") << file1Qml << 9 << 18 << firstResult << outOfOne << file1Qml
+ << -1 << positionAfterOneIndent;
+ QTest::addRow("colorBinding") << file1Qml << 39 << 8 << firstResult << outOfOne << file1Qml
+ << -1 << positionAfterOneIndent;
+
+ // check what happens between items (it should not crash)
+
+ QTest::addRow("onWhitespaceBeforeC")
+ << file1Qml << 16 << 1 << firstResult << outOfOne << noResultExpected << -1 << -1;
+
+ QTest::addRow("onWhitespaceAfterC")
+ << file1Qml << 17 << 23 << firstResult << outOfOne << noResultExpected << -1 << -1;
+
+ QTest::addRow("onWhitespaceBetweenCAndD")
+ << file1Qml << 17 << 24 << firstResult << outOfOne << noResultExpected << -1 << -1;
+
+ QTest::addRow("ic") << file1Qml << 15 << 15 << firstResult << outOfOne << file1Qml << 15 << 15;
+ QTest::addRow("icBase") << file1Qml << 15 << 20 << firstResult << outOfOne
+ << u"TODO: file location for C++ defined types?"_s << -1 << -1;
+ QTest::addRow("ic3") << file1Qml << 15 << 33 << firstResult << outOfOne << file1Qml << -1 << 18;
+
+ // TODO: type definition of function = type definition of return type?
+ // if not, this might need fixing:
+ // currently, asking the type definition of the "function" keyword returns
+ // the type definitin of the return type (when available).
+ QTest::addRow("function-keyword") << file1Qml << 33 << 5 << firstResult << outOfOne << file1Qml
+ << 7 << positionAfterOneIndent;
+ QTest::addRow("function-parameter-builtin")
+ << file1Qml << 33 << 20 << firstResult << outOfOne << file1Qml << -1 << -1;
+ QTest::addRow("function-parameter-item") << file1Qml << 33 << 36 << firstResult << outOfOne
+ << file1Qml << 7 << positionAfterOneIndent;
+
+ QTest::addRow("function-return") << file1Qml << 33 << 41 << firstResult << outOfOne << file1Qml
+ << 7 << positionAfterOneIndent;
+
+ QTest::addRow("void-function")
+ << file1Qml << 36 << 17 << firstResult << outOfOne << noResultExpected << -1 << -1;
+
+ QTest::addRow("rectangle-property") << file1Qml << 44 << 31 << firstResult << outOfOne
+ << "TODO: c++ type location" << -1 << -1;
+
+ QTest::addRow("functionParameterICUsage")
+ << file1Qml << 34 << 16 << firstResult << outOfOne << file1Qml << 7 << 15;
+
+ QTest::addRow("ICBindingUsage")
+ << file1Qml << 47 << 21 << firstResult << outOfOne << file1Qml << 7 << 15;
+ QTest::addRow("ICBindingUsage2")
+ << file1Qml << 49 << 11 << firstResult << outOfOne << file1Qml << 7 << 15;
+ QTest::addRow("ICBindingUsage3")
+ << file1Qml << 52 << 17 << firstResult << outOfOne << file1Qml << 7 << 15;
+}
+
+void tst_qmlls_utils::findTypeDefinitionFromLocation()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(int, resultIndex);
+ QFETCH(int, expectedItemsCount);
+ QFETCH(QString, expectedFilePath);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+ Q_ASSERT(expectedLine > 0);
+ Q_ASSERT(expectedCharacter > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QCOMPARE(locations.size(), expectedItemsCount);
+
+ auto base = QQmlLSUtils::findTypeDefinitionOf(locations[resultIndex].domItem);
+
+ // if expectedFilePath is empty, we probably just want to make sure that it does
+ // not crash
+ if (expectedFilePath == noResultExpected) {
+ QVERIFY(!base);
+ return;
+ }
+
+ QEXPECT_FAIL("findIntProperty", "Builtins not supported yet", Abort);
+ QEXPECT_FAIL("function-parameter-builtin", "Base types defined in C++ are not supported yet",
+ Abort);
+ QEXPECT_FAIL("colorBinding", "Types from C++ bases not supported yet", Abort);
+ QEXPECT_FAIL("rectangle-property", "Types from C++ bases not supported yet", Abort);
+ QEXPECT_FAIL("icBase", "Base types defined in C++ are not supported yet", Abort);
+ QVERIFY(base);
+
+ auto fileObject =
+ locations[resultIndex].domItem.goToFile(base->filename).as<QQmlJS::Dom::QmlFile>();
+
+ // print some debug message when failing, instead of using QVERIFY2
+ // (printing the type every time takes a lot of time).
+ if constexpr (enable_debug_output) {
+ if (!fileObject)
+ qDebug() << "Could not find the file" << base->filename << "in the Dom.";
+ }
+
+ QVERIFY(fileObject);
+ QCOMPARE(base->filename, expectedFilePath);
+ QCOMPARE(fileObject->canonicalFilePath(), expectedFilePath);
+
+ QCOMPARE(base->sourceLocation.startLine, quint32(expectedLine));
+ QCOMPARE(base->sourceLocation.startColumn, quint32(expectedCharacter));
+}
+
+void tst_qmlls_utils::findLocationOfItem_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+
+ const QString file1Qml = testFile(u"file1.qml"_s);
+
+ QTest::addRow("root-element") << file1Qml << 6 << 2 << -1 << 1;
+
+ QTest::addRow("property-a") << file1Qml << 9 << 18 << -1 << positionAfterOneIndent;
+ QTest::addRow("property-a2") << file1Qml << 9 << 10 << -1 << positionAfterOneIndent;
+ QTest::addRow("nested-C") << file1Qml << 20 << 9 << -1 << -1;
+ QTest::addRow("nested-C2") << file1Qml << 23 << 13 << -1 << -1;
+ QTest::addRow("D") << file1Qml << 17 << 33 << -1 << 32;
+ QTest::addRow("property-d-var-type") << file1Qml << 12 << 15 << -1 << 14;
+
+ QTest::addRow("import") << file1Qml << 4 << 6 << -1 << 1;
+}
+
+void tst_qmlls_utils::findLocationOfItem()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+ Q_ASSERT(expectedLine > 0);
+ Q_ASSERT(expectedCharacter > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ // grab item using already tested QQmlLSUtils::findLastItemsContaining
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+ QCOMPARE(locations.size(), 1);
+
+ // once the item is grabbed, make sure its line/character position can be obtained back
+ auto t = QQmlJS::Dom::FileLocations::treeOf(locations.front().domItem);
+
+ QCOMPARE(t->info().fullRegion.startLine, quint32(expectedLine));
+ QCOMPARE(t->info().fullRegion.startColumn, quint32(expectedCharacter));
+}
+
+void tst_qmlls_utils::findBaseObject_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ // to avoid mixing up the types (because they are all called Item or QQuickItem eitherway)
+ // mark them with properties and detect right types by their marker property,
+ // usually called (in<Filename>DotQml or in<Inline component Name>)
+ QTest::addColumn<QSet<QString>>("expectedPropertyName");
+ // because types inherit properties, make sure that derived type properties are not in the base
+ // type, to correctly detect mixups between types and their base types
+ QTest::addColumn<QSet<QString>>("unExpectedPropertyName");
+
+ // (non) Expected Properties Names = ePN (nEPN)
+ // marker properties for the root object in BaseType.qml
+ QSet<QString> ePNBaseType;
+ ePNBaseType << u"inBaseTypeDotQml"_s;
+ QSet<QString> nEPNBaseType;
+ nEPNBaseType << u"inTypeDotQml"_s;
+
+ // marker properties for the root object in Type.qml
+ QSet<QString> ePNType;
+ ePNType << u"inBaseTypeDotQml"_s << u"inTypeDotQml"_s;
+ QSet<QString> nEPNType;
+
+ // marker properties for QQuickItem (e.g. the base of "Item")
+ QSet<QString> ePNQQuickItem;
+ QSet<QString> nEPNQQuickItem;
+ nEPNQQuickItem << u"inBaseTypeDotQml"_s << u"inTypeDotQml"_s;
+
+ // marker properties for MyInlineComponent
+ QSet<QString> ePNMyInlineComponent;
+ QSet<QString> nEPNMyInlineComponent;
+ ePNMyInlineComponent << u"inBaseTypeDotQml"_s << u"inTypeDotQml"_s << u"inMyInlineComponent"_s;
+
+ // marker properties for MyNestedInlineComponent
+ const QSet<QString> ePNMyNestedInlineComponent{ u"inMyNestedInlineComponent"_s };
+ const QSet<QString> nEPNMyNestedInlineComponent{ u"inBaseTypeDotQml"_s, u"inTypeDotQml"_s,
+ u"inMyInlineComponent"_s };
+
+ // marker properties for MyBaseInlineComponent
+ const QSet<QString> ePNMyBaseInlineComponent{ u"inBaseTypeDotQml"_s };
+ const QSet<QString> nEPNMyBaseInlineComponent{ u"inTypeDotQml"_s, u"inMyInlineComponent"_s };
+
+ const int rootElementDefLine = 6;
+ QTest::addRow("root-element") << testFile(u"Type.qml"_s) << rootElementDefLine << 5
+ << ePNQQuickItem << nEPNQQuickItem;
+ QTest::addRow("root-element-from-id") << testFile(u"Type.qml"_s) << rootElementDefLine + 1 << 12
+ << ePNBaseType << nEPNBaseType;
+
+ const int myInlineComponentDefLine = 10;
+ // on the component name: go to BaseType
+ QTest::addRow("ic-name") << testFile(u"Type.qml"_s) << myInlineComponentDefLine << 26
+ << ePNBaseType << nEPNBaseType;
+ // on the "BaseType" type: go to QQuickitem (base type of BaseType).
+ QTest::addRow("ic-basetypename") << testFile(u"Type.qml"_s) << myInlineComponentDefLine << 37
+ << ePNQQuickItem << nEPNQQuickItem;
+ QTest::addRow("ic-from-id") << testFile(u"Type.qml"_s) << myInlineComponentDefLine + 1 << 19
+ << ePNBaseType << nEPNBaseType;
+
+ const int inlineTypeDefLine = 15;
+ QTest::addRow("inline") << testFile(u"Type.qml"_s) << inlineTypeDefLine << 23 << ePNQQuickItem
+ << nEPNQQuickItem;
+ QTest::addRow("inline2") << testFile(u"Type.qml"_s) << inlineTypeDefLine << 38 << ePNQQuickItem
+ << nEPNQQuickItem;
+ QTest::addRow("inline3") << testFile(u"Type.qml"_s) << inlineTypeDefLine << 15 << ePNQQuickItem
+ << nEPNQQuickItem;
+ QTest::addRow("inline-from-id") << testFile(u"Type.qml"_s) << inlineTypeDefLine + 1 << 24
+ << ePNBaseType << nEPNBaseType;
+
+ const int inlineIcDefLine = 23;
+ QTest::addRow("inline-ic") << testFile(u"Type.qml"_s) << inlineIcDefLine << 38
+ << ePNMyBaseInlineComponent << nEPNMyBaseInlineComponent;
+ QTest::addRow("inline-ic-from-id") << testFile(u"Type.qml"_s) << inlineIcDefLine + 1 << 28
+ << ePNMyBaseInlineComponent << nEPNMyBaseInlineComponent;
+
+ const int inlineNestedIcDefLine = 27;
+ QTest::addRow("inline-ic2") << testFile(u"Type.qml"_s) << inlineNestedIcDefLine << 46
+ << ePNMyNestedInlineComponent << nEPNMyNestedInlineComponent;
+ QTest::addRow("inline-ic2-from-id")
+ << testFile(u"Type.qml"_s) << inlineNestedIcDefLine + 1 << 23
+ << ePNMyNestedInlineComponent << nEPNMyNestedInlineComponent;
+}
+
+void tst_qmlls_utils::findBaseObject()
+{
+ const QByteArray failOnInlineComponentsMessage =
+ "The Dom cannot resolve inline components from the basetype yet.";
+
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QSet<QString>, expectedPropertyName);
+ QFETCH(QSet<QString>, unExpectedPropertyName);
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ // grab item using already tested QQmlLSUtils::findLastItemsContaining
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+ if constexpr (enable_debug_output) {
+ if (locations.size() > 1) {
+ for (auto &x : locations)
+ qDebug() << x.domItem.toString();
+ }
+ }
+ QCOMPARE(locations.size(), 1);
+
+ auto typeLocation = QQmlLSUtils::findTypeDefinitionOf(locations.front().domItem);
+ QEXPECT_FAIL("inline-ic", failOnInlineComponentsMessage, Abort);
+ QEXPECT_FAIL("inline-ic2", failOnInlineComponentsMessage, Abort);
+ QVERIFY(typeLocation);
+ QQmlJS::Dom::DomItem type = QQmlLSUtils::sourceLocationToDomItem(
+ locations.front().domItem.goToFile(typeLocation->filename),
+ typeLocation->sourceLocation);
+ auto base = QQmlLSUtils::baseObject(type);
+
+ if constexpr (enable_debug_output) {
+ if (!base)
+ qDebug() << u"Could not find the base of type "_s << type << u" from item:\n"_s
+ << locations.front().domItem.toString();
+ }
+
+ QEXPECT_FAIL("inline-ic-from-id", failOnInlineComponentsMessage, Abort);
+ QEXPECT_FAIL("inline-ic2-from-id", failOnInlineComponentsMessage, Abort);
+ QVERIFY(base);
+
+ const QSet<QString> propertyDefs = base.field(QQmlJS::Dom::Fields::propertyDefs).keys();
+ expectedPropertyName.subtract(propertyDefs);
+ QVERIFY2(expectedPropertyName.empty(),
+ u"Incorrect baseType found: it is missing following marker properties: "_s
+ .append(printSet(expectedPropertyName))
+ .toLatin1());
+ unExpectedPropertyName.intersect(propertyDefs);
+ QVERIFY2(unExpectedPropertyName.empty(),
+ u"Incorrect baseType found: it has an unexpected marker properties: "_s
+ .append(printSet(unExpectedPropertyName))
+ .toLatin1());
+}
+
+/*! \internal
+ \brief Wrapper for findUsages data.
+*/
+struct UsageData
+{
+ QString testFileName;
+ QList<QQmlLSUtilsLocation> expectedUsages;
+};
+
+void tst_qmlls_utils::findUsages_data()
+{
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<UsageData>("data");
+
+ const auto readFileContent = [](const QString &testFileName) {
+ QFile file(testFileName);
+ if (file.open(QIODeviceBase::ReadOnly))
+ return QString::fromUtf8(file.readAll());
+ return QString{};
+ };
+
+ const auto makeUsages = [](const QString &fileName, QList<QQmlLSUtilsLocation> &locations) {
+ UsageData data;
+ std::sort(locations.begin(), locations.end());
+ data.expectedUsages = locations;
+ data.testFileName = fileName;
+ return data;
+ };
+
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ const auto testFileName = testFile("findUsages/jsIdentifier/jsIdentifier.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ {
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 13, strlen("sum"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 13, strlen("sum"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 19, strlen("sum"));
+ const auto sumUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findSumFromDeclaration") << 8 << 13 << sumUsages;
+ QTest::addRow("findSumFromUsage") << 10 << 20 << sumUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 17, strlen("i"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 24, strlen("i"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 32, strlen("i"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 36, strlen("i"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 25, strlen("i"));
+ const auto iUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findIFromDeclaration") << 9 << 17 << iUsages;
+ QTest::addRow("findIFromUsage") << 9 << 24 << iUsages;
+ QTest::addRow("findIFromUsage2") << 10 << 25 << iUsages;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/property/property.qml");
+ const auto otherFile = testFile("findUsages/property/PropertyFromAnotherFile.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto otherFileContent = readFileContent(otherFile);
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 18, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 13, 13, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 13, 29, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 20, 9, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 21, 9, strlen("helloPropertyChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 5, strlen("onHelloPropertyChanged"));
+ const auto helloPropertyUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromDeclaration") << 8 << 18 << helloPropertyUsages;
+ QTest::addRow("findPropertyFromUsage") << 13 << 13 << helloPropertyUsages;
+ QTest::addRow("findPropertyFromUsage2") << 13 << 29 << helloPropertyUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 36, 20, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 38, 25, strlen("helloProperty"));
+ const auto subItemHelloPropertyUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromDeclarationInSubItem") << 38 << 25 << subItemHelloPropertyUsages;
+ QTest::addRow("findPropertyFromUsageInSubItem") << 36 << 20 << subItemHelloPropertyUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 27, 22, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 29, 20, strlen("helloProperty"));
+ const auto ICHelloPropertyUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromDeclarationInIC") << 27 << 22 << ICHelloPropertyUsages;
+ QTest::addRow("findPropertyFromUsageInIC") << 29 << 20 << ICHelloPropertyUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(otherFile, otherFileContent, 4, 18, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 42, 9, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 44, 20, strlen("helloProperty"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 46, 9, strlen("OnHelloPropertyChanged"));
+ const auto helloPropertyUsages = makeUsages(testFileName, expectedUsages);
+
+ QTest::addRow("findPropertyFromOtherFile") << 42 << 13 << helloPropertyUsages;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/propertyInNested/propertyInNested.qml");
+ const auto testFileContent = readFileContent(testFileName);
+
+ const auto componentFileName =
+ testFile("findUsages/propertyInNested/NestedComponentInFile.qml");
+ const auto componentFileContent = readFileContent(componentFileName);
+
+ const auto componentFileName3 =
+ testFile("findUsages/propertyInNested/NestedComponentInFile3.qml");
+ const auto componentFileContent3 = readFileContent(componentFileName);
+
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 7, 18, strlen("p2"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 33, 31, strlen("p2"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 34, 37, strlen("p2"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 35, 43, strlen("p2"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 36, 49, strlen("p2"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 42, 26, strlen("p2"));
+ const auto p2Usages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromDeclaration2") << 7 << 18 << p2Usages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 29, 13, strlen("myNested"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 32, 17, strlen("myNested"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 33, 17, strlen("myNested"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 34, 17, strlen("myNested"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 35, 17, strlen("myNested"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 36, 17, strlen("myNested"));
+ const auto nestedUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findIdFromUsage") << 36 << 20 << nestedUsages;
+ QTest::addRow("findIdFromDefinition") << 29 << 17 << nestedUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 14, 35, strlen("inner"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 32, 32, strlen("inner"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 35, 32, strlen("inner"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 36, 32, strlen("inner"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 16, 9, strlen("inner"));
+ const auto nestedComponent3Usages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findPropertyFromUsageInFieldMemberExpression")
+ << 36 << 34 << nestedComponent3Usages;
+
+ QTest::addRow("findFieldMemberExpressionUsageFromPropertyDefinition")
+ << 14 << 38 << nestedComponent3Usages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(componentFileName, componentFileContent, 4, 37, strlen("inner"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 50, 32, strlen("inner"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 52, 32, strlen("inner"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 53, 32, strlen("inner"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 54, 32, strlen("inner"));
+ const auto nestedComponent3Usages = makeUsages(testFileName, expectedUsages);
+ const auto nestedComponent3UsagesFromOtherFile =
+ makeUsages(componentFileName, expectedUsages);
+ QTest::addRow("findPropertyFromUsageInFieldMemberExpressionFromOtherFile")
+ << 50 << 33 << nestedComponent3Usages;
+
+ QTest::addRow("findFieldMemberExpressionUsageFromPropertyDefinitionFromOtherFile")
+ << 4 << 38 << nestedComponent3UsagesFromOtherFile;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 35, 38, strlen("p2"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 20, 22, strlen("p2"));
+ const auto nestedComponent3P2Usages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findProperty2FromUsageInFieldMemberExpression")
+ << 35 << 39 << nestedComponent3P2Usages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(componentFileName3, componentFileContent3,
+ 5, 18, strlen("p2"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 53, 44,
+ strlen("p2"));
+ const auto nestedComponent3P2Usages = makeUsages(testFileName, expectedUsages);
+ const auto nestedComponent3P2UsagesFromOtherFile = makeUsages(componentFileName3, expectedUsages);
+ QTest::addRow("findProperty2FromUsageInFieldMemberExpressionInOtherFile")
+ << 53 << 44 << nestedComponent3P2Usages;
+ QTest::addRow("findProperty2FromUsageInDefinitionInOtherFile")
+ << 5 << 19 << nestedComponent3P2UsagesFromOtherFile;
+ }
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ const auto testFileName = testFile("findUsages/idUsages/idUsages.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 7, 9, strlen("rootId"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 11, 17, strlen("rootId"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 12, 20, strlen("rootId"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 17, 9, strlen("rootId"));
+ const auto rootIdUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findIdFromUsageInChild") << 12 << 20 << rootIdUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ const auto testFileName = testFile("findUsages/recursive/recursive.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 14, strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 24, strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 34, strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 51, strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 68, strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 12, 20, strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 15, 34, strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 19, 27, strlen("recursive"));
+ const auto recursiveUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findFunctionUsage") << 10 << 30 << recursiveUsages;
+ QTest::addRow("findFunctionUsage2") << 12 << 24 << recursiveUsages;
+ QTest::addRow("findQualifiedFunctionUsage") << 19 << 31 << recursiveUsages;
+ QTest::addRow("findFunctionUsageFromDefinition") << 8 << 17 << recursiveUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ const auto testFileName = testFile("findUsages/recursive/recursive.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto otherFileName = testFile("findUsages/recursive/RecursiveInOtherFile.qml");
+ const auto otherFileContent = readFileContent(otherFileName);
+
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 27, 61,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 4, 14,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 6, 24,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 6, 34,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 6, 51,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 6, 68,
+ strlen("recursive"));
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 8, 20,
+ strlen("recursive"));
+
+ const auto recursiveUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findFunctionUsageFromOtherFile") << 27 << 64 << recursiveUsages;
+ const auto recursiveUsagesFromOtherFile = makeUsages(otherFileName, expectedUsages);
+ QTest::addRow("findFunctionUsageFromSameFile") << 6 << 39 << recursiveUsagesFromOtherFile;
+ QTest::addRow("findFunctionUsageFromDefinitionInOtherFile")
+ << 4 << 14 << recursiveUsagesFromOtherFile;
+ }
+ {
+ const auto testFileName = testFile("findUsages/signalsAndHandlers/signalsAndHandlers.qml");
+ const auto testFileContent = readFileContent(testFileName);
+
+ const auto otherFileName = testFile("findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml");
+ const auto otherFileContent = readFileContent(otherFileName);
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 12, strlen("helloSignal"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 11, 9, strlen("helloSignal"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 13, 13, strlen("helloSignal"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 17, 17, strlen("helloSignal"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 21, 9, strlen("helloSignal"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 39, 5, strlen("onHelloSignal"));
+ const auto helloSignalUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlSignalUsageFromDefinition") << 8 << 17 << helloSignalUsages;
+ QTest::addRow("findQmlSignalUsageFromUsage") << 13 << 17 << helloSignalUsages;
+ QTest::addRow("findQmlSignalUsageFromHandler") << 39 << 11 << helloSignalUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 5, 5, strlen("onWidthChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 13, strlen("widthChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 27, 17, strlen("widthChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 28, 20, strlen("widthChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 34, 20, strlen("widthChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 33, 13, strlen("widthChanged"));
+ const auto widthChangedUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findCppSignalUsageFromUsage") << 27 << 23 << widthChangedUsages;
+ QTest::addRow("findCppSignalUsageFromQualifiedUsage") << 28 << 23 << widthChangedUsages;
+ QTest::addRow("findCppSignalUsageFromQualifiedUsage2") << 34 << 24 << widthChangedUsages;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/binding/binding.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 18,
+ strlen("helloPropertyBinding"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 5,
+ strlen("helloPropertyBinding"));
+ const auto helloPropertyBindingUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findBindingUsagesFromDefinition") << 9 << 21 << helloPropertyBindingUsages;
+ QTest::addRow("findBindingUsagesFromBinding") << 10 << 19 << helloPropertyBindingUsages;
+ }
+ {
+ const auto testFileName = testFile("findUsages/signalsAndHandlers/signalAndHandlers2.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 7, 14, strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 20, strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 14, 29, strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 15, 24, strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 17, strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 24, 24, strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 25, 21, strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 33, 19, strlen("myHelloHandler"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 42, 29, strlen("myHelloHandler"));
+ const auto myHelloHandlerUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findJSMethodFromUsageInBinding") << 8 << 27 << myHelloHandlerUsages;
+ QTest::addRow("findJSMethodFromDefinition") << 7 << 22 << myHelloHandlerUsages;
+ QTest::addRow("findJSMethodFromDefinition2") << 7 << 9 << myHelloHandlerUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 13, 18, strlen("checkHandlers"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 14, 5,
+ strlen("onCheckHandlersChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 17, 9,
+ strlen("checkHandlersChanged"));
+ const auto checkHandlersUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlPropertyHandlerFromDefinition") << 13 << 18 << checkHandlersUsages;
+ QTest::addRow("findQmlPropertyHandlerFromHandler") << 14 << 5 << checkHandlersUsages;
+ QTest::addRow("findQmlPropertyHandlerFromSignalCall") << 17 << 9 << checkHandlersUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 15, 5,
+ strlen("onChildrenChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 18, 9, strlen("childrenChanged"));
+ const auto checkCppHandlersUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findCppPropertyHandlerFromHandler") << 15 << 5 << checkCppHandlersUsages;
+ QTest::addRow("findCppPropertyHandlerFromSignalCall") << 18 << 9 << checkCppHandlersUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 20, 18, strlen("_"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 5, strlen("on_Changed"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 27, 9, strlen("_Changed"));
+ const auto checkHandlersUsages2 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlPropertyHandler2FromDefinition") << 20 << 18 << checkHandlersUsages2;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 21, 18, strlen("______42"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 24, 5,
+ strlen("on______42Changed"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 28, 9, strlen("______42Changed"));
+ const auto checkHandlersUsages3 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlPropertyHandler3FromDefinition") << 21 << 18 << checkHandlersUsages3;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 22, 18, strlen("_123a"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 25, 5, strlen("on_123AChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 29, 9, strlen("_123aChanged"));
+ const auto checkHandlersUsages4 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findQmlPropertyHandler4FromDefinition") << 22 << 18 << checkHandlersUsages4;
+ }
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ const auto testFileName = testFile("findUsages/connections/connections.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 9, strlen("onClicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 17, 23, strlen("clicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 33, 15, strlen("clicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 16, 22, strlen("onClicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 34, 15, strlen("clicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 18, 23, strlen("clicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 28, 9, strlen("onClicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 35, 15, strlen("clicked"));
+ const auto signalInConnection = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findSignalsInConnectionFromSignal") << 33 << 15 << signalInConnection;
+ QTest::addRow("findSignalsInConnectionFromHandler") << 9 << 9 << signalInConnection;
+ QTest::addRow("findSignalsInConnectionFromFunction") << 16 << 22 << signalInConnection;
+ }
+ {
+ const auto testFileName =
+ testFile("findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 30, strlen("a"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 16, strlen("a"));
+ const auto aParamUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findMethodParameterA") << 9 << 16 << aParamUsages;
+ QTest::addRow("findMethodParameterAFromUsage") << 8 << 30 << aParamUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 50, strlen("x"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 28, strlen("x"));
+ const auto xParamUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findMethodParameterXDeconstructed") << 8 << 50 << xParamUsages;
+ QTest::addRow("findMethodParameterXDeconstructedFromUsage") << 9 << 28 << xParamUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 53, strlen("y"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 32, strlen("y"));
+ const auto yParamUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findMethodParameterYDeconstructed") << 8 << 53 << yParamUsages;
+ QTest::addRow("findMethodParameterYDeconstructedFromUsage") << 9 << 32 << yParamUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 59, strlen("z"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 36, strlen("z"));
+ const auto zParamUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findMethodParameterZDeconstructed") << 8 << 59 << zParamUsages;
+ QTest::addRow("findMethodParameterZDeconstructedFromUsage") << 9 << 36 << zParamUsages;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 13, 14, strlen("a"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 14, 17, strlen("a"));
+ const auto deconstructedAUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("deconstructed") << 14 << 17 << deconstructedAUsages;
+ QTest::addRow("deconstructedFromDefinition") << 13 << 14 << deconstructedAUsages;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/groupPropertyUsage/groupPropertyUsage.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto otherFileName = testFile("findUsages/groupPropertyUsage/fontFamilyUsage.qml");
+ const auto otherFileContent = readFileContent(otherFileName);
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 5, 34, strlen("family"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 14, 17, strlen("family"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 35, strlen("family"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 10, strlen("family"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 33, 48, strlen("family"));
+ const auto groupPropertyUsages1 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("groupPropertyUsages1") << 14 << 17 << groupPropertyUsages1;
+ const auto groupPropertyUsages1FromOtherFile =
+ makeUsages(otherFileName, expectedUsages);
+ QTest::addRow("groupPropertyUsages1FromOtherFile")
+ << 5 << 37 << groupPropertyUsages1FromOtherFile;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 5, strlen("font"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 24, 5, strlen("font"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 12, 13, strlen("font"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 30, strlen("font"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 32, 41, strlen("font"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 33, 43, strlen("font"));
+ const auto groupPropertyUsages2 = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("groupPropertyUsages2") << 23 << 5 << groupPropertyUsages2;
+ }
+ }
+ {
+ const auto testFileName =
+ testFile("findUsages/attachedPropertyUsage/attachedPropertyUsage.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 5, strlen("Keys"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 12, 25, strlen("Keys"));
+ const auto attachedPropertyUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("attachedPropertyUsages") << 12 << 25 << attachedPropertyUsages;
+ }
+ {
+ const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 22, strlen("foo"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 10, 44, strlen("foo"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 14, 27, strlen("foo"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 20, 20, strlen("foo"));
+ const auto inlineUsages = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("inlineUsagesFromProperty") << 9 << 22 << inlineUsages;
+ QTest::addRow("inlineUsagesFromUsageOfBaseProperty") << 14 << 27 << inlineUsages;
+ QTest::addRow("inlineUsagesFromJsScope") << 20 << 20 << inlineUsages;
+ }
+ {
+ const auto testFileName = testFile("findUsages/propertyChanges/propertyChanges.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 9, strlen("onClicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 16, 21, strlen("onClicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 19, 25, strlen("onClicked"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 25, 17, strlen("onClicked"));
+ const auto propertyChanges = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("propertyChanges1") << 16 << 21 << propertyChanges;
+ }
+ {
+ const auto testFileName = testFile("findUsages/bindings/bindings.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 11, 23, strlen("patronChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 14, 27, strlen("patronChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 21, 27, strlen("patronChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 27, 19, strlen("patronChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 27, 41, strlen("patronChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 34, 17, strlen("patronChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 13, 20, strlen("patronChanged"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 20, 23, strlen("\"patronChanged\""));
+ const auto bindings = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("propertyInBindingsFromDecl") << 11 << 23 << bindings;
+ QTest::addRow("generalizedGroupPropertyBindings") << 27 << 19 << bindings;
+ }
+ {
+ const auto testFileName = testFile("findUsages/enums/Enums.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto otherFileName = testFile("findUsages/enums/EnumsFromAnotherFile.qml");
+ const auto otherFileContent = readFileContent(otherFileName);
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 9, 9, strlen("Patron"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 22, 35, strlen("Patron"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 23, 34, strlen("Patron"));
+ const auto enums = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("enumValuesFromDeclaration") << 9 << 9 << enums;
+ QTest::addRow("enumValuesFromUsage") << 22 << 35 << enums;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 8, 10, strlen("Cats"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 22, 30, strlen("Cats"));
+ const auto enums = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("enumNameFromDeclaration") << 8 << 10 << enums;
+ QTest::addRow("enumNameFromUsage") << 22 << 30 << enums;
+ }
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 26, 46,
+ strlen("FromAnotherUniverse"));
+ expectedUsages << QQmlLSUtilsLocation::from(otherFileName, otherFileContent, 4, 68,
+ strlen("FromAnotherUniverse"));
+ const auto enums = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("enumNameFromDeclarationInOtherFile") << 26 << 50 << enums;
+ const auto enumsFromOtherFile = makeUsages(otherFileName, expectedUsages);
+ QTest::addRow("enumNameFromUsageFromOtherFile") << 4 << 81 << enumsFromOtherFile;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents2.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 4, 15, strlen("MyIC"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 5, 5, strlen("MyIC"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 5, 12, strlen("MyIC"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 5, 19, strlen("MyIC"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 6, 19, strlen("MyIC"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 6, 26, strlen("MyIC"));
+ const auto inlineComponents = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findICUsagesFromDefinition") << 4 << 16 << inlineComponents;
+ QTest::addRow("findICUsagesFromDefinition2") << 4 << 9 << inlineComponents;
+ QTest::addRow("findICUsagesFromUsage") << 5 << 19 << inlineComponents;
+ QTest::addRow("findICUsagesFromTypeUsage") << 6 << 19 << inlineComponents;
+ }
+ }
+ {
+ const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents.qml");
+ const auto testFileContent = readFileContent(testFileName);
+ const auto providerFileName =
+ testFile("findUsages/inlineComponents/InlineComponentProvider.qml");
+ const auto providerFileContent = readFileContent(providerFileName);
+ {
+ QList<QQmlLSUtilsLocation> expectedUsages;
+ expectedUsages << QQmlLSUtilsLocation::from(providerFileName, providerFileContent, 4,
+ 15, strlen("IC1"));
+ expectedUsages << QQmlLSUtilsLocation::from(providerFileName, providerFileContent, 5,
+ 36, strlen("IC1"));
+ expectedUsages << QQmlLSUtilsLocation::from(providerFileName, providerFileContent, 7, 5,
+ strlen("IC1"));
+ expectedUsages << QQmlLSUtilsLocation::from(providerFileName, providerFileContent, 17,
+ 13, strlen("IC1"));
+
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 25, 38,
+ strlen("IC1"));
+ expectedUsages << QQmlLSUtilsLocation::from(testFileName, testFileContent, 25, 84,
+ strlen("IC1"));
+
+ {
+ const auto usagesForTestFile = makeUsages(testFileName, expectedUsages);
+ QTest::addRow("findICUsagesFromTypeAnnotationInOtherFiles")
+ << 25 << 39 << usagesForTestFile;
+ QTest::addRow("findICUsagesFromInstantiationInOtherFiles")
+ << 25 << 84 << usagesForTestFile;
+ }
+
+ {
+ const auto usagesInProviderFile = makeUsages(providerFileName, expectedUsages);
+
+ QTest::addRow("findICUsagesFromDefinitionInOtherFiles")
+ << 4 << 16 << usagesInProviderFile;
+ QTest::addRow("findICUsagesFromInstantiationInOtherFiles2")
+ << 17 << 14 << usagesInProviderFile;
+ }
+ }
+ }
+}
+
+void tst_qmlls_utils::findUsages()
+{
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(UsageData, data);
+ QVERIFY(std::is_sorted(data.expectedUsages.begin(), data.expectedUsages.end()));
+
+ auto [env, file] = createEnvironmentAndLoadFile(data.testFileName);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ if constexpr (enable_debug_output) {
+ if (locations.size() > 1) {
+ for (auto &x : locations)
+ qDebug() << x.domItem.toString();
+ }
+ }
+ QCOMPARE(locations.size(), 1);
+
+ auto usages = QQmlLSUtils::findUsagesOf(locations.front().domItem);
+
+ if constexpr (enable_debug_output) {
+ if (usages != data.expectedUsages) {
+ qDebug() << "Got:\n";
+ for (auto &x : usages) {
+ qDebug() << x.filename << "(" << x.sourceLocation.startLine << ", "
+ << x.sourceLocation.startColumn << "), " << x.sourceLocation.offset << "+"
+ << x.sourceLocation.length;
+ }
+ qDebug() << "But expected: \n";
+ for (auto &x : data.expectedUsages) {
+ qDebug() << x.filename << "(" << x.sourceLocation.startLine << ", "
+ << x.sourceLocation.startColumn << "), " << x.sourceLocation.offset << "+"
+ << x.sourceLocation.length;
+ }
+ }
+ }
+
+ QCOMPARE(usages, data.expectedUsages);
+}
+
+
+void tst_qmlls_utils::renameUsages_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QString>("newName");
+ QTest::addColumn<QList<QQmlLSUtilsEdit>>("expectedRenames");
+ QTest::addColumn<QString>("expectedError");
+
+ const QString testFileName = testFile(u"JSUsages.qml"_s);
+ const QString testFileNameFromAnotherFile = testFile(u"JSUsagesFromAnotherFile.qml"_s);
+ const QString testFileContent = readFileContent(testFileName);
+ const QString testFileFromAnotherFileContent = readFileContent(testFileNameFromAnotherFile);
+
+ const QString noError;
+ const QList<QQmlLSUtilsEdit> noRenames;
+
+ QList<QQmlLSUtilsEdit> methodFRename{
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 72, 14, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 74, 24, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 74, 34, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 74, 51, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 74, 68, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 76, 20, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 79, 34, strlen("recursive"),
+ u"newNameNewMe"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 84, 27, strlen("recursive"),
+ u"newNameNewMe"_s),
+ };
+
+ QList<QQmlLSUtilsEdit> JSIdentifierSumRename{
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 8, 13, strlen("sum"),
+ u"sumsumsum123"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 10, 13, strlen("sum"),
+ u"sumsumsum123"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 10, 19, strlen("sum"),
+ u"sumsumsum123"_s),
+ };
+
+ QList<QQmlLSUtilsEdit> qmlSignalRename{
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 88, 12, strlen("helloSignal"),
+ u"finalSignal"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 91, 9, strlen("helloSignal"),
+ u"finalSignal"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 93, 13, strlen("helloSignal"),
+ u"finalSignal"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 97, 17, strlen("helloSignal"),
+ u"finalSignal"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 101, 9, strlen("helloSignal"),
+ u"finalSignal"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 119, 5, strlen("onHelloSignal"),
+ u"onFinalSignal"_s),
+ };
+
+ QList<QQmlLSUtilsEdit> helloPropertyRename{
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 17, 18, strlen("helloProperty"),
+ u"freshPropertyName"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 24, 13, strlen("helloProperty"),
+ u"freshPropertyName"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 24, 29, strlen("helloProperty"),
+ u"freshPropertyName"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 65, 60, strlen("helloProperty"),
+ u"freshPropertyName"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 151, 9, strlen("helloPropertyChanged"),
+ u"freshPropertyNameChanged"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 153, 5,
+ strlen("onHelloPropertyChanged"), u"onFreshPropertyNameChanged"_s),
+ QQmlLSUtilsEdit::from(testFileNameFromAnotherFile, testFileFromAnotherFileContent, 12, 16,
+ strlen("helloProperty"), u"freshPropertyName"_s),
+ };
+
+ QList<QQmlLSUtilsEdit> nestedComponentRename{
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 42, 15, strlen("NestedComponent"),
+ u"SuperInlineComponent"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 61, 5, strlen("NestedComponent"),
+ u"SuperInlineComponent"_s),
+ };
+
+ QList<QQmlLSUtilsEdit> myNestedIdRename{
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 62, 13, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 65, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 66, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 67, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 68, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ QQmlLSUtilsEdit::from(testFileName, testFileContent, 69, 17, strlen("myNested"),
+ u"freshNewIdForMyNested"_s),
+ };
+
+ std::sort(methodFRename.begin(), methodFRename.end());
+ std::sort(JSIdentifierSumRename.begin(), JSIdentifierSumRename.end());
+ std::sort(qmlSignalRename.begin(), qmlSignalRename.end());
+ std::sort(helloPropertyRename.begin(), helloPropertyRename.end());
+ std::sort(helloPropertyRename.begin(), helloPropertyRename.end());
+ std::sort(nestedComponentRename.begin(), nestedComponentRename.end());
+ std::sort(myNestedIdRename.begin(), myNestedIdRename.end());
+
+ const QString parserError = u"Invalid EcmaScript identifier!"_s;
+
+ QTest::addRow("renameMethod") << testFileName << 72 << 19 << u"newNameNewMe"_s << methodFRename
+ << noError;
+ QTest::addRow("renameJSIdentifier")
+ << testFileName << 10 << 19 << u"sumsumsum123"_s << JSIdentifierSumRename << noError;
+ QTest::addRow("renameQmlSignal")
+ << testFileName << 93 << 19 << u"finalSignal"_s << qmlSignalRename << noError;
+ QTest::addRow("renameQmlSignalHandler")
+ << testFileName << 119 << 10 << u"onFinalSignal"_s << qmlSignalRename << noError;
+
+ QTest::addRow("renameQmlProperty")
+ << testFileName << 17 << 20 << u"freshPropertyName"_s << helloPropertyRename << noError;
+ QTest::addRow("renameQmlPropertyChanged")
+ << testFileName << 151 << 18 << u"freshPropertyNameChanged"_s << helloPropertyRename
+ << noError;
+ QTest::addRow("renameQmlPropertyChangedHandler")
+ << testFileName << 153 << 22 << u"onFreshPropertyNameChanged"_s << helloPropertyRename
+ << noError;
+
+ QTest::addRow("renameQmlObjectId") << testFileName << 65 << 21 << u"freshNewIdForMyNested"_s
+ << myNestedIdRename << noError;
+
+ // rename forbidden stuff
+ QTest::addRow("renameCPPDefinedItem") << testFileName << 144 << 13 << u"onHelloWorld"_s
+ << noRenames << u"defined in non-QML files."_s;
+ QTest::addRow("renameFunctionKeyword") << testFileName << 8 << 10 << u"HelloWorld"_s
+ << noRenames << "Requested item cannot be renamed";
+ QTest::addRow("invalidCharactersInIdentifier")
+ << testFileName << 12 << 22 << u"\""_s << noRenames << parserError;
+ QTest::addRow("invalidCharactersInIdentifier2")
+ << testFileName << 12 << 22 << u"hello world"_s << noRenames << parserError;
+ QTest::addRow("invalidCharactersInIdentifier3")
+ << testFileName << 12 << 22 << u"sum.sum.sum"_s << noRenames << parserError;
+ QTest::addRow("emptyIdentifier")
+ << testFileName << 12 << 22 << QString() << noRenames << parserError;
+ QTest::addRow("usingKeywordAsIdentifier")
+ << testFileName << 12 << 22 << u"function"_s << noRenames << parserError;
+
+ QTest::addRow("changedSignalHandlerMissingOnChanged")
+ << testFileName << 134 << 9 << u"___"_s << noRenames
+ << u"Invalid name for a property changed handler identifier"_s;
+ QTest::addRow("changedSignalHandlerMissingChanged")
+ << testFileName << 134 << 9 << u"on___"_s << noRenames
+ << u"Invalid name for a property changed handler identifier"_s;
+ QTest::addRow("changedSignalHandlerMissingOn")
+ << testFileName << 134 << 9 << u"___Changed"_s << noRenames
+ << u"Invalid name for a property changed handler identifier"_s;
+ QTest::addRow("changedSignalHandlerTypoInChanged")
+ << testFileName << 134 << 9 << u"on___Chänged"_s << noRenames
+ << u"Invalid name for a property changed handler identifier"_s;
+
+ QTest::addRow("signalHandlerMissingOn")
+ << testFileName << 119 << 10 << u"helloSuperSignal"_s << noRenames
+ << u"Invalid name for a signal handler identifier"_s;
+ QTest::addRow("signalHandlerMissingCapitalization")
+ << testFileName << 119 << 10 << u"onhelloSuperSignal"_s << noRenames
+ << u"Invalid name for a signal handler identifier"_s;
+
+ QTest::addRow("JSIdentifierStartsWithNumber")
+ << testFileName << 67 << 13 << u"123"_s << noRenames << parserError;
+}
+
+void tst_qmlls_utils::renameUsages()
+{
+ // findAndRenameUsages() already tests if all usages will be renamed
+ // now test that the new name is correctly passed
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, newName);
+ QFETCH(QList<QQmlLSUtilsEdit>, expectedRenames);
+ QFETCH(QString, expectedError);
+
+ QVERIFY(std::is_sorted(expectedRenames.begin(), expectedRenames.end()));
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ if constexpr (enable_debug_output) {
+ if (locations.size() > 1) {
+ for (auto &x : locations)
+ qDebug() << x.domItem.toString();
+ }
+ }
+ QCOMPARE(locations.size(), 1);
+
+ if (auto errors = QQmlLSUtils::checkNameForRename(locations.front().domItem, newName)) {
+ if constexpr (enable_debug_output) {
+ if (expectedError.isEmpty())
+ qDebug() << "Expected no error but got" << errors->message;
+ if (!errors->message.contains(expectedError))
+ qDebug() << "Cannot find" << expectedError << "in" << errors->message;
+ }
+ QVERIFY(!expectedError.isEmpty());
+ QVERIFY(errors->message.contains(expectedError));
+ return;
+ }
+ auto edits = QQmlLSUtils::renameUsagesOf(locations.front().domItem, newName);
+
+ if constexpr (enable_debug_output) {
+ if (edits != expectedRenames) {
+ qDebug() << "Got:\n";
+ for (auto &x : edits) {
+ qDebug() << x.replacement << x.location.filename << "("
+ << x.location.sourceLocation.startLine << ", "
+ << x.location.sourceLocation.startColumn << "), "
+ << x.location.sourceLocation.offset << "+"
+ << x.location.sourceLocation.length;
+ }
+ qDebug() << "But expected: \n";
+ for (auto &x : expectedRenames) {
+ qDebug() << x.replacement << x.location.filename << "("
+ << x.location.sourceLocation.startLine << ", "
+ << x.location.sourceLocation.startColumn << "), "
+ << x.location.sourceLocation.offset << "+"
+ << x.location.sourceLocation.length;
+ }
+ }
+ }
+ QCOMPARE(edits, expectedRenames);
+}
+
+void tst_qmlls_utils::findDefinitionFromLocation_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+
+ QTest::addColumn<QString>("expectedFilePath");
+ // set to -1 when unchanged from above line and character. 0-based.
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<int>("expectedCharacter");
+ QTest::addColumn<size_t>("expectedLength");
+
+ const QString JSDefinitionsQml = testFile(u"JSDefinitions.qml"_s);
+ const QString BaseTypeQml = testFile(u"BaseType.qml"_s);
+
+ QTest::addRow("JSIdentifierX")
+ << JSDefinitionsQml << 14 << 11 << JSDefinitionsQml << 13 << 13 << strlen("x");
+ QTest::addRow("JSIdentifierX2")
+ << JSDefinitionsQml << 15 << 11 << JSDefinitionsQml << 13 << 13 << strlen("x");
+ QTest::addRow("propertyI") << JSDefinitionsQml << 14 << 14 << JSDefinitionsQml << 9 << 18
+ << strlen("i");
+ QTest::addRow("qualifiedPropertyI")
+ << JSDefinitionsQml << 15 << 21 << JSDefinitionsQml << 9 << 18 << strlen("i");
+ QTest::addRow("inlineComponentProperty")
+ << JSDefinitionsQml << 62 << 21 << JSDefinitionsQml << 54 << 22 << strlen("data");
+
+ QTest::addRow("parameterA") << JSDefinitionsQml << 10 << 16 << JSDefinitionsQml << 10 << 16
+ << strlen("a");
+ QTest::addRow("parameterAUsage")
+ << JSDefinitionsQml << 10 << 39 << JSDefinitionsQml << -1 << 16 << strlen("a");
+
+ QTest::addRow("parameterB") << JSDefinitionsQml << 10 << 28 << JSDefinitionsQml << 10 << 28
+ << strlen("b");
+ QTest::addRow("parameterBUsage")
+ << JSDefinitionsQml << 10 << 86 << JSDefinitionsQml << -1 << 28 << strlen("b");
+
+ QTest::addRow("comment") << JSDefinitionsQml << 10 << 21 << noResultExpected << -1 << -1
+ << size_t{};
+
+ QTest::addRow("scopedX") << JSDefinitionsQml << 22 << 18 << JSDefinitionsQml << 21 << 17
+ << strlen("scoped");
+ QTest::addRow("scopedX2") << JSDefinitionsQml << 25 << 22 << JSDefinitionsQml << 21 << 17
+ << strlen("scoped");
+ QTest::addRow("scopedX3") << JSDefinitionsQml << 28 << 14 << JSDefinitionsQml << 19 << 13
+ << strlen("scoped");
+
+ QTest::addRow("normalI") << JSDefinitionsQml << 22 << 23 << JSDefinitionsQml << 9 << 18
+ << strlen("i");
+ QTest::addRow("scopedI") << JSDefinitionsQml << 25 << 27 << JSDefinitionsQml << 24 << 32
+ << strlen("i");
+
+ QTest::addRow("shadowingProperty")
+ << JSDefinitionsQml << 37 << 21 << JSDefinitionsQml << 34 << 22 << strlen("i");
+ QTest::addRow("shadowingQualifiedProperty")
+ << JSDefinitionsQml << 37 << 35 << JSDefinitionsQml << 34 << 22 << strlen("i");
+ QTest::addRow("shadowedProperty")
+ << JSDefinitionsQml << 37 << 49 << JSDefinitionsQml << 9 << 18 << strlen("i");
+
+ QTest::addRow("propertyInBinding")
+ << JSDefinitionsQml << 64 << 37 << JSDefinitionsQml << 9 << 18 << strlen("i");
+ QTest::addRow("propertyInBinding2")
+ << JSDefinitionsQml << 65 << 38 << JSDefinitionsQml << 9 << 18 << strlen("i");
+ QTest::addRow("propertyInBinding3")
+ << JSDefinitionsQml << 66 << 51 << JSDefinitionsQml << 9 << 18 << strlen("i");
+
+ QTest::addRow("propertyFromDifferentFile")
+ << JSDefinitionsQml << 72 << 20 << BaseTypeQml << 24 << 18 << strlen("helloProperty");
+
+ QTest::addRow("id") << JSDefinitionsQml << 15 << 17 << JSDefinitionsQml << 7 << 9
+ << strlen("rootId");
+ QTest::addRow("onId") << JSDefinitionsQml << 32 << 16 << JSDefinitionsQml << 32 << 13
+ << strlen("nested");
+ QTest::addRow("parentId") << JSDefinitionsQml << 37 << 44 << JSDefinitionsQml << 7 << 9
+ << strlen("rootId");
+ QTest::addRow("currentId") << JSDefinitionsQml << 37 << 30 << JSDefinitionsQml << 32 << 13
+ << strlen("nested");
+ QTest::addRow("inlineComponentId")
+ << JSDefinitionsQml << 56 << 35 << JSDefinitionsQml << 52 << 13 << strlen("helloIC");
+
+ QTest::addRow("recursiveFunction")
+ << JSDefinitionsQml << 39 << 28 << JSDefinitionsQml << 36 << 18 << strlen("f");
+ QTest::addRow("recursiveFunction2")
+ << JSDefinitionsQml << 39 << 39 << JSDefinitionsQml << 36 << 18 << strlen("f");
+ QTest::addRow("functionFromFunction")
+ << JSDefinitionsQml << 44 << 20 << JSDefinitionsQml << 36 << 18 << strlen("f");
+ QTest::addRow("qualifiedFunctionName")
+ << JSDefinitionsQml << 48 << 23 << JSDefinitionsQml << 36 << 18 << strlen("f");
+
+ QTest::addRow("functionInParent")
+ << JSDefinitionsQml << 44 << 37 << JSDefinitionsQml << 18 << 14 << strlen("ffff");
+ QTest::addRow("functionFromDifferentFile")
+ << JSDefinitionsQml << 72 << 47 << BaseTypeQml << 25 << 14 << strlen("helloFunction");
+ QTest::addRow("componentFromFile")
+ << JSDefinitionsQml << 68 << 28 << BaseTypeQml << 6 << 1 << strlen("Item");
+ QTest::addRow("inlineComponentFromDifferentFile")
+ << JSDefinitionsQml << 75 << 27 << BaseTypeQml << 9 << 38 << strlen("Item");
+}
+
+void tst_qmlls_utils::findDefinitionFromLocation()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QString, expectedFilePath);
+ QFETCH(int, expectedLine);
+ QFETCH(int, expectedCharacter);
+ QFETCH(size_t, expectedLength);
+
+ if (expectedLine == -1)
+ expectedLine = line;
+ if (expectedCharacter == -1)
+ expectedCharacter = character;
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+ Q_ASSERT(expectedLine > 0);
+ Q_ASSERT(expectedCharacter > 0);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QCOMPARE(locations.size(), 1);
+
+ auto definition = QQmlLSUtils::findDefinitionOf(locations.front().domItem);
+
+ // if expectedFilePath is empty, we probably just want to make sure that it does
+ // not crash
+ if (expectedFilePath == noResultExpected) {
+ QVERIFY(!definition);
+ return;
+ }
+
+ QVERIFY(definition);
+
+ QCOMPARE(definition->filename, expectedFilePath);
+
+ QCOMPARE(definition->sourceLocation.startLine, quint32(expectedLine));
+ QCOMPARE(definition->sourceLocation.startColumn, quint32(expectedCharacter));
+ QCOMPARE(definition->sourceLocation.length, quint32(expectedLength));
+}
+
+void tst_qmlls_utils::resolveExpressionType_data()
+{
+ QTest::addColumn<QString>("filePath");
+ // keep in mind that line and character are starting at 1!
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<QQmlLSUtilsResolveOptions>("resolveOption");
+ QTest::addColumn<QString>("expectedFile");
+ // startline of the owners definition
+ QTest::addColumn<int>("expectedLine");
+ QTest::addColumn<QQmlLSUtilsIdentifierType>("expectedType");
+
+ const int noLine = -1;
+ const QString noFile;
+
+ {
+ const QString JSDefinitionsQml = testFile(u"JSDefinitions.qml"_s);
+ const int parentLine = 6;
+ const int childLine = 31;
+
+ QTest::addRow("id") << JSDefinitionsQml << 15 << 17 << ResolveOwnerType << JSDefinitionsQml
+ << parentLine << QmlObjectIdIdentifier;
+ QTest::addRow("childIddInChild") << JSDefinitionsQml << 37 << 30 << ResolveOwnerType
+ << JSDefinitionsQml << childLine << QmlObjectIdIdentifier;
+ QTest::addRow("parentIdInChild") << JSDefinitionsQml << 37 << 43 << ResolveOwnerType
+ << JSDefinitionsQml << parentLine << QmlObjectIdIdentifier;
+
+ QTest::addRow("propertyI") << JSDefinitionsQml << 14 << 14 << ResolveOwnerType
+ << JSDefinitionsQml << parentLine << PropertyIdentifier;
+ QTest::addRow("qualifiedPropertyI") << JSDefinitionsQml << 15 << 21 << ResolveOwnerType
+ << JSDefinitionsQml << parentLine << PropertyIdentifier;
+ QTest::addRow("propertyIInChild") << JSDefinitionsQml << 37 << 21 << ResolveOwnerType
+ << JSDefinitionsQml << childLine << PropertyIdentifier;
+ QTest::addRow("qualifiedChildPropertyIInChild")
+ << JSDefinitionsQml << 37 << 35 << ResolveOwnerType << JSDefinitionsQml
+ << childLine << PropertyIdentifier;
+ QTest::addRow("qualifiedParentPropertyIInChild")
+ << JSDefinitionsQml << 37 << 49 << ResolveOwnerType << JSDefinitionsQml
+ << parentLine << PropertyIdentifier;
+
+ QTest::addRow("childMethod") << JSDefinitionsQml << 48 << 23 << ResolveOwnerType
+ << JSDefinitionsQml << childLine << MethodIdentifier;
+ QTest::addRow("childMethod2") << JSDefinitionsQml << 44 << 20 << ResolveOwnerType
+ << JSDefinitionsQml << childLine << MethodIdentifier;
+ QTest::addRow("parentMethod") << JSDefinitionsQml << 14 << 9 << ResolveOwnerType
+ << JSDefinitionsQml << parentLine << MethodIdentifier;
+ }
+
+ {
+ const QString bindingsOnDeferredQml =
+ testFile(u"resolveExpressionType/BindingsOnDeferred.qml"_s);
+ const QString qQuickControl = u"private/qquickcontrol_p.h"_s;
+ const QString qQuickKeysAttachedType = u"private/qquickitem_p.h"_s;
+ QTest::addRow("bindingOnId") << bindingsOnDeferredQml << 12 << 14 << ResolveOwnerType
+ << bindingsOnDeferredQml << 8 << QmlObjectIdIdentifier;
+ QTest::addRow("bindingOnQualifiedDeferredProperty")
+ << bindingsOnDeferredQml << 12 << 24 << ResolveOwnerType << qQuickControl << noLine
+ << PropertyIdentifier;
+ QTest::addRow("groupedPropertyBindingOnId")
+ << bindingsOnDeferredQml << 14 << 14 << ResolveOwnerType << bindingsOnDeferredQml
+ << 8 << QmlObjectIdIdentifier;
+ QTest::addRow("someDeferredProperty")
+ << bindingsOnDeferredQml << 15 << 22 << ResolveOwnerType << qQuickControl << noLine
+ << PropertyIdentifier;
+ }
+
+ {
+ const QString JSUsagesQml = testFile(u"JSUsages.qml"_s);
+ const int rootLine = 6;
+ const int nestedComponent2Line = 46;
+ const int nestedComponent3Line = 51;
+ const int nestedComponent4Line = 57;
+ QTest::addRow("propertyAccess:inner.inner") << JSUsagesQml << 68 << 34 << ResolveOwnerType
+ << JSUsagesQml << nestedComponent2Line << PropertyIdentifier;
+ QTest::addRow("propertyAccess:inner.inner2") << JSUsagesQml << 69 << 34 << ResolveOwnerType
+ << JSUsagesQml << nestedComponent2Line << PropertyIdentifier;
+ QTest::addRow("propertyAccess:inner.inner.inner")
+ << JSUsagesQml << 69 << 40 << ResolveOwnerType << JSUsagesQml
+ << nestedComponent3Line << PropertyIdentifier;
+ QTest::addRow("propertyAccess:inner.inner.inner.p2")
+ << JSUsagesQml << 69 << 44 << ResolveOwnerType << JSUsagesQml
+ << nestedComponent4Line << PropertyIdentifier;
+
+ QTest::addRow("propertyAccess:helloProperty")
+ << JSUsagesQml << 65 << 68 << ResolveOwnerType << JSUsagesQml << rootLine << PropertyIdentifier;
+ QTest::addRow("propertyAccess:nestedHelloProperty")
+ << JSUsagesQml << 65 << 46 << ResolveOwnerType << JSUsagesQml
+ << nestedComponent4Line << PropertyIdentifier;
+ }
+
+ {
+ const QString derivedType = testFile(u"resolveExpressionType/DerivedType.qml"_s);
+ const QString derived2Type = testFile(u"resolveExpressionType/Derived2.qml"_s);
+ const QString baseType = testFile(u"resolveExpressionType/BaseType.qml"_s);
+ const QString qQuickValueTypes = u"private/qquickvaluetypes_p.h"_s;
+ const QString qQuickKeysAttachedType = u"private/qquickitem_p.h"_s;
+
+ const int baseTypeLine = 6;
+ const int derivedTypeLine = 6;
+ const int keysLine = 29;
+
+ QTest::addRow("ownerOfMethod")
+ << derivedType << 9 << 13 << ResolveOwnerType << baseType << baseTypeLine << MethodIdentifier;
+ QTest::addRow("ownerOfMethod2")
+ << derivedType << 15 << 33 << ResolveOwnerType << baseType << baseTypeLine << MethodIdentifier;
+ QTest::addRow("ownerOfQualifiedMethod")
+ << derivedType << 22 << 46 << ResolveOwnerType << baseType << baseTypeLine << MethodIdentifier;
+
+ QTest::addRow("ownerOfProperty")
+ << derivedType << 10 << 22 << ResolveOwnerType << baseType << baseTypeLine << PropertyIdentifier;
+ QTest::addRow("ownerOfProperty2")
+ << derivedType << 16 << 37 << ResolveOwnerType << baseType << baseTypeLine << PropertyIdentifier;
+ QTest::addRow("ownerOfQualifiedProperty")
+ << derivedType << 23 << 46 << ResolveOwnerType << baseType << baseTypeLine << PropertyIdentifier;
+
+ QTest::addRow("ownerOfOwnProperty")
+ << derivedType << 16 << 23 << ResolveOwnerType << derivedType << derivedTypeLine << PropertyIdentifier;
+
+ QTest::addRow("ownerOfSignal")
+ << derivedType << 11 << 13 << ResolveOwnerType << baseType << baseTypeLine << SignalIdentifier;
+ QTest::addRow("ownerOfSignal2")
+ << derivedType << 18 << 37 << ResolveOwnerType << baseType << baseTypeLine << SignalIdentifier;
+ QTest::addRow("ownerOfSignalHandler")
+ << derivedType << 19 << 10 << ResolveOwnerType << baseType << baseTypeLine << SignalHandlerIdentifier;
+ QTest::addRow("ownerOfQualifiedSignal")
+ << derivedType << 25 << 22 << ResolveOwnerType << baseType << baseTypeLine << SignalIdentifier;
+
+ QTest::addRow("ownerOfGroupedProperty")
+ << derivedType << 28 << 7 << ResolveOwnerType << baseType << baseTypeLine << GroupedPropertyIdentifier;
+ QTest::addRow("ownerOfGroupedProperty2")
+ << derivedType << 28 << 17 << ResolveOwnerType << qQuickValueTypes << noLine
+ << PropertyIdentifier;
+
+ QTest::addRow("ownerOfAttachedProperty")
+ << derivedType << 29 << 6 << ResolveOwnerType << derivedType << keysLine << AttachedTypeIdentifier;
+ QTest::addRow("ownerOfAttachedProperty2")
+ << derivedType << 29 << 14 << ResolveOwnerType << qQuickKeysAttachedType << noLine
+ << SignalHandlerIdentifier;
+
+ QTest::addRow("actualTypeOfAttachedProperty")
+ << derivedType << 29 << 14 << ResolveActualTypeForFieldMemberExpression << noFile
+ << noLine << SignalHandlerIdentifier;
+
+ QTest::addRow("id")
+ << derivedType << 7 << 10 << ResolveOwnerType << derivedType << 6 << QmlObjectIdIdentifier;
+ QTest::addRow("propertyBinding")
+ << derivedType << 31 << 13 << ResolveOwnerType << baseType << baseTypeLine << PropertyIdentifier;
+
+ QTest::addRow("qmlObject")
+ << derivedType << 6 << 4 << ResolveOwnerType << derived2Type << 4 << QmlComponentIdentifier;
+ }
+}
+
+void tst_qmlls_utils::resolveExpressionType()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(QQmlLSUtilsResolveOptions, resolveOption);
+ QFETCH(QString, expectedFile);
+ QFETCH(int, expectedLine);
+ QFETCH(QQmlLSUtilsIdentifierType, expectedType);
+
+ // they all start at 1.
+ Q_ASSERT(line > 0);
+ Q_ASSERT(character > 0);
+
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QCOMPARE(locations.size(), 1);
+
+ auto definition = QQmlLSUtils::resolveExpressionType(locations.front().domItem, resolveOption);
+
+ QVERIFY(definition);
+ if (!expectedFile.isEmpty()) {
+ QVERIFY(definition->semanticScope);
+ QCOMPARE(definition->semanticScope->filePath(), expectedFile);
+
+ if (expectedLine != -1) {
+ QQmlJS::SourceLocation location = definition->semanticScope->sourceLocation();
+ QCOMPARE((int)location.startLine, expectedLine);
+ }
+ } else {
+ QVERIFY(!definition->semanticScope);
+ }
+ QCOMPARE(definition->type, expectedType);
+}
+
+void tst_qmlls_utils::isValidEcmaScriptIdentifier_data()
+{
+ QTest::addColumn<QString>("identifier");
+ QTest::addColumn<bool>("isValid");
+
+ QTest::addRow("f") << u"f"_s << true;
+ QTest::addRow("f-unicode") << u"\\u0046"_s << true;
+ QTest::addRow("starts-with-digit") << u"8helloWorld"_s << false;
+ QTest::addRow("starts-with-unicode-digit") << u"\\u0038helloWorld"_s << false; // \u0038 == '8'
+ QTest::addRow("keyword") << u"return"_s << false;
+ QTest::addRow("not-keyword") << u"returny"_s << true;
+}
+
+void tst_qmlls_utils::isValidEcmaScriptIdentifier()
+{
+ QFETCH(QString, identifier);
+ QFETCH(bool, isValid);
+
+ QCOMPARE(QQmlLSUtils::isValidEcmaScriptIdentifier(identifier), isValid);
+}
+
+using namespace QLspSpecification;
+
+enum InsertOption { None, InsertColon };
+
+void tst_qmlls_utils::completions_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("character");
+ QTest::addColumn<ExpectedCompletions>("expected");
+ QTest::addColumn<QStringList>("notExpected");
+
+ const QString file = testFile(u"Yyy.qml"_s);
+ const QString emptyFile = testFile(u"emptyFile.qml"_s);
+ const QString pragmaFile = testFile(u"pragmas.qml"_s);
+
+ const QString singletonName = u"SystemInformation"_s;
+ const QString attachedTypeName = u"Component"_s;
+ const QString attachedTypeName2 = u"Keys"_s;
+ const auto attachedTypes = ExpectedCompletions({
+ { attachedTypeName, CompletionItemKind::Class },
+ { attachedTypeName2, CompletionItemKind::Class },
+ });
+
+ const auto keywords = ExpectedCompletions({
+ { u"function"_s, CompletionItemKind::Keyword },
+ { u"required"_s, CompletionItemKind::Keyword },
+ { u"enum"_s, CompletionItemKind::Keyword },
+ { u"component"_s, CompletionItemKind::Keyword },
+ });
+
+ const auto mixedTypes = ExpectedCompletions({
+ { u"Zzz"_s, CompletionItemKind::Class },
+ { u"Item"_s, CompletionItemKind::Class },
+ { u"int"_s, CompletionItemKind::Class },
+ { u"date"_s, CompletionItemKind::Class },
+ });
+ const auto constructorTypes = ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"MyRectangle"_s, CompletionItemKind::Constructor },
+ { u"Zzz"_s, CompletionItemKind::Constructor },
+ { u"Item"_s, CompletionItemKind::Constructor },
+ { u"QtObject"_s, CompletionItemKind::Constructor },
+ });
+ const auto rectangleTypes = ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"MyRectangle"_s, CompletionItemKind::Constructor },
+ });
+
+ QTest::newRow("objEmptyLine") << file << 9 << 1
+ << ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"width"_s, CompletionItemKind::Property },
+ })
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s });
+
+ const QString propertyCompletion = u"property type name: value;"_s;
+ const QString functionCompletion = u"function name(args...): returnType { statements...}"_s;
+
+ const ExpectedCompletions quickSnippetsWithQualifier{
+ { u"QQ.BorderImage snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.BorderImage {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"\twidth: ${3:100}; height: ${4:100}\n"
+ u"\tborder.left: ${5: 5}; border.top: ${5}\n"
+ u"\tborder.right: ${5}; border.bottom: ${5}\n"
+ u"}"_s },
+ { u"QQ.ColorAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.ColorAnimation {\n"
+ u"\tfrom: \"${1:white}\"\n"
+ u"\tto: \"${2:black}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"QQ.Image snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Image {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"}"_s },
+ { u"QQ.Item snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Item {\n"
+ u"\tid: ${1:name}\n"
+ u"}"_s },
+ { u"QQ.NumberAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.NumberAnimation {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"\teasing.type: QQ.Easing.${4:InOutQuad}\n"
+ u"}"_s },
+ { u"QQ.NumberAnimation with targets snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.NumberAnimation {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"QQ.PauseAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PauseAnimation {\n"
+ u"\tduration: ${1:200}\n"
+ u"}"_s },
+ { u"QQ.PropertyAction snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyAction {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ "}"_s },
+ { u"QQ.PropertyAction with targets snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyAction {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"}"_s },
+ { u"QQ.PropertyChanges snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyChanges {\n"
+ u"\ttarget: ${1:object}\n"
+ u"}"_s },
+ { u"QQ.State snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.State {\n"
+ u"\tname: ${1:name}\n"
+ u"\tQQ.PropertyChanges {\n"
+ u"\t\ttarget: ${2:object}\n"
+ u"\t}\n"
+ u"}"_s },
+ { u"QQ.Text snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Text {\n"
+ u"\tid: ${1:name}\n"
+ u"\ttext: qsTr(\"${2:text}\")\n"
+ u"}"_s },
+ { u"QQ.Transition snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Transition {\n"
+ u"\tfrom: \"${1:fromState}\"\n"
+ u"\tto: \"${2:toState}\"\n"
+ u"}"_s },
+ { u"states binding with PropertyChanges in State"_s, CompletionItemKind::Snippet,
+ u"states: [\n"
+ u"\tQQ.State {\n"
+ u"\t\tname: \"${1:name}\"\n"
+ u"\t\tQQ.PropertyChanges {\n"
+ u"\t\t\ttarget: ${2:object}\n"
+ u"\t\t}\n"
+ u"\t}\n"
+ u"]"_s },
+ { u"transitions binding with Transition"_s, CompletionItemKind::Snippet,
+ u"transitions: [\n"
+ u"\tQQ.Transition {\n"
+ u"\t\tfrom: \"${1:fromState}\"\n"
+ u"\t\tto: \"${2:fromState}\"\n"
+ u"\t}\n"
+ u"]"_s }
+ };
+ const ExpectedCompletions quickSnippetsWithoutQualifier{
+ { { u"BorderImage snippet"_s, CompletionItemKind::Snippet,
+ u"BorderImage {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"\twidth: ${3:100}; height: ${4:100}\n"
+ u"\tborder.left: ${5: 5}; border.top: ${5}\n"
+ u"\tborder.right: ${5}; border.bottom: ${5}\n"
+ u"}"_s },
+ { u"ColorAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"ColorAnimation {\n"
+ u"\tfrom: \"${1:white}\"\n"
+ u"\tto: \"${2:black}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"Image snippet"_s, CompletionItemKind::Snippet,
+ u"Image {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"}"_s },
+ { u"Item snippet"_s, CompletionItemKind::Snippet,
+ u"Item {\n"
+ u"\tid: ${1:name}\n"
+ u"}"_s },
+ { u"NumberAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"NumberAnimation {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"\teasing.type: Easing.${4:InOutQuad}\n"
+ u"}"_s },
+ { u"NumberAnimation with targets snippet"_s, CompletionItemKind::Snippet,
+ u"NumberAnimation {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"PauseAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"PauseAnimation {\n"
+ u"\tduration: ${1:200}\n"
+ u"}"_s },
+ { u"PropertyAction snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyAction {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ "}"_s },
+ { u"PropertyAction with targets snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyAction {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"}"_s },
+ { u"PropertyChanges snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyChanges {\n"
+ u"\ttarget: ${1:object}\n"
+ u"}"_s },
+ { u"State snippet"_s, CompletionItemKind::Snippet,
+ u"State {\n"
+ u"\tname: ${1:name}\n"
+ u"\tPropertyChanges {\n"
+ u"\t\ttarget: ${2:object}\n"
+ u"\t}\n"
+ u"}"_s },
+ { u"Text snippet"_s, CompletionItemKind::Snippet,
+ u"Text {\n"
+ u"\tid: ${1:name}\n"
+ u"\ttext: qsTr(\"${2:text}\")\n"
+ u"}"_s },
+ { u"Transition snippet"_s, CompletionItemKind::Snippet,
+ u"Transition {\n"
+ u"\tfrom: \"${1:fromState}\"\n"
+ u"\tto: \"${2:toState}\"\n"
+ u"}"_s } }
+ };
+ const ExpectedCompletions quickSnippetsWithoutQualifierWithBindings = ExpectedCompletions{
+ { { u"states binding with PropertyChanges in State"_s, CompletionItemKind::Snippet,
+ u"states: [\n"
+ u"\tState {\n"
+ u"\t\tname: \"${1:name}\"\n"
+ u"\t\tPropertyChanges {\n"
+ u"\t\t\ttarget: ${2:object}\n"
+ u"\t\t}\n"
+ u"\t}\n"
+ u"]"_s },
+ { u"transitions binding with Transition"_s, CompletionItemKind::Snippet,
+ u"transitions: [\n"
+ u"\tTransition {\n"
+ u"\t\tfrom: \"${1:fromState}\"\n"
+ u"\t\tto: \"${2:fromState}\"\n"
+ u"\t}\n"
+ u"]"_s } }
+ } += quickSnippetsWithoutQualifier;
+ QTest::newRow("objEmptyLineSnippets")
+ << file << 9 << 1
+ << (ExpectedCompletions({
+ { propertyCompletion, CompletionItemKind::Snippet,
+ u"property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"readonly property type name: value;"_s, CompletionItemKind::Snippet,
+ u"readonly property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"default property type name: value;"_s, CompletionItemKind::Snippet,
+ u"default property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"default required property type name: value;"_s,
+ CompletionItemKind::Snippet,
+ u"default required property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"required default property type name: value;"_s,
+ CompletionItemKind::Snippet,
+ u"required default property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"required property type name: value;"_s, CompletionItemKind::Snippet,
+ u"required property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"property type name;"_s, CompletionItemKind::Snippet,
+ u"property ${1:type} ${0:name};"_s },
+ { u"required property type name;"_s, CompletionItemKind::Snippet,
+ u"required property ${1:type} ${0:name};"_s },
+ { u"default property type name;"_s, CompletionItemKind::Snippet,
+ u"default property ${1:type} ${0:name};"_s },
+ { u"default required property type name;"_s, CompletionItemKind::Snippet,
+ u"default required property ${1:type} ${0:name};"_s },
+ { u"required default property type name;"_s, CompletionItemKind::Snippet,
+ u"required default property ${1:type} ${0:name};"_s },
+ { u"signal name(arg1:type1, ...)"_s, CompletionItemKind::Snippet,
+ u"signal ${1:name}($0)"_s },
+ { u"signal name;"_s, CompletionItemKind::Snippet, u"signal ${0:name};"_s },
+ { u"required name;"_s, CompletionItemKind::Snippet,
+ u"required ${0:name};"_s },
+ { functionCompletion, CompletionItemKind::Snippet,
+ u"function ${1:name}($2): ${3:returnType} {\n\t$0\n}"_s },
+ { u"enum name { Values...}"_s, CompletionItemKind::Snippet,
+ u"enum ${1:name} {\n\t${0:values}\n}"_s },
+ { u"component Name: BaseType { ... }"_s, CompletionItemKind::Snippet,
+ u"component ${1:name}: ${2:baseType} {\n\t$0\n}"_s },
+ }) += quickSnippetsWithoutQualifierWithBindings)
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImport")
+ << testFile("qualifiedModule.qml") << 5 << 1
+ << quickSnippetsWithQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportBeforeDot")
+ << testFile("qualifiedModule.qml") << 5 << 7
+ << quickSnippetsWithQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportAfterDot")
+ << testFile("qualifiedModule.qml") << 5 << 8
+ << quickSnippetsWithoutQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s,
+ u"states binding with PropertyChanges in State"_s,
+ u"transitions binding with Transition"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportBeforeDotInBinding")
+ << testFile("qualifiedModule.qml") << 4 << 33 << quickSnippetsWithQualifier
+ << QStringList();
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportAfterDotInBinding")
+ << testFile("qualifiedModule.qml") << 4 << 34 << quickSnippetsWithoutQualifier
+ << QStringList({ u"states binding with PropertyChanges in State"_s,
+ u"transitions binding with Transition"_s });
+
+ // forbid transitions and states because QtObject does not inherit from Item
+ QTest::newRow("qtObjectEmptyLineSnippets")
+ << file << 141 << 8
+ << ExpectedCompletions{ { u"Item"_s, CompletionItemKind::Constructor } }
+ << QStringList({ u"transitions binding with Transition"_s,
+ u"states binding with PropertyChanges in State"_s });
+
+ QTest::newRow("handlers") << file << 5 << 1
+ << ExpectedCompletions{ {
+ { u"onHandleMe"_s, CompletionItemKind::Method },
+ { u"onDefaultPropertyChanged"_s,
+ CompletionItemKind::Method },
+ } }
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s });
+
+ QTest::newRow("attachedTypes")
+ << file << 9 << 1 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
+
+ QTest::newRow("attachedTypesInScript")
+ << file << 6 << 12 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
+ QTest::newRow("attachedTypesInLongScript")
+ << file << 10 << 16 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
+
+ QTest::newRow("completionFromRootId") << file << 10 << 21
+ << ExpectedCompletions({
+ { u"width"_s, CompletionItemKind::Property },
+ { u"lala"_s, CompletionItemKind::Method },
+ { u"foo"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s };
+
+ QTest::newRow("attachedProperties") << file << 89 << 15
+ << ExpectedCompletions({
+ { u"onCompleted"_s, CompletionItemKind::Method },
+ })
+ << QStringList{ u"QtQuick"_s,
+ u"vector4d"_s,
+ attachedTypeName,
+ u"Rectangle"_s,
+ u"property"_s,
+ u"foo"_s,
+ u"onActiveFocusOnTabChanged"_s };
+
+ QTest::newRow("inBindingLabel") << file << 6 << 10
+ << ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { u"width"_s, CompletionItemKind::Property },
+ })
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"property"_s });
+
+ QTest::newRow("afterBinding") << file << 6 << 11
+ << (ExpectedCompletions({
+ { u"height"_s, CompletionItemKind::Property },
+ { u"width"_s, CompletionItemKind::Property },
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ { singletonName, CompletionItemKind::Class },
+ })
+ + attachedTypes)
+ << QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
+
+ QTest::newRow("jsGlobals") << file << 6 << 11
+ << ExpectedCompletions{ {
+ { u"console"_s, CompletionItemKind::Property },
+ { u"Math"_s, CompletionItemKind::Property },
+ } }
+ << QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
+
+ QTest::newRow("jsGlobals2") << file << 100 << 32
+ << ExpectedCompletions{ {
+ { u"abs"_s, CompletionItemKind::Method },
+ { u"log"_s, CompletionItemKind::Method },
+ { u"E"_s, CompletionItemKind::Property },
+ } }
+ << QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s,
+ u"foo"_s, u"lala"_s });
+
+ QTest::newRow("afterLongBinding")
+ << file << 10 << 16
+ << ExpectedCompletions({
+ { u"height"_s, CompletionItemKind::Property },
+ { u"width"_s, CompletionItemKind::Property },
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
+
+ QTest::newRow("afterId") << file << 5 << 8 << ExpectedCompletions({})
+ << QStringList({
+ u"QtQuick"_s,
+ u"property"_s,
+ u"Rectangle"_s,
+ u"width"_s,
+ u"vector4d"_s,
+ u"import"_s,
+ });
+
+ QTest::newRow("emptyFile") << emptyFile << 1 << 1
+ << ExpectedCompletions({
+ { u"import"_s, CompletionItemKind::Keyword },
+ { u"pragma"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s });
+
+ QTest::newRow("importImport") << file << 1 << 4
+ << ExpectedCompletions({
+ { u"import"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s,
+ u"Rectangle"_s });
+
+ QTest::newRow("importModuleStart")
+ << file << 1 << 8
+ << ExpectedCompletions({
+ { u"QtQuick"_s, CompletionItemKind::Module },
+ })
+ << QStringList({ u"vector4d"_s, u"width"_s, u"Rectangle"_s, u"import"_s });
+
+ QTest::newRow("importVersionStart")
+ << file << 1 << 16
+ << ExpectedCompletions({
+ { u"2"_s, CompletionItemKind::Constant },
+ { u"as"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList({ u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
+
+ // QTest::newRow("importVersionMinor")
+ // << uri << 1 << 18
+ // << ExpectedCompletions({
+ // { u"15"_s, CompletionItemKind::Constant },
+ // })
+ // << QStringList({ u"as"_s, u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
+
+ QTest::newRow("expandBase1") << file << 10 << 24
+ << ExpectedCompletions({
+ { u"width"_s, CompletionItemKind::Property },
+ { u"foo"_s, CompletionItemKind::Property },
+ })
+ << QStringList({ u"import"_s, u"Rectangle"_s });
+
+ QTest::newRow("expandBase2") << file << 11 << 30
+ << ExpectedCompletions({
+ { u"width"_s, CompletionItemKind::Property },
+ { u"color"_s, CompletionItemKind::Property },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"Rectangle"_s });
+
+ QTest::newRow("qualifiedTypeCompletionBeforeDot")
+ << testFile(u"qualifiedModule.qml"_s) << 4 << 31
+ << ExpectedCompletions({
+ { u"QQ.Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s, });
+
+ QTest::newRow("qualifiedTypeCompletionAfterDot")
+ << testFile(u"qualifiedModule.qml"_s) << 4 << 35
+ << ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s, u"width"_s });
+
+ QTest::newRow("qualifiedTypeCompletionBeforeDotInDefaultBinding")
+ << testFile(u"qualifiedModule.qml"_s) << 5 << 5
+ << ExpectedCompletions({
+ { u"QQ.Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s });
+
+ QTest::newRow("qualifiedTypeCompletionAfterDotInDefaultBinding")
+ << testFile(u"qualifiedModule.qml"_s) << 5 << 8
+ << ExpectedCompletions({
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s, u"width"_s });
+
+ QTest::newRow("parameterCompletion")
+ << file << 36 << 24
+ << ExpectedCompletions({
+ { u"helloWorld"_s, CompletionItemKind::Variable },
+ { u"helloMe"_s, CompletionItemKind::Variable },
+ })
+ << QStringList();
+
+ QTest::newRow("inMethodName") << file << 15 << 14 << ExpectedCompletions({})
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, u"foo"_s,
+ u"root"_s, u"Item"_s, singletonName };
+
+ QTest::newRow("inMethodReturnType") << file << 17 << 54 << mixedTypes
+ << QStringList{
+ u"QtQuick"_s,
+ u"foo"_s,
+ u"root"_s,
+ };
+
+ QTest::newRow("letStatement") << file << 95 << 13 << ExpectedCompletions({})
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, u"root"_s };
+
+ QTest::newRow("inParameterCompletion") << file << 35 << 39 << ExpectedCompletions({})
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ };
+
+ QTest::newRow("parameterTypeCompletion") << file << 35 << 55 << mixedTypes
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ };
+
+ QTest::newRow("propertyTypeCompletion") << file << 16 << 14 << mixedTypes
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ };
+ QTest::newRow("propertyTypeCompletion2") << file << 16 << 23 << mixedTypes
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ };
+ QTest::newRow("propertyNameCompletion")
+ << file << 16 << 24 << ExpectedCompletions({})
+ << QStringList{
+ u"helloWorld"_s, u"helloMe"_s, u"Zzz"_s, u"Item"_s, u"int"_s, u"date"_s,
+ };
+ QTest::newRow("propertyNameCompletion2")
+ << file << 16 << 25 << ExpectedCompletions({})
+ << QStringList{
+ u"helloWorld"_s, u"helloMe"_s, u"Zzz"_s, u"Item"_s, u"int"_s, u"date"_s,
+ };
+
+ QTest::newRow("propertyDefinitionBinding")
+ << file << 90 << 27
+ << (ExpectedCompletions({
+ { u"lala"_s, CompletionItemKind::Method },
+ { u"createRectangle"_s, CompletionItemKind::Method },
+ { u"createItem"_s, CompletionItemKind::Method },
+ { u"createAnything"_s, CompletionItemKind::Method },
+ }) += constructorTypes)
+ << QStringList{
+ u"helloWorld"_s,
+ u"helloMe"_s,
+ u"int"_s,
+ u"date"_s,
+ };
+
+ QTest::newRow("ignoreNonRelatedTypesForPropertyDefinitionBinding")
+ << file << 16 << 28
+ << (ExpectedCompletions({
+ { u"createRectangle"_s, CompletionItemKind::Method },
+ { u"createItem"_s, CompletionItemKind::Method },
+ { u"createAnything"_s, CompletionItemKind::Method },
+ }) += rectangleTypes)
+ << QStringList{
+ u"Item"_s, u"Zzz"_s, u"helloWorld"_s, u"helloMe"_s,
+ u"int"_s, u"date"_s, u"Item"_s, u"QtObject"_s,
+ };
+
+ QTest::newRow("inBoundObject")
+ << file << 16 << 40
+ << (ExpectedCompletions({
+ { u"objectName"_s, CompletionItemKind::Property },
+ { u"width"_s, CompletionItemKind::Property },
+ { propertyCompletion, CompletionItemKind::Snippet },
+ { functionCompletion, CompletionItemKind::Snippet },
+ }) += constructorTypes)
+ << QStringList{
+ u"helloWorld"_s, u"helloMe"_s, u"int"_s, u"date"_s, u"QtQuick"_s, u"vector4d"_s,
+ };
+
+ QTest::newRow("qualifiedIdentifierCompletion")
+ << file << 37 << 36
+ << ExpectedCompletions({
+ { u"helloProperty"_s, CompletionItemKind::Property },
+ { u"childAt"_s, CompletionItemKind::Method },
+ })
+ << QStringList{ u"helloVar"_s, u"someItem"_s, u"color"_s, u"helloWorld"_s,
+ u"propertyOfZZZ"_s };
+
+ QTest::newRow("scriptExpressionCompletion")
+ << file << 60 << 16
+ << ExpectedCompletions({
+ // parameters
+ { u"jsParameterInChild"_s, CompletionItemKind::Variable },
+ // own properties
+ { u"jsIdentifierInChild"_s, CompletionItemKind::Variable },
+ { u"functionInChild"_s, CompletionItemKind::Method },
+ { u"propertyInChild"_s, CompletionItemKind::Property },
+ // inherited properties from QML
+ { u"functionInBase"_s, CompletionItemKind::Method },
+ { u"propertyInBase"_s, CompletionItemKind::Property },
+ // inherited properties (transitive) from C++
+ { u"objectName"_s, CompletionItemKind::Property },
+ { u"someItem"_s, CompletionItemKind::Value },
+ { u"true"_s, CompletionItemKind::Value },
+ { u"false"_s, CompletionItemKind::Value },
+ { u"null"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"helloVar"_s,
+ u"color"_s,
+ u"helloWorld"_s,
+ u"propertyOfZZZ"_s,
+ u"propertyInDerived"_s,
+ u"functionInDerived"_s,
+ u"jsIdentifierInDerived"_s,
+ u"jsIdentifierInBase"_s,
+ u"lala"_s,
+ u"foo"_s,
+ u"jsParameterInBase"_s,
+ u"jsParameterInDerived"_s,
+ };
+
+ QTest::newRow("qualifiedScriptExpressionCompletion")
+ << file << 60 << 34
+ << ExpectedCompletions({
+ // own properties
+ { u"helloProperty"_s, CompletionItemKind::Property },
+ // inherited properties (transitive) from C++
+ { u"width"_s, CompletionItemKind::Property },
+ })
+ << QStringList{
+ u"helloVar"_s,
+ u"color"_s,
+ u"helloWorld"_s,
+ u"propertyOfZZZ"_s,
+ u"propertyInDerived"_s,
+ u"functionInDerived"_s,
+ u"jsIdentifierInDerived"_s,
+ u"jsIdentifierInBase"_s,
+ u"jsIdentifierInChild"_s,
+ u"lala"_s,
+ u"foo"_s,
+ u"jsParameterInBase"_s,
+ u"jsParameterInDerived"_s,
+ u"jsParameterInChild"_s,
+ u"functionInChild"_s,
+ };
+
+ QTest::newRow("pragma") << pragmaFile << 1 << 8
+ << ExpectedCompletions({
+ { u"NativeMethodBehavior"_s, CompletionItemKind::Value },
+ { u"ComponentBehavior"_s, CompletionItemKind::Value },
+ { u"ListPropertyAssignBehavior"_s,
+ CompletionItemKind::Value },
+ { u"Singleton"_s, CompletionItemKind::Value },
+ // note: only complete the Addressible/Inaddressible part of
+ // ValueTypeBehavior!
+ { u"ValueTypeBehavior"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"FunctionSignatureBehavior"_s,
+ u"Strict"_s,
+ };
+
+ QTest::newRow("pragmaValue") << pragmaFile << 2 << 30
+ << ExpectedCompletions({
+ { u"AcceptThisObject"_s, CompletionItemKind::Value },
+ { u"RejectThisObject"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"FunctionSignatureBehavior"_s,
+ u"Strict"_s,
+ u"NativeMethodBehavior"_s,
+ u"ComponentBehavior"_s,
+ u"ListPropertyAssignBehavior"_s,
+ u"Singleton"_s,
+ u"ValueTypeBehavior"_s,
+ u"Unbound"_s,
+ };
+
+ QTest::newRow("pragmaMultiValue")
+ << pragmaFile << 3 << 43
+ << ExpectedCompletions({
+ { u"ReplaceIfNotDefault"_s, CompletionItemKind::Value },
+ { u"Append"_s, CompletionItemKind::Value },
+ { u"Replace"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"FunctionSignatureBehavior"_s,
+ u"Strict"_s,
+ u"NativeMethodBehavior"_s,
+ u"ComponentBehavior"_s,
+ u"ListPropertyAssignBehavior"_s,
+ u"Singleton"_s,
+ u"ValueTypeBehavior"_s,
+ u"Unbound"_s,
+ };
+
+ QTest::newRow("pragmaWithoutValue")
+ << pragmaFile << 1 << 17
+ << ExpectedCompletions({
+ { u"NativeMethodBehavior"_s, CompletionItemKind::Value },
+ { u"ComponentBehavior"_s, CompletionItemKind::Value },
+ { u"ListPropertyAssignBehavior"_s, CompletionItemKind::Value },
+ { u"Singleton"_s, CompletionItemKind::Value },
+ // note: only complete the Addressible/Inaddressible part of
+ // ValueTypeBehavior!
+ { u"ValueTypeBehavior"_s, CompletionItemKind::Value },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"FunctionSignatureBehavior"_s,
+ u"Strict"_s,
+ };
+
+ QTest::newRow("non-block-scoped-variable")
+ << file << 69 << 21
+ << ExpectedCompletions({
+ { u"helloVarVariable"_s, CompletionItemKind::Variable },
+ })
+ << QStringList{};
+ QTest::newRow("block-scoped-variable")
+ << file << 76 << 21 << ExpectedCompletions{ { u"test2"_s, CompletionItemKind::Method } }
+ << QStringList{ u"helloLetVariable"_s, u"helloVarVariable"_s };
+
+ QTest::newRow("singleton") << file << 78 << 33
+ << ExpectedCompletions({
+ { singletonName, CompletionItemKind::Class },
+ })
+ << QStringList{};
+
+ QTest::newRow("singletonPropertyAndEnums")
+ << file << 78 << 52
+ << ExpectedCompletions({
+ { u"byteOrder"_s, CompletionItemKind::Property },
+ { u"Little"_s, CompletionItemKind::EnumMember },
+ { u"Endian"_s, CompletionItemKind::Enum },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ u"foo"_s,
+ };
+
+ QTest::newRow("enumsFromItem") << file << 86 << 33
+ << ExpectedCompletions({
+ { u"World"_s, CompletionItemKind::EnumMember },
+ { u"ValueOne"_s, CompletionItemKind::EnumMember },
+ { u"ValueTwo"_s, CompletionItemKind::EnumMember },
+ { u"Hello"_s, CompletionItemKind::Enum },
+ { u"MyEnum"_s, CompletionItemKind::Enum },
+ })
+ << QStringList{
+ u"int"_s,
+ u"Rectangle"_s,
+ };
+
+ QTest::newRow("enumsFromEnumName")
+ << file << 87 << 40
+ << ExpectedCompletions({
+ { u"World"_s, CompletionItemKind::EnumMember },
+ })
+ << QStringList{
+ u"int"_s, u"Rectangle"_s, u"foo"_s, u"ValueOne"_s,
+ u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("requiredProperty")
+ << file << 97 << 14
+ << ExpectedCompletions({
+ { u"property"_s, CompletionItemKind::Keyword },
+ { u"default"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList{
+ u"readonly"_s, u"required"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
+ u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("readonlyProperty")
+ << file << 98 << 13
+ << ExpectedCompletions({
+ { u"property"_s, CompletionItemKind::Keyword },
+ { u"default"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList{
+ u"required"_s, u"readonly"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
+ u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("defaultProperty")
+ << file << 99 << 12
+ << ExpectedCompletions({
+ { u"property"_s, CompletionItemKind::Keyword },
+ { u"readonly"_s, CompletionItemKind::Keyword },
+ { u"required"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList{
+ u"default"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
+ u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("defaultProperty2")
+ << file << 99 << 20
+ << ExpectedCompletions({
+ { u"property"_s, CompletionItemKind::Keyword },
+ { u"readonly"_s, CompletionItemKind::Keyword },
+ { u"required"_s, CompletionItemKind::Keyword },
+ })
+ << QStringList{
+ u"default"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
+ u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
+ };
+
+ QTest::newRow("defaultProperty3")
+ << file << 99 << 21 << ExpectedCompletions{ { u"int"_s, CompletionItemKind::Class } }
+ << QStringList{
+ u"property"_s,
+ u"readonly"_s,
+ u"required"_s,
+ };
+
+ const QString forStatementCompletion = u"for (initializer; condition; increment) { statements... }"_s;
+ const QString ifStatementCompletion = u"if (condition) statement"_s;
+ const QString letStatementCompletion = u"let variable = value;"_s;
+ const QString constStatementCompletion = u"const variable = value;"_s;
+ const QString varStatementCompletion = u"var variable = value;"_s;
+
+ // for the for loop
+ const QString letStatementCompletionWithoutSemicolon = letStatementCompletion.chopped(1);
+ const QString constStatementCompletionWithoutSemicolon = constStatementCompletion.chopped(1);
+ const QString varStatementCompletionWithoutSemicolon = varStatementCompletion.chopped(1);
+
+ const QString caseStatementCompletion = u"case value: statements..."_s;
+ const QString caseStatement2Completion = u"case value: { statements... }"_s;
+ const QString defaultStatementCompletion = u"default: statements..."_s;
+ const QString defaultStatement2Completion = u"default: { statements... }"_s;
+
+ // warning: the completion strings in the test below were all tested by hand in VS Code to
+ // make sure they are easy to use. Make sure to check the code snippets by hand before changing
+ // them.
+ QTest::newRow("jsStatements")
+ << file << 104 << 1
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet,
+ u"let ${1:variable} = $0;"_s },
+ { u"const variable = value;"_s, CompletionItemKind::Snippet,
+ u"const ${1:variable} = $0;"_s },
+ { u"var variable = value;"_s, CompletionItemKind::Snippet,
+ u"var ${1:variable} = $0;"_s },
+ { u"{ statements... }"_s, CompletionItemKind::Snippet,
+ u"{\n\t$0\n}"_s },
+ { u"if (condition) { statements }"_s,
+ CompletionItemKind::Snippet, u"if ($1) {\n\t$0\n}"_s },
+ { u"do { statements } while (condition);"_s,
+ CompletionItemKind::Snippet, u"do {\n\t$1\n} while ($0);"_s },
+ { u"while (condition) { statements...}"_s,
+ CompletionItemKind::Snippet, u"while ($1) {\n\t$0\n}"_s },
+ { forStatementCompletion,
+ CompletionItemKind::Snippet, u"for ($1;$2;$3) {\n\t$0\n}"_s },
+ { u"try { statements... } catch(error) { statements... }"_s,
+ CompletionItemKind::Snippet, u"try {\n\t$1\n} catch($2) {\n\t$0\n}"_s },
+ { u"try { statements... } finally { statements... }"_s,
+ CompletionItemKind::Snippet, u"try {\n\t$1\n} finally {\n\t$0\n}"_s },
+ { u"try { statements... } catch(error) { statements... } finally { statements... }"_s,
+ CompletionItemKind::Snippet, u"try {\n\t$1\n} catch($2) {\n\t$3\n} finally {\n\t$0\n}"_s },
+ { u"for (property in object) { statements... }"_s,
+ CompletionItemKind::Snippet, u"for ($1 in $2) {\n\t$0\n}"_s },
+ { u"for (element of array) { statements... }"_s,
+ CompletionItemKind::Snippet, u"for ($1 of $2) {\n\t$0\n}"_s },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ caseStatementCompletion,
+ caseStatement2Completion,
+ defaultStatementCompletion,
+ defaultStatement2Completion,
+ };
+
+ QTest::newRow("forStatementLet")
+ << file << 103 << 13
+ << ExpectedCompletions{ { letStatementCompletionWithoutSemicolon,
+ CompletionItemKind::Snippet, u"let ${1:variable} = $0"_s },
+ { constStatementCompletionWithoutSemicolon,
+ CompletionItemKind::Snippet, u"const ${1:variable} = $0"_s },
+ { varStatementCompletionWithoutSemicolon,
+ CompletionItemKind::Snippet, u"var ${1:variable} = $0"_s },
+ { u"helloJSStatements"_s, CompletionItemKind::Method } }
+ << QStringList{ u"property"_s,
+ u"readonly"_s,
+ u"required"_s,
+ forStatementCompletion,
+ ifStatementCompletion,
+ letStatementCompletion,
+ constStatementCompletion,
+ varStatementCompletion };
+
+ QTest::newRow("forStatementCondition")
+ << file << 103 << 25
+ << ExpectedCompletions{
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"i"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion, }
+ ;
+
+ QTest::newRow("forStatementIncrement")
+ << file << 103 << 30
+ << ExpectedCompletions{
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"i"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion, }
+ ;
+
+ QTest::newRow("forStatementIncrement2")
+ << file << 103 << 33
+ << ExpectedCompletions{ { u"helloJSStatements"_s, CompletionItemKind::Method } }
+ << QStringList{
+ u"property"_s, u"readonly"_s,
+ u"required"_s, forStatementCompletion,
+ ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion,
+ };
+
+ QTest::newRow("forStatementWithoutBlock")
+ << file << 107 << 12
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"j"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("blockStatementBeforeBracket")
+ << file << 103 << 36
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"i"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("blockStatementAfterBracket")
+ << file << 103 << 37
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("ifStatementCondition")
+ << file << 110 << 15
+ << ExpectedCompletions{
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion, constStatementCompletion }
+ ;
+
+ QTest::newRow("ifStatementConsequence")
+ << file << 111 << 12
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion }
+ ;
+
+ QTest::newRow("ifStatementAlternative")
+ << file << 113 << 12
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion }
+ ;
+
+ QTest::newRow("binaryExpressionCompletionInsideStatement")
+ << file << 113 << 21
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, forStatementCompletion }
+ ;
+
+ QTest::newRow("elseIfStatement")
+ << file << 121 << 18
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, letStatementCompletion, ifStatementCompletion }
+ ;
+ QTest::newRow("returnStatement")
+ << file << 125 << 16
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+ QTest::newRow("returnStatement2")
+ << testFile("completions/returnStatement.qml") << 8 << 15
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("whileCondition")
+ << file << 128 << 16
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("whileConsequence")
+ << file << 128 << 22
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
+ { letStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("doWhileCondition")
+ << file << 131 << 30
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("doWhileConsequence")
+ << file << 131 << 12
+ << ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
+ { letStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("forInStatementLet")
+ << file << 134 << 13
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method } }
+ << QStringList{
+ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion,
+ };
+
+ QTest::newRow("forOfStatementLet")
+ << file << 135 << 13
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method } }
+ << QStringList{
+ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion,
+ };
+
+ QTest::newRow("forInStatementTarget")
+ << file << 134 << 25
+ << ExpectedCompletions{
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion, }
+ ;
+
+ QTest::newRow("forOfStatementTarget")
+ << file << 135 << 24
+ << ExpectedCompletions{
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"hello"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
+ forStatementCompletion, ifStatementCompletion, varStatementCompletion,
+ letStatementCompletion, constStatementCompletion, }
+ ;
+
+ QTest::newRow("forInStatementConsequence")
+ << file << 134 << 31
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"hello"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("forOfStatementConsequence")
+ << file << 135 << 30
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { constStatementCompletion, CompletionItemKind::Snippet },
+ { varStatementCompletion, CompletionItemKind::Snippet },
+ { u"helloJSStatements"_s, CompletionItemKind::Method },
+ { u"hello"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("binaryExpressionRHS") << file << 138 << 17
+ << ExpectedCompletions{
+ { u"log"_s, CompletionItemKind::Method },
+ { u"error"_s, CompletionItemKind::Method },
+ }
+ << QStringList{ propertyCompletion, u"helloVarVariable"_s,
+ u"test1"_s, u"width"_s,
+ u"height"_s, u"layer"_s,
+ u"left"_s, forStatementCompletion }
+ ;
+ QTest::newRow("binaryExpressionLHS") << file << 138 << 12
+ << ExpectedCompletions{
+ { u"qualifiedScriptIdentifiers"_s, CompletionItemKind::Method },
+ { u"width"_s, CompletionItemKind::Property },
+ { u"layer"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ u"log"_s, u"error"_s, forStatementCompletion}
+ ;
+
+ const QString missingRHSFile = testFile(u"completions/missingRHS.qml"_s);
+ QTest::newRow("binaryExpressionMissingRHS") << missingRHSFile << 12 << 25
+ << ExpectedCompletions{
+ { u"good"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ propertyCompletion, u"bad"_s }
+ ;
+ QTest::newRow("binaryExpressionMissingRHSWithDefaultProperty") << missingRHSFile << 14 << 33
+ << ExpectedCompletions{
+ { u"good"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
+ ;
+
+ QTest::newRow("binaryExpressionMissingRHSWithSemicolon")
+ << testFile(u"completions/missingRHS.parserfail.qml"_s)
+ << 5 << 22
+ << ExpectedCompletions{
+ { u"good"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
+ ;
+
+ QTest::newRow("binaryExpressionMissingRHSWithStatement") <<
+ testFile(u"completions/missingRHS.parserfail.qml"_s)
+ << 6 << 22
+ << ExpectedCompletions{
+ { u"good"_s, CompletionItemKind::Property },
+ }
+ << QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
+ ;
+
+ QTest::newRow("tryStatements")
+ << testFile(u"completions/tryStatements.qml"_s) << 5 << 14
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{};
+
+ QTest::newRow("tryStatementsCatchParameter")
+ << testFile(u"completions/tryStatements.qml"_s) << 5 << 23 << ExpectedCompletions{}
+ << QStringList{ letStatementCompletion, forStatementCompletion };
+
+ QTest::newRow("tryStatementsCatchBlock")
+ << testFile(u"completions/tryStatements.qml"_s) << 5 << 27
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{};
+
+ QTest::newRow("tryStatementsFinallyBlock")
+ << testFile(u"completions/tryStatements.qml"_s) << 5 << 39
+ << ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{};
+
+ QTest::newRow("inSwitchExpression")
+ << testFile(u"completions/switchStatements.qml"_s) << 10 << 16
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ letStatementCompletion,
+ propertyCompletion,
+ };
+
+ QTest::newRow("beforeCaseStatement")
+ << testFile(u"completions/switchStatements.qml"_s) << 11 << 1
+ << ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { caseStatement2Completion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatement2Completion, CompletionItemKind::Snippet } }
+ << QStringList{ letStatementCompletion, propertyCompletion, u"x"_s, u"myProperty"_s };
+ QTest::newRow("inCaseExpression")
+ << testFile(u"completions/switchStatements.qml"_s) << 12 << 14
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ letStatementCompletion, propertyCompletion, caseStatementCompletion };
+ QTest::newRow("inCaseStatementList")
+ << testFile(u"completions/switchStatements.qml"_s) << 13 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"return"_s, CompletionItemKind::Keyword },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ propertyCompletion };
+ QTest::newRow("inDefaultStatementList")
+ << testFile(u"completions/switchStatements.qml"_s) << 24 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"return"_s, CompletionItemKind::Keyword },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ propertyCompletion };
+ QTest::newRow("inMoreCasesStatementList")
+ << testFile(u"completions/switchStatements.qml"_s) << 26 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"return"_s, CompletionItemKind::Keyword },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("inCaseBeforeBlock")
+ << testFile(u"completions/switchStatements.qml"_s) << 14 << 23
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+ QTest::newRow("inCaseBeforeBlock2")
+ << testFile(u"completions/switchStatements.qml"_s) << 14 << 24
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ QTest::newRow("inCaseNestedStatement")
+ << testFile(u"completions/switchStatements.qml"_s) << 16 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{ propertyCompletion, caseStatementCompletion,
+ defaultStatementCompletion };
+
+ QTest::newRow("inCaseAfterBlock")
+ << testFile(u"completions/switchStatements.qml"_s) << 22 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ QTest::newRow("inCaseBeforeDefault")
+ << testFile(u"completions/switchStatements.qml"_s) << 23 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ QTest::newRow("inCaseAfterDefault")
+ << testFile(u"completions/switchStatements.qml"_s) << 25 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ QTest::newRow("beforeAnyCase")
+ << testFile(u"completions/switchStatements.qml"_s) << 20 << 1
+ << ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion, letStatementCompletion, u"myProperty"_s, u"x"_s,
+ u"f"_s };
+
+ QTest::newRow("beforeAnyDefault")
+ << testFile(u"completions/switchStatements.qml"_s) << 32 << 1
+ << ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion, letStatementCompletion, u"myProperty"_s, u"x"_s,
+ u"f"_s };
+ QTest::newRow("inDefaultAfterDefault")
+ << testFile(u"completions/switchStatements.qml"_s) << 33 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { caseStatementCompletion, CompletionItemKind::Snippet },
+ { defaultStatementCompletion, CompletionItemKind::Snippet },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { u"myProperty"_s, CompletionItemKind::Property } }
+ << QStringList{
+ propertyCompletion,
+ };
+
+ // variableDeclaration.qml tests for let/const/var statements + destructuring
+
+ QTest::newRow("letStatement") << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 13
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion,
+ u"x"_s, u"data"_s };
+
+ QTest::newRow("letStatement2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 26
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("letStatementBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 28
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("letStatementBehindEqual2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 33
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("constStatement")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 8 << 19
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("constStatementBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 8 << 32
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("varStatement") << testFile(u"completions/variableDeclaration.qml"_s) << 9 << 17
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion,
+ u"x"_s, u"data"_s };
+
+ QTest::newRow("varStatementBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 9 << 28
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstruction")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 13 << 20
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("objectDeconstructionAloneBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 14 << 51
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstructionAloneBehindEqual2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 14 << 58
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstruction2BehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 83
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstruction2BehindEqual2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 90
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstruction3BehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 140
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstructionBehindComma")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 143
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("objectDeconstructionBetweenObjects")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 50
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("objectDeconstructionBetweenDeconstructions")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 15 << 97
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstructionAlone")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 19 << 24
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstructionAloneBehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 19 << 33
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("arrayDeconstruction2")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 21 << 71
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstruction2BehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 21 << 83
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+ QTest::newRow("arrayDeconstruction3")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 21 << 125
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstruction3BehindEqual")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 21 << 139
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("arrayDeconstructionIn_Wildcard")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 25 << 64
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("arrayDeconstructionBehind+")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 25 << 132
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("objectDeconstructionForNeedle")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 29 << 111
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("arrayInObjectDeconstructionInObjectInitializer")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 33 << 44
+ << ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
+ {u"data"_s, CompletionItemKind::Method},
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion }
+ ;
+
+ QTest::newRow("arrayInObjectDeconstructionInObjectPropertyName")
+ << testFile(u"completions/variableDeclaration.qml"_s) << 33 << 26
+ << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
+
+ QTest::newRow("throwStatement")
+ << testFile(u"completions/throwStatement.qml"_s) << 8 << 15
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method } }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("throwStatement2")
+ << testFile(u"completions/throwStatement.qml"_s) << 9 << 20
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method } }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("labelledStatement")
+ << testFile(u"completions/labelledStatement.qml"_s) << 5 << 16
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("nestedLabel")
+ << testFile(u"completions/labelledStatement.qml"_s) << 7 << 22
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("nestedLabel2")
+ << testFile(u"completions/labelledStatement.qml"_s) << 8 << 26
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("multiLabel")
+ << testFile(u"completions/labelledStatement.qml"_s) << 15 << 21
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("multiLabel2")
+ << testFile(u"completions/labelledStatement.qml"_s) << 16 << 21
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"f"_s, CompletionItemKind::Method },
+ { letStatementCompletion, CompletionItemKind::Snippet },
+ { forStatementCompletion, CompletionItemKind::Snippet },
+ }
+ << QStringList{ propertyCompletion, };
+
+ QTest::newRow("continueNested")
+ << testFile(u"completions/continueAndBreakStatement.qml"_s) << 12 << 26
+ << ExpectedCompletions{ { u"nestedLabel1"_s, CompletionItemKind::Value },
+ { u"nestedLabel2"_s, CompletionItemKind::Value }, }
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"multiLabel1"_s };
+
+ QTest::newRow("breakNested")
+ << testFile(u"completions/continueAndBreakStatement.qml"_s) << 13 << 23
+ << ExpectedCompletions{ { u"nestedLabel1"_s, CompletionItemKind::Value },
+ { u"nestedLabel2"_s, CompletionItemKind::Value }, }
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"multiLabel1"_s };
+
+ QTest::newRow("continueMulti")
+ << testFile(u"completions/continueAndBreakStatement.qml"_s) << 20 << 22
+ << ExpectedCompletions{ { u"multiLabel1"_s, CompletionItemKind::Value },
+ { u"multiLabel2"_s, CompletionItemKind::Value }, }
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"nestedLabel1"_s };
+
+ QTest::newRow("breakMulti")
+ << testFile(u"completions/continueAndBreakStatement.qml"_s) << 21 << 19
+ << ExpectedCompletions{ { u"multiLabel1"_s, CompletionItemKind::Value },
+ { u"multiLabel2"_s, CompletionItemKind::Value }, }
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"nestedLabel1"_s };
+
+ QTest::newRow("continueNoLabel") << testFile(u"completions/continueAndBreakStatement.qml"_s)
+ << 25 << 22 << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s,
+ u"nestedLabel1"_s, u"multiLabel1"_s };
+
+ QTest::newRow("breakNoLabel") << testFile(u"completions/continueAndBreakStatement.qml"_s) << 26
+ << 19 << ExpectedCompletions{}
+ << QStringList{ propertyCompletion, u"x"_s, u"f"_s,
+ u"nestedLabel1"_s, u"multiLabel1"_s };
+
+ QTest::newRow("insideMethodBody")
+ << testFile(u"completions/functionBody.qml"_s) << 5 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("insideMethodBody2")
+ << testFile(u"completions/functionBody.qml"_s) << 11 << 11
+ << ExpectedCompletions{ { u"helloProperty"_s, CompletionItemKind::Property }, }
+ << QStringList{ u"badProperty"_s, forStatementCompletion };
+
+ QTest::newRow("insideMethodBodyStart")
+ << testFile(u"completions/functionBody.qml"_s) << 11 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ u"helloProperty"_s };
+
+ QTest::newRow("insideMethodBodyEnd")
+ << testFile(u"completions/functionBody.qml"_s) << 12 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { forStatementCompletion, CompletionItemKind::Snippet } }
+ << QStringList{ u"helloProperty"_s };
+
+ QTest::newRow("noBreakInMethodBody")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 8 << 8
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable } }
+ << QStringList{ u"break"_s, u"continue"_s };
+
+ QTest::newRow("breakAndContinueInForLoop")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 11 << 12
+ << ExpectedCompletions{ { u"break"_s, CompletionItemKind::Keyword },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{};
+
+ QTest::newRow("noBreakInSwitch")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 15 << 12
+ << ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet }, }
+ << QStringList{ u"continue"_s, u"break"_s };
+
+ QTest::newRow("breakInSwitchCase")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 17 << 12
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("breakInSwitchDefault")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 19 << 12
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("breakInSwitchSecondCase")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 21 << 12
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("breakInLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 25 << 12
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("forLoopInsideOfLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 33 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ };
+
+ QTest::newRow("switchInsideForLoopInsideOfLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 36 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ };
+
+ QTest::newRow("switchInsideOfLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 45 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ u"continue"_s };
+
+ QTest::newRow("forLoopInSwitchInsideOfLabel")
+ << testFile(u"completions/suggestContinueAndBreak.qml"_s) << 47 << 1
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ { u"break"_s, CompletionItemKind::Keyword },
+ { u"continue"_s, CompletionItemKind::Keyword },
+ }
+ << QStringList{ };
+
+ QTest::newRow("commaExpression")
+ << testFile(u"completions/commaExpression.qml"_s) << 5 << 18
+ << ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
+ { u"b"_s, CompletionItemKind::Variable },
+ { u"c"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion };
+
+ QTest::newRow("conditionalExpressionConsequence")
+ << testFile(u"completions/conditionalExpression.qml"_s) << 5 << 17
+ << ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
+ { u"b"_s, CompletionItemKind::Variable },
+ { u"c"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("conditionalExpressionAlternative")
+ << testFile(u"completions/conditionalExpression.qml"_s) << 5 << 30
+ << ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
+ { u"b"_s, CompletionItemKind::Variable },
+ { u"c"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("unaryMinus")
+ << testFile(u"completions/unaryExpression.qml"_s) << 5 << 10
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("unaryPlus")
+ << testFile(u"completions/unaryExpression.qml"_s) << 6 << 10
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("unaryTilde")
+ << testFile(u"completions/unaryExpression.qml"_s) << 7 << 10
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("unaryNot")
+ << testFile(u"completions/unaryExpression.qml"_s) << 8 << 10
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("typeof")
+ << testFile(u"completions/unaryExpression.qml"_s) << 9 << 16
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("delete")
+ << testFile(u"completions/unaryExpression.qml"_s) << 10 << 16
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("void")
+ << testFile(u"completions/unaryExpression.qml"_s) << 11 << 14
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("postDecrement")
+ << testFile(u"completions/unaryExpression.qml"_s) << 12 << 9
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("postIncrement")
+ << testFile(u"completions/unaryExpression.qml"_s) << 13 << 9
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("preDecrement")
+ << testFile(u"completions/unaryExpression.qml"_s) << 14 << 11
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("preIncrement")
+ << testFile(u"completions/unaryExpression.qml"_s) << 15 << 11
+ << ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
+ }
+ << QStringList{ propertyCompletion, letStatementCompletion };
+
+ QTest::newRow("attachedPropertyAfterDot")
+ << testFile("completions/attachedAndGroupedProperty.qml") << 8 << 15
+ << ExpectedCompletions({
+ { u"onCompleted"_s, CompletionItemKind::Method },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s };
+
+ QTest::newRow("groupedPropertyAfterDot")
+ << testFile("completions/attachedAndGroupedProperty.qml") << 10 << 15
+ << ExpectedCompletions({
+ { u"family"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s, u"onCompleted"_s };
+
+ QTest::newRow("attachedPropertyAfterDotMissingRHS")
+ << testFile("completions/attachedPropertyMissingRHS.qml") << 7 << 17
+ << ExpectedCompletions({
+ { u"onCompleted"_s, CompletionItemKind::Method },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s };
+
+ QTest::newRow("groupedPropertyAfterDotMissingRHS")
+ << testFile("completions/groupedPropertyMissingRHS.qml") << 7 << 11
+ << ExpectedCompletions({
+ { u"family"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s, u"onCompleted"_s };
+
+ QTest::newRow("dotFollowedByDefaultBinding")
+ << testFile("completions/afterDots.qml") << 11 << 31
+ << ExpectedCompletions({
+ { u"good"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"bad"_s, u"QtQuick"_s, u"vector4d"_s,
+ attachedTypeName, u"Rectangle"_s, u"onCompleted"_s };
+
+ QTest::newRow("dotFollowedByBinding")
+ << testFile("completions/afterDots.qml") << 13 << 32
+ << ExpectedCompletions({
+ { u"good"_s, CompletionItemKind::Property },
+ })
+ << QStringList{ u"bad"_s, u"QtQuick"_s, u"vector4d"_s,
+ attachedTypeName, u"Rectangle"_s, u"onCompleted"_s };
+
+ QTest::newRow("dotFollowedByForStatement")
+ << testFile("completions/afterDots.qml") << 16 << 17
+ << ExpectedCompletions({
+ { u"good"_s, CompletionItemKind::Property },
+ })
+ << QStringList{
+ u"bad"_s, u"QtQuick"_s, u"vector4d"_s, attachedTypeName,
+ u"Rectangle"_s, u"onCompleted"_s, forStatementCompletion
+ };
+
+ QTest::newRow("qualifiedTypeCompletionWithoutQualifier")
+ << testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
+ << 9 << 5
+ << ExpectedCompletions({
+ { u"T.Button"_s, CompletionItemKind::Constructor },
+ { u"Button"_s, CompletionItemKind::Constructor },
+ { u"Rectangle"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, u"bad"_s, u"onCompleted"_s };
+
+ QTest::newRow("qualifiedTypeCompletionWithoutQualifier2")
+ << testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
+ << 10 << 19
+ << ExpectedCompletions({
+ { u"T.Button"_s, CompletionItemKind::Class },
+ { u"Button"_s, CompletionItemKind::Class },
+ { u"Rectangle"_s, CompletionItemKind::Class },
+ })
+ << QStringList{ u"QtQuick"_s, u"bad"_s, u"onCompleted"_s };
+
+ QTest::newRow("qualifiedTypeCompletionWithQualifier")
+ << testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
+ << 9 << 7
+ << ExpectedCompletions({
+ { u"Button"_s, CompletionItemKind::Constructor },
+ })
+ << QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s, u"onCompleted"_s, u"T.Button"_s };
+
+ QTest::newRow("qualifiedTypeCompletionWithQualifier2")
+ << testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
+ << 10 << 21
+ << ExpectedCompletions({
+ { u"Button"_s, CompletionItemKind::Class },
+ })
+ << QStringList{ u"QtQuick"_s, attachedTypeName, u"Rectangle"_s,
+ u"bad"_s, u"onCompleted"_s, u"T.Button"_s };
+
+ QTest::newRow("parenthesizedExpression")
+ << testFile("completions/parenthesizedExpression.qml") << 8 << 10
+ << ExpectedCompletions({
+ { u"x"_s, CompletionItemKind::Variable },
+ })
+ << QStringList{ u"QtQuick"_s, u"Rectangle"_s, forStatementCompletion };
+
+ QTest::newRow("behindParenthesizedExpression")
+ << testFile("completions/parenthesizedExpression.qml") << 8 << 16
+ << ExpectedCompletions({})
+ << QStringList{ u"QtQuick"_s, attachedTypeName, u"Rectangle"_s, forStatementCompletion,
+ u"x"_s };
+
+ QTest::newRow("assumeBoundComponentsIdFromParent")
+ << testFile("completions/boundComponents.qml") << 14 << 33
+ << ExpectedCompletions{ { u"rootId"_s, CompletionItemKind::Value } }
+ << QStringList{ u"inRoot"_s };
+
+ QTest::newRow("assumeBoundComponentsPropertyFromParent")
+ << testFile("completions/boundComponents.qml") << 14 << 40
+ << ExpectedCompletions{ { u"inRoot"_s, CompletionItemKind::Property } }
+ << QStringList{ u"root"_s };
+}
+
+void tst_qmlls_utils::completions()
+{
+ QFETCH(QString, filePath);
+ QFETCH(int, line);
+ QFETCH(int, character);
+ QFETCH(ExpectedCompletions, expected);
+ QFETCH(QStringList, notExpected);
+
+ auto [env, file] = createEnvironmentAndLoadFile(filePath);
+
+ auto locations = QQmlLSUtils::itemsFromTextLocation(
+ file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
+
+ QEXPECT_FAIL("binaryExpressionMissingRHSWithSemicolon",
+ "Current parser cannot recover from this error yet!", Abort);
+ QEXPECT_FAIL("binaryExpressionMissingRHSWithStatement",
+ "Current parser cannot recover from this error yet!", Abort);
+ QCOMPARE(locations.size(), 1);
+
+ QString code;
+ {
+ QFile file(filePath);
+ QVERIFY(file.open(QIODeviceBase::ReadOnly));
+ code = QString::fromUtf8(file.readAll());
+ }
+
+ qsizetype pos = QQmlLSUtils::textOffsetFrom(code, line - 1, character - 1);
+ CompletionContextStrings ctxt{ code, pos };
+ QQmlLSCompletion completionEngine(m_pluginLoader);
+ QList<CompletionItem> completions =
+ completionEngine.completions(locations.front().domItem, ctxt);
+
+ if (expected.isEmpty()) {
+ if constexpr (enable_debug_output) {
+ if (!completions.isEmpty()) {
+ QStringList unexpected;
+ for (const auto &current : completions) {
+ unexpected << current.label;
+ }
+ qDebug() << "Received unexpected completions:" << unexpected.join(u", ");
+ }
+ }
+ QEXPECT_FAIL("singleton", "completion not implemented yet!", Abort);
+ QVERIFY(completions.isEmpty());
+ return;
+ }
+
+ QSet<QString> labels;
+ QStringList sortedLabels;
+ QDuplicateTracker<QByteArray> modulesTracker;
+ QDuplicateTracker<QByteArray> keywordsTracker;
+ QDuplicateTracker<QByteArray> classesTracker;
+ QDuplicateTracker<QByteArray> fieldsTracker;
+ QDuplicateTracker<QByteArray> propertiesTracker;
+ QDuplicateTracker<QByteArray> snippetTracker;
+
+ // avoid QEXPECT_FAIL tests to XPASS when completion order changes
+ std::sort(completions.begin(), completions.end(),
+ [](const CompletionItem&a, const CompletionItem&b) {return a.label < b.label;});
+
+ for (const CompletionItem &c : completions) {
+ // explicitly forbid marker structs created by QQmlJSImporter
+ QVERIFY(!c.label.contains("$internal$."));
+ QVERIFY(!c.label.contains("$module$."));
+ QVERIFY(!c.label.contains("$anonymous$."));
+
+ if (c.kind->toInt() == int(CompletionItemKind::Module)) {
+ QVERIFY2(!modulesTracker.hasSeen(c.label), "Duplicate module: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Keyword)) {
+ QVERIFY2(!keywordsTracker.hasSeen(c.label), "Duplicate keyword: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Class)) {
+ QVERIFY2(!classesTracker.hasSeen(c.label), "Duplicate class: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Field)) {
+ QVERIFY2(!fieldsTracker.hasSeen(c.label), "Duplicate field: " + c.label);
+ } else if (c.kind->toInt() == int(CompletionItemKind::Snippet)) {
+ QVERIFY2(!snippetTracker.hasSeen(c.label), "Duplicate field: " + c.label);
+ if (c.insertText->contains('\n') || c.insertText->contains('\r')) {
+ QCOMPARE(c.insertTextMode, InsertTextMode::AdjustIndentation);
+ }
+ } else if (c.kind->toInt() == int(CompletionItemKind::Property)) {
+ QVERIFY2(!propertiesTracker.hasSeen(c.label), "Duplicate property: " + c.label);
+ QCOMPARE(c.insertText, std::nullopt);
+ }
+ labels << c.label;
+ sortedLabels << c.label;
+ }
+ const QString labelsForPrinting = sortedLabels.join(u", "_s);
+
+ for (const ExpectedCompletion &exp : expected) {
+ QEXPECT_FAIL("letStatementAfterEqual", "Completion not implemented yet!", Abort);
+
+ QVERIFY2(labels.contains(exp.label),
+ u"no %1 in %2"_s.arg(exp.label, labelsForPrinting).toUtf8());
+ if (labels.contains(exp.label)) {
+
+ bool foundEntry = false;
+ bool hasCorrectKind = false;
+ CompletionItemKind foundKind;
+ for (const CompletionItem &c : completions) {
+ if (c.label == exp.label) {
+ foundKind = static_cast<CompletionItemKind>(c.kind->toInt());
+ foundEntry = true;
+ if (foundKind == exp.kind) {
+ hasCorrectKind = true;
+ if (!exp.snippet.isEmpty()) {
+ QCOMPARE(QString::fromUtf8(c.insertText.value_or(QByteArray())),
+ exp.snippet);
+ }
+ break;
+ }
+ }
+ }
+
+ // Ignore QVERIFY for those completions not in the expected list.
+ if (!foundEntry)
+ continue;
+
+ QVERIFY2(hasCorrectKind,
+ qPrintable(QString::fromLatin1("Completion item '%1' has wrong kind '%2'")
+ .arg(exp.label)
+ .arg(QMetaEnum::fromType<CompletionItemKind>().valueToKey(
+ int(foundKind)))));
+ }
+ }
+ for (const QString &nexp : notExpected) {
+ QEXPECT_FAIL("ignoreNonRelatedTypesForPropertyDefinitionBinding",
+ "Filtering by Type not implemented yet, for example to avoid proposing "
+ "binding Items to Rectangle properties.",
+ Abort);
+ QVERIFY2(!labels.contains(nexp), u"found unexpected completion %1"_s.arg(nexp).toUtf8());
+ }
+}
+
+void tst_qmlls_utils::cmakeBuildCommand()
+{
+ const QString path = u"helloWorldPath"_s;
+ const QPair<QString, QStringList> expected{
+ u"cmake"_s, { u"--build"_s, path, u"-t"_s, u"all_qmltyperegistrations"_s }
+ };
+ QCOMPARE(QQmlLSUtils::cmakeBuildCommand(path), expected);
+}
+
+void tst_qmlls_utils::qdochtmlparser_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QDocHtmlExtractor::Element>("element");
+ QTest::addColumn<QDocHtmlExtractor::ExtractionMode>("extractionMode");
+ QTest::addColumn<QString>("expectedDocumentation");
+
+ QTest::addRow("qml-object-type-extended-plaintext")
+ << testFile("qdochtmlparser/qml-qtqml-qtobject.html")
+ << QDocHtmlExtractor::Element{"QtObject", QDocHtmlExtractor::ElementType::QmlType}
+ << QDocHtmlExtractor::ExtractionMode::Extended
+ << R"(The QtObject type is a non-visual element which contains only the objectName property.
+It can be useful to create a QtObject if you need an extremely lightweight type to enclose a set of custom properties:
+
+ import QtQuick
+
+ Item {
+ QtObject {
+ id: attributes
+ property string name
+ property int size
+ property variant attributes
+ }
+
+ Text { text: attributes.name }
+ }
+
+It can also be useful for C++ integration, as it is just a plain QObject. See the QObject documentation for further details.)";
+
+ QTest::addRow("qml-object-type-simplified-plaintext")
+ << testFile("qdochtmlparser/qml-qtqml-qtobject.html")
+ << QDocHtmlExtractor::Element{"QtObject", QDocHtmlExtractor::ElementType::QmlType}
+ << QDocHtmlExtractor::ExtractionMode::Simplified
+ << R"(A basic QML type.)";
+
+ QTest::addRow("qml-property-simplified-plaintext")
+ << testFile("qdochtmlparser/qml-qtqml-qtobject.html")
+ << QDocHtmlExtractor::Element{"objectName",QDocHtmlExtractor::ElementType::QmlProperty}
+ << QDocHtmlExtractor::ExtractionMode::Simplified
+ << R"(This property holds the QObject::objectName for this specific object instance.)";
+
+ QTest::addRow("qml-property-simplified-plaintext-from-Qt5")
+ << testFile("qdochtmlparser/qml-qtqml-qtobject-qt-5.html") << QDocHtmlExtractor::Element{"objectName", QDocHtmlExtractor::ElementType::QmlProperty}
+ << QDocHtmlExtractor::ExtractionMode::Simplified
+ << R"(This property holds the QObject::objectName for this specific object instance.)";
+
+ QTest::addRow("qml-property-simplified-plaintext")
+ << testFile("qdochtmlparser/qml-qtquick-item.html") << QDocHtmlExtractor::Element{"width", QDocHtmlExtractor::ElementType::QmlProperty}
+ << QDocHtmlExtractor::ExtractionMode::Simplified
+ << R"(Defines the item's position and size. The default value is 0.)";
+
+ QTest::addRow("qml-group-property-simplified-plaintext")
+ << testFile("qdochtmlparser/qml-qtquick-item.html") << QDocHtmlExtractor::Element{"anchors.fill", QDocHtmlExtractor::ElementType::QmlProperty}
+ << QDocHtmlExtractor::ExtractionMode::Simplified
+ << R"(Anchors provide a way to position an item by specifying its relationship with other items.)";
+ QTest::addRow("qml-functions")
+ << testFile("qdochtmlparser/qml-qtquick-item.html") << QDocHtmlExtractor::Element{"mapFromGlobal", QDocHtmlExtractor::ElementType::QmlMethod}
+ << QDocHtmlExtractor::ExtractionMode::Simplified
+ << "Maps the point (x, y), which is in the global coordinate system, to the item's coordinate system,"
+ " and returns a point matching the mapped coordinate.";
+
+ QTest::addRow("qml-functions-list")
+ << testFile("qdochtmlparser/qml-qtquick-item.html") << QDocHtmlExtractor::Element{"mapFromItem", QDocHtmlExtractor::ElementType::QmlMethod}
+ << QDocHtmlExtractor::ExtractionMode::Simplified
+ << "Maps the point (x, y) or rect (x, y, width, height), which is in item's coordinate system,"
+ " to this item's coordinate system, and returns a point or rect matching the mapped coordinate.";
+ QTest::addRow("qml-signal")
+ << testFile("qdochtmlparser/qml-qtquick-mousearea.html") << QDocHtmlExtractor::Element{"pressAndHold", QDocHtmlExtractor::ElementType::QmlSignal}
+ << QDocHtmlExtractor::ExtractionMode::Simplified
+ << "This signal is emitted when there is a long press (currently 800ms). The mouse parameter provides information about the press, "
+ "including the x and y position of the press, and which button is pressed.";
+}
+
+void tst_qmlls_utils::qdochtmlparser()
+{
+ QFETCH(QString, filePath);
+ QFETCH(QDocHtmlExtractor::Element, element);
+ QFETCH(QDocHtmlExtractor::ExtractionMode, extractionMode);
+ QFETCH(QString, expectedDocumentation);
+
+ const auto htmlCode = [](const QString &testFileName) {
+ QFile file(testFileName);
+ if (file.open(QIODeviceBase::ReadOnly | QIODevice::Text))
+ return QString::fromUtf8(file.readAll());
+ return QString{};
+ }(filePath);
+
+
+ QDocHtmlExtractor extractor(htmlCode);
+ const auto actual = extractor.extract(element, extractionMode);
+ QCOMPARE(actual, expectedDocumentation);
+}
+
+QTEST_MAIN(tst_qmlls_utils)
diff --git a/tests/auto/qmlls/utils/tst_qmlls_utils.h b/tests/auto/qmlls/utils/tst_qmlls_utils.h
new file mode 100644
index 0000000000..51fa74dd0a
--- /dev/null
+++ b/tests/auto/qmlls/utils/tst_qmlls_utils.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLLS_UTILS_H
+#define TST_QMLLS_UTILS_H
+
+#include <QtJsonRpc/private/qjsonrpcprotocol_p.h>
+#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtCore/private/qfactoryloader_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qlibraryinfo.h>
+
+#include <QtTest/qtest.h>
+
+#include <QtQmlLS/private/qqmllsutils_p.h>
+#include <QtQmlLS/private/qqmllscompletion_p.h>
+
+#include <iostream>
+
+using namespace Qt::StringLiterals;
+
+class tst_qmlls_utils : public QQmlDataTest
+{
+ Q_OBJECT
+
+ struct ExpectedCompletion
+ {
+ QString label;
+ QLspSpecification::CompletionItemKind kind;
+ QString snippet = {};
+ };
+ using ExpectedCompletions = QList<ExpectedCompletion>;
+
+ using ExpectedDocumentation = std::tuple<QString, QString, QString>;
+ using ExpectedDocumentations = QList<ExpectedDocumentation>;
+
+public:
+ tst_qmlls_utils()
+ : QQmlDataTest(QT_QMLLS_UTILS_DATADIR),
+ m_pluginLoader(QmlLSPluginInterface_iid, u"/qmlls"_s)
+ {
+ }
+
+private slots:
+ void textOffsetRowColumnConversions_data();
+ void textOffsetRowColumnConversions();
+
+ void findItemFromLocation_data();
+ void findItemFromLocation();
+
+ void findTypeDefinitionFromLocation_data();
+ void findTypeDefinitionFromLocation();
+
+ void findDefinitionFromLocation_data();
+ void findDefinitionFromLocation();
+
+ void findLocationOfItem_data();
+ void findLocationOfItem();
+
+ void findBaseObject();
+ void findBaseObject_data();
+
+ void findUsages();
+ void findUsages_data();
+
+ void renameUsages();
+ void renameUsages_data();
+
+ void resolveExpressionType();
+ void resolveExpressionType_data();
+
+ void isValidEcmaScriptIdentifier();
+ void isValidEcmaScriptIdentifier_data();
+
+ void completions_data();
+ void completions();
+
+ void cmakeBuildCommand();
+
+ void qdochtmlparser_data();
+ void qdochtmlparser();
+
+private:
+ using EnvironmentAndFile = std::tuple<QQmlJS::Dom::DomItem, QQmlJS::Dom::DomItem>;
+
+ EnvironmentAndFile createEnvironmentAndLoadFile(const QString &file);
+
+ using CacheKey = QString;
+ // avoid loading the same file over and over when running all the tests
+ QHash<CacheKey, std::shared_ptr<QQmlJS::Dom::DomEnvironment>> cache;
+ QFactoryLoader m_pluginLoader;
+
+};
+
+#endif // TST_QMLLS_UTILS_H
diff --git a/tests/auto/qmlnetwork/CMakeLists.txt b/tests/auto/qmlnetwork/CMakeLists.txt
new file mode 100644
index 0000000000..74cdf06a51
--- /dev/null
+++ b/tests/auto/qmlnetwork/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(qqmlnetworkinformation)
+if(QT_FEATURE_qml_ssl)
+ add_subdirectory(qqmlsslconfiguration)
+ add_subdirectory(qqmlsslkey)
+endif()
diff --git a/tests/auto/qmlnetwork/qqmlnetworkinformation/CMakeLists.txt b/tests/auto/qmlnetwork/qqmlnetworkinformation/CMakeLists.txt
new file mode 100644
index 0000000000..a9554a9295
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlnetworkinformation/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlnetworkinformation LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/data/tst_*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qqmlnetworkinformation
+ QMLTEST
+ SOURCES
+ tst_qqmlnetworkinformation.cpp
+ LIBRARIES
+ Qt::QmlPrivate
+ Qt::QuickTestUtilsPrivate
+ Qt::Network
+ TESTDATA ${test_data}
+)
+
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(tst_qqmlnetworkinformation)
+endif()
+
+qt_internal_extend_target(tst_qqmlnetworkinformation CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qqmlnetworkinformation CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
diff --git a/tests/auto/qmlnetwork/qqmlnetworkinformation/data/tst_networkinformation.qml b/tests/auto/qmlnetwork/qqmlnetworkinformation/data/tst_networkinformation.qml
new file mode 100644
index 0000000000..a429e05320
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlnetworkinformation/data/tst_networkinformation.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+import QtNetwork
+
+QtObject {
+ property int local: NetworkInformation.Reachability.Local
+ property int reachability: NetworkInformation.reachability
+ property bool isBehindCaptivePortal: NetworkInformation.isBehindCaptivePortal
+ property int ethernet: NetworkInformation.TransportMedium.Ethernet
+ property int transportMedium: NetworkInformation.transportMedium
+ property bool isMetered: NetworkInformation.isMetered
+}
diff --git a/tests/auto/qmlnetwork/qqmlnetworkinformation/tst_qqmlnetworkinformation.cpp b/tests/auto/qmlnetwork/qqmlnetworkinformation/tst_qqmlnetworkinformation.cpp
new file mode 100644
index 0000000000..768d751c53
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlnetworkinformation/tst_qqmlnetworkinformation.cpp
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <QtNetwork/qnetworkinformation.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+class tst_qqmlnetworkinformation : public QQmlDataTest
+{
+ Q_OBJECT
+
+public:
+ explicit tst_qqmlnetworkinformation() : QQmlDataTest(QT_QMLTEST_DATADIR) { }
+
+private Q_SLOTS:
+ void networkInformation();
+};
+
+void tst_qqmlnetworkinformation::networkInformation()
+{
+ QNetworkInformation::loadDefaultBackend();
+ QNetworkInformation *networkinfo = QNetworkInformation::instance();
+#if defined(Q_OS_LINUX) || defined(Q_OS_WIN) || defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN)
+ QVERIFY(networkinfo);
+#else
+ QSKIP("Platform does not provide network information");
+#endif
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("tst_networkinformation.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("local").toInt(),
+ static_cast<int>(QNetworkInformation::Reachability::Local));
+ QCOMPARE(object->property("reachability").toInt(),
+ static_cast<int>(networkinfo->reachability()));
+ QCOMPARE(object->property("isBehindCaptivePortal").toBool(),
+ networkinfo->isBehindCaptivePortal());
+ QCOMPARE(object->property("ethernet").toInt(),
+ static_cast<int>(QNetworkInformation::TransportMedium::Ethernet));
+ QCOMPARE(object->property("transportMedium").toInt(),
+ static_cast<int>(networkinfo->transportMedium()));
+ QCOMPARE(object->property("isMetered").toBool(), networkinfo->isMetered());
+}
+
+QTEST_MAIN(tst_qqmlnetworkinformation)
+
+#include "tst_qqmlnetworkinformation.moc"
diff --git a/tests/auto/qmlnetwork/qqmlsslconfiguration/CMakeLists.txt b/tests/auto/qmlnetwork/qqmlsslconfiguration/CMakeLists.txt
new file mode 100644
index 0000000000..bc5709dc86
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlsslconfiguration/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_sslconfiguration_qml LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_sslconfiguration_qml
+ QMLTEST
+ SOURCES
+ tst_sslconfiguration_qml.cpp
+ LIBRARIES
+ Qt::Qml
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlNetwork
+)
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_qml_module(tst_sslconfiguration_qml
+ URI QmlTestUri
+ VERSION 1.0
+ QML_FILES
+ qml/tst_sslconfiguration.qml
+)
+
+set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/data/cert.pem"
+ PROPERTIES QT_RESOURCE_ALIAS data/cert.pem)
+set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/data/key.pem"
+ PROPERTIES QT_RESOURCE_ALIAS data/key.pem)
+
+qt_add_resources(tst_sslconfiguration_qml
+ "data"
+ PREFIX
+ "/"
+ FILES
+ "${CMAKE_CURRENT_LIST_DIR}/data/cert.pem"
+ "${CMAKE_CURRENT_LIST_DIR}/data/key.pem"
+)
+
+qt_autogen_tools_initial_setup(tst_sslconfiguration_qml)
diff --git a/tests/auto/qmlnetwork/qqmlsslconfiguration/data/cert.pem b/tests/auto/qmlnetwork/qqmlsslconfiguration/data/cert.pem
new file mode 100644
index 0000000000..3fb973e1be
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlsslconfiguration/data/cert.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF6zCCA9OgAwIBAgIJAMPfLBc9ERZGMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
+VQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEzARBgNV
+BAoMClF0UHJvdG9idWYxDDAKBgNVBAsMA1JuRDESMBAGA1UEAwwJbG9jYWxob3N0
+MSMwIQYJKoZIhvcNAQkBFhRxdHByb3RvYnVmQGdtYWlsLmNvbTAeFw0xOTA0MjMx
+NTI5MzNaFw0yMDA0MjIxNTI5MzNaMIGLMQswCQYDVQQGEwJERTEPMA0GA1UECAwG
+QmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEzARBgNVBAoMClF0UHJvdG9idWYxDDAK
+BgNVBAsMA1JuRDESMBAGA1UEAwwJbG9jYWxob3N0MSMwIQYJKoZIhvcNAQkBFhRx
+dHByb3RvYnVmQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBANCB7zdc6v+gsYCsGIYsT3iyGPgvFG7cJJWnQO9iW6Tn/kBcreYuCdVSSODK
+/NtPaA1r8j9FCDvLkzewNQ2Idv08/oKBYfOaVtQvh1cn7ktr1usiXtYv16cimeKk
+8iYbiczkZOah2xq+ivm9+05WkYTzcSjBpXg19894024GHd7oRV9G5MCr760k8YLM
+ALnoPpTl0yfs5cEcTybqvZFZNqkHDX2ziEbcF/mVcxcyEsmenbX6MI0easg2qZeq
+Sb7AW7tIMVoWUxDkUIor4vogbgU2IljAjzn0i+fPncB41TU5IiIU35vO67kFBXyI
+Ms3LpN1+Siz5HYHjwaWl0ecebuT83kP33VNc1ULkKJG5UbZUYfgZjUwZPgFFzVzQ
+cif0mhYj9s5Nmn6Q/twxeIXOIZAQdLOq625Wwx8bh+mGaV5mtw3wtXJjbceGa1z/
+Pnni4x2B7IfSiOzGLZZNRRHIjUskeONHUHn8YBrLLg5RT+tvPnGDVPh0cKEH2P4F
+cNRPmaqg75siFoJ+m3DlFM952tRcfzJumgbUfEnrL8bxTZPYVoq22qCDm3UGNW8n
+pthv8Y2hemv6V9lF050z2vpL2DtU9RmtkBWx7ipPUdsEOm4e0rdOCk7zo8IAiWMQ
+6o1L23IPTsqaEpqk3b7tT+5dtLkpeomAT8Hz91ChWUb6jrTVAgMBAAGjUDBOMB0G
+A1UdDgQWBBQIb2RjqkeMsUjH9QJ5BYnAtJ/vojAfBgNVHSMEGDAWgBQIb2RjqkeM
+sUjH9QJ5BYnAtJ/vojAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQDG
+2x+I9C+4XFCpg9KVA+BBi6+7yi4ulH0yv7W3hNWBV2MxTpvVN6RIt8xI1sPFXc2T
+g2wCIAIPGIPNDe8hO3gcfIIe059P+J3ZNjybUg5p6CR9O7kqgqV/nL4gVRuBJeYq
+6YLr1lKL9r3Zdbkq1tcaCaj816zdXVky0us72XP28/xJc96crgKzDI69vESy5jq6
+QT/HoBwYiSaWXNgb0zJzc//e0upXSLeTkYizAJ5OGkQ/MQYE7gDvtPlGVhQ0rnl/
+FLsiZJokuxtLOTvYJ8Gynjz3QwbClN/bmUbOgD0fqW6BEZyZSJ4zCz0BJnwg46gc
+IN+p6vi50MG4ZcJcnMl/3tAxt2RxHNLi0j21NSLyFj60gK+vL3/zzkdYF9ZxX1L+
+dqhTqioCVrV96rJIQbz6JrbFhUCdyEHYG7yi6LxHTUex33XouhGAfZ0lri5wWZq8
+0Rx8PEZ7SbjARtnvA7uXIAfgD+n3oqnkg9IDPH8PbdRcJrAdje3O2c1+OzTAC8ni
+czaCWG748gfZPe0JpENP7P56RTh9avj4sHISCx4r+sh0lruLp2JZPr9qtw09uWlq
+Mn58bcnDu9RjVfPXk43s9WfJC9XII+JoNcW3iJDSHxQb9XLvwR9ntWhkVvOKenPP
+b00kWr6FfId6fiOmUww5WkW+Wtt4XOYsypaN+DskeA==
+-----END CERTIFICATE-----
diff --git a/tests/auto/qmlnetwork/qqmlsslconfiguration/data/key.pem b/tests/auto/qmlnetwork/qqmlsslconfiguration/data/key.pem
new file mode 100644
index 0000000000..176f5210c1
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlsslconfiguration/data/key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDQge83XOr/oLGA
+rBiGLE94shj4LxRu3CSVp0DvYluk5/5AXK3mLgnVUkjgyvzbT2gNa/I/RQg7y5M3
+sDUNiHb9PP6CgWHzmlbUL4dXJ+5La9brIl7WL9enIpnipPImG4nM5GTmodsavor5
+vftOVpGE83EowaV4NffPeNNuBh3e6EVfRuTAq++tJPGCzAC56D6U5dMn7OXBHE8m
+6r2RWTapBw19s4hG3Bf5lXMXMhLJnp21+jCNHmrINqmXqkm+wFu7SDFaFlMQ5FCK
+K+L6IG4FNiJYwI859Ivnz53AeNU1OSIiFN+bzuu5BQV8iDLNy6Tdfkos+R2B48Gl
+pdHnHm7k/N5D991TXNVC5CiRuVG2VGH4GY1MGT4BRc1c0HIn9JoWI/bOTZp+kP7c
+MXiFziGQEHSzqutuVsMfG4fphmleZrcN8LVyY23Hhmtc/z554uMdgeyH0ojsxi2W
+TUURyI1LJHjjR1B5/GAayy4OUU/rbz5xg1T4dHChB9j+BXDUT5mqoO+bIhaCfptw
+5RTPedrUXH8ybpoG1HxJ6y/G8U2T2FaKttqgg5t1BjVvJ6bYb/GNoXpr+lfZRdOd
+M9r6S9g7VPUZrZAVse4qT1HbBDpuHtK3TgpO86PCAIljEOqNS9tyD07KmhKapN2+
+7U/uXbS5KXqJgE/B8/dQoVlG+o601QIDAQABAoICAGYHPsxDfoap1lHVZIa7RgQU
+eh1vxDrfJFPKrP62jYurLgHGmB2rZ4poIltFWOfj+lGfAcIuAHJqElbMtZkyrq8K
+Wqv3rburSVO5Eiv20Sc81MToY6nBbXBOgSijeA5nqU2GcU1d5D45AP5mFYPm3nxF
+N5ku8M5a8jEmuab7/T/nPpL5uNQDDlwWWMudEbnmyEDKGUJPLLoLJTww36QxGIsr
+dVGOOWAbMOwjUlcGXKUmJZw3mexj9vKTtPcPD9j0fa6uC+A+TlVUs4h5Iy8sEUoh
+jDsLtsowPQmo0VOujP3nQCmXNzghz70QlPe0GdAUF09/DcLl/6dgkJCDDKxgevhW
+GYfUSaR6gjg6/QYVHIea9wCkxW2jRXPvG6pBAAaoseS4n7M3IckzSol4Nwh2vmzA
+yvGMLlLUkNIYHJ/P29mMt+EoBrtdME8XZln0sCkQC5c0+owvyBsPEjrpnBtqvPse
+CNQaULUZnsJC3kbJeU/xPcqNa8pGjnpiHqjDFN4CUJAYnLDuAanow075DCkLKWej
+ziXWQoJ+RO4ml9Gy0qoHE76iEg9fvKx7aWIv0DSmhRvwKeI8kc1yPp8kACy1rOBu
+f9gvDDB4jMVDQKYRDbZ6kyRrHX5XKvJJ6vkpFBT1fLaWVdH8fOtIsDKaLhSTc0Ia
+TbcDJquaBeLnQmpH5439AoIBAQDpccVOJ2mE1n6sluXkzE/zW17jmWMrBXAtYpnt
+nkBO8SuwNzZW3V0LvCTpEVo1XupvVbuMvWzZyHgAevqDL77Z5FP63hO1l1yT3mcw
+WB1Kr9XTXm52DL4IGnp4agrI1+zp56q8o/PbJfkk3JhiEmpHa3rh575LcYVs8HXV
+5+cTFc5upygXX4odRazS7qXtZdyBL1w7KpZijZJqrcG3t4sjCKEOcZn6XFVFtfHE
+GWrAIz9kWORh5nZ9MTI9TR/4MHBYJ8G/9kwbrnce+FeZ4BTkZHqQTp2MHeC9AFro
+JCtG8y1rhh1cxzoUMB6s3qW6Q/7b2/Wx7Hb6RMFXRFYTaeVXAoIBAQDkp1o5E+OB
+ErGPRBHpt+7nmEFq+U+biNcNUvxTKtL9aQKix5Xt9zgSQTN9LmIhAOKDKfuU2Elb
+rX5tCTbalFYcpUX+wD+idvcgpc7Ju+tRMC5Ai9avuCJ0n2oQZiaxFz8GIPc/1C3a
+gC5s1HHr0qTDKcs37nBiay3lmll49J0grrl0NOEWROGDnILvvgCN8jQoMBN5Od5k
+zCPXFuWl3JhWKtSoF+isk/io2JjM2asZuz0zi4mzBnjnVfCM0dAHufoTMQ8aHiVZ
+45iXDIZY9c8frOLgeZeE12mYTWpxZHUuaZSoqoXuApmW3nhoGHYSfX7sORDTYS/4
+2PEJlhkkPs+zAoIBAQC33GybBn2cK1gv1NWSY7zgnelZdzjc7HaSuGMl/IsH4fkX
+3BSHS+f50yB7FLio6m3YbHy/932g9bxWHIXsBxHZCXV/U6PQVTuMFxHMyMmhRmYy
+COEVRynwtfIZnuOJlk85VsZptvPceccF2lyGeZyNTcDF5kFBqFJ/H9CfPfwIUxd4
+nVz9M7lTHspkg6PaG20VrliFHSC+1GQqc1nsubnzSNuYxa6RumFK+2dEnQQv+lL2
+VPDjjqFqLvIzx+fTEUuakw2NhI4jC0E0+kH8prmtvNmviMubTPjxwzLWPY58XhE6
+67F6nktHFTND0kRTNTSos1CK5wQ6Tya79c2ZksEXAoIBAAdI7a7z20O5fL67xHZV
+zd7DExJ9bvPdoDxkcHWV37MDLXpSMYyrW7X5LdLHL4ktpgnXxJQxb+Tj2itPJ9g+
+8Z9oBJrhNSXP9H+tyLDUs+KaTl7wFZ7zluVwTsjG+GScAP4I/tehwvQ7MT92ZUrG
+I0m0gyz9A8ee8o9mI4OfB4KLDo2NQb6b4zN2QRWyUAI1vUOqhHRQS62ac2ne6OIn
+7RKRusTAPkGBVWLLw9KC/NiNBp4ly/VQN3nnWwqhhKc6XaVO4tRKMZZzkeD+HSmo
+azjvIStVtGYfFtYrYUDLmpAn/PyCslGq84nC/MMURG7CYNDV4JtbdVPQVZ2gkpx9
+A9ECggEAGuw6sJAkp381dHgf6tTkwsOmJldX4Bjxi6q3vXzNwsou+uwYoLNvXVnl
+mfMQdswCGW1Hm1XPMSBqkleyaXChL4bqM/FJGz8DgBD85vWfaYPrfKIELlfSo5lD
+opBZ8wrAEa9rP2Fm1mbAiFFyXtK78y09CuuMgCL5jZjAQEbNFDOSwfcrJzNY+xU7
+KtsDGCm7OmUAizdWjnAfQKQlB94uk7PimI1Hhs8175fgwaSS3KUILSR8oj/gKFPS
+L7DqR8DsvyGg/JuHx+sdSG3T5q5zGzz2w03mDkoSyxWe36u3F3EyChhPfjcaSape
+0mVZG9D69wbsZefVDJii9NLvWThGog==
+-----END PRIVATE KEY-----
diff --git a/tests/auto/qmlnetwork/qqmlsslconfiguration/qml/tst_sslconfiguration.qml b/tests/auto/qmlnetwork/qqmlsslconfiguration/qml/tst_sslconfiguration.qml
new file mode 100644
index 0000000000..f4404e5557
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlsslconfiguration/qml/tst_sslconfiguration.qml
@@ -0,0 +1,158 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtTest
+import QtNetwork
+import QmlTestUri
+
+Item {
+ id: root
+
+ property sslConfiguration userSslObject;
+ property sslConfiguration defaultSslObject;
+ property sslDtlsConfiguration dtlsDefaultSslObj;
+ property sslKey key;
+
+ TestCase {
+ name: "qtSslConfigurationTest"
+
+ function test_1initialization() {
+ userSslObject.peerVerifyMode = SslSocket.QueryPeer
+ userSslObject.peerVerifyDepth = 0
+ userSslObject.protocol = Ssl.TlsV1_2
+ userSslObject.sslOptions = [Ssl.SslOptionDisableEmptyFragments]
+
+ defaultSslObject.peerVerifyMode = SslSocket.VerifyPeer;
+ dtlsDefaultSslObj.peerVerifyMode = SslSocket.VerifyNone;
+
+ key.keyFile = ":/data/key.pem"
+ key.keyAlgorithm = Ssl.Rsa
+ key.keyFormat = Ssl.Pem
+
+ // Call invokable functions for ssl object
+ userSslObject.setCertificateFiles([":/data/cert.pem"])
+ userSslObject.setPrivateKey(key)
+ }
+
+ function test_sslEnumsFields_data() {
+ return [
+ // enum SslProtocol
+ { tag: "SslProtocol.TlsV1_2OrLater",
+ field: Ssl.TlsV1_2OrLater, answer: 7 },
+ { tag: "SslProtocol.DtlsV1_2",
+ field: Ssl.DtlsV1_2, answer: 10 },
+ { tag: "SslProtocol.DtlsV1_2OrLater",
+ field: Ssl.DtlsV1_2OrLater, answer: 11},
+ { tag: "SslProtocol.TlsV1_3",
+ field: Ssl.TlsV1_3, answer: 12 },
+ { tag: "SslProtocol.TlsV1_3OrLater",
+ field: Ssl.TlsV1_3OrLater, answer: 13 },
+ { tag: "SslProtocol.UnknownProtocol",
+ field: Ssl.UnknownProtocol, answer: -1 },
+
+ // enum EncodingFormat
+ { tag: "EncodingFormat.Pem",
+ field: Ssl.Pem, answer: 0 },
+ { tag: "EncodingFormat.Der",
+ field: Ssl.Der, answer: 1 },
+
+ // enum KeyType
+ { tag: "KeyType.key",
+ field: Ssl.PrivateKey, answer: 0 },
+ { tag: "KeyType.PublicKey",
+ field: Ssl.PublicKey, answer: 1 },
+
+ //enum KeyAlgorithm
+ { tag: "KeyAlgorithm.Opaque",
+ field: Ssl.Opaque, answer: 0 },
+ { tag: "KeyAlgorithm.Rsa",
+ field: Ssl.Rsa, answer: 1 },
+ { tag: "KeyAlgorithm.Dsa",
+ field: Ssl.Dsa, answer: 2 },
+ { tag: "KeyAlgorithm.Ec",
+ field: Ssl.Ec, answer: 3 },
+ { tag: "KeyAlgorithm.Dh",
+ field: Ssl.Dh, answer: 4 },
+
+ // enum SslOption
+ { tag: "SslOption.SslOptionDisableEmptyFragments",
+ field: Ssl.SslOptionDisableEmptyFragments, answer: 0x01 },
+ { tag: "SslOption.SslOptionDisableSessionTickets",
+ field: Ssl.SslOptionDisableSessionTickets, answer: 0x02 },
+ { tag: "SslOption.SslOptionDisableCompression",
+ field: Ssl.SslOptionDisableCompression, answer: 0x04 },
+ { tag: "SslOption.SslOptionDisableServerNameIndication",
+ field: Ssl.SslOptionDisableServerNameIndication, answer: 0x08 },
+ { tag: "SslOption.SslOptionDisableLegacyRenegotiation",
+ field: Ssl.SslOptionDisableLegacyRenegotiation, answer: 0x10 },
+ { tag: "SslOption.SslOptionDisableSessionSharing",
+ field: Ssl.SslOptionDisableSessionSharing, answer: 0x20 },
+ { tag: "SslOption.SslOptionDisableSessionPersistence",
+ field: Ssl.SslOptionDisableSessionPersistence, answer: 0x40 },
+ { tag: "SslOption.SslOptionDisableServerCipherPreference",
+ field: Ssl.SslOptionDisableServerCipherPreference, answer: 0x80 },
+
+ // enum PeerVerifyMode
+ { tag: "PeerVerifyMode.VerifyNone",
+ field: SslSocket.VerifyNone, answer: 0 },
+ { tag: "PeerVerifyMode.QueryPeer",
+ field: SslSocket.QueryPeer, answer: 1 },
+ { tag: "PeerVerifyMode.VerifyPeer",
+ field: SslSocket.VerifyPeer, answer: 2 },
+ { tag: "PeerVerifyMode.AutoVerifyPeer",
+ field: SslSocket.AutoVerifyPeer, answer: 3 }
+ ]
+ }
+
+ function test_sslEnumsFields(data) {
+ compare(data.field, data.answer)
+ }
+
+ function test_sslConfigurationFields_data() {
+ return [
+ { tag: "userSslObject is creatable object",
+ field: typeof userSslObject, answer: "object" },
+ { tag: "defaultSslObject is creatable object",
+ field: typeof defaultSslObject, answer: "object" },
+ { tag: "dtlsDefaultSslObj is creatable object",
+ field: typeof dtlsDefaultSslObj, answer: "object" },
+ { tag: "key is creatable object",
+ field: typeof key, answer: "object" },
+ { tag: "userSslObject.sslOptions is creatable object",
+ field: typeof userSslObject.sslOptions,
+ answer: "object" },
+
+ // userSslObject
+ { tag: "userSslObject.peerVerifyMode == Ssl.QueryPeer",
+ field: userSslObject.peerVerifyMode, answer: SslSocket.QueryPeer },
+ // defaultSslObject
+ { tag: "defaultSslObject.peerVerifyMode == Ssl.VerifyPeer",
+ field: defaultSslObject.peerVerifyMode, answer: SslSocket.VerifyPeer },
+ // dtlsDefaultSslObj
+ { tag: "dtlsDefaultSslObj.peerVerifyMode == Ssl.VerifyNone",
+ field: dtlsDefaultSslObj.peerVerifyMode,
+ answer: SslSocket.VerifyNone },
+
+ // userSslObject
+ { tag: "userSslObject.peerVerifyDepth == 0",
+ field: userSslObject.peerVerifyDepth, answer: 0 },
+ { tag: "userSslObject.sslOptions == Ssl.SslOptionDisableEmptyFragments",
+ field: userSslObject.sslOptions[0],
+ answer: Ssl.SslOptionDisableEmptyFragments },
+ { tag: "SSL configuration protocol == SslProtocol.TlsV1_2",
+ field: userSslObject.protocol, answer: Ssl.TlsV1_2 },
+ { tag: "key.keyFile == :/data/key.pem",
+ field: key.keyFile, answer: ":/data/key.pem" },
+ { tag: "key.keyAlgorithm == Ssl.Rsa",
+ field: key.keyAlgorithm, answer: Ssl.Rsa },
+ { tag: "key.keyFormat == Ssl.Pem",
+ field: key.keyFormat, answer: Ssl.Pem }
+ ]
+ }
+
+ function test_sslConfigurationFields(data) {
+ compare(data.field, data.answer)
+ }
+ }
+}
diff --git a/tests/auto/qmlnetwork/qqmlsslconfiguration/tst_sslconfiguration_qml.cpp b/tests/auto/qmlnetwork/qqmlsslconfiguration/tst_sslconfiguration_qml.cpp
new file mode 100644
index 0000000000..eded07c9ac
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlsslconfiguration/tst_sslconfiguration_qml.cpp
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQuickTest>
+
+QUICK_TEST_MAIN(tst_sslconfiguration_qml)
diff --git a/tests/auto/qmlnetwork/qqmlsslkey/CMakeLists.txt b/tests/auto/qmlnetwork/qqmlsslkey/CMakeLists.txt
new file mode 100644
index 0000000000..eb9562add6
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlsslkey/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_sslkey_qml LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_sslkey_qml
+ QMLTEST
+ SOURCES
+ tst_sslkey_qml.cpp
+ LIBRARIES
+ Qt::Qml
+ Qt::QuickTestUtilsPrivate
+ Qt::QmlNetwork
+)
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_qml_module(tst_sslkey_qml
+ URI QmlTestUri
+ VERSION 1.0
+ QML_FILES
+ qml/tst_sslkey.qml
+)
+
+qt_autogen_tools_initial_setup(tst_sslkey_qml)
diff --git a/tests/auto/qmlnetwork/qqmlsslkey/qml/tst_sslkey.qml b/tests/auto/qmlnetwork/qqmlsslkey/qml/tst_sslkey.qml
new file mode 100644
index 0000000000..3b4aab2ec6
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlsslkey/qml/tst_sslkey.qml
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtTest
+import QtNetwork
+import QmlTestUri
+
+Item {
+ id: root
+
+ property sslKey keyFirst;
+ property sslKey keySecond;
+
+ TestCase {
+ name: "qtSslKeyTest"
+
+ function test_1initialization() {
+ keyFirst.keyFile = ":/assets/key.pem"
+ keyFirst.keyAlgorithm = Ssl.Rsa
+ keyFirst.keyFormat = Ssl.Pem
+ keyFirst.keyType = Ssl.PrivateKey
+
+ keySecond = keyFirst;
+ }
+
+ function test_sslConfigurationFields_data() {
+ return [
+ { tag: "key is creatable object",
+ field: typeof keyFirst, answer: "object" },
+ { tag: "keySecond is creatable object",
+ field: typeof keySecond, answer: "object" },
+
+ // key values
+ { tag: "keyFirst.keyFile == :/assets/key.pem",
+ field: keyFirst.keyFile, answer: ":/assets/key.pem" },
+ { tag: "keyFirst.keyAlgorithm == Ssl.Rsa",
+ field: keyFirst.keyAlgorithm, answer: Ssl.Rsa },
+ { tag: "keyFirst.keyFormat == Ssl.Pem",
+ field: keyFirst.keyFormat, answer: Ssl.Pem },
+ { tag: "keyFirst.keyType == Ssl.PrivateKey",
+ field: keyFirst.keyType, answer: Ssl.PrivateKey },
+
+ // keySecond values
+ { tag: "keySecond.keyFile == :/assets/key.pem",
+ field: keySecond.keyFile, answer: ":/assets/key.pem" },
+ { tag: "keySecond.keyAlgorithm == Ssl.Rsa",
+ field: keySecond.keyAlgorithm, answer: Ssl.Rsa },
+ { tag: "keySecond.keyFormat == Ssl.Pem",
+ field: keySecond.keyFormat, answer: Ssl.Pem },
+ { tag: "keySecond.keyType == Ssl.PrivateKey",
+ field: keySecond.keyType, answer: Ssl.PrivateKey }
+ ]
+ }
+
+ function test_sslConfigurationFields(data) {
+ compare(data.field, data.answer)
+ }
+ }
+}
diff --git a/tests/auto/qmlnetwork/qqmlsslkey/tst_sslkey_qml.cpp b/tests/auto/qmlnetwork/qqmlsslkey/tst_sslkey_qml.cpp
new file mode 100644
index 0000000000..9369c356d8
--- /dev/null
+++ b/tests/auto/qmlnetwork/qqmlsslkey/tst_sslkey_qml.cpp
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQuickTest>
+
+QUICK_TEST_MAIN(tst_sslkey_qml)
diff --git a/tests/auto/qmltest-blacklist/animators/Box.qml b/tests/auto/qmltest-blacklist/animators/Box.qml
index d32d0e7c8e..f1fe1d091e 100644
--- a/tests/auto/qmltest-blacklist/animators/Box.qml
+++ b/tests/auto/qmltest-blacklist/animators/Box.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/qmltest-blacklist/animators/tst_behavior.qml b/tests/auto/qmltest-blacklist/animators/tst_behavior.qml
index eedbad5441..d001b0d49e 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_behavior.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_behavior.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_mixed.qml b/tests/auto/qmltest-blacklist/animators/tst_mixed.qml
index b0f5cb01f5..cc3d36b7e5 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_mixed.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_mixed.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml b/tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml
index 9e93021d38..96e762e6bd 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_mixedparallel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml b/tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml
index 9b0aee2a49..9a8d24fab8 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_mixedsequential.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml b/tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml
index d678c8db1d..f8e7096aa0 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_multiwindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_nested.qml b/tests/auto/qmltest-blacklist/animators/tst_nested.qml
index 8b73c07368..07ae9be3d2 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_nested.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_nested.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_on.qml b/tests/auto/qmltest-blacklist/animators/tst_on.qml
index f058aaacc1..264300e83c 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_on.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_on.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_opacity.qml b/tests/auto/qmltest-blacklist/animators/tst_opacity.qml
index d41537a637..e43163e593 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_opacity.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_opacity.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_parallel.qml b/tests/auto/qmltest-blacklist/animators/tst_parallel.qml
index bae616242d..16bdb4ac68 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_parallel.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_parallel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_restart.qml b/tests/auto/qmltest-blacklist/animators/tst_restart.qml
index 3e252ea273..0f247237c5 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_restart.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_restart.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_rotation.qml b/tests/auto/qmltest-blacklist/animators/tst_rotation.qml
index f72235b2e1..6af6f95803 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_rotation.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_rotation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_scale.qml b/tests/auto/qmltest-blacklist/animators/tst_scale.qml
index 384c597c7c..3aef72eb34 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_scale.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_scale.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_sequential.qml b/tests/auto/qmltest-blacklist/animators/tst_sequential.qml
index bbcb84d46b..ef9026a217 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_sequential.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_sequential.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_stopped.qml b/tests/auto/qmltest-blacklist/animators/tst_stopped.qml
index 3f27fa1a69..0966a3145c 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_stopped.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_stopped.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml b/tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml
index 104ecf2929..3cf6b90aa8 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_targetdestroyed.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Gunnar Sletta <gunnar@sletta.org>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml b/tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml
index be74ae7b5f..5947e69700 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_transformorigin.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_transition.qml b/tests/auto/qmltest-blacklist/animators/tst_transition.qml
index be177491e9..1e9ea0f94b 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_transition.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_transition.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_x.qml b/tests/auto/qmltest-blacklist/animators/tst_x.qml
index c8132b7ce1..63d1cf90f6 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_x.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_x.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/animators/tst_y.qml b/tests/auto/qmltest-blacklist/animators/tst_y.qml
index a58f803d47..de3cf0a322 100644
--- a/tests/auto/qmltest-blacklist/animators/tst_y.qml
+++ b/tests/auto/qmltest-blacklist/animators/tst_y.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml b/tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml
index 5acba142e4..6aead67530 100644
--- a/tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml
+++ b/tests/auto/qmltest-blacklist/item/tst_layerInPositioner.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
import QtTest 1.1
diff --git a/tests/auto/qmltest-blacklist/shortcut/CMakeLists.txt b/tests/auto/qmltest-blacklist/shortcut/CMakeLists.txt
index 962122f888..694f7522a7 100644
--- a/tests/auto/qmltest-blacklist/shortcut/CMakeLists.txt
+++ b/tests/auto/qmltest-blacklist/shortcut/CMakeLists.txt
@@ -7,6 +7,12 @@
## shortcut Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(shortcut LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(shortcut
GUI
QMLTEST
diff --git a/tests/auto/qmltest-blacklist/shortcut/tst_shortcut.qml b/tests/auto/qmltest-blacklist/shortcut/tst_shortcut.qml
index 9887f10c21..97ae2cc387 100644
--- a/tests/auto/qmltest-blacklist/shortcut/tst_shortcut.qml
+++ b/tests/auto/qmltest-blacklist/shortcut/tst_shortcut.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.8
import QtQuick.Window 2.2
diff --git a/tests/auto/qmltest/animatedimage/CMakeLists.txt b/tests/auto/qmltest/animatedimage/CMakeLists.txt
index 93f5b5edba..0887077356 100644
--- a/tests/auto/qmltest/animatedimage/CMakeLists.txt
+++ b/tests/auto/qmltest/animatedimage/CMakeLists.txt
@@ -7,6 +7,12 @@
## animatedimage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(animatedimage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(animatedimage
GUI
QMLTEST
diff --git a/tests/auto/qmltest/animatedimage/tst_animatedimage.qml b/tests/auto/qmltest/animatedimage/tst_animatedimage.qml
index cd46033acc..a5f248480e 100644
--- a/tests/auto/qmltest/animatedimage/tst_animatedimage.qml
+++ b/tests/auto/qmltest/animatedimage/tst_animatedimage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/animations/CMakeLists.txt b/tests/auto/qmltest/animations/CMakeLists.txt
index dcabfffc4a..64084605c8 100644
--- a/tests/auto/qmltest/animations/CMakeLists.txt
+++ b/tests/auto/qmltest/animations/CMakeLists.txt
@@ -7,6 +7,12 @@
## animations Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(animations LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(animations
GUI
QMLTEST
diff --git a/tests/auto/qmltest/animations/tst_abstractanimationjobcrash.qml b/tests/auto/qmltest/animations/tst_abstractanimationjobcrash.qml
index fd9a082f26..bad8a1c0ba 100644
--- a/tests/auto/qmltest/animations/tst_abstractanimationjobcrash.qml
+++ b/tests/auto/qmltest/animations/tst_abstractanimationjobcrash.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/borderimage/CMakeLists.txt b/tests/auto/qmltest/borderimage/CMakeLists.txt
index 3120f6781f..c582dc3380 100644
--- a/tests/auto/qmltest/borderimage/CMakeLists.txt
+++ b/tests/auto/qmltest/borderimage/CMakeLists.txt
@@ -7,6 +7,12 @@
## borderimage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(borderimage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(borderimage
GUI
QMLTEST
diff --git a/tests/auto/qmltest/borderimage/InvalidSciFile.qml b/tests/auto/qmltest/borderimage/InvalidSciFile.qml
index 1f8af5741e..1e52515b04 100644
--- a/tests/auto/qmltest/borderimage/InvalidSciFile.qml
+++ b/tests/auto/qmltest/borderimage/InvalidSciFile.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qmltest/borderimage/tst_borderimage.qml b/tests/auto/qmltest/borderimage/tst_borderimage.qml
index 7227343361..ab075d69c3 100644
--- a/tests/auto/qmltest/borderimage/tst_borderimage.qml
+++ b/tests/auto/qmltest/borderimage/tst_borderimage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/buttonclick/Button.qml b/tests/auto/qmltest/buttonclick/Button.qml
index 0dd684f780..f59ae5e3c7 100644
--- a/tests/auto/qmltest/buttonclick/Button.qml
+++ b/tests/auto/qmltest/buttonclick/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qmltest/buttonclick/CMakeLists.txt b/tests/auto/qmltest/buttonclick/CMakeLists.txt
index e00f5c9f93..1368afc73b 100644
--- a/tests/auto/qmltest/buttonclick/CMakeLists.txt
+++ b/tests/auto/qmltest/buttonclick/CMakeLists.txt
@@ -7,6 +7,12 @@
## buttonclick Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(buttonclick LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(buttonclick
GUI
QMLTEST
diff --git a/tests/auto/qmltest/buttonclick/tst_buttonclick.qml b/tests/auto/qmltest/buttonclick/tst_buttonclick.qml
index ba1537cd7c..9bbba7985c 100644
--- a/tests/auto/qmltest/buttonclick/tst_buttonclick.qml
+++ b/tests/auto/qmltest/buttonclick/tst_buttonclick.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/createbenchmark/CMakeLists.txt b/tests/auto/qmltest/createbenchmark/CMakeLists.txt
index 3da0d6e7db..f3cda2637c 100644
--- a/tests/auto/qmltest/createbenchmark/CMakeLists.txt
+++ b/tests/auto/qmltest/createbenchmark/CMakeLists.txt
@@ -7,6 +7,12 @@
## createbenchmark Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(createbenchmark LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(createbenchmark
GUI
QMLTEST
diff --git a/tests/auto/qmltest/createbenchmark/item.qml b/tests/auto/qmltest/createbenchmark/item.qml
index b37bc121db..9ab0a763f5 100644
--- a/tests/auto/qmltest/createbenchmark/item.qml
+++ b/tests/auto/qmltest/createbenchmark/item.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml b/tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml
index 0a40474f76..898bbb7f58 100644
--- a/tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml
+++ b/tests/auto/qmltest/createbenchmark/tst_createbenchmark.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/events/CMakeLists.txt b/tests/auto/qmltest/events/CMakeLists.txt
index 53e022ca41..865a8ff762 100644
--- a/tests/auto/qmltest/events/CMakeLists.txt
+++ b/tests/auto/qmltest/events/CMakeLists.txt
@@ -7,6 +7,12 @@
## events Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(events LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(events
GUI
QMLTEST
diff --git a/tests/auto/qmltest/events/tst_drag.qml b/tests/auto/qmltest/events/tst_drag.qml
index 7be981de4c..6885baa873 100644
--- a/tests/auto/qmltest/events/tst_drag.qml
+++ b/tests/auto/qmltest/events/tst_drag.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/events/tst_events.qml b/tests/auto/qmltest/events/tst_events.qml
index 341292c1c0..0011b63249 100644
--- a/tests/auto/qmltest/events/tst_events.qml
+++ b/tests/auto/qmltest/events/tst_events.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.0
diff --git a/tests/auto/qmltest/events/tst_touch.qml b/tests/auto/qmltest/events/tst_touch.qml
index dba656e21c..105945eeb6 100644
--- a/tests/auto/qmltest/events/tst_touch.qml
+++ b/tests/auto/qmltest/events/tst_touch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jeremy Katz
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.0
diff --git a/tests/auto/qmltest/events/tst_wheel.qml b/tests/auto/qmltest/events/tst_wheel.qml
index ec3b19de6e..9acd81910f 100644
--- a/tests/auto/qmltest/events/tst_wheel.qml
+++ b/tests/auto/qmltest/events/tst_wheel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/fontloader/CMakeLists.txt b/tests/auto/qmltest/fontloader/CMakeLists.txt
index 8700e106de..618896d90c 100644
--- a/tests/auto/qmltest/fontloader/CMakeLists.txt
+++ b/tests/auto/qmltest/fontloader/CMakeLists.txt
@@ -7,6 +7,12 @@
## fontloader Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(fontloader LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(fontloader
GUI
QMLTEST
diff --git a/tests/auto/qmltest/fontloader/tst_fontloader.qml b/tests/auto/qmltest/fontloader/tst_fontloader.qml
index fe873f9360..eea8b44107 100644
--- a/tests/auto/qmltest/fontloader/tst_fontloader.qml
+++ b/tests/auto/qmltest/fontloader/tst_fontloader.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
import QtTest 1.1
diff --git a/tests/auto/qmltest/fuzzycompare/CMakeLists.txt b/tests/auto/qmltest/fuzzycompare/CMakeLists.txt
index 4c93595376..83b505af71 100644
--- a/tests/auto/qmltest/fuzzycompare/CMakeLists.txt
+++ b/tests/auto/qmltest/fuzzycompare/CMakeLists.txt
@@ -7,6 +7,12 @@
## fuzzycompare Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(fuzzycompare LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(fuzzycompare
GUI
QMLTEST
diff --git a/tests/auto/qmltest/fuzzycompare/tst_FuzzyCompare.qml b/tests/auto/qmltest/fuzzycompare/tst_FuzzyCompare.qml
index 2e4ae8cc67..6beb89f848 100644
--- a/tests/auto/qmltest/fuzzycompare/tst_FuzzyCompare.qml
+++ b/tests/auto/qmltest/fuzzycompare/tst_FuzzyCompare.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 Alexander Akulich <akulichalexander@gmail.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtTest 1.0
TestCase {
diff --git a/tests/auto/qmltest/gradient/CMakeLists.txt b/tests/auto/qmltest/gradient/CMakeLists.txt
index 48a339f128..038281c495 100644
--- a/tests/auto/qmltest/gradient/CMakeLists.txt
+++ b/tests/auto/qmltest/gradient/CMakeLists.txt
@@ -7,6 +7,12 @@
## gradient Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(gradient LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(gradient
GUI
QMLTEST
diff --git a/tests/auto/qmltest/gradient/tst_gradient.qml b/tests/auto/qmltest/gradient/tst_gradient.qml
index 368f2ba27e..91fcfada89 100644
--- a/tests/auto/qmltest/gradient/tst_gradient.qml
+++ b/tests/auto/qmltest/gradient/tst_gradient.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/image/CMakeLists.txt b/tests/auto/qmltest/image/CMakeLists.txt
index 31d4cc2ce7..d9aa506b35 100644
--- a/tests/auto/qmltest/image/CMakeLists.txt
+++ b/tests/auto/qmltest/image/CMakeLists.txt
@@ -7,6 +7,12 @@
## image Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(image LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(image
GUI
QMLTEST
diff --git a/tests/auto/qmltest/image/tst_image.qml b/tests/auto/qmltest/image/tst_image.qml
index 9e1698cdef..5077e04024 100644
--- a/tests/auto/qmltest/image/tst_image.qml
+++ b/tests/auto/qmltest/image/tst_image.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/itemgrabber/CMakeLists.txt b/tests/auto/qmltest/itemgrabber/CMakeLists.txt
index 378cba80d6..34b0483cd8 100644
--- a/tests/auto/qmltest/itemgrabber/CMakeLists.txt
+++ b/tests/auto/qmltest/itemgrabber/CMakeLists.txt
@@ -7,6 +7,12 @@
## itemgrabber Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(itemgrabber LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(itemgrabber
GUI
QMLTEST
diff --git a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml
index 6ee09a9a5b..aad8722f03 100644
--- a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml
+++ b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
import QtTest 1.1
diff --git a/tests/auto/qmltest/layout/CMakeLists.txt b/tests/auto/qmltest/layout/CMakeLists.txt
index abd6682556..4b3e254e9d 100644
--- a/tests/auto/qmltest/layout/CMakeLists.txt
+++ b/tests/auto/qmltest/layout/CMakeLists.txt
@@ -7,6 +7,12 @@
## layout Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(layout LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(layout
GUI
QMLTEST
diff --git a/tests/auto/qmltest/layout/Container.qml b/tests/auto/qmltest/layout/Container.qml
index 63b26be1f0..402a704622 100644
--- a/tests/auto/qmltest/layout/Container.qml
+++ b/tests/auto/qmltest/layout/Container.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.5
import QtQuick.Layouts 1.2
diff --git a/tests/auto/qmltest/layout/ContainerUser.qml b/tests/auto/qmltest/layout/ContainerUser.qml
index 7e26ee46c2..c13aa584e3 100644
--- a/tests/auto/qmltest/layout/ContainerUser.qml
+++ b/tests/auto/qmltest/layout/ContainerUser.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.6
import QtQuick.Window 2.2
diff --git a/tests/auto/qmltest/layout/tst_layout.qml b/tests/auto/qmltest/layout/tst_layout.qml
index 910ec60a54..5d9799c6ef 100644
--- a/tests/auto/qmltest/layout/tst_layout.qml
+++ b/tests/auto/qmltest/layout/tst_layout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.0
diff --git a/tests/auto/qmltest/listmodel/CMakeLists.txt b/tests/auto/qmltest/listmodel/CMakeLists.txt
index a88402f813..d70bd288e1 100644
--- a/tests/auto/qmltest/listmodel/CMakeLists.txt
+++ b/tests/auto/qmltest/listmodel/CMakeLists.txt
@@ -7,6 +7,12 @@
## listmodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(listmodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(listmodel
GUI
QMLTEST
diff --git a/tests/auto/qmltest/listmodel/tst_listmodel.qml b/tests/auto/qmltest/listmodel/tst_listmodel.qml
index d959991050..4ee07d8162 100644
--- a/tests/auto/qmltest/listmodel/tst_listmodel.qml
+++ b/tests/auto/qmltest/listmodel/tst_listmodel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/listview/CMakeLists.txt b/tests/auto/qmltest/listview/CMakeLists.txt
index dd796a2086..6935b2cecf 100644
--- a/tests/auto/qmltest/listview/CMakeLists.txt
+++ b/tests/auto/qmltest/listview/CMakeLists.txt
@@ -7,6 +7,12 @@
## listview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(listview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(listview
GUI
QMLTEST
diff --git a/tests/auto/qmltest/listview/data/MultiDelegate.qml b/tests/auto/qmltest/listview/data/MultiDelegate.qml
index 5b7f9d12ac..491171d8e8 100644
--- a/tests/auto/qmltest/listview/data/MultiDelegate.qml
+++ b/tests/auto/qmltest/listview/data/MultiDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQml.Models 2.12
diff --git a/tests/auto/qmltest/listview/data/MultiDelegate2.qml b/tests/auto/qmltest/listview/data/MultiDelegate2.qml
index e3d0fc64cb..9ff7b44458 100644
--- a/tests/auto/qmltest/listview/data/MultiDelegate2.qml
+++ b/tests/auto/qmltest/listview/data/MultiDelegate2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQml.Models 2.12
diff --git a/tests/auto/qmltest/listview/data/MultiDelegate3.qml b/tests/auto/qmltest/listview/data/MultiDelegate3.qml
index a51dbbb49f..2e13364c66 100644
--- a/tests/auto/qmltest/listview/data/MultiDelegate3.qml
+++ b/tests/auto/qmltest/listview/data/MultiDelegate3.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQml.Models 2.12
diff --git a/tests/auto/qmltest/listview/data/asynclistviewloader.qml b/tests/auto/qmltest/listview/data/asynclistviewloader.qml
index 40d0c97b8c..1c500717db 100644
--- a/tests/auto/qmltest/listview/data/asynclistviewloader.qml
+++ b/tests/auto/qmltest/listview/data/asynclistviewloader.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qmltest/listview/data/asyncloadercurrentindex.qml b/tests/auto/qmltest/listview/data/asyncloadercurrentindex.qml
index 25976d5246..ec2cb6c24c 100644
--- a/tests/auto/qmltest/listview/data/asyncloadercurrentindex.qml
+++ b/tests/auto/qmltest/listview/data/asyncloadercurrentindex.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/qmltest/listview/tst_listview.qml b/tests/auto/qmltest/listview/tst_listview.qml
index e9c91e10f2..8d5204db40 100644
--- a/tests/auto/qmltest/listview/tst_listview.qml
+++ b/tests/auto/qmltest/listview/tst_listview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
import QtTest 1.1
diff --git a/tests/auto/qmltest/objectmodel/CMakeLists.txt b/tests/auto/qmltest/objectmodel/CMakeLists.txt
index abbe499257..6e92bb81a8 100644
--- a/tests/auto/qmltest/objectmodel/CMakeLists.txt
+++ b/tests/auto/qmltest/objectmodel/CMakeLists.txt
@@ -7,6 +7,12 @@
## objectmodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(objectmodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(objectmodel
GUI
QMLTEST
diff --git a/tests/auto/qmltest/objectmodel/tst_objectmodel.qml b/tests/auto/qmltest/objectmodel/tst_objectmodel.qml
index 440bf0abb5..61439b81d6 100644
--- a/tests/auto/qmltest/objectmodel/tst_objectmodel.qml
+++ b/tests/auto/qmltest/objectmodel/tst_objectmodel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.1
import QtQml.Models 2.3
diff --git a/tests/auto/qmltest/pathview/CMakeLists.txt b/tests/auto/qmltest/pathview/CMakeLists.txt
index b7d23f62cf..995958565c 100644
--- a/tests/auto/qmltest/pathview/CMakeLists.txt
+++ b/tests/auto/qmltest/pathview/CMakeLists.txt
@@ -7,6 +7,12 @@
## pathview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(pathview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(pathview
GUI
QMLTEST
diff --git a/tests/auto/qmltest/positioners/CMakeLists.txt b/tests/auto/qmltest/positioners/CMakeLists.txt
index a2fc28a487..2e91df68b7 100644
--- a/tests/auto/qmltest/positioners/CMakeLists.txt
+++ b/tests/auto/qmltest/positioners/CMakeLists.txt
@@ -7,6 +7,12 @@
## positioners Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(positioners LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(positioners
GUI
QMLTEST
diff --git a/tests/auto/qmltest/positioners/tst_positioners.qml b/tests/auto/qmltest/positioners/tst_positioners.qml
index 3933616de2..1c7f8ff95e 100644
--- a/tests/auto/qmltest/positioners/tst_positioners.qml
+++ b/tests/auto/qmltest/positioners/tst_positioners.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
import QtTest 1.1
diff --git a/tests/auto/qmltest/qqmlbinding/CMakeLists.txt b/tests/auto/qmltest/qqmlbinding/CMakeLists.txt
index ea35d622e4..8cb767a1ca 100644
--- a/tests/auto/qmltest/qqmlbinding/CMakeLists.txt
+++ b/tests/auto/qmltest/qqmlbinding/CMakeLists.txt
@@ -7,6 +7,12 @@
## qqmlbinding Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(qqmlbinding LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(qqmlbinding
GUI
QMLTEST
diff --git a/tests/auto/qmltest/qqmlbinding/tst_binding.qml b/tests/auto/qmltest/qqmlbinding/tst_binding.qml
index 783ec6dbed..981a88d738 100644
--- a/tests/auto/qmltest/qqmlbinding/tst_binding.qml
+++ b/tests/auto/qmltest/qqmlbinding/tst_binding.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/qqmlbinding/tst_binding2.qml b/tests/auto/qmltest/qqmlbinding/tst_binding2.qml
index 91ccb95f71..2bf5a0166a 100644
--- a/tests/auto/qmltest/qqmlbinding/tst_binding2.qml
+++ b/tests/auto/qmltest/qqmlbinding/tst_binding2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/qtbug46798/CMakeLists.txt b/tests/auto/qmltest/qtbug46798/CMakeLists.txt
index 83564df53f..492a9bab04 100644
--- a/tests/auto/qmltest/qtbug46798/CMakeLists.txt
+++ b/tests/auto/qmltest/qtbug46798/CMakeLists.txt
@@ -7,6 +7,12 @@
## qtbug46798 Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(qtbug46798 LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(qtbug46798
GUI
QMLTEST
diff --git a/tests/auto/qmltest/qtbug46798/tst_qtbug46798.qml b/tests/auto/qmltest/qtbug46798/tst_qtbug46798.qml
index f749705232..a99bdf7af7 100644
--- a/tests/auto/qmltest/qtbug46798/tst_qtbug46798.qml
+++ b/tests/auto/qmltest/qtbug46798/tst_qtbug46798.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtQml.Models 2.3
diff --git a/tests/auto/qmltest/rectangle/CMakeLists.txt b/tests/auto/qmltest/rectangle/CMakeLists.txt
index 08986a3013..9df9422824 100644
--- a/tests/auto/qmltest/rectangle/CMakeLists.txt
+++ b/tests/auto/qmltest/rectangle/CMakeLists.txt
@@ -7,6 +7,12 @@
## rectangle Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(rectangle LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(rectangle
GUI
QMLTEST
diff --git a/tests/auto/qmltest/rectangle/tst_rectangle.qml b/tests/auto/qmltest/rectangle/tst_rectangle.qml
index 4292ea02cf..ab479cbbbf 100644
--- a/tests/auto/qmltest/rectangle/tst_rectangle.qml
+++ b/tests/auto/qmltest/rectangle/tst_rectangle.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/CMakeLists.txt b/tests/auto/qmltest/selftests/CMakeLists.txt
index 8122f203ee..d2af0775b1 100644
--- a/tests/auto/qmltest/selftests/CMakeLists.txt
+++ b/tests/auto/qmltest/selftests/CMakeLists.txt
@@ -7,6 +7,12 @@
## selftests Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(selftests LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(selftests
GUI
QMLTEST
diff --git a/tests/auto/qmltest/selftests/tst_compare.qml b/tests/auto/qmltest/selftests/tst_compare.qml
index 04949d5e9a..62ce80944f 100644
--- a/tests/auto/qmltest/selftests/tst_compare.qml
+++ b/tests/auto/qmltest/selftests/tst_compare.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/tst_compare_quickobjects.qml b/tests/auto/qmltest/selftests/tst_compare_quickobjects.qml
index 30c7133e97..90a1f31bcc 100644
--- a/tests/auto/qmltest/selftests/tst_compare_quickobjects.qml
+++ b/tests/auto/qmltest/selftests/tst_compare_quickobjects.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/tst_createTemporaryObject.qml b/tests/auto/qmltest/selftests/tst_createTemporaryObject.qml
index a54e5f1e7b..2d9742dc5b 100644
--- a/tests/auto/qmltest/selftests/tst_createTemporaryObject.qml
+++ b/tests/auto/qmltest/selftests/tst_createTemporaryObject.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.8
import QtQuick.Window 2.2
diff --git a/tests/auto/qmltest/selftests/tst_datadriven.qml b/tests/auto/qmltest/selftests/tst_datadriven.qml
index 49024699a6..a26233f40a 100644
--- a/tests/auto/qmltest/selftests/tst_datadriven.qml
+++ b/tests/auto/qmltest/selftests/tst_datadriven.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/tst_destroy.qml b/tests/auto/qmltest/selftests/tst_destroy.qml
index 96814c4b77..880e03936e 100644
--- a/tests/auto/qmltest/selftests/tst_destroy.qml
+++ b/tests/auto/qmltest/selftests/tst_destroy.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/tst_findChild.qml b/tests/auto/qmltest/selftests/tst_findChild.qml
index c8af04810a..6cdaee4de9 100644
--- a/tests/auto/qmltest/selftests/tst_findChild.qml
+++ b/tests/auto/qmltest/selftests/tst_findChild.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/tst_grabImage.qml b/tests/auto/qmltest/selftests/tst_grabImage.qml
index 9cc22db9a6..9a1bfccdc9 100644
--- a/tests/auto/qmltest/selftests/tst_grabImage.qml
+++ b/tests/auto/qmltest/selftests/tst_grabImage.qml
@@ -1,6 +1,6 @@
// Copyright (C) 2017 Crimson AS <info@crimson.no>
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/tst_multiTestCase.qml b/tests/auto/qmltest/selftests/tst_multiTestCase.qml
index 220888a5da..90a6df8c2f 100644
--- a/tests/auto/qmltest/selftests/tst_multiTestCase.qml
+++ b/tests/auto/qmltest/selftests/tst_multiTestCase.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtTest
import QtQuick
diff --git a/tests/auto/qmltest/selftests/tst_selftests.qml b/tests/auto/qmltest/selftests/tst_selftests.qml
index 35089be68e..597a13632f 100644
--- a/tests/auto/qmltest/selftests/tst_selftests.qml
+++ b/tests/auto/qmltest/selftests/tst_selftests.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/tst_signalspy.qml b/tests/auto/qmltest/selftests/tst_signalspy.qml
new file mode 100644
index 0000000000..4346003a6f
--- /dev/null
+++ b/tests/auto/qmltest/selftests/tst_signalspy.qml
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtTest
+
+TestCase {
+ id: testCase
+ name: "SignalSpy"
+
+ Component {
+ id: itemWithAmbigiousSignalNameOnChild
+ Item {
+ property Item child: Item {
+ property bool opened: true
+ signal opened()
+ }
+ }
+ }
+
+ Component {
+ id: signalSpy
+ SignalSpy {}
+ }
+
+ function init() {
+ failOnWarning(/.?/)
+ }
+
+ function test_ambigiousSignalName() {
+ let control = createTemporaryObject(itemWithAmbigiousSignalNameOnChild, testCase)
+ verify(control)
+
+ let openedSpy = signalSpy.createObject(control, {target: control.child, signalName: "opened"})
+ verify(openedSpy)
+ compare(openedSpy.target, control.child)
+ compare(openedSpy.signalName, "opened")
+ verify(openedSpy.valid)
+
+ // reset the target, i.e. disconnect the spy
+ openedSpy.target = null
+ compare(openedSpy.target, null)
+ compare(openedSpy.signalName, "opened")
+ verify(!openedSpy.valid)
+
+ // connect again to check that it will be disconnected
+ // on SignalSpy destruction with no issues/warnings/etc.
+ openedSpy.target = control.child
+ compare(openedSpy.target, control.child)
+ compare(openedSpy.signalName, "opened")
+ verify(openedSpy.valid)
+ }
+}
diff --git a/tests/auto/qmltest/selftests/tst_stringify.qml b/tests/auto/qmltest/selftests/tst_stringify.qml
index d4cd02e4b4..ed228417e2 100644
--- a/tests/auto/qmltest/selftests/tst_stringify.qml
+++ b/tests/auto/qmltest/selftests/tst_stringify.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/selftests/tst_tryVerify.qml b/tests/auto/qmltest/selftests/tst_tryVerify.qml
index 653afeac75..b510874c47 100644
--- a/tests/auto/qmltest/selftests/tst_tryVerify.qml
+++ b/tests/auto/qmltest/selftests/tst_tryVerify.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.8
import QtTest 1.1
diff --git a/tests/auto/qmltest/shadersource/CMakeLists.txt b/tests/auto/qmltest/shadersource/CMakeLists.txt
index d95af4100c..477aebbe7f 100644
--- a/tests/auto/qmltest/shadersource/CMakeLists.txt
+++ b/tests/auto/qmltest/shadersource/CMakeLists.txt
@@ -7,6 +7,12 @@
## shadersource Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(shadersource LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(shadersource
GUI
QMLTEST
diff --git a/tests/auto/qmltest/shadersource/tst_DynamicallyCreated.qml b/tests/auto/qmltest/shadersource/tst_DynamicallyCreated.qml
index daec74f745..9679298b46 100644
--- a/tests/auto/qmltest/shadersource/tst_DynamicallyCreated.qml
+++ b/tests/auto/qmltest/shadersource/tst_DynamicallyCreated.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtQuick.Window 2.0
diff --git a/tests/auto/qmltest/shadersource/tst_DynamicallyCreatedSource.qml b/tests/auto/qmltest/shadersource/tst_DynamicallyCreatedSource.qml
index 4d79062ed5..4e018143dd 100644
--- a/tests/auto/qmltest/shadersource/tst_DynamicallyCreatedSource.qml
+++ b/tests/auto/qmltest/shadersource/tst_DynamicallyCreatedSource.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Gunnar Sletta <gunnar@sletta.org>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/shadersource/tst_SourceInOtherWindow.qml b/tests/auto/qmltest/shadersource/tst_SourceInOtherWindow.qml
index 8391225933..c615ea3073 100644
--- a/tests/auto/qmltest/shadersource/tst_SourceInOtherWindow.qml
+++ b/tests/auto/qmltest/shadersource/tst_SourceInOtherWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.0
diff --git a/tests/auto/qmltest/shadersource/tst_SourceItem.qml b/tests/auto/qmltest/shadersource/tst_SourceItem.qml
index 18e685b9b8..d7d50c8187 100644
--- a/tests/auto/qmltest/shadersource/tst_SourceItem.qml
+++ b/tests/auto/qmltest/shadersource/tst_SourceItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/shadersource/tst_SourcedFromOtherWindow.qml b/tests/auto/qmltest/shadersource/tst_SourcedFromOtherWindow.qml
index 2da2820fea..052b95dc2c 100644
--- a/tests/auto/qmltest/shadersource/tst_SourcedFromOtherWindow.qml
+++ b/tests/auto/qmltest/shadersource/tst_SourcedFromOtherWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.0
diff --git a/tests/auto/qmltest/stability/CMakeLists.txt b/tests/auto/qmltest/stability/CMakeLists.txt
index a2af01649e..14f74822d8 100644
--- a/tests/auto/qmltest/stability/CMakeLists.txt
+++ b/tests/auto/qmltest/stability/CMakeLists.txt
@@ -7,6 +7,12 @@
## stability Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(stability LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(stability
GUI
QMLTEST
diff --git a/tests/auto/qmltest/stability/tst_unloadrepeater.qml b/tests/auto/qmltest/stability/tst_unloadrepeater.qml
index d446d916db..064df03fcb 100644
--- a/tests/auto/qmltest/stability/tst_unloadrepeater.qml
+++ b/tests/auto/qmltest/stability/tst_unloadrepeater.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest/text/CMakeLists.txt b/tests/auto/qmltest/text/CMakeLists.txt
index 41621ff0f3..91966a7712 100644
--- a/tests/auto/qmltest/text/CMakeLists.txt
+++ b/tests/auto/qmltest/text/CMakeLists.txt
@@ -7,6 +7,12 @@
## text Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(text LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(text
GUI
QMLTEST
diff --git a/tests/auto/qmltest/text/tst_text.qml b/tests/auto/qmltest/text/tst_text.qml
index 407238b0f3..7366e94067 100644
--- a/tests/auto/qmltest/text/tst_text.qml
+++ b/tests/auto/qmltest/text/tst_text.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/textedit/CMakeLists.txt b/tests/auto/qmltest/textedit/CMakeLists.txt
index 863a9f5ba3..0b955a0fef 100644
--- a/tests/auto/qmltest/textedit/CMakeLists.txt
+++ b/tests/auto/qmltest/textedit/CMakeLists.txt
@@ -7,6 +7,12 @@
## textedit Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(textedit LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(textedit
GUI
QMLTEST
diff --git a/tests/auto/qmltest/textedit/tst_textedit.qml b/tests/auto/qmltest/textedit/tst_textedit.qml
index bf53965ae5..c161429de0 100644
--- a/tests/auto/qmltest/textedit/tst_textedit.qml
+++ b/tests/auto/qmltest/textedit/tst_textedit.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/qmltest/textedit/tst_textedit_editingfinished.qml b/tests/auto/qmltest/textedit/tst_textedit_editingfinished.qml
index 1cc338fdea..493f214cb7 100644
--- a/tests/auto/qmltest/textedit/tst_textedit_editingfinished.qml
+++ b/tests/auto/qmltest/textedit/tst_textedit_editingfinished.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.6
import QtTest 1.1
diff --git a/tests/auto/qmltest/textinput/CMakeLists.txt b/tests/auto/qmltest/textinput/CMakeLists.txt
index 832dc58040..4397d05216 100644
--- a/tests/auto/qmltest/textinput/CMakeLists.txt
+++ b/tests/auto/qmltest/textinput/CMakeLists.txt
@@ -7,6 +7,12 @@
## textinput Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(textinput LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(textinput
GUI
QMLTEST
diff --git a/tests/auto/qmltest/textinput/tst_textinput.qml b/tests/auto/qmltest/textinput/tst_textinput.qml
index e734c44370..1a19c5b633 100644
--- a/tests/auto/qmltest/textinput/tst_textinput.qml
+++ b/tests/auto/qmltest/textinput/tst_textinput.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtTest 1.1
@@ -97,7 +97,8 @@ Item {
}
function test_textentry() {
- txtentry.focus = true;
+ txtentry.forceActiveFocus()
+ verify(txtentry.activeFocus)
compare(txtentry.text, "")
keyClick(Qt.Key_H)
keyClick(Qt.Key_E)
diff --git a/tests/auto/qmltest/url/tst_url.qml b/tests/auto/qmltest/url/tst_url.qml
index 23bcc9b744..b3d46d6375 100644
--- a/tests/auto/qmltest/url/tst_url.qml
+++ b/tests/auto/qmltest/url/tst_url.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtTest 1.1
diff --git a/tests/auto/qmltest/window/CMakeLists.txt b/tests/auto/qmltest/window/CMakeLists.txt
index e80aa68dfd..da79240583 100644
--- a/tests/auto/qmltest/window/CMakeLists.txt
+++ b/tests/auto/qmltest/window/CMakeLists.txt
@@ -7,6 +7,12 @@
## window Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(window LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(window
GUI
QMLTEST
diff --git a/tests/auto/qmltest/window/tst_clickwindow.qml b/tests/auto/qmltest/window/tst_clickwindow.qml
index 81144c0923..d2ab052332 100644
--- a/tests/auto/qmltest/window/tst_clickwindow.qml
+++ b/tests/auto/qmltest/window/tst_clickwindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.0
diff --git a/tests/auto/quick/CMakeLists.txt b/tests/auto/quick/CMakeLists.txt
index 2ef42c5b2f..3ecf89f351 100644
--- a/tests/auto/quick/CMakeLists.txt
+++ b/tests/auto/quick/CMakeLists.txt
@@ -14,6 +14,7 @@ endif()
add_subdirectory(drawingmodes)
if(QT_FEATURE_private_tests)
+ add_subdirectory(doc)
add_subdirectory(examples)
add_subdirectory(nokeywords)
add_subdirectory(propertyrequirements)
@@ -42,6 +43,7 @@ if(QT_FEATURE_private_tests)
add_subdirectory(qquickdynamicpropertyanimation)
add_subdirectory(qquickborderimage)
add_subdirectory(qquickwindow)
+ add_subdirectory(qquickwindowcontainer)
add_subdirectory(qquickdeliveryagent)
add_subdirectory(qquickdrag)
add_subdirectory(qquickdragattached)
@@ -77,10 +79,10 @@ if(QT_FEATURE_private_tests)
add_subdirectory(qquickvisualdatamodel)
add_subdirectory(qquickview)
add_subdirectory(qquickview_extra)
- if(NOT ANDROID) # QTBUG-103099
- add_subdirectory(qquickcanvasitem)
+ add_subdirectory(qquickcanvasitem)
+ if(QT_FEATURE_quick_designer)
+ add_subdirectory(qquickdesignersupport)
endif()
- add_subdirectory(qquickdesignersupport)
add_subdirectory(qquickscreen)
add_subdirectory(touchmouse)
add_subdirectory(scenegraph)
@@ -91,7 +93,7 @@ if(QT_FEATURE_private_tests)
add_subdirectory(qquickshadereffect)
add_subdirectory(qquickanimatedsprite)
add_subdirectory(qquickspritesequence)
- add_subdirectory(qquickitemrhiintegration)
+ add_subdirectory(qquickrhiitem)
add_subdirectory(rendernode)
if(QT_FEATURE_opengl)
diff --git a/tests/auto/quick/doc/CMakeLists.txt b/tests/auto/quick/doc/CMakeLists.txt
new file mode 100644
index 0000000000..1a7e1b834e
--- /dev/null
+++ b/tests/auto/quick/doc/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# This directory contains auto tests for documentation examples/snippets.
+
+add_subdirectory(how-tos)
+
diff --git a/tests/auto/quick/doc/how-tos/CMakeLists.txt b/tests/auto/quick/doc/how-tos/CMakeLists.txt
new file mode 100644
index 0000000000..202ff38bb5
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(how-to-cpp-button)
+add_subdirectory(how-to-cpp-enum-js)
+add_subdirectory(how-to-qml)
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-button/CMakeLists.txt b/tests/auto/quick/doc/how-tos/how-to-cpp-button/CMakeLists.txt
new file mode 100644
index 0000000000..34f370c7b3
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-button/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_how-to-cpp-button LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_how-to-cpp-button
+ SOURCES
+ tst_how-to-cpp-button.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+ Qt::QuickPrivate
+ Qt::QuickControlsTestUtilsPrivate
+ Qt::QuickTemplates2Private
+)
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_qml_module(tst_how-to-cpp-button
+ URI MyModule
+ QML_FILES
+ Main.qml
+ SOURCES
+ backend.h
+ backend.cpp
+)
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-button/Main.qml b/tests/auto/quick/doc/how-tos/how-to-cpp-button/Main.qml
new file mode 100644
index 0000000000..5150f6c6d6
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-button/Main.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+import QtQuick.Controls
+
+import MyModule
+
+ApplicationWindow {
+ width: 400
+ height: 400
+ title: qsTr("C++ Button example")
+
+ Button {
+ text: qsTr("Click me")
+ onClicked: Backend.doStuff()
+ }
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-button/backend.cpp b/tests/auto/quick/doc/how-tos/how-to-cpp-button/backend.cpp
new file mode 100644
index 0000000000..4143aea9e6
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-button/backend.cpp
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+#include "backend.h"
+
+#include <QDebug>
+
+void Backend::doStuff()
+{
+ qDebug() << "Did stuff!";
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-button/backend.h b/tests/auto/quick/doc/how-tos/how-to-cpp-button/backend.h
new file mode 100644
index 0000000000..10249f5416
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-button/backend.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+#include <QObject>
+#include <QQmlEngine>
+
+class Backend : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ Q_INVOKABLE void doStuff();
+};
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-button/tst_how-to-cpp-button.cpp b/tests/auto/quick/doc/how-tos/how-to-cpp-button/tst_how-to-cpp-button.cpp
new file mode 100644
index 0000000000..792f84ffc2
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-button/tst_how-to-cpp-button.cpp
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qregularexpression.h>
+#include <QtTest/QtTest>
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuickTemplates2/private/qquickbutton_p.h>
+#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QQuickControlsTestUtils;
+
+class tst_HowToCppButton : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_HowToCppButton();
+
+private slots:
+ void example();
+};
+
+tst_HowToCppButton::tst_HowToCppButton()
+{
+}
+
+void tst_HowToCppButton::example()
+{
+ QTest::failOnWarning(QRegularExpression(QStringLiteral(".?")));
+
+ QQmlApplicationEngine engine;
+ engine.loadFromModule("MyModule", "Main");
+ QCOMPARE(engine.rootObjects().size(), 1);
+
+ auto *window = qobject_cast<QQuickWindow*>(engine.rootObjects().at(0));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *button = window->findChild<QQuickButton*>();
+ QVERIFY(button);
+ QCOMPARE(button->text(), "Click me");
+ QTest::ignoreMessage(QtDebugMsg, "Did stuff!");
+ QVERIFY(clickButton(button));
+}
+
+QT_END_NAMESPACE
+
+QTEST_MAIN(tst_HowToCppButton)
+
+#include "tst_how-to-cpp-button.moc"
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/CMakeLists.txt b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/CMakeLists.txt
new file mode 100644
index 0000000000..5adc456b4b
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/CMakeLists.txt
@@ -0,0 +1,25 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_how-to-cpp-enum-js LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_how-to-cpp-enum-js
+ SOURCES
+ backend.cpp
+ backend.h
+ tst_how-to-cpp-enum-js.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Qml
+)
+
+qt_add_resources(tst_how-to-cpp-enum-js "js"
+ PREFIX
+ /
+ FILES
+ script.mjs
+)
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/backend.cpp b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/backend.cpp
new file mode 100644
index 0000000000..a78678cc18
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/backend.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+#include "backend.h"
+
+Backend::Backend(QJSEngine *engine) :
+ mEngine(engine)
+{
+}
+
+bool Backend::load()
+{
+ // Do some loading here...
+
+ const QJSValue module = mEngine->importModule(":/script.mjs");
+ if (module.isError()) {
+ qWarning() << "Error loading script.mjs:" << module.toString();
+ return false;
+ }
+
+ const QJSValue function = module.property("backendStatusUpdate");
+ if (!function.isCallable()) {
+ qWarning() << "backendStatusUpdate script function is not callable!";
+ return false;
+ }
+
+ const QJSValue functionResult = function.call(QJSValueList() << Loaded);
+ if (functionResult.isError()) {
+ qWarning() << "backendStatusUpdate script function had errors:" << functionResult.toString();
+ return false;
+ }
+
+ return true;
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/backend.h b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/backend.h
new file mode 100644
index 0000000000..5e723d406e
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/backend.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+#include <QObject>
+#include <QJSEngine>
+
+class Backend : public QObject
+{
+ Q_OBJECT
+
+public:
+ Backend(QJSEngine *engine);
+
+ enum Status {
+ Unknown,
+ Error,
+ Loading,
+ Loaded
+ };
+
+ Q_ENUM(Status)
+
+ bool load();
+
+private:
+ QJSEngine *mEngine = nullptr;
+};
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/script.mjs b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/script.mjs
new file mode 100644
index 0000000000..a4f929222a
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/script.mjs
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+export function backendStatusUpdate(backendStatus) {
+ if (backendStatus === Backend.Error) {
+ console.warn("Error!")
+ return
+ }
+
+ console.log("Backend loaded successfully")
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/tst_how-to-cpp-enum-js.cpp b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/tst_how-to-cpp-enum-js.cpp
new file mode 100644
index 0000000000..933a1cf3e3
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-cpp-enum-js/tst_how-to-cpp-enum-js.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qregularexpression.h>
+#include <QtTest/QtTest>
+#include <QtQml/qjsengine.h>
+
+#include "backend.h"
+
+QT_BEGIN_NAMESPACE
+
+class tst_HowToCppEnumJs : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_HowToCppEnumJs();
+
+private slots:
+ void example();
+};
+
+tst_HowToCppEnumJs::tst_HowToCppEnumJs()
+{
+}
+
+void tst_HowToCppEnumJs::example()
+{
+ QTest::failOnWarning(QRegularExpression(QStringLiteral(".?")));
+
+ QJSEngine engine;
+ engine.installExtensions(QJSEngine::AllExtensions);
+
+ QJSValue backendJsMetaObject = engine.newQMetaObject(&Backend::staticMetaObject);
+ engine.globalObject().setProperty("Backend", backendJsMetaObject);
+
+ QTest::ignoreMessage(QtDebugMsg, "Backend loaded successfully");
+ Backend backend(&engine);
+ const bool loaded = backend.load();
+ QVERIFY(loaded);
+}
+
+QT_END_NAMESPACE
+
+QTEST_MAIN(tst_HowToCppEnumJs)
+
+#include "tst_how-to-cpp-enum-js.moc"
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/CMakeLists.txt b/tests/auto/quick/doc/how-tos/how-to-qml/CMakeLists.txt
new file mode 100644
index 0000000000..bdf84439c8
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_how-to-qml LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_how-to-qml
+ SOURCES
+ tst_how-to-qml.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+ Qt::QuickPrivate
+ Qt::QuickControlsTestUtilsPrivate
+ Qt::QuickTemplates2Private
+)
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_qml_module(tst_how-to-qml
+ URI HowToQml
+ QML_FILES
+ active-focus-debugging/ActiveFocusDebuggingMain.qml
+ time-picker/TimeComponentLabel.qml
+ time-picker/TimePickerDialog.qml
+ time-picker/TimePickerMain.qml
+ time-picker/TimePickerLabel.qml
+ time-picker/TimePicker.qml
+)
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/active-focus-debugging/ActiveFocusDebuggingMain.qml b/tests/auto/quick/doc/how-tos/how-to-qml/active-focus-debugging/ActiveFocusDebuggingMain.qml
new file mode 100644
index 0000000000..b0f48cdb59
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/active-focus-debugging/ActiveFocusDebuggingMain.qml
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+ visible: true
+ title: qsTr("Active focus debugging example")
+
+ onActiveFocusItemChanged: print("activeFocusItem: " + activeFocusItem)
+
+ Row {
+ TextField {
+ objectName: "textField1"
+ }
+ TextField {
+ objectName: "textField2"
+ }
+ }
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimeComponentLabel.qml b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimeComponentLabel.qml
new file mode 100644
index 0000000000..6b1653bde5
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimeComponentLabel.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+
+Label {
+ id: root
+ fontSizeMode: Label.Fit
+ horizontalAlignment: Label.AlignHCenter
+ verticalAlignment: Label.AlignVCenter
+
+ Material.foreground: Material.theme === Material.Light
+ ? Material.color(Material.Indigo, !dim ? Material.Shade500 : Material.Shade100)
+ : Material.color(Material.Indigo, dim ? Material.Shade300 : Material.Shade100)
+
+ Layout.fillHeight: true
+
+ property bool dim: false
+ property alias interactive: tapHandler.enabled
+
+ signal tapped
+
+ TapHandler {
+ id: tapHandler
+ onTapped: root.tapped()
+ }
+}
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePicker.qml b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePicker.qml
new file mode 100644
index 0000000000..edcf864784
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePicker.qml
@@ -0,0 +1,325 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+import QtQuick
+import QtQuick.Controls.Material
+
+Item {
+ id: root
+ implicitWidth: 250
+ implicitHeight: 250
+
+ enum Mode {
+ Hours,
+ Minutes
+ }
+
+ property int mode: TimePicker.Mode.Hours
+ property int hours
+ property int minutes
+ property bool is24Hour
+ property bool interactive: true
+
+ // The mode that the label delegates see, so that we can
+ // animate their opacity before their text changes.
+ property int __effectiveMode: TimePicker.Mode.Hours
+ // For 12 hour pickers, we can use 0 to 60 to represent all values.
+ property int __value: 0
+ // For 24 hour pickers, we need to store this extra flag.
+ property bool __is24HourValueSelected
+ // How many values the arm should snap to at a time.
+ readonly property int __stepSize: __getStepSize(mode)
+ readonly property int __to: 60
+ readonly property int __labelAngleStepSize: 360 / 12
+ property bool __switchingModes
+
+ // This signal could be used if TimePicker is used as a standalone component.
+ // It's emitted when the minute is selected.
+ signal accepted()
+
+ // Convenience for setting each property individually, but also
+ // ensures that the selector arm is properly rotated when there is
+ // a programmatic change in hours or minutes but not mode.
+ function openWith(mode, hours, minutes) {
+ root.mode = mode
+ root.hours = hours
+ root.minutes = minutes
+ __updateAfterModeOrTimeChange()
+ }
+
+ // Until QML gets private properties (QTBUG-11984), use the traditional
+ // double-underscore convention.
+ function __angleForValue(value: int): real {
+ return (value / __to) * 360
+ }
+
+ function __getStepSize(mode) {
+ return mode === TimePicker.Mode.Hours ? 5 : 1
+ }
+
+ function __updateAfterModeOrTimeChange() {
+ // We use a function for this rather than a binding, because we could be called before the
+ // __stepSize binding is evaluated.
+ if (mode === TimePicker.Mode.Hours) {
+ // modulo the hours value by __to because we want 60 (12) to be 0.
+ __value = hours * __getStepSize(mode) % __to
+ } else {
+ __value = minutes
+ }
+
+ __is24HourValueSelected = mode === TimePicker.Mode.Hours && hours >= 13
+ }
+
+ onModeChanged: __updateAfterModeOrTimeChange()
+
+ onIs24HourChanged: {
+ // Don't allow 24-hour values when we're not a 24-hour picker.
+ if (!is24Hour && hours > 12)
+ hours = 12
+ }
+
+ // Center dot.
+ Rectangle {
+ width: 6
+ height: 6
+ radius: width / 2
+ color: Material.primary
+ anchors.centerIn: parent
+ z: 1
+ }
+
+ Rectangle {
+ id: contentContainer
+ objectName: "contentContainer"
+ width: Math.min(parent.width, parent.height)
+ height: width
+ radius: width / 2
+ anchors.centerIn: parent
+ color: Material.theme === Material.Light ? "#eeeeee" : "#626262"
+
+ // Animate this so that we don't need an intermediate parent item for the
+ // labels to animate the opacity of that instead. That item would be required
+ // because we don't want to change the opacity of the contentContainer Rectangle.
+ property real labelOpacity: 1
+
+ function updateValueAfterPressPointChange() {
+ const y1 = height / 2
+ const x1 = width / 2
+ const y2 = tapHandler.point.position.y
+ const x2 = tapHandler.point.position.x
+ const yDistance = y2 - y1
+ const xDistance = x2 - x1
+ const angle = Math.atan2(yDistance, xDistance)
+
+ let angleInDegrees = (angle * (180 / Math.PI)) + 90.0
+ if (angleInDegrees < 0)
+ angleInDegrees = 360 + angleInDegrees
+
+ const normalisedAngle = angleInDegrees / 360.0
+ const rawValue = normalisedAngle * __to
+ // Snap to each step.
+ const steppedValue = Math.round(rawValue / __stepSize) * __stepSize
+ root.__value = steppedValue
+ // Account for the area where the angle wraps around from 360 to 0,
+ // otherwise values from 59.5 to 59.999[...] will register as 60 instead of 0.
+ if (rawValue > __to - __stepSize / 2)
+ root.__value = 0
+
+ const distanceFromCenter = Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2))
+ // Only allow selecting 24 hour values when it's in the correct mode.
+ root.__is24HourValueSelected = root.is24Hour && root.__effectiveMode === TimePicker.Mode.Hours
+ && distanceFromCenter < distanceFromCenterForLabels(true) + selectionIndicator.height * 0.5
+ }
+
+ // Returns the distance from our center at which a label should be centered over given is24Hour.
+ function distanceFromCenterForLabels(is24Hour) {
+ return contentContainer.radius - (is24Hour
+ ? selectionIndicator.height * 1.5 : selectionIndicator.height * 0.5)
+ }
+
+ states: [
+ State {
+ name: "hours"
+ when: root.mode === TimePicker.Mode.Hours
+ },
+ State {
+ name: "minutes"
+ when: root.mode === TimePicker.Mode.Minutes
+ }
+ ]
+
+ transitions: [
+ Transition {
+ // When the picker isn't interactive (e.g. when a dialog is opening),
+ // we shouldn't animate the opacity of the labels, as it looks wrong,
+ // and should only happen when switching between modes while the
+ // picker was already visible.
+ enabled: root.interactive
+
+ SequentialAnimation {
+ NumberAnimation {
+ target: contentContainer
+ property: "labelOpacity"
+ from: 1
+ to: 0
+ duration: 100
+ }
+
+ ScriptAction {
+ script: root.__effectiveMode = root.mode
+ }
+
+ NumberAnimation {
+ target: contentContainer
+ property: "labelOpacity"
+ from: 0
+ to: 1
+ duration: 100
+ }
+ }
+ },
+ Transition {
+ enabled: !root.interactive
+
+ // Since the transition above doesn't run when we're not interactive,
+ // we need to do the immediate property change here.
+ // See QTBUG-13268 for why we use a ScriptAction and not PropertyAction.
+ ScriptAction {
+ script: root.__effectiveMode = root.mode
+ }
+ }
+
+ ]
+
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ // Don't allow input while switching modes, or a click on an hour could go through to a minute.
+ enabled: root.interactive && root.__effectiveMode === root.mode
+
+ onPointChanged: {
+ if (pressed) {
+ // Don't call this when not pressed, as the position will be invalid.
+ contentContainer.updateValueAfterPressPointChange()
+
+ // Update the value (like a "live" Slider) while the pointer position changes.
+ if (mode === TimePicker.Mode.Hours) {
+ root.hours = root.__value / root.__stepSize
+
+ if (root.hours === 0) {
+ // A value of 0 (when it's not a 24-hour picker) is 12.
+ // When it is a 24-hour picker, it's 0.
+ if (!root.__is24HourValueSelected)
+ root.hours = 12
+ } else if (root.__is24HourValueSelected) {
+ root.hours += 12
+ }
+ } else {
+ root.minutes = root.__value
+ }
+ } else {
+ // Select the value that was chosen in the press code above.
+ if (mode === TimePicker.Mode.Hours) {
+ mode = TimePicker.Mode.Minutes
+ } else {
+ // Does nothing in our example, but could be used to hide the dialog
+ // if it didn't have an OK button to accept it.
+ root.accepted()
+ }
+ }
+ }
+ }
+
+ // The line connecting the center dot to the selection indicator.
+ Rectangle {
+ id: selectionArm
+ objectName: "selectionArm"
+ width: 2
+ height: contentContainer.distanceFromCenterForLabels(root.__is24HourValueSelected)
+ color: Material.primary
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.verticalCenter
+ rotation: root.__angleForValue(root.__value)
+ transformOrigin: Item.Bottom
+ antialiasing: true
+
+ Rectangle {
+ id: selectionIndicator
+ objectName: "selectionIndicator"
+ width: 40
+ height: 40
+ radius: width / 2
+ color: Material.primary
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.top
+
+ Rectangle {
+ width: 4
+ height: 4
+ radius: width / 2
+ color: Material.color(Material.Indigo, Material.Shade100)
+ anchors.centerIn: parent
+ // Only show the circle within the indicator between minute labels.
+ visible: root.__effectiveMode === TimePicker.Mode.Minutes
+ && root.__value % 5 !== 0
+ }
+ }
+ }
+
+ Repeater {
+ id: labelRepeater
+ model: root.mode === TimePicker.Mode.Hours && root.is24Hour ? 24 : 12
+ delegate: Label {
+ id: labelDelegate
+ text: displayValue
+ font.pixelSize: Qt.application.font.pixelSize * (is24HourValue ? 0.85 : 1)
+ rotation: -rotationTransform.angle
+ opacity: contentContainer.labelOpacity
+ anchors.centerIn: parent
+
+ // TODO: remove me - QTBUG-122679
+ Component.onCompleted: print("created", labelDelegate, "at index", index)
+ Component.onDestruction: print("destroyed", labelDelegate, "at index", index)
+
+ required property int index
+ // From 0 to 60.
+ readonly property int value: (index * 5) % root.__to
+ property int displayValue: root.__effectiveMode === TimePicker.Mode.Hours
+ ? index === 0
+ ? 12
+ : index === 12
+ ? 0
+ : index
+ : value
+ // The picker's current value can equal ours but we still might not be current -
+ // it depends on whether it's a 24 hour value or not.
+ readonly property bool current: root.__value === value && root.__is24HourValueSelected == is24HourValue
+ readonly property bool is24HourValue: index >= 12
+
+ Material.foreground: current
+ // When the selection arm is over us, invert our color so it's legible.
+ ? Material.color(Material.Indigo, Material.Shade100)
+ : root.Material.theme === Material.Light
+ ? is24HourValue ? "#686868" : "#484848"
+ : Material.color(Material.Indigo, is24HourValue ? Material.Shade300 : Material.Shade100)
+
+ transform: [
+ Translate {
+ // We're already centered in our parent, so we can use this function
+ // to determine our position, which doesn't need to be aware of our height -
+ // it just needs to tell us where our center position should be.
+ y: -contentContainer.distanceFromCenterForLabels(labelDelegate.is24HourValue)
+ },
+ Rotation {
+ id: rotationTransform
+ angle: root.__angleForValue(labelDelegate.value)
+ origin.x: labelDelegate.width / 2
+ origin.y: labelDelegate.height / 2
+ }
+ ]
+ }
+ }
+ }
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerDialog.qml b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerDialog.qml
new file mode 100644
index 0000000000..fa02234eb8
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerDialog.qml
@@ -0,0 +1,88 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+
+Dialog {
+ id: root
+ standardButtons: Dialog.Ok | Dialog.Cancel
+ closePolicy: Dialog.NoAutoClose
+ // We don't want so much space between the picker and dialog buttons.
+ bottomPadding: 8
+
+ property int hours: 12
+ property int minutes: 0
+ property alias is24Hour: timePicker.is24Hour
+
+ property int __initialHours
+ property int __initialMinutes
+
+ signal timeAccepted
+ signal timeRejected
+
+ function openWithMode(mode) {
+ timePicker.openWith(mode !== undefined ? mode : TimePicker.Mode.Hours, hours, minutes)
+
+ __initialHours = hours
+ __initialMinutes = minutes
+
+ open()
+ }
+
+ onAccepted: {
+ root.hours = timePicker.hours
+ root.minutes = timePicker.minutes
+ root.timeAccepted()
+ }
+ onRejected: {
+ hours = __initialHours
+ minutes = __initialMinutes
+ // Also reset the picker's time so that the onIs24HourChanged handler below works as expected.
+ timePicker.hours = __initialHours
+ timePicker.minutes = __initialMinutes
+ root.timeRejected()
+ }
+
+ // If is24Hour changes programmatically (only while we're not open),
+ // make sure we adapt to any possible clamping it did in the transition from 24 hours to 12.
+ onIs24HourChanged: {
+ if (!opened)
+ root.hours = timePicker.hours
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 12
+
+ TimePickerLabel {
+ id: timeLabel
+ // Use TimePicker's time, because that is updated live, whereas our values
+ // are only changed once we've been accepted.
+ time: new Date(1970, 1, 1, timePicker.hours, timePicker.minutes)
+ hoursActive: timePicker.mode === TimePicker.Mode.Hours
+ showAmPm: !timePicker.is24Hour
+
+ Layout.fillWidth: true
+ // Push us down a bit so we're not so close to the top of the dialog.
+ Layout.topMargin: 8
+
+ onHoursSelected: timePicker.mode = TimePicker.Mode.Hours
+ onMinutesSelected: timePicker.mode = TimePicker.Mode.Minutes
+ }
+
+ TimePicker {
+ id: timePicker
+ objectName: "timePicker"
+ // Our TapHandler may handle the click event on the Label if we don't do this,
+ // causing an hour to be inadvertently selected.
+ interactive: root.opened
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerLabel.qml b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerLabel.qml
new file mode 100644
index 0000000000..66b651c0c5
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerLabel.qml
@@ -0,0 +1,126 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+
+Item {
+ id: root
+ // Use TextMetrics' boundingRect.width rather than a RowLayout's implicitWidth
+ // because the latter can cause TimePickerDialog to jump around when the label text changes.
+ implicitWidth: fullTextMetrics.boundingRect.width + amPmLayout.implicitWidth + 80
+ implicitHeight: fullTextMetrics.boundingRect.height
+
+ property var time
+ property bool am: true
+ property bool showAmPm: true
+
+ property bool hoursActive: true
+
+ property int fontPixelSize: Qt.application.font.pixelSize * 4
+
+ signal hoursSelected
+ signal minutesSelected
+ signal amSelected
+ signal pmSelected
+
+ TextMetrics {
+ id: fullTextMetrics
+ font: hoursLabel.font
+ text: "99:99"
+ }
+
+ TextMetrics {
+ id: hourTextMetrics
+ font.family: hoursLabel.font.family
+ font.pixelSize: hoursLabel.fontInfo.pixelSize
+ text: "99"
+ }
+
+ TimeComponentLabel {
+ id: hoursLabel
+ objectName: "hoursLabel"
+ text: Qt.formatTime(root.time, "hh")
+ font.pixelSize: root.fontPixelSize
+ // Avoid any jumping around by using a dedicated TextMetrics object
+ // for our label too, and position ourselves based on its width.
+ x: colonLabel.x - hourTextMetrics.boundingRect.width - 4
+ anchors.verticalCenter: parent.verticalCenter
+ dim: !root.hoursActive
+
+ onTapped: root.hoursSelected()
+ }
+
+ TimeComponentLabel {
+ id: colonLabel
+ text: ":"
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ font.pixelSize: root.fontPixelSize
+ dim: true
+ }
+
+ TimeComponentLabel {
+ id: minutesLabel
+ objectName: "minutesLabel"
+ text: Qt.formatTime(root.time, "mm")
+ font.pixelSize: root.fontPixelSize
+ anchors.left: colonLabel.right
+ anchors.leftMargin: 4
+ anchors.verticalCenter: parent.verticalCenter
+ dim: root.hoursActive
+
+ onTapped: root.minutesSelected()
+ }
+
+ ColumnLayout {
+ id: amPmLayout
+ visible: root.showAmPm
+ // We also need to avoid the AM/PM label jumping around,
+ // so rather than anchor it to the right of the minutes label,
+ // we use colonLabel's horizontal center (which never changes), and fullTextMetrics.
+ anchors.left: colonLabel.horizontalCenter
+ anchors.leftMargin: fullTextMetrics.boundingRect.width / 2 + 12
+ anchors.verticalCenter: minutesLabel.verticalCenter
+
+ spacing: 0
+
+ ToolButton {
+ objectName: "amButton"
+ text: qsTr("AM")
+ autoExclusive: true
+
+ Material.foreground: Material.color(Material.Indigo,
+ root.am ? Material.Shade500 : Material.Shade100)
+
+ // Set the size explicitly to ensure that the buttons aren't too large.
+ // We also add 1 to the width because we want square ripple effects, not round.
+ Layout.preferredWidth: (implicitWidth * 0.7) + 1
+ Layout.preferredHeight: (implicitHeight * 0.7)
+
+ onClicked: {
+ root.am = true
+ root.amSelected()
+ }
+ }
+ ToolButton {
+ objectName: "pmButton"
+ text: qsTr("PM")
+ autoExclusive: true
+
+ Material.foreground: Material.color(Material.Indigo,
+ !root.am ? Material.Shade500 : Material.Shade100)
+
+ Layout.preferredWidth: (implicitWidth * 0.7) + 1
+ Layout.preferredHeight: (implicitHeight * 0.7)
+
+ onClicked: {
+ root.am = false
+ root.pmSelected()
+ }
+ }
+ }
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerMain.qml b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerMain.qml
new file mode 100644
index 0000000000..c7c304613d
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/time-picker/TimePickerMain.qml
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//! [file]
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+
+ApplicationWindow {
+ id: window
+ width: 600
+ height: 600
+ visible: true
+ title: qsTr("Time Picker Example")
+
+ Material.theme: darkThemeSwitch.checked ? Material.Dark : Material.Light
+
+ // Shows the selected time and opens the dialog.
+ TimeComponentLabel {
+ id: openDialogLabel
+ width: parent.width - 80
+ anchors.centerIn: parent
+ font.pixelSize: Qt.application.font.pixelSize * 8
+ renderTypeQuality: Text.VeryHighRenderTypeQuality
+ interactive: !timePickerDialog.opened
+
+ text: Qt.formatTime(new Date(1970, 1, 1, timePickerDialog.hours, timePickerDialog.minutes), "hh:mm")
+
+ onTapped: timePickerDialog.openWithMode(TimePicker.Mode.Hours)
+ }
+
+ ColumnLayout {
+ // We always want the openDialogLabel to be centered in the window, not us.
+ // For that reason, we use anchors rather than putting the root items into a ColumnLayout.
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: openDialogLabel.bottom
+ anchors.topMargin: 24
+ spacing: 12
+
+ Switch {
+ id: is24HourSwitch
+ text: qsTr("24 Hour")
+ checked: timePickerDialog.is24Hour
+ }
+ Switch {
+ id: darkThemeSwitch
+ text: qsTr("Dark")
+ }
+ }
+
+ TimePickerDialog {
+ id: timePickerDialog
+ anchors.centerIn: parent
+ is24Hour: is24HourSwitch.checked
+
+ onTimeAccepted: print("A time was chosen - do something here!")
+ }
+}
+//! [file]
diff --git a/tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp b/tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp
new file mode 100644
index 0000000000..66dcb208fc
--- /dev/null
+++ b/tests/auto/quick/doc/how-tos/how-to-qml/tst_how-to-qml.cpp
@@ -0,0 +1,368 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qregularexpression.h>
+#include <QtTest/QtTest>
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/private/qquickrepeater_p.h>
+#include <QtQuickTemplates2/private/qquickdialog_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>
+#include <QtQuickTemplates2/private/qquicklabel_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
+#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
+#include <QtQuickControlsTestUtils/private/dialogstestutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QQuickVisualTestUtils;
+using namespace QQuickControlsTestUtils;
+using namespace QQuickDialogTestUtils;
+
+// Allows us to use test macros outside of test functions.
+#define RETURN_IF_FAILED(expression) \
+expression; \
+if (QTest::currentTestFailed()) \
+ return
+
+class tst_HowToQml : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_HowToQml();
+
+private slots:
+ void init();
+ void activeFocusDebugging();
+ void timePicker();
+
+private:
+ QScopedPointer<QPointingDevice> touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
+};
+
+tst_HowToQml::tst_HowToQml()
+{
+ qputenv("QML_NO_TOUCH_COMPRESSION", "1");
+}
+
+void tst_HowToQml::init()
+{
+// QTest::failOnWarning(QRegularExpression(QStringLiteral(".?")));
+}
+
+void tst_HowToQml::activeFocusDebugging()
+{
+ QQmlApplicationEngine engine;
+ engine.loadFromModule("HowToQml", "ActiveFocusDebuggingMain");
+ QCOMPARE(engine.rootObjects().size(), 1);
+
+ auto *window = qobject_cast<QQuickWindow*>(engine.rootObjects().at(0));
+ window->show();
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("activeFocusItem: .*\"ActiveFocusDebuggingMain\""));
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("activeFocusItem: .*\"textField1\""));
+ auto *textField1 = window->findChild<QQuickTextField*>("textField1");
+ QVERIFY(textField1);
+ textField1->forceActiveFocus();
+ QVERIFY(textField1->hasActiveFocus());
+
+ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("activeFocusItem: .*\"textField2\""));
+ auto *textField2 = window->findChild<QQuickTextField*>("textField2");
+ QVERIFY(textField2);
+ QTest::keyClick(window, Qt::Key_Tab);
+ QVERIFY(textField2->hasActiveFocus());
+}
+
+void tst_HowToQml::timePicker()
+{
+ QQmlApplicationEngine engine;
+ engine.loadFromModule("HowToQml", "TimePickerMain");
+ QCOMPARE(engine.rootObjects().size(), 1);
+
+ auto *window = qobject_cast<QQuickWindow*>(engine.rootObjects().at(0));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *dialog = window->findChild<QQuickDialog *>();
+ QVERIFY(dialog);
+
+ auto *timePicker = dialog->findChild<QQuickItem *>("timePicker");
+ QVERIFY(timePicker);
+
+ auto *contentContainer = timePicker->findChild<QQuickItem *>("contentContainer");
+ QVERIFY(contentContainer);
+ const int contentRadius = contentContainer->property("radius").toReal();
+
+ auto *labelRepeater = timePicker->findChild<QQuickRepeater *>();
+ QVERIFY(labelRepeater);
+
+ auto *selectionArm = timePicker->findChild<QQuickItem *>("selectionArm");
+ QVERIFY(selectionArm);
+
+ auto *selectionIndicator = selectionArm->findChild<QQuickItem *>("selectionIndicator");
+ QVERIFY(selectionIndicator);
+ const int selectionIndicatorHeight = selectionIndicator->height();
+
+ auto angleForValue = [](int value) -> int {
+ return int((value / 60.0) * 360) % 360;
+ };
+
+ // Note that is24HourValue should be true if "value" is a 24-hour value,
+ // not if the picker's is24Hour property is true.
+ auto labelCenterPosForValue = [&](int value, bool is24HourValue = false) -> QPoint {
+ if (value < 0 || value > 60)
+ return {};
+
+ const qreal angle = angleForValue(value);
+
+ QTransform transform;
+ // Translate to the center.
+ transform.translate(contentRadius, contentRadius);
+ // Rotate to the correct angle.
+ transform.rotate(angle);
+ // Go outward.
+ const int labelDistance = is24HourValue ? selectionIndicatorHeight * 1.5 : selectionIndicatorHeight * 0.5;
+ transform.translate(0, -contentRadius + labelDistance);
+
+ const auto centerPos = transform.map(QPoint(0, 0));
+ return centerPos;
+ };
+
+ enum Mode {
+ Hours,
+ Minutes
+ };
+
+ const int valuesPerLabelStep = 5;
+ const bool TwelveHour = false;
+ const bool TwentyFourHour = true;
+
+ // Checks that all the labels are in their expected positions and that they have the correct text.
+ auto verifyLabels = [&](Mode expectedMode, bool is24Hour, int callerLineNumber) {
+ // When not in 24 hour mode, there are always 12 labels, regardless of whether it's showing hours or minutes.
+ const int expectedLabelCount = expectedMode == Hours && is24Hour ? 24 : 12;
+ QCOMPARE(labelRepeater->count(), expectedLabelCount);
+ for (int i = 0; i < expectedLabelCount; ++i) {
+ auto *labelDelegate = labelRepeater->itemAt(i);
+ QVERIFY2(labelDelegate, qPrintable(QString::fromLatin1("Expected valid label delegate item at index %1 (caller line %2)")
+ .arg(i).arg(callerLineNumber)));
+ // Use the waiting variant of the macro because there are opacity animations.
+ // TODO: is this causing the failure on line 224?
+ QTRY_VERIFY2(qFuzzyCompare(labelDelegate->opacity(), 1.0), qPrintable(QString::fromLatin1(
+ "Expected opacity of label delegate %1 at index %2 to be 1 but it's %3 (caller line %4) - QTBUG-118056: actual label delegate at this index is now %5")
+ .arg(QDebug::toString(labelDelegate)).arg(i).arg(labelDelegate->opacity()).arg(callerLineNumber).arg(QDebug::toString(labelRepeater->itemAt(i)))));
+
+ const int expectedValue = (i * valuesPerLabelStep) % 60;
+ const int actualValue = labelDelegate->property("value").toInt();
+ QVERIFY2(expectedValue == actualValue, qPrintable(QString::fromLatin1(
+ "Expected label's value at index %1 to be %2 but it's %3 (caller line %4)")
+ .arg(i).arg(expectedValue).arg(actualValue).arg(callerLineNumber)));
+
+ const QString expectedText = QString::number(expectedMode == Hours
+ ? (i == 0 ? 12 : (i == 12 ? 0 : i)) : i * valuesPerLabelStep);
+ // Use QTRY_VERIFY2 rather than QVERIFY2, because text changes are animated.
+ QTRY_VERIFY2(expectedText == labelDelegate->property("text").toString(), qPrintable(QString::fromLatin1(
+ "Expected label's text at index %1 to be %2 but it's %3 (caller line %4)").arg(i)
+ .arg(expectedText, labelDelegate->property("text").toString(), QString::number(callerLineNumber))));
+ }
+ };
+
+ auto verifySelectionIndicator = [&](int expectedValue, bool expect24HourValue, int callerLineNumber) {
+ const qreal actualRotation = int(selectionArm->rotation()) % 360;
+ const qreal expectedRotation = angleForValue(expectedValue);
+ QVERIFY2(qFuzzyCompare(actualRotation, expectedRotation), qPrintable(QString::fromLatin1(
+ "Expected selection arm's rotation to be %1 for expectedValue %2 but it's %3 (caller line %4)")
+ .arg(expectedRotation).arg(expectedValue).arg(actualRotation).arg(callerLineNumber)));
+
+ const QPoint expectedIndicatorCenterPos = labelCenterPosForValue(expectedValue, expect24HourValue);
+ const QPoint actualIndicatorCenterPos = selectionIndicator->mapToItem(
+ contentContainer, selectionIndicator->boundingRect().center().toPoint()).toPoint();
+ const QPoint difference = actualIndicatorCenterPos - expectedIndicatorCenterPos;
+ QVERIFY2(difference.x() <= 2 && difference.y() <= 2, qPrintable(QString::fromLatin1(
+ "Expected selection indicator's center position to be %1 (with 2 pixels of tolerance) but it's %2 (caller line %3)")
+ .arg(QDebug::toString(expectedIndicatorCenterPos), QDebug::toString(actualIndicatorCenterPos)).arg(callerLineNumber)));
+ };
+
+ auto valueForHour = [&](int hour) {
+ return (hour * valuesPerLabelStep) % 60;
+ };
+
+ // Open the picker to hours mode by clicking on the label.
+ auto *openDialogLabel = window->findChild<QQuickLabel *>(QString(), Qt::FindDirectChildrenOnly);
+ QVERIFY(openDialogLabel);
+ QCOMPARE(openDialogLabel->text(), "12:00");
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapCenterToWindow(openDialogLabel));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapCenterToWindow(openDialogLabel));
+ QTRY_COMPARE(dialog->property("opened").toBool(), true);
+ // It should show hours.
+ RETURN_IF_FAILED(verifyLabels(Hours, TwelveHour, __LINE__));
+ RETURN_IF_FAILED(verifySelectionIndicator(0, TwelveHour, __LINE__));
+
+ // Select the 3rd hour.
+ const QPoint thirdHourPos = labelCenterPosForValue(valueForHour(3));
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapToWindow(contentContainer, thirdHourPos));
+ RETURN_IF_FAILED(verifySelectionIndicator(valueForHour(3), TwelveHour, __LINE__));
+ QCOMPARE(timePicker->property("mode").toInt(), Hours);
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapToWindow(contentContainer, thirdHourPos));
+ QCOMPARE(timePicker->property("hours").toInt(), 3);
+ QCOMPARE(timePicker->property("minutes").toInt(), 0);
+ // The dialog's values shouldn't change until the dialog has been accepted.
+ QCOMPARE(dialog->property("hours").toInt(), 12);
+ QCOMPARE(dialog->property("minutes").toInt(), 0);
+ // It should be showing minutes now.
+ QCOMPARE(timePicker->property("mode").toInt(), Minutes);
+ RETURN_IF_FAILED(verifyLabels(Minutes, TwelveHour, __LINE__));
+ RETURN_IF_FAILED(verifySelectionIndicator(0, TwelveHour, __LINE__));
+ auto *minutesLabel = dialog->findChild<QQuickLabel *>("minutesLabel");
+ QVERIFY(minutesLabel);
+ QCOMPARE(minutesLabel->property("dim").toBool(), false);
+
+ // Select the 59th minute.
+ const QPoint fiftyNinthMinutePos = labelCenterPosForValue(59);
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapToWindow(contentContainer, fiftyNinthMinutePos));
+ RETURN_IF_FAILED(verifySelectionIndicator(59, TwelveHour, __LINE__));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapToWindow(contentContainer, fiftyNinthMinutePos));
+ QCOMPARE(timePicker->property("hours").toInt(), 3);
+ QCOMPARE(timePicker->property("minutes").toInt(), 59);
+ QCOMPARE(dialog->property("hours").toInt(), 12);
+ QCOMPARE(dialog->property("minutes").toInt(), 0);
+ // It shouldn't be closed until the OK or Cancel buttons are clicked.
+ QCOMPARE(dialog->property("opened").toBool(), true);
+
+ // Accept the dialog to make the changes.
+ auto *dialogButtonBox = qobject_cast<QQuickDialogButtonBox *>(dialog->footer());
+ QVERIFY(dialogButtonBox);
+ auto *okButton = findDialogButton(dialogButtonBox, "OK");
+ QTest::ignoreMessage(QtDebugMsg, "A time was chosen - do something here!");
+ QVERIFY(clickButton(okButton));
+ QTRY_COMPARE(dialog->property("visible").toBool(), false);
+ QCOMPARE(dialog->property("hours").toInt(), 3);
+ QCOMPARE(dialog->property("minutes").toInt(), 59);
+ QCOMPARE(openDialogLabel->text(), "03:59");
+
+ // Open it again.
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapCenterToWindow(openDialogLabel));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapCenterToWindow(openDialogLabel));
+ QTRY_COMPARE(dialog->property("opened"), true);
+ RETURN_IF_FAILED(verifyLabels(Hours, TwelveHour, __LINE__));
+ RETURN_IF_FAILED(verifySelectionIndicator(valueForHour(3), TwelveHour, __LINE__));
+ // The time label should be unchanged.
+ QCOMPARE(openDialogLabel->text(), "03:59");
+
+ // Switch from hours to minutes by clicking on the minutes label.
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapCenterToWindow(minutesLabel));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapCenterToWindow(minutesLabel));
+ RETURN_IF_FAILED(verifyLabels(Minutes, TwelveHour, __LINE__));
+ RETURN_IF_FAILED(verifySelectionIndicator(59, TwelveHour, __LINE__));
+
+ // Select the 1st minute.
+ const QPoint firstMinutePos = labelCenterPosForValue(1);
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapToWindow(contentContainer, firstMinutePos));
+ RETURN_IF_FAILED(verifySelectionIndicator(1, TwelveHour, __LINE__));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapToWindow(contentContainer, firstMinutePos));
+ QCOMPARE(timePicker->property("hours").toInt(), 3);
+ QCOMPARE(timePicker->property("minutes").toInt(), 1);
+ // It shouldn't be closed until the OK or Cancel buttons are clicked.
+ QCOMPARE(dialog->property("opened").toBool(), true);
+
+ // Accept the dialog to make the changes.
+ QTest::ignoreMessage(QtDebugMsg, "A time was chosen - do something here!");
+ QVERIFY(clickButton(okButton));
+ QTRY_COMPARE(dialog->property("visible").toBool(), false);
+ QCOMPARE(dialog->property("hours").toInt(), 3);
+ QCOMPARE(dialog->property("minutes").toInt(), 1);
+ QCOMPARE(openDialogLabel->text(), "03:01");
+
+ // Check that hours and minutes set programmatically on the picker and dialog are respected.
+ QVERIFY(dialog->setProperty("hours", QVariant::fromValue(7)));
+ QVERIFY(dialog->setProperty("minutes", QVariant::fromValue(8)));
+ QCOMPARE(openDialogLabel->text(), "07:08");
+ // Open the picker to hours mode by clicking on the label.
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapCenterToWindow(openDialogLabel));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapCenterToWindow(openDialogLabel));
+ QTRY_COMPARE(dialog->property("opened").toBool(), true);
+ RETURN_IF_FAILED(verifyLabels(Hours, TwelveHour, __LINE__));
+ RETURN_IF_FAILED(verifySelectionIndicator(valueForHour(7), TwelveHour, __LINE__));
+ QCOMPARE(timePicker->property("hours").toInt(), 7);
+ QCOMPARE(timePicker->property("minutes").toInt(), 8);
+
+ // Check that cancelling the dialog cancels any changes.
+ // Select the fourth hour.
+ const QPoint fourthHourPos = labelCenterPosForValue(20);
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapToWindow(contentContainer, fourthHourPos));
+ RETURN_IF_FAILED(verifySelectionIndicator(valueForHour(4), TwelveHour, __LINE__));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapToWindow(contentContainer, fourthHourPos));
+ QCOMPARE(timePicker->property("hours").toInt(), 4);
+ QCOMPARE(timePicker->property("minutes").toInt(), 8);
+ auto *cancelButton = findDialogButton(dialogButtonBox, "Cancel");
+ QVERIFY(clickButton(cancelButton));
+ QTRY_COMPARE(dialog->property("visible").toBool(), false);
+ QCOMPARE(dialog->property("hours").toInt(), 7);
+ QCOMPARE(dialog->property("minutes").toInt(), 8);
+
+ // Test that the 24 hour mode works.
+ const bool isCi = qgetenv("QTEST_ENVIRONMENT") == "ci"; // QTBUG-122679
+ if (isCi)
+ qDebug() << "about to set is24hour to true";
+ QVERIFY(dialog->setProperty("is24Hour", QVariant::fromValue(true)));
+ if (isCi)
+ qDebug() << "about to open picker";
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapCenterToWindow(openDialogLabel));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapCenterToWindow(openDialogLabel));
+ QTRY_COMPARE(dialog->property("opened").toBool(), true);
+ QCOMPARE(timePicker->property("mode").toInt(), Hours);
+ if (isCi)
+ qDebug() << "about to verify labels after switching to 24hr";
+ RETURN_IF_FAILED(verifyLabels(Hours, TwentyFourHour, __LINE__));
+ // TwelveHour because the selected value (7) is on the 12 hour "ring".
+ RETURN_IF_FAILED(verifySelectionIndicator(valueForHour(7), TwelveHour, __LINE__));
+ // It should still show the old time.
+ QCOMPARE(timePicker->property("hours").toInt(), 7);
+ QCOMPARE(timePicker->property("minutes").toInt(), 8);
+ QCOMPARE(dialog->property("hours").toInt(), 7);
+ QCOMPARE(dialog->property("minutes").toInt(), 8);
+
+ // Select the 23rd hour.
+ const QPoint twentyThirdHourPos = labelCenterPosForValue(valueForHour(11), TwentyFourHour);
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapToWindow(contentContainer, twentyThirdHourPos));
+ RETURN_IF_FAILED(verifySelectionIndicator(valueForHour(23), TwentyFourHour, __LINE__));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapToWindow(contentContainer, twentyThirdHourPos));
+ QCOMPARE(timePicker->property("hours").toInt(), 23);
+ QCOMPARE(timePicker->property("minutes").toInt(), 8);
+ QCOMPARE(dialog->property("hours").toInt(), 7);
+ QCOMPARE(dialog->property("minutes").toInt(), 8);
+ RETURN_IF_FAILED(verifyLabels(Minutes, TwentyFourHour, __LINE__));
+ RETURN_IF_FAILED(verifySelectionIndicator(8, TwelveHour, __LINE__));
+
+ // Select the 20th minute.
+ const QPoint twentiethMinutePos = labelCenterPosForValue(20);
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapToWindow(contentContainer, twentiethMinutePos));
+ RETURN_IF_FAILED(verifySelectionIndicator(20, TwelveHour, __LINE__));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapToWindow(contentContainer, twentiethMinutePos));
+ QCOMPARE(timePicker->property("hours").toInt(), 23);
+ QCOMPARE(timePicker->property("minutes").toInt(), 20);
+
+ // Go back to hours and make sure that the selection indicator is correct.
+ auto *hoursLabel = dialog->findChild<QQuickLabel *>("hoursLabel");
+ QVERIFY(hoursLabel);
+ QTest::touchEvent(window, touchScreen.data()).press(0, mapCenterToWindow(hoursLabel));
+ QTest::touchEvent(window, touchScreen.data()).release(0, mapCenterToWindow(hoursLabel));
+ RETURN_IF_FAILED(verifyLabels(Hours, TwentyFourHour, __LINE__));
+ RETURN_IF_FAILED(verifySelectionIndicator(valueForHour(23), TwentyFourHour, __LINE__));
+
+ // Accept.
+ QTest::ignoreMessage(QtDebugMsg, "A time was chosen - do something here!");
+ QVERIFY(clickButton(okButton));
+ QTRY_COMPARE(dialog->property("visible").toBool(), false);
+ QCOMPARE(dialog->property("hours").toInt(), 23);
+ QCOMPARE(dialog->property("minutes").toInt(), 20);
+}
+
+QT_END_NAMESPACE
+
+QTEST_MAIN(tst_HowToQml)
+
+#include "tst_how-to-qml.moc"
diff --git a/tests/auto/quick/drawingmodes/CMakeLists.txt b/tests/auto/quick/drawingmodes/CMakeLists.txt
index 1d680c7531..09f53f1037 100644
--- a/tests/auto/quick/drawingmodes/CMakeLists.txt
+++ b/tests/auto/quick/drawingmodes/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_drawingmodes Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_drawingmodes LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp b/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp
index 9cf3601a06..a68be8a745 100644
--- a/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp
+++ b/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
@@ -133,7 +133,7 @@ void tst_drawingmodes::points()
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
QSKIP("Skipping points test due to unexpected failures in M1 CI VM");
#endif
diff --git a/tests/auto/quick/examples/CMakeLists.txt b/tests/auto/quick/examples/CMakeLists.txt
index 4646fb82ca..74fe0fba51 100644
--- a/tests/auto/quick/examples/CMakeLists.txt
+++ b/tests/auto/quick/examples/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
# Generated from examples.pro.
@@ -7,6 +7,12 @@
## tst_examples Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_quick_examples LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_quick_examples
SOURCES
tst_examples.cpp
diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp
index de27fe92cb..4962f6b759 100644
--- a/tests/auto/quick/examples/tst_examples.cpp
+++ b/tests/auto/quick/examples/tst_examples.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QLibraryInfo>
@@ -54,7 +54,6 @@ tst_examples::tst_examples()
// Add directories you want excluded here
excludedDirs << "shared"; //Not an example
excludedDirs << "snippets/qml/path"; //No root QQuickItem
- excludedDirs << "examples/qml/qmlextensionplugins"; //Requires special import search path
// These snippets are not expected to run on their own.
excludedDirs << "snippets/qml/visualdatamodel_rootindex";
@@ -69,11 +68,9 @@ tst_examples::tst_examples()
#if !QT_CONFIG(opengl)
//No support for Particles
- excludedFiles << "examples/qml/dynamicscene/dynamicscene.qml";
excludedFiles << "examples/quick/animation/basics/color-animation.qml";
excludedFiles << "examples/quick/particles/affectors/content/age.qml";
- excludedFiles << "examples/quick/touchinteraction/multipointtouch/bearwhack.qml";
- excludedFiles << "examples/quick/touchinteraction/multipointtouch/multiflame.qml";
+ excludedFiles << "examples/quick/pointerhandlers/multiflame.qml";
excludedDirs << "examples/quick/particles";
// No Support for ShaderEffect
excludedFiles << "src/quick/doc/snippets/qml/animators.qml";
@@ -109,21 +106,19 @@ void tst_examples::namingConvention(const QDir &d)
return;
}
- QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
- QDir::Files);
+ const QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), QDir::Files);
bool seenQml = !files.isEmpty();
bool seenLowercase = false;
- foreach (const QString &file, files) {
+ for (const QString &file : files) {
if (file.at(0).isLower())
seenLowercase = true;
}
if (!seenQml) {
- QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
- QDir::NoSymLinks);
- foreach (const QString &dir, dirs) {
+ const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
+ for (const QString &dir : dirs) {
QDir sub = d;
sub.cd(dir);
namingConvention(sub);
@@ -144,11 +139,12 @@ void tst_examples::namingConvention(const QDir &d)
void tst_examples::namingConvention()
{
- QStringList examplesLocations;
- examplesLocations << QLibraryInfo::path(QLibraryInfo::ExamplesPath) + QLatin1String("/qml");
- examplesLocations << QLibraryInfo::path(QLibraryInfo::ExamplesPath) + QLatin1String("/quick");
+ const QStringList examplesLocations = {
+ QLibraryInfo::path(QLibraryInfo::ExamplesPath) + QLatin1String("/qml"),
+ QLibraryInfo::path(QLibraryInfo::ExamplesPath) + QLatin1String("/quick")
+ };
- foreach(const QString &examples, examplesLocations) {
+ for (const QString &examples : examplesLocations) {
QDir d(examples);
if (d.exists())
namingConvention(d);
@@ -167,9 +163,8 @@ QStringList tst_examples::findQmlFiles(const QDir &d)
QStringList cppfiles = d.entryList(QStringList() << QLatin1String("*.cpp"), QDir::Files);
if (cppfiles.isEmpty()) {
- QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
- QDir::Files);
- foreach (const QString &file, files) {
+ const QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), QDir::Files);
+ for (const QString &file : files) {
if (file.at(0).isLower()) {
bool superContinue = false;
for (int ii = 0; ii < excludedFiles.size(); ++ii) {
@@ -187,9 +182,8 @@ QStringList tst_examples::findQmlFiles(const QDir &d)
}
- QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
- QDir::NoSymLinks);
- foreach (const QString &dir, dirs) {
+ const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
+ for (const QString &dir : dirs) {
QDir sub = d;
sub.cd(dir);
rv << findQmlFiles(sub);
diff --git a/tests/auto/quick/geometry/CMakeLists.txt b/tests/auto/quick/geometry/CMakeLists.txt
index 5094c48e39..8231608a7f 100644
--- a/tests/auto/quick/geometry/CMakeLists.txt
+++ b/tests/auto/quick/geometry/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_geometry Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_geometry LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_geometry
SOURCES
tst_geometry.cpp
diff --git a/tests/auto/quick/geometry/tst_geometry.cpp b/tests/auto/quick/geometry/tst_geometry.cpp
index 08ef3c34d7..f3034977e5 100644
--- a/tests/auto/quick/geometry/tst_geometry.cpp
+++ b/tests/auto/quick/geometry/tst_geometry.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QString>
#include <QtTest/QtTest>
diff --git a/tests/auto/quick/nodes/CMakeLists.txt b/tests/auto/quick/nodes/CMakeLists.txt
index 85ed32e4dd..415179b79d 100644
--- a/tests/auto/quick/nodes/CMakeLists.txt
+++ b/tests/auto/quick/nodes/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_nodestest Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_nodestest LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_nodestest
SOURCES
tst_nodestest.cpp
diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp
index 5cec3034d8..d91654a6f6 100644
--- a/tests/auto/quick/nodes/tst_nodestest.cpp
+++ b/tests/auto/quick/nodes/tst_nodestest.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QString>
#include <QtTest/QtTest>
@@ -18,28 +18,23 @@
#include <QtGui/qpa/qplatformintegration.h>
#include <QtGui/qoffscreensurface.h>
-#include <QtGui/private/qrhi_p.h>
-#include <QtGui/private/qrhinull_p.h>
+#include <rhi/qrhi.h>
#if QT_CONFIG(opengl)
# include <QOpenGLContext>
-# include <QtGui/private/qrhigles2_p.h>
# define TST_GL
#endif
#if QT_CONFIG(vulkan)
# include <QVulkanInstance>
-# include <QtGui/private/qrhivulkan_p.h>
# define TST_VK
#endif
#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
# define TST_D3D11
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-# include <QtGui/private/qrhimetal_p.h>
+#if QT_CONFIG(metal)
# define TST_MTL
#endif
diff --git a/tests/auto/quick/nokeywords/CMakeLists.txt b/tests/auto/quick/nokeywords/CMakeLists.txt
index 947c1e72de..44e371ab3a 100644
--- a/tests/auto/quick/nokeywords/CMakeLists.txt
+++ b/tests/auto/quick/nokeywords/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_nokeywords Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_nokeywords LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_nokeywords
SOURCES
tst_nokeywords.cpp
diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
index 80350477fd..190c9fac34 100644
--- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp
+++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define QT_NO_KEYWORDS
#undef signals
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
index 5c5f2c9ed9..f5259a9e64 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST
@@ -5,8 +5,6 @@ opensuse-leap
windows gcc
[touchDragFlickableBehindButton]
windows gcc
-[touchDragSliderAndFlickable]
-* # QTBUG-86729
# QTBUG-95887
[mouseDragSlider]
opensuse-leap
@@ -26,3 +24,4 @@ android
android
[touchAndDragHandlerOnFlickable]
android
+
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt
index b3c93bf804..3836aefbb2 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_flickableinterop Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_flickableinterop LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml
index e3f15f399f..9483a12d1c 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml
index e4b2fb512d..ec790c9b99 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml
index 0afd397a62..cba135269f 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml
index 7f3045595d..99b53e6afb 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml
index a253465a78..f6748da19c 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml
index 224172b9d9..562dc156f9 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml
index 2cbb40e416..bbd5f5b278 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml
index 5468b5d98c..740b698fd4 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml
new file mode 100644
index 0000000000..e594f165b2
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Flickable {
+ id: root
+ width: 800
+ height: 480
+ contentWidth: 1000
+ contentHeight: 600
+
+ Rectangle {
+ id: pinchable
+ objectName: "pinchable"
+ border.color: "black"
+ color: pinch.active ? "salmon" : "peachpuff"
+ x: 100
+ y: 100
+ width: 200
+ height: 200
+ radius: 80
+ PinchHandler {
+ id: pinch
+ }
+ PointHandler {
+ id: p1
+ target: Rectangle {
+ parent: pinchable
+ color: "green"
+ visible: p1.active
+ x: p1.point.position.x - width / 2
+ y: p1.point.position.y - height / 2
+ width: 9; height: width; radius: width / 2
+ }
+ }
+ PointHandler {
+ id: p0
+ target: Rectangle {
+ parent: pinchable
+ color: "red"
+ visible: p0.active
+ x: p0.point.position.x - width / 2
+ y: p0.point.position.y - height / 2
+ width: 9; height: width; radius: width / 2
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml
new file mode 100644
index 0000000000..2660952f16
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml
@@ -0,0 +1,24 @@
+import QtQuick
+
+Rectangle {
+ width: 320
+ height: 320
+
+ TapHandler {
+ onTapped: color = "tomato"
+ }
+
+ Flickable {
+ anchors.fill: parent
+ contentWidth: content.width
+ contentHeight: content.height
+ Rectangle {
+ id: content
+ objectName: "pinchable"
+ width: 150
+ height: 150
+ color: "wheat"
+ PinchHandler {}
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml
index 6c2854b28d..dc32b1d82c 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml
index 99f3c2a98b..f4c0e5daaa 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml
index baa6d99cd2..0293ad03b0 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
index 05b764029a..790c7c771e 100644
--- a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
+++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -11,6 +11,7 @@
#include <QtQuick/private/qquickitemview_p.h>
#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquickdraghandler_p.h>
+#include <QtQuick/private/qquickpinchhandler_p.h>
#include <QtQuick/private/qquicktaphandler_p.h>
#include <QtQuick/private/qquicktableview_p.h>
#include <qpa/qwindowsysteminterface.h>
@@ -31,7 +32,6 @@ class tst_FlickableInterop : public QQmlDataTest
public:
tst_FlickableInterop()
: QQmlDataTest(QT_QMLTEST_DATADIR)
- , touchDevice(QTest::createTouchDevice())
{}
private slots:
@@ -55,10 +55,15 @@ private slots:
void touchDragSliderAndFlickable();
void touchAndDragHandlerOnFlickable_data();
void touchAndDragHandlerOnFlickable();
+ void pinchHandlerOnFlickable();
+ void nativeGesturePinchOnFlickableWithParentTapHandler_data();
+ void nativeGesturePinchOnFlickableWithParentTapHandler();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
- QPointingDevice *touchDevice;
+ QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
+ QScopedPointer<QPointingDevice> touchpad = QScopedPointer<QPointingDevice>(
+ QTest::createTouchDevice(QInputDevice::DeviceType::TouchPad));
};
void tst_FlickableInterop::createView(QScopedPointer<QQuickView> &window, const char *fileName)
@@ -97,24 +102,24 @@ void tst_FlickableInterop::touchTapButton()
// Button changes pressed state and emits tapped on release
QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint();
- QTest::touchEvent(window, touchDevice).press(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).press(1, p1, window);
QQuickTouchUtils::flush(window);
QTRY_VERIFY(button->property("pressed").toBool());
- QTest::touchEvent(window, touchDevice).release(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).release(1, p1, window);
QQuickTouchUtils::flush(window);
QTRY_VERIFY(!button->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 1);
// We can drag <= dragThreshold and the button still acts normal, Flickable doesn't grab
p1 = button->mapToScene(QPointF(20, 20)).toPoint();
- QTest::touchEvent(window, touchDevice).press(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).press(1, p1, window);
QQuickTouchUtils::flush(window);
QTRY_VERIFY(button->property("pressed").toBool());
p1 += QPoint(dragThreshold, 0);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(button->property("pressed").toBool());
- QTest::touchEvent(window, touchDevice).release(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).release(1, p1, window);
QQuickTouchUtils::flush(window);
QTRY_VERIFY(!button->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 2);
@@ -145,11 +150,11 @@ void tst_FlickableInterop::touchDragFlickableBehindButton()
tappedSpy.clear();
QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint();
- QTest::touchEvent(window, touchDevice).press(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).press(1, p1, window);
QQuickTouchUtils::flush(window);
QTRY_VERIFY(button->property("pressed").toBool());
p1 += QPoint(dragThreshold, 0);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(button->property("pressed").toBool());
int i = 0;
@@ -157,14 +162,14 @@ void tst_FlickableInterop::touchDragFlickableBehindButton()
// Button is no longer pressed because Flickable steals the grab
for (; i < 100 && !flickable->isMoving(); ++i) {
p1 += QPoint(1, 0);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
}
qCDebug(lcPointerTests) << "flickable started moving after" << i << "moves, when we got to" << p1;
QVERIFY(flickable->isMoving());
QCOMPARE(i, 2);
QVERIFY(!button->property("pressed").toBool());
- QTest::touchEvent(window, touchDevice).release(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).release(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(!button->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 0);
@@ -241,7 +246,7 @@ void tst_FlickableInterop::mouseDragFlickableBehindButton()
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
QTRY_VERIFY(button->property("pressed").toBool());
p1 += QPoint(dragThreshold, 0);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QVERIFY(button->property("pressed").toBool());
int i = 0;
for (; i < 100 && !flickable->isMoving(); ++i) {
@@ -278,19 +283,19 @@ void tst_FlickableInterop::touchDragSlider()
// Drag the slider in the allowed (vertical) direction
tappedSpy.clear();
QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint() - QPoint(0, 8);
- QTest::touchEvent(window, touchDevice).press(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).press(1, p1, window);
QQuickTouchUtils::flush(window);
QTRY_VERIFY(slider->property("pressed").toBool());
p1 += QPoint(0, dragThreshold);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(slider->property("pressed").toBool());
QCOMPARE(slider->property("value").toInt(), 49);
p1 += QPoint(0, 1);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
p1 += QPoint(0, 10);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(slider->property("value").toInt() < 49);
QVERIFY(!flickable->isMoving());
@@ -299,12 +304,12 @@ void tst_FlickableInterop::touchDragSlider()
// Now that the DragHandler is active, the Flickable will not steal the grab
// even if we move a large distance horizontally
p1 += QPoint(dragThreshold * 2, 0);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(!flickable->isMoving());
// Release, and do not expect the tapped signal
- QTest::touchEvent(window, touchDevice).release(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).release(1, p1, window);
QQuickTouchUtils::flush(window);
QCOMPARE(tappedSpy.size(), 0);
QCOMPARE(translationChangedSpy.size(), 1);
@@ -417,24 +422,24 @@ void tst_FlickableInterop::touchDragFlickableBehindSlider()
// because Flickable steals the grab
tappedSpy.clear();
QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint();
- QTest::touchEvent(window, touchDevice).press(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).press(1, p1, window);
QQuickTouchUtils::flush(window);
QTRY_VERIFY(slider->property("pressed").toBool());
p1 += QPoint(dragThreshold, 0);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(slider->property("pressed").toBool());
int i = 0;
for (; i < 100 && !flickable->isMoving(); ++i) {
p1 += QPoint(1, 0);
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
}
qCDebug(lcPointerTests) << "flickable started moving after" << i << "moves, when we got to" << p1;
QVERIFY(flickable->isMoving());
QCOMPARE(i, 2);
QVERIFY(!slider->property("pressed").toBool());
- QTest::touchEvent(window, touchDevice).release(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).release(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(!slider->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 0);
@@ -508,19 +513,19 @@ void tst_FlickableInterop::touchDragFlickableBehindItemWithHandlers()
QPoint p1 = rect->mapToScene(rect->clipRect().center()).toPoint();
QPoint originP1 = p1;
- QTest::touchEvent(window, touchDevice).press(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).press(1, p1, window);
QQuickTouchUtils::flush(window);
for (int i = 0; i < dragThreshold * 3; ++i) {
p1 = originP1;
p1.rx() += i;
- QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).move(1, p1, window);
QQuickTouchUtils::flush(window);
}
QCOMPARE(flickable->isMoving(), expectedFlickableMoving);
if (!expectedFlickableMoving) {
QVERIFY(rect->mapToScene(rect->clipRect().center()).toPoint().x() > originP1.x());
}
- QTest::touchEvent(window, touchDevice).release(1, p1, window);
+ QTest::touchEvent(window, touchDevice.get()).release(1, p1, window);
QQuickTouchUtils::flush(window);
}
@@ -585,7 +590,7 @@ void tst_FlickableInterop::touchDragSliderAndFlickable()
QVERIFY(knob);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>();
QVERIFY(flickable);
- QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice, false);
+ QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice.get(), false);
// The knob is initially centered over the slider's "groove"
qreal initialXOffset = qAbs(knob->mapToScene(knob->clipRect().center()).x() - slider->mapToScene
@@ -623,7 +628,10 @@ void tst_FlickableInterop::touchDragSliderAndFlickable()
QQuickTouchUtils::flush(window);
qCDebug(lcPointerTests) << "step" << i << ": fingers @" << p1 << p2 << "is Flickable moving yet?" << flickable->isMoving();
}
- QVERIFY(flickable->isMoving());
+ // In Qt 6, Flickable doesn't see the second touchpoint, so it doesn't move.
+ // One way to see this is that Flickable is more immune to stray touches than it otherwise would be.
+ // But time will tell if we are missing out on something useful, which was possible in Qt 5 (QTBUG-123490).
+ QCOMPARE(flickable->isMoving(), false);
qreal knobSliderXOffset = qAbs(knob->mapToScene(knob->clipRect().center()).toPoint().x() -
slider->mapToScene(slider->clipRect().center()).toPoint().x()) - initialXOffset;
if (knobSliderXOffset > 1)
@@ -715,7 +723,7 @@ void tst_FlickableInterop::touchAndDragHandlerOnFlickable()
}
// Drag one finger on the Flickable (between delegates) and make sure it flicks
- QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice, false);
+ QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice.get(), false);
QPoint p1(780, 460);
if (delegate)
p1 = delegate->mapToScene(delegate->clipRect().bottomRight()).toPoint() + QPoint(-1, 1);
@@ -782,6 +790,187 @@ void tst_FlickableInterop::touchAndDragHandlerOnFlickable()
touchSeq.release(1, p1, window).commit();
}
+void tst_FlickableInterop::pinchHandlerOnFlickable()
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("pinchOnFlickable.qml")));
+ QQuickFlickable *flickable = qmlobject_cast<QQuickFlickable*>(window.rootObject());
+ QVERIFY(flickable);
+ QQuickPointerHandler *pinchHandler = flickable->findChild<QQuickPointerHandler*>();
+ QVERIFY(pinchHandler);
+ QQuickItem *pinchable = pinchHandler->target();
+ QVERIFY(pinchable);
+
+ QSignalSpy flickMoveSpy(flickable, &QQuickFlickable::movementStarted);
+ QSignalSpy grabChangedSpy(touchDevice.get(), &QPointingDevice::grabChanged);
+
+ QObject *grabber = nullptr;
+ connect(touchDevice.get(), &QPointingDevice::grabChanged,
+ [&grabber](QObject *g, QPointingDevice::GrabTransition transition, const QPointerEvent *, const QEventPoint &) {
+ if (transition == QPointingDevice::GrabTransition::GrabExclusive)
+ grabber = g;
+ });
+
+ QPoint p0 = pinchable->mapToScene({50, 100}).toPoint();
+ QPoint p1 = pinchable->mapToScene({150, 100}).toPoint();
+ QTest::QTouchEventSequence touch = QTest::touchEvent(&window, touchDevice.get());
+
+ touch.press(0, p0, &window).press(1, p1, &window).commit();
+ QQuickTouchUtils::flush(&window);
+ int activeStep = -1;
+ int grabTransitionCount = 0;
+ // drag two fingers down: PinchHandler moves the item; Flickable doesn't grab, because there are 2 points
+ for (int i = 0; i < 4; ++i) {
+ p0 += QPoint(0, dragThreshold);
+ p1 += QPoint(0, dragThreshold);
+ touch.move(0, p0, &window).move(1, p1, &window).commit();
+ QQuickTouchUtils::flush(&window);
+ if (pinchHandler->active() && activeStep < 0) {
+ qCDebug(lcPointerTests) << "pinch began at step" << i;
+ activeStep = i;
+ QCOMPARE(grabber, pinchHandler);
+ grabTransitionCount = grabChangedSpy.count();
+ }
+ }
+ QVERIFY(pinchHandler->active());
+ QCOMPARE(grabChangedSpy.count(), grabTransitionCount);
+ QCOMPARE(grabber, pinchHandler);
+ qreal scale = pinchable->scale();
+ QCOMPARE(scale, 1);
+ qreal rot = pinchable->rotation();
+ QCOMPARE(rot, 0);
+ // start expanding and rotating
+ for (int i = 0; i < 4; ++i) {
+ p0 += QPoint(-5, 10);
+ p1 += QPoint(5, -10);
+ touch.move(0, p0, &window).move(1, p1, &window).commit();
+ QQuickTouchUtils::flush(&window);
+ QVERIFY(pinchHandler->active());
+ // PinchHandler keeps grab: no more transitions
+ QCOMPARE(grabChangedSpy.count(), grabTransitionCount);
+ QCOMPARE(grabber, pinchHandler);
+ QTRY_COMPARE_GT(pinchable->scale(), scale);
+ scale = pinchable->scale();
+ QCOMPARE_LT(pinchable->rotation(), rot);
+ rot = pinchable->rotation();
+ }
+ touch.release(0, p0, &window).release(1, p1, &window).commit();
+ QQuickTouchUtils::flush(&window);
+ QTRY_COMPARE(pinchHandler->active(), false);
+ QCOMPARE(flickMoveSpy.count(), 0); // Flickable never moved
+}
+
+void tst_FlickableInterop::nativeGesturePinchOnFlickableWithParentTapHandler_data()
+{
+ QTest::addColumn<const QPointingDevice*>("device");
+ QTest::addColumn<Qt::MouseButton>("button");
+ QTest::addColumn<Qt::NativeGestureType>("gesture");
+ QTest::addColumn<qreal>("value");
+ QTest::addColumn<qreal>("expectedPropertyValue");
+
+ const QPointingDevice *constTouchPad = touchpad.data();
+
+ QTest::newRow("touchpad: left and rotate") << constTouchPad << Qt::LeftButton << Qt::RotateNativeGesture << 5.0 << 10.0;
+ QTest::newRow("touchpad: right and rotate") << constTouchPad << Qt::RightButton << Qt::RotateNativeGesture << 5.0 << 10.0;
+ QTest::newRow("touchpad: left and scale") << constTouchPad << Qt::LeftButton << Qt::ZoomNativeGesture << 0.1 << 1.21;
+ QTest::newRow("touchpad: right and scale") << constTouchPad << Qt::RightButton << Qt::ZoomNativeGesture << 0.1 << 1.21;
+
+ const auto *mouse = QPointingDevice::primaryPointingDevice();
+ if (mouse->type() == QInputDevice::DeviceType::Mouse) {
+ QTest::newRow("mouse: left and rotate") << mouse << Qt::LeftButton << Qt::RotateNativeGesture << 5.0 << 10.0;
+ QTest::newRow("mouse: right and rotate") << mouse << Qt::RightButton << Qt::RotateNativeGesture << 5.0 << 10.0;
+ QTest::newRow("mouse: left and scale") << mouse << Qt::LeftButton << Qt::ZoomNativeGesture << 0.1 << 1.21;
+ QTest::newRow("mouse: right and scale") << mouse << Qt::RightButton << Qt::ZoomNativeGesture << 0.1 << 1.21;
+ } else {
+ qCWarning(lcPointerTests) << "skipping mouse tests: primary device is not a mouse" << mouse;
+ }
+}
+
+void tst_FlickableInterop::nativeGesturePinchOnFlickableWithParentTapHandler()
+{
+ QFETCH(const QPointingDevice*, device);
+ QFETCH(Qt::MouseButton, button);
+ QFETCH(Qt::NativeGestureType, gesture);
+ QFETCH(qreal, value);
+ QFETCH(qreal, expectedPropertyValue);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("pinchOnFlickableWithParentTapHandler.qml")));
+ QQuickFlickable *flickable = window.rootObject()->findChild<QQuickFlickable*>();
+ QVERIFY(flickable);
+ QQuickPointerHandler *pinchHandler = flickable->findChild<QQuickPinchHandler*>();
+ QVERIFY(pinchHandler);
+ QQuickItem *pinchable = pinchHandler->target();
+ QVERIFY(pinchable);
+ QQuickTapHandler *tapHandler = window.rootObject()->findChild<QQuickTapHandler*>();
+ QVERIFY(tapHandler);
+ const bool expectTap = button & tapHandler->acceptedButtons();
+
+ QSignalSpy flickMoveSpy(flickable, &QQuickFlickable::movementStarted);
+ QSignalSpy grabChangedSpy(touchDevice.get(), &QPointingDevice::grabChanged);
+ QSignalSpy tapActiveSpy(tapHandler, &QQuickTapHandler::activeChanged);
+ QSignalSpy tapSpy(tapHandler, &QQuickTapHandler::tapped);
+
+ QObject *grabber = nullptr;
+ connect(device, &QPointingDevice::grabChanged,
+ [&grabber](QObject *g, QPointingDevice::GrabTransition transition, const QPointerEvent *, const QEventPoint &) {
+ if (transition == QPointingDevice::GrabTransition::GrabExclusive)
+ grabber = g;
+ });
+
+ const QPoint pinchPos(75, 75);
+ const QPoint outsidePos(200, 200);
+
+ // move to position
+ QTest::mouseMove(&window, pinchPos);
+
+ // pinch via native gesture
+ ulong ts = 502; // after the mouse move, which is at time 501 in practice
+ QWindowSystemInterface::handleGestureEvent(&window, ts++, touchpad.get(),
+ Qt::BeginNativeGesture, pinchPos, pinchPos);
+ if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
+ for (int i = 0; i < 2; ++i) {
+ QWindowSystemInterface::handleGestureEventWithRealValue(&window, ts++, touchpad.get(),
+ gesture, value, pinchPos, pinchPos);
+ }
+ if (gesture == Qt::RotateNativeGesture)
+ QTRY_COMPARE(pinchHandler->parentItem()->rotation(), expectedPropertyValue);
+ else if (gesture == Qt::ZoomNativeGesture)
+ QTRY_COMPARE(pinchHandler->parentItem()->scale(), expectedPropertyValue);
+ QVERIFY(pinchHandler->active());
+ QCOMPARE(grabChangedSpy.count(), 0);
+ QCOMPARE(grabber, nullptr);
+ if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
+ QWindowSystemInterface::handleGestureEvent(&window, ts++, touchpad.get(),
+ Qt::EndNativeGesture, pinchPos, pinchPos);
+
+ // tap in square: TapHandler detects tap iff acceptedButtons permits
+ // TODO delay; unfortunately this also begins at timestamp 502 because we don't have testlib
+ // functions to send gesture events, and QQuickTest::pointerPress() doesn't take a delay value
+ QQuickTest::pointerPress(device, &window, 0, pinchPos, button);
+ QCOMPARE(tapHandler->point().id(), expectTap ? 0 : -1);
+ QQuickTest::pointerRelease(device, &window, 0, pinchPos, button);
+ if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
+ QCOMPARE(tapSpy.size() != 0, expectTap);
+ QCOMPARE(tapActiveSpy.size(), 0);
+ QCOMPARE(tapHandler->point().id(), -1); // does not keep tracking after release
+
+ // move outside: nothing should happen;
+ // but QTBUG-108896 happened because TapHandler was setting pointInfo to track this moving point
+ QQuickTest::pointerMove(device, &window, 0, outsidePos);
+ QCOMPARE(tapHandler->point().id(), -1); // does not track after mouse move
+
+ // tap outside: nothing happens
+ tapSpy.clear();
+ tapActiveSpy.clear();
+ QQuickTest::pointerPress(device, &window, 0, outsidePos, button);
+ QQuickTest::pointerRelease(device, &window, 0, outsidePos, button);
+ if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
+ QCOMPARE(tapSpy.size(), 0);
+ QCOMPARE(tapActiveSpy.size(), 0);
+}
+
QTEST_MAIN(tst_FlickableInterop)
#include "tst_flickableinterop.moc"
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt
index f66a7e8d0d..22123e6385 100644
--- a/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_mousearea_interop Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_mousearea_interop LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml
index 846c31cc61..1bde433dba 100644
--- a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
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 3c288cbb3b..b1a480b9cf 100644
--- a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -16,6 +16,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QtCore/qpointer.h>
+
Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
class tst_MouseAreaInterop : public QQmlDataTest
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST
index 3f01d3a7d4..80d6dd0ee8 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST
@@ -1,2 +1,3 @@
-[touchesThenPinch]
-* # QTBUG-86729
+# QTBUG-118062
+[touchDrag]
+opensuse-leap
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt
index b0843f726b..9f19e8b427 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_multipointtoucharea_interop Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_multipointtoucharea_interop LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml
index e6ebfdd552..ad4494eb25 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml
index b6fad6fade..199292e015 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
@@ -35,8 +35,8 @@ Rectangle {
Item {
id: crosshairs
property TouchPoint touchPoint
- x: touchPoint.x - width / 2
- y: touchPoint.y - height / 2
+ x: touchPoint?.x - width / 2
+ y: touchPoint?.y - height / 2
width: 300; height: 300
visible: touchPoint.pressed
rotation: touchPoint.rotation
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml
index e8b8fe769e..868a5265a6 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
index ab64f6bb24..a4cc182422 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -15,6 +15,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QtCore/qpointer.h>
+
Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
class tst_MptaInterop : public QQmlDataTest
@@ -250,7 +252,7 @@ void tst_MptaInterop::touchesThenPinch()
}
qCDebug(lcPointerTests) << "drag started after" << dragTookGrab
<< "moves; ended with translation" << drag->activeTranslation();
- QCOMPARE(devPriv->pointById(1)->exclusiveGrabber, drag);
+ QCOMPARE(devPriv->pointById(2)->exclusiveGrabber, drag);
QTRY_VERIFY(drag->activeTranslation().x() > 0);
touch.release(2, p2).commit();
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt
index 06cc34441a..23087c1d48 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickdraghandler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickdraghandler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml
index 7a305e7b79..065b0aaed8 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml
index e3f15f399f..9483a12d1c 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml
index 502891cd1b..200e846207 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml
index 1041cd4f07..47be6052ad 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml
index 7a4dc3a69a..5d700cdd08 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml
index 146881e0a4..ea71da5623 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml
index e805adbcb8..6c7a25c148 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml
index 0a3d3618a8..30b28ac9e8 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index 9d6e4409d0..15df656b93 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -17,6 +17,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QtCore/qpointer.h>
+
Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
class tst_DragHandler : public QQmlDataTest
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST
index 1c4499753a..c0d73ff05f 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST
@@ -3,3 +3,6 @@ macos # Can't move cursor (QTBUG-76312)
# QTBUG-103065
[movingItemWithHoverHandler]
android
+[window]
+opensuse-leap # QTBUG-122405
+
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt
index 25c8dfd604..a2e7d640d5 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickhoverhandler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickhoverhandler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml
new file mode 100644
index 0000000000..42b658a4d4
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+
+Rectangle {
+ id: brownRect
+ objectName: "brownRect"
+
+ width: 400
+ height: 400
+
+ HoverHandler {
+ id: hh
+ cursorShape: parent.colorIndex == 0 ?
+ Qt.CrossCursor :
+ Qt.OpenHandCursor
+ }
+
+ property list<color> colors: ["beige", "brown"]
+ property int colorIndex: 0
+
+ color: colors[colorIndex]
+
+ Timer {
+ id: colorTimer
+ interval: 200
+ running: true
+ repeat: true
+
+ onTriggered: {
+ parent.colorIndex = (parent.colorIndex + 1) % parent.colors.length;
+ parent.color = parent.colors[parent.colorIndex];
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml
index edb56ffdc6..48e130a35e 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml
@@ -3,47 +3,53 @@ import QtQuick
Item {
width: 200; height: 200
- HoverHandler {
- objectName: "stylus"
- acceptedDevices: PointerDevice.Stylus
- acceptedPointerTypes: PointerDevice.Pen
- cursorShape: Qt.CrossCursor
- }
+ Rectangle {
+ width: 100; height: 100
+ anchors.centerIn: parent
+ border.color: "black"
- HoverHandler {
- objectName: "stylus eraser"
- acceptedDevices: PointerDevice.Stylus
- acceptedPointerTypes: PointerDevice.Eraser
- cursorShape: Qt.PointingHandCursor
- }
+ HoverHandler {
+ objectName: "stylus"
+ acceptedDevices: PointerDevice.Stylus
+ acceptedPointerTypes: PointerDevice.Pen
+ cursorShape: Qt.CrossCursor
+ }
- HoverHandler {
- objectName: "airbrush"
- acceptedDevices: PointerDevice.Airbrush
- acceptedPointerTypes: PointerDevice.Pen
- cursorShape: Qt.BusyCursor
- }
+ HoverHandler {
+ objectName: "stylus eraser"
+ acceptedDevices: PointerDevice.Stylus
+ acceptedPointerTypes: PointerDevice.Eraser
+ cursorShape: Qt.PointingHandCursor
+ }
- HoverHandler {
- objectName: "airbrush eraser"
- acceptedDevices: PointerDevice.Airbrush
- acceptedPointerTypes: PointerDevice.Eraser
- cursorShape: Qt.OpenHandCursor
- }
+ HoverHandler {
+ objectName: "airbrush"
+ acceptedDevices: PointerDevice.Airbrush
+ acceptedPointerTypes: PointerDevice.Pen
+ cursorShape: Qt.BusyCursor
+ }
- HoverHandler {
- objectName: "mouse"
- acceptedDevices: PointerDevice.Mouse
- // acceptedPointerTypes can be omitted because Mouse is not ambiguous.
- // When a genuine mouse move is sent, there's a conflict, and this one should win.
- cursorShape: Qt.IBeamCursor
- }
+ HoverHandler {
+ objectName: "airbrush eraser"
+ acceptedDevices: PointerDevice.Airbrush
+ acceptedPointerTypes: PointerDevice.Eraser
+ cursorShape: Qt.OpenHandCursor
+ }
+
+ HoverHandler {
+ objectName: "mouse"
+ acceptedDevices: PointerDevice.Mouse
+ // acceptedPointerTypes can be omitted because Mouse is not ambiguous.
+ // When a genuine mouse move is sent, there's a conflict, and this one should win.
+ cursorShape: Qt.IBeamCursor
+ }
- HoverHandler {
- objectName: "conflictingMouse"
- acceptedDevices: PointerDevice.Mouse
- // acceptedPointerTypes can be omitted because Mouse is not ambiguous.
- // When a genuine mouse move is sent, there's a conflict, and this one should lose.
- cursorShape: Qt.ClosedHandCursor
+ HoverHandler {
+ objectName: "conflictingMouse"
+ acceptedDevices: PointerDevice.Mouse
+ // acceptedPointerTypes can be omitted because Mouse is not ambiguous.
+ // When a genuine mouse move is sent, there's a conflict, and this one should lose.
+ cursorShape: Qt.ClosedHandCursor
+ }
}
}
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml
new file mode 100644
index 0000000000..60dfc53c40
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml
@@ -0,0 +1,17 @@
+import QtQuick
+
+Item {
+ width: 320
+ height: 240
+
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: hh.hovered ? "lightsteelblue" : "beige"
+
+ HoverHandler {
+ id: hh
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml
index 43d9827ad7..ca30a7fe99 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
index e488c0486f..0569fed472 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -48,9 +48,13 @@ private slots:
void deviceCursor();
void addHandlerFromCpp();
void ensureHoverHandlerWorksWhenItemHasHoverDisabled();
+ void changeCursor();
+ void touchDrag();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
+
+ QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
void tst_HoverHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName)
@@ -567,6 +571,17 @@ void tst_HoverHandler::deviceCursor()
QCOMPARE(eraserHandler->isHovered(), false);
QCOMPARE(aibrushHandler->isHovered(), false);
QCOMPARE(airbrushEraserHandler->isHovered(), true); // there was no fresh QTabletEvent to tell it not to be hovered
+
+ // hover with the stylus again, then move the mouse outside the handlers' parent item
+ testStylusDevice(QInputDevice::DeviceType::Stylus, QPointingDevice::PointerType::Pen,
+ Qt::CrossCursor, stylusHandler);
+ QTest::mouseMove(&window, QPoint(180, 180));
+ // the mouse has left the item: all its HoverHandlers should be unhovered (QTBUG-116505)
+ QCOMPARE(stylusHandler->isHovered(), false);
+ QCOMPARE(eraserHandler->isHovered(), false);
+ QCOMPARE(aibrushHandler->isHovered(), false);
+ QCOMPARE(airbrushEraserHandler->isHovered(), false);
+ QCOMPARE(mouseHandler->isHovered(), false);
}
void tst_HoverHandler::addHandlerFromCpp()
@@ -671,6 +686,74 @@ void tst_HoverHandler::ensureHoverHandlerWorksWhenItemHasHoverDisabled()
QCOMPARE(spy.size(), 2);
}
+void tst_HoverHandler::changeCursor()
+{
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "changingCursor.qml");
+ QQuickView * window = windowPtr.data();
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickItem *item = window->findChild<QQuickItem *>("brownRect");
+ QVERIFY(item);
+ QQuickHoverHandler *hh = item->findChild<QQuickHoverHandler *>();
+ QVERIFY(hh);
+
+ QPoint itemCenter(item->mapToScene(QPointF(item->width() / 2, item->height() / 2)).toPoint());
+ QSignalSpy hoveredSpy(hh, SIGNAL(hoveredChanged()));
+
+ QTest::mouseMove(window, itemCenter);
+
+ QTRY_COMPARE(hoveredSpy.size(), 1);
+
+#if QT_CONFIG(cursor)
+ QTRY_COMPARE(window->cursor().shape(), Qt::CrossCursor);
+ QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor);
+ QTRY_COMPARE(window->cursor().shape(), Qt::CrossCursor);
+ QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor);
+#endif
+}
+
+void tst_HoverHandler::touchDrag()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("hoverHandler.qml")));
+ const QQuickItem *root = window.rootObject();
+ QQuickHoverHandler *handler = root->findChild<QQuickHoverHandler *>();
+ QVERIFY(handler);
+
+ // polishAndSync() calls flushFrameSynchronousEvents() before emitting afterAnimating()
+ QSignalSpy frameSyncSpy(&window, &QQuickWindow::afterAnimating);
+
+ const QPoint out(root->width() - 1, root->height() / 2);
+ QPoint in(root->width() / 2, root->height() / 2);
+
+ QTest::touchEvent(&window, touchscreen.get()).press(0, out, &window);
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(handler->isHovered(), false);
+
+ frameSyncSpy.clear();
+ QTest::touchEvent(&window, touchscreen.get()).move(0, in, &window);
+ QQuickTouchUtils::flush(&window);
+ QTRY_COMPARE(handler->isHovered(), true);
+ QCOMPARE(handler->point().scenePosition(), in);
+
+ in += {10, 10};
+ QTest::touchEvent(&window, touchscreen.get()).move(0, in, &window);
+ QQuickTouchUtils::flush(&window);
+ // ensure that the color change is visible
+ QTRY_COMPARE_GE(frameSyncSpy.size(), 1);
+ QCOMPARE(handler->isHovered(), true);
+ QCOMPARE(handler->point().scenePosition(), in);
+
+ QTest::touchEvent(&window, touchscreen.get()).move(0, out, &window);
+ QQuickTouchUtils::flush(&window);
+ QTRY_COMPARE_GE(frameSyncSpy.size(), 2);
+ QCOMPARE(handler->isHovered(), false);
+
+ QTest::touchEvent(&window, touchscreen.get()).release(0, out, &window);
+}
+
QTEST_MAIN(tst_HoverHandler)
#include "tst_qquickhoverhandler.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt
index f16dfa3421..1334607ab2 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpinchhandler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpinchhandler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml
index a348938aca..9d9903fc0e 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml
index 70c105836f..ddd63ec720 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml
index 37f22c949a..2b9b3eb156 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
@@ -35,7 +35,14 @@ Rectangle {
}
}
- Text { color: "magenta"; z: 1; text: "scale: " + blackRect.scale}
+ Text {
+ color: "magenta"
+ z: 1
+ text: "scale: " + blackRect.scale +
+ "\npos: " + blackRect.x.toFixed(2) + ", " + blackRect.y.toFixed(2) +
+ "\ntranslation: active " + pincharea.activeTranslation.x.toFixed(2) + ", " + pincharea.activeTranslation.y.toFixed(2) +
+ "\n persistent " + pincharea.persistentTranslation.x.toFixed(2) + ", " + pincharea.persistentTranslation.y.toFixed(2)
+ }
Rectangle {
id: blackRect
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml
index dbebf92933..ed9220f99e 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml
index 56fa8c9f8e..f25d0e9f38 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
index a61cbd76db..78483c5bdb 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
@@ -34,6 +34,8 @@ private slots:
void scaleThreeFingers();
void scaleNativeGesture_data();
void scaleNativeGesture();
+ void cumulativeNativeGestures_data();
+ void cumulativeNativeGestures();
void pan();
void dragAxesEnabled_data();
void dragAxesEnabled();
@@ -43,8 +45,8 @@ private slots:
void transformedpinchHandler();
private:
- QPointingDevice *touchscreen = QTest::createTouchDevice();
- QPointingDevice *touchpad = QTest::createTouchDevice(QInputDevice::DeviceType::TouchPad);
+ QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
+ QScopedPointer<QPointingDevice> touchpad = QScopedPointer<QPointingDevice>(QTest::createTouchDevice(QInputDevice::DeviceType::TouchPad));
};
void tst_QQuickPinchHandler::cleanupTestCase()
@@ -230,13 +232,13 @@ void tst_QQuickPinchHandler::scale()
QVERIFY(pinchHandler != nullptr);
QQuickItem *blackRect = (hasTarget ? pinchHandler->target() : pinchHandler->parentItem());
QVERIFY(blackRect != nullptr);
- QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint)));
+ QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint)));
QSignalSpy scaleChangedSpy(pinchHandler, &QQuickPinchHandler::scaleChanged);
if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
QPoint p0(80, 80);
QPoint p1(100, 100);
- QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&window, touchscreen);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&window, touchscreen.get());
pinchSequence.press(0, p0, &window).commit();
QQuickTouchUtils::flush(&window);
// In order for the stationary point to remember its previous position,
@@ -383,7 +385,7 @@ void tst_QQuickPinchHandler::scaleThreeFingers()
QPoint p1(220, 80);
QPoint p2(150, 220);
{
- QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get());
pinchSequence.press(0, p0, window).commit();
QQuickTouchUtils::flush(window);
// In order for the stationary point to remember its previous position,
@@ -488,10 +490,10 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
// so as to compensate for the change in size, to hold the centroid in place
const QPointF expectedPos = targetPos + QPointF( (pinchPos.x() - target->x()) * (expectedScale - 1),
(pinchPos.y() - target->y()) * (expectedScale - 1) );
- QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad,
+ QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(),
Qt::BeginNativeGesture, pinchPos, pinchPos);
if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
- QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad,
+ QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad.get(),
Qt::ZoomNativeGesture, scale - 1, pinchPos, pinchPos);
if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
QTRY_COMPARE(target->scale(), expectedScale);
@@ -509,7 +511,7 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
QCOMPARE(pinchHandler->activeRotation(), 0);
QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), 0);
QCOMPARE(pinchHandler->rotationAxis()->activeValue(), 0);
- QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad,
+ QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(),
Qt::EndNativeGesture, pinchPos, pinchPos);
QTRY_COMPARE(pinchHandler->active(), false);
QCOMPARE(target->scale(), expectedScale);
@@ -528,9 +530,9 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
const qreal reverseScale = (1 / expectedScale);
pinchPos = QPointF(110, 110);
pinchLocalPos = target->mapFromScene(pinchPos);
- QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad,
+ QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(),
Qt::BeginNativeGesture, pinchPos, pinchPos);
- QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad,
+ QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad.get(),
Qt::ZoomNativeGesture, reverseScale - 1, pinchPos, pinchPos);
QTRY_COMPARE(target->scale(), 1);
QCOMPARE(pinchHandler->active(), true);
@@ -541,7 +543,7 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
QCOMPARE(pinchHandler->persistentScale(), 1);
QCOMPARE(pinchHandler->activeScale(), reverseScale);
QCOMPARE(pinchHandler->scaleAxis()->activeValue(), reverseScale);
- QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad,
+ QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(),
Qt::EndNativeGesture, pinchPos, pinchPos);
QTRY_COMPARE(pinchHandler->active(), false);
QCOMPARE(target->scale(), 1);
@@ -550,6 +552,127 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1);
}
+void tst_QQuickPinchHandler::cumulativeNativeGestures_data()
+{
+ QTest::addColumn<const QPointingDevice*>("device");
+ QTest::addColumn<Qt::NativeGestureType>("gesture");
+ QTest::addColumn<qreal>("value");
+ QTest::addColumn<QList<QPoint>>("expectedTargetTranslations");
+
+ const auto *touchpadDevice = touchpad.get();
+ const auto *mouse = QPointingDevice::primaryPointingDevice();
+
+ QTest::newRow("touchpad: rotate") << touchpadDevice << Qt::RotateNativeGesture << 5.0
+ << QList<QPoint>{{-2, 2}, {-5, 4}, {-7, 6}, {-10, 7}};
+ QTest::newRow("touchpad: scale") << touchpadDevice << Qt::ZoomNativeGesture << 0.1
+ << QList<QPoint>{{3, 3}, {5, 5}, {8, 8}, {12, 12}};
+ if (mouse->type() == QInputDevice::DeviceType::Mouse) {
+ QTest::newRow("mouse: rotate") << mouse << Qt::RotateNativeGesture << 5.0
+ << QList<QPoint>{{-2, 2}, {-5, 4}, {-7, 6}, {-10, 7}};
+ QTest::newRow("mouse: scale") << mouse << Qt::ZoomNativeGesture << 0.1
+ << QList<QPoint>{{3, 3}, {5, 5}, {8, 8}, {12, 12}};
+ } else {
+ qCWarning(lcPointerTests) << "skipping mouse tests: primary device is not a mouse" << mouse;
+ }
+}
+
+void tst_QQuickPinchHandler::cumulativeNativeGestures()
+{
+ QFETCH(const QPointingDevice*, device);
+ QFETCH(Qt::NativeGestureType, gesture);
+ QFETCH(qreal, value);
+ QFETCH(QList<QPoint>, expectedTargetTranslations);
+
+ QCOMPARE(expectedTargetTranslations.size(), 4);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("pinchproperties.qml")));
+ QVERIFY(window.rootObject() != nullptr);
+ qApp->processEvents();
+
+ QQuickItem *root = qobject_cast<QQuickItem*>(window.rootObject());
+ QVERIFY(root != nullptr);
+ QQuickPinchHandler *pinchHandler = root->findChild<QQuickPinchHandler*>("pinchHandler");
+ QVERIFY(pinchHandler != nullptr);
+ QQuickItem *target = root->findChild<QQuickItem*>("blackrect");
+ QVERIFY(target != nullptr);
+ QCOMPARE(pinchHandler->target(), target);
+
+ ulong ts = 1;
+ qreal expectedScale = 1;
+ qreal expectedRotation = 0;
+ QPointF pinchPos(75, 75);
+ const QPointF initialTargetPos(target->position());
+ QWindowSystemInterface::handleGestureEvent(&window, ts++, device,
+ Qt::BeginNativeGesture, pinchPos, pinchPos);
+ if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
+ for (int i = 1; i <= 4; ++i) {
+ QWindowSystemInterface::handleGestureEventWithRealValue(&window, ts++, device,
+ gesture, value, pinchPos, pinchPos);
+ qApp->processEvents();
+ switch (gesture) {
+ case Qt::ZoomNativeGesture:
+ expectedScale = qBound(qreal(0.5), qPow(1 + value, i), qreal(4));
+ break;
+ case Qt::RotateNativeGesture:
+ expectedRotation = qBound(qreal(0), value * i, qreal(90));
+ break;
+ default:
+ break; // PinchHandler doesn't react to the others
+ }
+
+ qCDebug(lcPointerTests) << i << gesture << "with value" << value
+ << ": scale" << target->scale() << "expected" << expectedScale
+ << ": rotation" << target->rotation() << "expected" << expectedRotation;
+ if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
+ QCOMPARE(target->scale(), expectedScale);
+ QCOMPARE(target->rotation(), expectedRotation);
+ QCOMPARE(pinchHandler->persistentScale(), expectedScale);
+ QCOMPARE(pinchHandler->activeScale(), expectedScale);
+ QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), expectedScale);
+ QCOMPARE(pinchHandler->scaleAxis()->activeValue(), expectedScale);
+ QCOMPARE(pinchHandler->persistentRotation(), expectedRotation);
+ QCOMPARE(pinchHandler->activeRotation(), expectedRotation);
+ QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), expectedRotation);
+ QCOMPARE(pinchHandler->rotationAxis()->activeValue(), expectedRotation);
+ // The target gets transformed around the gesture position, for which
+ // QQuickItemPrivate::adjustedPosForTransform() computes its new position to compensate.
+ QPointF delta = target->position() - initialTargetPos;
+ qCDebug(lcPointerTests) << "target moved by" << delta << "to" << target->position()
+ << "active trans" << pinchHandler->activeTranslation()
+ << "perst trans" << pinchHandler->persistentTranslation();
+ QCOMPARE_NE(target->position(), initialTargetPos);
+ QCOMPARE(delta.toPoint(), expectedTargetTranslations.at(i - 1));
+ // The native pinch gesture cannot include a translation component (and
+ // the cursor doesn't move while you are performing the gesture on a touchpad).
+ QCOMPARE(pinchHandler->activeTranslation(), QPointF());
+ // The target only moves to compensate for scale and rotation changes, and that's
+ // not reflected in PinchHandler.persistentTranslation.
+ QCOMPARE(pinchHandler->persistentTranslation(), QPointF());
+ }
+ QCOMPARE(pinchHandler->active(), true);
+ qCDebug(lcPointerTests) << "centroid: local" << pinchHandler->centroid().position()
+ << "scene" << pinchHandler->centroid().scenePosition();
+ QCOMPARE(pinchHandler->persistentScale(), expectedScale);
+ QCOMPARE(pinchHandler->activeScale(), expectedScale);
+ QCOMPARE(pinchHandler->scaleAxis()->activeValue(), expectedScale);
+ QWindowSystemInterface::handleGestureEvent(&window, ts++, device,
+ Qt::EndNativeGesture, pinchPos, pinchPos);
+ QTRY_COMPARE(pinchHandler->active(), false);
+ QCOMPARE(target->scale(), expectedScale);
+ QCOMPARE(target->rotation(), expectedRotation);
+ QCOMPARE(pinchHandler->persistentScale(), expectedScale);
+ QCOMPARE(pinchHandler->activeScale(), 1);
+ QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), expectedScale);
+ QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1);
+ QCOMPARE(pinchHandler->persistentRotation(), expectedRotation);
+ QCOMPARE(pinchHandler->activeRotation(), 0);
+ QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), expectedRotation);
+ QCOMPARE(pinchHandler->rotationAxis()->activeValue(), 0);
+ QCOMPARE(pinchHandler->activeTranslation(), QPointF());
+ QCOMPARE(pinchHandler->persistentTranslation(), QPointF());
+}
+
void tst_QQuickPinchHandler::pan()
{
QQuickView *window = QQuickViewTestUtils::createView();
@@ -575,7 +698,7 @@ void tst_QQuickPinchHandler::pan()
QPoint p1(100, 100);
{
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
- QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get());
pinchSequence.press(0, p0, window).commit();
QQuickTouchUtils::flush(window);
// In order for the stationary point to remember its previous position,
@@ -649,7 +772,7 @@ void tst_QQuickPinchHandler::pan()
// pan x beyond bound
p0 += QPoint(100,100);
p1 += QPoint(100,100);
- QTest::touchEvent(window, touchscreen).move(0, p0, window).move(1, p1, window);
+ QTest::touchEvent(window, touchscreen.get()).move(0, p0, window).move(1, p1, window);
QQuickTouchUtils::flush(window);
QCOMPARE(blackRect->x(), 140.0);
@@ -657,7 +780,7 @@ void tst_QQuickPinchHandler::pan()
QCOMPARE(translationChangedSpy.size(), 5);
QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(100, 100));
- QTest::touchEvent(window, touchscreen).release(0, p0, window).release(1, p1, window);
+ QTest::touchEvent(window, touchscreen.get()).release(0, p0, window).release(1, p1, window);
QQuickTouchUtils::flush(window);
QVERIFY(!root->property("pinchActive").toBool());
}
@@ -697,7 +820,7 @@ void tst_QQuickPinchHandler::dragAxesEnabled()
QPoint blackRectPos = blackRect->position().toPoint();
// press two points, one above the rectangle's center and one below
- QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get());
pinchSequence.press(0, p0, window).press(1, p1, window).commit();
QQuickTouchUtils::flush(window);
@@ -734,7 +857,7 @@ void tst_QQuickPinchHandler::dragAxesEnabled()
QCOMPARE(blackRect->position().toPoint().x(), xEnabled ? 140 : blackRectPos.x()); // because of xAxis.maximum
QCOMPARE(blackRect->position().toPoint().y(), yEnabled ? 170 : blackRectPos.y()); // because of yAxis.maximum
- QTest::touchEvent(window, touchscreen).release(0, p0, window).release(1, p1, window);
+ QTest::touchEvent(window, touchscreen.get()).release(0, p0, window).release(1, p1, window);
QQuickTouchUtils::flush(window);
}
@@ -763,7 +886,7 @@ void tst_QQuickPinchHandler::retouch()
QPoint p0(80, 80);
QPoint p1(100, 100);
{
- QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get());
pinchSequence.press(0, p0, window).commit();
QQuickTouchUtils::flush(window);
// In order for the stationary point to remember its previous position,
@@ -846,7 +969,7 @@ void tst_QQuickPinchHandler::cancel()
QPoint p0(80, 80);
QPoint p1(100, 100);
{
- QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get());
pinchSequence.press(0, p0, window).commit();
QQuickTouchUtils::flush(window);
// In order for the stationary point to remember its previous position,
@@ -876,7 +999,7 @@ void tst_QQuickPinchHandler::cancel()
QSKIP("cancel is not supported atm");
- QTouchEvent cancelEvent(QEvent::TouchCancel, touchscreen);
+ QTouchEvent cancelEvent(QEvent::TouchCancel, touchscreen.get());
QCoreApplication::sendEvent(window, &cancelEvent);
QQuickTouchUtils::flush(window);
@@ -932,7 +1055,7 @@ void tst_QQuickPinchHandler::transformedpinchHandler()
const int threshold = qApp->styleHints()->startDragDistance();
{
- QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, touchscreen);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, touchscreen.get());
// start pinchHandler
pinchSequence.press(0, p0, view).commit();
QQuickTouchUtils::flush(view);
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt
index da110a6398..e15b802814 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpointerhandler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpointerhandler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml
new file mode 100644
index 0000000000..7bc3907c8c
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml
@@ -0,0 +1,36 @@
+import QtQuick
+import Qt.test 1.0
+
+Item {
+ width: 200
+ height: 200
+
+ Rectangle {
+ id: circle
+ y: 0
+ width: 100
+ height: width
+ radius: width/2
+ color: "#3e1"
+ clip: true
+
+ // Rectangle contains() is not affected by its 'radius' property
+ containmentMask: QtObject {
+ property alias radius: circle.radius
+ function contains(point: point) : bool {
+ return (Math.pow(point.x - radius, 2) + Math.pow(point.y - radius, 2)) < Math.pow(radius, 2)
+ }
+ }
+ EventHandler {
+ objectName: "circle eventHandler"
+ }
+ Rectangle {
+ width: circle.width/2
+ height: width
+ color: "red"
+ EventHandler {
+ objectName: "eventHandler"
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml
new file mode 100644
index 0000000000..7bc6028bc2
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml
@@ -0,0 +1,78 @@
+import QtQuick
+
+Window {
+ id: root
+ visible: true
+ objectName: "root"
+ width: 320
+ height: 480
+
+ property bool useTimer : false
+ property int grabChangedCounter : 0
+
+ Item {
+ id: back
+ anchors.fill: parent
+
+ Rectangle {
+ id: background
+ anchors.fill: parent
+ color: "blue"
+ }
+
+ Rectangle {
+ id: container
+ objectName: "container"
+ anchors.fill: parent
+ anchors.margins: 50
+ z: 2
+
+ Rectangle {
+ id: likeButton
+ color: "gray"
+ anchors.centerIn: parent
+ width: 200
+ height: 200
+
+ DragHandler {
+ id: handler
+ objectName: "dragHandler"
+ grabPermissions: PointerHandler.CanTakeOverFromItems
+ onGrabChanged: {
+ ++grabChangedCounter
+ }
+ }
+ }
+ }
+
+ Timer {
+ id: reparentTimer
+ running: false
+ interval: 100
+ repeat: false
+ onTriggered: {
+ container.parent = null
+ }
+ }
+
+ Rectangle {
+ id: likeButton2
+ color: "yellow"
+ anchors.centerIn: parent
+ width: 100
+ height: 100
+ z: 3
+
+ MultiPointTouchArea {
+ id: press
+ anchors.fill: parent
+ onPressed: {
+ if (useTimer)
+ reparentTimer.running = true
+ else
+ container.parent = null
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
index aa89ad0631..1120cb54c2 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -162,6 +162,7 @@ public:
class EventHandler : public QQuickPointerHandler
{
+ Q_OBJECT
public:
EventHandler(QQuickItem *parent = nullptr) :
QQuickPointerHandler(parent) {}
@@ -231,6 +232,9 @@ private slots:
void dynamicCreationInWindow();
void cppConstruction();
void reparenting();
+ void grabberSceneChange_data();
+ void grabberSceneChange();
+ void clip();
protected:
bool eventFilter(QObject *, QEvent *event) override
@@ -749,6 +753,112 @@ void tst_PointerHandlers::reparenting()
}
}
+/*!
+ Verify that removing an item that has a grabbing handler from the scene
+ does not result in crashes in our event dispatching code. The item's window()
+ pointer will be nullptr, so the handler must have released the grab, or never
+ gotten the grab, depending on when the item gets removed.
+
+ See QTBUG-114475.
+*/
+void tst_PointerHandlers::grabberSceneChange_data()
+{
+ QTest::addColumn<bool>("useTimer");
+ QTest::addColumn<int>("grabChangedCount");
+
+ QTest::addRow("Immediately") << false << 0;
+ QTest::addRow("Delayed") << true << 2;
+}
+
+void tst_PointerHandlers::grabberSceneChange()
+{
+ QFETCH(const bool, useTimer);
+ QFETCH(const int, grabChangedCount);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("grabberSceneChange.qml"));
+ QQuickWindow *window = qobject_cast<QQuickWindow*>(component.create());
+ QScopedPointer<QQuickWindow> cleanup(window);
+ QVERIFY(window);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ window->setProperty("useTimer", useTimer);
+
+ QQuickItem *container = window->findChild<QQuickItem *>("container");
+
+ QPoint p1 = QPoint(window->width() / 2, window->height() / 2);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ // The container gets removed from this window, either immediately on
+ // press, or through a timer.
+ QTRY_COMPARE(container->parentItem(), nullptr);
+
+ QEXPECT_FAIL("Delayed",
+ "PointerHandlers don't release their grab when item is removed", Continue);
+ QCOMPARE(window->property("grabChangedCounter").toInt(), grabChangedCount);
+
+ // this should not crash
+ QTest::mouseMove(window, p1 + QPoint(5, 5));
+}
+
+void tst_PointerHandlers::clip()
+{
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "clip.qml");
+ QQuickView * window = windowPtr.data();
+ QVERIFY(window);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ EventHandler *handler = window->contentItem()->findChild<EventHandler*>("eventHandler");
+ EventHandler *circleHandler = window->contentItem()->findChild<EventHandler*>("circle eventHandler");
+
+ QCOMPARE(handler->pressEventCount, 0);
+ QCOMPARE(circleHandler->pressEventCount, 0);
+ QCOMPARE(handler->releaseEventCount, 0);
+ QCOMPARE(circleHandler->releaseEventCount, 0);
+
+ const QPoint rectPt = QPoint(1, 1);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectPt);
+ QCOMPARE(handler->pressEventCount, 1);
+ QCOMPARE(circleHandler->pressEventCount, 0);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectPt);
+ QCOMPARE(handler->releaseEventCount, 1);
+ QCOMPARE(circleHandler->releaseEventCount, 0);
+
+
+ handler->pressEventCount = 0;
+ circleHandler->pressEventCount = 0;
+ handler->releaseEventCount = 0;
+ circleHandler->releaseEventCount = 0;
+
+ const QPoint rectAndCirclePt = QPoint(49 ,49);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt);
+ QCOMPARE(handler->pressEventCount, 1);
+ QCOMPARE(circleHandler->pressEventCount, 1);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt);
+ QCOMPARE(handler->releaseEventCount, 1);
+ QCOMPARE(circleHandler->releaseEventCount, 1);
+
+
+ handler->pressEventCount = 0;
+ circleHandler->pressEventCount = 0;
+ handler->releaseEventCount = 0;
+ circleHandler->releaseEventCount = 0;
+
+ const QPoint circlePt = QPoint(51 ,51);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, circlePt);
+ QCOMPARE(handler->pressEventCount, 0);
+ QCOMPARE(circleHandler->pressEventCount, 1);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, circlePt);
+ QCOMPARE(handler->releaseEventCount, 0);
+ QCOMPARE(circleHandler->releaseEventCount, 1);
+}
+
QTEST_MAIN(tst_PointerHandlers)
#include "tst_qquickpointerhandler.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt
index 969df8a4ce..aa73218361 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpointhandler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpointhandler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml
index d813160f44..616d526592 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml
+++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml
index 54d7ff7212..2de03703e8 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml
+++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
index 4435580d92..e1641c282e 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -18,6 +18,8 @@
#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
+#include <QtCore/qpointer.h>
+
Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
class tst_PointHandler : public QQmlDataTest
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST
index d13d25390b..1559014480 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST
@@ -1,11 +1,3 @@
-# QTBUG-95939
-[touchGesturePolicyDragThreshold]
-opensuse-leap
-
-# QTBUG-95939
-[mouseGesturePolicyDragThreshold]
-opensuse-leap
-
# QTBUG-103072
[gesturePolicyDragWithinBounds]
android
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt
index cab8fc1a0d..94834e04c6 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktaphandler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktaphandler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
index fa58d76e4a..fc1e623902 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
@@ -10,13 +10,20 @@ Rectangle {
property alias pressed: tap.pressed
property bool checked: false
property alias gesturePolicy: tap.gesturePolicy
+ property alias longPressThreshold: tap.longPressThreshold
property point tappedPosition: Qt.point(0, 0)
+ property real timeHeldWhenTapped: 0
+ property real timeHeldWhenLongPressed: 0
signal tapped
signal canceled
width: label.implicitWidth * 1.5; height: label.implicitHeight * 2.0
border.color: "#9f9d9a"; border.width: 1; radius: height / 4; antialiasing: true
+ function assignUndefinedLongPressThreshold() {
+ tap.longPressThreshold = undefined
+ }
+
gradient: Gradient {
GradientStop { position: 0.0; color: tap.pressed ? "#b8b5b2" : "#efebe7" }
GradientStop { position: 1.0; color: "#b8b5b2" }
@@ -25,14 +32,17 @@ Rectangle {
TapHandler {
id: tap
objectName: label.text
- longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped
onSingleTapped: console.log("Single tap")
onDoubleTapped: console.log("Double tap")
- onTapped: {
- console.log("Tapped")
+ onTapped: (eventPoint, button) => {
+ console.log("Tapped", button, eventPoint)
tapFlash.start()
root.tappedPosition = point.scenePosition
root.tapped()
+ root.timeHeldWhenTapped = tap.timeHeld // eventPoint.timeHeld is already 0
+ }
+ onLongPressed: {
+ root.timeHeldWhenLongPressed = tap.timeHeld
}
onCanceled: root.canceled()
}
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml
index e3f15f399f..9483a12d1c 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml
index c5f2f6e1ae..b36fcdef08 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml
index 5731f51f38..04fbbc176b 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
@@ -9,19 +9,25 @@ Item {
Button {
objectName: "DragThreshold"
label: "DragThreshold"
- x: 10; y: 10; width: parent.width - 20; height: 40
+ x: 10; y: 10; width: 300; height: 40
gesturePolicy: TapHandler.DragThreshold
}
Button {
objectName: "WithinBounds"
label: "WithinBounds"
- x: 10; y: 60; width: parent.width - 20; height: 40
+ x: 10; y: 60; width: 300; height: 40
gesturePolicy: TapHandler.WithinBounds
}
Button {
objectName: "ReleaseWithinBounds"
label: "ReleaseWithinBounds"
- x: 10; y: 110; width: parent.width - 20; height: 40
+ x: 10; y: 110; width: 300; height: 40
gesturePolicy: TapHandler.ReleaseWithinBounds
}
+ Button {
+ objectName: "DragWithinBounds"
+ label: "DragWithinBounds"
+ x: 10; y: 160; width: 300; height: 40
+ gesturePolicy: TapHandler.DragWithinBounds
+ }
}
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml
index 1f819a937f..5f60ef879e 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml
index f9db2d7179..c0f0b10bf4 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
Item {
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml
new file mode 100644
index 0000000000..7732c42082
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+import QtQuick.Controls 2.15
+
+Item {
+ width: 360
+ height: 280
+
+ Rectangle {
+ width: 200; height: 200; x: 100; y: 10
+ color: th1.pressed ? "blue" : "lightblue"
+
+ TapHandler {
+ id: th1
+ objectName: "th1"
+ }
+
+ Rectangle {
+ width: 200; height: 200; x: 50; y: 50
+ color: th2.pressed ? "steelblue" : "lightsteelblue"
+
+ TapHandler {
+ id: th2
+ objectName: "th2"
+ }
+ }
+ }
+
+ Rectangle {
+ width: 200; height: 200; x: 10; y: 50
+ color: th3.pressed ? "goldenrod" : "beige"
+
+ TapHandler {
+ id: th3
+ objectName: "th3"
+ }
+ }
+}
+
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml
index 3517afe9c3..85f5c87b39 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml
index 54adc87c54..026be48f83 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
index f000d48171..1dab0836cd 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -44,10 +44,12 @@ private slots:
void touchMultiTap();
void mouseMultiTap_data();
void mouseMultiTap();
+ void mouseMultiTapLeftRight_data();
+ void mouseMultiTapLeftRight();
void singleTapDoubleTap_data();
void singleTapDoubleTap();
- void touchLongPress();
- void mouseLongPress();
+ void longPress_data();
+ void longPress();
void buttonsMultiTouch();
void componentUserBehavioralOverride();
void rightLongPressIgnoreWheel();
@@ -55,6 +57,8 @@ private slots:
void nonTopLevelParentWindow();
void nestedDoubleTap_data();
void nestedDoubleTap();
+ void nestedAndSiblingPropagation_data();
+ void nestedAndSiblingPropagation();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName,
@@ -507,6 +511,23 @@ void tst_TapHandler::touchMultiTap()
QQuickTouchUtils::flush(window);
QTRY_VERIFY(!button->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 4);
+
+ // Test a stray touch begin
+ tappedSpy.clear();
+ constexpr int count = 2;
+ for (int i = 0; i < count; ++i) {
+ QTest::touchEvent(window, touchDevice).press(1, p1, window);
+ QQuickTouchUtils::flush(window);
+ p1 -= QPoint(dragThreshold, dragThreshold);
+ QTest::touchEvent(window, touchDevice).move(1, p1, window);
+ QQuickTouchUtils::flush(window);
+ QTest::touchEvent(window, touchDevice).release(1, p1, window);
+ QQuickTouchUtils::flush(window);
+ p1 += QPoint(dragThreshold, dragThreshold);
+ QTest::touchEvent(window, touchDevice).press(1, p1, window);
+ QQuickTouchUtils::flush(window);
+ }
+ QCOMPARE(tappedSpy.count(), count);
}
void tst_TapHandler::mouseMultiTap_data()
@@ -592,6 +613,66 @@ void tst_TapHandler::mouseMultiTap()
QCOMPARE(singleTapSpy.size(), expectedSingleTapsAfterWaiting);
}
+void tst_TapHandler::mouseMultiTapLeftRight_data()
+{
+ QTest::addColumn<QQuickTapHandler::ExclusiveSignals>("exclusiveSignals");
+ QTest::addColumn<int>("expectedSingleTaps");
+ QTest::addColumn<int>("expectedDoubleTaps");
+ QTest::addColumn<int>("expectedTabCount2");
+ QTest::addColumn<int>("expectedTabCount3");
+
+ QTest::newRow("NotExclusive") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive)
+ << 3 << 0 << 1 << 1;
+ QTest::newRow("SingleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap)
+ << 3 << 0 << 1 << 1;
+ QTest::newRow("DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap)
+ << 0 << 0 << 1 << 1;
+ QTest::newRow("SingleTap|DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap)
+ << 0 << 0 << 1 << 1;
+}
+
+void tst_TapHandler::mouseMultiTapLeftRight() //QTBUG-111557
+{
+ QFETCH(QQuickTapHandler::ExclusiveSignals, exclusiveSignals);
+ QFETCH(int, expectedSingleTaps);
+ QFETCH(int, expectedDoubleTaps);
+ QFETCH(int, expectedTabCount2);
+ QFETCH(int, expectedTabCount3);
+
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "buttons.qml");
+ QQuickView * window = windowPtr.data();
+
+ QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
+ QVERIFY(button);
+ QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>();
+ QVERIFY(tapHandler);
+ tapHandler->setExclusiveSignals(exclusiveSignals);
+ tapHandler->setAcceptedButtons(Qt::LeftButton | Qt::RightButton);
+ QSignalSpy tappedSpy(button, SIGNAL(tapped()));
+ QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped);
+ QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped);
+
+ // Click once with the left button
+ QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint();
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
+
+ // Click again with the right button -> should reset tabCount()
+ QTest::mousePress(window, Qt::RightButton, Qt::NoModifier, p1, 10);
+ QTest::mouseRelease(window, Qt::RightButton, Qt::NoModifier, p1, 10);
+
+ QCOMPARE(tapHandler->tapCount(), expectedTabCount2);
+
+ // Click again with the left button -> should reset tabCount()
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
+
+ QCOMPARE(tapHandler->tapCount(), expectedTabCount3);
+ QCOMPARE(singleTapSpy.size(), expectedSingleTaps);
+ QCOMPARE(doubleTapSpy.size(), expectedDoubleTaps);
+}
+
void tst_TapHandler::singleTapDoubleTap_data()
{
QTest::addColumn<QPointingDevice::DeviceType>("deviceType");
@@ -653,12 +734,13 @@ void tst_TapHandler::singleTapDoubleTap()
QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped);
QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped);
- auto tap = [window, tapHandler, deviceType, this](const QPoint &p1) {
+ auto tap = [window, tapHandler, deviceType, this](const QPoint &p1, int delay = 10) {
switch (static_cast<QPointingDevice::DeviceType>(deviceType)) {
case QPointingDevice::DeviceType::Mouse:
- QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, delay);
break;
case QPointingDevice::DeviceType::TouchScreen:
+ QTest::qWait(delay);
QTest::touchEvent(window, touchDevice).press(0, p1, window);
QTRY_VERIFY(tapHandler->isPressed());
QTest::touchEvent(window, touchDevice).release(0, p1, window);
@@ -679,78 +761,211 @@ void tst_TapHandler::singleTapDoubleTap()
QTRY_COMPARE(doubleTapSpy.size(), expectedDoubleTapCount);
QCOMPARE(tappedSpy.size(), 2);
QCOMPARE(singleTapSpy.size(), expectedEndingSingleTapCount);
-}
-void tst_TapHandler::touchLongPress()
-{
- QScopedPointer<QQuickView> windowPtr;
- createView(windowPtr, "buttons.qml");
- QQuickView * window = windowPtr.data();
+ // wait past the double-tap interval, then do it again
+ const auto delay = qApp->styleHints()->mouseDoubleClickInterval() + 10;
+ tappedSpy.clear();
+ singleTapSpy.clear();
+ doubleTapSpy.clear();
- QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
- QVERIFY(button);
- QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold");
- QVERIFY(tapHandler);
- QSignalSpy tappedSpy(button, SIGNAL(tapped()));
- QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged()));
- QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged()));
- QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed()));
+ // tap once with delay
+ tap(p1, delay);
+ QCOMPARE(tappedSpy.size(), 1);
+ QCOMPARE(doubleTapSpy.size(), 0);
- // Reduce the threshold so that we can get a long press quickly
- tapHandler->setLongPressThreshold(0.5);
- QCOMPARE(longPressThresholdChangedSpy.size(), 1);
+ // tap again immediately afterwards
+ tap(p1);
+ QTRY_COMPARE(doubleTapSpy.size(), expectedDoubleTapCount);
+ QCOMPARE(tappedSpy.size(), 2);
+ QCOMPARE(singleTapSpy.size(), expectedEndingSingleTapCount);
+}
- // Press and hold
- QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint();
- QTest::touchEvent(window, touchDevice).press(1, p1, window);
- QQuickTouchUtils::flush(window);
- QTRY_VERIFY(button->property("pressed").toBool());
- QTRY_COMPARE(longPressedSpy.size(), 1);
- timeHeldSpy.wait(); // the longer we hold it, the more this will occur
- qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.size() << "times";
- QVERIFY(timeHeldSpy.size() > 0);
- QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere
+void tst_TapHandler::longPress_data()
+{
+ QTest::addColumn<const QPointingDevice *>("device");
+ QTest::addColumn<QString>("buttonName");
+ QTest::addColumn<qreal>("longPressThreshold");
+ QTest::addColumn<QPoint>("releaseOffset");
+ QTest::addColumn<bool>("expectLongPress");
+ QTest::addColumn<bool>("expectTapped");
- // Release and verify that tapped was not emitted
- QTest::touchEvent(window, touchDevice).release(1, p1, window);
- QQuickTouchUtils::flush(window);
- QTRY_VERIFY(!button->property("pressed").toBool());
- QCOMPARE(tappedSpy.size(), 0);
+ const QPointingDevice *constTouchDevice = touchDevice;
+
+ // Reduce the threshold so that we can get a long press quickly (faster in CI)
+ const qreal longPressThreshold = 0.3;
+ QTest::newRow("mouse, lpt longPressThreshold: DragThreshold")
+ << QPointingDevice::primaryPointingDevice() << "DragThreshold"
+ << longPressThreshold << QPoint(0, 0) << true << false;
+ QTest::newRow("touch, lpt longPressThreshold: DragThreshold")
+ << constTouchDevice << "DragThreshold" << longPressThreshold
+ << QPoint(0, 0) << true << false;
+ QTest::newRow("mouse, lpt longPressThreshold: DragThreshold, drag")
+ << QPointingDevice::primaryPointingDevice() << "DragThreshold"
+ << longPressThreshold << QPoint(50, 0) << false << false;
+ QTest::newRow("touch, lpt longPressThreshold: DragThreshold, drag")
+ << constTouchDevice << "DragThreshold"
+ << longPressThreshold << QPoint(50, 0) << false << false;
+
+ QTest::newRow("mouse, lpt longPressThreshold: WithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "WithinBounds"
+ << longPressThreshold << QPoint(0, 0) << true << false;
+ QTest::newRow("touch, lpt longPressThreshold: WithinBounds")
+ << constTouchDevice << "WithinBounds"
+ << longPressThreshold << QPoint(0, 0) << true << false;
+ QTest::newRow("mouse, lpt longPressThreshold: WithinBounds, drag")
+ << QPointingDevice::primaryPointingDevice() << "WithinBounds"
+ << longPressThreshold << QPoint(50, 0) << true << false;
+ QTest::newRow("touch, lpt longPressThreshold: WithinBounds, drag")
+ << constTouchDevice << "WithinBounds"
+ << longPressThreshold << QPoint(50, 0) << true << false;
+ QTest::newRow("mouse, lpt longPressThreshold: WithinBounds, drag out")
+ << QPointingDevice::primaryPointingDevice() << "WithinBounds"
+ << longPressThreshold << QPoint(155, 0) << false << false;
+ QTest::newRow("touch, lpt longPressThreshold: WithinBounds, drag out")
+ << constTouchDevice << "WithinBounds"
+ << longPressThreshold << QPoint(155, 0) << false << false;
+
+ QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds"
+ << longPressThreshold << QPoint(0, 0) << true << false;
+ QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds")
+ << constTouchDevice << "ReleaseWithinBounds"
+ << longPressThreshold << QPoint(0, 0) << true << false;
+ QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds, drag")
+ << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds"
+ << longPressThreshold << QPoint(50, 0) << true << false;
+ QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds, drag")
+ << constTouchDevice << "ReleaseWithinBounds"
+ << longPressThreshold << QPoint(50, 0) << true << false;
+ QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds, drag out")
+ << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds"
+ << longPressThreshold << QPoint(155, 0) << false << false;
+ QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds, drag out")
+ << constTouchDevice << "ReleaseWithinBounds"
+ << longPressThreshold << QPoint(155, 0) << false << false;
+
+ QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "DragWithinBounds"
+ << longPressThreshold << QPoint(0, 0) << true << false;
+ QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds")
+ << constTouchDevice << "DragWithinBounds"
+ << longPressThreshold << QPoint(0, 0) << true << false;
+ QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds, drag")
+ << QPointingDevice::primaryPointingDevice() << "DragWithinBounds"
+ << longPressThreshold << QPoint(50, 0) << true << false;
+ QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds, drag")
+ << constTouchDevice << "DragWithinBounds"
+ << longPressThreshold << QPoint(50, 0) << true << false;
+ QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds, drag out")
+ << QPointingDevice::primaryPointingDevice() << "DragWithinBounds"
+ << longPressThreshold << QPoint(155, 0) << false << false;
+ QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds, drag out")
+ << constTouchDevice << "DragWithinBounds"
+ << longPressThreshold << QPoint(155, 0) << false << false;
+
+ // Zero or negative threshold means long press is disabled
+ QTest::newRow("mouse, lpt 0: DragThreshold")
+ << QPointingDevice::primaryPointingDevice() << "DragThreshold"
+ << qreal(0) << QPoint(0, 0) << false << true;
+ QTest::newRow("mouse, lpt -1: DragThreshold")
+ << QPointingDevice::primaryPointingDevice() << "DragThreshold"
+ << qreal(-1) << QPoint(0, 0) << true << false;
+ QTest::newRow("touch, lpt 0: DragThreshold")
+ << constTouchDevice << "DragThreshold"
+ << qreal(0) << QPoint(0, 0) << false << true;
+
+ QTest::newRow("mouse, lpt 0: WithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "WithinBounds"
+ << qreal(0) << QPoint(0, 0) << false << true;
+ QTest::newRow("mouse, lpt -1: WithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "WithinBounds"
+ << qreal(-1) << QPoint(0, 0) << true << false;
+ QTest::newRow("touch, lpt 0: WithinBounds")
+ << constTouchDevice << "WithinBounds"
+ << qreal(0) << QPoint(0, 0) << false << true;
+
+ QTest::newRow("mouse, lpt 0: ReleaseWithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds"
+ << qreal(0) << QPoint(0, 0) << false << true;
+ QTest::newRow("mouse, lpt -1: ReleaseWithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds"
+ << qreal(-1) << QPoint(0, 0) << true << false;
+ QTest::newRow("touch, lpt 0: ReleaseWithinBounds")
+ << constTouchDevice << "ReleaseWithinBounds"
+ << qreal(0) << QPoint(0, 0) << false << true;
+
+ QTest::newRow("mouse, lpt 0: DragWithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "DragWithinBounds"
+ << qreal(0) << QPoint(0, 0) << false << true;
+ QTest::newRow("mouse, lpt -1: DragWithinBounds")
+ << QPointingDevice::primaryPointingDevice() << "DragWithinBounds"
+ << qreal(-1) << QPoint(0, 0) << true << false;
+ QTest::newRow("touch, lpt 0: DragWithinBounds")
+ << constTouchDevice << "DragWithinBounds"
+ << qreal(0) << QPoint(0, 0) << false << true;
}
-void tst_TapHandler::mouseLongPress()
+void tst_TapHandler::longPress()
{
- QScopedPointer<QQuickView> windowPtr;
- createView(windowPtr, "buttons.qml");
- QQuickView * window = windowPtr.data();
+ QFETCH(const QPointingDevice *, device);
+ QFETCH(QString, buttonName);
+ QFETCH(qreal, longPressThreshold);
+ QFETCH(QPoint, releaseOffset);
+ QFETCH(bool, expectLongPress);
+ QFETCH(bool, expectTapped);
- QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("buttons.qml")));
+
+ QQuickItem *button = window.rootObject()->findChild<QQuickItem*>(buttonName);
QVERIFY(button);
- QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold");
+ QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>(buttonName);
QVERIFY(tapHandler);
QSignalSpy tappedSpy(button, SIGNAL(tapped()));
QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged()));
QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged()));
QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed()));
- // Reduce the threshold so that we can get a long press quickly
- tapHandler->setLongPressThreshold(0.5);
- QCOMPARE(longPressThresholdChangedSpy.size(), 1);
+ const qreal defaultThreshold = tapHandler->longPressThreshold();
+ qsizetype changedCount = 0;
+ QCOMPARE_GT(defaultThreshold, 0);
+ tapHandler->setLongPressThreshold(longPressThreshold);
+ if (longPressThreshold > 0)
+ QCOMPARE(longPressThresholdChangedSpy.size(), ++changedCount);
+ QVERIFY(QMetaObject::invokeMethod(button, "assignUndefinedLongPressThreshold"));
+ if (longPressThreshold > 0)
+ QCOMPARE(longPressThresholdChangedSpy.size(), ++changedCount);
+ QCOMPARE(tapHandler->longPressThreshold(), defaultThreshold);
+ tapHandler->setLongPressThreshold(longPressThreshold);
// Press and hold
QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint();
- QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QQuickTest::pointerPress(device, &window, 1, p1);
QTRY_VERIFY(button->property("pressed").toBool());
- QTRY_COMPARE(longPressedSpy.size(), 1);
+ QTRY_COMPARE(longPressedSpy.size(), expectLongPress ? 1 : 0);
timeHeldSpy.wait(); // the longer we hold it, the more this will occur
qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.size() << "times";
- QVERIFY(timeHeldSpy.size() > 0);
- QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere
+ QCOMPARE_GT(timeHeldSpy.size(), 0);
+ if (expectLongPress) {
+ // Should be > longPressThreshold but slow CI and timer granularity can interfere
+ QCOMPARE_GT(tapHandler->timeHeld(), longPressThreshold - 0.1);
+ } else {
+ // Should be quite small, but event delivery is not instantaneous
+ QCOMPARE_LT(tapHandler->timeHeld(), 0.3);
+ }
+
+ // If we have an offset, we need a move between press and release for realistic simulation
+ if (!releaseOffset.isNull())
+ QQuickTest::pointerMove(device, &window, 1, p1 + releaseOffset);
- // Release and verify that tapped was not emitted
- QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 500);
+ // Release (optionally at an offset) and check whether tapped was emitted
+ QQuickTest::pointerRelease(device, &window, 1, p1 + releaseOffset);
QTRY_VERIFY(!button->property("pressed").toBool());
- QCOMPARE(tappedSpy.size(), 0);
+ if (expectLongPress)
+ QCOMPARE_GT(button->property("timeHeldWhenLongPressed").toReal(), longPressThreshold - 0.1);
+ QCOMPARE(tapHandler->timeHeld(), -1);
+ QCOMPARE(tappedSpy.size(), expectTapped ? 1 : 0);
+ QCOMPARE(longPressedSpy.size(), expectLongPress ? 1 : 0);
}
void tst_TapHandler::buttonsMultiTouch()
@@ -866,8 +1081,8 @@ void tst_TapHandler::componentUserBehavioralOverride()
QQuickTapHandler *userTapHandler = button->findChild<QQuickTapHandler*>("override");
QVERIFY(userTapHandler);
QSignalSpy tappedSpy(button, SIGNAL(tapped()));
- QSignalSpy innerGrabChangedSpy(innerTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint)));
- QSignalSpy userGrabChangedSpy(userTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint)));
+ QSignalSpy innerGrabChangedSpy(innerTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint)));
+ QSignalSpy userGrabChangedSpy(userTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint)));
QSignalSpy innerPressedChangedSpy(innerTapHandler, SIGNAL(pressedChanged()));
QSignalSpy userPressedChangedSpy(userTapHandler, SIGNAL(pressedChanged()));
@@ -1019,6 +1234,61 @@ void tst_TapHandler::nestedDoubleTap() // QTBUG-102625
childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 4 : 2);
}
+void tst_TapHandler::nestedAndSiblingPropagation_data()
+{
+ QTest::addColumn<const QPointingDevice *>("device");
+ QTest::addColumn<QQuickTapHandler::GesturePolicy>("gesturePolicy");
+ QTest::addColumn<bool>("expectPropagation");
+
+ const QPointingDevice *constTouchDevice = touchDevice;
+
+ QTest::newRow("primary, DragThreshold") << QPointingDevice::primaryPointingDevice()
+ << QQuickTapHandler::GesturePolicy::DragThreshold << true;
+ QTest::newRow("primary, WithinBounds") << QPointingDevice::primaryPointingDevice()
+ << QQuickTapHandler::GesturePolicy::WithinBounds << false;
+ QTest::newRow("primary, ReleaseWithinBounds") << QPointingDevice::primaryPointingDevice()
+ << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds << false;
+ QTest::newRow("primary, DragWithinBounds") << QPointingDevice::primaryPointingDevice()
+ << QQuickTapHandler::GesturePolicy::DragWithinBounds << false;
+
+ QTest::newRow("touch, DragThreshold") << constTouchDevice
+ << QQuickTapHandler::GesturePolicy::DragThreshold << true;
+ QTest::newRow("touch, WithinBounds") << constTouchDevice
+ << QQuickTapHandler::GesturePolicy::WithinBounds << false;
+ QTest::newRow("touch, ReleaseWithinBounds") << constTouchDevice
+ << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds << false;
+ QTest::newRow("touch, DragWithinBounds") << constTouchDevice
+ << QQuickTapHandler::GesturePolicy::DragWithinBounds << false;
+}
+
+void tst_TapHandler::nestedAndSiblingPropagation() // QTBUG-117387
+{
+ QFETCH(const QPointingDevice *, device);
+ QFETCH(QQuickTapHandler::GesturePolicy, gesturePolicy);
+ QFETCH(bool, expectPropagation);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("nestedAndSibling.qml")));
+ QQuickItem *root = window.rootObject();
+ QQuickTapHandler *th1 = root->findChild<QQuickTapHandler*>("th1");
+ QVERIFY(th1);
+ th1->setGesturePolicy(gesturePolicy);
+ QQuickTapHandler *th2 = root->findChild<QQuickTapHandler*>("th2");
+ QVERIFY(th2);
+ th2->setGesturePolicy(gesturePolicy);
+ QQuickTapHandler *th3 = root->findChild<QQuickTapHandler*>("th3");
+ QVERIFY(th3);
+ th3->setGesturePolicy(gesturePolicy);
+
+ QPoint middle(180, 140);
+ QQuickTest::pointerPress(device, &window, 0, middle);
+ QVERIFY(th3->isPressed()); // it's on top
+ QCOMPARE(th2->isPressed(), expectPropagation);
+ QCOMPARE(th1->isPressed(), expectPropagation);
+
+ QQuickTest::pointerRelease(device, &window, 0, middle);
+}
+
QTEST_MAIN(tst_TapHandler)
#include "tst_qquicktaphandler.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt
index b5cfe1fa5a..d50181dfe4 100644
--- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickwheelhandler Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickwheelhandler LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml
index d50f6ac005..9c43df46da 100644
--- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml
index c0c5e776e9..ca6053ebcf 100644
--- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
@@ -14,7 +14,23 @@ Rectangle {
}
}
+ Rectangle {
+ color: "red"
+ width: 6; height: 6; radius: 3
+ x: wheelHandler.point.position.x - radius
+ y: wheelHandler.point.position.y - radius
+ }
+
+ Text {
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: 20
+ color: "white"
+ font.pixelSize: 18
+ text: parent.x.toFixed(2) + ", " + parent.y.toFixed(2)
+ }
+
WheelHandler {
+ id: wheelHandler
activeTimeout: 0.5
}
}
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp
index d87acc3200..c88b1af2ea 100644
--- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
diff --git a/tests/auto/quick/propertyrequirements/CMakeLists.txt b/tests/auto/quick/propertyrequirements/CMakeLists.txt
index 0685170946..cab8e3904c 100644
--- a/tests/auto/quick/propertyrequirements/CMakeLists.txt
+++ b/tests/auto/quick/propertyrequirements/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_propertyrequirements Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_propertyrequirements LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_propertyrequirements
SOURCES
tst_propertyrequirements.cpp
diff --git a/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp b/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
index b8de922777..c8efaab4b1 100644
--- a/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
+++ b/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Kevin Krammer <kevin.krammer@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlengine.h>
@@ -67,8 +67,9 @@ void tst_PropertyRequirements::constantOrNotifyableMain()
}
messages.sort();
- qWarning() << "\nThe following QML Types have properties which are neither CONSTANT nor NOTIFYable:\n"
- << qPrintable(messages.join("\n"));
+ qWarning() << "\nThe following QML Types have properties which are"
+ << "neither CONSTANT nor NOTIFYable nor BINDABLE:\n"
+ << qPrintable(messages.join("\n"));
// TODO enable once technical debt is fixes
// QCOMPARE(failuresByProperty.count(), 0);
@@ -94,7 +95,9 @@ void tst_PropertyRequirements::constantOrNotifyableFull()
}
- static const QString messagePattern("\nProperty %1 neither CONSTANT nor NOTIFYable. Affected types:\n\t%2");
+ static const QLatin1String messagePattern(
+ "\nProperty %1 neither CONSTANT nor NOTIFYable nor BINDABLE. "
+ "Affected types:\n\t%2");
QStringList occurrencesList = occurrences.values();
occurrencesList.sort();
messages.append(messagePattern.arg(it.key(), occurrencesList.join("\n\t")));
@@ -165,7 +168,7 @@ void tst_PropertyRequirements::testQmlType(TestDepth testDepth, const QQmlType &
const QMetaProperty property = metaClass->property(idx);
// needs to be either CONSTANT or have a NOTIFY signal
- if (!property.isConstant() && !property.hasNotifySignal()) {
+ if (!property.isConstant() && !property.hasNotifySignal() && !property.isBindable()) {
static const QString fullNamePattern("%1::%2");
const QString fullPropertyName = fullNamePattern.arg(metaClass->className(), property.name());
diff --git a/tests/auto/quick/qquickaccessible/CMakeLists.txt b/tests/auto/quick/qquickaccessible/CMakeLists.txt
index ddf890d1dc..517e910f73 100644
--- a/tests/auto/quick/qquickaccessible/CMakeLists.txt
+++ b/tests/auto/quick/qquickaccessible/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickaccessible Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickaccessible LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickaccessible/data/hittest.qml b/tests/auto/quick/qquickaccessible/data/hittest.qml
index 825f454b2c..09d60d8f07 100644
--- a/tests/auto/quick/qquickaccessible/data/hittest.qml
+++ b/tests/auto/quick/qquickaccessible/data/hittest.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickaccessible/data/ignored.qml b/tests/auto/quick/qquickaccessible/data/ignored.qml
index a4218cc627..150a2bd8ef 100644
--- a/tests/auto/quick/qquickaccessible/data/ignored.qml
+++ b/tests/auto/quick/qquickaccessible/data/ignored.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
index e7da38a5ce..e164d89217 100644
--- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
+++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -19,6 +19,7 @@
#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquicktext_p.h>
+#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
@@ -56,10 +57,12 @@ private slots:
void commonTests();
void quickAttachedProperties();
+ void attachedWins();
void basicPropertiesTest();
void hitTest();
void checkableTest();
void ignoredTest();
+ void passwordTest();
};
tst_QQuickAccessible::tst_QQuickAccessible()
@@ -322,6 +325,31 @@ void tst_QQuickAccessible::quickAttachedProperties()
QTestAccessibility::clearEvents();
}
+// Verify that a role can be explicitly set, and that the values from the
+// attached object are used even if the item has a default role - QTBUG-110114
+void tst_QQuickAccessible::attachedWins()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(R"(
+ import QtQuick
+ import QtQuick.Controls
+ Button {
+ text: "Button"
+ objectName: "button"
+ Accessible.role: Accessible.RadioButton
+ Accessible.description: "Radio Button"
+ })", QUrl());
+ auto button = std::unique_ptr<QObject>(component.create());
+ QVERIFY(button);
+
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(button.get());
+ QVERIFY(iface);
+
+ QCOMPARE(iface->role(), QAccessible::RadioButton);
+ QTestAccessibility::clearEvents();
+}
+
void tst_QQuickAccessible::basicPropertiesTest()
{
@@ -643,6 +671,34 @@ void tst_QQuickAccessible::ignoredTest()
QTestAccessibility::clearEvents();
}
+void tst_QQuickAccessible::passwordTest()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick\nTextInput {\n"
+ "Accessible.role: Accessible.EditableText\n"
+ "Accessible.name: \"Password\"\n"
+ "Accessible.passwordEdit: true\n"
+ "echoMode: TextInput.Password\n"
+ "text: \"Green\"\n"
+ "}", QUrl());
+ auto object = std::unique_ptr<QObject>(component.create());
+ QVERIFY(object != nullptr);
+
+ QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(object.get());
+ QVERIFY(textInput != nullptr);
+
+ const auto passwordCharacter = textInput->passwordCharacter();
+ const auto passwordLength = textInput->text().length();
+ const auto password = passwordCharacter.repeated(passwordLength);
+
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object.get());
+ QVERIFY(iface);
+ QCOMPARE(iface->text(QAccessible::Value), password);
+
+ QTestAccessibility::clearEvents();
+}
+
QTEST_MAIN(tst_QQuickAccessible)
#include "tst_qquickaccessible.moc"
diff --git a/tests/auto/quick/qquickanchors/CMakeLists.txt b/tests/auto/quick/qquickanchors/CMakeLists.txt
index af46206a7c..9bc4d5203b 100644
--- a/tests/auto/quick/qquickanchors/CMakeLists.txt
+++ b/tests/auto/quick/qquickanchors/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickanchors Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickanchors LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickanchors/data/centerin.qml b/tests/auto/quick/qquickanchors/data/centerin.qml
index b880219f0f..94c344b4e8 100644
--- a/tests/auto/quick/qquickanchors/data/centerin.qml
+++ b/tests/auto/quick/qquickanchors/data/centerin.qml
@@ -22,4 +22,11 @@ Rectangle {
anchors.centerIn: parent;
anchors.alignWhenCentered: false
}
+
+ Rectangle {
+ objectName: "centered4"
+ width: 0.9; height: 0.9; color: "plum"
+ anchors.centerIn: parent;
+ anchors.alignWhenCentered: false
+ }
}
diff --git a/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp b/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
index 7dd3eaece0..508a3906fc 100644
--- a/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
+++ b/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QSignalSpy>
#include <private/qquickitem_p.h>
@@ -143,15 +144,16 @@ void tst_qquickanchors::basicAnchorsRTL()
qApp->processEvents();
QQuickItem* rootItem = qobject_cast<QQuickItem*>(view->rootObject());
- foreach (QObject *child, rootItem->children()) {
+ const QObjectList children = rootItem->children();
+ for (QObject *child : children) {
bool mirrored = QQuickItemPrivate::get(qobject_cast<QQuickItem*>(child))->anchors()->mirrored();
QCOMPARE(mirrored, false);
}
- foreach (QObject *child, rootItem->children())
+ for (QObject *child : children)
mirrorAnchors(qobject_cast<QQuickItem*>(child));
- foreach (QObject *child, rootItem->children()) {
+ for (QObject *child : children) {
bool mirrored = QQuickItemPrivate::get(qobject_cast<QQuickItem*>(child))->anchors()->mirrored();
QCOMPARE(mirrored, true);
}
@@ -273,7 +275,8 @@ void tst_qquickanchors::illegalSets_data()
<< "Rectangle { id: rect; Rectangle { anchors.left: rect.left; anchors.right: rect.right; anchors.horizontalCenter: rect.horizontalCenter } }"
<< "<Unknown File>:2:23: QML Rectangle: Cannot specify left, right, and horizontalCenter anchors at the same time.";
- foreach (const QString &side, QStringList() << "left" << "right") {
+ const QStringList leftRight = {"left", "right"};
+ for (const QString &side : leftRight) {
QTest::newRow("H - anchor to V")
<< QString("Rectangle { Rectangle { anchors.%1: parent.top } }").arg(side)
<< "<Unknown File>:2:13: QML Rectangle: Cannot anchor a horizontal edge to a vertical edge.";
@@ -296,7 +299,8 @@ void tst_qquickanchors::illegalSets_data()
<< "Rectangle { Text { id: text1; text: \"Hello\" } Text { anchors.baseline: text1.baseline; anchors.top: text1.top; } }"
<< "<Unknown File>:2:47: QML Text: Baseline anchor cannot be used in conjunction with top, bottom, or verticalCenter anchors.";
- foreach (const QString &side, QStringList() << "top" << "bottom" << "baseline") {
+ const QStringList topBottomBaseline = {"top", "bottom", "baseline"};
+ for (const QString &side : topBottomBaseline) {
QTest::newRow("V - anchor to H")
<< QString("Rectangle { Rectangle { anchors.%1: parent.left } }").arg(side)
@@ -516,6 +520,15 @@ void tst_qquickanchors::centerIn()
QCOMPARE(rect3->x(), 94.5);
QCOMPARE(rect3->y(), 94.5);
+ //QTBUG-95224 (fractional positions are not center-rounded correctly)
+ // The center anchor lines on the parent will be rounded from 41.2 / 2 == 20.6 to 21
+ // rect4 has anchors.alignWhenCentered: false, so no rounding will be done on that items position.
+ // As a result, the expected position of rect4 will be 21 - 0.9/2 = 20.55
+ QQuickRectangle* rect4 = findItem<QQuickRectangle>(view->rootObject(), QLatin1String("centered4"));
+ view->rootObject()->setWidth(41.2);
+ view->rootObject()->setHeight(41.2);
+ QCOMPARE(rect4->x(), 20.55);
+ QCOMPARE(rect4->y(), 20.55);
delete view;
}
diff --git a/tests/auto/quick/qquickanimatedimage/CMakeLists.txt b/tests/auto/quick/qquickanimatedimage/CMakeLists.txt
index 2d58301f15..109e84cc14 100644
--- a/tests/auto/quick/qquickanimatedimage/CMakeLists.txt
+++ b/tests/auto/quick/qquickanimatedimage/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickanimatedimage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickanimatedimage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickanimatedimage/data/currentframe.qml b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml
index b679da2a99..3c58bdaa4b 100644
--- a/tests/auto/quick/qquickanimatedimage/data/currentframe.qml
+++ b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml
@@ -4,6 +4,7 @@ AnimatedImage {
property int currentFrameChangeCount: 0
property int frameChangeCount: 0
source: "stickman.gif"
+ paused: true
onCurrentFrameChanged: if (currentFrame > 0) ++currentFrameChangeCount;
onFrameChanged: if (currentFrame > 0) ++frameChangeCount;
function scriptedSetCurrentFrame(frame) {
diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
index cb81ec8a07..efea42de34 100644
--- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
+++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlexpression.h>
@@ -62,6 +62,7 @@ private slots:
void noCaching();
void sourceChangesOnFrameChanged();
void currentFrame();
+ void qtbug_120555();
};
void tst_qquickanimatedimage::cleanup()
@@ -646,6 +647,36 @@ void tst_qquickanimatedimage::currentFrame()
QCOMPARE(anim->property("frameChangeCount"), 2);
}
+void tst_qquickanimatedimage::qtbug_120555()
+{
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ server.serveDirectory(dataDirectory());
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\nAnimatedImage {}", {});
+
+ QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage*>(component.create());
+ QVERIFY(anim);
+
+ anim->setSource(server.url("/stickman.gif"));
+ QTRY_COMPARE(anim->status(), QQuickImage::Loading);
+
+ anim->setFillMode(QQuickImage::PreserveAspectFit);
+ QCOMPARE(anim->fillMode(), QQuickImage::PreserveAspectFit);
+ anim->setMipmap(true);
+ QCOMPARE(anim->mipmap(), true);
+ anim->setCache(false);
+ QCOMPARE(anim->cache(), false);
+ anim->setSourceSize(QSize(200, 200));
+ QCOMPARE(anim->sourceSize(), QSize(200, 200));
+
+ QTRY_COMPARE(anim->status(), QQuickImage::Ready);
+
+ delete anim;
+}
+
QTEST_MAIN(tst_qquickanimatedimage)
#include "tst_qquickanimatedimage.moc"
diff --git a/tests/auto/quick/qquickanimatedsprite/CMakeLists.txt b/tests/auto/quick/qquickanimatedsprite/CMakeLists.txt
index bb789eab09..36117df988 100644
--- a/tests/auto/quick/qquickanimatedsprite/CMakeLists.txt
+++ b/tests/auto/quick/qquickanimatedsprite/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickanimatedsprite Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickanimatedsprite LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickanimatedsprite/data/basic.qml b/tests/auto/quick/qquickanimatedsprite/data/basic.qml
index 2985db31ec..a6564a8983 100644
--- a/tests/auto/quick/qquickanimatedsprite/data/basic.qml
+++ b/tests/auto/quick/qquickanimatedsprite/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickanimatedsprite/data/frameChange.qml b/tests/auto/quick/qquickanimatedsprite/data/frameChange.qml
index e5cbcb98d3..dbbbc222b3 100644
--- a/tests/auto/quick/qquickanimatedsprite/data/frameChange.qml
+++ b/tests/auto/quick/qquickanimatedsprite/data/frameChange.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickanimatedsprite/data/infiniteLoops.qml b/tests/auto/quick/qquickanimatedsprite/data/infiniteLoops.qml
index 3e02ad2648..2771716774 100644
--- a/tests/auto/quick/qquickanimatedsprite/data/infiniteLoops.qml
+++ b/tests/auto/quick/qquickanimatedsprite/data/infiniteLoops.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickanimatedsprite/data/largeAnimation.qml b/tests/auto/quick/qquickanimatedsprite/data/largeAnimation.qml
index 6618b45ae0..bfff9c9d29 100644
--- a/tests/auto/quick/qquickanimatedsprite/data/largeAnimation.qml
+++ b/tests/auto/quick/qquickanimatedsprite/data/largeAnimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickanimatedsprite/data/runningChange.qml b/tests/auto/quick/qquickanimatedsprite/data/runningChange.qml
index ba64269eb7..7c746cae91 100644
--- a/tests/auto/quick/qquickanimatedsprite/data/runningChange.qml
+++ b/tests/auto/quick/qquickanimatedsprite/data/runningChange.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Tasuku Suzuki <stasuku@gmail.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml b/tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml
index 3ba335b152..319579d2e1 100644
--- a/tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml
+++ b/tests/auto/quick/qquickanimatedsprite/data/sourceSwitch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
diff --git a/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp b/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp
index 72c807bdf8..f7ed660f88 100644
--- a/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp
+++ b/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuick/qquickview.h>
diff --git a/tests/auto/quick/qquickanimationcontroller/CMakeLists.txt b/tests/auto/quick/qquickanimationcontroller/CMakeLists.txt
index 42c219afea..44c3e4d797 100644
--- a/tests/auto/quick/qquickanimationcontroller/CMakeLists.txt
+++ b/tests/auto/quick/qquickanimationcontroller/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickanimationcontroller Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickanimationcontroller LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickanimationcontroller/tst_qquickanimationcontroller.cpp b/tests/auto/quick/qquickanimationcontroller/tst_qquickanimationcontroller.cpp
index b231f121e6..41b3ae2ac7 100644
--- a/tests/auto/quick/qquickanimationcontroller/tst_qquickanimationcontroller.cpp
+++ b/tests/auto/quick/qquickanimationcontroller/tst_qquickanimationcontroller.cpp
@@ -1,4 +1,4 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(qquickanimationcontroller)
diff --git a/tests/auto/quick/qquickanimations/CMakeLists.txt b/tests/auto/quick/qquickanimations/CMakeLists.txt
index 5e7623afd1..1b6b0b84a5 100644
--- a/tests/auto/quick/qquickanimations/CMakeLists.txt
+++ b/tests/auto/quick/qquickanimations/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickanimations Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickanimations LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickanimations/data/animationJobSelfDestructionBug.qml b/tests/auto/quick/qquickanimations/data/animationJobSelfDestructionBug.qml
index 93fc498613..a9123427d5 100644
--- a/tests/auto/quick/qquickanimations/data/animationJobSelfDestructionBug.qml
+++ b/tests/auto/quick/qquickanimations/data/animationJobSelfDestructionBug.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.11
import QtQuick.Window 2.11
diff --git a/tests/auto/quick/qquickanimations/data/animatorInvalidTargetCrash.qml b/tests/auto/quick/qquickanimations/data/animatorInvalidTargetCrash.qml
index c575ce33ab..c69dc2a065 100644
--- a/tests/auto/quick/qquickanimations/data/animatorInvalidTargetCrash.qml
+++ b/tests/auto/quick/qquickanimations/data/animatorInvalidTargetCrash.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
import QtQuick.Window 2.2
diff --git a/tests/auto/quick/qquickanimations/data/changePropertiesDuringAnimation.qml b/tests/auto/quick/qquickanimations/data/changePropertiesDuringAnimation.qml
index c82fe4bb52..322bd9c0e4 100644
--- a/tests/auto/quick/qquickanimations/data/changePropertiesDuringAnimation.qml
+++ b/tests/auto/quick/qquickanimations/data/changePropertiesDuringAnimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window 2.2
diff --git a/tests/auto/quick/qquickanimations/data/fastFlickingBug.qml b/tests/auto/quick/qquickanimations/data/fastFlickingBug.qml
index 68bb618aca..1c653f59d2 100644
--- a/tests/auto/quick/qquickanimations/data/fastFlickingBug.qml
+++ b/tests/auto/quick/qquickanimations/data/fastFlickingBug.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.11
import QtQuick.Window 2.11
diff --git a/tests/auto/quick/qquickanimations/data/finished.qml b/tests/auto/quick/qquickanimations/data/finished.qml
index f5b19b037e..ba74a77ce4 100644
--- a/tests/auto/quick/qquickanimations/data/finished.qml
+++ b/tests/auto/quick/qquickanimations/data/finished.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/qquickanimations/data/frameAnimation.qml b/tests/auto/quick/qquickanimations/data/frameAnimation.qml
index 2b0cd7f3a7..6ed305db60 100644
--- a/tests/auto/quick/qquickanimations/data/frameAnimation.qml
+++ b/tests/auto/quick/qquickanimations/data/frameAnimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquickanimations/data/infiniteAnimationWithoutFrom.qml b/tests/auto/quick/qquickanimations/data/infiniteAnimationWithoutFrom.qml
index 6ae45ce9d9..21051e030e 100644
--- a/tests/auto/quick/qquickanimations/data/infiniteAnimationWithoutFrom.qml
+++ b/tests/auto/quick/qquickanimations/data/infiniteAnimationWithoutFrom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickanimations/data/restartAnimationGroupWhenDirty.qml b/tests/auto/quick/qquickanimations/data/restartAnimationGroupWhenDirty.qml
new file mode 100644
index 0000000000..aec1cec432
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/data/restartAnimationGroupWhenDirty.qml
@@ -0,0 +1,92 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 300
+
+ // test SequentialAnimation
+ Rectangle {
+ id: line0
+ y: 100
+ width: parent.width
+ height: 2
+ color: "blue"
+ }
+ Rectangle {
+ id: target0
+ objectName: "target0"
+ y: 100
+ anchors.verticalCenter: line0.verticalCenter
+ height: line0.height * 5
+ width: height
+ color: "red"
+ radius: height/2
+
+ property bool onFinishedCalled : false;
+
+ SequentialAnimation {
+ id: seqAnim0
+ objectName: "seqAnim0"
+ loops: 2
+ running: true
+ NumberAnimation {
+ id: anim0
+ target: target0
+ property: "x"
+ from: 0
+ to: 50
+ duration: 500
+ }
+ Component.onCompleted: anim0.to = 290
+ onFinished: target0.onFinishedCalled = true
+ }
+ }
+
+ // test ParallelAnimation
+ Rectangle {
+ id: line1
+ y: 200
+ width: parent.width
+ height: 2
+ color: "blue"
+ }
+ Rectangle {
+ id: target1
+ objectName: "target1"
+ anchors.verticalCenter: line1.verticalCenter
+ height: line1.height * 5
+ width: height
+ color: "yellow"
+ radius: height/2
+
+ property bool onFinishedCalled : false;
+
+ ParallelAnimation {
+ id: parAnim0
+ objectName: "parAnim0"
+ loops: 2
+ running: true
+ NumberAnimation {
+ id: anim1
+ target: target1
+ property: "x"
+ from: 0
+ to: 50
+ duration: 500
+ }
+ Component.onCompleted: anim1.to = 290
+ onFinished: target1.onFinishedCalled = true
+ }
+ }
+
+ Timer {
+ interval: 400
+ running: true
+ onTriggered: {
+ seqAnim0.pause()
+ parAnim0.pause()
+ anim0.to = 140
+ anim1.to = 140
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickanimations/data/restartNestedAnimationGroupWhenDirty.qml b/tests/auto/quick/qquickanimations/data/restartNestedAnimationGroupWhenDirty.qml
new file mode 100644
index 0000000000..da0de96448
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/data/restartNestedAnimationGroupWhenDirty.qml
@@ -0,0 +1,96 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 300
+
+ // test ParallelAnimation in SequentialAnimation
+ Rectangle {
+ id: line0
+ y: 100
+ width: parent.width
+ height: 2
+ color: "blue"
+ }
+ Rectangle {
+ id: target0
+ objectName: "target0"
+ y: 100
+ anchors.verticalCenter: line0.verticalCenter
+ height: line0.height * 5
+ width: height
+ color: "red"
+ radius: height/2
+
+ property bool onFinishedCalled : false;
+
+ SequentialAnimation {
+ id: seqAnim0
+ objectName: "seqAnim0"
+ loops: 2
+ running: true
+ ParallelAnimation {
+ NumberAnimation {
+ id: anim0
+ target: target0
+ property: "x"
+ from: 0
+ to: 50
+ duration: 500
+ }
+ }
+ Component.onCompleted: anim0.to = 290
+ onFinished: target0.onFinishedCalled = true
+ }
+ }
+
+ // test SequentialAnimation in ParallelAnimation
+ Rectangle {
+ id: line1
+ y: 200
+ width: parent.width
+ height: 2
+ color: "blue"
+ }
+ Rectangle {
+ id: target1
+ objectName: "target1"
+ anchors.verticalCenter: line1.verticalCenter
+ height: line1.height * 5
+ width: height
+ color: "yellow"
+ radius: height/2
+
+ property bool onFinishedCalled : false;
+
+ ParallelAnimation {
+ id: parAnim0
+ objectName: "parAnim0"
+ loops: 2
+ running: true
+ SequentialAnimation {
+ NumberAnimation {
+ id: anim1
+ target: target1
+ property: "x"
+ from: 0
+ to: 50
+ duration: 500
+ }
+ }
+ Component.onCompleted: anim1.to = 290
+ onFinished: target1.onFinishedCalled = true
+ }
+ }
+
+ Timer {
+ interval: 400
+ running: true
+ onTriggered: {
+ seqAnim0.pause()
+ parAnim0.pause()
+ anim0.to = 140
+ anim1.to = 140
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickanimations/data/scriptActionCrash.qml b/tests/auto/quick/qquickanimations/data/scriptActionCrash.qml
index b2e98550c0..ace63918d8 100644
--- a/tests/auto/quick/qquickanimations/data/scriptActionCrash.qml
+++ b/tests/auto/quick/qquickanimations/data/scriptActionCrash.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml b/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml
new file mode 100644
index 0000000000..e31caa1905
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml
@@ -0,0 +1,29 @@
+import QtQuick
+
+Item {
+ id: root
+
+ property list<Translate> targets
+ property alias animTargets: animation.targets
+
+ Component {
+ id: trComponent
+ Translate {}
+ }
+
+ Component.onCompleted: {
+ const target = trComponent.createObject(this);
+ targets.push(target);
+ target.destroy();
+ // give event loop some time to actually stop the animation and destroy the target
+ Qt.callLater(animation.start);
+ }
+
+ NumberAnimation {
+ id: animation
+ targets: root.targets
+ property: "x"
+ running: false
+ to: 100
+ }
+}
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index f7a84ca979..25c8559ed3 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -98,6 +98,9 @@ private slots:
void infiniteLoopsWithoutFrom();
void frameAnimation1();
void frameAnimation2();
+ void restartAnimationGroupWhenDirty();
+ void restartNestedAnimationGroupWhenDirty();
+ void targetsDeletedNotRemoved();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
@@ -2212,6 +2215,86 @@ void tst_qquickanimations::frameAnimation2()
QVERIFY(frameAnimation->currentFrame() > 3);
}
+//QTBUG-110589
+void tst_qquickanimations::restartAnimationGroupWhenDirty()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restartAnimationGroupWhenDirty.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ auto *root = qobject_cast<QQuickRectangle*>(obj.data());
+ QVERIFY2(root, qPrintable(c.errorString()));
+
+ QQuickSequentialAnimation *seqAnim0 = root->findChild<QQuickSequentialAnimation*>("seqAnim0");
+ QVERIFY(seqAnim0);
+ QQuickRectangle *target0 = root->findChild<QQuickRectangle*>("target0");
+ QVERIFY(target0);
+ QQuickParallelAnimation *parAnim0 = root->findChild<QQuickParallelAnimation*>("parAnim0");
+ QVERIFY(parAnim0);
+ QQuickRectangle *target1 = root->findChild<QQuickRectangle*>("target1");
+ QVERIFY(target1);
+
+ QTRY_VERIFY(seqAnim0->isPaused());
+ QTRY_VERIFY(parAnim0->isPaused());
+ QTRY_VERIFY(target0->x() > 140);
+ QTRY_VERIFY(target1->x() > 140);
+ seqAnim0->resume();
+ parAnim0->resume();
+ QTRY_VERIFY(target0->property("onFinishedCalled").value<bool>());
+ QTRY_VERIFY(target1->property("onFinishedCalled").value<bool>());
+ QTRY_COMPARE(target0->x(), 140);
+ QTRY_COMPARE(target1->x(), 140);
+}
+
+//QTBUG-95840
+void tst_qquickanimations::restartNestedAnimationGroupWhenDirty()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restartNestedAnimationGroupWhenDirty.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ auto *root = qobject_cast<QQuickRectangle*>(obj.data());
+ QVERIFY2(root, qPrintable(c.errorString()));
+
+ QQuickSequentialAnimation *seqAnim0 = root->findChild<QQuickSequentialAnimation*>("seqAnim0");
+ QVERIFY(seqAnim0);
+ QQuickRectangle *target0 = root->findChild<QQuickRectangle*>("target0");
+ QVERIFY(target0);
+ QQuickParallelAnimation *parAnim0 = root->findChild<QQuickParallelAnimation*>("parAnim0");
+ QVERIFY(parAnim0);
+ QQuickRectangle *target1 = root->findChild<QQuickRectangle*>("target1");
+ QVERIFY(target1);
+
+ QTRY_VERIFY(seqAnim0->isPaused());
+ QTRY_VERIFY(parAnim0->isPaused());
+ QTRY_VERIFY(target0->x() > 140);
+ QTRY_VERIFY(target1->x() > 140);
+ seqAnim0->resume();
+ parAnim0->resume();
+ QTRY_VERIFY(target0->property("onFinishedCalled").value<bool>());
+ QTRY_VERIFY(target1->property("onFinishedCalled").value<bool>());
+ QTRY_COMPARE(target0->x(), 140);
+ QTRY_COMPARE(target1->x(), 140);
+}
+
+void tst_qquickanimations::targetsDeletedNotRemoved()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("targetsDeletedWithoutRemoval.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj.get(), qPrintable(component.errorString()));
+ {
+ QQmlListReference ref(obj.get(), "targets");
+ QVERIFY(ref.isValid());
+ QCOMPARE(ref.size(), 1);
+ QTRY_COMPARE(ref.at(0), nullptr);
+ }
+ {
+ QQmlListReference ref(obj.get(), "animTargets");
+ QVERIFY(ref.isValid());
+ QCOMPARE(ref.size(), 1);
+ QCOMPARE(ref.at(0), nullptr);
+ }
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"
diff --git a/tests/auto/quick/qquickanimators/BLACKLIST b/tests/auto/quick/qquickanimators/BLACKLIST
index 019bf71d5e..f0da331c9f 100644
--- a/tests/auto/quick/qquickanimators/BLACKLIST
+++ b/tests/auto/quick/qquickanimators/BLACKLIST
@@ -1,4 +1,4 @@
# QTBUG-89023
[testTransitionsWithImplicitFrom]
-ubuntu-20.04
+ubuntu-22.04
opensuse-leap
diff --git a/tests/auto/quick/qquickanimators/CMakeLists.txt b/tests/auto/quick/qquickanimators/CMakeLists.txt
index c8f1f9819e..4865baa4cf 100644
--- a/tests/auto/quick/qquickanimators/CMakeLists.txt
+++ b/tests/auto/quick/qquickanimators/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickanimators Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickanimators LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickanimators/data/animatorImplicitFrom.qml b/tests/auto/quick/qquickanimators/data/animatorImplicitFrom.qml
index 1893fdc800..c291fcfd95 100644
--- a/tests/auto/quick/qquickanimators/data/animatorImplicitFrom.qml
+++ b/tests/auto/quick/qquickanimators/data/animatorImplicitFrom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml b/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml
index f1bdea3b9d..979d7df1d5 100644
--- a/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml
+++ b/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/qquickanimators/data/windowWithAnimator.qml b/tests/auto/quick/qquickanimators/data/windowWithAnimator.qml
index 61a8b86a95..95b22c3a20 100644
--- a/tests/auto/quick/qquickanimators/data/windowWithAnimator.qml
+++ b/tests/auto/quick/qquickanimators/data/windowWithAnimator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtQuick.Window 2.0
diff --git a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
index 92f75620bd..f6bc136422 100644
--- a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
+++ b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTest/quicktest.h>
diff --git a/tests/auto/quick/qquickapplication/BLACKLIST b/tests/auto/quick/qquickapplication/BLACKLIST
deleted file mode 100644
index 1b7464e7c4..0000000000
--- a/tests/auto/quick/qquickapplication/BLACKLIST
+++ /dev/null
@@ -1,3 +0,0 @@
-[active]
-opensuse-42.3
-opensuse-leap
diff --git a/tests/auto/quick/qquickapplication/CMakeLists.txt b/tests/auto/quick/qquickapplication/CMakeLists.txt
index 74f7075d33..25dd31046a 100644
--- a/tests/auto/quick/qquickapplication/CMakeLists.txt
+++ b/tests/auto/quick/qquickapplication/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickapplication Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickapplication LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
index a440c0c2f8..48b9b833d5 100644
--- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
+++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlcomponent.h>
@@ -93,13 +93,13 @@ void tst_qquickapplication::active()
window.show();
window.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&window));
- QCOMPARE(QGuiApplication::focusWindow(), &window);
+ QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QVERIFY(item->property("active").toBool());
QVERIFY(item->property("active2").toBool());
// not active again
- QWindowSystemInterface::handleWindowActivated(nullptr);
- QTRY_VERIFY(QGuiApplication::focusWindow() != &window);
+ QWindowSystemInterface::handleFocusWindowChanged(nullptr);
+ QTRY_COMPARE_NE(QGuiApplication::focusWindow(), &window);
QVERIFY(!item->property("active").toBool());
QVERIFY(!item->property("active2").toBool());
}
@@ -166,13 +166,13 @@ void tst_qquickapplication::state()
window.show();
window.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&window));
- QCOMPARE(QGuiApplication::focusWindow(), &window);
+ QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QCOMPARE(Qt::ApplicationState(item->property("state").toInt()), Qt::ApplicationActive);
QCOMPARE(Qt::ApplicationState(item->property("state2").toInt()), Qt::ApplicationActive);
// not active again
- QWindowSystemInterface::handleWindowActivated(nullptr);
- QTRY_VERIFY(QGuiApplication::focusWindow() != &window);
+ QWindowSystemInterface::handleFocusWindowChanged(nullptr);
+ QTRY_COMPARE_NE(QGuiApplication::focusWindow(), &window);
QCOMPARE(Qt::ApplicationState(item->property("state").toInt()),
Qt::ApplicationInactive);
QCOMPARE(Qt::ApplicationState(item->property("state2").toInt()),
diff --git a/tests/auto/quick/qquickbehaviors/CMakeLists.txt b/tests/auto/quick/qquickbehaviors/CMakeLists.txt
index b66226776a..4c79b376a8 100644
--- a/tests/auto/quick/qquickbehaviors/CMakeLists.txt
+++ b/tests/auto/quick/qquickbehaviors/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickbehaviors Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickbehaviors LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickbehaviors/bindable.h b/tests/auto/quick/qquickbehaviors/bindable.h
index aa6acc6615..0eeed6f87a 100644
--- a/tests/auto/quick/qquickbehaviors/bindable.h
+++ b/tests/auto/quick/qquickbehaviors/bindable.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef BINDABLE_H
#define BINDABLE_H
diff --git a/tests/auto/quick/qquickbehaviors/data/duplicated.qml b/tests/auto/quick/qquickbehaviors/data/duplicated.qml
new file mode 100644
index 0000000000..15518a05cf
--- /dev/null
+++ b/tests/auto/quick/qquickbehaviors/data/duplicated.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+Rectangle {
+ id: main
+
+ width: 400
+ height: 400
+
+ property bool behavior1Triggered
+ property bool behavior2Triggered
+
+ Behavior on x {
+ ScriptAction { script: behavior1Triggered = true }
+ }
+ Behavior on x {
+ ScriptAction { script: behavior2Triggered = true }
+ }
+
+ MouseArea {
+ id: clicker
+ anchors.fill: parent
+ }
+
+ states: State {
+ name: "moved"
+ when: clicker.pressed
+ PropertyChanges {
+ target: main
+ x: 200
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickbehaviors/data/qtbug21549-2.qml b/tests/auto/quick/qquickbehaviors/data/qtbug21549-2.qml
index 23804af711..4c3d4a0d26 100644
--- a/tests/auto/quick/qquickbehaviors/data/qtbug21549-2.qml
+++ b/tests/auto/quick/qquickbehaviors/data/qtbug21549-2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
index 5ea11729d8..e9720130f2 100644
--- a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
+++ b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <qsignalspy.h>
#include <QtQml/qqmlengine.h>
@@ -33,6 +33,7 @@ private slots:
void group();
void valueType();
void emptyBehavior();
+ void duplicatedBehavior();
void explicitSelection();
void nonSelectingBehavior();
void reassignedAnimation();
@@ -223,6 +224,20 @@ void tst_qquickbehaviors::emptyBehavior()
QCOMPARE(x, qreal(200)); //should change immediately
}
+void tst_qquickbehaviors::duplicatedBehavior()
+{
+ QTest::failOnWarning(QRegularExpression(".*"));
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg,
+ QRegularExpression("Attempting to set another interceptor on.*"));
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("duplicated.qml"));
+ QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle *>(c.create()));
+ QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
+
+ // Expecting no crash
+ QQuickItemPrivate::get(rect.data())->setState("moved");
+}
+
void tst_qquickbehaviors::explicitSelection()
{
QQmlEngine engine;
diff --git a/tests/auto/quick/qquickborderimage/CMakeLists.txt b/tests/auto/quick/qquickborderimage/CMakeLists.txt
index 1585177a0a..5d88680367 100644
--- a/tests/auto/quick/qquickborderimage/CMakeLists.txt
+++ b/tests/auto/quick/qquickborderimage/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickborderimage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickborderimage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
index c1b9818a00..83396c6944 100644
--- a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
+++ b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QTextDocument>
#include <QTcpServer>
@@ -45,6 +45,7 @@ private slots:
void sciSource();
void sciSource_data();
void invalidSciFile();
+ void nonExistingSciFile();
void validSciFiles_data();
void validSciFiles();
void pendingRemoteRequest();
@@ -335,6 +336,21 @@ void tst_qquickborderimage::invalidSciFile()
delete obj;
}
+void tst_qquickborderimage::nonExistingSciFile()
+{
+ const QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + testFileUrl("this_file_does_not_exist.sci").toString() +"\"; width: 300; height: 300 }";
+ QQmlComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ std::unique_ptr<QObject> obj(component.create());
+ auto bimage = qobject_cast<QQuickBorderImage *>(obj.get());
+ QVERIFY(bimage != nullptr);
+ QCOMPARE(bimage->width(), 300.);
+ QCOMPARE(bimage->height(), 300.);
+ QCOMPARE(bimage->status(), QQuickImageBase::Error);
+ QCOMPARE(bimage->horizontalTileMode(), QQuickBorderImage::Stretch);
+ QCOMPARE(bimage->verticalTileMode(), QQuickBorderImage::Stretch);
+}
+
void tst_qquickborderimage::validSciFiles_data()
{
QTest::addColumn<QString>("source");
diff --git a/tests/auto/quick/qquickboundaryrule/CMakeLists.txt b/tests/auto/quick/qquickboundaryrule/CMakeLists.txt
index 563102911c..c969657c3e 100644
--- a/tests/auto/quick/qquickboundaryrule/CMakeLists.txt
+++ b/tests/auto/quick/qquickboundaryrule/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickboundaryrule Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickboundaryrule LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
index 2964238849..c301848b1e 100644
--- a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
+++ b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <qsignalspy.h>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quick/qquickcanvasitem/BLACKLIST b/tests/auto/quick/qquickcanvasitem/BLACKLIST
index ec70b00e36..c7245e7633 100644
--- a/tests/auto/quick/qquickcanvasitem/BLACKLIST
+++ b/tests/auto/quick/qquickcanvasitem/BLACKLIST
@@ -6,6 +6,7 @@ macos
windows
macos
# QTBUG-41043
+android
[canvas::test_toDataURL]
*
[fillRect::test_fillRect]
diff --git a/tests/auto/quick/qquickcanvasitem/CMakeLists.txt b/tests/auto/quick/qquickcanvasitem/CMakeLists.txt
index f4fe685b83..fdc1416a32 100644
--- a/tests/auto/quick/qquickcanvasitem/CMakeLists.txt
+++ b/tests/auto/quick/qquickcanvasitem/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickcanvasitem Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickcanvasitem LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml b/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml
index 33fffd4cb1..00d13f31f4 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_angle_1(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -21,6 +22,7 @@ CanvasTestCase {
}
function test_angle_2(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -36,6 +38,7 @@ CanvasTestCase {
}
function test_angle_3(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -53,6 +56,7 @@ CanvasTestCase {
}
function test_angle_4(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -71,6 +75,7 @@ CanvasTestCase {
}
function test_angle_5(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -90,6 +95,7 @@ CanvasTestCase {
function test_angle_6(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -110,6 +116,7 @@ CanvasTestCase {
function test_empty(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -125,6 +132,7 @@ CanvasTestCase {
}
function test_nonempty(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -141,6 +149,7 @@ CanvasTestCase {
}
function test_nonfinite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -199,6 +208,7 @@ CanvasTestCase {
}
function test_end(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -216,6 +226,7 @@ CanvasTestCase {
}
function test_negative(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -234,6 +245,7 @@ CanvasTestCase {
function test_scale_1(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -270,6 +282,7 @@ CanvasTestCase {
function test_scale_2(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -296,6 +309,7 @@ CanvasTestCase {
function test_selfintersect_1(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -316,6 +330,7 @@ CanvasTestCase {
function test_selfintersect_2(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -340,6 +355,7 @@ CanvasTestCase {
function test_shape_1(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -361,6 +377,7 @@ CanvasTestCase {
function test_shape_2(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -381,6 +398,7 @@ CanvasTestCase {
}
function test_shape_3(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -401,6 +419,7 @@ CanvasTestCase {
function test_shape_4(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -421,6 +440,7 @@ CanvasTestCase {
function test_shape_5(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -441,6 +461,7 @@ CanvasTestCase {
function test_twopie(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -488,6 +509,7 @@ CanvasTestCase {
function test_zero(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml b/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml
index ef1b7a7b2a..81412d98c9 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml
@@ -7,6 +7,7 @@ CanvasTestCase {
function test_coincide(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -44,6 +45,7 @@ CanvasTestCase {
}
function test_collinear(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -109,6 +111,7 @@ CanvasTestCase {
}
function test_subpath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -135,6 +138,7 @@ CanvasTestCase {
function test_negative(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -152,6 +156,7 @@ CanvasTestCase {
function test_nonfinite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -208,6 +213,7 @@ CanvasTestCase {
}
function test_scale(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -237,6 +243,7 @@ CanvasTestCase {
function test_shape(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -348,6 +355,7 @@ CanvasTestCase {
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -374,6 +382,7 @@ CanvasTestCase {
}
function test_zero(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml b/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml
index 8a5a52cf1e..4ee24ad437 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_clearRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -17,6 +18,7 @@ CanvasTestCase {
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var composites = [ {compsite:"copy"},
{compsite:"destination-atop"},
@@ -47,6 +49,7 @@ CanvasTestCase {
function test_globalAlpha(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
compare(ctx.globalAlpha, 1.0);
@@ -83,6 +86,7 @@ CanvasTestCase {
function test_operation(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.globalCompositeOperation = 'xor';
@@ -137,6 +141,7 @@ CanvasTestCase {
function test_solid(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = Qt.rgba(0, 1, 1, 1.0);
@@ -233,6 +238,7 @@ CanvasTestCase {
}
function test_transparent(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
@@ -330,6 +336,7 @@ CanvasTestCase {
function test_uncovered(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml b/tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml
index 58ea6d7f59..9e8c0785c3 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_fillStyle.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_default(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
verify(ctx.fillStyle, "#000000");
@@ -14,6 +15,7 @@ CanvasTestCase {
}
function test_get(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#fa0';
@@ -23,6 +25,7 @@ CanvasTestCase {
}
function test_hex(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -37,6 +40,7 @@ CanvasTestCase {
}
function test_invalid(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#fa0';
@@ -60,6 +64,7 @@ CanvasTestCase {
}
function test_saverestore(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var old = ctx.fillStyle;
ctx.save();
@@ -75,6 +80,7 @@ CanvasTestCase {
}
function test_namedColor(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = "red";
@@ -91,6 +97,7 @@ CanvasTestCase {
}
function test_rgba(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = "rgb(-100, 300, 255)";
@@ -108,6 +115,7 @@ CanvasTestCase {
function test_hsla(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = "hsla(120, 100%, 50%, 0.499)";
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml b/tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml
index ce1c27c6bc..213abdd622 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_fillrect.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_fillRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml b/tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml
index 7c87d896fb..70e77c8760 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_gradient.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_basic(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -18,6 +19,7 @@ CanvasTestCase {
function test_interpolate(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -166,6 +168,7 @@ CanvasTestCase {
}
function test_radial(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -788,6 +791,7 @@ CanvasTestCase {
}
function test_linear(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
try { var err = false;
@@ -907,6 +911,7 @@ CanvasTestCase {
}
function test_object(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
var g1 = ctx.createLinearGradient(0, 0, 100, 0);
@@ -978,6 +983,7 @@ CanvasTestCase {
function test_conical(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
var g = ctx.createConicalGradient(10, 10, 50);
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
index 1f695d7080..a1eb272cd0 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
@@ -11,12 +11,12 @@ CanvasTestCase {
canvas.loadImage('rgrg-256x256.png');
canvas.loadImage('ggrr-256x256.png');
canvas.loadImage('broken.png');
- while (!canvas.isImageLoaded('green.png'))
- wait(200);
+ tryVerify(function() { return canvas.isImageLoaded('green.png'); })
}
function test_3args(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
ctx.reset();
@@ -35,6 +35,7 @@ CanvasTestCase {
}
function test_5args(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -54,6 +55,7 @@ CanvasTestCase {
}
function test_9args(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -118,6 +120,7 @@ CanvasTestCase {
}
function test_animated(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -125,6 +128,7 @@ CanvasTestCase {
}
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -139,6 +143,7 @@ CanvasTestCase {
}
function test_self(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -170,6 +175,7 @@ CanvasTestCase {
function test_outsidesource(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -207,6 +213,7 @@ CanvasTestCase {
function test_null(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -220,15 +227,18 @@ CanvasTestCase {
function test_url(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
canvas.loadImage(testCase.green);
+ tryVerify(function() { return canvas.isImageLoaded(testCase.green); })
ctx.drawImage(testCase.green, 0, 0);
comparePixel(ctx, 0,0, 0,255,0,255,2);
}
function test_composite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -241,12 +251,14 @@ CanvasTestCase {
}
function test_path(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
}
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -260,6 +272,7 @@ CanvasTestCase {
function test_imageitem(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -268,6 +281,7 @@ CanvasTestCase {
function test_imageData(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -276,6 +290,7 @@ CanvasTestCase {
function test_wrongtype(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -293,6 +308,7 @@ CanvasTestCase {
function test_nonfinite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -606,6 +622,7 @@ CanvasTestCase {
function test_negative(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -661,6 +678,7 @@ CanvasTestCase {
function test_canvas(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -684,6 +702,7 @@ CanvasTestCase {
function test_broken(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -694,6 +713,7 @@ CanvasTestCase {
function test_alpha(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
@@ -706,6 +726,7 @@ CanvasTestCase {
}
function test_multiple_painting(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
loadImages(canvas);
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml b/tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml
index 76b99a765e..aab7a95589 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_imagedata.qml
@@ -11,6 +11,7 @@ CanvasTestCase {
skip("ctx.getImageData crashes on offscreen/minimal platforms");
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var size = 17
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_invalidContext.qml b/tests/auto/quick/qquickcanvasitem/data/tst_invalidContext.qml
index 0df536cc02..904b0a3405 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_invalidContext.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_invalidContext.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
import QtTest 1.1
@@ -28,16 +28,16 @@ Item {
anchors.fill: parent
property var paintContext: null
- function paint() {
+ function doPaint() {
paintContext.fillStyle = Qt.rgba(1, 0, 0, 1);
paintContext.fillRect(0, 0, width, height);
- requestAnimationFrame(paint);
+ requestAnimationFrame(doPaint);
}
onAvailableChanged: {
if (available) {
paintContext = getContext("2d")
- requestAnimationFrame(paint);
+ requestAnimationFrame(doPaint);
}
}
}
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
index ba7ff09728..e5d03e5db7 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_line.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_default(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
compare(ctx.lineWidth, 1);
@@ -16,6 +17,7 @@ CanvasTestCase {
function test_cross(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -43,6 +45,7 @@ CanvasTestCase {
function test_join(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -306,6 +309,7 @@ CanvasTestCase {
}
function test_miter(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -481,6 +485,7 @@ CanvasTestCase {
}
function test_width(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -625,6 +630,7 @@ CanvasTestCase {
skip("line::test_cap crashes on Android, QTBUG-103257")
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -841,6 +847,7 @@ CanvasTestCase {
function test_lineDash(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.strokeStyle = "#fff";
@@ -899,6 +906,7 @@ CanvasTestCase {
function test_lineDashReset(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.strokeStyle = "#ff0000";
@@ -930,6 +938,7 @@ CanvasTestCase {
function test_lineDashOffset(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.strokeStyle = "#fff";
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_path.qml b/tests/auto/quick/qquickcanvasitem/data/tst_path.qml
index 60c782c8fa..ae08b58519 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_path.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_path.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_basic(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -36,6 +37,7 @@ CanvasTestCase {
}
function test_beginPath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@@ -48,6 +50,7 @@ CanvasTestCase {
}
function test_closePath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -86,6 +89,7 @@ CanvasTestCase {
function test_isPointInPath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.arc(50, 25, 10, 0, Math.PI, false);
@@ -256,6 +260,7 @@ CanvasTestCase {
function test_fill(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -378,6 +383,7 @@ CanvasTestCase {
}
function test_stroke(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -662,6 +668,7 @@ CanvasTestCase {
}
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -784,6 +791,7 @@ CanvasTestCase {
function test_moveTo(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -839,6 +847,7 @@ CanvasTestCase {
}
function test_lineTo(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -904,6 +913,7 @@ CanvasTestCase {
}
function test_bezierCurveTo(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -1072,6 +1082,7 @@ CanvasTestCase {
}
function test_quadraticCurveTo(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -1173,6 +1184,7 @@ CanvasTestCase {
}
function test_rect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -1371,6 +1383,7 @@ CanvasTestCase {
function test_clearRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#0f0';
@@ -1383,6 +1396,7 @@ CanvasTestCase {
}
function test_fillRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.beginPath();
@@ -1397,6 +1411,7 @@ CanvasTestCase {
function test_strokeRect(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.beginPath();
@@ -1412,6 +1427,7 @@ CanvasTestCase {
}
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml b/tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml
index 1d7fd2c12f..e0a4d7ad23 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_pattern.qml
@@ -6,36 +6,42 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_basic(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_animated(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_image(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_modified(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_paint(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_repeat(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml b/tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml
index 281264ba92..ef908db92a 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_pixel.qml
@@ -7,6 +7,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_createImageData(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var imageData = ctx.createImageData(1, 1);
var imageDataValues = imageData.data;
@@ -20,24 +21,28 @@ CanvasTestCase {
}
function test_getImageData(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_object(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_putImageData(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_filters(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml b/tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml
index 2baaa072d0..14318b3f56 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_shadow.qml
@@ -6,12 +6,14 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_basic(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_blur(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
@@ -19,6 +21,7 @@ CanvasTestCase {
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
@@ -26,6 +29,7 @@ CanvasTestCase {
function test_composite(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
@@ -33,6 +37,7 @@ CanvasTestCase {
function test_enable(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
@@ -40,36 +45,42 @@ CanvasTestCase {
function test_gradient(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_image(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_offset(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_pattern(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_stroke(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_tranform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_state.qml b/tests/auto/quick/qquickcanvasitem/data/tst_state.qml
index 18464def7c..ce02f9d7f7 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_state.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_state.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_bitmap(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -19,6 +20,7 @@ CanvasTestCase {
}
function test_clip(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -35,6 +37,7 @@ CanvasTestCase {
}
function test_fillStyle(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
// Test that restore() undoes any modifications
@@ -56,6 +59,7 @@ CanvasTestCase {
}
function test_font(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -78,6 +82,7 @@ CanvasTestCase {
}
function test_globalAlpha(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -100,6 +105,7 @@ CanvasTestCase {
}
function test_globalCompositeOperation(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -122,6 +128,7 @@ CanvasTestCase {
}
function test_lineCap(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -144,6 +151,7 @@ CanvasTestCase {
}
function test_lineJoin(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -166,6 +174,7 @@ CanvasTestCase {
}
function test_lineWidth(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -188,6 +197,7 @@ CanvasTestCase {
}
function test_miterLimit(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -210,6 +220,7 @@ CanvasTestCase {
}
function test_path(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -225,6 +236,7 @@ CanvasTestCase {
}
function test_shadow(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -295,6 +307,7 @@ CanvasTestCase {
}
function test_stack(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -330,6 +343,7 @@ CanvasTestCase {
}
function test_strokeStyle(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -353,6 +367,7 @@ CanvasTestCase {
function test_text(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -392,6 +407,7 @@ CanvasTestCase {
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml b/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml
index a3f1ab0a9b..0dc39b8b4c 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml
@@ -8,6 +8,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_default(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
compare(ctx.strokeStyle, "#000000")
@@ -17,6 +18,7 @@ CanvasTestCase {
}
function test_saverestore(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var old = ctx.strokeStyle;
ctx.save();
@@ -33,6 +35,7 @@ CanvasTestCase {
}
function test_namedColor(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.strokeStyle = "red";
@@ -50,6 +53,7 @@ CanvasTestCase {
}
function test_colorFromObjectToString(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -68,6 +72,7 @@ CanvasTestCase {
}
function test_withInvalidColor(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml b/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
index 2b39357bed..011fbd497b 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_svgpath(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
var svgs = [
// Absolute coordinates, explicit commands.
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_text.qml b/tests/auto/quick/qquickcanvasitem/data/tst_text.qml
index bfc4067040..f7cd66890b 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_text.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_text.qml
@@ -6,36 +6,42 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_baseLine(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_align(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_stroke(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_fill(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_font(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
}
function test_measure(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
canvas.destroy()
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_transform.qml b/tests/auto/quick/qquickcanvasitem/data/tst_transform.qml
index b2f5b51fa5..bbfec6a106 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_transform.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_transform.qml
@@ -6,6 +6,7 @@ CanvasTestCase {
function init_data() { return testData("2d"); }
function test_order(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -20,6 +21,7 @@ CanvasTestCase {
}
function test_rotate(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -90,6 +92,7 @@ CanvasTestCase {
}
function test_scale(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
@@ -182,6 +185,7 @@ CanvasTestCase {
}
function test_setTransform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -325,6 +329,7 @@ CanvasTestCase {
}
function test_transform(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
@@ -460,6 +465,7 @@ CanvasTestCase {
}
function test_translate(row) {
var canvas = createCanvasObject(row);
+ tryVerify(function() { return canvas.available; });
var ctx = canvas.getContext('2d');
ctx.reset();
ctx.fillStyle = '#f00';
diff --git a/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp b/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp
index 0215191d78..7e65b2a9d4 100644
--- a/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp
+++ b/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
diff --git a/tests/auto/quick/qquickcolorgroup/CMakeLists.txt b/tests/auto/quick/qquickcolorgroup/CMakeLists.txt
index 1d051476ff..0ced7947fd 100644
--- a/tests/auto/quick/qquickcolorgroup/CMakeLists.txt
+++ b/tests/auto/quick/qquickcolorgroup/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickcolorgroup Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickcolorgroup LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qquickcolorgroup
SOURCES
tst_qquickcolorgroup.cpp
diff --git a/tests/auto/quick/qquickcolorgroup/tst_qquickcolorgroup.cpp b/tests/auto/quick/qquickcolorgroup/tst_qquickcolorgroup.cpp
index 046cd5a14c..acf2849188 100644
--- a/tests/auto/quick/qquickcolorgroup/tst_qquickcolorgroup.cpp
+++ b/tests/auto/quick/qquickcolorgroup/tst_qquickcolorgroup.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/QSignalSpy>
diff --git a/tests/auto/quick/qquickdeliveryagent/CMakeLists.txt b/tests/auto/quick/qquickdeliveryagent/CMakeLists.txt
index 431ce4aaeb..f893d4372b 100644
--- a/tests/auto/quick/qquickdeliveryagent/CMakeLists.txt
+++ b/tests/auto/quick/qquickdeliveryagent/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qquickdeliveryagent Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickdeliveryagent LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickdeliveryagent/data/clearItemsOnHoverLeave.qml b/tests/auto/quick/qquickdeliveryagent/data/clearItemsOnHoverLeave.qml
new file mode 100644
index 0000000000..7b37b44050
--- /dev/null
+++ b/tests/auto/quick/qquickdeliveryagent/data/clearItemsOnHoverLeave.qml
@@ -0,0 +1,32 @@
+import QtQuick
+
+Rectangle{
+ id: mainWindow
+
+ visible: true
+ width: 800
+ height: 600
+
+ Column {
+ anchors.fill: parent
+ MouseArea {
+ width: parent.width
+ height: parent.height/3
+ hoverEnabled: true
+ }
+ MouseArea {
+ id: mouseArea
+ width: parent.width
+ height: parent.height/3
+ hoverEnabled: true
+ onExited: {
+ Window.window.close();
+ }
+ }
+ MouseArea {
+ width: parent.width
+ height: parent.height / 3
+ hoverEnabled: true
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickdeliveryagent/data/compoundControl.qml b/tests/auto/quick/qquickdeliveryagent/data/compoundControl.qml
new file mode 100644
index 0000000000..8ed05fc8d2
--- /dev/null
+++ b/tests/auto/quick/qquickdeliveryagent/data/compoundControl.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+
+Item {
+ id: root
+ objectName: "root"
+ width: 320
+ height: 240
+
+ FocusScope {
+ id: spinboxFocusScope
+ objectName: "spinboxFocusScope"
+ width: spinbox.width
+ height: spinbox.height
+ SpinBox {
+ id: spinbox
+ objectName: "spinbox"
+ editable: true
+ contentItem: TextField {
+ objectName: "spinboxContentItem"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/auto/quick/qquickdeliveryagent/data/flickableTextEdit.qml b/tests/auto/quick/qquickdeliveryagent/data/flickableTextEdit.qml
index 5286def525..79d91a25d4 100644
--- a/tests/auto/quick/qquickdeliveryagent/data/flickableTextEdit.qml
+++ b/tests/auto/quick/qquickdeliveryagent/data/flickableTextEdit.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquickdeliveryagent/data/listViewDelegate.qml b/tests/auto/quick/qquickdeliveryagent/data/listViewDelegate.qml
index bdb7246450..c5207e2a5d 100644
--- a/tests/auto/quick/qquickdeliveryagent/data/listViewDelegate.qml
+++ b/tests/auto/quick/qquickdeliveryagent/data/listViewDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp b/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp
index 7e6416b8e2..c9bce0c0b4 100644
--- a/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp
+++ b/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QSignalSpy>
@@ -19,9 +19,12 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QtQuick/private/qquickmousearea_p.h>
#include <QtGui/private/qeventpoint_p.h>
+#include <QtCore/qpointer.h>
+
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
// On one hand, uncommenting this will make troubleshooting easier (avoid the 60FPS hover events).
@@ -138,6 +141,11 @@ private slots:
void hoverPropagation_nested_data();
void hoverPropagation_nested();
void hoverPropagation_siblings();
+ void hoverEnterOnItemMove();
+ void hoverEnterOnItemMoveAfterHide();
+ void clearItemsOnHoverLeave();
+ void deleteTargetOnPress();
+ void compoundControlsFocusInSubscene();
private:
QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
@@ -175,7 +183,8 @@ void tst_qquickdeliveryagent::passiveGrabberOrder()
QPoint pos(75, 75);
QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, pos);
- QTest::qWait(1000);
+ QTRY_VERIFY(rootTap->isPressed());
+ QTRY_VERIFY(subsceneTap->isPressed());
auto devPriv = QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice());
const auto &persistentPoint = devPriv->activePoints.values().first();
qCDebug(lcTests) << "passive grabbers" << persistentPoint.passiveGrabbers << "contexts" << persistentPoint.passiveGrabbersContext;
@@ -185,7 +194,8 @@ void tst_qquickdeliveryagent::passiveGrabberOrder()
QCOMPARE(persistentPoint.passiveGrabbers.last(), rootTap);
QTest::mouseRelease(&view, Qt::LeftButton);
- QTest::qWait(100);
+ QTRY_COMPARE(rootTap->isPressed(), false);
+ QTRY_COMPARE(subsceneTap->isPressed(), false);
// QQuickWindow::event() has failsafe: clear all grabbers after release
QCOMPARE(persistentPoint.passiveGrabbers.size(), 0);
@@ -390,7 +400,7 @@ void tst_qquickdeliveryagent::undoDelegationWhenSubsceneFocusCleared() // QTBUG-
SubsceneRootItem subscene(listView, listView->boundingRect(), window.rootObject());
window.show();
- QVERIFY(QTest::qWaitForWindowExposed(&window));
+ QVERIFY(QTest::qWaitForWindowFocused(&window));
// populate a delegate in ListView
listView->setModel(1);
@@ -521,6 +531,151 @@ void tst_qquickdeliveryagent::hoverPropagation_siblings()
QCOMPARE(sibling2.hoverEnter, true);
}
+void tst_qquickdeliveryagent::hoverEnterOnItemMove()
+{
+ QQuickWindow window;
+ auto deliveryAgent = QQuickWindowPrivate::get(&window)->deliveryAgentPrivate();
+ window.resize(200, 200);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ // start with the mouse in the bottom right
+ QTest::mouseMove(&window, QPoint(150, 150));
+
+ HoverItem hoverItem(window.contentItem());
+ hoverItem.setAcceptHoverEvents(true);
+ hoverItem.setWidth(100);
+ hoverItem.setHeight(100);
+
+ deliveryAgent->flushFrameSynchronousEvents(&window);
+
+ QCOMPARE(hoverItem.hoverEnter, false);
+
+ // move the item so the mouse is now inside where the mouse was
+ hoverItem.setX(100);
+ hoverItem.setY(100);
+ deliveryAgent->flushFrameSynchronousEvents(&window);
+ QCOMPARE(hoverItem.hoverEnter, true);
+}
+
+void tst_qquickdeliveryagent::hoverEnterOnItemMoveAfterHide()
+{
+ QQuickWindow window;
+ auto deliveryAgent = QQuickWindowPrivate::get(&window)->deliveryAgentPrivate();
+ window.resize(200, 200);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ // start with the mouse in the bottom right
+ QTest::mouseMove(&window, QPoint(149, 149));
+
+ HoverItem hoverItem(window.contentItem());
+ hoverItem.setAcceptHoverEvents(true);
+ hoverItem.setWidth(100);
+ hoverItem.setHeight(100);
+
+ deliveryAgent->flushFrameSynchronousEvents(&window);
+ QCOMPARE(hoverItem.hoverEnter, false);
+
+ window.hide();
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QCOMPARE(hoverItem.hoverEnter, false);
+
+ // move the item so the mouse is now inside where the mouse was
+ hoverItem.setX(100);
+ hoverItem.setY(100);
+ deliveryAgent->flushFrameSynchronousEvents(&window);
+ QCOMPARE(hoverItem.hoverEnter, false);
+}
+
+void tst_qquickdeliveryagent::clearItemsOnHoverLeave()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("clearItemsOnHoverLeave.qml")));
+
+ QTest::mouseMove(&window, QPoint(10, 205)); // Move to MouseArea that triggers close
+ QTest::mouseMove(&window, QPoint(10, 405)); // Exit MouseArea that triggers close.
+}
+
+// QTBUG-91272
+void tst_qquickdeliveryagent::deleteTargetOnPress()
+{
+ QQuickWindow window;
+ auto deliveryAgent = QQuickWindowPrivate::get(&window)->deliveryAgentPrivate();
+ window.resize(200, 200);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickMouseArea *lowerArea = new QQuickMouseArea(window.contentItem());
+ lowerArea->setWidth(200);
+ lowerArea->setHeight(200);
+
+ QQuickMouseArea *upperArea = new QQuickMouseArea(window.contentItem());
+ upperArea->setWidth(180);
+ upperArea->setHeight(180);
+ bool pressed = false;
+ connect(upperArea, &QQuickMouseArea::pressed, this, [&]() {
+ pressed = true;
+ delete lowerArea;
+ lowerArea = nullptr;
+ });
+ QTest::mouseMove(&window, QPoint(100, 100));
+ QTest::mousePress(&window, Qt::MouseButton::LeftButton, {}, {100, 100});
+ deliveryAgent->flushFrameSynchronousEvents(&window);
+ QVERIFY(pressed);
+ QVERIFY(upperArea->isPressed());
+ QTest::mouseRelease(&window, Qt::MouseButton::LeftButton, {}, {100, 100});
+ deliveryAgent->flushFrameSynchronousEvents(&window);
+ QVERIFY(!upperArea->isPressed());
+}
+
+void tst_qquickdeliveryagent::compoundControlsFocusInSubscene()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::initView(window, testFileUrl("compoundControl.qml")));
+ QQuickItem *spinboxFocusScope = window.rootObject()->findChild<QQuickItem *>("spinboxFocusScope");
+ QVERIFY(spinboxFocusScope);
+ QQuickItem *spinbox = window.rootObject()->findChild<QQuickItem *>("spinbox");
+ QVERIFY(spinbox);
+ QQuickItem *textField = window.rootObject()->findChild<QQuickItem *>("spinboxContentItem");
+ QVERIFY(textField);
+
+ // put the items into a SubsceneRootItem
+ SubsceneRootItem subscene(spinboxFocusScope, spinboxFocusScope->boundingRect().translated(0, spinboxFocusScope->height() + 20), window.rootObject());
+
+ window.show();
+ QVERIFY(QTest::qWaitForWindowFocused(&window));
+
+ QVERIFY(!textField->hasActiveFocus());
+ QVERIFY(!textField->hasFocus());
+ QVERIFY(!spinbox->hasActiveFocus());
+ QVERIFY(!spinbox->hasFocus());
+ QVERIFY(!spinbox->scopedFocusItem());
+ QVERIFY(!spinboxFocusScope->hasActiveFocus());
+ QVERIFY(!spinboxFocusScope->hasFocus());
+ QVERIFY(!spinbox->scopedFocusItem());
+
+ auto clickPos = spinboxFocusScope->boundingRect().translated(0, spinboxFocusScope->height() + 20).center().toPoint();
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, clickPos);
+
+ QVERIFY(textField->hasActiveFocus());
+ QVERIFY(textField->hasFocus());
+ QTRY_VERIFY(spinbox->hasActiveFocus());
+ QVERIFY(spinbox->hasFocus());
+ QCOMPARE(spinbox->scopedFocusItem(), textField);
+ QVERIFY(spinboxFocusScope->hasActiveFocus());
+ QVERIFY(spinboxFocusScope->hasFocus());
+ QCOMPARE(spinboxFocusScope->scopedFocusItem(), spinbox);
+
+ QQuickDeliveryAgentPrivate *daPriv = static_cast<QQuickDeliveryAgentPrivate *>(QQuickDeliveryAgentPrivate::get(subscene.deliveryAgent));
+ QVERIFY(daPriv->rootItem->hasActiveFocus());
+ QCOMPARE(daPriv->activeFocusItem, textField);
+ QCOMPARE(QQuickWindowPrivate::get(&window)->deliveryAgentPrivate()->activeFocusItem, textField);
+ QCOMPARE(QQuickWindowPrivate::get(&window)->deliveryAgentPrivate()->rootItem->scopedFocusItem(), spinboxFocusScope);
+}
+
QTEST_MAIN(tst_qquickdeliveryagent)
#include "tst_qquickdeliveryagent.moc"
diff --git a/tests/auto/quick/qquickdesignersupport/CMakeLists.txt b/tests/auto/quick/qquickdesignersupport/CMakeLists.txt
index 9fbd69137a..985233f5f3 100644
--- a/tests/auto/quick/qquickdesignersupport/CMakeLists.txt
+++ b/tests/auto/quick/qquickdesignersupport/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickdesignersupport Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickdesignersupport LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickdesignersupport/data/RegTestComponent.qml b/tests/auto/quick/qquickdesignersupport/data/RegTestComponent.qml
new file mode 100644
index 0000000000..8bb8480daf
--- /dev/null
+++ b/tests/auto/quick/qquickdesignersupport/data/RegTestComponent.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+QtObject {
+ id: root
+
+ property int currentCategory: 0
+
+
+
+ property var materialsNames: [[qsTr("Car Paint"), qsTr("Glittery Car Paint"), qsTr("Aluminium"), qsTr("Chrome"), qsTr("Steel"), qsTr("Brushed Steel"), qsTr("Steel Floor"), qsTr("Copper"), qsTr("Silver"), qsTr("Gold"), qsTr("Mirror")],
+ [qsTr("Asphalt"), qsTr("Brick"), qsTr("Ceramic"), qsTr("Concrete"), qsTr("Glass"), qsTr("Darkened Glass"), qsTr("Wood"), qsTr("Wood - Planks"), qsTr("Wood - Parquet"), qsTr("Stone")],
+ [qsTr("Acrylic Paint"), qsTr("Carbon Fiber"), qsTr("Shiny Plastic"), qsTr("Matte Plastic"), qsTr("Textured Plastic"), qsTr("Rubber"), qsTr("Wax")],
+ [qsTr("Fabric"), qsTr("Fabric: Rough"), qsTr("Fabric: Satin"), qsTr("Leather"), qsTr("Paper")]]
+
+
+
+ property var model: root.materialsNames[root.currentCategory]
+
+} \ No newline at end of file
diff --git a/tests/auto/quick/qquickdesignersupport/data/regTestProperties.qml b/tests/auto/quick/qquickdesignersupport/data/regTestProperties.qml
new file mode 100644
index 0000000000..5ea3f58aff
--- /dev/null
+++ b/tests/auto/quick/qquickdesignersupport/data/regTestProperties.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.11
+
+Rectangle {
+ objectName: "rootItem"
+ color: "white"
+ width: 800
+ height: 600
+
+ RegTestComponent {
+ objectName: "testComponent"
+
+ }
+}
diff --git a/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp b/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
index c35f31b8f2..bdc8f97d3a 100644
--- a/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
+++ b/tests/auto/quick/qquickdesignersupport/tst_qquickdesignersupport.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -41,6 +42,7 @@ private slots:
void testDotProperties();
void testItemReparenting();
void testPropertyNames();
+ void regressionTestAllProperties();
};
@@ -557,14 +559,15 @@ void doComponentCompleteRecursive(QObject *object)
QList<QObject*> childList = object->children();
if (item) {
- foreach (QQuickItem *childItem, item->childItems()) {
+ const auto childItems = item->childItems();
+ for (QQuickItem *childItem : childItems) {
if (!childList.contains(childItem))
childList.append(childItem);
}
}
- foreach (QObject *child, childList)
- doComponentCompleteRecursive(child);
+ for (QObject *child : std::as_const(childList))
+ doComponentCompleteRecursive(child);
if (item) {
static_cast<QQmlParserStatus*>(item)->componentComplete();
@@ -781,6 +784,37 @@ void tst_qquickdesignersupport::testPropertyNames()
QVERIFY(!names.contains("testProperty"));
}
+void tst_qquickdesignersupport::regressionTestAllProperties()
+{
+ QScopedPointer<QQuickView> view(new QQuickView);
+ view->engine()->setOutputWarningsToStandardError(false);
+ view->setSource(testFileUrl("regTestProperties.qml"));
+
+ QVERIFY(view->errors().isEmpty());
+ QQuickItem *rootItem = view->rootObject();
+ QVERIFY(rootItem);
+
+ QObject *component = rootItem->findChild<QObject*>("testComponent");
+
+ QVERIFY(component);
+ QCOMPARE(component->objectName(), QLatin1String("testComponent"));
+
+ QVERIFY(component);
+
+ QQuickDesignerSupport::PropertyNameList names
+ = QQuickDesignerSupportProperties::allPropertyNames(component);
+ QVERIFY(!names.isEmpty());
+
+ const QByteArrayList expectedNames = QByteArrayList {
+ "objectName", "currentCategory", "materialsNames", "model"
+ };
+
+ QCOMPARE(names, expectedNames);
+ names = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(component);
+ QVERIFY(!names.isEmpty());
+ QCOMPARE(names, expectedNames);
+}
+
QTEST_MAIN(tst_qquickdesignersupport)
#include "tst_qquickdesignersupport.moc"
diff --git a/tests/auto/quick/qquickdrag/CMakeLists.txt b/tests/auto/quick/qquickdrag/CMakeLists.txt
index afd184dfd9..73e8c64bb7 100644
--- a/tests/auto/quick/qquickdrag/CMakeLists.txt
+++ b/tests/auto/quick/qquickdrag/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickdrag Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickdrag LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qquickdrag
SOURCES
tst_qquickdrag.cpp
diff --git a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
index d805bc19a1..b17b16b429 100644
--- a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
+++ b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
diff --git a/tests/auto/quick/qquickdragattached/CMakeLists.txt b/tests/auto/quick/qquickdragattached/CMakeLists.txt
index 7145b4bac7..822e38861e 100644
--- a/tests/auto/quick/qquickdragattached/CMakeLists.txt
+++ b/tests/auto/quick/qquickdragattached/CMakeLists.txt
@@ -1,3 +1,15 @@
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickdragattached LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
qt_internal_add_test(tst_qquickdragattached
SOURCES
tst_qquickdragattached.cpp
@@ -8,4 +20,19 @@ qt_internal_add_test(tst_qquickdragattached
Qt::Network
Qt::QmlPrivate
Qt::QuickPrivate
+ Qt::QuickTestUtilsPrivate
+ TESTDATA ${test_data}
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(tst_qquickdragattached CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qquickdragattached CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
)
diff --git a/tests/auto/quick/qquickdragattached/data/qt_logo.svg b/tests/auto/quick/qquickdragattached/data/qt_logo.svg
new file mode 100644
index 0000000000..062daff3e9
--- /dev/null
+++ b/tests/auto/quick/qquickdragattached/data/qt_logo.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="462pt"
+ height="339pt"
+ viewBox="0 0 462 339"
+ version="1.1"
+ id="svg2"
+>
+ <path
+ fill="#41cd52"
+ d=" M 63.50 0.00 L 462.00 0.00 L 462.00 274.79 C 440.60 296.26 419.13 317.66 397.61 339.00 L 0.00 339.00 L 0.00 63.39 C 21.08 42.18 42.34 21.13 63.50 0.00 Z"
+ id="path6"/>
+ <path
+ d=" M 122.37 71.33 C 137.50 61.32 156.21 58.79 174.00 58.95 C 190.94 59.16 208.72 62.13 222.76 72.24 C 232.96 79.41 239.59 90.48 244.01 101.93 C 251.16 120.73 253.26 141.03 253.50 161.01 C 253.53 181.13 252.62 201.69 245.96 220.86 C 241.50 233.90 233.01 245.48 221.81 253.52 C 229.87 266.58 238.09 279.54 246.15 292.60 C 236.02 297.27 225.92 301.97 215.78 306.62 C 207.15 292.38 198.56 278.11 189.90 263.89 C 178.19 265.81 166.21 265.66 154.44 264.36 C 140.34 262.67 125.97 258.37 115.09 248.88 C 106.73 241.64 101.48 231.51 97.89 221.21 C 92.01 203.79 90.43 185.25 90.16 166.97 C 90.02 147.21 91.28 127.14 97.24 108.18 C 101.85 93.92 109.48 79.69 122.37 71.33 Z"
+ id="path8"
+ fill="#ffffff"/>
+ <path
+ d=" M 294.13 70.69 C 304.73 70.68 315.33 70.68 325.93 70.69 C 325.96 84.71 325.92 98.72 325.95 112.74 C 339.50 112.76 353.05 112.74 366.60 112.75 C 366.37 121.85 366.12 130.95 365.86 140.05 C 352.32 140.08 338.79 140.04 325.25 140.07 C 325.28 163.05 325.18 186.03 325.30 209.01 C 325.56 215.30 325.42 221.94 328.19 227.75 C 330.21 232.23 335.65 233.38 340.08 233.53 C 348.43 233.50 356.77 233.01 365.12 232.86 C 365.63 241.22 366.12 249.59 366.60 257.95 C 349.99 260.74 332.56 264.08 316.06 258.86 C 309.11 256.80 302.63 252.19 299.81 245.32 C 294.76 233.63 294.35 220.62 294.13 208.07 C 294.11 185.40 294.13 162.74 294.12 140.07 C 286.73 140.05 279.34 140.08 271.95 140.05 C 271.93 130.96 271.93 121.86 271.95 112.76 C 279.34 112.73 286.72 112.77 294.11 112.74 C 294.14 98.72 294.10 84.71 294.13 70.69 Z"
+ id="path10"
+ fill="#ffffff"/>
+ <path
+ fill="#41cd52"
+ d=" M 160.51 87.70 C 170.80 86.36 181.60 86.72 191.34 90.61 C 199.23 93.73 205.93 99.84 209.47 107.58 C 214.90 119.31 216.98 132.26 218.03 145.05 C 219.17 162.07 219.01 179.25 216.66 196.17 C 215.01 206.24 212.66 216.85 205.84 224.79 C 198.92 232.76 188.25 236.18 178.01 236.98 C 167.21 237.77 155.82 236.98 146.07 231.87 C 140.38 228.84 135.55 224.09 132.73 218.27 C 129.31 211.30 127.43 203.69 126.11 196.07 C 122.13 171.91 121.17 146.91 126.61 122.89 C 128.85 113.83 132.11 104.53 138.73 97.70 C 144.49 91.85 152.51 88.83 160.51 87.70 Z"
+ id="path12"/>
+</svg>
diff --git a/tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp b/tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp
index 19e35703fd..243d767c80 100644
--- a/tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp
+++ b/tests/auto/quick/qquickdragattached/tst_qquickdragattached.cpp
@@ -1,22 +1,35 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/private/qquickdrag_p_p.h>
-class tst_QQuickDragAttached: public QObject
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+class tst_QQuickDragAttached: public QQmlDataTest
{
Q_OBJECT
+public:
+ tst_QQuickDragAttached();
+
private slots:
void setMimeData_data();
void setMimeData();
+ void imageSourceSize_data();
+ void imageSourceSize();
+
void startDrag_data();
void startDrag();
};
+tst_QQuickDragAttached::tst_QQuickDragAttached()
+ : QQmlDataTest(QT_QMLTEST_DATADIR)
+{
+}
+
void tst_QQuickDragAttached::setMimeData_data()
{
QTest::addColumn<QVariantMap>("mimeData");
@@ -53,9 +66,9 @@ void tst_QQuickDragAttached::setMimeData_data()
QTest::addRow("text/uri-list, variant")
<< makeMap("text/uri-list", QVariantList{QVariant("file://foo")})
<< QStringList{"text/uri-list"};
- QTest::addRow("mismatch image")
- << makeMap("image/png", "not an image")
- << QStringList{};
+ QTest::addRow("application/json")
+ << makeMap("application/json", "{}")
+ << QStringList{"application/json"};
QTest::addRow("missing charset")
<< makeMap("text/plain;charset=", QString("foo"))
<< QStringList{};
@@ -91,6 +104,45 @@ void tst_QQuickDragAttached::setMimeData()
QCOMPARE(spy.count(), expectedCount);
}
+void tst_QQuickDragAttached::imageSourceSize_data()
+{
+ QTest::addColumn<bool>("sizeFirst");
+ QTest::addColumn<QSize>("imageSourceSize");
+ QTest::addColumn<QSize>("expectedSourceSize");
+ QTest::addColumn<QSize>("expectedImageSize");
+
+ QTest::addRow("default size") << false << QSize() << QSize(462, 339) << QSize(462, 339);
+ QTest::addRow("shrunken elongated") << false << QSize(214, 114) << QSize(214, 114) << QSize(214, 114);
+ QTest::addRow("width, neg height") << false << QSize(154, -1) << QSize(154, 339) << QSize(154, 339);
+ QTest::addRow("width, zero height") << false << QSize(154, 0) << QSize(154, 0) << QSize(154, 113);
+
+ QTest::addRow("size first: default size") << true << QSize() << QSize(462, 339) << QSize(462, 339);
+ QTest::addRow("size first: shrunken elongated") << true << QSize(214, 114) << QSize(214, 114) << QSize(214, 114);
+ QTest::addRow("size first: width, neg height") << true << QSize(154, -1) << QSize(154, 113) << QSize(154, 113);
+ QTest::addRow("size first: width, zero height") << true << QSize(154, 0) << QSize(154, 0) << QSize(154, 113);
+}
+
+void tst_QQuickDragAttached::imageSourceSize()
+{
+ QFETCH(bool, sizeFirst);
+ QFETCH(QSize, imageSourceSize);
+ QFETCH(QSize, expectedSourceSize);
+ QFETCH(QSize, expectedImageSize);
+
+ QQuickDragAttached attached(nullptr);
+ QSignalSpy spy(&attached, &QQuickDragAttached::imageSourceSizeChanged);
+
+ if (sizeFirst)
+ attached.setImageSourceSize(imageSourceSize);
+ attached.setImageSource(testFileUrl("qt_logo.svg"));
+ attached.setImageSourceSize(imageSourceSize);
+
+ const int expectedCount = imageSourceSize.width() >= 0 || imageSourceSize.height() >= 0 ? 1 : 0;
+ QCOMPARE(spy.count(), expectedCount);
+ QCOMPARE(attached.imageSourceSize(), expectedSourceSize);
+ QCOMPARE(QQuickDragAttachedPrivate::get(&attached)->pixmapLoader.image().size(), expectedImageSize);
+}
+
void tst_QQuickDragAttached::startDrag_data()
{
setMimeData_data();
@@ -116,7 +168,11 @@ void tst_QQuickDragAttached::startDrag()
std::unique_ptr<QMimeData> data(d->createMimeData());
QVERIFY(data);
+ auto debugHelper = qScopeGuard([&data]{
+ qWarning() << data->formats();
+ });
QCOMPARE(data->formats(), formats);
+ debugHelper.dismiss();
#else
QSKIP("This test relies on private APIs that are only exported in developer-builds");
#endif
diff --git a/tests/auto/quick/qquickdroparea/CMakeLists.txt b/tests/auto/quick/qquickdroparea/CMakeLists.txt
index b39eb08010..32d631f08c 100644
--- a/tests/auto/quick/qquickdroparea/CMakeLists.txt
+++ b/tests/auto/quick/qquickdroparea/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickdroparea Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickdroparea LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickdroparea/data/nested1.qml b/tests/auto/quick/qquickdroparea/data/nested1.qml
index 28a21ab4c0..430421d27b 100644
--- a/tests/auto/quick/qquickdroparea/data/nested1.qml
+++ b/tests/auto/quick/qquickdroparea/data/nested1.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
Item {
diff --git a/tests/auto/quick/qquickdroparea/data/nested2.qml b/tests/auto/quick/qquickdroparea/data/nested2.qml
index 7585792ff6..c7261ef1d5 100644
--- a/tests/auto/quick/qquickdroparea/data/nested2.qml
+++ b/tests/auto/quick/qquickdroparea/data/nested2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
Item {
diff --git a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
index fb036b6fad..f861eb37f2 100644
--- a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
+++ b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
diff --git a/tests/auto/quick/qquickdynamicpropertyanimation/CMakeLists.txt b/tests/auto/quick/qquickdynamicpropertyanimation/CMakeLists.txt
index d7db4ff504..b9834a7a72 100644
--- a/tests/auto/quick/qquickdynamicpropertyanimation/CMakeLists.txt
+++ b/tests/auto/quick/qquickdynamicpropertyanimation/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickdynamicpropertyanimation Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickdynamicpropertyanimation LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickdynamicpropertyanimation/tst_qquickdynamicpropertyanimation.cpp b/tests/auto/quick/qquickdynamicpropertyanimation/tst_qquickdynamicpropertyanimation.cpp
index 7c875fb853..85ff58fd4d 100644
--- a/tests/auto/quick/qquickdynamicpropertyanimation/tst_qquickdynamicpropertyanimation.cpp
+++ b/tests/auto/quick/qquickdynamicpropertyanimation/tst_qquickdynamicpropertyanimation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlProperty>
diff --git a/tests/auto/quick/qquickflickable/BLACKLIST b/tests/auto/quick/qquickflickable/BLACKLIST
index 2430735613..d35ddb9e3f 100644
--- a/tests/auto/quick/qquickflickable/BLACKLIST
+++ b/tests/auto/quick/qquickflickable/BLACKLIST
@@ -1,5 +1,14 @@
[movingAndFlicking]
macos-11
+opensuse-leap # QTBUG-118060
[flickVelocity]
macos-11
+
+# QTBUG-118060
+[ignoreNonLeftMouseButtons]
+opensuse-leap
+
+# QTBUG-118063
+[nativeGesturePinchOnFlickableWithParentTapHandler]
+opensuse-leap
diff --git a/tests/auto/quick/qquickflickable/CMakeLists.txt b/tests/auto/quick/qquickflickable/CMakeLists.txt
index 44860d32bc..e46378188d 100644
--- a/tests/auto/quick/qquickflickable/CMakeLists.txt
+++ b/tests/auto/quick/qquickflickable/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickflickable Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickflickable LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickflickable/data/endpoints.qml b/tests/auto/quick/qquickflickable/data/endpoints.qml
new file mode 100644
index 0000000000..80caa32da5
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/endpoints.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Flickable {
+ width: 100; height: 100
+ contentWidth: row.width; contentHeight: row.height
+
+ Row {
+ id: row
+ Repeater {
+ model: 4
+ Rectangle { width: 200; height: 300; color: "blue" }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickflickable/data/flickableWithTapHandler.qml b/tests/auto/quick/qquickflickable/data/flickableWithTapHandler.qml
new file mode 100644
index 0000000000..91b81059ab
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/flickableWithTapHandler.qml
@@ -0,0 +1,24 @@
+import QtQuick
+
+Item {
+ width: 300
+ height: 300
+
+ Flickable {
+ anchors.fill: parent
+ anchors.topMargin: 100
+ contentWidth: 1000
+ contentHeight: 1000
+
+ Rectangle {
+ objectName: "childItem"
+ x: 20
+ y: 50
+ width: 20
+ height: 20
+ color: "red"
+ TapHandler {
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickflickable/data/nested.qml b/tests/auto/quick/qquickflickable/data/nested.qml
index 7fa331c46c..0cbeb5cef3 100644
--- a/tests/auto/quick/qquickflickable/data/nested.qml
+++ b/tests/auto/quick/qquickflickable/data/nested.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
Flickable {
diff --git a/tests/auto/quick/qquickflickable/data/overshoot.qml b/tests/auto/quick/qquickflickable/data/overshoot.qml
index be908584f1..baa1bfea01 100644
--- a/tests/auto/quick/qquickflickable/data/overshoot.qml
+++ b/tests/auto/quick/qquickflickable/data/overshoot.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
diff --git a/tests/auto/quick/qquickflickable/data/overshoot_reentrant.qml b/tests/auto/quick/qquickflickable/data/overshoot_reentrant.qml
index 2fa80a7d18..7bbe45907c 100644
--- a/tests/auto/quick/qquickflickable/data/overshoot_reentrant.qml
+++ b/tests/auto/quick/qquickflickable/data/overshoot_reentrant.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
diff --git a/tests/auto/quick/qquickflickable/data/parallel.qml b/tests/auto/quick/qquickflickable/data/parallel.qml
index bef8af3b37..8e7f64a84e 100644
--- a/tests/auto/quick/qquickflickable/data/parallel.qml
+++ b/tests/auto/quick/qquickflickable/data/parallel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickflickable/data/pressDelay.qml b/tests/auto/quick/qquickflickable/data/pressDelay.qml
index a69c4af6de..97bc6b794f 100644
--- a/tests/auto/quick/qquickflickable/data/pressDelay.qml
+++ b/tests/auto/quick/qquickflickable/data/pressDelay.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.0
+import QtQuick
Flickable {
flickableDirection: Flickable.VerticalFlick
@@ -15,7 +15,6 @@ Flickable {
MouseArea {
id: ma
- objectName: "mouseArea"
y: 100
anchors.horizontalCenter: parent.horizontalCenter
width: 240
@@ -32,14 +31,7 @@ Flickable {
Rectangle {
anchors.fill: parent
- color: parent.pressed ? 'blue' : 'green'
-
- Text {
- anchors.fill: parent
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- text: 'Hello'
- }
+ color: parent.pressed ? 'blue' : 'lightsteelblue'
}
}
}
diff --git a/tests/auto/quick/qquickflickable/data/ratios.qml b/tests/auto/quick/qquickflickable/data/ratios.qml
index a7e927cce1..7983b797fe 100644
--- a/tests/auto/quick/qquickflickable/data/ratios.qml
+++ b/tests/auto/quick/qquickflickable/data/ratios.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickflickable/data/visibleAreaBinding.qml b/tests/auto/quick/qquickflickable/data/visibleAreaBinding.qml
index 8a091f0906..a52f612af7 100644
--- a/tests/auto/quick/qquickflickable/data/visibleAreaBinding.qml
+++ b/tests/auto/quick/qquickflickable/data/visibleAreaBinding.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index ab21415235..b003511356 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
@@ -8,6 +8,7 @@
#include <QtGui/QStyleHints>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
+#include <private/qguiapplication_p.h>
#include <private/qquickflickable_p.h>
#include <private/qquickflickable_p_p.h>
#include <private/qquickmousearea_p.h>
@@ -15,6 +16,7 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qquicktaphandler_p.h>
#include <math.h>
+#include <QtGui/qpa/qplatformintegration.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/geometrytestutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
@@ -165,6 +167,7 @@ private slots:
void rebound();
void maximumFlickVelocity();
void flickDeceleration();
+ void pressDelay_data();
void pressDelay();
void nestedPressDelay();
void filterReplayedPress();
@@ -174,6 +177,7 @@ private slots:
void returnToBounds();
void returnToBounds_data();
void wheel();
+ void wheelBackwards();
void trackpad();
void nestedTrackpad();
void movingAndFlicking();
@@ -217,14 +221,24 @@ private slots:
void receiveTapOutsideContentItem();
void flickWhenRotated_data();
void flickWhenRotated();
+ void flickAndReleaseOutsideBounds();
void scrollingWithFractionalExtentSize_data();
void scrollingWithFractionalExtentSize();
void setContentPositionWhileDragging_data();
void setContentPositionWhileDragging();
+ void coalescedMove();
+ void onlyOneMove();
+ void proportionalWheelScrolling();
+ void touchCancel();
+ void pixelAlignedEndPoints();
private:
void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
QPointingDevice *touchDevice = QTest::createTouchDevice();
+ const QPointingDevice *mouseDevice = new QPointingDevice(
+ "test mouse", 1000, QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic,
+ QInputDevice::Capability::Position | QInputDevice::Capability::Hover | QInputDevice::Capability::Scroll,
+ 1, 5, QString(), QPointingDeviceUniqueId(), this);
};
void tst_qquickflickable::initTestCase()
@@ -234,6 +248,8 @@ void tst_qquickflickable::initTestCase()
#endif
QQmlDataTest::initTestCase();
qmlRegisterType<TouchDragArea>("Test",1,0,"TouchDragArea");
+ touchDevice->setParent(this); // avoid leak
+ QWindowSystemInterface::registerInputDevice(mouseDevice);
}
void tst_qquickflickable::cleanup()
@@ -519,45 +535,54 @@ void tst_qquickflickable::flickDeceleration()
delete flickable;
}
+void tst_qquickflickable::pressDelay_data()
+{
+ QTest::addColumn<const QPointingDevice *>("device");
+ const QPointingDevice *constTouchDevice = touchDevice;
+
+ QTest::newRow("mouse") << mouseDevice;
+ QTest::newRow("touch") << constTouchDevice;
+}
+
void tst_qquickflickable::pressDelay()
{
- QScopedPointer<QQuickView> window(new QQuickView);
- window->setSource(testFileUrl("pressDelay.qml"));
- QTRY_COMPARE(window->status(), QQuickView::Ready);
- QQuickViewTestUtils::centerOnScreen(window.data());
- QQuickViewTestUtils::moveMouseAway(window.data());
- window->show();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QVERIFY(window->rootObject() != nullptr);
+ QFETCH(const QPointingDevice *, device);
- QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
- QSignalSpy spy(flickable, SIGNAL(pressDelayChanged()));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("pressDelay.qml")));
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window.rootObject());
QVERIFY(flickable);
- QCOMPARE(flickable->pressDelay(), 100);
+ QQuickMouseArea *mouseArea = flickable->findChild<QQuickMouseArea*>();
+ QSignalSpy clickedSpy(mouseArea, &QQuickMouseArea::clicked);
+ // Test the pressDelay property itself
+ QSignalSpy pressDelayChangedSpy(flickable, &QQuickFlickable::pressDelayChanged);
+ QCOMPARE(flickable->pressDelay(), 100);
flickable->setPressDelay(200);
QCOMPARE(flickable->pressDelay(), 200);
- QCOMPARE(spy.size(),1);
+ QCOMPARE(pressDelayChangedSpy.size(), 1);
flickable->setPressDelay(200);
- QCOMPARE(spy.size(),1);
+ QCOMPARE(pressDelayChangedSpy.size(), 1);
- QQuickItem *mouseArea = window->rootObject()->findChild<QQuickItem*>("mouseArea");
- QSignalSpy clickedSpy(mouseArea, SIGNAL(clicked(QQuickMouseEvent*)));
- moveAndPress(window.data(), QPoint(150, 150));
+ // Test the press delay
+ QPoint p(150, 150);
+ if (device->type() == QInputDevice::DeviceType::Mouse)
+ QQuickTest::pointerMove(device, &window, 0, p);
+ QQuickTest::pointerPress(device, &window, 0, p);
// The press should not occur immediately
- QVERIFY(!mouseArea->property("pressed").toBool());
+ QCOMPARE(mouseArea->isPressed(), false);
// But, it should occur eventually
- QTRY_VERIFY(mouseArea->property("pressed").toBool());
+ QTRY_VERIFY(mouseArea->isPressed());
- QCOMPARE(clickedSpy.size(),0);
+ QCOMPARE(clickedSpy.size(), 0);
// On release the clicked signal should be emitted
- QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(150, 150));
- QCOMPARE(clickedSpy.size(),1);
+ QQuickTest::pointerRelease(device, &window, 0, p);
+ QCOMPARE(clickedSpy.size(), 1);
// Press and release position should match
QCOMPARE(flickable->property("pressX").toReal(), flickable->property("releaseX").toReal());
@@ -565,38 +590,44 @@ void tst_qquickflickable::pressDelay()
// Test a quick tap within the pressDelay timeout
+ p = QPoint(180, 180);
clickedSpy.clear();
- moveAndPress(window.data(), QPoint(180, 180));
+ if (device->type() == QInputDevice::DeviceType::Mouse)
+ QQuickTest::pointerMove(device, &window, 0, p);
+ QQuickTest::pointerPress(device, &window, 0, p);
// The press should not occur immediately
- QVERIFY(!mouseArea->property("pressed").toBool());
+ QCOMPARE(mouseArea->isPressed(), false);
+ QCOMPARE(clickedSpy.size(), 0);
- QCOMPARE(clickedSpy.size(),0);
-
- // On release the press, release and clicked signal should be emitted
- QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(180, 180));
- QCOMPARE(clickedSpy.size(),1);
+ // On release, the press, release and clicked signal should be emitted
+ QQuickTest::pointerRelease(device, &window, 0, p);
+ QCOMPARE(clickedSpy.size(), 1);
// Press and release position should match
QCOMPARE(flickable->property("pressX").toReal(), flickable->property("releaseX").toReal());
QCOMPARE(flickable->property("pressY").toReal(), flickable->property("releaseY").toReal());
- // QTBUG-31168
- moveAndPress(window.data(), QPoint(150, 110));
+ // Test flick after press (QTBUG-31168)
+ QPoint startPosition(150, 110);
+ p = QPoint(150, 190);
+ clickedSpy.clear();
+ if (device->type() == QInputDevice::DeviceType::Mouse)
+ QQuickTest::pointerMove(device, &window, 0, startPosition);
+ QQuickTest::pointerPress(device, &window, 0, startPosition);
// The press should not occur immediately
- QVERIFY(!mouseArea->property("pressed").toBool());
-
- QTest::mouseMove(window.data(), QPoint(150, 190));
+ QCOMPARE(mouseArea->isPressed(), false);
+ QQuickTest::pointerMove(device, &window, 0, p);
- // As we moved pass the drag threshold, we should never receive the press
- QVERIFY(!mouseArea->property("pressed").toBool());
- QTRY_VERIFY(!mouseArea->property("pressed").toBool());
+ // Since we moved past the drag threshold, we should never receive the press
+ QCOMPARE(mouseArea->isPressed(), false);
+ QTRY_COMPARE(mouseArea->isPressed(), false);
- // On release the clicked signal should *not* be emitted
- QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(150, 190));
- QCOMPARE(clickedSpy.size(),1);
+ // On release, the clicked signal should *not* be emitted
+ QQuickTest::pointerRelease(device, &window, 0, p);
+ QCOMPARE(clickedSpy.size(), 0);
}
// QTBUG-17361
@@ -935,6 +966,47 @@ void tst_qquickflickable::wheel()
QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886
}
+void tst_qquickflickable::wheelBackwards() // (QTBUG-121349)
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("wheel.qml")));
+ QQuickFlickable *flick = window.rootObject()->findChild<QQuickFlickable*>("flick");
+ QVERIFY(flick);
+ QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded()));
+ quint64 timestamp = 10;
+ const QPoint pos(200, 200);
+
+ // attempting to scroll vertically "backwards" beyond extents does not initiate overshoot
+ {
+ QWheelEvent event(pos, window.mapToGlobal(pos), QPoint(), QPoint(0, 120),
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
+ event.setAccepted(false);
+ event.setTimestamp(timestamp++);
+ QGuiApplication::sendEvent(&window, &event);
+ }
+ QCOMPARE(flick->contentY(), qreal(0));
+ QCOMPARE(flick->isMoving(), false);
+ QCOMPARE(moveEndSpy.size(), 0);
+
+ // get ready to test horizontal wheel
+ flick->setContentY(0); // which triggers movementEnded again
+ flick->setProperty("movementsAfterEnd", 0);
+ flick->setProperty("ended", false);
+ QCOMPARE(flick->contentY(), qreal(0));
+
+ // attempting to scroll horizontally "backwards" beyond extents does not initiate overshoot
+ {
+ QWheelEvent event(pos, window.mapToGlobal(pos), QPoint(), QPoint(120, 0),
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
+ event.setAccepted(false);
+ event.setTimestamp(timestamp);
+ QGuiApplication::sendEvent(&window, &event);
+ }
+ QCOMPARE(flick->contentX(), qreal(0));
+ QCOMPARE(flick->isMoving(), false);
+ QCOMPARE(moveEndSpy.size(), 0);
+}
+
void tst_qquickflickable::trackpad()
{
QScopedPointer<QQuickView> window(new QQuickView);
@@ -2872,7 +2944,7 @@ void tst_qquickflickable::receiveTapOutsideContentItem()
QVERIFY(QTest::qWaitForWindowActive(&window));
QQuickTapHandler tapHandler(&flickable);
- QSignalSpy clickedSpy(&tapHandler, SIGNAL(tapped(QEventPoint, Qt::MouseButton)));
+ QSignalSpy clickedSpy(&tapHandler, SIGNAL(tapped(QEventPoint,Qt::MouseButton)));
// Tap outside the content item in the top-left corner
QTest::mouseClick(&window, Qt::LeftButton, {}, QPoint(5, 5));
@@ -2935,6 +3007,42 @@ void tst_qquickflickable::flickWhenRotated() // QTBUG-99639
QVERIFY(!flickable->isAtYBeginning());
}
+void tst_qquickflickable::flickAndReleaseOutsideBounds() // QTBUG-104987
+{
+ // Check that flicking works when the mouse release happens
+ // outside the bounds of the flickable (and the flick started on top
+ // of a TapHandler that has a passive grab).
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("flickableWithTapHandler.qml")));
+ QQuickItem *rootItem = window.rootObject();
+ QVERIFY(rootItem);
+ QQuickFlickable *flickable = rootItem->findChild<QQuickFlickable*>();
+ QVERIFY(flickable);
+ QQuickItem *childItem = flickable->findChild<QQuickItem*>("childItem");
+ QVERIFY(childItem);
+
+ QVERIFY(flickable->isAtYBeginning());
+
+ // Startpoint is on top of the tapHandler, while the endpoint is outside the flickable
+ const QPointF startPos = childItem->mapToGlobal(QPoint(10, 10));
+ const QPointF endPos = flickable->mapToGlobal(QPoint(10, -10));
+ const QPoint globalStartPos = window.mapFromGlobal(startPos).toPoint();
+ const QPoint globalEndPos = window.mapFromGlobal(endPos).toPoint();
+ const qreal dragDistance = 20;
+
+ // Note: here we need to initiate a flick using raw events, rather than
+ // flickable.flick(), since we're testing if the mouse events takes the
+ // correct path to starts a flick (among passive and exclusive grabbers, combined
+ // with childMouseEventFilter()).
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, globalStartPos);
+ QTest::mouseMove(&window, globalStartPos - QPoint(0, dragDistance / 2));
+ QTest::mouseMove(&window, globalStartPos - QPoint(0, dragDistance));
+ QTest::mouseMove(&window, globalEndPos);
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, globalEndPos);
+
+ // Ensure that the content item ends up being moved more than what we dragged
+ QTRY_VERIFY(flickable->contentY() > dragDistance * 2);
+}
void tst_qquickflickable::scrollingWithFractionalExtentSize_data()
{
@@ -3123,6 +3231,207 @@ void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966
QVERIFY(!flickable->isDragging());
}
+void tst_qquickflickable::coalescedMove()
+{
+ QQuickView *window = new QQuickView;
+ QScopedPointer<QQuickView> windowPtr(window);
+ windowPtr->setSource(testFileUrl("flickable03.qml"));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+ QQuickVisualTestUtils::centerOnScreen(window);
+ QQuickVisualTestUtils::moveMouseAway(window);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(window->rootObject() != nullptr);
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
+ QVERIFY(flickable != nullptr);
+
+ QSignalSpy movementStartedSpy(flickable, SIGNAL(movementStarted()));
+ QSignalSpy movementEndedSpy(flickable, SIGNAL(movementEnded()));
+ QSignalSpy flickStartedSpy(flickable, SIGNAL(flickStarted()));
+ QSignalSpy flickEndedSpy(flickable, SIGNAL(flickEnded()));
+
+ QTest::touchEvent(window, touchDevice).press(0, {10, 10}).commit();
+
+ QTest::touchEvent(window, touchDevice).move(0, {10, 40}).commit();
+
+ QTest::touchEvent(window, touchDevice).move(0, {10, 100}).commit();
+
+ QTest::touchEvent(window, touchDevice).release(0, {10, 150}).commit();
+ QQuickTouchUtils::flush(window);
+
+ QTRY_VERIFY(!flickable->isMoving());
+
+ QCOMPARE(movementStartedSpy.size(), 1);
+ QCOMPARE(flickStartedSpy.size(), 1);
+ QCOMPARE(movementEndedSpy.size(), 1);
+ QCOMPARE(flickEndedSpy.size(), 1);
+}
+
+void tst_qquickflickable::onlyOneMove()
+{
+ QQuickView *window = new QQuickView;
+ QScopedPointer<QQuickView> windowPtr(window);
+ windowPtr->setSource(testFileUrl("flickable03.qml"));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+ QQuickVisualTestUtils::centerOnScreen(window);
+ QQuickVisualTestUtils::moveMouseAway(window);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(window->rootObject() != nullptr);
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
+ QVERIFY(flickable != nullptr);
+
+ QSignalSpy movementStartedSpy(flickable, SIGNAL(movementStarted()));
+ QSignalSpy movementEndedSpy(flickable, SIGNAL(movementEnded()));
+ QSignalSpy flickStartedSpy(flickable, SIGNAL(flickStarted()));
+ QSignalSpy flickEndedSpy(flickable, SIGNAL(flickEnded()));
+
+ QTest::touchEvent(window, touchDevice).press(0, {10, 10}).commit();
+ QQuickTouchUtils::flush(window);
+
+ QTest::touchEvent(window, touchDevice).move(0, {10, 100}).commit();
+ QQuickTouchUtils::flush(window);
+
+ QTest::touchEvent(window, touchDevice).release(0, {10, 200}).commit();
+ QQuickTouchUtils::flush(window);
+
+ QTRY_VERIFY(!flickable->isMoving());
+
+ QCOMPARE(movementStartedSpy.size(), 1);
+ QCOMPARE(flickStartedSpy.size(), 1);
+ QCOMPARE(movementEndedSpy.size(), 1);
+ QCOMPARE(flickEndedSpy.size(), 1);
+}
+
+void tst_qquickflickable::proportionalWheelScrolling() // QTBUG-106338 etc.
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("wheel.qml")));
+ QQuickViewTestUtils::centerOnScreen(&window);
+ QVERIFY(window.isVisible());
+ QQuickItem *rootItem = window.rootObject();
+ QVERIFY(rootItem);
+ QQuickFlickable *flickable = rootItem->findChild<QQuickFlickable *>();
+ QVERIFY(flickable);
+
+ QVERIFY(!flickable->property("ended").value<bool>());
+
+ QPointF pos(flickable->x() + flickable->width() / 2, flickable->y() + flickable->height() / 2);
+ QPoint angleDelta(0, -120);
+ QWheelEvent wheelEvent(pos, window.mapToGlobal(pos), QPoint(), angleDelta,
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
+
+ QGuiApplication::sendEvent(&window, &wheelEvent);
+ qApp->processEvents();
+
+ // Verify that scrolling is proportional to the wheel delta
+ QVERIFY(flickable->isMovingVertically());
+ QTRY_VERIFY(!flickable->isMovingVertically());
+
+ // The current movement formula being used is: delta / 120 * wheelScrollLines * 24
+ const int defaultWheelDecel = 15000;
+ bool wheelDecelerationEnvSet = false;
+ const int wheelDecelerationEnv = qEnvironmentVariableIntValue("QT_QUICK_FLICKABLE_WHEEL_DECELERATION", &wheelDecelerationEnvSet);
+ const qreal wheelDecel = wheelDecelerationEnvSet ? wheelDecelerationEnv : defaultWheelDecel;
+ const bool proportionalWheel = wheelDecel >= 15000;
+ qCDebug(lcTests) << "platform wheel decel" << defaultWheelDecel
+ << "env wheel decel" << wheelDecelerationEnv
+ << "expect proportional scrolling?" << proportionalWheel;
+ const qreal expectedMovementFromWheelClick = qAbs(angleDelta.y()) / 120 * qApp->styleHints()->wheelScrollLines() * 24;
+
+ if (proportionalWheel)
+ QCOMPARE(flickable->contentY(), expectedMovementFromWheelClick);
+
+ QVERIFY(flickable->property("ended").value<bool>());
+ QCOMPARE(flickable->property("movementsAfterEnd").value<int>(), 0);
+
+ flickable->setProperty("ended", QVariant::fromValue(false));
+ flickable->setContentY(0);
+
+ // Verify that multiple wheel events in a row won't accumulate the scroll distance, before the timeline completes
+ wheelEvent.setTimestamp(wheelEvent.timestamp() + 2000);
+ QGuiApplication::sendEvent(&window, &wheelEvent);
+
+ wheelEvent.setTimestamp(wheelEvent.timestamp() + 10);
+ QGuiApplication::sendEvent(&window, &wheelEvent);
+
+ wheelEvent.setTimestamp(wheelEvent.timestamp() + 10);
+ QGuiApplication::sendEvent(&window, &wheelEvent);
+
+ qApp->processEvents();
+
+ QVERIFY(flickable->isMovingVertically());
+ QTRY_VERIFY(!flickable->isMovingVertically());
+
+ if (proportionalWheel) {
+ QVERIFY2(flickable->contentY() >= expectedMovementFromWheelClick, "The contentItem moved shorter than expected from a wheelEvent");
+ QCOMPARE_LT(flickable->contentY(), expectedMovementFromWheelClick * 3);
+ }
+
+ QVERIFY(flickable->property("ended").value<bool>());
+ QCOMPARE(flickable->property("movementsAfterEnd").value<int>(), 0);
+}
+
+void tst_qquickflickable::touchCancel()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("flickable03.qml")));
+ QQuickViewTestUtils::centerOnScreen(&window);
+ QVERIFY(window.isVisible());
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window.rootObject());
+ QVERIFY(flickable != nullptr);
+
+ QSignalSpy movementStartedSpy(flickable, SIGNAL(movementStarted()));
+ QSignalSpy movementEndedSpy(flickable, SIGNAL(movementEnded()));
+
+ int touchPosY = 10;
+ QTest::touchEvent(&window, touchDevice).press(0, {10, touchPosY}).commit();
+ QQuickTouchUtils::flush(&window);
+
+ for (int i = 0; i < 3; ++i) {
+ touchPosY += qApp->styleHints()->startDragDistance();
+ QTest::touchEvent(&window, touchDevice).move(0, {10, touchPosY}).commit();
+ QQuickTouchUtils::flush(&window);
+ }
+
+ QTRY_COMPARE(movementStartedSpy.size(), 1);
+ QWindowSystemInterface::handleTouchCancelEvent(nullptr, touchDevice);
+ QTRY_COMPARE(movementEndedSpy.size(), 1);
+}
+
+void tst_qquickflickable::pixelAlignedEndPoints()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("endpoints.qml")));
+ QQuickViewTestUtils::centerOnScreen(&window);
+ QVERIFY(window.isVisible());
+ QQuickItem *rootItem = window.rootObject();
+ QVERIFY(rootItem);
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(rootItem);
+ QVERIFY(flickable);
+ flickable->setPixelAligned(true);
+ QVERIFY(flickable->isAtYBeginning());
+
+ QSignalSpy isAtEndSpy(flickable, &QQuickFlickable::atYEndChanged);
+ QSignalSpy isAtBeginningSpy(flickable, &QQuickFlickable::atYBeginningChanged);
+
+ flickable->setContentY(199.99);
+ QCOMPARE(flickable->contentY(), 200);
+ QVERIFY(!flickable->isAtYBeginning());
+ QVERIFY(flickable->isAtYEnd());
+ QCOMPARE(isAtEndSpy.count(), 1);
+ QCOMPARE(isAtBeginningSpy.count(), 1);
+
+ flickable->setContentY(0.01);
+ QCOMPARE(flickable->contentY(), 0);
+ QVERIFY(flickable->isAtYBeginning());
+ QVERIFY(!flickable->isAtYEnd());
+ QCOMPARE(isAtEndSpy.count(), 2);
+ QCOMPARE(isAtBeginningSpy.count(), 2);}
+
QTEST_MAIN(tst_qquickflickable)
#include "tst_qquickflickable.moc"
diff --git a/tests/auto/quick/qquickflipable/CMakeLists.txt b/tests/auto/quick/qquickflipable/CMakeLists.txt
index d764b1cfe4..f73d4f81d1 100644
--- a/tests/auto/quick/qquickflipable/CMakeLists.txt
+++ b/tests/auto/quick/qquickflipable/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickflipable Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickflipable LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml b/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml
new file mode 100644
index 0000000000..a52e1fd942
--- /dev/null
+++ b/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 UnionTech Software Technology Co., Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Flipable {
+ id: flipable
+
+ property real angle: 0
+ width: 3840 // wider than 1024 * 2: part of it goes behind the camera while flipping
+ height: 2160
+
+ front: Rectangle {
+ width: parent.width
+ height: parent.height
+ color: "red"
+ anchors.centerIn: parent
+ }
+ back: Rectangle {
+ color: "yellow"
+ anchors.centerIn: parent
+ width: parent.width
+ height: parent.height
+ }
+ transform: Rotation {
+ id: rotation
+ origin.x: flipable.width / 2
+ origin.y: flipable.height / 2
+ axis.x: 0; axis.y: 1; axis.z: 0
+ angle: flipable.angle
+ }
+}
diff --git a/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp b/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
index c1eab29759..5941f32832 100644
--- a/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
+++ b/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -27,6 +27,9 @@ private slots:
void QTBUG_9161_crash();
void QTBUG_8474_qgv_abort();
+ void flipRotationAngle_data();
+ void flipRotationAngle();
+
private:
QQmlEngine engine;
};
@@ -112,6 +115,31 @@ void tst_qquickflipable::QTBUG_8474_qgv_abort()
delete window;
}
+void tst_qquickflipable::flipRotationAngle_data()
+{
+ QTest::addColumn<int>("angle");
+ QTest::addColumn<QQuickFlipable::Side>("side");
+
+ QTest::newRow("89") << 89 << QQuickFlipable::Front;
+ QTest::newRow("91") << 91 << QQuickFlipable::Back;
+ QTest::newRow("-89") << -89 << QQuickFlipable::Front;
+ QTest::newRow("-91") << -91 << QQuickFlipable::Back;
+}
+
+void tst_qquickflipable::flipRotationAngle() // QTBUG-75954
+{
+ QFETCH(int, angle);
+ QFETCH(QQuickFlipable::Side, side);
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("flip-y-axis-flipable.qml"));
+ QQuickFlipable *obj = qobject_cast<QQuickFlipable*>(c.create());
+ QVERIFY(obj != nullptr);
+ obj->setProperty("angle", angle);
+ QCOMPARE(obj->side(), side);
+ delete obj;
+}
+
QTEST_MAIN(tst_qquickflipable)
#include "tst_qquickflipable.moc"
diff --git a/tests/auto/quick/qquickfocusscope/CMakeLists.txt b/tests/auto/quick/qquickfocusscope/CMakeLists.txt
index a9ea5022e0..755612121e 100644
--- a/tests/auto/quick/qquickfocusscope/CMakeLists.txt
+++ b/tests/auto/quick/qquickfocusscope/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickfocusscope Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickfocusscope LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp b/tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp
index 4dcbcc4884..70e5f227d3 100644
--- a/tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp
+++ b/tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QSignalSpy>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quick/qquickfontloader/CMakeLists.txt b/tests/auto/quick/qquickfontloader/CMakeLists.txt
index e13bf4e2e3..22f98b36d5 100644
--- a/tests/auto/quick/qquickfontloader/CMakeLists.txt
+++ b/tests/auto/quick/qquickfontloader/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickfontloader Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickfontloader LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp b/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp
index c34edbcdde..a94d9fdd21 100644
--- a/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp
+++ b/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quick/qquickfontloader_static/CMakeLists.txt b/tests/auto/quick/qquickfontloader_static/CMakeLists.txt
index 48e1459b64..3d1fe63d6b 100644
--- a/tests/auto/quick/qquickfontloader_static/CMakeLists.txt
+++ b/tests/auto/quick/qquickfontloader_static/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickfontloader_static Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickfontloader_static LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp b/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp
index aee924dd50..ed692fcdd0 100644
--- a/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp
+++ b/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickView>
diff --git a/tests/auto/quick/qquickfontmetrics/CMakeLists.txt b/tests/auto/quick/qquickfontmetrics/CMakeLists.txt
index da375bcbed..02a23f988e 100644
--- a/tests/auto/quick/qquickfontmetrics/CMakeLists.txt
+++ b/tests/auto/quick/qquickfontmetrics/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_quickfontmetrics Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_quickfontmetrics LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_quickfontmetrics
SOURCES
tst_quickfontmetrics.cpp
diff --git a/tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp b/tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp
index f34bdc2059..090d47d652 100644
--- a/tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp
+++ b/tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QFont>
#include <QFontDatabase>
@@ -30,10 +30,10 @@ tst_QuickFontMetrics::tst_QuickFontMetrics()
void tst_QuickFontMetrics::properties()
{
- QStringList families = QFontDatabase::families().mid(0, 10);
+ const QStringList families = QFontDatabase::families().mid(0, 10);
QQuickFontMetrics metrics;
- foreach (const QString &family, families) {
+ for (const QString &family : families) {
QFont font(family);
QFontMetricsF expected(font);
diff --git a/tests/auto/quick/qquickframebufferobject/CMakeLists.txt b/tests/auto/quick/qquickframebufferobject/CMakeLists.txt
index 7c02a2da42..9d87a5703d 100644
--- a/tests/auto/quick/qquickframebufferobject/CMakeLists.txt
+++ b/tests/auto/quick/qquickframebufferobject/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickframebufferobject Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickframebufferobject LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickframebufferobject/data/testStuff.qml b/tests/auto/quick/qquickframebufferobject/data/testStuff.qml
index b702b6c64b..fbde7ecc93 100644
--- a/tests/auto/quick/qquickframebufferobject/data/testStuff.qml
+++ b/tests/auto/quick/qquickframebufferobject/data/testStuff.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import FBOItem 1.0
diff --git a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp
index e6f74fb287..ffd6478f9c 100644
--- a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp
+++ b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
diff --git a/tests/auto/quick/qquickgraphicsinfo/CMakeLists.txt b/tests/auto/quick/qquickgraphicsinfo/CMakeLists.txt
index bfca6770af..13759ea9e3 100644
--- a/tests/auto/quick/qquickgraphicsinfo/CMakeLists.txt
+++ b/tests/auto/quick/qquickgraphicsinfo/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickgraphicsinfo Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickgraphicsinfo LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp b/tests/auto/quick/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp
index 9ffe3c0aca..e142cbaad6 100644
--- a/tests/auto/quick/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp
+++ b/tests/auto/quick/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
diff --git a/tests/auto/quick/qquickgridview/CMakeLists.txt b/tests/auto/quick/qquickgridview/CMakeLists.txt
index b16f37a93f..38dfc4f1f6 100644
--- a/tests/auto/quick/qquickgridview/CMakeLists.txt
+++ b/tests/auto/quick/qquickgridview/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickgridview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickgridview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickgridview/data/attachedProperties.qml b/tests/auto/quick/qquickgridview/data/attachedProperties.qml
index e00c566f47..f5fb173b89 100644
--- a/tests/auto/quick/qquickgridview/data/attachedProperties.qml
+++ b/tests/auto/quick/qquickgridview/data/attachedProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickgridview/data/displayMargin.qml b/tests/auto/quick/qquickgridview/data/displayMargin.qml
index 749deb5926..9bfdafbe61 100644
--- a/tests/auto/quick/qquickgridview/data/displayMargin.qml
+++ b/tests/auto/quick/qquickgridview/data/displayMargin.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
diff --git a/tests/auto/quick/qquickgridview/data/margins2.qml b/tests/auto/quick/qquickgridview/data/margins2.qml
index 6af4f5ca20..7735fc5362 100644
--- a/tests/auto/quick/qquickgridview/data/margins2.qml
+++ b/tests/auto/quick/qquickgridview/data/margins2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.7
diff --git a/tests/auto/quick/qquickgridview/data/negativeDisplayMargin.qml b/tests/auto/quick/qquickgridview/data/negativeDisplayMargin.qml
index d6a45e1a2a..3d5c8494a6 100644
--- a/tests/auto/quick/qquickgridview/data/negativeDisplayMargin.qml
+++ b/tests/auto/quick/qquickgridview/data/negativeDisplayMargin.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
diff --git a/tests/auto/quick/qquickgridview/data/qtbug49218.qml b/tests/auto/quick/qquickgridview/data/qtbug49218.qml
index 353157cf2a..3689c45b0e 100644
--- a/tests/auto/quick/qquickgridview/data/qtbug49218.qml
+++ b/tests/auto/quick/qquickgridview/data/qtbug49218.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
Item {
diff --git a/tests/auto/quick/qquickgridview/data/qtbug91461.qml b/tests/auto/quick/qquickgridview/data/qtbug91461.qml
index e547dc6184..4a13984b80 100644
--- a/tests/auto/quick/qquickgridview/data/qtbug91461.qml
+++ b/tests/auto/quick/qquickgridview/data/qtbug91461.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
Item {
diff --git a/tests/auto/quick/qquickgridview/data/removeAccessibleChildrenEvenIfReusingItems.qml b/tests/auto/quick/qquickgridview/data/removeAccessibleChildrenEvenIfReusingItems.qml
new file mode 100644
index 0000000000..a1d00bfbdf
--- /dev/null
+++ b/tests/auto/quick/qquickgridview/data/removeAccessibleChildrenEvenIfReusingItems.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.15
+
+Rectangle {
+ id: page
+ width: 640
+ height: 480
+
+ Accessible.role : Accessible.Client
+
+ GridView {
+ id: grid
+
+ width: 640
+ height: 480
+ model: listModel
+
+ cellWidth: 180
+ cellHeight: 30
+
+ delegate: Text {
+ id: textDelegate
+ text: name
+ Accessible.role: Accessible.Button
+ Accessible.name: name
+ }
+ reuseItems: true
+ Accessible.role : Accessible.Client
+ }
+
+ ListModel {
+ id: listModel
+ ListElement { name: "item11"}
+ ListElement { name: "item12"}
+ ListElement { name: "item13"}
+ ListElement { name: "item14"}
+ }
+
+ function replaceItems() {
+ listModel.clear()
+ listModel.insert(0, {"name" : "item21"})
+ listModel.insert(1, {"name" : "item22"})
+ listModel.insert(2, {"name" : "item23"})
+ listModel.insert(3, {"name" : "item24"})
+ }
+}
diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 9e42e0f4e4..3eb96e2bb4 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/qstringlistmodel.h>
@@ -194,6 +194,7 @@ private slots:
void keyNavigationEnabled();
void releaseItems();
+ void removeAccessibleChildrenEvenIfReusingItems();
private:
QList<int> toIntList(const QVariantList &list);
@@ -541,7 +542,7 @@ void tst_QQuickGridView::inserted_defaultLayout(QQuickGridView::Flow flow,
int firstVisibleIndex = -1;
for (int i=0; i<items.size(); i++) {
QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
- if (item && delegateVisible(item)) {
+ if (item && isDelegateVisible(item)) {
firstVisibleIndex = i;
break;
}
@@ -963,7 +964,7 @@ void tst_QQuickGridView::removed_defaultLayout(QQuickGridView::Flow flow,
QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
if (item) {
QRectF itemRect(item->x(), item->y(), item->width(), item->height());
- if (delegateVisible(item) && viewRect.intersects(itemRect)) {
+ if (isDelegateVisible(item) && viewRect.intersects(itemRect)) {
firstVisibleIndex = i;
QQmlExpression en(qmlContext(item), item, "name");
firstName = en.evaluate().toString();
@@ -1311,7 +1312,7 @@ void tst_QQuickGridView::moved_defaultLayout(QQuickGridView::Flow flow,
int firstVisibleIndex = -1;
for (int i=0; i<items.size(); i++) {
QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
- if (item && delegateVisible(item)) {
+ if (item && isDelegateVisible(item)) {
firstVisibleIndex = i;
break;
}
@@ -1891,9 +1892,9 @@ void tst_QQuickGridView::currentIndex()
// moving currentItem out of view should make it invisible
gridview->setCurrentIndex(0);
- QTRY_VERIFY(delegateVisible(gridview->currentItem()));
+ QTRY_VERIFY(isDelegateVisible(gridview->currentItem()));
gridview->setContentY(200);
- QTRY_VERIFY(!delegateVisible(gridview->currentItem()));
+ QTRY_VERIFY(!isDelegateVisible(gridview->currentItem()));
delete window;
}
@@ -2715,14 +2716,14 @@ void tst_QQuickGridView::mirroring()
QCOMPARE(gridviewA->layoutDirection(), gridviewA->effectiveLayoutDirection());
// LTR != RTL
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
gridviewB->setProperty("layoutDirection", Qt::LeftToRight);
// LTR == LTR
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
QCOMPARE(gridviewB->layoutDirection(), gridviewB->effectiveLayoutDirection());
@@ -2730,25 +2731,25 @@ void tst_QQuickGridView::mirroring()
QVERIFY(gridviewB->layoutDirection() != gridviewB->effectiveLayoutDirection());
// LTR != LTR+mirror
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
gridviewA->setProperty("layoutDirection", Qt::RightToLeft);
// RTL == LTR+mirror
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
// RTL != RTL+mirror
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
// LTR == RTL+mirror
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
delete windowA;
@@ -3662,7 +3663,7 @@ void tst_QQuickGridView::resizeViewAndRepaint()
QTRY_VERIFY(item);
QTRY_COMPARE(item->x(), qreal((i%3)*80));
QTRY_COMPARE(item->y(), qreal((i/3)*60));
- QCOMPARE(delegateVisible(item), i < 9); // inside view visible, outside not visible
+ QCOMPARE(isDelegateVisible(item), i < 9); // inside view visible, outside not visible
}
// ensure items outside view become invisible
@@ -3676,7 +3677,7 @@ void tst_QQuickGridView::resizeViewAndRepaint()
QTRY_VERIFY(item);
QTRY_COMPARE(item->x(), qreal((i%3)*80));
QTRY_COMPARE(item->y(), qreal((i/3)*60));
- QCOMPARE(delegateVisible(item), i < 6); // inside view visible, outside not visible
+ QCOMPARE(isDelegateVisible(item), i < 6); // inside view visible, outside not visible
}
delete window;
@@ -5790,7 +5791,8 @@ void tst_QQuickGridView::multipleDisplaced()
QTRY_VERIFY(gridview->property("displaceTransitionsDone").toBool());
QVariantMap transitionsStarted = gridview->property("displaceTransitionsStarted").toMap();
- foreach (const QString &name, transitionsStarted.keys()) {
+ const QStringList keys = transitionsStarted.keys();
+ for (const QString &name : keys) {
QVERIFY2(transitionsStarted[name] == 1,
QTest::toString(QString("%1 was displaced %2 times").arg(name).arg(transitionsStarted[name].toInt())));
}
@@ -6048,23 +6050,23 @@ void tst_QQuickGridView::unrequestedVisibility()
QQuickItem *item;
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 9));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 10));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
rightview->setCurrentIndex(0);
@@ -6072,9 +6074,9 @@ void tst_QQuickGridView::unrequestedVisibility()
QTRY_COMPARE(rightview->contentY(), 0.0);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
- QTRY_COMPARE(delegateVisible(item), true);
+ QTRY_COMPARE(isDelegateVisible(item), true);
QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 11));
QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 11));
@@ -6085,98 +6087,98 @@ void tst_QQuickGridView::unrequestedVisibility()
QTRY_COMPARE(rightview->contentY(), 0.0);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
- QTRY_COMPARE(delegateVisible(item), false);
+ QTRY_COMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
// move a non-visible item into view
model.moveItems(10, 9, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
// move a visible item out of view
model.moveItems(5, 3, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
// move a non-visible item into view
model.moveItems(3, 5, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
// move a visible item out of view
model.moveItems(9, 10, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
// move a non-visible item into view
model.moveItems(10, 9, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
delete window;
}
@@ -6464,22 +6466,22 @@ void tst_QQuickGridView::displayMargin()
QQuickItem *item97;
QVERIFY(item0 = findItem<QQuickItem>(content, "delegate", 0));
- QCOMPARE(delegateVisible(item0), true);
+ QCOMPARE(isDelegateVisible(item0), true);
// the 97th item should be within the end margin
QVERIFY(item97 = findItem<QQuickItem>(content, "delegate", 96));
- QCOMPARE(delegateVisible(item97), true);
+ QCOMPARE(isDelegateVisible(item97), true);
// GridView staggers item creation, so the 118th item should be outside the end margin.
QVERIFY(findItem<QQuickItem>(content, "delegate", 117) == nullptr);
// the first delegate should still be within the begin margin
gridview->positionViewAtIndex(20, QQuickGridView::Beginning);
- QCOMPARE(delegateVisible(item0), true);
+ QCOMPARE(isDelegateVisible(item0), true);
// the first delegate should now be outside the begin margin
gridview->positionViewAtIndex(36, QQuickGridView::Beginning);
- QCOMPARE(delegateVisible(item0), false);
+ QCOMPARE(isDelegateVisible(item0), false);
delete window;
}
@@ -6503,26 +6505,26 @@ void tst_QQuickGridView::negativeDisplayMargin()
QVERIFY(content != nullptr);
QVERIFY(item = findItem<QQuickItem>(content, "delegate", 0));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(content, "delegate", 7));
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
QVERIFY(item = findItem<QQuickItem>(content, "delegate", 8));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
// Flick until contentY means that delegate8 should be visible
listview->setProperty("contentY", 500);
QVERIFY(item = findItem<QQuickItem>(content, "delegate", 8));
- QTRY_COMPARE(delegateVisible(item), true);
+ QTRY_COMPARE(isDelegateVisible(item), true);
listview->setProperty("contentY", 1000);
QTRY_VERIFY(item = findItem<QQuickItem>(content, "delegate", 14));
- QTRY_COMPARE(delegateVisible(item), true);
+ QTRY_COMPARE(isDelegateVisible(item), true);
listview->setProperty("contentY", 0);
QVERIFY(item = findItem<QQuickItem>(content, "delegate", 4));
- QTRY_COMPARE(delegateVisible(item), true);
+ QTRY_COMPARE(isDelegateVisible(item), true);
delete window;
}
@@ -6843,6 +6845,36 @@ void tst_QQuickGridView::releaseItems()
gridview->setModel(123);
}
+void tst_QQuickGridView::removeAccessibleChildrenEvenIfReusingItems()
+{
+ auto window = std::make_unique<QQuickView>();
+ window->setSource(testFileUrl("removeAccessibleChildrenEvenIfReusingItems.qml"));
+ window->show();
+
+ QQuickItem *contentItem = window->contentItem();
+ QVERIFY(contentItem);
+ QQuickItem *rootItem = contentItem->childItems().first();
+ QVERIFY(rootItem);
+
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get());
+ QVERIFY(iface);
+ QAccessibleInterface *gridView = iface->child(0)->child(0);
+ QCOMPARE(gridView->childCount(), 4);
+ QCOMPARE(gridView->child(0)->text(QAccessible::Text::Name), "item11");
+ QCOMPARE(gridView->child(1)->text(QAccessible::Text::Name), "item12");
+ QCOMPARE(gridView->child(2)->text(QAccessible::Text::Name), "item13");
+ QCOMPARE(gridView->child(3)->text(QAccessible::Text::Name), "item14");
+
+ QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "replaceItems"));
+
+ QCOMPARE(gridView->childCount(), 4);
+ QTRY_COMPARE(gridView->child(0)->text(QAccessible::Text::Name), "item21");
+ QTRY_COMPARE(gridView->child(1)->text(QAccessible::Text::Name), "item22");
+ QTRY_COMPARE(gridView->child(2)->text(QAccessible::Text::Name), "item23");
+ QTRY_COMPARE(gridView->child(3)->text(QAccessible::Text::Name), "item24");
+}
+
+
QTEST_MAIN(tst_QQuickGridView)
#include "tst_qquickgridview.moc"
diff --git a/tests/auto/quick/qquickimage/BLACKLIST b/tests/auto/quick/qquickimage/BLACKLIST
index 348ae127f3..a5c718c4f0 100644
--- a/tests/auto/quick/qquickimage/BLACKLIST
+++ b/tests/auto/quick/qquickimage/BLACKLIST
@@ -1,6 +1,3 @@
-# QTBUG-98402
-[mirror]
-macos-12 ci
# QTBUG-102721
[hugeImages]
android
diff --git a/tests/auto/quick/qquickimage/CMakeLists.txt b/tests/auto/quick/qquickimage/CMakeLists.txt
index 4e3de0a905..774bdf0dcd 100644
--- a/tests/auto/quick/qquickimage/CMakeLists.txt
+++ b/tests/auto/quick/qquickimage/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickimage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickimage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickimage/data/hugeImages.qml b/tests/auto/quick/qquickimage/data/hugeImages.qml
index ee141c9456..00aad415f0 100644
--- a/tests/auto/quick/qquickimage/data/hugeImages.qml
+++ b/tests/auto/quick/qquickimage/data/hugeImages.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickimage/data/qtbug_32513.qml b/tests/auto/quick/qquickimage/data/qtbug_32513.qml
index 438717155b..6f8adc6ba4 100644
--- a/tests/auto/quick/qquickimage/data/qtbug_32513.qml
+++ b/tests/auto/quick/qquickimage/data/qtbug_32513.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickimage/data/statusChanged.qml b/tests/auto/quick/qquickimage/data/statusChanged.qml
new file mode 100644
index 0000000000..ce4b4c4be9
--- /dev/null
+++ b/tests/auto/quick/qquickimage/data/statusChanged.qml
@@ -0,0 +1,25 @@
+import QtQuick
+
+Image {
+ id: root
+ property var changeSignals: []
+ property var statusChanges: []
+ property size statusChangedFirstImplicitSize: Qt.size(-1,-1)
+
+ source: "heart.png"
+
+ onFrameCountChanged: root.changeSignals.push("frameCount")
+ onCurrentFrameChanged: root.changeSignals.push("currentFrame")
+ onSourceSizeChanged: root.changeSignals.push("sourceSize")
+ onImplicitWidthChanged: root.changeSignals.push("implicitWidth")
+ onImplicitHeightChanged: root.changeSignals.push("implicitHeight")
+ onPaintedWidthChanged: root.changeSignals.push("paintedWidth")
+ onPaintedHeightChanged: root.changeSignals.push("paintedHeight")
+ onProgressChanged: root.changeSignals.push("progress")
+ onStatusChanged: (status) => {
+ root.changeSignals.push("status")
+ root.statusChanges.push(status)
+ if (root.statusChangedFirstImplicitSize.width < 0)
+ root.statusChangedFirstImplicitSize = Qt.size(root.implicitWidth, root.implicitHeight)
+ }
+}
diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
index 89b20c3131..427a45977f 100644
--- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp
+++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QTextDocument>
#include <QTcpServer>
@@ -24,10 +24,13 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
// #define DEBUG_WRITE_OUTPUT
+Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+
using namespace QQuickVisualTestUtils;
Q_DECLARE_METATYPE(QQuickImageBase::Status)
@@ -68,6 +71,7 @@ private slots:
void sourceClipRect_data();
void sourceClipRect();
void progressAndStatusChanges();
+ void progressAndChangeSignalOrder();
void sourceSizeChanges();
void correctStatus();
void highdpi();
@@ -308,15 +312,19 @@ void tst_qquickimage::mirror()
QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QMap<QQuickImage::FillMode, QImage> screenshots;
- QList<QQuickImage::FillMode> fillModes;
- fillModes << QQuickImage::Stretch << QQuickImage::PreserveAspectFit << QQuickImage::PreserveAspectCrop
- << QQuickImage::Tile << QQuickImage::TileVertically << QQuickImage::TileHorizontally << QQuickImage::Pad;
+ const QList<QQuickImage::FillMode> fillModes{QQuickImage::Stretch,
+ QQuickImage::PreserveAspectFit,
+ QQuickImage::PreserveAspectCrop,
+ QQuickImage::Tile,
+ QQuickImage::TileVertically,
+ QQuickImage::TileHorizontally,
+ QQuickImage::Pad};
qreal width = 300;
qreal height = 250;
qreal devicePixelRatio = 1.0;
- foreach (QQuickImage::FillMode fillMode, fillModes) {
+ for (QQuickImage::FillMode fillMode : fillModes) {
QScopedPointer<QQuickView> window(new QQuickView);
window->setSource(testFileUrl("mirror.qml"));
@@ -333,7 +341,7 @@ void tst_qquickimage::mirror()
devicePixelRatio = window->devicePixelRatio();
}
- foreach (QQuickImage::FillMode fillMode, fillModes) {
+ for (QQuickImage::FillMode fillMode : fillModes) {
QPixmap srcPixmap;
QVERIFY(srcPixmap.load(testFile("pattern.png")));
@@ -453,9 +461,8 @@ void tst_qquickimage::geometry_data()
QTest::newRow("PreserveAspectCrop explicit width 300, height 400") << "PreserveAspectCrop" << true << true << 300.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0;
// bounding rect, painted rect and item rect are equal in stretching and tiling images
- QStringList fillModes;
- fillModes << "Stretch" << "Tile" << "TileVertically" << "TileHorizontally";
- foreach (QString fillMode, fillModes) {
+ QStringList fillModes{"Stretch", "Tile", "TileVertically", "TileHorizontally"};
+ for (const auto &fillMode : fillModes) {
QTest::newRow(fillMode.toLatin1()) << fillMode << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
QTest::newRow(QString(fillMode + " explicit width 300").toLatin1()) << fillMode << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 100.0 << 100.0;
QTest::newRow(QString(fillMode + " explicit height 400").toLatin1()) << fillMode << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 400.0 << 400.0;
@@ -975,6 +982,28 @@ void tst_qquickimage::progressAndStatusChanges()
delete obj;
}
+void tst_qquickimage::progressAndChangeSignalOrder()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("statusChanged.qml")));
+ QQuickImage *image = qmlobject_cast<QQuickImage *>(window.rootObject());
+ QVERIFY(image);
+
+ QTRY_COMPARE(image->status(), QQuickImageBase::Ready);
+ // QTBUG-120205: implicitSize should be correct when status changes to Ready
+ QCOMPARE(image->property("statusChangedFirstImplicitSize").toSize(), QSize(300, 300));
+ QCOMPARE(image->property("statusChanges").toList().size(), 1); // just Ready
+ const QStringList signalOrder = image->property("changeSignals").toStringList();
+ const QStringList expectedOrder = {"progress", "paintedHeight", "paintedWidth",
+ "implicitWidth", "implicitHeight",
+ "paintedHeight", "paintedWidth",
+ "status", "sourceSize", "frameCount"};
+ qCDebug(lcTests) << "signal order" << signalOrder;
+ // exact order may not be critical, and repeated signals may be silly;
+ // but this way we'll find out when it changes
+ QCOMPARE(signalOrder, expectedOrder);
+}
+
class TestQImageProvider : public QQuickImageProvider
{
public:
diff --git a/tests/auto/quick/qquickimageprovider/CMakeLists.txt b/tests/auto/quick/qquickimageprovider/CMakeLists.txt
index 801d54331b..33ab315873 100644
--- a/tests/auto/quick/qquickimageprovider/CMakeLists.txt
+++ b/tests/auto/quick/qquickimageprovider/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickimageprovider Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickimageprovider LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
index 78727d32a0..ae891c3f2a 100644
--- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
+++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
@@ -422,21 +423,19 @@ void tst_qquickimageprovider::threadTest()
QObject *obj = component.create();
//MUST not deadlock
QVERIFY(obj != nullptr);
- QList<QQuickImage *> images = obj->findChildren<QQuickImage *>();
+ const QList<QQuickImage *> images = obj->findChildren<QQuickImage *>();
QCOMPARE(images.size(), 4);
QTest::qWait(100);
- foreach (QQuickImage *img, images) {
+ for (QQuickImage *img : images)
QCOMPARE(img->status(), QQuickImage::Loading);
- }
{
QMutexLocker lock(&provider->mutex);
provider->ok = true;
provider->cond.wakeAll();
}
QTest::qWait(250);
- foreach (QQuickImage *img, images) {
+ for (QQuickImage *img : images)
QTRY_COMPARE(img->status(), QQuickImage::Ready);
- }
}
class TestImageResponseRunner : public QObject, public QRunnable {
@@ -541,21 +540,19 @@ void tst_qquickimageprovider::asyncTextureTest()
QObject *obj = component.create();
//MUST not deadlock
QVERIFY(obj != nullptr);
- QList<QQuickImage *> images = obj->findChildren<QQuickImage *>();
+ const QList<QQuickImage *> images = obj->findChildren<QQuickImage *>();
QCOMPARE(images.size(), 4);
QTRY_COMPARE(provider->pool.activeThreadCount(), 4);
- foreach (QQuickImage *img, images) {
+ for (QQuickImage *img : images)
QTRY_COMPARE(img->status(), QQuickImage::Loading);
- }
{
QMutexLocker lock(&provider->lock);
provider->ok = true;
provider->condition.wakeAll();
}
- foreach (QQuickImage *img, images) {
+ for (QQuickImage *img : images)
QTRY_COMPARE(img->status(), QQuickImage::Ready);
- }
}
class InstantAsyncImageResponse : public QQuickImageResponse
diff --git a/tests/auto/quick/qquickitem/CMakeLists.txt b/tests/auto/quick/qquickitem/CMakeLists.txt
index cf5293c698..82e7482e02 100644
--- a/tests/auto/quick/qquickitem/CMakeLists.txt
+++ b/tests/auto/quick/qquickitem/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickitem Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickitem LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -30,8 +36,6 @@ qt_internal_add_test(tst_qquickitem
#####################################################################
qt_internal_extend_target(tst_qquickitem CONDITION TARGET Qt::Widgets
- DEFINES
- TEST_QTBUG_60123
LIBRARIES
Qt::Widgets
)
diff --git a/tests/auto/quick/qquickitem/data/childAtRectangle.qml b/tests/auto/quick/qquickitem/data/childAtRectangle.qml
index 3070b33dca..b21f52c3cd 100644
--- a/tests/auto/quick/qquickitem/data/childAtRectangle.qml
+++ b/tests/auto/quick/qquickitem/data/childAtRectangle.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickitem/data/mainWindowQtBug60123.qml b/tests/auto/quick/qquickitem/data/mainWindowQtBug60123.qml
index 9f50be04cb..8790c5beb3 100644
--- a/tests/auto/quick/qquickitem/data/mainWindowQtBug60123.qml
+++ b/tests/auto/quick/qquickitem/data/mainWindowQtBug60123.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickitem/data/shortcutOverride.qml b/tests/auto/quick/qquickitem/data/shortcutOverride.qml
index dbc2be27b9..cde49ebaf9 100644
--- a/tests/auto/quick/qquickitem/data/shortcutOverride.qml
+++ b/tests/auto/quick/qquickitem/data/shortcutOverride.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.8
import QtQuick.Window 2.1
diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
index 23616156d6..e4cc434909 100644
--- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
@@ -24,7 +24,7 @@
#include <QTranslator>
#include <QtCore/qregularexpression.h>
-#ifdef TEST_QTBUG_60123
+#ifdef QT_WIDGETS_LIB
#include <QWidget>
#include <QMainWindow>
#endif
@@ -217,7 +217,7 @@ private slots:
void shortcutOverride();
-#ifdef TEST_QTBUG_60123
+#ifdef QT_WIDGETS_LIB
void qtBug60123();
#endif
@@ -228,6 +228,9 @@ private slots:
void polishLoopDetection();
void objectCastInDestructor();
+ void listsAreNotLists();
+
+ void transformChanged();
private:
@@ -1993,13 +1996,6 @@ void tst_qquickitem::acceptedMouseButtons()
QCOMPARE(item.releaseCount, 3);
}
-static void gc(QQmlEngine &engine)
-{
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
-}
-
void tst_qquickitem::visualParentOwnership()
{
QQmlEngine engine;
@@ -2338,7 +2334,7 @@ void tst_qquickitem::shortcutOverride()
QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 1);
}
-#ifdef TEST_QTBUG_60123
+#ifdef QT_WIDGETS_LIB
void tst_qquickitem::qtBug60123()
{
QMainWindow main;
@@ -2462,6 +2458,225 @@ void tst_qquickitem::objectCastInDestructor()
QVERIFY(QTest::qWaitFor([&destroyed]{ return destroyed; }));
}
+template<typename T>
+void verifyListProperty(const T &data)
+{
+ QVERIFY(data.object);
+ QVERIFY(data.append);
+ QVERIFY(data.count);
+ QVERIFY(data.at);
+ QVERIFY(data.clear);
+ QVERIFY(data.removeLast);
+
+ // We must not synthesize the replace and removeLast methods on those properties.
+ // They would be even more broken than the explicitly defined methods.
+ QVERIFY(!data.replace);
+}
+
+void tst_qquickitem::listsAreNotLists()
+{
+ QQuickItem item;
+ QQuickItem child;
+ QObject resource;
+
+ QQmlListProperty<QObject> data
+ = item.property("data").value<QQmlListProperty<QObject>>();
+ QQmlListProperty<QObject> resources
+ = item.property("resources").value<QQmlListProperty<QObject>>();
+ QQmlListProperty<QQuickItem> children
+ = item.property("children").value<QQmlListProperty<QQuickItem>>();
+
+ verifyListProperty(data);
+ verifyListProperty(resources);
+ verifyListProperty(children);
+
+
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+ children.append(&children, &child);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 1);
+ children.removeLast(&children);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+ data.append(&data, &child);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 1);
+ data.removeLast(&data);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+ children.append(&children, &child);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 1);
+ data.removeLast(&data);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+ data.append(&data, &child);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 1);
+ children.removeLast(&children);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+
+
+ resources.append(&resources, &resource);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 0);
+ resources.removeLast(&resources);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+ data.append(&data, &resource);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 0);
+ data.removeLast(&data);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+ resources.append(&resources, &resource);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 0);
+ data.removeLast(&data);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+ data.append(&data, &resource);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 0);
+ resources.removeLast(&resources);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+
+
+ children.append(&children, &child);
+ resources.append(&resources, &resource);
+ QCOMPARE(data.count(&data), 2);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 1);
+ children.removeLast(&children);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 0);
+ resources.removeLast(&resources);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+
+
+ children.append(&children, &child);
+ resources.append(&resources, &resource);
+ QCOMPARE(data.count(&data), 2);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 1);
+ resources.removeLast(&resources);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 1);
+ children.removeLast(&children);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+
+
+ data.append(&data, &child);
+ data.append(&data, &resource);
+ QCOMPARE(data.count(&data), 2);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 1);
+ data.removeLast(&data);
+ QCOMPARE(data.count(&data), 1);
+ QCOMPARE(resources.count(&resources), 1);
+ QCOMPARE(children.count(&children), 0);
+ data.removeLast(&data);
+ QCOMPARE(data.count(&data), 0);
+ QCOMPARE(resources.count(&resources), 0);
+ QCOMPARE(children.count(&children), 0);
+}
+
+class TransformItemPrivate;
+class TransformItem : public QQuickItem {
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(TransformItem)
+public:
+ TransformItem(QQuickItem *parent = nullptr);
+
+ bool transformChanged = false;
+};
+
+class TransformItemPrivate :public QQuickItemPrivate
+{
+protected:
+ Q_DECLARE_PUBLIC(TransformItem)
+
+ bool transformChanged(QQuickItem *transformedItem) override
+ {
+ Q_Q(TransformItem);
+ q->transformChanged = true;
+ return true;
+ }
+};
+
+TransformItem::TransformItem(QQuickItem *parent)
+ : QQuickItem(*(new TransformItemPrivate), parent)
+{
+}
+
+void tst_qquickitem::transformChanged()
+{
+ QQuickItem rootItem;
+ QQuickItem *parents[2][3] = {};
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ auto *item = new QQuickItem;
+ item->setObjectName(QString("Item-%1.%2").arg(i).arg(j));
+ item->setParentItem(j > 0 ? parents[i][j - 1] : &rootItem);
+ parents[i][j] = item;
+ }
+ }
+
+ // Setting the root item's position will result in transformChanged,
+ // which will clear the subtreeTransformChangedEnabled flag that is
+ // by default set to true, since there are no children that observe
+ // the viewport.
+ parents[0][0]->setPosition(QPoint(100, 100));
+ parents[1][0]->setPosition(QPoint(200, 200));
+
+ TransformItem transformItem;
+ transformItem.setParentItem(parents[0][2]);
+ transformItem.setFlag(QQuickItem::ItemObservesViewport);
+ QCOMPARE(transformItem.mapToScene(QPoint(0, 0)), parents[0][0]->position());
+
+ parents[0][0]->setPosition(QPoint(110, 110));
+ QVERIFY2(transformItem.transformChanged,
+ "Moving an ancestor should trigger transformChanged");
+
+ transformItem.transformChanged = false;
+ transformItem.setParentItem(parents[1][2]);
+ QVERIFY2(transformItem.transformChanged,
+ "Reparenting the item should result in a transformChanged");
+ QCOMPARE(transformItem.mapToScene(QPoint(0, 0)), parents[1][0]->position());
+
+ transformItem.transformChanged = false;
+ parents[1][0]->setPosition(QPoint(220, 220));
+ QVERIFY2(transformItem.transformChanged,
+ "Changing one of the new ancestors should result in transformChanged");
+ QCOMPARE(transformItem.mapToScene(QPoint(0, 0)), parents[1][0]->position());
+}
+
QTEST_MAIN(tst_qquickitem)
#include "tst_qquickitem.moc"
diff --git a/tests/auto/quick/qquickitem2/CMakeLists.txt b/tests/auto/quick/qquickitem2/CMakeLists.txt
index 44620cacd6..6b115efd2e 100644
--- a/tests/auto/quick/qquickitem2/CMakeLists.txt
+++ b/tests/auto/quick/qquickitem2/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickitem2 Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickitem2 LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -31,6 +37,11 @@ qt_internal_add_test(tst_qquickitem2
## Scopes:
#####################################################################
+qt_internal_extend_target(tst_qquickitem2 CONDITION TARGET Qt::Widgets
+ LIBRARIES
+ Qt::Widgets
+)
+
qt_internal_extend_target(tst_qquickitem2 CONDITION ANDROID OR IOS
DEFINES
QT_QMLTEST_DATADIR=":/data"
diff --git a/tests/auto/quick/qquickitem2/data/embedded.qml b/tests/auto/quick/qquickitem2/data/embedded.qml
new file mode 100644
index 0000000000..a9cf115699
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/embedded.qml
@@ -0,0 +1,30 @@
+import QtQuick
+
+Rectangle {
+ width: 300
+ height: 300
+
+ Column {
+ anchors.fill: parent
+ anchors.rightMargin: 2
+ anchors.leftMargin: 2
+ anchors.topMargin: 10
+ spacing: 20
+ Rectangle {
+ objectName: "rect1"
+ width: parent.width
+ height: 30
+ border.width: 1
+ border.color: activeFocus ? "blue" : "black"
+ focusPolicy: Qt.TabFocus
+ }
+ Rectangle {
+ objectName: "rect2"
+ width: parent.width
+ height: 30
+ border.width: 1
+ border.color: activeFocus ? "blue" : "black"
+ focusPolicy: Qt.TabFocus
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/data/focusInScopeChanges.qml b/tests/auto/quick/qquickitem2/data/focusInScopeChanges.qml
new file mode 100644
index 0000000000..3bf765a29d
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/focusInScopeChanges.qml
@@ -0,0 +1,31 @@
+import QtQuick
+
+Item {
+ id: main
+ objectName: "main"
+ width: 800
+ height: 600
+
+ FocusScope {
+ objectName: "focusScope"
+
+ Column {
+ Rectangle {
+ id: rectangle
+ focus: true
+ objectName: "rect"
+ width: textInput.width
+ height: textInput.height
+ border.width: 1
+ onActiveFocusChanged: textInput.forceActiveFocus()
+ }
+
+ TextInput {
+ id: textInput
+ objectName: "textInput"
+ font.pixelSize: 40
+ text: "focus me"
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/data/focusReason.qml b/tests/auto/quick/qquickitem2/data/focusReason.qml
new file mode 100644
index 0000000000..7f9e303dba
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/focusReason.qml
@@ -0,0 +1,85 @@
+import QtQuick
+
+Item {
+ Component.onCompleted: item.focus = true
+ width: 640
+ height: 480
+
+ Column {
+ anchors.top: parent.top
+ anchors.topMargin: 10
+ spacing: 10
+ objectName: "column"
+ focusPolicy: Qt.ClickFocus
+
+ Item {
+ id: item
+ implicitWidth: 100
+ implicitHeight: 20
+ objectName: "item"
+ focusPolicy: Qt.TabFocus
+
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ color: "yellow"
+ opacity: 0.5
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onClicked: function onClicked(mouseEvent) {
+ if (mouseEvent.button == Qt.RightButton)
+ rect.color = "pink"
+ }
+ }
+ }
+
+ Item {
+ id: customText
+ objectName: "customText"
+ implicitWidth: 100
+ implicitHeight: 50
+ TextInput {
+ anchors.fill: parent
+ objectName: "textInputChild"
+ text: parent.activeFocus ? "focus" : "no focus"
+ }
+ activeFocusOnTab: true
+ }
+
+ Item {
+ id: customItem
+ objectName: "customItem"
+ implicitWidth: 100
+ implicitHeight: 50
+ Rectangle {
+ anchors.fill: parent
+ color: parent.activeFocus ? "red" : "blue"
+ opacity: 0.3
+ }
+ focusPolicy: Qt.WheelFocus
+ }
+
+ Text {
+ id: hyperlink
+ objectName: "hyperlink"
+ color: "blue"
+ onLinkActivated: { text = "Clicked"; }
+ textFormat: Text.RichText
+ text: "<a href=\"http://qt-project.org\">Qt Project website</a>"
+ focusPolicy: Qt.StrongFocus
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton // Don't eat the mouse clicks
+ cursorShape: Qt.PointingHandCursor
+ // the acceptedButtons will take precedence
+ // and the click focus policy will be ignored
+ focusPolicy: Qt.ClickFocus
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/data/grabToImage.qml b/tests/auto/quick/qquickitem2/data/grabToImage.qml
index 7df6d097a6..1298a3c6a9 100644
--- a/tests/auto/quick/qquickitem2/data/grabToImage.qml
+++ b/tests/auto/quick/qquickitem2/data/grabToImage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickitem2/data/keysforward.qml b/tests/auto/quick/qquickitem2/data/keysforward.qml
index 8ea2d96022..09a0f926ed 100644
--- a/tests/auto/quick/qquickitem2/data/keysforward.qml
+++ b/tests/auto/quick/qquickitem2/data/keysforward.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickitem2/data/mapCoordinates.qml b/tests/auto/quick/qquickitem2/data/mapCoordinates.qml
index 1db5a4219d..29cfdd73cb 100644
--- a/tests/auto/quick/qquickitem2/data/mapCoordinates.qml
+++ b/tests/auto/quick/qquickitem2/data/mapCoordinates.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickitem2/data/mapCoordinatesRect.qml b/tests/auto/quick/qquickitem2/data/mapCoordinatesRect.qml
index 2cd48adc06..06f625624a 100644
--- a/tests/auto/quick/qquickitem2/data/mapCoordinatesRect.qml
+++ b/tests/auto/quick/qquickitem2/data/mapCoordinatesRect.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickitem2/data/mapCoordinatesWithWindows.qml b/tests/auto/quick/qquickitem2/data/mapCoordinatesWithWindows.qml
new file mode 100644
index 0000000000..8218c5230a
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/mapCoordinatesWithWindows.qml
@@ -0,0 +1,51 @@
+import QtQuick
+
+QtObject {
+ id: root
+
+ property Window windowA: Window {
+ visible: true
+ flags: Qt.FramelessWindowHint
+ color: "green"
+ x: 100; y: 100
+ width: 300; height: 300
+
+ Rectangle {
+ objectName: "childItem"
+ x: 50; y: 50
+ width: 100; height: 100
+ color: "red"
+ }
+
+ Window {
+ objectName: "childWindow"
+ parent: windowA
+ x: 100; y: 100
+ width: 100; height: 100
+ visible: true
+ color: "blue"
+
+ Rectangle {
+ objectName: "childItemInChildWindow"
+ x: 30; y: 30
+ width: 50; height: 50
+ color: "orange"
+ }
+ }
+ }
+
+ property Window windowB: Window {
+ visible: true
+ flags: Qt.FramelessWindowHint
+ color: "magenta"
+ x: 500; y: 200
+ width: 200; height: 200
+
+ Rectangle {
+ objectName: "childItem"
+ x: 50; y: 50
+ width: 100; height: 100
+ color: "cyan"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/data/standardkeys.qml b/tests/auto/quick/qquickitem2/data/standardkeys.qml
index 68ee185e87..e19b0705c1 100644
--- a/tests/auto/quick/qquickitem2/data/standardkeys.qml
+++ b/tests/auto/quick/qquickitem2/data/standardkeys.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index f7601cade1..267be73ec9 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QtQml/qqmlengine.h>
@@ -16,11 +17,17 @@
#include <QtQuick/private/qquickanchors_p.h>
#include <QtGui/qstylehints.h>
#include <private/qquickitem_p.h>
+#include <QtQuickTest/QtQuickTest>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/platforminputcontext_p.h>
#include <QtTest/private/qpropertytesthelper_p.h>
+#ifdef QT_WIDGETS_LIB
+#include <QtWidgets/qwidget.h>
+#include <QtWidgets/qboxlayout.h>
+#include <QtWidgets/qlineedit.h>
+#endif
using namespace QQuickVisualTestUtils;
@@ -87,6 +94,7 @@ private slots:
void mapCoordinates_data();
void mapCoordinatesRect();
void mapCoordinatesRect_data();
+ void mapCoordinatesWithWindows();
void propertyChanges();
void nonexistentPropertyConnection();
void transforms();
@@ -128,6 +136,13 @@ private slots:
void signalsOnDestruction();
void visibleChanged();
+ void lastFocusChangeReason();
+ void focusInScopeChanges();
+
+#ifdef QT_WIDGETS_LIB
+ void embeddedInWidgetsFocus();
+#endif
+
private:
QQmlEngine engine;
bool qt_tab_all_widgets() {
@@ -1873,90 +1888,90 @@ void tst_QQuickItem::layoutMirroring()
QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(rootItem);
QVERIFY(rootPrivate);
- QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "mirrored2")->effectiveLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored2")->effectiveLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true);
-
- QCOMPARE(anchorsMirrored(rootItem, "mirrored1"), true);
- QCOMPARE(anchorsMirrored(rootItem, "mirrored2"), true);
- QCOMPARE(anchorsMirrored(rootItem, "notMirrored1"), false);
- QCOMPARE(anchorsMirrored(rootItem, "notMirrored2"), false);
- QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror1"), true);
- QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror2"), true);
-
- QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritedLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritedLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true);
-
- QCOMPARE(childPrivate(rootItem, "mirrored1")->isMirrorImplicit, false);
- QCOMPARE(childPrivate(rootItem, "mirrored2")->isMirrorImplicit, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->isMirrorImplicit, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored2")->isMirrorImplicit, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->isMirrorImplicit, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->isMirrorImplicit, true);
-
- QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromParent, true);
- QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromParent, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromParent, true);
- QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromParent, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromParent, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromParent, true);
-
- QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromItem, true);
- QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromItem, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromItem, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromItem, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromItem, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromItem, false);
+ QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "mirrored2")->effectiveLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "notMirrored2")->effectiveLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror);
+
+ QVERIFY(anchorsMirrored(rootItem, "mirrored1"));
+ QVERIFY(anchorsMirrored(rootItem, "mirrored2"));
+ QVERIFY(!anchorsMirrored(rootItem, "notMirrored1"));
+ QVERIFY(!anchorsMirrored(rootItem, "notMirrored2"));
+ QVERIFY(anchorsMirrored(rootItem, "inheritedMirror1"));
+ QVERIFY(anchorsMirrored(rootItem, "inheritedMirror2"));
+
+ QVERIFY(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritedLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritedLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror);
+
+ QVERIFY(!childPrivate(rootItem, "mirrored1")->isMirrorImplicit);
+ QVERIFY(!childPrivate(rootItem, "mirrored2")->isMirrorImplicit);
+ QVERIFY(!childPrivate(rootItem, "notMirrored1")->isMirrorImplicit);
+ QVERIFY(childPrivate(rootItem, "notMirrored2")->isMirrorImplicit);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror1")->isMirrorImplicit);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror2")->isMirrorImplicit);
+
+ QVERIFY(childPrivate(rootItem, "mirrored1")->inheritMirrorFromParent);
+ QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritMirrorFromParent);
+ QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromParent);
+ QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritMirrorFromParent);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromParent);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromParent);
+
+ QVERIFY(childPrivate(rootItem, "mirrored1")->inheritMirrorFromItem);
+ QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritMirrorFromItem);
+ QVERIFY(!childPrivate(rootItem, "notMirrored1")->inheritMirrorFromItem);
+ QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritMirrorFromItem);
+ QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromItem);
+ QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromItem);
// load dynamic content using Loader that needs to inherit mirroring
rootItem->setProperty("state", "newContent");
- QCOMPARE(childPrivate(rootItem, "notMirrored3")->effectiveLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->effectiveLayoutMirror, true);
+ QVERIFY(!childPrivate(rootItem, "notMirrored3")->effectiveLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror3")->effectiveLayoutMirror);
- QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritedLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritedLayoutMirror, true);
+ QVERIFY(childPrivate(rootItem, "notMirrored3")->inheritedLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror3")->inheritedLayoutMirror);
- QCOMPARE(childPrivate(rootItem, "notMirrored3")->isMirrorImplicit, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->isMirrorImplicit, true);
+ QVERIFY(!childPrivate(rootItem, "notMirrored3")->isMirrorImplicit);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror3")->isMirrorImplicit);
- QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromParent, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritMirrorFromParent, true);
+ QVERIFY(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromParent);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror3")->inheritMirrorFromParent);
- QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false);
+ QVERIFY(!childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem);
+ QVERIFY(!childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem);
// disable inheritance
rootItem->setProperty("childrenInherit", false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
+ QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, false);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, false);
+ QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror);
// re-enable inheritance
rootItem->setProperty("childrenInherit", true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror);
+ QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true);
- QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror);
+ QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror);
//
// dynamic parenting
@@ -1969,24 +1984,24 @@ void tst_QQuickItem::layoutMirroring()
// inherit in constructor
QQuickItem *childItem1 = new QQuickItem(parentItem1);
- QCOMPARE(QQuickItemPrivate::get(childItem1)->effectiveLayoutMirror, true);
- QCOMPARE(QQuickItemPrivate::get(childItem1)->inheritMirrorFromParent, true);
+ QVERIFY(QQuickItemPrivate::get(childItem1)->effectiveLayoutMirror);
+ QVERIFY(QQuickItemPrivate::get(childItem1)->inheritMirrorFromParent);
// inherit through a parent change
QQuickItem *childItem2 = new QQuickItem();
- QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, false);
- QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, false);
+ QVERIFY(!QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror);
+ QVERIFY(!QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent);
childItem2->setParentItem(parentItem1);
- QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, true);
- QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, true);
+ QVERIFY(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror);
+ QVERIFY(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent);
// stop inherting through a parent change
QQuickItem *parentItem2 = new QQuickItem();
QQuickItemPrivate::get(parentItem2)->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true
QQuickItemPrivate::get(parentItem2)->resolveLayoutMirror();
childItem2->setParentItem(parentItem2);
- QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, false);
- QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, false);
+ QVERIFY(!QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror);
+ QVERIFY(!QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent);
delete parentItem1;
delete parentItem2;
@@ -2002,11 +2017,11 @@ void tst_QQuickItem::layoutMirroringWindow()
window->show();
QQuickItemPrivate *content = QQuickItemPrivate::get(window->contentItem());
- QCOMPARE(content->effectiveLayoutMirror, true);
- QCOMPARE(content->inheritedLayoutMirror, true);
- QCOMPARE(content->isMirrorImplicit, false);
- QCOMPARE(content->inheritMirrorFromParent, true);
- QCOMPARE(content->inheritMirrorFromItem, true);
+ QVERIFY(content->effectiveLayoutMirror);
+ QVERIFY(content->inheritedLayoutMirror);
+ QVERIFY(!content->isMirrorImplicit);
+ QVERIFY(content->inheritMirrorFromParent);
+ QVERIFY(content->inheritMirrorFromItem);
}
void tst_QQuickItem::layoutMirroringIllegalParent()
@@ -2797,6 +2812,76 @@ void tst_QQuickItem::mapCoordinatesRect_data()
QTest::newRow(QTest::toString(i)) << i << i << i << i;
}
+void tst_QQuickItem::mapCoordinatesWithWindows()
+{
+ QQmlComponent component(&engine, testFileUrl("mapCoordinatesWithWindows.qml"));
+ std::unique_ptr<QObject> root(component.create());
+ QVERIFY(root);
+
+ auto *windowA = root->property("windowA").value<QQuickWindow*>();
+ QVERIFY(windowA);
+
+ // The window container geometry, parenting, etc, is applied
+ // during polish, so to test these we need to wait for one.
+ QVERIFY(QQuickTest::qWaitForPolish(windowA));
+
+ auto *childItem = windowA->findChild<QQuickItem*>("childItem");
+ QVERIFY(childItem);
+
+ QPoint itemPos = childItem->position().toPoint();
+ QCOMPARE(childItem->mapToScene({0, 0}), itemPos);
+ QCOMPARE(childItem->mapToGlobal({0, 0}), windowA->position() + itemPos);
+
+ auto *childItemInChildWindow = windowA->findChild<QQuickItem*>("childItemInChildWindow");
+ QVERIFY(childItemInChildWindow);
+
+ QPoint windowItemPos = childItemInChildWindow->position().toPoint();
+ QCOMPARE(childItemInChildWindow->mapToScene({0, 0}), windowItemPos);
+ QCOMPARE(childItemInChildWindow->mapToGlobal({0, 0}), windowA->position()
+ + childItemInChildWindow->window()->position() + windowItemPos);
+
+ QCOMPARE(childItemInChildWindow->mapToItem(nullptr, {0, 0}), windowItemPos);
+
+ auto globalItemOffset = [](QQuickItem *a, QQuickItem *b) {
+ return a->mapToGlobal({0, 0}) - b->mapToGlobal({0, 0});
+ };
+
+ QCOMPARE(childItemInChildWindow->mapToItem(childItem, {0, 0}),
+ globalItemOffset(childItemInChildWindow, childItem));
+ QCOMPARE(childItemInChildWindow->mapFromItem(childItem, {0, 0}),
+ globalItemOffset(childItem, childItemInChildWindow));
+
+ QCOMPARE(childItem->mapToItem(childItemInChildWindow, {0, 0}),
+ globalItemOffset(childItem, childItemInChildWindow));
+ QCOMPARE(childItem->mapFromItem(childItemInChildWindow, {0, 0}),
+ globalItemOffset(childItemInChildWindow, childItem));
+
+ auto *windowB = root->property("windowB").value<QQuickWindow*>();
+ QVERIFY(windowA);
+ auto *childItemInOtherWindow = windowB->findChild<QQuickItem*>("childItem");
+ QVERIFY(childItemInOtherWindow);
+
+ QCOMPARE(childItemInOtherWindow->mapToItem(childItem, {0, 0}),
+ globalItemOffset(childItemInOtherWindow, childItem));
+ QCOMPARE(childItemInOtherWindow->mapFromItem(childItem, {0, 0}),
+ globalItemOffset(childItem, childItemInOtherWindow));
+
+ QCOMPARE(childItem->mapToItem(childItemInOtherWindow, {0, 0}),
+ globalItemOffset(childItem, childItemInOtherWindow));
+ QCOMPARE(childItem->mapFromItem(childItemInOtherWindow, {0, 0}),
+ globalItemOffset(childItemInOtherWindow, childItem));
+
+ QCOMPARE(childItemInOtherWindow->mapToItem(childItemInChildWindow, {0, 0}),
+ globalItemOffset(childItemInOtherWindow, childItemInChildWindow));
+ QCOMPARE(childItemInOtherWindow->mapFromItem(childItemInChildWindow, {0, 0}),
+ globalItemOffset(childItemInChildWindow, childItemInOtherWindow));
+
+ QCOMPARE(childItemInChildWindow->mapToItem(childItemInOtherWindow, {0, 0}),
+ globalItemOffset(childItemInChildWindow, childItemInOtherWindow));
+ QCOMPARE(childItemInChildWindow->mapFromItem(childItemInOtherWindow, {0, 0}),
+ globalItemOffset(childItemInOtherWindow, childItemInChildWindow));
+}
+
void tst_QQuickItem::transforms_data()
{
QTest::addColumn<QByteArray>("qml");
@@ -3268,93 +3353,93 @@ void tst_QQuickItem::changeListener()
listeners << new TestListener(true);
// itemVisibilityChanged x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Visibility);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setVisible(false);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::Visibility), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemRotationChanged x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Rotation);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setRotation(90);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::Rotation), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemOpacityChanged x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Opacity);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setOpacity(0.5);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::Opacity), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemChildAdded() x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
child1 = new QQuickItem(parent);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::Children), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemParentChanged() x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(child1)->addItemChangeListener(listener, QQuickItemPrivate::Parent);
QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.size(), listeners.size());
child1->setParentItem(nullptr);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::Parent), 1);
QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.size(), 0);
// itemImplicitWidthChanged() x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitWidth);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setImplicitWidth(parent->implicitWidth() + 1);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::ImplicitWidth), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemImplicitHeightChanged() x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitHeight);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setImplicitHeight(parent->implicitHeight() + 1);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::ImplicitHeight), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemGeometryChanged() x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Geometry);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setWidth(parent->width() + 1);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::Geometry), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemChildRemoved() x 5
child1->setParentItem(parent);
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
delete child1;
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::Children), 2);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemDestroyed() x 5
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Destroyed);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
delete parent;
- foreach (TestListener *listener, listeners)
+ for (TestListener *listener : std::as_const(listeners))
QCOMPARE(listener->count(QQuickItemPrivate::Destroyed), 1);
}
@@ -4139,6 +4224,286 @@ void tst_QQuickItem::visibleChanged()
QCOMPARE(childItemSpy.count(), 1);
}
+void tst_QQuickItem::lastFocusChangeReason()
+{
+ std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>();
+ window->setSource(testFileUrl("focusReason.qml"));
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window.get()));
+
+ QQuickItem *item = window->findChild<QQuickItem *>("item");
+ QQuickItem *customText = window->findChild<QQuickItem *>("customText");
+ QQuickItem *customItem = window->findChild<QQuickItem *>("customItem");
+ QQuickItem *hyperlink = window->findChild<QQuickItem *>("hyperlink");
+ QQuickItem *textInputChild = window->findChild<QQuickItem *>("textInputChild");
+
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ QQuickItemPrivate *customTextPrivate = QQuickItemPrivate::get(customText);
+ QQuickItemPrivate *customItemPrivate = QQuickItemPrivate::get(customItem);
+ QQuickItemPrivate *hyperlinkPrivate = QQuickItemPrivate::get(hyperlink);
+ QQuickItemPrivate *textInputChildPrivate = QQuickItemPrivate::get(textInputChild);
+
+ QVERIFY(item);
+ QVERIFY(customText);
+ QVERIFY(customItem);
+ QVERIFY(hyperlink);
+ QVERIFY(textInputChild);
+
+ QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls);
+ auto resetTabFocusBehavior = qScopeGuard([]{
+ QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusBehavior(-1));
+ });
+
+ // helper for clicking into an item
+ const auto itemCenter = [](const QQuickItem *item) -> QPoint {
+ return item->mapToScene(item->clipRect().center()).toPoint();
+ };
+
+ // setting focusPolicy to Strong/WheelFocus doesn't implicitly turn on event delivery
+ customText->setAcceptedMouseButtons(Qt::LeftButton);
+ customItem->setAcceptedMouseButtons(Qt::LeftButton);
+ customItem->setAcceptTouchEvents(true);
+ customText->setAcceptTouchEvents(true);
+ hyperlink->setAcceptTouchEvents(true);
+
+ // window activation -> ActiveWindowFocusReason
+ QVERIFY(item->hasFocus());
+ QVERIFY(item->hasActiveFocus());
+ if (itemPrivate->lastFocusChangeReason() != Qt::ActiveWindowFocusReason
+ && QStringList{"windows", "offscreen"}.contains(QGuiApplication::platformName())) {
+ QEXPECT_FAIL("", "On Windows and offscreen platforms, window activation does not set focus reason", Continue);
+ }
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::ActiveWindowFocusReason);
+
+ // test setter/getter
+ item->setFocus(false, Qt::MouseFocusReason);
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+ item->setFocus(true, Qt::TabFocusReason);
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+ item->setFocus(false, Qt::BacktabFocusReason);
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
+ item->forceActiveFocus(Qt::ShortcutFocusReason);
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::ShortcutFocusReason);
+ item->setFocus(false, Qt::NoFocusReason);
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::NoFocusReason);
+ QVERIFY(!item->hasFocus());
+
+ // programmatic focus changes
+ item->setFocus(true, Qt::OtherFocusReason);
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::OtherFocusReason);
+
+ QVERIFY(item->hasFocus());
+ QVERIFY(item->hasActiveFocus());
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::OtherFocusReason);
+
+ // tab focus -> TabFocusReason
+ QTest::keyClick(window.get(), Qt::Key_Tab);
+ QVERIFY(customText->hasFocus());
+ QVERIFY(customText->hasActiveFocus());
+ QCOMPARE(qApp->focusObject(), customText);
+ QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+
+ QTest::keyClick(window.get(), Qt::Key_Tab);
+ QVERIFY(customItem->hasFocus());
+ QVERIFY(customItem->hasActiveFocus());
+ QCOMPARE(qApp->focusObject(), customItem);
+ QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+ QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+
+ QTest::keyClick(window.get(), Qt::Key_Tab);
+ QVERIFY(hyperlink->hasFocus());
+ QVERIFY(hyperlink->hasActiveFocus());
+ QCOMPARE(qApp->focusObject(), hyperlink);
+ QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+ QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+
+ QTest::keyClick(window.get(), Qt::Key_Tab);
+ QVERIFY(item->hasFocus());
+ QVERIFY(item->hasActiveFocus());
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+ QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
+
+ // backtab -> BacktabFocusReason
+ QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier);
+ QVERIFY(hyperlink->hasFocus());
+ QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
+ QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
+
+ QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier);
+ QVERIFY(customItem->hasFocus());
+ QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
+ QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
+
+ QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier);
+ QVERIFY(customText->hasFocus());
+ QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
+ QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
+
+ // click focus -> MouseFocusReason
+ QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(customItem));
+ QVERIFY(customItem->hasFocus());
+ QVERIFY(customItem->hasActiveFocus());
+ QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+ QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+
+ QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(hyperlink));
+ QVERIFY(hyperlink->hasFocus());
+ QVERIFY(hyperlink->hasActiveFocus());
+ QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+ QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+
+ QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(customText));
+ QCOMPARE(textInputChild, textInputChild);
+ QVERIFY(textInputChild->hasFocus());
+ QVERIFY(textInputChild->hasActiveFocus());
+ QCOMPARE(textInputChildPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+ QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+
+ // touch focus -> MouseFocusReason
+ std::unique_ptr<QPointingDevice> touchDevice(QTest::createTouchDevice());
+
+ QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(customItem));
+ QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(customItem));
+ QTest::touchEvent(window.get(), touchDevice.get()).release(0, itemCenter(customItem));
+ QVERIFY(customItem->hasFocus());
+ QVERIFY(customItem->hasActiveFocus());
+ QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+ QCOMPARE(textInputChildPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+
+ QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(hyperlink));
+ QTest::touchEvent(window.get(), touchDevice.get()).release(0, itemCenter(hyperlink));
+ QVERIFY(hyperlink->hasFocus());
+ QVERIFY(hyperlink->hasActiveFocus());
+ QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+ QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+
+ // Wheel focus -> MouseFocusReason
+ QWheelEvent wheelEvent(QPointF(customItem->width() / 2, customItem->height() / 2), QPointF(),
+ QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
+ Qt::NoScrollPhase, false);
+ QGuiApplication::sendEvent(customItem, &wheelEvent);
+ QVERIFY(customItem->hasActiveFocus());
+ QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
+}
+
+void tst_QQuickItem::focusInScopeChanges()
+{
+ std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>();
+ window->setSource(testFileUrl("focusInScopeChanges.qml"));
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowFocused(window.get()));
+
+ QQuickItem *main = window->rootObject();
+ QVERIFY(main);
+ QQuickItem *focusScope = main->findChild<QQuickItem *>("focusScope");
+ QQuickItem *rect = main->findChild<QQuickItem *>("rect");
+ QQuickItem *textInput = main->findChild<QQuickItem *>("textInput");
+
+ QVERIFY(focusScope);
+ QVERIFY(rect);
+ QVERIFY(textInput);
+ QVERIFY(window->contentItem());
+
+ QSignalSpy fsActiveFocusSpy(focusScope, SIGNAL(activeFocusChanged(bool)));
+ QSignalSpy rectActiveFocusSpy(rect, SIGNAL(activeFocusChanged(bool)));
+ QSignalSpy textInputActiveFocusSpy(textInput, SIGNAL(activeFocusChanged(bool)));
+
+ // The window's content item will have activeFocus if window is focused
+ QTRY_VERIFY(window->contentItem()->hasActiveFocus());
+
+ QVERIFY(!focusScope->hasActiveFocus());
+ QVERIFY(!rect->hasActiveFocus());
+ QVERIFY(!textInput->hasActiveFocus());
+ QCOMPARE(fsActiveFocusSpy.size(), 0);
+ QCOMPARE(rectActiveFocusSpy.size(), 0);
+ QCOMPARE(textInputActiveFocusSpy.size(), 0);
+
+ // setting focus to rect shouldn't affect activeFocus as long as its
+ // parent focus scope doesn't have the activeFocus
+ rect->setFocus(true);
+ QCOMPARE(fsActiveFocusSpy.size(), 0);
+ QCOMPARE(rectActiveFocusSpy.size(), 0);
+ QCOMPARE(textInputActiveFocusSpy.size(), 0);
+
+ // focusScope is the only child with focus in the parent
+ // scope, so it will gain activeFocus
+ focusScope->setFocus(true);
+ QCOMPARE(fsActiveFocusSpy.size(), 1);
+ QVERIFY(fsActiveFocusSpy.first().at(0).toBool());
+ // rect loses activeFocus because textInput gains it (as a result of code in signal handler)
+ QCOMPARE(rectActiveFocusSpy.size(), 2);
+ QVERIFY(!rect->hasActiveFocus());
+ QCOMPARE(textInputActiveFocusSpy.size(), 1);
+ QVERIFY(textInput->hasActiveFocus());
+}
+
+#ifdef QT_WIDGETS_LIB
+void tst_QQuickItem::embeddedInWidgetsFocus()
+{
+ QWidget root;
+ QVBoxLayout *layout = new QVBoxLayout(&root);
+
+ QLineEdit *lineEdit1 = new QLineEdit(&root);
+ lineEdit1->setFocusPolicy(Qt::FocusPolicy::TabFocus);
+
+ QQuickView *quickView = new QQuickView;
+ quickView->setSource(testFileUrl("embedded.qml"));
+ QWidget *container = QWidget::createWindowContainer(quickView, &root);
+ container->setMinimumSize(quickView->size());
+ container->setFocusPolicy(Qt::TabFocus);
+
+ QLineEdit *lineEdit2 = new QLineEdit(&root);
+ lineEdit2->setFocusPolicy(Qt::FocusPolicy::TabFocus);
+
+ layout->addWidget(lineEdit1);
+ layout->addWidget(container);
+ layout->addWidget(lineEdit2);
+
+ QQuickItem *rect1 = findItem<QQuickItem>(quickView->rootObject(), "rect1");
+ QQuickItem *rect2 = findItem<QQuickItem>(quickView->rootObject(), "rect2");
+ QVERIFY(rect1);
+ QVERIFY(rect2);
+
+ root.show();
+ QTRY_VERIFY(root.isVisible());
+ QVERIFY(QTest::qWaitForWindowExposed(&root));
+ QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
+
+ lineEdit1->setFocus();
+ QTRY_VERIFY(lineEdit1->hasFocus());
+
+ // Tab forward
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
+ QTRY_VERIFY(container->hasFocus());
+ QVERIFY(QTest::qWaitForWindowFocused(quickView));
+ QVERIFY(rect1->hasActiveFocus());
+
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
+ QTRY_VERIFY(rect2->hasActiveFocus());
+
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
+ QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
+ QVERIFY(lineEdit2->hasFocus());
+ QVERIFY(!rect2->hasActiveFocus());
+
+ // Tab backwards
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
+ QTRY_VERIFY(container->hasFocus());
+ QVERIFY(QTest::qWaitForWindowFocused(quickView));
+ QVERIFY(rect2->hasActiveFocus());
+
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
+ QVERIFY(rect1->hasActiveFocus());
+
+ QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
+ QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
+ QVERIFY(lineEdit1->hasFocus());
+}
+#endif
+
QTEST_MAIN(tst_QQuickItem)
#include "tst_qquickitem.moc"
diff --git a/tests/auto/quick/qquickitemlayer/CMakeLists.txt b/tests/auto/quick/qquickitemlayer/CMakeLists.txt
index ae98bd5bb5..b83ada3081 100644
--- a/tests/auto/quick/qquickitemlayer/CMakeLists.txt
+++ b/tests/auto/quick/qquickitemlayer/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickitemlayer Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickitemlayer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
index 6cdcd2829c..fb18d2be54 100644
--- a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
+++ b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
diff --git a/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt b/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt
deleted file mode 100644
index 8f918dd753..0000000000
--- a/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-# Collect test data
-file(GLOB_RECURSE test_data_glob
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
- data/*)
-list(APPEND test_data ${test_data_glob})
-
-qt_internal_add_test(tst_qquickitemrhiintegration
- SOURCES
- tst_qquickitemrhiintegration.cpp
- rhiitem.cpp rhiitem.h rhiitem_p.h
- testrhiitem.cpp testrhiitem.h
- LIBRARIES
- Qt::CorePrivate
- Qt::Gui
- Qt::GuiPrivate
- Qt::QmlPrivate
- Qt::QuickPrivate
- Qt::QuickTestUtilsPrivate
- TESTDATA ${test_data}
-)
-
-qt_internal_extend_target(tst_qquickitemrhiintegration CONDITION ANDROID OR IOS
- DEFINES
- QT_QMLTEST_DATADIR=":/data"
-)
-
-qt_internal_extend_target(tst_qquickitemrhiintegration CONDITION NOT ANDROID AND NOT IOS
- DEFINES
- QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
-)
-
-qt_internal_add_shaders(tst_qquickitemrhiintegration "shaders"
- PREFIX
- "/"
- FILES
- "texture.vert"
- "texture.frag"
-)
-
-qt_add_qml_module(tst_qquickitemrhiintegration
- URI TestQquickitemrhiintegration
- AUTO_RESOURCE_PREFIX
-)
diff --git a/tests/auto/quick/qquickitemrhiintegration/data/test.qml b/tests/auto/quick/qquickitemrhiintegration/data/test.qml
deleted file mode 100644
index 8ffbe0d6ed..0000000000
--- a/tests/auto/quick/qquickitemrhiintegration/data/test.qml
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import QtQuick
-import TestQquickitemrhiintegration
-
-Item {
- width: 640
- height: 480
-
- Text {
- id: apiInfo
- color: "black"
- font.pixelSize: 16
- property int api: GraphicsInfo.api
- text: {
- if (GraphicsInfo.api === GraphicsInfo.OpenGLRhi)
- "OpenGL on QRhi";
- else if (GraphicsInfo.api === GraphicsInfo.Direct3D11Rhi)
- "D3D11 on QRhi";
- else if (GraphicsInfo.api === GraphicsInfo.VulkanRhi)
- "Vulkan on QRhi";
- else if (GraphicsInfo.api === GraphicsInfo.MetalRhi)
- "Metal on QRhi";
- else if (GraphicsInfo.api === GraphicsInfo.Null)
- "Null on QRhi";
- else
- "Unknown API";
- }
- }
-
- TestRhiItem {
- anchors.centerIn: parent
- width: 400
- height: 400
- color: "red"
- }
-}
diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp b/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp
deleted file mode 100644
index bcc1f2f6a3..0000000000
--- a/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp
+++ /dev/null
@@ -1,352 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "rhiitem_p.h"
-
-RhiItemNode::RhiItemNode(RhiItem *item)
- : m_item(item)
-{
- m_window = m_item->window();
- Q_ASSERT(m_window);
- connect(m_window, &QQuickWindow::beforeRendering, this, &RhiItemNode::render);
- connect(m_window, &QQuickWindow::screenChanged, this, [this]() {
- if (m_window->effectiveDevicePixelRatio() != m_dpr)
- m_item->update();
- });
-}
-
-RhiItemNode::~RhiItemNode()
-{
- delete m_renderer;
- delete m_sgWrapperTexture;
- releaseNativeTexture();
-}
-
-QSGTexture *RhiItemNode::texture() const
-{
- return m_sgWrapperTexture;
-}
-
-void RhiItemNode::createNativeTexture()
-{
- Q_ASSERT(!m_texture);
- Q_ASSERT(!m_pixelSize.isEmpty());
-
- m_texture = m_rhi->newTexture(QRhiTexture::RGBA8, m_pixelSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
- if (!m_texture->create()) {
- qWarning("Failed to create RhiItem texture of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
- delete m_texture;
- m_texture = nullptr;
- }
-}
-
-void RhiItemNode::releaseNativeTexture()
-{
- if (m_texture) {
- m_texture->deleteLater();
- m_texture = nullptr;
- }
-}
-
-void RhiItemNode::sync()
-{
- if (!m_rhi) {
- QSGRendererInterface *rif = m_window->rendererInterface();
- m_rhi = static_cast<QRhi *>(rif->getResource(m_window, QSGRendererInterface::RhiResource));
- if (!m_rhi) {
- qWarning("No QRhi found for window %p, RhiItem will not be functional", m_window);
- return;
- }
- }
-
- QSize newSize(m_item->explicitTextureWidth(), m_item->explicitTextureHeight());
- if (newSize.isEmpty()) {
- m_dpr = m_window->effectiveDevicePixelRatio();
- const int minTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMin);
- newSize = QSize(qMax<int>(minTexSize, m_item->width()),
- qMax<int>(minTexSize, m_item->height())) * m_dpr;
- }
-
- bool needsNew = !m_sgWrapperTexture;
- if (newSize != m_pixelSize) {
- needsNew = true;
- m_pixelSize = newSize;
- }
-
- if (needsNew) {
- if (m_texture && m_sgWrapperTexture) {
- m_texture->setPixelSize(m_pixelSize);
- if (m_texture->create())
- m_sgWrapperTexture->setTextureSize(m_pixelSize);
- else
- qWarning("Failed to recreate RhiItem texture of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
- } else {
- delete m_sgWrapperTexture;
- m_sgWrapperTexture = nullptr;
- releaseNativeTexture();
- createNativeTexture();
- if (m_texture) {
- m_sgWrapperTexture = new QSGPlainTexture;
- m_sgWrapperTexture->setOwnsTexture(false);
- m_sgWrapperTexture->setTexture(m_texture);
- m_sgWrapperTexture->setTextureSize(m_pixelSize);
- m_sgWrapperTexture->setHasAlphaChannel(m_item->alphaBlending());
- setTexture(m_sgWrapperTexture);
- }
- }
- RhiItemPrivate::get(m_item)->effectiveTextureSize = m_pixelSize;
- emit m_item->effectiveTextureSizeChanged();
- if (m_texture)
- m_renderer->initialize(m_rhi, m_texture);
- }
-
- if (m_sgWrapperTexture && m_sgWrapperTexture->hasAlphaChannel() != m_item->alphaBlending()) {
- m_sgWrapperTexture->setHasAlphaChannel(m_item->alphaBlending());
- // hasAlphaChannel is mapped to QSGMaterial::Blending in setTexture() so that has to be called again
- setTexture(m_sgWrapperTexture);
- }
-
- m_renderer->synchronize(m_item);
-}
-
-void RhiItemNode::render()
-{
- // called before Qt Quick starts recording its main render pass
-
- if (!m_rhi || !m_texture || !m_renderer)
- return;
-
- if (!m_renderPending)
- return;
-
- QSGRendererInterface *rif = m_window->rendererInterface();
- QRhiCommandBuffer *cb = nullptr;
- QRhiSwapChain *swapchain = static_cast<QRhiSwapChain *>(
- rif->getResource(m_window, QSGRendererInterface::RhiSwapchainResource));
- cb = swapchain ? swapchain->currentFrameCommandBuffer()
- : static_cast<QRhiCommandBuffer *>(rif->getResource(m_window, QSGRendererInterface::RhiRedirectCommandBuffer));
- if (!cb) {
- qWarning("Neither swapchain nor redirected command buffer are available.");
- return;
- }
-
- m_renderPending = false;
- m_renderer->render(cb);
-
- markDirty(QSGNode::DirtyMaterial);
- emit textureChanged();
-}
-
-void RhiItemNode::scheduleUpdate()
-{
- m_renderPending = true;
- m_window->update(); // ensure getting to beforeRendering() at some point
-}
-
-void RhiItemNode::setMirrorVertically(bool b)
-{
- if (m_rhi->isYUpInFramebuffer())
- b = !b;
-
- setTextureCoordinatesTransform(b ? QSGSimpleTextureNode::MirrorVertically : QSGSimpleTextureNode::NoTransform);
-}
-
-RhiItem::RhiItem(QQuickItem *parent)
- : QQuickItem(*new RhiItemPrivate, parent)
-{
- setFlag(ItemHasContents);
-}
-
-QSGNode *RhiItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
-{
- Q_D(RhiItem);
- RhiItemNode *n = static_cast<RhiItemNode *>(node);
-
- // Changing to an empty size should not involve destroying and then later
- // recreating the node, because we do not know how expensive the user's
- // renderer setup is. Rather, keep the node if it already exist, and clamp
- // all accesses to width and height. Hence the unusual !n condition here.
- if (!n && (width() <= 0 || height() <= 0))
- return nullptr;
-
- if (!n) {
- if (!d->node)
- d->node = new RhiItemNode(this);
- n = d->node;
- }
-
- if (!n->hasRenderer()) {
- RhiItemRenderer *r = createRenderer();
- if (r) {
- r->data = n;
- n->setRenderer(r);
- } else {
- qWarning("No RhiItemRenderer was created; the item will not render");
- delete n;
- d->node = nullptr;
- return nullptr;
- }
- }
-
- n->sync();
-
- if (!n->isValid()) {
- delete n;
- d->node = nullptr;
- return nullptr;
- }
-
- n->setMirrorVertically(d->mirrorVertically);
- n->setFiltering(QSGTexture::Linear);
- n->setRect(0, 0, qMax<int>(0, width()), qMax<int>(0, height()));
-
- n->scheduleUpdate();
-
- return n;
-}
-
-void RhiItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
-{
- QQuickItem::geometryChange(newGeometry, oldGeometry);
- if (newGeometry.size() != oldGeometry.size())
- update();
-}
-
-void RhiItem::releaseResources()
-{
- // called on the gui thread if the item is removed from scene
-
- Q_D(RhiItem);
- d->node = nullptr;
-}
-
-void RhiItem::invalidateSceneGraph()
-{
- // called on the render thread when the scenegraph is invalidated
-
- Q_D(RhiItem);
- d->node = nullptr;
-}
-
-bool RhiItem::isTextureProvider() const
-{
- return true;
-}
-
-QSGTextureProvider *RhiItem::textureProvider() const
-{
- if (QQuickItem::isTextureProvider()) // e.g. if Item::layer::enabled == true
- return QQuickItem::textureProvider();
-
- QQuickWindow *w = window();
- if (!w || !w->isSceneGraphInitialized() || QThread::currentThread() != QQuickWindowPrivate::get(w)->context->thread()) {
- qWarning("RhiItem::textureProvider: can only be queried on the rendering thread of an exposed window");
- return nullptr;
- }
-
- Q_D(const RhiItem);
- if (!d->node) // create a node to have a provider, the texture will be null but that's ok
- d->node = new RhiItemNode(const_cast<RhiItem *>(this));
-
- return d->node;
-}
-
-int RhiItem::explicitTextureWidth() const
-{
- Q_D(const RhiItem);
- return d->explicitTextureWidth;
-}
-
-void RhiItem::setExplicitTextureWidth(int w)
-{
- Q_D(RhiItem);
- if (d->explicitTextureWidth == w)
- return;
-
- d->explicitTextureWidth = w;
- emit explicitTextureWidthChanged();
- update();
-}
-
-int RhiItem::explicitTextureHeight() const
-{
- Q_D(const RhiItem);
- return d->explicitTextureHeight;
-}
-
-void RhiItem::setExplicitTextureHeight(int h)
-{
- Q_D(RhiItem);
- if (d->explicitTextureHeight == h)
- return;
-
- d->explicitTextureHeight = h;
- emit explicitTextureHeightChanged();
- update();
-}
-
-QSize RhiItem::effectiveTextureSize() const
-{
- Q_D(const RhiItem);
- return d->effectiveTextureSize;
-}
-
-bool RhiItem::alphaBlending() const
-{
- Q_D(const RhiItem);
- return d->blend;
-}
-
-void RhiItem::setAlphaBlending(bool enable)
-{
- Q_D(RhiItem);
- if (d->blend == enable)
- return;
-
- d->blend = enable;
- emit alphaBlendingChanged();
- update();
-}
-
-bool RhiItem::mirrorVertically() const
-{
- Q_D(const RhiItem);
- return d->mirrorVertically;
-}
-
-void RhiItem::setMirrorVertically(bool enable)
-{
- Q_D(RhiItem);
- if (d->mirrorVertically == enable)
- return;
-
- d->mirrorVertically = enable;
- emit mirrorVerticallyChanged();
- update();
-}
-
-void RhiItemRenderer::update()
-{
- if (data)
- static_cast<RhiItemNode *>(data)->scheduleUpdate();
-}
-
-RhiItemRenderer::~RhiItemRenderer()
-{
-}
-
-void RhiItemRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture)
-{
- Q_UNUSED(rhi);
- Q_UNUSED(outputTexture);
-}
-
-void RhiItemRenderer::synchronize(RhiItem *item)
-{
- Q_UNUSED(item);
-}
-
-void RhiItemRenderer::render(QRhiCommandBuffer *cb)
-{
- Q_UNUSED(cb);
-}
diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem.h b/tests/auto/quick/qquickitemrhiintegration/rhiitem.h
deleted file mode 100644
index 6439bfba4a..0000000000
--- a/tests/auto/quick/qquickitemrhiintegration/rhiitem.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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 RHIITEM_H
-#define RHIITEM_H
-
-#include <QQuickItem>
-#include <QtGui/private/qrhi_p.h>
-
-class RhiItem;
-class RhiItemPrivate;
-
-class RhiItemRenderer
-{
-public:
- virtual ~RhiItemRenderer();
- virtual void initialize(QRhi *rhi, QRhiTexture *outputTexture);
- virtual void synchronize(RhiItem *item);
- virtual void render(QRhiCommandBuffer *cb);
-
- void update();
-
-private:
- void *data;
- friend class RhiItem;
-};
-
-class RhiItem : public QQuickItem
-{
- Q_OBJECT
- Q_DECLARE_PRIVATE(RhiItem)
-
- Q_PROPERTY(int explicitTextureWidth READ explicitTextureWidth WRITE setExplicitTextureWidth NOTIFY explicitTextureWidthChanged)
- Q_PROPERTY(int explicitTextureHeight READ explicitTextureHeight WRITE setExplicitTextureHeight NOTIFY explicitTextureHeightChanged)
- Q_PROPERTY(QSize effectiveTextureSize READ effectiveTextureSize NOTIFY effectiveTextureSizeChanged)
- Q_PROPERTY(bool alphaBlending READ alphaBlending WRITE setAlphaBlending NOTIFY alphaBlendingChanged)
- Q_PROPERTY(bool mirrorVertically READ mirrorVertically WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged)
-
-public:
- RhiItem(QQuickItem *parent = nullptr);
-
- virtual RhiItemRenderer *createRenderer() = 0;
-
- int explicitTextureWidth() const;
- void setExplicitTextureWidth(int w);
- int explicitTextureHeight() const;
- void setExplicitTextureHeight(int h);
-
- QSize effectiveTextureSize() const;
-
- bool alphaBlending() const;
- void setAlphaBlending(bool enable);
-
- bool mirrorVertically() const;
- void setMirrorVertically(bool enable);
-
-protected:
- QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
- void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
- void releaseResources() override;
- bool isTextureProvider() const override;
- QSGTextureProvider *textureProvider() const override;
-
-Q_SIGNALS:
- void explicitTextureWidthChanged();
- void explicitTextureHeightChanged();
- void effectiveTextureSizeChanged();
- void alphaBlendingChanged();
- void mirrorVerticallyChanged();
-
-private Q_SLOTS:
- void invalidateSceneGraph();
-};
-
-#endif
diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h b/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h
deleted file mode 100644
index 5fe2de8de0..0000000000
--- a/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 RHIITEM_P_H
-#define RHIITEM_P_H
-
-#include "rhiitem.h"
-#include <QSGSimpleTextureNode>
-#include <QtQuick/private/qquickitem_p.h>
-#include <QtQuick/private/qsgplaintexture_p.h>
-
-class RhiItemNode : public QSGTextureProvider, public QSGSimpleTextureNode
-{
- Q_OBJECT
-
-public:
- RhiItemNode(RhiItem *item);
- ~RhiItemNode();
-
- QSGTexture *texture() const override;
-
- void sync();
- bool isValid() const { return m_rhi && m_texture && m_sgWrapperTexture; }
- void scheduleUpdate();
- bool hasRenderer() const { return m_renderer; }
- void setRenderer(RhiItemRenderer *r) { m_renderer = r; }
- void setMirrorVertically(bool b);
-
-private slots:
- void render();
-
-private:
- void createNativeTexture();
- void releaseNativeTexture();
-
- RhiItem *m_item;
- QQuickWindow *m_window;
- QSize m_pixelSize;
- qreal m_dpr = 0.0f;
- QRhi *m_rhi = nullptr;
- QRhiTexture *m_texture = nullptr;
- QSGPlainTexture *m_sgWrapperTexture = nullptr;
- bool m_renderPending = true;
- RhiItemRenderer *m_renderer = nullptr;
-};
-
-class RhiItemPrivate : public QQuickItemPrivate
-{
- Q_DECLARE_PUBLIC(RhiItem)
-public:
- static RhiItemPrivate *get(RhiItem *item) { return item->d_func(); }
- mutable RhiItemNode *node = nullptr;
- int explicitTextureWidth = 0;
- int explicitTextureHeight = 0;
- bool blend = true;
- bool mirrorVertically = false;
- QSize effectiveTextureSize;
-};
-
-#endif
diff --git a/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp
deleted file mode 100644
index 926bb53bcf..0000000000
--- a/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "testrhiitem.h"
-#include <QFile>
-
-static const QSize TEX_SIZE(512, 512);
-
-void TestRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture)
-{
- m_rhi = rhi;
- m_output = outputTexture;
-
- if (!m_ds) {
- m_ds.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_output->pixelSize()));
- m_ds->create();
- } else if (m_ds->pixelSize() != m_output->pixelSize()) {
- m_ds->setPixelSize(m_output->pixelSize());
- m_ds->create();
- }
-
- if (!m_rt) {
- m_rt.reset(m_rhi->newTextureRenderTarget({ { m_output }, m_ds.data() }));
- m_rp.reset(m_rt->newCompatibleRenderPassDescriptor());
- m_rt->setRenderPassDescriptor(m_rp.data());
- m_rt->create();
- }
-
- if (!scene.vbuf)
- initScene();
-
- const QSize outputSize = m_output->pixelSize();
- scene.mvp = m_rhi->clipSpaceCorrMatrix();
- scene.mvp.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
- scene.mvp.translate(0, 0, -4);
- if (!scene.resourceUpdates)
- scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
- scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.data(), 0, 64, scene.mvp.constData());
-}
-
-static QShader getShader(const QString &name)
-{
- QFile f(name);
- if (f.open(QIODevice::ReadOnly))
- return QShader::fromSerialized(f.readAll());
-
- return QShader();
-}
-
-void TestRenderer::updateTexture()
-{
- QImage img(TEX_SIZE, QImage::Format_RGBA8888);
- img.fill(itemData.color);
-
- if (!scene.resourceUpdates)
- scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
-
- scene.resourceUpdates->uploadTexture(scene.tex.data(), img);
-}
-
-void TestRenderer::initScene()
-{
- static const float tri[] = {
- 0.0f, 0.5f, 0.0f, 1.0f,
- -0.5f, -0.5f, 0.0f, 0.0f,
- 0.5f, -0.5f, 1.0f, 0.0f
- };
-
- scene.vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(tri)));
- scene.vbuf->create();
-
- scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
- scene.resourceUpdates->uploadStaticBuffer(scene.vbuf.data(), tri);
-
- scene.ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68));
- scene.ubuf->create();
-
- const qint32 flip = m_rhi->isYUpInFramebuffer() ? 1 : 0;
- scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.data(), 64, 4, &flip);
-
- scene.tex.reset(m_rhi->newTexture(QRhiTexture::RGBA8, TEX_SIZE));
- scene.tex->create();
-
- scene.sampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
- QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
- scene.sampler->create();
-
- scene.srb.reset(m_rhi->newShaderResourceBindings());
- scene.srb->setBindings({
- QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.ubuf.data()),
- QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.tex.data(), scene.sampler.data())
- });
- scene.srb->create();
-
- scene.ps.reset(m_rhi->newGraphicsPipeline());
- scene.ps->setDepthTest(true);
- scene.ps->setDepthWrite(true);
- scene.ps->setDepthOp(QRhiGraphicsPipeline::Less);
- scene.ps->setCullMode(QRhiGraphicsPipeline::Back);
- scene.ps->setFrontFace(QRhiGraphicsPipeline::CCW);
- QShader vs = getShader(QLatin1String(":/texture.vert.qsb"));
- Q_ASSERT(vs.isValid());
- QShader fs = getShader(QLatin1String(":/texture.frag.qsb"));
- Q_ASSERT(fs.isValid());
- scene.ps->setShaderStages({
- { QRhiShaderStage::Vertex, vs },
- { QRhiShaderStage::Fragment, fs }
- });
- QRhiVertexInputLayout inputLayout;
- inputLayout.setBindings({
- { 4 * sizeof(float) },
- });
- inputLayout.setAttributes({
- { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
- { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
- });
- scene.ps->setVertexInputLayout(inputLayout);
- scene.ps->setShaderResourceBindings(scene.srb.data());
- scene.ps->setRenderPassDescriptor(m_rp.data());
- scene.ps->create();
-}
-
-void TestRenderer::synchronize(RhiItem *rhiItem)
-{
- TestRhiItem *item = static_cast<TestRhiItem *>(rhiItem);
- if (item->color() != itemData.color) {
- itemData.color = item->color();
- updateTexture();
- }
-}
-
-void TestRenderer::render(QRhiCommandBuffer *cb)
-{
- QRhiResourceUpdateBatch *rub = scene.resourceUpdates;
- if (rub)
- scene.resourceUpdates = nullptr;
-
- const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f);
- cb->beginPass(m_rt.data(), clearColor, { 1.0f, 0 }, rub);
-
- cb->setGraphicsPipeline(scene.ps.data());
- const QSize outputSize = m_output->pixelSize();
- cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
- cb->setShaderResources();
- const QRhiCommandBuffer::VertexInput vbufBindings[] = {
- { scene.vbuf.data(), 0 },
- };
- cb->setVertexInput(0, 1, vbufBindings);
- cb->draw(3);
-
- cb->endPass();
-}
-
-void TestRhiItem::setColor(QColor c)
-{
- if (m_color == c)
- return;
-
- m_color = c;
- emit colorChanged();
- update();
-}
diff --git a/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h
deleted file mode 100644
index 94a50a6d9b..0000000000
--- a/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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 TESTRHIITEM_H
-#define TESTRHIITEM_H
-
-#include "rhiitem.h"
-
-class TestRenderer : public RhiItemRenderer
-{
-public:
- void initialize(QRhi *rhi, QRhiTexture *outputTexture) override;
- void synchronize(RhiItem *item) override;
- void render(QRhiCommandBuffer *cb) override;
-
-private:
- QRhi *m_rhi = nullptr;
- QRhiTexture *m_output = nullptr;
- QScopedPointer<QRhiRenderBuffer> m_ds;
- QScopedPointer<QRhiTextureRenderTarget> m_rt;
- QScopedPointer<QRhiRenderPassDescriptor> m_rp;
-
- struct {
- QRhiResourceUpdateBatch *resourceUpdates = nullptr;
- QScopedPointer<QRhiBuffer> vbuf;
- QScopedPointer<QRhiBuffer> ubuf;
- QScopedPointer<QRhiShaderResourceBindings> srb;
- QScopedPointer<QRhiGraphicsPipeline> ps;
- QScopedPointer<QRhiSampler> sampler;
- QScopedPointer<QRhiTexture> tex;
- QMatrix4x4 mvp;
- } scene;
-
- struct {
- QColor color;
- } itemData;
-
- void initScene();
- void updateTexture();
-};
-
-class TestRhiItem : public RhiItem
-{
- Q_OBJECT
- QML_NAMED_ELEMENT(TestRhiItem)
- Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
-
-public:
- RhiItemRenderer *createRenderer() override { return new TestRenderer; }
-
- QColor color() const { return m_color; }
- void setColor(QColor c);
-
-signals:
- void colorChanged();
-
-private:
- QColor m_color;
-};
-
-#endif
diff --git a/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp b/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp
deleted file mode 100644
index 9915c81fc1..0000000000
--- a/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include <qtest.h>
-#include <QtQuickTestUtils/private/qmlutils_p.h>
-
-#include <QtGui/private/qrhi_p.h>
-
-#include <QQuickView>
-#include <QSGRendererInterface>
-#include <private/qsgrhisupport_p.h>
-
-class tst_QQuickItemRhiIntegration : public QQmlDataTest
-{
- Q_OBJECT
-
-public:
- tst_QQuickItemRhiIntegration();
-
-private slots:
- void initTestCase() override;
- void cleanupTestCase();
- void rhiItem();
-};
-
-tst_QQuickItemRhiIntegration::tst_QQuickItemRhiIntegration()
- : QQmlDataTest(QT_QMLTEST_DATADIR)
-{
-}
-
-void tst_QQuickItemRhiIntegration::initTestCase()
-{
- QQmlDataTest::initTestCase();
- qDebug() << "RHI backend:" << QSGRhiSupport::instance()->rhiBackendName();
-}
-
-void tst_QQuickItemRhiIntegration::cleanupTestCase()
-{
-}
-
-void tst_QQuickItemRhiIntegration::rhiItem()
-{
- if (QGuiApplication::platformName() == QLatin1String("offscreen")
- || QGuiApplication::platformName() == QLatin1String("minimal"))
- {
- QSKIP("Skipping on offscreen/minimal");
- }
-
- // Load up a scene that instantiates a TestRhiItem, which in turn
- // renders a triangle with QRhi into a QRhiTexture and then draws
- // a quad textured with it.
-
- QQuickView view;
- view.setSource(testFileUrl(QLatin1String("test.qml")));
- view.setResizeMode(QQuickView::SizeViewToRootObject);
- view.show();
- QVERIFY(QTest::qWaitForWindowExposed(&view));
-
- if (!QSGRendererInterface::isApiRhiBased(view.rendererInterface()->graphicsApi()))
- QSKIP("Scenegraph does not use QRhi, test is pointless");
-
- QImage result = view.grabWindow();
- QVERIFY(!result.isNull());
-
- const int tolerance = 5;
-
- // The result is a red triangle in the center of the view, confirm at least one pixel.
- QRgb a = result.pixel(result.width() / 2, result.height() / 2);
- QRgb b = QColor(Qt::red).rgb();
- QVERIFY(qAbs(qRed(a) - qRed(b)) <= tolerance
- && qAbs(qGreen(a) - qGreen(b)) <= tolerance
- && qAbs(qBlue(a) - qBlue(b)) <= tolerance);
-}
-
-#include "tst_qquickitemrhiintegration.moc"
-
-QTEST_MAIN(tst_QQuickItemRhiIntegration)
diff --git a/tests/auto/quick/qquicklayouts/CMakeLists.txt b/tests/auto/quick/qquicklayouts/CMakeLists.txt
index 5f0b48f5a5..4716ee4a37 100644
--- a/tests/auto/quick/qquicklayouts/CMakeLists.txt
+++ b/tests/auto/quick/qquicklayouts/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicklayouts Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicklayouts LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicklayouts/data/LayoutHelperLibrary.js b/tests/auto/quick/qquicklayouts/data/LayoutHelperLibrary.js
new file mode 100644
index 0000000000..7dbbc1ad64
--- /dev/null
+++ b/tests/auto/quick/qquicklayouts/data/LayoutHelperLibrary.js
@@ -0,0 +1,42 @@
+.pragma library
+
+
+function buildLayout(layoutData, parentItem) {
+ let layout = null
+ switch (layoutData.type) {
+ case "GridLayout":
+ case "RowLayout":
+ case "ColumnLayout":
+ layout = Qt.createQmlObject("import QtQuick.Layouts\n" +
+ layoutData.type + " {}", parentItem)
+ break
+ default:
+ console.log("data.layout.type not recognized(" + layoutdata.type + ")")
+ }
+ if (layout) {
+ for (let name in layoutData) {
+ let val = layoutData[name]
+ switch (name) {
+ case "items":
+ let arrLayoutData = layoutData.items
+ for (let i = 0; i < arrLayoutData.length; i++) {
+ let layoutItemDesc = arrLayoutData[i]
+ let strProps = ""
+ for (let keyName in layoutItemDesc) {
+ strProps += "Layout." + keyName + ": " + layoutItemDesc[keyName] + ";"
+ }
+ // For some reason we cannot assign the "Layout." attached properties from
+ // here, so for now we have to serialize them as strings.
+ let rect = Qt.createQmlObject("import QtQuick\nimport QtQuick.Layouts\n\nRectangle { implicitWidth: 20; implicitHeight: 20; " + strProps + "}", layout)
+ }
+ break;
+ case "type":
+ break;
+ default:
+ layout[name] = val
+ break;
+ }
+ }
+ }
+ return layout
+}
diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml
index 962bc78580..9a5ae0cc7a 100644
--- a/tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml
+++ b/tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.7
import QtQuick.Layouts 1.3
diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml
index a2aa7b93b2..30880e2d91 100644
--- a/tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml
+++ b/tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
import QtQuick.Layouts 1.3
diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml
index c324131ef6..22cf2353da 100644
--- a/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml
+++ b/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.6
import QtQuick.Window 2.2
diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml
index 7a5389e81e..46fbae02a0 100644
--- a/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml
+++ b/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.6
diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml
index d7ad38ffaf..ac12868627 100644
--- a/tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml
+++ b/tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.7
import QtQuick.Layouts 1.3
diff --git a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml
index 261b8a9a8e..f311cc34d6 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml
@@ -1,9 +1,10 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.6
import QtTest 1.0
import QtQuick.Layouts 1.1
+import "LayoutHelperLibrary.js" as LayoutHelpers
Item {
id: container
@@ -207,7 +208,7 @@ Item {
}
function test_flowLeftToRightDefaultPositions() {
- ignoreWarning("QGridLayoutEngine::addItem: Cell (1, 0) already taken");
+ ignoreWarning(/QGridLayoutEngine::addItem: Can't add .* at cell \(1, 0\) because it's already taken by .*/);
var layout = createTemporaryObject(layout_flowLeftToRightDefaultPositions_Component, container);
compare(layout.implicitWidth, 40);
compare(layout.children[0].x, 0);
@@ -1255,5 +1256,126 @@ Item {
}
verify(layout.implicitHeight > initialImplicitHeight)
}
+
+ function test_uniformCellSizes_data()
+ {
+ return [
+ {
+ tag: "hor 9/3",
+ layout: {
+ type: "GridLayout",
+ columns: 3,
+ items: [
+ {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true}
+ ]
+ },
+ layoutWidth: 9,
+ expectedWidths: [3, 3, 3],
+ expectedPositions: [0, 3, 6]
+ },
+ {
+ tag: "hor 30/3",
+ layout: {
+ type: "GridLayout",
+ columns: 3,
+ items: [
+ {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true}
+ ]
+ },
+ layoutWidth: 30,
+ expectedWidths: [10, 10, 10]
+ },
+ {
+ tag: "hor 60/3",
+ layout: {
+ type: "GridLayout",
+ columns: 3,
+ items: [
+ {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true}
+ ]
+ },
+ layoutWidth: 60,
+ expectedWidths: [20, 10, 20], // We are beyond the maximumWidth. of the middle item,
+ expectedPositions: [0, 20, 40] // check that *cellSize* is still uniform
+ // (middle item will be left-aligned in the cell by default)
+ },
+ {
+ tag: "hor 66/3",
+ layout: {
+ type: "GridLayout",
+ columns: 3,
+ items: [
+ {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true}
+ ]
+ },
+ layoutWidth: 66,
+ expectedWidths: [20, 10, 22],
+ expectedPositions: [0, 22, 44]
+ },
+ {
+ tag: "ver 66/3",
+ layout: {
+ type: "GridLayout",
+ columns: 1,
+ items: [
+ {minimumHeight: 1, preferredHeight: 10, maximumHeight: 20, fillHeight: true},
+ {minimumHeight: 1, preferredHeight: 4, maximumHeight: 10, fillHeight: true},
+ {minimumHeight: 1, preferredHeight: 50, maximumHeight: 99, fillHeight: true}
+ ]
+ },
+ layoutHeight: 66,
+ expectedHeights: [20, 10, 22],
+ // If items are too small to fit the cell, they have a default alignment of
+ // Qt::AlignLeft | Qt::AlignVCenter
+ expectedPositions: [1, 22+6, 44]
+ }
+ ];
+ }
+
+ function test_uniformCellSizes(data)
+ {
+ let layout = LayoutHelpers.buildLayout(data.layout, testCase)
+ let isHorizontal = data.hasOwnProperty("expectedWidths")
+ layout.rowSpacing = 0
+ layout.columnSpacing = 0
+ layout.uniformCellWidths = true
+ layout.uniformCellHeights = true
+ waitForPolish(layout)
+ if (data.hasOwnProperty('layoutWidth')) {
+ layout.width = data.layoutWidth
+ }
+ if (data.hasOwnProperty('layoutHeight')) {
+ layout.height = data.layoutHeight
+ }
+
+ let expectedSizes = isHorizontal ? data.expectedWidths : data.expectedHeights
+ let actualSizes = []
+ let i = 0
+ for (i = 0; i < layout.children.length; i++) {
+ let item = layout.children[i]
+ actualSizes.push(isHorizontal ? item.width : item.height)
+ }
+ compare(actualSizes, expectedSizes)
+
+ if (data.hasOwnProperty('expectedPositions')) {
+ let actualPositions = []
+ // If items are too small to fit the cell, they have a default alignment of
+ // Qt::AlignLeft | Qt::AlignVCenter
+ for (i = 0; i < layout.children.length; i++) {
+ let item = layout.children[i]
+ actualPositions.push(isHorizontal ? item.x : item.y)
+ }
+ compare(actualPositions, data.expectedPositions)
+ }
+ }
+
}
}
diff --git a/tests/auto/quick/qquicklayouts/data/tst_layoutproxy.qml b/tests/auto/quick/qquicklayouts/data/tst_layoutproxy.qml
new file mode 100644
index 0000000000..28b0f9a61c
--- /dev/null
+++ b/tests/auto/quick/qquicklayouts/data/tst_layoutproxy.qml
@@ -0,0 +1,687 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.6
+import QtTest 1.0
+import QtQuick.Layouts
+
+Item {
+ id: container
+ width: 200
+ height: 200
+ TestCase {
+ id: testCase
+ name: "Tests_LayoutProxy"
+ when: windowShown
+ width: parent.width
+ height: parent.height
+
+ Component {
+ id: layout_proxy_Component
+ Item {
+ anchors.fill: container
+
+ property var rect1: Rectangle {
+ id: redRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [mapToItem(container, Qt.point(0, 0)).x,
+ mapToItem(container, Qt.point(0, 0)).y,
+ width, height]
+ color: "red"
+ }
+
+
+ property var rect2: Rectangle {
+ id: blueRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [mapToItem(container, Qt.point(0, 0)).x,
+ mapToItem(container, Qt.point(0, 0)).y,
+ width, height]
+ color: "blue"
+ }
+
+ property var layout1: RowLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ LayoutItemProxy { target: blueRectanlge }
+ }
+
+ property var layout2: ColumnLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ LayoutItemProxy { target: blueRectanlge }
+ }
+ }
+ }
+
+ function test_Proxy_simple()
+ {
+ var item = createTemporaryObject(layout_proxy_Component, container);
+
+ item.layout2.visible = false
+ item.layout1.visible = true
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200])
+
+ item.layout1.visible = false
+ item.layout2.visible = true
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100])
+ tryCompare(item.rect2, "itemRect", [ 0, 100, 200, 100])
+
+ item.layout1.visible = true
+ item.layout2.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200])
+
+ }
+
+ function test_Proxy_layout_destruction1()
+ {
+ var item = createTemporaryObject(layout_proxy_Component, container);
+
+ item.layout1.visible = true
+ item.layout2.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200])
+
+ item.layout1.visible = false
+ item.layout2.visible = true
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100])
+ tryCompare(item.rect2, "itemRect", [ 0, 100, 200, 100])
+
+ item.layout2.destroy() //destroy the layout that has control
+ wait(0) // process the scheduled delete and actually invoke the dtor
+ compare(item.layout2, null)
+ item.layout1.visible = true //check that the other one still works
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200])
+ }
+
+ function test_Proxy_layout_destruction2()
+ {
+ var item = createTemporaryObject(layout_proxy_Component, container);
+
+ item.layout1.visible = false
+ item.layout2.visible = false
+
+ //destroy both layouts while none has control
+ item.layout1.destroy()
+ item.layout2.destroy()
+ wait(0) // process the scheduled delete and actually invoke the dtor
+ //all layouts should be gone now
+ compare(item.layout1, null)
+ compare(item.layout2, null)
+ //but the rectangles should still be here
+ verify(item.rect1 !== null)
+ verify(item.rect2 !== null)
+ }
+
+ function test_Proxy_layout_destruction3()
+ {
+ var item = createTemporaryObject(layout_proxy_Component, container);
+
+ item.layout1.visible = true
+ item.layout2.visible = true
+
+ //destroy both layouts while both have control
+ item.layout1.destroy()
+ item.layout2.destroy()
+ wait(0) // process the scheduled delete and actually invoke the dtor
+ //all layouts should be gone now
+ compare(item.layout1, null)
+ compare(item.layout2, null)
+ //but the rectangles should still be here
+ verify(item.rect1 !== null)
+ verify(item.rect2 !== null)
+ }
+
+ function test_Proxy_layout_destruction_of_targets()
+ {
+ var item = createTemporaryObject(layout_proxy_Component, container);
+
+ item.layout1.visible = true
+ item.layout2.visible = false
+
+ //destroy a rectangle just to see if the proxy crashes
+ item.rect1.destroy()
+ wait(0) // process the scheduled delete and actually invoke the dtor
+ compare(item.rect1, null)
+
+ //the proxy still has the size of the item and is still there
+ tryCompare(item.layout1.children[0], "x", 0)
+ tryCompare(item.layout1.children[0], "y", 0)
+ tryCompare(item.layout1.children[0], "width", 100)
+ tryCompare(item.layout1.children[0], "height", 200)
+ //the second item is still here
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200])
+ //the most important thing is that it does not crash
+
+ }
+
+ Component {
+ id: layout_proxy_Component_Three
+ Item {
+ anchors.fill: container
+ property var rect1: Rectangle {
+ id: redRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [mapToItem(container, Qt.point(0, 0)).x,
+ mapToItem(container, Qt.point(0, 0)).y,
+ width, height]
+ color: "red"
+ }
+
+ property var rect2: Rectangle {
+ id: blueRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [mapToItem(container, Qt.point(0, 0)).x,
+ mapToItem(container, Qt.point(0, 0)).y,
+ width, height]
+ color: "blue"
+ }
+
+ property var layout1: RowLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ LayoutItemProxy { target: blueRectanlge }
+ }
+
+ property var layout2: ColumnLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ LayoutItemProxy { target: blueRectanlge }
+ }
+
+ property var layout3: ColumnLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ LayoutItemProxy { target: blueRectanlge }
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ color: "green"
+ }
+ }
+ }
+ }
+
+ function test_Proxy_native_item()
+ {
+ var item = createTemporaryObject(layout_proxy_Component_Three, container);
+
+ item.layout1.visible = true
+ item.layout2.visible = false
+ item.layout3.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200])
+
+ item.layout1.visible = false
+ item.layout2.visible = true
+ item.layout3.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100])
+ tryCompare(item.rect2, "itemRect", [ 0, 100, 200, 100])
+
+ item.layout1.visible = false
+ item.layout2.visible = false
+ item.layout3.visible = true
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 67]) //66.6 = 67
+ tryCompare(item.rect2, "itemRect", [ 0, 67, 200, 66])
+ }
+
+
+ Component {
+ id: layout_proxy_Component_overwrite
+ Item {
+ anchors.fill: container
+ property var rect1: Rectangle {
+ id: redRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.preferredWidth: 180
+ Layout.preferredHeight: 180
+ Layout.margins: 0
+ property var itemRect: [mapToItem(container, Qt.point(0, 0)).x,
+ mapToItem(container, Qt.point(0, 0)).y,
+ width, height]
+ color: "red"
+ }
+
+ property var rect2: Rectangle {
+ id: blueRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.margins: 0
+ property var itemRect: [mapToItem(container, Qt.point(0, 0)).x,
+ mapToItem(container, Qt.point(0, 0)).y,
+ width, height]
+ color: "blue"
+ }
+
+ property var layout1: RowLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ LayoutItemProxy { target: blueRectanlge }
+ }
+
+ property var layout2: ColumnLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ LayoutItemProxy { target: blueRectanlge }
+ }
+ }
+ }
+
+ function test_Proxy_overwrite_layout_properties()
+ {
+ var item = createTemporaryObject(layout_proxy_Component_overwrite, container);
+
+ item.layout1.visible = true
+ item.layout2.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 180, 200])
+ tryCompare(item.rect2, "itemRect", [ 180, 0, 20, 200])
+
+ item.layout1.visible = false
+ item.layout2.visible = true
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 180])
+ tryCompare(item.rect2, "itemRect", [ 0, 180, 200, 20])
+
+ //should overwrite the rectangles preferences
+ item.layout1.children[0].Layout.preferredWidth = 100
+ item.layout1.children[0].Layout.preferredHeight = 100
+ item.layout1.children[1].Layout.preferredWidth = 100
+ item.layout1.children[1].Layout.preferredHeight = 100
+
+ item.layout1.visible = true
+ item.layout2.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200])
+ }
+
+ Component {
+ id: layout_proxy_Component_overwrite_declarative
+ Item {
+ anchors.fill: container
+ property var rect1: Rectangle {
+ id: redRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.minimumWidth: 1
+ Layout.minimumHeight: 1
+ Layout.maximumWidth: 3
+ Layout.maximumHeight: 3
+ Layout.preferredWidth: 2
+ Layout.preferredHeight: 2
+ Layout.margins: 1
+ Layout.leftMargin: 2
+ Layout.topMargin: 3
+ Layout.rightMargin: 4
+ Layout.bottomMargin: 5
+ Layout.alignment: Qt.AlignBottom
+ property var itemRect: [mapToItem(container, Qt.point(0, 0)).x,
+ mapToItem(container, Qt.point(0, 0)).y,
+ width, height]
+ color: "red"
+ }
+
+
+ property var layout1: RowLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge
+ Layout.fillWidth: false
+ Layout.fillHeight: false
+ Layout.minimumWidth: 100
+ Layout.minimumHeight: 100
+ Layout.maximumWidth: 300
+ Layout.maximumHeight: 300
+ Layout.preferredWidth: 200
+ Layout.preferredHeight: 200
+ Layout.margins: 100
+ Layout.leftMargin: 200
+ Layout.topMargin: 300
+ Layout.rightMargin: 400
+ Layout.bottomMargin: 500
+ Layout.alignment: Qt.AlignTop
+ }
+ }
+
+ property var layout2: RowLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ }
+ }
+ }
+
+ function test_Proxy_overwrite_layout_properties_declarative()
+ {
+ var item = createTemporaryObject(layout_proxy_Component_overwrite_declarative, container);
+
+ compare(item.layout2.children[0].Layout.fillWidth, item.rect1.Layout.fillWidth)
+ compare(item.layout2.children[0].Layout.fillHeight, item.rect1.Layout.fillHeight)
+ compare(item.layout2.children[0].Layout.minimumWidth, item.rect1.Layout.minimumWidth)
+ compare(item.layout2.children[0].Layout.minimumHeight, item.rect1.Layout.minimumHeight)
+ compare(item.layout2.children[0].Layout.maximumWidth, item.rect1.Layout.maximumWidth)
+ compare(item.layout2.children[0].Layout.maximumHeight, item.rect1.Layout.maximumHeight)
+ compare(item.layout2.children[0].Layout.preferredWidth, item.rect1.Layout.preferredWidth)
+ compare(item.layout2.children[0].Layout.preferredHeight, item.rect1.Layout.preferredHeight)
+ compare(item.layout2.children[0].Layout.margins, item.rect1.Layout.margins)
+ compare(item.layout2.children[0].Layout.leftMargin, item.rect1.Layout.leftMargin)
+ compare(item.layout2.children[0].Layout.topMargin, item.rect1.Layout.topMargin)
+ compare(item.layout2.children[0].Layout.rightMargin, item.rect1.Layout.rightMargin)
+ compare(item.layout2.children[0].Layout.bottomMargin, item.rect1.Layout.bottomMargin)
+ compare(item.layout2.children[0].Layout.alignment, item.rect1.Layout.alignment)
+
+ verify(item.layout1.children[0].Layout.fillWidth != item.rect1.Layout.fillWidth)
+ verify(item.layout1.children[0].Layout.fillHeight != item.rect1.Layout.fillHeight)
+ verify(item.layout1.children[0].Layout.minimumWidth != item.rect1.Layout.minimumWidth)
+ verify(item.layout1.children[0].Layout.minimumHeight != item.rect1.Layout.minimumHeight)
+ verify(item.layout1.children[0].Layout.maximumWidth != item.rect1.Layout.maximumWidth)
+ verify(item.layout1.children[0].Layout.maximumHeight != item.rect1.Layout.maximumHeight)
+ verify(item.layout1.children[0].Layout.preferredWidth != item.rect1.Layout.preferredWidth)
+ verify(item.layout1.children[0].Layout.preferredHeight != item.rect1.Layout.preferredHeight)
+ verify(item.layout1.children[0].Layout.margins != item.rect1.Layout.margins)
+ verify(item.layout1.children[0].Layout.leftMargin != item.rect1.Layout.leftMargin)
+ verify(item.layout1.children[0].Layout.topMargin != item.rect1.Layout.topMargin)
+ verify(item.layout1.children[0].Layout.rightMargin != item.rect1.Layout.rightMargin)
+ verify(item.layout1.children[0].Layout.bottomMargin != item.rect1.Layout.bottomMargin)
+ verify(item.layout1.children[0].Layout.alignment != item.rect1.alignment)
+
+ compare(item.layout1.children[0].Layout.fillWidth, false)
+ compare(item.layout1.children[0].Layout.fillHeight, false)
+ compare(item.layout1.children[0].Layout.minimumWidth, item.rect1.Layout.minimumWidth * 100)
+ compare(item.layout1.children[0].Layout.minimumHeight, item.rect1.Layout.minimumHeight * 100)
+ compare(item.layout1.children[0].Layout.maximumWidth, item.rect1.Layout.maximumWidth * 100)
+ compare(item.layout1.children[0].Layout.maximumHeight, item.rect1.Layout.maximumHeight * 100)
+ compare(item.layout1.children[0].Layout.preferredWidth, item.rect1.Layout.preferredWidth * 100)
+ compare(item.layout1.children[0].Layout.preferredHeight, item.rect1.Layout.preferredHeight * 100)
+ compare(item.layout1.children[0].Layout.margins, item.rect1.Layout.margins * 100)
+ compare(item.layout1.children[0].Layout.leftMargin, item.rect1.Layout.leftMargin * 100)
+ compare(item.layout1.children[0].Layout.topMargin, item.rect1.Layout.topMargin * 100)
+ compare(item.layout1.children[0].Layout.rightMargin, item.rect1.Layout.rightMargin * 100)
+ compare(item.layout1.children[0].Layout.bottomMargin, item.rect1.Layout.bottomMargin * 100)
+ compare(item.layout1.children[0].Layout.alignment, Qt.AlignTop)
+ }
+
+ Component {
+ id: layout_proxy_Component_nesting
+ Item {
+ anchors.fill: container
+
+ property var rect1: Rectangle {
+ id: redRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0,
+ parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0,
+ width, height]
+ color: "red"
+ }
+
+
+ property var rect2: Rectangle {
+ id: blueRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0,
+ parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0,
+ width, height]
+ color: "blue"
+ }
+
+ property var rect3: Rectangle {
+ id: greenRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0,
+ parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0,
+ width, height]
+ color: "green"
+ }
+
+ property var layout1: RowLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ ColumnLayout {
+ spacing: 0
+ LayoutItemProxy { target: blueRectanlge }
+ LayoutItemProxy { target: greenRectanlge }
+ }
+ }
+
+ property var layout2: ColumnLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ RowLayout {
+ spacing: 0
+ LayoutItemProxy { target: blueRectanlge }
+ LayoutItemProxy { target: greenRectanlge }
+ }
+ }
+ }
+ }
+
+ function test_Proxy_nesting()
+ {
+ var item = createTemporaryObject(layout_proxy_Component_nesting, container);
+
+ item.layout2.visible = false
+ item.layout1.visible = true
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 100])
+ tryCompare(item.rect3, "itemRect", [ 100, 100, 100, 100])
+
+ item.layout1.visible = false
+ item.layout2.visible = true
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100])
+ tryCompare(item.rect2, "itemRect", [ 0, 100, 100, 100])
+ tryCompare(item.rect3, "itemRect", [ 100, 100, 100, 100])
+
+ item.layout1.visible = true
+ item.layout2.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 100])
+ tryCompare(item.rect3, "itemRect", [ 100, 100, 100, 100])
+ }
+
+
+
+ Component {
+ id: layout_proxy_Component_nesting_item
+ Item {
+ anchors.fill: container
+
+ property var rect1: Rectangle {
+ id: redRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0,
+ parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0,
+ width, height]
+ color: "red"
+ }
+
+ property var rect2: Rectangle {
+ id: blueRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0,
+ parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0,
+ width, height]
+ color: "blue"
+ }
+
+ property var rect3: Rectangle {
+ id: greenRectanlge
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0,
+ parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0,
+ width, height]
+ color: "green"
+ }
+
+ property var rect4: Rectangle {
+ id: yellowRectangle
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0,
+ parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0,
+ width, height]
+ color: "yellow"
+ }
+
+ property var rect5: Rectangle {
+ id: brownRectangle
+ parent: container
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0,
+ parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0,
+ width, height]
+ color: "brown"
+ }
+
+ property var layout1: RowLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ ColumnLayout {
+ spacing: 0
+ LayoutItemProxy { target: blueRectanlge }
+ LayoutItemProxy { target: greenRectanlge }
+ Item {
+ implicitWidth: rll.implicitWidth
+ implicitHeight: rll.implicitHeight
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ RowLayout {
+ id: rll
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: yellowRectangle }
+ LayoutItemProxy { target: brownRectangle }
+ }
+ }
+ }
+ }
+
+ property var layout2: ColumnLayout {
+ parent: container
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: redRectanlge }
+ RowLayout {
+ spacing: 0
+ LayoutItemProxy { target: blueRectanlge }
+ LayoutItemProxy { target: greenRectanlge }
+ Item {
+ implicitWidth: cll.implicitWidth
+ implicitHeight: cll.implicitHeight
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ ColumnLayout {
+ id: cll
+ anchors.fill: parent
+ spacing: 0
+ LayoutItemProxy { target: yellowRectangle }
+ LayoutItemProxy { target: brownRectangle }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function test_Proxy_nesting_item()
+ {
+ var item = createTemporaryObject(layout_proxy_Component_nesting_item, container);
+
+ item.layout1.visible = true
+ item.layout2.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200])
+ tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 67])
+ tryCompare(item.rect3, "itemRect", [ 100, 67, 100, 66])
+ tryCompare(item.rect4, "itemRect", [ 100, 133, 50, 67])
+ tryCompare(item.rect5, "itemRect", [ 150, 133, 50, 67])
+
+ item.layout2.visible = true
+ item.layout1.visible = false
+
+ tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100])
+ tryCompare(item.rect2, "itemRect", [ 0, 100, 67, 100])
+ tryCompare(item.rect3, "itemRect", [ 67, 100, 66, 100])
+ tryCompare(item.rect4, "itemRect", [ 133, 100, 67, 50])
+ tryCompare(item.rect5, "itemRect", [ 133, 150, 67, 50])
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
index 506893b631..d31a0b0fb8 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
@@ -1,9 +1,11 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-import QtQuick 2.2
-import QtTest 1.0
-import QtQuick.Layouts 1.0
+import QtQuick
+import QtTest
+import QtQuick.Controls
+import QtQuick.Layouts
+import "LayoutHelperLibrary.js" as LayoutHelpers
import org.qtproject.Test
@@ -23,6 +25,13 @@ Item {
return [item.x, item.y, item.width, item.height];
}
+ function cleanup() {
+ if (LayoutSetup.useDefaultSizePolicy) {
+ LayoutSetup.useDefaultSizePolicy = false
+ compare(LayoutSetup.useDefaultSizePolicy, false)
+ }
+ }
+
Component {
id: rectangle_Component
Rectangle {
@@ -926,6 +935,28 @@ Item {
},
layoutWidth: 28,
expectedWidths: [22, 6]
+ },{
+ tag: "resize_to_0_width",
+ layout: {
+ type: "RowLayout",
+ items: [
+ {preferredWidth: 10, fillWidth: true},
+ ]
+ },
+ layoutWidth: 0,
+ expectedWidths: [0]
+ },{
+ tag: "preferred_infinity", // Do not crash/assert when the preferred size is infinity
+ layout: {
+ type: "RowLayout",
+ items: [
+ {minimumWidth: 10, preferredWidth: Number.POSITIVE_INFINITY, fillWidth: true},
+ {minimumWidth: 20, preferredWidth: Number.POSITIVE_INFINITY, fillWidth: true},
+ ]
+ },
+ layoutWidth: 31, // Important that this is between minimum and preferred width of the layout.
+ expectedWidths: [10, 21] // The result here does not have to be exact. (This
+ // test is mostly concerned about not crashing).
}
];
}
@@ -946,6 +977,148 @@ Item {
layout.destroy();
}
+ function test_uniformCellSizes_data()
+ {
+ return [
+ {
+ tag: "hor 9/3",
+ layout: {
+ type: "RowLayout",
+ items: [
+ {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true}
+ ]
+ },
+ layoutWidth: 9,
+ expectedWidths: [3, 3, 3],
+ expectedPositions: [0, 3, 6]
+ },
+ {
+ tag: "hor 30/3",
+ layout: {
+ type: "RowLayout",
+ items: [
+ {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true}
+ ]
+ },
+ layoutWidth: 30,
+ expectedWidths: [10, 10, 10]
+ },
+ {
+ tag: "hor 60/3",
+ layout: {
+ type: "RowLayout",
+ items: [
+ {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true}
+ ]
+ },
+ layoutWidth: 60,
+ expectedWidths: [20, 10, 20], // We are beyond the maximumWidth. of the middle item,
+ expectedPositions: [0, 20, 40] // check that *cellSize* is still uniform
+ // (middle item will be left-aligned in the cell by default)
+ },
+ {
+ tag: "hor 66/3",
+ layout: {
+ type: "RowLayout",
+ items: [
+ {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
+ {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true}
+ ]
+ },
+ layoutWidth: 66,
+ expectedWidths: [20, 10, 22],
+ expectedPositions: [0, 22, 44]
+ },
+ {
+ tag: "ver 66/3",
+ layout: {
+ type: "ColumnLayout",
+ items: [
+ {minimumHeight: 1, preferredHeight: 10, maximumHeight: 20, fillHeight: true},
+ {minimumHeight: 1, preferredHeight: 4, maximumHeight: 10, fillHeight: true},
+ {minimumHeight: 1, preferredHeight: 50, maximumHeight: 99, fillHeight: true}
+ ]
+ },
+ layoutHeight: 66,
+ expectedHeights: [20, 10, 22],
+ // If items are too small to fit the cell, they have a default alignment of
+ // Qt::AlignLeft | Qt::AlignVCenter
+ expectedPositions: [1, 22+6, 44]
+ }
+ ];
+ }
+
+ function test_uniformCellSizes(data)
+ {
+ let layout = LayoutHelpers.buildLayout(data.layout, testCase)
+ let isHorizontal = data.hasOwnProperty("expectedWidths")
+ layout.spacing = 0
+ layout.uniformCellSizes = true
+ waitForPolish(layout)
+ if (data.hasOwnProperty('layoutWidth')) {
+ layout.width = data.layoutWidth
+ }
+ if (data.hasOwnProperty('layoutHeight')) {
+ layout.height = data.layoutHeight
+ }
+
+ let expectedSizes = (isHorizontal ? data.expectedWidths : data.expectedHeights)
+ let actualSizes = []
+ let i = 0
+ for (i = 0; i < layout.children.length; i++) {
+ let item = layout.children[i]
+ actualSizes.push(isHorizontal ? item.width : item.height)
+ }
+ compare(actualSizes, expectedSizes)
+
+ if (data.hasOwnProperty('expectedPositions')) {
+ let actualPositions = []
+ for (i = 0; i < layout.children.length; i++) {
+ let item = layout.children[i]
+ actualPositions.push(isHorizontal ? item.x : item.y)
+ }
+ compare(actualPositions, data.expectedPositions)
+ }
+ }
+
+ Component {
+ id: uniformCellSizes_QML_Component
+ RowLayout {
+ spacing: 0
+ uniformCellSizes: true
+ Rectangle {
+ implicitWidth: 1
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ color: "red"
+ }
+ Rectangle {
+ implicitWidth: 2
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ color: "blue"
+ }
+ }
+ }
+
+ function test_uniformCellSizes_QML(data)
+ {
+ var layout = createTemporaryObject(uniformCellSizes_QML_Component, testCase)
+ layout.width = 40
+ layout.height = 20
+ let expectedWidths = [20, 20]
+ let actualWidths = [layout.children[0].width, layout.children[1].width]
+ compare(actualWidths, expectedWidths)
+ }
+
+
Component {
id: layout_alignToPixelGrid_Component
RowLayout {
@@ -1168,6 +1341,78 @@ Item {
}
Component {
+ id: sizeHintBindingLoopComp
+ Item {
+ id: root
+ anchors.fill: parent
+ property var customWidth: 100
+ RowLayout {
+ id: col
+ Item {
+ id: item
+ implicitHeight: 80
+ implicitWidth: Math.max(col2.implicitWidth, root.customWidth + 20)
+ ColumnLayout {
+ id: col2
+ width: parent.width
+ Item {
+ id: rect
+ implicitWidth: root.customWidth
+ implicitHeight: 80
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function test_sizeHintBindingLoopIssue() {
+ var item = createTemporaryObject(sizeHintBindingLoopComp, container)
+ waitForRendering(item)
+ item.customWidth += 10
+ waitForRendering(item)
+ verify(!LayoutSetup.bindingLoopDetected, "Detected binding loop")
+ LayoutSetup.resetBindingLoopDetectedFlag()
+ }
+
+ Component {
+ id: polishLayoutItemComp
+ Item {
+ anchors.fill: parent
+ implicitHeight: contentLayout.implicitHeight
+ implicitWidth: contentLayout.implicitWidth
+ property alias textLayout: contentLayout
+ RowLayout {
+ width: parent.width
+ height: parent.height
+ ColumnLayout {
+ id: contentLayout
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
+ Layout.maximumWidth: 200
+ Repeater {
+ model: 2
+ Text {
+ Layout.fillWidth: true
+ text: "This is a long text causing line breaks to show the bug."
+ wrapMode: Text.Wrap
+ }
+ }
+ Item {
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+ }
+
+ function test_polishLayoutItemIssue() {
+ var rootItem = createTemporaryObject(polishLayoutItemComp, container)
+ waitForRendering(rootItem)
+ var textItem = rootItem.textLayout.children[1]
+ verify(textItem.y >= rootItem.textLayout.children[0].height)
+ }
+
+ Component {
id: rearrangeNestedLayouts_Component
RowLayout {
id: layout
@@ -1334,6 +1579,49 @@ Item {
compare(rootRect.item1.width, 100)
}
+ //---------------------------
+ // Layout with negative size
+ Component {
+ id: negativeSize_Component
+ Item {
+ id: rootItem
+ width: 0
+ height: 0
+ // default width x height: (0 x 0)
+ RowLayout {
+ spacing: 0
+ anchors.fill: parent
+ anchors.leftMargin: 1 // since parent size == (0 x 0), it causes layout size
+ anchors.bottomMargin: 1 // to become (-1, -1)
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+
+ function test_negativeSize() {
+ let rootItem = createTemporaryObject(negativeSize_Component, container)
+ let rowLayout = rootItem.children[0]
+ let item = rowLayout.children[0]
+
+ const arr = [7, 1, 7, 0]
+ arr.forEach((n) => {
+ rootItem.width = n
+ rootItem.height = n
+
+ // n === 0 is special: It will cause the layout to have a
+ // negative size. In this case it will simply not rearrange its
+ // child (and leave it at its previous size, 6)
+ const expectedItemExtent = n === 0 ? 6 : n - 1
+
+ compare(item.width, expectedItemExtent)
+ compare(item.height, expectedItemExtent)
+ });
+ }
+
+
//---------------------------
Component {
id: rowlayoutWithTextItems_Component
@@ -1467,9 +1755,146 @@ Item {
compare(rootItem.maxWidth, 66)
// Should not trigger a binding loop
- verify(!BindingLoopDetector.bindingLoopDetected, "Detected binding loop")
- BindingLoopDetector.reset()
+ verify(!LayoutSetup.bindingLoopDetected, "Detected binding loop")
+ LayoutSetup.resetBindingLoopDetectedFlag()
}
- }
+
+ //---------------------------
+ // QTBUG-111792
+ Component {
+ id: rowlayoutCrashes_Component
+ RowLayout {
+ spacing: 5
+ Rectangle {
+ color: "red"
+ implicitWidth: 10
+ implicitHeight: 10
+ }
+ Rectangle {
+ color: "green"
+ implicitWidth: 10
+ implicitHeight: 10
+ }
+ }
+ }
+
+ function test_dontCrashAfterDestroyingChildren_data() {
+ return [
+ { tag: "setWidth", func: function (layout) { layout.width = 42 } },
+ { tag: "setHeight", func: function (layout) { layout.height = 42 } },
+ { tag: "getImplicitWidth", func: function (layout) { let x = layout.implicitWidth } },
+ { tag: "getImplicitHeight", func: function (layout) { let x = layout.implicitHeight } },
+ ]
+ }
+
+ function test_dontCrashAfterDestroyingChildren(data) {
+ var layout = createTemporaryObject(rowlayoutCrashes_Component, container)
+ waitForRendering(layout)
+ compare(layout.implicitWidth, 25)
+ layout.children[0].destroy() // deleteLater()
+ wait(0) // process the scheduled delete and actually invoke the dtor
+ data.func(layout) // call a function that might ultimately access the deleted item (but shouldn't)
+ }
+
+ //---------------------------
+ // Default layout size policy
+ Component {
+ id: defaultLayoutComp
+ Item {
+ id: rootItem
+ width: 110
+ height: 100
+ // Check default layout size policy
+ RowLayout {
+ spacing: 0
+ anchors.fill: parent
+ // Rectangle item - SizePolicy { Horizontal: Fixed; Vertical: Fixed }
+ Rectangle {}
+ // Button item - SizePolicy { Horizontal: Preferred; Vertical: Fixed }
+ Button {}
+ // Frame item - SizePolicy { Horizontal: Preferred; Vertical: Preferred }
+ Frame {}
+ }
+ }
+ }
+
+ function test_defaultLayoutSize() {
+ let rootItem = createTemporaryObject(defaultLayoutComp, container)
+ waitForRendering(rootItem)
+
+ let rowLayout = rootItem.children[0]
+
+ // Test default size policy disabled by default
+ compare(LayoutSetup.useDefaultSizePolicy, false)
+
+ let defaultButtonWidth = rowLayout.children[1].width
+ let defaultFrameWidth = rowLayout.children[2].width
+
+ // Enable attached properties for items and check its size
+ {
+ rowLayout.children[0].Layout.fillWidth = true
+ rowLayout.children[0].Layout.fillHeight = true
+ rowLayout.children[1].Layout.fillWidth = true
+ rowLayout.children[1].Layout.fillHeight = true
+ rowLayout.children[2].Layout.fillWidth = true
+ rowLayout.children[2].Layout.fillHeight = true
+ waitForRendering(rowLayout)
+ let isAbovePreferred = rowLayout.width >= rowLayout.implicitWidth
+ // Removing rectangle as width & implicitWidth be zero always, which makes validation of no use
+ for (let i = 1; i < rowLayout.children.length; i++) {
+ compare(rowLayout.children[i].width >= rowLayout.children[i].implicitWidth, isAbovePreferred)
+ compare(rowLayout.children[i].height, rowLayout.height)
+ }
+ }
+
+ // Destroy existing object
+ rootItem.destroy()
+
+ // Enable default size policy
+ LayoutSetup.useDefaultSizePolicy = true
+ compare(LayoutSetup.useDefaultSizePolicy, true)
+ rootItem = createTemporaryObject(defaultLayoutComp, container)
+ waitForRendering(rootItem)
+ rowLayout = rootItem.children[0]
+
+ // The default size policy would stretch button and frame accordingly
+ {
+ verify(Math.abs(rowLayout.width - (rowLayout.children[0].width + rowLayout.children[1].width + rowLayout.children[2].width)) <= 1)
+ compare(rowLayout.children[1].width < rowLayout.children[2].width, rowLayout.children[1].implicitWidth < rowLayout.children[2].implicitWidth)
+ compare(rowLayout.children[1].height, rowLayout.children[1].implicitHeight)
+ compare(rowLayout.children[2].height, rowLayout.height)
+ }
+
+ // Change the width and height of the root item to see layout size change
+ // Since default size policy for button and frame are Preferred, these items should
+ // stretch
+ {
+ let szDefaultButtonWidth = rowLayout.children[1].width
+ let szDefaultFrameWidth = rowLayout.children[2].width
+
+ rootItem.width = 210
+ rootItem.height = 200
+ waitForRendering(rootItem)
+ verify(rowLayout.children[1].width > szDefaultButtonWidth)
+ compare(rowLayout.children[1].height, rowLayout.children[1].implicitHeight)
+ verify(rowLayout.children[2].width > szDefaultFrameWidth)
+ compare(rowLayout.children[2].height, rowLayout.height)
+ }
+
+ // Disable size policies through attached properties and check item size
+ {
+ rowLayout.children[1].Layout.fillWidth = false
+ rowLayout.children[2].Layout.fillWidth = false
+ rowLayout.children[2].Layout.fillHeight = false
+ waitForRendering(rowLayout)
+ compare(rowLayout.children[1].width, defaultButtonWidth)
+ compare(rowLayout.children[2].width, defaultFrameWidth)
+ for (let index = 1; index < rowLayout.children.length; index++)
+ compare(rowLayout.children[index].height, rowLayout.children[index].implicitHeight)
+ }
+
+ rootItem.destroy()
+ }
+ }
}
diff --git a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
index e8d311183e..f627a90dfd 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
import QtTest 1.15
@@ -724,6 +724,146 @@ Item {
compare(layout.num_onCountChanged, 1)
}
+ // QTBUG-111902
+ Component {
+ id: stackComponent
+ Loader {
+ id: loader
+ asynchronous: true
+ sourceComponent: StackLayout {
+ id: stackLayout
+ Repeater {
+ model: 3
+ Item {
+ required property int index
+ }
+ }
+ }
+ }
+ }
+
+ function test_loadStackLayoutAsynchronously() {
+ var loaderObj = stackComponent.createObject(container)
+ // Check for loader status to be ready
+ tryCompare(loaderObj, 'status', 1)
+ // Get stack layout object
+ var stackLayoutObj = loaderObj.item
+ // Check repeater index of child object
+ compare(stackLayoutObj.children[0].index, 0)
+ compare(stackLayoutObj.children[1].index, 1)
+ compare(stackLayoutObj.children[2].index, 2)
+ // Check stack layout attached property index
+ compare(stackLayoutObj.children[0].StackLayout.index, 0)
+ compare(stackLayoutObj.children[1].StackLayout.index, 1)
+ compare(stackLayoutObj.children[2].StackLayout.index, 2)
+ }
+
+ Component {
+ id: test_repeater_Component
+
+ Item {
+ property alias stackLayout : stackLayout
+ property var model : ListModel {
+ /*
+ * We cannot programmatically reorder siblings (QQuickItem::stackBefore()
+ * and QQuickItem::stackAfter() are not not available to QML, and we cannot
+ * alter the Item::children property to reorder siblings)
+ * Therefore, we have to go through the hoops with a ListModel and Repeater in
+ * order to trigger sibling reordering, just as reported in QTBUG-112691.
+ * Adding an item to a specific index (with model.insert()), will be done in
+ * two steps:
+ * 1. Append an Item to be the last of the siblings
+ * 2. Reorder that Rectangle to be at the correct child index that corresponds
+ * to the index given to model.insert()
+ *
+ * Adding an item to a specific index will therefore test sibling reordering
+ */
+ id: listModel
+ }
+ StackLayout {
+ id: stackLayout
+ anchors.fill: parent
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Repeater {
+ id: repeater
+ model:listModel
+ delegate: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 100
+ objectName: model.color
+ color: model.color
+ }
+ }
+ }
+ }
+ }
+
+ function test_repeater() {
+ let item = createTemporaryObject(test_repeater_Component, container)
+ let layout = item.stackLayout
+ let model = item.model
+ function verifyVisibilityOfItems() {
+ for (let i = 0; i < layout.count; ++i) {
+ compare(layout.children[i].visible, layout.currentIndex === i)
+ }
+ }
+
+ compare(layout.currentIndex, -1)
+ compare(layout.count, 0)
+ model.append({ "color": "red" })
+ compare(layout.currentIndex, 0)
+ compare(layout.count, 1)
+ verifyVisibilityOfItems()
+
+ model.append({ "color": "green" })
+ compare(layout.currentIndex, 0)
+ compare(layout.count, 2)
+ verifyVisibilityOfItems()
+
+ model.append({ "color": "blue" })
+ compare(layout.currentIndex, 0)
+ compare(layout.count, 3)
+ verifyVisibilityOfItems()
+
+ model.insert(0, { "color": "black" })
+ compare(layout.currentIndex, 1)
+ compare(layout.count, 4)
+ verifyVisibilityOfItems()
+
+ // An implicit currentIndex will reset back to -1 if
+ // the StackLayout is empty
+ model.clear()
+ compare(layout.currentIndex, -1)
+ compare(layout.count, 0)
+
+ // set explicit index to out of bounds
+ layout.currentIndex = 1
+ compare(layout.currentIndex, 1)
+ compare(layout.count, 0)
+ verifyVisibilityOfItems()
+
+ model.append({ "color": "red" })
+ compare(layout.currentIndex, 1)
+ compare(layout.count, 1)
+ verifyVisibilityOfItems()
+
+ model.append({ "color": "green" })
+ compare(layout.currentIndex, 1)
+ compare(layout.count, 2)
+ verifyVisibilityOfItems()
+
+ model.insert(1, { "color": "brown" })
+ compare(layout.currentIndex, 2)
+ compare(layout.count, 3)
+ verifyVisibilityOfItems()
+
+ // remove red, currentIndex should decrease
+ model.remove(0, 1)
+ compare(layout.currentIndex, 1)
+ compare(layout.count, 2)
+ verifyVisibilityOfItems()
+ }
}
}
diff --git a/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp b/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp
index 26b01b806d..98b5e96486 100644
--- a/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp
+++ b/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
#include <QtQuickTest/quicktest.h>
@@ -8,20 +8,23 @@ class Setup : public QObject
{
Q_OBJECT
Q_PROPERTY(bool bindingLoopDetected READ wasBindingLoopDetected FINAL)
+ Q_PROPERTY(bool useDefaultSizePolicy READ useDefaultSizePolicy WRITE setUseDefaultSizePolicy FINAL)
public:
Setup() {}
bool wasBindingLoopDetected() const { return mBindingLoopDetected; }
+ bool useDefaultSizePolicy() const { return QCoreApplication::testAttribute(Qt::AA_QtQuickUseDefaultSizePolicy); }
+ void setUseDefaultSizePolicy(bool policy) { QCoreApplication::setAttribute(Qt::AA_QtQuickUseDefaultSizePolicy, policy); }
+
public slots:
- void reset() { mBindingLoopDetected = false; }
+ void resetBindingLoopDetectedFlag() { mBindingLoopDetected = false; }
void qmlEngineAvailable(QQmlEngine *engine)
{
connect(engine, &QQmlEngine::warnings, this, &Setup::qmlWarnings);
-
- qmlRegisterSingletonInstance("org.qtproject.Test", 1, 0, "BindingLoopDetector", this);
+ qmlRegisterSingletonInstance("org.qtproject.Test", 1, 0, "LayoutSetup", this);
}
void qmlWarnings(const QList<QQmlError> &warnings)
diff --git a/tests/auto/quick/qquicklistview/CMakeLists.txt b/tests/auto/quick/qquicklistview/CMakeLists.txt
index 87171ec3ea..cee287ae41 100644
--- a/tests/auto/quick/qquicklistview/CMakeLists.txt
+++ b/tests/auto/quick/qquicklistview/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicklistview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicklistview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicklistview/data/addoncompleted.qml b/tests/auto/quick/qquicklistview/data/addoncompleted.qml
index 0ee94fa0b9..34ee459aa4 100644
--- a/tests/auto/quick/qquicklistview/data/addoncompleted.qml
+++ b/tests/auto/quick/qquicklistview/data/addoncompleted.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
diff --git a/tests/auto/quick/qquicklistview/data/attachedProperties.qml b/tests/auto/quick/qquicklistview/data/attachedProperties.qml
index bfcd8fc5eb..72fdc009e0 100644
--- a/tests/auto/quick/qquicklistview/data/attachedProperties.qml
+++ b/tests/auto/quick/qquicklistview/data/attachedProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquicklistview/data/displayMargin.qml b/tests/auto/quick/qquicklistview/data/displayMargin.qml
index 20ba124a97..64b668326f 100644
--- a/tests/auto/quick/qquicklistview/data/displayMargin.qml
+++ b/tests/auto/quick/qquicklistview/data/displayMargin.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
diff --git a/tests/auto/quick/qquicklistview/data/flickBothDirections.qml b/tests/auto/quick/qquicklistview/data/flickBothDirections.qml
index 6efd0d5a61..a9905adba5 100644
--- a/tests/auto/quick/qquicklistview/data/flickBothDirections.qml
+++ b/tests/auto/quick/qquicklistview/data/flickBothDirections.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
Rectangle {
diff --git a/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml b/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml
index 6d7b1c4e21..6f5160f13d 100644
--- a/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml
+++ b/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQml.Models 2.14
diff --git a/tests/auto/quick/qquicklistview/data/negativeDisplayMargin.qml b/tests/auto/quick/qquicklistview/data/negativeDisplayMargin.qml
index 87ae08478b..d34069d721 100644
--- a/tests/auto/quick/qquicklistview/data/negativeDisplayMargin.qml
+++ b/tests/auto/quick/qquicklistview/data/negativeDisplayMargin.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
diff --git a/tests/auto/quick/qquicklistview/data/objectModelCulling.qml b/tests/auto/quick/qquicklistview/data/objectModelCulling.qml
index c0a70ec485..9ca79ec8e4 100644
--- a/tests/auto/quick/qquicklistview/data/objectModelCulling.qml
+++ b/tests/auto/quick/qquicklistview/data/objectModelCulling.qml
@@ -3,6 +3,7 @@ import QtQuick 2.12
import QtQml.Models 2.12
Item {
+ anchors.fill: parent
ObjectModel {
id: model1
diff --git a/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml b/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
index 772b79b500..2215252aef 100644
--- a/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
+++ b/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
import QtTest 1.1
diff --git a/tests/auto/quick/qquicklistview/data/proxytest.qml b/tests/auto/quick/qquicklistview/data/proxytest.qml
index 2d733d81e4..dad5dbad8a 100644
--- a/tests/auto/quick/qquicklistview/data/proxytest.qml
+++ b/tests/auto/quick/qquicklistview/data/proxytest.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
import Proxy 1.0
diff --git a/tests/auto/quick/qquicklistview/data/qtbug34576.qml b/tests/auto/quick/qquicklistview/data/qtbug34576.qml
index e1b96f5d5f..fdc94c0c0b 100644
--- a/tests/auto/quick/qquicklistview/data/qtbug34576.qml
+++ b/tests/auto/quick/qquicklistview/data/qtbug34576.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.7
diff --git a/tests/auto/quick/qquicklistview/data/qtbug61537_modelChangesAsync.qml b/tests/auto/quick/qquicklistview/data/qtbug61537_modelChangesAsync.qml
index a526d8669a..c385a9b4b8 100644
--- a/tests/auto/quick/qquicklistview/data/qtbug61537_modelChangesAsync.qml
+++ b/tests/auto/quick/qquicklistview/data/qtbug61537_modelChangesAsync.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
Item {
diff --git a/tests/auto/quick/qquicklistview/data/repositionListViewOnPopulateTransition.qml b/tests/auto/quick/qquicklistview/data/repositionListViewOnPopulateTransition.qml
new file mode 100644
index 0000000000..7a313f7f87
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/repositionListViewOnPopulateTransition.qml
@@ -0,0 +1,51 @@
+import QtQuick
+import QtQuick.Controls.Basic
+
+ListView {
+ id: listView
+
+ width: 100
+ height: 100
+
+ verticalLayoutDirection: ListView.BottomToTop
+ interactive: true
+ reuseItems: false
+
+ model: ListModel {
+ ListElement {index: 0; text: "Item0"}
+ }
+
+ delegate: Button {
+ id: button
+
+ required text
+
+ property alias yScale: scaleTransform.yScale
+ property bool inverted: ListView.view.verticalLayoutDirection === ListView.BottomToTop
+
+ transform: Scale {
+ id: scaleTransform
+ origin.y: button.inverted ? button.implicitHeight : 0
+ }
+ }
+
+ populate: Transition {
+ id: populateTransition
+
+ NumberAnimation {
+ // in this case we do want to animate y to achieve a smooth
+ // transition from y=0 to its assigned y location
+ property: "y"
+ duration: 500
+ easing.type: Easing.InOutQuad
+ }
+ NumberAnimation {
+ property: "yScale"
+ from: 0
+ to: 1
+ duration: 500
+ easing.type: Easing.InOutQuad
+ }
+ }
+}
+
diff --git a/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml b/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml
index c169d92826..fdeca4454a 100644
--- a/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml
+++ b/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/qquicklistview/data/reusedelegateitems.qml b/tests/auto/quick/qquicklistview/data/reusedelegateitems.qml
index e5e64dcbc6..c54d10f88c 100644
--- a/tests/auto/quick/qquicklistview/data/reusedelegateitems.qml
+++ b/tests/auto/quick/qquicklistview/data/reusedelegateitems.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
Rectangle {
diff --git a/tests/auto/quick/qquicklistview/data/roundingErrors.qml b/tests/auto/quick/qquicklistview/data/roundingErrors.qml
index 2c1a1e665d..f850b3573a 100644
--- a/tests/auto/quick/qquicklistview/data/roundingErrors.qml
+++ b/tests/auto/quick/qquicklistview/data/roundingErrors.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQml.Models 2.1
diff --git a/tests/auto/quick/qquicklistview/data/setpositiononlayout.qml b/tests/auto/quick/qquicklistview/data/setpositiononlayout.qml
index 4e43efa33b..a605b2103d 100644
--- a/tests/auto/quick/qquicklistview/data/setpositiononlayout.qml
+++ b/tests/auto/quick/qquicklistview/data/setpositiononlayout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquicklistview/data/sizeTransitions.qml b/tests/auto/quick/qquicklistview/data/sizeTransitions.qml
index da0c8226fb..d1bddb2047 100644
--- a/tests/auto/quick/qquicklistview/data/sizeTransitions.qml
+++ b/tests/auto/quick/qquicklistview/data/sizeTransitions.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import QtQuick.Window 2.1
diff --git a/tests/auto/quick/qquicklistview/data/snapOneItemResize.qml b/tests/auto/quick/qquicklistview/data/snapOneItemResize.qml
index 7ecc833a64..4603cd9f34 100644
--- a/tests/auto/quick/qquicklistview/data/snapOneItemResize.qml
+++ b/tests/auto/quick/qquicklistview/data/snapOneItemResize.qml
@@ -2,6 +2,7 @@ import QtQuick 2.0
ListView {
id: list
+ anchors.fill: parent
currentIndex: 5
snapMode: ListView.SnapOneItem
orientation: ListView.Horizontal
diff --git a/tests/auto/quick/qquicklistview/data/stickyPositioning-both.qml b/tests/auto/quick/qquicklistview/data/stickyPositioning-both.qml
index c28070f183..ece0e2527b 100644
--- a/tests/auto/quick/qquicklistview/data/stickyPositioning-both.qml
+++ b/tests/auto/quick/qquicklistview/data/stickyPositioning-both.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
Rectangle {
diff --git a/tests/auto/quick/qquicklistview/data/stickyPositioning-footer.qml b/tests/auto/quick/qquicklistview/data/stickyPositioning-footer.qml
index 1b398138c8..568762b445 100644
--- a/tests/auto/quick/qquicklistview/data/stickyPositioning-footer.qml
+++ b/tests/auto/quick/qquicklistview/data/stickyPositioning-footer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
Rectangle {
diff --git a/tests/auto/quick/qquicklistview/data/stickyPositioning-header.qml b/tests/auto/quick/qquicklistview/data/stickyPositioning-header.qml
index c0f2a5b2fb..6b5889a808 100644
--- a/tests/auto/quick/qquicklistview/data/stickyPositioning-header.qml
+++ b/tests/auto/quick/qquicklistview/data/stickyPositioning-header.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
Rectangle {
diff --git a/tests/auto/quick/qquicklistview/data/usechooserwithoutdefault.qml b/tests/auto/quick/qquicklistview/data/usechooserwithoutdefault.qml
index 7c555ea264..cba0ad5720 100644
--- a/tests/auto/quick/qquicklistview/data/usechooserwithoutdefault.qml
+++ b/tests/auto/quick/qquicklistview/data/usechooserwithoutdefault.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicklistview/incrementalmodel.cpp b/tests/auto/quick/qquicklistview/incrementalmodel.cpp
index 1670fcd1cf..f2c6219711 100644
--- a/tests/auto/quick/qquicklistview/incrementalmodel.cpp
+++ b/tests/auto/quick/qquicklistview/incrementalmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "incrementalmodel.h"
#include <QGuiApplication>
diff --git a/tests/auto/quick/qquicklistview/incrementalmodel.h b/tests/auto/quick/qquicklistview/incrementalmodel.h
index 1cbd483551..f61d38a3d1 100644
--- a/tests/auto/quick/qquicklistview/incrementalmodel.h
+++ b/tests/auto/quick/qquicklistview/incrementalmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef IncrementalModel_H
#define IncrementalModel_H
diff --git a/tests/auto/quick/qquicklistview/proxytestinnermodel.cpp b/tests/auto/quick/qquicklistview/proxytestinnermodel.cpp
index d00b417429..e142a8a58b 100644
--- a/tests/auto/quick/qquicklistview/proxytestinnermodel.cpp
+++ b/tests/auto/quick/qquicklistview/proxytestinnermodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "proxytestinnermodel.h"
diff --git a/tests/auto/quick/qquicklistview/proxytestinnermodel.h b/tests/auto/quick/qquicklistview/proxytestinnermodel.h
index 3177ec51b3..5ea78ae864 100644
--- a/tests/auto/quick/qquicklistview/proxytestinnermodel.h
+++ b/tests/auto/quick/qquicklistview/proxytestinnermodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef INNERMODEL_H
#define INNERMODEL_H
diff --git a/tests/auto/quick/qquicklistview/randomsortmodel.cpp b/tests/auto/quick/qquicklistview/randomsortmodel.cpp
index 3e0c45bb1a..0e5f562e4b 100644
--- a/tests/auto/quick/qquicklistview/randomsortmodel.cpp
+++ b/tests/auto/quick/qquicklistview/randomsortmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "randomsortmodel.h"
#include <QRandomGenerator>
diff --git a/tests/auto/quick/qquicklistview/randomsortmodel.h b/tests/auto/quick/qquicklistview/randomsortmodel.h
index 9fae9b2b76..9d53a8cff3 100644
--- a/tests/auto/quick/qquicklistview/randomsortmodel.h
+++ b/tests/auto/quick/qquicklistview/randomsortmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef RANDOMSORTMODEL_H
#define RANDOMSORTMODEL_H
diff --git a/tests/auto/quick/qquicklistview/reusemodel.h b/tests/auto/quick/qquicklistview/reusemodel.h
index 4f0ab817b2..0a23a2bda4 100644
--- a/tests/auto/quick/qquicklistview/reusemodel.h
+++ b/tests/auto/quick/qquicklistview/reusemodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef REUSEMODEL_H
#define REUSEMODEL_H
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 8a3a23826d..48171266de 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/QStringListModel>
@@ -187,6 +187,7 @@ private slots:
void populateTransitions();
void populateTransitions_data();
+ void repositionFirstItemOnPopulateTransition();
void sizeTransitions();
void sizeTransitions_data();
@@ -1172,7 +1173,7 @@ void tst_QQuickListView::removed_more(const QUrl &source, QQuickItemView::Vertic
int firstVisibleIndex = -1;
for (int i=0; i<items.size(); i++) {
QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
- if (item && delegateVisible(item)) {
+ if (item && isDelegateVisible(item)) {
firstVisibleIndex = i;
break;
}
@@ -1404,7 +1405,7 @@ void tst_QQuickListView::moved(const QUrl &source, QQuickItemView::VerticalLayou
int firstVisibleIndex = -1;
for (int i=0; i<items.size(); i++) {
QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
- if (item && delegateVisible(item)) {
+ if (item && isDelegateVisible(item)) {
firstVisibleIndex = i;
break;
}
@@ -2739,7 +2740,7 @@ void tst_QQuickListView::sectionsSnap()
QTRY_VERIFY(listview != nullptr);
listview->setSnapMode(snapMode);
- QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+ QTRY_VERIFY(!QQuickItemPrivate::get(listview)->polishScheduled);
QTRY_COMPARE(listview->currentIndex(), 0);
QCOMPARE(listview->contentY(), qreal(-50));
@@ -2897,9 +2898,9 @@ void tst_QQuickListView::currentIndex()
// moving currentItem out of view should make it invisible
listview->setCurrentIndex(0);
- QTRY_VERIFY(delegateVisible(listview->currentItem()));
+ QTRY_VERIFY(isDelegateVisible(listview->currentItem()));
listview->setContentY(200);
- QTRY_VERIFY(!delegateVisible(listview->currentItem()));
+ QTRY_VERIFY(!isDelegateVisible(listview->currentItem()));
// empty model should reset currentIndex to -1
QaimModel emptyModel;
@@ -3131,37 +3132,37 @@ void tst_QQuickListView::itemListFlicker()
QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
item = findItem<QQuickItem>(contentItem, "item2");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
item = findItem<QQuickItem>(contentItem, "item3");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
listview->setCurrentIndex(1);
item = findItem<QQuickItem>(contentItem, "item1");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
item = findItem<QQuickItem>(contentItem, "item2");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
item = findItem<QQuickItem>(contentItem, "item3");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
listview->setCurrentIndex(2);
item = findItem<QQuickItem>(contentItem, "item1");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
item = findItem<QQuickItem>(contentItem, "item2");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
item = findItem<QQuickItem>(contentItem, "item3");
QVERIFY(item);
- QVERIFY(delegateVisible(item));
+ QVERIFY(isDelegateVisible(item));
}
void tst_QQuickListView::cacheBuffer()
@@ -4406,7 +4407,7 @@ void tst_QQuickListView::resizeView()
if (!item) qWarning() << "Item" << i << "not found";
QTRY_VERIFY(item);
QTRY_COMPARE(item->y(), i*20.);
- QCOMPARE(delegateVisible(item), i < 11); // inside view visible, outside not visible
+ QCOMPARE(isDelegateVisible(item), i < 11); // inside view visible, outside not visible
}
// ensure items outside view become invisible
@@ -4419,7 +4420,7 @@ void tst_QQuickListView::resizeView()
if (!item) qWarning() << "Item" << i << "not found";
QTRY_VERIFY(item);
QTRY_COMPARE(item->y(), i*20.);
- QCOMPARE(delegateVisible(item), i < 6); // inside view visible, outside not visible
+ QCOMPARE(isDelegateVisible(item), i < 6); // inside view visible, outside not visible
}
}
@@ -5058,14 +5059,14 @@ void tst_QQuickListView::test_mirroring()
QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection());
// LTR != RTL
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
listviewA->setProperty("layoutDirection", Qt::LeftToRight);
listviewB->setProperty("layoutDirection", Qt::LeftToRight);
// LTR == LTR
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
QCOMPARE(listviewB->layoutDirection(), listviewB->effectiveLayoutDirection());
@@ -5073,25 +5074,25 @@ void tst_QQuickListView::test_mirroring()
QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection());
// LTR != LTR+mirror
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
listviewA->setProperty("layoutDirection", Qt::RightToLeft);
// RTL == LTR+mirror
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
listviewB->setProperty("layoutDirection", Qt::RightToLeft);
// RTL != RTL+mirror
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
listviewA->setProperty("layoutDirection", Qt::LeftToRight);
// LTR == RTL+mirror
- foreach (const QString objectName, objectNames)
+ for (const QString &objectName : std::as_const(objectNames))
QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
}
@@ -6495,30 +6496,30 @@ void tst_QQuickListView::unrequestedVisibility()
const QString wrapperObjectName = QStringLiteral("wrapper");
QQuickItem *item = findItem<QQuickItem>(leftContent, wrapperObjectName, 1);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 1);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 19);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 19);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 16);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 17);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 3);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 4);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
rightview->setCurrentIndex(0);
@@ -6527,10 +6528,10 @@ void tst_QQuickListView::unrequestedVisibility()
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 1);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 1);
QVERIFY(item);
- QTRY_COMPARE(delegateVisible(item), true);
+ QTRY_COMPARE(isDelegateVisible(item), true);
QVERIFY(!findItem<QQuickItem>(leftContent, wrapperObjectName, 19));
QVERIFY(!findItem<QQuickItem>(rightContent, wrapperObjectName, 19));
@@ -6542,123 +6543,123 @@ void tst_QQuickListView::unrequestedVisibility()
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 1);
QVERIFY(item);
- QTRY_COMPARE(delegateVisible(item), false);
+ QTRY_COMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 1);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 19);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 19);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 3);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 4);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 16);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 17);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
model.moveItems(19, 1, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
QTRY_VERIFY((item = findItem<QQuickItem>(leftContent, wrapperObjectName, 1)));
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 1);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 19);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 19);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 4);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 5);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 16);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 17);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
model.moveItems(3, 4, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 4);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 5);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 16);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 17);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
model.moveItems(4, 3, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 4);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 5);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 16);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 17);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
model.moveItems(16, 17, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 4);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 5);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 16);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 17);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
model.moveItems(17, 16, 1);
QVERIFY(QQuickTest::qWaitForPolish(leftview));
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 4);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
item = findItem<QQuickItem>(leftContent, wrapperObjectName, 5);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 16);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(rightContent, wrapperObjectName, 17);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
}
void tst_QQuickListView::populateTransitions()
@@ -6792,6 +6793,20 @@ void tst_QQuickListView::populateTransitions_data()
QTest::newRow("empty to start with, no populate") << false << false << false;
}
+// QTBUG-111050
+/* Reposition first visible item in list view on populate transition
+ Note: Occurs only in BottomToTop or RightToLeft layout */
+void tst_QQuickListView::repositionFirstItemOnPopulateTransition()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("repositionListViewOnPopulateTransition.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickListView *listview = qobject_cast<QQuickListView*>(window->rootObject());
+ QTRY_VERIFY(listview != nullptr);
+ QTRY_COMPARE(listview->contentY(), -100.0);
+}
/*
* Tests if the first visible item is not repositioned if the same item
@@ -7871,7 +7886,8 @@ void tst_QQuickListView::multipleDisplaced()
QTRY_VERIFY(listview->property("displaceTransitionsDone").toBool());
QVariantMap transitionsStarted = listview->property("displaceTransitionsStarted").toMap();
- foreach (const QString &name, transitionsStarted.keys()) {
+ const QStringList keys = transitionsStarted.keys();
+ for (const QString &name : keys) {
QVERIFY2(transitionsStarted[name] == 1,
qPrintable(QString("%1 was displaced %2 times").arg(name).arg(transitionsStarted[name].toInt())));
}
@@ -8241,24 +8257,24 @@ void tst_QQuickListView::displayMargin()
QQuickItem *item0 = findItem<QQuickItem>(content, "delegate", 0);
QVERIFY(item0);
- QCOMPARE(delegateVisible(item0), true);
+ QCOMPARE(isDelegateVisible(item0), true);
// the 14th item should be within the end margin
QQuickItem *item14 = findItem<QQuickItem>(content, "delegate", 13);
QVERIFY(item14);
- QCOMPARE(delegateVisible(item14), true);
+ QCOMPARE(isDelegateVisible(item14), true);
// the 15th item should be outside the end margin
QVERIFY(findItem<QQuickItem>(content, "delegate", 14) == nullptr);
// the first delegate should still be within the begin margin
listview->positionViewAtIndex(3, QQuickListView::Beginning);
- QCOMPARE(delegateVisible(item0), true);
+ QCOMPARE(isDelegateVisible(item0), true);
// the first delegate should now be outside the begin margin
listview->positionViewAtIndex(4, QQuickListView::Beginning);
- QCOMPARE(delegateVisible(item0), false);
+ QCOMPARE(isDelegateVisible(item0), false);
}
void tst_QQuickListView::negativeDisplayMargin()
@@ -8280,29 +8296,29 @@ void tst_QQuickListView::negativeDisplayMargin()
QQuickItem *item = findItem<QQuickItem>(content, "delegate", 0);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(content, "delegate", 7);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), true);
+ QCOMPARE(isDelegateVisible(item), true);
item = findItem<QQuickItem>(content, "delegate", 8);
QVERIFY(item);
- QCOMPARE(delegateVisible(item), false);
+ QCOMPARE(isDelegateVisible(item), false);
// Flick until contentY means that delegate8 should be visible
listview->setProperty("contentY", 500);
item = findItem<QQuickItem>(content, "delegate", 8);
QVERIFY(item);
- QTRY_COMPARE(delegateVisible(item), true);
+ QTRY_COMPARE(isDelegateVisible(item), true);
listview->setProperty("contentY", 1000);
QTRY_VERIFY((item = findItem<QQuickItem>(content, "delegate", 14)));
- QTRY_COMPARE(delegateVisible(item), true);
+ QTRY_COMPARE(isDelegateVisible(item), true);
listview->setProperty("contentY", 0);
QTRY_VERIFY(item = findItem<QQuickItem>(content, "delegate", 4));
- QTRY_COMPARE(delegateVisible(item), true);
+ QTRY_COMPARE(isDelegateVisible(item), true);
}
void tst_QQuickListView::highlightItemGeometryChanges()
@@ -8404,7 +8420,7 @@ void tst_QQuickListView::stickyPositioning()
listview->positionViewAtIndex(positionIndex, positionMode);
- foreach (const QPointF &offset, movement) {
+ for (const QPointF &offset : std::as_const(movement)) {
listview->setContentX(listview->contentX() + offset.x());
listview->setContentY(listview->contentY() + offset.y());
}
diff --git a/tests/auto/quick/qquicklistview2/CMakeLists.txt b/tests/auto/quick/qquicklistview2/CMakeLists.txt
index 6af00b60e8..faa86ce733 100644
--- a/tests/auto/quick/qquicklistview2/CMakeLists.txt
+++ b/tests/auto/quick/qquicklistview2/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicklistview2 LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -23,9 +29,10 @@ qt_internal_add_test(tst_qquicklistview2
TESTDATA ${test_data}
)
+qt_policy(SET QTP0001 NEW)
+
qt6_add_qml_module(tst_qquicklistview2
URI Test
- AUTO_RESOURCE_PREFIX
)
qt_internal_extend_target(tst_qquicklistview2 CONDITION ANDROID OR IOS
diff --git a/tests/auto/quick/qquicklistview2/data/areaZeroView.qml b/tests/auto/quick/qquicklistview2/data/areaZeroView.qml
new file mode 100644
index 0000000000..e0329f4e83
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/areaZeroView.qml
@@ -0,0 +1,22 @@
+import QtQuick
+
+Window {
+ width: 600
+ height: 600
+ visible: true
+ property int delegateCreationCounter: 0
+
+ ListView {
+ id: lv
+ anchors.fill: parent
+ model: 6000
+
+ delegate: Rectangle {
+ width: ListView.view.width
+ height: ListView.view.width / 6
+ color: "white"
+ border.width: 1
+ Component.onCompleted: ++delegateCreationCounter
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/bindOnHeaderAndFooterXPosition.qml b/tests/auto/quick/qquicklistview2/data/bindOnHeaderAndFooterXPosition.qml
new file mode 100644
index 0000000000..69431fb525
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/bindOnHeaderAndFooterXPosition.qml
@@ -0,0 +1,29 @@
+import QtQuick
+
+ListView {
+ width: 200
+ height: 300
+ spacing: 20
+ orientation: ListView.Vertical
+
+ header: Rectangle {
+ x: (ListView.view.width - width) / 2
+ color: 'tomato'
+ width: 50
+ height: 50
+ }
+
+ footer: Rectangle {
+ x: (ListView.view.width - width) / 2
+ color: 'lime'
+ width: 50
+ height: 50
+ }
+
+ model: 3
+ delegate: Text {
+ text: 'Foobar'
+ horizontalAlignment: Text.AlignHCenter
+ width: ListView.view.width
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/bindOnHeaderAndFooterYPosition.qml b/tests/auto/quick/qquicklistview2/data/bindOnHeaderAndFooterYPosition.qml
new file mode 100644
index 0000000000..a484a154a7
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/bindOnHeaderAndFooterYPosition.qml
@@ -0,0 +1,29 @@
+import QtQuick
+
+ListView {
+ width: 300
+ height: 200
+ spacing: 20
+ orientation: ListView.Horizontal
+
+ header: Rectangle {
+ y: (ListView.view.height - height) / 2
+ color: 'tomato'
+ width: 50
+ height: 50
+ }
+
+ footer: Rectangle {
+ y: (ListView.view.height - height) / 2
+ color: 'lime'
+ width: 50
+ height: 50
+ }
+
+ model: 3
+ delegate: Text {
+ text: 'Foobar'
+ verticalAlignment: Text.AlignVCenter
+ height: ListView.view.height
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/boundDelegateComponent.qml b/tests/auto/quick/qquicklistview2/data/boundDelegateComponent.qml
index 6fb84279b0..5e4de1a08d 100644
--- a/tests/auto/quick/qquicklistview2/data/boundDelegateComponent.qml
+++ b/tests/auto/quick/qquicklistview2/data/boundDelegateComponent.qml
@@ -6,6 +6,8 @@ Item {
objectName: "outer"
ListView {
id: listView
+ width: 100
+ height: 100
model: 1
property string foo: "foo"
delegate: Text {
@@ -16,6 +18,8 @@ Item {
ListView {
id: listView2
+ width: 100
+ height: 100
model: 1
delegate: Text {
required property int index
@@ -49,6 +53,8 @@ Item {
}
ListView {
+ width: 100
+ height: 100
id: innerListView
model: listModel
delegate: innerComponent
diff --git a/tests/auto/quick/qquicklistview2/data/changingOrientationWithListModel.qml b/tests/auto/quick/qquicklistview2/data/changingOrientationWithListModel.qml
new file mode 100644
index 0000000000..366b20b029
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/changingOrientationWithListModel.qml
@@ -0,0 +1,47 @@
+import QtQuick
+
+ListView {
+ id: root
+
+ function allDelegates(valueSelector) {
+ let sum = 0;
+ for (let i = 0; i < root.count; i++)
+ sum += valueSelector(root.itemAtIndex(i));
+ return sum;
+ }
+
+ readonly property bool isXReset: allDelegates(function(item) { return item?.x ?? 0; }) === 0
+ readonly property bool isYReset: allDelegates(function(item) { return item?.y ?? 0; }) === 0
+
+ width: 500
+ height: 500
+ delegate: Rectangle {
+ width: root.width
+ height: root.height
+ color: c
+ }
+ model: ListModel {
+ ListElement {
+ c: "red"
+ }
+ ListElement {
+ c: "green"
+ }
+ ListElement {
+ c: "blue"
+ }
+ ListElement {
+ c: "cyan"
+ }
+ ListElement {
+ c: "magenta"
+ }
+ ListElement {
+ c: "teal"
+ }
+ }
+ clip: true
+ orientation: ListView.Vertical
+ snapMode: ListView.SnapOneItem
+ highlightRangeMode: ListView.StrictlyEnforceRange
+} \ No newline at end of file
diff --git a/tests/auto/quick/qquicklistview2/data/changingOrientationWithObjectModel.qml b/tests/auto/quick/qquicklistview2/data/changingOrientationWithObjectModel.qml
new file mode 100644
index 0000000000..1d165a496e
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/changingOrientationWithObjectModel.qml
@@ -0,0 +1,54 @@
+import QtQuick
+
+ListView {
+ id: root
+
+ readonly property bool isXReset: red.x === 0 && green.x === 0 && blue.x === 0 && cyan.x === 0 && magenta.x === 0 && teal.x === 0
+
+ readonly property bool isYReset: red.y === 0 && green.y === 0 && blue.y === 0 && cyan.y === 0 && magenta.y === 0 && teal.y === 0
+
+ width: 500
+ height: 500
+ model: ObjectModel {
+ Rectangle {
+ id: red
+ width: root.width
+ height: root.height
+ color: "red"
+ }
+ Rectangle {
+ id: green
+ width: root.width
+ height: root.height
+ color: "green"
+ }
+ Rectangle {
+ id: blue
+ width: root.width
+ height: root.height
+ color: "blue"
+ }
+ Rectangle {
+ id: cyan
+ width: root.width
+ height: root.height
+ color: "cyan"
+ }
+ Rectangle {
+ id: magenta
+ width: root.width
+ height: root.height
+ color: "magenta"
+ }
+ Rectangle {
+ id: teal
+ width: root.width
+ height: root.height
+ color: "teal"
+ }
+ }
+ clip: true
+ orientation: ListView.Vertical
+ snapMode: ListView.SnapOneItem
+ highlightRangeMode: ListView.StrictlyEnforceRange
+} \ No newline at end of file
diff --git a/tests/auto/quick/qquicklistview2/data/delegateChooserEnumRole.qml b/tests/auto/quick/qquicklistview2/data/delegateChooserEnumRole.qml
index 655900d9de..66e92c5616 100644
--- a/tests/auto/quick/qquicklistview2/data/delegateChooserEnumRole.qml
+++ b/tests/auto/quick/qquicklistview2/data/delegateChooserEnumRole.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import Qt.labs.qmlmodels
diff --git a/tests/auto/quick/qquicklistview2/data/delegateContextHandling.qml b/tests/auto/quick/qquicklistview2/data/delegateContextHandling.qml
new file mode 100644
index 0000000000..4c513df905
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/delegateContextHandling.qml
@@ -0,0 +1,75 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Window
+
+Item {
+ id: win
+ height: 640
+ width: 480
+
+ property string currentModel: 'foo'
+
+ function toggle() : Item {
+ var ret = listView.itemAtIndex(0);
+ win.currentModel = win.currentModel === 'foo' ? 'bar' : 'foo'
+
+ switch (win.currentModel) {
+ case 'foo':
+ if (listView.model) {
+ listView.model.destroy()
+ }
+ listView.model = fooModelComponent.createObject(win)
+ break
+
+ case 'bar':
+ if (listView.model) {
+ listView.model.destroy()
+ }
+ listView.model = barModelComponent.createObject(win)
+ break
+ }
+
+ return ret;
+ }
+
+ Component {
+ id: fooModelComponent
+ ListModel {
+ ListElement { textValue: "foo1" }
+ }
+ }
+
+ Component {
+ id: barModelComponent
+ ListModel {
+ ListElement { textValue: "bar1" }
+ }
+ }
+
+ ListView {
+ states: [
+ State {
+ when: win.currentModel === 'bar'
+ PropertyChanges {
+ listView.section.property: 'sectionProp'
+ }
+ }
+ ]
+
+ id: listView
+ model: fooModelComponent.createObject(win)
+ anchors.fill: parent
+
+ section.delegate: Text {
+ required property string section
+ text: section
+ }
+
+ delegate: Text {
+ id: delg
+ text: delg.textValue
+ required property string textValue
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/delegateModelRefresh.qml b/tests/auto/quick/qquicklistview2/data/delegateModelRefresh.qml
index 7a2a017515..7a22f1a0f4 100644
--- a/tests/auto/quick/qquicklistview2/data/delegateModelRefresh.qml
+++ b/tests/auto/quick/qquicklistview2/data/delegateModelRefresh.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml b/tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml
index c94001eb43..f447f913e6 100644
--- a/tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml
+++ b/tests/auto/quick/qquicklistview2/data/delegateWithMouseArea.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquicklistview2/data/fetchMore.qml b/tests/auto/quick/qquicklistview2/data/fetchMore.qml
new file mode 100644
index 0000000000..4ce53e4d28
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/fetchMore.qml
@@ -0,0 +1,21 @@
+import QtQuick
+import org.qtproject.Test
+
+ListView {
+ id: listView
+ width: 300
+ height: 150
+ flickDeceleration: 10000
+
+ model: FetchMoreModel
+ delegate: Text {
+ height: 50
+ text: model.display
+ }
+
+ Text {
+ anchors.right: parent.right
+ text: "count " + listView.count
+ color: listView.moving ? "red" : "blue"
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/footerUpdate.qml b/tests/auto/quick/qquicklistview2/data/footerUpdate.qml
index 058b7a6276..c5729ad633 100644
--- a/tests/auto/quick/qquicklistview2/data/footerUpdate.qml
+++ b/tests/auto/quick/qquicklistview2/data/footerUpdate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquicklistview2/data/innerRequired.qml b/tests/auto/quick/qquicklistview2/data/innerRequired.qml
index b45181c0d2..c0862cec0d 100644
--- a/tests/auto/quick/qquicklistview2/data/innerRequired.qml
+++ b/tests/auto/quick/qquicklistview2/data/innerRequired.qml
@@ -25,6 +25,8 @@ Item {
ListView {
id: listView
model: myModel
+ width: 100
+ height: 100
delegate: AnotherDelegate {
age: model.age
text: model.noise
diff --git a/tests/auto/quick/qquicklistview2/data/maxXExtent.qml b/tests/auto/quick/qquicklistview2/data/maxXExtent.qml
index b9e88cfc9e..d72f825654 100644
--- a/tests/auto/quick/qquicklistview2/data/maxXExtent.qml
+++ b/tests/auto/quick/qquicklistview2/data/maxXExtent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquicklistview2/data/maxYExtent.qml b/tests/auto/quick/qquicklistview2/data/maxYExtent.qml
index 3be8948691..b8a1f0e12b 100644
--- a/tests/auto/quick/qquicklistview2/data/maxYExtent.qml
+++ b/tests/auto/quick/qquicklistview2/data/maxYExtent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquicklistview2/data/metaSequenceAsModel.qml b/tests/auto/quick/qquicklistview2/data/metaSequenceAsModel.qml
index 2b4651feaa..461450239f 100644
--- a/tests/auto/quick/qquicklistview2/data/metaSequenceAsModel.qml
+++ b/tests/auto/quick/qquicklistview2/data/metaSequenceAsModel.qml
@@ -2,6 +2,8 @@ import QtQuick
ListView {
id: view
+ width: 100
+ height: 100
property list<rect> rects: [ Qt.rect(1, 2, 3, 4), Qt.rect(5, 6, 7, 8) ]
property list<string> texts
diff --git a/tests/auto/quick/qquicklistview2/data/qtbug86744.qml b/tests/auto/quick/qquicklistview2/data/qtbug86744.qml
index c69a4f7ec6..d8b89a147d 100644
--- a/tests/auto/quick/qquicklistview2/data/qtbug86744.qml
+++ b/tests/auto/quick/qquicklistview2/data/qtbug86744.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQml.Models
diff --git a/tests/auto/quick/qquicklistview2/data/qtbug98315.qml b/tests/auto/quick/qquicklistview2/data/qtbug98315.qml
index bf2ed857b1..4035915c6d 100644
--- a/tests/auto/quick/qquicklistview2/data/qtbug98315.qml
+++ b/tests/auto/quick/qquicklistview2/data/qtbug98315.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQml.Models
diff --git a/tests/auto/quick/qquicklistview2/data/qtbug_92809.qml b/tests/auto/quick/qquicklistview2/data/qtbug_92809.qml
index a003b51335..7507e83f73 100644
--- a/tests/auto/quick/qquicklistview2/data/qtbug_92809.qml
+++ b/tests/auto/quick/qquicklistview2/data/qtbug_92809.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquicklistview2/data/sectionBoundComponent.qml b/tests/auto/quick/qquicklistview2/data/sectionBoundComponent.qml
new file mode 100644
index 0000000000..74ab6b59fa
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/sectionBoundComponent.qml
@@ -0,0 +1,14 @@
+pragma ComponentBehavior: Bound
+import QtQuick
+ListView {
+ id: view
+ width: 100
+ height: 100
+ model: ListModel {
+ ListElement { name: "foo"; age: 42 }
+ ListElement { name: "bar"; age: 13 }
+ }
+ delegate: Text { required property string name; text: name}
+ section.property: "age"
+ section.delegate: Rectangle { color: "gray"; width: view.width; height: 20; required property string section; Text {text: parent.section} }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/sectionGeometryChange.qml b/tests/auto/quick/qquicklistview2/data/sectionGeometryChange.qml
new file mode 100644
index 0000000000..6981af51ec
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/sectionGeometryChange.qml
@@ -0,0 +1,58 @@
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+ width: 640
+ height: 480
+ color: "#FFFFFF"
+ ListView {
+ objectName: "list"
+ anchors.fill: parent
+
+ delegate: Rectangle {
+ objectName: value
+ implicitHeight: text.implicitHeight
+ color: "#ff3"
+
+ Text {
+ id: text
+ width: parent.width
+ padding: 5
+ font.pixelSize: 20
+ text: value
+ }
+ }
+
+ section {
+ property: "section"
+
+ delegate: Rectangle {
+ objectName: section
+ width: parent.width
+ implicitHeight: text.implicitHeight
+ color: "#3ff"
+
+ Text {
+ id: text
+ width: parent.width
+ padding: 5
+ font.pixelSize: 20
+ text: section
+ wrapMode: Text.Wrap
+ }
+ }
+ }
+
+ model: ListModel {
+ ListElement { value: "Element1"; section: "Section1" }
+ ListElement { value: "Element2"; section: "Section1" }
+ ListElement { value: "Element3"; section: "Section1" }
+ ListElement { value: "Element4"; section: "Section2" }
+ ListElement { value: "Element5"; section: "Section2" }
+ ListElement { value: "Element6"; section: "Section2" }
+ ListElement { value: "Element7"; section: "Section2" }
+ ListElement { value: "Element8"; section: "Section3" }
+ ListElement { value: "Element9"; section: "Section3" }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/sectionsNoOverlap.qml b/tests/auto/quick/qquicklistview2/data/sectionsNoOverlap.qml
index d931bbb77c..3a22626032 100644
--- a/tests/auto/quick/qquicklistview2/data/sectionsNoOverlap.qml
+++ b/tests/auto/quick/qquicklistview2/data/sectionsNoOverlap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quick/qquicklistview2/data/urlListModel.qml b/tests/auto/quick/qquicklistview2/data/urlListModel.qml
index 94d5c37a56..38237234e0 100644
--- a/tests/auto/quick/qquicklistview2/data/urlListModel.qml
+++ b/tests/auto/quick/qquicklistview2/data/urlListModel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquicklistview2/data/viewportAvoidUndesiredMovementOnSetCurrentIndex.qml b/tests/auto/quick/qquicklistview2/data/viewportAvoidUndesiredMovementOnSetCurrentIndex.qml
new file mode 100644
index 0000000000..cd3865d55b
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/viewportAvoidUndesiredMovementOnSetCurrentIndex.qml
@@ -0,0 +1,47 @@
+import QtQuick
+
+Item {
+ id: root
+ width: 400
+ height: 600
+
+ ListView {
+ id: rawList
+ objectName: "list"
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 300
+
+ // full disabling of automatic viewport positioning
+ highlightFollowsCurrentItem: false
+ snapMode: ListView.NoSnap
+ highlightRangeMode: ListView.NoHighlightRange
+
+ delegate: Rectangle {
+ color: model.index === rawList.currentIndex ? "red" : "white"
+ border.color: rawList.currentItem === this ? "red" : "black"
+ height: 100
+ width: 400
+
+ Text {
+ anchors.centerIn: parent
+ text: model.index
+ font.pixelSize: 50
+ }
+
+ MouseArea {
+ // only for using this file to do manual testing
+ // autotest calls setCurrentIndex
+ anchors.fill: parent
+
+ onClicked: {
+ rawList.currentIndex = model.index;
+ }
+ }
+ }
+
+ model: 30
+ }
+
+}
diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
index 117ec0c94d..bdac2112b6 100644
--- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
+++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuick/qquickview.h>
@@ -52,6 +52,21 @@ private slots:
void pullbackSparseList();
void highlightWithBound();
+ void sectionIsCompatibleWithBoundComponents();
+ void sectionGeometryChange();
+ void areaZeroviewDoesNotNeedlesslyPopulateWholeModel();
+ void viewportAvoidUndesiredMovementOnSetCurrentIndex();
+
+ void delegateContextHandling();
+ void fetchMore_data();
+ void fetchMore();
+
+ void changingOrientationResetsPreviousAxisValues_data();
+ void changingOrientationResetsPreviousAxisValues();
+ void bindingDirectlyOnPositionInHeaderAndFooterDelegates_data();
+ void bindingDirectlyOnPositionInHeaderAndFooterDelegates();
+
+ void clearObjectListModel();
private:
void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
@@ -120,6 +135,33 @@ void tst_QQuickListView2::dragDelegateWithMouseArea_data()
}
}
+void tst_QQuickListView2::viewportAvoidUndesiredMovementOnSetCurrentIndex()
+{
+ QScopedPointer<QQuickView> window(createView());
+ QVERIFY(window);
+ window->setFlag(Qt::FramelessWindowHint);
+ window->setSource(testFileUrl("viewportAvoidUndesiredMovementOnSetCurrentIndex.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+ QVERIFY(window->rootObject());
+ QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list");
+ QVERIFY(listview);
+ listview->setCurrentIndex(2); // change current item
+ // partially obscure first item
+ QCOMPARE(listview->contentY(), 0);
+ listview->setContentY(50);
+ QTRY_COMPARE(listview->contentY(), 50);
+ listview->setCurrentIndex(0); // change current item back to first one
+ QVERIFY(QQuickTest::qWaitForPolish(listview));
+ // that shouldn't have caused any movement
+ QCOMPARE(listview->contentY(), 50);
+
+ // that even applies to the case where the current item is completely out of the viewport
+ listview->setCurrentIndex(25);
+ QVERIFY(QQuickTest::qWaitForPolish(listview));
+ QCOMPARE(listview->contentY(), 50);
+}
+
void tst_QQuickListView2::dragDelegateWithMouseArea()
{
QFETCH(QQuickItemView::LayoutDirection, layoutDirection);
@@ -332,7 +374,7 @@ void tst_QQuickListView2::boundDelegateComponent()
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QTest::ignoreMessage(
- QtWarningMsg, qPrintable(QLatin1String("%1:12: ReferenceError: index is not defined")
+ QtWarningMsg, qPrintable(QLatin1String("%1:14: ReferenceError: index is not defined")
.arg(url.toString())));
QScopedPointer<QObject> o(c.create());
@@ -363,7 +405,7 @@ void tst_QQuickListView2::boundDelegateComponent()
for (int i = 0; i < 3; ++i) {
QTest::ignoreMessage(
QtWarningMsg,
- qPrintable(QLatin1String("%1:47:21: ReferenceError: model is not defined")
+ qPrintable(QLatin1String("%1:51:21: ReferenceError: model is not defined")
.arg(url.toString())));
}
@@ -381,29 +423,31 @@ void tst_QQuickListView2::tapDelegateDuringFlicking_data()
{
QTest::addColumn<QByteArray>("qmlFile");
QTest::addColumn<QQuickFlickable::BoundsBehavior>("boundsBehavior");
+ QTest::addColumn<bool>("expectCanceled");
QTest::newRow("Button StopAtBounds") << QByteArray("buttonDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) << false;
QTest::newRow("MouseArea StopAtBounds") << QByteArray("mouseAreaDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) << true;
QTest::newRow("Button DragOverBounds") << QByteArray("buttonDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) << false;
QTest::newRow("MouseArea DragOverBounds") << QByteArray("mouseAreaDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) << true;
QTest::newRow("Button OvershootBounds") << QByteArray("buttonDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) << false;
QTest::newRow("MouseArea OvershootBounds") << QByteArray("mouseAreaDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) << true;
QTest::newRow("Button DragAndOvershootBounds") << QByteArray("buttonDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) << false;
QTest::newRow("MouseArea DragAndOvershootBounds") << QByteArray("mouseAreaDelegate.qml")
- << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) << true;
}
void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832
{
QFETCH(QByteArray, qmlFile);
QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior);
+ QFETCH(bool, expectCanceled);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl(qmlFile.constData())));
@@ -416,7 +460,16 @@ void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832
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);
+ // At first glance one would expect MouseArea and Button would be consistent about this;
+ // but in fact, before ListView takes over the grab via filtering,
+ // Button.pressed transitions to false because QQuickAbstractButtonPrivate::handleMove
+ // sees that the touchpoint has strayed outside its bounds, but it does NOT emit the canceled signal
+ if (expectCanceled) {
+ const QVariantList canceledDelegates = listView->property("canceledDelegates").toList();
+ QCOMPARE(canceledDelegates.size(), 1);
+ QCOMPARE(canceledDelegates.first(), 4);
+ }
+ QCOMPARE(listView->property("releasedDelegates").toList().size(), 0);
// press a delegate during flicking (at y > 501 + 100, so likely delegate 6)
QTest::touchEvent(&window, touchDevice.data()).press(0, {100, 100});
@@ -439,7 +492,7 @@ void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832
QVERIFY(lastPressed > 5);
QCOMPARE(releasedDelegates.last(), lastPressed);
QCOMPARE(tappedDelegates.last(), lastPressed);
- QCOMPARE(canceledDelegates.size(), 1); // only the first press was canceled, not the second
+ QCOMPARE(canceledDelegates.size(), expectCanceled ? 1 : 0); // only the first press was canceled, not the second
}
void tst_QQuickListView2::flickDuringFlicking_data()
@@ -698,122 +751,122 @@ void tst_QQuickListView2::wheelSnap_data()
QTest::newRow("vertical, top to bottom")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::NoHighlightRange << QPoint(20, -120) << 200.0 << 600.0 << 0.0 << 0.0
+ << QQuickItemView::NoHighlightRange << QPoint(20, -240) << 200.0 << 600.0 << 0.0 << 0.0
<< 0.0;
QTest::newRow("vertical, bottom to top")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
- << QQuickItemView::NoHighlightRange << QPoint(20, 120) << -400.0 << -800.0 << -200.0
+ << QQuickItemView::NoHighlightRange << QPoint(20, 240) << -400.0 << -800.0 << -200.0
<< 0.0 << 0.0;
QTest::newRow("horizontal, left to right")
<< QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::NoHighlightRange << QPoint(-120, 20) << 200.0 << 600.0 << 0.0 << 0.0
+ << QQuickItemView::NoHighlightRange << QPoint(-240, 20) << 200.0 << 600.0 << 0.0 << 0.0
<< 0.0;
QTest::newRow("horizontal, right to left")
<< QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
- << QQuickItemView::NoHighlightRange << QPoint(120, 20) << -400.0 << -800.0 << -200.0
+ << QQuickItemView::NoHighlightRange << QPoint(240, 20) << -400.0 << -800.0 << -200.0
<< 0.0 << 0.0;
QTest::newRow("vertical, top to bottom, enforce range")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::StrictlyEnforceRange << QPoint(20, -120) << 200.0 << 600.0 << 0.0
+ << QQuickItemView::StrictlyEnforceRange << QPoint(20, -240) << 200.0 << 600.0 << 0.0
<< 0.0 << 0.0;
QTest::newRow("vertical, bottom to top, enforce range")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
- << QQuickItemView::StrictlyEnforceRange << QPoint(20, 120) << -400.0 << -800.0 << -200.0
+ << QQuickItemView::StrictlyEnforceRange << QPoint(20, 240) << -400.0 << -800.0 << -200.0
<< 0.0 << 0.0;
QTest::newRow("horizontal, left to right, enforce range")
<< QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::StrictlyEnforceRange << QPoint(-120, 20) << 200.0 << 600.0 << 0.0
+ << QQuickItemView::StrictlyEnforceRange << QPoint(-240, 20) << 200.0 << 600.0 << 0.0
<< 0.0 << 0.0;
QTest::newRow("horizontal, right to left, enforce range")
<< QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
- << QQuickItemView::StrictlyEnforceRange << QPoint(120, 20) << -400.0 << -800.0 << -200.0
+ << QQuickItemView::StrictlyEnforceRange << QPoint(240, 20) << -400.0 << -800.0 << -200.0
<< 0.0 << 0.0;
QTest::newRow("vertical, top to bottom, apply range")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::ApplyRange << QPoint(20, -120) << 200.0 << 600.0 << 0.0 << 0.0
+ << QQuickItemView::ApplyRange << QPoint(20, -240) << 200.0 << 600.0 << 0.0 << 0.0
<< 0.0;
QTest::newRow("vertical, bottom to top, apply range")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
- << QQuickItemView::ApplyRange << QPoint(20, 120) << -400.0 << -800.0 << -200.0 << 0.0
+ << QQuickItemView::ApplyRange << QPoint(20, 240) << -400.0 << -800.0 << -200.0 << 0.0
<< 0.0;
QTest::newRow("horizontal, left to right, apply range")
<< QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::ApplyRange << QPoint(-120, 20) << 200.0 << 600.0 << 0.0 << 0.0
+ << QQuickItemView::ApplyRange << QPoint(-240, 20) << 200.0 << 600.0 << 0.0 << 0.0
<< 0.0;
QTest::newRow("horizontal, right to left, apply range")
<< QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
- << QQuickItemView::ApplyRange << QPoint(120, 20) << -400.0 << -800.0 << -200.0 << 0.0
+ << QQuickItemView::ApplyRange << QPoint(240, 20) << -400.0 << -800.0 << -200.0 << 0.0
<< 0.0;
QTest::newRow("vertical, top to bottom with highlightRange")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::NoHighlightRange << QPoint(20, -120) << 190.0 << 600.0 << 0.0 << 10.0
+ << QQuickItemView::NoHighlightRange << QPoint(20, -240) << 190.0 << 600.0 << 0.0 << 10.0
<< 210.0;
QTest::newRow("vertical, bottom to top with highlightRange")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
- << QQuickItemView::NoHighlightRange << QPoint(20, 120) << -390.0 << -800.0 << -200.0
+ << QQuickItemView::NoHighlightRange << QPoint(20, 240) << -390.0 << -800.0 << -200.0
<< 10.0 << 210.0;
QTest::newRow("horizontal, left to right with highlightRange")
<< QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::NoHighlightRange << QPoint(-120, 20) << 190.0 << 600.0 << 0.0 << 10.0
+ << QQuickItemView::NoHighlightRange << QPoint(-240, 20) << 190.0 << 600.0 << 0.0 << 10.0
<< 210.0;
QTest::newRow("horizontal, right to left with highlightRange")
<< QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
- << QQuickItemView::NoHighlightRange << QPoint(120, 20) << -390.0 << -800.0 << -200.0
+ << QQuickItemView::NoHighlightRange << QPoint(240, 20) << -390.0 << -800.0 << -200.0
<< 10.0 << 210.0;
QTest::newRow("vertical, top to bottom, enforce range with highlightRange")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::StrictlyEnforceRange << QPoint(20, -120) << 190.0 << 590.0 << -10.0
+ << QQuickItemView::StrictlyEnforceRange << QPoint(20, -240) << 190.0 << 590.0 << -10.0
<< 10.0 << 210.0;
QTest::newRow("vertical, bottom to top, enforce range with highlightRange")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
- << QQuickItemView::StrictlyEnforceRange << QPoint(20, 120) << -390.0 << -790.0 << -190.0
+ << QQuickItemView::StrictlyEnforceRange << QPoint(20, 240) << -390.0 << -790.0 << -190.0
<< 10.0 << 210.0;
QTest::newRow("horizontal, left to right, enforce range with highlightRange")
<< QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::StrictlyEnforceRange << QPoint(-120, 20) << 190.0 << 590.0 << -10.0
+ << QQuickItemView::StrictlyEnforceRange << QPoint(-240, 20) << 190.0 << 590.0 << -10.0
<< 10.0 << 210.0;
QTest::newRow("horizontal, right to left, enforce range with highlightRange")
<< QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
- << QQuickItemView::StrictlyEnforceRange << QPoint(120, 20) << -390.0 << -790.0 << -190.0
+ << QQuickItemView::StrictlyEnforceRange << QPoint(240, 20) << -390.0 << -790.0 << -190.0
<< 10.0 << 210.0;
QTest::newRow("vertical, top to bottom, apply range with highlightRange")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::ApplyRange << QPoint(20, -120) << 190.0 << 600.0 << 0.0 << 10.0
+ << QQuickItemView::ApplyRange << QPoint(20, -240) << 190.0 << 600.0 << 0.0 << 10.0
<< 210.0;
QTest::newRow("vertical, bottom to top, apply range with highlightRange")
<< QQuickListView::Vertical << Qt::LeftToRight << QQuickItemView::BottomToTop
- << QQuickItemView::ApplyRange << QPoint(20, 120) << -390.0 << -800.0 << -200.0 << 10.0
+ << QQuickItemView::ApplyRange << QPoint(20, 240) << -390.0 << -800.0 << -200.0 << 10.0
<< 210.0;
QTest::newRow("horizontal, left to right, apply range with highlightRange")
<< QQuickListView::Horizontal << Qt::LeftToRight << QQuickItemView::TopToBottom
- << QQuickItemView::ApplyRange << QPoint(-120, 20) << 190.0 << 600.0 << 0.0 << 10.0
+ << QQuickItemView::ApplyRange << QPoint(-240, 20) << 190.0 << 600.0 << 0.0 << 10.0
<< 210.0;
QTest::newRow("horizontal, right to left, apply range with highlightRange")
<< QQuickListView::Horizontal << Qt::RightToLeft << QQuickItemView::TopToBottom
- << QQuickItemView::ApplyRange << QPoint(120, 20) << -390.0 << -800.0 << -200.0 << 10.0
+ << QQuickItemView::ApplyRange << QPoint(240, 20) << -390.0 << -800.0 << -200.0 << 10.0
<< 210.0;
}
@@ -946,6 +999,257 @@ void tst_QQuickListView2::highlightWithBound()
QCOMPARE(highlight->objectName(), QStringLiteral("highlight"));
}
+void tst_QQuickListView2::sectionIsCompatibleWithBoundComponents()
+{
+ QTest::failOnWarning(".?");
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("sectionBoundComponent.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QQuickListView *listView = qobject_cast<QQuickListView *>(o.data());
+ QVERIFY(listView);
+ QTRY_COMPARE(listView->currentSection(), "42");
+}
+
+void tst_QQuickListView2::sectionGeometryChange()
+{
+ QScopedPointer<QQuickView> window(createView());
+ QTRY_VERIFY(window);
+ window->setSource(testFileUrl("sectionGeometryChange.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list");
+ QTRY_VERIFY(listview);
+
+ QQuickItem *contentItem = listview->contentItem();
+ QTRY_VERIFY(contentItem);
+ QVERIFY(QQuickTest::qWaitForPolish(listview));
+
+ QQuickItem *section1 = findItem<QQuickItem>(contentItem, "Section1");
+ QVERIFY(section1);
+ QQuickItem *element1 = findItem<QQuickItem>(contentItem, "Element1");
+ QVERIFY(element1);
+
+ QCOMPARE(element1->y(), section1->y() + section1->height());
+
+ // Update the height of the section delegate and verify that the next element is not overlapping
+ section1->setHeight(section1->height() + 10);
+ QTRY_COMPARE(element1->y(), section1->y() + section1->height());
+}
+
+void tst_QQuickListView2::areaZeroviewDoesNotNeedlesslyPopulateWholeModel()
+{
+ QTest::failOnWarning(QRegularExpression(".*"));
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("areaZeroView.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ std::unique_ptr<QObject> root(c.create());
+ QVERIFY(root);
+ auto delegateCreationCounter = [&]() {
+ return root->property("delegateCreationCounter").toInt();
+ };
+ // wait for onComplete to be settled
+ QTRY_VERIFY(delegateCreationCounter() != 0);
+ auto view = qobject_cast<QQuickListView *>(qmlContext(root.get())->objectForName("lv"));
+ QVERIFY(view);
+ QCOMPARE(view->count(), 6'000);
+ // we use 100, which is < 6000, but larger than the actual expected value
+ // that's to give the test some leniency in case the ListView implementation
+ // changes in the future to instantiate a few more items outside of the viewport
+ QVERIFY(delegateCreationCounter() < 100);
+}
+
+void tst_QQuickListView2::delegateContextHandling()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("delegateContextHandling.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ std::unique_ptr<QObject> o(c.create());
+ QVERIFY(o);
+
+ for (int i = 0; i < 10; ++i) {
+ QQuickItem *delegate = nullptr;
+ QMetaObject::invokeMethod(o.get(), "toggle", Q_RETURN_ARG(QQuickItem *, delegate));
+ QVERIFY(delegate);
+ }
+
+}
+
+class TestFetchMoreModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ QVariant data(const QModelIndex& index, int role) const override
+ {
+ if (role == Qt::DisplayRole)
+ return QString::number(index.row());
+ return {};
+ }
+
+ int columnCount(const QModelIndex&) const override { return 1; }
+
+ int rowCount(const QModelIndex& parent) const override
+ {
+ return parent.isValid() ? 0 : m_lines;
+ }
+
+ QModelIndex parent(const QModelIndex&) const override { return {}; }
+
+ bool canFetchMore(const QModelIndex &) const override { return true; }
+
+ void fetchMore(const QModelIndex & parent) override
+ {
+ if (Q_UNLIKELY(parent.isValid()))
+ return;
+ beginInsertRows(parent, m_lines, m_lines);
+ m_lines++;
+ endInsertRows();
+ }
+
+ int m_lines = 3;
+};
+
+void tst_QQuickListView2::fetchMore_data()
+{
+ QTest::addColumn<bool>("reuseItems");
+ QTest::addColumn<int>("cacheBuffer");
+
+ QTest::newRow("no reuseItems, default buffer") << false << -1;
+ QTest::newRow("reuseItems, default buffer") << true << -1;
+ QTest::newRow("no reuseItems, no buffer") << false << 0;
+ QTest::newRow("reuseItems, no buffer") << true << 0;
+ QTest::newRow("no reuseItems, buffer 100 px") << false << 100;
+ QTest::newRow("reuseItems, buffer 100 px") << true << 100;
+}
+
+void tst_QQuickListView2::fetchMore() // QTBUG-95107
+{
+ QFETCH(bool, reuseItems);
+ QFETCH(int, cacheBuffer);
+
+ TestFetchMoreModel model;
+ qmlRegisterSingletonInstance("org.qtproject.Test", 1, 0, "FetchMoreModel", &model);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("fetchMore.qml")));
+ auto *listView = qobject_cast<QQuickListView*>(window.rootObject());
+ QVERIFY(listView);
+ listView->setReuseItems(reuseItems);
+ if (cacheBuffer >= 0)
+ listView->setCacheBuffer(cacheBuffer);
+
+ for (int i = 0; i < 3; ++i) {
+ const int rowCount = listView->count();
+ if (lcTests().isDebugEnabled()) QTest::qWait(1000);
+ listView->flick(0, -5000);
+ QTRY_VERIFY(!listView->isMoving());
+ qCDebug(lcTests) << "after flick: contentY" << listView->contentY()
+ << "rows" << rowCount << "->" << listView->count();
+ QCOMPARE_GT(listView->count(), rowCount);
+ QCOMPARE_GE(model.m_lines, listView->count()); // fetchMore() was called
+ }
+}
+
+void tst_QQuickListView2::changingOrientationResetsPreviousAxisValues_data()
+{
+ QTest::addColumn<QByteArray>("sourceFile");
+ QTest::newRow("ObjectModel") << QByteArray("changingOrientationWithObjectModel.qml");
+ QTest::newRow("ListModel") << QByteArray("changingOrientationWithListModel.qml");
+}
+
+void tst_QQuickListView2::changingOrientationResetsPreviousAxisValues() // QTBUG-115696
+{
+ QFETCH(QByteArray, sourceFile);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl(QString::fromLatin1(sourceFile))));
+ auto *listView = qobject_cast<QQuickListView *>(window.rootObject());
+ QVERIFY(listView);
+
+ // Starts of with vertical orientation. X should be 0 for all delegates, but not Y.
+ QVERIFY(listView->property("isXReset").toBool());
+ QVERIFY(!listView->property("isYReset").toBool());
+
+ listView->setOrientation(QQuickListView::Orientation::Horizontal);
+
+ // Y should be 0 for all delegates, but not X.
+ QVERIFY(!listView->property("isXReset").toBool());
+ QVERIFY(listView->property("isYReset").toBool());
+
+ listView->setOrientation(QQuickListView::Orientation::Vertical);
+
+ // X should be 0 for all delegates, but not Y.
+ QVERIFY(listView->property("isXReset").toBool());
+ QVERIFY(!listView->property("isYReset").toBool());
+}
+
+void tst_QQuickListView2::bindingDirectlyOnPositionInHeaderAndFooterDelegates_data()
+{
+ QTest::addColumn<QByteArray>("sourceFile");
+ QTest::addColumn<qreal(QQuickItem::*)()const>("pos");
+ QTest::addColumn<qreal(QQuickItem::*)()const>("size");
+ QTest::newRow("XPosition") << QByteArray("bindOnHeaderAndFooterXPosition.qml") << &QQuickItem::x << &QQuickItem::width;
+ QTest::newRow("YPosition") << QByteArray("bindOnHeaderAndFooterYPosition.qml") << &QQuickItem::y << &QQuickItem::height;
+}
+void tst_QQuickListView2::bindingDirectlyOnPositionInHeaderAndFooterDelegates()
+{
+
+ typedef qreal (QQuickItem::*position_func_t)() const;
+ QFETCH(QByteArray, sourceFile);
+ QFETCH(position_func_t, pos);
+ QFETCH(position_func_t, size);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl(QString::fromLatin1(sourceFile))));
+ auto *listView = qobject_cast<QQuickListView *>(window.rootObject());
+ QVERIFY(listView);
+
+ const qreal widthOrHeight = (listView->*size)();
+
+ QCOMPARE((listView->headerItem()->*pos)(), (widthOrHeight - 50) / 2);
+ QCOMPARE((listView->footerItem()->*pos)(), (widthOrHeight - 50) / 2);
+
+ // Verify that the "regular" delegate items, don't honor x and y bindings.
+ // This should only be allowed for header and footer delegates.
+ for (int i = 0; i < listView->count(); ++i)
+ QCOMPARE((listView->itemAtIndex(i)->*pos)(), 0);
+}
+
+void tst_QQuickListView2::clearObjectListModel()
+{
+ QQmlEngine engine;
+ QQmlComponent delegate(&engine);
+
+ // Need one required property to trigger the incremental rebuilding of metaobjects.
+ delegate.setData("import QtQuick\nItem { required property int index }", QUrl());
+
+ QQuickListView list;
+ engine.setContextForObject(&list, engine.rootContext());
+ list.setDelegate(&delegate);
+ list.setWidth(640);
+ list.setHeight(480);
+
+ QScopedPointer modelObject(new QObject);
+
+ // Use a list that might also carry something non-QObject
+
+ list.setModel(QVariantList {
+ QVariant::fromValue(modelObject.data()),
+ QVariant::fromValue(modelObject.data())
+ });
+
+ QVERIFY(list.itemAtIndex(0));
+
+ modelObject.reset();
+
+ // list should not access dangling pointer from old model data anymore.
+ list.setModel(QVariantList());
+
+ QVERIFY(!list.itemAtIndex(0));
+}
+
QTEST_MAIN(tst_QQuickListView2)
#include "tst_qquicklistview2.moc"
diff --git a/tests/auto/quick/qquicklistview2/typerolemodel.cpp b/tests/auto/quick/qquicklistview2/typerolemodel.cpp
index 36b3d28446..94da87beda 100644
--- a/tests/auto/quick/qquicklistview2/typerolemodel.cpp
+++ b/tests/auto/quick/qquicklistview2/typerolemodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "typerolemodel.h"
diff --git a/tests/auto/quick/qquicklistview2/typerolemodel.h b/tests/auto/quick/qquicklistview2/typerolemodel.h
index 4db39c9059..f47400a88c 100644
--- a/tests/auto/quick/qquicklistview2/typerolemodel.h
+++ b/tests/auto/quick/qquicklistview2/typerolemodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QAbstractListModel>
#include <qqml.h>
diff --git a/tests/auto/quick/qquickloader/CMakeLists.txt b/tests/auto/quick/qquickloader/CMakeLists.txt
index 0ee885a284..f7192df326 100644
--- a/tests/auto/quick/qquickloader/CMakeLists.txt
+++ b/tests/auto/quick/qquickloader/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickloader Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickloader LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickloader/data/Rect120x60.qml b/tests/auto/quick/qquickloader/data/Rect120x60.qml
index fc9e447e69..6a0bb3d766 100644
--- a/tests/auto/quick/qquickloader/data/Rect120x60.qml
+++ b/tests/auto/quick/qquickloader/data/Rect120x60.qml
@@ -1,6 +1,10 @@
import QtQuick 2.0
Rectangle {
- width: 120
- height:60
+ property int base: 60
+ property int w: base*2
+ property int h: base
+ property int ignore: 0
+ width: w
+ height: h
}
diff --git a/tests/auto/quick/qquickloader/data/bindings.qml b/tests/auto/quick/qquickloader/data/bindings.qml
index 1f4cc92864..5ea4ba34f4 100644
--- a/tests/auto/quick/qquickloader/data/bindings.qml
+++ b/tests/auto/quick/qquickloader/data/bindings.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml b/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml
index e4e6088ebe..0d355f6c38 100644
--- a/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml
+++ b/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml
@@ -18,7 +18,7 @@ Item {
color: "red"
title: "red"
flags: Qt.Dialog
- onVisibilityChanged: console.log("visibility " + visibility)
+ onVisibilityChanged: (visibility) => console.log("visibility " + visibility)
onVisibleChanged: console.log("visible " + visible)
}
}
diff --git a/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml b/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml
index e3eb0f7e9a..c5d4dd1509 100644
--- a/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml
+++ b/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml
@@ -14,7 +14,7 @@ Item {
color: "red"
title: "red"
flags: Qt.Dialog
- onVisibilityChanged: console.log("visibility " + visibility)
+ onVisibilityChanged: (visibility) => console.log("visibility " + visibility)
onVisibleChanged: console.log("visible " + visible)
}
}
diff --git a/tests/auto/quick/qquickloader/data/noEngine.qml b/tests/auto/quick/qquickloader/data/noEngine.qml
index 19e619f32e..821ed30649 100644
--- a/tests/auto/quick/qquickloader/data/noEngine.qml
+++ b/tests/auto/quick/qquickloader/data/noEngine.qml
@@ -18,7 +18,7 @@ Item {
Timer {
onTriggered: {
root.a = true
- l.source = "loaded.qml"
+ l.source = "BlueRect.qml"
}
interval: 0
running: true
diff --git a/tests/auto/quick/qquickloader/data/parentErrors.qml b/tests/auto/quick/qquickloader/data/parentErrors.qml
index 97a4e202a6..f73213c433 100644
--- a/tests/auto/quick/qquickloader/data/parentErrors.qml
+++ b/tests/auto/quick/qquickloader/data/parentErrors.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickloader/data/rootContext.qml b/tests/auto/quick/qquickloader/data/rootContext.qml
index bd6fe82bcf..f73df2c53c 100644
--- a/tests/auto/quick/qquickloader/data/rootContext.qml
+++ b/tests/auto/quick/qquickloader/data/rootContext.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
index af7fd237f9..26dfd595cd 100644
--- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp
+++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QSignalSpy>
@@ -63,6 +64,7 @@ private slots:
void componentToUrl();
void anchoredLoader();
void sizeLoaderToItem();
+ void sizeItemToLoader_data();
void sizeItemToLoader();
void noResize();
void networkRequestUrl();
@@ -108,6 +110,7 @@ private slots:
void statusChangeOnlyEmittedOnce();
void setSourceAndCheckStatus();
+ void loadComponentWithStates();
void asyncLoaderRace();
void noEngine();
@@ -119,7 +122,7 @@ private slots:
Q_DECLARE_METATYPE(QList<QQmlError>)
tst_QQuickLoader::tst_QQuickLoader()
- : QQmlDataTest(QT_QMLTEST_DATADIR)
+ : QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::FailOnWarnings)
{
qmlRegisterType<SlowComponent>("LoaderTest", 1, 0, "SlowComponent");
qRegisterMetaType<QList<QQmlError>>();
@@ -376,8 +379,29 @@ void tst_QQuickLoader::sizeLoaderToItem()
QCOMPARE(rect->height(), 30.0);
}
+void tst_QQuickLoader::sizeItemToLoader_data()
+{
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<int>("value");
+ QTest::addColumn<bool>("atOnce");
+
+ QTest::addRow("none_atonce") << "ignore" << 42 << true;
+ QTest::addRow("width_atonce") << "w" << 42 << true;
+ QTest::addRow("height_atonce") << "h" << 42 << true;
+ QTest::addRow("both_atonce") << "both" << 42 << true;
+
+
+ QTest::addRow("none") << "ignore" << 42 << false;
+ QTest::addRow("width") << "w" << 42 << false;
+ QTest::addRow("height") << "h" << 42 << false;
+ QTest::addRow("both") << "both" << 42 << false;
+}
+
void tst_QQuickLoader::sizeItemToLoader()
{
+ QFETCH(QString, property);
+ QFETCH(int, value);
+ QFETCH(bool, atOnce);
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("/SizeToLoader.qml"));
QScopedPointer<QQuickLoader> loader(qobject_cast<QQuickLoader*>(component.create()));
@@ -387,17 +411,25 @@ void tst_QQuickLoader::sizeItemToLoader()
QQuickItem *rect = qobject_cast<QQuickItem*>(loader->item());
QVERIFY(rect);
+ rect->setProperty(property.toUtf8(), value);
QCOMPARE(rect->width(), 200.0);
QCOMPARE(rect->height(), 80.0);
// Check resize
QSizeChangeListener sizeListener(rect);
const QSizeF size(180, 30);
- loader->setSize(size);
+ if (atOnce) {
+ loader->setSize(size);
+ } else {
+ loader->setWidth(size.width());
+ loader->setHeight(size.height());
+ }
QVERIFY2(!sizeListener.isEmpty(), "There should be at least one signal about the size changed");
- for (const QSizeF sizeOnGeometryChanged : sizeListener) {
- // Check that we have the correct size on all signals
- QCOMPARE(sizeOnGeometryChanged, size);
+ if (atOnce) {
+ for (const QSizeF sizeOnGeometryChanged : sizeListener) {
+ // Check that we have the correct size on all signals
+ QCOMPARE(sizeOnGeometryChanged, size);
+ }
}
QCOMPARE(rect->width(), size.width());
QCOMPARE(rect->height(), size.height());
@@ -704,7 +736,7 @@ void tst_QQuickLoader::initialPropertyValues()
ThreadedTestHTTPServer server(dataDirectory());
- foreach (const QString &warning, expectedWarnings)
+ for (const QString &warning : std::as_const(expectedWarnings))
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(warning.toLatin1().constData()));
QQmlEngine engine;
@@ -750,8 +782,10 @@ void tst_QQuickLoader::initialPropertyValuesError_data()
QTest::newRow("nonexistent source url") << testFileUrl("initialPropertyValues.error.2.qml")
<< (QStringList() << QString(testFileUrl("NonexistentSourceComponent.qml").toString() + ": No such file or directory"));
- QTest::newRow("invalid source url") << testFileUrl("initialPropertyValues.error.3.qml")
- << (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString() + ":5:1: Expected token `:'"));
+ QTest::newRow("invalid source url")
+ << testFileUrl("initialPropertyValues.error.3.qml")
+ << (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString()
+ + ":4:5: Incomplete binding, expected token `:` or `{`"));
QTest::newRow("invalid initial property values object with invalid property access") << testFileUrl("initialPropertyValues.error.4.qml")
<< (QStringList() << QString(testFileUrl("initialPropertyValues.error.4.qml").toString() + ":7:5: QML Loader: setSource: value is not an object")
@@ -763,7 +797,7 @@ void tst_QQuickLoader::initialPropertyValuesError()
QFETCH(QUrl, qmlFile);
QFETCH(QStringList, expectedWarnings);
- foreach (const QString &warning, expectedWarnings)
+ for (const QString &warning : std::as_const(expectedWarnings))
QTest::ignoreMessage(QtWarningMsg, warning.toUtf8().constData());
QQmlEngine engine;
@@ -896,8 +930,10 @@ void tst_QQuickLoader::asynchronous_data()
QTest::newRow("Non-existent component") << testFileUrl("IDoNotExist.qml")
<< (QStringList() << QString(testFileUrl("IDoNotExist.qml").toString() + ": No such file or directory"));
- QTest::newRow("Invalid component") << testFileUrl("InvalidSourceComponent.qml")
- << (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString() + ":5:1: Expected token `:'"));
+ QTest::newRow("Invalid component")
+ << testFileUrl("InvalidSourceComponent.qml")
+ << (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString()
+ + ":4:5: Incomplete binding, expected token `:` or `{`"));
}
void tst_QQuickLoader::asynchronous()
@@ -918,7 +954,7 @@ void tst_QQuickLoader::asynchronous()
QQuickLoader *loader = root->findChild<QQuickLoader*>("loader");
QVERIFY(loader);
- foreach (const QString &warning, expectedWarnings)
+ for (const QString &warning : std::as_const(expectedWarnings))
QTest::ignoreMessage(QtWarningMsg, warning.toUtf8().constData());
QVERIFY(!loader->item());
@@ -1491,10 +1527,30 @@ void tst_QQuickLoader::setSourceAndCheckStatus()
QMetaObject::invokeMethod(loader, "load", Q_ARG(QVariant, QVariant::fromValue(QStringLiteral(""))));
QCOMPARE(loader->status(), QQuickLoader::Null);
- QMetaObject::invokeMethod(loader, "load", Q_ARG(QVariant, QVariant()));
+ QMetaObject::invokeMethod(loader, "load", Q_ARG(QVariant, QVariant(QUrl())));
QCOMPARE(loader->status(), QQuickLoader::Null);
}
+void tst_QQuickLoader::loadComponentWithStates()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(QByteArray("import QtQuick\n"
+ "Loader {\n"
+ "id: loader\n"
+ "property int createdObjCount: 0\n"
+ "states: [ State { when: true; PropertyChanges { target: loader; sourceComponent: myComp } } ]\n"
+ "Component { id: myComp; Item { Component.onCompleted: { ++createdObjCount } } }\n"
+ "}" )
+ , dataDirectoryUrl());
+ QScopedPointer<QQuickLoader> loader(qobject_cast<QQuickLoader*>(component.create()));
+ QTest::qWait(200);
+ QTRY_VERIFY(loader != nullptr);
+ QVERIFY(loader->item());
+ QCOMPARE(static_cast<QQuickItem*>(loader.data())->childItems().size(), 1);
+ QCOMPARE(loader->property("createdObjCount").toInt(), 1);
+}
+
void tst_QQuickLoader::asyncLoaderRace()
{
QQmlApplicationEngine engine;
diff --git a/tests/auto/quick/qquickmousearea/CMakeLists.txt b/tests/auto/quick/qquickmousearea/CMakeLists.txt
index d358dfb0e8..48262229fc 100644
--- a/tests/auto/quick/qquickmousearea/CMakeLists.txt
+++ b/tests/auto/quick/qquickmousearea/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickmousearea Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickmousearea LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickmousearea/data/clickThrough2.qml b/tests/auto/quick/qquickmousearea/data/clickThrough2.qml
index 2624108225..4e87b4eeb5 100644
--- a/tests/auto/quick/qquickmousearea/data/clickThrough2.qml
+++ b/tests/auto/quick/qquickmousearea/data/clickThrough2.qml
@@ -28,8 +28,8 @@ Item{
enabled: true
anchors.fill: parent
propagateComposedEvents: !noPropagation
- onClicked: mouse.accepted = !letThrough;
- onDoubleClicked: mouse.accepted = !letThrough;
- onPressAndHold: mouse.accepted = !letThrough;
+ onClicked: function (mouse) { mouse.accepted = !letThrough; }
+ onDoubleClicked: function (mouse) { mouse.accepted = !letThrough; }
+ onPressAndHold: function (mouse) { mouse.accepted = !letThrough; }
}
}
diff --git a/tests/auto/quick/qquickmousearea/data/containsMouseAndHoverDisabled.qml b/tests/auto/quick/qquickmousearea/data/containsMouseAndHoverDisabled.qml
new file mode 100644
index 0000000000..d98ef85c55
--- /dev/null
+++ b/tests/auto/quick/qquickmousearea/data/containsMouseAndHoverDisabled.qml
@@ -0,0 +1,15 @@
+import QtQuick
+
+Rectangle {
+ width: 200
+ height: 200
+ visible: true
+ MouseArea {
+ id: mouseArea
+ objectName: "mouseArea"
+ anchors.fill: parent
+ hoverEnabled: false
+ onPressed: function(mouse) { mouse.accepted = false }
+ }
+}
+
diff --git a/tests/auto/quick/qquickmousearea/data/cursorUpdating.qml b/tests/auto/quick/qquickmousearea/data/cursorUpdating.qml
new file mode 100644
index 0000000000..2d06543c78
--- /dev/null
+++ b/tests/auto/quick/qquickmousearea/data/cursorUpdating.qml
@@ -0,0 +1,71 @@
+import QtQuick
+
+Item {
+ width: 600
+ height: 600
+
+ ListModel {
+ id: cursorsModel
+ ListElement { cursorShape: Qt.ArrowCursor; text: "Arrow" }
+ ListElement { cursorShape: Qt.UpArrowCursor; text: "UpArrow" }
+ ListElement { cursorShape: Qt.CrossCursor; text: "Cross" }
+ ListElement { cursorShape: Qt.WaitCursor; text: "Wait" }
+ ListElement { cursorShape: Qt.IBeamCursor; text: "IBeam" }
+ ListElement { cursorShape: Qt.SizeVerCursor; text: "SizeVer" }
+ ListElement { cursorShape: Qt.SizeHorCursor; text: "SizeHor" }
+ ListElement { cursorShape: Qt.SizeBDiagCursor; text: "SizeBDiag" }
+ ListElement { cursorShape: Qt.SizeFDiagCursor; text: "SizeFDiag" }
+ ListElement { cursorShape: Qt.SizeAllCursor; text: "SizeAll" }
+ ListElement { cursorShape: Qt.BlankCursor; text: "Blank" }
+ ListElement { cursorShape: Qt.SplitVCursor; text: "SplitV" }
+ ListElement { cursorShape: Qt.SplitHCursor; text: "SplitH" }
+ ListElement { cursorShape: Qt.PointingHandCursor; text: "PointingHand" }
+ ListElement { cursorShape: Qt.ForbiddenCursor; text: "Forbidden" }
+ ListElement { cursorShape: Qt.WhatsThisCursor; text: "WhatsThis" }
+ ListElement { cursorShape: Qt.BusyCursor; text: "Busy" }
+ ListElement { cursorShape: Qt.OpenHandCursor; text: "OpenHand" }
+ ListElement { cursorShape: Qt.ClosedHandCursor; text: "ClosedHand" }
+ ListElement { cursorShape: Qt.DragCopyCursor; text: "DragCopy" }
+ ListElement { cursorShape: Qt.DragMoveCursor; text: "DragMove" }
+ ListElement { cursorShape: Qt.DragLinkCursor; text: "DragLink" }
+ }
+
+ Flickable {
+ anchors.fill: parent
+ contentHeight: flow.height
+ Flow {
+ id: flow
+ width: parent.width
+ Repeater {
+ model: cursorsModel
+ Rectangle {
+ id: root
+ color: "white"
+ border.width: 5
+ border.color: "black"
+
+ width: 200
+ height: 200
+
+ Text {
+ id: textItem
+ anchors.fill: parent
+ anchors.margins: parent.width * 0.1
+ text: model.text
+ fontSizeMode: Text.Fit
+ minimumPixelSize: 10; font.pixelSize: height
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: model.cursorShape
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickmousearea/data/hoverAfterPress.qml b/tests/auto/quick/qquickmousearea/data/hoverAfterPress.qml
index 69ec8fbd47..97512d1106 100644
--- a/tests/auto/quick/qquickmousearea/data/hoverAfterPress.qml
+++ b/tests/auto/quick/qquickmousearea/data/hoverAfterPress.qml
@@ -16,7 +16,7 @@ Item {
objectName: "mouseArea"
anchors.fill: parent
hoverEnabled: true
- onPressed: mouse.accepted = false
+ onPressed: function (mouse) { mouse.accepted = false }
//onContainsMouseChanged: print("containsMouse changed =", containsMouse)
Rectangle {
diff --git a/tests/auto/quick/qquickmousearea/data/ignoreBySource.qml b/tests/auto/quick/qquickmousearea/data/ignoreBySource.qml
index a53cbf7b1d..bb20a317f7 100644
--- a/tests/auto/quick/qquickmousearea/data/ignoreBySource.qml
+++ b/tests/auto/quick/qquickmousearea/data/ignoreBySource.qml
@@ -21,7 +21,7 @@ Item {
MouseArea {
id: ma
objectName: "mousearea"
- onPressed: {
+ onPressed: function (mouse) {
root.lastEventSource = mouse.source
if (mouse.source !== root.allowedSource)
mouse.accepted = false
diff --git a/tests/auto/quick/qquickmousearea/data/mask.qml b/tests/auto/quick/qquickmousearea/data/mask.qml
index 1e8ae7c98a..ab8ecbf32d 100644
--- a/tests/auto/quick/qquickmousearea/data/mask.qml
+++ b/tests/auto/quick/qquickmousearea/data/mask.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 Ford Motor Company
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.11
import Test 1.0
diff --git a/tests/auto/quick/qquickmousearea/data/moveAndReleaseWithoutPress.qml b/tests/auto/quick/qquickmousearea/data/moveAndReleaseWithoutPress.qml
index 6c68f0c7c8..4b7324a98a 100644
--- a/tests/auto/quick/qquickmousearea/data/moveAndReleaseWithoutPress.qml
+++ b/tests/auto/quick/qquickmousearea/data/moveAndReleaseWithoutPress.qml
@@ -7,7 +7,7 @@ MouseArea {
property bool hadMove: false
property bool hadRelease: false
- onPressed: mouse.accepted = false
+ onPressed: function (mouse) { mouse.accepted = false }
onPositionChanged: hadMove = true
onReleased: hadRelease = true
}
diff --git a/tests/auto/quick/qquickmousearea/data/qtbug34368.qml b/tests/auto/quick/qquickmousearea/data/qtbug34368.qml
index 42d8ddd3b6..96e72a8edc 100644
--- a/tests/auto/quick/qquickmousearea/data/qtbug34368.qml
+++ b/tests/auto/quick/qquickmousearea/data/qtbug34368.qml
@@ -12,7 +12,7 @@ Rectangle {
anchors.fill: parent
propagateComposedEvents: true
z: 1
- onClicked: {
+ onClicked: function (mouse) {
mouse.accepted = false;
clicksEnabled += 1;
//console.log("Upper click");
@@ -23,7 +23,7 @@ Rectangle {
propagateComposedEvents: true
z: 0
enabled: !disableLower
- onClicked: {
+ onClicked: function (mouse) {
mouse.accepted = false;
clicksDisabled += 1;
//console.log("Lower click");
diff --git a/tests/auto/quick/qquickmousearea/data/qtbug49100.qml b/tests/auto/quick/qquickmousearea/data/qtbug49100.qml
index 39b293c8fa..d3dfe64d94 100644
--- a/tests/auto/quick/qquickmousearea/data/qtbug49100.qml
+++ b/tests/auto/quick/qquickmousearea/data/qtbug49100.qml
@@ -8,7 +8,7 @@ ListView {
delegate: Text {
text: index + 1
height: 30
- width: parent.width
+ width: ListView.view.width
MouseArea {
anchors.fill: parent
}
diff --git a/tests/auto/quick/qquickmousearea/data/rejectEvent.qml b/tests/auto/quick/qquickmousearea/data/rejectEvent.qml
index 48b68ee845..03a075300a 100644
--- a/tests/auto/quick/qquickmousearea/data/rejectEvent.qml
+++ b/tests/auto/quick/qquickmousearea/data/rejectEvent.qml
@@ -23,7 +23,7 @@ Rectangle {
id: mouseRegion2
objectName: "mouseRegion2"
width: 120; height: 120
- onPressed: { root.mr2_pressed = true; mouse.accepted = false }
+ onPressed: function (mouse) { root.mr2_pressed = true; mouse.accepted = false }
onReleased: { root.mr2_released = true }
onCanceled: { root.mr2_canceled = true }
}
diff --git a/tests/auto/quick/qquickmousearea/data/updateMousePosOnResize.qml b/tests/auto/quick/qquickmousearea/data/updateMousePosOnResize.qml
index 55af864060..a20f752864 100644
--- a/tests/auto/quick/qquickmousearea/data/updateMousePosOnResize.qml
+++ b/tests/auto/quick/qquickmousearea/data/updateMousePosOnResize.qml
@@ -22,19 +22,19 @@ Rectangle {
property bool mouseMatchesPos: true
anchors.fill: brother
- onPressed: {
+ onPressed: function (mouse) {
if (mouse.x != mouseX || mouse.y != mouseY)
mouseMatchesPos = false
x1 = mouseX; y1 = mouseY
anchors.fill = parent
}
onPositionChanged: { emitPositionChanged = true }
- onMouseXChanged: {
+ onMouseXChanged: function (mouse) {
if (mouse.x != mouseX || mouse.y != mouseY)
mouseMatchesPos = false
x2 = mouseX; y2 = mouseY
}
- onMouseYChanged: {
+ onMouseYChanged: function (mouse) {
if (mouse.x != mouseX || mouse.y != mouseY)
mouseMatchesPos = false
x2 = mouseX; y2 = mouseY
diff --git a/tests/auto/quick/qquickmousearea/data/wheel.qml b/tests/auto/quick/qquickmousearea/data/wheel.qml
index 3e0c0c2a48..6cd367b22f 100644
--- a/tests/auto/quick/qquickmousearea/data/wheel.qml
+++ b/tests/auto/quick/qquickmousearea/data/wheel.qml
@@ -14,7 +14,7 @@ Rectangle {
MouseArea {
anchors.fill: parent
- onWheel: {
+ onWheel: function (wheel) {
root.angleDeltaY = wheel.angleDelta.y;
root.mouseX = wheel.x;
root.mouseY = wheel.y;
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index fd2b442a63..37d8c910f5 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
@@ -23,6 +23,11 @@
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+static bool isPlatformWayland()
+{
+ return !QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive);
+}
+
class CircleMask : public QObject
{
Q_OBJECT
@@ -95,6 +100,7 @@ private slots:
void pressedCanceledOnWindowDeactivate();
void doubleClick_data() { acceptedButton_data(); }
void doubleClick();
+ void doubleTap();
void clickTwice_data() { acceptedButton_data(); }
void clickTwice();
void invalidClick_data() { rejectedButton_data(); }
@@ -120,6 +126,7 @@ private slots:
void changeAxis();
#if QT_CONFIG(cursor)
void cursorShape();
+ void cursorUpdating();
#endif
void moveAndReleaseWithoutPress();
void nestedStopAtBounds();
@@ -139,6 +146,7 @@ private slots:
void negativeZStackingOrder();
void containsMouseAndVisibility();
void containsMouseAndVisibilityMasked();
+ void containsMouseAndHoverDisabled();
void doubleClickToHide();
void releaseFirstTouchAfterSecond();
#if QT_CONFIG(tabletevent)
@@ -933,6 +941,61 @@ void tst_QQuickMouseArea::doubleClick()
QCOMPARE(window.rootObject()->property("released").toInt(), 2);
}
+void tst_QQuickMouseArea::doubleTap() // QTBUG-112434
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("doubleclick.qml")));
+
+ QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mousearea");
+ QVERIFY(mouseArea);
+ QPoint p1 = mouseArea->mapToScene(mouseArea->boundingRect().center()).toPoint();
+
+ QTest::touchEvent(&window, device).press(0, p1);
+ QQuickTouchUtils::flush(&window);
+ QTest::touchEvent(&window, device).release(0, p1);
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(window.rootObject()->property("released").toInt(), 1);
+ QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
+
+ p1 += QPoint(1, -1); // movement less than QPlatformTheme::TouchDoubleTapDistance
+ QTest::touchEvent(&window, device).press(1, p1); // touchpoint ID is different the second time
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(mouseArea->isPressed(), true);
+ // at this time QQuickDeliveryAgentPrivate::deliverTouchAsMouse() synthesizes the double-click event
+ QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
+
+ QTest::touchEvent(&window, device).release(1, p1);
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(window.rootObject()->property("released").toInt(), 2);
+ QCOMPARE(mouseArea->isPressed(), false);
+ QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
+
+ // now tap with two fingers simultaneously: only one of them generates synth-mouse
+ QPoint p2 = p1 + QPoint(50, 5);
+ QTest::touchEvent(&window, device).press(2, p1).press(3, p2);
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(mouseArea->isPressed(), true);
+ QTest::touchEvent(&window, device).release(2, p1).release(3, p2);
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(window.rootObject()->property("released").toInt(), 3);
+ QCOMPARE(window.rootObject()->property("clicked").toInt(), 2);
+ QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
+ QCOMPARE(mouseArea->isPressed(), false);
+
+ // tap with two fingers simultaneously again: get another double-click from one point
+ p1 -= QPoint(1, -1);
+ p2 += QPoint(1, -1);
+ QTest::touchEvent(&window, device).press(4, p1).press(5, p2);
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(mouseArea->isPressed(), true);
+ QTest::touchEvent(&window, device).release(4, p1).release(5, p2);
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(window.rootObject()->property("released").toInt(), 4);
+ QCOMPARE(window.rootObject()->property("clicked").toInt(), 2);
+ QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 2);
+ QCOMPARE(mouseArea->isPressed(), false); // make sure it doesn't get stuck
+}
+
// QTBUG-14832
void tst_QQuickMouseArea::clickTwice()
{
@@ -1563,11 +1626,11 @@ void tst_QQuickMouseArea::disableParentOnPress() // QTBUG-39806 and QTBUG-103788
QQuickTest::pointerPress(device, &window, 0, p);
QTRY_COMPARE(parentEnabledSpy.size(), 1);
- QCOMPARE(root->isEnabled(), false);
- QCOMPARE(mouseArea->isEnabled(), true); // enabled is independent, unfortunately (inverse of QTBUG-38364)
- QCOMPARE(QQuickItemPrivate::get(mouseArea)->effectiveEnable, false);
+ QVERIFY(!root->isEnabled());
+ QVERIFY(mouseArea->isEnabled()); // enabled is independent, unfortunately (inverse of QTBUG-38364)
+ QVERIFY(!QQuickItemPrivate::get(mouseArea)->effectiveEnable);
// bug fix: it knows it got effectively disabled, so now it's no longer pressed
- QCOMPARE(mouseArea->isPressed(), false);
+ QVERIFY(!mouseArea->isPressed());
QCOMPARE(canceledSpy.size(), 1); // ...because the press was canceled
QCOMPARE(pressedChangedSpy.size(), 2); // kerchunk
QQuickTest::pointerRelease(device, &window, 0, p);
@@ -1575,9 +1638,9 @@ void tst_QQuickMouseArea::disableParentOnPress() // QTBUG-39806 and QTBUG-103788
// now re-enable it and try again
root->setEnabled(true);
QQuickTest::pointerPress(device, &window, 0, p);
- QTRY_COMPARE(root->isEnabled(), false);
- QCOMPARE(QQuickItemPrivate::get(mouseArea)->effectiveEnable, false);
- QCOMPARE(mouseArea->isPressed(), false);
+ QTRY_VERIFY(!root->isEnabled());
+ QVERIFY(!QQuickItemPrivate::get(mouseArea)->effectiveEnable);
+ QVERIFY(!mouseArea->isPressed());
QCOMPARE(canceledSpy.size(), 2);
QCOMPARE(pressedChangedSpy.size(), 4);
QQuickTest::pointerRelease(device, &window, 0, p);
@@ -1844,6 +1907,38 @@ void tst_QQuickMouseArea::cursorShape()
QCOMPARE(mouseArea->cursor().shape(), Qt::WaitCursor);
QCOMPARE(spy.size(), 2);
}
+
+void tst_QQuickMouseArea::cursorUpdating()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("cursorUpdating.qml")));
+ QQuickItem *root = window.rootObject();
+ QVERIFY(root);
+ QQuickFlickable *flickable = root->findChild<QQuickFlickable*>();
+ QVERIFY(flickable);
+ QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(root);
+ QVERIFY(rootPrivate->subtreeCursorEnabled);
+
+ QTest::mouseMove(&window, QPoint(40, 40));
+ QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
+
+ QTest::mouseMove(&window, QPoint(240, 40));
+ QCOMPARE(window.cursor().shape(), Qt::UpArrowCursor);
+
+ if (isPlatformWayland())
+ QSKIP("Wayland: QCursor::setPos() doesn't work.");
+
+ // QTBUG-53987: with the cursor physically hovering, use wheel to
+ // position a different item that requests a different cursor
+ const QPoint p(240, 40);
+ const QPoint pg = window.mapToGlobal(p);
+ QCursor::setPos(pg);
+ QWheelEvent wheelEvent(p, pg, QPoint(60, -400), QPoint(0, -600),
+ Qt::NoButton, Qt::ControlModifier, Qt::NoScrollPhase, false);
+ QGuiApplication::sendEvent(&window, &wheelEvent);
+ QTRY_VERIFY(flickable->contentY() > 300);
+ QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
+}
#endif
void tst_QQuickMouseArea::moveAndReleaseWithoutPress()
@@ -2508,6 +2603,20 @@ void tst_QQuickMouseArea::containsMouseAndVisibilityMasked()
QTRY_VERIFY(!mouseArea2->hovered());
}
+// QTBUG-110594
+void tst_QQuickMouseArea::containsMouseAndHoverDisabled()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("containsMouseAndHoverDisabled.qml")));
+
+ QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
+ QVERIFY(mouseArea != nullptr);
+ QVERIFY(!mouseArea->hoverEnabled());
+
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
+ QTRY_VERIFY(!mouseArea->hovered());
+}
+
// QTBUG-35995 and QTBUG-102158
void tst_QQuickMouseArea::doubleClickToHide()
{
diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
index 2488eff270..c79f4b6a29 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
+++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
@@ -4,3 +4,7 @@ ubuntu-22.04
[nested]
ubuntu-20.04
ubuntu-22.04
+
+# QTBUG-118065
+[mouseGestureStarted]
+opensuse-leap
diff --git a/tests/auto/quick/qquickmultipointtoucharea/CMakeLists.txt b/tests/auto/quick/qquickmultipointtoucharea/CMakeLists.txt
index 38e91d5683..277df556cc 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/CMakeLists.txt
+++ b/tests/auto/quick/qquickmultipointtoucharea/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickmultipointtoucharea Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickmultipointtoucharea LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml b/tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml
index e108003bca..493257ac7e 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/cancel.qml
@@ -30,12 +30,12 @@ MultiPointTouchArea {
property int touchCount: 0
property bool touchUpdatedHandled: false
- onPressed: { touchPointPressCount = touchPoints.length }
- onUpdated: { touchPointUpdateCount = touchPoints.length }
- onReleased: { touchPointReleaseCount = touchPoints.length }
- onCanceled: { touchPointCancelCount = touchPoints.length }
- onTouchUpdated: {
- touchCount = touchPoints.length
+ onPressed: (points) => { touchPointPressCount = points.length }
+ onUpdated: (points) => { touchPointUpdateCount = points.length }
+ onReleased: (points) => { touchPointReleaseCount = v.length }
+ onCanceled: (points) => { touchPointCancelCount = points.length }
+ onTouchUpdated: (points) => {
+ touchCount = points.length
touchUpdatedHandled = true
}
}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml b/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml
index 32733613b3..e03ad43816 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml
@@ -32,7 +32,7 @@ Rectangle {
anchors.fill: parent
minimumTouchPoints: 2
maximumTouchPoints: 2
- onGestureStarted: {
+ onGestureStarted: (gesture) => {
if ((Math.abs(point2.x - point2.startX) > gesture.dragThreshold/2) &&
(Math.abs(point1.x - point1.startX) > gesture.dragThreshold/2)) {
gesture.grab()
@@ -43,8 +43,8 @@ Rectangle {
TouchPoint { id: point2; objectName: "point2" }
]
- onCanceled: root.cancelCount = touchPoints.length
- onTouchUpdated: root.touchCount = touchPoints.length
+ onCanceled: (touchPoints) => root.cancelCount = touchPoints.length
+ onTouchUpdated: (touchPoints) => root.touchCount = touchPoints.length
Text {
text: "â‘ "
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/mouse.qml b/tests/auto/quick/qquickmultipointtoucharea/data/mouse.qml
index b0410dac4a..6b9dd5b7c7 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/data/mouse.qml
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/mouse.qml
@@ -17,10 +17,10 @@ MultiPointTouchArea {
TouchPoint { objectName: "point2" }
]
- onPressed: { touchCount = touchPoints.length }
- onTouchUpdated: { touchCount = touchPoints.length }
- onCanceled: { cancelCount = touchPoints.length }
- onGestureStarted: {
+ onPressed: (points) => { touchCount = points.length }
+ onTouchUpdated: (points) => { touchCount = points.length }
+ onCanceled: (points) => { cancelCount = points.length }
+ onGestureStarted: (gesture) => {
gestureStartedX = gesture.touchPoints[0].startX
gestureStartedY = gesture.touchPoints[0].startY
if (grabGesture)
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/nested.qml b/tests/auto/quick/qquickmultipointtoucharea/data/nested.qml
index 8ab10a6926..dca1ffa07b 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/data/nested.qml
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/nested.qml
@@ -66,7 +66,7 @@ MultiPointTouchArea {
anchors.fill: parent
minimumTouchPoints: 3
maximumTouchPoints: 3
- onGestureStarted: if (grabInnerArea) gesture.grab()
+ onGestureStarted: (gesture) => { if (grabInnerArea) gesture.grab() }
touchPoints: [
TouchPoint { id: point21; objectName: "point21" },
TouchPoint { id: point22; objectName: "point22" },
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/nestedTouchPosCheck.qml b/tests/auto/quick/qquickmultipointtoucharea/data/nestedTouchPosCheck.qml
new file mode 100644
index 0000000000..700f06a3ac
--- /dev/null
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/nestedTouchPosCheck.qml
@@ -0,0 +1,52 @@
+import QtQuick
+import QtTest
+
+Item {
+ width: 300; height: 200
+ id: topLevelItem
+ MultiPointTouchArea {
+ objectName: "topMPTA"
+ anchors.fill: parent
+ Column {
+ width: parent.width
+ height: parent.height
+
+ Rectangle {
+ width: parent.width
+ height: 100
+ color: "green"
+ }
+ Rectangle {
+ id: rect
+ width: parent.width
+ height: 600
+ color: "red"
+
+ MultiPointTouchArea {
+
+ property var xPressed: 0
+ property var yPressed: 0
+ property var xReleased: 0
+ property var yReleased: 0
+
+ objectName: "bottomMPTA"
+ anchors.fill: parent
+ onPressed: (touchPoints) => {
+ for (let tp in touchPoints) {
+ let touch = touchPoints[tp]
+ xPressed = touch.x
+ yPressed = touch.y
+ }
+ }
+ onReleased: (touchPoints) => {
+ for (let tp in touchPoints) {
+ let touch = touchPoints[tp]
+ xReleased = touch.x
+ yReleased = touch.y
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml b/tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml
index 027f90c7f4..4130bc8dde 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml
@@ -9,7 +9,7 @@ Rectangle {
height: 160
minimumTouchPoints: 2
maximumTouchPoints: 2
- onGestureStarted: gesture.grab()
+ onGestureStarted: (gesture) => gesture.grab()
touchPoints: [
TouchPoint { id: point11; objectName: "point11" },
TouchPoint { id: point12; objectName: "point12" }
@@ -34,7 +34,7 @@ Rectangle {
y: 160
minimumTouchPoints: 3
maximumTouchPoints: 3
- onGestureStarted: gesture.grab()
+ onGestureStarted: (gesture) => gesture.grab()
touchPoints: [
TouchPoint { id: point21; objectName: "point21" },
TouchPoint { id: point22; objectName: "point22" },
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/signalTest.qml b/tests/auto/quick/qquickmultipointtoucharea/data/signalTest.qml
index 54b160c182..9ba3029193 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/data/signalTest.qml
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/signalTest.qml
@@ -20,11 +20,17 @@ MultiPointTouchArea {
maximumTouchPoints: 5
- onPressed: { touchPointPressCount = touchPoints.length }
- onUpdated: { touchPointUpdateCount = touchPoints.length }
+ // recommended syntax for a signal handler
+ onPressed: (points) => { touchPointPressCount = points.length }
+
+ // one with "touchPoints" being the signal argument rather than the property
+ onUpdated: (touchPoints) => { touchPointUpdateCount = touchPoints.length }
+
+ // one without the formal parameter, to test that it still works (with a warning)
onReleased: { touchPointReleaseCount = touchPoints.length }
- onTouchUpdated: {
- touchCount = touchPoints.length
+
+ onTouchUpdated: (points) => {
+ touchCount = points.length
touchUpdatedHandled = true
}
}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/touchOverMouseArea.qml b/tests/auto/quick/qquickmultipointtoucharea/data/touchOverMouseArea.qml
new file mode 100644
index 0000000000..ffe3751ec3
--- /dev/null
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/touchOverMouseArea.qml
@@ -0,0 +1,21 @@
+
+import QtQuick
+
+Rectangle {
+ x: 20
+ y: 20
+ width: 300
+ height: 200
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ }
+
+ MultiPointTouchArea {
+ id: touchArea
+ anchors.fill: parent
+ enabled: false
+ }
+}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/transformedMultiPointTouchArea.qml b/tests/auto/quick/qquickmultipointtoucharea/data/transformedMultiPointTouchArea.qml
index 296bf7996f..02985581e0 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/data/transformedMultiPointTouchArea.qml
+++ b/tests/auto/quick/qquickmultipointtoucharea/data/transformedMultiPointTouchArea.qml
@@ -19,8 +19,8 @@ Rectangle {
property int pointCount: 0
- onPressed: pointCount = touchPoints.length;
- onTouchUpdated: pointCount = touchPoints.length;
+ onPressed: (points) => pointCount = points.length;
+ onTouchUpdated: (points) => pointCount = points.length;
}
}
}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
index 509465b174..b233ed0232 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
+++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
@@ -19,6 +19,8 @@
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+using namespace Qt::StringLiterals;
+
class tst_QQuickMultiPointTouchArea : public QQmlDataTest
{
Q_OBJECT
@@ -34,6 +36,7 @@ private slots:
void reuse();
void nonOverlapping();
void nested();
+ void nestedTouchPosCheck();
void inFlickable();
void inFlickable2();
void inFlickableWithPressDelay();
@@ -50,6 +53,7 @@ private slots:
void stationaryTouchWithChangingPressure();
void touchFiltering();
void nestedPinchAreaMouse();
+ void disabledIgnoresHover();
private:
QQuickView *createAndShowView(const QString &file);
@@ -73,6 +77,12 @@ void tst_QQuickMultiPointTouchArea::properties()
void tst_QQuickMultiPointTouchArea::signalTest()
{
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QTest::ignoreMessage(QtWarningMsg, QString(testFileUrl("signalTest.qml").toString() +
+ u":30:5 Parameter \"touchPoints\" is not declared. Injection of parameters into signal handlers "
+ "is deprecated. Use JavaScript functions with formal parameters instead."_s).toLatin1().constData());
+#endif
+
QScopedPointer<QQuickView> window(createAndShowView("signalTest.qml"));
QVERIFY(window->rootObject() != nullptr);
@@ -551,6 +561,30 @@ void tst_QQuickMultiPointTouchArea::nested()
QQuickTouchUtils::flush(window.data());
}
+
+void tst_QQuickMultiPointTouchArea::nestedTouchPosCheck()
+{
+ QScopedPointer<QQuickView> window(createAndShowView("nestedTouchPosCheck.qml"));
+ QVERIFY(window->rootObject() != nullptr);
+
+ auto *bottomMPTA = window->rootObject()->findChild<QQuickMultiPointTouchArea *>("bottomMPTA");
+ QVERIFY(bottomMPTA != nullptr);
+
+ QTest::QTouchEventSequence sequence = QTest::touchEvent(window.data(), device);
+
+ sequence.press(0, QPoint(10, 110)).commit();
+ QQuickTouchUtils::flush(window.data());
+
+ sequence.release(0, QPoint(10, 110)).commit();
+ QQuickTouchUtils::flush(window.data());
+
+ QCOMPARE(bottomMPTA->property("xPressed").toInt(), 10);
+ QCOMPARE(bottomMPTA->property("yPressed").toInt(), 10);
+ QCOMPARE(bottomMPTA->property("xReleased").toInt(), 10);
+ QCOMPARE(bottomMPTA->property("yReleased").toInt(), 10);
+
+}
+
void tst_QQuickMultiPointTouchArea::inFlickable()
{
QScopedPointer<QQuickView> window(createAndShowView("inFlickable.qml"));
@@ -571,7 +605,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
QPoint p1(20,100);
QPoint p2(40,100);
- // moving one point vertically
+ // moving one point vertically: flickable gets the grab
QTest::touchEvent(window.data(), device).press(0, p1);
QQuickTouchUtils::flush(window.data());
@@ -595,10 +629,14 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
QTRY_VERIFY(!flickable->isMoving());
- // moving two points vertically
+ // moving two points vertically: MPTAs handle them, Flickable ignores multi-touch.
+ // The stray mouse events simulate OS-level synth-from-touch, and should not interfere.
p1 = QPoint(20,100);
QTest::touchEvent(window.data(), device).press(0, p1).press(1, p2);
- QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
+ QWindowSystemInterface::handleMouseEvent(window.data(), device, p1, window->mapToGlobal(p1),
+ Qt::LeftButton, Qt::LeftButton, QEvent::MouseButtonPress,
+ Qt::NoModifier, Qt::MouseEventSynthesizedBySystem);
+ qApp->processEvents();
QQuickTouchUtils::flush(window.data());
QCOMPARE(point11->pressed(), true);
@@ -611,20 +649,26 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
QTest::qWait(250);
p1 += delta; p2 += delta;
QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2);
- QTest::mouseMove(window.data(), p1);
+ QWindowSystemInterface::handleMouseEvent(window.data(), device, p1, window->mapToGlobal(p1),
+ Qt::LeftButton, Qt::NoButton, QEvent::MouseMove,
+ Qt::NoModifier, Qt::MouseEventSynthesizedBySystem);
+ qApp->processEvents();
QQuickTouchUtils::flush(window.data());
qCDebug(lcTests, "after drags %d to %d,%d and %d,%d contentY is %lf",
i, p1.x(), p1.y(), p2.x(), p2.y(), flickable->contentY());
}
- QVERIFY(flickable->contentY() < 0);
- QCOMPARE(point11->pressed(), false);
- QCOMPARE(point12->pressed(), false);
- QCOMPARE(window->rootObject()->property("cancelCount").toInt(), 2);
- QCOMPARE(window->rootObject()->property("touchCount").toInt(), 0);
+ QCOMPARE(flickable->contentY(), 0);
+ QCOMPARE(point11->pressed(), true);
+ QCOMPARE(point12->pressed(), true);
+ QCOMPARE(window->rootObject()->property("cancelCount").toInt(), 0);
+ QCOMPARE(window->rootObject()->property("touchCount").toInt(), 2);
QTest::touchEvent(window.data(), device).release(0, p1).release(1, p2);
- QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
+ QWindowSystemInterface::handleMouseEvent(window.data(), device, p1, window->mapToGlobal(p1),
+ Qt::NoButton, Qt::LeftButton, QEvent::MouseButtonRelease,
+ Qt::NoModifier, Qt::MouseEventSynthesizedBySystem);
+ qApp->processEvents();
QQuickTouchUtils::flush(window.data());
QTRY_VERIFY(!flickable->isMoving());
@@ -634,10 +678,15 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
p2 = QPoint(40,100);
QTest::touchEvent(window.data(), device).press(0, p1).press(1, p2);
QQuickTouchUtils::flush(window.data());
+ QCOMPARE(point11->pressed(), true);
+ QCOMPARE(point12->pressed(), true);
// ensure that mouse events do not fall through to the Flickable
mpta->setMaximumTouchPoints(3);
mpta->setAcceptedMouseButtons(Qt::LeftButton);
- QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
+ QWindowSystemInterface::handleMouseEvent(window.data(), device, p1, window->mapToGlobal(p1),
+ Qt::LeftButton, Qt::LeftButton, QEvent::MouseButtonPress,
+ Qt::NoModifier, Qt::MouseEventSynthesizedBySystem);
+ qApp->processEvents();
QCOMPARE(point11->pressed(), true);
QCOMPARE(point12->pressed(), true);
@@ -650,32 +699,40 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
delta = QPoint(0, 15);
p1 += delta; p2 += delta;
QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2);
- QTest::mouseMove(window.data(), p1);
+ QWindowSystemInterface::handleMouseEvent(window.data(), device, p1, window->mapToGlobal(p1),
+ Qt::LeftButton, Qt::NoButton, QEvent::MouseMove,
+ Qt::NoModifier, Qt::MouseEventSynthesizedBySystem);
+ qApp->processEvents();
QQuickTouchUtils::flush(window.data());
qCDebug(lcTests, "after drags %d to %d,%d and %d,%d contentY is %lf",
i, p1.x(), p1.y(), p2.x(), p2.y(), flickable->contentY());
}
- QEXPECT_FAIL("", "currently flickable does grab the actual mouse", Continue);
QCOMPARE(flickable->contentY(), qreal(0));
QCOMPARE(point11->pressed(), true);
- QEXPECT_FAIL("", "currently flickable does grab the actual mouse", Continue);
QCOMPARE(point12->pressed(), true);
QTest::touchEvent(window.data(), device).release(0, p1).release(1, p2);
- QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
+ QWindowSystemInterface::handleMouseEvent(window.data(), device, p1, window->mapToGlobal(p1),
+ Qt::NoButton, Qt::LeftButton, QEvent::MouseButtonRelease,
+ Qt::NoModifier, Qt::MouseEventSynthesizedBySystem);
+ qApp->processEvents();
QQuickTouchUtils::flush(window.data());
}
// test that dragging out of a Flickable containing a MPTA doesn't harm Flickable's state.
void tst_QQuickMultiPointTouchArea::inFlickable2()
{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
QScopedPointer<QQuickView> window(createAndShowView("inFlickable2.qml"));
QVERIFY(window->rootObject() != nullptr);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable != nullptr);
+ QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>();
+ QVERIFY(mpta);
+
QQuickTouchPoint *point11 = window->rootObject()->findChild<QQuickTouchPoint*>("point1");
QVERIFY(point11);
@@ -688,25 +745,12 @@ void tst_QQuickMultiPointTouchArea::inFlickable2()
QQuickTouchUtils::flush(window.data());
QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
- p1 += QPoint(15,0);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
- QTest::mouseMove(window.data(), p1);
-
- p1 += QPoint(15,0);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
- QTest::mouseMove(window.data(), p1);
-
- p1 += QPoint(15,0);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
- QTest::mouseMove(window.data(), p1);
-
- p1 += QPoint(15,0);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
- QTest::mouseMove(window.data(), p1);
+ for (int i = 0; i < 4; ++i) {
+ p1 += QPoint(dragThreshold, 0);
+ QTest::touchEvent(window.data(), device).move(0, p1);
+ QQuickTouchUtils::flush(window.data());
+ QTest::mouseMove(window.data(), p1);
+ }
QVERIFY(!flickable->isMoving());
QVERIFY(point11->pressed());
@@ -719,27 +763,21 @@ void tst_QQuickMultiPointTouchArea::inFlickable2()
QTRY_VERIFY(!flickable->isMoving());
// Check that we can still move the Flickable
+ QSignalSpy gestureStartedSpy(mpta, &QQuickMultiPointTouchArea::gestureStarted);
p1 = QPoint(50,100);
QTest::touchEvent(window.data(), device).press(0, p1);
QQuickTouchUtils::flush(window.data());
QCOMPARE(point11->pressed(), true);
- p1 += QPoint(0,15);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
-
- p1 += QPoint(0,15);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
-
- p1 += QPoint(0,15);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
-
- p1 += QPoint(0,15);
- QTest::touchEvent(window.data(), device).move(0, p1);
- QQuickTouchUtils::flush(window.data());
+ for (int i = 0; i < 4; ++i) {
+ p1 += QPoint(0, dragThreshold);
+ QTest::touchEvent(window.data(), device).move(0, p1);
+ QQuickTouchUtils::flush(window.data());
+ // QTBUG-113653: gestureStarted is emitted when touch delta exceeds drag threshold,
+ // regardless of the filtering Flickable parent
+ QCOMPARE(gestureStartedSpy.size(), i > 0 ? 1 : 0);
+ }
QVERIFY(flickable->contentY() < 0);
QVERIFY(flickable->isMoving());
@@ -1239,7 +1277,7 @@ void tst_QQuickMultiPointTouchArea::mouseGestureStarted() // QTBUG-70258
area->setProperty("grabGesture", grabGesture);
QQuickTouchPoint *point1 = view->rootObject()->findChild<QQuickTouchPoint*>("point1");
QCOMPARE(point1->pressed(), false);
- QSignalSpy gestureStartedSpy(area, SIGNAL(gestureStarted(QQuickGrabGestureEvent *)));
+ QSignalSpy gestureStartedSpy(area, SIGNAL(gestureStarted(QQuickGrabGestureEvent*)));
QPoint p1 = QPoint(distanceFromOrigin, distanceFromOrigin);
QTest::mousePress(view.data(), Qt::LeftButton, Qt::NoModifier, p1);
@@ -1420,6 +1458,23 @@ void tst_QQuickMultiPointTouchArea::nestedPinchAreaMouse() // QTBUG-83662
QCOMPARE(mpta->property("releasedCount").toInt(), 1);
}
+/*
+ A disabled MultiPointTouchArea should not interfere with hover event
+ propagation to siblings underneath.
+*/
+void tst_QQuickMultiPointTouchArea::disabledIgnoresHover()
+{
+ QScopedPointer<QQuickView> window(createAndShowView("touchOverMouseArea.qml"));
+ QQuickItem *root = qobject_cast<QQuickItem *>(window->rootObject());
+ QVERIFY(root);
+
+ QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea *>();
+
+ QTest::mouseMove(window.data(), QPoint(40, 40));
+ QTest::mouseMove(window.data(), QPoint(50, 50));
+ QVERIFY(mouseArea->hovered());
+}
+
QTEST_MAIN(tst_QQuickMultiPointTouchArea)
#include "tst_qquickmultipointtoucharea.moc"
diff --git a/tests/auto/quick/qquickpainteditem/CMakeLists.txt b/tests/auto/quick/qquickpainteditem/CMakeLists.txt
index 01978dec5b..ef92df98e3 100644
--- a/tests/auto/quick/qquickpainteditem/CMakeLists.txt
+++ b/tests/auto/quick/qquickpainteditem/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpainteditem Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpainteditem LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qquickpainteditem
SOURCES
tst_qquickpainteditem.cpp
diff --git a/tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp b/tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp
index 56ebfdbd48..eca935c258 100644
--- a/tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp
+++ b/tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
@@ -40,12 +40,14 @@ public:
: QQuickPaintedItem(parent)
, paintNode(nullptr)
, paintRequests(0)
+ , painterHadAA(false)
{
}
void paint(QPainter *painter) override
{
++paintRequests;
+ painterHadAA = painter->testRenderHint(QPainter::Antialiasing);
clipRect = painter->clipBoundingRect();
}
#if QT_CONFIG(opengl)
@@ -66,9 +68,12 @@ public:
QSGSoftwarePainterNode *paintNode;
#endif
int paintRequests;
+ bool painterHadAA;
QRectF clipRect;
};
+static bool hasDirtyAAFlag(QQuickItem *item) {
+ return QQuickItemPrivate::get(item)->dirtyAttributes & QQuickItemPrivate::Antialiasing; }
static bool hasDirtyContentFlag(QQuickItem *item) {
return QQuickItemPrivate::get(item)->dirtyAttributes & QQuickItemPrivate::Content; }
static void clearDirtyContentFlag(QQuickItem *item) {
@@ -167,32 +172,35 @@ void tst_QQuickPaintedItem::antialiasing()
item.setAntialiasing(false);
QCOMPARE(item.antialiasing(), false);
- QCOMPARE(hasDirtyContentFlag(&item), false);
+ QCOMPARE(hasDirtyAAFlag(&item), false);
item.update();
QTRY_COMPARE(hasDirtyContentFlag(&item), false);
QVERIFY(item.paintNode);
QCOMPARE(item.paintNode->smoothPainting(), false);
+ QCOMPARE(item.painterHadAA, false);
item.setAntialiasing(true);
QCOMPARE(item.antialiasing(), true);
- QCOMPARE(hasDirtyContentFlag(&item), true);
+ QCOMPARE(hasDirtyAAFlag(&item), true);
- QTRY_COMPARE(hasDirtyContentFlag(&item), false);
+ QTRY_COMPARE(hasDirtyAAFlag(&item), false);
QVERIFY(item.paintNode);
QCOMPARE(item.paintNode->smoothPainting(), true);
+ QCOMPARE(item.painterHadAA, true);
item.setAntialiasing(true);
QCOMPARE(item.antialiasing(), true);
- QCOMPARE(hasDirtyContentFlag(&item), false);
+ QCOMPARE(hasDirtyAAFlag(&item), false);
item.setAntialiasing(false);
QCOMPARE(item.antialiasing(), false);
- QCOMPARE(hasDirtyContentFlag(&item), true);
+ QCOMPARE(hasDirtyAAFlag(&item), true);
- QTRY_COMPARE(hasDirtyContentFlag(&item), false);
+ QTRY_COMPARE(hasDirtyAAFlag(&item), false);
QVERIFY(item.paintNode);
QCOMPARE(item.paintNode->smoothPainting(), false);
+ QCOMPARE(item.painterHadAA, false);
}
void tst_QQuickPaintedItem::mipmap()
diff --git a/tests/auto/quick/qquickpalette/CMakeLists.txt b/tests/auto/quick/qquickpalette/CMakeLists.txt
index 4e61f6b3b5..c938212d28 100644
--- a/tests/auto/quick/qquickpalette/CMakeLists.txt
+++ b/tests/auto/quick/qquickpalette/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpalette LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickpalette/data/palette-item-custom.qml b/tests/auto/quick/qquickpalette/data/palette-item-custom.qml
index 1e90b71b0a..a7a3a1d0b8 100644
--- a/tests/auto/quick/qquickpalette/data/palette-item-custom.qml
+++ b/tests/auto/quick/qquickpalette/data/palette-item-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquickpalette/data/palette-item-default.qml b/tests/auto/quick/qquickpalette/data/palette-item-default.qml
index b6cc0a0656..3d60a51195 100644
--- a/tests/auto/quick/qquickpalette/data/palette-item-default.qml
+++ b/tests/auto/quick/qquickpalette/data/palette-item-default.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquickpalette/data/palette-window-custom.qml b/tests/auto/quick/qquickpalette/data/palette-window-custom.qml
index b92fe6f374..60bed45405 100644
--- a/tests/auto/quick/qquickpalette/data/palette-window-custom.qml
+++ b/tests/auto/quick/qquickpalette/data/palette-window-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Window
diff --git a/tests/auto/quick/qquickpalette/data/palette-window-default.qml b/tests/auto/quick/qquickpalette/data/palette-window-default.qml
index e6a1c81033..cd0036ac64 100644
--- a/tests/auto/quick/qquickpalette/data/palette-window-default.qml
+++ b/tests/auto/quick/qquickpalette/data/palette-window-default.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Window
diff --git a/tests/auto/quick/qquickpalette/tst_qquickpalette.cpp b/tests/auto/quick/qquickpalette/tst_qquickpalette.cpp
index 53fa22dba5..7b7eace30b 100644
--- a/tests/auto/quick/qquickpalette/tst_qquickpalette.cpp
+++ b/tests/auto/quick/qquickpalette/tst_qquickpalette.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/QSignalSpy>
diff --git a/tests/auto/quick/qquickpath/CMakeLists.txt b/tests/auto/quick/qquickpath/CMakeLists.txt
index 40de1ce38d..80c6ed9980 100644
--- a/tests/auto/quick/qquickpath/CMakeLists.txt
+++ b/tests/auto/quick/qquickpath/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpath Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpath LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickpath/tst_qquickpath.cpp b/tests/auto/quick/qquickpath/tst_qquickpath.cpp
index 2c21fe39df..9fd4d9ec8a 100644
--- a/tests/auto/quick/qquickpath/tst_qquickpath.cpp
+++ b/tests/auto/quick/qquickpath/tst_qquickpath.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quick/qquickpathview/CMakeLists.txt b/tests/auto/quick/qquickpathview/CMakeLists.txt
index 998eba58f5..a7f74bafce 100644
--- a/tests/auto/quick/qquickpathview/CMakeLists.txt
+++ b/tests/auto/quick/qquickpathview/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpathview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpathview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickpathview/data/flickableDelegate.qml b/tests/auto/quick/qquickpathview/data/flickableDelegate.qml
index 3243dc23dc..0cb42dcf86 100644
--- a/tests/auto/quick/qquickpathview/data/flickableDelegate.qml
+++ b/tests/auto/quick/qquickpathview/data/flickableDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickpathview/data/mousePressAfterFlick.qml b/tests/auto/quick/qquickpathview/data/mousePressAfterFlick.qml
new file mode 100644
index 0000000000..a30234127b
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/mousePressAfterFlick.qml
@@ -0,0 +1,61 @@
+import QtQuick
+
+Item {
+ id: root
+ width: 360
+ height: 900
+
+ MouseArea {
+ id: rootMouseArea
+ objectName: "rootMouseArea"
+ anchors.fill: parent
+ }
+
+ PathView {
+ id: pathView
+ objectName: "pathView"
+ anchors.centerIn: parent
+ width: 150
+ height: parent.height
+ dragMargin: Number.POSITIVE_INFINITY
+
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+ pathItemCount: 9
+
+ Item {
+ anchors.fill: parent
+ }
+
+ model: 10
+
+ delegate: Rectangle {
+ id: delegate
+ width: parent.width
+ height: 120
+ color: "red"
+ Text {
+ anchors.fill: parent
+ horizontalAlignment: Qt.AlignHCenter
+ text: "Path element " + modelData
+ }
+ }
+
+ path: Path {
+ id: path
+ property int width: pathView.width
+ startX: path.width / 2
+ startY: 0
+
+ PathCurve {
+ x: path.width / 2
+ relativeY: 32
+ }
+ PathCurve {
+ x: path.width / 2
+ relativeY: pathView.height - 32
+ }
+ }
+ }
+}
+
diff --git a/tests/auto/quick/qquickpathview/data/nestedInFlickable.qml b/tests/auto/quick/qquickpathview/data/nestedInFlickable.qml
index 36938d69b5..35f5b12d17 100644
--- a/tests/auto/quick/qquickpathview/data/nestedInFlickable.qml
+++ b/tests/auto/quick/qquickpathview/data/nestedInFlickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickpathview/data/nestedmousearea2.qml b/tests/auto/quick/qquickpathview/data/nestedmousearea2.qml
index 5fa50ec3c1..985b53cd45 100644
--- a/tests/auto/quick/qquickpathview/data/nestedmousearea2.qml
+++ b/tests/auto/quick/qquickpathview/data/nestedmousearea2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickpathview/data/objectModelMove.qml b/tests/auto/quick/qquickpathview/data/objectModelMove.qml
index 7e87330b23..04102d0956 100644
--- a/tests/auto/quick/qquickpathview/data/objectModelMove.qml
+++ b/tests/auto/quick/qquickpathview/data/objectModelMove.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml.Models 2.11
import QtQuick 2.11
diff --git a/tests/auto/quick/qquickpathview/data/overcached.qml b/tests/auto/quick/qquickpathview/data/overcached.qml
new file mode 100644
index 0000000000..734b0cd7cf
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/overcached.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.0
+
+PathView {
+ id: view
+ property int delegatesCreated: 0
+ property int delegatesDestroyed: 0
+
+ anchors.fill: parent
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+ pathItemCount: 10
+ interactive: true
+ model: 15
+ // Much bigger cache than model - pathItemCount
+ cacheItemCount: 100
+ delegate: Text {
+ text: modelData
+ Component.onCompleted: view.delegatesCreated++;
+ Component.onDestruction: view.delegatesDestroyed++;
+ }
+ path: Path {
+ PathLine {
+ x: 0
+ y: 400
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpathview/data/qtbug37815.qml b/tests/auto/quick/qquickpathview/data/qtbug37815.qml
index ea12daec87..8854164b5c 100644
--- a/tests/auto/quick/qquickpathview/data/qtbug37815.qml
+++ b/tests/auto/quick/qquickpathview/data/qtbug37815.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Netris
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickpathview/data/qtbug46487.qml b/tests/auto/quick/qquickpathview/data/qtbug46487.qml
new file mode 100644
index 0000000000..840d77ffe4
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/qtbug46487.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+PathView {
+ id: view
+ property int delegatesCreated: 0
+ property int delegatesDestroyed: 0
+
+ width: 400
+ height: 400
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+ pathItemCount: 5
+ currentIndex: 1
+ model: customModel
+ delegate: Text {
+ text: "item: " + index + " of: " + view.count
+ Component.onCompleted: view.delegatesCreated++;
+ Component.onDestruction: view.delegatesDestroyed++;
+ }
+ path: Path {
+ startX: 50
+ startY: 0
+ PathLine {
+ x: 50
+ y: 400
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpathview/data/qtbug53464.qml b/tests/auto/quick/qquickpathview/data/qtbug53464.qml
index a2855c0fb5..df16763ccb 100644
--- a/tests/auto/quick/qquickpathview/data/qtbug53464.qml
+++ b/tests/auto/quick/qquickpathview/data/qtbug53464.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Netris
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickpathview/data/qtbug90479.qml b/tests/auto/quick/qquickpathview/data/qtbug90479.qml
new file mode 100644
index 0000000000..bfa4ab8243
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/qtbug90479.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+PathView {
+ id: view
+ property int delegatesCreated: 0
+ property int delegatesDestroyed: 0
+
+ anchors.fill: parent
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+ pathItemCount: 6
+ interactive: true
+ model: 19
+ delegate: Text {
+ text: modelData
+ Component.onCompleted: view.delegatesCreated++;
+ Component.onDestruction: view.delegatesDestroyed++;
+ }
+ path: Path {
+ PathLine {
+ x: 0
+ y: 400
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index 98085002ea..1b06e0047b 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuick/qquickview.h>
@@ -13,6 +13,7 @@
#include <QtQuick/private/qquickpath_p.h>
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
+#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuickTest/QtQuickTest>
#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qqmlvaluetype_p.h>
@@ -130,6 +131,10 @@ private slots:
void requiredPropertiesInDelegate();
void requiredPropertiesInDelegatePreventUnrelated();
void touchMove();
+ void mousePressAfterFlick();
+ void qtbug90479();
+ void overCached();
+ void qtbug46487();
private:
QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
@@ -861,10 +866,9 @@ void tst_QQuickPathView::dataModel()
model.moveItem(3, 5);
QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").size(), 5);
- QList<QQuickItem*> items = findItems<QQuickItem>(pathview, "wrapper");
- foreach (QQuickItem *item, items) {
+ const QList<QQuickItem *> items = findItems<QQuickItem>(pathview, "wrapper");
+ for (QQuickItem *item : items)
QVERIFY(item->property("onPath").toBool());
- }
QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
// QTBUG-14199
@@ -2502,7 +2506,8 @@ void tst_QQuickPathView::qtbug37815()
const int pathItemCount = pathView->pathItemCount();
const int cacheItemCount = pathView->cacheItemCount();
int totalCount = 0;
- foreach (QQuickItem *item, pathView->childItems()) {
+ const auto childItems = pathView->childItems();
+ for (QQuickItem *item : childItems) {
if (item->objectName().startsWith(QLatin1String("delegate")))
++totalCount;
}
@@ -2571,7 +2576,8 @@ void tst_QQuickPathView::qtbug53464()
const int pathItemCount = pathView->pathItemCount();
int totalCount = 0;
- foreach (QQuickItem *item, pathView->childItems()) {
+ const auto childItems = pathView->childItems();
+ for (QQuickItem *item : childItems) {
if (item->objectName().startsWith(QLatin1String("delegate")))
++totalCount;
}
@@ -2851,6 +2857,147 @@ void tst_QQuickPathView::touchMove()
}
+void tst_QQuickPathView::mousePressAfterFlick() // QTBUG-115121
+{
+ QScopedPointer<QQuickView> window(createView());
+ QQuickVisualTestUtils::moveMouseAway(window.data());
+ window->setSource(testFileUrl("mousePressAfterFlick.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView *>("pathView");
+ QVERIFY(pathview != nullptr);
+
+ QQuickMouseArea *mouseArea = window->rootObject()->findChild<QQuickMouseArea *>("rootMouseArea");
+ QVERIFY(mouseArea != nullptr);
+
+ QSignalSpy flickingSpy(pathview, SIGNAL(flickingChanged()));
+ QSignalSpy flickStartedSpy(pathview, SIGNAL(flickStarted()));
+ QSignalSpy flickEndedSpy(pathview, SIGNAL(flickEnded()));
+
+ QSignalSpy pressedSpy(mouseArea, &QQuickMouseArea::pressed);
+
+ flickingSpy.clear();
+ flickStartedSpy.clear();
+ flickEndedSpy.clear();
+
+ // Dragging the child mouse area should animate the PathView (MA has no drag target)
+ QPoint from = QPoint((window->width() / 2), (window->height() * 3 / 4));
+ QPoint to = QPoint((window->width() / 2), (window->height() / 4));
+ flick(window.data(), from, to, 100);
+ QVERIFY(pathview->isMoving());
+ QCOMPARE(flickingSpy.size(), 1);
+ QCOMPARE(flickStartedSpy.size(), 1);
+ QCOMPARE(flickEndedSpy.size(), 0);
+
+ // Now while it's still moving, click it.
+ // The PathView should stop at a position
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(window->width() / 2, window->height() / 4));
+ QTRY_VERIFY(!pathview->isMoving());
+ QCOMPARE(flickEndedSpy.size(), 1);
+ QCOMPARE(pressedSpy.size(), 0);
+}
+
+void tst_QQuickPathView::qtbug90479()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("qtbug90479.qml"));
+
+ window->show();
+ qApp->processEvents();
+
+ QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
+ QVERIFY(pathview);
+
+ // cache items will be created async. Let's wait...
+ QTest::qWait(1000);
+
+ // Should create just pathItemCount amount and not destroy any
+ QCOMPARE(pathview->property("delegatesCreated").toInt(), 6);
+ QCOMPARE(pathview->property("delegatesDestroyed").toInt(), 0);
+}
+
+void tst_QQuickPathView::overCached()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("overcached.qml"));
+
+ window->show();
+ qApp->processEvents();
+
+ QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
+ QVERIFY(pathview);
+
+ // cache items will be created async. Let's wait...
+ QTest::qWait(1000);
+
+ // Should create max model + 1 amount with the current implementation
+ QVERIFY(pathview->property("delegatesCreated").toInt() <= 16);
+ QVERIFY(pathview->property("delegatesDestroyed").toInt() <= 1);
+}
+
+class CustomModel : public QAbstractListModel
+{
+public:
+ CustomModel(QObject *parent = 0) : QAbstractListModel(parent) {
+ m_values << 0 << 1 << 2 << 3 << 4;
+ }
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const {
+ Q_UNUSED(parent);
+ return m_values.count();
+ }
+ QVariant data(const QModelIndex &index, int role) const {
+ if (index.row() < 0 || m_values.count() <= index.row())
+ return QVariant();
+
+ return m_values[index.row()];
+ }
+
+ Q_INVOKABLE void shrink() {
+ beginResetModel();
+ m_values.takeLast();
+ m_values.takeLast();
+ endResetModel();
+ }
+
+private:
+ QList<int> m_values;
+};
+
+void tst_QQuickPathView::qtbug46487()
+{
+ QScopedPointer<QQuickView> window(createView());
+
+ CustomModel* model = new CustomModel;
+ QQmlContext *ctxt = window->rootContext();
+ ctxt->setContextProperty("customModel", model);
+
+ window->setSource(testFileUrl("qtbug46487.qml"));
+ window->show();
+ qApp->processEvents();
+
+ QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
+ QVERIFY(pathview);
+
+ QTest::qWait(500);
+
+ // Should create just pathItemCount amount and not destroy any
+ QCOMPARE(pathview->count(), 5);
+ QCOMPARE(pathview->property("delegatesCreated").toInt(), 5);
+ QCOMPARE(pathview->property("delegatesDestroyed").toInt(), 0);
+
+ // Resets the model and removes 2 items.
+ model->shrink();
+ QTest::qWait(500);
+
+ // Should destroy previous items (begin/endResetModel) and
+ // (re)create 3 new items.
+ QCOMPARE(pathview->count(), 3);
+ QCOMPARE(pathview->property("delegatesCreated").toInt(), 5 + 3);
+ QCOMPARE(pathview->property("delegatesDestroyed").toInt(), 5);
+}
+
QTEST_MAIN(tst_QQuickPathView)
#include "tst_qquickpathview.moc"
diff --git a/tests/auto/quick/qquickpincharea/CMakeLists.txt b/tests/auto/quick/qquickpincharea/CMakeLists.txt
index 8408a3f70e..4d0af2d3a0 100644
--- a/tests/auto/quick/qquickpincharea/CMakeLists.txt
+++ b/tests/auto/quick/qquickpincharea/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpincharea Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpincharea LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
index 800d7211fb..1bab22c4e2 100644
--- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
+++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/private/qvariantanimation_p.h>
#include <QtTest/QtTest>
@@ -15,6 +15,9 @@
#include <QtQml/qqmlcontext.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
+
+using namespace QQuickVisualTestUtils;
class tst_QQuickPinchArea: public QQmlDataTest
{
@@ -622,20 +625,6 @@ void tst_QQuickPinchArea::dragTransformedPinchArea() // QTBUG-63673
QCOMPARE(pinchArea->pinch()->active(), false);
}
-template<typename F>
-void forEachLerpStep(int steps, F &&func)
-{
- for (int i = 0; i < steps; ++i) {
- const qreal t = qreal(i) / steps;
- func(t);
- }
-}
-
-QPoint lerpPoints(const QPoint &point1, const QPoint &point2, qreal t)
-{
- return QPoint(_q_interpolate(point1.x(), point2.x(), t), _q_interpolate(point1.y(), point2.y(), t));
-};
-
// QTBUG-105058
void tst_QQuickPinchArea::pinchAreaKeepsDragInView()
{
@@ -666,9 +655,10 @@ void tst_QQuickPinchArea::pinchAreaKeepsDragInView()
const int steps = 30;
QPoint point1End = point1Start + QPoint(-dragThreshold, dragThreshold);
QPoint point2End = point2Start + QPoint(dragThreshold, -dragThreshold);
- forEachLerpStep(steps, [&](qreal t) {
- pinchSequence.move(1, lerpPoints(point1Start, point1End, t), &view)
- .move(2, lerpPoints(point2Start, point2End, t), &view).commit();
+
+ forEachStep(steps, [&](qreal progress) {
+ pinchSequence.move(1, lerpPoints(point1Start, point1End, progress), &view)
+ .move(2, lerpPoints(point2Start, point2End, progress), &view).commit();
QQuickTouchUtils::flush(&view);
QTest::qWait(5);
});
@@ -691,9 +681,9 @@ void tst_QQuickPinchArea::pinchAreaKeepsDragInView()
point2Start = point2End;
point1End = point1Start + QPoint(100, 0);
point2End = point2Start + QPoint(100, 0);
- forEachLerpStep(steps, [&](qreal t) {
- pinchSequence.move(1, lerpPoints(point1Start, point1End, t), &view)
- .move(2, lerpPoints(point2Start, point2End, t), &view).commit();
+ forEachStep(steps, [&](qreal progress) {
+ pinchSequence.move(1, lerpPoints(point1Start, point1End, progress), &view)
+ .move(2, lerpPoints(point2Start, point2End, progress), &view).commit();
QQuickTouchUtils::flush(&view);
QTest::qWait(5);
});
diff --git a/tests/auto/quick/qquickpixmapcache/BLACKLIST b/tests/auto/quick/qquickpixmapcache/BLACKLIST
new file mode 100644
index 0000000000..00d8eb5619
--- /dev/null
+++ b/tests/auto/quick/qquickpixmapcache/BLACKLIST
@@ -0,0 +1,3 @@
+# QTBUG-118064
+[slowDevice]
+*
diff --git a/tests/auto/quick/qquickpixmapcache/CMakeLists.txt b/tests/auto/quick/qquickpixmapcache/CMakeLists.txt
index 2fbe3592c0..97735172f2 100644
--- a/tests/auto/quick/qquickpixmapcache/CMakeLists.txt
+++ b/tests/auto/quick/qquickpixmapcache/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpixmapcache Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpixmapcache LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -16,8 +22,8 @@ list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_qquickpixmapcache
SOURCES
tst_qquickpixmapcache.cpp
+ deviceloadingimage.h deviceloadingimage.cpp
LIBRARIES
- Qt::Concurrent
Qt::CorePrivate
Qt::Gui
Qt::GuiPrivate
@@ -40,3 +46,13 @@ qt_internal_extend_target(tst_qquickpixmapcache CONDITION NOT ANDROID AND NOT IO
DEFINES
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
)
+
+qt_internal_extend_target(tst_qquickpixmapcache CONDITION QT_FEATURE_concurrent
+ LIBRARIES
+ Qt::Concurrent
+)
+
+qt_policy(SET QTP0001 NEW)
+qt_add_qml_module(tst_qquickpixmapcache
+ URI PixmapCacheTest
+)
diff --git a/tests/auto/quick/qquickpixmapcache/data/tableViewWithDeviceLoadingImages.qml b/tests/auto/quick/qquickpixmapcache/data/tableViewWithDeviceLoadingImages.qml
new file mode 100644
index 0000000000..68119ac1a9
--- /dev/null
+++ b/tests/auto/quick/qquickpixmapcache/data/tableViewWithDeviceLoadingImages.qml
@@ -0,0 +1,45 @@
+import QtQuick
+import PixmapCacheTest
+
+TableView {
+ id: root
+ width: 640
+ height: 480
+ model: 100
+ rowSpacing: 6
+ property real size: 40
+ columnWidthProvider: function(col) { return root.size }
+ rowHeightProvider: function(col) { return root.size }
+
+ Timer {
+ interval: 200
+ repeat: true
+ running: true
+ onTriggered: {
+ root.size = Math.random() * 200
+ root.positionViewAtRow(Math.round(Math.random() * 100), TableView.Visible)
+ }
+ }
+
+ delegate: DeviceLoadingImage {
+ required property int index
+ width: root.size; height: root.size
+ asynchronous: true
+ source: "image://slow/" + index
+ sourceSize.width: width
+ sourceSize.height: height
+
+ Rectangle {
+ anchors.fill: parent
+ color: "transparent"
+ border.color: "red"
+ }
+
+ Text {
+ color: "red"
+ style: Text.Outline
+ styleColor: "white"
+ text: index + "\nsize " + root.size.toFixed(1)
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpixmapcache/deviceloadingimage.cpp b/tests/auto/quick/qquickpixmapcache/deviceloadingimage.cpp
new file mode 100644
index 0000000000..72a956d1d2
--- /dev/null
+++ b/tests/auto/quick/qquickpixmapcache/deviceloadingimage.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "deviceloadingimage.h"
+
+#include <QtQuick/private/qquickimage_p_p.h>
+
+Q_DECLARE_LOGGING_CATEGORY(lcTests)
+
+void DeviceLoadingImage::load()
+{
+ auto *d = static_cast<QQuickImagePrivate *>(QQuickImagePrivate::get(this));
+ static int thisRequestFinished = -1;
+ if (thisRequestFinished == -1) {
+ thisRequestFinished =
+ QQuickImageBase::staticMetaObject.indexOfSlot("requestFinished()");
+ }
+ const QQmlContext *context = qmlContext(this);
+ Q_ASSERT(context);
+ QUrl resolved = context->resolvedUrl(d->url);
+ device = std::make_unique<QFile>(resolved.toLocalFile());
+ d->pix.loadImageFromDevice(qmlEngine(this), device.get(), context->resolvedUrl(d->url),
+ d->sourceClipRect.toRect(), d->sourcesize * d->devicePixelRatio,
+ QQuickImageProviderOptions(), d->currentFrame, d->frameCount);
+
+ qCDebug(lcTests) << "loading page" << d->currentFrame << "of" << d->frameCount << "status" << d->pix.status();
+
+ switch (d->pix.status()) {
+ case QQuickPixmap::Ready:
+ pixmapChange();
+ break;
+ case QQuickPixmap::Loading:
+ d->pix.connectFinished(this, thisRequestFinished);
+ if (d->status != Loading) {
+ d->status = Loading;
+ emit statusChanged(d->status);
+ }
+ break;
+ default:
+ qCWarning(lcTests) << "unexpected status" << d->pix.status();
+ break;
+ }
+}
diff --git a/tests/auto/quick/qquickpixmapcache/deviceloadingimage.h b/tests/auto/quick/qquickpixmapcache/deviceloadingimage.h
new file mode 100644
index 0000000000..062f51d16f
--- /dev/null
+++ b/tests/auto/quick/qquickpixmapcache/deviceloadingimage.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/QFile>
+#include <QtQuick/private/qquickimage_p.h>
+
+class DeviceLoadingImage : public QQuickImage
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(DeviceLoadingImage)
+
+public:
+ DeviceLoadingImage(QQuickItem *parent = nullptr) : QQuickImage(parent) { }
+
+protected:
+ void load() override;
+
+ std::unique_ptr<QFile> device;
+};
diff --git a/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp b/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp
index 590f022e4e..fce9bba0c6 100644
--- a/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp
+++ b/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp
@@ -1,20 +1,51 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QtTest>
#include <QtQuick/private/qquickpixmapcache_p.h>
#include <QtQml/qqmlengine.h>
#include <QtQuick/qquickimageprovider.h>
+#include <QtQuick/qquickview.h>
#include <QtQml/QQmlComponent>
#include <QNetworkReply>
+#include <QtGui/qpainter.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
#if QT_CONFIG(concurrent)
#include <qtconcurrentrun.h>
#include <qfuture.h>
#endif
+Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+
+class SlowProvider : public QQuickImageProvider
+{
+public:
+ SlowProvider() : QQuickImageProvider(Pixmap) {}
+
+ QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) override
+ {
+ const int row = id.toInt();
+ qCDebug(lcTests) << requestCount << QThread::currentThread() << "row" << row << requestedSize;
+ QPixmap image(requestedSize);
+ QPainter painter(&image);
+ const QColor c(128, row % 8 * 32, 64);
+ painter.fillRect(0, 0, requestedSize.width(), requestedSize.height(), c);
+ if (size)
+ *size = requestedSize;
+ ++requestCount;
+ QThread::currentThread()->msleep(row);
+ return image;
+ }
+
+ int requestCount = 0;
+};
+Q_DECLARE_METATYPE(SlowProvider*);
+
+QT_BEGIN_NAMESPACE
+
#define PIXMAP_DATA_LEAK_TEST 0
class tst_qquickpixmapcache : public QQmlDataTest
@@ -41,6 +72,7 @@ private slots:
#if PIXMAP_DATA_LEAK_TEST
void dataLeak();
#endif
+ void slowDevice();
private:
QQmlEngine engine;
TestHTTPServer server;
@@ -450,7 +482,6 @@ void tst_qquickpixmapcache::asynchronousNoCache()
QScopedPointer<QObject> root {component.create()}; // should not crash
}
-
#if PIXMAP_DATA_LEAK_TEST
// This test should not be enabled by default as it
// produces spurious output in the expected case.
@@ -460,9 +491,9 @@ class DataLeakView : public QQuickView
Q_OBJECT
public:
- explicit DataLeakView() : QQuickView()
+ explicit DataLeakView(const QUrl &src) : QQuickView()
{
- setSource(testFileUrl("dataLeak.qml"));
+ setSource(src);
}
void showFor2Seconds()
@@ -480,14 +511,14 @@ Q_GLOBAL_STATIC(QQuickPixmap, dataLeakPixmap)
void tst_qquickpixmapcache::dataLeak()
{
// Should not leak cached QQuickPixmapData.
- // Unfortunately, since the QQuickPixmapStore
- // is a global static, and it releases the cache
+ // Unfortunately, since the QQuickPixmapCache
+ // is a singleton, and it releases the cache
// entries on dtor (application exit), we must use
- // valgrind to determine whether it leaks or not.
+ // valgrind or ASAN to determine whether it leaks or not.
QQuickPixmap *p1 = new QQuickPixmap;
QQuickPixmap *p2 = new QQuickPixmap;
{
- QScopedPointer<DataLeakView> test(new DataLeakView);
+ QScopedPointer<DataLeakView> test(new DataLeakView(testFileUrl("dataLeak.qml")));
test->showFor2Seconds();
dataLeakPixmap()->load(test->engine(), testFileUrl("exists.png"));
p1->load(test->engine(), testFileUrl("exists.png"));
@@ -497,12 +528,45 @@ void tst_qquickpixmapcache::dataLeak()
// When the (global static) dataLeakPixmap is deleted, it
// shouldn't attempt to dereference a QQuickPixmapData
- // which has been deleted by the QQuickPixmapStore
+ // which has been deleted by the QQuickPixmapCache
// destructor.
}
#endif
#undef PIXMAP_DATA_LEAK_TEST
+/*!
+ Test lots of async QQuickPixmap::loadImageFromDevice() jobs with random
+ sizes and frames, so that cached images are seldom reused. Some jobs get
+ cancelled, some succeed. This should not lead to any cache leaks.
+ Similar to the QtPdf usecase in QTBUG-114953
+*/
+void tst_qquickpixmapcache::slowDevice()
+{
+#ifdef QT_BUILD_INTERNAL
+ auto *provider = new SlowProvider;
+ engine.addImageProvider("slow", provider); // takes ownership
+
+ {
+ QQuickView window(&engine, nullptr);
+ QVERIFY(QQuickTest::showView(window, testFileUrl("tableViewWithDeviceLoadingImages.qml")));
+ // Timer generates 5 requests / sec; TableView shows multiple delegates (depending on size);
+ // so we should get 100 requests in some fraction of 20 sec. Give it 30 to be sure.
+ QTRY_COMPARE_GE_WITH_TIMEOUT(provider->requestCount, 100, 30000);
+ const int cacheCount = QQuickPixmapCache::instance()->m_cache.size();
+ qCDebug(lcTests) << "cached pixmaps" << cacheCount;
+ QCOMPARE_GT(cacheCount, 0);
+ } // window goes out of scope: all QQuickPixmapData instances should be eventually unreferenced
+
+ QTRY_COMPARE(QQuickPixmapCache::instance()->referencedCost(), 0);
+ const int leakedPixmaps = QQuickPixmapCache::instance()->destroyCache();
+ QCOMPARE(leakedPixmaps, 0);
+#else
+ QSKIP("This test relies on private APIs that are only exported in developer-builds");
+#endif
+}
+
+QT_END_NAMESPACE
+
QTEST_MAIN(tst_qquickpixmapcache)
#include "tst_qquickpixmapcache.moc"
diff --git a/tests/auto/quick/qquickpositioners/CMakeLists.txt b/tests/auto/quick/qquickpositioners/CMakeLists.txt
index 3e563155b6..fb046e3d45 100644
--- a/tests/auto/quick/qquickpositioners/CMakeLists.txt
+++ b/tests/auto/quick/qquickpositioners/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickpositioners Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpositioners LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
index 317694f06c..c250fbf554 100644
--- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
+++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <QtTest/QtTest>
#include <QtQuick/qquickview.h>
#include <qqmlengine.h>
@@ -1328,7 +1329,7 @@ void tst_qquickpositioners::checkItemPositions(QQuickItem *positioner, QaimModel
for (int i=0; i<model->count(); ++i) {
QQuickItem *item = findItem<QQuickItem>(positioner, "wrapper", i);
- QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+ QVERIFY2(item, qPrintable(QString("Item %1 not found").arg(i)));
QCOMPARE(item->width(), currentSize);
QCOMPARE(item->height(), currentSize);
@@ -3757,7 +3758,7 @@ void tst_qquickpositioners::test_mirroring()
QList<QString> objectNames;
objectNames << "one" << "two" << "three" << "four" << "five";
- foreach (const QString qmlFile, qmlFiles) {
+ for (const QString &qmlFile : std::as_const(qmlFiles)) {
QScopedPointer<QQuickView> windowA(createView(testFile(qmlFile)));
QQuickItem *rootA = qobject_cast<QQuickItem*>(windowA->rootObject());
@@ -3770,7 +3771,7 @@ void tst_qquickpositioners::test_mirroring()
rootA->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft
// LTR != RTL
- foreach (const QString objectName, objectNames) {
+ for (const QString &objectName : std::as_const(objectNames)) {
// horizontal.qml and horizontal-padding.qml only have three items
if (qmlFile.startsWith(QString("horizontal")) && objectName == QString("four"))
break;
@@ -3785,7 +3786,7 @@ void tst_qquickpositioners::test_mirroring()
inheritProp.write(true);
// RTL == mirror
- foreach (const QString objectName, objectNames) {
+ for (const QString &objectName : std::as_const(objectNames)) {
// horizontal.qml and horizontal-padding.qml only have three items
if (qmlFile.startsWith(QString("horizontal")) && objectName == QString("four"))
break;
@@ -3806,7 +3807,7 @@ void tst_qquickpositioners::test_mirroring()
rootB->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft
// LTR == RTL + mirror
- foreach (const QString objectName, objectNames) {
+ for (const QString &objectName : std::as_const(objectNames)) {
// horizontal.qml and horizontal-padding.qml only have three items
if (qmlFile.startsWith(QString("horizontal")) && objectName == QString("four"))
break;
@@ -4027,7 +4028,7 @@ void tst_qquickpositioners::matchItemsAndIndexes(const QVariantMap &items, const
QCOMPARE(it.value().typeId(), QMetaType::Int);
QString name = it.key();
int itemIndex = it.value().toInt();
- QVERIFY2(expectedIndexes.contains(itemIndex), QTest::toString(QString("Index %1 not found in expectedIndexes").arg(itemIndex)));
+ QVERIFY2(expectedIndexes.contains(itemIndex), qPrintable(QString("Index %1 not found in expectedIndexes").arg(itemIndex)));
if (model.name(itemIndex) != name)
qDebug() << itemIndex;
QCOMPARE(model.name(itemIndex), name);
@@ -4042,8 +4043,8 @@ void tst_qquickpositioners::matchItemLists(const QVariantList &itemLists, const
QVariantList current = itemLists[i].toList();
for (int j=0; j<current.size(); j++) {
QQuickItem *o = qobject_cast<QQuickItem*>(current[j].value<QObject*>());
- QVERIFY2(o, QTest::toString(QString("Invalid actual item at %1").arg(j)));
- QVERIFY2(expectedItems.contains(o), QTest::toString(QString("Cannot match item %1").arg(j)));
+ QVERIFY2(o, qPrintable(QString("Invalid actual item at %1").arg(j)));
+ QVERIFY2(expectedItems.contains(o), qPrintable(QString("Cannot match item %1").arg(j)));
}
QCOMPARE(current.size(), expectedItems.size());
}
diff --git a/tests/auto/quick/qquickrectangle/CMakeLists.txt b/tests/auto/quick/qquickrectangle/CMakeLists.txt
index ab1ca8ca29..2d12b1df71 100644
--- a/tests/auto/quick/qquickrectangle/CMakeLists.txt
+++ b/tests/auto/quick/qquickrectangle/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickrectangle Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickrectangle LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickrectangle/data/multi-radius.qml b/tests/auto/quick/qquickrectangle/data/multi-radius.qml
new file mode 100644
index 0000000000..18c74a2dd9
--- /dev/null
+++ b/tests/auto/quick/qquickrectangle/data/multi-radius.qml
@@ -0,0 +1,27 @@
+import QtQuick
+
+Item {
+ property alias firstRectangle: r1
+ property alias secondRectangle: r2
+ property alias thirdRectangle: r3
+ Rectangle {
+ id: r1
+ anchors.fill: parent
+ radius: 5
+ topLeftRadius: 10
+ bottomRightRadius: 2
+ }
+ Rectangle {
+ id: r2
+ anchors.fill: parent
+ radius: 5
+ topRightRadius: 10
+ bottomLeftRadius: 2
+ }
+ Rectangle {
+ id: r3
+ anchors.fill: parent
+ radius: 5
+ }
+}
+
diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
index d5520de682..4472be7d17 100644
--- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
+++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QSignalSpy>
@@ -26,6 +26,7 @@ private slots:
void gradient_multiple();
void gradient_preset();
void antialiasing();
+ void multiRadius();
private:
QQmlEngine engine;
@@ -231,6 +232,82 @@ void tst_qquickrectangle::antialiasing()
QCOMPARE(spy.size(), 7);
}
+void tst_qquickrectangle::multiRadius()
+{
+ QQuickView view;
+ view.setSource(testFileUrl("multi-radius.qml"));
+ view.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ QQuickRectangle *firstRect = qobject_cast<QQuickRectangle*>(view.rootObject()->property("firstRectangle").value<QObject*>());
+ QQuickRectangle *secondRect = qobject_cast<QQuickRectangle*>(view.rootObject()->property("secondRectangle").value<QObject*>());
+ QQuickRectangle *thirdRect = qobject_cast<QQuickRectangle*>(view.rootObject()->property("thirdRectangle").value<QObject*>());
+ QVERIFY(firstRect);
+ QVERIFY(secondRect);
+ QVERIFY(thirdRect);
+
+ QCOMPARE(firstRect->topLeftRadius(), 10.);
+ QCOMPARE(firstRect->topRightRadius(), 5.);
+ QCOMPARE(firstRect->bottomLeftRadius(), 5.);
+ QCOMPARE(firstRect->bottomRightRadius(), 2.);
+
+ QSignalSpy spytl(firstRect, SIGNAL(topLeftRadiusChanged()));
+ QSignalSpy spytr(firstRect, SIGNAL(topRightRadiusChanged()));
+ QSignalSpy spybl(firstRect, SIGNAL(bottomLeftRadiusChanged()));
+ QSignalSpy spybr(firstRect, SIGNAL(bottomRightRadiusChanged()));
+
+ firstRect->setRadius(12);
+
+ QCOMPARE(firstRect->topLeftRadius(), 10.);
+ QCOMPARE(firstRect->topRightRadius(), 12.);
+ QCOMPARE(firstRect->bottomLeftRadius(), 12.);
+ QCOMPARE(firstRect->bottomRightRadius(), 2.);
+
+ QCOMPARE(spytl.size(), 0);
+ QCOMPARE(spytr.size(), 1);
+ QCOMPARE(spybl.size(), 1);
+ QCOMPARE(spybr.size(), 0);
+
+ firstRect->resetTopLeftRadius();
+ firstRect->resetBottomRightRadius();
+ QCOMPARE(firstRect->topRightRadius(), 12.);
+ QCOMPARE(firstRect->bottomLeftRadius(), 12.);
+
+ QCOMPARE(secondRect->topLeftRadius(), 5.);
+ QCOMPARE(secondRect->topRightRadius(), 10.);
+ QCOMPARE(secondRect->bottomLeftRadius(), 2.);
+ QCOMPARE(secondRect->bottomRightRadius(), 5.);
+
+ QSignalSpy spytl2(secondRect, SIGNAL(topLeftRadiusChanged()));
+ QSignalSpy spytr2(secondRect, SIGNAL(topRightRadiusChanged()));
+ QSignalSpy spybl2(secondRect, SIGNAL(bottomLeftRadiusChanged()));
+ QSignalSpy spybr2(secondRect, SIGNAL(bottomRightRadiusChanged()));
+
+ secondRect->setRadius(12);
+
+ QCOMPARE(secondRect->topLeftRadius(), 12.);
+ QCOMPARE(secondRect->topRightRadius(), 10.);
+ QCOMPARE(secondRect->bottomLeftRadius(), 2.);
+ QCOMPARE(secondRect->bottomRightRadius(), 12.);
+
+ QCOMPARE(spytl2.size(), 1);
+ QCOMPARE(spytr2.size(), 0);
+ QCOMPARE(spybl2.size(), 0);
+ QCOMPARE(spybr2.size(), 1);
+
+ secondRect->resetTopRightRadius();
+ secondRect->resetBottomLeftRadius();
+ QCOMPARE(secondRect->topRightRadius(), 12.);
+ QCOMPARE(secondRect->bottomLeftRadius(), 12.);
+
+ QCOMPARE(thirdRect->topLeftRadius(), 5.);
+ QCOMPARE(thirdRect->topRightRadius(), 5.);
+ QCOMPARE(thirdRect->bottomLeftRadius(), 5.);
+ QCOMPARE(thirdRect->bottomRightRadius(), 5.);
+
+}
+
QTEST_MAIN(tst_qquickrectangle)
#include "tst_qquickrectangle.moc"
diff --git a/tests/auto/quick/qquickrendercontrol/CMakeLists.txt b/tests/auto/quick/qquickrendercontrol/CMakeLists.txt
index ad4e89e184..3dd086f42b 100644
--- a/tests/auto/quick/qquickrendercontrol/CMakeLists.txt
+++ b/tests/auto/quick/qquickrendercontrol/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickrendercontrol Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickrendercontrol LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickrendercontrol/data/rect.qml b/tests/auto/quick/qquickrendercontrol/data/rect.qml
index 82aeccbff8..bd4d7c15af 100644
--- a/tests/auto/quick/qquickrendercontrol/data/rect.qml
+++ b/tests/auto/quick/qquickrendercontrol/data/rect.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/qquickrendercontrol/data/rect_depth.qml b/tests/auto/quick/qquickrendercontrol/data/rect_depth.qml
new file mode 100644
index 0000000000..49709aa0ae
--- /dev/null
+++ b/tests/auto/quick/qquickrendercontrol/data/rect_depth.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ width: 200
+ height: 200
+ color: "steelblue"
+ Rectangle {
+ id: r
+ width: 150
+ height: 150
+ anchors.centerIn: parent
+ color: "palegreen"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 5000; loops: -1 }
+ }
+ Rectangle {
+ width: 100
+ height: 100
+ color: "red"
+ z: -1 // not visible as it is completely below, but this only works if depth testing works (and there's a depth buffer) since these are opaque items
+ }
+ Text {
+ text: "Hello World\n\nrotation=" + Math.round(r.rotation)
+ anchors.centerIn: parent
+ }
+}
diff --git a/tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp b/tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp
index 2d56312f9d..938f0a12b2 100644
--- a/tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp
+++ b/tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
@@ -19,8 +19,7 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
-#include <QtGui/private/qrhi_p.h>
-#include <QtQuick/private/qquickrendercontrol_p.h>
+#include <rhi/qrhi.h>
#if QT_CONFIG(vulkan)
#include <QVulkanInstance>
@@ -63,6 +62,7 @@ private slots:
void renderAndReadBackWithRhi_data();
void renderAndReadBackWithRhi();
void renderAndReadBackWithVulkanNative();
+ void renderAndReadBackWithVulkanAndCustomDepthTexture();
private:
#if QT_CONFIG(vulkan)
@@ -105,30 +105,32 @@ void tst_RenderControl::renderAndReadBackWithRhi_data()
QTest::addColumn<QSGRendererInterface::GraphicsApi>("api");
#if QT_CONFIG(opengl)
- QTest::newRow("OpenGL") << QSGRendererInterface::OpenGLRhi;
+ QTest::newRow("OpenGL") << QSGRendererInterface::OpenGL;
#endif
#if QT_CONFIG(vulkan)
- QTest::newRow("Vulkan") << QSGRendererInterface::VulkanRhi;
+ QTest::newRow("Vulkan") << QSGRendererInterface::Vulkan;
#endif
#ifdef Q_OS_WIN
- QTest::newRow("D3D11") << QSGRendererInterface::Direct3D11Rhi;
+ QTest::newRow("D3D11") << QSGRendererInterface::Direct3D11;
+ QTest::newRow("D3D12") << QSGRendererInterface::Direct3D12;
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
- QTest::newRow("Metal") << QSGRendererInterface::MetalRhi;
+#if QT_CONFIG(metal)
+ QTest::newRow("Metal") << QSGRendererInterface::Metal;
#endif
}
void tst_RenderControl::renderAndReadBackWithRhi()
{
QFETCH(QSGRendererInterface::GraphicsApi, api);
+
#if QT_CONFIG(vulkan)
- if (api == QSGRendererInterface::VulkanRhi && !vulkanInstance.isValid())
+ if (api == QSGRendererInterface::Vulkan && !vulkanInstance.isValid())
QSKIP("Skipping Vulkan-based QRhi readback test due to failing to create a VkInstance");
#endif
#ifdef Q_OS_ANDROID
// QTBUG-102780
- if (api == QSGRendererInterface::VulkanRhi)
+ if (api == QSGRendererInterface::Vulkan)
QSKIP("Vulkan-based rendering tests on Android are flaky.");
#endif
@@ -142,7 +144,7 @@ void tst_RenderControl::renderAndReadBackWithRhi()
QScopedPointer<QQuickRenderControl> renderControl(new QQuickRenderControl);
QScopedPointer<QQuickWindow> quickWindow(new QQuickWindow(renderControl.data()));
#if QT_CONFIG(vulkan)
- if (api == QSGRendererInterface::VulkanRhi)
+ if (api == QSGRendererInterface::Vulkan)
quickWindow->setVulkanInstance(&vulkanInstance);
#endif
@@ -181,7 +183,7 @@ void tst_RenderControl::renderAndReadBackWithRhi()
// failed. The exception for now is OpenGL - that should (usually) work.
if (!initSuccess) {
#if QT_CONFIG(opengl)
- if (api != QSGRendererInterface::OpenGLRhi
+ if (api != QSGRendererInterface::OpenGL
|| !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
#endif
{
@@ -193,13 +195,7 @@ void tst_RenderControl::renderAndReadBackWithRhi()
QCOMPARE(quickWindow->rendererInterface()->graphicsApi(), api);
- // What comes now is technically cheating - as long as QRhi is not a public
- // API this is not something applications can follow doing. However, it
- // allows us to test out the pipeline without having to write 4 different
- // native (Vulkan, Metal, D3D11, OpenGL) implementations of all what's below.
-
- QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(renderControl.data());
- QRhi *rhi = rd->rhi;
+ QRhi *rhi = renderControl->rhi();
Q_ASSERT(rhi);
const QSize size = rootItem->size().toSize();
@@ -302,7 +298,7 @@ void tst_RenderControl::renderAndReadBackWithRhi()
};
QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
readbackBatch->readBackTexture(tex.data(), &readResult);
- rd->cb->resourceUpdate(readbackBatch);
+ renderControl->commandBuffer()->resourceUpdate(readbackBatch);
// our frame is done, submit
renderControl->endFrame();
@@ -360,7 +356,7 @@ void tst_RenderControl::renderAndReadBackWithVulkanNative()
if (!vulkanInstance.isValid())
QSKIP("Skipping native Vulkan test due to failing to create a VkInstance");
- QQuickWindow::setGraphicsApi(QSGRendererInterface::VulkanRhi);
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);
// We will create our own VkDevice and friends, which will then get used by
// Qt Quick as well (instead of creating its own objects), so this is a test
@@ -666,21 +662,6 @@ void tst_RenderControl::renderAndReadBackWithVulkanNative()
QVERIFY(qAbs(qGreen(background) - 130) < maxFuzz);
QVERIFY(qAbs(qBlue(background) - 180) < maxFuzz);
- // after about 1.25 seconds (animation time, one iteration is 16 ms
- // thanks to our custom animation driver) the rectangle reaches a 90
- // degree rotation, that should be frame 76
- if (frame <= 2 || (frame >= 76 && frame <= 80)) {
- QRgb c = img.pixel(28, 28); // rectangle
- QVERIFY(qAbs(qRed(c) - 152) < maxFuzz);
- QVERIFY(qAbs(qGreen(c) - 251) < maxFuzz);
- QVERIFY(qAbs(qBlue(c) - 152) < maxFuzz);
- } else {
- QRgb c = img.pixel(28, 28); // background because rectangle got rotated so this pixel is not covered by it
- QVERIFY(qAbs(qRed(c) - 70) < maxFuzz);
- QVERIFY(qAbs(qGreen(c) - 130) < maxFuzz);
- QVERIFY(qAbs(qBlue(c) - 180) < maxFuzz);
- }
-
img = QImage();
df->vkUnmapMemory(dev, bufMem);
}
@@ -702,6 +683,151 @@ void tst_RenderControl::renderAndReadBackWithVulkanNative()
#endif
}
+void tst_RenderControl::renderAndReadBackWithVulkanAndCustomDepthTexture()
+{
+#if QT_CONFIG(vulkan)
+ if (!vulkanInstance.isValid())
+ QSKIP("Skipping native Vulkan test due to failing to create a VkInstance");
+
+ QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);
+
+#ifdef Q_OS_ANDROID
+ if (QQuickWindow::graphicsApi() == QSGRendererInterface::Vulkan)
+ QSKIP("Vulkan support is broken in Android emulator, skipping");
+#endif
+
+ QScopedPointer<QQuickRenderControl> renderControl(new QQuickRenderControl);
+ QScopedPointer<QQuickWindow> quickWindow(new QQuickWindow(renderControl.data()));
+
+ quickWindow->setVulkanInstance(&vulkanInstance);
+
+ QScopedPointer<QQmlEngine> qmlEngine(new QQmlEngine);
+ QScopedPointer<QQmlComponent> qmlComponent(new QQmlComponent(qmlEngine.data(),
+ testFileUrl(QLatin1String("rect_depth.qml"))));
+ QVERIFY(!qmlComponent->isLoading());
+ if (qmlComponent->isError()) {
+ for (const QQmlError &error : qmlComponent->errors())
+ qWarning() << error.url() << error.line() << error;
+ }
+ QVERIFY(!qmlComponent->isError());
+
+ QObject *rootObject = qmlComponent->create();
+ if (qmlComponent->isError()) {
+ for (const QQmlError &error : qmlComponent->errors())
+ qWarning() << error.url() << error.line() << error;
+ }
+ QVERIFY(!qmlComponent->isError());
+
+ QQuickItem *rootItem = qobject_cast<QQuickItem *>(rootObject);
+ QVERIFY(rootItem);
+ static const QSize ITEM_SIZE = QSize(200, 200);
+ QCOMPARE(rootItem->size(), ITEM_SIZE);
+
+ quickWindow->contentItem()->setSize(rootItem->size());
+ quickWindow->setGeometry(0, 0, rootItem->width(), rootItem->height());
+
+ rootItem->setParentItem(quickWindow->contentItem());
+
+ const bool initSuccess = renderControl->initialize();
+
+ if (!initSuccess)
+ QSKIP("Could not initialize graphics, perhaps unsupported graphics API, skipping");
+
+ QRhi *rhi = renderControl->rhi();
+ Q_ASSERT(rhi);
+
+ const QSize size = rootItem->size().toSize();
+ QScopedPointer<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8, size, 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ QVERIFY(tex->create());
+
+ // In this test we use QRhiTexture to create resources but pull out the
+ // native VkImage and pass that in via fromVulkanImage(). This is to
+ // exercise additional features, such as setting a custom depth texture,
+ // which is not available (not relevant) when using fromRhiRenderTarget().
+ // In particular, this setup (fromVulkanImage + setDepthTexture) exercises
+ // what we get in Qt Quick 3D with OpenXR, where rendering happens into a
+ // XrSwapchain-provided color and depth texture. (granted, here we only
+ // exercise the non-multiview, non-MSAA case).
+
+ QScopedPointer<QRhiTexture> depthTex(rhi->newTexture(QRhiTexture::D24S8, size, 1, QRhiTexture::RenderTarget));
+ QVERIFY(depthTex->create());
+
+ QQuickRenderTarget rt = QQuickRenderTarget::fromVulkanImage(VkImage(tex->nativeTexture().object),
+ VkImageLayout(tex->nativeTexture().layout),
+ VK_FORMAT_R8G8B8A8_UNORM,
+ VK_FORMAT_R8G8B8A8_UNORM,
+ size,
+ 1,
+ 0,
+ {});
+ rt.setDepthTexture(depthTex.data());
+ quickWindow->setRenderTarget(rt);
+
+ QSize currentSize = size;
+
+ for (int frame = 0; frame < 100; ++frame) {
+ QCoreApplication::processEvents();
+
+ if (frame > 0)
+ animDriver->advance();
+
+ renderControl->polishItems();
+
+ renderControl->beginFrame();
+
+ renderControl->sync();
+ renderControl->render();
+
+ bool readCompleted = false;
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readCompleted, &readResult, &result, &rhi] {
+ readCompleted = true;
+ QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888_Premultiplied);
+ if (rhi->isYUpInFramebuffer())
+ result = wrapperImage.mirrored();
+ else
+ result = wrapperImage.copy();
+ };
+ QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
+ readbackBatch->readBackTexture(tex.data(), &readResult);
+ renderControl->commandBuffer()->resourceUpdate(readbackBatch);
+
+ renderControl->endFrame();
+
+ QVERIFY(readCompleted);
+
+ QImage img = result;
+ QVERIFY(!img.isNull());
+ QCOMPARE(img.size(), currentSize);
+
+ const int maxFuzz = 2;
+
+ // The scene is: background, rectangle, text
+ // where rectangle rotates
+
+ QRgb background = img.pixel(5, 5);
+
+ // with rect_depth.qml the following would not pass if the depth buffering
+ // was not functional (because the red rectangle with z: -1 would still
+ // go on top, not below)
+ QVERIFY(qAbs(qRed(background) - 70) < maxFuzz);
+ QVERIFY(qAbs(qGreen(background) - 130) < maxFuzz);
+ QVERIFY(qAbs(qBlue(background) - 180) < maxFuzz);
+
+ background = img.pixel(195, 195);
+ QVERIFY(qAbs(qRed(background) - 70) < maxFuzz);
+ QVERIFY(qAbs(qGreen(background) - 130) < maxFuzz);
+ QVERIFY(qAbs(qBlue(background) - 180) < maxFuzz);
+ }
+#else
+ QSKIP("No Vulkan support in Qt build, skipping native Vulkan test");
+#endif
+}
+
#include "tst_qquickrendercontrol.moc"
QTEST_MAIN(tst_RenderControl)
diff --git a/tests/auto/quick/qquickrepeater/CMakeLists.txt b/tests/auto/quick/qquickrepeater/CMakeLists.txt
index 473e58dd81..91a55a6609 100644
--- a/tests/auto/quick/qquickrepeater/CMakeLists.txt
+++ b/tests/auto/quick/qquickrepeater/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickrepeater Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickrepeater LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
index 5a6498a5d7..821770bae7 100644
--- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
+++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
@@ -94,12 +94,12 @@ tst_QQuickRepeater::tst_QQuickRepeater()
void tst_QQuickRepeater::numberModel()
{
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testData", 5);
- TestObject *testObject = new TestObject;
- ctxt->setContextProperty("testObject", testObject);
+ std::unique_ptr<TestObject> testObject = std::make_unique<TestObject>();
+ ctxt->setContextProperty("testObject", testObject.get());
window->setSource(testFileUrl("intmodel.qml"));
qApp->processEvents();
@@ -121,9 +121,6 @@ void tst_QQuickRepeater::numberModel()
ctxt->setContextProperty("testData", -1234);
QCOMPARE(repeater->parentItem()->childItems().size(), 1);
-
- delete testObject;
- delete window;
}
void tst_QQuickRepeater::objectList_data()
@@ -149,8 +146,11 @@ public:
void tst_QQuickRepeater::objectList()
{
QFETCH(QUrl, filename);
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
QObjectList data;
+ auto cleanup = qScopeGuard([&]() {
+ qDeleteAll(data);
+ });
for (int i=0; i<100; i++)
data << new MyObject(i);
@@ -175,9 +175,6 @@ void tst_QQuickRepeater::objectList()
ctxt->setContextProperty("testData", QVariant::fromValue(data));
QCOMPARE(addedSpy.size(), data.size());
QCOMPARE(removedSpy.size(), data.size());
-
- qDeleteAll(data);
- delete window;
}
/*
@@ -187,7 +184,7 @@ elements to test this.
*/
void tst_QQuickRepeater::stringList()
{
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
QStringList data;
data << "One";
@@ -233,16 +230,14 @@ void tst_QQuickRepeater::stringList()
}
}
QVERIFY(saw_repeater);
-
- delete window;
}
void tst_QQuickRepeater::dataModel_adding()
{
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
QQmlContext *ctxt = window->rootContext();
- TestObject *testObject = new TestObject;
- ctxt->setContextProperty("testObject", testObject);
+ std::unique_ptr<TestObject> testObject = std::make_unique<TestObject>();
+ ctxt->setContextProperty("testObject", testObject.get());
QaimModel testModel;
ctxt->setContextProperty("testData", &testModel);
@@ -307,18 +302,17 @@ void tst_QQuickRepeater::dataModel_adding()
addedSpy.clear();
countSpy.clear();
- delete testObject;
+ testObject.reset();
addedSpy.clear();
countSpy.clear();
- delete window;
}
void tst_QQuickRepeater::dataModel_removing()
{
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
QQmlContext *ctxt = window->rootContext();
- TestObject *testObject = new TestObject;
- ctxt->setContextProperty("testObject", testObject);
+ auto testObject = std::make_unique<TestObject>();
+ ctxt->setContextProperty("testObject", testObject.get());
QaimModel testModel;
testModel.addItem("one", "1");
@@ -376,17 +370,14 @@ void tst_QQuickRepeater::dataModel_removing()
QCOMPARE(removedSpy.at(0).at(0).toInt(), 1);
QCOMPARE(removedSpy.at(0).at(1).value<QQuickItem*>(), item);
removedSpy.clear();
-
- delete testObject;
- delete window;
}
void tst_QQuickRepeater::dataModel_changes()
{
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
QQmlContext *ctxt = window->rootContext();
- TestObject *testObject = new TestObject;
- ctxt->setContextProperty("testObject", testObject);
+ auto testObject = std::make_unique<TestObject>();
+ ctxt->setContextProperty("testObject", testObject.get());
QaimModel testModel;
testModel.addItem("one", "1");
@@ -416,17 +407,14 @@ void tst_QQuickRepeater::dataModel_changes()
text = findItem<QQuickText>(window->rootObject(), "myNumber", 1);
QVERIFY(text);
QCOMPARE(text->text(), QString("_2"));
-
- delete testObject;
- delete window;
}
void tst_QQuickRepeater::itemModel()
{
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
QQmlContext *ctxt = window->rootContext();
- TestObject *testObject = new TestObject;
- ctxt->setContextProperty("testObject", testObject);
+ auto testObject = std::make_unique<TestObject>();
+ ctxt->setContextProperty("testObject", testObject.get());
window->setSource(testFileUrl("itemlist.qml"));
qApp->processEvents();
@@ -464,14 +452,11 @@ void tst_QQuickRepeater::itemModel()
testObject->setUseModel(false);
QCOMPARE(container->childItems().size(), 1);
-
- delete testObject;
- delete window;
}
void tst_QQuickRepeater::resetModel()
{
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
QStringList dataA;
for (int i=0; i<10; i++)
@@ -533,8 +518,6 @@ void tst_QQuickRepeater::resetModel()
countSpy.clear();
removedSpy.clear();
addedSpy.clear();
-
- delete window;
}
// QTBUG-17156
@@ -543,7 +526,8 @@ void tst_QQuickRepeater::modelChanged()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("modelChanged.qml"));
- QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *rootObject = qobject_cast<QQuickItem*>(root.get());
QVERIFY(rootObject);
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
QVERIFY(repeater);
@@ -557,8 +541,6 @@ void tst_QQuickRepeater::modelChanged()
QCOMPARE(repeater->count(), 10);
QCOMPARE(repeater->property("itemsCount").toInt(), 10);
QCOMPARE(repeater->property("itemsFound").toList().size(), 10);
-
- delete rootObject;
}
void tst_QQuickRepeater::modelReset()
@@ -645,7 +627,8 @@ void tst_QQuickRepeater::modelCleared()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("modelCleared.qml"));
- QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *rootObject = qobject_cast<QQuickItem*>(root.get());
QVERIFY(rootObject);
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
@@ -655,8 +638,6 @@ void tst_QQuickRepeater::modelCleared()
QQmlTestMessageHandler messageHandler;
repeater->setModel(0);
QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
-
- delete rootObject;
}
void tst_QQuickRepeater::properties()
@@ -664,7 +645,8 @@ void tst_QQuickRepeater::properties()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("properties.qml"));
- QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *rootObject = qobject_cast<QQuickItem*>(root.get());
QVERIFY(rootObject);
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
@@ -685,13 +667,11 @@ void tst_QQuickRepeater::properties()
QCOMPARE(delegateSpy.size(),1);
repeater->setDelegate(&rectComponent);
QCOMPARE(delegateSpy.size(),1);
-
- delete rootObject;
}
void tst_QQuickRepeater::asynchronous()
{
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
window->show();
QQmlIncubationController controller;
window->engine()->setIncubationController(&controller);
@@ -742,8 +722,6 @@ void tst_QQuickRepeater::asynchronous()
QQuickItem *item = findItem<QQuickItem>(container, name);
QTRY_COMPARE(item->y(), i * 50.0);
}
-
- delete window;
}
void tst_QQuickRepeater::initParent()
@@ -751,7 +729,8 @@ void tst_QQuickRepeater::initParent()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("initparent.qml"));
- QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *rootObject = qobject_cast<QQuickItem*>(root.get());
QVERIFY(rootObject);
QCOMPARE(qvariant_cast<QQuickItem*>(rootObject->property("parentItem")), rootObject);
@@ -763,7 +742,8 @@ void tst_QQuickRepeater::dynamicModelCrash()
QQmlComponent component(&engine, testFileUrl("dynamicmodelcrash.qml"));
// Don't crash
- QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *rootObject = qobject_cast<QQuickItem*>(root.get());
QVERIFY(rootObject);
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "rep");
@@ -776,10 +756,9 @@ void tst_QQuickRepeater::visualItemModelCrash()
// This used to crash because the model would get
// deleted before the repeater, leading to double-deletion
// of the items.
- QQuickView *window = createView();
+ std::unique_ptr<QQuickView> window { createView() };
window->setSource(testFileUrl("visualitemmodel.qml"));
qApp->processEvents();
- delete window;
}
class BadModel : public QAbstractListModel
@@ -859,7 +838,8 @@ void tst_QQuickRepeater::clearRemovalOrder()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("clearremovalorder.qml"));
- QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *rootObject = qobject_cast<QQuickItem*>(root.get());
QVERIFY(rootObject);
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
@@ -883,8 +863,6 @@ void tst_QQuickRepeater::clearRemovalOrder()
QCOMPARE(removedSpy.at(0).at(0).toInt(), 2);
QCOMPARE(removedSpy.at(1).at(0).toInt(), 1);
QCOMPARE(removedSpy.at(2).at(0).toInt(), 0);
-
- delete rootObject;
}
void tst_QQuickRepeater::destroyCount()
@@ -892,7 +870,8 @@ void tst_QQuickRepeater::destroyCount()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("destroycount.qml"));
- QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *rootObject = qobject_cast<QQuickItem*>(root.get());
QVERIFY(rootObject);
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
@@ -927,7 +906,8 @@ void tst_QQuickRepeater::stackingOrder()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("stackingorder.qml"));
- QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *rootObject = qobject_cast<QQuickItem*>(root.get());
QVERIFY(rootObject);
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
@@ -956,7 +936,8 @@ void tst_QQuickRepeater::objectModel()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("objectmodel.qml"));
- QQuickItem *positioner = qobject_cast<QQuickItem *>(component.create());
+ std::unique_ptr<QObject> root { component.create() };
+ QQuickItem *positioner = qobject_cast<QQuickItem *>(root.get());
QVERIFY(positioner);
QQuickRepeater *repeater = findItem<QQuickRepeater>(positioner, "repeater");
@@ -994,8 +975,6 @@ void tst_QQuickRepeater::objectModel()
model->clear();
QCOMPARE(model->count(), 0);
QCOMPARE(repeater->count(), 0);
-
- delete positioner;
}
class Ctrl : public QObject
@@ -1012,7 +991,7 @@ public:
void tst_QQuickRepeater::QTBUG54859_asynchronousMove()
{
Ctrl ctrl;
- QQuickView* view = createView();
+ std::unique_ptr<QQuickView> view { createView() };
view->rootContext()->setContextProperty("ctrl", &ctrl);
view->setSource(testFileUrl("asynchronousMove.qml"));
view->show();
@@ -1077,8 +1056,7 @@ void tst_QQuickRepeater::ownership()
QVERIFY(!QQmlData::keepAliveDuringGarbageCollection(aim.get()));
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ gc(engine);
QVERIFY(modelGuard);
@@ -1096,8 +1074,7 @@ void tst_QQuickRepeater::ownership()
QVERIFY(!QQmlData::keepAliveDuringGarbageCollection(delegate.get()));
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ gc(engine);
QVERIFY(delegateGuard);
@@ -1110,8 +1087,7 @@ void tst_QQuickRepeater::ownership()
delegate.release();
aim.release();
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ gc(engine);
QVERIFY(!delegateGuard);
QVERIFY(!modelGuard);
diff --git a/tests/auto/quick/qquickitemrhiintegration/BLACKLIST b/tests/auto/quick/qquickrhiitem/BLACKLIST
index 03e5bc8c1d..03e5bc8c1d 100644
--- a/tests/auto/quick/qquickitemrhiintegration/BLACKLIST
+++ b/tests/auto/quick/qquickrhiitem/BLACKLIST
diff --git a/tests/auto/quick/qquickrhiitem/CMakeLists.txt b/tests/auto/quick/qquickrhiitem/CMakeLists.txt
new file mode 100644
index 0000000000..ef9858ea6d
--- /dev/null
+++ b/tests/auto/quick/qquickrhiitem/CMakeLists.txt
@@ -0,0 +1,52 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickrhiitem LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qquickrhiitem
+ SOURCES
+ tst_qquickrhiitem.cpp
+ testrhiitem.cpp testrhiitem.h
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickPrivate
+ Qt::QuickTestUtilsPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qquickrhiitem CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qquickrhiitem CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
+
+qt_internal_add_shaders(tst_qquickrhiitem "shaders"
+ PREFIX
+ "/"
+ FILES
+ "texture.vert"
+ "texture.frag"
+)
+
+qt_policy(SET QTP0001 NEW)
+
+qt_add_qml_module(tst_qquickrhiitem
+ URI Testqquickrhiitem
+)
diff --git a/tests/auto/quick/qquickrhiitem/data/test.qml b/tests/auto/quick/qquickrhiitem/data/test.qml
new file mode 100644
index 0000000000..92fbfdb016
--- /dev/null
+++ b/tests/auto/quick/qquickrhiitem/data/test.qml
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import Testqquickrhiitem
+
+Item {
+ width: 640
+ height: 480
+
+ Text {
+ id: apiInfo
+ color: "black"
+ font.pixelSize: 16
+ property int api: GraphicsInfo.api
+ text: {
+ if (GraphicsInfo.api === GraphicsInfo.OpenGL)
+ "OpenGL on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Direct3D11)
+ "D3D11 on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Direct3D12)
+ "D3D12 on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Vulkan)
+ "Vulkan on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Metal)
+ "Metal on QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Null)
+ "Null on QRhi";
+ else
+ "Unknown API";
+ }
+ }
+
+ TestRhiItem {
+ anchors.centerIn: parent
+ width: 400
+ height: 400
+ color: "red"
+ objectName: "rhiitem"
+ }
+}
diff --git a/tests/auto/quick/qquickrhiitem/testrhiitem.cpp b/tests/auto/quick/qquickrhiitem/testrhiitem.cpp
new file mode 100644
index 0000000000..65fc329630
--- /dev/null
+++ b/tests/auto/quick/qquickrhiitem/testrhiitem.cpp
@@ -0,0 +1,157 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "testrhiitem.h"
+#include <QFile>
+
+static const QSize TEX_SIZE(512, 512);
+
+void TestRenderer::initialize(QRhiCommandBuffer *)
+{
+ if (m_rhi != rhi()) {
+ m_rhi = rhi();
+ scene = {};
+ }
+
+ if (m_sampleCount != renderTarget()->sampleCount()) {
+ m_sampleCount = renderTarget()->sampleCount();
+ scene = {};
+ }
+
+ if (!scene.vbuf)
+ initScene();
+
+ const QSize outputSize = renderTarget()->pixelSize();
+ scene.mvp = m_rhi->clipSpaceCorrMatrix();
+ scene.mvp.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
+ scene.mvp.translate(0, 0, -4);
+ if (!scene.resourceUpdates)
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.get(), 0, 64, scene.mvp.constData());
+}
+
+static QShader getShader(const QString &name)
+{
+ QFile f(name);
+ if (f.open(QIODevice::ReadOnly))
+ return QShader::fromSerialized(f.readAll());
+
+ return QShader();
+}
+
+void TestRenderer::updateTexture()
+{
+ QImage img(TEX_SIZE, QImage::Format_RGBA8888);
+ img.fill(itemData.color);
+
+ if (!scene.resourceUpdates)
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ scene.resourceUpdates->uploadTexture(scene.tex.get(), img);
+}
+
+void TestRenderer::initScene()
+{
+ static const float tri[] = {
+ 0.0f, 0.5f, 0.0f, 1.0f,
+ -0.5f, -0.5f, 0.0f, 0.0f,
+ 0.5f, -0.5f, 1.0f, 0.0f
+ };
+
+ scene.vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(tri)));
+ scene.vbuf->create();
+
+ scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ scene.resourceUpdates->uploadStaticBuffer(scene.vbuf.get(), tri);
+
+ scene.ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68));
+ scene.ubuf->create();
+
+ const qint32 flip = m_rhi->isYUpInFramebuffer() ? 1 : 0;
+ scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.get(), 64, 4, &flip);
+
+ scene.tex.reset(m_rhi->newTexture(QRhiTexture::RGBA8, TEX_SIZE));
+ scene.tex->create();
+
+ scene.sampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
+ scene.sampler->create();
+
+ scene.srb.reset(m_rhi->newShaderResourceBindings());
+ scene.srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.ubuf.get()),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.tex.get(), scene.sampler.get())
+ });
+ scene.srb->create();
+
+ scene.ps.reset(m_rhi->newGraphicsPipeline());
+ scene.ps->setDepthTest(true);
+ scene.ps->setDepthWrite(true);
+ scene.ps->setDepthOp(QRhiGraphicsPipeline::Less);
+ scene.ps->setCullMode(QRhiGraphicsPipeline::Back);
+ scene.ps->setFrontFace(QRhiGraphicsPipeline::CCW);
+ QShader vs = getShader(QLatin1String(":/texture.vert.qsb"));
+ Q_ASSERT(vs.isValid());
+ QShader fs = getShader(QLatin1String(":/texture.frag.qsb"));
+ Q_ASSERT(fs.isValid());
+ scene.ps->setShaderStages({
+ { QRhiShaderStage::Vertex, vs },
+ { QRhiShaderStage::Fragment, fs }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 4 * sizeof(float) },
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
+ });
+ scene.ps->setVertexInputLayout(inputLayout);
+ scene.ps->setSampleCount(m_sampleCount);
+ scene.ps->setShaderResourceBindings(scene.srb.get());
+ scene.ps->setRenderPassDescriptor(renderTarget()->renderPassDescriptor());
+ scene.ps->create();
+
+ updateTexture();
+}
+
+void TestRenderer::synchronize(QQuickRhiItem *rhiItem)
+{
+ TestRhiItem *item = static_cast<TestRhiItem *>(rhiItem);
+ if (item->color() != itemData.color) {
+ itemData.color = item->color();
+ updateTexture();
+ }
+}
+
+void TestRenderer::render(QRhiCommandBuffer *cb)
+{
+ QRhiResourceUpdateBatch *rub = scene.resourceUpdates;
+ if (rub)
+ scene.resourceUpdates = nullptr;
+
+ const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f);
+ cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, rub);
+
+ cb->setGraphicsPipeline(scene.ps.get());
+ const QSize outputSize = renderTarget()->pixelSize();
+ cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { scene.vbuf.get(), 0 },
+ };
+ cb->setVertexInput(0, 1, vbufBindings);
+ cb->draw(3);
+
+ cb->endPass();
+}
+
+void TestRhiItem::setColor(QColor c)
+{
+ if (m_color == c)
+ return;
+
+ m_color = c;
+ emit colorChanged();
+ update();
+}
diff --git a/tests/auto/quick/qquickrhiitem/testrhiitem.h b/tests/auto/quick/qquickrhiitem/testrhiitem.h
new file mode 100644
index 0000000000..6778f96d7b
--- /dev/null
+++ b/tests/auto/quick/qquickrhiitem/testrhiitem.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TESTRHIITEM_H
+#define TESTRHIITEM_H
+
+#include <QQuickRhiItem>
+#include <rhi/qrhi.h>
+
+class TestRenderer : public QQuickRhiItemRenderer
+{
+protected:
+ void initialize(QRhiCommandBuffer *cb) override;
+ void synchronize(QQuickRhiItem *item) override;
+ void render(QRhiCommandBuffer *cb) override;
+
+private:
+ QRhi *m_rhi = nullptr;
+ int m_sampleCount = 1;
+
+ struct {
+ QRhiResourceUpdateBatch *resourceUpdates = nullptr;
+ std::unique_ptr<QRhiBuffer> vbuf;
+ std::unique_ptr<QRhiBuffer> ubuf;
+ std::unique_ptr<QRhiShaderResourceBindings> srb;
+ std::unique_ptr<QRhiGraphicsPipeline> ps;
+ std::unique_ptr<QRhiSampler> sampler;
+ std::unique_ptr<QRhiTexture> tex;
+ QMatrix4x4 mvp;
+ } scene;
+
+ struct {
+ QColor color;
+ } itemData;
+
+ void initScene();
+ void updateTexture();
+
+ friend class tst_QQuickRhiItem;
+};
+
+class TestRhiItem : public QQuickRhiItem
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(TestRhiItem)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+
+public:
+ QQuickRhiItemRenderer *createRenderer() override { return new TestRenderer; }
+
+ QColor color() const { return m_color; }
+ void setColor(QColor c);
+
+signals:
+ void colorChanged();
+
+private:
+ QColor m_color;
+};
+
+#endif
diff --git a/tests/auto/quick/qquickitemrhiintegration/texture.frag b/tests/auto/quick/qquickrhiitem/texture.frag
index a7b8bce0ad..a7b8bce0ad 100644
--- a/tests/auto/quick/qquickitemrhiintegration/texture.frag
+++ b/tests/auto/quick/qquickrhiitem/texture.frag
diff --git a/tests/auto/quick/qquickitemrhiintegration/texture.vert b/tests/auto/quick/qquickrhiitem/texture.vert
index bf678d1e9b..bf678d1e9b 100644
--- a/tests/auto/quick/qquickitemrhiintegration/texture.vert
+++ b/tests/auto/quick/qquickrhiitem/texture.vert
diff --git a/tests/auto/quick/qquickrhiitem/tst_qquickrhiitem.cpp b/tests/auto/quick/qquickrhiitem/tst_qquickrhiitem.cpp
new file mode 100644
index 0000000000..e6c5b079ca
--- /dev/null
+++ b/tests/auto/quick/qquickrhiitem/tst_qquickrhiitem.cpp
@@ -0,0 +1,150 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <qtest.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QQuickView>
+#include <QSGRendererInterface>
+#include <private/qsgrhisupport_p.h>
+#include <private/qquickrhiitem_p.h>
+#include "testrhiitem.h"
+
+class tst_QQuickRhiItem : public QQmlDataTest
+{
+ Q_OBJECT
+
+public:
+ tst_QQuickRhiItem();
+
+private slots:
+ void initTestCase() override;
+ void cleanupTestCase();
+ void rhiItem();
+ void properties();
+};
+
+tst_QQuickRhiItem::tst_QQuickRhiItem()
+ : QQmlDataTest(QT_QMLTEST_DATADIR)
+{
+}
+
+void tst_QQuickRhiItem::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ qDebug() << "RHI backend:" << QSGRhiSupport::instance()->rhiBackendName();
+}
+
+void tst_QQuickRhiItem::cleanupTestCase()
+{
+}
+
+void tst_QQuickRhiItem::rhiItem()
+{
+ if (QGuiApplication::platformName() == QLatin1String("offscreen")
+ || QGuiApplication::platformName() == QLatin1String("minimal"))
+ {
+ QSKIP("Skipping on offscreen/minimal");
+ }
+
+ // Load up a scene that instantiates a TestRhiItem, which in turn
+ // renders a triangle with QRhi into a QRhiTexture and then draws
+ // a quad textured with it.
+
+ QQuickView view;
+ view.setSource(testFileUrl(QLatin1String("test.qml")));
+ view.setResizeMode(QQuickView::SizeViewToRootObject);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (!QSGRendererInterface::isApiRhiBased(view.rendererInterface()->graphicsApi()))
+ QSKIP("Scenegraph does not use QRhi, test is pointless");
+
+ QImage result = view.grabWindow();
+ QVERIFY(!result.isNull());
+
+ const int tolerance = 5;
+
+ // The result is a red triangle in the center of the view, confirm at least one pixel.
+ QRgb a = result.pixel(result.width() / 2, result.height() / 2);
+ QRgb b = QColor(Qt::red).rgb();
+ QVERIFY(qAbs(qRed(a) - qRed(b)) <= tolerance
+ && qAbs(qGreen(a) - qGreen(b)) <= tolerance
+ && qAbs(qBlue(a) - qBlue(b)) <= tolerance);
+}
+
+void tst_QQuickRhiItem::properties()
+{
+ if (QGuiApplication::platformName() == QLatin1String("offscreen")
+ || QGuiApplication::platformName() == QLatin1String("minimal"))
+ {
+ QSKIP("Skipping on offscreen/minimal");
+ }
+
+ QQuickView view;
+ view.setSource(testFileUrl(QLatin1String("test.qml")));
+ view.setResizeMode(QQuickView::SizeViewToRootObject);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (!QSGRendererInterface::isApiRhiBased(view.rendererInterface()->graphicsApi()))
+ QSKIP("Scenegraph does not use QRhi, test is pointless");
+
+ QQuickRhiItem *item = view.rootObject()->findChild<QQuickRhiItem *>("rhiitem");
+ QVERIFY(item);
+
+ view.grabWindow(); // to ensure there's a frame
+ // not quite safe in theory (threads etc.) but we know it works in practice
+ QQuickRhiItemPrivate *d = QQuickRhiItemPrivate::get(item);
+ QVERIFY(d->node);
+ TestRenderer *r = static_cast<TestRenderer *>(d->node->m_renderer.get());
+ QVERIFY(r);
+ QRhi *rhi = r->rhi();
+ QVERIFY(rhi);
+
+ QVERIFY(r->colorTexture());
+ QVERIFY(!r->msaaColorBuffer());
+ QVERIFY(r->depthStencilBuffer());
+ QVERIFY(r->renderTarget());
+ QCOMPARE(item->effectiveColorBufferSize(), r->colorTexture()->pixelSize());
+
+ QCOMPARE(item->sampleCount(), 1);
+ item->setSampleCount(4);
+
+ view.grabWindow(); // just to ensure the render thread rendered with the changed properties
+
+ if (rhi->supportedSampleCounts().contains(4)) {
+ QVERIFY(!r->colorTexture());
+ QVERIFY(r->msaaColorBuffer());
+ QCOMPARE(r->msaaColorBuffer()->sampleCount(), 4);
+ QCOMPARE(r->depthStencilBuffer()->sampleCount(), 4);
+ QCOMPARE(item->effectiveColorBufferSize(), r->msaaColorBuffer()->pixelSize());
+ }
+
+ QCOMPARE(item->alphaBlending(), false);
+ item->setAlphaBlending(true);
+
+ QCOMPARE(item->isMirrorVerticallyEnabled(), false);
+ item->setMirrorVertically(true);
+
+ item->setSampleCount(1);
+ item->setFixedColorBufferWidth(123);
+ item->setFixedColorBufferHeight(456);
+ view.grabWindow();
+ QCOMPARE(r->colorTexture()->pixelSize(), QSize(123, 456));
+ QCOMPARE(item->fixedColorBufferWidth(), 123);
+ QCOMPARE(item->fixedColorBufferHeight(), 456);
+ QCOMPARE(item->effectiveColorBufferSize(), QSize(123, 456));
+
+ QImage result = view.grabWindow();
+ QVERIFY(!result.isNull());
+ const int tolerance = 5;
+ QRgb a = result.pixel(result.width() / 2, result.height() / 2);
+ QRgb b = QColor(Qt::red).rgb();
+ QVERIFY(qAbs(qRed(a) - qRed(b)) <= tolerance
+ && qAbs(qGreen(a) - qGreen(b)) <= tolerance
+ && qAbs(qBlue(a) - qBlue(b)) <= tolerance);
+}
+
+#include "tst_qquickrhiitem.moc"
+
+QTEST_MAIN(tst_QQuickRhiItem)
diff --git a/tests/auto/quick/qquickscreen/CMakeLists.txt b/tests/auto/quick/qquickscreen/CMakeLists.txt
index 8586992d3d..0b269c736c 100644
--- a/tests/auto/quick/qquickscreen/CMakeLists.txt
+++ b/tests/auto/quick/qquickscreen/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickscreen Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickscreen LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp
index ccdf69facf..74d50eed22 100644
--- a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp
+++ b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
diff --git a/tests/auto/quick/qquickshadereffect/CMakeLists.txt b/tests/auto/quick/qquickshadereffect/CMakeLists.txt
index a87269d7a7..9aa9140337 100644
--- a/tests/auto/quick/qquickshadereffect/CMakeLists.txt
+++ b/tests/auto/quick/qquickshadereffect/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickshadereffect Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickshadereffect LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qquickshadereffect
SOURCES
tst_qquickshadereffect.cpp
diff --git a/tests/auto/quick/qquickshadereffect/data/MyIcon.qml b/tests/auto/quick/qquickshadereffect/data/MyIcon.qml
index b67f0cbc3f..f48b3698e1 100644
--- a/tests/auto/quick/qquickshadereffect/data/MyIcon.qml
+++ b/tests/auto/quick/qquickshadereffect/data/MyIcon.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
diff --git a/tests/auto/quick/qquickshadereffect/data/deleteShaderEffectSource.qml b/tests/auto/quick/qquickshadereffect/data/deleteShaderEffectSource.qml
index cac63d4903..eee1e212e7 100644
--- a/tests/auto/quick/qquickshadereffect/data/deleteShaderEffectSource.qml
+++ b/tests/auto/quick/qquickshadereffect/data/deleteShaderEffectSource.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/quick/qquickshadereffect/data/deleteSourceItem.qml b/tests/auto/quick/qquickshadereffect/data/deleteSourceItem.qml
index bf21d6a73b..88e44bab68 100644
--- a/tests/auto/quick/qquickshadereffect/data/deleteSourceItem.qml
+++ b/tests/auto/quick/qquickshadereffect/data/deleteSourceItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/auto/quick/qquickshadereffect/data/hideParent.qml b/tests/auto/quick/qquickshadereffect/data/hideParent.qml
index 57ff9ad2ae..2566cc276e 100644
--- a/tests/auto/quick/qquickshadereffect/data/hideParent.qml
+++ b/tests/auto/quick/qquickshadereffect/data/hideParent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/qquickshadereffect/data/testProperties.qml b/tests/auto/quick/qquickshadereffect/data/testProperties.qml
index 3962e9507b..e513cf0406 100644
--- a/tests/auto/quick/qquickshadereffect/data/testProperties.qml
+++ b/tests/auto/quick/qquickshadereffect/data/testProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml b/tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml
index 023c8b39ae..b32dc7fbb6 100644
--- a/tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml
+++ b/tests/auto/quick/qquickshadereffect/data/twoImagesOneShaderEffect.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
index 8029e30e25..27cf89407a 100644
--- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
+++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
diff --git a/tests/auto/quick/qquickshape/CMakeLists.txt b/tests/auto/quick/qquickshape/CMakeLists.txt
index f53f87b10f..b7b536e29d 100644
--- a/tests/auto/quick/qquickshape/CMakeLists.txt
+++ b/tests/auto/quick/qquickshape/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickshape Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickshape LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickshape/data/multiline.qml b/tests/auto/quick/qquickshape/data/multiline.qml
index 0556a9c3d0..edf92057c2 100644
--- a/tests/auto/quick/qquickshape/data/multiline.qml
+++ b/tests/auto/quick/qquickshape/data/multiline.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
diff --git a/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml b/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml
index 631db82ba5..64c61f4331 100644
--- a/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml
+++ b/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
diff --git a/tests/auto/quick/qquickshape/data/pathitem7.qml b/tests/auto/quick/qquickshape/data/pathitem7.qml
index 0b6637bbf0..43b42ecabc 100644
--- a/tests/auto/quick/qquickshape/data/pathitem7.qml
+++ b/tests/auto/quick/qquickshape/data/pathitem7.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import tst_qquickpathitem 1.0
diff --git a/tests/auto/quick/qquickshape/data/pathitem8.qml b/tests/auto/quick/qquickshape/data/pathitem8.qml
index faa73a0f2d..8b78dc9429 100644
--- a/tests/auto/quick/qquickshape/data/pathitem8.qml
+++ b/tests/auto/quick/qquickshape/data/pathitem8.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
diff --git a/tests/auto/quick/qquickshape/data/polyline.qml b/tests/auto/quick/qquickshape/data/polyline.qml
index cbb1822102..9c81bf4031 100644
--- a/tests/auto/quick/qquickshape/data/polyline.qml
+++ b/tests/auto/quick/qquickshape/data/polyline.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
index a9e15939d3..f846cc4e4f 100644
--- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp
+++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuick/qquickview.h>
diff --git a/tests/auto/quick/qquickshortcut/CMakeLists.txt b/tests/auto/quick/qquickshortcut/CMakeLists.txt
index 4541b08240..1056534634 100644
--- a/tests/auto/quick/qquickshortcut/CMakeLists.txt
+++ b/tests/auto/quick/qquickshortcut/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickshortcut Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickshortcut LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickshortcut/data/embedded.qml b/tests/auto/quick/qquickshortcut/data/embedded.qml
new file mode 100644
index 0000000000..4b80363b70
--- /dev/null
+++ b/tests/auto/quick/qquickshortcut/data/embedded.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+ id: root
+
+ width: 300
+ height: 300
+
+ property bool activated: false
+ property alias shortcut: shortcut
+
+ Shortcut {
+ id: shortcut
+ onActivated: root.activated = true
+ }
+}
diff --git a/tests/auto/quick/qquickshortcut/data/multiple.qml b/tests/auto/quick/qquickshortcut/data/multiple.qml
index 98554846c5..0ecfbc44fc 100644
--- a/tests/auto/quick/qquickshortcut/data/multiple.qml
+++ b/tests/auto/quick/qquickshortcut/data/multiple.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
import QtQuick.Window 2.2
diff --git a/tests/auto/quick/qquickshortcut/data/shortcuts.qml b/tests/auto/quick/qquickshortcut/data/shortcuts.qml
index 02759396fa..cf88c93eee 100644
--- a/tests/auto/quick/qquickshortcut/data/shortcuts.qml
+++ b/tests/auto/quick/qquickshortcut/data/shortcuts.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.5
import QtQuick.Window 2.2
diff --git a/tests/auto/quick/qquickshortcut/data/shortcutsRect.qml b/tests/auto/quick/qquickshortcut/data/shortcutsRect.qml
index c367021cf0..80d337aeda 100644
--- a/tests/auto/quick/qquickshortcut/data/shortcutsRect.qml
+++ b/tests/auto/quick/qquickshortcut/data/shortcutsRect.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.5
diff --git a/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp b/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
index 6ed18c2ee1..312f77e99a 100644
--- a/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
+++ b/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
@@ -1,11 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtQuick/qquickwindow.h>
#include <QtQml/qqmlapplicationengine.h>
#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
#include <QtTest/qsignalspy.h>
+#ifdef QT_WIDGETS_LIB
+#include <QtWidgets/qboxlayout.h>
+#endif
#ifdef QT_QUICKWIDGETS_LIB
#include <QtQuickWidgets/qquickwidget.h>
#endif
@@ -34,6 +38,8 @@ private slots:
void matcher();
void multiple_data();
void multiple();
+ void embedded_data();
+ void embedded();
#ifdef QT_QUICKWIDGETS_LIB
void quickWidgetShortcuts_data();
void quickWidgetShortcuts();
@@ -577,6 +583,87 @@ void tst_QQuickShortcut::contextChange()
QCOMPARE(inactiveWindow->property("activated").toBool(), activated);
}
+void tst_QQuickShortcut::embedded_data()
+{
+ QTest::addColumn<Qt::Key>("testKey");
+ QTest::addColumn<Qt::KeyboardModifiers>("testModifiers");
+ QTest::addColumn<QString>("windowShortcutSequence");
+ QTest::addColumn<bool>("windowShortcutActivated");
+
+ QTest::newRow("windowActivated") << Qt::Key_W << (Qt::ControlModifier|Qt::AltModifier)
+ << "Ctrl+Alt+W" << true;
+}
+
+void tst_QQuickShortcut::embedded()
+{
+#ifndef QT_WIDGETS_LIB
+ QSKIP("Skipping due to QT_WIDGETS_LIB is not defined");
+#else
+ QFETCH(Qt::Key, testKey);
+ QFETCH(Qt::KeyboardModifiers, testModifiers);
+ QFETCH(QString, windowShortcutSequence);
+ QFETCH(bool, windowShortcutActivated);
+
+ QWidget window;
+ QVBoxLayout *layout = new QVBoxLayout {&window};
+ QQuickView *quickView = new QQuickView;
+ quickView->setResizeMode(QQuickView::SizeRootObjectToView);
+ quickView->setSource(testFileUrl("embedded.qml"));
+
+ QWidget *container = QWidget::createWindowContainer(quickView);
+ container->setMinimumSize(quickView->size());
+ container->setFocusPolicy(Qt::TabFocus);
+
+ QWidget *widget = new QWidget;
+ // We will set focus to the widget and its default is NoFocus.
+ widget->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
+
+ layout->addWidget(widget);
+ layout->addWidget(container);
+
+ window.show();
+ QTRY_VERIFY(window.isVisible());
+ // The widget can get focused only when the including window has exposed.
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ widget->setFocus();
+ QTRY_VERIFY(widget->hasFocus());
+
+ // If the window-context shortcut is expected to be activated,
+ // then the QuickView window needs to be active.
+ // On Linux, the embedded QuickView window is active immediately
+ // after the containing window is active, but on Windows,
+ // the embedded QuickView window is activated after a delay
+ // once the containing window is active.
+ if (windowShortcutActivated)
+ QVERIFY(QTest::qWaitForWindowActive(quickView));
+
+ QQuickItem *item = quickView->rootObject();
+ QVERIFY(item);
+
+ QObject *windowShortcut = item->property("shortcut").value<QObject *>();
+ QVERIFY(windowShortcut);
+
+ windowShortcut->setProperty("context", Qt::WindowShortcut);
+ windowShortcut->setProperty("sequence", windowShortcutSequence);
+
+ QTest::keyPress(&window, testKey, testModifiers);
+ QTest::keyRelease(&window, testKey, testModifiers);
+ QCOMPARE(item->property("activated").toBool(), windowShortcutActivated);
+
+ quickView->requestActivate();
+ QTRY_VERIFY(quickView->isActive());
+ QVERIFY(quickView->isActive());
+
+ item->setProperty("activated", false);
+ QVERIFY(!item->property("activated").toBool());
+
+ QTest::keyPress(&window, testKey, testModifiers);
+ QTest::keyRelease(&window, testKey, testModifiers);
+ QCOMPARE(item->property("activated").toBool(), true);
+#endif
+}
+
#ifdef QT_QUICKWIDGETS_LIB
void tst_QQuickShortcut::quickWidgetShortcuts_data()
{
diff --git a/tests/auto/quick/qquicksmoothedanimation/CMakeLists.txt b/tests/auto/quick/qquicksmoothedanimation/CMakeLists.txt
index 8854ef2081..2a277441c4 100644
--- a/tests/auto/quick/qquicksmoothedanimation/CMakeLists.txt
+++ b/tests/auto/quick/qquicksmoothedanimation/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicksmoothedanimation Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicksmoothedanimation LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicksmoothedanimation/tst_qquicksmoothedanimation.cpp b/tests/auto/quick/qquicksmoothedanimation/tst_qquicksmoothedanimation.cpp
index 8fed114b26..7bd1b09d0f 100644
--- a/tests/auto/quick/qquicksmoothedanimation/tst_qquicksmoothedanimation.cpp
+++ b/tests/auto/quick/qquicksmoothedanimation/tst_qquicksmoothedanimation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
diff --git a/tests/auto/quick/qquickspringanimation/CMakeLists.txt b/tests/auto/quick/qquickspringanimation/CMakeLists.txt
index 5faedbfb87..0fa7b717c5 100644
--- a/tests/auto/quick/qquickspringanimation/CMakeLists.txt
+++ b/tests/auto/quick/qquickspringanimation/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickspringanimation Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickspringanimation LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickspringanimation/tst_qquickspringanimation.cpp b/tests/auto/quick/qquickspringanimation/tst_qquickspringanimation.cpp
index 65b4ecacc0..f78157ab26 100644
--- a/tests/auto/quick/qquickspringanimation/tst_qquickspringanimation.cpp
+++ b/tests/auto/quick/qquickspringanimation/tst_qquickspringanimation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
diff --git a/tests/auto/quick/qquickspritesequence/CMakeLists.txt b/tests/auto/quick/qquickspritesequence/CMakeLists.txt
index 619e8c2f9d..c492a16183 100644
--- a/tests/auto/quick/qquickspritesequence/CMakeLists.txt
+++ b/tests/auto/quick/qquickspritesequence/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickspritesequence Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickspritesequence LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickspritesequence/data/advance.qml b/tests/auto/quick/qquickspritesequence/data/advance.qml
index a12db397ad..2242c28705 100644
--- a/tests/auto/quick/qquickspritesequence/data/advance.qml
+++ b/tests/auto/quick/qquickspritesequence/data/advance.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickspritesequence/data/basic.qml b/tests/auto/quick/qquickspritesequence/data/basic.qml
index b01085f3ed..c2caad4f6e 100644
--- a/tests/auto/quick/qquickspritesequence/data/basic.qml
+++ b/tests/auto/quick/qquickspritesequence/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickspritesequence/data/crashonstart.qml b/tests/auto/quick/qquickspritesequence/data/crashonstart.qml
index 37bc137133..cee94fdf91 100644
--- a/tests/auto/quick/qquickspritesequence/data/crashonstart.qml
+++ b/tests/auto/quick/qquickspritesequence/data/crashonstart.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//QTBUG-24797
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickspritesequence/data/huge.qml b/tests/auto/quick/qquickspritesequence/data/huge.qml
index c30d493459..58d365ec84 100644
--- a/tests/auto/quick/qquickspritesequence/data/huge.qml
+++ b/tests/auto/quick/qquickspritesequence/data/huge.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickspritesequence/data/spriteaftergoal.qml b/tests/auto/quick/qquickspritesequence/data/spriteaftergoal.qml
index 7d8c8a78cc..eeee289b54 100644
--- a/tests/auto/quick/qquickspritesequence/data/spriteaftergoal.qml
+++ b/tests/auto/quick/qquickspritesequence/data/spriteaftergoal.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// QTBUG-40595
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickspritesequence/data/spritebeforegoal.qml b/tests/auto/quick/qquickspritesequence/data/spritebeforegoal.qml
index 2d6a18e1e7..b1695ff820 100644
--- a/tests/auto/quick/qquickspritesequence/data/spritebeforegoal.qml
+++ b/tests/auto/quick/qquickspritesequence/data/spritebeforegoal.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// QTBUG-40595
import QtQuick 2.0
diff --git a/tests/auto/quick/qquickspritesequence/tst_qquickspritesequence.cpp b/tests/auto/quick/qquickspritesequence/tst_qquickspritesequence.cpp
index cdfbe7707f..ffaa45449f 100644
--- a/tests/auto/quick/qquickspritesequence/tst_qquickspritesequence.cpp
+++ b/tests/auto/quick/qquickspritesequence/tst_qquickspritesequence.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuick/qquickview.h>
diff --git a/tests/auto/quick/qquickstates/CMakeLists.txt b/tests/auto/quick/qquickstates/CMakeLists.txt
index 672d30ff69..20141a489b 100644
--- a/tests/auto/quick/qquickstates/CMakeLists.txt
+++ b/tests/auto/quick/qquickstates/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickstates Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickstates LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -26,6 +32,21 @@ qt_internal_add_test(tst_qquickstates
TESTDATA ${test_data}
)
+qt_internal_add_test(tst_qquickstates_no_deferred_properties
+ SOURCES
+ tst_qquickstates.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickPrivate
+ Qt::QuickTestUtilsPrivate
+ TESTDATA ${test_data}
+ DEFINES
+ QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES
+)
+
## Scopes:
#####################################################################
@@ -38,3 +59,13 @@ qt_internal_extend_target(tst_qquickstates CONDITION NOT ANDROID AND NOT IOS
DEFINES
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
)
+
+qt_internal_extend_target(tst_qquickstates_no_deferred_properties CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qquickstates_no_deferred_properties CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
diff --git a/tests/auto/quick/qquickstates/data/anchorRewindSize.qml b/tests/auto/quick/qquickstates/data/anchorRewindSize.qml
new file mode 100644
index 0000000000..8fbae05c6d
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/anchorRewindSize.qml
@@ -0,0 +1,30 @@
+import QtQuick
+
+Item {
+ id: root
+ width: 400
+ height: 400
+ property bool changeState: false
+ Item {
+ id: outer
+ anchors.fill: parent
+ Item {
+ id: inner
+ width: 100
+ height: 100
+ anchors.left: outer.left
+ anchors.top: outer.top
+ }
+ states: [
+ State {
+ when: root.changeState
+ AnchorChanges {
+ target: inner
+ anchors.right: outer.right
+ anchors.bottom: outer.bottom
+ }
+ }
+ ]
+ }
+}
+
diff --git a/tests/auto/quick/qquickstates/data/broken.qml b/tests/auto/quick/qquickstates/data/broken.qml
new file mode 100644
index 0000000000..8813c8bb39
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/broken.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ states: ["test"]
+}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index 44187d2b65..3bdbe29d1e 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -119,7 +119,12 @@ class tst_qquickstates : public QQmlDataTest
{
Q_OBJECT
public:
- tst_qquickstates() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
+ tst_qquickstates() : QQmlDataTest(QT_QMLTEST_DATADIR)
+ {
+#ifdef QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES
+ qputenv("QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES", "1");
+#endif
+ }
private:
QByteArray fullDataPath(const QString &path) const;
@@ -184,7 +189,9 @@ private slots:
void parentChangeInvolvingBindings();
void deferredProperties();
void rewindAnchorChange();
+ void rewindAnchorChangeSize();
void bindingProperlyRemovedWithTransition();
+ void doNotCrashOnBroken();
};
void tst_qquickstates::initTestCase()
@@ -1650,7 +1657,7 @@ static int getRefCount(const QQmlAnyBinding &binding)
} else {
// this temporarily adds a refcount because we construc a new untypedpropertybinding
// thus -1
- return QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding())->ref - 1;
+ return QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding())->refCount() - 1;
}
}
@@ -1975,6 +1982,41 @@ void tst_qquickstates::rewindAnchorChange()
QTRY_COMPARE(innerRect->height(), 200);
}
+void tst_qquickstates::rewindAnchorChangeSize()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("anchorRewindSize.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ std::unique_ptr<QObject> root(c.create());
+ QVERIFY(root);
+
+ QQmlContext *context = qmlContext(root.get());
+ QVERIFY(context);
+
+ QObject *inner = context->objectForName(QStringLiteral("inner"));
+ QVERIFY(inner);
+
+ QQuickItem *innerRect = qobject_cast<QQuickItem *>(inner);
+ QVERIFY(innerRect);
+
+ QCOMPARE(innerRect->x(), 0);
+ QCOMPARE(innerRect->y(), 0);
+ QCOMPARE(innerRect->width(), 100);
+ QCOMPARE(innerRect->height(), 100);
+
+ root->setProperty("changeState", true);
+ QCOMPARE(innerRect->x(), 0);
+ QCOMPARE(innerRect->y(), 0);
+ QCOMPARE(innerRect->width(), 400);
+ QCOMPARE(innerRect->height(), 400);
+
+ root->setProperty("changeState", false);
+ QCOMPARE(innerRect->x(), 0);
+ QCOMPARE(innerRect->y(), 0);
+ QCOMPARE(innerRect->width(), 100);
+ QCOMPARE(innerRect->height(), 100);
+}
+
void tst_qquickstates::bindingProperlyRemovedWithTransition()
{
QQmlEngine engine;
@@ -1995,6 +2037,21 @@ void tst_qquickstates::bindingProperlyRemovedWithTransition()
QTRY_COMPARE(item->width(), 100);
}
+void tst_qquickstates::doNotCrashOnBroken()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("broken.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> root(c.create());
+ QVERIFY(root);
+ QQuickItem *item = qobject_cast<QQuickItem *>(root.get());
+ QVERIFY(item);
+
+ QQmlListReference states(item, "states");
+ QCOMPARE(states.size(), 1);
+ QCOMPARE(states.at(0), nullptr);
+}
+
QTEST_MAIN(tst_qquickstates)
#include "tst_qquickstates.moc"
diff --git a/tests/auto/quick/qquickstyledtext/CMakeLists.txt b/tests/auto/quick/qquickstyledtext/CMakeLists.txt
index 2e36a647c0..9cac845e6d 100644
--- a/tests/auto/quick/qquickstyledtext/CMakeLists.txt
+++ b/tests/auto/quick/qquickstyledtext/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickstyledtext Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickstyledtext LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qquickstyledtext
SOURCES
tst_qquickstyledtext.cpp
diff --git a/tests/auto/quick/qquickstyledtext/tst_qquickstyledtext.cpp b/tests/auto/quick/qquickstyledtext/tst_qquickstyledtext.cpp
index aebd9990f7..f0367e27f5 100644
--- a/tests/auto/quick/qquickstyledtext/tst_qquickstyledtext.cpp
+++ b/tests/auto/quick/qquickstyledtext/tst_qquickstyledtext.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QtTest>
#include <QtGui/QTextLayout>
@@ -127,6 +127,7 @@ void tst_qquickstyledtext::textOutput_data()
QTest::newRow("space leading bold") << "this is<b> bold</b>" << "this is bold" << (FormatList() << Format(Format::Bold, 7, 5)) << false;
QTest::newRow("space trailing bold") << "this is <b>bold </b>" << "this is bold " << (FormatList() << Format(Format::Bold, 8, 5)) << false;
QTest::newRow("img") << "a<img src=\"blah.png\"/>b" << "a b" << FormatList() << false;
+ QTest::newRow("img") << "a<img />b" << "a b" << FormatList() << false;
QTest::newRow("tag mix") << "<f6>ds<b></img><pro>gfh</b><w><w>ghj</stron><ql><sl><pl>dfg</j6><img><bol><r><prp>dfg<bkj></b><up><string>ewrq</al><bl>jklhj<zl>" << "dsgfhghjdfgdfgewrqjklhj" << (FormatList() << Format(Format::Bold, 2, 3)) << false;
QTest::newRow("named html entities") << "&gt; &lt; &amp; &quot; &nbsp;" << QLatin1String("> < & \" ") + QChar(QChar::Nbsp) << FormatList() << false;
QTest::newRow("invalid html entities") << "a &hello & a &goodbye;" << "a &hello & a " << FormatList() << false;
diff --git a/tests/auto/quick/qquicksystempalette/CMakeLists.txt b/tests/auto/quick/qquicksystempalette/CMakeLists.txt
index bb99fbefc4..b747b51978 100644
--- a/tests/auto/quick/qquicksystempalette/CMakeLists.txt
+++ b/tests/auto/quick/qquicksystempalette/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicksystempalette Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicksystempalette LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicksystempalette/tst_qquicksystempalette.cpp b/tests/auto/quick/qquicksystempalette/tst_qquicksystempalette.cpp
index b9e79051bb..e9b6054130 100644
--- a/tests/auto/quick/qquicksystempalette/tst_qquicksystempalette.cpp
+++ b/tests/auto/quick/qquicksystempalette/tst_qquicksystempalette.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
@@ -52,6 +52,8 @@ void tst_qquicksystempalette::activePalette()
QCOMPARE(palette.shadow().color(), object->shadow());
QCOMPARE(palette.highlight().color(), object->highlight());
QCOMPARE(palette.highlightedText().color(), object->highlightedText());
+ QCOMPARE(palette.placeholderText().color(), object->placeholderText());
+ QCOMPARE(palette.accent().color(), object->accent());
delete object;
}
@@ -80,6 +82,8 @@ void tst_qquicksystempalette::inactivePalette()
QCOMPARE(palette.shadow().color(), object->shadow());
QCOMPARE(palette.highlight().color(), object->highlight());
QCOMPARE(palette.highlightedText().color(), object->highlightedText());
+ QCOMPARE(palette.placeholderText().color(), object->placeholderText());
+ QCOMPARE(palette.accent().color(), object->accent());
delete object;
}
@@ -108,6 +112,8 @@ void tst_qquicksystempalette::disabledPalette()
QCOMPARE(palette.shadow().color(), object->shadow());
QCOMPARE(palette.highlight().color(), object->highlight());
QCOMPARE(palette.highlightedText().color(), object->highlightedText());
+ QCOMPARE(palette.placeholderText().color(), object->placeholderText());
+ QCOMPARE(palette.accent().color(), object->accent());
delete object;
}
diff --git a/tests/auto/quick/qquicktableview/CMakeLists.txt b/tests/auto/quick/qquicktableview/CMakeLists.txt
index 9fa59ca827..77c7f5f06a 100644
--- a/tests/auto/quick/qquicktableview/CMakeLists.txt
+++ b/tests/auto/quick/qquicktableview/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktableview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktableview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicktableview/data/alternatingrowheightcolumnwidth.qml b/tests/auto/quick/qquicktableview/data/alternatingrowheightcolumnwidth.qml
index c23394b3d5..106a95de7f 100644
--- a/tests/auto/quick/qquicktableview/data/alternatingrowheightcolumnwidth.qml
+++ b/tests/auto/quick/qquicktableview/data/alternatingrowheightcolumnwidth.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/asyncloader.qml b/tests/auto/quick/qquicktableview/data/asyncloader.qml
index 8e87b60b0f..ac901323f1 100644
--- a/tests/auto/quick/qquicktableview/data/asyncloader.qml
+++ b/tests/auto/quick/qquicktableview/data/asyncloader.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/auto/quick/qquicktableview/data/asyncplain.qml b/tests/auto/quick/qquicktableview/data/asyncplain.qml
index eba13e72a4..25eb14b016 100644
--- a/tests/auto/quick/qquicktableview/data/asyncplain.qml
+++ b/tests/auto/quick/qquicktableview/data/asyncplain.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/changemodelfromdelegate.qml b/tests/auto/quick/qquicktableview/data/changemodelfromdelegate.qml
index e20b099ae9..60b02bc245 100644
--- a/tests/auto/quick/qquicktableview/data/changemodelfromdelegate.qml
+++ b/tests/auto/quick/qquicktableview/data/changemodelfromdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/changemodelordelegateduringupdate.qml b/tests/auto/quick/qquicktableview/data/changemodelordelegateduringupdate.qml
index bcb310814c..f0c7814186 100644
--- a/tests/auto/quick/qquicktableview/data/changemodelordelegateduringupdate.qml
+++ b/tests/auto/quick/qquicktableview/data/changemodelordelegateduringupdate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml b/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml
index 600e120a12..463e21342c 100644
--- a/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml
+++ b/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/checkmodelpropertyrevision.qml b/tests/auto/quick/qquicktableview/data/checkmodelpropertyrevision.qml
index 560429308c..f89fff8de1 100644
--- a/tests/auto/quick/qquicktableview/data/checkmodelpropertyrevision.qml
+++ b/tests/auto/quick/qquicktableview/data/checkmodelpropertyrevision.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.12
diff --git a/tests/auto/quick/qquicktableview/data/checkrowandcolumnnotchanged.qml b/tests/auto/quick/qquicktableview/data/checkrowandcolumnnotchanged.qml
index bf62a0e393..fa15f2f54b 100644
--- a/tests/auto/quick/qquicktableview/data/checkrowandcolumnnotchanged.qml
+++ b/tests/auto/quick/qquicktableview/data/checkrowandcolumnnotchanged.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/columnwidthboundtoviewwidth.qml b/tests/auto/quick/qquicktableview/data/columnwidthboundtoviewwidth.qml
index bbbdf74d4e..eaf0cd692d 100644
--- a/tests/auto/quick/qquicktableview/data/columnwidthboundtoviewwidth.qml
+++ b/tests/auto/quick/qquicktableview/data/columnwidthboundtoviewwidth.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/contentwidthheight.qml b/tests/auto/quick/qquicktableview/data/contentwidthheight.qml
index 12cf58d5c4..f6ae474bf4 100644
--- a/tests/auto/quick/qquicktableview/data/contentwidthheight.qml
+++ b/tests/auto/quick/qquicktableview/data/contentwidthheight.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/countingtableview.qml b/tests/auto/quick/qquicktableview/data/countingtableview.qml
index 31cd7ec849..a73cb6e4cf 100644
--- a/tests/auto/quick/qquicktableview/data/countingtableview.qml
+++ b/tests/auto/quick/qquicktableview/data/countingtableview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/delegateWithRequired.qml b/tests/auto/quick/qquicktableview/data/delegateWithRequired.qml
index 27dff1aadd..728de37266 100644
--- a/tests/auto/quick/qquicktableview/data/delegateWithRequired.qml
+++ b/tests/auto/quick/qquicktableview/data/delegateWithRequired.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/delegatewithRequiredUnset.qml b/tests/auto/quick/qquicktableview/data/delegatewithRequiredUnset.qml
index 13afd88522..43a1eed402 100644
--- a/tests/auto/quick/qquicktableview/data/delegatewithRequiredUnset.qml
+++ b/tests/auto/quick/qquicktableview/data/delegatewithRequiredUnset.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/delegatewithanchors.qml b/tests/auto/quick/qquicktableview/data/delegatewithanchors.qml
index 6354d8d1ed..8b192e0b9e 100644
--- a/tests/auto/quick/qquicktableview/data/delegatewithanchors.qml
+++ b/tests/auto/quick/qquicktableview/data/delegatewithanchors.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/editdelegate.qml b/tests/auto/quick/qquicktableview/data/editdelegate.qml
index 8219aaa3b0..0c8bee2b27 100644
--- a/tests/auto/quick/qquicktableview/data/editdelegate.qml
+++ b/tests/auto/quick/qquicktableview/data/editdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -45,13 +45,13 @@ Item {
Component.onCompleted: {
tableView.editItem = editRoot
- tableView.editIndex = tableView.modelIndex(row, column)
+ tableView.editIndex = tableView.index(row, column)
selectAll()
}
Component.onDestruction: {
tableView.editItem = null
- tableView.editIndex = tableView.modelIndex(-1, -1)
+ tableView.editIndex = tableView.index(-1, -1)
}
}
}
diff --git a/tests/auto/quick/qquicktableview/data/editdelegate_combobox.qml b/tests/auto/quick/qquicktableview/data/editdelegate_combobox.qml
index b7de4cd406..c1344575ff 100644
--- a/tests/auto/quick/qquicktableview/data/editdelegate_combobox.qml
+++ b/tests/auto/quick/qquicktableview/data/editdelegate_combobox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -47,12 +47,12 @@ Item {
Component.onCompleted: {
tableView.editItem = editRoot
- tableView.editIndex = tableView.modelIndex(row, column)
+ tableView.editIndex = tableView.index(row, column)
}
Component.onDestruction: {
tableView.editItem = null
- tableView.editIndex = tableView.modelIndex(-1, -1)
+ tableView.editIndex = tableView.index(-1, -1)
}
ComboBox {
diff --git a/tests/auto/quick/qquicktableview/data/forcelayout.qml b/tests/auto/quick/qquicktableview/data/forcelayout.qml
index d15587cb36..5beaf2bd25 100644
--- a/tests/auto/quick/qquicktableview/data/forcelayout.qml
+++ b/tests/auto/quick/qquicktableview/data/forcelayout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/hiderowsandcolumns.qml b/tests/auto/quick/qquicktableview/data/hiderowsandcolumns.qml
index d0f5f06fcc..1a2737ad28 100644
--- a/tests/auto/quick/qquicktableview/data/hiderowsandcolumns.qml
+++ b/tests/auto/quick/qquicktableview/data/hiderowsandcolumns.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/iscolumnloaded.qml b/tests/auto/quick/qquicktableview/data/iscolumnloaded.qml
index df0b9d3bd2..84cbe981bd 100644
--- a/tests/auto/quick/qquicktableview/data/iscolumnloaded.qml
+++ b/tests/auto/quick/qquicktableview/data/iscolumnloaded.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quick/qquicktableview/data/plaintableview.qml b/tests/auto/quick/qquicktableview/data/plaintableview.qml
index 840ef3c5d5..a18b1c03eb 100644
--- a/tests/auto/quick/qquicktableview/data/plaintableview.qml
+++ b/tests/auto/quick/qquicktableview/data/plaintableview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/qquicktableview/data/positionlast.qml b/tests/auto/quick/qquicktableview/data/positionlast.qml
new file mode 100644
index 0000000000..fcc2f34fae
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/positionlast.qml
@@ -0,0 +1,52 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ width: 300
+ height: 300
+
+ property alias tableView: tableView
+ property bool positionOnRowsChanged: false
+ property bool positionOnColumnsChanged: false
+ property bool positionOnContentHeightChanged: false
+ property bool positionOnContentWidthChanged: false
+
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ clip: true
+
+ delegate: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 100
+ Text {
+ anchors.centerIn: parent
+ text: "row:" + row + "\ncol:" + column
+ font.pixelSize: 10
+ }
+ }
+
+ onRowsChanged: {
+ if (positionOnRowsChanged)
+ positionViewAtRow(rows - 1, TableView.AlignBottom)
+ }
+
+ onColumnsChanged: {
+ if (positionOnColumnsChanged)
+ positionViewAtColumn(columns - 1, TableView.AlignRight)
+ }
+
+ onContentHeightChanged: {
+ if (positionOnContentHeightChanged)
+ positionViewAtRow(rows - 1, TableView.AlignBottom)
+ }
+
+ onContentWidthChanged: {
+ if (positionOnContentWidthChanged)
+ positionViewAtColumn(columns - 1, TableView.AlignRight)
+ }
+ }
+
+}
diff --git a/tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml b/tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml
index 66887d8c6b..851fcb7310 100644
--- a/tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml
+++ b/tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/resetJsModelData.qml b/tests/auto/quick/qquicktableview/data/resetJsModelData.qml
new file mode 100644
index 0000000000..def5346147
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/resetJsModelData.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Item {
+ width: 100
+ height: 300
+
+ property alias tableView: tableView
+
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ property int modelUpdated: 0
+ onModelChanged: { ++modelUpdated }
+ delegate: Item {
+ implicitHeight: 10
+ implicitWidth: 10
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktableview/data/resetModelData.qml b/tests/auto/quick/qquicktableview/data/resetModelData.qml
new file mode 100644
index 0000000000..f7b3ec3009
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/resetModelData.qml
@@ -0,0 +1,25 @@
+import QtQuick
+
+Item {
+ width: 640
+ height: 450
+
+ property alias tableView: tableView
+
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ height: 300
+ width: 200
+
+ property bool success: false
+
+ delegate: Rectangle {
+ required property var model
+ implicitWidth: 100
+ implicitHeight: 50
+ property var mydata: model?.custom ?? model.display
+ onMydataChanged: tableView.success = mydata === 42
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktableview/data/setcontentpos.qml b/tests/auto/quick/qquicktableview/data/setcontentpos.qml
index d1dc53bb98..69cf73164f 100644
--- a/tests/auto/quick/qquicktableview/data/setcontentpos.qml
+++ b/tests/auto/quick/qquicktableview/data/setcontentpos.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml b/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml
index 4a1ee32cdd..deb6b81f8e 100644
--- a/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml
+++ b/tests/auto/quick/qquicktableview/data/sizefromdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/syncviewsimple.qml b/tests/auto/quick/qquicktableview/data/syncviewsimple.qml
index 880554b129..0fc2e6a819 100644
--- a/tests/auto/quick/qquicktableview/data/syncviewsimple.qml
+++ b/tests/auto/quick/qquicktableview/data/syncviewsimple.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/tableviewdefaultspacing.qml b/tests/auto/quick/qquicktableview/data/tableviewdefaultspacing.qml
index 6ab66984e7..f3563500c9 100644
--- a/tests/auto/quick/qquicktableview/data/tableviewdefaultspacing.qml
+++ b/tests/auto/quick/qquicktableview/data/tableviewdefaultspacing.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/tableviewfocus.qml b/tests/auto/quick/qquicktableview/data/tableviewfocus.qml
index 4bda963673..3bb7040e7f 100644
--- a/tests/auto/quick/qquicktableview/data/tableviewfocus.qml
+++ b/tests/auto/quick/qquicktableview/data/tableviewfocus.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/tableviewimplicitsize.qml b/tests/auto/quick/qquicktableview/data/tableviewimplicitsize.qml
index 8a0647f460..54414b6a71 100644
--- a/tests/auto/quick/qquicktableview/data/tableviewimplicitsize.qml
+++ b/tests/auto/quick/qquicktableview/data/tableviewimplicitsize.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/tableviewinteractive.qml b/tests/auto/quick/qquicktableview/data/tableviewinteractive.qml
new file mode 100644
index 0000000000..526313dc24
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/tableviewinteractive.qml
@@ -0,0 +1,32 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+ id: rootItem
+
+ width: 200
+ height: 200
+ visible: true
+
+ property int eventCount: 0
+ property alias tableView: tableView
+
+ MouseArea {
+ anchors.fill: parent
+ onPressed: function(mouse) {
+ ++eventCount
+ }
+ }
+
+ TableView {
+ id: tableView
+ objectName: "tableView"
+ anchors.fill: parent
+ model: 1
+ delegate: Rectangle {
+ color: "red"
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml b/tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml
index 0e0b8f20bb..f0396c2c34 100644
--- a/tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml
+++ b/tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml b/tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml
index 5892a72be4..489a00b3f8 100644
--- a/tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml
+++ b/tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quick/qquicktableview/data/tweakimplicitsize.qml b/tests/auto/quick/qquicktableview/data/tweakimplicitsize.qml
index d60e9d0b1e..e0e9c01908 100644
--- a/tests/auto/quick/qquicktableview/data/tweakimplicitsize.qml
+++ b/tests/auto/quick/qquicktableview/data/tweakimplicitsize.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/unloadheader.qml b/tests/auto/quick/qquicktableview/data/unloadheader.qml
new file mode 100644
index 0000000000..5b15e4c752
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/unloadheader.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import Qt.labs.qmlmodels
+
+Item {
+ width: 640
+ height: 480
+
+ property alias tableView: tableView
+ property alias loader: verticalHeaderLoader
+
+ Loader {
+ id: verticalHeaderLoader
+ x: 0
+ width: item ? item.contentWidth : 0
+ height: parent.height
+ sourceComponent: TableView {
+ model: 5
+ syncView: tableView
+ syncDirection: Qt.Vertical
+ delegate: Text {
+ text: index
+ }
+ }
+ }
+
+ TableView {
+ id: tableView
+ anchors {
+ left: verticalHeaderLoader.right
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ }
+
+ model: 5
+ delegate: Text {
+ text: index
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktableview/data/usechooserwithoutdefault.qml b/tests/auto/quick/qquicktableview/data/usechooserwithoutdefault.qml
index 48d28b0766..4ff2a7401f 100644
--- a/tests/auto/quick/qquicktableview/data/usechooserwithoutdefault.qml
+++ b/tests/auto/quick/qquicktableview/data/usechooserwithoutdefault.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml b/tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml
index c118077b14..65d918e072 100644
--- a/tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml
+++ b/tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml b/tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml
index 3e3ed9ab94..686bed12d0 100644
--- a/tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml
+++ b/tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/auto/quick/qquicktableview/data/zerosizedtableview.qml b/tests/auto/quick/qquicktableview/data/zerosizedtableview.qml
index c8d6168b81..b885684b18 100644
--- a/tests/auto/quick/qquicktableview/data/zerosizedtableview.qml
+++ b/tests/auto/quick/qquicktableview/data/zerosizedtableview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -15,6 +15,14 @@ Item {
width: 0
height: 0
delegate: tableViewDelegate
+
+ Component.onCompleted: {
+ positionViewAtCell(
+ Qt.point(0,0),
+ TableView.AlignHCenter,
+ Qt.point(-5,-5)
+ );
+ }
}
Component {
diff --git a/tests/auto/quick/qquicktableview/data/zerosizedviewport.qml b/tests/auto/quick/qquicktableview/data/zerosizedviewport.qml
new file mode 100644
index 0000000000..f32c7bd3b2
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/zerosizedviewport.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+
+Item {
+ width: 640
+ height: 450
+
+ property alias tableView: tableView
+
+ TableView {
+ id: tableView
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ width: contentWidth
+ delegate: tableViewDelegate
+ }
+
+ Component {
+ id: tableViewDelegate
+ Rectangle {
+ objectName: "tableViewDelegate"
+ implicitWidth: tableView.width
+ implicitHeight: 50
+ }
+ }
+
+}
diff --git a/tests/auto/quick/qquicktableview/testmodel.h b/tests/auto/quick/qquicktableview/testmodel.h
index 02a3478bdd..21ed7e03a5 100644
--- a/tests/auto/quick/qquicktableview/testmodel.h
+++ b/tests/auto/quick/qquicktableview/testmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QtCore>
#include <QtGui/QStandardItemModel>
@@ -36,13 +36,27 @@ public:
QVariant data(const QModelIndex &index, int role) const override
{
- if (!index.isValid() || role != Qt::DisplayRole)
+ if (!index.isValid())
return QVariant();
- int serializedIndex = index.row() + (index.column() * m_columns);
- if (modelData.contains(serializedIndex))
- return modelData.value(serializedIndex);
- return QStringLiteral("%1").arg(index.row());
+ QVariant ret;
+
+ switch (role) {
+ case Qt::UserRole:
+ ret = 42;
+ break;
+ case Qt::DisplayRole: {
+ int serializedIndex = index.row() + (index.column() * m_columns);
+ if (modelData.contains(serializedIndex))
+ ret = modelData.value(serializedIndex);
+ else
+ ret = QStringLiteral("%1").arg(index.row()); }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
}
Q_INVOKABLE QVariant dataFromSerializedIndex(int index) const
@@ -54,9 +68,18 @@ public:
QHash<int, QByteArray> roleNames() const override
{
+ if (m_useCustomRoleNames)
+ return { { Qt::UserRole, "custom"} };
return { {Qt::DisplayRole, "display"} };
}
+ Q_INVOKABLE void useCustomRoleNames(bool use)
+ {
+ beginResetModel();
+ m_useCustomRoleNames = use;
+ endResetModel();
+ }
+
Q_INVOKABLE void setModelData(const QPoint &cell, const QSize &span, const QString &string)
{
for (int c = 0; c < span.width(); ++c) {
@@ -159,6 +182,11 @@ public:
insertRow(row, QModelIndex());
}
+ Q_INVOKABLE void addColumn(int column)
+ {
+ insertColumn(column, QModelIndex());
+ }
+
Qt::ItemFlags flags(const QModelIndex &index) const override
{
Q_UNUSED(index)
@@ -178,6 +206,7 @@ private:
int m_rows = 0;
int m_columns = 0;
bool m_dataCanBeFetched = false;
+ bool m_useCustomRoleNames = false;
QHash<int, QString> modelData;
Qt::ItemFlags m_flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
};
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index ecea4adfd5..bb425b5a6f 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTest/quicktest.h>
@@ -87,6 +87,7 @@ private slots:
void checkZeroSizedDelegate();
void checkImplicitSizeDelegate();
void checkZeroSizedTableView();
+ void checkZeroSizedViewPort();
void checkColumnWidthWithoutProvider();
void checkColumnWidthAndRowHeightFunctions();
void checkDelegateWithAnchors();
@@ -103,7 +104,7 @@ private slots:
void isColumnLoadedAndIsRowLoaded();
void checkForceLayoutFunction();
void checkForceLayoutEndUpDoingALayout();
- void checkForceLayoutDuringModelChange();
+ void checkForceLayoutInbetweenAddingRowsToModel();
void checkForceLayoutWhenAllItemsAreHidden();
void checkLayoutChangedSignal();
void checkContentWidthAndHeight();
@@ -167,6 +168,7 @@ private slots:
void checkSyncView_pageFlicking();
void checkSyncView_emptyModel();
void checkSyncView_topLeftChanged();
+ void checkSyncView_unloadHeader();
void delegateWithRequiredProperties();
void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable();
void replaceModel();
@@ -188,6 +190,10 @@ private slots:
void positionViewAtCellForLargeCells_data();
void positionViewAtCellForLargeCells();
void positionViewAtCellForLargeCellsUsingSubrect();
+ void positionViewAtLastRow_data();
+ void positionViewAtLastRow();
+ void positionViewAtLastColumn_data();
+ void positionViewAtLastColumn();
void itemAtCell_data();
void itemAtCell();
void leftRightTopBottomProperties_data();
@@ -208,6 +214,7 @@ private slots:
void testSelectableStartPosEndPosOutsideView();
void testSelectableScrollTowardsPos();
void setCurrentIndexFromSelectionModel();
+ void clearSelectionOnTap_data();
void clearSelectionOnTap();
void moveCurrentIndexUsingArrowKeys();
void moveCurrentIndexUsingHomeAndEndKeys();
@@ -244,6 +251,7 @@ private slots:
void deletedDelegate();
void columnResizing_data();
void columnResizing();
+ void tableViewInteractive();
void rowResizing_data();
void rowResizing();
void rowAndColumnResizing_data();
@@ -267,6 +275,10 @@ private slots:
void editWarning_nonEditableModelItem();
void attachedPropertiesOnEditDelegate();
void requiredPropertiesOnEditDelegate();
+ void resettingRolesRespected();
+ void checkScroll_data();
+ void checkScroll();
+ void checkRebuildJsModel();
};
tst_QQuickTableView::tst_QQuickTableView()
@@ -300,6 +312,18 @@ QPoint tst_QQuickTableView::getContextRowAndColumn(const QQuickItem *item) const
return QPoint(column, row);
}
+static void sendWheelEvent(QQuickWindow *window, QPoint pos, QPoint angleDelta,
+ QPoint pixelDelta,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier,
+ Qt::ScrollPhase phase = Qt::NoScrollPhase,
+ bool inverted = false) {
+ QWheelEvent wheelEvent(pos, window->mapToGlobal(pos), pixelDelta,
+ angleDelta, Qt::NoButton, modifiers, phase,
+ inverted);
+ QGuiApplication::sendEvent(window, &wheelEvent);
+ qApp->processEvents();
+}
+
void tst_QQuickTableView::setAndGetModel_data()
{
QTest::addColumn<QVariant>("model");
@@ -443,7 +467,7 @@ void tst_QQuickTableView::checkZeroSizedTableView()
WAIT_UNTIL_POLISHED;
QCOMPARE(tableViewPrivate->loadedItems.size(), 2);
- const auto item = tableView->itemAtCell(0, 0);
+ const auto item = tableView->itemAtIndex(tableView->index(0, 0));
QVERIFY(item);
QCOMPARE(item->width(), 200);
@@ -457,6 +481,17 @@ void tst_QQuickTableView::checkZeroSizedTableView()
QCOMPARE(tableViewPrivate->loadedItems.size(), 1);
}
+void tst_QQuickTableView::checkZeroSizedViewPort()
+{
+ LOAD_TABLEVIEW("zerosizedviewport.qml");
+
+ auto model = TestModelAsVariant(20, 20);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ QVERIFY(!tableViewPrivate->loadedItems.isEmpty());
+}
void tst_QQuickTableView::checkColumnWidthWithoutProvider()
{
@@ -794,10 +829,11 @@ void tst_QQuickTableView::checkForceLayoutEndUpDoingALayout()
QCOMPARE(tableView->contentHeight(), (9 * (newDelegateSize + rowSpacing)) - rowSpacing);
}
-void tst_QQuickTableView::checkForceLayoutDuringModelChange()
+void tst_QQuickTableView::checkForceLayoutInbetweenAddingRowsToModel()
{
- // Check that TableView doesn't assert if we call
- // forceLayout() in the middle of a model change.
+ // Check that TableView doesn't assert if we call forceLayout() while waiting
+ // for a callback from the model that the row count has changed. Also make sure
+ // that we don't move the contentItem while doing so.
LOAD_TABLEVIEW("plaintableview.qml");
const int initialRowCount = 10;
@@ -812,9 +848,13 @@ void tst_QQuickTableView::checkForceLayoutDuringModelChange()
WAIT_UNTIL_POLISHED;
+ const int contentY = 10;
+ tableView->setContentY(contentY);
QCOMPARE(tableView->rows(), initialRowCount);
+ QCOMPARE(tableView->contentY(), contentY);
model.addRow(0);
QCOMPARE(tableView->rows(), initialRowCount + 1);
+ QCOMPARE(tableView->contentY(), contentY);
}
void tst_QQuickTableView::checkForceLayoutWhenAllItemsAreHidden()
@@ -2451,7 +2491,7 @@ void tst_QQuickTableView::checkChangingModelFromDelegate()
// And since the QML code tried to add another row as well, we
// expect rebuildScheduled to be true, and a polish event to be pending.
QVERIFY(tableViewPrivate->scheduledRebuildOptions);
- QCOMPARE(tableViewPrivate->polishScheduled, true);
+ QVERIFY(tableViewPrivate->polishScheduled);
WAIT_UNTIL_POLISHED;
// After handling the polish event, we expect also the third row to now be added
@@ -3253,6 +3293,21 @@ void tst_QQuickTableView::checkSyncView_emptyModel()
QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0);
}
+void tst_QQuickTableView::checkSyncView_unloadHeader()
+{
+ // Check that we don't get a crash in TableView if one
+ // of the sync children is suddenly deleted (from e.g a Loader).
+ LOAD_TABLEVIEW("unloadheader.qml");
+
+ const auto loader = view->rootObject()->property("loader").value<QQuickLoader *>();
+ QVERIFY(loader);
+ QVERIFY(loader->item());
+ loader->setActive(false);
+ QVERIFY(!loader->item());
+ gc(*qmlEngine(tableView));
+ tableView->forceLayout();
+}
+
void tst_QQuickTableView::checkSyncView_topLeftChanged()
{
LOAD_TABLEVIEW("syncviewsimple.qml");
@@ -3770,14 +3825,15 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation()
QPoint cell(tableView->rightColumn(), tableView->bottomRow());
const QRectF cellGeometry = tableViewPrivate->loadedTableItem(cell)->geometry();
- const int modelIndex = tableViewPrivate->modelIndexAtCell(cell);
+ const int serializedIndex = tableViewPrivate->modelIndexAtCell(cell);
+ const QModelIndex index = tableView->index(cell.y(), cell.x());
- QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex));
+ QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex));
QVERIFY(!tableViewPrivate->positionXAnimation.isRunning());
QVERIFY(!tableViewPrivate->positionYAnimation.isRunning());
// Animate the cell to the top left location in the view
- tableView->positionViewAtCell(cell, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
+ tableView->positionViewAtIndex(index, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
// Wait for animation to finish
QVERIFY(tableViewPrivate->positionXAnimation.isRunning());
@@ -3786,13 +3842,13 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation()
QTRY_COMPARE(tableViewPrivate->positionYAnimation.isRunning(), false);
// Check that the cell is now placed in the top left corner
- QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex));
+ QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex));
QPointF expectedPos = tableView->mapToItem(tableView->contentItem(), QPointF(0, 0));
QCOMPARE(cellGeometry.x(), expectedPos.x());
QCOMPARE(cellGeometry.y(), expectedPos.y());
// Animate the cell to the top right location in the view
- tableView->positionViewAtCell(cell, QQuickTableView::AlignTop | QQuickTableView::AlignRight);
+ tableView->positionViewAtIndex(index, QQuickTableView::AlignTop | QQuickTableView::AlignRight);
// Wait for animation to finish
QVERIFY(tableViewPrivate->positionXAnimation.isRunning());
@@ -3800,13 +3856,13 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation()
QTRY_COMPARE(tableViewPrivate->positionXAnimation.isRunning(), false);
// Check that the cell is now placed in the top right corner
- QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex));
+ QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex));
expectedPos = tableView->mapToItem(tableView->contentItem(), QPointF(tableView->width(), 0));
QCOMPARE(cellGeometry.right(), expectedPos.x());
QCOMPARE(cellGeometry.y(), expectedPos.y());
// Animate the cell to the bottom left location in the view
- tableView->positionViewAtCell(cell, QQuickTableView::AlignBottom | QQuickTableView::AlignLeft);
+ tableView->positionViewAtIndex(index, QQuickTableView::AlignBottom | QQuickTableView::AlignLeft);
// Wait for animation to finish
QVERIFY(tableViewPrivate->positionXAnimation.isRunning());
@@ -3815,7 +3871,7 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation()
QTRY_COMPARE(tableViewPrivate->positionYAnimation.isRunning(), false);
// Check that the cell is now placed in the bottom left corner
- QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex));
+ QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex));
expectedPos = tableView->mapToItem(tableView->contentItem(), QPointF(0, tableView->height()));
QCOMPARE(cellGeometry.x(), expectedPos.x());
QCOMPARE(cellGeometry.bottom(), expectedPos.y());
@@ -3829,7 +3885,7 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation()
QTRY_COMPARE(tableViewPrivate->positionXAnimation.isRunning(), false);
// Check that the cell is now placed in the bottom right corner
- QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex));
+ QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex));
expectedPos = tableView->mapToItem(tableView->contentItem(), QPointF(tableView->width(), tableView->height()));
QCOMPARE(cellGeometry.right(), expectedPos.x());
QCOMPARE(cellGeometry.bottom(), expectedPos.y());
@@ -4070,6 +4126,122 @@ void tst_QQuickTableView::positionViewAtCellForLargeCellsUsingSubrect()
QCOMPARE(tableView->contentY(), -(tableView->height() - subRect.bottom()));
}
+void tst_QQuickTableView::positionViewAtLastRow_data()
+{
+ QTest::addColumn<QString>("signalToTest");
+ QTest::addColumn<bool>("animate");
+
+ QTest::newRow("positionOnRowsChanged, animate=false") << "positionOnRowsChanged" << false;
+ QTest::newRow("positionOnRowsChanged, animate=true") << "positionOnRowsChanged" << true;
+ QTest::newRow("positionOnContentHeightChanged, animate=false") << "positionOnContentHeightChanged" << false;
+ QTest::newRow("positionOnContentHeightChanged, animate=true") << "positionOnContentHeightChanged" << true;
+}
+
+void tst_QQuickTableView::positionViewAtLastRow()
+{
+ // Check that we can make TableView always scroll to the
+ // last row in the model by positioning the view upon
+ // a rowsChanged callback
+ QFETCH(QString, signalToTest);
+ QFETCH(bool, animate);
+
+ LOAD_TABLEVIEW("positionlast.qml");
+
+ // Use a very large model to indirectly test that we "fast-flick" to
+ // the end at start-up (instead of loading and unloading rows, which
+ // would take forever).
+ TestModel model(2000000, 2000000);
+ tableView->setModel(QVariant::fromValue(&model));
+ tableView->setAnimate(animate);
+
+ view->rootObject()->setProperty(signalToTest.toUtf8().constData(), true);
+
+ WAIT_UNTIL_POLISHED;
+
+ const qreal delegateSize = 100.;
+ const qreal viewportRowCount = tableView->height() / delegateSize;
+
+ // Check that the viewport is positioned at the last row at start-up
+ QCOMPARE(tableView->rows(), model.rowCount());
+ QCOMPARE(tableView->bottomRow(), model.rowCount() - 1);
+ QCOMPARE(tableView->contentY(), (model.rowCount() - viewportRowCount) * delegateSize);
+
+ // Check that the viewport is positioned at the last
+ // row after more rows are added.
+ for (int row = 0; row < 2; ++row) {
+ model.addRow(model.rowCount() - 1);
+
+ WAIT_UNTIL_POLISHED;
+
+ if (tableView->animate()) {
+ QVERIFY(tableViewPrivate->positionYAnimation.isRunning());
+ QTRY_VERIFY(!tableViewPrivate->positionYAnimation.isRunning());
+ }
+
+ QCOMPARE(tableView->rows(), model.rowCount());
+ QCOMPARE(tableView->bottomRow(), model.rowCount() - 1);
+ QCOMPARE(tableView->contentY(), (model.rowCount() - viewportRowCount) * delegateSize);
+ }
+}
+
+void tst_QQuickTableView::positionViewAtLastColumn_data()
+{
+ QTest::addColumn<QString>("signalToTest");
+ QTest::addColumn<bool>("animate");
+
+ QTest::newRow("positionOnColumnsChanged, animate=false") << "positionOnColumnsChanged" << false;
+ QTest::newRow("positionOnColumnsChanged, animate=true") << "positionOnColumnsChanged" << true;
+ QTest::newRow("positionOnContentWidthChanged, animate=false") << "positionOnContentWidthChanged" << false;
+ QTest::newRow("positionOnContentWidthChanged, animate=true") << "positionOnContentWidthChanged" << true;
+}
+
+void tst_QQuickTableView::positionViewAtLastColumn()
+{
+ // Check that we can make TableView always scroll to the
+ // last column in the model by positioning the view upon
+ // a columnsChanged callback
+ QFETCH(QString, signalToTest);
+ QFETCH(bool, animate);
+
+ LOAD_TABLEVIEW("positionlast.qml");
+
+ // Use a very large model to indirectly test that we "fast-flick" to
+ // the end at start-up (instead of loading and unloading columns, which
+ // would take forever).
+ TestModel model(2000000, 2000000);
+ tableView->setModel(QVariant::fromValue(&model));
+ tableView->setAnimate(animate);
+
+ view->rootObject()->setProperty(signalToTest.toUtf8().constData(), true);
+
+ WAIT_UNTIL_POLISHED;
+
+ const qreal delegateSize = 100.;
+ const qreal viewportColumnCount = tableView->width() / delegateSize;
+
+ // Check that the viewport is positioned at the last column at start-up
+ QCOMPARE(tableView->columns(), model.columnCount());
+ QCOMPARE(tableView->rightColumn(), model.columnCount() - 1);
+ QCOMPARE(tableView->contentX(), (model.columnCount() - viewportColumnCount) * delegateSize);
+
+ // Check that the viewport is positioned at the last
+ // column after more columns are added.
+ for (int column = 0; column < 2; ++column) {
+ model.addColumn(model.columnCount() - 1);
+
+ WAIT_UNTIL_POLISHED;
+
+ if (tableView->animate()) {
+ QVERIFY(tableViewPrivate->positionXAnimation.isRunning());
+ QTRY_VERIFY(!tableViewPrivate->positionXAnimation.isRunning());
+ }
+
+ QCOMPARE(tableView->columns(), model.columnCount());
+ QCOMPARE(tableView->rightColumn(), model.columnCount() - 1);
+ QCOMPARE(tableView->contentX(), (model.columnCount() - viewportColumnCount) * delegateSize);
+ }
+}
+
void tst_QQuickTableView::itemAtCell_data()
{
QTest::addColumn<QPoint>("cell");
@@ -4378,7 +4550,7 @@ void tst_QQuickTableView::warnOnWrongModelInSelectionModel()
selectionModel.setModel(&model2);
// And change currentIndex. This will produce a warning.
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*model differs.*"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*TableView.selectionModel.model.*"));
selectionModel.setCurrentIndex(model2.index(0, 0), QItemSelectionModel::NoUpdate);
}
@@ -4410,9 +4582,10 @@ void tst_QQuickTableView::selectionBehaviorCells_data()
void tst_QQuickTableView::selectionBehaviorCells()
{
- // Check that the TableView implement QQuickSelectableInterface setSelectionStartPos, setSelectionEndPos
- // and clearSelection correctly. Do this by calling setSelectionStartPos/setSelectionEndPos on top of
- // different cells, and see that we end up with the expected selections.
+ // Check that the TableView implement QQuickSelectableInterface startSelection,
+ // setSelectionStartPos, setSelectionEndPos and clearSelection correctly. Do this by
+ // calling setSelectionStartPos/setSelectionEndPos on top of different cells, and see
+ // that we end up with the expected selections.
QFETCH(QPoint, endCellDist);
LOAD_TABLEVIEW("tableviewwithselected1.qml");
@@ -4442,6 +4615,7 @@ void tst_QQuickTableView::selectionBehaviorCells()
const QPointF endPos(endItem->x(), endItem->y());
const QPointF endPosWrapped(endItemWrapped->x(), endItemWrapped->y());
+ QVERIFY(tableViewPrivate->startSelection(startPos, Qt::NoModifier));
tableViewPrivate->setSelectionStartPos(startPos);
tableViewPrivate->setSelectionEndPos(endPos);
@@ -4482,8 +4656,9 @@ void tst_QQuickTableView::selectionBehaviorCells()
void tst_QQuickTableView::selectionBehaviorRows()
{
- // Check that the TableView implement QQuickSelectableInterface setSelectionStartPos, setSelectionEndPos
- // and clearSelection correctly for QQuickTableView::SelectRows.
+ // Check that the TableView implement QQuickSelectableInterface startSelection,
+ // setSelectionStartPos, setSelectionEndPos and clearSelection correctly for
+ // QQuickTableView::SelectRows.
LOAD_TABLEVIEW("tableviewwithselected1.qml");
TestModel model(10, 10);
@@ -4498,6 +4673,7 @@ void tst_QQuickTableView::selectionBehaviorRows()
QCOMPARE(selectionModel.hasSelection(), false);
// Drag from row 0 to row 3
+ QVERIFY(tableViewPrivate->startSelection(QPointF(0, 0), Qt::NoModifier));
tableViewPrivate->setSelectionStartPos(QPointF(0, 0));
tableViewPrivate->setSelectionEndPos(QPointF(60, 60));
@@ -4518,6 +4694,7 @@ void tst_QQuickTableView::selectionBehaviorRows()
QCOMPARE(selectionModel.hasSelection(), false);
// Drag from row 3 to row 0 (and overshoot mouse)
+ QVERIFY(tableViewPrivate->startSelection(QPointF(60, 60), Qt::NoModifier));
tableViewPrivate->setSelectionStartPos(QPointF(60, 60));
tableViewPrivate->setSelectionEndPos(QPointF(-10, -10));
@@ -4536,8 +4713,9 @@ void tst_QQuickTableView::selectionBehaviorRows()
void tst_QQuickTableView::selectionBehaviorColumns()
{
- // Check that the TableView implement QQuickSelectableInterface setSelectionStartPos, setSelectionEndPos
- // and clearSelection correctly for QQuickTableView::SelectColumns.
+ // Check that the TableView implement QQuickSelectableInterface startSelection,
+ // setSelectionStartPos, setSelectionEndPos and clearSelection correctly for
+ // QQuickTableView::SelectColumns.
LOAD_TABLEVIEW("tableviewwithselected1.qml");
TestModel model(10, 10);
@@ -4552,6 +4730,7 @@ void tst_QQuickTableView::selectionBehaviorColumns()
QCOMPARE(selectionModel.hasSelection(), false);
// Drag from column 0 to column 3
+ QVERIFY(tableViewPrivate->startSelection(QPointF(60, 60), Qt::NoModifier));
tableViewPrivate->setSelectionStartPos(QPointF(0, 0));
tableViewPrivate->setSelectionEndPos(QPointF(60, 60));
@@ -4572,6 +4751,7 @@ void tst_QQuickTableView::selectionBehaviorColumns()
QCOMPARE(selectionModel.hasSelection(), false);
// Drag from column 3 to column 0 (and overshoot mouse)
+ QVERIFY(tableViewPrivate->startSelection(QPointF(60, 60), Qt::NoModifier));
tableViewPrivate->setSelectionStartPos(QPointF(60, 60));
tableViewPrivate->setSelectionEndPos(QPointF(-10, -10));
@@ -4590,8 +4770,8 @@ void tst_QQuickTableView::selectionBehaviorColumns()
void tst_QQuickTableView::selectionBehaviorDisabled()
{
- // Check that the TableView implement QQuickSelectableInterface setSelectionStartPos, setSelectionEndPos
- // and clearSelection correctly for QQuickTableView::SelectionDisabled.
+ // Check that the TableView implement QQuickSelectableInterface startSelection
+ // correctly for QQuickTableView::SelectionDisabled.
LOAD_TABLEVIEW("tableviewwithselected1.qml");
TestModel model(10, 10);
@@ -4605,18 +4785,18 @@ void tst_QQuickTableView::selectionBehaviorDisabled()
QCOMPARE(selectionModel.hasSelection(), false);
- // Drag from column 0 to column 3
- tableViewPrivate->setSelectionStartPos(QPointF(0, 0));
- tableViewPrivate->setSelectionEndPos(QPointF(60, 60));
+ // Try to start a selection
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*TableView.SelectionDisabled"));
+ QVERIFY(!tableViewPrivate->startSelection(QPointF(0, 0), Qt::NoModifier));
QCOMPARE(selectionModel.hasSelection(), false);
}
void tst_QQuickTableView::testSelectableStartPosEndPosOutsideView()
{
- // Call setSelectionStartPos and setSelectionEndPos with positions outside the view.
- // This should first of all not crash, but instead just clamp the selection to the
- // cells that are visible inside the view.
+ // Call startSelection, setSelectionStartPos and setSelectionEndPos with positions
+ // outside the view. This should first of all not crash, but instead just clamp the
+ // selection to the cells that are visible inside the view.
LOAD_TABLEVIEW("tableviewwithselected1.qml");
TestModel model(10, 10);
@@ -4637,6 +4817,7 @@ void tst_QQuickTableView::testSelectableStartPosEndPosOutsideView()
const QPointF outsideTop(centerPos.x(), -100);
const QPointF outsideBottom(centerPos.x(), tableView->height() + 100);
+ QVERIFY(tableViewPrivate->startSelection(centerPos, Qt::NoModifier));
tableViewPrivate->setSelectionStartPos(centerPos);
tableViewPrivate->setSelectionEndPos(outsideLeft);
@@ -4743,12 +4924,25 @@ void tst_QQuickTableView::setCurrentIndexFromSelectionModel()
QVERIFY(tableView->itemAtCell(cellAtEnd)->property(kCurrent).toBool());
}
+void tst_QQuickTableView::clearSelectionOnTap_data()
+{
+ QTest::addColumn<bool>("selectionEnabled");
+ QTest::newRow("selections enabled") << true;
+ QTest::newRow("selections disabled") << false;
+}
+
void tst_QQuickTableView::clearSelectionOnTap()
{
+ // Check that we clear the current selection when tapping
+ // inside TableView. But only if TableView has selections
+ // enabled. Otherwise, TableView should not touch the selection model.
+ QFETCH(bool, selectionEnabled);
LOAD_TABLEVIEW("tableviewwithselected2.qml");
TestModel model(40, 40);
tableView->setModel(QVariant::fromValue(&model));
+ if (!selectionEnabled)
+ tableView->setSelectionBehavior(QQuickTableView::SelectionDisabled);
WAIT_UNTIL_POLISHED;
@@ -4757,13 +4951,14 @@ void tst_QQuickTableView::clearSelectionOnTap()
tableView->selectionModel()->select(index, QItemSelectionModel::Select);
QCOMPARE(tableView->selectionModel()->selectedIndexes().size(), 1);
- // Click on a cell. This should remove the selection
- const auto item = tableView->itemAtCell(0, 0);
+ // Click on a cell
+ const auto item = tableView->itemAtIndex(tableView->index(0, 0));
QVERIFY(item);
QPoint localPos = QPoint(item->width() / 2, item->height() / 2);
QPoint pos = item->window()->contentItem()->mapFromItem(item, localPos).toPoint();
QTest::mouseClick(item->window(), Qt::LeftButton, Qt::NoModifier, pos);
- QCOMPARE(tableView->selectionModel()->selectedIndexes().size(), 0);
+
+ QCOMPARE(tableView->selectionModel()->hasSelection(), !selectionEnabled);
}
void tst_QQuickTableView::moveCurrentIndexUsingArrowKeys()
@@ -5358,7 +5553,7 @@ void tst_QQuickTableView::disablePointerNavigation()
// Enable navigation, and try again
tableView->setPointerNavigationEnabled(true);
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, pos);
- QCOMPARE(selectionModel.currentIndex(), tableView->modelIndex(0, 0));
+ QCOMPARE(selectionModel.currentIndex(), tableView->index(0, 0));
QVERIFY(item0_0->property("current").toBool());
QCOMPARE(tableView->currentColumn(), cell0_0.x());
QCOMPARE(tableView->currentRow(), cell0_0.y());
@@ -5754,7 +5949,7 @@ void tst_QQuickTableView::boundDelegateComponent()
QVERIFY(inner != nullptr);
QQuickTableView *tableView = qobject_cast<QQuickTableView *>(inner);
QVERIFY(tableView != nullptr);
- QObject *item = tableView->itemAtCell(0, 0);
+ QObject *item = tableView->itemAtCell({0, 0});
QVERIFY(item);
QCOMPARE(item->objectName(), QLatin1String("fooouterundefined"));
@@ -5762,7 +5957,7 @@ void tst_QQuickTableView::boundDelegateComponent()
QVERIFY(inner2 != nullptr);
QQuickTableView *tableView2 = qobject_cast<QQuickTableView *>(inner2);
QVERIFY(tableView2 != nullptr);
- QObject *item2 = tableView2->itemAtCell(0, 0);
+ QObject *item2 = tableView2->itemAtCell({0, 0});
QVERIFY(item2);
QCOMPARE(item2->objectName(), QLatin1String("fooouter0"));
@@ -5784,7 +5979,7 @@ void tst_QQuickTableView::boundDelegateComponent()
QVERIFY(innerTableView != nullptr);
QCOMPARE(innerTableView->rows(), 3);
for (int i = 0; i < 3; ++i)
- QVERIFY(innerTableView->itemAtCell(0, i)->objectName().isEmpty());
+ QVERIFY(innerTableView->itemAtIndex(innerTableView->index(i, 0))->objectName().isEmpty());
}
void tst_QQuickTableView::setColumnWidth_data()
@@ -6127,6 +6322,47 @@ void tst_QQuickTableView::deletedDelegate()
QTRY_COMPARE(tv->delegate(), nullptr);
}
+void tst_QQuickTableView::tableViewInteractive()
+{
+ LOAD_TABLEVIEW("tableviewinteractive.qml");
+
+ auto *root = view->rootObject();
+ QVERIFY(root);
+ auto *window = root->window();
+ QVERIFY(window);
+
+ int eventCount = root->property("eventCount").toInt();
+ QCOMPARE(eventCount, 0);
+
+ // Event though we make 'interactive' as false, the TableView has
+ // pointerNacigationEnabled set as true by default, which allows it to consume
+ // mouse events and thus, eventCount still be zero
+ tableView->setInteractive(false);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
+ eventCount = root->property("eventCount").toInt();
+ QCOMPARE(eventCount, 0);
+
+ // Making both 'interactive' and 'pointerNavigationEnabled' as false, doesn't
+ // allow TableView (and its parent Flickable) to consume mouse event and it
+ // passes to the below visual item
+ tableView->setInteractive(false);
+ tableView->setPointerNavigationEnabled(false);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
+ eventCount = root->property("eventCount").toInt();
+ QCOMPARE(eventCount, 1);
+
+ // Making 'interactive' as true and 'pointerNavigationEnabled' as false,
+ // allows parent of TableView (i.e. Flickable) to consume mouse events
+ tableView->setInteractive(true);
+ tableView->setPointerNavigationEnabled(false);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
+ eventCount = root->property("eventCount").toInt();
+ QCOMPARE(eventCount, 1);
+}
+
void tst_QQuickTableView::columnResizing_data()
{
QTest::addColumn<int>("column");
@@ -6161,7 +6397,7 @@ void tst_QQuickTableView::columnResizing()
QSignalSpy currentIndexSpy(tableView->selectionModel(), &QItemSelectionModel::currentChanged);
QSignalSpy selectionSpy(tableView->selectionModel(), &QItemSelectionModel::selectionChanged);
- const auto item = tableView->itemAtCell(column, 0);
+ const auto item = tableView->itemAtIndex(tableView->index(0, column));
QQuickWindow *window = item->window();
const qreal columnStartWidth = tableView->columnWidth(column);
@@ -6212,7 +6448,7 @@ void tst_QQuickTableView::rowResizing()
QSignalSpy currentIndexSpy(tableView->selectionModel(), &QItemSelectionModel::currentChanged);
QSignalSpy selectionSpy(tableView->selectionModel(), &QItemSelectionModel::selectionChanged);
- const auto item = tableView->itemAtCell(0, row);
+ const auto item = tableView->itemAtIndex(tableView->index(row, 0));
QQuickWindow *window = item->window();
const qreal rowStartHeight = tableView->rowHeight(row);
@@ -6265,7 +6501,7 @@ void tst_QQuickTableView::rowAndColumnResizing()
QCOMPARE(tableView->explicitColumnWidth(rowAndColumn), -1);
QCOMPARE(tableView->explicitRowHeight(rowAndColumn), -1);
- const auto item = tableView->itemAtCell(rowAndColumn, rowAndColumn);
+ const auto item = tableView->itemAtIndex(tableView->index(rowAndColumn, rowAndColumn));
QVERIFY(item);
if (addDelegateDragHandler) {
@@ -6320,7 +6556,7 @@ void tst_QQuickTableView::columnResizingDisabled()
const int row = 1;
QCOMPARE(tableView->explicitRowHeight(row), -1);
- const auto item = tableView->itemAtCell(0, row);
+ const auto item = tableView->itemAtIndex(tableView->index(row, 0));
QQuickWindow *window = item->window();
const QPoint localPos = QPoint(item->width() / 2, item->height());
@@ -6357,7 +6593,7 @@ void tst_QQuickTableView::rowResizingDisabled()
const int row = 1;
QCOMPARE(tableView->explicitRowHeight(row), -1);
- const auto item = tableView->itemAtCell(0, row);
+ const auto item = tableView->itemAtIndex(tableView->index(row, 0));
QQuickWindow *window = item->window();
const QPoint localPos = QPoint(item->width() / 2, item->height());
@@ -6395,7 +6631,7 @@ void tst_QQuickTableView::dragFromCellCenter()
const int row = 1;
QCOMPARE(tableView->explicitRowHeight(row), -1);
- const auto item = tableView->itemAtCell(0, row);
+ const auto item = tableView->itemAtIndex(tableView->index(row, 0));
QQuickWindow *window = item->window();
const QPoint localPos = QPoint(item->width() / 2, item->height() / 2);
@@ -6590,7 +6826,7 @@ void tst_QQuickTableView::editUsingEditTriggers()
if (editTriggers & QQuickTableView::SelectedTapped) {
// select cell first, then tap on it
- tableView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate);
+ tableView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::Select);
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
QCOMPARE(tableView->selectionModel()->currentIndex(), index1);
const auto editItem1 = tableView->property(kEditItem).value<QQuickItem *>();
@@ -6611,6 +6847,11 @@ void tst_QQuickTableView::editUsingEditTriggers()
QVERIFY(!tableView->property(kEditItem).value<QQuickItem *>());
QVERIFY(!tableView->property(kEditIndex).value<QModelIndex>().isValid());
QCOMPARE(tableView->selectionModel()->currentIndex(), index2);
+
+ // tap on the current cell. This alone should not start an edit (unless it's also selected)
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1);
+ QVERIFY(!tableView->property(kEditItem).value<QQuickItem *>());
+ QVERIFY(!tableView->property(kEditIndex).value<QModelIndex>().isValid());
}
if (editTriggers & QQuickTableView::EditKeyPressed) {
@@ -7075,7 +7316,7 @@ void tst_QQuickTableView::editWarning_noEditDelegate()
WAIT_UNTIL_POLISHED;
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*cannot edit: no TableView.editDelegate set!"));
- tableView->edit(tableView->modelIndex(1, 1));
+ tableView->edit(tableView->index(1, 1));
}
void tst_QQuickTableView::editWarning_invalidIndex()
@@ -7090,7 +7331,7 @@ void tst_QQuickTableView::editWarning_invalidIndex()
WAIT_UNTIL_POLISHED;
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*cannot edit: index is not valid!"));
- tableView->edit(tableView->modelIndex(-1, -1));
+ tableView->edit(tableView->index(-1, -1));
}
void tst_QQuickTableView::editWarning_nonEditableModelItem()
@@ -7108,7 +7349,7 @@ void tst_QQuickTableView::editWarning_nonEditableModelItem()
WAIT_UNTIL_POLISHED;
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*cannot edit:.*flags.*Qt::ItemIsEditable"));
- tableView->edit(tableView->modelIndex(1, 1));
+ tableView->edit(tableView->index(1, 1));
}
void tst_QQuickTableView::attachedPropertiesOnEditDelegate()
@@ -7214,7 +7455,7 @@ void tst_QQuickTableView::requiredPropertiesOnEditDelegate()
const QPoint cell(1, 1);
const QModelIndex index1 = tableView->modelIndex(cell);
- const QModelIndex index2 = tableView->modelIndex(2, 2);
+ const QModelIndex index2 = tableView->index(2, 2);
tableView->edit(index1);
@@ -7232,6 +7473,81 @@ void tst_QQuickTableView::requiredPropertiesOnEditDelegate()
QCOMPARE(textInput->property("current").toBool(), false);
}
+void tst_QQuickTableView::resettingRolesRespected()
+{
+ LOAD_TABLEVIEW("resetModelData.qml");
+
+ TestModel model(1, 1);
+ tableView->setModel(QVariant::fromValue(&model));
+
+ WAIT_UNTIL_POLISHED;
+
+ QVERIFY(!tableView->property("success").toBool());
+ model.useCustomRoleNames(true);
+ QTRY_VERIFY(tableView->property("success").toBool());
+}
+
+void tst_QQuickTableView::checkScroll_data()
+{
+ QTest::addColumn<bool>("resizableColumns");
+ QTest::addColumn<bool>("resizableRows");
+ QTest::newRow("T, T") << true << true;
+ QTest::newRow("T, F") << true << false;
+ QTest::newRow("F, T") << false << true;
+ QTest::newRow("F, F") << false << false;
+}
+
+/*!
+ Make sure that the TableView is scrollable regardless
+ of the values of resizableColumns and resizableRows.
+*/
+void tst_QQuickTableView::checkScroll() // QTBUG-116566
+{
+ QFETCH(bool, resizableColumns);
+ QFETCH(bool, resizableRows);
+ LOAD_TABLEVIEW("plaintableview.qml"); // gives us 'tableView' variable
+
+ tableView->setResizableColumns(resizableColumns);
+ tableView->setResizableRows(resizableRows);
+
+ auto model = TestModelAsVariant(20, 10);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Scroll with the mouse wheel
+ sendWheelEvent(view, {10, 10}, {0, -120}, {0, -120});
+
+ // Check that scrolling succeeded
+ QTRY_COMPARE_GT(tableView->contentY(), 20);
+}
+
+void tst_QQuickTableView::checkRebuildJsModel()
+{
+ LOAD_TABLEVIEW("resetJsModelData.qml"); // gives us 'tableView' variable
+
+ // Generate javascript model
+ const int size = 5;
+ const char* modelUpdated = "modelUpdated";
+
+ QJSEngine jsEngine;
+ QJSValue jsArray;
+ jsArray = jsEngine.newArray(size);
+ for (int i = 0; i < size; ++i)
+ jsArray.setProperty(i, QRandomGenerator::global()->generate());
+
+ QVariant jsModel = QVariant::fromValue(jsArray);
+ tableView->setModel(jsModel);
+ WAIT_UNTIL_POLISHED;
+
+ // Model change would be triggered for the first time
+ QCOMPARE(tableView->property(modelUpdated).toInt(), 1);
+
+ // Set the same model once again and check if model changes
+ tableView->setModel(jsModel);
+ QCOMPARE(tableView->property(modelUpdated).toInt(), 1);
+}
+
QTEST_MAIN(tst_QQuickTableView)
#include "tst_qquicktableview.moc"
diff --git a/tests/auto/quick/qquicktext/CMakeLists.txt b/tests/auto/quick/qquicktext/CMakeLists.txt
index b4f2bae534..68330befd9 100644
--- a/tests/auto/quick/qquicktext/CMakeLists.txt
+++ b/tests/auto/quick/qquicktext/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktext Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktext LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml b/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml
new file mode 100644
index 0000000000..bb269e6ad5
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml
@@ -0,0 +1,27 @@
+import QtQuick
+
+Item {
+ id: root
+ property bool ok: false
+ width: 640
+ height: 480
+
+ Text {
+ id: text
+ text: "This is a quite long text. Click me and i should remain visible!!! Sadly this doesn't happen"
+ elide: Text.ElideRight
+ anchors {
+ fill: parent
+ margins: 1
+ }
+ }
+
+ Component.onCompleted: {
+ text.width = 300;
+ text.height = 0;
+ text.width = 0;
+ text.height = 30;
+ text.width = 300;
+ root.ok = text.paintedWidth > 0 && text.paintedHeight > 0
+ }
+}
diff --git a/tests/auto/quick/qquicktext/data/lineLayout.qml b/tests/auto/quick/qquicktext/data/lineLayout.qml
index 5a980de7da..74b9ecb500 100644
--- a/tests/auto/quick/qquicktext/data/lineLayout.qml
+++ b/tests/auto/quick/qquicktext/data/lineLayout.qml
@@ -20,7 +20,7 @@ Rectangle {
text: "<b>Lorem ipsum</b> dolor sit amet, consectetur adipiscing elit. Integer at ante dui. Sed eu egestas est.
<br/><p><i>Maecenas nec libero leo. Sed ac leo eget ipsum ultricies viverra sit amet eu orci. Praesent et tortor risus, viverra accumsan sapien. Sed faucibus eleifend lectus, sed euismod urna porta eu. Aenean ultricies lectus ut orci dictum quis convallis nisi ultrices. Nunc elit mi, iaculis a porttitor rutrum, venenatis malesuada nisi. Suspendisse turpis quam, euismod non imperdiet et, rutrum nec ligula. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam semper tristique metus eu sodales. Integer eget risus ipsum. Quisque ut risus ut nulla tristique volutpat at sit amet nisl. Aliquam pulvinar auctor diam nec bibendum.</i><br/><p>Quisque luctus sapien id arcu volutpat pharetra. Praesent pretium imperdiet euismod. Integer fringilla rhoncus condimentum. Quisque sit amet ornare nulla. Cras sapien augue, sagittis a dictum id, suscipit et nunc. Cras vitae augue in enim elementum venenatis sed nec risus. Sed nisi quam, mollis quis auctor ac, vestibulum in neque. Vivamus eu justo risus. Suspendisse vel mollis est. Vestibulum gravida interdum mi, in molestie neque gravida in. Donec nibh odio, mattis facilisis vulputate et, scelerisque ut felis. Sed ornare eros nec odio aliquam eu varius augue adipiscing. Vivamus sit amet massa dapibus sapien pulvinar consectetur a sit amet felis. Cras non mi id libero dictum iaculis id dignissim eros. Praesent eget enim dui, sed bibendum neque. Ut interdum nisl id leo malesuada ornare. Pellentesque id nisl eu odio volutpat posuere et at massa. Pellentesque nec lorem justo. Integer sem urna, pharetra sed sagittis vitae, condimentum ac felis. Ut vitae sapien ac tortor adipiscing pharetra. Cras tristique urna tempus ante volutpat eleifend non eu ligula. Mauris sodales nisl et lorem tristique sodales. Mauris arcu orci, vehicula semper cursus ac, dapibus ut mi."
- onLineLaidOut: {
+ onLineLaidOut: (line) => {
line.width = line.number * 15
if (line.number === 30 || line.number === 60) {
main.off = line.y
diff --git a/tests/auto/quick/qquicktext/data/lineLayoutFontUpdate.qml b/tests/auto/quick/qquicktext/data/lineLayoutFontUpdate.qml
new file mode 100644
index 0000000000..d018e31d29
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/lineLayoutFontUpdate.qml
@@ -0,0 +1,25 @@
+import QtQuick
+
+Item {
+ width: 640
+ height: 480
+
+ FontLoader {
+ id: fontIcons
+ source: "tarzeau_ocr_a.ttf"
+ }
+
+ Text {
+ id: exampleText
+ objectName: "exampleText"
+ text: "Example multiline text"
+ wrapMode: Text.WordWrap
+ width: 100
+ onLineLaidOut: (line) => {
+ if (line.number < 1) {
+ line.x += 40;
+ line.width -= 40;
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktext/data/lineLayoutImplicitWidth.qml b/tests/auto/quick/qquicktext/data/lineLayoutImplicitWidth.qml
index 0b04028417..94a6ac2797 100644
--- a/tests/auto/quick/qquicktext/data/lineLayoutImplicitWidth.qml
+++ b/tests/auto/quick/qquicktext/data/lineLayoutImplicitWidth.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 Jolla Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
@@ -23,7 +23,7 @@ Rectangle {
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis ante tristique, fermentum magna at, varius lacus. Donec elementum orci sit amet ligula efficitur, eget sodales orci porttitor. Etiam laoreet tellus quis nisi mollis lacinia. Cras vitae nisl sed nunc semper blandit. Duis egestas commodo lacus non congue. Fusce quis rhoncus urna. And magna arcu, sodales vitae nunc vel, rutrum hendrerit magna. Nullam imperdiet porttitor sem at euismod. Morbi faucibus libero sit amet vestibulum aliquam. Duis consectetur lacinia malesuada. Sed quis ante dui. Name dignissim faucibus felis. Quisque dapibus aliquam ante, eu cursus elit dictum in. Mauris placerat efficitur rutrum."
- onLineLaidOut: {
+ onLineLaidOut: (line) => {
var n = line.number
// Save information about the line so the autotest can retrieve it
diff --git a/tests/auto/quick/qquicktext/data/lineLayoutRelayout.qml b/tests/auto/quick/qquicktext/data/lineLayoutRelayout.qml
index 2e1aa6a17d..2f46159602 100644
--- a/tests/auto/quick/qquicktext/data/lineLayoutRelayout.qml
+++ b/tests/auto/quick/qquicktext/data/lineLayoutRelayout.qml
@@ -29,7 +29,7 @@ leo eget ipsum ultricies viverra sit amet eu orci. Praesent et tortor risus,
viverra accumsan sapien. Sed faucibus eleifend lectus, sed euismod urna porta
eu. Quisque vitae accumsan lectus."
- onLineLaidOut: {
+ onLineLaidOut: (line) => {
line.width = width / 2
if (line.y + line.height >= height) {
diff --git a/tests/auto/quick/qquicktext/data/loaderActiveOnVisible.qml b/tests/auto/quick/qquicktext/data/loaderActiveOnVisible.qml
new file mode 100644
index 0000000000..915dc21d84
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/loaderActiveOnVisible.qml
@@ -0,0 +1,24 @@
+import QtQuick
+
+Rectangle {
+ id: root
+ visible: false
+ width: 320
+ height: 240
+ Flickable {
+ id: flickable
+ anchors {
+ fill: root
+ margins: 10
+ }
+ contentHeight: loader.height
+
+ Loader {
+ id: loader
+ width: flickable.width
+ active: root.visible
+ source: "long.qml"
+ }
+ }
+ Component.onCompleted: visible = true
+}
diff --git a/tests/auto/quick/qquicktext/data/long.qml b/tests/auto/quick/qquicktext/data/long.qml
new file mode 100644
index 0000000000..ca27efb35a
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/long.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Text {
+ width: 300
+ wrapMode: Text.WordWrap
+ text: "GNU GENERAL PUBLIC LICENSE\r\nVersion 2, June 1991\r\nCopyright (C) 1989, 1991 Free Software Foundation, Inc.\r\n51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\r\n\r\nEveryone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\r\n\r\nPreamble\r\n\r\nThe licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.\r\n\r\nWhen we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.\r\n\r\nTo protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.\r\n\r\nFor example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\r\n\r\nWe protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and\/or modify the software.\r\n\r\nAlso, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.\r\n\r\nFinally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.\r\n\r\nThe precise terms and conditions for copying, distribution and modification follow.\r\n\r\nTERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r\n\r\n1. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \"Program\", below, refers to any such program or work, and a \"work based on the Program\" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and\/or translated into another language. (Hereinafter, translation is included without limitation in the term \"modification\".) Each licensee is addressed as \"you\".\r\n\r\nActivities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.\r\n\r\n2. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.\r\n\r\nYou may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.\r\n\r\n3. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:\r\n\r\na) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.\r\n\r\nb) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.\r\n\r\nc) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)\r\n\r\nThese requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.\r\n\r\nThus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.\r\n\r\nIn addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.\r\n\r\n4. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:\r\n\r\na) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\r\n\r\nb) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\r\n\r\nc) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)\r\n\r\nThe source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.\r\n\r\nIf distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.\r\n\r\n5. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.\r\n\r\n6. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.\r\n\r\n7. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.\r\n\r\n8. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.\r\n\r\nIf any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.\r\n\r\nIt is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author\/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.\r\n\r\nThis section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.\r\n\r\n9. If the distribution and\/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.\r\n\r\n10. The Free Software Foundation may publish revised and\/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\r\n\r\nEach version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \"any later version\", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.\r\n\r\n11. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.\r\n\r\nNO WARRANTY\r\n\r\n12. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND\/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\r\n\r\n13. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND\/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\r\nEND OF TERMS AND CONDITIONS\r\n\r\n"
+}
diff --git a/tests/auto/quick/qquicktext/data/qtbug_106205.qml b/tests/auto/quick/qquicktext/data/qtbug_106205.qml
new file mode 100644
index 0000000000..e4db73557a
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/qtbug_106205.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+ id: root
+ objectName: "root"
+ visible: true
+
+ anchors.fill: parent
+ property string text: ""
+
+ ScrollView {
+ anchors.fill: parent
+ Rectangle {
+ x: 100
+ width: 100
+ height: 30
+ color: "lightgray"
+
+ Text {
+ objectName: "textOutsideViewport"
+ text: root.text
+ width: 50
+ clip: true
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktext/data/tarzeau_ocr_a.ttf b/tests/auto/quick/qquicktext/data/tarzeau_ocr_a.ttf
new file mode 100644
index 0000000000..cf93f9651f
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/tarzeau_ocr_a.ttf
Binary files differ
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index c8d9561803..b731f1b069 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QTextDocument>
@@ -7,11 +7,12 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qjsvalue.h>
#include <QtQuick/private/qquicktext_p.h>
+#include <QtQuick/private/qquickflickable_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
+#include <QtQuick/private/qquickpixmapcache_p.h>
#include <QtQuickTest/QtQuickTest>
#include <private/qquicktext_p_p.h>
-#include <private/qquicktextnode_p.h>
-#include <private/qquicktextdocument_p.h>
+#include <private/qsginternaltextnode_p.h>
#include <private/qquickvaluetypes_p.h>
#include <QFontMetrics>
#include <qmath.h>
@@ -28,12 +29,17 @@ DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
Q_DECLARE_METATYPE(QQuickText::TextFormat)
-QT_BEGIN_NAMESPACE
-extern void qt_setQtEnableTestFont(bool value);
-QT_END_NAMESPACE
+typedef QVector<QPointF> PointVector;
+Q_DECLARE_METATYPE(PointVector);
+
+typedef qreal (*ExpectedBaseline)(QQuickText *item);
+Q_DECLARE_METATYPE(ExpectedBaseline)
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+QT_BEGIN_NAMESPACE
+extern void qt_setQtEnableTestFont(bool value);
+
class tst_qquicktext : public QQmlDataTest
{
Q_OBJECT
@@ -47,12 +53,14 @@ private slots:
void wrap();
void elide();
void elideParentChanged();
+ void elideRelayoutAfterZeroWidth_data();
void elideRelayoutAfterZeroWidth();
void multilineElide_data();
void multilineElide();
void implicitElide_data();
void implicitElide();
void textFormat();
+ void clipRectOutsideViewportDynamicallyChanged();
void baseUrl();
void embeddedImages_data();
@@ -100,8 +108,10 @@ private slots:
void clipRect();
void largeTextObservesViewport_data();
void largeTextObservesViewport();
+ void largeTextInDelayedLoader();
void lineLaidOut();
void lineLaidOutRelayout();
+ void lineLaidOutFontUpdate();
void lineLaidOutHAlign();
void lineLaidOutImplicitWidth();
@@ -248,13 +258,12 @@ void tst_qquicktext::text()
{
QQmlComponent textComponent(&engine);
textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->text(), QString(""));
QCOMPARE(textObject->width(), qreal(0));
-
- delete textObject;
}
for (int i = 0; i < standard.size(); i++)
@@ -262,14 +271,12 @@ void tst_qquicktext::text()
QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
-
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->text(), standard.at(i));
QVERIFY(textObject->width() > 0);
-
- delete textObject;
}
for (int i = 0; i < richText.size(); i++)
@@ -277,14 +284,13 @@ void tst_qquicktext::text()
QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QString expected = richText.at(i);
QCOMPARE(textObject->text(), expected.replace("\\\"", "\""));
QVERIFY(textObject->width() > 0);
-
- delete textObject;
}
}
@@ -294,12 +300,11 @@ void tst_qquicktext::width()
{
QQmlComponent textComponent(&engine);
textComponent.setData("import QtQuick 2.0\nText { text: \"\" }", QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->width(), 0.);
-
- delete textObject;
}
bool requiresUnhintedMetrics = !qmlDisableDistanceField();
@@ -341,14 +346,13 @@ void tst_qquicktext::width()
QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QVERIFY(textObject->boundingRect().width() > 0);
QCOMPARE(textObject->width(), qreal(metricWidth));
QVERIFY(textObject->textFormat() == QQuickText::AutoText); // setting text doesn't change format
-
- delete textObject;
}
for (int i = 0; i < richText.size(); i++)
@@ -358,7 +362,8 @@ void tst_qquicktext::width()
QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\"; textFormat: Text.RichText }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
@@ -370,8 +375,6 @@ void tst_qquicktext::width()
QCOMPARE(int(textObject->width()), int(doc->idealWidth()));
QCOMPARE(textObject->textFormat(), QQuickText::RichText);
-
- delete textObject;
}
}
@@ -382,14 +385,13 @@ void tst_qquicktext::wrap()
{
QQmlComponent textComponent(&engine);
textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; wrapMode: Text.WordWrap; width: 300 }", QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
textHeight = textObject->height();
- QVERIFY(textObject != nullptr);
QCOMPARE(textObject->wrapMode(), QQuickText::WordWrap);
QCOMPARE(textObject->width(), 300.);
-
- delete textObject;
}
for (int i = 0; i < standard.size(); i++)
@@ -397,7 +399,8 @@ void tst_qquicktext::wrap()
QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->width(), 30.);
@@ -406,8 +409,6 @@ void tst_qquicktext::wrap()
int oldHeight = textObject->height();
textObject->setWidth(100);
QVERIFY(textObject->height() < oldHeight);
-
- delete textObject;
}
for (int i = 0; i < richText.size(); i++)
@@ -415,7 +416,8 @@ void tst_qquicktext::wrap()
QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->width(), 30.);
@@ -424,8 +426,6 @@ void tst_qquicktext::wrap()
qreal oldHeight = textObject->height();
textObject->setWidth(100);
QVERIFY(textObject->height() < oldHeight);
-
- delete textObject;
}
// Check that increasing width from idealWidth will cause a relayout
@@ -434,7 +434,8 @@ void tst_qquicktext::wrap()
QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; textFormat: Text.RichText; width: 30; text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->width(), 30.);
@@ -453,8 +454,6 @@ void tst_qquicktext::wrap()
qreal oldHeight = textObject->height();
textObject->setWidth(100);
QVERIFY(textObject->height() < oldHeight);
-
- delete textObject;
}
// richtext again with a fixed height
@@ -463,7 +462,8 @@ void tst_qquicktext::wrap()
QString componentStr = "import QtQuick 2.0\nText { wrapMode: Text.WordWrap; width: 30; height: 50; text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->width(), 30.);
@@ -472,8 +472,6 @@ void tst_qquicktext::wrap()
qreal oldHeight = textObject->implicitHeight();
textObject->setWidth(100);
QVERIFY(textObject->implicitHeight() < oldHeight);
-
- delete textObject;
}
{
@@ -511,12 +509,12 @@ void tst_qquicktext::elide()
{
QQmlComponent textComponent(&engine);
textComponent.setData(("import QtQuick 2.0\nText { text: \"\"; "+elide+" width: 100 }").toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->elideMode(), m);
QCOMPARE(textObject->width(), 100.);
-
- delete textObject;
}
for (int i = 0; i < standard.size(); i++)
@@ -524,15 +522,15 @@ void tst_qquicktext::elide()
QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->elideMode(), m);
QCOMPARE(textObject->width(), 100.);
if (m != QQuickText::ElideNone && !standard.at(i).contains('\n'))
QVERIFY(textObject->contentWidth() <= textObject->width());
-
- delete textObject;
}
for (int i = 0; i < richText.size(); i++)
@@ -540,15 +538,15 @@ void tst_qquicktext::elide()
QString componentStr = "import QtQuick 2.0\nText { "+elide+" width: 100; text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->elideMode(), m);
QCOMPARE(textObject->width(), 100.);
if (m != QQuickText::ElideNone && standard.at(i).contains("<br>"))
QVERIFY(textObject->contentWidth() <= textObject->width());
-
- delete textObject;
}
}
}
@@ -592,10 +590,19 @@ void tst_qquicktext::elideParentChanged()
QCOMPARE(actualItemImageGrab, expectedItemImageGrab);
}
+void tst_qquicktext::elideRelayoutAfterZeroWidth_data()
+{
+ QTest::addColumn<QByteArray>("fileName");
+
+ QTest::newRow("no_margins") << QByteArray("elideZeroWidth.qml");
+ QTest::newRow("with_margins") << QByteArray("elideZeroWidthWithMargins.qml");
+}
+
void tst_qquicktext::elideRelayoutAfterZeroWidth()
{
+ QFETCH(const QByteArray, fileName);
QQmlEngine engine;
- QQmlComponent component(&engine, testFileUrl("elideZeroWidth.qml"));
+ QQmlComponent component(&engine, testFileUrl(fileName.constData()));
QScopedPointer<QObject> root(component.create());
QVERIFY2(root, qPrintable(component.errorString()));
QVERIFY(root->property("ok").toBool());
@@ -699,6 +706,7 @@ void tst_qquicktext::implicitElide()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QVERIFY(textObject->contentWidth() <= textObject->width());
textObject->setText("the quick brown fox jumped over");
@@ -712,7 +720,8 @@ void tst_qquicktext::textFormat()
{
QQmlComponent textComponent(&engine);
textComponent.setData("import QtQuick 2.0\nText { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->textFormat(), QQuickText::RichText);
@@ -720,13 +729,12 @@ void tst_qquicktext::textFormat()
QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
QVERIFY(textPrivate != nullptr);
QVERIFY(textPrivate->richText);
-
- delete textObject;
}
{
QQmlComponent textComponent(&engine);
textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\" }", QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->textFormat(), QQuickText::AutoText);
@@ -734,18 +742,15 @@ void tst_qquicktext::textFormat()
QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
QVERIFY(textPrivate != nullptr);
QVERIFY(textPrivate->styledText);
-
- delete textObject;
}
{
QQmlComponent textComponent(&engine);
textComponent.setData("import QtQuick 2.0\nText { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->textFormat(), QQuickText::PlainText);
-
- delete textObject;
}
{
@@ -827,6 +832,28 @@ void tst_qquicktext::textFormat()
}
}
+void tst_qquicktext::clipRectOutsideViewportDynamicallyChanged()
+{
+ // QTBUG-106205
+ QScopedPointer<QQuickView> view(createView(testFile("qtbug_106205.qml")));
+ view->setWidth(100);
+ view->setHeight(200);
+ view->showNormal();
+ QQuickItem *root = view->rootObject();
+ QVERIFY(root);
+ QVERIFY(QTest::qWaitForWindowExposed(view.get()));
+
+ auto clipRectMatches = [&]() -> bool {
+ auto *textOutsideInitialViewport = root->findChild<QQuickText *>("textOutsideViewport");
+ if (!textOutsideInitialViewport)
+ return false;
+ auto *clipNode = QQuickItemPrivate::get(textOutsideInitialViewport)->clipNode();
+
+ return textOutsideInitialViewport->clipRect() == clipNode->clipRect();
+ };
+ QTRY_VERIFY(clipRectMatches());
+}
+
//the alignment tests may be trivial o.oa
void tst_qquicktext::horizontalAlignment()
{
@@ -839,11 +866,11 @@ void tst_qquicktext::horizontalAlignment()
QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
-
- delete textObject;
}
}
@@ -854,11 +881,11 @@ void tst_qquicktext::horizontalAlignment()
QString componentStr = "import QtQuick 2.0\nText { horizontalAlignment: \"" + horizontalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QCOMPARE((int)textObject->hAlign(), (int)horizontalAlignmentments.at(j));
-
- delete textObject;
}
}
@@ -975,10 +1002,11 @@ void tst_qquicktext::horizontalAlignment_RightToLeft()
QString componentStr = "import QtQuick 2.0\nText {}";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->hAlign(), qApp->inputMethod()->inputDirection() == Qt::LeftToRight ?
QQuickText::AlignLeft : QQuickText::AlignRight);
- delete textObject;
}
int tst_qquicktext::numberOfNonWhitePixels(int fromX, int toX, const QImage &image)
@@ -1076,12 +1104,11 @@ void tst_qquicktext::verticalAlignment()
QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
-
- delete textObject;
}
}
@@ -1092,12 +1119,11 @@ void tst_qquicktext::verticalAlignment()
QString componentStr = "import QtQuick 2.0\nText { verticalAlignment: \"" + verticalAlignmentmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->vAlign(), (int)verticalAlignmentments.at(j));
-
- delete textObject;
}
}
@@ -1110,74 +1136,74 @@ void tst_qquicktext::font()
QString componentStr = "import QtQuick 2.0\nText { font.pointSize: 40; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->font().pointSize(), 40);
QCOMPARE(textObject->font().bold(), false);
QCOMPARE(textObject->font().italic(), false);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { font.pixelSize: 40; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->font().pixelSize(), 40);
QCOMPARE(textObject->font().bold(), false);
QCOMPARE(textObject->font().italic(), false);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { font.bold: true; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->font().bold(), true);
QCOMPARE(textObject->font().italic(), false);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { font.italic: true; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->font().italic(), true);
QCOMPARE(textObject->font().bold(), false);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { font.family: \"Helvetica\"; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->font().family(), QString("Helvetica"));
QCOMPARE(textObject->font().bold(), false);
QCOMPARE(textObject->font().italic(), false);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { font.family: \"\"; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->font().family(), QString(""));
-
- delete textObject;
}
}
@@ -1189,17 +1215,19 @@ void tst_qquicktext::style()
QString componentStr = "import QtQuick 2.0\nText { style: \"" + styleStrings.at(i) + "\"; styleColor: \"white\"; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE((int)textObject->style(), (int)styles.at(i));
QCOMPARE(textObject->styleColor(), QColor("white"));
-
- delete textObject;
}
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QRectF brPre = textObject->boundingRect();
textObject->setStyle(QQuickText::Outline);
@@ -1207,8 +1235,6 @@ void tst_qquicktext::style()
QVERIFY(brPre.width() < brPost.width());
QVERIFY(brPre.height() < brPost.height());
-
- delete textObject;
}
void tst_qquicktext::color()
@@ -1219,13 +1245,13 @@ void tst_qquicktext::color()
QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
QCOMPARE(textObject->styleColor(), QColor("black"));
QCOMPARE(textObject->linkColor(), QColor("blue"));
-
- delete textObject;
}
for (int i = 0; i < colorStrings.size(); i++)
@@ -1233,7 +1259,9 @@ void tst_qquicktext::color()
QString componentStr = "import QtQuick 2.0\nText { styleColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(i)));
// default color to black?
@@ -1264,8 +1292,6 @@ void tst_qquicktext::color()
textObject->setLinkColor(QColor("blue"));
QCOMPARE(textObject->linkColor(), QColor("blue"));
QCOMPARE(linkColorSpy.size(), 2);
-
- delete textObject;
}
for (int i = 0; i < colorStrings.size(); i++)
@@ -1273,13 +1299,13 @@ void tst_qquicktext::color()
QString componentStr = "import QtQuick 2.0\nText { linkColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->styleColor(), QColor("black"));
QCOMPARE(textObject->color(), QColor("black"));
QCOMPARE(textObject->linkColor(), QColor(colorStrings.at(i)));
-
- delete textObject;
}
for (int i = 0; i < colorStrings.size(); i++)
@@ -1293,13 +1319,13 @@ void tst_qquicktext::color()
"text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->color(), QColor(colorStrings.at(i)));
QCOMPARE(textObject->styleColor(), QColor(colorStrings.at(j)));
QCOMPARE(textObject->linkColor(), QColor(colorStrings.at(j)));
-
- delete textObject;
}
}
{
@@ -1310,11 +1336,11 @@ void tst_qquicktext::color()
QString componentStr = "import QtQuick 2.0\nText { color: \"" + colorStr + "\"; text: \"Hello World\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->color(), testColor);
-
- delete textObject;
} {
QString colorStr = "#001234";
QColor testColor(colorStr);
@@ -1324,6 +1350,7 @@ void tst_qquicktext::color()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QSignalSpy spy(textObject, SIGNAL(colorChanged()));
@@ -1345,6 +1372,7 @@ void tst_qquicktext::color()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QSignalSpy spy(textObject, SIGNAL(styleColorChanged()));
@@ -1366,6 +1394,7 @@ void tst_qquicktext::color()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QSignalSpy spy(textObject, SIGNAL(linkColorChanged()));
@@ -1389,19 +1418,19 @@ void tst_qquicktext::smooth()
QString componentStr = "import QtQuick 2.0\nText { smooth: false; text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->smooth(), false);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"" + standard.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->smooth(), true);
-
- delete textObject;
}
}
for (int i = 0; i < richText.size(); i++)
@@ -1410,19 +1439,19 @@ void tst_qquicktext::smooth()
QString componentStr = "import QtQuick 2.0\nText { smooth: false; text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->smooth(), false);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"" + richText.at(i) + "\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject != nullptr, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->smooth(), true);
-
- delete textObject;
}
}
}
@@ -1488,23 +1517,21 @@ void tst_qquicktext::weight()
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().weight(), int(QQuickFontEnums::Normal));
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { font.weight: Font.Bold; text: \"Hello world!\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().weight(), int(QQuickFontEnums::Bold));
-
- delete textObject;
}
}
@@ -1553,56 +1580,51 @@ void tst_qquicktext::capitalization()
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().capitalization(), int(QQuickFontEnums::MixedCase));
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllUppercase\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().capitalization(), int(QQuickFontEnums::AllUppercase));
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"AllLowercase\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().capitalization(), int(QQuickFontEnums::AllLowercase));
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"SmallCaps\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().capitalization(), int(QQuickFontEnums::SmallCaps));
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.capitalization: \"Capitalize\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().capitalization(), int(QQuickFontEnums::Capitalize));
-
- delete textObject;
}
}
@@ -1612,34 +1634,31 @@ void tst_qquicktext::letterSpacing()
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->font().letterSpacing(), 0.0);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: -2 }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->font().letterSpacing(), -2.);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.letterSpacing: 3 }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->font().letterSpacing(), 3.);
-
- delete textObject;
}
}
@@ -1649,34 +1668,31 @@ void tst_qquicktext::wordSpacing()
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->font().wordSpacing(), 0.0);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: -50 }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->font().wordSpacing(), -50.);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.wordSpacing: 200 }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->font().wordSpacing(), 200.);
-
- delete textObject;
}
}
@@ -1803,10 +1819,6 @@ public:
QTextLayout layout;
};
-
-typedef QVector<QPointF> PointVector;
-Q_DECLARE_METATYPE(PointVector);
-
void tst_qquicktext::linkInteraction_data()
{
QTest::addColumn<QString>("text");
@@ -2068,7 +2080,8 @@ void tst_qquicktext::linkInteraction()
"}";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
@@ -2119,8 +2132,6 @@ void tst_qquicktext::linkInteraction()
QCOMPARE(test.hoveredLink, QString());
QCOMPARE(textObject->hoveredLink(), QString());
QCOMPARE(textObject->linkAt(-1, -1), QString());
-
- delete textObject;
}
void tst_qquicktext::baseUrl()
@@ -2131,6 +2142,7 @@ void tst_qquicktext::baseUrl()
QQmlComponent textComponent(&engine);
textComponent.setData("import QtQuick 2.0\n Text {}", localUrl);
QQuickText *textObject = qobject_cast<QQuickText *>(textComponent.create());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->baseUrl(), localUrl);
@@ -2151,12 +2163,15 @@ void tst_qquicktext::baseUrl()
void tst_qquicktext::embeddedImages_data()
{
+ // Cancel some mess left by clipRectOutsideViewportDynamicallyChanged():
+ qmlClearTypeRegistrations();
+
QTest::addColumn<QUrl>("qmlfile");
QTest::addColumn<QString>("error");
QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
<< testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML Text: Cannot open: " + testFileUrl("http/notexists.png").toString();
- QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
+ QTest::newRow("local-relative") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
<< testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error transferring {{ServerBaseUrl}}/notexists.png - server replied: Not found";
@@ -2178,13 +2193,10 @@ void tst_qquicktext::embeddedImages()
if (!error.isEmpty())
QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
- QQuickView *view = new QQuickView;
- view->rootContext()->setContextProperty(QStringLiteral("serverBaseUrl"), server.baseUrl());
- view->setSource(qmlfile);
- view->show();
- view->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(view));
- QQuickText *textObject = qobject_cast<QQuickText*>(view->rootObject());
+ QQuickView view;
+ view.rootContext()->setContextProperty(QStringLiteral("serverBaseUrl"), server.baseUrl());
+ QVERIFY(QQuickTest::showView(view, qmlfile));
+ QQuickText *textObject = qobject_cast<QQuickText*>(view.rootObject());
QVERIFY(textObject != nullptr);
QTRY_COMPARE(textObject->resourcesLoading(), 0);
@@ -2199,7 +2211,9 @@ void tst_qquicktext::embeddedImages()
QCOMPARE(textObject->height(), 16.0);
}
- delete view;
+ // QTextDocument images are cached in QTextDocumentPrivate::cachedResources,
+ // so verify that we don't redundantly cache them in QQuickPixmapCache
+ QCOMPARE(QQuickPixmapCache::instance()->m_cache.size(), 0);
}
void tst_qquicktext::lineCount()
@@ -2357,7 +2371,9 @@ void tst_qquicktext::implicitSize()
"maximumLineCount: 2 }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QVERIFY(textObject->width() < textObject->implicitWidth());
QCOMPARE(textObject->height(), textObject->implicitHeight());
@@ -2366,8 +2382,6 @@ void tst_qquicktext::implicitSize()
textObject->resetWidth();
QCOMPARE(textObject->width(), textObject->implicitWidth());
QCOMPARE(textObject->height(), textObject->implicitHeight());
-
- delete textObject;
}
void tst_qquicktext::implicitSizeMaxLineCount()
@@ -2447,6 +2461,7 @@ void tst_qquicktext::contentSize()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QSignalSpy spySize(textObject, SIGNAL(contentSizeChanged()));
QSignalSpy spyWidth(textObject, SIGNAL(contentWidthChanged(qreal)));
@@ -2504,6 +2519,7 @@ void tst_qquicktext::geometryChanged()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
const qreal implicitHeight = textObject->implicitHeight();
@@ -2763,6 +2779,7 @@ void tst_qquicktext::implicitSizeBinding()
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
QScopedPointer<QObject> object(textComponent.create());
QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
+ QVERIFY2(textObject, qPrintable(textComponent.errorString()));
QCOMPARE(textObject->width(), textObject->implicitWidth());
QCOMPARE(textObject->height(), textObject->implicitHeight());
@@ -2951,10 +2968,10 @@ void tst_qquicktext::largeTextObservesViewport_data()
Q_ASSERT(text.size() > QQuickTextPrivate::largeTextSizeThreshold);
// by default, the root item acts as the viewport:
- // QQuickTextNode doesn't populate lines of text beyond the bottom of the window
+ // QSGInternalTextNode doesn't populate lines of text beyond the bottom of the window
QTest::newRow("default plain text") << text << QQuickText::PlainText << 0 << false;
// make the rectangle into a viewport item, and move the text upwards:
- // QQuickTextNode doesn't populate lines of text beyond the bottom of the viewport rectangle
+ // QSGInternalTextNode doesn't populate lines of text beyond the bottom of the viewport rectangle
QTest::newRow("clipped plain text") << text << QQuickText::PlainText << 10 << true;
{
@@ -2998,13 +3015,13 @@ void tst_qquicktext::largeTextObservesViewport()
QVERIFY(textItem);
QQuickItem *viewportItem = textItem->parentItem();
QQuickTextPrivate *textPriv = QQuickTextPrivate::get(textItem);
- QQuickTextNode *node = static_cast<QQuickTextNode *>(textPriv->paintNode);
+ QSGInternalTextNode *node = static_cast<QSGInternalTextNode *>(textPriv->paintNode);
QFontMetricsF fm(textItem->font());
const qreal expectedTextHeight = (parentIsViewport ? viewportItem->height() : window.height() - viewportItem->y());
const qreal lineSpacing = qCeil(fm.height());
// A paragraph break is the same as an extra line break; so since our "lines" are paragraphs in StyledText,
// visually, with StyledText we skip down 10 "lines", but the first paragraph you see says "line 5".
- // It's OK anyway for the test, because QQuickTextNode::addTextLayout() treats the paragraph breaks like lines of text.
+ // It's OK anyway for the test, because QSGTextNode::addTextLayout() treats the paragraph breaks like lines of text.
const int expectedLastLine = linesAboveViewport + int(expectedTextHeight / lineSpacing);
viewportItem->setFlag(QQuickItem::ItemIsViewport, parentIsViewport);
@@ -3029,6 +3046,29 @@ void tst_qquicktext::largeTextObservesViewport()
QVERIFY(qAbs(renderedLineRange.second - (expectedLastLine + 1)) < (linesAboveViewport > 80 ? 4 : 2));
}
+void tst_qquicktext::largeTextInDelayedLoader() // QTBUG-115687
+{
+ QQuickView view;
+ QVERIFY(QQuickTest::showView(view, testFileUrl("loaderActiveOnVisible.qml")));
+ auto flick = view.rootObject()->findChild<QQuickFlickable*>();
+ QVERIFY(flick);
+ auto textItem = view.rootObject()->findChild<QQuickText*>();
+ QVERIFY(textItem);
+ QQuickTextPrivate *textPriv = QQuickTextPrivate::get(textItem);
+ QSGInternalTextNode *node = static_cast<QSGInternalTextNode *>(textPriv->paintNode);
+ const auto initialLineRange = node->renderedLineRange();
+ qCDebug(lcTests) << "first line rendered" << initialLineRange.first
+ << "; first line past viewport" << initialLineRange.second;
+ flick->setContentY(500);
+ QTRY_COMPARE_NE(node->renderedLineRange(), initialLineRange);
+ const auto scrolledLineRange = node->renderedLineRange();
+ qCDebug(lcTests) << "after scroll: first line rendered" << scrolledLineRange.first
+ << "; first line past viewport" << scrolledLineRange.second;
+ // We scrolled a good bit more than one window-height, so we must render a
+ // non-overlapping range of text some distance past the initial blocks.
+ QCOMPARE_GT(scrolledLineRange.first, initialLineRange.second);
+}
+
void tst_qquicktext::lineLaidOut()
{
QScopedPointer<QQuickView> window(createView(testFile("lineLayout.qml")));
@@ -3043,11 +3083,11 @@ void tst_qquicktext::lineLaidOut()
for (int i = 0; i < textPrivate->layout.lineCount(); ++i) {
QRectF r = textPrivate->layout.lineAt(i).rect();
- QVERIFY(r.width() == i * 15);
+ QCOMPARE(r.width(), i * 15);
if (i >= 30)
- QVERIFY(r.x() == r.width() + 30);
+ QCOMPARE(r.x(), r.width() + 30);
if (i >= 60) {
- QVERIFY(r.x() == r.width() * 2 + 60);
+ QCOMPARE(r.x(), r.width() * 2 + 60);
QCOMPARE(r.height(), qreal(20));
}
}
@@ -3092,6 +3132,29 @@ void tst_qquicktext::lineLaidOutRelayout()
}
}
+void tst_qquicktext::lineLaidOutFontUpdate()
+{
+ QScopedPointer<QQuickView> window(createView(testFile("lineLayoutFontUpdate.qml")));
+
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+
+ auto *myText = window->rootObject()->findChild<QQuickText*>("exampleText");
+ QVERIFY(myText != nullptr);
+
+ QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
+ QVERIFY(textPrivate != nullptr);
+
+ QCOMPARE(textPrivate->layout.lineCount(), 2);
+
+ QTextLine firstLine = textPrivate->layout.lineAt(0);
+ QTextLine secondLine = textPrivate->layout.lineAt(1);
+
+ QCOMPARE(firstLine.rect().x(), secondLine.rect().x() + 40);
+ QCOMPARE(firstLine.rect().width(), secondLine.rect().width() - 40);
+}
+
void tst_qquicktext::lineLaidOutHAlign()
{
QScopedPointer<QQuickView> window(createView(testFile("lineLayoutHAlign.qml")));
@@ -3272,7 +3335,8 @@ void tst_qquicktext::imgTagsAlign()
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(), testFileUrl("."));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->height(), qreal(imgHeight));
@@ -3287,8 +3351,6 @@ void tst_qquicktext::imgTagsAlign()
QVERIFY(br.y() == imgHeight / 2.0 - br.height() / 2.0);
else if (align == "top")
QCOMPARE(br.y(), qreal(0));
-
- delete textObject;
}
void tst_qquicktext::imgTagsMultipleImages()
@@ -3297,7 +3359,8 @@ void tst_qquicktext::imgTagsMultipleImages()
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), testFileUrl("."));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE(textObject->height(), qreal(85));
@@ -3305,34 +3368,30 @@ void tst_qquicktext::imgTagsMultipleImages()
QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
QVERIFY(textPrivate != nullptr);
QCOMPARE(textPrivate->extra->visibleImgTags.size(), 2);
-
- delete textObject;
}
void tst_qquicktext::imgTagsElide()
{
QScopedPointer<QQuickView> window(createView(testFile("imgTagsElide.qml")));
- QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
- QVERIFY(myText != nullptr);
+ QScopedPointer<QQuickText> myText(window->rootObject()->findChild<QQuickText*>("myText"));
+ QVERIFY(myText);
- QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
+ QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText.data());
QVERIFY(textPrivate != nullptr);
QCOMPARE(textPrivate->extra->visibleImgTags.size(), 0);
myText->setMaximumLineCount(20);
QTRY_COMPARE(textPrivate->extra->visibleImgTags.size(), 1);
-
- delete myText;
}
void tst_qquicktext::imgTagsUpdates()
{
QScopedPointer<QQuickView> window(createView(testFile("imgTagsUpdates.qml")));
- QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
- QVERIFY(myText != nullptr);
+ QScopedPointer<QQuickText> myText(window->rootObject()->findChild<QQuickText*>("myText"));
+ QVERIFY(myText);
- QSignalSpy spy(myText, SIGNAL(contentSizeChanged()));
+ QSignalSpy spy(myText.data(), SIGNAL(contentSizeChanged()));
- QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
+ QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText.data());
QVERIFY(textPrivate != nullptr);
myText->setText("This is a heart<img src=\"images/heart200.png\">.");
@@ -3346,8 +3405,6 @@ void tst_qquicktext::imgTagsUpdates()
// if maximumLineCount is set and the img tag doesn't have an explicit size
// we relayout twice.
QCOMPARE(spy.size(), 3);
-
- delete myText;
}
void tst_qquicktext::imgTagsError()
@@ -3361,10 +3418,10 @@ void tst_qquicktext::imgTagsError()
+ testFileUrl("images/starfish_2.pn").toString());
QTest::ignoreMessage(QtWarningMsg, expectedMessage.toLatin1());
textComponent.setData(componentStr.toLatin1(), testFileUrl("."));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
- delete textObject;
}
void tst_qquicktext::fontSizeMode_data()
@@ -4027,9 +4084,6 @@ void tst_qquicktext::fontFormatSizes()
}
}
-typedef qreal (*ExpectedBaseline)(QQuickText *item);
-Q_DECLARE_METATYPE(ExpectedBaseline)
-
static qreal expectedBaselineTop(QQuickText *item)
{
QFontMetricsF fm(item->font());
@@ -4194,7 +4248,7 @@ void tst_qquicktext::baselineOffset_data()
QTest::newRow("customLine")
<< "hello world"
<< "hello\nworld"
- << QByteArray("height: 200; onLineLaidOut: line.y += 16")
+ << QByteArray("height: 200; onLineLaidOut: (line) => { line.y += 16; }")
<< &expectedBaselineCustom
<< &expectedBaselineCustom;
@@ -4290,7 +4344,8 @@ void tst_qquicktext::baselineOffset_data()
QTest::newRow("customLine with padding")
<< "hello world"
<< "hello\nworld"
- << QByteArray("height: 200; topPadding: 10; bottomPadding: 20; onLineLaidOut: line.y += 16")
+ << QByteArray("height: 200; topPadding: 10; bottomPadding: 20; "
+ "onLineLaidOut: (line) => { line.y += 16; }")
<< &expectedBaselineCustom
<< &expectedBaselineCustom;
@@ -4388,7 +4443,7 @@ void tst_qquicktext::htmlLists()
QFETCH(QString, text);
QFETCH(int, nbLines);
- QQuickView *view = createView(testFile("htmlLists.qml"));
+ QScopedPointer<QQuickView>view(createView(testFile("htmlLists.qml")));
QQuickText *textObject = view->rootObject()->findChild<QQuickText*>("myText");
QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
@@ -4400,11 +4455,9 @@ void tst_qquicktext::htmlLists()
view->show();
view->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(view));
+ QVERIFY(QTest::qWaitForWindowActive(view.get()));
QCOMPARE(textPrivate->extra->doc->lineCount(), nbLines);
-
- delete view;
}
void tst_qquicktext::htmlLists_data()
@@ -4489,9 +4542,9 @@ void tst_qquicktext::padding()
QTRY_COMPARE(window->status(), QQuickView::Ready);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
- QQuickItem *root = window->rootObject();
+ QScopedPointer<QQuickItem> root(window->rootObject());
QVERIFY(root);
- QQuickText *obj = qobject_cast<QQuickText*>(root);
+ QQuickText *obj = qobject_cast<QQuickText*>(root.data());
QVERIFY(obj != nullptr);
qreal cw = obj->contentWidth();
@@ -4582,8 +4635,6 @@ void tst_qquicktext::padding()
QCOMPARE(obj->leftPadding(), 0.0);
QCOMPARE(obj->rightPadding(), 0.0);
QCOMPARE(obj->bottomPadding(), 0.0);
-
- delete root;
}
void tst_qquicktext::paddingInLoader() // QTBUG-83413
@@ -4618,23 +4669,21 @@ void tst_qquicktext::hintingPreference()
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().hintingPreference(), (int)QFont::PreferDefaultHinting);
-
- delete textObject;
}
{
QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.hintingPreference: Font.PreferNoHinting }";
QQmlComponent textComponent(&engine);
textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
- QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+ QScopedPointer<QObject> object(textComponent.create());
+ QQuickText *textObject = qobject_cast<QQuickText*>(object.data());
QVERIFY(textObject != nullptr);
QCOMPARE((int)textObject->font().hintingPreference(), (int)QFont::PreferNoHinting);
-
- delete textObject;
}
}
@@ -4710,6 +4759,7 @@ void tst_qquicktext::fontInfo()
QScopedPointer<QObject> object(component.create());
QObject *root = object.data();
+ QVERIFY2(root, qPrintable(component.errorString()));
QQuickText *main = root->findChild<QQuickText *>("main");
QVERIFY(main);
@@ -4806,6 +4856,8 @@ void tst_qquicktext::displaySuperscriptedTag()
QCOMPARE(color.green(), 255);
}
+QT_END_NAMESPACE
+
QTEST_MAIN(tst_qquicktext)
#include "tst_qquicktext.moc"
diff --git a/tests/auto/quick/qquicktextdocument/CMakeLists.txt b/tests/auto/quick/qquicktextdocument/CMakeLists.txt
index e106efd6c2..5bcad96b0a 100644
--- a/tests/auto/quick/qquicktextdocument/CMakeLists.txt
+++ b/tests/auto/quick/qquicktextdocument/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktextdocument Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktextdocument LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicktextdocument/data/hello-8857-7.html b/tests/auto/quick/qquicktextdocument/data/hello-8857-7.html
new file mode 100644
index 0000000000..6bf900858a
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello-8857-7.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="iso-8859-7">
+</head>
+<body>
+<p>ÃåéÜ óïõ Êüóìå!</p>
+</body>
+</html>
diff --git a/tests/auto/quick/qquicktextdocument/data/hello-utf16be.html b/tests/auto/quick/qquicktextdocument/data/hello-utf16be.html
new file mode 100644
index 0000000000..be84e0d63c
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello-utf16be.html
Binary files differ
diff --git a/tests/auto/quick/qquicktextdocument/data/hello.html b/tests/auto/quick/qquicktextdocument/data/hello.html
new file mode 100644
index 0000000000..c160cd5e63
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello.html
@@ -0,0 +1 @@
+Γειά σου <i>Κόσμε</i>!
diff --git a/tests/auto/quick/qquicktextdocument/data/hello.md b/tests/auto/quick/qquicktextdocument/data/hello.md
new file mode 100644
index 0000000000..290f3f1397
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello.md
@@ -0,0 +1 @@
+Γειά σου *Κόσμε*!
diff --git a/tests/auto/quick/qquicktextdocument/data/hello.txt b/tests/auto/quick/qquicktextdocument/data/hello.txt
new file mode 100644
index 0000000000..1f72c3f7a4
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello.txt
@@ -0,0 +1 @@
+Γειά σου Κόσμε!
diff --git a/tests/auto/quick/qquicktextdocument/data/sideBySideIndependent.qml b/tests/auto/quick/qquicktextdocument/data/sideBySideIndependent.qml
new file mode 100644
index 0000000000..8275e7f5bd
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/sideBySideIndependent.qml
@@ -0,0 +1,21 @@
+import QtQuick
+
+Row {
+ width: 480; height: 200
+ TextEdit {
+ objectName: "plain"
+ width: parent.width / 2 - 1
+ textFormat: TextEdit.PlainText
+ textDocument.source: "hello.md"
+ }
+ Rectangle {
+ width: 2; height: parent.height
+ color: "lightsteelblue"
+ }
+ TextEdit {
+ objectName: "markdown"
+ width: parent.width / 2 - 1
+ textFormat: TextEdit.MarkdownText
+ textDocument.source: "hello.md"
+ }
+}
diff --git a/tests/auto/quick/qquicktextdocument/data/sideBySideIndependentReverse.qml b/tests/auto/quick/qquicktextdocument/data/sideBySideIndependentReverse.qml
new file mode 100644
index 0000000000..cbd92accec
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/sideBySideIndependentReverse.qml
@@ -0,0 +1,21 @@
+import QtQuick
+
+Row {
+ width: 480; height: 200
+ TextEdit {
+ objectName: "plain"
+ width: parent.width / 2 - 1
+ textDocument.source: "hello.md"
+ textFormat: TextEdit.PlainText
+ }
+ Rectangle {
+ width: 2; height: parent.height
+ color: "lightsteelblue"
+ }
+ TextEdit {
+ objectName: "markdown"
+ width: parent.width / 2 - 1
+ textDocument.source: "hello.md"
+ textFormat: TextEdit.MarkdownText
+ }
+}
diff --git a/tests/auto/quick/qquicktextdocument/data/text.qml b/tests/auto/quick/qquicktextdocument/data/text.qml
index 43a8c6bb82..9544471114 100644
--- a/tests/auto/quick/qquicktextdocument/data/text.qml
+++ b/tests/auto/quick/qquicktextdocument/data/text.qml
@@ -1,6 +1,14 @@
-import QtQuick 2.1
+import QtQuick
TextEdit {
- text: ""
-}
+ id: te
+ property int sourceChangeCount: 0
+ property int modifiedChangeCount: 0
+ property var statusHistory: []
+
+ text: "" // this is not a document modification
+ textDocument.onSourceChanged: ++te.sourceChangeCount
+ textDocument.onModifiedChanged: ++te.modifiedChangeCount
+ textDocument.onStatusChanged: te.statusHistory.push(textDocument.status)
+}
diff --git a/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp b/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
index 63d4f24a54..535f20e7bb 100644
--- a/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
+++ b/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
@@ -1,17 +1,30 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QtTest>
-#include <QtQuick/QQuickTextDocument>
#include <QtQuick/QQuickItem>
-#include <QtQuick/private/qquicktextedit_p.h>
+#include <QtQuick/QQuickTextDocument>
+#include <QtQuick/QQuickView>
#include <QtQuick/private/qquicktextdocument_p.h>
+#include <QtQuick/private/qquicktextedit_p.h>
+#include <QtQuick/private/qquicktextedit_p_p.h>
+#include <QtCore/QStringConverter>
+#include <QtGui/QTextBlock>
#include <QtGui/QTextDocument>
#include <QtGui/QTextDocumentWriter>
-#include <QtQml/QQmlEngine>
#include <QtQml/QQmlComponent>
+#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlFile>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
+#ifdef Q_OS_UNIX
+#include <unistd.h>
+#endif
+
+Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+
+using namespace Qt::StringLiterals;
class tst_qquicktextdocument : public QQmlDataTest
{
@@ -19,18 +32,91 @@ class tst_qquicktextdocument : public QQmlDataTest
public:
tst_qquicktextdocument();
+private:
+ QPair<int, int> fragmentsAndItalics(const QTextDocument *doc);
+ bool isMainFontFixed();
+
private slots:
void textDocumentWriter();
- void textDocumentWithImage();
+ void customDocument();
+ void sourceAndSave_data();
+ void sourceAndSave();
+ void loadErrorNoSuchFile();
+ void loadErrorPermissionDenied();
+ void overrideTextFormat_data();
+ void overrideTextFormat();
+ void independentDocumentsSameSource_data();
+ void independentDocumentsSameSource();
};
QString text = QStringLiteral("foo bar");
+// similar to TestDocument in tst_qtextdocument.cpp
+class FakeImageDocument : public QTextDocument
+{
+public:
+ inline FakeImageDocument(const QUrl &testUrl, const QString &testString)
+ : url(testUrl), string(testString), resourceLoaded(false) {}
+
+ bool hasResourceCached();
+
+protected:
+ virtual QVariant loadResource(int type, const QUrl &name) override;
+
+private:
+ QUrl url;
+ QString string;
+ bool resourceLoaded;
+};
+
+bool FakeImageDocument::hasResourceCached()
+{
+ return resourceLoaded;
+}
+
+QVariant FakeImageDocument::loadResource(int type, const QUrl &name)
+{
+ qCDebug(lcTests) << type << name << ": expecting" << int(QTextDocument::ImageResource) << url;
+ if (type == QTextDocument::ImageResource && name == url) {
+ resourceLoaded = true;
+ return string;
+ }
+ return QTextDocument::loadResource(type, name);
+}
+
tst_qquicktextdocument::tst_qquicktextdocument()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
}
+/*! \internal
+ Returns {fragmentCount, italicFragmentIndex}. If no italic fragment is found,
+ italicFragmentIndex is -1.
+*/
+QPair<int, int> tst_qquicktextdocument::fragmentsAndItalics(const QTextDocument *doc)
+{
+ int fragmentCount = 0;
+ int italicFragment = -1;
+ for (QTextBlock::iterator it = doc->firstBlock().begin(); !(it.atEnd()); ++it) {
+ QTextFragment currentFragment = it.fragment();
+ if (currentFragment.charFormat().fontItalic())
+ italicFragment = fragmentCount;
+ ++fragmentCount;
+ qCDebug(lcTests) << (currentFragment.charFormat().fontItalic() ? "italic" : "roman") << currentFragment.text();
+ }
+ return {fragmentCount, italicFragment};
+}
+
+bool tst_qquicktextdocument::isMainFontFixed()
+{
+ bool ret = QFontInfo(QGuiApplication::font()).fixedPitch();
+ if (ret) {
+ qCWarning(lcTests) << "QFontDatabase::GeneralFont is monospaced: markdown writing is likely to use too many backticks"
+ << QFontDatabase::systemFont(QFontDatabase::GeneralFont);
+ }
+ return ret;
+}
+
void tst_qquicktextdocument::textDocumentWriter()
{
QQmlEngine e;
@@ -54,18 +140,427 @@ void tst_qquicktextdocument::textDocumentWriter()
delete o;
}
-void tst_qquicktextdocument::textDocumentWithImage()
+/*! \internal
+ Verify that it's OK to replace the default QTextDocument that TextEdit creates
+ with a user-created QTextDocument.
+
+ Also verify that the user can still override QTextDocument::loadResource().
+ QTextDocument::loadResource() can call its QObject parent's loadResource();
+ the default QTextDocument's parent is the QQuickTextEdit, which provides an
+ implementation of QQuickTextEdit::loadResource(), which uses QQuickPixmap
+ to load and cache images. This will be bypassed if the user overrides
+ loadResource() to do something different.
+*/
+void tst_qquicktextdocument::customDocument()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("text.qml"));
+ QScopedPointer<QQuickTextEdit> textEdit(qobject_cast<QQuickTextEdit*>(c.create()));
+ QCOMPARE(textEdit.isNull(), false);
+ auto *textEditPriv = QQuickTextEditPrivate::get(textEdit.get());
+ QVERIFY(textEditPriv->ownsDocument);
+
+ QQuickTextDocument *quickDocument = textEdit->property("textDocument").value<QQuickTextDocument*>();
+ QVERIFY(quickDocument);
+ QPointer<QTextDocument> defaultDocument(quickDocument->textDocument());
+ const QString imageUrl = "https://www.qt.io/hubfs/Qt-logo-neon-small.png";
+ const QString fakeImageData = "foo!";
+
+ FakeImageDocument fdoc(QUrl(imageUrl), fakeImageData);
+ quickDocument->setTextDocument(&fdoc);
+ QVERIFY(defaultDocument.isNull()); // deleted because of being replaced (don't leak)
+ QCOMPARE(textEditPriv->ownsDocument, false);
+
+ // QQuickTextEdit::setText() -> QQuickTextControl::setHtml() ->
+ // QQuickTextControlPrivate::setContent() -> fdoc->setHtml()
+ // and eventually fdoc->loadResource() which substitutes a string for the requested image
+ textEdit->setTextFormat(QQuickTextEdit::RichText);
+ textEdit->setText("an image: <img src='" + imageUrl + "'/>");
+ QCOMPARE(fdoc.hasResourceCached(), true);
+
+ auto firstBlock = fdoc.firstBlock();
+ // check that image loading has been bypassed by FakeImageDocument
+ bool foundImage = false;
+ int fragmentCount = 0;
+ for (QTextBlock::iterator it = firstBlock.begin(); !(it.atEnd()); ++it) {
+ QTextFragment currentFragment = it.fragment();
+ QVERIFY(currentFragment.isValid());
+ ++fragmentCount;
+ const QString imageName = currentFragment.charFormat().stringProperty(QTextFormat::ImageName);
+ if (!imageName.isEmpty()) {
+ QCOMPARE(imageName, imageUrl);
+ foundImage = true;
+ QCOMPARE(fdoc.resource(QTextDocument::ImageResource, imageUrl).toString(), fakeImageData);
+ }
+ }
+ QVERIFY(foundImage);
+ QCOMPARE(fragmentCount, 2);
+}
+
+void tst_qquicktextdocument::sourceAndSave_data()
+{
+ QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<std::optional<QStringConverter::Encoding>>("expectedEncoding");
+ QTest::addColumn<Qt::TextFormat>("expectedTextFormat");
+ QTest::addColumn<int>("minCharCount");
+ QTest::addColumn<QString>("expectedPlainText");
+
+ const std::optional<QStringConverter::Encoding> nullEnc;
+
+ QTest::newRow("plain") << QQuickTextEdit::PlainText << "hello.txt"
+ << nullEnc << Qt::PlainText << 15 << u"Γειά σου Κόσμε!"_s;
+ QTest::newRow("markdown") << QQuickTextEdit::MarkdownText << "hello.md"
+ << nullEnc << Qt::MarkdownText << 15 << u"Γειά σου Κόσμε!"_s;
+ QTest::newRow("html") << QQuickTextEdit::RichText << "hello.html"
+ << std::optional<QStringConverter::Encoding>(QStringConverter::Utf8)
+ << Qt::RichText << 15 << u"Γειά σου Κόσμε!"_s;
+ QTest::newRow("html-utf16be") << QQuickTextEdit::AutoText << "hello-utf16be.html"
+ << std::optional<QStringConverter::Encoding>(QStringConverter::Utf16BE)
+ << Qt::RichText << 15 << u"Γειά σου Κόσμε!"_s;
+}
+
+void tst_qquicktextdocument::sourceAndSave()
+{
+ QFETCH(QQuickTextEdit::TextFormat, textFormat);
+ QFETCH(QString, source);
+ QFETCH(std::optional<QStringConverter::Encoding>, expectedEncoding);
+ QFETCH(Qt::TextFormat, expectedTextFormat);
+ QFETCH(int, minCharCount);
+ QFETCH(QString, expectedPlainText);
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("text.qml"));
+ QScopedPointer<QQuickTextEdit> textEdit(qobject_cast<QQuickTextEdit*>(c.create()));
+ QCOMPARE(textEdit.isNull(), false);
+ QQuickTextDocument *qqdoc = textEdit->property("textDocument").value<QQuickTextDocument*>();
+ QVERIFY(qqdoc);
+ const QQmlContext *ctxt = e.rootContext();
+ // text.qml has text: "" but that's not a real change; QQuickTextEdit::setText() returns early
+ // QQuickTextEditPrivate::init() also modifies defaults and then resets the modified state
+ QCOMPARE(qqdoc->isModified(), false);
+ // no stray signals should be visible to QML during init
+ QCOMPARE(textEdit->property("modifiedChangeCount").toInt(), 0);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 0);
+ QTextDocument *doc = qqdoc->textDocument();
+ QVERIFY(doc);
+ QSignalSpy sourceChangedSpy(qqdoc, &QQuickTextDocument::sourceChanged);
+ QSignalSpy modifiedChangedSpy(qqdoc, &QQuickTextDocument::modifiedChanged);
+ QSignalSpy statusChangedSpy(qqdoc, &QQuickTextDocument::statusChanged);
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ QFile sf(QQmlFile::urlToLocalFileOrQrc(ctxt->resolvedUrl(testFileUrl(source))));
+ qCDebug(lcTests) << source << "orig ->" << sf.fileName();
+ QVERIFY(sf.exists());
+ QString tmpPath = tmpDir.filePath(source);
+ QVERIFY(sf.copy(tmpPath));
+ qCDebug(lcTests) << source << "copy ->" << tmpDir.path() << ":" << tmpPath;
+
+ QCOMPARE(statusChangedSpy.size(), 0);
+ QCOMPARE(qqdoc->status(), QQuickTextDocument::Status::Null);
+ textEdit->setTextFormat(textFormat);
+ qqdoc->setProperty("source", QUrl::fromLocalFile(tmpPath));
+ QCOMPARE(sourceChangedSpy.size(), 1);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 1);
+ QCOMPARE(statusChangedSpy.size(), 2); // Loading, then Loaded
+ QCOMPARE(qqdoc->status(), QQuickTextDocument::Status::Loaded);
+ QVERIFY(qqdoc->errorString().isEmpty());
+ const auto *qqdp = QQuickTextDocumentPrivate::get(qqdoc);
+ QCOMPARE(qqdp->detectedFormat, expectedTextFormat);
+ QCOMPARE_GE(doc->characterCount(), minCharCount);
+ QCOMPARE(doc->toPlainText().trimmed(), expectedPlainText);
+ QCOMPARE(qqdp->encoding, expectedEncoding);
+
+ textEdit->setText("hello");
+ QCOMPARE(qqdoc->isModified(), false);
+ QCOMPARE_GE(textEdit->property("modifiedChangeCount").toInt(), 1);
+ QCOMPARE(textEdit->property("modifiedChangeCount").toInt(), modifiedChangedSpy.size());
+ modifiedChangedSpy.clear();
+ textEdit->insert(5, "!");
+ QCOMPARE_GE(modifiedChangedSpy.size(), 1);
+ QCOMPARE(qqdoc->isModified(), true);
+
+ qqdoc->save();
+ QCOMPARE(statusChangedSpy.size(), 4); // Saving, then Saved
+ QCOMPARE(qqdoc->status(), QQuickTextDocument::Status::Saved);
+ QVERIFY(qqdoc->errorString().isEmpty());
+ QFile tf(tmpPath);
+ QVERIFY(tf.open(QIODeviceBase::ReadOnly));
+ auto readBack = tf.readAll();
+ if (expectedTextFormat == Qt::RichText) {
+ QStringDecoder dec(*expectedEncoding);
+ const QString decStr = dec(readBack);
+ QVERIFY(decStr.contains("hello!</p>"));
+ } else {
+ QVERIFY(readBack.contains("hello!"));
+ }
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), sourceChangedSpy.size());
+}
+
+void tst_qquicktextdocument::loadErrorNoSuchFile()
{
- QQuickTextDocumentWithImageResources document(nullptr);
- QImage image(1, 1, QImage::Format_Mono);
- image.fill(1);
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("text.qml"));
+ QScopedPointer<QQuickTextEdit> textEdit(qobject_cast<QQuickTextEdit*>(c.create()));
+ QCOMPARE(textEdit.isNull(), false);
+ QQuickTextDocument *qqdoc = textEdit->property("textDocument").value<QQuickTextDocument*>();
+ QVERIFY(qqdoc);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 0);
+ QTextDocument *doc = qqdoc->textDocument();
+ QVERIFY(doc);
+ QSignalSpy sourceChangedSpy(qqdoc, &QQuickTextDocument::sourceChanged);
+ QSignalSpy statusChangedSpy(qqdoc, &QQuickTextDocument::statusChanged);
+
+ QCOMPARE(statusChangedSpy.size(), 0);
+ QCOMPARE(qqdoc->status(), QQuickTextDocument::Status::Null);
+ const QRegularExpression err(".*does not exist");
+ QTest::ignoreMessage(QtWarningMsg, err);
+ qqdoc->setProperty("source", testFileUrl("nosuchfile.md"));
+ QCOMPARE(sourceChangedSpy.size(), 1);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 1);
+ qCDebug(lcTests) << "status history" << textEdit->property("statusHistory").toList();
+ QCOMPARE(statusChangedSpy.size(), 1);
+ QCOMPARE(qqdoc->status(), QQuickTextDocument::Status::ReadError);
+ QVERIFY(qqdoc->errorString().contains(err));
+}
+
+void tst_qquicktextdocument::loadErrorPermissionDenied()
+{
+#ifdef Q_OS_UNIX
+ if (geteuid() == 0)
+ QSKIP("Permission will not be denied with root privileges.");
+#endif
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("text.qml"));
+ QScopedPointer<QQuickTextEdit> textEdit(qobject_cast<QQuickTextEdit*>(c.create()));
+ QCOMPARE(textEdit.isNull(), false);
+ QQuickTextDocument *qqdoc = textEdit->property("textDocument").value<QQuickTextDocument*>();
+ QVERIFY(qqdoc);
+ const QQmlContext *ctxt = e.rootContext();
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 0);
+ QTextDocument *doc = qqdoc->textDocument();
+ QVERIFY(doc);
+ QSignalSpy sourceChangedSpy(qqdoc, &QQuickTextDocument::sourceChanged);
+ QSignalSpy statusChangedSpy(qqdoc, &QQuickTextDocument::statusChanged);
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ const QString source("hello.md");
+ QFile sf(QQmlFile::urlToLocalFileOrQrc(ctxt->resolvedUrl(testFileUrl(source))));
+ qCDebug(lcTests) << source << "orig ->" << sf.fileName();
+ QVERIFY(sf.exists());
+ QString tmpPath = tmpDir.filePath(source);
+ QVERIFY(sf.copy(tmpPath));
+ qCDebug(lcTests) << source << "copy ->" << tmpDir.path() << ":" << tmpPath;
+ if (!QFile::setPermissions(tmpPath, QFileDevice::Permissions{})) // no permissions at all
+ QSKIP("Failed to change permissions of temporary file: cannot continue.");
+
+ QCOMPARE(statusChangedSpy.size(), 0);
+ QCOMPARE(qqdoc->status(), QQuickTextDocument::Status::Null);
+ const QRegularExpression err(".*Failed to read: Permission denied");
+ QTest::ignoreMessage(QtWarningMsg, err);
+ qqdoc->setProperty("source", QUrl::fromLocalFile(tmpPath));
+ QCOMPARE(sourceChangedSpy.size(), 1);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 1);
+ qCDebug(lcTests) << "status history" << textEdit->property("statusHistory").toList();
+ QCOMPARE(statusChangedSpy.size(), 1);
+ QCOMPARE(qqdoc->status(), QQuickTextDocument::Status::ReadError);
+ QVERIFY(qqdoc->errorString().contains(err));
+}
+
+void tst_qquicktextdocument::overrideTextFormat_data()
+{
+ QTest::addColumn<QUrl>("qmlfile");
+ QTest::addColumn<QQuickTextEdit::TextFormat>("initialFormat");
+ QTest::addColumn<QUrl>("source");
+ QTest::addColumn<int>("expectedInitialFragmentCount");
+ QTest::addColumn<int>("expectedInitialItalicFragment");
+ // first part of TextEdit.text after loading
+ QTest::addColumn<QString>("expectedTextPrefix");
+
+ QTest::addColumn<QQuickTextEdit::TextFormat>("replacementFormat");
+ QTest::addColumn<int>("expectedFragmentCount");
+ QTest::addColumn<int>("expectedItalicFragment");
+ // first part of TextEdit.text after switching to replacementFormat
+ QTest::addColumn<QString>("expectedReplacementPrefix");
+ QTest::addColumn<int>("expectedTextChangedSignalsAfterReplacement");
+
+ QTest::addColumn<QQuickTextEdit::TextFormat>("finalFormat");
+ QTest::addColumn<int>("expectedFinalFragmentCount");
+ QTest::addColumn<int>("expectedFinalItalicFragment");
+ // first part of TextEdit.text after switching to finalFormat
+ QTest::addColumn<QString>("expectedFinalPrefix");
+
+ QTest::newRow("load md, switch to plain, back to md")
+ << testFileUrl("text.qml") << QQuickTextEdit::MarkdownText << testFileUrl("hello.md")
+ << 3 << 1 << u"Γειά σου *Κόσμε*!"_s
+ << QQuickTextEdit::PlainText << 1 << -1 << u"Γειά σου *Κόσμε*!"_s << 2
+ << QQuickTextEdit::MarkdownText << 3 << 1 << u"Γειά σου *Κόσμε*!"_s;
+ QTest::newRow("load md, switch to plain, then auto")
+ << testFileUrl("text.qml") << QQuickTextEdit::MarkdownText << testFileUrl("hello.md")
+ << 3 << 1 << u"Γειά σου *Κόσμε*!"_s
+ << QQuickTextEdit::PlainText << 1 << -1 << u"Γειά σου *Κόσμε*!"_s << 2
+ << QQuickTextEdit::AutoText << 3 << 1 << u"Γειά σου *Κόσμε*!"_s;
+ QTest::newRow("load md, switch to html, then plain")
+ << testFileUrl("text.qml") << QQuickTextEdit::MarkdownText << testFileUrl("hello.md")
+ << 3 << 1 << u"Γειά σου *Κόσμε*!"_s
+ << QQuickTextEdit::RichText << 3 << 1 << u"<!DOCTYPE HTML"_s << 2
+ << QQuickTextEdit::PlainText << 1 << -1 << u"<!DOCTYPE HTML"_s;
+ QTest::newRow("load md as plain text, switch to md, back to plain")
+ << testFileUrl("text.qml") << QQuickTextEdit::PlainText << testFileUrl("hello.md")
+ << 1 << -1 << u"Γειά σου *Κόσμε*!"_s
+ << QQuickTextEdit::MarkdownText << 3 << 1 << u"Γειά σου *Κόσμε*!"_s << 2
+ << QQuickTextEdit::PlainText << 1 << -1 << u"Γειά σου *Κόσμε*!"_s;
+ QTest::newRow("load md as autotext, switch to plain, back to auto")
+ << testFileUrl("text.qml") << QQuickTextEdit::AutoText << testFileUrl("hello.md")
+ << 3 << 1 << u"Γειά σου *Κόσμε*!"_s
+ << QQuickTextEdit::PlainText << 1 << -1 << u"Γειά σου *Κόσμε*!"_s << 2
+ << QQuickTextEdit::AutoText << 3 << 1 << u"Γειά σου *Κόσμε*!"_s;
+ QTest::newRow("load md as autotext, switch to md, then plain")
+ << testFileUrl("text.qml") << QQuickTextEdit::AutoText << testFileUrl("hello.md")
+ << 3 << 1 << u"Γειά σου *Κόσμε*!"_s
+ << QQuickTextEdit::MarkdownText << 3 << 1 << u"Γειά σου *Κόσμε*!"_s
+ // going from AutoText to a matching explicit format does not cause extra textChanged()
+ << 1
+ << QQuickTextEdit::PlainText << 1 << -1 << u"Γειά σου *Κόσμε*!"_s;
+ QTest::newRow("load md as autotext, switch to html, then plain")
+ << testFileUrl("text.qml") << QQuickTextEdit::AutoText << testFileUrl("hello.md")
+ << 3 << 1 << u"Γειά σου *Κόσμε*!"_s
+ << QQuickTextEdit::RichText << 3 << 1 << u"<!DOCTYPE HTML"_s << 2
+ << QQuickTextEdit::PlainText << 1 << -1 << u"<!DOCTYPE HTML"_s;
+
+ QTest::newRow("load html, switch to plain, back to rich")
+ << testFileUrl("text.qml") << QQuickTextEdit::RichText << testFileUrl("hello.html")
+ << 3 << 1 << u"<!DOCTYPE HTML"_s
+ << QQuickTextEdit::PlainText << 1 << -1 << u"<!DOCTYPE HTML"_s << 2
+ << QQuickTextEdit::RichText << 3 << 1 << u"<!DOCTYPE HTML"_s;
+ QTest::newRow("load html as plain text, switch to html, back to plain")
+ << testFileUrl("text.qml") << QQuickTextEdit::PlainText << testFileUrl("hello.html")
+ << 1 << -1 << u"Γειά σου <i>Κόσμε</i>!"_s
+ << QQuickTextEdit::RichText << 3 << 1 << u"<!DOCTYPE HTML"_s << 2
+ << QQuickTextEdit::PlainText << 1 << -1 << u"<!DOCTYPE HTML"_s;
+ QTest::newRow("load html as autotext, switch to plain, back to auto")
+ << testFileUrl("text.qml") << QQuickTextEdit::AutoText << testFileUrl("hello.html")
+ << 3 << 1 << u"<!DOCTYPE HTML"_s
+ << QQuickTextEdit::PlainText << 1 << -1 << u"<!DOCTYPE HTML"_s << 2
+ << QQuickTextEdit::AutoText << 3 << 1 << u"<!DOCTYPE HTML"_s;
+ QTest::newRow("load html as autotext, switch to html, then plain")
+ << testFileUrl("text.qml") << QQuickTextEdit::AutoText << testFileUrl("hello.html")
+ << 3 << 1 << u"<!DOCTYPE HTML"_s
+ << QQuickTextEdit::RichText << 3 << 1 << u"<!DOCTYPE HTML"_s
+ // going from AutoText to a matching explicit format does not cause extra textChanged()
+ << 1
+ << QQuickTextEdit::PlainText << 1 << -1 << u"<!DOCTYPE HTML"_s;
+ QTest::newRow("load html as autotext, switch to markdown, then plain")
+ << testFileUrl("text.qml") << QQuickTextEdit::AutoText << testFileUrl("hello.html")
+ << 3 << 1 << u"<!DOCTYPE HTML"_s
+ << QQuickTextEdit::MarkdownText << 3 << 1 << u"Γειά σου *Κόσμε*!"_s << 2
+ << QQuickTextEdit::PlainText << 1 << -1 << u"Γειά σου *Κόσμε*!"_s;
+}
+
+void tst_qquicktextdocument::overrideTextFormat() // QTBUG-120772
+{
+ if (isMainFontFixed())
+ QSKIP("fixed-pitch main font (QTBUG-103484)");
+
+ QFETCH(QUrl, qmlfile);
+ QFETCH(QQuickTextEdit::TextFormat, initialFormat);
+ QFETCH(QUrl, source);
+ QFETCH(int, expectedInitialFragmentCount);
+ QFETCH(int, expectedInitialItalicFragment);
+ QFETCH(QString, expectedTextPrefix);
+
+ QFETCH(QQuickTextEdit::TextFormat, replacementFormat);
+ QFETCH(int, expectedFragmentCount);
+ QFETCH(int, expectedItalicFragment);
+ QFETCH(QString, expectedReplacementPrefix);
+ QFETCH(int, expectedTextChangedSignalsAfterReplacement);
+
+ QFETCH(QQuickTextEdit::TextFormat, finalFormat);
+ QFETCH(int, expectedFinalFragmentCount);
+ QFETCH(int, expectedFinalItalicFragment);
+ QFETCH(QString, expectedFinalPrefix);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, qmlfile));
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
+ QVERIFY(textEdit);
+ QQuickTextDocument *qqdoc = textEdit->property("textDocument").value<QQuickTextDocument*>();
+ QVERIFY(qqdoc);
+ QTextDocument *doc = qqdoc->textDocument();
+ QVERIFY(doc);
+
+ textEdit->setTextFormat(initialFormat);
+ QCOMPARE(qqdoc->isModified(), false);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 0);
+ QSignalSpy sourceChangedSpy(qqdoc, &QQuickTextDocument::sourceChanged);
+ QSignalSpy textChangedSpy(textEdit, &QQuickTextEdit::textChanged);
+
+ qqdoc->setProperty("source", source);
+ QCOMPARE(sourceChangedSpy.size(), 1);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 1);
+ QCOMPARE_GE(textChangedSpy.size(), 1);
+ auto fragCountAndItalic = fragmentsAndItalics(doc);
+ QCOMPARE(fragCountAndItalic.first, expectedInitialFragmentCount);
+ QCOMPARE(fragCountAndItalic.second, expectedInitialItalicFragment);
+ QString textPropValue = textEdit->text();
+ qCDebug(lcTests) << "expect text()" << textPropValue.first(qMin(20, textPropValue.size() - 1))
+ << "to start with" << expectedTextPrefix;
+ QVERIFY(textPropValue.startsWith(expectedTextPrefix));
+
+ textEdit->setTextFormat(replacementFormat);
+ QCOMPARE(qqdoc->isModified(), false);
+ QCOMPARE(sourceChangedSpy.size(), 1);
+ QCOMPARE_GE(textChangedSpy.size(), expectedTextChangedSignalsAfterReplacement);
+ fragCountAndItalic = fragmentsAndItalics(doc);
+ QCOMPARE(fragCountAndItalic.first, expectedFragmentCount);
+ QCOMPARE(fragCountAndItalic.second, expectedItalicFragment);
+ textPropValue = textEdit->text();
+ qCDebug(lcTests) << "expect text()" << textPropValue.first(qMin(20, textPropValue.size() - 1))
+ << "to start with" << expectedReplacementPrefix;
+ QVERIFY(textPropValue.startsWith(expectedReplacementPrefix));
+
+ textEdit->setTextFormat(finalFormat);
+ QCOMPARE(qqdoc->isModified(), false);
+ QCOMPARE(sourceChangedSpy.size(), 1);
+ QCOMPARE_GE(textChangedSpy.size(), expectedTextChangedSignalsAfterReplacement + 1);
+ fragCountAndItalic = fragmentsAndItalics(doc);
+ QCOMPARE(fragCountAndItalic.first, expectedFinalFragmentCount);
+ QCOMPARE(fragCountAndItalic.second, expectedFinalItalicFragment);
+ textPropValue = textEdit->text();
+ qCDebug(lcTests) << "expect text()" << textPropValue.first(qMin(20, textPropValue.size() - 1))
+ << "to start with" << expectedFinalPrefix;
+ QVERIFY(textPropValue.startsWith(expectedFinalPrefix));
+}
+
+void tst_qquicktextdocument::independentDocumentsSameSource_data()
+{
+ QTest::addColumn<QUrl>("qmlfile");
+
+ QTest::newRow("textFormat above source") << testFileUrl("sideBySideIndependent.qml");
+ QTest::newRow("source above textFormat") << testFileUrl("sideBySideIndependentReverse.qml");
+}
+
+// ensure that two TextEdits' textFormat properties take effect, regardless of qml init order
+void tst_qquicktextdocument::independentDocumentsSameSource() // QTBUG-120772
+{
+ QFETCH(QUrl, qmlfile);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, qmlfile));
+ QQuickTextEdit *textEditPlain = window.rootObject()->findChild<QQuickTextEdit *>("plain");
+ QVERIFY(textEditPlain);
+ QQuickTextEdit *textEditMarkdown = window.rootObject()->findChild<QQuickTextEdit *>("markdown");
+ QVERIFY(textEditMarkdown);
+
+ auto fragCountAndItalic = fragmentsAndItalics(textEditPlain->textDocument()->textDocument());
+ QCOMPARE(fragCountAndItalic.first, 1);
+ QCOMPARE(fragCountAndItalic.second, -1);
- QString name = "image";
- document.addResource(QTextDocument::ImageResource, name, image);
- QTextImageFormat format;
- format.setName(name);
- QCOMPARE(image, document.image(format));
- QCOMPARE(image, document.resource(QTextDocument::ImageResource, name).value<QImage>());
+ fragCountAndItalic = fragmentsAndItalics(textEditMarkdown->textDocument()->textDocument());
+ QCOMPARE(fragCountAndItalic.first, 3);
+ QCOMPARE(fragCountAndItalic.second, 1);
}
QTEST_MAIN(tst_qquicktextdocument)
diff --git a/tests/auto/quick/qquicktextedit/BLACKLIST b/tests/auto/quick/qquicktextedit/BLACKLIST
index 270a4f7869..d9c889dde3 100644
--- a/tests/auto/quick/qquicktextedit/BLACKLIST
+++ b/tests/auto/quick/qquicktextedit/BLACKLIST
@@ -1,10 +1,3 @@
-# QTBUG-109488 temporary fix
-[largeTextObservesViewport]
-linux
-windows
-android
-darwin
-
[mouseSelection]
opensuse-leap
@@ -36,3 +29,11 @@ android
# QTBUG-108346
[pasteHtmlIntoMarkdown]
ubuntu-20.04
+
+# QTBUG-118066
+[inFlickableMouse]
+opensuse-leap
+
+# QTBUG-118066
+[inFlickableTouch]
+opensuse-leap
diff --git a/tests/auto/quick/qquicktextedit/CMakeLists.txt b/tests/auto/quick/qquicktextedit/CMakeLists.txt
index c071517c04..76ac64d876 100644
--- a/tests/auto/quick/qquicktextedit/CMakeLists.txt
+++ b/tests/auto/quick/qquicktextedit/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktextedit Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktextedit LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicktextedit/data/embeddedImagesLocalError.qml b/tests/auto/quick/qquicktextedit/data/embeddedImagesLocalError.qml
index 067b6d72da..869ef455df 100644
--- a/tests/auto/quick/qquicktextedit/data/embeddedImagesLocalError.qml
+++ b/tests/auto/quick/qquicktextedit/data/embeddedImagesLocalError.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.0
+import QtQuick
TextEdit {
textFormat: TextEdit.RichText
diff --git a/tests/auto/quick/qquicktextedit/data/embeddedImagesRemote.qml b/tests/auto/quick/qquicktextedit/data/embeddedImagesRemote.qml
index 6fc12edf35..a80e669140 100644
--- a/tests/auto/quick/qquicktextedit/data/embeddedImagesRemote.qml
+++ b/tests/auto/quick/qquicktextedit/data/embeddedImagesRemote.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.0
+import QtQuick
TextEdit {
property string serverBaseUrl;
diff --git a/tests/auto/quick/qquicktextedit/data/embeddedImagesRemoteError.qml b/tests/auto/quick/qquicktextedit/data/embeddedImagesRemoteError.qml
index 7ac59e2db4..75b0c5fd5d 100644
--- a/tests/auto/quick/qquicktextedit/data/embeddedImagesRemoteError.qml
+++ b/tests/auto/quick/qquicktextedit/data/embeddedImagesRemoteError.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.0
+import QtQuick
TextEdit {
property string serverBaseUrl;
diff --git a/tests/auto/quick/qquicktextedit/data/focusByDefault.qml b/tests/auto/quick/qquicktextedit/data/focusByDefault.qml
new file mode 100644
index 0000000000..0f518aa7da
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/focusByDefault.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+TextEdit {
+ width: 200
+ height: 100
+ focus: true
+}
diff --git a/tests/auto/quick/qquicktextedit/data/httpfail/warning.png b/tests/auto/quick/qquicktextedit/data/httpfail/warning.png
new file mode 100644
index 0000000000..08d74b8b83
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/httpfail/warning.png
Binary files differ
diff --git a/tests/auto/quick/qquicktextedit/data/httpslow/turtle.svg b/tests/auto/quick/qquicktextedit/data/httpslow/turtle.svg
new file mode 100644
index 0000000000..6f34022d0e
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/httpslow/turtle.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="355.07501" height="205.41908" viewBox="0 0 284.06001 164.33527" version="1.1" id="svg2" inkscape:version="0.48.4 r9939" sodipodi:docname="AnimalSilhouettes2-Rabbit.svg"><link xmlns="" type="text/css" rel="stylesheet" id="dark-mode-custom-link"/><link xmlns="" type="text/css" rel="stylesheet" id="dark-mode-general-link"/><style xmlns="" lang="en" type="text/css" id="dark-mode-custom-style"/><style xmlns="" lang="en" type="text/css" id="dark-mode-native-style"/><style xmlns="" lang="en" type="text/css" id="dark-mode-native-sheet"/>
+ <metadata id="metadata11474">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs11472"/>
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1855" inkscape:window-height="1056" id="namedview11470" showgrid="false" units="in" fit-margin-top="0.25" fit-margin-left="0.25" fit-margin-right="0.25" fit-margin-bottom="0.25" inkscape:zoom="0.56869759" inkscape:cx="-269.65967" inkscape:cy="538.58793" inkscape:window-x="65" inkscape:window-y="24" inkscape:window-maximized="1" inkscape:current-layer="svg2"/>
+ <path d="m 116.14,18.675268 c 5.28,-0.32 10.73,-1.18 16,-0.09 1.71,0.51 3.5,0.69 5.3,0.64 0.66,0.08 1.99,0.24 2.65,0.32 0.98,0.67 2.07,0.89 3.26,0.67 l 0.84,0.14 c 0.53,0.21 1.6,0.63 2.13,0.84 l 0.82,-0.07 0.82,0.24 c 0.57,0.2 1.7,0.6 2.27,0.8 l 0.82,0.3 c 0.57,0.22 1.69,0.67 2.26,0.89 0.55,0.24 1.64,0.73 2.19,0.98 l 0.74,0.33 c 0.34,0.16 1.02,0.49 1.36,0.65 l 0.68,0.34 c 0.31,0.17 0.95,0.51 1.26,0.68 l 0.64,0.35 c 0.29,0.18 0.87,0.53 1.17,0.71 0.26,0.19 0.78,0.58 1.04,0.78 l 0.63,0.28 c 0.28,0.2 0.85,0.6 1.13,0.8 0.29,0.21 0.88,0.63 1.17,0.84 l 0.62,0.45 c 0.29,0.22 0.87,0.66 1.16,0.88 0.27,0.21 0.83,0.65 1.11,0.87 0.27,0.22 0.82,0.65 1.09,0.87 0.25,0.23 0.74,0.7 0.99,0.93 l 0.62,0.36 c 0.26,0.24 0.79,0.71 1.06,0.95 0.26,0.24 0.77,0.74 1.03,0.99 0.24,0.25 0.74,0.77 0.99,1.02 0.24,0.27 0.72,0.8 0.96,1.07 l 0.38,0.61 c 0.24,0.25 0.72,0.74 0.96,0.99 0.23,0.26 0.69,0.8 0.92,1.06 0.23,0.27 0.71,0.8 0.94,1.06 0.24,0.26 0.72,0.79 0.96,1.05 0.24,0.27 0.72,0.8 0.96,1.06 0.24,0.27 0.71,0.8 0.95,1.07 0.24,0.27 0.7,0.81 0.94,1.08 l 0.37,0.62 c 0.23,0.24 0.71,0.73 0.94,0.98 0.23,0.26 0.68,0.79 0.91,1.05 0.23,0.26 0.69,0.78 0.92,1.04 0.23,0.26 0.7,0.78 0.94,1.04 0.23,0.26 0.7,0.79 0.93,1.05 0.24,0.27 0.71,0.8 0.95,1.07 0.23,0.28 0.71,0.84 0.95,1.13 l 0.49,0.6 c 0.23,0.28 0.71,0.84 0.94,1.13 0.24,0.26 0.72,0.8 0.96,1.07 0.24,0.25 0.72,0.77 0.97,1.02 0.24,0.25 0.74,0.75 0.98,1 0.26,0.24 0.77,0.72 1.02,0.96 0.26,0.23 0.78,0.69 1.04,0.92 0.27,0.22 0.8,0.66 1.07,0.88 0.25,0.21 0.77,0.64 1.02,0.86 l 0.63,0.32 c 0.28,0.19 0.86,0.56 1.14,0.75 0.28,0.19 0.83,0.56 1.1,0.75 l 0.65,0.25 c 0.29,0.18 0.88,0.54 1.17,0.72 l 0.72,0.21 0.72,0.32 c 0.36,0.15 1.07,0.47 1.43,0.62 l 0.79,0.32 c 0.57,0.23 1.72,0.69 2.29,0.92 l 0.82,0.3 c 0.53,0.28 1.58,0.86 2.1,1.15 l 0.62,0.55 c 0,0.39 0.01,1.16 0.01,1.54 l -0.01,0.7 c -0.14,0.27 -0.42,0.81 -0.56,1.09 -0.13,0.24 -0.38,0.72 -0.5,0.96 l -0.19,0.5 c -0.1,0.23 -0.28,0.69 -0.38,0.92 -0.08,0.22 -0.24,0.66 -0.32,0.89 l -0.14,0.47 -0.16,0.48 c 0.2,-0.11 0.61,-0.34 0.81,-0.45 l 0.55,-0.03 c 0.3,-0.03 0.92,-0.1 1.22,-0.14 l 0.72,-0.12 c 0.52,-0.22 1.56,-0.66 2.09,-0.88 l 0.73,-0.2 c 0.31,-0.18 0.91,-0.55 1.21,-0.73 l 0.69,-0.21 c 0.3,-0.18 0.9,-0.54 1.2,-0.72 l 0.69,-0.22 c 0.3,-0.18 0.9,-0.55 1.2,-0.73 l 0.71,-0.22 c 0.3,-0.19 0.9,-0.55 1.21,-0.73 l 0.7,-0.23 c 0.3,-0.18 0.9,-0.54 1.2,-0.72 l 0.69,-0.23 c 0.3,-0.17 0.89,-0.53 1.19,-0.71 l 0.68,-0.2 c 0.29,-0.18 0.87,-0.53 1.16,-0.7 l 0.7,-0.18 c 0.29,-0.19 0.88,-0.55 1.18,-0.74 0.54,-0.23 1.61,-0.71 2.14,-0.94 l 0.76,-0.34 c 0.35,-0.15 1.07,-0.47 1.42,-0.62 l 0.77,-0.32 c 0.56,-0.2 1.69,-0.62 2.25,-0.83 l 0.82,-0.27 c 0.58,-0.18 1.74,-0.55 2.32,-0.74 0.44,-0.1 1.31,-0.3 1.75,-0.4 0.61,-0.15 1.84,-0.45 2.45,-0.61 0.73,-0.09 2.18,-0.27 2.91,-0.36 1.62,0.07 3.26,0.15 4.9,0.19 l 0.93,0.11 0.89,0.32 c 0.63,0.18 1.88,0.55 2.5,0.74 0.2,0.22 0.61,0.66 0.81,0.87 l 0.61,0.29 c 0.29,0.18 0.85,0.55 1.14,0.73 0.28,0.2 0.85,0.6 1.13,0.8 l 0.59,0.44 c 0.27,0.22 0.8,0.67 1.07,0.89 0.24,0.24 0.74,0.7 0.98,0.94 0.23,0.25 0.68,0.75 0.9,1 0.21,0.27 0.62,0.82 0.83,1.09 l 0.2,0.68 c 0.19,0.3 0.58,0.9 0.77,1.2 l 0.13,0.81 c 0.04,1.24 0.12,2.49 0.22,3.75 l -0.09,0.96 -0.27,0.95 c -0.2,0.66 -0.6,1.99 -0.8,2.65 -0.22,0.21 -0.66,0.62 -0.88,0.83 l -0.3,0.59 c -0.19,0.26 -0.57,0.78 -0.77,1.04 -0.21,0.24 -0.62,0.73 -0.83,0.97 -0.22,0.22 -0.67,0.67 -0.89,0.9 -0.24,0.21 -0.72,0.64 -0.95,0.85 -0.27,0.2 -0.8,0.61 -1.06,0.82 l -0.59,0.4 c -0.31,0.2 -0.92,0.59 -1.23,0.78 -0.53,0.24 -1.61,0.73 -2.15,0.97 -0.42,0.14 -1.26,0.41 -1.68,0.55 -0.59,0.18 -1.76,0.54 -2.34,0.73 l -0.83,0.25 c -0.57,0.19 -1.7,0.57 -2.27,0.76 l -0.81,0.25 c -0.57,0.17 -1.7,0.53 -2.27,0.7 -0.42,0.1 -1.26,0.29 -1.68,0.39 -0.56,0.17 -1.69,0.51 -2.26,0.69 l -0.82,0.23 c -0.57,0.17 -1.71,0.53 -2.27,0.7 -0.43,0.1 -1.27,0.29 -1.7,0.39 -0.57,0.18 -1.71,0.53 -2.29,0.71 l -0.82,0.24 c -0.56,0.19 -1.69,0.57 -2.25,0.76 l -0.81,0.23 -0.83,-0.05 c -0.53,0.21 -1.59,0.65 -2.12,0.87 l -0.83,0.19 c -0.57,0.18 -1.72,0.54 -2.29,0.73 l -0.82,0.250002 c -0.57,0.19 -1.7,0.58 -2.27,0.77 l -0.81,0.23 -0.83,-0.05 c -0.53,0.21 -1.58,0.65 -2.11,0.87 l -0.83,0.18 c -0.57,0.18 -1.69,0.54 -2.26,0.72 l -0.81,0.24 c -0.56,0.19 -1.67,0.57 -2.22,0.76 l -0.81,0.25 c -0.52,0.23 -1.58,0.67 -2.1,0.9 -0.53,0.2 -1.59,0.59 -2.12,0.79 l -0.77,0.25 c -0.54,0.13 -1.6,0.4 -2.14,0.53 l -0.62,0.4 0.17,0.54 c 0.23,0.76 0.46,1.52 0.7,2.29 l 0.19,0.61 c 0.42,-0.01 1.26,-0.03 1.68,-0.04 l 0.85,0 c 0.55,0.17 1.65,0.52 2.21,0.7 l 0.8,0.25 c 0.55,0.19 1.65,0.56 2.21,0.75 l 0.8,0.25 c 0.57,0.18 1.7,0.54 2.26,0.71 l 0.83,0.2 c 0.53,0.21 1.61,0.63 2.14,0.84 l 0.83,-0.07 0.83,0.2 c 0.96,0.75 2.04,1.01 3.25,0.79 l 0.88,-0.04 c 0.68,0.11 2.03,0.32 2.71,0.43 0.6,0.15 1.81,0.45 2.42,0.59 0.42,0.1 1.27,0.3 1.69,0.4 0.54,0.2 1.61,0.59 2.15,0.79 l 0.68,0.32 c 0.33,0.09 0.99,0.25 1.31,0.34 l 0.71,-0.02 c -0.2,0.55 -0.58,1.64 -0.78,2.19 -0.45,1.29 -1.09,2.52 -1.69,3.76 -1.54,0 -2.88,0.8 -4.06,1.7 -0.25,0.2 -0.75,0.61 -1,0.82 l -0.48,0.43 c -0.25,0.21 -0.75,0.61 -1,0.81 -1.23,0.36 -2.39,0.94 -3.3,1.85 -0.36,0.4 -1.1,1.21 -1.47,1.62 l -0.5,0.5 c -0.28,0.18 -0.83,0.52 -1.1,0.69 l -0.63,0.02 -0.63,-0.04 c -0.27,-0.14 -0.81,-0.41 -1.08,-0.55 -0.26,-0.16 -0.78,-0.49 -1.04,-0.66 -0.28,-0.18 -0.83,-0.54 -1.1,-0.72 l -0.59,-0.4 c -0.29,-0.2 -0.87,-0.6 -1.16,-0.79 -0.3,-0.2 -0.89,-0.59 -1.19,-0.79 l -0.63,-0.4 c -0.3,-0.19 -0.9,-0.57 -1.2,-0.76 -0.28,-0.21 -0.82,-0.61 -1.09,-0.82 l -0.67,-0.27 c -0.31,-0.19 -0.93,-0.58 -1.24,-0.77 l -0.65,-0.41 c -0.31,-0.2 -0.93,-0.6 -1.23,-0.8 -0.31,-0.21 -0.93,-0.61 -1.24,-0.81 l -0.64,-0.42 c -0.31,-0.21 -0.91,-0.62 -1.22,-0.83 -0.3,-0.21 -0.9,-0.62 -1.2,-0.82 l -0.63,-0.43 c -0.29,-0.2 -0.87,-0.62 -1.16,-0.82 -0.28,-0.2 -0.83,-0.6 -1.11,-0.8 -0.26,-0.21 -0.77,-0.63 -1.03,-0.84 l -0.62,-0.31 c -0.29,-0.2 -0.86,-0.6 -1.14,-0.81 -0.3,-0.2 -0.89,-0.6 -1.18,-0.81 l -0.63,-0.41 c -0.3,-0.2 -0.9,-0.6 -1.2,-0.8 -0.31,-0.19 -0.92,-0.56 -1.22,-0.75 l -0.64,-0.36 c -0.31,-0.17 -0.92,-0.51 -1.23,-0.68 -0.29,-0.15 -0.88,-0.46 -1.18,-0.62 l -0.72,-0.14 -0.74,-0.2 c -0.58,-1.39 -1.32,-1.5 -2.22,-0.32 -0.66,0.02 -1.99,0.07 -2.66,0.09 -1.79,-0.26 -3.55,0.52 -4.44,2.13 l -0.54,0.65 c -0.15,0.57 -0.44,1.71 -0.58,2.27 l -0.2,0.84 c -0.18,0.57 -0.55,1.72 -0.73,2.3 l -0.24,0.85 c -0.15,0.63 -0.46,1.89 -0.61,2.52 0.08,0.49 0.22,1.49 0.3,1.98 l -0.52,-0.35 c 0.08,0.3 0.24,0.91 0.32,1.21 0.15,0.32 0.45,0.96 0.6,1.28 0.17,0.29 0.5,0.86 0.67,1.15 0.21,0.25 0.63,0.75 0.84,1 0.24,0.22 0.72,0.67 0.97,0.89 l 0.52,0.44 c 0.24,0.21 0.73,0.64 0.98,0.85 0.22,0.22 0.65,0.67 0.86,0.89 l 0.53,0.38 c 0.2,0.26 0.59,0.8 0.78,1.07 0.09,0.34 0.25,1.02 0.34,1.36 0,0.31 0.01,0.94 0.02,1.25 l 0.39,-0.45 c -0.2,0.56 -0.61,1.66 -0.81,2.22 -0.07,0.32 -0.2,0.96 -0.27,1.28 l 0.49,-0.42 -0.67,0.52 c -0.32,0.48 -0.97,1.43 -1.3,1.91 l -0.17,0.79 c -0.03,0.4 -0.09,1.2 -0.11,1.61 -0.46,0.06 -1.37,0.18 -1.83,0.25 -1.52,0.03 -3,0.37 -4.37,0.98 -0.7,0.1 -2.11,0.28 -2.81,0.38 l -0.94,0.04 c -1.75,-0.01 -5.24,-0.03 -6.98,-0.04 -0.73,-0.11 -2.19,-0.33 -2.91,-0.44 -1,-0.7 -2.09,-0.99 -3.29,-0.87 l -0.73,-0.27 c -0.31,-0.14 -0.95,-0.43 -1.27,-0.58 l -0.61,-0.31 c -0.28,-0.18 -0.83,-0.53 -1.11,-0.71 l -0.53,-0.41 c -0.23,-0.25 -0.69,-0.74 -0.92,-0.99 l -0.39,-0.6 c -0.16,-0.34 -0.49,-1.01 -0.65,-1.35 l -0.24,-0.81 c -0.06,-0.69 -0.18,-2.07 -0.25,-2.75 0.07,-0.71 0.19,-2.13 0.25,-2.83 0.17,-0.36 0.5,-1.08 0.67,-1.43 -0.01,-0.43 -0.01,-1.28 -0.02,-1.71 l 0.18,-0.82 c 0.18,-0.34 0.54,-1 0.73,-1.34 0.02,-0.42 0.07,-1.27 0.09,-1.7 l 0.23,-0.85 c 0.17,-0.58 0.53,-1.75 0.7,-2.33 l 0.19,-0.8 c 0.2,-0.31 0.61,-0.91 0.82,-1.21 0.05,-0.41 0.15,-1.23 0.2,-1.65 l 0.36,-0.77 c 0.14,-0.4 0.42,-1.21 0.56,-1.61 l -0.78,-0.26 c -2.71,-0.95 -5.39,-1.57 -8.1,-2.36 -0.38,0.05 -1.15,0.14 -1.53,0.19 -3.58,-0.27 -7.09,-0.38 -10.65,-0.39 -6.39,-0.14 -12.77,0.3 -19.13,-0.1 -0.54,0.18 -1.61,0.52 -2.15,0.69 -0.36,0.09 -1.08,0.27 -1.44,0.36 -0.3,0.1 -0.89,0.3 -1.19,0.4 l 0.71,0.26 c -1.46,-0.04 -1.69,0.56 -0.71,1.8 l 0.07,0.82 c 0,0.4 0,1.21 0,1.61 0.16,0.33 0.47,0.98 0.63,1.31 l 0.14,0.79 c 0.06,0.61 0.17,1.82 0.22,2.43 0.17,0.32 0.5,0.97 0.67,1.29 l 0.24,0.77 c 0.27,0.51 0.8,1.53 1.07,2.04 0.49,0.31 1.48,0.93 1.98,1.23 0.42,0.11 1.25,0.32 1.67,0.42 1.31,0.63 2.73,1 4.19,1.09 0.45,0.07 1.34,0.21 1.79,0.29 0.02,0.43 0.05,1.29 0.07,1.71 l 0.11,0.87 c 0.05,0.43 0.14,1.31 0.19,1.75 l -0.19,0.89 c -0.04,0.44 -0.12,1.33 -0.16,1.77 -1.16,-0.14 -2.02,0.34 -2.57,1.45 -0.43,0.36 -1.3,1.07 -1.73,1.42 -0.12,0.39 -0.35,1.16 -0.47,1.55 l -0.87,-0.12 c -1.23,-0.3 -2.33,-0.04 -3.3,0.79 -1.69,-0.03 -5.06,-0.09 -6.74,-0.13 -1.78,-0.4 -3.59,-0.63 -5.4,-0.61 l -0.87,-0.04 c -0.42,-0.08 -1.28,-0.25 -1.71,-0.34 -0.56,-0.17 -1.67,-0.51 -2.23,-0.68 l -0.7,-0.26 c -0.26,-0.18 -0.79,-0.55 -1.05,-0.73 l -0.63,-0.19 c -0.25,-0.23 -0.74,-0.68 -0.99,-0.9 -0.31,-0.44 -0.94,-1.33 -1.25,-1.77 -0.22,-0.55 -0.65,-1.63 -0.87,-2.17 -0.11,-0.42 -0.34,-1.26 -0.46,-1.67 -0.15,-0.59 -0.47,-1.76 -0.62,-2.35 -0.08,-0.43 -0.24,-1.29 -0.32,-1.72 0.24,-1.21 0,-2.31 -0.7,-3.29 -0.07,-0.43 -0.23,-1.3 -0.3,-1.73 0.24,-1.21 0.01,-2.32 -0.68,-3.31 -0.09,-0.67 -0.27,-1.99 -0.36,-2.65 0.28,-1.2 0.08,-2.3 -0.6,-3.29 -0.12,-0.42 -0.36,-1.25 -0.47,-1.66 -0.4,-0.41 -1.19,-1.21 -1.58,-1.62 -1.12,-0.22 -2.23,-0.48 -3.31,-0.79 -1.11,-0.93 -2.62,-0.28 -2.98,1.03 -0.23,0.22 -0.69,0.65 -0.92,0.86 l -0.33,0.58 c -0.19,0.29 -0.57,0.85 -0.76,1.13 l -0.39,0.59 c -0.19,0.29 -0.57,0.86 -0.75,1.15 -0.2,0.26 -0.6,0.79 -0.8,1.06 l -0.28,0.63 c -0.19,0.28 -0.58,0.86 -0.77,1.14 -0.21,0.27 -0.63,0.8 -0.84,1.06 l -0.32,0.65 c -0.21,0.3 -0.63,0.88 -0.85,1.17 -0.22,0.3 -0.66,0.9 -0.88,1.2 l -0.47,0.62 c -0.23,0.29 -0.7,0.87 -0.93,1.16 -0.24,0.27 -0.71,0.81 -0.95,1.08 -0.24,0.25 -0.72,0.77 -0.96,1.02 -0.25,0.25 -0.75,0.75 -1,0.99 -0.25,0.24 -0.76,0.71 -1.02,0.95 -0.26,0.23 -0.79,0.68 -1.06,0.91 -0.28,0.21 -0.84,0.64 -1.11,0.85 l -0.65,0.28 c -0.28,0.2 -0.82,0.59 -1.1,0.79 l -0.67,0.19 c -0.3,0.16 -0.89,0.49 -1.19,0.65 l -0.67,0.25 c -0.34,0.09 -1.01,0.29 -1.34,0.38 l -0.7,0.06 c -0.32,-0.08 -0.97,-0.23 -1.29,-0.31 l -0.73,0.01 c -0.36,-0.13 -1.07,-0.37 -1.43,-0.5 -0.32,-0.17 -0.95,-0.53 -1.26,-0.71 l -0.6,-0.43 c -0.24,-0.26 -0.73,-0.77 -0.97,-1.03 -0.36,-0.12 -1.1,-0.36 -1.47,-0.49 -0.04,-1.19 -0.55,-2.15 -1.52,-2.86 -0.17,-0.37 -0.49,-1.11 -0.65,-1.47 l -0.35,-0.82 c -0.02,-3.04 -0.02,-5.92 0.08,-8.89 l 0.13,-0.88 0.25,-0.81 c 0.17,-0.3 0.51,-0.91 0.68,-1.21 l 0.13,-0.68 c 0.19,-0.25 0.57,-0.76 0.76,-1.01 l 0.26,-0.59 c 0.22,-0.24 0.65,-0.72 0.86,-0.96 0.26,-0.21 0.78,-0.65 1.03,-0.86 l 0.61,-0.41 c 0.32,-0.19 0.97,-0.57 1.29,-0.75 l 0.74,-0.36 c 0.57,-0.25 1.71,-0.73 2.28,-0.98 l 0.81,-0.34 c 0.52,-0.29 1.57,-0.86 2.09,-1.14 l 0.6,-0.46 c 0.23,-0.21 0.69,-0.65 0.92,-0.86 l 0.29,-0.54 c 0.12,-0.25 0.37,-0.76 0.49,-1.01 l 0.15,-0.55 c 0.09,-0.25 0.25,-0.74 0.33,-0.99 -0.16,-0.21 -0.49,-0.62 -0.66,-0.83 -0.4,-0.33 -1.2,-1.000002 -1.6,-1.330002 l -0.66,-0.34 c -0.28,-0.17 -0.85,-0.51 -1.14,-0.68 l -0.7,-0.15 -0.67,-0.29 c -0.33,-0.14 -0.98,-0.43 -1.31,-0.57 l -0.67,-0.3 c -0.34,-0.15 -1,-0.44 -1.34,-0.59 l -0.71,-0.3 c -0.36,-0.14 -1.09,-0.42 -1.45,-0.56 -0.42,-0.14 -1.25,-0.41 -1.67,-0.54 -1.07,-0.7 -2.23,-0.98 -3.48,-0.83 -0.44,-0.08 -1.33,-0.23 -1.77,-0.31 -0.36,-0.16 -1.07,-0.48 -1.43,-0.64 -0.65,-0.04 -1.94,-0.11 -2.59,-0.15 l -0.84,-0.16 c -0.34,-0.17 -1.03,-0.51 -1.38,-0.68 -0.42,-0.02 -1.27,-0.05 -1.7,-0.06 l -0.83,-0.19 c -0.34,-0.18 -1.03,-0.55 -1.37,-0.74 -0.44,-0.03 -1.33,-0.09 -1.77,-0.11 l -0.88,-0.26 c -0.6,-0.19 -1.82,-0.58 -2.43,-0.78 l -0.8,-0.26 c -0.32,-0.17 -0.97,-0.51 -1.29,-0.68 l -0.76,-0.1 -0.7,-0.3 c -0.33,-0.16 -0.99,-0.47 -1.31,-0.63 l -0.65,-0.32 c -0.29,-0.16 -0.88,-0.49 -1.18,-0.66 l -0.57,-0.32 c -0.24,-0.18 -0.73,-0.52 -0.98,-0.69 -0.21,-0.18 -0.64,-0.54 -0.85,-0.72 -0.2,-0.21 -0.6,-0.63 -0.8,-0.83 l -0.01,-0.64 0.39,-0.57 0.69,0.24 c 0.55,0.12 1.65,0.37 2.19,0.5 l 0.82,0.18 c 0.54,0.2 1.62,0.61 2.16,0.82 l 0.83,-0.06 0.83,0.18 c 0.55,0.22 1.65,0.65 2.2,0.87 l 0.85,-0.05 0.85,0.23 c 1.03,0.7 2.18,0.97 3.43,0.8 0.69,0.12 2.06,0.35 2.75,0.47 1.27,0.5 2.68,1.04 4.06,0.59 l 0.74,-0.14 c 0.91,-0.41 2.71,-0.11 2.88,-1.4 -0.06,-0.25 -0.19,-0.76 -0.26,-1.01 -0.22,-0.18 -0.68,-0.55 -0.91,-0.73 l -0.49,-0.32 c -0.23,-0.16 -0.69,-0.49 -0.92,-0.65 -0.21,-0.16 -0.63,-0.5 -0.84,-0.67 l -0.45,-0.32 c -0.17,-0.21 -0.53,-0.62 -0.7,-0.82 -0.12,-0.25 -0.35,-0.74 -0.46,-0.99 l 0.01,-0.56 0.33,-0.46 c 0.16,-0.23 0.46,-0.7 0.61,-0.93 l 0.45,-0.38 c 0.2,-0.23 0.58,-0.7 0.78,-0.93 0.21,-0.27 0.64,-0.82 0.85,-1.09 l 0.47,-0.63 c 0.31,-0.49 0.94,-1.47 1.25,-1.96 0.22,-0.27 0.67,-0.82 0.89,-1.09 l 0.28,-0.66 c 0.21,-0.28 0.63,-0.82 0.84,-1.1 0.22,-0.26 0.64,-0.79 0.86,-1.05 0.22,-0.26 0.65,-0.78 0.87,-1.04 0.22,-0.27 0.66,-0.81 0.88,-1.08 0.22,-0.29 0.65,-0.86 0.87,-1.15 l 0.45,-0.63 c 0.22,-0.3 0.65,-0.9 0.86,-1.21 0.21,-0.31 0.63,-0.93 0.84,-1.24 l 0.42,-0.65 c 0.2,-0.31 0.6,-0.92 0.8,-1.23 0.22,-0.27 0.64,-0.82 0.85,-1.09 l 0.29,-0.67 c 0.19,-0.29 0.59,-0.89 0.78,-1.18 0.21,-0.27 0.64,-0.82 0.85,-1.09 l 0.3,-0.66 c 0.2,-0.3 0.6,-0.9 0.8,-1.2 0.22,-0.27 0.65,-0.82 0.87,-1.1 l 0.31,-0.67 c 0.2,-0.3 0.61,-0.9 0.81,-1.2 0.22,-0.28 0.66,-0.83 0.87,-1.1 l 0.32,-0.67 c 0.2,-0.3 0.62,-0.9 0.82,-1.19 0.23,-0.27 0.67,-0.81 0.9,-1.08 l 0.32,-0.66 c 0.22,-0.29 0.65,-0.87 0.87,-1.16 0.21,-0.29 0.64,-0.86 0.86,-1.14 0.22,-0.26 0.68,-0.77 0.91,-1.03 l 0.35,-0.63 c 0.22,-0.28 0.67,-0.84 0.89,-1.11 0.23,-0.28 0.69,-0.83 0.91,-1.1 0.23,-0.28 0.69,-0.83 0.92,-1.11 0.23,-0.29 0.7,-0.86 0.93,-1.14 l 0.48,-0.61 c 0.24,-0.27 0.71,-0.83 0.94,-1.11 0.24,-0.26 0.71,-0.79 0.94,-1.05 0.24,-0.25 0.71,-0.75 0.95,-1 0.24,-0.25 0.73,-0.73 0.97,-0.97 0.25,-0.24 0.75,-0.71 1,-0.94 0.25,-0.24 0.77,-0.7 1.03,-0.93 0.28,-0.23 0.82,-0.69 1.1,-0.92 l 0.58,-0.47 c 0.28,-0.22 0.84,-0.67 1.12,-0.89 0.27,-0.22 0.81,-0.65 1.08,-0.87 0.27,-0.22 0.82,-0.65 1.09,-0.87 0.28,-0.21 0.85,-0.64 1.13,-0.85 l 0.61,-0.43 c 0.29,-0.2 0.88,-0.61 1.18,-0.82 0.3,-0.19 0.9,-0.58 1.2,-0.77 l 0.63,-0.38 c 0.3,-0.17 0.91,-0.51 1.22,-0.68 l 0.63,-0.26 c 0.21,-0.22 0.63,-0.66 0.84,-0.87 0.61,-0.19 1.82,-0.56 2.42,-0.75 l 0.84,-0.37 c 0.57,-0.24 1.73,-0.71 2.31,-0.94 0.41,-0.04 1.24,-0.11 1.66,-0.15 0.31,-0.2 0.93,-0.6 1.24,-0.8 l 0.81,-0.2 c 0.6,-0.16 1.8,-0.48 2.4,-0.64 0.45,-0.08 1.36,-0.25 1.81,-0.34 1.19,0 2.39,-0.04 3.59,-0.11 l 0.82,0.17 0.38,-0.76 c 1.2,-0.18 2.41,-0.29 3.64,-0.33 2.49,-0.05 4.98,-0.21 7.47,-0.48 z" id="path9046" inkscape:connector-curvature="0" style="fill:#000000;stroke:#000000;stroke-width:0.09375"/>
+</svg> \ No newline at end of file
diff --git a/tests/auto/quick/qquicktextedit/data/inFlickable.qml b/tests/auto/quick/qquicktextedit/data/inFlickable.qml
index 7a896db29b..183ddd6701 100644
--- a/tests/auto/quick/qquicktextedit/data/inFlickable.qml
+++ b/tests/auto/quick/qquicktextedit/data/inFlickable.qml
@@ -1,6 +1,7 @@
import QtQuick 2.0
Flickable {
+ id: flick
width: 320; height: 120; contentHeight: text.height
TextEdit {
id: text
@@ -8,4 +9,10 @@ Flickable {
font.pixelSize: 20
text: "several\nlines\nof\ntext\n-\ntry\nto\nflick"
}
+ Text {
+ color: "red"
+ parent: flick // stay on top
+ anchors.right: parent.right
+ text: flick.contentY.toFixed(1)
+ }
}
diff --git a/tests/auto/quick/qquicktextedit/data/multipleRemoteImages.md b/tests/auto/quick/qquicktextedit/data/multipleRemoteImages.md
new file mode 100644
index 0000000000..370924e739
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/multipleRemoteImages.md
@@ -0,0 +1,13 @@
+# Images
+
+Unsupported URL scheme: ![alt text](gopher://qt.io/icon48.png "Qt doesnt't support gopher")
+
+Just an image, expected to work: ![alt text](serverBaseUrl/exists.png "Qt logo")
+
+Reference-style, same image: ![alt text][logo]
+
+[logo]: serverBaseUrl/exists.png "Qt logo"
+
+Slow: ![Turtle](serverBaseUrl/turtle.svg "Behold the turtle")
+
+Fails to load from server: ![FailBang](serverBaseUrl/warning.png "Warning")
diff --git a/tests/auto/quick/qquicktextedit/data/qtConfigureHelp.qml b/tests/auto/quick/qquicktextedit/data/qtConfigureHelp.qml
new file mode 100644
index 0000000000..58ef58b372
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/qtConfigureHelp.qml
@@ -0,0 +1,186 @@
+import QtQuick
+import Qt.test 1.0
+
+NodeCheckerTextEdit {
+ id: textedit
+ width: 640
+ height: 480
+ text: "Usage: configure [options] [-- cmake-options]
+
+This is a convenience script for configuring Qt with CMake.
+Options after the double dash are directly passed to CMake.
+
+Top-level installation directories:
+ -prefix <dir> ...... The deployment directory, as seen on the target device.
+ [/usr/local/Qt-$QT_VERSION; qtbase build directory if
+ -developer-build]
+ -no-prefix ......... The deployment directory is set to the qtbase build
+ directory. Can be used instead of -developer-build
+ to not have to install, as well as avoid
+ -developer-build's default of -warnings-are-errors.
+ -extprefix <dir> ... The installation directory, as seen on the host machine.
+ [SYSROOT/PREFIX]
+
+Fine tuning of installation directory layout. Note that all directories
+except -sysconfdir should be located under -prefix:
+
+ -bindir <dir> ......... Executables [PREFIX/bin]
+ -headerdir <dir> ...... Header files [PREFIX/include]
+ -libdir <dir> ......... Libraries [PREFIX/lib]
+ -archdatadir <dir> .... Arch-dependent data [PREFIX]
+ -plugindir <dir> ...... Plugins [ARCHDATADIR/plugins]
+ -libexecdir <dir> ..... Helper programs [ARCHDATADIR/bin on Windows,
+ ARCHDATADIR/libexec otherwise]
+ -qmldir <dir> ......... QML imports [ARCHDATADIR/qml]
+ -datadir <dir> ........ Arch-independent data [PREFIX]
+ -docdir <dir> ......... Documentation [DATADIR/doc]
+ -translationdir <dir> . Translations [DATADIR/translations]
+ -sysconfdir <dir> ..... Settings used by Qt programs [PREFIX/etc/xdg]
+ -examplesdir <dir> .... Examples [PREFIX/examples]
+ -testsdir <dir> ....... Tests [PREFIX/tests]
+ -hostdatadir <dir> .... Data used by qmake [PREFIX]
+
+Conventions for the remaining options: When an option's description is
+followed by a list of values in brackets, the interpretation is as follows:
+'yes' represents the bare option; all other values are possible prefixes to
+the option, e.g., -no-gui. Alternatively, the value can be assigned, e.g.,
+--gui=yes. Values are listed in the order they are tried if not specified;
+'auto' is a shorthand for 'yes/no'. Solitary 'yes' and 'no' represent binary
+options without auto-detection.
+
+Configure meta:
+
+ -help, -h ............ Display this help screen
+ -redo ................ Re-configure with previously used options. In addition,
+ redo removes CMakeCache.txt file and CMakeFiles/ directory
+ and recreates them from scratch.
+ Additional options may be passed, but will not be
+ saved for later use by -redo.
+
+ -feature-<feature> ... Enable <feature>
+ -no-feature-<feature> Disable <feature> [none]
+ -list-features ....... List available features. Note that some features
+ have dedicated command line options as well.
+
+Build options:
+
+ -cmake-generator <name> ... Explicitly specify the build system generator for
+ CMake instead of auto-detecting one.
+ -cmake-use-default-generator ... Turn off auto-detection of the CMake build
+ system generatsr.
+ -cmake-file-api ...... Let CMake store build metadata for loading the build
+ into an IDE. [no; yes if -developer-build]
+ -no-guess-compiler ... Do not guess the compiler from the target mkspec.
+ -release ............. Build Qt with debugging turned off [yes]
+ -debug ............... Build Qt with debugging turned on [no]
+ -debug-and-release ... Build two versions of Qt, with and without
+ debugging turned on [yes] (Apple and Windows only)
+ -optimize-debug ...... Enable debug-friendly optimizations in debug builds
+ [auto] (Not supported with MSVC or Clang toolchains)
+ -optimize-size ....... Optimize release builds for size instead of speed [no]
+ -force-debug-info .... Create symbol files for release builds [no]
+ -separate-debug-info . Split off debug information to separate files [no]
+ -gdb-index ........... Index the debug info to speed up GDB
+ [no; auto if -developer-build with debug info]
+ -gc-binaries ......... Place each function or data item into its own section
+ and enable linker garbage collection of unused
+ sections. [auto for static builds, otherwise no]
+ -force-asserts ....... Enable Q_ASSERT even in release builds [no]
+ -developer-build ..... Compile and link Qt for developing Qt itself
+ (exports for auto-tests, extra checks, etc.) [no]
+
+ -shared .............. Build shared Qt libraries [yes] (no for UIKit)
+ -static .............. Build static Qt libraries [no] (yes for UIKit)
+ -framework ........... Build Qt framework bundles [yes] (Apple only)
+
+ -platform <target> ... Select mkspec for the qmake companion files
+ -device <name> ....... Select devices/mkspec for the qmake companion files
+ -device-option <key=value> ... Add option for the device mkspec
+
+ -appstore-compliant .. Disable code that is not allowed in platform app stores.
+ This is on by default for platforms which require distribution
+ through an app store by default, in particular Android,
+ iOS, tvOS, and watchOS. [auto]
+
+ -qt-host-path <path> . Specify path to a Qt host build for cross-compiling.
+ -qtnamespace <name> .. Wrap all Qt library code in 'namespace <name> {...}'.
+ -qtlibinfix <infix> .. Rename all libQt6*.so to libQt6*<infix>.so.
+
+ -testcocoon .......... Instrument with the TestCocoon code coverage tool [no]
+ -gcov ................ Instrument with the GCov code coverage tool [no]
+
+ -trace [backend] ..... Enable instrumentation with tracepoints.
+ Currently supported backends are 'etw' (Windows) and
+ 'lttng' (Linux), or 'yes' for auto-detection. [no]
+
+ -sanitize {address|thread|memory|fuzzer-no-link|undefined}
+ Instrument with the specified compiler sanitizer.
+ Note that some sanitizers cannot be combined;
+ for example, -sanitize address cannot be combined with
+ -sanitize thread.
+
+ -mips_dsp/-mips_dspr2 Use MIPS DSP/rev2 instructions [auto]
+
+ -qreal <type> ........ typedef qreal to the specified type. [double]
+ Note: this affects binary compatibility.
+
+ -R <string> .......... Add an explicit runtime library path to the Qt
+ libraries. Supports paths relative to LIBDIR.
+ -rpath ............... Link Qt libraries and executables using the library
+ install path as a runtime library path. Similar to
+ -R LIBDIR. On Apple platforms, disabling this implies
+ using absolute install names (based in LIBDIR) for
+ dynamic libraries and frameworks. [auto]
+
+ -reduce-exports ...... Reduce amount of exported symbols [auto]
+ -reduce-relocations .. Reduce amount of relocations [auto] (Unix only)
+
+ -plugin-manifests .... Embed manifests into plugins [no] (Windows only)
+ -static-runtime ...... With -static, use static runtime [no] (Windows only)
+
+ -pch ................. Use precompiled headers [auto]
+ -ltcg ................ Use Link Time Code Generation [no]
+ -intelcet ............ Use Intel Control-flow Enforcement Technology [no]
+ -linker [bfd,gold,lld,mold]
+ Force use of the GNU ld, GNU gold, LLVM/LLD or mold
+ linker instead of default one (GCC and clang only)
+ -ccache .............. Use the ccache compiler cache [no] (Unix only)
+ -unity-build ......... Enable Unity (Jumbo) build
+ -unity-build-batch-size <int>
+ Maximum number of source files used by the unity build
+ to create unity source files [8]
+
+ -warnings-are-errors . Treat warnings as errors [no; yes if -developer-build]
+
+ -disable-deprecated-up-to <version>
+ Set the QT_DISABLE_DEPRECATED_UP_TO value to <version>.
+ QT_DISABLE_DEPRECATED_UP_TO is used to remove
+ deprecated methods from both API and ABI.
+ <version> is a hex value, for example 0x060500 can be
+ used to remove all code deprecated in Qt 6.5.0 or
+ earlier releases.
+ By default <version> is set to 0x040000 and 0x050000 on
+ Windows, and non-Windows respectively.
+ -disable-deprecated-up-to <version>
+ Set the QT_DISABLE_DEPRECATED_UP_TO value to <version>.
+ QT_DISABLE_DEPRECATED_UP_TO is used to remove
+ deprecated methods from both API and ABI.
+ <version> is a hex value, for example 0x060500 can be
+ used to remove all code deprecated in Qt 6.5.0 or
+ earlier releases.
+ By default <version> is set to 0x040000 and 0x050000 on
+ Windows, and non-Windows respectively.
+used to remove all code deprecated in Qt 6.5.0 or
+ earlier releases.
+
+Build environment:
+
+ -sysroot <dir> ....... Set <dir> as the target sysroot
+
+ -pkg-config .......... Use pkg-config [auto] (Unix only)
+
+ -D <string> .......... Pass additional preprocessor define
+ -I <string> .......... Pass additional include path
+ -L <string> .......... Pass additional library path
+ -F <string> .......... Pass additional framework path (Apple only)>"
+}
diff --git a/tests/auto/quick/qquicktextedit/data/qtbug-112858.qml b/tests/auto/quick/qquicktextedit/data/qtbug-112858.qml
new file mode 100644
index 0000000000..95f7905737
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/qtbug-112858.qml
@@ -0,0 +1,12 @@
+import QtQuick
+import QtQuick.Layouts
+
+ColumnLayout {
+ id: layout
+ TextEdit {
+ readOnly: true
+ text: 'שדגשגשגשגדוגמ×
+בעיות יישור
+טֶקסט'
+ }
+}
diff --git a/tests/auto/quick/qquicktextedit/data/resizeTextEditPolish.qml b/tests/auto/quick/qquicktextedit/data/resizeTextEditPolish.qml
new file mode 100644
index 0000000000..44819a0de9
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/resizeTextEditPolish.qml
@@ -0,0 +1,42 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+ width: 400
+ height: 150
+
+ TextEdit {
+ id: textEdit
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ }
+ readOnly: true
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ text: "Lorem ipsum dolor sit amet, consectetur adipisicing"
+ states: [
+ State {
+ name: "multi-line"
+ when: textEdit.lineCount > 1
+ AnchorChanges {
+ target: textEdit
+ anchors.bottom: undefined
+ }
+ PropertyChanges {
+ target: textEdit
+ height: undefined
+ }
+ },
+ State {
+ name: "single-line"
+ when: true
+ AnchorChanges {
+ target: textEdit
+ anchors.bottom: { return textEdit.parent.bottom }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/auto/quick/qquicktextedit/data/textEdit.qml b/tests/auto/quick/qquicktextedit/data/textEdit.qml
new file mode 100644
index 0000000000..3bcc40350e
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/textEdit.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+TextEdit {
+ width: 400
+ height: 540
+}
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index d15d40083d..cc994fe783 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -1,12 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
#include <math.h>
#include <QFile>
#include <QtQuick/QQuickTextDocument>
+#include <QtQuickTest/QtQuickTest>
#include <QTextDocument>
+#include <QtGui/qtextobject.h>
+#include <QtGui/QTextTable>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
@@ -18,7 +21,7 @@
#include <private/qquicktextedit_p.h>
#include <private/qquicktextedit_p_p.h>
#include <private/qquicktext_p.h>
-#include <private/qquicktextdocument_p.h>
+#include <QtQuick/private/qquickpixmapcache_p.h>
#include <QFontMetrics>
#include <QtQuick/QQuickView>
#include <QDir>
@@ -40,14 +43,31 @@ Q_DECLARE_METATYPE(QQuickTextEdit::SelectionMode)
Q_DECLARE_METATYPE(Qt::Key)
DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
+typedef QList<int> IntList;
+Q_DECLARE_METATYPE(IntList)
+
+typedef QPair<int, QChar> Key;
+typedef QList<Key> KeyList;
+Q_DECLARE_METATYPE(KeyList)
+
+Q_DECLARE_METATYPE(QQuickTextEdit::HAlignment)
+Q_DECLARE_METATYPE(QQuickTextEdit::VAlignment)
+Q_DECLARE_METATYPE(QQuickTextEdit::TextFormat)
+
+#if QT_CONFIG(shortcut)
+Q_DECLARE_METATYPE(QKeySequence::StandardKey)
+#endif
+
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+// #define DEBUG_WRITE_INPUT
+
static bool isPlatformWayland()
{
return !QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive);
}
-typedef QPair<int, QChar> Key;
+QT_BEGIN_NAMESPACE
class tst_qquicktextedit : public QQmlDataTest
@@ -140,7 +160,10 @@ private slots:
void implicitSizeBinding();
void largeTextObservesViewport_data();
void largeTextObservesViewport();
+ void largeTextSelection();
void renderingAroundSelection();
+ void largeTextTables_data();
+ void largeTextTables();
void signal_editingfinished();
@@ -177,6 +200,7 @@ private slots:
void baseUrl();
void embeddedImages();
void embeddedImages_data();
+ void remoteImagesInDocumentSource();
void emptytags_QTBUG_22058();
void cursorRectangle_QTBUG_38947();
@@ -206,6 +230,13 @@ private slots:
void touchscreenDoesNotSelect();
void touchscreenSetsFocusAndMovesCursor();
+ void longPressInputMethod();
+
+ void rtlAlignmentInColumnLayout_QTBUG_112858();
+
+ void fontManipulationWithCursorSelection();
+ void resizeTextEditPolish();
+
private:
void simulateKeys(QWindow *window, const QList<Key> &keys);
#if QT_CONFIG(shortcut)
@@ -232,16 +263,6 @@ private:
QPointingDevice *touchDevice = QTest::createTouchDevice();
};
-typedef QList<int> IntList;
-Q_DECLARE_METATYPE(IntList)
-
-typedef QList<Key> KeyList;
-Q_DECLARE_METATYPE(KeyList)
-
-Q_DECLARE_METATYPE(QQuickTextEdit::HAlignment)
-Q_DECLARE_METATYPE(QQuickTextEdit::VAlignment)
-Q_DECLARE_METATYPE(QQuickTextEdit::TextFormat)
-
void tst_qquicktextedit::simulateKeys(QWindow *window, const QList<Key> &keys)
{
for (int i = 0; i < keys.size(); ++i) {
@@ -346,7 +367,7 @@ public:
void populateLinePositions(QSGNode *node)
{
- linePositions.clear();
+ sortedLinePositions.clear();
lastLinePosition = 0;
QSGNode *ch = node->firstChild();
while (ch != node->lastChild()) {
@@ -361,14 +382,12 @@ public:
qCDebug(lcTests) << "ignoring leaf TransformNode" << tn << "@ y" << y;
} else {
qCDebug(lcTests) << "child" << tn << "@ y" << y << "has children" << tn->childCount();
- if (!linePositions.contains(y)) {
- linePositions.append(y);
- lastLinePosition = qMax(lastLinePosition, y);
- }
+ sortedLinePositions.append(y);
+ lastLinePosition = qMax(lastLinePosition, y);
}
ch = ch->nextSibling();
}
- std::sort(linePositions.begin(), linePositions.end());
+ std::sort(sortedLinePositions.begin(), sortedLinePositions.end());
}
QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) override
@@ -379,7 +398,7 @@ public:
return ret;
}
- QList<int> linePositions;
+ QList<int> sortedLinePositions;
int lastLinePosition;
};
@@ -714,9 +733,9 @@ void tst_qquicktextedit::lineCount_data()
void tst_qquicktextedit::lineCount()
{
- QFETCH(QStringList, texts);
+ QFETCH(const QStringList, texts);
- foreach (const QString& text, texts) {
+ for (const QString& text : texts) {
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nTextEdit { }", QUrl());
@@ -1297,10 +1316,8 @@ void tst_qquicktextedit::textMargin()
void tst_qquicktextedit::persistentSelection()
{
- QQuickView window(testFileUrl("persistentSelection.qml"));
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("persistentSelection.qml")));
QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(edit);
@@ -1410,6 +1427,7 @@ void tst_qquicktextedit::selectionOnFocusOut()
void tst_qquicktextedit::focusOnPress()
{
+ const int doubleClickInterval = qApp->styleHints()->mouseDoubleClickInterval();
QString componentStr =
"import QtQuick 2.0\n"
"TextEdit {\n"
@@ -1466,7 +1484,7 @@ void tst_qquicktextedit::focusOnPress()
QCOMPARE(activeFocusSpy.size(), 2);
// Wait for double click timeout to expire before clicking again.
- QTest::qWait(400);
+ QTest::qWait(doubleClickInterval + 1);
QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
QGuiApplication::processEvents();
QCOMPARE(textEditObject->hasFocus(), false);
@@ -1482,7 +1500,7 @@ void tst_qquicktextedit::focusOnPress()
// Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
textEditObject->setProperty("selectOnFocus", true);
- QTest::qWait(400);
+ QTest::qWait(doubleClickInterval + 1);
QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
QGuiApplication::processEvents();
QCOMPARE(textEditObject->hasFocus(), true);
@@ -1579,20 +1597,12 @@ void tst_qquicktextedit::selection()
void tst_qquicktextedit::overwriteMode()
{
- QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; }";
- QQmlComponent textEditComponent(&engine);
- textEditComponent.setData(componentStr.toLatin1(), QUrl());
- QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
- QVERIFY(textEdit != nullptr);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("focusByDefault.qml")));
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QSignalSpy spy(textEdit, SIGNAL(overwriteModeChanged(bool)));
- QQuickWindow window;
- textEdit->setParentItem(window.contentItem());
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
-
QVERIFY(textEdit->hasActiveFocus());
textEdit->setOverwriteMode(true);
@@ -1704,12 +1714,8 @@ void tst_qquicktextedit::isRightToLeft()
void tst_qquicktextedit::keySelection()
{
- QQuickView window(testFileUrl("navigation.qml"));
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
-
- QVERIFY(window.rootObject() != nullptr);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("navigation.qml")));
QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
@@ -2086,7 +2092,7 @@ void tst_qquicktextedit::moveCursorSelectionSequence()
void tst_qquicktextedit::mouseSelection_data()
{
- QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<QUrl>("qmlfile");
QTest::addColumn<int>("from");
QTest::addColumn<int>("to");
QTest::addColumn<QString>("selectedText");
@@ -2095,64 +2101,64 @@ void tst_qquicktextedit::mouseSelection_data()
QTest::addColumn<int>("clicks");
// import installed
- QTest::newRow("on") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << true << true << 1;
- QTest::newRow("off") << testFile("mouseselection_false.qml") << 4 << 9 << QString() << true << true << 1;
- QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << 4 << 9 << "45678" << true << true << 1;
- QTest::newRow("off word selection") << testFile("mouseselection_false_words.qml") << 4 << 9 << QString() << true << true << 1;
- QTest::newRow("on word selection (4,9)") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << true << true << 1;
-
- QTest::newRow("on unfocused") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << false << false << 1;
- QTest::newRow("on word selection (4,9) unfocused") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << false << 1;
-
- QTest::newRow("on focus on press") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << false << true << 1;
- QTest::newRow("on word selection (4,9) focus on press") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << true << 1;
-
- QTest::newRow("on word selection (2,13)") << testFile("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
- QTest::newRow("on word selection (2,30)") << testFile("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
- QTest::newRow("on word selection (9,13)") << testFile("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
- QTest::newRow("on word selection (9,30)") << testFile("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
- QTest::newRow("on word selection (13,2)") << testFile("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
- QTest::newRow("on word selection (20,2)") << testFile("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
- QTest::newRow("on word selection (12,9)") << testFile("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
- QTest::newRow("on word selection (30,9)") << testFile("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
-
- QTest::newRow("on double click (4,9)") << testFile("mouseselection_true.qml") << 4 << 9 << "0123456789" << true << true << 2;
- QTest::newRow("on double click (2,13)") << testFile("mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
- QTest::newRow("on double click (2,30)") << testFile("mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
- QTest::newRow("on double click (9,13)") << testFile("mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
- QTest::newRow("on double click (9,30)") << testFile("mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
- QTest::newRow("on double click (13,2)") << testFile("mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
- QTest::newRow("on double click (20,2)") << testFile("mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
- QTest::newRow("on double click (12,9)") << testFile("mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
- QTest::newRow("on double click (30,9)") << testFile("mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
-
- QTest::newRow("on triple click (4,9)") << testFile("mouseselection_true.qml") << 4 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
- QTest::newRow("on triple click (2,13)") << testFile("mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
- QTest::newRow("on triple click (2,30)") << testFile("mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
- QTest::newRow("on triple click (9,13)") << testFile("mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
- QTest::newRow("on triple click (9,30)") << testFile("mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
- QTest::newRow("on triple click (13,2)") << testFile("mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
- QTest::newRow("on triple click (20,2)") << testFile("mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
- QTest::newRow("on triple click (12,9)") << testFile("mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
- QTest::newRow("on triple click (30,9)") << testFile("mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
-
- QTest::newRow("on triple click (2,40)") << testFile("mouseselection_true.qml") << 2 << 40 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
- QTest::newRow("on triple click (2,50)") << testFile("mouseselection_true.qml") << 2 << 50 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
- QTest::newRow("on triple click (25,40)") << testFile("mouseselection_true.qml") << 25 << 40 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
- QTest::newRow("on triple click (25,50)") << testFile("mouseselection_true.qml") << 25 << 50 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
- QTest::newRow("on triple click (40,25)") << testFile("mouseselection_true.qml") << 40 << 25 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
- QTest::newRow("on triple click (40,50)") << testFile("mouseselection_true.qml") << 40 << 50 << "9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
- QTest::newRow("on triple click (50,25)") << testFile("mouseselection_true.qml") << 50 << 25 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
- QTest::newRow("on triple click (50,40)") << testFile("mouseselection_true.qml") << 50 << 40 << "9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
-
- QTest::newRow("on tr align") << testFile("mouseselection_align_tr.qml") << 4 << 9 << "45678" << true << true << 1;
- QTest::newRow("on center align") << testFile("mouseselection_align_center.qml") << 4 << 9 << "45678" << true << true << 1;
- QTest::newRow("on bl align") << testFile("mouseselection_align_bl.qml") << 4 << 9 << "45678" << true << true << 1;
+ QTest::newRow("on") << testFileUrl("mouseselection_true.qml") << 4 << 9 << "45678" << true << true << 1;
+ QTest::newRow("off") << testFileUrl("mouseselection_false.qml") << 4 << 9 << QString() << true << true << 1;
+ QTest::newRow("default") << testFileUrl("mouseselectionmode_default.qml") << 4 << 9 << "45678" << true << true << 1;
+ QTest::newRow("off word selection") << testFileUrl("mouseselection_false_words.qml") << 4 << 9 << QString() << true << true << 1;
+ QTest::newRow("on word selection (4,9)") << testFileUrl("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << true << true << 1;
+
+ QTest::newRow("on unfocused") << testFileUrl("mouseselection_true.qml") << 4 << 9 << "45678" << false << false << 1;
+ QTest::newRow("on word selection (4,9) unfocused") << testFileUrl("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << false << 1;
+
+ QTest::newRow("on focus on press") << testFileUrl("mouseselection_true.qml") << 4 << 9 << "45678" << false << true << 1;
+ QTest::newRow("on word selection (4,9) focus on press") << testFileUrl("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << true << 1;
+
+ QTest::newRow("on word selection (2,13)") << testFileUrl("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
+ QTest::newRow("on word selection (2,30)") << testFileUrl("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
+ QTest::newRow("on word selection (9,13)") << testFileUrl("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
+ QTest::newRow("on word selection (9,30)") << testFileUrl("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
+ QTest::newRow("on word selection (13,2)") << testFileUrl("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
+ QTest::newRow("on word selection (20,2)") << testFileUrl("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
+ QTest::newRow("on word selection (12,9)") << testFileUrl("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
+ QTest::newRow("on word selection (30,9)") << testFileUrl("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
+
+ QTest::newRow("on double click (4,9)") << testFileUrl("mouseselection_true.qml") << 4 << 9 << "0123456789" << true << true << 2;
+ QTest::newRow("on double click (2,13)") << testFileUrl("mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
+ QTest::newRow("on double click (2,30)") << testFileUrl("mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
+ QTest::newRow("on double click (9,13)") << testFileUrl("mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
+ QTest::newRow("on double click (9,30)") << testFileUrl("mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
+ QTest::newRow("on double click (13,2)") << testFileUrl("mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
+ QTest::newRow("on double click (20,2)") << testFileUrl("mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
+ QTest::newRow("on double click (12,9)") << testFileUrl("mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
+ QTest::newRow("on double click (30,9)") << testFileUrl("mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
+
+ QTest::newRow("on triple click (4,9)") << testFileUrl("mouseselection_true.qml") << 4 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+ QTest::newRow("on triple click (2,13)") << testFileUrl("mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+ QTest::newRow("on triple click (2,30)") << testFileUrl("mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+ QTest::newRow("on triple click (9,13)") << testFileUrl("mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+ QTest::newRow("on triple click (9,30)") << testFileUrl("mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+ QTest::newRow("on triple click (13,2)") << testFileUrl("mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+ QTest::newRow("on triple click (20,2)") << testFileUrl("mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+ QTest::newRow("on triple click (12,9)") << testFileUrl("mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+ QTest::newRow("on triple click (30,9)") << testFileUrl("mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
+
+ QTest::newRow("on triple click (2,40)") << testFileUrl("mouseselection_true.qml") << 2 << 40 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
+ QTest::newRow("on triple click (2,50)") << testFileUrl("mouseselection_true.qml") << 2 << 50 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
+ QTest::newRow("on triple click (25,40)") << testFileUrl("mouseselection_true.qml") << 25 << 40 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
+ QTest::newRow("on triple click (25,50)") << testFileUrl("mouseselection_true.qml") << 25 << 50 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
+ QTest::newRow("on triple click (40,25)") << testFileUrl("mouseselection_true.qml") << 40 << 25 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
+ QTest::newRow("on triple click (40,50)") << testFileUrl("mouseselection_true.qml") << 40 << 50 << "9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
+ QTest::newRow("on triple click (50,25)") << testFileUrl("mouseselection_true.qml") << 50 << 25 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
+ QTest::newRow("on triple click (50,40)") << testFileUrl("mouseselection_true.qml") << 50 << 40 << "9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
+
+ QTest::newRow("on tr align") << testFileUrl("mouseselection_align_tr.qml") << 4 << 9 << "45678" << true << true << 1;
+ QTest::newRow("on center align") << testFileUrl("mouseselection_align_center.qml") << 4 << 9 << "45678" << true << true << 1;
+ QTest::newRow("on bl align") << testFileUrl("mouseselection_align_bl.qml") << 4 << 9 << "45678" << true << true << 1;
}
void tst_qquicktextedit::mouseSelection()
{
- QFETCH(QString, qmlfile);
+ QFETCH(QUrl, qmlfile);
QFETCH(int, from);
QFETCH(int, to);
QFETCH(QString, selectedText);
@@ -2160,13 +2166,8 @@ void tst_qquicktextedit::mouseSelection()
QFETCH(bool, focusOnPress);
QFETCH(int, clicks);
- QQuickView window(QUrl::fromLocalFile(qmlfile));
-
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
-
- QVERIFY(window.rootObject() != nullptr);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, qmlfile));
QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(textEditObject != nullptr);
@@ -2201,15 +2202,8 @@ void tst_qquicktextedit::mouseSelection()
void tst_qquicktextedit::dragMouseSelection()
{
- QString qmlfile = testFile("mouseselection_true.qml");
-
- QQuickView window(QUrl::fromLocalFile(qmlfile));
-
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
-
- QVERIFY(window.rootObject() != nullptr);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("mouseselection_true.qml")));
QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(textEditObject != nullptr);
@@ -2220,7 +2214,6 @@ void tst_qquicktextedit::dragMouseSelection()
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(x1,y));
QTest::mouseMove(&window, QPoint(x2, y));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(x2,y));
- QTest::qWait(300);
QString str1;
QTRY_VERIFY((str1 = textEditObject->selectedText()).size() > 3);
@@ -2230,7 +2223,6 @@ void tst_qquicktextedit::dragMouseSelection()
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(x1,y));
QTest::mouseMove(&window, QPoint(x2, y));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(x2,y));
- QTest::qWait(300);
QString str2;
QTRY_VERIFY((str2 = textEditObject->selectedText()).size() > 3);
@@ -2240,29 +2232,25 @@ void tst_qquicktextedit::dragMouseSelection()
void tst_qquicktextedit::mouseSelectionMode_data()
{
- QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<QUrl>("qmlfile");
QTest::addColumn<bool>("selectWords");
// import installed
- QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
- QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
- QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
+ QTest::newRow("SelectWords") << testFileUrl("mouseselectionmode_words.qml") << true;
+ QTest::newRow("SelectCharacters") << testFileUrl("mouseselectionmode_characters.qml") << false;
+ QTest::newRow("default") << testFileUrl("mouseselectionmode_default.qml") << false;
}
void tst_qquicktextedit::mouseSelectionMode()
{
- QFETCH(QString, qmlfile);
+ QFETCH(QUrl, qmlfile);
QFETCH(bool, selectWords);
- QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
- QQuickView window(QUrl::fromLocalFile(qmlfile));
+ const QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, qmlfile));
- QVERIFY(window.rootObject() != nullptr);
QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(textEditObject != nullptr);
textEditObject->setSelectByMouse(true);
@@ -2372,8 +2360,6 @@ void tst_qquicktextedit::selectByKeyboard()
#if QT_CONFIG(shortcut)
-Q_DECLARE_METATYPE(QKeySequence::StandardKey)
-
void tst_qquicktextedit::keyboardSelection_data()
{
QTest::addColumn<QString>("text");
@@ -2433,27 +2419,19 @@ void tst_qquicktextedit::keyboardSelection()
QFETCH(QKeySequence::StandardKey, standardKey);
QFETCH(QString, selectedText);
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.1\n TextEdit { focus: true }", QUrl());
- QScopedPointer<QObject> object(component.create());
- QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data());
- QVERIFY(edit);
-
- edit->setText(text);
- edit->setSelectByKeyboard(selectByKeyboard);
- edit->setReadOnly(readOnly);
- edit->setCursorPosition(cursorPosition);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("focusByDefault.qml")));
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
+ QVERIFY(textEdit->hasActiveFocus());
- QQuickWindow window;
- edit->setParentItem(window.contentItem());
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
- QVERIFY(edit->hasActiveFocus());
+ textEdit->setText(text);
+ textEdit->setSelectByKeyboard(selectByKeyboard);
+ textEdit->setReadOnly(readOnly);
+ textEdit->setCursorPosition(cursorPosition);
simulateKeys(&window, standardKey);
- QCOMPARE(edit->selectedText(), selectedText);
+ QCOMPARE(textEdit->selectedText(), selectedText);
}
#endif // QT_CONFIG(shortcut)
@@ -2484,11 +2462,8 @@ void tst_qquicktextedit::renderType()
void tst_qquicktextedit::inputMethodHints()
{
- QQuickView window(testFileUrl("inputmethodhints.qml"));
- window.show();
- window.requestActivate();
-
- QVERIFY(window.rootObject() != nullptr);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inputmethodhints.qml")));
QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(textEditObject != nullptr);
QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
@@ -2523,11 +2498,8 @@ void tst_qquicktextedit::positionAt()
QFETCH(QQuickTextEdit::HAlignment, horizontalAlignment);
QFETCH(QQuickTextEdit::VAlignment, verticalAlignment);
- QQuickView window(testFileUrl("positionAt.qml"));
- QVERIFY(window.rootObject() != nullptr);
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("positionAt.qml")));
QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(texteditObject != nullptr);
@@ -2658,13 +2630,8 @@ void tst_qquicktextedit::linkHover()
void tst_qquicktextedit::linkInteraction()
{
- QQuickView window(testFileUrl("linkInteraction.qml"));
- QVERIFY(window.rootObject() != nullptr);
- QQuickVisualTestUtils::centerOnScreen(&window);
- QQuickVisualTestUtils::moveMouseAway(&window);
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("linkInteraction.qml")));
QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(texteditObject != nullptr);
@@ -2721,11 +2688,11 @@ void tst_qquicktextedit::cursorDelegate_data()
void tst_qquicktextedit::cursorDelegate()
{
QFETCH(QUrl, source);
- QQuickView view(source);
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
- QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
+
+ const int doubleClickInterval = qApp->styleHints()->mouseDoubleClickInterval();
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, source));
+ QQuickTextEdit *textEditObject = window.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
QVERIFY(textEditObject != nullptr);
// Delegate creation is deferred until focus in or cursor visibility is forced.
QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
@@ -2747,9 +2714,8 @@ void tst_qquicktextedit::cursorDelegate()
textEditObject->setSelectByMouse(true);
textEditObject->setCursorPosition(0);
const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint();
- QTest::qWait(400); //ensure this isn't treated as a double-click
- QTest::mouseClick(&view, Qt::LeftButton, Qt::NoModifier, point1);
- QTest::qWait(50);
+ QTest::qWait(doubleClickInterval + 1); // ensure this isn't treated as a double-click
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, point1);
QTRY_VERIFY(textEditObject->cursorPosition() != 0);
QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
@@ -2757,29 +2723,26 @@ void tst_qquicktextedit::cursorDelegate()
// Test delegate gets moved on mouse drag
textEditObject->setCursorPosition(0);
const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
- QTest::qWait(400); //ensure this isn't treated as a double-click
- QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, point1);
- QMouseEvent mv(QEvent::MouseMove, point2, view.mapToGlobal(point2),
+ QTest::qWait(doubleClickInterval + 1);
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, point1);
+ QMouseEvent mv(QEvent::MouseMove, point2, window.mapToGlobal(point2),
Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
- QGuiApplication::sendEvent(&view, &mv);
- QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, point2);
- QTest::qWait(50);
+ QGuiApplication::sendEvent(&window, &mv);
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, point2);
QTRY_COMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
textEditObject->setReadOnly(true);
textEditObject->setCursorPosition(0);
- QTest::qWait(400); //ensure this isn't treated as a double-click
- QTest::mouseClick(&view, Qt::LeftButton, Qt::NoModifier, textEditObject->positionToRectangle(5).center().toPoint());
- QTest::qWait(50);
+ QTest::qWait(doubleClickInterval + 1);
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, textEditObject->positionToRectangle(5).center().toPoint());
QTRY_VERIFY(textEditObject->cursorPosition() != 0);
QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
textEditObject->setCursorPosition(0);
- QTest::qWait(400); //ensure this isn't treated as a double-click
- QTest::mouseClick(&view, Qt::LeftButton, Qt::NoModifier, textEditObject->positionToRectangle(5).center().toPoint());
- QTest::qWait(50);
+ QTest::qWait(doubleClickInterval + 1);
+ QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, textEditObject->positionToRectangle(5).center().toPoint());
QTRY_VERIFY(textEditObject->cursorPosition() != 0);
QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
@@ -2793,7 +2756,7 @@ void tst_qquicktextedit::cursorDelegate()
// Delegate moved when text is entered
textEditObject->setText(QString());
for (int i = 0; i < 20; ++i) {
- QTest::keyClick(&view, Qt::Key_A);
+ QTest::keyClick(&window, Qt::Key_A);
QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
}
@@ -2863,11 +2826,10 @@ void tst_qquicktextedit::cursorVisible()
edit.componentComplete();
QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
- QQuickView view(testFileUrl("cursorVisible.qml"));
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
- QCOMPARE(&view, qGuiApp->focusWindow());
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("cursorVisible.qml")));
+
+ QCOMPARE(qGuiApp->focusWindow(), &window);
QCOMPARE(edit.isCursorVisible(), false);
@@ -2883,7 +2845,7 @@ void tst_qquicktextedit::cursorVisible()
QCOMPARE(edit.isCursorVisible(), false);
QCOMPARE(spy.size(), 2);
- edit.setParentItem(view.rootObject());
+ edit.setParentItem(window.rootObject());
QCOMPARE(edit.isCursorVisible(), true);
QCOMPARE(spy.size(), 3);
@@ -2903,8 +2865,8 @@ void tst_qquicktextedit::cursorVisible()
QCOMPARE(edit.isCursorVisible(), false);
QCOMPARE(spy.size(), 6);
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
+ window.requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(&window));
QCOMPARE(edit.isCursorVisible(), true);
QCOMPARE(spy.size(), 7);
@@ -3008,11 +2970,9 @@ void tst_qquicktextedit::delegateLoading()
void tst_qquicktextedit::cursorDelegateHeight()
{
- QQuickView view(testFileUrl("cursorHeight.qml"));
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
- QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("cursorHeight.qml")));
+ QQuickTextEdit *textEditObject = window.rootObject()->findChild<QQuickTextEdit*>();
QVERIFY(textEditObject);
// Delegate creation is deferred until focus in or cursor visibility is forced.
QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
@@ -3049,12 +3009,8 @@ the extent of the text, then they should ignore the keys.
*/
void tst_qquicktextedit::navigation()
{
- QQuickView window(testFileUrl("navigation.qml"));
- window.show();
- window.requestActivate();
-
- QVERIFY(window.rootObject() != nullptr);
-
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("navigation.qml")));
QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
QVERIFY(input != nullptr);
@@ -3200,13 +3156,8 @@ void tst_qquicktextedit::middleClickPaste()
if (!PlatformQuirks::isClipboardAvailable())
QSKIP("This machine doesn't support the clipboard");
- QQuickView window(testFileUrl("mouseselection_true.qml"));
-
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
-
- QVERIFY(window.rootObject() != nullptr);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("mouseselection_true.qml")));
QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(textEditObject != nullptr);
@@ -3236,9 +3187,8 @@ void tst_qquicktextedit::middleClickPaste()
void tst_qquicktextedit::readOnly()
{
- QQuickView window(testFileUrl("readOnly.qml"));
- window.show();
- window.requestActivate();
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("readOnly.qml")));
QVERIFY(window.rootObject() != nullptr);
@@ -3278,11 +3228,9 @@ void tst_qquicktextedit::inFlickableMouse()
QFETCH(int, expectFlickingAfter);
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
- QQuickView view(testFileUrl("inFlickable.qml"));
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
- QQuickFlickable *flick = qobject_cast<QQuickFlickable *>(view.rootObject());
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inFlickable.qml")));
+ QQuickFlickable *flick = qobject_cast<QQuickFlickable *>(window.rootObject());
QVERIFY(flick);
QQuickTextEdit *edit = flick->findChild<QQuickTextEdit*>("text");
QVERIFY(edit);
@@ -3291,7 +3239,7 @@ void tst_qquicktextedit::inFlickableMouse()
// flick with mouse
QPoint p(10, 100);
- QTest::mousePress(&view, Qt::LeftButton, {}, p);
+ QTest::mousePress(&window, Qt::LeftButton, {}, p);
QObject *pressGrabber = QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice())->firstPointExclusiveGrabber();
// even if TextEdit is readonly, it still grabs on press. But not if it's disabled.
if (enabled)
@@ -3302,14 +3250,14 @@ void tst_qquicktextedit::inFlickableMouse()
// after a couple of events, Flickable steals the grab and starts moving
for (; i < 4 && !flick->isMoving(); ++i) {
p -= QPoint(0, dragThreshold);
- QTest::mouseMove(&view, p);
+ QTest::mouseMove(&window, p);
}
QCOMPARE(flick->isMoving(), bool(expectFlickingAfter));
if (expectFlickingAfter) {
qCDebug(lcTests) << "flickable started moving after" << i << "moves, when we got to" << p;
QCOMPARE(i, expectFlickingAfter);
}
- QTest::mouseRelease(&view, Qt::LeftButton, {}, p);
+ QTest::mouseRelease(&window, Qt::LeftButton, {}, p);
}
void tst_qquicktextedit::inFlickableTouch_data()
@@ -3391,11 +3339,9 @@ bool tst_qquicktextedit::hasWindowActivation()
void tst_qquicktextedit::textInput()
{
- QQuickView view(testFileUrl("inputMethodEvent.qml"));
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
- QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inputMethodEvent.qml")));
+ QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(edit);
QVERIFY(edit->hasActiveFocus());
@@ -3444,11 +3390,9 @@ void tst_qquicktextedit::inputMethodUpdate()
QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
inputMethodPrivate->testContext = &platformInputContext;
- QQuickView view(testFileUrl("inputMethodEvent.qml"));
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
- QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inputMethodEvent.qml")));
+ QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(edit);
QVERIFY(edit->hasActiveFocus());
@@ -3534,12 +3478,10 @@ void tst_qquicktextedit::openInputPanel()
QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
inputMethodPrivate->testContext = &platformInputContext;
- QQuickView view(testFileUrl("openInputPanel.qml"));
- view.showNormal();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("openInputPanel.qml")));
- QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
+ QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(edit);
// check default values
@@ -3550,28 +3492,28 @@ void tst_qquicktextedit::openInputPanel()
QCOMPARE(qApp->inputMethod()->isVisible(), false);
// input panel should open on focus
- QPoint centerPoint(view.width()/2, view.height()/2);
+ QPoint centerPoint(window.width()/2, window.height()/2);
Qt::KeyboardModifiers noModifiers = Qt::NoModifier;
- QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
+ QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
QGuiApplication::processEvents();
QVERIFY(edit->hasActiveFocus());
QCOMPARE(qApp->focusObject(), edit);
QCOMPARE(qApp->inputMethod()->isVisible(), true);
- QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
+ QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
// input panel should be re-opened when pressing already focused TextEdit
qApp->inputMethod()->hide();
QCOMPARE(qApp->inputMethod()->isVisible(), false);
QVERIFY(edit->hasActiveFocus());
- QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
+ QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
QGuiApplication::processEvents();
QCOMPARE(qApp->inputMethod()->isVisible(), true);
- QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
+ QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
// input panel should stay visible if focus is lost to another text editor
QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
QQuickTextEdit anotherEdit;
- anotherEdit.setParentItem(view.rootObject());
+ anotherEdit.setParentItem(window.rootObject());
anotherEdit.setFocus(true);
QCOMPARE(qApp->inputMethod()->isVisible(), true);
QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherEdit));
@@ -3579,7 +3521,7 @@ void tst_qquicktextedit::openInputPanel()
anotherEdit.setFocus(false);
QVERIFY(qApp->focusObject() != &anotherEdit);
- QCOMPARE(view.activeFocusItem(), view.contentItem());
+ QCOMPARE(window.activeFocusItem(), window.contentItem());
anotherEdit.setFocus(true);
qApp->inputMethod()->hide();
@@ -3588,8 +3530,8 @@ void tst_qquicktextedit::openInputPanel()
edit->setReadOnly(true);
edit->setFocus(true);
QCOMPARE(qApp->inputMethod()->isVisible(), false);
- QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
- QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
+ QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
+ QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
QGuiApplication::processEvents();
QCOMPARE(qApp->inputMethod()->isVisible(), false);
@@ -3598,8 +3540,8 @@ void tst_qquicktextedit::openInputPanel()
edit->setFocus(false);
edit->setFocus(true);
QCOMPARE(qApp->inputMethod()->isVisible(), false);
- QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
- QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
+ QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
+ QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
QCOMPARE(qApp->inputMethod()->isVisible(), false);
inputMethodPrivate->testContext = nullptr;
@@ -3763,16 +3705,16 @@ void tst_qquicktextedit::largeTextObservesViewport_data()
// by default, the root item acts as the viewport:
// QQuickTextEdit doesn't populate lines of text beyond the bottom of the window
// cursor position 1000 is on line 121
- QTest::newRow("default plain text") << text << QQuickTextEdit::PlainText << false << 1000 << 0
- << 1 << 118 << 142 << 2400 << 3000;
+ QTest::newRow("default plain text") << text << QQuickTextEdit::PlainText << false << 1000 << 2
+ << 3 << 118 << 144 << 2150 << 3000;
// make the rectangle into a viewport item, and move the text upwards:
// QQuickTextEdit doesn't populate lines of text beyond the bottom of the viewport rectangle
QTest::newRow("clipped plain text") << text << QQuickTextEdit::PlainText << true << 1000 << 0
- << 1 << 123 << 137 << 2550 << 3000;
+ << 3 << 123 << 137 << 2200 << 3000;
// scroll backwards
QTest::newRow("scroll backwards in plain text") << text << QQuickTextEdit::PlainText << true << 1000 << 600
- << 1 << 93 << 108 << 1475 << 2300;
+ << 3 << 91 << 108 << 1475 << 2300;
{
QStringList lines;
@@ -3796,20 +3738,20 @@ void tst_qquicktextedit::largeTextObservesViewport_data()
// by default, the root item acts as the viewport:
// QQuickTextEdit doesn't populate blocks beyond the bottom of the window
QTest::newRow("default styled text") << text << QQuickTextEdit::RichText << false << 1000 << 0
- << 1 << 124 << 139 << 3900 << 4500;
+ << 3 << 122 << 139 << 3600 << 4500;
// make the rectangle into a viewport item, and move the text upwards:
// QQuickTextEdit doesn't populate blocks that don't intersect the viewport rectangle
QTest::newRow("clipped styled text") << text << QQuickTextEdit::RichText << true << 1000 << 0
- << 1 << 127 << 136 << 4000 << 4360;
+ << 3 << 127 << 136 << 3700 << 4360;
// get the "chapter 2" heading into the viewport
QTest::newRow("heading visible") << text << QQuickTextEdit::RichText << true << 800 << 0
- << 1 << 105 << 113 << 3300 << 3600;
+ << 3 << 105 << 113 << 3050 << 3600;
// get the "chapter 2" heading into the viewport, and then scroll backwards
QTest::newRow("scroll backwards") << text << QQuickTextEdit::RichText << true << 800 << 20
- << 1 << 104 << 113 << 3200 << 3600;
+ << 3 << 104 << 113 << 3000 << 3600;
// get the "chapter 2" heading into the viewport, and then scroll forwards
QTest::newRow("scroll forwards") << text << QQuickTextEdit::RichText << true << 800 << -50
- << 1 << 106 << 115 << 3300 << 3670;
+ << 3 << 106 << 115 << 3000 << 3670;
}
void tst_qquicktextedit::largeTextObservesViewport()
@@ -3881,14 +3823,49 @@ void tst_qquicktextedit::largeTextObservesViewport()
QCOMPARE(textPriv->cursorItem->isVisible(), textPriv->renderedRegion.intersects(textItem->cursorRectangle()));
}
+void tst_qquicktextedit::largeTextSelection()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("qtConfigureHelp.qml")));
+ NodeCheckerTextEdit *textItem = qmlobject_cast<NodeCheckerTextEdit *>(window.rootObject());
+ QVERIFY(textItem);
+ QTRY_VERIFY(textItem->sortedLinePositions.size() > 0);
+ const auto sortedLinePositions = textItem->sortedLinePositions;
+
+ QQuickTextEditPrivate *textPriv = QQuickTextEditPrivate::get(textItem);
+ QSignalSpy renderSpy(&window, &QQuickWindow::afterRendering);
+ if (lcTests().isDebugEnabled())
+ QTest::qWait(500); // for visual check; not needed in CI
+
+ const int renderCount = renderSpy.size();
+ textItem->setCursorPosition(200);
+ textItem->moveCursorSelection(220);
+ QTRY_COMPARE_GT(renderSpy.size(), renderCount);
+
+ if (lcTests().isDebugEnabled())
+ QTest::qWait(500); // for visual check; not needed in CI
+
+ qCDebug(lcTests) << "TextEdit's nodes" << textPriv->textNodeMap;
+ qCDebug(lcTests) << "font" << textItem->font() << "line positions"
+ << textItem->sortedLinePositions << "expected" << sortedLinePositions;
+
+ const bool eachTextNodeRenderedOnlyOnce = [textItem]() -> bool {
+ for (auto i = 1; i < textItem->sortedLinePositions.count(); ++i)
+ if (textItem->sortedLinePositions[i - 1] == textItem->sortedLinePositions[i])
+ return false;
+ return true;
+ }();
+ QVERIFY(eachTextNodeRenderedOnlyOnce);
+}
+
void tst_qquicktextedit::renderingAroundSelection()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("threeLines.qml")));
NodeCheckerTextEdit *textItem = qmlobject_cast<NodeCheckerTextEdit*>(window.rootObject());
QVERIFY(textItem);
- QTRY_VERIFY(textItem->linePositions.size() > 0);
- const auto linePositions = textItem->linePositions;
+ QTRY_VERIFY(textItem->sortedLinePositions.size() > 0);
+ const auto sortedLinePositions = textItem->sortedLinePositions;
const int lastLinePosition = textItem->lastLinePosition;
QQuickTextEditPrivate *textPriv = QQuickTextEditPrivate::get(textItem);
QSignalSpy renderSpy(&window, &QQuickWindow::afterRendering);
@@ -3910,27 +3887,120 @@ void tst_qquicktextedit::renderingAroundSelection()
QTest::qWait(500); // for visual check; not needed in CI
qCDebug(lcTests) << "TextEdit's nodes" << textPriv->textNodeMap;
- qCDebug(lcTests) << "font" << textItem->font() << "line positions" << textItem->linePositions << "should be" << linePositions;
+ qCDebug(lcTests) << "font" << textItem->font() << "line positions" << textItem->sortedLinePositions << "should be" << sortedLinePositions;
QCOMPARE(textItem->lastLinePosition, lastLinePosition);
- QTRY_COMPARE(textItem->linePositions, linePositions);
+ QTRY_COMPARE(textItem->sortedLinePositions, sortedLinePositions);
}
-void tst_qquicktextedit::signal_editingfinished()
-{
- QQuickView *window = new QQuickView(nullptr);
- window->setBaseSize(QSize(800,600));
+struct OffsetAndExpectedBlocks {
+ int tableIndex; // which nested frame
+ qreal tableOffset; // fraction of that frame's height to scroll to
+ int minExpectedBlockCount;
- window->setSource(testFileUrl("signal_editingfinished.qml"));
- window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
- QCOMPARE(QGuiApplication::focusWindow(), window);
+ OffsetAndExpectedBlocks(int i, qreal o, int c)
+ : tableIndex(i), tableOffset(o), minExpectedBlockCount(c) {}
+};
- QVERIFY(window->rootObject() != nullptr);
+typedef QList<OffsetAndExpectedBlocks> OffsetAndExpectedBlocksList;
+
+void tst_qquicktextedit::largeTextTables_data()
+{
+ QTest::addColumn<int>("tables");
+ QTest::addColumn<int>("tableCols");
+ QTest::addColumn<int>("tableRows");
+ QTest::addColumn<OffsetAndExpectedBlocksList>("steps");
+
+ QTest::newRow("one big table") << 1 << 3 << 70
+ << OffsetAndExpectedBlocksList{
+ OffsetAndExpectedBlocks(1, 0.75, 150),
+ OffsetAndExpectedBlocks(1, 0.5, 150)};
+ QTest::newRow("short tables") << 5 << 3 << 10
+ << OffsetAndExpectedBlocksList{
+ OffsetAndExpectedBlocks(4, 0.75, 35),
+ OffsetAndExpectedBlocks(3, 0.25, 50),
+ OffsetAndExpectedBlocks(2, 0.75, 50)};
+}
+
+void tst_qquicktextedit::largeTextTables() // QTBUG-118636
+{
+ QFETCH(int, tables);
+ QFETCH(int, tableCols);
+ QFETCH(int, tableRows);
+ QFETCH(OffsetAndExpectedBlocksList, steps);
+
+ QStringList lines;
+
+ lines << QLatin1String("<h1>") + QTest::currentDataTag() + "</h1>";
+ for (int t = 0; t < tables; ++t) {
+ if (t > 0)
+ lines << QString("<p>table %1</p>").arg(t);
+ lines << "<table border='1'>";
+ for (int r = 0; r < tableRows; ++r) {
+ lines << " <tr>";
+ for (int c = 0; c < tableCols; ++c)
+ lines << QString(" <td>table %1 cell %2, %3</td>").arg(t).arg(c).arg(r);
+ lines << " </tr>";
+ }
+ lines << "</table>";
+ }
+ lines << "<p>here endeth the tables</p>";
+ QString html = lines.join('\n');
+
+#ifdef DEBUG_WRITE_INPUT
+ QFile f(QLatin1String("/tmp/") + QTest::currentDataTag() + ".html");
+ f.open(QFile::WriteOnly);
+ f.write(html.toUtf8());
+ f.close();
+#endif
- QQuickTextEdit *input1 = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(window->rootObject()->property("input1")));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inFlickable.qml")));
+ QQuickFlickable *flick = qmlobject_cast<QQuickFlickable *>(window.rootObject());
+ QVERIFY(flick);
+ QQuickTextEdit *te = window.rootObject()->findChild<QQuickTextEdit *>();
+ QVERIFY(te);
+ auto *tePriv = QQuickTextEditPrivate::get(te);
+ auto font = te->font();
+ font.setPixelSize(10);
+ te->setFont(font);
+
+ te->setTextFormat(QQuickTextEdit::RichText);
+ te->setText(html);
+ te->setFlag(QQuickItem::ItemObservesViewport); // this isn't "large text", but test viewporting anyway
+
+ QTextDocument *doc = te->textDocument()->textDocument();
+ QList<QTextFrame *> frames = doc->rootFrame()->childFrames();
+ frames.prepend(doc->rootFrame());
+ qCDebug(lcTests) << "blocks" << doc->blockCount() << "chars" << doc->characterCount() << "frames" << frames;
+
+ for (const OffsetAndExpectedBlocks &oeb : steps) {
+ QCOMPARE_GT(frames.size(), oeb.tableIndex);
+ const QTextFrame *textFrame = frames.at(oeb.tableIndex);
+ const QTextCursor top = textFrame->firstCursorPosition();
+ const qreal yTop = te->positionToRectangle(top.position()).top();
+ const QTextCursor bottom = textFrame->lastCursorPosition();
+ const qreal yBottom = te->positionToRectangle(bottom.position()).bottom();
+ const qreal y = yTop + (yBottom - yTop) * oeb.tableOffset;
+ qCDebug(lcTests) << "frame" << textFrame << "goes from pos" << top.position() << "y" << yTop
+ << "to pos" << bottom.position() << "y" << yBottom << "; scrolling to" << y
+ << "which is at" << oeb.tableOffset << "of table height" << (yBottom - yTop);
+ flick->setContentY(y);
+ qCDebug(lcTests) << tePriv->renderedRegion << "rendered blocks" << tePriv->renderedBlockCount << ":"
+ << tePriv->firstBlockInViewport << "to" << tePriv->firstBlockPastViewport;
+ QTRY_COMPARE_GE(tePriv->renderedBlockCount, oeb.minExpectedBlockCount);
+ }
+}
+
+void tst_qquicktextedit::signal_editingfinished()
+{
+ QQuickView window;
+ window.setBaseSize(QSize(800, 600));
+ QVERIFY(QQuickTest::showView(window, testFileUrl("signal_editingfinished.qml")));
+ QCOMPARE(QGuiApplication::focusWindow(), &window);
+
+ QQuickTextEdit *input1 = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(window.rootObject()->property("input1")));
QVERIFY(input1);
- QQuickTextEdit *input2 = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(window->rootObject()->property("input2")));
+ QQuickTextEdit *input2 = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(window.rootObject()->property("input2")));
QVERIFY(input2);
QSignalSpy editingFinished1Spy(input1, SIGNAL(editingFinished()));
@@ -3941,7 +4011,7 @@ void tst_qquicktextedit::signal_editingfinished()
{
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
- QGuiApplication::sendEvent(window, &key);
+ QGuiApplication::sendEvent(&window, &key);
QVERIFY(key.isAccepted());
QTRY_COMPARE(editingFinished1Spy.size(), 1);
@@ -3957,7 +4027,7 @@ void tst_qquicktextedit::signal_editingfinished()
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
- QGuiApplication::sendEvent(window, &key);
+ QGuiApplication::sendEvent(&window, &key);
QVERIFY(key.isAccepted());
QTRY_COMPARE(editingFinished2Spy.size(), 1);
@@ -4139,12 +4209,10 @@ void tst_qquicktextedit::preeditCursorRectangle()
{
QString preeditText = "super";
- QQuickView view(testFileUrl("inputMethodEvent.qml"));
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inputMethodEvent.qml")));
- QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
+ QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(edit);
QQuickItem *cursor = edit->findChild<QQuickItem *>("cursor");
@@ -4222,12 +4290,10 @@ void tst_qquicktextedit::inputMethodComposing()
{
QString text = "supercalifragisiticexpialidocious!";
- QQuickView view(testFileUrl("inputContext.qml"));
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("inputContext.qml")));
- QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
+ QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(edit);
QCOMPARE(QGuiApplication::focusObject(), static_cast<QObject *>(edit));
@@ -4338,11 +4404,11 @@ void tst_qquicktextedit::cursorRectangleSize()
{
QFETCH(bool, useCursorDelegate);
- QQuickView *window = new QQuickView(testFileUrl("positionAt.qml"));
- QVERIFY(window->rootObject() != nullptr);
- QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window->rootObject());
+ QQuickView window(testFileUrl("positionAt.qml"));
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
+ QVERIFY(textEdit);
- QQmlComponent cursorDelegate(window->engine());
+ QQmlComponent cursorDelegate(window.engine());
if (useCursorDelegate) {
cursorDelegate.setData("import QtQuick 2.0\nRectangle { width:10; height:10; }", QUrl());
textEdit->setCursorDelegate(&cursorDelegate);
@@ -4354,9 +4420,9 @@ void tst_qquicktextedit::cursorRectangleSize()
textEdit->setCursorPosition(3);
QVERIFY(textEdit != nullptr);
textEdit->setFocus(true);
- window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ window.show();
+ window.requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(&window));
QInputMethodQueryEvent event(Qt::ImCursorRectangle);
qApp->sendEvent(textEdit, &event);
@@ -4381,8 +4447,6 @@ void tst_qquicktextedit::cursorRectangleSize()
// input panel cursorRectangle property and tranformed item cursor rectangle match
QRectF sceneCursorRect = QQuickItemPrivate::get(textEdit)->itemToWindowTransform().mapRect(cursorRectFromItem);
QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
-
- delete window;
}
void tst_qquicktextedit::getText_data()
@@ -5567,17 +5631,9 @@ void tst_qquicktextedit::undo()
QFETCH(IntList, insertMode);
QFETCH(QStringList, expectedString);
- QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
- QQmlComponent textEditComponent(&engine);
- textEditComponent.setData(componentStr.toLatin1(), QUrl());
- QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
- QVERIFY(textEdit != nullptr);
-
- QQuickWindow window;
- textEdit->setParentItem(window.contentItem());
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("focusByDefault.qml")));
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(textEdit->hasActiveFocus());
QVERIFY(!textEdit->canUndo());
@@ -5653,17 +5709,10 @@ void tst_qquicktextedit::redo()
QFETCH(IntList, insertIndex);
QFETCH(QStringList, expectedString);
- QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
- QQmlComponent textEditComponent(&engine);
- textEditComponent.setData(componentStr.toLatin1(), QUrl());
- QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
- QVERIFY(textEdit != nullptr);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("focusByDefault.qml")));
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
- QQuickWindow window;
- textEdit->setParentItem(window.contentItem());
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
QVERIFY(textEdit->hasActiveFocus());
QVERIFY(!textEdit->canUndo());
@@ -5876,17 +5925,10 @@ void tst_qquicktextedit::undo_keypressevents()
QFETCH(KeyList, keys);
QFETCH(QStringList, expectedString);
- QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
- QQmlComponent textEditComponent(&engine);
- textEditComponent.setData(componentStr.toLatin1(), QUrl());
- QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
- QVERIFY(textEdit != nullptr);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("focusByDefault.qml")));
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
- QQuickWindow window;
- textEdit->setParentItem(window.contentItem());
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
QVERIFY(textEdit->hasActiveFocus());
simulateKeys(&window, keys);
@@ -5902,17 +5944,9 @@ void tst_qquicktextedit::undo_keypressevents()
void tst_qquicktextedit::clear()
{
- QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
- QQmlComponent textEditComponent(&engine);
- textEditComponent.setData(componentStr.toLatin1(), QUrl());
- QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
- QVERIFY(textEdit != nullptr);
-
- QQuickWindow window;
- textEdit->setParentItem(window.contentItem());
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("focusByDefault.qml")));
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(window.rootObject());
QVERIFY(textEdit->hasActiveFocus());
QSignalSpy spy(textEdit, SIGNAL(canUndoChanged()));
@@ -5993,11 +6027,11 @@ void tst_qquicktextedit::embeddedImages_data()
QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
<< testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML TextEdit: Cannot open: " + testFileUrl("http/notexists.png").toString();
- QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
+ QTest::newRow("local-relative") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
<< testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML TextEdit: Error transferring {{ServerBaseUrl}}/notexists.png - server replied: Not found";
- QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
+ QTest::newRow("remote-relative") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
}
void tst_qquicktextedit::embeddedImages()
@@ -6015,40 +6049,93 @@ void tst_qquicktextedit::embeddedImages()
QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
QQmlComponent textComponent(&engine, qmlfile);
- QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.beginCreate(engine.rootContext()));
- QVERIFY(textObject != nullptr);
+ QScopedPointer<QQuickTextEdit> textObject(qobject_cast<QQuickTextEdit*>(textComponent.beginCreate(engine.rootContext())));
+ QVERIFY(!textObject.isNull());
const int baseUrlPropertyIndex = textObject->metaObject()->indexOfProperty("serverBaseUrl");
if (baseUrlPropertyIndex != -1) {
QMetaProperty prop = textObject->metaObject()->property(baseUrlPropertyIndex);
- QVERIFY(prop.write(textObject, server.baseUrl().toString()));
+ QVERIFY(prop.write(textObject.get(), server.baseUrl().toString()));
}
textComponent.completeCreate();
- QTRY_COMPARE(QQuickTextEditPrivate::get(textObject)->document->resourcesLoading(), 0);
+ QTRY_COMPARE(textObject->resourcesLoading(), 0);
QPixmap pm(testFile("http/exists.png"));
if (error.isEmpty()) {
- QCOMPARE(textObject->width(), double(pm.width()));
- QCOMPARE(textObject->height(), double(pm.height()));
+ QCOMPARE(textObject->width(), pm.width());
+ QCOMPARE(textObject->height(), pm.height());
} else {
QVERIFY(16 != pm.width()); // check test is effective
- QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
- QCOMPARE(textObject->height(), 16.0);
+ QCOMPARE(textObject->width(), 16); // default size of QTextDocument broken image icon
+ QCOMPARE(textObject->height(), 16);
}
- delete textObject;
+ // QTextDocument images are cached in QTextDocumentPrivate::cachedResources,
+ // so verify that we don't redundantly cache them in QQuickPixmapCache
+ QCOMPARE(QQuickPixmapCache::instance()->m_cache.size(), 0);
}
-void tst_qquicktextedit::emptytags_QTBUG_22058()
+void tst_qquicktextedit::remoteImagesInDocumentSource()
{
- QQuickView window(testFileUrl("qtbug-22058.qml"));
- QVERIFY(window.rootObject() != nullptr);
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ server.serveDirectory(testFile("http"));
+ server.serveDirectory(testFile("httpfail"), TestHTTPServer::Disconnect);
+ server.serveDirectory(testFile("httpslow"), TestHTTPServer::Delay);
- window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ QString tmpPath = tmpDir.filePath("multipleRemoteImages.md");
+ QByteArray markdownBuf;
+ {
+ QFile sf(QQmlFile::urlToLocalFileOrQrc(testFileUrl("multipleRemoteImages.md")));
+ QVERIFY(sf.open(QIODeviceBase::ReadOnly));
+ markdownBuf = sf.readAll();
+ qCDebug(lcTests) << sf.fileName() << "->" << tmpPath
+ << "s/serverBaseUrl/" << server.baseUrl().toString()
+ << "/ in markdown: size" << markdownBuf.size();
+ }
+ markdownBuf.replace("serverBaseUrl", server.baseUrl().toString().toLocal8Bit());
+ {
+ QFile of(tmpPath);
+ QVERIFY(of.open(QIODeviceBase::WriteOnly));
+ QCOMPARE(of.write(markdownBuf), markdownBuf.size());
+ }
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("textEdit.qml")));
+ auto *textEdit = qmlobject_cast<QQuickTextEdit *>(window.rootObject());
+ QVERIFY(textEdit);
+ QQuickTextEditPrivate *priv = QQuickTextEditPrivate::get(textEdit);
+ QSignalSpy implicitHeightChangedSpy(textEdit, &QQuickTextEdit::implicitHeightChanged);
+
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Protocol \"gopher\" is unknown"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Connection closed")); // httpfail/warning.png
+ textEdit->setTextFormat(QQuickTextEdit::MarkdownText);
+ textEdit->textDocument()->setSource(QUrl::fromLocalFile(tmpPath));
+
+ // the document gets loaded first, then the resources
+ QTRY_COMPARE(textEdit->textDocument()->status(), QQuickTextDocument::Status::Loaded);
+ const qreal implicitHeight = textEdit->implicitHeight();
+
+ // all resource-loading jobs complete or fail eventually
+ QTRY_COMPARE(priv->pixmapsInProgress.size(), 0);
+
+ // after httpslow/turtle.svg loads, implicitHeight increases
+ QCOMPARE(implicitHeightChangedSpy.size(), 2);
+ QCOMPARE_GT(textEdit->implicitHeight(), implicitHeight);
+
+ // QTextDocument images are cached in QTextDocumentPrivate::cachedResources,
+ // so verify that we don't redundantly cache them in QQuickPixmapCache
+ QCOMPARE(QQuickPixmapCache::instance()->m_cache.size(), 0);
+}
+
+void tst_qquicktextedit::emptytags_QTBUG_22058()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("qtbug-22058.qml")));
QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(window.rootObject()->property("inputField")));
QVERIFY(input->hasActiveFocus());
@@ -6235,31 +6322,25 @@ void tst_qquicktextedit::QTBUG_51115_readOnlyResetsSelection()
void tst_qquicktextedit::keys_shortcutoverride()
{
// Tests that QML TextEdit receives Keys.onShortcutOverride (QTBUG-68711)
- QQuickView view;
- view.setSource(testFileUrl("keys_shortcutoverride.qml"));
- view.show();
- view.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&view));
- QObject *root = view.rootObject();
- QVERIFY(root);
-
- QQuickTextEdit *textEdit = root->findChild<QQuickTextEdit*>();
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("keys_shortcutoverride.qml")));
+ QQuickTextEdit *textEdit = window.rootObject()->findChild<QQuickTextEdit*>();
QVERIFY(textEdit);
- QQuickRectangle *rectangle = root->findChild<QQuickRectangle*>(QLatin1String("rectangle"));
+ QQuickRectangle *rectangle = window.rootObject()->findChild<QQuickRectangle*>(QLatin1String("rectangle"));
QVERIFY(rectangle);
// Precondition: check if its not already changed
- QCOMPARE(root->property("who").value<QString>(), QLatin1String("nobody"));
+ QCOMPARE(window.rootObject()->property("who").value<QString>(), QLatin1String("nobody"));
// send Key_Escape to the Rectangle
QVERIFY(rectangle->hasActiveFocus());
- QTest::keyPress(&view, Qt::Key_Escape);
- QCOMPARE(root->property("who").value<QString>(), QLatin1String("Rectangle"));
+ QTest::keyPress(&window, Qt::Key_Escape);
+ QCOMPARE(window.rootObject()->property("who").value<QString>(), QLatin1String("Rectangle"));
// send Key_Escape to TextEdit
textEdit->setFocus(true);
- QTest::keyPress(&view, Qt::Key_Escape);
- QCOMPARE(root->property("who").value<QString>(), QLatin1String("TextEdit"));
+ QTest::keyPress(&window, Qt::Key_Escape);
+ QCOMPARE(window.rootObject()->property("who").value<QString>(), QLatin1String("TextEdit"));
}
void tst_qquicktextedit::transparentSelectionColor()
@@ -6267,18 +6348,13 @@ void tst_qquicktextedit::transparentSelectionColor()
if (QGuiApplication::platformName() == QLatin1String("minimal"))
QSKIP("Skipping due to grabWindow not functional on minimal platforms");
- QQuickView view;
- view.setSource(testFileUrl("transparentSelectionColor.qml"));
- view.show();
- QVERIFY(QTest::qWaitForWindowExposed(&view));
- QObject *root = view.rootObject();
- QVERIFY(root);
-
- QQuickTextEdit *textEdit = root->findChild<QQuickTextEdit *>();
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("transparentSelectionColor.qml")));
+ QQuickTextEdit *textEdit = window.rootObject()->findChild<QQuickTextEdit *>();
QVERIFY(textEdit);
textEdit->selectAll();
- QImage img = view.grabWindow();
+ QImage img = window.grabWindow();
QCOMPARE(img.isNull(), false);
QColor color = img.pixelColor(int(textEdit->width() / 2), int(textEdit->height()) / 2);
@@ -6289,11 +6365,9 @@ void tst_qquicktextedit::transparentSelectionColor()
void tst_qquicktextedit::keyEventPropagation()
{
- QQuickView view;
- view.setSource(testFileUrl("keyEventPropagation.qml"));
- view.show();
- QVERIFY(QTest::qWaitForWindowExposed(&view));
- QObject *root = view.rootObject();
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("keyEventPropagation.qml")));
+ QObject *root = window.rootObject();
QVERIFY(root);
QSignalSpy downSpy(root, SIGNAL(keyDown(int)));
@@ -6301,7 +6375,7 @@ void tst_qquicktextedit::keyEventPropagation()
QQuickTextEdit *textEdit = root->findChild<QQuickTextEdit *>();
QVERIFY(textEdit->hasActiveFocus());
- simulateKey(&view, Qt::Key_Back);
+ simulateKey(&window, Qt::Key_Back);
QCOMPARE(downSpy.size(), 1);
QCOMPARE(upSpy.size(), 1);
auto downKey = downSpy.takeFirst();
@@ -6309,7 +6383,7 @@ void tst_qquicktextedit::keyEventPropagation()
QCOMPARE(downKey.at(0).toInt(), Qt::Key_Back);
QCOMPARE(upKey.at(0).toInt(), Qt::Key_Back);
- simulateKey(&view, Qt::Key_Shift);
+ simulateKey(&window, Qt::Key_Shift);
QCOMPARE(downSpy.size(), 1);
QCOMPARE(upSpy.size(), 1);
downKey = downSpy.takeFirst();
@@ -6317,11 +6391,11 @@ void tst_qquicktextedit::keyEventPropagation()
QCOMPARE(downKey.at(0).toInt(), Qt::Key_Shift);
QCOMPARE(upKey.at(0).toInt(), Qt::Key_Shift);
- simulateKey(&view, Qt::Key_A);
+ simulateKey(&window, Qt::Key_A);
QCOMPARE(downSpy.size(), 0);
QCOMPARE(upSpy.size(), 0);
- simulateKey(&view, Qt::Key_Right);
+ simulateKey(&window, Qt::Key_Right);
QCOMPARE(downSpy.size(), 0);
QCOMPARE(upSpy.size(), 1);
upKey = upSpy.takeFirst();
@@ -6488,6 +6562,169 @@ void tst_qquicktextedit::touchscreenSetsFocusAndMovesCursor()
QVERIFY(top->selectedText().isEmpty());
}
+void tst_qquicktextedit::longPressInputMethod() // QTBUG-115004
+{
+ QQuickView window;
+ window.setMinimumWidth(200);
+ window.setMinimumHeight(100);
+ QVERIFY(QQuickTest::showView(window, testFileUrl("positionAt.qml")));
+ QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(window.rootObject());
+ QVERIFY(edit);
+
+ // Realistically there are touch events. But QQuickTextEdit doesn't handle them yet;
+ // so we only test the synth-mouse events for now.
+ QPoint pos = edit->positionToRectangle(20).center().toPoint(); // in the word "pi|ece"
+ QTest::mousePress(&window, Qt::LeftButton, {}, pos);
+
+ // Simulate input method events as seen on Android during long-press
+ {
+ QInputMethodEvent imEvent({}, QList<QInputMethodEvent::Attribute>()
+ << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 20, 0, {}));
+ QCoreApplication::sendEvent(edit, &imEvent);
+ }
+ {
+ QInputMethodEvent imEvent({}, QList<QInputMethodEvent::Attribute>()
+ << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, {})
+ << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 13, -5, {}));
+ QCoreApplication::sendEvent(edit, &imEvent);
+ }
+
+ // Release later => long press
+ QTest::mouseRelease(&window, Qt::LeftButton, {}, pos, 1500);
+
+ QTRY_COMPARE(edit->selectedText(), "piece");
+}
+
+void tst_qquicktextedit::rtlAlignmentInColumnLayout_QTBUG_112858()
+{
+ QQuickView window(testFileUrl("qtbug-112858.qml"));
+ QVERIFY(window.rootObject() != nullptr);
+
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickTextEdit *edit = window.rootObject()->findChild<QQuickTextEdit *>();
+ QVERIFY(edit != nullptr);
+
+ const auto text = edit->text();
+ const auto lines = text.split("\n");
+ QCOMPARE(lines.size(), edit->lineCount());
+
+ int currentLineStartPos = 0;
+ QRectF firstLineStartPosRect;
+
+ // check that all lines are aligned, for RTL text it means that they have the same pos at the right
+ for (int i = 0; i < lines.size(); ++i) {
+ const auto lineStartPosRect = edit->positionToRectangle(currentLineStartPos);
+ QVERIFY(lineStartPosRect.isValid());
+
+ if (i == 0)
+ firstLineStartPosRect = lineStartPosRect;
+ else
+ QCOMPARE(lineStartPosRect.right(), firstLineStartPosRect.right());
+
+ currentLineStartPos += lines.at(i).size() + 1;
+ }
+}
+
+void tst_qquicktextedit::fontManipulationWithCursorSelection()
+{
+ QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
+ QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
+ QQmlComponent texteditComponent(&engine);
+ texteditComponent.setData(componentStr.toLatin1(), QUrl());
+ QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(texteditComponent.create());
+ QVERIFY(textEditObject != nullptr);
+
+ const int originalStartPos = 0;
+ const int originalEndPos = (testStr.size() - 1) / 2;
+
+ textEditObject->select(originalStartPos, originalEndPos);
+ QCOMPARE(textEditObject->selectionStart(), originalStartPos);
+ QCOMPARE(textEditObject->selectionEnd(), originalEndPos);
+
+ QCOMPARE(textEditObject->cursorSelection()->text(), textEditObject->text().mid(originalStartPos, originalEndPos));
+
+ // test font manipulation
+ QFont font = textEditObject->cursorSelection()->font();
+ QVERIFY(!font.bold());
+ font.setBold(true);
+ textEditObject->cursorSelection()->setFont(font);
+ QVERIFY(textEditObject->cursorSelection()->font().bold());
+
+ // test color manipulation
+ QCOMPARE_NE(textEditObject->cursorSelection()->color(), QColorConstants::Cyan);
+ textEditObject->cursorSelection()->setColor(QColorConstants::Cyan);
+ QCOMPARE(textEditObject->cursorSelection()->color(), QColorConstants::Cyan);
+
+ // test alignment
+ QCOMPARE(textEditObject->cursorSelection()->alignment(), Qt::AlignLeft);
+ textEditObject->cursorSelection()->setAlignment(Qt::AlignRight);
+ QCOMPARE(textEditObject->cursorSelection()->alignment(), Qt::AlignRight);
+
+ // change seleciton and verify that we don't keep the same formatting
+ const int newStartPos = testStr.size() / 2;
+ const int newEndPos = testStr.size() - 1;
+
+ textEditObject->select(newStartPos, newEndPos);
+ QCOMPARE(textEditObject->selectionStart(), newStartPos);
+ QCOMPARE(textEditObject->selectionEnd(), newEndPos);
+ QVERIFY(!textEditObject->cursorSelection()->font().bold());
+ QCOMPARE_NE(textEditObject->cursorSelection()->color(), QColorConstants::Cyan);
+ QEXPECT_FAIL("", "The text alignment doesn't update when changing selection", Continue);
+ QCOMPARE(textEditObject->cursorSelection()->alignment(), Qt::AlignLeft);
+
+ // change back to the previous fragment, and verify that we have the old formatting
+ textEditObject->select(originalStartPos, originalEndPos);
+ QVERIFY(font.bold());
+ QCOMPARE(textEditObject->cursorSelection()->color(), QColorConstants::Cyan);
+ QCOMPARE(textEditObject->cursorSelection()->alignment(), Qt::AlignRight);
+
+ // test text manipulation
+ textEditObject->cursorSelection()->setText("Q");
+ QEXPECT_FAIL("", "QQuickTextSelection::text doesn't currently work correctly", Continue);
+ QCOMPARE(textEditObject->text(), QLatin1String("Q%1").arg(testStr.mid(newStartPos, newEndPos)));
+
+ // Make sure that QQuickTextEdit::setFont() affects all blocks
+ font.setItalic(true);
+ font.setWeight(QFont::Black);
+ textEditObject->setFont(font);
+ const auto *doc = textEditObject->textDocument()->textDocument();
+ for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next())
+ QCOMPARE(block.charFormat().font(), font);
+}
+
+void tst_qquicktextedit::resizeTextEditPolish()
+{
+ QQuickView window(testFileUrl("resizeTextEditPolish.qml"));
+ QVERIFY(window.rootObject() != nullptr);
+
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ auto *edit = window.rootObject()->findChild<QQuickTextEdit *>();
+ QVERIFY(edit != nullptr);
+ QCOMPARE(edit->lineCount(), 1);
+
+ QSignalSpy spy(edit, SIGNAL(lineCountChanged()));
+
+ // Resize item and check for item polished
+ auto *item = edit->parentItem();
+ item->setWidth(item->width() - (item->width() / 2));
+
+ QVERIFY(QQuickTest::qIsPolishScheduled(edit));
+ QVERIFY(QQuickTest::qWaitForPolish(edit));
+
+ QTRY_COMPARE(spy.size(), 1);
+ QVERIFY(edit->lineCount() > 1);
+ QCOMPARE(edit->state(), QString("multi-line"));
+ auto *editPriv = QQuickTextEditPrivate::get(edit);
+ QCOMPARE(editPriv->xoff, 0);
+ QCOMPARE(editPriv->yoff, 0);
+}
+
+QT_END_NAMESPACE
+
QTEST_MAIN(tst_qquicktextedit)
#include "tst_qquicktextedit.moc"
diff --git a/tests/auto/quick/qquicktextinput/BLACKLIST b/tests/auto/quick/qquicktextinput/BLACKLIST
index 4d03982f73..2af586c68a 100644
--- a/tests/auto/quick/qquicktextinput/BLACKLIST
+++ b/tests/auto/quick/qquicktextinput/BLACKLIST
@@ -2,14 +2,6 @@
[mouseSelectionMode]
opensuse-leap
-# QTBUG-99150
-[copyAndPaste]
-opensuse-leap
-
-# QTBUG-99150
-[canPaste]
-opensuse-leap
-
# QTBUG-82058
[setInputMask]
macos ci
@@ -18,22 +10,6 @@ macos ci
[passwordCharacter]
ci b2qt 32bit
-# QTBUG-95940
-[canPaste]
-opensuse-leap
-
-# QTBUG-95940
-[canPasteEmpty]
-opensuse-leap
-
-# QTBUG-95940
-[copyAndPaste]
-opensuse-leap
-
-# QTBUG-95940
-[copyAndPasteKeySequence]
-opensuse-leap
-
# QTBUG-103256
[copyAndPasteKeySequence]
android
diff --git a/tests/auto/quick/qquicktextinput/CMakeLists.txt b/tests/auto/quick/qquicktextinput/CMakeLists.txt
index 716d7b371b..70003a63b8 100644
--- a/tests/auto/quick/qquicktextinput/CMakeLists.txt
+++ b/tests/auto/quick/qquicktextinput/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktextinput Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktextinput LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
index 267b9c10a0..b7e689e147 100644
--- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -1877,7 +1878,7 @@ void tst_qquicktextinput::maxLength()
QVERIFY(textinputObject != nullptr);
QVERIFY(textinputObject->text().isEmpty());
QCOMPARE(textinputObject->maxLength(), 10);
- foreach (const QString &str, standard) {
+ for (const QString &str : std::as_const(standard)) {
QVERIFY(textinputObject->text().size() <= 10);
textinputObject->setText(str);
QVERIFY(textinputObject->text().size() <= 10);
@@ -2757,15 +2758,18 @@ void tst_qquicktextinput::copyAndPasteKeySequence()
#if QT_CONFIG(clipboard) && QT_CONFIG(shortcut)
void tst_qquicktextinput::canPasteEmpty()
{
+ if (!PlatformQuirks::isClipboardAvailable())
+ QSKIP("This machine has no clipboard support.");
+
QGuiApplication::clipboard()->clear();
- QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
+ const QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
QQmlComponent textInputComponent(&engine);
textInputComponent.setData(componentStr.toLatin1(), QUrl());
QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
QVERIFY(textInput != nullptr);
- bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().size() != 0;
+ const bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().size() != 0;
QCOMPARE(textInput->canPaste(), cp);
}
#endif
@@ -2773,15 +2777,18 @@ void tst_qquicktextinput::canPasteEmpty()
#if QT_CONFIG(clipboard) && QT_CONFIG(shortcut)
void tst_qquicktextinput::canPaste()
{
+ if (!PlatformQuirks::isClipboardAvailable())
+ QSKIP("This machine has no clipboard support.");
+
QGuiApplication::clipboard()->setText("Some text");
- QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
+ const QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
QQmlComponent textInputComponent(&engine);
textInputComponent.setData(componentStr.toLatin1(), QUrl());
QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
QVERIFY(textInput != nullptr);
- bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().size() != 0;
+ const bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().size() != 0;
QCOMPARE(textInput->canPaste(), cp);
}
#endif
@@ -2790,7 +2797,7 @@ void tst_qquicktextinput::canPaste()
void tst_qquicktextinput::middleClickPaste()
{
if (!PlatformQuirks::isClipboardAvailable())
- QSKIP("This machine doesn't support the clipboard");
+ QSKIP("This machine has no clipboard support.");
QQuickView window(testFileUrl("mouseselectionmode_default.qml"));
@@ -2805,8 +2812,8 @@ void tst_qquicktextinput::middleClickPaste()
textInputObject->setFocus(true);
- QString originalText = textInputObject->text();
- QString selectedText = "234567";
+ const QString originalText = textInputObject->text();
+ const QString selectedText = "234567";
// press-and-drag-and-release from x1 to x2
const QPoint p1 = textInputObject->positionToRectangle(2).center().toPoint();
@@ -6427,7 +6434,7 @@ void tst_qquicktextinput::setInputMask()
// inputMaskChanged signal
QString unescapedMask = mask; // mask is escaped, because '\' is also escape in a JS string
unescapedMask.replace(QLatin1String("\\\\"), QLatin1String("\\")); // simple unescape
- QSignalSpy spy(textInput, SIGNAL(inputMaskChanged(const QString &)));
+ QSignalSpy spy(textInput, SIGNAL(inputMaskChanged(QString)));
textInput->setInputMask(unescapedMask);
QCOMPARE(spy.size(), 0);
@@ -7176,6 +7183,20 @@ void tst_qquicktextinput::touchscreenDoesNotSelect()
QTest::touchEvent(&window, touchscreen.data()).release(0, QPoint(x2,y), &window);
QQuickTouchUtils::flush(&window);
QVERIFY(textInputObject->selectedText().isEmpty());
+
+ // select all text (which moves the cursor to the end), then tap:
+ // with old API, it deselects, and moves the cursor (QTBUG-116606)
+ // with new API, it remains selected, and the cursor remains at the end
+ textInputObject->selectAll();
+ const int cursorPos = textInputObject->cursorPosition();
+ QTest::touchEvent(&window, touchscreen.data()).press(0, QPoint(x2,y), &window);
+ QTest::touchEvent(&window, touchscreen.data()).release(0, QPoint(x2,y), &window);
+ QQuickTouchUtils::flush(&window);
+ QCOMPARE(textInputObject->selectedText().isEmpty(), !expectDefaultSelectByMouse);
+ if (expectDefaultSelectByMouse)
+ QCOMPARE(textInputObject->cursorPosition(), cursorPos);
+ else
+ QCOMPARE_NE(textInputObject->cursorPosition(), cursorPos);
}
void tst_qquicktextinput::touchscreenSetsFocusAndMovesCursor()
diff --git a/tests/auto/quick/qquicktextmetrics/CMakeLists.txt b/tests/auto/quick/qquicktextmetrics/CMakeLists.txt
index 6880cf8445..86bc976156 100644
--- a/tests/auto/quick/qquicktextmetrics/CMakeLists.txt
+++ b/tests/auto/quick/qquicktextmetrics/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktextmetrics Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktextmetrics LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qquicktextmetrics
SOURCES
tst_qquicktextmetrics.cpp
diff --git a/tests/auto/quick/qquicktextmetrics/tst_qquicktextmetrics.cpp b/tests/auto/quick/qquicktextmetrics/tst_qquicktextmetrics.cpp
index 21c816fa9d..8c3448d422 100644
--- a/tests/auto/quick/qquicktextmetrics/tst_qquicktextmetrics.cpp
+++ b/tests/auto/quick/qquicktextmetrics/tst_qquicktextmetrics.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QFont>
#include <QString>
diff --git a/tests/auto/quick/qquicktimeline/CMakeLists.txt b/tests/auto/quick/qquicktimeline/CMakeLists.txt
index bdb2d19ad1..e4e1de1346 100644
--- a/tests/auto/quick/qquicktimeline/CMakeLists.txt
+++ b/tests/auto/quick/qquicktimeline/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquicktimeline Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktimeline LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qquicktimeline
SOURCES
tst_qquicktimeline.cpp
diff --git a/tests/auto/quick/qquicktimeline/tst_qquicktimeline.cpp b/tests/auto/quick/qquicktimeline/tst_qquicktimeline.cpp
index 76de05258d..247af56fd7 100644
--- a/tests/auto/quick/qquicktimeline/tst_qquicktimeline.cpp
+++ b/tests/auto/quick/qquicktimeline/tst_qquicktimeline.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <private/qquicktimeline_p_p.h>
diff --git a/tests/auto/quick/qquicktreeview/CMakeLists.txt b/tests/auto/quick/qquicktreeview/CMakeLists.txt
index 27e94c2c1e..1fa487c2e8 100644
--- a/tests/auto/quick/qquicktreeview/CMakeLists.txt
+++ b/tests/auto/quick/qquicktreeview/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qquicktreeview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquicktreeview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquicktreeview/data/CustomDelegate.qml b/tests/auto/quick/qquicktreeview/data/CustomDelegate.qml
index 72fa344b0b..525f9ea337 100644
--- a/tests/auto/quick/qquicktreeview/data/CustomDelegate.qml
+++ b/tests/auto/quick/qquicktreeview/data/CustomDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import TestModel
diff --git a/tests/auto/quick/qquicktreeview/data/normaltreeview.qml b/tests/auto/quick/qquicktreeview/data/normaltreeview.qml
index cf4d72d0ca..7a68646c80 100644
--- a/tests/auto/quick/qquicktreeview/data/normaltreeview.qml
+++ b/tests/auto/quick/qquicktreeview/data/normaltreeview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import TestModel
diff --git a/tests/auto/quick/qquicktreeview/testmodel.cpp b/tests/auto/quick/qquicktreeview/testmodel.cpp
index 9962234a06..66fbf9b656 100644
--- a/tests/auto/quick/qquicktreeview/testmodel.cpp
+++ b/tests/auto/quick/qquicktreeview/testmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testmodel.h"
@@ -128,3 +128,33 @@ bool TestModel::insertRows(int position, int rows, const QModelIndex &parent)
endInsertRows();
return true;
}
+
+
+void insertColumnsRecursive(TreeItem *item, int row, int pos, int cols)
+{
+ for (int col = 0; col < cols; col++)
+ item->m_entries.insert(pos + col, QVariant(QString("%1, %2 (inserted)").arg(row).arg(pos + col)));
+ for (auto child : item->m_childItems) {
+ insertColumnsRecursive(child, row, pos, cols);
+ row++;
+ }
+}
+
+bool TestModel::insertColumns(int position, int cols, const QModelIndex &parent)
+{
+ if (!parent.isValid()) {
+ qWarning() << "Cannot insert columns on an invalid parent!";
+ return false;
+ }
+
+ beginInsertColumns(parent, position, position + cols - 1);
+ TreeItem *parentItem = treeItem(parent);
+
+ TreeItem *item = m_rootItem.data();
+
+ insertColumnsRecursive(item, 0, position, cols);
+ m_columnCount += cols;
+
+ endInsertColumns();
+ return true;
+}
diff --git a/tests/auto/quick/qquicktreeview/testmodel.h b/tests/auto/quick/qquicktreeview/testmodel.h
index 4cb80d0eab..e12d0ae1d6 100644
--- a/tests/auto/quick/qquicktreeview/testmodel.h
+++ b/tests/auto/quick/qquicktreeview/testmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTMODEL_H
#define TESTMODEL_H
@@ -39,6 +39,7 @@ public:
int maxDepth() { return 4; }
bool insertRows(int position, int rows, const QModelIndex &parent) override;
+ bool insertColumns(int position, int cols, const QModelIndex &parent) override;
private:
QScopedPointer<TreeItem> m_rootItem;
diff --git a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
index 6e7a1e1216..d41d811496 100644
--- a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
+++ b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTest/quicktest.h>
@@ -76,6 +76,7 @@ private slots:
void emptyModel();
void updatedModifiedModel();
void insertRows();
+ void insertColumns();
void toggleExpandedUsingArrowKeys();
void expandAndCollapsUsingDoubleClick();
void selectionBehaviorCells_data();
@@ -87,6 +88,8 @@ private slots:
void sortTreeModel();
void sortTreeModelDynamic_data();
void sortTreeModelDynamic();
+ void setRootIndex();
+ void setRootIndexToLeaf();
};
tst_qquicktreeview::tst_qquicktreeview()
@@ -139,7 +142,7 @@ void tst_qquicktreeview::expandAndCollapseRoot()
// Check that the view only has one row loaded so far (the root of the tree)
QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
- QSignalSpy expandedSpy(treeView, SIGNAL(expanded(int, int)));
+ QSignalSpy expandedSpy(treeView, SIGNAL(expanded(int,int)));
// Expand the root
treeView->expand(0);
@@ -184,7 +187,7 @@ void tst_qquicktreeview::expandAndCollapseChildren()
LOAD_TREEVIEW("normaltreeview.qml");
const int childCount = 4;
- QSignalSpy expandedSpy(treeView, SIGNAL(expanded(int, int)));
+ QSignalSpy expandedSpy(treeView, SIGNAL(expanded(int,int)));
// Expand the last child of a parent recursively four times
for (int level = 0; level < 4; ++level) {
@@ -305,7 +308,7 @@ void tst_qquicktreeview::requiredPropertiesChildren()
QCOMPARE(viewProp, treeView);
QCOMPARE(isTreeNode, true);
QCOMPARE(expanded, row == 4);
- QCOMPARE(hasChildren, model->hasChildren(treeView->modelIndex(row, 0)));
+ QCOMPARE(hasChildren, model->hasChildren(treeView->index(row, 0)));
QCOMPARE(depth, row <= 4 ? 1 : 2);
}
}
@@ -328,7 +331,7 @@ void tst_qquicktreeview::emptyModel()
QCOMPARE(treeView->depth(0), -1);
QCOMPARE(treeView->isExpanded(0), false);
- QVERIFY(!treeView->modelIndex(10, 10).isValid());
+ QVERIFY(!treeView->index(10, 10).isValid());
QCOMPARE(treeView->rowAtIndex(QModelIndex()), -1);
QCOMPARE(treeView->columnAtIndex(QModelIndex()), -1);
}
@@ -390,6 +393,48 @@ void tst_qquicktreeview::insertRows()
QCOMPARE(treeView->rows(), 9);
}
+void tst_qquicktreeview::insertColumns()
+{
+ // Check that if we add new columns to the model, TreeView gets updated
+ // to contain the new expected number of rows (flattened to a list)
+ LOAD_TREEVIEW("normaltreeview.qml");
+ treeView->expand(0);
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(treeView->columns(), 5);
+
+ const QModelIndex rootNode = model->index(0, 0, QModelIndex());
+ model->insertColumns(0, 2, rootNode);
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(treeView->columns(), 7);
+ auto childItem1 = treeViewPrivate->loadedTableItem(QPoint(0, 1))->item;
+ QCOMPARE(childItem1->property("text").toString(), "0, 0 (inserted)");
+ auto childItem2 = treeViewPrivate->loadedTableItem(QPoint(0, 2))->item;
+ QCOMPARE(childItem2->property("text").toString(), "1, 0 (inserted)");
+ auto childItem3 = treeViewPrivate->loadedTableItem(QPoint(0, 3))->item;
+ QCOMPARE(childItem3->property("text").toString(), "2, 0 (inserted)");
+ auto childItem4 = treeViewPrivate->loadedTableItem(QPoint(3, 0))->item;
+ QCOMPARE(childItem4->property("text").toString(), "0, 1");
+ auto childItem5 = treeViewPrivate->loadedTableItem(QPoint(3, 1))->item;
+ QCOMPARE(childItem5->property("text").toString(), "0, 1");
+
+ const QModelIndex indexOfInsertedChild = model->index(1, 0, rootNode);
+ model->insertRows(0, 2, indexOfInsertedChild);
+ treeView->expand(2);
+ WAIT_UNTIL_POLISHED;
+
+ QCOMPARE(treeView->rows(), 7);
+ QCOMPARE(treeView->columns(), 7);
+
+ for (int i = 0; i < 7; i++) {
+ for (int j = 0; j < 7; j++) {
+ auto childItem = treeViewPrivate->loadedTableItem(QPoint(j, i))->item;
+ QVERIFY(childItem);
+ }
+ }
+}
+
void tst_qquicktreeview::expandChildPendingToBeVisible()
{
// Check that if we expand a row r1, and that row has a child r2 that can
@@ -434,7 +479,7 @@ void tst_qquicktreeview::expandRecursivelyRoot()
QFETCH(int, depth);
LOAD_TREEVIEW("normaltreeview.qml");
- QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
+ QSignalSpy spy(treeView, SIGNAL(expanded(int,int)));
treeView->expandRecursively(rowToExpand, depth);
@@ -461,7 +506,7 @@ void tst_qquicktreeview::expandRecursivelyRoot()
// Check that all rows after rowToExpand, that are also
// children of that row, is expanded (down to depth)
for (int currentRow = rowToExpand + 1; currentRow < treeView->rows(); ++currentRow) {
- const auto modelIndex = treeView->modelIndex(currentRow, 0);
+ const auto modelIndex = treeView->index(currentRow, 0);
const int currentDepth = treeView->depth(currentRow);
const bool isChild = currentDepth > rowToExpandDepth;
const bool isExpandable = model->rowCount(modelIndex) > 0;
@@ -491,7 +536,7 @@ void tst_qquicktreeview::expandRecursivelyChild()
QFETCH(int, depth);
LOAD_TREEVIEW("normaltreeview.qml");
- QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
+ QSignalSpy spy(treeView, SIGNAL(expanded(int,int)));
treeView->expand(0);
@@ -513,7 +558,7 @@ void tst_qquicktreeview::expandRecursivelyChild()
WAIT_UNTIL_POLISHED;
- const bool rowToExpandDepth = treeView->depth(rowToExpand);
+ const int rowToExpandDepth = treeView->depth(rowToExpand);
const int effectiveMaxDepth = depth != -1 ? rowToExpandDepth + depth : model->maxDepth();
// Check that none of the rows before rowToExpand are expanded
@@ -532,7 +577,7 @@ void tst_qquicktreeview::expandRecursivelyChild()
for (int currentRow = rowToExpand + 1; currentRow < treeView->rows(); ++currentRow) {
const int currentDepth = treeView->depth(currentRow);
const bool isChild = currentDepth > rowToExpandDepth;
- const auto modelIndex = treeView->modelIndex(currentRow, 0);
+ const auto modelIndex = treeView->index(currentRow, 0);
const bool isExpandable = model->rowCount(modelIndex) > 0;
const bool shouldBeExpanded = isChild && isExpandable && currentDepth < effectiveMaxDepth;
QCOMPARE(treeView->isExpanded(currentRow), shouldBeExpanded);
@@ -543,7 +588,7 @@ void tst_qquicktreeview::expandRecursivelyWholeTree()
{
// Check that we expand the whole tree recursively by passing -1, -1
LOAD_TREEVIEW("normaltreeview.qml");
- QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
+ QSignalSpy spy(treeView, SIGNAL(expanded(int,int)));
treeView->expandRecursively(-1, -1);
QCOMPARE(spy.size(), 1);
@@ -555,7 +600,7 @@ void tst_qquicktreeview::expandRecursivelyWholeTree()
// Check that all rows that have children are expanded
for (int currentRow = 0; currentRow < treeView->rows(); ++currentRow) {
- const auto modelIndex = treeView->modelIndex(currentRow, 0);
+ const auto modelIndex = treeView->index(currentRow, 0);
const bool isExpandable = model->rowCount(modelIndex) > 0;
QCOMPARE(treeView->isExpanded(currentRow), isExpandable);
}
@@ -575,7 +620,7 @@ void tst_qquicktreeview::collapseRecursivelyRoot()
const int expectedRowCount = 1 + (model->maxDepth() * 8) - 4;
QCOMPARE(treeView->rows(), expectedRowCount);
- QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool)));
+ QSignalSpy spy(treeView, SIGNAL(collapsed(int,bool)));
// Collapse the whole tree again. This time, only the root should end up visible
treeView->collapseRecursively();
@@ -595,7 +640,7 @@ void tst_qquicktreeview::collapseRecursivelyRoot()
// We can do that by simply iterate over the rows in the view as we expand.
int currentRow = 0;
while (currentRow < treeView->rows()) {
- const QModelIndex currentIndex = treeView->modelIndex(currentRow, 0);
+ const QModelIndex currentIndex = treeView->index(currentRow, 0);
if (model->hasChildren(currentIndex)) {
QVERIFY(!treeView->isExpanded(currentRow));
treeView->expand(currentRow);
@@ -622,11 +667,11 @@ void tst_qquicktreeview::collapseRecursivelyChild()
const int expectedRowCount = 1 + (model->maxDepth() * 8) - 4;
QCOMPARE(treeView->rows(), expectedRowCount);
- QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool)));
+ QSignalSpy spy(treeView, SIGNAL(collapsed(int,bool)));
// Collapse the 8th child recursive
const int rowToCollapse = 8;
- const QModelIndex collapseIndex = treeView->modelIndex(rowToCollapse, 0);
+ const QModelIndex collapseIndex = treeView->index(rowToCollapse, 0);
const auto expectedLabel = model->data(collapseIndex, Qt::DisplayRole);
QCOMPARE(expectedLabel, QStringLiteral("3, 0"));
treeView->collapseRecursively(rowToCollapse);
@@ -646,7 +691,7 @@ void tst_qquicktreeview::collapseRecursivelyChild()
// We can do that by simply iterate over the rows in the view as we expand.
int currentRow = 1; // start at first child
while (currentRow < treeView->rows()) {
- const QModelIndex currentIndex = treeView->modelIndex(currentRow, 0);
+ const QModelIndex currentIndex = treeView->index(currentRow, 0);
if (model->hasChildren(currentIndex)) {
if (treeView->depth(currentRow) == 1 && currentIndex.row() == 2) {
// We did only recursively expand the 4th child, so the
@@ -669,7 +714,7 @@ void tst_qquicktreeview::collapseRecursivelyWholeTree()
{
// Check that we collapse the whole tree recursively by passing -1
LOAD_TREEVIEW("normaltreeview.qml");
- QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool)));
+ QSignalSpy spy(treeView, SIGNAL(collapsed(int,bool)));
treeView->expandRecursively();
treeView->collapseRecursively();
@@ -688,7 +733,7 @@ void tst_qquicktreeview::expandToIndex()
// Check that expandToIndex(index) expands the tree so
// that index becomes visible in the view
LOAD_TREEVIEW("normaltreeview.qml");
- QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
+ QSignalSpy spy(treeView, SIGNAL(expanded(int,int)));
const QModelIndex root = model->index(0, 0);
const QModelIndex child1 = model->index(3, 0, root);
@@ -731,7 +776,7 @@ void tst_qquicktreeview::toggleExpandedUsingArrowKeys()
QQuickWindow *window = treeView->window();
// Start by making cell 0, 0 current
- treeView->selectionModel()->setCurrentIndex(treeView->modelIndex(0, 0), QItemSelectionModel::NoUpdate);
+ treeView->selectionModel()->setCurrentIndex(treeView->index(0, 0), QItemSelectionModel::NoUpdate);
// Expand row 0
const int row0 = 0;
@@ -747,7 +792,7 @@ void tst_qquicktreeview::toggleExpandedUsingArrowKeys()
// Hitting Key_Right again should be a no-op
QTest::keyPress(window, Qt::Key_Right);
QVERIFY(treeView->isExpanded(row0));
- QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->modelIndex(row0, 0));
+ QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->index(row0, 0));
// Move down to row 1 and try to expand it. Since Row 1
// doesn't have children, expanding it will be a no-op.
@@ -758,7 +803,7 @@ void tst_qquicktreeview::toggleExpandedUsingArrowKeys()
QTest::keyPress(window, Qt::Key_Down);
QTest::keyPress(window, Qt::Key_Right);
QVERIFY(!treeView->isExpanded(row1));
- QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->modelIndex(row1, 0));
+ QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->index(row1, 0));
// Move down to row 4 and expand it
const int row4 = 4;
@@ -768,7 +813,7 @@ void tst_qquicktreeview::toggleExpandedUsingArrowKeys()
QVERIFY(!treeView->isExpanded(row4));
QTest::keyPress(window, Qt::Key_Right);
QVERIFY(treeView->isExpanded(row4));
- QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->modelIndex(row4, 0));
+ QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->index(row4, 0));
// Move up again to row 0 and collapse it
while (treeView->currentRow() != row0)
@@ -781,7 +826,7 @@ void tst_qquicktreeview::toggleExpandedUsingArrowKeys()
// Hitting Key_Left again should be a no-op
QTest::keyPress(window, Qt::Key_Left);
QVERIFY(!treeView->isExpanded(row0));
- QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->modelIndex(row0, 0));
+ QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->index(row0, 0));
}
void tst_qquicktreeview::expandAndCollapsUsingDoubleClick()
@@ -791,7 +836,7 @@ void tst_qquicktreeview::expandAndCollapsUsingDoubleClick()
QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
// Expand the root by double clicking on the row
- const auto item = treeView->itemAtCell(0, 0);
+ const auto item = treeView->itemAtIndex(treeView->index(0, 0));
QVERIFY(item);
const QPoint localPos = QPoint(item->width() / 2, item->height() / 2);
const QPoint pos = item->window()->contentItem()->mapFromItem(item, localPos).toPoint();
@@ -876,6 +921,7 @@ void tst_qquicktreeview::selectionBehaviorCells()
const QPointF endPos(endItem->x(), endItem->y());
const QPointF endPosWrapped(endItemWrapped->x(), endItemWrapped->y());
+ QVERIFY(treeViewPrivate->startSelection(startPos, Qt::NoModifier));
treeViewPrivate->setSelectionStartPos(startPos);
treeViewPrivate->setSelectionEndPos(endPos);
@@ -888,7 +934,7 @@ void tst_qquicktreeview::selectionBehaviorCells()
for (int x = x1; x < x2; ++x) {
for (int y = y1; y < y2; ++y) {
- const auto index = treeView->modelIndex(y, x);
+ const auto index = treeView->index(y, x);
QVERIFY(selectionModel->isSelected(index));
}
}
@@ -930,6 +976,7 @@ void tst_qquicktreeview::selectionBehaviorRows()
QCOMPARE(selectionModel->hasSelection(), false);
// Drag from row 0 to row 3
+ QVERIFY(treeViewPrivate->startSelection(QPointF(0, 0), Qt::NoModifier));
treeViewPrivate->setSelectionStartPos(QPointF(0, 0));
treeViewPrivate->setSelectionEndPos(QPointF(80, 60));
@@ -941,7 +988,7 @@ void tst_qquicktreeview::selectionBehaviorRows()
for (int x = 0; x < treeView->columns(); ++x) {
for (int y = 0; y < 3; ++y) {
- const auto index = treeView->modelIndex(y, x);
+ const auto index = treeView->index(y, x);
QVERIFY(selectionModel->isSelected(index));
}
}
@@ -950,6 +997,7 @@ void tst_qquicktreeview::selectionBehaviorRows()
QCOMPARE(selectionModel->hasSelection(), false);
// Drag from row 3 to row 0 (and overshoot mouse)
+ QVERIFY(treeViewPrivate->startSelection(QPointF(80, 60), Qt::NoModifier));
treeViewPrivate->setSelectionStartPos(QPointF(80, 60));
treeViewPrivate->setSelectionEndPos(QPointF(-10, -10));
@@ -960,7 +1008,7 @@ void tst_qquicktreeview::selectionBehaviorRows()
for (int x = 0; x < treeView->columns(); ++x) {
for (int y = 0; y < 3; ++y) {
- const auto index = treeView->modelIndex(y, x);
+ const auto index = treeView->index(y, x);
QVERIFY(selectionModel->isSelected(index));
}
}
@@ -981,6 +1029,7 @@ void tst_qquicktreeview::selectionBehaviorColumns()
QCOMPARE(selectionModel->hasSelection(), false);
// Drag from column 0 to column 3
+ QVERIFY(treeViewPrivate->startSelection(QPointF(0, 0), Qt::NoModifier));
treeViewPrivate->setSelectionStartPos(QPointF(0, 0));
treeViewPrivate->setSelectionEndPos(QPointF(225, 90));
@@ -992,7 +1041,7 @@ void tst_qquicktreeview::selectionBehaviorColumns()
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < treeView->rows(); ++y) {
- const auto index = treeView->modelIndex(y, x);
+ const auto index = treeView->index(y, x);
QVERIFY(selectionModel->isSelected(index));
}
}
@@ -1001,6 +1050,7 @@ void tst_qquicktreeview::selectionBehaviorColumns()
QCOMPARE(selectionModel->hasSelection(), false);
// Drag from column 3 to column 0 (and overshoot mouse)
+ QVERIFY(treeViewPrivate->startSelection(QPointF(225, 90), Qt::NoModifier));
treeViewPrivate->setSelectionStartPos(QPointF(225, 90));
treeViewPrivate->setSelectionEndPos(QPointF(-10, -10));
@@ -1011,7 +1061,7 @@ void tst_qquicktreeview::selectionBehaviorColumns()
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < treeView->rows(); ++y) {
- const auto index = treeView->modelIndex(y, x);
+ const auto index = treeView->index(y, x);
QVERIFY(selectionModel->isSelected(index));
}
}
@@ -1030,10 +1080,11 @@ void tst_qquicktreeview::selectionBehaviorDisabled()
QCOMPARE(selectionModel->hasSelection(), false);
- // Drag from column 0 to column 3
- treeViewPrivate->setSelectionStartPos(QPointF(0, 0));
- treeViewPrivate->setSelectionEndPos(QPointF(60, 60));
-
+ // Try to start a selection. treeViewPrivate->startSelection() should
+ // reject that, and and return false. The selectionFlag will there stay as
+ // QItemSelectionModel::NoUpdate, meaning no active selection is ongoing.
+ QVERIFY(!treeViewPrivate->startSelection(QPointF(0, 0), Qt::NoModifier));
+ QCOMPARE(treeViewPrivate->selectionFlag, QItemSelectionModel::NoUpdate);
QCOMPARE(selectionModel->hasSelection(), false);
}
@@ -1068,7 +1119,7 @@ void tst_qquicktreeview::sortTreeModel()
// is the same as in the view. That means that QQmlTreeModelToTableModel
// and QSortFilterProxyModel are in sync.
for (int row = 0; row < treeView->rows(); ++row) {
- const auto index = treeView->modelIndex(row, 0);
+ const auto index = treeView->index(row, 0);
const QString modelDisplay = proxyModel.data(index, Qt::DisplayRole).toString();
const auto childFxItem = treeViewPrivate->loadedTableItem(QPoint(0, row));
QVERIFY(childFxItem);
@@ -1084,7 +1135,7 @@ void tst_qquicktreeview::sortTreeModel()
WAIT_UNTIL_POLISHED;
for (int row = 0; row < treeView->rows(); ++row) {
- const auto index = treeView->modelIndex(row, 0);
+ const auto index = treeView->index(row, 0);
const QString modelDisplay = proxyModel.data(index, Qt::DisplayRole).toString();
const auto childFxItem = treeViewPrivate->loadedTableItem(QPoint(0, row));
QVERIFY(childFxItem);
@@ -1133,7 +1184,7 @@ void tst_qquicktreeview::sortTreeModelDynamic()
// is the same as in the view. That means that QQmlTreeModelToTableModel
// and QSortFilterProxyModel are in sync.
for (int row = 0; row < treeView->rows(); ++row) {
- const auto index = treeView->modelIndex(row, 0);
+ const auto index = treeView->index(row, 0);
const QString modelDisplay = proxyModel.data(index, Qt::DisplayRole).toString();
const auto childFxItem = treeViewPrivate->loadedTableItem(QPoint(0, row));
QVERIFY(childFxItem);
@@ -1146,10 +1197,10 @@ void tst_qquicktreeview::sortTreeModelDynamic()
// Now change the text in one of the items. This will trigger
// a sort for only one of the parents in the model.
- proxyModel.setData(treeView->modelIndex(row, 0), u"xxx"_s, Qt::DisplayRole);
+ proxyModel.setData(treeView->index(row, 0), u"xxx"_s, Qt::DisplayRole);
for (int row = 0; row < treeView->rows(); ++row) {
- const auto index = treeView->modelIndex(row, 0);
+ const auto index = treeView->index(row, 0);
const QString modelDisplay = proxyModel.data(index, Qt::DisplayRole).toString();
const auto childFxItem = treeViewPrivate->loadedTableItem(QPoint(0, row));
QVERIFY(childFxItem);
@@ -1161,6 +1212,100 @@ void tst_qquicktreeview::sortTreeModelDynamic()
}
}
+void tst_qquicktreeview::setRootIndex()
+{
+ // Check that if you can change the root index in the view to point
+ // at a child branch in the model
+ LOAD_TREEVIEW("normaltreeview.qml");
+
+ const QModelIndex rootIndex = model->index(0, 0);
+ const QModelIndex childIndex = model->index(3, 0, rootIndex);
+ QVERIFY(model->hasChildren(childIndex));
+ treeView->setRootIndex(childIndex);
+
+ // Go through all rows in the view, and check that view shows the
+ // same display text as the display role in the model (under the
+ // given root).
+ for (int row = 0; row < treeView->rows(); ++row) {
+ const auto index = model->index(row, 0, childIndex);
+ const QString modelDisplay = model->data(index, Qt::DisplayRole).toString();
+ const auto childFxItem = treeViewPrivate->loadedTableItem(QPoint(0, row));
+ QVERIFY(childFxItem);
+ const auto childItem = childFxItem->item;
+ QVERIFY(childItem);
+ const auto context = qmlContext(childItem.data());
+ const auto itemDisplay = context->contextProperty("display").toString();
+ QCOMPARE(itemDisplay, modelDisplay);
+ }
+
+ // Do the same once more, but this time choose a child that is deeper in the model
+ const QModelIndex childIndex2 = model->index(3, 0, childIndex);
+ QVERIFY(model->hasChildren(childIndex2));
+ treeView->setRootIndex(childIndex);
+
+ for (int row = 0; row < treeView->rows(); ++row) {
+ const auto index = model->index(row, 0, childIndex2);
+ const QString modelDisplay = model->data(index, Qt::DisplayRole).toString();
+ const auto childFxItem = treeViewPrivate->loadedTableItem(QPoint(0, row));
+ QVERIFY(childFxItem);
+ const auto childItem = childFxItem->item;
+ QVERIFY(childItem);
+ const auto context = qmlContext(childItem.data());
+ const auto itemDisplay = context->contextProperty("display").toString();
+ QCOMPARE(itemDisplay, modelDisplay);
+ }
+
+ // Reset rootIndex. This should show the whole model again
+ treeView->setRootIndex(QModelIndex());
+
+ for (int row = 0; row < treeView->rows(); ++row) {
+ const auto index = model->index(row, 0);
+ const QString modelDisplay = model->data(index, Qt::DisplayRole).toString();
+ const auto childFxItem = treeViewPrivate->loadedTableItem(QPoint(0, row));
+ QVERIFY(childFxItem);
+ const auto childItem = childFxItem->item;
+ QVERIFY(childItem);
+ const auto context = qmlContext(childItem.data());
+ const auto itemDisplay = context->contextProperty("display").toString();
+ QCOMPARE(itemDisplay, modelDisplay);
+ }
+}
+
+void tst_qquicktreeview::setRootIndexToLeaf()
+{
+ // When you set a custom root index, the root index itself will not
+ // be shown. Therefore, check that if you change the root index to a
+ // leaf in the model, TreeView will be empty.
+ LOAD_TREEVIEW("normaltreeview.qml");
+
+ const QModelIndex rootIndex = model->index(0, 0);
+ const QModelIndex leafIndex = model->index(1, 0, rootIndex);
+ QVERIFY(!model->hasChildren(leafIndex));
+ treeView->setRootIndex(leafIndex);
+ WAIT_UNTIL_POLISHED;
+ QCOMPARE(treeView->rows(), 0);
+
+ // According to the docs, you can set rootIndex to undefined
+ // in order to show the whole model again. This is the same
+ // as calling 'reset' on the property from c++. Verify that this works.
+ const QMetaObject *metaObject = treeView->metaObject();
+ const int propertyIndex = metaObject->indexOfProperty("rootIndex");
+ QVERIFY(propertyIndex != -1);
+ metaObject->property(propertyIndex).reset(treeView);
+
+ for (int row = 0; row < treeView->rows(); ++row) {
+ const auto index = model->index(row, 0);
+ const QString modelDisplay = model->data(index, Qt::DisplayRole).toString();
+ const auto childFxItem = treeViewPrivate->loadedTableItem(QPoint(0, row));
+ QVERIFY(childFxItem);
+ const auto childItem = childFxItem->item;
+ QVERIFY(childItem);
+ const auto context = qmlContext(childItem.data());
+ const auto itemDisplay = context->contextProperty("display").toString();
+ QCOMPARE(itemDisplay, modelDisplay);
+ }
+}
+
QTEST_MAIN(tst_qquicktreeview)
#include "tst_qquicktreeview.moc"
diff --git a/tests/auto/quick/qquickview/CMakeLists.txt b/tests/auto/quick/qquickview/CMakeLists.txt
index d5d248481a..658d957de5 100644
--- a/tests/auto/quick/qquickview/CMakeLists.txt
+++ b/tests/auto/quick/qquickview/CMakeLists.txt
@@ -7,6 +7,14 @@
## tst_qquickview Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickview LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_policy(SET QTP0001 NEW)
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -26,6 +34,14 @@ qt_internal_add_test(tst_qquickview
TESTDATA ${test_data}
)
+
+qt_add_qml_module(
+ tst_qquickview
+ URI test
+ QML_FILES
+ "data/TestQml.qml"
+)
+
## Scopes:
#####################################################################
diff --git a/tests/auto/quick/qquickview/data/TestQml.qml b/tests/auto/quick/qquickview/data/TestQml.qml
new file mode 100644
index 0000000000..3052615aef
--- /dev/null
+++ b/tests/auto/quick/qquickview/data/TestQml.qml
@@ -0,0 +1,3 @@
+import QtQuick
+
+Item {}
diff --git a/tests/auto/quick/qquickview/data/overlay.qml b/tests/auto/quick/qquickview/data/overlay.qml
new file mode 100644
index 0000000000..cefaaed8bd
--- /dev/null
+++ b/tests/auto/quick/qquickview/data/overlay.qml
@@ -0,0 +1,17 @@
+// RootItem.qml
+import QtQuick
+import QtQuick.Controls.Basic
+
+Item {
+ id: root
+ width: 640
+ height: 480
+
+ property double scaleFactor: 2.0
+ Scale {
+ id: scale
+ xScale: root.scaleFactor
+ yScale: root.scaleFactor
+ }
+ Overlay.overlay.transform: scale
+}
diff --git a/tests/auto/quick/qquickview/tst_qquickview.cpp b/tests/auto/quick/qquickview/tst_qquickview.cpp
index e226cfdca7..91a45077fb 100644
--- a/tests/auto/quick/qquickview/tst_qquickview.cpp
+++ b/tests/auto/quick/qquickview/tst_qquickview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QtQml/qqmlcomponent.h>
@@ -10,9 +10,13 @@
#include <QtGui/QWindow>
#include <QtCore/QDebug>
#include <QtQml/qqmlengine.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4mm_p.h>
#include <QtQuickTestUtils/private/geometrytestutils_p.h>
+using namespace Qt::StringLiterals;
+
class tst_QQuickView : public QQmlDataTest
{
Q_OBJECT
@@ -20,11 +24,16 @@ public:
tst_QQuickView();
private slots:
+ void gc();
void resizemodeitem();
void errors();
void engine();
void findChild();
void setInitialProperties();
+ void fromModuleCtor();
+ void loadFromModule_data();
+ void loadFromModule();
+ void overlay();
};
@@ -33,6 +42,36 @@ tst_QQuickView::tst_QQuickView()
{
}
+void tst_QQuickView::gc()
+{
+ QQuickView view;
+ QQmlEngine *engine = view.engine();
+ QV4::ExecutionEngine *v4 = engine->handle();
+
+ v4->memoryManager->gcStateMachine->deadline = QDeadlineTimer(QDeadlineTimer::Forever);
+ auto sm = v4->memoryManager->gcStateMachine.get();
+ sm->reset();
+ while (sm->state != QV4::GCState::CallDestroyObjects) {
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ view.loadFromModule("test", "TestQml");
+ auto root = view.rootObject();
+ QVERIFY(root);
+ auto ddata = QQmlData::get(root, false);
+ while (sm->state != QV4::GCState::DoSweep) {
+ if (sm->state > QV4::GCState::InitCallDestroyObjects) {
+ sm->mm->collectFromJSStack(sm->mm->markStack());
+ sm->mm->m_markStack->drain();
+ }
+ QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
+ sm->state = stateInfo.execute(sm, sm->stateData);
+ }
+ QVERIFY(ddata);
+ QVERIFY(ddata->jsWrapper.asManaged());
+ QVERIFY(ddata->jsWrapper.asManaged()->markBit());
+}
+
void tst_QQuickView::resizemodeitem()
{
QWindow window;
@@ -270,6 +309,50 @@ void tst_QQuickView::setInitialProperties()
QCOMPARE(rootObject->property("width").toInt(), 100);
}
+void tst_QQuickView::fromModuleCtor()
+{
+ QQuickView view("QtQuick", "Rectangle");
+ // creation is always synchronous for C++ defined types, so we don't need _TRY
+ QObject *rootObject = view.rootObject();
+ QVERIFY(rootObject);
+ QCOMPARE(rootObject->metaObject()->className(), "QQuickRectangle");
+}
+
+void tst_QQuickView::loadFromModule_data()
+{
+ QTest::addColumn<QString>("module");
+ QTest::addColumn<QString>("typeName");
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<QQuickView::Status>("status");
+
+ QTest::addRow("Item") << u"QtQuick"_s << u"Item"_s << QUrl() << QQuickView::Ready;
+ QTest::addRow("composite") << u"test"_s << u"TestQml"_s << QUrl("qrc:/qt/qml/test/data/TestQml.qml") << QQuickView::Ready;
+ QTest::addRow("nonexistent") << u"missing"_s << u"Type"_s << QUrl() << QQuickView::Error;
+}
+
+void tst_QQuickView::loadFromModule()
+{
+ QFETCH(QString, module);
+ QFETCH(QString, typeName);
+ QFETCH(QUrl, url);
+ QFETCH(QQuickView::Status, status);
+
+ QQuickView view;
+ view.loadFromModule(module, typeName);
+ QTRY_COMPARE(view.status(), status);
+ QCOMPARE(view.source(), url);
+}
+
+void tst_QQuickView::overlay()
+{
+ QTest::ignoreMessage(QtWarningMsg,
+ QRegularExpression(".*: Cannot set properties on overlay as it is null"));
+ QQuickView view;
+ view.setSource(testFileUrl("overlay.qml"));
+ QObject *rootObject = view.rootObject();
+ QVERIFY(!rootObject);
+}
+
QTEST_MAIN(tst_QQuickView)
#include "tst_qquickview.moc"
diff --git a/tests/auto/quick/qquickview_extra/CMakeLists.txt b/tests/auto/quick/qquickview_extra/CMakeLists.txt
index 2d8bf7e282..4287c97f51 100644
--- a/tests/auto/quick/qquickview_extra/CMakeLists.txt
+++ b/tests/auto/quick/qquickview_extra/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickview_extra Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickview_extra LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickview_extra/tst_qquickview_extra.cpp b/tests/auto/quick/qquickview_extra/tst_qquickview_extra.cpp
index 0cd4b69e00..9fbd318669 100644
--- a/tests/auto/quick/qquickview_extra/tst_qquickview_extra.cpp
+++ b/tests/auto/quick/qquickview_extra/tst_qquickview_extra.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QtQuick/qquickview.h>
@@ -42,7 +42,7 @@ void tst_QQuickViewExtra::qtbug_87228()
auto children = contentItem->childItems();
QVERIFY(children.size() > 0);
// for the sake of this test, any child would be suitable, so pick first
- deletionSpy.reset(new QSignalSpy(children[0], SIGNAL(destroyed(QObject *))));
+ deletionSpy.reset(new QSignalSpy(children[0], SIGNAL(destroyed(QObject*))));
}
QCOMPARE(deletionSpy->size(), 1);
}
diff --git a/tests/auto/quick/qquickvisualdatamodel/CMakeLists.txt b/tests/auto/quick/qquickvisualdatamodel/CMakeLists.txt
index 05aa977033..2b11d7233a 100644
--- a/tests/auto/quick/qquickvisualdatamodel/CMakeLists.txt
+++ b/tests/auto/quick/qquickvisualdatamodel/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickvisualdatamodel Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickvisualdatamodel LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickvisualdatamodel/data/externalManagedModel.qml b/tests/auto/quick/qquickvisualdatamodel/data/externalManagedModel.qml
index d6444dd636..f067e7a841 100644
--- a/tests/auto/quick/qquickvisualdatamodel/data/externalManagedModel.qml
+++ b/tests/auto/quick/qquickvisualdatamodel/data/externalManagedModel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Window 2.2
import QtQuick 2.6
diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
index dd845b37d5..0821669703 100644
--- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
+++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
@@ -59,7 +60,7 @@ public:
struct Branch {
Branch(Branch *parent = nullptr) : parent(parent) {}
- ~Branch() { foreach (const Node &child, children) delete child.branch; }
+ ~Branch() { for (const Node &child : std::as_const(children)) delete child.branch; }
int indexOf(Branch *branch) const {
for (int i = 0; i < children.size(); ++i) {
if (children.at(i).branch == branch)
@@ -75,7 +76,7 @@ public:
SingleRoleModel(const QStringList &list = QStringList(), const QByteArray &role = "name", QObject *parent = nullptr)
: QAbstractItemModel(parent), m_role(role)
{
- foreach (const QString &string, list)
+ for (const QString &string : list)
trunk.children.append(Node(string));
}
~SingleRoleModel() {}
@@ -182,7 +183,7 @@ public:
QStringList getList() const {
QStringList list;
- foreach (const Node &node, trunk.children)
+ for (const Node &node : trunk.children)
list.append(node.display);
return list;
}
@@ -190,13 +191,13 @@ public:
void setList(const QStringList &l) {
if (trunk.children.size() > 0) {
beginRemoveRows(QModelIndex(), 0, trunk.children.size() - 1);
- foreach (const Node &child, trunk.children) delete child.branch;
+ for (const Node &child : std::as_const(trunk.children)) delete child.branch;
trunk.children.clear();
endRemoveRows();
}
if (l.size() > 0) {
beginInsertRows(QModelIndex(), 0, l.size() -1);
- foreach (const QString &string, l)
+ for (const QString &string : l)
trunk.children.append(Node(string));
endInsertRows();
}
@@ -603,7 +604,8 @@ void tst_qquickvisualdatamodel::childChanged()
QVERIFY(name);
QCOMPARE(name->text(), QString("Row 2 updated child"));
- model.item(1,0)->appendRow(new QStandardItem(QLatin1String("Row 2 Child Item 2")));
+ QStandardItem item(QLatin1String("Row 2 Child Item 2"));
+ model.item(1,0)->appendRow(&item);
QCOMPARE(listview->count(), 2);
listview->forceLayout();
@@ -635,10 +637,10 @@ void tst_qquickvisualdatamodel::objectListModel()
QQuickView view;
QList<QObject*> dataList;
- dataList.append(new DataObject("Item 1", "red"));
- dataList.append(new DataObject("Item 2", "green"));
- dataList.append(new DataObject("Item 3", "blue"));
- dataList.append(new DataObject("Item 4", "yellow"));
+ dataList.append(new DataObject("Item 1", "red", &view));
+ dataList.append(new DataObject("Item 2", "green", &view));
+ dataList.append(new DataObject("Item 3", "blue", &view));
+ dataList.append(new DataObject("Item 4", "yellow", &view));
QQmlContext *ctxt = view.rootContext();
ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
@@ -777,10 +779,10 @@ void tst_qquickvisualdatamodel::modelProperties()
QQuickView view;
QList<QObject*> dataList;
- dataList.append(new DataObject("Item 1", "red"));
- dataList.append(new DataObject("Item 2", "green"));
- dataList.append(new DataObject("Item 3", "blue"));
- dataList.append(new DataObject("Item 4", "yellow"));
+ dataList.append(new DataObject("Item 1", "red", &view));
+ dataList.append(new DataObject("Item 2", "green", &view));
+ dataList.append(new DataObject("Item 3", "blue", &view));
+ dataList.append(new DataObject("Item 4", "yellow", &view));
QQmlContext *ctxt = view.rootContext();
ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
@@ -846,12 +848,9 @@ void tst_qquickvisualdatamodel::modelProperties()
QUrl source(testFileUrl("modelproperties2.qml"));
//3 items, 3 i each
- QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: ReferenceError: modelData is not defined");
- QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: ReferenceError: modelData is not defined");
- QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: ReferenceError: modelData is not defined");
- QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":11: ReferenceError: modelData is not defined");
- QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":11: ReferenceError: modelData is not defined");
- QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":11: ReferenceError: modelData is not defined");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: TypeError: Cannot read property 'display' of undefined");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: TypeError: Cannot read property 'display' of undefined");
+ QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":13: TypeError: Cannot read property 'display' of undefined");
QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":17: TypeError: Cannot read property 'display' of undefined");
QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":17: TypeError: Cannot read property 'display' of undefined");
QTest::ignoreMessage(QtWarningMsg, source.toString().toLatin1() + ":17: TypeError: Cannot read property 'display' of undefined");
@@ -2261,7 +2260,7 @@ void tst_qquickvisualdatamodel::onChanged()
evaluate<void>(object.data(), expression);
- foreach (const QString &test, tests) {
+ for (const QString &test : std::as_const(tests)) {
bool passed = evaluate<bool>(object.data(), test);
if (!passed)
qWarning() << test;
@@ -3998,7 +3997,8 @@ void tst_qquickvisualdatamodel::asynchronousInsert()
engine.rootContext()->setContextProperty("myModel", &model);
- QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create());
+ QScopedPointer<QObject> o(c.create());
+ QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(o.data());
QVERIFY(visualModel);
ItemRequester requester;
@@ -4063,7 +4063,8 @@ void tst_qquickvisualdatamodel::asynchronousRemove()
engine.rootContext()->setContextProperty("myModel", &model);
- QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create());
+ QScopedPointer<QObject> o(c.create());
+ QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(o.data());
QVERIFY(visualModel);
ItemRequester requester;
@@ -4142,7 +4143,8 @@ void tst_qquickvisualdatamodel::asynchronousMove()
engine.rootContext()->setContextProperty("myModel", &model);
- QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create());
+ QScopedPointer<QObject> o(c.create());
+ QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(o.data());
QVERIFY(visualModel);
ItemRequester requester;
@@ -4190,7 +4192,8 @@ void tst_qquickvisualdatamodel::asynchronousCancel()
engine.rootContext()->setContextProperty("myModel", &model);
- QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create());
+ QScopedPointer<QObject> o(c.create());
+ QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(o.data());
QVERIFY(visualModel);
QQuickItem *item = qobject_cast<QQuickItem*>(visualModel->object(requestIndex, QQmlIncubator::Asynchronous));
@@ -4312,7 +4315,8 @@ void tst_qquickvisualdatamodel::delegateModelChangeDelegate()
c.setData("import QtQml.Models 2.2\nDelegateModel {}\n", QUrl());
QCOMPARE(c.status(), QQmlComponent::Ready);
- QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create(context.data()));
+ QScopedPointer<QObject> o(c.create(context.data()));
+ QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(o.data());
QVERIFY(visualModel);
visualModel->setModel(QVariant(3));
diff --git a/tests/auto/quick/qquickwindow/CMakeLists.txt b/tests/auto/quick/qquickwindow/CMakeLists.txt
index 6e8cef42b2..0bec56ea73 100644
--- a/tests/auto/quick/qquickwindow/CMakeLists.txt
+++ b/tests/auto/quick/qquickwindow/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickwindow Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickwindow LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/qquickwindow/data/conflictingVisibleFalse.qml b/tests/auto/quick/qquickwindow/data/conflictingVisibleFalse.qml
new file mode 100644
index 0000000000..98f374a741
--- /dev/null
+++ b/tests/auto/quick/qquickwindow/data/conflictingVisibleFalse.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Window {
+ visibility: Window.Windowed
+ visible: false
+}
diff --git a/tests/auto/quick/qquickwindow/data/conflictingVisibleTrue.qml b/tests/auto/quick/qquickwindow/data/conflictingVisibleTrue.qml
new file mode 100644
index 0000000000..52d9b2fc34
--- /dev/null
+++ b/tests/auto/quick/qquickwindow/data/conflictingVisibleTrue.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Window {
+ visibility: Window.Hidden
+ visible: true
+}
diff --git a/tests/auto/quick/qquickwindow/data/eventTypes.qml b/tests/auto/quick/qquickwindow/data/eventTypes.qml
new file mode 100644
index 0000000000..05d80bfbd1
--- /dev/null
+++ b/tests/auto/quick/qquickwindow/data/eventTypes.qml
@@ -0,0 +1,20 @@
+import QtQuick
+import QtQuick.Window
+
+Window {
+ id: root
+ function handleKey(e: KeyEvent) {}
+ function handleMouse(e: MouseEvent) {}
+ function handleWheel(e: WheelEvent) {}
+ function handleClose(e: CloseEvent) {}
+
+ Item {
+ Keys.onDeletePressed: root.handleKey
+ MouseArea {
+ onClicked: root.handleMouse
+ onWheel: root.handleWheel
+ }
+ }
+
+ onClosing: handleClose
+}
diff --git a/tests/auto/quick/qquickwindow/data/maximized.qml b/tests/auto/quick/qquickwindow/data/maximized.qml
new file mode 100644
index 0000000000..4d7054645b
--- /dev/null
+++ b/tests/auto/quick/qquickwindow/data/maximized.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Window {
+ visibility: Window.Maximized
+}
diff --git a/tests/auto/quick/qquickwindow/data/shortcut.qml b/tests/auto/quick/qquickwindow/data/shortcut.qml
index 3b92cdf2e0..892eca1727 100644
--- a/tests/auto/quick/qquickwindow/data/shortcut.qml
+++ b/tests/auto/quick/qquickwindow/data/shortcut.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
import QtQuick.Window 2.2
diff --git a/tests/auto/quick/qquickwindow/data/shortcutOverride.qml b/tests/auto/quick/qquickwindow/data/shortcutOverride.qml
index e5d35a984d..960035c10f 100644
--- a/tests/auto/quick/qquickwindow/data/shortcutOverride.qml
+++ b/tests/auto/quick/qquickwindow/data/shortcutOverride.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quick/qquickwindow/data/visibilityDoesntClobberWindowState.qml b/tests/auto/quick/qquickwindow/data/visibilityDoesntClobberWindowState.qml
new file mode 100644
index 0000000000..af899ec5dd
--- /dev/null
+++ b/tests/auto/quick/qquickwindow/data/visibilityDoesntClobberWindowState.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Window {
+
+}
diff --git a/tests/auto/quick/qquickwindow/data/windowattached.qml b/tests/auto/quick/qquickwindow/data/windowattached.qml
index 66083db428..2f0d08984f 100644
--- a/tests/auto/quick/qquickwindow/data/windowattached.qml
+++ b/tests/auto/quick/qquickwindow/data/windowattached.qml
@@ -1,5 +1,4 @@
-import QtQuick 2.4
-import QtQuick.Window 2.2
+import QtQuick
Rectangle {
id: root
@@ -19,6 +18,7 @@ Rectangle {
property Window extraWindow: Window {
objectName: "extraWindow"
title: "extra window"
+ transientParent: null
Text {
objectName: "extraWindowText"
anchors.centerIn: parent
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index d0f3e4156a..ff3edb3b64 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QDebug>
@@ -27,13 +27,12 @@
#include <QOperatingSystemVersion>
#include <functional>
#include <QtGui/private/qeventpoint_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#if QT_CONFIG(opengl)
#include <QOpenGLContext>
#endif
#if QT_CONFIG(vulkan)
#include <QVulkanInstance>
-#include <QtGui/private/qrhivulkan_p.h>
#endif
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
@@ -547,6 +546,13 @@ private slots:
void graphicsConfiguration();
+ void visibleVsVisibility_data();
+ void visibleVsVisibility();
+
+ void visibilityDoesntClobberWindowState();
+
+ void eventTypes();
+
private:
QPointingDevice *touchDevice; // TODO make const after fixing QTBUG-107864
const QPointingDevice *touchDeviceWithVelocity;
@@ -2924,8 +2930,8 @@ void tst_qquickwindow::test_circleMapItem()
mat->setObjectName("Top Item/MouseArea");
mat->setSize(QSizeF(40, 40));
- QSignalSpy bottomSpy(mab, SIGNAL(clicked(QQuickMouseEvent *)));
- QSignalSpy topSpy(mat, SIGNAL(clicked(QQuickMouseEvent *)));
+ QSignalSpy bottomSpy(mab, SIGNAL(clicked(QQuickMouseEvent*)));
+ QSignalSpy topSpy(mat, SIGNAL(clicked(QQuickMouseEvent*)));
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
@@ -3295,57 +3301,48 @@ public:
*/
bool testFilterPreConditions() const { return !m_filterNotPreAccepted; }
static QVector<DeliveryRecord> &deliveryList() { return m_deliveryList; }
- static QSet<QEvent::Type> &includedEventTypes()
+ static void setExpectedDeliveryList(const QVector<DeliveryRecord> &v) { m_expectedDeliveryList = v; }
+
+ bool isRelevant(QQuickItem *receiver, QEvent *e)
{
- if (m_includedEventTypes.isEmpty())
- m_includedEventTypes << QEvent::MouseButtonPress;
- return m_includedEventTypes;
+ if (receiver->acceptTouchEvents())
+ return e->type() == QEvent::TouchBegin;
+ return e->type() == QEvent::MouseButtonPress;
}
- static void setExpectedDeliveryList(const QVector<DeliveryRecord> &v) { m_expectedDeliveryList = v; }
protected:
bool childMouseEventFilter(QQuickItem *i, QEvent *e) override
{
+ if (!isRelevant(i, e))
+ return QQuickRectangle::childMouseEventFilter(i, e);
+
appendEvent(this, i, e);
- switch (e->type()) {
- case QEvent::MouseButtonPress:
- if (!e->isAccepted())
- m_filterNotPreAccepted = true;
- e->setAccepted(m_filterAccepts);
- // qCDebug(lcTests) << objectName() << i->objectName();
- return m_filterReturns;
- default:
- break;
- }
- return QQuickRectangle::childMouseEventFilter(i, e);
+ if (!e->isAccepted())
+ m_filterNotPreAccepted = true;
+ e->setAccepted(m_filterAccepts);
+ return m_filterReturns;
}
bool event(QEvent *e) override
{
+ if (!isRelevant(this, e))
+ QQuickRectangle::event(e);
+
appendEvent(nullptr, this, e);
- switch (e->type()) {
- case QEvent::MouseButtonPress:
- // qCDebug(lcTests) << objectName();
- e->setAccepted(m_eventAccepts);
- return true;
- default:
- break;
- }
- return QQuickRectangle::event(e);
+ e->setAccepted(m_eventAccepts);
+ return true;
}
private:
static void appendEvent(QQuickItem *filter, QQuickItem *receiver, QEvent *event) {
- if (includedEventTypes().contains(event->type())) {
- auto record = DeliveryRecord(filter ? filter->objectName() : QString(), receiver ? receiver->objectName() : QString());
- int i = m_deliveryList.size();
- if (m_expectedDeliveryList.size() > i && m_expectedDeliveryList[i] == record)
- qCDebug(lcTests).noquote().nospace() << i << ": " << record;
- else
- qCDebug(lcTests).noquote().nospace() << i << ": " << record
- << ", expected " << (m_expectedDeliveryList.size() > i ? m_expectedDeliveryList[i].toString() : QLatin1String("nothing")) << " <---";
- m_deliveryList << record;
- }
+ auto record = DeliveryRecord(filter ? filter->objectName() : QString(), receiver ? receiver->objectName() : QString());
+ int i = m_deliveryList.size();
+ if (m_expectedDeliveryList.size() > i && m_expectedDeliveryList[i] == record)
+ qCDebug(lcTests).noquote().nospace() << i << ": " << record;
+ else
+ qCDebug(lcTests).noquote().nospace() << i << ": " << record
+ << ", expected " << (m_expectedDeliveryList.size() > i ? m_expectedDeliveryList[i].toString() : QLatin1String("nothing")) << " <---";
+ m_deliveryList << record;
}
bool m_eventAccepts;
bool m_filterReturns;
@@ -3355,12 +3352,10 @@ private:
// list of (filtering-parent . receiver) pairs
static DeliveryRecordVector m_expectedDeliveryList;
static DeliveryRecordVector m_deliveryList;
- static QSet<QEvent::Type> m_includedEventTypes;
};
DeliveryRecordVector EventItem::m_expectedDeliveryList;
DeliveryRecordVector EventItem::m_deliveryList;
-QSet<QEvent::Type> EventItem::m_includedEventTypes;
typedef QVector<const char*> CharStarVector;
@@ -3385,11 +3380,17 @@ void tst_qquickwindow::testChildMouseEventFilter_data()
// r0->r1->r2->r3
//
QTest::addColumn<QPoint>("mousePos");
+ QTest::addColumn<QString>("eventMode");
QTest::addColumn<InputState>("inputState");
QTest::addColumn<DeliveryRecordVector>("expectedDeliveryOrder");
- QTest::newRow("if filtered and rejected, do not deliver it to the item that filtered it")
+ for (const QString &eventMode : {"mouse", "touch", "touchToMouse"}) {
+
+ #define desc(txt) qPrintable(QString("%1 events, ").arg(eventMode) + txt)
+
+ QTest::newRow(desc("if filtered and rejected, do not deliver it to the item that filtered it"))
<< QPoint(100, 100)
+ << eventMode
<< InputState({
// | event() | child mouse filter
// +---------+---------+---------+---------
@@ -3407,8 +3408,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data()
<< DeliveryRecord("r1")
);
- QTest::newRow("no filtering, no accepting")
+ QTest::newRow(desc("no filtering, no accepting"))
<< QPoint(100, 100)
+ << eventMode
<< InputState({
// | event() | child mouse filter
// +---------+---------+---------+---------
@@ -3427,8 +3429,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data()
<< DeliveryRecord("root")
);
- QTest::newRow("all filtering, no accepting")
+ QTest::newRow(desc("all filtering, no accepting"))
<< QPoint(100, 100)
+ << eventMode
<< InputState({
// | event() | child mouse filter
// +---------+---------+---------+---------
@@ -3454,8 +3457,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data()
);
- QTest::newRow("some filtering, no accepting")
+ QTest::newRow(desc("some filtering, no accepting"))
<< QPoint(100, 100)
+ << eventMode
<< InputState({
// | event() | child mouse filter
// +---------+---------+---------+---------
@@ -3479,8 +3483,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data()
<< DeliveryRecord("root")
);
- QTest::newRow("r1 accepts")
+ QTest::newRow(desc("r1 accepts"))
<< QPoint(100, 100)
+ << eventMode
<< InputState({
// | event() | child mouse filter
// +---------+---------+---------+---------
@@ -3502,8 +3507,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data()
<< DeliveryRecord("r1")
);
- QTest::newRow("r1 rejects and filters")
+ QTest::newRow(desc("r1 rejects and filters"))
<< QPoint(100, 100)
+ << eventMode
<< InputState({
// | event() | child mouse filter
// +---------+---------+---------+---------
@@ -3526,12 +3532,13 @@ void tst_qquickwindow::testChildMouseEventFilter_data()
<< DeliveryRecord("r0")
<< DeliveryRecord("root")
);
-
+ }
}
void tst_qquickwindow::testChildMouseEventFilter()
{
QFETCH(QPoint, mousePos);
+ QFETCH(QString, eventMode);
QFETCH(InputState, inputState);
QFETCH(DeliveryRecordVector, expectedDeliveryOrder);
@@ -3546,21 +3553,28 @@ void tst_qquickwindow::testChildMouseEventFilter()
QScopedPointer<EventFilter> rootFilter(new EventFilter);
root->installEventFilter(rootFilter.data());
+ const bool useMouseEvents = eventMode == "mouse";
+ const bool acceptTouchEvents = eventMode == "touch";
+
// Create 4 items; each item a child of the previous item.
EventItem *r[4];
r[0] = new EventItem(root);
r[0]->setColor(QColor(0x404040));
r[0]->setWidth(200);
r[0]->setHeight(200);
+ r[0]->setAcceptTouchEvents(acceptTouchEvents);
r[1] = new EventItem(r[0]);
r[1]->setColor(QColor(0x606060));
+ r[1]->setAcceptTouchEvents(acceptTouchEvents);
r[2] = new EventItem(r[1]);
r[2]->setColor(Qt::red);
+ r[2]->setAcceptTouchEvents(acceptTouchEvents);
r[3] = new EventItem(r[2]);
r[3]->setColor(Qt::green);
+ r[3]->setAcceptTouchEvents(acceptTouchEvents);
for (uint i = 0; i < sizeof(r)/sizeof(EventItem*); ++i) {
r[i]->setEventAccepts(inputState.r[i].eventAccepts);
@@ -3577,7 +3591,11 @@ void tst_qquickwindow::testChildMouseEventFilter()
DeliveryRecordVector &actualDeliveryOrder = EventItem::deliveryList();
actualDeliveryOrder.clear();
- QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, mousePos);
+
+ if (useMouseEvents)
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, mousePos);
+ else
+ QTest::touchEvent(&window, touchDevice).press(0, mousePos, &window);
// Check if event got delivered to the root item. If so, append it to the list of items the event got delivered to
if (rootFilter->events.contains(QEvent::MouseButtonPress))
@@ -3593,8 +3611,11 @@ void tst_qquickwindow::testChildMouseEventFilter()
QVERIFY(item->testFilterPreConditions());
}
- // "restore" mouse state
- QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, mousePos);
+ // "restore" mouse/touch state
+ if (useMouseEvents)
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, mousePos);
+ else
+ QTest::touchEvent(&window, touchDevice).release(0, mousePos, &window);
}
void tst_qquickwindow::cleanupGrabsOnRelease()
@@ -3849,7 +3870,7 @@ void tst_qquickwindow::rendererInterfaceWithRenderControl_data()
#ifdef Q_OS_WIN
QTest::newRow("D3D11") << QSGRendererInterface::Direct3D11Rhi;
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
QTest::newRow("Metal") << QSGRendererInterface::MetalRhi;
#endif
}
@@ -4083,6 +4104,87 @@ void tst_qquickwindow::graphicsConfiguration()
#endif
}
+void tst_qquickwindow::visibleVsVisibility_data()
+{
+ QTest::addColumn<QUrl>("qmlfile");
+ QTest::addColumn<bool>("expectVisible");
+ QTest::addColumn<bool>("expectConflictingPropertyWarning");
+
+ QTest::newRow("default invisible") << testFileUrl("window.qml") << false << false;
+ QTest::newRow("just visibility") << testFileUrl("maximized.qml") << true << false;
+ // In these conflicting cases, the 'visibility' property "wins" (see QQuickWindowQmlImpl::setWindowVisibility())
+ QTest::newRow("conflicting invisible") << testFileUrl("conflictingVisibleFalse.qml") << true << true;
+ QTest::newRow("conflicting visible") << testFileUrl("conflictingVisibleTrue.qml") << false << true;
+}
+
+void tst_qquickwindow::visibleVsVisibility()
+{
+ QFETCH(QUrl, qmlfile);
+ QFETCH(bool, expectVisible);
+ QFETCH(bool, expectConflictingPropertyWarning);
+
+ const QString warningMsg = qmlfile.toString() + ":3:1: QML Window: Conflicting properties 'visible' and 'visibility'";
+
+ QTest::failOnWarning(QRegularExpression(".*"));
+ if (expectConflictingPropertyWarning)
+ QTest::ignoreMessage(QtWarningMsg, warningMsg.toUtf8().data());
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(qmlfile);
+ QObject *created = component.create();
+ QScopedPointer<QObject> cleanup(created);
+ QVERIFY(created);
+
+ QQuickWindow *window = qobject_cast<QQuickWindow*>(created);
+ QVERIFY(window);
+ QCOMPARE(window->isVisible(), expectVisible);
+}
+
+void tst_qquickwindow::visibilityDoesntClobberWindowState()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("visibilityDoesntClobberWindowState.qml"));
+ QObject *created = component.create();
+ QScopedPointer<QObject> cleanup(created);
+ QVERIFY(created);
+
+ QQuickWindow *window = qobject_cast<QQuickWindow*>(created);
+ QVERIFY(window);
+
+ window->showMaximized();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+ QCOMPARE(window->windowState(), Qt::WindowMaximized);
+
+ window->setProperty("visible", false);
+ window->setProperty("visible", true);
+
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+ QCOMPARE(window->windowState(), Qt::WindowMaximized);
+
+ EventFilter eventFilter;
+ window->installEventFilter(&eventFilter);
+ window->setProperty("visibility", QWindow::FullScreen);
+ QTRY_VERIFY(eventFilter.events.contains(QEvent::WindowStateChange));
+ QCOMPARE(window->windowState(), Qt::WindowFullScreen);
+
+ eventFilter.events.clear();
+ window->setWindowState(Qt::WindowMaximized);
+ QTRY_VERIFY(eventFilter.events.contains(QEvent::WindowStateChange));
+ QTRY_COMPARE(window->windowState(), Qt::WindowMaximized);
+}
+
+void tst_qquickwindow::eventTypes()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("eventTypes.qml"));
+ QObject *created = component.create();
+ QScopedPointer<QObject> cleanup(created);
+ QVERIFY(created);
+}
+
QTEST_MAIN(tst_qquickwindow)
#include "tst_qquickwindow.moc"
diff --git a/tests/auto/quick/qquickwindowcontainer/CMakeLists.txt b/tests/auto/quick/qquickwindowcontainer/CMakeLists.txt
new file mode 100644
index 0000000000..6a02080cfc
--- /dev/null
+++ b/tests/auto/quick/qquickwindowcontainer/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickwindowcontainer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qquickwindowcontainer
+ SOURCES
+ tst_qquickwindowcontainer.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickPrivate
+ Qt::QuickTestUtilsPrivate
+ TESTDATA ${test_data}
+)
+
+if(ANDROID OR IOS)
+ set(TESTDATA_DIR ":/data")
+else()
+ set(TESTDATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/data")
+endif()
+
+qt_internal_extend_target(tst_qquickwindowcontainer
+ DEFINES
+ QT_QMLTEST_DATADIR="${TESTDATA_DIR}"
+)
diff --git a/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_container.qml b/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_container.qml
new file mode 100644
index 0000000000..3e0bd8ae1c
--- /dev/null
+++ b/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_container.qml
@@ -0,0 +1,15 @@
+import QtQuick
+
+Window {
+ id: topLevel
+ Item {
+ id: childItem
+ x: 100; y: 100
+
+ WindowContainer {
+ window: Window {
+ objectName: "childWindow"
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_item.qml b/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_item.qml
new file mode 100644
index 0000000000..301c5f1494
--- /dev/null
+++ b/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_item.qml
@@ -0,0 +1,15 @@
+import QtQuick
+
+Window {
+ id: topLevel
+ Item {
+ id: childItem
+ x: 100; y: 100
+
+ Window {
+ parent: childItem
+ objectName: "childWindow"
+ x: 100; y: 100
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_window.qml b/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_window.qml
new file mode 100644
index 0000000000..d6d2c9dff0
--- /dev/null
+++ b/tests/auto/quick/qquickwindowcontainer/data/basicFunctionality_window.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Window {
+ id: topLevel
+ Window {
+ parent: topLevel
+ objectName: "childWindow"
+ x: 100; y: 100
+ }
+}
diff --git a/tests/auto/quick/qquickwindowcontainer/data/deferredVisibilityWithoutWindow.qml b/tests/auto/quick/qquickwindowcontainer/data/deferredVisibilityWithoutWindow.qml
new file mode 100644
index 0000000000..3bd882defe
--- /dev/null
+++ b/tests/auto/quick/qquickwindowcontainer/data/deferredVisibilityWithoutWindow.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Window {
+ id: topLevel
+ visible: true
+ Window {
+ parent: topLevel
+ objectName: "childWindow"
+ visible: true
+ }
+}
diff --git a/tests/auto/quick/qquickwindowcontainer/data/windowComponent.qml b/tests/auto/quick/qquickwindowcontainer/data/windowComponent.qml
new file mode 100644
index 0000000000..48a067030b
--- /dev/null
+++ b/tests/auto/quick/qquickwindowcontainer/data/windowComponent.qml
@@ -0,0 +1,28 @@
+import QtQuick
+
+Window {
+ Item { id: itemParent; objectName: "itemParent" }
+ Window { id: windowParent; objectName: "windowParent" }
+
+ property QtObject window_item: null
+ property QtObject window_window: null
+
+ property QtObject window_item_parent: null
+ property QtObject window_window_parent: null
+
+ Component { id: windowComponent; Window{} }
+
+ Component.onCompleted: {
+ // Windows created with QObjects, Items, or Windows as their parent
+ // do not their visual parent set via auto-parenting -- only their
+ // transient parent.
+ window_item = windowComponent.createObject(itemParent);
+ window_window = windowComponent.createObject(windowParent);
+
+ // A visual parent must be set explicitly
+ window_item_parent = windowComponent.createObject(itemParent);
+ window_item_parent.parent = itemParent;
+ window_window_parent = windowComponent.createObject(windowParent);
+ window_window_parent.parent = windowParent;
+ }
+}
diff --git a/tests/auto/quick/qquickwindowcontainer/tst_qquickwindowcontainer.cpp b/tests/auto/quick/qquickwindowcontainer/tst_qquickwindowcontainer.cpp
new file mode 100644
index 0000000000..c702cf96dc
--- /dev/null
+++ b/tests/auto/quick/qquickwindowcontainer/tst_qquickwindowcontainer.cpp
@@ -0,0 +1,192 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/qtest.h>
+#include <QtTest/qsignalspy.h>
+
+#include <QtQuickTest/QtQuickTest>
+
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlapplicationengine.h>
+
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/private/qquickwindowcontainer_p.h>
+
+class tst_QQuickWindowContainer : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_QQuickWindowContainer()
+ : QQmlDataTest(QT_QMLTEST_DATADIR)
+ {
+ }
+
+private slots:
+ void init() override;
+ void cleanup();
+
+ void basicFunctionality_data();
+ void basicFunctionality();
+
+ void windowDestroyed();
+ void windowLifetimeFollowsContainer();
+ void deferredVisibilityWithoutWindow();
+ void windowComponent();
+
+private:
+ std::unique_ptr<QQmlApplicationEngine> m_engine;
+};
+
+void tst_QQuickWindowContainer::init()
+{
+ QQmlDataTest::init();
+
+ QString testFile = QTest::currentTestFunction();
+ if (auto *dataTag = QTest::currentDataTag())
+ testFile += QChar('_') % dataTag;
+ testFile += ".qml";
+
+ const auto testUrl = testFileUrl(testFile);
+ if (QFileInfo::exists(QQmlFile::urlToLocalFileOrQrc(testUrl))) {
+ m_engine.reset(new QQmlApplicationEngine(testUrl));
+ QVERIFY(m_engine->rootObjects().size() > 0);
+ }
+}
+
+void tst_QQuickWindowContainer::cleanup()
+{
+ m_engine.reset(nullptr);
+}
+
+void tst_QQuickWindowContainer::basicFunctionality_data()
+{
+ QTest::addColumn<QPoint>("position");
+
+ QTest::newRow("window") << QPoint(100, 100);
+ QTest::newRow("item") << QPoint(200, 200);
+ QTest::newRow("container") << QPoint(100, 100);
+}
+
+void tst_QQuickWindowContainer::basicFunctionality()
+{
+ QFETCH(QPoint, position);
+
+ auto *topLevelWindow = qobject_cast<QQuickWindow*>(m_engine->rootObjects().first());
+ auto *childWindow = topLevelWindow->findChild<QWindow*>("childWindow");
+ QVERIFY(childWindow);
+
+ // The top level isn't visible yet, so there hasn't been any
+ // polish, which we rely on for the actual reparenting.
+ QCOMPARE(childWindow->parent(), nullptr);
+
+ topLevelWindow->setVisible(true);
+ QVERIFY(QQuickTest::qWaitForPolish(topLevelWindow));
+ QCOMPARE(childWindow->parent(), topLevelWindow);
+
+ QCOMPARE(childWindow->position(), position);
+}
+
+void tst_QQuickWindowContainer::windowDestroyed()
+{
+ std::unique_ptr<QWindow> window(new QWindow);
+
+ QQuickWindowContainer container;
+ QSignalSpy spy(&container, &QQuickWindowContainer::containedWindowChanged);
+
+ container.setContainedWindow(window.get());
+ QCOMPARE(container.containedWindow(), window.get());
+ QCOMPARE(spy.size(), 1);
+
+ window.reset(nullptr);
+
+ QVERIFY(!container.containedWindow());
+ QCOMPARE(spy.size(), 2);
+}
+
+void tst_QQuickWindowContainer::windowLifetimeFollowsContainer()
+{
+ QWindow window;
+ QPointer<QWindow> windowGuard = &window;
+
+ QQuickWindowContainer container;
+ container.setContainedWindow(&window);
+
+ {
+ QQuickWindow quickWindow;
+ container.setParentItem(quickWindow.contentItem());
+ quickWindow.show();
+ QVERIFY(QQuickTest::qWaitForPolish(&quickWindow));
+ QCOMPARE(window.parent(), &quickWindow);
+
+ // Decouple container from Quick window
+ container.setParentItem(nullptr);
+ }
+
+ QVERIFY(windowGuard);
+}
+
+void tst_QQuickWindowContainer::deferredVisibilityWithoutWindow()
+{
+ auto *topLevelWindow = qobject_cast<QQuickWindow*>(m_engine->rootObjects().first());
+ auto *childWindow = topLevelWindow->findChild<QQuickWindow*>("childWindow");
+ QVERIFY(childWindow);
+ QVERIFY(QQuickTest::qWaitForPolish(topLevelWindow));
+
+ QSignalSpy spy(childWindow, &QWindow::visibleChanged);
+ m_engine.reset(nullptr);
+
+ // Deleting the engine should only hide the window once,
+ // not hide, show, and then hide again, which would be
+ // the result of applying visibility without a window.
+ QCOMPARE(spy.count(), 1);
+}
+
+void tst_QQuickWindowContainer::windowComponent()
+{
+ auto *root = qobject_cast<QQuickWindow *>(m_engine->rootObjects().first());
+ QVERIFY(root);
+ auto *itemParent = root->findChild<QQuickItem*>("itemParent");
+ auto *windowParent = root->findChild<QQuickWindow*>("windowParent");
+ QVERIFY(itemParent);
+ QVERIFY(windowParent);
+
+ root->setVisible(true);
+ QVERIFY(QQuickTest::qWaitForPolish(root));
+ windowParent->setVisible(true);
+ QVERIFY(QQuickTest::qWaitForPolish(windowParent));
+
+ QObject *window_item = root->property("window_item").value<QObject*>();
+ QObject *window_window = root->property("window_window").value<QObject*>();
+ QObject *window_item_parent = root->property("window_item_parent").value<QObject*>();
+ QObject *window_window_parent = root->property("window_window_parent").value<QObject*>();
+
+ QVERIFY(window_item);
+ QVERIFY(window_window);
+ QVERIFY(window_item_parent);
+ QVERIFY(window_window_parent);
+
+ QCOMPARE(window_item->parent(), itemParent);
+ QCOMPARE(window_window->parent(), windowParent);
+ QCOMPARE(window_item_parent->parent(), root);
+ QCOMPARE(window_window_parent->parent(), windowParent);
+
+ QCOMPARE(qobject_cast<QQuickWindow *>(window_item)->transientParent(), root);
+ QCOMPARE(qobject_cast<QQuickWindow *>(window_window)->transientParent(), windowParent);
+ QCOMPARE(qobject_cast<QQuickWindow *>(window_item)->parent(), nullptr);
+ QCOMPARE(qobject_cast<QQuickWindow *>(window_window)->parent(), nullptr);
+
+ QEXPECT_FAIL("", "The automatic transient parent logic doesn't account for visual parent", Continue);
+ QCOMPARE(qobject_cast<QQuickWindow *>(window_item_parent)->transientParent(), nullptr);
+ QEXPECT_FAIL("", "The automatic transient parent logic doesn't account for visual parent", Continue);
+ QCOMPARE(qobject_cast<QQuickWindow *>(window_window_parent)->transientParent(), nullptr);
+ QCOMPARE(qobject_cast<QQuickWindow *>(window_item_parent)->parent(), root);
+ QCOMPARE(qobject_cast<QQuickWindow *>(window_window_parent)->parent(), windowParent);
+}
+
+QTEST_MAIN(tst_QQuickWindowContainer)
+
+#include "tst_qquickwindowcontainer.moc"
diff --git a/tests/auto/quick/rendernode/CMakeLists.txt b/tests/auto/quick/rendernode/CMakeLists.txt
index 44d1026bf8..db5fac6439 100644
--- a/tests/auto/quick/rendernode/CMakeLists.txt
+++ b/tests/auto/quick/rendernode/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_rendernode Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_rendernode LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/rendernode/data/glsimple.qml b/tests/auto/quick/rendernode/data/glsimple.qml
index 18cc98d6d0..da038476b3 100644
--- a/tests/auto/quick/rendernode/data/glsimple.qml
+++ b/tests/auto/quick/rendernode/data/glsimple.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import Test
diff --git a/tests/auto/quick/rendernode/data/simple.qml b/tests/auto/quick/rendernode/data/simple.qml
index 7a55c742c4..2194d3484f 100644
--- a/tests/auto/quick/rendernode/data/simple.qml
+++ b/tests/auto/quick/rendernode/data/simple.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import Test
diff --git a/tests/auto/quick/rendernode/tst_rendernode.cpp b/tests/auto/quick/rendernode/tst_rendernode.cpp
index 44e1123cd7..b21c1f01b1 100644
--- a/tests/auto/quick/rendernode/tst_rendernode.cpp
+++ b/tests/auto/quick/rendernode/tst_rendernode.cpp
@@ -1,12 +1,12 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include <private/qsgrendernode_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
diff --git a/tests/auto/quick/scenegraph/BLACKLIST b/tests/auto/quick/scenegraph/BLACKLIST
deleted file mode 100644
index 80f39de9ab..0000000000
--- a/tests/auto/quick/scenegraph/BLACKLIST
+++ /dev/null
@@ -1,3 +0,0 @@
-# QTBUG-102589
-[render]
-android
diff --git a/tests/auto/quick/scenegraph/CMakeLists.txt b/tests/auto/quick/scenegraph/CMakeLists.txt
index ba6c362222..c8d8ad34c1 100644
--- a/tests/auto/quick/scenegraph/CMakeLists.txt
+++ b/tests/auto/quick/scenegraph/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_scenegraph Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_scenegraph LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -26,6 +32,16 @@ qt_internal_add_test(tst_scenegraph
TESTDATA ${test_data}
)
+qt_internal_add_shaders(tst_scenegraph "shaders"
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ "/"
+ FILES
+ "data/render_bug37422.frag"
+)
+
#### Keys ignored in scope 1:.:.:scenegraph.pro:<TRUE>:
# OTHER_FILES = "data/render_OutOfFloatRange.qml" "data/simple.qml" "data/render_ImageFiltering.qml"
diff --git a/tests/auto/quick/scenegraph/data/RenderTestBase.qml b/tests/auto/quick/scenegraph/data/RenderTestBase.qml
index 8369ddf25b..c753c76512 100644
--- a/tests/auto/quick/scenegraph/data/RenderTestBase.qml
+++ b/tests/auto/quick/scenegraph/data/RenderTestBase.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/manyWindows_dftext.qml b/tests/auto/quick/scenegraph/data/manyWindows_dftext.qml
index 7952799bc1..4e0107c416 100644
--- a/tests/auto/quick/scenegraph/data/manyWindows_dftext.qml
+++ b/tests/auto/quick/scenegraph/data/manyWindows_dftext.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/manyWindows_image.qml b/tests/auto/quick/scenegraph/data/manyWindows_image.qml
index 53e8991559..8db982ce52 100644
--- a/tests/auto/quick/scenegraph/data/manyWindows_image.qml
+++ b/tests/auto/quick/scenegraph/data/manyWindows_image.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/manyWindows_ntext.qml b/tests/auto/quick/scenegraph/data/manyWindows_ntext.qml
index 62d676c124..9792a0c3ab 100644
--- a/tests/auto/quick/scenegraph/data/manyWindows_ntext.qml
+++ b/tests/auto/quick/scenegraph/data/manyWindows_ntext.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/manyWindows_rects.qml b/tests/auto/quick/scenegraph/data/manyWindows_rects.qml
index 29cdb4959d..d2b9ace55f 100644
--- a/tests/auto/quick/scenegraph/data/manyWindows_rects.qml
+++ b/tests/auto/quick/scenegraph/data/manyWindows_rects.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/renderControl_rect.qml b/tests/auto/quick/scenegraph/data/renderControl_rect.qml
index e2c9c07430..3c24bf57fd 100644
--- a/tests/auto/quick/scenegraph/data/renderControl_rect.qml
+++ b/tests/auto/quick/scenegraph/data/renderControl_rect.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml b/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml
index a586d7417b..e9a72cfddd 100644
--- a/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml
+++ b/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quick/scenegraph/data/render_BreakOpacityBatch.qml b/tests/auto/quick/scenegraph/data/render_BreakOpacityBatch.qml
index ff9e1f7412..9f4fd9b482 100644
--- a/tests/auto/quick/scenegraph/data/render_BreakOpacityBatch.qml
+++ b/tests/auto/quick/scenegraph/data/render_BreakOpacityBatch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/render_DrawSets.qml b/tests/auto/quick/scenegraph/data/render_DrawSets.qml
index cf3f68db8c..ab1a50098b 100644
--- a/tests/auto/quick/scenegraph/data/render_DrawSets.qml
+++ b/tests/auto/quick/scenegraph/data/render_DrawSets.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
import SceneGraphTest 1.0
diff --git a/tests/auto/quick/scenegraph/data/render_ImageFiltering.qml b/tests/auto/quick/scenegraph/data/render_ImageFiltering.qml
index a52265ab0c..1a2d1b2e1c 100644
--- a/tests/auto/quick/scenegraph/data/render_ImageFiltering.qml
+++ b/tests/auto/quick/scenegraph/data/render_ImageFiltering.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/render_Mipmap.qml b/tests/auto/quick/scenegraph/data/render_Mipmap.qml
index fd4f689b74..858f4b2fef 100644
--- a/tests/auto/quick/scenegraph/data/render_Mipmap.qml
+++ b/tests/auto/quick/scenegraph/data/render_Mipmap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
import QtQuick.Window 2.2
diff --git a/tests/auto/quick/scenegraph/data/render_MovingOverlap.qml b/tests/auto/quick/scenegraph/data/render_MovingOverlap.qml
index 7b1cef32f2..15cc358023 100644
--- a/tests/auto/quick/scenegraph/data/render_MovingOverlap.qml
+++ b/tests/auto/quick/scenegraph/data/render_MovingOverlap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/render_OpacityThroughBatchRoot.qml b/tests/auto/quick/scenegraph/data/render_OpacityThroughBatchRoot.qml
index 49f9fbf77b..a26ba374a6 100644
--- a/tests/auto/quick/scenegraph/data/render_OpacityThroughBatchRoot.qml
+++ b/tests/auto/quick/scenegraph/data/render_OpacityThroughBatchRoot.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/render_OutOfFloatRange.qml b/tests/auto/quick/scenegraph/data/render_OutOfFloatRange.qml
index c80256dfb2..a4262ed1f7 100644
--- a/tests/auto/quick/scenegraph/data/render_OutOfFloatRange.qml
+++ b/tests/auto/quick/scenegraph/data/render_OutOfFloatRange.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/render_Overlap.qml b/tests/auto/quick/scenegraph/data/render_Overlap.qml
index 2b2cc0b6ea..fafe103ef6 100644
--- a/tests/auto/quick/scenegraph/data/render_Overlap.qml
+++ b/tests/auto/quick/scenegraph/data/render_Overlap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/render_StackingOrder.qml b/tests/auto/quick/scenegraph/data/render_StackingOrder.qml
index 562670f624..416a5db329 100644
--- a/tests/auto/quick/scenegraph/data/render_StackingOrder.qml
+++ b/tests/auto/quick/scenegraph/data/render_StackingOrder.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb b/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb
deleted file mode 100644
index b8770b1332..0000000000
--- a/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb
+++ /dev/null
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/render_bug37422.qml b/tests/auto/quick/scenegraph/data/render_bug37422.qml
index 440159aef0..d9b294c766 100644
--- a/tests/auto/quick/scenegraph/data/render_bug37422.qml
+++ b/tests/auto/quick/scenegraph/data/render_bug37422.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
@@ -48,7 +48,7 @@ RenderTestBase
y: 10
fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL
? "varying highp vec2 qt_TexCoord0; void main() { gl_FragColor = vec4(1, 0, 0, 1); }"
- : "file:data/render_bug37422.frag.qsb"
+ : "qrc:/data/render_bug37422.frag.qsb"
Rectangle {
width: 5
diff --git a/tests/auto/quick/scenegraph/data/simple.qml b/tests/auto/quick/scenegraph/data/simple.qml
index 97009b451d..a8eb2ad516 100644
--- a/tests/auto/quick/scenegraph/data/simple.qml
+++ b/tests/auto/quick/scenegraph/data/simple.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.2
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
index 64c50f74ae..1d76fe3181 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
@@ -96,6 +96,7 @@ private slots:
void createTextureFromImage();
void withAdoptedRhi();
void resizeTextureFromImage();
+ void textureNativeInterface();
private:
QQuickView *createView(const QString &file, QWindow *parent = nullptr, int x = -1, int y = -1, int w = -1, int h = -1);
@@ -289,7 +290,9 @@ struct Sample {
}
bool check(const QImage &image, qreal scale) {
- QColor color(image.pixel(x * scale, y * scale));
+ const int scaledX = qRound(x * scale);
+ const int scaledY = qRound(y * scale);
+ const QColor color(image.pixel(scaledX, scaledY));
return qAbs(color.redF() - r) <= tolerance
&& qAbs(color.greenF() - g) <= tolerance
&& qAbs(color.blueF() - b) <= tolerance;
@@ -370,16 +373,16 @@ void tst_SceneGraph::render_data()
QRegularExpression baseSamples("#base: *(\\d+) *(\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+)");
QRegularExpression finalSamples("#final: *(\\d+) *(\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+) *(\\d\\.\\d+)");
- foreach (QString fileName, files) {
+ for (QString fileName : std::as_const(files)) {
QFile file(testFile(fileName));
if (!file.open(QFile::ReadOnly)) {
qFatal("render_data: QFile::open failed! file=%s, error=%s",
qPrintable(fileName), qPrintable(file.errorString()));
}
- QStringList contents = QString::fromLatin1(file.readAll()).split(QLatin1Char('\n'));
+ const QStringList contents = QString::fromLatin1(file.readAll()).split(QLatin1Char('\n'));
int samples = -1;
- foreach (QString line, contents) {
+ for (const QString &line : contents) {
auto match = sampleCount.match(line);
if (match.hasMatch()) {
samples = match.captured(1).toInt();
@@ -390,7 +393,7 @@ void tst_SceneGraph::render_data()
qFatal("render_data: failed to find string '#samples: [count], file=%s", qPrintable(fileName));
QList<Sample> baseStage, finalStage;
- foreach (QString line, contents) {
+ for (const QString &line : contents) {
auto match = baseSamples.match(line);
if (match.hasMatch())
baseStage << sample_from_regexp(&match);
@@ -430,6 +433,10 @@ void tst_SceneGraph::render()
// ideal world.
// Just keep this in mind when writing tests.
qreal scale = view.devicePixelRatio();
+ const bool isIntegerScale = qFuzzyIsNull(qreal(qFloor(scale)) - scale);
+
+ if (file == "render_OutOfFloatRange.qml" && !isIntegerScale)
+ QSKIP("render_OutOfFloatRange doesn't work with non-integer scaling factors");
// Grab the window and check all our base stage samples
QImage content = view.grabWindow();
@@ -777,6 +784,59 @@ void tst_SceneGraph::resizeTextureFromImage()
TestOffscreenScene::cleanup();
}
+void tst_SceneGraph::textureNativeInterface()
+{
+ if (!isRunningOnRhi())
+ QSKIP("Skipping texture native interface tests due to not running with QRhi");
+
+ // test it offscreen because we want to do QSG stuff here on the main thread
+ QScopedPointer<TestOffscreenScene> scene(createOffscreenScene(testFileUrl(QLatin1String("renderControl_rect.qml"))));
+ QVERIFY(scene->renderControl && scene->window && scene->rootItem);
+
+ QImage image(512, 512, QImage::Format_RGBA8888);
+
+ QScopedPointer<QSGTexture> texture(scene->window->createTextureFromImage(image, QQuickWindow::TextureHasAlphaChannel));
+ QVERIFY(texture.data());
+
+ commitTexture(scene->window->rhi(), texture.data());
+
+#if QT_CONFIG(metal)
+ if (scene->window->graphicsApi() == QSGRendererInterface::Metal) {
+ auto texNatIf = texture->nativeInterface<QNativeInterface::QSGMetalTexture>();
+ QVERIFY(texNatIf);
+ QVERIFY(texNatIf->nativeTexture());
+ }
+#endif
+
+#if defined(Q_OS_WIN)
+ if (scene->window->graphicsApi() == QSGRendererInterface::Direct3D11) {
+ auto texNatIf = texture->nativeInterface<QNativeInterface::QSGD3D11Texture>();
+ QVERIFY(texNatIf);
+ QVERIFY(texNatIf->nativeTexture());
+ } else if (scene->window->graphicsApi() == QSGRendererInterface::Direct3D12) {
+ auto texNatIf = texture->nativeInterface<QNativeInterface::QSGD3D12Texture>();
+ QVERIFY(texNatIf);
+ QVERIFY(texNatIf->nativeTexture());
+ }
+#endif
+
+#if QT_CONFIG(opengl)
+ if (scene->window->graphicsApi() == QSGRendererInterface::OpenGL) {
+ auto texNatIf = texture->nativeInterface<QNativeInterface::QSGOpenGLTexture>();
+ QVERIFY(texNatIf);
+ QVERIFY(texNatIf->nativeTexture());
+ }
+#endif
+
+#if QT_CONFIG(vulkan)
+ if (scene->window->graphicsApi() == QSGRendererInterface::Vulkan) {
+ auto texNatIf = texture->nativeInterface<QNativeInterface::QSGVulkanTexture>();
+ QVERIFY(texNatIf);
+ QVERIFY(texNatIf->nativeImage());
+ }
+#endif
+}
+
bool tst_SceneGraph::isRunningOnRhi()
{
static bool retval = false;
diff --git a/tests/auto/quick/sharedimage/CMakeLists.txt b/tests/auto/quick/sharedimage/CMakeLists.txt
index 642f55b0b9..c6d71896c6 100644
--- a/tests/auto/quick/sharedimage/CMakeLists.txt
+++ b/tests/auto/quick/sharedimage/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_sharedimage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_sharedimage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/sharedimage/tst_sharedimage.cpp b/tests/auto/quick/sharedimage/tst_sharedimage.cpp
index cdc5663710..e3d87f68dd 100644
--- a/tests/auto/quick/sharedimage/tst_sharedimage.cpp
+++ b/tests/auto/quick/sharedimage/tst_sharedimage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <private/qquickimage_p.h>
diff --git a/tests/auto/quick/softwarerenderer/CMakeLists.txt b/tests/auto/quick/softwarerenderer/CMakeLists.txt
index af44ced1c1..9e8433f8df 100644
--- a/tests/auto/quick/softwarerenderer/CMakeLists.txt
+++ b/tests/auto/quick/softwarerenderer/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_softwarerenderer Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_softwarerenderer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_softwarerenderer
SOURCES
tst_softwarerenderer.cpp
diff --git a/tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp b/tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp
index 2d2581a3fa..034c6b8139 100644
--- a/tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp
+++ b/tests/auto/quick/softwarerenderer/tst_softwarerenderer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 zccrs <zccrs@live.com>, JiDe Zhang <zhangjide@uniontech.com>.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuick>
#include <QtQml>
diff --git a/tests/auto/quick/touchmouse/BLACKLIST b/tests/auto/quick/touchmouse/BLACKLIST
index 214da65864..aaf5b8e95d 100644
--- a/tests/auto/quick/touchmouse/BLACKLIST
+++ b/tests/auto/quick/touchmouse/BLACKLIST
@@ -9,5 +9,6 @@ windows gcc developer-build
[touchButtonOnFlickable]
windows gcc developer-build
-[hoverEnabled]
-* # QTBUG-86729
+# QTBUG-118059
+[tapOnDismissiveTopMouseAreaClicksBottomOne]
+opensuse-leap
diff --git a/tests/auto/quick/touchmouse/CMakeLists.txt b/tests/auto/quick/touchmouse/CMakeLists.txt
index e664784da2..bca541bbfb 100644
--- a/tests/auto/quick/touchmouse/CMakeLists.txt
+++ b/tests/auto/quick/touchmouse/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_touchmouse Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_touchmouse LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
index 0d1256e368..501d996797 100644
--- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp
+++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -713,7 +713,7 @@ void tst_TouchMouse::touchButtonOnFlickable()
QTRY_COMPARE(eventItem2->touchUngrabCount, 1);
qCDebug(lcTests) << "expected delivered events: press(touch) move(touch)" << eventItem2->eventList;
- QCOMPARE(eventItem2->eventList.size(), 2);
+ QCOMPARE(eventItem2->eventList.size(), 3);
QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate);
QCOMPARE(grabMonitor.exclusiveGrabber, flickable);
// both EventItem and Flickable handled the actual touch, so synth-mouse doesn't happen
@@ -1440,6 +1440,7 @@ void tst_TouchMouse::hoverEnabled() // QTBUG-40856
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("hoverMouseAreas.qml")));
QQuickItem *root = window.rootObject();
+ auto deliveryAgent = QQuickWindowPrivate::get(&window)->deliveryAgentPrivate();
QQuickMouseArea *mouseArea1 = root->findChild<QQuickMouseArea*>("mouseArea1");
QVERIFY(mouseArea1 != nullptr);
@@ -1449,11 +1450,11 @@ void tst_TouchMouse::hoverEnabled() // QTBUG-40856
QSignalSpy enterSpy1(mouseArea1, SIGNAL(entered()));
QSignalSpy exitSpy1(mouseArea1, SIGNAL(exited()));
- QSignalSpy clickSpy1(mouseArea1, SIGNAL(clicked(QQuickMouseEvent *)));
+ QSignalSpy clickSpy1(mouseArea1, SIGNAL(clicked(QQuickMouseEvent*)));
QSignalSpy enterSpy2(mouseArea2, SIGNAL(entered()));
QSignalSpy exitSpy2(mouseArea2, SIGNAL(exited()));
- QSignalSpy clickSpy2(mouseArea2, SIGNAL(clicked(QQuickMouseEvent *)));
+ QSignalSpy clickSpy2(mouseArea2, SIGNAL(clicked(QQuickMouseEvent*)));
QPoint p1(150, 150);
QPoint p2(150, 250);
@@ -1467,6 +1468,7 @@ void tst_TouchMouse::hoverEnabled() // QTBUG-40856
// ------------------------- Touch click on mouseArea1
QTest::touchEvent(&window, device).press(0, p1, &window);
+ deliveryAgent->flushFrameSynchronousEvents(&window);
QCOMPARE(enterSpy1.size(), 1);
QCOMPARE(enterSpy2.size(), 0);
@@ -1475,37 +1477,42 @@ void tst_TouchMouse::hoverEnabled() // QTBUG-40856
QVERIFY(!mouseArea2->hovered());
QTest::touchEvent(&window, device).release(0, p1, &window);
+ deliveryAgent->flushFrameSynchronousEvents(&window);
QVERIFY(clickSpy1.size() == 1);
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
// ------------------------- Touch click on mouseArea2
QTest::touchEvent(&window, device).press(0, p2, &window);
+ deliveryAgent->flushFrameSynchronousEvents(&window);
- QVERIFY(mouseArea1->hovered());
+ QVERIFY(!mouseArea1->hovered());
QVERIFY(mouseArea2->hovered());
QVERIFY(mouseArea2->isPressed());
QCOMPARE(enterSpy1.size(), 1);
QCOMPARE(enterSpy2.size(), 1);
QTest::touchEvent(&window, device).release(0, p2, &window);
+ deliveryAgent->flushFrameSynchronousEvents(&window);
QVERIFY(clickSpy2.size() == 1);
- QVERIFY(mouseArea1->hovered());
+ QVERIFY(!mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
- QCOMPARE(exitSpy1.size(), 0);
+ QCOMPARE(exitSpy1.size(), 1);
QCOMPARE(exitSpy2.size(), 1);
// ------------------------- Another touch click on mouseArea1
QTest::touchEvent(&window, device).press(0, p1, &window);
+ deliveryAgent->flushFrameSynchronousEvents(&window);
- QCOMPARE(enterSpy1.size(), 1);
+ QCOMPARE(enterSpy1.size(), 2);
QCOMPARE(enterSpy2.size(), 1);
QVERIFY(mouseArea1->isPressed());
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
QTest::touchEvent(&window, device).release(0, p1, &window);
+ deliveryAgent->flushFrameSynchronousEvents(&window);
QCOMPARE(clickSpy1.size(), 2);
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea1->isPressed());
diff --git a/tests/auto/quickcontrols/CMakeLists.txt b/tests/auto/quickcontrols/CMakeLists.txt
index 6d33fb1ae9..f4f2e6b2c6 100644
--- a/tests/auto/quickcontrols/CMakeLists.txt
+++ b/tests/auto/quickcontrols/CMakeLists.txt
@@ -10,7 +10,9 @@ endif()
add_subdirectory(cursor)
add_subdirectory(customization)
add_subdirectory(deferred)
-add_subdirectory(designer)
+if(QT_FEATURE_quick_designer)
+ add_subdirectory(designer)
+endif()
if(NOT ANDROID) # QTBUG-100258
add_subdirectory(focus)
endif()
@@ -32,6 +34,7 @@ add_subdirectory(qquickmaterialstyleconf)
add_subdirectory(qquickmenu)
add_subdirectory(qquickmenubar)
add_subdirectory(qquickninepatchimage)
+add_subdirectory(qquickoverlay)
add_subdirectory(qquickpopup)
add_subdirectory(qquickstyle)
add_subdirectory(qquicktextarea)
diff --git a/tests/auto/quickcontrols/accessibility/CMakeLists.txt b/tests/auto/quickcontrols/accessibility/CMakeLists.txt
index 3831c5863d..e1221950bc 100644
--- a/tests/auto/quickcontrols/accessibility/CMakeLists.txt
+++ b/tests/auto/quickcontrols/accessibility/CMakeLists.txt
@@ -3,14 +3,14 @@
# Generated from accessibility.pro.
-if(NOT QT_FEATURE_accessibility)
- return()
-endif()
-
-if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_accessibility LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+if(NOT QT_FEATURE_accessibility)
+ return()
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp b/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
index 40ee98691d..9774bf4e07 100644
--- a/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
+++ b/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quickcontrols/cmake/CMakeLists.txt b/tests/auto/quickcontrols/cmake/CMakeLists.txt
deleted file mode 100644
index 93ecd12386..0000000000
--- a/tests/auto/quickcontrols/cmake/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-cmake_minimum_required(VERSION 3.16)
-
-project(qmake_cmake_files)
-
-enable_testing()
-
-find_package(Qt5Core REQUIRED)
-
-include("${_Qt5CTestMacros}")
-
-test_module_includes(
- QuickControls2 QQuickStyle
-)
diff --git a/tests/auto/quickcontrols/controls/basic/CMakeLists.txt b/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
index dabab76bbe..cc991ca165 100644
--- a/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/basic/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_basic LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/controls/basic/tst_basic.cpp b/tests/auto/quickcontrols/controls/basic/tst_basic.cpp
index 47cd9a393e..33417cca55 100644
--- a/tests/auto/quickcontrols/controls/basic/tst_basic.cpp
+++ b/tests/auto/quickcontrols/controls/basic/tst_basic.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
diff --git a/tests/auto/quickcontrols/controls/data/SignalSequenceSpy.qml b/tests/auto/quickcontrols/controls/data/SignalSequenceSpy.qml
index 5e536248a8..879e7e546f 100644
--- a/tests/auto/quickcontrols/controls/data/SignalSequenceSpy.qml
+++ b/tests/auto/quickcontrols/controls/data/SignalSequenceSpy.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quickcontrols/controls/data/TestItem.qml b/tests/auto/quickcontrols/controls/data/TestItem.qml
index c36ddc2f11..7f01b2231a 100644
--- a/tests/auto/quickcontrols/controls/data/TestItem.qml
+++ b/tests/auto/quickcontrols/controls/data/TestItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/auto/quickcontrols/controls/data/TumblerDatePicker.qml b/tests/auto/quickcontrols/controls/data/TumblerDatePicker.qml
index c1b12e8fbc..bb24557fe2 100644
--- a/tests/auto/quickcontrols/controls/data/TumblerDatePicker.qml
+++ b/tests/auto/quickcontrols/controls/data/TumblerDatePicker.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/controls/data/TumblerListView.qml b/tests/auto/quickcontrols/controls/data/TumblerListView.qml
index a794f255db..ccc44cfe15 100644
--- a/tests/auto/quickcontrols/controls/data/TumblerListView.qml
+++ b/tests/auto/quickcontrols/controls/data/TumblerListView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/controls/data/TumblerPathView.qml b/tests/auto/quickcontrols/controls/data/TumblerPathView.qml
index 26de69dab2..971369a2dd 100644
--- a/tests/auto/quickcontrols/controls/data/TumblerPathView.qml
+++ b/tests/auto/quickcontrols/controls/data/TumblerPathView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/controls/data/splitview/fillItemInMiddle.qml b/tests/auto/quickcontrols/controls/data/splitview/fillItemInMiddle.qml
index 1849e66e1c..63e0028220 100644
--- a/tests/auto/quickcontrols/controls/data/splitview/fillItemInMiddle.qml
+++ b/tests/auto/quickcontrols/controls/data/splitview/fillItemInMiddle.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/controls/data/splitview/fillItemOnLeft.qml b/tests/auto/quickcontrols/controls/data/splitview/fillItemOnLeft.qml
index 8307a05951..815e764215 100644
--- a/tests/auto/quickcontrols/controls/data/splitview/fillItemOnLeft.qml
+++ b/tests/auto/quickcontrols/controls/data/splitview/fillItemOnLeft.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/controls/data/splitview/fillItemOnTop.qml b/tests/auto/quickcontrols/controls/data/splitview/fillItemOnTop.qml
index 41ce0f386e..4d41624b7e 100644
--- a/tests/auto/quickcontrols/controls/data/splitview/fillItemOnTop.qml
+++ b/tests/auto/quickcontrols/controls/data/splitview/fillItemOnTop.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/controls/data/stackview/Rect.qml b/tests/auto/quickcontrols/controls/data/stackview/Rect.qml
new file mode 100644
index 0000000000..699cef6b5d
--- /dev/null
+++ b/tests/auto/quickcontrols/controls/data/stackview/Rect.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ color: "tomato"
+}
diff --git a/tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml b/tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml
index 663b152c70..822c703a42 100644
--- a/tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_abstractbutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -42,15 +42,17 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(defaultComponent, testCase)
verify(control)
}
function test_text() {
- var control = createTemporaryObject(button, testCase);
+ let control = createTemporaryObject(button, testCase);
verify(control);
compare(control.text, "");
@@ -61,7 +63,7 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(button, testCase, {padding: 6})
+ let control = createTemporaryObject(button, testCase, {padding: 6})
verify(control)
compare(control.baselineOffset, 0)
control.contentItem = item.createObject(control, {baselineOffset: 12})
@@ -69,7 +71,7 @@ TestCase {
}
function test_implicitSize() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
compare(control.implicitWidth, 0)
@@ -96,22 +98,22 @@ TestCase {
}
function test_pressPoint(data) {
- var control = createTemporaryObject(button, testCase, {width: 100, height: 40})
+ let control = createTemporaryObject(button, testCase, {width: 100, height: 40})
verify(control)
- var pressXChanges = 0
- var pressYChanges = 0
+ let pressXChanges = 0
+ let pressYChanges = 0
- var pressXSpy = signalSpy.createObject(control, {target: control, signalName: "pressXChanged"})
+ let pressXSpy = signalSpy.createObject(control, {target: control, signalName: "pressXChanged"})
verify(pressXSpy.valid)
- var pressYSpy = signalSpy.createObject(control, {target: control, signalName: "pressYChanged"})
+ let pressYSpy = signalSpy.createObject(control, {target: control, signalName: "pressYChanged"})
verify(pressYSpy.valid)
compare(control.pressX, 0)
compare(control.pressY, 0)
- var touch = data.touch ? touchEvent(control) : null
+ let touch = data.touch ? touchEvent(control) : null
if (data.touch)
touch.press(0, control, control.width / 2, control.height / 2).commit()
@@ -196,10 +198,10 @@ TestCase {
}
function test_pressAndHold() {
- var control = createTemporaryObject(button, testCase, {checkable: true})
+ let control = createTemporaryObject(button, testCase, {checkable: true})
verify(control)
- var pressAndHoldSpy = signalSpy.createObject(control, {target: control, signalName: "pressAndHold"})
+ let pressAndHoldSpy = signalSpy.createObject(control, {target: control, signalName: "pressAndHold"})
verify(pressAndHoldSpy.valid)
mousePress(control)
@@ -215,8 +217,8 @@ TestCase {
Item {
property int lastKeyPress: -1
property int lastKeyRelease: -1
- Keys.onPressed: lastKeyPress = event.key
- Keys.onReleased: lastKeyRelease = event.key
+ Keys.onPressed: function (event) { lastKeyPress = event.key }
+ Keys.onReleased: function (event) { lastKeyRelease = event.key }
}
}
@@ -228,10 +230,10 @@ TestCase {
}
function test_keyEvents(data) {
- var container = createTemporaryObject(keyCatcher, testCase)
+ let container = createTemporaryObject(keyCatcher, testCase)
verify(container)
- var control = button.createObject(container)
+ let control = button.createObject(container)
verify(control)
control.forceActiveFocus()
@@ -245,7 +247,7 @@ TestCase {
}
function test_icon() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
compare(control.icon.name, "")
compare(control.icon.source, "")
@@ -253,7 +255,7 @@ TestCase {
compare(control.icon.height, 0)
compare(control.icon.color, "#00000000")
- var iconSpy = signalSpy.createObject(control, { target: control, signalName: "iconChanged"} )
+ let iconSpy = signalSpy.createObject(control, { target: control, signalName: "iconChanged"} )
verify(iconSpy.valid)
control.icon.name = "test-name"
@@ -322,14 +324,14 @@ TestCase {
}
function test_action(data) {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
control[data.property] = data.initButton
- var act = action.createObject(control)
+ let act = action.createObject(control)
act[data.property] = data.initAction
- var spy = signalSpy.createObject(control, {target: control, signalName: data.property + "Changed"})
+ let spy = signalSpy.createObject(control, {target: control, signalName: data.property + "Changed"})
verify(spy.valid)
// assign action
@@ -362,23 +364,23 @@ TestCase {
}
function test_actionIcon_data() {
- var data = []
+ let data = []
// Save duplicating the rows by reusing them with different properties of the same type.
// This means that the first loop will test icon.name and the second one will test icon.source.
- var stringPropertyValueSuffixes = [
+ let stringPropertyValueSuffixes = [
{ propertyName: "name", valueSuffix: "IconName" },
{ propertyName: "source", valueSuffix: "IconSource" }
]
- for (var i = 0; i < stringPropertyValueSuffixes.length; ++i) {
- var propertyName = stringPropertyValueSuffixes[i].propertyName
- var valueSuffix = stringPropertyValueSuffixes[i].valueSuffix
+ for (let i = 0; i < stringPropertyValueSuffixes.length; ++i) {
+ let propertyName = stringPropertyValueSuffixes[i].propertyName
+ let valueSuffix = stringPropertyValueSuffixes[i].valueSuffix
- var buttonPropertyValue = "Button" + valueSuffix
- var buttonPropertyValue2 = "Button" + valueSuffix + "2"
- var actionPropertyValue = "Action" + valueSuffix
- var actionPropertyValue2 = "Action" + valueSuffix + "2"
+ let buttonPropertyValue = "Button" + valueSuffix
+ let buttonPropertyValue2 = "Button" + valueSuffix + "2"
+ let actionPropertyValue = "Action" + valueSuffix
+ let actionPropertyValue2 = "Action" + valueSuffix + "2"
data.push({ tag: "implicit " + propertyName, property: propertyName,
initButton: undefined, initAction: actionPropertyValue,
@@ -420,19 +422,19 @@ TestCase {
resetExpected: buttonPropertyValue, resetChanged: false })
}
- var intPropertyNames = [
+ let intPropertyNames = [
"width",
"height",
]
- for (i = 0; i < intPropertyNames.length; ++i) {
- propertyName = intPropertyNames[i]
+ for (let i = 0; i < intPropertyNames.length; ++i) {
+ let propertyName = intPropertyNames[i]
- buttonPropertyValue = 20
- buttonPropertyValue2 = 21
- actionPropertyValue = 40
- actionPropertyValue2 = 41
- var defaultValue = 0
+ let buttonPropertyValue = 20
+ let buttonPropertyValue2 = 21
+ let actionPropertyValue = 40
+ let actionPropertyValue2 = 41
+ let defaultValue = 0
data.push({ tag: "implicit " + propertyName, property: propertyName,
initButton: undefined, initAction: actionPropertyValue,
@@ -474,12 +476,12 @@ TestCase {
resetExpected: buttonPropertyValue, resetChanged: false })
}
- propertyName = "color"
- buttonPropertyValue = "#aa0000"
- buttonPropertyValue2 = "#ff0000"
- actionPropertyValue = "#0000aa"
- actionPropertyValue2 = "#0000ff"
- defaultValue = "#00000000"
+ let propertyName = "color"
+ let buttonPropertyValue = "#aa0000"
+ let buttonPropertyValue2 = "#ff0000"
+ let actionPropertyValue = "#0000aa"
+ let actionPropertyValue2 = "#0000ff"
+ let defaultValue = "#00000000"
data.push({ tag: "implicit " + propertyName, property: propertyName,
initButton: undefined, initAction: actionPropertyValue,
@@ -524,14 +526,14 @@ TestCase {
}
function test_actionIcon(data) {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
control.icon[data.property] = data.initButton
- var act = action.createObject(control)
+ let act = action.createObject(control)
act.icon[data.property] = data.initAction
- var spy = signalSpy.createObject(control, {target: control, signalName: "iconChanged"})
+ let spy = signalSpy.createObject(control, {target: control, signalName: "iconChanged"})
verify(spy.valid)
// assign action
@@ -580,7 +582,7 @@ TestCase {
}
function test_actionButton() {
- var control = createTemporaryObject(actionButton, testCase)
+ let control = createTemporaryObject(actionButton, testCase)
verify(control)
// initial values
@@ -590,7 +592,7 @@ TestCase {
compare(control.enabled, false)
compare(control.icon.name, "checked")
- var textSpy = signalSpy.createObject(control, { target: control, signalName: "textChanged" })
+ let textSpy = signalSpy.createObject(control, { target: control, signalName: "textChanged" })
verify(textSpy.valid)
// changes via action
@@ -631,7 +633,7 @@ TestCase {
// setting an action while button has a particular property set
// shouldn't cause a change in the button's effective property value
- var secondAction = createTemporaryObject(action, testCase)
+ let secondAction = createTemporaryObject(action, testCase)
verify(secondAction)
secondAction.text = "SecondAction"
control.action = secondAction
@@ -639,7 +641,7 @@ TestCase {
compare(textSpy.count, 2)
// test setting an action whose properties aren't set
- var thirdAction = createTemporaryObject(action, testCase)
+ let thirdAction = createTemporaryObject(action, testCase)
verify(thirdAction)
control.action = thirdAction
compare(control.text, "Button")
@@ -657,7 +659,7 @@ TestCase {
}
function test_checkable_button() {
- var control = createTemporaryObject(checkableButton, testCase)
+ let control = createTemporaryObject(checkableButton, testCase)
verify(control)
control.checked = false
control.forceActiveFocus()
@@ -681,9 +683,9 @@ TestCase {
compare(control.action.checked, false)
compare(control.checked, false)
- var checkedSpy = signalSpy.createObject(control, {target: control.action, signalName: "checkedChanged"})
- var toggledSpy = signalSpy.createObject(control, {target: control, signalName: "toggled"})
- var actionToggledSpy = signalSpy.createObject(control, {target: control.action, signalName: "toggled"})
+ let checkedSpy = signalSpy.createObject(control, {target: control.action, signalName: "checkedChanged"})
+ let toggledSpy = signalSpy.createObject(control, {target: control, signalName: "toggled"})
+ let actionToggledSpy = signalSpy.createObject(control, {target: control.action, signalName: "toggled"})
verify(checkedSpy.valid)
verify(toggledSpy.valid)
@@ -723,16 +725,16 @@ TestCase {
}
function test_trigger(data) {
- var control = createTemporaryObject(actionButton, testCase, {"action.enabled": data.action, "enabled": data.button})
+ let control = createTemporaryObject(actionButton, testCase, {"action.enabled": data.action, "enabled": data.button})
verify(control)
compare(control.enabled, data.button)
compare(control.action.enabled, data.action)
- var buttonSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
+ let buttonSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
verify(buttonSpy.valid)
- var actionSpy = signalSpy.createObject(control, {target: control.action, signalName: "triggered"})
+ let actionSpy = signalSpy.createObject(control, {target: control.action, signalName: "triggered"})
verify(actionSpy.valid)
if (data.click)
@@ -748,13 +750,13 @@ TestCase {
if (Qt.platform.os === "osx" || Qt.platform.os === "macos")
skip("Mnemonics are not used on macOS")
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
control.text = "&Hello"
compare(control.text, "&Hello")
- var clickSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
+ let clickSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
verify(clickSpy.valid)
keyClick(Qt.Key_H, Qt.AltModifier)
@@ -789,7 +791,7 @@ TestCase {
control.text = undefined
control.action = action.createObject(control, {text: "&Action"})
- var actionSpy = signalSpy.createObject(control, {target: control.action, signalName: "triggered"})
+ let actionSpy = signalSpy.createObject(control, {target: control.action, signalName: "triggered"})
verify(actionSpy.valid)
keyClick(Qt.Key_A, Qt.AltModifier)
@@ -814,12 +816,12 @@ TestCase {
}
function test_actionGroup() {
- var group = createTemporaryObject(actionGroup, testCase)
+ let group = createTemporaryObject(actionGroup, testCase)
verify(group)
- var button1 = createTemporaryObject(button, testCase, {action: group.actions[0], width: 10, height: 10})
- var button2 = createTemporaryObject(button, testCase, {action: group.actions[1], width: 10, height: 10, y: 10})
- var button3 = createTemporaryObject(button, testCase, {action: group.actions[2], width: 10, height: 10, y: 20})
+ let button1 = createTemporaryObject(button, testCase, {action: group.actions[0], width: 10, height: 10})
+ let button2 = createTemporaryObject(button, testCase, {action: group.actions[1], width: 10, height: 10, y: 10})
+ let button3 = createTemporaryObject(button, testCase, {action: group.actions[2], width: 10, height: 10, y: 20})
verify(button1)
compare(button1.checked, true)
@@ -846,10 +848,10 @@ TestCase {
}
function test_clickedAfterLongPress() {
- var control = createTemporaryObject(button, testCase, { text: "Hello" })
+ let control = createTemporaryObject(button, testCase, { text: "Hello" })
verify(control)
- var clickedSpy = signalSpy.createObject(control, { target: control, signalName: "clicked" })
+ let clickedSpy = signalSpy.createObject(control, { target: control, signalName: "clicked" })
verify(clickedSpy.valid)
mousePress(control)
@@ -967,5 +969,39 @@ TestCase {
verify(control)
verify(!control.checkable)
- }
+ }
+
+ function test_rightMouseButton() {
+ let control = createTemporaryObject(button, testCase)
+ verify(control)
+
+ let pressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
+ verify(pressedSpy.valid)
+
+ let releasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
+ verify(releasedSpy.valid)
+
+ let clickedSpy = signalSpy.createObject(control, { target: control, signalName: "clicked" })
+ verify(clickedSpy.valid)
+
+ // button should not react on the right mouse button by defualt
+ mousePress(control, control.width / 2, control.height / 2, Qt.RightButton)
+ mouseRelease(control, control.width / 2, control.height / 2, Qt.RightButton)
+
+ compare(pressedSpy.count, 0)
+ compare(releasedSpy.count, 0)
+ compare(clickedSpy.count, 0)
+
+ // QTBUG-116289 - adding a HoverHandler into the button should not affect the handling of the right mouse button
+ let hoverHandler = createTemporaryQmlObject("import QtQuick; HoverHandler {}", control)
+ verify(hoverHandler)
+ compare(hoverHandler.target, control)
+
+ mousePress(control, control.width / 2, control.height / 2, Qt.RightButton)
+ mouseRelease(control, control.width / 2, control.height / 2, Qt.RightButton)
+
+ compare(pressedSpy.count, 0)
+ compare(releasedSpy.count, 0)
+ compare(clickedSpy.count, 0)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_action.qml b/tests/auto/quickcontrols/controls/data/tst_action.qml
index 845a6507af..f2b7a8674d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_action.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_action.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -24,18 +24,20 @@ TestCase {
SignalSpy { }
}
- function test_empty() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_empty() {
let control = createTemporaryObject(component, testCase)
verify(control)
}
function test_enabled() {
- var action = createTemporaryObject(component, testCase)
+ let action = createTemporaryObject(component, testCase)
verify(action)
- var spy = createTemporaryObject(signalSpy, testCase, {target: action, signalName: "triggered"})
+ let spy = createTemporaryObject(signalSpy, testCase, {target: action, signalName: "triggered"})
verify(spy.valid)
action.trigger()
@@ -53,24 +55,34 @@ TestCase {
Component {
id: buttonAndMenu
Item {
+ objectName: "container"
+
property alias button: button
property alias menu: menu
property alias menuItem: menuItem
property alias action: sharedAction
property var lastSource
+
Action {
id: sharedAction
+ objectName: "sharedAction"
text: "Shared"
shortcut: "Ctrl+B"
onTriggered: (source) => lastSource = source
}
Button {
id: button
+ objectName: "button"
action: sharedAction
+
Menu {
id: menu
+ objectName: "menu"
+ contentItem.objectName: "menuContentItem"
+
MenuItem {
id: menuItem
+ objectName: "menuItem"
action: sharedAction
}
}
@@ -79,7 +91,7 @@ TestCase {
}
function test_shared() {
- var container = createTemporaryObject(buttonAndMenu, testCase)
+ let container = createTemporaryObject(buttonAndMenu, testCase)
verify(container)
keyClick(Qt.Key_B, Qt.ControlModifier)
@@ -116,10 +128,10 @@ TestCase {
}
function test_repeater() {
- var container = createTemporaryObject(actionAndRepeater, testCase)
+ let container = createTemporaryObject(actionAndRepeater, testCase)
verify(container)
- var spy = signalSpy.createObject(container, {target: container.action, signalName: "triggered"})
+ let spy = signalSpy.createObject(container, {target: container.action, signalName: "triggered"})
verify(spy.valid)
keyClick(Qt.Key_A, Qt.ControlModifier)
@@ -150,7 +162,7 @@ TestCase {
}
function test_shortcutBinding() {
- var container = createTemporaryObject(shortcutBinding, testCase);
+ let container = createTemporaryObject(shortcutBinding, testCase);
verify(container)
compare(container.indirect.nativeText, container.direct.nativeText);
}
@@ -178,7 +190,7 @@ TestCase {
function test_shortcutCleanup() {
{
- var container = createTemporaryObject(shortcutCleanup, testCase);
+ let container = createTemporaryObject(shortcutCleanup, testCase);
verify(container)
container.action.shortcut = "Delete"
container.menu.open()
diff --git a/tests/auto/quickcontrols/controls/data/tst_actiongroup.qml b/tests/auto/quickcontrols/controls/data/tst_actiongroup.qml
index 4b5fdc7d65..dc17d554a4 100644
--- a/tests/auto/quickcontrols/controls/data/tst_actiongroup.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_actiongroup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -37,8 +37,12 @@ TestCase {
SignalSpy { }
}
+ function init() {
+ failOnWarning(/.?/)
+ }
+
function test_null() {
- var group = createTemporaryObject(actionGroup, testCase)
+ let group = createTemporaryObject(actionGroup, testCase)
verify(group)
group.addAction(null)
@@ -51,9 +55,7 @@ TestCase {
}
function test_defaults() {
- failOnWarning(/.?/)
-
- var group = createTemporaryObject(actionGroup, testCase)
+ let group = createTemporaryObject(actionGroup, testCase)
verify(group)
compare(group.actions.length, 0)
compare(group.checkedAction, null)
@@ -61,16 +63,16 @@ TestCase {
}
function test_current() {
- var group = createTemporaryObject(actionGroup, testCase)
+ let group = createTemporaryObject(actionGroup, testCase)
verify(group)
- var checkedActionSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "checkedActionChanged"})
+ let checkedActionSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "checkedActionChanged"})
verify(checkedActionSpy.valid)
verify(!group.checkedAction)
- var action1 = createTemporaryObject(action, testCase, {checked: true})
- var action2 = createTemporaryObject(action, testCase, {checked: false})
- var action3 = createTemporaryObject(action, testCase, {checked: true, objectName: "3"})
+ let action1 = createTemporaryObject(action, testCase, {checked: true})
+ let action2 = createTemporaryObject(action, testCase, {checked: false})
+ let action3 = createTemporaryObject(action, testCase, {checked: true, objectName: "3"})
// add checked
group.addAction(action1)
@@ -130,17 +132,17 @@ TestCase {
}
function test_actions() {
- var group = createTemporaryObject(actionGroup, testCase)
+ let group = createTemporaryObject(actionGroup, testCase)
verify(group)
- var actionsSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "actionsChanged"})
+ let actionsSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "actionsChanged"})
verify(actionsSpy.valid)
compare(group.actions.length, 0)
compare(group.checkedAction, null)
- var action1 = createTemporaryObject(action, testCase, {checked: true})
- var action2 = createTemporaryObject(action, testCase, {checked: false})
+ let action1 = createTemporaryObject(action, testCase, {checked: true})
+ let action2 = createTemporaryObject(action, testCase, {checked: false})
group.actions = [action1, action2]
compare(group.actions.length, 2)
@@ -149,7 +151,7 @@ TestCase {
compare(group.checkedAction, action1)
compare(actionsSpy.count, 2)
- var action3 = createTemporaryObject(action, testCase, {checked: true})
+ let action3 = createTemporaryObject(action, testCase, {checked: true})
group.addAction(action3)
compare(group.actions.length, 3)
@@ -173,7 +175,7 @@ TestCase {
}
function test_declarative() {
- var group = createTemporaryObject(declarativeGroup, testCase)
+ let group = createTemporaryObject(declarativeGroup, testCase)
verify(group)
compare(group.actions.length, 3)
@@ -190,14 +192,14 @@ TestCase {
}
function test_triggered(data) {
- var group = createTemporaryObject(actionGroup, testCase, {exclusive: data.exclusive})
+ let group = createTemporaryObject(actionGroup, testCase, {exclusive: data.exclusive})
verify(group)
- var triggeredSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "triggered"})
+ let triggeredSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "triggered"})
verify(triggeredSpy.valid)
- var action1 = createTemporaryObject(action, testCase)
- var action2 = createTemporaryObject(action, testCase)
+ let action1 = createTemporaryObject(action, testCase)
+ let action2 = createTemporaryObject(action, testCase)
group.addAction(action1)
group.addAction(action2)
@@ -222,7 +224,7 @@ TestCase {
}
function test_attached() {
- var container = createTemporaryObject(attachedGroup, testCase)
+ let container = createTemporaryObject(attachedGroup, testCase)
verify(container)
verify(!container.group.checkedAction)
@@ -247,13 +249,13 @@ TestCase {
}
function test_actionDestroyed() {
- var group = createTemporaryObject(actionGroup, testCase)
+ let group = createTemporaryObject(actionGroup, testCase)
verify(group)
- var actionsSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "actionsChanged"})
+ let actionsSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "actionsChanged"})
verify(actionsSpy.valid)
- var action1 = createTemporaryObject(action, testCase, {objectName: "action1", checked: true})
+ let action1 = createTemporaryObject(action, testCase, {objectName: "action1", checked: true})
group.addAction(action1)
compare(group.actions.length, 1)
@@ -269,15 +271,15 @@ TestCase {
}
function test_nonExclusive() {
- var group = createTemporaryObject(nonExclusiveGroup, testCase)
+ let group = createTemporaryObject(nonExclusiveGroup, testCase)
verify(group)
- var action1 = createTemporaryObject(action, testCase, {checked: true})
+ let action1 = createTemporaryObject(action, testCase, {checked: true})
group.addAction(action1)
compare(action1.checked, true)
compare(group.checkedAction, null)
- var action2 = createTemporaryObject(action, testCase, {checked: true})
+ let action2 = createTemporaryObject(action, testCase, {checked: true})
group.addAction(action2)
compare(action1.checked, true)
compare(action2.checked, true)
@@ -305,18 +307,18 @@ TestCase {
}
function test_enabled() {
- var group = createTemporaryObject(actionGroup, testCase)
+ let group = createTemporaryObject(actionGroup, testCase)
verify(group)
compare(group.enabled, true)
- var action1 = createTemporaryObject(action, testCase)
- var action2 = createTemporaryObject(action, testCase)
+ let action1 = createTemporaryObject(action, testCase)
+ let action2 = createTemporaryObject(action, testCase)
compare(action1.enabled, true)
compare(action2.enabled, true)
- var action1Spy = createTemporaryObject(signalSpy, testCase, {target: action1, signalName: "enabledChanged"})
- var action2Spy = createTemporaryObject(signalSpy, testCase, {target: action2, signalName: "enabledChanged"})
+ let action1Spy = createTemporaryObject(signalSpy, testCase, {target: action1, signalName: "enabledChanged"})
+ let action2Spy = createTemporaryObject(signalSpy, testCase, {target: action2, signalName: "enabledChanged"})
verify(action1Spy.valid && action2Spy.valid)
group.addAction(action1)
@@ -352,4 +354,130 @@ TestCase {
compare(action2Spy.count, 2)
compare(action2Spy.signalArguments[1][0], true)
}
+
+ Component {
+ id: checkBoxes
+ Item {
+ property ActionGroup group: ActionGroup { id: group }
+ property CheckBox control1: CheckBox { action: Action { ActionGroup.group: group } }
+ property CheckBox control2: CheckBox { action: Action { ActionGroup.group: group } }
+ property CheckBox control3: CheckBox { action: Action { ActionGroup.group: group } }
+ }
+ }
+
+ Component {
+ id: radioButtons
+ Item {
+ property ActionGroup group: ActionGroup { id: group }
+ property RadioButton control1: RadioButton { action: Action { ActionGroup.group: group } }
+ property RadioButton control2: RadioButton { action: Action { ActionGroup.group: group } }
+ property RadioButton control3: RadioButton { action: Action { ActionGroup.group: group } }
+ }
+ }
+
+ Component {
+ id: switches
+ Item {
+ property ActionGroup group: ActionGroup { id: group }
+ property Switch control1: Switch { action: Action { ActionGroup.group: group } }
+ property Switch control2: Switch { action: Action { ActionGroup.group: group } }
+ property Switch control3: Switch { action: Action { ActionGroup.group: group } }
+ }
+ }
+
+ function test_controls_data() {
+ return [
+ { tag: "CheckBox", component: checkBoxes },
+ { tag: "RadioButton", component: radioButtons },
+ { tag: "Switch", component: switches },
+ ]
+ }
+
+ function test_controls(data) {
+ let container = createTemporaryObject(data.component, testCase)
+ verify(container)
+
+ verify(!container.group.checkedAction)
+
+ container.control1.checked = true
+ compare(container.group.checkedAction, container.control1.action)
+ compare(container.control1.checked, true)
+ compare(container.control2.checked, false)
+ compare(container.control3.checked, false)
+
+ container.control2.checked = true
+ compare(container.group.checkedAction, container.control2.action)
+ compare(container.control1.checked, false)
+ compare(container.control2.checked, true)
+ compare(container.control3.checked, false)
+
+ container.control3.checked = true
+ compare(container.group.checkedAction, container.control3.action)
+ compare(container.control1.checked, false)
+ compare(container.control2.checked, false)
+ compare(container.control3.checked, true)
+ }
+
+ Component {
+ id: exclusiveMenus
+ Column {
+ property ActionGroup group: ActionGroup { id: group }
+ property alias control1: control1
+ property alias control2: control2
+ property alias control3: control3
+ MenuItem {
+ id: control1
+ action: Action {
+ checkable: true
+ ActionGroup.group: group
+ }
+ }
+ MenuItem {
+ id: control2
+ action: Action {
+ checkable: true
+ ActionGroup.group: group
+ }
+ }
+ MenuItem {
+ id: control3
+ action: Action {
+ checkable: true
+ ActionGroup.group: group
+ }
+ }
+ }
+ }
+
+ function test_exclusiveActionButtons() {
+ const container = createTemporaryObject(exclusiveMenus, testCase)
+ verify(container)
+
+ verify(!container.group.checkedAction)
+
+ mouseClick(container.control1)
+ compare(container.group.checkedAction, container.control1.action)
+ compare(container.control1.checked, true)
+ compare(container.control2.checked, false)
+ compare(container.control3.checked, false)
+
+ mouseClick(container.control2)
+ compare(container.group.checkedAction, container.control2.action)
+ compare(container.control1.checked, false)
+ compare(container.control2.checked, true)
+ compare(container.control3.checked, false)
+
+ mouseClick(container.control3)
+ compare(container.group.checkedAction, container.control3.action)
+ compare(container.control1.checked, false)
+ compare(container.control2.checked, false)
+ compare(container.control3.checked, true)
+
+ // here comes the tricky part: clicking on checked control must not uncheck it
+ mouseClick(container.control3)
+ compare(container.group.checkedAction, container.control3.action)
+ compare(container.control1.checked, false)
+ compare(container.control2.checked, false)
+ compare(container.control3.checked, true)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_busyindicator.qml b/tests/auto/quickcontrols/controls/data/tst_busyindicator.qml
index a50fd1bfab..001b0c474f 100644
--- a/tests/auto/quickcontrols/controls/data/tst_busyindicator.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_busyindicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -22,17 +22,26 @@ TestCase {
id: mouseArea
MouseArea { }
}
-
- function test_defaults() {
+
+ Component {
+ id: busyIndicatorInItem
+ Item {
+ BusyIndicator { }
+ }
+ }
+
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(busyIndicator, testCase)
verify(control)
compare(control.running, true)
}
function test_running() {
- var control = createTemporaryObject(busyIndicator, testCase)
+ let control = createTemporaryObject(busyIndicator, testCase)
verify(control)
compare(control.running, true)
@@ -42,10 +51,10 @@ TestCase {
// QTBUG-61785
function test_mouseArea() {
- var ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
+ let ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
verify(ma)
- var control = busyIndicator.createObject(ma, {width: testCase.width, height: testCase.height})
+ let control = busyIndicator.createObject(ma, {width: testCase.width, height: testCase.height})
verify(control)
mousePress(control)
@@ -54,11 +63,23 @@ TestCase {
mouseRelease(control)
verify(!ma.pressed)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control).commit()
verify(ma.pressed)
touch.release(0, control).commit()
verify(!ma.pressed)
}
+
+ // QTBUG-108808
+ function test_visibility() {
+ let control = createTemporaryObject(busyIndicatorInItem, testCase, {visible: false})
+ verify(control)
+
+ let invisibleImage = grabImage(control)
+ control.visible = true
+ let visibleImage = grabImage(control)
+
+ verify(!invisibleImage.equals(visibleImage))
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_button.qml b/tests/auto/quickcontrols/controls/data/tst_button.qml
index 9ddfa0885d..0368a229a9 100644
--- a/tests/auto/quickcontrols/controls/data/tst_button.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -30,9 +30,11 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(button, testCase)
verify(control)
compare(control.highlighted, false)
@@ -40,7 +42,7 @@ TestCase {
}
function test_text() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
compare(control.text, "")
@@ -51,10 +53,10 @@ TestCase {
}
function test_mouse() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// click
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
@@ -120,11 +122,11 @@ TestCase {
}
function test_touch() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
- var touch = touchEvent(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let touch = touchEvent(control)
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// click
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
@@ -163,15 +165,15 @@ TestCase {
}
function test_multiTouch() {
- var control1 = createTemporaryObject(button, testCase)
+ let control1 = createTemporaryObject(button, testCase)
verify(control1)
- var pressedCount1 = 0
+ let pressedCount1 = 0
- var pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"})
+ let pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"})
verify(pressedSpy1.valid)
- var touch = touchEvent(control1)
+ let touch = touchEvent(control1)
touch.press(0, control1, 0, 0).commit().move(0, control1, control1.width - 1, control1.height - 1).commit()
compare(pressedSpy1.count, ++pressedCount1)
@@ -185,12 +187,12 @@ TestCase {
compare(pressedSpy1.count, pressedCount1)
compare(control1.pressed, true)
- var control2 = createTemporaryObject(button, testCase, {y: control1.height})
+ let control2 = createTemporaryObject(button, testCase, {y: control1.height})
verify(control2)
- var pressedCount2 = 0
+ let pressedCount2 = 0
- var pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"})
+ let pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"})
verify(pressedSpy2.valid)
// press the second button
@@ -213,13 +215,13 @@ TestCase {
}
function test_keys() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
control.forceActiveFocus()
verify(control.activeFocus)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// click
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
@@ -236,8 +238,8 @@ TestCase {
sequenceSpy.expectedSequence = []
// Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
// pressing buttons and the CI uses the QGnomeTheme platform theme.
- var keys = [Qt.Key_Escape, Qt.Key_Tab]
- for (var i = 0; i < keys.length; ++i) {
+ let keys = [Qt.Key_Escape, Qt.Key_Tab]
+ for (let i = 0; i < keys.length; ++i) {
sequenceSpy.reset()
keyClick(keys[i])
verify(sequenceSpy.success)
@@ -249,7 +251,7 @@ TestCase {
}
function test_autoRepeat() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
compare(control.autoRepeat, false)
@@ -259,11 +261,11 @@ TestCase {
control.forceActiveFocus()
verify(control.activeFocus)
- var clickSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
+ let clickSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
verify(clickSpy.valid)
- var pressSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"})
+ let pressSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"})
verify(pressSpy.valid)
- var releaseSpy = signalSpy.createObject(control, {target: control, signalName: "released"})
+ let releaseSpy = signalSpy.createObject(control, {target: control, signalName: "released"})
verify(releaseSpy.valid)
// auto-repeat mouse click
@@ -333,18 +335,18 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
function test_checkable() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
verify(control.hasOwnProperty("checkable"))
verify(!control.checkable)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
["downChanged", { "down": true }],
@@ -386,7 +388,7 @@ TestCase {
}
function test_highlighted() {
- var control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(button, testCase)
verify(control)
verify(!control.highlighted)
@@ -395,11 +397,11 @@ TestCase {
}
function test_spacing() {
- var control = createTemporaryObject(button, testCase, { text: "Some long, long, long text" })
+ let control = createTemporaryObject(button, testCase, { text: "Some long, long, long text" })
verify(control)
verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth)
- var textLabel = findChild(control.contentItem, "label")
+ let textLabel = findChild(control.contentItem, "label")
verify(textLabel)
// The implicitWidth of the IconLabel that all buttons use as their contentItem
@@ -428,7 +430,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(button, testCase, {
+ let control = createTemporaryObject(button, testCase, {
text: "Button",
display: data.display,
"icon.source": "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png",
@@ -437,8 +439,8 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
switch (control.display) {
case Button.IconOnly:
diff --git a/tests/auto/quickcontrols/controls/data/tst_buttongroup.qml b/tests/auto/quickcontrols/controls/data/tst_buttongroup.qml
index 2ae86d1737..c628a5cb02 100644
--- a/tests/auto/quickcontrols/controls/data/tst_buttongroup.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_buttongroup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -28,14 +28,6 @@ TestCase {
SignalSpy { }
}
- function test_null() {
- var group = createTemporaryObject(buttonGroup, testCase)
- verify(group)
-
- group.addButton(null)
- group.removeButton(null)
- }
-
Component {
id: button
Button { }
@@ -46,10 +38,12 @@ TestCase {
QtObject { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var group = createTemporaryObject(buttonGroup, testCase)
+ function test_defaults() {
+ let group = createTemporaryObject(buttonGroup, testCase)
verify(group)
compare(group.buttons.length, 0)
compare(group.checkedButton, null)
@@ -57,17 +51,25 @@ TestCase {
compare(group.checkState, Qt.Unchecked)
}
+ function test_null() {
+ let group = createTemporaryObject(buttonGroup, testCase)
+ verify(group)
+
+ group.addButton(null)
+ group.removeButton(null)
+ }
+
function test_current() {
- var group = createTemporaryObject(buttonGroup, testCase)
+ let group = createTemporaryObject(buttonGroup, testCase)
verify(group)
- var checkedButtonSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "checkedButtonChanged"})
+ let checkedButtonSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "checkedButtonChanged"})
verify(checkedButtonSpy.valid)
verify(!group.checkedButton)
- var button1 = createTemporaryObject(button, testCase, {checked: true})
- var button2 = createTemporaryObject(button, testCase, {checked: false})
- var button3 = createTemporaryObject(button, testCase, {checked: true, objectName: "3"})
+ let button1 = createTemporaryObject(button, testCase, {checked: true})
+ let button2 = createTemporaryObject(button, testCase, {checked: false})
+ let button3 = createTemporaryObject(button, testCase, {checked: true, objectName: "3"})
// add checked
group.addButton(button1)
@@ -127,17 +129,17 @@ TestCase {
}
function test_buttons() {
- var group = createTemporaryObject(buttonGroup, testCase)
+ let group = createTemporaryObject(buttonGroup, testCase)
verify(group)
- var buttonsSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "buttonsChanged"})
+ let buttonsSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "buttonsChanged"})
verify(buttonsSpy.valid)
compare(group.buttons.length, 0)
compare(group.checkedButton, null)
- var button1 = createTemporaryObject(button, testCase, {checked: true})
- var button2 = createTemporaryObject(button, testCase, {checked: false})
+ let button1 = createTemporaryObject(button, testCase, {checked: true})
+ let button2 = createTemporaryObject(button, testCase, {checked: false})
group.buttons = [button1, button2]
compare(group.buttons.length, 2)
@@ -146,7 +148,7 @@ TestCase {
compare(group.checkedButton, button1)
compare(buttonsSpy.count, 2)
- var button3 = createTemporaryObject(button, testCase, {checked: true})
+ let button3 = createTemporaryObject(button, testCase, {checked: true})
group.addButton(button3)
compare(group.buttons.length, 3)
@@ -177,14 +179,14 @@ TestCase {
}
function test_clicked(data) {
- var group = createTemporaryObject(buttonGroup, testCase, {exclusive: data.exclusive})
+ let group = createTemporaryObject(buttonGroup, testCase, {exclusive: data.exclusive})
verify(group)
- var clickedSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "clicked"})
+ let clickedSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "clicked"})
verify(clickedSpy.valid)
- var button1 = createTemporaryObject(button, testCase)
- var button2 = createTemporaryObject(button, testCase)
+ let button1 = createTemporaryObject(button, testCase)
+ let button2 = createTemporaryObject(button, testCase)
group.addButton(button1)
group.addButton(button2)
@@ -252,7 +254,7 @@ TestCase {
}
function test_controls(data) {
- var container = createTemporaryObject(data.component, testCase)
+ let container = createTemporaryObject(data.component, testCase)
verify(container)
verify(!container.group.checkedButton)
@@ -277,13 +279,13 @@ TestCase {
}
function test_buttonDestroyed() {
- var group = createTemporaryObject(buttonGroup, testCase)
+ let group = createTemporaryObject(buttonGroup, testCase)
verify(group)
- var buttonsSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "buttonsChanged"})
+ let buttonsSpy = createTemporaryObject(signalSpy, testCase, {target: group, signalName: "buttonsChanged"})
verify(buttonsSpy.valid)
- var button1 = createTemporaryObject(button, testCase, {objectName: "button1", checked: true})
+ let button1 = createTemporaryObject(button, testCase, {objectName: "button1", checked: true})
group.addButton(button1)
compare(group.buttons.length, 1)
@@ -316,7 +318,7 @@ TestCase {
}
function test_repeater() {
- var container = createTemporaryObject(repeater, testCase)
+ let container = createTemporaryObject(repeater, testCase)
verify(container)
verify(container.group.checkedButton)
@@ -324,25 +326,25 @@ TestCase {
}
function test_nonExclusive() {
- var group = createTemporaryObject(nonExclusiveGroup, testCase)
+ let group = createTemporaryObject(nonExclusiveGroup, testCase)
verify(group)
compare(group.checkState, Qt.Unchecked)
- var button1 = createTemporaryObject(button, testCase, {checked: true})
+ let button1 = createTemporaryObject(button, testCase, {checked: true})
group.addButton(button1)
compare(button1.checked, true)
compare(group.checkedButton, null)
compare(group.checkState, Qt.Checked)
- var button2 = createTemporaryObject(button, testCase, {checked: true})
+ let button2 = createTemporaryObject(button, testCase, {checked: true})
group.addButton(button2)
compare(button1.checked, true)
compare(button2.checked, true)
compare(group.checkedButton, null)
compare(group.checkState, Qt.Checked)
- var button3 = createTemporaryObject(button, testCase, {checked: false})
+ let button3 = createTemporaryObject(button, testCase, {checked: false})
group.addButton(button3)
compare(button1.checked, true)
compare(button2.checked, true)
@@ -401,7 +403,7 @@ TestCase {
model: ListModel {
id: listModel
Component.onCompleted: {
- for (var i = 0; i < 10; ++i)
+ for (let i = 0; i < 10; ++i)
append({text: i})
}
}
@@ -410,11 +412,63 @@ TestCase {
}
function test_checkedButtonDestroyed() {
- var column = createTemporaryObject(checkedButtonColumn, testCase)
+ let column = createTemporaryObject(checkedButtonColumn, testCase)
verify(column)
waitForRendering(column)
mouseClick(column.children[0])
wait(0) // don't crash (QTBUG-62946, QTBUG-63470)
}
+
+ Component {
+ id: buttonGroupComp
+
+ Item {
+
+ property ButtonGroup buttonGroup: ButtonGroup { }
+ property int buttonGroupCount: buttonGroup.buttons.length
+ property int buttonGrpSigCnt: 0
+
+ function clearButtonGroup() {
+ buttonGroup.buttons = []
+ }
+ function assignButtonGroup() {
+ radioButton1.ButtonGroup.group = buttonGroup
+ }
+
+ Column {
+ RadioButton {
+ id: radioButton1
+ visible: false
+ ButtonGroup.group: buttonGroup
+ ButtonGroup.onGroupChanged: { (ButtonGroup.group === null) ? --buttonGrpSigCnt : ++buttonGrpSigCnt }
+ }
+ RadioButton {
+ id: radioButton2
+ visible: false
+ ButtonGroup.group: buttonGroup
+ ButtonGroup.onGroupChanged: { (ButtonGroup.group === null) ? --buttonGrpSigCnt : ++buttonGrpSigCnt }
+ }
+ }
+ }
+ }
+
+ function test_resetButtonGroup() {
+ let container = createTemporaryObject(buttonGroupComp, testCase)
+ verify(container)
+
+ // Check for initial buttons assigned to button group
+ compare(container.buttonGroupCount, 2)
+ compare(container.buttonGrpSigCnt, 2)
+ // Clear buttons in the button group
+ container.clearButtonGroup()
+ // Check for buttons in the group and group changed event for buttons
+ compare(container.buttonGroupCount, 0)
+ compare(container.buttonGrpSigCnt, 0)
+ // Assign same group to the button
+ container.assignButtonGroup()
+ // Check for group change event from the button
+ compare(container.buttonGroupCount, 1)
+ compare(container.buttonGrpSigCnt, 1)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml b/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml
index 19070e074d..3cb35ab266 100644
--- a/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml
import QtQuick
@@ -29,32 +29,45 @@ TestCase {
}
}
+ function init() {
+ failOnWarning(/.?/)
+ }
+
function test_indices_data() {
return [
+ // "from" and "to" must currently be in the same year.
{ tag: "2013", from: "2013-01-01", to: "2013-12-31", count: 12 },
- { tag: "2016", from: "2016-01-01", to: "2016-03-31", count: 3 }
+ { tag: "2016", from: "2016-01-01", to: "2016-03-31", count: 3 },
+ { tag: "2016-02-01 to 2016-12-31", from: "2016-02-01", to: "2016-12-31", count: 11 },
+ { tag: "2014-11-30 to 2016-01-01", from: "2014-11-30", to: "2016-01-01", count: 15 }
]
}
function test_indices(data) {
- var model = calendarModel.createObject(testCase, {from: data.from, to: data.to})
+ let model = calendarModel.createObject(testCase, {from: data.from, to: data.to})
verify(model)
compare(model.count, data.count)
- var y = parseInt(data.tag)
- for (var m = 0; m < 12; ++m) {
- compare(model.yearAt(m), y)
- compare(model.indexOf(y, m), m)
- compare(model.indexOf(new Date(y, m, 1)), m)
- compare(model.monthAt(m), m)
+ const from = new Date(data.from)
+ const to = new Date(data.to)
+ let index = 0
+ for (let date = from; date <= to; date.setMonth(date.getMonth() + 1, 28), ++index) {
+ compare(model.yearAt(index), date.getFullYear(),
+ `yearAt(${index}) returned incorrect value`)
+ compare(model.indexOf(date.getFullYear(), date.getMonth()), index,
+ `indexOf(${date.getFullYear()}, ${date.getMonth()}) returned incorrect value`)
+ compare(model.indexOf(date), index,
+ `indexOf(${date}) returned incorrect value`)
+ compare(model.monthAt(index), date.getMonth(),
+ `monthAt(${index}) returned incorrect value`)
}
model.destroy()
}
function test_invalid() {
- var model = calendarModel.createObject(testCase)
+ let model = calendarModel.createObject(testCase)
verify(model)
compare(model.indexOf(-1, -1), -1)
@@ -64,11 +77,11 @@ TestCase {
}
function test_instantiator() {
- var inst = instantiator.createObject(testCase)
+ let inst = instantiator.createObject(testCase)
verify(inst)
compare(inst.count, 12)
- for (var m = 0; m < inst.count; ++m) {
+ for (let m = 0; m < inst.count; ++m) {
compare(inst.objectAt(m).month, m)
compare(inst.objectAt(m).year, 2016)
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_checkbox.qml b/tests/auto/quickcontrols/controls/data/tst_checkbox.qml
index 5991ae9008..6c3b859e40 100644
--- a/tests/auto/quickcontrols/controls/data/tst_checkbox.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_checkbox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -25,9 +25,11 @@ TestCase {
}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(checkBox, testCase)
verify(control)
compare(control.tristate, false)
@@ -35,7 +37,7 @@ TestCase {
}
function test_text() {
- var control = createTemporaryObject(checkBox, testCase)
+ let control = createTemporaryObject(checkBox, testCase)
verify(control)
compare(control.text, "")
@@ -46,10 +48,10 @@ TestCase {
}
function test_checked() {
- var control = createTemporaryObject(checkBox, testCase)
+ let control = createTemporaryObject(checkBox, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
sequenceSpy.expectedSequence = []
compare(control.checked, false)
@@ -72,10 +74,10 @@ TestCase {
}
function test_checkState() {
- var control = createTemporaryObject(checkBox, testCase)
+ let control = createTemporaryObject(checkBox, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
sequenceSpy.expectedSequence = []
compare(control.checked, false)
@@ -98,10 +100,10 @@ TestCase {
}
function test_mouse() {
- var control = createTemporaryObject(checkBox, testCase)
+ let control = createTemporaryObject(checkBox, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// check
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }],
@@ -169,12 +171,12 @@ TestCase {
}
function test_touch() {
- var control = createTemporaryObject(checkBox, testCase)
+ let control = createTemporaryObject(checkBox, testCase)
verify(control)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// check
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }],
@@ -235,10 +237,10 @@ TestCase {
}
function test_keys() {
- var control = createTemporaryObject(checkBox, testCase)
+ let control = createTemporaryObject(checkBox, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
sequenceSpy.expectedSequence = []
control.forceActiveFocus()
@@ -277,8 +279,8 @@ TestCase {
sequenceSpy.expectedSequence = []
// Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
// pressing buttons and the CI uses the QGnomeTheme platform theme.
- var keys = [Qt.Key_Escape, Qt.Key_Tab]
- for (var i = 0; i < keys.length; ++i) {
+ let keys = [Qt.Key_Escape, Qt.Key_Tab]
+ for (let i = 0; i < keys.length; ++i) {
sequenceSpy.reset()
keyClick(keys[i])
compare(control.checked, false)
@@ -295,7 +297,7 @@ TestCase {
}
function test_checked_binding() {
- var container = createTemporaryObject(checkedBoundBoxes, testCase)
+ let container = createTemporaryObject(checkedBoundBoxes, testCase)
verify(container)
compare(container.cb1.checked, false)
@@ -325,7 +327,7 @@ TestCase {
}
function test_checkState_binding() {
- var container = createTemporaryObject(checkStateBoundBoxes, testCase)
+ let container = createTemporaryObject(checkStateBoundBoxes, testCase)
verify(container)
compare(container.cb1.checked, false)
@@ -361,9 +363,9 @@ TestCase {
}
function test_tristate() {
- var control = createTemporaryObject(checkBox, testCase, {tristate: true})
+ let control = createTemporaryObject(checkBox, testCase, {tristate: true})
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
sequenceSpy.expectedSequence = []
control.forceActiveFocus()
@@ -457,7 +459,7 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(checkBox, testCase)
+ let control = createTemporaryObject(checkBox, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
@@ -484,7 +486,7 @@ TestCase {
}
function test_nextCheckState(data) {
- var control = createTemporaryObject(nextCheckStateBox, testCase)
+ let control = createTemporaryObject(nextCheckStateBox, testCase)
verify(control)
// mouse
@@ -496,7 +498,7 @@ TestCase {
// touch
control.checkState = data.checkState
compare(control.checkState, data.checkState)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control).commit().release(0, control).commit()
compare(control.checkState, data.expectedState)
diff --git a/tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml b/tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml
index d3e0910e83..acb6ebc653 100644
--- a/tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -20,16 +20,18 @@ TestCase {
// TODO: data-fy tst_checkbox (rename to tst_check?) so we don't duplicate its tests here?
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(checkDelegate, testCase);
+ function test_defaults() {
+ let control = createTemporaryObject(checkDelegate, testCase);
verify(control);
verify(!control.checked);
}
function test_checked() {
- var control = createTemporaryObject(checkDelegate, testCase);
+ let control = createTemporaryObject(checkDelegate, testCase);
verify(control);
mouseClick(control);
@@ -40,17 +42,17 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(checkDelegate, testCase);
+ let control = createTemporaryObject(checkDelegate, testCase);
verify(control);
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset);
}
function test_spacing() {
- var control = createTemporaryObject(checkDelegate, testCase, { text: "Some long, long, long text" })
+ let control = createTemporaryObject(checkDelegate, testCase, { text: "Some long, long, long text" })
verify(control)
verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth)
- var textLabel = findChild(control.contentItem, "label")
+ let textLabel = findChild(control.contentItem, "label")
verify(textLabel)
// The implicitWidth of the IconLabel that all buttons use as their contentItem should be
@@ -77,7 +79,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(checkDelegate, testCase, {
+ let control = createTemporaryObject(checkDelegate, testCase, {
text: "CheckDelegate",
display: data.display,
width: 400,
@@ -87,11 +89,11 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
- var availableWidth = control.availableWidth - control.indicator.width - control.spacing
- var indicatorOffset = control.mirrored ? control.indicator.width + control.spacing : 0
+ let availableWidth = control.availableWidth - control.indicator.width - control.spacing
+ let indicatorOffset = control.mirrored ? control.indicator.width + control.spacing : 0
switch (control.display) {
case CheckDelegate.IconOnly:
@@ -148,7 +150,7 @@ TestCase {
}
function test_nextCheckState(data) {
- var control = createTemporaryObject(nextCheckStateDelegate, testCase)
+ let control = createTemporaryObject(nextCheckStateDelegate, testCase)
verify(control)
// mouse
@@ -160,7 +162,7 @@ TestCase {
// touch
control.checkState = data.checkState
compare(control.checkState, data.checkState)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control).commit().release(0, control).commit()
compare(control.checkState, data.expectedState)
diff --git a/tests/auto/quickcontrols/controls/data/tst_combobox.qml b/tests/auto/quickcontrols/controls/data/tst_combobox.qml
index 5bb9d61815..9f852e29e4 100644
--- a/tests/auto/quickcontrols/controls/data/tst_combobox.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_combobox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -1710,7 +1710,7 @@ TestCase {
ComboBox {
editable: true
property bool gotit: false
- Keys.onPressed: {
+ Keys.onPressed: function (event) {
if (!gotit && event.key === Qt.Key_B) {
gotit = true
event.accepted = true
@@ -2277,6 +2277,35 @@ TestCase {
compare(control.currentIndex, 1)
}
+ // QTBUG-109721 - verify that an eaten press event for the space key
+ // doesn't open the popup when the key is released.
+ Component {
+ id: comboboxEatsSpace
+ ComboBox {
+ id: nonEditableComboBox
+ editable: false
+ model: ["NonEditable", "Delta", "Echo", "Foxtrot"]
+ Keys.onSpacePressed: (event) => event.accept
+ }
+ }
+
+ function test_spacePressEaten() {
+ let control = createTemporaryObject(comboboxEatsSpace, testCase)
+ verify(control)
+ control.forceActiveFocus()
+
+ var visibleChangedSpy = signalSpy.createObject(control, {target: control.popup, signalName: "visibleChanged"})
+ verify(visibleChangedSpy.valid)
+
+ // press doesn't open
+ keyPress(Qt.Key_Space)
+ verify(!control.pressed)
+ compare(visibleChangedSpy.count, 0)
+ // neither does release
+ keyRelease(Qt.Key_Space)
+ compare(visibleChangedSpy.count, 0)
+ }
+
Component {
id: listOfGadgets
QtObject {
@@ -2295,4 +2324,25 @@ TestCase {
control.currentIndex = 1;
compare(control.displayText, "7");
}
+
+ function test_contextObject() {
+ // We use the default delegate with required properties and pass
+ // an array of objects as model. This should work despite
+ // ComboBox setting itself as model object for the delegate.
+
+ let control = createTemporaryObject(
+ comboBox, testCase, {model: fruitarray, textRole: "color"});
+ verify(control);
+ compare(control.popup.contentItem.itemAtIndex(0).text, "red");
+
+ // Now we pass an AbstractItemModel with 2 roles. Since we use required properties
+ // the model object should still have the anonymous property, and it should be a
+ // QQmlDMAbstractItemModelData.
+
+ control = createTemporaryObject(comboBox, testCase, { model: fruitmodel });
+ verify(control);
+ for (var i = 0; i < 3; ++i)
+ ignoreWarning(/ComboBox\.qml\:[0-9]+\:[0-9]+\: Unable to assign QQmlDMAbstractItemModelData to QString/);
+ compare(control.popup.contentItem.itemAtIndex(0).text, "");
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_container.qml b/tests/auto/quickcontrols/controls/data/tst_container.qml
index 9988625744..35c9f93b05 100644
--- a/tests/auto/quickcontrols/controls/data/tst_container.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_container.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -24,16 +24,18 @@ TestCase {
Rectangle { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(container, testCase)
verify(control)
compare(control.count, 0)
}
function test_implicitSize() {
- var control = createTemporaryObject(container, testCase)
+ let control = createTemporaryObject(container, testCase)
verify(control)
compare(control.implicitWidth, 0)
@@ -53,16 +55,16 @@ TestCase {
}
function test_currentIndex() {
- var control1 = createTemporaryObject(container, testCase)
+ let control1 = createTemporaryObject(container, testCase)
verify(control1)
- var control2 = createTemporaryObject(container, testCase)
+ let control2 = createTemporaryObject(container, testCase)
verify(control2)
compare(control1.currentIndex, -1)
compare(control2.currentIndex, -1)
- for (var i = 0; i < 3; ++i) {
+ for (let i = 0; i < 3; ++i) {
control1.addItem(rectangle.createObject(control1))
control2.addItem(rectangle.createObject(control2))
}
@@ -130,7 +132,7 @@ TestCase {
// don't crash (QTBUG-61310)
function test_repeater(data) {
- var control = createTemporaryObject(data.component, testCase)
+ let control = createTemporaryObject(data.component, testCase)
verify(control)
compare(control.itemAt(0).objectName, "0")
@@ -140,12 +142,12 @@ TestCase {
}
function test_removeTakeItem() {
- var control = createTemporaryObject(container, testCase)
+ let control = createTemporaryObject(container, testCase)
verify(control)
- var item1 = rectangle.createObject(control)
- var item2 = rectangle.createObject(control)
- var item3 = rectangle.createObject(control)
+ let item1 = rectangle.createObject(control)
+ let item2 = rectangle.createObject(control)
+ let item3 = rectangle.createObject(control)
item1.Component.onDestruction.connect(function() { item1 = null })
item2.Component.onDestruction.connect(function() { item2 = null })
@@ -210,9 +212,9 @@ TestCase {
}
function test_contentItemDeletionOrder() {
- var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ let control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
verify(control1)
- var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ let control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
verify(control2)
}
@@ -251,9 +253,9 @@ TestCase {
}
function test_backgroundDeletionOrder() {
- var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ let control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
verify(control1)
- var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ let control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
verify(control2)
}
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_control.qml b/tests/auto/quickcontrols/controls/data/tst_control.qml
index 0747943421..a3e65f2b0f 100644
--- a/tests/auto/quickcontrols/controls/data/tst_control.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_control.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -34,9 +34,11 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(component, testCase)
verify(control)
compare(control.background, null)
@@ -44,37 +46,37 @@ TestCase {
}
function test_padding() {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
verify(control)
- var paddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "paddingChanged"})
+ let paddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "paddingChanged"})
verify(paddingSpy.valid)
- var topPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topPaddingChanged"})
+ let topPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topPaddingChanged"})
verify(topPaddingSpy.valid)
- var leftPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftPaddingChanged"})
+ let leftPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftPaddingChanged"})
verify(leftPaddingSpy.valid)
- var rightPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightPaddingChanged"})
+ let rightPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightPaddingChanged"})
verify(rightPaddingSpy.valid)
- var bottomPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomPaddingChanged"})
+ let bottomPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomPaddingChanged"})
verify(bottomPaddingSpy.valid)
- var horizontalPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "horizontalPaddingChanged"})
+ let horizontalPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "horizontalPaddingChanged"})
verify(horizontalPaddingSpy.valid)
- var verticalPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "verticalPaddingChanged"})
+ let verticalPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "verticalPaddingChanged"})
verify(verticalPaddingSpy.valid)
- var paddingChanges = 0
- var topPaddingChanges = 0
- var leftPaddingChanges = 0
- var rightPaddingChanges = 0
- var bottomPaddingChanges = 0
- var horizontalPaddingChanges = 0
- var verticalPaddingChanges = 0
+ let paddingChanges = 0
+ let topPaddingChanges = 0
+ let leftPaddingChanges = 0
+ let rightPaddingChanges = 0
+ let bottomPaddingChanges = 0
+ let horizontalPaddingChanges = 0
+ let verticalPaddingChanges = 0
compare(control.padding, 0)
compare(control.topPadding, 0)
@@ -315,17 +317,17 @@ TestCase {
}
function test_availableSize() {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
verify(control)
- var availableWidthSpy = signalSpy.createObject(control, {target: control, signalName: "availableWidthChanged"})
+ let availableWidthSpy = signalSpy.createObject(control, {target: control, signalName: "availableWidthChanged"})
verify(availableWidthSpy.valid)
- var availableHeightSpy = signalSpy.createObject(control, {target: control, signalName: "availableHeightChanged"})
+ let availableHeightSpy = signalSpy.createObject(control, {target: control, signalName: "availableHeightChanged"})
verify(availableHeightSpy.valid)
- var availableWidthChanges = 0
- var availableHeightChanges = 0
+ let availableWidthChanges = 0
+ let availableHeightChanges = 0
control.width = 100
compare(control.availableWidth, 100)
@@ -385,10 +387,10 @@ TestCase {
}
function test_mirrored() {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
verify(control)
- var mirroredSpy = signalSpy.createObject(control, {target: control, signalName: "mirroredChanged"})
+ let mirroredSpy = signalSpy.createObject(control, {target: control, signalName: "mirroredChanged"})
verify(mirroredSpy.valid)
control.locale = Qt.locale("en_US")
@@ -414,7 +416,7 @@ TestCase {
}
function test_background() {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
verify(control)
control.background = component.createObject(control)
@@ -465,6 +467,36 @@ TestCase {
}
Component {
+ id: backgroundTest2
+ Button {
+ id: btn
+ width: 100
+ height: 100
+ topInset: 0
+ objectName: ""
+
+ background: Rectangle {
+ id: bg
+ implicitHeight: 80
+ border.color: "red"
+ y: btn.objectName === "aaa" ? 20 : 0
+ }
+ }
+ }
+
+ // QTBUG-120033: Make sure that the binding for y on the tab button's background doesn't get removed
+ function test_background2() {
+ let button = createTemporaryObject(backgroundTest2, testCase)
+ verify(button)
+
+ verify(button.background.y === 0)
+ button.objectName = "aaa"
+ verify(button.background.y === 20)
+ button.objectName = ""
+ verify(button.background.y === 0)
+ }
+
+ Component {
id: component2
T.Control {
id: item2
@@ -502,7 +534,7 @@ TestCase {
}
function test_font() {
- var control2 = createTemporaryObject(component2, testCase)
+ let control2 = createTemporaryObject(component2, testCase)
verify(control2)
verify(control2.item2_2)
verify(control2.item2_3)
@@ -645,7 +677,7 @@ TestCase {
}
function test_font_2() {
- var control3 = createTemporaryObject(component3, testCase)
+ let control3 = createTemporaryObject(component3, testCase)
verify(control3)
verify(control3.item3_2)
verify(control3.item3_3)
@@ -760,14 +792,14 @@ TestCase {
}
function test_font_3() {
- var control4 = createTemporaryObject(component4, testCase)
+ let control4 = createTemporaryObject(component4, testCase)
verify(control4)
verify(control4.item4_2)
verify(control4.item4_3)
verify(control4.item4_4)
- var family = control4.font.family
- var ps = control4.font.pixelSize
+ let family = control4.font.family
+ let ps = control4.font.pixelSize
compare(control4.item4_2.font.family, control4.font.family)
compare(control4.item4_3.font.family, control4.font.family)
@@ -797,19 +829,19 @@ TestCase {
}
function test_font_explicit_attributes(data) {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
verify(control)
- var child = component.createObject(control)
+ let child = component.createObject(control)
verify(child)
- var controlSpy = signalSpy.createObject(control, {target: control, signalName: "fontChanged"})
+ let controlSpy = signalSpy.createObject(control, {target: control, signalName: "fontChanged"})
verify(controlSpy.valid)
- var childSpy = signalSpy.createObject(child, {target: child, signalName: "fontChanged"})
+ let childSpy = signalSpy.createObject(child, {target: child, signalName: "fontChanged"})
verify(childSpy.valid)
- var defaultValue = control.font[data.tag]
+ let defaultValue = control.font[data.tag]
child.font[data.tag] = defaultValue
compare(child.font[data.tag], defaultValue)
@@ -825,7 +857,7 @@ TestCase {
}
function test_locale() {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
verify(control)
control.locale = Qt.locale("en_US")
@@ -896,12 +928,12 @@ TestCase {
}
function test_locale_2() {
- var control = createTemporaryObject(component5, testCase)
+ let control = createTemporaryObject(component5, testCase)
verify(control)
verify(control.item2_2)
verify(control.item2_3)
- var defaultLocale = Qt.locale()
+ let defaultLocale = Qt.locale()
compare(control.locale.name, defaultLocale.name)
compare(control.item2_2.locale.name, defaultLocale.name)
@@ -989,14 +1021,14 @@ TestCase {
}
function test_locale_3() {
- var control = createTemporaryObject(component6, testCase)
+ let control = createTemporaryObject(component6, testCase)
verify(control)
verify(control.item6_2)
verify(control.item6_3)
verify(control.item6_4)
verify(control.item6_5)
- var defaultLocale = Qt.locale()
+ let defaultLocale = Qt.locale()
compare(control.locale.name, defaultLocale.name)
compare(control.item6_5.locale.name, defaultLocale.name)
@@ -1028,7 +1060,7 @@ TestCase {
}
function test_hover(data) {
- var control = createTemporaryObject(data.target, testCase, {width: 100, height: 100})
+ let control = createTemporaryObject(data.target, testCase, {width: 100, height: 100})
verify(control)
compare(control.hovered, false)
@@ -1065,17 +1097,17 @@ TestCase {
}
function test_hoverEnabled() {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
compare(control.hoverEnabled, Qt.styleHints.useHoverEffects)
- var child = component.createObject(control)
- var grandChild = component.createObject(child)
+ let child = component.createObject(control)
+ let grandChild = component.createObject(child)
- var childExplicitHoverEnabled = component.createObject(control, {hoverEnabled: true})
- var grandChildExplicitHoverDisabled = component.createObject(childExplicitHoverEnabled, {hoverEnabled: false})
+ let childExplicitHoverEnabled = component.createObject(control, {hoverEnabled: true})
+ let grandChildExplicitHoverDisabled = component.createObject(childExplicitHoverEnabled, {hoverEnabled: false})
- var childExplicitHoverDisabled = component.createObject(control, {hoverEnabled: false})
- var grandChildExplicitHoverEnabled = component.createObject(childExplicitHoverDisabled, {hoverEnabled: true})
+ let childExplicitHoverDisabled = component.createObject(control, {hoverEnabled: false})
+ let grandChildExplicitHoverEnabled = component.createObject(childExplicitHoverDisabled, {hoverEnabled: true})
control.hoverEnabled = false
compare(control.hoverEnabled, false)
@@ -1099,33 +1131,33 @@ TestCase {
}
function test_implicitSize() {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
verify(control)
- var implicitWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitWidthChanged"})
+ let implicitWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitWidthChanged"})
verify(implicitWidthSpy.valid)
- var implicitHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitHeightChanged"})
+ let implicitHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitHeightChanged"})
verify(implicitHeightSpy.valid)
- var implicitContentWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitContentWidthChanged"})
+ let implicitContentWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitContentWidthChanged"})
verify(implicitContentWidthSpy.valid)
- var implicitContentHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitContentHeightChanged"})
+ let implicitContentHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitContentHeightChanged"})
verify(implicitContentHeightSpy.valid)
- var implicitBackgroundWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundWidthChanged"})
+ let implicitBackgroundWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundWidthChanged"})
verify(implicitBackgroundWidthSpy.valid)
- var implicitBackgroundHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundHeightChanged"})
+ let implicitBackgroundHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundHeightChanged"})
verify(implicitBackgroundHeightSpy.valid)
- var implicitWidthChanges = 0
- var implicitHeightChanges = 0
- var implicitContentWidthChanges = 0
- var implicitContentHeightChanges = 0
- var implicitBackgroundWidthChanges = 0
- var implicitBackgroundHeightChanges = 0
+ let implicitWidthChanges = 0
+ let implicitHeightChanges = 0
+ let implicitContentWidthChanges = 0
+ let implicitContentHeightChanges = 0
+ let implicitBackgroundWidthChanges = 0
+ let implicitBackgroundHeightChanges = 0
compare(control.implicitWidth, 0)
compare(control.implicitHeight, 0)
@@ -1194,12 +1226,12 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(component, testCase)
+ let control = createTemporaryObject(component, testCase)
verify(control)
compare(control.baselineOffset, 0)
- var baselineSpy = signalSpy.createObject(control, {target: control, signalName: "baselineOffsetChanged"})
+ let baselineSpy = signalSpy.createObject(control, {target: control, signalName: "baselineOffsetChanged"})
verify(baselineSpy.valid)
control.contentItem = rectangle.createObject(control, {baselineOffset: 12})
@@ -1232,25 +1264,25 @@ TestCase {
}
function test_inset() {
- var control = createTemporaryObject(component, testCase, {background: rectangle.createObject(control)})
+ let control = createTemporaryObject(component, testCase, {background: rectangle.createObject(null)})
verify(control)
- var topInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topInsetChanged"})
+ let topInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topInsetChanged"})
verify(topInsetSpy.valid)
- var leftInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftInsetChanged"})
+ let leftInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftInsetChanged"})
verify(leftInsetSpy.valid)
- var rightInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightInsetChanged"})
+ let rightInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightInsetChanged"})
verify(rightInsetSpy.valid)
- var bottomInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomInsetChanged"})
+ let bottomInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomInsetChanged"})
verify(bottomInsetSpy.valid)
- var topInsetChanges = 0
- var leftInsetChanges = 0
- var rightInsetChanges = 0
- var bottomInsetChanges = 0
+ let topInsetChanges = 0
+ let leftInsetChanges = 0
+ let rightInsetChanges = 0
+ let bottomInsetChanges = 0
compare(control.topInset, 0)
compare(control.leftInset, 0)
@@ -1412,9 +1444,9 @@ TestCase {
}
function test_contentItemDeletionOrder() {
- var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ let control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
verify(control1)
- var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ let control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
verify(control2)
}
@@ -1453,9 +1485,9 @@ TestCase {
}
function test_backgroundDeletionOrder() {
- var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ let control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
verify(control1)
- var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ let control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
verify(control2)
}
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml b/tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml
index 67403e240a..b93337e62d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -18,15 +18,17 @@ TestCase {
DayOfWeekRow { }
}
- function test_defaults() {
+ function init () {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(component, testCase)
verify(control)
}
function test_locale() {
- var control = component.createObject(testCase)
+ let control = component.createObject(testCase)
verify(control.contentItem.children[0])
@@ -43,7 +45,7 @@ TestCase {
}
function test_font() {
- var control = component.createObject(testCase)
+ let control = component.createObject(testCase)
verify(control.contentItem.children[0])
diff --git a/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml b/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml
index e2abe389ca..708e6a8a22 100644
--- a/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -38,18 +38,20 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(defaultComponent, testCase)
verify(control)
}
function test_mouse() {
- var control = createTemporaryObject(delayButton, testCase)
+ let control = createTemporaryObject(delayButton, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// click
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
@@ -145,12 +147,12 @@ TestCase {
}
function test_touch() {
- var control = createTemporaryObject(delayButton, testCase)
+ let control = createTemporaryObject(delayButton, testCase)
verify(control)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// click
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
@@ -224,13 +226,13 @@ TestCase {
}
function test_keys() {
- var control = createTemporaryObject(delayButton, testCase)
+ let control = createTemporaryObject(delayButton, testCase)
verify(control)
control.forceActiveFocus()
verify(control.activeFocus)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// click
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }],
@@ -275,8 +277,8 @@ TestCase {
sequenceSpy.expectedSequence = []
// Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
// pressing buttons and the CI uses the QGnomeTheme platform theme.
- var keys = [Qt.Key_Escape, Qt.Key_Tab]
- for (var i = 0; i < keys.length; ++i) {
+ let keys = [Qt.Key_Escape, Qt.Key_Tab]
+ for (let i = 0; i < keys.length; ++i) {
sequenceSpy.reset()
keyClick(keys[i])
verify(sequenceSpy.success)
@@ -284,10 +286,10 @@ TestCase {
}
function test_progress() {
- var control = createTemporaryObject(delayButton, testCase)
+ let control = createTemporaryObject(delayButton, testCase)
verify(control)
- var progressSpy = signalSpy.createObject(control, {target: control, signalName: "progressChanged"})
+ let progressSpy = signalSpy.createObject(control, {target: control, signalName: "progressChanged"})
verify(progressSpy.valid)
compare(control.progress, 0.0)
@@ -297,7 +299,7 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(delayButton, testCase)
+ let control = createTemporaryObject(delayButton, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_dial.qml b/tests/auto/quickcontrols/controls/data/tst_dial.qml
index 3fcf8c7e88..1f2b9fdd5c 100644
--- a/tests/auto/quickcontrols/controls/data/tst_dial.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_dial.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -27,10 +27,13 @@ TestCase {
SignalSpy {}
}
- function test_instance() {
+ function init() {
+ // Fail on any warning that we don't expect.
failOnWarning(/.?/)
+ }
- var dial = createTemporaryObject(dialComponent, testCase);
+ function test_instance() {
+ let dial = createTemporaryObject(dialComponent, testCase);
verify(dial);
compare(dial.value, 0.0);
compare(dial.from, 0.0);
@@ -41,7 +44,7 @@ TestCase {
}
function test_value() {
- var dial = createTemporaryObject(dialComponent, testCase);
+ let dial = createTemporaryObject(dialComponent, testCase);
verify(dial);
compare(dial.value, 0.0);
@@ -59,7 +62,7 @@ TestCase {
}
function test_range() {
- var dial = createTemporaryObject(dialComponent, testCase);
+ let dial = createTemporaryObject(dialComponent, testCase);
verify(dial);
dial.from = 0;
@@ -94,7 +97,7 @@ TestCase {
}
function test_inverted() {
- var dial = createTemporaryObject(dialComponent, testCase, { from: 1.0, to: -1.0 });
+ let dial = createTemporaryObject(dialComponent, testCase, { from: 1.0, to: -1.0 });
verify(dial);
compare(dial.from, 1.0);
compare(dial.to, -1.0);
@@ -120,7 +123,7 @@ TestCase {
}
function test_pressed() {
- var dial = createTemporaryObject(dialComponent, testCase);
+ let dial = createTemporaryObject(dialComponent, testCase);
verify(dial);
pressSpy.target = dial;
@@ -135,7 +138,7 @@ TestCase {
verify(!dial.pressed);
compare(pressSpy.count, 2);
- var touch = touchEvent(dial);
+ let touch = touchEvent(dial);
touch.press(0).commit();
verify(dial.pressed);
compare(pressSpy.count, 3);
@@ -160,7 +163,7 @@ TestCase {
}
function test_dragging(data) {
- var dial = createTemporaryObject(dialComponent, testCase);
+ let dial = createTemporaryObject(dialComponent, testCase);
verify(dial);
dial.wrap = true;
@@ -172,10 +175,10 @@ TestCase {
valueSpy.target = dial;
verify(valueSpy.valid);
- var moveSpy = createTemporaryObject(signalSpy, testCase, {target: dial, signalName: "moved"});
+ let moveSpy = createTemporaryObject(signalSpy, testCase, {target: dial, signalName: "moved"});
verify(moveSpy.valid);
- var minimumExpectedValueCount = data.live ? 2 : 1;
+ let minimumExpectedValueCount = data.live ? 2 : 1;
// drag to the left
// we always add or subtract 1 to ensure we start the drag from the opposite side
@@ -217,55 +220,56 @@ TestCase {
}
function test_nonWrapping() {
- var dial = createTemporaryObject(dialComponent, testCase);
+ let dial = createTemporaryObject(dialComponent, testCase);
verify(dial);
compare(dial.wrap, false);
dial.value = 0;
// Ensure that dragging from bottom left to bottom right doesn't work.
- var yPos = dial.height * 0.75;
+ let yPos = dial.height * 0.75;
mousePress(dial, dial.width * 0.25, yPos, Qt.LeftButton);
- var positionAtPress = dial.position;
+ let positionAtPress = dial.position;
mouseMove(dial, dial.width * 0.5, yPos);
- compare(dial.position, positionAtPress);
+ verify(dial.position < positionAtPress);
mouseMove(dial, dial.width * 0.75, yPos);
- compare(dial.position, positionAtPress);
+ verify(dial.position < positionAtPress);
mouseRelease(dial, dial.width * 0.75, yPos, Qt.LeftButton);
- compare(dial.position, positionAtPress);
+ verify(dial.position < positionAtPress);
// Try the same thing, but a bit higher.
yPos = dial.height * 0.6;
mousePress(dial, dial.width * 0.25, yPos, Qt.LeftButton);
positionAtPress = dial.position;
mouseMove(dial, dial.width * 0.5, yPos);
- compare(dial.position, positionAtPress);
+ verify(dial.position < positionAtPress);
mouseMove(dial, dial.width * 0.75, yPos);
- compare(dial.position, positionAtPress);
+ verify(dial.position < positionAtPress);
mouseRelease(dial, dial.width * 0.75, yPos, Qt.LeftButton);
- compare(dial.position, positionAtPress);
+ verify(dial.position < positionAtPress);
// Going from below the center of the dial to above it should work (once it gets above the center).
mousePress(dial, dial.width * 0.25, dial.height * 0.75, Qt.LeftButton);
positionAtPress = dial.position;
mouseMove(dial, dial.width * 0.5, dial.height * 0.6);
- compare(dial.position, positionAtPress);
- mouseMove(dial, dial.width * 0.75, dial.height * 0.4);
+ verify(dial.position < positionAtPress);
+ mouseMove(dial, dial.width * 0.5, dial.height * 0.4); //move over the top
+ mouseMove(dial, dial.width * 0.75, dial.height * 0.6); //and back down again
verify(dial.position > positionAtPress);
mouseRelease(dial, dial.width * 0.75, dial.height * 0.3, Qt.LeftButton);
verify(dial.position > positionAtPress);
}
function test_touch() {
- var dial = createTemporaryObject(dialComponent, testCase);
+ let dial = createTemporaryObject(dialComponent, testCase);
verify(dial);
- var touch = touchEvent(dial);
+ let touch = touchEvent(dial);
// Ensure that dragging from bottom left to bottom right doesn't work.
- var yPos = dial.height * 0.75;
+ let yPos = dial.height * 0.75;
touch.press(0, dial, dial.width * 0.25, yPos).commit();
- var positionAtPress = dial.position;
+ let positionAtPress = dial.position;
touch.move(0, dial, dial.width * 0.5, yPos).commit();
compare(dial.position, positionAtPress);
touch.move(0, dial, dial.width * 0.75, yPos).commit();
@@ -289,22 +293,23 @@ TestCase {
positionAtPress = dial.position;
touch.move(0, dial, dial.width * 0.5, dial.height * 0.6).commit();
compare(dial.position, positionAtPress);
- touch.move(0, dial, dial.width * 0.75, dial.height * 0.4).commit();
+ touch.move(0, dial, dial.width * 0.5, dial.height * 0.4).commit(); //move over the top
+ touch.move(0, dial, dial.width * 0.75, dial.height * 0.6).commit(); //and back down again
verify(dial.position > positionAtPress);
touch.release(0, dial, dial.width * 0.75, dial.height * 0.3).commit();
verify(dial.position > positionAtPress);
}
function test_multiTouch() {
- var dial1 = createTemporaryObject(dialComponent, testCase);
+ let dial1 = createTemporaryObject(dialComponent, testCase);
verify(dial1);
- var touch = touchEvent(dial1);
+ let touch = touchEvent(dial1);
touch.press(0, dial1).commit().move(0, dial1, dial1.width / 4, dial1.height / 4).commit();
compare(dial1.pressed, true);
verify(dial1.position > 0.0);
- var pos1Before = dial1.position;
+ let pos1Before = dial1.position;
// second touch point on the same control is ignored
touch.stationary(0).press(1, dial1, 0, 0).commit()
@@ -313,7 +318,7 @@ TestCase {
compare(dial1.pressed, true);
compare(dial1.position, pos1Before);
- var dial2 = createTemporaryObject(dialComponent, testCase, {y: dial1.height});
+ let dial2 = createTemporaryObject(dialComponent, testCase, {y: dial1.height});
verify(dial2);
// press the second dial
@@ -322,7 +327,7 @@ TestCase {
compare(dial2.position, 0.0);
pos1Before = dial1.position;
- var pos2Before = dial2.position;
+ let pos2Before = dial2.position;
// move both dials
touch.move(0, dial1).move(2, dial2, dial2.width / 4, dial2.height / 4).commit();
@@ -354,19 +359,19 @@ TestCase {
}
function test_keyboardNavigation() {
- var dial = createTemporaryObject(dialComponent, testCase);
+ let dial = createTemporaryObject(dialComponent, testCase);
verify(dial);
- var focusScope = createTemporaryObject(focusTest, testCase);
+ let focusScope = createTemporaryObject(focusTest, testCase);
verify(focusScope);
- var moveCount = 0;
+ let moveCount = 0;
// Tests that we've accepted events that we're interested in.
parentEventSpy.target = focusScope;
parentEventSpy.signalName = "receivedKeyPress";
- var moveSpy = createTemporaryObject(signalSpy, testCase, {target: dial, signalName: "moved"});
+ let moveSpy = createTemporaryObject(signalSpy, testCase, {target: dial, signalName: "moved"});
verify(moveSpy.valid);
dial.parent = focusScope;
@@ -382,10 +387,10 @@ TestCase {
compare(moveSpy.count, moveCount);
compare(dial.value, 0);
- var oldValue = 0.0;
- var keyPairs = [[Qt.Key_Left, Qt.Key_Right], [Qt.Key_Down, Qt.Key_Up]];
- for (var keyPairIndex = 0; keyPairIndex < 2; ++keyPairIndex) {
- for (var i = 1; i <= 10; ++i) {
+ let oldValue = 0.0;
+ let keyPairs = [[Qt.Key_Left, Qt.Key_Right], [Qt.Key_Down, Qt.Key_Up]];
+ for (let keyPairIndex = 0; keyPairIndex < 2; ++keyPairIndex) {
+ for (let i = 1; i <= 10; ++i) {
oldValue = dial.value;
keyClick(keyPairs[keyPairIndex][1]);
compare(parentEventSpy.count, 0);
@@ -396,7 +401,7 @@ TestCase {
compare(dial.value, dial.to);
- for (i = 10; i > 0; --i) {
+ for (let i = 10; i > 0; --i) {
oldValue = dial.value;
keyClick(keyPairs[keyPairIndex][0]);
compare(parentEventSpy.count, 0);
@@ -434,7 +439,7 @@ TestCase {
{ tag: "NoSnap", snapMode: Dial.NoSnap, from: 0, to: 2, values: [0, 0, 1], positions: [0, 0.5, 0.5] },
{ tag: "SnapAlways (0..2)", snapMode: Dial.SnapAlways, from: 0, to: 2, values: [0.0, 0.0, 1.0], positions: [0.0, 0.5, 0.5] },
{ tag: "SnapAlways (1..3)", snapMode: Dial.SnapAlways, from: 1, to: 3, values: [1.0, 1.0, 2.0], positions: [0.0, 0.5, 0.5] },
- { tag: "SnapAlways (-1..1)", snapMode: Dial.SnapAlways, from: -1, to: 1, values: [0.0, 0.0, 0.0], positions: [0.5, 0.5, 0.5] },
+ { tag: "SnapAlways (-1..1)", snapMode: Dial.SnapAlways, from: -1, to: 1, values: [0.0, 0.0, 0.0], positions: [immediate ? 0.0 : 0.5, 0.5, 0.5] },
{ tag: "SnapAlways (1..-1)", snapMode: Dial.SnapAlways, from: 1, to: -1, values: [1.0, 1.0, 0.0], positions: [0.0, 0.5, 0.5] },
{ tag: "SnapOnRelease (0..2)", snapMode: Dial.SnapOnRelease, from: 0, to: 2, values: [0.0, 0.0, 1.0], positions: [0.0, 0.5, 0.5] },
{ tag: "SnapOnRelease (1..3)", snapMode: Dial.SnapOnRelease, from: 1, to: 3, values: [1.0, 1.0, 2.0], positions: [0.0, 0.5, 0.5] },
@@ -448,7 +453,7 @@ TestCase {
}
function test_snapMode_mouse(data) {
- var dial = createTemporaryObject(dialComponent, testCase, {live: false});
+ let dial = createTemporaryObject(dialComponent, testCase, {live: false});
verify(dial);
dial.snapMode = data.snapMode;
@@ -456,7 +461,7 @@ TestCase {
dial.to = data.to;
dial.stepSize = 0.2;
- var fuzz = 0.055;
+ let fuzz = 0.055;
mousePress(dial, dial.width * 0.25, dial.height * 0.75);
fuzzyCompare(dial.value, data.values[0], fuzz);
@@ -476,7 +481,7 @@ TestCase {
}
function test_snapMode_touch(data) {
- var dial = createTemporaryObject(dialComponent, testCase, {live: false});
+ let dial = createTemporaryObject(dialComponent, testCase, {live: false});
verify(dial);
dial.snapMode = data.snapMode;
@@ -484,9 +489,9 @@ TestCase {
dial.to = data.to;
dial.stepSize = 0.2;
- var fuzz = 0.05;
+ let fuzz = 0.05;
- var touch = touchEvent(dial);
+ let touch = touchEvent(dial);
touch.press(0, dial, dial.width * 0.25, dial.height * 0.75).commit()
compare(dial.value, data.values[0]);
compare(dial.position, data.positions[0]);
@@ -502,13 +507,13 @@ TestCase {
function test_wheel_data() {
return [
- { tag: "horizontal", orientation: Qt.Horizontal, dx: 120, dy: 0 },
- { tag: "vertical", orientation: Qt.Vertical, dx: 0, dy: 120 }
+ { tag: "horizontal", dx: 120, dy: 0 },
+ { tag: "vertical", dx: 0, dy: 120 }
]
}
function test_wheel(data) {
- var control = createTemporaryObject(dialComponent, testCase, {wheelEnabled: true, orientation: data.orientation})
+ let control = createTemporaryObject(dialComponent, testCase, {wheelEnabled: true})
verify(control)
compare(control.value, 0.0)
@@ -546,7 +551,7 @@ TestCase {
}
function test_nullHandle() {
- var control = createTemporaryObject(dialComponent, testCase)
+ let control = createTemporaryObject(dialComponent, testCase)
verify(control)
control.handle = null
@@ -562,7 +567,7 @@ TestCase {
if (inputEventType === "mouseInput") {
mouseMove(control, x, y);
} else {
- var touch = touchEvent(control);
+ let touch = touchEvent(control);
touch.move(0, control, x, y).commit();
}
}
@@ -571,7 +576,7 @@ TestCase {
if (inputEventType === "mouseInput") {
mousePress(control, x, y);
} else {
- var touch = touchEvent(control);
+ let touch = touchEvent(control);
touch.press(0, control, x, y).commit();
}
}
@@ -580,13 +585,13 @@ TestCase {
if (inputEventType === "mouseInput") {
mouseRelease(control, x, y);
} else {
- var touch = touchEvent(control);
+ let touch = touchEvent(control);
touch.release(0, control, x, y).commit();
}
}
function test_horizontalAndVertical_data() {
- var data = [
+ let data = [
{ eventType: "mouseInput", inputMode: Dial.Vertical, moveToX: 0.5, moveToY: 0.25, expectedPosition: 0.125 },
// Horizontal movement should have no effect on a vertical dial.
{ eventType: "mouseInput", inputMode: Dial.Vertical, moveToX: 2.0, moveToY: 0.25, expectedPosition: 0.125 },
@@ -612,15 +617,15 @@ TestCase {
];
// Do the same tests for touch by copying the mouse tests and adding them to the end of the array.
- var mouseTestCount = data.length;
- for (var i = mouseTestCount; i < mouseTestCount * 2; ++i) {
+ let mouseTestCount = data.length;
+ for (let i = mouseTestCount; i < mouseTestCount * 2; ++i) {
// Shallow-copy the object.
data[i] = JSON.parse(JSON.stringify(data[i - mouseTestCount]));
data[i].eventType = "touchInput";
}
- for (i = 0; i < data.length; ++i) {
- var row = data[i];
+ for (let i = 0; i < data.length; ++i) {
+ let row = data[i];
row.tag = "eventType=" + row.eventType + ", "
+ "inputMode=" + (row.inputMode === Dial.Vertical ? "Vertical" : "Horizontal") + ", "
+ "moveToX=" + row.moveToX + ", moveToY=" + row.moveToY + ", "
@@ -631,7 +636,7 @@ TestCase {
}
function test_horizontalAndVertical(data) {
- var control = createTemporaryObject(dialComponent, testCase, { inputMode: data.inputMode });
+ let control = createTemporaryObject(dialComponent, testCase, { inputMode: data.inputMode });
verify(control);
press(data.eventType, control);
@@ -648,7 +653,7 @@ TestCase {
}
function test_integerStepping() {
- var dial = createTemporaryObject(dialComponent, testCase)
+ let dial = createTemporaryObject(dialComponent, testCase)
verify(dial)
dial.from = 1
@@ -661,4 +666,202 @@ TestCase {
keyClick(Qt.Key_Right)
}
}
+
+ function test_startEndAngle_data() {
+ return [
+ {
+ tag: "Default wrap", startAngle: -140, endAngle: 140, from: 0, to: 1, wrap: true,
+ x: [0.49, 0.25, 0.5, 0.75, 0.51, 0.49, 0.51],
+ y: [0.99, 0.5, 0.01, 0.5, 0.99, 0.99, 0.99],
+ values: [0.0, 0.5-0.32, 0.5, 0.5+0.32, 1.0, 0.0, 1.0], //140/90*0.5 = 0.32
+ angles: [-140.0, -90.0, 0.0, 90.0, 140.0, -140.0, 140.0],
+ wrapClockwise: 1,
+ wrapCounterClockwise: 1
+ },
+ {
+ tag: "-30..30 wrap", startAngle: -30, endAngle: 30, from: 0, to: 1, wrap: true,
+ x: [0.49, 0.25, 0.5, 0.75, 0.51, 0.49, 0.51],
+ y: [0.99, 0.5, 0.01, 0.5, 0.99, 0.99, 0.99],
+ values: [0.0, 0.0, 0.5, 1.0, 1.0, 0.0, 1.0],
+ angles: [-30.0, -30.0, 0.0, 30.0, 30.0, -30.0, 30.0],
+ wrapClockwise: 0, //no wrap if angle < 180
+ wrapCounterClockwise: 0
+ },
+ {
+ tag: "-180..180 wrap", startAngle: -180, endAngle: 180, from: 0, to: 1, wrap: true,
+ x: [0.49, 0.25, 0.5, 0.75, 0.51, 0.49, 0.51],
+ y: [0.99, 0.5, 0.01, 0.5, 0.99, 0.99, 0.99],
+ values: [0.0, 0.25, 0.5, 0.75, 1.0, 0.0, 1.0],
+ angles: [-180.0, -90.0, 0.0, 90.0, 180.0, -180.0, 180.0],
+ wrapClockwise: 1,
+ wrapCounterClockwise: 1
+ },
+ {
+ tag: "90..360 wrap", startAngle: 90, endAngle: 360, from: 0, to: 1, wrap: true,
+ x: [0.49, 0.25, 0.5, 0.75, 0.51, 0.49, 0.5],
+ y: [0.99, 0.5, 0.01, 0.5, 0.99, 0.99, 0.01],
+ values: [0.33, 0.66, 1.0, 0.0, 0.33, 0.33, 1.0],
+ angles: [180.0, 270.0, 360.0, 90.0, 180.0, 180.0, 360.0],
+ wrapClockwise: 1,
+ wrapCounterClockwise: 1
+ },
+ {
+ tag: "90..450 wrap", startAngle: 90, endAngle: 450, from: 0, to: 1, wrap: true,
+ x: [0.49, 0.25, 0.5, 0.75, 0.75, 0.51, 0.49, 0.75, 0.75],
+ y: [0.99, 0.5, 0.01, 0.49, 0.501, 0.99, 0.99, 0.49, 0.501],
+ values: [0.25, 0.5, 0.75, 1.0, 0.0, 0.25, 0.25, 1.0, 0.0],
+ angles: [180.0, 270.0, 360.0, 450.0, 90.0, 180.0, 180.0, 450.0, 90.0],
+ wrapClockwise: 2,
+ wrapCounterClockwise: 1
+ },
+ {
+ tag: "Default nowrap", startAngle: -140, endAngle: 140, from: 0, to: 1, wrap: false,
+ x: [0.49, 0.25, 0.5, 0.75, 0.51, 0.49],
+ y: [0.99, 0.5, 0.01, 0.5, 0.99, 0.99],
+ values: [0.0, 0.5-0.32, 0.5, 0.5+0.32, 1.0, 1.0], //140/90*0.5 = 0.32
+ angles: [-140.0, -90.0, 0.0, 90.0, 140.0, 140.0],
+ wrapClockwise: 0,
+ wrapCounterClockwise: 0
+ },
+ {
+ tag: "-30..30 nowrap", startAngle: -30, endAngle: 30, from: 0, to: 1, wrap: false,
+ x: [0.49, 0.25, 0.5, 0.75, 0.51, 0.49],
+ y: [0.99, 0.5, 0.01, 0.5, 0.99, 0.99],
+ values: [0.0, 0.0, 0.5, 1.0, 1.0, 1.0],
+ angles: [-30.0, -30.0, 0.0, 30.0, 30.0, 30.0],
+ wrapClockwise: 0,
+ wrapCounterClockwise: 0
+ },
+ {
+ tag: "-180..180 nowrap", startAngle: -180, endAngle: 180, from: 0, to: 1, wrap: false,
+ x: [0.49, 0.25, 0.5, 0.75, 0.51, 0.49],
+ y: [0.99, 0.5, 0.01, 0.5, 0.99, 0.99],
+ values: [0.0, 0.25, 0.5, 0.75, 1.0, 1.0],
+ angles: [-180.0, -90.0, 0.0, 90.0, 180.0, 180.0],
+ wrapClockwise: 0,
+ wrapCounterClockwise: 0
+ },
+ {
+ tag: "90..360 nowrap", startAngle: 90, endAngle: 360, from: 0, to: 1, wrap: false,
+ x: [0.49, 0.25, 0.5, 0.75, 0.51, 0.49],
+ y: [0.99, 0.5, 0.01, 0.5, 0.99, 0.99],
+ values: [0.33, 0.66, 1.0, 1.0, 1.0, 1.0],
+ angles: [180.0, 270.0, 360.0, 360.0, 360.0, 360.0],
+ wrapClockwise: 0,
+ wrapCounterClockwise: 0
+ }
+ ]
+ }
+
+ function test_startEndAngle(data) {
+ let dial = createTemporaryObject(dialComponent, testCase)
+ verify(dial)
+
+ dial.startAngle = data.startAngle
+ dial.endAngle = data.endAngle
+ dial.from = data.from
+ dial.to = data.to
+ //Give a defined start in case wrap = true
+ dial.value = data.values[0]
+ dial.wrap = data.wrap
+
+ compare(dial.startAngle, data.startAngle)
+ compare(dial.endAngle, data.endAngle)
+
+ let wrappedSpy = signalSpy.createObject(dial, {target: dial, signalName: "wrapped"})
+ verify(wrappedSpy.valid)
+
+ for (let i = 0; i < data.x.length; i++) {
+ mousePress(dial, dial.width * data.x[i], dial.height * 0.5 + dial.width * ( data.y[i] - 0.5))
+ fuzzyCompare(dial.angle, data.angles[i], 3.0)
+ fuzzyCompare(dial.value, data.values[i], 0.1)
+ }
+
+ let clockwiseCount = 0
+ let counterClockwiseCount = 0
+ for (let i = 0; i < wrappedSpy.count; i++) {
+ if (wrappedSpy.signalArguments[i][0] == 0)
+ clockwiseCount++;
+ else
+ counterClockwiseCount++;
+ }
+
+ compare(clockwiseCount, data.wrapClockwise)
+ compare(counterClockwiseCount, data.wrapCounterClockwise)
+ }
+
+ function test_startEndAngleWarnings(data) {
+ let dial = createTemporaryObject(dialComponent, testCase)
+ verify(dial)
+
+ dial.startAngle = -180.
+ dial.endAngle = 180.
+
+ //provoke warning
+ ignoreWarning(new RegExp("Changing endAngle to avoid overlaps"))
+ dial.startAngle = -270.
+ dial.endAngle = 90.
+
+ compare(dial.startAngle, -270.)
+ compare(dial.endAngle, 90.)
+
+
+ dial.startAngle = -180.
+ dial.endAngle = 180.
+
+ //provoke warning
+ ignoreWarning(new RegExp("Changing startAngle to avoid overlaps"))
+ dial.endAngle = 270.
+ dial.startAngle = -90.
+
+ compare(dial.startAngle, -90.)
+ compare(dial.endAngle, 270.)
+
+ {
+ // Should not warn since we delay the setting of start and end angles to avoid
+ // binding order evaluation conflicts.
+ let dial = createTemporaryObject(dialComponent, testCase, { startAngle: -10, endAngle: 300 })
+ verify(dial)
+ compare(dial.startAngle, -10.)
+ compare(dial.endAngle, 300.)
+ }
+ }
+
+ function test_notSquareGeometry() {
+ let dial = createTemporaryObject(dialComponent, testCase)
+ verify(dial);
+ if (!dial.handle) {
+ skip("Test cannot run on styles where handle == null (macOS style)")
+ }
+ dial.from = 0
+ dial.to = 1
+ dial.live = true
+ dial.wrap = true
+ dial.startAngle = -180
+ dial.endAngle = 180
+
+ // Dial input handling always assumes that the dial is in the *center*.
+ // Instantiate a Dial with a geometries of 400x100 and then 100x400
+ // Some styles always could wrongly align the Dial background and handle in the topLeft
+ // corner. Pressing in the handle would cause the Dial to move because the dial
+ // assumes that the "Dial circle" is center aligned in its geometry.
+ for (let pass = 0; pass < 2; ++pass) {
+ if (pass === 0) {
+ dial.width = testCase.width
+ dial.height = 100
+ } else {
+ dial.width = 100
+ dial.height = testCase.height
+ }
+
+ let val = pass * 0.25
+ dial.value = val
+ // find coordinates in the middle of the handle
+ let pt2 = dial.mapFromItem(dial.handle, dial.handle.width/2, dial.handle.height/2)
+ // press the knob in the middle. It shouldn't move (except from due to rounding errors)
+ mousePress(dial, pt2.x, pt2.y)
+ fuzzyCompare(dial.value, val, 0.1)
+ }
+ }
+
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_dialog.qml b/tests/auto/quickcontrols/controls/data/tst_dialog.qml
index 76ff95a32a..8c888be98f 100644
--- a/tests/auto/quickcontrols/controls/data/tst_dialog.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -39,13 +39,12 @@ TestCase {
}
function init() {
+ failOnWarning(/.?/)
tryCompare(testCase.Window.window, "active", true)
}
function test_defaults() {
- failOnWarning(/.?/)
-
- var control = createTemporaryObject(dialog, testCase)
+ let control = createTemporaryObject(dialog, testCase)
verify(control)
verify(control.header)
verify(control.footer)
@@ -54,9 +53,9 @@ TestCase {
}
function test_accept() {
- var control = createTemporaryObject(dialog, testCase)
+ let control = createTemporaryObject(dialog, testCase)
- var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
+ let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
verify(openedSpy.valid)
control.open()
@@ -64,10 +63,10 @@ TestCase {
compare(openedSpy.count, 1)
verify(control.visible)
- var acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"})
+ let acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"})
verify(acceptedSpy.valid)
- var closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"})
+ let closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"})
verify(closedSpy.valid)
control.accept()
@@ -80,9 +79,9 @@ TestCase {
}
function test_reject() {
- var control = createTemporaryObject(dialog, testCase)
+ let control = createTemporaryObject(dialog, testCase)
- var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
+ let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
verify(openedSpy.valid)
control.open()
@@ -90,10 +89,10 @@ TestCase {
compare(openedSpy.count, 1)
verify(control.visible)
- var rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"})
+ let rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"})
verify(rejectedSpy.valid)
- var closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"})
+ let closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"})
verify(closedSpy.valid)
control.reject()
@@ -127,6 +126,8 @@ TestCase {
control.closePolicy = Popup.CloseOnPressOutside
control.open()
verify(control.visible)
+ // wait for enter transitions to finish
+ openedSpy.wait()
mousePress(testCase, 1, 1)
compare(rejectedSpy.count, 3)
@@ -145,6 +146,7 @@ TestCase {
control.closePolicy = Popup.CloseOnReleaseOutside
control.open()
verify(control.visible)
+ openedSpy.wait()
mousePress(testCase, 1, 1)
compare(rejectedSpy.count, 3)
@@ -165,46 +167,46 @@ TestCase {
}
function test_buttonBox(data) {
- var control = createTemporaryObject(dialog, testCase)
+ let control = createTemporaryObject(dialog, testCase)
if (data.custom)
control.footer = buttonBox.createObject(testCase)
control.standardButtons = Dialog.Ok | Dialog.Cancel
- var box = control.footer
+ let box = control.footer
verify(box)
compare(box.standardButtons, Dialog.Ok | Dialog.Cancel)
- var acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"})
+ let acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"})
verify(acceptedSpy.valid)
box.accepted()
compare(acceptedSpy.count, 1)
- var rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"})
+ let rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"})
verify(rejectedSpy.valid)
box.rejected()
compare(rejectedSpy.count, 1)
}
function test_qtbug71444() {
- var control = createTemporaryObject(qtbug71444, testCase)
+ let control = createTemporaryObject(qtbug71444, testCase)
verify(control)
}
function test_standardButtons() {
- var control = createTemporaryObject(dialog, testCase)
+ let control = createTemporaryObject(dialog, testCase)
control.standardButtons = Dialog.Ok
- var box = control.footer ? control.footer : control.header
+ let box = control.footer ? control.footer : control.header
verify(box)
compare(box.count, 1)
- var okButton = box.itemAt(0)
+ let okButton = box.itemAt(0)
verify(okButton)
compare(okButton.text.toUpperCase(), "OK")
control.standardButtons = Dialog.Cancel
compare(box.count, 1)
- var cancelButton = control.footer.itemAt(0)
+ let cancelButton = control.footer.itemAt(0)
verify(cancelButton)
compare(cancelButton.text.toUpperCase(), "CANCEL")
@@ -227,10 +229,10 @@ TestCase {
}
function test_layout() {
- var control = createTemporaryObject(dialog, testCase, {width: 100, height: 100})
+ let control = createTemporaryObject(dialog, testCase, {width: 100, height: 100})
verify(control)
- var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
+ let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
verify(openedSpy.valid)
control.open()
@@ -300,6 +302,7 @@ TestCase {
compare(control.implicitHeight, control.contentItem.implicitHeight + control.topPadding + control.bottomPadding
+ control.header.implicitHeight + control.footer.implicitHeight)
+ control.footer.implicitWidth = 0
control.header.implicitWidth = 150
compare(control.implicitWidth, control.header.implicitWidth)
@@ -320,10 +323,10 @@ TestCase {
}
function test_spacing(data) {
- var control = createTemporaryObject(dialog, testCase, {spacing: 20, width: 100, height: 100})
+ let control = createTemporaryObject(dialog, testCase, {spacing: 20, width: 100, height: 100})
verify(control)
- var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
+ let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
verify(openedSpy.valid)
control.open()
@@ -377,14 +380,14 @@ TestCase {
}
function test_signals(data) {
- var control = createTemporaryObject(dialog, testCase)
+ let control = createTemporaryObject(dialog, testCase)
verify(control)
control.standardButtons = data.standardButton
- var button = control.standardButton(data.standardButton)
+ let button = control.standardButton(data.standardButton)
verify(button)
- var buttonSpy = signalSpy.createObject(control.contentItem, {target: control, signalName: data.signalName})
+ let buttonSpy = signalSpy.createObject(control.contentItem, {target: control, signalName: data.signalName})
verify(buttonSpy.valid)
button.clicked()
@@ -428,7 +431,7 @@ TestCase {
if (Qt.platform.pluginName === "offscreen")
skip("QTBUG-89909")
- var window = createTemporaryObject(qtbug85884, testCase)
+ let window = createTemporaryObject(qtbug85884, testCase)
verify(window)
tryCompare(window, "focusItemActiveFocus", true)
diff --git a/tests/auto/quickcontrols/controls/data/tst_dialogbuttonbox.qml b/tests/auto/quickcontrols/controls/data/tst_dialogbuttonbox.qml
index 6eb339a192..2fa26881bc 100644
--- a/tests/auto/quickcontrols/controls/data/tst_dialogbuttonbox.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_dialogbuttonbox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -28,10 +28,12 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(buttonBox, testCase)
+ function test_defaults() {
+ let control = createTemporaryObject(buttonBox, testCase)
verify(control)
compare(control.count, 0)
verify(control.delegate)
@@ -39,19 +41,19 @@ TestCase {
}
function test_standardButtons() {
- var control = createTemporaryObject(buttonBox, testCase)
+ let control = createTemporaryObject(buttonBox, testCase)
verify(control)
compare(control.count, 0)
control.standardButtons = DialogButtonBox.Ok
compare(control.count, 1)
- var okButton = control.itemAt(0)
+ let okButton = control.itemAt(0)
verify(okButton)
compare(okButton.text.toUpperCase(), "OK")
control.standardButtons = DialogButtonBox.Cancel
compare(control.count, 1)
- var cancelButton = control.itemAt(0)
+ let cancelButton = control.itemAt(0)
verify(cancelButton)
compare(cancelButton.text.toUpperCase(), "CANCEL")
@@ -79,21 +81,21 @@ TestCase {
}
function test_attached() {
- var control = createTemporaryObject(buttonBox, testCase)
+ let control = createTemporaryObject(buttonBox, testCase)
verify(control)
control.standardButtons = DialogButtonBox.Ok
- var okButton = control.itemAt(0)
+ let okButton = control.itemAt(0)
compare(okButton.DialogButtonBox.buttonBox, control)
compare(okButton.DialogButtonBox.buttonRole, DialogButtonBox.AcceptRole)
- var saveButton = button.createObject(control, {text: "Save"})
+ let saveButton = button.createObject(control, {text: "Save"})
compare(saveButton.DialogButtonBox.buttonBox, control)
compare(saveButton.DialogButtonBox.buttonRole, DialogButtonBox.InvalidRole)
saveButton.DialogButtonBox.buttonRole = DialogButtonBox.AcceptRole
compare(saveButton.DialogButtonBox.buttonRole, DialogButtonBox.AcceptRole)
- var closeButton = createTemporaryObject(button, null, {text: "Save"})
+ let closeButton = createTemporaryObject(button, null, {text: "Save"})
compare(closeButton.DialogButtonBox.buttonBox, null)
compare(closeButton.DialogButtonBox.buttonRole, DialogButtonBox.InvalidRole)
closeButton.DialogButtonBox.buttonRole = DialogButtonBox.DestructiveRole
@@ -131,18 +133,18 @@ TestCase {
}
function test_signals(data) {
- var control = createTemporaryObject(buttonBox, testCase)
+ let control = createTemporaryObject(buttonBox, testCase)
verify(control)
control.standardButtons = data.standardButton
compare(control.count, 1)
- var button = control.itemAt(0)
+ let button = control.itemAt(0)
verify(button)
compare(button.DialogButtonBox.buttonRole, data.buttonRole)
- var clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
+ let clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
verify(clickedSpy.valid)
- var roleSpy = signalSpy.createObject(control, {target: control, signalName: data.signalName})
+ let roleSpy = signalSpy.createObject(control, {target: control, signalName: data.signalName})
verify(roleSpy.valid)
button.clicked()
@@ -162,16 +164,16 @@ TestCase {
}
function test_buttonLayout(data) {
- var control = createTemporaryObject(buttonBox, testCase, {buttonLayout: data.buttonLayout, standardButtons: DialogButtonBox.Ok|DialogButtonBox.Cancel})
+ let control = createTemporaryObject(buttonBox, testCase, {buttonLayout: data.buttonLayout, standardButtons: DialogButtonBox.Ok|DialogButtonBox.Cancel})
verify(control)
compare(control.count, 2)
- var button1 = control.itemAt(0)
+ let button1 = control.itemAt(0)
verify(button1)
compare(button1.DialogButtonBox.buttonRole, data.button1Role)
- var button2 = control.itemAt(1)
+ let button2 = control.itemAt(1)
verify(button2)
compare(button2.DialogButtonBox.buttonRole, data.button2Role)
}
@@ -185,16 +187,16 @@ TestCase {
// QTBUG-59719
function test_implicitSize(data) {
- var control = createTemporaryObject(buttonBox, testCase, {standardButtons: data.standardButtons})
+ let control = createTemporaryObject(buttonBox, testCase, {standardButtons: data.standardButtons})
verify(control)
- var listView = control.contentItem
+ let listView = control.contentItem
verify(listView && listView.hasOwnProperty("contentWidth"))
waitForRendering(listView)
- var implicitContentWidth = control.leftPadding + control.rightPadding
- for (var i = 0; i < listView.contentItem.children.length; ++i) {
- var button = listView.contentItem.children[i]
+ let implicitContentWidth = control.leftPadding + control.rightPadding
+ for (let i = 0; i < listView.contentItem.children.length; ++i) {
+ let button = listView.contentItem.children[i]
if (!button.hasOwnProperty("text"))
continue
implicitContentWidth += button.implicitWidth
@@ -217,14 +219,14 @@ TestCase {
}
function test_buttonSize() {
- var control = createTemporaryObject(okCancelBox, testCase)
+ let control = createTemporaryObject(okCancelBox, testCase)
verify(control)
- var okButton = control.itemAt(0)
+ let okButton = control.itemAt(0)
verify(okButton)
verify(okButton.width > 0)
- var cancelButton = control.itemAt(1)
+ let cancelButton = control.itemAt(1)
verify(cancelButton)
verify(cancelButton.width > 0)
@@ -232,14 +234,14 @@ TestCase {
}
function test_oneButtonInFixedWidthBox() {
- var control = createTemporaryObject(buttonBox, testCase,
+ let control = createTemporaryObject(buttonBox, testCase,
{ width: 400, standardButtons: Dialog.Close })
verify(control)
- var listView = control.contentItem
+ let listView = control.contentItem
waitForRendering(listView)
- var button = control.itemAt(0)
+ let button = control.itemAt(0)
verify(button)
// The button should never go outside of the box.
@@ -280,14 +282,14 @@ TestCase {
// QTBUG-73860
function test_oneButtonAlignedRightInImplicitWidthBox() {
- var dialog = createTemporaryObject(dialogComponent, testCase)
+ let dialog = createTemporaryObject(dialogComponent, testCase)
verify(dialog)
- var box = dialog.footer
- var listView = box.contentItem
+ let box = dialog.footer
+ let listView = box.contentItem
waitForRendering(listView)
- var button = box.itemAt(0)
+ let button = box.itemAt(0)
verify(button)
// The button should never go outside of the box.
@@ -353,13 +355,13 @@ TestCase {
// QTBUG-72886
function test_changeCustomButtonText(data) {
- var control = createTemporaryObject(data.component, testCase, {})
+ let control = createTemporaryObject(data.component, testCase, {})
verify(control)
- var listView = control.contentItem
+ let listView = control.contentItem
waitForRendering(listView)
- var button = control.okButton
+ let button = control.okButton
verify(button)
button.text = "some longer text";
@@ -519,9 +521,9 @@ TestCase {
}
function test_contentItemDeletionOrder() {
- var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ let control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
verify(control1)
- var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ let control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
verify(control2)
}
@@ -560,9 +562,9 @@ TestCase {
}
function test_backgroundDeletionOrder() {
- var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ let control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
verify(control1)
- var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ let control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
verify(control2)
}
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_frame.qml b/tests/auto/quickcontrols/controls/data/tst_frame.qml
index bef46f7650..141940edc3 100644
--- a/tests/auto/quickcontrols/controls/data/tst_frame.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_frame.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -52,10 +52,12 @@ TestCase {
}
}
- function test_empty() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(frame, testCase)
+ function test_empty() {
+ let control = createTemporaryObject(frame, testCase)
verify(control)
verify(control.contentItem)
@@ -66,7 +68,7 @@ TestCase {
}
function test_oneChild() {
- var control = createTemporaryObject(oneChildFrame, testCase)
+ let control = createTemporaryObject(oneChildFrame, testCase)
verify(control)
compare(control.contentWidth, 100)
@@ -78,7 +80,7 @@ TestCase {
}
function test_twoChildren() {
- var control = createTemporaryObject(twoChildrenFrame, testCase)
+ let control = createTemporaryObject(twoChildrenFrame, testCase)
verify(control)
compare(control.contentWidth, 0)
@@ -90,7 +92,7 @@ TestCase {
}
function test_contentItem() {
- var control = createTemporaryObject(contentFrame, testCase)
+ let control = createTemporaryObject(contentFrame, testCase)
verify(control)
compare(control.contentWidth, 100)
diff --git a/tests/auto/quickcontrols/controls/data/tst_groupbox.qml b/tests/auto/quickcontrols/controls/data/tst_groupbox.qml
index b5867dd956..501870e1bc 100644
--- a/tests/auto/quickcontrols/controls/data/tst_groupbox.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_groupbox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -52,10 +52,12 @@ TestCase {
}
}
- function test_empty() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(groupBox, testCase)
+ function test_empty() {
+ let control = createTemporaryObject(groupBox, testCase)
verify(control)
verify(control.contentItem)
@@ -66,7 +68,7 @@ TestCase {
}
function test_oneChild() {
- var control = createTemporaryObject(oneChildBox, testCase)
+ let control = createTemporaryObject(oneChildBox, testCase)
verify(control)
compare(control.contentWidth, 100)
@@ -78,7 +80,7 @@ TestCase {
}
function test_twoChildren() {
- var control = createTemporaryObject(twoChildrenBox, testCase)
+ let control = createTemporaryObject(twoChildrenBox, testCase)
verify(control)
compare(control.contentWidth, 0)
@@ -90,7 +92,7 @@ TestCase {
}
function test_contentItem() {
- var control = createTemporaryObject(contentBox, testCase)
+ let control = createTemporaryObject(contentBox, testCase)
verify(control)
compare(control.contentWidth, 100)
diff --git a/tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml b/tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml
index 9d4847b4b1..db78299c25 100644
--- a/tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -18,21 +18,23 @@ TestCase {
ItemDelegate { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(itemDelegate, testCase)
verify(control)
}
function test_baseline() {
- var control = createTemporaryObject(itemDelegate, testCase)
+ let control = createTemporaryObject(itemDelegate, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
function test_highlighted() {
- var control = createTemporaryObject(itemDelegate, testCase)
+ let control = createTemporaryObject(itemDelegate, testCase)
verify(control)
verify(!control.highlighted)
@@ -41,11 +43,11 @@ TestCase {
}
function test_spacing() {
- var control = createTemporaryObject(itemDelegate, testCase, { text: "Some long, long, long text" })
+ let control = createTemporaryObject(itemDelegate, testCase, { text: "Some long, long, long text" })
verify(control)
verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth)
- var textLabel = findChild(control.contentItem, "label")
+ let textLabel = findChild(control.contentItem, "label")
verify(textLabel)
// The implicitWidth of the IconLabel that all buttons use as their contentItem
@@ -74,7 +76,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(itemDelegate, testCase, {
+ let control = createTemporaryObject(itemDelegate, testCase, {
text: "ItemDelegate",
display: data.display,
width: 400,
@@ -84,8 +86,8 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
switch (control.display) {
case ItemDelegate.IconOnly:
diff --git a/tests/auto/quickcontrols/controls/data/tst_label.qml b/tests/auto/quickcontrols/controls/data/tst_label.qml
index 853d5ba686..2477ce338d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_label.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_label.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -35,10 +35,12 @@ TestCase {
SignalSpy { }
}
- function test_creation() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(label, testCase)
+ function test_creation() {
+ let control = createTemporaryObject(label, testCase)
verify(control)
}
@@ -46,7 +48,7 @@ TestCase {
return [
{tag: "bold", value: true},
{tag: "capitalization", value: Font.Capitalize},
- {tag: "family", value: "Courier"},
+ {tag: "family", value: "Tahoma"},
{tag: "italic", value: true},
{tag: "strikeout", value: true},
{tag: "underline", value: true},
@@ -56,19 +58,19 @@ TestCase {
}
function test_font_explicit_attributes(data) {
- var control = createTemporaryObject(label, testCase)
+ let control = createTemporaryObject(label, testCase)
verify(control)
- var child = label.createObject(control)
+ let child = label.createObject(control)
verify(child)
- var controlSpy = signalSpy.createObject(control, {target: control, signalName: "fontChanged"})
+ let controlSpy = signalSpy.createObject(control, {target: control, signalName: "fontChanged"})
verify(controlSpy.valid)
- var childSpy = signalSpy.createObject(child, {target: child, signalName: "fontChanged"})
+ let childSpy = signalSpy.createObject(child, {target: child, signalName: "fontChanged"})
verify(childSpy.valid)
- var defaultValue = control.font[data.tag]
+ let defaultValue = control.font[data.tag]
child.font[data.tag] = defaultValue
compare(child.font[data.tag], defaultValue)
@@ -84,7 +86,7 @@ TestCase {
}
function test_background() {
- var control = createTemporaryObject(backgroundLabel, testCase, {text: "Label"})
+ let control = createTemporaryObject(backgroundLabel, testCase, {text: "Label"})
verify(control)
compare(control.background.width, control.width)
@@ -102,25 +104,25 @@ TestCase {
}
function test_inset() {
- var control = createTemporaryObject(label, testCase, {background: rectangle.createObject(control)})
+ let control = createTemporaryObject(label, testCase, {background: rectangle.createObject(testCase)})
verify(control)
- var topInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topInsetChanged"})
+ let topInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topInsetChanged"})
verify(topInsetSpy.valid)
- var leftInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftInsetChanged"})
+ let leftInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftInsetChanged"})
verify(leftInsetSpy.valid)
- var rightInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightInsetChanged"})
+ let rightInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightInsetChanged"})
verify(rightInsetSpy.valid)
- var bottomInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomInsetChanged"})
+ let bottomInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomInsetChanged"})
verify(bottomInsetSpy.valid)
- var topInsetChanges = 0
- var leftInsetChanges = 0
- var rightInsetChanges = 0
- var bottomInsetChanges = 0
+ let topInsetChanges = 0
+ let leftInsetChanges = 0
+ let rightInsetChanges = 0
+ let bottomInsetChanges = 0
compare(control.topInset, 0)
compare(control.leftInset, 0)
diff --git a/tests/auto/quickcontrols/controls/data/tst_menuitem.qml b/tests/auto/quickcontrols/controls/data/tst_menuitem.qml
index d1ed076705..35335ec88d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_menuitem.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_menuitem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -23,21 +23,23 @@ TestCase {
Menu { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(menuItem, testCase)
verify(control)
}
function test_baseline() {
- var control = createTemporaryObject(menuItem, testCase)
+ let control = createTemporaryObject(menuItem, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
function test_checkable() {
- var control = createTemporaryObject(menuItem, testCase)
+ let control = createTemporaryObject(menuItem, testCase)
verify(control)
verify(control.hasOwnProperty("checkable"))
verify(!control.checkable)
@@ -54,7 +56,7 @@ TestCase {
}
function test_highlighted() {
- var control = createTemporaryObject(menuItem, testCase)
+ let control = createTemporaryObject(menuItem, testCase)
verify(control)
verify(!control.highlighted)
@@ -76,7 +78,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(menuItem, testCase, {
+ let control = createTemporaryObject(menuItem, testCase, {
text: "MenuItem",
display: data.display,
"icon.source": "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png",
@@ -85,9 +87,9 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var padding = data.mirrored ? control.contentItem.rightPadding : control.contentItem.leftPadding
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let padding = data.mirrored ? control.contentItem.rightPadding : control.contentItem.leftPadding
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
switch (control.display) {
case MenuItem.IconOnly:
@@ -123,14 +125,14 @@ TestCase {
}
function test_menu() {
- var control = createTemporaryObject(menu, testCase)
+ let control = createTemporaryObject(menu, testCase)
verify(control)
- var item1 = createTemporaryObject(menuItem, testCase)
+ let item1 = createTemporaryObject(menuItem, testCase)
verify(item1)
compare(item1.menu, null)
- var item2 = createTemporaryObject(menuItem, testCase)
+ let item2 = createTemporaryObject(menuItem, testCase)
verify(item2)
compare(item2.menu, null)
diff --git a/tests/auto/quickcontrols/controls/data/tst_monthgrid.qml b/tests/auto/quickcontrols/controls/data/tst_monthgrid.qml
index 5b1081675e..d0d66442c0 100644
--- a/tests/auto/quickcontrols/controls/data/tst_monthgrid.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_monthgrid.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -37,15 +37,17 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(defaultGrid, testCase)
verify(control)
}
function test_locale() {
- var control = delegateGrid.createObject(testCase, {month: 0, year: 2013})
+ let control = delegateGrid.createObject(testCase, {month: 0, year: 2013})
compare(control.contentItem.children.length, 6 * 7 + 1)
@@ -58,15 +60,15 @@ TestCase {
compare(control.locale.name, "en_GB")
// M T W T F S S
- var en_GB = ["2012-12-31", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05", "2013-01-06",
+ let en_GB = ["2012-12-31", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05", "2013-01-06",
"2013-01-07", "2013-01-08", "2013-01-09", "2013-01-10", "2013-01-11", "2013-01-12", "2013-01-13",
"2013-01-14", "2013-01-15", "2013-01-16", "2013-01-17", "2013-01-18", "2013-01-19", "2013-01-20",
"2013-01-21", "2013-01-22", "2013-01-23", "2013-01-24", "2013-01-25", "2013-01-26", "2013-01-27",
"2013-01-28", "2013-01-29", "2013-01-30", "2013-01-31", "2013-02-01", "2013-02-02", "2013-02-03",
"2013-02-04", "2013-02-05", "2013-02-06", "2013-02-07", "2013-02-08", "2013-02-09", "2013-02-10"]
- for (var i = 0; i < 42; ++i) {
- var cellDate = new Date(en_GB[i])
+ for (let i = 0; i < 42; ++i) {
+ let cellDate = new Date(en_GB[i])
compare(control.contentItem.children[i].date.getFullYear(), cellDate.getUTCFullYear())
compare(control.contentItem.children[i].date.getMonth(), cellDate.getUTCMonth())
compare(control.contentItem.children[i].date.getDate(), cellDate.getUTCDate())
@@ -81,15 +83,15 @@ TestCase {
compare(control.locale.name, "en_US")
// S M T W T F S
- var en_US = ["2012-12-30", "2012-12-31", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05",
+ let en_US = ["2012-12-30", "2012-12-31", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05",
"2013-01-06", "2013-01-07", "2013-01-08", "2013-01-09", "2013-01-10", "2013-01-11", "2013-01-12",
"2013-01-13", "2013-01-14", "2013-01-15", "2013-01-16", "2013-01-17", "2013-01-18", "2013-01-19",
"2013-01-20", "2013-01-21", "2013-01-22", "2013-01-23", "2013-01-24", "2013-01-25", "2013-01-26",
"2013-01-27", "2013-01-28", "2013-01-29", "2013-01-30", "2013-01-31", "2013-02-01", "2013-02-02",
"2013-02-03", "2013-02-04", "2013-02-05", "2013-02-06", "2013-02-07", "2013-02-08", "2013-02-09"]
- for (var j = 0; j < 42; ++j) {
- cellDate = new Date(en_US[j])
+ for (let j = 0; j < 42; ++j) {
+ let cellDate = new Date(en_US[j])
compare(control.contentItem.children[j].date.getFullYear(), cellDate.getUTCFullYear())
compare(control.contentItem.children[j].date.getMonth(), cellDate.getUTCMonth())
compare(control.contentItem.children[j].date.getDate(), cellDate.getUTCDate())
@@ -103,7 +105,7 @@ TestCase {
}
function test_range() {
- var control = defaultGrid.createObject(testCase)
+ let control = defaultGrid.createObject(testCase)
control.month = 0
compare(control.month, 0)
@@ -138,7 +140,7 @@ TestCase {
}
function test_bce() {
- var control = defaultGrid.createObject(testCase)
+ let control = defaultGrid.createObject(testCase)
compare(control.contentItem.children.length, 6 * 7 + 1)
@@ -153,14 +155,14 @@ TestCase {
compare(control.year, -1)
// M T W T F S S
- var jan1bce = [27, 28, 29, 30, 31, 1, 2,
+ let jan1bce = [27, 28, 29, 30, 31, 1, 2,
3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30,
31, 1, 2, 3, 4, 5, 6]
- for (var i = 0; i < 42; ++i)
+ for (let i = 0; i < 42; ++i)
compare(control.contentItem.children[i].text, jan1bce[i].toString())
// February 1 BCE
@@ -170,21 +172,21 @@ TestCase {
compare(control.year, -1)
// M T W T F S S
- var feb1bce = [31, 1, 2, 3, 4, 5, 6,
+ let feb1bce = [31, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27,
28, 29, 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11, 12]
- for (var j = 0; j < 42; ++j)
+ for (let j = 0; j < 42; ++j)
compare(control.contentItem.children[j].text, feb1bce[j].toString())
control.destroy()
}
function test_font() {
- var control = defaultGrid.createObject(testCase)
+ let control = defaultGrid.createObject(testCase)
verify(control.contentItem.children[0])
@@ -202,24 +204,24 @@ TestCase {
}
function test_clicked(data) {
- var control = createTemporaryObject(defaultGrid, testCase)
+ let control = createTemporaryObject(defaultGrid, testCase)
verify(control)
compare(control.contentItem.children.length, 6 * 7 + 1)
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"})
verify(pressedSpy.valid)
- var releasedSpy = signalSpy.createObject(control, {target: control, signalName: "released"})
+ let releasedSpy = signalSpy.createObject(control, {target: control, signalName: "released"})
verify(releasedSpy.valid)
- var clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
+ let clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
verify(clickedSpy.valid)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
- for (var i = 0; i < 42; ++i) {
- var cell = control.contentItem.children[i]
+ for (let i = 0; i < 42; ++i) {
+ let cell = control.contentItem.children[i]
verify(cell)
if (data.touch)
diff --git a/tests/auto/quickcontrols/controls/data/tst_page.qml b/tests/auto/quickcontrols/controls/data/tst_page.qml
index ca816d963b..2396b30dff 100644
--- a/tests/auto/quickcontrols/controls/data/tst_page.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_page.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -69,10 +69,12 @@ TestCase {
ToolBar { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(page, testCase)
+ function test_defaults() {
+ let control = createTemporaryObject(page, testCase)
verify(control)
verify(control.contentItem)
@@ -81,9 +83,7 @@ TestCase {
}
function test_empty() {
- failOnWarning(/.?/)
-
- var control = createTemporaryObject(page, testCase)
+ let control = createTemporaryObject(page, testCase)
verify(control)
verify(control.contentItem)
@@ -94,7 +94,7 @@ TestCase {
}
function test_oneChild() {
- var control = createTemporaryObject(oneChildPage, testCase)
+ let control = createTemporaryObject(oneChildPage, testCase)
verify(control)
compare(control.contentWidth, 100)
@@ -106,7 +106,7 @@ TestCase {
}
function test_twoChildren() {
- var control = createTemporaryObject(twoChildrenPage, testCase)
+ let control = createTemporaryObject(twoChildrenPage, testCase)
verify(control)
compare(control.contentWidth, 0)
@@ -120,7 +120,7 @@ TestCase {
}
function test_contentItem() {
- var control = createTemporaryObject(contentPage, testCase)
+ let control = createTemporaryObject(contentPage, testCase)
verify(control)
compare(control.contentWidth, 100)
@@ -132,7 +132,7 @@ TestCase {
}
function test_layout() {
- var control = createTemporaryObject(page, testCase, {width: 100, height: 100})
+ let control = createTemporaryObject(page, testCase, {width: 100, height: 100})
verify(control)
compare(control.width, 100)
@@ -220,7 +220,7 @@ TestCase {
}
function test_spacing(data) {
- var control = createTemporaryObject(page, testCase, {spacing: 20, width: 100, height: 100})
+ let control = createTemporaryObject(page, testCase, {spacing: 20, width: 100, height: 100})
verify(control)
control.contentItem.visible = data.content
@@ -246,7 +246,7 @@ TestCase {
}
function test_headerFooter() {
- var control = createTemporaryObject(headerFooterPage, testCase, {width: 100, height: 100})
+ let control = createTemporaryObject(headerFooterPage, testCase, {width: 100, height: 100})
verify(control)
compare(control.width, 100)
@@ -268,5 +268,18 @@ TestCase {
compare(control.contentItem.y, control.header.height)
compare(control.contentItem.width, control.width)
compare(control.contentItem.height, control.height - control.header.height - control.footer.height)
+
+ // swap places and make sure geometry is updated correctly
+ const oldHeader = control.header
+ const oldFooter = control.footer
+ // reset both first, so one item does not end up in two places at once
+ control.header = null
+ control.footer = null
+ control.header = oldFooter
+ control.footer = oldHeader
+ verify(control.header.visible)
+ verify(control.footer.visible)
+ compare(control.header.y, 0)
+ compare(control.footer.y, control.height - control.footer.height)
}
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_pageindicator.qml b/tests/auto/quickcontrols/controls/data/tst_pageindicator.qml
index 837972cdc2..391eb91248 100644
--- a/tests/auto/quickcontrols/controls/data/tst_pageindicator.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_pageindicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -15,23 +15,30 @@ TestCase {
Component {
id: pageIndicator
- PageIndicator { }
+ PageIndicator {
+ objectName: "pageIndicator"
+ contentItem.objectName: "pageIndicatorContentItem"
+ }
}
Component {
id: mouseArea
- MouseArea { }
+ MouseArea {
+ objectName: "mouseArea"
+ }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(pageIndicator, testCase)
verify(control)
}
function test_count() {
- var control = createTemporaryObject(pageIndicator, testCase)
+ let control = createTemporaryObject(pageIndicator, testCase)
verify(control)
compare(control.count, 0)
@@ -40,7 +47,7 @@ TestCase {
}
function test_currentIndex() {
- var control = createTemporaryObject(pageIndicator, testCase)
+ let control = createTemporaryObject(pageIndicator, testCase)
verify(control)
compare(control.currentIndex, 0)
@@ -56,13 +63,13 @@ TestCase {
}
function test_interactive(data) {
- var control = createTemporaryObject(pageIndicator, testCase, {count: 5, spacing: 10, topPadding: 10, leftPadding: 10, rightPadding: 10, bottomPadding: 10})
+ let control = createTemporaryObject(pageIndicator, testCase, {count: 5, spacing: 10, topPadding: 10, leftPadding: 10, rightPadding: 10, bottomPadding: 10})
verify(control)
verify(!control.interactive)
compare(control.currentIndex, 0)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
if (data.touch)
touch.press(0, control).commit().release(0, control).commit()
@@ -80,10 +87,10 @@ TestCase {
compare(control.currentIndex, 2)
// test also clicking outside delegates => the nearest should be selected
- for (var i = 0; i < control.count; ++i) {
- var child = control.contentItem.children[i]
+ for (let i = 0; i < control.count; ++i) {
+ let child = control.contentItem.children[i]
- var points = [
+ let points = [
Qt.point(child.width / 2, -2), // top
Qt.point(-2, child.height / 2), // left
Qt.point(child.width + 2, child.height / 2), // right
@@ -95,12 +102,12 @@ TestCase {
Qt.point(child.width + 2, child.height + 2), // bottom-right
]
- for (var j = 0; j < points.length; ++j) {
+ for (let j = 0; j < points.length; ++j) {
control.currentIndex = -1
compare(control.currentIndex, -1)
- var point = points[j]
- var pos = control.mapFromItem(child, x, y)
+ let point = points[j]
+ let pos = control.mapFromItem(child, x, y)
if (data.touch)
touch.press(0, control, pos.x, pos.y).commit().release(0, control, pos.x, pos.y).commit()
else
@@ -119,10 +126,10 @@ TestCase {
// QTBUG-61785
function test_mouseArea(data) {
- var ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
+ let ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
verify(ma)
- var control = pageIndicator.createObject(ma, {count: 5, interactive: data.interactive, width: testCase.width, height: testCase.height})
+ let control = pageIndicator.createObject(ma, {count: 5, interactive: data.interactive, width: testCase.width, height: testCase.height})
verify(control)
compare(control.interactive, data.interactive)
@@ -133,7 +140,7 @@ TestCase {
mouseRelease(control)
verify(!ma.pressed)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control).commit()
compare(ma.pressed, !data.interactive)
diff --git a/tests/auto/quickcontrols/controls/data/tst_pane.qml b/tests/auto/quickcontrols/controls/data/tst_pane.qml
index f823031c79..ab042edec2 100644
--- a/tests/auto/quickcontrols/controls/data/tst_pane.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_pane.qml
@@ -1,9 +1,10 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
import QtQuick.Controls
+import QtQuick.Layouts
TestCase {
id: testCase
@@ -18,57 +19,22 @@ TestCase {
Pane { }
}
- Component {
- id: oneChildPane
- Pane {
- Item {
- implicitWidth: 100
- implicitHeight: 30
- }
- }
- }
-
- Component {
- id: twoChildrenPane
- Pane {
- Item {
- implicitWidth: 100
- implicitHeight: 30
- }
- Item {
- implicitWidth: 200
- implicitHeight: 60
- }
- }
+ function init() {
+ failOnWarning(/.?/)
}
- Component {
- id: contentPane
- Pane {
- contentItem: Item {
- implicitWidth: 100
- implicitHeight: 30
- }
- }
- }
+ function test_implicitContentItem() {
+ let control = createTemporaryObject(pane, testCase, {width: 100, height: 100})
+ verify(control)
- Component {
- id: pressPane
- MouseArea {
- width: 200
- height: 200
- property int pressCount
- onPressed: ++pressCount
- Pane {
- anchors.fill: parent
- }
- }
+ compare(control.width, 100)
+ compare(control.height, 100)
+ compare(control.contentItem.width, control.availableWidth)
+ compare(control.contentItem.height, control.availableHeight)
}
function test_empty() {
- failOnWarning(/.?/)
-
- var control = createTemporaryObject(pane, testCase)
+ let control = createTemporaryObject(pane, testCase)
verify(control)
verify(control.contentItem)
@@ -78,8 +44,18 @@ TestCase {
compare(control.implicitContentHeight, 0)
}
+ Component {
+ id: oneChildPane
+ Pane {
+ Item {
+ implicitWidth: 100
+ implicitHeight: 30
+ }
+ }
+ }
+
function test_oneChild() {
- var control = createTemporaryObject(oneChildPane, testCase)
+ let control = createTemporaryObject(oneChildPane, testCase)
verify(control)
compare(control.contentWidth, 100)
@@ -101,8 +77,22 @@ TestCase {
verify(control.implicitHeight > 40)
}
+ Component {
+ id: twoChildrenPane
+ Pane {
+ Item {
+ implicitWidth: 100
+ implicitHeight: 30
+ }
+ Item {
+ implicitWidth: 200
+ implicitHeight: 60
+ }
+ }
+ }
+
function test_twoChildren() {
- var control = createTemporaryObject(twoChildrenPane, testCase)
+ let control = createTemporaryObject(twoChildrenPane, testCase)
verify(control)
compare(control.contentWidth, 0)
@@ -113,8 +103,18 @@ TestCase {
verify(control.implicitHeight > 0)
}
+ Component {
+ id: contentPane
+ Pane {
+ contentItem: Item {
+ implicitWidth: 100
+ implicitHeight: 30
+ }
+ }
+ }
+
function test_contentItem() {
- var control = createTemporaryObject(contentPane, testCase)
+ let control = createTemporaryObject(contentPane, testCase)
verify(control)
compare(control.contentWidth, 100)
@@ -125,18 +125,47 @@ TestCase {
verify(control.implicitHeight > 30)
}
- function test_implicitContentItem() {
- var control = createTemporaryObject(pane, testCase, {width: 100, height: 100})
+ Component {
+ id: contentItemPane
+ Pane {
+ property string description: ""
+ contentItem: ColumnLayout {
+ Label {
+ Layout.maximumWidth: 100
+ text: description
+ elide: Label.ElideRight
+ }
+ }
+ Component.onCompleted: {
+ description = "Binding loop issue ".repeat(100)
+ }
+ }
+ }
+
+ function test_paneBindingLoop() {
+ let control = createTemporaryObject(contentItemPane, testCase)
verify(control)
+ // Wait for content item to be polished
+ waitForPolish(control.contentItem)
- compare(control.width, 100)
- compare(control.height, 100)
- compare(control.contentItem.width, control.availableWidth)
- compare(control.contentItem.height, control.availableHeight)
+ compare(control.contentWidth, 100)
+ }
+
+ Component {
+ id: pressPane
+ MouseArea {
+ width: 200
+ height: 200
+ property int pressCount
+ onPressed: ++pressCount
+ Pane {
+ anchors.fill: parent
+ }
+ }
}
function test_press() {
- var control = createTemporaryObject(pressPane, testCase)
+ let control = createTemporaryObject(pressPane, testCase)
verify(control)
compare(control.pressCount, 0)
diff --git a/tests/auto/quickcontrols/controls/data/tst_popup.qml b/tests/auto/quickcontrols/controls/data/tst_popup.qml
index 23879d817a..2428192961 100644
--- a/tests/auto/quickcontrols/controls/data/tst_popup.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_popup.qml
@@ -1,9 +1,10 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
import QtQuick.Controls
+import QtQuick.Layouts
import QtQuick.Templates as T
import QtQuick.NativeStyle as NativeStyle
import Qt.test.controls
@@ -42,37 +43,39 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(popupControl, testCase)
verify(control)
}
function test_padding() {
- var control = createTemporaryObject(popupTemplate, testCase)
+ let control = createTemporaryObject(popupTemplate, testCase)
verify(control)
- var paddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "paddingChanged"})
+ let paddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "paddingChanged"})
verify(paddingSpy.valid)
- var topPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topPaddingChanged"})
+ let topPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topPaddingChanged"})
verify(topPaddingSpy.valid)
- var leftPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftPaddingChanged"})
+ let leftPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftPaddingChanged"})
verify(leftPaddingSpy.valid)
- var rightPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightPaddingChanged"})
+ let rightPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightPaddingChanged"})
verify(rightPaddingSpy.valid)
- var bottomPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomPaddingChanged"})
+ let bottomPaddingSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomPaddingChanged"})
verify(bottomPaddingSpy.valid)
- var paddingChanges = 0
- var topPaddingChanges = 0
- var leftPaddingChanges = 0
- var rightPaddingChanges = 0
- var bottomPaddingChanges = 0
+ let paddingChanges = 0
+ let topPaddingChanges = 0
+ let leftPaddingChanges = 0
+ let rightPaddingChanges = 0
+ let bottomPaddingChanges = 0
compare(control.padding, 0)
compare(control.topPadding, 0)
@@ -159,17 +162,17 @@ TestCase {
}
function test_availableSize() {
- var control = createTemporaryObject(popupTemplate, testCase)
+ let control = createTemporaryObject(popupTemplate, testCase)
verify(control)
- var availableWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "availableWidthChanged"})
+ let availableWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "availableWidthChanged"})
verify(availableWidthSpy.valid)
- var availableHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "availableHeightChanged"})
+ let availableHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "availableHeightChanged"})
verify(availableHeightSpy.valid)
- var availableWidthChanges = 0
- var availableHeightChanges = 0
+ let availableWidthChanges = 0
+ let availableHeightChanges = 0
control.width = 100
compare(control.availableWidth, 100)
@@ -229,14 +232,14 @@ TestCase {
}
function test_position() {
- var control = createTemporaryObject(popupControl, testCase, {visible: true, leftMargin: 10, topMargin: 20, width: 100, height: 100})
+ let control = createTemporaryObject(popupControl, testCase, {visible: true, leftMargin: 10, topMargin: 20, width: 100, height: 100})
verify(control)
verify(control.visible)
- var xSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "xChanged"})
+ let xSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "xChanged"})
verify(xSpy.valid)
- var ySpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "yChanged"})
+ let ySpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "yChanged"})
verify(ySpy.valid)
// moving outside margins does not trigger change notifiers
@@ -285,7 +288,7 @@ TestCase {
}
function test_resetSize() {
- var control = createTemporaryObject(popupControl, testCase, {visible: true, margins: 0})
+ let control = createTemporaryObject(popupControl, testCase, {visible: true, margins: 0})
verify(control)
control.scale = 1.0
@@ -302,7 +305,7 @@ TestCase {
}
function test_negativeMargins() {
- var control = createTemporaryObject(popupControl, testCase, {implicitWidth: testCase.width, implicitHeight: testCase.height})
+ let control = createTemporaryObject(popupControl, testCase, {implicitWidth: testCase.width, implicitHeight: testCase.height})
verify(control)
control.open()
@@ -319,12 +322,12 @@ TestCase {
control.x = -10
control.y = -10
- compare(control.x, 0)
- compare(control.y, 0)
+ compare(control.x, -10)
+ compare(control.y, -10)
}
function test_margins() {
- var control = createTemporaryObject(popupTemplate, testCase, {width: 100, height: 100})
+ let control = createTemporaryObject(popupTemplate, testCase, {width: 100, height: 100})
verify(control)
control.open()
@@ -410,7 +413,7 @@ TestCase {
}
function test_background() {
- var control = createTemporaryObject(popupTemplate, testCase)
+ let control = createTemporaryObject(popupTemplate, testCase)
verify(control)
control.background = rect.createObject(testCase)
@@ -455,8 +458,8 @@ TestCase {
}
function getChild(control, objname, idx) {
- var index = idx
- for (var i = index+1; i < control.children.length; i++)
+ let index = idx
+ for (let i = index+1; i < control.children.length; i++)
{
if (control.children[i].objectName === objname) {
index = i
@@ -525,15 +528,15 @@ TestCase {
}
function test_font() { // QTBUG_50984, QTBUG-51696
- var window = createTemporaryObject(component, testCase)
+ let window = createTemporaryObject(component, testCase)
verify(window)
compare(window.font.pixelSize, 40)
compare(window.pane.font.pixelSize, 30)
compare(window.pane.button.font.pixelSize, 20)
compare(window.popup.font.pixelSize, 40)
- var idx1 = getChild(window.popup.listview.contentItem, "delegate", -1)
- var idx2 = getChild(window.popup.listview.contentItem, "delegate", idx1)
+ let idx1 = getChild(window.popup.listview.contentItem, "delegate", -1)
+ let idx2 = getChild(window.popup.listview.contentItem, "delegate", idx1)
window.popup.listview.contentItem.children[idx1].fontspy.clear()
window.popup.listview.contentItem.children[idx2].fontspy.clear()
window.popup.button.fontspy.clear()
@@ -649,7 +652,7 @@ TestCase {
function test_locale() { // QTBUG_50984
// test looking up natural locale from ancestors
- var control = createTemporaryObject(localeComponent, applicationWindow.contentItem)
+ let control = createTemporaryObject(localeComponent, applicationWindow.contentItem)
verify(control)
compare(control.locale.name, "en_US")
@@ -732,10 +735,10 @@ TestCase {
function test_locale_changes() { // QTBUG_50984
// test default locale and locale inheritance
- var control = createTemporaryObject(localeChangeComponent, applicationWindow.contentItem)
+ let control = createTemporaryObject(localeChangeComponent, applicationWindow.contentItem)
verify(control)
- var defaultLocale = Qt.locale()
+ let defaultLocale = Qt.locale()
compare(control.ApplicationWindow.window.locale.name, defaultLocale.name)
compare(control.locale.name, defaultLocale.name)
compare(control.button.locale.name, defaultLocale.name)
@@ -901,10 +904,10 @@ TestCase {
}
function test_size() {
- var control = createTemporaryObject(popupControl, testCase)
+ let control = createTemporaryObject(popupControl, testCase)
verify(control)
- var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
+ let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
verify(openedSpy.valid)
control.open()
@@ -962,7 +965,7 @@ TestCase {
}
function test_visible() {
- var control = createTemporaryObject(popupTemplate, testCase, {visible: true})
+ let control = createTemporaryObject(popupTemplate, testCase, {visible: true})
verify(control)
// QTBUG-51989
@@ -1020,7 +1023,7 @@ TestCase {
}
function indexOf(array, item) {
- for (var idx = 0; idx < array.length; ++idx) {
+ for (let idx = 0; idx < array.length; ++idx) {
if (item === array[idx])
return idx;
}
@@ -1028,13 +1031,13 @@ TestCase {
}
function findOverlay(window, popup) {
- var item = popup.contentItem.parent
- var idx = indexOf(window.Overlay.overlay.children, item)
+ let item = popup.contentItem.parent
+ let idx = indexOf(window.Overlay.overlay.children, item)
return window.Overlay.overlay.children[idx - 1]
}
function test_overlay() {
- var window = createTemporaryObject(overlayTest, testCase)
+ let window = createTemporaryObject(overlayTest, testCase)
verify(window)
window.requestActivate()
@@ -1042,7 +1045,7 @@ TestCase {
compare(window.Overlay.overlay.children.length, 0)
- var firstOverlay = findOverlay(window, window.firstDrawer)
+ let firstOverlay = findOverlay(window, window.firstDrawer)
verify(!firstOverlay)
window.firstDrawer.open()
compare(window.Overlay.overlay.children.length, 2) // 1 drawer + 1 overlay
@@ -1053,7 +1056,7 @@ TestCase {
indexOf(window.Overlay.overlay.children, window.firstDrawer.contentItem.parent) - 1)
tryCompare(firstOverlay, "opacity", 1.0)
- var secondOverlay = findOverlay(window, window.secondDrawer)
+ let secondOverlay = findOverlay(window, window.secondDrawer)
verify(!secondOverlay)
window.secondDrawer.open()
compare(window.Overlay.overlay.children.length, 4) // 2 drawers + 2 overlays
@@ -1076,7 +1079,7 @@ TestCase {
verify(!secondOverlay)
compare(window.Overlay.overlay.children.length, 0)
- var modalOverlay = findOverlay(window, window.modalPopup)
+ let modalOverlay = findOverlay(window, window.modalPopup)
verify(!modalOverlay)
window.modalPopup.open()
modalOverlay = findOverlay(window, window.modalPopup)
@@ -1086,7 +1089,7 @@ TestCase {
tryCompare(modalOverlay, "opacity", 1.0)
compare(window.Overlay.overlay.children.length, 2) // 1 popup + 1 overlay
- var modelessOverlay = findOverlay(window, window.modelessPopup)
+ let modelessOverlay = findOverlay(window, window.modelessPopup)
verify(!modelessOverlay)
window.modelessPopup.open()
modelessOverlay = findOverlay(window, window.modelessPopup)
@@ -1135,10 +1138,10 @@ TestCase {
}
function test_attached_applicationwindow() {
- var control = createTemporaryObject(popupControl, applicationWindow.contentItem)
+ let control = createTemporaryObject(popupControl, applicationWindow.contentItem)
verify(control)
- var child = rect.createObject(control.contentItem)
+ let child = rect.createObject(control.contentItem)
compare(control.ApplicationWindow.window, applicationWindow)
compare(control.contentItem.ApplicationWindow.window, applicationWindow)
@@ -1159,14 +1162,14 @@ TestCase {
}
function test_openedClosed() {
- var control = createTemporaryObject(pausePopup, testCase)
+ let control = createTemporaryObject(pausePopup, testCase)
verify(control)
- var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
+ let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"})
verify(openedSpy.valid)
- var closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"})
+ let closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"})
verify(closedSpy.valid)
- var openedChangeSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "openedChanged"})
+ let openedChangeSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "openedChanged"})
verify(openedChangeSpy.valid)
control.open()
@@ -1214,15 +1217,15 @@ TestCase {
}
function test_xyBindingLoop() {
- var window = createTemporaryObject(xyBindingLoop, testCase)
- var control = window.popup
+ let window = createTemporaryObject(xyBindingLoop, testCase)
+ let control = window.popup
waitForRendering(control.contentItem)
compare(control.x, (control.parent.width - control.width) / 2)
compare(control.y, (control.parent.height - control.height) / 2)
}
function test_windowParent() {
- var control = createTemporaryObject(popupControl, applicationWindow, {width: 100, height: 100})
+ let control = createTemporaryObject(popupControl, applicationWindow, {width: 100, height: 100})
verify(control)
control.open()
@@ -1230,7 +1233,7 @@ TestCase {
}
function test_deferredBackgroundSize() {
- var control = createTemporaryObject(popupControl, testCase, {width: 200, height: 100})
+ let control = createTemporaryObject(popupControl, testCase, {width: 200, height: 100})
verify(control)
compare(control.background.width, 200 + (control.background.leftInset || 0) + (control.background.rightInset || 0))
@@ -1238,8 +1241,12 @@ TestCase {
}
function test_anchors() {
- var control = createTemporaryObject(popupControl, applicationWindow.contentItem.Overlay.overlay,
+ let control = createTemporaryObject(popupControl, applicationWindow.contentItem.Overlay.overlay,
{ visible: true, width: 100, height: 100 })
+
+ applicationWindow.visible = true
+ verify(waitForRendering(applicationWindow.contentItem))
+
verify(control)
verify(control.visible)
// If there is a transition then make sure it is finished
@@ -1249,14 +1256,12 @@ TestCase {
compare(control.x, 0)
compare(control.y, 0)
- var overlay = control.Overlay.overlay
+ let overlay = control.Overlay.overlay
verify(overlay)
- var centerInSpy = createTemporaryObject(signalSpy, testCase, { target: control.anchors, signalName: "centerInChanged" })
+ let centerInSpy = createTemporaryObject(signalSpy, testCase, { target: control.anchors, signalName: "centerInChanged" })
verify(centerInSpy.valid)
- applicationWindow.visible = true
- verify(waitForRendering(applicationWindow.contentItem))
verify(overlay.width > 0)
verify(overlay.height > 0)
@@ -1267,7 +1272,7 @@ TestCase {
compare(control.y, (overlay.height - (control.width * control.scale)) / 2)
// Ensure that it warns when trying to set it to an item that's not its parent.
- var anotherItem = createTemporaryObject(rect, applicationWindow.contentItem, { x: 100, y: 100, width: 50, height: 50 })
+ let anotherItem = createTemporaryObject(rect, applicationWindow.contentItem, { x: 100, y: 100, width: 50, height: 50 })
verify(anotherItem)
ignoreWarning(new RegExp(".*QML Popup: Popup can only be centered within its immediate parent or Overlay.overlay"))
@@ -1338,13 +1343,13 @@ TestCase {
function test_shortcut() {
// Tests that a Shortcut with Qt.WindowShortcut context
// that is declared within a Popup is activated.
- var window = createTemporaryObject(shortcutWindowComponent, testCase)
- var control = window.popup
+ let window = createTemporaryObject(shortcutWindowComponent, testCase)
+ let control = window.popup
window.requestActivate()
tryCompare(window, "active", true)
- var shortcutActivatedSpy = createTemporaryObject(signalSpy, testCase,
+ let shortcutActivatedSpy = createTemporaryObject(signalSpy, testCase,
{ target: window.shortcut, signalName: "activated"} )
verify(shortcutActivatedSpy.valid)
@@ -1430,6 +1435,8 @@ TestCase {
let popup = window.popup
popup.open()
+ tryCompare(popup, "opened", true)
+ waitForRendering(popup.contentItem)
// mouse clicks into the popup must not propagate to the parent
mouseClick(window)
@@ -1449,6 +1456,18 @@ TestCase {
mouseRelease(title, pressPoint.x, pressPoint.y)
compare(title.pressedPosition, Qt.point(0, 0))
+
+ // Set modal as true and check for the same operation
+ popup.modal = true
+ oldPos = Qt.point(popup.x, popup.y)
+ mousePress(title, pressPoint.x, pressPoint.y)
+ fuzzyCompare(title.pressedPosition.x, pressPoint.x, 1)
+ fuzzyCompare(title.pressedPosition.y, pressPoint.y, 1)
+ mouseMove(title, pressPoint.x + 5, pressPoint.y + 5)
+ fuzzyCompare(popup.x, oldPos.x + 5, 1)
+ fuzzyCompare(popup.y, oldPos.y + 5, 1)
+ mouseRelease(title, pressPoint.x, pressPoint.y)
+ compare(title.pressedPosition, Qt.point(0, 0))
}
Component {
@@ -1470,4 +1489,73 @@ TestCase {
let rect = findChild(control.Overlay.overlay, "rect")
verify(rect)
}
+
+ Component {
+ id: popupOverlay
+
+ Popup {
+ id: popupCenterIn
+ property alias text: toastText.text
+ anchors.centerIn: Overlay.overlay
+ dim: true
+ modal: true
+ contentItem: ColumnLayout {
+ Text {
+ id: toastText
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ }
+ }
+ }
+ }
+
+ function test_popupOverlayCenterIn() {
+ let control = createTemporaryObject(popupOverlay, testCase)
+ verify(control)
+
+ // Modify text in the popup content item
+ control.text = "this is a long text causing a line break to show the binding loop "
+ + "(height) again after the first initialization of the text";
+
+ // Open popup item and wait for it to be rendered
+ control.open()
+ waitForRendering(testCase)
+ tryCompare(control, "opened", true)
+
+ // Verify popup position
+ compare(control.x, 0)
+ compare(control.y, control.parent.height / 2 - control.height / 2)
+ control.close()
+ }
+
+ Component {
+ id: popupWithOverlayInLoader
+
+ Loader {
+ id: loader
+ active: false
+ sourceComponent: Item {
+ anchors.fill: parent
+ Popup {
+ modal: true
+ visible: true
+ Overlay.modal: Rectangle { color: 'grey' }
+ }
+ }
+ }
+ }
+
+ function test_popupWithOverlayInLoader() { // QTBUG-122915
+ let loader = createTemporaryObject(popupWithOverlayInLoader, testCase)
+ verify(loader)
+
+ let overlay = loader.Overlay.overlay
+ verify(overlay)
+
+ loader.active = true
+ tryCompare(overlay, "visible", true)
+
+ loader.active = false
+ tryCompare(overlay, "visible", false)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_progressbar.qml b/tests/auto/quickcontrols/controls/data/tst_progressbar.qml
index 6484df815a..043863043d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_progressbar.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_progressbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -18,15 +18,17 @@ TestCase {
ProgressBar { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(progressBar, testCase)
verify(control)
}
function test_value() {
- var control = createTemporaryObject(progressBar, testCase)
+ let control = createTemporaryObject(progressBar, testCase)
verify(control)
compare(control.value, 0.0)
@@ -41,7 +43,7 @@ TestCase {
}
function test_range() {
- var control = createTemporaryObject(progressBar, testCase, {from: 0, to: 100, value: 50})
+ let control = createTemporaryObject(progressBar, testCase, {from: 0, to: 100, value: 50})
verify(control)
compare(control.from, 0)
@@ -73,7 +75,7 @@ TestCase {
}
function test_inverted() {
- var control = createTemporaryObject(progressBar, testCase, {from: 1.0, to: -1.0})
+ let control = createTemporaryObject(progressBar, testCase, {from: 1.0, to: -1.0})
verify(control)
compare(control.from, 1.0)
@@ -95,7 +97,7 @@ TestCase {
}
function test_position() {
- var control = createTemporaryObject(progressBar, testCase)
+ let control = createTemporaryObject(progressBar, testCase)
verify(control)
compare(control.value, 0)
@@ -111,7 +113,7 @@ TestCase {
}
function test_visualPosition() {
- var control = createTemporaryObject(progressBar, testCase)
+ let control = createTemporaryObject(progressBar, testCase)
verify(control)
compare(control.value, 0)
@@ -143,7 +145,7 @@ TestCase {
}
function test_indeterminate() {
- var control = createTemporaryObject(progressBar, testCase)
+ let control = createTemporaryObject(progressBar, testCase)
verify(control)
compare(control.indeterminate, false)
diff --git a/tests/auto/quickcontrols/controls/data/tst_radiobutton.qml b/tests/auto/quickcontrols/controls/data/tst_radiobutton.qml
index 08caac977d..a4d0a08d7d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_radiobutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_radiobutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -25,15 +25,17 @@ TestCase {
}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(radioButton, testCase)
verify(control)
}
function test_text() {
- var control = createTemporaryObject(radioButton, testCase)
+ let control = createTemporaryObject(radioButton, testCase)
verify(control)
compare(control.text, "")
@@ -44,10 +46,10 @@ TestCase {
}
function test_checked() {
- var control = createTemporaryObject(radioButton, testCase)
+ let control = createTemporaryObject(radioButton, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
sequenceSpy.expectedSequence = [] // No change expected
compare(control.checked, false)
@@ -65,10 +67,10 @@ TestCase {
}
function test_mouse() {
- var control = createTemporaryObject(radioButton, testCase)
+ let control = createTemporaryObject(radioButton, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// check
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
@@ -127,12 +129,12 @@ TestCase {
}
function test_touch() {
- var control = createTemporaryObject(radioButton, testCase)
+ let control = createTemporaryObject(radioButton, testCase)
verify(control)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
// check
sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
@@ -184,10 +186,10 @@ TestCase {
}
function test_keys() {
- var control = createTemporaryObject(radioButton, testCase)
+ let control = createTemporaryObject(radioButton, testCase)
verify(control)
- var sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
+ let sequenceSpy = signalSequenceSpy.createObject(control, {target: control})
sequenceSpy.expectedSequence = []
control.forceActiveFocus()
@@ -220,8 +222,8 @@ TestCase {
sequenceSpy.expectedSequence = []
// Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
// pressing buttons and the CI uses the QGnomeTheme platform theme.
- var keys = [Qt.Key_Escape, Qt.Key_Tab]
- for (var i = 0; i < keys.length; ++i) {
+ let keys = [Qt.Key_Escape, Qt.Key_Tab]
+ for (let i = 0; i < keys.length; ++i) {
sequenceSpy.reset()
keyClick(keys[i])
compare(control.checked, true)
@@ -238,7 +240,7 @@ TestCase {
}
function test_binding() {
- var container = createTemporaryObject(twoRadioButtons, testCase)
+ let container = createTemporaryObject(twoRadioButtons, testCase)
verify(container)
compare(container.rb1.checked, false)
@@ -277,65 +279,65 @@ TestCase {
}
function test_autoExclusive() {
- var container = createTemporaryObject(radioButtonGroup, testCase)
+ let container = createTemporaryObject(radioButtonGroup, testCase)
compare(container.children.length, 8)
- var checkStates = [false, false, false, false, false, false, false, false]
- for (var i = 0; i < 8; ++i)
+ let checkStates = [false, false, false, false, false, false, false, false]
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[0].checked = true
checkStates[0] = true
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[1].checked = true
checkStates[0] = false
checkStates[1] = true
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[2].checked = true
checkStates[2] = true
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[3].checked = true
checkStates[2] = false
checkStates[3] = true
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[4].checked = true
checkStates[4] = true
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[5].checked = true
checkStates[4] = false
checkStates[5] = true
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[6].checked = true
checkStates[6] = true
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[7].checked = true
checkStates[7] = true
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
container.children[0].checked = true
checkStates[0] = true
checkStates[1] = false
- for (i = 0; i < 8; ++i)
+ for (let i = 0; i < 8; ++i)
compare(container.children[i].checked, checkStates[i])
}
function test_baseline() {
- var control = createTemporaryObject(radioButton, testCase)
+ let control = createTemporaryObject(radioButton, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_radiodelegate.qml b/tests/auto/quickcontrols/controls/data/tst_radiodelegate.qml
index fe89838bda..fc6d93886f 100644
--- a/tests/auto/quickcontrols/controls/data/tst_radiodelegate.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_radiodelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -20,16 +20,18 @@ TestCase {
// TODO: data-fy tst_radiobutton (rename to tst_radio?) so we don't duplicate its tests here?
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(radioDelegate, testCase);
+ function test_defaults() {
+ let control = createTemporaryObject(radioDelegate, testCase);
verify(control);
verify(!control.checked);
}
function test_checked() {
- var control = createTemporaryObject(radioDelegate, testCase);
+ let control = createTemporaryObject(radioDelegate, testCase);
verify(control);
mouseClick(control);
@@ -40,17 +42,17 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(radioDelegate, testCase);
+ let control = createTemporaryObject(radioDelegate, testCase);
verify(control);
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset);
}
function test_spacing() {
- var control = createTemporaryObject(radioDelegate, testCase, { text: "Some long, long, long text" })
+ let control = createTemporaryObject(radioDelegate, testCase, { text: "Some long, long, long text" })
verify(control)
verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth)
- var textLabel = findChild(control.contentItem, "label")
+ let textLabel = findChild(control.contentItem, "label")
verify(textLabel)
// The implicitWidth of the IconLabel that all buttons use as their contentItem should be
@@ -77,7 +79,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(radioDelegate, testCase, {
+ let control = createTemporaryObject(radioDelegate, testCase, {
text: "RadioDelegate",
display: data.display,
width: 400,
@@ -87,11 +89,11 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
- var availableWidth = control.availableWidth - control.indicator.width - control.spacing
- var indicatorOffset = control.mirrored ? control.indicator.width + control.spacing : 0
+ let availableWidth = control.availableWidth - control.indicator.width - control.spacing
+ let indicatorOffset = control.mirrored ? control.indicator.width + control.spacing : 0
switch (control.display) {
case RadioDelegate.IconOnly:
diff --git a/tests/auto/quickcontrols/controls/data/tst_rangeslider.qml b/tests/auto/quickcontrols/controls/data/tst_rangeslider.qml
index 91e366c819..a574566bfb 100644
--- a/tests/auto/quickcontrols/controls/data/tst_rangeslider.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_rangeslider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -42,10 +42,12 @@ TestCase {
}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(sliderComponent, testCase)
+ function test_defaults() {
+ let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.stepSize, 0)
@@ -56,7 +58,7 @@ TestCase {
}
function test_values() {
- var control = createTemporaryObject(sliderComponent, testCase)
+ let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.first.value, 0.0)
@@ -81,7 +83,7 @@ TestCase {
}
function test_range() {
- var control = createTemporaryObject(sliderComponent, testCase, { from: 0, to: 100, "first.value": 50, "second.value": 100 })
+ let control = createTemporaryObject(sliderComponent, testCase, { from: 0, to: 100, "first.value": 50, "second.value": 100 })
verify(control)
compare(control.from, 0)
@@ -115,16 +117,16 @@ TestCase {
}
function test_setToFromUpdatesHandles() {
- var control = createTemporaryObject(sliderComponent, testCase, { from: 0, to: 100, "first.value": 50, "second.value": 75 })
+ let control = createTemporaryObject(sliderComponent, testCase, { from: 0, to: 100, "first.value": 50, "second.value": 75 })
verify(control)
let firstPos = control.first.position
let secondPos = control.second.position
- var firstPosChangesSpy = signalSpy.createObject(control, {target: control.first, signalName: "positionChanged"})
+ let firstPosChangesSpy = signalSpy.createObject(control, {target: control.first, signalName: "positionChanged"})
verify(firstPosChangesSpy.valid)
- var secondPosChangesSpy = signalSpy.createObject(control, {target: control.second, signalName: "positionChanged"})
+ let secondPosChangesSpy = signalSpy.createObject(control, {target: control.second, signalName: "positionChanged"})
verify(secondPosChangesSpy.valid)
// Increasing the 'to' value, so the positions of the handles should be
@@ -152,7 +154,7 @@ TestCase {
}
function test_setValues() {
- var control = createTemporaryObject(sliderComponent, testCase)
+ let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.from, 0)
@@ -177,7 +179,7 @@ TestCase {
}
function test_inverted() {
- var control = createTemporaryObject(sliderComponent, testCase, { from: 1.0, to: -1.0 })
+ let control = createTemporaryObject(sliderComponent, testCase, { from: 1.0, to: -1.0 })
verify(control)
compare(control.from, 1.0)
@@ -207,7 +209,7 @@ TestCase {
}
function test_visualPosition() {
- var control = createTemporaryObject(sliderComponent, testCase)
+ let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.first.value, 0.0)
@@ -252,7 +254,7 @@ TestCase {
}
function test_orientation() {
- var control = createTemporaryObject(sliderComponent, testCase)
+ let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.orientation, Qt.Horizontal)
@@ -277,19 +279,19 @@ TestCase {
}
function test_mouse(data) {
- var control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation, live: data.live })
+ let control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation, live: data.live })
verify(control)
- var firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
+ let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
- var firstMovedSpy = signalSpy.createObject(control, {target: control.first, signalName: "moved"})
+ let firstMovedSpy = signalSpy.createObject(control, {target: control.first, signalName: "moved"})
verify(firstMovedSpy.valid)
- var secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
+ let secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(secondPressedSpy.valid)
- var secondMovedSpy = signalSpy.createObject(control, {target: control.second, signalName: "moved"})
+ let secondMovedSpy = signalSpy.createObject(control, {target: control.second, signalName: "moved"})
verify(secondMovedSpy.valid)
// Press and release the first handle without moving it.
@@ -380,9 +382,9 @@ TestCase {
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
- var horizontal = control.orientation === Qt.Horizontal
- var toX = horizontal ? control.width * 0.5 : control.first.handle.x
- var toY = horizontal ? control.first.handle.y : control.height * 0.5
+ let horizontal = control.orientation === Qt.Horizontal
+ let toX = horizontal ? control.width * 0.5 : control.first.handle.x
+ let toY = horizontal ? control.first.handle.y : control.height * 0.5
mouseMove(control, toX, toY)
compare(firstPressedSpy.count, 5)
compare(firstMovedSpy.count, 1)
@@ -422,24 +424,25 @@ TestCase {
}
function test_touch(data) {
- var control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation, live: data.live })
+ let control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation, live: data.live })
verify(control)
- var firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
+ let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
- var firstMovedSpy = signalSpy.createObject(control, {target: control.first, signalName: "moved"})
+ let firstMovedSpy = signalSpy.createObject(control, {target: control.first, signalName: "moved"})
verify(firstMovedSpy.valid)
- var secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
+ let secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(secondPressedSpy.valid)
- var secondMovedSpy = signalSpy.createObject(control, {target: control.second, signalName: "moved"})
+ let secondMovedSpy = signalSpy.createObject(control, {target: control.second, signalName: "moved"})
verify(secondMovedSpy.valid)
// Press and release the first handle without moving it.
- var touch = touchEvent(control)
- touch.press(0, control, control.width * 0.25, control.height * 0.75).commit()
+ let touch = touchEvent(control)
+ control.setValues(0, 1);
+ touch.press(0, control, control.leftPadding, control.height - control.bottomPadding - 1).commit()
compare(firstPressedSpy.count, 1)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 0)
@@ -451,7 +454,7 @@ TestCase {
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
- touch.release(0, control, control.width * 0.25, control.height * 0.75).commit()
+ touch.release(0, control, control.leftPadding, control.height - control.bottomPadding - 1).commit()
compare(firstPressedSpy.count, 2)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 0)
@@ -464,7 +467,7 @@ TestCase {
compare(control.second.position, 1.0)
// Press and release the second handle without moving it.
- touch.press(0, control, control.width * 0.75, control.height * 0.25).commit()
+ touch.press(0, control, control.width - control.rightPadding - 1, control.topPadding).commit()
compare(firstPressedSpy.count, 2)
compare(secondPressedSpy.count, 1)
compare(control.first.pressed, false)
@@ -474,7 +477,7 @@ TestCase {
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
- touch.release(0, control, control.width * 0.75, control.height * 0.25).commit()
+ touch.release(0, control, control.width - control.rightPadding - 1, control.topPadding).commit()
compare(firstPressedSpy.count, 2)
compare(secondPressedSpy.count, 2)
compare(control.first.pressed, false)
@@ -516,9 +519,9 @@ TestCase {
compare(control.second.position, 1.0)
// Drag the first handle.
- var horizontal = control.orientation === Qt.Horizontal
- var toX = horizontal ? control.width * 0.5 : control.first.handle.x
- var toY = horizontal ? control.first.handle.y : control.height * 0.5
+ let horizontal = control.orientation === Qt.Horizontal
+ let toX = horizontal ? control.width * 0.5 : control.first.handle.x
+ let toY = horizontal ? control.first.handle.y : control.height * 0.5
touch.move(0, control, toX, toY).commit()
compare(firstPressedSpy.count, 5)
compare(secondPressedSpy.count, 2)
@@ -545,11 +548,11 @@ TestCase {
}
function test_multiTouch() {
- var control1 = createTemporaryObject(sliderComponent, testCase)
+ let control1 = createTemporaryObject(sliderComponent, testCase)
verify(control1)
// press and move the first handle of the first slider
- var touch = touchEvent(control1)
+ let touch = touchEvent(control1)
touch.press(0, control1, 0, 0).commit().move(0, control1, control1.width / 2, control1.height / 2).commit()
compare(control1.first.pressed, true)
compare(control1.first.position, 0.5)
@@ -564,7 +567,7 @@ TestCase {
compare(control1.second.pressed, true)
compare(control1.second.position, 0.5)
- var control2 = createTemporaryObject(sliderComponent, testCase, {y: control1.height})
+ let control2 = createTemporaryObject(sliderComponent, testCase, {y: control1.height})
verify(control2)
// press and move the first handle of the second slider
@@ -604,7 +607,7 @@ TestCase {
}
function test_overlappingHandles() {
- var control = createTemporaryObject(sliderComponent, testCase)
+ let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
// By default, we force the second handle to be after the first in
@@ -669,12 +672,12 @@ TestCase {
}
function test_keys(data) {
- var control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation })
+ let control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation })
verify(control)
- var pressedCount = 0
+ let pressedCount = 0
- var firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
+ let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
control.first.handle.forceActiveFocus()
@@ -682,7 +685,7 @@ TestCase {
control.first.value = 0.5
- for (var d1 = 1; d1 <= 10; ++d1) {
+ for (let d1 = 1; d1 <= 10; ++d1) {
keyPress(data.decrease)
compare(control.first.pressed, true)
compare(firstPressedSpy.count, ++pressedCount)
@@ -695,7 +698,7 @@ TestCase {
compare(firstPressedSpy.count, ++pressedCount)
}
- for (var i1 = 1; i1 <= 20; ++i1) {
+ for (let i1 = 1; i1 <= 20; ++i1) {
keyPress(data.increase)
compare(control.first.pressed, true)
compare(firstPressedSpy.count, ++pressedCount)
@@ -712,13 +715,13 @@ TestCase {
control.stepSize = 0.25
pressedCount = 0;
- var secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
+ let secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(secondPressedSpy.valid)
control.second.handle.forceActiveFocus()
verify(control.second.handle.activeFocus)
- for (var d2 = 1; d2 <= 10; ++d2) {
+ for (let d2 = 1; d2 <= 10; ++d2) {
keyPress(data.decrease)
compare(control.second.pressed, true)
compare(secondPressedSpy.count, ++pressedCount)
@@ -731,7 +734,7 @@ TestCase {
compare(secondPressedSpy.count, ++pressedCount)
}
- for (var i2 = 1; i2 <= 10; ++i2) {
+ for (let i2 = 1; i2 <= 10; ++i2) {
keyPress(data.increase)
compare(control.second.pressed, true)
compare(secondPressedSpy.count, ++pressedCount)
@@ -749,10 +752,10 @@ TestCase {
// test with "unbalanced" paddings (left padding != right padding) to ensure
// that the slider position calculation is done taking padding into account
// ==> the position is _not_ 0.5 in the middle of the control
- var control = createTemporaryObject(sliderComponent, testCase, { leftPadding: 10, rightPadding: 20, live: false })
+ let control = createTemporaryObject(sliderComponent, testCase, { leftPadding: 10, rightPadding: 20, live: false })
verify(control)
- var firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
+ let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
mousePress(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
@@ -835,13 +838,13 @@ TestCase {
}
function test_snapMode_mouse(data) {
- var control = createTemporaryObject(sliderComponent, testCase, {snapMode: data.snapMode, from: data.from, to: data.to, stepSize: 0.2, live: false, width: testCase.width})
+ let control = createTemporaryObject(sliderComponent, testCase, {snapMode: data.snapMode, from: data.from, to: data.to, stepSize: 0.2, live: false, width: testCase.width})
verify(control)
control.first.value = 0
control.second.value = data.to
- var fuzz = 0.05
+ let fuzz = 0.05
mousePress(control, control.leftPadding)
compare(control.first.pressed, true)
@@ -864,15 +867,15 @@ TestCase {
}
function test_snapMode_touch(data) {
- var control = createTemporaryObject(sliderComponent, testCase, {snapMode: data.snapMode, from: data.from, to: data.to, stepSize: 0.2, live: false, width: testCase.width})
+ let control = createTemporaryObject(sliderComponent, testCase, {snapMode: data.snapMode, from: data.from, to: data.to, stepSize: 0.2, live: false, width: testCase.width})
verify(control)
control.first.value = 0
control.second.value = data.to
- var fuzz = 0.05
+ let fuzz = 0.05
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control, control.first.handle.x, control.first.handle.y).commit()
compare(control.first.pressed, true)
compare(control.first.value, data.values[0])
@@ -890,7 +893,7 @@ TestCase {
}
function test_focus() {
- var control = createTemporaryObject(sliderComponent, testCase)
+ let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.activeFocus, false)
@@ -930,10 +933,10 @@ TestCase {
}
function test_hover(data) {
- var control = createTemporaryObject(sliderComponent, testCase, {hoverEnabled: data.hoverEnabled})
+ let control = createTemporaryObject(sliderComponent, testCase, {hoverEnabled: data.hoverEnabled})
verify(control)
- var node = control[data.node]
+ let node = control[data.node]
compare(control.hovered, false)
compare(node.hovered, false)
@@ -946,7 +949,7 @@ TestCase {
}
function test_nullHandles() {
- var control = createTemporaryObject(sliderComponent, testCase, {"second.value": 1})
+ let control = createTemporaryObject(sliderComponent, testCase, {"second.value": 1})
verify(control)
verify(control.first.handle)
@@ -973,7 +976,7 @@ TestCase {
}
function test_touchDragThreshold_data() {
- var d1 = 3; var d2 = 7;
+ let d1 = 3; let d2 = 7;
return [
{ tag: "horizontal", orientation: Qt.Horizontal, dx1: d1, dy1: 0, dx2: d2, dy2: 0 },
{ tag: "vertical", orientation: Qt.Vertical, dx1: 0, dy1: -d1, dx2: 0, dy2: -d2 },
@@ -983,19 +986,19 @@ TestCase {
}
function test_touchDragThreshold(data) {
- var control = createTemporaryObject(sliderComponent, testCase, {touchDragThreshold: 10, live: true, orientation: data.orientation, "first.value": 0, "second.value": 1})
+ let control = createTemporaryObject(sliderComponent, testCase, {touchDragThreshold: 10, live: true, orientation: data.orientation, "first.value": 0, "second.value": 1})
verify(control)
compare(control.touchDragThreshold, 10)
- var valueChangedCount = 0
- var valueChangedSpy = signalSpy.createObject(control, {target: control, signalName: "touchDragThresholdChanged"})
+ let valueChangedCount = 0
+ let valueChangedSpy = signalSpy.createObject(control, {target: control, signalName: "touchDragThresholdChanged"})
verify(valueChangedSpy.valid)
control.touchDragThreshold = undefined
compare(control.touchDragThreshold, -1) // reset to -1
compare(valueChangedSpy.count, ++valueChangedCount)
- var t = 5
+ let t = 5
control.touchDragThreshold = t
compare(control.touchDragThreshold, t)
compare(valueChangedSpy.count, ++valueChangedCount)
@@ -1004,26 +1007,26 @@ TestCase {
compare(control.touchDragThreshold, t)
compare(valueChangedSpy.count, valueChangedCount)
- var pressedCount = 0
- var pressedCount2 = 0
- var visualPositionCount = 0
- var visualPositionCount2 = 0
+ let pressedCount = 0
+ let pressedCount2 = 0
+ let visualPositionCount = 0
+ let visualPositionCount2 = 0
- var pressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(pressedSpy.valid)
- var pressedSpy2 = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
+ let pressedSpy2 = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(pressedSpy2.valid)
- var visualPositionSpy = signalSpy.createObject(control, {target: control.first, signalName: "visualPositionChanged"})
+ let visualPositionSpy = signalSpy.createObject(control, {target: control.first, signalName: "visualPositionChanged"})
verify(visualPositionSpy.valid)
- var visualPositionSpy2 = signalSpy.createObject(control, {target: control.second, signalName: "visualPositionChanged"})
+ let visualPositionSpy2 = signalSpy.createObject(control, {target: control.second, signalName: "visualPositionChanged"})
verify(visualPositionSpy2.valid)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
control.first.value = 0.4
control.second.value = 1
- var x0 = control.first.handle.x + control.first.handle.width * 0.5
- var y0 = control.first.handle.y + control.first.handle.height * 0.5
+ let x0 = control.first.handle.x + control.first.handle.width * 0.5
+ let y0 = control.first.handle.y + control.first.handle.height * 0.5
touch.press(0, control, x0, y0).commit()
compare(pressedSpy.count, ++pressedCount)
compare(control.first.pressed, true)
@@ -1073,7 +1076,7 @@ TestCase {
}
function test_valueAt(data) {
- var control = createTemporaryObject(sliderComponent, testCase, data.properties)
+ let control = createTemporaryObject(sliderComponent, testCase, data.properties)
verify(control)
compare(control.valueAt(0.0), data.values[0])
@@ -1081,4 +1084,173 @@ TestCase {
compare(control.valueAt(0.5), data.values[2])
compare(control.valueAt(1.0), data.values[3])
}
+
+ function test_updatePositionOnPress() {
+ let control = createTemporaryObject(sliderComponent, testCase)
+ verify(control)
+
+ let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
+ verify(firstPressedSpy.valid)
+
+ let firstMovedSpy = signalSpy.createObject(control, {target: control.first, signalName: "moved"})
+ verify(firstMovedSpy.valid)
+
+ let secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
+ verify(secondPressedSpy.valid)
+
+ let secondMovedSpy = signalSpy.createObject(control, {target: control.second, signalName: "moved"})
+ verify(secondMovedSpy.valid)
+
+ // Touch press and release on the left corner
+ control.setValues(0.2, 0.8)
+ compare(control.first.value, 0.2)
+ compare(control.second.value, 0.8)
+ let touch = touchEvent(control)
+ touch.press(0, control, 0, control.height * 0.75).commit()
+ compare(firstPressedSpy.count, 1)
+ compare(firstMovedSpy.count, 0)
+ compare(secondPressedSpy.count, 0)
+ compare(secondMovedSpy.count, 0)
+ compare(control.first.pressed, true)
+ compare(control.first.value, 0.2)
+ compare(control.first.position, 0.2)
+ compare(control.second.pressed, false)
+ compare(control.second.value, 0.8)
+ compare(control.second.position, 0.8)
+
+ touch.release(0, control, 0, control.height * 0.75).commit()
+ compare(firstPressedSpy.count, 2)
+ compare(firstMovedSpy.count, 1)
+ compare(secondPressedSpy.count, 0)
+ compare(secondMovedSpy.count, 0)
+ compare(control.first.pressed, false)
+ compare(control.first.value, 0.0)
+ compare(control.first.position, 0.0)
+ compare(control.second.pressed, false)
+ compare(control.second.value, 0.8)
+ compare(control.second.position, 0.8)
+
+ firstPressedSpy.clear()
+ firstMovedSpy.clear()
+ secondPressedSpy.clear()
+ secondMovedSpy.clear()
+
+ // Touch press and release on the right corner
+ // On touch, the handle position is updated on release
+ control.setValues(0.2, 0.8)
+ compare(control.first.value, 0.2)
+ compare(control.second.value, 0.8)
+ touch = touchEvent(control)
+ touch.press(0, control, control.width - control.rightPadding - 1, control.height * 0.75).commit()
+ compare(secondPressedSpy.count, 1)
+ compare(secondMovedSpy.count, 0)
+ compare(firstPressedSpy.count, 0)
+ compare(firstMovedSpy.count, 0)
+ compare(control.second.pressed, true)
+ compare(control.second.value, 0.8)
+ compare(control.second.position, 0.8)
+ compare(control.first.pressed, false)
+ compare(control.first.value, 0.2)
+ compare(control.first.position, 0.2)
+
+ touch.release(0, control, control.width - control.rightPadding - 1, control.height * 0.75).commit()
+ compare(secondPressedSpy.count, 2)
+ compare(secondMovedSpy.count, 1)
+ compare(firstPressedSpy.count, 0)
+ compare(firstMovedSpy.count, 0)
+ compare(control.second.pressed, false)
+ compare(control.second.value, 1.0)
+ compare(control.second.position, 1.0)
+ compare(control.first.pressed, false)
+ compare(control.first.value, 0.2)
+ compare(control.first.position, 0.2)
+
+ firstPressedSpy.clear()
+ firstMovedSpy.clear()
+ secondPressedSpy.clear()
+ secondMovedSpy.clear()
+
+ // Mouse press and release on the left corner
+ // On mouse, the position is immediately updated on press
+ control.setValues(0.2, 0.8)
+ compare(control.first.value, 0.2)
+ compare(control.second.value, 0.8)
+ mousePress(control, 0, control.height * 0.75, Qt.LeftButton)
+ compare(firstPressedSpy.count, 1)
+ compare(firstMovedSpy.count, 1)
+ compare(secondPressedSpy.count, 0)
+ compare(secondMovedSpy.count, 0)
+ compare(control.first.pressed, true)
+ compare(control.first.value, 0.0)
+ compare(control.first.position, 0.0)
+ compare(control.second.pressed, false)
+ compare(control.second.value, 0.8)
+ compare(control.second.position, 0.8)
+
+ mouseRelease(control, 0, control.height * 0.75, Qt.LeftButton)
+ compare(firstPressedSpy.count, 2)
+ compare(firstMovedSpy.count, 1)
+ compare(secondPressedSpy.count, 0)
+ compare(secondMovedSpy.count, 0)
+ compare(control.first.pressed, false)
+ compare(control.first.value, 0.0)
+ compare(control.first.position, 0.0)
+ compare(control.second.pressed, false)
+ compare(control.second.value, 0.8)
+ compare(control.second.position, 0.8)
+
+ firstPressedSpy.clear()
+ firstMovedSpy.clear()
+ secondPressedSpy.clear()
+ secondMovedSpy.clear()
+
+ // Mouse press and release on the right corner
+ control.setValues(0.2, 0.8)
+ compare(control.first.value, 0.2)
+ compare(control.second.value, 0.8)
+ mousePress(control, control.width - control.rightPadding - 1, control.height * 0.75, Qt.LeftButton)
+ compare(secondPressedSpy.count, 1)
+ compare(secondMovedSpy.count, 1)
+ compare(firstPressedSpy.count, 0)
+ compare(firstMovedSpy.count, 0)
+ compare(control.second.pressed, true)
+ compare(control.second.value, 1.0)
+ compare(control.second.position, 1.0)
+ compare(control.first.pressed, false)
+ compare(control.first.value, 0.2)
+ compare(control.first.position, 0.2)
+
+ mouseRelease(control, control.width - control.rightPadding - 1, control.height * 0.75, Qt.LeftButton)
+ compare(secondPressedSpy.count, 2)
+ compare(secondMovedSpy.count, 1)
+ compare(firstPressedSpy.count, 0)
+ compare(firstMovedSpy.count, 0)
+ compare(control.second.pressed, false)
+ compare(control.second.value, 1.0)
+ compare(control.second.position, 1.0)
+ compare(control.first.pressed, false)
+ compare(control.first.value, 0.2)
+ compare(control.first.position, 0.2)
+ }
+
+ function test_clickFocus() {
+ let control = createTemporaryObject(sliderComponent, testCase)
+ verify(control)
+
+ // Click on the second handle - it should get focus on press.
+ mousePress(control, control.second.handle.x, control.second.handle.y, Qt.LeftButton)
+ if (Qt.platform.os === "osx")
+ verify(!control.activeFocus)
+ else
+ verify(control.activeFocus)
+ mouseRelease(control, control.second.handle.x, control.second.handle.y, Qt.LeftButton)
+
+ // Click on the first handle - it should get focus on press.
+ mousePress(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
+ if (Qt.platform.os === "osx")
+ verify(!control.activeFocus)
+ else
+ verify(control.activeFocus)
+ mouseRelease(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_roundbutton.qml b/tests/auto/quickcontrols/controls/data/tst_roundbutton.qml
index dfdd62086d..553d99a2d8 100644
--- a/tests/auto/quickcontrols/controls/data/tst_roundbutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_roundbutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -18,18 +18,20 @@ TestCase {
RoundButton { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(roundButton, testCase)
verify(control)
}
function test_radius() {
- var control = createTemporaryObject(roundButton, testCase);
+ let control = createTemporaryObject(roundButton, testCase);
verify(control);
- var implicitRadius = control.radius;
+ let implicitRadius = control.radius;
compare(implicitRadius, Math.min(control.width, control.height) / 2);
control.radius = 10;
@@ -46,11 +48,11 @@ TestCase {
}
function test_spacing() {
- var control = createTemporaryObject(roundButton, testCase, { text: "Some long, long, long text" })
+ let control = createTemporaryObject(roundButton, testCase, { text: "Some long, long, long text" })
verify(control)
verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth)
- var textLabel = findChild(control.contentItem, "label")
+ let textLabel = findChild(control.contentItem, "label")
verify(textLabel)
// The implicitWidth of the IconLabel that all buttons use as their contentItem
@@ -79,7 +81,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(roundButton, testCase, {
+ let control = createTemporaryObject(roundButton, testCase, {
text: "RoundButton",
display: data.display,
"icon.source": "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png",
@@ -88,8 +90,8 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
switch (control.display) {
case RoundButton.IconOnly:
diff --git a/tests/auto/quickcontrols/controls/data/tst_scrollbar.qml b/tests/auto/quickcontrols/controls/data/tst_scrollbar.qml
index f26d8f5cb0..2031019157 100644
--- a/tests/auto/quickcontrols/controls/data/tst_scrollbar.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_scrollbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -28,6 +28,7 @@ TestCase {
Component {
id: scrollBar
ScrollBar {
+ objectName: "scrollBar"
padding: 0
minimumSize: 0
}
@@ -43,6 +44,7 @@ TestCase {
Component {
id: flickable
Flickable {
+ objectName: "flickable"
width: 100
height: 100
contentWidth: 200
@@ -52,19 +54,21 @@ TestCase {
}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(defaultScrollBar, testCase)
verify(control)
}
function test_attach() {
- var container = createTemporaryObject(flickable, testCase)
+ let container = createTemporaryObject(flickable, testCase)
verify(container)
waitForRendering(container)
- var vertical = scrollBar.createObject()
+ let vertical = scrollBar.createObject(null, { objectName: "verticalScrollBar" })
verify(!vertical.parent)
compare(vertical.size, 0.0)
compare(vertical.position, 0.0)
@@ -94,7 +98,7 @@ TestCase {
container.width += 10
compare(vertical.x, 123)
- var horizontal = createTemporaryObject(scrollBar, null)
+ let horizontal = createTemporaryObject(scrollBar, null, { objectName: "horizontalScrollBar" })
verify(!horizontal.parent)
compare(horizontal.size, 0.0)
compare(horizontal.position, 0.0)
@@ -124,7 +128,7 @@ TestCase {
container.height += 10
compare(horizontal.y, 123)
- var velocity = container.maximumFlickVelocity
+ let velocity = container.maximumFlickVelocity
compare(container.flicking, false)
container.flick(-velocity, -velocity)
@@ -146,16 +150,16 @@ TestCase {
compare(horizontal.size, container.visibleArea.widthRatio)
compare(horizontal.position, container.visibleArea.xPosition)
- var oldY = vertical.y
- var oldHeight = vertical.height
+ let oldY = vertical.y
+ let oldHeight = vertical.height
vertical.parent = testCase
vertical.y -= 10
container.height += 10
compare(vertical.y, oldY - 10)
compare(vertical.height, oldHeight)
- var oldX = horizontal.x
- var oldWidth = horizontal.width
+ let oldX = horizontal.x
+ let oldWidth = horizontal.width
horizontal.parent = testCase
horizontal.x -= 10
container.width += 10
@@ -216,12 +220,12 @@ TestCase {
}
function test_mouse(data) {
- var control = createTemporaryObject(scrollBarWithDefaultPadding, testCase, data.properties)
+ let control = createTemporaryObject(scrollBarWithDefaultPadding, testCase, data.properties)
verify(control)
- var grooveRange = getGrooveRange(control)
+ let grooveRange = getGrooveRange(control)
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
verify(pressedSpy.valid)
mousePress(control, grooveRange.start.x, grooveRange.start.y, Qt.LeftButton)
@@ -268,7 +272,7 @@ TestCase {
fuzzyCompare(control.position, 0.25, 0.01)
if (control.__decreaseVisual.indicator !== null) {
- var p = control.__decreaseVisual.indicator.mapToItem(control, Qt.point(0, 0))
+ let p = control.__decreaseVisual.indicator.mapToItem(control, Qt.point(0, 0))
mousePress(control, p.x, p.y, Qt.LeftButton)
compare(pressedSpy.count, 4)
compare(control.pressed, false)
@@ -296,18 +300,18 @@ TestCase {
}
function test_touch(data) {
- var control = createTemporaryObject(scrollBar, testCase, data.properties)
+ let control = createTemporaryObject(scrollBar, testCase, data.properties)
verify(control)
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
verify(pressedSpy.valid)
control.width += (control.leftPadding + control.rightPadding)
control.height += (control.topPadding + control.bottomPadding)
- var availableSlideWidth = 0
- var availableSlideHeight = 0
+ let availableSlideWidth = 0
+ let availableSlideHeight = 0
- var p0 = {}
+ let p0 = {}
if (control.orientation === Qt.Horizontal) {
availableSlideWidth = control.width - control.rightPadding - control.leftPadding
p0 = { x = control.leftPadding, y = control.height/2 }
@@ -316,7 +320,7 @@ TestCase {
p0 = { x = control.width/2, y = control.topPadding}
}
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control, p0.x, p0.y).commit()
compare(pressedSpy.count, 1)
@@ -360,23 +364,23 @@ TestCase {
}
function test_multiTouch() {
- var control1 = createTemporaryObject(scrollBar, testCase)
+ let control1 = createTemporaryObject(scrollBar, testCase)
verify(control1)
control1.height = 200 + (control1.topPadding + control1.bottomPadding)
- var grooveRange = getGrooveRange(control1)
+ let grooveRange = getGrooveRange(control1)
- var pressedCount1 = 0
- var movedCount1 = 0
+ let pressedCount1 = 0
+ let movedCount1 = 0
- var pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"})
+ let pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"})
verify(pressedSpy1.valid)
- var positionSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "positionChanged"})
+ let positionSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "positionChanged"})
verify(positionSpy1.valid)
- var touch = touchEvent(control1)
+ let touch = touchEvent(control1)
touch.press(0, control1, grooveRange.start.x, grooveRange.start.y).commit().move(0, control1, grooveRange.end.x+1, grooveRange.end.y+1).commit()
compare(pressedSpy1.count, ++pressedCount1)
@@ -394,17 +398,17 @@ TestCase {
compare(control1.pressed, true)
compare(control1.position, 1.0)
- var control2 = createTemporaryObject(scrollBar, testCase, {y: control1.height})
+ let control2 = createTemporaryObject(scrollBar, testCase, {y: control1.height})
verify(control2)
control2.height = 200 + (control2.topPadding + control2.bottomPadding)
- var pressedCount2 = 0
- var movedCount2 = 0
+ let pressedCount2 = 0
+ let movedCount2 = 0
- var pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"})
+ let pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"})
verify(pressedSpy2.valid)
- var positionSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "positionChanged"})
+ let positionSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "positionChanged"})
verify(positionSpy2.valid)
// press the second scrollbar
@@ -457,7 +461,7 @@ TestCase {
}
function test_increase_decrease(data) {
- var control = createTemporaryObject(scrollBar, testCase, {position: 0.5, active: data.active})
+ let control = createTemporaryObject(scrollBar, testCase, {position: 0.5, active: data.active})
verify(control)
if (data.increase) {
@@ -479,18 +483,18 @@ TestCase {
}
function test_stepSize(data) {
- var control = createTemporaryObject(scrollBar, testCase, {stepSize: data.stepSize})
+ let control = createTemporaryObject(scrollBar, testCase, {stepSize: data.stepSize})
verify(control)
compare(control.stepSize, data.stepSize)
compare(control.position, 0.0)
- var count = 10
+ let count = 10
if (data.stepSize !== 0.0)
count = 1.0 / data.stepSize
// increase until 1.0
- for (var i = 1; i <= count; ++i) {
+ for (let i = 1; i <= count; ++i) {
control.increase()
compare(control.position, i / count)
}
@@ -498,7 +502,7 @@ TestCase {
compare(control.position, 1.0)
// decrease until 0.0
- for (var d = count - 1; d >= 0; --d) {
+ for (let d = count - 1; d >= 0; --d) {
control.decrease()
compare(control.position, d / count)
}
@@ -514,7 +518,7 @@ TestCase {
}
function test_padding(data) {
- var control = createTemporaryObject(scrollBar, testCase, data.properties)
+ let control = createTemporaryObject(scrollBar, testCase, data.properties)
mousePress(control, control.leftPadding + control.availableWidth * 0.5, control.topPadding + control.availableHeight * 0.5, Qt.LeftButton)
mouseRelease(control, control.leftPadding + control.availableWidth * 0.5, control.topPadding + control.availableHeight * 0.5, Qt.LeftButton)
@@ -528,7 +532,7 @@ TestCase {
}
function test_mirrored() {
- var container = createTemporaryObject(flickable, testCase)
+ let container = createTemporaryObject(flickable, testCase)
verify(container)
waitForRendering(container)
@@ -547,7 +551,7 @@ TestCase {
}
function test_hover(data) {
- var control = createTemporaryObject(scrollBar, testCase, {hoverEnabled: data.hoverEnabled, interactive: data.interactive})
+ let control = createTemporaryObject(scrollBar, testCase, {hoverEnabled: data.hoverEnabled, interactive: data.interactive})
verify(control)
compare(control.hovered, false)
@@ -579,14 +583,14 @@ TestCase {
}
function test_snapMode_mouse(data) {
- var control = createTemporaryObject(scrollBar, testCase, {snapMode: data.snapMode, orientation: Qt.Horizontal, stepSize: data.stepSize, size: data.size, width: data.width})
+ let control = createTemporaryObject(scrollBar, testCase, {snapMode: data.snapMode, orientation: Qt.Horizontal, stepSize: data.stepSize, size: data.size, width: data.width})
verify(control)
// In case the slider is surrounded with decrease/increase buttons
// Adjust slider width so that slider area is a whole number (to avoid rounding errors)
control.width += control.leftPadding + control.rightPadding
function snappedPosition(pos) {
- var effectiveStep = control.stepSize * (1.0 - control.size)
+ let effectiveStep = control.stepSize * (1.0 - control.size)
return Math.round(pos / effectiveStep) * effectiveStep
}
@@ -594,14 +598,14 @@ TestCase {
return Math.max(0, Math.min(pos, 1.0 - control.size))
}
- var minHandlePos = control.leftPadding
- var maxHandlePos = control.width - control.rightPadding
- var availableSlideWidth = maxHandlePos - minHandlePos
+ let minHandlePos = control.leftPadding
+ let maxHandlePos = control.width - control.rightPadding
+ let availableSlideWidth = maxHandlePos - minHandlePos
mousePress(control, minHandlePos, 0)
compare(control.position, 0)
mouseMove(control, minHandlePos + availableSlideWidth * 0.3, 0)
- var expectedMovePos = 0.3
+ let expectedMovePos = 0.3
if (control.snapMode === ScrollBar.SnapAlways) {
expectedMovePos = snappedPosition(expectedMovePos)
verify(expectedMovePos !== 0.3)
@@ -609,7 +613,7 @@ TestCase {
compare(control.position, expectedMovePos)
mouseRelease(control, minHandlePos + availableSlideWidth * 0.75, 0)
- var expectedReleasePos = 0.75
+ let expectedReleasePos = 0.75
if (control.snapMode !== ScrollBar.NoSnap) {
expectedReleasePos = snappedPosition(expectedReleasePos)
verify(expectedReleasePos !== 0.75)
@@ -619,10 +623,10 @@ TestCase {
control.position = 0
mousePress(control, minHandlePos, 0)
- var steps = 0
- var prevPos = 0
+ let steps = 0
+ let prevPos = 0
- for (var x = minHandlePos; x < maxHandlePos; ++x) {
+ for (let x = minHandlePos; x < maxHandlePos; ++x) {
mouseMove(control, x, 0)
expectedMovePos = boundPosition((x - minHandlePos) / availableSlideWidth)
if (control.snapMode === ScrollBar.SnapAlways)
@@ -643,14 +647,14 @@ TestCase {
}
function test_snapMode_touch(data) {
- var control = createTemporaryObject(scrollBar, testCase, {snapMode: data.snapMode, orientation: Qt.Horizontal, stepSize: data.stepSize, size: data.size, width: data.width})
+ let control = createTemporaryObject(scrollBar, testCase, {snapMode: data.snapMode, orientation: Qt.Horizontal, stepSize: data.stepSize, size: data.size, width: data.width})
verify(control)
// In case the slider is surrounded with decrease/increase buttons
// Adjust slider width so that slider area is a whole number (to avoid rounding errors)
control.width += control.leftPadding + control.rightPadding
function snappedPosition(pos) {
- var effectiveStep = control.stepSize * (1.0 - control.size)
+ let effectiveStep = control.stepSize * (1.0 - control.size)
return Math.round(pos / effectiveStep) * effectiveStep
}
@@ -658,16 +662,16 @@ TestCase {
return Math.max(0, Math.min(pos, 1.0 - control.size))
}
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
- var minHandlePos = control.leftPadding
- var maxHandlePos = control.width - control.rightPadding
- var availableSlideWidth = maxHandlePos - minHandlePos
+ let minHandlePos = control.leftPadding
+ let maxHandlePos = control.width - control.rightPadding
+ let availableSlideWidth = maxHandlePos - minHandlePos
touch.press(0, control, minHandlePos, 0).commit()
compare(control.position, 0)
touch.move(0, control, minHandlePos + availableSlideWidth*0.3, 0).commit()
- var expectedMovePos = 0.3
+ let expectedMovePos = 0.3
if (control.snapMode === ScrollBar.SnapAlways) {
expectedMovePos = snappedPosition(expectedMovePos)
verify(expectedMovePos !== 0.3)
@@ -675,7 +679,7 @@ TestCase {
compare(control.position, expectedMovePos)
touch.release(0, control, minHandlePos + availableSlideWidth*0.75, 0).commit()
- var expectedReleasePos = 0.75
+ let expectedReleasePos = 0.75
if (control.snapMode !== ScrollBar.NoSnap) {
expectedReleasePos = snappedPosition(expectedReleasePos)
verify(expectedReleasePos !== 0.75)
@@ -685,10 +689,10 @@ TestCase {
control.position = 0
touch.press(0, control, minHandlePos, 0).commit()
- var steps = 0
- var prevPos = 0
+ let steps = 0
+ let prevPos = 0
- for (var x = minHandlePos; x < maxHandlePos; ++x) {
+ for (let x = minHandlePos; x < maxHandlePos; ++x) {
touch.move(0, control, x, 0).commit()
expectedMovePos = boundPosition((x - minHandlePos) / availableSlideWidth)
if (control.snapMode === ScrollBar.SnapAlways)
@@ -712,7 +716,7 @@ TestCase {
}
function test_interactive(data) {
- var control = createTemporaryObject(scrollBar, testCase, {interactive: data.interactive})
+ let control = createTemporaryObject(scrollBar, testCase, {interactive: data.interactive})
verify(control)
compare(control.interactive, data.interactive)
@@ -759,7 +763,7 @@ TestCase {
}
function test_policy() {
- var control = createTemporaryObject(scrollBar, testCase, {active: true})
+ let control = createTemporaryObject(scrollBar, testCase, {active: true})
verify(control)
compare(ScrollBar.AsNeeded, Qt.ScrollBarAsNeeded)
@@ -769,8 +773,8 @@ TestCase {
compare(control.visible, true)
compare(control.policy, ScrollBar.AsNeeded)
- var windowsStyle = false
- var macOSStyle = false
+ let windowsStyle = false
+ let macOSStyle = false
if (control.background instanceof NativeStyle.StyleItem) {
windowsStyle = Qt.platform.pluginName === "windows"
macOSStyle = Qt.platform.pluginName === "cocoa"
@@ -794,14 +798,14 @@ TestCase {
}
function test_overshoot() {
- var container = createTemporaryObject(flickable, testCase)
+ let container = createTemporaryObject(flickable, testCase)
verify(container)
waitForRendering(container)
- var vertical = scrollBar.createObject(container, {size: 0.5})
+ let vertical = scrollBar.createObject(container, {size: 0.5})
container.ScrollBar.vertical = vertical
- var horizontal = scrollBar.createObject(container, {size: 0.5})
+ let horizontal = scrollBar.createObject(container, {size: 0.5})
container.ScrollBar.horizontal = horizontal
// negative vertical overshoot (pos < 0)
@@ -826,7 +830,7 @@ TestCase {
}
function test_orientation() {
- var control = createTemporaryObject(scrollBar, testCase)
+ let control = createTemporaryObject(scrollBar, testCase)
verify(control)
compare(control.orientation, Qt.Vertical)
@@ -840,10 +844,10 @@ TestCase {
}
function test_flashing() {
- var control = createTemporaryObject(scrollBar, testCase, {size: 0.2})
+ let control = createTemporaryObject(scrollBar, testCase, {size: 0.2})
verify(control)
- var activeSpy = signalSpy.createObject(control, {target: control, signalName: "activeChanged"})
+ let activeSpy = signalSpy.createObject(control, {target: control, signalName: "activeChanged"})
verify(activeSpy.valid)
compare(control.active, false)
@@ -884,11 +888,11 @@ TestCase {
}
function test_minimumSize() {
- var container = createTemporaryObject(flickable, testCase)
+ let container = createTemporaryObject(flickable, testCase)
verify(container)
waitForRendering(container)
- var vertical = scrollBar.createObject(container, {minimumSize: 0.1})
+ let vertical = scrollBar.createObject(container, {minimumSize: 0.1})
container.ScrollBar.vertical = vertical
compare(container.visibleArea.heightRatio, 0.5)
@@ -932,7 +936,7 @@ TestCase {
}
function test_resize() {
- var vertical = createTemporaryObject(scrollBar, testCase, { height:200, orientation: Qt.Vertical, size: 0.5, position: 0.5 })
+ let vertical = createTemporaryObject(scrollBar, testCase, { height:200, orientation: Qt.Vertical, size: 0.5, position: 0.5 })
verify(vertical)
vertical.size = 0.8
@@ -943,7 +947,7 @@ TestCase {
compare(vertical.visualPosition, 0.2)
- var horizontal = createTemporaryObject(scrollBar, testCase, { width:200, orientation: Qt.Horizontal, size: 0.5, position: 0.5 })
+ let horizontal = createTemporaryObject(scrollBar, testCase, { width:200, orientation: Qt.Horizontal, size: 0.5, position: 0.5 })
verify(horizontal)
horizontal.size = 0.8
@@ -955,7 +959,7 @@ TestCase {
}
function test_setting_invalid_property_values() {
- var control = createTemporaryObject(scrollBar, testCase, {size: 2.0, minimumSize: -1.0})
+ let control = createTemporaryObject(scrollBar, testCase, {size: 2.0, minimumSize: -1.0})
verify(control)
// check that the values are within the expected range
@@ -987,4 +991,19 @@ TestCase {
compare(oldPosition, control.position)
compare(oldStepSize, control.stepSize)
}
+
+ function test_visualSizeChanged_is_emitted_by_setSize() {
+ const control = createTemporaryObject(scrollBar, testCase, {size: 0.2})
+ verify(control)
+
+ const spy = signalSpy.createObject(control, {target: control, signalName: "visualSizeChanged"})
+ verify(spy.valid)
+
+ compare(control.visualSize, 0.2)
+
+ control.size = 0.5
+
+ compare(spy.count, 1)
+ compare(control.visualSize, 0.5)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_scrollindicator.qml b/tests/auto/quickcontrols/controls/data/tst_scrollindicator.qml
index d87e29f980..74713f930c 100644
--- a/tests/auto/quickcontrols/controls/data/tst_scrollindicator.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_scrollindicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -35,19 +35,21 @@ TestCase {
}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(scrollIndicator, testCase)
verify(control)
}
function test_attach() {
- var container = createTemporaryObject(flickable, testCase)
+ let container = createTemporaryObject(flickable, testCase)
verify(container)
waitForRendering(container)
- var vertical = createTemporaryObject(scrollIndicator, null)
+ let vertical = createTemporaryObject(scrollIndicator, null)
verify(!vertical.parent)
compare(vertical.size, 0.0)
compare(vertical.position, 0.0)
@@ -77,7 +79,7 @@ TestCase {
container.width += 10
compare(vertical.x, 123)
- var horizontal = createTemporaryObject(scrollIndicator, null)
+ let horizontal = createTemporaryObject(scrollIndicator, null)
verify(!horizontal.parent)
compare(horizontal.size, 0.0)
compare(horizontal.position, 0.0)
@@ -107,7 +109,7 @@ TestCase {
container.height += 10
compare(horizontal.y, 123)
- var velocity = container.maximumFlickVelocity
+ let velocity = container.maximumFlickVelocity
compare(container.flicking, false)
container.flick(-velocity, -velocity)
@@ -129,16 +131,16 @@ TestCase {
compare(horizontal.size, container.visibleArea.widthRatio)
compare(horizontal.position, container.visibleArea.xPosition)
- var oldY = vertical.y
- var oldHeight = vertical.height
+ let oldY = vertical.y
+ let oldHeight = vertical.height
vertical.parent = testCase
vertical.y -= 10
container.height += 10
compare(vertical.y, oldY - 10)
compare(vertical.height, oldHeight)
- var oldX = horizontal.x
- var oldWidth = horizontal.width
+ let oldX = horizontal.x
+ let oldWidth = horizontal.width
horizontal.parent = testCase
horizontal.x -= 10
container.width += 10
@@ -152,14 +154,14 @@ TestCase {
}
function test_overshoot() {
- var container = createTemporaryObject(flickable, testCase)
+ let container = createTemporaryObject(flickable, testCase)
verify(container)
waitForRendering(container)
- var vertical = scrollIndicator.createObject(container, {size: 0.5})
+ let vertical = scrollIndicator.createObject(container, {size: 0.5})
container.ScrollIndicator.vertical = vertical
- var horizontal = scrollIndicator.createObject(container, {size: 0.5})
+ let horizontal = scrollIndicator.createObject(container, {size: 0.5})
container.ScrollIndicator.horizontal = horizontal
// negative vertical overshoot (pos < 0)
@@ -184,7 +186,7 @@ TestCase {
}
function test_orientation() {
- var control = createTemporaryObject(scrollIndicator, testCase)
+ let control = createTemporaryObject(scrollIndicator, testCase)
verify(control)
compare(control.orientation, Qt.Vertical)
@@ -199,10 +201,10 @@ TestCase {
// QTBUG-61785
function test_mouseArea() {
- var ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
+ let ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
verify(ma)
- var control = scrollIndicator.createObject(ma, {active: true, size: 0.9, width: testCase.width, height: testCase.height})
+ let control = scrollIndicator.createObject(ma, {active: true, size: 0.9, width: testCase.width, height: testCase.height})
verify(control)
mousePress(control)
@@ -211,7 +213,7 @@ TestCase {
mouseRelease(control)
verify(!ma.pressed)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control).commit()
verify(ma.pressed)
@@ -220,11 +222,11 @@ TestCase {
}
function test_minimumSize() {
- var container = createTemporaryObject(flickable, testCase)
+ let container = createTemporaryObject(flickable, testCase)
verify(container)
waitForRendering(container)
- var vertical = scrollIndicator.createObject(container, {minimumSize: 0.1})
+ let vertical = scrollIndicator.createObject(container, {minimumSize: 0.1})
container.ScrollIndicator.vertical = vertical
compare(container.visibleArea.heightRatio, 0.5)
@@ -268,7 +270,7 @@ TestCase {
}
function test_resize() {
- var vertical = createTemporaryObject(scrollIndicator, testCase, { height:200, orientation: Qt.Vertical, size: 0.5, position: 0.5 })
+ let vertical = createTemporaryObject(scrollIndicator, testCase, { height:200, orientation: Qt.Vertical, size: 0.5, position: 0.5 })
verify(vertical)
vertical.size = 0.8
@@ -279,7 +281,7 @@ TestCase {
compare(vertical.visualPosition, 0.2)
- var horizontal = createTemporaryObject(scrollIndicator, testCase, { width:200, orientation: Qt.Horizontal, size: 0.5, position: 0.5 })
+ let horizontal = createTemporaryObject(scrollIndicator, testCase, { width:200, orientation: Qt.Horizontal, size: 0.5, position: 0.5 })
verify(horizontal)
horizontal.size = 0.8
diff --git a/tests/auto/quickcontrols/controls/data/tst_scrollview.qml b/tests/auto/quickcontrols/controls/data/tst_scrollview.qml
index e5600830a8..622f341f78 100644
--- a/tests/auto/quickcontrols/controls/data/tst_scrollview.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_scrollview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -146,6 +146,7 @@ TestCase {
}
}
}
+
Component {
id: scrollableTextAreaWithSibling
ScrollView {
@@ -156,21 +157,23 @@ TestCase {
}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(scrollView, testCase)
verify(control)
}
function test_scrollBars() {
- var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200})
+ let control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200})
verify(control)
- var vertical = control.ScrollBar.vertical
+ let vertical = control.ScrollBar.vertical
verify(vertical)
- var horizontal = control.ScrollBar.horizontal
+ let horizontal = control.ScrollBar.horizontal
verify(horizontal)
control.contentHeight = 400
@@ -188,6 +191,18 @@ TestCase {
horizontal.increase()
verify(horizontal.position > 0)
compare(control.contentItem.visibleArea.xPosition, horizontal.position)
+
+ vertical.policy = ScrollBar.AlwaysOn
+ horizontal.policy = ScrollBar.AlwaysOn
+
+ verify(control.effectiveScrollBarWidth > 0)
+ verify(control.effectiveScrollBarHeight > 0)
+
+ vertical.policy = ScrollBar.AlwaysOff
+ horizontal.policy = ScrollBar.AlwaysOff
+
+ compare(control.effectiveScrollBarWidth, 0)
+ compare(control.effectiveScrollBarHeight, 0)
}
function test_oneChild_data() {
@@ -198,14 +213,14 @@ TestCase {
}
function test_oneChild(data) {
- var control = createTemporaryObject(data.component, testCase)
+ let control = createTemporaryObject(data.component, testCase)
verify(control)
- var flickable = control.contentItem
+ let flickable = control.contentItem
verify(flickable.hasOwnProperty("contentX"))
verify(flickable.hasOwnProperty("contentY"))
- var label = flickable.contentItem.children[0]
+ let label = flickable.contentItem.children[0]
compare(label.text, "ABC")
compare(control.implicitWidth, label.implicitWidth)
@@ -229,34 +244,34 @@ TestCase {
}
function test_multipleChildren() {
- var control = createTemporaryObject(scrollableLabels, testCase)
+ let control = createTemporaryObject(scrollableLabels, testCase)
verify(control)
- var flickable = control.contentItem
+ let flickable = control.contentItem
verify(flickable.hasOwnProperty("contentX"))
verify(flickable.hasOwnProperty("contentY"))
compare(control.contentChildren, flickable.contentItem.children)
- var label1 = control.contentChildren[0]
+ let label1 = control.contentChildren[0]
compare(label1.text, "First")
- var label2 = control.contentChildren[1]
+ let label2 = control.contentChildren[1]
compare(label2.text, "Second")
- var label3 = control.contentChildren[2]
+ let label3 = control.contentChildren[2]
compare(label3.text, "Third")
- var expectedContentHeight = label1.implicitHeight + label2.implicitHeight + label3.implicitHeight
+ let expectedContentHeight = label1.implicitHeight + label2.implicitHeight + label3.implicitHeight
compare(control.contentHeight, expectedContentHeight)
compare(flickable.contentHeight, expectedContentHeight)
}
function test_listView() {
- var control = createTemporaryObject(scrollableListView, testCase)
+ let control = createTemporaryObject(scrollableListView, testCase)
verify(control)
- var listview = control.contentItem
+ let listview = control.contentItem
verify(listview.hasOwnProperty("contentX"))
verify(listview.hasOwnProperty("contentY"))
verify(listview.hasOwnProperty("model"))
@@ -272,10 +287,10 @@ TestCase {
// children, even if the flickable has an empty or negative content
// size. Some flickables (e.g ListView) sets a negative
// contentWidth on purpose, which should be respected.
- var scrollview = createTemporaryObject(scrollableFlickable, testCase)
+ let scrollview = createTemporaryObject(scrollableFlickable, testCase)
verify(scrollview)
- var flickable = scrollview.contentItem
+ let flickable = scrollview.contentItem
verify(flickable.hasOwnProperty("contentX"))
verify(flickable.hasOwnProperty("contentY"))
@@ -290,10 +305,10 @@ TestCase {
// not the flickable, then those values will be forwarded and used
// by the flickable (rather than trying to calculate the content size
// based on the flickables children).
- var scrollview = createTemporaryObject(scrollableWithContentSize, testCase)
+ let scrollview = createTemporaryObject(scrollableWithContentSize, testCase)
verify(scrollview)
- var flickable = scrollview.contentItem
+ let flickable = scrollview.contentItem
verify(flickable.hasOwnProperty("contentX"))
verify(flickable.hasOwnProperty("contentY"))
@@ -307,10 +322,10 @@ TestCase {
// Check that if both the scrollview and the flickable has
// contentWidth/Height set (which is an inconsistency/fault
// by the app), the content size of the scrollview wins.
- var scrollview = createTemporaryObject(scrollableAndFlicableWithContentSize, testCase)
+ let scrollview = createTemporaryObject(scrollableAndFlicableWithContentSize, testCase)
verify(scrollview)
- var flickable = scrollview.contentItem
+ let flickable = scrollview.contentItem
verify(flickable.hasOwnProperty("contentX"))
verify(flickable.hasOwnProperty("contentY"))
@@ -321,14 +336,14 @@ TestCase {
}
function test_flickableWithExplicitContentSize() {
- var control = createTemporaryObject(emptyFlickable, testCase)
+ let control = createTemporaryObject(emptyFlickable, testCase)
verify(control)
- var flickable = control.contentItem
+ let flickable = control.contentItem
verify(flickable.hasOwnProperty("contentX"))
verify(flickable.hasOwnProperty("contentY"))
- var flickableContentSize = 1000;
+ let flickableContentSize = 1000;
flickable.contentWidth = flickableContentSize;
flickable.contentHeight = flickableContentSize;
@@ -354,13 +369,13 @@ TestCase {
}
function test_mouse() {
- var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400})
+ let control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400})
verify(control)
mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton)
compare(control.contentItem.contentY, 0)
- for (var y = control.height / 2; y >= 0; --y) {
+ for (let y = control.height / 2; y >= 0; --y) {
mouseMove(control, control.width / 2, y, 10)
compare(control.contentItem.contentY, 0)
}
@@ -370,10 +385,10 @@ TestCase {
}
function test_hover() {
- var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400})
+ let control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400})
verify(control)
- var vertical = control.ScrollBar.vertical
+ let vertical = control.ScrollBar.vertical
verify(vertical)
vertical.hoverEnabled = true
@@ -385,10 +400,10 @@ TestCase {
}
function test_wheel() {
- var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400})
+ let control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400})
verify(control)
- var vertical = control.ScrollBar.vertical
+ let vertical = control.ScrollBar.vertical
verify(vertical)
mouseWheel(control, control.width / 2, control.height / 2, 0, -120)
@@ -398,21 +413,21 @@ TestCase {
}
function test_touch() {
- var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400})
+ let control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400})
verify(control)
- var vertical = control.ScrollBar.vertical
+ let vertical = control.ScrollBar.vertical
verify(vertical)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control, control.width / 2, control.height / 2).commit()
compare(control.contentItem.contentY, 0)
compare(vertical.active, false)
compare(vertical.interactive, false)
- var maxContentY = 0
- for (var y = control.height / 2; y >= 0; --y) {
+ let maxContentY = 0
+ for (let y = control.height / 2; y >= 0; --y) {
touch.move(0, control, control.width / 2, y).commit()
maxContentY = Math.max(maxContentY, control.contentItem.contentY)
}
@@ -425,7 +440,7 @@ TestCase {
}
function test_keys() {
- var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentWidth: 400, contentHeight: 400})
+ let control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentWidth: 400, contentHeight: 400})
verify(control)
// If the viewport is smaller than the size of the ScrollView
// (like windows style does due to its opaque scrollbars),
@@ -436,31 +451,31 @@ TestCase {
control.forceActiveFocus()
verify(control.activeFocus)
- var vertical = control.ScrollBar.vertical
+ let vertical = control.ScrollBar.vertical
verify(vertical)
compare(vertical.position, 0.0)
- for (var i = 1; i <= 10; ++i) {
+ for (let i = 1; i <= 10; ++i) {
keyClick(Qt.Key_Down)
compare(vertical.position, Math.min(0.5, i * 0.1))
}
compare(vertical.position, 0.5)
- for (i = 1; i <= 10; ++i) {
+ for (let i = 1; i <= 10; ++i) {
keyClick(Qt.Key_Up)
compare(vertical.position, Math.max(0.0, 0.5 - i * 0.1))
}
compare(vertical.position, 0.0)
- var horizontal = control.ScrollBar.horizontal
+ let horizontal = control.ScrollBar.horizontal
verify(horizontal)
compare(horizontal.position, 0.0)
- for (i = 1; i <= 10; ++i) {
+ for (let i = 1; i <= 10; ++i) {
keyClick(Qt.Key_Right)
compare(horizontal.position, Math.min(0.5, i * 0.1))
}
compare(horizontal.position, 0.5)
- for (i = 1; i <= 10; ++i) {
+ for (let i = 1; i <= 10; ++i) {
keyClick(Qt.Key_Left)
compare(horizontal.position, Math.max(0.0, 0.5 - i * 0.1))
}
@@ -469,22 +484,49 @@ TestCase {
function test_textArea() {
// TODO: verify no binding loop warnings (QTBUG-62325)
- var control = createTemporaryObject(scrollableTextArea, testCase)
+ let control = createTemporaryObject(scrollableTextArea, testCase)
verify(control)
- var flickable = control.contentItem
+ let flickable = control.contentItem
verify(flickable && flickable.hasOwnProperty("contentX"))
- var textArea = flickable.contentItem.children[0]
+ let textArea = flickable.contentItem.children[0]
verify(textArea && textArea.hasOwnProperty("text"))
compare(control.contentWidth, flickable.contentWidth)
compare(control.contentHeight, flickable.contentHeight)
}
+ Component {
+ id: scrollableTextAreaWithPadding
+
+ ScrollView {
+ TextArea {
+ text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas id dignissim ipsum. Nam molestie nisl turpis."
+ wrapMode: TextArea.WordWrap
+ leftPadding: 1
+ topPadding: 1
+ }
+ }
+ }
+
+ function test_textAreaWithPadding() {
+ let control = createTemporaryObject(scrollableTextAreaWithPadding, testCase)
+ verify(control)
+
+ let flickable = control.contentItem
+ verify(flickable)
+
+ let textArea = flickable.contentItem.children[0]
+ verify(textArea)
+
+ compare(control.contentWidth, flickable.contentWidth)
+ compare(control.contentHeight, flickable.contentHeight)
+ }
+
function test_textAreaWithSibling() {
// Checks that it does not crash when the ScrollView is deleted
- var control = createTemporaryObject(scrollableTextAreaWithSibling, testCase)
+ let control = createTemporaryObject(scrollableTextAreaWithSibling, testCase)
verify(control)
}
@@ -590,6 +632,7 @@ TestCase {
id: bindingToContentItemAndStandaloneFlickable
Item {
+ objectName: "container"
width: 200
height: 200
@@ -628,7 +671,7 @@ TestCase {
verify(verticalScrollBar.visible)
verify(horizontalScrollBar.visible)
- mouseDrag(verticalScrollBar, verticalScrollBar.width / 2, verticalScrollBar.height / 2, 0, 50)
+ mouseWheel(control, control.width / 2, control.height / 2, 0, -120)
verify(verticalScrollBar.active)
verify(horizontalScrollBar.active)
}
@@ -637,6 +680,7 @@ TestCase {
id: contentItemAssignedImperatively
Item {
+ objectName: "container"
width: 100
height: 100
@@ -644,6 +688,7 @@ TestCase {
ListView {
id: listView
+ objectName: "customListView"
model: 20
delegate: Text {
text: modelData
@@ -662,11 +707,59 @@ TestCase {
}
// Tests that a ListView declared before the ScrollView (as the QObject destruction order
- // is relevant for the bug) and assigned imperatively to ScrollView does not cause a crash
- // on exit.
+ // is relevant for the bug) and assigned imperatively to ScrollView does not cause:
+ // - a crash on exit
+ // - scroll bars that should be hidden to be visible
function test_contentItemAssignedImperatively() {
let root = createTemporaryObject(contentItemAssignedImperatively, testCase)
verify(root)
+
+ let control = root.scrollView
+ let flickable = control.contentItem
+ compare(flickable.parent, control)
+
+ let horizontalScrollBar = control.ScrollBar.horizontal
+ let verticalScrollBar = control.ScrollBar.vertical
+ // The horizontal ScrollBar's policy is set to AlwaysOff, so it shouldn't ever be visible.
+ verify(!horizontalScrollBar.visible)
+ // The vertical ScrollBar should be visible...
+ verify(verticalScrollBar.visible)
+
+ // ... and it should become active when the ScrollView is scrolled.
+ mouseWheel(control, control.width / 2, control.height / 2, 0, -120)
+ verify(verticalScrollBar.active)
+
// Shouldn't crash.
}
+
+ Component {
+ id: scrollViewContentItemComp
+
+ ScrollView {
+ id: scrollView
+ anchors.fill: parent
+ Column {
+ width: parent.width
+ Repeater {
+ model: 20
+ Rectangle {
+ width: scrollView.width
+ height: 60
+ color: (index % 2 == 0) ? "red" : "green"
+ }
+ }
+ }
+ }
+ }
+
+ function test_scrollViewContentItemSize() {
+ let scrollview = createTemporaryObject(scrollViewContentItemComp, testCase)
+ verify(scrollview)
+ let contentItem = scrollview.contentItem
+ waitForRendering(contentItem)
+ compare(contentItem.contentWidth, 400)
+ compare(contentItem.contentHeight, 1200)
+ compare(scrollview.contentWidth, 400)
+ compare(scrollview.contentHeight, 1200)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml b/tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml
index dd4f984aa2..879e8c68d4 100644
--- a/tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -129,13 +129,55 @@ TestCase {
}
Component {
+ id: headerviewComp
+ HorizontalHeaderView {
+ id: headerView
+ clip: true
+ anchors.fill: parent
+
+ model: TableModel {
+ TableModelColumn { display: "c1" }
+ TableModelColumn { display: "c2" }
+ TableModelColumn { display: "c3" }
+ TableModelColumn { display: "c4" }
+ TableModelColumn { display: "c5" }
+ TableModelColumn { display: "c6" }
+ TableModelColumn { display: "c7" }
+ TableModelColumn { display: "c8" }
+ rows: [
+ { "c1": "v1", "c2":"v2", "c3":"v3", "c4": "v4", "c5": "v5", "c6":"v6", "c7":"v7", "c8": "v8" }
+ ]
+ }
+
+ delegate: Rectangle {
+ required property bool selected
+ implicitWidth: cellWidth
+ implicitHeight: cellHeight
+ color: selected ? "lightblue" : "gray"
+ Text { text: row + "," + column }
+ }
+
+ selectionModel: ItemSelectionModel { }
+
+ property alias selectionRectangle: selectionRectangle
+ SelectionRectangle {
+ id: selectionRectangle
+ target: headerView
+ }
+ }
+
+ }
+
+ Component {
id: signalSpy
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(defaultSelectionRectangle, testCase)
verify(control)
}
@@ -195,13 +237,15 @@ TestCase {
function test_drag_data() {
return [
- { tag: "resize enabled", resizeEnabled: true },
- { tag: "resize disabled", resizeEnabled: false },
+ { tag: "resize enabled (tableview)", resizeEnabled: true, viewComp: tableviewComp },
+ { tag: "resize disabled (tableview)", resizeEnabled: false, viewComp: tableviewComp },
+ { tag: "resize enabled (headerview)", resizeEnabled: true, viewComp: headerviewComp },
+ { tag: "resize disabled (headerview)", resizeEnabled: false, viewComp: headerviewComp },
]
}
function test_drag(data) {
- let tableView = createTemporaryObject(tableviewComp, testCase)
+ let tableView = createTemporaryObject(data.viewComp, testCase)
verify(tableView)
let selectionRectangle = tableView.selectionRectangle
verify(selectionRectangle)
@@ -222,8 +266,8 @@ TestCase {
mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
verify(tableView.selectionModel.hasSelection)
compare(tableView.selectionModel.selectedIndexes.length, 2)
- verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
- verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ verify(tableView.selectionModel.isSelected(tableView.selectionModel.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.selectionModel.model.index(0, 1)))
compare(activeSpy.count, 1)
compare(draggingSpy.count, 2)
@@ -236,8 +280,218 @@ TestCase {
// Ensure that a press and hold doesn't start a selection
mousePress(tableView, 1, 1, Qt.LeftButton)
- mousePress(tableView, 1, 1, Qt.LeftButton, Qt.NoModifier, 1000)
+ mouseRelease(tableView, 1, 1, Qt.LeftButton, Qt.NoModifier, 1000)
+ verify(!tableView.selectionModel.hasSelection)
+ }
+
+ function test_tableView_singleSelection_data() {
+ return [
+ { viewComp: tableviewComp },
+ { viewComp: headerviewComp },
+ ]
+ }
+
+ function test_tableView_singleSelection(data) {
+ let tableView = createTemporaryObject(data.viewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+ tableView.selectionMode = TableView.SingleSelection
+
+ // Try to select two cells by dragging. Only one cell should be selected.
+ verify(!tableView.selectionModel.hasSelection)
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ verify(tableView.selectionModel.hasSelection)
+ compare(tableView.selectionModel.selectedIndexes.length, 1)
+ verify(tableView.selectionModel.isSelected(tableView.selectionModel.model.index(0, 0)))
+
+ // A control click should clear the current selection and select a new cell
+ mouseClick(tableView, (cellWidth * 2) - 1, 1, Qt.LeftButton, Qt.ControlModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 1)
+ verify(tableView.selectionModel.isSelected(tableView.selectionModel.model.index(0, 1)))
+
+ // A shift click is a no-op, and doesn't change the current selection
+ mouseClick(tableView, 1, 1, Qt.LeftButton, Qt.ShiftModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 1)
+ verify(tableView.selectionModel.isSelected(tableView.selectionModel.model.index(0, 1)))
+ }
+
+ function test_tableView_contiguousSelection_data() {
+ return [
+ { startFromCurrentIndex: false },
+ { startFromCurrentIndex: true },
+ ]
+ }
+
+ function test_tableView_contiguousSelection(data) {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+ tableView.selectionMode = TableView.ContiguousSelection
+
+ if (data.startFromCurrentIndex) {
+ // Click on a cell to set current index, but set no selection.
+ // A later shift-click should then start a new selection from the
+ // current cell.
+ mouseClick(tableView, 1, 1, Qt.LeftButton, Qt.NoModifier)
+ verify(!tableView.selectionModel.hasSelection)
+ compare(tableView.selectionModel.currentIndex, tableView.index(0, 0))
+ } else {
+ // Start a new selection by dragging on two cells
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ verify(tableView.selectionModel.hasSelection)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ }
+
+ // A shift click should extend the selection
+ mouseClick(tableView, (cellWidth * 4) - 3, 1, Qt.LeftButton, Qt.ShiftModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 4)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 2)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 3)))
+
+ // A shift click closer to the first selected cell should shrink it again
+ mouseClick(tableView, (cellWidth * 3) - 2, 1, Qt.LeftButton, Qt.ShiftModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 3)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 2)))
+
+ // A control click should clear the selection, and select a new cell
+ mouseClick(tableView, 1, (cellHeight * 2) - 1, Qt.LeftButton, Qt.ControlModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 1)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(1, 0)))
+
+ // A control drag should clear the selection, and select new cells
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton, Qt.ControlModifier)
+ verify(tableView.selectionModel.hasSelection)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ }
+
+ function test_tableView_extendedSelection() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+ // ExtendedSelection should be the default selection mode
+ compare(tableView.selectionMode, TableView.ExtendedSelection)
+
+ // Select two cells by dragging
verify(!tableView.selectionModel.hasSelection)
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ verify(tableView.selectionModel.hasSelection)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+
+ // A shift click should extend the selection
+ mouseClick(tableView, (cellWidth * 3) - 2, 1, Qt.LeftButton, Qt.ShiftModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 3)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 2)))
+
+ // A control click should add a new cell to the selection
+ mouseClick(tableView, 1, (cellHeight * 2) - 1, Qt.LeftButton, Qt.ControlModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 4)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 2)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(1, 0)))
+
+ // A shift click should further extend the selection from the last cell selected
+ mouseClick(tableView, (cellWidth * 3) - 2, (cellHeight * 2) - 1, Qt.LeftButton, Qt.ShiftModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 6)
+ for (let r = 0; r < 2; ++r)
+ for (let c = 0; c < 3; ++c)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(r, c)))
+
+ // Shift click the second selection so that it overlaps with the first
+ mouseClick(tableView, 1, 1, Qt.LeftButton, Qt.ShiftModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 4)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 2)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(1, 0)))
+
+ // Shift click the selection back again. The first selection on
+ // row 0 should still be present, even if the second selection
+ // no longer overlaps it.
+ mouseClick(tableView, (cellWidth * 3) - 2, (cellHeight * 2) - 1, Qt.LeftButton, Qt.ShiftModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 6)
+ for (let r = 0; r < 2; ++r)
+ for (let c = 0; c < 3; ++c)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(r, c)))
+ }
+
+ function test_unselect_click() {
+ // Check at a ctrl click on top a selected cell
+ // will cause the cell to be unselected.
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ // Select some cells
+ tableView.selectionModel.select(tableView.index(0, 0), ItemSelectionModel.Select)
+ tableView.selectionModel.select(tableView.index(0, 1), ItemSelectionModel.Select)
+ tableView.selectionModel.select(tableView.index(0, 3), ItemSelectionModel.Select)
+ tableView.selectionModel.select(tableView.index(1, 0), ItemSelectionModel.Select)
+ compare(tableView.selectionModel.selectedIndexes.length, 4)
+
+ // Do a ctrl-click on top of a selected cell. This
+ // should cause the cell to be unselected.
+ mouseClick(tableView, cellWidth / 2, cellHeight / 2, Qt.LeftButton, Qt.ControlModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 3)
+ verify(!tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 3)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(1, 0)))
+ }
+
+ function test_unselect_drag() {
+ // Check at a ctrl drag on top a selected cell
+ // will cause the dragged-over cells to be unselected.
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ // Select some cells
+ tableView.selectionModel.select(tableView.index(0, 0), ItemSelectionModel.Select)
+ tableView.selectionModel.select(tableView.index(0, 1), ItemSelectionModel.Select)
+ tableView.selectionModel.select(tableView.index(0, 3), ItemSelectionModel.Select)
+ tableView.selectionModel.select(tableView.index(1, 0), ItemSelectionModel.Select)
+ compare(tableView.selectionModel.selectedIndexes.length, 4)
+
+ // Do a ctrl-drag on top of the selected cells. This
+ // should cause all the cells to be unselected.
+ mousePress(tableView.contentItem, cellWidth / 2, cellHeight / 2, Qt.LeftButton, Qt.ControlModifier)
+ mouseMove(tableView.contentItem, cellWidth * 4, cellHeight * 2, 0, Qt.LeftButton, Qt.ControlModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 0)
+
+ // Move the mouse back to cell 2, and release the mouse. Only
+ // the top left cells will then be unselected
+ mouseMove(tableView.contentItem, (cellWidth * 2) - 1, (cellHeight * 2) - 1, 0, Qt.LeftButton, Qt.ControlModifier)
+ mouseRelease(tableView.contentItem, (cellWidth * 2) - 1, (cellHeight * 2) - 1, Qt.LeftButton, Qt.ControlModifier)
+ compare(tableView.selectionModel.selectedIndexes.length, 1)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 3)))
}
function test_handle_position() {
@@ -337,8 +591,8 @@ TestCase {
// verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
// verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
-// // Hold down shift, and drag again to do a multi-selection
-// mouseDrag(tableView, 1, cellHeight + 5, (cellWidth * 2) - 2, 1, Qt.LeftButton, Qt.ShiftModifier)
+// // Hold down control, and drag again to do a multi-selection
+// mouseDrag(tableView, 1, cellHeight + 5, (cellWidth * 2) - 2, 1, Qt.LeftButton, Qt.ControlModifier)
// verify(tableView.selectionModel.hasSelection)
// compare(tableView.selectionModel.selectedIndexes.length, 4)
// verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
@@ -375,7 +629,7 @@ TestCase {
verify(!tableView.selectionModel.hasSelection)
// Do a press and hold
mousePress(tableView, 1, 1, Qt.LeftButton)
- mousePress(tableView, 1, 1, Qt.LeftButton, Qt.NoModifier, 1000)
+ mouseRelease(tableView, 1, 1, Qt.LeftButton, Qt.NoModifier, 1000)
verify(tableView.selectionModel.hasSelection)
compare(tableView.selectionModel.selectedIndexes.length, 1)
verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
@@ -384,7 +638,7 @@ TestCase {
compare(activeSpy.count, 1)
// Remove selection
- mouseClick(tableView, 1, 1, Qt.LeftButton)
+ mouseClick(tableView, 100, 100, Qt.LeftButton)
verify(!tableView.selectionModel.hasSelection)
compare(draggingSpy.count, 0)
compare(activeSpy.count, 2)
@@ -394,6 +648,31 @@ TestCase {
verify(!tableView.selectionModel.hasSelection)
}
+ function test_pressAndHoldPlussShift() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ verify(!tableView.selectionModel.hasSelection)
+ verify(!tableView.selectionModel.currentIndex.isValid)
+
+ // select cell 0,0
+ mouseClick(tableView, 1, 1, Qt.LeftButton)
+ compare(tableView.selectionModel.currentIndex, tableView.index(0, 0))
+
+ // do a long press on cell 1,0 while holding down Shift. This will
+ // select both cells.
+ mousePress(tableView, cellWidth + 1, 1, Qt.LeftButton, Qt.ShiftModifier)
+ mouseRelease(tableView, cellWidth + 1, 1, Qt.LeftButton, Qt.ShiftModifier, 2000)
+ verify(tableView.selectionModel.hasSelection)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+ }
+
function test_pressAndHold_on_top_of_handle() {
let tableView = createTemporaryObject(tableviewComp, testCase)
verify(tableView)
@@ -439,8 +718,8 @@ TestCase {
// Select four cells in the middle
mouseDrag(tableView, cellWidth + 1, cellHeight + 1, (cellWidth * 2) - 2, (cellHeight * 2) - 2, Qt.LeftButton)
compare(tableView.selectionModel.selectedIndexes.length, 4)
- for (var x = 1; x < 3; ++x) {
- for (var y = 1; y < 3; ++y) {
+ for (let x = 1; x < 3; ++x) {
+ for (let y = 1; y < 3; ++y) {
verify(tableView.selectionModel.isSelected(tableView.model.index(x, y)))
}
}
@@ -448,8 +727,8 @@ TestCase {
// Drag on the top left handle, so that the selection extends to cell 0, 0
mouseDrag(tableView, cellWidth, cellHeight, -cellWidth / 2, -cellHeight / 2, Qt.LeftButton)
compare(tableView.selectionModel.selectedIndexes.length, 9)
- for (x = 0; x < 3; ++x) {
- for (y = 0; y < 3; ++y) {
+ for (let x = 0; x < 3; ++x) {
+ for (let y = 0; y < 3; ++y) {
verify(tableView.selectionModel.isSelected(tableView.model.index(x, y)))
}
}
@@ -467,8 +746,8 @@ TestCase {
// Select four cells in the middle
mouseDrag(tableView, cellWidth + 1, cellHeight + 1, (cellWidth * 2) - 2, (cellHeight * 2) - 2, Qt.LeftButton)
compare(tableView.selectionModel.selectedIndexes.length, 4)
- for (var x = 1; x < 3; ++x) {
- for (var y = 1; y < 3; ++y) {
+ for (let x = 1; x < 3; ++x) {
+ for (let y = 1; y < 3; ++y) {
verify(tableView.selectionModel.isSelected(tableView.model.index(x, y)))
}
}
@@ -491,8 +770,8 @@ TestCase {
// Select four cells in the middle
mouseDrag(tableView, cellWidth + 1, cellHeight + 1, (cellWidth * 2) - 2, (cellHeight * 2) - 2, Qt.LeftButton)
compare(tableView.selectionModel.selectedIndexes.length, 4)
- for (var x = 1; x < 3; ++x) {
- for (var y = 1; y < 3; ++y) {
+ for (let x = 1; x < 3; ++x) {
+ for (let y = 1; y < 3; ++y) {
verify(tableView.selectionModel.isSelected(tableView.model.index(x, y)))
}
}
@@ -500,11 +779,59 @@ TestCase {
// Drag on the bottom right handle, so that the selection expands to cell 9 cells
mouseDrag(tableView, cellWidth * 3, cellHeight * 3, 10, 10, Qt.LeftButton)
compare(tableView.selectionModel.selectedIndexes.length, 9)
- for (x = 1; x < 4; ++x) {
- for (y = 1; y < 4; ++y) {
+ for (let x = 1; x < 4; ++x) {
+ for (let y = 1; y < 4; ++y) {
verify(tableView.selectionModel.isSelected(tableView.model.index(x, y)))
}
}
}
+ function test_programmatic_unselect() {
+ // Check that the SelectionRectangle will be deactivated if the
+ // selection is changed programatically.
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ verify(!tableView.selectionModel.hasSelection)
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(selectionRectangle.active)
+
+ tableView.selectionModel.clearSelection()
+ verify(!selectionRectangle.active)
+ }
+
+ function test_extend_using_keyboard() {
+ // Check that the bottom-right selection handle will move if an
+ // acitve selection is extended with the keyboard
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.bottomRightHandle = bottomRightHandleComp
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ tableView.forceActiveFocus()
+ verify(!tableView.selectionModel.hasSelection)
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(selectionRectangle.active)
+ verify(bottomRightHandle)
+ compare(bottomRightHandle.x, (cellWidth * 2) - (bottomRightHandle.width / 2))
+ compare(bottomRightHandle.y, cellHeight - (bottomRightHandle.height / 2))
+
+ keyPress(Qt.Key_Down, Qt.ShiftModifier)
+ keyRelease(Qt.Key_Down, Qt.ShiftModifier)
+ keyPress(Qt.Key_Right, Qt.ShiftModifier)
+ keyRelease(Qt.Key_Right, Qt.ShiftModifier)
+ verify(selectionRectangle.active)
+ compare(tableView.selectionModel.selectedIndexes.length, 6)
+ compare(bottomRightHandle.x, (cellWidth * 3) - (bottomRightHandle.width / 2))
+ compare(bottomRightHandle.y, (cellHeight * 2) - (bottomRightHandle.height / 2))
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_slider.qml b/tests/auto/quickcontrols/controls/data/tst_slider.qml
index df3eb71ae2..991d513122 100644
--- a/tests/auto/quickcontrols/controls/data/tst_slider.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -23,10 +23,12 @@ TestCase {
Slider { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(slider, testCase)
+ function test_defaults() {
+ let control = createTemporaryObject(slider, testCase)
verify(control)
compare(control.stepSize, 0)
@@ -37,7 +39,7 @@ TestCase {
}
function test_value() {
- var control = createTemporaryObject(slider, testCase)
+ let control = createTemporaryObject(slider, testCase)
verify(control)
compare(control.value, 0.0)
@@ -52,7 +54,7 @@ TestCase {
}
function test_range() {
- var control = createTemporaryObject(slider, testCase, {from: 0, to: 100, value: 50})
+ let control = createTemporaryObject(slider, testCase, {from: 0, to: 100, value: 50})
verify(control)
compare(control.from, 0)
@@ -84,7 +86,7 @@ TestCase {
}
function test_inverted() {
- var control = createTemporaryObject(slider, testCase, {from: 1.0, to: -1.0})
+ let control = createTemporaryObject(slider, testCase, {from: 1.0, to: -1.0})
verify(control)
compare(control.from, 1.0)
@@ -106,7 +108,7 @@ TestCase {
}
function test_position() {
- var control = createTemporaryObject(slider, testCase)
+ let control = createTemporaryObject(slider, testCase)
verify(control)
compare(control.value, 0.0)
@@ -122,7 +124,7 @@ TestCase {
}
function test_visualPosition() {
- var control = createTemporaryObject(slider, testCase)
+ let control = createTemporaryObject(slider, testCase)
verify(control)
compare(control.value, 0.0)
@@ -154,7 +156,7 @@ TestCase {
}
function test_orientation() {
- var control = createTemporaryObject(slider, testCase)
+ let control = createTemporaryObject(slider, testCase)
verify(control)
compare(control.orientation, Qt.Horizontal)
@@ -179,16 +181,16 @@ TestCase {
}
function test_mouse(data) {
- var control = createTemporaryObject(slider, testCase, {orientation: data.orientation, live: data.live})
+ let control = createTemporaryObject(slider, testCase, {orientation: data.orientation, live: data.live})
verify(control)
- var pressedCount = 0
- var movedCount = 0
+ let pressedCount = 0
+ let movedCount = 0
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
verify(pressedSpy.valid)
- var movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
+ let movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
verify(movedSpy.valid)
mousePress(control, 0, control.height - 1, Qt.LeftButton)
@@ -268,19 +270,19 @@ TestCase {
}
function test_touch(data) {
- var control = createTemporaryObject(slider, testCase, {orientation: data.orientation, live: data.live})
+ let control = createTemporaryObject(slider, testCase, {orientation: data.orientation, live: data.live})
verify(control)
- var pressedCount = 0
- var movedCount = 0
+ let pressedCount = 0
+ let movedCount = 0
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
verify(pressedSpy.valid)
- var movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
+ let movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
verify(movedSpy.valid)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control, 0, 0).commit()
compare(pressedSpy.count, ++pressedCount)
compare(movedSpy.count, movedCount)
@@ -349,19 +351,19 @@ TestCase {
}
function test_multiTouch() {
- var control1 = createTemporaryObject(slider, testCase, {live: false})
+ let control1 = createTemporaryObject(slider, testCase, {live: false})
verify(control1)
- var pressedCount1 = 0
- var movedCount1 = 0
+ let pressedCount1 = 0
+ let movedCount1 = 0
- var pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"})
+ let pressedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "pressedChanged"})
verify(pressedSpy1.valid)
- var movedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "moved"})
+ let movedSpy1 = signalSpy.createObject(control1, {target: control1, signalName: "moved"})
verify(movedSpy1.valid)
- var touch = touchEvent(control1)
+ let touch = touchEvent(control1)
touch.press(0, control1, 0, 0).commit().move(0, control1, control1.width, control1.height).commit()
compare(pressedSpy1.count, ++pressedCount1)
@@ -379,16 +381,16 @@ TestCase {
compare(control1.pressed, true)
compare(control1.position, 1.0)
- var control2 = createTemporaryObject(slider, testCase, {y: control1.height, live: false})
+ let control2 = createTemporaryObject(slider, testCase, {y: control1.height, live: false})
verify(control2)
- var pressedCount2 = 0
- var movedCount2 = 0
+ let pressedCount2 = 0
+ let movedCount2 = 0
- var pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"})
+ let pressedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "pressedChanged"})
verify(pressedSpy2.valid)
- var movedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "moved"})
+ let movedSpy2 = signalSpy.createObject(control2, {target: control2, signalName: "moved"})
verify(movedSpy2.valid)
// press the second slider
@@ -443,25 +445,25 @@ TestCase {
}
function test_keys(data) {
- var control = createTemporaryObject(slider, testCase, {orientation: data.orientation})
+ let control = createTemporaryObject(slider, testCase, {orientation: data.orientation})
verify(control)
- var pressedCount = 0
- var movedCount = 0
+ let pressedCount = 0
+ let movedCount = 0
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
verify(pressedSpy.valid)
- var movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
+ let movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
verify(movedSpy.valid)
control.forceActiveFocus()
verify(control.activeFocus)
- var oldValue = 0.0
+ let oldValue = 0.0
control.value = 0.5
- for (var d1 = 1; d1 <= 10; ++d1) {
+ for (let d1 = 1; d1 <= 10; ++d1) {
oldValue = control.value
keyPress(data.decrease)
compare(control.pressed, true)
@@ -478,7 +480,7 @@ TestCase {
compare(movedSpy.count, movedCount)
}
- for (var i1 = 1; i1 <= 20; ++i1) {
+ for (let i1 = 1; i1 <= 20; ++i1) {
oldValue = control.value
keyPress(data.increase)
compare(control.pressed, true)
@@ -497,7 +499,7 @@ TestCase {
control.stepSize = 0.25
- for (var d2 = 1; d2 <= 10; ++d2) {
+ for (let d2 = 1; d2 <= 10; ++d2) {
oldValue = control.value
keyPress(data.decrease)
compare(control.pressed, true)
@@ -514,7 +516,7 @@ TestCase {
compare(movedSpy.count, movedCount)
}
- for (var i2 = 1; i2 <= 10; ++i2) {
+ for (let i2 = 1; i2 <= 10; ++i2) {
oldValue = control.value
keyPress(data.increase)
compare(control.pressed, true)
@@ -536,10 +538,10 @@ TestCase {
// test with "unbalanced" paddings (left padding != right padding) to ensure
// that the slider position calculation is done taking padding into account
// ==> the position is _not_ 0.5 in the middle of the control
- var control = createTemporaryObject(slider, testCase, {leftPadding: 10, rightPadding: 20, live: false})
+ let control = createTemporaryObject(slider, testCase, {leftPadding: 10, rightPadding: 20, live: false})
verify(control)
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
verify(pressedSpy.valid)
mousePress(control, 0, 0, Qt.LeftButton)
@@ -620,12 +622,12 @@ TestCase {
{ tag: "SnapOnRelease (-1..1)", snapMode: Slider.SnapOnRelease, from: -1, to: 1, values: [0.0, 0.0, -0.8], positions: [immediate ? 0.0 : 0.5, 0.1, 0.1] },
{ tag: "SnapOnRelease (1..-1)", snapMode: Slider.SnapOnRelease, from: 1, to: -1, values: [0.0, 0.0, 0.8], positions: [immediate ? 0.0 : 0.5, 0.1, 0.1] },
// Live
- { tag: "SnapAlwaysLive", snapMode: Slider.SnapAlways, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.6, values: [0, 1, 1], positions: [0, 1, 1] },
- { tag: "SnapAlwaysLive", snapMode: Slider.SnapAlways, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.4, values: [0, 0, 0], positions: [0, 0, 0] },
- { tag: "NoSnapLive", snapMode: Slider.NoSnap, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.6, values: [0, 1, 1], positions: [0, 0.6, 0.6] },
- { tag: "NoSnapLive", snapMode: Slider.NoSnap, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.4, values: [0, 0, 0], positions: [0, 0.4, 0.4] },
- { tag: "SnapOnReleaseLive", snapMode: Slider.SnapOnRelease, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.6, values: [0, 1, 1], positions: [0, 0.6, 1] },
- { tag: "SnapOnReleaseLive", snapMode: Slider.SnapOnRelease, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.4, values: [0, 0, 0], positions: [0, 0.4, 0] },
+ { tag: "SnapAlwaysLive1", snapMode: Slider.SnapAlways, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.6, values: [0, 1, 1], positions: [0, 1, 1] },
+ { tag: "SnapAlwaysLive2", snapMode: Slider.SnapAlways, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.4, values: [0, 0, 0], positions: [0, 0, 0] },
+ { tag: "NoSnapLive1", snapMode: Slider.NoSnap, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.6, values: [0, 1, 1], positions: [0, 0.6, 0.6] },
+ { tag: "NoSnapLive2", snapMode: Slider.NoSnap, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.4, values: [0, 0, 0], positions: [0, 0.4, 0.4] },
+ { tag: "SnapOnReleaseLive1", snapMode: Slider.SnapOnRelease, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.6, values: [0, 1, 1], positions: [0, 0.6, 1] },
+ { tag: "SnapOnReleaseLive2", snapMode: Slider.SnapOnRelease, from: 0, to: 1, value: 0, stepSize: 1, live: true, sliderPos: 0.4, values: [0, 0, 0], positions: [0, 0.4, 0] },
]
}
@@ -635,7 +637,7 @@ TestCase {
let sliderPos = data.sliderPos !== undefined ? data.sliderPos : 0.1
let fuzz = 0.05
- var control = createTemporaryObject(slider, testCase, {live: live, snapMode: data.snapMode, from: data.from, to: data.to, stepSize: stepSize})
+ let control = createTemporaryObject(slider, testCase, {live: live, snapMode: data.snapMode, from: data.from, to: data.to, stepSize: stepSize})
verify(control)
// The test assumes there is no drag threshold for neither mouse or touch.
@@ -644,7 +646,7 @@ TestCase {
// on a very narrow slider, we ensure the same width of all sliders
control.width = testCase.width
- var touch = useMouse ? null : touchEvent(control)
+ let touch = useMouse ? null : touchEvent(control)
if (useMouse)
mousePress(control, calcMousePos(control, 0.0))
@@ -695,11 +697,11 @@ TestCase {
}
function test_wheel(data) {
- var control = createTemporaryObject(slider, testCase, {wheelEnabled: true, orientation: data.orientation})
+ let control = createTemporaryObject(slider, testCase, {wheelEnabled: true, orientation: data.orientation})
verify(control)
- var movedCount = 0
- var movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
+ let movedCount = 0
+ let movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
verify(movedSpy.valid)
compare(control.value, 0.0)
@@ -761,19 +763,19 @@ TestCase {
}
function test_wheelPropagation(data) {
- var mouseArea = createTemporaryObject(mouseAreaComponent, testCase, { width: parent.width, height: parent.height })
+ let mouseArea = createTemporaryObject(mouseAreaComponent, testCase, { width: parent.width, height: parent.height })
verify(mouseArea)
- var mouseAreaWheelSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "wheel" })
+ let mouseAreaWheelSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "wheel" })
verify(mouseAreaWheelSpy.valid)
- var control = createTemporaryObject(slider, mouseArea,
+ let control = createTemporaryObject(slider, mouseArea,
{ wheelEnabled: true, orientation: data.orientation, stepSize: 1 })
verify(control)
compare(control.value, 0.0)
- var movedCount = 0
- var movedSpy = signalSpy.createObject(control, { target: control, signalName: "moved" })
+ let movedCount = 0
+ let movedSpy = signalSpy.createObject(control, { target: control, signalName: "moved" })
verify(movedSpy.valid)
// Scroll the handle to the edge.
@@ -826,7 +828,7 @@ TestCase {
}
function test_nullHandle() {
- var control = createTemporaryObject(slider, testCase)
+ let control = createTemporaryObject(slider, testCase)
verify(control)
control.handle = null
@@ -839,7 +841,7 @@ TestCase {
}
function test_touchDragThreshold_data() {
- var d1 = 3; var d2 = 7;
+ let d1 = 3; let d2 = 7;
return [
{ tag: "horizontal", orientation: Qt.Horizontal, dx1: d1, dy1: 0, dx2: d2, dy2: 0 },
{ tag: "vertical", orientation: Qt.Vertical, dx1: 0, dy1: -d1, dx2: 0, dy2: -d2 },
@@ -849,19 +851,19 @@ TestCase {
}
function test_touchDragThreshold(data) {
- var control = createTemporaryObject(slider, testCase, {touchDragThreshold: 10, live: true, orientation: data.orientation, value: 0.5})
+ let control = createTemporaryObject(slider, testCase, {touchDragThreshold: 10, live: true, orientation: data.orientation, value: 0.5})
verify(control)
compare(control.touchDragThreshold, 10)
- var valueChangedCount = 0
- var valueChangedSpy = signalSpy.createObject(control, {target: control, signalName: "touchDragThresholdChanged"})
+ let valueChangedCount = 0
+ let valueChangedSpy = signalSpy.createObject(control, {target: control, signalName: "touchDragThresholdChanged"})
verify(valueChangedSpy.valid)
control.touchDragThreshold = undefined
compare(control.touchDragThreshold, -1) // reset to -1
compare(valueChangedSpy.count, ++valueChangedCount)
- var t = 5
+ let t = 5
control.touchDragThreshold = t
compare(control.touchDragThreshold, t)
compare(valueChangedSpy.count, ++valueChangedCount)
@@ -870,18 +872,18 @@ TestCase {
compare(control.touchDragThreshold, t)
compare(valueChangedSpy.count, valueChangedCount)
- var pressedCount = 0
- var movedCount = 0
+ let pressedCount = 0
+ let movedCount = 0
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
verify(pressedSpy.valid)
- var movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
+ let movedSpy = signalSpy.createObject(control, {target: control, signalName: "moved"})
verify(movedSpy.valid)
- var touch = touchEvent(control)
- var x0 = control.handle.x + control.handle.width * 0.5
- var y0 = control.handle.y + control.handle.height * 0.5
+ let touch = touchEvent(control)
+ let x0 = control.handle.x + control.handle.width * 0.5
+ let y0 = control.handle.y + control.handle.height * 0.5
touch.press(0, control, x0, y0).commit()
compare(pressedSpy.count, ++pressedCount)
compare(movedSpy.count, movedCount)
@@ -902,9 +904,20 @@ TestCase {
function test_nullTexture() {
failOnWarning("No QSGTexture provided from updateSampledImage(). This is wrong.")
- var control = createTemporaryObject(slider, testCase, {width: -100})
+ let control = createTemporaryObject(slider, testCase, {width: -100})
verify(control)
control.visible = true
waitForRendering(control)
}
+
+ function test_clickFocus() {
+ let control = createTemporaryObject(slider, testCase)
+ verify(control)
+
+ mouseClick(control)
+ if (Qt.platform.os === "osx")
+ verify(!control.activeFocus)
+ else
+ verify(control.activeFocus)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_spinbox.qml b/tests/auto/quickcontrols/controls/data/tst_spinbox.qml
index df30ef4beb..ba27aeed77 100644
--- a/tests/auto/quickcontrols/controls/data/tst_spinbox.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_spinbox.qml
@@ -1,10 +1,11 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
import QtQuick.Controls
import QtQuick.Window
+import QtQuick.Layouts
TestCase {
id: testCase
@@ -29,10 +30,12 @@ TestCase {
MouseArea { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(spinBox, testCase)
+ function test_defaults() {
+ let control = createTemporaryObject(spinBox, testCase)
verify(control)
compare(control.from, 0)
@@ -47,7 +50,7 @@ TestCase {
}
function test_value() {
- var control = createTemporaryObject(spinBox, testCase)
+ let control = createTemporaryObject(spinBox, testCase)
verify(control)
compare(control.value, 0)
@@ -62,7 +65,7 @@ TestCase {
}
function test_range() {
- var control = createTemporaryObject(spinBox, testCase, {from: 0, to: 100, value: 50})
+ let control = createTemporaryObject(spinBox, testCase, {from: 0, to: 100, value: 50})
verify(control)
compare(control.from, 0)
@@ -132,7 +135,7 @@ TestCase {
}
function test_inverted() {
- var control = createTemporaryObject(spinBox, testCase, {from: 100, to: -100})
+ let control = createTemporaryObject(spinBox, testCase, {from: 100, to: -100})
verify(control)
compare(control.from, 100)
@@ -169,16 +172,16 @@ TestCase {
}
function test_mouse(data) {
- var control = createTemporaryObject(spinBox, testCase, {value: data.value})
+ let control = createTemporaryObject(spinBox, testCase, {value: data.value})
verify(control)
- var button = control[data.button]
+ let button = control[data.button]
verify(button)
- var pressedSpy = signalSpy.createObject(control, {target: button, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: button, signalName: "pressedChanged"})
verify(pressedSpy.valid)
- var valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
+ let valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
verify(valueModifiedSpy.valid)
mousePress(button.indicator)
@@ -209,27 +212,27 @@ TestCase {
}
function test_keys(data) {
- var control = createTemporaryObject(spinBox, testCase, data.properties)
+ let control = createTemporaryObject(spinBox, testCase, data.properties)
verify(control)
- var upPressedCount = 0
- var downPressedCount = 0
- var valueModifiedCount = 0
+ let upPressedCount = 0
+ let downPressedCount = 0
+ let valueModifiedCount = 0
- var upPressedSpy = signalSpy.createObject(control, {target: control.up, signalName: "pressedChanged"})
+ let upPressedSpy = signalSpy.createObject(control, {target: control.up, signalName: "pressedChanged"})
verify(upPressedSpy.valid)
- var downPressedSpy = signalSpy.createObject(control, {target: control.down, signalName: "pressedChanged"})
+ let downPressedSpy = signalSpy.createObject(control, {target: control.down, signalName: "pressedChanged"})
verify(downPressedSpy.valid)
- var valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
+ let valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
verify(valueModifiedSpy.valid)
control.forceActiveFocus()
verify(control.activeFocus)
- for (var u = 0; u < data.upSteps.length; ++u) {
- var wasUpEnabled = control.wrap || control.value < control.to
+ for (let u = 0; u < data.upSteps.length; ++u) {
+ let wasUpEnabled = control.wrap || control.value < control.to
keyPress(Qt.Key_Up)
compare(control.up.pressed, wasUpEnabled)
compare(control.down.pressed, false)
@@ -251,8 +254,8 @@ TestCase {
compare(valueModifiedSpy.count, valueModifiedCount)
}
- for (var d = 0; d < data.downSteps.length; ++d) {
- var wasDownEnabled = control.wrap || control.value > control.from
+ for (let d = 0; d < data.downSteps.length; ++d) {
+ let wasDownEnabled = control.wrap || control.value > control.from
keyPress(Qt.Key_Down)
compare(control.down.pressed, wasDownEnabled)
compare(control.up.pressed, false)
@@ -276,26 +279,26 @@ TestCase {
}
function test_locale() {
- var control = createTemporaryObject(spinBox, testCase)
+ let control = createTemporaryObject(spinBox, testCase)
verify(control)
control.locale = Qt.locale("ar_EG") // Arabic, Egypt
- var numbers = ["Ù ", "Ù¡", "Ù¢", "Ù£", "Ù¤", "Ù¥", "Ù¦", "Ù§", "Ù¨", "Ù©"]
- for (var i = 0; i < 10; ++i) {
+ let numbers = ["Ù ", "Ù¡", "Ù¢", "Ù£", "Ù¤", "Ù¥", "Ù¦", "Ù§", "Ù¨", "Ù©"]
+ for (let i = 0; i < 10; ++i) {
control.value = i
compare(control.contentItem.text, numbers[i])
}
}
function test_baseline() {
- var control = createTemporaryObject(spinBox, testCase)
+ let control = createTemporaryObject(spinBox, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
function test_focus() {
- var control = createTemporaryObject(spinBox, testCase, {from: 10, to: 1000, value: 100, focus: true})
+ let control = createTemporaryObject(spinBox, testCase, {from: 10, to: 1000, value: 100, focus: true})
verify(control)
control.forceActiveFocus()
@@ -314,41 +317,465 @@ TestCase {
}
function test_initialFocus() {
- var window = testCase.Window.window
+ let window = testCase.Window.window
verify(window)
compare(window.activeFocusItem, window.contentItem)
- var control = createTemporaryObject(spinBox, testCase, { editable: true, focus: true })
+ let control = createTemporaryObject(spinBox, testCase, { editable: true, focus: true })
verify(control)
tryCompare(control.contentItem, "activeFocus", true)
}
function test_editable() {
- var control = createTemporaryObject(spinBox, testCase)
+ let control = createTemporaryObject(spinBox, testCase)
verify(control)
- var valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
+ let valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
verify(valueModifiedSpy.valid)
+ let displayTextChangedSpy = signalSpy.createObject(control, {target: control, signalName: "displayTextChanged"})
+ verify(displayTextChangedSpy.valid)
+
+
+ control.from = 0
+ control.to = 10
+ compare(control.from, 0)
+ compare(control.to, 10)
+
control.contentItem.forceActiveFocus()
compare(control.contentItem.activeFocus, true)
compare(control.editable, false)
control.contentItem.selectAll()
+ compare(control.displayText, "0")
keyClick(Qt.Key_5)
keyClick(Qt.Key_Return)
+ compare(control.displayText, "0")
compare(control.value, 0)
compare(valueModifiedSpy.count, 0)
control.editable = true
compare(control.editable, true)
control.contentItem.selectAll()
+ keyClick(Qt.Key_Backspace)
keyClick(Qt.Key_5)
+ compare(control.displayText, "5")
keyClick(Qt.Key_Return)
compare(control.value, 5)
compare(valueModifiedSpy.count, 1)
+ compare(displayTextChangedSpy.count, 2)
+
+ keyClick(Qt.Key_0)
+ compare(control.displayText, "50")
+ compare(control.value, 5)
+ compare(valueModifiedSpy.count, 1)
+ compare(displayTextChangedSpy.count, 3)
+ keyClick(Qt.Key_Return) //will set the value to maximum = 10
+ compare(control.displayText, "10")
+ compare(control.value, 10)
+ compare(valueModifiedSpy.count, 2) // 0->5->10
+ compare(displayTextChangedSpy.count, 4) //0->5->50->10
}
+
+ function test_editable_liveUpdate() {
+ let control = createTemporaryObject(spinBox, testCase)
+ verify(control)
+
+ let valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
+ verify(valueModifiedSpy.valid)
+
+ let valueChangedSpy = signalSpy.createObject(control, {target: control, signalName: "valueChanged"})
+ verify(valueChangedSpy.valid)
+
+ let displayTextChangedSpy = signalSpy.createObject(control, {target: control, signalName: "displayTextChanged"})
+ verify(displayTextChangedSpy.valid)
+
+ control.contentItem.forceActiveFocus()
+ compare(control.contentItem.activeFocus, true)
+
+ control.editable = true
+ control.live = true
+ control.from = -10
+ control.to = 10
+ compare(control.editable, true)
+ compare(control.live, true)
+ compare(control.from, -10)
+ compare(control.to, 10)
+
+ control.contentItem.selectAll()
+ keyClick(Qt.Key_5)
+ compare(control.displayText, "5")
+ compare(control.value, 5)
+ compare(valueModifiedSpy.count, 0)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.count, 1)
+
+ keyClick(Qt.Key_0)
+ compare(control.displayText, "50") // do not set the value
+ compare(control.value, 5) // if it is out of bounds
+ compare(valueModifiedSpy.count, 0)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.count, 2)
+
+
+ keyClick(Qt.Key_Backspace)
+ compare(control.displayText, "5")
+ compare(control.value, 5)
+ compare(valueModifiedSpy.count, 0)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.count, 3)
+
+ keyClick(Qt.Key_Backspace)
+ compare(control.displayText, "0")
+ compare(control.value, 0)
+ compare(valueModifiedSpy.count, 0)
+ compare(valueChangedSpy.count, 2)
+ compare(displayTextChangedSpy.count, 4)
+
+ keyClick(Qt.Key_Backspace)
+ compare(control.displayText, "")
+ compare(control.value, 0)
+ compare(valueModifiedSpy.count, 0)
+ compare(valueChangedSpy.count, 2)
+ compare(displayTextChangedSpy.count, 5)
+ }
+
+ Component {
+ id: doubleBox
+ SpinBox {
+ id: doubleSpinBox
+ locale: Qt.locale("en_US")
+
+ property int decimals: 2
+ property double realValue: value / 10**decimals
+ validator: DoubleValidator {
+ bottom: Math.min(doubleSpinBox.from, doubleSpinBox.to)
+ top: Math.max(doubleSpinBox.from, doubleSpinBox.to)
+ decimals: doubleSpinBox.decimals
+ notation: DoubleValidator.StandardNotation
+ locale: doubleSpinBox.locale.name
+ }
+
+ textFromValue: function(value, locale) {
+ let res = Number(value / 10**doubleSpinBox.decimals).toLocaleString(locale, 'f', doubleSpinBox.decimals)
+ return res
+ }
+
+ valueFromText: function(text, locale) {
+ let res = Math.round(Number.fromLocaleString(locale, text) * 10**doubleSpinBox.decimals)
+ return res
+ }
+
+ onDisplayTextChanged: {
+ displayTextChangedWithArg(doubleSpinBox.displayText);
+ }
+
+ signal displayTextChangedWithArg(text: string)
+ }
+ }
+
+ function test_editable_doubleSpinBox() {
+ let control = createTemporaryObject(doubleBox, testCase)
+ verify(control)
+
+ control.editable = true
+ control.from = 0
+ control.to = 1000000
+ control.value = 500
+ control.stepSize = 1
+
+ compare(control.editable, true)
+ compare(control.from, 0)
+ compare(control.to, 1000000)
+ compare(control.value, 500)
+ compare(control.realValue, 5.00)
+ compare(control.displayText, "5.00")
+
+ let valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
+ verify(valueModifiedSpy.valid)
+
+ let valueChangedSpy = signalSpy.createObject(control, {target: control, signalName: "valueChanged"})
+ verify(valueChangedSpy.valid)
+
+ let displayTextChangedSpy = signalSpy.createObject(control, {target: control, signalName: "displayTextChangedWithArg"})
+ verify(displayTextChangedSpy.valid)
+
+ control.contentItem.forceActiveFocus()
+ compare(control.contentItem.activeFocus, true)
+
+ control.contentItem.selectAll()
+ compare(displayTextChangedSpy.count, 0)
+ keyClick(Qt.Key_4)
+ compare(control.value, 500)
+ compare(control.realValue, 5.00)
+ compare(control.displayText, "4")
+ compare(valueModifiedSpy.count, 0)
+ compare(valueChangedSpy.count, 0)
+ compare(displayTextChangedSpy.signalArguments[0][0], "4")
+ compare(displayTextChangedSpy.count, 1)
+
+ keyClick(Qt.Key_Enter)
+ compare(control.value, 400)
+ compare(control.realValue, 4.00)
+ compare(control.displayText, "4.00")
+ compare(valueModifiedSpy.count, 1)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.signalArguments[1][0], "4.00")
+ compare(displayTextChangedSpy.count, 2)
+
+ keyClick(Qt.Key_Backspace)
+ compare(control.value, 400)
+ compare(control.realValue, 4.00)
+ compare(control.displayText, "4.0")
+ compare(valueModifiedSpy.count, 1)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.signalArguments[2][0], "4.0")
+ compare(displayTextChangedSpy.count, 3)
+
+ keyClick(Qt.Key_Backspace)
+ compare(control.value, 400)
+ compare(control.realValue, 4.00)
+ compare(control.displayText, "4") //The fixup removes the trailing "."
+ compare(valueModifiedSpy.count, 1)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.signalArguments[3][0], "4")
+ compare(displayTextChangedSpy.count, 4)
+
+ keyClick(Qt.Key_0)
+ compare(control.value, 400)
+ compare(control.realValue, 4.00)
+ compare(control.displayText, "40")
+ compare(valueModifiedSpy.count, 1)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.signalArguments[4][0], "40")
+ compare(displayTextChangedSpy.count, 5)
+
+ keyClick(Qt.Key_0)
+ compare(control.value, 400)
+ compare(control.realValue, 4.00)
+ compare(control.displayText, "400")
+ compare(valueModifiedSpy.count, 1)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.signalArguments[5][0], "400")
+ compare(displayTextChangedSpy.count, 6)
+
+ keyClick(Qt.Key_0)
+ compare(control.value, 400)
+ compare(control.realValue, 4.00)
+ compare(control.displayText, "4,000")
+ compare(valueModifiedSpy.count, 1)
+ compare(valueChangedSpy.count, 1)
+ compare(displayTextChangedSpy.signalArguments[6][0], "4,000")
+ compare(displayTextChangedSpy.count, 7)
+
+ keyClick(Qt.Key_Enter)
+ compare(control.value, 400000)
+ compare(control.realValue, 4000.00)
+ compare(control.displayText, "4,000.00")
+ compare(valueModifiedSpy.count, 2)
+ compare(valueChangedSpy.count, 2)
+ compare(displayTextChangedSpy.signalArguments[7][0], "4,000.00")
+ compare(displayTextChangedSpy.count, 8)
+
+ // Changing to and testing live mode
+ control.live = true
+ compare(control.live, true)
+
+ keyClick(Qt.Key_Backspace)
+ compare(control.value, 400000)
+ compare(control.realValue, 4000.00)
+ compare(control.displayText, "4,000.0")
+ compare(valueModifiedSpy.count, 2)
+ compare(valueChangedSpy.count, 2)
+ compare(displayTextChangedSpy.signalArguments[8][0], "4,000.0")
+ compare(displayTextChangedSpy.count, 9)
+
+ keyClick(Qt.Key_Backspace)
+ compare(control.value, 400000)
+ compare(control.realValue, 4000.00)
+ compare(control.displayText, "4,000") //The fixup removes the trailing "."
+ compare(valueModifiedSpy.count, 2)
+ compare(valueChangedSpy.count, 2)
+ compare(displayTextChangedSpy.signalArguments[9][0], "4,000")
+ compare(displayTextChangedSpy.count, 10)
+
+ keyClick(Qt.Key_Backspace)
+ compare(control.displayText, "400.00")
+ compare(control.value, 40000)
+ compare(control.realValue, 400.00)
+ compare(valueModifiedSpy.count, 2)
+ compare(valueChangedSpy.count, 3)
+ compare(displayTextChangedSpy.signalArguments[10][0], "400.00")
+ compare(displayTextChangedSpy.count, 11)
+
+ // It is a bit unfortunate that we need 3 Backspace to go from
+ // 400 to 4000 on live editing mode. Maybe think about a fix in
+ // the future to make it more user friendly
+ keyClick(Qt.Key_Backspace)
+ keyClick(Qt.Key_Backspace)
+ keyClick(Qt.Key_Backspace)
+ compare(control.displayText, "40.00")
+ compare(control.value, 4000)
+ compare(control.realValue, 40.00)
+ compare(valueModifiedSpy.count, 2)
+ compare(valueChangedSpy.count, 4)
+ compare(displayTextChangedSpy.signalArguments[11][0], "400.0")
+ compare(displayTextChangedSpy.signalArguments[12][0], "400")
+ compare(displayTextChangedSpy.signalArguments[13][0], "40.00")
+ compare(displayTextChangedSpy.count, 14)
+
+ keyClick(Qt.Key_Backspace)
+ keyClick(Qt.Key_Backspace)
+ keyClick(Qt.Key_Backspace)
+ compare(control.displayText, "4.00")
+ compare(control.value, 400)
+ compare(control.realValue, 4.00)
+ compare(valueModifiedSpy.count, 2)
+ compare(valueChangedSpy.count, 5)
+ compare(displayTextChangedSpy.signalArguments[14][0], "40.0")
+ compare(displayTextChangedSpy.signalArguments[15][0], "40")
+ compare(displayTextChangedSpy.signalArguments[16][0], "4.00")
+ compare(displayTextChangedSpy.count, 17)
+
+ keyClick(Qt.Key_Backspace)
+ keyClick(Qt.Key_Backspace)
+ keyClick(Qt.Key_1)
+ compare(control.displayText, "41.00")
+ compare(control.value, 4100)
+ compare(control.realValue, 41.00)
+ compare(valueModifiedSpy.count, 2)
+ compare(valueChangedSpy.count, 6)
+ compare(displayTextChangedSpy.signalArguments[17][0], "4.0")
+ compare(displayTextChangedSpy.signalArguments[18][0], "4")
+ compare(displayTextChangedSpy.signalArguments[19][0], "41.00")
+ compare(displayTextChangedSpy.count, 20)
+ }
+
+ function test_groupSeparatorHandling_data() {
+ return [
+ { tag: "en_EN" },
+ { tag: "de_DE" }
+ ]
+ }
+
+ function test_groupSeparatorHandling(data) {
+ let control = createTemporaryObject(spinBox, testCase)
+ verify(control)
+
+ let testLoc = Qt.locale(data.tag)
+ control.locale = testLoc
+
+ control.contentItem.forceActiveFocus()
+ compare(control.contentItem.activeFocus, true)
+
+ control.editable = true
+ control.live = true
+ control.from = 0
+ control.to = 10*1000*1000
+ compare(control.editable, true)
+ compare(control.live, true)
+ compare(control.from, 0)
+ compare(control.to, 10*1000*1000)
+
+ control.contentItem.selectAll()
+ keyClick(Qt.Key_5)
+ compare(control.displayText, "5")
+ compare(control.value, 5)
+
+ let i = 50
+ for (; i < 1e7; i*=10) {
+ keyClick(Qt.Key_0)
+ compare(control.displayText, testLoc.toString(i))
+ compare(control.value, i)
+ }
+
+ i /= 100;
+ for (; i > 10; i/=10) {
+ keyClick(Qt.Key_Backspace)
+ compare(control.displayText, testLoc.toString(i))
+ compare(control.value, i)
+ }
+ }
+
+ function test_qtbug64151() {
+ // Slightly modified example from QTBUG-64151. We use displayText
+ // instead of contentItem.text as a workaround.
+ let control = createTemporaryObject(spinBox, testCase)
+ verify(control)
+
+ control.locale = Qt.locale("en_EN")
+
+ control.from = 0
+ control.to = 2000
+ control.value = 2000
+ control.editable = true
+
+ compare(control.displayText, "2,000")
+
+ control.contentItem.forceActiveFocus()
+ compare(control.contentItem.activeFocus, true)
+ keyClick(Qt.Key_Backspace)
+ keyClick(Qt.Key_Return)
+
+ compare(control.displayText, "200")
+ compare(control.valueFromText(control.contentItem.text, control.locale), 200)
+ compare(control.value, 200)
+
+ control.contentItem.forceActiveFocus()
+ keyClick(Qt.Key_0)
+ keyClick(Qt.Key_Return)
+ compare(control.displayText, "2,000")
+ }
+
+ Component {
+ id: spinBoxAndAction
+ RowLayout {
+ id: layout
+ property alias spinbox: theSpinbox
+ property alias button: theButton
+ SpinBox {
+ id: theSpinbox
+ from: 0
+ to: 200
+ value: 200
+ editable: true
+ live: true
+ }
+
+ Button {
+ id: theButton
+ property int value: 0
+ action: Action {
+ text: "&Do something"
+ shortcut: "Return"
+ onTriggered: {
+ theButton.value = theSpinbox.value;
+ }
+ }
+ }
+ }
+ }
+
+ function test_qtbug103205() {
+
+ let control = createTemporaryObject(spinBoxAndAction, testCase)
+ verify(control)
+ verify(control.spinbox)
+
+ compare(control.spinbox.displayText, "200")
+ control.forceActiveFocus()
+ control.spinbox.forceActiveFocus()
+ control.spinbox.contentItem.forceActiveFocus()
+ compare(control.spinbox.contentItem.activeFocus, true)
+ keyClick(Qt.Key_Backspace)
+ keyClick(Qt.Key_Return)
+
+ compare(control.spinbox.displayText, "20")
+ compare(control.button.value, 20)
+ }
+
+
function test_wheel_data() {
return [
{ tag: "1", properties: { from: 1, to: 10, value: 1, stepSize: 1 }, upSteps: [2,3,4], downSteps: [3,2,1,1] },
@@ -361,24 +788,24 @@ TestCase {
}
function test_wheel(data) {
- var ma = createTemporaryObject(mouseArea, testCase, {width: 100, height: 100})
+ let ma = createTemporaryObject(mouseArea, testCase, {width: 100, height: 100})
verify(ma)
data.properties.wheelEnabled = true
- var control = spinBox.createObject(ma, data.properties)
+ let control = spinBox.createObject(ma, data.properties)
verify(control)
- var valueModifiedCount = 0
- var valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
+ let valueModifiedCount = 0
+ let valueModifiedSpy = signalSpy.createObject(control, {target: control, signalName: "valueModified"})
verify(valueModifiedSpy.valid)
- var delta = 120
+ let delta = 120
- var spy = signalSpy.createObject(ma, {target: ma, signalName: "wheel"})
+ let spy = signalSpy.createObject(ma, {target: ma, signalName: "wheel"})
verify(spy.valid)
- for (var u = 0; u < data.upSteps.length; ++u) {
- var wasUpEnabled = control.wrap || control.value < control.to
+ for (let u = 0; u < data.upSteps.length; ++u) {
+ let wasUpEnabled = control.wrap || control.value < control.to
mouseWheel(control, control.width / 2, control.height / 2, delta, delta)
if (wasUpEnabled)
++valueModifiedCount
@@ -387,8 +814,8 @@ TestCase {
compare(control.value, data.upSteps[u])
}
- for (var d = 0; d < data.downSteps.length; ++d) {
- var wasDownEnabled = control.wrap || control.value > control.from
+ for (let d = 0; d < data.downSteps.length; ++d) {
+ let wasDownEnabled = control.wrap || control.value > control.from
mouseWheel(control, control.width / 2, control.height / 2, -delta, -delta)
if (wasDownEnabled)
++valueModifiedCount
@@ -408,7 +835,7 @@ TestCase {
}
function test_initiallyDisabledIndicators(data) {
- var control = createTemporaryObject(spinBox, testCase, { from: data.from, value: data.value, to: data.to })
+ let control = createTemporaryObject(spinBox, testCase, { from: data.from, value: data.value, to: data.to })
verify(control)
compare(control.up.indicator.enabled, data.upEnabled)
@@ -427,10 +854,10 @@ TestCase {
}
function test_hover(data) {
- var control = createTemporaryObject(spinBox, testCase, {hoverEnabled: data.hoverEnabled, value: data.value})
+ let control = createTemporaryObject(spinBox, testCase, {hoverEnabled: data.hoverEnabled, value: data.value})
verify(control)
- var button = control[data.button]
+ let button = control[data.button]
compare(control.hovered, false)
compare(button.hovered, false)
@@ -451,16 +878,16 @@ TestCase {
// QTBUG-74688
function test_hoverWhilePressed(data) {
- var control = createTemporaryObject(spinBox, testCase, { hoverEnabled: true, value: 50 })
+ let control = createTemporaryObject(spinBox, testCase, { hoverEnabled: true, value: 50 })
verify(control)
- var button = control[data.tag]
+ let button = control[data.tag]
compare(control.hovered, false)
compare(button.hovered, false)
// Hover over the indicator. It should be hovered.
- var buttonXCenter = button.indicator.x + button.indicator.width / 2
- var buttonYCenter = button.indicator.y + button.indicator.height / 2
+ let buttonXCenter = button.indicator.x + button.indicator.width / 2
+ let buttonYCenter = button.indicator.y + button.indicator.height / 2
mouseMove(control, buttonXCenter, buttonYCenter)
compare(button.hovered, true)
@@ -483,13 +910,13 @@ TestCase {
}
function test_valueFromText(data) {
- var control = createTemporaryObject(spinBox, testCase, {editable: data.editable})
+ let control = createTemporaryObject(spinBox, testCase, {editable: data.editable})
verify(control)
control.forceActiveFocus()
verify(control.activeFocus)
- var valueFromTextCalls = 0
+ let valueFromTextCalls = 0
control.valueFromText = function(text, locale) {
++valueFromTextCalls
return Number.fromLocaleString(locale, text);
@@ -506,21 +933,21 @@ TestCase {
}
function test_callDefaultValueFromText() {
- var control = createTemporaryObject(spinBox, testCase)
+ let control = createTemporaryObject(spinBox, testCase)
verify(control)
compare(control.valueFromText("123", control.locale), 123)
}
function test_autoRepeat() {
- var control = createTemporaryObject(spinBox, testCase)
+ let control = createTemporaryObject(spinBox, testCase)
verify(control)
compare(control.value, 0)
- var valueSpy = signalSpy.createObject(control, {target: control, signalName: "valueChanged"})
+ let valueSpy = signalSpy.createObject(control, {target: control, signalName: "valueChanged"})
verify(valueSpy.valid)
- var countBefore = 0
+ let countBefore = 0
// repeat up
mousePress(control.up.indicator)
@@ -572,7 +999,7 @@ TestCase {
}
function test_initialValue() {
- var control = createTemporaryObject(spinBox, testCase, {from: 1000, to: 10000})
+ let control = createTemporaryObject(spinBox, testCase, {from: 1000, to: 10000})
verify(control)
compare(control.value, 1000)
}
@@ -594,7 +1021,7 @@ TestCase {
}
valueFromText: function(text) {
- for (var i = 0; i < items.length; ++i) {
+ for (let i = 0; i < items.length; ++i) {
if (items[i].toLowerCase().indexOf(text.toLowerCase()) === 0)
return i
}
@@ -611,10 +1038,10 @@ TestCase {
}
function test_textFromValue(data) {
- var control = createTemporaryObject(data.component, testCase)
+ let control = createTemporaryObject(data.component, testCase)
verify(control)
- for (var i = 0; i < data.values.length; ++i) {
+ for (let i = 0; i < data.values.length; ++i) {
control.value = data.values[i]
compare(control.value, data.values[i])
compare(control.displayText, data.displayTexts[i])
@@ -622,7 +1049,7 @@ TestCase {
}
function test_callDefaultTextFromValue() {
- var control = createTemporaryObject(spinBox, testCase)
+ let control = createTemporaryObject(spinBox, testCase)
verify(control)
compare(control.textFromValue(123, control.locale), "123")
}
@@ -638,7 +1065,7 @@ TestCase {
}
function test_indicatorOverridden() {
- var control = createTemporaryObject(overriddenSpinBox, testCase)
+ let control = createTemporaryObject(overriddenSpinBox, testCase)
verify(control)
compare(control.up.indicator.s, "this is the one");
}
@@ -659,9 +1086,8 @@ TestCase {
}
function test_indicatorOverriddenWithIds() {
- var control = createTemporaryObject(overriddenSpinBoxWithIds, testCase)
+ let control = createTemporaryObject(overriddenSpinBoxWithIds, testCase)
verify(control)
- // TODO: Use failOnWarning() here when it has been implemented
// Specifying an id will result in both the default indicator implementations
// and the custom ones being created, but it shouldn't result in any TypeErrors.
compare(control.up.indicator.s, "up");
@@ -670,7 +1096,7 @@ TestCase {
function test_valueEnterFromOutsideRange() {
// Check that changing from 2 to 99 goes to 98 then changing to 99 puts it back to 98
- var control = createTemporaryObject(spinBox, testCase, {from: 2, to: 98, value: 2, editable: true})
+ let control = createTemporaryObject(spinBox, testCase, {from: 2, to: 98, value: 2, editable: true})
verify(control)
control.forceActiveFocus()
@@ -692,4 +1118,39 @@ TestCase {
compare(control.displayText, "98")
compare(control.contentItem.text, "98")
}
+
+ function test_pressedBeforeIncrementOrDecrement(data) {
+ let control = createTemporaryObject(spinBox, testCase, {from: -8, to: 8, value: 0})
+ verify(control)
+
+ control.forceActiveFocus()
+ verify(control.activeFocus)
+
+ // up, down
+ control.stepSize = Qt.binding(() => control.up.pressed ? 2 : 1)
+ keyClick(Qt.Key_Up) // +2
+ compare(control.value, 2)
+ mouseClick(control.up.indicator) // +2
+ compare(control.value, 4)
+ keyClick(Qt.Key_Down) // -1
+ compare(control.value, 3)
+ mouseClick(control.down.indicator) // -1
+ compare(control.value, 2)
+
+ // down, up
+ control.stepSize = Qt.binding(() => control.down.pressed ? 2 : 1)
+ keyClick(Qt.Key_Down) // -2
+ compare(control.value, 0)
+ mouseClick(control.down.indicator) // -2
+ compare(control.value, -2)
+ keyClick(Qt.Key_Up) // +1
+ compare(control.value, -1)
+ mouseClick(control.up.indicator) // +1
+ compare(control.value, 0)
+ }
+
+ function test_nullValidator() {
+ let control = createTemporaryObject(spinBox, testCase, { validator: null })
+ verify(control)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_splitview.qml b/tests/auto/quickcontrols/controls/data/tst_splitview.qml
index 4cefd834cb..aed303689a 100644
--- a/tests/auto/quickcontrols/controls/data/tst_splitview.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_splitview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtCore
import QtQuick
@@ -24,9 +24,9 @@ TestCase {
}
function findHandles(splitView) {
- var handles = []
- for (var i = 0; i < splitView.children.length; ++i) {
- var child = splitView.children[i]
+ let handles = []
+ for (let i = 0; i < splitView.children.length; ++i) {
+ let child = splitView.children[i]
if (child.objectName.toLowerCase().indexOf("handle") !== -1)
handles.push(child)
}
@@ -42,14 +42,14 @@ TestCase {
compare(control.count, Math.floor(expectedGeometries.length / 2) + 1,
"Mismatch in actual vs expected number of split items" + context)
- var handles = findHandles(control)
+ let handles = findHandles(control)
compare(handles.length, Math.floor(expectedGeometries.length / 2),
"Mismatch in actual vs expected number of handle items" + context)
- for (var i = 0, splitItemIndex = 0, handleItemIndex = 0; i < expectedGeometries.length; ++i) {
- var item = null
- var itemType = ""
- var typeSpecificIndex = -1
+ for (let i = 0, splitItemIndex = 0, handleItemIndex = 0; i < expectedGeometries.length; ++i) {
+ let item = null
+ let itemType = ""
+ let typeSpecificIndex = -1
if (i % 2 == 0) {
item = control.itemAt(splitItemIndex)
itemType = "split item"
@@ -64,7 +64,7 @@ TestCase {
verify(item, itemType + " at index " + typeSpecificIndex + " should not be null" + context)
- var expectedGeometry = expectedGeometries[i]
+ let expectedGeometry = expectedGeometries[i]
if (expectedGeometry.hasOwnProperty("hidden")) {
// It's geometry doesn't matter because it's hidden.
verify(!item.visible, itemType + " at index " + typeSpecificIndex + " should be hidden" + context)
@@ -74,20 +74,19 @@ TestCase {
// Note that the indices mentioned here account for handles; they do not
// match the indices reported by QQuickSplitView's logging categories.
compare(item.x, expectedGeometry.x, "Mismatch in actual vs expected x value of "
- + itemType + " at index " + typeSpecificIndex + context)
+ + itemType + " " + item + " at index " + typeSpecificIndex + context)
compare(item.y, expectedGeometry.y, "Mismatch in actual vs expected y value of "
- + itemType + " at index " + typeSpecificIndex + context)
+ + itemType + " " + item + " at index " + typeSpecificIndex + context)
compare(item.width, expectedGeometry.width, "Mismatch in actual vs expected width value of "
- + itemType + " at index " + typeSpecificIndex + context)
+ + itemType + " " + item + " at index " + typeSpecificIndex + context)
compare(item.height, expectedGeometry.height, "Mismatch in actual vs expected height value of "
- + itemType + " at index " + typeSpecificIndex + context)
+ + itemType + " " + item + " at index " + typeSpecificIndex + context)
}
}
property real defaultHorizontalHandleWidth: 10
property real defaultVerticalHandleHeight: 10
-
Component {
id: signalSpyComponent
SignalSpy {}
@@ -96,14 +95,14 @@ TestCase {
Component {
id: handleComponent
Rectangle {
- objectName: "handle"
+ objectName: `handle ${x},${y} ${width}x${height} visible: ${visible}`
implicitWidth: defaultHorizontalHandleWidth
implicitHeight: defaultVerticalHandleHeight
color: "#444"
Text {
- objectName: "handleText_" + text
- text: parent.x + "," + parent.y + " " + parent.width + "x" + parent.height
+ objectName: text + "_Text"
+ text: parent.objectName
color: "white"
anchors.centerIn: parent
rotation: 90
@@ -132,18 +131,20 @@ TestCase {
Rectangle {}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(defaultSplitView, testCase)
verify(control)
}
function test_addItemsAfterCompletion() {
- var control = createTemporaryObject(splitViewComponent, testCase)
+ let control = createTemporaryObject(splitViewComponent, testCase)
verify(control)
- var item0 = rectangleComponent.createObject(control, { implicitWidth: 25, color: "salmon" })
+ let item0 = rectangleComponent.createObject(control, { implicitWidth: 25, color: "salmon" })
verify(item0)
verify(isPolishScheduled(control))
verify(waitForItemPolished(control))
@@ -153,7 +154,7 @@ TestCase {
compare(item0.width, testCase.width)
compare(item0.height, testCase.height)
- var item1 = rectangleComponent.createObject(control, { implicitWidth: 25, color: "steelblue" })
+ let item1 = rectangleComponent.createObject(control, { implicitWidth: 25, color: "steelblue" })
verify(item1)
verify(isPolishScheduled(control))
verify(waitForItemPolished(control))
@@ -162,8 +163,8 @@ TestCase {
compare(item0.y, 0)
compare(item0.width, item0.implicitWidth)
compare(item0.height, testCase.height)
- var handles = findHandles(control)
- var handle0 = handles[0]
+ let handles = findHandles(control)
+ let handle0 = handles[0]
compare(handle0.x, item0.implicitWidth)
compare(handle0.y, 0)
compare(handle0.width, defaultHorizontalHandleWidth)
@@ -175,10 +176,10 @@ TestCase {
}
function test_addItemsWithNoSizeAfterCompletion() {
- var control = createTemporaryObject(splitViewComponent, testCase)
+ let control = createTemporaryObject(splitViewComponent, testCase)
verify(control)
- var item0 = rectangleComponent.createObject(control, { color: "salmon" })
+ let item0 = rectangleComponent.createObject(control, { color: "salmon" })
verify(item0)
verify(isPolishScheduled(control))
verify(waitForItemPolished(control))
@@ -187,7 +188,7 @@ TestCase {
compare(item0.width, testCase.width)
compare(item0.height, testCase.height)
- var item1 = rectangleComponent.createObject(control, { color: "steelblue" })
+ let item1 = rectangleComponent.createObject(control, { color: "steelblue" })
verify(item1)
verify(isPolishScheduled(control))
verify(waitForItemPolished(control))
@@ -195,8 +196,8 @@ TestCase {
compare(item0.y, 0)
compare(item0.width, 0)
compare(item0.height, testCase.height)
- var handles = findHandles(control)
- var handle0 = handles[0]
+ let handles = findHandles(control)
+ let handle0 = handles[0]
compare(handle0.x, 0)
compare(handle0.y, 0)
compare(handle0.width, defaultHorizontalHandleWidth)
@@ -230,35 +231,35 @@ TestCase {
}
function test_changeAttachedPropertiesAfterCompletion() {
- var control = createTemporaryObject(threeZeroSizedItemsComponent, testCase)
+ let control = createTemporaryObject(threeZeroSizedItemsComponent, testCase)
verify(control)
- var item0 = control.itemAt(0)
+ let item0 = control.itemAt(0)
compare(item0.x, 0)
compare(item0.y, 0)
compare(item0.width, 0)
compare(item0.height, testCase.height)
- var handles = findHandles(control)
- var handle0 = handles[0]
+ let handles = findHandles(control)
+ let handle0 = handles[0]
compare(handle0.x, 0)
compare(handle0.y, 0)
compare(handle0.width, defaultHorizontalHandleWidth)
compare(handle0.height, testCase.height)
- var item1 = control.itemAt(1)
+ let item1 = control.itemAt(1)
compare(item1.x, defaultHorizontalHandleWidth)
compare(item1.y, 0)
compare(item1.width, 0)
compare(item1.height, testCase.height)
- var handle1 = handles[1]
+ let handle1 = handles[1]
compare(handle1.x, defaultHorizontalHandleWidth)
compare(handle1.y, 0)
compare(handle1.width, defaultHorizontalHandleWidth)
compare(handle1.height, testCase.height)
- var item2 = control.itemAt(2)
+ let item2 = control.itemAt(2)
compare(item2.x, defaultHorizontalHandleWidth * 2)
compare(item2.y, 0)
compare(item2.width, testCase.width - item2.x)
@@ -396,19 +397,19 @@ TestCase {
}
function test_useAttachedPropertiesIncorrectly_data() {
- var properties = [ "fillWidth", "fillHeight", "minimumWidth", "minimumHeight",
+ let properties = [ "fillWidth", "fillHeight", "minimumWidth", "minimumHeight",
"preferredWidth", "preferredHeight", "maximumWidth", "maximumHeight" ]
- var data = []
+ let data = []
- for (var i = 0; i < properties.length; ++i) {
- var property = properties[i]
+ for (let i = 0; i < properties.length; ++i) {
+ let property = properties[i]
data.push({ tag: "Item," + property, component: itemComponent, property: property,
expectedWarning: /.*SplitView: attached properties must be accessed through a direct child of SplitView/ })
}
- for (i = 0; i < properties.length; ++i) {
- property = properties[i]
+ for (let i = 0; i < properties.length; ++i) {
+ let property = properties[i]
data.push({ tag: "QtObject," + property, component: objectComponent, property: property,
expectedWarning: /.*SplitView: attached properties can only be used on Items/ })
}
@@ -418,7 +419,7 @@ TestCase {
function test_useAttachedPropertiesIncorrectly(data) {
// The object (whatever it may be) is not managed by a SplitView.
- var object = createTemporaryObject(data.component, testCase, { objectName: data.tag })
+ let object = createTemporaryObject(data.component, testCase, { objectName: data.tag })
verify(object)
ignoreWarning(data.expectedWarning)
@@ -427,12 +428,11 @@ TestCase {
}
function test_sizes_data() {
- var splitViewWidth = testCase.width
- var splitViewHeight = testCase.height
- var data = [
+ let splitViewWidth = testCase.width
+ let splitViewHeight = testCase.height
+ let data = [
{
- // When the combined size of items is too large, the non-fill items should just exceed
- // the size of the SplitView, exactly as they would were they in a RowLayout, for example.
+ // When the combined size of items is too large, make them fit.
tag: "fillItemOnLeft",
expectedGeometries: [
// We're the fill item, but since the combined implicitWidths
@@ -445,8 +445,8 @@ TestCase {
// Second handle.
{ x: 200 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
height: splitViewHeight },
- // The third item also gets its implicitWidth.
- { x: 200 + defaultHorizontalHandleWidth * 2, y: 0, width: 200, height: splitViewHeight }
+ // The third item is reduced from its implicitWidth to fit into SplitView.
+ { x: 200 + defaultHorizontalHandleWidth * 2, y: 0, width: 200 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
]
},
{
@@ -463,8 +463,8 @@ TestCase {
// Second handle.
{ x: 0, y: 200 + defaultVerticalHandleHeight, width: splitViewWidth,
height: defaultVerticalHandleHeight },
- // The third item also gets its implicitHeight.
- { x: 0, y: 200 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 }
+ // The third item is reduced from its implicitWidth to fit into SplitView.
+ { x: 0, y: 200 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 - 2 * defaultVerticalHandleHeight }
]
},
{
@@ -489,9 +489,9 @@ TestCase {
}
function test_sizes(data) {
- var component = Qt.createComponent("splitview/" + data.tag + ".qml")
+ let component = Qt.createComponent("splitview/" + data.tag + ".qml")
compare(component.status, Component.Ready, component.errorString());
- var control = createTemporaryObject(component, testCase, { "handle": handleComponent })
+ let control = createTemporaryObject(component, testCase, { "handle": handleComponent })
verify(control)
compareSizes(control, data.expectedGeometries)
@@ -526,9 +526,9 @@ TestCase {
}
function test_resetAttachedProperties_data() {
- var splitViewWidth = testCase.width
- var splitViewHeight = testCase.height
- var data = [
+ let splitViewWidth = testCase.width
+ let splitViewHeight = testCase.height
+ let data = [
{
tag: "resetMinimumWidth",
orientation: Qt.Horizontal,
@@ -696,11 +696,11 @@ TestCase {
}
function test_resetAttachedProperties(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase,
{ "orientation": data.orientation })
verify(control)
- var splitItem = control.itemAt(data.splitItemIndex)
+ let splitItem = control.itemAt(data.splitItemIndex)
splitItem.SplitView[data.propertyName] = data.propertyValue
verify(isPolishScheduled(control))
verify(waitForItemPolished(control))
@@ -713,35 +713,35 @@ TestCase {
}
function test_orientation() {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase)
verify(control)
- var item0 = control.itemAt(0)
+ let item0 = control.itemAt(0)
compare(item0.x, 0)
compare(item0.y, 0)
compare(item0.width, item0.implicitWidth)
compare(item0.height, testCase.height)
- var handles = findHandles(control)
- var handle0 = handles[0]
+ let handles = findHandles(control)
+ let handle0 = handles[0]
compare(handle0.x, item0.implicitWidth)
compare(handle0.y, 0)
compare(handle0.width, defaultHorizontalHandleWidth)
compare(handle0.height, testCase.height)
- var item1 = control.itemAt(1)
+ let item1 = control.itemAt(1)
compare(item1.x, item0.width + defaultHorizontalHandleWidth)
compare(item1.y, 0)
compare(item1.width, item1.implicitWidth)
compare(item1.height, testCase.height)
- var handle1 = handles[1]
+ let handle1 = handles[1]
compare(handle1.x, item1.x + item1.width)
compare(handle1.y, 0)
compare(handle1.width, defaultHorizontalHandleWidth)
compare(handle1.height, testCase.height)
- var item2 = control.itemAt(2)
+ let item2 = control.itemAt(2)
compare(item2.x, item0.width + item1.width + defaultHorizontalHandleWidth * 2)
compare(item2.y, 0)
compare(item2.width, testCase.width - item2.x)
@@ -775,6 +775,75 @@ TestCase {
compare(item2.height, testCase.height - item2.y)
}
+ Component {
+ id: threeSizedItemsComponentWithDefaultHandle
+
+ SplitView {
+ anchors.fill: parent
+
+ Rectangle {
+ objectName: "salmon"
+ color: objectName
+ implicitWidth: 25
+ implicitHeight: 25
+ }
+ Rectangle {
+ objectName: "navajowhite"
+ color: objectName
+ implicitWidth: 100
+ implicitHeight: 100
+ }
+ Rectangle {
+ objectName: "steelblue"
+ color: objectName
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ }
+ }
+
+ function test_orientationWithDefaultHandle() {
+ const control = createTemporaryObject(threeSizedItemsComponentWithDefaultHandle, testCase)
+ verify(control)
+
+ const item0 = control.itemAt(0)
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, item0.implicitWidth)
+ compare(item0.height, testCase.height)
+
+ const item1 = control.itemAt(1)
+ let handleDefaultWidth = item1.x - item0.width // Find default handle width
+ compare(item1.x, item0.width + handleDefaultWidth)
+ compare(item1.y, 0)
+ compare(item1.width, item1.implicitWidth)
+ compare(item1.height, testCase.height)
+
+ const item2 = control.itemAt(2)
+ compare(item2.x, item0.width + item1.width + handleDefaultWidth * 2)
+ compare(item2.y, 0)
+ compare(item2.width, testCase.width - item2.x)
+ compare(item2.height, testCase.height)
+
+ control.orientation = Qt.Vertical
+ verify(isPolishScheduled(control))
+ verify(waitForItemPolished(control))
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, testCase.width)
+ compare(item0.height, item0.implicitHeight)
+
+ compare(item1.x, 0)
+ compare(item1.y, item0.height + handleDefaultWidth)
+ compare(item1.width, testCase.width)
+ compare(item1.height, item1.implicitHeight)
+
+ compare(item2.x, 0)
+ compare(item2.y, item0.height + item1.height + handleDefaultWidth * 2)
+ compare(item2.width, testCase.width)
+ compare(item2.height, testCase.height - item2.y)
+ }
+
readonly property int splitViewMargins: 50
Component {
@@ -876,9 +945,9 @@ TestCase {
}
function test_dragHandle_data() {
- var splitViewWidth = testCase.width - splitViewMargins * 2
- var splitViewHeight = testCase.height - splitViewMargins * 2
- var data = [
+ let splitViewWidth = testCase.width - splitViewMargins * 2
+ let splitViewHeight = testCase.height - splitViewMargins * 2
+ let data = [
{
tag: "fillThirdItemAndDragFirstHandlePastRightSide",
component: threeSizedItemsComponent,
@@ -1034,7 +1103,7 @@ TestCase {
{ x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
- { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: 200, height: splitViewHeight }
+ { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: 200 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
],
expectedGeometriesAfterDrag: [
// The fill item is to the left of the handle at index 1, so the handle belongs
@@ -1067,7 +1136,7 @@ TestCase {
{ x: 0, y: 0, width: splitViewWidth, height: defaultVerticalHandleHeight },
{ x: 0, y: defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
{ x: 0, y: 100 + defaultVerticalHandleHeight, width: splitViewWidth, height: defaultVerticalHandleHeight },
- { x: 0, y: 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 }
+ { x: 0, y: 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 - defaultVerticalHandleHeight * 2 }
],
expectedGeometriesAfterDrag: [
// The fill item is to the top of the handle at index 1, so the handle belongs
@@ -1099,8 +1168,8 @@ TestCase {
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
// The second item's implicitWidth is 100, and ours is 200. The available width is 300,
- // so both items get their implicit widths.
- { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
+ // so this item gets size 300 - 100 - 2 * 10 = 180.
+ { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
],
// Should be unchanged.
expectedGeometriesAfterDrag: [
@@ -1108,7 +1177,7 @@ TestCase {
{ x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
- { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
+ { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
]
},
{
@@ -1123,7 +1192,9 @@ TestCase {
{ x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
{ x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
{ x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
- { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
+ // The first item is the filling one, with minimum size 25. Second item is 100.
+ // The available size is 300, so third item is 300 - 100 - 25 - 2 * 10 = 155.
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100 - 25 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
],
// Should be unchanged.
expectedGeometriesAfterDrag: [
@@ -1131,7 +1202,7 @@ TestCase {
{ x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
{ x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
{ x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
- { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100 - 25 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
]
},
{
@@ -1191,7 +1262,7 @@ TestCase {
}
function test_dragHandle(data) {
- var control = createTemporaryObject(data.component, testCase)
+ let control = createTemporaryObject(data.component, testCase)
verify(control)
control.orientation = data.orientation
@@ -1199,7 +1270,7 @@ TestCase {
// Ensure that there is space to drag outside of the SplitView.
control.anchors.margins = splitViewMargins
- var fillItem = control.itemAt(data.fillIndex)
+ let fillItem = control.itemAt(data.fillIndex)
if (control.orientation === Qt.Horizontal)
fillItem.SplitView.fillWidth = true
else
@@ -1211,13 +1282,13 @@ TestCase {
compareSizes(control, data.expectedGeometriesBeforeDrag, "before drag")
// Drag the handle.
- var handles = findHandles(control)
- var targetHandle = handles[data.handleIndex]
+ let handles = findHandles(control)
+ let targetHandle = handles[data.handleIndex]
verify(targetHandle.visible)
mousePress(targetHandle)
verify(control.resizing)
// newHandlePos is in scene coordinates, so map it to coordinates local to the handle.
- var localPos = testCase.mapToItem(targetHandle, data.newHandlePos.x, data.newHandlePos.y)
+ let localPos = testCase.mapToItem(targetHandle, data.newHandlePos.x, data.newHandlePos.y)
mouseMove(targetHandle, localPos.x - targetHandle.width / 2, localPos.y - targetHandle.height / 2)
verify(control.resizing)
compareSizes(control, data.expectedGeometriesAfterDrag, "after drag move")
@@ -1229,10 +1300,10 @@ TestCase {
}
function test_splitViewGeometryChanges_data() {
- var defaultSplitViewWidth = testCase.width
- var defaultSplitViewHeight = testCase.height
+ let defaultSplitViewWidth = testCase.width
+ let defaultSplitViewHeight = testCase.height
- var data = [
+ let data = [
{
tag: "growWidth",
orientation: Qt.Horizontal,
@@ -1296,7 +1367,7 @@ TestCase {
}
function test_splitViewGeometryChanges(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase,
{ "handle": handleComponent, "anchors.fill": undefined, "orientation": data.orientation })
verify(control)
@@ -1316,10 +1387,10 @@ TestCase {
}
function test_splitItemImplicitSizeChanges_data() {
- var defaultSplitViewWidth = testCase.width
- var defaultSplitViewHeight = testCase.height
+ let defaultSplitViewWidth = testCase.width
+ let defaultSplitViewHeight = testCase.height
- var data = [
+ let data = [
{
tag: "growImplicitWidth",
orientation: Qt.Horizontal,
@@ -1354,11 +1425,11 @@ TestCase {
// Tests that implicitWidth/Height changes in items are noticed by SplitView.
function test_splitItemImplicitSizeChanges(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase,
{ "handle": handleComponent, "orientation": data.orientation })
verify(control)
- var firstItem = control.itemAt(0)
+ let firstItem = control.itemAt(0)
if (data.hasOwnProperty("splitItemImplicitWidth"))
firstItem.implicitWidth = data.splitItemImplicitWidth
@@ -1392,10 +1463,10 @@ TestCase {
}
function test_handleChanges_data() {
- var splitViewWidth = testCase.width
- var splitViewHeight = testCase.height
+ let splitViewWidth = testCase.width
+ let splitViewHeight = testCase.height
- var data = [
+ let data = [
{
tag: "growHandleWidth",
orientation: Qt.Horizontal,
@@ -1455,7 +1526,7 @@ TestCase {
}
function test_handleChanges(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase,
{ "orientation": data.orientation })
verify(control)
@@ -1466,10 +1537,10 @@ TestCase {
}
function test_insertRemoveItems_data() {
- var splitViewWidth = testCase.width
- var splitViewHeight = testCase.height
+ let splitViewWidth = testCase.width
+ let splitViewHeight = testCase.height
- var data = [
+ let data = [
{
tag: "insertItemAtHorizontalEnd",
orientation: Qt.Horizontal,
@@ -1552,17 +1623,17 @@ TestCase {
}
function test_insertRemoveItems(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase,
{ "orientation": data.orientation })
verify(control)
if (data.hasOwnProperty("removeItemAtIndex")) {
- var itemToRemove = control.itemAt(data.removeItemAtIndex)
+ let itemToRemove = control.itemAt(data.removeItemAtIndex)
verify(itemToRemove)
control.removeItem(itemToRemove)
} else if (data.hasOwnProperty("insertItemAtIndex")) {
- var itemToAdd = smallRectComponent.createObject(control)
+ let itemToAdd = smallRectComponent.createObject(control)
control.insertItem(data.insertItemAtIndex, itemToAdd)
}
@@ -1572,19 +1643,19 @@ TestCase {
}
function test_removeAllItems() {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase)
verify(control)
while (control.count > 0)
- var itemToRemove = control.removeItem(control.itemAt(0))
+ control.removeItem(control.itemAt(0))
// Shouldn't crash.
}
function test_hideItems_data() {
- var splitViewWidth = testCase.width
- var splitViewHeight = testCase.height
+ let splitViewWidth = testCase.width
+ let splitViewHeight = testCase.height
- var data = [
+ let data = [
{
tag: "hideItemAtHorizontalEnd",
orientation: Qt.Horizontal,
@@ -1655,12 +1726,12 @@ TestCase {
}
function test_hideItems(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase,
{ "orientation": data.orientation })
verify(control)
- for (var i = 0; i < data.hideIndices.length; ++i) {
- var itemToHide = control.itemAt(data.hideIndices[i])
+ for (let i = 0; i < data.hideIndices.length; ++i) {
+ let itemToHide = control.itemAt(data.hideIndices[i])
verify(itemToHide)
itemToHide.visible = false
}
@@ -1671,10 +1742,10 @@ TestCase {
}
function test_hideAndShowItems_data() {
- var splitViewWidth = testCase.width
- var splitViewHeight = testCase.height
+ let splitViewWidth = testCase.width
+ let splitViewHeight = testCase.height
- var data = [
+ let data = [
{
tag: "hideLastTwoHorizontalItems",
orientation: Qt.Horizontal,
@@ -1704,12 +1775,12 @@ TestCase {
}
function test_hideAndShowItems(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase,
{ "orientation": data.orientation })
verify(control)
- for (var i = 0; i < data.hideIndices.length; ++i) {
- var itemToHide = control.itemAt(data.hideIndices[i])
+ for (let i = 0; i < data.hideIndices.length; ++i) {
+ let itemToHide = control.itemAt(data.hideIndices[i])
verify(itemToHide)
itemToHide.visible = false
}
@@ -1717,8 +1788,8 @@ TestCase {
verify(waitForItemPolished(control))
compareSizes(control, data.expectedGeometriesAfterHiding, "after hiding")
- for (i = 0; i < data.showIndices.length; ++i) {
- var itemToShow = control.itemAt(data.showIndices[i])
+ for (let i = 0; i < data.showIndices.length; ++i) {
+ let itemToShow = control.itemAt(data.showIndices[i])
verify(itemToShow)
itemToShow.visible = true
}
@@ -1728,10 +1799,10 @@ TestCase {
}
function test_moveHiddenItems_data() {
- var splitViewWidth = testCase.width
- var splitViewHeight = testCase.height
+ let splitViewWidth = testCase.width
+ let splitViewHeight = testCase.height
- var data = [
+ let data = [
{
tag: "hideSecondItemAndMoveItToFirst",
orientation: Qt.Horizontal,
@@ -1767,12 +1838,12 @@ TestCase {
}
function test_moveHiddenItems(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase,
{ "orientation": data.orientation })
verify(control)
- for (var i = 0; i < data.hideIndices.length; ++i) {
- var itemToHide = control.itemAt(data.hideIndices[i])
+ for (let i = 0; i < data.hideIndices.length; ++i) {
+ let itemToHide = control.itemAt(data.hideIndices[i])
verify(itemToHide)
itemToHide.visible = false
}
@@ -1782,8 +1853,8 @@ TestCase {
verify(waitForItemPolished(control))
compareSizes(control, data.expectedGeometriesAfterMoving, "after moving")
- for (i = 0; i < data.showIndices.length; ++i) {
- var itemToShow = control.itemAt(data.showIndices[i])
+ for (let i = 0; i < data.showIndices.length; ++i) {
+ let itemToShow = control.itemAt(data.showIndices[i])
verify(itemToShow)
itemToShow.visible = true
}
@@ -1802,10 +1873,10 @@ TestCase {
}
function test_draggingHandleInFlickable() {
- var flickable = createTemporaryObject(flickableComponent, testCase)
+ let flickable = createTemporaryObject(flickableComponent, testCase)
verify(flickable)
- var control = threeSizedItemsComponent.createObject(flickable.contentItem)
+ let control = threeSizedItemsComponent.createObject(flickable.contentItem)
verify(control)
control.anchors.fill = undefined
@@ -1816,19 +1887,19 @@ TestCase {
verify(isPolishScheduled(control))
verify(waitForItemPolished(control))
- var contentXSpy = signalSpyComponent.createObject(flickable,
+ let contentXSpy = signalSpyComponent.createObject(flickable,
{ target: flickable, signalName: "contentXChanged" })
verify(contentXSpy.valid)
- var contentYSpy = signalSpyComponent.createObject(flickable,
+ let contentYSpy = signalSpyComponent.createObject(flickable,
{ target: flickable, signalName: "contentYChanged" })
verify(contentYSpy.valid)
// Drag the first handle to the right;
// the flickable's contentX and contentY shouldn't change.
- var firstItem = control.itemAt(0)
- var firstItemOriginalWidth = firstItem.width
- var handles = findHandles(control)
- var firstHandle = handles[0]
+ let firstItem = control.itemAt(0)
+ let firstItemOriginalWidth = firstItem.width
+ let handles = findHandles(control)
+ let firstHandle = handles[0]
// Add some vertical movement in there as well.
mouseDrag(firstHandle, firstHandle.width / 2, firstHandle.height / 2, 100, 50)
compare(contentXSpy.count, 0)
@@ -1839,7 +1910,7 @@ TestCase {
control.orientation = Qt.Vertical
control.width = control.parent.width - 100
control.height = 400
- var firstItemOriginalHeight = firstItem.height
+ let firstItemOriginalHeight = firstItem.height
verify(isPolishScheduled(control))
verify(waitForItemPolished(control))
@@ -2284,14 +2355,14 @@ TestCase {
if ((Qt.platform.pluginName === "offscreen") || (Qt.platform.pluginName === "minimal"))
skip("Mouse hovering not functional on offscreen/minimal platforms")
- var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase)
verify(control)
control.anchors.margins = 50
- var handles = findHandles(control)
- var firstHandle = handles[0]
+ let handles = findHandles(control)
+ let firstHandle = handles[0]
- var handleCenter = control.mapFromItem(firstHandle, firstHandle.width / 2, firstHandle.height / 2)
+ let handleCenter = control.mapFromItem(firstHandle, firstHandle.width / 2, firstHandle.height / 2)
// Test fails if we don't do two moves for some reason...
mouseMove(control, handleCenter.x, handleCenter.y)
mouseMove(control, handleCenter.x, handleCenter.y)
@@ -2316,15 +2387,15 @@ TestCase {
if ((Qt.platform.pluginName === "offscreen") || (Qt.platform.pluginName === "minimal"))
skip("Mouse hovering not functional on offscreen/minimal platforms")
- var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase)
verify(control)
control.anchors.margins = 50
- var handles = findHandles(control)
- var firstHandle = handles[0]
+ let handles = findHandles(control)
+ let firstHandle = handles[0]
// First, ensure that the handle is hovered + pressed.
- var handleCenter = control.mapFromItem(firstHandle, firstHandle.width / 2, firstHandle.height / 2)
+ let handleCenter = control.mapFromItem(firstHandle, firstHandle.width / 2, firstHandle.height / 2)
// Test fails if we don't do two moves for some reason...
mouseMove(control, handleCenter.x, handleCenter.y)
mouseMove(control, handleCenter.x, handleCenter.y)
@@ -2342,8 +2413,8 @@ TestCase {
compare(handles.length, 1)
// No handles should be hovered/pressed.
- for (var i = 0; i < handles.length; ++i) {
- var handle = handles[i]
+ for (let i = 0; i < handles.length; ++i) {
+ let handle = handles[i]
verify(!handle.SplitHandle.hovered, "handle at index " + i + " should not be hovered")
verify(!handle.SplitHandle.pressed, "handle at index " + i + " should not be hovered")
}
@@ -2366,16 +2437,16 @@ TestCase {
}
function test_saveAndRestoreState(data) {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase, { orientation: data.orientation })
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase, { orientation: data.orientation })
verify(control)
compare(control.orientation, data.orientation)
- var lastItem = control.itemAt(2)
+ let lastItem = control.itemAt(2)
verify(lastItem)
lastItem.SplitView[data.propertyName] = data.propertyValue
// Save the state.
- var settings = createTemporaryObject(settingsComponent, testCase)
+ let settings = createTemporaryObject(settingsComponent, testCase)
verify(settings)
settings.setValue("splitView", control.saveState())
@@ -2394,11 +2465,11 @@ TestCase {
}
function test_changePreferredSizeDuringLayout() {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase)
verify(control)
- var firstItem = control.itemAt(0)
- var secondItem = control.itemAt(1)
+ let firstItem = control.itemAt(0)
+ let secondItem = control.itemAt(1)
secondItem.widthChanged.connect(function() {
if (secondItem.width < 10)
firstItem.SplitView.preferredWidth = 50
@@ -2439,28 +2510,31 @@ TestCase {
// m_ignoreNextDelayedLayoutRequest doesn't interfere with any action from
// the user that results in a delayed layout.
function test_changePreferredSizeDuringLayoutWhileDraggingHandle() {
- var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ let control = createTemporaryObject(threeSizedItemsComponent, testCase)
verify(control)
- var firstItem = control.itemAt(0)
- var secondItem = control.itemAt(1)
+ let firstItem = control.itemAt(0)
+ let secondItem = control.itemAt(1)
firstItem.widthChanged.connect(function() {
if (firstItem.width === 0)
secondItem.SplitView.preferredWidth = 50
})
// Start dragging the handle.
- var handles = findHandles(control)
- var targetHandle = handles[0]
+ let handles = findHandles(control)
+ let targetHandle = handles[0]
mousePress(targetHandle)
verify(control.resizing)
- var localPos = testCase.mapToItem(targetHandle, 15, testCase.height / 2)
+ let localPos = testCase.mapToItem(targetHandle, 15, testCase.height / 2)
// Move the handle to the very left, so that the item's width becomes zero.
mouseMove(targetHandle, -100, targetHandle.height / 2)
verify(control.resizing)
compare(firstItem.width, 0)
compare(secondItem.SplitView.preferredWidth, 50)
+ // Wait for polish so item width becomes preferredWidth
+ verify(isPolishScheduled(control))
+ verify(waitForItemPolished(control))
compare(secondItem.width, 50)
mouseRelease(targetHandle, -100, targetHandle.height / 2, Qt.LeftButton)
verify(!control.resizing)
@@ -2476,7 +2550,7 @@ TestCase {
// QTBUG-79270
function test_hideSplitViewWithOneItem() {
- var control = createTemporaryObject(oneItemComponent, testCase)
+ let control = createTemporaryObject(oneItemComponent, testCase)
verify(control)
// Shouldn't be an assertion failure.
control.visible = false
@@ -2485,7 +2559,7 @@ TestCase {
// QTBUG-79302: ensure that the Repeater's items are actually generated.
// test_dragHandle:repeater tests dragging behavior with a Repeater.
function test_repeater(data) {
- var control = createTemporaryObject(repeaterSplitViewComponent, testCase)
+ let control = createTemporaryObject(repeaterSplitViewComponent, testCase)
verify(control)
compare(control.repeater.count, 3)
compare(control.contentChildren.length, 3)
@@ -2515,15 +2589,15 @@ TestCase {
if (Qt.platform.pluginName === "offscreen" || Qt.platform.pluginName === "minimal")
skip("Mouse hovering not functional on offscreen/minimal platforms")
- var control = createTemporaryObject(hoverableChildrenSplitViewComponent, testCase)
+ let control = createTemporaryObject(hoverableChildrenSplitViewComponent, testCase)
verify(control)
verify(isPolishScheduled(control))
verify(waitForItemPolished(control))
// Move the mouse over the handle.
- var handles = findHandles(control)
- var targetHandle = handles[0]
+ let handles = findHandles(control)
+ let targetHandle = handles[0]
// Test fails if we don't do two moves for some reason... QTBUG-94968
mouseMove(targetHandle, targetHandle.width / 2, targetHandle.height / 2)
mouseMove(targetHandle, targetHandle.width / 2, targetHandle.height / 2)
@@ -2603,4 +2677,88 @@ TestCase {
verify(!firstHandle.SplitHandle.pressed)
compare(firstItem.width, 125)
}
+
+ Component {
+ id: hiddenItemComponent
+
+ SplitView {
+ anchors.fill: parent
+ handle: handleComponent
+ orientation: Qt.Horizontal
+
+ component SplitItem: Rectangle {
+ objectName: labelText
+
+ SplitView.preferredWidth: 50
+ SplitView.fillHeight: true
+
+ required property string labelText
+
+ Text {
+ anchors.fill: parent
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: `${parent.labelText} - width: ${parent.width.toFixed(2)}`
+ }
+ }
+
+ SplitItem {
+ color: "blue"
+ labelText: "View 1"
+ }
+ SplitItem {
+ color: "red"
+ labelText: "View 2 (hidden)"
+ visible: false
+ }
+ SplitItem {
+ color: "purple"
+ labelText: "View 3"
+ }
+ SplitItem {
+ color: "yellow"
+ labelText: "View 4"
+ }
+ }
+ }
+
+ function test_resizeHiddenItem() {
+ let control = createTemporaryObject(hiddenItemComponent, testCase)
+ verify(control)
+
+ const standardItemWidth = 50
+ let expectedGeometries = [
+ // First item.
+ { x: 0, y: 0, width: standardItemWidth, height: control.height },
+ // First handle.
+ { x: standardItemWidth, y: 0, width: defaultHorizontalHandleWidth, height: control.height },
+ // The second item and its handle are hidden.
+ { hidden: true },
+ { hidden: true },
+ // Third item.
+ { x: standardItemWidth + defaultHorizontalHandleWidth, y: 0, width: standardItemWidth, height: control.height },
+ // Third handle.
+ { x: (standardItemWidth * 2) + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: control.height },
+ // Fourth item.
+ { x: (standardItemWidth * 2) + (defaultHorizontalHandleWidth * 2), y: 0,
+ width: control.width - (standardItemWidth * 2) - (defaultHorizontalHandleWidth * 2), height: control.height }
+ ]
+ compareSizes(control, expectedGeometries, "before dragging handle")
+
+ // Drag the third handle to the right.
+ let handles = findHandles(control)
+ let thirdHandle = handles[2]
+ // The third (index 4 here) item should get one pixel bigger, and the fourth one pixel smaller.
+ ++expectedGeometries[4].width
+ ++expectedGeometries[5].x // handle
+ ++expectedGeometries[6].x
+ --expectedGeometries[6].width
+ // Use individual events rather than mouseDrag because that will move it past the drag threshold,
+ // which we don't want, since we only want to move by 1 pixel.
+ mousePress(thirdHandle)
+ mouseMove(thirdHandle, thirdHandle.width / 2 + 1, thirdHandle.height / 2, 16)
+ mouseRelease(thirdHandle)
+ compareSizes(control, expectedGeometries, "after dragging handle")
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_stackview.qml b/tests/auto/quickcontrols/controls/data/tst_stackview.qml
index 8584385cbf..3d304f385a 100644
--- a/tests/auto/quickcontrols/controls/data/tst_stackview.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_stackview.qml
@@ -1,13 +1,17 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma ComponentBehavior: Bound
import QtQuick
import QtTest
import QtQuick.Controls
+import QtQuick.Templates as T
import Qt.test.controls
TestCase {
id: testCase
+ objectName: name
width: 200
height: 200
visible: true
@@ -16,10 +20,10 @@ TestCase {
Item { id: item }
Component { id: textField; TextField { } }
- Component { id: component; Item { } }
+ Component { id: itemComponent; Item { } }
Component {
- id: stackView
+ id: stackViewComponent
StackView { }
}
@@ -30,60 +34,62 @@ TestCase {
Component { id: withRequired; Item { required property int i }}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- let control = createTemporaryObject(stackView, testCase)
+ function test_defaults() {
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
}
function test_initialItem() {
- var control1 = createTemporaryObject(stackView, testCase)
+ let control1 = createTemporaryObject(stackViewComponent, testCase)
verify(control1)
compare(control1.currentItem, null)
control1.destroy()
- var control2 = createTemporaryObject(stackView, testCase, {initialItem: item})
+ let control2 = createTemporaryObject(stackViewComponent, testCase, {initialItem: item})
verify(control2)
compare(control2.currentItem, item)
control2.destroy()
- var control3 = createTemporaryObject(stackView, testCase, {initialItem: component})
+ let control3 = createTemporaryObject(stackViewComponent, testCase, {initialItem: itemComponent})
verify(control3)
verify(control3.currentItem)
control3.destroy()
}
function test_currentItem() {
- var control = createTemporaryObject(stackView, testCase, {initialItem: item})
+ let control = createTemporaryObject(stackViewComponent, testCase, {initialItem: item})
verify(control)
compare(control.currentItem, item)
- control.push(component)
+ control.push(itemComponent)
verify(control.currentItem !== item)
control.pop(StackView.Immediate)
compare(control.currentItem, item)
}
function test_busy() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
compare(control.busy, false)
- var busyCount = 0
- var busySpy = signalSpy.createObject(control, {target: control, signalName: "busyChanged"})
+ let busyCount = 0
+ let busySpy = signalSpy.createObject(control, {target: control, signalName: "busyChanged"})
verify(busySpy.valid)
- control.push(component)
+ control.push(itemComponent)
compare(control.busy, false)
compare(busySpy.count, busyCount)
- control.push(component)
+ control.push(itemComponent)
compare(control.busy, true)
compare(busySpy.count, ++busyCount)
tryCompare(control, "busy", false)
compare(busySpy.count, ++busyCount)
- control.replace(component)
+ control.replace(itemComponent)
compare(control.busy, true)
compare(busySpy.count, ++busyCount)
tryCompare(control, "busy", false)
@@ -98,14 +104,14 @@ TestCase {
control.pushEnter = null
control.pushExit = null
- control.push(component)
+ control.push(itemComponent)
compare(control.busy, false)
compare(busySpy.count, busyCount)
control.replaceEnter = null
control.replaceExit = null
- control.replace(component)
+ control.replace(itemComponent)
compare(control.busy, false)
compare(busySpy.count, busyCount)
@@ -118,15 +124,15 @@ TestCase {
}
function test_status() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var item1 = component.createObject(control)
+ let item1 = itemComponent.createObject(control)
compare(item1.StackView.status, StackView.Inactive)
control.push(item1)
compare(item1.StackView.status, StackView.Active)
- var item2 = component.createObject(control)
+ let item2 = itemComponent.createObject(control)
compare(item2.StackView.status, StackView.Inactive)
control.push(item2)
compare(item2.StackView.status, StackView.Activating)
@@ -142,15 +148,15 @@ TestCase {
}
function test_index() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var item1 = component.createObject(control)
+ let item1 = itemComponent.createObject(control)
compare(item1.StackView.index, -1)
control.push(item1, StackView.Immediate)
compare(item1.StackView.index, 0)
- var item2 = component.createObject(control)
+ let item2 = itemComponent.createObject(control)
compare(item2.StackView.index, -1)
control.push(item2, StackView.Immediate)
compare(item2.StackView.index, 1)
@@ -162,15 +168,15 @@ TestCase {
}
function test_view() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var item1 = component.createObject(control)
+ let item1 = itemComponent.createObject(control)
compare(item1.StackView.view, null)
control.push(item1, StackView.Immediate)
compare(item1.StackView.view, control)
- var item2 = component.createObject(control)
+ let item2 = itemComponent.createObject(control)
compare(item2.StackView.view, null)
control.push(item2, StackView.Immediate)
compare(item2.StackView.view, control)
@@ -182,13 +188,13 @@ TestCase {
}
function test_depth() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var depthChanges = 0
- var emptyChanges = 0
- var depthSpy = signalSpy.createObject(control, {target: control, signalName: "depthChanged"})
- var emptySpy = signalSpy.createObject(control, {target: control, signalName: "emptyChanged"})
+ let depthChanges = 0
+ let emptyChanges = 0
+ let depthSpy = signalSpy.createObject(control, {target: control, signalName: "depthChanged"})
+ let emptySpy = signalSpy.createObject(control, {target: control, signalName: "emptyChanged"})
verify(depthSpy.valid)
verify(emptySpy.valid)
compare(control.depth, 0)
@@ -206,25 +212,25 @@ TestCase {
compare(control.empty, true)
compare(emptySpy.count, ++emptyChanges)
- control.push(component, StackView.Immediate)
+ control.push(itemComponent, StackView.Immediate)
compare(control.depth, 1)
compare(depthSpy.count, ++depthChanges)
compare(control.empty, false)
compare(emptySpy.count, ++emptyChanges)
- control.push(component, StackView.Immediate)
+ control.push(itemComponent, StackView.Immediate)
compare(control.depth, 2)
compare(depthSpy.count, ++depthChanges)
compare(control.empty, false)
compare(emptySpy.count, emptyChanges)
- control.replace(component, StackView.Immediate)
+ control.replace(itemComponent, StackView.Immediate)
compare(control.depth, 2)
compare(depthSpy.count, depthChanges)
compare(control.empty, false)
compare(emptySpy.count, emptyChanges)
- control.replace([component, component], StackView.Immediate)
+ control.replace([itemComponent, itemComponent], StackView.Immediate)
compare(control.depth, 3)
compare(depthSpy.count, ++depthChanges)
compare(control.empty, false)
@@ -253,12 +259,21 @@ TestCase {
compare(depthSpy.count, depthChanges)
compare(control.empty, true)
compare(emptySpy.count, emptyChanges)
+
+ control.push(item, StackView.PushTransition)
+ compare(depthSpy.count, ++depthChanges)
+ compare(emptySpy.count, ++emptyChanges)
+ compare(control.depth, 1)
+ control.clear(StackView.PopTransition)
+ compare(depthSpy.count, ++depthChanges)
+ compare(emptySpy.count, ++emptyChanges)
+ compare(control.depth, 0)
}
function test_size() {
- var container = createTemporaryObject(component, testCase, {width: 200, height: 200})
+ let container = createTemporaryObject(itemComponent, testCase, {width: 200, height: 200})
verify(container)
- var control = stackView.createObject(container, {width: 100, height: 100})
+ let control = stackViewComponent.createObject(container, {width: 100, height: 100})
verify(control)
container.width += 10
@@ -286,7 +301,7 @@ TestCase {
compare(item.height, control.height)
}
- function test_focus_data() {
+ function test_focus_data() : var {
return [
{ tag: "true", focus: true, forceActiveFocus: false },
{ tag: "false", focus: false, forceActiveFocus: false },
@@ -294,8 +309,8 @@ TestCase {
]
}
- function test_focus(data) {
- var control = createTemporaryObject(stackView, testCase, {initialItem: item, width: 200, height: 200})
+ function test_focus(data: var) {
+ let control = createTemporaryObject(stackViewComponent, testCase, {initialItem: item, width: 200, height: 200})
verify(control)
if (data.focus)
@@ -304,7 +319,7 @@ TestCase {
control.forceActiveFocus()
compare(control.activeFocus, data.focus || data.forceActiveFocus)
- var page = control.push(textField, StackView.Immediate)
+ let page = control.push(textField, StackView.Immediate)
verify(page)
compare(control.currentItem, page)
compare(page.activeFocus, control.activeFocus)
@@ -316,12 +331,12 @@ TestCase {
}
function test_find() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var item1 = component.createObject(control, {objectName: "1"})
- var item2 = component.createObject(control, {objectName: "2"})
- var item3 = component.createObject(control, {objectName: "3"})
+ let item1 = itemComponent.createObject(control, {objectName: "1"})
+ let item2 = itemComponent.createObject(control, {objectName: "2"})
+ let item3 = itemComponent.createObject(control, {objectName: "3"})
control.push(item1, StackView.Immediate)
control.push(item2, StackView.Immediate)
@@ -341,10 +356,10 @@ TestCase {
}
function test_get() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- control.push([item, component, component], StackView.Immediate)
+ control.push([item, itemComponent, itemComponent], StackView.Immediate)
verify(control.get(0, StackView.DontLoad))
compare(control.get(0, StackView.ForceLoad), item)
@@ -355,8 +370,10 @@ TestCase {
verify(control.get(2, StackView.ForceLoad))
}
+ property bool qmlProperty
+
function test_push() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
// missing arguments
@@ -372,58 +389,163 @@ TestCase {
control.push(Qt.createQmlObject('import QtQml; QtObject { }', control))
// push(item)
- var item1 = component.createObject(control, {objectName:"1"})
+ let item1 = itemComponent.createObject(control, {objectName:"1"})
compare(control.push(item1, StackView.Immediate), item1)
compare(control.depth, 1)
compare(control.currentItem, item1)
// push([item])
- var item2 = component.createObject(control, {objectName:"2"})
+ let item2 = itemComponent.createObject(control, {objectName:"2"})
compare(control.push([item2], StackView.Immediate), item2)
compare(control.depth, 2)
compare(control.currentItem, item2)
// push(item, {properties})
- var item3 = component.createObject(control)
+ let item3 = itemComponent.createObject(control)
compare(control.push(item3, {objectName:"3"}, StackView.Immediate), item3)
compare(item3.objectName, "3")
compare(control.depth, 3)
compare(control.currentItem, item3)
// push([item, {properties}])
- var item4 = component.createObject(control)
+ let item4 = itemComponent.createObject(control)
compare(control.push([item4, {objectName:"4"}], StackView.Immediate), item4)
compare(item4.objectName, "4")
compare(control.depth, 4)
compare(control.currentItem, item4)
// push(component, {properties})
- var item5 = control.push(component, {objectName:"5"}, StackView.Immediate)
+ let item5 = control.push(itemComponent, {objectName:"5"}, StackView.Immediate)
compare(item5.objectName, "5")
compare(control.depth, 5)
compare(control.currentItem, item5)
// push([component, {properties}])
- var item6 = control.push([component, {objectName:"6"}], StackView.Immediate)
+ let item6 = control.push([itemComponent, {objectName:"6"}], StackView.Immediate)
compare(item6.objectName, "6")
compare(control.depth, 6)
compare(control.currentItem, item6)
+
+ // push([component, {binding}]) - with JS variable in binding
+ let jsVariable = false
+ let item7 = control.push([itemComponent, {objectName: Qt.binding(() => {
+ return jsVariable.toString() })}], StackView.Immediate)
+ compare(item7.objectName, "false")
+ compare(control.depth, 7)
+ compare(control.currentItem, item7)
+ jsVariable = true
+ expectFailContinue("", "QTBUG-114959")
+ compare(item7.objectName, "true")
+
+ // push([component, {binding}]) - with QML property in binding
+ qmlProperty = false
+ let item8 = control.push([itemComponent, {objectName: Qt.binding(() => {
+ return testCase.qmlProperty.toString() })}], StackView.Immediate)
+ compare(item8.objectName, "false")
+ compare(control.depth, 8)
+ compare(control.currentItem, item8)
+ qmlProperty = true
+ compare(item8.objectName, "true")
+ }
+
+ function test_pushNew() {
+ let control = createTemporaryObject(stackViewComponent, testCase)
+ verify(control)
+
+ // Passing the wrong type to a strongly-typed function results in an exception.
+ let exceptionThrown = false
+ try {
+ ignoreWarning(/Could not convert argument 0 at/)
+ const stackTraceLineCount = 5
+ for (let i = 0; i < stackTraceLineCount; ++i)
+ ignoreWarning(/.*@.*qml/)
+ control.pushItem(Qt.createQmlObject('import QtQml; QtObject { }', control))
+ } catch (e) {
+ exceptionThrown = true
+ }
+ verify(exceptionThrown)
+ compare(control.depth, 0)
+
+ // pushItem(item)
+ let item1 = itemComponent.createObject(control, {objectName:"1"})
+ compare(control.pushItem(item1, {}, StackView.Immediate), item1)
+ compare(control.depth, 1)
+ compare(control.currentItem, item1)
+
+ // pushItems([item])
+ let item2 = itemComponent.createObject(control, {objectName:"2"})
+ compare(control.pushItems([item2], StackView.Immediate), item2)
+ compare(control.depth, 2)
+ compare(control.currentItem, item2)
+
+ // pushItems([item, component, url])
+ let item3 = itemComponent.createObject(control)
+ let actualCurrent = control.pushItems([item3, itemComponent, Qt.resolvedUrl("stackview/Rect.qml")], StackView.Immediate)
+ let expectedCurrent = control.get(control.depth - 1, StackView.DontLoad)
+ compare(actualCurrent, expectedCurrent)
+ compare(control.depth, 5)
+ compare(control.currentItem, expectedCurrent)
+
+ // pushItems([item, {properties}])
+ let item4 = itemComponent.createObject(control)
+ compare(control.pushItems([item4, {objectName:"4"}], StackView.Immediate), item4)
+ compare(item4.objectName, "4")
+ compare(control.depth, 6)
+ compare(control.currentItem, item4)
+
+ // pushItems([item, {properties}, component, {properties}, url, {properties}])
+ let item5 = itemComponent.createObject(control)
+ let item7 = control.pushItems([
+ item5, {objectName: "object5"},
+ itemComponent, {objectName: "object6"},
+ Qt.resolvedUrl("stackview/Rect.qml"), {objectName: "object7"}
+ ],
+ StackView.Immediate)
+ item5 = control.get(control.depth - 3, StackView.ForceLoad)
+ let item6 = control.get(control.depth - 2, StackView.ForceLoad)
+ compare(item7, control.get(control.depth - 1, StackView.ForceLoad))
+ compare(item5.objectName, "object5")
+ compare(item6.objectName, "object6")
+ compare(item7.objectName, "object7")
+ compare(control.depth, 9)
+ compare(control.currentItem, item7)
+
+ // pushItems([component, {binding}]) - with JS variable in binding
+ let jsVariable = false
+ let item8 = control.pushItems([itemComponent, {objectName: Qt.binding(() => {
+ return jsVariable.toString() })}], StackView.Immediate)
+ compare(item8.objectName, "false")
+ compare(control.depth, 10)
+ compare(control.currentItem, item8)
+ jsVariable = true
+ expectFailContinue("", "QTBUG-114959")
+ compare(item8.objectName, "true")
+
+ // pushItems([component, {binding}]) - with QML property in binding
+ qmlProperty = false
+ let item9 = control.pushItems([itemComponent, {objectName: Qt.binding(() => {
+ return testCase.qmlProperty.toString() })}], StackView.Immediate)
+ compare(item9.objectName, "false")
+ compare(control.depth, 11)
+ compare(control.currentItem, item9)
+ qmlProperty = true
+ compare(item9.objectName, "true")
}
// Escape special Regexp characters with a '\' (backslash) prefix so that \a str can be
// used as a Regexp pattern.
- function escapeRegExp(str) {
+ function escapeRegExp(str: string) {
// "$&" is the last matched substring
return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
function test_pop() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var items = []
- for (var i = 0; i < 7; ++i)
- items.push(component.createObject(control, {objectName:i}))
+ let items = []
+ for (let i = 0; i < 7; ++i)
+ items.push(itemComponent.createObject(control, {objectName:i}))
control.push(items, StackView.Immediate)
@@ -451,7 +573,7 @@ TestCase {
compare(control.currentItem, items[2])
// don't pop non-existent item
- ignoreWarning(new RegExp(".*QML StackView: pop: unknown argument: " + escapeRegExp(testCase.toString())))
+ ignoreWarning(new RegExp(".*QML StackView: pop: can't find item to pop: " + escapeRegExp(testCase.toString())))
compare(control.pop(testCase, StackView.Immediate), null)
compare(control.depth, 3)
compare(control.currentItem, items[2])
@@ -462,8 +584,75 @@ TestCase {
compare(control.currentItem, items[0])
}
+ function test_popNew() {
+ let control = createTemporaryObject(stackViewComponent, testCase)
+ verify(control)
+
+ let items = []
+ for (let i = 0; i < 7; ++i)
+ items.push(itemComponent.createObject(control, {objectName:i}))
+
+ compare(control.pushItems(items, StackView.Immediate), items[6])
+ compare(control.depth, 7)
+
+ // pop the top most item
+ compare(control.popCurrentItem(StackView.Immediate), items[6])
+ compare(control.depth, 6)
+ compare(control.currentItem, items[5])
+
+ // pop down to the current item (does nothing as it's already the top-most item)
+ compare(control.popToItem(control.currentItem, StackView.Immediate), null)
+ compare(control.depth, 6)
+ compare(control.currentItem, items[5])
+
+ // pop down to (but not including) the Nth item
+ compare(control.popToItem(items[3], StackView.Immediate), items[5])
+ compare(control.depth, 4)
+ compare(control.currentItem, items[3])
+
+ // pop the top most item
+ compare(control.popCurrentItem(StackView.Immediate), items[3])
+ compare(control.depth, 3)
+ compare(control.currentItem, items[2])
+
+ // don't pop non-existent item
+ ignoreWarning(new RegExp(".*QML StackView: pop: can't find item to pop: TestCase.*"))
+ compare(control.popToItem(testCase, StackView.Immediate), null)
+ compare(control.depth, 3)
+ compare(control.currentItem, items[2])
+
+ // The new functions don't support passing null.
+ ignoreWarning(new RegExp(".*QML StackView: pop: item cannot be null"))
+ compare(control.popToItem(null, StackView.Immediate), null)
+ compare(control.depth, 3)
+ compare(control.currentItem, items[2])
+
+ // Test that popToIndex pops down to an Nth item.
+ control.clear()
+ compare(control.depth, 0)
+
+ items = []
+ for (let i = 0; i < 3; ++i)
+ items.push(itemComponent.createObject(control, { objectName: i }))
+ compare(control.pushItems(items, StackView.Immediate), items[2])
+ compare(control.depth, 3)
+
+ compare(control.popToIndex(1, StackView.Immediate), items[2])
+ compare(control.depth, 2)
+
+ compare(control.popToIndex(0, StackView.Immediate), items[1])
+ compare(control.depth, 1)
+
+ // Also check that popCurrentItem still pops when depth is 1,
+ // because pop() doesn't, and we don't want that behavior with the new function.
+ compare(control.popCurrentItem(StackView.Immediate), items[0])
+
+ ignoreWarning(new RegExp(".*QML StackView: pop: no items to pop"))
+ compare(control.popCurrentItem(StackView.Immediate), null)
+ }
+
function test_replace() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
// missing arguments
@@ -479,71 +668,116 @@ TestCase {
compare(control.replace(Qt.createQmlObject('import QtQml; QtObject { }', control)), null)
// replace(item)
- var item1 = component.createObject(control, {objectName:"1"})
+ let item1 = itemComponent.createObject(control, {objectName:"1"})
compare(control.replace(item1, StackView.Immediate), item1)
compare(control.depth, 1)
compare(control.currentItem, item1)
// replace([item])
- var item2 = component.createObject(control, {objectName:"2"})
+ let item2 = itemComponent.createObject(control, {objectName:"2"})
compare(control.replace([item2], StackView.Immediate), item2)
compare(control.depth, 1)
compare(control.currentItem, item2)
// replace(item, {properties})
- var item3 = component.createObject(control)
+ let item3 = itemComponent.createObject(control)
compare(control.replace(item3, {objectName:"3"}, StackView.Immediate), item3)
compare(item3.objectName, "3")
compare(control.depth, 1)
compare(control.currentItem, item3)
// replace([item, {properties}])
- var item4 = component.createObject(control)
+ let item4 = itemComponent.createObject(control)
compare(control.replace([item4, {objectName:"4"}], StackView.Immediate), item4)
compare(item4.objectName, "4")
compare(control.depth, 1)
compare(control.currentItem, item4)
// replace(component, {properties})
- var item5 = control.replace(component, {objectName:"5"}, StackView.Immediate)
+ let item5 = control.replace(itemComponent, {objectName:"5"}, StackView.Immediate)
compare(item5.objectName, "5")
compare(control.depth, 1)
compare(control.currentItem, item5)
// replace([component, {properties}])
- var item6 = control.replace([component, {objectName:"6"}], StackView.Immediate)
+ let item6 = control.replace([itemComponent, {objectName:"6"}], StackView.Immediate)
compare(item6.objectName, "6")
compare(control.depth, 1)
compare(control.currentItem, item6)
// replace the topmost item
- control.push(component)
+ control.push(itemComponent)
compare(control.depth, 2)
- var item7 = control.replace(control.get(1), component, StackView.Immediate)
+ let item7 = control.replace(control.get(1), itemComponent, StackView.Immediate)
compare(control.depth, 2)
compare(control.currentItem, item7)
// replace the item in the middle
- control.push(component)
- control.push(component)
- control.push(component)
+ control.push(itemComponent)
+ control.push(itemComponent)
+ control.push(itemComponent)
compare(control.depth, 5)
- var item8 = control.replace(control.get(2), component, StackView.Immediate)
+ let item8 = control.replace(control.get(2), itemComponent, StackView.Immediate)
compare(control.depth, 3)
compare(control.currentItem, item8)
}
+ function test_replaceNew() {
+ let control = createTemporaryObject(stackViewComponent, testCase)
+ verify(control)
+
+ // replace(item) - replace currentItem
+ let item1 = itemComponent.createObject(control, {objectName:"1"})
+ compare(control.replaceCurrentItem(item1, {}, StackView.Immediate), item1)
+ compare(control.depth, 1)
+ compare(control.currentItem, item1)
+
+ // replace([item]) - replace currentItem
+ let item2 = itemComponent.createObject(control, {objectName:"2"})
+ compare(control.replaceCurrentItem(item2, {}, StackView.Immediate), item2)
+ compare(control.depth, 1)
+ compare(control.currentItem, item2)
+
+ // replace(item, {properties}) - replace currentItem
+ let item3 = itemComponent.createObject(control)
+ compare(control.replaceCurrentItem(item3, {objectName:"3"}, StackView.Immediate), item3)
+ compare(item3.objectName, "3")
+ compare(control.depth, 1)
+ compare(control.currentItem, item3)
+
+ // replace([item, {properties}]) - replace currentItem
+ let item4 = itemComponent.createObject(control)
+ compare(control.replaceCurrentItem([item4, {objectName:"4"}], StackView.Immediate), item4)
+ compare(item4.objectName, "4")
+ compare(control.depth, 1)
+ compare(control.currentItem, item4)
+
+ // replace(component, {properties}) - replace currentItem
+ let item5 = control.replaceCurrentItem(itemComponent, {objectName:"5"}, StackView.Immediate)
+ compare(control.currentItem, item5)
+ compare(item5.objectName, "5")
+ compare(control.depth, 1)
+ compare(control.currentItem, item5)
+
+ // replace([component, {properties}]) - replace currentItem
+ let item6 = control.replaceCurrentItem([itemComponent, {objectName:"6"}], StackView.Immediate)
+ compare(control.currentItem, item6)
+ compare(item6.objectName, "6")
+ compare(control.depth, 1)
+ compare(control.currentItem, item6)
+ }
+
function test_clear() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- control.push(component, StackView.Immediate)
+ control.push(itemComponent, StackView.Immediate)
control.clear()
compare(control.depth, 0)
compare(control.busy, false)
- control.push(component, StackView.Immediate)
+ control.push(itemComponent, StackView.Immediate)
control.clear(StackView.PopTransition)
compare(control.depth, 0)
@@ -551,22 +785,22 @@ TestCase {
tryCompare(control, "busy", false)
}
- function test_visibility_data() {
+ function test_visibility_data() : var {
return [
{tag:"default transitions", properties: {}},
{tag:"null transitions", properties: {pushEnter: null, pushExit: null, popEnter: null, popExit: null}}
]
}
- function test_visibility(data) {
- var control = createTemporaryObject(stackView, testCase, data.properties)
+ function test_visibility(data: var) {
+ let control = createTemporaryObject(stackViewComponent, testCase, data.properties)
verify(control)
- var item1 = component.createObject(control)
+ let item1 = itemComponent.createObject(control)
control.push(item1, StackView.Immediate)
verify(item1.visible)
- var item2 = component.createObject(control)
+ let item2 = itemComponent.createObject(control)
control.push(item2)
tryCompare(item1, "visible", false)
verify(item2.visible)
@@ -579,6 +813,7 @@ TestCase {
Component {
id: transitionView
StackView {
+ id: stackView
property int popEnterRuns
property int popExitRuns
property int pushEnterRuns
@@ -587,32 +822,32 @@ TestCase {
property int replaceExitRuns
popEnter: Transition {
PauseAnimation { duration: 1 }
- onRunningChanged: if (!running) ++popEnterRuns
+ onRunningChanged: if (!running) ++stackView.popEnterRuns
}
popExit: Transition {
PauseAnimation { duration: 1 }
- onRunningChanged: if (!running) ++popExitRuns
+ onRunningChanged: if (!running) ++stackView.popExitRuns
}
pushEnter: Transition {
PauseAnimation { duration: 1 }
- onRunningChanged: if (!running) ++pushEnterRuns
+ onRunningChanged: if (!running) ++stackView.pushEnterRuns
}
pushExit: Transition {
PauseAnimation { duration: 1 }
- onRunningChanged: if (!running) ++pushExitRuns
+ onRunningChanged: if (!running) ++stackView.pushExitRuns
}
replaceEnter: Transition {
PauseAnimation { duration: 1 }
- onRunningChanged: if (!running) ++replaceEnterRuns
+ onRunningChanged: if (!running) ++stackView.replaceEnterRuns
}
replaceExit: Transition {
PauseAnimation { duration: 1 }
- onRunningChanged: if (!running) ++replaceExitRuns
+ onRunningChanged: if (!running) ++stackView.replaceExitRuns
}
}
}
- function test_transitions_data() {
+ function test_transitions_data() : var {
return [
{ tag: "undefined", operation: undefined,
pushEnterRuns: [1,2,2,2], pushExitRuns: [0,1,1,1], replaceEnterRuns: [0,0,1,1], replaceExitRuns: [0,0,1,1], popEnterRuns: [0,0,0,1], popExitRuns: [0,0,0,1] },
@@ -627,11 +862,11 @@ TestCase {
]
}
- function test_transitions(data) {
- var control = createTemporaryObject(transitionView, testCase)
+ function test_transitions(data: var) {
+ let control = createTemporaryObject(transitionView, testCase)
verify(control)
- control.push(component, data.operation)
+ control.push(itemComponent, data.operation)
tryCompare(control, "busy", false)
compare(control.pushEnterRuns, data.pushEnterRuns[0])
compare(control.pushExitRuns, data.pushExitRuns[0])
@@ -640,7 +875,7 @@ TestCase {
compare(control.popEnterRuns, data.popEnterRuns[0])
compare(control.popExitRuns, data.popExitRuns[0])
- control.push(component, data.operation)
+ control.push(itemComponent, data.operation)
tryCompare(control, "busy", false)
compare(control.pushEnterRuns, data.pushEnterRuns[1])
compare(control.pushExitRuns, data.pushExitRuns[1])
@@ -649,7 +884,7 @@ TestCase {
compare(control.popEnterRuns, data.popEnterRuns[1])
compare(control.popExitRuns, data.popExitRuns[1])
- control.replace(component, data.operation)
+ control.replace(itemComponent, data.operation)
tryCompare(control, "busy", false)
compare(control.pushEnterRuns, data.pushEnterRuns[2])
compare(control.pushExitRuns, data.pushExitRuns[2])
@@ -677,7 +912,7 @@ TestCase {
TestItem { }
}
- function test_ownership_data() {
+ function test_ownership_data() : var {
return [
{tag:"item, transition", arg: indestructibleItem, operation: StackView.Transition, destroyed: false},
{tag:"item, immediate", arg: indestructibleItem, operation: StackView.Immediate, destroyed: false},
@@ -688,15 +923,15 @@ TestCase {
]
}
- function test_ownership(data) {
- var control = createTemporaryObject(transitionView, testCase, {initialItem: component})
+ function test_ownership(data: var) {
+ let control = createTemporaryObject(transitionView, testCase, {initialItem: itemComponent})
verify(control)
// push-pop
control.push(data.arg, StackView.Immediate)
verify(control.currentItem)
verify(control.currentItem.hasOwnProperty("destroyedCallback"))
- var destroyed = false
+ let destroyed = false
control.currentItem.destroyedCallback = function() { destroyed = true }
control.pop(data.operation)
tryCompare(control, "busy", false)
@@ -709,7 +944,7 @@ TestCase {
verify(control.currentItem.hasOwnProperty("destroyedCallback"))
destroyed = false
control.currentItem.destroyedCallback = function() { destroyed = true }
- control.replace(component, data.operation)
+ control.replace(itemComponent, data.operation)
tryCompare(control, "busy", false)
wait(0) // deferred delete
compare(destroyed, data.destroyed)
@@ -725,17 +960,17 @@ TestCase {
}
function test_destroyOnRemoved() {
- var control = createTemporaryObject(stackView, testCase, { initialItem: component })
+ let control = createTemporaryObject(stackViewComponent, testCase, { initialItem: itemComponent })
verify(control)
- var item = removeComponent.createObject(control)
+ let item = removeComponent.createObject(control)
verify(item)
- var removedSpy = signalSpy.createObject(control, { target: item.StackView, signalName: "removed" })
+ let removedSpy = signalSpy.createObject(control, { target: item.StackView, signalName: "removed" })
verify(removedSpy)
verify(removedSpy.valid)
- var destructionSpy = signalSpy.createObject(control, { target: item.Component, signalName: "destruction" })
+ let destructionSpy = signalSpy.createObject(control, { target: item.Component, signalName: "destruction" })
verify(destructionSpy)
verify(destructionSpy.valid)
@@ -760,7 +995,7 @@ TestCase {
// push-replace
control.push(item, StackView.Immediate)
compare(control.currentItem, item)
- control.replace(component, StackView.Transition)
+ control.replace(itemComponent, StackView.Transition)
item = null
tryCompare(removedSpy, "count", 2)
tryCompare(destructionSpy, "count", 2)
@@ -768,15 +1003,15 @@ TestCase {
}
function test_pushOnRemoved() {
- var control = createTemporaryObject(stackView, testCase, { initialItem: component })
+ let control = createTemporaryObject(stackViewComponent, testCase, { initialItem: itemComponent })
verify(control)
- var item = control.push(component, StackView.Immediate)
+ let item = control.push(itemComponent, StackView.Immediate)
verify(item)
item.StackView.onRemoved.connect(function() {
ignoreWarning(/.*QML StackView: cannot push while already in the process of completing a pop/)
- control.push(component, StackView.Immediate)
+ control.push(itemComponent, StackView.Immediate)
})
// don't crash (QTBUG-62153)
@@ -787,13 +1022,13 @@ TestCase {
id: attachedItem
Item {
property int index: StackView.index
- property StackView view: StackView.view
+ property T.StackView view: StackView.view
property int status: StackView.status
}
}
function test_attached() {
- var control = createTemporaryObject(stackView, testCase, {initialItem: attachedItem})
+ let control = createTemporaryObject(stackViewComponent, testCase, {initialItem: attachedItem})
compare(control.get(0).index, 0)
compare(control.get(0).view, control)
@@ -825,18 +1060,19 @@ TestCase {
}
function test_interaction() {
- var control = createTemporaryObject(stackView, testCase, {initialItem: testButton, width: testCase.width, height: testCase.height})
+ let control = createTemporaryObject(stackViewComponent, testCase,
+ {initialItem: testButton, width: testCase.width, height: testCase.height})
verify(control)
- var firstButton = control.currentItem
+ let firstButton = control.currentItem
verify(firstButton)
- var firstClicks = 0
- var secondClicks = 0
- var thirdClicks = 0
+ let firstClicks = 0
+ let secondClicks = 0
+ let thirdClicks = 0
// push - default transition
- var secondButton = control.push(testButton)
+ let secondButton = control.push(testButton)
compare(control.busy, true)
mouseClick(firstButton) // filtered while busy
mouseClick(secondButton) // filtered while busy
@@ -847,7 +1083,7 @@ TestCase {
compare(secondButton.clicks, ++secondClicks)
// replace - default transition
- var thirdButton = control.replace(testButton)
+ let thirdButton = control.replace(testButton)
compare(control.busy, true)
mouseClick(secondButton) // filtered while busy
mouseClick(thirdButton) // filtered while busy
@@ -941,10 +1177,11 @@ TestCase {
// QTBUG-50305
function test_events() {
- var control = createTemporaryObject(stackView, testCase, {initialItem: mouseArea, width: testCase.width, height: testCase.height})
+ let control = createTemporaryObject(stackViewComponent, testCase,
+ {initialItem: mouseArea, width: testCase.width, height: testCase.height})
verify(control)
- var testItem = control.currentItem
+ let testItem = control.currentItem
verify(testItem)
testItem.doubleClicked.connect(function() {
@@ -961,10 +1198,11 @@ TestCase {
}
function test_ungrab() {
- var control = createTemporaryObject(stackView, testCase, {initialItem: mouseArea, width: testCase.width, height: testCase.height})
+ let control = createTemporaryObject(stackViewComponent, testCase,
+ {initialItem: mouseArea, width: testCase.width, height: testCase.height})
verify(control)
- var testItem = control.currentItem
+ let testItem = control.currentItem
verify(testItem)
mousePress(testItem)
@@ -981,7 +1219,7 @@ TestCase {
}
function test_failures() {
- var control = createTemporaryObject(stackView, testCase, {initialItem: component})
+ let control = createTemporaryObject(stackViewComponent, testCase, {initialItem: itemComponent})
verify(control)
ignoreWarning("QQmlComponent: Component is not ready")
@@ -1010,10 +1248,10 @@ TestCase {
}
function test_properties() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var rect = control.push(rectangle, {color: "#ff0000"})
+ let rect = control.push(rectangle, {color: "#ff0000"})
compare(rect.color, "#ff0000")
compare(rect.initialColor, "#ff0000")
}
@@ -1030,10 +1268,10 @@ TestCase {
}
function test_signals() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var item1 = signalTest.createObject(control)
+ let item1 = signalTest.createObject(control)
compare(item1.StackView.status, StackView.Inactive)
control.push(item1)
compare(item1.StackView.status, StackView.Active)
@@ -1042,7 +1280,7 @@ TestCase {
compare(item1.deactivatedSpy.count, 0)
compare(item1.deactivatingSpy.count, 0)
- var item2 = signalTest.createObject(control)
+ let item2 = signalTest.createObject(control)
compare(item2.StackView.status, StackView.Inactive)
control.push(item2)
compare(item2.StackView.status, StackView.Activating)
@@ -1074,11 +1312,12 @@ TestCase {
// QTBUG-56158
function test_repeatedPop() {
- var control = createTemporaryObject(stackView, testCase, {initialItem: component, width: testCase.width, height: testCase.height})
+ let control = createTemporaryObject(stackViewComponent, testCase,
+ {initialItem: itemComponent, width: testCase.width, height: testCase.height})
verify(control)
- for (var i = 0; i < 12; ++i)
- control.push(component)
+ for (let i = 0; i < 12; ++i)
+ control.push(itemComponent)
tryCompare(control, "busy", false)
while (control.depth > 1) {
@@ -1089,7 +1328,7 @@ TestCase {
}
function test_pushSameItem() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
control.push(item, StackView.Immediate)
@@ -1103,27 +1342,27 @@ TestCase {
compare(control.depth, 1)
// Push a component so that it becomes current.
- var current = control.push(component, StackView.Immediate)
+ let current = control.push(itemComponent, StackView.Immediate)
compare(control.currentItem, current)
compare(control.depth, 2)
// Push a bunch of stuff. "item" is already in the stack, so it should be ignored.
- current = control.push(component, item, StackView.Immediate)
+ current = control.push(itemComponent, item, StackView.Immediate)
verify(current !== item)
compare(control.currentItem, current)
compare(control.depth, 3)
}
function test_visible() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var item1 = component.createObject(control)
+ let item1 = itemComponent.createObject(control)
control.push(item1, StackView.Immediate)
compare(item1.visible, true)
compare(item1.StackView.visible, item1.visible)
- var item2 = component.createObject(control)
+ let item2 = itemComponent.createObject(control)
control.push(item2, StackView.Immediate)
compare(item1.visible, false)
compare(item2.visible, true)
@@ -1132,7 +1371,7 @@ TestCase {
// keep explicitly visible
item2.StackView.visible = true
- control.push(component, StackView.Immediate)
+ control.push(itemComponent, StackView.Immediate)
compare(item2.visible, true)
compare(item2.StackView.visible, true)
@@ -1166,26 +1405,26 @@ TestCase {
}
function test_resolveInitialItem() {
- var control = createTemporaryObject(stackView, testCase, {initialItem: "TestItem.qml"})
+ let control = createTemporaryObject(stackViewComponent, testCase, {initialItem: "TestItem.qml"})
verify(control)
verify(control.currentItem)
}
function test_resolve() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
- var item = control.push("TestItem.qml")
+ let item = control.push("TestItem.qml")
compare(control.depth, 1)
verify(item)
}
// QTBUG-65084
function test_mouseArea() {
- var ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
+ let ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
verify(ma)
- var control = stackView.createObject(ma, {width: testCase.width, height: testCase.height})
+ let control = stackViewComponent.createObject(ma, {width: testCase.width, height: testCase.height})
verify(control)
mousePress(control)
@@ -1194,7 +1433,7 @@ TestCase {
mouseRelease(control)
verify(!ma.pressed)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control).commit()
verify(ma.pressed)
@@ -1204,14 +1443,14 @@ TestCase {
// Separate function to ensure that the temporary value created to hold the return value of the Qt.createComponent()
// call is out of scope when the caller calls gc().
- function stackViewFactory()
+ function stackViewFactory() : T.StackView
{
- return createTemporaryObject(stackView, testCase, {initialItem: Qt.createComponent("TestItem.qml")})
+ return createTemporaryObject(stackViewComponent, testCase, {initialItem: Qt.createComponent("TestItem.qml")})
}
function test_initalItemOwnership()
{
- var control = stackViewFactory()
+ let control = stackViewFactory()
verify(control)
gc()
verify(control.initialItem)
@@ -1237,7 +1476,7 @@ TestCase {
// We don't actually do this on destruction because destruction is delayed.
// Rather, we do it when we get un-parented.
if (parent === null)
- container.onDestructionCallback(stackView)
+ container.onDestructionCallback(stackView) // qmllint disable use-proper-function
}
}
}
@@ -1288,7 +1527,7 @@ TestCase {
let control = container.control
// Push an extra item so that we can call pop(null) and reproduce the conditions for the crash.
- control.push(component, StackView.Immediate)
+ control.push(itemComponent, StackView.Immediate)
control.push(container.clearUponDestructionComponent, StackView.Immediate)
// Pop the top item, then pop down to the first item in response.
@@ -1306,12 +1545,12 @@ TestCase {
// Replace the top item, then clear in response.
ignoreWarning(/.*cannot clear while already in the process of completing a replace/)
- control.replace(component, StackView.Immediate)
+ control.replace(itemComponent, StackView.Immediate)
}
function test_recursiveClearReplace() {
let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase,
- { onDestructionCallback: function(stackView) { stackView.replace(component, StackView.Immediate) }})
+ { onDestructionCallback: function(stackView) { stackView.replace(itemComponent, StackView.Immediate) }})
verify(container)
let control = container.control
@@ -1383,7 +1622,7 @@ TestCase {
// QTBUG-84381
function test_clearAndPushAfterDepthChange() {
- var control = createTemporaryObject(stackView, testCase, {
+ let control = createTemporaryObject(stackViewComponent, testCase, {
popEnter: null, popExit: null, pushEnter: null,
pushExit: null, replaceEnter: null, replaceExit: null
})
@@ -1396,12 +1635,12 @@ TestCase {
control.clear()
// Shouldn't crash.
ignoreWarning(/.*QML StackView: cannot push while already in the process of completing a push/)
- control.push(component)
+ control.push(itemComponent)
}
})
- control.push(component)
- control.push(component)
+ control.push(itemComponent)
+ control.push(itemComponent)
}
// QTBUG-96966
@@ -1516,8 +1755,9 @@ TestCase {
}
function test_requiredProperties() {
- var control = createTemporaryObject(stackView, testCase)
+ let control = createTemporaryObject(stackViewComponent, testCase)
verify(control)
+ ignoreWarning(/QML StackView: push: Property i was marked as required but not set/)
let failedPush = control.push(withRequired)
compare(failedPush, null);
control.push(withRequired, {"i": 42})
@@ -1564,7 +1804,7 @@ TestCase {
StackView {
id: stackView
anchors.fill: parent
- initialItem: cppComponent
+ initialItem: stackView.cppComponent
property Component cppComponent: ComponentCreator.createComponent("import QtQuick; Rectangle { color: \"navajowhite\" }")
}
@@ -1601,4 +1841,42 @@ TestCase {
control.push(noProperties, { "unknownProperty.test": "crashes" })
verify(!control.empty)
}
+
+ Component {
+ id: deletePoppedItem
+
+ StackView {
+ id: stackView
+ anchors.fill: parent
+ property int visibleChangedCounter
+ property bool secondDestroyed: false
+ initialItem: Text {
+ text: "First"
+ onVisibleChanged: {
+ ++visibleChangedCounter
+ if (visible)
+ tryVerify(function() { return secondDestroyed; })
+ }
+ }
+ }
+ }
+
+ Component {
+ id: otherComp
+ Text {
+ text: "Second"
+ property var stackView
+ Component.onDestruction: stackView.secondDestroyed = true
+ }
+ }
+
+ function test_deletePoppedItem() {
+ let control = createTemporaryObject(deletePoppedItem, testCase)
+ verify(control)
+ control.push(otherComp)
+ tryCompare(control, "visibleChangedCounter", 1)
+ control.currentItem.stackView = control
+ let item = control.pop()
+ tryCompare(control, "visibleChangedCounter", 2)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml b/tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml
index 901170bf9f..4e4022a1c5 100644
--- a/tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_swipedelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -86,12 +86,12 @@ TestCase {
}
function test_horizontalAnchors(data) {
- var warningMessage = Qt.resolvedUrl("tst_swipedelegate.qml") + data.warningLocation
+ let warningMessage = Qt.resolvedUrl("tst_swipedelegate.qml") + data.warningLocation
+ ": QML QQuickItem: SwipeDelegate: cannot use horizontal anchors with " + data.itemName + "; unable to layout the item."
ignoreWarning(warningMessage);
- var control = createTemporaryObject(data.component, testCase);
+ let control = createTemporaryObject(data.component, testCase);
verify(control.contentItem);
}
@@ -144,7 +144,7 @@ TestCase {
// Sanity check.
compare(control.swipe.position, from);
- var distance = (to - from) * control.width;
+ let distance = (to - from) * control.width;
mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton);
mouseMove(control, control.width / 2 + distance, control.height / 2);
@@ -166,7 +166,7 @@ TestCase {
}
function test_settingDelegates() {
- var control = createTemporaryObject(swipeDelegateComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateComponent, testCase);
verify(control);
ignoreWarning(/QML SwipeDelegate: cannot set both behind and left\/right properties/)
@@ -200,8 +200,8 @@ TestCase {
// First, try the left item.
swipe(control, 0.0, 1.0);
- var oldLeft = control.swipe.left;
- var oldLeftItem = control.swipe.leftItem;
+ let oldLeft = control.swipe.left;
+ let oldLeftItem = control.swipe.leftItem;
ignoreWarning(/QML SwipeDelegate: left\/right\/behind properties may only be set when swipe.position is 0/)
control.swipe.left = null;
compare(control.swipe.left, oldLeft);
@@ -210,8 +210,8 @@ TestCase {
// Try the same thing with the right item.
swipe(control, 1.0, -1.0);
- var oldRight = control.swipe.right;
- var oldRightItem = control.swipe.rightItem;
+ let oldRight = control.swipe.right;
+ let oldRightItem = control.swipe.rightItem;
ignoreWarning(/QML SwipeDelegate: left\/right\/behind properties may only be set when swipe.position is 0/)
control.swipe.right = null;
compare(control.swipe.right, oldRight);
@@ -235,18 +235,20 @@ TestCase {
swipe(control, 0.0, 1.0);
- var oldBehind = control.swipe.behind;
- var oldBehindItem = control.swipe.behindItem;
+ let oldBehind = control.swipe.behind;
+ let oldBehindItem = control.swipe.behindItem;
ignoreWarning(/QML SwipeDelegate: left\/right\/behind properties may only be set when swipe.position is 0/)
control.swipe.behind = null;
compare(control.swipe.behind, oldBehind);
compare(control.swipe.behindItem, oldBehindItem);
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(swipeDelegateComponent, testCase);
+ function test_defaults() {
+ let control = createTemporaryObject(swipeDelegateComponent, testCase);
verify(control);
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset);
@@ -261,20 +263,20 @@ TestCase {
}
function test_swipe() {
- var control = createTemporaryObject(swipeDelegateComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateComponent, testCase);
verify(control);
- var overDragDistance = Math.round(dragDistance * 1.1);
+ let overDragDistance = Math.round(dragDistance * 1.1);
- var completedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "completed" });
+ let completedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "completed" });
verify(completedSpy);
verify(completedSpy.valid);
- var openedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "opened" });
+ let openedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "opened" });
verify(openedSpy);
verify(openedSpy.valid);
- var closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" });
+ let closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" });
verify(closedSpy);
verify(closedSpy.valid);
@@ -437,10 +439,10 @@ TestCase {
function test_swipeVelocity(data) {
skip("QTBUG-52003");
- var control = createTemporaryObject(swipeDelegateComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateComponent, testCase);
verify(control);
- var distance = Math.round(dragDistance * 1.1);
+ let distance = Math.round(dragDistance * 1.1);
if (distance >= control.width / 2)
skip("This test requires a startDragDistance that is less than half the width of the control");
@@ -464,10 +466,10 @@ TestCase {
verify(control.swipe.position < 0.5);
verify(!control.swipe.complete);
- var expectedVisibleItem;
- var expectedVisibleObjectName;
- var expectedHiddenItem;
- var expectedContentItemX;
+ let expectedVisibleItem;
+ let expectedVisibleObjectName;
+ let expectedHiddenItem;
+ let expectedContentItemX;
if (distance > 0) {
expectedVisibleObjectName = "leftItem";
expectedVisibleItem = control.swipe.leftItem;
@@ -505,7 +507,9 @@ TestCase {
text: "SwipeDelegate"
width: 150
swipe.right: Button {
- width: parent.width
+ // make the button a bit shorter than the delegate, so
+ // that we're able to release the mouse outside of it
+ width: parent.width - 4
height: parent.height
text: "Boo!"
}
@@ -513,10 +517,10 @@ TestCase {
}
function test_eventsToLeftAndRight() {
- var control = createTemporaryObject(swipeDelegateWithButtonComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateWithButtonComponent, testCase);
verify(control);
- var closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" });
+ let closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" });
verify(closedSpy);
verify(closedSpy.valid);
@@ -529,20 +533,20 @@ TestCase {
verify(control.swipe.rightItem.visible);
compare(control.swipe.rightItem.parent, control);
- var buttonPressedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "pressed" });
+ let buttonPressedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "pressed" });
verify(buttonPressedSpy);
verify(buttonPressedSpy.valid);
- var buttonReleasedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "released" });
+ let buttonReleasedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "released" });
verify(buttonReleasedSpy);
verify(buttonReleasedSpy.valid);
- var buttonClickedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "clicked" });
+ let buttonClickedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "clicked" });
verify(buttonClickedSpy);
verify(buttonClickedSpy.valid);
// Now press the button.
mousePress(control, control.width / 2, control.height / 2);
verify(!control.pressed);
- var button = control.swipe.rightItem;
+ let button = control.swipe.rightItem;
verify(button.pressed);
compare(buttonPressedSpy.count, 1);
compare(buttonReleasedSpy.count, 0);
@@ -564,10 +568,26 @@ TestCase {
verify(!button.pressed);
mouseRelease(control, control.width / 2, control.height / 2);
verify(!control.pressed);
+
+ // Try to press the button again, but drag and release outside of it.
+ // This should not click the button.
+ buttonClickedSpy.clear();
+ // Open the control, and press the button
+ mouseDrag(control, control.width / 2, control.height / 2, -control.width, 0);
+ mousePress(control);
+ verify(button.pressed);
+
+ // Drag the mouse outside the button, and release
+ mouseMove(control, control.width - 2, control.height / 2, -1, Qt.LeftButton);
+ mouseRelease(control);
+ verify(!button.pressed);
+ verify(!button.hovered);
+ // This should not be a click
+ compare(buttonClickedSpy.count, 0);
}
function test_mouseButtons() {
- var control = createTemporaryObject(swipeDelegateComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateComponent, testCase);
verify(control);
// click
@@ -609,7 +629,7 @@ TestCase {
verify(mouseSignalSequenceSpy.success);
// press and hold
- var pressAndHoldSpy = signalSpyComponent.createObject(control, { target: control, signalName: "pressAndHold" });
+ let pressAndHoldSpy = signalSpyComponent.createObject(control, { target: control, signalName: "pressAndHold" });
verify(pressAndHoldSpy);
verify(pressAndHoldSpy.valid);
@@ -691,27 +711,45 @@ TestCase {
}
}
+ function test_removableDelegates_data() {
+ return [
+ { tag: "mouse", touch: false },
+ { tag: "touch", touch: true }
+ ]
+ }
+
function test_removableDelegates() {
- var listView = createTemporaryObject(removableDelegatesComponent, testCase);
+ let listView = createTemporaryObject(removableDelegatesComponent, testCase);
verify(listView);
compare(listView.count, 3);
+ let touch = data.touch ? touchEvent(listView) : null
+
// Expose the remove button.
- var firstItem = listView.itemAt(0, 0);
- mousePress(listView, firstItem.width / 2, firstItem.height / 2);
+ let firstItem = listView.itemAt(0, 0);
+ if (data.touch)
+ touch.press(0, listView, firstItem.width / 2, firstItem.height / 2).commit()
+ else
+ mousePress(listView, firstItem.width / 2, firstItem.height / 2);
verify(firstItem.pressed);
compare(firstItem.swipe.position, 0.0);
verify(!firstItem.swipe.complete);
verify(!firstItem.swipe.leftItem);
- mouseMove(listView, firstItem.width * 1.1, firstItem.height / 2);
+ if (data.touch)
+ touch.move(0, listView, firstItem.width * 1.1, firstItem.height / 2).commit()
+ else
+ mouseMove(listView, firstItem.width * 1.1, firstItem.height / 2);
verify(firstItem.pressed);
compare(firstItem.swipe.position, 0.6);
verify(!firstItem.swipe.complete);
verify(firstItem.swipe.leftItem);
verify(!firstItem.swipe.leftItem.SwipeDelegate.pressed);
- mouseRelease(listView, firstItem.width / 2, firstItem.height / 2);
+ if (data.touch)
+ touch.release(0, listView, firstItem.width / 2, firstItem.height / 2).commit()
+ else
+ mouseRelease(listView, firstItem.width / 2, firstItem.height / 2);
verify(!firstItem.pressed);
tryCompare(firstItem.swipe, "position", 1.0);
tryCompare(firstItem.swipe, "complete", true);
@@ -720,19 +758,32 @@ TestCase {
// Wait for it to settle down.
tryCompare(firstItem.contentItem, "x", firstItem.leftPadding + firstItem.width);
- var leftClickedSpy = signalSpyComponent.createObject(firstItem.swipe.leftItem,
+ let leftClickedSpy = signalSpyComponent.createObject(firstItem.swipe.leftItem,
{ target: firstItem.swipe.leftItem.SwipeDelegate, signalName: "clicked" });
verify(leftClickedSpy);
verify(leftClickedSpy.valid);
// Click the left item to remove the delegate from the list.
- var contentItemX = firstItem.contentItem.x;
- mousePress(listView, firstItem.width / 2, firstItem.height / 2);
+ let contentItemX = firstItem.contentItem.x;
+ // press
+ if (data.touch)
+ touch.press(0, listView, firstItem.width / 2, firstItem.height / 2).commit()
+ else
+ mousePress(listView, firstItem.width / 2, firstItem.height / 2);
verify(firstItem.swipe.leftItem.SwipeDelegate.pressed);
compare(leftClickedSpy.count, 0);
verify(firstItem.pressed);
- mouseRelease(listView, firstItem.width / 2, firstItem.height / 2);
+ // simulate inadvertent movement which can easily happen
+ if (data.touch)
+ touch.move(0, listView, firstItem.width / 2 + 1, firstItem.height / 2).commit()
+ else
+ mouseMove(listView, firstItem.width / 2 + 1, firstItem.height / 2);
+ // release
+ if (data.touch)
+ touch.release(0, listView, firstItem.width / 2, firstItem.height / 2).commit()
+ else
+ mouseRelease(listView, firstItem.width / 2, firstItem.height / 2);
verify(!firstItem.swipe.leftItem.SwipeDelegate.pressed);
compare(leftClickedSpy.count, 1);
verify(!firstItem.pressed);
@@ -806,7 +857,7 @@ TestCase {
}
function test_leadingTrailing(data) {
- var control = createTemporaryObject(data.component, testCase);
+ let control = createTemporaryObject(data.component, testCase);
verify(control);
mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton);
@@ -817,7 +868,7 @@ TestCase {
}
function test_minMaxPosition() {
- var control = createTemporaryObject(leadingTrailingXComponent, testCase);
+ let control = createTemporaryObject(leadingTrailingXComponent, testCase);
verify(control);
// Should be limited within the range -1.0 to 1.0.
@@ -853,13 +904,13 @@ TestCase {
// swipe.position should be scaled to the width of the relevant delegate,
// and it shouldn't be possible to drag past the delegate (so that content behind the control is visible).
function test_delegateWidth() {
- var control = createTemporaryObject(emptySwipeDelegateComponent, testCase);
+ let control = createTemporaryObject(emptySwipeDelegateComponent, testCase);
verify(control);
control.swipe.left = smallLeftComponent;
// Ensure that the position is scaled to the width of the currently visible delegate.
- var overDragDistance = Math.round(dragDistance * 1.1);
+ let overDragDistance = Math.round(dragDistance * 1.1);
mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton);
mouseMove(control, control.width / 2 + overDragDistance, control.height / 2);
verify(control.swipe.leftItem);
@@ -891,7 +942,7 @@ TestCase {
}
function test_positionAfterSwipeCompleted() {
- var control = createTemporaryObject(swipeDelegateComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateComponent, testCase);
verify(control);
// Ensure that both delegates are constructed.
@@ -916,7 +967,7 @@ TestCase {
mousePress(control, control.swipe.leftItem.width - 1, control.height / 2, Qt.LeftButton);
compare(leftVisibleSpy.count, 0);
compare(rightVisibleSpy.count, 0);
- var newX = control.swipe.leftItem.width - Math.round(dragDistance * 1.1) -1;
+ let newX = control.swipe.leftItem.width - Math.round(dragDistance * 1.1) -1;
mouseMove(control, newX, control.height / 2);
compare(leftVisibleSpy.count, 0);
compare(rightVisibleSpy.count, 0);
@@ -986,7 +1037,7 @@ TestCase {
}
function test_leadingTrailingBehindItem() {
- var control = createTemporaryObject(behindSwipeDelegateComponent, testCase);
+ let control = createTemporaryObject(behindSwipeDelegateComponent, testCase);
verify(control);
swipe(control, 0.0, 1.0);
@@ -1038,10 +1089,10 @@ TestCase {
}
function test_close() {
- var control = createTemporaryObject(closeSwipeDelegateComponent, testCase);
+ let control = createTemporaryObject(closeSwipeDelegateComponent, testCase);
verify(control);
- var closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" });
+ let closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" });
verify(closedSpy);
verify(closedSpy.valid);
@@ -1173,14 +1224,14 @@ TestCase {
// Tests that it's possible to have multiple non-interactive items in one delegate
// (e.g. left/right/behind) that can each receive clicks.
function test_multipleClickableActions() {
- var control = createTemporaryObject(multiActionSwipeDelegateComponent, testCase);
+ let control = createTemporaryObject(multiActionSwipeDelegateComponent, testCase);
verify(control);
swipe(control, 0.0, -1.0);
verify(control.swipe.rightItem);
tryCompare(control.swipe, "complete", true);
- var firstClickedSpy = signalSpyComponent.createObject(control,
+ let firstClickedSpy = signalSpyComponent.createObject(control,
{ target: control.swipe.rightItem.firstAction.SwipeDelegate, signalName: "clicked" });
verify(firstClickedSpy);
verify(firstClickedSpy.valid);
@@ -1204,7 +1255,7 @@ TestCase {
compare(firstClickedSpy.count, 1);
compare(control.swipe.rightItem.firstClickCount, 1);
- var secondClickedSpy = signalSpyComponent.createObject(control,
+ let secondClickedSpy = signalSpyComponent.createObject(control,
{ target: control.swipe.rightItem.secondAction.SwipeDelegate, signalName: "clicked" });
verify(secondClickedSpy);
verify(secondClickedSpy.valid);
@@ -1280,25 +1331,25 @@ TestCase {
// rotation, for example), the positions of the contentItem and background items
// should be updated accordingly.
function test_contentItemPosOnWidthChanged() {
- var control = createTemporaryObject(swipeDelegateComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateComponent, testCase);
verify(control);
swipe(control, 0.0, 1.0);
- var oldContentItemX = control.contentItem.x;
- var oldBackgroundX = control.background.x;
+ let oldContentItemX = control.contentItem.x;
+ let oldBackgroundX = control.background.x;
control.width += 100;
compare(control.contentItem.x, oldContentItemX + 100);
compare(control.background.x, oldBackgroundX + 100);
}
function test_contentItemHeightOnHeightChanged() {
- var control = createTemporaryObject(swipeDelegateComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateComponent, testCase);
verify(control);
// Try when swipe.complete is false.
- var originalHeight = control.height;
- var originalContentItemHeight = control.contentItem.height;
+ let originalHeight = control.height;
+ let originalContentItemHeight = control.contentItem.height;
verify(control.height !== 10);
control.height = 10;
compare(control.contentItem.height, control.availableHeight);
@@ -1322,7 +1373,7 @@ TestCase {
}
function test_releaseOutside(data) {
- var control = createTemporaryObject(data.component, testCase);
+ let control = createTemporaryObject(data.component, testCase);
verify(control);
// Press and then release below the control.
@@ -1337,7 +1388,7 @@ TestCase {
verify(mouseSignalSequenceSpy.success);
// Press and then release to the right of the control.
- var hasDelegates = control.swipe.left || control.swipe.right || control.swipe.behind;
+ let hasDelegates = control.swipe.left || control.swipe.right || control.swipe.behind;
mouseSignalSequenceSpy.target = control;
mouseSignalSequenceSpy.expectedSequence = hasDelegates
? [["pressedChanged", { "pressed": true }], "pressed"]
@@ -1405,7 +1456,7 @@ TestCase {
}
function test_beginSwipeOverRightItem() {
- var control = createTemporaryObject(leftRightWithLabelsComponent, testCase);
+ let control = createTemporaryObject(leftRightWithLabelsComponent, testCase);
verify(control);
// Swipe to the left, exposing the right item.
@@ -1416,8 +1467,8 @@ TestCase {
// TODO: Swipe to the left, with the mouse over the Label in the right item.
// The left item should not become visible at any point.
- var rightLabel = control.swipe.rightItem.label;
- var overDragDistance = Math.round(dragDistance * 1.1);
+ let rightLabel = control.swipe.rightItem.label;
+ let overDragDistance = Math.round(dragDistance * 1.1);
mousePress(rightLabel, rightLabel.width / 2, rightLabel.height / 2, Qt.rightButton);
mouseMove(rightLabel, rightLabel.width / 2 - overDragDistance, rightLabel.height / 2);
verify(!control.swipe.leftItem);
@@ -1450,7 +1501,7 @@ TestCase {
}
function test_swipeEnabled() {
- var control = createTemporaryObject(swipeDelegateDisabledComponent, testCase);
+ let control = createTemporaryObject(swipeDelegateDisabledComponent, testCase);
mousePress(control, control.width / 2, control.height / 2);
verify(control.pressed);
@@ -1460,7 +1511,7 @@ TestCase {
verify(!control.swipe.rightItem);
// It shouldn't be possible to swipe.
- var overDragDistance = Math.round(dragDistance * 1.1);
+ let overDragDistance = Math.round(dragDistance * 1.1);
mouseMove(control, control.width / 2 - overDragDistance, control.height / 2);
verify(control.pressed);
compare(control.swipe.position, 0.0);
@@ -1527,7 +1578,7 @@ TestCase {
}
function test_open_side(data) {
- var control = createTemporaryObject(emptySwipeDelegateComponent, testCase,
+ let control = createTemporaryObject(emptySwipeDelegateComponent, testCase,
{"swipe.left": data.left, "swipe.right": data.right, "swipe.behind": data.behind});
verify(control);
@@ -1553,7 +1604,7 @@ TestCase {
}
function test_open() {
- var control = createTemporaryObject(openSwipeDelegateComponent, testCase);
+ let control = createTemporaryObject(openSwipeDelegateComponent, testCase);
verify(control);
mouseClick(control);
@@ -1599,7 +1650,7 @@ TestCase {
function test_animations() {
// Test that animations are run when releasing from a drag.
- var control = createTemporaryObject(animationSwipeDelegateComponent, testCase);
+ let control = createTemporaryObject(animationSwipeDelegateComponent, testCase);
verify(control);
mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton);
@@ -1615,11 +1666,11 @@ TestCase {
}
function test_spacing() {
- var control = createTemporaryObject(swipeDelegateComponent, testCase, { text: "Some long, long, long text" })
+ let control = createTemporaryObject(swipeDelegateComponent, testCase, { text: "Some long, long, long text" })
verify(control)
verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth)
- var textLabel = findChild(control.contentItem, "label")
+ let textLabel = findChild(control.contentItem, "label")
verify(textLabel)
// The implicitWidth of the IconLabel that all buttons use as their contentItem
@@ -1648,7 +1699,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(swipeDelegateComponent, testCase, {
+ let control = createTemporaryObject(swipeDelegateComponent, testCase, {
text: "SwipeDelegate",
display: data.display,
width: 400,
@@ -1658,8 +1709,8 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
switch (control.display) {
case SwipeDelegate.IconOnly:
@@ -1747,4 +1798,47 @@ TestCase {
swipe(control, 0, -1.0)
compare(control.swipe.rightItem.color, Qt.color("tomato"))
}
+
+
+ Component {
+ id: swipeDelegate
+
+ SwipeDelegate {
+ anchors.centerIn: parent
+ width: 100
+ height: 50
+ contentItem: Rectangle {
+ color: "red"
+ }
+ swipe.right: Row {
+ height: parent.height
+ anchors.right: parent.right
+ property alias buttonItem: button
+ Button {
+ id: button
+ width: 50
+ height: parent.height
+ text: "Button"
+ }
+ }
+ }
+ }
+
+ function test_mouseEventOnNonVisualItem() {
+ let control = createTemporaryObject(swipeDelegate, testCase)
+ verify(control)
+
+ swipe(control, 0, -1.0)
+ verify(control.swipe.rightItem.visible)
+
+ let rightItem = control.swipe.rightItem
+ let rightClickSpy = signalSpyComponent.createObject(control,
+ { target: rightItem.buttonItem, signalName: "clicked" })
+ verify(rightClickSpy)
+ verify(rightClickSpy.valid)
+
+ mouseClick(rightItem)
+
+ compare(rightClickSpy.count, 1)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_swipeview.qml b/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
index c853f6dd55..8d59c4a09a 100644
--- a/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -28,24 +28,26 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(swipeView, testCase)
verify(control)
}
function test_current() {
- var control = createTemporaryObject(swipeView, testCase)
+ let control = createTemporaryObject(swipeView, testCase)
- var currentItemChangedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "currentItemChanged"})
+ let currentItemChangedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "currentItemChanged"})
verify(currentItemChangedSpy.valid)
compare(control.count, 0)
compare(control.currentIndex, -1)
compare(control.currentItem, null)
- var item0 = page.createObject(control, {text: "0"})
+ let item0 = page.createObject(control, {text: "0"})
control.addItem(item0)
compare(control.count, 1)
compare(control.currentIndex, 0)
@@ -54,7 +56,7 @@ TestCase {
compare(control.contentWidth, item0.implicitWidth)
compare(control.contentHeight, item0.implicitHeight)
- var item1 = page.createObject(control, {text: "11"})
+ let item1 = page.createObject(control, {text: "11"})
control.addItem(item1)
compare(control.count, 2)
compare(control.currentIndex, 0)
@@ -63,7 +65,7 @@ TestCase {
compare(control.contentWidth, item0.implicitWidth)
compare(control.contentHeight, item0.implicitHeight)
- var item2 = page.createObject(control, {text: "222"})
+ let item2 = page.createObject(control, {text: "222"})
control.addItem(item2)
compare(control.count, 3)
compare(control.currentIndex, 0)
@@ -119,7 +121,7 @@ TestCase {
}
function test_initialCurrent() {
- var control = createTemporaryObject(initialCurrentSwipeView, testCase)
+ let control = createTemporaryObject(initialCurrentSwipeView, testCase)
compare(control.count, 2)
compare(control.currentIndex, 1)
@@ -127,7 +129,7 @@ TestCase {
}
function test_addRemove() {
- var control = createTemporaryObject(swipeView, testCase)
+ let control = createTemporaryObject(swipeView, testCase)
function verifyCurrentIndexCountDiff() {
verify(control.currentIndex < control.count)
@@ -135,7 +137,7 @@ TestCase {
control.currentIndexChanged.connect(verifyCurrentIndexCountDiff)
control.countChanged.connect(verifyCurrentIndexCountDiff)
- var currentItemChangedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "currentItemChanged"})
+ let currentItemChangedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "currentItemChanged"})
verify(currentItemChangedSpy.valid)
compare(control.count, 0)
@@ -235,12 +237,12 @@ TestCase {
}
function test_content() {
- var control = createTemporaryObject(contentView, testCase)
+ let control = createTemporaryObject(contentView, testCase)
function compareObjectNames(content, names) {
if (content.length !== names.length)
return false
- for (var i = 0; i < names.length; ++i) {
+ for (let i = 0; i < names.length; ++i) {
if (content[i].objectName !== names[i])
return false
}
@@ -280,20 +282,20 @@ TestCase {
}
function test_repeater() {
- var control = createTemporaryObject(repeated, testCase)
+ let control = createTemporaryObject(repeated, testCase)
verify(control)
- var model = control.contentModel
+ let model = control.contentModel
verify(model)
- var repeater = control.repeater
+ let repeater = control.repeater
verify(repeater)
compare(repeater.count, 5)
compare(model.count, 5)
- for (var i = 0; i < 5; ++i) {
- var item1 = control.itemAt(i)
+ for (let i = 0; i < 5; ++i) {
+ let item1 = control.itemAt(i)
verify(item1)
compare(item1.idx, i)
compare(model.get(i), item1)
@@ -304,8 +306,8 @@ TestCase {
compare(repeater.count, 3)
compare(model.count, 3)
- for (var j = 0; j < 3; ++j) {
- var item2 = control.itemAt(j)
+ for (let j = 0; j < 3; ++j) {
+ let item2 = control.itemAt(j)
verify(item2)
compare(item2.idx, j)
compare(model.get(j), item2)
@@ -334,7 +336,7 @@ TestCase {
}
function test_order() {
- var control = createTemporaryObject(ordered, testCase)
+ let control = createTemporaryObject(ordered, testCase)
verify(control)
compare(control.count, 7)
@@ -395,14 +397,14 @@ TestCase {
}
function test_move(data) {
- var control = createTemporaryObject(swipeView, testCase)
+ let control = createTemporaryObject(swipeView, testCase)
compare(control.count, 0)
- var titles = ["1", "2", "3"]
+ let titles = ["1", "2", "3"]
- var i = 0;
+ let i = 0;
for (i = 0; i < titles.length; ++i) {
- var item = pageAttached.createObject(control, {text: titles[i]})
+ let item = pageAttached.createObject(control, {text: titles[i]})
control.addItem(item)
}
@@ -427,7 +429,7 @@ TestCase {
compare(control.count, titles.length)
compare(control.currentIndex, data.currentAfter)
- var title = titles[data.from]
+ let title = titles[data.from]
titles.splice(data.from, 1)
titles.splice(data.to, 0, title)
@@ -455,13 +457,13 @@ TestCase {
}
function test_dynamic() {
- var control = createTemporaryObject(dynamicView, testCase)
+ let control = createTemporaryObject(dynamicView, testCase)
// insertItem(), addItem(), createObject() and static page {}
compare(control.count, 4)
compare(control.itemAt(0).text, "inserted")
- var tab = page.createObject(control, {text: "dying"})
+ let tab = page.createObject(control, {text: "dying"})
compare(control.count, 5)
compare(control.itemAt(4).text, "dying")
@@ -472,9 +474,9 @@ TestCase {
}
function test_attachedParent() {
- var control = createTemporaryObject(swipeView, testCase);
+ let control = createTemporaryObject(swipeView, testCase);
- var page = createTemporaryObject(pageAttached, testCase);
+ let page = createTemporaryObject(pageAttached, testCase);
compare(page.view, null);
compare(page.index, -1);
compare(page.isCurrentItem, false);
@@ -509,17 +511,17 @@ TestCase {
}
function test_orientation() {
- var control = createTemporaryObject(swipeView, testCase, {width: 200, height: 200})
+ let control = createTemporaryObject(swipeView, testCase, {width: 200, height: 200})
verify(control)
- for (var i = 0; i < 3; ++i)
+ for (let i = 0; i < 3; ++i)
control.addItem(page.createObject(control, {text: i}))
compare(control.orientation, Qt.Horizontal)
compare(control.horizontal, true)
compare(control.vertical, false)
- for (i = 0; i < control.count; ++i) {
+ for (let i = 0; i < control.count; ++i) {
control.currentIndex = i
compare(control.itemAt(i).y, 0)
}
@@ -529,7 +531,7 @@ TestCase {
compare(control.horizontal, false)
compare(control.vertical, true)
- for (i = 0; i < control.count; ++i) {
+ for (let i = 0; i < control.count; ++i) {
control.currentIndex = i
compare(control.itemAt(i).x, 0)
}
@@ -564,7 +566,7 @@ TestCase {
if (Qt.styleHints.tabFocusBehavior !== Qt.TabFocusAllControls)
skip("This platform only allows tab focus for text controls")
- var control = createTemporaryObject(focusSwipeViewComponent, testCase)
+ let control = createTemporaryObject(focusSwipeViewComponent, testCase)
verify(control)
compare(control.focus, true)
compare(control.contentItem.focus, true)
@@ -643,7 +645,7 @@ TestCase {
tryCompare(rect, "visible", true)
if (Qt.platform.pluginName === "offscreen")
skip("grabImage() is not functional on the offscreen platform (QTBUG-63185)")
- var image = grabImage(control)
+ let image = grabImage(control)
compare(image.pixel(3, 3), "#ffff00")
}
@@ -651,7 +653,10 @@ TestCase {
id: translucentPages
SwipeView {
spacing: 10
- padding: 10
+ leftPadding: 10
+ topPadding: 10
+ rightPadding: 10
+ bottomPadding: 10
Text { text: "page 0" }
Text { text: "page 1"; font.pointSize: 16 }
Text { text: "page 2"; font.pointSize: 24 }
@@ -662,18 +667,97 @@ TestCase {
function test_initialPositions() { // QTBUG-102487
const control = createTemporaryObject(translucentPages, testCase, {width: 320, height: 200})
verify(control)
+ compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding)
+ compare(control.spacing, 10)
+
compare(control.orientation, Qt.Horizontal)
- for (var i = 0; i < control.count; ++i) {
+ for (let i = 0; i < control.count; ++i) {
const page = control.itemAt(i)
- // control.contentItem.width + control.spacing == 310; except Imagine style has contentItem.width == 320
- compare(page.x, i * 310)
+ compare(page.x, i * (control.contentItem.width + control.spacing))
compare(page.y, 0)
+ compare(page.width, control.contentItem.width)
+ compare(page.height, control.contentItem.height)
}
control.orientation = Qt.Vertical
- for (var i = 0; i < control.count; ++i) {
+ for (let i = 0; i < control.count; ++i) {
const page = control.itemAt(i)
compare(page.y, i * (control.contentItem.height + control.spacing))
compare(page.x, 0)
+ compare(page.width, control.contentItem.width)
+ compare(page.height, control.contentItem.height)
+ }
+
+ // QTBUG-115468: add a page after startup and check that that works too.
+ control.orientation = Qt.Horizontal
+ let page4 = page.createObject(control, { text: "page 4", "font.pointSize": 40 })
+ control.insertItem(control.count, page4)
+ compare(page4.x, (control.count - 1) * 310)
+ compare(page4.y, 0)
+ compare(page4.width, control.contentItem.width)
+ compare(page4.height, control.contentItem.height)
+ }
+
+ Component {
+ id: doublePageWithLabels
+ SwipeView {
+ anchors.fill: parent
+ property alias item1: item1
+ property alias item2: item2
+ Item {
+ id: item1
+ property alias label: label1
+ Label { id: label1; anchors.centerIn: parent; text: "1"; }
+ }
+ Item {
+ id: item2
+ property alias label: label2
+ Label { id: label2; anchors.centerIn: parent; text: "2"; }
+ }
+ }
+ }
+
+ function test_rightClick_data() {
+ return [
+ { tag: "mouse_left", mouse: true, button: Qt.LeftButton },
+ { tag: "mouse_right", mouse: true, button: Qt.RightButton },
+ { tag: "touch", touch: true }
+ ]
+ }
+
+ function test_rightClick(data) {
+ let swipeView = createTemporaryObject(doublePageWithLabels, testCase)
+ verify(swipeView)
+ let item1 = swipeView.item1
+ verify(item1)
+ let item2 = swipeView.item2
+ verify(item2)
+ let label1 = item1.label
+ verify(label1)
+ let label2 = item2.label
+ verify(label2)
+ const swipeListView = swipeView.contentItem
+ verify(swipeListView)
+
+ swipeView.currentIndex = 0
+ compare(swipeView.currentIndex, 0)
+ compare(swipeView.currentItem, item1)
+ tryCompare(swipeListView, "contentX", 0, 1000)
+ compare(item2.x, swipeListView.width)
+
+ let touch = data.touch ? touchEvent(swipeView) : null
+
+ if (data.touch) {
+ touch.press(0, label1, label1.width / 2, label1.height / 2)
+ touch.commit()
+ touch.release(0, label1, label1.width / 2, label1.height / 2)
+ touch.commit()
+ } else if (data.mouse) {
+ mouseClick(label1, label1.width / 2, label1.height / 2, data.button)
}
+ swipeView.currentIndex = 1
+ compare(swipeView.currentIndex, 1)
+ compare(swipeView.currentItem, item2)
+ tryCompare(swipeListView, "contentX", swipeListView.width, 1000)
+ compare(item2.x, swipeListView.width)
}
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_switch.qml b/tests/auto/quickcontrols/controls/data/tst_switch.qml
index 407513b914..40fdbaff0d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_switch.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_switch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -25,15 +25,17 @@ TestCase {
}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(swtch, testCase)
verify(control)
}
function test_text() {
- var control = createTemporaryObject(swtch, testCase)
+ let control = createTemporaryObject(swtch, testCase)
verify(control)
compare(control.text, "")
@@ -44,12 +46,12 @@ TestCase {
}
function test_checked() {
- var control = createTemporaryObject(swtch, testCase)
+ let control = createTemporaryObject(swtch, testCase)
verify(control)
compare(control.checked, false)
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
spy.expectedSequence = [["checkedChanged", { "checked": true }]]
control.checked = true
compare(control.checked, true)
@@ -69,7 +71,7 @@ TestCase {
}
function test_pressed(data) {
- var control = createTemporaryObject(swtch, testCase, {padding: 10})
+ let control = createTemporaryObject(swtch, testCase, {padding: 10})
verify(control)
// stays pressed when dragged outside
@@ -83,11 +85,11 @@ TestCase {
}
function test_mouse() {
- var control = createTemporaryObject(swtch, testCase)
+ let control = createTemporaryObject(swtch, testCase)
verify(control)
// check
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton)
@@ -184,13 +186,13 @@ TestCase {
}
function test_touch() {
- var control = createTemporaryObject(swtch, testCase)
+ let control = createTemporaryObject(swtch, testCase)
verify(control)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
// check
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
touch.press(0, control, control.width / 2, control.height / 2).commit()
@@ -282,10 +284,10 @@ TestCase {
}
function test_mouseDrag() {
- var control = createTemporaryObject(swtch, testCase, {leftPadding: 100, rightPadding: 100})
+ let control = createTemporaryObject(swtch, testCase, {leftPadding: 100, rightPadding: 100})
verify(control)
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
compare(control.position, 0.0)
compare(control.checked, false)
compare(control.pressed, false)
@@ -387,12 +389,12 @@ TestCase {
}
function test_touchDrag() {
- var control = createTemporaryObject(swtch, testCase, {leftPadding: 100, rightPadding: 100})
+ let control = createTemporaryObject(swtch, testCase, {leftPadding: 100, rightPadding: 100})
verify(control)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
compare(control.position, 0.0)
compare(control.checked, false)
compare(control.pressed, false)
@@ -497,14 +499,14 @@ TestCase {
}
function test_keys() {
- var control = createTemporaryObject(swtch, testCase)
+ let control = createTemporaryObject(swtch, testCase)
verify(control)
control.forceActiveFocus()
verify(control.activeFocus)
// check
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed",
["pressedChanged", { "pressed": false, "checked": false }],
@@ -532,8 +534,8 @@ TestCase {
spy.expectedSequence = []
// Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
// pressing buttons and the CI uses the QGnomeTheme platform theme.
- var keys = [Qt.Key_Escape, Qt.Key_Tab]
- for (var i = 0; i < keys.length; ++i) {
+ let keys = [Qt.Key_Escape, Qt.Key_Tab]
+ for (let i = 0; i < keys.length; ++i) {
keyClick(keys[i])
compare(control.checked, false)
verify(spy.success)
@@ -549,7 +551,7 @@ TestCase {
}
function test_binding() {
- var container = createTemporaryObject(twoSwitches, testCase)
+ let container = createTemporaryObject(twoSwitches, testCase)
verify(container)
compare(container.sw1.checked, false)
@@ -565,13 +567,13 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(swtch, testCase)
+ let control = createTemporaryObject(swtch, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
function test_focus() {
- var control = createTemporaryObject(swtch, testCase)
+ let control = createTemporaryObject(swtch, testCase)
verify(control)
verify(!control.activeFocus)
@@ -597,9 +599,9 @@ TestCase {
}
function test_deletionOrder() {
- var control1 = createTemporaryObject(deletionOrder1, testCase)
+ let control1 = createTemporaryObject(deletionOrder1, testCase)
verify(control1)
- var control2 = createTemporaryObject(deletionOrder2, testCase)
+ let control2 = createTemporaryObject(deletionOrder2, testCase)
verify(control2)
}
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml b/tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml
index 42d58b9668..c03555f59c 100644
--- a/tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_switchdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -27,27 +27,29 @@ TestCase {
// TODO: data-fy tst_checkbox (rename to tst_check?) so we don't duplicate its tests here?
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(switchDelegate, testCase);
- verify(control);
- verify(!control.checked);
+ function test_defaults() {
+ let control = createTemporaryObject(switchDelegate, testCase);
+ verify(control)
+ verify(!control.checked)
}
function test_checked() {
- var control = createTemporaryObject(switchDelegate, testCase);
- verify(control);
+ let control = createTemporaryObject(switchDelegate, testCase);
+ verify(control)
- mouseClick(control);
- verify(control.checked);
+ mouseClick(control)
+ verify(control.checked)
- mouseClick(control);
- verify(!control.checked);
+ mouseClick(control)
+ verify(!control.checked)
}
function test_baseline() {
- var control = createTemporaryObject(switchDelegate, testCase);
+ let control = createTemporaryObject(switchDelegate, testCase);
verify(control);
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset);
}
@@ -60,7 +62,7 @@ TestCase {
}
function test_pressed(data) {
- var control = createTemporaryObject(switchDelegate, testCase, {padding: 10})
+ let control = createTemporaryObject(switchDelegate, testCase, {padding: 10})
verify(control)
// stays pressed when dragged outside
@@ -74,11 +76,11 @@ TestCase {
}
function test_mouse() {
- var control = createTemporaryObject(switchDelegate, testCase)
+ let control = createTemporaryObject(switchDelegate, testCase)
verify(control)
// check
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton)
@@ -175,13 +177,13 @@ TestCase {
}
function test_touch() {
- var control = createTemporaryObject(switchDelegate, testCase)
+ let control = createTemporaryObject(switchDelegate, testCase)
verify(control)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
// check
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
"pressed"]
touch.press(0, control, control.width / 2, control.height / 2).commit()
@@ -273,10 +275,10 @@ TestCase {
}
function test_mouseDrag() {
- var control = createTemporaryObject(switchDelegate, testCase, {leftPadding: 100, rightPadding: 100})
+ let control = createTemporaryObject(switchDelegate, testCase, {leftPadding: 100, rightPadding: 100})
verify(control)
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
compare(control.position, 0.0)
compare(control.checked, false)
compare(control.pressed, false)
@@ -378,12 +380,12 @@ TestCase {
}
function test_touchDrag() {
- var control = createTemporaryObject(switchDelegate, testCase, {leftPadding: 100, rightPadding: 100})
+ let control = createTemporaryObject(switchDelegate, testCase, {leftPadding: 100, rightPadding: 100})
verify(control)
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
- var spy = signalSequenceSpy.createObject(control, {target: control})
+ let spy = signalSequenceSpy.createObject(control, {target: control})
compare(control.position, 0.0)
compare(control.checked, false)
compare(control.pressed, false)
@@ -488,11 +490,11 @@ TestCase {
}
function test_spacing() {
- var control = createTemporaryObject(switchDelegate, testCase, { text: "Some long, long, long text" })
+ let control = createTemporaryObject(switchDelegate, testCase, { text: "Some long, long, long text" })
verify(control)
verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth)
- var textLabel = findChild(control.contentItem, "label")
+ let textLabel = findChild(control.contentItem, "label")
verify(textLabel)
// The implicitWidth of the IconLabel that all buttons use as their contentItem should be
@@ -519,7 +521,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(switchDelegate, testCase, {
+ let control = createTemporaryObject(switchDelegate, testCase, {
text: "SwitchDelegate",
display: data.display,
width: 400,
@@ -529,11 +531,11 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
- var availableWidth = control.availableWidth - control.indicator.width - control.spacing
- var indicatorOffset = control.mirrored ? control.indicator.width + control.spacing : 0
+ let availableWidth = control.availableWidth - control.indicator.width - control.spacing
+ let indicatorOffset = control.mirrored ? control.indicator.width + control.spacing : 0
switch (control.display) {
case SwitchDelegate.IconOnly:
diff --git a/tests/auto/quickcontrols/controls/data/tst_tabbar.qml b/tests/auto/quickcontrols/controls/data/tst_tabbar.qml
index 58c0b1ae61..f4b8d170ce 100644
--- a/tests/auto/quickcontrols/controls/data/tst_tabbar.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_tabbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -53,10 +53,12 @@ TestCase {
SignalSpy { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(tabBar, testCase)
+ function test_defaults() {
+ let control = createTemporaryObject(tabBar, testCase)
verify(control)
compare(control.count, 0)
compare(control.currentIndex, -1)
@@ -64,7 +66,7 @@ TestCase {
}
function test_current() {
- var control = createTemporaryObject(tabBar, testCase)
+ let control = createTemporaryObject(tabBar, testCase)
compare(control.count, 0)
compare(control.currentIndex, -1)
@@ -110,7 +112,7 @@ TestCase {
}
function test_current_static() {
- var control = createTemporaryObject(tabBarStaticTabs, testCase)
+ let control = createTemporaryObject(tabBarStaticTabs, testCase)
compare(control.count, 2)
compare(control.currentIndex, 0)
@@ -126,7 +128,7 @@ TestCase {
}
function test_addRemove() {
- var control = createTemporaryObject(tabBar, testCase)
+ let control = createTemporaryObject(tabBar, testCase)
function verifyCurrentIndexCountDiff() {
verify(control.currentIndex < control.count)
@@ -134,7 +136,7 @@ TestCase {
control.currentIndexChanged.connect(verifyCurrentIndexCountDiff)
control.countChanged.connect(verifyCurrentIndexCountDiff)
- var contentChildrenSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "contentChildrenChanged"})
+ let contentChildrenSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "contentChildrenChanged"})
verify(contentChildrenSpy.valid)
compare(control.count, 0)
@@ -226,7 +228,7 @@ TestCase {
}
function test_removeCurrent() {
- var control = createTemporaryObject(tabBar, testCase)
+ let control = createTemporaryObject(tabBar, testCase)
control.addItem(tabButton.createObject(control, {text: "1"}))
control.addItem(tabButton.createObject(control, {text: "2"}))
@@ -260,19 +262,19 @@ TestCase {
}
function test_content() {
- var control = createTemporaryObject(contentBar, testCase)
+ let control = createTemporaryObject(contentBar, testCase)
function compareObjectNames(content, names) {
if (content.length !== names.length)
return false
- for (var i = 0; i < names.length; ++i) {
+ for (let i = 0; i < names.length; ++i) {
if (content[i].objectName !== names[i])
return false
}
return true
}
- var contentChildrenSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "contentChildrenChanged"})
+ let contentChildrenSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "contentChildrenChanged"})
verify(contentChildrenSpy.valid)
verify(compareObjectNames(control.contentData, ["object", "button1", "timer", "button2", ""]))
@@ -312,20 +314,20 @@ TestCase {
}
function test_repeater() {
- var control = createTemporaryObject(repeated, testCase)
+ let control = createTemporaryObject(repeated, testCase)
verify(control)
- var model = control.contentModel
+ let model = control.contentModel
verify(model)
- var repeater = control.repeater
+ let repeater = control.repeater
verify(repeater)
compare(repeater.count, 5)
compare(model.count, 5)
- for (var i = 0; i < 5; ++i) {
- var item1 = control.itemAt(i)
+ for (let i = 0; i < 5; ++i) {
+ let item1 = control.itemAt(i)
verify(item1)
compare(item1.idx, i)
compare(model.get(i), item1)
@@ -336,8 +338,8 @@ TestCase {
compare(repeater.count, 3)
compare(model.count, 3)
- for (var j = 0; j < 3; ++j) {
- var item2 = control.itemAt(j)
+ for (let j = 0; j < 3; ++j) {
+ let item2 = control.itemAt(j)
verify(item2)
compare(item2.idx, j)
compare(model.get(j), item2)
@@ -366,7 +368,7 @@ TestCase {
}
function test_order() {
- var control = createTemporaryObject(ordered, testCase)
+ let control = createTemporaryObject(ordered, testCase)
verify(control)
compare(control.count, 7)
@@ -415,12 +417,12 @@ TestCase {
}
function test_move(data) {
- var control = createTemporaryObject(tabBar, testCase)
+ let control = createTemporaryObject(tabBar, testCase)
compare(control.count, 0)
- var titles = ["1", "2", "3"]
+ let titles = ["1", "2", "3"]
- var i = 0;
+ let i = 0;
for (i = 0; i < titles.length; ++i)
control.addItem(tabButton.createObject(control, {text: titles[i]}))
@@ -434,7 +436,7 @@ TestCase {
compare(control.count, titles.length)
compare(control.currentIndex, data.currentAfter)
- var title = titles[data.from]
+ let title = titles[data.from]
titles.splice(data.from, 1)
titles.splice(data.to, 0, title)
@@ -457,13 +459,13 @@ TestCase {
}
function test_dynamic() {
- var control = createTemporaryObject(dynamicBar, testCase)
+ let control = createTemporaryObject(dynamicBar, testCase)
// insertItem(), addItem(), createObject() and static TabButton {}
compare(control.count, 4)
compare(control.itemAt(0).text, "inserted")
- var tab = tabButton.createObject(control, {text: "dying"})
+ let tab = tabButton.createObject(control, {text: "dying"})
compare(control.count, 5)
compare(control.itemAt(4).text, "dying")
@@ -482,13 +484,13 @@ TestCase {
}
function test_layout(data) {
- var control = createTemporaryObject(tabBar, testCase, {spacing: data.spacing, width: 200})
+ let control = createTemporaryObject(tabBar, testCase, {spacing: data.spacing, width: 200})
// remove the background so that it won't affect the implicit size of the tabbar,
// so the implicit sizes tested below are entirely based on the content size
control.background = null
- var tab1 = tabButton.createObject(control, {text: "First"})
+ let tab1 = tabButton.createObject(control, {text: "First"})
control.addItem(tab1)
tryCompare(tab1, "width", control.width)
compare(tab1.height, control.height)
@@ -499,7 +501,7 @@ TestCase {
compare(control.implicitWidth, control.contentWidth + control.leftPadding + control.rightPadding)
compare(control.implicitHeight, control.contentHeight + control.topPadding + control.bottomPadding)
- var tab2 = tabButton.createObject(control, {implicitHeight: tab1.implicitHeight + 10, text: "Second"})
+ let tab2 = tabButton.createObject(control, {implicitHeight: tab1.implicitHeight + 10, text: "Second"})
control.addItem(tab2)
tryCompare(tab1, "width", (control.width - data.spacing) / 2)
compare(tab1.height, control.height)
@@ -512,7 +514,7 @@ TestCase {
compare(control.implicitWidth, control.contentWidth + control.leftPadding + control.rightPadding)
compare(control.implicitHeight, control.contentHeight + control.topPadding + control.bottomPadding)
- var tab3 = tabButton.createObject(control, {width: 50, height: tab1.implicitHeight - 10, text: "Third"})
+ let tab3 = tabButton.createObject(control, {width: 50, height: tab1.implicitHeight - 10, text: "Third"})
control.addItem(tab3)
tryCompare(tab1, "width", (control.width - 2 * data.spacing - 50) / 2)
compare(tab1.y, 0)
@@ -531,7 +533,7 @@ TestCase {
compare(control.implicitWidth, control.contentWidth + control.leftPadding + control.rightPadding)
compare(control.implicitHeight, control.contentHeight + control.topPadding + control.bottomPadding)
- var expectedWidth = tab3.contentItem.implicitWidth + tab3.leftPadding + tab3.rightPadding
+ let expectedWidth = tab3.contentItem.implicitWidth + tab3.leftPadding + tab3.rightPadding
tab3.width = tab3.implicitWidth
tab3.height = tab3.implicitHeight
tryCompare(tab1, "width", (control.width - 2 * data.spacing - expectedWidth) / 2)
@@ -621,10 +623,10 @@ TestCase {
}
function test_attached() {
- var control = createTemporaryObject(tabBar, testCase, {position: TabBar.Footer})
+ let control = createTemporaryObject(tabBar, testCase, {position: TabBar.Footer})
// append
- var tab1 = createTemporaryObject(attachedButton, testCase)
+ let tab1 = createTemporaryObject(attachedButton, testCase)
compare(tab1.index, -1)
compare(tab1.tabBar, null)
compare(tab1.position, TabBar.Header)
@@ -635,7 +637,7 @@ TestCase {
compare(tab1.position, TabBar.Footer)
// insert in the beginning
- var tab2 = createTemporaryObject(attachedButton, testCase)
+ let tab2 = createTemporaryObject(attachedButton, testCase)
compare(tab2.index, -1)
compare(tab2.tabBar, null)
compare(tab2.position, TabBar.Header)
@@ -648,7 +650,7 @@ TestCase {
compare(tab1.index, 1)
// insert in the middle
- var tab3 = createTemporaryObject(attachedButton, testCase)
+ let tab3 = createTemporaryObject(attachedButton, testCase)
compare(tab3.index, -1)
compare(tab3.tabBar, null)
compare(tab3.position, TabBar.Header)
@@ -662,7 +664,7 @@ TestCase {
compare(tab1.index, 2)
// insert in the end
- var tab4 = createTemporaryObject(attachedButton, testCase)
+ let tab4 = createTemporaryObject(attachedButton, testCase)
compare(tab4.index, -1)
compare(tab4.tabBar, null)
compare(tab4.position, TabBar.Header)
diff --git a/tests/auto/quickcontrols/controls/data/tst_tabbutton.qml b/tests/auto/quickcontrols/controls/data/tst_tabbutton.qml
index 591622b8a2..1c6181a97f 100644
--- a/tests/auto/quickcontrols/controls/data/tst_tabbutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_tabbutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -28,22 +28,24 @@ TestCase {
}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(tabButton, testCase)
verify(control)
}
function test_autoExclusive() {
- var container = createTemporaryObject(repeater, testCase)
+ let container = createTemporaryObject(repeater, testCase)
- for (var i = 0; i < 3; ++i) {
+ for (let i = 0; i < 3; ++i) {
container.children[i].checked = true
compare(container.children[i].checked, true)
// check that all other buttons are unchecked
- for (var j = 0; j < 3; ++j) {
+ for (let j = 0; j < 3; ++j) {
if (j !== i)
compare(container.children[j].checked, false)
}
@@ -51,18 +53,18 @@ TestCase {
}
function test_baseline() {
- var control = createTemporaryObject(tabButton, testCase)
+ let control = createTemporaryObject(tabButton, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
function test_spacing() {
- var control = createTemporaryObject(tabButton, testCase, { text: "Some long, long, long text" })
+ let control = createTemporaryObject(tabButton, testCase, { text: "Some long, long, long text" })
verify(control)
if (control.background)
verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth)
- var textLabel = findChild(control.contentItem, "label")
+ let textLabel = findChild(control.contentItem, "label")
verify(textLabel)
// The implicitWidth of the IconLabel that all buttons use as their contentItem
@@ -91,7 +93,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(tabButton, testCase, {
+ let control = createTemporaryObject(tabButton, testCase, {
text: "TabButton",
display: data.display,
"icon.source": "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png",
@@ -100,8 +102,8 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
switch (control.display) {
case TabButton.IconOnly:
diff --git a/tests/auto/quickcontrols/controls/data/tst_textarea.qml b/tests/auto/quickcontrols/controls/data/tst_textarea.qml
index 0f295905f1..5e28b6733f 100644
--- a/tests/auto/quickcontrols/controls/data/tst_textarea.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_textarea.qml
@@ -1,9 +1,10 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
import QtQuick.Controls
+import Qt.test.controls
TestCase {
id: testCase
@@ -68,33 +69,35 @@ TestCase {
id: util
}
- function test_creation() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(textArea, testCase)
+ function test_creation() {
+ let control = createTemporaryObject(textArea, testCase)
verify(control)
}
function test_implicitSize() {
- var control = createTemporaryObject(textArea, testCase)
+ let control = createTemporaryObject(textArea, testCase)
verify(control)
- var implicitWidthSpy = signalSpy.createObject(control, { target: control, signalName: "implicitWidthChanged"} )
+ let implicitWidthSpy = signalSpy.createObject(control, { target: control, signalName: "implicitWidthChanged"} )
verify(implicitWidthSpy.valid)
- var implicitHeightSpy = signalSpy.createObject(control, { target: control, signalName: "implicitHeightChanged"} )
+ let implicitHeightSpy = signalSpy.createObject(control, { target: control, signalName: "implicitHeightChanged"} )
verify(implicitHeightSpy.valid)
- var implicitBackgroundWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundWidthChanged"})
+ let implicitBackgroundWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundWidthChanged"})
verify(implicitBackgroundWidthSpy.valid)
- var implicitBackgroundHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundHeightChanged"})
+ let implicitBackgroundHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundHeightChanged"})
verify(implicitBackgroundHeightSpy.valid)
- var implicitWidthChanges = 0
- var implicitHeightChanges = 0
- var implicitBackgroundWidthChanges = 0
- var implicitBackgroundHeightChanges = 0
+ let implicitWidthChanges = 0
+ let implicitHeightChanges = 0
+ let implicitBackgroundWidthChanges = 0
+ let implicitBackgroundHeightChanges = 0
verify(control.implicitWidth >= control.leftPadding + control.rightPadding)
verify(control.implicitHeight >= control.contentHeight + control.topPadding + control.bottomPadding)
@@ -129,11 +132,11 @@ TestCase {
compare(implicitWidthSpy.count, ++implicitWidthChanges)
defaultFontMetrics.font = control.font
- var leading = defaultFontMetrics.leading
- var ascent = defaultFontMetrics.ascent
- var descent = defaultFontMetrics.descent
+ let leading = defaultFontMetrics.leading
+ let ascent = defaultFontMetrics.ascent
+ let descent = defaultFontMetrics.descent
- var leadingOverflow = Math.ceil(ascent + descent) < Math.ceil(ascent + descent + leading)
+ let leadingOverflow = Math.ceil(ascent + descent) < Math.ceil(ascent + descent + leading)
// If the font in use triggers QTBUG-83894, it is possible that this will cause
// the following compare to fail if the implicitHeight from the TextEdit is ued.
@@ -206,20 +209,28 @@ TestCase {
}
function test_alignment(data) {
- var control = createTemporaryObject(textArea, testCase, {text: data.text, placeholderText: data.placeholderText, horizontalAlignment: data.textAlignment})
+ let control = createTemporaryObject(textArea, testCase, {text: data.text, placeholderText: data.placeholderText})
- if (data.textAlignment !== undefined)
+ if (data.textAlignment !== undefined) {
+ control.horizontalAlignment = data.textAlignment
compare(control.horizontalAlignment, data.textAlignment)
- for (var i = 0; i < control.children.length; ++i) {
- if (control.children[i].hasOwnProperty("horizontalAlignment"))
- compare(control.children[i].effectiveHorizontalAlignment, data.placeholderAlignment) // placeholder
+ }
+
+ // The placeholder text of the Material style doesn't currently respect the alignment of the control.
+ if (StyleInfo.styleName !== "Material") {
+ for (let i = 0; i < control.children.length; ++i) {
+ if (control.children[i].hasOwnProperty("horizontalAlignment"))
+ compare(control.children[i].effectiveHorizontalAlignment, data.placeholderAlignment) // placeholder
+ }
}
control.verticalAlignment = TextArea.AlignBottom
compare(control.verticalAlignment, TextArea.AlignBottom)
- for (var j = 0; j < control.children.length; ++j) {
- if (control.children[j].hasOwnProperty("verticalAlignment"))
- compare(control.children[j].verticalAlignment, Text.AlignBottom) // placeholder
+ if (StyleInfo.styleName !== "Material") {
+ for (let j = 0; j < control.children.length; ++j) {
+ if (control.children[j].hasOwnProperty("verticalAlignment"))
+ compare(control.children[j].verticalAlignment, Text.AlignBottom) // placeholder
+ }
}
}
@@ -227,7 +238,7 @@ TestCase {
return [
{tag: "bold", value: true},
{tag: "capitalization", value: Font.Capitalize},
- {tag: "family", value: "Courier"},
+ {tag: "family", value: "Tahoma"},
{tag: "italic", value: true},
{tag: "strikeout", value: true},
{tag: "underline", value: true},
@@ -237,19 +248,19 @@ TestCase {
}
function test_font_explicit_attributes(data) {
- var control = createTemporaryObject(textArea, testCase)
+ let control = createTemporaryObject(textArea, testCase)
verify(control)
- var child = textArea.createObject(control)
+ let child = textArea.createObject(control)
verify(child)
- var controlSpy = signalSpy.createObject(control, {target: control, signalName: "fontChanged"})
+ let controlSpy = signalSpy.createObject(control, {target: control, signalName: "fontChanged"})
verify(controlSpy.valid)
- var childSpy = signalSpy.createObject(child, {target: child, signalName: "fontChanged"})
+ let childSpy = signalSpy.createObject(child, {target: child, signalName: "fontChanged"})
verify(childSpy.valid)
- var defaultValue = control.font[data.tag]
+ let defaultValue = control.font[data.tag]
child.font[data.tag] = defaultValue
compare(child.font[data.tag], defaultValue)
@@ -265,33 +276,33 @@ TestCase {
}
function test_flickable() {
- var control = createTemporaryObject(flickable, testCase, {text:"line0", selectByMouse: true})
+ let control = createTemporaryObject(flickable, testCase)
verify(control)
- var textArea = control.TextArea.flickable
+ let textArea = control.TextArea.flickable
verify(textArea)
if (textArea.background)
compare(textArea.background.parent, control)
- for (var i = 1; i <= 100; ++i)
+ for (let i = 1; i <= 100; ++i)
textArea.text += "line\n" + i
verify(textArea.contentWidth > 0)
verify(textArea.contentHeight > 200)
- compare(control.contentWidth, textArea.contentWidth + textArea.leftPadding + textArea.rightPadding)
- compare(control.contentHeight, textArea.contentHeight + textArea.topPadding + textArea.bottomPadding)
+ compare(control.contentWidth, textArea.implicitWidth)
+ compare(control.contentHeight, textArea.implicitHeight)
compare(textArea.cursorPosition, 0)
- var center = textArea.positionAt(control.width / 2, control.height / 2)
+ let center = textArea.positionAt(control.width / 2, control.height / 2)
verify(center > 0)
mouseClick(textArea, control.width / 2, control.height / 2)
compare(textArea.cursorPosition, center)
// click inside text area, but below flickable
- var below = textArea.positionAt(control.width / 2, control.height + 1)
+ let below = textArea.positionAt(control.width / 2, control.height + 1)
verify(below > center)
mouseClick(textArea, control.width / 2, control.height + 1)
compare(textArea.cursorPosition, center) // no change
@@ -300,7 +311,7 @@ TestCase {
control.contentY = -(control.contentHeight - control.height) / 2
// click inside textarea, but above flickable
- var above = textArea.positionAt(control.width / 2, textArea.topPadding)
+ let above = textArea.positionAt(control.width / 2, textArea.topPadding)
verify(above > 0 && above < center)
mouseClick(textArea, control.width / 2, 0)
compare(textArea.cursorPosition, center) // no change
@@ -310,10 +321,10 @@ TestCase {
// Test that the TextArea background item is parented out of the
// TextArea and into the Flicable, and that it has the same size
// as the flickable.
- var flickable = createTemporaryObject(flickableCustomBackground, testCase)
+ let flickable = createTemporaryObject(flickableCustomBackground, testCase)
verify(flickable)
- var textArea = flickable.TextArea.flickable
+ let textArea = flickable.TextArea.flickable
verify(textArea)
verify(textArea.background)
compare(textArea.background.width, flickable.width)
@@ -321,10 +332,10 @@ TestCase {
}
function test_scrollable_paste_large() {
- var control = createTemporaryObject(flickableWithScrollBar, testCase)
+ let control = createTemporaryObject(flickableWithScrollBar, testCase)
verify(control)
- var textArea = control.TextArea.flickable
+ let textArea = control.TextArea.flickable
verify(textArea)
if (typeof(textArea.paste) !== "function")
@@ -352,7 +363,7 @@ TestCase {
}
function test_hover(data) {
- var control = createTemporaryObject(textArea, testCase, {text: "TextArea", hoverEnabled: data.hoverEnabled})
+ let control = createTemporaryObject(textArea, testCase, {text: "TextArea", hoverEnabled: data.hoverEnabled})
verify(control)
compare(control.hovered, false)
@@ -417,9 +428,9 @@ TestCase {
}
function test_pressedReleased(data) {
- var mouseArea = createTemporaryObject(mouseAreaComponent, testCase)
+ let mouseArea = createTemporaryObject(mouseAreaComponent, testCase)
verify(mouseArea)
- var control = textArea.createObject(mouseArea, {text: "TextArea"})
+ let control = textArea.createObject(mouseArea, {text: "TextArea"})
verify(control)
// Give enough room to check presses outside of the control and on the parent.
@@ -449,13 +460,13 @@ TestCase {
if (data.parentReleaseEvent)
control.onReleased.connect(checkParentReleaseEvent)
- var controlPressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
+ let controlPressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
verify(controlPressedSpy.valid)
- var controlReleasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
+ let controlReleasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
verify(controlReleasedSpy.valid)
- var parentPressedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "pressed" })
+ let parentPressedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "pressed" })
verify(parentPressedSpy.valid)
- var parentReleasedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "released" })
+ let parentReleasedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "released" })
verify(parentReleasedSpy.valid)
mousePress(control, data.x, data.y, data.button)
@@ -473,8 +484,8 @@ TestCase {
property bool ignorePress: false
property bool ignoreRelease: false
- onPressed: if (ignorePress) event.accepted = false
- onReleased: if (ignoreRelease) event.accepted = false
+ onPressed: function (event) { if (ignorePress) event.accepted = false }
+ onReleased: function (event) { if (ignoreRelease) event.accepted = false }
}
}
@@ -487,18 +498,18 @@ TestCase {
}
function test_ignorePressRelease() {
- var mouseArea = createTemporaryObject(mouseAreaComponent, testCase)
+ let mouseArea = createTemporaryObject(mouseAreaComponent, testCase)
verify(mouseArea)
- var control = ignoreTextArea.createObject(mouseArea)
+ let control = ignoreTextArea.createObject(mouseArea)
verify(control)
- var controlPressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
+ let controlPressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
verify(controlPressedSpy.valid)
- var controlReleasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
+ let controlReleasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
verify(controlReleasedSpy.valid)
- var parentPressedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "pressed" })
+ let parentPressedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "pressed" })
verify(parentPressedSpy.valid)
- var parentReleasedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "released" })
+ let parentReleasedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "released" })
verify(parentReleasedSpy.valid)
// Ignore only press events.
@@ -529,12 +540,12 @@ TestCase {
}
function test_multiClick() {
- var control = createTemporaryObject(textArea, testCase, {text: "Qt Quick Controls 2 TextArea", selectByMouse: true})
+ let control = createTemporaryObject(textArea, testCase, {text: "Qt Quick Controls 2 TextArea", selectByMouse: true})
verify(control)
waitForRendering(control)
control.width = control.contentWidth
- var rect = control.positionToRectangle(12)
+ let rect = control.positionToRectangle(12)
// double click -> select word
mouseDoubleClickSequence(control, rect.x + rect.width / 2, rect.y + rect.height / 2)
@@ -553,7 +564,7 @@ TestCase {
}
function test_scrollView() {
- var control = createTemporaryObject(scrollView, testCase)
+ let control = createTemporaryObject(scrollView, testCase)
verify(control)
// don't crash (QTBUG-62292)
@@ -562,7 +573,7 @@ TestCase {
}
function test_placeholderTextColor() {
- var control = createTemporaryObject(textArea, testCase)
+ let control = createTemporaryObject(textArea, testCase)
verify(control)
// usually default value should not be pure opacue black
@@ -570,32 +581,32 @@ TestCase {
control.placeholderTextColor = "#12345678"
compare(control.placeholderTextColor, "#12345678")
- for (var i = 0; i < control.children.length; ++i) {
+ for (let i = 0; i < control.children.length; ++i) {
if (control.children[i].hasOwnProperty("text"))
compare(control.children[i].color, control.placeholderTextColor) // placeholder.color
}
}
function test_inset() {
- var control = createTemporaryObject(textArea, testCase, {background: rectangle.createObject(control)})
+ let control = createTemporaryObject(textArea, testCase, {background: rectangle.createObject(testCase)})
verify(control)
- var topInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topInsetChanged"})
+ let topInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topInsetChanged"})
verify(topInsetSpy.valid)
- var leftInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftInsetChanged"})
+ let leftInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftInsetChanged"})
verify(leftInsetSpy.valid)
- var rightInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightInsetChanged"})
+ let rightInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightInsetChanged"})
verify(rightInsetSpy.valid)
- var bottomInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomInsetChanged"})
+ let bottomInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomInsetChanged"})
verify(bottomInsetSpy.valid)
- var topInsetChanges = 0
- var leftInsetChanges = 0
- var rightInsetChanges = 0
- var bottomInsetChanges = 0
+ let topInsetChanges = 0
+ let leftInsetChanges = 0
+ let rightInsetChanges = 0
+ let bottomInsetChanges = 0
compare(control.topInset, 0)
compare(control.leftInset, 0)
@@ -756,7 +767,7 @@ TestCase {
}
function test_resize_background() {
- var control = createTemporaryObject(testResizeBackground, testCase)
+ let control = createTemporaryObject(testResizeBackground, testCase)
compare(control.textArea.background.width, control.width)
compare(control.textArea.background.height, 1)
diff --git a/tests/auto/quickcontrols/controls/data/tst_textfield.qml b/tests/auto/quickcontrols/controls/data/tst_textfield.qml
index f5b3d91fe1..ef1cca71b5 100644
--- a/tests/auto/quickcontrols/controls/data/tst_textfield.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_textfield.qml
@@ -1,10 +1,11 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
import QtQuick.Controls
import QtQuick.Layouts
+import Qt.test.controls
TestCase {
id: testCase
@@ -29,33 +30,35 @@ TestCase {
SignalSpy { }
}
- function test_creation() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(textField, testCase)
+ function test_creation() {
+ let control = createTemporaryObject(textField, testCase)
verify(control)
}
function test_implicitSize() {
- var control = createTemporaryObject(textField, testCase)
+ let control = createTemporaryObject(textField, testCase)
verify(control)
- var implicitWidthSpy = signalSpy.createObject(control, { target: control, signalName: "implicitWidthChanged"} )
+ let implicitWidthSpy = signalSpy.createObject(control, { target: control, signalName: "implicitWidthChanged"} )
verify(implicitWidthSpy.valid)
- var implicitHeightSpy = signalSpy.createObject(control, { target: control, signalName: "implicitHeightChanged"} )
+ let implicitHeightSpy = signalSpy.createObject(control, { target: control, signalName: "implicitHeightChanged"} )
verify(implicitHeightSpy.valid)
- var implicitBackgroundWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundWidthChanged"})
+ let implicitBackgroundWidthSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundWidthChanged"})
verify(implicitBackgroundWidthSpy.valid)
- var implicitBackgroundHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundHeightChanged"})
+ let implicitBackgroundHeightSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "implicitBackgroundHeightChanged"})
verify(implicitBackgroundHeightSpy.valid)
- var implicitWidthChanges = 0
- var implicitHeightChanges = 0
- var implicitBackgroundWidthChanges = 0
- var implicitBackgroundHeightChanges = 0
+ let implicitWidthChanges = 0
+ let implicitHeightChanges = 0
+ let implicitBackgroundWidthChanges = 0
+ let implicitBackgroundHeightChanges = 0
verify(control.implicitWidth >= control.leftPadding + control.rightPadding)
verify(control.implicitHeight >= control.contentHeight + control.topPadding + control.bottomPadding)
@@ -153,20 +156,27 @@ TestCase {
}
function test_alignment(data) {
- var control = createTemporaryObject(textField, testCase, {text: data.text, placeholderText: data.placeholderText, horizontalAlignment: data.textAlignment})
+ let control = createTemporaryObject(textField, testCase, {text: data.text, placeholderText: data.placeholderText})
- if (data.textAlignment !== undefined)
+ if (data.textAlignment !== undefined) {
+ control.horizontalAlignment = data.textAlignment
compare(control.horizontalAlignment, data.textAlignment)
- for (var i = 0; i < control.children.length; ++i) {
- if (control.children[i].hasOwnProperty("text") && control.children[i].hasOwnProperty("horizontalAlignment"))
- compare(control.children[i].effectiveHorizontalAlignment, data.placeholderAlignment) // placeholder
+ }
+ // The placeholder text of the Material style doesn't currently respect the alignment of the control.
+ if (StyleInfo.styleName !== "Material") {
+ for (let i = 0; i < control.children.length; ++i) {
+ if (control.children[i].hasOwnProperty("text") && control.children[i].hasOwnProperty("horizontalAlignment"))
+ compare(control.children[i].effectiveHorizontalAlignment, data.placeholderAlignment) // placeholder
+ }
}
control.verticalAlignment = TextField.AlignBottom
compare(control.verticalAlignment, TextField.AlignBottom)
- for (var j = 0; j < control.children.length; ++j) {
- if (control.children[j].hasOwnProperty("text") && control.children[j].hasOwnProperty("verticalAlignment"))
- compare(control.children[j].verticalAlignment, Text.AlignBottom) // placeholder
+ if (StyleInfo.styleName !== "Material") {
+ for (let j = 0; j < control.children.length; ++j) {
+ if (control.children[j].hasOwnProperty("text") && control.children[j].hasOwnProperty("verticalAlignment"))
+ compare(control.children[j].verticalAlignment, Text.AlignBottom) // placeholder
+ }
}
}
@@ -174,7 +184,7 @@ TestCase {
return [
{tag: "bold", value: true},
{tag: "capitalization", value: Font.Capitalize},
- {tag: "family", value: "Courier"},
+ {tag: "family", value: "Tahoma"},
{tag: "italic", value: true},
{tag: "strikeout", value: true},
{tag: "underline", value: true},
@@ -184,19 +194,19 @@ TestCase {
}
function test_font_explicit_attributes(data) {
- var control = createTemporaryObject(textField, testCase)
+ let control = createTemporaryObject(textField, testCase)
verify(control)
- var child = textField.createObject(control)
+ let child = textField.createObject(control)
verify(child)
- var controlSpy = signalSpy.createObject(control, {target: control, signalName: "fontChanged"})
+ let controlSpy = signalSpy.createObject(control, {target: control, signalName: "fontChanged"})
verify(controlSpy.valid)
- var childSpy = signalSpy.createObject(child, {target: child, signalName: "fontChanged"})
+ let childSpy = signalSpy.createObject(child, {target: child, signalName: "fontChanged"})
verify(childSpy.valid)
- var defaultValue = control.font[data.tag]
+ let defaultValue = control.font[data.tag]
child.font[data.tag] = defaultValue
compare(child.font[data.tag], defaultValue)
@@ -219,7 +229,7 @@ TestCase {
}
function test_hover(data) {
- var control = createTemporaryObject(textField, testCase, {hoverEnabled: data.hoverEnabled})
+ let control = createTemporaryObject(textField, testCase, {hoverEnabled: data.hoverEnabled})
verify(control)
compare(control.hovered, false)
@@ -284,9 +294,9 @@ TestCase {
}
function test_pressedReleased(data) {
- var mouseArea = createTemporaryObject(mouseAreaComponent, testCase)
+ let mouseArea = createTemporaryObject(mouseAreaComponent, testCase)
verify(mouseArea)
- var control = textField.createObject(mouseArea)
+ let control = textField.createObject(mouseArea)
verify(control)
// Give enough room to check presses outside of the control and on the parent.
@@ -316,13 +326,13 @@ TestCase {
if (data.parentReleaseEvent)
control.onReleased.connect(checkParentReleaseEvent)
- var controlPressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
+ let controlPressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
verify(controlPressedSpy.valid)
- var controlReleasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
+ let controlReleasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
verify(controlReleasedSpy.valid)
- var parentPressedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "pressed" })
+ let parentPressedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "pressed" })
verify(parentPressedSpy.valid)
- var parentReleasedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "released" })
+ let parentReleasedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "released" })
verify(parentReleasedSpy.valid)
mousePress(control, data.x, data.y, data.button)
@@ -340,8 +350,8 @@ TestCase {
property bool ignorePress: false
property bool ignoreRelease: false
- onPressed: if (ignorePress) event.accepted = false
- onReleased: if (ignoreRelease) event.accepted = false
+ onPressed: function (event) { if (ignorePress) event.accepted = false }
+ onReleased: function (event) { if (ignoreRelease) event.accepted = false }
}
}
@@ -354,18 +364,18 @@ TestCase {
}
function test_ignorePressRelease() {
- var mouseArea = createTemporaryObject(mouseAreaComponent, testCase)
+ let mouseArea = createTemporaryObject(mouseAreaComponent, testCase)
verify(mouseArea)
- var control = ignoreTextField.createObject(mouseArea)
+ let control = ignoreTextField.createObject(mouseArea)
verify(control)
- var controlPressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
+ let controlPressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" })
verify(controlPressedSpy.valid)
- var controlReleasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
+ let controlReleasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" })
verify(controlReleasedSpy.valid)
- var parentPressedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "pressed" })
+ let parentPressedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "pressed" })
verify(parentPressedSpy.valid)
- var parentReleasedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "released" })
+ let parentReleasedSpy = signalSpy.createObject(mouseArea, { target: mouseArea, signalName: "released" })
verify(parentReleasedSpy.valid)
// Ignore only press events.
@@ -396,12 +406,12 @@ TestCase {
}
function test_multiClick() {
- var control = createTemporaryObject(textField, testCase, {text: "Qt Quick Controls 2 TextArea"})
+ let control = createTemporaryObject(textField, testCase, {text: "Qt Quick Controls 2 TextArea"})
verify(control)
waitForRendering(control)
control.width = control.contentWidth
- var rect = control.positionToRectangle(12)
+ let rect = control.positionToRectangle(12)
// double click -> select word
mouseDoubleClickSequence(control, rect.x + rect.width / 2, rect.y + rect.height / 2)
@@ -414,7 +424,7 @@ TestCase {
// QTBUG-64048
function test_rightClick() {
- var control = createTemporaryObject(textField, testCase, {text: "TextField"})
+ let control = createTemporaryObject(textField, testCase, {text: "TextField"})
verify(control)
control.selectAll()
@@ -428,10 +438,10 @@ TestCase {
}
function test_mouseSelect() {
- var control = createTemporaryObject(textField, testCase, {text: "Text", width: parent.width})
+ let control = createTemporaryObject(textField, testCase, {text: "Text", width: parent.width})
verify(control)
verify(control.selectByMouse) // true by default since 6.4
- var pressSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"})
+ let pressSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"})
const y = control.height / 2
mousePress(control, 0, y, Qt.LeftButton)
@@ -442,11 +452,11 @@ TestCase {
}
function test_noTouchSelect() {
- var control = createTemporaryObject(textField, testCase, {text: "Text"})
+ let control = createTemporaryObject(textField, testCase, {text: "Text"})
verify(control)
verify(control.selectByMouse) // true by default since 6.4
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
const y = control.height / 2
touch.press(0, control, 0, y).commit()
touch.move(0, control, control.implicitWidth, 0).commit()
@@ -455,13 +465,13 @@ TestCase {
}
function test_aaTouchPressAndHold() {
- var control = createTemporaryObject(textField, testCase, {text: "Text"})
+ let control = createTemporaryObject(textField, testCase, {text: "Text"})
verify(control)
verify(control.selectByMouse) // true by default since 6.4
- var pressSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"})
- var pressAndHoldSpy = signalSpy.createObject(control, {target: control, signalName: "pressAndHold"})
+ let pressSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"})
+ let pressAndHoldSpy = signalSpy.createObject(control, {target: control, signalName: "pressAndHold"})
- var touch = touchEvent(control)
+ let touch = touchEvent(control)
touch.press(0, control).commit()
tryCompare(pressSpy, "count", 1)
tryCompare(pressAndHoldSpy, "count", 1)
@@ -470,7 +480,7 @@ TestCase {
// QTBUG-66260
function test_placeholderTextColor() {
- var control = createTemporaryObject(textField, testCase)
+ let control = createTemporaryObject(textField, testCase)
verify(control)
// usually default value should not be pure opacue black
@@ -478,32 +488,32 @@ TestCase {
control.placeholderTextColor = "#12345678"
compare(control.placeholderTextColor, "#12345678")
- for (var i = 0; i < control.children.length; ++i) {
+ for (let i = 0; i < control.children.length; ++i) {
if (control.children[i].hasOwnProperty("text"))
compare(control.children[i].color, control.placeholderTextColor) // placeholder.color
}
}
function test_inset() {
- var control = createTemporaryObject(textField, testCase, {background: rectangle.createObject(control)})
+ let control = createTemporaryObject(textField, testCase, {background: rectangle.createObject(testCase)})
verify(control)
- var topInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topInsetChanged"})
+ let topInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "topInsetChanged"})
verify(topInsetSpy.valid)
- var leftInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftInsetChanged"})
+ let leftInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "leftInsetChanged"})
verify(leftInsetSpy.valid)
- var rightInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightInsetChanged"})
+ let rightInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rightInsetChanged"})
verify(rightInsetSpy.valid)
- var bottomInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomInsetChanged"})
+ let bottomInsetSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "bottomInsetChanged"})
verify(bottomInsetSpy.valid)
- var topInsetChanges = 0
- var leftInsetChanges = 0
- var rightInsetChanges = 0
- var bottomInsetChanges = 0
+ let topInsetChanges = 0
+ let leftInsetChanges = 0
+ let rightInsetChanges = 0
+ let bottomInsetChanges = 0
compare(control.topInset, 0)
compare(control.leftInset, 0)
@@ -647,10 +657,10 @@ TestCase {
}
function test_inLayout() {
- var layout = createTemporaryObject(layoutComponent, testCase)
+ let layout = createTemporaryObject(layoutComponent, testCase)
verify(layout)
- var control = layout.textField
+ let control = layout.textField
verify(control)
compare(control.width, control.parent.width)
@@ -676,14 +686,12 @@ TestCase {
// macOS is special: 43eca45b061fe965fe2a6f1876d4a35a58e3a9e4
if (Qt.platform.os === "osx" || Qt.platform.os === "macos")
skip("TextField hard-codes pixel size on macOS")
- failOnWarning("Both point size and pixel size set. Using pixel size.")
- var textField = createTemporaryObject(textFieldWithPointSizeSet, testCase)
+ let textField = createTemporaryObject(textFieldWithPointSizeSet, testCase)
verify(textField)
}
function test_setPixelSizeDoesNotWarn() {
- failOnWarning("Both point size and pixel size set. Using pixel size.")
- var textField = createTemporaryObject(textFieldWithPixelSizeSet, testCase)
+ let textField = createTemporaryObject(textFieldWithPixelSizeSet, testCase)
verify(textField)
}
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_toolbar.qml b/tests/auto/quickcontrols/controls/data/tst_toolbar.qml
index d2c9e4a20d..e6be264a03 100644
--- a/tests/auto/quickcontrols/controls/data/tst_toolbar.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_toolbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -52,10 +52,12 @@ TestCase {
}
}
- function test_empty() {
+ function init() {
failOnWarning(/.?/)
+ }
- var control = createTemporaryObject(toolBar, testCase)
+ function test_empty() {
+ let control = createTemporaryObject(toolBar, testCase)
verify(control)
verify(control.contentItem)
@@ -66,7 +68,7 @@ TestCase {
}
function test_oneChild() {
- var control = createTemporaryObject(oneChildBar, testCase)
+ let control = createTemporaryObject(oneChildBar, testCase)
verify(control)
compare(control.contentWidth, 100)
@@ -78,7 +80,7 @@ TestCase {
}
function test_twoChildren() {
- var control = createTemporaryObject(twoChildrenBar, testCase)
+ let control = createTemporaryObject(twoChildrenBar, testCase)
verify(control)
compare(control.contentWidth, 0)
@@ -90,7 +92,7 @@ TestCase {
}
function test_contentItem() {
- var control = createTemporaryObject(contentBar, testCase)
+ let control = createTemporaryObject(contentBar, testCase)
verify(control)
compare(control.contentWidth, 100)
diff --git a/tests/auto/quickcontrols/controls/data/tst_toolbutton.qml b/tests/auto/quickcontrols/controls/data/tst_toolbutton.qml
index 4879e91611..2cf746d288 100644
--- a/tests/auto/quickcontrols/controls/data/tst_toolbutton.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_toolbutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -23,15 +23,17 @@ TestCase {
ToolButton { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(toolButton, testCase)
verify(control)
}
function test_text() {
- var control = createTemporaryObject(toolButton, testCase)
+ let control = createTemporaryObject(toolButton, testCase)
verify(control)
compare(control.text, "")
@@ -42,16 +44,16 @@ TestCase {
}
function test_mouse() {
- var control = createTemporaryObject(toolButton, testCase)
+ let control = createTemporaryObject(toolButton, testCase)
verify(control)
- var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
+ let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressedChanged"})
verify(pressedSpy.valid)
- var downSpy = signalSpy.createObject(control, {target: control, signalName: "downChanged"})
+ let downSpy = signalSpy.createObject(control, {target: control, signalName: "downChanged"})
verify(downSpy.valid)
- var clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
+ let clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
verify(clickedSpy.valid)
// check
@@ -111,10 +113,10 @@ TestCase {
}
function test_keys() {
- var control = createTemporaryObject(toolButton, testCase)
+ let control = createTemporaryObject(toolButton, testCase)
verify(control)
- var clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
+ let clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"})
verify(clickedSpy.valid)
control.forceActiveFocus()
@@ -131,15 +133,15 @@ TestCase {
// no change
// Not testing Key_Enter and Key_Return because QGnomeTheme uses them for
// pressing buttons and the CI uses the QGnomeTheme platform theme.
- var keys = [Qt.Key_Escape, Qt.Key_Tab]
- for (var i = 0; i < keys.length; ++i) {
+ let keys = [Qt.Key_Escape, Qt.Key_Tab]
+ for (let i = 0; i < keys.length; ++i) {
keyClick(keys[i])
compare(clickedSpy.count, 2)
}
}
function test_baseline() {
- var control = createTemporaryObject(toolButton, testCase)
+ let control = createTemporaryObject(toolButton, testCase)
verify(control)
compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset)
}
@@ -158,7 +160,7 @@ TestCase {
}
function test_display(data) {
- var control = createTemporaryObject(toolButton, testCase, {
+ let control = createTemporaryObject(toolButton, testCase, {
text: "ToolButton",
display: data.display,
"icon.source": "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png",
@@ -167,8 +169,8 @@ TestCase {
verify(control)
compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png")
- var iconImage = findChild(control.contentItem, "image")
- var textLabel = findChild(control.contentItem, "label")
+ let iconImage = findChild(control.contentItem, "image")
+ let textLabel = findChild(control.contentItem, "label")
switch (control.display) {
case ToolButton.IconOnly:
diff --git a/tests/auto/quickcontrols/controls/data/tst_toolseparator.qml b/tests/auto/quickcontrols/controls/data/tst_toolseparator.qml
index ef7fc35758..f9ca5aec72 100644
--- a/tests/auto/quickcontrols/controls/data/tst_toolseparator.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_toolseparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -17,15 +17,17 @@ TestCase {
ToolSeparator {}
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(toolSeparator, testCase)
verify(control)
}
function test_size() {
- var control = createTemporaryObject(toolSeparator, testCase);
+ let control = createTemporaryObject(toolSeparator, testCase);
verify(control);
verify(control.width > 1);
verify(control.height > 1);
@@ -37,15 +39,15 @@ TestCase {
}
function test_orientation() {
- var control = createTemporaryObject(toolSeparator, testCase);
+ let control = createTemporaryObject(toolSeparator, testCase);
verify(control);
compare(control.horizontal, false);
compare(control.vertical, true);
- var orientationSpy = signalSpyComponent.createObject(control, { target: control, signalName: "orientationChanged" });
+ let orientationSpy = signalSpyComponent.createObject(control, { target: control, signalName: "orientationChanged" });
- var originalWidth = control.width;
- var originalHeight = control.height;
+ let originalWidth = control.width;
+ let originalHeight = control.height;
control.orientation = Qt.Horizontal;
compare(control.orientation, Qt.Horizontal);
compare(control.width, originalHeight);
diff --git a/tests/auto/quickcontrols/controls/data/tst_tooltip.qml b/tests/auto/quickcontrols/controls/data/tst_tooltip.qml
index 1aa63e56a2..205536c61c 100644
--- a/tests/auto/quickcontrols/controls/data/tst_tooltip.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_tooltip.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -37,9 +37,11 @@ TestCase {
target: ToolTip.toolTip
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(toolTip, testCase)
verify(control)
}
@@ -53,12 +55,12 @@ TestCase {
}
function test_properties(data) {
- var control = createTemporaryObject(toolTip, testCase)
+ let control = createTemporaryObject(toolTip, testCase)
verify(control)
compare(control[data.property], data.defaultValue)
- var spy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: data.signalName})
+ let spy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: data.signalName})
verify(spy.valid)
control[data.property] = data.setValue
@@ -75,24 +77,24 @@ TestCase {
}
function test_attached(data) {
- var item1 = createTemporaryObject(mouseArea, testCase)
+ let item1 = createTemporaryObject(mouseArea, testCase)
verify(item1)
- var item2 = createTemporaryObject(mouseArea, testCase)
+ let item2 = createTemporaryObject(mouseArea, testCase)
verify(item2)
// Reset the properties to the expected default values, in case
// we're not the first test that uses attached properties to be run.
- var sharedTip = ToolTip.toolTip
+ let sharedTip = ToolTip.toolTip
sharedTip[data.property] = data.defaultValue
compare(item1.ToolTip[data.property], data.defaultValue)
compare(item2.ToolTip[data.property], data.defaultValue)
- var spy1 = signalSpy.createObject(item1, {target: item1.ToolTip, signalName: data.signalName})
+ let spy1 = signalSpy.createObject(item1, {target: item1.ToolTip, signalName: data.signalName})
verify(spy1.valid)
- var spy2 = signalSpy.createObject(item2, {target: item2.ToolTip, signalName: data.signalName})
+ let spy2 = signalSpy.createObject(item2, {target: item2.ToolTip, signalName: data.signalName})
verify(spy2.valid)
sharedSpy.signalName = data.signalName
@@ -138,7 +140,7 @@ TestCase {
}
function test_delay(data) {
- var control = createTemporaryObject(toolTip, testCase, {delay: data.delay})
+ let control = createTemporaryObject(toolTip, testCase, {delay: data.delay})
compare(control.visible, false)
if (data.imperative)
@@ -157,7 +159,7 @@ TestCase {
}
function test_timeout(data) {
- var control = createTemporaryObject(toolTip, testCase, {timeout: 100})
+ let control = createTemporaryObject(toolTip, testCase, {timeout: 100})
compare(control.visible, false)
if (data.imperative)
@@ -179,7 +181,6 @@ TestCase {
function test_warning() {
ignoreWarning(new RegExp(".*QML QtObject: ToolTip must be attached to an Item"))
- ignoreWarning(new RegExp(".*: QML ToolTip: cannot find any window to open popup in."))
object.ToolTip.show("") // don't crash (QTBUG-56243)
}
@@ -206,7 +207,7 @@ TestCase {
}
function test_makeVisibleWhileExitTransitionRunning(data) {
- var control = createTemporaryObject(toolTipWithExitTransition, testCase)
+ let control = createTemporaryObject(toolTipWithExitTransition, testCase)
// Show, hide, and show the tooltip again. Its exit transition should
// start and get cancelled, and then its enter transition should run.
@@ -259,18 +260,18 @@ TestCase {
skip("Mouse hovering not functional on offscreen/minimal platforms")
// Window shortcuts (the default context for Shortcut) require the window to have focus.
- var window = testCase.Window.window
+ let window = testCase.Window.window
verify(window)
window.requestActivate()
tryCompare(window, "active", true)
- var root = createTemporaryObject(buttonAndShortcutComponent, testCase)
+ let root = createTemporaryObject(buttonAndShortcutComponent, testCase)
verify(root)
mouseMove(root.button, root.button.width / 2, root.button.height / 2)
tryCompare(root.button.ToolTip.toolTip, "visible", true)
- var shortcutActivatedSpy = signalSpy.createObject(root, { target: root.shortcut, signalName: "activated" })
+ let shortcutActivatedSpy = signalSpy.createObject(root, { target: root.shortcut, signalName: "activated" })
verify(shortcutActivatedSpy.valid)
keyPress(Qt.Key_A)
compare(shortcutActivatedSpy.count, 1)
@@ -295,13 +296,13 @@ TestCase {
// QTBUG-63644
function test_hover() {
- var root = createTemporaryObject(hoverComponent, testCase)
+ let root = createTemporaryObject(hoverComponent, testCase)
verify(root)
- var tooltip = root.tooltip
+ let tooltip = root.tooltip
verify(tooltip)
- for (var pos = 0; pos <= 25; pos += 5) {
+ for (let pos = 0; pos <= 25; pos += 5) {
mouseMove(root, pos, pos)
verify(tooltip.visible)
}
@@ -313,7 +314,7 @@ TestCase {
}
function test_nonAttachedToolTipShowAndHide() {
- var tip = createTemporaryObject(nonAttachedToolTipComponent, testCase)
+ let tip = createTemporaryObject(nonAttachedToolTipComponent, testCase)
verify(tip)
tip.show("hello");
verify(tip.visible)
@@ -346,11 +347,11 @@ TestCase {
// QTBUG-74226
function test_attachedTimeout() {
- var row = createTemporaryObject(timeoutButtonRowComponent, testCase)
+ let row = createTemporaryObject(timeoutButtonRowComponent, testCase)
verify(row)
// Press the button that has no timeout; it should stay visible.
- var button2 = row.children[1]
+ let button2 = row.children[1]
mousePress(button2)
compare(button2.down, true)
tryCompare(button2.ToolTip.toolTip, "opened", true)
@@ -365,7 +366,7 @@ TestCase {
tryCompare(button2.ToolTip, "visible", false)
// Now, press the first button that does have a timeout; it should close on its own eventually.
- var button1 = row.children[0]
+ let button1 = row.children[0]
mousePress(button1)
compare(button1.down, true)
// We use a short timeout to speed up the test, but tryCompare(...opened, true) then
@@ -402,7 +403,7 @@ TestCase {
// QTBUG-62350
function test_wrap() {
- var item = createTemporaryObject(wrapComponent, testCase)
+ let item = createTemporaryObject(wrapComponent, testCase)
verify(item)
// Avoid "cannot find window to popup in" warning that can occur if it's made visible too early.
@@ -466,4 +467,20 @@ TestCase {
mouseRelease(longTextButton)
tryCompare(longTextButton.ToolTip.toolTip, "visible", false)
}
+
+ Component {
+ id: initiallyVisibleComponent
+
+ Item {
+ ToolTip.text: "Some text"
+ ToolTip.visible: true
+ }
+ }
+
+ // QTBUG-75483
+ function test_initiallyVisible() {
+ let item = createTemporaryObject(initiallyVisibleComponent, testCase)
+ verify(item)
+ verify(item.ToolTip.visible)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_tumbler.qml b/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
index bb098f92db..c5c567b7d2 100644
--- a/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_tumbler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
@@ -464,7 +464,10 @@ TestCase {
];
for (var i = 0; i < data.length; ++i) {
var row = data[i];
- row.tag = "delegate" + row.index + " offset=" + row.offset + " expectedDisplacement=" + row.expectedDisplacement;
+ row.tag = "count=" + row.count
+ + " delegate" + row.index
+ + " offset=" + row.offset
+ + " expectedDisplacement=" + row.expectedDisplacement;
}
return data;
}
@@ -1244,4 +1247,46 @@ TestCase {
tumbler = createTemporaryObject(initialCurrentIndexTumbler, testCase, {wrap: false});
compare(tumbler.currentIndex, 4);
}
+
+ // QTBUG-109995
+ Component {
+ id: flickTumbler
+ Flickable {
+ width: 50
+ height: 200
+ interactive: true
+ contentHeight: 400
+ property alias tumblerItem: noWrapTumbler
+ Tumbler {
+ id: noWrapTumbler
+ anchors.fill: parent
+ model: 20
+ wrap: false
+ }
+ }
+ }
+
+ function test_flick() {
+ let control = createTemporaryObject(flickTumbler, testCase)
+ verify(control)
+
+ let tumbler = control.tumblerItem
+ compare(tumbler.count, 20)
+ compare(tumbler.wrap, false)
+
+ let touch = touchEvent(tumbler)
+ let tumblerView = findView(tumbler)
+ let delegateHeight = tumblerView.children[0].children[0].height
+
+ // Move delegates through touch operation and check the current index
+ touch.press(0, tumblerView, control.width / 2, control.height / 2).commit()
+ // Move slowly, otherwise its considered as flick which cause current index
+ // to be varied according to its velocity
+ var scrollOffset = control.height / 2
+ for (; scrollOffset > delegateHeight / 2; scrollOffset-=5) {
+ touch.move(0, tumblerView, control.width / 2, scrollOffset).commit()
+ }
+ touch.release(0, tumblerView, control.width / 2, scrollOffset).commit()
+ tryCompare(tumblerView, "currentIndex", 2)
+ }
}
diff --git a/tests/auto/quickcontrols/controls/data/tst_weeknumbercolumn.qml b/tests/auto/quickcontrols/controls/data/tst_weeknumbercolumn.qml
index 40566b018c..11868bfbbf 100644
--- a/tests/auto/quickcontrols/controls/data/tst_weeknumbercolumn.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_weeknumbercolumn.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -18,15 +18,17 @@ TestCase {
WeekNumberColumn { }
}
- function test_defaults() {
+ function init() {
failOnWarning(/.?/)
+ }
+ function test_defaults() {
let control = createTemporaryObject(component, testCase)
verify(control)
}
function test_locale() {
- var control = component.createObject(testCase)
+ let control = component.createObject(testCase)
compare(control.contentItem.children.length, 6 + 1)
@@ -35,12 +37,12 @@ TestCase {
// en_US: [48...53]
control.locale = Qt.locale("en_US")
- for (var i = 0; i < 6; ++i)
+ for (let i = 0; i < 6; ++i)
compare(control.contentItem.children[i].text, (i + 48).toString())
// no_NO: [49...1]
control.locale = Qt.locale("no_NO")
- for (var j = 0; j < 5; ++j)
+ for (let j = 0; j < 5; ++j)
compare(control.contentItem.children[j].text, (j + 49).toString())
compare(control.contentItem.children[5].text, "1")
@@ -48,7 +50,7 @@ TestCase {
}
function test_range() {
- var control = component.createObject(testCase)
+ let control = component.createObject(testCase)
control.month = 0
compare(control.month, 0)
@@ -82,7 +84,7 @@ TestCase {
}
function test_font() {
- var control = component.createObject(testCase)
+ let control = component.createObject(testCase)
verify(control.contentItem.children[0])
diff --git a/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt b/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
index 5495efd858..672e0e18c3 100644
--- a/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/fusion/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_fusion LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/controls/fusion/tst_fusion.cpp b/tests/auto/quickcontrols/controls/fusion/tst_fusion.cpp
index b7aa9aa434..4485ca0c70 100644
--- a/tests/auto/quickcontrols/controls/fusion/tst_fusion.cpp
+++ b/tests/auto/quickcontrols/controls/fusion/tst_fusion.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
diff --git a/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt b/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
index 6efb1a0a45..6e95134e1b 100644
--- a/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/imagine/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_imagine LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/controls/imagine/tst_imagine.cpp b/tests/auto/quickcontrols/controls/imagine/tst_imagine.cpp
index 1a46d26c1c..54c363797b 100644
--- a/tests/auto/quickcontrols/controls/imagine/tst_imagine.cpp
+++ b/tests/auto/quickcontrols/controls/imagine/tst_imagine.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
diff --git a/tests/auto/quickcontrols/controls/ios/CMakeLists.txt b/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
index fe6839990f..d0f111e66f 100644
--- a/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/ios/CMakeLists.txt
@@ -4,7 +4,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_ios LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
# Collect test data
diff --git a/tests/auto/quickcontrols/controls/ios/tst_ios.cpp b/tests/auto/quickcontrols/controls/ios/tst_ios.cpp
index 954b964958..11c6f35b0b 100644
--- a/tests/auto/quickcontrols/controls/ios/tst_ios.cpp
+++ b/tests/auto/quickcontrols/controls/ios/tst_ios.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
diff --git a/tests/auto/quickcontrols/controls/macos/CMakeLists.txt b/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
index 04d186c63f..0d2e523a2a 100644
--- a/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/macos/CMakeLists.txt
@@ -4,7 +4,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_macos LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
# Collect test data
diff --git a/tests/auto/quickcontrols/controls/macos/tst_macos.cpp b/tests/auto/quickcontrols/controls/macos/tst_macos.cpp
index 59c8585f03..1ba0ebf587 100644
--- a/tests/auto/quickcontrols/controls/macos/tst_macos.cpp
+++ b/tests/auto/quickcontrols/controls/macos/tst_macos.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
diff --git a/tests/auto/quickcontrols/controls/material/CMakeLists.txt b/tests/auto/quickcontrols/controls/material/CMakeLists.txt
index 3e87bf1609..a528834c04 100644
--- a/tests/auto/quickcontrols/controls/material/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/material/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_material LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/controls/material/tst_material.cpp b/tests/auto/quickcontrols/controls/material/tst_material.cpp
index 1fa55ab4dc..782397a592 100644
--- a/tests/auto/quickcontrols/controls/material/tst_material.cpp
+++ b/tests/auto/quickcontrols/controls/material/tst_material.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
diff --git a/tests/auto/quickcontrols/controls/universal/CMakeLists.txt b/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
index f0c7d2ed80..9e779420e7 100644
--- a/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/universal/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_universal LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/controls/universal/tst_universal.cpp b/tests/auto/quickcontrols/controls/universal/tst_universal.cpp
index 87faccc5ff..2d9e687bea 100644
--- a/tests/auto/quickcontrols/controls/universal/tst_universal.cpp
+++ b/tests/auto/quickcontrols/controls/universal/tst_universal.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
diff --git a/tests/auto/quickcontrols/controls/windows/CMakeLists.txt b/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
index a5ba30a51b..e5f30d146b 100644
--- a/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
+++ b/tests/auto/quickcontrols/controls/windows/CMakeLists.txt
@@ -4,7 +4,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_windows LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
# Collect test data
diff --git a/tests/auto/quickcontrols/controls/windows/tst_windows.cpp b/tests/auto/quickcontrols/controls/windows/tst_windows.cpp
index 560cec4383..221ff116dd 100644
--- a/tests/auto/quickcontrols/controls/windows/tst_windows.cpp
+++ b/tests/auto/quickcontrols/controls/windows/tst_windows.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
diff --git a/tests/auto/quickcontrols/cursor/CMakeLists.txt b/tests/auto/quickcontrols/cursor/CMakeLists.txt
index 6d0fa9eed6..55ef2ebb5d 100644
--- a/tests/auto/quickcontrols/cursor/CMakeLists.txt
+++ b/tests/auto/quickcontrols/cursor/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_cursor LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/cursor/data/buttons.qml b/tests/auto/quickcontrols/cursor/data/buttons.qml
index b2a3cccc34..988233dfdd 100644
--- a/tests/auto/quickcontrols/cursor/data/buttons.qml
+++ b/tests/auto/quickcontrols/cursor/data/buttons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/cursor/data/containers.qml b/tests/auto/quickcontrols/cursor/data/containers.qml
index 420a1a592d..51533db7f1 100644
--- a/tests/auto/quickcontrols/cursor/data/containers.qml
+++ b/tests/auto/quickcontrols/cursor/data/containers.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/cursor/data/editable.qml b/tests/auto/quickcontrols/cursor/data/editable.qml
index 1750c481b9..fdf2e1ada2 100644
--- a/tests/auto/quickcontrols/cursor/data/editable.qml
+++ b/tests/auto/quickcontrols/cursor/data/editable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/cursor/data/pageindicator.qml b/tests/auto/quickcontrols/cursor/data/pageindicator.qml
index 3f9178a50d..a29d1531e3 100644
--- a/tests/auto/quickcontrols/cursor/data/pageindicator.qml
+++ b/tests/auto/quickcontrols/cursor/data/pageindicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/cursor/data/scrollbar.qml b/tests/auto/quickcontrols/cursor/data/scrollbar.qml
index 1269cc9c6b..9e308e16a5 100644
--- a/tests/auto/quickcontrols/cursor/data/scrollbar.qml
+++ b/tests/auto/quickcontrols/cursor/data/scrollbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/cursor/data/sliders.qml b/tests/auto/quickcontrols/cursor/data/sliders.qml
index ae2f8a962d..b27d8bd305 100644
--- a/tests/auto/quickcontrols/cursor/data/sliders.qml
+++ b/tests/auto/quickcontrols/cursor/data/sliders.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/cursor/tst_cursor.cpp b/tests/auto/quickcontrols/cursor/tst_cursor.cpp
index 02a25848dc..5f900e7ce0 100644
--- a/tests/auto/quickcontrols/cursor/tst_cursor.cpp
+++ b/tests/auto/quickcontrols/cursor/tst_cursor.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
diff --git a/tests/auto/quickcontrols/customization/CMakeLists.txt b/tests/auto/quickcontrols/customization/CMakeLists.txt
index 79dd408257..aa5106ee03 100644
--- a/tests/auto/quickcontrols/customization/CMakeLists.txt
+++ b/tests/auto/quickcontrols/customization/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_customization LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/AbstractButton.qml b/tests/auto/quickcontrols/customization/data/styles/empty/AbstractButton.qml
index 82e9e4bd4a..070ea02d2d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/AbstractButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/AbstractButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ApplicationWindow.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ApplicationWindow.qml
index 92067837d3..cde9da12bd 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ApplicationWindow.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ApplicationWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/BusyIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/empty/BusyIndicator.qml
index 2364737a9d..aa86dfd390 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/BusyIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/BusyIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Button.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Button.qml
index 7f14f150c1..78eca1d91e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Button.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/CheckBox.qml b/tests/auto/quickcontrols/customization/data/styles/empty/CheckBox.qml
index 29b63ab1d4..3a2848fd24 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/CheckBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/CheckBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/CheckDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/empty/CheckDelegate.qml
index 0760efad05..5d1947be84 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/CheckDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/CheckDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ComboBox.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ComboBox.qml
index 4bb5b6538e..6f24f84f11 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ComboBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ComboBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Container.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Container.qml
index bcf0a906ac..b12be91825 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Container.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Container.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Control.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Control.qml
index 0a1db09a82..c9cf94064c 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Control.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Control.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/DelayButton.qml b/tests/auto/quickcontrols/customization/data/styles/empty/DelayButton.qml
index 849a82f9f7..231cc42459 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/DelayButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/DelayButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Dial.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Dial.qml
index b4d062c47b..2880dc1f90 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Dial.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Dial.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Dialog.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Dialog.qml
index b43550817a..a93d862d72 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Dialog.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/DialogButtonBox.qml b/tests/auto/quickcontrols/customization/data/styles/empty/DialogButtonBox.qml
index 14c4982d8c..1d668f6d3a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/DialogButtonBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/DialogButtonBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Drawer.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Drawer.qml
index eee198aa10..2b9cca2123 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Drawer.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Drawer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Frame.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Frame.qml
index cb7dc659e8..6879f49c2d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Frame.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Frame.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/GroupBox.qml b/tests/auto/quickcontrols/customization/data/styles/empty/GroupBox.qml
index 42a26c114d..624ce1b472 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/GroupBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/GroupBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ItemDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ItemDelegate.qml
index ff7cc8dac8..c6dc9097c3 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ItemDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ItemDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Label.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Label.qml
index a70541c788..c0a53b1ef7 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Label.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Label.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Menu.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Menu.qml
index 7f03fd0193..66fca1d3a9 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Menu.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/MenuBar.qml b/tests/auto/quickcontrols/customization/data/styles/empty/MenuBar.qml
index 742492a467..ef60883189 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/MenuBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/MenuBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/MenuBarItem.qml b/tests/auto/quickcontrols/customization/data/styles/empty/MenuBarItem.qml
index b17f33bedc..6e39d5a323 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/MenuBarItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/MenuBarItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/MenuItem.qml b/tests/auto/quickcontrols/customization/data/styles/empty/MenuItem.qml
index c0639e3dc2..c8e5cfe1fc 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/MenuItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/MenuItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/MenuSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/empty/MenuSeparator.qml
index f470175d9e..3fa89b958a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/MenuSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/MenuSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Page.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Page.qml
index 39631703f7..76bbf857bf 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Page.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Page.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/PageIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/empty/PageIndicator.qml
index fa47a9e8e1..95d03279a5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/PageIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/PageIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Pane.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Pane.qml
index 090ca74ef0..08b6fb7b69 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Pane.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Pane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Popup.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Popup.qml
index 3fac8c15dd..766aee5c80 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Popup.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ProgressBar.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ProgressBar.qml
index 19701a3566..270fa946e8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ProgressBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ProgressBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/RadioButton.qml b/tests/auto/quickcontrols/customization/data/styles/empty/RadioButton.qml
index e39736a523..a51248e730 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/RadioButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/RadioButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/RadioDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/empty/RadioDelegate.qml
index 1f4aa70f04..b22ddc74e0 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/RadioDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/RadioDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/RangeSlider.qml b/tests/auto/quickcontrols/customization/data/styles/empty/RangeSlider.qml
index 26f410b4b7..69ca2ae440 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/RangeSlider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/RangeSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/RoundButton.qml b/tests/auto/quickcontrols/customization/data/styles/empty/RoundButton.qml
index adaaddc6e4..7415e98166 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/RoundButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/RoundButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ScrollBar.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ScrollBar.qml
index f10d4c9ce7..fafad4bb0f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ScrollBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ScrollBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ScrollIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ScrollIndicator.qml
index 211178f39a..68c20b6d98 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ScrollIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ScrollIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ScrollView.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ScrollView.qml
index b80618674b..5044d22853 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ScrollView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ScrollView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Slider.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Slider.qml
index 098b615ffa..c88530b5d5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Slider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/SpinBox.qml b/tests/auto/quickcontrols/customization/data/styles/empty/SpinBox.qml
index f7ef626944..c5a442111e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/SpinBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/SpinBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/StackView.qml b/tests/auto/quickcontrols/customization/data/styles/empty/StackView.qml
index 5dc2edc34c..afb9e95767 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/StackView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/StackView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/SwipeDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/empty/SwipeDelegate.qml
index f802533d97..5e2d2e6b86 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/SwipeDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/SwipeDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/SwipeView.qml b/tests/auto/quickcontrols/customization/data/styles/empty/SwipeView.qml
index b4b6d3be6c..729be7853c 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/SwipeView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/SwipeView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Switch.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Switch.qml
index ace8f83321..0b808a9099 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Switch.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Switch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/SwitchDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/empty/SwitchDelegate.qml
index c516e8b178..4b401bcaad 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/SwitchDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/SwitchDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/TabBar.qml b/tests/auto/quickcontrols/customization/data/styles/empty/TabBar.qml
index 3f71b3a0ec..26305ee022 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/TabBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/TabBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/TabButton.qml b/tests/auto/quickcontrols/customization/data/styles/empty/TabButton.qml
index 1bf2625589..3d9977c478 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/TabButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/TabButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/TextArea.qml b/tests/auto/quickcontrols/customization/data/styles/empty/TextArea.qml
index 01807d1606..e20a005b61 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/TextArea.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/TextArea.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/TextField.qml b/tests/auto/quickcontrols/customization/data/styles/empty/TextField.qml
index 6001e445a1..c816aeda65 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/TextField.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/TextField.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ToolBar.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ToolBar.qml
index b8dd897b6e..7beea9edb9 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ToolBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ToolBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ToolButton.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ToolButton.qml
index c6d9c4df5b..a15c893083 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ToolButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ToolButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ToolSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ToolSeparator.qml
index a9a26e562e..e9eb7e1dc8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ToolSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ToolSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/ToolTip.qml b/tests/auto/quickcontrols/customization/data/styles/empty/ToolTip.qml
index 73ccddb576..383b36c108 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/ToolTip.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/ToolTip.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/empty/Tumbler.qml b/tests/auto/quickcontrols/customization/data/styles/empty/Tumbler.qml
index 11c3b0b552..63c7e9e848 100644
--- a/tests/auto/quickcontrols/customization/data/styles/empty/Tumbler.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/empty/Tumbler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/AbstractButton.qml b/tests/auto/quickcontrols/customization/data/styles/identified/AbstractButton.qml
index 2db395fe7a..6f20decf6c 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/AbstractButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/AbstractButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ApplicationWindow.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ApplicationWindow.qml
index 846cc0fd00..f6c2d26599 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ApplicationWindow.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ApplicationWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/BusyIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/identified/BusyIndicator.qml
index 177685b980..33e67d3f03 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/BusyIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/BusyIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Button.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Button.qml
index 2fea6704dc..2142cfb0d4 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Button.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/CheckBox.qml b/tests/auto/quickcontrols/customization/data/styles/identified/CheckBox.qml
index 3813aeeb7a..3c6235230d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/CheckBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/CheckBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/CheckDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/identified/CheckDelegate.qml
index f78b3f7984..53b96f362f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/CheckDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/CheckDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ComboBox.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ComboBox.qml
index 0945e49bb4..154ea327b0 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ComboBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ComboBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Container.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Container.qml
index 941b9b7467..bdd55c9995 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Container.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Container.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Control.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Control.qml
index f2f3928c8a..9daceba2d8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Control.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Control.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/DelayButton.qml b/tests/auto/quickcontrols/customization/data/styles/identified/DelayButton.qml
index 18119a8aa6..fb94304c0b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/DelayButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/DelayButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Dial.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Dial.qml
index 84dea2aa70..d4f30b834d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Dial.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Dial.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Dialog.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Dialog.qml
index 4beace01e7..a77206778a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Dialog.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/DialogButtonBox.qml b/tests/auto/quickcontrols/customization/data/styles/identified/DialogButtonBox.qml
index 0cec0f5a6c..288eb06c5c 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/DialogButtonBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/DialogButtonBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Drawer.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Drawer.qml
index d8634410cd..4c6d6fad97 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Drawer.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Drawer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Frame.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Frame.qml
index 014b2a74b3..fe643bc3d5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Frame.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Frame.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/GroupBox.qml b/tests/auto/quickcontrols/customization/data/styles/identified/GroupBox.qml
index 767259242c..7b8332e24f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/GroupBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/GroupBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ItemDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ItemDelegate.qml
index c7d4002407..13b99ae0a8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ItemDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ItemDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Label.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Label.qml
index c9b00ff1ca..a611f58fa8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Label.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Label.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Menu.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Menu.qml
index 822824dca8..6d273fbfe3 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Menu.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/MenuBar.qml b/tests/auto/quickcontrols/customization/data/styles/identified/MenuBar.qml
index 94b5997165..ad8f72c7dd 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/MenuBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/MenuBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/MenuBarItem.qml b/tests/auto/quickcontrols/customization/data/styles/identified/MenuBarItem.qml
index da6c584ebc..16c4ecc8a5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/MenuBarItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/MenuBarItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/MenuItem.qml b/tests/auto/quickcontrols/customization/data/styles/identified/MenuItem.qml
index 4ba04908ad..7a199e5e67 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/MenuItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/MenuItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/MenuSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/identified/MenuSeparator.qml
index 6928eb01bc..b3f08a4b76 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/MenuSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/MenuSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Page.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Page.qml
index d63e4ce80d..7b7e45952e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Page.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Page.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/PageIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/identified/PageIndicator.qml
index c47cb69e9e..bcbe688e7a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/PageIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/PageIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Pane.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Pane.qml
index c5739700d3..01c08ecabd 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Pane.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Pane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Popup.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Popup.qml
index e71be2f3de..f944b58382 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Popup.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ProgressBar.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ProgressBar.qml
index d1cfd16419..c3e5734fe4 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ProgressBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ProgressBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/RadioButton.qml b/tests/auto/quickcontrols/customization/data/styles/identified/RadioButton.qml
index ce64bdee5a..8147129984 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/RadioButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/RadioButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/RadioDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/identified/RadioDelegate.qml
index eeba29faa7..3c5ae0e1c3 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/RadioDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/RadioDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/RangeSlider.qml b/tests/auto/quickcontrols/customization/data/styles/identified/RangeSlider.qml
index 1fae11c2fa..5c21f529da 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/RangeSlider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/RangeSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/RoundButton.qml b/tests/auto/quickcontrols/customization/data/styles/identified/RoundButton.qml
index b431a3a02c..910f788d02 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/RoundButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/RoundButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ScrollBar.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ScrollBar.qml
index 4bb5da5a18..8f225e378c 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ScrollBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ScrollBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ScrollIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ScrollIndicator.qml
index a62b36afa2..6e312c99cc 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ScrollIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ScrollIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ScrollView.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ScrollView.qml
index 94cb1bc1a5..2bc4d83305 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ScrollView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ScrollView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Slider.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Slider.qml
index 5298a1556e..806243e3ba 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Slider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/SpinBox.qml b/tests/auto/quickcontrols/customization/data/styles/identified/SpinBox.qml
index 2e285b0f47..345113ee2b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/SpinBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/SpinBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/StackView.qml b/tests/auto/quickcontrols/customization/data/styles/identified/StackView.qml
index 5c5cd31af4..d770b5f356 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/StackView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/StackView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/SwipeDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/identified/SwipeDelegate.qml
index 403b7d5a82..1cc00a4dfb 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/SwipeDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/SwipeDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/SwipeView.qml b/tests/auto/quickcontrols/customization/data/styles/identified/SwipeView.qml
index 8c8345a524..3525a0102a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/SwipeView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/SwipeView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Switch.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Switch.qml
index 9fc0c0f010..0bf7f07176 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Switch.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Switch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/SwitchDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/identified/SwitchDelegate.qml
index 3f7f9568fb..87f8fe72bf 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/SwitchDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/SwitchDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/TabBar.qml b/tests/auto/quickcontrols/customization/data/styles/identified/TabBar.qml
index 50e36498b7..dd02c30d38 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/TabBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/TabBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/TabButton.qml b/tests/auto/quickcontrols/customization/data/styles/identified/TabButton.qml
index 19145e14fd..f054365b04 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/TabButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/TabButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/TextArea.qml b/tests/auto/quickcontrols/customization/data/styles/identified/TextArea.qml
index a7d4e212f0..7029f69c65 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/TextArea.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/TextArea.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/TextField.qml b/tests/auto/quickcontrols/customization/data/styles/identified/TextField.qml
index 3d78c4b73c..06ffbd57f8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/TextField.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/TextField.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ToolBar.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ToolBar.qml
index b328828267..2fe8d79446 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ToolBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ToolBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ToolButton.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ToolButton.qml
index 465de9a6f1..28ce2d0309 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ToolButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ToolButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ToolSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ToolSeparator.qml
index 81c8f40178..e1ebb0164f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ToolSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ToolSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/ToolTip.qml b/tests/auto/quickcontrols/customization/data/styles/identified/ToolTip.qml
index 2d8533dd0a..dfd5d71dea 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/ToolTip.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/ToolTip.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/identified/Tumbler.qml b/tests/auto/quickcontrols/customization/data/styles/identified/Tumbler.qml
index 67d4cd9b64..0a60e52ba7 100644
--- a/tests/auto/quickcontrols/customization/data/styles/identified/Tumbler.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/identified/Tumbler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/AbstractButton.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/AbstractButton.qml
index b29c8fa15e..7f9faffc6f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/AbstractButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/AbstractButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ApplicationWindow.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ApplicationWindow.qml
index 437410b72d..d733a7d90d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ApplicationWindow.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ApplicationWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/BusyIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/BusyIndicator.qml
index 6c1ed812b7..bd33867410 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/BusyIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/BusyIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Button.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Button.qml
index d0bfc4c563..43aacf2f01 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Button.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/CheckBox.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/CheckBox.qml
index 34d01e4e70..fb11260f8d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/CheckBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/CheckBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/CheckDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/CheckDelegate.qml
index e17820ac4e..9eada2bc70 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/CheckDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/CheckDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ComboBox.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ComboBox.qml
index 611358a381..de49947e22 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ComboBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ComboBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Container.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Container.qml
index d6276f222e..d595d9ffc1 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Container.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Container.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Control.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Control.qml
index bb924ce15c..82b60c4f8a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Control.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Control.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/DelayButton.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/DelayButton.qml
index 60a1d67e62..39c866faa5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/DelayButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/DelayButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Dial.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Dial.qml
index a16ad247ac..a1568274b4 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Dial.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Dial.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Dialog.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Dialog.qml
index 2cd9bb9b20..5a6e94cd5c 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Dialog.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/DialogButtonBox.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/DialogButtonBox.qml
index 23eea3c0df..7e034fca9e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/DialogButtonBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/DialogButtonBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Drawer.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Drawer.qml
index 0fbc879fb2..aee8d27814 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Drawer.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Drawer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Frame.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Frame.qml
index c84280cb21..14a34ed78c 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Frame.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Frame.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/GroupBox.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/GroupBox.qml
index 518a836e92..07eb825747 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/GroupBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/GroupBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ItemDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ItemDelegate.qml
index 7150c79aa4..e6a99f7042 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ItemDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ItemDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Label.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Label.qml
index 662e22eac5..c1edb306a8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Label.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Label.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Menu.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Menu.qml
index 173c50f7e8..198c58a66a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Menu.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBar.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBar.qml
index 680caa9ada..6b7d869992 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBarItem.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBarItem.qml
index b3cdc391b9..7c0b048739 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBarItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuBarItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuItem.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuItem.qml
index 1f277bc466..a1fbe406c5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuSeparator.qml
index e9ad13b303..41b0fea578 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/MenuSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Page.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Page.qml
index 0ddf3f5b51..0f2222310f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Page.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Page.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/PageIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/PageIndicator.qml
index bc4c22f093..339ee26989 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/PageIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/PageIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Pane.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Pane.qml
index 816dcf5669..8e375a1c19 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Pane.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Pane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Popup.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Popup.qml
index 55dac84936..d6cf4057ae 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Popup.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ProgressBar.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ProgressBar.qml
index 9ce1cd99f3..ffc9de2d35 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ProgressBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ProgressBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/RadioButton.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/RadioButton.qml
index e76b465c69..729f27ad7a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/RadioButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/RadioButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/RadioDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/RadioDelegate.qml
index 4514f466c8..1c2fbf539a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/RadioDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/RadioDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/RangeSlider.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/RangeSlider.qml
index d747ce8bc2..24da75f6d5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/RangeSlider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/RangeSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/RoundButton.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/RoundButton.qml
index 5152ed430f..2280e1a0a9 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/RoundButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/RoundButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollBar.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollBar.qml
index 55e74e088b..19f19955e6 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollIndicator.qml
index 9bf0a176e1..87548c46a7 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollView.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollView.qml
index 1029e4ed20..8c8e460006 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ScrollView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Slider.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Slider.qml
index 52038ea8ed..643b586f2a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Slider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/SpinBox.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/SpinBox.qml
index 388e82e51b..487207d534 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/SpinBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/SpinBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/StackView.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/StackView.qml
index 2664fea8d7..2e6f395587 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/StackView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/StackView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeDelegate.qml
index 71df762fe8..71b6936ced 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeView.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeView.qml
index b0801f7a74..ba3ead1ce9 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/SwipeView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Switch.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Switch.qml
index f413f5b310..a93766a29d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Switch.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Switch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/SwitchDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/SwitchDelegate.qml
index 9342280478..5a2987b7cf 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/SwitchDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/SwitchDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/TabBar.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/TabBar.qml
index ec59e9eedc..9a003d1bed 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/TabBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/TabBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/TabButton.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/TabButton.qml
index 9a3eda7aa9..bdc2dfb02f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/TabButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/TabButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/TextArea.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/TextArea.qml
index fe03103e9f..c4ae8ece64 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/TextArea.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/TextArea.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/TextField.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/TextField.qml
index ff2e08e82e..4857153785 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/TextField.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/TextField.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolBar.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolBar.qml
index 73e8d45187..0792bd668f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolButton.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolButton.qml
index f45e98f851..76fc32bcad 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolSeparator.qml
index 840bd16711..eedac97c3e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolTip.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolTip.qml
index 9d6e77cb60..6cfb463c27 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolTip.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/ToolTip.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/incomplete/Tumbler.qml b/tests/auto/quickcontrols/customization/data/styles/incomplete/Tumbler.qml
index dba21f56d1..170aa2ffc3 100644
--- a/tests/auto/quickcontrols/customization/data/styles/incomplete/Tumbler.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/incomplete/Tumbler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/AbstractButton.qml b/tests/auto/quickcontrols/customization/data/styles/override/AbstractButton.qml
index ba1b1cb8fa..1a2baaa0ce 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/AbstractButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/AbstractButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ApplicationWindow.qml b/tests/auto/quickcontrols/customization/data/styles/override/ApplicationWindow.qml
index d2439bfb84..84df787316 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ApplicationWindow.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ApplicationWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/BusyIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/override/BusyIndicator.qml
index 827b45810a..bc2ad21833 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/BusyIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/BusyIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Button.qml b/tests/auto/quickcontrols/customization/data/styles/override/Button.qml
index a5bea3ebfe..367d074ba6 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Button.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/CheckBox.qml b/tests/auto/quickcontrols/customization/data/styles/override/CheckBox.qml
index aa7230cbd9..e60790b716 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/CheckBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/CheckBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/CheckDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/override/CheckDelegate.qml
index ea0b768bb4..cfcecd22e4 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/CheckDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/CheckDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ComboBox.qml b/tests/auto/quickcontrols/customization/data/styles/override/ComboBox.qml
index ba2467e0be..081975ab4b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ComboBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ComboBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Container.qml b/tests/auto/quickcontrols/customization/data/styles/override/Container.qml
index 68186cd2e0..8774f692f0 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Container.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Container.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Control.qml b/tests/auto/quickcontrols/customization/data/styles/override/Control.qml
index b9ca4c2643..a47f59306e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Control.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Control.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/DelayButton.qml b/tests/auto/quickcontrols/customization/data/styles/override/DelayButton.qml
index 68ff2684d3..e329c5c2de 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/DelayButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/DelayButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Dial.qml b/tests/auto/quickcontrols/customization/data/styles/override/Dial.qml
index e451b6b0c2..b8dc00e7ef 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Dial.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Dial.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Dialog.qml b/tests/auto/quickcontrols/customization/data/styles/override/Dialog.qml
index 1870887243..aa6daebb61 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Dialog.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/DialogButtonBox.qml b/tests/auto/quickcontrols/customization/data/styles/override/DialogButtonBox.qml
index 4e7ceb0ab6..f37b1f8f04 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/DialogButtonBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/DialogButtonBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Drawer.qml b/tests/auto/quickcontrols/customization/data/styles/override/Drawer.qml
index 0de7097601..63bae6f606 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Drawer.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Drawer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Frame.qml b/tests/auto/quickcontrols/customization/data/styles/override/Frame.qml
index 3e4effff0a..21b11a370b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Frame.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Frame.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/GroupBox.qml b/tests/auto/quickcontrols/customization/data/styles/override/GroupBox.qml
index a50e3a519b..db173c00ea 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/GroupBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/GroupBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ItemDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/override/ItemDelegate.qml
index 4e4f8d0fdd..c1d25096ed 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ItemDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ItemDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Label.qml b/tests/auto/quickcontrols/customization/data/styles/override/Label.qml
index 22ea5aab7c..e7d03faf00 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Label.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Label.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Menu.qml b/tests/auto/quickcontrols/customization/data/styles/override/Menu.qml
index 88d8510675..e040f7e0c9 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Menu.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/MenuBar.qml b/tests/auto/quickcontrols/customization/data/styles/override/MenuBar.qml
index 90aa93ca19..9f33e0f4e3 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/MenuBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/MenuBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/MenuBarItem.qml b/tests/auto/quickcontrols/customization/data/styles/override/MenuBarItem.qml
index 18db0060a1..39fbf4b991 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/MenuBarItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/MenuBarItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/MenuItem.qml b/tests/auto/quickcontrols/customization/data/styles/override/MenuItem.qml
index 43acebc514..96aab5f75e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/MenuItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/MenuItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/MenuSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/override/MenuSeparator.qml
index 703e381698..a5989b9506 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/MenuSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/MenuSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Page.qml b/tests/auto/quickcontrols/customization/data/styles/override/Page.qml
index 2fa1ad4d44..22cc330f47 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Page.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Page.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/PageIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/override/PageIndicator.qml
index 4e4cef6d90..3e5342725e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/PageIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/PageIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Pane.qml b/tests/auto/quickcontrols/customization/data/styles/override/Pane.qml
index 8a281da2b0..d476c5d60b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Pane.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Pane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Popup.qml b/tests/auto/quickcontrols/customization/data/styles/override/Popup.qml
index 6dfa3b20e4..98b157e010 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Popup.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ProgressBar.qml b/tests/auto/quickcontrols/customization/data/styles/override/ProgressBar.qml
index e111790fe3..4477c659da 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ProgressBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ProgressBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/RadioButton.qml b/tests/auto/quickcontrols/customization/data/styles/override/RadioButton.qml
index b7a3a0a10d..751104590d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/RadioButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/RadioButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/RadioDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/override/RadioDelegate.qml
index 83c0d73551..8a093c9870 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/RadioDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/RadioDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/RangeSlider.qml b/tests/auto/quickcontrols/customization/data/styles/override/RangeSlider.qml
index 3025960649..a4286f6b95 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/RangeSlider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/RangeSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/RoundButton.qml b/tests/auto/quickcontrols/customization/data/styles/override/RoundButton.qml
index 3949749ed2..77ce760dff 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/RoundButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/RoundButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ScrollBar.qml b/tests/auto/quickcontrols/customization/data/styles/override/ScrollBar.qml
index a546bf2eec..ebf1078181 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ScrollBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ScrollBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ScrollIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/override/ScrollIndicator.qml
index f514210c1a..876425cb1f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ScrollIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ScrollIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ScrollView.qml b/tests/auto/quickcontrols/customization/data/styles/override/ScrollView.qml
index ae6da7860b..6b2aea3c1b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ScrollView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ScrollView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Slider.qml b/tests/auto/quickcontrols/customization/data/styles/override/Slider.qml
index 5ced07590a..19e11901e3 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Slider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/SpinBox.qml b/tests/auto/quickcontrols/customization/data/styles/override/SpinBox.qml
index 66c13d3cf8..34fa33089e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/SpinBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/SpinBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/StackView.qml b/tests/auto/quickcontrols/customization/data/styles/override/StackView.qml
index 8759551ae1..e9ec4fda2f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/StackView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/StackView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/SwipeDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/override/SwipeDelegate.qml
index ae994c612c..fc669d48df 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/SwipeDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/SwipeDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/SwipeView.qml b/tests/auto/quickcontrols/customization/data/styles/override/SwipeView.qml
index d0b584bfd4..a6517cf521 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/SwipeView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/SwipeView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Switch.qml b/tests/auto/quickcontrols/customization/data/styles/override/Switch.qml
index 83e5f2b56b..cf564ac8d5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Switch.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Switch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/SwitchDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/override/SwitchDelegate.qml
index 5e75fdbd64..6e297aafe2 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/SwitchDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/SwitchDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/TabBar.qml b/tests/auto/quickcontrols/customization/data/styles/override/TabBar.qml
index 0437d3b5af..22bb535e05 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/TabBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/TabBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/TabButton.qml b/tests/auto/quickcontrols/customization/data/styles/override/TabButton.qml
index ab78b3246f..cdbdfb2a65 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/TabButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/TabButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/TextArea.qml b/tests/auto/quickcontrols/customization/data/styles/override/TextArea.qml
index 17d1883624..b986c8358d 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/TextArea.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/TextArea.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/TextField.qml b/tests/auto/quickcontrols/customization/data/styles/override/TextField.qml
index 22c21fd751..e4c11beb7b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/TextField.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/TextField.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ToolBar.qml b/tests/auto/quickcontrols/customization/data/styles/override/ToolBar.qml
index 957f3f7b5b..fd169b57ba 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ToolBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ToolBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ToolButton.qml b/tests/auto/quickcontrols/customization/data/styles/override/ToolButton.qml
index 19999c8a4a..cc6e3e916f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ToolButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ToolButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ToolSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/override/ToolSeparator.qml
index b88fba1bab..683fdf72f5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ToolSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ToolSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/ToolTip.qml b/tests/auto/quickcontrols/customization/data/styles/override/ToolTip.qml
index b4922d05eb..720328ed14 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/ToolTip.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/ToolTip.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/override/Tumbler.qml b/tests/auto/quickcontrols/customization/data/styles/override/Tumbler.qml
index ac49cd7675..e972865093 100644
--- a/tests/auto/quickcontrols/customization/data/styles/override/Tumbler.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/override/Tumbler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "../simple" as Simple
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/AbstractButton.qml b/tests/auto/quickcontrols/customization/data/styles/simple/AbstractButton.qml
index f0d9935706..b26f291ef0 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/AbstractButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/AbstractButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ApplicationWindow.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ApplicationWindow.qml
index 8f3f02a05a..1e529d0631 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ApplicationWindow.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ApplicationWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/BusyIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/simple/BusyIndicator.qml
index d5520d2ba6..5fef119a82 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/BusyIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/BusyIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Button.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Button.qml
index b71c5e3af9..8ee7716b77 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Button.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/CheckBox.qml b/tests/auto/quickcontrols/customization/data/styles/simple/CheckBox.qml
index aef812111a..ff507b930a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/CheckBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/CheckBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/CheckDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/simple/CheckDelegate.qml
index 06d5ac9cb0..b61e305c84 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/CheckDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/CheckDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ComboBox.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ComboBox.qml
index c5519f1c2a..2d8ceb8503 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ComboBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ComboBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Container.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Container.qml
index 85c59b4053..b8953a5944 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Container.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Container.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Control.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Control.qml
index 7e4808101b..3fc90a7d64 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Control.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Control.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/DelayButton.qml b/tests/auto/quickcontrols/customization/data/styles/simple/DelayButton.qml
index db6dd16514..0bba61fd3b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/DelayButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/DelayButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Dial.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Dial.qml
index 6365f50387..d75c267fa9 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Dial.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Dial.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Dialog.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Dialog.qml
index b17a593b84..d847bed730 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Dialog.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/DialogButtonBox.qml b/tests/auto/quickcontrols/customization/data/styles/simple/DialogButtonBox.qml
index b994d6a8b6..5e62f4bcba 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/DialogButtonBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/DialogButtonBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Drawer.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Drawer.qml
index 3b6257b4f2..cd7839812e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Drawer.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Drawer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Frame.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Frame.qml
index e1730dfbe6..229c2d7a35 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Frame.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Frame.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/GroupBox.qml b/tests/auto/quickcontrols/customization/data/styles/simple/GroupBox.qml
index 9aaad3a0c1..53b9cb9857 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/GroupBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/GroupBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ItemDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ItemDelegate.qml
index 0f5c22addd..cb157f6e4f 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ItemDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ItemDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Label.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Label.qml
index f128725923..10e801f909 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Label.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Label.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Menu.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Menu.qml
index f494d37711..90129b88f8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Menu.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/MenuBar.qml b/tests/auto/quickcontrols/customization/data/styles/simple/MenuBar.qml
index ee6aecefe6..91f129471a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/MenuBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/MenuBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/MenuBarItem.qml b/tests/auto/quickcontrols/customization/data/styles/simple/MenuBarItem.qml
index f0271ba20f..101aecf079 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/MenuBarItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/MenuBarItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/MenuItem.qml b/tests/auto/quickcontrols/customization/data/styles/simple/MenuItem.qml
index 7dee9ce2f3..991c2b0de7 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/MenuItem.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/MenuItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/MenuSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/simple/MenuSeparator.qml
index bbb90b6f62..a620732015 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/MenuSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/MenuSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Page.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Page.qml
index 4dc17112e9..ee06dcca31 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Page.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Page.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/PageIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/simple/PageIndicator.qml
index 59ce9282dc..31b6f99581 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/PageIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/PageIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Pane.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Pane.qml
index 0873baa770..76a852aff8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Pane.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Pane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Popup.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Popup.qml
index ebdb88f8a2..ed6c99da64 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Popup.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ProgressBar.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ProgressBar.qml
index c6cd7bb7d6..a47e1b7ec2 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ProgressBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ProgressBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/RadioButton.qml b/tests/auto/quickcontrols/customization/data/styles/simple/RadioButton.qml
index a80230c455..dd0f335bac 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/RadioButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/RadioButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/RadioDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/simple/RadioDelegate.qml
index cdded1165c..db61d8302a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/RadioDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/RadioDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/RangeSlider.qml b/tests/auto/quickcontrols/customization/data/styles/simple/RangeSlider.qml
index 0227f4d296..0da700d6f6 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/RangeSlider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/RangeSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/RoundButton.qml b/tests/auto/quickcontrols/customization/data/styles/simple/RoundButton.qml
index 0a9e5e13d1..faa3211241 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/RoundButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/RoundButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ScrollBar.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ScrollBar.qml
index a5cd90089c..304eff76a4 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ScrollBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ScrollBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ScrollIndicator.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ScrollIndicator.qml
index ce1d6a919c..00fd335883 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ScrollIndicator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ScrollIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ScrollView.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ScrollView.qml
index 765bb87f4a..7dafc4dfaf 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ScrollView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ScrollView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Slider.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Slider.qml
index b91e93941f..8695be1ae8 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Slider.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/SpinBox.qml b/tests/auto/quickcontrols/customization/data/styles/simple/SpinBox.qml
index 6c4cee6298..87ad0df995 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/SpinBox.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/SpinBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/StackView.qml b/tests/auto/quickcontrols/customization/data/styles/simple/StackView.qml
index 35b668df49..0e314d4583 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/StackView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/StackView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/SwipeDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/simple/SwipeDelegate.qml
index 95ddab00cc..1edf89377a 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/SwipeDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/SwipeDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/SwipeView.qml b/tests/auto/quickcontrols/customization/data/styles/simple/SwipeView.qml
index 73ecba8586..4c0666cd85 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/SwipeView.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/SwipeView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Switch.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Switch.qml
index c8e42d3ada..0b9a9518d9 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Switch.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Switch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/SwitchDelegate.qml b/tests/auto/quickcontrols/customization/data/styles/simple/SwitchDelegate.qml
index 00831df6c3..af563dfd54 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/SwitchDelegate.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/SwitchDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/TabBar.qml b/tests/auto/quickcontrols/customization/data/styles/simple/TabBar.qml
index 282a11aa52..ff5cdd63a1 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/TabBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/TabBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/TabButton.qml b/tests/auto/quickcontrols/customization/data/styles/simple/TabButton.qml
index 83de47fe54..2e5c464a3b 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/TabButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/TabButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/TextArea.qml b/tests/auto/quickcontrols/customization/data/styles/simple/TextArea.qml
index 6125d255be..3ff5ee56bd 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/TextArea.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/TextArea.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/TextField.qml b/tests/auto/quickcontrols/customization/data/styles/simple/TextField.qml
index a49bbed804..82981b53d2 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/TextField.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/TextField.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ToolBar.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ToolBar.qml
index 0e7c697722..be796e25f5 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ToolBar.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ToolBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ToolButton.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ToolButton.qml
index e91a0d9335..74e65ba03e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ToolButton.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ToolButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ToolSeparator.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ToolSeparator.qml
index c766f50754..979618f392 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ToolSeparator.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ToolSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/ToolTip.qml b/tests/auto/quickcontrols/customization/data/styles/simple/ToolTip.qml
index 58bf428f72..7a9b3e708e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/ToolTip.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/ToolTip.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/data/styles/simple/Tumbler.qml b/tests/auto/quickcontrols/customization/data/styles/simple/Tumbler.qml
index 3e7a2f486b..1b1755d63e 100644
--- a/tests/auto/quickcontrols/customization/data/styles/simple/Tumbler.qml
+++ b/tests/auto/quickcontrols/customization/data/styles/simple/Tumbler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/customization/tst_customization.cpp b/tests/auto/quickcontrols/customization/tst_customization.cpp
index 8e5ac2a95a..d011f24d6a 100644
--- a/tests/auto/quickcontrols/customization/tst_customization.cpp
+++ b/tests/auto/quickcontrols/customization/tst_customization.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtCore/private/qhooks_p.h>
@@ -365,18 +365,18 @@ void tst_customization::override_data()
{
"macOS",
{
- "Button", "CheckBox", "ComboBox", "Dial", "Dialog", "DialogButtonBox", "Frame", "GroupBox",
- "ProgressBar", "RadioButton", "SelectionRectangle", "Slider",
- "SpinBox", "TextArea", "TextField", "TreeViewDelegate"
+ "Button", "CheckBox", "CheckDelegate", "ComboBox", "DelayButton", "Dial", "Frame",
+ "GroupBox", "ProgressBar", "RadioButton", "RadioDelegate", "SelectionRectangle",
+ "RangeSlider", "Slider", "SpinBox", "TextArea", "TextField", "TreeViewDelegate"
// TODO: ScrollView, ScrollBar
}
},
{
"Windows",
{
- "Button", "CheckBox", "ComboBox", "Frame", "GroupBox", "ProgressBar", "RadioButton",
- "SelectionRectangle", "Slider", "SpinBox", "TextArea", "TextField"
- // TODO: ScrollView
+ "Button", "CheckBox", "CheckDelegate", "ComboBox", "DelayButton", "Frame", "GroupBox",
+ "ProgressBar", "RadioButton", "RadioDelegate", "RangeSlider", "SelectionRectangle",
+ "ScrollBar", "Slider", "SpinBox", "Switch", "SwitchDelegate", "TextArea", "TextField"
}
}
};
diff --git a/tests/auto/quickcontrols/deferred/CMakeLists.txt b/tests/auto/quickcontrols/deferred/CMakeLists.txt
index 58ae1653e6..07df0b6d3d 100644
--- a/tests/auto/quickcontrols/deferred/CMakeLists.txt
+++ b/tests/auto/quickcontrols/deferred/CMakeLists.txt
@@ -1,3 +1,9 @@
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickdeferred LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
data/*)
diff --git a/tests/auto/quickcontrols/deferred/data/abortedIncubation.qml b/tests/auto/quickcontrols/deferred/data/abortedIncubation.qml
new file mode 100644
index 0000000000..6cd4c1c04a
--- /dev/null
+++ b/tests/auto/quickcontrols/deferred/data/abortedIncubation.qml
@@ -0,0 +1,171 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+import QtQuick.Controls.Basic
+
+ApplicationWindow {
+ id: appWindow
+
+ // QQuickDeferredPointer<QQuickItem> background;
+ background: Rectangle {
+ id: backgroundRect
+ color: "black"
+ }
+
+ // internal property handle
+ ColorDialog {
+ id: colorDialog
+ options: ColorDialog.DontUseNativeDialog
+ }
+
+ // internal property upButton
+ // internal property textField
+ FolderDialog {
+ id: folderDialog
+ options: FolderDialog.DontUseNativeDialog
+ }
+
+ // indicator property of AbstractButton
+ Button {
+ id: basicButton
+ indicator: Rectangle {
+ id: basicButtonIndicator
+ color: "pink"
+ }
+ }
+
+ Dial {
+ id: dial
+ // QQuickDeferredPointer<QQuickItem> handle;
+ handle: Item {
+ Rectangle {
+ id: dialRect
+ }
+ }
+ }
+
+ GroupBox {
+ id: groupBox
+ // QQuickDeferredPointer<QQuickItem> label;
+ label: Label {
+ id: groupBoxLabel
+ text: "yo"
+ }
+ }
+
+ Label {
+ id: label
+ text: "yo2"
+ background: Rectangle {
+ id: labelBackground
+ color: "green"
+ }
+ }
+
+ Menu {
+ id: menu
+
+ MenuItem {
+ id: menuItem
+ text: "New..."
+ arrow: Rectangle {
+ id: menuItemArrow
+ color: "pink"
+ }
+ }
+ }
+
+ ScrollBar {
+ id: scrollbar
+ }
+
+ SpinBox {
+ id: spinBox
+
+ up.indicator: Rectangle {
+ id: spinBoxUpIndicator
+ color: "pink"
+ }
+ down.indicator: Rectangle {
+ id: spinBoxDownIndicator
+ color: "blue"
+ }
+ }
+
+ Control {
+ id: genericControl
+ background: Rectangle {
+ id: genericControlBackground
+ color: "red"
+ }
+ contentItem: Canvas {
+ id: genericControlContentItem
+ }
+ }
+
+ ComboBox {
+ id: comboBox
+ model: ["foo", "bar"]
+
+ popup: Popup {
+ id: comboBoxPopup
+ contentItem: ListView {
+ ScrollBar.vertical: ScrollBar {}
+ }
+ }
+ indicator: Item {
+ id: emptyPopupItem
+ }
+ }
+
+ Slider {
+ id: slider
+ // QQuickDeferredPointer<QQuickItem> handle;
+ handle: Item {
+ Rectangle {
+ id: rect
+ }
+ }
+ }
+
+ RangeSlider {
+ id: rangeSlider
+ first.handle: Item {
+ Rectangle {
+ id: rangeSliderRect1
+ }
+ }
+ second.handle: Item {
+ Rectangle {
+ id: rangeSliderRect2
+ }
+ }
+ }
+
+ TextArea {
+ id: textArea
+ background: Rectangle {
+ id: textAreaBackground
+ color: "pink"
+ }
+ }
+
+ TextField {
+ id: textField
+ background: Rectangle {
+ id: textFieldBackground
+ color: "pink"
+ }
+ }
+
+ TabBar {
+ id: bar
+ TabButton {
+ text: qsTr("One")
+ }
+ TabButton {
+ text: qsTr("Two")
+ }
+ }
+}
+
diff --git a/tests/auto/quickcontrols/deferred/tst_qquickdeferred.cpp b/tests/auto/quickcontrols/deferred/tst_qquickdeferred.cpp
index 4d3eadbea4..1c50531d57 100644
--- a/tests/auto/quickcontrols/deferred/tst_qquickdeferred.cpp
+++ b/tests/auto/quickcontrols/deferred/tst_qquickdeferred.cpp
@@ -1,11 +1,12 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtTest/qtest.h>
#include <QQmlEngine>
#include <QtQuick/qquickitem.h>
#include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h>
+#include <QQmlIncubator>
class DeferredPropertyTester : public QObject
{
@@ -48,6 +49,7 @@ public:
tst_qquickdeferred() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
private slots:
void noSpuriousBinding();
+ void abortedIncubation();
};
@@ -63,6 +65,25 @@ void tst_qquickdeferred::noSpuriousBinding() {
root->setProperty("toggle", false);
}
+// QTBUG-116828
+// This test checks the case where we cancel incubation of a componet with a deferred property
+// Components that have deferred properties should also provide an itemDestoryed method that
+// that resets the deferred property to null to prevent issues with dangling pointers.
+void tst_qquickdeferred::abortedIncubation()
+{
+ QQmlEngine engine;
+ QQmlIncubationController controller;
+ engine.setIncubationController(&controller);
+
+ {
+ QQmlIncubator incubator;
+ QQmlComponent componet(&engine, testFileUrl("abortedIncubation.qml"));
+ componet.create(incubator);
+ controller.incubateFor(1);
+ incubator.clear(); // abort incubation (and dont crash)
+ }
+}
+
QTEST_MAIN(tst_qquickdeferred)
#include "tst_qquickdeferred.moc"
diff --git a/tests/auto/quickcontrols/designer/CMakeLists.txt b/tests/auto/quickcontrols/designer/CMakeLists.txt
index 9ad0512f80..5157ad562a 100644
--- a/tests/auto/quickcontrols/designer/CMakeLists.txt
+++ b/tests/auto/quickcontrols/designer/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_designer LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/designer/tst_designer.cpp b/tests/auto/quickcontrols/designer/tst_designer.cpp
index 760f739a02..5b3c7690e2 100644
--- a/tests/auto/quickcontrols/designer/tst_designer.cpp
+++ b/tests/auto/quickcontrols/designer/tst_designer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtQuick>
diff --git a/tests/auto/quickcontrols/focus/CMakeLists.txt b/tests/auto/quickcontrols/focus/CMakeLists.txt
index 87200bf05c..ca22f82924 100644
--- a/tests/auto/quickcontrols/focus/CMakeLists.txt
+++ b/tests/auto/quickcontrols/focus/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_focus LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/focus/data/activeFocusOnTab.qml b/tests/auto/quickcontrols/focus/data/activeFocusOnTab.qml
index b158c673f0..856d026750 100644
--- a/tests/auto/quickcontrols/focus/data/activeFocusOnTab.qml
+++ b/tests/auto/quickcontrols/focus/data/activeFocusOnTab.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/focus/data/keyNavigation.qml b/tests/auto/quickcontrols/focus/data/keyNavigation.qml
index f1a10a2569..d617b78899 100644
--- a/tests/auto/quickcontrols/focus/data/keyNavigation.qml
+++ b/tests/auto/quickcontrols/focus/data/keyNavigation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/focus/data/visualFocus.qml b/tests/auto/quickcontrols/focus/data/visualFocus.qml
index ec35905e27..64c4ea65bc 100644
--- a/tests/auto/quickcontrols/focus/data/visualFocus.qml
+++ b/tests/auto/quickcontrols/focus/data/visualFocus.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/focus/tst_focus.cpp b/tests/auto/quickcontrols/focus/tst_focus.cpp
index ca70146885..5d745813dc 100644
--- a/tests/auto/quickcontrols/focus/tst_focus.cpp
+++ b/tests/auto/quickcontrols/focus/tst_focus.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QSignalSpy>
@@ -198,6 +198,7 @@ void tst_focus::policy()
control->setFocus(false);
QVERIFY(!control->hasActiveFocus());
+#if QT_CONFIG(wheelevent)
// Qt::WheelFocus
QWheelEvent wheelEvent(QPointF(control->width() / 2, control->height() / 2), QPointF(),
QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
@@ -212,6 +213,7 @@ void tst_focus::policy()
QGuiApplication::sendEvent(control, &wheelEvent);
QVERIFY(control->hasActiveFocus());
QVERIFY(!control->hasVisualFocus());
+#endif
}
void tst_focus::reason()
@@ -396,6 +398,7 @@ void tst_focus::reason()
customItem->setFocusReason(Qt::NoFocusReason);
customText->setFocusReason(Qt::NoFocusReason);
+#if QT_CONFIG(wheelevent)
// Wheel focus -> MouseFocusReason
QWheelEvent wheelEvent(QPointF(customItem->width() / 2, customItem->height() / 2), QPointF(),
QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
@@ -409,6 +412,7 @@ void tst_focus::reason()
QTRY_VERIFY(!customItem->hasActiveFocus());
QCOMPARE(customItem->focusReason(), Qt::PopupFocusReason);
QTest::keyClick(&view, Qt::Key_Escape); // close the popup
+#endif
}
void tst_focus::visualFocus()
@@ -502,6 +506,7 @@ void tst_focus::scope()
QVERIFY(child->hasActiveFocus());
QVERIFY(control->hasActiveFocus());
+#if QT_CONFIG(wheelevent)
// Qt::WheelFocus
QWheelEvent wheelEvent(QPointF(control->width() / 2, control->height() / 2), QPointF(),
QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
@@ -509,6 +514,7 @@ void tst_focus::scope()
QGuiApplication::sendEvent(control, &wheelEvent);
QVERIFY(!child->hasActiveFocus());
QVERIFY(control->hasActiveFocus());
+#endif
}
QTEST_MAIN(tst_focus)
diff --git a/tests/auto/quickcontrols/font/CMakeLists.txt b/tests/auto/quickcontrols/font/CMakeLists.txt
index 9eaee341d0..150d77a358 100644
--- a/tests/auto/quickcontrols/font/CMakeLists.txt
+++ b/tests/auto/quickcontrols/font/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_font LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/font/data/font-appwindow-custom.qml b/tests/auto/quickcontrols/font/data/font-appwindow-custom.qml
index fa621ae6a0..7cd3e4d978 100644
--- a/tests/auto/quickcontrols/font/data/font-appwindow-custom.qml
+++ b/tests/auto/quickcontrols/font/data/font-appwindow-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/font-appwindow-default.qml b/tests/auto/quickcontrols/font/data/font-appwindow-default.qml
index f9c767b876..62809ea03b 100644
--- a/tests/auto/quickcontrols/font/data/font-appwindow-default.qml
+++ b/tests/auto/quickcontrols/font/data/font-appwindow-default.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/font-control-custom.qml b/tests/auto/quickcontrols/font/data/font-control-custom.qml
index c7ee583d04..8b8b138939 100644
--- a/tests/auto/quickcontrols/font/data/font-control-custom.qml
+++ b/tests/auto/quickcontrols/font/data/font-control-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/font-control-default.qml b/tests/auto/quickcontrols/font/data/font-control-default.qml
index 7cd58e12b3..48320fd3a8 100644
--- a/tests/auto/quickcontrols/font/data/font-control-default.qml
+++ b/tests/auto/quickcontrols/font/data/font-control-default.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/font-popup-custom.qml b/tests/auto/quickcontrols/font/data/font-popup-custom.qml
index fcf942e3a5..dcf1010892 100644
--- a/tests/auto/quickcontrols/font/data/font-popup-custom.qml
+++ b/tests/auto/quickcontrols/font/data/font-popup-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/font-popup-default.qml b/tests/auto/quickcontrols/font/data/font-popup-default.qml
index b9db73c9fb..a6bc8d11db 100644
--- a/tests/auto/quickcontrols/font/data/font-popup-default.qml
+++ b/tests/auto/quickcontrols/font/data/font-popup-default.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/inheritance-childcontrol.qml b/tests/auto/quickcontrols/font/data/inheritance-childcontrol.qml
index f476020517..6b625ca1ca 100644
--- a/tests/auto/quickcontrols/font/data/inheritance-childcontrol.qml
+++ b/tests/auto/quickcontrols/font/data/inheritance-childcontrol.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/inheritance-childpopup.qml b/tests/auto/quickcontrols/font/data/inheritance-childpopup.qml
index 8b7a033bd7..d668c626ac 100644
--- a/tests/auto/quickcontrols/font/data/inheritance-childpopup.qml
+++ b/tests/auto/quickcontrols/font/data/inheritance-childpopup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/inheritance-control.qml b/tests/auto/quickcontrols/font/data/inheritance-control.qml
index 2cd5c125f4..fc67bd8735 100644
--- a/tests/auto/quickcontrols/font/data/inheritance-control.qml
+++ b/tests/auto/quickcontrols/font/data/inheritance-control.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/inheritance-dynamicchildcontrol.qml b/tests/auto/quickcontrols/font/data/inheritance-dynamicchildcontrol.qml
index 18305058bc..c4436bdd91 100644
--- a/tests/auto/quickcontrols/font/data/inheritance-dynamicchildcontrol.qml
+++ b/tests/auto/quickcontrols/font/data/inheritance-dynamicchildcontrol.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/inheritance-dynamicchildpopup.qml b/tests/auto/quickcontrols/font/data/inheritance-dynamicchildpopup.qml
index 6b16f1a46b..30dac26408 100644
--- a/tests/auto/quickcontrols/font/data/inheritance-dynamicchildpopup.qml
+++ b/tests/auto/quickcontrols/font/data/inheritance-dynamicchildpopup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/inheritance-dynamiccontrol.qml b/tests/auto/quickcontrols/font/data/inheritance-dynamiccontrol.qml
index df0e13bf2c..bf8cfdc3c8 100644
--- a/tests/auto/quickcontrols/font/data/inheritance-dynamiccontrol.qml
+++ b/tests/auto/quickcontrols/font/data/inheritance-dynamiccontrol.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/inheritance-dynamicpopup.qml b/tests/auto/quickcontrols/font/data/inheritance-dynamicpopup.qml
index 76b7d7cbda..b0454dc38c 100644
--- a/tests/auto/quickcontrols/font/data/inheritance-dynamicpopup.qml
+++ b/tests/auto/quickcontrols/font/data/inheritance-dynamicpopup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/inheritance-popup.qml b/tests/auto/quickcontrols/font/data/inheritance-popup.qml
index a5010f0f11..98698304a7 100644
--- a/tests/auto/quickcontrols/font/data/inheritance-popup.qml
+++ b/tests/auto/quickcontrols/font/data/inheritance-popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/listview.qml b/tests/auto/quickcontrols/font/data/listview.qml
index aa07a337cf..1b59cb16a2 100644
--- a/tests/auto/quickcontrols/font/data/listview.qml
+++ b/tests/auto/quickcontrols/font/data/listview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/data/resolve.qml b/tests/auto/quickcontrols/font/data/resolve.qml
index 00e5c767bb..4e82309dea 100644
--- a/tests/auto/quickcontrols/font/data/resolve.qml
+++ b/tests/auto/quickcontrols/font/data/resolve.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 yeshanshan <yeshanshan@uniontech.com>.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/font/tst_font.cpp b/tests/auto/quickcontrols/font/tst_font.cpp
index 26caf0c476..bdb73bbf4b 100644
--- a/tests/auto/quickcontrols/font/tst_font.cpp
+++ b/tests/auto/quickcontrols/font/tst_font.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
@@ -38,6 +38,9 @@ private slots:
void listView();
void resolve();
+
+ void variableAxes_data();
+ void variableAxes();
};
static QFont testFont()
@@ -371,6 +374,39 @@ void tst_font::resolve()
QVERIFY(control2ChildControlFont != control4Font);
}
+void tst_font::variableAxes_data()
+{
+ QTest::addColumn<QFont::Tag>("axesName");
+ QTest::addColumn<float>("axesValue");
+
+ QTest::addRow("wght") << QFont::Tag("wght") << 200.0f;
+}
+
+void tst_font::variableAxes()
+{
+ QFETCH(QFont::Tag, axesName);
+ QFETCH(float, axesValue);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(QString(R"QML(
+ import QtQuick
+ import QtQuick.Controls
+
+ Text {
+ font.variableAxes: {
+ "%1": %2
+ }
+ }
+ )QML").arg(axesName.toString()).arg(axesValue).toUtf8(), QUrl());
+
+ QScopedPointer<QObject> control(component.create());
+ QVERIFY2(!control.isNull(), qPrintable(component.errorString()));
+
+ const QFont font = control->property("font").value<QFont>();
+ QCOMPARE(font.variableAxisValue(axesName), axesValue);
+}
+
QTEST_MAIN(tst_font)
#include "tst_font.moc"
diff --git a/tests/auto/quickcontrols/palette/CMakeLists.txt b/tests/auto/quickcontrols/palette/CMakeLists.txt
index fd4ee2d680..26c143d1c4 100644
--- a/tests/auto/quickcontrols/palette/CMakeLists.txt
+++ b/tests/auto/quickcontrols/palette/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_palette LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/palette/data/bindings.qml b/tests/auto/quickcontrols/palette/data/bindings.qml
index a584007e54..44f31ccf7d 100644
--- a/tests/auto/quickcontrols/palette/data/bindings.qml
+++ b/tests/auto/quickcontrols/palette/data/bindings.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/palette/data/childPopupInheritance.qml b/tests/auto/quickcontrols/palette/data/childPopupInheritance.qml
new file mode 100644
index 0000000000..35859b5798
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/childPopupInheritance.qml
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+
+ Popup {
+ id: parentPopup
+ objectName: "parentPopup"
+ palette.windowText: "#ffdead"
+
+ Label {
+ text: "parentPopupLabel"
+ objectName: text
+ }
+
+ Popup {
+ id: childPopup
+ objectName: "childPopup"
+
+ Label {
+ text: "childPopupLabel"
+ objectName: text
+ }
+
+ Popup {
+ id: grandchildPopup
+ objectName: "grandchildPopup"
+
+ Label {
+ text: "grandchildPopupLabel"
+ objectName: text
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/data/comboBoxPopupWithApplicationWindow.qml b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithApplicationWindow.qml
new file mode 100644
index 0000000000..436d3cdad6
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithApplicationWindow.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias topLevelComboBox: topLevelComboBox
+ property alias popup: popup
+ property alias comboBoxInPopup: comboBoxInPopup
+
+ ComboBox {
+ id: topLevelComboBox
+ model: ["ONE", "TWO", "THREE"]
+ }
+
+ Popup {
+ id: popup
+ width: 200
+ height: 200
+ visible: true
+ palette.window: "red"
+
+ ComboBox {
+ id: comboBoxInPopup
+ model: ["ONE", "TWO", "THREE"]
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/data/comboBoxPopupWithWindow.qml b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithWindow.qml
new file mode 100644
index 0000000000..d806f30d01
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/comboBoxPopupWithWindow.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+
+Window {
+ width: 400
+ height: 400
+
+ property alias topLevelComboBox: topLevelComboBox
+ property alias popup: popup
+ property alias comboBoxInPopup: comboBoxInPopup
+
+ ComboBox {
+ id: topLevelComboBox
+ model: ["ONE", "TWO", "THREE"]
+ }
+
+ Popup {
+ id: popup
+ width: 200
+ height: 200
+ visible: true
+ palette.window: "red"
+
+ ComboBox {
+ id: comboBoxInPopup
+ model: ["ONE", "TWO", "THREE"]
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/data/inheritance-childcontrol.qml b/tests/auto/quickcontrols/palette/data/inheritance-childcontrol.qml
index f476020517..6b625ca1ca 100644
--- a/tests/auto/quickcontrols/palette/data/inheritance-childcontrol.qml
+++ b/tests/auto/quickcontrols/palette/data/inheritance-childcontrol.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/inheritance-childpopup.qml b/tests/auto/quickcontrols/palette/data/inheritance-childpopup.qml
index 8b7a033bd7..d668c626ac 100644
--- a/tests/auto/quickcontrols/palette/data/inheritance-childpopup.qml
+++ b/tests/auto/quickcontrols/palette/data/inheritance-childpopup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/inheritance-control.qml b/tests/auto/quickcontrols/palette/data/inheritance-control.qml
index 2cd5c125f4..fc67bd8735 100644
--- a/tests/auto/quickcontrols/palette/data/inheritance-control.qml
+++ b/tests/auto/quickcontrols/palette/data/inheritance-control.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/inheritance-dynamicchildcontrol.qml b/tests/auto/quickcontrols/palette/data/inheritance-dynamicchildcontrol.qml
index 18305058bc..c4436bdd91 100644
--- a/tests/auto/quickcontrols/palette/data/inheritance-dynamicchildcontrol.qml
+++ b/tests/auto/quickcontrols/palette/data/inheritance-dynamicchildcontrol.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/inheritance-dynamicchildpopup.qml b/tests/auto/quickcontrols/palette/data/inheritance-dynamicchildpopup.qml
index 6b16f1a46b..30dac26408 100644
--- a/tests/auto/quickcontrols/palette/data/inheritance-dynamicchildpopup.qml
+++ b/tests/auto/quickcontrols/palette/data/inheritance-dynamicchildpopup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/inheritance-dynamiccontrol.qml b/tests/auto/quickcontrols/palette/data/inheritance-dynamiccontrol.qml
index df0e13bf2c..bf8cfdc3c8 100644
--- a/tests/auto/quickcontrols/palette/data/inheritance-dynamiccontrol.qml
+++ b/tests/auto/quickcontrols/palette/data/inheritance-dynamiccontrol.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/inheritance-dynamicpopup.qml b/tests/auto/quickcontrols/palette/data/inheritance-dynamicpopup.qml
index 76b7d7cbda..b0454dc38c 100644
--- a/tests/auto/quickcontrols/palette/data/inheritance-dynamicpopup.qml
+++ b/tests/auto/quickcontrols/palette/data/inheritance-dynamicpopup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/inheritance-popup.qml b/tests/auto/quickcontrols/palette/data/inheritance-popup.qml
index a5010f0f11..98698304a7 100644
--- a/tests/auto/quickcontrols/palette/data/inheritance-popup.qml
+++ b/tests/auto/quickcontrols/palette/data/inheritance-popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/listview.qml b/tests/auto/quickcontrols/palette/data/listview.qml
index 92816fde19..8555b0b8ba 100644
--- a/tests/auto/quickcontrols/palette/data/listview.qml
+++ b/tests/auto/quickcontrols/palette/data/listview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/palette-appwindow-bindingpalette.qml b/tests/auto/quickcontrols/palette/data/palette-appwindow-bindingpalette.qml
new file mode 100644
index 0000000000..ce7ca82700
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/palette-appwindow-bindingpalette.qml
@@ -0,0 +1,15 @@
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 500
+ height: 300
+ palette: customPalette
+
+ property alias cstmPalette: customPalette
+
+ Palette { id: customPalette }
+
+ Component.onCompleted: { window.palette.buttonText = "white" }
+}
diff --git a/tests/auto/quickcontrols/palette/data/palette-appwindow-custom.qml b/tests/auto/quickcontrols/palette/data/palette-appwindow-custom.qml
index 22876b90fa..752385ac02 100644
--- a/tests/auto/quickcontrols/palette/data/palette-appwindow-custom.qml
+++ b/tests/auto/quickcontrols/palette/data/palette-appwindow-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -25,4 +25,5 @@ ApplicationWindow {
palette.window: "plum"
palette.windowText: "salmon"
palette.placeholderText: "magenta"
+ palette.accent: "darkkhaki"
}
diff --git a/tests/auto/quickcontrols/palette/data/palette-appwindow-default.qml b/tests/auto/quickcontrols/palette/data/palette-appwindow-default.qml
index f9c767b876..62809ea03b 100644
--- a/tests/auto/quickcontrols/palette/data/palette-appwindow-default.qml
+++ b/tests/auto/quickcontrols/palette/data/palette-appwindow-default.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/palette-control-custom.qml b/tests/auto/quickcontrols/palette/data/palette-control-custom.qml
index 4c4206cdfd..ae3eda25f7 100644
--- a/tests/auto/quickcontrols/palette/data/palette-control-custom.qml
+++ b/tests/auto/quickcontrols/palette/data/palette-control-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -25,4 +25,5 @@ Control {
palette.window: "plum"
palette.windowText: "salmon"
palette.placeholderText: "magenta"
+ palette.accent: "darkkhaki"
}
diff --git a/tests/auto/quickcontrols/palette/data/palette-control-default.qml b/tests/auto/quickcontrols/palette/data/palette-control-default.qml
index 7cd58e12b3..48320fd3a8 100644
--- a/tests/auto/quickcontrols/palette/data/palette-control-default.qml
+++ b/tests/auto/quickcontrols/palette/data/palette-control-default.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/palette-popup-custom.qml b/tests/auto/quickcontrols/palette/data/palette-popup-custom.qml
index c534d30156..a254fbcb99 100644
--- a/tests/auto/quickcontrols/palette/data/palette-popup-custom.qml
+++ b/tests/auto/quickcontrols/palette/data/palette-popup-custom.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -25,4 +25,5 @@ Popup {
palette.window: "plum"
palette.windowText: "salmon"
palette.placeholderText: "magenta"
+ palette.accent: "darkkhaki"
}
diff --git a/tests/auto/quickcontrols/palette/data/palette-popup-default.qml b/tests/auto/quickcontrols/palette/data/palette-popup-default.qml
index b9db73c9fb..a6bc8d11db 100644
--- a/tests/auto/quickcontrols/palette/data/palette-popup-default.qml
+++ b/tests/auto/quickcontrols/palette/data/palette-popup-default.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/reset-color.qml b/tests/auto/quickcontrols/palette/data/reset-color.qml
new file mode 100644
index 0000000000..4b62211cdb
--- /dev/null
+++ b/tests/auto/quickcontrols/palette/data/reset-color.qml
@@ -0,0 +1,52 @@
+// Copyright (C) 2022 zccrs <zccrs@live.com>, JiDe Zhang <zhangjide@uniontech.com>.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ property alias control: control
+ property alias item1Palette: item1.palette
+ property alias item2Palette: item2.palette
+
+ palette {
+ disabled {
+ windowText: "blue"
+ }
+ }
+
+ Item {
+ id: item1
+
+ palette.disabled {
+ text: "yellow"
+ }
+
+ Item {
+ id: item2
+
+ palette.disabled {
+ window: "yellow"
+ }
+ }
+ }
+
+ Control {
+ id: control
+ objectName: "Control"
+
+ palette {
+ window: "red"
+ disabled: item2.palette.disabled
+ }
+ }
+
+ function resetColor() {
+ control.palette.window = undefined
+ }
+
+ function resetGroup() {
+ control.palette.disabled = undefined
+ }
+}
diff --git a/tests/auto/quickcontrols/palette/data/resolve.qml b/tests/auto/quickcontrols/palette/data/resolve.qml
index 78cf091848..72ee0a9a4a 100644
--- a/tests/auto/quickcontrols/palette/data/resolve.qml
+++ b/tests/auto/quickcontrols/palette/data/resolve.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 zccrs <zccrs@live.com>, JiDe Zhang <zhangjide@uniontech.com>.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/data/set-palette.qml b/tests/auto/quickcontrols/palette/data/set-palette.qml
index b987a7d6b1..d263e6edb0 100644
--- a/tests/auto/quickcontrols/palette/data/set-palette.qml
+++ b/tests/auto/quickcontrols/palette/data/set-palette.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/palette/qtquickcontrols2.conf b/tests/auto/quickcontrols/palette/qtquickcontrols2.conf
index c5de6b36dd..97b122c566 100644
--- a/tests/auto/quickcontrols/palette/qtquickcontrols2.conf
+++ b/tests/auto/quickcontrols/palette/qtquickcontrols2.conf
@@ -1,5 +1,3 @@
[Basic]
Palette\Base=#efefef
-
-[Basic\Palette]
-Text=#101010
+Palette\Text=#101010
diff --git a/tests/auto/quickcontrols/palette/tst_palette.cpp b/tests/auto/quickcontrols/palette/tst_palette.cpp
index e3a53f0c21..75ee023bb7 100644
--- a/tests/auto/quickcontrols/palette/tst_palette.cpp
+++ b/tests/auto/quickcontrols/palette/tst_palette.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtGui/qpalette.h>
@@ -7,8 +7,11 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
+#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
+#include <QtQuickTemplates2/private/qquickcombobox_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p.h>
@@ -16,14 +19,10 @@
#include <QtQuickTemplates2/private/qquicktheme_p_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickControls2/qquickstyle.h>
+#include <QSignalSpy>
-//using namespace QQuickVisualTestUtils;
-
-// Need a more descriptive failure message: QTBUG-87039
-#define COMPARE_PALETTES(actualPalette, expectedPalette) \
- QVERIFY2(actualPalette == expectedPalette, \
- qPrintable(QString::fromLatin1("\n Actual: %1\n Expected: %2") \
- .arg(QDebug::toString(actualPalette)).arg(QDebug::toString(expectedPalette))));
+using namespace QQuickVisualTestUtils;
+using namespace QQuickControlsTestUtils;
class tst_palette : public QQmlDataTest
{
@@ -40,6 +39,7 @@ private slots:
void inheritance_data();
void inheritance();
+ void childPopupInheritance();
void defaultPalette_data();
void defaultPalette();
@@ -52,6 +52,12 @@ private slots:
void updateBindings();
void resolve();
+
+ void resetColor();
+ void updateBindingPalette();
+
+ void comboBoxPopup_data();
+ void comboBoxPopup();
};
tst_palette::tst_palette()
@@ -106,6 +112,7 @@ void tst_palette::palette_data()
customPalette.setColor(QPalette::Window, QColor("plum"));
customPalette.setColor(QPalette::WindowText, QColor("salmon"));
customPalette.setColor(QPalette::PlaceholderText, QColor("magenta"));
+ customPalette.setColor(QPalette::Accent, QColor("darkkhaki"));
QTest::newRow("Control:custom") << "palette-control-custom.qml" << customPalette;
QTest::newRow("AppWindow:custom") << "palette-appwindow-custom.qml" << customPalette;
@@ -127,7 +134,7 @@ void tst_palette::palette()
QVariant var = object->property("palette");
QVERIFY(var.isValid());
- COMPARE_PALETTES(var.value<QQuickPalette*>()->toQPalette(), expectedPalette);
+ QCOMPARE(var.value<QQuickPalette*>()->toQPalette(), expectedPalette);
}
void tst_palette::inheritance_data()
@@ -203,6 +210,40 @@ void tst_palette::inheritance()
QCOMPARE(grandChildPalette->window(), windowPalette->window());
}
+// The child popups in inheritance() don't test actual nested child popups,
+// only popups that are children of items and the items within those popups.
+// We need to specifically test this to prevent QTBUG-115707 from happening again.
+void tst_palette::childPopupInheritance()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("childPopupInheritance.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ const auto *windowPrivate = QQuickWindowPrivate::get(helper.window);
+ const auto windowsWindowTextColor = windowPrivate->palette()->toQPalette().color(QPalette::WindowText);
+
+ // parentPopup sets windowText explicitly, so its label should use that color.
+ auto *parentPopup = helper.appWindow->findChild<QQuickPopup *>("parentPopup");
+ QVERIFY(parentPopup);
+ parentPopup->open();
+ QTRY_VERIFY(parentPopup->isOpened());
+ auto *parentPopupLabel = helper.appWindow->findChild<QObject *>("parentPopupLabel");
+ QVERIFY(parentPopupLabel);
+ QCOMPARE(parentPopupLabel->property("color").value<QColor>(), "#ffdead");
+
+ // All other child popups don't set anything explicitly, and should inherit from their window.
+ auto *childPopup = helper.appWindow->findChild<QQuickPopup *>("childPopup");
+ QVERIFY(childPopup);
+ auto *childPopupLabel = helper.appWindow->findChild<QObject *>("childPopupLabel");
+ QVERIFY(childPopupLabel);
+ QCOMPARE(childPopupLabel->property("color").value<QColor>(), windowsWindowTextColor);
+
+ auto *grandchildPopup = helper.appWindow->findChild<QQuickPopup *>("grandchildPopup");
+ QVERIFY(grandchildPopup);
+ auto *grandchildPopupLabel = helper.appWindow->findChild<QObject *>("grandchildPopupLabel");
+ QVERIFY(grandchildPopupLabel);
+ QCOMPARE(grandchildPopupLabel->property("color").value<QColor>(), windowsWindowTextColor);
+}
+
class TestTheme : public QQuickTheme
{
public:
@@ -427,6 +468,150 @@ void tst_palette::resolve()
control->property("palette").value<QQuickPalette*>()->windowText());
}
+void tst_palette::resetColor()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("reset-color.qml"));
+
+ QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow*>(component.create()));
+ QVERIFY2(!window.isNull(), qPrintable(component.errorString()));
+ auto windowPalette = window->property("palette").value<QQuickPalette*>();
+ QVERIFY(windowPalette);
+
+ auto control = window->property("control").value<QQuickControl*>();
+ QVERIFY(control);
+ auto controlPalette = control->property("palette").value<QQuickPalette*>();
+ QVERIFY(controlPalette);
+ auto item1Palette = window->property("item1Palette").value<QQuickPalette*>();
+ QVERIFY(item1Palette);
+ auto item2Palette = window->property("item2Palette").value<QQuickPalette*>();
+ QVERIFY(item2Palette);
+
+ QCOMPARE(controlPalette->disabled()->window(), item2Palette->disabled()->window());
+ QCOMPARE(controlPalette->disabled()->text(), item1Palette->disabled()->text());
+ QCOMPARE(controlPalette->disabled()->windowText(), windowPalette->disabled()->windowText());
+
+ {
+ QSignalSpy spy(controlPalette, &QQuickPalette::changed);
+ item1Palette->disabled()->setText(Qt::red);
+ QVERIFY(spy.count() == 1 || spy.wait());
+ QCOMPARE(controlPalette->disabled()->text(), QColor(Qt::red));
+ }
+
+ {
+ QSignalSpy spy(controlPalette, &QQuickPalette::changed);
+ item1Palette->disabled()->setWindowText(Qt::red);
+ QVERIFY(spy.count() == 1 || spy.wait());
+ QCOMPARE(controlPalette->disabled()->windowText(), QColor(Qt::red));
+ }
+
+ {
+ QSignalSpy spy(controlPalette, &QQuickPalette::changed);
+ item2Palette->disabled()->setWindowText(Qt::blue);
+ QVERIFY(spy.count() == 1 || spy.wait());
+ QCOMPARE(controlPalette->disabled()->windowText(), QColor(Qt::blue));
+ }
+
+ {
+ QSignalSpy spy(controlPalette, &QQuickPalette::changed);
+ QMetaObject::invokeMethod(window.get(), "resetColor", Qt::DirectConnection);
+ QCOMPARE(controlPalette->window(), windowPalette->window());
+ windowPalette->setWindow(Qt::green);
+ QCOMPARE(controlPalette->window(), QColor(Qt::green));
+ QVERIFY(spy.count() >= 2);
+ }
+
+ {
+ QSignalSpy spy(controlPalette, &QQuickPalette::changed);
+ QMetaObject::invokeMethod(window.get(), "resetGroup", Qt::DirectConnection);
+ QCOMPARE(controlPalette->disabled()->windowText(), windowPalette->disabled()->windowText());
+ windowPalette->disabled()->setWindow(Qt::blue);
+ QCOMPARE(controlPalette->disabled()->window(), QColor(Qt::blue));
+ item2Palette->disabled()->setWindow(Qt::red);
+ QCOMPARE(controlPalette->disabled()->window(), QColor(Qt::blue));
+ if (spy.count() == 0)
+ spy.wait();
+ QCOMPARE(spy.count(), 2);
+ }
+}
+
+void tst_palette::updateBindingPalette()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("palette-appwindow-bindingpalette.qml"));
+
+ QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow*>(component.create()));
+ QVERIFY2(!window.isNull(), qPrintable(component.errorString()));
+ auto *windowPalette = window->property("palette").value<QQuickPalette *>();
+ QVERIFY(windowPalette);
+ auto *customPalette = window->property("cstmPalette").value<QQuickPalette *>();
+ QVERIFY(customPalette);
+
+ QCOMPARE(windowPalette->buttonText(), QColor("white"));
+
+ QColor buttonTextColor("red");
+ customPalette->setButtonText(buttonTextColor);
+ QCOMPARE(customPalette->buttonText(), buttonTextColor);
+ QCOMPARE(windowPalette->buttonText(), customPalette->buttonText());
+}
+
+void tst_palette::comboBoxPopup_data()
+{
+ QTest::addColumn<QString>("style");
+ QTest::addColumn<QString>("qmlFilePath");
+
+ QTest::newRow("Window, Basic") << "Basic" << "comboBoxPopupWithWindow.qml";
+ QTest::newRow("ApplicationWindow, Basic") << "Basic" << "comboBoxPopupWithApplicationWindow.qml";
+ QTest::newRow("Window, Fusion") << "Fusion" << "comboBoxPopupWithWindow.qml";
+ QTest::newRow("ApplicationWindow, Fusion") << "Fusion" << "comboBoxPopupWithApplicationWindow.qml";
+}
+
+// Unlike regular popups, which should inherit their palette from the window and not the parent popup,
+// combo box popups should inherit their palette from the combo box itself.
+void tst_palette::comboBoxPopup()
+{
+ QFETCH(QString, style);
+ QFETCH(QString, qmlFilePath);
+
+ qmlClearTypeRegistrations();
+ QQuickStyle::setStyle(style);
+
+ QQuickApplicationHelper helper(this, qmlFilePath);
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ const auto *windowPalette = window->property("palette").value<QQuickPalette *>();
+ QVERIFY(windowPalette);
+
+ const auto *popup = window->property("popup").value<QQuickPopup *>();
+ QVERIFY(popup);
+ const auto *popupBackground = popup->background();
+ QCOMPARE(popupBackground->property("color"), QColorConstants::Red);
+ QCOMPARE(popupBackground->property("palette").value<QQuickPalette*>()->toQPalette().window().color(),
+ QColorConstants::Red);
+
+ // This has the default palette.
+ const auto *topLevelComboBox = window->property("topLevelComboBox").value<QQuickComboBox *>();
+ QVERIFY(topLevelComboBox);
+ const auto *topLevelComboBoxBackground = topLevelComboBox->popup()->background();
+ QCOMPARE_NE(topLevelComboBoxBackground->property("color"), QColorConstants::Red);
+ QCOMPARE_NE(topLevelComboBoxBackground->property("palette").value<QQuickPalette*>()->toQPalette().window().color(),
+ QColorConstants::Red);
+
+ // The popup that this combo box is in has its window role set to red,
+ // so the combo box's popup background should be red too.
+ const auto *comboBoxInPopup = window->property("comboBoxInPopup").value<QQuickComboBox *>();
+ QVERIFY(comboBoxInPopup);
+ const auto *comboBoxInPopupBackground = comboBoxInPopup->popup()->background();
+ QCOMPARE(comboBoxInPopupBackground->property("color"), QColorConstants::Red);
+ QCOMPARE(comboBoxInPopupBackground->property("palette").value<QQuickPalette*>()->toQPalette().window().color(),
+ QColorConstants::Red);
+}
+
QTEST_MAIN(tst_palette)
#include "tst_palette.moc"
diff --git a/tests/auto/quickcontrols/platform/CMakeLists.txt b/tests/auto/quickcontrols/platform/CMakeLists.txt
index a2f77fdd9c..5669052475 100644
--- a/tests/auto/quickcontrols/platform/CMakeLists.txt
+++ b/tests/auto/quickcontrols/platform/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_platform LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/platform/data/tst_colordialog.qml b/tests/auto/quickcontrols/platform/data/tst_colordialog.qml
index 093a99b31b..6d5b11e84b 100644
--- a/tests/auto/quickcontrols/platform/data/tst_colordialog.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_colordialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_filedialog.qml b/tests/auto/quickcontrols/platform/data/tst_filedialog.qml
index 63a6747e67..66f27cc9ec 100644
--- a/tests/auto/quickcontrols/platform/data/tst_filedialog.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_filedialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_folderdialog.qml b/tests/auto/quickcontrols/platform/data/tst_folderdialog.qml
index 67a5bf0ac0..ed975746b7 100644
--- a/tests/auto/quickcontrols/platform/data/tst_folderdialog.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_folderdialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_fontdialog.qml b/tests/auto/quickcontrols/platform/data/tst_fontdialog.qml
index 884ea7e7d2..684f6e9058 100644
--- a/tests/auto/quickcontrols/platform/data/tst_fontdialog.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_fontdialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_menu.qml b/tests/auto/quickcontrols/platform/data/tst_menu.qml
index 3119c459d6..5e5bf02a2f 100644
--- a/tests/auto/quickcontrols/platform/data/tst_menu.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_menubar.qml b/tests/auto/quickcontrols/platform/data/tst_menubar.qml
index ea0d005528..180e656694 100644
--- a/tests/auto/quickcontrols/platform/data/tst_menubar.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_menubar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_menuitem.qml b/tests/auto/quickcontrols/platform/data/tst_menuitem.qml
index a446bf8c4d..fe218bd32f 100644
--- a/tests/auto/quickcontrols/platform/data/tst_menuitem.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_menuitem.qml
@@ -1,9 +1,10 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
import Qt.labs.platform
+import org.qtproject.Test
TestCase {
id: testCase
@@ -63,6 +64,30 @@ TestCase {
compare(spy.count, 1)
}
+ function test_shortcut() {
+ if (!TestHelper.shortcutsSupported)
+ return;
+
+ let item = createTemporaryObject(menuItem, testCase)
+ verify(item)
+ let spy = createTemporaryObject(signalSpyComponent, testCase, {
+ target: item, signalName: "triggered"
+ })
+ verify(spy)
+ verify(spy.valid)
+
+ data = [TestHelper.shortcutInt, TestHelper.shortcutString, TestHelper.shortcutKeySequence]
+ for (let i = 0; i < data.length; ++i) {
+ item.shortcut = data[i]
+
+ compare(spy.count, i)
+ keySequence("CTRL+P")
+ compare(spy.count, i + 1)
+
+ item.shortcut = {}
+ }
+ }
+
function test_role() {
// Q_ENUMS(QPlatformMenuItem::MenuRole)
compare(MenuItem.NoRole, 0)
diff --git a/tests/auto/quickcontrols/platform/data/tst_menuitemgroup.qml b/tests/auto/quickcontrols/platform/data/tst_menuitemgroup.qml
index 74f3a59a48..e83567cae2 100644
--- a/tests/auto/quickcontrols/platform/data/tst_menuitemgroup.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_menuitemgroup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_menuseparator.qml b/tests/auto/quickcontrols/platform/data/tst_menuseparator.qml
index 4fee4b9374..1c3b58ae98 100644
--- a/tests/auto/quickcontrols/platform/data/tst_menuseparator.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_menuseparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_messagedialog.qml b/tests/auto/quickcontrols/platform/data/tst_messagedialog.qml
index 93ed4e261e..a42541a4a8 100644
--- a/tests/auto/quickcontrols/platform/data/tst_messagedialog.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_messagedialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_standardpaths.qml b/tests/auto/quickcontrols/platform/data/tst_standardpaths.qml
index 95cdbdf49d..d70323db52 100644
--- a/tests/auto/quickcontrols/platform/data/tst_standardpaths.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_standardpaths.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/data/tst_systemtrayicon.qml b/tests/auto/quickcontrols/platform/data/tst_systemtrayicon.qml
index 3f043d275f..ef2e0b957c 100644
--- a/tests/auto/quickcontrols/platform/data/tst_systemtrayicon.qml
+++ b/tests/auto/quickcontrols/platform/data/tst_systemtrayicon.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/platform/tst_platform.cpp b/tests/auto/quickcontrols/platform/tst_platform.cpp
index 94bf1d6a59..dbcc383f03 100644
--- a/tests/auto/quickcontrols/platform/tst_platform.cpp
+++ b/tests/auto/quickcontrols/platform/tst_platform.cpp
@@ -1,13 +1,26 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqmlengine.h>
#include <QtQuickTest/quicktest.h>
+using namespace Qt::Literals;
+
class Setup : public QObject
{
Q_OBJECT
Q_PROPERTY(bool shortcutsSupported READ areShortcutsSupported CONSTANT FINAL)
+ Q_PROPERTY(QString shortcutString MEMBER m_shortcutString CONSTANT FINAL)
+#if QT_CONFIG(shortcut)
+ Q_PROPERTY(int shortcutInt MEMBER m_shortcutInt CONSTANT FINAL)
+ Q_PROPERTY(QKeySequence shortcutKeySequence MEMBER m_shortcutKeySequence CONSTANT FINAL)
+#endif
+
+ const QString m_shortcutString = u"CTRL+P"_s;
+#if QT_CONFIG(shortcut)
+ const int m_shortcutInt = QKeySequence::Print;
+ const QKeySequence m_shortcutKeySequence{ Qt::CTRL | Qt::Key_P };
+#endif
public:
bool areShortcutsSupported() const
diff --git a/tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt b/tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt
index 8bc7b26b31..4715d15c95 100644
--- a/tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt
+++ b/tests/auto/quickcontrols/pointerhandlers/CMakeLists.txt
@@ -4,7 +4,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_pointerhandlers LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp b/tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp
index aa83175252..46521aad8a 100644
--- a/tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp
+++ b/tests/auto/quickcontrols/pointerhandlers/tst_pointerhandlers.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTest/quicktest.h>
@@ -199,7 +199,7 @@ void tst_pointerhandlers::buttonTapHandler() // QTBUG-105609
case QPointingDevice::DeviceType::Mouse:
// click it
QTest::mouseClick(&window, mouseButton, Qt::NoModifier, pos);
- QTRY_COMPARE(clickedSpy.size(), 1); // perhaps Button should not react to right-click, but it does
+ QTRY_COMPARE(clickedSpy.size(), mouseButton == Qt::RightButton ? 0 : 1);
QCOMPARE(tappedSpy.size(), 1);
break;
diff --git a/tests/auto/quickcontrols/pressandhold/CMakeLists.txt b/tests/auto/quickcontrols/pressandhold/CMakeLists.txt
index 8d8c967a56..88213edbf3 100644
--- a/tests/auto/quickcontrols/pressandhold/CMakeLists.txt
+++ b/tests/auto/quickcontrols/pressandhold/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_pressandhold LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/pressandhold/tst_pressandhold.cpp b/tests/auto/quickcontrols/pressandhold/tst_pressandhold.cpp
index 98013a1d0c..e65c69a856 100644
--- a/tests/auto/quickcontrols/pressandhold/tst_pressandhold.cpp
+++ b/tests/auto/quickcontrols/pressandhold/tst_pressandhold.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtQuick>
@@ -43,17 +43,19 @@ void tst_PressAndHold::pressAndHold_data()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<QByteArray>("signal");
+ QTest::addColumn<bool>("acceptsRightMouseButton");
- QTest::newRow("Button") << QByteArray("import QtQuick.Controls; Button { text: 'Button' }") << QByteArray(SIGNAL(pressAndHold()));
- QTest::newRow("SwipeDelegate") << QByteArray("import QtQuick.Controls; SwipeDelegate { text: 'SwipeDelegate' }") << QByteArray(SIGNAL(pressAndHold()));
- QTest::newRow("TextField") << QByteArray("import QtQuick.Controls; TextField { text: 'TextField' }") << QByteArray(SIGNAL(pressAndHold(QQuickMouseEvent*)));
- QTest::newRow("TextArea") << QByteArray("import QtQuick.Controls; TextArea { text: 'TextArea' }") << QByteArray(SIGNAL(pressAndHold(QQuickMouseEvent*)));
+ QTest::newRow("Button") << QByteArray("import QtQuick.Controls; Button { text: 'Button' }") << QByteArray(SIGNAL(pressAndHold())) << false;
+ QTest::newRow("SwipeDelegate") << QByteArray("import QtQuick.Controls; SwipeDelegate { text: 'SwipeDelegate' }") << QByteArray(SIGNAL(pressAndHold())) << false;
+ QTest::newRow("TextField") << QByteArray("import QtQuick.Controls; TextField { text: 'TextField' }") << QByteArray(SIGNAL(pressAndHold(QQuickMouseEvent*))) << true;
+ QTest::newRow("TextArea") << QByteArray("import QtQuick.Controls; TextArea { text: 'TextArea' }") << QByteArray(SIGNAL(pressAndHold(QQuickMouseEvent*))) << true;
}
void tst_PressAndHold::pressAndHold()
{
QFETCH(QByteArray, data);
QFETCH(QByteArray, signal);
+ QFETCH(bool, acceptsRightMouseButton);
QQmlEngine engine;
QQmlComponent component(&engine);
@@ -106,19 +108,21 @@ void tst_PressAndHold::pressAndHold()
waitSpy.clear();
// pressAndHold() canceled by 2nd press
- QGuiApplication::sendEvent(control.data(), &press);
- QGuiApplication::sendEvent(control.data(), &press2); // cancels pressAndHold()
- QGuiApplication::sendEvent(waitControl.data(), &press);
- // by the time the second control emits pressAndHold(), we can reliably
- // assume that the first control would have emitted pressAndHold() if it
- // wasn't canceled as appropriate by the 2nd press event above
- QTRY_COMPARE(waitSpy.size(), 1);
- QCOMPARE(spy.size(), 0);
- QGuiApplication::sendEvent(control.data(), &release);
- QGuiApplication::sendEvent(waitControl.data(), &release);
- QCOMPARE(waitSpy.size(), 1);
- QCOMPARE(spy.size(), 0);
- waitSpy.clear();
+ if (acceptsRightMouseButton) {
+ QGuiApplication::sendEvent(control.data(), &press);
+ QGuiApplication::sendEvent(control.data(), &press2); // cancels pressAndHold()
+ QGuiApplication::sendEvent(waitControl.data(), &press);
+ // by the time the second control emits pressAndHold(), we can reliably
+ // assume that the first control would have emitted pressAndHold() if it
+ // wasn't canceled as appropriate by the 2nd press event above
+ QTRY_COMPARE(waitSpy.size(), 1);
+ QCOMPARE(spy.size(), 0);
+ QGuiApplication::sendEvent(control.data(), &release);
+ QGuiApplication::sendEvent(waitControl.data(), &release);
+ QCOMPARE(waitSpy.size(), 1);
+ QCOMPARE(spy.size(), 0);
+ waitSpy.clear();
+ }
}
void tst_PressAndHold::keepSelection_data()
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt b/tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt
index 397441d16a..c8134173b7 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickapplicationwindow LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
@@ -40,11 +40,11 @@ qt_internal_add_test(tst_qquickapplicationwindow
TESTDATA ${test_data}
)
-#### Keys ignored in scope 1:.:.:qquickapplicationwindow.pro:<TRUE>:
-# OTHER_FILES = "data/*.qml"
-
-## Scopes:
-#####################################################################
+# Make the QML files available to Creator's locator.
+target_sources(tst_qquickapplicationwindow
+ PRIVATE
+ ${test_data}
+)
qt_internal_extend_target(tst_qquickapplicationwindow CONDITION ANDROID OR IOS
DEFINES
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/activeFocusControl.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/activeFocusControl.qml
index f699a27fc1..c6073fedeb 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/activeFocusControl.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/activeFocusControl.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/activefocusontab.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/activefocusontab.qml
index 7798c4ff1d..dd53faf537 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/activefocusontab.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/activefocusontab.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/attachedProperties.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/attachedProperties.qml
index 4bb773a831..d84cdc3daa 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/attachedProperties.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/attachedProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml
new file mode 100644
index 0000000000..0e4ed277d0
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/backgroundSize.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 600
+ height: 400
+
+ background: Item {
+ implicitWidth: 123
+ implicitHeight: 456
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/basicapplicationwindow.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/basicapplicationwindow.qml
index ba4bf1597e..d59afa7e6b 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/basicapplicationwindow.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/basicapplicationwindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/clearfocusondestruction.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/clearfocusondestruction.qml
index 56dfa89e68..e86902c12c 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/clearfocusondestruction.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/clearfocusondestruction.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/defaultFocus.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/defaultFocus.qml
index 02ff15ff45..68b889bdc2 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/defaultFocus.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/defaultFocus.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/fill.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/fill.qml
index d57c4c4f71..db0c2a4e89 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/fill.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/fill.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/focusAfterPopupClosed.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/focusAfterPopupClosed.qml
index 5e13ac274a..a40277a45c 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/focusAfterPopupClosed.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/focusAfterPopupClosed.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/font.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/font.qml
index 4f33439811..8909666b76 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/font.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/font.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/layout.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/layout.qml
index 11f2371673..4bcb56e993 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/layout.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/layout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/layoutLayout.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/layoutLayout.qml
index e27fe07b12..3f6b51706c 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/layoutLayout.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/layoutLayout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/locale.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/locale.qml
index 7e0b40095e..e2ccd677d7 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/locale.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/locale.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/opacity.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/opacity.qml
index ea2de5ccd2..5a7edc532b 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/data/opacity.qml
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/opacity.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
index e055340dde..1d25ffeed6 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtCore/qoperatingsystemversion.h>
@@ -22,8 +22,10 @@
#include <QtQuickTemplates2/private/qquicktextfield_p.h>
#include <QtQuickTemplates2/private/qquicktheme_p_p.h>
#include <QtQuickControls2/qquickstyle.h>
+#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
using namespace QQuickVisualTestUtils;
+using namespace QQuickControlsTestUtils;
class tst_QQuickApplicationWindow : public QQmlDataTest
{
@@ -50,6 +52,7 @@ private slots:
void layoutLayout();
void componentComplete();
void opacity();
+ void backgroundSize();
};
tst_QQuickApplicationWindow::tst_QQuickApplicationWindow()
@@ -275,6 +278,10 @@ void tst_QQuickApplicationWindow::implicitFill()
void tst_QQuickApplicationWindow::attachedProperties()
{
+ if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive))
+ {
+ QSKIP("This test uses multiple windows and it crashes on EGLFS because of that");
+ }
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("attachedProperties.qml"));
@@ -930,6 +937,51 @@ void tst_QQuickApplicationWindow::opacity()
QVERIFY(window);
}
+void tst_QQuickApplicationWindow::backgroundSize()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("backgroundSize.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ QCOMPARE(window->width(), 600);
+ QCOMPARE(window->height(), 400);
+
+ auto *background = window->background();
+ QCOMPARE(background->implicitWidth(), 123);
+ QCOMPARE(background->implicitHeight(), 456);
+ QCOMPARE(background->width(), window->width());
+ QCOMPARE(background->height(), window->height());
+
+ // Changing the implicit size of the background shouldn't have any effect
+ // on its size if it was never explicitly set.
+ background->setImplicitWidth(234);
+ QCOMPARE(background->implicitWidth(), 234);
+ QCOMPARE(window->width(), 600);
+ QCOMPARE(background->width(), window->width());
+
+ background->setImplicitHeight(567);
+ QCOMPARE(background->implicitHeight(), 567);
+ QCOMPARE(window->height(), 400);
+ QCOMPARE(background->height(), window->height());
+
+ // Explicitly setting the size of the background should ensure
+ // that it's respected from that point onwards.
+ background->setWidth(345);
+ QCOMPARE(background->implicitWidth(), 234);
+ QCOMPARE(window->width(), 600);
+ QCOMPARE(background->width(), 345);
+
+ window->setWidth(610);
+ QCOMPARE(background->width(), 345);
+
+ background->setHeight(678);
+ QCOMPARE(background->implicitHeight(), 567);
+ QCOMPARE(window->height(), 400);
+ QCOMPARE(background->height(), 678);
+
+ window->setHeight(410);
+ QCOMPARE(background->height(), 678);
+}
+
QTEST_MAIN(tst_QQuickApplicationWindow)
#include "tst_qquickapplicationwindow.moc"
diff --git a/tests/auto/quickcontrols/qquickcolor/CMakeLists.txt b/tests/auto/quickcontrols/qquickcolor/CMakeLists.txt
index efd06f15ff..9cf98b4c84 100644
--- a/tests/auto/quickcontrols/qquickcolor/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickcolor/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickcolor LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickcolor/data/tst_color.qml b/tests/auto/quickcontrols/qquickcolor/data/tst_color.qml
index d7b6848c3c..cdbb7c41aa 100644
--- a/tests/auto/quickcontrols/qquickcolor/data/tst_color.qml
+++ b/tests/auto/quickcontrols/qquickcolor/data/tst_color.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
diff --git a/tests/auto/quickcontrols/qquickcolor/tst_qquickcolor.cpp b/tests/auto/quickcontrols/qquickcolor/tst_qquickcolor.cpp
index ba92ef5b86..fc6ab552dd 100644
--- a/tests/auto/quickcontrols/qquickcolor/tst_qquickcolor.cpp
+++ b/tests/auto/quickcontrols/qquickcolor/tst_qquickcolor.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(tst_qquickcolor)
diff --git a/tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt b/tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt
index a43b14f1fa..69ae922913 100644
--- a/tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickcontrol/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickcontrol LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickcontrol/data/flickable.qml b/tests/auto/quickcontrols/qquickcontrol/data/flickable.qml
index 9378e3f9c0..e8ea9d0f76 100644
--- a/tests/auto/quickcontrols/qquickcontrol/data/flickable.qml
+++ b/tests/auto/quickcontrols/qquickcontrol/data/flickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp b/tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp
index ddb4b7ff8b..1060ddb7fe 100644
--- a/tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp
+++ b/tests/auto/quickcontrols/qquickcontrol/tst_qquickcontrol.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
diff --git a/tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt b/tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt
index 2515a8d30e..62a0ee1e96 100644
--- a/tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickdrawer/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickdrawer LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-button.qml b/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-button.qml
index ebba679f97..81f7218436 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-button.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-hover.qml b/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-hover.qml
index d3a9ba499f..b2d380cbda 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-hover.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-hover.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-wheel.qml b/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-wheel.qml
index f59bf5b2db..792fd6b814 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-wheel.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow-wheel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow.qml b/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow.qml
index dcc37f4307..e279c89b11 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/applicationwindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/dragOverModalShadow.qml b/tests/auto/quickcontrols/qquickdrawer/data/dragOverModalShadow.qml
index 73d01fe968..b91e0e9678 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/dragOverModalShadow.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/dragOverModalShadow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/flickable.qml b/tests/auto/quickcontrols/qquickdrawer/data/flickable.qml
index 4ef4605f72..699c214941 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/flickable.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/flickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/grabber.qml b/tests/auto/quickcontrols/qquickdrawer/data/grabber.qml
index fdcce390d7..20d992cd6a 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/grabber.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/grabber.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/header.qml b/tests/auto/quickcontrols/qquickdrawer/data/header.qml
index c68202ab1d..a36eb3b904 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/header.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/header.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/multiTouch.qml b/tests/auto/quickcontrols/qquickdrawer/data/multiTouch.qml
index e9e9aa7a18..bf2e49f016 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/multiTouch.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/multiTouch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/multiple.qml b/tests/auto/quickcontrols/qquickdrawer/data/multiple.qml
index 0f9f8ab63a..f6e5eb02af 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/multiple.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/multiple.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/reposition.qml b/tests/auto/quickcontrols/qquickdrawer/data/reposition.qml
index 4c9eea2b67..dc9003e7e2 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/reposition.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/reposition.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/slider.qml b/tests/auto/quickcontrols/qquickdrawer/data/slider.qml
index f1deb12500..b2d4853ff9 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/slider.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/topEdgeScreenEdge.qml b/tests/auto/quickcontrols/qquickdrawer/data/topEdgeScreenEdge.qml
index 8bf3b2b4fc..6826df8645 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/topEdgeScreenEdge.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/topEdgeScreenEdge.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/window-button.qml b/tests/auto/quickcontrols/qquickdrawer/data/window-button.qml
index 2f0d4af7e6..5c4a40547e 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/window-button.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/window-button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/window-hover.qml b/tests/auto/quickcontrols/qquickdrawer/data/window-hover.qml
index 81a14dc700..3756873504 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/window-hover.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/window-hover.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/window-wheel.qml b/tests/auto/quickcontrols/qquickdrawer/data/window-wheel.qml
index 802c60bf1c..80a35fc4da 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/window-wheel.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/window-wheel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickdrawer/data/window.qml b/tests/auto/quickcontrols/qquickdrawer/data/window.qml
index fbdc4047d7..a6cf9aab8e 100644
--- a/tests/auto/quickcontrols/qquickdrawer/data/window.qml
+++ b/tests/auto/quickcontrols/qquickdrawer/data/window.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
index 4ec9943135..5eb3895849 100644
--- a/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
+++ b/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
@@ -63,8 +63,10 @@ private slots:
void hover_data();
void hover();
+#if QT_CONFIG(wheelevent)
void wheel_data();
void wheel();
+#endif
void multiple();
@@ -617,6 +619,7 @@ void tst_QQuickDrawer::hover()
QVERIFY(!drawerItem->isHovered());
}
+#if QT_CONFIG(wheelevent)
void tst_QQuickDrawer::wheel_data()
{
QTest::addColumn<QString>("source");
@@ -703,6 +706,7 @@ void tst_QQuickDrawer::wheel()
QVERIFY(qFuzzyCompare(drawerSlider->value(), oldDrawerValue)); // must not have moved
}
}
+#endif
void tst_QQuickDrawer::multiple()
{
@@ -826,6 +830,13 @@ void tst_QQuickDrawer::multiple()
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDrawer->width() / 2, window->height() / 2));
QTRY_COMPARE(rightDrawer->position(), 1.0);
QCOMPARE(leftDrawer->position(), 0.0);
+
+ // Hide the window, so it receives no more stray events
+ window->hide();
+ QVERIFY(QTest::qWaitFor([window](){ return !window->isVisible(); }));
+
+ // Remove posted events, before the window goes out of scope
+ QGuiApplication::removePostedEvents(window);
}
void tst_QQuickDrawer::touch_data()
@@ -1135,14 +1146,16 @@ void tst_QQuickDrawer::flickable()
QTest::touchEvent(window, touchDevice.data()).press(0, from);
static const int steps = 10;
- for (int i = 0; i < steps; ++i) {
- int x = i * qAbs(from.x() - to.x()) / steps;
- int y = i * qAbs(from.y() - to.y()) / steps;
-
+ const QPoint distance(to.x() - from.x(), to.y() - from.y());
+ for (int i = 0; i <= steps; ++i) {
+ const auto pos = from + QPoint(
+ i * distance.x() / steps,
+ i * distance.y() / steps
+ );
if (mouse)
- QTest::mouseMove(window, QPoint(x, y));
+ QTest::mouseMove(window, pos);
else
- QTest::touchEvent(window, touchDevice.data()).move(0, QPoint(x, y));
+ QTest::touchEvent(window, touchDevice.data()).move(0, pos);
QTest::qWait(1); // avoid infinite velocity
}
@@ -1432,14 +1445,24 @@ void tst_QQuickDrawer::touchOutsideOverlay() // QTBUG-103811
void tst_QQuickDrawer::destroyWhileVisible()
{
- QQuickView window;
- QVERIFY(QQuickTest::showView(window, testFileUrl("itemPartialOverlayModal.qml")));
- auto *drawer = window.rootObject()->findChild<QQuickDrawer*>();
+ QScopedPointer<QQuickView> window(new QQuickView());
+ QVERIFY(QQuickTest::showView(*window, testFileUrl("itemPartialOverlayModal.qml")));
+ auto *drawer = window->rootObject()->findChild<QQuickDrawer*>();
QVERIFY(drawer);
drawer->open();
QTRY_VERIFY(drawer->isOpened());
+
+ QQuickItem *dimmer = QQuickPopupPrivate::get(drawer)->dimmer;
+ QSignalSpy dimmerDeletedSpy(dimmer, &QObject::destroyed);
+
// don't crash here when the drawer closes with an exit transition
+ window.reset();
+
+ // make sure the dimmer is deleted
+ QTRY_COMPARE(dimmerDeletedSpy.size(), 1);
+
+
}
QTEST_QUICKCONTROLS_MAIN(tst_QQuickDrawer)
diff --git a/tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt b/tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt
index 1925c85dc8..37220a37f7 100644
--- a/tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickheaderview/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickheaderview LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickheaderview/data/DefaultRoles.qml b/tests/auto/quickcontrols/qquickheaderview/data/DefaultRoles.qml
new file mode 100644
index 0000000000..29e7e80d9e
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickheaderview/data/DefaultRoles.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Controls
+import TestTableModel
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias headerView: headerView
+
+ Column {
+ HorizontalHeaderView {
+ id: headerView
+
+ width: 200
+ height: 200
+
+ model: TestTableModel {
+ id: tm
+ objectName: "tableModel"
+ rowCount: 1
+ columnCount: 1
+ }
+ textRole: "toolTip"
+ delegate: Rectangle {
+ required property string toolTip
+ implicitWidth: 40
+ implicitHeight: 40
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickheaderview/data/ListModel.qml b/tests/auto/quickcontrols/qquickheaderview/data/ListModel.qml
index de63fc600b..5c6eef831b 100644
--- a/tests/auto/quickcontrols/qquickheaderview/data/ListModel.qml
+++ b/tests/auto/quickcontrols/qquickheaderview/data/ListModel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickheaderview/data/Window.qml b/tests/auto/quickcontrols/qquickheaderview/data/Window.qml
index 6082fab42e..5f910bbd9c 100644
--- a/tests/auto/quickcontrols/qquickheaderview/data/Window.qml
+++ b/tests/auto/quickcontrols/qquickheaderview/data/Window.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickheaderview/data/headerData.qml b/tests/auto/quickcontrols/qquickheaderview/data/headerData.qml
new file mode 100644
index 0000000000..6cc1d8aeb0
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickheaderview/data/headerData.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import TestTableModelWithHeader
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias headerView: headerView
+
+ Column {
+ HorizontalHeaderView {
+ id: headerView
+ syncView: tableView
+ textRole: "customRole"
+ }
+ TableView {
+ id: tableView
+ width: 100
+ height: 100
+ model: TestTableModelWithHeader {
+ rowCount: 2
+ columnCount: 2
+ }
+ delegate: Label {
+ text: customRole
+ leftPadding: 10
+ rightPadding: 10
+
+ required property string customRole
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickheaderview/data/resizableHandlerBlockingEvents.qml b/tests/auto/quickcontrols/qquickheaderview/data/resizableHandlerBlockingEvents.qml
new file mode 100644
index 0000000000..0b520c3be3
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickheaderview/data/resizableHandlerBlockingEvents.qml
@@ -0,0 +1,29 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ width: 800
+ height: 600
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ color: mouseArea.pressed ? "tomato" : "steelblue"
+
+ MouseArea {
+ id: mouseArea
+ objectName: "mouseArea"
+ anchors.fill: parent
+ }
+ }
+
+ HorizontalHeaderView {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickheaderview/tst_qquickheaderview.cpp b/tests/auto/quickcontrols/qquickheaderview/tst_qquickheaderview.cpp
index 28b92fef71..1d83b56f41 100644
--- a/tests/auto/quickcontrols/qquickheaderview/tst_qquickheaderview.cpp
+++ b/tests/auto/quickcontrols/qquickheaderview/tst_qquickheaderview.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qsignalspy.h>
#include <QtTest/qtest.h>
@@ -8,13 +8,18 @@
#include <QAbstractItemModelTester>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlComponent>
-#include <QtQuick/private/qquickwindow_p.h>
+#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquicktext_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
#include <QtQuickTemplates2/private/qquickheaderview_p.h>
+#include <QtQuickTemplates2/private/qquicklabel_p.h>
#include <private/qquickheaderview_p_p.h>
+using namespace QQuickVisualTestUtils;
+
class TestTableModel : public QAbstractTableModel {
Q_OBJECT
Q_PROPERTY(int rowCount READ rowCount WRITE setRowCount NOTIFY rowCountChanged)
@@ -130,6 +135,10 @@ class TestTableModelWithHeader : public TestTableModel {
Q_OBJECT
public:
+ enum Role {
+ CustomRole = Qt::UserRole
+ };
+
void setRowCount(int count) override
{
vData.resize(count);
@@ -141,6 +150,17 @@ public:
hData.resize(count);
TestTableModel::setColumnCount(count);
}
+
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ switch (role) {
+ case CustomRole:
+ return QString("%1-%2").arg(index.column()).arg(index.row());
+ default:
+ return TestTableModel::data(index, role);
+ }
+ }
+
Q_INVOKABLE QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override
{
@@ -153,6 +173,8 @@ public:
auto &data = orientation == Qt::Horizontal ? hData : vData;
return data[section].toString();
}
+ case CustomRole:
+ return (orientation == Qt::Horizontal ? "c" : "r") + QString::number(section);
default:
return QVariant();
}
@@ -174,6 +196,13 @@ public:
return true;
}
+ Q_INVOKABLE QHash<int, QByteArray> roleNames() const override
+ {
+ auto names = TestTableModel::roleNames();
+ names[CustomRole] = "customRole";
+ return names;
+ }
+
private:
QList<QVariant> hData, vData;
};
@@ -196,6 +225,12 @@ private slots:
void testModel();
void listModel();
+ void resizableHandlerBlockingEvents();
+
+ void headerData();
+
+ void warnMissingDefaultRole();
+
private:
QQmlEngine *engine;
QString errorString;
@@ -212,7 +247,7 @@ private:
};
tst_QQuickHeaderView::tst_QQuickHeaderView()
- : QQmlDataTest(QT_QMLTEST_DATADIR)
+ : QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::FailOnWarnings)
{
}
@@ -296,10 +331,14 @@ void tst_QQuickHeaderView::testOrientation()
QVERIFY(vhv);
hhv->setSyncView(&otherView);
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*Setting syncDirection other than Qt::Horizontal is invalid."));
hhv->setSyncDirection(Qt::Vertical);
QVERIFY(QQuickTest::qWaitForPolish(hhv));
vhv->setSyncView(&otherView);
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*Setting syncDirection other than Qt::Vertical is invalid."));
vhv->setSyncDirection(Qt::Horizontal);
QVERIFY(QQuickTest::qWaitForPolish(vhv));
@@ -376,6 +415,50 @@ void tst_QQuickHeaderView::listModel()
QCOMPARE(vhvCell2->property("text"), "222");
}
+// A header shouldn't block events outside of itself.
+void tst_QQuickHeaderView::resizableHandlerBlockingEvents()
+{
+ QQuickApplicationHelper helper(this, QStringLiteral("resizableHandlerBlockingEvents.qml"));
+ QVERIFY2(helper.errorMessage.isEmpty(), helper.errorMessage);
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto mouseArea = window->findChild<QQuickMouseArea *>("mouseArea");
+ QVERIFY(mouseArea);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, mapCenterToWindow(mouseArea));
+ QVERIFY(mouseArea->isPressed());
+}
+
+void tst_QQuickHeaderView::headerData()
+{
+ QQuickApplicationHelper helper(this, QStringLiteral("headerData.qml"));
+ QVERIFY2(helper.errorMessage.isEmpty(), helper.errorMessage);
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto headerView = window->property("headerView").value<QQuickHeaderViewBase *>();
+ QVERIFY(headerView);
+ const auto firstHeaderCell = headerView->itemAtIndex(headerView->index(0, 0));
+ QVERIFY(firstHeaderCell);
+ const auto label = firstHeaderCell->findChild<QQuickLabel *>();
+ QVERIFY(label);
+ QCOMPARE(label->text(), "c0");
+}
+
+void tst_QQuickHeaderView::warnMissingDefaultRole()
+{
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*toolTip.*"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Required property.*"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("TableView.*"));
+ QQuickApplicationHelper helper(this, QStringLiteral("DefaultRoles.qml"));
+ QVERIFY2(helper.errorMessage.isEmpty(), helper.errorMessage);
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+}
+
QTEST_MAIN(tst_QQuickHeaderView)
#include "tst_qquickheaderview.moc"
diff --git a/tests/auto/quickcontrols/qquickiconimage/BLACKLIST b/tests/auto/quickcontrols/qquickiconimage/BLACKLIST
index 70c935375d..dcd5b24391 100644
--- a/tests/auto/quickcontrols/qquickiconimage/BLACKLIST
+++ b/tests/auto/quickcontrols/qquickiconimage/BLACKLIST
@@ -13,3 +13,5 @@ android
android
[translucentColors]
android
+[svgSourceBindingSourceSize]
+android
diff --git a/tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt b/tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt
index 58f8a8f6f5..0eae234d7f 100644
--- a/tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickiconimage/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickiconimage LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickiconimage/data/translucentColors.qml b/tests/auto/quickcontrols/qquickiconimage/data/translucentColors.qml
index 61ad329d55..d30be4340a 100644
--- a/tests/auto/quickcontrols/qquickiconimage/data/translucentColors.qml
+++ b/tests/auto/quickcontrols/qquickiconimage/data/translucentColors.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickiconimage/tst_qquickiconimage.cpp b/tests/auto/quickcontrols/qquickiconimage/tst_qquickiconimage.cpp
index 83df118de3..74f3140ebe 100644
--- a/tests/auto/quickcontrols/qquickiconimage/tst_qquickiconimage.cpp
+++ b/tests/auto/quickcontrols/qquickiconimage/tst_qquickiconimage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/qsignalspy.h>
@@ -49,9 +49,6 @@ private slots:
private:
void setTheme();
-
- qreal dpr;
- int integerDpr;
};
static QImage grabItemToImage(QQuickItem *item)
@@ -62,14 +59,14 @@ static QImage grabItemToImage(QQuickItem *item)
return result->image();
}
-#define SKIP_IF_DPR_TOO_HIGH() \
+#define INIT_DPR(view) \
+ [[maybe_unused]] const qreal dpr = view.devicePixelRatio(); \
+ [[maybe_unused]] const int integerDpr = qCeil(dpr); \
if (dpr > 2) \
QSKIP("Test does not support device pixel ratio greater than 2")
tst_qquickiconimage::tst_qquickiconimage() :
- QQmlDataTest(QT_QMLTEST_DATADIR),
- dpr(qGuiApp->devicePixelRatio()),
- integerDpr(qCeil(dpr))
+ QQmlDataTest(QT_QMLTEST_DATADIR)
{
QQuickStyle::setStyle("Basic");
}
@@ -78,6 +75,7 @@ void tst_qquickiconimage::initTestCase()
{
QQmlDataTest::initTestCase();
QIcon::setThemeName(QStringLiteral("testtheme"));
+
}
void tst_qquickiconimage::defaults()
@@ -91,14 +89,12 @@ void tst_qquickiconimage::defaults()
void tst_qquickiconimage::nameBindingSourceSize()
{
- // We can't have images for every DPR.
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view(testFileUrl("nameBindingSourceSize.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject()->childItems().at(0));
QVERIFY(iconImage);
@@ -132,11 +128,10 @@ void tst_qquickiconimage::nameBindingSourceSize()
void tst_qquickiconimage::nameBindingSourceSizeWidthHeight()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view(testFileUrl("nameBindingSourceSizeWidthHeight.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject());
QVERIFY(iconImage);
@@ -150,11 +145,10 @@ void tst_qquickiconimage::nameBindingSourceSizeWidthHeight()
void tst_qquickiconimage::nameBindingNoSizes()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view(testFileUrl("nameBindingNoSizes.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject());
QVERIFY(iconImage);
@@ -169,11 +163,11 @@ void tst_qquickiconimage::nameBindingNoSizes()
void tst_qquickiconimage::sourceBindingNoSizes()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view(testFileUrl("sourceBindingNoSizes.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
+ INIT_DPR(view);
+
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
@@ -194,13 +188,12 @@ void tst_qquickiconimage::sourceBindingNoSizes()
void tst_qquickiconimage::sourceBindingSourceSize()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view(testFileUrl("sourceBindingSourceSize.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject()->childItems().at(0));
QVERIFY(iconImage);
@@ -227,13 +220,12 @@ void tst_qquickiconimage::sourceBindingSourceSize()
void tst_qquickiconimage::sourceBindingSourceSizeWidthHeight()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view(testFileUrl("sourceBindingSourceSizeWidthHeight.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject());
QVERIFY(iconImage);
@@ -247,13 +239,12 @@ void tst_qquickiconimage::sourceBindingSourceSizeWidthHeight()
void tst_qquickiconimage::sourceBindingSourceTooLarge()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view(testFileUrl("sourceBindingSourceTooLarge.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject());
QVERIFY(iconImage);
@@ -283,8 +274,6 @@ void tst_qquickiconimage::alignment_data()
void tst_qquickiconimage::alignment()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QFETCH(QQuickImage::HAlignment, horizontalAlignment);
QFETCH(QQuickImage::VAlignment, verticalAlignment);
@@ -293,6 +282,7 @@ void tst_qquickiconimage::alignment()
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject()->childItems().at(0));
QVERIFY(iconImage);
@@ -335,6 +325,9 @@ void tst_qquickiconimage::svgNoSizes()
QQuickView view(testFileUrl("svgNoSizes.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
+
+ INIT_DPR(view);
+
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
@@ -344,8 +337,8 @@ void tst_qquickiconimage::svgNoSizes()
QQuickImage *image = qobject_cast<QQuickImage*>(view.rootObject()->childItems().at(1));
QVERIFY(image);
- QCOMPARE(iconImage->sourceSize().width(), 48);
- QCOMPARE(iconImage->sourceSize().height(), 48);
+ QCOMPARE(iconImage->sourceSize().width(), 48 * integerDpr);
+ QCOMPARE(iconImage->sourceSize().height(), 48 * integerDpr);
QCOMPARE(iconImage->implicitWidth(), 48.0);
QCOMPARE(iconImage->implicitHeight(), 48.0);
QCOMPARE(iconImage->width(), 48.0);
@@ -383,13 +376,12 @@ void tst_qquickiconimage::svgSourceBindingSourceSize()
void tst_qquickiconimage::color()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view(testFileUrl("color.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject()->childItems().at(0));
QVERIFY(iconImage);
@@ -438,8 +430,6 @@ void tst_qquickiconimage::changeSourceSize()
void tst_qquickiconimage::fileSelectors()
{
- SKIP_IF_DPR_TOO_HIGH();
-
QQuickView view;
QScopedPointer<QQmlFileSelector> fileSelector(new QQmlFileSelector(view.engine()));
fileSelector->setExtraSelectors(QStringList() << "testselector");
@@ -448,6 +438,7 @@ void tst_qquickiconimage::fileSelectors()
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
+ INIT_DPR(view);
QQuickIconImage *iconImage = qobject_cast<QQuickIconImage*>(view.rootObject()->childItems().at(0));
QVERIFY(iconImage);
diff --git a/tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt b/tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt
index 48ede72c13..66add1069d 100644
--- a/tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickiconlabel/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickiconlabel LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickiconlabel/data/colorChanges.qml b/tests/auto/quickcontrols/qquickiconlabel/data/colorChanges.qml
index 35785313db..d28ea1238b 100644
--- a/tests/auto/quickcontrols/qquickiconlabel/data/colorChanges.qml
+++ b/tests/auto/quickcontrols/qquickiconlabel/data/colorChanges.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickiconlabel/data/iconlabel.qml b/tests/auto/quickcontrols/qquickiconlabel/data/iconlabel.qml
index e181526cfd..5b9db97a0d 100644
--- a/tests/auto/quickcontrols/qquickiconlabel/data/iconlabel.qml
+++ b/tests/auto/quickcontrols/qquickiconlabel/data/iconlabel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyIcon.qml b/tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyIcon.qml
index a1d90afbf4..bc0d62e867 100644
--- a/tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyIcon.qml
+++ b/tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyIcon.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyText.qml b/tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyText.qml
index 5894434b30..c79597a382 100644
--- a/tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyText.qml
+++ b/tests/auto/quickcontrols/qquickiconlabel/data/spacingWithOnlyText.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp b/tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp
index 8d42cf5faf..9f0feb5139 100644
--- a/tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp
+++ b/tests/auto/quickcontrols/qquickiconlabel/tst_qquickiconlabel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qlist.h>
@@ -308,6 +308,7 @@ void tst_qquickiconlabel::colorChanges()
void tst_qquickiconlabel::iconSourceContext()
{
+#if defined(QT_BUILD_INTERNAL)
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("iconSourceContext.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
@@ -337,7 +338,7 @@ void tst_qquickiconlabel::iconSourceContext()
= static_cast<QQuickImagePrivate *>(QQuickItemPrivate::get(image));
QCOMPARE(imagePrivate->pix.url(), testFileUrl("a.png"));
}
-
+#endif
}
QTEST_MAIN(tst_qquickiconlabel)
diff --git a/tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt
index 1985784386..91df77c5c9 100644
--- a/tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickimaginestyle/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickimaginestyle LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickimaginestyle/data/tst_imagine.qml b/tests/auto/quickcontrols/qquickimaginestyle/data/tst_imagine.qml
index c6bb83f72a..26e5f15920 100644
--- a/tests/auto/quickcontrols/qquickimaginestyle/data/tst_imagine.qml
+++ b/tests/auto/quickcontrols/qquickimaginestyle/data/tst_imagine.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickimaginestyle/tst_qquickimaginestyle.cpp b/tests/auto/quickcontrols/qquickimaginestyle/tst_qquickimaginestyle.cpp
index 6cdbbca8e9..aa5d5a0459 100644
--- a/tests/auto/quickcontrols/qquickimaginestyle/tst_qquickimaginestyle.cpp
+++ b/tests/auto/quickcontrols/qquickimaginestyle/tst_qquickimaginestyle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(tst_qquickimaginestyle)
diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
index 784da21614..0fe3fe7b9a 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickmaterialstyle LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
@@ -41,7 +41,8 @@ qt_internal_add_resource(tst_qquickmaterialstyle "qmake_immediate"
${qmake_immediate_resource_files}
)
-
-#### Keys ignored in scope 1:.:.:qquickmaterialstyle.pro:<TRUE>:
-# OTHER_FILES = "$$PWD/data/*.qml"
-# TEMPLATE = "app"
+# Make the QML files available to Creator's locator.
+target_sources(tst_qquickmaterialstyle
+ PRIVATE
+ ${test_data}
+)
diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
index bf836b2048..cdd8fb41e6 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
+++ b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -7,6 +7,7 @@ import QtTest
import QtQuick.Templates as T
import QtQuick.Controls
import QtQuick.Controls.Material
+import QtQuick.Controls.Material.impl as MaterialImpl
TestCase {
id: testCase
@@ -23,12 +24,17 @@ TestCase {
}
Component {
- id: button
+ id: signalSpyComponent
+ SignalSpy {}
+ }
+
+ Component {
+ id: buttonComponent
Button { }
}
Component {
- id: styledButton
+ id: styledButtonComponent
Button {
Material.theme: Material.Dark
Material.primary: Material.DeepOrange
@@ -45,17 +51,6 @@ TestCase {
}
Component {
- id: styledWindowComponent
- Window {
- Material.theme: Material.Dark
- Material.primary: Material.Brown
- Material.accent: Material.Green
- Material.background: Material.Yellow
- Material.foreground: Material.Grey
- }
- }
-
- Component {
id: buttonLoaderComponent
Loader {
active: false
@@ -96,6 +91,7 @@ TestCase {
property alias label2: labelInstance2
Popup {
id: popupInstance
+ objectName: "popupQObject"
Label {
id: labelInstance
text: "test"
@@ -151,7 +147,7 @@ TestCase {
}
function test_defaults() {
- let control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(buttonComponent, testCase)
verify(control)
verify(control.Material)
compare(control.Material.primary, Material.color(Material.Indigo))
@@ -165,7 +161,7 @@ TestCase {
}
function test_set() {
- let control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(buttonComponent, testCase)
verify(control)
control.Material.primary = Material.Green
control.Material.accent = Material.Brown
@@ -182,7 +178,7 @@ TestCase {
}
function test_reset() {
- let control = createTemporaryObject(styledButton, testCase)
+ let control = createTemporaryObject(styledButtonComponent, testCase)
verify(control)
compare(control.Material.primary, Material.color(Material.DeepOrange))
compare(control.Material.accent, Material.color(Material.DeepPurple, themeshade(control.Material.theme)))
@@ -218,18 +214,18 @@ TestCase {
function test_inheritance(data) {
let prop = data.tag
- let parent = createTemporaryObject(button, testCase)
+ let parent = createTemporaryObject(buttonComponent, testCase)
parent.Material[prop] = data.value1
compare(parent.Material[prop], data.value1)
- let child1 = button.createObject(parent)
+ let child1 = buttonComponent.createObject(parent)
compare(child1.Material[prop], data.value1)
parent.Material[prop] = data.value2
compare(parent.Material[prop], data.value2)
compare(child1.Material[prop], data.value2)
- let child2 = button.createObject(parent)
+ let child2 = buttonComponent.createObject(parent)
compare(child2.Material[prop], data.value2)
child2.Material[prop] = data.value1
@@ -243,13 +239,13 @@ TestCase {
compare(child1.Material[prop], parent.Material[prop])
verify(child2.Material[prop] !== parent.Material[prop])
- let grandChild1 = button.createObject(child1)
- let grandChild2 = button.createObject(child2)
+ let grandChild1 = buttonComponent.createObject(child1)
+ let grandChild2 = buttonComponent.createObject(child2)
compare(grandChild1.Material[prop], child1.Material[prop])
compare(grandChild2.Material[prop], child2.Material[prop])
- let themelessGrandGrandChild = button.createObject(grandChild1)
- let grandGrandGrandChild1 = button.createObject(themelessGrandGrandChild)
+ let themelessGrandGrandChild = buttonComponent.createObject(grandChild1)
+ let grandGrandGrandChild1 = buttonComponent.createObject(themelessGrandGrandChild)
compare(grandGrandGrandChild1.Material[prop], parent.Material[prop])
child1.Material[prop] = data.value2
@@ -417,7 +413,7 @@ TestCase {
let parentWindow = createTemporaryObject(data.component, null)
verify(parentWindow)
- let control = button.createObject(parentWindow.contentItem)
+ let control = buttonComponent.createObject(parentWindow.contentItem)
verify(control)
compare(control.Material.primary, parentWindow.Material.primary)
compare(control.Material.accent, parentWindow.Material.accent)
@@ -548,14 +544,16 @@ TestCase {
window.combo.forceActiveFocus()
verify(window.combo.activeFocus)
keyClick(Qt.Key_Space)
- verify(window.combo.popup.visible)
- let listView = window.combo.popup.contentItem
+ let comboPopup = window.combo.popup
+ verify(comboPopup.visible)
+ let listView = comboPopup.contentItem
verify(listView)
let child = listView.contentItem.children[0]
verify(child)
compare(window.Material.theme, Material.Light)
compare(window.combo.Material.theme, Material.Dark)
compare(child.Material.theme, Material.Dark)
+ compare(comboPopup.background.color, comboPopup.Material.dialogColor)
compare(window.Material.primary, Material.color(Material.Blue))
compare(window.combo.Material.primary, Material.color(Material.Blue))
compare(child.Material.primary, Material.color(Material.Blue))
@@ -589,7 +587,7 @@ TestCase {
}
function test_colors(data) {
- let control = createTemporaryObject(button, testCase)
+ let control = createTemporaryObject(buttonComponent, testCase)
verify(control)
let prop = data.tag
@@ -635,15 +633,15 @@ TestCase {
return [
{tag: "Button:pixelSize", type: "Button", attribute: "pixelSize", value: 14, window: 20, pane: 10},
{tag: "Button:weight", type: "Button", attribute: "weight", value: Font.Medium, window: Font.Black, pane: Font.Bold},
- {tag: "Button:capitalization", type: "Button", attribute: "capitalization", value: Font.AllUppercase, window: Font.Capitalize, pane: Font.AllLowercase},
+ {tag: "Button:capitalization", type: "Button", attribute: "capitalization", value: Font.MixedCase, window: Font.Capitalize, pane: Font.AllLowercase},
{tag: "TabButton:pixelSize", type: "TabButton", attribute: "pixelSize", value: 14, window: 20, pane: 10},
{tag: "TabButton:weight", type: "TabButton", attribute: "weight", value: Font.Medium, window: Font.Black, pane: Font.Bold},
- {tag: "TabButton:capitalization", type: "TabButton", attribute: "capitalization", value: Font.AllUppercase, window: Font.Capitalize, pane: Font.AllLowercase},
+ {tag: "TabButton:capitalization", type: "TabButton", attribute: "capitalization", value: Font.MixedCase, window: Font.Capitalize, pane: Font.AllLowercase},
{tag: "ToolButton:pixelSize", type: "ToolButton", attribute: "pixelSize", value: 14, window: 20, pane: 10},
{tag: "ToolButton:weight", type: "ToolButton", attribute: "weight", value: Font.Medium, window: Font.Black, pane: Font.Bold},
- {tag: "ToolButton:capitalization", type: "ToolButton", attribute: "capitalization", value: Font.AllUppercase, window: Font.Capitalize, pane: Font.AllLowercase},
+ {tag: "ToolButton:capitalization", type: "ToolButton", attribute: "capitalization", value: Font.MixedCase, window: Font.Capitalize, pane: Font.AllLowercase},
{tag: "ItemDelegate:pixelSize", type: "ItemDelegate", attribute: "pixelSize", value: 14, window: 20, pane: 10},
{tag: "ItemDelegate:weight", type: "ItemDelegate", attribute: "weight", value: Font.Medium, window: Font.Black, pane: Font.Bold},
@@ -746,7 +744,6 @@ TestCase {
function test_background_data() {
return [
{ tag: "button", inherit: false, wait: 400 },
- { tag: "combobox", inherit: false, wait: 400 },
{ tag: "drawer", inherit: true },
{ tag: "groupbox", inherit: true },
{ tag: "frame", inherit: true },
@@ -872,4 +869,500 @@ TestCase {
let item = createTemporaryObject(data.component, testCase)
verify(item)
}
+
+ Component {
+ id: sliderTickMarks
+ Slider {
+ }
+ }
+
+ function test_sliderTickMarks_data() {
+ return [
+ { // 1000 / 100 == 10 tick marks
+ to: 1000,
+ from: 0,
+ stepSize: 100,
+ snapMode: Slider.SnapAlways,
+ expectTickmarks: true
+ },
+ { // 10 / 1 == 10 tick marks
+ to: 10,
+ from: 0,
+ stepSize: 1,
+ snapMode: Slider.SnapAlways,
+ expectTickmarks: true
+ },
+ { // Should still be within the default backgrounds width/height ratio
+ to: 50,
+ from: 10,
+ stepSize: 1,
+ snapMode: Slider.SnapAlways,
+ expectTickmarks: true
+ },
+ { // Math.abs(.0 - 10.0) % 3 != 0 (aka not divisible)
+ to: 10,
+ from: 0,
+ stepSize: 3,
+ snapMode: Slider.SnapAlways,
+ expectTickmarks: false
+ },
+ { // Math.abs(0 - 810.0) % 100 != 0 (aka not divisible)
+ to: 810,
+ from: 0,
+ stepSize: 100,
+ snapMode: Slider.SnapAlways,
+ expectTickmarks: false
+ },
+ { // stepSize isn't positive
+ to: 10,
+ from: 0,
+ stepSize: -1,
+ snapMode: Slider.SnapAlways,
+ expectTickmarks: false
+ },
+ { // stepSize isn't positive
+ to: 10,
+ from: 0,
+ stepSize: 0,
+ snapMode: Slider.SnapAlways,
+ expectTickmarks: false
+ },
+ { // Number of tick marks would exceed the default width/height ratio
+ to: 100,
+ from: 10,
+ stepSize: 1,
+ snapMode: Slider.SnapAlways,
+ expectTickmarks: false
+ },
+ { // snapMode != Slider.SnapAlways
+ to: 10,
+ from: 0,
+ stepSize: 1,
+ snapMode: Slider.NoSnap,
+ expectTickmarks: false
+ }
+ ]
+ }
+
+ function test_sliderTickMarks(data) {
+ let params = {to: data.to, from: data.from, stepSize: data.stepSize, snapMode: data.snapMode}
+ let item = createTemporaryObject(sliderTickMarks, testCase, params)
+ verify(item)
+ compare(item["__isDiscrete"], data.expectTickmarks)
+ }
+
+ Component {
+ id: textFieldComponent
+ TextField {}
+ }
+
+ Component {
+ id: textAreaComponent
+ TextArea {}
+ }
+
+ function test_textFieldPlaceholderTextHorizontalAlignment_data() {
+ return [
+ { tag: "AlignLeft", horizontalAlignment: TextField.AlignLeft },
+ { tag: "AlignHCenter", horizontalAlignment: TextField.AlignHCenter },
+ { tag: "AlignRight", horizontalAlignment: TextField.AlignRight }
+ ]
+ }
+
+ function test_textFieldPlaceholderTextHorizontalAlignment(data) {
+ // The placeholder text should always be near the left side of the TextField, regardless of its horizontalAlignment.
+ let textField = createTemporaryObject(textFieldComponent, testCase, {
+ placeholderText: "TextField",
+ horizontalAlignment: data.horizontalAlignment
+ })
+ verify(textField)
+ let placeholderTextItem = textField.children[0]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ compare(placeholderTextItem.horizontalAlignment, data.horizontalAlignment)
+
+ textField.forceActiveFocus()
+ compare(placeholderTextItem.horizontalAlignment, data.horizontalAlignment)
+ textField.destroy()
+ }
+
+ function test_textAreaPlaceholderTextHorizontalAlignment_data() {
+ return [
+ { tag: "AlignLeft", horizontalAlignment: TextArea.AlignLeft },
+ { tag: "AlignHCenter", horizontalAlignment: TextArea.AlignHCenter },
+ { tag: "AlignRight", horizontalAlignment: TextArea.AlignRight },
+ { tag: "AlignJustify", horizontalAlignment: TextArea.AlignJustify }
+ ]
+ }
+
+ function test_textAreaPlaceholderTextHorizontalAlignment(data) {
+ // The placeholder text should always be near the left side of the TextArea, regardless of its horizontalAlignment.
+ let textArea = createTemporaryObject(textAreaComponent, testCase, {
+ placeholderText: "TextArea",
+ horizontalAlignment: data.horizontalAlignment
+ })
+ verify(textArea)
+ let placeholderTextItem = textArea.children[0]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ compare(placeholderTextItem.horizontalAlignment, data.horizontalAlignment)
+
+ textArea.forceActiveFocus()
+ compare(placeholderTextItem.horizontalAlignment, data.horizontalAlignment)
+ }
+
+ function test_placeholderTextPos() {
+ {
+ // The non-floating placeholder text should be in the middle of TextField regardless of its height.
+ let textField = createTemporaryObject(textFieldComponent, testCase, { placeholderText: "TextField" })
+ verify(textField)
+ let placeholderTextItem = textField.children[0]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ compare(placeholderTextItem.y, (textField.height - placeholderTextItem.height) / 2)
+ textField.height = 10
+ compare(placeholderTextItem.y, (textField.height - placeholderTextItem.height) / 2)
+ textField.destroy()
+ }
+
+ {
+ // The non-floating placeholder text should be near the top of TextArea while it has room...
+ let textArea = createTemporaryObject(textAreaComponent, testCase, { placeholderText: "TextArea" })
+ verify(textArea)
+ let placeholderTextItem = textArea.children[0]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ compare(placeholderTextItem.y, (placeholderTextItem.controlImplicitBackgroundHeight - placeholderTextItem.largestHeight) / 2)
+
+ // ... also when it has a lot of room...
+ textArea.height = 200
+ compare(placeholderTextItem.y, (placeholderTextItem.controlImplicitBackgroundHeight - placeholderTextItem.largestHeight) / 2)
+
+ // ... but when it doesn't have room, it should start behaving like TextField's.
+ textArea.height = 10
+ compare(placeholderTextItem.y, (textArea.height - placeholderTextItem.height) / 2)
+ }
+ }
+
+ function test_outlinedPlaceholderTextPosWithPadding_data() {
+ return [
+ { tag: "TextField, leftPadding=0", component: textFieldComponent, leftPadding: 0 },
+ { tag: "TextField, rightPadding=0", component: textFieldComponent, rightPadding: 0 },
+ { tag: "TextField, leftPadding=20", component: textFieldComponent, leftPadding: 20 },
+ { tag: "TextField, rightPadding=20", component: textFieldComponent, rightPadding: 20 },
+ { tag: "TextArea, leftPadding=0", component: textAreaComponent, leftPadding: 0 },
+ { tag: "TextArea, rightPadding=0", component: textAreaComponent, rightPadding: 0 },
+ { tag: "TextArea, leftPadding=20", component: textAreaComponent, leftPadding: 20 },
+ { tag: "TextArea, rightPadding=20", component: textAreaComponent, rightPadding: 20 },
+ ]
+ }
+
+ function test_outlinedPlaceholderTextPosWithPadding(data) {
+ let control = createTemporaryObject(data.component, testCase, {
+ text: "Text",
+ placeholderText: "Enter text..."
+ })
+ verify(control)
+
+ // Work around QTBUG-99231.
+ if (data.leftPadding !== undefined)
+ control.leftPadding = data.leftPadding
+ if (data.rightPadding !== undefined)
+ control.rightPadding = data.rightPadding
+
+ let placeholderTextItem = control.children[0]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ // This is the default value returned by textFieldHorizontalPadding when using a non-dense variant.
+ compare(placeholderTextItem.x, 16)
+ }
+
+ Component {
+ id: flickableTextAreaComponent
+
+ Flickable {
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: 20
+ width: 180
+ height: 100
+
+ TextArea.flickable: TextArea {
+ placeholderText: "Type something..."
+ text: "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn"
+ }
+ }
+ }
+
+ function test_placeholderTextInFlickable() {
+ let flickable = createTemporaryObject(flickableTextAreaComponent, testCase)
+ verify(flickable)
+
+ let textArea = flickable.TextArea.flickable
+ verify(textArea)
+ let placeholderTextItem = flickable.children[2]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+
+ // The placeholder text should always float at a fixed position at the top
+ // when text has been set, even when it's in a Flickable.
+ flickable.contentY = -50
+ compare(placeholderTextItem.y, -placeholderTextItem.largestHeight / 2)
+ flickable.contentY = 0
+
+ // When the text is cleared, it shouldn't float.
+ flickable.height = 160
+ textArea.text = ""
+ compare(placeholderTextItem.y, (placeholderTextItem.controlImplicitBackgroundHeight - placeholderTextItem.largestHeight) / 2)
+ // The background outline gap should be closed.
+ let textContainer = flickable.children[1]
+ verify(textContainer as MaterialImpl.MaterialTextContainer)
+ compare(textContainer.focusAnimationProgress, 0)
+ }
+
+ function test_outlinedTextAreaInFlickablePlaceholderTextClipping() {
+ let flickable = createTemporaryObject(flickableTextAreaComponent, testCase)
+ verify(flickable)
+
+ let textArea = flickable.TextArea.flickable
+ verify(textArea)
+ let placeholderTextItem = flickable.children[2]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ compare(textArea.Material.containerStyle, Material.Outlined)
+ // The Flickable doesn't clip at the moment so topInset should be 0.
+ compare(textArea.topInset, 0)
+ compare(textArea.topPadding, (textArea.implicitBackgroundHeight - placeholderTextItem.largestHeight) / 2)
+
+ // topInset should now be half the placeholder text's height,
+ // and topPadding adjusted accordingly.
+ flickable.clip = true
+ compare(textArea.topInset, placeholderTextItem.largestHeight / 2)
+ compare(textArea.topPadding, ((textArea.implicitBackgroundHeight - placeholderTextItem.largestHeight) / 2) + textArea.topInset)
+
+ // When the text is cleared, the placeholder text shouldn't float, but it should still be accounted for
+ // to avoid it causing jumps in layout sizes, for example.
+ const initialText = textArea.text
+ textArea.text = ""
+ compare(textArea.topPadding, ((textArea.implicitBackgroundHeight - placeholderTextItem.largestHeight) / 2) + textArea.topInset)
+
+ flickable.clip = false
+ compare(textArea.topInset, 0)
+ compare(textArea.topPadding, (textArea.implicitBackgroundHeight - placeholderTextItem.largestHeight) / 2)
+ }
+
+ function test_outlinedTextAreaPlaceholderTextClipping() {
+ let textArea = createTemporaryObject(textAreaComponent, testCase, {
+ placeholderText: "Type something",
+ text: "Text"
+ })
+ verify(textArea)
+ let placeholderTextItem = textArea.children[0]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ compare(textArea.topInset, 0)
+ compare(textArea.topPadding, (textArea.implicitBackgroundHeight - placeholderTextItem.largestHeight) / 2)
+
+ // topInset should now be half the placeholder text's height, and topPadding adjusted accordingly.
+ textArea.clip = true
+ compare(textArea.topInset, placeholderTextItem.largestHeight / 2)
+ compare(textArea.topPadding, ((textArea.implicitBackgroundHeight - placeholderTextItem.largestHeight) / 2) + textArea.topInset)
+ }
+
+ function test_outlinedTextFieldPlaceholderTextClipping() {
+ let textField = createTemporaryObject(textFieldComponent, testCase, {
+ placeholderText: "Type something",
+ text: "Text"
+ })
+ verify(textField)
+ let placeholderTextItem = textField.children[0]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ compare(textField.topInset, 0)
+ compare(textField.topPadding, textField.Material.textFieldVerticalPadding)
+
+ // topInset should now be half the placeholder text's height, and topPadding adjusted accordingly.
+ textField.clip = true
+ compare(textField.topInset, placeholderTextItem.largestHeight / 2)
+ compare(textField.topPadding, textField.Material.textFieldVerticalPadding + textField.topInset)
+ }
+
+ function test_flatButton() {
+ let button = createTemporaryObject(buttonComponent, testCase, { flat: true })
+ verify(button)
+ // A flat button should be transparent by default.
+ compare(button.background.color, "#00000000")
+
+ // However, if a background color is explicitly specified, it should be respected.
+ button.Material.background = "#ff6347"
+ compare(button.background.color, "#ff6347")
+
+ // The text should be legible when it's highlighted.
+ button.Material.background = undefined
+ button.highlighted = true
+ compare(button.background.color.a, 0.25)
+ compare(button.contentItem.color.a, 1)
+ }
+
+ Component {
+ id: itemPropagationToComboBoxPopup
+
+ Rectangle {
+ id: rectangle
+ objectName: "rectangle"
+ anchors.fill: parent
+ color: "#444"
+
+ property alias comboBox: comboBox
+
+ Material.theme: Material.Dark
+
+ ComboBox {
+ id: comboBox
+ objectName: "comboBox"
+ model: 3
+ popup.background.objectName: "comboBoxPopupBackground"
+ popup.contentItem.objectName: "comboBoxPopupContentItem"
+ }
+ }
+ }
+
+ function test_itemPropagationToComboBoxPopup() {
+ let rect = createTemporaryObject(itemPropagationToComboBoxPopup, testCase)
+ verify(rect)
+
+ let comboBox = rect.comboBox
+ mouseClick(comboBox)
+ let comboBoxPopup = comboBox.popup
+ tryCompare(comboBoxPopup, "opened", true)
+ compare(comboBoxPopup.background.color, comboBoxPopup.Material.dialogColor)
+ }
+
+ function test_nullTextAreaBackground() {
+ let textArea = createTemporaryObject(textAreaComponent, testCase)
+ verify(textArea)
+ // Store the placeholder text item before we set the background to null,
+ // because it will be unparented at that point.
+ let placeholderTextItem = textArea.children[0]
+ verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText)
+ // Assigning null to the background shouldn't cause any warnings,
+ // it should just hide the placeholder text item, since it has nothing to anchor to.
+ // Note that we can't use the properties argument of createTemporaryObject due to QTBUG-117201.
+ textArea.background = null
+ verify(!placeholderTextItem.visible)
+ }
+
+ Component {
+ id: textFieldAndButtonComponent
+
+ FocusScope {
+ focus: true
+ anchors.fill: parent
+
+ property alias textField: textField
+ property alias button: button
+
+ Keys.onEscapePressed: function (event) {
+ event.accepted = true
+ button.forceActiveFocus()
+ textField.forceActiveFocus()
+ }
+
+ TextField {
+ id: textField
+ focus: true
+ placeholderText: "placeholderText"
+ anchors.fill: parent
+ }
+
+ Button {
+ id: button
+ anchors.right: parent.right
+ text: focus ? "focus" : "no focus"
+ }
+ }
+ }
+
+ // QTBUG-118889
+ function test_focusChanges() {
+ let focusScope = createTemporaryObject(textFieldAndButtonComponent, testCase)
+ verify(focusScope)
+ testCase.Window.window.requestActivate()
+ tryCompare(testCase.Window.window, "active", true)
+
+ let textField = focusScope.textField
+ verify(textField.activeFocus)
+ let textFieldActiveFocusSpy = signalSpyComponent.createObject(textField,
+ { target: textField, signalName: "activeFocusChanged" })
+ verify(textFieldActiveFocusSpy.valid)
+
+ let button = focusScope.button
+ let buttonActiveFocusSpy = signalSpyComponent.createObject(button,
+ { target: button, signalName: "activeFocusChanged" })
+ verify(buttonActiveFocusSpy.valid)
+
+ // Shouldn't assert after quickly switching focus.
+ keyClick(Qt.Key_Escape)
+ // true => false => true.
+ compare(textFieldActiveFocusSpy.count, 2)
+ // false => true => false.
+ compare(buttonActiveFocusSpy.count, 2)
+ }
+
+ Component {
+ id: childWindowComponent
+
+ ApplicationWindow {
+ objectName: "parentWindow"
+ property alias childWindow: childWindow
+
+ Material.theme: Material.Dark
+ Material.primary: Material.Brown
+ Material.accent: Material.Green
+ Material.background: Material.Yellow
+ Material.foreground: Material.Grey
+
+ ApplicationWindow {
+ id: childWindow
+ objectName: "childWindow"
+ }
+ }
+ }
+
+ function test_windowBackgroundColorPropagation() {
+ let parentWindow = createTemporaryObject(childWindowComponent, testCase)
+ verify(parentWindow)
+
+ let childWindow = parentWindow.childWindow
+ compare(childWindow.Material.theme, Material.Dark)
+ }
+
+ Component {
+ id: themePropagationWithBehaviorComponent
+
+ ApplicationWindow {
+ width: 200
+ height: 200
+ visible: true
+
+ Material.theme: Material.Dark
+
+ property alias listView: listView
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+ header: Text {
+ text: `Material.theme for header is ${Material.theme} - should be 1`
+
+ Rectangle {
+ anchors.fill: parent
+ z: -1
+ }
+
+ Material.elevation: 6
+ // Having this would break the theme (QTBUG-122783)
+ Behavior on Material.elevation {}
+ }
+ }
+ }
+ }
+
+ function test_themePropagationWithBehavior() {
+ let window = createTemporaryObject(themePropagationWithBehaviorComponent, testCase)
+ verify(window)
+
+ let headerItem = window.listView.headerItem
+ compare(headerItem.Material.theme, Material.Dark)
+ }
}
diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp b/tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp
index 3594f4b1b0..783c5499c8 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp
+++ b/tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(tst_qquickmaterialstyle)
diff --git a/tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt b/tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt
index 36c518aa9c..21765b854d 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmaterialstyleconf/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickmaterialstyleconf LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickmaterialstyleconf/data/applicationwindow.qml b/tests/auto/quickcontrols/qquickmaterialstyleconf/data/applicationwindow.qml
index 40a384c42f..cfd6ccf682 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyleconf/data/applicationwindow.qml
+++ b/tests/auto/quickcontrols/qquickmaterialstyleconf/data/applicationwindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmaterialstyleconf/qtquickcontrols2.conf b/tests/auto/quickcontrols/qquickmaterialstyleconf/qtquickcontrols2.conf
index 30cee878be..af51b66f05 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyleconf/qtquickcontrols2.conf
+++ b/tests/auto/quickcontrols/qquickmaterialstyleconf/qtquickcontrols2.conf
@@ -4,7 +4,5 @@ Style=Material
[Material]
Background=#444444
Foreground=Red
+Font\Family=Courier
Font\PixelSize=22
-
-[Material\Font]
-Family=Courier
diff --git a/tests/auto/quickcontrols/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp b/tests/auto/quickcontrols/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp
index cec4149149..fa4de03e51 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp
+++ b/tests/auto/quickcontrols/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQuick/private/qquickitem_p.h>
diff --git a/tests/auto/quickcontrols/qquickmenu/CMakeLists.txt b/tests/auto/quickcontrols/qquickmenu/CMakeLists.txt
index 0277c88446..92bb70af07 100644
--- a/tests/auto/quickcontrols/qquickmenu/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmenu/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickmenu LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickmenu/data/actionShortcuts.qml b/tests/auto/quickcontrols/qquickmenu/data/actionShortcuts.qml
index 3d839b98d4..0fbc813250 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/actionShortcuts.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/actionShortcuts.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/actions.qml b/tests/auto/quickcontrols/qquickmenu/data/actions.qml
index 0c4449d888..6420531c12 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/actions.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/actions.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/addItem.qml b/tests/auto/quickcontrols/qquickmenu/data/addItem.qml
index 5678210e25..db9fdccd36 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/addItem.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/addItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/applicationWindowScrollable.qml b/tests/auto/quickcontrols/qquickmenu/data/applicationWindowScrollable.qml
index eb8140eea5..3c95846d21 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/applicationWindowScrollable.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/applicationWindowScrollable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/applicationwindow.qml b/tests/auto/quickcontrols/qquickmenu/data/applicationwindow.qml
index 1b42ee6965..253e265a81 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/applicationwindow.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/applicationwindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/delegateFromSeparateComponent.qml b/tests/auto/quickcontrols/qquickmenu/data/delegateFromSeparateComponent.qml
index 44a7d8d54a..e0009bf94e 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/delegateFromSeparateComponent.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/delegateFromSeparateComponent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/disableWhenTriggered.qml b/tests/auto/quickcontrols/qquickmenu/data/disableWhenTriggered.qml
index e59026677a..b745bcea11 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/disableWhenTriggered.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/disableWhenTriggered.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/disabledMenuItemKeyNavigation.qml b/tests/auto/quickcontrols/qquickmenu/data/disabledMenuItemKeyNavigation.qml
index c9c893bb9c..827729370f 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/disabledMenuItemKeyNavigation.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/disabledMenuItemKeyNavigation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/giveMenuItemFocusOnButtonPress.qml b/tests/auto/quickcontrols/qquickmenu/data/giveMenuItemFocusOnButtonPress.qml
index 1db3e351bc..c4e8df236a 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/giveMenuItemFocusOnButtonPress.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/giveMenuItemFocusOnButtonPress.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/invalidUrlInImgTag.qml b/tests/auto/quickcontrols/qquickmenu/data/invalidUrlInImgTag.qml
new file mode 100644
index 0000000000..0bc6c92fd6
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickmenu/data/invalidUrlInImgTag.qml
@@ -0,0 +1,14 @@
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 100
+ height: 100
+ property alias menu: menu
+ Menu {
+ id: menu
+ MenuItem{
+ text: "<img />"
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickmenu/data/menuItemWidths.qml b/tests/auto/quickcontrols/qquickmenu/data/menuItemWidths.qml
index ff99abd399..ab8cce3833 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/menuItemWidths.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/menuItemWidths.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/menuSeparator.qml b/tests/auto/quickcontrols/qquickmenu/data/menuSeparator.qml
index b13cd534fd..570dcb3f4d 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/menuSeparator.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/menuSeparator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml b/tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml
index 8929b00275..3e072e130f 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/mnemonics.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/order.qml b/tests/auto/quickcontrols/qquickmenu/data/order.qml
index 185c9e45c3..4e3ecb7b46 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/order.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/order.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/popup.qml b/tests/auto/quickcontrols/qquickmenu/data/popup.qml
index 8201c9e03f..b9e0fd136b 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/popup.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/popup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/pressAndHold.qml b/tests/auto/quickcontrols/qquickmenu/data/pressAndHold.qml
index ac0f394604..5dde687181 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/pressAndHold.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/pressAndHold.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/removeTakeItem.qml b/tests/auto/quickcontrols/qquickmenu/data/removeTakeItem.qml
index aa321cb3a9..8d19f9df99 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/removeTakeItem.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/removeTakeItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/repeater.qml b/tests/auto/quickcontrols/qquickmenu/data/repeater.qml
index 3c056a1d38..0eddd9f868 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/repeater.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/repeater.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml b/tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml
new file mode 100644
index 0000000000..c4f4906123
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickmenu/data/scrollableWithFixedHeight.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Window
+
+Window {
+ width: 300
+ height: 300
+
+ property alias menu: menu
+
+ Menu {
+ id: menu
+ anchors.centerIn: parent
+ height: 100
+ visible: true
+ Repeater {
+ model: 10
+ delegate: MenuItem {
+ objectName: text
+ text: (index + 1)
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickmenu/data/scrollableWithPadding.qml b/tests/auto/quickcontrols/qquickmenu/data/scrollableWithPadding.qml
index 842cb33bfd..9945ee8128 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/scrollableWithPadding.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/scrollableWithPadding.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/subMenuDisabled.qml b/tests/auto/quickcontrols/qquickmenu/data/subMenuDisabled.qml
index 4d5db0bd67..abb29579a9 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/subMenuDisabled.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/subMenuDisabled.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/subMenus.qml b/tests/auto/quickcontrols/qquickmenu/data/subMenus.qml
index 280fd404e8..24d6d9396e 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/subMenus.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/subMenus.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/data/windowScrollable.qml b/tests/auto/quickcontrols/qquickmenu/data/windowScrollable.qml
index 97a06da63d..b927896741 100644
--- a/tests/auto/quickcontrols/qquickmenu/data/windowScrollable.qml
+++ b/tests/auto/quickcontrols/qquickmenu/data/windowScrollable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
index ee7ec2c4d3..a80aec5ca1 100644
--- a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
+++ b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
@@ -86,6 +86,7 @@ private slots:
void giveMenuItemFocusOnButtonPress();
void customMenuCullItems();
void customMenuUseRepeaterAsTheContentItem();
+ void invalidUrlInImgTag();
private:
static bool hasWindowActivation();
@@ -1787,6 +1788,7 @@ void tst_QQuickMenu::scrollable_data()
QTest::addRow("Window") << QString::fromLatin1("windowScrollable.qml");
QTest::addRow("ApplicationWindow") << QString::fromLatin1("applicationWindowScrollable.qml");
QTest::addRow("WithPadding") << QString::fromLatin1("scrollableWithPadding.qml");
+ QTest::addRow("FixedHeight") << QString::fromLatin1("scrollableWithFixedHeight.qml");
}
void tst_QQuickMenu::scrollable()
@@ -2115,6 +2117,27 @@ void tst_QQuickMenu::customMenuUseRepeaterAsTheContentItem()
QTRY_VERIFY(!QQuickItemPrivate::get(menuItemLast)->culled);
}
+// QTBUG-116672 - Application loads menu containing invalid styled text
+// (img tag) without crash
+void tst_QQuickMenu::invalidUrlInImgTag()
+{
+ QTest::ignoreMessage(QtWarningMsg, "StyledText - Invalid base url in img tag");
+
+ QQuickControlsApplicationHelper helper(this, QLatin1String("invalidUrlInImgTag.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->isVisible());
+
+ QQuickMenuItem *menuItemFirst = qobject_cast<QQuickMenuItem *>(menu->itemAt(0));
+ QVERIFY(menuItemFirst);
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickMenu)
#include "tst_qquickmenu.moc"
diff --git a/tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt b/tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt
index cee75be2f9..0a1c1ccddd 100644
--- a/tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmenubar/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickmenubar LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickmenubar/data/checkHighlightWhenDismissed.qml b/tests/auto/quickcontrols/qquickmenubar/data/checkHighlightWhenDismissed.qml
index 2f8132e2fc..92eae3ef4a 100644
--- a/tests/auto/quickcontrols/qquickmenubar/data/checkHighlightWhenDismissed.qml
+++ b/tests/auto/quickcontrols/qquickmenubar/data/checkHighlightWhenDismissed.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenubar/data/delegateFromSeparateComponent.qml b/tests/auto/quickcontrols/qquickmenubar/data/delegateFromSeparateComponent.qml
index ac03210fa0..bff2d40410 100644
--- a/tests/auto/quickcontrols/qquickmenubar/data/delegateFromSeparateComponent.qml
+++ b/tests/auto/quickcontrols/qquickmenubar/data/delegateFromSeparateComponent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenubar/data/empty.qml b/tests/auto/quickcontrols/qquickmenubar/data/empty.qml
index 870c3a0bc4..34c0cbc9d8 100644
--- a/tests/auto/quickcontrols/qquickmenubar/data/empty.qml
+++ b/tests/auto/quickcontrols/qquickmenubar/data/empty.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenubar/data/hoverAfterClosingWithEscape.qml b/tests/auto/quickcontrols/qquickmenubar/data/hoverAfterClosingWithEscape.qml
new file mode 100644
index 0000000000..998a6387dd
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickmenubar/data/hoverAfterClosingWithEscape.qml
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 300
+ height: 300
+
+ MenuBar {
+ objectName: "menuBar"
+
+ Menu {
+ title: qsTr("File")
+
+ MenuItem {
+ text: qsTr("New")
+ }
+ }
+
+ Menu {
+ title: qsTr("Edit")
+
+ MenuItem {
+ text: qsTr("Undo")
+ }
+ }
+
+ Menu {
+ title: qsTr("View")
+
+ MenuItem {
+ text: qsTr("Center")
+ }
+ }
+
+ Menu {
+ title: qsTr("Tools")
+
+ MenuItem {
+ text: qsTr("Options")
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickmenubar/data/menubar.qml b/tests/auto/quickcontrols/qquickmenubar/data/menubar.qml
index 9edb7606ba..cf8958e4c4 100644
--- a/tests/auto/quickcontrols/qquickmenubar/data/menubar.qml
+++ b/tests/auto/quickcontrols/qquickmenubar/data/menubar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickmenubar/data/touch.qml b/tests/auto/quickcontrols/qquickmenubar/data/touch.qml
new file mode 100644
index 0000000000..5d54553307
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickmenubar/data/touch.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ header: MenuBar {
+ Menu {
+ title: "&File"
+
+ MenuItem {
+ text: "&Open..."
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
index 95006ba77c..63dec4dc6a 100644
--- a/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
+++ b/tests/auto/quickcontrols/qquickmenubar/tst_qquickmenubar.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGui/qpa/qplatformintegration.h>
#include <QtGui/qpa/qplatformtheme.h>
@@ -14,9 +14,11 @@
#include <QtQuickTemplates2/private/qquickmenubar_p.h>
#include <QtQuickTemplates2/private/qquickmenubaritem_p.h>
#include <QtQuickTemplates2/private/qquickmenuitem_p.h>
+#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
using namespace QQuickVisualTestUtils;
+using namespace QQuickControlsTestUtils;
class tst_qquickmenubar : public QQmlDataTest
{
@@ -28,19 +30,29 @@ public:
private slots:
void delegate();
void mouse();
+ void touch();
void keys();
void mnemonics();
void altNavigation();
void addRemove();
void checkHighlightWhenMenuDismissed();
+ void hoverAfterClosingWithEscape();
private:
static bool hasWindowActivation();
+
+ QScopedPointer<QPointingDevice> touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
+QPoint itemSceneCenter(const QQuickItem *item)
+{
+ return item->mapToScene(QPointF(item->width() / 2, item->height() / 2)).toPoint();
+}
+
tst_qquickmenubar::tst_qquickmenubar()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
+ qputenv("QML_NO_TOUCH_COMPRESSION", "1");
}
bool tst_qquickmenubar::hasWindowActivation()
@@ -95,7 +107,7 @@ void tst_qquickmenubar::mouse()
QVERIFY(fileMenuBarItem && editMenuBarItem && viewMenuBarItem && helpMenuBarItem);
// highlight a menubar item
- QTest::mouseMove(window.data(), fileMenuBarItem->mapToScene(QPointF(fileMenuBarItem->width() / 2, fileMenuBarItem->height() / 2)).toPoint());
+ QTest::mouseMove(window.data(), itemSceneCenter(fileMenuBarItem));
#ifndef Q_OS_ANDROID
// Android theme does not use hover effects, so moving the mouse would not
// highlight an item
@@ -104,7 +116,7 @@ void tst_qquickmenubar::mouse()
QVERIFY(!fileMenuBarMenu->isVisible());
// highlight another menubar item
- QTest::mouseMove(window.data(), editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint());
+ QTest::mouseMove(window.data(), itemSceneCenter(editMenuBarItem));
#ifndef Q_OS_ANDROID
// Android theme does not use hover effects, so moving the mouse would not
// highlight an item
@@ -114,26 +126,28 @@ void tst_qquickmenubar::mouse()
QVERIFY(!fileMenuBarMenu->isVisible());
QVERIFY(!editMenuBarMenu->isVisible());
- // trigger a menubar item to open a menu
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint());
+ // trigger a menubar item to open a menu - it should open on press
+ QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(editMenuBarItem));
QVERIFY(editMenuBarItem->isHighlighted());
QVERIFY(editMenuBarMenu->isVisible());
QTRY_VERIFY(editMenuBarMenu->isOpened());
+ QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(editMenuBarItem));
- // re-trigger a menubar item to hide the menu
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint());
+ // re-trigger a menubar item to hide the menu - it should close on press
+ QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(editMenuBarItem));
QVERIFY(editMenuBarItem->isHighlighted());
QVERIFY(editMenuBarItem->hasActiveFocus());
QTRY_VERIFY(!editMenuBarMenu->isVisible());
+ QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(editMenuBarItem));
// re-trigger a menubar item to show the menu again
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint());
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(editMenuBarItem));
QVERIFY(editMenuBarItem->isHighlighted());
QVERIFY(editMenuBarMenu->isVisible());
QTRY_VERIFY(editMenuBarMenu->isOpened());
// highlight another menubar item to open another menu
- QTest::mouseMove(window.data(), helpMenuBarItem->mapToScene(QPointF(helpMenuBarItem->width() / 2, helpMenuBarItem->height() / 2)).toPoint());
+ QTest::mouseMove(window.data(), itemSceneCenter(helpMenuBarItem));
#ifdef Q_OS_ANDROID
// Android theme does not use hover effects, so moving the mouse would not
// highlight an item. Add a mouse click to change menubar item selection.
@@ -159,7 +173,7 @@ void tst_qquickmenubar::mouse()
QTRY_VERIFY(!helpMenuBarMenu->isVisible());
// highlight a menubar item
- QTest::mouseMove(window.data(), editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint());
+ QTest::mouseMove(window.data(), itemSceneCenter(editMenuBarItem));
#ifndef Q_OS_ANDROID
// Android theme does not use hover effects, so moving the mouse would not
// highlight an item
@@ -170,7 +184,7 @@ void tst_qquickmenubar::mouse()
QVERIFY(!helpMenuBarMenu->isVisible());
// trigger a menubar item to open a menu
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, viewMenuBarItem->mapToScene(QPointF(viewMenuBarItem->width() / 2, viewMenuBarItem->height() / 2)).toPoint());
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(viewMenuBarItem));
QVERIFY(!editMenuBarItem->isHighlighted());
QVERIFY(viewMenuBarItem->isHighlighted());
QVERIFY(viewMenuBarMenu->isVisible());
@@ -181,7 +195,7 @@ void tst_qquickmenubar::mouse()
QVERIFY(alignmentSubMenuItem);
QQuickMenu *alignmentSubMenu = alignmentSubMenuItem->subMenu();
QVERIFY(alignmentSubMenu);
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, alignmentSubMenuItem->mapToScene(QPointF(alignmentSubMenuItem->width() / 2, alignmentSubMenuItem->height() / 2)).toPoint());
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(alignmentSubMenuItem));
#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.
@@ -196,7 +210,7 @@ void tst_qquickmenubar::mouse()
QVERIFY(verticalSubMenuItem);
QQuickMenu *verticalSubMenu = verticalSubMenuItem->subMenu();
QVERIFY(verticalSubMenu);
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, verticalSubMenuItem->mapToScene(QPointF(verticalSubMenuItem->width() / 2, verticalSubMenuItem->height() / 2)).toPoint());
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(verticalSubMenuItem));
#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.
@@ -210,7 +224,7 @@ void tst_qquickmenubar::mouse()
// trigger a menu item to close the whole chain of menus
QQuickMenuItem *centerMenuItem = qobject_cast<QQuickMenuItem *>(verticalSubMenu->itemAt(1));
QVERIFY(centerMenuItem);
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, centerMenuItem->mapToScene(QPointF(centerMenuItem->width() / 2, centerMenuItem->height() / 2)).toPoint());
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(centerMenuItem));
QVERIFY(!viewMenuBarItem->isHighlighted());
QTRY_VERIFY(!viewMenuBarMenu->isVisible());
QTRY_VERIFY(!alignmentSubMenu->isVisible());
@@ -225,11 +239,11 @@ void tst_qquickmenubar::mouse()
#endif
// re-open the chain of menus
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, viewMenuBarItem->mapToScene(QPointF(viewMenuBarItem->width() / 2, viewMenuBarItem->height() / 2)).toPoint());
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(viewMenuBarItem));
QTRY_VERIFY(viewMenuBarMenu->isOpened());
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, alignmentSubMenuItem->mapToScene(QPointF(alignmentSubMenuItem->width() / 2, alignmentSubMenuItem->height() / 2)).toPoint());
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(alignmentSubMenuItem));
QTRY_VERIFY(alignmentSubMenu->isOpened());
- QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, verticalSubMenuItem->mapToScene(QPointF(verticalSubMenuItem->width() / 2, verticalSubMenuItem->height() / 2)).toPoint());
+ QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, itemSceneCenter(verticalSubMenuItem));
QTRY_VERIFY(verticalSubMenu->isOpened());
// click outside to close the whole chain of menus
@@ -240,6 +254,37 @@ void tst_qquickmenubar::mouse()
QTRY_VERIFY(!verticalSubMenu->isVisible());
}
+// Not sure how relevant MenuBar is for touch, but this test is here to make
+// sure that only release events cause the menu to open, as:
+// - That is how it has always behaved, so maintain that behavior.
+// - It's what happens with e.g. overflow menus on Android.
+void tst_qquickmenubar::touch()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("touch.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ centerOnScreen(helper.window);
+ helper.window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(helper.window));
+
+ QQuickMenuBar *menuBar = helper.window->property("header").value<QQuickMenuBar *>();
+ QVERIFY(menuBar);
+
+ QQuickMenu *fileMenuBarMenu = menuBar->menuAt(0);
+ QVERIFY(fileMenuBarMenu);
+
+ QQuickMenuBarItem *fileMenuBarItem = qobject_cast<QQuickMenuBarItem *>(fileMenuBarMenu->parentItem());
+ QVERIFY(fileMenuBarItem);
+
+ // Trigger a menubar item to open a menu - it should only open on release.
+ QTest::touchEvent(helper.window, touchScreen.data()).press(0, itemSceneCenter(fileMenuBarItem));
+ QVERIFY(!fileMenuBarItem->isHighlighted());
+ QVERIFY(!fileMenuBarMenu->isVisible());
+ QTest::touchEvent(helper.window, touchScreen.data()).release(0, itemSceneCenter(fileMenuBarItem));
+ QVERIFY(fileMenuBarItem->isHighlighted());
+ QVERIFY(fileMenuBarMenu->isVisible());
+ QTRY_VERIFY(fileMenuBarMenu->isOpened());
+}
+
void tst_qquickmenubar::keys()
{
if (!hasWindowActivation())
@@ -713,32 +758,67 @@ void tst_qquickmenubar::checkHighlightWhenMenuDismissed()
QPointF(staticMenuBarItem->width() / 2, staticMenuBarItem->height() / 2)).toPoint());
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier,
staticMenuBarItem->mapToScene(QPointF(staticMenuBarItem->width() / 2, staticMenuBarItem->height() / 2)).toPoint());
- QCOMPARE(staticMenuBarItem->isHighlighted(), true);
- QCOMPARE(staticMenu->isVisible(), true);
- QTRY_COMPARE(staticMenu->isOpened(), true);
-
+ QVERIFY(staticMenuBarItem->isHighlighted());
+ QVERIFY(staticMenu->isVisible());
+ QTRY_VERIFY(staticMenu->isOpened());
// click a menu item to dismiss the menu and unhighlight the static MenuBarItem
QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(staticMenu->itemAt(0));
QVERIFY(menuItem);
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier,
menuItem->mapToScene(QPointF(menuItem->width() / 2, menuItem->height() / 2)).toPoint());
- QCOMPARE(staticMenuBarItem->isHighlighted(), false);
+ QVERIFY(!staticMenuBarItem->isHighlighted());
+ // wait for the menu to be fully gone so that it doesn't interfere with the next test
+ QTRY_VERIFY(!staticMenu->isVisible());
// highlight the dynamic MenuBarItem and open the menu
QTest::mouseMove(window.data(), dynamicMenuBarItem->mapToScene(
QPointF(dynamicMenuBarItem->width() / 2, dynamicMenuBarItem->height() / 2)).toPoint());
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier,
dynamicMenuBarItem->mapToScene(QPointF(dynamicMenuBarItem->width() / 2, dynamicMenuBarItem->height() / 2)).toPoint());
- QCOMPARE(dynamicMenuBarItem->isHighlighted(), true);
- QCOMPARE(dynamicMenu->isVisible(), true);
- QTRY_COMPARE(dynamicMenu->isOpened(), true);
+ QVERIFY(dynamicMenuBarItem->isHighlighted());
+ QVERIFY(dynamicMenu->isVisible());
+ QTRY_VERIFY(dynamicMenu->isOpened());
// click a menu item to dismiss the menu and unhighlight the dynamic MenuBarItem
menuItem = qobject_cast<QQuickMenuItem *>(dynamicMenu->itemAt(0));
QVERIFY(menuItem);
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier,
menuItem->mapToScene(QPointF(menuItem->width() / 2, menuItem->height() / 2)).toPoint());
- QCOMPARE(dynamicMenuBarItem->isHighlighted(), false);
+ QVERIFY(!dynamicMenuBarItem->isHighlighted());
+}
+
+void tst_qquickmenubar::hoverAfterClosingWithEscape()
+{
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QSKIP("Mouse highlight not functional on offscreen/minimal platforms");
+
+ QQuickControlsApplicationHelper helper(this, QLatin1String("hoverAfterClosingWithEscape.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickMenuBar *menuBar = window->findChild<QQuickMenuBar *>("menuBar");
+ QVERIFY(menuBar);
+
+ // Open the first menu by clicking on the first menu bar item.
+ auto *firstMenuBarItem(qobject_cast<QQuickMenuBarItem *>(menuBar->itemAt(0)));
+ QVERIFY(clickButton(firstMenuBarItem));
+ QQuickMenu *firstMenu = menuBar->menuAt(0);
+ QVERIFY(firstMenu);
+ QTRY_VERIFY(firstMenu->isOpened());
+
+ // Close it with the escape key.
+ QTest::keyClick(window, Qt::Key_Escape);
+ QTRY_VERIFY(!firstMenu->isVisible());
+
+ // Hover over the second menu bar item; it shouldn't cause its menu to open.
+ auto *secondMenuBarItem(qobject_cast<QQuickMenuBarItem *>(menuBar->itemAt(1)));
+ QTest::mouseMove(window, mapCenterToWindow(secondMenuBarItem));
+ QQuickMenu *secondMenu = menuBar->menuAt(1);
+ QVERIFY(secondMenu);
+ QVERIFY(!secondMenu->isVisible());
}
QTEST_QUICKCONTROLS_MAIN(tst_qquickmenubar)
diff --git a/tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt b/tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt
index 0523912506..003bdd7725 100644
--- a/tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickninepatchimage/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickninepatchimage LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
@@ -16,11 +16,7 @@ endif()
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/data/*.qml)
-list(APPEND test_data ${test_data_glob})
-file(GLOB_RECURSE test_data_glob
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/data/*.png)
+ ${CMAKE_CURRENT_SOURCE_DIR}/data/*)
list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_qquickninepatchimage
diff --git a/tests/auto/quickcontrols/qquickninepatchimage/data/logo.pkm b/tests/auto/quickcontrols/qquickninepatchimage/data/logo.pkm
new file mode 100644
index 0000000000..c0987c5c36
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickninepatchimage/data/logo.pkm
Binary files differ
diff --git a/tests/auto/quickcontrols/qquickninepatchimage/data/o1_bc1.ktx b/tests/auto/quickcontrols/qquickninepatchimage/data/o1_bc1.ktx
new file mode 100644
index 0000000000..d61194a745
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickninepatchimage/data/o1_bc1.ktx
Binary files differ
diff --git a/tests/auto/quickcontrols/qquickninepatchimage/data/qt4.astc b/tests/auto/quickcontrols/qquickninepatchimage/data/qt4.astc
new file mode 100644
index 0000000000..7f7a3f4739
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickninepatchimage/data/qt4.astc
Binary files differ
diff --git a/tests/auto/quickcontrols/qquickninepatchimage/tst_qquickninepatchimage.cpp b/tests/auto/quickcontrols/qquickninepatchimage/tst_qquickninepatchimage.cpp
index c1fb1f7264..7e0e974f23 100644
--- a/tests/auto/quickcontrols/qquickninepatchimage/tst_qquickninepatchimage.cpp
+++ b/tests/auto/quickcontrols/qquickninepatchimage/tst_qquickninepatchimage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/qsignalspy.h>
@@ -11,8 +11,10 @@
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitemgrabresult.h>
#include <QtQuick/private/qquickimage_p.h>
+#include <QtQuick/private/qquickimage_p_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
+#include <QtGui/private/qrhi_p.h>
using namespace QQuickVisualTestUtils;
@@ -32,6 +34,8 @@ private slots:
void inset();
void implicitSize_data();
void implicitSize();
+ void hwCompressedImages_data();
+ void hwCompressedImages();
};
static QImage grabItemToImage(QQuickItem *item)
@@ -91,20 +95,25 @@ void tst_qquickninepatchimage::ninePatch()
// Generate an image to compare against the actual 9-patch image.
QImage generatedImage(size * dpr, ninePatchImageGrab.format());
+ generatedImage.setDevicePixelRatio(dpr);
generatedImage.fill(Qt::red);
QImage blueRect(4 * dpr, 4 * dpr, ninePatchImageGrab.format());
+ blueRect.setDevicePixelRatio(dpr);
blueRect.fill(Qt::blue);
+ const QSizeF generatedPaintedSize = generatedImage.deviceIndependentSize();
+ const QSizeF blueRectPaintedSize = blueRect.deviceIndependentSize();
+
QPainter painter(&generatedImage);
// Top-left
painter.drawImage(0, 0, blueRect);
// Top-right
- painter.drawImage(generatedImage.width() - blueRect.width(), 0, blueRect);
+ painter.drawImage(generatedPaintedSize.width() - blueRectPaintedSize.width(), 0, blueRect);
// Bottom-right
- painter.drawImage(generatedImage.width() - blueRect.width(), generatedImage.height() - blueRect.height(), blueRect);
+ painter.drawImage(generatedPaintedSize.width() - blueRectPaintedSize.width(), generatedPaintedSize.height() - blueRectPaintedSize.height(), blueRect);
// Bottom-left
- painter.drawImage(0, generatedImage.height() - blueRect.height(), blueRect);
+ painter.drawImage(0, generatedPaintedSize.height() - blueRectPaintedSize.height(), blueRect);
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
@@ -230,6 +239,61 @@ void tst_qquickninepatchimage::implicitSize()
QCOMPARE(ninePatchImage->implicitHeight(), implicitSize.height());
}
+void tst_qquickninepatchimage::hwCompressedImages_data()
+{
+ QTest::addColumn<int>("dpr");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QSize>("size");
+ QTest::addColumn<QRhiTexture::Format>("format");
+
+ const struct TestFile {
+ QString name;
+ QSize size;
+ QRhiTexture::Format format;
+ } testFiles [] = {
+ { "o1_bc1.ktx", QSize(64, 64), QRhiTexture::BC1 },
+ { "logo.pkm", QSize(256, 256), QRhiTexture::ETC2_RGB8 },
+ { "qt4.astc", QSize(250, 200), QRhiTexture::ASTC_8x8 }
+ };
+
+ for (const TestFile &file : testFiles) {
+ for (int dpr = 1; dpr <= 4; ++dpr)
+ QTest::newRow(qPrintable(QString::fromLatin1("%1 DPR=%2").arg(file.name).arg(dpr))) << dpr << file.name << file.size << file.format;
+ }
+}
+
+void tst_qquickninepatchimage::hwCompressedImages()
+{
+ QFETCH(int, dpr);
+ QFETCH(QString, file);
+ QFETCH(QSize, size);
+ QFETCH(QRhiTexture::Format, format);
+
+ QHighDpiScaling::setGlobalFactor(dpr);
+
+ QQuickView view(testFileUrl("ninepatchimage.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (!QSGRendererInterface::isApiRhiBased(view.rendererInterface()->graphicsApi()))
+ QSKIP("Skipping due to using software backend");
+
+ QRhi *rhi = static_cast<QRhi *>(view.rendererInterface()->getResource(&view, QSGRendererInterface::RhiResource));
+ if (!rhi->isTextureFormatSupported(format))
+ QSKIP(qPrintable(QString::fromLatin1("%1 not supported, skip").arg(format)));
+
+ QQuickImage *ninePatchImage = qobject_cast<QQuickImage *>(view.rootObject());
+ QVERIFY(ninePatchImage);
+ ninePatchImage->setSource(testFileUrl(file));
+ ninePatchImage->setSize(size);
+ QSignalSpy spy(&view, SIGNAL(afterSynchronizing()));
+ QTRY_VERIFY(spy.size() >= 1);
+
+ QQuickImagePrivate *ninePatchImagePrivate = static_cast<QQuickImagePrivate *>(QQuickItemPrivate::get(ninePatchImage));
+ QVERIFY(ninePatchImagePrivate->paintNode);
+}
+
QTEST_MAIN(tst_qquickninepatchimage)
#include "tst_qquickninepatchimage.moc"
diff --git a/tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt b/tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt
new file mode 100644
index 0000000000..e5d9da4dc9
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickoverlay/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickpopup LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+#####################################################################
+## tst_qquickoverlay Test:
+#####################################################################
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qquickoverlay
+ SOURCES
+ tst_qquickoverlay.cpp
+ DEFINES
+ QQC2_IMPORT_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols"
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+ Qt::QuickPrivate
+ Qt::QuickTemplates2
+ Qt::QuickTemplates2Private
+ Qt::TestPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qquickoverlay CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qquickoverlay CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
diff --git a/tests/auto/quickcontrols/qquickoverlay/tst_qquickoverlay.cpp b/tests/auto/quickcontrols/qquickoverlay/tst_qquickoverlay.cpp
new file mode 100644
index 0000000000..a2ab819a7a
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickoverlay/tst_qquickoverlay.cpp
@@ -0,0 +1,223 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/qtest.h>
+#include <QtTest/qsignalspy.h>
+
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquickpointerdevicehandler_p.h>
+#include <QtQuickTemplates2/private/qquickoverlay_p.h>
+
+class tst_QQuickOverlay : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QQuickOverlay();
+
+private slots:
+ void clearGrabbers();
+ void retainOrientation();
+};
+
+class TestInputHandler : public QQuickPointerDeviceHandler
+{
+public:
+ TestInputHandler(QQuickItem * parent)
+ : QQuickPointerDeviceHandler(parent)
+ {
+ setGrabPermissions(GrabPermission::TakeOverForbidden);
+ }
+
+ bool wantsPointerEvent(QPointerEvent * event) override
+ {
+ return QQuickPointerDeviceHandler::wantsPointerEvent(event)
+ && !eligiblePoints(event).isEmpty();
+ }
+
+ bool wantsEventPoint(const QPointerEvent * event, const QEventPoint & point) override
+ {
+ bool ret = QQuickPointerDeviceHandler::wantsEventPoint(event, point);
+ return ret || (ret && event->exclusiveGrabber(point) != this);
+ }
+
+ bool grabPoints(QPointerEvent * event, const QVector<QEventPoint> & points)
+ {
+ if (points.isEmpty())
+ return false;
+
+ bool allowed = true;
+
+ for (const auto & point : points) {
+ if (event->exclusiveGrabber(point) != this && !canGrab(event, point)) {
+ allowed = false;
+ break;
+ }
+ }
+
+ if (allowed) {
+ for (auto point : points) {
+ if (event->exclusiveGrabber(point) != this)
+ event->setExclusiveGrabber(point, this);
+ point.setAccepted(true);
+ }
+ }
+
+ return allowed;
+ }
+
+ bool grabEligiblePoints(QPointerEvent * event)
+ {
+ return grabPoints(event, eligiblePoints(event));
+ }
+
+ QList<QEventPoint> eligiblePoints(QPointerEvent * event)
+ {
+ QList<QEventPoint> ret;
+
+ // elligible points should return all points which either
+ // 1. have no owner
+ // 2. are owned by us
+ // 3. we can steal
+
+ for (int i = 0; i < event->pointCount(); ++i) {
+ auto p = event->point(i);
+ auto * exclusiveGrabber = event->exclusiveGrabber(p);
+
+ if (exclusiveGrabber && exclusiveGrabber != this && !canGrab(event, p))
+ continue;
+
+ if (p.state() != QEventPoint::Released && wantsEventPoint(event, p))
+ ret << p;
+ }
+
+ return ret;
+ }
+
+ void handlePointerEventImpl(QPointerEvent * event) override
+ {
+ QQuickPointerDeviceHandler::handlePointerEventImpl(event);
+ auto grabbed = grabEligiblePoints(event);
+
+ if (!active()) {
+ setActive(grabbed);
+ return;
+ }
+
+ for (int i = 0; i < event->pointCount(); ++i) {
+ auto point = event->point(i);
+
+ if (event->exclusiveGrabber(point) == this)
+ Q_ASSERT(m_points.contains(point.id()));
+ }
+ }
+
+ void onGrabChanged(QQuickPointerHandler * grabber,
+ QPointingDevice::GrabTransition transition,
+ QPointerEvent * event,
+ QEventPoint & point) override
+ {
+ if (grabber == this) {
+ switch (transition) {
+ case QPointingDevice::GrabTransition::GrabExclusive:
+ Q_ASSERT(!m_points.contains(point.id()));
+ m_points.append(point.id());
+ break;
+
+ case QPointingDevice::GrabTransition::UngrabExclusive:
+ case QPointingDevice::GrabTransition::CancelGrabExclusive:
+ if (m_points.contains(point.id()))
+ m_points.removeOne(point.id());
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ QQuickPointerDeviceHandler::onGrabChanged(grabber, transition, event, point);
+ }
+
+ QList<int> m_points;
+};
+
+tst_QQuickOverlay::tst_QQuickOverlay() = default;
+
+void tst_QQuickOverlay::clearGrabbers()
+{
+ QQuickWindow window;
+
+ auto *overlay = QQuickOverlay::overlay(&window);
+ auto *overlayItem = new QQuickItem(overlay);
+ QVERIFY(overlayItem);
+
+ const auto size = QSize(640, 480);
+ window.resize(size);
+
+ auto *item = new QQuickItem(window.contentItem());
+ item->setSize(size);
+
+ auto testPointerhandler = TestInputHandler(item);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ auto *dev = QTest::createTouchDevice();
+
+ QTest::touchEvent(&window, dev)
+ .press(0, QPoint(10, 10));
+
+ QCOMPARE(testPointerhandler.m_points.size(), 1);
+ QCOMPARE(testPointerhandler.m_points[0], 0);
+
+ QTest::touchEvent(&window, dev)
+ .stationary(0)
+ .press(1, QPoint(20, 20));
+
+ QCOMPARE(testPointerhandler.m_points.size(), 2);
+ QCOMPARE(testPointerhandler.m_points[0], 0);
+ QCOMPARE(testPointerhandler.m_points[1], 1);
+
+ QTest::touchEvent(&window, dev)
+ .move(0, QPoint(20, 20))
+ .move(1, QPoint(30, 30));
+
+ QCOMPARE(testPointerhandler.m_points.size(), 2);
+ QCOMPARE(testPointerhandler.m_points[0], 0);
+ QCOMPARE(testPointerhandler.m_points[1], 1);
+
+ QTest::touchEvent(&window, dev)
+ .release(0, QPoint(30, 30))
+ .stationary(1);
+
+ QCOMPARE(testPointerhandler.m_points.size(), 1);
+ QCOMPARE(testPointerhandler.m_points[0], 1);
+
+ QTest::touchEvent(&window, dev)
+ .release(1, QPoint(40, 40));
+
+ QVERIFY(testPointerhandler.m_points.isEmpty());
+}
+
+void tst_QQuickOverlay::retainOrientation()
+{
+ QQuickWindow window;
+ auto *overlay = QQuickOverlay::overlay(&window);
+
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ qreal rot = 10;
+ overlay->setRotation(rot);
+ const QSizeF sz = overlay->size();
+ window.resize(window.size() + QSize(10, 10));
+ // wait for the resize event to call QQuickOverlay::updateGeometry
+ QTRY_COMPARE_NE(overlay->size(), sz);
+
+ QCOMPARE(overlay->rotation(), rot);
+}
+
+QTEST_MAIN(tst_QQuickOverlay)
+
+#include "tst_qquickoverlay.moc"
diff --git a/tests/auto/quickcontrols/qquickpopup/BLACKLIST b/tests/auto/quickcontrols/qquickpopup/BLACKLIST
index aa31440328..bd2185328f 100644
--- a/tests/auto/quickcontrols/qquickpopup/BLACKLIST
+++ b/tests/auto/quickcontrols/qquickpopup/BLACKLIST
@@ -1,5 +1,16 @@
# See qtbase/src/testlib/qtestblacklist.cpp for format
+# QTBUG-114718 (begin)
+[overlay]
+macos
+[invisibleToolTipOpen]
+macos
+[cursorShape]
+macos
+[doubleClickInMouseArea]
+macos
+# QTBUG-114718 (end)
+
[overlay]
macos # QTBUG-89938
diff --git a/tests/auto/quickcontrols/qquickpopup/CMakeLists.txt b/tests/auto/quickcontrols/qquickpopup/CMakeLists.txt
index 81eb78c906..3110e4d26b 100644
--- a/tests/auto/quickcontrols/qquickpopup/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickpopup/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickpopup LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterExit.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterExit.qml
index 359dc772f2..d918159f65 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterExit.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterExit.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml
new file mode 100644
index 0000000000..1d4dc87e41
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias popup: popup
+ property alias button: button
+
+ Button {
+ id: button
+ text: "button"
+ focus: true
+ }
+
+ Popup {
+ id: popup
+ focus: true
+ width: 100
+ height: 100
+ anchors.centerIn: Overlay.overlay
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose1.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose1.qml
index 81893bbcbd..7cbc5f479a 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose1.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose1.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose2.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose2.qml
index 6cb1c5ad07..c1b60b7282 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose2.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose3.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose3.qml
index 89165e355e..0e1e2aa703 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose3.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClose3.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClosingSeveralPopups.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClosingSeveralPopups.qml
index 3a85f7d580..5ed1c5988d 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClosingSeveralPopups.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnClosingSeveralPopups.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnDelayedEnter.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnDelayedEnter.qml
index 37a595ec25..e4b7438d2a 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnDelayedEnter.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusOnDelayedEnter.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Controls 2.12
diff --git a/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml b/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml
index d4c8b78355..f1acf42d2a 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -15,22 +15,24 @@ ApplicationWindow {
Button {
id: parentButton
text: "Parent"
+ palette.buttonText: hovered ? "tomato" : "black"
anchors.fill: parent
+ anchors.margins: 10
Popup {
id: popup
- x: 1
- y: 1
- leftPadding: 1
- rightPadding: 1
- topPadding: 1
- bottomPadding: 1
-
+ x: 10
+ y: 10
+ leftPadding: 10
+ rightPadding: 10
+ topPadding: 10
+ bottomPadding: 10
Button {
anchors.centerIn: parent
id: childButton
text: "Child"
+ palette.buttonText: hovered ? "tomato" : "black"
}
}
}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-wheel.qml b/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-wheel.qml
index 1668b042e0..eb09d7eba3 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-wheel.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-wheel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickpopup/data/applicationwindow.qml b/tests/auto/quickcontrols/qquickpopup/data/applicationwindow.qml
index 46c4dda18b..7ab22ff8bd 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/applicationwindow.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/applicationwindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/centerInOverlayWithinStackViewItem.qml b/tests/auto/quickcontrols/qquickpopup/data/centerInOverlayWithinStackViewItem.qml
index 1c96bb687c..a0a76206da 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/centerInOverlayWithinStackViewItem.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/centerInOverlayWithinStackViewItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/closeOnEscapeWithNestedPopups.qml b/tests/auto/quickcontrols/qquickpopup/data/closeOnEscapeWithNestedPopups.qml
index 6aeede9aff..5533591ac4 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/closeOnEscapeWithNestedPopups.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/closeOnEscapeWithNestedPopups.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/countChanged.qml b/tests/auto/quickcontrols/qquickpopup/data/countChanged.qml
index e95be76e56..41f306781d 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/countChanged.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/countChanged.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickpopup/data/cursor.qml b/tests/auto/quickcontrols/qquickpopup/data/cursor.qml
index 3eefa7570e..6ea4e03dac 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/cursor.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/cursor.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/destroyDuringExitTransition.qml b/tests/auto/quickcontrols/qquickpopup/data/destroyDuringExitTransition.qml
index dacffd3199..5c34f22f60 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/destroyDuringExitTransition.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/destroyDuringExitTransition.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/dialog.qml b/tests/auto/quickcontrols/qquickpopup/data/dialog.qml
index 112bfaefcd..d28e540701 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/dialog.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickpopup/data/dimmerContainmentMask.qml b/tests/auto/quickcontrols/qquickpopup/data/dimmerContainmentMask.qml
index 30a412a1cc..5f7e9afc08 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/dimmerContainmentMask.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/dimmerContainmentMask.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/disabledPalette.qml b/tests/auto/quickcontrols/qquickpopup/data/disabledPalette.qml
index 4b54b5b1fd..2af92158dc 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/disabledPalette.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/disabledPalette.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/doubleClickInMouseArea.qml b/tests/auto/quickcontrols/qquickpopup/data/doubleClickInMouseArea.qml
new file mode 100644
index 0000000000..e43a2f3160
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/doubleClickInMouseArea.qml
@@ -0,0 +1,23 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Window
+
+Rectangle {
+ width: 200; height: 200
+ color: mouseArea.pressed ? "red" : "orange"
+
+ Popup {
+ visible: true
+ closePolicy: Popup.NoAutoClose
+ width: 100
+ height: 100
+ contentItem: MouseArea {
+ id: mouseArea
+
+ anchors.fill: parent
+ }
+ background: Rectangle {
+ color: "green"
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/fadeDimmer.qml b/tests/auto/quickcontrols/qquickpopup/data/fadeDimmer.qml
new file mode 100644
index 0000000000..d5edc11466
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/fadeDimmer.qml
@@ -0,0 +1,39 @@
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ id: window
+
+ Text {text: "Hello"}
+ Popup {
+ id: popup
+ anchors.centerIn: parent
+ dim: true
+ property double dimmerOpacity: 0.5
+
+ Overlay.modeless: Rectangle {
+ color: "blue"
+ opacity: popup.dimmerOpacity
+
+ Behavior on opacity {
+ NumberAnimation { from: 0; to: popup.dimmerOpacity; duration: 250 }
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: "blue"
+ opacity: popup.dimmerOpacity
+
+ Behavior on opacity {
+ NumberAnimation { from: 0; to: popup.dimmerOpacity; duration: 250 }
+ }
+ }
+
+ enter: Transition {
+ NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 250 }
+ }
+ exit: Transition {
+ NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 250 }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/grabber.qml b/tests/auto/quickcontrols/qquickpopup/data/grabber.qml
index ec76829f4f..952212dc0b 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/grabber.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/grabber.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/mirroredCombobox.qml b/tests/auto/quickcontrols/qquickpopup/data/mirroredCombobox.qml
index ed48179bce..7449072e89 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/mirroredCombobox.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/mirroredCombobox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/modelessOnModalOnModeless.qml b/tests/auto/quickcontrols/qquickpopup/data/modelessOnModalOnModeless.qml
index 5da5a5524b..f1fe64e49a 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/modelessOnModalOnModeless.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/modelessOnModalOnModeless.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/multiplepopup.qml b/tests/auto/quickcontrols/qquickpopup/data/multiplepopup.qml
new file mode 100644
index 0000000000..395514546d
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/multiplepopup.qml
@@ -0,0 +1,82 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Window {
+ id: window
+ width: 200
+ height: 200
+ visible: false
+
+ Item {
+ id: rootItem
+ objectName: "rootItem"
+
+ width: parent.width
+ height: parent.height
+ visible: true
+ focus: true
+
+ Popup {
+ id: popup1
+ objectName: "popup1"
+
+ width: parent.width / 2
+ height: parent.height
+ focus: true
+ modal: true
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+
+ Button {
+ id: buttonPopup1
+ objectName: "button"
+
+ text: "ButtonPopup"
+ width: 20
+ visible: true
+ }
+ }
+
+ Popup {
+ id: popup2
+ objectName: "popup2"
+
+ width: parent.width / 2
+ height: parent.height
+ focus: true
+ modal: true
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+
+ TextEdit {
+ id: texteditPopup2
+ objectName: "textedit"
+
+ focus: true
+ text: "Text Edit Content"
+ visible: true
+ }
+ }
+
+ Popup {
+ id: popup3
+ objectName: "popup3"
+
+ width: parent.width / 2
+ height: parent.height
+ focus: true
+ modal: true
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+
+ Drawer {
+ id: drawerPopup3
+ objectName: "drawer"
+
+ width: parent.width / 2
+ height: parent.height
+ focus: true
+ visible: true
+ }
+ }
+ }
+}
+
diff --git a/tests/auto/quickcontrols/qquickpopup/data/nested-wheel-overlay-parent.qml b/tests/auto/quickcontrols/qquickpopup/data/nested-wheel-overlay-parent.qml
new file mode 100644
index 0000000000..06dafa112a
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/nested-wheel-overlay-parent.qml
@@ -0,0 +1,39 @@
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 100
+ height: 100
+
+ property alias _drawer: drawer
+ property alias _listView: drawer.contentItem
+ property alias _dropArea: dropArea
+
+ Drawer {
+ id: drawer
+ width: 50
+ height: parent.height
+ contentItem: ListView {
+ width: parent.width
+ height: contentHeight
+ model: 500
+ delegate: Rectangle {
+ height: 15
+ Label {
+ text: modelData
+ }
+ }
+ }
+ }
+
+ DropArea {
+ id: dropArea
+ anchors.fill: parent
+ Connections {
+ target: drawer
+ function onOpened() {
+ dropArea.parent = drawer.parent
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/nested.qml b/tests/auto/quickcontrols/qquickpopup/data/nested.qml
index d88142f661..d0257bf700 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/nested.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/nested.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/noDimmer.qml b/tests/auto/quickcontrols/qquickpopup/data/noDimmer.qml
new file mode 100644
index 0000000000..6d42dc10e8
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/noDimmer.qml
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+
+Window {
+ T.Drawer {
+ id: root
+ onModalChanged: {
+ if (!modal) {
+ open()
+ }
+ }
+ }
+ Timer {
+ interval: 100
+ running: true
+ repeat: false
+ onTriggered: root.modal = false
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/orientation.qml b/tests/auto/quickcontrols/qquickpopup/data/orientation.qml
index 198f716fc0..f83cfda74e 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/orientation.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/orientation.qml
@@ -1,29 +1,35 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
- width: 600
- height: 300
+ width: 200
+ height: 100
property alias popup: popup
Rectangle {
- width: 60
- height: 30
+ width: 100
+ height: 80
anchors.centerIn: parent
border.width: 1
Popup {
id: popup
- x: parent.width
- y: parent.height
- width: 30
- height: 60
+ width: 50
+ height: 40
+ anchors.centerIn: parent
visible: true
+ modal: false
+ dim: true
+
+ Overlay.modeless: Rectangle {
+ opacity: 0.5
+ color: "blue"
+ }
}
}
}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/releaseAfterExitTransition.qml b/tests/auto/quickcontrols/qquickpopup/data/releaseAfterExitTransition.qml
index f3a1f218c1..9ddf4577ee 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/releaseAfterExitTransition.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/releaseAfterExitTransition.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/rotatedCombobox.qml b/tests/auto/quickcontrols/qquickpopup/data/rotatedCombobox.qml
index df217be4b7..16cce45263 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/rotatedCombobox.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/rotatedCombobox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowHeight.qml b/tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowHeight.qml
index b9b8874622..0de6122431 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowHeight.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowHeight.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowWidth.qml b/tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowWidth.qml
index aa78353eb0..2cb655f129 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowWidth.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/shrinkPopupThatWasLargerThanWindowWidth.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickpopup/data/tabFence.qml b/tests/auto/quickcontrols/qquickpopup/data/tabFence.qml
index a96cf1687c..376ca66e6e 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/tabFence.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/tabFence.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Layouts
diff --git a/tests/auto/quickcontrols/qquickpopup/data/toolTipCrashOnClose.qml b/tests/auto/quickcontrols/qquickpopup/data/toolTipCrashOnClose.qml
index f40fa0939c..8e4eb877dc 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/toolTipCrashOnClose.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/toolTipCrashOnClose.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickpopup/data/window-hover.qml b/tests/auto/quickcontrols/qquickpopup/data/window-hover.qml
index fa61e8b706..5beca69317 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/window-hover.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/window-hover.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -16,21 +16,24 @@ Window {
Button {
id: parentButton
text: "Parent"
+ palette.buttonText: hovered ? "tomato" : "black"
anchors.fill: parent
+ anchors.margins: 10
Popup {
id: popup
- x: 1
- y: 1
- topPadding: 1
- bottomPadding: 1
- leftPadding: 1
- rightPadding: 1
+ x: 10
+ y: 10
+ leftPadding: 10
+ rightPadding: 10
+ topPadding: 10
+ bottomPadding: 10
Button {
anchors.centerIn: parent
id: childButton
text: "Child"
+ palette.buttonText: hovered ? "tomato" : "black"
}
}
}
diff --git a/tests/auto/quickcontrols/qquickpopup/data/window-wheel.qml b/tests/auto/quickcontrols/qquickpopup/data/window-wheel.qml
index ae9c2ecdfd..57cce40982 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/window-wheel.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/window-wheel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickpopup/data/window.qml b/tests/auto/quickcontrols/qquickpopup/data/window.qml
index 6ffe5165fe..0db6896f59 100644
--- a/tests/auto/quickcontrols/qquickpopup/data/window.qml
+++ b/tests/auto/quickcontrols/qquickpopup/data/window.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
index 8343cc4f3a..f894387672 100644
--- a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
@@ -1,14 +1,16 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
+#include <QtQuickTest/quicktest.h>
#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtGui/qpa/qplatformintegration.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtQuick/qquickview.h>
+#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquickpalette_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
@@ -25,6 +27,10 @@
#include <QtQuickTemplates2/private/qquickstackview_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
#include <QtQuickTemplates2/private/qquicktooltip_p.h>
+#include <QtQuickTemplates2/private/qquickdrawer_p.h>
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuick/private/qquicktextedit_p.h>
+#include <QtQuick/private/qquickdroparea_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
@@ -59,13 +65,19 @@ private slots:
void activeFocusAfterExit();
void activeFocusOnDelayedEnter();
void activeFocusDespiteLowerStackingOrder();
+ void activeFocusItemAfterWindowInactive();
void hover_data();
void hover();
+#if QT_CONFIG(wheelevent)
void wheel_data();
void wheel();
+#endif
void parentDestroyed();
void nested();
+#if QT_CONFIG(wheelevent)
void nestedWheel();
+ void nestedWheelWithOverlayParent();
+#endif
void modelessOnModalOnModeless();
void grabber();
void cursorShape();
@@ -92,6 +104,12 @@ private slots:
void relativeZOrder();
void mirroredCombobox();
void rotatedCombobox();
+ void focusMultiplePopup();
+ void contentChildrenChange();
+ void doubleClickInMouseArea();
+ void fadeDimmer_data();
+ void fadeDimmer();
+ void noDimmer();
private:
static bool hasWindowActivation();
@@ -217,6 +235,10 @@ void tst_QQuickPopup::overlay_data()
void tst_QQuickPopup::overlay()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Test crashes. See QTBUG-118532");
+#endif
+
QFETCH(QString, source);
QFETCH(bool, modal);
QFETCH(bool, dim);
@@ -937,6 +959,53 @@ void tst_QQuickPopup::activeFocusDespiteLowerStackingOrder()
QVERIFY(!popup1->hasActiveFocus());
}
+void tst_QQuickPopup::activeFocusItemAfterWindowInactive()
+{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
+ QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusAfterWindowInactive.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowFocused(window));
+
+ QQuickPopup *popup = helper.appWindow->property("popup").value<QQuickPopup*>();
+ QQuickButton *button = helper.appWindow->property("button").value<QQuickButton*>();
+ QVERIFY(popup);
+ QVERIFY(button);
+
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
+ QVERIFY(popup->hasActiveFocus());
+ QVERIFY(!button->hasActiveFocus());
+
+ popup->close();
+ QVERIFY(!popup->isVisible());
+ QTRY_VERIFY(!popup->isOpened());
+ QVERIFY(button->hasActiveFocus());
+ QCOMPARE(window->activeFocusItem(), button);
+
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
+
+ QQuickWindow newWindow;
+ newWindow.setTitle("newFocusWindow");
+ newWindow.show();
+ newWindow.requestActivate();
+ QVERIFY(QTest::qWaitForWindowFocused(&newWindow));
+
+ popup->close();
+ QCOMPARE(QGuiApplication::focusWindow(), &newWindow);
+
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowFocused(window));
+ QCOMPARE(window->activeFocusItem(), button);
+}
+
void tst_QQuickPopup::hover_data()
{
QTest::addColumn<QString>("source");
@@ -977,18 +1046,19 @@ void tst_QQuickPopup::hover()
QVERIFY(openedSpy.size() == 1 || openedSpy.wait());
QTRY_VERIFY(popup->width() > 10); // somehow this can take a short time with macOS style
- // hover the parent button outside the popup
- QTest::mouseMove(window, QPoint(window->width() - 1, window->height() - 1));
+ // Hover the parent button outside the popup. It has 10 pixel anchor margins around the window.
+ PointLerper pointLerper(window);
+ pointLerper.move(15, 15);
QCOMPARE(parentButton->isHovered(), !modal);
QVERIFY(!childButton->isHovered());
- // hover the popup background
- QTest::mouseMove(window, QPoint(1, 1));
+ // Hover the popup background. Its top-left is 10 pixels in from its parent.
+ pointLerper.move(25, 25);
QVERIFY(!parentButton->isHovered());
QVERIFY(!childButton->isHovered());
- // hover the child button in a popup
- QTest::mouseMove(window, QPoint(popup->x() + popup->width() / 2, popup->y() + popup->height() / 2));
+ // Hover the child button in a popup.
+ pointLerper.move(mapCenterToWindow(childButton));
QVERIFY(!parentButton->isHovered());
QVERIFY(childButton->isHovered());
@@ -1002,6 +1072,7 @@ void tst_QQuickPopup::hover()
QVERIFY(parentButton->isHovered());
}
+#if QT_CONFIG(wheelevent)
void tst_QQuickPopup::wheel_data()
{
QTest::addColumn<QString>("source");
@@ -1115,6 +1186,7 @@ void tst_QQuickPopup::wheel()
QVERIFY(qFuzzyCompare(popupSlider->value(), oldPopupValue)); // must not have moved
}
}
+#endif
void tst_QQuickPopup::parentDestroyed()
{
@@ -1153,6 +1225,7 @@ void tst_QQuickPopup::nested()
QCOMPARE(modalPopup->isVisible(), true);
}
+#if QT_CONFIG(wheelevent)
void tst_QQuickPopup::nestedWheel()
{
QQuickControlsApplicationHelper helper(this, QStringLiteral("nested-wheel.qml"));
@@ -1183,6 +1256,42 @@ void tst_QQuickPopup::nestedWheel()
QTRY_COMPARE_GT(vbar->property("position").toDouble(), startPosition);
}
+void tst_QQuickPopup::nestedWheelWithOverlayParent()
+{
+ QQuickControlsApplicationHelper helper(this, QStringLiteral("nested-wheel-overlay-parent.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *drawer= window->property("_drawer").value<QQuickDrawer *>();
+ QVERIFY(drawer);
+
+ auto *dropArea = window->property("_dropArea").value<QQuickDropArea *>();
+ QVERIFY(dropArea);
+
+ drawer->open();
+ QCOMPARE(drawer->isVisible(), true);
+ QTRY_COMPARE(drawer->isOpened(), true);
+
+ QQuickListView *listView = window->property("_listView").value<QQuickListView *>();
+ QTRY_VERIFY(listView != nullptr);
+ QQuickItem *contentItem = listView->contentItem();
+ QTRY_VERIFY(contentItem != nullptr);
+
+ // Check parent is set as overlay
+ QTRY_COMPARE(dropArea->parentItem(), QQuickOverlay::overlay(window));
+ // Consider the center point of the control as event position to trigger wheel event
+ QVERIFY(sendWheelEvent(listView, -15));
+
+ if (QQuickTest::qIsPolishScheduled(listView))
+ QVERIFY(QQuickTest::qWaitForPolish(listView));
+
+ // Wheel over the list view, verify that it scrolls
+ QTRY_COMPARE(listView->contentY(), 72.);
+}
+#endif
+
void tst_QQuickPopup::modelessOnModalOnModeless()
{
QQuickControlsApplicationHelper helper(this, QStringLiteral("modelessOnModalOnModeless.qml"));
@@ -1450,66 +1559,49 @@ void tst_QQuickPopup::enabled()
void tst_QQuickPopup::orientation_data()
{
QTest::addColumn<Qt::ScreenOrientation>("orientation");
- QTest::addColumn<QPointF>("position");
- // On Android the screen size will usually be smaller than the 600x300
- // size of a Window in orientation.qml
- // Because of that we need to calculate proper positions at runtime.
-#ifndef Q_OS_ANDROID
- QQuickControlsApplicationHelper helper(this, "orientation.qml");
- const QSize availableSize = helper.window->size();
-#else
- const QSize availableSize = QGuiApplication::primaryScreen()->availableSize();
-#endif
- const int width = availableSize.width();
- const int height = availableSize.height();
-
- // The width & height might be odd numbers, so we calculate center in a way
- // similar to anchors.centerIn.
- // Also note that when we emulate the screen orientation change (by calling
- // window->reportContentOrientationChange() in the test), these values need
- // to be adjusted, because the "logical" (0, 0) of the screen changes.
- const int widthCenter = (width % 2) ? (width + 1) / 2 : width / 2;
- const int heightCenter = (height % 2) ? (height + 1) / 2 : height / 2;
-
- // Rectangle is (60x30); popup is (30x60).
- // Rectangle is using "anchors.centerIn: parent", and popup is positioned at
- // (rectangle.width, rectangle.height)
- QTest::newRow("Portrait") << Qt::PortraitOrientation
- << QPointF(widthCenter - 30 + 60, heightCenter - 15 + 30);
- // in landscape orientation the top left corner of physical screen
- // (not rotated) becomes (0, 0), so we need to adjust our widthCenter
- QTest::newRow("Landscape") << Qt::LandscapeOrientation
- << QPointF(heightCenter - 15 + 30, (width - widthCenter) + 30 - 60);
- // In inverted portrait orientation the bottom right corner of physical
- // screen (not rotated) becomes (0, 0), so we need to adjust both
- // widthCenter and heightCenter
- QTest::newRow("InvertedPortrait") << Qt::InvertedPortraitOrientation
- << QPointF((width - widthCenter) + 30 - 60, (height - heightCenter) + 15 - 30);
- // In inverted landscape orientation the bottom right corner of physical
- // screen (not rotated) becomes (0, 0), so we need to adjust heightCenter
- QTest::newRow("InvertedLandscape") << Qt::InvertedLandscapeOrientation
- << QPointF((height - heightCenter) + 15 - 30, widthCenter - 30 + 60);
+ QTest::newRow("Portrait") << Qt::PortraitOrientation;
+ QTest::newRow("Landscape") << Qt::LandscapeOrientation;
+ QTest::newRow("InvertedPortrait") << Qt::InvertedPortraitOrientation;
+ QTest::newRow("InvertedLandscape") << Qt::InvertedLandscapeOrientation;
}
+/*
+ Verify that the popup is centered, and that the dimmer and overlay cover
+ the content item for any content orientation and content rotation.
+*/
void tst_QQuickPopup::orientation()
{
QFETCH(Qt::ScreenOrientation, orientation);
- QFETCH(QPointF, position);
QQuickControlsApplicationHelper helper(this, "orientation.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
- window->reportContentOrientationChange(orientation);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
- QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
+ QQuickPopup *popup = window->property("popup").value<QQuickPopup *>();
QVERIFY(popup);
- popup->open();
QTRY_VERIFY(popup->isOpened());
- QCOMPARE(popup->popupItem()->position(), position);
+
+ QQuickItem *dimmer = QQuickPopupPrivate::get(popup)->dimmer;
+ QVERIFY(dimmer);
+ QQuickItem *popupItem = popup->popupItem();
+ QVERIFY(popupItem);
+ QQuickOverlay *overlay = QQuickOverlay::overlay(window);
+ QVERIFY(overlay);
+ QQuickItem *contentItem = window->contentItem();
+ QVERIFY(contentItem);
+
+ const auto rotation = window->screen()->angleBetween(Qt::PrimaryOrientation, orientation);
+ QCOMPARE(popupItem->mapToScene(popupItem->boundingRect().center()), contentItem->boundingRect().center());
+ QCOMPARE(dimmer->boundingRect(), contentItem->boundingRect());
+ QCOMPARE(overlay->boundingRect(), contentItem->boundingRect());
+ window->contentItem()->setRotation(rotation);
+ QCOMPARE(popupItem->mapToScene(popupItem->boundingRect().center()), contentItem->boundingRect().center());
+ QCOMPARE(dimmer->boundingRect(), contentItem->boundingRect());
+ QCOMPARE(overlay->boundingRect(), contentItem->boundingRect());
}
void tst_QQuickPopup::qquickview()
@@ -2149,6 +2241,152 @@ void tst_QQuickPopup::rotatedCombobox()
}
}
+void tst_QQuickPopup::focusMultiplePopup()
+{
+ QQuickApplicationHelper helper(this, "multiplepopup.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *rootItem = window->findChild<QQuickItem *>("rootItem");
+ QTRY_VERIFY(rootItem->hasFocus());
+
+ auto *buttonPopup = window->findChild<QQuickPopup *>("popup1");
+ buttonPopup->open();
+ QTRY_VERIFY(buttonPopup->isOpened());
+ QVERIFY(!rootItem->hasFocus());
+ QVERIFY(buttonPopup->hasFocus());
+
+ auto *textEditPopup = window->findChild<QQuickPopup *>("popup2");
+ textEditPopup->open();
+ QTRY_VERIFY(textEditPopup->isOpened());
+ QVERIFY(textEditPopup->hasFocus());
+
+ auto *drawerPopup = window->findChild<QQuickPopup *>("popup3");
+ drawerPopup->open();
+ QTRY_VERIFY(drawerPopup->isVisible());
+ QVERIFY(drawerPopup->hasFocus());
+
+ auto *drawer = window->findChild<QQuickDrawer *>("drawer");
+ drawer->close();
+ QTRY_VERIFY(!drawer->isVisible());
+ drawerPopup->close();
+ QTRY_VERIFY(!drawerPopup->isVisible());
+ QVERIFY(textEditPopup->hasFocus());
+
+ textEditPopup->close();
+ QTRY_VERIFY(!textEditPopup->isVisible());
+ QVERIFY(buttonPopup->hasFocus());
+
+ buttonPopup->close();
+ QTRY_VERIFY(!buttonPopup->isVisible());
+
+ QVERIFY(rootItem->hasFocus());
+}
+
+void tst_QQuickPopup::contentChildrenChange()
+{
+ QQmlEngine engine;
+ QQmlComponent comp(&engine);
+ comp.loadFromModule("QtQuick.Controls", "Popup");
+ std::unique_ptr<QObject> root {comp.create()};
+ QVERIFY(root);
+ QQuickPopup *popup = qobject_cast<QQuickPopup *>(root.get());
+ QVERIFY(popup);
+ QSignalSpy spy(popup, &QQuickPopup::contentChildrenChanged);
+ auto contentItem = std::make_unique<QQuickItem>();
+ popup->setContentItem(contentItem.get());
+ QCOMPARE(spy.count(), 1);
+ auto newChild = std::make_unique<QQuickItem>();
+ QQmlProperty contentItemChildren(contentItem.get());
+ contentItemChildren.write(QVariant::fromValue(newChild.get()));
+ QCOMPARE(spy.count(), 2);
+}
+
+void tst_QQuickPopup::doubleClickInMouseArea()
+{
+#ifdef Q_OS_ANDROID
+ QSKIP("Test crashes. See QTBUG-118532");
+#endif
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("doubleClickInMouseArea.qml")));
+
+ auto *ma = window.rootObject()->findChild<QQuickMouseArea *>();
+ QVERIFY(ma);
+ QSignalSpy doubleClickSpy(ma, &QQuickMouseArea::doubleClicked);
+ QSignalSpy longPressSpy(ma, &QQuickMouseArea::pressAndHold);
+ QPoint p = ma->mapToScene(ma->boundingRect().center()).toPoint();
+
+ // check with normal double click
+ QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, p);
+ QCOMPARE(doubleClickSpy.count(), 1);
+
+ // wait enough time for a wrong long press to happen
+ QTest::qWait(QGuiApplication::styleHints()->mousePressAndHoldInterval() + 10);
+ QCOMPARE(longPressSpy.count(), 0);
+}
+
+void tst_QQuickPopup::fadeDimmer_data()
+{
+ QTest::addColumn<bool>("modality");
+
+ QTest::addRow("modal") << true;
+ QTest::addRow("modeless") << true;
+}
+
+void tst_QQuickPopup::fadeDimmer()
+{
+ QFETCH(const bool, modality);
+ QQuickApplicationHelper helper(this, "fadeDimmer.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *popup = window->contentItem()->findChild<QQuickPopup *>();
+ QVERIFY(popup);
+
+ popup->setModal(modality);
+ popup->open();
+ auto dimmer = QQuickPopupPrivate::get(popup)->dimmer;
+ QVERIFY(dimmer);
+ int opacityChangeCount = 0;
+ connect(dimmer, &QQuickItem::opacityChanged, this, [&opacityChangeCount]{
+ ++opacityChangeCount;
+ });
+ QTRY_VERIFY(popup->isOpened());
+ QTRY_COMPARE(dimmer->opacity(), popup->property("dimmerOpacity").toDouble());
+ QCOMPARE_GT(opacityChangeCount, 2);
+
+ opacityChangeCount = 0;
+ popup->setVisible(false);
+ QTRY_VERIFY(!popup->isVisible());
+ QCOMPARE_GT(opacityChangeCount, 2);
+}
+
+void tst_QQuickPopup::noDimmer()
+{
+ QQuickApplicationHelper helper(this, "noDimmer.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *drawer = window->contentItem()->findChild<QQuickDrawer *>();
+ QVERIFY(drawer);
+
+ drawer->open();
+ auto dimmer = QQuickPopupPrivate::get(drawer)->dimmer;
+ QVERIFY(dimmer);
+ // this must not crash
+ QTRY_VERIFY(!drawer->isModal());
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"
diff --git a/tests/auto/quickcontrols/qquickstyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickstyle/CMakeLists.txt
index 4255850e30..01b13e1f11 100644
--- a/tests/auto/quickcontrols/qquickstyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickstyle/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickstyle LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickstyle/tst_qquickstyle.cpp b/tests/auto/quickcontrols/qquickstyle/tst_qquickstyle.cpp
index e7572164b8..8806b04a1e 100644
--- a/tests/auto/quickcontrols/qquickstyle/tst_qquickstyle.cpp
+++ b/tests/auto/quickcontrols/qquickstyle/tst_qquickstyle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt b/tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt
index f64b896e5c..1d159b69b5 100644
--- a/tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquicktextarea/CMakeLists.txt
@@ -1,10 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+# SPDX-License-Identifier: BSD-3-Clause
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquicktextarea LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp b/tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp
index 1c40b078f7..a576bb7941 100644
--- a/tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp
+++ b/tests/auto/quickcontrols/qquicktextarea/tst_qquicktextarea.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
@@ -86,9 +86,11 @@ void tst_QQuickTextArea::touchscreenDoesNotSelect()
if (selectByMouse) {
// press-drag-and-release from x1 to x2
- int x1 = 10;
- int x2 = 70;
- int y = QFontMetrics(textEditObject->font()).height() / 2;
+ const int x1 = textEditObject->leftPadding();
+ const int x2 = textEditObject->width() / 2;
+ // Account for all styles by being aware of vertical padding.
+ // contentHeight / 2 should be half the line height considering that we only have one line of text.
+ const int y = textEditObject->topPadding() + textEditObject->contentHeight() / 2;
QTest::touchEvent(&window, touchDevice.data()).press(0, QPoint(x1,y), &window);
QTest::touchEvent(&window, touchDevice.data()).move(0, QPoint(x2,y), &window);
QTest::touchEvent(&window, touchDevice.data()).release(0, QPoint(x2,y), &window);
diff --git a/tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt b/tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt
index 50755b7f1c..9ad2308e7c 100644
--- a/tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquicktextfield/CMakeLists.txt
@@ -1,10 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+# SPDX-License-Identifier: BSD-3-Clause
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquicktextfield LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp b/tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp
index 19219bb79e..11d309fd42 100644
--- a/tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp
+++ b/tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
@@ -84,9 +84,9 @@ void tst_QQuickTextField::touchscreenDoesNotSelect()
if (selectByMouse) {
// press-drag-and-release from x1 to x2
- int x1 = 10;
- int x2 = 70;
- int y = QFontMetrics(textField->font()).height() / 2;
+ const int x1 = textField->leftPadding();
+ const int x2 = textField->width() / 2;
+ const int y = textField->height() / 2;
QTest::touchEvent(&window, touchDevice.data()).press(0, QPoint(x1,y), &window);
QTest::touchEvent(&window, touchDevice.data()).move(0, QPoint(x2,y), &window);
QTest::touchEvent(&window, touchDevice.data()).release(0, QPoint(x2,y), &window);
diff --git a/tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt b/tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt
index 94d4096586..e577bb6a5d 100644
--- a/tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquicktreeviewdelegate/CMakeLists.txt
@@ -4,7 +4,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquicktreeviewdelegate LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquicktreeviewdelegate/data/unmodified.qml b/tests/auto/quickcontrols/qquicktreeviewdelegate/data/unmodified.qml
index c3edb37d1c..65de049893 100644
--- a/tests/auto/quickcontrols/qquicktreeviewdelegate/data/unmodified.qml
+++ b/tests/auto/quickcontrols/qquicktreeviewdelegate/data/unmodified.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import TestModel
diff --git a/tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.cpp b/tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.cpp
index 6c6da8452b..26822bfa1e 100644
--- a/tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.cpp
+++ b/tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testmodel.h"
diff --git a/tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.h b/tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.h
index f886c56d4a..e0c4a9953e 100644
--- a/tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.h
+++ b/tests/auto/quickcontrols/qquicktreeviewdelegate/testmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTMODEL_H
#define TESTMODEL_H
diff --git a/tests/auto/quickcontrols/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp b/tests/auto/quickcontrols/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp
index 8471a31408..2725248aee 100644
--- a/tests/auto/quickcontrols/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp
+++ b/tests/auto/quickcontrols/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtQuickTest/quicktest.h>
@@ -25,7 +25,7 @@ using namespace QQuickVisualTestUtils;
#define LOAD_TREEVIEW(fileName) \
view->setSource(testFileUrl(fileName)); \
view->show(); \
- QVERIFY(QTest::qWaitForWindowActive(view)); \
+ QVERIFY(QTest::qWaitForWindowActive(view.get())); \
auto treeView = view->rootObject()->property("treeView").value<QQuickTreeView *>(); \
QVERIFY(treeView); \
auto treeViewPrivate = QQuickTreeViewPrivate::get(treeView); \
@@ -46,7 +46,7 @@ public:
tst_qquicktreeviewdelegate();
private:
- QQuickView *view = nullptr;
+ std::unique_ptr<QQuickView> view = nullptr;
private slots:
void initTestCase() override;
@@ -74,7 +74,7 @@ void tst_qquicktreeviewdelegate::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<TestModel>("TestModel", 1, 0, "TestModel");
- view = createView();
+ view.reset(createView());
}
void tst_qquicktreeviewdelegate::showTreeView()
@@ -91,7 +91,7 @@ void tst_qquicktreeviewdelegate::expandAndCollapsUsingDoubleClick()
QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
// Expand the root by double clicking on the row
- const auto item = treeView->itemAtCell(0, 0);
+ const auto item = treeView->itemAtIndex(treeView->index(0, 0));
QVERIFY(item);
const QPoint localPos = QPoint(item->width() / 2, item->height() / 2);
const QPoint pos = item->window()->contentItem()->mapFromItem(item, localPos).toPoint();
@@ -128,7 +128,7 @@ void tst_qquicktreeviewdelegate::expandAndCollapseClickOnIndicator()
QCOMPARE(treeViewPrivate->loadedRows.count(), 1);
// Expand the root by clicking on the indicator
- const auto item = qobject_cast<QQuickTreeViewDelegate *>(treeView->itemAtCell(0, 0));
+ const auto item = qobject_cast<QQuickTreeViewDelegate *>(treeView->itemAtIndex(treeView->index(0, 0)));
QVERIFY(item);
const auto indicator = item->indicator();
const QPoint localPos = QPoint(indicator->width() / 2, indicator->height() / 2);
@@ -159,7 +159,7 @@ void tst_qquicktreeviewdelegate::pointerNavigationDisabled()
QVERIFY(!treeView->isExpanded(0));
// Try to expand the root by clicking on the indicator
- const auto item = qobject_cast<QQuickTreeViewDelegate *>(treeView->itemAtCell(0, 0));
+ const auto item = qobject_cast<QQuickTreeViewDelegate *>(treeView->itemAtIndex(treeView->index(0, 0)));
QVERIFY(item);
const auto indicator = item->indicator();
QPoint localPos = QPoint(indicator->width() / 2, indicator->height() / 2);
@@ -292,7 +292,7 @@ void tst_qquicktreeviewdelegate::checkClickedSignal()
LOAD_TREEVIEW("unmodified.qml");
treeView->setPointerNavigationEnabled(pointerNavigationEnabled);
- const auto item = treeView->itemAtCell(0, 0);
+ const auto item = treeView->itemAtIndex(treeView->index(0, 0));
QVERIFY(item);
QSignalSpy clickedSpy(item, SIGNAL(clicked()));
@@ -323,7 +323,7 @@ void tst_qquicktreeviewdelegate::clearSelectionOnClick()
QCOMPARE(treeView->selectionModel()->selectedIndexes().size(), 1);
// Click on a cell. This should remove the selection
- const auto item = qobject_cast<QQuickTreeViewDelegate *>(treeView->itemAtCell(0, 0));
+ const auto item = qobject_cast<QQuickTreeViewDelegate *>(treeView->itemAtIndex(treeView->index(0, 0)));
QVERIFY(item);
QPoint localPos = QPoint(item->width() / 2, item->height() / 2);
QPoint pos = item->window()->contentItem()->mapFromItem(item, localPos).toPoint();
@@ -348,8 +348,8 @@ void tst_qquicktreeviewdelegate::dragToSelect()
QCOMPARE(treeView->selectionBehavior(), QQuickTableView::SelectRows);
// Drag on from cell 0,0 to 0,1
- const auto item0_0 = treeView->itemAtCell(0, 0);
- const auto item0_1 = treeView->itemAtCell(0, 1);
+ const auto item0_0 = treeView->itemAtIndex(treeView->index(0, 0));
+ const auto item0_1 = treeView->itemAtIndex(treeView->index(1, 0));
QVERIFY(item0_0);
QVERIFY(item0_1);
@@ -385,7 +385,7 @@ void tst_qquicktreeviewdelegate::pressAndHoldToSelect()
QCOMPARE(treeView->selectionBehavior(), QQuickTableView::SelectRows);
// PressAndHold on cell 0,0
- const auto item0_0 = treeView->itemAtCell(0, 0);
+ const auto item0_0 = treeView->itemAtIndex(treeView->index(0, 0));
QVERIFY(item0_0);
QQuickWindow *window = treeView->window();
diff --git a/tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt
index a7f8ebfd6f..7b93c9812b 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickuniversalstyle/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickuniversalstyle LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickuniversalstyle/data/tst_universal.qml b/tests/auto/quickcontrols/qquickuniversalstyle/data/tst_universal.qml
index 2ce0cadb38..20b2fedc06 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyle/data/tst_universal.qml
+++ b/tests/auto/quickcontrols/qquickuniversalstyle/data/tst_universal.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/auto/quickcontrols/qquickuniversalstyle/tst_qquickuniversalstyle.cpp b/tests/auto/quickcontrols/qquickuniversalstyle/tst_qquickuniversalstyle.cpp
index 1d2d6332f7..f703c549f8 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyle/tst_qquickuniversalstyle.cpp
+++ b/tests/auto/quickcontrols/qquickuniversalstyle/tst_qquickuniversalstyle.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(tst_qquickuniversalstyle)
diff --git a/tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt b/tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt
index cfe781950d..a8387a0486 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickuniversalstyleconf/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickuniversalstyleconf LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/qquickuniversalstyleconf/data/applicationwindow.qml b/tests/auto/quickcontrols/qquickuniversalstyleconf/data/applicationwindow.qml
index acbccdeab0..a1d9367b7b 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyleconf/data/applicationwindow.qml
+++ b/tests/auto/quickcontrols/qquickuniversalstyleconf/data/applicationwindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/qquickuniversalstyleconf/qtquickcontrols2.conf b/tests/auto/quickcontrols/qquickuniversalstyleconf/qtquickcontrols2.conf
index 0ec1325871..aef058c7d2 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyleconf/qtquickcontrols2.conf
+++ b/tests/auto/quickcontrols/qquickuniversalstyleconf/qtquickcontrols2.conf
@@ -4,7 +4,5 @@ Style=Universal
[Universal]
Background=#444444
Foreground=Red
+Font\Family=Courier
Font\PixelSize=22
-
-[Universal\Font]
-Family=Courier
diff --git a/tests/auto/quickcontrols/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp b/tests/auto/quickcontrols/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp
index b75668fb66..f220a8c54e 100644
--- a/tests/auto/quickcontrols/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp
+++ b/tests/auto/quickcontrols/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQuick/private/qquickitem_p.h>
diff --git a/tests/auto/quickcontrols/revisions/CMakeLists.txt b/tests/auto/quickcontrols/revisions/CMakeLists.txt
index 8c51f85d20..2dc13eb887 100644
--- a/tests/auto/quickcontrols/revisions/CMakeLists.txt
+++ b/tests/auto/quickcontrols/revisions/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_revisions LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/revisions/tst_revisions.cpp b/tests/auto/quickcontrols/revisions/tst_revisions.cpp
index 51f0f02aae..f000603fb0 100644
--- a/tests/auto/quickcontrols/revisions/tst_revisions.cpp
+++ b/tests/auto/quickcontrols/revisions/tst_revisions.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
diff --git a/tests/auto/quickcontrols/sanity/CMakeLists.txt b/tests/auto/quickcontrols/sanity/CMakeLists.txt
index 31fbe176c0..bf9a061be7 100644
--- a/tests/auto/quickcontrols/sanity/CMakeLists.txt
+++ b/tests/auto/quickcontrols/sanity/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_sanity LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
file(GLOB_RECURSE test_data_glob
diff --git a/tests/auto/quickcontrols/sanity/data/attachedTypeResolution.qml b/tests/auto/quickcontrols/sanity/data/attachedTypeResolution.qml
new file mode 100644
index 0000000000..d6062be1f6
--- /dev/null
+++ b/tests/auto/quickcontrols/sanity/data/attachedTypeResolution.qml
@@ -0,0 +1,6 @@
+import QtQml
+import QtQuick
+
+Window {
+
+}
diff --git a/tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp b/tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp
index 7d8a216c78..0f8ea47bfd 100644
--- a/tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp
+++ b/tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "quickcontrolssanity.h"
@@ -10,7 +10,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-static constexpr LoggerWarningId qmlControlsSanity{ "QuickControlsSanity.controls-sanity" };
+static constexpr QQmlJS::LoggerWarningId qmlControlsSanity{ "QuickControlsSanity.controls-sanity" };
AnchorsElementPass::AnchorsElementPass(QQmlSA::PassManager *manager)
: QQmlSA::ElementPass(manager), m_item(resolveType("QtQuick"_L1, "Item"_L1))
@@ -19,14 +19,14 @@ AnchorsElementPass::AnchorsElementPass(QQmlSA::PassManager *manager)
bool AnchorsElementPass::shouldRun(const QQmlSA::Element &element)
{
- return !m_item.isNull() && element->inherits(m_item)
- && element->hasOwnPropertyBindings("anchors"_L1);
+ return !m_item.isNull() && element.inherits(m_item)
+ && element.hasOwnPropertyBindings("anchors"_L1);
}
void AnchorsElementPass::run(const QQmlSA::Element &element)
{
- const auto anchorBindings = element->propertyBindings("anchors"_L1);
- for (auto &&anchors : anchorBindings) {
+ const auto anchorBindings = element.propertyBindings("anchors"_L1);
+ for (const auto &anchors : anchorBindings) {
emitWarning(u"Using anchors here"_s, qmlControlsSanity, anchors.sourceLocation());
}
}
@@ -38,17 +38,19 @@ SignalHandlerPass::SignalHandlerPass(QQmlSA::PassManager *manager)
bool SignalHandlerPass::shouldRun(const QQmlSA::Element &element)
{
- return !m_qtobject.isNull() && element->inherits(m_qtobject);
+ return !m_qtobject.isNull() && element.inherits(m_qtobject);
}
void SignalHandlerPass::run(const QQmlSA::Element &element)
{
- for (auto &&[propertyName, propertyBinding] :
- element->ownPropertyBindings().asKeyValueRange()) {
+ const auto &ownBindings = element.ownPropertyBindings();
+ for (auto it = ownBindings.constBegin(); it != ownBindings.constEnd(); ++it) {
+ const auto &propertyName = it.key();
+ const auto &propertyBinding = it.value();
// Already script binding, check if the script kind is signal handler
- if (propertyBinding.bindingType() == QQmlJSMetaPropertyBinding::Script) {
- if (propertyBinding.scriptKind() == QQmlJSMetaPropertyBinding::Script_SignalHandler) {
+ if (propertyBinding.bindingType() == QQmlSA::BindingType::Script) {
+ if (propertyBinding.scriptKind() == QQmlSA::ScriptBindingKind::SignalHandler) {
emitWarning(u"Declared signal handler \"%1\""_s.arg(propertyName),
qmlControlsSanity, propertyBinding.sourceLocation());
}
@@ -56,11 +58,11 @@ void SignalHandlerPass::run(const QQmlSA::Element &element)
}
// Current property is attached property, recursively go through attaching type
- if (propertyBinding.bindingType() == QQmlJSMetaPropertyBinding::AttachedProperty) {
- const auto scope = propertyBinding.attachingType();
+ if (propertyBinding.bindingType() == QQmlSA::BindingType::AttachedProperty) {
+ const auto scope = QQmlSA::Element{ propertyBinding.attachingType() };
run(scope);
}
- };
+ }
}
FunctionDeclarationPass::FunctionDeclarationPass(QQmlSA::PassManager *manager)
@@ -76,12 +78,12 @@ bool FunctionDeclarationPass::shouldRun(const QQmlSA::Element &element)
void FunctionDeclarationPass::run(const QQmlSA::Element &element)
{
- for (auto &&method : element->ownMethods()) {
- if (method.methodType() != QQmlJSMetaMethod::Method)
+ for (const auto &method : element.ownMethods()) {
+ if (method.methodType() != QQmlSA::MethodType::Method)
continue;
emitWarning(u"Declared function \"%1\""_s.arg(method.methodName()), qmlControlsSanity,
- element->sourceLocation());
+ element.sourceLocation());
}
}
diff --git a/tests/auto/quickcontrols/sanity/quickcontrolssanity.h b/tests/auto/quickcontrols/sanity/quickcontrolssanity.h
index 2b6eb5b9d9..29bc0c7a3a 100644
--- a/tests/auto/quickcontrols/sanity/quickcontrolssanity.h
+++ b/tests/auto/quickcontrols/sanity/quickcontrolssanity.h
@@ -1,5 +1,5 @@
// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QUICKCONTROLSSANITY_H
#define QUICKCONTROLSSANITY_H
diff --git a/tests/auto/quickcontrols/sanity/tst_sanity.cpp b/tests/auto/quickcontrols/sanity/tst_sanity.cpp
index 493b717253..01524e4a89 100644
--- a/tests/auto/quickcontrols/sanity/tst_sanity.cpp
+++ b/tests/auto/quickcontrols/sanity/tst_sanity.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtQml>
@@ -19,6 +19,8 @@
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQmlCompiler/private/qqmljslinter_p.h>
+#include <QtQmlCompiler/private/qqmljstyperesolver_p.h>
+#include <QtQmlCompiler/private/qqmljsimportvisitor_p.h>
Q_IMPORT_PLUGIN(QuickControlsSanityPlugin)
@@ -33,7 +35,7 @@ public:
tst_Sanity();
private slots:
- void initTestCase();
+ void initTestCase() override;
void jsFiles();
void qmllint();
@@ -48,7 +50,7 @@ private:
QStringList m_importPaths;
QQmlJSLinter m_linter;
- QList<QQmlJSLogger::Category> m_categories;
+ QList<QQmlJS::LoggerCategory> m_categories;
};
tst_Sanity::tst_Sanity()
@@ -64,10 +66,12 @@ tst_Sanity::tst_Sanity()
m_linter.setPluginsEnabled(true);
for (auto &category : m_categories) {
- if (category == qmlDeferredPropertyId || category == qmlAttachedPropertyReuse) {
- category.setLevel(u"warning"_s);
+ if (category.id() == qmlDeferredPropertyId || category.id() == qmlAttachedPropertyReuse) {
+ category.setLevel(QtWarningMsg);
+ category.setIgnored(false);
} else {
- category.setLevel(u"disable"_s);
+ category.setLevel(QtCriticalMsg);
+ category.setIgnored(true);
}
}
}
diff --git a/tests/auto/quickcontrols/snippets/CMakeLists.txt b/tests/auto/quickcontrols/snippets/CMakeLists.txt
index cfb2fb4560..37a4b67e49 100644
--- a/tests/auto/quickcontrols/snippets/CMakeLists.txt
+++ b/tests/auto/quickcontrols/snippets/CMakeLists.txt
@@ -1,12 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
# Generated from snippets.pro.
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_snippets LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/snippets/tst_snippets.cpp b/tests/auto/quickcontrols/snippets/tst_snippets.cpp
index b16066a693..f0d1da48d7 100644
--- a/tests/auto/quickcontrols/snippets/tst_snippets.cpp
+++ b/tests/auto/quickcontrols/snippets/tst_snippets.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtQuick>
diff --git a/tests/auto/quickcontrols/styleimports/CMakeLists.txt b/tests/auto/quickcontrols/styleimports/CMakeLists.txt
index 4d19521f2e..d18a7effc1 100644
--- a/tests/auto/quickcontrols/styleimports/CMakeLists.txt
+++ b/tests/auto/quickcontrols/styleimports/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_styleimports LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/styleimports/data/applicationWindowWithButton.qml b/tests/auto/quickcontrols/styleimports/data/applicationWindowWithButton.qml
index 512bd0bb08..50f0e43419 100644
--- a/tests/auto/quickcontrols/styleimports/data/applicationWindowWithButton.qml
+++ b/tests/auto/quickcontrols/styleimports/data/applicationWindowWithButton.qml
@@ -1,7 +1,6 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-import QtQuick
import QtQuick.Controls
ApplicationWindow {
diff --git a/tests/auto/quickcontrols/styleimports/data/customStyleSelector.qml b/tests/auto/quickcontrols/styleimports/data/customStyleSelector.qml
index a3def8b14b..020d091156 100644
--- a/tests/auto/quickcontrols/styleimports/data/customStyleSelector.qml
+++ b/tests/auto/quickcontrols/styleimports/data/customStyleSelector.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/styleimports/data/importStyleWithQmlDirFallback.qml b/tests/auto/quickcontrols/styleimports/data/importStyleWithQmlDirFallback.qml
new file mode 100644
index 0000000000..3202fca1cd
--- /dev/null
+++ b/tests/auto/quickcontrols/styleimports/data/importStyleWithQmlDirFallback.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import StyleThatImportsMaterial
+
+ApplicationWindow {
+ title: "Test Application Window"
+ width: 400
+ height: 400
+
+ property alias button: button
+
+ Button {
+ id: button
+ text: "QmlOnly Button"
+ }
+}
diff --git a/tests/auto/quickcontrols/styleimports/data/styles/FileSystemStyle/Button.qml b/tests/auto/quickcontrols/styleimports/data/styles/FileSystemStyle/Button.qml
index a5078b14c2..71c451ab0f 100644
--- a/tests/auto/quickcontrols/styleimports/data/styles/FileSystemStyle/Button.qml
+++ b/tests/auto/quickcontrols/styleimports/data/styles/FileSystemStyle/Button.qml
@@ -1,4 +1,9 @@
import QtQuick.Templates as T
T.Button {
+ id: control
objectName: "FileSystemStyle"
+ contentItem: T.Label {
+ text: control.text
+ color: "#0000ff"
+ }
}
diff --git a/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsMaterial/Button.qml b/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsMaterial/Button.qml
new file mode 100644
index 0000000000..b7e77c2864
--- /dev/null
+++ b/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsMaterial/Button.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+
+T.Button {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ padding: 6
+ horizontalPadding: padding + 2
+ spacing: 6
+
+ contentItem: T.Label {
+ text: control.text
+ font: control.font
+ color: "#ff0000"
+ verticalAlignment: T.Label.AlignVCenter
+ }
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ visible: !control.flat || control.down || control.checked || control.highlighted
+ color: "#ccc"
+ border.color: control.palette.highlight
+ border.width: control.visualFocus ? 2 : 0
+ }
+}
diff --git a/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsMaterial/qmldir b/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsMaterial/qmldir
new file mode 100644
index 0000000000..eeffe70d4e
--- /dev/null
+++ b/tests/auto/quickcontrols/styleimports/data/styles/StyleThatImportsMaterial/qmldir
@@ -0,0 +1,3 @@
+module StyleThatImportsMaterial
+Button 1.0 Button.qml
+import QtQuick.Controls.Material
diff --git a/tests/auto/quickcontrols/styleimports/tst_styleimports.cpp b/tests/auto/quickcontrols/styleimports/tst_styleimports.cpp
index 175541296a..a88c154448 100644
--- a/tests/auto/quickcontrols/styleimports/tst_styleimports.cpp
+++ b/tests/auto/quickcontrols/styleimports/tst_styleimports.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qregularexpression.h>
#include <QtGui/qpalette.h>
@@ -15,7 +15,12 @@
#include <QtQuickControls2/qquickstyle.h>
#include <QtQuickControls2/private/qquickstyle_p.h>
#include <QtQuickControls2Impl/private/qquickiconlabel_p.h>
+#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
+#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
+#include <QtQuickTemplates2/private/qquickbutton_p.h>
+
+using namespace QQuickControlsTestUtils;
class tst_StyleImports : public QQmlDataTest
{
@@ -38,6 +43,9 @@ private slots:
void fallbackStyleShouldNotOverwriteTheme_data();
void fallbackStyleShouldNotOverwriteTheme();
+ void fallbackStyleThemeRespected_data();
+ void fallbackStyleThemeRespected();
+
void attachedTypesAvailable_data();
void attachedTypesAvailable();
};
@@ -278,6 +286,92 @@ void tst_StyleImports::fallbackStyleShouldNotOverwriteTheme()
QCOMPARE(contentItem->color(), expectedContentItemColor);
}
+enum FallbackMethod {
+ QmlDirImport,
+ EnvVar
+};
+
+void tst_StyleImports::fallbackStyleThemeRespected_data()
+{
+ QTest::addColumn<QString>("qmlFilePath");
+ QTest::addColumn<QString>("runtimeStyle");
+ QTest::addColumn<FallbackMethod>("fallbackMethod");
+ QTest::addColumn<Qt::ColorScheme>("colorScheme");
+ QTest::addColumn<QColor>("expectedButtonTextColor");
+ QTest::addColumn<QColor>("expectedWindowColor");
+
+ // Taken from qquickmaterialstyle.cpp.
+ static const QRgb materialBackgroundColorLight = 0xFFFFFBFE;
+ static const QRgb materialBackgroundColorDark = 0xFF1C1B1F;
+
+ // Notes:
+ // - FileSystemStyle has blue button text.
+ // - StyleThatImportsMaterial has red button text.
+ // - All rows result in Material being the fallback.
+
+ QTest::newRow("import controls, env var fallback, light") << "applicationWindowWithButton.qml"
+ << "FileSystemStyle" << EnvVar << Qt::ColorScheme::Light
+ << QColor::fromRgb(0x0000ff) << QColor::fromRgba(materialBackgroundColorLight);
+ QTest::newRow("import controls, env var fallback, dark") << "applicationWindowWithButton.qml"
+ << "FileSystemStyle" << EnvVar << Qt::ColorScheme::Dark
+ << QColor::fromRgb(0x0000ff) << QColor::fromRgba(materialBackgroundColorDark);
+
+ QTest::newRow("import style, qmldir fallback, light") << "importStyleWithQmlDirFallback.qml"
+ << "" << QmlDirImport << Qt::ColorScheme::Light
+ << QColor::fromRgb(0xff0000) << QColor::fromRgba(materialBackgroundColorLight);
+ QTest::newRow("import style, qmldir fallback, dark") << "importStyleWithQmlDirFallback.qml"
+ << "" << QmlDirImport << Qt::ColorScheme::Dark
+ << QColor::fromRgb(0xff0000) << QColor::fromRgba(materialBackgroundColorDark);
+}
+
+// Tests that a fallback style's (the Material style, in this case) theme settings
+// are respected for both run-time and compile-time style selection.
+void tst_StyleImports::fallbackStyleThemeRespected()
+{
+ QFETCH(QString, qmlFilePath);
+ QFETCH(QString, runtimeStyle);
+ QFETCH(FallbackMethod, fallbackMethod);
+ QFETCH(Qt::ColorScheme, colorScheme);
+ QFETCH(QColor, expectedButtonTextColor);
+ QFETCH(QColor, expectedWindowColor);
+
+ const char *materialThemeEnvVarName = "QT_QUICK_CONTROLS_MATERIAL_THEME";
+ const QString originalMaterialTheme = qgetenv(materialThemeEnvVarName);
+ qputenv(materialThemeEnvVarName, colorScheme == Qt::ColorScheme::Light ? "Light" : "Dark");
+
+ // Only set this if it's not empty, because setting an empty style
+ // will still cause it be resolved and we end up using the platform default.
+ if (!runtimeStyle.isEmpty())
+ QQuickStyle::setStyle(runtimeStyle);
+
+ const char *fallbackStyleEnvVarName = "QT_QUICK_CONTROLS_FALLBACK_STYLE";
+ const QString originalFallbackStyle = qgetenv(fallbackStyleEnvVarName);
+ if (fallbackMethod == EnvVar)
+ qputenv(fallbackStyleEnvVarName, "Material");
+
+ auto cleanup = qScopeGuard([&]() {
+ qputenv(materialThemeEnvVarName, qPrintable(originalMaterialTheme));
+ qputenv(fallbackStyleEnvVarName, qPrintable(originalFallbackStyle));
+ });
+
+ QQuickControlsApplicationHelper helper(this, qmlFilePath, {},
+ QStringList() << dataDirectory() + QLatin1String("/styles"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ auto button = helper.window->property("button").value<QQuickButton*>();
+ QVERIFY(button);
+ // contentItem should be a label with "salmon" text color.
+ QCOMPARE(button->contentItem()->property("color").value<QColor>(), expectedButtonTextColor);
+ QCOMPARE(helper.appWindow->color(), expectedWindowColor);
+
+ // If using run-time style selection, check that QQuickStyle reports the correct values.
+ // QQuickStyle is not supported when using compile-time style selection.
+ if (!runtimeStyle.isEmpty()) {
+ QCOMPARE(QQuickStyle::name(), runtimeStyle);
+ QCOMPARE(QQuickStylePrivate::fallbackStyle(), "Material");
+ }
+}
+
void tst_StyleImports::attachedTypesAvailable_data()
{
QTest::addColumn<QString>("import");
diff --git a/tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt b/tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt
index 6e7f22da64..6352b778d3 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt
+++ b/tests/auto/quickcontrols/styleimportscompiletimematerial/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_styleimportscompiletimematerial LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/styleimportscompiletimematerial/data/importMaterialStyleWithoutControls.qml b/tests/auto/quickcontrols/styleimportscompiletimematerial/data/importMaterialStyleWithoutControls.qml
index 2c8a8dadfd..0d5300ec27 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimematerial/data/importMaterialStyleWithoutControls.qml
+++ b/tests/auto/quickcontrols/styleimportscompiletimematerial/data/importMaterialStyleWithoutControls.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls.Material
diff --git a/tests/auto/quickcontrols/styleimportscompiletimematerial/tst_styleimportscompiletimematerial.cpp b/tests/auto/quickcontrols/styleimportscompiletimematerial/tst_styleimportscompiletimematerial.cpp
index dc582ea6c2..5c78b97a8d 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimematerial/tst_styleimportscompiletimematerial.cpp
+++ b/tests/auto/quickcontrols/styleimportscompiletimematerial/tst_styleimportscompiletimematerial.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qregularexpression.h>
#include <QtTest/qtest.h>
diff --git a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt
index 6e56e6a96e..e1f88295ce 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt
+++ b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_styleimportscompiletimeqmlonly LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/QmlOnly/Button.qml b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/QmlOnly/Button.qml
index 16b83c93d3..3e0fefed02 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/QmlOnly/Button.qml
+++ b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/QmlOnly/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Templates as T
diff --git a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/importQmlOnlyStyleWithoutControls.qml b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/importQmlOnlyStyleWithoutControls.qml
index 4d0492232c..3298a4ac41 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/importQmlOnlyStyleWithoutControls.qml
+++ b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/data/importQmlOnlyStyleWithoutControls.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QmlOnly
diff --git a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
index ec5d51f846..dae8424ec8 100644
--- a/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
+++ b/tests/auto/quickcontrols/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qregularexpression.h>
#include <QtTest/qtest.h>
diff --git a/tests/auto/quickcontrols/translation/CMakeLists.txt b/tests/auto/quickcontrols/translation/CMakeLists.txt
index 745262cdcc..cfb2ca6a0a 100644
--- a/tests/auto/quickcontrols/translation/CMakeLists.txt
+++ b/tests/auto/quickcontrols/translation/CMakeLists.txt
@@ -6,7 +6,7 @@
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_translation LANGUAGES C CXX ASM)
- find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
#####################################################################
diff --git a/tests/auto/quickcontrols/translation/data/comboBox.qml b/tests/auto/quickcontrols/translation/data/comboBox.qml
index e6a58787ae..f410b7f586 100644
--- a/tests/auto/quickcontrols/translation/data/comboBox.qml
+++ b/tests/auto/quickcontrols/translation/data/comboBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/translation/data/dialogButtonBox.qml b/tests/auto/quickcontrols/translation/data/dialogButtonBox.qml
index ababa65019..11e3260e4b 100644
--- a/tests/auto/quickcontrols/translation/data/dialogButtonBox.qml
+++ b/tests/auto/quickcontrols/translation/data/dialogButtonBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/translation/data/dialogButtonBoxWithCustomButtons.qml b/tests/auto/quickcontrols/translation/data/dialogButtonBoxWithCustomButtons.qml
index 6e8dca8ad5..b58e58f2e7 100644
--- a/tests/auto/quickcontrols/translation/data/dialogButtonBoxWithCustomButtons.qml
+++ b/tests/auto/quickcontrols/translation/data/dialogButtonBoxWithCustomButtons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/translation/data/stackView.qml b/tests/auto/quickcontrols/translation/data/stackView.qml
index 44d85c2ea1..85d5c41ce5 100644
--- a/tests/auto/quickcontrols/translation/data/stackView.qml
+++ b/tests/auto/quickcontrols/translation/data/stackView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickcontrols/translation/tst_translation.cpp b/tests/auto/quickcontrols/translation/tst_translation.cpp
index f0798d0367..abd30a7ddb 100644
--- a/tests/auto/quickcontrols/translation/tst_translation.cpp
+++ b/tests/auto/quickcontrols/translation/tst_translation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qsignalspy.h>
#include <QtTest/qtest.h>
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/CMakeLists.txt b/tests/auto/quickdialogs/qquickcolordialogimpl/CMakeLists.txt
index c046db0124..f7e49089fd 100644
--- a/tests/auto/quickdialogs/qquickcolordialogimpl/CMakeLists.txt
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickcolordialogimpl LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialog.qml b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialog.qml
index c7be270a6d..5b0361622f 100644
--- a/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialog.qml
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindow.qml b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindow.qml
new file mode 100644
index 0000000000..4794d20106
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindow.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+Rectangle {
+ width: 480
+ height: 640
+ color: dialog.selectedColor
+
+ property alias dialog: dialog
+
+ function doneAccepted() {
+ dialog.done(ColorDialog.Accepted)
+ }
+
+ function doneRejected() {
+ dialog.done(ColorDialog.Rejected)
+ }
+
+ ColorDialog {
+ id: dialog
+ objectName: "ColorDialog"
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindowVisibleTrue.qml b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindowVisibleTrue.qml
new file mode 100644
index 0000000000..9a5a4d6f4d
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/data/colorDialogWithoutWindowVisibleTrue.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+Rectangle {
+ width: 480
+ height: 640
+ color: dialog.selectedColor
+
+ property alias dialog: dialog
+
+ function doneAccepted() {
+ dialog.done(ColorDialog.Accepted)
+ }
+
+ function doneRejected() {
+ dialog.done(ColorDialog.Rejected)
+ }
+
+ ColorDialog {
+ id: dialog
+ objectName: "ColorDialog"
+ visible: true
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/data/windowSwapping.qml b/tests/auto/quickdialogs/qquickcolordialogimpl/data/windowSwapping.qml
new file mode 100644
index 0000000000..d84e52ca9f
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/data/windowSwapping.qml
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+Window {
+ width: 480
+ height: 640
+
+ property alias dialog: dialog
+
+ function getSubWindow1 () {
+ return subwindow1
+ }
+
+ function getSubWindow2 () {
+ return subwindow2
+ }
+
+ function goToSubWindow1() {
+ dialog.close()
+ dialog.parentWindow = subwindow1
+ dialog.open()
+ }
+
+ function goToSubWindow2() {
+ dialog.close()
+ dialog.parentWindow = subwindow2
+ dialog.open()
+ }
+
+ function resetParentWindow() {
+ dialog.close()
+ dialog.parentWindow = undefined
+ dialog.open()
+ }
+
+ Window {
+ id: subwindow1
+ width: 480
+ height: 640
+ visible: true
+ }
+
+ Window {
+ id: subwindow2
+ width: 480
+ height: 640
+ visible: true
+ }
+
+ ColorDialog {
+ id: dialog
+ objectName: "ColorDialog"
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp b/tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp
index c8a1eb2231..786d0ca207 100644
--- a/tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickcolordialogimpl/tst_qquickcolordialogimpl.cpp
@@ -1,9 +1,10 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtQuickTest/quicktest.h>
#include <QtTest/qsignalspy.h>
+#include <QtQuick/qquickview.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/dialogstestutils_p.h>
@@ -18,6 +19,7 @@
#include <QtQuickTemplates2/private/qquicklabel_p.h>
#include <QtQuickTemplates2/private/qquickoverlay_p.h>
#include <QtQuickTemplates2/private/qquickslider_p.h>
+#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickControls2/qquickstyle.h>
#include <qpa/qplatformintegration.h>
@@ -53,6 +55,9 @@ private slots:
void changeColorFromTextFields();
void windowTitle_data();
void windowTitle();
+ void workingInsideQQuickViewer_data();
+ void workingInsideQQuickViewer();
+ void dialogCanMoveBetweenWindows();
private:
bool closePopup(DialogTestHelper<QQuickColorDialog, QQuickColorDialogImpl> *dialogHelper,
@@ -127,6 +132,23 @@ void tst_QQuickColorDialogImpl::defaultValues()
QVERIFY(dialogHelper.openDialog());
QVERIFY(alphaSlider->isVisible());
+ const bool wayland = QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive) == 0;
+ const bool offscreen = qgetenv("QT_QPA_PLATFORM").compare(QLatin1String("offscreen"), Qt::CaseInsensitive) == 0;
+
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ScreenWindowGrabbing) && !wayland && !offscreen) {
+ QQuickButton *eyeDropperButton = dialogHelper.quickDialog->findChild<QQuickButton *>("eyeDropperButton");
+ QVERIFY(eyeDropperButton);
+
+ QVERIFY2(eyeDropperButton->isVisible(),
+ "The Eye Dropper Button should be visible unless the NoEyeDropperButton option has "
+ "explicitly been set");
+ dialogHelper.dialog->close();
+ QTRY_VERIFY(!dialogHelper.isQuickDialogOpen());
+ dialogHelper.dialog->setOptions(QColorDialogOptions::NoEyeDropperButton);
+ QVERIFY(dialogHelper.openDialog());
+ QVERIFY(!eyeDropperButton->isVisible());
+ }
+
QVERIFY(dialogHelper.quickDialog->isHsl());
QCOMPARE(dialogHelper.quickDialog->alpha(), 1.0);
QCOMPARE(dialogHelper.quickDialog->hue(), 0.0);
@@ -352,17 +374,17 @@ void tst_QQuickColorDialogImpl::changeColorFromTextFields_data()
QTest::newRow("rgbRed") << ColorSpec::Rgb << "255" << "100" << QColor(100, 255, 255);
QTest::newRow("rgbGreen") << ColorSpec::Rgb << "255" << "0" << QColorConstants::Magenta;
QTest::newRow("rgbBlue") << ColorSpec::Rgb << "255" << "200" << QColor(255, 255, 200);
- QTest::newRow("rgbAlpha") << ColorSpec::Rgb << "100%" << "50%" << QColor::fromRgbF(1.0, 1.0, 1.0, 0.5);
+ QTest::newRow("rgbAlpha") << ColorSpec::Rgb << "100%" << "50%" << QColor::fromRgbF(1.0f, 1.0f, 1.0f, 0.5f);
- QTest::newRow("hsvHue") << ColorSpec::Hsv << "0°" << "60°" << QColor::fromHsvF(.2, 0.0, 1.0);
- QTest::newRow("hsvSaturation") << ColorSpec::Hsv << "0%" << "50%" << QColor::fromHsvF(0.0, 0.5, 1.0);
- QTest::newRow("hsvValue") << ColorSpec::Hsv << "100%" << "90%" << QColor::fromHsvF(0.0, 0.0, 0.9, 1.0);
- QTest::newRow("hsvAlpha") << ColorSpec::Hsv << "100%" << "40%" << QColor::fromHsvF(0.0, 0.0, 1.0, 0.4);
+ QTest::newRow("hsvHue") << ColorSpec::Hsv << "0°" << "60°" << QColor::fromHsvF(.2f, 0.0f, 1.0f);
+ QTest::newRow("hsvSaturation") << ColorSpec::Hsv << "0%" << "50%" << QColor::fromHsvF(0.0f, 0.5f, 1.0f);
+ QTest::newRow("hsvValue") << ColorSpec::Hsv << "100%" << "90%" << QColor::fromHsvF(0.0f, 0.0f, 0.9f, 1.0f);
+ QTest::newRow("hsvAlpha") << ColorSpec::Hsv << "100%" << "40%" << QColor::fromHsvF(0.0f, 0.0f, 1.0f, 0.4f);
- QTest::newRow("hslHue") << ColorSpec::Hsl << "0°" << "90°" << QColor::fromHslF(.25, 1.0, 1.0);
- QTest::newRow("hslSaturation") << ColorSpec::Hsl << "0%" << "40%" << QColor::fromHslF(0.0, 0.4, 1.0);
- QTest::newRow("hslLightness") << ColorSpec::Hsl << "100%" << "80%" << QColor::fromHslF(0.0, 0.0, 0.8, 1.0);
- QTest::newRow("hslAlpha") << ColorSpec::Hsl << "100%" << "60%" << QColor::fromHslF(0.0, 0.0, 1.0, 0.6);
+ QTest::newRow("hslHue") << ColorSpec::Hsl << "0°" << "90°" << QColor::fromHslF(.25f, 1.0f, 1.0f);
+ QTest::newRow("hslSaturation") << ColorSpec::Hsl << "0%" << "40%" << QColor::fromHslF(0.0f, 0.4f, 1.0f);
+ QTest::newRow("hslLightness") << ColorSpec::Hsl << "100%" << "80%" << QColor::fromHslF(0.0f, 0.0f, 0.8f, 1.0f);
+ QTest::newRow("hslAlpha") << ColorSpec::Hsl << "100%" << "60%" << QColor::fromHslF(0.0f, 0.0f, 1.0f, 0.6f);
}
void tst_QQuickColorDialogImpl::changeColorFromTextFields()
@@ -456,6 +478,60 @@ void tst_QQuickColorDialogImpl::windowTitle()
CLOSE_DIALOG("Ok");
}
+void tst_QQuickColorDialogImpl::workingInsideQQuickViewer_data()
+{
+ QTest::addColumn<bool>("initialVisible");
+ QTest::addColumn<QString>("urlToQmlFile");
+ QTest::newRow("visible: false") << false << "colorDialogWithoutWindow.qml";
+ QTest::newRow("visible: true") << true << "colorDialogWithoutWindowVisibleTrue.qml";
+}
+
+void tst_QQuickColorDialogImpl::workingInsideQQuickViewer() // QTBUG-106598
+{
+ QFETCH(bool, initialVisible);
+ QFETCH(QString, urlToQmlFile);
+
+ QQuickView dialogView;
+ dialogView.setSource(testFileUrl(urlToQmlFile));
+ dialogView.show();
+
+ auto dialog = dialogView.findChild<QQuickColorDialog *>("ColorDialog");
+ QVERIFY(dialog);
+ QCOMPARE(dialog->isVisible(), initialVisible);
+
+ dialog->open();
+ QVERIFY(dialog->isVisible());
+}
+
+void tst_QQuickColorDialogImpl::dialogCanMoveBetweenWindows()
+{
+ DialogTestHelper<QQuickColorDialog, QQuickColorDialogImpl> dialogHelper(this, "windowSwapping.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ QCOMPARE(dialogHelper.quickDialog->parent(), dialogHelper.window());
+ QVariant subWindow1;
+ QVariant subWindow2;
+
+ QMetaObject::invokeMethod(dialogHelper.window(), "getSubWindow1", Q_RETURN_ARG(QVariant, subWindow1));
+ QMetaObject::invokeMethod(dialogHelper.window(), "getSubWindow2", Q_RETURN_ARG(QVariant, subWindow2));
+
+ // Confirm that the dialog can swap to different windows
+ QMetaObject::invokeMethod(dialogHelper.window(), "goToSubWindow1");
+ QCOMPARE(dialogHelper.dialog->parentWindow(), qvariant_cast<QQuickWindow *>(subWindow1));
+
+ QMetaObject::invokeMethod(dialogHelper.window(), "goToSubWindow2");
+ QCOMPARE(dialogHelper.dialog->parentWindow(), qvariant_cast<QQuickWindow *>(subWindow2));
+
+ QMetaObject::invokeMethod(dialogHelper.window(), "resetParentWindow");
+ QCOMPARE(dialogHelper.quickDialog->parent(), dialogHelper.window());
+
+ CLOSE_DIALOG("Ok");
+}
+
QTEST_MAIN(tst_QQuickColorDialogImpl)
#include "tst_qquickcolordialogimpl.moc"
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt b/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt
index c5f9bdb7f6..fa6888a537 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickfiledialogimpl LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml
index d164acb986..4aa4d9dfe0 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml
index 4e13824df6..9356a6e93a 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml
index 78af831403..367decb18d 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTitle.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTitle.qml
index 0695d44a2a..4df73c6a9e 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTitle.qml
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTitle.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml
index 4cb84d0ffb..fbdbd406a0 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/fileDialog.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/fileDialog.qml
index a863e10129..104b0449f0 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/data/fileDialog.qml
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/fileDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml
index 62ef23fff8..e0c13fdff2 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
index 31c2a93d1c..f7f2bd8c85 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qloggingcategory.h>
#include <QtTest/qtest.h>
@@ -89,8 +89,20 @@ private slots:
void setSelectedFile();
void selectNewFileViaTextField_data();
void selectNewFileViaTextField();
+ void selectExistingFileShouldWarnUserWhenFileModeEqualsSaveFile();
+ void fileNameTextFieldOnlyChangesWhenSelectingFiles();
+ void setSchemeForSelectedFile();
private:
+ enum DelegateOrderPolicy
+ {
+ ShowDirectoriesFirst,
+ ShowFilesFirst
+ };
+
+ QStringList tempDirExpectedVisibleFiles(DelegateOrderPolicy order) const;
+ QStringList tempSubDirExpectedVisibleFiles(DelegateOrderPolicy order) const;
+
QTemporaryDir tempDir;
QScopedPointer<QFile> tempFile1;
QScopedPointer<QFile> tempFile2;
@@ -106,10 +118,26 @@ private:
QDir oldCurrentDir;
+#if QT_CONFIG(shortcut)
const QKeySequence goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
const QKeySequence editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+#endif
};
+QStringList tst_QQuickFileDialogImpl::tempDirExpectedVisibleFiles(DelegateOrderPolicy order) const
+{
+ return order == ShowDirectoriesFirst
+ ? QStringList { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }
+ : QStringList { tempFile1->fileName(), tempFile2->fileName(), tempSubDir.path() };
+}
+
+QStringList tst_QQuickFileDialogImpl::tempSubDirExpectedVisibleFiles(DelegateOrderPolicy order) const
+{
+ return order == ShowDirectoriesFirst
+ ? QStringList { tempSubSubDir.path(), tempSubFile1->fileName(), tempSubFile2->fileName() }
+ : QStringList { tempSubFile1->fileName(), tempSubFile2->fileName(), tempSubSubDir.path() };
+}
+
// We don't want to fail on warnings until QTBUG-98964 is fixed,
// as we deliberately prevent deferred execution in some of the tests here,
// which causes warnings.
@@ -135,13 +163,13 @@ void tst_QQuickFileDialogImpl::initTestCase()
/*
Create a couple of files within the temporary directory. The structure is:
- [temp directory]
- ├── sub-dir
- │ ├── sub-sub-dir
- │ ├── sub-file1.txt
- │ └── sub-file2.txt
- ├── file1.txt
- └── file2.txt
+ [temp directory] (tempDir)
+ ├── sub-dir (tempSubDir)
+ │ ├── sub-sub-dir (tempSubSubDir)
+ │ ├── sub-file1.txt (tempSubFile1)
+ │ └── sub-file2.txt (tempSubFile2)
+ ├── file1.txt (tempFile1)
+ └── file2.txt (tempFile2)
*/
tempSubDir = QDir(tempDir.path());
QVERIFY2(tempSubDir.mkdir("sub-dir"), qPrintable(QString::fromLatin1(
@@ -232,8 +260,8 @@ void tst_QQuickFileDialogImpl::cleanupTestCase()
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, expectedCurrentIndex, fileDelegate)); \
QVERIFY2(fileDelegate->hasActiveFocus(), qPrintable(QString::fromLatin1( \
"Expected delegate at index %1 to have focus, but %2 has it") \
- .arg(QString::number(expectedCurrentIndex)) \
- .arg(QDebug::toString(dialogHelper.window()->activeFocusItem())))); \
+ .arg(QString::number(expectedCurrentIndex), \
+ QDebug::toString(dialogHelper.window()->activeFocusItem())))); \
QVERIFY(fileDelegate->isHighlighted()); \
}
@@ -365,6 +393,7 @@ void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
// below fail due to it being hidden when it loses activeFocus.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
@@ -384,6 +413,7 @@ void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
QVERIFY(!dialogHelper.dialog->isVisible());
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+#endif
}
void tst_QQuickFileDialogImpl::chooseFileViaEnter()
@@ -418,8 +448,8 @@ void tst_QQuickFileDialogImpl::bindCurrentFolder_data()
const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDir.path());
const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName());
- const QStringList currentDirFiles = { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() };
- const QStringList tempSubDirFiles = { tempSubSubDir.path(), tempSubFile1->fileName(), tempSubFile2->fileName() };
+ const QStringList currentDirFiles = tempDirExpectedVisibleFiles(ShowDirectoriesFirst);
+ const QStringList tempSubDirFiles = tempSubDirExpectedVisibleFiles(ShowDirectoriesFirst);
// Setting the folder to "sub-dir" should result in "sub-file1.txt" and "sub-file2.txt" being visible.
QTest::addRow("sub-dir") << tempSubDirUrl << tempSubDirUrl << tempSubDirFiles;
@@ -505,9 +535,7 @@ void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick()
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, subDirIndex, subDirDelegate));
COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
QVERIFY(doubleClickButton(subDirDelegate));
- const QStringList expectedVisibleFiles = showDirsFirst
- ? QStringList { tempSubSubDir.path(), tempSubFile1->fileName(), tempSubFile2->fileName() }
- : QStringList { tempSubFile1->fileName(), tempSubFile2->fileName(), tempSubSubDir.path() };
+ const QStringList expectedVisibleFiles = tempSubDirExpectedVisibleFiles(showDirsFirst ? ShowDirectoriesFirst : ShowFilesFirst);
QString failureMessage;
QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
// The first file in the directory should now be selected.
@@ -529,9 +557,11 @@ void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
// See comment in chooseFileViaTextEdit for why we check for this.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
@@ -544,6 +574,9 @@ void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
+ tempSubDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
// The first file in the directory should be selected, which is "sub-sub-dir".
// Note that the TextEdit will still have focus, so we can't use VERIFY_FILE_SELECTED_AND_FOCUSED.
VERIFY_FILE_SELECTED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()));
@@ -566,9 +599,9 @@ void tst_QQuickFileDialogImpl::chooseFolderViaEnter()
// Select the delegate by pressing enter.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
- const QStringList expectedVisibleFiles = { tempSubSubDir.path(), tempSubFile1->fileName(), tempSubFile2->fileName() };
QString failureMessage;
- QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
+ tempSubDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
// The first file in the new directory should be selected, which is "sub-sub-dir".
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()), 0);
// Since we only chose a folder, the dialog should still be open.
@@ -587,8 +620,10 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
// See comment in chooseFileViaTextEdit for why we check for this.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
@@ -615,8 +650,10 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
// The breadcrumbs should be visible after opening, not the text edit.
QVERIFY(!breadcrumbBar->textField()->isVisible());
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
QVERIFY(breadcrumbBar->textField()->isVisible());
// The text edit should show the directory that contains the last file that was selected.
QCOMPARE(breadcrumbBar->textField()->text(), tempDir.path());
@@ -628,6 +665,9 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
+ tempSubDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
// The first file in the directory should be selected, which is "sub-sub-dir".
// Note that the TextEdit will still have focus, so we can't use VERIFY_FILE_SELECTED_AND_FOCUSED.
VERIFY_FILE_SELECTED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()));
@@ -647,8 +687,10 @@ void tst_QQuickFileDialogImpl::cancelDialogWhileTextEditHasFocus()
// See comment in chooseFileViaTextEdit for why we check for this.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->hasActiveFocus());
@@ -727,9 +769,13 @@ void tst_QQuickFileDialogImpl::goUp()
QVERIFY(QQuickTest::qWaitForPolish(barListView));
QVERIFY(clickButton(breadcrumbBar->upButton()));
// The previous directory that we were in should now be selected (matches e.g. Windows and Ubuntu).
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
+ tempDirExpectedVisibleFiles(showDirsFirst ? ShowDirectoriesFirst : ShowFilesFirst), failureMessage), qPrintable(failureMessage));
int expectedCurrentIndex = showDirsFirst ? 0 : 2;
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), expectedCurrentIndex);
+#if QT_CONFIG(shortcut)
// Go up a directory via the keyboard shortcut.
QDir tempParentDir(tempDir.path());
QVERIFY(tempParentDir.cdUp());
@@ -742,6 +788,7 @@ void tst_QQuickFileDialogImpl::goUp()
QVERIFY(expectedCurrentIndex != -1);
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempParentDir.path()), QUrl::fromLocalFile(tempDir.path()), expectedCurrentIndex);
}
+#endif
}
void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
@@ -753,8 +800,10 @@ void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
// See comment in chooseFileViaTextEdit for why we check for this.
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->hasActiveFocus());
@@ -821,6 +870,7 @@ void tst_QQuickFileDialogImpl::goUpIntoLargeFolder()
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(largeTempDirLargeSubDir.path()),
QUrl::fromLocalFile(largeTempDirLargeSubDir.path() + "/sub-dir000"), 0);
+#if QT_CONFIG(shortcut)
// Go up a directory via the keyboard shortcut.
QTest::keySequence(dialogHelper.window(), goUpKeySequence);
QString failureMessage;
@@ -828,6 +878,7 @@ void tst_QQuickFileDialogImpl::goUpIntoLargeFolder()
largeTempDirPaths, failureMessage), qPrintable(failureMessage));
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(largeTempDir.path()),
QUrl::fromLocalFile(largeTempDirLargeSubDir.path()), largeTempDirLargeSubDirIndex);
+#endif
}
void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
@@ -837,16 +888,20 @@ void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
OPEN_QUICK_DIALOG();
VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(breadcrumbBar->textField()->text(), dialogHelper.dialog->currentFolder().toLocalFile());
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
+#if QT_CONFIG(shortcut)
// Ctrl+L shouldn't hide it.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
QVERIFY(breadcrumbBar->textField()->isVisible());
// Cancel it with the escape key.
@@ -854,8 +909,10 @@ void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
QVERIFY(!breadcrumbBar->textField()->isVisible());
QVERIFY(dialogHelper.dialog->isVisible());
+#if QT_CONFIG(shortcut)
// Make it visible.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
QVERIFY(breadcrumbBar->textField()->isVisible());
// Cancel it with the escape key again.
@@ -878,7 +935,7 @@ void tst_QQuickFileDialogImpl::bindNameFilters()
// Only "sub-dir", "text1.txt" and "text2.txt" should be visible, since *.txt is the first filter.
QString failureMessage;
QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
- { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
+ tempDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
}
void tst_QQuickFileDialogImpl::changeNameFilters()
@@ -929,7 +986,7 @@ void tst_QQuickFileDialogImpl::changeNameFilters()
// Only "sub-dir", "text1.txt" and "text2.txt" should be visible, since *.txt is the first filter.
QString failureMessage;
QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
- { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
+ tempDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
// Open the ComboBox's popup.
const QQuickComboBox *comboBox = dialogHelper.quickDialog->findChild<QQuickComboBox*>();
@@ -964,7 +1021,7 @@ void tst_QQuickFileDialogImpl::changeNameFilters()
}
QTRY_VERIFY(!comboBox->popup()->isVisible());
QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
- { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
+ tempDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
}
void tst_QQuickFileDialogImpl::changeNameFiltersAfterChangingFolder()
@@ -976,7 +1033,7 @@ void tst_QQuickFileDialogImpl::changeNameFiltersAfterChangingFolder()
// Go into the "sub-dir" folder.
QString failureMessage;
QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
- { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
+ tempDirExpectedVisibleFiles(ShowDirectoriesFirst), failureMessage), qPrintable(failureMessage));
QQuickFileDialogDelegate *subDirDelegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 0, subDirDelegate));
QVERIFY(doubleClickButton(subDirDelegate));
@@ -1154,10 +1211,12 @@ void tst_QQuickFileDialogImpl::itemsDisabledWhenNecessary()
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(anotherTempDir.path()));
COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(anotherTempDir.path()));
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L. The Open button should now be disabled.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(openButton->isEnabled(), false);
+#endif
// Hide it with the escape key. The Open button should now be enabled.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
@@ -1168,15 +1227,17 @@ void tst_QQuickFileDialogImpl::itemsDisabledWhenNecessary()
void tst_QQuickFileDialogImpl::fileMode_data()
{
QTest::addColumn<QQuickFileDialog::FileMode>("fileMode");
+ QTest::addColumn<QString>("acceptButtonText");
- QTest::newRow("OpenFile") << QQuickFileDialog::OpenFile;
- QTest::newRow("OpenFiles") << QQuickFileDialog::OpenFiles;
- QTest::newRow("SaveFile") << QQuickFileDialog::SaveFile;
+ QTest::newRow("OpenFile") << QQuickFileDialog::OpenFile << "Open";
+ QTest::newRow("OpenFiles") << QQuickFileDialog::OpenFiles << "Open";
+ QTest::newRow("SaveFile") << QQuickFileDialog::SaveFile << "Save";
}
void tst_QQuickFileDialogImpl::fileMode()
{
QFETCH(QQuickFileDialog::FileMode, fileMode);
+ QFETCH(QString, acceptButtonText);
// Open the dialog.
FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
@@ -1191,13 +1252,13 @@ void tst_QQuickFileDialogImpl::fileMode()
COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile1->fileName()) });
- // All modes should support opening an existing file, so the Open button should be enabled.
+ // All modes should support opening an existing file, so the accept button should be enabled.
QVERIFY(dialogHelper.quickDialog->footer());
auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
QVERIFY(dialogButtonBox);
- QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Open");
- QVERIFY(openButton);
- QCOMPARE(openButton->isEnabled(), true);
+ QQuickAbstractButton *acceptButton = findDialogButton(dialogButtonBox, acceptButtonText);
+ QVERIFY(acceptButton);
+ QCOMPARE(acceptButton->isEnabled(), true);
// Only the OpenFiles mode should allow multiple files to be selected, however.
QQuickFileDialogDelegate *tempFile2Delegate = nullptr;
@@ -1219,8 +1280,10 @@ void tst_QQuickFileDialogImpl::fileMode()
COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
}
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
@@ -1342,6 +1405,9 @@ void tst_QQuickFileDialogImpl::setSelectedFile()
};
FileDialogTestHelper dialogHelper(
this, "setSelectedFile.qml", {}, initialProperties);
+
+ dialogHelper.dialog->setOptions(QFileDialogOptions::DontConfirmOverwrite);
+
OPEN_QUICK_DIALOG();
// The selected file should be what we set.
@@ -1396,6 +1462,7 @@ void tst_QQuickFileDialogImpl::selectNewFileViaTextField_data()
void tst_QQuickFileDialogImpl::selectNewFileViaTextField()
{
QFETCH(QQuickFileDialog::FileMode, fileMode);
+ QFETCH(QString, acceptButtonText);
// Open the dialog.
FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
@@ -1415,22 +1482,208 @@ void tst_QQuickFileDialogImpl::selectNewFileViaTextField()
"The TextField for file name should only be visible when the FileMode is 'SaveFile'");
if (fileMode == QQuickFileDialog::SaveFile) {
+ QVERIFY(dialogHelper.quickDialog->footer());
+ auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
+ QVERIFY(dialogButtonBox);
+ QQuickAbstractButton *acceptButton = findDialogButton(dialogButtonBox, acceptButtonText);
+ QVERIFY(acceptButton);
+ QCOMPARE(acceptButton->isEnabled(), false);
+
const QPoint textFieldCenterPos =
fileNameTextField->mapToScene({ fileNameTextField->width() / 2, fileNameTextField->height() / 2 }).toPoint();
QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, textFieldCenterPos);
QTRY_VERIFY(fileNameTextField->hasActiveFocus());
+ QCOMPARE(acceptButton->isEnabled(), false);
const QByteArray newFileName("foo.txt");
for (const auto &c : newFileName)
QTest::keyClick(dialogHelper.window(), c);
+ QCOMPARE(acceptButton->isEnabled(), true);
+
QTest::keyClick(dialogHelper.window(), Qt::Key_Enter);
+ QCOMPARE(acceptButton->isEnabled(), true);
QTRY_COMPARE(fileNameTextField->text(), newFileName);
QCOMPARE(dialogHelper.dialog->selectedFile().fileName(), newFileName);
+
+ QVERIFY(fileNameTextField->hasActiveFocus());
+ for (int i = 0; i < newFileName.size(); i++)
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Backspace);
+ QCOMPARE(acceptButton->isEnabled(), false);
}
}
+void tst_QQuickFileDialogImpl::selectExistingFileShouldWarnUserWhenFileModeEqualsSaveFile()
+{
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ dialogHelper.dialog->setFileMode(QQuickFileDialog::SaveFile);
+ dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(tempFile1->fileName()));
+
+ OPEN_QUICK_DIALOG();
+ QQuickTest::qWaitForPolish(dialogHelper.window());
+
+ QSignalSpy acceptedSpy(dialogHelper.dialog, SIGNAL(accepted()));
+
+ auto *dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox *>();
+ QVERIFY(dialogButtonBox);
+
+ auto *confirmationDialog = dialogHelper.quickDialog->findChild<QQuickDialog *>("confirmationDialog");
+ QVERIFY(confirmationDialog);
+
+ auto *openButton = dialogButtonBox->standardButton(QPlatformDialogHelper::Open);
+ QVERIFY(openButton);
+
+ auto *confirmationButtonBox = qobject_cast<QQuickDialogButtonBox *>(confirmationDialog->footer());
+ QVERIFY(confirmationButtonBox);
+
+ const QPoint openButtonCenterPos =
+ openButton->mapToScene({ openButton->width() / 2, openButton->height() / 2 }).toPoint();
+
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, openButtonCenterPos);
+
+ QTRY_VERIFY(confirmationDialog->isOpened());
+ QVERIFY(dialogHelper.dialog->isVisible());
+
+ // Yes button should have focus by default
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Space, Qt::NoModifier);
+
+ QTRY_VERIFY(!confirmationDialog->isOpened());
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QCOMPARE(acceptedSpy.count(), 1);
+
+ // Try again, but click "No" this time.
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Enter, Qt::NoModifier);
+
+ QTRY_VERIFY(confirmationDialog->isOpened());
+ QVERIFY(dialogHelper.dialog->isVisible());
+
+ // Make No button trigger a clicked() event.
+ auto *confirmationNoButton = confirmationButtonBox->standardButton(QPlatformDialogHelper::No);
+ QVERIFY(confirmationNoButton);
+ QVERIFY(clickButton(confirmationNoButton));
+
+ // FileDialog is still opened
+ QTRY_VERIFY(!confirmationDialog->isOpened());
+ QVERIFY(dialogHelper.dialog->isVisible());
+ QCOMPARE(acceptedSpy.count(), 1);
+
+ // Try again
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Enter, Qt::NoModifier);
+
+ QTRY_VERIFY(confirmationDialog->isOpened());
+ QVERIFY(dialogHelper.dialog->isVisible());
+
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Space, Qt::NoModifier);
+
+ QTRY_VERIFY(!confirmationDialog->isOpened());
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QCOMPARE(acceptedSpy.count(), 2);
+
+ // Make sure that DontConfirmOverwrite works
+ dialogHelper.dialog->setOptions(QFileDialogOptions::DontConfirmOverwrite);
+
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Enter, Qt::NoModifier);
+ QTRY_VERIFY(!confirmationDialog->isOpened());
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QCOMPARE(acceptedSpy.count(), 3);
+}
+
+void tst_QQuickFileDialogImpl::fileNameTextFieldOnlyChangesWhenSelectingFiles()
+{
+ const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName());
+ const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDir.path());
+ const auto tempFile11Url = QUrl::fromLocalFile(tempFile1->fileName());
+
+ const QVariantMap initialProperties = {
+ { "tempFile1Url", QVariant::fromValue(tempSubFile1Url) },
+ { "fileMode", QVariant::fromValue(QQuickFileDialog::SaveFile) }
+ };
+ FileDialogTestHelper dialogHelper(this, "setSelectedFile.qml", {}, initialProperties);
+
+ OPEN_QUICK_DIALOG();
+ QQuickTest::qWaitForPolish(dialogHelper.window());
+
+ QQuickTextField *fileNameTextField =
+ dialogHelper.quickDialog->findChild<QQuickTextField *>("fileNameTextField");
+ QVERIFY(fileNameTextField);
+
+ auto getSelectedFileInfo = [&dialogHelper]() {
+ return QFileInfo(dialogHelper.dialog->selectedFile().toLocalFile());
+ };
+
+ QVERIFY(getSelectedFileInfo().isFile());
+ QCOMPARE(fileNameTextField->text(), tempSubFile1Url.fileName());
+ QCOMPARE(dialogHelper.dialog->selectedFile(), tempSubFile1Url);
+
+ auto *breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar *>();
+ QVERIFY(breadcrumbBar);
+
+ // Pressing the up button causes tempSubDir to be selected
+ QVERIFY(clickButton(breadcrumbBar->upButton()));
+
+ QVERIFY(getSelectedFileInfo().isDir());
+ QCOMPARE(fileNameTextField->text(), tempSubFile1Url.fileName());
+ QCOMPARE(dialogHelper.dialog->selectedFile(), tempSubDirUrl);
+
+ // Change the selected file from the outside
+ dialogHelper.dialog->close();
+ dialogHelper.dialog->setSelectedFile(tempFile11Url);
+ dialogHelper.openDialog();
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ QVERIFY(getSelectedFileInfo().isFile());
+ QCOMPARE(fileNameTextField->text(), tempFile11Url.fileName());
+ QCOMPARE(dialogHelper.dialog->selectedFile(), tempFile11Url);
+}
+
+void tst_QQuickFileDialogImpl::setSchemeForSelectedFile()
+{
+ const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName());
+
+ const QVariantMap initialProperties = {
+ { "tempFile1Url", QVariant::fromValue(tempSubFile1Url) },
+ { "fileMode", QVariant::fromValue(QQuickFileDialog::SaveFile) }
+ };
+ FileDialogTestHelper dialogHelper(this, "setSelectedFile.qml", {}, initialProperties);
+
+ OPEN_QUICK_DIALOG();
+ QQuickTest::qWaitForPolish(dialogHelper.window());
+
+ QQuickTextField *fileNameTextField =
+ dialogHelper.quickDialog->findChild<QQuickTextField *>("fileNameTextField");
+ QVERIFY(fileNameTextField);
+
+ QVERIFY(!tempSubFile1Url.scheme().isEmpty());
+ QVERIFY(!dialogHelper.dialog->selectedFile().scheme().isEmpty());
+ QCOMPARE(tempSubFile1Url, dialogHelper.dialog->selectedFile());
+
+ fileNameTextField->clear();
+
+ const QPoint textFieldCenterPos =
+ fileNameTextField->mapToScene({ fileNameTextField->width() / 2, fileNameTextField->height() / 2 }).toPoint();
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, textFieldCenterPos);
+
+ const QByteArray newFileName("helloworld.txt");
+ for (const auto &c : newFileName)
+ QTest::keyClick(dialogHelper.window(), c);
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Enter, Qt::NoModifier);
+
+ QTRY_COMPARE(fileNameTextField->text(), QString::fromLatin1(newFileName));
+
+ const auto newFilePath =
+ QUrl::fromLocalFile(QFileInfo(tempSubFile1Url.toLocalFile()).dir().absolutePath() + u'/' + newFileName);
+ QVERIFY(!newFilePath.scheme().isEmpty());
+ QVERIFY(!dialogHelper.dialog->selectedFile().scheme().isEmpty());
+ QCOMPARE(dialogHelper.dialog->selectedFile(), newFilePath);
+}
+
QTEST_MAIN(tst_QQuickFileDialogImpl)
#include "tst_qquickfiledialogimpl.moc"
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/CMakeLists.txt b/tests/auto/quickdialogs/qquickfolderdialogimpl/CMakeLists.txt
index d3a2f9f2ae..48ffeeeef8 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/CMakeLists.txt
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickfolderdialogimpl LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/data/acceptRejectLabel.qml b/tests/auto/quickdialogs/qquickfolderdialogimpl/data/acceptRejectLabel.qml
index 0b056fc8e1..f84c55d890 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/data/acceptRejectLabel.qml
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/data/acceptRejectLabel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindCurrentFolder.qml b/tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindCurrentFolder.qml
index 4cc5027547..a38629c53b 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindCurrentFolder.qml
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindCurrentFolder.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindTitle.qml b/tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindTitle.qml
index a35d58d759..38ae9f3288 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindTitle.qml
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/data/bindTitle.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/data/folderDialog.qml b/tests/auto/quickdialogs/qquickfolderdialogimpl/data/folderDialog.qml
index 984bba0d58..85e1532ffc 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/data/folderDialog.qml
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/data/folderDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp b/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
index 01116aa7e9..2afcd81a44 100644
--- a/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/tst_qquickfolderdialogimpl.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
@@ -185,9 +185,9 @@ class FolderDialogSignalHelper
public:
FolderDialogSignalHelper(const FolderDialogTestHelper &dialogTestHelper) :
dialogSelectedFolderChangedSpy(dialogTestHelper.dialog, SIGNAL(selectedFolderChanged())),
- quickDialogSelectedFolderChangedSpy(dialogTestHelper.quickDialog, SIGNAL(selectedFolderChanged(const QUrl &))),
+ quickDialogSelectedFolderChangedSpy(dialogTestHelper.quickDialog, SIGNAL(selectedFolderChanged(QUrl))),
dialogCurrentFolderChangedSpy(dialogTestHelper.dialog, SIGNAL(currentFolderChanged())),
- quickDialogCurrentFolderChangedSpy(dialogTestHelper.quickDialog, SIGNAL(currentFolderChanged(const QUrl &)))
+ quickDialogCurrentFolderChangedSpy(dialogTestHelper.quickDialog, SIGNAL(currentFolderChanged(QUrl)))
{
if (!dialogSelectedFolderChangedSpy.isValid())
errorMessage = "selectedFolderChanged signal of dialog is not valid";
@@ -344,6 +344,7 @@ void tst_QQuickFolderDialogImpl::changeFolderViaTextEdit()
QVERIFY(dialogHelper.openDialog());
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
@@ -356,6 +357,7 @@ void tst_QQuickFolderDialogImpl::changeFolderViaTextEdit()
// Enter the path to the folder in the text edit.
enterText(dialogHelper.window(), tempSubDir2.path());
QCOMPARE(breadcrumbBar->textField()->text(), tempSubDir2.path());
+#endif
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
@@ -417,9 +419,11 @@ void tst_QQuickFolderDialogImpl::cancelDialogWhileTextEditHasFocus()
QVERIFY(dialogHelper.openDialog());
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
@@ -471,6 +475,7 @@ void tst_QQuickFolderDialogImpl::goUp()
QTRY_VERIFY(findViewDelegateItem(folderDialogListView, 0, subDirDelegate));
QCOMPARE(subDirDelegate->isHighlighted(), true);
+#if QT_CONFIG(shortcut)
// Go up a directory via the keyboard shortcut.
const auto goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
QTest::keySequence(dialogHelper.window(), goUpKeySequence);
@@ -478,6 +483,7 @@ void tst_QQuickFolderDialogImpl::goUp()
QVERIFY(tempParentDir.cdUp());
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempParentDir.path()));
COMPARE_URL(dialogHelper.dialog->selectedFolder(), QUrl::fromLocalFile(tempDir.path()));
+#endif
}
void tst_QQuickFolderDialogImpl::goUpWhileTextEditHasFocus()
@@ -610,27 +616,33 @@ void tst_QQuickFolderDialogImpl::keyAndShortcutHandling()
QVERIFY(dialogHelper.openDialog());
QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+#endif
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(breadcrumbBar->textField()->text(), dialogHelper.dialog->currentFolder().toLocalFile());
QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
+#if QT_CONFIG(shortcut)
// Ctrl+L shouldn't hide it.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
+#endif
// Cancel it with the escape key.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
QVERIFY(!breadcrumbBar->textField()->isVisible());
QVERIFY(dialogHelper.dialog->isVisible());
+#if QT_CONFIG(shortcut)
// Make it visible.
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
+#endif
// Cancel it with the escape key again.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
@@ -795,11 +807,13 @@ void tst_QQuickFolderDialogImpl::itemsDisabledWhenNecessary()
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+#if QT_CONFIG(shortcut)
// Get the text edit visible with Ctrl+L. The Open button should now be disabled.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(openButton->isEnabled(), false);
+#endif
// Hide it with the escape key. The Open button should now be enabled.
QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
diff --git a/tests/auto/quickdialogs/qquickfontdialogimpl/CMakeLists.txt b/tests/auto/quickdialogs/qquickfontdialogimpl/CMakeLists.txt
index cec40964cd..552b5c9faf 100644
--- a/tests/auto/quickdialogs/qquickfontdialogimpl/CMakeLists.txt
+++ b/tests/auto/quickdialogs/qquickfontdialogimpl/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickfontdialogimpl LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quickdialogs/qquickfontdialogimpl/data/fontDialog.qml b/tests/auto/quickdialogs/qquickfontdialogimpl/data/fontDialog.qml
index 49ba215d6e..3cb6555e7a 100644
--- a/tests/auto/quickdialogs/qquickfontdialogimpl/data/fontDialog.qml
+++ b/tests/auto/quickdialogs/qquickfontdialogimpl/data/fontDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp b/tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp
index 5de4fe291b..dc0d547fe9 100644
--- a/tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
diff --git a/tests/auto/quickdialogs/qquickmessagedialogimpl/CMakeLists.txt b/tests/auto/quickdialogs/qquickmessagedialogimpl/CMakeLists.txt
index 50a2fc1a2c..5685404073 100644
--- a/tests/auto/quickdialogs/qquickmessagedialogimpl/CMakeLists.txt
+++ b/tests/auto/quickdialogs/qquickmessagedialogimpl/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickmessagedialogimpl LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialog.qml b/tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialog.qml
index baa7ac5e90..015ee79dba 100644
--- a/tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialog.qml
+++ b/tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialogWithButtons.qml b/tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialogWithButtons.qml
new file mode 100644
index 0000000000..33bc2f4cf2
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickmessagedialogimpl/data/messageDialogWithButtons.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ width: 640
+ height: 480
+
+ property alias dialog: dialog
+
+ MessageDialog {
+ id: dialog
+ objectName: "MessageDialog"
+ buttons: MessageDialog.Yes | MessageDialog.No | MessageDialog.Discard | MessageDialog.Apply
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp b/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
index b2f3d6456c..92af0036ba 100644
--- a/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
@@ -8,6 +8,7 @@
#include <QtQuickDialogs2QuickImpl/private/qquickmessagedialogimpl_p.h>
#include <QtQuickTest/quicktest.h>
#include <QtQuickControls2/qquickstyle.h>
+#include <QtQuickTemplates2/private/qquickdialog_p_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/dialogstestutils_p.h>
@@ -39,6 +40,7 @@ private slots:
void changeInformativeText();
void changeStandardButtons();
void detailedText();
+ void resultReflectsLastStandardButtonPressed();
};
// We don't want to fail on warnings until QTBUG-98964 is fixed,
@@ -269,6 +271,101 @@ void tst_QQuickMessageDialogImpl::detailedText()
dialogHelper.dialog->close();
}
+void tst_QQuickMessageDialogImpl::resultReflectsLastStandardButtonPressed()
+{
+ DialogTestHelper<QQuickMessageDialog, QQuickMessageDialogImpl> dialogHelper(
+ this, "messageDialogWithButtons.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ QSignalSpy acceptedSpy(dialogHelper.dialog, SIGNAL(accepted()));
+ QSignalSpy rejectedSpy(dialogHelper.dialog, SIGNAL(rejected()));
+ QSignalSpy resultChangedSpy(dialogHelper.dialog, SIGNAL(resultChanged()));
+
+ auto buttonBox = dialogHelper.quickDialog->findChild<QQuickDialogButtonBox *>("buttonBox");
+ QVERIFY(buttonBox);
+
+ QQuickTest::qWaitForPolish(dialogHelper.window());
+
+ bool yesFound = false;
+ bool noFound = false;
+ bool discardFound = false;
+ bool applyFound = false;
+
+ int expectedNumberOfAcceptedSignals = 0;
+ int expectedNumberOfRejectedSignals = 0;
+
+ // The dialogButtonBox has different layouts depending on platform. This tries to account for all possible layouts.
+ // If the role of a button is YesRole, AcceptRole, NoRole or RejectRole, then pressing that button should emit accepted, or rejected.
+ // And since this is a MessageDialog, the result property should reflect the last button pressed, rather than the StandardCode.
+ for (int i = 0; i < buttonBox->count(); ++i) {
+ auto button = qobject_cast<QQuickAbstractButton *>(buttonBox->itemAt(i));
+ switch (QQuickDialogPrivate::buttonRole(button)) {
+ case QPlatformDialogHelper::YesRole:
+ yesFound = true;
+ expectedNumberOfAcceptedSignals++;
+
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, button->mapToScene({ button->width() / 2, button->height() / 2 }).toPoint());
+ QTRY_VERIFY(!dialogHelper.isQuickDialogOpen());
+
+ QCOMPARE(dialogHelper.dialog->result(), QPlatformDialogHelper::StandardButton::Yes);
+ QCOMPARE(resultChangedSpy.count(), i + 1);
+
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ break;
+ case QPlatformDialogHelper::NoRole:
+ noFound = true;
+ expectedNumberOfRejectedSignals++;
+
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, button->mapToScene({ button->width() / 2, button->height() / 2 }).toPoint());
+ QTRY_VERIFY(!dialogHelper.isQuickDialogOpen());
+
+ QCOMPARE(dialogHelper.dialog->result(), QPlatformDialogHelper::StandardButton::No);
+ QCOMPARE(resultChangedSpy.count(), i + 1);
+
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ break;
+ case QPlatformDialogHelper::DestructiveRole:
+ discardFound = true;
+
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, button->mapToScene({ button->width() / 2, button->height() / 2 }).toPoint());
+ QTRY_VERIFY(!dialogHelper.isQuickDialogOpen());
+
+ QCOMPARE(dialogHelper.dialog->result(), QPlatformDialogHelper::StandardButton::Discard);
+ QCOMPARE(resultChangedSpy.count(), i + 1);
+
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ break;
+ case QPlatformDialogHelper::ApplyRole:
+ applyFound = true;
+
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, button->mapToScene({ button->width() / 2, button->height() / 2 }).toPoint());
+ QTRY_VERIFY(!dialogHelper.isQuickDialogOpen());
+
+ QCOMPARE(dialogHelper.dialog->result(), QPlatformDialogHelper::StandardButton::Apply);
+ QCOMPARE(resultChangedSpy.count(), i + 1);
+
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ break;
+ default:
+ QFAIL(qPrintable(QStringLiteral("Unexpected role %1").arg(QQuickDialogPrivate::buttonRole(button))));
+ }
+ }
+
+ QVERIFY2(yesFound && noFound && discardFound && applyFound, "A button that was expected to be present, wasn't found when iterating over all of them.");
+ QCOMPARE(acceptedSpy.count(), expectedNumberOfAcceptedSignals);
+ QCOMPARE(rejectedSpy.count(), expectedNumberOfRejectedSignals);
+
+ dialogHelper.dialog->close();
+}
+
QTEST_MAIN(tst_QQuickMessageDialogImpl)
#include "tst_qquickmessagedialogimpl.moc"
diff --git a/tests/auto/quicktest/polish-qml/CMakeLists.txt b/tests/auto/quicktest/polish-qml/CMakeLists.txt
index cbae62a349..9b7805f57f 100644
--- a/tests/auto/quicktest/polish-qml/CMakeLists.txt
+++ b/tests/auto/quicktest/polish-qml/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_polish-qml Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_polish-qml LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quicktest/polish-qml/data/tst_polish.qml b/tests/auto/quicktest/polish-qml/data/tst_polish.qml
index 089d1d9620..8a62db4ecd 100644
--- a/tests/auto/quicktest/polish-qml/data/tst_polish.qml
+++ b/tests/auto/quicktest/polish-qml/data/tst_polish.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.13
import QtTest 1.13
diff --git a/tests/auto/quicktest/polish-qml/tst_polish-qml.cpp b/tests/auto/quicktest/polish-qml/tst_polish-qml.cpp
index 58156868df..6f4bdc6a9a 100644
--- a/tests/auto/quicktest/polish-qml/tst_polish-qml.cpp
+++ b/tests/auto/quicktest/polish-qml/tst_polish-qml.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quicktest/polish/CMakeLists.txt b/tests/auto/quicktest/polish/CMakeLists.txt
index a05f1dfa15..0f7ecb6808 100644
--- a/tests/auto/quicktest/polish/CMakeLists.txt
+++ b/tests/auto/quicktest/polish/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_polish Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_polish LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quicktest/polish/data/childPolish.qml b/tests/auto/quicktest/polish/data/childPolish.qml
index f8e72bfaf4..08f5b5bfb6 100644
--- a/tests/auto/quicktest/polish/data/childPolish.qml
+++ b/tests/auto/quicktest/polish/data/childPolish.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Test 1.0
diff --git a/tests/auto/quicktest/polish/data/polish.qml b/tests/auto/quicktest/polish/data/polish.qml
index 76783624e9..6015957845 100644
--- a/tests/auto/quicktest/polish/data/polish.qml
+++ b/tests/auto/quicktest/polish/data/polish.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Test 1.0
diff --git a/tests/auto/quicktest/polish/tst_polish.cpp b/tests/auto/quicktest/polish/tst_polish.cpp
index aea7fb92c5..396375d081 100644
--- a/tests/auto/quicktest/polish/tst_polish.cpp
+++ b/tests/auto/quicktest/polish/tst_polish.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt b/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt
index 2ad66c00c9..9f583c7723 100644
--- a/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt
+++ b/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_quicktestmainwithsetup Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_quicktestmainwithsetup LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml b/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml
index 2cfe936a6c..68eebf2965 100644
--- a/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml
+++ b/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.2
diff --git a/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp b/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp
index 23f59fa06c..d05a5730fb 100644
--- a/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp
+++ b/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtQml/qqmlengine.h>
diff --git a/tests/auto/quicktest/signalspy/CMakeLists.txt b/tests/auto/quicktest/signalspy/CMakeLists.txt
index 75248d4aea..1d5d816f0c 100644
--- a/tests/auto/quicktest/signalspy/CMakeLists.txt
+++ b/tests/auto/quicktest/signalspy/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_signalspy Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_signalspy LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
data/*)
diff --git a/tests/auto/quicktest/signalspy/data/signalspy.qml b/tests/auto/quicktest/signalspy/data/signalspy.qml
index e5fbb915b6..72cae86602 100644
--- a/tests/auto/quicktest/signalspy/data/signalspy.qml
+++ b/tests/auto/quicktest/signalspy/data/signalspy.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/quicktest/signalspy/data/signalspy2.qml b/tests/auto/quicktest/signalspy/data/signalspy2.qml
index 0532e5fb54..5329927f87 100644
--- a/tests/auto/quicktest/signalspy/data/signalspy2.qml
+++ b/tests/auto/quicktest/signalspy/data/signalspy2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.1
diff --git a/tests/auto/quicktest/signalspy/mypropertymap.cpp b/tests/auto/quicktest/signalspy/mypropertymap.cpp
index b374efb167..6ac7fef766 100644
--- a/tests/auto/quicktest/signalspy/mypropertymap.cpp
+++ b/tests/auto/quicktest/signalspy/mypropertymap.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mypropertymap.h"
diff --git a/tests/auto/quicktest/signalspy/mypropertymap.h b/tests/auto/quicktest/signalspy/mypropertymap.h
index e4a7506b6b..d6a640aab4 100644
--- a/tests/auto/quicktest/signalspy/mypropertymap.h
+++ b/tests/auto/quicktest/signalspy/mypropertymap.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QQmlPropertyMap>
diff --git a/tests/auto/quicktest/signalspy/tst_signalspy.cpp b/tests/auto/quicktest/signalspy/tst_signalspy.cpp
index 545f4d3cc7..6b9c37b237 100644
--- a/tests/auto/quicktest/signalspy/tst_signalspy.cpp
+++ b/tests/auto/quicktest/signalspy/tst_signalspy.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
diff --git a/tests/auto/quicktest/testfiltering/quicktestmain/CMakeLists.txt b/tests/auto/quicktest/testfiltering/quicktestmain/CMakeLists.txt
index 137ab8db95..a60398d2a0 100644
--- a/tests/auto/quicktest/testfiltering/quicktestmain/CMakeLists.txt
+++ b/tests/auto/quicktest/testfiltering/quicktestmain/CMakeLists.txt
@@ -7,6 +7,12 @@
## quicktestmain Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(quicktestmain LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quicktest/testfiltering/quicktestmain/quicktestmain.cpp b/tests/auto/quicktest/testfiltering/quicktestmain/quicktestmain.cpp
index 618ed14351..321763c7e7 100644
--- a/tests/auto/quicktest/testfiltering/quicktestmain/quicktestmain.cpp
+++ b/tests/auto/quicktest/testfiltering/quicktestmain/quicktestmain.cpp
@@ -1,4 +1,4 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(quicktestmain)
diff --git a/tests/auto/quicktest/testfiltering/quicktestmain/tst_first.qml b/tests/auto/quicktest/testfiltering/quicktestmain/tst_first.qml
index 46a4f86e2f..917e731cdb 100644
--- a/tests/auto/quicktest/testfiltering/quicktestmain/tst_first.qml
+++ b/tests/auto/quicktest/testfiltering/quicktestmain/tst_first.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
import QtTest 1.1
diff --git a/tests/auto/quicktest/testfiltering/quicktestmain/tst_second.qml b/tests/auto/quicktest/testfiltering/quicktestmain/tst_second.qml
index 61e218facc..0b4ef2c0d6 100644
--- a/tests/auto/quicktest/testfiltering/quicktestmain/tst_second.qml
+++ b/tests/auto/quicktest/testfiltering/quicktestmain/tst_second.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
import QtTest 1.1
diff --git a/tests/auto/quicktest/testfiltering/quicktestmain/tst_third.qml b/tests/auto/quicktest/testfiltering/quicktestmain/tst_third.qml
index 41df307755..9856212259 100644
--- a/tests/auto/quicktest/testfiltering/quicktestmain/tst_third.qml
+++ b/tests/auto/quicktest/testfiltering/quicktestmain/tst_third.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
import QtTest 1.1
diff --git a/tests/auto/quicktest/testfiltering/test/CMakeLists.txt b/tests/auto/quicktest/testfiltering/test/CMakeLists.txt
index c59818d2f2..fa6a387c91 100644
--- a/tests/auto/quicktest/testfiltering/test/CMakeLists.txt
+++ b/tests/auto/quicktest/testfiltering/test/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_testfiltering Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_testfiltering LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_testfiltering
OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../" # special case
diff --git a/tests/auto/quicktest/testfiltering/tst_testfiltering.cpp b/tests/auto/quicktest/testfiltering/tst_testfiltering.cpp
index 3f0c8c41b4..9dc0f46335 100644
--- a/tests/auto/quicktest/testfiltering/tst_testfiltering.cpp
+++ b/tests/auto/quicktest/testfiltering/tst_testfiltering.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QProcess>
#include <QtTest>
diff --git a/tests/auto/quicktest/testswithcomponents/CMakeLists.txt b/tests/auto/quicktest/testswithcomponents/CMakeLists.txt
index 3a77fc38d1..a2beefc298 100644
--- a/tests/auto/quicktest/testswithcomponents/CMakeLists.txt
+++ b/tests/auto/quicktest/testswithcomponents/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_quicktestmainwithsetup Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_quicktestswithcomponents LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quicktest/testswithcomponents/data/tst_setup.qml b/tests/auto/quicktest/testswithcomponents/data/tst_setup.qml
index 685ccd0927..55ba80b92f 100644
--- a/tests/auto/quicktest/testswithcomponents/data/tst_setup.qml
+++ b/tests/auto/quicktest/testswithcomponents/data/tst_setup.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtTest 1.2
diff --git a/tests/auto/quicktest/testswithcomponents/tst_quicktestswithcomponents.cpp b/tests/auto/quicktest/testswithcomponents/tst_quicktestswithcomponents.cpp
index 4b3584d33c..cfec909f72 100644
--- a/tests/auto/quicktest/testswithcomponents/tst_quicktestswithcomponents.cpp
+++ b/tests/auto/quicktest/testswithcomponents/tst_quicktestswithcomponents.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtQuickTest/quicktest.h>
diff --git a/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt b/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
index 5afe445cff..233fae553b 100644
--- a/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
+++ b/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qquickwidget Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qquickwidget LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml b/tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml
index 11bbfdd90e..65f017b1b1 100644
--- a/tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml
+++ b/tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
diff --git a/tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml b/tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml
index 2168d29831..3fb6e5b7b1 100644
--- a/tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml
+++ b/tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
diff --git a/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml b/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml
new file mode 100644
index 0000000000..fe3d1925e5
--- /dev/null
+++ b/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Rectangle {
+ width: 100
+ height: 100
+ color: th.pressed ? "steelblue" : "beige"
+
+ TapHandler {
+ id: th
+ }
+}
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index 8316625d87..0067c716e0 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <qtesttouch.h>
@@ -11,6 +11,7 @@
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
+#include <QtQuick/private/qquicktaphandler_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtGui/QWindow>
@@ -23,6 +24,7 @@
#include <QtGui/qstylehints.h>
#include <QtWidgets/QBoxLayout>
#include <QtWidgets/QLabel>
+#include <QtWidgets/private/qapplication_p.h>
#include <QtQuickWidgets/QQuickWidget>
@@ -127,6 +129,8 @@ private slots:
void synthMouseFromTouch();
void touchTapMouseArea();
void touchTapButton();
+ void touchTapHandler_data();
+ void touchTapHandler();
void touchMultipleWidgets();
void tabKey();
void resizeOverlay();
@@ -135,6 +139,9 @@ private slots:
#if QT_CONFIG(graphicsview)
void focusOnClickInProxyWidget();
#endif
+ void focusPreserved();
+ void accessibilityHandlesViewChange();
+ void cleanupRhi();
private:
QPointingDevice *device = QTest::createTouchDevice();
@@ -678,11 +685,54 @@ void tst_qquickwidget::touchTapButton()
QTRY_VERIFY(rootItem->property("wasClicked").toBool());
}
+void tst_qquickwidget::touchTapHandler_data()
+{
+ QTest::addColumn<bool>("guiSynthMouse"); // AA_SynthesizeMouseForUnhandledTouchEvents
+ QTest::addColumn<QQuickTapHandler::GesturePolicy>("gesturePolicy");
+
+ // QTest::newRow("nosynth: passive grab") << false << QQuickTapHandler::DragThreshold; // still failing
+ QTest::newRow("nosynth: exclusive grab") << false << QQuickTapHandler::ReleaseWithinBounds;
+ QTest::newRow("allowsynth: passive grab") << true << QQuickTapHandler::DragThreshold; // QTBUG-113558
+ QTest::newRow("allowsynth: exclusive grab") << true << QQuickTapHandler::ReleaseWithinBounds;
+}
+
+void tst_qquickwidget::touchTapHandler()
+{
+ QFETCH(bool, guiSynthMouse);
+ QFETCH(QQuickTapHandler::GesturePolicy, gesturePolicy);
+
+ QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, guiSynthMouse);
+ QQuickWidget quick;
+ if (!quick.testAttribute(Qt::WA_AcceptTouchEvents))
+ QSKIP("irrelevant on non-touch platforms");
+
+ quick.setSource(testFileUrl("tapHandler.qml"));
+ quick.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&quick));
+
+ QQuickItem *rootItem = quick.rootObject();
+ QVERIFY(rootItem);
+ QQuickTapHandler *th = rootItem->findChild<QQuickTapHandler *>();
+ QVERIFY(th);
+ th->setGesturePolicy(gesturePolicy);
+ QSignalSpy tappedSpy(th, &QQuickTapHandler::tapped);
+
+ const QPoint p(50, 50);
+ QTest::touchEvent(&quick, device).press(0, p, &quick);
+ QTRY_COMPARE(th->isPressed(), true);
+ QTest::touchEvent(&quick, device).release(0, p, &quick);
+ QTRY_COMPARE(tappedSpy.size(), 1);
+ QCOMPARE(th->isPressed(), false);
+}
+
void tst_qquickwidget::touchMultipleWidgets()
{
QWidget window;
QQuickWidget *leftQuick = new QQuickWidget;
leftQuick->setSource(testFileUrl("button.qml"));
+ if (!leftQuick->testAttribute(Qt::WA_AcceptTouchEvents))
+ QSKIP("irrelevant on non-touch platforms");
+
QQuickWidget *rightQuick = new QQuickWidget;
rightQuick->setSource(testFileUrl("button.qml"));
@@ -939,6 +989,110 @@ void tst_qquickwidget::focusOnClickInProxyWidget()
}
#endif
+void tst_qquickwidget::focusPreserved()
+{
+ if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
+ QSKIP("Window Activation is not supported.");
+ if (QGuiApplication::platformName() == "android")
+ QSKIP("Test doesn't exit cleanly on Android and generates many warnings - QTBUG-112696");
+
+ QScopedPointer<QWidget> widget(new QWidget());
+ QScopedPointer<QQuickWidget> quick(new QQuickWidget());
+ QQuickItem *root = new QQuickItem(); // will be owned by quick after setContent
+ QScopedPointer<QQuickItem> content(new QQuickItem());
+ content->setActiveFocusOnTab(true);
+ content->setFocus(true);
+ quick->setFocusPolicy(Qt::StrongFocus);
+ quick->setContent(QUrl(), nullptr, root);
+ root->setFlag(QQuickItem::ItemHasContents);
+ content->setParentItem(root);
+
+ quick->setGeometry(0, 0, 200, 200);
+ quick->show();
+ quick->setFocus();
+ quick->activateWindow();
+ QVERIFY(QTest::qWaitForWindowExposed(quick.get()));
+ QTRY_VERIFY(quick->hasFocus());
+ QTRY_VERIFY(content->hasFocus());
+ QTRY_VERIFY(content->hasActiveFocus());
+
+ widget->show();
+ widget->setFocus();
+ widget->activateWindow();
+ QVERIFY(QTest::qWaitForWindowExposed(widget.get()));
+ QTRY_VERIFY(widget->hasFocus());
+
+ quick->setParent(widget.get());
+
+ quick->show();
+ quick->setFocus();
+ quick->activateWindow();
+ QTRY_VERIFY(quick->hasFocus());
+ QTRY_VERIFY(content->hasFocus());
+ QTRY_VERIFY(content->hasActiveFocus());
+}
+
+/*
+ Reparenting the QQuickWidget recreates the offscreen QQuickWindow.
+ Since the accessible interface that is cached for the QQuickWidget dispatches
+ all calls to the offscreen QQuickWindow, it must fix itself when the offscreen
+ view changes. QTBUG-108226
+*/
+void tst_qquickwidget::accessibilityHandlesViewChange()
+{
+ if (QGuiApplication::platformName() == "offscreen")
+ QSKIP("Doesn't test anything on offscreen platform.");
+ if (QGuiApplication::platformName() == "android")
+ QSKIP("Test doesn't exit cleanly on Android and generates many warnings - QTBUG-112696");
+
+ QWidget window;
+
+ QPointer<QQuickWindow> backingScene;
+
+ QQuickWidget *childView = new QQuickWidget(&window);
+ childView->setSource(testFileUrl("rectangle.qml"));
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+ backingScene = childView->quickWindow();
+ QVERIFY(backingScene);
+
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(childView);
+ QVERIFY(iface);
+ (void)iface->child(0);
+
+ std::unique_ptr<QQuickWidget> quickWidget(childView);
+ childView->setParent(nullptr);
+ childView->show();
+ QVERIFY(QTest::qWaitForWindowExposed(childView));
+ QVERIFY(!backingScene); // the old QQuickWindow should be gone now
+ QVERIFY(childView->quickWindow()); // long live the new QQuickWindow
+
+ iface = QAccessible::queryAccessibleInterface(childView);
+ QVERIFY(iface);
+ // this would crash if QAccessibleQuickWidget hadn't repaired itself to
+ // delegate calls to the new (or at least not the old, destroyed) QQuickWindow.
+ (void)iface->child(0);
+}
+
+class CreateDestroyWidget : public QWidget
+{
+public:
+ using QWidget::create;
+ using QWidget::destroy;
+};
+
+void tst_qquickwidget::cleanupRhi()
+{
+ CreateDestroyWidget topLevel;
+ QQuickWidget quickWidget(&topLevel);
+ quickWidget.setSource(testFileUrl("rectangle.qml"));
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+
+ topLevel.destroy();
+ topLevel.create();
+}
+
QTEST_MAIN(tst_qquickwidget)
#include "tst_qquickwidget.moc"
diff --git a/tests/auto/toolsupport/CMakeLists.txt b/tests/auto/toolsupport/CMakeLists.txt
index 71a5cbcd6b..3732b473ef 100644
--- a/tests/auto/toolsupport/CMakeLists.txt
+++ b/tests/auto/toolsupport/CMakeLists.txt
@@ -7,9 +7,13 @@
## tst_toolsupport Test:
#####################################################################
-# special case begin
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qml_toolsupport LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qml_toolsupport
-# special case end
SOURCES
tst_toolsupport.cpp
LIBRARIES
diff --git a/tests/auto/toolsupport/tst_toolsupport.cpp b/tests/auto/toolsupport/tst_toolsupport.cpp
index 893f4b95b9..625e3b660f 100644
--- a/tests/auto/toolsupport/tst_toolsupport.cpp
+++ b/tests/auto/toolsupport/tst_toolsupport.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
@@ -9,6 +9,7 @@
#include <private/qobject_p.h>
#include <private/qv4compileddata_p.h>
+#include <private/qv4executablecompilationunit_p.h>
#include <private/qv4string_p.h>
#include <private/qqmlrefcount_p.h>
#include <qobject.h>
@@ -68,7 +69,7 @@ void tst_toolsupport::offsets_data()
{
QTestData &data = QTest::newRow("sizeof(QQmlRefCount)")
<< sizeof(QQmlRefCount);
- data << 8 << 16;
+ data << 4 << 4;
}
#if RUN_MEMBER_OFFSET_TEST
@@ -77,13 +78,13 @@ void tst_toolsupport::offsets_data()
= QTest::newRow("CompiledData::CompilationUnit::data")
<< pmm_to_offsetof(&QV4::CompiledData::CompilationUnit::data);
- data << 20 << 40;
+ data << 4 << 8;
}
{
QTestData &data
- = QTest::newRow("CompiledData::CompilationUnit::runtimeStrings")
- << pmm_to_offsetof(&QV4::CompiledData::CompilationUnit::runtimeStrings);
+ = QTest::newRow("ExecutableCompilationUnit::runtimeStrings")
+ << pmm_to_offsetof(&QV4::ExecutableCompilationUnit::runtimeStrings);
data << 0 << 0;
}
diff --git a/tests/baseline/CMakeLists.txt b/tests/baseline/CMakeLists.txt
index f00e3f197d..346444d79b 100644
--- a/tests/baseline/CMakeLists.txt
+++ b/tests/baseline/CMakeLists.txt
@@ -7,6 +7,8 @@ endif()
# Special case: test includes the QBaselineTest module sources from qtbase directly
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../../qtbase/tests/baseline/shared")
- add_subdirectory(scenegraph)
- add_subdirectory(controls)
+ if(QT_FEATURE_processenvironment)
+ add_subdirectory(scenegraph)
+ add_subdirectory(controls)
+ endif()
endif()
diff --git a/tests/baseline/controls/BLACKLIST b/tests/baseline/controls/BLACKLIST
new file mode 100644
index 0000000000..834b16d765
--- /dev/null
+++ b/tests/baseline/controls/BLACKLIST
@@ -0,0 +1,15 @@
+# animated controls, can't take stable snapshot
+[native:busyIndicator/busy_indicator]
+*
+[basic:busyIndicator/busy_indicator]
+*
+[universal:busyIndicator/busy_indicator]
+*
+[imagine:busyIndicator/busy_indicator]
+*
+[fusion:busyIndicator/busy_indicator]
+*
+[material:busyIndicator/busy_indicator]
+*
+[ios:busyIndicator/busy_indicator]
+*
diff --git a/tests/baseline/controls/data/dialog/dialog.qml b/tests/baseline/controls/data/dialog/dialog.qml
index fc3f3f26f3..cf41707da0 100644
--- a/tests/baseline/controls/data/dialog/dialog.qml
+++ b/tests/baseline/controls/data/dialog/dialog.qml
@@ -12,9 +12,7 @@ Item {
width: 50
visible: true
enabled: true
- header: Text {
- text: qsTr("dialog 1")
- }
+ title: qsTr("dialog 1")
}
Dialog {
diff --git a/tests/baseline/controls/data/dialog/modal.qml b/tests/baseline/controls/data/dialog/modal.qml
index a602d6d037..c59b9b8545 100644
--- a/tests/baseline/controls/data/dialog/modal.qml
+++ b/tests/baseline/controls/data/dialog/modal.qml
@@ -12,8 +12,6 @@ Item {
visible: true
enabled: true
modal: true
- header: Text {
- text: qsTr("dialog 1")
- }
+ title: qsTr("dialog 1")
}
}
diff --git a/tests/baseline/controls/data/groupBox/groupbox.qml b/tests/baseline/controls/data/groupBox/groupbox.qml
index b7930a1b35..25834b93f1 100644
--- a/tests/baseline/controls/data/groupBox/groupbox.qml
+++ b/tests/baseline/controls/data/groupBox/groupbox.qml
@@ -13,7 +13,7 @@ ColumnLayout {
GroupBox {
title: "Title 2"
- label: Text {
+ label: Label {
text: "I am Text label in GroupBox"
}
}
diff --git a/tests/baseline/controls/data/popup/modal.qml b/tests/baseline/controls/data/popup/modal.qml
index 609fa19956..dfcab2e3f2 100644
--- a/tests/baseline/controls/data/popup/modal.qml
+++ b/tests/baseline/controls/data/popup/modal.qml
@@ -9,7 +9,7 @@ Item {
anchors.centerIn: parent
modal: true
visible: true
- Text {
+ Label {
text: "visible"
}
}
diff --git a/tests/baseline/controls/data/popup/popup.qml b/tests/baseline/controls/data/popup/popup.qml
index 50d41b351d..350d34fb1c 100644
--- a/tests/baseline/controls/data/popup/popup.qml
+++ b/tests/baseline/controls/data/popup/popup.qml
@@ -8,7 +8,7 @@ Item {
Popup {
anchors.centerIn: parent
visible: true
- Text {
+ Label {
text: "visible"
}
}
diff --git a/tests/baseline/controls/data/textarea/textarea.qml b/tests/baseline/controls/data/textarea/textarea.qml
index e34709c6cf..57b57606b4 100644
--- a/tests/baseline/controls/data/textarea/textarea.qml
+++ b/tests/baseline/controls/data/textarea/textarea.qml
@@ -8,11 +8,13 @@ ColumnLayout {
TextArea {
text: "TextArea\n...\n...\n...\n..."
+ cursorVisible: false
}
TextArea {
placeholderText: "TextArea\n...\n...\n..."
enabled: false
+ cursorVisible: false
}
TextArea {
@@ -24,6 +26,7 @@ ColumnLayout {
TextArea {
text: "TextArea\n...\n...\n...\n..."
LayoutMirroring.enabled: true
+ cursorVisible: false
}
}
diff --git a/tests/baseline/controls/data/textfield/textfield.qml b/tests/baseline/controls/data/textfield/textfield.qml
index bc0b1f215f..00b00913f8 100644
--- a/tests/baseline/controls/data/textfield/textfield.qml
+++ b/tests/baseline/controls/data/textfield/textfield.qml
@@ -9,21 +9,25 @@ ColumnLayout {
TextField {
placeholderText: qsTr("Enter text")
enabled: false
+ cursorVisible: false
}
TextField {
placeholderText: qsTr("Enter text")
placeholderTextColor: "red"
+ cursorVisible: false
}
TextField {
placeholderText: qsTr("Enter text")
focus: true
+ cursorVisible: false
}
TextField {
placeholderText: qsTr("Enter text")
LayoutMirroring.enabled: true
+ cursorVisible: false
}
TextField {
@@ -31,5 +35,6 @@ ColumnLayout {
+ "sed do eiusmod tempor incididunt utlabore et dolore magna"
+ "aliqua.Ut enim ad minim veniam, quis nostrud exercitation"
+ "ullamco laboris nisi ut aliquip ex ea commodo consequat.")
+ cursorVisible: false
}
}
diff --git a/tests/baseline/controls/tst_baseline_controls.cpp b/tests/baseline/controls/tst_baseline_controls.cpp
index 2b398edf3c..1467749d06 100644
--- a/tests/baseline/controls/tst_baseline_controls.cpp
+++ b/tests/baseline/controls/tst_baseline_controls.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qbaselinetest.h>
@@ -123,24 +123,24 @@ void tst_Baseline_Controls::initTestCase()
// See also qwidgetbaselinetest.cpp
QPalette palette;
QFont font;
- QByteArray appearanceBytes;
+ QByteArray colorSchemeBytes;
{
- QDataStream appearanceStream(&appearanceBytes, QIODevice::WriteOnly);
- appearanceStream << palette << font;
+ QDataStream colorSchemeStream(&colorSchemeBytes, QIODevice::WriteOnly);
+ colorSchemeStream << palette << font;
const qreal screenDpr = QGuiApplication::primaryScreen()->devicePixelRatio();
if (screenDpr != 1.0) {
qWarning() << "DPR is" << screenDpr << "- images will not be compared to 1.0 baseline!";
- appearanceStream << screenDpr;
+ colorSchemeStream << screenDpr;
}
}
- const quint16 appearanceId = qChecksum(appearanceBytes);
+ const quint16 colorSchemeId = qChecksum(colorSchemeBytes);
const QColor windowColor = palette.window().color();
const QColor textColor = palette.text().color();
- const QString appearanceIdString = (windowColor.value() > textColor.value()
+ const QString colorSchemeIdStr = (windowColor.value() > textColor.value()
? QString("light-%1") : QString("dark-%1"))
- .arg(appearanceId, 0, 16);
- QBaselineTest::addClientProperty("AppearanceID", appearanceIdString);
+ .arg(colorSchemeId, 0, 16);
+ QBaselineTest::addClientProperty("AppearanceID", colorSchemeIdStr);
QByteArray msg;
if (!QBaselineTest::connectToBaselineServer(&msg))
@@ -148,7 +148,7 @@ void tst_Baseline_Controls::initTestCase()
// let users know where they can find the results
qInfo("PlatformName computed to be : %s", qPrintable(platformName));
- qInfo("Appearance ID computed as : %s", qPrintable(appearanceIdString));
+ qInfo("Color Scheme computed as : %s", qPrintable(colorSchemeIdStr));
qInfo("Native style name is : %s", qPrintable(QQuickStyle::name()));
}
@@ -203,6 +203,7 @@ void tst_Baseline_Controls::runTest(const QString& style)
QStringList args;
if (!style.isEmpty())
args.append({"-style", style});
+ args.append("-useAppWindow");
if (renderAndGrab(qmlFile, args, &screenShot, &errorMessage)) {
consecutiveErrors = 0;
} else {
@@ -233,8 +234,9 @@ bool tst_Baseline_Controls::renderAndGrab(const QString& qmlFile, const QStringL
bool res = usePipe ? img.load(&grabber, "ppm") : img.load(tmpfile);
if (!res || img.isNull()) {
if (errMsg) {
- QString s("Failed to grab screen. qmlscenegrabber exitcode: %1. Process error: %2. Stderr:%3");
- *errMsg = s.arg(grabber.exitCode()).arg(grabber.errorString()).arg(blockify(grabber.readAllStandardError()));
+ QString s("Failed to grab screen in %1. qmlscenegrabber exitcode: %2. Process error: %3. Stderr:%4");
+ *errMsg = s.arg(qmlFile).arg(grabber.exitCode())
+ .arg(grabber.errorString()).arg(blockify(grabber.readAllStandardError()));
}
if (!usePipe)
QFile::remove(tmpfile);
diff --git a/tests/baseline/scenegraph/data/Ignore b/tests/baseline/scenegraph/data/Ignore
index e904d53e8c..909d546391 100644
--- a/tests/baseline/scenegraph/data/Ignore
+++ b/tests/baseline/scenegraph/data/Ignore
@@ -3,6 +3,7 @@
# These are items to be used in other scenes; lack size
borderimages/SimpleBorderImage.qml
borderimages/SimpleNoBorder.qml
+shape/OrderedPaths.qml
# This will not stabilize before the timeout
text/text_2500_chinese_characters.qml
diff --git a/tests/baseline/scenegraph/data/rectangles/rectangles_allCornerCombinations.qml b/tests/baseline/scenegraph/data/rectangles/rectangles_allCornerCombinations.qml
new file mode 100644
index 0000000000..dc657587f4
--- /dev/null
+++ b/tests/baseline/scenegraph/data/rectangles/rectangles_allCornerCombinations.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Item {
+ id: root
+ width: layout.implicitWidth
+ height: layout.implicitHeight
+
+ property int num: 6
+ property double rStep: 2
+ GridLayout {
+ id: layout
+ columns: num*num
+ rows: num*num
+ columnSpacing: 0
+ rowSpacing: 0
+ Repeater {
+ model: num*num*num*num
+ Rectangle {
+ //transform: Rotation {angle: 10} works too
+ color: Qt.rgba(1, 0, 0, 1)
+ border.color: Qt.rgba(0, 0, 0, 1)
+ border.width: 4
+ topLeftRadius: rStep*Math.floor(index/num/num/num)
+ topRightRadius: rStep*(Math.floor(index/num/num)%num)
+ bottomLeftRadius: rStep*(Math.floor(index/num)%num)
+ bottomRightRadius: rStep*(index%num)
+ antialiasing: true
+ opacity: 0.4
+
+ Layout.preferredWidth: 2*(num-1)*rStep
+ Layout.preferredHeight: 2*(num-1)*rStep
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 0
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/rectangles/rectangles_irregularcorners.qml b/tests/baseline/scenegraph/data/rectangles/rectangles_irregularcorners.qml
new file mode 100644
index 0000000000..f1f8d78acd
--- /dev/null
+++ b/tests/baseline/scenegraph/data/rectangles/rectangles_irregularcorners.qml
@@ -0,0 +1,1579 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ id: r_0000
+ width: (standardRectWidth+1) * 10 + 3
+ height: (standardRectHeight+1) * 10 + 3
+ color: "white"
+
+ property int standardRectWidth: 32
+ property int standardRectHeight: 48
+ property int yPlacementRow_0: 2
+ property int yPlacementRow_1: 2 + (standardRectHeight+1)
+ property int yPlacementRow_2: 2 + (standardRectHeight+1)*2
+ property int yPlacementRow_3: 2 + (standardRectHeight+1)*3
+ property int yPlacementRow_4: 2 + (standardRectHeight+1)*4
+ property int yPlacementRow_5: 2 + (standardRectHeight+1)*5
+ property int yPlacementRow_6: 2 + (standardRectHeight+1)*6
+ property int yPlacementRow_7: 2 + (standardRectHeight+1)*7
+ property int yPlacementRow_8: 2 + (standardRectHeight+1)*8
+ property int yPlacementRow_9: 2 + (standardRectHeight+1)*9
+ property int xPlacementCol_0: 2
+ property int xPlacementCol_1: 2 + (standardRectWidth+1)
+ property int xPlacementCol_2: 2 + (standardRectWidth+1)*2
+ property int xPlacementCol_3: 2 + (standardRectWidth+1)*3
+ property int xPlacementCol_4: 2 + (standardRectWidth+1)*4
+ property int xPlacementCol_5: 2 + (standardRectWidth+1)*5
+ property int xPlacementCol_6: 2 + (standardRectWidth+1)*6
+ property int xPlacementCol_7: 2 + (standardRectWidth+1)*7
+ property int xPlacementCol_8: 2 + (standardRectWidth+1)*8
+ property int xPlacementCol_9: 2 + (standardRectWidth+1)*9
+
+ Rectangle{
+ id: r_0001
+ x: xPlacementCol_0
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0002
+ x: xPlacementCol_1
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0003
+ x: xPlacementCol_2
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0004
+ x: xPlacementCol_3
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0005
+ x: xPlacementCol_4
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0006
+ x: xPlacementCol_5
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0007
+ x: xPlacementCol_6
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0008
+ x: xPlacementCol_7
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0009
+ x: xPlacementCol_8
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0010
+ x: xPlacementCol_9
+ y: yPlacementRow_0
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+
+ /*
+
+ OUTLINE AS THICK AS THE RADIUS
+
+ */
+
+ Rectangle{
+ id: r_0011
+ x: xPlacementCol_0
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0012
+ x: xPlacementCol_1
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0013
+ x: xPlacementCol_2
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0014
+ x: xPlacementCol_3
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_00015
+ x: xPlacementCol_4
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0016
+ x: xPlacementCol_5
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0017
+ x: xPlacementCol_6
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0018
+ x: xPlacementCol_7
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0019
+ x: xPlacementCol_8
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0020
+ x: xPlacementCol_9
+ y: yPlacementRow_1
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 10
+ color: "red"
+ }
+
+ /*
+
+ OUTLINE THICKER THAN RADIUS
+
+ */
+
+ Rectangle{
+ id: r_0021
+ x: xPlacementCol_0
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0022
+ x: xPlacementCol_1
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0023
+ x: xPlacementCol_2
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0024
+ x: xPlacementCol_3
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_00025
+ x: xPlacementCol_4
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0026
+ x: xPlacementCol_5
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0027
+ x: xPlacementCol_6
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0028
+ x: xPlacementCol_7
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 10
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0029
+ x: xPlacementCol_8
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 10
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0030
+ x: xPlacementCol_9
+ y: yPlacementRow_2
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 0
+ topRightRadius: 0
+ bottomLeftRadius: 0
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 15
+ color: "red"
+ }
+
+ /*
+
+ DIFFERENT RADII
+
+ */
+
+ Rectangle{
+ id: r_0031
+ x: xPlacementCol_0
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 5
+ bottomLeftRadius: 10
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0032
+ x: xPlacementCol_1
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0033
+ x: xPlacementCol_2
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0034
+ x: xPlacementCol_3
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 10
+ bottomLeftRadius: 15
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0035
+ x: xPlacementCol_4
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 10
+ bottomLeftRadius: 5
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0036
+ x: xPlacementCol_5
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 15
+ bottomLeftRadius: 10
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0037
+ x: xPlacementCol_6
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 0
+ bottomLeftRadius: 15
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0038
+ x: xPlacementCol_7
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 5
+ bottomLeftRadius: 0
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0039
+ x: xPlacementCol_8
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+ Rectangle{
+ id: r_0040
+ x: xPlacementCol_9
+ y: yPlacementRow_3
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 5
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ color: "red"
+ }
+
+ /*
+
+ DIFFERENT RADII; NO OUTLINE
+
+ */
+
+ Rectangle{
+ id: r_0041
+ x: xPlacementCol_0
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 5
+ bottomLeftRadius: 10
+ bottomRightRadius: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0042
+ x: xPlacementCol_1
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0043
+ x: xPlacementCol_2
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 5
+ color: "red"
+ }
+ Rectangle{
+ id: r_0044
+ x: xPlacementCol_3
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 10
+ bottomLeftRadius: 15
+ bottomRightRadius: 0
+ color: "red"
+ }
+ Rectangle{
+ id: r_0045
+ x: xPlacementCol_4
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 10
+ bottomLeftRadius: 5
+ bottomRightRadius: 0
+ color: "red"
+ }
+ Rectangle{
+ id: r_0046
+ x: xPlacementCol_5
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 15
+ bottomLeftRadius: 10
+ bottomRightRadius: 5
+ color: "red"
+ }
+ Rectangle{
+ id: r_0047
+ x: xPlacementCol_6
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 0
+ bottomLeftRadius: 15
+ bottomRightRadius: 10
+ color: "red"
+ }
+ Rectangle{
+ id: r_0048
+ x: xPlacementCol_7
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 5
+ bottomLeftRadius: 0
+ bottomRightRadius: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0049
+ x: xPlacementCol_8
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 15
+ color: "red"
+ }
+ Rectangle{
+ id: r_0050
+ x: xPlacementCol_9
+ y: yPlacementRow_4
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 5
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ color: "red"
+ }
+
+ /*
+
+ DIFFERENT RADII; LOW FREQ GRAD HORIZONTAL
+
+ */
+
+ Gradient {
+ id: lowFGradH
+ orientation: Qt.Horizontal
+ GradientStop { position: 0.0; color: "green" }
+ GradientStop { position: 0.33; color: "blue" }
+ GradientStop { position: 0.66; color: "red" }
+ GradientStop { position: 1.0; color: "yellow" }
+ }
+
+ Rectangle{
+ id: r_0051
+ x: xPlacementCol_0
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 5
+ bottomLeftRadius: 10
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0052
+ x: xPlacementCol_1
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0053
+ x: xPlacementCol_2
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0054
+ x: xPlacementCol_3
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 10
+ bottomLeftRadius: 15
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0055
+ x: xPlacementCol_4
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 10
+ bottomLeftRadius: 5
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0056
+ x: xPlacementCol_5
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 15
+ bottomLeftRadius: 10
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0057
+ x: xPlacementCol_6
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 0
+ bottomLeftRadius: 15
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0058
+ x: xPlacementCol_7
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 5
+ bottomLeftRadius: 0
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0059
+ x: xPlacementCol_8
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+ Rectangle{
+ id: r_0060
+ x: xPlacementCol_9
+ y: yPlacementRow_5
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 5
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ }
+
+ /*
+
+ DIFFERENT RADII; LOW FREQ GRAD VERTICAL
+
+ */
+
+ Gradient {
+ id: lowFGradV
+ orientation: Qt.Vertical
+ GradientStop { position: 0.0; color: "green" }
+ GradientStop { position: 0.33; color: "blue" }
+ GradientStop { position: 0.66; color: "red" }
+ GradientStop { position: 1.0; color: "yellow" }
+ }
+
+ Rectangle{
+ id: r_0061
+ x: xPlacementCol_0
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 5
+ bottomLeftRadius: 10
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0062
+ x: xPlacementCol_1
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0063
+ x: xPlacementCol_2
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0064
+ x: xPlacementCol_3
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 10
+ bottomLeftRadius: 15
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0065
+ x: xPlacementCol_4
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 10
+ bottomLeftRadius: 5
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0066
+ x: xPlacementCol_5
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 15
+ bottomLeftRadius: 10
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0067
+ x: xPlacementCol_6
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 0
+ bottomLeftRadius: 15
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0068
+ x: xPlacementCol_7
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 5
+ bottomLeftRadius: 0
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0069
+ x: xPlacementCol_8
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+ Rectangle{
+ id: r_0070
+ x: xPlacementCol_9
+ y: yPlacementRow_6
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 5
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradV
+ }
+
+ /*
+
+ DIFFERENT RADII; HIGH FREQ GRAD HORIZONTAL
+
+ */
+
+ Gradient {
+ id: highFGradH
+ orientation: Qt.Horizontal
+ GradientStop { position: 0.00; color: "yellow" }
+ GradientStop { position: 0.05; color: "red" }
+ GradientStop { position: 0.10; color: "yellow" }
+ GradientStop { position: 0.15; color: "red" }
+ GradientStop { position: 0.20; color: "yellow" }
+ GradientStop { position: 0.25; color: "red" }
+ GradientStop { position: 0.30; color: "yellow" }
+ GradientStop { position: 0.35; color: "red" }
+ GradientStop { position: 0.40; color: "yellow" }
+ GradientStop { position: 0.45; color: "red" }
+ GradientStop { position: 0.50; color: "yellow" }
+ GradientStop { position: 0.55; color: "red" }
+ GradientStop { position: 0.60; color: "yellow" }
+ GradientStop { position: 0.65; color: "red" }
+ GradientStop { position: 0.70; color: "yellow" }
+ GradientStop { position: 0.75; color: "red" }
+ GradientStop { position: 0.80; color: "yellow" }
+ GradientStop { position: 0.85; color: "red" }
+ GradientStop { position: 0.90; color: "yellow" }
+ GradientStop { position: 0.95; color: "red" }
+ GradientStop { position: 1.00; color: "yellow" }
+ }
+
+ Rectangle{
+ id: r_0071
+ x: xPlacementCol_0
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 5
+ bottomLeftRadius: 10
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0072
+ x: xPlacementCol_1
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0073
+ x: xPlacementCol_2
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0074
+ x: xPlacementCol_3
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 10
+ bottomLeftRadius: 15
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0075
+ x: xPlacementCol_4
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 10
+ bottomLeftRadius: 5
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0076
+ x: xPlacementCol_5
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 15
+ bottomLeftRadius: 10
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0077
+ x: xPlacementCol_6
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 0
+ bottomLeftRadius: 15
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0078
+ x: xPlacementCol_7
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 5
+ bottomLeftRadius: 0
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0079
+ x: xPlacementCol_8
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+ Rectangle{
+ id: r_0080
+ x: xPlacementCol_9
+ y: yPlacementRow_7
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 5
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradH
+ }
+
+ /*
+
+ DIFFERENT RADII; HIGH FREQ GRAD VERTICAL
+
+ */
+
+ Gradient {
+ id: highFGradV
+ orientation: Qt.Vertical
+ GradientStop { position: 0.00; color: "yellow" }
+ GradientStop { position: 0.05; color: "red" }
+ GradientStop { position: 0.10; color: "yellow" }
+ GradientStop { position: 0.15; color: "red" }
+ GradientStop { position: 0.20; color: "yellow" }
+ GradientStop { position: 0.25; color: "red" }
+ GradientStop { position: 0.30; color: "yellow" }
+ GradientStop { position: 0.35; color: "red" }
+ GradientStop { position: 0.40; color: "yellow" }
+ GradientStop { position: 0.45; color: "red" }
+ GradientStop { position: 0.50; color: "yellow" }
+ GradientStop { position: 0.55; color: "red" }
+ GradientStop { position: 0.60; color: "yellow" }
+ GradientStop { position: 0.65; color: "red" }
+ GradientStop { position: 0.70; color: "yellow" }
+ GradientStop { position: 0.75; color: "red" }
+ GradientStop { position: 0.80; color: "yellow" }
+ GradientStop { position: 0.85; color: "red" }
+ GradientStop { position: 0.90; color: "yellow" }
+ GradientStop { position: 0.95; color: "red" }
+ GradientStop { position: 1.00; color: "yellow" }
+ }
+
+ Rectangle{
+ id: r_0081
+ x: xPlacementCol_0
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 5
+ bottomLeftRadius: 10
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0082
+ x: xPlacementCol_1
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0083
+ x: xPlacementCol_2
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0084
+ x: xPlacementCol_3
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 10
+ bottomLeftRadius: 15
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0085
+ x: xPlacementCol_4
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 10
+ bottomLeftRadius: 5
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0086
+ x: xPlacementCol_5
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 15
+ bottomLeftRadius: 10
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0087
+ x: xPlacementCol_6
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 0
+ bottomLeftRadius: 15
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0088
+ x: xPlacementCol_7
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 5
+ bottomLeftRadius: 0
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0089
+ x: xPlacementCol_8
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+ Rectangle{
+ id: r_0090
+ x: xPlacementCol_9
+ y: yPlacementRow_8
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 5
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: highFGradV
+ }
+
+ /*
+
+ TRANSPARENCY
+
+ */
+
+ Rectangle{
+ id: r_0091
+ x: xPlacementCol_0
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 10
+ topLeftRadius: 0
+ topRightRadius: 5
+ bottomLeftRadius: 10
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0092
+ x: xPlacementCol_1
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0093
+ x: xPlacementCol_2
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0094
+ x: xPlacementCol_3
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 10
+ bottomLeftRadius: 15
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0095
+ x: xPlacementCol_4
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 15
+ topRightRadius: 10
+ bottomLeftRadius: 5
+ bottomRightRadius: 0
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0096
+ x: xPlacementCol_5
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 0
+ topRightRadius: 15
+ bottomLeftRadius: 10
+ bottomRightRadius: 5
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0097
+ x: xPlacementCol_6
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 5
+ topRightRadius: 0
+ bottomLeftRadius: 15
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0098
+ x: xPlacementCol_7
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 5
+ bottomLeftRadius: 0
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0099
+ x: xPlacementCol_8
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ topLeftRadius: 10
+ topRightRadius: 0
+ bottomLeftRadius: 5
+ bottomRightRadius: 15
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+ Rectangle{
+ id: r_0100
+ x: xPlacementCol_9
+ y: yPlacementRow_9
+ width: r_0000.standardRectWidth
+ height: r_0000.standardRectHeight
+ radius: 0
+ topLeftRadius: 5
+ topRightRadius: 15
+ bottomLeftRadius: 0
+ bottomRightRadius: 10
+ border.color: "black"
+ border.width: 2
+ gradient: lowFGradH
+ opacity: 0.3
+ }
+}
+
diff --git a/tests/baseline/scenegraph/data/shape/OrderedPaths.qml b/tests/baseline/scenegraph/data/shape/OrderedPaths.qml
new file mode 100644
index 0000000000..688fbfdb59
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/OrderedPaths.qml
@@ -0,0 +1,141 @@
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ id: root
+ property bool async: false
+
+ property int counter: 0
+ NumberAnimation {
+ target: root
+ property: "counter"
+ duration: 2000
+ from: 0
+ to: 15
+ running: true
+ }
+
+ component RectStack : Shape {
+ asynchronous: root.async
+
+ property alias p1FillColor: p1.fillColor
+ property alias p2FillColor: p2.fillColor
+ property alias p3FillColor: p3.fillColor
+ property alias p4FillColor: p4.fillColor
+ property alias p1StrokeColor: p1.strokeColor
+ property alias p2StrokeColor: p2.strokeColor
+ property alias p3StrokeColor: p3.strokeColor
+ property alias p4StrokeColor: p4.strokeColor
+
+ ShapePath {
+ id: p1
+ fillColor: "black"
+ strokeColor: "transparent"
+ strokeWidth: 5
+ startX: 10
+ startY: 10
+ PathLine { relativeX: 100; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 60 }
+ PathLine { relativeX: -100; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -60 }
+ }
+ ShapePath {
+ id: p2
+ fillColor: "red"
+ strokeColor: "transparent"
+ strokeWidth: 5
+ startX: 20
+ startY: 15
+ PathLine { relativeX: 100; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 60 }
+ PathLine { relativeX: -100; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -60 }
+ }
+ ShapePath {
+ id: p3
+ fillColor: "green"
+ strokeColor: "transparent"
+ strokeWidth: 5
+ startX: 30
+ startY: 20
+ PathLine { relativeX: 100; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 60 }
+ PathLine { relativeX: -100; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -60 }
+ }
+ ShapePath {
+ id: p4
+ fillColor: "blue"
+ strokeColor: "transparent"
+ strokeWidth: 5
+ startX: 40
+ startY: 25
+ PathLine { relativeX: 100; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 60 }
+ PathLine { relativeX: -100; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -60 }
+ }
+ }
+
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ spacing: 5
+ Repeater {
+ model: renderers
+ Column {
+ spacing: 5
+
+ RectStack {
+ preferredRendererType: renderer
+ }
+
+ RectStack {
+ preferredRendererType: renderer
+ p1FillColor: counter % 16 >= 8 ? "black" : "transparent"
+ p2FillColor: counter % 8 >= 4 ? "red" : "transparent"
+ p3FillColor: counter % 4 >= 2 ? "green" : "transparent"
+ p4FillColor: counter % 2 >= 1 ? "blue" : "transparent"
+ }
+
+ RectStack {
+ preferredRendererType: renderer
+ property int shifter: counter < 4 ? counter : counter + 1
+ p1FillColor: shifter % 2 == 0 ? "black" : "transparent"
+ p2FillColor: counter % 2 == 0 ? "red" : "transparent"
+ p3FillColor: shifter % 2 == 1 ? "green" : "transparent"
+ p4FillColor: counter % 2 == 1 ? "blue" : "transparent"
+ }
+
+ RectStack {
+ preferredRendererType: renderer
+ p1FillColor: counter % 16 >= 8 ? "black" : "transparent"
+ p2FillColor: counter % 8 >= 4 ? "red" : "transparent"
+ p3FillColor: counter % 4 >= 2 ? "green" : "transparent"
+ p4FillColor: counter % 2 >= 1 ? "blue" : "transparent"
+ p1StrokeColor: counter % 2 >= 1 ? "transparent" : "lightblue"
+ p2StrokeColor: counter % 4 >= 2 ? "transparent" : "lightgreen"
+ p3StrokeColor: counter % 8 >= 4 ? "transparent" : "pink"
+ p4StrokeColor: counter % 16 >= 8 ? "transparent" : "gray"
+ }
+
+ RectStack {
+ preferredRendererType: renderer
+ p1FillColor: "transparent"
+ p2FillColor: "transparent"
+ p3FillColor: "transparent"
+ p4FillColor: "transparent"
+ p1StrokeColor: counter % 16 >= 8 ? "lightblue": "transparent"
+ p2StrokeColor: counter % 8 >= 4 ? "lightgreen": "transparent"
+ p3StrokeColor: counter % 4 >= 2 ? "pink": "transparent"
+ p4StrokeColor: counter % 2 >= 1 ? "gray" : "transparent"
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_alignment.qml b/tests/baseline/scenegraph/data/shape/shape_alignment.qml
new file mode 100644
index 0000000000..6a46c9b692
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_alignment.qml
@@ -0,0 +1,90 @@
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 400
+ height: 600
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ component ExampleShape : Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "black"
+ fillColor: "gray"
+
+ startX: 0; startY: 0
+ PathLine { x: 100; y: 0 }
+ PathLine { x: 100; y: 30 }
+ PathLine { x: 0; y: 30 }
+ PathLine { x: 0; y: 0 }
+ }
+ ShapePath {
+ strokeWidth: 2
+ strokeColor: "yellow"
+ fillColor: "transparent"
+ startX: 0; startY: 0
+ PathLine { x: 100; y: 30 }
+ }
+ z: -1
+ }
+
+ component ParentRect: Rectangle {
+ width: 150
+ height: 100
+ color: "transparent"
+ border.color: "blue"
+ border.width: 2
+ radius: 10
+ }
+
+ Row {
+ x: 10
+ y: 10
+ spacing: 20
+ Repeater {
+ model: renderers
+ Column {
+ spacing: 10
+ ParentRect {
+ ExampleShape {
+ }
+ }
+ ParentRect {
+ ExampleShape {
+ horizontalAlignment: Shape.AlignHCenter
+ verticalAlignment: Shape.AlignBottom
+ }
+ }
+ ParentRect {
+ ExampleShape {
+ horizontalAlignment: Shape.AlignHCenter
+ verticalAlignment: Shape.AlignVCenter
+ fillMode: Shape.PreserveAspectFit
+ }
+ }
+ ParentRect {
+ clip: true
+ ExampleShape {
+ horizontalAlignment: Shape.AlignHCenter
+ verticalAlignment: Shape.AlignVCenter
+ fillMode: Shape.PreserveAspectCrop
+ }
+ }
+ ParentRect {
+ ExampleShape {
+ horizontalAlignment: Shape.AlignHCenter
+ verticalAlignment: Shape.AlignBottom
+ fillMode: Shape.Stretch
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_arc.qml b/tests/baseline/scenegraph/data/shape/shape_arc.qml
index 0b2396012e..7a767ab07a 100644
--- a/tests/baseline/scenegraph/data/shape/shape_arc.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_arc.qml
@@ -1,108 +1,119 @@
import QtQuick 2.9
-import QtQuick.Shapes 1.0
+import QtQuick.Shapes 6.6
Item {
width: 320
height: 480
- Column {
- Item {
- width: 200
- height: 100
-
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 4; startY: 4
- PathArc {
- id: arc
- x: 96; y: 96
- radiusX: 100; radiusY: 100
- direction: model.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ Repeater {
+ model: renderers
+ Column {
+ Item {
+ width: 160
+ height: 100
+
+ Repeater {
+ model: 2
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: model.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 4; startY: 4
+ PathArc {
+ id: arc
+ x: 96; y: 96
+ radiusX: 100; radiusY: 100
+ direction: model.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise
+ }
+ }
}
}
}
- }
- }
- Item {
- width: 200
- height: 100
-
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 50; startY: 100
- PathArc {
- x: 100; y: 150
- radiusX: 50; radiusY: 50
- useLargeArc: model.index === 1
+ Item {
+ width: 160
+ height: 100
+
+ Repeater {
+ model: 2
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: model.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 10; startY: 100
+ PathArc {
+ x: 60; y: 150
+ radiusX: 50; radiusY: 50
+ useLargeArc: model.index === 1
+ }
+ }
}
}
}
- }
- }
- Item {
- width: 200
- height: 100
-
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 50; startY: 150
- PathArc {
- x: 150; y: 150
- radiusX: 50; radiusY: 20
- xAxisRotation: model.index === 0 ? 0 : 45
+ Item {
+ width: 160
+ height: 100
+
+ Repeater {
+ model: 2
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: model.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 30; startY: 150
+ PathArc {
+ x: 130; y: 150
+ radiusX: 50; radiusY: 20
+ xAxisRotation: model.index === 0 ? 0 : 45
+ }
+ }
}
}
- }
- }
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
-
- ShapePath {
- fillColor: "transparent"
- strokeColor: model.index === 0 ? "red" : "blue"
-
- startX: 50; startY: 150
- PathArc {
- x: 150; y: 150
- radiusX: 50; radiusY: 20
- xAxisRotation: model.index === 0 ? 0 : 45
- direction: PathArc.Counterclockwise
+ Repeater {
+ model: 2
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: model.index === 0 ? "red" : "blue"
+
+ startX: 30; startY: 150
+ PathArc {
+ x: 130; y: 150
+ radiusX: 50; radiusY: 20
+ xAxisRotation: model.index === 0 ? 0 : 45
+ direction: PathArc.Counterclockwise
+ }
+ }
}
}
}
diff --git a/tests/baseline/scenegraph/data/shape/shape_arc_fill.qml b/tests/baseline/scenegraph/data/shape/shape_arc_fill.qml
index fefc2ec3eb..f822d6b53f 100644
--- a/tests/baseline/scenegraph/data/shape/shape_arc_fill.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_arc_fill.qml
@@ -1,108 +1,119 @@
import QtQuick 2.9
-import QtQuick.Shapes 1.0
+import QtQuick.Shapes 6.6
Item {
width: 320
height: 480
- Column {
- Item {
- width: 200
- height: 100
-
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
-
- ShapePath {
- fillColor: "lightBlue"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 4; startY: 4
- PathArc {
- id: arc
- x: 96; y: 96
- radiusX: 100; radiusY: 100
- direction: model.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ Repeater {
+ model: renderers
+ Column {
+ Item {
+ width: 160
+ height: 100
+
+ Repeater {
+ model: 2
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ fillColor: "lightBlue"
+ strokeColor: model.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 4; startY: 4
+ PathArc {
+ id: arc
+ x: 96; y: 96
+ radiusX: 100; radiusY: 100
+ direction: model.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise
+ }
+ }
}
}
}
- }
- }
- Item {
- width: 200
- height: 100
-
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
-
- ShapePath {
- fillColor: "green"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 50; startY: 100
- PathArc {
- x: 100; y: 150
- radiusX: 50; radiusY: 50
- useLargeArc: model.index === 1
+ Item {
+ width: 160
+ height: 100
+
+ Repeater {
+ model: 2
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ fillColor: "green"
+ strokeColor: model.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 10; startY: 100
+ PathArc {
+ x: 60; y: 150
+ radiusX: 50; radiusY: 50
+ useLargeArc: model.index === 1
+ }
+ }
}
}
}
- }
- }
- Item {
- width: 200
- height: 100
-
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
-
- ShapePath {
- fillColor: "gray"
- strokeColor: model.index === 0 ? "red" : "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
-
- startX: 50; startY: 150
- PathArc {
- x: 150; y: 150
- radiusX: 50; radiusY: 20
- xAxisRotation: model.index === 0 ? 0 : 45
+ Item {
+ width: 160
+ height: 100
+
+ Repeater {
+ model: 2
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ fillColor: "gray"
+ strokeColor: model.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 30; startY: 150
+ PathArc {
+ x: 130; y: 150
+ radiusX: 50; radiusY: 20
+ xAxisRotation: model.index === 0 ? 0 : 45
+ }
+ }
}
}
- }
- }
- Repeater {
- model: 2
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
-
- ShapePath {
- fillColor: "lightGray"
- strokeColor: model.index === 0 ? "red" : "blue"
-
- startX: 50; startY: 150
- PathArc {
- x: 150; y: 150
- radiusX: 50; radiusY: 20
- xAxisRotation: model.index === 0 ? 0 : 45
- direction: PathArc.Counterclockwise
+ Repeater {
+ model: 2
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ fillColor: "lightGray"
+ strokeColor: model.index === 0 ? "red" : "blue"
+
+ startX: 30; startY: 150
+ PathArc {
+ x: 130; y: 150
+ radiusX: 50; radiusY: 20
+ xAxisRotation: model.index === 0 ? 0 : 45
+ direction: PathArc.Counterclockwise
+ }
+ }
}
}
}
diff --git a/tests/baseline/scenegraph/data/shape/shape_boundingrect.qml b/tests/baseline/scenegraph/data/shape/shape_boundingrect.qml
new file mode 100644
index 0000000000..e82c4d3b2b
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_boundingrect.qml
@@ -0,0 +1,46 @@
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 620
+ height: 400
+ transformOrigin: Item.TopLeft
+
+
+ property var svgPaths: [
+ "M 0 25 L 200 25",
+ "M 0 0 L 215 125",
+ "M 0 0 L 175 175",
+ "M 100 0 Q 200 0 200 75 Q 200 150 100 150 Q 0 150 0 75 Q 0 0 100 0 z",
+ "M 50 0 L 50 150"
+ ]
+
+ Flow {
+ anchors.fill: parent
+ padding: 25
+ spacing: 25
+ Repeater {
+ model: svgPaths
+ Shape {
+ preferredRendererType: Shape.CurveRenderer
+ ShapePath {
+ strokeColor: "red"
+ strokeWidth: 22
+ fillColor: "gray"
+ capStyle: ShapePath.RoundCap
+ PathSvg { path: modelData }
+ }
+ Rectangle {
+ color: "transparent"
+ border.color: "blue"
+ border.width: 2
+ x: parent.boundingRect.x
+ y: parent.boundingRect.y
+ width: parent.boundingRect.width
+ height: parent.boundingRect.height
+ }
+ }
+}
+
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_boundingrect_square_cap.qml b/tests/baseline/scenegraph/data/shape/shape_boundingrect_square_cap.qml
new file mode 100644
index 0000000000..d4c714cb86
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_boundingrect_square_cap.qml
@@ -0,0 +1,45 @@
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 620
+ height: 400
+ transformOrigin: Item.TopLeft
+
+ property var svgPaths: [
+ "M 0 25 L 200 25",
+ "M 0 0 L 215 125",
+ "M 0 0 L 175 175",
+ "M 100 0 Q 200 0 200 75 Q 200 150 100 150 Q 0 150 0 75 Q 0 0 100 0 z",
+ "M 50 0 L 50 150"
+ ]
+
+ Flow {
+ anchors.fill: parent
+ padding: 25
+ spacing: 25
+ Repeater {
+ model: svgPaths
+ Shape {
+ preferredRendererType: Shape.CurveRenderer
+ ShapePath {
+ strokeColor: "red"
+ strokeWidth: 22
+ fillColor: "gray"
+ capStyle: ShapePath.SquareCap
+ PathSvg { path: modelData }
+ }
+ Rectangle {
+ color: "transparent"
+ border.color: "blue"
+ border.width: 2
+ x: parent.boundingRect.x
+ y: parent.boundingRect.y
+ width: parent.boundingRect.width
+ height: parent.boundingRect.height
+ }
+ }
+ }
+
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_cubic.qml b/tests/baseline/scenegraph/data/shape/shape_cubic.qml
index 1d2f9fd40d..8db08b4268 100644
--- a/tests/baseline/scenegraph/data/shape/shape_cubic.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_cubic.qml
@@ -1,31 +1,42 @@
import QtQuick 2.9
-import QtQuick.Shapes 1.0
+import QtQuick.Shapes 6.6
Item {
width: 320
height: 480
- Column {
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
Repeater {
- model: 4
- Item {
- width: 200
- height: 100
+ model: renderers
+ Column {
+ Repeater {
+ model: 4
+ Item {
+ width: 160
+ height: 100
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
- ShapePath {
- strokeWidth: (model.index + 2) * 2
- strokeColor: "black"
- fillColor: "lightBlue"
+ ShapePath {
+ strokeWidth: (model.index + 2) * 2
+ strokeColor: "black"
+ fillColor: "lightBlue"
- startX: 50; startY: 100
- PathCubic {
- x: 150; y: 100
- control1X: model.index * 10; control1Y: model.index * 5
- control2X: model.index * -10; control2Y: model.index * 10
+ startX: 30; startY: 100
+ PathCubic {
+ x: 130; y: 100
+ control1X: model.index * 10; control1Y: model.index * 5
+ control2X: model.index * -10; control2Y: model.index * 10
+ }
+ }
}
}
}
diff --git a/tests/baseline/scenegraph/data/shape/shape_dashcurves.qml b/tests/baseline/scenegraph/data/shape/shape_dashcurves.qml
new file mode 100644
index 0000000000..f4f0ca72ea
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_dashcurves.qml
@@ -0,0 +1,547 @@
+import QtQuick 2.9
+import QtQuick.Shapes 6.6
+
+Item {
+ id: root
+ width: 320
+ height: 480
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ Repeater {
+ model: renderers
+ Item {
+ width: root.width / 2
+ height: root.height
+
+ Rectangle {
+ x: 10
+ y: 5
+ width : 140
+ height: 470
+ color: "yellow"
+ }
+
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ startY: 5
+ strokeWidth: 1
+ dashOffset: 0
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 10.5
+ strokeWidth: 1
+ dashOffset: 0
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 15
+ strokeWidth: 1
+ dashOffset: 0
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 20
+ strokeWidth: 3
+ dashOffset: 0
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 25
+ strokeWidth: 3
+ dashOffset: 0
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 30
+ strokeWidth: 3
+ dashOffset: 0
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 40
+ strokeWidth: 3
+ dashOffset: 0.5
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 45
+ strokeWidth: 3
+ dashOffset: 2
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 50
+ strokeWidth: 3
+ dashOffset: 4
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 55
+ strokeWidth: 3
+ dashOffset: 100.7
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 60
+ strokeWidth: 3
+ dashOffset: 0
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 65
+ strokeWidth: 3
+ dashOffset: 6
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 70
+ strokeWidth: 3
+ dashOffset: -6
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 75
+ strokeWidth: 3
+ dashOffset: -2.5
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 85
+ strokeWidth: 3
+ dashPattern: [1, 1]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 90
+ strokeWidth: 3
+ dashPattern: [1.3, 1]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 95
+ strokeWidth: 3
+ dashPattern: [1, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 100
+ strokeWidth: 3
+ dashPattern: [2, 1]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 105
+ strokeWidth: 3
+ dashPattern: [2, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 110
+ strokeWidth: 3
+ dashPattern: [4, 2, 2, 3, 5, 1]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 115
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 120
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ dashOffset: 1
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 125
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ dashOffset: 3
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 130
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ dashOffset: 6
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 135
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ dashOffset: 10
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+
+ ShapePath {
+ startY: 150
+ strokeWidth: 7
+ dashPattern: [3, 2]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 160
+ strokeWidth: 7
+ dashPattern: [3, 1.5]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 170
+ strokeWidth: 7
+ dashPattern: [3, 1]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 180
+ strokeWidth: 7
+ dashPattern: [3, 0.7]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 190
+ strokeWidth: 7
+ dashPattern: [3, 0.2]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 200
+ strokeWidth: 7
+ dashPattern: [3, 0]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ // QTBUG-123805
+ startY: 210
+ strokeWidth: 5.8575662686300545;
+ dashPattern: [2, 2]
+ dashOffset: 4
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 220
+ strokeWidth: 3
+ dashPattern: [3, 3, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 225
+ strokeWidth: 3
+ dashPattern: [0, 3, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 230
+ strokeWidth: 3
+ dashPattern: [-3, 3, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 235
+ strokeWidth: 3
+ dashPattern: [-3, 3, 2]
+ dashOffset: 1
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 240
+ strokeWidth: 3
+ dashPattern: [-3, 3, 2]
+ dashOffset: -1
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 245
+ strokeWidth: 3
+ dashPattern: [-5, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 250
+ strokeWidth: 3
+ dashPattern: [5, -2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 255
+ strokeWidth: 3
+ dashPattern: [-5, -2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 260
+ strokeWidth: 3
+ dashPattern: [-5, -2, -4]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 265
+ strokeWidth: 3
+ dashPattern: [-5, -2, 4]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathQuad { relativeControlX: 30; relativeControlY: 30; relativeX: 140; relativeY: 0 }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_dashlines.qml b/tests/baseline/scenegraph/data/shape/shape_dashlines.qml
new file mode 100644
index 0000000000..a1ea4a315e
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_dashlines.qml
@@ -0,0 +1,505 @@
+import QtQuick 2.9
+import QtQuick.Shapes 6.6
+
+Item {
+ id: root
+ width: 320
+ height: 480
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ Repeater {
+ model: renderers
+ Item {
+ width: root.width / 2
+ height: root.height
+
+ Rectangle {
+ x: 10
+ y: 5
+ width : 140
+ height: 470
+ color: "yellow"
+ }
+
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ startY: 5
+ strokeWidth: 1
+ dashOffset: 0
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 10.5
+ strokeWidth: 1
+ dashOffset: 0
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 15
+ strokeWidth: 1
+ dashOffset: 0
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 20
+ strokeWidth: 3
+ dashOffset: 0
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 25
+ strokeWidth: 3
+ dashOffset: 0
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 30
+ strokeWidth: 3
+ dashOffset: 0
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 40
+ strokeWidth: 3
+ dashOffset: 0.5
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 45
+ strokeWidth: 3
+ dashOffset: 2
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 50
+ strokeWidth: 3
+ dashOffset: 4
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 55
+ strokeWidth: 3
+ dashOffset: 100.7
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 60
+ strokeWidth: 3
+ dashOffset: 0
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 65
+ strokeWidth: 3
+ dashOffset: 6
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 70
+ strokeWidth: 3
+ dashOffset: -6
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 75
+ strokeWidth: 3
+ dashOffset: -2.5
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 85
+ strokeWidth: 3
+ dashPattern: [1, 1]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 90
+ strokeWidth: 3
+ dashPattern: [1.3, 1]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 95
+ strokeWidth: 3
+ dashPattern: [1, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 100
+ strokeWidth: 3
+ dashPattern: [2, 1]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 105
+ strokeWidth: 3
+ dashPattern: [2, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 110
+ strokeWidth: 3
+ dashPattern: [4, 2, 2, 3, 5, 1]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 115
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 120
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ dashOffset: 1
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 125
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ dashOffset: 3
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 130
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ dashOffset: 6
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 135
+ strokeWidth: 3
+ dashPattern: [1, 2, 3, 4]
+ dashOffset: 10
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+
+ ShapePath {
+ startY: 150
+ strokeWidth: 7
+ dashPattern: [3, 2]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 160
+ strokeWidth: 7
+ dashPattern: [3, 1.5]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 170
+ strokeWidth: 7
+ dashPattern: [3, 1]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 180
+ strokeWidth: 7
+ dashPattern: [3, 0.7]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 190
+ strokeWidth: 7
+ dashPattern: [3, 0.2]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 200
+ strokeWidth: 7
+ dashPattern: [3, 0]
+ capStyle: ShapePath.RoundCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ // QTBUG-123805
+ startY: 210
+ strokeWidth: 5.8575662686300545;
+ dashPattern: [2, 2]
+ dashOffset: 4
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 220
+ strokeWidth: 3
+ dashPattern: [3, 3, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 225
+ strokeWidth: 3
+ dashPattern: [0, 3, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 230
+ strokeWidth: 3
+ dashPattern: [-3, 3, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 235
+ strokeWidth: 3
+ dashPattern: [-3, 3, 2]
+ dashOffset: 1
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 240
+ strokeWidth: 3
+ dashPattern: [-3, 3, 2]
+ dashOffset: -1
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 245
+ strokeWidth: 3
+ dashPattern: [-5, 2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 250
+ strokeWidth: 3
+ dashPattern: [5, -2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 255
+ strokeWidth: 3
+ dashPattern: [-5, -2]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 260
+ strokeWidth: 3
+ dashPattern: [-5, -2, -4]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+
+ ShapePath {
+ startY: 265
+ strokeWidth: 3
+ dashPattern: [-5, -2, 4]
+ capStyle: ShapePath.FlatCap
+ strokeColor: "blue"
+ startX: 10
+ strokeStyle: ShapePath.DashLine
+ PathLine { relativeX: 140; relativeY: 0 }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_implicit_size.qml b/tests/baseline/scenegraph/data/shape/shape_implicit_size.qml
new file mode 100644
index 0000000000..49481a5417
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_implicit_size.qml
@@ -0,0 +1,91 @@
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 320
+ height: 480
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+
+ component ExampleShape : Shape {
+ preferredRendererType: renderer
+ ShapePath {
+ strokeWidth: 2
+ strokeColor: "black"
+ fillColor: "#77ff99"
+
+ startX: 30; startY: 30
+ PathQuad {
+ x: 100; y: 100
+ controlX: 30; controlY: 100
+ }
+ }
+ ShapePath {
+ fillColor: "gray"
+ strokeColor: "transparent"
+ PathText {
+ x: 30
+ y: 50
+ font.family: "Arial"
+ font.pixelSize: 64
+ text: "Test"
+ }
+ }
+ }
+
+ component ShowRect: Rectangle {
+ color: "transparent"
+ border.color: "blue"
+ border.width: 2
+ radius: 10
+ }
+
+ Row {
+ x: 10
+ y: 10
+ Repeater {
+ model: renderers
+ Column {
+ Item {
+ width: 150
+ height: 150
+ ExampleShape {
+ id: testSize
+ }
+ ShowRect {
+ anchors.fill: testSize
+ }
+ }
+ Item {
+ width: 150
+ height: 150
+ ExampleShape {
+ id: testBoundingRect
+ }
+ ShowRect {
+ x: testBoundingRect.boundingRect.x
+ y: testBoundingRect.boundingRect.y
+ width: testBoundingRect.boundingRect.width
+ height: testBoundingRect.boundingRect.height
+ }
+ }
+ Item {
+ width: 150
+ height: 150
+ ExampleShape {
+ id: testImplicitSize
+ }
+ ShowRect {
+ width: testImplicitSize.implicitWidth
+ height: testImplicitSize.implicitHeight
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_intersecting1.qml b/tests/baseline/scenegraph/data/shape/shape_intersecting1.qml
new file mode 100644
index 0000000000..a507b40c6d
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_intersecting1.qml
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 600
+ height: 600
+
+ ListModel {
+ id: fillRules
+ ListElement { fillrule: ShapePath.WindingFill }
+ ListElement { fillrule: ShapePath.OddEvenFill }
+ }
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ ListModel {
+ id: svgstrings
+ ListElement { scaleToFit: 0.11; offsetX: 20; offsetY: 20; pathString: "M 885.276 1047.5 Q 1310 -265 722.654 1071.85 Q 1100 1100 636.857 929.582 Q 100 1100 322.456 883.006 L 1003.49 134.715 Q 944.382 591.106 0 0 L 885.276 1047.5"}
+ ListElement { scaleToFit: 0.035; offsetX: -60; offsetY: 0; pathString: "M 5000 3475 Q 5600 825 2290 245 Q 3635 -30 5360 3885 Q 730 2990 4685 390 L 2445 1435 L 5000 3475" }
+ ListElement { scaleToFit: 0.11; offsetX: 10; offsetY: 10; pathString: "M 600 200 Q 1000 600 600 1000 Q 200 1200 200 600 Q 200 200 600 200 M 1000 200 Q 1400 600 1000 1000 Q 600 1200 600 600 Q 200 -200 1000 200" }
+ ListElement { scaleToFit: 0.11; offsetX: -10; offsetY: 10; pathString: "M 865 270 Q 1000 600 955 820 Q 675 690 775 525 Q 200 200 865 270 M 1000 200 Q 1400 600 1000 1000 Q 600 1200 590 635 Q 200 -200 1000 200" }
+ }
+
+ Column {
+ Repeater {
+ model: renderers
+ Column {
+ Repeater {
+ model: fillRules
+ Row {
+ Repeater {
+ model: svgstrings
+ Rectangle {
+ width: 150
+ height: 150
+ border.color: "black"
+
+ Shape {
+ preferredRendererType: renderer
+ ShapePath {
+ fillColor: renderer == Shape.CurveRenderer ? "#99483d8b" : "#99dc143c"
+ fillRule: fillrule
+ strokeWidth: 0
+ PathSvg { path: pathString }
+ }
+
+ transform: Matrix4x4 {
+ matrix: Qt.matrix4x4(scaleToFit, 0, 0, offsetX,
+ 0, scaleToFit, 0, offsetY,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_intersecting2.qml b/tests/baseline/scenegraph/data/shape/shape_intersecting2.qml
new file mode 100644
index 0000000000..d2e0fd1bd0
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_intersecting2.qml
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 600
+ height: 600
+
+ ListModel {
+ id: fillRules
+ ListElement { fillrule: ShapePath.WindingFill }
+ ListElement { fillrule: ShapePath.OddEvenFill }
+ }
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ ListModel {
+ id: svgstrings
+ ListElement { scaleToFit: 0.11; offsetX: -10; offsetY: 10; pathString: "M 568.243 506.927 Q 1000 600 955 820 Q 675 690 1007.14 396.964 Q 616.417 79.1432 568.243 506.927 M 1000 200 Q 1400 600 1000 1000 Q 600 1200 590 635 Q 200 -200 1000 200" }
+ ListElement { scaleToFit: 0.11; offsetX: -10; offsetY: 10; pathString: "M 562.26 285.556 Q 728.371 717.267 955 820 Q 1037.57 642.136 1007.14 396.964 Q 616.417 79.1432 562.26 285.556 M 1000 200 Q 1400 600 1000 1000 Q 600 1200 590 635 Q 200 -200 1000 200" }
+ ListElement { scaleToFit: 0.09; offsetX: -10; offsetY: 10; pathString: "M 550.789, 661.103 Q 1435.03, -8.14249 1668.28, 799.321 Q 1100, 1100 593.639, 527.481 Q 593.639 527.481 100 1100 100 600 Q 100 600 1208.1 876.376 2316.21, 1152.75 Q 2316.21 1152.75 1433.5 906.927 550.789 661.103" }
+ ListElement { scaleToFit: 0.04; offsetX: 35; offsetY: 10; pathString: "M 846.361 1397.06 Q 2485 -660 2485 1015 Q 1598.13 4529.81 975 290 Q -69.0821 3853.43 -568.237 1473.61 L 2515.73 2262.31 L 846.361 1397.06" }
+ }
+
+ Column {
+ Repeater {
+ model: renderers
+ Column {
+ Repeater {
+ model: fillRules
+ Row {
+ Repeater {
+ model: svgstrings
+ Rectangle {
+ width: 150
+ height: 150
+ border.color: "black"
+
+ Shape {
+ preferredRendererType: renderer
+ ShapePath {
+ fillColor: renderer == Shape.CurveRenderer ? "#99483d8b" : "#99dc143c"
+ fillRule: fillrule
+ strokeWidth: 0
+ PathSvg { path: pathString }
+ }
+
+ transform: Matrix4x4 {
+ matrix: Qt.matrix4x4(scaleToFit, 0, 0, offsetX,
+ 0, scaleToFit, 0, offsetY,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_intersecting3.qml b/tests/baseline/scenegraph/data/shape/shape_intersecting3.qml
new file mode 100644
index 0000000000..f56ebc2a3b
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_intersecting3.qml
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 600
+ height: 600
+
+ ListModel {
+ id: fillRules
+ ListElement { fillrule: ShapePath.WindingFill }
+ ListElement { fillrule: ShapePath.OddEvenFill }
+ }
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ ListModel {
+ id: svgstrings
+ ListElement { scaleToFit: 0.11; offsetX: 20; offsetY: 20; pathString: "M 200 200 Q 600 1000 1000 200 Q -100 200 200 1000 Q -100 1000 -100 500 Q -300 200 200 200 M 300 200 L 800 700 L 300 700 L 800 200 L 300 200"}
+ ListElement { scaleToFit: 0.11; offsetX: 20; offsetY: 20; pathString: "M 200 200 Q 600 1000 1000 200 Q -100 200 200 1000 Q -100 1000 -100 500 Q -300 200 200 200 M 300 200 L 300 700 L 800 200 L 800 700 L 300 200"}
+ ListElement { scaleToFit: 1; offsetX: 0; offsetY: 0; pathString: "M 128.84 103.6 L 112.71 68.78 L 64.32 19.15 L 144.33 94.34 L 47.33 98.82 Q 37.0 60.87 39.01 12.06 L 51.92 72.5 L 19.87 130.09 L 139.11 47.81 Q 110.47 70.31 113.71 87.09 "}
+ ListElement { scaleToFit: 1; offsetX: 0; offsetY: 0; pathString: "M 85.23 92.08 Q 126.6 2.98 23.72 51.94 Z M 103.97 123.27 L 145.32 76.51 Z M 134.57 28.24 L 41.44 8.54 Z M 43.48 123.1 Q 58.58 39.75 92.72 144.17 Z M 23.86 116.3 Q 5.82 83.87 84.11 130.88"}
+ }
+
+ Column {
+ Repeater {
+ model: renderers
+ Column {
+ Repeater {
+ model: fillRules
+ Row {
+ Repeater {
+ model: svgstrings
+ Rectangle {
+ width: 150
+ height: 150
+ border.color: "black"
+
+ Shape {
+ preferredRendererType: renderer
+ ShapePath {
+ fillColor: renderer == Shape.CurveRenderer ? "#99483d8b" : "#99dc143c"
+ fillRule: fillrule
+ strokeWidth: 0
+ PathSvg { path: pathString }
+ }
+
+ transform: Matrix4x4 {
+ matrix: Qt.matrix4x4(scaleToFit, 0, 0, offsetX,
+ 0, scaleToFit, 0, offsetY,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_intersecting4.qml b/tests/baseline/scenegraph/data/shape/shape_intersecting4.qml
new file mode 100644
index 0000000000..6815906d34
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_intersecting4.qml
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 600
+ height: 600
+
+ ListModel {
+ id: fillRules
+ ListElement { fillrule: ShapePath.WindingFill }
+ ListElement { fillrule: ShapePath.OddEvenFill }
+ }
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ ListModel {
+ id: svgstrings
+ ListElement { scaleToFit: 1; offsetX: 0; offsetY: 0; pathString: "M 64.9 142.96 Q 4.68 9.31 7.25 121.42 Z M 5.3 86.37 Q 31.57 11.81 34.04 5.0 Q 25.26 47.39 31.31 87.97 Z M 4.2 17.35 L 33.27 124.0 Q 111.36 33.37 8.23 118.77 L 65.78 27.48 Q 139.84 87.06 96.03 23.08"}
+ ListElement { scaleToFit: 1; offsetX: 0; offsetY: 0; pathString: "M 88.78 105.24 L 69.72 114.27 L 26.94 71.0 Q 62.05 122.52 83.1 22.44 Q 105.46 40.25 108.12 54.23 L 103.51 40.41 Q 70.16 41.09 29.88 96.16 Z M 60.49 96.92 L 14.58 112.34 Q 1.76 12.05 106.52 128.97"}
+ // These don't work because there are subpaths with just two lines in it:
+ // ListElement { scaleToFit: 1; offsetX: 0; offsetY: 0; pathString: "M 70.21 146.61 L 54.01 6.38 Z M 52.51 5.75 L 70.75 44.78 L 81.98 128.43 Z M 2.71 72.51 L 96.86 4.48 L 136.96 83.88 Z M 25.32 52.79 Q 3.59 111.11 125.15 137.23"}
+ // ListElement { scaleToFit: 1; offsetX: 0; offsetY: 0; pathString: "M 131.61 147.43 L 4.5 6.5 L 110.44 40.71 Q 128.69 39.15 136.85 6.84 Q 8.9 139.71 76.35 103.86 L 77.47 43.63 L 127.06 97.65 L 14.97 66.38 Z M 47.53 0.08 L 42.02 33.25"}
+ // Removing those lines fixes the problem. We might want to do it in the intersectionSolver at some point.
+ ListElement { scaleToFit: 1; offsetX: 0; offsetY: 0; pathString: "M 52.51 5.75 L 70.75 44.78 L 81.98 128.43 Z M 2.71 72.51 L 96.86 4.48 L 136.96 83.88 Z M 25.32 52.79 Q 3.59 111.11 125.15 137.23"}
+ ListElement { scaleToFit: 1; offsetX: 0; offsetY: 0; pathString: "M 131.61 147.43 L 4.5 6.5 L 110.44 40.71 Q 128.69 39.15 136.85 6.84 Q 8.9 139.71 76.35 103.86 L 77.47 43.63 L 127.06 97.65 L 14.97 66.38 Z"}
+ }
+
+ Column {
+ Repeater {
+ model: renderers
+ Column {
+ Repeater {
+ model: fillRules
+ Row {
+ Repeater {
+ model: svgstrings
+ Rectangle {
+ width: 150
+ height: 150
+ border.color: "black"
+
+ Shape {
+ preferredRendererType: renderer
+ ShapePath {
+ fillColor: renderer == Shape.CurveRenderer ? "#99483d8b" : "#99dc143c"
+ fillRule: fillrule
+ strokeWidth: 0
+ PathSvg { path: pathString }
+ }
+
+ transform: Matrix4x4 {
+ matrix: Qt.matrix4x4(scaleToFit, 0, 0, offsetX,
+ 0, scaleToFit, 0, offsetY,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_intersecting5.qml b/tests/baseline/scenegraph/data/shape/shape_intersecting5.qml
new file mode 100644
index 0000000000..447b192182
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_intersecting5.qml
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 600
+ height: 600
+
+ ListModel {
+ id: fillRules
+ ListElement { fillrule: ShapePath.WindingFill }
+ ListElement { fillrule: ShapePath.OddEvenFill }
+ }
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ ListModel {
+ id: svgstrings
+ ListElement { scaleToFit: 1; offsetX: 20; offsetY: 20; pathString: "M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 M 5 40 L 100 40 L 100 60 L 5 60 Z" }
+ ListElement { scaleToFit: 5; offsetX: 10; offsetY: 20; pathString: "M 7.10938 0 Q 7.73438 0 8.28125 0.140625 Q 8.82813 0.28125 9.14063 0.546875 L 8.64063 2.10938 Q 8.35938 1.92188 8.02344 1.83594 Q 7.6875 1.75 7.26563 1.75 Q 6.23438 1.75 5.66406 2.24219 Q 5.09375 2.73438 5.09375 3.8125 L 5.09375 5.64063 L 5.09375 6.26563 L 5.09375 17.6406 L 3.17188 17.6406 L 3.17188 3.625 Q 3.17188 2.82813 3.40625 2.15625 Q 3.64063 1.48438 4.13281 1.00781 Q 4.625 0.53125 5.375 0.265625 Q 6.125 1.77636e-15 7.10938 0 M 9.09375 5.64063 L 9.09375 7.29688 L 0.765625 7.29688 L 0.765625 5.64063 L 9.09375 5.64063 M 16.2813 5.40625 Q 17.9844 5.40625 19.2891 6.14844 Q 20.5938 6.89063 21.3438 8.29688 Q 22.0938 9.70313 22.0938 11.6406 Q 22.0938 13.5781 21.3438 14.9844 Q 20.5938 16.3906 19.2891 17.1328 Q 17.9844 17.875 16.2813 17.875 Q 14.5938 17.875 13.2734 17.1328 Q 11.9531 16.3906 11.2109 14.9844 Q 10.4688 13.5781 10.4688 11.6406 Q 10.4688 9.70313 11.2109 8.29688 Q 11.9531 6.89063 13.2734 6.14844 Q 14.5938 5.40625 16.2813 5.40625 M 16.2813 7.0625 Q 15.0469 7.0625 14.1875 7.60938 Q 13.3281 8.15625 12.8594 9.17969 Q 12.3906 10.2031 12.3906 11.6406 Q 12.3906 13.0625 12.8594 14.0938 Q 13.3281 15.125 14.1875 15.6719 Q 15.0469 16.2188 16.2813 16.2188 Q 17.5 16.2188 18.3672 15.6719 Q 19.2344 15.125 19.7031 14.0938 Q 20.1719 13.0625 20.1719 11.6406 Q 20.1719 10.2031 19.7031 9.17969 Q 19.2344 8.15625 18.3672 7.60938 Q 17.5 7.0625 16.2813 7.0625" }
+ ListElement { scaleToFit: 5; offsetX: -120; offsetY: 20; pathString: "M 30.6719 5.40625 Q 32.375 5.40625 33.6797 6.14844 Q 34.9844 6.89063 35.7344 8.29688 Q 36.4844 9.70313 36.4844 11.6406 Q 36.4844 13.5781 35.7344 14.9844 Q 34.9844 16.3906 33.6797 17.1328 Q 32.375 17.875 30.6719 17.875 Q 28.9844 17.875 27.6641 17.1328 Q 26.3438 16.3906 25.6016 14.9844 Q 24.8594 13.5781 24.8594 11.6406 Q 24.8594 9.70313 25.6016 8.29688 Q 26.3438 6.89063 27.6641 6.14844 Q 28.9844 5.40625 30.6719 5.40625 M 30.6719 7.0625 Q 29.4375 7.0625 28.5781 7.60938 Q 27.7188 8.15625 27.25 9.17969 Q 26.7813 10.2031 26.7813 11.6406 Q 26.7813 13.0625 27.25 14.0938 Q 27.7188 15.125 28.5781 15.6719 Q 29.4375 16.2188 30.6719 16.2188 Q 31.8906 16.2188 32.7578 15.6719 Q 33.625 15.125 34.0938 14.0938 Q 34.5625 13.0625 34.5625 11.6406 Q 34.5625 10.2031 34.0938 9.17969 Q 33.625 8.15625 32.7578 7.60938 Q 31.8906 7.0625 30.6719 7.0625 M 45.9688 17.875 Q 44.9688 17.875 44.1016 17.5156 Q 43.2344 17.1563 42.6094 16.5078 Q 41.9844 15.8594 41.6719 15.0469 L 41.9375 14.7188 L 41.7656 17.6406 L 40.0938 17.6406 L 40.0938 0.125 L 42.0156 0.125 L 42.0156 8.35938 L 41.7656 7.98438 Q 42.25 6.85938 43.3906 6.13281 Q 44.5313 5.40625 46 5.40625 Q 47.4844 5.40625 48.7344 6.14844 Q 49.9844 6.89063 50.7266 8.28125 Q 51.4688 9.67188 51.4688 11.6406 Q 51.4688 13.5781 50.7109 14.9844 Q 49.9531 16.3906 48.7031 17.1328 Q 47.4531 17.875 45.9688 17.875 M 45.7813 16.2031 Q 47.5625 16.2031 48.5547 14.9531 Q 49.5469 13.7031 49.5469 11.6406 Q 49.5469 9.57813 48.5625 8.32813 Q 47.5781 7.07813 45.7969 7.07813 Q 44.6719 7.07813 43.8203 7.65625 Q 42.9688 8.23438 42.4922 9.26563 Q 42.0156 10.2969 42.0156 11.6875 Q 42.0156 13.0625 42.4844 14.0781 Q 42.9531 15.0938 43.8047 15.6484 Q 44.6563 16.2031 45.7813 16.2031" }
+ ListElement { scaleToFit: 5; offsetX: -260; offsetY: 20; pathString: "M 62.1406 9.89063 Q 62.1406 8.54688 61.4219 7.8125 Q 60.7031 7.07813 59.3906 7.07813 Q 58.1406 7.07813 57.2734 7.61719 Q 56.4063 8.15625 56 9.35938 L 54.4375 8.40625 Q 54.9219 7.0625 56.2188 6.23438 Q 57.5156 5.40625 59.4375 5.40625 Q 60.7344 5.40625 61.7891 5.84375 Q 62.8438 6.28125 63.4531 7.1875 Q 64.0625 8.09375 64.0625 9.5 L 64.0625 15.3125 Q 64.0625 16.1719 64.9844 16.1719 Q 65.4375 16.1719 65.875 16.0625 L 65.7656 17.5625 Q 65.2969 17.8125 64.5156 17.8125 Q 63.8281 17.8125 63.2734 17.5469 Q 62.7188 17.2813 62.4063 16.7422 Q 62.0938 16.2031 62.0938 15.3906 L 62.0938 15.1406 L 62.5781 15.2188 Q 62.2969 16.1563 61.6016 16.7422 Q 60.9063 17.3281 60.0391 17.6016 Q 59.1719 17.875 58.3281 17.875 Q 57.2813 17.875 56.3672 17.5156 Q 55.4531 17.1563 54.9141 16.4297 Q 54.375 15.7031 54.375 14.6094 Q 54.375 13.2656 55.2734 12.3906 Q 56.1719 11.5156 57.7813 11.2031 L 62.5313 10.2656 L 62.5313 11.8594 L 58.6719 12.6406 Q 57.4844 12.8906 56.9141 13.3203 Q 56.3438 13.75 56.3438 14.5156 Q 56.3438 15.2656 56.9297 15.7344 Q 57.5156 16.2031 58.5938 16.2031 Q 59.2969 16.2031 59.9297 16.0234 Q 60.5625 15.8438 61.0703 15.4688 Q 61.5781 15.0938 61.8594 14.5313 Q 62.1406 13.9688 62.1406 13.2031 L 62.1406 9.89063 M 68.7188 17.6406 L 68.7188 5.64063 L 70.2813 5.64063 L 70.5469 7.60938 Q 71.0469 6.5 72.0156 5.95313 Q 72.9844 5.40625 74.375 5.40625 Q 74.6875 5.40625 75.0625 5.45313 Q 75.4375 5.5 75.7031 5.64063 L 75.3594 7.39063 Q 75.0938 7.29688 74.7891 7.25 Q 74.4844 7.20313 73.9063 7.20313 Q 73.1563 7.20313 72.3984 7.63281 Q 71.6406 8.0625 71.1406 8.9375 Q 70.6406 9.8125 70.6406 11.1563 L 70.6406 17.6406 L 68.7188 17.6406" }
+ }
+ Column {
+ Repeater {
+ model: renderers
+ Column {
+ Repeater {
+ model: fillRules
+ Row {
+ Repeater {
+ model: svgstrings
+ Rectangle {
+ width: 150
+ height: 150
+ border.color: "black"
+
+ Shape {
+ preferredRendererType: renderer
+ ShapePath {
+ fillColor: renderer == Shape.CurveRenderer ? "#99483d8b" : "#99dc143c"
+ fillRule: fillrule
+ strokeWidth: 0
+ PathSvg { path: pathString }
+ }
+
+ transform: Matrix4x4 {
+ matrix: Qt.matrix4x4(scaleToFit, 0, 0, offsetX,
+ 0, scaleToFit, 0, offsetY,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_intersecting6.qml b/tests/baseline/scenegraph/data/shape/shape_intersecting6.qml
new file mode 100644
index 0000000000..e1b2b80be8
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_intersecting6.qml
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 600
+ height: 600
+
+ ListModel {
+ id: fillRules
+ ListElement { fillrule: ShapePath.WindingFill }
+ ListElement { fillrule: ShapePath.OddEvenFill }
+ }
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ ListModel {
+ id: svgstrings
+ ListElement { scaleToFit: 1; offsetX: 20; offsetY: 20; pathString: "M 0 0 L 100 0 L 100 50 L 0 50 Z M 0 50 L 100 50 L 100 100 L 0 100 Z " }
+ ListElement { scaleToFit: 1; offsetX: 20; offsetY: 20; pathString: "M 0 0 L 0 50 L 100 50 L 100 0 Z M 0 50 L 0 100 L 100 100 L 100 50 Z " }
+ ListElement { scaleToFit: 1; offsetX: 20; offsetY: 20; pathString: "M 0 0 L 0 50 L 100 50 L 100 0 Z M 0 50 L 100 50 L 100 100 L 0 100 Z " }
+ ListElement { scaleToFit: 1; offsetX: 20; offsetY: 40; pathString: "M 0 0 L 100 0 L 100 50 L 0 50 Z M 20 0 Q 20 -30 50 -30 Q 80 -30 80 0 L 80 70 Q 50 100 20 70 Z" }
+ }
+ Column {
+ Repeater {
+ model: renderers
+ Column {
+ Repeater {
+ model: fillRules
+ Row {
+ Repeater {
+ model: svgstrings
+ Rectangle {
+ width: 150
+ height: 150
+ border.color: "black"
+
+ Shape {
+ preferredRendererType: renderer
+ ShapePath {
+ fillColor: renderer == Shape.CurveRenderer ? "#99483d8b" : "#99dc143c"
+ fillRule: fillrule
+ strokeWidth: 0
+ PathSvg { path: pathString }
+ }
+
+ transform: Matrix4x4 {
+ matrix: Qt.matrix4x4(scaleToFit, 0, 0, offsetX,
+ 0, scaleToFit, 0, offsetY,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_intersecting7.qml b/tests/baseline/scenegraph/data/shape/shape_intersecting7.qml
new file mode 100644
index 0000000000..18fdde2cc1
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_intersecting7.qml
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 600
+ height: 600
+
+ ListModel {
+ id: fillRules
+ ListElement { fillrule: ShapePath.WindingFill }
+ ListElement { fillrule: ShapePath.OddEvenFill }
+ }
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ ListModel {
+ id: svgstrings
+ ListElement { scaleToFit: 0.08; offsetX: 20; offsetY: 20; pathString: "M 150 100 L 1250 150 L 100 1000 L 150 100 M 600 1200 Q 700 600 800 1200 Q 700 700 600 1200 M 100 1000 Q 200 1500 700 1500 Q 1200 1500 1200 1000 Q 1100 700 800 600 L 800 800 Q 1000 600 1000 1000 Q 1000 1300 700 1300 Q 400 1300 400 500 L 100 1000 " }
+ ListElement { scaleToFit: 1; offsetX: 20; offsetY: 20; pathString: "M 0 0 Q 50 -10 100 0 Q 110 50 100 100 Q 50 110 0 100 Q -10 50 0 0 M 10 10 Q 50 0 90 10 Q 100 50 90 90 Q 50 100 10 90 Q 0 50 10 10 M 20 20 Q 50 10 80 20 Q 100 50 80 80 Q 50 100 20 80 Q 0 50 20 20" }
+ ListElement { scaleToFit: 1; offsetX: 20; offsetY: 20; pathString: "M 0 0 Q 50 -10 100 0 Q 110 50 100 100 Q 50 110 0 100 Q -10 50 0 0 M 10 10 Q 0 50 10 90 Q 50 100 90 90 Q 100 50 90 10 Q 50 0 10 10 M 20 20 Q 50 10 80 20 Q 100 50 80 80 Q 50 100 20 80 Q 0 50 20 20" }
+ ListElement { scaleToFit: 1; offsetX: 20; offsetY: 20; pathString: "M 0 0 Q 50 -10 100 0 Q 110 50 100 100 Q 50 110 0 100 Q -10 50 0 0 M 10 10 Q 50 0 90 10 Q 100 50 90 90 Q 50 100 10 90 Q 0 50 10 10 M 20 20 Q 10 50 20 80 Q 50 100 80 80 Q 100 50 80 20 Q 50 0 20 20" }
+ }
+ Column {
+ Repeater {
+ model: renderers
+ Column {
+ Repeater {
+ model: fillRules
+ Row {
+ Repeater {
+ model: svgstrings
+ Rectangle {
+ width: 150
+ height: 150
+ border.color: "black"
+
+ Shape {
+ preferredRendererType: renderer
+ ShapePath {
+ fillColor: renderer == Shape.CurveRenderer ? "#99483d8b" : "#99dc143c"
+ fillRule: fillrule
+ strokeWidth: 0
+ PathSvg { path: pathString }
+ }
+
+ transform: Matrix4x4 {
+ matrix: Qt.matrix4x4(scaleToFit, 0, 0, offsetX,
+ 0, scaleToFit, 0, offsetY,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_linear_gradient.qml b/tests/baseline/scenegraph/data/shape/shape_linear_gradient.qml
index 1caaec7781..e4d87e398d 100644
--- a/tests/baseline/scenegraph/data/shape/shape_linear_gradient.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_linear_gradient.qml
@@ -1,33 +1,47 @@
import QtQuick 2.9
-import QtQuick.Shapes 1.0
+import QtQuick.Shapes 6.6
Item {
width: 320
height: 480
- Shape {
- vendorExtensionsEnabled: false
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
- anchors.fill: parent
+ Column {
+ Repeater {
+ model: renderers
+ Item {
+ width: 200
+ height: 150
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
- ShapePath {
- strokeWidth: 4
- strokeColor: "red"
- fillGradient: LinearGradient {
- x1: 20; y1: 20
- x2: 180; y2: 130
- GradientStop { position: 0; color: "blue" }
- GradientStop { position: 0.2; color: "green" }
- GradientStop { position: 0.4; color: "red" }
- GradientStop { position: 0.6; color: "yellow" }
- GradientStop { position: 1; color: "cyan" }
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "red"
+ fillGradient: LinearGradient {
+ x1: 20; y1: 20
+ x2: 180; y2: 130
+ GradientStop { position: 0; color: "blue" }
+ GradientStop { position: 0.2; color: "green" }
+ GradientStop { position: 0.4; color: "red" }
+ GradientStop { position: 0.6; color: "yellow" }
+ GradientStop { position: 1; color: "cyan" }
+ }
+ strokeStyle: ShapePath.DashLine
+ dashPattern: [ 1, 4 ]
+ startX: 20; startY: 20
+ PathLine { x: 180; y: 130 }
+ PathLine { x: 20; y: 130 }
+ PathLine { x: 20; y: 20 }
+ }
+ }
}
- strokeStyle: ShapePath.DashLine
- dashPattern: [ 1, 4 ]
- startX: 20; startY: 20
- PathLine { x: 180; y: 130 }
- PathLine { x: 20; y: 130 }
- PathLine { x: 20; y: 20 }
}
}
}
diff --git a/tests/baseline/scenegraph/data/shape/shape_lines.qml b/tests/baseline/scenegraph/data/shape/shape_lines.qml
index 56045cb5ae..f1759e64bb 100644
--- a/tests/baseline/scenegraph/data/shape/shape_lines.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_lines.qml
@@ -1,92 +1,108 @@
import QtQuick 2.9
-import QtQuick.Shapes 1.0
+import QtQuick.Shapes 6.6
Item {
+ id: root
width: 320
height: 480
- Shape {
- vendorExtensionsEnabled: false
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
- anchors.fill: parent
+ Column {
+ Repeater {
+ model: renderers
+ Item {
+ width: root.width
+ height: root.height / 2
- ShapePath {
- strokeWidth: 1
- strokeColor: "red"
- fillColor: "transparent"
- PathLine { x: 50; y: 50 }
- }
- ShapePath {
- strokeWidth: 2
- strokeColor: "blue"
- fillColor: "transparent"
- startX: 20
- PathLine { x: 70; y: 50 }
- }
- ShapePath {
- strokeWidth: 3
- strokeColor: "green"
- fillColor: "transparent"
- startX: 40
- PathLine { x: 90; y: 50 }
- }
- ShapePath {
- strokeWidth: 4
- strokeColor: "yellow"
- fillColor: "transparent"
- startX: 60
- PathLine { x: 110; y: 50 }
- }
- ShapePath {
- strokeWidth: 5
- strokeColor: "black"
- fillColor: "transparent"
- strokeStyle: ShapePath.DashLine
- startX: 80
- PathLine { x: 130; y: 50 }
- }
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
- ShapePath {
- strokeWidth: 20
- strokeColor: "gray"
- fillColor: "transparent"
- capStyle: ShapePath.RoundCap
- startX: 120; startY: 20
- PathLine { x: 200; y: 100 }
- }
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "red"
+ fillColor: "transparent"
+ PathLine { x: 50; y: 50 }
+ }
+ ShapePath {
+ strokeWidth: 2
+ strokeColor: "blue"
+ fillColor: "transparent"
+ startX: 20
+ PathLine { x: 70; y: 50 }
+ }
+ ShapePath {
+ strokeWidth: 3
+ strokeColor: "green"
+ fillColor: "transparent"
+ startX: 40
+ PathLine { x: 90; y: 50 }
+ }
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "yellow"
+ fillColor: "transparent"
+ startX: 60
+ PathLine { x: 110; y: 50 }
+ }
+ ShapePath {
+ strokeWidth: 5
+ strokeColor: "black"
+ fillColor: "transparent"
+ strokeStyle: ShapePath.DashLine
+ startX: 80
+ PathLine { x: 130; y: 50 }
+ }
- ShapePath {
- strokeColor: "black"
- strokeWidth: 16
- fillColor: "transparent"
- capStyle: ShapePath.RoundCap
- joinStyle: ShapePath.BevelJoin
- startX: 20
- startY: 100
- PathLine { x: 120; y: 200 }
- PathLine { x: 50; y: 200 }
- }
- ShapePath {
- strokeColor: "black"
- strokeWidth: 16
- fillColor: "transparent"
- capStyle: ShapePath.RoundCap
- joinStyle: ShapePath.MiterJoin
- startX: 150
- startY: 100
- PathLine { x: 250; y: 200 }
- PathLine { x: 180; y: 200 }
- }
- ShapePath {
- strokeColor: "black"
- strokeWidth: 16
- fillColor: "transparent"
- capStyle: ShapePath.RoundCap
- joinStyle: ShapePath.RoundJoin
- startX: 270
- startY: 100
- PathLine { x: 310; y: 200 }
- PathLine { x: 280; y: 200 }
+ ShapePath {
+ strokeWidth: 20
+ strokeColor: "gray"
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+ startX: 120; startY: 20
+ PathLine { x: 200; y: 100 }
+ }
+
+ ShapePath {
+ strokeColor: "black"
+ strokeWidth: 16
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+ joinStyle: ShapePath.BevelJoin
+ startX: 20
+ startY: 100
+ PathLine { x: 120; y: 200 }
+ PathLine { x: 50; y: 200 }
+ }
+ ShapePath {
+ strokeColor: "black"
+ strokeWidth: 16
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+ joinStyle: ShapePath.MiterJoin
+ startX: 150
+ startY: 100
+ PathLine { x: 250; y: 200 }
+ PathLine { x: 180; y: 200 }
+ }
+ ShapePath {
+ strokeColor: "black"
+ strokeWidth: 16
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+ joinStyle: ShapePath.RoundJoin
+ startX: 270
+ startY: 100
+ PathLine { x: 310; y: 200 }
+ PathLine { x: 280; y: 200 }
+ }
+ }
+ }
}
}
}
diff --git a/tests/baseline/scenegraph/data/shape/shape_multiline.qml b/tests/baseline/scenegraph/data/shape/shape_multiline.qml
index d1fcf164cb..7b68d3c7a0 100644
--- a/tests/baseline/scenegraph/data/shape/shape_multiline.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_multiline.qml
@@ -1,48 +1,65 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
-import QtQuick.Shapes 1.14
+import QtQuick.Shapes 6.6
Item {
id: root
width: 320
- height: 320
-
- Shape {
- vendorExtensionsEnabled: false
- anchors.fill: parent
-
- ShapePath {
- strokeWidth: 1
- strokeColor: "red"
- fillColor: Qt.rgba(1,0,0,0.3)
- scale: Qt.size(root.width - 1, root.height - 1)
- PathMultiline {
- paths: [
- [Qt.point(0.5, 0.06698),
- Qt.point(1, 0.93301),
- Qt.point(0, 0.93301),
- Qt.point(0.5, 0.06698)],
-
- [Qt.point(0.5, 0.12472),
- Qt.point(0.95, 0.90414),
- Qt.point(0.05, 0.90414),
- Qt.point(0.5, 0.12472)],
-
- [Qt.point(0.47131, 0.32986),
- Qt.point(0.36229, 0.64789),
- Qt.point(0.51492, 0.58590),
- Qt.point(0.47563, 0.76014),
- Qt.point(0.44950, 0.73590),
- Qt.point(0.46292, 0.83392),
- Qt.point(0.52162, 0.75190),
- Qt.point(0.48531, 0.76230),
- Qt.point(0.57529, 0.53189),
- Qt.point(0.41261, 0.59189),
- Qt.point(0.53001, 0.32786),
- Qt.point(0.47131, 0.32986)]
- ]
+ height: 480
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Column {
+ Repeater {
+ model: renderers
+ Item {
+ x: 20
+ width: root.width
+ height: root.height / 2
+
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
+
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "red"
+ fillColor: Qt.rgba(1,0,0,0.3)
+ scale: Qt.size(root.height / 2, root.height / 2)
+ PathMultiline {
+ paths: [
+ [Qt.point(0.5, 0.06698),
+ Qt.point(1, 0.93301),
+ Qt.point(0, 0.93301),
+ Qt.point(0.5, 0.06698)],
+
+ [Qt.point(0.5, 0.12472),
+ Qt.point(0.95, 0.90414),
+ Qt.point(0.05, 0.90414),
+ Qt.point(0.5, 0.12472)],
+
+ [Qt.point(0.47131, 0.32986),
+ Qt.point(0.36229, 0.64789),
+ Qt.point(0.51492, 0.58590),
+ Qt.point(0.47563, 0.76014),
+ Qt.point(0.44950, 0.73590),
+ Qt.point(0.46292, 0.83392),
+ Qt.point(0.52162, 0.75190),
+ Qt.point(0.48531, 0.76230),
+ Qt.point(0.57529, 0.53189),
+ Qt.point(0.41261, 0.59189),
+ Qt.point(0.53001, 0.32786),
+ Qt.point(0.47131, 0.32986)]
+ ]
+ }
+ }
+ }
}
}
}
diff --git a/tests/baseline/scenegraph/data/shape/shape_order.qml b/tests/baseline/scenegraph/data/shape/shape_order.qml
new file mode 100644
index 0000000000..14d2a322f9
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_order.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Item {
+ width: 320
+ height: 480
+
+ OrderedPaths {
+ anchors.fill: parent
+ async: false
+ }
+}
+
diff --git a/tests/baseline/scenegraph/data/shape/shape_order2.qml b/tests/baseline/scenegraph/data/shape/shape_order2.qml
new file mode 100644
index 0000000000..1887cc527e
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_order2.qml
@@ -0,0 +1,91 @@
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ id: root
+ width: 320
+ height: 480
+
+ property int counter: 0
+ NumberAnimation {
+ target: root
+ property: "counter"
+ duration: 150
+ from: 0
+ to: 1
+ running: true
+ }
+
+ component MyShapes : Item {
+ width: 100
+ height: 200
+ property bool async: false
+ property int preferredRenderer: Shape.GeometryRenderer
+
+ Shape {
+ id: s1
+ asynchronous: parent.async
+ preferredRendererType: parent.preferredRenderer
+ ShapePath {
+ id: p1
+ fillColor: "orange"
+ strokeColor: counter < 1 ? "transparent" : "lime"
+ strokeWidth: 10
+ strokeStyle: ShapePath.DashLine
+ PathAngleArc {
+ centerX: 50
+ centerY: 50
+ radiusX: 40
+ radiusY: 40
+ sweepAngle: 270
+ }
+ }
+ }
+
+ Shape {
+ id: s2
+ y: 100
+ asynchronous: parent.async
+ preferredRendererType: parent.preferredRenderer
+ ShapePath {
+ id: p2
+ fillColor: counter < 1 ? "transparent" : "orange"
+ strokeColor: "lime"
+ strokeWidth: 10
+ strokeStyle: ShapePath.DashLine
+ PathAngleArc {
+ moveToStart: true
+ centerX: 50
+ centerY: 50
+ radiusX: 40
+ radiusY: 40
+ sweepAngle: 270
+ }
+ }
+ }
+ }
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ spacing: 25
+ Repeater {
+ model: renderers
+ Column {
+ spacing: 25
+ MyShapes {
+ async: false
+ preferredRenderer: renderer
+ }
+ MyShapes {
+ async: true
+ preferredRenderer: renderer
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_order_async.qml b/tests/baseline/scenegraph/data/shape/shape_order_async.qml
new file mode 100644
index 0000000000..5dc96721b3
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_order_async.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Item {
+ width: 320
+ height: 480
+
+ OrderedPaths {
+ anchors.fill: parent
+ async: true
+ }
+}
+
diff --git a/tests/baseline/scenegraph/data/shape/shape_quad.qml b/tests/baseline/scenegraph/data/shape/shape_quad.qml
index a4c95f7c15..05d2b2519b 100644
--- a/tests/baseline/scenegraph/data/shape/shape_quad.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_quad.qml
@@ -1,30 +1,41 @@
import QtQuick 2.9
-import QtQuick.Shapes 1.0
+import QtQuick.Shapes 6.6
Item {
width: 320
height: 480
- Column {
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
Repeater {
- model: 4
- Item {
- width: 200
- height: 100
+ model: renderers
+ Column {
+ Repeater {
+ model: 4
+ Item {
+ width: 160
+ height: 100
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
- ShapePath {
- strokeWidth: (model.index + 2) * 2
- strokeColor: "black"
- fillColor: "lightBlue"
+ ShapePath {
+ strokeWidth: (model.index + 2) * 2
+ strokeColor: "black"
+ fillColor: "lightBlue"
- startX: 50; startY: 100
- PathQuad {
- x: 150; y: 100
- controlX: model.index * 10; controlY: model.index * 5
+ startX: 30; startY: 100
+ PathQuad {
+ x: 130; y: 100
+ controlX: model.index * 10; controlY: model.index * 5
+ }
+ }
}
}
}
diff --git a/tests/baseline/scenegraph/data/shape/shape_spread.qml b/tests/baseline/scenegraph/data/shape/shape_spread.qml
index f310f08773..dffa4a1bae 100644
--- a/tests/baseline/scenegraph/data/shape/shape_spread.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_spread.qml
@@ -1,33 +1,44 @@
import QtQuick 2.9
-import QtQuick.Shapes 1.0
+import QtQuick.Shapes 6.6
Item {
width: 320
height: 480
- Column {
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
Repeater {
- model: 3
- Shape {
- vendorExtensionsEnabled: false
- width: 200
- height: 150
- ShapePath {
- strokeColor: "transparent"
+ model: renderers
+ Column {
+ Repeater {
+ model: 3
+ Shape {
+ preferredRendererType: renderer
+ width: 160
+ height: 150
+ ShapePath {
+ strokeColor: "transparent"
- fillGradient: LinearGradient {
- id: grad
- y1: 50; y2: 80
- spread: model.index === 0 ? ShapeGradient.PadSpread : (model.index === 1 ? ShapeGradient.RepeatSpread : ShapeGradient.ReflectSpread)
- GradientStop { position: 0; color: "black" }
- GradientStop { position: 1; color: "red" }
- }
+ fillGradient: LinearGradient {
+ id: grad
+ y1: 50; y2: 80
+ spread: model.index === 0 ? ShapeGradient.PadSpread : (model.index === 1 ? ShapeGradient.RepeatSpread : ShapeGradient.ReflectSpread)
+ GradientStop { position: 0; color: "black" }
+ GradientStop { position: 1; color: "red" }
+ }
- startX: 10; startY: 10
- PathLine { relativeX: 180; relativeY: 0 }
- PathLine { relativeX: 0; relativeY: 100 }
- PathLine { relativeX: -180; relativeY: 0 }
- PathLine { relativeX: 0; relativeY: -100 }
+ startX: 10; startY: 10
+ PathLine { relativeX: 140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 100 }
+ PathLine { relativeX: -140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -100 }
+ }
+ }
}
}
}
diff --git a/tests/baseline/scenegraph/data/shape/shape_text.qml b/tests/baseline/scenegraph/data/shape/shape_text.qml
index 37367054b5..427c78c8d0 100644
--- a/tests/baseline/scenegraph/data/shape/shape_text.qml
+++ b/tests/baseline/scenegraph/data/shape/shape_text.qml
@@ -1,88 +1,99 @@
import QtQuick 2.15
-import QtQuick.Shapes 1.0
+import QtQuick.Shapes 6.6
Item {
width: 320
height: 480
- Column {
- Item {
- width: 200
- height: 160
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ Repeater {
+ model: renderers
+ Column {
+ Item {
+ width: 160
+ height: 160
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
- ShapePath {
- fillColor: "transparent"
- strokeColor: "blue"
- strokeStyle: ShapePath.DashLine
- strokeWidth: 4
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
- PathText {
- x: 96
- y: 10
- font.pixelSize: 120
- text: "Qt"
+ PathText {
+ x: 24
+ y: 10
+ font.pixelSize: 96
+ text: "Qt"
+ }
+ }
}
}
- }
- }
- Item {
- width: 200
- height: 160
+ Item {
+ width: 100
+ height: 160
- Rectangle {
- anchors.fill: parent
- color: "blue"
- }
+ Rectangle {
+ anchors.fill: parent
+ color: "blue"
+ }
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
- ShapePath {
- fillColor: "red"
- strokeColor: "blue"
- strokeStyle: ShapePath.DashLine
- capStyle: ShapePath.RoundCap
- strokeWidth: 8
+ ShapePath {
+ fillColor: "red"
+ strokeColor: "blue"
+ strokeStyle: ShapePath.DashLine
+ capStyle: ShapePath.RoundCap
+ strokeWidth: 4
- PathText {
- x: 96; y: 10
- font.pixelSize: 150
- text: "Qt"
+ PathText {
+ x: 24; y: 10
+ font.pixelSize: 96
+ text: "Qt"
+ }
+ }
}
}
- }
- }
- Item {
- width: 200
- height: 160
+ Item {
+ width: 160
+ height: 160
- Shape {
- anchors.fill: parent
- vendorExtensionsEnabled: false
+ Shape {
+ anchors.fill: parent
+ preferredRendererType: renderer
- ShapePath {
- fillGradient: LinearGradient {
- x1: 0; x2: 200; y1: 0; y2: 160
- spread: ShapeGradient.PadSpread
- GradientStop { position: 0.0; color: "red"; }
- GradientStop { position: 1.0; color: "green"; }
- }
- strokeColor: "blue"
- strokeStyle: ShapePath.DashLine
- joinStyle: ShapePath.RoundJoin
- strokeWidth: 4
+ ShapePath {
+ fillGradient: LinearGradient {
+ x1: 0; x2: 200; y1: 0; y2: 160
+ spread: ShapeGradient.PadSpread
+ GradientStop { position: 0.0; color: "red"; }
+ GradientStop { position: 1.0; color: "green"; }
+ }
+ strokeColor: "blue"
+ strokeStyle: ShapePath.DashLine
+ joinStyle: ShapePath.RoundJoin
+ strokeWidth: 4
- PathText {
- x: 96; y: 10
- font.pixelSize: 150
- text: "Qt"
+ PathText {
+ x: 24; y: 10
+ font.pixelSize: 96
+ text: "Qt"
+ }
+ }
}
}
}
diff --git a/tests/baseline/scenegraph/data/text/text_baseline_alignment.qml b/tests/baseline/scenegraph/data/text/text_baseline_alignment.qml
new file mode 100644
index 0000000000..20ede53f4b
--- /dev/null
+++ b/tests/baseline/scenegraph/data/text/text_baseline_alignment.qml
@@ -0,0 +1,37 @@
+import QtQuick
+
+Item {
+ width: 320
+ height: 300
+ Column {
+ anchors.fill: parent
+ Repeater {
+ model: [ Text.RichText, Text.StyledText ]
+ Item {
+ width: 320
+ height: 100
+ Rectangle {
+ id: alignItem
+ width: parent.width
+ height: 1
+ color: "blue"
+ anchors.bottom: parent.bottom
+ }
+
+ Text {
+ id: textItem
+ textFormat: modelData
+ font.family: "Arial"
+ font.pixelSize: 50
+
+ text: textFormat == Text.RichText
+ ? "a&#29340;&#2339;<span style=\"font-size: 25px\">a&#29340;&#2339;</span><span style=\"font-size: 10px\">a&#29340;&#2339;</span>"
+ : "<font size=\"3\">a&#29340;&#2339;</font><font size=\"3\">a&#29340;&#2339;</font><font size=\"1\">a&#29340;&#2339;</font>"
+
+ anchors.baseline: alignItem.top
+ anchors.left: alignItem.left
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/text/text_context_font_merging.qml b/tests/baseline/scenegraph/data/text/text_context_font_merging.qml
new file mode 100644
index 0000000000..3f0adfe2d8
--- /dev/null
+++ b/tests/baseline/scenegraph/data/text/text_context_font_merging.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+ width: 320
+ height: 100
+ Column {
+ anchors.fill: parent
+ Text {
+ text: "說文閩音通說文閩音通說文閩音通"
+ font.pixelSize: 20
+ }
+ Text {
+ text: "說文閩音通說文閩音通說文閩音通"
+ font.contextFontMerging: true
+ font.pixelSize: 20
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/text/text_emoji.qml b/tests/baseline/scenegraph/data/text/text_emoji.qml
index d50f6d6715..c9b9a33715 100644
--- a/tests/baseline/scenegraph/data/text/text_emoji.qml
+++ b/tests/baseline/scenegraph/data/text/text_emoji.qml
@@ -2,14 +2,13 @@ import QtQuick 2.0
Item {
width: 320
- height: 480
-
+ height: 680
Component {
id: component
Column {
property variant listModel: model
Repeater {
- model: [Text.NativeRendering, Text.QtRendering]
+ model: [Text.NativeRendering, Text.QtRendering, Text.CurveRendering]
Rectangle {
width: text.implicitWidth
height: text.implicitHeight
diff --git a/tests/baseline/scenegraph/data/text/text_emoji_hebrew.qml b/tests/baseline/scenegraph/data/text/text_emoji_hebrew.qml
index b67d584a12..4062690cd0 100644
--- a/tests/baseline/scenegraph/data/text/text_emoji_hebrew.qml
+++ b/tests/baseline/scenegraph/data/text/text_emoji_hebrew.qml
@@ -2,14 +2,14 @@ import QtQuick 2.0
Item {
width: 320
- height: 480
+ height: 680
Component {
id: component
Column {
property variant listModel: model
Repeater {
- model: [Text.NativeRendering, Text.QtRendering]
+ model: [Text.NativeRendering, Text.QtRendering, Text.CurveRendering]
Rectangle {
width: text.implicitWidth
height: text.implicitHeight
diff --git a/tests/baseline/scenegraph/data/text/textedit_baseline_alignment.qml b/tests/baseline/scenegraph/data/text/textedit_baseline_alignment.qml
new file mode 100644
index 0000000000..a012c08313
--- /dev/null
+++ b/tests/baseline/scenegraph/data/text/textedit_baseline_alignment.qml
@@ -0,0 +1,29 @@
+import QtQuick
+
+Item {
+ width: 320
+ height: 160
+ Item {
+ width: 320
+ height: 100
+ Rectangle {
+ id: alignItem
+ width: parent.width
+ height: 1
+ color: "blue"
+ anchors.bottom: parent.bottom
+ }
+
+ TextEdit {
+ id: textItem
+ textFormat: TextEdit.RichText
+ font.family: "Arial"
+ font.pixelSize: 50
+
+ text: "a&#29340;&#2339;<span style=\"font-size: 25px\">a&#29340;&#2339;</span><span style=\"font-size: 10px\">a&#29340;&#2339;</span>"
+
+ anchors.baseline: alignItem.top
+ anchors.left: alignItem.left
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/scenegrabber/CMakeLists.txt b/tests/baseline/scenegraph/scenegrabber/CMakeLists.txt
index 1ace458afb..3530eb8595 100644
--- a/tests/baseline/scenegraph/scenegrabber/CMakeLists.txt
+++ b/tests/baseline/scenegraph/scenegrabber/CMakeLists.txt
@@ -13,6 +13,8 @@ qt_internal_add_manual_test(qmlscenegrabber
Qt::Gui
Qt::Quick
Qt::QuickControls2
+ Qt::QuickPrivate
+ Qt::QuickTemplates2Private
)
qt6_add_shaders(qmlscenegrabber "qmlscenegrabber_shaders"
diff --git a/tests/baseline/scenegraph/scenegrabber/main.cpp b/tests/baseline/scenegraph/scenegrabber/main.cpp
index 482ca3b3fe..7a55973d60 100644
--- a/tests/baseline/scenegraph/scenegrabber/main.cpp
+++ b/tests/baseline/scenegraph/scenegrabber/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QTimer>
#include <QtCore/QDebug>
@@ -7,10 +7,13 @@
#include <QtCore/QHashFunctions>
#include <QtGui/QGuiApplication>
#include <QtGui/QImage>
+#include <QtCore/QLoggingCategory>
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickItem>
#include <QtQuickControls2/qquickstyle.h>
+#include <QQmlApplicationEngine>
+#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
#ifdef Q_OS_WIN
# include <fcntl.h>
@@ -27,6 +30,7 @@
#define SCENE_TIMEOUT 6000
//#define GRABBERDEBUG
+Q_LOGGING_CATEGORY(lcGrabber, "qt.baseline.scenegrabber")
static const QSize DefaultGrabSize(320, 480);
@@ -35,24 +39,35 @@ class GrabbingView : public QQuickView
Q_OBJECT
public:
- GrabbingView(const QString &outputFile)
- : ofile(outputFile), grabNo(0), isGrabbing(false), initDone(false), justShow(outputFile.isEmpty())
+ GrabbingView(const QString &outputFile, const bool useAppWindow)
+ : ofile(outputFile), grabNo(0), isGrabbing(false), initDone(false), justShow(outputFile.isEmpty()), preferAppWindow(useAppWindow)
{
if (justShow)
return;
grabTimer = new QTimer(this);
grabTimer->setSingleShot(true);
grabTimer->setInterval(SCENE_STABLE_TIME);
- connect(grabTimer, SIGNAL(timeout()), SLOT(grab()));
+ connect(grabTimer, &QTimer::timeout, this, &GrabbingView::grab);
- connect(this, SIGNAL(afterRendering()), SLOT(startGrabbing()));
+ if (!preferAppWindow)
+ QObject::connect(this, &QQuickWindow::afterRendering, this, &GrabbingView::startGrabbing);
- QTimer::singleShot(SCENE_TIMEOUT, this, SLOT(timedOut()));
+ QTimer::singleShot(SCENE_TIMEOUT, this, &GrabbingView::timedOut);
}
+ void setApplicationWindow(QWindow* window) {
+ qCDebug(lcGrabber) << "Using ApplicationWindow as visual parent" << this;
+
+ appwindow = qobject_cast<QQuickApplicationWindow *>(window);
+ if (preferAppWindow)
+ QObject::connect(appwindow, &QQuickWindow::afterRendering, this, &GrabbingView::startGrabbing);
+ }
+ QQuickApplicationWindow* appWindow() { return appwindow; }
+
private slots:
void startGrabbing()
{
+ qCDebug(lcGrabber) << "Starting to grab";
if (!initDone) {
initDone = true;
grabTimer->start();
@@ -61,29 +76,28 @@ private slots:
void grab()
{
- if (isGrabbing)
+ if (isGrabbing) {
+ qCDebug(lcGrabber) << "Already grabbing, skipping";
return;
- isGrabbing = true;
+ }
+
+ QScopedValueRollback grabGuard(isGrabbing, true);
+
grabNo++;
-#ifdef GRABBERDEBUG
- printf("grab no. %i\n", grabNo);
-#endif
- QImage img = grabWindow();
+ qCDebug(lcGrabber) << "grab no." << grabNo;
+ QImage img;
+ img = appwindow ? appwindow->grabWindow() : grabWindow();
if (!img.isNull() && img == lastGrab) {
sceneStabilized();
} else {
lastGrab = img;
grabTimer->start();
}
-
- isGrabbing = false;
}
void sceneStabilized()
{
-#ifdef GRABBERDEBUG
- printf("...sceneStabilized IN\n");
-#endif
+ qCDebug(lcGrabber) << "...sceneStabilized IN";
if (QGuiApplication::platformName() == QLatin1String("eglfs")) {
QSize grabSize = initialSize().isEmpty() ? DefaultGrabSize : initialSize();
lastGrab = lastGrab.copy(QRect(QPoint(0, 0), grabSize));
@@ -108,9 +122,7 @@ private slots:
}
}
QGuiApplication::exit(0);
-#ifdef GRABBERDEBUG
- printf("...sceneStabilized OUT\n");
-#endif
+ qCDebug(lcGrabber) << "...sceneStabilized OUT";
}
void timedOut()
@@ -127,6 +139,8 @@ private:
bool isGrabbing;
bool initDone;
bool justShow;
+ QQuickApplicationWindow *appwindow = nullptr;
+ bool preferAppWindow = false;
};
@@ -140,9 +154,11 @@ int main(int argc, char *argv[])
QString ifile, ofile, style;
bool noText = false;
bool justShow = false;
+
QStringList args = a.arguments();
int i = 0;
bool argError = false;
+ bool useAppWindow = false;
while (++i < args.size()) {
QString arg = args.at(i);
if ((arg == "-o") && (i < args.size()-1)) {
@@ -157,14 +173,19 @@ int main(int argc, char *argv[])
else if (arg == "-viewonly") {
justShow = true;
}
- else if (ifile.isEmpty()) {
- ifile = arg;
- }
else if (arg == "-style") {
if (i < args.size()-1)
style = args.at(++i);
- else
+ else {
argError = true;
+ break;
+ }
+ }
+ else if (arg == "-useAppWindow") {
+ useAppWindow = true;
+ }
+ else if (ifile.isEmpty()) {
+ ifile = arg;
}
else {
argError = true;
@@ -187,26 +208,58 @@ int main(int argc, char *argv[])
if (!style.isEmpty())
QQuickStyle::setStyle(style);
- GrabbingView v(ofile);
- v.setSource(QUrl::fromLocalFile(ifile));
+ GrabbingView v(ofile, useAppWindow);
+
+ if (useAppWindow) {
+ QQmlEngine *engine = new QQmlEngine;
+ {
+ // test qml component creation
+ QQmlComponent component(engine, QUrl::fromLocalFile(ifile));
+ auto *itemObject = qobject_cast<QQuickItem*>(component.create());
+
+ // TODO: Hack to import native style forcefully for windows
+ QString appCompStr = "import QtQuick.Controls\n";
+ if (QQuickStyle::name() == "Windows")
+ appCompStr += "import QtQuick.NativeStyle\n";
+ appCompStr += "ApplicationWindow{}";
- if (noText) {
- const QList<QQuickItem*> items = v.rootObject()->findChildren<QQuickItem*>();
- for (QQuickItem *item : items) {
- if (QByteArray(item->metaObject()->className()).contains("Text"))
- item->setVisible(false);
+ // Create application window
+ QQmlComponent appWndComp(engine);
+ appWndComp.setData(appCompStr.toLatin1(), QUrl::fromLocalFile(""));
+ auto *appWindow = qobject_cast<QQuickApplicationWindow *>(appWndComp.create());
+
+ if (!appWindow) {
+ qWarning() << "Error: failed to create application window.";
+ QGuiApplication::exit(2);
+ }
+
+ appWindow->resize(itemObject->size().toSize());
+
+ // Use application window as visual parent to the loaded component
+ v.setApplicationWindow(appWindow);
+ itemObject->setParentItem(v.appWindow()->contentItem());
+ itemObject->setParent(v.appWindow());
+ }
+ v.appWindow()->show();
+ } else {
+ v.setSource(QUrl::fromLocalFile(ifile));
+
+ if (noText) {
+ const QList<QQuickItem*> items = v.rootObject()->findChildren<QQuickItem*>();
+ for (QQuickItem *item : items) {
+ if (QByteArray(item->metaObject()->className()).contains("Text"))
+ item->setVisible(false);
+ }
}
- }
- if (v.initialSize().isEmpty())
- v.resize(DefaultGrabSize);
+ if (v.initialSize().isEmpty())
+ v.resize(DefaultGrabSize);
- v.show();
+ v.show();
+ }
int retVal = a.exec();
-#ifdef GRABBERDEBUG
- printf("...retVal=%i\n", retVal);
-#endif
+ qCDebug(lcGrabber) << "...retVal=" << retVal;
return retVal;
}
diff --git a/tests/baseline/scenegraph/scenegraph/tst_baseline_scenegraph.cpp b/tests/baseline/scenegraph/scenegraph/tst_baseline_scenegraph.cpp
index 124580f3b6..e02e979897 100644
--- a/tests/baseline/scenegraph/scenegraph/tst_baseline_scenegraph.cpp
+++ b/tests/baseline/scenegraph/scenegraph/tst_baseline_scenegraph.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qbaselinetest.h>
@@ -32,6 +32,7 @@ public:
private Q_SLOTS:
void initTestCase();
+ void init();
void cleanup();
#ifdef TEXTLESS_TEST
void testNoTextRendering_data();
@@ -102,6 +103,11 @@ void tst_Scenegraph::initTestCase()
QSKIP(msg);
}
+void tst_Scenegraph::init()
+{
+ // This gets called for every row. QSKIP if current item is blacklisted on the baseline server:
+ QBASELINE_SKIP_IF_BLACKLISTED;
+}
void tst_Scenegraph::cleanup()
{
@@ -192,7 +198,7 @@ void tst_Scenegraph::runTest(const QStringList& extraArgs)
consecutiveErrors = 0;
}
else {
- if (++consecutiveErrors >= 3)
+ if (++consecutiveErrors >= 3 && QBaselineTest::shouldAbortIfUnstable())
aborted = true; // Just give up if screen grabbing fails 3 times in a row
QFAIL(qPrintable("QuickView grabbing failed: " + errorMessage));
}
@@ -241,7 +247,8 @@ quint16 tst_Scenegraph::checksumFileOrDir(const QString &path)
return 0;
if (fi.isFile()) {
QFile f(path);
- f.open(QIODevice::ReadOnly);
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("Could not open file %s", qPrintable(path));
QByteArray contents = f.readAll();
return qChecksum(contents);
}
diff --git a/tests/benchmarks/particles/affectors/data/basic.qml b/tests/benchmarks/particles/affectors/data/basic.qml
index da8a02abdb..87c943532c 100644
--- a/tests/benchmarks/particles/affectors/data/basic.qml
+++ b/tests/benchmarks/particles/affectors/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/benchmarks/particles/affectors/data/filtered.qml b/tests/benchmarks/particles/affectors/data/filtered.qml
index 488a33503e..60f892bad5 100644
--- a/tests/benchmarks/particles/affectors/data/filtered.qml
+++ b/tests/benchmarks/particles/affectors/data/filtered.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/benchmarks/particles/affectors/tst_affectors.cpp b/tests/benchmarks/particles/affectors/tst_affectors.cpp
index 172d986186..7ec6732447 100644
--- a/tests/benchmarks/particles/affectors/tst_affectors.cpp
+++ b/tests/benchmarks/particles/affectors/tst_affectors.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QtTest>
diff --git a/tests/benchmarks/particles/emission/data/basic.qml b/tests/benchmarks/particles/emission/data/basic.qml
index 31bf99bcfe..d1d951452e 100644
--- a/tests/benchmarks/particles/emission/data/basic.qml
+++ b/tests/benchmarks/particles/emission/data/basic.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/benchmarks/particles/emission/tst_emission.cpp b/tests/benchmarks/particles/emission/tst_emission.cpp
index 875906ad45..1b7a4998ac 100644
--- a/tests/benchmarks/particles/emission/tst_emission.cpp
+++ b/tests/benchmarks/particles/emission/tst_emission.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtTest/QtTest>
diff --git a/tests/benchmarks/qml/CMakeLists.txt b/tests/benchmarks/qml/CMakeLists.txt
index 3ccbb6f2db..39aa2e2de6 100644
--- a/tests/benchmarks/qml/CMakeLists.txt
+++ b/tests/benchmarks/qml/CMakeLists.txt
@@ -5,6 +5,7 @@
add_subdirectory(binding)
add_subdirectory(compilation)
+add_subdirectory(deltablue)
add_subdirectory(javascript)
add_subdirectory(holistic)
add_subdirectory(qqmlchangeset)
@@ -21,3 +22,6 @@ endif()
if(TARGET Qt::OpenGL AND TARGET Qt::OpenGLWidgets)
add_subdirectory(painting)
endif()
+if(TARGET Qt::QmlDomPrivate AND NOT CMAKE_CROSSCOMPILING)
+ add_subdirectory(qmldom)
+endif()
diff --git a/tests/benchmarks/qml/animation/data/animation.qml b/tests/benchmarks/qml/animation/data/animation.qml
index 31f94e07a4..ba43142ef6 100644
--- a/tests/benchmarks/qml/animation/data/animation.qml
+++ b/tests/benchmarks/qml/animation/data/animation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/animation/tst_animation.cpp b/tests/benchmarks/qml/animation/tst_animation.cpp
index d8e26ed882..1685baf98f 100644
--- a/tests/benchmarks/qml/animation/tst_animation.cpp
+++ b/tests/benchmarks/qml/animation/tst_animation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
diff --git a/tests/benchmarks/qml/binding/testtypes.cpp b/tests/benchmarks/qml/binding/testtypes.cpp
index 01d07eb145..1b848b02fd 100644
--- a/tests/benchmarks/qml/binding/testtypes.cpp
+++ b/tests/benchmarks/qml/binding/testtypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
void registerTypes()
diff --git a/tests/benchmarks/qml/binding/testtypes.h b/tests/benchmarks/qml/binding/testtypes.h
index 1c343fa9bd..f6fa9e3e81 100644
--- a/tests/benchmarks/qml/binding/testtypes.h
+++ b/tests/benchmarks/qml/binding/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
diff --git a/tests/benchmarks/qml/binding/tst_binding.cpp b/tests/benchmarks/qml/binding/tst_binding.cpp
index 6cb35d664e..703622cd43 100644
--- a/tests/benchmarks/qml/binding/tst_binding.cpp
+++ b/tests/benchmarks/qml/binding/tst_binding.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
diff --git a/tests/benchmarks/qml/compilation/data/BoomBlock.qml b/tests/benchmarks/qml/compilation/data/BoomBlock.qml
index a5cb725c2a..1ecdc8b3ab 100644
--- a/tests/benchmarks/qml/compilation/data/BoomBlock.qml
+++ b/tests/benchmarks/qml/compilation/data/BoomBlock.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/benchmarks/qml/compilation/tst_compilation.cpp b/tests/benchmarks/qml/compilation/tst_compilation.cpp
index 47e1423dae..61e95360de 100644
--- a/tests/benchmarks/qml/compilation/tst_compilation.cpp
+++ b/tests/benchmarks/qml/compilation/tst_compilation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
diff --git a/tests/benchmarks/qml/creation/data/CustomItem.qml b/tests/benchmarks/qml/creation/data/CustomItem.qml
index abf7eb989e..61ac9253c7 100644
--- a/tests/benchmarks/qml/creation/data/CustomItem.qml
+++ b/tests/benchmarks/qml/creation/data/CustomItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/emptyCustomItem.qml b/tests/benchmarks/qml/creation/data/emptyCustomItem.qml
index 9f7dc02911..e363b1a567 100644
--- a/tests/benchmarks/qml/creation/data/emptyCustomItem.qml
+++ b/tests/benchmarks/qml/creation/data/emptyCustomItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/emptyItem.qml b/tests/benchmarks/qml/creation/data/emptyItem.qml
index abf7eb989e..61ac9253c7 100644
--- a/tests/benchmarks/qml/creation/data/emptyItem.qml
+++ b/tests/benchmarks/qml/creation/data/emptyItem.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/item.qml b/tests/benchmarks/qml/creation/data/item.qml
index b37bc121db..9ab0a763f5 100644
--- a/tests/benchmarks/qml/creation/data/item.qml
+++ b/tests/benchmarks/qml/creation/data/item.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemUsingOnComponentCompleted.qml b/tests/benchmarks/qml/creation/data/itemUsingOnComponentCompleted.qml
index 21957938c8..23d9744fc7 100644
--- a/tests/benchmarks/qml/creation/data/itemUsingOnComponentCompleted.qml
+++ b/tests/benchmarks/qml/creation/data/itemUsingOnComponentCompleted.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemWithAnchoredChild.qml b/tests/benchmarks/qml/creation/data/itemWithAnchoredChild.qml
index d458a05be4..9b5aba61f9 100644
--- a/tests/benchmarks/qml/creation/data/itemWithAnchoredChild.qml
+++ b/tests/benchmarks/qml/creation/data/itemWithAnchoredChild.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemWithChildBindedToSize.qml b/tests/benchmarks/qml/creation/data/itemWithChildBindedToSize.qml
index 9050023d2e..f3aec225d0 100644
--- a/tests/benchmarks/qml/creation/data/itemWithChildBindedToSize.qml
+++ b/tests/benchmarks/qml/creation/data/itemWithChildBindedToSize.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemWithProperties.qml b/tests/benchmarks/qml/creation/data/itemWithProperties.qml
index c6b5504696..1c1a894f08 100644
--- a/tests/benchmarks/qml/creation/data/itemWithProperties.qml
+++ b/tests/benchmarks/qml/creation/data/itemWithProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest1.qml b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest1.qml
index ca943a785c..758732dbed 100644
--- a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest1.qml
+++ b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest1.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest2.qml b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest2.qml
index 1c9909782e..94991e5de1 100644
--- a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest2.qml
+++ b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest3.qml b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest3.qml
index 923c6d7210..ee82bc05da 100644
--- a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest3.qml
+++ b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest3.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest4.qml b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest4.qml
index e3c897d756..0d82416949 100644
--- a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest4.qml
+++ b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest4.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest5.qml b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest5.qml
index ba8a396164..24ddaa0d66 100644
--- a/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest5.qml
+++ b/tests/benchmarks/qml/creation/data/itemWithPropertyBindingsTest5.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/data/qobject.qml b/tests/benchmarks/qml/creation/data/qobject.qml
index d1ecde50cd..7b137d5257 100644
--- a/tests/benchmarks/qml/creation/data/qobject.qml
+++ b/tests/benchmarks/qml/creation/data/qobject.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/creation/tst_creation.cpp b/tests/benchmarks/qml/creation/tst_creation.cpp
index 1816ae346f..ae169cd5a4 100644
--- a/tests/benchmarks/qml/creation/tst_creation.cpp
+++ b/tests/benchmarks/qml/creation/tst_creation.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
diff --git a/tests/benchmarks/qml/deltablue/BaseConstraint.qml b/tests/benchmarks/qml/deltablue/BaseConstraint.qml
new file mode 100644
index 0000000000..4de049699e
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/BaseConstraint.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+import QtQml
+
+QtObject {
+ required property int strength
+ property int satisfaction: Satisfaction.NONE
+ property bool isInput: false
+}
diff --git a/tests/benchmarks/qml/deltablue/CMakeLists.txt b/tests/benchmarks/qml/deltablue/CMakeLists.txt
new file mode 100644
index 0000000000..36a682315a
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_benchmark(tst_deltablue
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Qml
+ Qt::Test
+)
+
+
+set_source_files_properties(
+ Main.qml
+ Strength.qml
+ PROPERTIES
+ QT_QML_SINGLETON_TYPE True
+)
+
+qt_add_qml_module(tst_deltablue
+ URI DeltaBlue
+ VERSION 1.0
+ QML_FILES
+ BaseConstraint.qml
+ ChainTest.qml
+ Constraint.qml
+ Main.qml
+ Main2.qml
+ Plan.qml
+ Planner.qml
+ ProjectionTest.qml
+ Satisfaction.qml
+ Strength.qml
+ Variable.qml
+ deltablue.js
+)
+
diff --git a/tests/benchmarks/qml/deltablue/ChainTest.qml b/tests/benchmarks/qml/deltablue/ChainTest.qml
new file mode 100644
index 0000000000..f671af6567
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/ChainTest.qml
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma ComponentBehavior: Bound
+pragma Strict
+import QtQml
+
+QtObject {
+ id: chainTest
+ property int length: 100
+
+ property Planner planner: Planner { id: planner }
+ property Plan plan: Plan { id: plan }
+
+ property Component variable: Variable {}
+ property list<Variable> variables: new Array(length)
+ property Variable first: variables[0]
+ property Variable last: variables[length - 1]
+ function setVariable(i: int, v: Variable) { variables[i] = v; }
+
+ property Component constraint: Constraint {}
+
+ property Constraint stayConstraint: Constraint {
+ id: stayConstraint
+ myOutput: chainTest.last
+ strength: Strength.STRONG_DEFAULT
+ }
+
+ property Constraint editConstraint: Constraint {
+ id: editConstraint
+ isInput: true
+ myOutput: chainTest.first
+ strength: Strength.PREFERRED
+ }
+
+ Component.onCompleted: {
+ for (let i = 0, end = length; i < end; ++i)
+ setVariable(i, variable.createObject(this, {objectName: "v" + i}) as Variable);
+
+ let v = variables;
+ let p = planner;
+ for (let i = 0, end = length - 1; i < end; ++i) {
+ p.incrementalAdd(constraint.createObject(this, {
+ myInput: v[i],
+ myOutput: v[i + 1],
+ strength: Strength.REQUIRED
+ }) as Constraint);
+ }
+
+ planner.incrementalAdd(stayConstraint)
+ planner.incrementalAdd(editConstraint)
+ }
+
+ function verify(i: int) {
+ if (last.value !== i)
+ console.error("Chain test failed.", last.value, i, first.value);
+ }
+
+ function run() {
+ planner.populatePlanFromConstraint(editConstraint, plan);
+ for (let i = 0; i < 100; ++i) {
+ first.value = i;
+ plan.execute();
+ verify(i);
+ }
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/Constraint.qml b/tests/benchmarks/qml/deltablue/Constraint.qml
new file mode 100644
index 0000000000..bf84870fa9
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/Constraint.qml
@@ -0,0 +1,147 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+import QtQml
+
+BaseConstraint {
+ id: self
+
+ property Variable myInput
+ property Variable myOutput
+
+ property Variable scale
+ property Variable offset
+
+ function addToGraph() {
+ let ihn = myInput;
+ if (ihn)
+ ihn.addConstraint(self);
+ let out = myOutput;
+ if (out)
+ out.addConstraint(self);
+ let s = scale;
+ if (s)
+ s.addConstraint(self);
+ let o = offset;
+ if (o)
+ o.addConstraint(self);
+ satisfaction = Satisfaction.NONE;
+ }
+
+ function removeFromGraph() {
+ if (myInput)
+ myInput.removeConstraint(self);
+ if (myOutput)
+ myOutput.removeConstraint(self);
+ if (scale)
+ scale.removeConstraint(self);
+ if (offset)
+ offset.removeConstraint(self);
+ satisfaction = Satisfaction.NONE;
+ }
+
+ property Variable input: (satisfaction === Satisfaction.BACKWARD)
+ ? myOutput
+ : myInput
+
+ property Variable output: (satisfaction === Satisfaction.BACKWARD)
+ ? myInput
+ : myOutput
+
+ function recalculate() {
+ let ihn = input;
+ let out = output;
+
+ if (!ihn) {
+ out.walkStrength = strength;
+ out.stay = !isInput;
+ return;
+ }
+
+ out.walkStrength = Strength.weakestOf(strength, ihn.walkStrength);
+ let stay = ihn.stay;
+
+ // Optimize for number of lookups. We lookup scale and offset only once and we keep
+ // stay in a local as long as we can.
+
+ let s = scale
+ if (s)
+ stay = stay && s.stay;
+
+ let o = offset
+ if (o)
+ stay = stay && o.stay;
+
+ out.stay = stay;
+
+ if (stay)
+ out.value = evaluate();
+ }
+
+ function chooseMethod(mark: int) {
+ let ihn = myInput;
+ let out = myOutput;
+ let outStrength = out.walkStrength;
+
+ if (!ihn) {
+ satisfaction = (out.mark !== mark && Strength.stronger(strength, outStrength))
+ ? Satisfaction.FORWARD
+ : Satisfaction.NONE;
+ return;
+ }
+
+ let ihnStrength = ihn.walkStrength;
+
+ if (Strength.weaker(ihnStrength, outStrength)) {
+ satisfaction = Strength.stronger(strength, ihnStrength)
+ ? Satisfaction.BACKWARD
+ : Satisfaction.NONE;
+ return;
+ }
+
+ satisfaction = Strength.stronger(strength, outStrength)
+ ? Satisfaction.FORWARD
+ : Satisfaction.BACKWARD
+ }
+
+ function markInputs(mark: int) {
+ let i = input;
+ if (i)
+ i.mark = mark;
+ let s = scale;
+ if (s)
+ s.mark = mark;
+ let o = offset;
+ if (o)
+ o.mark = mark;
+ }
+
+ function inputsKnown(mark: int) : bool {
+ let ihn = input;
+ return !ihn || ihn.mark === mark || ihn.stay || ihn.determinedBy === null;
+ }
+
+ function evaluate() : int {
+ let result = input.value;
+
+ // This is a rather hot code path. It pays off to do the lookups for offset and scale
+ // only once
+ let o = offset;
+ let s = scale;
+
+ if (satisfaction === Satisfaction.BACKWARD) {
+ if (o)
+ result = result - o.value;
+ if (s)
+ result = result / s.value;
+ } else {
+ if (s)
+ result = result * s.value;
+ if (o)
+ result = result + o.value; // TODO: += and -= miscompile!
+ }
+
+ return result;
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/Main.qml b/tests/benchmarks/qml/deltablue/Main.qml
new file mode 100644
index 0000000000..9260cace31
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/Main.qml
@@ -0,0 +1,132 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Singleton
+import QtQml
+
+QtObject {
+ property Component newPlanner: Planner {}
+ property Component newVariable: Variable {}
+ property Component newConstraint: Constraint {
+ id: constraint
+ Component.onCompleted: planner.incrementalAdd(constraint)
+ }
+ property Component newPlan: Plan {}
+
+ // Global variable holding the current planner.
+ property Planner planner: null
+
+ property Variable prev
+ property Variable first
+ property Variable last
+ function chainTest(n: int) {
+ planner = newPlanner.createObject(this) as Planner;
+ prev = null;
+ first = null;
+ last = null;
+
+ // Build chain of n equality constraints
+ for (var i = 0; i <= n; i++) {
+ var v = newVariable.createObject(this, {objectName: "v" + i}) as Variable;
+ if (prev !== null) {
+ newConstraint.createObject(this, {
+ myInput: prev,
+ myOutput: v,
+ strength: Strength.REQUIRED
+ });
+ }
+ if (i === 0)
+ first = v;
+ if (i === n)
+ last = v;
+ prev = v;
+ }
+
+ newConstraint.createObject(this, {myOutput: last, strength: Strength.STRONG_DEFAULT});
+ let chainConstraint =
+ newConstraint.createObject(this, {
+ isInput: true,
+ myOutput: first,
+ strength: Strength.PREFERRED
+ }) as Constraint;
+
+ let plan = newPlan.createObject(this);
+ planner.populatePlanFromConstraint(chainConstraint, plan);
+ for (let i = 0; i < 100; i++) {
+ first.value = i;
+ plan.execute();
+ if (last.value != i)
+ console.error("Chain test failed.");
+ }
+ }
+
+ property Variable scale
+ property Variable offset
+ property Variable src
+ property Variable dst
+ property list<Variable> dests
+ function projectionTest(n: int) {
+ planner = newPlanner.createObject(this) as Planner;
+ scale = newVariable.createObject(this, {objectName: "scale", value: 10}) as Variable;
+ offset = newVariable.createObject(this, {objectName: "offset", value: 1000}) as Variable;
+ src = null;
+ dst = null;
+
+ dests = [];
+ for (let i = 0; i < n; i++) {
+ src = newVariable.createObject(this, {objectName: "src" + i, value: i}) as Variable;
+ dst = newVariable.createObject(this, {objectName: "dst" + i, value: i}) as Variable;
+ dests.push(dst);
+ newConstraint.createObject(this, {myOutput: src, strength: Strength.NORMAL});
+ newConstraint.createObject(this, {
+ myInput: src,
+ myOutput: dst,
+ scale: scale,
+ offset: offset,
+ strength: Strength.REQUIRED
+ });
+ }
+
+ change(src, 17);
+ if (dst.value !== 1170)
+ console.error("Projection 1 failed");
+ change(dst, 1050);
+ if (src.value !== 5)
+ console.error("Projection 2 failed");
+ change(scale, 5);
+ for (let i = 0; i < n - 1; i++) {
+ if (dests[i].value !== i * 5 + 1000)
+ console.error("Projection 3 failed");
+ }
+ change(offset, 2000);
+ for (let i = 0; i < n - 1; i++) {
+ if (dests[i].value !== i * 5 + 2000)
+ console.error("Projection 4 failed");
+ }
+ }
+
+ property Constraint edit
+ function change(v: Variable, newValue: int) {
+ edit = newConstraint.createObject(this, {
+ isInput: true,
+ myOutput: v,
+ strength: Strength.PREFERRED
+ }) as Constraint
+ let plan = newPlan.createObject(this);
+ planner.populatePlanFromConstraint(edit, plan);
+ for (let i = 0; i < 10; i++) {
+ v.value = newValue;
+ plan.execute();
+ }
+
+ if (edit.satisfaction !== Satisfaction.NONE)
+ planner.incrementalRemove(edit);
+ else
+ edit.removeFromGraph();
+ }
+
+ function deltaBlue() {
+ chainTest(100);
+ projectionTest(100);
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/Main2.qml b/tests/benchmarks/qml/deltablue/Main2.qml
new file mode 100644
index 0000000000..5f2f610fb6
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/Main2.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+import "deltablue.js" as DeltaBlue
+
+QtObject {
+ property var chain: DeltaBlue.chainTest(100)
+ property var projection: DeltaBlue.projectionTest(100);
+
+ function deltaBlue() {
+ chain();
+ projection();
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/Plan.qml b/tests/benchmarks/qml/deltablue/Plan.qml
new file mode 100644
index 0000000000..4e3bd7c8a6
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/Plan.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+import QtQml
+
+QtObject {
+ property list<Constraint> v
+
+ function clear() {
+ v.length = 0;
+ }
+
+ function addConstraint(c: Constraint) {
+ let constraints = v;
+ constraints[constraints.length++] = c;
+ }
+
+ function execute() {
+ let constraints = v;
+ for (let i = 0, length = constraints.length; i < length; ++i) {
+ let c = constraints[i];
+ if (c.input)
+ c.output.value = c.evaluate();
+ }
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/Planner.qml b/tests/benchmarks/qml/deltablue/Planner.qml
new file mode 100644
index 0000000000..8642b9e62f
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/Planner.qml
@@ -0,0 +1,194 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+pragma ComponentBehavior: Bound
+
+import QtQml
+
+QtObject {
+ id: planner
+
+ component Remover: QtObject {
+ property list<Constraint> unsatisfied
+ property list<Variable> toRemove
+
+ function removeOne(v: Variable) {
+ let vDeterminedBy = v.determinedBy;
+ for (let i = 0, length = v.length(); i < length; ++i) {
+ let next = v.constraint(i) as Constraint;
+ if (next.satisfaction === Satisfaction.NONE) {
+ let u = unsatisfied;
+ u[u.length++] = next;
+ } else if (next !== vDeterminedBy) {
+ next.recalculate();
+ let t = toRemove;
+ t[t.length++] = next.output;
+ }
+ }
+ }
+
+ function run(c: Constraint) {
+ c.satisfaction = Satisfaction.NONE;
+ c.removeFromGraph();
+
+ let out = c.output as Variable;
+ out.determinedBy = null;
+ out.walkStrength = Strength.WEAKEST;
+ out.stay = true;
+
+ removeOne(out);
+ let t = toRemove;
+ while (t.length > 0) {
+ let one = t[t.length - 1];
+ --t.length;
+ removeOne(one);
+ }
+
+ let u = unsatisfied;
+ let ss = Strength;
+ let sat = satisfier
+ for (let s = ss.REQUIRED, end = ss.WEAKEST; s !== end; s = ss.nextWeaker(s)) {
+ for (let i = 0, length = u.length; i < length; ++i) {
+ let uu = u[i];
+ if (uu.strength === s)
+ sat.run(uu);
+ }
+ }
+
+ u.length = 0;
+ }
+ }
+
+ component Satisfier: QtObject {
+ property list<Constraint> todo
+
+ function addPropagate(c: Constraint, mark: int) : bool {
+ let d = c;
+ while (true) {
+ let output = d.output as Variable;
+
+ if (output.mark === mark) {
+ remover.run(c);
+ todo.length = 0;
+ return false;
+ }
+ d.recalculate();
+
+ let outputDeterminedBy = output.determinedBy;
+ for (let i = 0, length = output.length(); i < length; ++i) {
+ let dd = output.constraint(i) as Constraint;
+ if (dd !== outputDeterminedBy && dd.satisfaction !== Satisfaction.NONE)
+ todo[todo.length++] = dd;
+ }
+
+ let todoLength = todo.length;
+ if (todoLength === 0)
+ break;
+
+ d = todo[todoLength - 1] as Constraint;
+ --todo.length;
+ }
+ return true;
+ }
+
+ function satisfy(c: Constraint, mark: int) : Constraint {
+ c.chooseMethod(mark);
+ if (c.satisfaction === Satisfaction.NONE) {
+ if (c.strength === Strength.REQUIRED)
+ console.error("Could not satisfy a required constraint!");
+ return null;
+ }
+
+ c.markInputs(mark);
+ let out = c.output as Variable;
+ let overridden = out.determinedBy as Constraint;
+ if (overridden !== null)
+ overridden.satisfaction = Satisfaction.NONE;
+
+ out.determinedBy = c;
+ if (!addPropagate(c, mark))
+ console.error("Cycle encountered");
+
+ out.mark = mark;
+ return overridden;
+ }
+
+ function run(c: Constraint) {
+ let mark = planner.newMark();
+ let overridden = satisfy(c, mark);
+ while (overridden !== null)
+ overridden = satisfy(overridden, mark);
+ }
+ }
+
+ component Populator: QtObject {
+ property list<Constraint> constraints
+
+ function run(initial: Constraint, plan: Plan) {
+ // TODO: If we pass initial as a list here, we cannot assign to constraints!
+ let l = constraints;
+ l.length = 1;
+ l[0] = initial;
+ plan.clear();
+ for (let i = 0; i < l.length; ++i) {
+ let c = l[i];
+ if (!c.isInput || c.satisfaction === Satisfaction.NONE) {
+ // Not eligible for inclusion. Remove
+ let last = l[l.length - 1];
+ if (i < l.length--)
+ l[i] = last;
+ }
+ }
+
+ let mark = planner.newMark();
+ while (l.length > 0) {
+ let c = l[l.length - 1];
+ --l.length;
+
+ let output = c.output as Variable;
+ if (output.mark !== mark && c.inputsKnown(mark)) {
+ plan.addConstraint(c);
+ output.mark = mark;
+ addConstraintsConsumingTo(output);
+ }
+ }
+ }
+
+ function addConstraintsConsumingTo(v: Variable) {
+ let vDeterminedBy = v.determinedBy;
+ let l = constraints;
+ for (let i = 0, length = v.length(); i < length; ++i) {
+ let c = v.constraint(i) as Constraint;
+ if (c !== vDeterminedBy && c.satisfaction !== Satisfaction.NONE)
+ l[l.length++] = c;
+ }
+ }
+ }
+
+ property Remover remover: Remover { id: remover }
+ property Satisfier satisfier: Satisfier { id: satisfier }
+ property Populator populator: Populator { id: populator }
+
+ property int currentMark: 0
+
+
+ function incrementalAdd(c : Constraint) {
+ c.addToGraph();
+ satisfier.run(c);
+ }
+
+ function incrementalRemove(c : Constraint) {
+ remover.run(c);
+ }
+
+ // TODO: If we pass a list here and modify it in place, the behavior differs between
+ // interpreter and compiler.
+ function populatePlanFromConstraint(c: Constraint, plan: Plan) {
+ populator.run(c, plan)
+ }
+
+ function newMark() : int {
+ return ++currentMark;
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/ProjectionTest.qml b/tests/benchmarks/qml/deltablue/ProjectionTest.qml
new file mode 100644
index 0000000000..4386e226f6
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/ProjectionTest.qml
@@ -0,0 +1,112 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma ComponentBehavior: Bound
+pragma Strict
+import QtQml
+
+QtObject {
+ id: projectionTest
+ property int length: 100
+
+ property Planner planner: Planner {
+ id: planner
+ }
+
+ property Variable scale: Variable {
+ id: scaleVariable
+ objectName: "scale"
+ value: 10
+ }
+
+ property Variable offset: Variable {
+ id: offsetVariable
+ objectName: "offset"
+ value: 1000
+ }
+
+ property Component variable: Variable {}
+
+ property list<Variable> sources: new Array(length)
+ property Variable src: sources[length - 1]
+ function setSource(i: int, v: Variable) { sources[i] = v; }
+
+ property list<Variable> destinations: new Array(length)
+ property Variable dst: destinations[length - 1]
+ function setDestination(i: int, v: Variable) { destinations[i] = v; }
+
+ property Component constraint: Constraint {}
+
+ Component.onCompleted: {
+ let p = planner;
+ for (let i = 0, end = length; i < end; ++i) {
+ let source = variable.createObject(this, {
+ objectName: "src" + i,
+ value: i
+ }) as Variable;
+ setSource(i, source);
+
+ let destination = variable.createObject(this, {
+ objectName: "dst" + i,
+ value: i
+ }) as Variable;
+ setDestination(i, destination);
+
+ p.incrementalAdd(constraint.createObject(this, {
+ myOutput: source,
+ strength: Strength.NORMAL
+ }) as Constraint);
+
+ p.incrementalAdd(constraint.createObject(this, {
+ myInput: source,
+ myOutput: destination,
+ scale: scaleVariable,
+ offset: offsetVariable,
+ strength: Strength.REQUIRED
+ }) as Constraint);
+ }
+ }
+
+ component Changer : Constraint {
+ id: editConstraint
+ isInput: true
+
+ strength: Strength.PREFERRED
+
+ property Plan plan: Plan {}
+
+ function run(v: Variable, newValue: int) {
+ myOutput = v;
+ planner.incrementalAdd(editConstraint);
+ planner.populatePlanFromConstraint(editConstraint, plan);
+ for (let i = 0; i < 10; ++i) {
+ v.value = newValue;
+ plan.execute();
+ }
+ planner.incrementalRemove(editConstraint);
+ }
+ }
+
+ property Changer changer: Changer { id: changer }
+
+ function run() {
+ changer.run(src, 17);
+ if (dst.value !== 1170)
+ console.error("Projection 1 failed", dst.value, offsetVariable.value);
+ changer.run(dst, 1050);
+ if (src.value !== 5)
+ console.error("Projection 2 failed", src.value, offsetVariable.value);
+ changer.run(scaleVariable, 5);
+
+ let d = destinations;
+ for (let i = 0, end = length - 1; i < end; ++i) {
+ if (d[i].value !== i * 5 + 1000)
+ console.error("Projection 3 failed");
+ }
+ changer.run(offsetVariable, 2000);
+ for (let i = 0, end = length - 1; i < end; ++i) {
+ if (d[i].value !== i * 5 + 2000)
+ console.error("Projection 4 failed");
+ }
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/Satisfaction.qml b/tests/benchmarks/qml/deltablue/Satisfaction.qml
new file mode 100644
index 0000000000..a281f41c62
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/Satisfaction.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+import QtQml
+
+QtObject {
+ enum Value {
+ NONE = 0,
+ FORWARD = 1,
+ BACKWARD = 2
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/Strength.qml b/tests/benchmarks/qml/deltablue/Strength.qml
new file mode 100644
index 0000000000..529230ca80
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/Strength.qml
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+pragma Singleton
+import QtQml
+
+QtObject {
+ enum Value {
+ REQUIRED = 0,
+ STRONG_PREFERRED = 1,
+ PREFERRED = 2,
+ STRONG_DEFAULT = 3,
+ NORMAL = 4,
+ WEAK_DEFAULT = 5,
+ WEAKEST = 6
+ }
+
+ function stronger(s1: int, s2: int) : bool {
+ return s1 < s2;
+ }
+
+ function weaker(s1: int, s2: int) : bool {
+ return s1 > s2;
+ }
+
+ function weakestOf(s1: int, s2: int) : int {
+ return weaker(s1, s2) ? s1 : s2;
+ }
+
+ function strongestOf(s1: int, s2: int) : int {
+ return stronger(s1, s2) ? s1 : s2;
+ }
+
+ function nextWeaker(s: int) : int {
+ switch (s) {
+ case 0: return Strength.WEAKEST;
+ case 1: return Strength.WEAK_DEFAULT;
+ case 2: return Strength.NORMAL;
+ case 3: return Strength.STRONG_DEFAULT;
+ case 4: return Strength.PREFERRED;
+ case 5: return Strength.REQUIRED;
+ }
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/Variable.qml b/tests/benchmarks/qml/deltablue/Variable.qml
new file mode 100644
index 0000000000..6b3411e520
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/Variable.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Strict
+import QtQml
+
+QtObject {
+ property int value: 0
+ property list<BaseConstraint> constraints
+ property BaseConstraint determinedBy: null
+ property int mark: 0
+ property int walkStrength: Strength.WEAKEST
+ property bool stay: true
+
+ function addConstraint(c: BaseConstraint) {
+ let l = constraints;
+ l[l.length++] = c;
+ }
+
+ function removeConstraint(c: BaseConstraint) {
+ let l = constraints;
+ let index = 0;
+ let skipped = 0;
+ for (let i = 0, length = l.length; i < length; ++i) {
+ let value = l[i] as BaseConstraint;
+ if (value !== c) {
+ if (index != i)
+ l[index] = value;
+ ++index
+ } else {
+ ++skipped;
+ }
+ }
+
+ l.length -= skipped;
+
+ if (determinedBy === c)
+ determinedBy = null;
+ }
+
+ function length(): int {
+ return constraints.length
+ }
+
+ function constraint(i: int) : BaseConstraint {
+ return constraints[i];
+ }
+}
diff --git a/tests/benchmarks/qml/deltablue/deltablue.js b/tests/benchmarks/qml/deltablue/deltablue.js
new file mode 100644
index 0000000000..b57af3f6f2
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/deltablue.js
@@ -0,0 +1,858 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/**
+ * A JavaScript implementation of the DeltaBlue constraint-solving
+ * algorithm, as described in:
+ *
+ * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
+ * Bjorn N. Freeman-Benson and John Maloney
+ * January 1990 Communications of the ACM,
+ * also available as University of Washington TR 89-08-06.
+ *
+ * Beware: this benchmark is written in a grotesque style where
+ * the constraint model is built by side-effects from constructors.
+ * I've kept it this way to avoid deviating too much from the original
+ * implementation.
+ */
+
+
+/* --- O b j e c t M o d e l --- */
+
+Object.prototype.inheritsFrom = function (shuper) {
+ function Inheriter() { }
+ Inheriter.prototype = shuper.prototype;
+ this.prototype = new Inheriter();
+ this.superConstructor = shuper;
+}
+
+function OrderedCollection() {
+ this.elms = new Array();
+}
+
+OrderedCollection.prototype.add = function (elm) {
+ this.elms.push(elm);
+}
+
+OrderedCollection.prototype.at = function (index) {
+ return this.elms[index];
+}
+
+OrderedCollection.prototype.size = function () {
+ return this.elms.length;
+}
+
+OrderedCollection.prototype.removeFirst = function () {
+ return this.elms.pop();
+}
+
+OrderedCollection.prototype.remove = function (elm) {
+ var index = 0, skipped = 0;
+ for (var i = 0; i < this.elms.length; i++) {
+ var value = this.elms[i];
+ if (value != elm) {
+ this.elms[index] = value;
+ index++;
+ } else {
+ skipped++;
+ }
+ }
+ for (var i = 0; i < skipped; i++)
+ this.elms.pop();
+}
+
+/* --- *
+ * S t r e n g t h
+ * --- */
+
+/**
+ * Strengths are used to measure the relative importance of constraints.
+ * New strengths may be inserted in the strength hierarchy without
+ * disrupting current constraints. Strengths cannot be created outside
+ * this class, so pointer comparison can be used for value comparison.
+ */
+function Strength(strengthValue, name) {
+ this.strengthValue = strengthValue;
+ this.name = name;
+}
+
+Strength.stronger = function (s1, s2) {
+ return s1.strengthValue < s2.strengthValue;
+}
+
+Strength.weaker = function (s1, s2) {
+ return s1.strengthValue > s2.strengthValue;
+}
+
+Strength.weakestOf = function (s1, s2) {
+ return this.weaker(s1, s2) ? s1 : s2;
+}
+
+Strength.strongest = function (s1, s2) {
+ return this.stronger(s1, s2) ? s1 : s2;
+}
+
+Strength.prototype.nextWeaker = function () {
+ switch (this.strengthValue) {
+ case 0: return Strength.WEAKEST;
+ case 1: return Strength.WEAK_DEFAULT;
+ case 2: return Strength.NORMAL;
+ case 3: return Strength.STRONG_DEFAULT;
+ case 4: return Strength.PREFERRED;
+ case 5: return Strength.REQUIRED;
+ }
+}
+
+// Strength constants.
+Strength.REQUIRED = new Strength(0, "required");
+Strength.STONG_PREFERRED = new Strength(1, "strongPreferred");
+Strength.PREFERRED = new Strength(2, "preferred");
+Strength.STRONG_DEFAULT = new Strength(3, "strongDefault");
+Strength.NORMAL = new Strength(4, "normal");
+Strength.WEAK_DEFAULT = new Strength(5, "weakDefault");
+Strength.WEAKEST = new Strength(6, "weakest");
+
+/* --- *
+ * C o n s t r a i n t
+ * --- */
+
+/**
+ * An abstract class representing a system-maintainable relationship
+ * (or "constraint") between a set of variables. A constraint supplies
+ * a strength instance variable; concrete subclasses provide a means
+ * of storing the constrained variables and other information required
+ * to represent a constraint.
+ */
+function Constraint(strength) {
+ this.strength = strength;
+}
+
+/**
+ * Activate this constraint and attempt to satisfy it.
+ */
+Constraint.prototype.addConstraint = function () {
+ this.addToGraph();
+ planner.incrementalAdd(this);
+}
+
+/**
+ * Attempt to find a way to enforce this constraint. If successful,
+ * record the solution, perhaps modifying the current dataflow
+ * graph. Answer the constraint that this constraint overrides, if
+ * there is one, or nil, if there isn't.
+ * Assume: I am not already satisfied.
+ */
+Constraint.prototype.satisfy = function (mark) {
+ this.chooseMethod(mark);
+ if (!this.isSatisfied()) {
+ if (this.strength == Strength.REQUIRED)
+ alert("Could not satisfy a required constraint!");
+ return null;
+ }
+ this.markInputs(mark);
+ var out = this.output();
+ var overridden = out.determinedBy;
+ if (overridden != null) overridden.markUnsatisfied();
+ out.determinedBy = this;
+ if (!planner.addPropagate(this, mark))
+ alert("Cycle encountered");
+ out.mark = mark;
+ return overridden;
+}
+
+Constraint.prototype.destroyConstraint = function () {
+ if (this.isSatisfied()) planner.incrementalRemove(this);
+ else this.removeFromGraph();
+}
+
+/**
+ * Normal constraints are not input constraints. An input constraint
+ * is one that depends on external state, such as the mouse, the
+ * keybord, a clock, or some arbitraty piece of imperative code.
+ */
+Constraint.prototype.isInput = function () {
+ return false;
+}
+
+/* --- *
+ * U n a r y C o n s t r a i n t
+ * --- */
+
+/**
+ * Abstract superclass for constraints having a single possible output
+ * variable.
+ */
+function UnaryConstraint(v, strength) {
+ UnaryConstraint.superConstructor.call(this, strength);
+ this.myOutput = v;
+ this.satisfied = false;
+ this.addConstraint();
+}
+
+UnaryConstraint.inheritsFrom(Constraint);
+
+/**
+ * Adds this constraint to the constraint graph
+ */
+UnaryConstraint.prototype.addToGraph = function () {
+ this.myOutput.addConstraint(this);
+ this.satisfied = false;
+}
+
+/**
+ * Decides if this constraint can be satisfied and records that
+ * decision.
+ */
+UnaryConstraint.prototype.chooseMethod = function (mark) {
+ this.satisfied = (this.myOutput.mark != mark)
+ && Strength.stronger(this.strength, this.myOutput.walkStrength);
+}
+
+/**
+ * Returns true if this constraint is satisfied in the current solution.
+ */
+UnaryConstraint.prototype.isSatisfied = function () {
+ return this.satisfied;
+}
+
+UnaryConstraint.prototype.markInputs = function (mark) {
+ // has no inputs
+}
+
+/**
+ * Returns the current output variable.
+ */
+UnaryConstraint.prototype.output = function () {
+ return this.myOutput;
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this constraint. Assume
+ * this constraint is satisfied.
+ */
+UnaryConstraint.prototype.recalculate = function () {
+ this.myOutput.walkStrength = this.strength;
+ this.myOutput.stay = !this.isInput();
+ if (this.myOutput.stay) this.execute(); // Stay optimization
+}
+
+/**
+ * Records that this constraint is unsatisfied
+ */
+UnaryConstraint.prototype.markUnsatisfied = function () {
+ this.satisfied = false;
+}
+
+UnaryConstraint.prototype.inputsKnown = function () {
+ return true;
+}
+
+UnaryConstraint.prototype.removeFromGraph = function () {
+ if (this.myOutput != null) this.myOutput.removeConstraint(this);
+ this.satisfied = false;
+}
+
+/* --- *
+ * S t a y C o n s t r a i n t
+ * --- */
+
+/**
+ * Variables that should, with some level of preference, stay the same.
+ * Planners may exploit the fact that instances, if satisfied, will not
+ * change their output during plan execution. This is called "stay
+ * optimization".
+ */
+function StayConstraint(v, str) {
+ StayConstraint.superConstructor.call(this, v, str);
+}
+
+StayConstraint.inheritsFrom(UnaryConstraint);
+
+StayConstraint.prototype.execute = function () {
+ // Stay constraints do nothing
+}
+
+/* --- *
+ * E d i t C o n s t r a i n t
+ * --- */
+
+/**
+ * A unary input constraint used to mark a variable that the client
+ * wishes to change.
+ */
+function EditConstraint(v, str) {
+ EditConstraint.superConstructor.call(this, v, str);
+}
+
+EditConstraint.inheritsFrom(UnaryConstraint);
+
+/**
+ * Edits indicate that a variable is to be changed by imperative code.
+ */
+EditConstraint.prototype.isInput = function () {
+ return true;
+}
+
+EditConstraint.prototype.execute = function () {
+ // Edit constraints do nothing
+}
+
+/* --- *
+ * B i n a r y C o n s t r a i n t
+ * --- */
+
+var Direction = new Object();
+Direction.NONE = 0;
+Direction.FORWARD = 1;
+Direction.BACKWARD = -1;
+
+/**
+ * Abstract superclass for constraints having two possible output
+ * variables.
+ */
+function BinaryConstraint(var1, var2, strength) {
+ BinaryConstraint.superConstructor.call(this, strength);
+ this.v1 = var1;
+ this.v2 = var2;
+ this.direction = Direction.NONE;
+ this.addConstraint();
+}
+
+BinaryConstraint.inheritsFrom(Constraint);
+
+/**
+ * Decides if this constraint can be satisfied and which way it
+ * should flow based on the relative strength of the variables related,
+ * and record that decision.
+ */
+BinaryConstraint.prototype.chooseMethod = function (mark) {
+ if (this.v1.mark == mark) {
+ this.direction = (this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength))
+ ? Direction.FORWARD
+ : Direction.NONE;
+ }
+ if (this.v2.mark == mark) {
+ this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength))
+ ? Direction.BACKWARD
+ : Direction.NONE;
+ }
+ if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) {
+ this.direction = Strength.stronger(this.strength, this.v1.walkStrength)
+ ? Direction.BACKWARD
+ : Direction.NONE;
+ } else {
+ this.direction = Strength.stronger(this.strength, this.v2.walkStrength)
+ ? Direction.FORWARD
+ : Direction.BACKWARD
+ }
+}
+
+/**
+ * Add this constraint to the constraint graph
+ */
+BinaryConstraint.prototype.addToGraph = function () {
+ this.v1.addConstraint(this);
+ this.v2.addConstraint(this);
+ this.direction = Direction.NONE;
+}
+
+/**
+ * Answer true if this constraint is satisfied in the current solution.
+ */
+BinaryConstraint.prototype.isSatisfied = function () {
+ return this.direction != Direction.NONE;
+}
+
+/**
+ * Mark the input variable with the given mark.
+ */
+BinaryConstraint.prototype.markInputs = function (mark) {
+ this.input().mark = mark;
+}
+
+/**
+ * Returns the current input variable
+ */
+BinaryConstraint.prototype.input = function () {
+ return (this.direction == Direction.FORWARD) ? this.v1 : this.v2;
+}
+
+/**
+ * Returns the current output variable
+ */
+BinaryConstraint.prototype.output = function () {
+ return (this.direction == Direction.FORWARD) ? this.v2 : this.v1;
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this
+ * constraint. Assume this constraint is satisfied.
+ */
+BinaryConstraint.prototype.recalculate = function () {
+ var ihn = this.input(), out = this.output();
+ out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
+ out.stay = ihn.stay;
+ if (out.stay) this.execute();
+}
+
+/**
+ * Record the fact that this constraint is unsatisfied.
+ */
+BinaryConstraint.prototype.markUnsatisfied = function () {
+ this.direction = Direction.NONE;
+}
+
+BinaryConstraint.prototype.inputsKnown = function (mark) {
+ var i = this.input();
+ return i.mark == mark || i.stay || i.determinedBy == null;
+}
+
+BinaryConstraint.prototype.removeFromGraph = function () {
+ if (this.v1 != null) this.v1.removeConstraint(this);
+ if (this.v2 != null) this.v2.removeConstraint(this);
+ this.direction = Direction.NONE;
+}
+
+/* --- *
+ * S c a l e C o n s t r a i n t
+ * --- */
+
+/**
+ * Relates two variables by the linear scaling relationship: "v2 =
+ * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
+ * this relationship but the scale factor and offset are considered
+ * read-only.
+ */
+function ScaleConstraint(src, scale, offset, dest, strength) {
+ this.direction = Direction.NONE;
+ this.scale = scale;
+ this.offset = offset;
+ ScaleConstraint.superConstructor.call(this, src, dest, strength);
+}
+
+ScaleConstraint.inheritsFrom(BinaryConstraint);
+
+/**
+ * Adds this constraint to the constraint graph.
+ */
+ScaleConstraint.prototype.addToGraph = function () {
+ ScaleConstraint.superConstructor.prototype.addToGraph.call(this);
+ this.scale.addConstraint(this);
+ this.offset.addConstraint(this);
+}
+
+ScaleConstraint.prototype.removeFromGraph = function () {
+ ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this);
+ if (this.scale != null) this.scale.removeConstraint(this);
+ if (this.offset != null) this.offset.removeConstraint(this);
+}
+
+ScaleConstraint.prototype.markInputs = function (mark) {
+ ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark);
+ this.scale.mark = this.offset.mark = mark;
+}
+
+/**
+ * Enforce this constraint. Assume that it is satisfied.
+ */
+ScaleConstraint.prototype.execute = function () {
+ if (this.direction == Direction.FORWARD) {
+ this.v2.value = this.v1.value * this.scale.value + this.offset.value;
+ } else {
+ this.v1.value = (this.v2.value - this.offset.value) / this.scale.value;
+ }
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this constraint. Assume
+ * this constraint is satisfied.
+ */
+ScaleConstraint.prototype.recalculate = function () {
+ var ihn = this.input(), out = this.output();
+ out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
+ out.stay = ihn.stay && this.scale.stay && this.offset.stay;
+ if (out.stay) this.execute();
+}
+
+/* --- *
+ * E q u a l i t y C o n s t r a i n t
+ * --- */
+
+/**
+ * Constrains two variables to have the same value.
+ */
+function EqualityConstraint(var1, var2, strength) {
+ EqualityConstraint.superConstructor.call(this, var1, var2, strength);
+}
+
+EqualityConstraint.inheritsFrom(BinaryConstraint);
+
+/**
+ * Enforce this constraint. Assume that it is satisfied.
+ */
+EqualityConstraint.prototype.execute = function () {
+ this.output().value = this.input().value;
+}
+
+/* --- *
+ * V a r i a b l e
+ * --- */
+
+/**
+ * A constrained variable. In addition to its value, it maintain the
+ * structure of the constraint graph, the current dataflow graph, and
+ * various parameters of interest to the DeltaBlue incremental
+ * constraint solver.
+ **/
+function Variable(name, initialValue) {
+ this.value = initialValue || 0;
+ this.constraints = new OrderedCollection();
+ this.determinedBy = null;
+ this.mark = 0;
+ this.walkStrength = Strength.WEAKEST;
+ this.stay = true;
+ this.name = name;
+}
+
+/**
+ * Add the given constraint to the set of all constraints that refer
+ * this variable.
+ */
+Variable.prototype.addConstraint = function (c) {
+ this.constraints.add(c);
+}
+
+/**
+ * Removes all traces of c from this variable.
+ */
+Variable.prototype.removeConstraint = function (c) {
+ this.constraints.remove(c);
+ if (this.determinedBy == c) this.determinedBy = null;
+}
+
+/* --- *
+ * P l a n n e r
+ * --- */
+
+/**
+ * The DeltaBlue planner
+ */
+function Planner() {
+ this.currentMark = 0;
+}
+
+/**
+ * Attempt to satisfy the given constraint and, if successful,
+ * incrementally update the dataflow graph. Details: If satifying
+ * the constraint is successful, it may override a weaker constraint
+ * on its output. The algorithm attempts to resatisfy that
+ * constraint using some other method. This process is repeated
+ * until either a) it reaches a variable that was not previously
+ * determined by any constraint or b) it reaches a constraint that
+ * is too weak to be satisfied using any of its methods. The
+ * variables of constraints that have been processed are marked with
+ * a unique mark value so that we know where we've been. This allows
+ * the algorithm to avoid getting into an infinite loop even if the
+ * constraint graph has an inadvertent cycle.
+ */
+Planner.prototype.incrementalAdd = function (c) {
+ var mark = this.newMark();
+ var overridden = c.satisfy(mark);
+ while (overridden != null)
+ overridden = overridden.satisfy(mark);
+}
+
+/**
+ * Entry point for retracting a constraint. Remove the given
+ * constraint and incrementally update the dataflow graph.
+ * Details: Retracting the given constraint may allow some currently
+ * unsatisfiable downstream constraint to be satisfied. We therefore collect
+ * a list of unsatisfied downstream constraints and attempt to
+ * satisfy each one in turn. This list is traversed by constraint
+ * strength, strongest first, as a heuristic for avoiding
+ * unnecessarily adding and then overriding weak constraints.
+ * Assume: c is satisfied.
+ */
+Planner.prototype.incrementalRemove = function (c) {
+ var out = c.output();
+ c.markUnsatisfied();
+ c.removeFromGraph();
+ var unsatisfied = this.removePropagateFrom(out);
+ var strength = Strength.REQUIRED;
+ do {
+ for (var i = 0; i < unsatisfied.size(); i++) {
+ var u = unsatisfied.at(i);
+ if (u.strength == strength)
+ this.incrementalAdd(u);
+ }
+ strength = strength.nextWeaker();
+ } while (strength != Strength.WEAKEST);
+}
+
+/**
+ * Select a previously unused mark value.
+ */
+Planner.prototype.newMark = function () {
+ return ++this.currentMark;
+}
+
+/**
+ * Extract a plan for resatisfaction starting from the given source
+ * constraints, usually a set of input constraints. This method
+ * assumes that stay optimization is desired; the plan will contain
+ * only constraints whose output variables are not stay. Constraints
+ * that do no computation, such as stay and edit constraints, are
+ * not included in the plan.
+ * Details: The outputs of a constraint are marked when it is added
+ * to the plan under construction. A constraint may be appended to
+ * the plan when all its input variables are known. A variable is
+ * known if either a) the variable is marked (indicating that has
+ * been computed by a constraint appearing earlier in the plan), b)
+ * the variable is 'stay' (i.e. it is a constant at plan execution
+ * time), or c) the variable is not determined by any
+ * constraint. The last provision is for past states of history
+ * variables, which are not stay but which are also not computed by
+ * any constraint.
+ * Assume: sources are all satisfied.
+ */
+Planner.prototype.makePlan = function (sources) {
+ var mark = this.newMark();
+ var plan = new Plan();
+ var todo = sources;
+ while (todo.size() > 0) {
+ var c = todo.removeFirst();
+ if (c.output().mark != mark && c.inputsKnown(mark)) {
+ plan.addConstraint(c);
+ c.output().mark = mark;
+ this.addConstraintsConsumingTo(c.output(), todo);
+ }
+ }
+ return plan;
+}
+
+/**
+ * Extract a plan for resatisfying starting from the output of the
+ * given constraints, usually a set of input constraints.
+ */
+Planner.prototype.extractPlanFromConstraints = function (constraints) {
+ var sources = new OrderedCollection();
+ for (var i = 0; i < constraints.size(); i++) {
+ var c = constraints.at(i);
+ if (c.isInput() && c.isSatisfied())
+ // not in plan already and eligible for inclusion
+ sources.add(c);
+ }
+ return this.makePlan(sources);
+}
+
+/**
+ * Recompute the walkabout strengths and stay flags of all variables
+ * downstream of the given constraint and recompute the actual
+ * values of all variables whose stay flag is true. If a cycle is
+ * detected, remove the given constraint and answer
+ * false. Otherwise, answer true.
+ * Details: Cycles are detected when a marked variable is
+ * encountered downstream of the given constraint. The sender is
+ * assumed to have marked the inputs of the given constraint with
+ * the given mark. Thus, encountering a marked node downstream of
+ * the output constraint means that there is a path from the
+ * constraint's output to one of its inputs.
+ */
+Planner.prototype.addPropagate = function (c, mark) {
+ var todo = new OrderedCollection();
+ todo.add(c);
+ while (todo.size() > 0) {
+ var d = todo.removeFirst();
+ if (d.output().mark == mark) {
+ this.incrementalRemove(c);
+ return false;
+ }
+ d.recalculate();
+ this.addConstraintsConsumingTo(d.output(), todo);
+ }
+ return true;
+}
+
+
+/**
+ * Update the walkabout strengths and stay flags of all variables
+ * downstream of the given constraint. Answer a collection of
+ * unsatisfied constraints sorted in order of decreasing strength.
+ */
+Planner.prototype.removePropagateFrom = function (out) {
+ out.determinedBy = null;
+ out.walkStrength = Strength.WEAKEST;
+ out.stay = true;
+ var unsatisfied = new OrderedCollection();
+ var todo = new OrderedCollection();
+ todo.add(out);
+ while (todo.size() > 0) {
+ var v = todo.removeFirst();
+ for (var i = 0; i < v.constraints.size(); i++) {
+ var c = v.constraints.at(i);
+ if (!c.isSatisfied())
+ unsatisfied.add(c);
+ }
+ var determining = v.determinedBy;
+ for (var i = 0; i < v.constraints.size(); i++) {
+ var next = v.constraints.at(i);
+ if (next != determining && next.isSatisfied()) {
+ next.recalculate();
+ todo.add(next.output());
+ }
+ }
+ }
+ return unsatisfied;
+}
+
+Planner.prototype.addConstraintsConsumingTo = function (v, coll) {
+ var determining = v.determinedBy;
+ var cc = v.constraints;
+ for (var i = 0; i < cc.size(); i++) {
+ var c = cc.at(i);
+ if (c != determining && c.isSatisfied())
+ coll.add(c);
+ }
+}
+
+/* --- *
+ * P l a n
+ * --- */
+
+/**
+ * A Plan is an ordered list of constraints to be executed in sequence
+ * to resatisfy all currently satisfiable constraints in the face of
+ * one or more changing inputs.
+ */
+function Plan() {
+ this.v = new OrderedCollection();
+}
+
+Plan.prototype.addConstraint = function (c) {
+ this.v.add(c);
+}
+
+Plan.prototype.size = function () {
+ return this.v.size();
+}
+
+Plan.prototype.constraintAt = function (index) {
+ return this.v.at(index);
+}
+
+Plan.prototype.execute = function () {
+ for (var i = 0; i < this.size(); i++) {
+ var c = this.constraintAt(i);
+ c.execute();
+ }
+}
+
+/* --- *
+ * M a i n
+ * --- */
+
+/**
+ * This is the standard DeltaBlue benchmark. A long chain of equality
+ * constraints is constructed with a stay constraint on one end. An
+ * edit constraint is then added to the opposite end and the time is
+ * measured for adding and removing this constraint, and extracting
+ * and executing a constraint satisfaction plan. There are two cases.
+ * In case 1, the added constraint is stronger than the stay
+ * constraint and values must propagate down the entire length of the
+ * chain. In case 2, the added constraint is weaker than the stay
+ * constraint so it cannot be accomodated. The cost in this case is,
+ * of course, very low. Typical situations lie somewhere between these
+ * two extremes.
+ */
+function chainTest(n) {
+ planner = new Planner();
+ var prev = null, first = null, last = null;
+
+ // Build chain of n equality constraints
+ for (var i = 0; i <= n; i++) {
+ var name = "v" + i;
+ var v = new Variable(name);
+ if (prev != null)
+ new EqualityConstraint(prev, v, Strength.REQUIRED);
+ if (i == 0) first = v;
+ if (i == n) last = v;
+ prev = v;
+ }
+
+ new StayConstraint(last, Strength.STRONG_DEFAULT);
+ var edit = new EditConstraint(first, Strength.PREFERRED);
+ var edits = new OrderedCollection();
+ edits.add(edit);
+
+ return function() {
+ var plan = planner.extractPlanFromConstraints(edits);
+ for (var i = 0; i < 100; i++) {
+ first.value = i;
+ plan.execute();
+ if (last.value != i)
+ alert("Chain test failed.");
+ }
+ }
+}
+
+/**
+ * This test constructs a two sets of variables related to each
+ * other by a simple linear transformation (scale and offset). The
+ * time is measured to change a variable on either side of the
+ * mapping and to change the scale and offset factors.
+ */
+function projectionTest(n) {
+ planner = new Planner();
+ var scale = new Variable("scale", 10);
+ var offset = new Variable("offset", 1000);
+ var src = null, dst = null;
+
+ var dests = new OrderedCollection();
+ for (var i = 0; i < n; i++) {
+ src = new Variable("src" + i, i);
+ dst = new Variable("dst" + i, i);
+ dests.add(dst);
+ new StayConstraint(src, Strength.NORMAL);
+ new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED);
+ }
+
+ return function() {
+ change(src, 17);
+ if (dst.value != 1170) alert("Projection 1 failed");
+ change(dst, 1050);
+ if (src.value != 5) alert("Projection 2 failed");
+ change(scale, 5);
+ for (var i = 0; i < n - 1; i++) {
+ if (dests.at(i).value != i * 5 + 1000)
+ alert("Projection 3 failed");
+ }
+ change(offset, 2000);
+ for (var i = 0; i < n - 1; i++) {
+ if (dests.at(i).value != i * 5 + 2000)
+ alert("Projection 4 failed");
+ }
+ }
+}
+
+function change(v, newValue) {
+ var edit = new EditConstraint(v, Strength.PREFERRED);
+ var edits = new OrderedCollection();
+ edits.add(edit);
+ var plan = planner.extractPlanFromConstraints(edits);
+ for (var i = 0; i < 10; i++) {
+ v.value = newValue;
+ plan.execute();
+ }
+ edit.destroyConstraint();
+}
+
+// Global variable holding the current planner.
+var planner = null;
+
+function deltaBlue() {
+ chainTest(100)();
+ projectionTest(100)();
+}
diff --git a/tests/benchmarks/qml/deltablue/main.cpp b/tests/benchmarks/qml/deltablue/main.cpp
new file mode 100644
index 0000000000..727c740fb6
--- /dev/null
+++ b/tests/benchmarks/qml/deltablue/main.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtQml/qqmlcomponent.h>
+
+#include <QtTest/qbenchmark.h>
+#include <QtTest/qtest.h>
+#include <QtTest/qtestcase.h>
+
+class DeltaBlue : public QObject
+{
+ Q_OBJECT
+public:
+
+private slots:
+ void qml() {
+ QBENCHMARK {
+ QObject *instance = engine.singletonInstance<QObject *>(
+ "DeltaBlue", "Main");
+ QMetaObject::invokeMethod(instance, "deltaBlue");
+ engine.clearSingletons();
+ }
+ }
+
+ void nicer() {
+ QQmlComponent chain(&engine, "DeltaBlue", "ChainTest");
+ QQmlComponent projection(&engine, "DeltaBlue", "ProjectionTest");
+ QBENCHMARK {
+ QScopedPointer<QObject> chainTest(chain.create());
+ QMetaObject::invokeMethod(chainTest.data(), "run");
+
+ QScopedPointer<QObject> projectionTest(projection.create());
+ QMetaObject::invokeMethod(projectionTest.data(), "run");
+ }
+ }
+
+ void onlyCreate() {
+ QQmlComponent chain(&engine, "DeltaBlue", "ChainTest");
+ QQmlComponent projection(&engine, "DeltaBlue", "ProjectionTest");
+ QBENCHMARK {
+ QScopedPointer<QObject> chainTest(chain.create());
+ QScopedPointer<QObject> projectionTest(projection.create());
+ }
+ }
+
+ void js() {
+ QQmlComponent main2(&engine, "DeltaBlue", "Main2");
+ QBENCHMARK {
+ QScopedPointer<QObject> instance(main2.create());
+ QMetaObject::invokeMethod(instance.data(), "deltaBlue");
+ }
+ }
+
+ void jsOnlyCreate() {
+ QQmlComponent main2(&engine, "DeltaBlue", "Main2");
+ QBENCHMARK {
+ QScopedPointer<QObject> instance(main2.create());
+ }
+ }
+
+private:
+ QQmlApplicationEngine engine;
+};
+
+QTEST_MAIN(DeltaBlue)
+
+#include "main.moc"
diff --git a/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicFour.qml b/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicFour.qml
index 2533866000..73a21273b7 100644
--- a/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicFour.qml
+++ b/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicFour.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicOne.qml b/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicOne.qml
index ebf04a48f0..4deda51286 100644
--- a/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicOne.qml
+++ b/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicOne.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicThree.qml b/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicThree.qml
index 3f68170c8b..f8d4a5eba7 100644
--- a/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicThree.qml
+++ b/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicThree.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicTwo.qml b/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicTwo.qml
index 6b98271fca..c5376cb0a1 100644
--- a/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicTwo.qml
+++ b/tests/benchmarks/qml/holistic/data/dynamicTargets/DynamicTwo.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/Mlbsi.qml b/tests/benchmarks/qml/holistic/data/jsImports/Mlbsi.qml
index d33b0b6817..7ccd128775 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/Mlbsi.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/Mlbsi.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/Mldsi.qml b/tests/benchmarks/qml/holistic/data/jsImports/Mldsi.qml
index fad6c26efb..39ef2fef02 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/Mldsi.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/Mldsi.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/Mlsi.qml b/tests/benchmarks/qml/holistic/data/jsImports/Mlsi.qml
index 665a3e4ec1..8ab708e3f4 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/Mlsi.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/Mlsi.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/ModuleBm.qml b/tests/benchmarks/qml/holistic/data/jsImports/ModuleBm.qml
index 636e70f77b..0391417a06 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/ModuleBm.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/ModuleBm.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/Msbsi.qml b/tests/benchmarks/qml/holistic/data/jsImports/Msbsi.qml
index 84429ac023..06a8c2a533 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/Msbsi.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/Msbsi.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/Msdsi.qml b/tests/benchmarks/qml/holistic/data/jsImports/Msdsi.qml
index c590c69fdb..6cfe326186 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/Msdsi.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/Msdsi.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/Mssi.qml b/tests/benchmarks/qml/holistic/data/jsImports/Mssi.qml
index e95c047d09..04992de45d 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/Mssi.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/Mssi.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/PragmaBm.qml b/tests/benchmarks/qml/holistic/data/jsImports/PragmaBm.qml
index d3c2ff0bdb..cf769d5477 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/PragmaBm.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/PragmaBm.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/PragmaModuleBm.qml b/tests/benchmarks/qml/holistic/data/jsImports/PragmaModuleBm.qml
index f0389c173a..a2452ceff1 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/PragmaModuleBm.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/PragmaModuleBm.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/Slsi.qml b/tests/benchmarks/qml/holistic/data/jsImports/Slsi.qml
index 20451d8f07..0b4ac751bb 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/Slsi.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/Slsi.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/Sssi.qml b/tests/benchmarks/qml/holistic/data/jsImports/Sssi.qml
index b40ef5fa1b..3917ca5ae0 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/Sssi.qml
+++ b/tests/benchmarks/qml/holistic/data/jsImports/Sssi.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi.js
index c6f8931a98..cc34652e3e 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports many other (non-nested) single, small, scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi1.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi1.js
index a634eab272..9781994675 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi1.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi1.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi10.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi10.js
index 61c3b40a21..b686a69a14 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi10.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi10.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi11.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi11.js
index 0696e506b2..8bdc0e780a 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi11.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi11.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi12.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi12.js
index 05ef034d62..686a062918 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi12.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi12.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi13.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi13.js
index cc7f4e40c5..bd4f71bf48 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi13.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi13.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi14.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi14.js
index 6ed488e4e9..42e9f34316 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi14.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi14.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi15.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi15.js
index 59290b90a5..ab7a7aa9a6 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi15.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi15.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi2.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi2.js
index a669714ba8..b4e4c8b653 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi2.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi2.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi3.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi3.js
index 893439e76a..38dddd526d 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi3.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi3.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi4.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi4.js
index e2fcb46a9f..4b96cc940d 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi4.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi4.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi5.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi5.js
index 0e6f7b10f9..312984c068 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi5.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi5.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi6.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi6.js
index 8251734d82..65ca7fa5c0 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi6.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi6.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi7.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi7.js
index 489d78a66c..60cd1ce314 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi7.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi7.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi8.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi8.js
index 32ea7ff50b..d74e88932e 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi8.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi8.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi9.js b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi9.js
index d65718e701..cecd7caab0 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlbsi9.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlbsi9.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi.js
index 9462d8fa73..e7bfdbf08d 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi1.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi1.js
index 09802717f2..c8c65823dc 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi1.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi1.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi10.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi10.js
index 622dab5145..4607362b4a 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi10.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi10.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi11.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi11.js
index c669b5dc3d..872ada8515 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi11.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi11.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi12.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi12.js
index 2289f119aa..019279ab8f 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi12.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi12.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi13.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi13.js
index 11770051c0..7505800a55 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi13.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi13.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi14.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi14.js
index 716c63d616..0c5671a225 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi14.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi14.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi15.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi15.js
index 9fc586cffe..c400f79e02 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi15.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi15.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It is imported by another script.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi2.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi2.js
index dabbbba9c4..7a27657b75 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi2.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi2.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi3.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi3.js
index 202057cc11..04097d5493 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi3.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi3.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi4.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi4.js
index ccaed43d01..993ad3a05a 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi4.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi4.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi5.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi5.js
index eec6d03ad5..7e45c22527 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi5.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi5.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi6.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi6.js
index 97c4f8c1a9..5957709133 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi6.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi6.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi7.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi7.js
index 8541c2efa4..6520fdd4a9 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi7.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi7.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi8.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi8.js
index ed6fae6e0d..539de63d5f 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi8.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi8.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mldsi9.js b/tests/benchmarks/qml/holistic/data/jsImports/mldsi9.js
index 97b5f1cc21..952686cb46 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mldsi9.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mldsi9.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports other large scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mlsi.js b/tests/benchmarks/qml/holistic/data/jsImports/mlsi.js
index 8cf36563f7..b4a2c9f47b 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mlsi.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mlsi.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports many other (non-nested) single, large, scripts,
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/moduleBm.js b/tests/benchmarks/qml/holistic/data/jsImports/moduleBm.js
index 5d2c2ebf40..a43b708992 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/moduleBm.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/moduleBm.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi.js
index d968903291..c8f413c418 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports many other (non-nested) single, small, scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi1.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi1.js
index 01a1086ef7..faa743157a 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi1.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi1.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi10.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi10.js
index 05bdc4a0a5..9887e4f434 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi10.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi10.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi11.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi11.js
index a89c87eb41..fa5db379aa 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi11.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi11.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi12.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi12.js
index a9d4ea2dc2..35c0684002 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi12.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi12.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi13.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi13.js
index 331f437407..2b8b911919 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi13.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi13.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi14.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi14.js
index 74038d0ab6..e75c926609 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi14.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi14.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi15.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi15.js
index 32e61de5cd..3474fad080 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi15.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi15.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi2.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi2.js
index cfe165ea39..c893c580a5 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi2.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi2.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi3.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi3.js
index 27e58a414b..a4a51b0207 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi3.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi3.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi4.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi4.js
index 0d838f0e1e..4a020a8d06 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi4.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi4.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi5.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi5.js
index a2509d886c..b92b708816 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi5.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi5.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi6.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi6.js
index 82dbebc3e1..70bf447e38 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi6.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi6.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi7.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi7.js
index 6d272f5350..6fbd19c0ae 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi7.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi7.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi8.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi8.js
index d4b3ce2f53..4ef304e180 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi8.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi8.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msbsi9.js b/tests/benchmarks/qml/holistic/data/jsImports/msbsi9.js
index 1a86225412..e4a5dd5b79 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msbsi9.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msbsi9.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi.js
index dad4692f54..9fe68fc02a 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi1.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi1.js
index d2bee164e0..4907173f08 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi1.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi1.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi10.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi10.js
index e0e88a0083..d1b6b4c9d2 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi10.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi10.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi11.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi11.js
index 53a47fe77e..ef58f57e37 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi11.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi11.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi12.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi12.js
index 7a25c59440..d6c77bcdb4 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi12.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi12.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi13.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi13.js
index 19cf1ca3a7..a53dd48398 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi13.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi13.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi14.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi14.js
index 5dd1b872f3..86c5ffb35c 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi14.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi14.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi15.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi15.js
index 0180690d18..e7c874f235 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi15.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi15.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It is imported by another script.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi2.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi2.js
index a5902de42c..092b3be0b7 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi2.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi2.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi3.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi3.js
index 05c5951712..38475b26cd 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi3.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi3.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi4.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi4.js
index f0b8cd021e..1dc58313d5 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi4.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi4.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi5.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi5.js
index b4c873a9e9..427de1926d 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi5.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi5.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi6.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi6.js
index c4bfcc9e91..bb893148e0 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi6.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi6.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi7.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi7.js
index 64eabdafe7..f0d710996f 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi7.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi7.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi8.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi8.js
index 7bbdb3b589..1ccd698caf 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi8.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi8.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/msdsi9.js b/tests/benchmarks/qml/holistic/data/jsImports/msdsi9.js
index 954d753564..95712592e3 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/msdsi9.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/msdsi9.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports other small scripts which are deeply nested.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/mssi.js b/tests/benchmarks/qml/holistic/data/jsImports/mssi.js
index d5d12f82ff..6bfbe4e734 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/mssi.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/mssi.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It imports many other (non-nested) single, small, scripts,
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/pragmaBmOne.js b/tests/benchmarks/qml/holistic/data/jsImports/pragmaBmOne.js
index bbea449891..0c3fc1850e 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/pragmaBmOne.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/pragmaBmOne.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports a shared library script.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/pragmaBmTwo.js b/tests/benchmarks/qml/holistic/data/jsImports/pragmaBmTwo.js
index ad47e6fb4f..39437fe54d 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/pragmaBmTwo.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/pragmaBmTwo.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports a shared library script.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/pragmaLib.js b/tests/benchmarks/qml/holistic/data/jsImports/pragmaLib.js
index 571c2f77ef..d3e6353d30 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/pragmaLib.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/pragmaLib.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/pragmaModuleBm.js b/tests/benchmarks/qml/holistic/data/jsImports/pragmaModuleBm.js
index fcdb982742..9c8a447016 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/pragmaModuleBm.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/pragmaModuleBm.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It imports a QML module and two other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/slsi.js b/tests/benchmarks/qml/holistic/data/jsImports/slsi.js
index 372f82e764..43f8f114b5 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/slsi.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/slsi.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, large, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsImports/sssi.js b/tests/benchmarks/qml/holistic/data/jsImports/sssi.js
index 861a3ca607..1dc74c9273 100644
--- a/tests/benchmarks/qml/holistic/data/jsImports/sssi.js
+++ b/tests/benchmarks/qml/holistic/data/jsImports/sssi.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This JavaScript file is a single, small, imported script.
// It doesn't import any other scripts.
diff --git a/tests/benchmarks/qml/holistic/data/jsTargets/JsOne.qml b/tests/benchmarks/qml/holistic/data/jsTargets/JsOne.qml
index c21cae4381..a451e9c615 100644
--- a/tests/benchmarks/qml/holistic/data/jsTargets/JsOne.qml
+++ b/tests/benchmarks/qml/holistic/data/jsTargets/JsOne.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/jsTargets/JsTwo.qml b/tests/benchmarks/qml/holistic/data/jsTargets/JsTwo.qml
index 29c8c97296..d4b5cc1578 100644
--- a/tests/benchmarks/qml/holistic/data/jsTargets/JsTwo.qml
+++ b/tests/benchmarks/qml/holistic/data/jsTargets/JsTwo.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/largeTargets/gridview-example.qml b/tests/benchmarks/qml/holistic/data/largeTargets/gridview-example.qml
index afbcfec50c..d8f0d9dcb5 100644
--- a/tests/benchmarks/qml/holistic/data/largeTargets/gridview-example.qml
+++ b/tests/benchmarks/qml/holistic/data/largeTargets/gridview-example.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/largeTargets/layoutdirection.qml b/tests/benchmarks/qml/holistic/data/largeTargets/layoutdirection.qml
index a06dc69e07..647711afe3 100644
--- a/tests/benchmarks/qml/holistic/data/largeTargets/layoutdirection.qml
+++ b/tests/benchmarks/qml/holistic/data/largeTargets/layoutdirection.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/largeTargets/mousearea-example.qml b/tests/benchmarks/qml/holistic/data/largeTargets/mousearea-example.qml
index ec8b9106ef..da20d5fd42 100644
--- a/tests/benchmarks/qml/holistic/data/largeTargets/mousearea-example.qml
+++ b/tests/benchmarks/qml/holistic/data/largeTargets/mousearea-example.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/resolutionTargets/ResolveOne.qml b/tests/benchmarks/qml/holistic/data/resolutionTargets/ResolveOne.qml
index 02ce89be67..76576afc72 100644
--- a/tests/benchmarks/qml/holistic/data/resolutionTargets/ResolveOne.qml
+++ b/tests/benchmarks/qml/holistic/data/resolutionTargets/ResolveOne.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This file has some nested items and does a lot of id resolution.
// This will allow us to benchmark the cost of resolving names in
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/CppToJs.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/CppToJs.qml
index 7874864338..9e8435959c 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/CppToJs.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/CppToJs.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import "cppToJs.js" as CppToJs
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/CppToQml.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/CppToQml.qml
index f7f87c9b24..dcc923c6fa 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/CppToQml.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/CppToQml.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEight.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEight.qml
index 0b76ecbb88..e86e4de581 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEight.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEight.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEleven.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEleven.qml
index 5bf8d0d185..6977f09101 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEleven.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppEleven.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFive.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFive.qml
index 119d670fba..ec854d1cb0 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFive.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFive.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFour.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFour.qml
index 52a00aee57..b48677fad6 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFour.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppFour.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppNine.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppNine.qml
index 45157f264e..18117f2a83 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppNine.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppNine.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppOne.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppOne.qml
index c969620e63..43abee9c5f 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppOne.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppOne.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSeven.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSeven.qml
index c6701778b8..3bb67b5cea 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSeven.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSeven.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSix.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSix.qml
index 4665091949..2c34038853 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSix.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppSix.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTen.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTen.qml
index c5cd0c9c6d..2267807346 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTen.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTen.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppThree.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppThree.qml
index 0730db8e7f..47c1418c9f 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppThree.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppThree.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTwo.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTwo.qml
index 0c8c724962..6584ab9e19 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTwo.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/JsToCppTwo.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceOne.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceOne.qml
index 3fd0c73699..32cef038f0 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceOne.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceOne.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceTwo.qml b/tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceTwo.qml
index 0de203ee94..cb63de929b 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceTwo.qml
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/ScarceTwo.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/holistic/data/scopeSwitching/cppToJs.js b/tests/benchmarks/qml/holistic/data/scopeSwitching/cppToJs.js
index ed24310da0..344f92f275 100644
--- a/tests/benchmarks/qml/holistic/data/scopeSwitching/cppToJs.js
+++ b/tests/benchmarks/qml/holistic/data/scopeSwitching/cppToJs.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
.pragma library
diff --git a/tests/benchmarks/qml/holistic/data/smallTargets/SmallFour.qml b/tests/benchmarks/qml/holistic/data/smallTargets/SmallFour.qml
index bb59b39450..9561ccc34a 100644
--- a/tests/benchmarks/qml/holistic/data/smallTargets/SmallFour.qml
+++ b/tests/benchmarks/qml/holistic/data/smallTargets/SmallFour.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/smallTargets/SmallOne.qml b/tests/benchmarks/qml/holistic/data/smallTargets/SmallOne.qml
index 6fbb5e0a6e..c1af1e6ea2 100644
--- a/tests/benchmarks/qml/holistic/data/smallTargets/SmallOne.qml
+++ b/tests/benchmarks/qml/holistic/data/smallTargets/SmallOne.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/smallTargets/SmallThree.qml b/tests/benchmarks/qml/holistic/data/smallTargets/SmallThree.qml
index 205ee789e9..bac55b17ea 100644
--- a/tests/benchmarks/qml/holistic/data/smallTargets/SmallThree.qml
+++ b/tests/benchmarks/qml/holistic/data/smallTargets/SmallThree.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/data/smallTargets/SmallTwo.qml b/tests/benchmarks/qml/holistic/data/smallTargets/SmallTwo.qml
index 2fe41ee4a2..4f6b177c8e 100644
--- a/tests/benchmarks/qml/holistic/data/smallTargets/SmallTwo.qml
+++ b/tests/benchmarks/qml/holistic/data/smallTargets/SmallTwo.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/holistic/testtypes.cpp b/tests/benchmarks/qml/holistic/testtypes.cpp
index e39543630e..b4e0227fda 100644
--- a/tests/benchmarks/qml/holistic/testtypes.cpp
+++ b/tests/benchmarks/qml/holistic/testtypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
#include <QQmlEngine>
diff --git a/tests/benchmarks/qml/holistic/testtypes.h b/tests/benchmarks/qml/holistic/testtypes.h
index 52f505eb78..bd1d68993e 100644
--- a/tests/benchmarks/qml/holistic/testtypes.h
+++ b/tests/benchmarks/qml/holistic/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
diff --git a/tests/benchmarks/qml/holistic/tst_holistic.cpp b/tests/benchmarks/qml/holistic/tst_holistic.cpp
index 52a798fc51..27fc90136f 100644
--- a/tests/benchmarks/qml/holistic/tst_holistic.cpp
+++ b/tests/benchmarks/qml/holistic/tst_holistic.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
diff --git a/tests/benchmarks/qml/javascript/testtypes.cpp b/tests/benchmarks/qml/javascript/testtypes.cpp
index ab7b7f6a9f..64d8b1ec62 100644
--- a/tests/benchmarks/qml/javascript/testtypes.cpp
+++ b/tests/benchmarks/qml/javascript/testtypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
#include <QtQml/qqml.h>
diff --git a/tests/benchmarks/qml/javascript/testtypes.h b/tests/benchmarks/qml/javascript/testtypes.h
index d73e85ed4a..2508fa92fb 100644
--- a/tests/benchmarks/qml/javascript/testtypes.h
+++ b/tests/benchmarks/qml/javascript/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
diff --git a/tests/benchmarks/qml/javascript/tst_javascript.cpp b/tests/benchmarks/qml/javascript/tst_javascript.cpp
index 6970e57fa7..50e4a32e6e 100644
--- a/tests/benchmarks/qml/javascript/tst_javascript.cpp
+++ b/tests/benchmarks/qml/javascript/tst_javascript.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDirIterator>
#include <QDebug>
diff --git a/tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp b/tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp
index f9f24fdbaf..e82117c0cb 100644
--- a/tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp
+++ b/tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qjsvalue.h>
diff --git a/tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp b/tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp
index 20094ae728..3a360ce0c8 100644
--- a/tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/benchmarks/qml/js/qjsvalue/tst_qjsvalue.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qjsvalue.h>
@@ -78,8 +78,10 @@ private slots:
void isString();
void isUndefined_data();
void isUndefined();
+#if QT_DEPRECATED_SINCE(6, 9)
void isVariant_data();
void isVariant();
+#endif
void toBool_data();
void toBool();
void toDateTime_data();
@@ -547,6 +549,7 @@ void tst_QJSValue::isUndefined()
}
}
+#if QT_DEPRECATED_SINCE(6, 9)
void tst_QJSValue::isVariant_data()
{
defineStandardTestValues();
@@ -559,6 +562,7 @@ void tst_QJSValue::isVariant()
val.isVariant();
}
}
+#endif
void tst_QJSValue::toBool_data()
{
diff --git a/tests/benchmarks/qml/js/qjsvalueiterator/tst_qjsvalueiterator.cpp b/tests/benchmarks/qml/js/qjsvalueiterator/tst_qjsvalueiterator.cpp
index ec652c19a3..6d8191cb99 100644
--- a/tests/benchmarks/qml/js/qjsvalueiterator/tst_qjsvalueiterator.cpp
+++ b/tests/benchmarks/qml/js/qjsvalueiterator/tst_qjsvalueiterator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQml/qjsengine.h>
diff --git a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
index f64a44d66a..918fb37d67 100644
--- a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
+++ b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <qqml.h>
diff --git a/tests/benchmarks/qml/painting/paintbenchmark.cpp b/tests/benchmarks/qml/painting/paintbenchmark.cpp
index ab40d35304..b1fde76324 100644
--- a/tests/benchmarks/qml/painting/paintbenchmark.cpp
+++ b/tests/benchmarks/qml/painting/paintbenchmark.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QPixmap>
diff --git a/tests/benchmarks/qml/qmldom/CMakeLists.txt b/tests/benchmarks/qml/qmldom/CMakeLists.txt
new file mode 100644
index 0000000000..7610d2c772
--- /dev/null
+++ b/tests/benchmarks/qml/qmldom/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qmldomConstruction Test:
+#####################################################################
+
+qt_internal_add_benchmark(tst_qmldomconstruction
+ SOURCES
+ tst_qmldomconstruction.cpp
+ DEFINES
+ SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}"
+ LIBRARIES
+ Qt::QmlDomPrivate
+ Qt::Test
+)
diff --git a/tests/benchmarks/qml/qmldom/data/deeplyNested.qml b/tests/benchmarks/qml/qmldom/data/deeplyNested.qml
new file mode 100644
index 0000000000..712763a0c0
--- /dev/null
+++ b/tests/benchmarks/qml/qmldom/data/deeplyNested.qml
@@ -0,0 +1,2995 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int myProperty1
+ property int mySum: parent.myProperty0 + parent.myProperty0 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty2
+ property int mySum: parent.myProperty1 + parent.myProperty1 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty3
+ property int mySum: parent.myProperty2 + parent.myProperty2 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty4
+ property int mySum: parent.myProperty3 + parent.myProperty3 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty5
+ property int mySum: parent.myProperty4 + parent.myProperty4 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty6
+ property int mySum: parent.myProperty5 + parent.myProperty5 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty7
+ property int mySum: parent.myProperty6 + parent.myProperty6 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty8
+ property int mySum: parent.myProperty7 + parent.myProperty7 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty9
+ property int mySum: parent.myProperty8 + parent.myProperty8 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty10
+ property int mySum: parent.myProperty9 + parent.myProperty9 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty11
+ property int mySum: parent.myProperty10 + parent.myProperty10 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty12
+ property int mySum: parent.myProperty11 + parent.myProperty11 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty13
+ property int mySum: parent.myProperty12 + parent.myProperty12 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty14
+ property int mySum: parent.myProperty13 + parent.myProperty13 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty15
+ property int mySum: parent.myProperty14 + parent.myProperty14 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty16
+ property int mySum: parent.myProperty15 + parent.myProperty15 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty17
+ property int mySum: parent.myProperty16 + parent.myProperty16 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty18
+ property int mySum: parent.myProperty17 + parent.myProperty17 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty19
+ property int mySum: parent.myProperty18 + parent.myProperty18 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty20
+ property int mySum: parent.myProperty19 + parent.myProperty19 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty21
+ property int mySum: parent.myProperty20 + parent.myProperty20 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty22
+ property int mySum: parent.myProperty21 + parent.myProperty21 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty23
+ property int mySum: parent.myProperty22 + parent.myProperty22 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty24
+ property int mySum: parent.myProperty23 + parent.myProperty23 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty25
+ property int mySum: parent.myProperty24 + parent.myProperty24 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty26
+ property int mySum: parent.myProperty25 + parent.myProperty25 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty27
+ property int mySum: parent.myProperty26 + parent.myProperty26 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty28
+ property int mySum: parent.myProperty27 + parent.myProperty27 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty29
+ property int mySum: parent.myProperty28 + parent.myProperty28 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty30
+ property int mySum: parent.myProperty29 + parent.myProperty29 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty31
+ property int mySum: parent.myProperty30 + parent.myProperty30 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty32
+ property int mySum: parent.myProperty31 + parent.myProperty31 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty33
+ property int mySum: parent.myProperty32 + parent.myProperty32 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty34
+ property int mySum: parent.myProperty33 + parent.myProperty33 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty35
+ property int mySum: parent.myProperty34 + parent.myProperty34 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty36
+ property int mySum: parent.myProperty35 + parent.myProperty35 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty37
+ property int mySum: parent.myProperty36 + parent.myProperty36 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty38
+ property int mySum: parent.myProperty37 + parent.myProperty37 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty39
+ property int mySum: parent.myProperty38 + parent.myProperty38 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty40
+ property int mySum: parent.myProperty39 + parent.myProperty39 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty41
+ property int mySum: parent.myProperty40 + parent.myProperty40 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty42
+ property int mySum: parent.myProperty41 + parent.myProperty41 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty43
+ property int mySum: parent.myProperty42 + parent.myProperty42 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty44
+ property int mySum: parent.myProperty43 + parent.myProperty43 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty45
+ property int mySum: parent.myProperty44 + parent.myProperty44 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty46
+ property int mySum: parent.myProperty45 + parent.myProperty45 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty47
+ property int mySum: parent.myProperty46 + parent.myProperty46 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty48
+ property int mySum: parent.myProperty47 + parent.myProperty47 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty49
+ property int mySum: parent.myProperty48 + parent.myProperty48 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty50
+ property int mySum: parent.myProperty49 + parent.myProperty49 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty51
+ property int mySum: parent.myProperty50 + parent.myProperty50 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty52
+ property int mySum: parent.myProperty51 + parent.myProperty51 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty53
+ property int mySum: parent.myProperty52 + parent.myProperty52 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty54
+ property int mySum: parent.myProperty53 + parent.myProperty53 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty55
+ property int mySum: parent.myProperty54 + parent.myProperty54 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty56
+ property int mySum: parent.myProperty55 + parent.myProperty55 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty57
+ property int mySum: parent.myProperty56 + parent.myProperty56 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty58
+ property int mySum: parent.myProperty57 + parent.myProperty57 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty59
+ property int mySum: parent.myProperty58 + parent.myProperty58 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty60
+ property int mySum: parent.myProperty59 + parent.myProperty59 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty61
+ property int mySum: parent.myProperty60 + parent.myProperty60 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty62
+ property int mySum: parent.myProperty61 + parent.myProperty61 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty63
+ property int mySum: parent.myProperty62 + parent.myProperty62 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty64
+ property int mySum: parent.myProperty63 + parent.myProperty63 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty65
+ property int mySum: parent.myProperty64 + parent.myProperty64 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty66
+ property int mySum: parent.myProperty65 + parent.myProperty65 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty67
+ property int mySum: parent.myProperty66 + parent.myProperty66 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty68
+ property int mySum: parent.myProperty67 + parent.myProperty67 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty69
+ property int mySum: parent.myProperty68 + parent.myProperty68 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty70
+ property int mySum: parent.myProperty69 + parent.myProperty69 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty71
+ property int mySum: parent.myProperty70 + parent.myProperty70 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty72
+ property int mySum: parent.myProperty71 + parent.myProperty71 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty73
+ property int mySum: parent.myProperty72 + parent.myProperty72 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty74
+ property int mySum: parent.myProperty73 + parent.myProperty73 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty75
+ property int mySum: parent.myProperty74 + parent.myProperty74 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty76
+ property int mySum: parent.myProperty75 + parent.myProperty75 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty77
+ property int mySum: parent.myProperty76 + parent.myProperty76 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty78
+ property int mySum: parent.myProperty77 + parent.myProperty77 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty79
+ property int mySum: parent.myProperty78 + parent.myProperty78 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty80
+ property int mySum: parent.myProperty79 + parent.myProperty79 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty81
+ property int mySum: parent.myProperty80 + parent.myProperty80 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty82
+ property int mySum: parent.myProperty81 + parent.myProperty81 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty83
+ property int mySum: parent.myProperty82 + parent.myProperty82 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty84
+ property int mySum: parent.myProperty83 + parent.myProperty83 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty85
+ property int mySum: parent.myProperty84 + parent.myProperty84 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty86
+ property int mySum: parent.myProperty85 + parent.myProperty85 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty87
+ property int mySum: parent.myProperty86 + parent.myProperty86 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty88
+ property int mySum: parent.myProperty87 + parent.myProperty87 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty89
+ property int mySum: parent.myProperty88 + parent.myProperty88 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty90
+ property int mySum: parent.myProperty89 + parent.myProperty89 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty91
+ property int mySum: parent.myProperty90 + parent.myProperty90 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty92
+ property int mySum: parent.myProperty91 + parent.myProperty91 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty93
+ property int mySum: parent.myProperty92 + parent.myProperty92 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty94
+ property int mySum: parent.myProperty93 + parent.myProperty93 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty95
+ property int mySum: parent.myProperty94 + parent.myProperty94 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty96
+ property int mySum: parent.myProperty95 + parent.myProperty95 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty97
+ property int mySum: parent.myProperty96 + parent.myProperty96 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty98
+ property int mySum: parent.myProperty97 + parent.myProperty97 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty99
+ property int mySum: parent.myProperty98 + parent.myProperty98 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty100
+ property int mySum: parent.myProperty99 + parent.myProperty99 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty101
+ property int mySum: parent.myProperty100 + parent.myProperty100 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty102
+ property int mySum: parent.myProperty101 + parent.myProperty101 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty103
+ property int mySum: parent.myProperty102 + parent.myProperty102 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty104
+ property int mySum: parent.myProperty103 + parent.myProperty103 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty105
+ property int mySum: parent.myProperty104 + parent.myProperty104 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty106
+ property int mySum: parent.myProperty105 + parent.myProperty105 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty107
+ property int mySum: parent.myProperty106 + parent.myProperty106 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty108
+ property int mySum: parent.myProperty107 + parent.myProperty107 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty109
+ property int mySum: parent.myProperty108 + parent.myProperty108 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty110
+ property int mySum: parent.myProperty109 + parent.myProperty109 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty111
+ property int mySum: parent.myProperty110 + parent.myProperty110 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty112
+ property int mySum: parent.myProperty111 + parent.myProperty111 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty113
+ property int mySum: parent.myProperty112 + parent.myProperty112 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty114
+ property int mySum: parent.myProperty113 + parent.myProperty113 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty115
+ property int mySum: parent.myProperty114 + parent.myProperty114 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty116
+ property int mySum: parent.myProperty115 + parent.myProperty115 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty117
+ property int mySum: parent.myProperty116 + parent.myProperty116 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty118
+ property int mySum: parent.myProperty117 + parent.myProperty117 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty119
+ property int mySum: parent.myProperty118 + parent.myProperty118 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty120
+ property int mySum: parent.myProperty119 + parent.myProperty119 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty121
+ property int mySum: parent.myProperty120 + parent.myProperty120 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty122
+ property int mySum: parent.myProperty121 + parent.myProperty121 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty123
+ property int mySum: parent.myProperty122 + parent.myProperty122 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty124
+ property int mySum: parent.myProperty123 + parent.myProperty123 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty125
+ property int mySum: parent.myProperty124 + parent.myProperty124 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty126
+ property int mySum: parent.myProperty125 + parent.myProperty125 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty127
+ property int mySum: parent.myProperty126 + parent.myProperty126 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty128
+ property int mySum: parent.myProperty127 + parent.myProperty127 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty129
+ property int mySum: parent.myProperty128 + parent.myProperty128 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty130
+ property int mySum: parent.myProperty129 + parent.myProperty129 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty131
+ property int mySum: parent.myProperty130 + parent.myProperty130 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty132
+ property int mySum: parent.myProperty131 + parent.myProperty131 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty133
+ property int mySum: parent.myProperty132 + parent.myProperty132 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty134
+ property int mySum: parent.myProperty133 + parent.myProperty133 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty135
+ property int mySum: parent.myProperty134 + parent.myProperty134 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty136
+ property int mySum: parent.myProperty135 + parent.myProperty135 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty137
+ property int mySum: parent.myProperty136 + parent.myProperty136 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty138
+ property int mySum: parent.myProperty137 + parent.myProperty137 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty139
+ property int mySum: parent.myProperty138 + parent.myProperty138 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty140
+ property int mySum: parent.myProperty139 + parent.myProperty139 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty141
+ property int mySum: parent.myProperty140 + parent.myProperty140 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty142
+ property int mySum: parent.myProperty141 + parent.myProperty141 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty143
+ property int mySum: parent.myProperty142 + parent.myProperty142 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty144
+ property int mySum: parent.myProperty143 + parent.myProperty143 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty145
+ property int mySum: parent.myProperty144 + parent.myProperty144 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty146
+ property int mySum: parent.myProperty145 + parent.myProperty145 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty147
+ property int mySum: parent.myProperty146 + parent.myProperty146 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty148
+ property int mySum: parent.myProperty147 + parent.myProperty147 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty149
+ property int mySum: parent.myProperty148 + parent.myProperty148 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty150
+ property int mySum: parent.myProperty149 + parent.myProperty149 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty151
+ property int mySum: parent.myProperty150 + parent.myProperty150 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty152
+ property int mySum: parent.myProperty151 + parent.myProperty151 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty153
+ property int mySum: parent.myProperty152 + parent.myProperty152 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty154
+ property int mySum: parent.myProperty153 + parent.myProperty153 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty155
+ property int mySum: parent.myProperty154 + parent.myProperty154 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty156
+ property int mySum: parent.myProperty155 + parent.myProperty155 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty157
+ property int mySum: parent.myProperty156 + parent.myProperty156 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty158
+ property int mySum: parent.myProperty157 + parent.myProperty157 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty159
+ property int mySum: parent.myProperty158 + parent.myProperty158 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty160
+ property int mySum: parent.myProperty159 + parent.myProperty159 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty161
+ property int mySum: parent.myProperty160 + parent.myProperty160 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty162
+ property int mySum: parent.myProperty161 + parent.myProperty161 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty163
+ property int mySum: parent.myProperty162 + parent.myProperty162 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty164
+ property int mySum: parent.myProperty163 + parent.myProperty163 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty165
+ property int mySum: parent.myProperty164 + parent.myProperty164 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty166
+ property int mySum: parent.myProperty165 + parent.myProperty165 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty167
+ property int mySum: parent.myProperty166 + parent.myProperty166 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty168
+ property int mySum: parent.myProperty167 + parent.myProperty167 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty169
+ property int mySum: parent.myProperty168 + parent.myProperty168 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty170
+ property int mySum: parent.myProperty169 + parent.myProperty169 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty171
+ property int mySum: parent.myProperty170 + parent.myProperty170 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty172
+ property int mySum: parent.myProperty171 + parent.myProperty171 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty173
+ property int mySum: parent.myProperty172 + parent.myProperty172 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty174
+ property int mySum: parent.myProperty173 + parent.myProperty173 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty175
+ property int mySum: parent.myProperty174 + parent.myProperty174 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty176
+ property int mySum: parent.myProperty175 + parent.myProperty175 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty177
+ property int mySum: parent.myProperty176 + parent.myProperty176 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty178
+ property int mySum: parent.myProperty177 + parent.myProperty177 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty179
+ property int mySum: parent.myProperty178 + parent.myProperty178 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty180
+ property int mySum: parent.myProperty179 + parent.myProperty179 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty181
+ property int mySum: parent.myProperty180 + parent.myProperty180 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty182
+ property int mySum: parent.myProperty181 + parent.myProperty181 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty183
+ property int mySum: parent.myProperty182 + parent.myProperty182 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty184
+ property int mySum: parent.myProperty183 + parent.myProperty183 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty185
+ property int mySum: parent.myProperty184 + parent.myProperty184 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty186
+ property int mySum: parent.myProperty185 + parent.myProperty185 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty187
+ property int mySum: parent.myProperty186 + parent.myProperty186 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty188
+ property int mySum: parent.myProperty187 + parent.myProperty187 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty189
+ property int mySum: parent.myProperty188 + parent.myProperty188 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty190
+ property int mySum: parent.myProperty189 + parent.myProperty189 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty191
+ property int mySum: parent.myProperty190 + parent.myProperty190 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty192
+ property int mySum: parent.myProperty191 + parent.myProperty191 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty193
+ property int mySum: parent.myProperty192 + parent.myProperty192 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty194
+ property int mySum: parent.myProperty193 + parent.myProperty193 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty195
+ property int mySum: parent.myProperty194 + parent.myProperty194 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty196
+ property int mySum: parent.myProperty195 + parent.myProperty195 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty197
+ property int mySum: parent.myProperty196 + parent.myProperty196 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty198
+ property int mySum: parent.myProperty197 + parent.myProperty197 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty199
+ property int mySum: parent.myProperty198 + parent.myProperty198 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty200
+ property int mySum: parent.myProperty199 + parent.myProperty199 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty201
+ property int mySum: parent.myProperty200 + parent.myProperty200 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty202
+ property int mySum: parent.myProperty201 + parent.myProperty201 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty203
+ property int mySum: parent.myProperty202 + parent.myProperty202 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty204
+ property int mySum: parent.myProperty203 + parent.myProperty203 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty205
+ property int mySum: parent.myProperty204 + parent.myProperty204 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty206
+ property int mySum: parent.myProperty205 + parent.myProperty205 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty207
+ property int mySum: parent.myProperty206 + parent.myProperty206 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty208
+ property int mySum: parent.myProperty207 + parent.myProperty207 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty209
+ property int mySum: parent.myProperty208 + parent.myProperty208 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty210
+ property int mySum: parent.myProperty209 + parent.myProperty209 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty211
+ property int mySum: parent.myProperty210 + parent.myProperty210 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty212
+ property int mySum: parent.myProperty211 + parent.myProperty211 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty213
+ property int mySum: parent.myProperty212 + parent.myProperty212 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty214
+ property int mySum: parent.myProperty213 + parent.myProperty213 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty215
+ property int mySum: parent.myProperty214 + parent.myProperty214 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty216
+ property int mySum: parent.myProperty215 + parent.myProperty215 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty217
+ property int mySum: parent.myProperty216 + parent.myProperty216 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty218
+ property int mySum: parent.myProperty217 + parent.myProperty217 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty219
+ property int mySum: parent.myProperty218 + parent.myProperty218 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty220
+ property int mySum: parent.myProperty219 + parent.myProperty219 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty221
+ property int mySum: parent.myProperty220 + parent.myProperty220 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty222
+ property int mySum: parent.myProperty221 + parent.myProperty221 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty223
+ property int mySum: parent.myProperty222 + parent.myProperty222 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty224
+ property int mySum: parent.myProperty223 + parent.myProperty223 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty225
+ property int mySum: parent.myProperty224 + parent.myProperty224 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty226
+ property int mySum: parent.myProperty225 + parent.myProperty225 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty227
+ property int mySum: parent.myProperty226 + parent.myProperty226 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty228
+ property int mySum: parent.myProperty227 + parent.myProperty227 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty229
+ property int mySum: parent.myProperty228 + parent.myProperty228 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty230
+ property int mySum: parent.myProperty229 + parent.myProperty229 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty231
+ property int mySum: parent.myProperty230 + parent.myProperty230 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty232
+ property int mySum: parent.myProperty231 + parent.myProperty231 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty233
+ property int mySum: parent.myProperty232 + parent.myProperty232 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty234
+ property int mySum: parent.myProperty233 + parent.myProperty233 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty235
+ property int mySum: parent.myProperty234 + parent.myProperty234 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty236
+ property int mySum: parent.myProperty235 + parent.myProperty235 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty237
+ property int mySum: parent.myProperty236 + parent.myProperty236 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty238
+ property int mySum: parent.myProperty237 + parent.myProperty237 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty239
+ property int mySum: parent.myProperty238 + parent.myProperty238 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty240
+ property int mySum: parent.myProperty239 + parent.myProperty239 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty241
+ property int mySum: parent.myProperty240 + parent.myProperty240 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty242
+ property int mySum: parent.myProperty241 + parent.myProperty241 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty243
+ property int mySum: parent.myProperty242 + parent.myProperty242 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty244
+ property int mySum: parent.myProperty243 + parent.myProperty243 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty245
+ property int mySum: parent.myProperty244 + parent.myProperty244 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty246
+ property int mySum: parent.myProperty245 + parent.myProperty245 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty247
+ property int mySum: parent.myProperty246 + parent.myProperty246 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty248
+ property int mySum: parent.myProperty247 + parent.myProperty247 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty249
+ property int mySum: parent.myProperty248 + parent.myProperty248 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty250
+ property int mySum: parent.myProperty249 + parent.myProperty249 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty251
+ property int mySum: parent.myProperty250 + parent.myProperty250 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty252
+ property int mySum: parent.myProperty251 + parent.myProperty251 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty253
+ property int mySum: parent.myProperty252 + parent.myProperty252 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty254
+ property int mySum: parent.myProperty253 + parent.myProperty253 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty255
+ property int mySum: parent.myProperty254 + parent.myProperty254 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty256
+ property int mySum: parent.myProperty255 + parent.myProperty255 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty257
+ property int mySum: parent.myProperty256 + parent.myProperty256 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty258
+ property int mySum: parent.myProperty257 + parent.myProperty257 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty259
+ property int mySum: parent.myProperty258 + parent.myProperty258 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty260
+ property int mySum: parent.myProperty259 + parent.myProperty259 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty261
+ property int mySum: parent.myProperty260 + parent.myProperty260 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty262
+ property int mySum: parent.myProperty261 + parent.myProperty261 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty263
+ property int mySum: parent.myProperty262 + parent.myProperty262 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty264
+ property int mySum: parent.myProperty263 + parent.myProperty263 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty265
+ property int mySum: parent.myProperty264 + parent.myProperty264 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty266
+ property int mySum: parent.myProperty265 + parent.myProperty265 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty267
+ property int mySum: parent.myProperty266 + parent.myProperty266 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty268
+ property int mySum: parent.myProperty267 + parent.myProperty267 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty269
+ property int mySum: parent.myProperty268 + parent.myProperty268 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty270
+ property int mySum: parent.myProperty269 + parent.myProperty269 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty271
+ property int mySum: parent.myProperty270 + parent.myProperty270 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty272
+ property int mySum: parent.myProperty271 + parent.myProperty271 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty273
+ property int mySum: parent.myProperty272 + parent.myProperty272 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty274
+ property int mySum: parent.myProperty273 + parent.myProperty273 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty275
+ property int mySum: parent.myProperty274 + parent.myProperty274 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty276
+ property int mySum: parent.myProperty275 + parent.myProperty275 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty277
+ property int mySum: parent.myProperty276 + parent.myProperty276 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty278
+ property int mySum: parent.myProperty277 + parent.myProperty277 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty279
+ property int mySum: parent.myProperty278 + parent.myProperty278 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty280
+ property int mySum: parent.myProperty279 + parent.myProperty279 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty281
+ property int mySum: parent.myProperty280 + parent.myProperty280 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty282
+ property int mySum: parent.myProperty281 + parent.myProperty281 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty283
+ property int mySum: parent.myProperty282 + parent.myProperty282 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty284
+ property int mySum: parent.myProperty283 + parent.myProperty283 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty285
+ property int mySum: parent.myProperty284 + parent.myProperty284 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty286
+ property int mySum: parent.myProperty285 + parent.myProperty285 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty287
+ property int mySum: parent.myProperty286 + parent.myProperty286 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty288
+ property int mySum: parent.myProperty287 + parent.myProperty287 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty289
+ property int mySum: parent.myProperty288 + parent.myProperty288 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty290
+ property int mySum: parent.myProperty289 + parent.myProperty289 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty291
+ property int mySum: parent.myProperty290 + parent.myProperty290 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty292
+ property int mySum: parent.myProperty291 + parent.myProperty291 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty293
+ property int mySum: parent.myProperty292 + parent.myProperty292 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty294
+ property int mySum: parent.myProperty293 + parent.myProperty293 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty295
+ property int mySum: parent.myProperty294 + parent.myProperty294 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty296
+ property int mySum: parent.myProperty295 + parent.myProperty295 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty297
+ property int mySum: parent.myProperty296 + parent.myProperty296 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty298
+ property int mySum: parent.myProperty297 + parent.myProperty297 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ Item {
+ property int myProperty299
+ property int mySum: parent.myProperty298 + parent.myProperty298 + 1
+ function f (a,b,c,d,e,f) {
+ return e + f
+ }
+ signal mySignal()
+ enum MyEnum { A, B, C, D, E, F }
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+}
+
diff --git a/tests/benchmarks/qml/qmldom/data/longQmlFile.qml b/tests/benchmarks/qml/qmldom/data/longQmlFile.qml
new file mode 100644
index 0000000000..2322877607
--- /dev/null
+++ b/tests/benchmarks/qml/qmldom/data/longQmlFile.qml
@@ -0,0 +1,3667 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Shape {
+ asynchronous: true
+ width: 494; height: 510
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -122.304; y: 84.285 }
+ PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 }
+ PathCubic { control1X: -123.851; control1Y: 86.141; control2X: -140.305; control2Y: 38.066; x: -160.833; y: 40.309 }
+ PathCubic { control1X: -160.833; control1Y: 40.309; control2X: -143.05; control2Y: 32.956; x: -122.304; y: 84.285 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -118.774; y: 81.262 }
+ PathCubic { control1X: -118.774; control1Y: 81.262; control2X: -119.323; control2Y: 83.078; x: -120.092; y: 82.779 }
+ PathCubic { control1X: -120.86; control1Y: 82.481; control2X: -119.977; control2Y: 31.675; x: -140.043; y: 26.801 }
+ PathCubic { control1X: -140.043; control1Y: 26.801; control2X: -120.82; control2Y: 25.937; x: -118.774; y: 81.262 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -91.284; y: 123.59 }
+ PathCubic { control1X: -91.284; control1Y: 123.59; control2X: -89.648; control2Y: 124.55; x: -90.118; y: 125.227 }
+ PathCubic { control1X: -90.589; control1Y: 125.904; control2X: -139.763; control2Y: 113.102; x: -149.218; y: 131.459 }
+ PathCubic { control1X: -149.218; control1Y: 131.459; control2X: -145.539; control2Y: 112.572; x: -91.284; y: 123.59 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -94.093; y: 133.801 }
+ PathCubic { control1X: -94.093; control1Y: 133.801; control2X: -92.237; control2Y: 134.197; x: -92.471; y: 134.988 }
+ PathCubic { control1X: -92.704; control1Y: 135.779; control2X: -143.407; control2Y: 139.121; x: -146.597; y: 159.522 }
+ PathCubic { control1X: -146.597; control1Y: 159.522; control2X: -149.055; control2Y: 140.437; x: -94.093; y: 133.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -98.304; y: 128.276 }
+ PathCubic { control1X: -98.304; control1Y: 128.276; control2X: -96.526; control2Y: 128.939; x: -96.872; y: 129.687 }
+ PathCubic { control1X: -97.218; control1Y: 130.435; control2X: -147.866; control2Y: 126.346; x: -153.998; y: 146.064 }
+ PathCubic { control1X: -153.998; control1Y: 146.064; control2X: -153.646; control2Y: 126.825; x: -98.304; y: 128.276 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -109.009; y: 110.072 }
+ PathCubic { control1X: -109.009; control1Y: 110.072; control2X: -107.701; control2Y: 111.446; x: -108.34; y: 111.967 }
+ PathCubic { control1X: -108.979; control1Y: 112.488; control2X: -152.722; control2Y: 86.634; x: -166.869; y: 101.676 }
+ PathCubic { control1X: -166.869; control1Y: 101.676; control2X: -158.128; control2Y: 84.533; x: -109.009; y: 110.072 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -116.554; y: 114.263 }
+ PathCubic { control1X: -116.554; control1Y: 114.263; control2X: -115.098; control2Y: 115.48; x: -115.674; y: 116.071 }
+ PathCubic { control1X: -116.25; control1Y: 116.661; control2X: -162.638; control2Y: 95.922; x: -174.992; y: 112.469 }
+ PathCubic { control1X: -174.992; control1Y: 112.469; control2X: -168.247; control2Y: 94.447; x: -116.554; y: 114.263 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -119.154; y: 118.335 }
+ PathCubic { control1X: -119.154; control1Y: 118.335; control2X: -117.546; control2Y: 119.343; x: -118.036; y: 120.006 }
+ PathCubic { control1X: -118.526; control1Y: 120.669; control2X: -167.308; control2Y: 106.446; x: -177.291; y: 124.522 }
+ PathCubic { control1X: -177.291; control1Y: 124.522; control2X: -173.066; control2Y: 105.749; x: -119.154; y: 118.335 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -108.42; y: 118.949 }
+ PathCubic { control1X: -108.42; control1Y: 118.949; control2X: -107.298; control2Y: 120.48; x: -107.999; y: 120.915 }
+ PathCubic { control1X: -108.7; control1Y: 121.35; control2X: -148.769; control2Y: 90.102; x: -164.727; y: 103.207 }
+ PathCubic { control1X: -164.727; control1Y: 103.207; control2X: -153.862; control2Y: 87.326; x: -108.42; y: 118.949 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -128.2; y: 90 }
+ PathCubic { control1X: -128.2; control1Y: 90; control2X: -127.6; control2Y: 91.8; x: -128.4; y: 92 }
+ PathCubic { control1X: -129.2; control1Y: 92.2; control2X: -157.8; control2Y: 50.2; x: -177.001; y: 57.8 }
+ PathCubic { control1X: -177.001; control1Y: 57.8; control2X: -161.8; control2Y: 46; x: -128.2; y: 90 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -127.505; y: 96.979 }
+ PathCubic { control1X: -127.505; control1Y: 96.979; control2X: -126.53; control2Y: 98.608; x: -127.269; y: 98.975 }
+ PathCubic { control1X: -128.007; control1Y: 99.343; control2X: -164.992; control2Y: 64.499; x: -182.101; y: 76.061 }
+ PathCubic { control1X: -182.101; control1Y: 76.061; control2X: -169.804; control2Y: 61.261; x: -127.505; y: 96.979 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ PathMove { x: -127.62; y: 101.349 }
+ PathCubic { control1X: -127.62; control1Y: 101.349; control2X: -126.498; control2Y: 102.88; x: -127.199; y: 103.315 }
+ PathCubic { control1X: -127.9; control1Y: 103.749; control2X: -167.969; control2Y: 72.502; x: -183.927; y: 85.607 }
+ PathCubic { control1X: -183.927; control1Y: 85.607; control2X: -173.062; control2Y: 69.726; x: -127.62; y: 101.349 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -129.83; y: 103.065 }
+ PathCubic { control1X: -129.327; control1Y: 109.113; control2X: -128.339; control2Y: 115.682; x: -126.6; y: 118.801 }
+ PathCubic { control1X: -126.6; control1Y: 118.801; control2X: -130.2; control2Y: 131.201; x: -121.4; y: 144.401 }
+ PathCubic { control1X: -121.4; control1Y: 144.401; control2X: -121.8; control2Y: 151.601; x: -120.2; y: 154.801 }
+ PathCubic { control1X: -120.2; control1Y: 154.801; control2X: -116.2; control2Y: 163.201; x: -111.4; y: 164.001 }
+ PathCubic { control1X: -107.516; control1Y: 164.648; control2X: -98.793; control2Y: 167.717; x: -88.932; y: 169.121 }
+ PathCubic { control1X: -88.932; control1Y: 169.121; control2X: -71.8; control2Y: 183.201; x: -75; y: 196.001 }
+ PathCubic { control1X: -75; control1Y: 196.001; control2X: -75.4; control2Y: 212.401; x: -79; y: 214.001 }
+ PathCubic { control1X: -79; control1Y: 214.001; control2X: -67.4; control2Y: 202.801; x: -77; y: 219.601 }
+ PathLine { x: -81.4; y: 238.401 }
+ PathCubic { control1X: -81.4; control1Y: 238.401; control2X: -55.8; control2Y: 216.801; x: -71.4; y: 235.201 }
+ PathLine { x: -81.4; y: 261.201 }
+ PathCubic { control1X: -81.4; control1Y: 261.201; control2X: -61.8; control2Y: 242.801; x: -69; y: 251.201 }
+ PathLine { x: -72.2; y: 260.001 }
+ PathCubic { control1X: -72.2; control1Y: 260.001; control2X: -29; control2Y: 232.801; x: -59.8; y: 262.401 }
+ PathCubic { control1X: -59.8; control1Y: 262.401; control2X: -51.8; control2Y: 258.801; x: -47.4; y: 261.601 }
+ PathCubic { control1X: -47.4; control1Y: 261.601; control2X: -40.6; control2Y: 260.401; x: -41.4; y: 262.001 }
+ PathCubic { control1X: -41.4; control1Y: 262.001; control2X: -62.2; control2Y: 272.401; x: -65.8; y: 290.801 }
+ PathCubic { control1X: -65.8; control1Y: 290.801; control2X: -57.4; control2Y: 280.801; x: -60.6; y: 291.601 }
+ PathLine { x: -60.2; y: 303.201 }
+ PathCubic { control1X: -60.2; control1Y: 303.201; control2X: -56.2; control2Y: 281.601; x: -56.6; y: 319.201 }
+ PathCubic { control1X: -56.6; control1Y: 319.201; control2X: -37.4; control2Y: 301.201; x: -49; y: 322.001 }
+ PathLine { x: -49; y: 338.801 }
+ PathCubic { control1X: -49; control1Y: 338.801; control2X: -33.8; control2Y: 322.401; x: -40.2; y: 335.201 }
+ PathCubic { control1X: -40.2; control1Y: 335.201; control2X: -30.2; control2Y: 326.401; x: -34.2; y: 341.601 }
+ PathCubic { control1X: -34.2; control1Y: 341.601; control2X: -35; control2Y: 352.001; x: -30.6; y: 340.801 }
+ PathCubic { control1X: -30.6; control1Y: 340.801; control2X: -14.6; control2Y: 310.201; x: -20.6; y: 336.401 }
+ PathCubic { control1X: -20.6; control1Y: 336.401; control2X: -21.4; control2Y: 355.601; x: -16.6; y: 340.801 }
+ PathCubic { control1X: -16.6; control1Y: 340.801; control2X: -16.2; control2Y: 351.201; x: -7; y: 358.401 }
+ PathCubic { control1X: -7; control1Y: 358.401; control2X: -8.2; control2Y: 307.601; x: 4.6; y: 343.601 }
+ PathLine { x: 8.6; y: 360.001 }
+ PathCubic { control1X: 8.6; control1Y: 360.001; control2X: 11.4; control2Y: 350.801; x: 11; y: 345.601 }
+ PathCubic { control1X: 11; control1Y: 345.601; control2X: 25.8; control2Y: 329.201; x: 19; y: 353.601 }
+ PathCubic { control1X: 19; control1Y: 353.601; control2X: 34.2; control2Y: 330.801; x: 31; y: 344.001 }
+ PathCubic { control1X: 31; control1Y: 344.001; control2X: 23.4; control2Y: 360.001; x: 25; y: 364.801 }
+ PathCubic { control1X: 25; control1Y: 364.801; control2X: 41.8; control2Y: 330.001; x: 43; y: 328.401 }
+ PathCubic { control1X: 43; control1Y: 328.401; control2X: 41; control2Y: 370.802; x: 51.8; y: 334.801 }
+ PathCubic { control1X: 51.8; control1Y: 334.801; control2X: 57.4; control2Y: 346.801; x: 54.6; y: 351.201 }
+ PathCubic { control1X: 54.6; control1Y: 351.201; control2X: 62.6; control2Y: 343.201; x: 61.8; y: 340.001 }
+ PathCubic { control1X: 61.8; control1Y: 340.001; control2X: 66.4; control2Y: 331.801; x: 69.2; y: 345.401 }
+ PathCubic { control1X: 69.2; control1Y: 345.401; control2X: 71; control2Y: 354.801; x: 72.6; y: 351.601 }
+ PathCubic { control1X: 72.6; control1Y: 351.601; control2X: 76.6; control2Y: 375.602; x: 77.8; y: 352.801 }
+ PathCubic { control1X: 77.8; control1Y: 352.801; control2X: 79.4; control2Y: 339.201; x: 72.2; y: 327.601 }
+ PathCubic { control1X: 72.2; control1Y: 327.601; control2X: 73; control2Y: 324.401; x: 70.2; y: 320.401 }
+ PathCubic { control1X: 70.2; control1Y: 320.401; control2X: 83.8; control2Y: 342.001; x: 76.6; y: 313.201 }
+ PathCubic { control1X: 76.6; control1Y: 313.201; control2X: 87.801; control2Y: 321.201; x: 89.001; y: 321.201 }
+ PathCubic { control1X: 89.001; control1Y: 321.201; control2X: 75.4; control2Y: 298.001; x: 84.2; y: 302.801 }
+ PathCubic { control1X: 84.2; control1Y: 302.801; control2X: 79; control2Y: 292.401; x: 97.001; y: 304.401 }
+ PathCubic { control1X: 97.001; control1Y: 304.401; control2X: 81; control2Y: 288.401; x: 98.601; y: 298.001 }
+ PathCubic { control1X: 98.601; control1Y: 298.001; control2X: 106.601; control2Y: 304.401; x: 99.001; y: 294.401 }
+ PathCubic { control1X: 99.001; control1Y: 294.401; control2X: 84.6; control2Y: 278.401; x: 106.601; y: 296.401 }
+ PathCubic { control1X: 106.601; control1Y: 296.401; control2X: 118.201; control2Y: 312.801; x: 119.001; y: 315.601 }
+ PathCubic { control1X: 119.001; control1Y: 315.601; control2X: 109.001; control2Y: 286.401; x: 104.601; y: 283.601 }
+ PathCubic { control1X: 104.601; control1Y: 283.601; control2X: 113.001; control2Y: 247.201; x: 154.201; y: 262.801 }
+ PathCubic { control1X: 154.201; control1Y: 262.801; control2X: 161.001; control2Y: 280.001; x: 165.401; y: 261.601 }
+ PathCubic { control1X: 165.401; control1Y: 261.601; control2X: 178.201; control2Y: 255.201; x: 189.401; y: 282.801 }
+ PathCubic { control1X: 189.401; control1Y: 282.801; control2X: 193.401; control2Y: 269.201; x: 192.601; y: 266.401 }
+ PathCubic { control1X: 192.601; control1Y: 266.401; control2X: 199.401; control2Y: 267.601; x: 198.601; y: 266.401 }
+ PathCubic { control1X: 198.601; control1Y: 266.401; control2X: 211.801; control2Y: 270.801; x: 213.001; y: 270.001 }
+ PathCubic { control1X: 213.001; control1Y: 270.001; control2X: 219.801; control2Y: 276.801; x: 220.201; y: 273.201 }
+ PathCubic { control1X: 220.201; control1Y: 273.201; control2X: 229.401; control2Y: 276.001; x: 227.401; y: 272.401 }
+ PathCubic { control1X: 227.401; control1Y: 272.401; control2X: 236.201; control2Y: 288.001; x: 236.601; y: 291.601 }
+ PathLine { x: 239.001; y: 277.601 }
+ PathLine { x: 241.001; y: 280.401 }
+ PathCubic { control1X: 241.001; control1Y: 280.401; control2X: 242.601; control2Y: 272.801; x: 241.801; y: 271.601 }
+ PathCubic { control1X: 241.001; control1Y: 270.401; control2X: 261.801; control2Y: 278.401; x: 266.601; y: 299.201 }
+ PathLine { x: 268.601; y: 307.601 }
+ PathCubic { control1X: 268.601; control1Y: 307.601; control2X: 274.601; control2Y: 292.801; x: 273.001; y: 288.801 }
+ PathCubic { control1X: 273.001; control1Y: 288.801; control2X: 278.201; control2Y: 289.601; x: 278.601; y: 294.001 }
+ PathCubic { control1X: 278.601; control1Y: 294.001; control2X: 282.601; control2Y: 270.801; x: 277.801; y: 264.801 }
+ PathCubic { control1X: 277.801; control1Y: 264.801; control2X: 282.201; control2Y: 264.001; x: 283.401; y: 267.601 }
+ PathLine { x: 283.401; y: 260.401 }
+ PathCubic { control1X: 283.401; control1Y: 260.401; control2X: 290.601; control2Y: 261.201; x: 290.601; y: 258.801 }
+ PathCubic { control1X: 290.601; control1Y: 258.801; control2X: 295.001; control2Y: 254.801; x: 297.001; y: 259.601 }
+ PathCubic { control1X: 297.001; control1Y: 259.601; control2X: 284.601; control2Y: 224.401; x: 303.001; y: 243.601 }
+ PathCubic { control1X: 303.001; control1Y: 243.601; control2X: 310.201; control2Y: 254.401; x: 306.601; y: 235.601 }
+ PathCubic { control1X: 303.001; control1Y: 216.801; control2X: 299.001; control2Y: 215.201; x: 303.801; y: 214.801 }
+ PathCubic { control1X: 303.801; control1Y: 214.801; control2X: 304.601; control2Y: 211.201; x: 302.601; y: 209.601 }
+ PathCubic { control1X: 300.601; control1Y: 208.001; control2X: 303.801; control2Y: 209.601; x: 303.801; y: 209.601 }
+ PathCubic { control1X: 303.801; control1Y: 209.601; control2X: 308.601; control2Y: 213.601; x: 303.401; y: 191.601 }
+ PathCubic { control1X: 303.401; control1Y: 191.601; control2X: 309.801; control2Y: 193.201; x: 297.801; y: 164.001 }
+ PathCubic { control1X: 297.801; control1Y: 164.001; control2X: 300.601; control2Y: 161.601; x: 296.601; y: 153.201 }
+ PathCubic { control1X: 296.601; control1Y: 153.201; control2X: 304.601; control2Y: 157.601; x: 307.401; y: 156.001 }
+ PathCubic { control1X: 307.401; control1Y: 156.001; control2X: 307.001; control2Y: 154.401; x: 303.801; y: 150.401 }
+ PathCubic { control1X: 303.801; control1Y: 150.401; control2X: 282.201; control2Y: 95.6; x: 302.601; y: 117.601 }
+ PathCubic { control1X: 302.601; control1Y: 117.601; control2X: 314.451; control2Y: 131.151; x: 308.051; y: 108.351 }
+ PathCubic { control1X: 308.051; control1Y: 108.351; control2X: 298.94; control2Y: 84.341; x: 299.717; y: 80.045 }
+ PathLine { x: -129.83; y: 103.065 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: 299.717; y: 80.245 }
+ PathCubic { control1X: 300.345; control1Y: 80.426; control2X: 302.551; control2Y: 81.55; x: 303.801; y: 83.2 }
+ PathCubic { control1X: 303.801; control1Y: 83.2; control2X: 310.601; control2Y: 94; x: 305.401; y: 75.6 }
+ PathCubic { control1X: 305.401; control1Y: 75.6; control2X: 296.201; control2Y: 46.8; x: 305.001; y: 58 }
+ PathCubic { control1X: 305.001; control1Y: 58; control2X: 311.001; control2Y: 65.2; x: 307.801; y: 51.6 }
+ PathCubic { control1X: 303.936; control1Y: 35.173; control2X: 301.401; control2Y: 28.8; x: 301.401; y: 28.8 }
+ PathCubic { control1X: 301.401; control1Y: 28.8; control2X: 313.001; control2Y: 33.6; x: 286.201; y: -6 }
+ PathLine { x: 295.001; y: -2.4 }
+ PathCubic { control1X: 295.001; control1Y: -2.4; control2X: 275.401; control2Y: -42; x: 253.801; y: -47.2 }
+ PathLine { x: 245.801; y: -53.2 }
+ PathCubic { control1X: 245.801; control1Y: -53.2; control2X: 284.201; control2Y: -91.2; x: 271.401; y: -128 }
+ PathCubic { control1X: 271.401; control1Y: -128; control2X: 264.601; control2Y: -133.2; x: 255.001; y: -124 }
+ PathCubic { control1X: 255.001; control1Y: -124; control2X: 248.601; control2Y: -119.2; x: 242.601; y: -120.8 }
+ PathCubic { control1X: 242.601; control1Y: -120.8; control2X: 211.801; control2Y: -119.6; x: 209.801; y: -119.6 }
+ PathCubic { control1X: 207.801; control1Y: -119.6; control2X: 173.001; control2Y: -156.8; x: 107.401; y: -139.2 }
+ PathCubic { control1X: 107.401; control1Y: -139.2; control2X: 102.201; control2Y: -137.2; x: 97.801; y: -138.4 }
+ PathCubic { control1X: 97.801; control1Y: -138.4; control2X: 79.4; control2Y: -154.4; x: 30.6; y: -131.6 }
+ PathCubic { control1X: 30.6; control1Y: -131.6; control2X: 20.6; control2Y: -129.6; x: 19; y: -129.6 }
+ PathCubic { control1X: 17.4; control1Y: -129.6; control2X: 14.6; control2Y: -129.6; x: 6.6; y: -123.2 }
+ PathCubic { control1X: -1.4; control1Y: -116.8; control2X: -1.8; control2Y: -116; x: -3.8; y: -114.4 }
+ PathCubic { control1X: -3.8; control1Y: -114.4; control2X: -20.2; control2Y: -103.2; x: -25; y: -102.4 }
+ PathCubic { control1X: -25; control1Y: -102.4; control2X: -36.6; control2Y: -96; x: -41; y: -86 }
+ PathLine { x: -44.6; y: -84.8 }
+ PathCubic { control1X: -44.6; control1Y: -84.8; control2X: -46.2; control2Y: -77.6; x: -46.6; y: -76.4 }
+ PathCubic { control1X: -46.6; control1Y: -76.4; control2X: -51.4; control2Y: -72.8; x: -52.2; y: -67.2 }
+ PathCubic { control1X: -52.2; control1Y: -67.2; control2X: -61; control2Y: -61.2; x: -60.6; y: -56.8 }
+ PathCubic { control1X: -60.6; control1Y: -56.8; control2X: -62.2; control2Y: -51.6; x: -63; y: -46.8 }
+ PathCubic { control1X: -63; control1Y: -46.8; control2X: -70.2; control2Y: -42; x: -69.4; y: -39.2 }
+ PathCubic { control1X: -69.4; control1Y: -39.2; control2X: -77; control2Y: -25.2; x: -75.8; y: -18.4 }
+ PathCubic { control1X: -75.8; control1Y: -18.4; control2X: -82.2; control2Y: -18.8; x: -85; y: -16.4 }
+ PathCubic { control1X: -85; control1Y: -16.4; control2X: -85.8; control2Y: -11.6; x: -87.4; y: -11.2 }
+ PathCubic { control1X: -87.4; control1Y: -11.2; control2X: -90.2; control2Y: -10; x: -87.8; y: -6 }
+ PathCubic { control1X: -87.8; control1Y: -6; control2X: -89.4; control2Y: -3.2; x: -89.8; y: -1.6 }
+ PathCubic { control1X: -89.8; control1Y: -1.6; control2X: -89; control2Y: 1.2; x: -93.4; y: 6.8 }
+ PathCubic { control1X: -93.4; control1Y: 6.8; control2X: -99.8; control2Y: 25.6; x: -97.8; y: 30.8 }
+ PathCubic { control1X: -97.8; control1Y: 30.8; control2X: -97.4; control2Y: 35.6; x: -100.2; y: 37.2 }
+ PathCubic { control1X: -100.2; control1Y: 37.2; control2X: -103.8; control2Y: 36.8; x: -95.4; y: 48.8 }
+ PathCubic { control1X: -95.4; control1Y: 48.8; control2X: -94.6; control2Y: 50; x: -97.8; y: 52.4 }
+ PathCubic { control1X: -97.8; control1Y: 52.4; control2X: -115; control2Y: 56; x: -117.4; y: 72.4 }
+ PathCubic { control1X: -117.4; control1Y: 72.4; control2X: -131; control2Y: 87.2; x: -131; y: 92.4 }
+ PathCubic { control1X: -131; control1Y: 94.705; control2X: -130.729; control2Y: 97.852; x: -130.03; y: 102.465 }
+ PathCubic { control1X: -130.03; control1Y: 102.465; control2X: -130.6; control2Y: 110.801; x: -103; y: 111.601 }
+ PathCubic { control1X: -75.4; control1Y: 112.401; control2X: 299.717; control2Y: 80.245; x: 299.717; y: 80.245 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: -115.6; y: 102.6 }
+ PathCubic { control1X: -140.6; control1Y: 63.2; control2X: -126.2; control2Y: 119.601; x: -126.2; y: 119.601 }
+ PathCubic { control1X: -117.4; control1Y: 154.001; control2X: 12.2; control2Y: 116.401; x: 12.2; y: 116.401 }
+ PathCubic { control1X: 12.2; control1Y: 116.401; control2X: 181.001; control2Y: 86; x: 192.201; y: 82 }
+ PathCubic { control1X: 203.401; control1Y: 78; control2X: 298.601; control2Y: 84.4; x: 298.601; y: 84.4 }
+ PathLine { x: 293.001; y: 67.6 }
+ PathCubic { control1X: 228.201; control1Y: 21.2; control2X: 209.001; control2Y: 44.4; x: 195.401; y: 40.4 }
+ PathCubic { control1X: 181.801; control1Y: 36.4; control2X: 184.201; control2Y: 46; x: 181.001; y: 46.8 }
+ PathCubic { control1X: 177.801; control1Y: 47.6; control2X: 138.601; control2Y: 22.8; x: 132.201; y: 23.6 }
+ PathCubic { control1X: 125.801; control1Y: 24.4; control2X: 100.459; control2Y: 0.649; x: 115.401; y: 32.4 }
+ PathCubic { control1X: 131.401; control1Y: 66.4; control2X: 57; control2Y: 71.6; x: 40.2; y: 60.4 }
+ PathCubic { control1X: 23.4; control1Y: 49.2; control2X: 47.4; control2Y: 78.8; x: 47.4; y: 78.8 }
+ PathCubic { control1X: 65.8; control1Y: 98.8; control2X: 31.4; control2Y: 82; x: 31.4; y: 82 }
+ PathCubic { control1X: -3; control1Y: 69.2; control2X: -27; control2Y: 94.8; x: -30.2; y: 95.6 }
+ PathCubic { control1X: -33.4; control1Y: 96.4; control2X: -38.2; control2Y: 99.6; x: -39; y: 93.2 }
+ PathCubic { control1X: -39.8; control1Y: 86.8; control2X: -47.31; control2Y: 70.099; x: -79; y: 96.4 }
+ PathCubic { control1X: -99; control1Y: 113.001; control2X: -112.8; control2Y: 91; x: -112.8; y: 91 }
+ PathLine { x: -115.6; y: 102.6 }
+ }
+
+ ShapePath {
+ fillColor: "#e87f3a"
+ strokeWidth: -1
+ PathMove { x: 133.51; y: 25.346 }
+ PathCubic { control1X: 127.11; control1Y: 26.146; control2X: 101.743; control2Y: 2.407; x: 116.71; y: 34.146 }
+ PathCubic { control1X: 133.31; control1Y: 69.346; control2X: 58.31; control2Y: 73.346; x: 41.51; y: 62.146 }
+ PathCubic { control1X: 24.709; control1Y: 50.946; control2X: 48.71; control2Y: 80.546; x: 48.71; y: 80.546 }
+ PathCubic { control1X: 67.11; control1Y: 100.546; control2X: 32.709; control2Y: 83.746; x: 32.709; y: 83.746 }
+ PathCubic { control1X: -1.691; control1Y: 70.946; control2X: -25.691; control2Y: 96.546; x: -28.891; y: 97.346 }
+ PathCubic { control1X: -32.091; control1Y: 98.146; control2X: -36.891; control2Y: 101.346; x: -37.691; y: 94.946 }
+ PathCubic { control1X: -38.491; control1Y: 88.546; control2X: -45.87; control2Y: 72.012; x: -77.691; y: 98.146 }
+ PathCubic { control1X: -98.927; control1Y: 115.492; control2X: -112.418; control2Y: 94.037; x: -112.418; y: 94.037 }
+ PathLine { x: -115.618; y: 104.146 }
+ PathCubic { control1X: -140.618; control1Y: 64.346; control2X: -125.546; control2Y: 122.655; x: -125.546; y: 122.655 }
+ PathCubic { control1X: -116.745; control1Y: 157.056; control2X: 13.509; control2Y: 118.146; x: 13.509; y: 118.146 }
+ PathCubic { control1X: 13.509; control1Y: 118.146; control2X: 182.31; control2Y: 87.746; x: 193.51; y: 83.746 }
+ PathCubic { control1X: 204.71; control1Y: 79.746; control2X: 299.038; control2Y: 86.073; x: 299.038; y: 86.073 }
+ PathLine { x: 293.51; y: 68.764 }
+ PathCubic { control1X: 228.71; control1Y: 22.364; control2X: 210.31; control2Y: 46.146; x: 196.71; y: 42.146 }
+ PathCubic { control1X: 183.11; control1Y: 38.146; control2X: 185.51; control2Y: 47.746; x: 182.31; y: 48.546 }
+ PathCubic { control1X: 179.11; control1Y: 49.346; control2X: 139.91; control2Y: 24.546; x: 133.51; y: 25.346 }
+ }
+
+ ShapePath {
+ fillColor: "#ea8c4d"
+ strokeWidth: -1
+ PathMove { x: 134.819; y: 27.091 }
+ PathCubic { control1X: 128.419; control1Y: 27.891; control2X: 103.685; control2Y: 3.862; x: 118.019; y: 35.891 }
+ PathCubic { control1X: 134.219; control1Y: 72.092; control2X: 59.619; control2Y: 75.092; x: 42.819; y: 63.892 }
+ PathCubic { control1X: 26.019; control1Y: 52.692; control2X: 50.019; control2Y: 82.292; x: 50.019; y: 82.292 }
+ PathCubic { control1X: 68.419; control1Y: 102.292; control2X: 34.019; control2Y: 85.492; x: 34.019; y: 85.492 }
+ PathCubic { control1X: -0.381; control1Y: 72.692; control2X: -24.382; control2Y: 98.292; x: -27.582; y: 99.092 }
+ PathCubic { control1X: -30.782; control1Y: 99.892; control2X: -35.582; control2Y: 103.092; x: -36.382; y: 96.692 }
+ PathCubic { control1X: -37.182; control1Y: 90.292; control2X: -44.43; control2Y: 73.925; x: -76.382; y: 99.892 }
+ PathCubic { control1X: -98.855; control1Y: 117.983; control2X: -112.036; control2Y: 97.074; x: -112.036; y: 97.074 }
+ PathLine { x: -115.636; y: 105.692 }
+ PathCubic { control1X: -139.436; control1Y: 66.692; control2X: -124.891; control2Y: 125.71; x: -124.891; y: 125.71 }
+ PathCubic { control1X: -116.091; control1Y: 160.11; control2X: 14.819; control2Y: 119.892; x: 14.819; y: 119.892 }
+ PathCubic { control1X: 14.819; control1Y: 119.892; control2X: 183.619; control2Y: 89.492; x: 194.819; y: 85.492 }
+ PathCubic { control1X: 206.019; control1Y: 81.492; control2X: 299.474; control2Y: 87.746; x: 299.474; y: 87.746 }
+ PathLine { x: 294.02; y: 69.928 }
+ PathCubic { control1X: 229.219; control1Y: 23.528; control2X: 211.619; control2Y: 47.891; x: 198.019; y: 43.891 }
+ PathCubic { control1X: 184.419; control1Y: 39.891; control2X: 186.819; control2Y: 49.491; x: 183.619; y: 50.292 }
+ PathCubic { control1X: 180.419; control1Y: 51.092; control2X: 141.219; control2Y: 26.291; x: 134.819; y: 27.091 }
+ }
+
+ ShapePath {
+ fillColor: "#ec9961"
+ strokeWidth: -1
+ PathMove { x: 136.128; y: 28.837 }
+ PathCubic { control1X: 129.728; control1Y: 29.637; control2X: 104.999; control2Y: 5.605; x: 119.328; y: 37.637 }
+ PathCubic { control1X: 136.128; control1Y: 75.193; control2X: 60.394; control2Y: 76.482; x: 44.128; y: 65.637 }
+ PathCubic { control1X: 27.328; control1Y: 54.437; control2X: 51.328; control2Y: 84.037; x: 51.328; y: 84.037 }
+ PathCubic { control1X: 69.728; control1Y: 104.037; control2X: 35.328; control2Y: 87.237; x: 35.328; y: 87.237 }
+ PathCubic { control1X: 0.928; control1Y: 74.437; control2X: -23.072; control2Y: 100.037; x: -26.272; y: 100.837 }
+ PathCubic { control1X: -29.472; control1Y: 101.637; control2X: -34.272; control2Y: 104.837; x: -35.072; y: 98.437 }
+ PathCubic { control1X: -35.872; control1Y: 92.037; control2X: -42.989; control2Y: 75.839; x: -75.073; y: 101.637 }
+ PathCubic { control1X: -98.782; control1Y: 120.474; control2X: -111.655; control2Y: 100.11; x: -111.655; y: 100.11 }
+ PathLine { x: -115.655; y: 107.237 }
+ PathCubic { control1X: -137.455; control1Y: 70.437; control2X: -124.236; control2Y: 128.765; x: -124.236; y: 128.765 }
+ PathCubic { control1X: -115.436; control1Y: 163.165; control2X: 16.128; control2Y: 121.637; x: 16.128; y: 121.637 }
+ PathCubic { control1X: 16.128; control1Y: 121.637; control2X: 184.928; control2Y: 91.237; x: 196.129; y: 87.237 }
+ PathCubic { control1X: 207.329; control1Y: 83.237; control2X: 299.911; control2Y: 89.419; x: 299.911; y: 89.419 }
+ PathLine { x: 294.529; y: 71.092 }
+ PathCubic { control1X: 229.729; control1Y: 24.691; control2X: 212.929; control2Y: 49.637; x: 199.329; y: 45.637 }
+ PathCubic { control1X: 185.728; control1Y: 41.637; control2X: 188.128; control2Y: 51.237; x: 184.928; y: 52.037 }
+ PathCubic { control1X: 181.728; control1Y: 52.837; control2X: 142.528; control2Y: 28.037; x: 136.128; y: 28.837 }
+ }
+
+ ShapePath {
+ fillColor: "#eea575"
+ strokeWidth: -1
+ PathMove { x: 137.438; y: 30.583 }
+ PathCubic { control1X: 131.037; control1Y: 31.383; control2X: 106.814; control2Y: 7.129; x: 120.637; y: 39.383 }
+ PathCubic { control1X: 137.438; control1Y: 78.583; control2X: 62.237; control2Y: 78.583; x: 45.437; y: 67.383 }
+ PathCubic { control1X: 28.637; control1Y: 56.183; control2X: 52.637; control2Y: 85.783; x: 52.637; y: 85.783 }
+ PathCubic { control1X: 71.037; control1Y: 105.783; control2X: 36.637; control2Y: 88.983; x: 36.637; y: 88.983 }
+ PathCubic { control1X: 2.237; control1Y: 76.183; control2X: -21.763; control2Y: 101.783; x: -24.963; y: 102.583 }
+ PathCubic { control1X: -28.163; control1Y: 103.383; control2X: -32.963; control2Y: 106.583; x: -33.763; y: 100.183 }
+ PathCubic { control1X: -34.563; control1Y: 93.783; control2X: -41.548; control2Y: 77.752; x: -73.763; y: 103.383 }
+ PathCubic { control1X: -98.709; control1Y: 122.965; control2X: -111.273; control2Y: 103.146; x: -111.273; y: 103.146 }
+ PathLine { x: -115.673; y: 108.783 }
+ PathCubic { control1X: -135.473; control1Y: 73.982; control2X: -123.582; control2Y: 131.819; x: -123.582; y: 131.819 }
+ PathCubic { control1X: -114.782; control1Y: 166.22; control2X: 17.437; control2Y: 123.383; x: 17.437; y: 123.383 }
+ PathCubic { control1X: 17.437; control1Y: 123.383; control2X: 186.238; control2Y: 92.983; x: 197.438; y: 88.983 }
+ PathCubic { control1X: 208.638; control1Y: 84.983; control2X: 300.347; control2Y: 91.092; x: 300.347; y: 91.092 }
+ PathLine { x: 295.038; y: 72.255 }
+ PathCubic { control1X: 230.238; control1Y: 25.855; control2X: 214.238; control2Y: 51.383; x: 200.638; y: 47.383 }
+ PathCubic { control1X: 187.038; control1Y: 43.383; control2X: 189.438; control2Y: 52.983; x: 186.238; y: 53.783 }
+ PathCubic { control1X: 183.038; control1Y: 54.583; control2X: 143.838; control2Y: 29.783; x: 137.438; y: 30.583 }
+ }
+
+ ShapePath {
+ fillColor: "#f1b288"
+ strokeWidth: -1
+ PathMove { x: 138.747; y: 32.328 }
+ PathCubic { control1X: 132.347; control1Y: 33.128; control2X: 106.383; control2Y: 9.677; x: 121.947; y: 41.128 }
+ PathCubic { control1X: 141.147; control1Y: 79.928; control2X: 63.546; control2Y: 80.328; x: 46.746; y: 69.128 }
+ PathCubic { control1X: 29.946; control1Y: 57.928; control2X: 53.946; control2Y: 87.528; x: 53.946; y: 87.528 }
+ PathCubic { control1X: 72.346; control1Y: 107.528; control2X: 37.946; control2Y: 90.728; x: 37.946; y: 90.728 }
+ PathCubic { control1X: 3.546; control1Y: 77.928; control2X: -20.454; control2Y: 103.528; x: -23.654; y: 104.328 }
+ PathCubic { control1X: -26.854; control1Y: 105.128; control2X: -31.654; control2Y: 108.328; x: -32.454; y: 101.928 }
+ PathCubic { control1X: -33.254; control1Y: 95.528; control2X: -40.108; control2Y: 79.665; x: -72.454; y: 105.128 }
+ PathCubic { control1X: -98.636; control1Y: 125.456; control2X: -110.891; control2Y: 106.183; x: -110.891; y: 106.183 }
+ PathLine { x: -115.691; y: 110.328 }
+ PathCubic { control1X: -133.691; control1Y: 77.128; control2X: -122.927; control2Y: 134.874; x: -122.927; y: 134.874 }
+ PathCubic { control1X: -114.127; control1Y: 169.274; control2X: 18.746; control2Y: 125.128; x: 18.746; y: 125.128 }
+ PathCubic { control1X: 18.746; control1Y: 125.128; control2X: 187.547; control2Y: 94.728; x: 198.747; y: 90.728 }
+ PathCubic { control1X: 209.947; control1Y: 86.728; control2X: 300.783; control2Y: 92.764; x: 300.783; y: 92.764 }
+ PathLine { x: 295.547; y: 73.419 }
+ PathCubic { control1X: 230.747; control1Y: 27.019; control2X: 215.547; control2Y: 53.128; x: 201.947; y: 49.128 }
+ PathCubic { control1X: 188.347; control1Y: 45.128; control2X: 190.747; control2Y: 54.728; x: 187.547; y: 55.528 }
+ PathCubic { control1X: 184.347; control1Y: 56.328; control2X: 145.147; control2Y: 31.528; x: 138.747; y: 32.328 }
+ }
+
+ ShapePath {
+ fillColor: "#f3bf9c"
+ strokeWidth: -1
+ PathMove { x: 140.056; y: 34.073 }
+ PathCubic { control1X: 133.655; control1Y: 34.873; control2X: 107.313; control2Y: 11.613; x: 123.255; y: 42.873 }
+ PathCubic { control1X: 143.656; control1Y: 82.874; control2X: 64.855; control2Y: 82.074; x: 48.055; y: 70.874 }
+ PathCubic { control1X: 31.255; control1Y: 59.674; control2X: 55.255; control2Y: 89.274; x: 55.255; y: 89.274 }
+ PathCubic { control1X: 73.655; control1Y: 109.274; control2X: 39.255; control2Y: 92.474; x: 39.255; y: 92.474 }
+ PathCubic { control1X: 4.855; control1Y: 79.674; control2X: -19.145; control2Y: 105.274; x: -22.345; y: 106.074 }
+ PathCubic { control1X: -25.545; control1Y: 106.874; control2X: -30.345; control2Y: 110.074; x: -31.145; y: 103.674 }
+ PathCubic { control1X: -31.945; control1Y: 97.274; control2X: -38.668; control2Y: 81.578; x: -71.145; y: 106.874 }
+ PathCubic { control1X: -98.564; control1Y: 127.947; control2X: -110.509; control2Y: 109.219; x: -110.509; y: 109.219 }
+ PathLine { x: -115.709; y: 111.874 }
+ PathCubic { control1X: -131.709; control1Y: 81.674; control2X: -122.273; control2Y: 137.929; x: -122.273; y: 137.929 }
+ PathCubic { control1X: -113.473; control1Y: 172.329; control2X: 20.055; control2Y: 126.874; x: 20.055; y: 126.874 }
+ PathCubic { control1X: 20.055; control1Y: 126.874; control2X: 188.856; control2Y: 96.474; x: 200.056; y: 92.474 }
+ PathCubic { control1X: 211.256; control1Y: 88.474; control2X: 301.22; control2Y: 94.437; x: 301.22; y: 94.437 }
+ PathLine { x: 296.056; y: 74.583 }
+ PathCubic { control1X: 231.256; control1Y: 28.183; control2X: 216.856; control2Y: 54.874; x: 203.256; y: 50.874 }
+ PathCubic { control1X: 189.656; control1Y: 46.873; control2X: 192.056; control2Y: 56.474; x: 188.856; y: 57.274 }
+ PathCubic { control1X: 185.656; control1Y: 58.074; control2X: 146.456; control2Y: 33.273; x: 140.056; y: 34.073 }
+ }
+
+ ShapePath {
+ fillColor: "#f5ccb0"
+ strokeWidth: -1
+ PathMove { x: 141.365; y: 35.819 }
+ PathCubic { control1X: 134.965; control1Y: 36.619; control2X: 107.523; control2Y: 13.944; x: 124.565; y: 44.619 }
+ PathCubic { control1X: 146.565; control1Y: 84.219; control2X: 66.164; control2Y: 83.819; x: 49.364; y: 72.619 }
+ PathCubic { control1X: 32.564; control1Y: 61.419; control2X: 56.564; control2Y: 91.019; x: 56.564; y: 91.019 }
+ PathCubic { control1X: 74.964; control1Y: 111.019; control2X: 40.564; control2Y: 94.219; x: 40.564; y: 94.219 }
+ PathCubic { control1X: 6.164; control1Y: 81.419; control2X: -17.836; control2Y: 107.019; x: -21.036; y: 107.819 }
+ PathCubic { control1X: -24.236; control1Y: 108.619; control2X: -29.036; control2Y: 111.819; x: -29.836; y: 105.419 }
+ PathCubic { control1X: -30.636; control1Y: 99.019; control2X: -37.227; control2Y: 83.492; x: -69.836; y: 108.619 }
+ PathCubic { control1X: -98.491; control1Y: 130.438; control2X: -110.127; control2Y: 112.256; x: -110.127; y: 112.256 }
+ PathLine { x: -115.727; y: 113.419 }
+ PathCubic { control1X: -130.128; control1Y: 85.019; control2X: -121.618; control2Y: 140.983; x: -121.618; y: 140.983 }
+ PathCubic { control1X: -112.818; control1Y: 175.384; control2X: 21.364; control2Y: 128.619; x: 21.364; y: 128.619 }
+ PathCubic { control1X: 21.364; control1Y: 128.619; control2X: 190.165; control2Y: 98.219; x: 201.365; y: 94.219 }
+ PathCubic { control1X: 212.565; control1Y: 90.219; control2X: 301.656; control2Y: 96.11; x: 301.656; y: 96.11 }
+ PathLine { x: 296.565; y: 75.746 }
+ PathCubic { control1X: 231.765; control1Y: 29.346; control2X: 218.165; control2Y: 56.619; x: 204.565; y: 52.619 }
+ PathCubic { control1X: 190.965; control1Y: 48.619; control2X: 193.365; control2Y: 58.219; x: 190.165; y: 59.019 }
+ PathCubic { control1X: 186.965; control1Y: 59.819; control2X: 147.765; control2Y: 35.019; x: 141.365; y: 35.819 }
+ }
+
+ ShapePath {
+ fillColor: "#f8d8c4"
+ strokeWidth: -1
+ PathMove { x: 142.674; y: 37.565 }
+ PathCubic { control1X: 136.274; control1Y: 38.365; control2X: 108.832; control2Y: 15.689; x: 125.874; y: 46.365 }
+ PathCubic { control1X: 147.874; control1Y: 85.965; control2X: 67.474; control2Y: 85.565; x: 50.674; y: 74.365 }
+ PathCubic { control1X: 33.874; control1Y: 63.165; control2X: 57.874; control2Y: 92.765; x: 57.874; y: 92.765 }
+ PathCubic { control1X: 76.274; control1Y: 112.765; control2X: 41.874; control2Y: 95.965; x: 41.874; y: 95.965 }
+ PathCubic { control1X: 7.473; control1Y: 83.165; control2X: -16.527; control2Y: 108.765; x: -19.727; y: 109.565 }
+ PathCubic { control1X: -22.927; control1Y: 110.365; control2X: -27.727; control2Y: 113.565; x: -28.527; y: 107.165 }
+ PathCubic { control1X: -29.327; control1Y: 100.765; control2X: -35.786; control2Y: 85.405; x: -68.527; y: 110.365 }
+ PathCubic { control1X: -98.418; control1Y: 132.929; control2X: -109.745; control2Y: 115.293; x: -109.745; y: 115.293 }
+ PathLine { x: -115.745; y: 114.965 }
+ PathCubic { control1X: -129.346; control1Y: 88.564; control2X: -120.963; control2Y: 144.038; x: -120.963; y: 144.038 }
+ PathCubic { control1X: -112.163; control1Y: 178.438; control2X: 22.673; control2Y: 130.365; x: 22.673; y: 130.365 }
+ PathCubic { control1X: 22.673; control1Y: 130.365; control2X: 191.474; control2Y: 99.965; x: 202.674; y: 95.965 }
+ PathCubic { control1X: 213.874; control1Y: 91.965; control2X: 302.093; control2Y: 97.783; x: 302.093; y: 97.783 }
+ PathLine { x: 297.075; y: 76.91 }
+ PathCubic { control1X: 232.274; control1Y: 30.51; control2X: 219.474; control2Y: 58.365; x: 205.874; y: 54.365 }
+ PathCubic { control1X: 192.274; control1Y: 50.365; control2X: 194.674; control2Y: 59.965; x: 191.474; y: 60.765 }
+ PathCubic { control1X: 188.274; control1Y: 61.565; control2X: 149.074; control2Y: 36.765; x: 142.674; y: 37.565 }
+ }
+
+ ShapePath {
+ fillColor: "#fae5d7"
+ strokeWidth: -1
+ PathMove { x: 143.983; y: 39.31 }
+ PathCubic { control1X: 137.583; control1Y: 40.11; control2X: 110.529; control2Y: 17.223; x: 127.183; y: 48.11 }
+ PathCubic { control1X: 149.183; control1Y: 88.91; control2X: 68.783; control2Y: 87.31; x: 51.983; y: 76.11 }
+ PathCubic { control1X: 35.183; control1Y: 64.91; control2X: 59.183; control2Y: 94.51; x: 59.183; y: 94.51 }
+ PathCubic { control1X: 77.583; control1Y: 114.51; control2X: 43.183; control2Y: 97.71; x: 43.183; y: 97.71 }
+ PathCubic { control1X: 8.783; control1Y: 84.91; control2X: -15.217; control2Y: 110.51; x: -18.417; y: 111.31 }
+ PathCubic { control1X: -21.618; control1Y: 112.11; control2X: -26.418; control2Y: 115.31; x: -27.218; y: 108.91 }
+ PathCubic { control1X: -28.018; control1Y: 102.51; control2X: -34.346; control2Y: 87.318; x: -67.218; y: 112.11 }
+ PathCubic { control1X: -98.345; control1Y: 135.42; control2X: -109.363; control2Y: 118.329; x: -109.363; y: 118.329 }
+ PathLine { x: -115.764; y: 116.51 }
+ PathCubic { control1X: -128.764; control1Y: 92.51; control2X: -120.309; control2Y: 147.093; x: -120.309; y: 147.093 }
+ PathCubic { control1X: -111.509; control1Y: 181.493; control2X: 23.983; control2Y: 132.11; x: 23.983; y: 132.11 }
+ PathCubic { control1X: 23.983; control1Y: 132.11; control2X: 192.783; control2Y: 101.71; x: 203.983; y: 97.71 }
+ PathCubic { control1X: 215.183; control1Y: 93.71; control2X: 302.529; control2Y: 99.456; x: 302.529; y: 99.456 }
+ PathLine { x: 297.583; y: 78.074 }
+ PathCubic { control1X: 232.783; control1Y: 31.673; control2X: 220.783; control2Y: 60.11; x: 207.183; y: 56.11 }
+ PathCubic { control1X: 193.583; control1Y: 52.11; control2X: 195.983; control2Y: 61.71; x: 192.783; y: 62.51 }
+ PathCubic { control1X: 189.583; control1Y: 63.31; control2X: 150.383; control2Y: 38.51; x: 143.983; y: 39.31 }
+ }
+
+ ShapePath {
+ fillColor: "#fcf2eb"
+ strokeWidth: -1
+ PathMove { x: 145.292; y: 41.055 }
+ PathCubic { control1X: 138.892; control1Y: 41.855; control2X: 112.917; control2Y: 18.411; x: 128.492; y: 49.855 }
+ PathCubic { control1X: 149.692; control1Y: 92.656; control2X: 70.092; control2Y: 89.056; x: 53.292; y: 77.856 }
+ PathCubic { control1X: 36.492; control1Y: 66.656; control2X: 60.492; control2Y: 96.256; x: 60.492; y: 96.256 }
+ PathCubic { control1X: 78.892; control1Y: 116.256; control2X: 44.492; control2Y: 99.456; x: 44.492; y: 99.456 }
+ PathCubic { control1X: 10.092; control1Y: 86.656; control2X: -13.908; control2Y: 112.256; x: -17.108; y: 113.056 }
+ PathCubic { control1X: -20.308; control1Y: 113.856; control2X: -25.108; control2Y: 117.056; x: -25.908; y: 110.656 }
+ PathCubic { control1X: -26.708; control1Y: 104.256; control2X: -32.905; control2Y: 89.232; x: -65.908; y: 113.856 }
+ PathCubic { control1X: -98.273; control1Y: 137.911; control2X: -108.982; control2Y: 121.365; x: -108.982; y: 121.365 }
+ PathLine { x: -115.782; y: 118.056 }
+ PathCubic { control1X: -128.582; control1Y: 94.856; control2X: -119.654; control2Y: 150.147; x: -119.654; y: 150.147 }
+ PathCubic { control1X: -110.854; control1Y: 184.547; control2X: 25.292; control2Y: 133.856; x: 25.292; y: 133.856 }
+ PathCubic { control1X: 25.292; control1Y: 133.856; control2X: 194.093; control2Y: 103.456; x: 205.293; y: 99.456 }
+ PathCubic { control1X: 216.493; control1Y: 95.456; control2X: 302.965; control2Y: 101.128; x: 302.965; y: 101.128 }
+ PathLine { x: 298.093; y: 79.237 }
+ PathCubic { control1X: 233.292; control1Y: 32.837; control2X: 222.093; control2Y: 61.856; x: 208.493; y: 57.856 }
+ PathCubic { control1X: 194.893; control1Y: 53.855; control2X: 197.293; control2Y: 63.456; x: 194.093; y: 64.256 }
+ PathCubic { control1X: 190.892; control1Y: 65.056; control2X: 151.692; control2Y: 40.255; x: 145.292; y: 41.055 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: -115.8; y: 119.601 }
+ PathCubic { control1X: -128.6; control1Y: 97.6; control2X: -119; control2Y: 153.201; x: -119; y: 153.201 }
+ PathCubic { control1X: -110.2; control1Y: 187.601; control2X: 26.6; control2Y: 135.601; x: 26.6; y: 135.601 }
+ PathCubic { control1X: 26.6; control1Y: 135.601; control2X: 195.401; control2Y: 105.2; x: 206.601; y: 101.2 }
+ PathCubic { control1X: 217.801; control1Y: 97.2; control2X: 303.401; control2Y: 102.8; x: 303.401; y: 102.8 }
+ PathLine { x: 298.601; y: 80.4 }
+ PathCubic { control1X: 233.801; control1Y: 34; control2X: 223.401; control2Y: 63.6; x: 209.801; y: 59.6 }
+ PathCubic { control1X: 196.201; control1Y: 55.6; control2X: 198.601; control2Y: 65.2; x: 195.401; y: 66 }
+ PathCubic { control1X: 192.201; control1Y: 66.8; control2X: 153.001; control2Y: 42; x: 146.601; y: 42.8 }
+ PathCubic { control1X: 140.201; control1Y: 43.6; control2X: 114.981; control2Y: 19.793; x: 129.801; y: 51.6 }
+ PathCubic { control1X: 152.028; control1Y: 99.307; control2X: 69.041; control2Y: 89.227; x: 54.6; y: 79.6 }
+ PathCubic { control1X: 37.8; control1Y: 68.4; control2X: 61.8; control2Y: 98; x: 61.8; y: 98 }
+ PathCubic { control1X: 80.2; control1Y: 118.001; control2X: 45.8; control2Y: 101.2; x: 45.8; y: 101.2 }
+ PathCubic { control1X: 11.4; control1Y: 88.4; control2X: -12.6; control2Y: 114.001; x: -15.8; y: 114.801 }
+ PathCubic { control1X: -19; control1Y: 115.601; control2X: -23.8; control2Y: 118.801; x: -24.6; y: 112.401 }
+ PathCubic { control1X: -25.4; control1Y: 106; control2X: -31.465; control2Y: 91.144; x: -64.6; y: 115.601 }
+ PathCubic { control1X: -98.2; control1Y: 140.401; control2X: -108.6; control2Y: 124.401; x: -108.6; y: 124.401 }
+ PathLine { x: -115.8; y: 119.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -74.2; y: 149.601 }
+ PathCubic { control1X: -74.2; control1Y: 149.601; control2X: -81.4; control2Y: 161.201; x: -60.6; y: 174.401 }
+ PathCubic { control1X: -60.6; control1Y: 174.401; control2X: -59.2; control2Y: 175.801; x: -77.2; y: 171.601 }
+ PathCubic { control1X: -77.2; control1Y: 171.601; control2X: -83.4; control2Y: 169.601; x: -85; y: 159.201 }
+ PathCubic { control1X: -85; control1Y: 159.201; control2X: -89.8; control2Y: 154.801; x: -94.6; y: 149.201 }
+ PathCubic { control1X: -99.4; control1Y: 143.601; control2X: -74.2; control2Y: 149.601; x: -74.2; y: 149.601 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 65.8; y: 102 }
+ PathCubic { control1X: 65.8; control1Y: 102; control2X: 83.498; control2Y: 128.821; x: 82.9; y: 133.601 }
+ PathCubic { control1X: 81.6; control1Y: 144.001; control2X: 81.4; control2Y: 153.601; x: 84.6; y: 157.601 }
+ PathCubic { control1X: 87.801; control1Y: 161.601; control2X: 96.601; control2Y: 194.801; x: 96.601; y: 194.801 }
+ PathCubic { control1X: 96.601; control1Y: 194.801; control2X: 96.201; control2Y: 196.001; x: 108.601; y: 158.001 }
+ PathCubic { control1X: 108.601; control1Y: 158.001; control2X: 120.201; control2Y: 142.001; x: 100.201; y: 123.601 }
+ PathCubic { control1X: 100.201; control1Y: 123.601; control2X: 65; control2Y: 94.8; x: 65.8; y: 102 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -54.2; y: 176.401 }
+ PathCubic { control1X: -54.2; control1Y: 176.401; control2X: -43; control2Y: 183.601; x: -57.4; y: 214.801 }
+ PathLine { x: -51; y: 212.401 }
+ PathCubic { control1X: -51; control1Y: 212.401; control2X: -51.8; control2Y: 223.601; x: -55; y: 226.001 }
+ PathLine { x: -47.8; y: 222.801 }
+ PathCubic { control1X: -47.8; control1Y: 222.801; control2X: -43; control2Y: 230.801; x: -47; y: 235.601 }
+ PathCubic { control1X: -47; control1Y: 235.601; control2X: -30.2; control2Y: 243.601; x: -31; y: 250.001 }
+ PathCubic { control1X: -31; control1Y: 250.001; control2X: -24.6; control2Y: 242.001; x: -28.6; y: 235.601 }
+ PathCubic { control1X: -32.6; control1Y: 229.201; control2X: -39.8; control2Y: 233.201; x: -39; y: 214.801 }
+ PathLine { x: -47.8; y: 218.001 }
+ PathCubic { control1X: -47.8; control1Y: 218.001; control2X: -42.2; control2Y: 209.201; x: -42.2; y: 202.801 }
+ PathLine { x: -50.2; y: 205.201 }
+ PathCubic { control1X: -50.2; control1Y: 205.201; control2X: -34.731; control2Y: 178.623; x: -45.4; y: 177.201 }
+ PathCubic { control1X: -51.4; control1Y: 176.401; control2X: -54.2; control2Y: 176.401; x: -54.2; y: 176.401 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -21.8; y: 193.201 }
+ PathCubic { control1X: -21.8; control1Y: 193.201; control2X: -19; control2Y: 188.801; x: -21.8; y: 189.601 }
+ PathCubic { control1X: -24.6; control1Y: 190.401; control2X: -55.8; control2Y: 205.201; x: -61.8; y: 214.801 }
+ PathCubic { control1X: -61.8; control1Y: 214.801; control2X: -27.4; control2Y: 190.401; x: -21.8; y: 193.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -11.4; y: 201.201 }
+ PathCubic { control1X: -11.4; control1Y: 201.201; control2X: -8.6; control2Y: 196.801; x: -11.4; y: 197.601 }
+ PathCubic { control1X: -14.2; control1Y: 198.401; control2X: -45.4; control2Y: 213.201; x: -51.4; y: 222.801 }
+ PathCubic { control1X: -51.4; control1Y: 222.801; control2X: -17; control2Y: 198.401; x: -11.4; y: 201.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 1.8; y: 186.001 }
+ PathCubic { control1X: 1.8; control1Y: 186.001; control2X: 4.6; control2Y: 181.601; x: 1.8; y: 182.401 }
+ PathCubic { control1X: -1; control1Y: 183.201; control2X: -32.2; control2Y: 198.001; x: -38.2; y: 207.601 }
+ PathCubic { control1X: -38.2; control1Y: 207.601; control2X: -3.8; control2Y: 183.201; x: 1.8; y: 186.001 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -21.4; y: 229.601 }
+ PathCubic { control1X: -21.4; control1Y: 229.601; control2X: -21.4; control2Y: 223.601; x: -24.2; y: 224.401 }
+ PathCubic { control1X: -27; control1Y: 225.201; control2X: -63; control2Y: 242.801; x: -69; y: 252.401 }
+ PathCubic { control1X: -69; control1Y: 252.401; control2X: -27; control2Y: 226.801; x: -21.4; y: 229.601 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -20.2; y: 218.801 }
+ PathCubic { control1X: -20.2; control1Y: 218.801; control2X: -19; control2Y: 214.001; x: -21.8; y: 214.801 }
+ PathCubic { control1X: -23.8; control1Y: 214.801; control2X: -50.2; control2Y: 226.401; x: -56.2; y: 236.001 }
+ PathCubic { control1X: -56.2; control1Y: 236.001; control2X: -26.6; control2Y: 214.401; x: -20.2; y: 218.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -34.6; y: 266.401 }
+ PathLine { x: -44.6; y: 274.001 }
+ PathCubic { control1X: -44.6; control1Y: 274.001; control2X: -34.2; control2Y: 266.401; x: -30.6; y: 267.601 }
+ PathCubic { control1X: -30.6; control1Y: 267.601; control2X: -37.4; control2Y: 278.801; x: -38.2; y: 284.001 }
+ PathCubic { control1X: -38.2; control1Y: 284.001; control2X: -27.8; control2Y: 271.201; x: -22.2; y: 271.601 }
+ PathCubic { control1X: -22.2; control1Y: 271.601; control2X: -14.6; control2Y: 272.001; x: -14.6; y: 282.801 }
+ PathCubic { control1X: -14.6; control1Y: 282.801; control2X: -9; control2Y: 272.401; x: -5.8; y: 272.801 }
+ PathCubic { control1X: -5.8; control1Y: 272.801; control2X: -4.6; control2Y: 279.201; x: -5.8; y: 286.001 }
+ PathCubic { control1X: -5.8; control1Y: 286.001; control2X: -1.8; control2Y: 278.401; x: 2.2; y: 280.001 }
+ PathCubic { control1X: 2.2; control1Y: 280.001; control2X: 8.6; control2Y: 278.001; x: 7.8; y: 289.601 }
+ PathCubic { control1X: 7.8; control1Y: 289.601; control2X: 7.8; control2Y: 300.001; x: 7; y: 302.801 }
+ PathCubic { control1X: 7; control1Y: 302.801; control2X: 12.6; control2Y: 276.401; x: 15; y: 276.001 }
+ PathCubic { control1X: 15; control1Y: 276.001; control2X: 23; control2Y: 274.801; x: 27.8; y: 283.601 }
+ PathCubic { control1X: 27.8; control1Y: 283.601; control2X: 23.8; control2Y: 276.001; x: 28.6; y: 278.001 }
+ PathCubic { control1X: 28.6; control1Y: 278.001; control2X: 39.4; control2Y: 279.601; x: 42.6; y: 286.401 }
+ PathCubic { control1X: 42.6; control1Y: 286.401; control2X: 35.8; control2Y: 274.401; x: 41.4; y: 277.601 }
+ PathCubic { control1X: 41.4; control1Y: 277.601; control2X: 48.2; control2Y: 277.601; x: 49.4; y: 284.001 }
+ PathCubic { control1X: 49.4; control1Y: 284.001; control2X: 57.8; control2Y: 305.201; x: 59.8; y: 306.801 }
+ PathCubic { control1X: 59.8; control1Y: 306.801; control2X: 52.2; control2Y: 285.201; x: 53.8; y: 285.201 }
+ PathCubic { control1X: 53.8; control1Y: 285.201; control2X: 51.8; control2Y: 273.201; x: 57; y: 288.001 }
+ PathCubic { control1X: 57; control1Y: 288.001; control2X: 53.8; control2Y: 274.001; x: 59.4; y: 274.801 }
+ PathCubic { control1X: 65; control1Y: 275.601; control2X: 69.4; control2Y: 285.601; x: 77.8; y: 283.201 }
+ PathCubic { control1X: 77.8; control1Y: 283.201; control2X: 87.401; control2Y: 288.801; x: 89.401; y: 219.601 }
+ PathLine { x: -34.6; y: 266.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -29.8; y: 173.601 }
+ PathCubic { control1X: -29.8; control1Y: 173.601; control2X: -15; control2Y: 167.601; x: 25; y: 173.601 }
+ PathCubic { control1X: 25; control1Y: 173.601; control2X: 32.2; control2Y: 174.001; x: 39; y: 165.201 }
+ PathCubic { control1X: 45.8; control1Y: 156.401; control2X: 72.6; control2Y: 149.201; x: 79; y: 151.201 }
+ PathLine { x: 88.601; y: 157.601 }
+ PathLine { x: 89.401; y: 158.801 }
+ PathCubic { control1X: 89.401; control1Y: 158.801; control2X: 101.801; control2Y: 169.201; x: 102.201; y: 176.801 }
+ PathCubic { control1X: 102.601; control1Y: 184.401; control2X: 87.801; control2Y: 232.401; x: 78.2; y: 248.401 }
+ PathCubic { control1X: 68.6; control1Y: 264.401; control2X: 59; control2Y: 276.801; x: 39.8; y: 274.401 }
+ PathCubic { control1X: 39.8; control1Y: 274.401; control2X: 19; control2Y: 270.401; x: -6.6; y: 274.401 }
+ PathCubic { control1X: -6.6; control1Y: 274.401; control2X: -35.8; control2Y: 272.801; x: -38.6; y: 264.801 }
+ PathCubic { control1X: -41.4; control1Y: 256.801; control2X: -27.4; control2Y: 241.601; x: -27.4; y: 241.601 }
+ PathCubic { control1X: -27.4; control1Y: 241.601; control2X: -23; control2Y: 233.201; x: -24.2; y: 218.801 }
+ PathCubic { control1X: -25.4; control1Y: 204.401; control2X: -25; control2Y: 176.401; x: -29.8; y: 173.601 }
+ }
+
+ ShapePath {
+ fillColor: "#e5668c"
+ strokeWidth: -1
+ PathMove { x: -7.8; y: 175.601 }
+ PathCubic { control1X: 0.6; control1Y: 194.001; control2X: -29; control2Y: 259.201; x: -29; y: 259.201 }
+ PathCubic { control1X: -31; control1Y: 260.801; control2X: -16.34; control2Y: 266.846; x: -6.2; y: 264.401 }
+ PathCubic { control1X: 4.746; control1Y: 261.763; control2X: 45; control2Y: 266.001; x: 45; y: 266.001 }
+ PathCubic { control1X: 68.6; control1Y: 250.401; control2X: 81.4; control2Y: 206.001; x: 81.4; y: 206.001 }
+ PathCubic { control1X: 81.4; control1Y: 206.001; control2X: 91.801; control2Y: 182.001; x: 74.2; y: 178.801 }
+ PathCubic { control1X: 56.6; control1Y: 175.601; control2X: -7.8; control2Y: 175.601; x: -7.8; y: 175.601 }
+ }
+
+ ShapePath {
+ fillColor: "#b23259"
+ strokeWidth: -1
+ PathMove { x: -9.831; y: 206.497 }
+ PathCubic { control1X: -6.505; control1Y: 193.707; control2X: -4.921; control2Y: 181.906; x: -7.8; y: 175.601 }
+ PathCubic { control1X: -7.8; control1Y: 175.601; control2X: 54.6; control2Y: 182.001; x: 65.8; y: 161.201 }
+ PathCubic { control1X: 70.041; control1Y: 153.326; control2X: 84.801; control2Y: 184.001; x: 84.4; y: 193.601 }
+ PathCubic { control1X: 84.4; control1Y: 193.601; control2X: 21.4; control2Y: 208.001; x: 6.6; y: 196.801 }
+ PathLine { x: -9.831; y: 206.497 }
+ }
+
+ ShapePath {
+ fillColor: "#a5264c"
+ strokeWidth: -1
+ PathMove { x: -5.4; y: 222.801 }
+ PathCubic { control1X: -5.4; control1Y: 222.801; control2X: -3.4; control2Y: 230.001; x: -5.8; y: 234.001 }
+ PathCubic { control1X: -5.8; control1Y: 234.001; control2X: -7.4; control2Y: 234.801; x: -8.6; y: 235.201 }
+ PathCubic { control1X: -8.6; control1Y: 235.201; control2X: -7.4; control2Y: 238.801; x: -1.4; y: 240.401 }
+ PathCubic { control1X: -1.4; control1Y: 240.401; control2X: 0.6; control2Y: 244.801; x: 3; y: 245.201 }
+ PathCubic { control1X: 5.4; control1Y: 245.601; control2X: 10.2; control2Y: 251.201; x: 14.2; y: 250.001 }
+ PathCubic { control1X: 18.2; control1Y: 248.801; control2X: 29.4; control2Y: 244.801; x: 29.4; y: 244.801 }
+ PathCubic { control1X: 29.4; control1Y: 244.801; control2X: 35; control2Y: 241.601; x: 43.8; y: 245.201 }
+ PathCubic { control1X: 43.8; control1Y: 245.201; control2X: 46.175; control2Y: 244.399; x: 46.6; y: 240.401 }
+ PathCubic { control1X: 47.1; control1Y: 235.701; control2X: 50.2; control2Y: 232.001; x: 52.2; y: 230.001 }
+ PathCubic { control1X: 54.2; control1Y: 228.001; control2X: 63.8; control2Y: 215.201; x: 62.6; y: 214.801 }
+ PathCubic { control1X: 61.4; control1Y: 214.401; control2X: -5.4; control2Y: 222.801; x: -5.4; y: 222.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ff727f"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -9.8; y: 174.401 }
+ PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -12.6; control2Y: 196.801; x: -9.4; y: 205.201 }
+ PathCubic { control1X: -6.2; control1Y: 213.601; control2X: -7; control2Y: 215.601; x: -7.8; y: 219.601 }
+ PathCubic { control1X: -8.6; control1Y: 223.601; control2X: -4.2; control2Y: 233.601; x: 1.4; y: 239.601 }
+ PathLine { x: 13.4; y: 241.201 }
+ PathCubic { control1X: 13.4; control1Y: 241.201; control2X: 28.6; control2Y: 237.601; x: 37.8; y: 240.401 }
+ PathCubic { control1X: 37.8; control1Y: 240.401; control2X: 46.794; control2Y: 241.744; x: 50.2; y: 226.801 }
+ PathCubic { control1X: 50.2; control1Y: 226.801; control2X: 55; control2Y: 220.401; x: 62.2; y: 217.601 }
+ PathCubic { control1X: 69.4; control1Y: 214.801; control2X: 76.6; control2Y: 173.201; x: 72.6; y: 165.201 }
+ PathCubic { control1X: 68.6; control1Y: 157.201; control2X: 54.2; control2Y: 152.801; x: 38.2; y: 168.401 }
+ PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -8.2; y: 249.201 }
+ PathCubic { control1X: -8.2; control1Y: 249.201; control2X: -9; control2Y: 247.201; x: -13.4; y: 246.801 }
+ PathCubic { control1X: -13.4; control1Y: 246.801; control2X: -35.8; control2Y: 243.201; x: -44.2; y: 230.801 }
+ PathCubic { control1X: -44.2; control1Y: 230.801; control2X: -51; control2Y: 225.201; x: -46.6; y: 236.801 }
+ PathCubic { control1X: -46.6; control1Y: 236.801; control2X: -36.2; control2Y: 257.201; x: -29.4; y: 260.001 }
+ PathCubic { control1X: -29.4; control1Y: 260.001; control2X: -13; control2Y: 264.001; x: -8.2; y: 249.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cc3f4c"
+ strokeWidth: -1
+ PathMove { x: 71.742; y: 185.229 }
+ PathCubic { control1X: 72.401; control1Y: 177.323; control2X: 74.354; control2Y: 168.709; x: 72.6; y: 165.201 }
+ PathCubic { control1X: 66.154; control1Y: 152.307; control2X: 49.181; control2Y: 157.695; x: 38.2; y: 168.401 }
+ PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 }
+ PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -11.545; control2Y: 188.364; x: -10.705; y: 198.376 }
+ PathCubic { control1X: -10.705; control1Y: 198.376; control2X: 26.6; control2Y: 186.801; x: 27.4; y: 192.401 }
+ PathCubic { control1X: 27.4; control1Y: 192.401; control2X: 29; control2Y: 189.201; x: 38.2; y: 189.201 }
+ PathCubic { control1X: 47.4; control1Y: 189.201; control2X: 70.142; control2Y: 188.029; x: 71.742; y: 185.229 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a51926"
+ strokeWidth: 2
+ PathMove { x: 28.6; y: 175.201 }
+ PathCubic { control1X: 28.6; control1Y: 175.201; control2X: 33.4; control2Y: 180.001; x: 29.8; y: 189.601 }
+ PathCubic { control1X: 29.8; control1Y: 189.601; control2X: 15.4; control2Y: 205.601; x: 17.4; y: 219.601 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -19.4; y: 260.001 }
+ PathCubic { control1X: -19.4; control1Y: 260.001; control2X: -23.8; control2Y: 247.201; x: -15; y: 254.001 }
+ PathCubic { control1X: -15; control1Y: 254.001; control2X: -10.2; control2Y: 256.001; x: -11.4; y: 257.601 }
+ PathCubic { control1X: -12.6; control1Y: 259.201; control2X: -18.2; control2Y: 263.201; x: -19.4; y: 260.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -14.36; y: 261.201 }
+ PathCubic { control1X: -14.36; control1Y: 261.201; control2X: -17.88; control2Y: 250.961; x: -10.84; y: 256.401 }
+ PathCubic { control1X: -10.84; control1Y: 256.401; control2X: -6.419; control2Y: 258.849; x: -7.96; y: 259.281 }
+ PathCubic { control1X: -12.52; control1Y: 260.561; control2X: -7.96; control2Y: 263.121; x: -14.36; y: 261.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -9.56; y: 261.201 }
+ PathCubic { control1X: -9.56; control1Y: 261.201; control2X: -13.08; control2Y: 250.961; x: -6.04; y: 256.401 }
+ PathCubic { control1X: -6.04; control1Y: 256.401; control2X: -1.665; control2Y: 258.711; x: -3.16; y: 259.281 }
+ PathCubic { control1X: -6.52; control1Y: 260.561; control2X: -3.16; control2Y: 263.121; x: -9.56; y: 261.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -2.96; y: 261.401 }
+ PathCubic { control1X: -2.96; control1Y: 261.401; control2X: -6.48; control2Y: 251.161; x: 0.56; y: 256.601 }
+ PathCubic { control1X: 0.56; control1Y: 256.601; control2X: 4.943; control2Y: 258.933; x: 3.441; y: 259.481 }
+ PathCubic { control1X: 0.48; control1Y: 260.561; control2X: 3.441; control2Y: 263.321; x: -2.96; y: 261.401 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: 3.52; y: 261.321 }
+ PathCubic { control1X: 3.52; control1Y: 261.321; control2X: 0; control2Y: 251.081; x: 7.041; y: 256.521 }
+ PathCubic { control1X: 7.041; control1Y: 256.521; control2X: 10.881; control2Y: 258.121; x: 9.921; y: 259.401 }
+ PathCubic { control1X: 8.961; control1Y: 260.681; control2X: 9.921; control2Y: 263.241; x: 3.52; y: 261.321 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: 10.2; y: 262.001 }
+ PathCubic { control1X: 10.2; control1Y: 262.001; control2X: 5.4; control2Y: 249.601; x: 14.6; y: 256.001 }
+ PathCubic { control1X: 14.6; control1Y: 256.001; control2X: 19.4; control2Y: 258.001; x: 18.2; y: 259.601 }
+ PathCubic { control1X: 17; control1Y: 261.201; control2X: 18.2; control2Y: 264.401; x: 10.2; y: 262.001 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ PathMove { x: -18.2; y: 244.801 }
+ PathCubic { control1X: -18.2; control1Y: 244.801; control2X: -5; control2Y: 242.001; x: 1; y: 245.201 }
+ PathCubic { control1X: 1; control1Y: 245.201; control2X: 7; control2Y: 246.401; x: 8.2; y: 246.001 }
+ PathCubic { control1X: 9.4; control1Y: 245.601; control2X: 12.6; control2Y: 245.201; x: 12.6; y: 245.201 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ PathMove { x: 15.8; y: 253.601 }
+ PathCubic { control1X: 15.8; control1Y: 253.601; control2X: 27.8; control2Y: 240.001; x: 39.8; y: 244.401 }
+ PathCubic { control1X: 46.816; control1Y: 246.974; control2X: 45.8; control2Y: 243.601; x: 46.6; y: 240.801 }
+ PathCubic { control1X: 47.4; control1Y: 238.001; control2X: 47.6; control2Y: 233.801; x: 52.6; y: 230.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: 33; y: 237.601 }
+ PathCubic { control1X: 33; control1Y: 237.601; control2X: 29; control2Y: 226.801; x: 26.2; y: 239.601 }
+ PathCubic { control1X: 23.4; control1Y: 252.401; control2X: 20.2; control2Y: 256.001; x: 18.6; y: 258.801 }
+ PathCubic { control1X: 18.6; control1Y: 258.801; control2X: 18.6; control2Y: 264.001; x: 27; y: 263.601 }
+ PathCubic { control1X: 27; control1Y: 263.601; control2X: 37.8; control2Y: 263.201; x: 38.2; y: 260.401 }
+ PathCubic { control1X: 38.6; control1Y: 257.601; control2X: 37; control2Y: 246.001; x: 33; y: 237.601 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ PathMove { x: 47; y: 244.801 }
+ PathCubic { control1X: 47; control1Y: 244.801; control2X: 50.6; control2Y: 242.401; x: 53; y: 243.601 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ PathMove { x: 53.5; y: 228.401 }
+ PathCubic { control1X: 53.5; control1Y: 228.401; control2X: 56.4; control2Y: 223.501; x: 61.2; y: 222.701 }
+ }
+
+ ShapePath {
+ fillColor: "#b2b2b2"
+ strokeWidth: -1
+ PathMove { x: -25.8; y: 265.201 }
+ PathCubic { control1X: -25.8; control1Y: 265.201; control2X: -7.8; control2Y: 268.401; x: -3.4; y: 266.801 }
+ PathCubic { control1X: -3.4; control1Y: 266.801; control2X: 5.4; control2Y: 266.801; x: -3; y: 268.801 }
+ PathCubic { control1X: -3; control1Y: 268.801; control2X: -15.8; control2Y: 268.801; x: -23.8; y: 267.601 }
+ PathCubic { control1X: -23.8; control1Y: 267.601; control2X: -35.4; control2Y: 262.001; x: -25.8; y: 265.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -11.8; y: 172.001 }
+ PathCubic { control1X: -11.8; control1Y: 172.001; control2X: 5.8; control2Y: 172.001; x: 7.8; y: 172.801 }
+ PathCubic { control1X: 7.8; control1Y: 172.801; control2X: 15; control2Y: 203.601; x: 11.4; y: 211.201 }
+ PathCubic { control1X: 11.4; control1Y: 211.201; control2X: 10.2; control2Y: 214.001; x: 7.4; y: 208.401 }
+ PathCubic { control1X: 7.4; control1Y: 208.401; control2X: -11; control2Y: 175.601; x: -14.2; y: 173.601 }
+ PathCubic { control1X: -17.4; control1Y: 171.601; control2X: -13; control2Y: 172.001; x: -11.8; y: 172.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -88.9; y: 169.301 }
+ PathCubic { control1X: -88.9; control1Y: 169.301; control2X: -80; control2Y: 171.001; x: -67.4; y: 173.601 }
+ PathCubic { control1X: -67.4; control1Y: 173.601; control2X: -62.6; control2Y: 196.001; x: -59.4; y: 200.801 }
+ PathCubic { control1X: -56.2; control1Y: 205.601; control2X: -59.8; control2Y: 205.601; x: -63.4; y: 202.801 }
+ PathCubic { control1X: -67; control1Y: 200.001; control2X: -81.8; control2Y: 186.001; x: -83.8; y: 181.601 }
+ PathCubic { control1X: -85.8; control1Y: 177.201; control2X: -88.9; control2Y: 169.301; x: -88.9; y: 169.301 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -67.039; y: 173.818 }
+ PathCubic { control1X: -67.039; control1Y: 173.818; control2X: -61.239; control2Y: 175.366; x: -60.23; y: 177.581 }
+ PathCubic { control1X: -59.222; control1Y: 179.795; control2X: -61.432; control2Y: 183.092; x: -61.432; y: 183.092 }
+ PathCubic { control1X: -61.432; control1Y: 183.092; control2X: -62.432; control2Y: 186.397; x: -63.634; y: 184.235 }
+ PathCubic { control1X: -64.836; control1Y: 182.072; control2X: -67.708; control2Y: 174.412; x: -67.039; y: 173.818 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -67; y: 173.601 }
+ PathCubic { control1X: -67; control1Y: 173.601; control2X: -63.4; control2Y: 178.801; x: -59.8; y: 178.801 }
+ PathCubic { control1X: -56.2; control1Y: 178.801; control2X: -55.818; control2Y: 178.388; x: -53; y: 179.001 }
+ PathCubic { control1X: -48.4; control1Y: 180.001; control2X: -48.8; control2Y: 178.001; x: -42.2; y: 179.201 }
+ PathCubic { control1X: -39.56; control1Y: 179.681; control2X: -37; control2Y: 178.801; x: -34.2; y: 180.001 }
+ PathCubic { control1X: -31.4; control1Y: 181.201; control2X: -28.2; control2Y: 180.401; x: -27; y: 178.401 }
+ PathCubic { control1X: -25.8; control1Y: 176.401; control2X: -21; control2Y: 172.201; x: -21; y: 172.201 }
+ PathCubic { control1X: -21; control1Y: 172.201; control2X: -33.8; control2Y: 174.001; x: -36.6; y: 174.801 }
+ PathCubic { control1X: -36.6; control1Y: 174.801; control2X: -59; control2Y: 176.001; x: -67; y: 173.601 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -22.4; y: 173.801 }
+ PathCubic { control1X: -22.4; control1Y: 173.801; control2X: -28.85; control2Y: 177.301; x: -29.25; y: 179.701 }
+ PathCubic { control1X: -29.65; control1Y: 182.101; control2X: -24; control2Y: 185.801; x: -24; y: 185.801 }
+ PathCubic { control1X: -24; control1Y: 185.801; control2X: -21.25; control2Y: 190.401; x: -20.65; y: 188.001 }
+ PathCubic { control1X: -20.05; control1Y: 185.601; control2X: -21.6; control2Y: 174.201; x: -22.4; y: 173.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -59.885; y: 179.265 }
+ PathCubic { control1X: -59.885; control1Y: 179.265; control2X: -52.878; control2Y: 190.453; x: -52.661; y: 179.242 }
+ PathCubic { control1X: -52.661; control1Y: 179.242; control2X: -52.104; control2Y: 177.984; x: -53.864; y: 177.962 }
+ PathCubic { control1X: -59.939; control1Y: 177.886; control2X: -58.418; control2Y: 173.784; x: -59.885; y: 179.265 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -52.707; y: 179.514 }
+ PathCubic { control1X: -52.707; control1Y: 179.514; control2X: -44.786; control2Y: 190.701; x: -45.422; y: 179.421 }
+ PathCubic { control1X: -45.422; control1Y: 179.421; control2X: -45.415; control2Y: 179.089; x: -47.168; y: 178.936 }
+ PathCubic { control1X: -51.915; control1Y: 178.522; control2X: -51.57; control2Y: 174.004; x: -52.707; y: 179.514 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -45.494; y: 179.522 }
+ PathCubic { control1X: -45.494; control1Y: 179.522; control2X: -37.534; control2Y: 190.15; x: -38.203; y: 180.484 }
+ PathCubic { control1X: -38.203; control1Y: 180.484; control2X: -38.084; control2Y: 179.251; x: -39.738; y: 178.95 }
+ PathCubic { control1X: -43.63; control1Y: 178.244; control2X: -43.841; control2Y: 174.995; x: -45.494; y: 179.522 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ PathMove { x: -38.618; y: 179.602 }
+ PathCubic { control1X: -38.618; control1Y: 179.602; control2X: -30.718; control2Y: 191.163; x: -30.37; y: 181.382 }
+ PathCubic { control1X: -30.37; control1Y: 181.382; control2X: -28.726; control2Y: 180.004; x: -30.472; y: 179.782 }
+ PathCubic { control1X: -36.29; control1Y: 179.042; control2X: -35.492; control2Y: 174.588; x: -38.618; y: 179.602 }
+ }
+
+ ShapePath {
+ fillColor: "#e5e5b2"
+ strokeWidth: -1
+ PathMove { x: -74.792; y: 183.132 }
+ PathLine { x: -82.45; y: 181.601 }
+ PathCubic { control1X: -85.05; control1Y: 176.601; control2X: -87.15; control2Y: 170.451; x: -87.15; y: 170.451 }
+ PathCubic { control1X: -87.15; control1Y: 170.451; control2X: -80.8; control2Y: 171.451; x: -68.3; y: 174.251 }
+ PathCubic { control1X: -68.3; control1Y: 174.251; control2X: -67.424; control2Y: 177.569; x: -65.952; y: 183.364 }
+ PathLine { x: -74.792; y: 183.132 }
+ }
+
+ ShapePath {
+ fillColor: "#e5e5b2"
+ strokeWidth: -1
+ PathMove { x: -9.724; y: 178.47 }
+ PathCubic { control1X: -11.39; control1Y: 175.964; control2X: -12.707; control2Y: 174.206; x: -13.357; y: 173.8 }
+ PathCubic { control1X: -16.37; control1Y: 171.917; control2X: -12.227; control2Y: 172.294; x: -11.098; y: 172.294 }
+ PathCubic { control1X: -11.098; control1Y: 172.294; control2X: 5.473; control2Y: 172.294; x: 7.356; y: 173.047 }
+ PathCubic { control1X: 7.356; control1Y: 173.047; control2X: 7.88; control2Y: 175.289; x: 8.564; y: 178.68 }
+ PathCubic { control1X: 8.564; control1Y: 178.68; control2X: -1.524; control2Y: 176.67; x: -9.724; y: 178.47 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 43.88; y: 40.321 }
+ PathCubic { control1X: 71.601; control1Y: 44.281; control2X: 97.121; control2Y: 8.641; x: 98.881; y: -1.04 }
+ PathCubic { control1X: 100.641; control1Y: -10.72; control2X: 90.521; control2Y: -22.6; x: 90.521; y: -22.6 }
+ PathCubic { control1X: 91.841; control1Y: -25.68; control2X: 87.001; control2Y: -39.76; x: 81.721; y: -49 }
+ PathCubic { control1X: 76.441; control1Y: -58.24; control2X: 60.54; control2Y: -57.266; x: 43; y: -58.24 }
+ PathCubic { control1X: 27.16; control1Y: -59.12; control2X: 8.68; control2Y: -35.8; x: 7.36; y: -34.04 }
+ PathCubic { control1X: 6.04; control1Y: -32.28; control2X: 12.2; control2Y: 6.001; x: 13.52; y: 11.721 }
+ PathCubic { control1X: 14.84; control1Y: 17.441; control2X: 12.2; control2Y: 43.841; x: 12.2; y: 43.841 }
+ PathCubic { control1X: 46.44; control1Y: 34.741; control2X: 16.16; control2Y: 36.361; x: 43.88; y: 40.321 }
+ }
+
+ ShapePath {
+ fillColor: "#ea8e51"
+ strokeWidth: -1
+ PathMove { x: 8.088; y: -33.392 }
+ PathCubic { control1X: 6.792; control1Y: -31.664; control2X: 12.84; control2Y: 5.921; x: 14.136; y: 11.537 }
+ PathCubic { control1X: 15.432; control1Y: 17.153; control2X: 12.84; control2Y: 43.073; x: 12.84; y: 43.073 }
+ PathCubic { control1X: 45.512; control1Y: 34.193; control2X: 16.728; control2Y: 35.729; x: 43.944; y: 39.617 }
+ PathCubic { control1X: 71.161; control1Y: 43.505; control2X: 96.217; control2Y: 8.513; x: 97.945; y: -0.992 }
+ PathCubic { control1X: 99.673; control1Y: -10.496; control2X: 89.737; control2Y: -22.16; x: 89.737; y: -22.16 }
+ PathCubic { control1X: 91.033; control1Y: -25.184; control2X: 86.281; control2Y: -39.008; x: 81.097; y: -48.08 }
+ PathCubic { control1X: 75.913; control1Y: -57.152; control2X: 60.302; control2Y: -56.195; x: 43.08; y: -57.152 }
+ PathCubic { control1X: 27.528; control1Y: -58.016; control2X: 9.384; control2Y: -35.12; x: 8.088; y: -33.392 }
+ }
+
+ ShapePath {
+ fillColor: "#efaa7c"
+ strokeWidth: -1
+ PathMove { x: 8.816; y: -32.744 }
+ PathCubic { control1X: 7.544; control1Y: -31.048; control2X: 13.48; control2Y: 5.841; x: 14.752; y: 11.353 }
+ PathCubic { control1X: 16.024; control1Y: 16.865; control2X: 13.48; control2Y: 42.305; x: 13.48; y: 42.305 }
+ PathCubic { control1X: 44.884; control1Y: 33.145; control2X: 17.296; control2Y: 35.097; x: 44.008; y: 38.913 }
+ PathCubic { control1X: 70.721; control1Y: 42.729; control2X: 95.313; control2Y: 8.385; x: 97.009; y: -0.944 }
+ PathCubic { control1X: 98.705; control1Y: -10.272; control2X: 88.953; control2Y: -21.72; x: 88.953; y: -21.72 }
+ PathCubic { control1X: 90.225; control1Y: -24.688; control2X: 85.561; control2Y: -38.256; x: 80.473; y: -47.16 }
+ PathCubic { control1X: 75.385; control1Y: -56.064; control2X: 60.063; control2Y: -55.125; x: 43.16; y: -56.064 }
+ PathCubic { control1X: 27.896; control1Y: -56.912; control2X: 10.088; control2Y: -34.44; x: 8.816; y: -32.744 }
+ }
+
+ ShapePath {
+ fillColor: "#f4c6a8"
+ strokeWidth: -1
+ PathMove { x: 9.544; y: -32.096 }
+ PathCubic { control1X: 8.296; control1Y: -30.432; control2X: 14.12; control2Y: 5.761; x: 15.368; y: 11.169 }
+ PathCubic { control1X: 16.616; control1Y: 16.577; control2X: 14.12; control2Y: 41.537; x: 14.12; y: 41.537 }
+ PathCubic { control1X: 43.556; control1Y: 32.497; control2X: 17.864; control2Y: 34.465; x: 44.072; y: 38.209 }
+ PathCubic { control1X: 70.281; control1Y: 41.953; control2X: 94.409; control2Y: 8.257; x: 96.073; y: -0.895 }
+ PathCubic { control1X: 97.737; control1Y: -10.048; control2X: 88.169; control2Y: -21.28; x: 88.169; y: -21.28 }
+ PathCubic { control1X: 89.417; control1Y: -24.192; control2X: 84.841; control2Y: -37.504; x: 79.849; y: -46.24 }
+ PathCubic { control1X: 74.857; control1Y: -54.976; control2X: 59.824; control2Y: -54.055; x: 43.24; y: -54.976 }
+ PathCubic { control1X: 28.264; control1Y: -55.808; control2X: 10.792; control2Y: -33.76; x: 9.544; y: -32.096 }
+ }
+
+ ShapePath {
+ fillColor: "#f9e2d3"
+ strokeWidth: -1
+ PathMove { x: 10.272; y: -31.448 }
+ PathCubic { control1X: 9.048; control1Y: -29.816; control2X: 14.76; control2Y: 5.681; x: 15.984; y: 10.985 }
+ PathCubic { control1X: 17.208; control1Y: 16.289; control2X: 14.76; control2Y: 40.769; x: 14.76; y: 40.769 }
+ PathCubic { control1X: 42.628; control1Y: 31.849; control2X: 18.432; control2Y: 33.833; x: 44.136; y: 37.505 }
+ PathCubic { control1X: 69.841; control1Y: 41.177; control2X: 93.505; control2Y: 8.129; x: 95.137; y: -0.848 }
+ PathCubic { control1X: 96.769; control1Y: -9.824; control2X: 87.385; control2Y: -20.84; x: 87.385; y: -20.84 }
+ PathCubic { control1X: 88.609; control1Y: -23.696; control2X: 84.121; control2Y: -36.752; x: 79.225; y: -45.32 }
+ PathCubic { control1X: 74.329; control1Y: -53.888; control2X: 59.585; control2Y: -52.985; x: 43.32; y: -53.888 }
+ PathCubic { control1X: 28.632; control1Y: -54.704; control2X: 11.496; control2Y: -33.08; x: 10.272; y: -31.448 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 44.2; y: 36.8 }
+ PathCubic { control1X: 69.4; control1Y: 40.4; control2X: 92.601; control2Y: 8; x: 94.201; y: -0.8 }
+ PathCubic { control1X: 95.801; control1Y: -9.6; control2X: 86.601; control2Y: -20.4; x: 86.601; y: -20.4 }
+ PathCubic { control1X: 87.801; control1Y: -23.2; control2X: 83.4; control2Y: -36; x: 78.6; y: -44.4 }
+ PathCubic { control1X: 73.8; control1Y: -52.8; control2X: 59.346; control2Y: -51.914; x: 43.4; y: -52.8 }
+ PathCubic { control1X: 29; control1Y: -53.6; control2X: 12.2; control2Y: -32.4; x: 11; y: -30.8 }
+ PathCubic { control1X: 9.8; control1Y: -29.2; control2X: 15.4; control2Y: 5.6; x: 16.6; y: 10.8 }
+ PathCubic { control1X: 17.8; control1Y: 16; control2X: 15.4; control2Y: 40; x: 15.4; y: 40 }
+ PathCubic { control1X: 40.9; control1Y: 31.4; control2X: 19; control2Y: 33.2; x: 44.2; y: 36.8 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 90.601; y: 2.8 }
+ PathCubic { control1X: 90.601; control1Y: 2.8; control2X: 62.8; control2Y: 10.4; x: 51.2; y: 8.8 }
+ PathCubic { control1X: 51.2; control1Y: 8.8; control2X: 35.4; control2Y: 2.2; x: 26.6; y: 24 }
+ PathCubic { control1X: 26.6; control1Y: 24; control2X: 23; control2Y: 31.2; x: 21; y: 33.2 }
+ PathCubic { control1X: 19; control1Y: 35.2; control2X: 90.601; control2Y: 2.8; x: 90.601; y: 2.8 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 94.401; y: 0.6 }
+ PathCubic { control1X: 94.401; control1Y: 0.6; control2X: 65.4; control2Y: 12.8; x: 55.4; y: 12.4 }
+ PathCubic { control1X: 55.4; control1Y: 12.4; control2X: 39; control2Y: 7.8; x: 30.6; y: 22.4 }
+ PathCubic { control1X: 30.6; control1Y: 22.4; control2X: 22.2; control2Y: 31.6; x: 19; y: 33.2 }
+ PathCubic { control1X: 19; control1Y: 33.2; control2X: 18.6; control2Y: 34.8; x: 25; y: 30.8 }
+ PathLine { x: 35.4; y: 36 }
+ PathCubic { control1X: 35.4; control1Y: 36; control2X: 50.2; control2Y: 45.6; x: 59.8; y: 29.6 }
+ PathCubic { control1X: 59.8; control1Y: 29.6; control2X: 63.8; control2Y: 18.4; x: 63.8; y: 16.4 }
+ PathCubic { control1X: 63.8; control1Y: 14.4; control2X: 85; control2Y: 8.8; x: 86.601; y: 8.4 }
+ PathCubic { control1X: 88.201; control1Y: 8; control2X: 94.801; control2Y: 3.8; x: 94.401; y: 0.6 }
+ }
+
+ ShapePath {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ PathMove { x: 47; y: 36.514 }
+ PathCubic { control1X: 40.128; control1Y: 36.514; control2X: 31.755; control2Y: 32.649; x: 31.755; y: 26.4 }
+ PathCubic { control1X: 31.755; control1Y: 20.152; control2X: 40.128; control2Y: 13.887; x: 47; y: 13.887 }
+ PathCubic { control1X: 53.874; control1Y: 13.887; control2X: 59.446; control2Y: 18.952; x: 59.446; y: 25.2 }
+ PathCubic { control1X: 59.446; control1Y: 31.449; control2X: 53.874; control2Y: 36.514; x: 47; y: 36.514 }
+ }
+
+ ShapePath {
+ fillColor: "#659900"
+ strokeWidth: -1
+ PathMove { x: 43.377; y: 19.83 }
+ PathCubic { control1X: 38.531; control1Y: 20.552; control2X: 33.442; control2Y: 22.055; x: 33.514; y: 21.839 }
+ PathCubic { control1X: 35.054; control1Y: 17.22; control2X: 41.415; control2Y: 13.887; x: 47; y: 13.887 }
+ PathCubic { control1X: 51.296; control1Y: 13.887; control2X: 55.084; control2Y: 15.865; x: 57.32; y: 18.875 }
+ PathCubic { control1X: 57.32; control1Y: 18.875; control2X: 52.004; control2Y: 18.545; x: 43.377; y: 19.83 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 55.4; y: 19.6 }
+ PathCubic { control1X: 55.4; control1Y: 19.6; control2X: 51; control2Y: 16.4; x: 51; y: 18.6 }
+ PathCubic { control1X: 51; control1Y: 18.6; control2X: 54.6; control2Y: 23; x: 55.4; y: 19.6 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 45.4; y: 27.726 }
+ PathCubic { control1X: 42.901; control1Y: 27.726; control2X: 40.875; control2Y: 25.7; x: 40.875; y: 23.2 }
+ PathCubic { control1X: 40.875; control1Y: 20.701; control2X: 42.901; control2Y: 18.675; x: 45.4; y: 18.675 }
+ PathCubic { control1X: 47.9; control1Y: 18.675; control2X: 49.926; control2Y: 20.701; x: 49.926; y: 23.2 }
+ PathCubic { control1X: 49.926; control1Y: 25.7; control2X: 47.9; control2Y: 27.726; x: 45.4; y: 27.726 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: -58.6; y: 14.4 }
+ PathCubic { control1X: -58.6; control1Y: 14.4; control2X: -61.8; control2Y: -6.8; x: -59.4; y: -11.2 }
+ PathCubic { control1X: -59.4; control1Y: -11.2; control2X: -48.6; control2Y: -21.2; x: -49; y: -24.8 }
+ PathCubic { control1X: -49; control1Y: -24.8; control2X: -49.4; control2Y: -42.8; x: -50.6; y: -43.6 }
+ PathCubic { control1X: -51.8; control1Y: -44.4; control2X: -59.4; control2Y: -50.4; x: -65.4; y: -44 }
+ PathCubic { control1X: -65.4; control1Y: -44; control2X: -75.8; control2Y: -26; x: -75; y: -19.6 }
+ PathLine { x: -75; y: -17.6 }
+ PathCubic { control1X: -75; control1Y: -17.6; control2X: -82.6; control2Y: -18; x: -84.2; y: -16 }
+ PathCubic { control1X: -84.2; control1Y: -16; control2X: -85.4; control2Y: -10.8; x: -86.6; y: -10.4 }
+ PathCubic { control1X: -86.6; control1Y: -10.4; control2X: -89.4; control2Y: -8; x: -87.4; y: -5.2 }
+ PathCubic { control1X: -87.4; control1Y: -5.2; control2X: -89.4; control2Y: -2.8; x: -89; y: 1.2 }
+ PathLine { x: -81.4; y: 5.2 }
+ PathCubic { control1X: -81.4; control1Y: 5.2; control2X: -79.4; control2Y: 19.6; x: -68.6; y: 24.8 }
+ PathCubic { control1X: -63.764; control1Y: 27.129; control2X: -60.6; control2Y: 20.4; x: -58.6; y: 14.4 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: -59.6; y: 12.56 }
+ PathCubic { control1X: -59.6; control1Y: 12.56; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 }
+ PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 }
+ PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 }
+ PathCubic { control1X: -53.48; control1Y: -40.36; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 }
+ PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 }
+ PathLine { x: -74.36; y: -16.24 }
+ PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 }
+ PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 }
+ PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 }
+ PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 }
+ PathLine { x: -80.12; y: 4.28 }
+ PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 }
+ PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.96; x: -59.6; y: 12.56 }
+ }
+
+ ShapePath {
+ fillColor: "#eb955c"
+ strokeWidth: -1
+ PathMove { x: -51.05; y: -42.61 }
+ PathCubic { control1X: -52.14; control1Y: -43.47; control2X: -59.63; control2Y: -49.24; x: -65.48; y: -43 }
+ PathCubic { control1X: -65.48; control1Y: -43; control2X: -75.62; control2Y: -25.45; x: -74.84; y: -19.21 }
+ PathLine { x: -74.84; y: -17.26 }
+ PathCubic { control1X: -74.84; control1Y: -17.26; control2X: -82.25; control2Y: -17.65; x: -83.81; y: -15.7 }
+ PathCubic { control1X: -83.81; control1Y: -15.7; control2X: -84.98; control2Y: -10.63; x: -86.15; y: -10.24 }
+ PathCubic { control1X: -86.15; control1Y: -10.24; control2X: -88.88; control2Y: -7.9; x: -86.93; y: -5.17 }
+ PathCubic { control1X: -86.93; control1Y: -5.17; control2X: -88.88; control2Y: -2.83; x: -88.49; y: 1.07 }
+ PathLine { x: -81.08; y: 4.97 }
+ PathCubic { control1X: -81.08; control1Y: 4.97; control2X: -79.13; control2Y: 19.01; x: -68.6; y: 24.08 }
+ PathCubic { control1X: -63.886; control1Y: 26.35; control2X: -60.8; control2Y: 19.79; x: -58.85; y: 13.94 }
+ PathCubic { control1X: -58.85; control1Y: 13.94; control2X: -61.97; control2Y: -6.73; x: -59.63; y: -11.02 }
+ PathCubic { control1X: -59.63; control1Y: -11.02; control2X: -49.1; control2Y: -20.77; x: -49.49; y: -24.28 }
+ PathCubic { control1X: -49.49; control1Y: -24.28; control2X: -49.88; control2Y: -41.83; x: -51.05; y: -42.61 }
+ }
+
+ ShapePath {
+ fillColor: "#f2b892"
+ strokeWidth: -1
+ PathMove { x: -51.5; y: -41.62 }
+ PathCubic { control1X: -52.48; control1Y: -42.54; control2X: -59.86; control2Y: -48.08; x: -65.56; y: -42 }
+ PathCubic { control1X: -65.56; control1Y: -42; control2X: -75.44; control2Y: -24.9; x: -74.68; y: -18.82 }
+ PathLine { x: -74.68; y: -16.92 }
+ PathCubic { control1X: -74.68; control1Y: -16.92; control2X: -81.9; control2Y: -17.3; x: -83.42; y: -15.4 }
+ PathCubic { control1X: -83.42; control1Y: -15.4; control2X: -84.56; control2Y: -10.46; x: -85.7; y: -10.08 }
+ PathCubic { control1X: -85.7; control1Y: -10.08; control2X: -88.36; control2Y: -7.8; x: -86.46; y: -5.14 }
+ PathCubic { control1X: -86.46; control1Y: -5.14; control2X: -88.36; control2Y: -2.86; x: -87.98; y: 0.94 }
+ PathLine { x: -80.76; y: 4.74 }
+ PathCubic { control1X: -80.76; control1Y: 4.74; control2X: -78.86; control2Y: 18.42; x: -68.6; y: 23.36 }
+ PathCubic { control1X: -64.006; control1Y: 25.572; control2X: -61; control2Y: 19.18; x: -59.1; y: 13.48 }
+ PathCubic { control1X: -59.1; control1Y: 13.48; control2X: -62.14; control2Y: -6.66; x: -59.86; y: -10.84 }
+ PathCubic { control1X: -59.86; control1Y: -10.84; control2X: -49.6; control2Y: -20.34; x: -49.98; y: -23.76 }
+ PathCubic { control1X: -49.98; control1Y: -23.76; control2X: -50.36; control2Y: -40.86; x: -51.5; y: -41.62 }
+ }
+
+ ShapePath {
+ fillColor: "#f8dcc8"
+ strokeWidth: -1
+ PathMove { x: -51.95; y: -40.63 }
+ PathCubic { control1X: -52.82; control1Y: -41.61; control2X: -60.09; control2Y: -46.92; x: -65.64; y: -41 }
+ PathCubic { control1X: -65.64; control1Y: -41; control2X: -75.26; control2Y: -24.35; x: -74.52; y: -18.43 }
+ PathLine { x: -74.52; y: -16.58 }
+ PathCubic { control1X: -74.52; control1Y: -16.58; control2X: -81.55; control2Y: -16.95; x: -83.03; y: -15.1 }
+ PathCubic { control1X: -83.03; control1Y: -15.1; control2X: -84.14; control2Y: -10.29; x: -85.25; y: -9.92 }
+ PathCubic { control1X: -85.25; control1Y: -9.92; control2X: -87.84; control2Y: -7.7; x: -85.99; y: -5.11 }
+ PathCubic { control1X: -85.99; control1Y: -5.11; control2X: -87.84; control2Y: -2.89; x: -87.47; y: 0.81 }
+ PathLine { x: -80.44; y: 4.51 }
+ PathCubic { control1X: -80.44; control1Y: 4.51; control2X: -78.59; control2Y: 17.83; x: -68.6; y: 22.64 }
+ PathCubic { control1X: -64.127; control1Y: 24.794; control2X: -61.2; control2Y: 18.57; x: -59.35; y: 13.02 }
+ PathCubic { control1X: -59.35; control1Y: 13.02; control2X: -62.31; control2Y: -6.59; x: -60.09; y: -10.66 }
+ PathCubic { control1X: -60.09; control1Y: -10.66; control2X: -50.1; control2Y: -19.91; x: -50.47; y: -23.24 }
+ PathCubic { control1X: -50.47; control1Y: -23.24; control2X: -50.84; control2Y: -39.89; x: -51.95; y: -40.63 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: -59.6; y: 12.46 }
+ PathCubic { control1X: -59.6; control1Y: 12.46; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 }
+ PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 }
+ PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 }
+ PathCubic { control1X: -53.16; control1Y: -40.68; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 }
+ PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 }
+ PathLine { x: -74.36; y: -16.24 }
+ PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 }
+ PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 }
+ PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 }
+ PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 }
+ PathLine { x: -80.12; y: 4.28 }
+ PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 }
+ PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.86; x: -59.6; y: 12.46 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -62.7; y: 6.2 }
+ PathCubic { control1X: -62.7; control1Y: 6.2; control2X: -84.3; control2Y: -4; x: -85.2; y: -4.8 }
+ PathCubic { control1X: -85.2; control1Y: -4.8; control2X: -76.1; control2Y: 3.4; x: -75.3; y: 3.4 }
+ PathCubic { control1X: -74.5; control1Y: 3.4; control2X: -62.7; control2Y: 6.2; x: -62.7; y: 6.2 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -79.8; y: 0 }
+ PathCubic { control1X: -79.8; control1Y: 0; control2X: -61.4; control2Y: 3.6; x: -61.4; y: 8 }
+ PathCubic { control1X: -61.4; control1Y: 10.912; control2X: -61.643; control2Y: 24.331; x: -67; y: 22.8 }
+ PathCubic { control1X: -75.4; control1Y: 20.4; control2X: -71.8; control2Y: 6; x: -79.8; y: 0 }
+ }
+
+ ShapePath {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ PathMove { x: -71.4; y: 3.8 }
+ PathCubic { control1X: -71.4; control1Y: 3.8; control2X: -62.422; control2Y: 5.274; x: -61.4; y: 8 }
+ PathCubic { control1X: -60.8; control1Y: 9.6; control2X: -60.137; control2Y: 17.908; x: -65.6; y: 19 }
+ PathCubic { control1X: -70.152; control1Y: 19.911; control2X: -72.382; control2Y: 9.69; x: -71.4; y: 3.8 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 14.595; y: 46.349 }
+ PathCubic { control1X: 14.098; control1Y: 44.607; control2X: 15.409; control2Y: 44.738; x: 17.2; y: 44.2 }
+ PathCubic { control1X: 19.2; control1Y: 43.6; control2X: 31.4; control2Y: 39.8; x: 32.2; y: 37.2 }
+ PathCubic { control1X: 33; control1Y: 34.6; control2X: 46.2; control2Y: 39; x: 46.2; y: 39 }
+ PathCubic { control1X: 48; control1Y: 39.8; control2X: 52.4; control2Y: 42.4; x: 52.4; y: 42.4 }
+ PathCubic { control1X: 57.2; control1Y: 43.6; control2X: 63.8; control2Y: 44; x: 63.8; y: 44 }
+ PathCubic { control1X: 66.2; control1Y: 45; control2X: 69.6; control2Y: 47.8; x: 69.6; y: 47.8 }
+ PathCubic { control1X: 84.2; control1Y: 58; control2X: 96.601; control2Y: 50.8; x: 96.601; y: 50.8 }
+ PathCubic { control1X: 116.601; control1Y: 44.2; control2X: 110.601; control2Y: 27; x: 110.601; y: 27 }
+ PathCubic { control1X: 107.601; control1Y: 18; control2X: 110.801; control2Y: 14.6; x: 110.801; y: 14.6 }
+ PathCubic { control1X: 111.001; control1Y: 10.8; control2X: 118.201; control2Y: 17.2; x: 118.201; y: 17.2 }
+ PathCubic { control1X: 120.801; control1Y: 21.4; control2X: 121.601; control2Y: 26.4; x: 121.601; y: 26.4 }
+ PathCubic { control1X: 129.601; control1Y: 37.6; control2X: 126.201; control2Y: 19.8; x: 126.201; y: 19.8 }
+ PathCubic { control1X: 126.401; control1Y: 18.8; control2X: 123.601; control2Y: 15.2; x: 123.601; y: 14 }
+ PathCubic { control1X: 123.601; control1Y: 12.8; control2X: 121.801; control2Y: 9.4; x: 121.801; y: 9.4 }
+ PathCubic { control1X: 118.801; control1Y: 6; control2X: 121.201; control2Y: -1; x: 121.201; y: -1 }
+ PathCubic { control1X: 123.001; control1Y: -14.8; control2X: 120.801; control2Y: -13; x: 120.801; y: -13 }
+ PathCubic { control1X: 119.601; control1Y: -14.8; control2X: 110.401; control2Y: -4.8; x: 110.401; y: -4.8 }
+ PathCubic { control1X: 108.201; control1Y: -1.4; control2X: 102.201; control2Y: 0.2; x: 102.201; y: 0.2 }
+ PathCubic { control1X: 99.401; control1Y: 2; control2X: 96.001; control2Y: 0.6; x: 96.001; y: 0.6 }
+ PathCubic { control1X: 93.401; control1Y: 0.2; control2X: 87.801; control2Y: 7.2; x: 87.801; y: 7.2 }
+ PathCubic { control1X: 90.601; control1Y: 7; control2X: 93.001; control2Y: 11.4; x: 95.401; y: 11.6 }
+ PathCubic { control1X: 97.801; control1Y: 11.8; control2X: 99.601; control2Y: 9.2; x: 101.201; y: 8.6 }
+ PathCubic { control1X: 102.801; control1Y: 8; control2X: 105.601; control2Y: 13.8; x: 105.601; y: 13.8 }
+ PathCubic { control1X: 106.001; control1Y: 16.4; control2X: 100.401; control2Y: 21.2; x: 100.401; y: 21.2 }
+ PathCubic { control1X: 100.001; control1Y: 25.8; control2X: 98.401; control2Y: 24.2; x: 98.401; y: 24.2 }
+ PathCubic { control1X: 95.401; control1Y: 23.6; control2X: 94.201; control2Y: 27.4; x: 93.201; y: 32 }
+ PathCubic { control1X: 92.201; control1Y: 36.6; control2X: 88.001; control2Y: 37; x: 88.001; y: 37 }
+ PathCubic { control1X: 86.401; control1Y: 44.4; control2X: 85.2; control2Y: 41.4; x: 85.2; y: 41.4 }
+ PathCubic { control1X: 85; control1Y: 35.8; control2X: 79; control2Y: 41.6; x: 79; y: 41.6 }
+ PathCubic { control1X: 77.8; control1Y: 43.6; control2X: 73.2; control2Y: 41.4; x: 73.2; y: 41.4 }
+ PathCubic { control1X: 66.4; control1Y: 39.4; control2X: 68.8; control2Y: 37.4; x: 68.8; y: 37.4 }
+ PathCubic { control1X: 70.6; control1Y: 35.2; control2X: 81.8; control2Y: 37.4; x: 81.8; y: 37.4 }
+ PathCubic { control1X: 84; control1Y: 35.8; control2X: 76; control2Y: 31.8; x: 76; y: 31.8 }
+ PathCubic { control1X: 75.4; control1Y: 30; control2X: 76.4; control2Y: 25.6; x: 76.4; y: 25.6 }
+ PathCubic { control1X: 77.6; control1Y: 22.4; control2X: 84.4; control2Y: 16.8; x: 84.4; y: 16.8 }
+ PathCubic { control1X: 93.801; control1Y: 15.6; control2X: 91.001; control2Y: 14; x: 91.001; y: 14 }
+ PathCubic { control1X: 84.801; control1Y: 8.8; control2X: 79; control2Y: 16.4; x: 79; y: 16.4 }
+ PathCubic { control1X: 76.8; control1Y: 22.6; control2X: 59.4; control2Y: 37.6; x: 59.4; y: 37.6 }
+ PathCubic { control1X: 54.6; control1Y: 41; control2X: 57.2; control2Y: 34.2; x: 53.2; y: 37.6 }
+ PathCubic { control1X: 49.2; control1Y: 41; control2X: 28.6; control2Y: 32; x: 28.6; y: 32 }
+ PathCubic { control1X: 17.038; control1Y: 30.807; control2X: 14.306; control2Y: 46.549; x: 10.777; y: 43.429 }
+ PathCubic { control1X: 10.777; control1Y: 43.429; control2X: 16.195; control2Y: 51.949; x: 14.595; y: 46.349 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 209.401; y: -120 }
+ PathCubic { control1X: 209.401; control1Y: -120; control2X: 183.801; control2Y: -112; x: 181.001; y: -93.2 }
+ PathCubic { control1X: 181.001; control1Y: -93.2; control2X: 178.601; control2Y: -70.4; x: 199.001; y: -52.8 }
+ PathCubic { control1X: 199.001; control1Y: -52.8; control2X: 199.401; control2Y: -46.4; x: 201.401; y: -43.2 }
+ PathCubic { control1X: 201.401; control1Y: -43.2; control2X: 199.801; control2Y: -38.4; x: 218.601; y: -46 }
+ PathLine { x: 245.801; y: -54.4 }
+ PathCubic { control1X: 245.801; control1Y: -54.4; control2X: 252.201; control2Y: -56.8; x: 257.401; y: -65.6 }
+ PathCubic { control1X: 262.601; control1Y: -74.4; control2X: 277.801; control2Y: -93.2; x: 274.201; y: -118.4 }
+ PathCubic { control1X: 274.201; control1Y: -118.4; control2X: 275.401; control2Y: -129.6; x: 269.401; y: -130 }
+ PathCubic { control1X: 269.401; control1Y: -130; control2X: 261.001; control2Y: -131.6; x: 253.801; y: -124 }
+ PathCubic { control1X: 253.801; control1Y: -124; control2X: 247.001; control2Y: -120.8; x: 244.601; y: -121.2 }
+ PathLine { x: 209.401; y: -120 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 264.022; y: -120.99 }
+ PathCubic { control1X: 264.022; control1Y: -120.99; control2X: 266.122; control2Y: -129.92; x: 261.282; y: -125.08 }
+ PathCubic { control1X: 261.282; control1Y: -125.08; control2X: 254.242; control2Y: -119.36; x: 246.761; y: -119.36 }
+ PathCubic { control1X: 246.761; control1Y: -119.36; control2X: 232.241; control2Y: -117.16; x: 227.841; y: -103.96 }
+ PathCubic { control1X: 227.841; control1Y: -103.96; control2X: 223.881; control2Y: -77.12; x: 231.801; y: -71.4 }
+ PathCubic { control1X: 231.801; control1Y: -71.4; control2X: 236.641; control2Y: -63.92; x: 243.681; y: -70.52 }
+ PathCubic { control1X: 250.722; control1Y: -77.12; control2X: 266.222; control2Y: -107.35; x: 264.022; y: -120.99 }
+ }
+
+ ShapePath {
+ fillColor: "#323232"
+ strokeWidth: -1
+ PathMove { x: 263.648; y: -120.632 }
+ PathCubic { control1X: 263.648; control1Y: -120.632; control2X: 265.738; control2Y: -129.376; x: 260.986; y: -124.624 }
+ PathCubic { control1X: 260.986; control1Y: -124.624; control2X: 254.074; control2Y: -119.008; x: 246.729; y: -119.008 }
+ PathCubic { control1X: 246.729; control1Y: -119.008; control2X: 232.473; control2Y: -116.848; x: 228.153; y: -103.888 }
+ PathCubic { control1X: 228.153; control1Y: -103.888; control2X: 224.265; control2Y: -77.536; x: 232.041; y: -71.92 }
+ PathCubic { control1X: 232.041; control1Y: -71.92; control2X: 236.793; control2Y: -64.576; x: 243.705; y: -71.056 }
+ PathCubic { control1X: 250.618; control1Y: -77.536; control2X: 265.808; control2Y: -107.24; x: 263.648; y: -120.632 }
+ }
+
+ ShapePath {
+ fillColor: "#666666"
+ strokeWidth: -1
+ PathMove { x: 263.274; y: -120.274 }
+ PathCubic { control1X: 263.274; control1Y: -120.274; control2X: 265.354; control2Y: -128.832; x: 260.69; y: -124.168 }
+ PathCubic { control1X: 260.69; control1Y: -124.168; control2X: 253.906; control2Y: -118.656; x: 246.697; y: -118.656 }
+ PathCubic { control1X: 246.697; control1Y: -118.656; control2X: 232.705; control2Y: -116.536; x: 228.465; y: -103.816 }
+ PathCubic { control1X: 228.465; control1Y: -103.816; control2X: 224.649; control2Y: -77.952; x: 232.281; y: -72.44 }
+ PathCubic { control1X: 232.281; control1Y: -72.44; control2X: 236.945; control2Y: -65.232; x: 243.729; y: -71.592 }
+ PathCubic { control1X: 250.514; control1Y: -77.952; control2X: 265.394; control2Y: -107.13; x: 263.274; y: -120.274 }
+ }
+
+ ShapePath {
+ fillColor: "#999999"
+ strokeWidth: -1
+ PathMove { x: 262.9; y: -119.916 }
+ PathCubic { control1X: 262.9; control1Y: -119.916; control2X: 264.97; control2Y: -128.288; x: 260.394; y: -123.712 }
+ PathCubic { control1X: 260.394; control1Y: -123.712; control2X: 253.738; control2Y: -118.304; x: 246.665; y: -118.304 }
+ PathCubic { control1X: 246.665; control1Y: -118.304; control2X: 232.937; control2Y: -116.224; x: 228.777; y: -103.744 }
+ PathCubic { control1X: 228.777; control1Y: -103.744; control2X: 225.033; control2Y: -78.368; x: 232.521; y: -72.96 }
+ PathCubic { control1X: 232.521; control1Y: -72.96; control2X: 237.097; control2Y: -65.888; x: 243.753; y: -72.128 }
+ PathCubic { control1X: 250.41; control1Y: -78.368; control2X: 264.98; control2Y: -107.02; x: 262.9; y: -119.916 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 262.526; y: -119.558 }
+ PathCubic { control1X: 262.526; control1Y: -119.558; control2X: 264.586; control2Y: -127.744; x: 260.098; y: -123.256 }
+ PathCubic { control1X: 260.098; control1Y: -123.256; control2X: 253.569; control2Y: -117.952; x: 246.633; y: -117.952 }
+ PathCubic { control1X: 246.633; control1Y: -117.952; control2X: 233.169; control2Y: -115.912; x: 229.089; y: -103.672 }
+ PathCubic { control1X: 229.089; control1Y: -103.672; control2X: 225.417; control2Y: -78.784; x: 232.761; y: -73.48 }
+ PathCubic { control1X: 232.761; control1Y: -73.48; control2X: 237.249; control2Y: -66.544; x: 243.777; y: -72.664 }
+ PathCubic { control1X: 250.305; control1Y: -78.784; control2X: 264.566; control2Y: -106.91; x: 262.526; y: -119.558 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 262.151; y: -119.2 }
+ PathCubic { control1X: 262.151; control1Y: -119.2; control2X: 264.201; control2Y: -127.2; x: 259.801; y: -122.8 }
+ PathCubic { control1X: 259.801; control1Y: -122.8; control2X: 253.401; control2Y: -117.6; x: 246.601; y: -117.6 }
+ PathCubic { control1X: 246.601; control1Y: -117.6; control2X: 233.401; control2Y: -115.6; x: 229.401; y: -103.6 }
+ PathCubic { control1X: 229.401; control1Y: -103.6; control2X: 225.801; control2Y: -79.2; x: 233.001; y: -74 }
+ PathCubic { control1X: 233.001; control1Y: -74; control2X: 237.401; control2Y: -67.2; x: 243.801; y: -73.2 }
+ PathCubic { control1X: 250.201; control1Y: -79.2; control2X: 264.151; control2Y: -106.8; x: 262.151; y: -119.2 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: 50.6; y: 84 }
+ PathCubic { control1X: 50.6; control1Y: 84; control2X: 30.2; control2Y: 64.8; x: 22.2; y: 64 }
+ PathCubic { control1X: 22.2; control1Y: 64; control2X: -12.2; control2Y: 60; x: -27; y: 78 }
+ PathCubic { control1X: -27; control1Y: 78; control2X: -9.4; control2Y: 57.6; x: 18.2; y: 63.2 }
+ PathCubic { control1X: 18.2; control1Y: 63.2; control2X: -3.4; control2Y: 58.8; x: -15.8; y: 62 }
+ PathCubic { control1X: -15.8; control1Y: 62; control2X: -32.6; control2Y: 62; x: -42.2; y: 76 }
+ PathLine { x: -45; y: 80.8 }
+ PathCubic { control1X: -45; control1Y: 80.8; control2X: -41; control2Y: 66; x: -22.6; y: 60 }
+ PathCubic { control1X: -22.6; control1Y: 60; control2X: 0.2; control2Y: 55.2; x: 11; y: 60 }
+ PathCubic { control1X: 11; control1Y: 60; control2X: -10.6; control2Y: 53.2; x: -20.6; y: 55.2 }
+ PathCubic { control1X: -20.6; control1Y: 55.2; control2X: -51; control2Y: 52.8; x: -63.8; y: 79.2 }
+ PathCubic { control1X: -63.8; control1Y: 79.2; control2X: -59.8; control2Y: 64.8; x: -45; y: 57.6 }
+ PathCubic { control1X: -45; control1Y: 57.6; control2X: -31.4; control2Y: 48.8; x: -11; y: 51.6 }
+ PathCubic { control1X: -11; control1Y: 51.6; control2X: 3.4; control2Y: 54.8; x: 8.6; y: 57.2 }
+ PathCubic { control1X: 13.8; control1Y: 59.6; control2X: 12.6; control2Y: 56.8; x: 4.2; y: 52 }
+ PathCubic { control1X: 4.2; control1Y: 52; control2X: -1.4; control2Y: 42; x: -15.4; y: 42.4 }
+ PathCubic { control1X: -15.4; control1Y: 42.4; control2X: -58.2; control2Y: 46; x: -68.6; y: 58 }
+ PathCubic { control1X: -68.6; control1Y: 58; control2X: -55; control2Y: 46.8; x: -44.6; y: 44 }
+ PathCubic { control1X: -44.6; control1Y: 44; control2X: -22.2; control2Y: 36; x: -13.8; y: 36.8 }
+ PathCubic { control1X: -13.8; control1Y: 36.8; control2X: 11; control2Y: 37.8; x: 18.6; y: 33.8 }
+ PathCubic { control1X: 18.6; control1Y: 33.8; control2X: 7.4; control2Y: 38.8; x: 10.6; y: 42 }
+ PathCubic { control1X: 13.8; control1Y: 45.2; control2X: 20.6; control2Y: 52.8; x: 20.6; y: 54 }
+ PathCubic { control1X: 20.6; control1Y: 55.2; control2X: 44.8; control2Y: 77.3; x: 48.4; y: 81.7 }
+ PathLine { x: 50.6; y: 84 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 189; y: 278 }
+ PathCubic { control1X: 189; control1Y: 278; control2X: 173.5; control2Y: 241.5; x: 161; y: 232 }
+ PathCubic { control1X: 161; control1Y: 232; control2X: 187; control2Y: 248; x: 190.5; y: 266 }
+ PathCubic { control1X: 190.5; control1Y: 266; control2X: 190.5; control2Y: 276; x: 189; y: 278 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 236; y: 285.5 }
+ PathCubic { control1X: 236; control1Y: 285.5; control2X: 209.5; control2Y: 230.5; x: 191; y: 206.5 }
+ PathCubic { control1X: 191; control1Y: 206.5; control2X: 234.5; control2Y: 244; x: 239.5; y: 270.5 }
+ PathLine { x: 240; y: 276 }
+ PathLine { x: 237; y: 273.5 }
+ PathCubic { control1X: 237; control1Y: 273.5; control2X: 236.5; control2Y: 282.5; x: 236; y: 285.5 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 292.5; y: 237 }
+ PathCubic { control1X: 292.5; control1Y: 237; control2X: 230; control2Y: 177.5; x: 228.5; y: 175 }
+ PathCubic { control1X: 228.5; control1Y: 175; control2X: 289; control2Y: 241; x: 292; y: 248.5 }
+ PathCubic { control1X: 292; control1Y: 248.5; control2X: 290; control2Y: 239.5; x: 292.5; y: 237 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 104; y: 280.5 }
+ PathCubic { control1X: 104; control1Y: 280.5; control2X: 123.5; control2Y: 228.5; x: 142.5; y: 251 }
+ PathCubic { control1X: 142.5; control1Y: 251; control2X: 157.5; control2Y: 261; x: 157; y: 264 }
+ PathCubic { control1X: 157; control1Y: 264; control2X: 153; control2Y: 257.5; x: 135; y: 258 }
+ PathCubic { control1X: 135; control1Y: 258; control2X: 116; control2Y: 255; x: 104; y: 280.5 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 294.5; y: 153 }
+ PathCubic { control1X: 294.5; control1Y: 153; control2X: 249.5; control2Y: 124.5; x: 242; y: 123 }
+ PathCubic { control1X: 230.193; control1Y: 120.639; control2X: 291.5; control2Y: 152; x: 296.5; y: 162.5 }
+ PathCubic { control1X: 296.5; control1Y: 162.5; control2X: 298.5; control2Y: 160; x: 294.5; y: 153 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 143.801; y: 259.601 }
+ PathCubic { control1X: 143.801; control1Y: 259.601; control2X: 164.201; control2Y: 257.601; x: 171.001; y: 250.801 }
+ PathLine { x: 175.401; y: 254.401 }
+ PathLine { x: 193.001; y: 216.001 }
+ PathLine { x: 196.601; y: 221.201 }
+ PathCubic { control1X: 196.601; control1Y: 221.201; control2X: 211.001; control2Y: 206.401; x: 210.201; y: 198.401 }
+ PathCubic { control1X: 209.401; control1Y: 190.401; control2X: 223.001; control2Y: 204.401; x: 223.001; y: 204.401 }
+ PathCubic { control1X: 223.001; control1Y: 204.401; control2X: 222.201; control2Y: 192.801; x: 229.401; y: 199.601 }
+ PathCubic { control1X: 229.401; control1Y: 199.601; control2X: 227.001; control2Y: 184.001; x: 235.401; y: 192.001 }
+ PathCubic { control1X: 235.401; control1Y: 192.001; control2X: 224.864; control2Y: 161.844; x: 247.401; y: 187.601 }
+ PathCubic { control1X: 253.001; control1Y: 194.001; control2X: 248.601; control2Y: 187.201; x: 248.601; y: 187.201 }
+ PathCubic { control1X: 248.601; control1Y: 187.201; control2X: 222.601; control2Y: 139.201; x: 244.201; y: 153.601 }
+ PathCubic { control1X: 244.201; control1Y: 153.601; control2X: 246.201; control2Y: 130.801; x: 245.001; y: 126.401 }
+ PathCubic { control1X: 243.801; control1Y: 122.001; control2X: 241.801; control2Y: 99.6; x: 237.001; y: 94.4 }
+ PathCubic { control1X: 232.201; control1Y: 89.2; control2X: 237.401; control2Y: 87.6; x: 243.001; y: 92.8 }
+ PathCubic { control1X: 243.001; control1Y: 92.8; control2X: 231.801; control2Y: 68.8; x: 245.001; y: 80.8 }
+ PathCubic { control1X: 245.001; control1Y: 80.8; control2X: 241.401; control2Y: 65.6; x: 237.001; y: 62.8 }
+ PathCubic { control1X: 237.001; control1Y: 62.8; control2X: 231.401; control2Y: 45.6; x: 246.601; y: 56.4 }
+ PathCubic { control1X: 246.601; control1Y: 56.4; control2X: 242.201; control2Y: 44; x: 239.001; y: 40.8 }
+ PathCubic { control1X: 239.001; control1Y: 40.8; control2X: 227.401; control2Y: 13.2; x: 234.601; y: 18 }
+ PathLine { x: 239.001; y: 21.6 }
+ PathCubic { control1X: 239.001; control1Y: 21.6; control2X: 232.201; control2Y: 7.6; x: 238.601; y: 12 }
+ PathCubic { control1X: 245.001; control1Y: 16.4; control2X: 245.001; control2Y: 16; x: 245.001; y: 16 }
+ PathCubic { control1X: 245.001; control1Y: 16; control2X: 223.801; control2Y: -17.2; x: 244.201; y: 0.4 }
+ PathCubic { control1X: 244.201; control1Y: 0.4; control2X: 236.042; control2Y: -13.518; x: 232.601; y: -20.4 }
+ PathCubic { control1X: 232.601; control1Y: -20.4; control2X: 213.801; control2Y: -40.8; x: 228.201; y: -34.4 }
+ PathLine { x: 233.001; y: -32.8 }
+ PathCubic { control1X: 233.001; control1Y: -32.8; control2X: 224.201; control2Y: -42.8; x: 216.201; y: -44.4 }
+ PathCubic { control1X: 208.201; control1Y: -46; control2X: 218.601; control2Y: -52.4; x: 225.001; y: -50.4 }
+ PathCubic { control1X: 231.401; control1Y: -48.4; control2X: 247.001; control2Y: -40.8; x: 247.001; y: -40.8 }
+ PathCubic { control1X: 247.001; control1Y: -40.8; control2X: 259.801; control2Y: -22; x: 263.801; y: -21.6 }
+ PathCubic { control1X: 263.801; control1Y: -21.6; control2X: 243.801; control2Y: -29.2; x: 249.801; y: -21.2 }
+ PathCubic { control1X: 249.801; control1Y: -21.2; control2X: 264.201; control2Y: -7.2; x: 257.001; y: -7.6 }
+ PathCubic { control1X: 257.001; control1Y: -7.6; control2X: 251.001; control2Y: -0.4; x: 255.801; y: 8.4 }
+ PathCubic { control1X: 255.801; control1Y: 8.4; control2X: 237.342; control2Y: -9.991; x: 252.201; y: 15.6 }
+ PathLine { x: 259.001; y: 32 }
+ PathCubic { control1X: 259.001; control1Y: 32; control2X: 234.601; control2Y: 7.2; x: 245.801; y: 29.2 }
+ PathCubic { control1X: 245.801; control1Y: 29.2; control2X: 263.001; control2Y: 52.8; x: 265.001; y: 53.2 }
+ PathCubic { control1X: 267.001; control1Y: 53.6; control2X: 271.401; control2Y: 62.4; x: 271.401; y: 62.4 }
+ PathLine { x: 267.001; y: 60.4 }
+ PathLine { x: 272.201; y: 69.2 }
+ PathCubic { control1X: 272.201; control1Y: 69.2; control2X: 261.001; control2Y: 57.2; x: 267.001; y: 70.4 }
+ PathLine { x: 272.601; y: 84.8 }
+ PathCubic { control1X: 272.601; control1Y: 84.8; control2X: 252.201; control2Y: 62.8; x: 265.801; y: 92.4 }
+ PathCubic { control1X: 265.801; control1Y: 92.4; control2X: 249.401; control2Y: 87.2; x: 258.201; y: 104.4 }
+ PathCubic { control1X: 258.201; control1Y: 104.4; control2X: 256.601; control2Y: 120.401; x: 257.001; y: 125.601 }
+ PathCubic { control1X: 257.401; control1Y: 130.801; control2X: 258.601; control2Y: 159.201; x: 254.201; y: 167.201 }
+ PathCubic { control1X: 249.801; control1Y: 175.201; control2X: 260.201; control2Y: 194.401; x: 262.201; y: 198.401 }
+ PathCubic { control1X: 264.201; control1Y: 202.401; control2X: 267.801; control2Y: 213.201; x: 259.001; y: 204.001 }
+ PathCubic { control1X: 250.201; control1Y: 194.801; control2X: 254.601; control2Y: 200.401; x: 256.601; y: 209.201 }
+ PathCubic { control1X: 258.601; control1Y: 218.001; control2X: 264.601; control2Y: 233.601; x: 263.801; y: 239.201 }
+ PathCubic { control1X: 263.801; control1Y: 239.201; control2X: 262.601; control2Y: 240.401; x: 259.401; y: 236.801 }
+ PathCubic { control1X: 259.401; control1Y: 236.801; control2X: 244.601; control2Y: 214.001; x: 246.201; y: 228.401 }
+ PathCubic { control1X: 246.201; control1Y: 228.401; control2X: 245.001; control2Y: 236.401; x: 241.801; y: 245.201 }
+ PathCubic { control1X: 241.801; control1Y: 245.201; control2X: 238.601; control2Y: 256.001; x: 238.601; y: 247.201 }
+ PathCubic { control1X: 238.601; control1Y: 247.201; control2X: 235.401; control2Y: 230.401; x: 232.601; y: 238.001 }
+ PathCubic { control1X: 229.801; control1Y: 245.601; control2X: 226.201; control2Y: 251.601; x: 223.401; y: 254.001 }
+ PathCubic { control1X: 220.601; control1Y: 256.401; control2X: 215.401; control2Y: 233.601; x: 214.201; y: 244.001 }
+ PathCubic { control1X: 214.201; control1Y: 244.001; control2X: 202.201; control2Y: 231.601; x: 197.401; y: 248.001 }
+ PathLine { x: 185.801; y: 264.401 }
+ PathCubic { control1X: 185.801; control1Y: 264.401; control2X: 185.401; control2Y: 252.001; x: 184.201; y: 258.001 }
+ PathCubic { control1X: 184.201; control1Y: 258.001; control2X: 154.201; control2Y: 264.001; x: 143.801; y: 259.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 109.401; y: -97.2 }
+ PathCubic { control1X: 109.401; control1Y: -97.2; control2X: 97.801; control2Y: -105.2; x: 93.801; y: -104.8 }
+ PathCubic { control1X: 89.801; control1Y: -104.4; control2X: 121.401; control2Y: -113.6; x: 162.601; y: -86 }
+ PathCubic { control1X: 162.601; control1Y: -86; control2X: 167.401; control2Y: -83.2; x: 171.001; y: -83.6 }
+ PathCubic { control1X: 171.001; control1Y: -83.6; control2X: 174.201; control2Y: -81.2; x: 171.401; y: -77.6 }
+ PathCubic { control1X: 171.401; control1Y: -77.6; control2X: 162.601; control2Y: -68; x: 173.801; y: -56.8 }
+ PathCubic { control1X: 173.801; control1Y: -56.8; control2X: 192.201; control2Y: -50; x: 186.601; y: -58.8 }
+ PathCubic { control1X: 186.601; control1Y: -58.8; control2X: 197.401; control2Y: -54.8; x: 199.801; y: -50.8 }
+ PathCubic { control1X: 202.201; control1Y: -46.8; control2X: 201.001; control2Y: -50.8; x: 201.001; y: -50.8 }
+ PathCubic { control1X: 201.001; control1Y: -50.8; control2X: 194.601; control2Y: -58; x: 188.601; y: -63.2 }
+ PathCubic { control1X: 188.601; control1Y: -63.2; control2X: 183.401; control2Y: -65.2; x: 180.601; y: -73.6 }
+ PathCubic { control1X: 177.801; control1Y: -82; control2X: 175.401; control2Y: -92; x: 179.801; y: -95.2 }
+ PathCubic { control1X: 179.801; control1Y: -95.2; control2X: 175.801; control2Y: -90.8; x: 176.601; y: -94.8 }
+ PathCubic { control1X: 177.401; control1Y: -98.8; control2X: 181.001; control2Y: -102.4; x: 182.601; y: -102.8 }
+ PathCubic { control1X: 184.201; control1Y: -103.2; control2X: 200.601; control2Y: -119; x: 207.401; y: -119.4 }
+ PathCubic { control1X: 207.401; control1Y: -119.4; control2X: 198.201; control2Y: -118; x: 195.201; y: -119 }
+ PathCubic { control1X: 192.201; control1Y: -120; control2X: 165.601; control2Y: -131.4; x: 159.601; y: -132.6 }
+ PathCubic { control1X: 159.601; control1Y: -132.6; control2X: 142.801; control2Y: -139.2; x: 154.801; y: -137.2 }
+ PathCubic { control1X: 154.801; control1Y: -137.2; control2X: 190.601; control2Y: -133.4; x: 208.801; y: -120.2 }
+ PathCubic { control1X: 208.801; control1Y: -120.2; control2X: 201.601; control2Y: -128.6; x: 183.201; y: -135.6 }
+ PathCubic { control1X: 183.201; control1Y: -135.6; control2X: 161.001; control2Y: -148.2; x: 125.801; y: -143.2 }
+ PathCubic { control1X: 125.801; control1Y: -143.2; control2X: 108.001; control2Y: -140; x: 100.201; y: -138.2 }
+ PathCubic { control1X: 100.201; control1Y: -138.2; control2X: 97.601; control2Y: -138.8; x: 97.001; y: -139.2 }
+ PathCubic { control1X: 96.401; control1Y: -139.6; control2X: 84.6; control2Y: -148.6; x: 57; y: -141.6 }
+ PathCubic { control1X: 57; control1Y: -141.6; control2X: 40; control2Y: -137; x: 31.4; y: -132.2 }
+ PathCubic { control1X: 31.4; control1Y: -132.2; control2X: 16.2; control2Y: -131; x: 12.6; y: -127.8 }
+ PathCubic { control1X: 12.6; control1Y: -127.8; control2X: -6; control2Y: -113.2; x: -8; y: -112.4 }
+ PathCubic { control1X: -10; control1Y: -111.6; control2X: -21.4; control2Y: -104; x: -22.2; y: -103.6 }
+ PathCubic { control1X: -22.2; control1Y: -103.6; control2X: 2.4; control2Y: -110.2; x: 4.8; y: -112.6 }
+ PathCubic { control1X: 7.2; control1Y: -115; control2X: 24.6; control2Y: -117.6; x: 27; y: -116.2 }
+ PathCubic { control1X: 29.4; control1Y: -114.8; control2X: 37.8; control2Y: -115.4; x: 28.2; y: -114.8 }
+ PathCubic { control1X: 28.2; control1Y: -114.8; control2X: 103.801; control2Y: -100; x: 104.601; y: -98 }
+ PathCubic { control1X: 105.401; control1Y: -96; control2X: 109.401; control2Y: -97.2; x: 109.401; y: -97.2 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 180.801; y: -106.4 }
+ PathCubic { control1X: 180.801; control1Y: -106.4; control2X: 170.601; control2Y: -113.8; x: 168.601; y: -113.8 }
+ PathCubic { control1X: 166.601; control1Y: -113.8; control2X: 154.201; control2Y: -124; x: 150.001; y: -123.6 }
+ PathCubic { control1X: 145.801; control1Y: -123.2; control2X: 133.601; control2Y: -133.2; x: 106.201; y: -125 }
+ PathCubic { control1X: 106.201; control1Y: -125; control2X: 105.601; control2Y: -127; x: 109.201; y: -127.8 }
+ PathCubic { control1X: 109.201; control1Y: -127.8; control2X: 115.601; control2Y: -130; x: 116.001; y: -130.6 }
+ PathCubic { control1X: 116.001; control1Y: -130.6; control2X: 136.201; control2Y: -134.8; x: 143.401; y: -131.2 }
+ PathCubic { control1X: 143.401; control1Y: -131.2; control2X: 152.601; control2Y: -128.6; x: 158.801; y: -122.4 }
+ PathCubic { control1X: 158.801; control1Y: -122.4; control2X: 170.001; control2Y: -119.2; x: 173.201; y: -120.2 }
+ PathCubic { control1X: 173.201; control1Y: -120.2; control2X: 182.001; control2Y: -118; x: 182.401; y: -116.2 }
+ PathCubic { control1X: 182.401; control1Y: -116.2; control2X: 188.201; control2Y: -113.2; x: 186.401; y: -110.6 }
+ PathCubic { control1X: 186.401; control1Y: -110.6; control2X: 186.801; control2Y: -109; x: 180.801; y: -106.4 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 168.33; y: -108.509 }
+ PathCubic { control1X: 169.137; control1Y: -107.877; control2X: 170.156; control2Y: -107.779; x: 170.761; y: -106.97 }
+ PathCubic { control1X: 170.995; control1Y: -106.656; control2X: 170.706; control2Y: -106.33; x: 170.391; y: -106.233 }
+ PathCubic { control1X: 169.348; control1Y: -105.916; control2X: 168.292; control2Y: -106.486; x: 167.15; y: -105.898 }
+ PathCubic { control1X: 166.748; control1Y: -105.691; control2X: 166.106; control2Y: -105.873; x: 165.553; y: -106.022 }
+ PathCubic { control1X: 163.921; control1Y: -106.463; control2X: 162.092; control2Y: -106.488; x: 160.401; y: -105.8 }
+ PathCubic { control1X: 158.416; control1Y: -106.929; control2X: 156.056; control2Y: -106.345; x: 153.975; y: -107.346 }
+ PathCubic { control1X: 153.917; control1Y: -107.373; control2X: 153.695; control2Y: -107.027; x: 153.621; y: -107.054 }
+ PathCubic { control1X: 150.575; control1Y: -108.199; control2X: 146.832; control2Y: -107.916; x: 144.401; y: -110.2 }
+ PathCubic { control1X: 141.973; control1Y: -110.612; control2X: 139.616; control2Y: -111.074; x: 137.188; y: -111.754 }
+ PathCubic { control1X: 135.37; control1Y: -112.263; control2X: 133.961; control2Y: -113.252; x: 132.341; y: -114.084 }
+ PathCubic { control1X: 130.964; control1Y: -114.792; control2X: 129.507; control2Y: -115.314; x: 127.973; y: -115.686 }
+ PathCubic { control1X: 126.11; control1Y: -116.138; control2X: 124.279; control2Y: -116.026; x: 122.386; y: -116.546 }
+ PathCubic { control1X: 122.293; control1Y: -116.571; control2X: 122.101; control2Y: -116.227; x: 122.019; y: -116.254 }
+ PathCubic { control1X: 121.695; control1Y: -116.362; control2X: 121.405; control2Y: -116.945; x: 121.234; y: -116.892 }
+ PathCubic { control1X: 119.553; control1Y: -116.37; control2X: 118.065; control2Y: -117.342; x: 116.401; y: -117 }
+ PathCubic { control1X: 115.223; control1Y: -118.224; control2X: 113.495; control2Y: -117.979; x: 111.949; y: -118.421 }
+ PathCubic { control1X: 108.985; control1Y: -119.269; control2X: 105.831; control2Y: -117.999; x: 102.801; y: -119 }
+ PathCubic { control1X: 106.914; control1Y: -120.842; control2X: 111.601; control2Y: -119.61; x: 115.663; y: -121.679 }
+ PathCubic { control1X: 117.991; control1Y: -122.865; control2X: 120.653; control2Y: -121.763; x: 123.223; y: -122.523 }
+ PathCubic { control1X: 123.71; control1Y: -122.667; control2X: 124.401; control2Y: -122.869; x: 124.801; y: -122.2 }
+ PathCubic { control1X: 124.935; control1Y: -122.335; control2X: 125.117; control2Y: -122.574; x: 125.175; y: -122.546 }
+ PathCubic { control1X: 127.625; control1Y: -121.389; control2X: 129.94; control2Y: -120.115; x: 132.422; y: -119.049 }
+ PathCubic { control1X: 132.763; control1Y: -118.903; control2X: 133.295; control2Y: -119.135; x: 133.547; y: -118.933 }
+ PathCubic { control1X: 135.067; control1Y: -117.717; control2X: 137.01; control2Y: -117.82; x: 138.401; y: -116.6 }
+ PathCubic { control1X: 140.099; control1Y: -117.102; control2X: 141.892; control2Y: -116.722; x: 143.621; y: -117.346 }
+ PathCubic { control1X: 143.698; control1Y: -117.373; control2X: 143.932; control2Y: -117.032; x: 143.965; y: -117.054 }
+ PathCubic { control1X: 145.095; control1Y: -117.802; control2X: 146.25; control2Y: -117.531; x: 147.142; y: -117.227 }
+ PathCubic { control1X: 147.48; control1Y: -117.112; control2X: 148.143; control2Y: -116.865; x: 148.448; y: -116.791 }
+ PathCubic { control1X: 149.574; control1Y: -116.515; control2X: 150.43; control2Y: -116.035; x: 151.609; y: -115.852 }
+ PathCubic { control1X: 151.723; control1Y: -115.834; control2X: 151.908; control2Y: -116.174; x: 151.98; y: -116.146 }
+ PathCubic { control1X: 153.103; control1Y: -115.708; control2X: 154.145; control2Y: -115.764; x: 154.801; y: -114.6 }
+ PathCubic { control1X: 154.936; control1Y: -114.735; control2X: 155.101; control2Y: -114.973; x: 155.183; y: -114.946 }
+ PathCubic { control1X: 156.21; control1Y: -114.608; control2X: 156.859; control2Y: -113.853; x: 157.96; y: -113.612 }
+ PathCubic { control1X: 158.445; control1Y: -113.506; control2X: 159.057; control2Y: -112.88; x: 159.633; y: -112.704 }
+ PathCubic { control1X: 162.025; control1Y: -111.973; control2X: 163.868; control2Y: -110.444; x: 166.062; y: -109.549 }
+ PathCubic { control1X: 166.821; control1Y: -109.239; control2X: 167.697; control2Y: -109.005; x: 168.33; y: -108.509 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 91.696; y: -122.739 }
+ PathCubic { control1X: 89.178; control1Y: -124.464; control2X: 86.81; control2Y: -125.57; x: 84.368; y: -127.356 }
+ PathCubic { control1X: 84.187; control1Y: -127.489; control2X: 83.827; control2Y: -127.319; x: 83.625; y: -127.441 }
+ PathCubic { control1X: 82.618; control1Y: -128.05; control2X: 81.73; control2Y: -128.631; x: 80.748; y: -129.327 }
+ PathCubic { control1X: 80.209; control1Y: -129.709; control2X: 79.388; control2Y: -129.698; x: 78.88; y: -129.956 }
+ PathCubic { control1X: 76.336; control1Y: -131.248; control2X: 73.707; control2Y: -131.806; x: 71.2; y: -133 }
+ PathCubic { control1X: 71.882; control1Y: -133.638; control2X: 73.004; control2Y: -133.394; x: 73.6; y: -134.2 }
+ PathCubic { control1X: 73.795; control1Y: -133.92; control2X: 74.033; control2Y: -133.636; x: 74.386; y: -133.827 }
+ PathCubic { control1X: 76.064; control1Y: -134.731; control2X: 77.914; control2Y: -134.884; x: 79.59; y: -134.794 }
+ PathCubic { control1X: 81.294; control1Y: -134.702; control2X: 83.014; control2Y: -134.397; x: 84.789; y: -134.125 }
+ PathCubic { control1X: 85.096; control1Y: -134.078; control2X: 85.295; control2Y: -133.555; x: 85.618; y: -133.458 }
+ PathCubic { control1X: 87.846; control1Y: -132.795; control2X: 90.235; control2Y: -133.32; x: 92.354; y: -132.482 }
+ PathCubic { control1X: 93.945; control1Y: -131.853; control2X: 95.515; control2Y: -131.03; x: 96.754; y: -129.755 }
+ PathCubic { control1X: 97.006; control1Y: -129.495; control2X: 96.681; control2Y: -129.194; x: 96.401; y: -129 }
+ PathCubic { control1X: 96.789; control1Y: -129.109; control2X: 97.062; control2Y: -128.903; x: 97.173; y: -128.59 }
+ PathCubic { control1X: 97.257; control1Y: -128.351; control2X: 97.257; control2Y: -128.049; x: 97.173; y: -127.81 }
+ PathCubic { control1X: 97.061; control1Y: -127.498; control2X: 96.782; control2Y: -127.397; x: 96.408; y: -127.346 }
+ PathCubic { control1X: 95.001; control1Y: -127.156; control2X: 96.773; control2Y: -128.536; x: 96.073; y: -128.088 }
+ PathCubic { control1X: 94.8; control1Y: -127.274; control2X: 95.546; control2Y: -125.868; x: 94.801; y: -124.6 }
+ PathCubic { control1X: 94.521; control1Y: -124.794; control2X: 94.291; control2Y: -125.012; x: 94.401; y: -125.4 }
+ PathCubic { control1X: 94.635; control1Y: -124.878; control2X: 94.033; control2Y: -124.588; x: 93.865; y: -124.272 }
+ PathCubic { control1X: 93.48; control1Y: -123.547; control2X: 92.581; control2Y: -122.132; x: 91.696; y: -122.739 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 59.198; y: -115.391 }
+ PathCubic { control1X: 56.044; control1Y: -116.185; control2X: 52.994; control2Y: -116.07; x: 49.978; y: -117.346 }
+ PathCubic { control1X: 49.911; control1Y: -117.374; control2X: 49.688; control2Y: -117.027; x: 49.624; y: -117.054 }
+ PathCubic { control1X: 48.258; control1Y: -117.648; control2X: 47.34; control2Y: -118.614; x: 46.264; y: -119.66 }
+ PathCubic { control1X: 45.351; control1Y: -120.548; control2X: 43.693; control2Y: -120.161; x: 42.419; y: -120.648 }
+ PathCubic { control1X: 42.095; control1Y: -120.772; control2X: 41.892; control2Y: -121.284; x: 41.591; y: -121.323 }
+ PathCubic { control1X: 40.372; control1Y: -121.48; control2X: 39.445; control2Y: -122.429; x: 38.4; y: -123 }
+ PathCubic { control1X: 40.736; control1Y: -123.795; control2X: 43.147; control2Y: -123.764; x: 45.609; y: -124.148 }
+ PathCubic { control1X: 45.722; control1Y: -124.166; control2X: 45.867; control2Y: -123.845; x: 46; y: -123.845 }
+ PathCubic { control1X: 46.136; control1Y: -123.845; control2X: 46.266; control2Y: -124.066; x: 46.4; y: -124.2 }
+ PathCubic { control1X: 46.595; control1Y: -123.92; control2X: 46.897; control2Y: -123.594; x: 47.154; y: -123.848 }
+ PathCubic { control1X: 47.702; control1Y: -124.388; control2X: 48.258; control2Y: -124.198; x: 48.798; y: -124.158 }
+ PathCubic { control1X: 48.942; control1Y: -124.148; control2X: 49.067; control2Y: -123.845; x: 49.2; y: -123.845 }
+ PathCubic { control1X: 49.336; control1Y: -123.845; control2X: 49.467; control2Y: -124.156; x: 49.6; y: -124.156 }
+ PathCubic { control1X: 49.736; control1Y: -124.155; control2X: 49.867; control2Y: -123.845; x: 50; y: -123.845 }
+ PathCubic { control1X: 50.136; control1Y: -123.845; control2X: 50.266; control2Y: -124.066; x: 50.4; y: -124.2 }
+ PathCubic { control1X: 51.092; control1Y: -123.418; control2X: 51.977; control2Y: -123.972; x: 52.799; y: -123.793 }
+ PathCubic { control1X: 53.837; control1Y: -123.566; control2X: 54.104; control2Y: -122.418; x: 55.178; y: -122.12 }
+ PathCubic { control1X: 59.893; control1Y: -120.816; control2X: 64.03; control2Y: -118.671; x: 68.393; y: -116.584 }
+ PathCubic { control1X: 68.7; control1Y: -116.437; control2X: 68.91; control2Y: -116.189; x: 68.8; y: -115.8 }
+ PathCubic { control1X: 69.067; control1Y: -115.8; control2X: 69.38; control2Y: -115.888; x: 69.57; y: -115.756 }
+ PathCubic { control1X: 70.628; control1Y: -115.024; control2X: 71.669; control2Y: -114.476; x: 72.366; y: -113.378 }
+ PathCubic { control1X: 72.582; control1Y: -113.039; control2X: 72.253; control2Y: -112.632; x: 72.02; y: -112.684 }
+ PathCubic { control1X: 67.591; control1Y: -113.679; control2X: 63.585; control2Y: -114.287; x: 59.198; y: -115.391 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 45.338; y: -71.179 }
+ PathCubic { control1X: 43.746; control1Y: -72.398; control2X: 43.162; control2Y: -74.429; x: 42.034; y: -76.221 }
+ PathCubic { control1X: 41.82; control1Y: -76.561; control2X: 42.094; control2Y: -76.875; x: 42.411; y: -76.964 }
+ PathCubic { control1X: 42.971; control1Y: -77.123; control2X: 43.514; control2Y: -76.645; x: 43.923; y: -76.443 }
+ PathCubic { control1X: 45.668; control1Y: -75.581; control2X: 47.203; control2Y: -74.339; x: 49.2; y: -74.2 }
+ PathCubic { control1X: 51.19; control1Y: -71.966; control2X: 55.45; control2Y: -71.581; x: 55.457; y: -68.2 }
+ PathCubic { control1X: 55.458; control1Y: -67.341; control2X: 54.03; control2Y: -68.259; x: 53.6; y: -67.4 }
+ PathCubic { control1X: 51.149; control1Y: -68.403; control2X: 48.76; control2Y: -68.3; x: 46.38; y: -69.767 }
+ PathCubic { control1X: 45.763; control1Y: -70.148; control2X: 46.093; control2Y: -70.601; x: 45.338; y: -71.179 }
+ }
+
+ ShapePath {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ PathMove { x: 17.8; y: -123.756 }
+ PathCubic { control1X: 17.935; control1Y: -123.755; control2X: 24.966; control2Y: -123.522; x: 24.949; y: -123.408 }
+ PathCubic { control1X: 24.904; control1Y: -123.099; control2X: 17.174; control2Y: -122.05; x: 16.81; y: -122.22 }
+ PathCubic { control1X: 16.646; control1Y: -122.296; control2X: 9.134; control2Y: -119.866; x: 9; y: -120 }
+ PathCubic { control1X: 9.268; control1Y: -120.135; control2X: 17.534; control2Y: -123.756; x: 17.8; y: -123.756 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 33.2; y: -114 }
+ PathCubic { control1X: 33.2; control1Y: -114; control2X: 18.4; control2Y: -112.2; x: 14; y: -111 }
+ PathCubic { control1X: 9.6; control1Y: -109.8; control2X: -9; control2Y: -102.2; x: -12; y: -100.2 }
+ PathCubic { control1X: -12; control1Y: -100.2; control2X: -25.4; control2Y: -94.8; x: -42.4; y: -74.8 }
+ PathCubic { control1X: -42.4; control1Y: -74.8; control2X: -34.8; control2Y: -78.2; x: -32.6; y: -81 }
+ PathCubic { control1X: -32.6; control1Y: -81; control2X: -19; control2Y: -93.6; x: -19.2; y: -91 }
+ PathCubic { control1X: -19.2; control1Y: -91; control2X: -7; control2Y: -99.6; x: -7.6; y: -97.4 }
+ PathCubic { control1X: -7.6; control1Y: -97.4; control2X: 16.8; control2Y: -108.6; x: 14.8; y: -105.4 }
+ PathCubic { control1X: 14.8; control1Y: -105.4; control2X: 36.4; control2Y: -110; x: 35.4; y: -108 }
+ PathCubic { control1X: 35.4; control1Y: -108; control2X: 54.2; control2Y: -103.6; x: 51.4; y: -103.4 }
+ PathCubic { control1X: 51.4; control1Y: -103.4; control2X: 45.6; control2Y: -102.2; x: 52; y: -98.6 }
+ PathCubic { control1X: 52; control1Y: -98.6; control2X: 48.6; control2Y: -94.2; x: 43.2; y: -98.2 }
+ PathCubic { control1X: 37.8; control1Y: -102.2; control2X: 40.8; control2Y: -100; x: 35.8; y: -99 }
+ PathCubic { control1X: 35.8; control1Y: -99; control2X: 33.2; control2Y: -98.2; x: 28.6; y: -102.2 }
+ PathCubic { control1X: 28.6; control1Y: -102.2; control2X: 23; control2Y: -106.8; x: 14.2; y: -103.2 }
+ PathCubic { control1X: 14.2; control1Y: -103.2; control2X: -16.4; control2Y: -90.6; x: -18.4; y: -90 }
+ PathCubic { control1X: -18.4; control1Y: -90; control2X: -22; control2Y: -87.2; x: -24.4; y: -83.6 }
+ PathCubic { control1X: -24.4; control1Y: -83.6; control2X: -30.2; control2Y: -79.2; x: -33.2; y: -77.8 }
+ PathCubic { control1X: -33.2; control1Y: -77.8; control2X: -46; control2Y: -66.2; x: -47.2; y: -64.8 }
+ PathCubic { control1X: -47.2; control1Y: -64.8; control2X: -50.6; control2Y: -59.6; x: -51.4; y: -59.2 }
+ PathCubic { control1X: -51.4; control1Y: -59.2; control2X: -45; control2Y: -63; x: -43; y: -65 }
+ PathCubic { control1X: -43; control1Y: -65; control2X: -29; control2Y: -75; x: -23.6; y: -75.8 }
+ PathCubic { control1X: -23.6; control1Y: -75.8; control2X: -19.2; control2Y: -78.8; x: -18.4; y: -80.2 }
+ PathCubic { control1X: -18.4; control1Y: -80.2; control2X: -4; control2Y: -89.4; x: 0.2; y: -89.4 }
+ PathCubic { control1X: 0.2; control1Y: -89.4; control2X: 9.4; control2Y: -84.2; x: 11.8; y: -91.2 }
+ PathCubic { control1X: 11.8; control1Y: -91.2; control2X: 17.6; control2Y: -93; x: 23.2; y: -91.8 }
+ PathCubic { control1X: 23.2; control1Y: -91.8; control2X: 26.4; control2Y: -94.4; x: 25.6; y: -96.6 }
+ PathCubic { control1X: 25.6; control1Y: -96.6; control2X: 27.2; control2Y: -98.4; x: 28.2; y: -94.6 }
+ PathCubic { control1X: 28.2; control1Y: -94.6; control2X: 31.6; control2Y: -91; x: 36.4; y: -93 }
+ PathCubic { control1X: 36.4; control1Y: -93; control2X: 40.4; control2Y: -93.2; x: 38.4; y: -90.8 }
+ PathCubic { control1X: 38.4; control1Y: -90.8; control2X: 34; control2Y: -87; x: 22.2; y: -86.8 }
+ PathCubic { control1X: 22.2; control1Y: -86.8; control2X: 9.8; control2Y: -86.2; x: -6.6; y: -78.6 }
+ PathCubic { control1X: -6.6; control1Y: -78.6; control2X: -36.4; control2Y: -68.2; x: -45.6; y: -57.8 }
+ PathCubic { control1X: -45.6; control1Y: -57.8; control2X: -52; control2Y: -49; x: -57.4; y: -47.8 }
+ PathCubic { control1X: -57.4; control1Y: -47.8; control2X: -63.2; control2Y: -47; x: -69.2; y: -39.6 }
+ PathCubic { control1X: -69.2; control1Y: -39.6; control2X: -59.4; control2Y: -45.4; x: -50.4; y: -45.4 }
+ PathCubic { control1X: -50.4; control1Y: -45.4; control2X: -46.4; control2Y: -47.8; x: -50.2; y: -44.2 }
+ PathCubic { control1X: -50.2; control1Y: -44.2; control2X: -53.8; control2Y: -36.6; x: -52.2; y: -31.2 }
+ PathCubic { control1X: -52.2; control1Y: -31.2; control2X: -52.8; control2Y: -26; x: -53.6; y: -24.4 }
+ PathCubic { control1X: -53.6; control1Y: -24.4; control2X: -61.4; control2Y: -11.6; x: -61.4; y: -9.2 }
+ PathCubic { control1X: -61.4; control1Y: -6.8; control2X: -60.2; control2Y: 3; x: -59.8; y: 3.6 }
+ PathCubic { control1X: -59.4; control1Y: 4.2; control2X: -60.8; control2Y: 2; x: -57; y: 4.4 }
+ PathCubic { control1X: -53.2; control1Y: 6.8; control2X: -50.4; control2Y: 8.4; x: -49.6; y: 11.2 }
+ PathCubic { control1X: -48.8; control1Y: 14; control2X: -51.6; control2Y: 5.8; x: -51.8; y: 4 }
+ PathCubic { control1X: -52; control1Y: 2.2; control2X: -56.2; control2Y: -5; x: -55.4; y: -7.4 }
+ PathCubic { control1X: -55.4; control1Y: -7.4; control2X: -54.4; control2Y: -6.4; x: -53.6; y: -5 }
+ PathCubic { control1X: -53.6; control1Y: -5; control2X: -54.2; control2Y: -5.6; x: -53.6; y: -9.2 }
+ PathCubic { control1X: -53.6; control1Y: -9.2; control2X: -52.8; control2Y: -14.4; x: -51.4; y: -17.6 }
+ PathCubic { control1X: -50; control1Y: -20.8; control2X: -48; control2Y: -24.6; x: -47.6; y: -25.4 }
+ PathCubic { control1X: -47.2; control1Y: -26.2; control2X: -47.2; control2Y: -32; x: -45.8; y: -29.4 }
+ PathLine { x: -42.4; y: -26.8 }
+ PathCubic { control1X: -42.4; control1Y: -26.8; control2X: -45.2; control2Y: -29.4; x: -43; y: -31.6 }
+ PathCubic { control1X: -43; control1Y: -31.6; control2X: -44; control2Y: -37.2; x: -42.2; y: -39.8 }
+ PathCubic { control1X: -42.2; control1Y: -39.8; control2X: -35.2; control2Y: -48.2; x: -33.6; y: -49.2 }
+ PathCubic { control1X: -32; control1Y: -50.2; control2X: -33.4; control2Y: -49.8; x: -33.4; y: -49.8 }
+ PathCubic { control1X: -33.4; control1Y: -49.8; control2X: -27.4; control2Y: -54; x: -33.2; y: -52.4 }
+ PathCubic { control1X: -33.2; control1Y: -52.4; control2X: -37.2; control2Y: -50.8; x: -40.2; y: -50.8 }
+ PathCubic { control1X: -40.2; control1Y: -50.8; control2X: -47.8; control2Y: -48.8; x: -43.8; y: -53 }
+ PathCubic { control1X: -39.8; control1Y: -57.2; control2X: -29.8; control2Y: -62.6; x: -26; y: -62.4 }
+ PathLine { x: -25.2; y: -60.8 }
+ PathLine { x: -14; y: -63.2 }
+ PathLine { x: -15.2; y: -62.4 }
+ PathCubic { control1X: -15.2; control1Y: -62.4; control2X: -15.4; control2Y: -62.6; x: -11.2; y: -63 }
+ PathCubic { control1X: -7; control1Y: -63.4; control2X: -1.2; control2Y: -62; x: 0.2; y: -63.8 }
+ PathCubic { control1X: 1.6; control1Y: -65.6; control2X: 5; control2Y: -66.6; x: 4.6; y: -65.2 }
+ PathCubic { control1X: 4.2; control1Y: -63.8; control2X: 4; control2Y: -61.8; x: 4; y: -61.8 }
+ PathCubic { control1X: 4; control1Y: -61.8; control2X: 9; control2Y: -67.6; x: 8.4; y: -65.4 }
+ PathCubic { control1X: 7.8; control1Y: -63.2; control2X: -0.4; control2Y: -58; x: -1.8; y: -51.8 }
+ PathLine { x: 8.6; y: -60 }
+ PathLine { x: 12.2; y: -63 }
+ PathCubic { control1X: 12.2; control1Y: -63; control2X: 15.8; control2Y: -60.8; x: 16; y: -62.4 }
+ PathCubic { control1X: 16.2; control1Y: -64; control2X: 20.8; control2Y: -69.8; x: 22; y: -69.6 }
+ PathCubic { control1X: 23.2; control1Y: -69.4; control2X: 25.2; control2Y: -72.2; x: 25; y: -69.6 }
+ PathCubic { control1X: 24.8; control1Y: -67; control2X: 32.4; control2Y: -61.6; x: 32.4; y: -61.6 }
+ PathCubic { control1X: 32.4; control1Y: -61.6; control2X: 35.6; control2Y: -63.4; x: 37; y: -62 }
+ PathCubic { control1X: 38.4; control1Y: -60.6; control2X: 42.6; control2Y: -81.8; x: 42.6; y: -81.8 }
+ PathLine { x: 67.6; y: -92.4 }
+ PathLine { x: 111.201; y: -95.8 }
+ PathLine { x: 94.201; y: -102.6 }
+ PathLine { x: 33.2; y: -114 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ PathMove { x: 51.4; y: 85 }
+ PathCubic { control1X: 51.4; control1Y: 85; control2X: 36.4; control2Y: 68.2; x: 28; y: 65.6 }
+ PathCubic { control1X: 28; control1Y: 65.6; control2X: 14.6; control2Y: 58.8; x: -10; y: 66.6 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ PathMove { x: 24.8; y: 64.2 }
+ PathCubic { control1X: 24.8; control1Y: 64.2; control2X: -0.4; control2Y: 56.2; x: -15.8; y: 60.4 }
+ PathCubic { control1X: -15.8; control1Y: 60.4; control2X: -34.2; control2Y: 62.4; x: -42.6; y: 76.2 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ PathMove { x: 21.2; y: 63 }
+ PathCubic { control1X: 21.2; control1Y: 63; control2X: 4.2; control2Y: 55.8; x: -10.6; y: 53.6 }
+ PathCubic { control1X: -10.6; control1Y: 53.6; control2X: -27.2; control2Y: 51; x: -43.8; y: 58.2 }
+ PathCubic { control1X: -43.8; control1Y: 58.2; control2X: -56; control2Y: 64.2; x: -61.4; y: 74.4 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ PathMove { x: 22.2; y: 63.4 }
+ PathCubic { control1X: 22.2; control1Y: 63.4; control2X: 6.8; control2Y: 52.4; x: 5.8; y: 51 }
+ PathCubic { control1X: 5.8; control1Y: 51; control2X: -1.2; control2Y: 40; x: -14.2; y: 39.6 }
+ PathCubic { control1X: -14.2; control1Y: 39.6; control2X: -35.6; control2Y: 40.4; x: -52.8; y: 48.4 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 20.895; y: 54.407 }
+ PathCubic { control1X: 22.437; control1Y: 55.87; control2X: 49.4; control2Y: 84.8; x: 49.4; y: 84.8 }
+ PathCubic { control1X: 84.6; control1Y: 121.401; control2X: 56.6; control2Y: 87.2; x: 56.6; y: 87.2 }
+ PathCubic { control1X: 49; control1Y: 82.4; control2X: 39.8; control2Y: 63.6; x: 39.8; y: 63.6 }
+ PathCubic { control1X: 38.6; control1Y: 60.8; control2X: 53.8; control2Y: 70.8; x: 53.8; y: 70.8 }
+ PathCubic { control1X: 57.8; control1Y: 71.6; control2X: 71.4; control2Y: 90.8; x: 71.4; y: 90.8 }
+ PathCubic { control1X: 64.6; control1Y: 88.4; control2X: 69.4; control2Y: 95.6; x: 69.4; y: 95.6 }
+ PathCubic { control1X: 72.2; control1Y: 97.6; control2X: 92.601; control2Y: 113.201; x: 92.601; y: 113.201 }
+ PathCubic { control1X: 96.201; control1Y: 117.201; control2X: 100.201; control2Y: 118.801; x: 100.201; y: 118.801 }
+ PathCubic { control1X: 114.201; control1Y: 113.601; control2X: 107.801; control2Y: 126.801; x: 107.801; y: 126.801 }
+ PathCubic { control1X: 110.201; control1Y: 133.601; control2X: 115.801; control2Y: 122.001; x: 115.801; y: 122.001 }
+ PathCubic { control1X: 127.001; control1Y: 105.2; control2X: 110.601; control2Y: 107.601; x: 110.601; y: 107.601 }
+ PathCubic { control1X: 80.6; control1Y: 110.401; control2X: 73.8; control2Y: 94.4; x: 73.8; y: 94.4 }
+ PathCubic { control1X: 71.4; control1Y: 92; control2X: 80.2; control2Y: 94.4; x: 80.2; y: 94.4 }
+ PathCubic { control1X: 88.601; control1Y: 96.4; control2X: 73; control2Y: 82; x: 73; y: 82 }
+ PathCubic { control1X: 75.4; control1Y: 82; control2X: 84.6; control2Y: 88.8; x: 84.6; y: 88.8 }
+ PathCubic { control1X: 95.001; control1Y: 98; control2X: 97.001; control2Y: 96; x: 97.001; y: 96 }
+ PathCubic { control1X: 115.001; control1Y: 87.2; control2X: 125.401; control2Y: 94.8; x: 125.401; y: 94.8 }
+ PathCubic { control1X: 127.401; control1Y: 96.4; control2X: 121.801; control2Y: 103.2; x: 123.401; y: 108.401 }
+ PathCubic { control1X: 125.001; control1Y: 113.601; control2X: 129.801; control2Y: 126.001; x: 129.801; y: 126.001 }
+ PathCubic { control1X: 127.401; control1Y: 127.601; control2X: 127.801; control2Y: 138.401; x: 127.801; y: 138.401 }
+ PathCubic { control1X: 144.601; control1Y: 161.601; control2X: 135.001; control2Y: 159.601; x: 135.001; y: 159.601 }
+ PathCubic { control1X: 119.401; control1Y: 159.201; control2X: 134.201; control2Y: 166.801; x: 134.201; y: 166.801 }
+ PathCubic { control1X: 137.401; control1Y: 168.801; control2X: 146.201; control2Y: 176.001; x: 146.201; y: 176.001 }
+ PathCubic { control1X: 143.401; control1Y: 174.801; control2X: 141.801; control2Y: 180.001; x: 141.801; y: 180.001 }
+ PathCubic { control1X: 146.601; control1Y: 184.001; control2X: 143.801; control2Y: 188.801; x: 143.801; y: 188.801 }
+ PathCubic { control1X: 137.801; control1Y: 190.001; control2X: 136.601; control2Y: 194.001; x: 136.601; y: 194.001 }
+ PathCubic { control1X: 143.401; control1Y: 202.001; control2X: 133.401; control2Y: 202.401; x: 133.401; y: 202.401 }
+ PathCubic { control1X: 137.001; control1Y: 206.801; control2X: 132.201; control2Y: 218.801; x: 132.201; y: 218.801 }
+ PathCubic { control1X: 127.401; control1Y: 218.801; control2X: 121.001; control2Y: 224.401; x: 121.001; y: 224.401 }
+ PathCubic { control1X: 123.401; control1Y: 229.201; control2X: 113.001; control2Y: 234.801; x: 113.001; y: 234.801 }
+ PathCubic { control1X: 104.601; control1Y: 236.401; control2X: 107.401; control2Y: 243.201; x: 107.401; y: 243.201 }
+ PathCubic { control1X: 99.401; control1Y: 249.201; control2X: 97.001; control2Y: 265.201; x: 97.001; y: 265.201 }
+ PathCubic { control1X: 96.201; control1Y: 275.601; control2X: 93.801; control2Y: 278.801; x: 99.001; y: 276.801 }
+ PathCubic { control1X: 104.201; control1Y: 274.801; control2X: 103.401; control2Y: 262.401; x: 103.401; y: 262.401 }
+ PathCubic { control1X: 98.601; control1Y: 246.801; control2X: 141.401; control2Y: 230.801; x: 141.401; y: 230.801 }
+ PathCubic { control1X: 145.401; control1Y: 229.201; control2X: 146.201; control2Y: 224.001; x: 146.201; y: 224.001 }
+ PathCubic { control1X: 148.201; control1Y: 224.401; control2X: 157.001; control2Y: 232.001; x: 157.001; y: 232.001 }
+ PathCubic { control1X: 164.601; control1Y: 243.201; control2X: 165.001; control2Y: 234.001; x: 165.001; y: 234.001 }
+ PathCubic { control1X: 166.201; control1Y: 230.401; control2X: 164.601; control2Y: 224.401; x: 164.601; y: 224.401 }
+ PathCubic { control1X: 170.601; control1Y: 202.801; control2X: 156.601; control2Y: 196.401; x: 156.601; y: 196.401 }
+ PathCubic { control1X: 146.601; control1Y: 162.801; control2X: 160.601; control2Y: 171.201; x: 160.601; y: 171.201 }
+ PathCubic { control1X: 163.401; control1Y: 176.801; control2X: 174.201; control2Y: 182.001; x: 174.201; y: 182.001 }
+ PathLine { x: 177.801; y: 179.601 }
+ PathCubic { control1X: 176.201; control1Y: 174.801; control2X: 184.601; control2Y: 168.801; x: 184.601; y: 168.801 }
+ PathCubic { control1X: 187.401; control1Y: 175.201; control2X: 193.401; control2Y: 167.201; x: 193.401; y: 167.201 }
+ PathCubic { control1X: 197.001; control1Y: 142.801; control2X: 209.401; control2Y: 157.201; x: 209.401; y: 157.201 }
+ PathCubic { control1X: 213.401; control1Y: 158.401; control2X: 214.601; control2Y: 151.601; x: 214.601; y: 151.601 }
+ PathCubic { control1X: 218.201; control1Y: 141.201; control2X: 214.601; control2Y: 127.601; x: 214.601; y: 127.601 }
+ PathCubic { control1X: 218.201; control1Y: 127.201; control2X: 227.801; control2Y: 133.201; x: 227.801; y: 133.201 }
+ PathCubic { control1X: 230.601; control1Y: 129.601; control2X: 221.401; control2Y: 112.801; x: 225.401; y: 115.201 }
+ PathCubic { control1X: 229.401; control1Y: 117.601; control2X: 233.801; control2Y: 119.201; x: 233.801; y: 119.201 }
+ PathCubic { control1X: 234.601; control1Y: 117.201; control2X: 224.601; control2Y: 104.801; x: 224.601; y: 104.801 }
+ PathCubic { control1X: 220.201; control1Y: 102; control2X: 215.001; control2Y: 81.6; x: 215.001; y: 81.6 }
+ PathCubic { control1X: 222.201; control1Y: 85.2; control2X: 212.201; control2Y: 70; x: 212.201; y: 70 }
+ PathCubic { control1X: 212.201; control1Y: 66.8; control2X: 218.201; control2Y: 55.6; x: 218.201; y: 55.6 }
+ PathCubic { control1X: 217.401; control1Y: 48.8; control2X: 218.201; control2Y: 49.2; x: 218.201; y: 49.2 }
+ PathCubic { control1X: 221.001; control1Y: 50.4; control2X: 229.001; control2Y: 52; x: 222.201; y: 45.6 }
+ PathCubic { control1X: 215.401; control1Y: 39.2; control2X: 223.001; control2Y: 34.4; x: 223.001; y: 34.4 }
+ PathCubic { control1X: 227.401; control1Y: 31.6; control2X: 213.801; control2Y: 32; x: 213.801; y: 32 }
+ PathCubic { control1X: 208.601; control1Y: 27.6; control2X: 209.001; control2Y: 23.6; x: 209.001; y: 23.6 }
+ PathCubic { control1X: 217.001; control1Y: 25.6; control2X: 202.601; control2Y: 11.2; x: 200.201; y: 7.6 }
+ PathCubic { control1X: 197.801; control1Y: 4; control2X: 207.401; control2Y: -1.2; x: 207.401; y: -1.2 }
+ PathCubic { control1X: 220.601; control1Y: -4.8; control2X: 209.001; control2Y: -8; x: 209.001; y: -8 }
+ PathCubic { control1X: 189.401; control1Y: -7.6; control2X: 200.201; control2Y: -18.4; x: 200.201; y: -18.4 }
+ PathCubic { control1X: 206.201; control1Y: -18; control2X: 204.601; control2Y: -20.4; x: 204.601; y: -20.4 }
+ PathCubic { control1X: 199.401; control1Y: -21.6; control2X: 189.801; control2Y: -28; x: 189.801; y: -28 }
+ PathCubic { control1X: 185.801; control1Y: -31.6; control2X: 189.401; control2Y: -30.8; x: 189.401; y: -30.8 }
+ PathCubic { control1X: 206.201; control1Y: -29.6; control2X: 177.401; control2Y: -40.8; x: 177.401; y: -40.8 }
+ PathCubic { control1X: 185.401; control1Y: -40.8; control2X: 167.401; control2Y: -51.2; x: 167.401; y: -51.2 }
+ PathCubic { control1X: 165.401; control1Y: -52.8; control2X: 162.201; control2Y: -60.4; x: 162.201; y: -60.4 }
+ PathCubic { control1X: 156.201; control1Y: -65.6; control2X: 151.401; control2Y: -72.4; x: 151.401; y: -72.4 }
+ PathCubic { control1X: 151.001; control1Y: -76.8; control2X: 146.201; control2Y: -81.6; x: 146.201; y: -81.6 }
+ PathCubic { control1X: 134.601; control1Y: -95.2; control2X: 129.001; control2Y: -94.8; x: 129.001; y: -94.8 }
+ PathCubic { control1X: 114.201; control1Y: -98.4; control2X: 109.001; control2Y: -97.6; x: 109.001; y: -97.6 }
+ PathLine { x: 56.2; y: -93.2 }
+ PathCubic { control1X: 29.8; control1Y: -80.4; control2X: 37.6; control2Y: -59.4; x: 37.6; y: -59.4 }
+ PathCubic { control1X: 44; control1Y: -51; control2X: 53.2; control2Y: -54.8; x: 53.2; y: -54.8 }
+ PathCubic { control1X: 57.8; control1Y: -61; control2X: 69.4; control2Y: -58.8; x: 69.4; y: -58.8 }
+ PathCubic { control1X: 89.801; control1Y: -55.6; control2X: 87.201; control2Y: -59.2; x: 87.201; y: -59.2 }
+ PathCubic { control1X: 84.801; control1Y: -63.8; control2X: 68.6; control2Y: -70; x: 68.4; y: -70.6 }
+ PathCubic { control1X: 68.2; control1Y: -71.2; control2X: 59.4; control2Y: -74.6; x: 59.4; y: -74.6 }
+ PathCubic { control1X: 56.4; control1Y: -75.8; control2X: 52; control2Y: -85; x: 52; y: -85 }
+ PathCubic { control1X: 48.8; control1Y: -88.4; control2X: 64.6; control2Y: -82.6; x: 64.6; y: -82.6 }
+ PathCubic { control1X: 63.4; control1Y: -81.6; control2X: 70.8; control2Y: -77.6; x: 70.8; y: -77.6 }
+ PathCubic { control1X: 88.201; control1Y: -78.6; control2X: 98.801; control2Y: -67.8; x: 98.801; y: -67.8 }
+ PathCubic { control1X: 109.601; control1Y: -51.2; control2X: 109.801; control2Y: -59.4; x: 109.801; y: -59.4 }
+ PathCubic { control1X: 112.601; control1Y: -68.8; control2X: 100.801; control2Y: -90; x: 100.801; y: -90 }
+ PathCubic { control1X: 101.201; control1Y: -92; control2X: 109.401; control2Y: -85.4; x: 109.401; y: -85.4 }
+ PathCubic { control1X: 110.801; control1Y: -87.4; control2X: 111.601; control2Y: -81.6; x: 111.601; y: -81.6 }
+ PathCubic { control1X: 111.801; control1Y: -79.2; control2X: 115.601; control2Y: -71.2; x: 115.601; y: -71.2 }
+ PathCubic { control1X: 118.401; control1Y: -58.2; control2X: 122.001; control2Y: -65.6; x: 122.001; y: -65.6 }
+ PathLine { x: 126.601; y: -56.2 }
+ PathCubic { control1X: 128.001; control1Y: -53.6; control2X: 122.001; control2Y: -46; x: 122.001; y: -46 }
+ PathCubic { control1X: 121.801; control1Y: -43.2; control2X: 122.601; control2Y: -43.4; x: 117.001; y: -35.8 }
+ PathCubic { control1X: 111.401; control1Y: -28.2; control2X: 114.801; control2Y: -23.8; x: 114.801; y: -23.8 }
+ PathCubic { control1X: 113.401; control1Y: -17.2; control2X: 122.201; control2Y: -17.6; x: 122.201; y: -17.6 }
+ PathCubic { control1X: 124.801; control1Y: -15.4; control2X: 128.201; control2Y: -15.4; x: 128.201; y: -15.4 }
+ PathCubic { control1X: 130.001; control1Y: -13.4; control2X: 132.401; control2Y: -14; x: 132.401; y: -14 }
+ PathCubic { control1X: 134.001; control1Y: -17.8; control2X: 140.201; control2Y: -15.8; x: 140.201; y: -15.8 }
+ PathCubic { control1X: 141.601; control1Y: -18.2; control2X: 149.801; control2Y: -18.6; x: 149.801; y: -18.6 }
+ PathCubic { control1X: 150.801; control1Y: -21.2; control2X: 151.201; control2Y: -22.8; x: 154.601; y: -23.4 }
+ PathCubic { control1X: 158.001; control1Y: -24; control2X: 133.401; control2Y: -67; x: 133.401; y: -67 }
+ PathCubic { control1X: 139.801; control1Y: -67.8; control2X: 131.601; control2Y: -80.2; x: 131.601; y: -80.2 }
+ PathCubic { control1X: 129.401; control1Y: -86.8; control2X: 140.801; control2Y: -72.2; x: 143.001; y: -70.8 }
+ PathCubic { control1X: 145.201; control1Y: -69.4; control2X: 146.201; control2Y: -67.2; x: 144.601; y: -67.4 }
+ PathCubic { control1X: 143.001; control1Y: -67.6; control2X: 141.201; control2Y: -65.4; x: 142.601; y: -65.2 }
+ PathCubic { control1X: 144.001; control1Y: -65; control2X: 157.001; control2Y: -50; x: 160.401; y: -39.8 }
+ PathCubic { control1X: 163.801; control1Y: -29.6; control2X: 169.801; control2Y: -25.6; x: 176.001; y: -19.6 }
+ PathCubic { control1X: 182.201; control1Y: -13.6; control2X: 181.401; control2Y: 10.6; x: 181.401; y: 10.6 }
+ PathCubic { control1X: 181.001; control1Y: 19.4; control2X: 187.001; control2Y: 30; x: 187.001; y: 30 }
+ PathCubic { control1X: 189.001; control1Y: 33.8; control2X: 184.801; control2Y: 52; x: 184.801; y: 52 }
+ PathCubic { control1X: 182.801; control1Y: 54.2; control2X: 184.201; control2Y: 55; x: 184.201; y: 55 }
+ PathCubic { control1X: 185.201; control1Y: 56.2; control2X: 192.001; control2Y: 69.4; x: 192.001; y: 69.4 }
+ PathCubic { control1X: 190.201; control1Y: 69.2; control2X: 193.801; control2Y: 72.8; x: 193.801; y: 72.8 }
+ PathCubic { control1X: 199.001; control1Y: 78.8; control2X: 192.601; control2Y: 75.8; x: 192.601; y: 75.8 }
+ PathCubic { control1X: 186.601; control1Y: 74.2; control2X: 193.601; control2Y: 84; x: 193.601; y: 84 }
+ PathCubic { control1X: 194.801; control1Y: 85.8; control2X: 185.801; control2Y: 81.2; x: 185.801; y: 81.2 }
+ PathCubic { control1X: 176.601; control1Y: 80.6; control2X: 188.201; control2Y: 87.8; x: 188.201; y: 87.8 }
+ PathCubic { control1X: 196.801; control1Y: 95; control2X: 185.401; control2Y: 90.6; x: 185.401; y: 90.6 }
+ PathCubic { control1X: 180.801; control1Y: 88.8; control2X: 184.001; control2Y: 95.6; x: 184.001; y: 95.6 }
+ PathCubic { control1X: 187.201; control1Y: 97.2; control2X: 204.401; control2Y: 104.2; x: 204.401; y: 104.2 }
+ PathCubic { control1X: 204.801; control1Y: 108.001; control2X: 201.801; control2Y: 113.001; x: 201.801; y: 113.001 }
+ PathCubic { control1X: 202.201; control1Y: 117.001; control2X: 200.001; control2Y: 120.401; x: 200.001; y: 120.401 }
+ PathCubic { control1X: 198.801; control1Y: 128.601; control2X: 198.201; control2Y: 129.401; x: 198.201; y: 129.401 }
+ PathCubic { control1X: 194.001; control1Y: 129.601; control2X: 186.601; control2Y: 143.401; x: 186.601; y: 143.401 }
+ PathCubic { control1X: 184.801; control1Y: 146.001; control2X: 174.601; control2Y: 158.001; x: 174.601; y: 158.001 }
+ PathCubic { control1X: 172.601; control1Y: 165.001; control2X: 154.601; control2Y: 157.801; x: 154.601; y: 157.801 }
+ PathCubic { control1X: 148.001; control1Y: 161.201; control2X: 150.001; control2Y: 157.801; x: 150.001; y: 157.801 }
+ PathCubic { control1X: 149.601; control1Y: 155.601; control2X: 154.401; control2Y: 149.601; x: 154.401; y: 149.601 }
+ PathCubic { control1X: 161.401; control1Y: 147.001; control2X: 158.801; control2Y: 136.201; x: 158.801; y: 136.201 }
+ PathCubic { control1X: 162.801; control1Y: 134.801; control2X: 151.601; control2Y: 132.001; x: 151.801; y: 130.801 }
+ PathCubic { control1X: 152.001; control1Y: 129.601; control2X: 157.801; control2Y: 128.201; x: 157.801; y: 128.201 }
+ PathCubic { control1X: 165.801; control1Y: 126.201; control2X: 161.401; control2Y: 123.801; x: 161.401; y: 123.801 }
+ PathCubic { control1X: 160.801; control1Y: 119.801; control2X: 163.801; control2Y: 114.201; x: 163.801; y: 114.201 }
+ PathCubic { control1X: 175.401; control1Y: 113.401; control2X: 163.801; control2Y: 97.2; x: 163.801; y: 97.2 }
+ PathCubic { control1X: 153.001; control1Y: 89.6; control2X: 152.001; control2Y: 83.8; x: 152.001; y: 83.8 }
+ PathCubic { control1X: 164.601; control1Y: 75.6; control2X: 156.401; control2Y: 63.2; x: 156.601; y: 59.6 }
+ PathCubic { control1X: 156.801; control1Y: 56; control2X: 158.001; control2Y: 34.4; x: 158.001; y: 34.4 }
+ PathCubic { control1X: 156.001; control1Y: 28.2; control2X: 153.001; control2Y: 14.6; x: 153.001; y: 14.6 }
+ PathCubic { control1X: 155.201; control1Y: 9.4; control2X: 162.601; control2Y: -3.2; x: 162.601; y: -3.2 }
+ PathCubic { control1X: 165.401; control1Y: -7.4; control2X: 174.201; control2Y: -12.2; x: 172.001; y: -15.2 }
+ PathCubic { control1X: 169.801; control1Y: -18.2; control2X: 162.001; control2Y: -16.4; x: 162.001; y: -16.4 }
+ PathCubic { control1X: 154.201; control1Y: -17.8; control2X: 154.801; control2Y: -12.6; x: 154.801; y: -12.6 }
+ PathCubic { control1X: 153.201; control1Y: -11.6; control2X: 152.401; control2Y: -6.6; x: 152.401; y: -6.6 }
+ PathCubic { control1X: 151.68; control1Y: 1.333; control2X: 142.801; control2Y: 7.6; x: 142.801; y: 7.6 }
+ PathCubic { control1X: 131.601; control1Y: 13.8; control2X: 140.801; control2Y: 17.8; x: 140.801; y: 17.8 }
+ PathCubic { control1X: 146.801; control1Y: 24.4; control2X: 137.001; control2Y: 24.6; x: 137.001; y: 24.6 }
+ PathCubic { control1X: 126.001; control1Y: 22.8; control2X: 134.201; control2Y: 33; x: 134.201; y: 33 }
+ PathCubic { control1X: 145.001; control1Y: 45.8; control2X: 142.001; control2Y: 48.6; x: 142.001; y: 48.6 }
+ PathCubic { control1X: 131.801; control1Y: 49.6; control2X: 144.401; control2Y: 58.8; x: 144.401; y: 58.8 }
+ PathCubic { control1X: 144.401; control1Y: 58.8; control2X: 143.601; control2Y: 56.8; x: 143.801; y: 58.6 }
+ PathCubic { control1X: 144.001; control1Y: 60.4; control2X: 147.001; control2Y: 64.6; x: 147.801; y: 66.6 }
+ PathCubic { control1X: 148.601; control1Y: 68.6; control2X: 144.601; control2Y: 68.8; x: 144.601; y: 68.8 }
+ PathCubic { control1X: 145.201; control1Y: 78.4; control2X: 129.801; control2Y: 74.2; x: 129.801; y: 74.2 }
+ PathCubic { control1X: 129.801; control1Y: 74.2; control2X: 129.801; control2Y: 74.2; x: 128.201; y: 74.4 }
+ PathCubic { control1X: 126.601; control1Y: 74.6; control2X: 115.401; control2Y: 73.8; x: 109.601; y: 71.6 }
+ PathCubic { control1X: 103.801; control1Y: 69.4; control2X: 97.001; control2Y: 69.4; x: 97.001; y: 69.4 }
+ PathCubic { control1X: 97.001; control1Y: 69.4; control2X: 93.001; control2Y: 71.2; x: 85.4; y: 71 }
+ PathCubic { control1X: 77.8; control1Y: 70.8; control2X: 69.8; control2Y: 73.6; x: 69.8; y: 73.6 }
+ PathCubic { control1X: 65.4; control1Y: 73.2; control2X: 74; control2Y: 68.8; x: 74.2; y: 69 }
+ PathCubic { control1X: 74.4; control1Y: 69.2; control2X: 80; control2Y: 63.6; x: 72; y: 64.2 }
+ PathCubic { control1X: 50.203; control1Y: 65.835; control2X: 39.4; control2Y: 55.6; x: 39.4; y: 55.6 }
+ PathCubic { control1X: 37.4; control1Y: 54.2; control2X: 34.8; control2Y: 51.4; x: 34.8; y: 51.4 }
+ PathCubic { control1X: 24.8; control1Y: 49.4; control2X: 36.2; control2Y: 63.8; x: 36.2; y: 63.8 }
+ PathCubic { control1X: 37.4; control1Y: 65.2; control2X: 36; control2Y: 66.2; x: 36; y: 66.2 }
+ PathCubic { control1X: 35.2; control1Y: 64.6; control2X: 27.4; control2Y: 59.2; x: 27.4; y: 59.2 }
+ PathCubic { control1X: 24.589; control1Y: 58.227; control2X: 23.226; control2Y: 56.893; x: 20.895; y: 54.407 }
+ }
+
+ ShapePath {
+ fillColor: "#4c0000"
+ strokeWidth: -1
+ PathMove { x: -3; y: 42.8 }
+ PathCubic { control1X: -3; control1Y: 42.8; control2X: 8.6; control2Y: 48.4; x: 11.2; y: 51.2 }
+ PathCubic { control1X: 13.8; control1Y: 54; control2X: 27.8; control2Y: 65.4; x: 27.8; y: 65.4 }
+ PathCubic { control1X: 27.8; control1Y: 65.4; control2X: 22.4; control2Y: 63.4; x: 19.8; y: 61.6 }
+ PathCubic { control1X: 17.2; control1Y: 59.8; control2X: 6.4; control2Y: 51.6; x: 6.4; y: 51.6 }
+ PathCubic { control1X: 6.4; control1Y: 51.6; control2X: 2.6; control2Y: 45.6; x: -3; y: 42.8 }
+ }
+
+ ShapePath {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ PathMove { x: -61.009; y: 11.603 }
+ PathCubic { control1X: -60.672; control1Y: 11.455; control2X: -61.196; control2Y: 8.743; x: -61.4; y: 8.2 }
+ PathCubic { control1X: -62.422; control1Y: 5.474; control2X: -71.4; control2Y: 4; x: -71.4; y: 4 }
+ PathCubic { control1X: -71.627; control1Y: 5.365; control2X: -71.682; control2Y: 6.961; x: -71.576; y: 8.599 }
+ PathCubic { control1X: -71.576; control1Y: 8.599; control2X: -66.708; control2Y: 14.118; x: -61.009; y: 11.603 }
+ }
+
+ ShapePath {
+ fillColor: "#659900"
+ strokeWidth: -1
+ PathMove { x: -61.009; y: 11.403 }
+ PathCubic { control1X: -61.458; control1Y: 11.561; control2X: -61.024; control2Y: 8.669; x: -61.2; y: 8.2 }
+ PathCubic { control1X: -62.222; control1Y: 5.474; control2X: -71.4; control2Y: 3.9; x: -71.4; y: 3.9 }
+ PathCubic { control1X: -71.627; control1Y: 5.265; control2X: -71.682; control2Y: 6.861; x: -71.576; y: 8.499 }
+ PathCubic { control1X: -71.576; control1Y: 8.499; control2X: -67.308; control2Y: 13.618; x: -61.009; y: 11.403 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -65.4; y: 11.546 }
+ PathCubic { control1X: -66.025; control1Y: 11.546; control2X: -66.531; control2Y: 10.406; x: -66.531; y: 9 }
+ PathCubic { control1X: -66.531; control1Y: 7.595; control2X: -66.025; control2Y: 6.455; x: -65.4; y: 6.455 }
+ PathCubic { control1X: -64.775; control1Y: 6.455; control2X: -64.268; control2Y: 7.595; x: -64.268; y: 9 }
+ PathCubic { control1X: -64.268; control1Y: 10.406; control2X: -64.775; control2Y: 11.546; x: -65.4; y: 11.546 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -65.4; y: 9 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -111; y: 109.601 }
+ PathCubic { control1X: -111; control1Y: 109.601; control2X: -116.6; control2Y: 119.601; x: -91.8; y: 113.601 }
+ PathCubic { control1X: -91.8; control1Y: 113.601; control2X: -77.8; control2Y: 112.401; x: -75.4; y: 110.001 }
+ PathCubic { control1X: -74.2; control1Y: 110.801; control2X: -65.834; control2Y: 113.734; x: -63; y: 114.401 }
+ PathCubic { control1X: -56.2; control1Y: 116.001; control2X: -47.8; control2Y: 106; x: -47.8; y: 106 }
+ PathCubic { control1X: -47.8; control1Y: 106; control2X: -43.2; control2Y: 95.5; x: -40.4; y: 95.5 }
+ PathCubic { control1X: -37.6; control1Y: 95.5; control2X: -40.8; control2Y: 97.1; x: -40.8; y: 97.1 }
+ PathCubic { control1X: -40.8; control1Y: 97.1; control2X: -47.4; control2Y: 107.201; x: -47; y: 108.801 }
+ PathCubic { control1X: -47; control1Y: 108.801; control2X: -52.2; control2Y: 128.801; x: -68.2; y: 129.601 }
+ PathCubic { control1X: -68.2; control1Y: 129.601; control2X: -84.35; control2Y: 130.551; x: -83; y: 136.401 }
+ PathCubic { control1X: -83; control1Y: 136.401; control2X: -74.2; control2Y: 134.001; x: -71.8; y: 136.401 }
+ PathCubic { control1X: -71.8; control1Y: 136.401; control2X: -61; control2Y: 136.001; x: -69; y: 142.401 }
+ PathLine { x: -75.8; y: 154.001 }
+ PathCubic { control1X: -75.8; control1Y: 154.001; control2X: -75.66; control2Y: 157.919; x: -85.8; y: 154.401 }
+ PathCubic { control1X: -95.6; control1Y: 151.001; control2X: -105.9; control2Y: 138.101; x: -105.9; y: 138.101 }
+ PathCubic { control1X: -105.9; control1Y: 138.101; control2X: -121.85; control2Y: 123.551; x: -111; y: 109.601 }
+ }
+
+ ShapePath {
+ fillColor: "#e59999"
+ strokeWidth: -1
+ PathMove { x: -112.2; y: 113.601 }
+ PathCubic { control1X: -112.2; control1Y: 113.601; control2X: -114.2; control2Y: 123.201; x: -77.4; y: 112.801 }
+ PathCubic { control1X: -77.4; control1Y: 112.801; control2X: -73; control2Y: 112.801; x: -70.6; y: 113.601 }
+ PathCubic { control1X: -68.2; control1Y: 114.401; control2X: -56.2; control2Y: 117.201; x: -54.2; y: 116.001 }
+ PathCubic { control1X: -54.2; control1Y: 116.001; control2X: -61.4; control2Y: 129.601; x: -73; y: 128.001 }
+ PathCubic { control1X: -73; control1Y: 128.001; control2X: -86.2; control2Y: 129.601; x: -85.8; y: 134.401 }
+ PathCubic { control1X: -85.8; control1Y: 134.401; control2X: -81.8; control2Y: 141.601; x: -77; y: 144.001 }
+ PathCubic { control1X: -77; control1Y: 144.001; control2X: -74.2; control2Y: 146.401; x: -74.6; y: 149.601 }
+ PathCubic { control1X: -75; control1Y: 152.801; control2X: -77.8; control2Y: 154.401; x: -79.8; y: 155.201 }
+ PathCubic { control1X: -81.8; control1Y: 156.001; control2X: -85; control2Y: 152.801; x: -86.6; y: 152.801 }
+ PathCubic { control1X: -88.2; control1Y: 152.801; control2X: -96.6; control2Y: 146.401; x: -101; y: 141.601 }
+ PathCubic { control1X: -105.4; control1Y: 136.801; control2X: -113.8; control2Y: 124.801; x: -113.4; y: 122.001 }
+ PathCubic { control1X: -113; control1Y: 119.201; control2X: -112.2; control2Y: 113.601; x: -112.2; y: 113.601 }
+ }
+
+ ShapePath {
+ fillColor: "#b26565"
+ strokeWidth: -1
+ PathMove { x: -109; y: 131.051 }
+ PathCubic { control1X: -106.4; control1Y: 135.001; control2X: -103.2; control2Y: 139.201; x: -101; y: 141.601 }
+ PathCubic { control1X: -96.6; control1Y: 146.401; control2X: -88.2; control2Y: 152.801; x: -86.6; y: 152.801 }
+ PathCubic { control1X: -85; control1Y: 152.801; control2X: -81.8; control2Y: 156.001; x: -79.8; y: 155.201 }
+ PathCubic { control1X: -77.8; control1Y: 154.401; control2X: -75; control2Y: 152.801; x: -74.6; y: 149.601 }
+ PathCubic { control1X: -74.2; control1Y: 146.401; control2X: -77; control2Y: 144.001; x: -77; y: 144.001 }
+ PathCubic { control1X: -80.066; control1Y: 142.468; control2X: -82.806; control2Y: 138.976; x: -84.385; y: 136.653 }
+ PathCubic { control1X: -84.385; control1Y: 136.653; control2X: -84.2; control2Y: 139.201; x: -89.4; y: 138.401 }
+ PathCubic { control1X: -94.6; control1Y: 137.601; control2X: -99.8; control2Y: 134.801; x: -101.4; y: 131.601 }
+ PathCubic { control1X: -103; control1Y: 128.401; control2X: -105.4; control2Y: 126.001; x: -103.8; y: 129.601 }
+ PathCubic { control1X: -102.2; control1Y: 133.201; control2X: -99.8; control2Y: 136.801; x: -98.2; y: 137.201 }
+ PathCubic { control1X: -96.6; control1Y: 137.601; control2X: -97; control2Y: 138.801; x: -99.4; y: 138.401 }
+ PathCubic { control1X: -101.8; control1Y: 138.001; control2X: -104.6; control2Y: 137.601; x: -109; y: 132.401 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: -111.6; y: 110.001 }
+ PathCubic { control1X: -111.6; control1Y: 110.001; control2X: -109.8; control2Y: 96.4; x: -108.6; y: 92.4 }
+ PathCubic { control1X: -108.6; control1Y: 92.4; control2X: -109.4; control2Y: 85.6; x: -107; y: 81.4 }
+ PathCubic { control1X: -104.6; control1Y: 77.2; control2X: -102.6; control2Y: 71; x: -99.6; y: 65.6 }
+ PathCubic { control1X: -96.6; control1Y: 60.2; control2X: -96.4; control2Y: 56.2; x: -92.4; y: 54.6 }
+ PathCubic { control1X: -88.4; control1Y: 53; control2X: -82.4; control2Y: 44.4; x: -79.6; y: 43.4 }
+ PathCubic { control1X: -76.8; control1Y: 42.4; control2X: -77; control2Y: 43.2; x: -77; y: 43.2 }
+ PathCubic { control1X: -77; control1Y: 43.2; control2X: -70.2; control2Y: 28.4; x: -56.6; y: 32.4 }
+ PathCubic { control1X: -56.6; control1Y: 32.4; control2X: -72.8; control2Y: 29.6; x: -57; y: 20.2 }
+ PathCubic { control1X: -57; control1Y: 20.2; control2X: -61.8; control2Y: 21.3; x: -58.5; y: 14.3 }
+ PathCubic { control1X: -56.299; control1Y: 9.632; control2X: -56.8; control2Y: 16.4; x: -67.8; y: 28.2 }
+ PathCubic { control1X: -67.8; control1Y: 28.2; control2X: -72.8; control2Y: 36.8; x: -78; y: 39.8 }
+ PathCubic { control1X: -83.2; control1Y: 42.8; control2X: -95.2; control2Y: 49.8; x: -96.4; y: 53.6 }
+ PathCubic { control1X: -97.6; control1Y: 57.4; control2X: -100.8; control2Y: 63.2; x: -102.8; y: 64.8 }
+ PathCubic { control1X: -104.8; control1Y: 66.4; control2X: -107.6; control2Y: 70.6; x: -108; y: 74 }
+ PathCubic { control1X: -108; control1Y: 74; control2X: -109.2; control2Y: 78; x: -110.6; y: 79.2 }
+ PathCubic { control1X: -112; control1Y: 80.4; control2X: -112.2; control2Y: 83.6; x: -112.2; y: 85.6 }
+ PathCubic { control1X: -112.2; control1Y: 87.6; control2X: -114.2; control2Y: 90.4; x: -114; y: 92.8 }
+ PathCubic { control1X: -114; control1Y: 92.8; control2X: -113.2; control2Y: 111.801; x: -113.6; y: 113.801 }
+ PathLine { x: -111.6; y: 110.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: -120.2; y: 114.601 }
+ PathCubic { control1X: -120.2; control1Y: 114.601; control2X: -122.2; control2Y: 113.201; x: -126.6; y: 119.201 }
+ PathCubic { control1X: -126.6; control1Y: 119.201; control2X: -119.3; control2Y: 152.201; x: -119.3; y: 153.601 }
+ PathCubic { control1X: -119.3; control1Y: 153.601; control2X: -118.2; control2Y: 151.501; x: -119.5; y: 144.301 }
+ PathCubic { control1X: -120.8; control1Y: 137.101; control2X: -121.7; control2Y: 124.401; x: -121.7; y: 124.401 }
+ PathLine { x: -120.2; y: 114.601 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: -98.6; y: 54 }
+ PathCubic { control1X: -98.6; control1Y: 54; control2X: -116.2; control2Y: 57.2; x: -115.8; y: 86.4 }
+ PathLine { x: -116.6; y: 111.201 }
+ PathCubic { control1X: -116.6; control1Y: 111.201; control2X: -117.8; control2Y: 85.6; x: -119; y: 84 }
+ PathCubic { control1X: -120.2; control1Y: 82.4; control2X: -116.2; control2Y: 71.2; x: -119.4; y: 77.2 }
+ PathCubic { control1X: -119.4; control1Y: 77.2; control2X: -133.4; control2Y: 91.2; x: -125.4; y: 112.401 }
+ PathCubic { control1X: -125.4; control1Y: 112.401; control2X: -123.9; control2Y: 115.701; x: -126.9; y: 111.101 }
+ PathCubic { control1X: -126.9; control1Y: 111.101; control2X: -131.5; control2Y: 98.5; x: -130.4; y: 92.1 }
+ PathCubic { control1X: -130.4; control1Y: 92.1; control2X: -130.2; control2Y: 89.9; x: -128.3; y: 87.1 }
+ PathCubic { control1X: -128.3; control1Y: 87.1; control2X: -119.7; control2Y: 75.4; x: -117; y: 73.1 }
+ PathCubic { control1X: -117; control1Y: 73.1; control2X: -115.2; control2Y: 58.7; x: -99.8; y: 53.5 }
+ PathCubic { control1X: -99.8; control1Y: 53.5; control2X: -94.1; control2Y: 51.2; x: -98.6; y: 54 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 40.8; y: -12.2 }
+ PathCubic { control1X: 41.46; control1Y: -12.554; control2X: 41.451; control2Y: -13.524; x: 42.031; y: -13.697 }
+ PathCubic { control1X: 43.18; control1Y: -14.041; control2X: 43.344; control2Y: -15.108; x: 43.862; y: -15.892 }
+ PathCubic { control1X: 44.735; control1Y: -17.211; control2X: 44.928; control2Y: -18.744; x: 45.51; y: -20.235 }
+ PathCubic { control1X: 45.782; control1Y: -20.935; control2X: 45.809; control2Y: -21.89; x: 45.496; y: -22.55 }
+ PathCubic { control1X: 44.322; control1Y: -25.031; control2X: 43.62; control2Y: -27.48; x: 42.178; y: -29.906 }
+ PathCubic { control1X: 41.91; control1Y: -30.356; control2X: 41.648; control2Y: -31.15; x: 41.447; y: -31.748 }
+ PathCubic { control1X: 40.984; control1Y: -33.132; control2X: 39.727; control2Y: -34.123; x: 38.867; y: -35.443 }
+ PathCubic { control1X: 38.579; control1Y: -35.884; control2X: 39.104; control2Y: -36.809; x: 38.388; y: -36.893 }
+ PathCubic { control1X: 37.491; control1Y: -36.998; control2X: 36.042; control2Y: -37.578; x: 35.809; y: -36.552 }
+ PathCubic { control1X: 35.221; control1Y: -33.965; control2X: 36.232; control2Y: -31.442; x: 37.2; y: -29 }
+ PathCubic { control1X: 36.418; control1Y: -28.308; control2X: 36.752; control2Y: -27.387; x: 36.904; y: -26.62 }
+ PathCubic { control1X: 37.614; control1Y: -23.014; control2X: 36.416; control2Y: -19.662; x: 35.655; y: -16.188 }
+ PathCubic { control1X: 35.632; control1Y: -16.084; control2X: 35.974; control2Y: -15.886; x: 35.946; y: -15.824 }
+ PathCubic { control1X: 34.724; control1Y: -13.138; control2X: 33.272; control2Y: -10.693; x: 31.453; y: -8.312 }
+ PathCubic { control1X: 30.695; control1Y: -7.32; control2X: 29.823; control2Y: -6.404; x: 29.326; y: -5.341 }
+ PathCubic { control1X: 28.958; control1Y: -4.554; control2X: 28.55; control2Y: -3.588; x: 28.8; y: -2.6 }
+ PathCubic { control1X: 25.365; control1Y: 0.18; control2X: 23.115; control2Y: 4.025; x: 20.504; y: 7.871 }
+ PathCubic { control1X: 20.042; control1Y: 8.551; control2X: 20.333; control2Y: 9.76; x: 20.884; y: 10.029 }
+ PathCubic { control1X: 21.697; control1Y: 10.427; control2X: 22.653; control2Y: 9.403; x: 23.123; y: 8.557 }
+ PathCubic { control1X: 23.512; control1Y: 7.859; control2X: 23.865; control2Y: 7.209; x: 24.356; y: 6.566 }
+ PathCubic { control1X: 24.489; control1Y: 6.391; control2X: 24.31; control2Y: 5.972; x: 24.445; y: 5.851 }
+ PathCubic { control1X: 27.078; control1Y: 3.504; control2X: 28.747; control2Y: 0.568; x: 31.2; y: -1.8 }
+ PathCubic { control1X: 33.15; control1Y: -2.129; control2X: 34.687; control2Y: -3.127; x: 36.435; y: -4.14 }
+ PathCubic { control1X: 36.743; control1Y: -4.319; control2X: 37.267; control2Y: -4.07; x: 37.557; y: -4.265 }
+ PathCubic { control1X: 39.31; control1Y: -5.442; control2X: 39.308; control2Y: -7.478; x: 39.414; y: -9.388 }
+ PathCubic { control1X: 39.464; control1Y: -10.272; control2X: 39.66; control2Y: -11.589; x: 40.8; y: -12.2 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 31.959; y: -16.666 }
+ PathCubic { control1X: 32.083; control1Y: -16.743; control2X: 31.928; control2Y: -17.166; x: 32.037; y: -17.382 }
+ PathCubic { control1X: 32.199; control1Y: -17.706; control2X: 32.602; control2Y: -17.894; x: 32.764; y: -18.218 }
+ PathCubic { control1X: 32.873; control1Y: -18.434; control2X: 32.71; control2Y: -18.814; x: 32.846; y: -18.956 }
+ PathCubic { control1X: 35.179; control1Y: -21.403; control2X: 35.436; control2Y: -24.427; x: 34.4; y: -27.4 }
+ PathCubic { control1X: 35.424; control1Y: -28.02; control2X: 35.485; control2Y: -29.282; x: 35.06; y: -30.129 }
+ PathCubic { control1X: 34.207; control1Y: -31.829; control2X: 34.014; control2Y: -33.755; x: 33.039; y: -35.298 }
+ PathCubic { control1X: 32.237; control1Y: -36.567; control2X: 30.659; control2Y: -37.811; x: 29.288; y: -36.508 }
+ PathCubic { control1X: 28.867; control1Y: -36.108; control2X: 28.546; control2Y: -35.321; x: 28.824; y: -34.609 }
+ PathCubic { control1X: 28.888; control1Y: -34.446; control2X: 29.173; control2Y: -34.3; x: 29.146; y: -34.218 }
+ PathCubic { control1X: 29.039; control1Y: -33.894; control2X: 28.493; control2Y: -33.67; x: 28.487; y: -33.398 }
+ PathCubic { control1X: 28.457; control1Y: -31.902; control2X: 27.503; control2Y: -30.391; x: 28.133; y: -29.062 }
+ PathCubic { control1X: 28.905; control1Y: -27.433; control2X: 29.724; control2Y: -25.576; x: 30.4; y: -23.8 }
+ PathCubic { control1X: 29.166; control1Y: -21.684; control2X: 30.199; control2Y: -19.235; x: 28.446; y: -17.358 }
+ PathCubic { control1X: 28.31; control1Y: -17.212; control2X: 28.319; control2Y: -16.826; x: 28.441; y: -16.624 }
+ PathCubic { control1X: 28.733; control1Y: -16.138; control2X: 29.139; control2Y: -15.732; x: 29.625; y: -15.44 }
+ PathCubic { control1X: 29.827; control1Y: -15.319; control2X: 30.175; control2Y: -15.317; x: 30.375; y: -15.441 }
+ PathCubic { control1X: 30.953; control1Y: -15.803; control2X: 31.351; control2Y: -16.29; x: 31.959; y: -16.666 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 94.771; y: -26.977 }
+ PathCubic { control1X: 96.16; control1Y: -25.185; control2X: 96.45; control2Y: -22.39; x: 94.401; y: -21 }
+ PathCubic { control1X: 94.951; control1Y: -17.691; control2X: 98.302; control2Y: -19.67; x: 100.401; y: -20.2 }
+ PathCubic { control1X: 100.292; control1Y: -20.588; control2X: 100.519; control2Y: -20.932; x: 100.802; y: -20.937 }
+ PathCubic { control1X: 101.859; control1Y: -20.952; control2X: 102.539; control2Y: -21.984; x: 103.601; y: -21.8 }
+ PathCubic { control1X: 104.035; control1Y: -23.357; control2X: 105.673; control2Y: -24.059; x: 106.317; y: -25.439 }
+ PathCubic { control1X: 108.043; control1Y: -29.134; control2X: 107.452; control2Y: -33.407; x: 104.868; y: -36.653 }
+ PathCubic { control1X: 104.666; control1Y: -36.907; control2X: 104.883; control2Y: -37.424; x: 104.759; y: -37.786 }
+ PathCubic { control1X: 104.003; control1Y: -39.997; control2X: 101.935; control2Y: -40.312; x: 100.001; y: -41 }
+ PathCubic { control1X: 98.824; control1Y: -44.875; control2X: 98.163; control2Y: -48.906; x: 96.401; y: -52.6 }
+ PathCubic { control1X: 94.787; control1Y: -52.85; control2X: 94.089; control2Y: -54.589; x: 92.752; y: -55.309 }
+ PathCubic { control1X: 91.419; control1Y: -56.028; control2X: 90.851; control2Y: -54.449; x: 90.892; y: -53.403 }
+ PathCubic { control1X: 90.899; control1Y: -53.198; control2X: 91.351; control2Y: -52.974; x: 91.181; y: -52.609 }
+ PathCubic { control1X: 91.105; control1Y: -52.445; control2X: 90.845; control2Y: -52.334; x: 90.845; y: -52.2 }
+ PathCubic { control1X: 90.846; control1Y: -52.065; control2X: 91.067; control2Y: -51.934; x: 91.201; y: -51.8 }
+ PathCubic { control1X: 90.283; control1Y: -50.98; control2X: 88.86; control2Y: -50.503; x: 88.565; y: -49.358 }
+ PathCubic { control1X: 87.611; control1Y: -45.648; control2X: 90.184; control2Y: -42.523; x: 91.852; y: -39.322 }
+ PathCubic { control1X: 92.443; control1Y: -38.187; control2X: 91.707; control2Y: -36.916; x: 90.947; y: -35.708 }
+ PathCubic { control1X: 90.509; control1Y: -35.013; control2X: 90.617; control2Y: -33.886; x: 90.893; y: -33.03 }
+ PathCubic { control1X: 91.645; control1Y: -30.699; control2X: 93.236; control2Y: -28.96; x: 94.771; y: -26.977 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 57.611; y: -8.591 }
+ PathCubic { control1X: 56.124; control1Y: -6.74; control2X: 52.712; control2Y: -4.171; x: 55.629; y: -2.243 }
+ PathCubic { control1X: 55.823; control1Y: -2.114; control2X: 56.193; control2Y: -2.11; x: 56.366; y: -2.244 }
+ PathCubic { control1X: 58.387; control1Y: -3.809; control2X: 60.39; control2Y: -4.712; x: 62.826; y: -5.294 }
+ PathCubic { control1X: 62.95; control1Y: -5.323; control2X: 63.224; control2Y: -4.856; x: 63.593; y: -5.017 }
+ PathCubic { control1X: 65.206; control1Y: -5.72; control2X: 67.216; control2Y: -5.662; x: 68.4; y: -7 }
+ PathCubic { control1X: 72.167; control1Y: -6.776; control2X: 75.732; control2Y: -7.892; x: 79.123; y: -9.2 }
+ PathCubic { control1X: 80.284; control1Y: -9.648; control2X: 81.554; control2Y: -10.207; x: 82.755; y: -10.709 }
+ PathCubic { control1X: 84.131; control1Y: -11.285; control2X: 85.335; control2Y: -12.213; x: 86.447; y: -13.354 }
+ PathCubic { control1X: 86.58; control1Y: -13.49; control2X: 86.934; control2Y: -13.4; x: 87.201; y: -13.4 }
+ PathCubic { control1X: 87.161; control1Y: -14.263; control2X: 88.123; control2Y: -14.39; x: 88.37; y: -15.012 }
+ PathCubic { control1X: 88.462; control1Y: -15.244; control2X: 88.312; control2Y: -15.64; x: 88.445; y: -15.742 }
+ PathCubic { control1X: 90.583; control1Y: -17.372; control2X: 91.503; control2Y: -19.39; x: 90.334; y: -21.767 }
+ PathCubic { control1X: 90.049; control1Y: -22.345; control2X: 89.8; control2Y: -22.963; x: 89.234; y: -23.439 }
+ PathCubic { control1X: 88.149; control1Y: -24.35; control2X: 87.047; control2Y: -23.496; x: 86; y: -23.8 }
+ PathCubic { control1X: 85.841; control1Y: -23.172; control2X: 85.112; control2Y: -23.344; x: 84.726; y: -23.146 }
+ PathCubic { control1X: 83.867; control1Y: -22.707; control2X: 82.534; control2Y: -23.292; x: 81.675; y: -22.854 }
+ PathCubic { control1X: 80.313; control1Y: -22.159; control2X: 79.072; control2Y: -21.99; x: 77.65; y: -21.613 }
+ PathCubic { control1X: 77.338; control1Y: -21.531; control2X: 76.56; control2Y: -21.627; x: 76.4; y: -21 }
+ PathCubic { control1X: 76.266; control1Y: -21.134; control2X: 76.118; control2Y: -21.368; x: 76.012; y: -21.346 }
+ PathCubic { control1X: 74.104; control1Y: -20.95; control2X: 72.844; control2Y: -20.736; x: 71.543; y: -19.044 }
+ PathCubic { control1X: 71.44; control1Y: -18.911; control2X: 70.998; control2Y: -19.09; x: 70.839; y: -18.955 }
+ PathCubic { control1X: 69.882; control1Y: -18.147; control2X: 69.477; control2Y: -16.913; x: 68.376; y: -16.241 }
+ PathCubic { control1X: 68.175; control1Y: -16.118; control2X: 67.823; control2Y: -16.286; x: 67.629; y: -16.157 }
+ PathCubic { control1X: 66.983; control1Y: -15.726; control2X: 66.616; control2Y: -15.085; x: 65.974; y: -14.638 }
+ PathCubic { control1X: 65.645; control1Y: -14.409; control2X: 65.245; control2Y: -14.734; x: 65.277; y: -14.99 }
+ PathCubic { control1X: 65.522; control1Y: -16.937; control2X: 66.175; control2Y: -18.724; x: 65.6; y: -20.6 }
+ PathCubic { control1X: 67.677; control1Y: -23.12; control2X: 70.194; control2Y: -25.069; x: 72; y: -27.8 }
+ PathCubic { control1X: 72.015; control1Y: -29.966; control2X: 72.707; control2Y: -32.112; x: 72.594; y: -34.189 }
+ PathCubic { control1X: 72.584; control1Y: -34.382; control2X: 72.296; control2Y: -35.115; x: 72.17; y: -35.462 }
+ PathCubic { control1X: 71.858; control1Y: -36.316; control2X: 72.764; control2Y: -37.382; x: 71.92; y: -38.106 }
+ PathCubic { control1X: 70.516; control1Y: -39.309; control2X: 69.224; control2Y: -38.433; x: 68.4; y: -37 }
+ PathCubic { control1X: 66.562; control1Y: -36.61; control2X: 64.496; control2Y: -35.917; x: 62.918; y: -37.151 }
+ PathCubic { control1X: 61.911; control1Y: -37.938; control2X: 61.333; control2Y: -38.844; x: 60.534; y: -39.9 }
+ PathCubic { control1X: 59.549; control1Y: -41.202; control2X: 59.884; control2Y: -42.638; x: 59.954; y: -44.202 }
+ PathCubic { control1X: 59.96; control1Y: -44.33; control2X: 59.645; control2Y: -44.466; x: 59.645; y: -44.6 }
+ PathCubic { control1X: 59.646; control1Y: -44.735; control2X: 59.866; control2Y: -44.866; x: 60; y: -45 }
+ PathCubic { control1X: 59.294; control1Y: -45.626; control2X: 59.019; control2Y: -46.684; x: 58; y: -47 }
+ PathCubic { control1X: 58.305; control1Y: -48.092; control2X: 57.629; control2Y: -48.976; x: 56.758; y: -49.278 }
+ PathCubic { control1X: 54.763; control1Y: -49.969; control2X: 53.086; control2Y: -48.057; x: 51.194; y: -47.984 }
+ PathCubic { control1X: 50.68; control1Y: -47.965; control2X: 50.213; control2Y: -49.003; x: 49.564; y: -49.328 }
+ PathCubic { control1X: 49.132; control1Y: -49.544; control2X: 48.428; control2Y: -49.577; x: 48.066; y: -49.311 }
+ PathCubic { control1X: 47.378; control1Y: -48.807; control2X: 46.789; control2Y: -48.693; x: 46.031; y: -48.488 }
+ PathCubic { control1X: 44.414; control1Y: -48.052; control2X: 43.136; control2Y: -46.958; x: 41.656; y: -46.103 }
+ PathCubic { control1X: 40.171; control1Y: -45.246; control2X: 39.216; control2Y: -43.809; x: 38.136; y: -42.489 }
+ PathCubic { control1X: 37.195; control1Y: -41.337; control2X: 37.059; control2Y: -38.923; x: 38.479; y: -38.423 }
+ PathCubic { control1X: 40.322; control1Y: -37.773; control2X: 41.626; control2Y: -40.476; x: 43.592; y: -40.15 }
+ PathCubic { control1X: 43.904; control1Y: -40.099; control2X: 44.11; control2Y: -39.788; x: 44; y: -39.4 }
+ PathCubic { control1X: 44.389; control1Y: -39.291; control2X: 44.607; control2Y: -39.52; x: 44.8; y: -39.8 }
+ PathCubic { control1X: 45.658; control1Y: -38.781; control2X: 46.822; control2Y: -38.444; x: 47.76; y: -37.571 }
+ PathCubic { control1X: 48.73; control1Y: -36.667; control2X: 50.476; control2Y: -37.085; x: 51.491; y: -36.088 }
+ PathCubic { control1X: 53.02; control1Y: -34.586; control2X: 52.461; control2Y: -31.905; x: 54.4; y: -30.6 }
+ PathCubic { control1X: 53.814; control1Y: -29.287; control2X: 53.207; control2Y: -28.01; x: 52.872; y: -26.583 }
+ PathCubic { control1X: 52.59; control1Y: -25.377; control2X: 53.584; control2Y: -24.18; x: 54.795; y: -24.271 }
+ PathCubic { control1X: 56.053; control1Y: -24.365; control2X: 56.315; control2Y: -25.124; x: 56.8; y: -26.2 }
+ PathCubic { control1X: 57.067; control1Y: -25.933; control2X: 57.536; control2Y: -25.636; x: 57.495; y: -25.42 }
+ PathCubic { control1X: 57.038; control1Y: -23.033; control2X: 56.011; control2Y: -21.04; x: 55.553; y: -18.609 }
+ PathCubic { control1X: 55.494; control1Y: -18.292; control2X: 55.189; control2Y: -18.09; x: 54.8; y: -18.2 }
+ PathCubic { control1X: 54.332; control1Y: -14.051; control2X: 50.28; control2Y: -11.657; x: 47.735; y: -8.492 }
+ PathCubic { control1X: 47.332; control1Y: -7.99; control2X: 47.328; control2Y: -6.741; x: 47.737; y: -6.338 }
+ PathCubic { control1X: 49.14; control1Y: -4.951; control2X: 51.1; control2Y: -6.497; x: 52.8; y: -7 }
+ PathCubic { control1X: 53.013; control1Y: -8.206; control2X: 53.872; control2Y: -9.148; x: 55.204; y: -9.092 }
+ PathCubic { control1X: 55.46; control1Y: -9.082; control2X: 55.695; control2Y: -9.624; x: 56.019; y: -9.754 }
+ PathCubic { control1X: 56.367; control1Y: -9.892; control2X: 56.869; control2Y: -9.668; x: 57.155; y: -9.866 }
+ PathCubic { control1X: 58.884; control1Y: -11.061; control2X: 60.292; control2Y: -12.167; x: 62.03; y: -13.356 }
+ PathCubic { control1X: 62.222; control1Y: -13.487; control2X: 62.566; control2Y: -13.328; x: 62.782; y: -13.436 }
+ PathCubic { control1X: 63.107; control1Y: -13.598; control2X: 63.294; control2Y: -13.985; x: 63.617; y: -14.17 }
+ PathCubic { control1X: 63.965; control1Y: -14.37; control2X: 64.207; control2Y: -14.08; x: 64.4; y: -13.8 }
+ PathCubic { control1X: 63.754; control1Y: -13.451; control2X: 63.75; control2Y: -12.494; x: 63.168; y: -12.292 }
+ PathCubic { control1X: 62.393; control1Y: -12.024; control2X: 61.832; control2Y: -11.511; x: 61.158; y: -11.064 }
+ PathCubic { control1X: 60.866; control1Y: -10.871; control2X: 60.207; control2Y: -11.119; x: 60.103; y: -10.94 }
+ PathCubic { control1X: 59.505; control1Y: -9.912; control2X: 58.321; control2Y: -9.474; x: 57.611; y: -8.591 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 2.2; y: -58 }
+ PathCubic { control1X: 2.2; control1Y: -58; control2X: -7.038; control2Y: -60.872; x: -18.2; y: -35.2 }
+ PathCubic { control1X: -18.2; control1Y: -35.2; control2X: -20.6; control2Y: -30; x: -23; y: -28 }
+ PathCubic { control1X: -25.4; control1Y: -26; control2X: -36.6; control2Y: -22.4; x: -38.6; y: -18.4 }
+ PathLine { x: -49; y: -2.4 }
+ PathCubic { control1X: -49; control1Y: -2.4; control2X: -34.2; control2Y: -18.4; x: -31; y: -20.8 }
+ PathCubic { control1X: -31; control1Y: -20.8; control2X: -23; control2Y: -29.2; x: -26.2; y: -22.4 }
+ PathCubic { control1X: -26.2; control1Y: -22.4; control2X: -40.2; control2Y: -11.6; x: -39; y: -2.4 }
+ PathCubic { control1X: -39; control1Y: -2.4; control2X: -44.6; control2Y: 12; x: -45.4; y: 14 }
+ PathCubic { control1X: -45.4; control1Y: 14; control2X: -29.4; control2Y: -18; x: -27; y: -19.2 }
+ PathCubic { control1X: -24.6; control1Y: -20.4; control2X: -23.4; control2Y: -20.4; x: -24.6; y: -16.8 }
+ PathCubic { control1X: -25.8; control1Y: -13.2; control2X: -26.2; control2Y: 3.2; x: -29; y: 5.2 }
+ PathCubic { control1X: -29; control1Y: 5.2; control2X: -21; control2Y: -15.2; x: -21.8; y: -18.4 }
+ PathCubic { control1X: -21.8; control1Y: -18.4; control2X: -18.6; control2Y: -22; x: -16.2; y: -16.8 }
+ PathLine { x: -17.4; y: -0.8 }
+ PathLine { x: -13; y: 11.2 }
+ PathCubic { control1X: -13; control1Y: 11.2; control2X: -15.4; control2Y: 0; x: -13.8; y: -15.6 }
+ PathCubic { control1X: -13.8; control1Y: -15.6; control2X: -15.8; control2Y: -26; x: -11.8; y: -20.4 }
+ PathCubic { control1X: -7.8; control1Y: -14.8; control2X: 1.8; control2Y: -8.8; x: 1.8; y: -4 }
+ PathCubic { control1X: 1.8; control1Y: -4; control2X: -3.4; control2Y: -21.6; x: -12.6; y: -26.4 }
+ PathLine { x: -16.6; y: -20.4 }
+ PathLine { x: -17.8; y: -22.4 }
+ PathCubic { control1X: -17.8; control1Y: -22.4; control2X: -21.4; control2Y: -23.2; x: -17; y: -30 }
+ PathCubic { control1X: -12.6; control1Y: -36.8; control2X: -13; control2Y: -37.6; x: -13; y: -37.6 }
+ PathCubic { control1X: -13; control1Y: -37.6; control2X: -6.6; control2Y: -30.4; x: -5; y: -30.4 }
+ PathCubic { control1X: -5; control1Y: -30.4; control2X: 8.2; control2Y: -38; x: 9.4; y: -13.6 }
+ PathCubic { control1X: 9.4; control1Y: -13.6; control2X: 16.2; control2Y: -28; x: 7; y: -34.8 }
+ PathCubic { control1X: 7; control1Y: -34.8; control2X: -7.8; control2Y: -36.8; x: -6.6; y: -42 }
+ PathLine { x: 0.6; y: -54.4 }
+ PathCubic { control1X: 4.2; control1Y: -59.6; control2X: 2.6; control2Y: -56.8; x: 2.6; y: -56.8 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -17.8; y: -41.6 }
+ PathCubic { control1X: -17.8; control1Y: -41.6; control2X: -30.6; control2Y: -41.6; x: -33.8; y: -36.4 }
+ PathLine { x: -41; y: -26.8 }
+ PathCubic { control1X: -41; control1Y: -26.8; control2X: -23.8; control2Y: -36.8; x: -19.8; y: -38 }
+ PathCubic { control1X: -15.8; control1Y: -39.2; control2X: -17.8; control2Y: -41.6; x: -17.8; y: -41.6 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -57.8; y: -35.2 }
+ PathCubic { control1X: -57.8; control1Y: -35.2; control2X: -59.8; control2Y: -34; x: -60.2; y: -31.2 }
+ PathCubic { control1X: -60.6; control1Y: -28.4; control2X: -63; control2Y: -28; x: -62.2; y: -25.2 }
+ PathCubic { control1X: -61.4; control1Y: -22.4; control2X: -59.4; control2Y: -20; x: -59.4; y: -24 }
+ PathCubic { control1X: -59.4; control1Y: -28; control2X: -57.8; control2Y: -30; x: -57; y: -31.2 }
+ PathCubic { control1X: -56.2; control1Y: -32.4; control2X: -54.6; control2Y: -36.8; x: -57.8; y: -35.2 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -66.6; y: 26 }
+ PathCubic { control1X: -66.6; control1Y: 26; control2X: -75; control2Y: 22; x: -78.2; y: 18.4 }
+ PathCubic { control1X: -81.4; control1Y: 14.8; control2X: -80.948; control2Y: 19.966; x: -85.8; y: 19.6 }
+ PathCubic { control1X: -91.647; control1Y: 19.159; control2X: -90.6; control2Y: 3.2; x: -90.6; y: 3.2 }
+ PathLine { x: -94.6; y: 10.8 }
+ PathCubic { control1X: -94.6; control1Y: 10.8; control2X: -95.8; control2Y: 25.2; x: -87.8; y: 22.8 }
+ PathCubic { control1X: -83.893; control1Y: 21.628; control2X: -82.6; control2Y: 23.2; x: -84.2; y: 24 }
+ PathCubic { control1X: -85.8; control1Y: 24.8; control2X: -78.6; control2Y: 25.2; x: -81.4; y: 26.8 }
+ PathCubic { control1X: -84.2; control1Y: 28.4; control2X: -69.8; control2Y: 23.2; x: -72.2; y: 33.6 }
+ PathLine { x: -66.6; y: 26 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -79.2; y: 40.4 }
+ PathCubic { control1X: -79.2; control1Y: 40.4; control2X: -94.6; control2Y: 44.8; x: -98.2; y: 35.2 }
+ PathCubic { control1X: -98.2; control1Y: 35.2; control2X: -103; control2Y: 37.6; x: -100.8; y: 40.6 }
+ PathCubic { control1X: -98.6; control1Y: 43.6; control2X: -97.4; control2Y: 44; x: -97.4; y: 44 }
+ PathCubic { control1X: -97.4; control1Y: 44; control2X: -92; control2Y: 45.2; x: -92.6; y: 46 }
+ PathCubic { control1X: -93.2; control1Y: 46.8; control2X: -95.6; control2Y: 50.2; x: -95.6; y: 50.2 }
+ PathCubic { control1X: -95.6; control1Y: 50.2; control2X: -85.4; control2Y: 44.2; x: -79.2; y: 40.4 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 149.201; y: 118.601 }
+ PathCubic { control1X: 148.774; control1Y: 120.735; control2X: 147.103; control2Y: 121.536; x: 145.201; y: 122.201 }
+ PathCubic { control1X: 143.284; control1Y: 121.243; control2X: 140.686; control2Y: 118.137; x: 138.801; y: 120.201 }
+ PathCubic { control1X: 138.327; control1Y: 119.721; control2X: 137.548; control2Y: 119.661; x: 137.204; y: 118.999 }
+ PathCubic { control1X: 136.739; control1Y: 118.101; control2X: 137.011; control2Y: 117.055; x: 136.669; y: 116.257 }
+ PathCubic { control1X: 136.124; control1Y: 114.985; control2X: 135.415; control2Y: 113.619; x: 135.601; y: 112.201 }
+ PathCubic { control1X: 137.407; control1Y: 111.489; control2X: 138.002; control2Y: 109.583; x: 137.528; y: 107.82 }
+ PathCubic { control1X: 137.459; control1Y: 107.563; control2X: 137.03; control2Y: 107.366; x: 137.23; y: 107.017 }
+ PathCubic { control1X: 137.416; control1Y: 106.694; control2X: 137.734; control2Y: 106.467; x: 138.001; y: 106.2 }
+ PathCubic { control1X: 137.866; control1Y: 106.335; control2X: 137.721; control2Y: 106.568; x: 137.61; y: 106.548 }
+ PathCubic { control1X: 137; control1Y: 106.442; control2X: 137.124; control2Y: 105.805; x: 137.254; y: 105.418 }
+ PathCubic { control1X: 137.839; control1Y: 103.672; control2X: 139.853; control2Y: 103.408; x: 141.201; y: 104.6 }
+ PathCubic { control1X: 141.457; control1Y: 104.035; control2X: 141.966; control2Y: 104.229; x: 142.401; y: 104.2 }
+ PathCubic { control1X: 142.351; control1Y: 103.621; control2X: 142.759; control2Y: 103.094; x: 142.957; y: 102.674 }
+ PathCubic { control1X: 143.475; control1Y: 101.576; control2X: 145.104; control2Y: 102.682; x: 145.901; y: 102.07 }
+ PathCubic { control1X: 146.977; control1Y: 101.245; control2X: 148.04; control2Y: 100.546; x: 149.118; y: 101.149 }
+ PathCubic { control1X: 150.927; control1Y: 102.162; control2X: 152.636; control2Y: 103.374; x: 153.835; y: 105.115 }
+ PathCubic { control1X: 154.41; control1Y: 105.949; control2X: 154.65; control2Y: 107.23; x: 154.592; y: 108.188 }
+ PathCubic { control1X: 154.554; control1Y: 108.835; control2X: 153.173; control2Y: 108.483; x: 152.83; y: 109.412 }
+ PathCubic { control1X: 152.185; control1Y: 111.16; control2X: 154.016; control2Y: 111.679; x: 154.772; y: 113.017 }
+ PathCubic { control1X: 154.97; control1Y: 113.366; control2X: 154.706; control2Y: 113.67; x: 154.391; y: 113.768 }
+ PathCubic { control1X: 153.98; control1Y: 113.896; control2X: 153.196; control2Y: 113.707; x: 153.334; y: 114.16 }
+ PathCubic { control1X: 154.306; control1Y: 117.353; control2X: 151.55; control2Y: 118.031; x: 149.201; y: 118.601 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ PathMove { x: 139.6; y: 138.201 }
+ PathCubic { control1X: 139.593; control1Y: 136.463; control2X: 137.992; control2Y: 134.707; x: 139.201; y: 133.001 }
+ PathCubic { control1X: 139.336; control1Y: 133.135; control2X: 139.467; control2Y: 133.356; x: 139.601; y: 133.356 }
+ PathCubic { control1X: 139.736; control1Y: 133.356; control2X: 139.867; control2Y: 133.135; x: 140.001; y: 133.001 }
+ PathCubic { control1X: 141.496; control1Y: 135.217; control2X: 145.148; control2Y: 136.145; x: 145.006; y: 138.991 }
+ PathCubic { control1X: 144.984; control1Y: 139.438; control2X: 143.897; control2Y: 140.356; x: 144.801; y: 141.001 }
+ PathCubic { control1X: 142.988; control1Y: 142.349; control2X: 142.933; control2Y: 144.719; x: 142.001; y: 146.601 }
+ PathCubic { control1X: 140.763; control1Y: 146.315; control2X: 139.551; control2Y: 145.952; x: 138.401; y: 145.401 }
+ PathCubic { control1X: 138.753; control1Y: 143.915; control2X: 138.636; control2Y: 142.231; x: 139.456; y: 140.911 }
+ PathCubic { control1X: 139.89; control1Y: 140.213; control2X: 139.603; control2Y: 139.134; x: 139.6; y: 138.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -26.6; y: 129.201 }
+ PathCubic { control1X: -26.6; control1Y: 129.201; control2X: -43.458; control2Y: 139.337; x: -29.4; y: 124.001 }
+ PathCubic { control1X: -20.6; control1Y: 114.401; control2X: -10.6; control2Y: 108.801; x: -10.6; y: 108.801 }
+ PathCubic { control1X: -10.6; control1Y: 108.801; control2X: -0.2; control2Y: 104.4; x: 3.4; y: 103.2 }
+ PathCubic { control1X: 7; control1Y: 102; control2X: 22.2; control2Y: 96.8; x: 25.4; y: 96.4 }
+ PathCubic { control1X: 28.6; control1Y: 96; control2X: 38.2; control2Y: 92; x: 45; y: 96 }
+ PathCubic { control1X: 51.8; control1Y: 100; control2X: 59.8; control2Y: 104.4; x: 59.8; y: 104.4 }
+ PathCubic { control1X: 59.8; control1Y: 104.4; control2X: 43.4; control2Y: 96; x: 39.8; y: 98.4 }
+ PathCubic { control1X: 36.2; control1Y: 100.8; control2X: 29; control2Y: 100.4; x: 23; y: 103.6 }
+ PathCubic { control1X: 23; control1Y: 103.6; control2X: 8.2; control2Y: 108.001; x: 5; y: 110.001 }
+ PathCubic { control1X: 1.8; control1Y: 112.001; control2X: -8.6; control2Y: 123.601; x: -10.2; y: 122.801 }
+ PathCubic { control1X: -11.8; control1Y: 122.001; control2X: -9.8; control2Y: 121.601; x: -8.6; y: 118.801 }
+ PathCubic { control1X: -7.4; control1Y: 116.001; control2X: -9.4; control2Y: 114.401; x: -17.4; y: 120.801 }
+ PathCubic { control1X: -25.4; control1Y: 127.201; control2X: -26.6; control2Y: 129.201; x: -26.6; y: 129.201 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -19.195; y: 123.234 }
+ PathCubic { control1X: -19.195; control1Y: 123.234; control2X: -17.785; control2Y: 110.194; x: -9.307; y: 111.859 }
+ PathCubic { control1X: -9.307; control1Y: 111.859; control2X: -1.081; control2Y: 107.689; x: 1.641; y: 105.721 }
+ PathCubic { control1X: 1.641; control1Y: 105.721; control2X: 9.78; control2Y: 104.019; x: 11.09; y: 103.402 }
+ PathCubic { control1X: 29.569; control1Y: 94.702; control2X: 44.288; control2Y: 99.221; x: 44.835; y: 98.101 }
+ PathCubic { control1X: 45.381; control1Y: 96.982; control2X: 65.006; control2Y: 104.099; x: 68.615; y: 108.185 }
+ PathCubic { control1X: 69.006; control1Y: 108.628; control2X: 58.384; control2Y: 102.588; x: 48.686; y: 100.697 }
+ PathCubic { control1X: 40.413; control1Y: 99.083; control2X: 18.811; control2Y: 100.944; x: 7.905; y: 106.48 }
+ PathCubic { control1X: 4.932; control1Y: 107.989; control2X: -4.013; control2Y: 113.773; x: -6.544; y: 113.662 }
+ PathCubic { control1X: -9.075; control1Y: 113.55; control2X: -19.195; control2Y: 123.234; x: -19.195; y: 123.234 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -23; y: 148.801 }
+ PathCubic { control1X: -23; control1Y: 148.801; control2X: -38.2; control2Y: 146.401; x: -21.4; y: 144.801 }
+ PathCubic { control1X: -21.4; control1Y: 144.801; control2X: -3.4; control2Y: 142.801; x: 0.6; y: 137.601 }
+ PathCubic { control1X: 0.6; control1Y: 137.601; control2X: 14.2; control2Y: 128.401; x: 17; y: 128.001 }
+ PathCubic { control1X: 19.8; control1Y: 127.601; control2X: 49.8; control2Y: 120.401; x: 50.2; y: 118.001 }
+ PathCubic { control1X: 50.6; control1Y: 115.601; control2X: 56.2; control2Y: 115.601; x: 57.8; y: 116.401 }
+ PathCubic { control1X: 59.4; control1Y: 117.201; control2X: 58.6; control2Y: 118.401; x: 55.8; y: 119.201 }
+ PathCubic { control1X: 53; control1Y: 120.001; control2X: 21.8; control2Y: 136.401; x: 15.4; y: 137.601 }
+ PathCubic { control1X: 9; control1Y: 138.801; control2X: -2.6; control2Y: 146.401; x: -7.4; y: 147.601 }
+ PathCubic { control1X: -12.2; control1Y: 148.801; control2X: -23; control2Y: 148.801; x: -23; y: 148.801 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -3.48; y: 141.403 }
+ PathCubic { control1X: -3.48; control1Y: 141.403; control2X: -12.062; control2Y: 140.574; x: -3.461; y: 139.755 }
+ PathCubic { control1X: -3.461; control1Y: 139.755; control2X: 5.355; control2Y: 136.331; x: 7.403; y: 133.668 }
+ PathCubic { control1X: 7.403; control1Y: 133.668; control2X: 14.367; control2Y: 128.957; x: 15.8; y: 128.753 }
+ PathCubic { control1X: 17.234; control1Y: 128.548; control2X: 31.194; control2Y: 124.861; x: 31.399; y: 123.633 }
+ PathCubic { control1X: 31.604; control1Y: 122.404; control2X: 65.67; control2Y: 109.823; x: 70.09; y: 113.013 }
+ PathCubic { control1X: 73.001; control1Y: 115.114; control2X: 63.1; control2Y: 113.437; x: 53.466; y: 117.847 }
+ PathCubic { control1X: 52.111; control1Y: 118.467; control2X: 18.258; control2Y: 133.054; x: 14.981; y: 133.668 }
+ PathCubic { control1X: 11.704; control1Y: 134.283; control2X: 5.765; control2Y: 138.174; x: 3.307; y: 138.788 }
+ PathCubic { control1X: 0.85; control1Y: 139.403; control2X: -3.48; control2Y: 141.403; x: -3.48; y: 141.403 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -11.4; y: 143.601 }
+ PathCubic { control1X: -11.4; control1Y: 143.601; control2X: -6.2; control2Y: 143.201; x: -7.4; y: 144.801 }
+ PathCubic { control1X: -8.6; control1Y: 146.401; control2X: -11; control2Y: 145.601; x: -11; y: 145.601 }
+ PathLine { x: -11.4; y: 143.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -18.6; y: 145.201 }
+ PathCubic { control1X: -18.6; control1Y: 145.201; control2X: -13.4; control2Y: 144.801; x: -14.6; y: 146.401 }
+ PathCubic { control1X: -15.8; control1Y: 148.001; control2X: -18.2; control2Y: 147.201; x: -18.2; y: 147.201 }
+ PathLine { x: -18.6; y: 145.201 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -29; y: 146.801 }
+ PathCubic { control1X: -29; control1Y: 146.801; control2X: -23.8; control2Y: 146.401; x: -25; y: 148.001 }
+ PathCubic { control1X: -26.2; control1Y: 149.601; control2X: -28.6; control2Y: 148.801; x: -28.6; y: 148.801 }
+ PathLine { x: -29; y: 146.801 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -36.6; y: 147.601 }
+ PathCubic { control1X: -36.6; control1Y: 147.601; control2X: -31.4; control2Y: 147.201; x: -32.6; y: 148.801 }
+ PathCubic { control1X: -33.8; control1Y: 150.401; control2X: -36.2; control2Y: 149.601; x: -36.2; y: 149.601 }
+ PathLine { x: -36.6; y: 147.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 1.8; y: 108.001 }
+ PathCubic { control1X: 1.8; control1Y: 108.001; control2X: 6.2; control2Y: 108.001; x: 5; y: 109.601 }
+ PathCubic { control1X: 3.8; control1Y: 111.201; control2X: 0.6; control2Y: 110.801; x: 0.6; y: 110.801 }
+ PathLine { x: 1.8; y: 108.001 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -8.2; y: 113.601 }
+ PathCubic { control1X: -8.2; control1Y: 113.601; control2X: -1.694; control2Y: 111.46; x: -4.2; y: 114.801 }
+ PathCubic { control1X: -5.4; control1Y: 116.401; control2X: -7.8; control2Y: 115.601; x: -7.8; y: 115.601 }
+ PathLine { x: -8.2; y: 113.601 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -19.4; y: 118.401 }
+ PathCubic { control1X: -19.4; control1Y: 118.401; control2X: -14.2; control2Y: 118.001; x: -15.4; y: 119.601 }
+ PathCubic { control1X: -16.6; control1Y: 121.201; control2X: -19; control2Y: 120.401; x: -19; y: 120.401 }
+ PathLine { x: -19.4; y: 118.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -27; y: 124.401 }
+ PathCubic { control1X: -27; control1Y: 124.401; control2X: -21.8; control2Y: 124.001; x: -23; y: 125.601 }
+ PathCubic { control1X: -24.2; control1Y: 127.201; control2X: -26.6; control2Y: 126.401; x: -26.6; y: 126.401 }
+ PathLine { x: -27; y: 124.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -33.8; y: 129.201 }
+ PathCubic { control1X: -33.8; control1Y: 129.201; control2X: -28.6; control2Y: 128.801; x: -29.8; y: 130.401 }
+ PathCubic { control1X: -31; control1Y: 132.001; control2X: -33.4; control2Y: 131.201; x: -33.4; y: 131.201 }
+ PathLine { x: -33.8; y: 129.201 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 5.282; y: 135.598 }
+ PathCubic { control1X: 5.282; control1Y: 135.598; control2X: 12.203; control2Y: 135.066; x: 10.606; y: 137.195 }
+ PathCubic { control1X: 9.009; control1Y: 139.325; control2X: 5.814; control2Y: 138.26; x: 5.814; y: 138.26 }
+ PathLine { x: 5.282; y: 135.598 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 15.682; y: 130.798 }
+ PathCubic { control1X: 15.682; control1Y: 130.798; control2X: 22.603; control2Y: 130.266; x: 21.006; y: 132.395 }
+ PathCubic { control1X: 19.409; control1Y: 134.525; control2X: 16.214; control2Y: 133.46; x: 16.214; y: 133.46 }
+ PathLine { x: 15.682; y: 130.798 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 26.482; y: 126.398 }
+ PathCubic { control1X: 26.482; control1Y: 126.398; control2X: 33.403; control2Y: 125.866; x: 31.806; y: 127.995 }
+ PathCubic { control1X: 30.209; control1Y: 130.125; control2X: 27.014; control2Y: 129.06; x: 27.014; y: 129.06 }
+ PathLine { x: 26.482; y: 126.398 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 36.882; y: 121.598 }
+ PathCubic { control1X: 36.882; control1Y: 121.598; control2X: 43.803; control2Y: 121.066; x: 42.206; y: 123.195 }
+ PathCubic { control1X: 40.609; control1Y: 125.325; control2X: 37.414; control2Y: 124.26; x: 37.414; y: 124.26 }
+ PathLine { x: 36.882; y: 121.598 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 9.282; y: 103.598 }
+ PathCubic { control1X: 9.282; control1Y: 103.598; control2X: 16.203; control2Y: 103.066; x: 14.606; y: 105.195 }
+ PathCubic { control1X: 13.009; control1Y: 107.325; control2X: 9.014; control2Y: 107.06; x: 9.014; y: 107.06 }
+ PathLine { x: 9.282; y: 103.598 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 19.282; y: 100.398 }
+ PathCubic { control1X: 19.282; control1Y: 100.398; control2X: 26.203; control2Y: 99.866; x: 24.606; y: 101.995 }
+ PathCubic { control1X: 23.009; control1Y: 104.125; control2X: 18.614; control2Y: 103.86; x: 18.614; y: 103.86 }
+ PathLine { x: 19.282; y: 100.398 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -3.4; y: 140.401 }
+ PathCubic { control1X: -3.4; control1Y: 140.401; control2X: 1.8; control2Y: 140.001; x: 0.6; y: 141.601 }
+ PathCubic { control1X: -0.6; control1Y: 143.201; control2X: -3; control2Y: 142.401; x: -3; y: 142.401 }
+ PathLine { x: -3.4; y: 140.401 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: -76.6; y: 41.2 }
+ PathCubic { control1X: -76.6; control1Y: 41.2; control2X: -81; control2Y: 50; x: -81.4; y: 53.2 }
+ PathCubic { control1X: -81.4; control1Y: 53.2; control2X: -80.6; control2Y: 44.4; x: -79.4; y: 42.4 }
+ PathCubic { control1X: -78.2; control1Y: 40.4; control2X: -76.6; control2Y: 41.2; x: -76.6; y: 41.2 }
+ }
+
+ ShapePath {
+ fillColor: "#992600"
+ strokeWidth: -1
+ PathMove { x: -95; y: 55.2 }
+ PathCubic { control1X: -95; control1Y: 55.2; control2X: -98.2; control2Y: 69.6; x: -97.8; y: 72.4 }
+ PathCubic { control1X: -97.8; control1Y: 72.4; control2X: -99; control2Y: 60.8; x: -98.6; y: 59.6 }
+ PathCubic { control1X: -98.2; control1Y: 58.4; control2X: -95; control2Y: 55.2; x: -95; y: 55.2 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -74.2; y: -19.4 }
+ PathLine { x: -74.4; y: -16.2 }
+ PathLine { x: -76.6; y: -16 }
+ PathCubic { control1X: -76.6; control1Y: -16; control2X: -62.4; control2Y: -3.4; x: -61.8; y: 4.2 }
+ PathCubic { control1X: -61.8; control1Y: 4.2; control2X: -61; control2Y: -4; x: -74.2; y: -19.4 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -70.216; y: -18.135 }
+ PathCubic { control1X: -70.647; control1Y: -18.551; control2X: -70.428; control2Y: -19.296; x: -70.836; y: -19.556 }
+ PathCubic { control1X: -71.645; control1Y: -20.072; control2X: -69.538; control2Y: -20.129; x: -69.766; y: -20.845 }
+ PathCubic { control1X: -70.149; control1Y: -22.051; control2X: -69.962; control2Y: -22.072; x: -70.084; y: -23.348 }
+ PathCubic { control1X: -70.141; control1Y: -23.946; control2X: -69.553; control2Y: -25.486; x: -69.168; y: -25.926 }
+ PathCubic { control1X: -67.722; control1Y: -27.578; control2X: -69.046; control2Y: -30.51; x: -67.406; y: -32.061 }
+ PathCubic { control1X: -67.102; control1Y: -32.35; control2X: -66.726; control2Y: -32.902; x: -66.441; y: -33.32 }
+ PathCubic { control1X: -65.782; control1Y: -34.283; control2X: -64.598; control2Y: -34.771; x: -63.648; y: -35.599 }
+ PathCubic { control1X: -63.33; control1Y: -35.875; control2X: -63.531; control2Y: -36.702; x: -62.962; y: -36.61 }
+ PathCubic { control1X: -62.248; control1Y: -36.495; control2X: -61.007; control2Y: -36.625; x: -61.052; y: -35.784 }
+ PathCubic { control1X: -61.165; control1Y: -33.664; control2X: -62.494; control2Y: -31.944; x: -63.774; y: -30.276 }
+ PathCubic { control1X: -63.323; control1Y: -29.572; control2X: -63.781; control2Y: -28.937; x: -64.065; y: -28.38 }
+ PathCubic { control1X: -65.4; control1Y: -25.76; control2X: -65.211; control2Y: -22.919; x: -65.385; y: -20.079 }
+ PathCubic { control1X: -65.39; control1Y: -19.994; control2X: -65.697; control2Y: -19.916; x: -65.689; y: -19.863 }
+ PathCubic { control1X: -65.336; control1Y: -17.528; control2X: -64.752; control2Y: -15.329; x: -63.873; y: -13.1 }
+ PathCubic { control1X: -63.507; control1Y: -12.17; control2X: -63.036; control2Y: -11.275; x: -62.886; y: -10.348 }
+ PathCubic { control1X: -62.775; control1Y: -9.662; control2X: -62.672; control2Y: -8.829; x: -63.08; y: -8.124 }
+ PathCubic { control1X: -61.045; control1Y: -5.234; control2X: -62.354; control2Y: -2.583; x: -61.185; y: 0.948 }
+ PathCubic { control1X: -60.978; control1Y: 1.573; control2X: -59.286; control2Y: 3.487; x: -59.749; y: 3.326 }
+ PathCubic { control1X: -62.262; control1Y: 2.455; control2X: -62.374; control2Y: 2.057; x: -62.551; y: 1.304 }
+ PathCubic { control1X: -62.697; control1Y: 0.681; control2X: -63.027; control2Y: -0.696; x: -63.264; y: -1.298 }
+ PathCubic { control1X: -63.328; control1Y: -1.462; control2X: -63.499; control2Y: -3.346; x: -63.577; y: -3.468 }
+ PathCubic { control1X: -65.09; control1Y: -5.85; control2X: -63.732; control2Y: -5.674; x: -65.102; y: -8.032 }
+ PathCubic { control1X: -66.53; control1Y: -8.712; control2X: -67.496; control2Y: -9.816; x: -68.619; y: -10.978 }
+ PathCubic { control1X: -68.817; control1Y: -11.182; control2X: -67.674; control2Y: -11.906; x: -67.855; y: -12.119 }
+ PathCubic { control1X: -68.947; control1Y: -13.408; control2X: -70.1; control2Y: -14.175; x: -69.764; y: -15.668 }
+ PathCubic { control1X: -69.609; control1Y: -16.358; control2X: -69.472; control2Y: -17.415; x: -70.216; y: -18.135 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -73.8; y: -16.4 }
+ PathCubic { control1X: -73.8; control1Y: -16.4; control2X: -73.4; control2Y: -9.6; x: -71; y: -8 }
+ PathCubic { control1X: -68.6; control1Y: -6.4; control2X: -69.8; control2Y: -7.2; x: -73; y: -8.4 }
+ PathCubic { control1X: -76.2; control1Y: -9.6; control2X: -75; control2Y: -10.4; x: -75; y: -10.4 }
+ PathCubic { control1X: -75; control1Y: -10.4; control2X: -77.8; control2Y: -10; x: -75.4; y: -8 }
+ PathCubic { control1X: -73; control1Y: -6; control2X: -69.4; control2Y: -3.6; x: -71; y: -3.6 }
+ PathCubic { control1X: -72.6; control1Y: -3.6; control2X: -80.2; control2Y: -7.6; x: -80.2; y: -10.4 }
+ PathCubic { control1X: -80.2; control1Y: -13.2; control2X: -81.2; control2Y: -17.3; x: -81.2; y: -17.3 }
+ PathCubic { control1X: -81.2; control1Y: -17.3; control2X: -80.1; control2Y: -18.1; x: -75.3; y: -18 }
+ PathCubic { control1X: -75.3; control1Y: -18; control2X: -73.9; control2Y: -17.3; x: -73.8; y: -16.4 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -74.6; y: 2.2 }
+ PathCubic { control1X: -74.6; control1Y: 2.2; control2X: -83.12; control2Y: -0.591; x: -101.6; y: 2.8 }
+ PathCubic { control1X: -101.6; control1Y: 2.8; control2X: -92.569; control2Y: 0.722; x: -73.8; y: 3 }
+ PathCubic { control1X: -63.5; control1Y: 4.25; control2X: -74.6; control2Y: 2.2; x: -74.6; y: 2.2 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -72.502; y: 2.129 }
+ PathCubic { control1X: -72.502; control1Y: 2.129; control2X: -80.748; control2Y: -1.389; x: -99.453; y: 0.392 }
+ PathCubic { control1X: -99.453; control1Y: 0.392; control2X: -90.275; control2Y: -0.897; x: -71.774; y: 2.995 }
+ PathCubic { control1X: -61.62; control1Y: 5.131; control2X: -72.502; control2Y: 2.129; x: -72.502; y: 2.129 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -70.714; y: 2.222 }
+ PathCubic { control1X: -70.714; control1Y: 2.222; control2X: -78.676; control2Y: -1.899; x: -97.461; y: -1.514 }
+ PathCubic { control1X: -97.461; control1Y: -1.514; control2X: -88.213; control2Y: -2.118; x: -70.052; y: 3.14 }
+ PathCubic { control1X: -60.086; control1Y: 6.025; control2X: -70.714; control2Y: 2.222; x: -70.714; y: 2.222 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -69.444; y: 2.445 }
+ PathCubic { control1X: -69.444; control1Y: 2.445; control2X: -76.268; control2Y: -1.862; x: -93.142; y: -2.96 }
+ PathCubic { control1X: -93.142; control1Y: -2.96; control2X: -84.803; control2Y: -2.79; x: -68.922; y: 3.319 }
+ PathCubic { control1X: -60.206; control1Y: 6.672; control2X: -69.444; control2Y: 2.445; x: -69.444; y: 2.445 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 45.84; y: 12.961 }
+ PathCubic { control1X: 45.84; control1Y: 12.961; control2X: 44.91; control2Y: 13.605; x: 45.124; y: 12.424 }
+ PathCubic { control1X: 45.339; control1Y: 11.243; control2X: 73.547; control2Y: -1.927; x: 77.161; y: -1.677 }
+ PathCubic { control1X: 77.161; control1Y: -1.677; control2X: 46.913; control2Y: 11.529; x: 45.84; y: 12.961 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 42.446; y: 13.6 }
+ PathCubic { control1X: 42.446; control1Y: 13.6; control2X: 41.57; control2Y: 14.315; x: 41.691; y: 13.121 }
+ PathCubic { control1X: 41.812; control1Y: 11.927; control2X: 68.899; control2Y: -3.418; x: 72.521; y: -3.452 }
+ PathCubic { control1X: 72.521; control1Y: -3.452; control2X: 43.404; control2Y: 12.089; x: 42.446; y: 13.6 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 39.16; y: 14.975 }
+ PathCubic { control1X: 39.16; control1Y: 14.975; control2X: 38.332; control2Y: 15.747; x: 38.374; y: 14.547 }
+ PathCubic { control1X: 38.416; control1Y: 13.348; control2X: 58.233; control2Y: -2.149; x: 68.045; y: -4.023 }
+ PathCubic { control1X: 68.045; control1Y: -4.023; control2X: 50.015; control2Y: 4.104; x: 39.16; y: 14.975 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 36.284; y: 16.838 }
+ PathCubic { control1X: 36.284; control1Y: 16.838; control2X: 35.539; control2Y: 17.532; x: 35.577; y: 16.453 }
+ PathCubic { control1X: 35.615; control1Y: 15.373; control2X: 53.449; control2Y: 1.426; x: 62.28; y: -0.26 }
+ PathCubic { control1X: 62.28; control1Y: -0.26; control2X: 46.054; control2Y: 7.054; x: 36.284; y: 16.838 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 4.6; y: 164.801 }
+ PathCubic { control1X: 4.6; control1Y: 164.801; control2X: -10.6; control2Y: 162.401; x: 6.2; y: 160.801 }
+ PathCubic { control1X: 6.2; control1Y: 160.801; control2X: 24.2; control2Y: 158.801; x: 28.2; y: 153.601 }
+ PathCubic { control1X: 28.2; control1Y: 153.601; control2X: 41.8; control2Y: 144.401; x: 44.6; y: 144.001 }
+ PathCubic { control1X: 47.4; control1Y: 143.601; control2X: 63.8; control2Y: 140.001; x: 64.2; y: 137.601 }
+ PathCubic { control1X: 64.6; control1Y: 135.201; control2X: 70.6; control2Y: 132.801; x: 72.2; y: 133.601 }
+ PathCubic { control1X: 73.8; control1Y: 134.401; control2X: 73.8; control2Y: 143.601; x: 71; y: 144.401 }
+ PathCubic { control1X: 68.2; control1Y: 145.201; control2X: 49.4; control2Y: 152.401; x: 43; y: 153.601 }
+ PathCubic { control1X: 36.6; control1Y: 154.801; control2X: 25; control2Y: 162.401; x: 20.2; y: 163.601 }
+ PathCubic { control1X: 15.4; control1Y: 164.801; control2X: 4.6; control2Y: 164.801; x: 4.6; y: 164.801 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 77.6; y: 127.401 }
+ PathCubic { control1X: 77.6; control1Y: 127.401; control2X: 74.6; control2Y: 129.001; x: 73.4; y: 131.601 }
+ PathCubic { control1X: 73.4; control1Y: 131.601; control2X: 67; control2Y: 142.201; x: 52.8; y: 145.401 }
+ PathCubic { control1X: 52.8; control1Y: 145.401; control2X: 29.8; control2Y: 154.401; x: 22; y: 156.401 }
+ PathCubic { control1X: 22; control1Y: 156.401; control2X: 8.6; control2Y: 161.401; x: 1.2; y: 160.601 }
+ PathCubic { control1X: 1.2; control1Y: 160.601; control2X: -5.8; control2Y: 160.801; x: 0.4; y: 162.401 }
+ PathCubic { control1X: 0.4; control1Y: 162.401; control2X: 20.6; control2Y: 160.401; x: 24; y: 158.601 }
+ PathCubic { control1X: 24; control1Y: 158.601; control2X: 39.6; control2Y: 153.401; x: 42.6; y: 150.801 }
+ PathCubic { control1X: 45.6; control1Y: 148.201; control2X: 63.8; control2Y: 143.201; x: 66; y: 141.201 }
+ PathCubic { control1X: 68.2; control1Y: 139.201; control2X: 78; control2Y: 130.801; x: 77.6; y: 127.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 18.882; y: 158.911 }
+ PathCubic { control1X: 18.882; control1Y: 158.911; control2X: 24.111; control2Y: 158.685; x: 22.958; y: 160.234 }
+ PathCubic { control1X: 21.805; control1Y: 161.784; control2X: 19.357; control2Y: 160.91; x: 19.357; y: 160.91 }
+ PathLine { x: 18.882; y: 158.911 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 11.68; y: 160.263 }
+ PathCubic { control1X: 11.68; control1Y: 160.263; control2X: 16.908; control2Y: 160.037; x: 15.756; y: 161.586 }
+ PathCubic { control1X: 14.603; control1Y: 163.136; control2X: 12.155; control2Y: 162.263; x: 12.155; y: 162.263 }
+ PathLine { x: 11.68; y: 160.263 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 1.251; y: 161.511 }
+ PathCubic { control1X: 1.251; control1Y: 161.511; control2X: 6.48; control2Y: 161.284; x: 5.327; y: 162.834 }
+ PathCubic { control1X: 4.174; control1Y: 164.383; control2X: 1.726; control2Y: 163.51; x: 1.726; y: 163.51 }
+ PathLine { x: 1.251; y: 161.511 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -6.383; y: 162.055 }
+ PathCubic { control1X: -6.383; control1Y: 162.055; control2X: -1.154; control2Y: 161.829; x: -2.307; y: 163.378 }
+ PathCubic { control1X: -3.46; control1Y: 164.928; control2X: -5.908; control2Y: 164.054; x: -5.908; y: 164.054 }
+ PathLine { x: -6.383; y: 162.055 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 35.415; y: 151.513 }
+ PathCubic { control1X: 35.415; control1Y: 151.513; control2X: 42.375; control2Y: 151.212; x: 40.84; y: 153.274 }
+ PathCubic { control1X: 39.306; control1Y: 155.336; control2X: 36.047; control2Y: 154.174; x: 36.047; y: 154.174 }
+ PathLine { x: 35.415; y: 151.513 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 45.73; y: 147.088 }
+ PathCubic { control1X: 45.73; control1Y: 147.088; control2X: 51.689; control2Y: 143.787; x: 51.155; y: 148.849 }
+ PathCubic { control1X: 50.885; control1Y: 151.405; control2X: 46.362; control2Y: 149.749; x: 46.362; y: 149.749 }
+ PathLine { x: 45.73; y: 147.088 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 54.862; y: 144.274 }
+ PathCubic { control1X: 54.862; control1Y: 144.274; control2X: 62.021; control2Y: 140.573; x: 60.287; y: 146.035 }
+ PathCubic { control1X: 59.509; control1Y: 148.485; control2X: 55.493; control2Y: 146.935; x: 55.493; y: 146.935 }
+ PathLine { x: 54.862; y: 144.274 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 64.376; y: 139.449 }
+ PathCubic { control1X: 64.376; control1Y: 139.449; control2X: 68.735; control2Y: 134.548; x: 69.801; y: 141.21 }
+ PathCubic { control1X: 70.207; control1Y: 143.748; control2X: 65.008; control2Y: 142.11; x: 65.008; y: 142.11 }
+ PathLine { x: 64.376; y: 139.449 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 26.834; y: 155.997 }
+ PathCubic { control1X: 26.834; control1Y: 155.997; control2X: 32.062; control2Y: 155.77; x: 30.91; y: 157.32 }
+ PathCubic { control1X: 29.757; control1Y: 158.869; control2X: 27.308; control2Y: 157.996; x: 27.308; y: 157.996 }
+ PathLine { x: 26.834; y: 155.997 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 62.434; y: 34.603 }
+ PathCubic { control1X: 62.434; control1Y: 34.603; control2X: 61.708; control2Y: 35.268; x: 61.707; y: 34.197 }
+ PathCubic { control1X: 61.707; control1Y: 33.127; control2X: 79.191; control2Y: 19.863; x: 88.034; y: 18.479 }
+ PathCubic { control1X: 88.034; control1Y: 18.479; control2X: 71.935; control2Y: 25.208; x: 62.434; y: 34.603 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: 65.4; y: 98.4 }
+ PathCubic { control1X: 65.4; control1Y: 98.4; control2X: 87.401; control2Y: 120.801; x: 96.601; y: 124.401 }
+ PathCubic { control1X: 96.601; control1Y: 124.401; control2X: 105.801; control2Y: 135.601; x: 101.801; y: 161.601 }
+ PathCubic { control1X: 101.801; control1Y: 161.601; control2X: 98.601; control2Y: 169.201; x: 95.401; y: 148.401 }
+ PathCubic { control1X: 95.401; control1Y: 148.401; control2X: 98.601; control2Y: 123.201; x: 87.401; y: 139.201 }
+ PathCubic { control1X: 87.401; control1Y: 139.201; control2X: 79; control2Y: 129.301; x: 85.4; y: 129.601 }
+ PathCubic { control1X: 85.4; control1Y: 129.601; control2X: 88.601; control2Y: 131.601; x: 89.001; y: 130.001 }
+ PathCubic { control1X: 89.401; control1Y: 128.401; control2X: 81.4; control2Y: 114.801; x: 64.2; y: 100.4 }
+ PathCubic { control1X: 47; control1Y: 86; control2X: 65.4; control2Y: 98.4; x: 65.4; y: 98.4 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 7; y: 137.201 }
+ PathCubic { control1X: 7; control1Y: 137.201; control2X: 6.8; control2Y: 135.401; x: 8.6; y: 136.201 }
+ PathCubic { control1X: 10.4; control1Y: 137.001; control2X: 104.601; control2Y: 143.201; x: 136.201; y: 167.201 }
+ PathCubic { control1X: 136.201; control1Y: 167.201; control2X: 91.001; control2Y: 144.001; x: 7; y: 137.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 17.4; y: 132.801 }
+ PathCubic { control1X: 17.4; control1Y: 132.801; control2X: 17.2; control2Y: 131.001; x: 19; y: 131.801 }
+ PathCubic { control1X: 20.8; control1Y: 132.601; control2X: 157.401; control2Y: 131.601; x: 181.001; y: 164.001 }
+ PathCubic { control1X: 181.001; control1Y: 164.001; control2X: 159.001; control2Y: 138.801; x: 17.4; y: 132.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 29; y: 128.801 }
+ PathCubic { control1X: 29; control1Y: 128.801; control2X: 28.8; control2Y: 127.001; x: 30.6; y: 127.801 }
+ PathCubic { control1X: 32.4; control1Y: 128.601; control2X: 205.801; control2Y: 115.601; x: 229.401; y: 148.001 }
+ PathCubic { control1X: 229.401; control1Y: 148.001; control2X: 219.801; control2Y: 122.401; x: 29; y: 128.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 39; y: 124.001 }
+ PathCubic { control1X: 39; control1Y: 124.001; control2X: 38.8; control2Y: 122.201; x: 40.6; y: 123.001 }
+ PathCubic { control1X: 42.4; control1Y: 123.801; control2X: 164.601; control2Y: 85.2; x: 188.201; y: 117.601 }
+ PathCubic { control1X: 188.201; control1Y: 117.601; control2X: 174.801; control2Y: 93; x: 39; y: 124.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -19; y: 146.801 }
+ PathCubic { control1X: -19; control1Y: 146.801; control2X: -19.2; control2Y: 145.001; x: -17.4; y: 145.801 }
+ PathCubic { control1X: -15.6; control1Y: 146.601; control2X: 2.2; control2Y: 148.801; x: 4.2; y: 187.601 }
+ PathCubic { control1X: 4.2; control1Y: 187.601; control2X: -3; control2Y: 145.601; x: -19; y: 146.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -27.8; y: 148.401 }
+ PathCubic { control1X: -27.8; control1Y: 148.401; control2X: -28; control2Y: 146.601; x: -26.2; y: 147.401 }
+ PathCubic { control1X: -24.4; control1Y: 148.201; control2X: -10.2; control2Y: 143.601; x: -13; y: 182.401 }
+ PathCubic { control1X: -13; control1Y: 182.401; control2X: -11.8; control2Y: 147.201; x: -27.8; y: 148.401 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -35.8; y: 148.801 }
+ PathCubic { control1X: -35.8; control1Y: 148.801; control2X: -36; control2Y: 147.001; x: -34.2; y: 147.801 }
+ PathCubic { control1X: -32.4; control1Y: 148.601; control2X: -17; control2Y: 149.201; x: -29.4; y: 171.601 }
+ PathCubic { control1X: -29.4; control1Y: 171.601; control2X: -19.8; control2Y: 147.601; x: -35.8; y: 148.801 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 11.526; y: 104.465 }
+ PathCubic { control1X: 11.526; control1Y: 104.465; control2X: 11.082; control2Y: 106.464; x: 12.631; y: 105.247 }
+ PathCubic { control1X: 28.699; control1Y: 92.622; control2X: 61.141; control2Y: 33.72; x: 116.826; y: 28.086 }
+ PathCubic { control1X: 116.826; control1Y: 28.086; control2X: 78.518; control2Y: 15.976; x: 11.526; y: 104.465 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 22.726; y: 102.665 }
+ PathCubic { control1X: 22.726; control1Y: 102.665; control2X: 21.363; control2Y: 101.472; x: 23.231; y: 100.847 }
+ PathCubic { control1X: 25.099; control1Y: 100.222; control2X: 137.541; control2Y: 27.72; x: 176.826; y: 35.686 }
+ PathCubic { control1X: 176.826; control1Y: 35.686; control2X: 149.719; control2Y: 28.176; x: 22.726; y: 102.665 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 1.885; y: 108.767 }
+ PathCubic { control1X: 1.885; control1Y: 108.767; control2X: 1.376; control2Y: 110.366; x: 3.087; y: 109.39 }
+ PathCubic { control1X: 12.062; control1Y: 104.27; control2X: 15.677; control2Y: 47.059; x: 59.254; y: 45.804 }
+ PathCubic { control1X: 59.254; control1Y: 45.804; control2X: 26.843; control2Y: 31.09; x: 1.885; y: 108.767 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -18.038; y: 119.793 }
+ PathCubic { control1X: -18.038; control1Y: 119.793; control2X: -19.115; control2Y: 121.079; x: -17.162; y: 120.825 }
+ PathCubic { control1X: -6.916; control1Y: 119.493; control2X: 14.489; control2Y: 78.222; x: 58.928; y: 83.301 }
+ PathCubic { control1X: 58.928; control1Y: 83.301; control2X: 26.962; control2Y: 68.955; x: -18.038; y: 119.793 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -6.8; y: 113.667 }
+ PathCubic { control1X: -6.8; control1Y: 113.667; control2X: -7.611; control2Y: 115.136; x: -5.742; y: 114.511 }
+ PathCubic { control1X: 4.057; control1Y: 111.237; control2X: 17.141; control2Y: 66.625; x: 61.729; y: 63.078 }
+ PathCubic { control1X: 61.729; control1Y: 63.078; control2X: 27.603; control2Y: 55.135; x: -6.8; y: 113.667 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -25.078; y: 124.912 }
+ PathCubic { control1X: -25.078; control1Y: 124.912; control2X: -25.951; control2Y: 125.954; x: -24.369; y: 125.748 }
+ PathCubic { control1X: -16.07; control1Y: 124.669; control2X: 1.268; control2Y: 91.24; x: 37.264; y: 95.354 }
+ PathCubic { control1X: 37.264; control1Y: 95.354; control2X: 11.371; control2Y: 83.734; x: -25.078; y: 124.912 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -32.677; y: 130.821 }
+ PathCubic { control1X: -32.677; control1Y: 130.821; control2X: -33.682; control2Y: 131.866; x: -32.091; y: 131.748 }
+ PathCubic { control1X: -27.923; control1Y: 131.439; control2X: 2.715; control2Y: 98.36; x: 21.183; y: 113.862 }
+ PathCubic { control1X: 21.183; control1Y: 113.862; control2X: 9.168; control2Y: 95.139; x: -32.677; y: 130.821 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 36.855; y: 98.898 }
+ PathCubic { control1X: 36.855; control1Y: 98.898; control2X: 35.654; control2Y: 97.543; x: 37.586; y: 97.158 }
+ PathCubic { control1X: 39.518; control1Y: 96.774; control2X: 160.221; control2Y: 39.061; x: 198.184; y: 51.927 }
+ PathCubic { control1X: 198.184; control1Y: 51.927; control2X: 172.243; control2Y: 41.053; x: 36.855; y: 98.898 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 3.4; y: 163.201 }
+ PathCubic { control1X: 3.4; control1Y: 163.201; control2X: 3.2; control2Y: 161.401; x: 5; y: 162.201 }
+ PathCubic { control1X: 6.8; control1Y: 163.001; control2X: 22.2; control2Y: 163.601; x: 9.8; y: 186.001 }
+ PathCubic { control1X: 9.8; control1Y: 186.001; control2X: 19.4; control2Y: 162.001; x: 3.4; y: 163.201 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 13.8; y: 161.601 }
+ PathCubic { control1X: 13.8; control1Y: 161.601; control2X: 13.6; control2Y: 159.801; x: 15.4; y: 160.601 }
+ PathCubic { control1X: 17.2; control1Y: 161.401; control2X: 35; control2Y: 163.601; x: 37; y: 202.401 }
+ PathCubic { control1X: 37; control1Y: 202.401; control2X: 29.8; control2Y: 160.401; x: 13.8; y: 161.601 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 20.6; y: 160.001 }
+ PathCubic { control1X: 20.6; control1Y: 160.001; control2X: 20.4; control2Y: 158.201; x: 22.2; y: 159.001 }
+ PathCubic { control1X: 24; control1Y: 159.801; control2X: 48.6; control2Y: 163.201; x: 72.2; y: 195.601 }
+ PathCubic { control1X: 72.2; control1Y: 195.601; control2X: 36.6; control2Y: 158.801; x: 20.6; y: 160.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 28.225; y: 157.972 }
+ PathCubic { control1X: 28.225; control1Y: 157.972; control2X: 27.788; control2Y: 156.214; x: 29.678; y: 156.768 }
+ PathCubic { control1X: 31.568; control1Y: 157.322; control2X: 52.002; control2Y: 155.423; x: 90.099; y: 189.599 }
+ PathCubic { control1X: 90.099; control1Y: 189.599; control2X: 43.924; control2Y: 154.656; x: 28.225; y: 157.972 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 38.625; y: 153.572 }
+ PathCubic { control1X: 38.625; control1Y: 153.572; control2X: 38.188; control2Y: 151.814; x: 40.078; y: 152.368 }
+ PathCubic { control1X: 41.968; control1Y: 152.922; control2X: 76.802; control2Y: 157.423; x: 128.499; y: 192.399 }
+ PathCubic { control1X: 128.499; control1Y: 192.399; control2X: 54.324; control2Y: 150.256; x: 38.625; y: 153.572 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -1.8; y: 142.001 }
+ PathCubic { control1X: -1.8; control1Y: 142.001; control2X: -2; control2Y: 140.201; x: -0.2; y: 141.001 }
+ PathCubic { control1X: 1.6; control1Y: 141.801; control2X: 55; control2Y: 144.401; x: 85.4; y: 171.201 }
+ PathCubic { control1X: 85.4; control1Y: 171.201; control2X: 50.499; control2Y: 146.426; x: -1.8; y: 142.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: -11.8; y: 146.001 }
+ PathCubic { control1X: -11.8; control1Y: 146.001; control2X: -12; control2Y: 144.201; x: -10.2; y: 145.001 }
+ PathCubic { control1X: -8.4; control1Y: 145.801; control2X: 16.2; control2Y: 149.201; x: 39.8; y: 181.601 }
+ PathCubic { control1X: 39.8; control1Y: 181.601; control2X: 4.2; control2Y: 144.801; x: -11.8; y: 146.001 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 49.503; y: 148.962 }
+ PathCubic { control1X: 49.503; control1Y: 148.962; control2X: 48.938; control2Y: 147.241; x: 50.864; y: 147.655 }
+ PathCubic { control1X: 52.79; control1Y: 148.068; control2X: 87.86; control2Y: 150.004; x: 141.981; y: 181.098 }
+ PathCubic { control1X: 141.981; control1Y: 181.098; control2X: 64.317; control2Y: 146.704; x: 49.503; y: 148.962 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 57.903; y: 146.562 }
+ PathCubic { control1X: 57.903; control1Y: 146.562; control2X: 57.338; control2Y: 144.841; x: 59.264; y: 145.255 }
+ PathCubic { control1X: 61.19; control1Y: 145.668; control2X: 96.26; control2Y: 147.604; x: 150.381; y: 178.698 }
+ PathCubic { control1X: 150.381; control1Y: 178.698; control2X: 73.317; control2Y: 143.904; x: 57.903; y: 146.562 }
+ }
+
+ ShapePath {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ PathMove { x: 67.503; y: 141.562 }
+ PathCubic { control1X: 67.503; control1Y: 141.562; control2X: 66.938; control2Y: 139.841; x: 68.864; y: 140.255 }
+ PathCubic { control1X: 70.79; control1Y: 140.668; control2X: 113.86; control2Y: 145.004; x: 203.582; y: 179.298 }
+ PathCubic { control1X: 203.582; control1Y: 179.298; control2X: 82.917; control2Y: 138.904; x: 67.503; y: 141.562 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -43.8; y: 148.401 }
+ PathCubic { control1X: -43.8; control1Y: 148.401; control2X: -38.6; control2Y: 148.001; x: -39.8; y: 149.601 }
+ PathCubic { control1X: -41; control1Y: 151.201; control2X: -43.4; control2Y: 150.401; x: -43.4; y: 150.401 }
+ PathLine { x: -43.8; y: 148.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -13; y: 162.401 }
+ PathCubic { control1X: -13; control1Y: 162.401; control2X: -7.8; control2Y: 162.001; x: -9; y: 163.601 }
+ PathCubic { control1X: -10.2; control1Y: 165.201; control2X: -12.6; control2Y: 164.401; x: -12.6; y: 164.401 }
+ PathLine { x: -13; y: 162.401 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -21.8; y: 162.001 }
+ PathCubic { control1X: -21.8; control1Y: 162.001; control2X: -16.6; control2Y: 161.601; x: -17.8; y: 163.201 }
+ PathCubic { control1X: -19; control1Y: 164.801; control2X: -21.4; control2Y: 164.001; x: -21.4; y: 164.001 }
+ PathLine { x: -21.8; y: 162.001 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -117.169; y: 150.182 }
+ PathCubic { control1X: -117.169; control1Y: 150.182; control2X: -112.124; control2Y: 151.505; x: -113.782; y: 152.624 }
+ PathCubic { control1X: -115.439; control1Y: 153.744; control2X: -117.446; control2Y: 152.202; x: -117.446; y: 152.202 }
+ PathLine { x: -117.169; y: 150.182 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -115.169; y: 140.582 }
+ PathCubic { control1X: -115.169; control1Y: 140.582; control2X: -110.124; control2Y: 141.905; x: -111.782; y: 143.024 }
+ PathCubic { control1X: -113.439; control1Y: 144.144; control2X: -115.446; control2Y: 142.602; x: -115.446; y: 142.602 }
+ PathLine { x: -115.169; y: 140.582 }
+ }
+
+ ShapePath {
+ fillColor: "#000000"
+ strokeWidth: -1
+ PathMove { x: -122.369; y: 136.182 }
+ PathCubic { control1X: -122.369; control1Y: 136.182; control2X: -117.324; control2Y: 137.505; x: -118.982; y: 138.624 }
+ PathCubic { control1X: -120.639; control1Y: 139.744; control2X: -122.646; control2Y: 138.202; x: -122.646; y: 138.202 }
+ PathLine { x: -122.369; y: 136.182 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -42.6; y: 211.201 }
+ PathCubic { control1X: -42.6; control1Y: 211.201; control2X: -44.2; control2Y: 211.201; x: -48.2; y: 213.201 }
+ PathCubic { control1X: -50.2; control1Y: 213.201; control2X: -61.4; control2Y: 216.801; x: -67; y: 226.801 }
+ PathCubic { control1X: -67; control1Y: 226.801; control2X: -54.6; control2Y: 217.201; x: -42.6; y: 211.201 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 45.116; y: 303.847 }
+ PathCubic { control1X: 45.257; control1Y: 304.105; control2X: 45.312; control2Y: 304.525; x: 45.604; y: 304.542 }
+ PathCubic { control1X: 46.262; control1Y: 304.582; control2X: 47.495; control2Y: 304.883; x: 47.37; y: 304.247 }
+ PathCubic { control1X: 46.522; control1Y: 299.941; control2X: 45.648; control2Y: 295.004; x: 41.515; y: 293.197 }
+ PathCubic { control1X: 40.876; control1Y: 292.918; control2X: 39.434; control2Y: 293.331; x: 39.36; y: 294.215 }
+ PathCubic { control1X: 39.233; control1Y: 295.739; control2X: 39.116; control2Y: 297.088; x: 39.425; y: 298.554 }
+ PathCubic { control1X: 39.725; control1Y: 299.975; control2X: 41.883; control2Y: 299.985; x: 42.8; y: 298.601 }
+ PathCubic { control1X: 43.736; control1Y: 300.273; control2X: 44.168; control2Y: 302.116; x: 45.116; y: 303.847 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 34.038; y: 308.581 }
+ PathCubic { control1X: 34.786; control1Y: 309.994; control2X: 34.659; control2Y: 311.853; x: 36.074; y: 312.416 }
+ PathCubic { control1X: 36.814; control1Y: 312.71; control2X: 38.664; control2Y: 311.735; x: 38.246; y: 310.661 }
+ PathCubic { control1X: 37.444; control1Y: 308.6; control2X: 37.056; control2Y: 306.361; x: 35.667; y: 304.55 }
+ PathCubic { control1X: 35.467; control1Y: 304.288; control2X: 35.707; control2Y: 303.755; x: 35.547; y: 303.427 }
+ PathCubic { control1X: 34.953; control1Y: 302.207; control2X: 33.808; control2Y: 301.472; x: 32.4; y: 301.801 }
+ PathCubic { control1X: 31.285; control1Y: 304.004; control2X: 32.433; control2Y: 306.133; x: 33.955; y: 307.842 }
+ PathCubic { control1X: 34.091; control1Y: 307.994; control2X: 33.925; control2Y: 308.37; x: 34.038; y: 308.581 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -5.564; y: 303.391 }
+ PathCubic { control1X: -5.672; control1Y: 303.014; control2X: -5.71; control2Y: 302.551; x: -5.545; y: 302.23 }
+ PathCubic { control1X: -5.014; control1Y: 301.197; control2X: -4.221; control2Y: 300.075; x: -4.558; y: 299.053 }
+ PathCubic { control1X: -4.906; control1Y: 297.997; control2X: -6.022; control2Y: 298.179; x: -6.672; y: 298.748 }
+ PathCubic { control1X: -7.807; control1Y: 299.742; control2X: -7.856; control2Y: 301.568; x: -8.547; y: 302.927 }
+ PathCubic { control1X: -8.743; control1Y: 303.313; control2X: -8.692; control2Y: 303.886; x: -9.133; y: 304.277 }
+ PathCubic { control1X: -9.607; control1Y: 304.698; control2X: -10.047; control2Y: 306.222; x: -9.951; y: 306.793 }
+ PathCubic { control1X: -9.898; control1Y: 307.106; control2X: -10.081; control2Y: 317.014; x: -9.859; y: 316.751 }
+ PathCubic { control1X: -9.24; control1Y: 316.018; control2X: -6.19; control2Y: 306.284; x: -6.121; y: 305.392 }
+ PathCubic { control1X: -6.064; control1Y: 304.661; control2X: -5.332; control2Y: 304.196; x: -5.564; y: 303.391 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -31.202; y: 296.599 }
+ PathCubic { control1X: -28.568; control1Y: 294.1; control2X: -25.778; control2Y: 291.139; x: -26.22; y: 287.427 }
+ PathCubic { control1X: -26.336; control1Y: 286.451; control2X: -28.111; control2Y: 286.978; x: -28.298; y: 287.824 }
+ PathCubic { control1X: -29.1; control1Y: 291.449; control2X: -31.139; control2Y: 294.11; x: -33.707; y: 296.502 }
+ PathCubic { control1X: -35.903; control1Y: 298.549; control2X: -37.765; control2Y: 304.893; x: -38; y: 305.401 }
+ PathCubic { control1X: -34.303; control1Y: 300.145; control2X: -32.046; control2Y: 297.399; x: -31.202; y: 296.599 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -44.776; y: 290.635 }
+ PathCubic { control1X: -44.253; control1Y: 290.265; control2X: -44.555; control2Y: 289.774; x: -44.338; y: 289.442 }
+ PathCubic { control1X: -43.385; control1Y: 287.984; control2X: -42.084; control2Y: 286.738; x: -42.066; y: 285 }
+ PathCubic { control1X: -42.063; control1Y: 284.723; control2X: -42.441; control2Y: 284.414; x: -42.776; y: 284.638 }
+ PathCubic { control1X: -43.053; control1Y: 284.822; control2X: -43.395; control2Y: 284.952; x: -43.503; y: 285.082 }
+ PathCubic { control1X: -45.533; control1Y: 287.531; control2X: -46.933; control2Y: 290.202; x: -48.376; y: 293.014 }
+ PathCubic { control1X: -48.559; control1Y: 293.371; control2X: -49.703; control2Y: 297.862; x: -49.39; y: 297.973 }
+ PathCubic { control1X: -49.151; control1Y: 298.058; control2X: -47.431; control2Y: 293.877; x: -47.221; y: 293.763 }
+ PathCubic { control1X: -45.958; control1Y: 293.077; control2X: -45.946; control2Y: 291.462; x: -44.776; y: 290.635 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -28.043; y: 310.179 }
+ PathCubic { control1X: -27.599; control1Y: 309.31; control2X: -26.023; control2Y: 308.108; x: -26.136; y: 307.219 }
+ PathCubic { control1X: -26.254; control1Y: 306.291; control2X: -25.786; control2Y: 304.848; x: -26.698; y: 305.536 }
+ PathCubic { control1X: -27.955; control1Y: 306.484; control2X: -31.404; control2Y: 307.833; x: -31.674; y: 313.641 }
+ PathCubic { control1X: -31.7; control1Y: 314.212; control2X: -28.726; control2Y: 311.519; x: -28.043; y: 310.179 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -13.6; y: 293.001 }
+ PathCubic { control1X: -13.2; control1Y: 292.333; control2X: -12.492; control2Y: 292.806; x: -12.033; y: 292.543 }
+ PathCubic { control1X: -11.385; control1Y: 292.171; control2X: -10.774; control2Y: 291.613; x: -10.482; y: 290.964 }
+ PathCubic { control1X: -9.512; control1Y: 288.815; control2X: -7.743; control2Y: 286.995; x: -7.6; y: 284.601 }
+ PathCubic { control1X: -9.091; control1Y: 283.196; control2X: -9.77; control2Y: 285.236; x: -10.4; y: 286.201 }
+ PathCubic { control1X: -11.723; control1Y: 284.554; control2X: -12.722; control2Y: 286.428; x: -14.022; y: 286.947 }
+ PathCubic { control1X: -14.092; control1Y: 286.975; control2X: -14.305; control2Y: 286.628; x: -14.38; y: 286.655 }
+ PathCubic { control1X: -15.557; control1Y: 287.095; control2X: -16.237; control2Y: 288.176; x: -17.235; y: 288.957 }
+ PathCubic { control1X: -17.406; control1Y: 289.091; control2X: -17.811; control2Y: 288.911; x: -17.958; y: 289.047 }
+ PathCubic { control1X: -18.61; control1Y: 289.65; control2X: -19.583; control2Y: 289.975; x: -19.863; y: 290.657 }
+ PathCubic { control1X: -20.973; control1Y: 293.364; control2X: -24.113; control2Y: 295.459; x: -26; y: 303.001 }
+ PathCubic { control1X: -25.619; control1Y: 303.91; control2X: -21.488; control2Y: 296.359; x: -21.001; y: 295.661 }
+ PathCubic { control1X: -20.165; control1Y: 294.465; control2X: -20.047; control2Y: 297.322; x: -18.771; y: 296.656 }
+ PathCubic { control1X: -18.72; control1Y: 296.629; control2X: -18.534; control2Y: 296.867; x: -18.4; y: 297.001 }
+ PathCubic { control1X: -18.206; control1Y: 296.721; control2X: -17.988; control2Y: 296.492; x: -17.6; y: 296.601 }
+ PathCubic { control1X: -17.6; control1Y: 296.201; control2X: -17.734; control2Y: 295.645; x: -17.533; y: 295.486 }
+ PathCubic { control1X: -16.296; control1Y: 294.509; control2X: -16.38; control2Y: 293.441; x: -15.6; y: 292.201 }
+ PathCubic { control1X: -15.142; control1Y: 292.99; control2X: -14.081; control2Y: 292.271; x: -13.6; y: 293.001 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 46.2; y: 347.401 }
+ PathCubic { control1X: 46.2; control1Y: 347.401; control2X: 53.6; control2Y: 327.001; x: 49.2; y: 315.801 }
+ PathCubic { control1X: 49.2; control1Y: 315.801; control2X: 60.6; control2Y: 337.401; x: 56; y: 348.601 }
+ PathCubic { control1X: 56; control1Y: 348.601; control2X: 55.6; control2Y: 338.201; x: 51.6; y: 333.201 }
+ PathCubic { control1X: 51.6; control1Y: 333.201; control2X: 47.6; control2Y: 346.001; x: 46.2; y: 347.401 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 31.4; y: 344.801 }
+ PathCubic { control1X: 31.4; control1Y: 344.801; control2X: 36.8; control2Y: 336.001; x: 28.8; y: 317.601 }
+ PathCubic { control1X: 28.8; control1Y: 317.601; control2X: 28; control2Y: 338.001; x: 21.2; y: 349.001 }
+ PathCubic { control1X: 21.2; control1Y: 349.001; control2X: 35.4; control2Y: 328.801; x: 31.4; y: 344.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 21.4; y: 342.801 }
+ PathCubic { control1X: 21.4; control1Y: 342.801; control2X: 21.2; control2Y: 322.801; x: 21.6; y: 319.801 }
+ PathCubic { control1X: 21.6; control1Y: 319.801; control2X: 17.8; control2Y: 336.401; x: 7.6; y: 346.001 }
+ PathCubic { control1X: 7.6; control1Y: 346.001; control2X: 22; control2Y: 334.001; x: 21.4; y: 342.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 11.8; y: 310.801 }
+ PathCubic { control1X: 11.8; control1Y: 310.801; control2X: 17.8; control2Y: 324.401; x: 7.8; y: 342.801 }
+ PathCubic { control1X: 7.8; control1Y: 342.801; control2X: 14.2; control2Y: 330.601; x: 9.4; y: 323.601 }
+ PathCubic { control1X: 9.4; control1Y: 323.601; control2X: 12; control2Y: 320.201; x: 11.8; y: 310.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -7.4; y: 342.401 }
+ PathCubic { control1X: -7.4; control1Y: 342.401; control2X: -8.4; control2Y: 326.801; x: -6.6; y: 324.601 }
+ PathCubic { control1X: -6.6; control1Y: 324.601; control2X: -6.4; control2Y: 318.201; x: -6.8; y: 317.201 }
+ PathCubic { control1X: -6.8; control1Y: 317.201; control2X: -2.8; control2Y: 311.001; x: -2.6; y: 318.401 }
+ PathCubic { control1X: -2.6; control1Y: 318.401; control2X: -1.2; control2Y: 326.201; x: 1.6; y: 330.801 }
+ PathCubic { control1X: 1.6; control1Y: 330.801; control2X: 5.2; control2Y: 336.201; x: 5; y: 342.601 }
+ PathCubic { control1X: 5; control1Y: 342.601; control2X: -5; control2Y: 312.401; x: -7.4; y: 342.401 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -11; y: 314.801 }
+ PathCubic { control1X: -11; control1Y: 314.801; control2X: -17.6; control2Y: 325.601; x: -19.4; y: 344.601 }
+ PathCubic { control1X: -19.4; control1Y: 344.601; control2X: -20.8; control2Y: 338.401; x: -17; y: 324.001 }
+ PathCubic { control1X: -17; control1Y: 324.001; control2X: -12.8; control2Y: 308.601; x: -11; y: 314.801 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -32.8; y: 334.601 }
+ PathCubic { control1X: -32.8; control1Y: 334.601; control2X: -27.8; control2Y: 329.201; x: -26.4; y: 324.201 }
+ PathCubic { control1X: -26.4; control1Y: 324.201; control2X: -22.8; control2Y: 308.401; x: -29.2; y: 317.001 }
+ PathCubic { control1X: -29.2; control1Y: 317.001; control2X: -29; control2Y: 325.001; x: -37.2; y: 332.401 }
+ PathCubic { control1X: -37.2; control1Y: 332.401; control2X: -32.4; control2Y: 330.001; x: -32.8; y: 334.601 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -38.6; y: 329.601 }
+ PathCubic { control1X: -38.6; control1Y: 329.601; control2X: -35.2; control2Y: 312.201; x: -34.4; y: 311.401 }
+ PathCubic { control1X: -34.4; control1Y: 311.401; control2X: -32.6; control2Y: 308.001; x: -35.4; y: 311.201 }
+ PathCubic { control1X: -35.4; control1Y: 311.201; control2X: -44.2; control2Y: 330.401; x: -48.2; y: 337.001 }
+ PathCubic { control1X: -48.2; control1Y: 337.001; control2X: -40.2; control2Y: 327.801; x: -38.6; y: 329.601 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -44.4; y: 313.001 }
+ PathCubic { control1X: -44.4; control1Y: 313.001; control2X: -32.8; control2Y: 290.601; x: -54.6; y: 316.401 }
+ PathCubic { control1X: -54.6; control1Y: 316.401; control2X: -43.6; control2Y: 306.601; x: -44.4; y: 313.001 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: -59.8; y: 298.401 }
+ PathCubic { control1X: -59.8; control1Y: 298.401; control2X: -55; control2Y: 279.601; x: -52.4; y: 279.801 }
+ PathCubic { control1X: -52.4; control1Y: 279.801; control2X: -44.2; control2Y: 270.801; x: -50.8; y: 281.401 }
+ PathCubic { control1X: -50.8; control1Y: 281.401; control2X: -56.8; control2Y: 291.001; x: -56.2; y: 300.801 }
+ PathCubic { control1X: -56.2; control1Y: 300.801; control2X: -56.8; control2Y: 291.201; x: -59.8; y: 298.401 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 270.5; y: 287 }
+ PathCubic { control1X: 270.5; control1Y: 287; control2X: 258.5; control2Y: 277; x: 256; y: 273.5 }
+ PathCubic { control1X: 256; control1Y: 273.5; control2X: 269.5; control2Y: 292; x: 269.5; y: 299 }
+ PathCubic { control1X: 269.5; control1Y: 299; control2X: 272; control2Y: 291.5; x: 270.5; y: 287 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 276; y: 265 }
+ PathCubic { control1X: 276; control1Y: 265; control2X: 255; control2Y: 250; x: 251.5; y: 242.5 }
+ PathCubic { control1X: 251.5; control1Y: 242.5; control2X: 278; control2Y: 272; x: 278; y: 276.5 }
+ PathCubic { control1X: 278; control1Y: 276.5; control2X: 278.5; control2Y: 267.5; x: 276; y: 265 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 293; y: 111 }
+ PathCubic { control1X: 293; control1Y: 111; control2X: 281; control2Y: 103; x: 279.5; y: 105 }
+ PathCubic { control1X: 279.5; control1Y: 105; control2X: 290; control2Y: 111.5; x: 292.5; y: 120 }
+ PathCubic { control1X: 292.5; control1Y: 120; control2X: 291; control2Y: 111; x: 293; y: 111 }
+ }
+
+ ShapePath {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ PathMove { x: 301.5; y: 191.5 }
+ PathLine { x: 284; y: 179.5 }
+ PathCubic { control1X: 284; control1Y: 179.5; control2X: 303; control2Y: 196.5; x: 303.5; y: 200.5 }
+ PathLine { x: 301.5; y: 191.5 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -89.25; y: 169 }
+ PathLine { x: -67.25; y: 173.75 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -39; y: 331 }
+ PathCubic { control1X: -39; control1Y: 331; control2X: -39.5; control2Y: 327.5; x: -48.5; y: 338 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: -33.5; y: 336 }
+ PathCubic { control1X: -33.5; control1Y: 336; control2X: -31.5; control2Y: 329.5; x: -38; y: 334 }
+ }
+
+ ShapePath {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ PathMove { x: 20.5; y: 344.5 }
+ PathCubic { control1X: 20.5; control1Y: 344.5; control2X: 22; control2Y: 333.5; x: 10.5; y: 346.5 }
+ }
+
+ Shape {
+ x: -184; y: -144; width: parent.width; height: parent.height
+ opacity: tapHandler.pressed ? 1 : 0
+ containsMode: Shape.FillContains
+
+ TapHandler { id: tapHandler }
+
+ ShapePath {
+ strokeColor: "red"
+ strokeWidth: 4
+ fillColor: "transparent"
+ PathSvg { path: `m 325.03711,0.5
+ c -26.61408,6.4494547 -49.95197,2.1018066 -76.21132,1.0771669
+ -22.26577,7.6817151 -47.96405,9.3627181 -65.67832,25.8497861
+ -15.74718,12.80008 -41.1564,19.605644 -45.74903,40.600391
+ -12.46933,17.76181 -25.36105,35.720146 -29.20117,57.999996
+ -18.709864,3.10961 -16.347355,30.83801 -22.385143,46.675
+ -6.848711,11.2677 11.07278,24.69174 -8.514666,27.97383
+ -10.266901,5.61543 -12.859313,28.96588 -13.732346,5.78143
+ 0.940083,-11.53398 -13.486195,-38.30626 -16.81701,-34.20231
+ 14.608079,7.8234 21.299281,50.52979 11.380052,48.14418
+ -3.406456,-15.12428 -26.181106,-38.29457 -31.849471,-35.62945
+ 16.851912,6.41472 35.569884,31.75215 28.172486,47.93115
+ -7.906485,-15.42757 -37.758959,-35.53783 -44.275447,-31.28685
+ 18.975831,1.7428 37.986009,20.68109 42.87115,37.14427 C
+ 42.279655,225.774 9.879724,213.57795 4.7080253,219.04989
+ 20.780803,212.57418 55.055919,239.88547 49.602579,241.25683
+ 38.186641,230.40078 6.6930104,222.77983 2.5752529,228.41774 c
+ 13.6045481,-8.33065 49.4437901,14.89041 43.5525671,14.2358
+ -9.759981,-7.96123 -43.5842921,7.36937 -17.554974,-1.20248
+ 9.464499,-3.73452 40.555672,12.80659 16.398749,5.14121
+ -9.1987,-7.28225 -39.0013156,3.37352 -14.121965,-2.12828
+ 13.244874,-0.0206 35.758428,14.62706 10.562447,6.42228
+ -10.780465,-8.4873 -47.8282254,11.10651 -21.027329,-0.003
+ 11.640859,-4.82877 52.615601,10.74471 24.234828,8.2659
+ -10.695834,-7.03902 -42.9384162,8.93905 -34.227854,5.58373
+ 9.077539,-8.56443 49.068801,-5.28097 43.06838,0.45546
+ -10.900893,-0.7118 -27.449619,17.27258 -10.00187,3.46526
+ 15.705191,-9.18198 18.344231,9.31645 1.10807,8.73907
+ -9.908444,1.77856 -21.108189,20.66671 -7.974821,4.92019
+ 15.750746,-14.10374 34.01348,2.07267 9.796961,8.69337
+ -8.17128,5.49929 -12.642664,19.13654 -3.994573,4.19708
+ 9.044753,-8.7077 23.850399,-13.64552 21.404959,4.02329
+ 12.509737,17.12562 51.158782,11.0442 45.106112,43.34009
+ -0.65006,10.05318 -3.79228,13.95389 1.62128,14.30064
+ -4.30913,8.82737 -14.652714,37.9591 2.92144,17.46024
+ 7.37972,-3.68333 -7.62399,16.24161 -7.98007,23.83761
+ -9.336865,18.77418 19.74873,-18.55943 6.62229,5.46195
+ 5.46464,-3.7389 36.23886,-19.41901 14.78167,0.58987
+ -8.59505,4.55644 29.29441,-2.99423 8.95489,6.47134 -9.22562,5.54437
+ -24.09765,26.79976 -11.73274,22.20385 -0.81685,5.4936
+ -1.58629,21.47626 2.34158,9.14886 1.61237,14.67029
+ -2.38384,25.22225 12.26908,15.1741 -4.40761,8.01039
+ -8.23679,36.91214 5.12235,17.92578 1.53454,2.99551 9.37569,3.1726
+ 7.15304,14.93579 3.51234,-11.31873 18.4607,-29.83809
+ 12.36869,-6.48005 -0.22629,16.26174 5.44303,-7.24791
+ 6.56926,10.49819 12.45412,28.9931 3.40908,-41.89883
+ 17.52051,-9.19238 3.23093,11.1924 6.53006,29.46941 7.55984,5.1249
+ 15.37236,-19.52583 4.09776,20.07416 12.64063,1.48215
+ 18.11247,-24.55068 -8.92586,38.39355 6.73828,6.62225
+ 4.55353,-6.91007 15.35028,-38.88977 12.55806,-13.78666
+ 1.05309,27.02664 11.54743,-24.40259 12.40657,6.86306
+ -1.72561,13.28253 11.85393,-24.15909 13.85568,-1.38002
+ 3.12455,8.33539 8.76536,26.46432 8.73882,5.09231 3.57025,-10.37352
+ -16.025,-37.75672 0.20707,-22.5788 -1.2458,-14.17213
+ -2.38918,-16.90145 10.85489,-6.71468 -16.57629,-17.22152
+ 0.19706,-26.08949 5.7751,-19.14889 -14.91681,-16.1674
+ 19.74174,7.19334 2.31875,-9.86869 -4.32508,-15.23278
+ 27.25228,29.12341 20.27514,18.81172 -11.97527,-18.92603
+ -17.96305,-45.80333 11.70099,-51.52566 17.19069,-9.57351
+ 31.17452,21.93154 38.50541,1.56304 16.26048,-4.6633
+ 22.3749,38.26516 24.86349,9.11316 5.94153,-9.9731 30.14313,6.97379
+ 36.34294,4.75012 7.07435,18.27732 8.06778,14.78971 11.04264,3.86016
+ 2.73754,-15.85945 28.7269,10.06391 28.09146,25.96561 3.00672,2.4754
+ 6.55025,-22.10264 11.23552,-14.43872 2.84155,-11.4823
+ -3.28976,-27.88574 4.24895,-25.5189 -0.61494,-11.53957
+ 22.83611,0.11011 10.64648,-15.28756 -6.5587,-21.38598
+ 9.32959,-3.0159 13.5107,-4.69375 -1.38592,-16.74533
+ -8.66673,-31.83316 -1.90087,-41.0875 2.39623,-15.14303
+ -12.50533,-44.45478 -4.70573,-48.49375 15.08472,3.42779
+ -20.39159,-42.17451 -1.69776,-40.85728 24.07272,21.63552
+ -3.65989,-30.10299 2.27233,-33.17152 16.90643,17.53071
+ -12.7383,-38.42821 6.79531,-21.57013 -4.50946,-21.08135
+ -2.53357,-37.43561 -15.5535,-55.59527 -11.0035,-12.40086
+ -1.87775,-7.12745 1.34831,-8.11755 C 468.27562,118.9774
+ 451.40746,102.656 430.98897,92.119168 439.06192,78.203836
+ 455.88012,60.123881 457.38638,40.337815 463.2373,23.183067
+ 450.82861,4.7342783 435.04883,22.626367 409.5188,28.206712
+ 386.3569,24.131269 365.63904,8.0954152 352.788,2.8857182
+ 338.88892,0.40735091 325.03711,0.5 Z m -219.0625,357.04297
+ -0.97656,0.88476 z`
+ }
+ }
+ }
+}
+
diff --git a/tests/benchmarks/qml/qmldom/tst_qmldomconstruction.cpp b/tests/benchmarks/qml/qmldom/tst_qmldomconstruction.cpp
new file mode 100644
index 0000000000..6c687661f9
--- /dev/null
+++ b/tests/benchmarks/qml/qmldom/tst_qmldomconstruction.cpp
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+
+#include <QtTest/QtTest>
+#include <QtCore/QLibraryInfo>
+
+class tst_qmldomconstruction : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void domConstructionTime_data();
+ void domConstructionTime();
+};
+
+void tst_qmldomconstruction::domConstructionTime_data()
+{
+ using namespace QQmlJS::Dom;
+ using namespace Qt::StringLiterals;
+
+ const auto baseDir = QLatin1String(SRCDIR) + QLatin1String("/data");
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<DomCreationOptions>("withScope");
+
+ DomCreationOptions withScope = DomCreationOption::WithSemanticAnalysis;
+ DomCreationOptions noScope = DomCreationOption::None;
+ DomCreationOptions withScopeAndScriptExpressions;
+ withScopeAndScriptExpressions.setFlag(DomCreationOption::WithSemanticAnalysis);
+ withScopeAndScriptExpressions.setFlag(DomCreationOption::WithScriptExpressions);
+
+ QTest::addRow("tiger.qml") << baseDir + u"/longQmlFile.qml"_s << noScope;
+ QTest::addRow("tiger.qml-with-scope") << baseDir + u"/longQmlFile.qml"_s << withScope;
+ QTest::addRow("tiger.qml-with-scope-and-scriptexpressions")
+ << baseDir + u"/longQmlFile.qml"_s << withScopeAndScriptExpressions;
+
+ QTest::addRow("deeplyNested.qml") << baseDir + u"/deeplyNested.qml"_s << noScope;
+ QTest::addRow("deeplyNested.qml-with-scope") << baseDir + u"/deeplyNested.qml"_s << withScope;
+ QTest::addRow("deeplyNested.qml-with-scope-and-scriptexpressions")
+ << baseDir + u"/deeplyNested.qml"_s << withScopeAndScriptExpressions;
+}
+
+void tst_qmldomconstruction::domConstructionTime()
+{
+ using namespace QQmlJS::Dom;
+ QFETCH(QString, fileName);
+ QFETCH(DomCreationOptions, withScope);
+
+ const QStringList importPaths = {
+ QLibraryInfo::path(QLibraryInfo::QmlImportsPath),
+ };
+
+ DomItem tFile;
+ QBENCHMARK {
+ auto envPtr = DomEnvironment::create(
+ importPaths,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies, withScope);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, fileName),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+ }
+}
+
+QTEST_MAIN(tst_qmldomconstruction)
+#include "tst_qmldomconstruction.moc"
diff --git a/tests/benchmarks/qml/qproperty/main.cpp b/tests/benchmarks/qml/qproperty/main.cpp
index 5ee6d30585..eb503484d7 100644
--- a/tests/benchmarks/qml/qproperty/main.cpp
+++ b/tests/benchmarks/qml/qproperty/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlEngine>
diff --git a/tests/benchmarks/qml/qproperty/propertytester.h b/tests/benchmarks/qml/qproperty/propertytester.h
index f913395af6..a89d51c286 100644
--- a/tests/benchmarks/qml/qproperty/propertytester.h
+++ b/tests/benchmarks/qml/qproperty/propertytester.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef PROPERTYTESTER_H
#define PROPERTYTESTER_H
diff --git a/tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp b/tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp
index eb605f4895..c5cb54b75e 100644
--- a/tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp
+++ b/tests/benchmarks/qml/qqmlchangeset/tst_qqmlchangeset.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
diff --git a/tests/benchmarks/qml/qqmlcomponent/data/myqmlobject.qml b/tests/benchmarks/qml/qqmlcomponent/data/myqmlobject.qml
index 5451124d1d..c3dd7c5e7d 100644
--- a/tests/benchmarks/qml/qqmlcomponent/data/myqmlobject.qml
+++ b/tests/benchmarks/qml/qqmlcomponent/data/myqmlobject.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 4.6
diff --git a/tests/benchmarks/qml/qqmlcomponent/data/myqmlobject_binding.qml b/tests/benchmarks/qml/qqmlcomponent/data/myqmlobject_binding.qml
index e790acbdfa..f09bd9ede7 100644
--- a/tests/benchmarks/qml/qqmlcomponent/data/myqmlobject_binding.qml
+++ b/tests/benchmarks/qml/qqmlcomponent/data/myqmlobject_binding.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 4.6
diff --git a/tests/benchmarks/qml/qqmlcomponent/data/object.qml b/tests/benchmarks/qml/qqmlcomponent/data/object.qml
index c9b569c990..336dc5cb69 100644
--- a/tests/benchmarks/qml/qqmlcomponent/data/object.qml
+++ b/tests/benchmarks/qml/qqmlcomponent/data/object.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/qqmlcomponent/data/object_id.qml b/tests/benchmarks/qml/qqmlcomponent/data/object_id.qml
index e9a70cda57..7249022e29 100644
--- a/tests/benchmarks/qml/qqmlcomponent/data/object_id.qml
+++ b/tests/benchmarks/qml/qqmlcomponent/data/object_id.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/qqmlcomponent/data/samegame/BoomBlock.qml b/tests/benchmarks/qml/qqmlcomponent/data/samegame/BoomBlock.qml
index a5cb725c2a..1ecdc8b3ab 100644
--- a/tests/benchmarks/qml/qqmlcomponent/data/samegame/BoomBlock.qml
+++ b/tests/benchmarks/qml/qqmlcomponent/data/samegame/BoomBlock.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.2.qml b/tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.2.qml
index 16e4c99558..04160b4491 100644
--- a/tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.2.qml
+++ b/tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.qml b/tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.qml
index f022233749..8791cda5d9 100644
--- a/tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.qml
+++ b/tests/benchmarks/qml/qqmlcomponent/data/synthesized_properties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/qqmlcomponent/testtypes.cpp b/tests/benchmarks/qml/qqmlcomponent/testtypes.cpp
index c66f4ceee0..c63c554df0 100644
--- a/tests/benchmarks/qml/qqmlcomponent/testtypes.cpp
+++ b/tests/benchmarks/qml/qqmlcomponent/testtypes.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testtypes.h"
void registerTypes()
diff --git a/tests/benchmarks/qml/qqmlcomponent/testtypes.h b/tests/benchmarks/qml/qqmlcomponent/testtypes.h
index 1c343fa9bd..f6fa9e3e81 100644
--- a/tests/benchmarks/qml/qqmlcomponent/testtypes.h
+++ b/tests/benchmarks/qml/qqmlcomponent/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
diff --git a/tests/benchmarks/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/benchmarks/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 6be374fcb8..a1e36765d2 100644
--- a/tests/benchmarks/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/benchmarks/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
diff --git a/tests/benchmarks/qml/qqmlmetaproperty/data/object.qml b/tests/benchmarks/qml/qqmlmetaproperty/data/object.qml
index dc64192e4c..e1603ceffc 100644
--- a/tests/benchmarks/qml/qqmlmetaproperty/data/object.qml
+++ b/tests/benchmarks/qml/qqmlmetaproperty/data/object.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/qqmlmetaproperty/data/synthesized_object.qml b/tests/benchmarks/qml/qqmlmetaproperty/data/synthesized_object.qml
index 9690707133..273c06c706 100644
--- a/tests/benchmarks/qml/qqmlmetaproperty/data/synthesized_object.qml
+++ b/tests/benchmarks/qml/qqmlmetaproperty/data/synthesized_object.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/qqmlmetaproperty/tst_qqmlmetaproperty.cpp b/tests/benchmarks/qml/qqmlmetaproperty/tst_qqmlmetaproperty.cpp
index 4014f3f02e..cb53582428 100644
--- a/tests/benchmarks/qml/qqmlmetaproperty/tst_qqmlmetaproperty.cpp
+++ b/tests/benchmarks/qml/qqmlmetaproperty/tst_qqmlmetaproperty.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
diff --git a/tests/benchmarks/qml/qquickwindow/tst_qquickwindow.cpp b/tests/benchmarks/qml/qquickwindow/tst_qquickwindow.cpp
index 4cf5d4dfa0..edd662d27a 100644
--- a/tests/benchmarks/qml/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/benchmarks/qml/qquickwindow/tst_qquickwindow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 - 2012 Research In Motion
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuick/QQuickWindow>
#include <QtQuick/private/qquickrectangle_p.h>
diff --git a/tests/benchmarks/qml/script/data/CustomObject.qml b/tests/benchmarks/qml/script/data/CustomObject.qml
index 98b79d5ea0..3fd8467c87 100644
--- a/tests/benchmarks/qml/script/data/CustomObject.qml
+++ b/tests/benchmarks/qml/script/data/CustomObject.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/script/data/block.qml b/tests/benchmarks/qml/script/data/block.qml
index 465d3a254e..4949531d40 100644
--- a/tests/benchmarks/qml/script/data/block.qml
+++ b/tests/benchmarks/qml/script/data/block.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/script/data/enums.qml b/tests/benchmarks/qml/script/data/enums.qml
index 7e88fe4c9e..c11cc23264 100644
--- a/tests/benchmarks/qml/script/data/enums.qml
+++ b/tests/benchmarks/qml/script/data/enums.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/benchmarks/qml/script/data/global.js b/tests/benchmarks/qml/script/data/global.js
index 8dca9d74c4..0d4c834c10 100644
--- a/tests/benchmarks/qml/script/data/global.js
+++ b/tests/benchmarks/qml/script/data/global.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
var incVar = 1;
var total;
diff --git a/tests/benchmarks/qml/script/data/global_prop.qml b/tests/benchmarks/qml/script/data/global_prop.qml
index 8edd75e24b..80d4220e12 100644
--- a/tests/benchmarks/qml/script/data/global_prop.qml
+++ b/tests/benchmarks/qml/script/data/global_prop.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import "global.js" as Program
diff --git a/tests/benchmarks/qml/script/data/namespacedEnums.qml b/tests/benchmarks/qml/script/data/namespacedEnums.qml
index 9e16e11ab6..2b1726fccd 100644
--- a/tests/benchmarks/qml/script/data/namespacedEnums.qml
+++ b/tests/benchmarks/qml/script/data/namespacedEnums.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0 as QtQuick
diff --git a/tests/benchmarks/qml/script/data/scriptCall.qml b/tests/benchmarks/qml/script/data/scriptCall.qml
index 745e4bdb27..ea70269897 100644
--- a/tests/benchmarks/qml/script/data/scriptCall.qml
+++ b/tests/benchmarks/qml/script/data/scriptCall.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import "script.js" as Script
diff --git a/tests/benchmarks/qml/script/data/signal_args.qml b/tests/benchmarks/qml/script/data/signal_args.qml
index 90a774fcec..14359200bf 100644
--- a/tests/benchmarks/qml/script/data/signal_args.qml
+++ b/tests/benchmarks/qml/script/data/signal_args.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/script/data/signal_heavyArgsAccess.qml b/tests/benchmarks/qml/script/data/signal_heavyArgsAccess.qml
index 6e976d7c0f..2658cc7c24 100644
--- a/tests/benchmarks/qml/script/data/signal_heavyArgsAccess.qml
+++ b/tests/benchmarks/qml/script/data/signal_heavyArgsAccess.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/script/data/signal_heavyIdAccess.qml b/tests/benchmarks/qml/script/data/signal_heavyIdAccess.qml
index 2f878bcc02..8dd5c623f0 100644
--- a/tests/benchmarks/qml/script/data/signal_heavyIdAccess.qml
+++ b/tests/benchmarks/qml/script/data/signal_heavyIdAccess.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/script/data/signal_qml.qml b/tests/benchmarks/qml/script/data/signal_qml.qml
index bd3db6e0e4..c5ae42ed76 100644
--- a/tests/benchmarks/qml/script/data/signal_qml.qml
+++ b/tests/benchmarks/qml/script/data/signal_qml.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/script/data/signal_unconnected.qml b/tests/benchmarks/qml/script/data/signal_unconnected.qml
index f6de983071..5f05fae06a 100644
--- a/tests/benchmarks/qml/script/data/signal_unconnected.qml
+++ b/tests/benchmarks/qml/script/data/signal_unconnected.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/script/data/signal_unusedArgs.qml b/tests/benchmarks/qml/script/data/signal_unusedArgs.qml
index c5d8aeed6e..08293d0d8d 100644
--- a/tests/benchmarks/qml/script/data/signal_unusedArgs.qml
+++ b/tests/benchmarks/qml/script/data/signal_unusedArgs.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/script/data/slot_complex.qml b/tests/benchmarks/qml/script/data/slot_complex.qml
index fd1a2f1394..148789e36f 100644
--- a/tests/benchmarks/qml/script/data/slot_complex.qml
+++ b/tests/benchmarks/qml/script/data/slot_complex.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/script/data/slot_complex_js.qml b/tests/benchmarks/qml/script/data/slot_complex_js.qml
index 48ea207822..a9dd5abf30 100644
--- a/tests/benchmarks/qml/script/data/slot_complex_js.qml
+++ b/tests/benchmarks/qml/script/data/slot_complex_js.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
import "slot_complex_js.js" as Logic
diff --git a/tests/benchmarks/qml/script/data/slot_simple.qml b/tests/benchmarks/qml/script/data/slot_simple.qml
index 98a3482325..0e0f56c051 100644
--- a/tests/benchmarks/qml/script/data/slot_simple.qml
+++ b/tests/benchmarks/qml/script/data/slot_simple.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
diff --git a/tests/benchmarks/qml/script/data/slot_simple_js.qml b/tests/benchmarks/qml/script/data/slot_simple_js.qml
index 223703232c..e54cd4e0f9 100644
--- a/tests/benchmarks/qml/script/data/slot_simple_js.qml
+++ b/tests/benchmarks/qml/script/data/slot_simple_js.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 1.0
import "slot_simple_js.js" as Logic
diff --git a/tests/benchmarks/qml/script/tst_script.cpp b/tests/benchmarks/qml/script/tst_script.cpp
index e0713ec277..e8635cf3b6 100644
--- a/tests/benchmarks/qml/script/tst_script.cpp
+++ b/tests/benchmarks/qml/script/tst_script.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
diff --git a/tests/benchmarks/qml/typeimports/data/QmlTestType1.qml b/tests/benchmarks/qml/typeimports/data/QmlTestType1.qml
index 3fcea7082b..ffb0805848 100644
--- a/tests/benchmarks/qml/typeimports/data/QmlTestType1.qml
+++ b/tests/benchmarks/qml/typeimports/data/QmlTestType1.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 2.0
TestType1 { }
diff --git a/tests/benchmarks/qml/typeimports/data/QmlTestType2.qml b/tests/benchmarks/qml/typeimports/data/QmlTestType2.qml
index fa3b6706ed..578d43109b 100644
--- a/tests/benchmarks/qml/typeimports/data/QmlTestType2.qml
+++ b/tests/benchmarks/qml/typeimports/data/QmlTestType2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 2.0
TestType2 { }
diff --git a/tests/benchmarks/qml/typeimports/data/QmlTestType3.qml b/tests/benchmarks/qml/typeimports/data/QmlTestType3.qml
index 02c22ea904..d8f4d1b640 100644
--- a/tests/benchmarks/qml/typeimports/data/QmlTestType3.qml
+++ b/tests/benchmarks/qml/typeimports/data/QmlTestType3.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 2.0
TestType3 { }
diff --git a/tests/benchmarks/qml/typeimports/data/QmlTestType4.qml b/tests/benchmarks/qml/typeimports/data/QmlTestType4.qml
index 342d110697..2fcd1931a8 100644
--- a/tests/benchmarks/qml/typeimports/data/QmlTestType4.qml
+++ b/tests/benchmarks/qml/typeimports/data/QmlTestType4.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 2.0
TestType4 { }
diff --git a/tests/benchmarks/qml/typeimports/data/cpp.qml b/tests/benchmarks/qml/typeimports/data/cpp.qml
index ae6f3aeb4d..38e07c619b 100644
--- a/tests/benchmarks/qml/typeimports/data/cpp.qml
+++ b/tests/benchmarks/qml/typeimports/data/cpp.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import Qt.test 2.0
diff --git a/tests/benchmarks/qml/typeimports/data/qml.qml b/tests/benchmarks/qml/typeimports/data/qml.qml
index ef57627e42..dab3bf6fc1 100644
--- a/tests/benchmarks/qml/typeimports/data/qml.qml
+++ b/tests/benchmarks/qml/typeimports/data/qml.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
QmlTestType1 {
QmlTestType1 { } QmlTestType2 { } QmlTestType3 { } QmlTestType4 { }
diff --git a/tests/benchmarks/qml/typeimports/tst_typeimports.cpp b/tests/benchmarks/qml/typeimports/tst_typeimports.cpp
index ee67ff40ff..c749911329 100644
--- a/tests/benchmarks/qml/typeimports/tst_typeimports.cpp
+++ b/tests/benchmarks/qml/typeimports/tst_typeimports.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QQmlEngine>
diff --git a/tests/benchmarks/quick/CMakeLists.txt b/tests/benchmarks/quick/CMakeLists.txt
index 48edce2029..b9d0cd381f 100644
--- a/tests/benchmarks/quick/CMakeLists.txt
+++ b/tests/benchmarks/quick/CMakeLists.txt
@@ -5,3 +5,4 @@
add_subdirectory(events)
add_subdirectory(colorresolving)
+add_subdirectory(curverenderer)
diff --git a/tests/benchmarks/quick/colorresolving/CMakeLists.txt b/tests/benchmarks/quick/colorresolving/CMakeLists.txt
index 572d02767a..082897799c 100644
--- a/tests/benchmarks/quick/colorresolving/CMakeLists.txt
+++ b/tests/benchmarks/quick/colorresolving/CMakeLists.txt
@@ -7,25 +7,20 @@
## tst_colorresolving Test:
#####################################################################
-# Collect test data
-file(GLOB_RECURSE test_data_glob
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/data/tst_*)
-list(APPEND test_data ${test_data_glob})
-
-qt_internal_add_test(tst_colorresolving
- GUI
- QMLTEST
+qt_internal_add_benchmark(tst_colorresolving
SOURCES
tst_colorresolving.cpp
LIBRARIES
Qt::Gui
- TESTDATA ${test_data}
+ Qt::QuickTest
)
-#### Keys ignored in scope 1:.:.:colorresolving.pro:<TRUE>:
-# OTHER_FILES = "$$PWD/data/*.qml"
-# TEMPLATE = "app"
+qt_internal_extend_target(tst_colorresolving CONDITION NOT ANDROID AND NOT IOS
+DEFINES
+ QUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
-## Scopes:
-#####################################################################
+qt_internal_extend_target(tst_colorresolving CONDITION ANDROID OR IOS
+DEFINES
+ QUICK_TEST_SOURCE_DIR=":/data"
+)
diff --git a/tests/benchmarks/quick/colorresolving/data/tst_colorresolving.qml b/tests/benchmarks/quick/colorresolving/data/tst_colorresolving.qml
index 6f7ad32f8d..5b16eda8e9 100644
--- a/tests/benchmarks/quick/colorresolving/data/tst_colorresolving.qml
+++ b/tests/benchmarks/quick/colorresolving/data/tst_colorresolving.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtTest 1.2
import QtQuick 6.0
diff --git a/tests/benchmarks/quick/colorresolving/tst_colorresolving.cpp b/tests/benchmarks/quick/colorresolving/tst_colorresolving.cpp
index fc19ede502..83b4e93079 100644
--- a/tests/benchmarks/quick/colorresolving/tst_colorresolving.cpp
+++ b/tests/benchmarks/quick/colorresolving/tst_colorresolving.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(tst_colorresolving)
diff --git a/tests/benchmarks/quick/curverenderer/CMakeLists.txt b/tests/benchmarks/quick/curverenderer/CMakeLists.txt
new file mode 100644
index 0000000000..c690bed7cc
--- /dev/null
+++ b/tests/benchmarks/quick/curverenderer/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_benchmark(tst_bench_curverenderer
+ SOURCES
+ tst_bench_curverenderer.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Test
+ Qt::QuickShapesPrivate
+)
diff --git a/tests/benchmarks/quick/curverenderer/tst_bench_curverenderer.cpp b/tests/benchmarks/quick/curverenderer/tst_bench_curverenderer.cpp
new file mode 100644
index 0000000000..4f891e96b2
--- /dev/null
+++ b/tests/benchmarks/quick/curverenderer/tst_bench_curverenderer.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <qtest.h>
+#include <QPainterPath>
+#include <QSGNode>
+#include <private/qquickshapecurverenderer_p.h>
+
+class tst_CurveRenderer : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_CurveRenderer();
+
+private slots:
+ void initTestCase_data();
+
+ void render_data();
+ void render();
+};
+
+tst_CurveRenderer::tst_CurveRenderer()
+{
+}
+
+void tst_CurveRenderer::initTestCase_data()
+{
+ QTest::addColumn<bool>("hasFill");
+ QTest::addColumn<int>("strokeWidth");
+ // tbd: dashed, gradient fill etc.
+
+ QTest::newRow("onlyfill") << true << 0;
+ QTest::newRow("onlystroke") << false << 10;
+ QTest::newRow("strokeandfill") << true << 10;
+}
+
+void tst_CurveRenderer::render_data()
+{
+ QTest::addColumn<QPainterPath>("path");
+
+ {
+ QPainterPath path;
+ path.moveTo(100, 400);
+ path.lineTo(200, 400);
+ path.quadTo(220, 500, 700, 700);
+ path.cubicTo(600, 600, 800, 200, 200, 50);
+ path.lineTo(50, 750);
+ path.cubicTo(600, 700, 300, 200, 750, 50);
+ path.cubicTo(800, 200, 300, 800, 300, 400);
+ QTest::newRow("figure1") << path;
+ }
+}
+
+void tst_CurveRenderer::render()
+{
+ QFETCH_GLOBAL(bool, hasFill);
+ QFETCH_GLOBAL(int, strokeWidth);
+ QFETCH(QPainterPath, path);
+
+ QSGNode dummyNode;
+ QQuickShapeCurveRenderer renderer(nullptr);
+ renderer.setRootNode(&dummyNode);
+ renderer.beginSync(1, nullptr); // Just sets the number of path items to 1; needs no endsync
+ renderer.setFillColor(0, hasFill ? Qt::yellow : Qt::transparent);
+ renderer.setStrokeColor(0, Qt::black);
+ renderer.setStrokeWidth(0, strokeWidth);
+
+ QBENCHMARK {
+ renderer.beginSync(1, nullptr);
+ renderer.setPath(0, path);
+ renderer.endSync(false);
+ renderer.updateNode();
+ }
+}
+
+QTEST_MAIN(tst_CurveRenderer)
+#include "tst_bench_curverenderer.moc"
diff --git a/tests/benchmarks/quick/events/data/mouseevent.qml b/tests/benchmarks/quick/events/data/mouseevent.qml
index 1a08a453b8..216aa48a65 100644
--- a/tests/benchmarks/quick/events/data/mouseevent.qml
+++ b/tests/benchmarks/quick/events/data/mouseevent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
Item {
diff --git a/tests/benchmarks/quick/events/data/touchevent.qml b/tests/benchmarks/quick/events/data/touchevent.qml
index cc0a8e6680..e10271637a 100644
--- a/tests/benchmarks/quick/events/data/touchevent.qml
+++ b/tests/benchmarks/quick/events/data/touchevent.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
Item {
diff --git a/tests/benchmarks/quick/events/tst_events.cpp b/tests/benchmarks/quick/events/tst_events.cpp
index 27b31d4a70..c8899f7848 100644
--- a/tests/benchmarks/quick/events/tst_events.cpp
+++ b/tests/benchmarks/quick/events/tst_events.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
#include <QtQuick>
diff --git a/tests/benchmarks/quickcontrols/creationtime/tst_creationtime.cpp b/tests/benchmarks/quickcontrols/creationtime/tst_creationtime.cpp
index 82259269d9..8f525ea766 100644
--- a/tests/benchmarks/quickcontrols/creationtime/tst_creationtime.cpp
+++ b/tests/benchmarks/quickcontrols/creationtime/tst_creationtime.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qscopedpointer.h>
#include <QtTest>
diff --git a/tests/benchmarks/quickcontrols/objectcount/tst_objectcount.cpp b/tests/benchmarks/quickcontrols/objectcount/tst_objectcount.cpp
index e1d37eec8f..f4119f0737 100644
--- a/tests/benchmarks/quickcontrols/objectcount/tst_objectcount.cpp
+++ b/tests/benchmarks/quickcontrols/objectcount/tst_objectcount.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtQuick>
diff --git a/tests/libfuzzer/qml/jsapi/evaluate/main.cpp b/tests/libfuzzer/qml/jsapi/evaluate/main.cpp
index f1e555d58a..4b75f76bf2 100644
--- a/tests/libfuzzer/qml/jsapi/evaluate/main.cpp
+++ b/tests/libfuzzer/qml/jsapi/evaluate/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QJSEngine>
diff --git a/tests/libfuzzer/qml/qml/qqmlcomponent/create/main.cpp b/tests/libfuzzer/qml/qml/qqmlcomponent/create/main.cpp
index fc1e040686..0f895a083c 100644
--- a/tests/libfuzzer/qml/qml/qqmlcomponent/create/main.cpp
+++ b/tests/libfuzzer/qml/qml/qqmlcomponent/create/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QQmlComponent>
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt
index b41a2b3182..95845fea8e 100644
--- a/tests/manual/CMakeLists.txt
+++ b/tests/manual/CMakeLists.txt
@@ -8,12 +8,31 @@ add_subdirectory(mousearea)
add_subdirectory(nodetypes_ng)
# add_subdirectory(qmltypememory) # TODO: C++ API changed
# add_subdirectory(quickwidgetviewer) # TODO: C++ API changed
+add_subdirectory(righttoleft)
add_subdirectory(scalablepath)
add_subdirectory(shapestest)
add_subdirectory(tableview)
add_subdirectory(text)
add_subdirectory(touch)
+add_subdirectory(threading)
# add_subdirectory(v4) # TODO: port if needed
add_subdirectory(quickcontrols)
add_subdirectory(quickdialogs)
add_subdirectory(frameanimation)
+if(QT_FEATURE_qml_network AND TARGET Qt::Quick)
+ add_subdirectory(networkaccessmanagerfactory)
+endif()
+add_subdirectory(qmlextensionplugins)
+add_subdirectory(fontfeatures)
+add_subdirectory(wasm)
+add_subdirectory(qmltest)
+add_subdirectory(window)
+add_subdirectory(itemparticle)
+add_subdirectory(delegatechooser)
+add_subdirectory(maskedmousearea)
+add_subdirectory(windowembedding)
+if(TARGET Qt::QuickVectorImage)
+ add_subdirectory(quickvectorimage)
+ add_subdirectory(svg)
+endif()
+add_subdirectory(e2e)
diff --git a/tests/manual/accessibility/animation.qml b/tests/manual/accessibility/animation.qml
index c4c7d6f024..75a54f9041 100644
--- a/tests/manual/accessibility/animation.qml
+++ b/tests/manual/accessibility/animation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/accessibility/behavior.qml b/tests/manual/accessibility/behavior.qml
index be3ea0a24e..ca003593fd 100644
--- a/tests/manual/accessibility/behavior.qml
+++ b/tests/manual/accessibility/behavior.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/accessibility/flickable.qml b/tests/manual/accessibility/flickable.qml
index 053392b279..8464be6cbb 100644
--- a/tests/manual/accessibility/flickable.qml
+++ b/tests/manual/accessibility/flickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/accessibility/hittest.qml b/tests/manual/accessibility/hittest.qml
index 270b74e494..fe1553f95b 100644
--- a/tests/manual/accessibility/hittest.qml
+++ b/tests/manual/accessibility/hittest.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/accessibility/numberanimation.qml b/tests/manual/accessibility/numberanimation.qml
index 0759ce9aac..fa6947e30c 100644
--- a/tests/manual/accessibility/numberanimation.qml
+++ b/tests/manual/accessibility/numberanimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/accessibility/textandbuttons.qml b/tests/manual/accessibility/textandbuttons.qml
index 54995f0b56..1f9db1ff93 100644
--- a/tests/manual/accessibility/textandbuttons.qml
+++ b/tests/manual/accessibility/textandbuttons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/accessibility/transition.qml b/tests/manual/accessibility/transition.qml
index a3cf00dfd1..70575ba70e 100644
--- a/tests/manual/accessibility/transition.qml
+++ b/tests/manual/accessibility/transition.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/cursorChange/main.qml b/tests/manual/cursorChange/main.qml
index 0c4e581e6e..dd5f41c09f 100644
--- a/tests/manual/cursorChange/main.qml
+++ b/tests/manual/cursorChange/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.0
diff --git a/tests/manual/delegatechooser/CMakeLists.txt b/tests/manual/delegatechooser/CMakeLists.txt
new file mode 100644
index 0000000000..0954fda17a
--- /dev/null
+++ b/tests/manual/delegatechooser/CMakeLists.txt
@@ -0,0 +1,42 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(delegatechooser LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/delegatechooser")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+add_subdirectory("../shared" "shared")
+
+qt_add_executable(delegatechooserexample WIN32 MACOSX_BUNDLE main.cpp)
+
+target_link_libraries(delegatechooserexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+add_dependencies(delegatechooserexample delegatechooser_shared)
+
+qt_add_qml_module(delegatechooserexample
+ URI delegatechooser
+ QML_FILES
+ "delegatechooser.qml"
+)
+
+install(TARGETS delegatechooserexample
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+
+bundle_shared(delegatechooserexample)
diff --git a/examples/quick/delegatechooser/delegatechooser.pro b/tests/manual/delegatechooser/delegatechooser.pro
index f06864ba89..f06864ba89 100644
--- a/examples/quick/delegatechooser/delegatechooser.pro
+++ b/tests/manual/delegatechooser/delegatechooser.pro
diff --git a/tests/manual/delegatechooser/delegatechooser.qml b/tests/manual/delegatechooser/delegatechooser.qml
new file mode 100644
index 0000000000..24af646ef9
--- /dev/null
+++ b/tests/manual/delegatechooser/delegatechooser.qml
@@ -0,0 +1,99 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQml.Models
+import QtQuick.Layouts
+import Qt.labs.qmlmodels
+import shared
+
+Rectangle {
+ visible: true
+ width: 640
+ height: 640
+
+ ListModel {
+ id: listModel
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ }
+
+ ListModel {
+ id: listModel2
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "lightsteelblue" }
+ ListElement { dataType: "rect"; color: "fuchsia" }
+ ListElement { dataType: "rect"; color: "lime" }
+ }
+
+ DelegateChooser {
+ id: fancyDelegate
+ role: "dataType"
+ DelegateChoice {
+ roleValue: "rect"
+ delegate: DelegateChooser {
+ DelegateChoice {
+ row: 0
+ Rectangle {
+ width: parent.width
+ height: 50
+ color: "red"
+ border.color: "black"
+ border.width: 1
+ }
+ }
+ DelegateChoice {
+ Rectangle {
+ width: parent.width
+ height: 50
+ color: model.color
+ border.color: "black"
+ border.width: 1
+ }
+ }
+ }
+ }
+ DelegateChoice {
+ roleValue: "image"
+ delegate: Image {
+ width: parent.width
+ height: 100
+ source: Images.qtLogo
+ fillMode: Image.PreserveAspectFit
+ }
+ }
+ }
+
+ Item {
+ anchors.fill: parent
+ id: ite
+ RowLayout {
+ ListView {
+ Layout.preferredHeight: ite.height
+ Layout.preferredWidth: ite.width * 0.5
+ model: listModel
+ delegate: fancyDelegate
+ }
+ ListView {
+ Layout.preferredHeight: ite.height
+ Layout.preferredWidth: ite.width * 0.5
+ model: listModel2
+ delegate: fancyDelegate
+ }
+ }
+ }
+}
diff --git a/tests/manual/delegatechooser/main.cpp b/tests/manual/delegatechooser/main.cpp
new file mode 100644
index 0000000000..650434c1c0
--- /dev/null
+++ b/tests/manual/delegatechooser/main.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "../shared/shared.h"
+DECLARATIVE_EXAMPLE_MAIN(delegatechooser/delegatechooser)
diff --git a/examples/quick/delegatechooser/qml.qrc b/tests/manual/delegatechooser/qml.qrc
index 6128befab3..6128befab3 100644
--- a/examples/quick/delegatechooser/qml.qrc
+++ b/tests/manual/delegatechooser/qml.qrc
diff --git a/examples/quick/views/delegatemodel/delegatemodel.qmlproject b/tests/manual/delegatemodel/delegatemodel.qmlproject
index e78ddcb187..e78ddcb187 100644
--- a/examples/quick/views/delegatemodel/delegatemodel.qmlproject
+++ b/tests/manual/delegatemodel/delegatemodel.qmlproject
diff --git a/tests/manual/delegatemodel/slideshow.qml b/tests/manual/delegatemodel/slideshow.qml
new file mode 100644
index 0000000000..29591e06fe
--- /dev/null
+++ b/tests/manual/delegatemodel/slideshow.qml
@@ -0,0 +1,158 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQml.Models
+import shared as Shared
+
+pragma ComponentBehavior: Bound
+
+Rectangle {
+ id: root
+
+ property Item displayItem: null
+
+ width: 300
+ height: 400
+
+ color: "black"
+
+ Shared.FlickrRssModel {
+ id: flickrModel
+ tags: "fjords,mountains"
+ }
+ DelegateModel {
+ id: visualModel
+
+ model: flickrModel
+ delegate: Item {
+ id: delegateItem
+
+ width: 76
+ height: 76
+
+ required property string thumbnail
+
+ Rectangle {
+ id: image
+ x: 0
+ y: 0
+ width: 76
+ height: 76
+ border.width: 1
+ border.color: "white"
+ color: "black"
+
+ Image {
+ anchors.fill: parent
+ anchors.leftMargin: 1
+ anchors.topMargin: 1
+
+ source: delegateItem.thumbnail
+ fillMode: Image.PreserveAspectFit
+ }
+
+ MouseArea {
+ id: clickArea
+ anchors.fill: parent
+
+ onClicked: root.displayItem = root.displayItem !== delegateItem ? delegateItem : null
+ }
+
+ states: [
+ State {
+ when: root.displayItem === delegateItem
+ name: "inDisplay";
+ ParentChange {
+ target: image
+ parent: imageContainer
+ x: 75
+ y: 75
+ width: 150
+ height: 150
+ }
+ PropertyChanges {
+ image.z: 2
+ delegateItem.DelegateModel.inItems: false
+ }
+ },
+ State {
+ when: root.displayItem !== delegateItem
+ name: "inList";
+ ParentChange {
+ target: image
+ parent: delegateItem
+ x: 2
+ y: 2
+ width: 75
+ height: 75
+ }
+ PropertyChanges {
+ image.z: 1
+ delegateItem.DelegateModel.inItems: true
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "inList"
+ SequentialAnimation {
+ PropertyAction {
+ target: delegateItem
+ property: "DelegateModel.inPersistedItems"
+ value: true
+ }
+ ParentAnimation {
+ target: image;
+ via: root
+ NumberAnimation {
+ target: image
+ properties: "x,y,width,height"
+ duration: 1000
+ }
+ }
+ }
+ }, Transition {
+ from: "inDisplay"
+ SequentialAnimation {
+ ParentAnimation {
+ target: image
+ NumberAnimation {
+ target: image
+ properties: "x,y,width,height"
+ duration: 1000
+ }
+ }
+ PropertyAction {
+ target: delegateItem
+ property: "DelegateModel.inPersistedItems"
+ value: false
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+
+
+ PathView {
+ id: imagePath
+
+ anchors { left: parent.left; top: imageContainer.bottom; right: parent.right; bottom: parent.bottom }
+ model: visualModel
+
+ pathItemCount: 7
+ path: Path {
+ startX: -50; startY: 0
+ PathQuad { x: 150; y: 50; controlX: 0; controlY: 50 }
+ PathQuad { x: 350; y: 0; controlX: 300; controlY: 50 }
+ }
+ }
+
+ Item {
+ id: imageContainer
+ anchors { fill: parent; bottomMargin: 100 }
+ }
+}
diff --git a/tests/manual/e2e/CMakeLists.txt b/tests/manual/e2e/CMakeLists.txt
new file mode 100644
index 0000000000..1876f5a2c4
--- /dev/null
+++ b/tests/manual/e2e/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(qml)
diff --git a/tests/manual/e2e/qml/CMakeLists.txt b/tests/manual/e2e/qml/CMakeLists.txt
new file mode 100644
index 0000000000..e9f349be5d
--- /dev/null
+++ b/tests/manual/e2e/qml/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(QT_FEATURE_process)
+ add_subdirectory(qmlformat)
+endif()
diff --git a/tests/manual/e2e/qml/qmlformat/CMakeLists.txt b/tests/manual/e2e/qml/qmlformat/CMakeLists.txt
new file mode 100644
index 0000000000..b93b2b6543
--- /dev/null
+++ b/tests/manual/e2e/qml/qmlformat/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## e2e_qmlformat Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmlformat LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(e2e_qmlformat
+ SOURCES
+ e2e_qmlformat.cpp
+ DEFINES
+ SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::TestPrivate
+ Qt::QuickTestUtilsPrivate
+ TIMEOUT 3000
+)
+
+add_dependencies(e2e_qmlformat Qt::qmlformat)
diff --git a/tests/manual/e2e/qml/qmlformat/e2e_qmlformat.cpp b/tests/manual/e2e/qml/qmlformat/e2e_qmlformat.cpp
new file mode 100644
index 0000000000..7c0e7a21a6
--- /dev/null
+++ b/tests/manual/e2e/qml/qmlformat/e2e_qmlformat.cpp
@@ -0,0 +1,309 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QDir>
+#include <QFile>
+#include <QProcess>
+#include <QString>
+#include <QTemporaryDir>
+#include <QtTest/private/qemulationdetector_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomlinewriter_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+using namespace QQmlJS::Dom;
+
+class E2ETestQmlformat : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+
+#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
+ void testExample();
+ void testExample_data();
+ void normalizeExample();
+ void normalizeExample_data();
+#endif
+
+private:
+ //TODO(QTBUG-117849) refactor this helper function
+ QString formatInMemory(const QString &fileToFormat, bool *didSucceed = nullptr,
+ LineWriterOptions options = LineWriterOptions(),
+ WriteOutChecks extraChecks = WriteOutCheck::ReparseCompare,
+ WriteOutChecks largeChecks = WriteOutCheck::None);
+
+ QString m_qmlformatPath;
+ QStringList m_excludedDirs;
+ QStringList m_invalidFiles;
+ QStringList m_ignoreFiles;
+
+ QStringList findFiles(const QDir &);
+ bool isInvalidFile(const QFileInfo &fileName) const;
+ bool isIgnoredFile(const QFileInfo &fileName) const;
+};
+
+void E2ETestQmlformat::initTestCase()
+{
+ // QQmlDataTest::initTestCase();
+ m_qmlformatPath = QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlformat");
+#ifdef Q_OS_WIN
+ m_qmlformatPath += QLatin1String(".exe");
+#endif
+ if (!QFileInfo(m_qmlformatPath).exists()) {
+ QString message = QStringLiteral("qmlformat executable not found (looked for %0)").arg(m_qmlformatPath);
+ QFAIL(qPrintable(message));
+ }
+
+ // Add directories you want excluded here
+
+ // These snippets are not expected to run on their own.
+ m_excludedDirs << "doc/src/snippets/qml/visualdatamodel_rootindex";
+ m_excludedDirs << "doc/src/snippets/qml/qtbinding";
+ m_excludedDirs << "doc/src/snippets/qml/imports";
+ m_excludedDirs << "doc/src/snippets/qtquick1/visualdatamodel_rootindex";
+ m_excludedDirs << "doc/src/snippets/qtquick1/qtbinding";
+ m_excludedDirs << "doc/src/snippets/qtquick1/imports";
+ m_excludedDirs << "tests/manual/v4";
+ m_excludedDirs << "tests/manual/qmllsformatter";
+ m_excludedDirs << "tests/auto/qml/ecmascripttests";
+ m_excludedDirs << "tests/auto/qml/qmllint";
+
+ // Add invalid files (i.e. files with syntax errors)
+ m_invalidFiles << "tests/auto/quick/qquickloader/data/InvalidSourceComponent.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.5.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/property.4.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/empty.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/missingObject.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/insertedSemicolon.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nonexistantProperty.5.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidRoot.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidID.4.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/questionDotEOF.qml";
+ m_invalidFiles << "tests/auto/qml/qquickfolderlistmodel/data/dummy.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
+ m_invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/emptyFile.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/afterDots.qml";
+ m_invalidFiles << "tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/qualifiedModule.qml";
+
+ // Files that get changed:
+ // rewrite of import "bla/bla/.." to import "bla"
+ m_invalidFiles << "tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.4.qml";
+ // block -> object in internal update
+ m_invalidFiles << "tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml";
+ // removal of unsupported indexing of Object declaration
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/hangOnWarning.qml";
+ // removal of duplicated id
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/component.3.qml";
+ // Optional chains are not permitted on the left-hand-side in assignments
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/optionalChaining.LHS.qml";
+ // object literal with = assignements
+ m_invalidFiles << "tests/auto/quickcontrols/controls/data/tst_scrollbar.qml";
+
+ // These files rely on exact formatting
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
+ m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml";
+
+ // These files are too big
+ m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/longQmlFile.qml";
+ m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/deeplyNested.qml";
+}
+
+QStringList E2ETestQmlformat::findFiles(const QDir &d)
+{
+ for (int ii = 0; ii < m_excludedDirs.size(); ++ii) {
+ QString s = m_excludedDirs.at(ii);
+ if (d.absolutePath().endsWith(s))
+ return QStringList();
+ }
+
+ QStringList rv;
+
+ const QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
+ QDir::Files);
+ for (const QString &file: files) {
+ QString absoluteFilePath = d.absoluteFilePath(file);
+ if (!isIgnoredFile(QFileInfo(absoluteFilePath)))
+ rv << absoluteFilePath;
+ }
+
+ const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
+ QDir::NoSymLinks);
+ for (const QString &dir: dirs) {
+ QDir sub = d;
+ sub.cd(dir);
+ rv << findFiles(sub);
+ }
+
+ return rv;
+}
+
+bool E2ETestQmlformat::isInvalidFile(const QFileInfo &fileName) const
+{
+ for (const QString &invalidFile : m_invalidFiles) {
+ if (fileName.absoluteFilePath().endsWith(invalidFile))
+ return true;
+ }
+ return false;
+}
+
+bool E2ETestQmlformat::isIgnoredFile(const QFileInfo &fileName) const
+{
+ for (const QString &file : m_ignoreFiles) {
+ if (fileName.absoluteFilePath().endsWith(file))
+ return true;
+ }
+ return false;
+}
+
+#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
+void E2ETestQmlformat::testExample_data()
+{
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Crashes in QEMU. (timeout)");
+ QTest::addColumn<QString>("file");
+
+ QString examples = QLatin1String(SRCDIR) + "/../../../../../examples/";
+ QString tests = QLatin1String(SRCDIR) + "/../../../../../tests/";
+
+ QStringList files;
+ files << findFiles(QDir(examples));
+ files << findFiles(QDir(tests));
+
+ for (const QString &file : files)
+ QTest::newRow(qPrintable(file)) << file;
+}
+
+void E2ETestQmlformat::normalizeExample_data()
+{
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Crashes in QEMU. (timeout)");
+ QTest::addColumn<QString>("file");
+
+ QString examples = QLatin1String(SRCDIR) + "/../../../../../examples/";
+ QString tests = QLatin1String(SRCDIR) + "/../../../../../tests/";
+
+ // normalizeExample is similar to testExample, so we test it only on nExamples + nTests
+ // files to avoid making too many
+ QStringList files;
+ const int nExamples = 10;
+ int i = 0;
+ for (const auto &f : findFiles(QDir(examples))) {
+ files << f;
+ if (++i == nExamples)
+ break;
+ }
+ const int nTests = 10;
+ i = 0;
+ for (const auto &f : findFiles(QDir(tests))) {
+ files << f;
+ if (++i == nTests)
+ break;
+ }
+
+ for (const QString &file : files)
+ QTest::newRow(qPrintable(file)) << file;
+}
+#endif
+
+#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
+void E2ETestQmlformat::testExample()
+{
+ QFETCH(QString, file);
+ const bool isInvalid = isInvalidFile(QFileInfo(file));
+ bool wasSuccessful;
+ LineWriterOptions opts;
+ opts.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+ QString output = formatInMemory(file, &wasSuccessful, opts);
+
+ if (!isInvalid)
+ QVERIFY(wasSuccessful && !output.isEmpty());
+}
+
+void E2ETestQmlformat::normalizeExample()
+{
+ QFETCH(QString, file);
+ const bool isInvalid = isInvalidFile(QFileInfo(file));
+ bool wasSuccessful;
+ LineWriterOptions opts;
+ opts.attributesSequence = LineWriterOptions::AttributesSequence::Normalize;
+ QString output = formatInMemory(file, &wasSuccessful, opts);
+
+ if (!isInvalid)
+ QVERIFY(wasSuccessful && !output.isEmpty());
+}
+#endif
+
+QString E2ETestQmlformat::formatInMemory(const QString &fileToFormat, bool *didSucceed,
+ LineWriterOptions options, WriteOutChecks extraChecks,
+ WriteOutChecks largeChecks)
+{
+ auto env = DomEnvironment::create(
+ QStringList(), // as we load no dependencies we do not need any paths
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+ DomItem tFile;
+ env->loadFile(FileToLoad::fromFileSystem(env, fileToFormat),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ env->loadPendingDependencies();
+ MutableDomItem myFile = tFile.field(Fields::currentItem);
+
+ bool writtenOut;
+ QString resultStr;
+ if (myFile.field(Fields::isValid).value().toBool()) {
+ WriteOutChecks checks = extraChecks;
+ const qsizetype largeFileSize = 32000;
+ if (tFile.field(Fields::code).value().toString().size() > largeFileSize)
+ checks = largeChecks;
+
+ QTextStream res(&resultStr);
+ LineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"), options);
+ OutWriter ow(lw);
+ ow.indentNextlines = true;
+ DomItem qmlFile = tFile.field(Fields::currentItem);
+ writtenOut = qmlFile.writeOutForFile(ow, checks);
+ lw.eof();
+ res.flush();
+ }
+ if (didSucceed)
+ *didSucceed = writtenOut;
+ return resultStr;
+}
+
+QTEST_MAIN(E2ETestQmlformat)
+#include "e2e_qmlformat.moc"
diff --git a/tests/manual/fontfeatures/CMakeLists.txt b/tests/manual/fontfeatures/CMakeLists.txt
new file mode 100644
index 0000000000..7938d64803
--- /dev/null
+++ b/tests/manual/fontfeatures/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(fontfeatures LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(tst_manual_fontfeatures
+ GUI
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::QuickControls2
+)
+
+# Resources:
+set(qmake_immediate_resource_files
+ "main.qml"
+)
+
+qt_internal_add_resource(tst_manual_fontfeatures "qmake_immediate"
+ PREFIX
+ "/"
+ FILES
+ ${qmake_immediate_resource_files}
+)
diff --git a/tests/manual/fontfeatures/main.cpp b/tests/manual/fontfeatures/main.cpp
new file mode 100644
index 0000000000..6bc4e13e03
--- /dev/null
+++ b/tests/manual/fontfeatures/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qsettings.h>
+#include <QtGui/qguiapplication.h>
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtQuickControls2/qquickstyle.h>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
diff --git a/tests/manual/fontfeatures/main.qml b/tests/manual/fontfeatures/main.qml
new file mode 100644
index 0000000000..77a06a143b
--- /dev/null
+++ b/tests/manual/fontfeatures/main.qml
@@ -0,0 +1,247 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtCore
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ id: window
+ width: 800
+ height: 600
+ title: "Font features"
+ visible: true
+ color: "white"
+
+ function handleFont(font)
+ {
+ var map = font.features
+ for (var i = 0; i < listView.count; ++i) {
+ var item = listView.itemAtIndex(i)
+ if (item !== null) {
+ if (item.checkState === Qt.Checked)
+ map[item.text] = true
+ else if (item.checkState === Qt.Unchecked)
+ map[item.text] = false
+ }
+ }
+
+ font.features = map
+ return font
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ RowLayout {
+ Text {
+ text: "Font:"
+ }
+ Text {
+ text: sampleText.font.family
+ }
+ ToolButton {
+ text: "..."
+ Layout.fillWidth: false
+ onClicked: fontDialog.visible = true
+ }
+ }
+ TextField {
+ id: sampleText
+ text: "This text is fine"
+ font.family: "Calibri"
+ font.pixelSize: 20
+ Layout.fillWidth: false
+ }
+ TextField {
+ id: smcpText
+ text: "Small caps"
+ font: {
+ "family": sampleText.font.family,
+ "pixelSize": sampleText.font.pixelSize,
+ "features": { "smcp": 1 }
+ }
+ Layout.fillWidth: false
+ }
+ TextField {
+ id: noLigaturesOrKerning
+ text: "This text is fine"
+ font.family: sampleText.font.family
+ font.pixelSize: sampleText.font.pixelSize
+ font.features: { "liga": 0, "dlig": 0, "clig": 0, "hlig": 0, "kern": 0 }
+ Layout.fillWidth: false
+ }
+
+ ListView {
+ id: listView
+ height: window.height / 2
+ width: window.width
+ Layout.fillWidth: false
+ Layout.fillHeight: false
+ model: [ "aalt",
+ "abvf",
+ "abvm",
+ "abvs",
+ "afrc",
+ "akhn",
+ "blwf",
+ "blwm",
+ "blws",
+ "calt",
+ "case",
+ "ccmp",
+ "cfar",
+ "chws",
+ "cjct",
+ "clig",
+ "cpct",
+ "cpsp",
+ "cswh",
+ "curs",
+ "cv01",
+ "c2pc",
+ "c2sc",
+ "dist",
+ "dlig",
+ "dnom",
+ "dtls",
+ "expt",
+ "falt",
+ "fin2",
+ "fin3",
+ "fina",
+ "flac",
+ "frac",
+ "fwid",
+ "half",
+ "haln",
+ "halt",
+ "hist",
+ "hkna",
+ "hlig",
+ "hngl",
+ "hojo",
+ "hwid",
+ "init",
+ "isol",
+ "ital",
+ "jalt",
+ "jp78",
+ "jp83",
+ "jp90",
+ "jp04",
+ "kern",
+ "lfbd",
+ "liga",
+ "ljmo",
+ "lnum",
+ "locl",
+ "ltra",
+ "ltrm",
+ "mark",
+ "med2",
+ "medi",
+ "mgrk",
+ "mkmk",
+ "mset",
+ "nalt",
+ "nlck",
+ "nukt",
+ "numr",
+ "onum",
+ "opbd",
+ "ordn",
+ "ornm",
+ "palt",
+ "pcap",
+ "pkna",
+ "pnum",
+ "pref",
+ "pres",
+ "pstf",
+ "psts",
+ "pwid",
+ "qwid",
+ "rand",
+ "rclt",
+ "rkrf",
+ "rlig",
+ "rphf",
+ "rtbd",
+ "rtla",
+ "rtlm",
+ "ruby",
+ "rvrn",
+ "salt",
+ "sinf",
+ "size",
+ "smcp",
+ "smpl",
+ "ss01",
+ "ss02",
+ "ss03",
+ "ss04",
+ "ss05",
+ "ss06",
+ "ss07",
+ "ss08",
+ "ss09",
+ "ss10",
+ "ss11",
+ "ss12",
+ "ss13",
+ "ss14",
+ "ss15",
+ "ss16",
+ "ss17",
+ "ss18",
+ "ss19",
+ "ss20",
+ "ssty",
+ "stch",
+ "subs",
+ "sups",
+ "swsh",
+ "titl",
+ "tjmo",
+ "tnam",
+ "tnum",
+ "trad",
+ "twid",
+ "unic",
+ "valt",
+ "vatu",
+ "vchw",
+ "vert",
+ "vhal",
+ "vjmo",
+ "vkna",
+ "vkrn",
+ "vpal",
+ "vrt2",
+ "vrtr",
+ "zero"
+ ]
+
+ delegate: CheckBox {
+ id: checkBox
+ text: modelData
+ tristate: true
+ checkState: Qt.PartiallyChecked
+ onCheckedChanged: {
+ sampleText.font = handleFont(fontDialog.currentFont)
+ }
+ }
+ }
+ }
+
+ FontDialog {
+ id: fontDialog
+ visible: false
+ title: "Please select a font"
+ onAccepted: {
+ sampleText.font = handleFont(fontDialog.currentFont)
+ }
+ }
+}
diff --git a/tests/manual/frameanimation/main.cpp b/tests/manual/frameanimation/main.cpp
index 55070cdfcc..6f55d52258 100644
--- a/tests/manual/frameanimation/main.cpp
+++ b/tests/manual/frameanimation/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/frameanimation/main.qml b/tests/manual/frameanimation/main.qml
index b3eee97e62..e691b9c5df 100644
--- a/tests/manual/frameanimation/main.qml
+++ b/tests/manual/frameanimation/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/helloswift/CMakeLists.txt b/tests/manual/helloswift/CMakeLists.txt
new file mode 100644
index 0000000000..2549577172
--- /dev/null
+++ b/tests/manual/helloswift/CMakeLists.txt
@@ -0,0 +1,97 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.26)
+project(helloswift LANGUAGES CXX Swift)
+
+# --------------- Qt ----------------
+
+find_package(Qt6 REQUIRED COMPONENTS Core Quick)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/helloswift")
+
+qt_add_executable(helloswift WIN32 MACOSX_BUNDLE
+ main.cpp
+)
+
+target_include_directories(helloswift PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+target_link_libraries(helloswift PRIVATE
+ Qt6::Quick
+)
+
+qt_add_qml_module(helloswift
+ URI helloswift
+ QML_FILES
+ "main.qml"
+)
+
+install(TARGETS helloswift
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+
+# ------------- Swift ---------------
+
+# Verify that we have a new enough compiler
+if("${CMAKE_Swift_COMPILER_VERSION}" VERSION_LESS 5.9)
+ message(FATAL_ERROR "Swift 5.9 required for C++ interoperability")
+endif()
+
+set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
+set(CMAKE_CXX_STANDARD 17)
+
+get_target_property(QT_CORE_INCLUDES Qt6::Core INTERFACE_INCLUDE_DIRECTORIES)
+
+# Swift library
+set(SWIFT_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/hello.swift")
+add_library(hello-swift STATIC ${SWIFT_SOURCES})
+set_target_properties(hello-swift PROPERTIES Swift_MODULE_NAME "HelloSwift")
+target_include_directories(hello-swift PUBLIC
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ "${QT_CORE_INCLUDES}"
+)
+target_compile_options(hello-swift PUBLIC
+ $<$<COMPILE_LANGUAGE:Swift>:-cxx-interoperability-mode=default>
+ $<$<COMPILE_LANGUAGE:Swift>:-Xcc -std=c++17>)
+
+# Swift to C++ bridging header
+set(SWIFT_BRIDGING_HEADER "${CMAKE_CURRENT_BINARY_DIR}/hello-swift.h")
+list(TRANSFORM QT_CORE_INCLUDES PREPEND "-I")
+add_custom_command(
+ COMMAND
+ ${CMAKE_Swift_COMPILER} -frontend -typecheck
+ ${SWIFT_SOURCES}
+ -I ${CMAKE_CURRENT_SOURCE_DIR}
+ ${QT_CORE_INCLUDES}
+ -sdk ${CMAKE_OSX_SYSROOT}
+ -module-name HelloSwift
+ -cxx-interoperability-mode=default
+ -Xcc -std=c++17
+ -emit-clang-header-path "${SWIFT_BRIDGING_HEADER}"
+ OUTPUT
+ "${SWIFT_BRIDGING_HEADER}"
+ DEPENDS
+ ${SWIFT_SOURCES}
+ )
+
+add_custom_target(swift-bridging-header
+ DEPENDS
+ "${SWIFT_BRIDGING_HEADER}"
+)
+
+add_dependencies(hello-swift swift-bridging-header)
+
+# Tie everything together
+
+target_link_libraries(helloswift PRIVATE
+ hello-swift
+)
diff --git a/tests/manual/helloswift/greeter.h b/tests/manual/helloswift/greeter.h
new file mode 100644
index 0000000000..722380051c
--- /dev/null
+++ b/tests/manual/helloswift/greeter.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef GREETER_H
+#define GREETER_H
+
+#include <QtCore/qobject.h>
+#include <string>
+#include <swift/bridging>
+
+namespace HelloSwift {
+class SwiftGreeter;
+class Test;
+}
+
+template <typename SwiftType, typename QtType>
+class SwiftWrapper
+{
+public:
+ template <typename ...Args>
+ SwiftWrapper(Args && ...args)
+ : swiftImpl(new SwiftType(
+ SwiftType::init(
+ static_cast<QtType*>(this),
+ std::forward<Args>(args)...)))
+ {
+ }
+
+protected:
+ std::unique_ptr<SwiftType> swiftImpl;
+};
+
+class Greeter : public QObject, public SwiftWrapper<HelloSwift::SwiftGreeter, Greeter>
+{
+ Q_OBJECT
+public:
+ Q_PRIVATE_PROPERTY(swiftImpl, std::string greeting READ getGreeting NOTIFY greetingChanged)
+ Q_PRIVATE_SLOT(swiftImpl, void updateGreeting())
+ Q_SIGNAL void greetingChanged();
+} SWIFT_UNSAFE_REFERENCE;
+
+
+#endif // GREETER_H
diff --git a/tests/manual/helloswift/hello.swift b/tests/manual/helloswift/hello.swift
new file mode 100644
index 0000000000..99043c328d
--- /dev/null
+++ b/tests/manual/helloswift/hello.swift
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import Greeter
+
+public class SwiftGreeter {
+ private let qtImpl: Greeter
+
+ public init(greeter: Greeter ) {
+ self.qtImpl = greeter;
+ }
+
+ public var greeting: String {
+ let greetings = ["Hello", "Howdy", "Hey", "Hola", "Heisan"]
+ return greetings[Int.random(in: 0..<greetings.count)]
+ }
+
+ public func updateGreeting() {
+ qtImpl.greetingChanged();
+ }
+}
diff --git a/tests/manual/helloswift/main.cpp b/tests/manual/helloswift/main.cpp
new file mode 100644
index 0000000000..264c4cb136
--- /dev/null
+++ b/tests/manual/helloswift/main.cpp
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QStringList>
+
+#include <qqmlengine.h>
+#include <qqmlcontext.h>
+#include <qqml.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
+
+#include "greeter.h"
+#include "hello-swift.h"
+
+int main(int argc, char ** argv)
+{
+ QGuiApplication app(argc, argv);
+
+ QMetaType::registerConverter<std::string, QString>(&QString::fromStdString);
+
+ Greeter greeter;
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.engine()->rootContext()->setContextProperty("greeter", &greeter);
+
+ view.setSource(QUrl("qrc:/qt/qml/helloswift/main.qml"));
+ view.show();
+
+ return app.exec();
+}
+
+#include "moc_greeter.cpp"
diff --git a/tests/manual/helloswift/main.qml b/tests/manual/helloswift/main.qml
new file mode 100644
index 0000000000..3992cde4e4
--- /dev/null
+++ b/tests/manual/helloswift/main.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ id: rectangle
+ width: 500; height: 500
+ color: "lightgray"
+
+ Text {
+ id: text;
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: -50
+ font.pointSize: 40
+ text: greeter.greeting
+ }
+
+ Image {
+ anchors.top: text.bottom
+ anchors.topMargin: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.horizontalCenterOffset: -8
+ source: "https://www.swift.org/assets/images/swift.svg"
+ fillMode: Image.PreserveAspectFit
+ sourceSize.width: 150
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: greeter.updateGreeting();
+ }
+ }
+}
diff --git a/tests/manual/helloswift/module.modulemap b/tests/manual/helloswift/module.modulemap
new file mode 100644
index 0000000000..4cc079abea
--- /dev/null
+++ b/tests/manual/helloswift/module.modulemap
@@ -0,0 +1,3 @@
+module Greeter {
+ header "greeter.h"
+}
diff --git a/tests/manual/highdpi/CMakeLists.txt b/tests/manual/highdpi/CMakeLists.txt
index 7fd28828f1..f4b6dc70e8 100644
--- a/tests/manual/highdpi/CMakeLists.txt
+++ b/tests/manual/highdpi/CMakeLists.txt
@@ -1,16 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from highdpi.pro.
-
-#####################################################################
-## qmlimageproviderplugin Generic Library:
-#####################################################################
-
-qt_internal_add_cmake_library(qmlimageproviderplugin
+qt_internal_add_cmake_library(qmlhighdpiimageproviderplugin
MODULE
- INSTALL_DIRECTORY "${INSTALL_EXAMPLESDIR}/quick/imageprovider/ImageProvider"
- OUTPUT_DIRECTORY "ImageProvider"
+ INSTALL_DIRECTORY "${INSTALL_EXAMPLESDIR}/quick/imageprovider/HighDPIImageProvider"
+ OUTPUT_DIRECTORY "HighDPIImageProvider"
SOURCES
imageprovider.cpp
LIBRARIES
@@ -20,11 +14,4 @@ qt_internal_add_cmake_library(qmlimageproviderplugin
Qt::Quick
)
-#### Keys ignored in scope 1:.:.:highdpi.pro:<TRUE>:
-# INSTALLS = "target" "qml"
-# TEMPLATE = "lib"
-# qml.files = "ImageProvider/qmldir"
-# qml.path = "$$[QT_INSTALL_EXAMPLES]/quick/imageprovider/ImageProvider"
-# target.path = "$$[QT_INSTALL_EXAMPLES]/quick/imageprovider/ImageProvider"
-
-qt_autogen_tools_initial_setup(qmlimageproviderplugin)
+qt_autogen_tools_initial_setup(qmlhighdpiimageproviderplugin)
diff --git a/tests/manual/highdpi/HighDPIImageProvider/qmldir b/tests/manual/highdpi/HighDPIImageProvider/qmldir
new file mode 100644
index 0000000000..a65519510e
--- /dev/null
+++ b/tests/manual/highdpi/HighDPIImageProvider/qmldir
@@ -0,0 +1 @@
+plugin qmlhighdpiimageproviderplugin
diff --git a/tests/manual/highdpi/ImageProvider/qmldir b/tests/manual/highdpi/ImageProvider/qmldir
deleted file mode 100644
index add39d4ee8..0000000000
--- a/tests/manual/highdpi/ImageProvider/qmldir
+++ /dev/null
@@ -1 +0,0 @@
-plugin qmlimageproviderplugin
diff --git a/tests/manual/highdpi/borderimage.qml b/tests/manual/highdpi/borderimage.qml
index 6c292dc5b4..ac6f354925 100644
--- a/tests/manual/highdpi/borderimage.qml
+++ b/tests/manual/highdpi/borderimage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/highdpi/fillmodes.qml b/tests/manual/highdpi/fillmodes.qml
index 090a6caff2..7e12f615ba 100644
--- a/tests/manual/highdpi/fillmodes.qml
+++ b/tests/manual/highdpi/fillmodes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/highdpi/image.qml b/tests/manual/highdpi/image.qml
index b504076533..a5e6626df6 100644
--- a/tests/manual/highdpi/image.qml
+++ b/tests/manual/highdpi/image.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/highdpi/image2.qml b/tests/manual/highdpi/image2.qml
index c308bfebfc..0d49f41bc3 100644
--- a/tests/manual/highdpi/image2.qml
+++ b/tests/manual/highdpi/image2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/highdpi/imageprovider.cpp b/tests/manual/highdpi/imageprovider.cpp
index 6b942a5327..b81b34e36e 100644
--- a/tests/manual/highdpi/imageprovider.cpp
+++ b/tests/manual/highdpi/imageprovider.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qqmlextensionplugin.h>
diff --git a/tests/manual/highdpi/imageprovider.qml b/tests/manual/highdpi/imageprovider.qml
index 348d12b2e1..ceadeaf472 100644
--- a/tests/manual/highdpi/imageprovider.qml
+++ b/tests/manual/highdpi/imageprovider.qml
@@ -1,8 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
-import "ImageProvider"
+import "HighDPIImageProvider"
Rectangle {
width: 400
diff --git a/tests/manual/highdpi/imagesize.qml b/tests/manual/highdpi/imagesize.qml
index e612296d70..66bf3d7991 100644
--- a/tests/manual/highdpi/imagesize.qml
+++ b/tests/manual/highdpi/imagesize.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/highdpi/mirror.qml b/tests/manual/highdpi/mirror.qml
index a121e2f4e7..e416f6248c 100644
--- a/tests/manual/highdpi/mirror.qml
+++ b/tests/manual/highdpi/mirror.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/highdpi/sourcesize.qml b/tests/manual/highdpi/sourcesize.qml
index 302e817b88..f5fdb31e59 100644
--- a/tests/manual/highdpi/sourcesize.qml
+++ b/tests/manual/highdpi/sourcesize.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/highdpi/svg.qml b/tests/manual/highdpi/svg.qml
index 11f61d6348..2bb125444e 100644
--- a/tests/manual/highdpi/svg.qml
+++ b/tests/manual/highdpi/svg.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/httpserver/main.cpp b/tests/manual/httpserver/main.cpp
index 965656607a..81a9ac8c88 100644
--- a/tests/manual/httpserver/main.cpp
+++ b/tests/manual/httpserver/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <testhttpserver.h>
diff --git a/tests/manual/imagehandler/main.qml b/tests/manual/imagehandler/main.qml
index e8ad7e5ffc..1afb8c7786 100644
--- a/tests/manual/imagehandler/main.qml
+++ b/tests/manual/imagehandler/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.8
import QtQuick.Window 2.0
diff --git a/tests/manual/itemparticle/CMakeLists.txt b/tests/manual/itemparticle/CMakeLists.txt
new file mode 100644
index 0000000000..8a22a02783
--- /dev/null
+++ b/tests/manual/itemparticle/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(itemparticle LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/particles/itemparticle")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+add_subdirectory("../shared" "shared")
+
+qt_add_executable(itemparticleexample WIN32 MACOSX_BUNDLE
+ main.cpp
+)
+
+target_link_libraries(itemparticleexample PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+add_dependencies(itemparticleexample itemparticle_shared)
+
+# Resources:
+qt_add_qml_module(itemparticleexample
+ URI itemparticle
+ QML_FILES
+ "itemparticle.qml"
+ "delegates.qml"
+ "script.js"
+ RESOURCES
+ "images/rocket.png"
+)
+
+install(TARGETS itemparticleexample
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+
+bundle_shared(itemparticleexample)
diff --git a/tests/manual/itemparticle/delegates.qml b/tests/manual/itemparticle/delegates.qml
new file mode 100644
index 0000000000..be5d716168
--- /dev/null
+++ b/tests/manual/itemparticle/delegates.qml
@@ -0,0 +1,58 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Particles
+
+Rectangle {
+ id: root;
+ width: 360
+ height: 600
+ color: "black"
+
+ function newPithySaying() {
+ switch (Math.floor(Math.random()*16)) {
+ case 0: return "Hello World";
+ case 1: return "G'day Mate";
+ case 2: return "Code Less";
+ case 3: return "Create More";
+ case 4: return "Deploy Everywhere";
+ case 5: return "Qt Meta-object Language";
+ case 6: return "Qt Magic Language";
+ case 7: return "Fluid UIs";
+ case 8: return "Touchable";
+ case 9: return "How's it going?";
+ case 10: return "Do you like text?";
+ case 11: return "Enjoy!";
+ case 12: return "ERROR: Out of pith";
+ case 13: return "Punctuation Failure";
+ case 14: return "I can go faster";
+ case 15: return "I can go slower";
+ default: return "OMGWTFBBQ";
+ }
+ }
+
+ ParticleSystem {
+ anchors.fill: parent
+ id: syssy
+ MouseArea {
+ anchors.fill: parent
+ onClicked: syssy.running = !syssy.running
+ }
+ Emitter {
+ anchors.centerIn: parent
+ emitRate: 1
+ lifeSpan: 4800
+ lifeSpanVariation: 1600
+ velocity: AngleDirection {angleVariation: 360; magnitude: 40; magnitudeVariation: 20}
+ }
+ ItemParticle {
+ delegate: Text {
+ text: root.newPithySaying();
+ color: "white"
+ font.pixelSize: 18
+ font.bold: true
+ }
+ }
+ }
+}
diff --git a/examples/quick/particles/itemparticle/images/rocket.png b/tests/manual/itemparticle/images/rocket.png
index a171610b03..a171610b03 100644
--- a/examples/quick/particles/itemparticle/images/rocket.png
+++ b/tests/manual/itemparticle/images/rocket.png
Binary files differ
diff --git a/tests/manual/itemparticle/itemparticle.pro b/tests/manual/itemparticle/itemparticle.pro
new file mode 100644
index 0000000000..4228852ae0
--- /dev/null
+++ b/tests/manual/itemparticle/itemparticle.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+
+QT += quick qml
+SOURCES += main.cpp
+RESOURCES += \
+ itemparticle.qrc \
+ ../shared/shared.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/particles/itemparticle
+INSTALLS += target
diff --git a/tests/manual/itemparticle/itemparticle.qml b/tests/manual/itemparticle/itemparticle.qml
new file mode 100644
index 0000000000..68f82cf801
--- /dev/null
+++ b/tests/manual/itemparticle/itemparticle.qml
@@ -0,0 +1,262 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Particles
+import shared
+import "script.js" as Script
+
+Item {
+ id: root
+ width: 640
+ height: 480
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
+ z: -1
+ }
+ Item {
+ id: loading
+ Behavior on opacity {NumberAnimation {}}
+ anchors.fill: parent
+ Text {
+ anchors.centerIn: parent
+ text: "Loading"
+ color: "white"
+ }
+ }
+ ParticleSystem {
+ id: sys;
+ running: true
+ }
+ Emitter {
+ id: emitter
+ system: sys
+ height: parent.height - 132/2
+ x: -132/2
+ y: 132/2
+ velocity: PointDirection { x: 32; xVariation: 8 }
+ emitRate: 0.5
+ lifeSpan: Emitter.InfiniteLife
+ group: "photos"
+ }
+ Age {
+ system: sys
+ x: parent.width + 132/2
+ height: parent.height
+ width: 1000
+ }
+ ImageParticle {
+ system: sys
+ groups: ["fireworks"]
+ source: "qrc:///particleresources/star.png"
+ color: "lightsteelblue"
+ alpha: 0
+ colorVariation: 0
+ z: 1000
+ }
+ ItemParticle {
+ id: mp
+ z: 0
+ system: sys
+ fade: false
+ groups: ["photos"]
+ }
+ Component {
+ id: alertDelegate
+ Rectangle {
+ color: "DarkSlateGray"
+ border.width: 1
+ border.color: "LightSteelBlue"
+ width: 144
+ height: 132
+ antialiasing: true
+ radius: 3
+ NumberAnimation on scale {
+ running: true
+ loops: 1
+ from: 0.2
+ to: 1
+ }
+ Image {
+ source: "images/rocket.png"
+ anchors.centerIn: parent
+ }
+ Text {
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: "A new ship has arrived!"
+ color: "LightSteelBlue"
+ }
+ }
+ }
+ property Item alertItem;
+ function alert() {
+ force.enabled = true;
+ alertItem = alertDelegate.createObject(root);
+ alertItem.x = root.width/2 - alertItem.width/2
+ alertItem.y = root.height/2 - alertItem.height/2
+ spawnFireworks.pulse(200);
+ stopAlert.start();
+ }
+ focus: true
+ Keys.onSpacePressed: alert();
+ Timer {
+ id: stopAlert
+ running: false
+ repeat: false
+ interval: 800
+ onTriggered: {
+ force.enabled = false;
+ mp.take(root.alertItem, true);
+ centerEmitter.burst(1);
+ }
+ }
+ Attractor {
+ id: force
+ system: sys
+ pointX: root.width/2
+ pointY: root.height/2
+ strength: -10000
+ enabled: false
+ anchors.centerIn: parent
+ width: parent.width/2
+ height: parent.height/2
+ groups:["photos"]
+ affectedParameter: Attractor.Position
+ }
+ Emitter {
+ id: centerEmitter
+ velocity: PointDirection { x: 32; xVariation: 8;}
+ emitRate: 0.5
+ lifeSpan: 12000 //TODO: A -1 or something which does 'infinite'? (but need disable fade first)
+ maximumEmitted: 20
+ group: "photos"
+ system: sys
+ anchors.centerIn: parent
+ enabled: false
+
+ //TODO: Zoom in effect
+ }
+ Emitter {
+ id: spawnFireworks
+ group: "fireworks"
+ system: sys
+ maximumEmitted: 400
+ emitRate: 400
+ lifeSpan: 2800
+ x: parent.width/2
+ y: parent.height/2 - 64
+ width: 8
+ height: 8
+ enabled: false
+ size: 32
+ endSize: 8
+ velocity: AngleDirection { magnitude: 160; magnitudeVariation: 120; angleVariation: 90; angle: 270 }
+ acceleration: PointDirection { y: 160 }
+ }
+ Item { x: -1000; y: -1000 //offscreen
+ Repeater {//Load them here, add to system on completed
+ model: flickrModel
+ delegate: theDelegate
+ }
+ }
+ FlickrRssModel {
+ id: flickrModel
+ tags: "particle,particles"
+ }
+ Component {
+ id: theDelegate
+ Image {
+ required property int index
+ required property string title
+ required property string media
+ required property string thumbnail
+
+ id: image
+ antialiasing: true;
+ source: thumbnail
+ cache: true
+ property real depth: Math.random()
+ property real darken: 0.75
+ z: Math.floor(depth * 100)
+ scale: (depth + 1) / 2
+ sourceSize {
+ width: root.width
+ height: root.height
+ }
+ width: 132
+ height: 132
+ fillMode: Image.PreserveAspectFit
+ Rectangle {
+ // Darken based on depth
+ anchors.centerIn: parent
+ width: parent.paintedWidth + 1
+ height: parent.paintedHeight + 1
+ color: "black"
+ opacity: image.darken * (1 - image.depth)
+ antialiasing: true;
+ }
+ Text {
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottomMargin: Math.max(parent.paintedWidth, parent.paintedHeight) - Math.min(parent.width, parent.height)
+ width: parent.paintedWidth - 4
+ horizontalAlignment: Text.AlignHCenter
+ elide: Text.ElideRight
+ text: image.title
+ color: "black"
+ }
+ ItemParticle.onDetached: mp.take(image); // respawns
+ onStatusChanged: if (image.status == Image.Ready) {
+ loading.opacity = 0;
+ mp.take(image);
+ }
+ function manage()
+ {
+ if (state == "selected") {
+ console.log("Taking " + index);
+ mp.freeze(image);
+ } else {
+ console.log("Returning " +index);
+ mp.unfreeze(image);
+ }
+ }
+ TapHandler {
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ onTapped: image.state = (image.state == "" ? "selected" : "")
+ }
+ states: State {
+ name: "selected"
+ ParentChange {
+ target: image
+ parent: root
+ }
+ PropertyChanges {
+ image {
+ source: image.media
+ x: 0
+ y: 0
+ width: root.width
+ height: root.height
+ z: 101
+ opacity: 1
+ rotation: 0
+ darken: 0
+ }
+ }
+ }
+ transitions: Transition {
+ to: "selected"
+ reversible: true
+ SequentialAnimation {
+ ScriptAction { script: image.manage() }
+ ParallelAnimation {
+ ParentAnimation {NumberAnimation { properties: "x,y" }}
+ PropertyAnimation { properties: "width, height, z, rotation, darken"; easing.type: Easing.InOutQuad }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/particles/itemparticle/itemparticle.qrc b/tests/manual/itemparticle/itemparticle.qrc
index d345d2a957..d345d2a957 100644
--- a/examples/quick/particles/itemparticle/itemparticle.qrc
+++ b/tests/manual/itemparticle/itemparticle.qrc
diff --git a/tests/manual/itemparticle/main.cpp b/tests/manual/itemparticle/main.cpp
new file mode 100644
index 0000000000..cc7410f861
--- /dev/null
+++ b/tests/manual/itemparticle/main.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "../shared/shared.h"
+DECLARATIVE_EXAMPLE_MAIN(itemparticle/itemparticle)
diff --git a/examples/quick/particles/itemparticle/script.js b/tests/manual/itemparticle/script.js
index e8ef93a847..e8ef93a847 100644
--- a/examples/quick/particles/itemparticle/script.js
+++ b/tests/manual/itemparticle/script.js
diff --git a/tests/manual/listview/main.cpp b/tests/manual/listview/main.cpp
index 0504db3093..923af90daa 100644
--- a/tests/manual/listview/main.cpp
+++ b/tests/manual/listview/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/listview/main.qml b/tests/manual/listview/main.qml
index 5392375678..0f81eec156 100644
--- a/tests/manual/listview/main.qml
+++ b/tests/manual/listview/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
import QtQuick.Window 2.2
diff --git a/tests/manual/maskedmousearea/CMakeLists.txt b/tests/manual/maskedmousearea/CMakeLists.txt
new file mode 100644
index 0000000000..9274fcdeee
--- /dev/null
+++ b/tests/manual/maskedmousearea/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(maskedmousearea LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick/customitems/maskedmousearea")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
+
+qt_add_executable(maskedmousearea
+ WIN32
+ MACOSX_BUNDLE
+ main.cpp
+ maskedmousearea.cpp maskedmousearea.h
+)
+
+target_link_libraries(maskedmousearea PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+qt_add_qml_module(maskedmousearea
+ URI Example
+ QML_FILES
+ maskedmousearea.qml
+ RESOURCES
+ images/cloud_1.png
+ images/cloud_2.png
+ images/moon.png
+ RESOURCE_PREFIX /customitems/maskedmousearea
+ NO_RESOURCE_TARGET_PATH
+)
+
+install(TARGETS maskedmousearea
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/quick/customitems/maskedmousearea/images/cloud_1.png b/tests/manual/maskedmousearea/images/cloud_1.png
index 9beb7e8442..9beb7e8442 100644
--- a/examples/quick/customitems/maskedmousearea/images/cloud_1.png
+++ b/tests/manual/maskedmousearea/images/cloud_1.png
Binary files differ
diff --git a/examples/quick/customitems/maskedmousearea/images/cloud_2.png b/tests/manual/maskedmousearea/images/cloud_2.png
index 981bbd2630..981bbd2630 100644
--- a/examples/quick/customitems/maskedmousearea/images/cloud_2.png
+++ b/tests/manual/maskedmousearea/images/cloud_2.png
Binary files differ
diff --git a/examples/quick/customitems/maskedmousearea/images/moon.png b/tests/manual/maskedmousearea/images/moon.png
index 0a8037dd85..0a8037dd85 100644
--- a/examples/quick/customitems/maskedmousearea/images/moon.png
+++ b/tests/manual/maskedmousearea/images/moon.png
Binary files differ
diff --git a/tests/manual/maskedmousearea/main.cpp b/tests/manual/maskedmousearea/main.cpp
new file mode 100644
index 0000000000..ae681ccff8
--- /dev/null
+++ b/tests/manual/maskedmousearea/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQuickView>
+
+int main(int argc, char* argv[])
+{
+ QGuiApplication app(argc,argv);
+ QQuickView view;
+
+ view.setSource(QUrl("qrc:///customitems/maskedmousearea/maskedmousearea.qml"));
+ view.show();
+ return QGuiApplication::exec();
+}
diff --git a/tests/manual/maskedmousearea/maskedmousearea.cpp b/tests/manual/maskedmousearea/maskedmousearea.cpp
new file mode 100644
index 0000000000..0456e8f7b1
--- /dev/null
+++ b/tests/manual/maskedmousearea/maskedmousearea.cpp
@@ -0,0 +1,104 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "maskedmousearea.h"
+
+#include <QStyleHints>
+#include <QGuiApplication>
+#include <qqmlfile.h>
+
+MaskedMouseArea::MaskedMouseArea(QQuickItem *parent)
+ : QQuickItem(parent),
+ m_pressed(false),
+ m_alphaThreshold(0.0),
+ m_containsMouse(false)
+{
+ setAcceptHoverEvents(true);
+ setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+void MaskedMouseArea::setPressed(bool pressed)
+{
+ if (m_pressed != pressed) {
+ m_pressed = pressed;
+ emit pressedChanged();
+ }
+}
+
+void MaskedMouseArea::setContainsMouse(bool containsMouse)
+{
+ if (m_containsMouse != containsMouse) {
+ m_containsMouse = containsMouse;
+ emit containsMouseChanged();
+ }
+}
+
+void MaskedMouseArea::setMaskSource(const QUrl &source)
+{
+ if (m_maskSource != source) {
+ m_maskSource = source;
+ m_maskImage = QImage(QQmlFile::urlToLocalFileOrQrc(source));
+ emit maskSourceChanged();
+ }
+}
+
+void MaskedMouseArea::setAlphaThreshold(qreal threshold)
+{
+ if (m_alphaThreshold != threshold) {
+ m_alphaThreshold = threshold;
+ emit alphaThresholdChanged();
+ }
+}
+
+bool MaskedMouseArea::contains(const QPointF &point) const
+{
+ if (!QQuickItem::contains(point) || m_maskImage.isNull())
+ return false;
+
+ QPoint p = point.toPoint();
+
+ if (p.x() < 0 || p.x() >= m_maskImage.width() ||
+ p.y() < 0 || p.y() >= m_maskImage.height())
+ return false;
+
+ qreal r = qBound<int>(0, m_alphaThreshold * 255, 255);
+ return qAlpha(m_maskImage.pixel(p)) > r;
+}
+
+void MaskedMouseArea::mousePressEvent(QMouseEvent *event)
+{
+ setPressed(true);
+ m_pressPoint = event->position().toPoint();
+ emit pressed();
+}
+
+void MaskedMouseArea::mouseReleaseEvent(QMouseEvent *event)
+{
+ setPressed(false);
+ emit released();
+
+ const int threshold = qApp->styleHints()->startDragDistance();
+ const bool isClick = (threshold >= qAbs(event->position().toPoint().x() - m_pressPoint.x()) &&
+ threshold >= qAbs(event->position().toPoint().y() - m_pressPoint.y()));
+
+ if (isClick)
+ emit clicked();
+}
+
+void MaskedMouseArea::mouseUngrabEvent()
+{
+ setPressed(false);
+ emit canceled();
+}
+
+void MaskedMouseArea::hoverEnterEvent(QHoverEvent *event)
+{
+ Q_UNUSED(event);
+ setContainsMouse(true);
+}
+
+void MaskedMouseArea::hoverLeaveEvent(QHoverEvent *event)
+{
+ Q_UNUSED(event);
+ setContainsMouse(false);
+}
diff --git a/tests/manual/maskedmousearea/maskedmousearea.h b/tests/manual/maskedmousearea/maskedmousearea.h
new file mode 100644
index 0000000000..ea4a4c5033
--- /dev/null
+++ b/tests/manual/maskedmousearea/maskedmousearea.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MASKEDMOUSEAREA_H
+#define MASKEDMOUSEAREA_H
+
+#include <QImage>
+#include <QQuickItem>
+
+
+class MaskedMouseArea : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
+ Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged)
+ Q_PROPERTY(QUrl maskSource READ maskSource WRITE setMaskSource NOTIFY maskSourceChanged)
+ Q_PROPERTY(qreal alphaThreshold READ alphaThreshold WRITE setAlphaThreshold NOTIFY alphaThresholdChanged)
+ QML_ELEMENT
+
+public:
+ MaskedMouseArea(QQuickItem *parent = nullptr);
+
+ bool contains(const QPointF &point) const override;
+
+ bool isPressed() const { return m_pressed; }
+ bool containsMouse() const { return m_containsMouse; }
+
+ QUrl maskSource() const { return m_maskSource; }
+ void setMaskSource(const QUrl &source);
+
+ qreal alphaThreshold() const { return m_alphaThreshold; }
+ void setAlphaThreshold(qreal threshold);
+
+signals:
+ void pressed();
+ void released();
+ void clicked();
+ void canceled();
+ void pressedChanged();
+ void maskSourceChanged();
+ void containsMouseChanged();
+ void alphaThresholdChanged();
+
+protected:
+ void setPressed(bool pressed);
+ void setContainsMouse(bool containsMouse);
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void hoverEnterEvent(QHoverEvent *event) override;
+ void hoverLeaveEvent(QHoverEvent *event) override;
+ void mouseUngrabEvent() override;
+
+private:
+ bool m_pressed;
+ QUrl m_maskSource;
+ QImage m_maskImage;
+ QPointF m_pressPoint;
+ qreal m_alphaThreshold;
+ bool m_containsMouse;
+};
+
+#endif
diff --git a/examples/quick/customitems/maskedmousearea/maskedmousearea.pro b/tests/manual/maskedmousearea/maskedmousearea.pro
index c5cedf4e6e..c5cedf4e6e 100644
--- a/examples/quick/customitems/maskedmousearea/maskedmousearea.pro
+++ b/tests/manual/maskedmousearea/maskedmousearea.pro
diff --git a/tests/manual/maskedmousearea/maskedmousearea.qml b/tests/manual/maskedmousearea/maskedmousearea.qml
new file mode 100644
index 0000000000..717b6c2a0f
--- /dev/null
+++ b/tests/manual/maskedmousearea/maskedmousearea.qml
@@ -0,0 +1,95 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import Example
+
+Rectangle {
+ height: 480
+ width: 320
+ color: "black"
+
+ Text {
+ text: qsTr("CLICK AND HOVER")
+ opacity: 0.6
+ color: "white"
+ font.pixelSize: 20
+ anchors.top: parent.top
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 50
+ }
+
+ Image {
+ id: moon
+ anchors.centerIn: parent
+ scale: moonArea.pressed ? 1.1 : 1.0
+ opacity: moonArea.containsMouse ? 1.0 : 0.7
+ source: Qt.resolvedUrl("images/moon.png")
+
+ MaskedMouseArea {
+ id: moonArea
+ anchors.fill: parent
+ alphaThreshold: 0.4
+ maskSource: moon.source
+ }
+
+ Behavior on opacity {
+ NumberAnimation { duration: 200 }
+ }
+ Behavior on scale {
+ NumberAnimation { duration: 100 }
+ }
+ }
+
+ Image {
+ id: rightCloud
+ anchors {
+ centerIn: moon
+ verticalCenterOffset: 30
+ horizontalCenterOffset: 80
+ }
+ scale: rightCloudArea.pressed ? 1.1 : 1.0
+ opacity: rightCloudArea.containsMouse ? 1.0 : 0.7
+ source: Qt.resolvedUrl("images/cloud_2.png")
+
+ MaskedMouseArea {
+ id: rightCloudArea
+ anchors.fill: parent
+ alphaThreshold: 0.4
+ maskSource: rightCloud.source
+ }
+
+ Behavior on opacity {
+ NumberAnimation { duration: 200 }
+ }
+ Behavior on scale {
+ NumberAnimation { duration: 100 }
+ }
+ }
+
+ Image {
+ id: leftCloud
+ anchors {
+ centerIn: moon
+ verticalCenterOffset: 40
+ horizontalCenterOffset: -80
+ }
+ scale: leftCloudArea.pressed ? 1.1 : 1.0
+ opacity: leftCloudArea.containsMouse ? 1.0 : 0.7
+ source: Qt.resolvedUrl("images/cloud_1.png")
+
+ MaskedMouseArea {
+ id: leftCloudArea
+ anchors.fill: parent
+ alphaThreshold: 0.4
+ maskSource: leftCloud.source
+ }
+
+ Behavior on opacity {
+ NumberAnimation { duration: 200 }
+ }
+ Behavior on scale {
+ NumberAnimation { duration: 100 }
+ }
+ }
+}
diff --git a/examples/quick/customitems/maskedmousearea/maskedmousearea.qmlproject b/tests/manual/maskedmousearea/maskedmousearea.qmlproject
index 709c19866f..709c19866f 100644
--- a/examples/quick/customitems/maskedmousearea/maskedmousearea.qmlproject
+++ b/tests/manual/maskedmousearea/maskedmousearea.qmlproject
diff --git a/examples/quick/customitems/maskedmousearea/maskedmousearea.qrc b/tests/manual/maskedmousearea/maskedmousearea.qrc
index 3cc3955e56..3cc3955e56 100644
--- a/examples/quick/customitems/maskedmousearea/maskedmousearea.qrc
+++ b/tests/manual/maskedmousearea/maskedmousearea.qrc
diff --git a/tests/manual/mousearea/main.cpp b/tests/manual/mousearea/main.cpp
index 2ef54395d6..0629775da0 100644
--- a/tests/manual/mousearea/main.cpp
+++ b/tests/manual/mousearea/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
diff --git a/tests/manual/mousearea/main.qml b/tests/manual/mousearea/main.qml
index 80d66092c4..959cdaa850 100644
--- a/tests/manual/mousearea/main.qml
+++ b/tests/manual/mousearea/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.2
diff --git a/tests/manual/mousearea/plainMouseArea.qml b/tests/manual/mousearea/plainMouseArea.qml
index e30846e11b..85c939151f 100644
--- a/tests/manual/mousearea/plainMouseArea.qml
+++ b/tests/manual/mousearea/plainMouseArea.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.11
diff --git a/tests/manual/networkaccessmanagerfactory/CMakeLists.txt b/tests/manual/networkaccessmanagerfactory/CMakeLists.txt
new file mode 100644
index 0000000000..80ab183588
--- /dev/null
+++ b/tests/manual/networkaccessmanagerfactory/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(networkaccessmanagerfactory LANGUAGES CXX)
+ find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Qml Quick Test)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+set(CMAKE_AUTOMOC ON)
+
+qt_internal_add_manual_test(networkaccessmanagerfactory
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Network
+ Qt::Qml
+ Qt::Quick
+)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/networkaccessmanagerfactory")
+
+set_target_properties(networkaccessmanagerfactory PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+qt_add_qml_module(networkaccessmanagerfactory
+ URI Example
+ QML_FILES view.qml
+ NO_RESOURCE_TARGET_PATH
+)
+
+install(TARGETS networkaccessmanagerfactory
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/qml/networkaccessmanagerfactory/doc/src/networkaccessmanagerfactory.qdoc b/tests/manual/networkaccessmanagerfactory/doc/src/networkaccessmanagerfactory.qdoc
index 7c46fd0cf7..7c46fd0cf7 100644
--- a/examples/qml/networkaccessmanagerfactory/doc/src/networkaccessmanagerfactory.qdoc
+++ b/tests/manual/networkaccessmanagerfactory/doc/src/networkaccessmanagerfactory.qdoc
diff --git a/tests/manual/networkaccessmanagerfactory/main.cpp b/tests/manual/networkaccessmanagerfactory/main.cpp
new file mode 100644
index 0000000000..85250f9dfd
--- /dev/null
+++ b/tests/manual/networkaccessmanagerfactory/main.cpp
@@ -0,0 +1,97 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QCommandLineParser>
+#include <QGuiApplication>
+#include <QNetworkAccessManager>
+#include <QNetworkProxy>
+
+#include <QQmlEngine>
+#include <QQmlNetworkAccessManagerFactory>
+#include <QtQuick/QQuickView>
+
+
+/*
+ This example illustrates using a QQmlNetworkAccessManagerFactory to
+ create a QNetworkAccessManager with a proxy.
+
+ Usage:
+ networkaccessmanagerfactory [-host <proxy> -port <port>] [file]
+*/
+
+#if QT_CONFIG(networkproxy)
+static QString proxyHost;
+static int proxyPort = 0;
+#endif // networkproxy
+
+class MyNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
+{
+public:
+ QNetworkAccessManager *create(QObject *parent) override;
+};
+
+QNetworkAccessManager *MyNetworkAccessManagerFactory::create(QObject *parent)
+{
+ QNetworkAccessManager *nam = new QNetworkAccessManager(parent);
+#if QT_CONFIG(networkproxy)
+ if (!proxyHost.isEmpty()) {
+ qDebug() << "Created QNetworkAccessManager using proxy" << (proxyHost + ":" + QString::number(proxyPort));
+ QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, proxyHost, proxyPort);
+ nam->setProxy(proxy);
+ }
+#endif // networkproxy
+
+ return nam;
+}
+
+int main(int argc, char ** argv)
+{
+ QUrl source("qrc:view.qml");
+
+ QGuiApplication app(argc, argv);
+
+ QCommandLineParser parser;
+#if QT_CONFIG(networkproxy)
+ QCommandLineOption proxyHostOption("host", "The proxy host to use.", "host");
+ parser.addOption(proxyHostOption);
+ QCommandLineOption proxyPortOption("port", "The proxy port to use.", "port", "0");
+ parser.addOption(proxyPortOption);
+#endif // networkproxy
+ parser.addPositionalArgument("file", "The file to use.");
+ QCommandLineOption helpOption = parser.addHelpOption();
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+ QStringList arguments = QCoreApplication::arguments();
+ if (!parser.parse(arguments)) {
+ qWarning() << parser.helpText() << '\n' << parser.errorText();
+ exit(1);
+ }
+ if (parser.isSet(helpOption)) {
+ qWarning() << parser.helpText();
+ exit(0);
+ }
+#if QT_CONFIG(networkproxy)
+ if (parser.isSet(proxyHostOption))
+ proxyHost = parser.value(proxyHostOption);
+ if (parser.isSet(proxyPortOption)) {
+ bool ok = true;
+ proxyPort = parser.value(proxyPortOption).toInt(&ok);
+ if (!ok || proxyPort < 1 || proxyPort > 65535) {
+ qWarning() << parser.helpText() << "\nNo valid port given. It should\
+ be a number between 1 and 65535";
+ exit(1);
+ }
+ }
+#endif // networkproxy
+ if (parser.positionalArguments().count() == 1)
+ source = QUrl::fromLocalFile(parser.positionalArguments().first());
+
+ QQuickView view;
+ MyNetworkAccessManagerFactory networkManagerFactory;
+ view.engine()->setNetworkAccessManagerFactory(&networkManagerFactory);
+
+ view.setSource(source);
+ view.show();
+
+ return app.exec();
+}
+
diff --git a/examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.pro b/tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.pro
index b4d6d827fd..b4d6d827fd 100644
--- a/examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.pro
+++ b/tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.pro
diff --git a/examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.qmlproject b/tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.qmlproject
index 2bb4016996..2bb4016996 100644
--- a/examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.qmlproject
+++ b/tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.qmlproject
diff --git a/examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.qrc b/tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.qrc
index 17e9301471..17e9301471 100644
--- a/examples/qml/networkaccessmanagerfactory/networkaccessmanagerfactory.qrc
+++ b/tests/manual/networkaccessmanagerfactory/networkaccessmanagerfactory.qrc
diff --git a/tests/manual/networkaccessmanagerfactory/view.qml b/tests/manual/networkaccessmanagerfactory/view.qml
new file mode 100644
index 0000000000..eaaec13449
--- /dev/null
+++ b/tests/manual/networkaccessmanagerfactory/view.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Image {
+ width: 300
+ height: 214
+ source: "https://www.qt.io/hs-fs/hubfs/Qt-logo-neon_900px.png"
+}
diff --git a/tests/manual/nodetypes_ng/AtlasedImages.qml b/tests/manual/nodetypes_ng/AtlasedImages.qml
index d8a99691b8..40c9d391e7 100644
--- a/tests/manual/nodetypes_ng/AtlasedImages.qml
+++ b/tests/manual/nodetypes_ng/AtlasedImages.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.8
diff --git a/tests/manual/nodetypes_ng/CompressedImages.qml b/tests/manual/nodetypes_ng/CompressedImages.qml
index 21a8b186d3..3c05670991 100644
--- a/tests/manual/nodetypes_ng/CompressedImages.qml
+++ b/tests/manual/nodetypes_ng/CompressedImages.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/DistanceFieldText.qml b/tests/manual/nodetypes_ng/DistanceFieldText.qml
index 455d54c773..5560e52ba3 100644
--- a/tests/manual/nodetypes_ng/DistanceFieldText.qml
+++ b/tests/manual/nodetypes_ng/DistanceFieldText.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/Images.qml b/tests/manual/nodetypes_ng/Images.qml
index 9e5f72dc58..cc94dbf363 100644
--- a/tests/manual/nodetypes_ng/Images.qml
+++ b/tests/manual/nodetypes_ng/Images.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.3
diff --git a/tests/manual/nodetypes_ng/Layers.qml b/tests/manual/nodetypes_ng/Layers.qml
index eb6bf79960..819bcbccf1 100644
--- a/tests/manual/nodetypes_ng/Layers.qml
+++ b/tests/manual/nodetypes_ng/Layers.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/LotsOfNodes.qml b/tests/manual/nodetypes_ng/LotsOfNodes.qml
index 638bb8cf5c..3e6e9007ce 100644
--- a/tests/manual/nodetypes_ng/LotsOfNodes.qml
+++ b/tests/manual/nodetypes_ng/LotsOfNodes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Stuff 1.0
diff --git a/tests/manual/nodetypes_ng/LotsOfRects.qml b/tests/manual/nodetypes_ng/LotsOfRects.qml
index 13c1caa67a..fc29874103 100644
--- a/tests/manual/nodetypes_ng/LotsOfRects.qml
+++ b/tests/manual/nodetypes_ng/LotsOfRects.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/MoreWindows.qml b/tests/manual/nodetypes_ng/MoreWindows.qml
index 9ba703dedb..04938b6171 100644
--- a/tests/manual/nodetypes_ng/MoreWindows.qml
+++ b/tests/manual/nodetypes_ng/MoreWindows.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Window 2.12
diff --git a/tests/manual/nodetypes_ng/MultiClipRects.qml b/tests/manual/nodetypes_ng/MultiClipRects.qml
index ed84dc98c4..40ec59c358 100644
--- a/tests/manual/nodetypes_ng/MultiClipRects.qml
+++ b/tests/manual/nodetypes_ng/MultiClipRects.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/Painter.qml b/tests/manual/nodetypes_ng/Painter.qml
index ca6469af29..77e8cb39d9 100644
--- a/tests/manual/nodetypes_ng/Painter.qml
+++ b/tests/manual/nodetypes_ng/Painter.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Stuff 1.0
diff --git a/tests/manual/nodetypes_ng/Rects.qml b/tests/manual/nodetypes_ng/Rects.qml
index 529a855c93..5cd2a50e30 100644
--- a/tests/manual/nodetypes_ng/Rects.qml
+++ b/tests/manual/nodetypes_ng/Rects.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/ShaderEffect.qml b/tests/manual/nodetypes_ng/ShaderEffect.qml
index 35d07ff696..5e8332d1ad 100644
--- a/tests/manual/nodetypes_ng/ShaderEffect.qml
+++ b/tests/manual/nodetypes_ng/ShaderEffect.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Use QtQuick 2.8 to get GraphicsInfo and others
import QtQuick 2.8
diff --git a/tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml b/tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml
index 2bcb710d38..96cacde80f 100644
--- a/tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml
+++ b/tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.8
diff --git a/tests/manual/nodetypes_ng/ShaderEffectSource.qml b/tests/manual/nodetypes_ng/ShaderEffectSource.qml
index ce3acd16bd..a6dc78adb8 100644
--- a/tests/manual/nodetypes_ng/ShaderEffectSource.qml
+++ b/tests/manual/nodetypes_ng/ShaderEffectSource.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/SimpleRect.qml b/tests/manual/nodetypes_ng/SimpleRect.qml
index 55f410de5c..920cde4c45 100644
--- a/tests/manual/nodetypes_ng/SimpleRect.qml
+++ b/tests/manual/nodetypes_ng/SimpleRect.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/Text.qml b/tests/manual/nodetypes_ng/Text.qml
index 59afbce904..2928a5626e 100644
--- a/tests/manual/nodetypes_ng/Text.qml
+++ b/tests/manual/nodetypes_ng/Text.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/main.qml b/tests/manual/nodetypes_ng/main.qml
index 0b13ab0dfd..acf3ea8e08 100644
--- a/tests/manual/nodetypes_ng/main.qml
+++ b/tests/manual/nodetypes_ng/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/manual/nodetypes_ng/nodetypes_ng.cpp b/tests/manual/nodetypes_ng/nodetypes_ng.cpp
index f7ece99ec7..110355e5cc 100644
--- a/tests/manual/nodetypes_ng/nodetypes_ng.cpp
+++ b/tests/manual/nodetypes_ng/nodetypes_ng.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QThread>
diff --git a/tests/manual/painterpathquickshape/1535737773.svg b/tests/manual/painterpathquickshape/1535737773.svg
new file mode 100644
index 0000000000..c836b8d18d
--- /dev/null
+++ b/tests/manual/painterpathquickshape/1535737773.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="498" height="450" viewBox="0, 0, 498, 450">
+ <g id="Layer_1">
+ <path d="M377.478,156.433 C390.511,164.689 405.743,177.699 401.901,188.702 L400.821,214.061 L387.401,200.274 C377.97,203.879 355.363,209.091 344.882,209.309 C327.622,209.086 318.23,200.447 301.246,210.607 L300.15,211.261 C287.503,219.529 278.841,222.432 261.924,220.372 C240.649,217.635 222.088,219.976 200.944,221.401 C189.111,222.851 186.176,229.895 185.117,238.294 C184.679,248.402 186.989,251.163 188.144,259.871 C189.694,271.542 180.447,281.398 175.785,292.36 C171.836,302.716 170.28,317.969 170.783,332.682 L350.629,302.414 L497.063,442.618 L439.68,448.335 L171.109,338.724 C171.796,348.056 173.32,356.85 175.59,363.646 C180.514,378.376 199.634,392.772 200.249,404.625 C200.896,417.212 187.414,419.239 174.601,416.362 C155.05,411.713 154.299,415.621 141.357,426.569 C113.335,450.279 98.118,429.304 71.605,431.841 C55.253,433.405 40.729,432.918 27.507,424.542 C16.53,416.991 17.367,406.636 15.482,396.464 C14.067,388.824 15.899,382.701 25.702,379.28 C39.023,376.32 56.656,378.437 65.04,370.918 C74.247,362.664 72.98,362.849 84.625,355.865 C105.108,343.576 98.591,331.952 98.436,313.869 C98.388,308.464 98.797,303.473 99.42,298.096 C101.462,281.407 101.799,265.05 101.034,248.311 C100.788,243.23 100.462,237.986 98.094,233.185 C86.734,214.876 67.777,210.151 74.41,184.825 C83.029,151.909 117.925,169.13 136.27,162.996 C147.594,159.208 152.842,157.385 165.027,154.781 C169.689,153.785 174.381,152.847 179.057,151.883 C191.796,149.238 202.615,147.549 215.653,146.058 C237.815,143.521 239.756,129.349 272.799,126.685 C294.042,124.898 292.304,121.222 309.264,115.415 C330.998,107.974 345.932,115.338 355.323,130.552 C363.442,143.706 362.125,146.461 377.478,156.433" fill="#D0CFCA"/>
+ <path d="M439.68,210.512 L439.68,448.335 L0.464,341.45 L0.464,103.627 L210.167,90.042 L439.68,210.512" fill="#DFB142"/>
+ <path d="M0.464,103.627 C53.025,50.996 164.147,14.658 292.833,14.465 L439.68,210.512 L0.464,103.627" fill="#F0C55C"/>
+ <path d="M439.68,272.823 L439.68,379.792 L0.464,272.907 L0.464,165.939 L439.68,272.823" fill="#685227"/>
+ <path d="M362.421,63.072 C375.451,73.881 390.685,90.917 386.843,105.325 L385.763,138.531 L372.343,120.479 C362.913,125.199 340.305,132.023 329.822,132.31 C312.565,132.018 303.174,120.703 286.188,134.008 L285.091,134.866 C272.443,145.693 263.784,149.494 246.864,146.795 C225.593,143.212 207.028,146.279 185.882,148.143 C174.055,150.042 171.114,159.266 170.059,170.264 C169.617,183.499 171.93,187.117 173.087,198.519 C174.633,213.801 165.388,226.707 160.726,241.06 C153.682,265.245 154.256,309.816 160.533,334.409 C165.455,353.698 184.577,372.548 185.187,388.07 C185.839,404.551 172.354,407.205 159.541,403.436 C139.991,397.352 139.242,402.465 126.298,416.803 C98.275,447.852 83.059,420.385 56.547,423.705 C40.193,425.756 25.669,425.118 12.448,414.151 C1.472,404.259 2.308,390.702 0.423,377.381 C-0.993,367.375 0.84,359.362 10.644,354.877 C23.964,351.004 41.596,353.777 49.98,343.931 C59.186,333.122 57.921,333.366 69.564,324.218 C90.05,308.127 83.529,292.907 83.374,269.229 C83.331,262.151 83.737,255.615 84.363,248.573 C86.4,226.719 86.737,205.301 85.978,183.381 C85.73,176.728 85.403,169.861 83.034,163.574 C71.676,139.599 52.721,133.411 59.351,100.248 C67.97,57.145 102.863,79.697 121.208,71.664 C132.535,66.704 137.782,64.318 149.968,60.907 C154.63,59.602 159.322,58.374 163.998,57.113 C176.736,53.648 187.556,51.437 200.594,49.483 C222.753,46.163 224.694,27.604 257.742,24.117 C278.982,21.776 277.247,16.962 294.207,9.359 C315.939,-0.384 330.873,9.258 340.265,29.18 C348.384,46.406 347.065,50.012 362.421,63.072" fill="#A02529"/>
+ <path d="M377.222,77.803 C384.346,86.596 389.209,96.457 386.843,105.325 L385.763,138.531 L372.343,120.479 C362.913,125.199 340.305,132.023 329.822,132.31 C312.565,132.018 303.174,120.703 286.188,134.008 L285.091,134.866 C272.443,145.693 263.784,149.494 246.864,146.795 C225.593,143.212 207.028,146.279 185.882,148.143 C174.055,150.042 171.114,159.266 170.059,170.264 C169.617,183.499 171.93,187.117 173.087,198.519 C174.633,213.801 165.388,226.707 160.726,241.06 C153.682,265.245 154.256,309.816 160.533,334.409 C165.455,353.698 184.577,372.548 185.187,388.07 C185.839,404.551 172.354,407.205 159.541,403.436 C139.991,397.352 139.242,402.465 126.298,416.803 C98.275,447.852 83.059,420.385 56.547,423.705 C41.406,425.604 27.835,425.198 15.41,416.418 C15.886,416.252 16.353,416.086 16.806,415.923 C40.174,407.529 48.443,396.154 76.787,401.466 C98.441,405.379 98.762,406.43 114.283,392.336 C122.29,385.065 130.835,382.674 136.828,376.579 C147.786,365.44 143.055,348.134 141.242,333.961 C139.79,322.607 138.545,311.693 137.868,300.247 C136.302,270.394 136.149,249.273 146.169,220.601 C156.699,185.664 142.878,187.253 136.676,158.625 C133.559,140.425 141.91,130.414 159.322,126.454 C174.809,123.09 185.026,122.958 200.438,117.255 C226.389,107.653 237.86,126.159 267.655,111.396 C290.299,100.173 288,101.231 313.813,100.553 C322.81,100.316 331.664,100.323 340.66,100.543 C354.877,100.939 372.706,99.879 376.406,83.305 C376.799,81.544 377.061,79.698 377.222,77.803" fill="#852124"/>
+ <path d="M116.994,372.295 C96.441,393.218 85.04,378.461 60.386,384.618 C41.425,389.804 40.515,394.084 19.935,387.145 C-2.438,379.129 8.007,369.233 23.439,362.447 C35.36,357.424 45.017,353.945 56.672,347.815 C66.414,342.692 74.814,340.533 84.187,336.313 C95.931,330.505 89.501,323.825 91.833,310.488 C97.182,279.909 97.906,262.688 96.68,231.982 C96.449,226.215 93.495,190.875 105.333,195.129 C112.567,197.728 113.875,226.189 114.663,233.57 C116.893,254.394 116.754,269.973 116.572,290.65 C116.391,311.867 117.946,313.248 125.413,333.1 C131.815,350.125 129.549,358.997 116.994,372.295 z M277.376,26.93 C256.4,34.431 249.969,33.482 233.64,49.962 C224.443,59.24 216.728,61.36 203.788,61.262 C187.182,60.92 176.702,55.995 160.816,63.608 C158.592,64.676 156.373,65.745 154.144,66.805 C141.264,72.87 114.971,74.232 107.809,85.082 C102.256,93.498 109.541,98.832 117.668,100.211 C132.249,102.681 153.315,101.858 167.441,97.815 C195.955,89.668 202.928,88.077 232.115,95.399 C252.075,98.504 255.43,86.181 270.652,77.257 C290.588,65.572 311.159,75.362 332.33,74.141 C343.4,73.555 354.434,70.833 345.116,57.437 C340.157,50.307 328.218,41.032 321.232,33.358 C305.834,16.439 297.153,19.871 277.376,26.93" fill="#BB242A"/>
+ <path d="M213.284,102.237 C235.308,102.237 253.268,93.724 253.268,83.285 C253.268,72.846 235.308,64.331 213.284,64.331 C191.261,64.331 173.301,72.846 173.301,83.285 C173.301,93.724 191.261,102.237 213.284,102.237" fill="#A02529"/>
+ <path d="M296.932,67.335 C315.226,67.335 330.146,60.262 330.146,51.591 C330.146,42.919 315.226,35.846 296.932,35.846 C278.638,35.846 263.715,42.919 263.715,51.591 C263.715,60.262 278.638,67.335 296.932,67.335" fill="#A02529"/>
+ <path d="M271.697,1.555 C274.782,1.555 277.761,2.011 280.573,2.854 C283.386,2.011 286.366,1.555 289.454,1.555 C306.489,1.555 320.297,15.363 320.297,32.398 C320.297,49.431 306.489,63.239 289.454,63.239 C286.366,63.239 283.386,62.784 280.573,61.94 C277.761,62.784 274.782,63.239 271.697,63.239 C254.663,63.239 240.854,49.431 240.854,32.398 C240.854,15.363 254.663,1.555 271.697,1.555" fill="#852124"/>
+ <path d="M300.104,3.445 C311.89,7.783 320.297,19.108 320.297,32.398 C320.297,49.431 306.489,63.239 289.454,63.239 C286.366,63.239 283.386,62.784 280.573,61.94 C277.761,62.784 274.782,63.239 271.697,63.239 C257.32,63.239 245.242,53.405 241.822,40.097 C251.466,53.606 263.565,57.28 278.381,50.083 C312.573,61.654 325.556,25.161 300.104,3.445" fill="#61191B"/>
+ <path d="M297.153,16.662 C294.196,13.79 290.182,11.311 285.934,11.371 C279.467,11.465 279.824,12.683 273.232,9.94 C269.746,8.489 266.511,8.766 262.892,9.656 C257.742,10.921 253.609,13.591 250.145,17.573 C243.862,25.22 245.932,33.326 255.406,36.683 L256.029,36.902 C258.651,37.773 266.73,40.311 268.992,39.858 C273.745,38.905 276.231,33.803 282.086,37.96 C286.883,41.431 287.62,43.618 294.196,42.936 C297.701,42.379 299.156,39.906 300.562,36.849 C303.38,30.721 302.722,23.53 298.527,18.185 C298.084,17.63 297.646,17.174 297.153,16.662" fill="#A02529"/>
+ <path d="M182.906,23.052 C186.623,23.052 190.21,23.601 193.595,24.617 C196.982,23.601 200.567,23.052 204.283,23.052 C224.79,23.052 241.411,39.675 241.411,60.179 C241.411,80.684 224.79,97.306 204.283,97.306 C200.567,97.306 196.982,96.758 193.595,95.742 C190.21,96.758 186.623,97.306 182.906,97.306 C162.401,97.306 145.778,80.684 145.778,60.179 C145.778,39.675 162.401,23.052 182.906,23.052" fill="#852124"/>
+ <path d="M217.107,25.326 C231.292,30.548 241.411,44.182 241.411,60.179 C241.411,80.684 224.79,97.306 204.283,97.306 C200.567,97.306 196.982,96.758 193.595,95.742 C190.21,96.758 186.623,97.306 182.906,97.306 C165.602,97.306 151.064,85.468 146.947,69.449 L146.947,69.448 C158.552,85.71 173.119,90.133 190.956,81.469 C232.115,95.399 247.744,51.467 217.107,25.326" fill="#61191B"/>
+ <path d="M213.552,41.237 C209.99,37.779 205.16,34.796 200.046,34.87 C192.261,34.98 192.691,36.448 184.757,33.146 C180.557,31.397 176.667,31.732 172.31,32.804 C166.109,34.328 161.138,37.54 156.962,42.333 C149.401,51.538 151.893,61.298 163.298,65.338 L164.046,65.602 C167.206,66.649 176.932,69.705 179.65,69.159 C185.374,68.012 188.365,61.87 195.415,66.875 C201.189,71.055 202.077,73.687 209.996,72.866 C214.212,72.195 215.963,69.218 217.655,65.538 C221.048,58.161 220.254,49.505 215.209,43.07 C214.669,42.402 214.146,41.853 213.552,41.237" fill="#A02529"/>
+ <path d="M88.513,390.871 C110.533,390.871 128.495,382.359 128.495,371.918 C128.495,361.479 110.533,352.964 88.513,352.964 C66.489,352.964 48.529,361.479 48.529,371.918 C48.529,382.359 66.489,390.871 88.513,390.871" fill="#A02529"/>
+ <path d="M58.132,311.686 C61.848,311.686 65.436,312.234 68.82,313.249 C72.207,312.234 75.795,311.686 79.511,311.686 C100.016,311.686 116.639,328.307 116.639,348.812 C116.639,369.319 100.016,385.942 79.511,385.942 C75.795,385.942 72.207,385.391 68.82,384.375 C65.436,385.391 61.848,385.942 58.132,385.942 C37.629,385.942 21.006,369.319 21.006,348.812 C21.006,328.307 37.629,311.686 58.132,311.686" fill="#852124"/>
+ <path d="M92.335,313.96 C106.521,319.182 116.639,332.816 116.639,348.812 C116.639,369.319 100.016,385.942 79.511,385.942 C75.795,385.942 72.207,385.391 68.82,384.375 C65.436,385.391 61.848,385.942 58.132,385.942 C40.828,385.942 26.289,374.101 22.174,358.082 C33.779,374.344 48.345,378.766 66.182,370.103 C107.344,384.033 122.97,340.101 92.335,313.96" fill="#61191B"/>
+ <path d="M88.779,329.871 C85.219,326.413 80.385,323.43 75.273,323.502 C67.489,323.615 67.919,325.082 59.982,321.779 C55.785,320.031 51.893,320.367 47.535,321.437 C41.339,322.96 36.362,326.173 32.19,330.967 C24.628,340.172 27.119,349.932 38.524,353.97 L39.272,354.237 C42.432,355.284 52.159,358.339 54.876,357.793 C60.602,356.645 63.591,350.503 70.641,355.509 C76.418,359.689 77.303,362.32 85.222,361.498 C89.438,360.827 91.188,357.851 92.881,354.171 C96.273,346.794 95.481,338.137 90.435,331.702 C89.898,331.035 89.373,330.488 88.779,329.871" fill="#A02529"/>
+ </g>
+</svg>
diff --git a/tests/manual/painterpathquickshape/CMakeLists.txt b/tests/manual/painterpathquickshape/CMakeLists.txt
new file mode 100644
index 0000000000..4677fad26c
--- /dev/null
+++ b/tests/manual/painterpathquickshape/CMakeLists.txt
@@ -0,0 +1,87 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ project(painterPathQuickShape LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+find_package(Qt6 COMPONENTS ShaderTools)
+
+qt_internal_add_manual_test(painterPathQuickShape
+ GUI
+ SOURCES
+ main.cpp
+ debugpaintitem.h debugpaintitem.cpp
+ svgpathloader.h svgpathloader.cpp
+ debugvisualizationcontroller.h debugvisualizationcontroller.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+ Qt::QuickPrivate
+ Qt::QuickShapesPrivate
+)
+
+
+set(qml_resource_files
+ "1535737773.svg"
+ "main.qml"
+ "SvgShape.qml"
+ "ControlPanel.qml"
+ "ControlPoint.qml"
+ "TextShape.qml"
+ "SimpleShape.qml"
+ "SmallPolygon.qml"
+ "Squircle.qml"
+ "Intersect.qml"
+ "Intersect2.qml"
+ "ControlledShape.qml"
+ "Mussel.qml"
+ "Graziano.ttf"
+ "CubicShape.qml"
+ "DashedStroke.qml"
+ "hand-print.svg"
+ "background.png"
+ "arcDirection.qml"
+ "arcRotation.qml"
+ "capStyles.qml"
+ "cubicCurve.qml"
+ "dashPattern.qml"
+ "ellipticalArcs.qml"
+ "fillRules.qml"
+ "gradientSpreadModes.qml"
+ "joinStyles.qml"
+ "largeOrSmallArc.qml"
+ "linearGradient.qml"
+ "quadraticCurve.qml"
+ "radialGradient.qml"
+ "strokeOrFill.qml"
+ "text.qml"
+ "tiger.qml"
+)
+
+qt_internal_add_resource(painterPathQuickShape "qml"
+ PREFIX
+ "/"
+ FILES
+ ${qml_resource_files}
+)
+
+qt_add_shaders(painterPathQuickShape "shaders"
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ "/"
+ FILES
+ "zoombox.frag"
+)
+
+qt_add_qml_module(painterPathQuickShape
+ VERSION 1.0
+ URI InstancingTest
+ RESOURCE_PREFIX /
+)
diff --git a/tests/manual/painterpathquickshape/ControlPanel.qml b/tests/manual/painterpathquickshape/ControlPanel.qml
new file mode 100644
index 0000000000..87eb9ae3e7
--- /dev/null
+++ b/tests/manual/painterpathquickshape/ControlPanel.qml
@@ -0,0 +1,416 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Shapes
+import QtQuick.Dialogs
+import QtCore
+
+Item {
+ property real scale: +scaleSlider.value.toFixed(4)
+ property color backgroundColor: setBackground.checked ? Qt.rgba(bgColor.color.r, bgColor.color.g, bgColor.color.b, 1.0) : Qt.rgba(0,0,0,0)
+
+ property color outlineColor: enableOutline.checked ? Qt.rgba(outlineColor.color.r, outlineColor.color.g, outlineColor.color.b, outlineAlpha) : Qt.rgba(0,0,0,0)
+ property color fillColor: Qt.rgba(fillColor.color.r, fillColor.color.g, fillColor.color.b, pathAlpha)
+ property alias pathAlpha: alphaSlider.value
+ property alias fillRule: fillRule.currentValue
+ property alias outlineAlpha: outlineAlphaSlider.value
+ property real outlineWidth: cosmeticPen.checked ? outlineWidthEdit.text / scale : outlineWidthEdit.text
+ property alias outlineStyle: outlineStyle.currentValue
+ property alias capStyle: capStyle.currentValue
+ property alias joinStyle: joinStyle.currentValue
+ property alias debugCurves: enableDebug.checked
+ property alias debugWireframe: enableWireframe.checked
+ property alias painterComparison: painterComparison.currentIndex
+ property alias painterComparisonColor: painterComparisonColor.color
+ property alias painterComparisonAlpha: painterComparisonColorAlpha.value
+ property alias outlineEnabled: enableOutline.checked
+ property alias gradientType: gradientType.currentIndex
+ property alias rendererName: rendererLabel.text
+ property alias preferCurve: rendererLabel.preferCurve
+
+ property int subShape: pickSubShape.checked ? subShapeSelector.value : -1
+ property bool subShapeGreaterThan : pickSubShapeGreaterThan.checked
+
+ property real pathMargin: marginEdit.text
+
+ Settings {
+ property alias enableOutline: enableOutline.checked
+ property alias outlineColor: outlineColor.color
+ property alias outlineWidth: outlineWidthEdit.text
+ property alias outlineAlpha: outlineAlphaSlider.value
+ property alias outlineStyle: outlineStyle.currentIndex
+ property alias joinStyle: joinStyle.currentIndex
+ property alias capStyle: capStyle.currentIndex
+ property alias cosmeticPen: cosmeticPen.checked
+
+ property alias pathAlpha: alphaSlider.value
+ property alias fillRule: fillRule.currentIndex
+ property alias fillColor: fillColor.color
+
+ property alias setBackground: setBackground.checked
+ property alias backgroundColor: bgColor.color
+ }
+
+ function setScale(x) {
+ scaleSlider.value = x
+ }
+
+ signal pathChanged
+ function updatePath()
+ {
+ pathChanged()
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: 10
+ RowLayout {
+ Label {
+ text: "Renderer:"
+ color: "white"
+ }
+ Label {
+ id: rendererLabel
+ property bool preferCurve: true
+ color: "white"
+ text: "Unknown"
+
+ TapHandler {
+ onTapped: {
+ rendererLabel.preferCurve = !rendererLabel.preferCurve
+ }
+ }
+ }
+ CheckBox {
+ id: enableDebug
+ Layout.fillWidth: false
+ }
+ Label {
+ text: "Debug"
+ color: "white"
+ }
+ CheckBox {
+ id: enableWireframe
+ Layout.fillWidth: false
+ }
+ Label {
+ text: "Wireframe"
+ color: "white"
+ }
+ ComboBox {
+ id: painterComparison
+ Layout.fillWidth: false
+ model: [
+ "No QPainter comparison",
+ "Overlaid QPainter comparison",
+ "Underlaid QPainter comparison"
+ ]
+ }
+ Rectangle {
+ id: painterComparisonColor
+ color: "red"
+ width: 20
+ height: 20
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ painterComparisonColorDialog.open()
+ }
+ }
+ }
+ Slider {
+ id: painterComparisonColorAlpha
+ from: 0.0
+ to: 1.0
+ value: 1.0
+ Layout.fillWidth: false
+ }
+ Label {
+ text: "Alpha"
+ color: "white"
+ }
+ CheckBox {
+ text: "Pick SVG sub-shape"
+ id: pickSubShape
+ palette.windowText: "white"
+ Layout.fillWidth: false
+ }
+ SpinBox {
+ id: subShapeSelector
+ visible: pickSubShape.checked
+ value: 0
+ to: 999
+ editable: true
+ Layout.fillWidth: false
+ }
+ CheckBox {
+ id: pickSubShapeGreaterThan
+ visible: pickSubShape.checked
+ text: "show greater than"
+ palette.windowText: "white"
+ Layout.fillWidth: false
+ }
+ CheckBox {
+ id: setBackground
+ text: "Solid background"
+ palette.windowText: "white"
+ Layout.fillWidth: false
+ }
+ RowLayout {
+ visible: setBackground.checked
+ Rectangle {
+ id: bgColor
+ property color selectedColor: "#a9a9a9"
+ color: selectedColor
+ border.color: "black"
+ border.width: 2
+ width: 21
+ height: 21
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ bgColorDialog.open()
+ }
+ }
+ }
+ }
+ }
+ RowLayout {
+ Label {
+ text: "Margin:"
+ color: "white"
+ }
+ TextField {
+ id: marginEdit
+ text: "150"
+ validator: DoubleValidator{ bottom: 0.0 }
+ }
+ Label {
+ text: "Scale:"
+ color: "white"
+ }
+ TextField {
+ id: scaleEdit
+ text: scaleSlider.value.toFixed(4)
+ Layout.fillWidth: false
+ onEditingFinished: {
+ let val = +text
+ if (val > 0)
+ scaleSlider.value = val
+ }
+ }
+ Slider {
+ id: scaleSlider
+ Layout.fillWidth: true
+ from: 0.01
+ to: 500.0
+ value: 0.2
+ onValueChanged: scaleEdit.text = value.toFixed(4)
+ }
+ }
+ RowLayout {
+ Label {
+ text: "Fill color"
+ color: "white"
+ }
+ Rectangle {
+ id: fillColor
+ color: "#ffffff"
+ width: 20
+ height: 20
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ fillColorDialog.open()
+ }
+ }
+ }
+ ComboBox {
+ id: gradientType
+ Layout.fillWidth: false
+ model: [ "NoGradient", "LinearGradient", "RadialGradient", "ConicalGradient" ]
+ }
+ Label {
+ text: "Fill rule:"
+ color: "white"
+ }
+ ComboBox {
+ id: fillRule
+ textRole: "text"
+ valueRole: "style"
+ Layout.fillWidth: false
+ model: ListModel {
+ ListElement {
+ text: "WindingFill"
+ style: ShapePath.WindingFill
+ }
+ ListElement {
+ text: "OddEvenFill"
+ style: ShapePath.OddEvenFill
+ }
+ }
+ }
+ Label {
+ text: "Fill alpha(" + Math.round(alphaSlider.value*100)/100 + "):"
+ color: "white"
+ }
+ Slider {
+ id: alphaSlider
+ Layout.fillWidth: true
+ from: 0.0
+ to: 1.0
+ value: 1.0
+ }
+ }
+ RowLayout {
+ CheckBox {
+ id: enableOutline
+ text: "Enable outline"
+ palette.windowText: "white"
+ Layout.fillWidth: false
+ }
+ RowLayout {
+ opacity: enableOutline.checked ? 1 : 0
+ Label {
+ text: "Outline color:"
+ color: "white"
+ }
+ Rectangle {
+ id: outlineColor
+ property color selectedColor: "#33ccbb"
+ color: selectedColor
+ width: 20
+ height: 20
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ outlineColorDialog.open()
+ }
+ }
+ }
+ ComboBox {
+ id: outlineStyle
+ textRole: "text"
+ valueRole: "style"
+ Layout.fillWidth: false
+ model: ListModel {
+ ListElement {
+ text: "Solid line"
+ style: ShapePath.SolidLine
+ }
+ ListElement {
+ text: "Dash line"
+ style: ShapePath.DashLine
+ }
+ }
+ }
+ ComboBox {
+ id: joinStyle
+ textRole: "text"
+ valueRole: "style"
+ Layout.fillWidth: false
+ model: ListModel {
+ ListElement {
+ text: "Miter join"
+ style: ShapePath.MiterJoin
+ }
+ ListElement {
+ text: "Bevel join"
+ style: ShapePath.BevelJoin
+ }
+ ListElement {
+ text: "Round join"
+ style: ShapePath.RoundJoin
+ }
+ }
+ }
+ ComboBox {
+ id: capStyle
+ textRole: "text"
+ valueRole: "style"
+ Layout.fillWidth: false
+ model: ListModel {
+ ListElement {
+ text: "Square cap"
+ style: ShapePath.SquareCap
+ }
+ ListElement {
+ text: "Round cap"
+ style: ShapePath.RoundCap
+ }
+ ListElement {
+ text: "Flat cap"
+ style: ShapePath.FlatCap
+ }
+ }
+ }
+ Label {
+ text: "Outline width:"
+ color: "white"
+ }
+ TextField {
+ id: outlineWidthEdit
+ text: (cosmeticPen.checked ? outlineWidthSlider.value: outlineWidthSlider.value ** 2).toFixed(2)
+ Layout.fillWidth: false
+ onEditingFinished: {
+ let val = +text
+ if (val > 0) {
+ if (cosmeticPen.checked)
+ outlineWidth.value = val * scale
+ else
+ outlineWidth.value = Math.sqrt(val)
+ }
+ }
+ }
+ Slider {
+ id: outlineWidthSlider
+ Layout.fillWidth: true
+ from: 0.0
+ to: 10.0
+ value: Math.sqrt(10)
+ }
+ CheckBox {
+ id: cosmeticPen
+ text: "Cosmetic pen"
+ palette.windowText: "white"
+ Layout.fillWidth: false
+ }
+ Label {
+ text: "Outline alpha (" + Math.round(outlineAlphaSlider.value*100)/100 + "):"
+ color: "white"
+ }
+ Slider {
+ id: outlineAlphaSlider
+ Layout.fillWidth: true
+ from: 0.0
+ to: 1.0
+ value: 1.0
+ }
+ }
+ }
+
+ }
+ ColorDialog {
+ id: bgColorDialog
+ selectedColor: bgColor.selectedColor
+ onAccepted: bgColor.selectedColor = selectedColor
+ }
+ ColorDialog {
+ id: outlineColorDialog
+ selectedColor: outlineColor.selectedColor
+ onAccepted: outlineColor.selectedColor = selectedColor
+ }
+ ColorDialog {
+ id: fillColorDialog
+ selectedColor: fillColor.color
+ onAccepted: fillColor.color = selectedColor
+ }
+ ColorDialog {
+ id: painterComparisonColorDialog
+ selectedColor: painterComparisonColor.color
+ onAccepted: painterComparisonColor.color = selectedColor
+ }
+}
diff --git a/tests/manual/painterpathquickshape/ControlPoint.qml b/tests/manual/painterpathquickshape/ControlPoint.qml
new file mode 100644
index 0000000000..e946a9484b
--- /dev/null
+++ b/tests/manual/painterpathquickshape/ControlPoint.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ id: point1
+ color: "red"
+ border.width: 1
+ border.color: "black"
+ opacity: 0.3
+ width: 20
+ height: 20
+ visible: !theMouseArea.pressed
+
+ property real cx: 400
+ property real cy: 800
+
+ property point pt: Qt.point(cx, cy)
+
+ DragHandler {
+ id: handler
+ xAxis.minimum: -controlPanel.pathMargin
+ yAxis.minimum: -controlPanel.pathMargin
+ xAxis.onActiveValueChanged: {
+ cx = (x + width/2) / controlPanel.scale
+ controlPanel.updatePath()
+ }
+ yAxis.onActiveValueChanged: {
+ cy = (y + height/2) / controlPanel.scale
+ controlPanel.updatePath()
+ }
+ }
+
+ Component.onCompleted: {
+ x = cx * controlPanel.scale - width/2
+ y = cy * controlPanel.scale - height/2
+ }
+
+ Connections {
+ target: controlPanel
+ function onScaleChanged() {
+ x = cx * controlPanel.scale - width/2
+ y = cy * controlPanel.scale - height/2
+ }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/ControlledShape.qml b/tests/manual/painterpathquickshape/ControlledShape.qml
new file mode 100644
index 0000000000..e690f59ccc
--- /dev/null
+++ b/tests/manual/painterpathquickshape/ControlledShape.qml
@@ -0,0 +1,135 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+import io.qt
+
+Item {
+ id: topLevel
+ property alias capStyle: shapePath.capStyle
+ property alias dashOffset: shapePath.dashOffset
+ property alias dashPattern: shapePath.dashPattern
+ property alias fillColor: shapePath.fillColor
+ property alias fillRule: shapePath.fillRule
+ property alias strokeColor: shapePath.strokeColor
+ property alias strokeStyle: shapePath.strokeStyle
+ property alias strokeWidth: shapePath.strokeWidth
+ property alias shapeTransform: shape.transform
+
+ property alias startX: shapePath.startX
+ property alias startY: shapePath.startY
+
+ property rect boundingRect: shape.boundingRect
+
+ width: boundingRect.width
+ height: boundingRect.height
+
+ property vector2d startPoint: "0,0"
+ property list<QtObject> delegate
+
+ LinearGradient {
+ id: linearGradient
+ x1: shape.boundingRect.left
+ y1: shape.boundingRect.top
+ x2: shape.boundingRect.right
+ y2: shape.boundingRect.bottom
+ GradientStop { position: 0; color: fillColor }
+ GradientStop { position: 1; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) }
+ }
+
+ RadialGradient {
+ id: radialGradient
+ centerX: 0.5 * (shape.boundingRect.right + shape.boundingRect.left)
+ centerY: 0.5 * (shape.boundingRect.top + shape.boundingRect.bottom)
+ focalX: centerX
+ focalY: centerY
+ centerRadius: 0.5 * (shape.boundingRect.right - shape.boundingRect.left)
+ focalRadius: 0.1
+ GradientStop { position: 0.0; color: fillColor }
+ GradientStop { position: 1.0; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) }
+ }
+
+ ConicalGradient {
+ id: conicalGradient
+ centerX: 0.5 * (shape.boundingRect.right + shape.boundingRect.left)
+ centerY: 0.5 * (shape.boundingRect.top + shape.boundingRect.bottom)
+ GradientStop { position: 0.0; color: fillColor }
+ GradientStop { position: 0.5; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) }
+ GradientStop { position: 1.0; color: fillColor }
+ }
+
+ property var gradients: [ null, linearGradient, radialGradient, conicalGradient ]
+
+ Item {
+ transform: [
+ Scale {
+ xScale: controlPanel.scale
+ yScale: controlPanel.scale
+ }
+ ]
+ Shape {
+ id: shape
+ x: 0
+ y: 0
+ preferredRendererType: controlPanel.preferCurve ? Shape.CurveRenderer : Shape.UnknownRenderer
+ onRendererTypeChanged: {
+ controlPanel.rendererName = rendererType == Shape.SoftwareRenderer ? "Software" :
+ rendererType == Shape.GeometryRenderer ? "Geometry" :
+ rendererType == Shape.CurveRenderer ? "Curve" : "Unknown";
+ }
+
+ ShapePath {
+ id: shapePath
+ fillRule: controlPanel.fillRule
+ fillGradient: gradients[controlPanel.gradientType]
+ strokeColor: controlPanel.outlineColor
+ fillColor: controlPanel.fillColor
+ strokeWidth: controlPanel.outlineWidth
+ strokeStyle: controlPanel.outlineStyle
+ joinStyle: controlPanel.joinStyle
+ capStyle: controlPanel.capStyle
+ }
+
+ Repeater {
+ model: topLevel.delegate
+ onModelChanged: {
+ shapePath.pathElements = []
+ for (var i = 0; i < model.length; ++i)
+ shapePath.pathElements.push(model[i])
+ }
+ }
+ }
+ }
+ Connections {
+ target: controlPanel
+ function onPathChanged() {
+ debugPaintPath.update()
+ }
+ }
+
+ DebugPaintItem {
+ id: debugPaintPath
+ shape: shapePath
+ visible: controlPanel.painterComparison > 0
+ color: controlPanel.painterComparisonColor
+ opacity: controlPanel.painterComparisonAlpha
+ z: controlPanel.painterComparison > 1 ? -1 : 0
+ pathScale: controlPanel.scale
+ fillRule: topLevel.fillRule
+ strokeStyle: topLevel.strokeStyle
+ strokeColor: controlPanel.outlineEnabled ? Qt.rgba(1 - color.r, 1 - color.g, 1 - color.b, 1) : "transparent"
+ capStyle: topLevel.capStyle
+ joinStyle: controlPanel.joinStyle
+ strokeWidth: topLevel.strokeWidth
+
+ width: visible ? (shape.boundingRect.width + shape.boundingRect.x) * controlPanel.scale : 1
+ height: visible ? (shape.boundingRect.height + shape.boundingRect.y) * controlPanel.scale : 1
+ }
+
+ DebugVisualizationController {
+ showCurves: controlPanel.debugCurves
+ showWireframe: controlPanel.debugWireframe
+ onSettingsChanged: { shapePath.changed() }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/CubicShape.qml b/tests/manual/painterpathquickshape/CubicShape.qml
new file mode 100644
index 0000000000..2491833f0f
--- /dev/null
+++ b/tests/manual/painterpathquickshape/CubicShape.qml
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ delegate: [
+ PathMove { x: start.cx; y: start.cy},
+ PathCubic { x: end.cx; y: end.cy;
+ control1X: control1.cx; control1Y: control1.cy
+ control2X: control2.cx; control2Y: control2.cy },
+ PathMove { x: start2.cx; y: start2.cy },
+ PathCubic { x: end2.cx; y: end2.cy;
+ control1X: control21.cx; control1Y: control21.cy
+ control2X: control22.cx; control2Y: control22.cy },
+ PathLine { x: lineEnd.cx; y: lineEnd.cy }
+ ]
+
+ // Cubic path 1
+ ControlPoint {
+ id: start
+ cx: 200
+ cy: 400
+ }
+ ControlPoint {
+ id: control1
+ color: "blue"
+ cx: 800
+ cy: 0
+ }
+ ControlPoint {
+ id: control2
+ color: "blue"
+ cx: 800
+ cy: 1000
+ }
+ ControlPoint {
+ id: end
+ cx: 200
+ cy: 600
+ }
+
+ // Cubic path 2
+ ControlPoint {
+ id: start2
+ cx: 2200
+ cy: 200
+ }
+ ControlPoint {
+ id: control21
+ color: "blue"
+ cx: 1200
+ cy: 600
+ }
+ ControlPoint {
+ id: control22
+ color: "blue"
+ cx: 3200
+ cy: 1000
+ }
+ ControlPoint {
+ id: end2
+ cx: 2200
+ cy: 1400
+ }
+ ControlPoint {
+ id: lineEnd
+ cx: 1200
+ cy: 200
+ }
+}
diff --git a/tests/manual/painterpathquickshape/DashedStroke.qml b/tests/manual/painterpathquickshape/DashedStroke.qml
new file mode 100644
index 0000000000..d2aa409fb6
--- /dev/null
+++ b/tests/manual/painterpathquickshape/DashedStroke.qml
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ fillColor: "transparent"
+ strokeStyle: ShapePath.DashLine
+ dashOffset: 6.5
+ dashPattern: [ 4, 2, 1, 2, 1, 2 ]
+ delegate: [
+ PathMove { x: 200; y: 200 },
+ PathLine { x: 1000; y: 200 },
+ PathMove { x: 200; y: 300 },
+ PathLine { x: 1000; y: 300 },
+ PathMove { x: 200; y: 400 },
+ PathQuad { x: 1000; y: 400; controlX: 450; controlY: 600 },
+ PathLine { x: 1600; y: 500 },
+ PathMove { x: 200; y: 500 },
+ PathQuad { x: 1000; y: 500; controlX: 450; controlY: 700 },
+ PathLine { x: 1600; y: 600 }
+ ]
+}
diff --git a/tests/manual/painterpathquickshape/FONTLOG.txt b/tests/manual/painterpathquickshape/FONTLOG.txt
new file mode 100644
index 0000000000..138634b237
--- /dev/null
+++ b/tests/manual/painterpathquickshape/FONTLOG.txt
@@ -0,0 +1,22 @@
+1. FONTLOG for Graziano
+
+ This file provides detailed information on the Graziano
+ Font Software. This information should be distributed along with
+ the Graziano font and any derivative works.
+
+2. Basic Font Information
+
+ A font based on my left-handed handwriting.
+ To take notes with Block and lowercase letters on plain
+ white paper without any guideline (with a large-tipped pen)
+ is a good training ground for writing.
+
+3. ChangeLog
+
+ 28 May 2011 (Graziano Capelli) Graziano version 1.0.
+ - Initial release
+
+4 Acknowledgements
+
+ None yet. Feature requests, bug reports and contributions should be
+ sent to Graziano Capelli (air at shweb dot it; femtosoft at libero dot it)
diff --git a/tests/manual/painterpathquickshape/Graziano.ttf b/tests/manual/painterpathquickshape/Graziano.ttf
new file mode 100644
index 0000000000..2c3bd35b9a
--- /dev/null
+++ b/tests/manual/painterpathquickshape/Graziano.ttf
Binary files differ
diff --git a/tests/manual/painterpathquickshape/Intersect.qml b/tests/manual/painterpathquickshape/Intersect.qml
new file mode 100644
index 0000000000..8803c6fb23
--- /dev/null
+++ b/tests/manual/painterpathquickshape/Intersect.qml
@@ -0,0 +1,141 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ delegate: [
+ PathMove { x: p0.cx; y: p0.cy },
+ PathQuad { x: p1.cx; y: p1.cy; controlX: c0.cx; controlY: c0.cy },
+ PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy },
+ PathQuad { x: p3.cx; y: p3.cy; controlX: c2.cx; controlY: c2.cy },
+ PathQuad { x: p0.cx; y: p0.cy; controlX: c3.cx; controlY: c3.cy },
+ PathMove { x: p4.cx; y: p4.cy; },
+ PathLine { x: p5.cx; y: p5.cy; },
+ PathLine { x: p6.cx; y: p6.cy; },
+ PathLine { x: p7.cx; y: p7.cy; },
+ PathLine { x: p4.cx; y: p4.cy; }
+ ]
+
+ ControlPoint {
+ id: p0
+ cx: 200
+ cy: 200
+ }
+ ControlPoint {
+ id: c0
+ color: "blue"
+ cx: 600
+ cy: 1000
+ }
+ ControlPoint {
+ id: p1
+ cx: 1000
+ cy: 200
+ }
+ ControlPoint {
+ id: c1
+ color: "blue"
+ cx: -100
+ cy: 200
+ }
+ ControlPoint {
+ id: p2
+ color: "red"
+ cx: 200
+ cy: 1000
+ }
+ ControlPoint {
+ id: c2
+ color: "blue"
+ cx: -100
+ cy: 1000
+ }
+ ControlPoint {
+ id: p3
+ color: "red"
+ cx: -100
+ cy: 500
+ }
+ ControlPoint {
+ id: c3
+ color: "blue"
+ cx: -300
+ cy: 200
+ }
+
+ ControlPoint {
+ id: p4
+ color: "green"
+ cx: 2000
+ cy: 200
+ }
+ ControlPoint {
+ id: p5
+ color: "green"
+ cx: 2500
+ cy: 700
+ }
+ ControlPoint {
+ id: p6
+ color: "green"
+ cx: 2000
+ cy: 700
+ }
+ ControlPoint {
+ id: p7
+ color: "green"
+ cx: 2500
+ cy: 200
+ }
+
+ Text {
+ anchors.centerIn: p0
+ text: "p0"
+ }
+ Text {
+ anchors.centerIn: p1
+ text: "p1"
+ }
+ Text {
+ anchors.centerIn: p2
+ text: "p2"
+ }
+ Text {
+ anchors.centerIn: p3
+ text: "p3"
+ }
+ Text {
+ anchors.centerIn: c0
+ text: "c0"
+ }
+ Text {
+ anchors.centerIn: c1
+ text: "c1"
+ }
+ Text {
+ anchors.centerIn: c2
+ text: "c2"
+ }
+ Text {
+ anchors.centerIn: c3
+ text: "c3"
+ }
+ Text {
+ anchors.centerIn: p4
+ text: "p4"
+ }
+ Text {
+ anchors.centerIn: p5
+ text: "p5"
+ }
+ Text {
+ anchors.centerIn: p6
+ text: "p6"
+ }
+ Text {
+ anchors.centerIn: p7
+ text: "p7"
+ }
+}
diff --git a/tests/manual/painterpathquickshape/Intersect2.qml b/tests/manual/painterpathquickshape/Intersect2.qml
new file mode 100644
index 0000000000..407de52539
--- /dev/null
+++ b/tests/manual/painterpathquickshape/Intersect2.qml
@@ -0,0 +1,136 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ delegate: [
+ PathMove { x: p0.cx; y: p0.cy },
+ PathQuad { x: p1.cx; y: p1.cy; controlX: c0.cx; controlY: c0.cy },
+ PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy },
+ PathQuad { x: p0.cx; y: p0.cy; controlX: c2.cx; controlY: c2.cy },
+ PathMove { x: p3.cx; y: p3.cy },
+ PathQuad { x: p4.cx; y: p4.cy; controlX: c3.cx; controlY: c3.cy },
+ PathQuad { x: p5.cx; y: p5.cy; controlX: c4.cx; controlY: c4.cy },
+ PathQuad { x: p3.cx; y: p3.cy; controlX: c5.cx; controlY: c5.cy }
+ ]
+
+ ControlPoint {
+ id: p0
+ cx: 600
+ cy: 200
+ }
+ ControlPoint {
+ id: c0
+ color: "blue"
+ cx: 1000
+ cy: 600
+ }
+ ControlPoint {
+ id: p1
+ cx: 600
+ cy: 1000
+ }
+ ControlPoint {
+ id: c1
+ color: "blue"
+ cx: 200
+ cy: 1200
+ }
+ ControlPoint {
+ id: p2
+ cx: 200
+ cy: 600
+ }
+ ControlPoint {
+ id: c2
+ color: "blue"
+ cx: 200
+ cy: 200
+ }
+
+ ControlPoint {
+ id: p3
+ cx: 1000
+ cy: 200
+ }
+ ControlPoint {
+ id: c3
+ color: "blue"
+ cx: 1400
+ cy: 600
+ }
+ ControlPoint {
+ id: p4
+ cx: 1000
+ cy: 1000
+ }
+ ControlPoint {
+ id: c4
+ color: "blue"
+ cx: 600
+ cy: 1200
+ }
+ ControlPoint {
+ id: p5
+ cx: 600
+ cy: 600
+ }
+ ControlPoint {
+ id: c5
+ color: "blue"
+ cx: 200
+ cy: -200
+ }
+
+
+ Text {
+ anchors.centerIn: p0
+ text: "p0"
+ }
+ Text {
+ anchors.centerIn: p1
+ text: "p1"
+ }
+ Text {
+ anchors.centerIn: p2
+ text: "p2"
+ }
+ Text {
+ anchors.centerIn: p3
+ text: "p3"
+ }
+ Text {
+ anchors.centerIn: p4
+ text: "p4"
+ }
+ Text {
+ anchors.centerIn: p5
+ text: "p5"
+ }
+ Text {
+ anchors.centerIn: c0
+ text: "c0"
+ }
+ Text {
+ anchors.centerIn: c1
+ text: "c1"
+ }
+ Text {
+ anchors.centerIn: c2
+ text: "c2"
+ }
+ Text {
+ anchors.centerIn: c3
+ text: "c3"
+ }
+ Text {
+ anchors.centerIn: c4
+ text: "c4"
+ }
+ Text {
+ anchors.centerIn: c5
+ text: "c5"
+ }
+}
diff --git a/tests/manual/painterpathquickshape/Mussel.qml b/tests/manual/painterpathquickshape/Mussel.qml
new file mode 100644
index 0000000000..5f487e2c08
--- /dev/null
+++ b/tests/manual/painterpathquickshape/Mussel.qml
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ delegate: [
+ PathMove { x: p1.cx; y: p1.cy },
+ PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy },
+ PathQuad { x: p3.cx; y: p3.cy; controlX: c2.cx; controlY: c2.cy },
+ PathLine { x: p1.cx; y: p1.cy }
+ ]
+
+ ControlPoint {
+ id: p1
+ cx: 200
+ cy: 200
+ }
+ ControlPoint {
+ id: c1
+ color: "blue"
+ cx: 600
+ cy: 0
+ }
+ ControlPoint {
+ id: p2
+ cx: 1000
+ cy: 200
+ }
+ ControlPoint {
+ id: c2
+ color: "blue"
+ cx: 1000
+ cy: 1000
+ }
+ ControlPoint {
+ id: p3
+ cx: 200
+ cy: 1000
+ }
+}
diff --git a/tests/manual/painterpathquickshape/OFL-FAQ.txt b/tests/manual/painterpathquickshape/OFL-FAQ.txt
new file mode 100644
index 0000000000..f351a36568
--- /dev/null
+++ b/tests/manual/painterpathquickshape/OFL-FAQ.txt
@@ -0,0 +1,534 @@
+OFL FAQ - Frequently Asked Questions about the SIL Open Font License (OFL)
+Version 1.1-update1 - 31 March 2009
+(See http://scripts.sil.org/OFL for updates)
+
+
+1 ABOUT USING AND DISTRIBUTING FONTS LICENSED UNDER THE OFL
+
+1.1 Can I use the fonts in any publication, even embedded in the file?
+Yes. You may use them like most other fonts, but unlike some fonts you may
+include an embedded subset of the fonts in your document. Such use does not
+require you to include this license or other files (listed in OFL condition 2),
+nor does it require any type of acknowledgement within the publication. Some
+mention of the font name within the publication information (such as in a
+colophon) is usually appreciated. If you wish to include the complete font as a
+separate file, you should distribute the full font package, including all
+existing acknowledgements, and comply with the OFL conditions. Of course,
+referencing or embedding an OFL font in any document does not change the
+license of the document itself. The requirement for fonts to remain under the
+OFL does not apply to any document created using the fonts and their
+derivatives. Similarly, creating any kind of graphic using a font under OFL
+does not make the resulting artwork subject to the OFL.
+
+1.2 Can I make web pages using these fonts?
+Yes! Go ahead! Using CSS (Cascading Style Sheets) is recommended. Direct usage
+of fonts retrieved from a remote server - also referred to as font linking -
+using cross-platform open standards like @font-face is encouraged. This is
+considered to be use and distribution for which the OFL explicitly grants
+permission. The font file itself is not embedded in the webpage but referenced
+through a web address (i.e. a URI regardless of file format or access protocol)
+which will cause the browser to retrieve and use the corresponding font to
+render the webpage. This usage scenario is different from font embedding within
+a document (i.e. a PDF) where the font or some of its elements become part of
+the document. Note that embedding in a document is also permitted by the
+license as indicated in 1.1. (See 1.10 for details related to URL-based access
+restrictions methods or DRM mechanisms).
+
+1.3 Can I make the fonts available to others from my web site?
+Yes, as long as you meet the conditions of the license (do not sell by itself,
+include the necessary files, include the necessary copyright and license
+information, rename Modified Versions, do not abuse the Author(s)' name(s) and
+do not sublicense). In the case where you are hosting fonts to be served on the
+web, make sure the file contains the needed copyright notice(s) and licensing
+information in its metadata. Please double-check the accuracy of every field to
+prevent contradictory information. If you are making the font available for use
+via the @font-face open standard, putting this information in the standard font
+metadata fields is sufficient. Other font formats, including EOT and proposed
+superior alternatives, already provide fields for this information.
+
+1.4 Can the fonts be included with Free/Libre and Open Source Software
+collections such as GNU/Linux and BSD distributions?
+Yes! Fonts licensed under the OFL can be freely aggregated with software under
+FLOSS (Free/Libre and Open Source Software) licenses. Since fonts are much more
+useful aggregated to than merged with existing software, possible
+incompatibility with existing software licenses is not a problem. You can also
+repackage the fonts and the accompanying components in a .rpm or .deb package
+and include them in distro CD/DVDs and online repositories.
+
+1.5 I want to distribute the fonts with my program. Does this mean my program
+also has to be free/libre and open source software?
+No. Only the portions based on the Font Software are required to be released
+under the OFL. The intent of the license is to allow aggregation or bundling
+with software under restricted licensing as well.
+
+1.6 Can I include the fonts on a CD of freeware or commercial fonts?
+Yes, as long some other font or software is also on the disk, so the OFL font
+is not sold by itself.
+
+1.7 Can I sell a software package that includes these fonts?
+Yes, you can do this with both the Original Version and a Modified Version.
+Examples of bundling made possible by the OFL would include: word processors,
+design and publishing applications, training and educational software,
+edutainment software, etc.
+
+1.8 Why won't the OFL let me sell the fonts alone?
+The intent is to keep people from making money by simply redistributing the
+fonts. The only people who ought to profit directly from the fonts should be
+the original authors, and those authors have kindly given up potential direct
+income to distribute their fonts under the OFL. Please honor and respect their
+contribution!
+
+1.9 I've come across a font released under the OFL. How can I easily get more
+information about the Original Version? How can I know where it stands compared
+to the Original Version or other Modified Versions?
+Consult the copyright statement(s) in the license for ways to contact the
+original authors. Consult the FONTLOG for information on how the font differs
+from the Original Version, and get in touch with the various contributors via
+the information in the acknowledgment section. Please consider using the
+Original Versions of the fonts whenever possible.
+
+1.10 What do you mean in condition 4? Can you provide examples of abusive
+promotion / endorsement / advertisement vs. normal acknowledgement?
+The intent is that the goodwill and reputation of the author(s) should not be
+used in a way that makes it sound like the original author(s) endorse or
+approve of a specific Modified Version or software bundle. For example, it
+would not be right to advertise a word processor by naming the author(s) in a
+listing of software features, or to promote a Modified Version on a web site by
+saying "designed by ...". However, it would be appropriate to acknowledge the
+author(s) if your software package has a list of people who deserve thanks. We
+realize that this can seem to be a gray area, but the standard used to judge an
+acknowledgement is that if the acknowledgement benefits the author(s) it is
+allowed, but if it primarily benefits other parties, or could reflect poorly on
+the author(s), then it is not.
+
+1.11 Can Font Software released under the OFL be subject to URL-based access
+restrictions methods or DRM mechanisms?
+Yes, but these issues are out-of-scope for the OFL. The license itself neither
+encourages their use nor prohibits them since such mechanisms are not
+implemented in the components of the Font Software but through external
+software. Such restrictions are put in place for many different purposes
+corresponding to various usage scenarios. One common example is to limit
+potentially dangerous cross-site scripting attacks. However, in the spirit of
+libre/open fonts and unrestricted writing systems, we strongly encourage open
+sharing and reuse of OFL fonts, and the establishment of an environment where
+such restrictions are unnecessary. Note that whether you wish to use such
+mechanisms or you prefer not to, you must still abide by the rules set forth by
+the OFL when using fonts released by their authors under this license.
+Derivative fonts must be licensed under the OFL, even if they are part of a
+service for which you charge fees and/or for which access to source code is
+restricted. You may not sell the fonts on their own - they must be part of a
+larger software package or bundle. For example, even if the OFL font is
+distributed in a software package or via an online service using a DRM
+mechanism, the user would still have the right to extract that font, use,
+study, modify and redistribute it under the OFL.
+
+1.12 What about distributing fonts with a document? Within a compressed folder
+structure like an OpenDocument file (.odt) for example? Is it redistribution,
+bundling or embedding?
+The vast majority of the time, documents circulated in electronic form
+reference a font name which will match the corresponding font on the target
+system but do not carry the font within themselves. There may, however, be some
+cases where you need to bundle a font with the document. Certain document
+formats may allow the inclusion of an unmodified font within their file
+structure which consists of a compressed folder containing the various
+resources forming the document (such as pictures and thumbnails). Including
+fonts within such a structure is understood as being different from embedding
+but rather similar to bundling (or mere aggregation) for which the licensing
+makes explicit provision. In this case the font is conveyed unchanged whereas
+embedding a font transforms it from the original format. The OFL does not allow
+anyone to extract the font from such a structure to then redistribute it under
+another license. The explicit permission to redistribute and embed does not
+cancel the requirement for the Font Software to remain under the license chosen
+by its Author(s).
+
+1.13 If OFL fonts are extracted from a document in which they are embedded
+(such as a PDF file), what can be done with them? Is this a risk to Author(s)?
+The few utilities that can extract fonts embedded in a PDF will only output
+limited amounts of outlines - not a complete font. To create a working font
+from this method is much more difficult than finding the source of the original
+OFL font. So there is little chance that an OFL font would be extracted and
+redistributed inappropriately through this method. Even so, copyright laws
+address any misrepresentation of authorship. All Font Software released under
+the OFL and marked as such by the Author(s) is intended to remain under this
+license regardless of the distribution method, and cannot be redistributed
+under any other license. We strongly discourage any font extraction - we
+recommend directly using the font sources instead - but if you extract font
+outlines from a document please be considerate, use your common sense and
+respect the work of the Author(s) and the licensing model.
+
+1.14 What about sharing OFL fonts with friends on a CD, DVD or USB stick?
+You are very welcome to share open fonts with friends, family and colleagues on
+such removable media. Please make sure that you share and share-alike as much
+as possible from what the Author(s) released and that you don't strip away
+useful information which may not be present in the binary font files
+themselves. Just remember that in the case where you sell the font, it has to
+come bundled with software.
+
+
+2 ABOUT MODIFYING OFL LICENSED FONTS
+
+2.1 Can I change the fonts? Are there any limitations to what things I can and
+cannot change?
+You are allowed to change anything, as long as such changes do not violate the
+terms of the license. In other words, you are not allowed to remove the
+copyright statement(s) from the font, but you could add additional information
+into it that covers your contribution.
+
+2.2 I have a font that needs a few extra glyphs - can I take them from an OFL
+licensed font and copy them into mine?
+Yes, but if you distribute that font to others it must be under the OFL, and
+include the information mentioned in condition 2 of the license.
+
+2.3 Can I charge people for my additional work? In other words, if I add a
+bunch of special glyphs and/or OpenType/Graphite code, can I sell the enhanced
+font?
+Not by itself. Derivative fonts must be released under the OFL and cannot be
+sold by themselves. It is permitted, however, to include them in a larger
+software package (such as text editors, office suites or operating systems),
+even if the larger package is sold. In that case, you are strongly encouraged,
+but not required, to also make that derived font easily and freely available
+outside of the larger package.
+
+2.4 Can I pay someone to enhance the fonts for my use and distribution?
+Yes. This is a good way to fund the further development of the fonts. Keep in
+mind, however, that if the font is distributed to others it must be under the
+OFL. You won't be able to recover your investment by exclusively selling the
+font, but you will be making a valuable contribution to the community. Please
+remember how you have benefitted from the contributions of others.
+
+2.5 I need to make substantial revisions to the font to make it work with my
+program. It will be a lot of work, and a big investment, and I want to be sure
+that it can only be distributed with my program. Can I restrict its use?
+No. If you redistribute a Modified Version of the font it must be under the
+OFL. You may not restrict it in any way. This is intended to ensure that all
+released improvements to the fonts become available to everyone. But you will
+likely get an edge over competitors by being the first to distribute a bundle
+with the enhancements. Again, please remember how you have benefitted from the
+contributions of others.
+
+2.6 Do I have to make any derivative fonts (including extended source files,
+build scripts, documentation, etc.) publicly available?
+No, but please do share your improvements with others. You may find that you
+receive more than what you gave in return.
+
+2.7 Why can't I use the Reserved Font Name(s) in my derivative font names? I'd
+like people to know where the design came from.
+The best way to acknowledge the source of the design is to thank the original
+authors and any other contributors in the files that are distributed with your
+revised font (although no acknowledgement is required). The FONTLOG is a
+natural place to do this. Reserved Font Name(s) ensure that the only fonts that
+have the original names are the unmodified Original Versions. This allows
+designers to maintain artistic integrity while allowing collaboration to
+happen. It eliminates potential confusion and name conflicts. When choosing a
+name be creative and avoid names that reuse almost all the same letters in the
+same order or sound like the original. Keep in mind that the Copyright
+Holder(s) can allow a specific trusted partner to use Reserved Font Name(s)
+through a separate written agreement.
+
+2.8 What do you mean by "primary name as presented to the user"? Are you
+referring to the font menu name?
+Yes, the requirement to change the visible name used to differentiate the font
+from others applies to the font menu name and other mechanisms to specify a
+font in a document. It would be fine, for example, to keep a text reference to
+the original fonts in the description field, in your modified source file or in
+documentation provided alongside your derivative as long as no one could be
+confused that your modified source is the original. But you cannot use the
+Reserved Font Names in any way to identify the font to the user (unless the
+Copyright Holder(s) allow(s) it through a separate agreement; see section 2.7).
+Users who install derivatives ("Modified Versions") on their systems should not
+see any of the original names ("Reserved Font Names") in their font menus, for
+example. Again, this is to ensure that users are not confused and do not
+mistake a font for another and so expect features only another derivative or
+the Original Version can actually offer. Ultimately, creating name conflicts
+will cause many problems for the users as well as for the designer of both the
+Original and Modified versions, so please think ahead and find a good name for
+your own derivative. Font substitution systems like fontconfig, or
+application-level font fallback configuration within OpenOffice.org or Scribus,
+will also get very confused if the name of the font they are configured to
+substitute to actually refers to another physical font on the user's hard
+drive. It will help everyone if Original Versions and Modified Versions can
+easily be distinguished from one another and from other derivatives. The
+substitution mechanism itself is outside the scope of the license. Users can
+always manually change a font reference in a document or set up some kind of
+substitution at a higher level but at the lower level the fonts themselves have
+to respect the Reserved Font Name(s) requirement to prevent ambiguity. If a
+substitution is currently active the user should be aware of it.
+
+2.9 Am I not allowed to use any part of the Reserved Font Names?
+You may not use the words of the font names, but you would be allowed to use
+parts of words, as long as you do not use any word from the Reserved Font Names
+entirely. We do not recommend using parts of words because of potential
+confusion, but it is allowed. For example, if "Foobar" was a Reserved Font
+Name, you would be allowed to use "Foo" or "bar", although we would not
+recommend it. Such an unfortunate choice would confuse the users of your fonts
+as well as make it harder for other designers to contribute.
+
+2.10 So what should I, as an author, identify as Reserved Font Names?
+Original authors are encouraged to name their fonts using clear, distinct
+names, and only declare the unique parts of the name as Reserved Font Names.
+For example, the author of a font called "Foobar Sans" would declare "Foobar"
+as a Reserved Font Name, but not "Sans", as that is a common typographical
+term, and may be a useful word to use in a derivative font name. Reserved Font
+Names should also be single words. A font called "Flowing River" should have
+Reserved Font Names "Flowing" and "River", not "Flowing River".
+
+2.11 Do I, as an author, have to identify any Reserved Font Names?
+No, but we strongly encourage you to do so. This is to avoid confusion between
+your work and Modified versions. You may, however, give certain trusted parties
+the right to use any of your Reserved Font Names through separate written
+agreements. For example, even if "Foobar" is a RFN, you could write up an
+agreement to give company "XYZ" the right to distribute a modified version with
+a name that includes "Foobar". This allows for freedom without confusion.
+
+2.12 Are any names (such as the main font name) reserved by default?
+No. That is a change to the license as of version 1.1. If you want any names to
+be Reserved Font Names, they must be specified after the copyright statement(s).
+
+2.13 What is this FONTLOG thing exactly?
+It has three purposes: 1) to provide basic information on the font to users and
+other developers, 2) to document changes that have been made to the font or
+accompanying files, either by the original authors or others, and 3) to provide
+a place to acknowledge the authors and other contributors. Please use it! See
+below for details on how changes should be noted.
+
+2.14 Am I required to update the FONTLOG?
+No, but users, designers and other developers might get very frustrated at you
+if you don't! People need to know how derivative fonts differ from the
+original, and how to take advantage of the changes, or build on them.
+
+
+3 ABOUT THE FONTLOG
+
+The FONTLOG can take a variety of formats, but should include these four
+sections:
+
+3.1 FONTLOG for <FontFamilyName>
+This file provides detailed information on the <FontFamilyName> Font Software.
+This information should be distributed along with the <FontFamilyName> fonts
+and any derivative works.
+
+3.2 Basic Font Information
+(Here is where you would describe the purpose and brief specifications for the
+font project, and where users can find more detailed documentation. It can also
+include references to how changes can be contributed back to the Original
+Version. You may also wish to include a short guide to the design, or a
+reference to such a document.)
+
+3.3 ChangeLog
+(This should list both major and minor changes, most recent first. Here are
+some examples:)
+
+7 February 2007 (Pat Johnson) <NewFontFamilyName> Version 1.3
+- Added Greek and Cyrillic glyphs
+- Released as "<NewFontFamilyName>"
+
+7 March 2006 (Fred Foobar) <NewFontFamilyName> Version 1.2
+- Tweaked contextual behaviours
+- Released as "<NewFontFamilyName>"
+
+1 Feb 2005 (Jane Doe) <NewFontFamilyName> Version 1.1
+- Improved build script performance and verbosity
+- Extended the smart code documentation
+- Corrected minor typos in the documentation
+- Fixed position of combining inverted breve below (U+032F)
+- Added OpenType/Graphite smart code for Armenian
+- Added Armenian glyphs (U+0531 -> U+0587)
+- Released as "<NewFontFamilyName>"
+
+1 Jan 2005 (Joe Smith) <FontFamilyName> Version 1.0
+- Initial release of font "<FontFamilyName>"
+
+3.4 Acknowledgements
+(Here is where contributors can be acknowledged.
+
+If you make modifications be sure to add your name (N), email (E), web-address
+(W) and description (D). This list is sorted by last name in alphabetical
+order.)
+
+N: Jane Doe
+E: jane@university.edu
+W: http://art.university.edu/projects/fonts
+D: Contributor - Armenian glyphs and code
+
+N: Fred Foobar
+E: fred@foobar.org
+W: http://foobar.org
+D: Contributor - misc Graphite fixes
+
+N: Pat Johnson
+E: pat@fontstudio.org
+W: http://pat.fontstudio.org
+D: Designer - Greek & Cyrillic glyphs based on Roman design
+
+N: Tom Parker
+E: tom@company.com
+W: http://www.company.com/tom/projects/fonts
+D: Engineer - original smart font code
+
+N: Joe Smith
+E: joe@fontstudio.org
+W: http://joe.fontstudio.org
+D: Designer - original Roman glyphs
+
+(Original authors can also include information here about their organization.)
+
+
+4 ABOUT MAKING CONTRIBUTIONS
+
+4.1 Why should I contribute my changes back to the original authors?
+It would benefit many people if you contributed back to what you've received.
+Providing your contributions and improvements to the fonts and other components
+(data files, source code, build scripts, documentation, etc.) could be a
+tremendous help and would encourage others to contribute as well and 'give
+back', which means you will have an opportunity to benefit from other people's
+contributions as well. Sometimes maintaining your own separate version takes
+more effort than merging back with the original. Be aware that any
+contributions, however, must be either your own original creation or work that
+you own, and you may be asked to affirm that clearly when you contribute.
+
+4.2 I've made some very nice improvements to the font, will you consider
+adopting them and putting them into future Original Versions?
+Most authors would be very happy to receive such contributions. Keep in mind
+that it is unlikely that they would want to incorporate major changes that
+would require additional work on their end. Any contributions would likely need
+to be made for all the fonts in a family and match the overall design and
+style. Authors are encouraged to include a guide to the design with the fonts.
+It would also help to have contributions submitted as patches or clearly marked
+changes (the use of smart source revision control systems like subversion, svk,
+mercurial, git or bzr is a good idea). Examples of useful contributions are bug
+fixes, additional glyphs, stylistic alternates (and the smart font code to
+access them) or improved hinting.
+
+4.3 How can I financially support the development of OFL fonts?
+It is likely that most authors of OFL fonts would accept financial
+contributions - contact them for instructions on how to do this. Such
+contributions would support future development. You can also pay for others to
+enhance the fonts and contribute the results back to the original authors for
+inclusion in the Original Version.
+
+
+5 ABOUT THE LICENSE
+
+5.1 I see that this is version 1.1 of the license. Will there be later changes?
+Version 1.1 is the first minor revision of the OFL. We are confident that
+version 1.1 will meet most needs, but are open to future improvements. Any
+revisions would be for future font releases, and previously existing licenses
+would remain in effect. No retroactive changes are possible, although the
+Copyright Holder(s) can re-release the font under a revised OFL. All versions
+will be available on our web site: http://scripts.sil.org/OFL.
+
+5.2 Can I use the SIL Open Font License for my own fonts?
+Yes! We heartily encourage anyone to use the OFL to distribute their own
+original fonts. It is a carefully constructed license that allows great freedom
+along with enough artistic integrity protection for the work of the authors as
+well as clear rules for other contributors and those who redistribute the
+fonts. Some additional information about using the OFL is included at the end
+of this FAQ.
+
+5.3 Does this license restrict the rights of the Copyright Holder(s)?
+No. The Copyright Holder(s) still retain(s) all the rights to their creation;
+they are only releasing a portion of it for use in a specific way. For example,
+the Copyright Holder(s) may choose to release a 'basic' version of their font
+under the OFL, but sell a restricted 'enhanced' version. Only the Copyright
+Holder(s) can do this.
+
+5.4 Is the OFL a contract or a license?
+The OFL is a license and not a contract and so does not require you to sign it
+to have legal validity. By using, modifying and redistributing components under
+the OFL you indicate that you accept the license.
+
+5.5 How about translating the license and the FAQ into other languages?
+SIL certainly recognises the need for people who are not familiar with English
+to be able to understand the OFL and this FAQ better in their own language.
+Making the license very clear and readable is a key goal of the OFL.
+
+If you are an experienced translator, you are very welcome to help by
+translating the OFL and its FAQ so that designers and users in your language
+community can understand the license better. But only the original English
+version of the license has legal value and has been approved by the community.
+Translations do not count as legal substitutes and should only serve as a way
+to explain the original license. SIL - as the author and steward of the license
+for the community at large - does not approve any translation of the OFL as
+legally valid because even small translation ambiguities could be abused and
+create problems.
+
+We give permission to publish unofficial translations into other languages
+provided that they comply with the following guidelines:
+
+- put the following disclaimer in both English and the target language stating
+clearly that the translation is unofficial:
+
+"This is an unofficial translation of the SIL Open Font License into $language.
+It was not published by SIL International, and does not legally state the
+distribution terms for fonts that use the OFL. A release under the OFL is only
+valid when using the original English text.
+
+However, we recognize that this unofficial translation will help users and
+designers not familiar with English to understand the SIL OFL better and make
+it easier to use and release font families under this collaborative font design
+model. We encourage designers who consider releasing their creation under the
+OFL to read the FAQ in their own language if it is available.
+
+Please go to http://scripts.sil.org/OFL for the official version of the license
+and the accompanying FAQ."
+
+- keep your unofficial translation current and update it at our request if
+needed, for example if there is any ambiguity which could lead to confusion.
+
+If you start such a unofficial translation effort of the OFL and its
+accompanying FAQ please let us know, thank you.
+
+
+6 ABOUT SIL INTERNATIONAL
+
+6.1 Who is SIL International and what does it do?
+SIL International is a worldwide faith-based education and development
+organization (NGO) that studies, documents, and assists in developing the
+world's lesser-known languages through literacy, linguistics, translation, and
+other academic disciplines. SIL makes its services available to all without
+regard to religious belief, political ideology, gender, race, or ethnic
+background. SIL's members and volunteers share a Christian commitment.
+
+6.2 What does this have to do with font licensing?
+The ability to read, write, type and publish in one's own language is one of
+the most critical needs for millions of people around the world. This requires
+fonts that are widely available and support lesser-known languages. SIL
+develops - and encourages others to develop - a complete stack of writing
+systems implementation components available under open licenses. This open
+stack includes input methods, smart fonts, smart rendering libraries and smart
+applications. There has been a need for a common open license that is
+specifically applicable to fonts and related software (a crucial component of
+this stack) so SIL developed the SIL Open Font License with the help of the
+FLOSS community.
+
+6.3 How can I contact SIL?
+Our main web site is: http://www.sil.org/
+Our site about complex scripts is: http://scripts.sil.org/
+Information about this license (including contact email information) is at:
+http://scripts.sil.org/OFL
+
+
+7 ABOUT USING THE OFL FOR YOUR ORIGINAL FONTS
+
+If you want to release your fonts under the OFL, you only need to do the
+following:
+
+7.1 Put your copyright and reserved font names information in the beginning of
+the main OFL file (simply use the dedicated placeholders).
+7.2 Put your copyright and the OFL references in your various font files (such
+as in the copyright, license and description fields) and in your other
+components (build scripts, glyph databases, documentation, rendering samples,
+etc). Accurate metadata in your font files is beneficial to you as an
+increasing number of applications are exposing this information to the user.
+For example, clickable links can bring users back to your website and let them
+know about other work you have done or services you provide. Depending on the
+format of your fonts and sources, you can use template human-readable headers
+or machine-readable metadata.
+7.3 Write an initial FONTLOG for your font and include it in the release
+package.
+7.4 Include the OFL license file in your release package.
+7.5 We also highly recommend you include the relevant practical documentation
+on the license by putting the OFL-FAQ in your package.
+7.6 If you wish, you can use the OFL Graphics on your web page.
+
+That's all. If you have any more questions please get in touch with us.
diff --git a/tests/manual/painterpathquickshape/OFL.txt b/tests/manual/painterpathquickshape/OFL.txt
new file mode 100644
index 0000000000..8dce0ad901
--- /dev/null
+++ b/tests/manual/painterpathquickshape/OFL.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2011, Graziano Capelli (air@shweb.it; femtosoft@libero.it).
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/tests/manual/painterpathquickshape/README.md b/tests/manual/painterpathquickshape/README.md
new file mode 100644
index 0000000000..8ae04115ab
--- /dev/null
+++ b/tests/manual/painterpathquickshape/README.md
@@ -0,0 +1,7 @@
+SVGs included:
+ peace_victory.svg: Public Domain by OpenClipart (https://freesvg.org/peace-sign)
+ hand-print.svg: Public Domain by OpenClipart (https://freesvg.org/palm-print-in-black-and-white)
+ 1535737773.svg: Public Domain by publicdomainvectors.org (https://freesvg.org/piece-of-cake-4)
+
+Fonts included:
+ Graziano.ttf: by Graziano Capelli (https://fontlibrary.org/en/font/graziano), OFL (SIL Open Font License)
diff --git a/tests/manual/painterpathquickshape/SimpleShape.qml b/tests/manual/painterpathquickshape/SimpleShape.qml
new file mode 100644
index 0000000000..b5d2226cb4
--- /dev/null
+++ b/tests/manual/painterpathquickshape/SimpleShape.qml
@@ -0,0 +1,115 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ delegate: [
+ // A triangle
+ PathPolyline {
+ id: ppl
+ path: [ Qt.point(150.0, 100.0),
+ Qt.point(1250.0, 150.0),
+ Qt.point(100.0, 1000.0),
+ Qt.point(150, 100)
+ ]
+ },
+ // A very narrow shape with one convex and one concave curve
+ PathMove { x: 600; y: 1200},
+ PathQuad { x: 800; y: 1200; controlX: 700; controlY: 600 },
+ PathQuad { x: 600; y: 1200; controlX: 700; controlY: 700 },
+
+ // A more complex path with editable points
+ PathMove { x: p1.cx; y: p1.cy },
+ PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy },
+ PathQuad { x: p3.cx; y: p3.cy; controlX: c2.cx; controlY: c2.cy },
+ PathQuad { x: p4.cx; y: p4.cy; controlX: c3.cx; controlY: c3.cy },
+ PathLine { x: p5.cx; y: p5.cy },
+ PathQuad { x: p6.cx; y: p6.cy; controlX: c5.cx; controlY: c5.cy },
+ PathQuad { x: p7.cx; y: p7.cy; controlX: c6.cx; controlY: c6.cy },
+ PathQuad { x: p8.cx; y: p8.cy; controlX: c7.cx; controlY: c7.cy }
+ ]
+
+ // Control points for the editable part:
+ // Curve p1-c1-p2, Curve p2-c2-p3, Curve p3-c3-p4
+ // Line p4-p5, Curve p5-c5-p6, Curve p6-c6-p7, Curve p7-c7-p8
+
+ ControlPoint {
+ id: p1
+ cx: 100
+ cy: 1000
+ }
+ ControlPoint {
+ id: c1
+ color: "blue"
+ cx: 200
+ cy: 1500
+ }
+ ControlPoint {
+ id: p2
+ cx: 700
+ cy: 1500
+ }
+ ControlPoint {
+ id: c2
+ color: "blue"
+ cx: 1200
+ cy: 1500
+ }
+ ControlPoint {
+ id: p3
+ cx: 1200
+ cy: 1000
+ }
+ ControlPoint {
+ id: c3
+ color: "blue"
+ cx: 1100
+ cy: 700
+ }
+ ControlPoint {
+ id: p4
+ cx: 800
+ cy: 600
+ }
+ ControlPoint {
+ id: p5
+ cx: 800
+ cy: 800
+ }
+ ControlPoint {
+ id: c5
+ color: "blue"
+ cx: 1000
+ cy: 600
+ }
+ ControlPoint {
+ id: p6
+ cx: 1000
+ cy: 1000
+ }
+ ControlPoint {
+ id: c6
+ color: "blue"
+ cx: 1000
+ cy: 1300
+ }
+ ControlPoint {
+ id: p7
+ cx: 700
+ cy: 1300
+ }
+ ControlPoint {
+ id: c7
+ color: "blue"
+ cx: 400
+ cy: 1300
+ }
+ ControlPoint {
+ id: p8
+ cx: 400
+ cy: 1000
+ }
+
+}
diff --git a/tests/manual/painterpathquickshape/SmallPolygon.qml b/tests/manual/painterpathquickshape/SmallPolygon.qml
new file mode 100644
index 0000000000..f298960358
--- /dev/null
+++ b/tests/manual/painterpathquickshape/SmallPolygon.qml
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ delegate: [
+ PathPolyline {
+ path: [ point1.pt,
+ point2.pt,
+ point3.pt,
+ point4.pt,
+ point5.pt,
+ point1.pt
+ ]
+
+ }
+ ]
+
+ ControlPoint {
+ id: point1
+ cx: 400
+ cy: 900
+ }
+ ControlPoint {
+ id: point2
+ cx: 1500
+ cy: 600
+ }
+ ControlPoint {
+ id: point3
+ cx: 2500
+ cy: 1100
+ }
+ ControlPoint {
+ id: point4
+ cx: 3000
+ cy: 1100
+ }
+ ControlPoint {
+ id: point5
+ cx: 2000
+ cy: 1900
+ }
+
+}
diff --git a/tests/manual/painterpathquickshape/Squircle.qml b/tests/manual/painterpathquickshape/Squircle.qml
new file mode 100644
index 0000000000..2ad6181eeb
--- /dev/null
+++ b/tests/manual/painterpathquickshape/Squircle.qml
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ delegate: [
+ PathMove { x: p1.cx; y: p1.cy },
+ PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy },
+ PathQuad { x: p3.cx; y: p3.cy; controlX: c2.cx; controlY: c2.cy },
+ PathQuad { x: p4.cx; y: p4.cy; controlX: c3.cx; controlY: c3.cy },
+ //PathQuad { x: 600; y: 100; controlX: 100; controlY: 100 },
+ PathLine { x: p5.cx; y: p5.cy }
+ ]
+
+ ControlPoint {
+ id: p1
+ cx: 600
+ cy: 100
+ }
+ ControlPoint {
+ id: c1
+ color: "blue"
+ cx: 1100
+ cy: 100
+ }
+ ControlPoint {
+ id: p2
+ cx: 1100
+ cy: 600
+ }
+ ControlPoint {
+ id: c2
+ color: "blue"
+ cx: 1100
+ cy: 1100
+ }
+ ControlPoint {
+ id: p3
+ cx: 600
+ cy: 1100
+ }
+ ControlPoint {
+ id: c3
+ color: "blue"
+ cx: 100
+ cy: 1100
+ }
+ ControlPoint {
+ id: p4
+ cx: 100
+ cy: 600
+ }
+ ControlPoint {
+ id: p5
+ cx: 600
+ cy: 330
+ }
+}
diff --git a/tests/manual/painterpathquickshape/SvgShape.qml b/tests/manual/painterpathquickshape/SvgShape.qml
new file mode 100644
index 0000000000..21ae852cde
--- /dev/null
+++ b/tests/manual/painterpathquickshape/SvgShape.qml
@@ -0,0 +1,116 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Controls
+import QtQuick.Dialogs
+import io.qt
+
+Item {
+ id: topLevel
+ property var boundingRect: Qt.rect(0, 0, 100, 100)
+
+ Text {
+ id: fileNameLabel
+ x: boundingRect.x
+ y: boundingRect.bottom * controlPanel.scale + font.pixelSize*2
+ text: "Filename: " + pathLoader.source
+ }
+
+ ToolButton {
+ anchors.left: fileNameLabel.right
+ anchors.top: fileNameLabel.top
+ anchors.leftMargin: 10
+ text: "..."
+
+ onClicked: {
+ fileDialog.visible = true
+ }
+ }
+
+ property var children: []
+ SvgPathLoader {
+ id: pathLoader
+ function reload()
+ {
+ for (var j = 0; j < children.length; ++j) {
+ children[j].destroy()
+ }
+ children = []
+
+ let first = true
+ let pickShape = controlPanel.subShape
+ let pickOne = pickShape >= 0 && !controlPanel.subShapeGreaterThan
+ let pickGreater = pickShape >= 0 && controlPanel.subShapeGreaterThan
+ if (pickOne)
+ console.log("Creating SVG item", pickShape, "out of", pathLoader.paths.length)
+ else if (pickGreater)
+ console.debug("Creating " + (pathLoader.paths.length - pickShape) + " SVG items")
+ else
+ console.debug("Creating " + pathLoader.paths.length + " SVG items")
+ for (var i = 0; i < pathLoader.paths.length; ++i) {
+ if ((pickOne && pickShape !== i ) || (pickGreater && i < pickShape))
+ continue
+ var s = pathLoader.paths[i]
+ var fillColor = pathLoader.fillColors[i]
+ let strokeText = "";
+ let strokeColor = pathLoader.strokeColors[i]
+ let strokeWidth = pathLoader.strokeWidths[i]
+ let transform = pathLoader.transforms[i]
+ if (strokeColor) {
+ if (!strokeWidth)
+ strokeWidth = "1.0" // default value defined by SVG standard
+ strokeText = "strokeColor: \"" + strokeColor + "\"; strokeWidth: " + strokeWidth + ";"
+ }
+
+ let fillText = "";
+ if (fillColor)
+ fillText = "fillColor: \"" + fillColor + "\";\n";
+
+ var obj = Qt.createQmlObject("import QtQuick\nimport QtQuick.Shapes\n ControlledShape { \n"
+ + fillText
+ + "shapeTransform: Matrix4x4 { matrix: Qt.matrix4x4(" + transform + "); }\n"
+ + strokeText + "\n"
+ + "fillRule: ShapePath.WindingFill; delegate: [ PathSvg { path: \"" + s + "\"; } ] }\n",
+ topLevel, "SvgPathComponent_" + i)
+
+
+ children.push(obj)
+ if (first) {
+ topLevel.boundingRect = obj.boundingRect
+ first = false
+ } else {
+ var minX = Math.min(topLevel.boundingRect.x, obj.boundingRect.x)
+ var minY = Math.min(topLevel.boundingRect.y, obj.boundingRect.y)
+ var maxX = Math.max(topLevel.boundingRect.x + topLevel.boundingRect.width,
+ obj.boundingRect.x + obj.boundingRect.width)
+ var maxY = Math.max(topLevel.boundingRect.y + topLevel.boundingRect.height,
+ obj.boundingRect.y + obj.boundingRect.height)
+
+ topLevel.boundingRect = Qt.rect(minX, minY, maxX - minX, maxY - minY)
+ }
+ }
+ console.debug("Finished SVG")
+ }
+ onPathsChanged: reload()
+
+ Component.onCompleted: {
+ pathLoader.source = "qrc:/1535737773.svg"
+ }
+ }
+
+ Connections {
+ target: controlPanel
+ function onSubShapeChanged() { pathLoader.reload() }
+ }
+
+ FileDialog {
+ id: fileDialog
+ title: "Please choose a file"
+ onAccepted: {
+ pathLoader.source = fileDialog.selectedFile
+ fileDialog.visible = false
+ }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/TextShape.qml b/tests/manual/painterpathquickshape/TextShape.qml
new file mode 100644
index 0000000000..79caaa239b
--- /dev/null
+++ b/tests/manual/painterpathquickshape/TextShape.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ControlledShape {
+ fillRule: ShapePath.WindingFill
+ delegate: [
+ PathText {
+ text: "foobar"
+ font: fontDialog.selectedFont
+ }
+ ]
+
+ FontDialog {
+ id: fontDialog
+ currentFont.family: "Graziano"
+ currentFont.pixelSize: 500
+ }
+
+ Button {
+ anchors.top: parent.bottom
+ anchors.left: parent.left
+ text: "Select font"
+ onClicked: fontDialog.open()
+ }
+
+}
diff --git a/tests/manual/painterpathquickshape/arcDirection.qml b/tests/manual/painterpathquickshape/arcDirection.qml
new file mode 100644
index 0000000000..547888ff7c
--- /dev/null
+++ b/tests/manual/painterpathquickshape/arcDirection.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ Repeater {
+ anchors.fill: parent
+ model: 2
+ delegate: ControlledShape {
+ id: delegate
+
+ required property int index
+
+ anchors.fill: parent
+ fillColor: "transparent"
+ strokeColor: delegate.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ startX: 4
+ startY: 4
+
+ delegate: [
+ PathArc {
+ id: arc
+ x: 96
+ y: 96
+ radiusX: 100
+ radiusY: 100
+ direction: delegate.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/arcRotation.qml b/tests/manual/painterpathquickshape/arcRotation.qml
new file mode 100644
index 0000000000..174bb7f0c8
--- /dev/null
+++ b/tests/manual/painterpathquickshape/arcRotation.qml
@@ -0,0 +1,72 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ Repeater {
+ anchors.fill: parent
+ model: 2
+ delegate: ControlledShape {
+ id: delegate1
+
+ required property int index
+
+ fillColor: "transparent"
+ strokeColor: delegate1.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+ startX: 50
+ startY: 100
+
+ delegate: [
+ PathArc {
+ x: 150
+ y: 100
+ radiusX: 50
+ radiusY: 20
+ xAxisRotation: delegate1.index === 0 ? 0 : 45
+ }
+ ]
+ }
+ }
+
+ Repeater {
+ anchors.fill: parent
+ model: 2
+ delegate: ControlledShape {
+ id: delegate2
+
+ required property int index
+
+ width: 200
+ height: 200
+ anchors.centerIn: parent
+
+ fillColor: "transparent"
+ strokeColor: delegate2.index === 0 ? "red" : "blue"
+
+ startX: 50
+ startY: 100
+
+ delegate: [
+ PathArc {
+ x: 150
+ y: 100
+ radiusX: 50
+ radiusY: 20
+ xAxisRotation: delegate2.index === 0 ? 0 : 45
+ direction: PathArc.Counterclockwise
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/background.png b/tests/manual/painterpathquickshape/background.png
new file mode 100644
index 0000000000..9921459886
--- /dev/null
+++ b/tests/manual/painterpathquickshape/background.png
Binary files differ
diff --git a/tests/manual/painterpathquickshape/capStyles.qml b/tests/manual/painterpathquickshape/capStyles.qml
new file mode 100644
index 0000000000..886e867eb5
--- /dev/null
+++ b/tests/manual/painterpathquickshape/capStyles.qml
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ id: capTest
+ strokeColor: "green"
+ strokeWidth: 20
+ fillColor: "transparent"
+
+ property int capStyleIdx: 0
+ readonly property variant styles: [ ShapePath.FlatCap, ShapePath.SquareCap, ShapePath.RoundCap ]
+ readonly property variant styleTexts: [ qsTr("FlatCap"), qsTr("SquareCap"), qsTr("RoundCap") ]
+
+ capStyle: styles[capStyleIdx]
+
+ startX: 40
+ startY: 30
+ delegate: [
+ PathQuad {
+ x: 50
+ y: 80
+ controlX: 0
+ controlY: 80
+ },
+ PathLine {
+ x: 150
+ y: 80
+ },
+ PathQuad {
+ x: 160
+ y: 30
+ controlX: 200
+ controlY: 80
+ }
+ ]
+
+ Timer {
+ interval: 1000
+ repeat: true
+ running: true
+ onTriggered: capTest.capStyleIdx = (capTest.capStyleIdx + 1) % capTest.styles.length
+ }
+}
diff --git a/tests/manual/painterpathquickshape/cubicCurve.qml b/tests/manual/painterpathquickshape/cubicCurve.qml
new file mode 100644
index 0000000000..1580e0f41a
--- /dev/null
+++ b/tests/manual/painterpathquickshape/cubicCurve.qml
@@ -0,0 +1,103 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ id: shape
+
+ strokeWidth: 4
+ strokeColor: "black"
+
+ startX: 50
+ startY: 100
+ delegate: [
+ PathCubic {
+ x: 150
+ y: 100
+ control1X: cp1.x
+ control1Y: cp1.y
+ control2X: cp2.x
+ control2Y: cp2.y
+ }
+ ]
+ Rectangle {
+ id: cp1
+ color: "red"
+ width: 10
+ height: 10
+ SequentialAnimation {
+ loops: Animation.Infinite
+ running: true
+ NumberAnimation {
+ target: cp1
+ property: "x"
+ from: 0
+ to: flickable.width - cp1.width
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp1
+ property: "x"
+ from: flickable.width - cp1.width
+ to: 0
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp1
+ property: "y"
+ from: 0
+ to: flickable.height - cp1.height
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp1
+ property: "y"
+ from: flickable.height - cp1.height
+ to: 0
+ duration: 5000
+ }
+ }
+ }
+
+ Rectangle {
+ id: cp2
+ color: "blue"
+ width: 10
+ height: 10
+ x: flickable.width - width
+ SequentialAnimation {
+ loops: Animation.Infinite
+ running: true
+ NumberAnimation {
+ target: cp2
+ property: "y"
+ from: 0
+ to: flickable.height - cp2.height
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp2
+ property: "y"
+ from: flickable.height - cp2.height
+ to: 0
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp2
+ property: "x"
+ from: flickable.width - cp2.width
+ to: 0
+ duration: 5000
+ }
+ NumberAnimation {
+ target: cp2
+ property: "x"
+ from: 0
+ to: flickable.width - cp2.width
+ duration: 5000
+ }
+ }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/dashPattern.qml b/tests/manual/painterpathquickshape/dashPattern.qml
new file mode 100644
index 0000000000..23a7cbbc55
--- /dev/null
+++ b/tests/manual/painterpathquickshape/dashPattern.qml
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ id: shape
+ anchors.fill: parent
+ strokeWidth: 5
+ strokeColor: "blue"
+ strokeStyle: ShapePath.DashLine
+ //dashPattern: [ 1, 4, 4, 4 ]
+ fillColor: "lightBlue"
+ property real xr: 70
+ property real yr: 30
+ startX: shape.width / 2 - xr
+ startY: shape.height / 2 - yr
+
+ delegate: [
+ PathArc {
+ x: shape.width / 2 + shape.xr
+ y: shape.height / 2 + shape.yr
+ radiusX: shape.xr
+ radiusY: shape.yr
+ useLargeArc: true
+ },
+ PathArc {
+ x: shape.width / 2 - shape.xr
+ y: shape.height / 2 - shape.yr
+ radiusX: shape.xr
+ radiusY: shape.yr
+ useLargeArc: true
+ }
+ ]
+}
diff --git a/tests/manual/painterpathquickshape/debugpaintitem.cpp b/tests/manual/painterpathquickshape/debugpaintitem.cpp
new file mode 100644
index 0000000000..ff0ff8be42
--- /dev/null
+++ b/tests/manual/painterpathquickshape/debugpaintitem.cpp
@@ -0,0 +1,170 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "debugpaintitem.h"
+
+#include <QPainter>
+#include <QPen>
+
+DebugPaintItem::DebugPaintItem(QQuickItem *item)
+ : QQuickPaintedItem(item)
+{
+ connect(this, &DebugPaintItem::shapeChanged, this, &QQuickItem::update);
+ connect(this, &DebugPaintItem::colorChanged, this, &QQuickItem::update);
+ connect(this, &DebugPaintItem::pathScaleChanged, this, &QQuickItem::update);
+ connect(this, &DebugPaintItem::fillRuleChanged, this, &QQuickItem::update);
+ connect(this, &DebugPaintItem::strokeChanged, this, &QQuickItem::update);
+}
+
+void DebugPaintItem::setShape(QQuickPath *p)
+{
+ if (p == m_path)
+ return;
+
+ m_path = p;
+ emit shapeChanged();
+}
+
+QQuickPath *DebugPaintItem::shape() const
+{
+ return m_path;
+}
+
+void DebugPaintItem::handlePathChanged()
+{
+ update();
+}
+
+void DebugPaintItem::paint(QPainter *p)
+{
+ if (!isVisible())
+ return;
+
+ if (m_path != nullptr) {
+ QPainterPath painterPath = m_path->path();
+ painterPath.setFillRule(m_fillRule);
+
+ p->scale(m_pathScale, m_pathScale);
+ p->setRenderHint(QPainter::Antialiasing);
+ if (m_strokeColor.alpha() > 0) {
+ QPen pen(m_strokeStyle);
+ pen.setWidthF(m_strokeWidth);
+ pen.setCapStyle(m_capStyle);
+ pen.setJoinStyle(m_joinStyle);
+ pen.setColor(m_strokeColor);
+ p->setPen(pen);
+ } else {
+ p->setPen(Qt::NoPen);
+ }
+ p->setBrush(m_color);
+ p->drawPath(painterPath);
+ }
+}
+
+QColor DebugPaintItem::color() const
+{
+ return m_color;
+}
+
+void DebugPaintItem::setColor(const QColor &color)
+{
+ if (m_color == color)
+ return;
+
+ m_color = color;
+ emit colorChanged();
+}
+
+void DebugPaintItem::setPathScale(qreal pathScale)
+{
+ if (qFuzzyCompare(m_pathScale, pathScale))
+ return;
+
+ m_pathScale = pathScale;
+ emit pathScaleChanged();
+}
+
+qreal DebugPaintItem::pathScale() const
+{
+ return m_pathScale;
+}
+
+void DebugPaintItem::setFillRule(Qt::FillRule fillRule)
+{
+ if (m_fillRule == fillRule)
+ return;
+
+ m_fillRule = fillRule;
+ emit fillRuleChanged();
+}
+
+Qt::FillRule DebugPaintItem::fillRule() const
+{
+ return m_fillRule;
+}
+
+void DebugPaintItem::setStrokeColor(const QColor &strokeColor)
+{
+ if (m_strokeColor == strokeColor)
+ return;
+ m_strokeColor = strokeColor;
+ emit strokeChanged();
+}
+
+QColor DebugPaintItem::strokeColor() const
+{
+ return m_strokeColor;
+}
+
+void DebugPaintItem::setStrokeStyle(Qt::PenStyle strokeStyle)
+{
+ if (m_strokeStyle == strokeStyle)
+ return;
+
+ m_strokeStyle = strokeStyle;
+ emit strokeChanged();
+}
+
+Qt::PenStyle DebugPaintItem::strokeStyle() const
+{
+ return m_strokeStyle;
+}
+
+void DebugPaintItem::setJoinStyle(Qt::PenJoinStyle style)
+{
+ if (m_joinStyle == style)
+ return;
+ m_joinStyle = style;
+ emit strokeChanged();
+}
+
+Qt::PenJoinStyle DebugPaintItem::joinStyle() const
+{
+ return m_joinStyle;
+}
+
+void DebugPaintItem::setCapStyle(Qt::PenCapStyle style)
+{
+ if (m_capStyle == style)
+ return;
+ m_capStyle = style;
+ emit strokeChanged();
+}
+
+Qt::PenCapStyle DebugPaintItem::capStyle() const
+{
+ return m_capStyle;
+}
+
+void DebugPaintItem::setStrokeWidth(qreal w)
+{
+ if (qFuzzyCompare(m_strokeWidth, w))
+ return;
+ m_strokeWidth = w;
+ emit strokeChanged();
+}
+
+qreal DebugPaintItem::strokeWidth() const
+{
+ return m_strokeWidth;
+}
diff --git a/tests/manual/painterpathquickshape/debugpaintitem.h b/tests/manual/painterpathquickshape/debugpaintitem.h
new file mode 100644
index 0000000000..a8fb8fa8cb
--- /dev/null
+++ b/tests/manual/painterpathquickshape/debugpaintitem.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef DEBUGPAINTITEM_H
+#define DEBUGPAINTITEM_H
+
+#include <QQuickPaintedItem>
+#include <QtQuick/private/qquickpath_p.h>
+
+class DebugPaintItem : public QQuickPaintedItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPath *shape READ shape WRITE setShape NOTIFY shapeChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(qreal pathScale READ pathScale WRITE setPathScale NOTIFY pathScaleChanged)
+ Q_PROPERTY(Qt::FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged)
+
+ Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeChanged)
+ Q_PROPERTY(Qt::PenStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeChanged)
+ Q_PROPERTY(Qt::PenJoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY strokeChanged)
+ Q_PROPERTY(Qt::PenCapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY strokeChanged)
+ Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeChanged)
+public:
+ DebugPaintItem(QQuickItem *item = nullptr);
+
+ void setShape(QQuickPath *path);
+ QQuickPath *shape() const;
+
+ void setColor(const QColor &color);
+ QColor color() const;
+
+ void setPathScale(qreal pathScale);
+ qreal pathScale() const;
+
+ void setFillRule(Qt::FillRule filleRule);
+ Qt::FillRule fillRule() const;
+
+ QColor strokeColor() const;
+ void setStrokeColor(const QColor &strokeColor);
+
+ Qt::PenStyle strokeStyle() const;
+ void setStrokeStyle(Qt::PenStyle penStyle);
+
+ Qt::PenJoinStyle joinStyle() const;
+ void setJoinStyle(Qt::PenJoinStyle style);
+
+ Qt::PenCapStyle capStyle() const;
+ void setCapStyle(Qt::PenCapStyle style);
+
+ qreal strokeWidth() const;
+ void setStrokeWidth(qreal w);
+
+signals:
+ void shapeChanged();
+ void colorChanged();
+ void opacityChanged();
+ void pathScaleChanged();
+ void fillRuleChanged();
+ void strokeChanged();
+
+public slots:
+ void handlePathChanged();
+
+protected:
+ void paint(QPainter *p) override;
+
+private:
+ QQuickPath *m_path = nullptr;
+ QColor m_color = Qt::red;
+ qreal m_pathScale = 1.0;
+ Qt::FillRule m_fillRule = Qt::WindingFill;
+ Qt::PenStyle m_strokeStyle = Qt::NoPen;
+ QColor m_strokeColor = Qt::transparent;
+ qreal m_strokeWidth = 1.0;
+ Qt::PenCapStyle m_capStyle = Qt::FlatCap;
+ Qt::PenJoinStyle m_joinStyle = Qt::MiterJoin;
+};
+
+#endif // DEBUGPAINTITEM_H
diff --git a/tests/manual/painterpathquickshape/debugvisualizationcontroller.cpp b/tests/manual/painterpathquickshape/debugvisualizationcontroller.cpp
new file mode 100644
index 0000000000..633d028df7
--- /dev/null
+++ b/tests/manual/painterpathquickshape/debugvisualizationcontroller.cpp
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "debugvisualizationcontroller.h"
+#include <private/qquickshapecurverenderer_p.h>
+
+DebugVisualizationController::DebugVisualizationController(QObject *parent)
+ : QObject{parent}
+{
+}
+
+bool DebugVisualizationController::showCurves() const
+{
+ return m_showCurves;
+}
+
+void DebugVisualizationController::setShowCurves(bool newShowCurves)
+{
+ if (m_showCurves == newShowCurves)
+ return;
+ m_showCurves = newShowCurves;
+ update();
+ emit showCurvesChanged();
+}
+
+bool DebugVisualizationController::showWireframe() const
+{
+ return m_showWireframe;
+}
+
+void DebugVisualizationController::setWireframe(bool newShowWireframe)
+{
+ if (m_showWireframe == newShowWireframe)
+ return;
+ m_showWireframe = newShowWireframe;
+ update();
+ emit showWireframeChanged();
+}
+
+void DebugVisualizationController::update()
+{
+ int flags = (m_showCurves ? QQuickShapeCurveRenderer::DebugCurves : 0)
+ | (m_showWireframe ? QQuickShapeCurveRenderer::DebugWireframe : 0);
+ QQuickShapeCurveRenderer::setDebugVisualization(flags);
+ emit settingsChanged();
+}
diff --git a/tests/manual/painterpathquickshape/debugvisualizationcontroller.h b/tests/manual/painterpathquickshape/debugvisualizationcontroller.h
new file mode 100644
index 0000000000..337736b95b
--- /dev/null
+++ b/tests/manual/painterpathquickshape/debugvisualizationcontroller.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef DEBUGVISUALIZATIONCONTROLLER_H
+#define DEBUGVISUALIZATIONCONTROLLER_H
+
+#include <QObject>
+
+class DebugVisualizationController : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool showCurves READ showCurves WRITE setShowCurves NOTIFY showCurvesChanged)
+ Q_PROPERTY(bool showWireframe READ showWireframe WRITE setWireframe NOTIFY showWireframeChanged)
+
+public:
+ explicit DebugVisualizationController(QObject *parent = nullptr);
+
+ bool showCurves() const;
+ void setShowCurves(bool newShowCurves);
+
+ bool showWireframe() const;
+ void setWireframe(bool newShowWireframe);
+
+signals:
+ void showCurvesChanged();
+ void showWireframeChanged();
+ void settingsChanged();
+
+private:
+ void update();
+ bool m_showCurves = false;
+ bool m_showWireframe = false;
+};
+
+#endif // DEBUGVISUALIZATIONCONTROLLER_H
diff --git a/tests/manual/painterpathquickshape/ellipticalArcs.qml b/tests/manual/painterpathquickshape/ellipticalArcs.qml
new file mode 100644
index 0000000000..aa5bb591d7
--- /dev/null
+++ b/tests/manual/painterpathquickshape/ellipticalArcs.qml
@@ -0,0 +1,77 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ ControlledShape {
+ id: shape
+ anchors.top: parent.top
+ anchors.left: parent.left
+ width: parent.width
+ height: parent.height / 2
+
+ startX: 10
+ startY: 100
+ delegate: [
+ PathArc {
+ relativeX: 50
+ y: 100
+ radiusX: 25
+ radiusY: 25
+ },
+ PathArc {
+ relativeX: 50
+ y: 100
+ radiusX: 25
+ radiusY: 35
+ },
+ PathArc {
+ relativeX: 50
+ y: 100
+ radiusX: 25
+ radiusY: 60
+ },
+ PathArc {
+ relativeX: 50
+ y: 100
+ radiusX: 50
+ radiusY: 120
+ }
+ ]
+ }
+
+ ControlledShape {
+ width: parent.width
+ height: parent.height / 2
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+
+ fillColor: "transparent"
+ strokeColor: "darkBlue"
+ strokeWidth: 20
+ capStyle: ShapePath.RoundCap
+
+ delegate: [
+ PathAngleArc {
+ centerX: 65
+ centerY: 95
+ radiusX: 45
+ radiusY: 45
+ startAngle: -180
+ SequentialAnimation on sweepAngle {
+ loops: Animation.Infinite
+ NumberAnimation {
+ to: 360
+ duration: 2000
+ }
+ NumberAnimation {
+ to: 0
+ duration: 2000
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/manual/painterpathquickshape/fillRules.qml b/tests/manual/painterpathquickshape/fillRules.qml
new file mode 100644
index 0000000000..bb9def3b0f
--- /dev/null
+++ b/tests/manual/painterpathquickshape/fillRules.qml
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ id: star
+ strokeColor: "blue"
+ fillColor: "magenta"
+ strokeWidth: 2
+ delegate: [
+ PathMove {
+ x: 90
+ y: 50
+ },
+ PathLine {
+ x: 50 + 40 * Math.cos(0.8 * 1 * Math.PI)
+ y: 50 + 40 * Math.sin(0.8 * 1 * Math.PI)
+ },
+ PathLine {
+ x: 50 + 40 * Math.cos(0.8 * 2 * Math.PI)
+ y: 50 + 40 * Math.sin(0.8 * 2 * Math.PI)
+ },
+ PathLine {
+ x: 50 + 40 * Math.cos(0.8 * 3 * Math.PI)
+ y: 50 + 40 * Math.sin(0.8 * 3 * Math.PI)
+ },
+ PathLine {
+ x: 50 + 40 * Math.cos(0.8 * 4 * Math.PI)
+ y: 50 + 40 * Math.sin(0.8 * 4 * Math.PI)
+ },
+ PathLine {
+ x: 90
+ y: 50
+ }
+ ]
+ Timer {
+ interval: 2000
+ onTriggered: star.fillRule = (star.fillRule === ShapePath.OddEvenFill ? ShapePath.WindingFill : ShapePath.OddEvenFill)
+ repeat: true
+ running: true
+ }
+}
diff --git a/tests/manual/painterpathquickshape/gradientSpreadModes.qml b/tests/manual/painterpathquickshape/gradientSpreadModes.qml
new file mode 100644
index 0000000000..5f3d6b6d6a
--- /dev/null
+++ b/tests/manual/painterpathquickshape/gradientSpreadModes.qml
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ ControlledShape {
+ anchors.fill: parent
+ strokeColor: "transparent"
+ startX: 10
+ startY: 10
+
+ delegate: [
+ PathLine {
+ relativeX: 180
+ relativeY: 0
+ },
+ PathLine {
+ relativeX: 0
+ relativeY: 180
+ },
+ PathLine {
+ relativeX: -180
+ relativeY: 0
+ },
+ PathLine {
+ relativeX: 0
+ relativeY: -180
+ }
+ ]
+ }
+
+ Timer {
+ id: spreadTimer
+ interval: 3000
+ running: true
+ repeat: true
+ readonly property variant spreads: [ ShapeGradient.PadSpread, ShapeGradient.RepeatSpread, ShapeGradient.ReflectSpread ]
+ readonly property variant spreadTexts: [ qsTr("PadSpread"), qsTr("RepeatSpread"), qsTr("ReflectSpread") ]
+ property int spreadIdx: 0
+ onTriggered: function() {
+ spreadIdx = (spreadIdx + 1) % spreads.length
+ grad.spread = spreads[spreadIdx]
+ }
+ }
+
+ ControlledShape {
+ anchors.fill: parent
+ strokeColor: "gray"
+ strokeWidth: 2
+ fillColor: "transparent"
+ delegate: [
+ PathMove {
+ x: 0
+ y: 50
+ },
+ PathLine {
+ relativeX: 200
+ relativeY: 0
+ },
+ PathMove {
+ x: 0
+ y: 150
+ },
+ PathLine {
+ relativeX: 200
+ relativeY: 0
+ }
+ ]
+ }
+}
diff --git a/tests/manual/painterpathquickshape/hand-print.svg b/tests/manual/painterpathquickshape/hand-print.svg
new file mode 100644
index 0000000000..eb8d56060d
--- /dev/null
+++ b/tests/manual/painterpathquickshape/hand-print.svg
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:ns1="http://sozi.baierouge.fr"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ id="svg10114"
+ sodipodi:docname="New document 114"
+ viewBox="0 0 210.55 207.21"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ >
+ <sodipodi:namedview
+ id="base"
+ bordercolor="#666666"
+ inkscape:pageshadow="2"
+ inkscape:window-y="170"
+ fit-margin-left="0"
+ pagecolor="#ffffff"
+ fit-margin-top="0"
+ inkscape:window-maximized="0"
+ inkscape:zoom="0.35"
+ inkscape:window-x="381"
+ inkscape:window-height="455"
+ showgrid="false"
+ borderopacity="1.0"
+ inkscape:current-layer="layer1"
+ inkscape:cx="177.4157"
+ inkscape:cy="37.888957"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="515"
+ inkscape:pageopacity="0.0"
+ inkscape:document-units="px"
+ />
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ transform="translate(-197.58 -363.04)"
+ >
+ <path
+ id="path10106"
+ sodipodi:nodetypes="sssssssscssssssssssssssssssssssssssssscssssssssscsssssssssssassssssssssssssscscccssssssssssssssssscacasssssssssssssssssssscscc"
+ style="fill:#000000"
+ inkscape:connector-curvature="0"
+ d="m322.61 371.62c-1.5377 0.0333-3.5271 0.80745-6.0938 2.25-4.9988 2.8095-6.3561 7.2849-5.4688 18.062 0.51964 6.3116 0.32732 8.239-1.0312 10.312-1.356 2.0695-1.5585 4.005-1.0625 10.438 0.33561 4.3521 1.1508 8.7842 1.8125 9.8438 0.96765 1.5495 0.87327 2.6094-0.46875 5.4375-2.049 4.3179-2.0715 5.7903-0.1875 8.2812 0.8075 1.0676 1.4825 2.5854 1.5 3.375 0.0175 0.78955 0.99163 2.477 2.1875 3.75 2.608 2.7761 3.0737 2.8262 5.0312 0.5625 3.1122-3.5987 3.9502-8.9023 4.375-28.031 0.31534-14.199 0.80913-20.033 1.875-22.094 0.80879-1.564 1.5884-6.3682 1.75-10.844 0.28534-7.9048-0.83575-11.417-4.2188-11.344zm-53 14.062c-1.9081 0-6.859 2.9524-7.8125 4.6562-1.1433 2.0429-0.39376 13.788 1.125 17.844 0.61791 1.65 1.9625 3.6232 3 4.375 1.1193 0.81114 1.6944 2.1169 1.4062 3.2188-0.69123 2.6432 0.97813 9.3176 3.8438 15.375 1.3522 2.8583 2.9145 7.6087 3.4688 10.531 0.55425 2.9226 2.2388 7.2762 3.75 9.6875 2.3396 3.7331 3.1564 4.3347 5.625 4.0938 3.7598-0.36694 5.5441-3.0273 3.8438-5.75-0.68348-1.0944-1.2188-3.1291-1.2188-4.5s-1.1024-5.7509-2.4375-9.75-2.4246-9.0812-2.4062-11.281c0.0184-2.2-0.46367-6.25-1.0938-9-1.0663-4.6539-4.014-21.112-4.0625-22.719-0.0353-1.1676-5.8553-6.7812-7.0312-6.7812zm87.719 2.1875c-3.4842-0.0817-7.1481 2.6491-8.6562 7.2188-1.0035 3.0406-2.0408 14.629-2.0938 23.594-0.0114 1.925-0.49233 5.0413-1.0938 6.9062-0.60142 1.8649-1.0482 5.3488-1 7.75 0.07 3.4862-0.5085 5.0547-2.875 7.75-3.4871 3.9716-4.1106 9.8469-1.2188 11.531 0.9625 0.56061 2.261 1.0146 2.9062 1.0312 2.0704 0.0537 7.3438-5.081 7.3438-7.1562 0-1.1038 0.64287-3.1964 1.4375-4.6562 2.6446-4.8585 7.6275-23.031 7-25.531-0.35831-1.4276 0.1021-3.7719 1.125-5.75 0.9498-1.8367 1.8627-4.9087 2.0312-6.8438 0.16858-1.935 0.60352-5.3826 0.96875-7.6562 0.54958-3.4212 0.28253-4.4987-1.4688-6.25-1.2836-1.2836-2.8225-1.9004-4.4062-1.9375zm38.962 39.776c-0.41087-1.3177-2.649-3.1827-2.649-3.1827-5.6222 0.62958-7.1736 1.4539-9.9688 5.3125-2.033 2.8064-3.124 5.767-3.5938 9.7188-0.50455 4.2441-1.3633 6.4493-3.4375 8.6875-1.5291 1.65-3.8228 5.0269-5.0625 7.5s-2.9062 4.9996-3.7188 5.5938c-1.7406 1.2728-6.1921 14.341-5.5 16.156 0.90097 2.3631 4.0452 1.26 7.4062-2.625 1.8515-2.1402 4.4737-5.1777 5.8438-6.75 4.0598-4.659 11.031-16.282 11.031-18.406 0-0.63241 0.9-2.2891 2-3.6875s2-3.3296 2-4.2812c0-2.1982 3.7305-4.6335 4.742-7.3758 0.77537-2.1021 1.6609-4.2424 0.90707-6.6602zm-107.4 33.88c-2.7007-0.0514-4.8692 0.33799-5.7812 1.25-0.9007 0.9007-1.6649 4.5217-2.0312 9.5938-0.61163 8.4673 0.68391 13.233 4.375 15.938 1.3221 0.96856 1.125 1.6325-1.625 4.9688-1.7472 2.1197-3.6988 1.3702-5.4885 2.2118-2.7058 1.2724-4.2526 5.347-4.5115 8.4758-0.19436 2.3492-1.2274 4.6649-2.75 6.1875-1.3413 1.3413-2.4375 3.5424-2.4375 4.875 0 1.3327-0.68534 2.9801-1.5 3.6562-1.5011 1.2458-7.4902 0.99608-10.75-0.4375-2.0861-0.91741-3.75 2.6725-3.75 8.0312 0 2.7703 1.0145 4.5375 5.2812 9.1562 17.679 19.138 24.149 24.25 30.625 24.25 1.6394 0 3.2137 0.42371 3.5312 0.9375 0.31753 0.51376 2.0351 0.73192 3.8125 0.46875 5.7811-0.85595 23.001-2.3031 33.219-2.7812l10-0.46875 3.5-3.9375c1.934-2.1628 5.0241-6.3457 6.8438-9.3125 2.7166-4.4293 3.2194-6.1294 2.8125-9.4062-0.34558-2.7831-0.0335-4.5077 1.0625-5.7188 1.1973-1.323 1.6274-4.4114 1.8125-13 0.16512-7.6629 0.77195-12.524 1.875-15.156 0.89247-2.1297 1.6388-4.8297 1.6562-6 0.0175-1.1703 0.6925-2.9949 1.5-4.0625 3.3493-4.4282 0.60788-8.5625-5.6875-8.5625-3.1563 0-4.9116-0.75392-8.1562-3.5312-2.2727-1.9453-5.2665-3.8195-6.6562-4.1562-1.3898-0.33673-3.4151-1.2258-4.5-1.9688-1.0849-0.74294-2.614-1.3438-3.375-1.3438-0.76098 0-3.0271-1.2925-5.0312-2.875-4.3642-3.446-12.675-4.9864-17.094-3.1562-2.3602 0.97763-3.4086 0.8936-6.1875-0.5625-4.1163-2.1568-10.093-3.4769-14.594-3.5625zm-73.375 17.938c-1.3882-0.0661-2.5361 0.22802-4.2812 0.84375-2.1608 0.7624-2.8143 1.7251-3.6496 3.0802-0.96454 1.5646-1.225 3.8922-1.2843 5.3625-0.0716 1.7769 0.60748 3.6445 1.6214 5.1199 2.4979 3.6347 4.7532 2.6468 10.344 8.25 4.7079 4.7186 9.3065 8.5625 10.25 8.5625 0.94347 0 3.2613 1.125 5.125 2.5 4.0233 2.9683 6.95 3.1826 10.031 0.6875 2.9687-2.4039 2.1009-6.0679-2-8.375-2.5053-1.4095-3.0312-2.3588-3.0312-5.375 0-8.2634-7.1413-16.183-17.438-19.344-2.671-0.81994-4.2994-1.2464-5.6875-1.3125zm87.781 15.844c0.43126 0.007 0.89972 0.14969 1.4375 0.4375 2.3391 1.2518 14.915 1.6771 16.719 0.5625 2.3457-1.4497 2.6432 0.3729 0.65625 4.0625-1.5014 2.7879-1.9615 5.6783-2 12.719-0.0274 5.0078-0.38695 9.6562-0.8125 10.344-0.92832 1.4998-3.2958 1.6422-4.1562 0.25-0.33992-0.55-1.9394-1-3.5312-1-1.9787 0-3.4592-0.80091-4.6875-2.5625-0.98817-1.4172-3.0717-3.9871-4.6562-5.6875-2.7528-2.9542-2.8667-3.3899-2.3125-10 0.54096-6.4526 1.475-9.157 3.3438-9.125zm94.438 65.938c0.24133 0.40465 0.5482 0.4375 0.90625 0.4375 0.31607 0 0.55718-0.0265 0.78125-0.4375z"
+ />
+ </g
+ >
+ <metadata
+ >
+ <rdf:RDF
+ >
+ <cc:Work
+ >
+ <dc:format
+ >image/svg+xml</dc:format
+ >
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage"
+ />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/publicdomain/"
+ />
+ <dc:publisher
+ >
+ <cc:Agent
+ rdf:about="http://openclipart.org/"
+ >
+ <dc:title
+ >Openclipart</dc:title
+ >
+ </cc:Agent
+ >
+ </dc:publisher
+ >
+ </cc:Work
+ >
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/publicdomain/"
+ >
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Reproduction"
+ />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Distribution"
+ />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
+ />
+ </cc:License
+ >
+ </rdf:RDF
+ >
+ </metadata
+ >
+</svg
+>
diff --git a/tests/manual/painterpathquickshape/joinStyles.qml b/tests/manual/painterpathquickshape/joinStyles.qml
new file mode 100644
index 0000000000..df057fab3b
--- /dev/null
+++ b/tests/manual/painterpathquickshape/joinStyles.qml
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ strokeColor: "black"
+ strokeWidth: 16
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+
+ //readonly property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ]
+ //joinStyle: styles[joinStyleIdx]
+
+ property int joinStyleIdx: 0
+ readonly property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ]
+ readonly property variant styleTexts: [ qsTr("BevelJoin"), qsTr("MiterJoin"), qsTr("RoundJoin") ]
+
+ startX: 30
+ startY: 30
+
+ delegate: [
+ PathLine {
+ x: 100
+ y: 100
+ },
+ PathLine {
+ x: 30
+ y: 100
+ }
+ ]
+
+// Timer {
+// interval: 1000
+// repeat: true
+// running: true
+// onTriggered: joinTest.joinStyleIdx = (joinTest.joinStyleIdx + 1) % joinTest.styles.length
+// }
+}
diff --git a/tests/manual/painterpathquickshape/largeOrSmallArc.qml b/tests/manual/painterpathquickshape/largeOrSmallArc.qml
new file mode 100644
index 0000000000..265249fd2a
--- /dev/null
+++ b/tests/manual/painterpathquickshape/largeOrSmallArc.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ Repeater {
+ anchors.fill: parent
+ model: 2
+ delegate: ControlledShape {
+ id: delegate
+ required property int index
+
+ anchors.fill: parent
+ fillColor: "transparent"
+ strokeColor: delegate.index === 0 ? "red" : "blue"
+ strokeStyle: ShapePath.DashLine
+ strokeWidth: 4
+ startX: 50
+ startY: 100
+
+ delegate: [
+ PathArc {
+ x: 100
+ y: 150
+ radiusX: 50
+ radiusY: 50
+ useLargeArc: delegate.index === 1
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/linearGradient.qml b/tests/manual/painterpathquickshape/linearGradient.qml
new file mode 100644
index 0000000000..63049eab97
--- /dev/null
+++ b/tests/manual/painterpathquickshape/linearGradient.qml
@@ -0,0 +1,84 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ strokeWidth: 4
+ strokeColor: "red"
+// fillGradient: LinearGradient {
+// x1: 20
+// y1: 20
+// x2: 180
+// y2: 130
+// GradientStop {
+// position: 0
+// color: "blue"
+// }
+// GradientStop {
+// position: 0.2
+// color: "green"
+// }
+// GradientStop {
+// position: 0.4
+// color: "red"
+// }
+// GradientStop {
+// position: 0.6
+// color: "yellow"
+// }
+// GradientStop {
+// position: 1
+// color: "cyan"
+// }
+// }
+ fillColor: "blue" // ignored with the gradient set
+ strokeStyle: ShapePath.DashLine
+// dashPattern: [ 1, 4 ]
+ startX: 20
+ startY: 20
+
+ delegate: [
+ PathLine {
+ x: 180
+ y: 130
+ },
+ PathLine {
+ x: 20
+ y: 130
+ },
+ PathLine {
+ x: 20
+ y: 20
+ }
+ ]
+
+// transform: Rotation {
+// origin.x: 100
+// origin.y: 50
+// axis {
+// x: 0
+// y: 1
+// z: 0
+// }
+// SequentialAnimation on angle {
+// NumberAnimation {
+// from: 0
+// to: 75
+// duration: 2000
+// }
+// NumberAnimation {
+// from: 75
+// to: -75
+// duration: 4000
+// }
+// NumberAnimation {
+// from: -75
+// to: 0
+// duration: 2000
+// }
+// loops: Animation.Infinite
+// }
+// }
+}
diff --git a/tests/manual/painterpathquickshape/main.cpp b/tests/manual/painterpathquickshape/main.cpp
new file mode 100644
index 0000000000..5f45be81b8
--- /dev/null
+++ b/tests/manual/painterpathquickshape/main.cpp
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QFontDatabase>
+
+#include "svgpathloader.h"
+#include "debugpaintitem.h"
+#include "debugvisualizationcontroller.h"
+
+int main(int argc, char *argv[])
+{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+#endif
+ QGuiApplication app(argc, argv);
+ app.setOrganizationName("QtProject");
+
+ qmlRegisterType<DebugPaintItem>("io.qt", 1, 0, "DebugPaintItem");
+ qmlRegisterType<SvgPathLoader>("io.qt", 1, 0, "SvgPathLoader");
+ qmlRegisterType<DebugVisualizationController>("io.qt", 1, 0, "DebugVisualizationController");
+
+ QFontDatabase::addApplicationFont(":/Graziano.ttf");
+ QQmlApplicationEngine engine;
+ const QUrl url(QStringLiteral("qrc:/main.qml"));
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ &app, [url](QObject *obj, const QUrl &objUrl) {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ }, Qt::QueuedConnection);
+ engine.load(url);
+
+ return app.exec();
+}
diff --git a/tests/manual/painterpathquickshape/main.qml b/tests/manual/painterpathquickshape/main.qml
new file mode 100644
index 0000000000..5a4192a393
--- /dev/null
+++ b/tests/manual/painterpathquickshape/main.qml
@@ -0,0 +1,292 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Shapes
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtCore
+
+Window {
+ id: theWindow
+ width: 1024
+ height: 768
+ visible: true
+ title: qsTr("Hello World")
+ color: "#666"
+
+ ListModel {
+ id: sampleList
+
+ ListElement {
+ text: "Small polygon"
+ source: "SmallPolygon.qml"
+ }
+ ListElement {
+ text: "Text"
+ source: "TextShape.qml"
+ }
+ ListElement {
+ text: "SVG"
+ source: "SvgShape.qml"
+ }
+ ListElement {
+ text: "QuadShape"
+ source: "SimpleShape.qml"
+ }
+ ListElement {
+ text: "Squircle"
+ source: "Squircle.qml"
+ }
+ ListElement {
+ text: "Intersect"
+ source: "Intersect.qml"
+ }
+ ListElement {
+ text: "Intersect2"
+ source: "Intersect2.qml"
+ }
+ ListElement {
+ text: "CubicShape"
+ source: "CubicShape.qml"
+ }
+ ListElement {
+ text: "DashedStroke"
+ source: "DashedStroke.qml"
+ }
+ ListElement {
+ text: "Mussel"
+ source: "Mussel.qml"
+ }
+ ListElement {
+ text: "Arc Direction"
+ source: "arcDirection.qml"
+ }
+
+ ListElement {
+ text: "Arc Rotation"
+ source: "arcRotation.qml"
+ }
+
+ ListElement {
+ text: "Cap Styles"
+ source: "capStyles.qml"
+ }
+
+ ListElement {
+ text: "Cubic Curve"
+ source: "cubicCurve.qml"
+ }
+
+ ListElement {
+ text: "Dash Pattern"
+ source: "dashPattern.qml"
+ }
+
+ ListElement {
+ text: "Elliptical Arcs"
+ source: "ellipticalArcs.qml"
+ }
+
+ ListElement {
+ text: "Fill rules"
+ source: "fillRules.qml"
+ }
+
+ ListElement {
+ text: "Gradient spread modes"
+ source: "gradientSpreadModes.qml"
+ }
+
+ ListElement {
+ text: "Join styles"
+ source: "joinStyles.qml"
+ }
+
+ ListElement {
+ text: "Large or small arc"
+ source: "largeOrSmallArc.qml"
+ }
+
+ ListElement {
+ text: "Linear gradient"
+ source: "linearGradient.qml"
+ }
+
+ ListElement {
+ text: "Quadratic curve"
+ source: "quadraticCurve.qml"
+ }
+
+ ListElement {
+ text: "Radial gradient"
+ source: "radialGradient.qml"
+ }
+
+ ListElement {
+ text: "Stroke or fill"
+ source: "strokeOrFill.qml"
+ }
+
+ ListElement {
+ text: "Qt! text"
+ source: "text.qml"
+ }
+
+ ListElement {
+ text: "Tiger"
+ source: "tiger.qml"
+ }
+ }
+
+ ComboBox {
+ id: comboBox
+ model: sampleList
+ textRole: "text"
+ valueRole: "source"
+ onCurrentValueChanged: {
+ loader.source = currentValue
+ }
+ }
+ Image {
+ id: background
+ anchors.fill: flickable
+ fillMode: Image.Tile
+ source: "qrc:/background.png"
+ }
+ Rectangle {
+ id: solidBackground
+ anchors.fill: flickable
+ color: controlPanel.backgroundColor
+ }
+
+ Flickable {
+ id: flickable
+ clip: true
+ contentWidth: loader.item ? loader.item.boundingRect.right * controlPanel.scale + controlPanel.pathMargin * 2 : 1
+ contentHeight: loader.item ? loader.item.boundingRect.bottom * controlPanel.scale + controlPanel.pathMargin * 2 : 1
+ anchors.top: comboBox.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: controlPanel.top
+
+ WheelHandler {
+ onWheel: (event)=> {
+ let scale = controlPanel.scale
+ // position in scaled path:
+ let posX = event.x - controlPanel.pathMargin
+ let posY = event.y - controlPanel.pathMargin
+
+ let pathX = posX / scale
+ let pathY = posY / scale
+
+ if (event.angleDelta.y > 0)
+ scale = scale * 1.1
+ else
+ scale = scale / 1.1
+ controlPanel.setScale(scale)
+
+ scale = controlPanel.scale
+ let scaledPosX = pathX * scale
+ let scaledPosY = pathY * scale
+
+ flickable.contentX += scaledPosX - posX
+ flickable.contentY += scaledPosY - posY
+ flickable.returnToBounds()
+ }
+ }
+
+ Loader {
+ x: controlPanel.pathMargin
+ y: controlPanel.pathMargin
+ id: loader
+ }
+ MouseArea {
+ id: theMouseArea
+ acceptedButtons: Qt.RightButton
+ anchors.fill: parent
+ onMouseXChanged: {
+ let p = Qt.point(Math.round(mouseX), Math.round(mouseY))
+ p = mapToItem(loader.item, p)
+ zoomTarget.sourceRect.x = p.x - zoomTarget.sourceRect.width/2
+ }
+ onMouseYChanged: {
+ let p = Qt.point(Math.round(mouseX), Math.round(mouseY))
+ p = mapToItem(loader.item, p)
+ zoomTarget.sourceRect.y = p.y - zoomTarget.sourceRect.height/2
+ }
+ hoverEnabled: true
+ }
+ ShaderEffectSource {
+ id: zoomTarget
+ sourceItem: loader.item
+ sourceRect.width: 16
+ sourceRect.height: 16
+ }
+ }
+
+
+ Rectangle {
+ anchors.top: flickable.top
+ anchors.right: flickable.right
+ anchors.margins: 5
+ width: 256
+ height: 256
+ border.color: Qt.black
+ ShaderEffect {
+ anchors.fill: parent
+ anchors.margins: 1
+ property variant src: zoomTarget
+ property real textureWidth: zoomTarget.sourceRect.width
+ property real textureHeight: zoomTarget.sourceRect.height
+ fragmentShader: "zoombox.frag.qsb"
+ }
+ Button {
+ id: plusButton
+ text: "+"
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.margins: 2
+ width: 20
+ height: 20
+ onPressed: {
+ zoomTarget.sourceRect.width = Math.max(zoomTarget.sourceRect.width / 2, 4)
+ zoomTarget.sourceRect.height = Math.max(zoomTarget.sourceRect.height / 2, 4)
+ }
+ }
+ Button {
+ id: minusButton
+ text: "-"
+ anchors.top: plusButton.bottom
+ anchors.right: parent.right
+ anchors.margins: 2
+ width: 20
+ height: 20
+ onPressed: {
+ zoomTarget.sourceRect.width = Math.max(zoomTarget.sourceRect.width * 2, 4)
+ zoomTarget.sourceRect.height = Math.max(zoomTarget.sourceRect.height * 2, 4)
+ }
+ }
+ Text {
+ text: "x"+parent.width / zoomTarget.sourceRect.width
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ anchors.margins: 2
+ }
+ }
+
+
+ ControlPanel {
+ id: controlPanel
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: parent.height / 4
+ }
+ Settings {
+ property alias currentTab: comboBox.currentIndex
+ property alias windowWidth: theWindow.width
+ property alias windowHeight: theWindow.height
+ }
+}
diff --git a/tests/manual/painterpathquickshape/peace_victory.svg b/tests/manual/painterpathquickshape/peace_victory.svg
new file mode 100644
index 0000000000..25480cbaee
--- /dev/null
+++ b/tests/manual/painterpathquickshape/peace_victory.svg
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ width="330.31769"
+ height="652.32654"
+ sodipodi:docname="166413_160676353985218_105678756151645_360714_7013010_n.jpg">
+ <metadata
+ id="metadata8">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1280"
+ inkscape:window-height="749"
+ id="namedview4"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="0.76005001"
+ inkscape:cx="162.4278"
+ inkscape:cy="342.07912"
+ inkscape:window-x="0"
+ inkscape:window-y="26"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <g
+ id="g3001"
+ transform="translate(-120.24391,40.527705)">
+ <path
+ sodipodi:nodetypes="ssssssssczsssssss"
+ inkscape:connector-curvature="0"
+ id="path2999"
+ d="m 333.68126,543.99038 c -6.91753,-4.38697 -11.36161,-10.23632 -13.34555,-17.56558 -1.93876,-7.16237 -17.29569,-22.30618 -25.49276,-31.28027 -5.66358,-6.20045 -10.53284,-9.06593 -4.03049,-16.38723 5.60876,-6.31517 11.66711,-27.57804 12.39492,-33.72331 0.89475,-7.55473 19.88838,13.07827 29.55699,12.32647 15.07492,-1.17218 19.87293,-8.57979 31.333,-21.95526 9.95433,-11.61808 15.38922,-24.72893 21.56477,-35.66576 3.10302,-5.49543 14.54668,-28.68741 15.54455,-27.78873 2.65231,2.82162 7.60302,15.21755 8.9266,25.36832 1.32359,10.15076 1.33221,24.47455 5.4914,37.36519 3.70609,11.48633 2.22562,32.47582 -3.24615,46.02256 -2.19534,5.43511 -13.41276,12.171 -28.30712,34.76544 -2.04972,3.1094 -5.63972,6.63261 -9.09235,8.92323 -3.1379,2.08181 -9.31989,7.24499 -13.73775,11.47372 -4.41786,4.22873 -9.45802,8.42578 -11.20034,9.32677 -4.95722,2.56348 -11.13434,2.10828 -16.35972,-1.20556 z"
+ style="fill:#000000" />
+ <path
+ sodipodi:nodetypes="ssssssssssssssssscssss"
+ inkscape:connector-curvature="0"
+ id="path2997"
+ d="m 236.5,478.28799 c -26.46888,-1.7341 -59.59471,-6.73425 -65.76997,-9.9276 -3.78272,-1.95612 -8.3648,-9.36928 -9.82546,-15.89622 -1.67608,-7.48953 -0.60289,-28.26342 1.85793,-35.96417 1.05453,-3.3 4.9189,-13.03322 8.58748,-21.62937 C 179.00832,376.92572 182,372.28356 182,363.21743 c 0,-3.50469 0.61152,-7.83574 1.35894,-9.62455 1.49509,-3.57827 2.36599,-9.42794 23.40534,-17.67708 15.33826,-6.01386 25.16813,-11.90646 32.74071,-18.37591 12.3733,-10.57084 26.6062,-13.06992 48.57877,-7.38876 17.54428,4.5362 25.34686,7.84887 38.9754,7.84887 6.08978,0 17.19611,-0.85492 18.24842,-0.29174 1.11527,0.59687 1.27225,5.31825 2.21511,8.56158 1.33843,4.60404 1.41526,7.68415 0.44529,11.54174 -2.05134,8.15817 -9.32024,15.59461 -13.88659,17.61453 -5.06052,2.23851 -16.40257,4.51284 -31.25983,5.67633 -14.7923,1.15841 -19.21657,-0.79634 -27.99177,15.58765 l -3.31538,6.19009 10.91268,15.36198 c 11.78018,16.58318 17.44498,48.05873 11.58963,61.65598 -3.10121,7.2016 -8.24447,17.64394 -14.43161,22.20637 -9.7269,7.17267 -28.89257,-2.88671 -43.08511,-3.81652 z"
+ style="fill:#000000" />
+ <path
+ sodipodi:nodetypes="ssaaaasscss"
+ inkscape:connector-curvature="0"
+ id="path2995"
+ d="m 317.68552,447.77108 c -4.67916,-1.18677 -9.1372,-7.95998 -9.65154,-12.98136 -0.48763,-4.76058 3.66294,-14.83262 6.92258,-21.68422 4.49604,-9.45044 11.55001,-17.4567 17.1823,-26.27727 5.4084,-8.46995 10.65679,-17.0411 15.94923,-25.58399 7.17401,-11.58005 7.84367,-33.73833 21.42439,-34.80044 14.18977,-1.10974 28.2626,27.80345 27.76586,32.43896 -1.92945,18.00547 -12.74615,35.2835 -22.17876,51.23197 -6.61784,11.18931 -11.77374,15.63521 -18.1815,27.31116 -4.3386,7.809 -9.17117,10.7187 -17.04193,15.96767 -8.33187,5.55649 -19.11228,-4.84172 -22.19063,-5.62248 z"
+ style="fill:#000000" />
+ <path
+ sodipodi:nodetypes="csssassaasc"
+ inkscape:connector-curvature="0"
+ id="path2993"
+ d="m 297.7121,397.62093 c -4.37978,-0.76378 -11.1662,-5.35567 -13.43054,-9.08751 -1.05408,-1.73722 -5.27599,-4.42533 -4.52995,-6.66133 3.79539,-11.37539 8.14861,-15.05527 11.11936,-17.01179 1.47519,-0.97155 10.91285,-2.35405 16.45476,-2.37949 5.35694,-0.0246 14.398,2.6739 15.926,2.15393 6.39248,-2.1753 21.58665,-11.63284 16.66639,-3.95961 -1.74425,2.72018 -9.86689,20.36174 -15.66412,30.05992 -2.80151,4.68665 -3.99996,11.91427 -9.21074,13.54551 -5.90623,1.84894 -15.24781,-6.29628 -17.33116,-6.65959 z"
+ style="fill:#000000" />
+ <path
+ sodipodi:nodetypes="ssssssssssccssssccsssscssccssssscss"
+ inkscape:connector-curvature="0"
+ id="path2991"
+ d="m 189,334.76618 c -3.56886,-1.87317 -3.73828,-7.15428 -0.55687,-17.35819 5.01517,-16.0854 8.92638,-21.55121 8.92639,-47.57388 2e-5,-23.36514 -3.95828,-35.38789 -7.18098,-47.31903 -1.78273,-6.6 -3.77628,-16.86508 -6.83769,-24.01508 -8.99294,-21.00325 -12.2685,-31.92583 -13.8777,-46.27612 C 168.53694,143.87507 165.62544,129.70037 161.00571,111 152.60865,77.00927 151.53855,67.780331 154.95103,58.78242 158.45942,49.531613 167.63712,43 177.12722,43 c 5.76576,0 14.29179,3.435547 18.43755,7.429376 4.91773,4.73752 12.26688,9.449135 15.70129,28.754921 2.71875,14.37298 5.5615,30.709973 7.68228,44.315703 2.82481,18.38886 4.9316,24.88951 8.2153,32.19284 5.77357,12.84108 9.22986,29.46676 11.71356,50.30716 0.55716,4.675 2.6273,13.9 4.60031,20.5 1.97301,6.6 4.35732,12.20086 5.75964,13.99194 4.31214,5.50757 4.20182,11.46411 9.8041,16.33937 4.89284,3.932 5.17553,-4.93168 8.48229,-8.86127 5.04933,-6.00079 4.34525,-17.53657 9.98389,-34.12517 6.53313,-19.22016 5.46933,-38.15405 19.40326,-68.32952 12.90365,-27.94429 9.48745,-39.81066 16.06865,-59.54708 6.06973,-18.202577 7.65173,-41.314931 21.79742,-54.279307 6.74694,-6.183506 18.29831,-8.217335 26.58616,-6.854856 8.28785,1.362479 17.87216,3.615793 19.20995,12.536783 0.8831,5.888923 2.70582,18.266918 -3.21886,39.099813 -6.49981,22.855208 -13.44316,35.388777 -16.01082,52.270797 -6.1548,20.88948 -8.08415,24.9957 -13.41238,50.5742 -4.92944,22.91145 -9.053,30.98714 -15.44314,44.38602 -4.78823,10.03997 -5.29352,16.18349 -7.50248,25.34215 -1.64534,6.82178 -4.51529,18.70324 -6.37768,26.40324 -3.3461,13.83435 -11.83198,26.93346 -15.1878,30.64161 -2.62156,2.89679 -7.70019,2.39802 -22.59508,-2.21904 -8.11727,-2.51617 -16.06028,-4.30586 -20.32463,-4.57947 -11.21682,-1.7957 -18.88746,8.09995 -28,14.65341 -27.91809,20.07783 -36.8097,24.33407 -43.5,20.82256 z"
+ style="fill:#000000" />
+ <path
+ sodipodi:nodetypes="cscsssssssccc"
+ inkscape:connector-curvature="0"
+ id="path2987"
+ d="m 338,310.79118 c -1.925,-0.46211 -8.55748,-0.57196 -14.73884,-0.24413 l -11.23883,0.59606 -1.16709,-2.56149 c -1.04742,-2.29882 -0.4417,-4.02378 5.9069,-16.82155 C 320.65283,283.91703 324.74157,274.35 325.84822,270.5 327.62523,264.31785 333,241.17153 333,239.70102 c 0,-0.30699 0.70714,-1.26531 1.57143,-2.12959 2.10347,-2.10348 9.18161,-2.03578 17.79224,0.17017 5.65285,1.4482 7.36756,2.39455 9.72002,5.36446 6.48214,8.18349 21.03837,15.40973 14.91994,28.32586 -1.41866,14.95087 -7.97079,34.16095 -14.83219,45.02554 C 359.76469,319.83743 342.69403,311.918 338,310.79118 z"
+ style="fill:#000000" />
+ </g>
+</svg>
diff --git a/tests/manual/painterpathquickshape/quadraticCurve.qml b/tests/manual/painterpathquickshape/quadraticCurve.qml
new file mode 100644
index 0000000000..9199302c1a
--- /dev/null
+++ b/tests/manual/painterpathquickshape/quadraticCurve.qml
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ ControlledShape {
+ id: shape
+ anchors.fill: parent
+ strokeWidth: 4
+ strokeColor: "black"
+ fillColor: "transparent"
+
+ startX: 50
+ startY: 50
+
+ delegate: [
+ PathQuad {
+ x: 150
+ y: 50
+ controlX: cp.x
+ controlY: cp.y
+ }
+ ]
+ }
+
+ Rectangle {
+ id: cp
+ color: "red"
+ width: 10
+ height: 10
+ SequentialAnimation on x {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 0
+ to: shape.width - cp.width
+ duration: 5000
+ }
+ NumberAnimation {
+ from: shape.width - cp.width
+ to: 0
+ duration: 5000
+ }
+ }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/radialGradient.qml b/tests/manual/painterpathquickshape/radialGradient.qml
new file mode 100644
index 0000000000..896db76902
--- /dev/null
+++ b/tests/manual/painterpathquickshape/radialGradient.qml
@@ -0,0 +1,106 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+// fillGradient: RadialGradient {
+// centerX: 100
+// centerY: 100
+// centerRadius: 100
+// SequentialAnimation on focalRadius {
+// loops: Animation.Infinite
+// NumberAnimation {
+// from: 1
+// to: 20
+// duration: 2000
+// }
+// NumberAnimation {
+// from: 20
+// to: 1
+// duration: 2000
+// }
+// }
+// SequentialAnimation on focalX {
+// loops: Animation.Infinite
+// NumberAnimation {
+// from: 50
+// to: 150
+// duration: 3000
+// }
+// NumberAnimation {
+// from: 150
+// to: 50
+// duration: 3000
+// }
+// }
+// SequentialAnimation on focalY {
+// loops: Animation.Infinite
+// NumberAnimation {
+// from: 50
+// to: 150
+// duration: 1000
+// }
+// NumberAnimation {
+// from: 150
+// to: 50
+// duration: 1000
+// }
+// }
+// GradientStop {
+// position: 0
+// color: "#ffffff"
+// }
+// GradientStop {
+// position: 0.11
+// color: "#f9ffa0"
+// }
+// GradientStop {
+// position: 0.13
+// color: "#f9ff99"
+// }
+// GradientStop {
+// position: 0.14
+// color: "#f3ff86"
+// }
+// GradientStop {
+// position: 0.49
+// color: "#93b353"
+// }
+// GradientStop {
+// position: 0.87
+// color: "#264619"
+// }
+// GradientStop {
+// position: 0.96
+// color: "#0c1306"
+// }
+// GradientStop {
+// position: 1
+// color: "#000000"
+// }
+// }
+
+ strokeWidth: 4
+ strokeColor: "red"
+ fillColor: "blue" // ignored with the gradient set
+ strokeStyle: ShapePath.DashLine
+ //dashPattern: [ 1, 4 ]
+ startX: 20
+ startY: 20
+ delegate: [
+ PathLine {
+ x: 180
+ y: 130
+ },
+ PathLine {
+ x: 20
+ y: 130
+ },
+ PathLine {
+ x: 20
+ y: 20
+ }
+ ]
+}
diff --git a/tests/manual/painterpathquickshape/strokeOrFill.qml b/tests/manual/painterpathquickshape/strokeOrFill.qml
new file mode 100644
index 0000000000..e97fcd0ca5
--- /dev/null
+++ b/tests/manual/painterpathquickshape/strokeOrFill.qml
@@ -0,0 +1,115 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ ControlledShape {
+ id: circ1
+ anchors.fill: parent
+ fillColor: "transparent" // stroke only
+ strokeWidth: 4
+
+ SequentialAnimation on strokeColor {
+ loops: Animation.Infinite
+ ColorAnimation {
+ from: "black"
+ to: "yellow"
+ duration: 5000
+ }
+ ColorAnimation {
+ from: "yellow"
+ to: "green"
+ duration: 5000
+ }
+ ColorAnimation {
+ from: "green"
+ to: "black"
+ duration: 5000
+ }
+ }
+
+ readonly property real r: 60
+ startX: circ1.width / 2 - r
+ startY: circ1.height / 2 - r
+
+ delegate: [
+ PathArc {
+ x: circ1.width / 2 + circ1.r
+ y: circ1.height / 2 + circ1.r
+ radiusX: circ1.r
+ radiusY: circ1.r
+ useLargeArc: true
+ },
+ PathArc {
+ x: circ1.width / 2 - circ1.r
+ y: circ1.height / 2 - circ1.r
+ radiusX: circ1.r
+ radiusY: circ1.r
+ useLargeArc: true
+ }
+ ]
+ }
+
+ ControlledShape {
+ id: circ2
+ anchors.fill: parent
+
+ SequentialAnimation on opacity {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1.0
+ to: 0.0
+ duration: 5000
+ }
+ NumberAnimation {
+ from: 0.0
+ to: 1.0
+ duration: 5000
+ }
+ }
+
+ strokeWidth: -1 // or strokeColor: "transparent"
+
+ SequentialAnimation on fillColor {
+ loops: Animation.Infinite
+ ColorAnimation {
+ from: "gray"
+ to: "purple"
+ duration: 3000
+ }
+ ColorAnimation {
+ from: "purple"
+ to: "red"
+ duration: 3000
+ }
+ ColorAnimation {
+ from: "red"
+ to: "gray"
+ duration: 3000
+ }
+ }
+
+ readonly property real r: 40
+ startX: circ2.width / 2 - r
+ startY: circ2.height / 2 - r
+
+ delegate: [
+ PathArc {
+ x: circ2.width / 2 + circ2.r
+ y: circ2.height / 2 + circ2.r
+ radiusX: circ2.r
+ radiusY: circ2.r
+ useLargeArc: true
+ },
+ PathArc {
+ x: circ2.width / 2 - circ2.r
+ y: circ2.height / 2 - circ2.r
+ radiusX: circ2.r
+ radiusY: circ2.r
+ useLargeArc: true
+ }
+ ]
+ }
+}
diff --git a/tests/manual/painterpathquickshape/svgpathloader.cpp b/tests/manual/painterpathquickshape/svgpathloader.cpp
new file mode 100644
index 0000000000..2a9a395c97
--- /dev/null
+++ b/tests/manual/painterpathquickshape/svgpathloader.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "svgpathloader.h"
+
+#include <QFile>
+#include <QPainterPath>
+#include <QXmlStreamReader>
+#include <QXmlStreamAttributes>
+#include <QStack>
+#include <QLocale>
+#include <QMatrix4x4>
+
+SvgPathLoader::SvgPathLoader()
+{
+ connect(this, &SvgPathLoader::sourceChanged, this, &SvgPathLoader::loadPaths);
+
+ loadPaths();
+}
+
+struct SvgState
+{
+ QString fillColor = {};
+ QString strokeColor = {};
+ QString strokeWidth = {};
+ QMatrix4x4 transform;
+};
+
+void SvgPathLoader::loadPaths()
+{
+ m_paths.clear();
+ m_fillColors.clear();
+ m_strokeColors.clear();
+ m_strokeWidths.clear();
+ m_transforms.clear();
+ if (m_source.isEmpty())
+ return;
+
+ QStack<SvgState> states;
+ states.push({});
+
+ QString fileName;
+ if (m_source.isLocalFile())
+ fileName = m_source.toLocalFile();
+ else if (m_source.scheme() == QStringLiteral("qrc"))
+ fileName = QStringLiteral(":/") + m_source.fileName();
+
+ QFile f(fileName);
+ if (f.open(QIODevice::ReadOnly)) {
+ QXmlStreamReader reader(&f);
+ SvgState currentState = {};
+ while (!reader.atEnd()) {
+ reader.readNext();
+ QXmlStreamAttributes attrs = reader.attributes();
+ if (reader.isStartElement())
+ states.push(currentState);
+
+ if (attrs.hasAttribute(QStringLiteral("transform"))) {
+ QString t = attrs.value(QStringLiteral("transform")).toString();
+ const bool isTranslate = t.startsWith(QStringLiteral("translate"));
+ const bool isScale = t.startsWith(QStringLiteral("scale"));
+ const bool isMatrix = t.startsWith(QStringLiteral("matrix"));
+ if (isTranslate || isScale || isMatrix) {
+ int pStart = t.indexOf(QLatin1Char('('));
+ int pEnd = t.indexOf(QLatin1Char(')'));
+ if (pStart >= 0 && pEnd > pStart + 1) {
+ t = t.mid(pStart + 1, pEnd - pStart - 1);
+ QStringList coords = t.split(QLatin1Char(','));
+ if (isMatrix && coords.size() == 6) {
+ QMatrix3x3 m;
+ m(0, 0) = coords.at(0).toDouble();
+ m(1, 0) = coords.at(1).toDouble();
+ m(2, 0) = 0.0f;
+
+ m(0, 1) = coords.at(2).toDouble();
+ m(1, 1) = coords.at(3).toDouble();
+ m(2, 1) = 0.0f;
+
+ m(0, 2) = coords.at(4).toDouble();
+ m(1, 2) = coords.at(5).toDouble();
+ m(2, 2) = 1.0f;
+
+ currentState.transform *= QMatrix4x4(m);
+ } else if (coords.size() == 2) {
+ qreal c1 = coords.first().toDouble();
+ qreal c2 = coords.last().toDouble();
+
+ if (isTranslate)
+ currentState.transform.translate(c1, c2);
+ else if (isScale)
+ currentState.transform.scale(c1, c2);
+ }
+ }
+ }
+ }
+
+ if (attrs.hasAttribute(QStringLiteral("fill"))) {
+ currentState.fillColor = attrs.value(QStringLiteral("fill")).toString();
+ if (!currentState.fillColor.startsWith("#"))
+ currentState.fillColor = "";
+ } else if (attrs.hasAttribute(QStringLiteral("style"))) {
+ QString s = attrs.value(QStringLiteral("style")).toString();
+ int idx = s.indexOf(QStringLiteral("fill:"));
+ if (idx >= 0) {
+ idx = s.indexOf(QLatin1Char('#'), idx);
+ if (idx >= 0)
+ currentState.fillColor = s.mid(idx, 7);
+ }
+ }
+ if (currentState.fillColor == QStringLiteral("none"))
+ currentState.fillColor = {};
+ if (attrs.hasAttribute(QStringLiteral("stroke"))) {
+ currentState.strokeColor = attrs.value(QStringLiteral("stroke")).toString();
+ if (currentState.strokeColor == QStringLiteral("none"))
+ currentState.strokeColor = {};
+ }
+ if (attrs.hasAttribute(QStringLiteral("stroke-width")))
+ currentState.strokeWidth = attrs.value(QStringLiteral("stroke-width")).toString();
+ if (reader.isStartElement() && reader.name() == QStringLiteral("path")) {
+ m_fillColors.append(currentState.fillColor);
+ m_strokeColors.append(currentState.strokeColor);
+ m_strokeWidths.append(currentState.strokeWidth);
+
+ QString t;
+ for (int i = 0; i < 4; ++i) {
+ if (i > 0)
+ t += QLatin1Char(',');
+ QVector4D row = currentState.transform.row(i);
+
+ QLocale c(QLocale::C);
+ t += QStringLiteral("%1, %2, %3, %4")
+ .arg(c.toString(row.x()))
+ .arg(c.toString(row.y()))
+ .arg(c.toString(row.z()))
+ .arg(c.toString(row.w()));
+ }
+
+ m_transforms.append(t);
+ if (attrs.hasAttribute(QStringLiteral("d"))) {
+ m_paths.append(attrs.value(QStringLiteral("d")).toString());
+ }
+ }
+ if (reader.isEndElement()) {
+ currentState = states.pop();
+ }
+ }
+ } else {
+ qWarning() << "Can't open file" << fileName;
+ }
+
+ emit pathsChanged();
+}
diff --git a/tests/manual/painterpathquickshape/svgpathloader.h b/tests/manual/painterpathquickshape/svgpathloader.h
new file mode 100644
index 0000000000..655ea25312
--- /dev/null
+++ b/tests/manual/painterpathquickshape/svgpathloader.h
@@ -0,0 +1,78 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SVGPATHLOADER_H
+#define SVGPATHLOADER_H
+
+#include <QObject>
+#include <QUrl>
+#include <QPainterPath>
+#include <QDebug>
+
+class SvgPathLoader : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(QStringList paths READ paths NOTIFY pathsChanged)
+ Q_PROPERTY(QStringList fillColors READ fillColors NOTIFY pathsChanged)
+ Q_PROPERTY(QStringList strokeColors READ strokeColors NOTIFY pathsChanged)
+ Q_PROPERTY(QStringList strokeWidths READ strokeWidths NOTIFY pathsChanged)
+ Q_PROPERTY(QStringList transforms READ transforms NOTIFY pathsChanged)
+public:
+ SvgPathLoader();
+
+ QUrl source() const
+ {
+ return m_source;
+ }
+ void setSource(const QUrl &url)
+ {
+ if (url == m_source)
+ return;
+ m_source = url;
+ qDebug() << "Set source" << url;
+ emit sourceChanged();
+ }
+
+ QStringList paths() const
+ {
+ return m_paths;
+ }
+
+ QStringList fillColors() const
+ {
+ return m_fillColors;
+ }
+
+ QStringList strokeColors() const
+ {
+ return m_strokeColors;
+ }
+
+ QStringList strokeWidths() const
+ {
+ return m_strokeWidths;
+ }
+
+ QStringList transforms() const
+ {
+ return m_transforms;
+ }
+
+private slots:
+ void loadPaths();
+
+signals:
+ void sourceChanged();
+ void pathsChanged();
+
+private:
+ QUrl m_source;
+ QStringList m_paths;
+ QStringList m_fillColors;
+ QStringList m_strokeColors;
+ QStringList m_strokeWidths;
+ QStringList m_transforms;
+};
+
+#endif // SVGPATHLOADER_H
diff --git a/tests/manual/painterpathquickshape/text.qml b/tests/manual/painterpathquickshape/text.qml
new file mode 100644
index 0000000000..a4ca287600
--- /dev/null
+++ b/tests/manual/painterpathquickshape/text.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+ControlledShape {
+ strokeColor: "black"
+ strokeWidth: 1
+ fillColor: "black"
+
+ delegate: [
+ PathText {
+ x: 0
+ y: 0
+ text: qsTr("Qt!")
+ font.family: "Arial"
+ font.pixelSize: 150
+ }
+ ]
+}
diff --git a/tests/manual/painterpathquickshape/tiger.qml b/tests/manual/painterpathquickshape/tiger.qml
new file mode 100644
index 0000000000..d64493505d
--- /dev/null
+++ b/tests/manual/painterpathquickshape/tiger.qml
@@ -0,0 +1,3810 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ property var boundingRect: Qt.rect(-1000, -1000, 4000, 4000)
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -122.304; y: 84.285 }
+ , PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 }
+ , PathCubic { control1X: -123.851; control1Y: 86.141; control2X: -140.305; control2Y: 38.066; x: -160.833; y: 40.309 }
+ , PathCubic { control1X: -160.833; control1Y: 40.309; control2X: -143.05; control2Y: 32.956; x: -122.304; y: 84.285 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -118.774; y: 81.262 }
+ , PathCubic { control1X: -118.774; control1Y: 81.262; control2X: -119.323; control2Y: 83.078; x: -120.092; y: 82.779 }
+ , PathCubic { control1X: -120.86; control1Y: 82.481; control2X: -119.977; control2Y: 31.675; x: -140.043; y: 26.801 }
+ , PathCubic { control1X: -140.043; control1Y: 26.801; control2X: -120.82; control2Y: 25.937; x: -118.774; y: 81.262 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -91.284; y: 123.59 }
+ , PathCubic { control1X: -91.284; control1Y: 123.59; control2X: -89.648; control2Y: 124.55; x: -90.118; y: 125.227 }
+ , PathCubic { control1X: -90.589; control1Y: 125.904; control2X: -139.763; control2Y: 113.102; x: -149.218; y: 131.459 }
+ , PathCubic { control1X: -149.218; control1Y: 131.459; control2X: -145.539; control2Y: 112.572; x: -91.284; y: 123.59 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -94.093; y: 133.801 }
+ , PathCubic { control1X: -94.093; control1Y: 133.801; control2X: -92.237; control2Y: 134.197; x: -92.471; y: 134.988 }
+ , PathCubic { control1X: -92.704; control1Y: 135.779; control2X: -143.407; control2Y: 139.121; x: -146.597; y: 159.522 }
+ , PathCubic { control1X: -146.597; control1Y: 159.522; control2X: -149.055; control2Y: 140.437; x: -94.093; y: 133.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -98.304; y: 128.276 }
+ , PathCubic { control1X: -98.304; control1Y: 128.276; control2X: -96.526; control2Y: 128.939; x: -96.872; y: 129.687 }
+ , PathCubic { control1X: -97.218; control1Y: 130.435; control2X: -147.866; control2Y: 126.346; x: -153.998; y: 146.064 }
+ , PathCubic { control1X: -153.998; control1Y: 146.064; control2X: -153.646; control2Y: 126.825; x: -98.304; y: 128.276 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -109.009; y: 110.072 }
+ , PathCubic { control1X: -109.009; control1Y: 110.072; control2X: -107.701; control2Y: 111.446; x: -108.34; y: 111.967 }
+ , PathCubic { control1X: -108.979; control1Y: 112.488; control2X: -152.722; control2Y: 86.634; x: -166.869; y: 101.676 }
+ , PathCubic { control1X: -166.869; control1Y: 101.676; control2X: -158.128; control2Y: 84.533; x: -109.009; y: 110.072 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -116.554; y: 114.263 }
+ , PathCubic { control1X: -116.554; control1Y: 114.263; control2X: -115.098; control2Y: 115.48; x: -115.674; y: 116.071 }
+ , PathCubic { control1X: -116.25; control1Y: 116.661; control2X: -162.638; control2Y: 95.922; x: -174.992; y: 112.469 }
+ , PathCubic { control1X: -174.992; control1Y: 112.469; control2X: -168.247; control2Y: 94.447; x: -116.554; y: 114.263 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -119.154; y: 118.335 }
+ , PathCubic { control1X: -119.154; control1Y: 118.335; control2X: -117.546; control2Y: 119.343; x: -118.036; y: 120.006 }
+ , PathCubic { control1X: -118.526; control1Y: 120.669; control2X: -167.308; control2Y: 106.446; x: -177.291; y: 124.522 }
+ , PathCubic { control1X: -177.291; control1Y: 124.522; control2X: -173.066; control2Y: 105.749; x: -119.154; y: 118.335 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -108.42; y: 118.949 }
+ , PathCubic { control1X: -108.42; control1Y: 118.949; control2X: -107.298; control2Y: 120.48; x: -107.999; y: 120.915 }
+ , PathCubic { control1X: -108.7; control1Y: 121.35; control2X: -148.769; control2Y: 90.102; x: -164.727; y: 103.207 }
+ , PathCubic { control1X: -164.727; control1Y: 103.207; control2X: -153.862; control2Y: 87.326; x: -108.42; y: 118.949 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -128.2; y: 90 }
+ , PathCubic { control1X: -128.2; control1Y: 90; control2X: -127.6; control2Y: 91.8; x: -128.4; y: 92 }
+ , PathCubic { control1X: -129.2; control1Y: 92.2; control2X: -157.8; control2Y: 50.2; x: -177.001; y: 57.8 }
+ , PathCubic { control1X: -177.001; control1Y: 57.8; control2X: -161.8; control2Y: 46; x: -128.2; y: 90 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -127.505; y: 96.979 }
+ , PathCubic { control1X: -127.505; control1Y: 96.979; control2X: -126.53; control2Y: 98.608; x: -127.269; y: 98.975 }
+ , PathCubic { control1X: -128.007; control1Y: 99.343; control2X: -164.992; control2Y: 64.499; x: -182.101; y: 76.061 }
+ , PathCubic { control1X: -182.101; control1Y: 76.061; control2X: -169.804; control2Y: 61.261; x: -127.505; y: 96.979 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.172
+ delegate: [PathMove { x: -127.62; y: 101.349 }
+ , PathCubic { control1X: -127.62; control1Y: 101.349; control2X: -126.498; control2Y: 102.88; x: -127.199; y: 103.315 }
+ , PathCubic { control1X: -127.9; control1Y: 103.749; control2X: -167.969; control2Y: 72.502; x: -183.927; y: 85.607 }
+ , PathCubic { control1X: -183.927; control1Y: 85.607; control2X: -173.062; control2Y: 69.726; x: -127.62; y: 101.349 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ delegate: [PathMove { x: -129.83; y: 103.065 }
+ , PathCubic { control1X: -129.327; control1Y: 109.113; control2X: -128.339; control2Y: 115.682; x: -126.6; y: 118.801 }
+ , PathCubic { control1X: -126.6; control1Y: 118.801; control2X: -130.2; control2Y: 131.201; x: -121.4; y: 144.401 }
+ , PathCubic { control1X: -121.4; control1Y: 144.401; control2X: -121.8; control2Y: 151.601; x: -120.2; y: 154.801 }
+ , PathCubic { control1X: -120.2; control1Y: 154.801; control2X: -116.2; control2Y: 163.201; x: -111.4; y: 164.001 }
+ , PathCubic { control1X: -107.516; control1Y: 164.648; control2X: -98.793; control2Y: 167.717; x: -88.932; y: 169.121 }
+ , PathCubic { control1X: -88.932; control1Y: 169.121; control2X: -71.8; control2Y: 183.201; x: -75; y: 196.001 }
+ , PathCubic { control1X: -75; control1Y: 196.001; control2X: -75.4; control2Y: 212.401; x: -79; y: 214.001 }
+ , PathCubic { control1X: -79; control1Y: 214.001; control2X: -67.4; control2Y: 202.801; x: -77; y: 219.601 }
+ , PathLine { x: -81.4; y: 238.401 }
+ , PathCubic { control1X: -81.4; control1Y: 238.401; control2X: -55.8; control2Y: 216.801; x: -71.4; y: 235.201 }
+ , PathLine { x: -81.4; y: 261.201 }
+ , PathCubic { control1X: -81.4; control1Y: 261.201; control2X: -61.8; control2Y: 242.801; x: -69; y: 251.201 }
+ , PathLine { x: -72.2; y: 260.001 }
+ , PathCubic { control1X: -72.2; control1Y: 260.001; control2X: -29; control2Y: 232.801; x: -59.8; y: 262.401 }
+ , PathCubic { control1X: -59.8; control1Y: 262.401; control2X: -51.8; control2Y: 258.801; x: -47.4; y: 261.601 }
+ , PathCubic { control1X: -47.4; control1Y: 261.601; control2X: -40.6; control2Y: 260.401; x: -41.4; y: 262.001 }
+ , PathCubic { control1X: -41.4; control1Y: 262.001; control2X: -62.2; control2Y: 272.401; x: -65.8; y: 290.801 }
+ , PathCubic { control1X: -65.8; control1Y: 290.801; control2X: -57.4; control2Y: 280.801; x: -60.6; y: 291.601 }
+ , PathLine { x: -60.2; y: 303.201 }
+ , PathCubic { control1X: -60.2; control1Y: 303.201; control2X: -56.2; control2Y: 281.601; x: -56.6; y: 319.201 }
+ , PathCubic { control1X: -56.6; control1Y: 319.201; control2X: -37.4; control2Y: 301.201; x: -49; y: 322.001 }
+ , PathLine { x: -49; y: 338.801 }
+ , PathCubic { control1X: -49; control1Y: 338.801; control2X: -33.8; control2Y: 322.401; x: -40.2; y: 335.201 }
+ , PathCubic { control1X: -40.2; control1Y: 335.201; control2X: -30.2; control2Y: 326.401; x: -34.2; y: 341.601 }
+ , PathCubic { control1X: -34.2; control1Y: 341.601; control2X: -35; control2Y: 352.001; x: -30.6; y: 340.801 }
+ , PathCubic { control1X: -30.6; control1Y: 340.801; control2X: -14.6; control2Y: 310.201; x: -20.6; y: 336.401 }
+ , PathCubic { control1X: -20.6; control1Y: 336.401; control2X: -21.4; control2Y: 355.601; x: -16.6; y: 340.801 }
+ , PathCubic { control1X: -16.6; control1Y: 340.801; control2X: -16.2; control2Y: 351.201; x: -7; y: 358.401 }
+ , PathCubic { control1X: -7; control1Y: 358.401; control2X: -8.2; control2Y: 307.601; x: 4.6; y: 343.601 }
+ , PathLine { x: 8.6; y: 360.001 }
+ , PathCubic { control1X: 8.6; control1Y: 360.001; control2X: 11.4; control2Y: 350.801; x: 11; y: 345.601 }
+ , PathCubic { control1X: 11; control1Y: 345.601; control2X: 25.8; control2Y: 329.201; x: 19; y: 353.601 }
+ , PathCubic { control1X: 19; control1Y: 353.601; control2X: 34.2; control2Y: 330.801; x: 31; y: 344.001 }
+ , PathCubic { control1X: 31; control1Y: 344.001; control2X: 23.4; control2Y: 360.001; x: 25; y: 364.801 }
+ , PathCubic { control1X: 25; control1Y: 364.801; control2X: 41.8; control2Y: 330.001; x: 43; y: 328.401 }
+ , PathCubic { control1X: 43; control1Y: 328.401; control2X: 41; control2Y: 370.802; x: 51.8; y: 334.801 }
+ , PathCubic { control1X: 51.8; control1Y: 334.801; control2X: 57.4; control2Y: 346.801; x: 54.6; y: 351.201 }
+ , PathCubic { control1X: 54.6; control1Y: 351.201; control2X: 62.6; control2Y: 343.201; x: 61.8; y: 340.001 }
+ , PathCubic { control1X: 61.8; control1Y: 340.001; control2X: 66.4; control2Y: 331.801; x: 69.2; y: 345.401 }
+ , PathCubic { control1X: 69.2; control1Y: 345.401; control2X: 71; control2Y: 354.801; x: 72.6; y: 351.601 }
+ , PathCubic { control1X: 72.6; control1Y: 351.601; control2X: 76.6; control2Y: 375.602; x: 77.8; y: 352.801 }
+ , PathCubic { control1X: 77.8; control1Y: 352.801; control2X: 79.4; control2Y: 339.201; x: 72.2; y: 327.601 }
+ , PathCubic { control1X: 72.2; control1Y: 327.601; control2X: 73; control2Y: 324.401; x: 70.2; y: 320.401 }
+ , PathCubic { control1X: 70.2; control1Y: 320.401; control2X: 83.8; control2Y: 342.001; x: 76.6; y: 313.201 }
+ , PathCubic { control1X: 76.6; control1Y: 313.201; control2X: 87.801; control2Y: 321.201; x: 89.001; y: 321.201 }
+ , PathCubic { control1X: 89.001; control1Y: 321.201; control2X: 75.4; control2Y: 298.001; x: 84.2; y: 302.801 }
+ , PathCubic { control1X: 84.2; control1Y: 302.801; control2X: 79; control2Y: 292.401; x: 97.001; y: 304.401 }
+ , PathCubic { control1X: 97.001; control1Y: 304.401; control2X: 81; control2Y: 288.401; x: 98.601; y: 298.001 }
+ , PathCubic { control1X: 98.601; control1Y: 298.001; control2X: 106.601; control2Y: 304.401; x: 99.001; y: 294.401 }
+ , PathCubic { control1X: 99.001; control1Y: 294.401; control2X: 84.6; control2Y: 278.401; x: 106.601; y: 296.401 }
+ , PathCubic { control1X: 106.601; control1Y: 296.401; control2X: 118.201; control2Y: 312.801; x: 119.001; y: 315.601 }
+ , PathCubic { control1X: 119.001; control1Y: 315.601; control2X: 109.001; control2Y: 286.401; x: 104.601; y: 283.601 }
+ , PathCubic { control1X: 104.601; control1Y: 283.601; control2X: 113.001; control2Y: 247.201; x: 154.201; y: 262.801 }
+ , PathCubic { control1X: 154.201; control1Y: 262.801; control2X: 161.001; control2Y: 280.001; x: 165.401; y: 261.601 }
+ , PathCubic { control1X: 165.401; control1Y: 261.601; control2X: 178.201; control2Y: 255.201; x: 189.401; y: 282.801 }
+ , PathCubic { control1X: 189.401; control1Y: 282.801; control2X: 193.401; control2Y: 269.201; x: 192.601; y: 266.401 }
+ , PathCubic { control1X: 192.601; control1Y: 266.401; control2X: 199.401; control2Y: 267.601; x: 198.601; y: 266.401 }
+ , PathCubic { control1X: 198.601; control1Y: 266.401; control2X: 211.801; control2Y: 270.801; x: 213.001; y: 270.001 }
+ , PathCubic { control1X: 213.001; control1Y: 270.001; control2X: 219.801; control2Y: 276.801; x: 220.201; y: 273.201 }
+ , PathCubic { control1X: 220.201; control1Y: 273.201; control2X: 229.401; control2Y: 276.001; x: 227.401; y: 272.401 }
+ , PathCubic { control1X: 227.401; control1Y: 272.401; control2X: 236.201; control2Y: 288.001; x: 236.601; y: 291.601 }
+ , PathLine { x: 239.001; y: 277.601 }
+ , PathLine { x: 241.001; y: 280.401 }
+ , PathCubic { control1X: 241.001; control1Y: 280.401; control2X: 242.601; control2Y: 272.801; x: 241.801; y: 271.601 }
+ , PathCubic { control1X: 241.001; control1Y: 270.401; control2X: 261.801; control2Y: 278.401; x: 266.601; y: 299.201 }
+ , PathLine { x: 268.601; y: 307.601 }
+ , PathCubic { control1X: 268.601; control1Y: 307.601; control2X: 274.601; control2Y: 292.801; x: 273.001; y: 288.801 }
+ , PathCubic { control1X: 273.001; control1Y: 288.801; control2X: 278.201; control2Y: 289.601; x: 278.601; y: 294.001 }
+ , PathCubic { control1X: 278.601; control1Y: 294.001; control2X: 282.601; control2Y: 270.801; x: 277.801; y: 264.801 }
+ , PathCubic { control1X: 277.801; control1Y: 264.801; control2X: 282.201; control2Y: 264.001; x: 283.401; y: 267.601 }
+ , PathLine { x: 283.401; y: 260.401 }
+ , PathCubic { control1X: 283.401; control1Y: 260.401; control2X: 290.601; control2Y: 261.201; x: 290.601; y: 258.801 }
+ , PathCubic { control1X: 290.601; control1Y: 258.801; control2X: 295.001; control2Y: 254.801; x: 297.001; y: 259.601 }
+ , PathCubic { control1X: 297.001; control1Y: 259.601; control2X: 284.601; control2Y: 224.401; x: 303.001; y: 243.601 }
+ , PathCubic { control1X: 303.001; control1Y: 243.601; control2X: 310.201; control2Y: 254.401; x: 306.601; y: 235.601 }
+ , PathCubic { control1X: 303.001; control1Y: 216.801; control2X: 299.001; control2Y: 215.201; x: 303.801; y: 214.801 }
+ , PathCubic { control1X: 303.801; control1Y: 214.801; control2X: 304.601; control2Y: 211.201; x: 302.601; y: 209.601 }
+ , PathCubic { control1X: 300.601; control1Y: 208.001; control2X: 303.801; control2Y: 209.601; x: 303.801; y: 209.601 }
+ , PathCubic { control1X: 303.801; control1Y: 209.601; control2X: 308.601; control2Y: 213.601; x: 303.401; y: 191.601 }
+ , PathCubic { control1X: 303.401; control1Y: 191.601; control2X: 309.801; control2Y: 193.201; x: 297.801; y: 164.001 }
+ , PathCubic { control1X: 297.801; control1Y: 164.001; control2X: 300.601; control2Y: 161.601; x: 296.601; y: 153.201 }
+ , PathCubic { control1X: 296.601; control1Y: 153.201; control2X: 304.601; control2Y: 157.601; x: 307.401; y: 156.001 }
+ , PathCubic { control1X: 307.401; control1Y: 156.001; control2X: 307.001; control2Y: 154.401; x: 303.801; y: 150.401 }
+ , PathCubic { control1X: 303.801; control1Y: 150.401; control2X: 282.201; control2Y: 95.6; x: 302.601; y: 117.601 }
+ , PathCubic { control1X: 302.601; control1Y: 117.601; control2X: 314.451; control2Y: 131.151; x: 308.051; y: 108.351 }
+ , PathCubic { control1X: 308.051; control1Y: 108.351; control2X: 298.94; control2Y: 84.341; x: 299.717; y: 80.045 }
+ , PathLine { x: -129.83; y: 103.065 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ delegate: [PathMove { x: 299.717; y: 80.245 }
+ , PathCubic { control1X: 300.345; control1Y: 80.426; control2X: 302.551; control2Y: 81.55; x: 303.801; y: 83.2 }
+ , PathCubic { control1X: 303.801; control1Y: 83.2; control2X: 310.601; control2Y: 94; x: 305.401; y: 75.6 }
+ , PathCubic { control1X: 305.401; control1Y: 75.6; control2X: 296.201; control2Y: 46.8; x: 305.001; y: 58 }
+ , PathCubic { control1X: 305.001; control1Y: 58; control2X: 311.001; control2Y: 65.2; x: 307.801; y: 51.6 }
+ , PathCubic { control1X: 303.936; control1Y: 35.173; control2X: 301.401; control2Y: 28.8; x: 301.401; y: 28.8 }
+ , PathCubic { control1X: 301.401; control1Y: 28.8; control2X: 313.001; control2Y: 33.6; x: 286.201; y: -6 }
+ , PathLine { x: 295.001; y: -2.4 }
+ , PathCubic { control1X: 295.001; control1Y: -2.4; control2X: 275.401; control2Y: -42; x: 253.801; y: -47.2 }
+ , PathLine { x: 245.801; y: -53.2 }
+ , PathCubic { control1X: 245.801; control1Y: -53.2; control2X: 284.201; control2Y: -91.2; x: 271.401; y: -128 }
+ , PathCubic { control1X: 271.401; control1Y: -128; control2X: 264.601; control2Y: -133.2; x: 255.001; y: -124 }
+ , PathCubic { control1X: 255.001; control1Y: -124; control2X: 248.601; control2Y: -119.2; x: 242.601; y: -120.8 }
+ , PathCubic { control1X: 242.601; control1Y: -120.8; control2X: 211.801; control2Y: -119.6; x: 209.801; y: -119.6 }
+ , PathCubic { control1X: 207.801; control1Y: -119.6; control2X: 173.001; control2Y: -156.8; x: 107.401; y: -139.2 }
+ , PathCubic { control1X: 107.401; control1Y: -139.2; control2X: 102.201; control2Y: -137.2; x: 97.801; y: -138.4 }
+ , PathCubic { control1X: 97.801; control1Y: -138.4; control2X: 79.4; control2Y: -154.4; x: 30.6; y: -131.6 }
+ , PathCubic { control1X: 30.6; control1Y: -131.6; control2X: 20.6; control2Y: -129.6; x: 19; y: -129.6 }
+ , PathCubic { control1X: 17.4; control1Y: -129.6; control2X: 14.6; control2Y: -129.6; x: 6.6; y: -123.2 }
+ , PathCubic { control1X: -1.4; control1Y: -116.8; control2X: -1.8; control2Y: -116; x: -3.8; y: -114.4 }
+ , PathCubic { control1X: -3.8; control1Y: -114.4; control2X: -20.2; control2Y: -103.2; x: -25; y: -102.4 }
+ , PathCubic { control1X: -25; control1Y: -102.4; control2X: -36.6; control2Y: -96; x: -41; y: -86 }
+ , PathLine { x: -44.6; y: -84.8 }
+ , PathCubic { control1X: -44.6; control1Y: -84.8; control2X: -46.2; control2Y: -77.6; x: -46.6; y: -76.4 }
+ , PathCubic { control1X: -46.6; control1Y: -76.4; control2X: -51.4; control2Y: -72.8; x: -52.2; y: -67.2 }
+ , PathCubic { control1X: -52.2; control1Y: -67.2; control2X: -61; control2Y: -61.2; x: -60.6; y: -56.8 }
+ , PathCubic { control1X: -60.6; control1Y: -56.8; control2X: -62.2; control2Y: -51.6; x: -63; y: -46.8 }
+ , PathCubic { control1X: -63; control1Y: -46.8; control2X: -70.2; control2Y: -42; x: -69.4; y: -39.2 }
+ , PathCubic { control1X: -69.4; control1Y: -39.2; control2X: -77; control2Y: -25.2; x: -75.8; y: -18.4 }
+ , PathCubic { control1X: -75.8; control1Y: -18.4; control2X: -82.2; control2Y: -18.8; x: -85; y: -16.4 }
+ , PathCubic { control1X: -85; control1Y: -16.4; control2X: -85.8; control2Y: -11.6; x: -87.4; y: -11.2 }
+ , PathCubic { control1X: -87.4; control1Y: -11.2; control2X: -90.2; control2Y: -10; x: -87.8; y: -6 }
+ , PathCubic { control1X: -87.8; control1Y: -6; control2X: -89.4; control2Y: -3.2; x: -89.8; y: -1.6 }
+ , PathCubic { control1X: -89.8; control1Y: -1.6; control2X: -89; control2Y: 1.2; x: -93.4; y: 6.8 }
+ , PathCubic { control1X: -93.4; control1Y: 6.8; control2X: -99.8; control2Y: 25.6; x: -97.8; y: 30.8 }
+ , PathCubic { control1X: -97.8; control1Y: 30.8; control2X: -97.4; control2Y: 35.6; x: -100.2; y: 37.2 }
+ , PathCubic { control1X: -100.2; control1Y: 37.2; control2X: -103.8; control2Y: 36.8; x: -95.4; y: 48.8 }
+ , PathCubic { control1X: -95.4; control1Y: 48.8; control2X: -94.6; control2Y: 50; x: -97.8; y: 52.4 }
+ , PathCubic { control1X: -97.8; control1Y: 52.4; control2X: -115; control2Y: 56; x: -117.4; y: 72.4 }
+ , PathCubic { control1X: -117.4; control1Y: 72.4; control2X: -131; control2Y: 87.2; x: -131; y: 92.4 }
+ , PathCubic { control1X: -131; control1Y: 94.705; control2X: -130.729; control2Y: 97.852; x: -130.03; y: 102.465 }
+ , PathCubic { control1X: -130.03; control1Y: 102.465; control2X: -130.6; control2Y: 110.801; x: -103; y: 111.601 }
+ , PathCubic { control1X: -75.4; control1Y: 112.401; control2X: 299.717; control2Y: 80.245; x: 299.717; y: 80.245 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: -115.6; y: 102.6 }
+ , PathCubic { control1X: -140.6; control1Y: 63.2; control2X: -126.2; control2Y: 119.601; x: -126.2; y: 119.601 }
+ , PathCubic { control1X: -117.4; control1Y: 154.001; control2X: 12.2; control2Y: 116.401; x: 12.2; y: 116.401 }
+ , PathCubic { control1X: 12.2; control1Y: 116.401; control2X: 181.001; control2Y: 86; x: 192.201; y: 82 }
+ , PathCubic { control1X: 203.401; control1Y: 78; control2X: 298.601; control2Y: 84.4; x: 298.601; y: 84.4 }
+ , PathLine { x: 293.001; y: 67.6 }
+ , PathCubic { control1X: 228.201; control1Y: 21.2; control2X: 209.001; control2Y: 44.4; x: 195.401; y: 40.4 }
+ , PathCubic { control1X: 181.801; control1Y: 36.4; control2X: 184.201; control2Y: 46; x: 181.001; y: 46.8 }
+ , PathCubic { control1X: 177.801; control1Y: 47.6; control2X: 138.601; control2Y: 22.8; x: 132.201; y: 23.6 }
+ , PathCubic { control1X: 125.801; control1Y: 24.4; control2X: 100.459; control2Y: 0.649; x: 115.401; y: 32.4 }
+ , PathCubic { control1X: 131.401; control1Y: 66.4; control2X: 57; control2Y: 71.6; x: 40.2; y: 60.4 }
+ , PathCubic { control1X: 23.4; control1Y: 49.2; control2X: 47.4; control2Y: 78.8; x: 47.4; y: 78.8 }
+ , PathCubic { control1X: 65.8; control1Y: 98.8; control2X: 31.4; control2Y: 82; x: 31.4; y: 82 }
+ , PathCubic { control1X: -3; control1Y: 69.2; control2X: -27; control2Y: 94.8; x: -30.2; y: 95.6 }
+ , PathCubic { control1X: -33.4; control1Y: 96.4; control2X: -38.2; control2Y: 99.6; x: -39; y: 93.2 }
+ , PathCubic { control1X: -39.8; control1Y: 86.8; control2X: -47.31; control2Y: 70.099; x: -79; y: 96.4 }
+ , PathCubic { control1X: -99; control1Y: 113.001; control2X: -112.8; control2Y: 91; x: -112.8; y: 91 }
+ , PathLine { x: -115.6; y: 102.6 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#e87f3a"
+ strokeWidth: -1
+ delegate: [PathMove { x: 133.51; y: 25.346 }
+ , PathCubic { control1X: 127.11; control1Y: 26.146; control2X: 101.743; control2Y: 2.407; x: 116.71; y: 34.146 }
+ , PathCubic { control1X: 133.31; control1Y: 69.346; control2X: 58.31; control2Y: 73.346; x: 41.51; y: 62.146 }
+ , PathCubic { control1X: 24.709; control1Y: 50.946; control2X: 48.71; control2Y: 80.546; x: 48.71; y: 80.546 }
+ , PathCubic { control1X: 67.11; control1Y: 100.546; control2X: 32.709; control2Y: 83.746; x: 32.709; y: 83.746 }
+ , PathCubic { control1X: -1.691; control1Y: 70.946; control2X: -25.691; control2Y: 96.546; x: -28.891; y: 97.346 }
+ , PathCubic { control1X: -32.091; control1Y: 98.146; control2X: -36.891; control2Y: 101.346; x: -37.691; y: 94.946 }
+ , PathCubic { control1X: -38.491; control1Y: 88.546; control2X: -45.87; control2Y: 72.012; x: -77.691; y: 98.146 }
+ , PathCubic { control1X: -98.927; control1Y: 115.492; control2X: -112.418; control2Y: 94.037; x: -112.418; y: 94.037 }
+ , PathLine { x: -115.618; y: 104.146 }
+ , PathCubic { control1X: -140.618; control1Y: 64.346; control2X: -125.546; control2Y: 122.655; x: -125.546; y: 122.655 }
+ , PathCubic { control1X: -116.745; control1Y: 157.056; control2X: 13.509; control2Y: 118.146; x: 13.509; y: 118.146 }
+ , PathCubic { control1X: 13.509; control1Y: 118.146; control2X: 182.31; control2Y: 87.746; x: 193.51; y: 83.746 }
+ , PathCubic { control1X: 204.71; control1Y: 79.746; control2X: 299.038; control2Y: 86.073; x: 299.038; y: 86.073 }
+ , PathLine { x: 293.51; y: 68.764 }
+ , PathCubic { control1X: 228.71; control1Y: 22.364; control2X: 210.31; control2Y: 46.146; x: 196.71; y: 42.146 }
+ , PathCubic { control1X: 183.11; control1Y: 38.146; control2X: 185.51; control2Y: 47.746; x: 182.31; y: 48.546 }
+ , PathCubic { control1X: 179.11; control1Y: 49.346; control2X: 139.91; control2Y: 24.546; x: 133.51; y: 25.346 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ea8c4d"
+ strokeWidth: -1
+ delegate: [PathMove { x: 134.819; y: 27.091 }
+ , PathCubic { control1X: 128.419; control1Y: 27.891; control2X: 103.685; control2Y: 3.862; x: 118.019; y: 35.891 }
+ , PathCubic { control1X: 134.219; control1Y: 72.092; control2X: 59.619; control2Y: 75.092; x: 42.819; y: 63.892 }
+ , PathCubic { control1X: 26.019; control1Y: 52.692; control2X: 50.019; control2Y: 82.292; x: 50.019; y: 82.292 }
+ , PathCubic { control1X: 68.419; control1Y: 102.292; control2X: 34.019; control2Y: 85.492; x: 34.019; y: 85.492 }
+ , PathCubic { control1X: -0.381; control1Y: 72.692; control2X: -24.382; control2Y: 98.292; x: -27.582; y: 99.092 }
+ , PathCubic { control1X: -30.782; control1Y: 99.892; control2X: -35.582; control2Y: 103.092; x: -36.382; y: 96.692 }
+ , PathCubic { control1X: -37.182; control1Y: 90.292; control2X: -44.43; control2Y: 73.925; x: -76.382; y: 99.892 }
+ , PathCubic { control1X: -98.855; control1Y: 117.983; control2X: -112.036; control2Y: 97.074; x: -112.036; y: 97.074 }
+ , PathLine { x: -115.636; y: 105.692 }
+ , PathCubic { control1X: -139.436; control1Y: 66.692; control2X: -124.891; control2Y: 125.71; x: -124.891; y: 125.71 }
+ , PathCubic { control1X: -116.091; control1Y: 160.11; control2X: 14.819; control2Y: 119.892; x: 14.819; y: 119.892 }
+ , PathCubic { control1X: 14.819; control1Y: 119.892; control2X: 183.619; control2Y: 89.492; x: 194.819; y: 85.492 }
+ , PathCubic { control1X: 206.019; control1Y: 81.492; control2X: 299.474; control2Y: 87.746; x: 299.474; y: 87.746 }
+ , PathLine { x: 294.02; y: 69.928 }
+ , PathCubic { control1X: 229.219; control1Y: 23.528; control2X: 211.619; control2Y: 47.891; x: 198.019; y: 43.891 }
+ , PathCubic { control1X: 184.419; control1Y: 39.891; control2X: 186.819; control2Y: 49.491; x: 183.619; y: 50.292 }
+ , PathCubic { control1X: 180.419; control1Y: 51.092; control2X: 141.219; control2Y: 26.291; x: 134.819; y: 27.091 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ec9961"
+ strokeWidth: -1
+ delegate: [PathMove { x: 136.128; y: 28.837 }
+ , PathCubic { control1X: 129.728; control1Y: 29.637; control2X: 104.999; control2Y: 5.605; x: 119.328; y: 37.637 }
+ , PathCubic { control1X: 136.128; control1Y: 75.193; control2X: 60.394; control2Y: 76.482; x: 44.128; y: 65.637 }
+ , PathCubic { control1X: 27.328; control1Y: 54.437; control2X: 51.328; control2Y: 84.037; x: 51.328; y: 84.037 }
+ , PathCubic { control1X: 69.728; control1Y: 104.037; control2X: 35.328; control2Y: 87.237; x: 35.328; y: 87.237 }
+ , PathCubic { control1X: 0.928; control1Y: 74.437; control2X: -23.072; control2Y: 100.037; x: -26.272; y: 100.837 }
+ , PathCubic { control1X: -29.472; control1Y: 101.637; control2X: -34.272; control2Y: 104.837; x: -35.072; y: 98.437 }
+ , PathCubic { control1X: -35.872; control1Y: 92.037; control2X: -42.989; control2Y: 75.839; x: -75.073; y: 101.637 }
+ , PathCubic { control1X: -98.782; control1Y: 120.474; control2X: -111.655; control2Y: 100.11; x: -111.655; y: 100.11 }
+ , PathLine { x: -115.655; y: 107.237 }
+ , PathCubic { control1X: -137.455; control1Y: 70.437; control2X: -124.236; control2Y: 128.765; x: -124.236; y: 128.765 }
+ , PathCubic { control1X: -115.436; control1Y: 163.165; control2X: 16.128; control2Y: 121.637; x: 16.128; y: 121.637 }
+ , PathCubic { control1X: 16.128; control1Y: 121.637; control2X: 184.928; control2Y: 91.237; x: 196.129; y: 87.237 }
+ , PathCubic { control1X: 207.329; control1Y: 83.237; control2X: 299.911; control2Y: 89.419; x: 299.911; y: 89.419 }
+ , PathLine { x: 294.529; y: 71.092 }
+ , PathCubic { control1X: 229.729; control1Y: 24.691; control2X: 212.929; control2Y: 49.637; x: 199.329; y: 45.637 }
+ , PathCubic { control1X: 185.728; control1Y: 41.637; control2X: 188.128; control2Y: 51.237; x: 184.928; y: 52.037 }
+ , PathCubic { control1X: 181.728; control1Y: 52.837; control2X: 142.528; control2Y: 28.037; x: 136.128; y: 28.837 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#eea575"
+ strokeWidth: -1
+ delegate: [PathMove { x: 137.438; y: 30.583 }
+ , PathCubic { control1X: 131.037; control1Y: 31.383; control2X: 106.814; control2Y: 7.129; x: 120.637; y: 39.383 }
+ , PathCubic { control1X: 137.438; control1Y: 78.583; control2X: 62.237; control2Y: 78.583; x: 45.437; y: 67.383 }
+ , PathCubic { control1X: 28.637; control1Y: 56.183; control2X: 52.637; control2Y: 85.783; x: 52.637; y: 85.783 }
+ , PathCubic { control1X: 71.037; control1Y: 105.783; control2X: 36.637; control2Y: 88.983; x: 36.637; y: 88.983 }
+ , PathCubic { control1X: 2.237; control1Y: 76.183; control2X: -21.763; control2Y: 101.783; x: -24.963; y: 102.583 }
+ , PathCubic { control1X: -28.163; control1Y: 103.383; control2X: -32.963; control2Y: 106.583; x: -33.763; y: 100.183 }
+ , PathCubic { control1X: -34.563; control1Y: 93.783; control2X: -41.548; control2Y: 77.752; x: -73.763; y: 103.383 }
+ , PathCubic { control1X: -98.709; control1Y: 122.965; control2X: -111.273; control2Y: 103.146; x: -111.273; y: 103.146 }
+ , PathLine { x: -115.673; y: 108.783 }
+ , PathCubic { control1X: -135.473; control1Y: 73.982; control2X: -123.582; control2Y: 131.819; x: -123.582; y: 131.819 }
+ , PathCubic { control1X: -114.782; control1Y: 166.22; control2X: 17.437; control2Y: 123.383; x: 17.437; y: 123.383 }
+ , PathCubic { control1X: 17.437; control1Y: 123.383; control2X: 186.238; control2Y: 92.983; x: 197.438; y: 88.983 }
+ , PathCubic { control1X: 208.638; control1Y: 84.983; control2X: 300.347; control2Y: 91.092; x: 300.347; y: 91.092 }
+ , PathLine { x: 295.038; y: 72.255 }
+ , PathCubic { control1X: 230.238; control1Y: 25.855; control2X: 214.238; control2Y: 51.383; x: 200.638; y: 47.383 }
+ , PathCubic { control1X: 187.038; control1Y: 43.383; control2X: 189.438; control2Y: 52.983; x: 186.238; y: 53.783 }
+ , PathCubic { control1X: 183.038; control1Y: 54.583; control2X: 143.838; control2Y: 29.783; x: 137.438; y: 30.583 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#f1b288"
+ strokeWidth: -1
+ delegate: [PathMove { x: 138.747; y: 32.328 }
+ , PathCubic { control1X: 132.347; control1Y: 33.128; control2X: 106.383; control2Y: 9.677; x: 121.947; y: 41.128 }
+ , PathCubic { control1X: 141.147; control1Y: 79.928; control2X: 63.546; control2Y: 80.328; x: 46.746; y: 69.128 }
+ , PathCubic { control1X: 29.946; control1Y: 57.928; control2X: 53.946; control2Y: 87.528; x: 53.946; y: 87.528 }
+ , PathCubic { control1X: 72.346; control1Y: 107.528; control2X: 37.946; control2Y: 90.728; x: 37.946; y: 90.728 }
+ , PathCubic { control1X: 3.546; control1Y: 77.928; control2X: -20.454; control2Y: 103.528; x: -23.654; y: 104.328 }
+ , PathCubic { control1X: -26.854; control1Y: 105.128; control2X: -31.654; control2Y: 108.328; x: -32.454; y: 101.928 }
+ , PathCubic { control1X: -33.254; control1Y: 95.528; control2X: -40.108; control2Y: 79.665; x: -72.454; y: 105.128 }
+ , PathCubic { control1X: -98.636; control1Y: 125.456; control2X: -110.891; control2Y: 106.183; x: -110.891; y: 106.183 }
+ , PathLine { x: -115.691; y: 110.328 }
+ , PathCubic { control1X: -133.691; control1Y: 77.128; control2X: -122.927; control2Y: 134.874; x: -122.927; y: 134.874 }
+ , PathCubic { control1X: -114.127; control1Y: 169.274; control2X: 18.746; control2Y: 125.128; x: 18.746; y: 125.128 }
+ , PathCubic { control1X: 18.746; control1Y: 125.128; control2X: 187.547; control2Y: 94.728; x: 198.747; y: 90.728 }
+ , PathCubic { control1X: 209.947; control1Y: 86.728; control2X: 300.783; control2Y: 92.764; x: 300.783; y: 92.764 }
+ , PathLine { x: 295.547; y: 73.419 }
+ , PathCubic { control1X: 230.747; control1Y: 27.019; control2X: 215.547; control2Y: 53.128; x: 201.947; y: 49.128 }
+ , PathCubic { control1X: 188.347; control1Y: 45.128; control2X: 190.747; control2Y: 54.728; x: 187.547; y: 55.528 }
+ , PathCubic { control1X: 184.347; control1Y: 56.328; control2X: 145.147; control2Y: 31.528; x: 138.747; y: 32.328 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#f3bf9c"
+ strokeWidth: -1
+ delegate: [PathMove { x: 140.056; y: 34.073 }
+ , PathCubic { control1X: 133.655; control1Y: 34.873; control2X: 107.313; control2Y: 11.613; x: 123.255; y: 42.873 }
+ , PathCubic { control1X: 143.656; control1Y: 82.874; control2X: 64.855; control2Y: 82.074; x: 48.055; y: 70.874 }
+ , PathCubic { control1X: 31.255; control1Y: 59.674; control2X: 55.255; control2Y: 89.274; x: 55.255; y: 89.274 }
+ , PathCubic { control1X: 73.655; control1Y: 109.274; control2X: 39.255; control2Y: 92.474; x: 39.255; y: 92.474 }
+ , PathCubic { control1X: 4.855; control1Y: 79.674; control2X: -19.145; control2Y: 105.274; x: -22.345; y: 106.074 }
+ , PathCubic { control1X: -25.545; control1Y: 106.874; control2X: -30.345; control2Y: 110.074; x: -31.145; y: 103.674 }
+ , PathCubic { control1X: -31.945; control1Y: 97.274; control2X: -38.668; control2Y: 81.578; x: -71.145; y: 106.874 }
+ , PathCubic { control1X: -98.564; control1Y: 127.947; control2X: -110.509; control2Y: 109.219; x: -110.509; y: 109.219 }
+ , PathLine { x: -115.709; y: 111.874 }
+ , PathCubic { control1X: -131.709; control1Y: 81.674; control2X: -122.273; control2Y: 137.929; x: -122.273; y: 137.929 }
+ , PathCubic { control1X: -113.473; control1Y: 172.329; control2X: 20.055; control2Y: 126.874; x: 20.055; y: 126.874 }
+ , PathCubic { control1X: 20.055; control1Y: 126.874; control2X: 188.856; control2Y: 96.474; x: 200.056; y: 92.474 }
+ , PathCubic { control1X: 211.256; control1Y: 88.474; control2X: 301.22; control2Y: 94.437; x: 301.22; y: 94.437 }
+ , PathLine { x: 296.056; y: 74.583 }
+ , PathCubic { control1X: 231.256; control1Y: 28.183; control2X: 216.856; control2Y: 54.874; x: 203.256; y: 50.874 }
+ , PathCubic { control1X: 189.656; control1Y: 46.873; control2X: 192.056; control2Y: 56.474; x: 188.856; y: 57.274 }
+ , PathCubic { control1X: 185.656; control1Y: 58.074; control2X: 146.456; control2Y: 33.273; x: 140.056; y: 34.073 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#f5ccb0"
+ strokeWidth: -1
+ delegate: [PathMove { x: 141.365; y: 35.819 }
+ , PathCubic { control1X: 134.965; control1Y: 36.619; control2X: 107.523; control2Y: 13.944; x: 124.565; y: 44.619 }
+ , PathCubic { control1X: 146.565; control1Y: 84.219; control2X: 66.164; control2Y: 83.819; x: 49.364; y: 72.619 }
+ , PathCubic { control1X: 32.564; control1Y: 61.419; control2X: 56.564; control2Y: 91.019; x: 56.564; y: 91.019 }
+ , PathCubic { control1X: 74.964; control1Y: 111.019; control2X: 40.564; control2Y: 94.219; x: 40.564; y: 94.219 }
+ , PathCubic { control1X: 6.164; control1Y: 81.419; control2X: -17.836; control2Y: 107.019; x: -21.036; y: 107.819 }
+ , PathCubic { control1X: -24.236; control1Y: 108.619; control2X: -29.036; control2Y: 111.819; x: -29.836; y: 105.419 }
+ , PathCubic { control1X: -30.636; control1Y: 99.019; control2X: -37.227; control2Y: 83.492; x: -69.836; y: 108.619 }
+ , PathCubic { control1X: -98.491; control1Y: 130.438; control2X: -110.127; control2Y: 112.256; x: -110.127; y: 112.256 }
+ , PathLine { x: -115.727; y: 113.419 }
+ , PathCubic { control1X: -130.128; control1Y: 85.019; control2X: -121.618; control2Y: 140.983; x: -121.618; y: 140.983 }
+ , PathCubic { control1X: -112.818; control1Y: 175.384; control2X: 21.364; control2Y: 128.619; x: 21.364; y: 128.619 }
+ , PathCubic { control1X: 21.364; control1Y: 128.619; control2X: 190.165; control2Y: 98.219; x: 201.365; y: 94.219 }
+ , PathCubic { control1X: 212.565; control1Y: 90.219; control2X: 301.656; control2Y: 96.11; x: 301.656; y: 96.11 }
+ , PathLine { x: 296.565; y: 75.746 }
+ , PathCubic { control1X: 231.765; control1Y: 29.346; control2X: 218.165; control2Y: 56.619; x: 204.565; y: 52.619 }
+ , PathCubic { control1X: 190.965; control1Y: 48.619; control2X: 193.365; control2Y: 58.219; x: 190.165; y: 59.019 }
+ , PathCubic { control1X: 186.965; control1Y: 59.819; control2X: 147.765; control2Y: 35.019; x: 141.365; y: 35.819 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#f8d8c4"
+ strokeWidth: -1
+ delegate: [PathMove { x: 142.674; y: 37.565 }
+ , PathCubic { control1X: 136.274; control1Y: 38.365; control2X: 108.832; control2Y: 15.689; x: 125.874; y: 46.365 }
+ , PathCubic { control1X: 147.874; control1Y: 85.965; control2X: 67.474; control2Y: 85.565; x: 50.674; y: 74.365 }
+ , PathCubic { control1X: 33.874; control1Y: 63.165; control2X: 57.874; control2Y: 92.765; x: 57.874; y: 92.765 }
+ , PathCubic { control1X: 76.274; control1Y: 112.765; control2X: 41.874; control2Y: 95.965; x: 41.874; y: 95.965 }
+ , PathCubic { control1X: 7.473; control1Y: 83.165; control2X: -16.527; control2Y: 108.765; x: -19.727; y: 109.565 }
+ , PathCubic { control1X: -22.927; control1Y: 110.365; control2X: -27.727; control2Y: 113.565; x: -28.527; y: 107.165 }
+ , PathCubic { control1X: -29.327; control1Y: 100.765; control2X: -35.786; control2Y: 85.405; x: -68.527; y: 110.365 }
+ , PathCubic { control1X: -98.418; control1Y: 132.929; control2X: -109.745; control2Y: 115.293; x: -109.745; y: 115.293 }
+ , PathLine { x: -115.745; y: 114.965 }
+ , PathCubic { control1X: -129.346; control1Y: 88.564; control2X: -120.963; control2Y: 144.038; x: -120.963; y: 144.038 }
+ , PathCubic { control1X: -112.163; control1Y: 178.438; control2X: 22.673; control2Y: 130.365; x: 22.673; y: 130.365 }
+ , PathCubic { control1X: 22.673; control1Y: 130.365; control2X: 191.474; control2Y: 99.965; x: 202.674; y: 95.965 }
+ , PathCubic { control1X: 213.874; control1Y: 91.965; control2X: 302.093; control2Y: 97.783; x: 302.093; y: 97.783 }
+ , PathLine { x: 297.075; y: 76.91 }
+ , PathCubic { control1X: 232.274; control1Y: 30.51; control2X: 219.474; control2Y: 58.365; x: 205.874; y: 54.365 }
+ , PathCubic { control1X: 192.274; control1Y: 50.365; control2X: 194.674; control2Y: 59.965; x: 191.474; y: 60.765 }
+ , PathCubic { control1X: 188.274; control1Y: 61.565; control2X: 149.074; control2Y: 36.765; x: 142.674; y: 37.565 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#fae5d7"
+ strokeWidth: -1
+ delegate: [PathMove { x: 143.983; y: 39.31 }
+ , PathCubic { control1X: 137.583; control1Y: 40.11; control2X: 110.529; control2Y: 17.223; x: 127.183; y: 48.11 }
+ , PathCubic { control1X: 149.183; control1Y: 88.91; control2X: 68.783; control2Y: 87.31; x: 51.983; y: 76.11 }
+ , PathCubic { control1X: 35.183; control1Y: 64.91; control2X: 59.183; control2Y: 94.51; x: 59.183; y: 94.51 }
+ , PathCubic { control1X: 77.583; control1Y: 114.51; control2X: 43.183; control2Y: 97.71; x: 43.183; y: 97.71 }
+ , PathCubic { control1X: 8.783; control1Y: 84.91; control2X: -15.217; control2Y: 110.51; x: -18.417; y: 111.31 }
+ , PathCubic { control1X: -21.618; control1Y: 112.11; control2X: -26.418; control2Y: 115.31; x: -27.218; y: 108.91 }
+ , PathCubic { control1X: -28.018; control1Y: 102.51; control2X: -34.346; control2Y: 87.318; x: -67.218; y: 112.11 }
+ , PathCubic { control1X: -98.345; control1Y: 135.42; control2X: -109.363; control2Y: 118.329; x: -109.363; y: 118.329 }
+ , PathLine { x: -115.764; y: 116.51 }
+ , PathCubic { control1X: -128.764; control1Y: 92.51; control2X: -120.309; control2Y: 147.093; x: -120.309; y: 147.093 }
+ , PathCubic { control1X: -111.509; control1Y: 181.493; control2X: 23.983; control2Y: 132.11; x: 23.983; y: 132.11 }
+ , PathCubic { control1X: 23.983; control1Y: 132.11; control2X: 192.783; control2Y: 101.71; x: 203.983; y: 97.71 }
+ , PathCubic { control1X: 215.183; control1Y: 93.71; control2X: 302.529; control2Y: 99.456; x: 302.529; y: 99.456 }
+ , PathLine { x: 297.583; y: 78.074 }
+ , PathCubic { control1X: 232.783; control1Y: 31.673; control2X: 220.783; control2Y: 60.11; x: 207.183; y: 56.11 }
+ , PathCubic { control1X: 193.583; control1Y: 52.11; control2X: 195.983; control2Y: 61.71; x: 192.783; y: 62.51 }
+ , PathCubic { control1X: 189.583; control1Y: 63.31; control2X: 150.383; control2Y: 38.51; x: 143.983; y: 39.31 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#fcf2eb"
+ strokeWidth: -1
+ delegate: [PathMove { x: 145.292; y: 41.055 }
+ , PathCubic { control1X: 138.892; control1Y: 41.855; control2X: 112.917; control2Y: 18.411; x: 128.492; y: 49.855 }
+ , PathCubic { control1X: 149.692; control1Y: 92.656; control2X: 70.092; control2Y: 89.056; x: 53.292; y: 77.856 }
+ , PathCubic { control1X: 36.492; control1Y: 66.656; control2X: 60.492; control2Y: 96.256; x: 60.492; y: 96.256 }
+ , PathCubic { control1X: 78.892; control1Y: 116.256; control2X: 44.492; control2Y: 99.456; x: 44.492; y: 99.456 }
+ , PathCubic { control1X: 10.092; control1Y: 86.656; control2X: -13.908; control2Y: 112.256; x: -17.108; y: 113.056 }
+ , PathCubic { control1X: -20.308; control1Y: 113.856; control2X: -25.108; control2Y: 117.056; x: -25.908; y: 110.656 }
+ , PathCubic { control1X: -26.708; control1Y: 104.256; control2X: -32.905; control2Y: 89.232; x: -65.908; y: 113.856 }
+ , PathCubic { control1X: -98.273; control1Y: 137.911; control2X: -108.982; control2Y: 121.365; x: -108.982; y: 121.365 }
+ , PathLine { x: -115.782; y: 118.056 }
+ , PathCubic { control1X: -128.582; control1Y: 94.856; control2X: -119.654; control2Y: 150.147; x: -119.654; y: 150.147 }
+ , PathCubic { control1X: -110.854; control1Y: 184.547; control2X: 25.292; control2Y: 133.856; x: 25.292; y: 133.856 }
+ , PathCubic { control1X: 25.292; control1Y: 133.856; control2X: 194.093; control2Y: 103.456; x: 205.293; y: 99.456 }
+ , PathCubic { control1X: 216.493; control1Y: 95.456; control2X: 302.965; control2Y: 101.128; x: 302.965; y: 101.128 }
+ , PathLine { x: 298.093; y: 79.237 }
+ , PathCubic { control1X: 233.292; control1Y: 32.837; control2X: 222.093; control2Y: 61.856; x: 208.493; y: 57.856 }
+ , PathCubic { control1X: 194.893; control1Y: 53.855; control2X: 197.293; control2Y: 63.456; x: 194.093; y: 64.256 }
+ , PathCubic { control1X: 190.892; control1Y: 65.056; control2X: 151.692; control2Y: 40.255; x: 145.292; y: 41.055 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: -115.8; y: 119.601 }
+ , PathCubic { control1X: -128.6; control1Y: 97.6; control2X: -119; control2Y: 153.201; x: -119; y: 153.201 }
+ , PathCubic { control1X: -110.2; control1Y: 187.601; control2X: 26.6; control2Y: 135.601; x: 26.6; y: 135.601 }
+ , PathCubic { control1X: 26.6; control1Y: 135.601; control2X: 195.401; control2Y: 105.2; x: 206.601; y: 101.2 }
+ , PathCubic { control1X: 217.801; control1Y: 97.2; control2X: 303.401; control2Y: 102.8; x: 303.401; y: 102.8 }
+ , PathLine { x: 298.601; y: 80.4 }
+ , PathCubic { control1X: 233.801; control1Y: 34; control2X: 223.401; control2Y: 63.6; x: 209.801; y: 59.6 }
+ , PathCubic { control1X: 196.201; control1Y: 55.6; control2X: 198.601; control2Y: 65.2; x: 195.401; y: 66 }
+ , PathCubic { control1X: 192.201; control1Y: 66.8; control2X: 153.001; control2Y: 42; x: 146.601; y: 42.8 }
+ , PathCubic { control1X: 140.201; control1Y: 43.6; control2X: 114.981; control2Y: 19.793; x: 129.801; y: 51.6 }
+ , PathCubic { control1X: 152.028; control1Y: 99.307; control2X: 69.041; control2Y: 89.227; x: 54.6; y: 79.6 }
+ , PathCubic { control1X: 37.8; control1Y: 68.4; control2X: 61.8; control2Y: 98; x: 61.8; y: 98 }
+ , PathCubic { control1X: 80.2; control1Y: 118.001; control2X: 45.8; control2Y: 101.2; x: 45.8; y: 101.2 }
+ , PathCubic { control1X: 11.4; control1Y: 88.4; control2X: -12.6; control2Y: 114.001; x: -15.8; y: 114.801 }
+ , PathCubic { control1X: -19; control1Y: 115.601; control2X: -23.8; control2Y: 118.801; x: -24.6; y: 112.401 }
+ , PathCubic { control1X: -25.4; control1Y: 106; control2X: -31.465; control2Y: 91.144; x: -64.6; y: 115.601 }
+ , PathCubic { control1X: -98.2; control1Y: 140.401; control2X: -108.6; control2Y: 124.401; x: -108.6; y: 124.401 }
+ , PathLine { x: -115.8; y: 119.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -74.2; y: 149.601 }
+ , PathCubic { control1X: -74.2; control1Y: 149.601; control2X: -81.4; control2Y: 161.201; x: -60.6; y: 174.401 }
+ , PathCubic { control1X: -60.6; control1Y: 174.401; control2X: -59.2; control2Y: 175.801; x: -77.2; y: 171.601 }
+ , PathCubic { control1X: -77.2; control1Y: 171.601; control2X: -83.4; control2Y: 169.601; x: -85; y: 159.201 }
+ , PathCubic { control1X: -85; control1Y: 159.201; control2X: -89.8; control2Y: 154.801; x: -94.6; y: 149.201 }
+ , PathCubic { control1X: -99.4; control1Y: 143.601; control2X: -74.2; control2Y: 149.601; x: -74.2; y: 149.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 65.8; y: 102 }
+ , PathCubic { control1X: 65.8; control1Y: 102; control2X: 83.498; control2Y: 128.821; x: 82.9; y: 133.601 }
+ , PathCubic { control1X: 81.6; control1Y: 144.001; control2X: 81.4; control2Y: 153.601; x: 84.6; y: 157.601 }
+ , PathCubic { control1X: 87.801; control1Y: 161.601; control2X: 96.601; control2Y: 194.801; x: 96.601; y: 194.801 }
+ , PathCubic { control1X: 96.601; control1Y: 194.801; control2X: 96.201; control2Y: 196.001; x: 108.601; y: 158.001 }
+ , PathCubic { control1X: 108.601; control1Y: 158.001; control2X: 120.201; control2Y: 142.001; x: 100.201; y: 123.601 }
+ , PathCubic { control1X: 100.201; control1Y: 123.601; control2X: 65; control2Y: 94.8; x: 65.8; y: 102 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -54.2; y: 176.401 }
+ , PathCubic { control1X: -54.2; control1Y: 176.401; control2X: -43; control2Y: 183.601; x: -57.4; y: 214.801 }
+ , PathLine { x: -51; y: 212.401 }
+ , PathCubic { control1X: -51; control1Y: 212.401; control2X: -51.8; control2Y: 223.601; x: -55; y: 226.001 }
+ , PathLine { x: -47.8; y: 222.801 }
+ , PathCubic { control1X: -47.8; control1Y: 222.801; control2X: -43; control2Y: 230.801; x: -47; y: 235.601 }
+ , PathCubic { control1X: -47; control1Y: 235.601; control2X: -30.2; control2Y: 243.601; x: -31; y: 250.001 }
+ , PathCubic { control1X: -31; control1Y: 250.001; control2X: -24.6; control2Y: 242.001; x: -28.6; y: 235.601 }
+ , PathCubic { control1X: -32.6; control1Y: 229.201; control2X: -39.8; control2Y: 233.201; x: -39; y: 214.801 }
+ , PathLine { x: -47.8; y: 218.001 }
+ , PathCubic { control1X: -47.8; control1Y: 218.001; control2X: -42.2; control2Y: 209.201; x: -42.2; y: 202.801 }
+ , PathLine { x: -50.2; y: 205.201 }
+ , PathCubic { control1X: -50.2; control1Y: 205.201; control2X: -34.731; control2Y: 178.623; x: -45.4; y: 177.201 }
+ , PathCubic { control1X: -51.4; control1Y: 176.401; control2X: -54.2; control2Y: 176.401; x: -54.2; y: 176.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -21.8; y: 193.201 }
+ , PathCubic { control1X: -21.8; control1Y: 193.201; control2X: -19; control2Y: 188.801; x: -21.8; y: 189.601 }
+ , PathCubic { control1X: -24.6; control1Y: 190.401; control2X: -55.8; control2Y: 205.201; x: -61.8; y: 214.801 }
+ , PathCubic { control1X: -61.8; control1Y: 214.801; control2X: -27.4; control2Y: 190.401; x: -21.8; y: 193.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -11.4; y: 201.201 }
+ , PathCubic { control1X: -11.4; control1Y: 201.201; control2X: -8.6; control2Y: 196.801; x: -11.4; y: 197.601 }
+ , PathCubic { control1X: -14.2; control1Y: 198.401; control2X: -45.4; control2Y: 213.201; x: -51.4; y: 222.801 }
+ , PathCubic { control1X: -51.4; control1Y: 222.801; control2X: -17; control2Y: 198.401; x: -11.4; y: 201.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 1.8; y: 186.001 }
+ , PathCubic { control1X: 1.8; control1Y: 186.001; control2X: 4.6; control2Y: 181.601; x: 1.8; y: 182.401 }
+ , PathCubic { control1X: -1; control1Y: 183.201; control2X: -32.2; control2Y: 198.001; x: -38.2; y: 207.601 }
+ , PathCubic { control1X: -38.2; control1Y: 207.601; control2X: -3.8; control2Y: 183.201; x: 1.8; y: 186.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -21.4; y: 229.601 }
+ , PathCubic { control1X: -21.4; control1Y: 229.601; control2X: -21.4; control2Y: 223.601; x: -24.2; y: 224.401 }
+ , PathCubic { control1X: -27; control1Y: 225.201; control2X: -63; control2Y: 242.801; x: -69; y: 252.401 }
+ , PathCubic { control1X: -69; control1Y: 252.401; control2X: -27; control2Y: 226.801; x: -21.4; y: 229.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -20.2; y: 218.801 }
+ , PathCubic { control1X: -20.2; control1Y: 218.801; control2X: -19; control2Y: 214.001; x: -21.8; y: 214.801 }
+ , PathCubic { control1X: -23.8; control1Y: 214.801; control2X: -50.2; control2Y: 226.401; x: -56.2; y: 236.001 }
+ , PathCubic { control1X: -56.2; control1Y: 236.001; control2X: -26.6; control2Y: 214.401; x: -20.2; y: 218.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -34.6; y: 266.401 }
+ , PathLine { x: -44.6; y: 274.001 }
+ , PathCubic { control1X: -44.6; control1Y: 274.001; control2X: -34.2; control2Y: 266.401; x: -30.6; y: 267.601 }
+ , PathCubic { control1X: -30.6; control1Y: 267.601; control2X: -37.4; control2Y: 278.801; x: -38.2; y: 284.001 }
+ , PathCubic { control1X: -38.2; control1Y: 284.001; control2X: -27.8; control2Y: 271.201; x: -22.2; y: 271.601 }
+ , PathCubic { control1X: -22.2; control1Y: 271.601; control2X: -14.6; control2Y: 272.001; x: -14.6; y: 282.801 }
+ , PathCubic { control1X: -14.6; control1Y: 282.801; control2X: -9; control2Y: 272.401; x: -5.8; y: 272.801 }
+ , PathCubic { control1X: -5.8; control1Y: 272.801; control2X: -4.6; control2Y: 279.201; x: -5.8; y: 286.001 }
+ , PathCubic { control1X: -5.8; control1Y: 286.001; control2X: -1.8; control2Y: 278.401; x: 2.2; y: 280.001 }
+ , PathCubic { control1X: 2.2; control1Y: 280.001; control2X: 8.6; control2Y: 278.001; x: 7.8; y: 289.601 }
+ , PathCubic { control1X: 7.8; control1Y: 289.601; control2X: 7.8; control2Y: 300.001; x: 7; y: 302.801 }
+ , PathCubic { control1X: 7; control1Y: 302.801; control2X: 12.6; control2Y: 276.401; x: 15; y: 276.001 }
+ , PathCubic { control1X: 15; control1Y: 276.001; control2X: 23; control2Y: 274.801; x: 27.8; y: 283.601 }
+ , PathCubic { control1X: 27.8; control1Y: 283.601; control2X: 23.8; control2Y: 276.001; x: 28.6; y: 278.001 }
+ , PathCubic { control1X: 28.6; control1Y: 278.001; control2X: 39.4; control2Y: 279.601; x: 42.6; y: 286.401 }
+ , PathCubic { control1X: 42.6; control1Y: 286.401; control2X: 35.8; control2Y: 274.401; x: 41.4; y: 277.601 }
+ , PathCubic { control1X: 41.4; control1Y: 277.601; control2X: 48.2; control2Y: 277.601; x: 49.4; y: 284.001 }
+ , PathCubic { control1X: 49.4; control1Y: 284.001; control2X: 57.8; control2Y: 305.201; x: 59.8; y: 306.801 }
+ , PathCubic { control1X: 59.8; control1Y: 306.801; control2X: 52.2; control2Y: 285.201; x: 53.8; y: 285.201 }
+ , PathCubic { control1X: 53.8; control1Y: 285.201; control2X: 51.8; control2Y: 273.201; x: 57; y: 288.001 }
+ , PathCubic { control1X: 57; control1Y: 288.001; control2X: 53.8; control2Y: 274.001; x: 59.4; y: 274.801 }
+ , PathCubic { control1X: 65; control1Y: 275.601; control2X: 69.4; control2Y: 285.601; x: 77.8; y: 283.201 }
+ , PathCubic { control1X: 77.8; control1Y: 283.201; control2X: 87.401; control2Y: 288.801; x: 89.401; y: 219.601 }
+ , PathLine { x: -34.6; y: 266.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -29.8; y: 173.601 }
+ , PathCubic { control1X: -29.8; control1Y: 173.601; control2X: -15; control2Y: 167.601; x: 25; y: 173.601 }
+ , PathCubic { control1X: 25; control1Y: 173.601; control2X: 32.2; control2Y: 174.001; x: 39; y: 165.201 }
+ , PathCubic { control1X: 45.8; control1Y: 156.401; control2X: 72.6; control2Y: 149.201; x: 79; y: 151.201 }
+ , PathLine { x: 88.601; y: 157.601 }
+ , PathLine { x: 89.401; y: 158.801 }
+ , PathCubic { control1X: 89.401; control1Y: 158.801; control2X: 101.801; control2Y: 169.201; x: 102.201; y: 176.801 }
+ , PathCubic { control1X: 102.601; control1Y: 184.401; control2X: 87.801; control2Y: 232.401; x: 78.2; y: 248.401 }
+ , PathCubic { control1X: 68.6; control1Y: 264.401; control2X: 59; control2Y: 276.801; x: 39.8; y: 274.401 }
+ , PathCubic { control1X: 39.8; control1Y: 274.401; control2X: 19; control2Y: 270.401; x: -6.6; y: 274.401 }
+ , PathCubic { control1X: -6.6; control1Y: 274.401; control2X: -35.8; control2Y: 272.801; x: -38.6; y: 264.801 }
+ , PathCubic { control1X: -41.4; control1Y: 256.801; control2X: -27.4; control2Y: 241.601; x: -27.4; y: 241.601 }
+ , PathCubic { control1X: -27.4; control1Y: 241.601; control2X: -23; control2Y: 233.201; x: -24.2; y: 218.801 }
+ , PathCubic { control1X: -25.4; control1Y: 204.401; control2X: -25; control2Y: 176.401; x: -29.8; y: 173.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#e5668c"
+ strokeWidth: -1
+ delegate: [PathMove { x: -7.8; y: 175.601 }
+ , PathCubic { control1X: 0.6; control1Y: 194.001; control2X: -29; control2Y: 259.201; x: -29; y: 259.201 }
+ , PathCubic { control1X: -31; control1Y: 260.801; control2X: -16.34; control2Y: 266.846; x: -6.2; y: 264.401 }
+ , PathCubic { control1X: 4.746; control1Y: 261.763; control2X: 45; control2Y: 266.001; x: 45; y: 266.001 }
+ , PathCubic { control1X: 68.6; control1Y: 250.401; control2X: 81.4; control2Y: 206.001; x: 81.4; y: 206.001 }
+ , PathCubic { control1X: 81.4; control1Y: 206.001; control2X: 91.801; control2Y: 182.001; x: 74.2; y: 178.801 }
+ , PathCubic { control1X: 56.6; control1Y: 175.601; control2X: -7.8; control2Y: 175.601; x: -7.8; y: 175.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#b23259"
+ strokeWidth: -1
+ delegate: [PathMove { x: -9.831; y: 206.497 }
+ , PathCubic { control1X: -6.505; control1Y: 193.707; control2X: -4.921; control2Y: 181.906; x: -7.8; y: 175.601 }
+ , PathCubic { control1X: -7.8; control1Y: 175.601; control2X: 54.6; control2Y: 182.001; x: 65.8; y: 161.201 }
+ , PathCubic { control1X: 70.041; control1Y: 153.326; control2X: 84.801; control2Y: 184.001; x: 84.4; y: 193.601 }
+ , PathCubic { control1X: 84.4; control1Y: 193.601; control2X: 21.4; control2Y: 208.001; x: 6.6; y: 196.801 }
+ , PathLine { x: -9.831; y: 206.497 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#a5264c"
+ strokeWidth: -1
+ delegate: [PathMove { x: -5.4; y: 222.801 }
+ , PathCubic { control1X: -5.4; control1Y: 222.801; control2X: -3.4; control2Y: 230.001; x: -5.8; y: 234.001 }
+ , PathCubic { control1X: -5.8; control1Y: 234.001; control2X: -7.4; control2Y: 234.801; x: -8.6; y: 235.201 }
+ , PathCubic { control1X: -8.6; control1Y: 235.201; control2X: -7.4; control2Y: 238.801; x: -1.4; y: 240.401 }
+ , PathCubic { control1X: -1.4; control1Y: 240.401; control2X: 0.6; control2Y: 244.801; x: 3; y: 245.201 }
+ , PathCubic { control1X: 5.4; control1Y: 245.601; control2X: 10.2; control2Y: 251.201; x: 14.2; y: 250.001 }
+ , PathCubic { control1X: 18.2; control1Y: 248.801; control2X: 29.4; control2Y: 244.801; x: 29.4; y: 244.801 }
+ , PathCubic { control1X: 29.4; control1Y: 244.801; control2X: 35; control2Y: 241.601; x: 43.8; y: 245.201 }
+ , PathCubic { control1X: 43.8; control1Y: 245.201; control2X: 46.175; control2Y: 244.399; x: 46.6; y: 240.401 }
+ , PathCubic { control1X: 47.1; control1Y: 235.701; control2X: 50.2; control2Y: 232.001; x: 52.2; y: 230.001 }
+ , PathCubic { control1X: 54.2; control1Y: 228.001; control2X: 63.8; control2Y: 215.201; x: 62.6; y: 214.801 }
+ , PathCubic { control1X: 61.4; control1Y: 214.401; control2X: -5.4; control2Y: 222.801; x: -5.4; y: 222.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ff727f"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ delegate: [PathMove { x: -9.8; y: 174.401 }
+ , PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -12.6; control2Y: 196.801; x: -9.4; y: 205.201 }
+ , PathCubic { control1X: -6.2; control1Y: 213.601; control2X: -7; control2Y: 215.601; x: -7.8; y: 219.601 }
+ , PathCubic { control1X: -8.6; control1Y: 223.601; control2X: -4.2; control2Y: 233.601; x: 1.4; y: 239.601 }
+ , PathLine { x: 13.4; y: 241.201 }
+ , PathCubic { control1X: 13.4; control1Y: 241.201; control2X: 28.6; control2Y: 237.601; x: 37.8; y: 240.401 }
+ , PathCubic { control1X: 37.8; control1Y: 240.401; control2X: 46.794; control2Y: 241.744; x: 50.2; y: 226.801 }
+ , PathCubic { control1X: 50.2; control1Y: 226.801; control2X: 55; control2Y: 220.401; x: 62.2; y: 217.601 }
+ , PathCubic { control1X: 69.4; control1Y: 214.801; control2X: 76.6; control2Y: 173.201; x: 72.6; y: 165.201 }
+ , PathCubic { control1X: 68.6; control1Y: 157.201; control2X: 54.2; control2Y: 152.801; x: 38.2; y: 168.401 }
+ , PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -8.2; y: 249.201 }
+ , PathCubic { control1X: -8.2; control1Y: 249.201; control2X: -9; control2Y: 247.201; x: -13.4; y: 246.801 }
+ , PathCubic { control1X: -13.4; control1Y: 246.801; control2X: -35.8; control2Y: 243.201; x: -44.2; y: 230.801 }
+ , PathCubic { control1X: -44.2; control1Y: 230.801; control2X: -51; control2Y: 225.201; x: -46.6; y: 236.801 }
+ , PathCubic { control1X: -46.6; control1Y: 236.801; control2X: -36.2; control2Y: 257.201; x: -29.4; y: 260.001 }
+ , PathCubic { control1X: -29.4; control1Y: 260.001; control2X: -13; control2Y: 264.001; x: -8.2; y: 249.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc3f4c"
+ strokeWidth: -1
+ delegate: [PathMove { x: 71.742; y: 185.229 }
+ , PathCubic { control1X: 72.401; control1Y: 177.323; control2X: 74.354; control2Y: 168.709; x: 72.6; y: 165.201 }
+ , PathCubic { control1X: 66.154; control1Y: 152.307; control2X: 49.181; control2Y: 157.695; x: 38.2; y: 168.401 }
+ , PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 }
+ , PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -11.545; control2Y: 188.364; x: -10.705; y: 198.376 }
+ , PathCubic { control1X: -10.705; control1Y: 198.376; control2X: 26.6; control2Y: 186.801; x: 27.4; y: 192.401 }
+ , PathCubic { control1X: 27.4; control1Y: 192.401; control2X: 29; control2Y: 189.201; x: 38.2; y: 189.201 }
+ , PathCubic { control1X: 47.4; control1Y: 189.201; control2X: 70.142; control2Y: 188.029; x: 71.742; y: 185.229 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#a51926"
+ strokeWidth: 2
+ delegate: [PathMove { x: 28.6; y: 175.201 }
+ , PathCubic { control1X: 28.6; control1Y: 175.201; control2X: 33.4; control2Y: 180.001; x: 29.8; y: 189.601 }
+ , PathCubic { control1X: 29.8; control1Y: 189.601; control2X: 15.4; control2Y: 205.601; x: 17.4; y: 219.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -19.4; y: 260.001 }
+ , PathCubic { control1X: -19.4; control1Y: 260.001; control2X: -23.8; control2Y: 247.201; x: -15; y: 254.001 }
+ , PathCubic { control1X: -15; control1Y: 254.001; control2X: -10.2; control2Y: 256.001; x: -11.4; y: 257.601 }
+ , PathCubic { control1X: -12.6; control1Y: 259.201; control2X: -18.2; control2Y: 263.201; x: -19.4; y: 260.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -14.36; y: 261.201 }
+ , PathCubic { control1X: -14.36; control1Y: 261.201; control2X: -17.88; control2Y: 250.961; x: -10.84; y: 256.401 }
+ , PathCubic { control1X: -10.84; control1Y: 256.401; control2X: -6.419; control2Y: 258.849; x: -7.96; y: 259.281 }
+ , PathCubic { control1X: -12.52; control1Y: 260.561; control2X: -7.96; control2Y: 263.121; x: -14.36; y: 261.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -9.56; y: 261.201 }
+ , PathCubic { control1X: -9.56; control1Y: 261.201; control2X: -13.08; control2Y: 250.961; x: -6.04; y: 256.401 }
+ , PathCubic { control1X: -6.04; control1Y: 256.401; control2X: -1.665; control2Y: 258.711; x: -3.16; y: 259.281 }
+ , PathCubic { control1X: -6.52; control1Y: 260.561; control2X: -3.16; control2Y: 263.121; x: -9.56; y: 261.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -2.96; y: 261.401 }
+ , PathCubic { control1X: -2.96; control1Y: 261.401; control2X: -6.48; control2Y: 251.161; x: 0.56; y: 256.601 }
+ , PathCubic { control1X: 0.56; control1Y: 256.601; control2X: 4.943; control2Y: 258.933; x: 3.441; y: 259.481 }
+ , PathCubic { control1X: 0.48; control1Y: 260.561; control2X: 3.441; control2Y: 263.321; x: -2.96; y: 261.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: 3.52; y: 261.321 }
+ , PathCubic { control1X: 3.52; control1Y: 261.321; control2X: 0; control2Y: 251.081; x: 7.041; y: 256.521 }
+ , PathCubic { control1X: 7.041; control1Y: 256.521; control2X: 10.881; control2Y: 258.121; x: 9.921; y: 259.401 }
+ , PathCubic { control1X: 8.961; control1Y: 260.681; control2X: 9.921; control2Y: 263.241; x: 3.52; y: 261.321 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: 10.2; y: 262.001 }
+ , PathCubic { control1X: 10.2; control1Y: 262.001; control2X: 5.4; control2Y: 249.601; x: 14.6; y: 256.001 }
+ , PathCubic { control1X: 14.6; control1Y: 256.001; control2X: 19.4; control2Y: 258.001; x: 18.2; y: 259.601 }
+ , PathCubic { control1X: 17; control1Y: 261.201; control2X: 18.2; control2Y: 264.401; x: 10.2; y: 262.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ delegate: [PathMove { x: -18.2; y: 244.801 }
+ , PathCubic { control1X: -18.2; control1Y: 244.801; control2X: -5; control2Y: 242.001; x: 1; y: 245.201 }
+ , PathCubic { control1X: 1; control1Y: 245.201; control2X: 7; control2Y: 246.401; x: 8.2; y: 246.001 }
+ , PathCubic { control1X: 9.4; control1Y: 245.601; control2X: 12.6; control2Y: 245.201; x: 12.6; y: 245.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ delegate: [PathMove { x: 15.8; y: 253.601 }
+ , PathCubic { control1X: 15.8; control1Y: 253.601; control2X: 27.8; control2Y: 240.001; x: 39.8; y: 244.401 }
+ , PathCubic { control1X: 46.816; control1Y: 246.974; control2X: 45.8; control2Y: 243.601; x: 46.6; y: 240.801 }
+ , PathCubic { control1X: 47.4; control1Y: 238.001; control2X: 47.6; control2Y: 233.801; x: 52.6; y: 230.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: 33; y: 237.601 }
+ , PathCubic { control1X: 33; control1Y: 237.601; control2X: 29; control2Y: 226.801; x: 26.2; y: 239.601 }
+ , PathCubic { control1X: 23.4; control1Y: 252.401; control2X: 20.2; control2Y: 256.001; x: 18.6; y: 258.801 }
+ , PathCubic { control1X: 18.6; control1Y: 258.801; control2X: 18.6; control2Y: 264.001; x: 27; y: 263.601 }
+ , PathCubic { control1X: 27; control1Y: 263.601; control2X: 37.8; control2Y: 263.201; x: 38.2; y: 260.401 }
+ , PathCubic { control1X: 38.6; control1Y: 257.601; control2X: 37; control2Y: 246.001; x: 33; y: 237.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ delegate: [PathMove { x: 47; y: 244.801 }
+ , PathCubic { control1X: 47; control1Y: 244.801; control2X: 50.6; control2Y: 242.401; x: 53; y: 243.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#a5264c"
+ strokeWidth: 2
+ delegate: [PathMove { x: 53.5; y: 228.401 }
+ , PathCubic { control1X: 53.5; control1Y: 228.401; control2X: 56.4; control2Y: 223.501; x: 61.2; y: 222.701 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#b2b2b2"
+ strokeWidth: -1
+ delegate: [PathMove { x: -25.8; y: 265.201 }
+ , PathCubic { control1X: -25.8; control1Y: 265.201; control2X: -7.8; control2Y: 268.401; x: -3.4; y: 266.801 }
+ , PathCubic { control1X: -3.4; control1Y: 266.801; control2X: 5.4; control2Y: 266.801; x: -3; y: 268.801 }
+ , PathCubic { control1X: -3; control1Y: 268.801; control2X: -15.8; control2Y: 268.801; x: -23.8; y: 267.601 }
+ , PathCubic { control1X: -23.8; control1Y: 267.601; control2X: -35.4; control2Y: 262.001; x: -25.8; y: 265.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -11.8; y: 172.001 }
+ , PathCubic { control1X: -11.8; control1Y: 172.001; control2X: 5.8; control2Y: 172.001; x: 7.8; y: 172.801 }
+ , PathCubic { control1X: 7.8; control1Y: 172.801; control2X: 15; control2Y: 203.601; x: 11.4; y: 211.201 }
+ , PathCubic { control1X: 11.4; control1Y: 211.201; control2X: 10.2; control2Y: 214.001; x: 7.4; y: 208.401 }
+ , PathCubic { control1X: 7.4; control1Y: 208.401; control2X: -11; control2Y: 175.601; x: -14.2; y: 173.601 }
+ , PathCubic { control1X: -17.4; control1Y: 171.601; control2X: -13; control2Y: 172.001; x: -11.8; y: 172.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -88.9; y: 169.301 }
+ , PathCubic { control1X: -88.9; control1Y: 169.301; control2X: -80; control2Y: 171.001; x: -67.4; y: 173.601 }
+ , PathCubic { control1X: -67.4; control1Y: 173.601; control2X: -62.6; control2Y: 196.001; x: -59.4; y: 200.801 }
+ , PathCubic { control1X: -56.2; control1Y: 205.601; control2X: -59.8; control2Y: 205.601; x: -63.4; y: 202.801 }
+ , PathCubic { control1X: -67; control1Y: 200.001; control2X: -81.8; control2Y: 186.001; x: -83.8; y: 181.601 }
+ , PathCubic { control1X: -85.8; control1Y: 177.201; control2X: -88.9; control2Y: 169.301; x: -88.9; y: 169.301 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -67.039; y: 173.818 }
+ , PathCubic { control1X: -67.039; control1Y: 173.818; control2X: -61.239; control2Y: 175.366; x: -60.23; y: 177.581 }
+ , PathCubic { control1X: -59.222; control1Y: 179.795; control2X: -61.432; control2Y: 183.092; x: -61.432; y: 183.092 }
+ , PathCubic { control1X: -61.432; control1Y: 183.092; control2X: -62.432; control2Y: 186.397; x: -63.634; y: 184.235 }
+ , PathCubic { control1X: -64.836; control1Y: 182.072; control2X: -67.708; control2Y: 174.412; x: -67.039; y: 173.818 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -67; y: 173.601 }
+ , PathCubic { control1X: -67; control1Y: 173.601; control2X: -63.4; control2Y: 178.801; x: -59.8; y: 178.801 }
+ , PathCubic { control1X: -56.2; control1Y: 178.801; control2X: -55.818; control2Y: 178.388; x: -53; y: 179.001 }
+ , PathCubic { control1X: -48.4; control1Y: 180.001; control2X: -48.8; control2Y: 178.001; x: -42.2; y: 179.201 }
+ , PathCubic { control1X: -39.56; control1Y: 179.681; control2X: -37; control2Y: 178.801; x: -34.2; y: 180.001 }
+ , PathCubic { control1X: -31.4; control1Y: 181.201; control2X: -28.2; control2Y: 180.401; x: -27; y: 178.401 }
+ , PathCubic { control1X: -25.8; control1Y: 176.401; control2X: -21; control2Y: 172.201; x: -21; y: 172.201 }
+ , PathCubic { control1X: -21; control1Y: 172.201; control2X: -33.8; control2Y: 174.001; x: -36.6; y: 174.801 }
+ , PathCubic { control1X: -36.6; control1Y: 174.801; control2X: -59; control2Y: 176.001; x: -67; y: 173.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -22.4; y: 173.801 }
+ , PathCubic { control1X: -22.4; control1Y: 173.801; control2X: -28.85; control2Y: 177.301; x: -29.25; y: 179.701 }
+ , PathCubic { control1X: -29.65; control1Y: 182.101; control2X: -24; control2Y: 185.801; x: -24; y: 185.801 }
+ , PathCubic { control1X: -24; control1Y: 185.801; control2X: -21.25; control2Y: 190.401; x: -20.65; y: 188.001 }
+ , PathCubic { control1X: -20.05; control1Y: 185.601; control2X: -21.6; control2Y: 174.201; x: -22.4; y: 173.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -59.885; y: 179.265 }
+ , PathCubic { control1X: -59.885; control1Y: 179.265; control2X: -52.878; control2Y: 190.453; x: -52.661; y: 179.242 }
+ , PathCubic { control1X: -52.661; control1Y: 179.242; control2X: -52.104; control2Y: 177.984; x: -53.864; y: 177.962 }
+ , PathCubic { control1X: -59.939; control1Y: 177.886; control2X: -58.418; control2Y: 173.784; x: -59.885; y: 179.265 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -52.707; y: 179.514 }
+ , PathCubic { control1X: -52.707; control1Y: 179.514; control2X: -44.786; control2Y: 190.701; x: -45.422; y: 179.421 }
+ , PathCubic { control1X: -45.422; control1Y: 179.421; control2X: -45.415; control2Y: 179.089; x: -47.168; y: 178.936 }
+ , PathCubic { control1X: -51.915; control1Y: 178.522; control2X: -51.57; control2Y: 174.004; x: -52.707; y: 179.514 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -45.494; y: 179.522 }
+ , PathCubic { control1X: -45.494; control1Y: 179.522; control2X: -37.534; control2Y: 190.15; x: -38.203; y: 180.484 }
+ , PathCubic { control1X: -38.203; control1Y: 180.484; control2X: -38.084; control2Y: 179.251; x: -39.738; y: 178.95 }
+ , PathCubic { control1X: -43.63; control1Y: 178.244; control2X: -43.841; control2Y: 174.995; x: -45.494; y: 179.522 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffcc"
+ strokeColor: "#000000"
+ strokeWidth: 0.5
+ delegate: [PathMove { x: -38.618; y: 179.602 }
+ , PathCubic { control1X: -38.618; control1Y: 179.602; control2X: -30.718; control2Y: 191.163; x: -30.37; y: 181.382 }
+ , PathCubic { control1X: -30.37; control1Y: 181.382; control2X: -28.726; control2Y: 180.004; x: -30.472; y: 179.782 }
+ , PathCubic { control1X: -36.29; control1Y: 179.042; control2X: -35.492; control2Y: 174.588; x: -38.618; y: 179.602 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#e5e5b2"
+ strokeWidth: -1
+ delegate: [PathMove { x: -74.792; y: 183.132 }
+ , PathLine { x: -82.45; y: 181.601 }
+ , PathCubic { control1X: -85.05; control1Y: 176.601; control2X: -87.15; control2Y: 170.451; x: -87.15; y: 170.451 }
+ , PathCubic { control1X: -87.15; control1Y: 170.451; control2X: -80.8; control2Y: 171.451; x: -68.3; y: 174.251 }
+ , PathCubic { control1X: -68.3; control1Y: 174.251; control2X: -67.424; control2Y: 177.569; x: -65.952; y: 183.364 }
+ , PathLine { x: -74.792; y: 183.132 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#e5e5b2"
+ strokeWidth: -1
+ delegate: [PathMove { x: -9.724; y: 178.47 }
+ , PathCubic { control1X: -11.39; control1Y: 175.964; control2X: -12.707; control2Y: 174.206; x: -13.357; y: 173.8 }
+ , PathCubic { control1X: -16.37; control1Y: 171.917; control2X: -12.227; control2Y: 172.294; x: -11.098; y: 172.294 }
+ , PathCubic { control1X: -11.098; control1Y: 172.294; control2X: 5.473; control2Y: 172.294; x: 7.356; y: 173.047 }
+ , PathCubic { control1X: 7.356; control1Y: 173.047; control2X: 7.88; control2Y: 175.289; x: 8.564; y: 178.68 }
+ , PathCubic { control1X: 8.564; control1Y: 178.68; control2X: -1.524; control2Y: 176.67; x: -9.724; y: 178.47 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: 43.88; y: 40.321 }
+ , PathCubic { control1X: 71.601; control1Y: 44.281; control2X: 97.121; control2Y: 8.641; x: 98.881; y: -1.04 }
+ , PathCubic { control1X: 100.641; control1Y: -10.72; control2X: 90.521; control2Y: -22.6; x: 90.521; y: -22.6 }
+ , PathCubic { control1X: 91.841; control1Y: -25.68; control2X: 87.001; control2Y: -39.76; x: 81.721; y: -49 }
+ , PathCubic { control1X: 76.441; control1Y: -58.24; control2X: 60.54; control2Y: -57.266; x: 43; y: -58.24 }
+ , PathCubic { control1X: 27.16; control1Y: -59.12; control2X: 8.68; control2Y: -35.8; x: 7.36; y: -34.04 }
+ , PathCubic { control1X: 6.04; control1Y: -32.28; control2X: 12.2; control2Y: 6.001; x: 13.52; y: 11.721 }
+ , PathCubic { control1X: 14.84; control1Y: 17.441; control2X: 12.2; control2Y: 43.841; x: 12.2; y: 43.841 }
+ , PathCubic { control1X: 46.44; control1Y: 34.741; control2X: 16.16; control2Y: 36.361; x: 43.88; y: 40.321 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ea8e51"
+ strokeWidth: -1
+ delegate: [PathMove { x: 8.088; y: -33.392 }
+ , PathCubic { control1X: 6.792; control1Y: -31.664; control2X: 12.84; control2Y: 5.921; x: 14.136; y: 11.537 }
+ , PathCubic { control1X: 15.432; control1Y: 17.153; control2X: 12.84; control2Y: 43.073; x: 12.84; y: 43.073 }
+ , PathCubic { control1X: 45.512; control1Y: 34.193; control2X: 16.728; control2Y: 35.729; x: 43.944; y: 39.617 }
+ , PathCubic { control1X: 71.161; control1Y: 43.505; control2X: 96.217; control2Y: 8.513; x: 97.945; y: -0.992 }
+ , PathCubic { control1X: 99.673; control1Y: -10.496; control2X: 89.737; control2Y: -22.16; x: 89.737; y: -22.16 }
+ , PathCubic { control1X: 91.033; control1Y: -25.184; control2X: 86.281; control2Y: -39.008; x: 81.097; y: -48.08 }
+ , PathCubic { control1X: 75.913; control1Y: -57.152; control2X: 60.302; control2Y: -56.195; x: 43.08; y: -57.152 }
+ , PathCubic { control1X: 27.528; control1Y: -58.016; control2X: 9.384; control2Y: -35.12; x: 8.088; y: -33.392 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#efaa7c"
+ strokeWidth: -1
+ delegate: [PathMove { x: 8.816; y: -32.744 }
+ , PathCubic { control1X: 7.544; control1Y: -31.048; control2X: 13.48; control2Y: 5.841; x: 14.752; y: 11.353 }
+ , PathCubic { control1X: 16.024; control1Y: 16.865; control2X: 13.48; control2Y: 42.305; x: 13.48; y: 42.305 }
+ , PathCubic { control1X: 44.884; control1Y: 33.145; control2X: 17.296; control2Y: 35.097; x: 44.008; y: 38.913 }
+ , PathCubic { control1X: 70.721; control1Y: 42.729; control2X: 95.313; control2Y: 8.385; x: 97.009; y: -0.944 }
+ , PathCubic { control1X: 98.705; control1Y: -10.272; control2X: 88.953; control2Y: -21.72; x: 88.953; y: -21.72 }
+ , PathCubic { control1X: 90.225; control1Y: -24.688; control2X: 85.561; control2Y: -38.256; x: 80.473; y: -47.16 }
+ , PathCubic { control1X: 75.385; control1Y: -56.064; control2X: 60.063; control2Y: -55.125; x: 43.16; y: -56.064 }
+ , PathCubic { control1X: 27.896; control1Y: -56.912; control2X: 10.088; control2Y: -34.44; x: 8.816; y: -32.744 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#f4c6a8"
+ strokeWidth: -1
+ delegate: [PathMove { x: 9.544; y: -32.096 }
+ , PathCubic { control1X: 8.296; control1Y: -30.432; control2X: 14.12; control2Y: 5.761; x: 15.368; y: 11.169 }
+ , PathCubic { control1X: 16.616; control1Y: 16.577; control2X: 14.12; control2Y: 41.537; x: 14.12; y: 41.537 }
+ , PathCubic { control1X: 43.556; control1Y: 32.497; control2X: 17.864; control2Y: 34.465; x: 44.072; y: 38.209 }
+ , PathCubic { control1X: 70.281; control1Y: 41.953; control2X: 94.409; control2Y: 8.257; x: 96.073; y: -0.895 }
+ , PathCubic { control1X: 97.737; control1Y: -10.048; control2X: 88.169; control2Y: -21.28; x: 88.169; y: -21.28 }
+ , PathCubic { control1X: 89.417; control1Y: -24.192; control2X: 84.841; control2Y: -37.504; x: 79.849; y: -46.24 }
+ , PathCubic { control1X: 74.857; control1Y: -54.976; control2X: 59.824; control2Y: -54.055; x: 43.24; y: -54.976 }
+ , PathCubic { control1X: 28.264; control1Y: -55.808; control2X: 10.792; control2Y: -33.76; x: 9.544; y: -32.096 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#f9e2d3"
+ strokeWidth: -1
+ delegate: [PathMove { x: 10.272; y: -31.448 }
+ , PathCubic { control1X: 9.048; control1Y: -29.816; control2X: 14.76; control2Y: 5.681; x: 15.984; y: 10.985 }
+ , PathCubic { control1X: 17.208; control1Y: 16.289; control2X: 14.76; control2Y: 40.769; x: 14.76; y: 40.769 }
+ , PathCubic { control1X: 42.628; control1Y: 31.849; control2X: 18.432; control2Y: 33.833; x: 44.136; y: 37.505 }
+ , PathCubic { control1X: 69.841; control1Y: 41.177; control2X: 93.505; control2Y: 8.129; x: 95.137; y: -0.848 }
+ , PathCubic { control1X: 96.769; control1Y: -9.824; control2X: 87.385; control2Y: -20.84; x: 87.385; y: -20.84 }
+ , PathCubic { control1X: 88.609; control1Y: -23.696; control2X: 84.121; control2Y: -36.752; x: 79.225; y: -45.32 }
+ , PathCubic { control1X: 74.329; control1Y: -53.888; control2X: 59.585; control2Y: -52.985; x: 43.32; y: -53.888 }
+ , PathCubic { control1X: 28.632; control1Y: -54.704; control2X: 11.496; control2Y: -33.08; x: 10.272; y: -31.448 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: 44.2; y: 36.8 }
+ , PathCubic { control1X: 69.4; control1Y: 40.4; control2X: 92.601; control2Y: 8; x: 94.201; y: -0.8 }
+ , PathCubic { control1X: 95.801; control1Y: -9.6; control2X: 86.601; control2Y: -20.4; x: 86.601; y: -20.4 }
+ , PathCubic { control1X: 87.801; control1Y: -23.2; control2X: 83.4; control2Y: -36; x: 78.6; y: -44.4 }
+ , PathCubic { control1X: 73.8; control1Y: -52.8; control2X: 59.346; control2Y: -51.914; x: 43.4; y: -52.8 }
+ , PathCubic { control1X: 29; control1Y: -53.6; control2X: 12.2; control2Y: -32.4; x: 11; y: -30.8 }
+ , PathCubic { control1X: 9.8; control1Y: -29.2; control2X: 15.4; control2Y: 5.6; x: 16.6; y: 10.8 }
+ , PathCubic { control1X: 17.8; control1Y: 16; control2X: 15.4; control2Y: 40; x: 15.4; y: 40 }
+ , PathCubic { control1X: 40.9; control1Y: 31.4; control2X: 19; control2Y: 33.2; x: 44.2; y: 36.8 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 90.601; y: 2.8 }
+ , PathCubic { control1X: 90.601; control1Y: 2.8; control2X: 62.8; control2Y: 10.4; x: 51.2; y: 8.8 }
+ , PathCubic { control1X: 51.2; control1Y: 8.8; control2X: 35.4; control2Y: 2.2; x: 26.6; y: 24 }
+ , PathCubic { control1X: 26.6; control1Y: 24; control2X: 23; control2Y: 31.2; x: 21; y: 33.2 }
+ , PathCubic { control1X: 19; control1Y: 35.2; control2X: 90.601; control2Y: 2.8; x: 90.601; y: 2.8 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 94.401; y: 0.6 }
+ , PathCubic { control1X: 94.401; control1Y: 0.6; control2X: 65.4; control2Y: 12.8; x: 55.4; y: 12.4 }
+ , PathCubic { control1X: 55.4; control1Y: 12.4; control2X: 39; control2Y: 7.8; x: 30.6; y: 22.4 }
+ , PathCubic { control1X: 30.6; control1Y: 22.4; control2X: 22.2; control2Y: 31.6; x: 19; y: 33.2 }
+ , PathCubic { control1X: 19; control1Y: 33.2; control2X: 18.6; control2Y: 34.8; x: 25; y: 30.8 }
+ , PathLine { x: 35.4; y: 36 }
+ , PathCubic { control1X: 35.4; control1Y: 36; control2X: 50.2; control2Y: 45.6; x: 59.8; y: 29.6 }
+ , PathCubic { control1X: 59.8; control1Y: 29.6; control2X: 63.8; control2Y: 18.4; x: 63.8; y: 16.4 }
+ , PathCubic { control1X: 63.8; control1Y: 14.4; control2X: 85; control2Y: 8.8; x: 86.601; y: 8.4 }
+ , PathCubic { control1X: 88.201; control1Y: 8; control2X: 94.801; control2Y: 3.8; x: 94.401; y: 0.6 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ delegate: [PathMove { x: 47; y: 36.514 }
+ , PathCubic { control1X: 40.128; control1Y: 36.514; control2X: 31.755; control2Y: 32.649; x: 31.755; y: 26.4 }
+ , PathCubic { control1X: 31.755; control1Y: 20.152; control2X: 40.128; control2Y: 13.887; x: 47; y: 13.887 }
+ , PathCubic { control1X: 53.874; control1Y: 13.887; control2X: 59.446; control2Y: 18.952; x: 59.446; y: 25.2 }
+ , PathCubic { control1X: 59.446; control1Y: 31.449; control2X: 53.874; control2Y: 36.514; x: 47; y: 36.514 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#659900"
+ strokeWidth: -1
+ delegate: [PathMove { x: 43.377; y: 19.83 }
+ , PathCubic { control1X: 38.531; control1Y: 20.552; control2X: 33.442; control2Y: 22.055; x: 33.514; y: 21.839 }
+ , PathCubic { control1X: 35.054; control1Y: 17.22; control2X: 41.415; control2Y: 13.887; x: 47; y: 13.887 }
+ , PathCubic { control1X: 51.296; control1Y: 13.887; control2X: 55.084; control2Y: 15.865; x: 57.32; y: 18.875 }
+ , PathCubic { control1X: 57.32; control1Y: 18.875; control2X: 52.004; control2Y: 18.545; x: 43.377; y: 19.83 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: 55.4; y: 19.6 }
+ , PathCubic { control1X: 55.4; control1Y: 19.6; control2X: 51; control2Y: 16.4; x: 51; y: 18.6 }
+ , PathCubic { control1X: 51; control1Y: 18.6; control2X: 54.6; control2Y: 23; x: 55.4; y: 19.6 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 45.4; y: 27.726 }
+ , PathCubic { control1X: 42.901; control1Y: 27.726; control2X: 40.875; control2Y: 25.7; x: 40.875; y: 23.2 }
+ , PathCubic { control1X: 40.875; control1Y: 20.701; control2X: 42.901; control2Y: 18.675; x: 45.4; y: 18.675 }
+ , PathCubic { control1X: 47.9; control1Y: 18.675; control2X: 49.926; control2Y: 20.701; x: 49.926; y: 23.2 }
+ , PathCubic { control1X: 49.926; control1Y: 25.7; control2X: 47.9; control2Y: 27.726; x: 45.4; y: 27.726 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: -58.6; y: 14.4 }
+ , PathCubic { control1X: -58.6; control1Y: 14.4; control2X: -61.8; control2Y: -6.8; x: -59.4; y: -11.2 }
+ , PathCubic { control1X: -59.4; control1Y: -11.2; control2X: -48.6; control2Y: -21.2; x: -49; y: -24.8 }
+ , PathCubic { control1X: -49; control1Y: -24.8; control2X: -49.4; control2Y: -42.8; x: -50.6; y: -43.6 }
+ , PathCubic { control1X: -51.8; control1Y: -44.4; control2X: -59.4; control2Y: -50.4; x: -65.4; y: -44 }
+ , PathCubic { control1X: -65.4; control1Y: -44; control2X: -75.8; control2Y: -26; x: -75; y: -19.6 }
+ , PathLine { x: -75; y: -17.6 }
+ , PathCubic { control1X: -75; control1Y: -17.6; control2X: -82.6; control2Y: -18; x: -84.2; y: -16 }
+ , PathCubic { control1X: -84.2; control1Y: -16; control2X: -85.4; control2Y: -10.8; x: -86.6; y: -10.4 }
+ , PathCubic { control1X: -86.6; control1Y: -10.4; control2X: -89.4; control2Y: -8; x: -87.4; y: -5.2 }
+ , PathCubic { control1X: -87.4; control1Y: -5.2; control2X: -89.4; control2Y: -2.8; x: -89; y: 1.2 }
+ , PathLine { x: -81.4; y: 5.2 }
+ , PathCubic { control1X: -81.4; control1Y: 5.2; control2X: -79.4; control2Y: 19.6; x: -68.6; y: 24.8 }
+ , PathCubic { control1X: -63.764; control1Y: 27.129; control2X: -60.6; control2Y: 20.4; x: -58.6; y: 14.4 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: -59.6; y: 12.56 }
+ , PathCubic { control1X: -59.6; control1Y: 12.56; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 }
+ , PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 }
+ , PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 }
+ , PathCubic { control1X: -53.48; control1Y: -40.36; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 }
+ , PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 }
+ , PathLine { x: -74.36; y: -16.24 }
+ , PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 }
+ , PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 }
+ , PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 }
+ , PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 }
+ , PathLine { x: -80.12; y: 4.28 }
+ , PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 }
+ , PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.96; x: -59.6; y: 12.56 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#eb955c"
+ strokeWidth: -1
+ delegate: [PathMove { x: -51.05; y: -42.61 }
+ , PathCubic { control1X: -52.14; control1Y: -43.47; control2X: -59.63; control2Y: -49.24; x: -65.48; y: -43 }
+ , PathCubic { control1X: -65.48; control1Y: -43; control2X: -75.62; control2Y: -25.45; x: -74.84; y: -19.21 }
+ , PathLine { x: -74.84; y: -17.26 }
+ , PathCubic { control1X: -74.84; control1Y: -17.26; control2X: -82.25; control2Y: -17.65; x: -83.81; y: -15.7 }
+ , PathCubic { control1X: -83.81; control1Y: -15.7; control2X: -84.98; control2Y: -10.63; x: -86.15; y: -10.24 }
+ , PathCubic { control1X: -86.15; control1Y: -10.24; control2X: -88.88; control2Y: -7.9; x: -86.93; y: -5.17 }
+ , PathCubic { control1X: -86.93; control1Y: -5.17; control2X: -88.88; control2Y: -2.83; x: -88.49; y: 1.07 }
+ , PathLine { x: -81.08; y: 4.97 }
+ , PathCubic { control1X: -81.08; control1Y: 4.97; control2X: -79.13; control2Y: 19.01; x: -68.6; y: 24.08 }
+ , PathCubic { control1X: -63.886; control1Y: 26.35; control2X: -60.8; control2Y: 19.79; x: -58.85; y: 13.94 }
+ , PathCubic { control1X: -58.85; control1Y: 13.94; control2X: -61.97; control2Y: -6.73; x: -59.63; y: -11.02 }
+ , PathCubic { control1X: -59.63; control1Y: -11.02; control2X: -49.1; control2Y: -20.77; x: -49.49; y: -24.28 }
+ , PathCubic { control1X: -49.49; control1Y: -24.28; control2X: -49.88; control2Y: -41.83; x: -51.05; y: -42.61 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#f2b892"
+ strokeWidth: -1
+ delegate: [PathMove { x: -51.5; y: -41.62 }
+ , PathCubic { control1X: -52.48; control1Y: -42.54; control2X: -59.86; control2Y: -48.08; x: -65.56; y: -42 }
+ , PathCubic { control1X: -65.56; control1Y: -42; control2X: -75.44; control2Y: -24.9; x: -74.68; y: -18.82 }
+ , PathLine { x: -74.68; y: -16.92 }
+ , PathCubic { control1X: -74.68; control1Y: -16.92; control2X: -81.9; control2Y: -17.3; x: -83.42; y: -15.4 }
+ , PathCubic { control1X: -83.42; control1Y: -15.4; control2X: -84.56; control2Y: -10.46; x: -85.7; y: -10.08 }
+ , PathCubic { control1X: -85.7; control1Y: -10.08; control2X: -88.36; control2Y: -7.8; x: -86.46; y: -5.14 }
+ , PathCubic { control1X: -86.46; control1Y: -5.14; control2X: -88.36; control2Y: -2.86; x: -87.98; y: 0.94 }
+ , PathLine { x: -80.76; y: 4.74 }
+ , PathCubic { control1X: -80.76; control1Y: 4.74; control2X: -78.86; control2Y: 18.42; x: -68.6; y: 23.36 }
+ , PathCubic { control1X: -64.006; control1Y: 25.572; control2X: -61; control2Y: 19.18; x: -59.1; y: 13.48 }
+ , PathCubic { control1X: -59.1; control1Y: 13.48; control2X: -62.14; control2Y: -6.66; x: -59.86; y: -10.84 }
+ , PathCubic { control1X: -59.86; control1Y: -10.84; control2X: -49.6; control2Y: -20.34; x: -49.98; y: -23.76 }
+ , PathCubic { control1X: -49.98; control1Y: -23.76; control2X: -50.36; control2Y: -40.86; x: -51.5; y: -41.62 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#f8dcc8"
+ strokeWidth: -1
+ delegate: [PathMove { x: -51.95; y: -40.63 }
+ , PathCubic { control1X: -52.82; control1Y: -41.61; control2X: -60.09; control2Y: -46.92; x: -65.64; y: -41 }
+ , PathCubic { control1X: -65.64; control1Y: -41; control2X: -75.26; control2Y: -24.35; x: -74.52; y: -18.43 }
+ , PathLine { x: -74.52; y: -16.58 }
+ , PathCubic { control1X: -74.52; control1Y: -16.58; control2X: -81.55; control2Y: -16.95; x: -83.03; y: -15.1 }
+ , PathCubic { control1X: -83.03; control1Y: -15.1; control2X: -84.14; control2Y: -10.29; x: -85.25; y: -9.92 }
+ , PathCubic { control1X: -85.25; control1Y: -9.92; control2X: -87.84; control2Y: -7.7; x: -85.99; y: -5.11 }
+ , PathCubic { control1X: -85.99; control1Y: -5.11; control2X: -87.84; control2Y: -2.89; x: -87.47; y: 0.81 }
+ , PathLine { x: -80.44; y: 4.51 }
+ , PathCubic { control1X: -80.44; control1Y: 4.51; control2X: -78.59; control2Y: 17.83; x: -68.6; y: 22.64 }
+ , PathCubic { control1X: -64.127; control1Y: 24.794; control2X: -61.2; control2Y: 18.57; x: -59.35; y: 13.02 }
+ , PathCubic { control1X: -59.35; control1Y: 13.02; control2X: -62.31; control2Y: -6.59; x: -60.09; y: -10.66 }
+ , PathCubic { control1X: -60.09; control1Y: -10.66; control2X: -50.1; control2Y: -19.91; x: -50.47; y: -23.24 }
+ , PathCubic { control1X: -50.47; control1Y: -23.24; control2X: -50.84; control2Y: -39.89; x: -51.95; y: -40.63 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: -59.6; y: 12.46 }
+ , PathCubic { control1X: -59.6; control1Y: 12.46; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 }
+ , PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 }
+ , PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 }
+ , PathCubic { control1X: -53.16; control1Y: -40.68; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 }
+ , PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 }
+ , PathLine { x: -74.36; y: -16.24 }
+ , PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 }
+ , PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 }
+ , PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 }
+ , PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 }
+ , PathLine { x: -80.12; y: 4.28 }
+ , PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 }
+ , PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.86; x: -59.6; y: 12.46 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -62.7; y: 6.2 }
+ , PathCubic { control1X: -62.7; control1Y: 6.2; control2X: -84.3; control2Y: -4; x: -85.2; y: -4.8 }
+ , PathCubic { control1X: -85.2; control1Y: -4.8; control2X: -76.1; control2Y: 3.4; x: -75.3; y: 3.4 }
+ , PathCubic { control1X: -74.5; control1Y: 3.4; control2X: -62.7; control2Y: 6.2; x: -62.7; y: 6.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -79.8; y: 0 }
+ , PathCubic { control1X: -79.8; control1Y: 0; control2X: -61.4; control2Y: 3.6; x: -61.4; y: 8 }
+ , PathCubic { control1X: -61.4; control1Y: 10.912; control2X: -61.643; control2Y: 24.331; x: -67; y: 22.8 }
+ , PathCubic { control1X: -75.4; control1Y: 20.4; control2X: -71.8; control2Y: 6; x: -79.8; y: 0 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ delegate: [PathMove { x: -71.4; y: 3.8 }
+ , PathCubic { control1X: -71.4; control1Y: 3.8; control2X: -62.422; control2Y: 5.274; x: -61.4; y: 8 }
+ , PathCubic { control1X: -60.8; control1Y: 9.6; control2X: -60.137; control2Y: 17.908; x: -65.6; y: 19 }
+ , PathCubic { control1X: -70.152; control1Y: 19.911; control2X: -72.382; control2Y: 9.69; x: -71.4; y: 3.8 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 14.595; y: 46.349 }
+ , PathCubic { control1X: 14.098; control1Y: 44.607; control2X: 15.409; control2Y: 44.738; x: 17.2; y: 44.2 }
+ , PathCubic { control1X: 19.2; control1Y: 43.6; control2X: 31.4; control2Y: 39.8; x: 32.2; y: 37.2 }
+ , PathCubic { control1X: 33; control1Y: 34.6; control2X: 46.2; control2Y: 39; x: 46.2; y: 39 }
+ , PathCubic { control1X: 48; control1Y: 39.8; control2X: 52.4; control2Y: 42.4; x: 52.4; y: 42.4 }
+ , PathCubic { control1X: 57.2; control1Y: 43.6; control2X: 63.8; control2Y: 44; x: 63.8; y: 44 }
+ , PathCubic { control1X: 66.2; control1Y: 45; control2X: 69.6; control2Y: 47.8; x: 69.6; y: 47.8 }
+ , PathCubic { control1X: 84.2; control1Y: 58; control2X: 96.601; control2Y: 50.8; x: 96.601; y: 50.8 }
+ , PathCubic { control1X: 116.601; control1Y: 44.2; control2X: 110.601; control2Y: 27; x: 110.601; y: 27 }
+ , PathCubic { control1X: 107.601; control1Y: 18; control2X: 110.801; control2Y: 14.6; x: 110.801; y: 14.6 }
+ , PathCubic { control1X: 111.001; control1Y: 10.8; control2X: 118.201; control2Y: 17.2; x: 118.201; y: 17.2 }
+ , PathCubic { control1X: 120.801; control1Y: 21.4; control2X: 121.601; control2Y: 26.4; x: 121.601; y: 26.4 }
+ , PathCubic { control1X: 129.601; control1Y: 37.6; control2X: 126.201; control2Y: 19.8; x: 126.201; y: 19.8 }
+ , PathCubic { control1X: 126.401; control1Y: 18.8; control2X: 123.601; control2Y: 15.2; x: 123.601; y: 14 }
+ , PathCubic { control1X: 123.601; control1Y: 12.8; control2X: 121.801; control2Y: 9.4; x: 121.801; y: 9.4 }
+ , PathCubic { control1X: 118.801; control1Y: 6; control2X: 121.201; control2Y: -1; x: 121.201; y: -1 }
+ , PathCubic { control1X: 123.001; control1Y: -14.8; control2X: 120.801; control2Y: -13; x: 120.801; y: -13 }
+ , PathCubic { control1X: 119.601; control1Y: -14.8; control2X: 110.401; control2Y: -4.8; x: 110.401; y: -4.8 }
+ , PathCubic { control1X: 108.201; control1Y: -1.4; control2X: 102.201; control2Y: 0.2; x: 102.201; y: 0.2 }
+ , PathCubic { control1X: 99.401; control1Y: 2; control2X: 96.001; control2Y: 0.6; x: 96.001; y: 0.6 }
+ , PathCubic { control1X: 93.401; control1Y: 0.2; control2X: 87.801; control2Y: 7.2; x: 87.801; y: 7.2 }
+ , PathCubic { control1X: 90.601; control1Y: 7; control2X: 93.001; control2Y: 11.4; x: 95.401; y: 11.6 }
+ , PathCubic { control1X: 97.801; control1Y: 11.8; control2X: 99.601; control2Y: 9.2; x: 101.201; y: 8.6 }
+ , PathCubic { control1X: 102.801; control1Y: 8; control2X: 105.601; control2Y: 13.8; x: 105.601; y: 13.8 }
+ , PathCubic { control1X: 106.001; control1Y: 16.4; control2X: 100.401; control2Y: 21.2; x: 100.401; y: 21.2 }
+ , PathCubic { control1X: 100.001; control1Y: 25.8; control2X: 98.401; control2Y: 24.2; x: 98.401; y: 24.2 }
+ , PathCubic { control1X: 95.401; control1Y: 23.6; control2X: 94.201; control2Y: 27.4; x: 93.201; y: 32 }
+ , PathCubic { control1X: 92.201; control1Y: 36.6; control2X: 88.001; control2Y: 37; x: 88.001; y: 37 }
+ , PathCubic { control1X: 86.401; control1Y: 44.4; control2X: 85.2; control2Y: 41.4; x: 85.2; y: 41.4 }
+ , PathCubic { control1X: 85; control1Y: 35.8; control2X: 79; control2Y: 41.6; x: 79; y: 41.6 }
+ , PathCubic { control1X: 77.8; control1Y: 43.6; control2X: 73.2; control2Y: 41.4; x: 73.2; y: 41.4 }
+ , PathCubic { control1X: 66.4; control1Y: 39.4; control2X: 68.8; control2Y: 37.4; x: 68.8; y: 37.4 }
+ , PathCubic { control1X: 70.6; control1Y: 35.2; control2X: 81.8; control2Y: 37.4; x: 81.8; y: 37.4 }
+ , PathCubic { control1X: 84; control1Y: 35.8; control2X: 76; control2Y: 31.8; x: 76; y: 31.8 }
+ , PathCubic { control1X: 75.4; control1Y: 30; control2X: 76.4; control2Y: 25.6; x: 76.4; y: 25.6 }
+ , PathCubic { control1X: 77.6; control1Y: 22.4; control2X: 84.4; control2Y: 16.8; x: 84.4; y: 16.8 }
+ , PathCubic { control1X: 93.801; control1Y: 15.6; control2X: 91.001; control2Y: 14; x: 91.001; y: 14 }
+ , PathCubic { control1X: 84.801; control1Y: 8.8; control2X: 79; control2Y: 16.4; x: 79; y: 16.4 }
+ , PathCubic { control1X: 76.8; control1Y: 22.6; control2X: 59.4; control2Y: 37.6; x: 59.4; y: 37.6 }
+ , PathCubic { control1X: 54.6; control1Y: 41; control2X: 57.2; control2Y: 34.2; x: 53.2; y: 37.6 }
+ , PathCubic { control1X: 49.2; control1Y: 41; control2X: 28.6; control2Y: 32; x: 28.6; y: 32 }
+ , PathCubic { control1X: 17.038; control1Y: 30.807; control2X: 14.306; control2Y: 46.549; x: 10.777; y: 43.429 }
+ , PathCubic { control1X: 10.777; control1Y: 43.429; control2X: 16.195; control2Y: 51.949; x: 14.595; y: 46.349 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 209.401; y: -120 }
+ , PathCubic { control1X: 209.401; control1Y: -120; control2X: 183.801; control2Y: -112; x: 181.001; y: -93.2 }
+ , PathCubic { control1X: 181.001; control1Y: -93.2; control2X: 178.601; control2Y: -70.4; x: 199.001; y: -52.8 }
+ , PathCubic { control1X: 199.001; control1Y: -52.8; control2X: 199.401; control2Y: -46.4; x: 201.401; y: -43.2 }
+ , PathCubic { control1X: 201.401; control1Y: -43.2; control2X: 199.801; control2Y: -38.4; x: 218.601; y: -46 }
+ , PathLine { x: 245.801; y: -54.4 }
+ , PathCubic { control1X: 245.801; control1Y: -54.4; control2X: 252.201; control2Y: -56.8; x: 257.401; y: -65.6 }
+ , PathCubic { control1X: 262.601; control1Y: -74.4; control2X: 277.801; control2Y: -93.2; x: 274.201; y: -118.4 }
+ , PathCubic { control1X: 274.201; control1Y: -118.4; control2X: 275.401; control2Y: -129.6; x: 269.401; y: -130 }
+ , PathCubic { control1X: 269.401; control1Y: -130; control2X: 261.001; control2Y: -131.6; x: 253.801; y: -124 }
+ , PathCubic { control1X: 253.801; control1Y: -124; control2X: 247.001; control2Y: -120.8; x: 244.601; y: -121.2 }
+ , PathLine { x: 209.401; y: -120 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 264.022; y: -120.99 }
+ , PathCubic { control1X: 264.022; control1Y: -120.99; control2X: 266.122; control2Y: -129.92; x: 261.282; y: -125.08 }
+ , PathCubic { control1X: 261.282; control1Y: -125.08; control2X: 254.242; control2Y: -119.36; x: 246.761; y: -119.36 }
+ , PathCubic { control1X: 246.761; control1Y: -119.36; control2X: 232.241; control2Y: -117.16; x: 227.841; y: -103.96 }
+ , PathCubic { control1X: 227.841; control1Y: -103.96; control2X: 223.881; control2Y: -77.12; x: 231.801; y: -71.4 }
+ , PathCubic { control1X: 231.801; control1Y: -71.4; control2X: 236.641; control2Y: -63.92; x: 243.681; y: -70.52 }
+ , PathCubic { control1X: 250.722; control1Y: -77.12; control2X: 266.222; control2Y: -107.35; x: 264.022; y: -120.99 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#323232"
+ strokeWidth: -1
+ delegate: [PathMove { x: 263.648; y: -120.632 }
+ , PathCubic { control1X: 263.648; control1Y: -120.632; control2X: 265.738; control2Y: -129.376; x: 260.986; y: -124.624 }
+ , PathCubic { control1X: 260.986; control1Y: -124.624; control2X: 254.074; control2Y: -119.008; x: 246.729; y: -119.008 }
+ , PathCubic { control1X: 246.729; control1Y: -119.008; control2X: 232.473; control2Y: -116.848; x: 228.153; y: -103.888 }
+ , PathCubic { control1X: 228.153; control1Y: -103.888; control2X: 224.265; control2Y: -77.536; x: 232.041; y: -71.92 }
+ , PathCubic { control1X: 232.041; control1Y: -71.92; control2X: 236.793; control2Y: -64.576; x: 243.705; y: -71.056 }
+ , PathCubic { control1X: 250.618; control1Y: -77.536; control2X: 265.808; control2Y: -107.24; x: 263.648; y: -120.632 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#666666"
+ strokeWidth: -1
+ delegate: [PathMove { x: 263.274; y: -120.274 }
+ , PathCubic { control1X: 263.274; control1Y: -120.274; control2X: 265.354; control2Y: -128.832; x: 260.69; y: -124.168 }
+ , PathCubic { control1X: 260.69; control1Y: -124.168; control2X: 253.906; control2Y: -118.656; x: 246.697; y: -118.656 }
+ , PathCubic { control1X: 246.697; control1Y: -118.656; control2X: 232.705; control2Y: -116.536; x: 228.465; y: -103.816 }
+ , PathCubic { control1X: 228.465; control1Y: -103.816; control2X: 224.649; control2Y: -77.952; x: 232.281; y: -72.44 }
+ , PathCubic { control1X: 232.281; control1Y: -72.44; control2X: 236.945; control2Y: -65.232; x: 243.729; y: -71.592 }
+ , PathCubic { control1X: 250.514; control1Y: -77.952; control2X: 265.394; control2Y: -107.13; x: 263.274; y: -120.274 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#999999"
+ strokeWidth: -1
+ delegate: [PathMove { x: 262.9; y: -119.916 }
+ , PathCubic { control1X: 262.9; control1Y: -119.916; control2X: 264.97; control2Y: -128.288; x: 260.394; y: -123.712 }
+ , PathCubic { control1X: 260.394; control1Y: -123.712; control2X: 253.738; control2Y: -118.304; x: 246.665; y: -118.304 }
+ , PathCubic { control1X: 246.665; control1Y: -118.304; control2X: 232.937; control2Y: -116.224; x: 228.777; y: -103.744 }
+ , PathCubic { control1X: 228.777; control1Y: -103.744; control2X: 225.033; control2Y: -78.368; x: 232.521; y: -72.96 }
+ , PathCubic { control1X: 232.521; control1Y: -72.96; control2X: 237.097; control2Y: -65.888; x: 243.753; y: -72.128 }
+ , PathCubic { control1X: 250.41; control1Y: -78.368; control2X: 264.98; control2Y: -107.02; x: 262.9; y: -119.916 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 262.526; y: -119.558 }
+ , PathCubic { control1X: 262.526; control1Y: -119.558; control2X: 264.586; control2Y: -127.744; x: 260.098; y: -123.256 }
+ , PathCubic { control1X: 260.098; control1Y: -123.256; control2X: 253.569; control2Y: -117.952; x: 246.633; y: -117.952 }
+ , PathCubic { control1X: 246.633; control1Y: -117.952; control2X: 233.169; control2Y: -115.912; x: 229.089; y: -103.672 }
+ , PathCubic { control1X: 229.089; control1Y: -103.672; control2X: 225.417; control2Y: -78.784; x: 232.761; y: -73.48 }
+ , PathCubic { control1X: 232.761; control1Y: -73.48; control2X: 237.249; control2Y: -66.544; x: 243.777; y: -72.664 }
+ , PathCubic { control1X: 250.305; control1Y: -78.784; control2X: 264.566; control2Y: -106.91; x: 262.526; y: -119.558 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: 262.151; y: -119.2 }
+ , PathCubic { control1X: 262.151; control1Y: -119.2; control2X: 264.201; control2Y: -127.2; x: 259.801; y: -122.8 }
+ , PathCubic { control1X: 259.801; control1Y: -122.8; control2X: 253.401; control2Y: -117.6; x: 246.601; y: -117.6 }
+ , PathCubic { control1X: 246.601; control1Y: -117.6; control2X: 233.401; control2Y: -115.6; x: 229.401; y: -103.6 }
+ , PathCubic { control1X: 229.401; control1Y: -103.6; control2X: 225.801; control2Y: -79.2; x: 233.001; y: -74 }
+ , PathCubic { control1X: 233.001; control1Y: -74; control2X: 237.401; control2Y: -67.2; x: 243.801; y: -73.2 }
+ , PathCubic { control1X: 250.201; control1Y: -79.2; control2X: 264.151; control2Y: -106.8; x: 262.151; y: -119.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#992600"
+ strokeWidth: -1
+ delegate: [PathMove { x: 50.6; y: 84 }
+ , PathCubic { control1X: 50.6; control1Y: 84; control2X: 30.2; control2Y: 64.8; x: 22.2; y: 64 }
+ , PathCubic { control1X: 22.2; control1Y: 64; control2X: -12.2; control2Y: 60; x: -27; y: 78 }
+ , PathCubic { control1X: -27; control1Y: 78; control2X: -9.4; control2Y: 57.6; x: 18.2; y: 63.2 }
+ , PathCubic { control1X: 18.2; control1Y: 63.2; control2X: -3.4; control2Y: 58.8; x: -15.8; y: 62 }
+ , PathCubic { control1X: -15.8; control1Y: 62; control2X: -32.6; control2Y: 62; x: -42.2; y: 76 }
+ , PathLine { x: -45; y: 80.8 }
+ , PathCubic { control1X: -45; control1Y: 80.8; control2X: -41; control2Y: 66; x: -22.6; y: 60 }
+ , PathCubic { control1X: -22.6; control1Y: 60; control2X: 0.2; control2Y: 55.2; x: 11; y: 60 }
+ , PathCubic { control1X: 11; control1Y: 60; control2X: -10.6; control2Y: 53.2; x: -20.6; y: 55.2 }
+ , PathCubic { control1X: -20.6; control1Y: 55.2; control2X: -51; control2Y: 52.8; x: -63.8; y: 79.2 }
+ , PathCubic { control1X: -63.8; control1Y: 79.2; control2X: -59.8; control2Y: 64.8; x: -45; y: 57.6 }
+ , PathCubic { control1X: -45; control1Y: 57.6; control2X: -31.4; control2Y: 48.8; x: -11; y: 51.6 }
+ , PathCubic { control1X: -11; control1Y: 51.6; control2X: 3.4; control2Y: 54.8; x: 8.6; y: 57.2 }
+ , PathCubic { control1X: 13.8; control1Y: 59.6; control2X: 12.6; control2Y: 56.8; x: 4.2; y: 52 }
+ , PathCubic { control1X: 4.2; control1Y: 52; control2X: -1.4; control2Y: 42; x: -15.4; y: 42.4 }
+ , PathCubic { control1X: -15.4; control1Y: 42.4; control2X: -58.2; control2Y: 46; x: -68.6; y: 58 }
+ , PathCubic { control1X: -68.6; control1Y: 58; control2X: -55; control2Y: 46.8; x: -44.6; y: 44 }
+ , PathCubic { control1X: -44.6; control1Y: 44; control2X: -22.2; control2Y: 36; x: -13.8; y: 36.8 }
+ , PathCubic { control1X: -13.8; control1Y: 36.8; control2X: 11; control2Y: 37.8; x: 18.6; y: 33.8 }
+ , PathCubic { control1X: 18.6; control1Y: 33.8; control2X: 7.4; control2Y: 38.8; x: 10.6; y: 42 }
+ , PathCubic { control1X: 13.8; control1Y: 45.2; control2X: 20.6; control2Y: 52.8; x: 20.6; y: 54 }
+ , PathCubic { control1X: 20.6; control1Y: 55.2; control2X: 44.8; control2Y: 77.3; x: 48.4; y: 81.7 }
+ , PathLine { x: 50.6; y: 84 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 189; y: 278 }
+ , PathCubic { control1X: 189; control1Y: 278; control2X: 173.5; control2Y: 241.5; x: 161; y: 232 }
+ , PathCubic { control1X: 161; control1Y: 232; control2X: 187; control2Y: 248; x: 190.5; y: 266 }
+ , PathCubic { control1X: 190.5; control1Y: 266; control2X: 190.5; control2Y: 276; x: 189; y: 278 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 236; y: 285.5 }
+ , PathCubic { control1X: 236; control1Y: 285.5; control2X: 209.5; control2Y: 230.5; x: 191; y: 206.5 }
+ , PathCubic { control1X: 191; control1Y: 206.5; control2X: 234.5; control2Y: 244; x: 239.5; y: 270.5 }
+ , PathLine { x: 240; y: 276 }
+ , PathLine { x: 237; y: 273.5 }
+ , PathCubic { control1X: 237; control1Y: 273.5; control2X: 236.5; control2Y: 282.5; x: 236; y: 285.5 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 292.5; y: 237 }
+ , PathCubic { control1X: 292.5; control1Y: 237; control2X: 230; control2Y: 177.5; x: 228.5; y: 175 }
+ , PathCubic { control1X: 228.5; control1Y: 175; control2X: 289; control2Y: 241; x: 292; y: 248.5 }
+ , PathCubic { control1X: 292; control1Y: 248.5; control2X: 290; control2Y: 239.5; x: 292.5; y: 237 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 104; y: 280.5 }
+ , PathCubic { control1X: 104; control1Y: 280.5; control2X: 123.5; control2Y: 228.5; x: 142.5; y: 251 }
+ , PathCubic { control1X: 142.5; control1Y: 251; control2X: 157.5; control2Y: 261; x: 157; y: 264 }
+ , PathCubic { control1X: 157; control1Y: 264; control2X: 153; control2Y: 257.5; x: 135; y: 258 }
+ , PathCubic { control1X: 135; control1Y: 258; control2X: 116; control2Y: 255; x: 104; y: 280.5 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 294.5; y: 153 }
+ , PathCubic { control1X: 294.5; control1Y: 153; control2X: 249.5; control2Y: 124.5; x: 242; y: 123 }
+ , PathCubic { control1X: 230.193; control1Y: 120.639; control2X: 291.5; control2Y: 152; x: 296.5; y: 162.5 }
+ , PathCubic { control1X: 296.5; control1Y: 162.5; control2X: 298.5; control2Y: 160; x: 294.5; y: 153 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 143.801; y: 259.601 }
+ , PathCubic { control1X: 143.801; control1Y: 259.601; control2X: 164.201; control2Y: 257.601; x: 171.001; y: 250.801 }
+ , PathLine { x: 175.401; y: 254.401 }
+ , PathLine { x: 193.001; y: 216.001 }
+ , PathLine { x: 196.601; y: 221.201 }
+ , PathCubic { control1X: 196.601; control1Y: 221.201; control2X: 211.001; control2Y: 206.401; x: 210.201; y: 198.401 }
+ , PathCubic { control1X: 209.401; control1Y: 190.401; control2X: 223.001; control2Y: 204.401; x: 223.001; y: 204.401 }
+ , PathCubic { control1X: 223.001; control1Y: 204.401; control2X: 222.201; control2Y: 192.801; x: 229.401; y: 199.601 }
+ , PathCubic { control1X: 229.401; control1Y: 199.601; control2X: 227.001; control2Y: 184.001; x: 235.401; y: 192.001 }
+ , PathCubic { control1X: 235.401; control1Y: 192.001; control2X: 224.864; control2Y: 161.844; x: 247.401; y: 187.601 }
+ , PathCubic { control1X: 253.001; control1Y: 194.001; control2X: 248.601; control2Y: 187.201; x: 248.601; y: 187.201 }
+ , PathCubic { control1X: 248.601; control1Y: 187.201; control2X: 222.601; control2Y: 139.201; x: 244.201; y: 153.601 }
+ , PathCubic { control1X: 244.201; control1Y: 153.601; control2X: 246.201; control2Y: 130.801; x: 245.001; y: 126.401 }
+ , PathCubic { control1X: 243.801; control1Y: 122.001; control2X: 241.801; control2Y: 99.6; x: 237.001; y: 94.4 }
+ , PathCubic { control1X: 232.201; control1Y: 89.2; control2X: 237.401; control2Y: 87.6; x: 243.001; y: 92.8 }
+ , PathCubic { control1X: 243.001; control1Y: 92.8; control2X: 231.801; control2Y: 68.8; x: 245.001; y: 80.8 }
+ , PathCubic { control1X: 245.001; control1Y: 80.8; control2X: 241.401; control2Y: 65.6; x: 237.001; y: 62.8 }
+ , PathCubic { control1X: 237.001; control1Y: 62.8; control2X: 231.401; control2Y: 45.6; x: 246.601; y: 56.4 }
+ , PathCubic { control1X: 246.601; control1Y: 56.4; control2X: 242.201; control2Y: 44; x: 239.001; y: 40.8 }
+ , PathCubic { control1X: 239.001; control1Y: 40.8; control2X: 227.401; control2Y: 13.2; x: 234.601; y: 18 }
+ , PathLine { x: 239.001; y: 21.6 }
+ , PathCubic { control1X: 239.001; control1Y: 21.6; control2X: 232.201; control2Y: 7.6; x: 238.601; y: 12 }
+ , PathCubic { control1X: 245.001; control1Y: 16.4; control2X: 245.001; control2Y: 16; x: 245.001; y: 16 }
+ , PathCubic { control1X: 245.001; control1Y: 16; control2X: 223.801; control2Y: -17.2; x: 244.201; y: 0.4 }
+ , PathCubic { control1X: 244.201; control1Y: 0.4; control2X: 236.042; control2Y: -13.518; x: 232.601; y: -20.4 }
+ , PathCubic { control1X: 232.601; control1Y: -20.4; control2X: 213.801; control2Y: -40.8; x: 228.201; y: -34.4 }
+ , PathLine { x: 233.001; y: -32.8 }
+ , PathCubic { control1X: 233.001; control1Y: -32.8; control2X: 224.201; control2Y: -42.8; x: 216.201; y: -44.4 }
+ , PathCubic { control1X: 208.201; control1Y: -46; control2X: 218.601; control2Y: -52.4; x: 225.001; y: -50.4 }
+ , PathCubic { control1X: 231.401; control1Y: -48.4; control2X: 247.001; control2Y: -40.8; x: 247.001; y: -40.8 }
+ , PathCubic { control1X: 247.001; control1Y: -40.8; control2X: 259.801; control2Y: -22; x: 263.801; y: -21.6 }
+ , PathCubic { control1X: 263.801; control1Y: -21.6; control2X: 243.801; control2Y: -29.2; x: 249.801; y: -21.2 }
+ , PathCubic { control1X: 249.801; control1Y: -21.2; control2X: 264.201; control2Y: -7.2; x: 257.001; y: -7.6 }
+ , PathCubic { control1X: 257.001; control1Y: -7.6; control2X: 251.001; control2Y: -0.4; x: 255.801; y: 8.4 }
+ , PathCubic { control1X: 255.801; control1Y: 8.4; control2X: 237.342; control2Y: -9.991; x: 252.201; y: 15.6 }
+ , PathLine { x: 259.001; y: 32 }
+ , PathCubic { control1X: 259.001; control1Y: 32; control2X: 234.601; control2Y: 7.2; x: 245.801; y: 29.2 }
+ , PathCubic { control1X: 245.801; control1Y: 29.2; control2X: 263.001; control2Y: 52.8; x: 265.001; y: 53.2 }
+ , PathCubic { control1X: 267.001; control1Y: 53.6; control2X: 271.401; control2Y: 62.4; x: 271.401; y: 62.4 }
+ , PathLine { x: 267.001; y: 60.4 }
+ , PathLine { x: 272.201; y: 69.2 }
+ , PathCubic { control1X: 272.201; control1Y: 69.2; control2X: 261.001; control2Y: 57.2; x: 267.001; y: 70.4 }
+ , PathLine { x: 272.601; y: 84.8 }
+ , PathCubic { control1X: 272.601; control1Y: 84.8; control2X: 252.201; control2Y: 62.8; x: 265.801; y: 92.4 }
+ , PathCubic { control1X: 265.801; control1Y: 92.4; control2X: 249.401; control2Y: 87.2; x: 258.201; y: 104.4 }
+ , PathCubic { control1X: 258.201; control1Y: 104.4; control2X: 256.601; control2Y: 120.401; x: 257.001; y: 125.601 }
+ , PathCubic { control1X: 257.401; control1Y: 130.801; control2X: 258.601; control2Y: 159.201; x: 254.201; y: 167.201 }
+ , PathCubic { control1X: 249.801; control1Y: 175.201; control2X: 260.201; control2Y: 194.401; x: 262.201; y: 198.401 }
+ , PathCubic { control1X: 264.201; control1Y: 202.401; control2X: 267.801; control2Y: 213.201; x: 259.001; y: 204.001 }
+ , PathCubic { control1X: 250.201; control1Y: 194.801; control2X: 254.601; control2Y: 200.401; x: 256.601; y: 209.201 }
+ , PathCubic { control1X: 258.601; control1Y: 218.001; control2X: 264.601; control2Y: 233.601; x: 263.801; y: 239.201 }
+ , PathCubic { control1X: 263.801; control1Y: 239.201; control2X: 262.601; control2Y: 240.401; x: 259.401; y: 236.801 }
+ , PathCubic { control1X: 259.401; control1Y: 236.801; control2X: 244.601; control2Y: 214.001; x: 246.201; y: 228.401 }
+ , PathCubic { control1X: 246.201; control1Y: 228.401; control2X: 245.001; control2Y: 236.401; x: 241.801; y: 245.201 }
+ , PathCubic { control1X: 241.801; control1Y: 245.201; control2X: 238.601; control2Y: 256.001; x: 238.601; y: 247.201 }
+ , PathCubic { control1X: 238.601; control1Y: 247.201; control2X: 235.401; control2Y: 230.401; x: 232.601; y: 238.001 }
+ , PathCubic { control1X: 229.801; control1Y: 245.601; control2X: 226.201; control2Y: 251.601; x: 223.401; y: 254.001 }
+ , PathCubic { control1X: 220.601; control1Y: 256.401; control2X: 215.401; control2Y: 233.601; x: 214.201; y: 244.001 }
+ , PathCubic { control1X: 214.201; control1Y: 244.001; control2X: 202.201; control2Y: 231.601; x: 197.401; y: 248.001 }
+ , PathLine { x: 185.801; y: 264.401 }
+ , PathCubic { control1X: 185.801; control1Y: 264.401; control2X: 185.401; control2Y: 252.001; x: 184.201; y: 258.001 }
+ , PathCubic { control1X: 184.201; control1Y: 258.001; control2X: 154.201; control2Y: 264.001; x: 143.801; y: 259.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 109.401; y: -97.2 }
+ , PathCubic { control1X: 109.401; control1Y: -97.2; control2X: 97.801; control2Y: -105.2; x: 93.801; y: -104.8 }
+ , PathCubic { control1X: 89.801; control1Y: -104.4; control2X: 121.401; control2Y: -113.6; x: 162.601; y: -86 }
+ , PathCubic { control1X: 162.601; control1Y: -86; control2X: 167.401; control2Y: -83.2; x: 171.001; y: -83.6 }
+ , PathCubic { control1X: 171.001; control1Y: -83.6; control2X: 174.201; control2Y: -81.2; x: 171.401; y: -77.6 }
+ , PathCubic { control1X: 171.401; control1Y: -77.6; control2X: 162.601; control2Y: -68; x: 173.801; y: -56.8 }
+ , PathCubic { control1X: 173.801; control1Y: -56.8; control2X: 192.201; control2Y: -50; x: 186.601; y: -58.8 }
+ , PathCubic { control1X: 186.601; control1Y: -58.8; control2X: 197.401; control2Y: -54.8; x: 199.801; y: -50.8 }
+ , PathCubic { control1X: 202.201; control1Y: -46.8; control2X: 201.001; control2Y: -50.8; x: 201.001; y: -50.8 }
+ , PathCubic { control1X: 201.001; control1Y: -50.8; control2X: 194.601; control2Y: -58; x: 188.601; y: -63.2 }
+ , PathCubic { control1X: 188.601; control1Y: -63.2; control2X: 183.401; control2Y: -65.2; x: 180.601; y: -73.6 }
+ , PathCubic { control1X: 177.801; control1Y: -82; control2X: 175.401; control2Y: -92; x: 179.801; y: -95.2 }
+ , PathCubic { control1X: 179.801; control1Y: -95.2; control2X: 175.801; control2Y: -90.8; x: 176.601; y: -94.8 }
+ , PathCubic { control1X: 177.401; control1Y: -98.8; control2X: 181.001; control2Y: -102.4; x: 182.601; y: -102.8 }
+ , PathCubic { control1X: 184.201; control1Y: -103.2; control2X: 200.601; control2Y: -119; x: 207.401; y: -119.4 }
+ , PathCubic { control1X: 207.401; control1Y: -119.4; control2X: 198.201; control2Y: -118; x: 195.201; y: -119 }
+ , PathCubic { control1X: 192.201; control1Y: -120; control2X: 165.601; control2Y: -131.4; x: 159.601; y: -132.6 }
+ , PathCubic { control1X: 159.601; control1Y: -132.6; control2X: 142.801; control2Y: -139.2; x: 154.801; y: -137.2 }
+ , PathCubic { control1X: 154.801; control1Y: -137.2; control2X: 190.601; control2Y: -133.4; x: 208.801; y: -120.2 }
+ , PathCubic { control1X: 208.801; control1Y: -120.2; control2X: 201.601; control2Y: -128.6; x: 183.201; y: -135.6 }
+ , PathCubic { control1X: 183.201; control1Y: -135.6; control2X: 161.001; control2Y: -148.2; x: 125.801; y: -143.2 }
+ , PathCubic { control1X: 125.801; control1Y: -143.2; control2X: 108.001; control2Y: -140; x: 100.201; y: -138.2 }
+ , PathCubic { control1X: 100.201; control1Y: -138.2; control2X: 97.601; control2Y: -138.8; x: 97.001; y: -139.2 }
+ , PathCubic { control1X: 96.401; control1Y: -139.6; control2X: 84.6; control2Y: -148.6; x: 57; y: -141.6 }
+ , PathCubic { control1X: 57; control1Y: -141.6; control2X: 40; control2Y: -137; x: 31.4; y: -132.2 }
+ , PathCubic { control1X: 31.4; control1Y: -132.2; control2X: 16.2; control2Y: -131; x: 12.6; y: -127.8 }
+ , PathCubic { control1X: 12.6; control1Y: -127.8; control2X: -6; control2Y: -113.2; x: -8; y: -112.4 }
+ , PathCubic { control1X: -10; control1Y: -111.6; control2X: -21.4; control2Y: -104; x: -22.2; y: -103.6 }
+ , PathCubic { control1X: -22.2; control1Y: -103.6; control2X: 2.4; control2Y: -110.2; x: 4.8; y: -112.6 }
+ , PathCubic { control1X: 7.2; control1Y: -115; control2X: 24.6; control2Y: -117.6; x: 27; y: -116.2 }
+ , PathCubic { control1X: 29.4; control1Y: -114.8; control2X: 37.8; control2Y: -115.4; x: 28.2; y: -114.8 }
+ , PathCubic { control1X: 28.2; control1Y: -114.8; control2X: 103.801; control2Y: -100; x: 104.601; y: -98 }
+ , PathCubic { control1X: 105.401; control1Y: -96; control2X: 109.401; control2Y: -97.2; x: 109.401; y: -97.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: 180.801; y: -106.4 }
+ , PathCubic { control1X: 180.801; control1Y: -106.4; control2X: 170.601; control2Y: -113.8; x: 168.601; y: -113.8 }
+ , PathCubic { control1X: 166.601; control1Y: -113.8; control2X: 154.201; control2Y: -124; x: 150.001; y: -123.6 }
+ , PathCubic { control1X: 145.801; control1Y: -123.2; control2X: 133.601; control2Y: -133.2; x: 106.201; y: -125 }
+ , PathCubic { control1X: 106.201; control1Y: -125; control2X: 105.601; control2Y: -127; x: 109.201; y: -127.8 }
+ , PathCubic { control1X: 109.201; control1Y: -127.8; control2X: 115.601; control2Y: -130; x: 116.001; y: -130.6 }
+ , PathCubic { control1X: 116.001; control1Y: -130.6; control2X: 136.201; control2Y: -134.8; x: 143.401; y: -131.2 }
+ , PathCubic { control1X: 143.401; control1Y: -131.2; control2X: 152.601; control2Y: -128.6; x: 158.801; y: -122.4 }
+ , PathCubic { control1X: 158.801; control1Y: -122.4; control2X: 170.001; control2Y: -119.2; x: 173.201; y: -120.2 }
+ , PathCubic { control1X: 173.201; control1Y: -120.2; control2X: 182.001; control2Y: -118; x: 182.401; y: -116.2 }
+ , PathCubic { control1X: 182.401; control1Y: -116.2; control2X: 188.201; control2Y: -113.2; x: 186.401; y: -110.6 }
+ , PathCubic { control1X: 186.401; control1Y: -110.6; control2X: 186.801; control2Y: -109; x: 180.801; y: -106.4 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: 168.33; y: -108.509 }
+ , PathCubic { control1X: 169.137; control1Y: -107.877; control2X: 170.156; control2Y: -107.779; x: 170.761; y: -106.97 }
+ , PathCubic { control1X: 170.995; control1Y: -106.656; control2X: 170.706; control2Y: -106.33; x: 170.391; y: -106.233 }
+ , PathCubic { control1X: 169.348; control1Y: -105.916; control2X: 168.292; control2Y: -106.486; x: 167.15; y: -105.898 }
+ , PathCubic { control1X: 166.748; control1Y: -105.691; control2X: 166.106; control2Y: -105.873; x: 165.553; y: -106.022 }
+ , PathCubic { control1X: 163.921; control1Y: -106.463; control2X: 162.092; control2Y: -106.488; x: 160.401; y: -105.8 }
+ , PathCubic { control1X: 158.416; control1Y: -106.929; control2X: 156.056; control2Y: -106.345; x: 153.975; y: -107.346 }
+ , PathCubic { control1X: 153.917; control1Y: -107.373; control2X: 153.695; control2Y: -107.027; x: 153.621; y: -107.054 }
+ , PathCubic { control1X: 150.575; control1Y: -108.199; control2X: 146.832; control2Y: -107.916; x: 144.401; y: -110.2 }
+ , PathCubic { control1X: 141.973; control1Y: -110.612; control2X: 139.616; control2Y: -111.074; x: 137.188; y: -111.754 }
+ , PathCubic { control1X: 135.37; control1Y: -112.263; control2X: 133.961; control2Y: -113.252; x: 132.341; y: -114.084 }
+ , PathCubic { control1X: 130.964; control1Y: -114.792; control2X: 129.507; control2Y: -115.314; x: 127.973; y: -115.686 }
+ , PathCubic { control1X: 126.11; control1Y: -116.138; control2X: 124.279; control2Y: -116.026; x: 122.386; y: -116.546 }
+ , PathCubic { control1X: 122.293; control1Y: -116.571; control2X: 122.101; control2Y: -116.227; x: 122.019; y: -116.254 }
+ , PathCubic { control1X: 121.695; control1Y: -116.362; control2X: 121.405; control2Y: -116.945; x: 121.234; y: -116.892 }
+ , PathCubic { control1X: 119.553; control1Y: -116.37; control2X: 118.065; control2Y: -117.342; x: 116.401; y: -117 }
+ , PathCubic { control1X: 115.223; control1Y: -118.224; control2X: 113.495; control2Y: -117.979; x: 111.949; y: -118.421 }
+ , PathCubic { control1X: 108.985; control1Y: -119.269; control2X: 105.831; control2Y: -117.999; x: 102.801; y: -119 }
+ , PathCubic { control1X: 106.914; control1Y: -120.842; control2X: 111.601; control2Y: -119.61; x: 115.663; y: -121.679 }
+ , PathCubic { control1X: 117.991; control1Y: -122.865; control2X: 120.653; control2Y: -121.763; x: 123.223; y: -122.523 }
+ , PathCubic { control1X: 123.71; control1Y: -122.667; control2X: 124.401; control2Y: -122.869; x: 124.801; y: -122.2 }
+ , PathCubic { control1X: 124.935; control1Y: -122.335; control2X: 125.117; control2Y: -122.574; x: 125.175; y: -122.546 }
+ , PathCubic { control1X: 127.625; control1Y: -121.389; control2X: 129.94; control2Y: -120.115; x: 132.422; y: -119.049 }
+ , PathCubic { control1X: 132.763; control1Y: -118.903; control2X: 133.295; control2Y: -119.135; x: 133.547; y: -118.933 }
+ , PathCubic { control1X: 135.067; control1Y: -117.717; control2X: 137.01; control2Y: -117.82; x: 138.401; y: -116.6 }
+ , PathCubic { control1X: 140.099; control1Y: -117.102; control2X: 141.892; control2Y: -116.722; x: 143.621; y: -117.346 }
+ , PathCubic { control1X: 143.698; control1Y: -117.373; control2X: 143.932; control2Y: -117.032; x: 143.965; y: -117.054 }
+ , PathCubic { control1X: 145.095; control1Y: -117.802; control2X: 146.25; control2Y: -117.531; x: 147.142; y: -117.227 }
+ , PathCubic { control1X: 147.48; control1Y: -117.112; control2X: 148.143; control2Y: -116.865; x: 148.448; y: -116.791 }
+ , PathCubic { control1X: 149.574; control1Y: -116.515; control2X: 150.43; control2Y: -116.035; x: 151.609; y: -115.852 }
+ , PathCubic { control1X: 151.723; control1Y: -115.834; control2X: 151.908; control2Y: -116.174; x: 151.98; y: -116.146 }
+ , PathCubic { control1X: 153.103; control1Y: -115.708; control2X: 154.145; control2Y: -115.764; x: 154.801; y: -114.6 }
+ , PathCubic { control1X: 154.936; control1Y: -114.735; control2X: 155.101; control2Y: -114.973; x: 155.183; y: -114.946 }
+ , PathCubic { control1X: 156.21; control1Y: -114.608; control2X: 156.859; control2Y: -113.853; x: 157.96; y: -113.612 }
+ , PathCubic { control1X: 158.445; control1Y: -113.506; control2X: 159.057; control2Y: -112.88; x: 159.633; y: -112.704 }
+ , PathCubic { control1X: 162.025; control1Y: -111.973; control2X: 163.868; control2Y: -110.444; x: 166.062; y: -109.549 }
+ , PathCubic { control1X: 166.821; control1Y: -109.239; control2X: 167.697; control2Y: -109.005; x: 168.33; y: -108.509 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: 91.696; y: -122.739 }
+ , PathCubic { control1X: 89.178; control1Y: -124.464; control2X: 86.81; control2Y: -125.57; x: 84.368; y: -127.356 }
+ , PathCubic { control1X: 84.187; control1Y: -127.489; control2X: 83.827; control2Y: -127.319; x: 83.625; y: -127.441 }
+ , PathCubic { control1X: 82.618; control1Y: -128.05; control2X: 81.73; control2Y: -128.631; x: 80.748; y: -129.327 }
+ , PathCubic { control1X: 80.209; control1Y: -129.709; control2X: 79.388; control2Y: -129.698; x: 78.88; y: -129.956 }
+ , PathCubic { control1X: 76.336; control1Y: -131.248; control2X: 73.707; control2Y: -131.806; x: 71.2; y: -133 }
+ , PathCubic { control1X: 71.882; control1Y: -133.638; control2X: 73.004; control2Y: -133.394; x: 73.6; y: -134.2 }
+ , PathCubic { control1X: 73.795; control1Y: -133.92; control2X: 74.033; control2Y: -133.636; x: 74.386; y: -133.827 }
+ , PathCubic { control1X: 76.064; control1Y: -134.731; control2X: 77.914; control2Y: -134.884; x: 79.59; y: -134.794 }
+ , PathCubic { control1X: 81.294; control1Y: -134.702; control2X: 83.014; control2Y: -134.397; x: 84.789; y: -134.125 }
+ , PathCubic { control1X: 85.096; control1Y: -134.078; control2X: 85.295; control2Y: -133.555; x: 85.618; y: -133.458 }
+ , PathCubic { control1X: 87.846; control1Y: -132.795; control2X: 90.235; control2Y: -133.32; x: 92.354; y: -132.482 }
+ , PathCubic { control1X: 93.945; control1Y: -131.853; control2X: 95.515; control2Y: -131.03; x: 96.754; y: -129.755 }
+ , PathCubic { control1X: 97.006; control1Y: -129.495; control2X: 96.681; control2Y: -129.194; x: 96.401; y: -129 }
+ , PathCubic { control1X: 96.789; control1Y: -129.109; control2X: 97.062; control2Y: -128.903; x: 97.173; y: -128.59 }
+ , PathCubic { control1X: 97.257; control1Y: -128.351; control2X: 97.257; control2Y: -128.049; x: 97.173; y: -127.81 }
+ , PathCubic { control1X: 97.061; control1Y: -127.498; control2X: 96.782; control2Y: -127.397; x: 96.408; y: -127.346 }
+ , PathCubic { control1X: 95.001; control1Y: -127.156; control2X: 96.773; control2Y: -128.536; x: 96.073; y: -128.088 }
+ , PathCubic { control1X: 94.8; control1Y: -127.274; control2X: 95.546; control2Y: -125.868; x: 94.801; y: -124.6 }
+ , PathCubic { control1X: 94.521; control1Y: -124.794; control2X: 94.291; control2Y: -125.012; x: 94.401; y: -125.4 }
+ , PathCubic { control1X: 94.635; control1Y: -124.878; control2X: 94.033; control2Y: -124.588; x: 93.865; y: -124.272 }
+ , PathCubic { control1X: 93.48; control1Y: -123.547; control2X: 92.581; control2Y: -122.132; x: 91.696; y: -122.739 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: 59.198; y: -115.391 }
+ , PathCubic { control1X: 56.044; control1Y: -116.185; control2X: 52.994; control2Y: -116.07; x: 49.978; y: -117.346 }
+ , PathCubic { control1X: 49.911; control1Y: -117.374; control2X: 49.688; control2Y: -117.027; x: 49.624; y: -117.054 }
+ , PathCubic { control1X: 48.258; control1Y: -117.648; control2X: 47.34; control2Y: -118.614; x: 46.264; y: -119.66 }
+ , PathCubic { control1X: 45.351; control1Y: -120.548; control2X: 43.693; control2Y: -120.161; x: 42.419; y: -120.648 }
+ , PathCubic { control1X: 42.095; control1Y: -120.772; control2X: 41.892; control2Y: -121.284; x: 41.591; y: -121.323 }
+ , PathCubic { control1X: 40.372; control1Y: -121.48; control2X: 39.445; control2Y: -122.429; x: 38.4; y: -123 }
+ , PathCubic { control1X: 40.736; control1Y: -123.795; control2X: 43.147; control2Y: -123.764; x: 45.609; y: -124.148 }
+ , PathCubic { control1X: 45.722; control1Y: -124.166; control2X: 45.867; control2Y: -123.845; x: 46; y: -123.845 }
+ , PathCubic { control1X: 46.136; control1Y: -123.845; control2X: 46.266; control2Y: -124.066; x: 46.4; y: -124.2 }
+ , PathCubic { control1X: 46.595; control1Y: -123.92; control2X: 46.897; control2Y: -123.594; x: 47.154; y: -123.848 }
+ , PathCubic { control1X: 47.702; control1Y: -124.388; control2X: 48.258; control2Y: -124.198; x: 48.798; y: -124.158 }
+ , PathCubic { control1X: 48.942; control1Y: -124.148; control2X: 49.067; control2Y: -123.845; x: 49.2; y: -123.845 }
+ , PathCubic { control1X: 49.336; control1Y: -123.845; control2X: 49.467; control2Y: -124.156; x: 49.6; y: -124.156 }
+ , PathCubic { control1X: 49.736; control1Y: -124.155; control2X: 49.867; control2Y: -123.845; x: 50; y: -123.845 }
+ , PathCubic { control1X: 50.136; control1Y: -123.845; control2X: 50.266; control2Y: -124.066; x: 50.4; y: -124.2 }
+ , PathCubic { control1X: 51.092; control1Y: -123.418; control2X: 51.977; control2Y: -123.972; x: 52.799; y: -123.793 }
+ , PathCubic { control1X: 53.837; control1Y: -123.566; control2X: 54.104; control2Y: -122.418; x: 55.178; y: -122.12 }
+ , PathCubic { control1X: 59.893; control1Y: -120.816; control2X: 64.03; control2Y: -118.671; x: 68.393; y: -116.584 }
+ , PathCubic { control1X: 68.7; control1Y: -116.437; control2X: 68.91; control2Y: -116.189; x: 68.8; y: -115.8 }
+ , PathCubic { control1X: 69.067; control1Y: -115.8; control2X: 69.38; control2Y: -115.888; x: 69.57; y: -115.756 }
+ , PathCubic { control1X: 70.628; control1Y: -115.024; control2X: 71.669; control2Y: -114.476; x: 72.366; y: -113.378 }
+ , PathCubic { control1X: 72.582; control1Y: -113.039; control2X: 72.253; control2Y: -112.632; x: 72.02; y: -112.684 }
+ , PathCubic { control1X: 67.591; control1Y: -113.679; control2X: 63.585; control2Y: -114.287; x: 59.198; y: -115.391 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: 45.338; y: -71.179 }
+ , PathCubic { control1X: 43.746; control1Y: -72.398; control2X: 43.162; control2Y: -74.429; x: 42.034; y: -76.221 }
+ , PathCubic { control1X: 41.82; control1Y: -76.561; control2X: 42.094; control2Y: -76.875; x: 42.411; y: -76.964 }
+ , PathCubic { control1X: 42.971; control1Y: -77.123; control2X: 43.514; control2Y: -76.645; x: 43.923; y: -76.443 }
+ , PathCubic { control1X: 45.668; control1Y: -75.581; control2X: 47.203; control2Y: -74.339; x: 49.2; y: -74.2 }
+ , PathCubic { control1X: 51.19; control1Y: -71.966; control2X: 55.45; control2Y: -71.581; x: 55.457; y: -68.2 }
+ , PathCubic { control1X: 55.458; control1Y: -67.341; control2X: 54.03; control2Y: -68.259; x: 53.6; y: -67.4 }
+ , PathCubic { control1X: 51.149; control1Y: -68.403; control2X: 48.76; control2Y: -68.3; x: 46.38; y: -69.767 }
+ , PathCubic { control1X: 45.763; control1Y: -70.148; control2X: 46.093; control2Y: -70.601; x: 45.338; y: -71.179 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cc7226"
+ strokeWidth: -1
+ delegate: [PathMove { x: 17.8; y: -123.756 }
+ , PathCubic { control1X: 17.935; control1Y: -123.755; control2X: 24.966; control2Y: -123.522; x: 24.949; y: -123.408 }
+ , PathCubic { control1X: 24.904; control1Y: -123.099; control2X: 17.174; control2Y: -122.05; x: 16.81; y: -122.22 }
+ , PathCubic { control1X: 16.646; control1Y: -122.296; control2X: 9.134; control2Y: -119.866; x: 9; y: -120 }
+ , PathCubic { control1X: 9.268; control1Y: -120.135; control2X: 17.534; control2Y: -123.756; x: 17.8; y: -123.756 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 33.2; y: -114 }
+ , PathCubic { control1X: 33.2; control1Y: -114; control2X: 18.4; control2Y: -112.2; x: 14; y: -111 }
+ , PathCubic { control1X: 9.6; control1Y: -109.8; control2X: -9; control2Y: -102.2; x: -12; y: -100.2 }
+ , PathCubic { control1X: -12; control1Y: -100.2; control2X: -25.4; control2Y: -94.8; x: -42.4; y: -74.8 }
+ , PathCubic { control1X: -42.4; control1Y: -74.8; control2X: -34.8; control2Y: -78.2; x: -32.6; y: -81 }
+ , PathCubic { control1X: -32.6; control1Y: -81; control2X: -19; control2Y: -93.6; x: -19.2; y: -91 }
+ , PathCubic { control1X: -19.2; control1Y: -91; control2X: -7; control2Y: -99.6; x: -7.6; y: -97.4 }
+ , PathCubic { control1X: -7.6; control1Y: -97.4; control2X: 16.8; control2Y: -108.6; x: 14.8; y: -105.4 }
+ , PathCubic { control1X: 14.8; control1Y: -105.4; control2X: 36.4; control2Y: -110; x: 35.4; y: -108 }
+ , PathCubic { control1X: 35.4; control1Y: -108; control2X: 54.2; control2Y: -103.6; x: 51.4; y: -103.4 }
+ , PathCubic { control1X: 51.4; control1Y: -103.4; control2X: 45.6; control2Y: -102.2; x: 52; y: -98.6 }
+ , PathCubic { control1X: 52; control1Y: -98.6; control2X: 48.6; control2Y: -94.2; x: 43.2; y: -98.2 }
+ , PathCubic { control1X: 37.8; control1Y: -102.2; control2X: 40.8; control2Y: -100; x: 35.8; y: -99 }
+ , PathCubic { control1X: 35.8; control1Y: -99; control2X: 33.2; control2Y: -98.2; x: 28.6; y: -102.2 }
+ , PathCubic { control1X: 28.6; control1Y: -102.2; control2X: 23; control2Y: -106.8; x: 14.2; y: -103.2 }
+ , PathCubic { control1X: 14.2; control1Y: -103.2; control2X: -16.4; control2Y: -90.6; x: -18.4; y: -90 }
+ , PathCubic { control1X: -18.4; control1Y: -90; control2X: -22; control2Y: -87.2; x: -24.4; y: -83.6 }
+ , PathCubic { control1X: -24.4; control1Y: -83.6; control2X: -30.2; control2Y: -79.2; x: -33.2; y: -77.8 }
+ , PathCubic { control1X: -33.2; control1Y: -77.8; control2X: -46; control2Y: -66.2; x: -47.2; y: -64.8 }
+ , PathCubic { control1X: -47.2; control1Y: -64.8; control2X: -50.6; control2Y: -59.6; x: -51.4; y: -59.2 }
+ , PathCubic { control1X: -51.4; control1Y: -59.2; control2X: -45; control2Y: -63; x: -43; y: -65 }
+ , PathCubic { control1X: -43; control1Y: -65; control2X: -29; control2Y: -75; x: -23.6; y: -75.8 }
+ , PathCubic { control1X: -23.6; control1Y: -75.8; control2X: -19.2; control2Y: -78.8; x: -18.4; y: -80.2 }
+ , PathCubic { control1X: -18.4; control1Y: -80.2; control2X: -4; control2Y: -89.4; x: 0.2; y: -89.4 }
+ , PathCubic { control1X: 0.2; control1Y: -89.4; control2X: 9.4; control2Y: -84.2; x: 11.8; y: -91.2 }
+ , PathCubic { control1X: 11.8; control1Y: -91.2; control2X: 17.6; control2Y: -93; x: 23.2; y: -91.8 }
+ , PathCubic { control1X: 23.2; control1Y: -91.8; control2X: 26.4; control2Y: -94.4; x: 25.6; y: -96.6 }
+ , PathCubic { control1X: 25.6; control1Y: -96.6; control2X: 27.2; control2Y: -98.4; x: 28.2; y: -94.6 }
+ , PathCubic { control1X: 28.2; control1Y: -94.6; control2X: 31.6; control2Y: -91; x: 36.4; y: -93 }
+ , PathCubic { control1X: 36.4; control1Y: -93; control2X: 40.4; control2Y: -93.2; x: 38.4; y: -90.8 }
+ , PathCubic { control1X: 38.4; control1Y: -90.8; control2X: 34; control2Y: -87; x: 22.2; y: -86.8 }
+ , PathCubic { control1X: 22.2; control1Y: -86.8; control2X: 9.8; control2Y: -86.2; x: -6.6; y: -78.6 }
+ , PathCubic { control1X: -6.6; control1Y: -78.6; control2X: -36.4; control2Y: -68.2; x: -45.6; y: -57.8 }
+ , PathCubic { control1X: -45.6; control1Y: -57.8; control2X: -52; control2Y: -49; x: -57.4; y: -47.8 }
+ , PathCubic { control1X: -57.4; control1Y: -47.8; control2X: -63.2; control2Y: -47; x: -69.2; y: -39.6 }
+ , PathCubic { control1X: -69.2; control1Y: -39.6; control2X: -59.4; control2Y: -45.4; x: -50.4; y: -45.4 }
+ , PathCubic { control1X: -50.4; control1Y: -45.4; control2X: -46.4; control2Y: -47.8; x: -50.2; y: -44.2 }
+ , PathCubic { control1X: -50.2; control1Y: -44.2; control2X: -53.8; control2Y: -36.6; x: -52.2; y: -31.2 }
+ , PathCubic { control1X: -52.2; control1Y: -31.2; control2X: -52.8; control2Y: -26; x: -53.6; y: -24.4 }
+ , PathCubic { control1X: -53.6; control1Y: -24.4; control2X: -61.4; control2Y: -11.6; x: -61.4; y: -9.2 }
+ , PathCubic { control1X: -61.4; control1Y: -6.8; control2X: -60.2; control2Y: 3; x: -59.8; y: 3.6 }
+ , PathCubic { control1X: -59.4; control1Y: 4.2; control2X: -60.8; control2Y: 2; x: -57; y: 4.4 }
+ , PathCubic { control1X: -53.2; control1Y: 6.8; control2X: -50.4; control2Y: 8.4; x: -49.6; y: 11.2 }
+ , PathCubic { control1X: -48.8; control1Y: 14; control2X: -51.6; control2Y: 5.8; x: -51.8; y: 4 }
+ , PathCubic { control1X: -52; control1Y: 2.2; control2X: -56.2; control2Y: -5; x: -55.4; y: -7.4 }
+ , PathCubic { control1X: -55.4; control1Y: -7.4; control2X: -54.4; control2Y: -6.4; x: -53.6; y: -5 }
+ , PathCubic { control1X: -53.6; control1Y: -5; control2X: -54.2; control2Y: -5.6; x: -53.6; y: -9.2 }
+ , PathCubic { control1X: -53.6; control1Y: -9.2; control2X: -52.8; control2Y: -14.4; x: -51.4; y: -17.6 }
+ , PathCubic { control1X: -50; control1Y: -20.8; control2X: -48; control2Y: -24.6; x: -47.6; y: -25.4 }
+ , PathCubic { control1X: -47.2; control1Y: -26.2; control2X: -47.2; control2Y: -32; x: -45.8; y: -29.4 }
+ , PathLine { x: -42.4; y: -26.8 }
+ , PathCubic { control1X: -42.4; control1Y: -26.8; control2X: -45.2; control2Y: -29.4; x: -43; y: -31.6 }
+ , PathCubic { control1X: -43; control1Y: -31.6; control2X: -44; control2Y: -37.2; x: -42.2; y: -39.8 }
+ , PathCubic { control1X: -42.2; control1Y: -39.8; control2X: -35.2; control2Y: -48.2; x: -33.6; y: -49.2 }
+ , PathCubic { control1X: -32; control1Y: -50.2; control2X: -33.4; control2Y: -49.8; x: -33.4; y: -49.8 }
+ , PathCubic { control1X: -33.4; control1Y: -49.8; control2X: -27.4; control2Y: -54; x: -33.2; y: -52.4 }
+ , PathCubic { control1X: -33.2; control1Y: -52.4; control2X: -37.2; control2Y: -50.8; x: -40.2; y: -50.8 }
+ , PathCubic { control1X: -40.2; control1Y: -50.8; control2X: -47.8; control2Y: -48.8; x: -43.8; y: -53 }
+ , PathCubic { control1X: -39.8; control1Y: -57.2; control2X: -29.8; control2Y: -62.6; x: -26; y: -62.4 }
+ , PathLine { x: -25.2; y: -60.8 }
+ , PathLine { x: -14; y: -63.2 }
+ , PathLine { x: -15.2; y: -62.4 }
+ , PathCubic { control1X: -15.2; control1Y: -62.4; control2X: -15.4; control2Y: -62.6; x: -11.2; y: -63 }
+ , PathCubic { control1X: -7; control1Y: -63.4; control2X: -1.2; control2Y: -62; x: 0.2; y: -63.8 }
+ , PathCubic { control1X: 1.6; control1Y: -65.6; control2X: 5; control2Y: -66.6; x: 4.6; y: -65.2 }
+ , PathCubic { control1X: 4.2; control1Y: -63.8; control2X: 4; control2Y: -61.8; x: 4; y: -61.8 }
+ , PathCubic { control1X: 4; control1Y: -61.8; control2X: 9; control2Y: -67.6; x: 8.4; y: -65.4 }
+ , PathCubic { control1X: 7.8; control1Y: -63.2; control2X: -0.4; control2Y: -58; x: -1.8; y: -51.8 }
+ , PathLine { x: 8.6; y: -60 }
+ , PathLine { x: 12.2; y: -63 }
+ , PathCubic { control1X: 12.2; control1Y: -63; control2X: 15.8; control2Y: -60.8; x: 16; y: -62.4 }
+ , PathCubic { control1X: 16.2; control1Y: -64; control2X: 20.8; control2Y: -69.8; x: 22; y: -69.6 }
+ , PathCubic { control1X: 23.2; control1Y: -69.4; control2X: 25.2; control2Y: -72.2; x: 25; y: -69.6 }
+ , PathCubic { control1X: 24.8; control1Y: -67; control2X: 32.4; control2Y: -61.6; x: 32.4; y: -61.6 }
+ , PathCubic { control1X: 32.4; control1Y: -61.6; control2X: 35.6; control2Y: -63.4; x: 37; y: -62 }
+ , PathCubic { control1X: 38.4; control1Y: -60.6; control2X: 42.6; control2Y: -81.8; x: 42.6; y: -81.8 }
+ , PathLine { x: 67.6; y: -92.4 }
+ , PathLine { x: 111.201; y: -95.8 }
+ , PathLine { x: 94.201; y: -102.6 }
+ , PathLine { x: 33.2; y: -114 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ delegate: [PathMove { x: 51.4; y: 85 }
+ , PathCubic { control1X: 51.4; control1Y: 85; control2X: 36.4; control2Y: 68.2; x: 28; y: 65.6 }
+ , PathCubic { control1X: 28; control1Y: 65.6; control2X: 14.6; control2Y: 58.8; x: -10; y: 66.6 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ delegate: [PathMove { x: 24.8; y: 64.2 }
+ , PathCubic { control1X: 24.8; control1Y: 64.2; control2X: -0.4; control2Y: 56.2; x: -15.8; y: 60.4 }
+ , PathCubic { control1X: -15.8; control1Y: 60.4; control2X: -34.2; control2Y: 62.4; x: -42.6; y: 76.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ delegate: [PathMove { x: 21.2; y: 63 }
+ , PathCubic { control1X: 21.2; control1Y: 63; control2X: 4.2; control2Y: 55.8; x: -10.6; y: 53.6 }
+ , PathCubic { control1X: -10.6; control1Y: 53.6; control2X: -27.2; control2Y: 51; x: -43.8; y: 58.2 }
+ , PathCubic { control1X: -43.8; control1Y: 58.2; control2X: -56; control2Y: 64.2; x: -61.4; y: 74.4 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#4c0000"
+ strokeWidth: 2
+ delegate: [PathMove { x: 22.2; y: 63.4 }
+ , PathCubic { control1X: 22.2; control1Y: 63.4; control2X: 6.8; control2Y: 52.4; x: 5.8; y: 51 }
+ , PathCubic { control1X: 5.8; control1Y: 51; control2X: -1.2; control2Y: 40; x: -14.2; y: 39.6 }
+ , PathCubic { control1X: -14.2; control1Y: 39.6; control2X: -35.6; control2Y: 40.4; x: -52.8; y: 48.4 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 20.895; y: 54.407 }
+ , PathCubic { control1X: 22.437; control1Y: 55.87; control2X: 49.4; control2Y: 84.8; x: 49.4; y: 84.8 }
+ , PathCubic { control1X: 84.6; control1Y: 121.401; control2X: 56.6; control2Y: 87.2; x: 56.6; y: 87.2 }
+ , PathCubic { control1X: 49; control1Y: 82.4; control2X: 39.8; control2Y: 63.6; x: 39.8; y: 63.6 }
+ , PathCubic { control1X: 38.6; control1Y: 60.8; control2X: 53.8; control2Y: 70.8; x: 53.8; y: 70.8 }
+ , PathCubic { control1X: 57.8; control1Y: 71.6; control2X: 71.4; control2Y: 90.8; x: 71.4; y: 90.8 }
+ , PathCubic { control1X: 64.6; control1Y: 88.4; control2X: 69.4; control2Y: 95.6; x: 69.4; y: 95.6 }
+ , PathCubic { control1X: 72.2; control1Y: 97.6; control2X: 92.601; control2Y: 113.201; x: 92.601; y: 113.201 }
+ , PathCubic { control1X: 96.201; control1Y: 117.201; control2X: 100.201; control2Y: 118.801; x: 100.201; y: 118.801 }
+ , PathCubic { control1X: 114.201; control1Y: 113.601; control2X: 107.801; control2Y: 126.801; x: 107.801; y: 126.801 }
+ , PathCubic { control1X: 110.201; control1Y: 133.601; control2X: 115.801; control2Y: 122.001; x: 115.801; y: 122.001 }
+ , PathCubic { control1X: 127.001; control1Y: 105.2; control2X: 110.601; control2Y: 107.601; x: 110.601; y: 107.601 }
+ , PathCubic { control1X: 80.6; control1Y: 110.401; control2X: 73.8; control2Y: 94.4; x: 73.8; y: 94.4 }
+ , PathCubic { control1X: 71.4; control1Y: 92; control2X: 80.2; control2Y: 94.4; x: 80.2; y: 94.4 }
+ , PathCubic { control1X: 88.601; control1Y: 96.4; control2X: 73; control2Y: 82; x: 73; y: 82 }
+ , PathCubic { control1X: 75.4; control1Y: 82; control2X: 84.6; control2Y: 88.8; x: 84.6; y: 88.8 }
+ , PathCubic { control1X: 95.001; control1Y: 98; control2X: 97.001; control2Y: 96; x: 97.001; y: 96 }
+ , PathCubic { control1X: 115.001; control1Y: 87.2; control2X: 125.401; control2Y: 94.8; x: 125.401; y: 94.8 }
+ , PathCubic { control1X: 127.401; control1Y: 96.4; control2X: 121.801; control2Y: 103.2; x: 123.401; y: 108.401 }
+ , PathCubic { control1X: 125.001; control1Y: 113.601; control2X: 129.801; control2Y: 126.001; x: 129.801; y: 126.001 }
+ , PathCubic { control1X: 127.401; control1Y: 127.601; control2X: 127.801; control2Y: 138.401; x: 127.801; y: 138.401 }
+ , PathCubic { control1X: 144.601; control1Y: 161.601; control2X: 135.001; control2Y: 159.601; x: 135.001; y: 159.601 }
+ , PathCubic { control1X: 119.401; control1Y: 159.201; control2X: 134.201; control2Y: 166.801; x: 134.201; y: 166.801 }
+ , PathCubic { control1X: 137.401; control1Y: 168.801; control2X: 146.201; control2Y: 176.001; x: 146.201; y: 176.001 }
+ , PathCubic { control1X: 143.401; control1Y: 174.801; control2X: 141.801; control2Y: 180.001; x: 141.801; y: 180.001 }
+ , PathCubic { control1X: 146.601; control1Y: 184.001; control2X: 143.801; control2Y: 188.801; x: 143.801; y: 188.801 }
+ , PathCubic { control1X: 137.801; control1Y: 190.001; control2X: 136.601; control2Y: 194.001; x: 136.601; y: 194.001 }
+ , PathCubic { control1X: 143.401; control1Y: 202.001; control2X: 133.401; control2Y: 202.401; x: 133.401; y: 202.401 }
+ , PathCubic { control1X: 137.001; control1Y: 206.801; control2X: 132.201; control2Y: 218.801; x: 132.201; y: 218.801 }
+ , PathCubic { control1X: 127.401; control1Y: 218.801; control2X: 121.001; control2Y: 224.401; x: 121.001; y: 224.401 }
+ , PathCubic { control1X: 123.401; control1Y: 229.201; control2X: 113.001; control2Y: 234.801; x: 113.001; y: 234.801 }
+ , PathCubic { control1X: 104.601; control1Y: 236.401; control2X: 107.401; control2Y: 243.201; x: 107.401; y: 243.201 }
+ , PathCubic { control1X: 99.401; control1Y: 249.201; control2X: 97.001; control2Y: 265.201; x: 97.001; y: 265.201 }
+ , PathCubic { control1X: 96.201; control1Y: 275.601; control2X: 93.801; control2Y: 278.801; x: 99.001; y: 276.801 }
+ , PathCubic { control1X: 104.201; control1Y: 274.801; control2X: 103.401; control2Y: 262.401; x: 103.401; y: 262.401 }
+ , PathCubic { control1X: 98.601; control1Y: 246.801; control2X: 141.401; control2Y: 230.801; x: 141.401; y: 230.801 }
+ , PathCubic { control1X: 145.401; control1Y: 229.201; control2X: 146.201; control2Y: 224.001; x: 146.201; y: 224.001 }
+ , PathCubic { control1X: 148.201; control1Y: 224.401; control2X: 157.001; control2Y: 232.001; x: 157.001; y: 232.001 }
+ , PathCubic { control1X: 164.601; control1Y: 243.201; control2X: 165.001; control2Y: 234.001; x: 165.001; y: 234.001 }
+ , PathCubic { control1X: 166.201; control1Y: 230.401; control2X: 164.601; control2Y: 224.401; x: 164.601; y: 224.401 }
+ , PathCubic { control1X: 170.601; control1Y: 202.801; control2X: 156.601; control2Y: 196.401; x: 156.601; y: 196.401 }
+ , PathCubic { control1X: 146.601; control1Y: 162.801; control2X: 160.601; control2Y: 171.201; x: 160.601; y: 171.201 }
+ , PathCubic { control1X: 163.401; control1Y: 176.801; control2X: 174.201; control2Y: 182.001; x: 174.201; y: 182.001 }
+ , PathLine { x: 177.801; y: 179.601 }
+ , PathCubic { control1X: 176.201; control1Y: 174.801; control2X: 184.601; control2Y: 168.801; x: 184.601; y: 168.801 }
+ , PathCubic { control1X: 187.401; control1Y: 175.201; control2X: 193.401; control2Y: 167.201; x: 193.401; y: 167.201 }
+ , PathCubic { control1X: 197.001; control1Y: 142.801; control2X: 209.401; control2Y: 157.201; x: 209.401; y: 157.201 }
+ , PathCubic { control1X: 213.401; control1Y: 158.401; control2X: 214.601; control2Y: 151.601; x: 214.601; y: 151.601 }
+ , PathCubic { control1X: 218.201; control1Y: 141.201; control2X: 214.601; control2Y: 127.601; x: 214.601; y: 127.601 }
+ , PathCubic { control1X: 218.201; control1Y: 127.201; control2X: 227.801; control2Y: 133.201; x: 227.801; y: 133.201 }
+ , PathCubic { control1X: 230.601; control1Y: 129.601; control2X: 221.401; control2Y: 112.801; x: 225.401; y: 115.201 }
+ , PathCubic { control1X: 229.401; control1Y: 117.601; control2X: 233.801; control2Y: 119.201; x: 233.801; y: 119.201 }
+ , PathCubic { control1X: 234.601; control1Y: 117.201; control2X: 224.601; control2Y: 104.801; x: 224.601; y: 104.801 }
+ , PathCubic { control1X: 220.201; control1Y: 102; control2X: 215.001; control2Y: 81.6; x: 215.001; y: 81.6 }
+ , PathCubic { control1X: 222.201; control1Y: 85.2; control2X: 212.201; control2Y: 70; x: 212.201; y: 70 }
+ , PathCubic { control1X: 212.201; control1Y: 66.8; control2X: 218.201; control2Y: 55.6; x: 218.201; y: 55.6 }
+ , PathCubic { control1X: 217.401; control1Y: 48.8; control2X: 218.201; control2Y: 49.2; x: 218.201; y: 49.2 }
+ , PathCubic { control1X: 221.001; control1Y: 50.4; control2X: 229.001; control2Y: 52; x: 222.201; y: 45.6 }
+ , PathCubic { control1X: 215.401; control1Y: 39.2; control2X: 223.001; control2Y: 34.4; x: 223.001; y: 34.4 }
+ , PathCubic { control1X: 227.401; control1Y: 31.6; control2X: 213.801; control2Y: 32; x: 213.801; y: 32 }
+ , PathCubic { control1X: 208.601; control1Y: 27.6; control2X: 209.001; control2Y: 23.6; x: 209.001; y: 23.6 }
+ , PathCubic { control1X: 217.001; control1Y: 25.6; control2X: 202.601; control2Y: 11.2; x: 200.201; y: 7.6 }
+ , PathCubic { control1X: 197.801; control1Y: 4; control2X: 207.401; control2Y: -1.2; x: 207.401; y: -1.2 }
+ , PathCubic { control1X: 220.601; control1Y: -4.8; control2X: 209.001; control2Y: -8; x: 209.001; y: -8 }
+ , PathCubic { control1X: 189.401; control1Y: -7.6; control2X: 200.201; control2Y: -18.4; x: 200.201; y: -18.4 }
+ , PathCubic { control1X: 206.201; control1Y: -18; control2X: 204.601; control2Y: -20.4; x: 204.601; y: -20.4 }
+ , PathCubic { control1X: 199.401; control1Y: -21.6; control2X: 189.801; control2Y: -28; x: 189.801; y: -28 }
+ , PathCubic { control1X: 185.801; control1Y: -31.6; control2X: 189.401; control2Y: -30.8; x: 189.401; y: -30.8 }
+ , PathCubic { control1X: 206.201; control1Y: -29.6; control2X: 177.401; control2Y: -40.8; x: 177.401; y: -40.8 }
+ , PathCubic { control1X: 185.401; control1Y: -40.8; control2X: 167.401; control2Y: -51.2; x: 167.401; y: -51.2 }
+ , PathCubic { control1X: 165.401; control1Y: -52.8; control2X: 162.201; control2Y: -60.4; x: 162.201; y: -60.4 }
+ , PathCubic { control1X: 156.201; control1Y: -65.6; control2X: 151.401; control2Y: -72.4; x: 151.401; y: -72.4 }
+ , PathCubic { control1X: 151.001; control1Y: -76.8; control2X: 146.201; control2Y: -81.6; x: 146.201; y: -81.6 }
+ , PathCubic { control1X: 134.601; control1Y: -95.2; control2X: 129.001; control2Y: -94.8; x: 129.001; y: -94.8 }
+ , PathCubic { control1X: 114.201; control1Y: -98.4; control2X: 109.001; control2Y: -97.6; x: 109.001; y: -97.6 }
+ , PathLine { x: 56.2; y: -93.2 }
+ , PathCubic { control1X: 29.8; control1Y: -80.4; control2X: 37.6; control2Y: -59.4; x: 37.6; y: -59.4 }
+ , PathCubic { control1X: 44; control1Y: -51; control2X: 53.2; control2Y: -54.8; x: 53.2; y: -54.8 }
+ , PathCubic { control1X: 57.8; control1Y: -61; control2X: 69.4; control2Y: -58.8; x: 69.4; y: -58.8 }
+ , PathCubic { control1X: 89.801; control1Y: -55.6; control2X: 87.201; control2Y: -59.2; x: 87.201; y: -59.2 }
+ , PathCubic { control1X: 84.801; control1Y: -63.8; control2X: 68.6; control2Y: -70; x: 68.4; y: -70.6 }
+ , PathCubic { control1X: 68.2; control1Y: -71.2; control2X: 59.4; control2Y: -74.6; x: 59.4; y: -74.6 }
+ , PathCubic { control1X: 56.4; control1Y: -75.8; control2X: 52; control2Y: -85; x: 52; y: -85 }
+ , PathCubic { control1X: 48.8; control1Y: -88.4; control2X: 64.6; control2Y: -82.6; x: 64.6; y: -82.6 }
+ , PathCubic { control1X: 63.4; control1Y: -81.6; control2X: 70.8; control2Y: -77.6; x: 70.8; y: -77.6 }
+ , PathCubic { control1X: 88.201; control1Y: -78.6; control2X: 98.801; control2Y: -67.8; x: 98.801; y: -67.8 }
+ , PathCubic { control1X: 109.601; control1Y: -51.2; control2X: 109.801; control2Y: -59.4; x: 109.801; y: -59.4 }
+ , PathCubic { control1X: 112.601; control1Y: -68.8; control2X: 100.801; control2Y: -90; x: 100.801; y: -90 }
+ , PathCubic { control1X: 101.201; control1Y: -92; control2X: 109.401; control2Y: -85.4; x: 109.401; y: -85.4 }
+ , PathCubic { control1X: 110.801; control1Y: -87.4; control2X: 111.601; control2Y: -81.6; x: 111.601; y: -81.6 }
+ , PathCubic { control1X: 111.801; control1Y: -79.2; control2X: 115.601; control2Y: -71.2; x: 115.601; y: -71.2 }
+ , PathCubic { control1X: 118.401; control1Y: -58.2; control2X: 122.001; control2Y: -65.6; x: 122.001; y: -65.6 }
+ , PathLine { x: 126.601; y: -56.2 }
+ , PathCubic { control1X: 128.001; control1Y: -53.6; control2X: 122.001; control2Y: -46; x: 122.001; y: -46 }
+ , PathCubic { control1X: 121.801; control1Y: -43.2; control2X: 122.601; control2Y: -43.4; x: 117.001; y: -35.8 }
+ , PathCubic { control1X: 111.401; control1Y: -28.2; control2X: 114.801; control2Y: -23.8; x: 114.801; y: -23.8 }
+ , PathCubic { control1X: 113.401; control1Y: -17.2; control2X: 122.201; control2Y: -17.6; x: 122.201; y: -17.6 }
+ , PathCubic { control1X: 124.801; control1Y: -15.4; control2X: 128.201; control2Y: -15.4; x: 128.201; y: -15.4 }
+ , PathCubic { control1X: 130.001; control1Y: -13.4; control2X: 132.401; control2Y: -14; x: 132.401; y: -14 }
+ , PathCubic { control1X: 134.001; control1Y: -17.8; control2X: 140.201; control2Y: -15.8; x: 140.201; y: -15.8 }
+ , PathCubic { control1X: 141.601; control1Y: -18.2; control2X: 149.801; control2Y: -18.6; x: 149.801; y: -18.6 }
+ , PathCubic { control1X: 150.801; control1Y: -21.2; control2X: 151.201; control2Y: -22.8; x: 154.601; y: -23.4 }
+ , PathCubic { control1X: 158.001; control1Y: -24; control2X: 133.401; control2Y: -67; x: 133.401; y: -67 }
+ , PathCubic { control1X: 139.801; control1Y: -67.8; control2X: 131.601; control2Y: -80.2; x: 131.601; y: -80.2 }
+ , PathCubic { control1X: 129.401; control1Y: -86.8; control2X: 140.801; control2Y: -72.2; x: 143.001; y: -70.8 }
+ , PathCubic { control1X: 145.201; control1Y: -69.4; control2X: 146.201; control2Y: -67.2; x: 144.601; y: -67.4 }
+ , PathCubic { control1X: 143.001; control1Y: -67.6; control2X: 141.201; control2Y: -65.4; x: 142.601; y: -65.2 }
+ , PathCubic { control1X: 144.001; control1Y: -65; control2X: 157.001; control2Y: -50; x: 160.401; y: -39.8 }
+ , PathCubic { control1X: 163.801; control1Y: -29.6; control2X: 169.801; control2Y: -25.6; x: 176.001; y: -19.6 }
+ , PathCubic { control1X: 182.201; control1Y: -13.6; control2X: 181.401; control2Y: 10.6; x: 181.401; y: 10.6 }
+ , PathCubic { control1X: 181.001; control1Y: 19.4; control2X: 187.001; control2Y: 30; x: 187.001; y: 30 }
+ , PathCubic { control1X: 189.001; control1Y: 33.8; control2X: 184.801; control2Y: 52; x: 184.801; y: 52 }
+ , PathCubic { control1X: 182.801; control1Y: 54.2; control2X: 184.201; control2Y: 55; x: 184.201; y: 55 }
+ , PathCubic { control1X: 185.201; control1Y: 56.2; control2X: 192.001; control2Y: 69.4; x: 192.001; y: 69.4 }
+ , PathCubic { control1X: 190.201; control1Y: 69.2; control2X: 193.801; control2Y: 72.8; x: 193.801; y: 72.8 }
+ , PathCubic { control1X: 199.001; control1Y: 78.8; control2X: 192.601; control2Y: 75.8; x: 192.601; y: 75.8 }
+ , PathCubic { control1X: 186.601; control1Y: 74.2; control2X: 193.601; control2Y: 84; x: 193.601; y: 84 }
+ , PathCubic { control1X: 194.801; control1Y: 85.8; control2X: 185.801; control2Y: 81.2; x: 185.801; y: 81.2 }
+ , PathCubic { control1X: 176.601; control1Y: 80.6; control2X: 188.201; control2Y: 87.8; x: 188.201; y: 87.8 }
+ , PathCubic { control1X: 196.801; control1Y: 95; control2X: 185.401; control2Y: 90.6; x: 185.401; y: 90.6 }
+ , PathCubic { control1X: 180.801; control1Y: 88.8; control2X: 184.001; control2Y: 95.6; x: 184.001; y: 95.6 }
+ , PathCubic { control1X: 187.201; control1Y: 97.2; control2X: 204.401; control2Y: 104.2; x: 204.401; y: 104.2 }
+ , PathCubic { control1X: 204.801; control1Y: 108.001; control2X: 201.801; control2Y: 113.001; x: 201.801; y: 113.001 }
+ , PathCubic { control1X: 202.201; control1Y: 117.001; control2X: 200.001; control2Y: 120.401; x: 200.001; y: 120.401 }
+ , PathCubic { control1X: 198.801; control1Y: 128.601; control2X: 198.201; control2Y: 129.401; x: 198.201; y: 129.401 }
+ , PathCubic { control1X: 194.001; control1Y: 129.601; control2X: 186.601; control2Y: 143.401; x: 186.601; y: 143.401 }
+ , PathCubic { control1X: 184.801; control1Y: 146.001; control2X: 174.601; control2Y: 158.001; x: 174.601; y: 158.001 }
+ , PathCubic { control1X: 172.601; control1Y: 165.001; control2X: 154.601; control2Y: 157.801; x: 154.601; y: 157.801 }
+ , PathCubic { control1X: 148.001; control1Y: 161.201; control2X: 150.001; control2Y: 157.801; x: 150.001; y: 157.801 }
+ , PathCubic { control1X: 149.601; control1Y: 155.601; control2X: 154.401; control2Y: 149.601; x: 154.401; y: 149.601 }
+ , PathCubic { control1X: 161.401; control1Y: 147.001; control2X: 158.801; control2Y: 136.201; x: 158.801; y: 136.201 }
+ , PathCubic { control1X: 162.801; control1Y: 134.801; control2X: 151.601; control2Y: 132.001; x: 151.801; y: 130.801 }
+ , PathCubic { control1X: 152.001; control1Y: 129.601; control2X: 157.801; control2Y: 128.201; x: 157.801; y: 128.201 }
+ , PathCubic { control1X: 165.801; control1Y: 126.201; control2X: 161.401; control2Y: 123.801; x: 161.401; y: 123.801 }
+ , PathCubic { control1X: 160.801; control1Y: 119.801; control2X: 163.801; control2Y: 114.201; x: 163.801; y: 114.201 }
+ , PathCubic { control1X: 175.401; control1Y: 113.401; control2X: 163.801; control2Y: 97.2; x: 163.801; y: 97.2 }
+ , PathCubic { control1X: 153.001; control1Y: 89.6; control2X: 152.001; control2Y: 83.8; x: 152.001; y: 83.8 }
+ , PathCubic { control1X: 164.601; control1Y: 75.6; control2X: 156.401; control2Y: 63.2; x: 156.601; y: 59.6 }
+ , PathCubic { control1X: 156.801; control1Y: 56; control2X: 158.001; control2Y: 34.4; x: 158.001; y: 34.4 }
+ , PathCubic { control1X: 156.001; control1Y: 28.2; control2X: 153.001; control2Y: 14.6; x: 153.001; y: 14.6 }
+ , PathCubic { control1X: 155.201; control1Y: 9.4; control2X: 162.601; control2Y: -3.2; x: 162.601; y: -3.2 }
+ , PathCubic { control1X: 165.401; control1Y: -7.4; control2X: 174.201; control2Y: -12.2; x: 172.001; y: -15.2 }
+ , PathCubic { control1X: 169.801; control1Y: -18.2; control2X: 162.001; control2Y: -16.4; x: 162.001; y: -16.4 }
+ , PathCubic { control1X: 154.201; control1Y: -17.8; control2X: 154.801; control2Y: -12.6; x: 154.801; y: -12.6 }
+ , PathCubic { control1X: 153.201; control1Y: -11.6; control2X: 152.401; control2Y: -6.6; x: 152.401; y: -6.6 }
+ , PathCubic { control1X: 151.68; control1Y: 1.333; control2X: 142.801; control2Y: 7.6; x: 142.801; y: 7.6 }
+ , PathCubic { control1X: 131.601; control1Y: 13.8; control2X: 140.801; control2Y: 17.8; x: 140.801; y: 17.8 }
+ , PathCubic { control1X: 146.801; control1Y: 24.4; control2X: 137.001; control2Y: 24.6; x: 137.001; y: 24.6 }
+ , PathCubic { control1X: 126.001; control1Y: 22.8; control2X: 134.201; control2Y: 33; x: 134.201; y: 33 }
+ , PathCubic { control1X: 145.001; control1Y: 45.8; control2X: 142.001; control2Y: 48.6; x: 142.001; y: 48.6 }
+ , PathCubic { control1X: 131.801; control1Y: 49.6; control2X: 144.401; control2Y: 58.8; x: 144.401; y: 58.8 }
+ , PathCubic { control1X: 144.401; control1Y: 58.8; control2X: 143.601; control2Y: 56.8; x: 143.801; y: 58.6 }
+ , PathCubic { control1X: 144.001; control1Y: 60.4; control2X: 147.001; control2Y: 64.6; x: 147.801; y: 66.6 }
+ , PathCubic { control1X: 148.601; control1Y: 68.6; control2X: 144.601; control2Y: 68.8; x: 144.601; y: 68.8 }
+ , PathCubic { control1X: 145.201; control1Y: 78.4; control2X: 129.801; control2Y: 74.2; x: 129.801; y: 74.2 }
+ , PathCubic { control1X: 129.801; control1Y: 74.2; control2X: 129.801; control2Y: 74.2; x: 128.201; y: 74.4 }
+ , PathCubic { control1X: 126.601; control1Y: 74.6; control2X: 115.401; control2Y: 73.8; x: 109.601; y: 71.6 }
+ , PathCubic { control1X: 103.801; control1Y: 69.4; control2X: 97.001; control2Y: 69.4; x: 97.001; y: 69.4 }
+ , PathCubic { control1X: 97.001; control1Y: 69.4; control2X: 93.001; control2Y: 71.2; x: 85.4; y: 71 }
+ , PathCubic { control1X: 77.8; control1Y: 70.8; control2X: 69.8; control2Y: 73.6; x: 69.8; y: 73.6 }
+ , PathCubic { control1X: 65.4; control1Y: 73.2; control2X: 74; control2Y: 68.8; x: 74.2; y: 69 }
+ , PathCubic { control1X: 74.4; control1Y: 69.2; control2X: 80; control2Y: 63.6; x: 72; y: 64.2 }
+ , PathCubic { control1X: 50.203; control1Y: 65.835; control2X: 39.4; control2Y: 55.6; x: 39.4; y: 55.6 }
+ , PathCubic { control1X: 37.4; control1Y: 54.2; control2X: 34.8; control2Y: 51.4; x: 34.8; y: 51.4 }
+ , PathCubic { control1X: 24.8; control1Y: 49.4; control2X: 36.2; control2Y: 63.8; x: 36.2; y: 63.8 }
+ , PathCubic { control1X: 37.4; control1Y: 65.2; control2X: 36; control2Y: 66.2; x: 36; y: 66.2 }
+ , PathCubic { control1X: 35.2; control1Y: 64.6; control2X: 27.4; control2Y: 59.2; x: 27.4; y: 59.2 }
+ , PathCubic { control1X: 24.589; control1Y: 58.227; control2X: 23.226; control2Y: 56.893; x: 20.895; y: 54.407 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#4c0000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -3; y: 42.8 }
+ , PathCubic { control1X: -3; control1Y: 42.8; control2X: 8.6; control2Y: 48.4; x: 11.2; y: 51.2 }
+ , PathCubic { control1X: 13.8; control1Y: 54; control2X: 27.8; control2Y: 65.4; x: 27.8; y: 65.4 }
+ , PathCubic { control1X: 27.8; control1Y: 65.4; control2X: 22.4; control2Y: 63.4; x: 19.8; y: 61.6 }
+ , PathCubic { control1X: 17.2; control1Y: 59.8; control2X: 6.4; control2Y: 51.6; x: 6.4; y: 51.6 }
+ , PathCubic { control1X: 6.4; control1Y: 51.6; control2X: 2.6; control2Y: 45.6; x: -3; y: 42.8 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#99cc32"
+ strokeWidth: -1
+ delegate: [PathMove { x: -61.009; y: 11.603 }
+ , PathCubic { control1X: -60.672; control1Y: 11.455; control2X: -61.196; control2Y: 8.743; x: -61.4; y: 8.2 }
+ , PathCubic { control1X: -62.422; control1Y: 5.474; control2X: -71.4; control2Y: 4; x: -71.4; y: 4 }
+ , PathCubic { control1X: -71.627; control1Y: 5.365; control2X: -71.682; control2Y: 6.961; x: -71.576; y: 8.599 }
+ , PathCubic { control1X: -71.576; control1Y: 8.599; control2X: -66.708; control2Y: 14.118; x: -61.009; y: 11.603 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#659900"
+ strokeWidth: -1
+ delegate: [PathMove { x: -61.009; y: 11.403 }
+ , PathCubic { control1X: -61.458; control1Y: 11.561; control2X: -61.024; control2Y: 8.669; x: -61.2; y: 8.2 }
+ , PathCubic { control1X: -62.222; control1Y: 5.474; control2X: -71.4; control2Y: 3.9; x: -71.4; y: 3.9 }
+ , PathCubic { control1X: -71.627; control1Y: 5.265; control2X: -71.682; control2Y: 6.861; x: -71.576; y: 8.499 }
+ , PathCubic { control1X: -71.576; control1Y: 8.499; control2X: -67.308; control2Y: 13.618; x: -61.009; y: 11.403 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -65.4; y: 11.546 }
+ , PathCubic { control1X: -66.025; control1Y: 11.546; control2X: -66.531; control2Y: 10.406; x: -66.531; y: 9 }
+ , PathCubic { control1X: -66.531; control1Y: 7.595; control2X: -66.025; control2Y: 6.455; x: -65.4; y: 6.455 }
+ , PathCubic { control1X: -64.775; control1Y: 6.455; control2X: -64.268; control2Y: 7.595; x: -64.268; y: 9 }
+ , PathCubic { control1X: -64.268; control1Y: 10.406; control2X: -64.775; control2Y: 11.546; x: -65.4; y: 11.546 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -65.4; y: 9 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -111; y: 109.601 }
+ , PathCubic { control1X: -111; control1Y: 109.601; control2X: -116.6; control2Y: 119.601; x: -91.8; y: 113.601 }
+ , PathCubic { control1X: -91.8; control1Y: 113.601; control2X: -77.8; control2Y: 112.401; x: -75.4; y: 110.001 }
+ , PathCubic { control1X: -74.2; control1Y: 110.801; control2X: -65.834; control2Y: 113.734; x: -63; y: 114.401 }
+ , PathCubic { control1X: -56.2; control1Y: 116.001; control2X: -47.8; control2Y: 106; x: -47.8; y: 106 }
+ , PathCubic { control1X: -47.8; control1Y: 106; control2X: -43.2; control2Y: 95.5; x: -40.4; y: 95.5 }
+ , PathCubic { control1X: -37.6; control1Y: 95.5; control2X: -40.8; control2Y: 97.1; x: -40.8; y: 97.1 }
+ , PathCubic { control1X: -40.8; control1Y: 97.1; control2X: -47.4; control2Y: 107.201; x: -47; y: 108.801 }
+ , PathCubic { control1X: -47; control1Y: 108.801; control2X: -52.2; control2Y: 128.801; x: -68.2; y: 129.601 }
+ , PathCubic { control1X: -68.2; control1Y: 129.601; control2X: -84.35; control2Y: 130.551; x: -83; y: 136.401 }
+ , PathCubic { control1X: -83; control1Y: 136.401; control2X: -74.2; control2Y: 134.001; x: -71.8; y: 136.401 }
+ , PathCubic { control1X: -71.8; control1Y: 136.401; control2X: -61; control2Y: 136.001; x: -69; y: 142.401 }
+ , PathLine { x: -75.8; y: 154.001 }
+ , PathCubic { control1X: -75.8; control1Y: 154.001; control2X: -75.66; control2Y: 157.919; x: -85.8; y: 154.401 }
+ , PathCubic { control1X: -95.6; control1Y: 151.001; control2X: -105.9; control2Y: 138.101; x: -105.9; y: 138.101 }
+ , PathCubic { control1X: -105.9; control1Y: 138.101; control2X: -121.85; control2Y: 123.551; x: -111; y: 109.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#e59999"
+ strokeWidth: -1
+ delegate: [PathMove { x: -112.2; y: 113.601 }
+ , PathCubic { control1X: -112.2; control1Y: 113.601; control2X: -114.2; control2Y: 123.201; x: -77.4; y: 112.801 }
+ , PathCubic { control1X: -77.4; control1Y: 112.801; control2X: -73; control2Y: 112.801; x: -70.6; y: 113.601 }
+ , PathCubic { control1X: -68.2; control1Y: 114.401; control2X: -56.2; control2Y: 117.201; x: -54.2; y: 116.001 }
+ , PathCubic { control1X: -54.2; control1Y: 116.001; control2X: -61.4; control2Y: 129.601; x: -73; y: 128.001 }
+ , PathCubic { control1X: -73; control1Y: 128.001; control2X: -86.2; control2Y: 129.601; x: -85.8; y: 134.401 }
+ , PathCubic { control1X: -85.8; control1Y: 134.401; control2X: -81.8; control2Y: 141.601; x: -77; y: 144.001 }
+ , PathCubic { control1X: -77; control1Y: 144.001; control2X: -74.2; control2Y: 146.401; x: -74.6; y: 149.601 }
+ , PathCubic { control1X: -75; control1Y: 152.801; control2X: -77.8; control2Y: 154.401; x: -79.8; y: 155.201 }
+ , PathCubic { control1X: -81.8; control1Y: 156.001; control2X: -85; control2Y: 152.801; x: -86.6; y: 152.801 }
+ , PathCubic { control1X: -88.2; control1Y: 152.801; control2X: -96.6; control2Y: 146.401; x: -101; y: 141.601 }
+ , PathCubic { control1X: -105.4; control1Y: 136.801; control2X: -113.8; control2Y: 124.801; x: -113.4; y: 122.001 }
+ , PathCubic { control1X: -113; control1Y: 119.201; control2X: -112.2; control2Y: 113.601; x: -112.2; y: 113.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#b26565"
+ strokeWidth: -1
+ delegate: [PathMove { x: -109; y: 131.051 }
+ , PathCubic { control1X: -106.4; control1Y: 135.001; control2X: -103.2; control2Y: 139.201; x: -101; y: 141.601 }
+ , PathCubic { control1X: -96.6; control1Y: 146.401; control2X: -88.2; control2Y: 152.801; x: -86.6; y: 152.801 }
+ , PathCubic { control1X: -85; control1Y: 152.801; control2X: -81.8; control2Y: 156.001; x: -79.8; y: 155.201 }
+ , PathCubic { control1X: -77.8; control1Y: 154.401; control2X: -75; control2Y: 152.801; x: -74.6; y: 149.601 }
+ , PathCubic { control1X: -74.2; control1Y: 146.401; control2X: -77; control2Y: 144.001; x: -77; y: 144.001 }
+ , PathCubic { control1X: -80.066; control1Y: 142.468; control2X: -82.806; control2Y: 138.976; x: -84.385; y: 136.653 }
+ , PathCubic { control1X: -84.385; control1Y: 136.653; control2X: -84.2; control2Y: 139.201; x: -89.4; y: 138.401 }
+ , PathCubic { control1X: -94.6; control1Y: 137.601; control2X: -99.8; control2Y: 134.801; x: -101.4; y: 131.601 }
+ , PathCubic { control1X: -103; control1Y: 128.401; control2X: -105.4; control2Y: 126.001; x: -103.8; y: 129.601 }
+ , PathCubic { control1X: -102.2; control1Y: 133.201; control2X: -99.8; control2Y: 136.801; x: -98.2; y: 137.201 }
+ , PathCubic { control1X: -96.6; control1Y: 137.601; control2X: -97; control2Y: 138.801; x: -99.4; y: 138.401 }
+ , PathCubic { control1X: -101.8; control1Y: 138.001; control2X: -104.6; control2Y: 137.601; x: -109; y: 132.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#992600"
+ strokeWidth: -1
+ delegate: [PathMove { x: -111.6; y: 110.001 }
+ , PathCubic { control1X: -111.6; control1Y: 110.001; control2X: -109.8; control2Y: 96.4; x: -108.6; y: 92.4 }
+ , PathCubic { control1X: -108.6; control1Y: 92.4; control2X: -109.4; control2Y: 85.6; x: -107; y: 81.4 }
+ , PathCubic { control1X: -104.6; control1Y: 77.2; control2X: -102.6; control2Y: 71; x: -99.6; y: 65.6 }
+ , PathCubic { control1X: -96.6; control1Y: 60.2; control2X: -96.4; control2Y: 56.2; x: -92.4; y: 54.6 }
+ , PathCubic { control1X: -88.4; control1Y: 53; control2X: -82.4; control2Y: 44.4; x: -79.6; y: 43.4 }
+ , PathCubic { control1X: -76.8; control1Y: 42.4; control2X: -77; control2Y: 43.2; x: -77; y: 43.2 }
+ , PathCubic { control1X: -77; control1Y: 43.2; control2X: -70.2; control2Y: 28.4; x: -56.6; y: 32.4 }
+ , PathCubic { control1X: -56.6; control1Y: 32.4; control2X: -72.8; control2Y: 29.6; x: -57; y: 20.2 }
+ , PathCubic { control1X: -57; control1Y: 20.2; control2X: -61.8; control2Y: 21.3; x: -58.5; y: 14.3 }
+ , PathCubic { control1X: -56.299; control1Y: 9.632; control2X: -56.8; control2Y: 16.4; x: -67.8; y: 28.2 }
+ , PathCubic { control1X: -67.8; control1Y: 28.2; control2X: -72.8; control2Y: 36.8; x: -78; y: 39.8 }
+ , PathCubic { control1X: -83.2; control1Y: 42.8; control2X: -95.2; control2Y: 49.8; x: -96.4; y: 53.6 }
+ , PathCubic { control1X: -97.6; control1Y: 57.4; control2X: -100.8; control2Y: 63.2; x: -102.8; y: 64.8 }
+ , PathCubic { control1X: -104.8; control1Y: 66.4; control2X: -107.6; control2Y: 70.6; x: -108; y: 74 }
+ , PathCubic { control1X: -108; control1Y: 74; control2X: -109.2; control2Y: 78; x: -110.6; y: 79.2 }
+ , PathCubic { control1X: -112; control1Y: 80.4; control2X: -112.2; control2Y: 83.6; x: -112.2; y: 85.6 }
+ , PathCubic { control1X: -112.2; control1Y: 87.6; control2X: -114.2; control2Y: 90.4; x: -114; y: 92.8 }
+ , PathCubic { control1X: -114; control1Y: 92.8; control2X: -113.2; control2Y: 111.801; x: -113.6; y: 113.801 }
+ , PathLine { x: -111.6; y: 110.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: -120.2; y: 114.601 }
+ , PathCubic { control1X: -120.2; control1Y: 114.601; control2X: -122.2; control2Y: 113.201; x: -126.6; y: 119.201 }
+ , PathCubic { control1X: -126.6; control1Y: 119.201; control2X: -119.3; control2Y: 152.201; x: -119.3; y: 153.601 }
+ , PathCubic { control1X: -119.3; control1Y: 153.601; control2X: -118.2; control2Y: 151.501; x: -119.5; y: 144.301 }
+ , PathCubic { control1X: -120.8; control1Y: 137.101; control2X: -121.7; control2Y: 124.401; x: -121.7; y: 124.401 }
+ , PathLine { x: -120.2; y: 114.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#992600"
+ strokeWidth: -1
+ delegate: [PathMove { x: -98.6; y: 54 }
+ , PathCubic { control1X: -98.6; control1Y: 54; control2X: -116.2; control2Y: 57.2; x: -115.8; y: 86.4 }
+ , PathLine { x: -116.6; y: 111.201 }
+ , PathCubic { control1X: -116.6; control1Y: 111.201; control2X: -117.8; control2Y: 85.6; x: -119; y: 84 }
+ , PathCubic { control1X: -120.2; control1Y: 82.4; control2X: -116.2; control2Y: 71.2; x: -119.4; y: 77.2 }
+ , PathCubic { control1X: -119.4; control1Y: 77.2; control2X: -133.4; control2Y: 91.2; x: -125.4; y: 112.401 }
+ , PathCubic { control1X: -125.4; control1Y: 112.401; control2X: -123.9; control2Y: 115.701; x: -126.9; y: 111.101 }
+ , PathCubic { control1X: -126.9; control1Y: 111.101; control2X: -131.5; control2Y: 98.5; x: -130.4; y: 92.1 }
+ , PathCubic { control1X: -130.4; control1Y: 92.1; control2X: -130.2; control2Y: 89.9; x: -128.3; y: 87.1 }
+ , PathCubic { control1X: -128.3; control1Y: 87.1; control2X: -119.7; control2Y: 75.4; x: -117; y: 73.1 }
+ , PathCubic { control1X: -117; control1Y: 73.1; control2X: -115.2; control2Y: 58.7; x: -99.8; y: 53.5 }
+ , PathCubic { control1X: -99.8; control1Y: 53.5; control2X: -94.1; control2Y: 51.2; x: -98.6; y: 54 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 40.8; y: -12.2 }
+ , PathCubic { control1X: 41.46; control1Y: -12.554; control2X: 41.451; control2Y: -13.524; x: 42.031; y: -13.697 }
+ , PathCubic { control1X: 43.18; control1Y: -14.041; control2X: 43.344; control2Y: -15.108; x: 43.862; y: -15.892 }
+ , PathCubic { control1X: 44.735; control1Y: -17.211; control2X: 44.928; control2Y: -18.744; x: 45.51; y: -20.235 }
+ , PathCubic { control1X: 45.782; control1Y: -20.935; control2X: 45.809; control2Y: -21.89; x: 45.496; y: -22.55 }
+ , PathCubic { control1X: 44.322; control1Y: -25.031; control2X: 43.62; control2Y: -27.48; x: 42.178; y: -29.906 }
+ , PathCubic { control1X: 41.91; control1Y: -30.356; control2X: 41.648; control2Y: -31.15; x: 41.447; y: -31.748 }
+ , PathCubic { control1X: 40.984; control1Y: -33.132; control2X: 39.727; control2Y: -34.123; x: 38.867; y: -35.443 }
+ , PathCubic { control1X: 38.579; control1Y: -35.884; control2X: 39.104; control2Y: -36.809; x: 38.388; y: -36.893 }
+ , PathCubic { control1X: 37.491; control1Y: -36.998; control2X: 36.042; control2Y: -37.578; x: 35.809; y: -36.552 }
+ , PathCubic { control1X: 35.221; control1Y: -33.965; control2X: 36.232; control2Y: -31.442; x: 37.2; y: -29 }
+ , PathCubic { control1X: 36.418; control1Y: -28.308; control2X: 36.752; control2Y: -27.387; x: 36.904; y: -26.62 }
+ , PathCubic { control1X: 37.614; control1Y: -23.014; control2X: 36.416; control2Y: -19.662; x: 35.655; y: -16.188 }
+ , PathCubic { control1X: 35.632; control1Y: -16.084; control2X: 35.974; control2Y: -15.886; x: 35.946; y: -15.824 }
+ , PathCubic { control1X: 34.724; control1Y: -13.138; control2X: 33.272; control2Y: -10.693; x: 31.453; y: -8.312 }
+ , PathCubic { control1X: 30.695; control1Y: -7.32; control2X: 29.823; control2Y: -6.404; x: 29.326; y: -5.341 }
+ , PathCubic { control1X: 28.958; control1Y: -4.554; control2X: 28.55; control2Y: -3.588; x: 28.8; y: -2.6 }
+ , PathCubic { control1X: 25.365; control1Y: 0.18; control2X: 23.115; control2Y: 4.025; x: 20.504; y: 7.871 }
+ , PathCubic { control1X: 20.042; control1Y: 8.551; control2X: 20.333; control2Y: 9.76; x: 20.884; y: 10.029 }
+ , PathCubic { control1X: 21.697; control1Y: 10.427; control2X: 22.653; control2Y: 9.403; x: 23.123; y: 8.557 }
+ , PathCubic { control1X: 23.512; control1Y: 7.859; control2X: 23.865; control2Y: 7.209; x: 24.356; y: 6.566 }
+ , PathCubic { control1X: 24.489; control1Y: 6.391; control2X: 24.31; control2Y: 5.972; x: 24.445; y: 5.851 }
+ , PathCubic { control1X: 27.078; control1Y: 3.504; control2X: 28.747; control2Y: 0.568; x: 31.2; y: -1.8 }
+ , PathCubic { control1X: 33.15; control1Y: -2.129; control2X: 34.687; control2Y: -3.127; x: 36.435; y: -4.14 }
+ , PathCubic { control1X: 36.743; control1Y: -4.319; control2X: 37.267; control2Y: -4.07; x: 37.557; y: -4.265 }
+ , PathCubic { control1X: 39.31; control1Y: -5.442; control2X: 39.308; control2Y: -7.478; x: 39.414; y: -9.388 }
+ , PathCubic { control1X: 39.464; control1Y: -10.272; control2X: 39.66; control2Y: -11.589; x: 40.8; y: -12.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 31.959; y: -16.666 }
+ , PathCubic { control1X: 32.083; control1Y: -16.743; control2X: 31.928; control2Y: -17.166; x: 32.037; y: -17.382 }
+ , PathCubic { control1X: 32.199; control1Y: -17.706; control2X: 32.602; control2Y: -17.894; x: 32.764; y: -18.218 }
+ , PathCubic { control1X: 32.873; control1Y: -18.434; control2X: 32.71; control2Y: -18.814; x: 32.846; y: -18.956 }
+ , PathCubic { control1X: 35.179; control1Y: -21.403; control2X: 35.436; control2Y: -24.427; x: 34.4; y: -27.4 }
+ , PathCubic { control1X: 35.424; control1Y: -28.02; control2X: 35.485; control2Y: -29.282; x: 35.06; y: -30.129 }
+ , PathCubic { control1X: 34.207; control1Y: -31.829; control2X: 34.014; control2Y: -33.755; x: 33.039; y: -35.298 }
+ , PathCubic { control1X: 32.237; control1Y: -36.567; control2X: 30.659; control2Y: -37.811; x: 29.288; y: -36.508 }
+ , PathCubic { control1X: 28.867; control1Y: -36.108; control2X: 28.546; control2Y: -35.321; x: 28.824; y: -34.609 }
+ , PathCubic { control1X: 28.888; control1Y: -34.446; control2X: 29.173; control2Y: -34.3; x: 29.146; y: -34.218 }
+ , PathCubic { control1X: 29.039; control1Y: -33.894; control2X: 28.493; control2Y: -33.67; x: 28.487; y: -33.398 }
+ , PathCubic { control1X: 28.457; control1Y: -31.902; control2X: 27.503; control2Y: -30.391; x: 28.133; y: -29.062 }
+ , PathCubic { control1X: 28.905; control1Y: -27.433; control2X: 29.724; control2Y: -25.576; x: 30.4; y: -23.8 }
+ , PathCubic { control1X: 29.166; control1Y: -21.684; control2X: 30.199; control2Y: -19.235; x: 28.446; y: -17.358 }
+ , PathCubic { control1X: 28.31; control1Y: -17.212; control2X: 28.319; control2Y: -16.826; x: 28.441; y: -16.624 }
+ , PathCubic { control1X: 28.733; control1Y: -16.138; control2X: 29.139; control2Y: -15.732; x: 29.625; y: -15.44 }
+ , PathCubic { control1X: 29.827; control1Y: -15.319; control2X: 30.175; control2Y: -15.317; x: 30.375; y: -15.441 }
+ , PathCubic { control1X: 30.953; control1Y: -15.803; control2X: 31.351; control2Y: -16.29; x: 31.959; y: -16.666 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 94.771; y: -26.977 }
+ , PathCubic { control1X: 96.16; control1Y: -25.185; control2X: 96.45; control2Y: -22.39; x: 94.401; y: -21 }
+ , PathCubic { control1X: 94.951; control1Y: -17.691; control2X: 98.302; control2Y: -19.67; x: 100.401; y: -20.2 }
+ , PathCubic { control1X: 100.292; control1Y: -20.588; control2X: 100.519; control2Y: -20.932; x: 100.802; y: -20.937 }
+ , PathCubic { control1X: 101.859; control1Y: -20.952; control2X: 102.539; control2Y: -21.984; x: 103.601; y: -21.8 }
+ , PathCubic { control1X: 104.035; control1Y: -23.357; control2X: 105.673; control2Y: -24.059; x: 106.317; y: -25.439 }
+ , PathCubic { control1X: 108.043; control1Y: -29.134; control2X: 107.452; control2Y: -33.407; x: 104.868; y: -36.653 }
+ , PathCubic { control1X: 104.666; control1Y: -36.907; control2X: 104.883; control2Y: -37.424; x: 104.759; y: -37.786 }
+ , PathCubic { control1X: 104.003; control1Y: -39.997; control2X: 101.935; control2Y: -40.312; x: 100.001; y: -41 }
+ , PathCubic { control1X: 98.824; control1Y: -44.875; control2X: 98.163; control2Y: -48.906; x: 96.401; y: -52.6 }
+ , PathCubic { control1X: 94.787; control1Y: -52.85; control2X: 94.089; control2Y: -54.589; x: 92.752; y: -55.309 }
+ , PathCubic { control1X: 91.419; control1Y: -56.028; control2X: 90.851; control2Y: -54.449; x: 90.892; y: -53.403 }
+ , PathCubic { control1X: 90.899; control1Y: -53.198; control2X: 91.351; control2Y: -52.974; x: 91.181; y: -52.609 }
+ , PathCubic { control1X: 91.105; control1Y: -52.445; control2X: 90.845; control2Y: -52.334; x: 90.845; y: -52.2 }
+ , PathCubic { control1X: 90.846; control1Y: -52.065; control2X: 91.067; control2Y: -51.934; x: 91.201; y: -51.8 }
+ , PathCubic { control1X: 90.283; control1Y: -50.98; control2X: 88.86; control2Y: -50.503; x: 88.565; y: -49.358 }
+ , PathCubic { control1X: 87.611; control1Y: -45.648; control2X: 90.184; control2Y: -42.523; x: 91.852; y: -39.322 }
+ , PathCubic { control1X: 92.443; control1Y: -38.187; control2X: 91.707; control2Y: -36.916; x: 90.947; y: -35.708 }
+ , PathCubic { control1X: 90.509; control1Y: -35.013; control2X: 90.617; control2Y: -33.886; x: 90.893; y: -33.03 }
+ , PathCubic { control1X: 91.645; control1Y: -30.699; control2X: 93.236; control2Y: -28.96; x: 94.771; y: -26.977 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 57.611; y: -8.591 }
+ , PathCubic { control1X: 56.124; control1Y: -6.74; control2X: 52.712; control2Y: -4.171; x: 55.629; y: -2.243 }
+ , PathCubic { control1X: 55.823; control1Y: -2.114; control2X: 56.193; control2Y: -2.11; x: 56.366; y: -2.244 }
+ , PathCubic { control1X: 58.387; control1Y: -3.809; control2X: 60.39; control2Y: -4.712; x: 62.826; y: -5.294 }
+ , PathCubic { control1X: 62.95; control1Y: -5.323; control2X: 63.224; control2Y: -4.856; x: 63.593; y: -5.017 }
+ , PathCubic { control1X: 65.206; control1Y: -5.72; control2X: 67.216; control2Y: -5.662; x: 68.4; y: -7 }
+ , PathCubic { control1X: 72.167; control1Y: -6.776; control2X: 75.732; control2Y: -7.892; x: 79.123; y: -9.2 }
+ , PathCubic { control1X: 80.284; control1Y: -9.648; control2X: 81.554; control2Y: -10.207; x: 82.755; y: -10.709 }
+ , PathCubic { control1X: 84.131; control1Y: -11.285; control2X: 85.335; control2Y: -12.213; x: 86.447; y: -13.354 }
+ , PathCubic { control1X: 86.58; control1Y: -13.49; control2X: 86.934; control2Y: -13.4; x: 87.201; y: -13.4 }
+ , PathCubic { control1X: 87.161; control1Y: -14.263; control2X: 88.123; control2Y: -14.39; x: 88.37; y: -15.012 }
+ , PathCubic { control1X: 88.462; control1Y: -15.244; control2X: 88.312; control2Y: -15.64; x: 88.445; y: -15.742 }
+ , PathCubic { control1X: 90.583; control1Y: -17.372; control2X: 91.503; control2Y: -19.39; x: 90.334; y: -21.767 }
+ , PathCubic { control1X: 90.049; control1Y: -22.345; control2X: 89.8; control2Y: -22.963; x: 89.234; y: -23.439 }
+ , PathCubic { control1X: 88.149; control1Y: -24.35; control2X: 87.047; control2Y: -23.496; x: 86; y: -23.8 }
+ , PathCubic { control1X: 85.841; control1Y: -23.172; control2X: 85.112; control2Y: -23.344; x: 84.726; y: -23.146 }
+ , PathCubic { control1X: 83.867; control1Y: -22.707; control2X: 82.534; control2Y: -23.292; x: 81.675; y: -22.854 }
+ , PathCubic { control1X: 80.313; control1Y: -22.159; control2X: 79.072; control2Y: -21.99; x: 77.65; y: -21.613 }
+ , PathCubic { control1X: 77.338; control1Y: -21.531; control2X: 76.56; control2Y: -21.627; x: 76.4; y: -21 }
+ , PathCubic { control1X: 76.266; control1Y: -21.134; control2X: 76.118; control2Y: -21.368; x: 76.012; y: -21.346 }
+ , PathCubic { control1X: 74.104; control1Y: -20.95; control2X: 72.844; control2Y: -20.736; x: 71.543; y: -19.044 }
+ , PathCubic { control1X: 71.44; control1Y: -18.911; control2X: 70.998; control2Y: -19.09; x: 70.839; y: -18.955 }
+ , PathCubic { control1X: 69.882; control1Y: -18.147; control2X: 69.477; control2Y: -16.913; x: 68.376; y: -16.241 }
+ , PathCubic { control1X: 68.175; control1Y: -16.118; control2X: 67.823; control2Y: -16.286; x: 67.629; y: -16.157 }
+ , PathCubic { control1X: 66.983; control1Y: -15.726; control2X: 66.616; control2Y: -15.085; x: 65.974; y: -14.638 }
+ , PathCubic { control1X: 65.645; control1Y: -14.409; control2X: 65.245; control2Y: -14.734; x: 65.277; y: -14.99 }
+ , PathCubic { control1X: 65.522; control1Y: -16.937; control2X: 66.175; control2Y: -18.724; x: 65.6; y: -20.6 }
+ , PathCubic { control1X: 67.677; control1Y: -23.12; control2X: 70.194; control2Y: -25.069; x: 72; y: -27.8 }
+ , PathCubic { control1X: 72.015; control1Y: -29.966; control2X: 72.707; control2Y: -32.112; x: 72.594; y: -34.189 }
+ , PathCubic { control1X: 72.584; control1Y: -34.382; control2X: 72.296; control2Y: -35.115; x: 72.17; y: -35.462 }
+ , PathCubic { control1X: 71.858; control1Y: -36.316; control2X: 72.764; control2Y: -37.382; x: 71.92; y: -38.106 }
+ , PathCubic { control1X: 70.516; control1Y: -39.309; control2X: 69.224; control2Y: -38.433; x: 68.4; y: -37 }
+ , PathCubic { control1X: 66.562; control1Y: -36.61; control2X: 64.496; control2Y: -35.917; x: 62.918; y: -37.151 }
+ , PathCubic { control1X: 61.911; control1Y: -37.938; control2X: 61.333; control2Y: -38.844; x: 60.534; y: -39.9 }
+ , PathCubic { control1X: 59.549; control1Y: -41.202; control2X: 59.884; control2Y: -42.638; x: 59.954; y: -44.202 }
+ , PathCubic { control1X: 59.96; control1Y: -44.33; control2X: 59.645; control2Y: -44.466; x: 59.645; y: -44.6 }
+ , PathCubic { control1X: 59.646; control1Y: -44.735; control2X: 59.866; control2Y: -44.866; x: 60; y: -45 }
+ , PathCubic { control1X: 59.294; control1Y: -45.626; control2X: 59.019; control2Y: -46.684; x: 58; y: -47 }
+ , PathCubic { control1X: 58.305; control1Y: -48.092; control2X: 57.629; control2Y: -48.976; x: 56.758; y: -49.278 }
+ , PathCubic { control1X: 54.763; control1Y: -49.969; control2X: 53.086; control2Y: -48.057; x: 51.194; y: -47.984 }
+ , PathCubic { control1X: 50.68; control1Y: -47.965; control2X: 50.213; control2Y: -49.003; x: 49.564; y: -49.328 }
+ , PathCubic { control1X: 49.132; control1Y: -49.544; control2X: 48.428; control2Y: -49.577; x: 48.066; y: -49.311 }
+ , PathCubic { control1X: 47.378; control1Y: -48.807; control2X: 46.789; control2Y: -48.693; x: 46.031; y: -48.488 }
+ , PathCubic { control1X: 44.414; control1Y: -48.052; control2X: 43.136; control2Y: -46.958; x: 41.656; y: -46.103 }
+ , PathCubic { control1X: 40.171; control1Y: -45.246; control2X: 39.216; control2Y: -43.809; x: 38.136; y: -42.489 }
+ , PathCubic { control1X: 37.195; control1Y: -41.337; control2X: 37.059; control2Y: -38.923; x: 38.479; y: -38.423 }
+ , PathCubic { control1X: 40.322; control1Y: -37.773; control2X: 41.626; control2Y: -40.476; x: 43.592; y: -40.15 }
+ , PathCubic { control1X: 43.904; control1Y: -40.099; control2X: 44.11; control2Y: -39.788; x: 44; y: -39.4 }
+ , PathCubic { control1X: 44.389; control1Y: -39.291; control2X: 44.607; control2Y: -39.52; x: 44.8; y: -39.8 }
+ , PathCubic { control1X: 45.658; control1Y: -38.781; control2X: 46.822; control2Y: -38.444; x: 47.76; y: -37.571 }
+ , PathCubic { control1X: 48.73; control1Y: -36.667; control2X: 50.476; control2Y: -37.085; x: 51.491; y: -36.088 }
+ , PathCubic { control1X: 53.02; control1Y: -34.586; control2X: 52.461; control2Y: -31.905; x: 54.4; y: -30.6 }
+ , PathCubic { control1X: 53.814; control1Y: -29.287; control2X: 53.207; control2Y: -28.01; x: 52.872; y: -26.583 }
+ , PathCubic { control1X: 52.59; control1Y: -25.377; control2X: 53.584; control2Y: -24.18; x: 54.795; y: -24.271 }
+ , PathCubic { control1X: 56.053; control1Y: -24.365; control2X: 56.315; control2Y: -25.124; x: 56.8; y: -26.2 }
+ , PathCubic { control1X: 57.067; control1Y: -25.933; control2X: 57.536; control2Y: -25.636; x: 57.495; y: -25.42 }
+ , PathCubic { control1X: 57.038; control1Y: -23.033; control2X: 56.011; control2Y: -21.04; x: 55.553; y: -18.609 }
+ , PathCubic { control1X: 55.494; control1Y: -18.292; control2X: 55.189; control2Y: -18.09; x: 54.8; y: -18.2 }
+ , PathCubic { control1X: 54.332; control1Y: -14.051; control2X: 50.28; control2Y: -11.657; x: 47.735; y: -8.492 }
+ , PathCubic { control1X: 47.332; control1Y: -7.99; control2X: 47.328; control2Y: -6.741; x: 47.737; y: -6.338 }
+ , PathCubic { control1X: 49.14; control1Y: -4.951; control2X: 51.1; control2Y: -6.497; x: 52.8; y: -7 }
+ , PathCubic { control1X: 53.013; control1Y: -8.206; control2X: 53.872; control2Y: -9.148; x: 55.204; y: -9.092 }
+ , PathCubic { control1X: 55.46; control1Y: -9.082; control2X: 55.695; control2Y: -9.624; x: 56.019; y: -9.754 }
+ , PathCubic { control1X: 56.367; control1Y: -9.892; control2X: 56.869; control2Y: -9.668; x: 57.155; y: -9.866 }
+ , PathCubic { control1X: 58.884; control1Y: -11.061; control2X: 60.292; control2Y: -12.167; x: 62.03; y: -13.356 }
+ , PathCubic { control1X: 62.222; control1Y: -13.487; control2X: 62.566; control2Y: -13.328; x: 62.782; y: -13.436 }
+ , PathCubic { control1X: 63.107; control1Y: -13.598; control2X: 63.294; control2Y: -13.985; x: 63.617; y: -14.17 }
+ , PathCubic { control1X: 63.965; control1Y: -14.37; control2X: 64.207; control2Y: -14.08; x: 64.4; y: -13.8 }
+ , PathCubic { control1X: 63.754; control1Y: -13.451; control2X: 63.75; control2Y: -12.494; x: 63.168; y: -12.292 }
+ , PathCubic { control1X: 62.393; control1Y: -12.024; control2X: 61.832; control2Y: -11.511; x: 61.158; y: -11.064 }
+ , PathCubic { control1X: 60.866; control1Y: -10.871; control2X: 60.207; control2Y: -11.119; x: 60.103; y: -10.94 }
+ , PathCubic { control1X: 59.505; control1Y: -9.912; control2X: 58.321; control2Y: -9.474; x: 57.611; y: -8.591 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 2.2; y: -58 }
+ , PathCubic { control1X: 2.2; control1Y: -58; control2X: -7.038; control2Y: -60.872; x: -18.2; y: -35.2 }
+ , PathCubic { control1X: -18.2; control1Y: -35.2; control2X: -20.6; control2Y: -30; x: -23; y: -28 }
+ , PathCubic { control1X: -25.4; control1Y: -26; control2X: -36.6; control2Y: -22.4; x: -38.6; y: -18.4 }
+ , PathLine { x: -49; y: -2.4 }
+ , PathCubic { control1X: -49; control1Y: -2.4; control2X: -34.2; control2Y: -18.4; x: -31; y: -20.8 }
+ , PathCubic { control1X: -31; control1Y: -20.8; control2X: -23; control2Y: -29.2; x: -26.2; y: -22.4 }
+ , PathCubic { control1X: -26.2; control1Y: -22.4; control2X: -40.2; control2Y: -11.6; x: -39; y: -2.4 }
+ , PathCubic { control1X: -39; control1Y: -2.4; control2X: -44.6; control2Y: 12; x: -45.4; y: 14 }
+ , PathCubic { control1X: -45.4; control1Y: 14; control2X: -29.4; control2Y: -18; x: -27; y: -19.2 }
+ , PathCubic { control1X: -24.6; control1Y: -20.4; control2X: -23.4; control2Y: -20.4; x: -24.6; y: -16.8 }
+ , PathCubic { control1X: -25.8; control1Y: -13.2; control2X: -26.2; control2Y: 3.2; x: -29; y: 5.2 }
+ , PathCubic { control1X: -29; control1Y: 5.2; control2X: -21; control2Y: -15.2; x: -21.8; y: -18.4 }
+ , PathCubic { control1X: -21.8; control1Y: -18.4; control2X: -18.6; control2Y: -22; x: -16.2; y: -16.8 }
+ , PathLine { x: -17.4; y: -0.8 }
+ , PathLine { x: -13; y: 11.2 }
+ , PathCubic { control1X: -13; control1Y: 11.2; control2X: -15.4; control2Y: 0; x: -13.8; y: -15.6 }
+ , PathCubic { control1X: -13.8; control1Y: -15.6; control2X: -15.8; control2Y: -26; x: -11.8; y: -20.4 }
+ , PathCubic { control1X: -7.8; control1Y: -14.8; control2X: 1.8; control2Y: -8.8; x: 1.8; y: -4 }
+ , PathCubic { control1X: 1.8; control1Y: -4; control2X: -3.4; control2Y: -21.6; x: -12.6; y: -26.4 }
+ , PathLine { x: -16.6; y: -20.4 }
+ , PathLine { x: -17.8; y: -22.4 }
+ , PathCubic { control1X: -17.8; control1Y: -22.4; control2X: -21.4; control2Y: -23.2; x: -17; y: -30 }
+ , PathCubic { control1X: -12.6; control1Y: -36.8; control2X: -13; control2Y: -37.6; x: -13; y: -37.6 }
+ , PathCubic { control1X: -13; control1Y: -37.6; control2X: -6.6; control2Y: -30.4; x: -5; y: -30.4 }
+ , PathCubic { control1X: -5; control1Y: -30.4; control2X: 8.2; control2Y: -38; x: 9.4; y: -13.6 }
+ , PathCubic { control1X: 9.4; control1Y: -13.6; control2X: 16.2; control2Y: -28; x: 7; y: -34.8 }
+ , PathCubic { control1X: 7; control1Y: -34.8; control2X: -7.8; control2Y: -36.8; x: -6.6; y: -42 }
+ , PathLine { x: 0.6; y: -54.4 }
+ , PathCubic { control1X: 4.2; control1Y: -59.6; control2X: 2.6; control2Y: -56.8; x: 2.6; y: -56.8 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -17.8; y: -41.6 }
+ , PathCubic { control1X: -17.8; control1Y: -41.6; control2X: -30.6; control2Y: -41.6; x: -33.8; y: -36.4 }
+ , PathLine { x: -41; y: -26.8 }
+ , PathCubic { control1X: -41; control1Y: -26.8; control2X: -23.8; control2Y: -36.8; x: -19.8; y: -38 }
+ , PathCubic { control1X: -15.8; control1Y: -39.2; control2X: -17.8; control2Y: -41.6; x: -17.8; y: -41.6 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -57.8; y: -35.2 }
+ , PathCubic { control1X: -57.8; control1Y: -35.2; control2X: -59.8; control2Y: -34; x: -60.2; y: -31.2 }
+ , PathCubic { control1X: -60.6; control1Y: -28.4; control2X: -63; control2Y: -28; x: -62.2; y: -25.2 }
+ , PathCubic { control1X: -61.4; control1Y: -22.4; control2X: -59.4; control2Y: -20; x: -59.4; y: -24 }
+ , PathCubic { control1X: -59.4; control1Y: -28; control2X: -57.8; control2Y: -30; x: -57; y: -31.2 }
+ , PathCubic { control1X: -56.2; control1Y: -32.4; control2X: -54.6; control2Y: -36.8; x: -57.8; y: -35.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -66.6; y: 26 }
+ , PathCubic { control1X: -66.6; control1Y: 26; control2X: -75; control2Y: 22; x: -78.2; y: 18.4 }
+ , PathCubic { control1X: -81.4; control1Y: 14.8; control2X: -80.948; control2Y: 19.966; x: -85.8; y: 19.6 }
+ , PathCubic { control1X: -91.647; control1Y: 19.159; control2X: -90.6; control2Y: 3.2; x: -90.6; y: 3.2 }
+ , PathLine { x: -94.6; y: 10.8 }
+ , PathCubic { control1X: -94.6; control1Y: 10.8; control2X: -95.8; control2Y: 25.2; x: -87.8; y: 22.8 }
+ , PathCubic { control1X: -83.893; control1Y: 21.628; control2X: -82.6; control2Y: 23.2; x: -84.2; y: 24 }
+ , PathCubic { control1X: -85.8; control1Y: 24.8; control2X: -78.6; control2Y: 25.2; x: -81.4; y: 26.8 }
+ , PathCubic { control1X: -84.2; control1Y: 28.4; control2X: -69.8; control2Y: 23.2; x: -72.2; y: 33.6 }
+ , PathLine { x: -66.6; y: 26 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -79.2; y: 40.4 }
+ , PathCubic { control1X: -79.2; control1Y: 40.4; control2X: -94.6; control2Y: 44.8; x: -98.2; y: 35.2 }
+ , PathCubic { control1X: -98.2; control1Y: 35.2; control2X: -103; control2Y: 37.6; x: -100.8; y: 40.6 }
+ , PathCubic { control1X: -98.6; control1Y: 43.6; control2X: -97.4; control2Y: 44; x: -97.4; y: 44 }
+ , PathCubic { control1X: -97.4; control1Y: 44; control2X: -92; control2Y: 45.2; x: -92.6; y: 46 }
+ , PathCubic { control1X: -93.2; control1Y: 46.8; control2X: -95.6; control2Y: 50.2; x: -95.6; y: 50.2 }
+ , PathCubic { control1X: -95.6; control1Y: 50.2; control2X: -85.4; control2Y: 44.2; x: -79.2; y: 40.4 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: 149.201; y: 118.601 }
+ , PathCubic { control1X: 148.774; control1Y: 120.735; control2X: 147.103; control2Y: 121.536; x: 145.201; y: 122.201 }
+ , PathCubic { control1X: 143.284; control1Y: 121.243; control2X: 140.686; control2Y: 118.137; x: 138.801; y: 120.201 }
+ , PathCubic { control1X: 138.327; control1Y: 119.721; control2X: 137.548; control2Y: 119.661; x: 137.204; y: 118.999 }
+ , PathCubic { control1X: 136.739; control1Y: 118.101; control2X: 137.011; control2Y: 117.055; x: 136.669; y: 116.257 }
+ , PathCubic { control1X: 136.124; control1Y: 114.985; control2X: 135.415; control2Y: 113.619; x: 135.601; y: 112.201 }
+ , PathCubic { control1X: 137.407; control1Y: 111.489; control2X: 138.002; control2Y: 109.583; x: 137.528; y: 107.82 }
+ , PathCubic { control1X: 137.459; control1Y: 107.563; control2X: 137.03; control2Y: 107.366; x: 137.23; y: 107.017 }
+ , PathCubic { control1X: 137.416; control1Y: 106.694; control2X: 137.734; control2Y: 106.467; x: 138.001; y: 106.2 }
+ , PathCubic { control1X: 137.866; control1Y: 106.335; control2X: 137.721; control2Y: 106.568; x: 137.61; y: 106.548 }
+ , PathCubic { control1X: 137; control1Y: 106.442; control2X: 137.124; control2Y: 105.805; x: 137.254; y: 105.418 }
+ , PathCubic { control1X: 137.839; control1Y: 103.672; control2X: 139.853; control2Y: 103.408; x: 141.201; y: 104.6 }
+ , PathCubic { control1X: 141.457; control1Y: 104.035; control2X: 141.966; control2Y: 104.229; x: 142.401; y: 104.2 }
+ , PathCubic { control1X: 142.351; control1Y: 103.621; control2X: 142.759; control2Y: 103.094; x: 142.957; y: 102.674 }
+ , PathCubic { control1X: 143.475; control1Y: 101.576; control2X: 145.104; control2Y: 102.682; x: 145.901; y: 102.07 }
+ , PathCubic { control1X: 146.977; control1Y: 101.245; control2X: 148.04; control2Y: 100.546; x: 149.118; y: 101.149 }
+ , PathCubic { control1X: 150.927; control1Y: 102.162; control2X: 152.636; control2Y: 103.374; x: 153.835; y: 105.115 }
+ , PathCubic { control1X: 154.41; control1Y: 105.949; control2X: 154.65; control2Y: 107.23; x: 154.592; y: 108.188 }
+ , PathCubic { control1X: 154.554; control1Y: 108.835; control2X: 153.173; control2Y: 108.483; x: 152.83; y: 109.412 }
+ , PathCubic { control1X: 152.185; control1Y: 111.16; control2X: 154.016; control2Y: 111.679; x: 154.772; y: 113.017 }
+ , PathCubic { control1X: 154.97; control1Y: 113.366; control2X: 154.706; control2Y: 113.67; x: 154.391; y: 113.768 }
+ , PathCubic { control1X: 153.98; control1Y: 113.896; control2X: 153.196; control2Y: 113.707; x: 153.334; y: 114.16 }
+ , PathCubic { control1X: 154.306; control1Y: 117.353; control2X: 151.55; control2Y: 118.031; x: 149.201; y: 118.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeWidth: -1
+ delegate: [PathMove { x: 139.6; y: 138.201 }
+ , PathCubic { control1X: 139.593; control1Y: 136.463; control2X: 137.992; control2Y: 134.707; x: 139.201; y: 133.001 }
+ , PathCubic { control1X: 139.336; control1Y: 133.135; control2X: 139.467; control2Y: 133.356; x: 139.601; y: 133.356 }
+ , PathCubic { control1X: 139.736; control1Y: 133.356; control2X: 139.867; control2Y: 133.135; x: 140.001; y: 133.001 }
+ , PathCubic { control1X: 141.496; control1Y: 135.217; control2X: 145.148; control2Y: 136.145; x: 145.006; y: 138.991 }
+ , PathCubic { control1X: 144.984; control1Y: 139.438; control2X: 143.897; control2Y: 140.356; x: 144.801; y: 141.001 }
+ , PathCubic { control1X: 142.988; control1Y: 142.349; control2X: 142.933; control2Y: 144.719; x: 142.001; y: 146.601 }
+ , PathCubic { control1X: 140.763; control1Y: 146.315; control2X: 139.551; control2Y: 145.952; x: 138.401; y: 145.401 }
+ , PathCubic { control1X: 138.753; control1Y: 143.915; control2X: 138.636; control2Y: 142.231; x: 139.456; y: 140.911 }
+ , PathCubic { control1X: 139.89; control1Y: 140.213; control2X: 139.603; control2Y: 139.134; x: 139.6; y: 138.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -26.6; y: 129.201 }
+ , PathCubic { control1X: -26.6; control1Y: 129.201; control2X: -43.458; control2Y: 139.337; x: -29.4; y: 124.001 }
+ , PathCubic { control1X: -20.6; control1Y: 114.401; control2X: -10.6; control2Y: 108.801; x: -10.6; y: 108.801 }
+ , PathCubic { control1X: -10.6; control1Y: 108.801; control2X: -0.2; control2Y: 104.4; x: 3.4; y: 103.2 }
+ , PathCubic { control1X: 7; control1Y: 102; control2X: 22.2; control2Y: 96.8; x: 25.4; y: 96.4 }
+ , PathCubic { control1X: 28.6; control1Y: 96; control2X: 38.2; control2Y: 92; x: 45; y: 96 }
+ , PathCubic { control1X: 51.8; control1Y: 100; control2X: 59.8; control2Y: 104.4; x: 59.8; y: 104.4 }
+ , PathCubic { control1X: 59.8; control1Y: 104.4; control2X: 43.4; control2Y: 96; x: 39.8; y: 98.4 }
+ , PathCubic { control1X: 36.2; control1Y: 100.8; control2X: 29; control2Y: 100.4; x: 23; y: 103.6 }
+ , PathCubic { control1X: 23; control1Y: 103.6; control2X: 8.2; control2Y: 108.001; x: 5; y: 110.001 }
+ , PathCubic { control1X: 1.8; control1Y: 112.001; control2X: -8.6; control2Y: 123.601; x: -10.2; y: 122.801 }
+ , PathCubic { control1X: -11.8; control1Y: 122.001; control2X: -9.8; control2Y: 121.601; x: -8.6; y: 118.801 }
+ , PathCubic { control1X: -7.4; control1Y: 116.001; control2X: -9.4; control2Y: 114.401; x: -17.4; y: 120.801 }
+ , PathCubic { control1X: -25.4; control1Y: 127.201; control2X: -26.6; control2Y: 129.201; x: -26.6; y: 129.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -19.195; y: 123.234 }
+ , PathCubic { control1X: -19.195; control1Y: 123.234; control2X: -17.785; control2Y: 110.194; x: -9.307; y: 111.859 }
+ , PathCubic { control1X: -9.307; control1Y: 111.859; control2X: -1.081; control2Y: 107.689; x: 1.641; y: 105.721 }
+ , PathCubic { control1X: 1.641; control1Y: 105.721; control2X: 9.78; control2Y: 104.019; x: 11.09; y: 103.402 }
+ , PathCubic { control1X: 29.569; control1Y: 94.702; control2X: 44.288; control2Y: 99.221; x: 44.835; y: 98.101 }
+ , PathCubic { control1X: 45.381; control1Y: 96.982; control2X: 65.006; control2Y: 104.099; x: 68.615; y: 108.185 }
+ , PathCubic { control1X: 69.006; control1Y: 108.628; control2X: 58.384; control2Y: 102.588; x: 48.686; y: 100.697 }
+ , PathCubic { control1X: 40.413; control1Y: 99.083; control2X: 18.811; control2Y: 100.944; x: 7.905; y: 106.48 }
+ , PathCubic { control1X: 4.932; control1Y: 107.989; control2X: -4.013; control2Y: 113.773; x: -6.544; y: 113.662 }
+ , PathCubic { control1X: -9.075; control1Y: 113.55; control2X: -19.195; control2Y: 123.234; x: -19.195; y: 123.234 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -23; y: 148.801 }
+ , PathCubic { control1X: -23; control1Y: 148.801; control2X: -38.2; control2Y: 146.401; x: -21.4; y: 144.801 }
+ , PathCubic { control1X: -21.4; control1Y: 144.801; control2X: -3.4; control2Y: 142.801; x: 0.6; y: 137.601 }
+ , PathCubic { control1X: 0.6; control1Y: 137.601; control2X: 14.2; control2Y: 128.401; x: 17; y: 128.001 }
+ , PathCubic { control1X: 19.8; control1Y: 127.601; control2X: 49.8; control2Y: 120.401; x: 50.2; y: 118.001 }
+ , PathCubic { control1X: 50.6; control1Y: 115.601; control2X: 56.2; control2Y: 115.601; x: 57.8; y: 116.401 }
+ , PathCubic { control1X: 59.4; control1Y: 117.201; control2X: 58.6; control2Y: 118.401; x: 55.8; y: 119.201 }
+ , PathCubic { control1X: 53; control1Y: 120.001; control2X: 21.8; control2Y: 136.401; x: 15.4; y: 137.601 }
+ , PathCubic { control1X: 9; control1Y: 138.801; control2X: -2.6; control2Y: 146.401; x: -7.4; y: 147.601 }
+ , PathCubic { control1X: -12.2; control1Y: 148.801; control2X: -23; control2Y: 148.801; x: -23; y: 148.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -3.48; y: 141.403 }
+ , PathCubic { control1X: -3.48; control1Y: 141.403; control2X: -12.062; control2Y: 140.574; x: -3.461; y: 139.755 }
+ , PathCubic { control1X: -3.461; control1Y: 139.755; control2X: 5.355; control2Y: 136.331; x: 7.403; y: 133.668 }
+ , PathCubic { control1X: 7.403; control1Y: 133.668; control2X: 14.367; control2Y: 128.957; x: 15.8; y: 128.753 }
+ , PathCubic { control1X: 17.234; control1Y: 128.548; control2X: 31.194; control2Y: 124.861; x: 31.399; y: 123.633 }
+ , PathCubic { control1X: 31.604; control1Y: 122.404; control2X: 65.67; control2Y: 109.823; x: 70.09; y: 113.013 }
+ , PathCubic { control1X: 73.001; control1Y: 115.114; control2X: 63.1; control2Y: 113.437; x: 53.466; y: 117.847 }
+ , PathCubic { control1X: 52.111; control1Y: 118.467; control2X: 18.258; control2Y: 133.054; x: 14.981; y: 133.668 }
+ , PathCubic { control1X: 11.704; control1Y: 134.283; control2X: 5.765; control2Y: 138.174; x: 3.307; y: 138.788 }
+ , PathCubic { control1X: 0.85; control1Y: 139.403; control2X: -3.48; control2Y: 141.403; x: -3.48; y: 141.403 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -11.4; y: 143.601 }
+ , PathCubic { control1X: -11.4; control1Y: 143.601; control2X: -6.2; control2Y: 143.201; x: -7.4; y: 144.801 }
+ , PathCubic { control1X: -8.6; control1Y: 146.401; control2X: -11; control2Y: 145.601; x: -11; y: 145.601 }
+ , PathLine { x: -11.4; y: 143.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -18.6; y: 145.201 }
+ , PathCubic { control1X: -18.6; control1Y: 145.201; control2X: -13.4; control2Y: 144.801; x: -14.6; y: 146.401 }
+ , PathCubic { control1X: -15.8; control1Y: 148.001; control2X: -18.2; control2Y: 147.201; x: -18.2; y: 147.201 }
+ , PathLine { x: -18.6; y: 145.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -29; y: 146.801 }
+ , PathCubic { control1X: -29; control1Y: 146.801; control2X: -23.8; control2Y: 146.401; x: -25; y: 148.001 }
+ , PathCubic { control1X: -26.2; control1Y: 149.601; control2X: -28.6; control2Y: 148.801; x: -28.6; y: 148.801 }
+ , PathLine { x: -29; y: 146.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -36.6; y: 147.601 }
+ , PathCubic { control1X: -36.6; control1Y: 147.601; control2X: -31.4; control2Y: 147.201; x: -32.6; y: 148.801 }
+ , PathCubic { control1X: -33.8; control1Y: 150.401; control2X: -36.2; control2Y: 149.601; x: -36.2; y: 149.601 }
+ , PathLine { x: -36.6; y: 147.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 1.8; y: 108.001 }
+ , PathCubic { control1X: 1.8; control1Y: 108.001; control2X: 6.2; control2Y: 108.001; x: 5; y: 109.601 }
+ , PathCubic { control1X: 3.8; control1Y: 111.201; control2X: 0.6; control2Y: 110.801; x: 0.6; y: 110.801 }
+ , PathLine { x: 1.8; y: 108.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -8.2; y: 113.601 }
+ , PathCubic { control1X: -8.2; control1Y: 113.601; control2X: -1.694; control2Y: 111.46; x: -4.2; y: 114.801 }
+ , PathCubic { control1X: -5.4; control1Y: 116.401; control2X: -7.8; control2Y: 115.601; x: -7.8; y: 115.601 }
+ , PathLine { x: -8.2; y: 113.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -19.4; y: 118.401 }
+ , PathCubic { control1X: -19.4; control1Y: 118.401; control2X: -14.2; control2Y: 118.001; x: -15.4; y: 119.601 }
+ , PathCubic { control1X: -16.6; control1Y: 121.201; control2X: -19; control2Y: 120.401; x: -19; y: 120.401 }
+ , PathLine { x: -19.4; y: 118.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -27; y: 124.401 }
+ , PathCubic { control1X: -27; control1Y: 124.401; control2X: -21.8; control2Y: 124.001; x: -23; y: 125.601 }
+ , PathCubic { control1X: -24.2; control1Y: 127.201; control2X: -26.6; control2Y: 126.401; x: -26.6; y: 126.401 }
+ , PathLine { x: -27; y: 124.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -33.8; y: 129.201 }
+ , PathCubic { control1X: -33.8; control1Y: 129.201; control2X: -28.6; control2Y: 128.801; x: -29.8; y: 130.401 }
+ , PathCubic { control1X: -31; control1Y: 132.001; control2X: -33.4; control2Y: 131.201; x: -33.4; y: 131.201 }
+ , PathLine { x: -33.8; y: 129.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 5.282; y: 135.598 }
+ , PathCubic { control1X: 5.282; control1Y: 135.598; control2X: 12.203; control2Y: 135.066; x: 10.606; y: 137.195 }
+ , PathCubic { control1X: 9.009; control1Y: 139.325; control2X: 5.814; control2Y: 138.26; x: 5.814; y: 138.26 }
+ , PathLine { x: 5.282; y: 135.598 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 15.682; y: 130.798 }
+ , PathCubic { control1X: 15.682; control1Y: 130.798; control2X: 22.603; control2Y: 130.266; x: 21.006; y: 132.395 }
+ , PathCubic { control1X: 19.409; control1Y: 134.525; control2X: 16.214; control2Y: 133.46; x: 16.214; y: 133.46 }
+ , PathLine { x: 15.682; y: 130.798 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 26.482; y: 126.398 }
+ , PathCubic { control1X: 26.482; control1Y: 126.398; control2X: 33.403; control2Y: 125.866; x: 31.806; y: 127.995 }
+ , PathCubic { control1X: 30.209; control1Y: 130.125; control2X: 27.014; control2Y: 129.06; x: 27.014; y: 129.06 }
+ , PathLine { x: 26.482; y: 126.398 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 36.882; y: 121.598 }
+ , PathCubic { control1X: 36.882; control1Y: 121.598; control2X: 43.803; control2Y: 121.066; x: 42.206; y: 123.195 }
+ , PathCubic { control1X: 40.609; control1Y: 125.325; control2X: 37.414; control2Y: 124.26; x: 37.414; y: 124.26 }
+ , PathLine { x: 36.882; y: 121.598 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 9.282; y: 103.598 }
+ , PathCubic { control1X: 9.282; control1Y: 103.598; control2X: 16.203; control2Y: 103.066; x: 14.606; y: 105.195 }
+ , PathCubic { control1X: 13.009; control1Y: 107.325; control2X: 9.014; control2Y: 107.06; x: 9.014; y: 107.06 }
+ , PathLine { x: 9.282; y: 103.598 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 19.282; y: 100.398 }
+ , PathCubic { control1X: 19.282; control1Y: 100.398; control2X: 26.203; control2Y: 99.866; x: 24.606; y: 101.995 }
+ , PathCubic { control1X: 23.009; control1Y: 104.125; control2X: 18.614; control2Y: 103.86; x: 18.614; y: 103.86 }
+ , PathLine { x: 19.282; y: 100.398 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -3.4; y: 140.401 }
+ , PathCubic { control1X: -3.4; control1Y: 140.401; control2X: 1.8; control2Y: 140.001; x: 0.6; y: 141.601 }
+ , PathCubic { control1X: -0.6; control1Y: 143.201; control2X: -3; control2Y: 142.401; x: -3; y: 142.401 }
+ , PathLine { x: -3.4; y: 140.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#992600"
+ strokeWidth: -1
+ delegate: [PathMove { x: -76.6; y: 41.2 }
+ , PathCubic { control1X: -76.6; control1Y: 41.2; control2X: -81; control2Y: 50; x: -81.4; y: 53.2 }
+ , PathCubic { control1X: -81.4; control1Y: 53.2; control2X: -80.6; control2Y: 44.4; x: -79.4; y: 42.4 }
+ , PathCubic { control1X: -78.2; control1Y: 40.4; control2X: -76.6; control2Y: 41.2; x: -76.6; y: 41.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#992600"
+ strokeWidth: -1
+ delegate: [PathMove { x: -95; y: 55.2 }
+ , PathCubic { control1X: -95; control1Y: 55.2; control2X: -98.2; control2Y: 69.6; x: -97.8; y: 72.4 }
+ , PathCubic { control1X: -97.8; control1Y: 72.4; control2X: -99; control2Y: 60.8; x: -98.6; y: 59.6 }
+ , PathCubic { control1X: -98.2; control1Y: 58.4; control2X: -95; control2Y: 55.2; x: -95; y: 55.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -74.2; y: -19.4 }
+ , PathLine { x: -74.4; y: -16.2 }
+ , PathLine { x: -76.6; y: -16 }
+ , PathCubic { control1X: -76.6; control1Y: -16; control2X: -62.4; control2Y: -3.4; x: -61.8; y: 4.2 }
+ , PathCubic { control1X: -61.8; control1Y: 4.2; control2X: -61; control2Y: -4; x: -74.2; y: -19.4 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -70.216; y: -18.135 }
+ , PathCubic { control1X: -70.647; control1Y: -18.551; control2X: -70.428; control2Y: -19.296; x: -70.836; y: -19.556 }
+ , PathCubic { control1X: -71.645; control1Y: -20.072; control2X: -69.538; control2Y: -20.129; x: -69.766; y: -20.845 }
+ , PathCubic { control1X: -70.149; control1Y: -22.051; control2X: -69.962; control2Y: -22.072; x: -70.084; y: -23.348 }
+ , PathCubic { control1X: -70.141; control1Y: -23.946; control2X: -69.553; control2Y: -25.486; x: -69.168; y: -25.926 }
+ , PathCubic { control1X: -67.722; control1Y: -27.578; control2X: -69.046; control2Y: -30.51; x: -67.406; y: -32.061 }
+ , PathCubic { control1X: -67.102; control1Y: -32.35; control2X: -66.726; control2Y: -32.902; x: -66.441; y: -33.32 }
+ , PathCubic { control1X: -65.782; control1Y: -34.283; control2X: -64.598; control2Y: -34.771; x: -63.648; y: -35.599 }
+ , PathCubic { control1X: -63.33; control1Y: -35.875; control2X: -63.531; control2Y: -36.702; x: -62.962; y: -36.61 }
+ , PathCubic { control1X: -62.248; control1Y: -36.495; control2X: -61.007; control2Y: -36.625; x: -61.052; y: -35.784 }
+ , PathCubic { control1X: -61.165; control1Y: -33.664; control2X: -62.494; control2Y: -31.944; x: -63.774; y: -30.276 }
+ , PathCubic { control1X: -63.323; control1Y: -29.572; control2X: -63.781; control2Y: -28.937; x: -64.065; y: -28.38 }
+ , PathCubic { control1X: -65.4; control1Y: -25.76; control2X: -65.211; control2Y: -22.919; x: -65.385; y: -20.079 }
+ , PathCubic { control1X: -65.39; control1Y: -19.994; control2X: -65.697; control2Y: -19.916; x: -65.689; y: -19.863 }
+ , PathCubic { control1X: -65.336; control1Y: -17.528; control2X: -64.752; control2Y: -15.329; x: -63.873; y: -13.1 }
+ , PathCubic { control1X: -63.507; control1Y: -12.17; control2X: -63.036; control2Y: -11.275; x: -62.886; y: -10.348 }
+ , PathCubic { control1X: -62.775; control1Y: -9.662; control2X: -62.672; control2Y: -8.829; x: -63.08; y: -8.124 }
+ , PathCubic { control1X: -61.045; control1Y: -5.234; control2X: -62.354; control2Y: -2.583; x: -61.185; y: 0.948 }
+ , PathCubic { control1X: -60.978; control1Y: 1.573; control2X: -59.286; control2Y: 3.487; x: -59.749; y: 3.326 }
+ , PathCubic { control1X: -62.262; control1Y: 2.455; control2X: -62.374; control2Y: 2.057; x: -62.551; y: 1.304 }
+ , PathCubic { control1X: -62.697; control1Y: 0.681; control2X: -63.027; control2Y: -0.696; x: -63.264; y: -1.298 }
+ , PathCubic { control1X: -63.328; control1Y: -1.462; control2X: -63.499; control2Y: -3.346; x: -63.577; y: -3.468 }
+ , PathCubic { control1X: -65.09; control1Y: -5.85; control2X: -63.732; control2Y: -5.674; x: -65.102; y: -8.032 }
+ , PathCubic { control1X: -66.53; control1Y: -8.712; control2X: -67.496; control2Y: -9.816; x: -68.619; y: -10.978 }
+ , PathCubic { control1X: -68.817; control1Y: -11.182; control2X: -67.674; control2Y: -11.906; x: -67.855; y: -12.119 }
+ , PathCubic { control1X: -68.947; control1Y: -13.408; control2X: -70.1; control2Y: -14.175; x: -69.764; y: -15.668 }
+ , PathCubic { control1X: -69.609; control1Y: -16.358; control2X: -69.472; control2Y: -17.415; x: -70.216; y: -18.135 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -73.8; y: -16.4 }
+ , PathCubic { control1X: -73.8; control1Y: -16.4; control2X: -73.4; control2Y: -9.6; x: -71; y: -8 }
+ , PathCubic { control1X: -68.6; control1Y: -6.4; control2X: -69.8; control2Y: -7.2; x: -73; y: -8.4 }
+ , PathCubic { control1X: -76.2; control1Y: -9.6; control2X: -75; control2Y: -10.4; x: -75; y: -10.4 }
+ , PathCubic { control1X: -75; control1Y: -10.4; control2X: -77.8; control2Y: -10; x: -75.4; y: -8 }
+ , PathCubic { control1X: -73; control1Y: -6; control2X: -69.4; control2Y: -3.6; x: -71; y: -3.6 }
+ , PathCubic { control1X: -72.6; control1Y: -3.6; control2X: -80.2; control2Y: -7.6; x: -80.2; y: -10.4 }
+ , PathCubic { control1X: -80.2; control1Y: -13.2; control2X: -81.2; control2Y: -17.3; x: -81.2; y: -17.3 }
+ , PathCubic { control1X: -81.2; control1Y: -17.3; control2X: -80.1; control2Y: -18.1; x: -75.3; y: -18 }
+ , PathCubic { control1X: -75.3; control1Y: -18; control2X: -73.9; control2Y: -17.3; x: -73.8; y: -16.4 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -74.6; y: 2.2 }
+ , PathCubic { control1X: -74.6; control1Y: 2.2; control2X: -83.12; control2Y: -0.591; x: -101.6; y: 2.8 }
+ , PathCubic { control1X: -101.6; control1Y: 2.8; control2X: -92.569; control2Y: 0.722; x: -73.8; y: 3 }
+ , PathCubic { control1X: -63.5; control1Y: 4.25; control2X: -74.6; control2Y: 2.2; x: -74.6; y: 2.2 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -72.502; y: 2.129 }
+ , PathCubic { control1X: -72.502; control1Y: 2.129; control2X: -80.748; control2Y: -1.389; x: -99.453; y: 0.392 }
+ , PathCubic { control1X: -99.453; control1Y: 0.392; control2X: -90.275; control2Y: -0.897; x: -71.774; y: 2.995 }
+ , PathCubic { control1X: -61.62; control1Y: 5.131; control2X: -72.502; control2Y: 2.129; x: -72.502; y: 2.129 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -70.714; y: 2.222 }
+ , PathCubic { control1X: -70.714; control1Y: 2.222; control2X: -78.676; control2Y: -1.899; x: -97.461; y: -1.514 }
+ , PathCubic { control1X: -97.461; control1Y: -1.514; control2X: -88.213; control2Y: -2.118; x: -70.052; y: 3.14 }
+ , PathCubic { control1X: -60.086; control1Y: 6.025; control2X: -70.714; control2Y: 2.222; x: -70.714; y: 2.222 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -69.444; y: 2.445 }
+ , PathCubic { control1X: -69.444; control1Y: 2.445; control2X: -76.268; control2Y: -1.862; x: -93.142; y: -2.96 }
+ , PathCubic { control1X: -93.142; control1Y: -2.96; control2X: -84.803; control2Y: -2.79; x: -68.922; y: 3.319 }
+ , PathCubic { control1X: -60.206; control1Y: 6.672; control2X: -69.444; control2Y: 2.445; x: -69.444; y: 2.445 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 45.84; y: 12.961 }
+ , PathCubic { control1X: 45.84; control1Y: 12.961; control2X: 44.91; control2Y: 13.605; x: 45.124; y: 12.424 }
+ , PathCubic { control1X: 45.339; control1Y: 11.243; control2X: 73.547; control2Y: -1.927; x: 77.161; y: -1.677 }
+ , PathCubic { control1X: 77.161; control1Y: -1.677; control2X: 46.913; control2Y: 11.529; x: 45.84; y: 12.961 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 42.446; y: 13.6 }
+ , PathCubic { control1X: 42.446; control1Y: 13.6; control2X: 41.57; control2Y: 14.315; x: 41.691; y: 13.121 }
+ , PathCubic { control1X: 41.812; control1Y: 11.927; control2X: 68.899; control2Y: -3.418; x: 72.521; y: -3.452 }
+ , PathCubic { control1X: 72.521; control1Y: -3.452; control2X: 43.404; control2Y: 12.089; x: 42.446; y: 13.6 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 39.16; y: 14.975 }
+ , PathCubic { control1X: 39.16; control1Y: 14.975; control2X: 38.332; control2Y: 15.747; x: 38.374; y: 14.547 }
+ , PathCubic { control1X: 38.416; control1Y: 13.348; control2X: 58.233; control2Y: -2.149; x: 68.045; y: -4.023 }
+ , PathCubic { control1X: 68.045; control1Y: -4.023; control2X: 50.015; control2Y: 4.104; x: 39.16; y: 14.975 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 36.284; y: 16.838 }
+ , PathCubic { control1X: 36.284; control1Y: 16.838; control2X: 35.539; control2Y: 17.532; x: 35.577; y: 16.453 }
+ , PathCubic { control1X: 35.615; control1Y: 15.373; control2X: 53.449; control2Y: 1.426; x: 62.28; y: -0.26 }
+ , PathCubic { control1X: 62.28; control1Y: -0.26; control2X: 46.054; control2Y: 7.054; x: 36.284; y: 16.838 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 4.6; y: 164.801 }
+ , PathCubic { control1X: 4.6; control1Y: 164.801; control2X: -10.6; control2Y: 162.401; x: 6.2; y: 160.801 }
+ , PathCubic { control1X: 6.2; control1Y: 160.801; control2X: 24.2; control2Y: 158.801; x: 28.2; y: 153.601 }
+ , PathCubic { control1X: 28.2; control1Y: 153.601; control2X: 41.8; control2Y: 144.401; x: 44.6; y: 144.001 }
+ , PathCubic { control1X: 47.4; control1Y: 143.601; control2X: 63.8; control2Y: 140.001; x: 64.2; y: 137.601 }
+ , PathCubic { control1X: 64.6; control1Y: 135.201; control2X: 70.6; control2Y: 132.801; x: 72.2; y: 133.601 }
+ , PathCubic { control1X: 73.8; control1Y: 134.401; control2X: 73.8; control2Y: 143.601; x: 71; y: 144.401 }
+ , PathCubic { control1X: 68.2; control1Y: 145.201; control2X: 49.4; control2Y: 152.401; x: 43; y: 153.601 }
+ , PathCubic { control1X: 36.6; control1Y: 154.801; control2X: 25; control2Y: 162.401; x: 20.2; y: 163.601 }
+ , PathCubic { control1X: 15.4; control1Y: 164.801; control2X: 4.6; control2Y: 164.801; x: 4.6; y: 164.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 77.6; y: 127.401 }
+ , PathCubic { control1X: 77.6; control1Y: 127.401; control2X: 74.6; control2Y: 129.001; x: 73.4; y: 131.601 }
+ , PathCubic { control1X: 73.4; control1Y: 131.601; control2X: 67; control2Y: 142.201; x: 52.8; y: 145.401 }
+ , PathCubic { control1X: 52.8; control1Y: 145.401; control2X: 29.8; control2Y: 154.401; x: 22; y: 156.401 }
+ , PathCubic { control1X: 22; control1Y: 156.401; control2X: 8.6; control2Y: 161.401; x: 1.2; y: 160.601 }
+ , PathCubic { control1X: 1.2; control1Y: 160.601; control2X: -5.8; control2Y: 160.801; x: 0.4; y: 162.401 }
+ , PathCubic { control1X: 0.4; control1Y: 162.401; control2X: 20.6; control2Y: 160.401; x: 24; y: 158.601 }
+ , PathCubic { control1X: 24; control1Y: 158.601; control2X: 39.6; control2Y: 153.401; x: 42.6; y: 150.801 }
+ , PathCubic { control1X: 45.6; control1Y: 148.201; control2X: 63.8; control2Y: 143.201; x: 66; y: 141.201 }
+ , PathCubic { control1X: 68.2; control1Y: 139.201; control2X: 78; control2Y: 130.801; x: 77.6; y: 127.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 18.882; y: 158.911 }
+ , PathCubic { control1X: 18.882; control1Y: 158.911; control2X: 24.111; control2Y: 158.685; x: 22.958; y: 160.234 }
+ , PathCubic { control1X: 21.805; control1Y: 161.784; control2X: 19.357; control2Y: 160.91; x: 19.357; y: 160.91 }
+ , PathLine { x: 18.882; y: 158.911 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 11.68; y: 160.263 }
+ , PathCubic { control1X: 11.68; control1Y: 160.263; control2X: 16.908; control2Y: 160.037; x: 15.756; y: 161.586 }
+ , PathCubic { control1X: 14.603; control1Y: 163.136; control2X: 12.155; control2Y: 162.263; x: 12.155; y: 162.263 }
+ , PathLine { x: 11.68; y: 160.263 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 1.251; y: 161.511 }
+ , PathCubic { control1X: 1.251; control1Y: 161.511; control2X: 6.48; control2Y: 161.284; x: 5.327; y: 162.834 }
+ , PathCubic { control1X: 4.174; control1Y: 164.383; control2X: 1.726; control2Y: 163.51; x: 1.726; y: 163.51 }
+ , PathLine { x: 1.251; y: 161.511 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -6.383; y: 162.055 }
+ , PathCubic { control1X: -6.383; control1Y: 162.055; control2X: -1.154; control2Y: 161.829; x: -2.307; y: 163.378 }
+ , PathCubic { control1X: -3.46; control1Y: 164.928; control2X: -5.908; control2Y: 164.054; x: -5.908; y: 164.054 }
+ , PathLine { x: -6.383; y: 162.055 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 35.415; y: 151.513 }
+ , PathCubic { control1X: 35.415; control1Y: 151.513; control2X: 42.375; control2Y: 151.212; x: 40.84; y: 153.274 }
+ , PathCubic { control1X: 39.306; control1Y: 155.336; control2X: 36.047; control2Y: 154.174; x: 36.047; y: 154.174 }
+ , PathLine { x: 35.415; y: 151.513 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 45.73; y: 147.088 }
+ , PathCubic { control1X: 45.73; control1Y: 147.088; control2X: 51.689; control2Y: 143.787; x: 51.155; y: 148.849 }
+ , PathCubic { control1X: 50.885; control1Y: 151.405; control2X: 46.362; control2Y: 149.749; x: 46.362; y: 149.749 }
+ , PathLine { x: 45.73; y: 147.088 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 54.862; y: 144.274 }
+ , PathCubic { control1X: 54.862; control1Y: 144.274; control2X: 62.021; control2Y: 140.573; x: 60.287; y: 146.035 }
+ , PathCubic { control1X: 59.509; control1Y: 148.485; control2X: 55.493; control2Y: 146.935; x: 55.493; y: 146.935 }
+ , PathLine { x: 54.862; y: 144.274 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 64.376; y: 139.449 }
+ , PathCubic { control1X: 64.376; control1Y: 139.449; control2X: 68.735; control2Y: 134.548; x: 69.801; y: 141.21 }
+ , PathCubic { control1X: 70.207; control1Y: 143.748; control2X: 65.008; control2Y: 142.11; x: 65.008; y: 142.11 }
+ , PathLine { x: 64.376; y: 139.449 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 26.834; y: 155.997 }
+ , PathCubic { control1X: 26.834; control1Y: 155.997; control2X: 32.062; control2Y: 155.77; x: 30.91; y: 157.32 }
+ , PathCubic { control1X: 29.757; control1Y: 158.869; control2X: 27.308; control2Y: 157.996; x: 27.308; y: 157.996 }
+ , PathLine { x: 26.834; y: 155.997 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 62.434; y: 34.603 }
+ , PathCubic { control1X: 62.434; control1Y: 34.603; control2X: 61.708; control2Y: 35.268; x: 61.707; y: 34.197 }
+ , PathCubic { control1X: 61.707; control1Y: 33.127; control2X: 79.191; control2Y: 19.863; x: 88.034; y: 18.479 }
+ , PathCubic { control1X: 88.034; control1Y: 18.479; control2X: 71.935; control2Y: 25.208; x: 62.434; y: 34.603 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: 65.4; y: 98.4 }
+ , PathCubic { control1X: 65.4; control1Y: 98.4; control2X: 87.401; control2Y: 120.801; x: 96.601; y: 124.401 }
+ , PathCubic { control1X: 96.601; control1Y: 124.401; control2X: 105.801; control2Y: 135.601; x: 101.801; y: 161.601 }
+ , PathCubic { control1X: 101.801; control1Y: 161.601; control2X: 98.601; control2Y: 169.201; x: 95.401; y: 148.401 }
+ , PathCubic { control1X: 95.401; control1Y: 148.401; control2X: 98.601; control2Y: 123.201; x: 87.401; y: 139.201 }
+ , PathCubic { control1X: 87.401; control1Y: 139.201; control2X: 79; control2Y: 129.301; x: 85.4; y: 129.601 }
+ , PathCubic { control1X: 85.4; control1Y: 129.601; control2X: 88.601; control2Y: 131.601; x: 89.001; y: 130.001 }
+ , PathCubic { control1X: 89.401; control1Y: 128.401; control2X: 81.4; control2Y: 114.801; x: 64.2; y: 100.4 }
+ , PathCubic { control1X: 47; control1Y: 86; control2X: 65.4; control2Y: 98.4; x: 65.4; y: 98.4 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 7; y: 137.201 }
+ , PathCubic { control1X: 7; control1Y: 137.201; control2X: 6.8; control2Y: 135.401; x: 8.6; y: 136.201 }
+ , PathCubic { control1X: 10.4; control1Y: 137.001; control2X: 104.601; control2Y: 143.201; x: 136.201; y: 167.201 }
+ , PathCubic { control1X: 136.201; control1Y: 167.201; control2X: 91.001; control2Y: 144.001; x: 7; y: 137.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 17.4; y: 132.801 }
+ , PathCubic { control1X: 17.4; control1Y: 132.801; control2X: 17.2; control2Y: 131.001; x: 19; y: 131.801 }
+ , PathCubic { control1X: 20.8; control1Y: 132.601; control2X: 157.401; control2Y: 131.601; x: 181.001; y: 164.001 }
+ , PathCubic { control1X: 181.001; control1Y: 164.001; control2X: 159.001; control2Y: 138.801; x: 17.4; y: 132.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 29; y: 128.801 }
+ , PathCubic { control1X: 29; control1Y: 128.801; control2X: 28.8; control2Y: 127.001; x: 30.6; y: 127.801 }
+ , PathCubic { control1X: 32.4; control1Y: 128.601; control2X: 205.801; control2Y: 115.601; x: 229.401; y: 148.001 }
+ , PathCubic { control1X: 229.401; control1Y: 148.001; control2X: 219.801; control2Y: 122.401; x: 29; y: 128.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 39; y: 124.001 }
+ , PathCubic { control1X: 39; control1Y: 124.001; control2X: 38.8; control2Y: 122.201; x: 40.6; y: 123.001 }
+ , PathCubic { control1X: 42.4; control1Y: 123.801; control2X: 164.601; control2Y: 85.2; x: 188.201; y: 117.601 }
+ , PathCubic { control1X: 188.201; control1Y: 117.601; control2X: 174.801; control2Y: 93; x: 39; y: 124.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -19; y: 146.801 }
+ , PathCubic { control1X: -19; control1Y: 146.801; control2X: -19.2; control2Y: 145.001; x: -17.4; y: 145.801 }
+ , PathCubic { control1X: -15.6; control1Y: 146.601; control2X: 2.2; control2Y: 148.801; x: 4.2; y: 187.601 }
+ , PathCubic { control1X: 4.2; control1Y: 187.601; control2X: -3; control2Y: 145.601; x: -19; y: 146.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -27.8; y: 148.401 }
+ , PathCubic { control1X: -27.8; control1Y: 148.401; control2X: -28; control2Y: 146.601; x: -26.2; y: 147.401 }
+ , PathCubic { control1X: -24.4; control1Y: 148.201; control2X: -10.2; control2Y: 143.601; x: -13; y: 182.401 }
+ , PathCubic { control1X: -13; control1Y: 182.401; control2X: -11.8; control2Y: 147.201; x: -27.8; y: 148.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -35.8; y: 148.801 }
+ , PathCubic { control1X: -35.8; control1Y: 148.801; control2X: -36; control2Y: 147.001; x: -34.2; y: 147.801 }
+ , PathCubic { control1X: -32.4; control1Y: 148.601; control2X: -17; control2Y: 149.201; x: -29.4; y: 171.601 }
+ , PathCubic { control1X: -29.4; control1Y: 171.601; control2X: -19.8; control2Y: 147.601; x: -35.8; y: 148.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 11.526; y: 104.465 }
+ , PathCubic { control1X: 11.526; control1Y: 104.465; control2X: 11.082; control2Y: 106.464; x: 12.631; y: 105.247 }
+ , PathCubic { control1X: 28.699; control1Y: 92.622; control2X: 61.141; control2Y: 33.72; x: 116.826; y: 28.086 }
+ , PathCubic { control1X: 116.826; control1Y: 28.086; control2X: 78.518; control2Y: 15.976; x: 11.526; y: 104.465 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 22.726; y: 102.665 }
+ , PathCubic { control1X: 22.726; control1Y: 102.665; control2X: 21.363; control2Y: 101.472; x: 23.231; y: 100.847 }
+ , PathCubic { control1X: 25.099; control1Y: 100.222; control2X: 137.541; control2Y: 27.72; x: 176.826; y: 35.686 }
+ , PathCubic { control1X: 176.826; control1Y: 35.686; control2X: 149.719; control2Y: 28.176; x: 22.726; y: 102.665 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 1.885; y: 108.767 }
+ , PathCubic { control1X: 1.885; control1Y: 108.767; control2X: 1.376; control2Y: 110.366; x: 3.087; y: 109.39 }
+ , PathCubic { control1X: 12.062; control1Y: 104.27; control2X: 15.677; control2Y: 47.059; x: 59.254; y: 45.804 }
+ , PathCubic { control1X: 59.254; control1Y: 45.804; control2X: 26.843; control2Y: 31.09; x: 1.885; y: 108.767 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -18.038; y: 119.793 }
+ , PathCubic { control1X: -18.038; control1Y: 119.793; control2X: -19.115; control2Y: 121.079; x: -17.162; y: 120.825 }
+ , PathCubic { control1X: -6.916; control1Y: 119.493; control2X: 14.489; control2Y: 78.222; x: 58.928; y: 83.301 }
+ , PathCubic { control1X: 58.928; control1Y: 83.301; control2X: 26.962; control2Y: 68.955; x: -18.038; y: 119.793 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -6.8; y: 113.667 }
+ , PathCubic { control1X: -6.8; control1Y: 113.667; control2X: -7.611; control2Y: 115.136; x: -5.742; y: 114.511 }
+ , PathCubic { control1X: 4.057; control1Y: 111.237; control2X: 17.141; control2Y: 66.625; x: 61.729; y: 63.078 }
+ , PathCubic { control1X: 61.729; control1Y: 63.078; control2X: 27.603; control2Y: 55.135; x: -6.8; y: 113.667 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -25.078; y: 124.912 }
+ , PathCubic { control1X: -25.078; control1Y: 124.912; control2X: -25.951; control2Y: 125.954; x: -24.369; y: 125.748 }
+ , PathCubic { control1X: -16.07; control1Y: 124.669; control2X: 1.268; control2Y: 91.24; x: 37.264; y: 95.354 }
+ , PathCubic { control1X: 37.264; control1Y: 95.354; control2X: 11.371; control2Y: 83.734; x: -25.078; y: 124.912 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -32.677; y: 130.821 }
+ , PathCubic { control1X: -32.677; control1Y: 130.821; control2X: -33.682; control2Y: 131.866; x: -32.091; y: 131.748 }
+ , PathCubic { control1X: -27.923; control1Y: 131.439; control2X: 2.715; control2Y: 98.36; x: 21.183; y: 113.862 }
+ , PathCubic { control1X: 21.183; control1Y: 113.862; control2X: 9.168; control2Y: 95.139; x: -32.677; y: 130.821 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 36.855; y: 98.898 }
+ , PathCubic { control1X: 36.855; control1Y: 98.898; control2X: 35.654; control2Y: 97.543; x: 37.586; y: 97.158 }
+ , PathCubic { control1X: 39.518; control1Y: 96.774; control2X: 160.221; control2Y: 39.061; x: 198.184; y: 51.927 }
+ , PathCubic { control1X: 198.184; control1Y: 51.927; control2X: 172.243; control2Y: 41.053; x: 36.855; y: 98.898 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 3.4; y: 163.201 }
+ , PathCubic { control1X: 3.4; control1Y: 163.201; control2X: 3.2; control2Y: 161.401; x: 5; y: 162.201 }
+ , PathCubic { control1X: 6.8; control1Y: 163.001; control2X: 22.2; control2Y: 163.601; x: 9.8; y: 186.001 }
+ , PathCubic { control1X: 9.8; control1Y: 186.001; control2X: 19.4; control2Y: 162.001; x: 3.4; y: 163.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 13.8; y: 161.601 }
+ , PathCubic { control1X: 13.8; control1Y: 161.601; control2X: 13.6; control2Y: 159.801; x: 15.4; y: 160.601 }
+ , PathCubic { control1X: 17.2; control1Y: 161.401; control2X: 35; control2Y: 163.601; x: 37; y: 202.401 }
+ , PathCubic { control1X: 37; control1Y: 202.401; control2X: 29.8; control2Y: 160.401; x: 13.8; y: 161.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 20.6; y: 160.001 }
+ , PathCubic { control1X: 20.6; control1Y: 160.001; control2X: 20.4; control2Y: 158.201; x: 22.2; y: 159.001 }
+ , PathCubic { control1X: 24; control1Y: 159.801; control2X: 48.6; control2Y: 163.201; x: 72.2; y: 195.601 }
+ , PathCubic { control1X: 72.2; control1Y: 195.601; control2X: 36.6; control2Y: 158.801; x: 20.6; y: 160.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 28.225; y: 157.972 }
+ , PathCubic { control1X: 28.225; control1Y: 157.972; control2X: 27.788; control2Y: 156.214; x: 29.678; y: 156.768 }
+ , PathCubic { control1X: 31.568; control1Y: 157.322; control2X: 52.002; control2Y: 155.423; x: 90.099; y: 189.599 }
+ , PathCubic { control1X: 90.099; control1Y: 189.599; control2X: 43.924; control2Y: 154.656; x: 28.225; y: 157.972 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 38.625; y: 153.572 }
+ , PathCubic { control1X: 38.625; control1Y: 153.572; control2X: 38.188; control2Y: 151.814; x: 40.078; y: 152.368 }
+ , PathCubic { control1X: 41.968; control1Y: 152.922; control2X: 76.802; control2Y: 157.423; x: 128.499; y: 192.399 }
+ , PathCubic { control1X: 128.499; control1Y: 192.399; control2X: 54.324; control2Y: 150.256; x: 38.625; y: 153.572 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -1.8; y: 142.001 }
+ , PathCubic { control1X: -1.8; control1Y: 142.001; control2X: -2; control2Y: 140.201; x: -0.2; y: 141.001 }
+ , PathCubic { control1X: 1.6; control1Y: 141.801; control2X: 55; control2Y: 144.401; x: 85.4; y: 171.201 }
+ , PathCubic { control1X: 85.4; control1Y: 171.201; control2X: 50.499; control2Y: 146.426; x: -1.8; y: 142.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: -11.8; y: 146.001 }
+ , PathCubic { control1X: -11.8; control1Y: 146.001; control2X: -12; control2Y: 144.201; x: -10.2; y: 145.001 }
+ , PathCubic { control1X: -8.4; control1Y: 145.801; control2X: 16.2; control2Y: 149.201; x: 39.8; y: 181.601 }
+ , PathCubic { control1X: 39.8; control1Y: 181.601; control2X: 4.2; control2Y: 144.801; x: -11.8; y: 146.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 49.503; y: 148.962 }
+ , PathCubic { control1X: 49.503; control1Y: 148.962; control2X: 48.938; control2Y: 147.241; x: 50.864; y: 147.655 }
+ , PathCubic { control1X: 52.79; control1Y: 148.068; control2X: 87.86; control2Y: 150.004; x: 141.981; y: 181.098 }
+ , PathCubic { control1X: 141.981; control1Y: 181.098; control2X: 64.317; control2Y: 146.704; x: 49.503; y: 148.962 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 57.903; y: 146.562 }
+ , PathCubic { control1X: 57.903; control1Y: 146.562; control2X: 57.338; control2Y: 144.841; x: 59.264; y: 145.255 }
+ , PathCubic { control1X: 61.19; control1Y: 145.668; control2X: 96.26; control2Y: 147.604; x: 150.381; y: 178.698 }
+ , PathCubic { control1X: 150.381; control1Y: 178.698; control2X: 73.317; control2Y: 143.904; x: 57.903; y: 146.562 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#ffffff"
+ strokeColor: "#000000"
+ strokeWidth: 0.1
+ delegate: [PathMove { x: 67.503; y: 141.562 }
+ , PathCubic { control1X: 67.503; control1Y: 141.562; control2X: 66.938; control2Y: 139.841; x: 68.864; y: 140.255 }
+ , PathCubic { control1X: 70.79; control1Y: 140.668; control2X: 113.86; control2Y: 145.004; x: 203.582; y: 179.298 }
+ , PathCubic { control1X: 203.582; control1Y: 179.298; control2X: 82.917; control2Y: 138.904; x: 67.503; y: 141.562 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -43.8; y: 148.401 }
+ , PathCubic { control1X: -43.8; control1Y: 148.401; control2X: -38.6; control2Y: 148.001; x: -39.8; y: 149.601 }
+ , PathCubic { control1X: -41; control1Y: 151.201; control2X: -43.4; control2Y: 150.401; x: -43.4; y: 150.401 }
+ , PathLine { x: -43.8; y: 148.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -13; y: 162.401 }
+ , PathCubic { control1X: -13; control1Y: 162.401; control2X: -7.8; control2Y: 162.001; x: -9; y: 163.601 }
+ , PathCubic { control1X: -10.2; control1Y: 165.201; control2X: -12.6; control2Y: 164.401; x: -12.6; y: 164.401 }
+ , PathLine { x: -13; y: 162.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -21.8; y: 162.001 }
+ , PathCubic { control1X: -21.8; control1Y: 162.001; control2X: -16.6; control2Y: 161.601; x: -17.8; y: 163.201 }
+ , PathCubic { control1X: -19; control1Y: 164.801; control2X: -21.4; control2Y: 164.001; x: -21.4; y: 164.001 }
+ , PathLine { x: -21.8; y: 162.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -117.169; y: 150.182 }
+ , PathCubic { control1X: -117.169; control1Y: 150.182; control2X: -112.124; control2Y: 151.505; x: -113.782; y: 152.624 }
+ , PathCubic { control1X: -115.439; control1Y: 153.744; control2X: -117.446; control2Y: 152.202; x: -117.446; y: 152.202 }
+ , PathLine { x: -117.169; y: 150.182 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -115.169; y: 140.582 }
+ , PathCubic { control1X: -115.169; control1Y: 140.582; control2X: -110.124; control2Y: 141.905; x: -111.782; y: 143.024 }
+ , PathCubic { control1X: -113.439; control1Y: 144.144; control2X: -115.446; control2Y: 142.602; x: -115.446; y: 142.602 }
+ , PathLine { x: -115.169; y: 140.582 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#000000"
+ strokeWidth: -1
+ delegate: [PathMove { x: -122.369; y: 136.182 }
+ , PathCubic { control1X: -122.369; control1Y: 136.182; control2X: -117.324; control2Y: 137.505; x: -118.982; y: 138.624 }
+ , PathCubic { control1X: -120.639; control1Y: 139.744; control2X: -122.646; control2Y: 138.202; x: -122.646; y: 138.202 }
+ , PathLine { x: -122.369; y: 136.182 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -42.6; y: 211.201 }
+ , PathCubic { control1X: -42.6; control1Y: 211.201; control2X: -44.2; control2Y: 211.201; x: -48.2; y: 213.201 }
+ , PathCubic { control1X: -50.2; control1Y: 213.201; control2X: -61.4; control2Y: 216.801; x: -67; y: 226.801 }
+ , PathCubic { control1X: -67; control1Y: 226.801; control2X: -54.6; control2Y: 217.201; x: -42.6; y: 211.201 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 45.116; y: 303.847 }
+ , PathCubic { control1X: 45.257; control1Y: 304.105; control2X: 45.312; control2Y: 304.525; x: 45.604; y: 304.542 }
+ , PathCubic { control1X: 46.262; control1Y: 304.582; control2X: 47.495; control2Y: 304.883; x: 47.37; y: 304.247 }
+ , PathCubic { control1X: 46.522; control1Y: 299.941; control2X: 45.648; control2Y: 295.004; x: 41.515; y: 293.197 }
+ , PathCubic { control1X: 40.876; control1Y: 292.918; control2X: 39.434; control2Y: 293.331; x: 39.36; y: 294.215 }
+ , PathCubic { control1X: 39.233; control1Y: 295.739; control2X: 39.116; control2Y: 297.088; x: 39.425; y: 298.554 }
+ , PathCubic { control1X: 39.725; control1Y: 299.975; control2X: 41.883; control2Y: 299.985; x: 42.8; y: 298.601 }
+ , PathCubic { control1X: 43.736; control1Y: 300.273; control2X: 44.168; control2Y: 302.116; x: 45.116; y: 303.847 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 34.038; y: 308.581 }
+ , PathCubic { control1X: 34.786; control1Y: 309.994; control2X: 34.659; control2Y: 311.853; x: 36.074; y: 312.416 }
+ , PathCubic { control1X: 36.814; control1Y: 312.71; control2X: 38.664; control2Y: 311.735; x: 38.246; y: 310.661 }
+ , PathCubic { control1X: 37.444; control1Y: 308.6; control2X: 37.056; control2Y: 306.361; x: 35.667; y: 304.55 }
+ , PathCubic { control1X: 35.467; control1Y: 304.288; control2X: 35.707; control2Y: 303.755; x: 35.547; y: 303.427 }
+ , PathCubic { control1X: 34.953; control1Y: 302.207; control2X: 33.808; control2Y: 301.472; x: 32.4; y: 301.801 }
+ , PathCubic { control1X: 31.285; control1Y: 304.004; control2X: 32.433; control2Y: 306.133; x: 33.955; y: 307.842 }
+ , PathCubic { control1X: 34.091; control1Y: 307.994; control2X: 33.925; control2Y: 308.37; x: 34.038; y: 308.581 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -5.564; y: 303.391 }
+ , PathCubic { control1X: -5.672; control1Y: 303.014; control2X: -5.71; control2Y: 302.551; x: -5.545; y: 302.23 }
+ , PathCubic { control1X: -5.014; control1Y: 301.197; control2X: -4.221; control2Y: 300.075; x: -4.558; y: 299.053 }
+ , PathCubic { control1X: -4.906; control1Y: 297.997; control2X: -6.022; control2Y: 298.179; x: -6.672; y: 298.748 }
+ , PathCubic { control1X: -7.807; control1Y: 299.742; control2X: -7.856; control2Y: 301.568; x: -8.547; y: 302.927 }
+ , PathCubic { control1X: -8.743; control1Y: 303.313; control2X: -8.692; control2Y: 303.886; x: -9.133; y: 304.277 }
+ , PathCubic { control1X: -9.607; control1Y: 304.698; control2X: -10.047; control2Y: 306.222; x: -9.951; y: 306.793 }
+ , PathCubic { control1X: -9.898; control1Y: 307.106; control2X: -10.081; control2Y: 317.014; x: -9.859; y: 316.751 }
+ , PathCubic { control1X: -9.24; control1Y: 316.018; control2X: -6.19; control2Y: 306.284; x: -6.121; y: 305.392 }
+ , PathCubic { control1X: -6.064; control1Y: 304.661; control2X: -5.332; control2Y: 304.196; x: -5.564; y: 303.391 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -31.202; y: 296.599 }
+ , PathCubic { control1X: -28.568; control1Y: 294.1; control2X: -25.778; control2Y: 291.139; x: -26.22; y: 287.427 }
+ , PathCubic { control1X: -26.336; control1Y: 286.451; control2X: -28.111; control2Y: 286.978; x: -28.298; y: 287.824 }
+ , PathCubic { control1X: -29.1; control1Y: 291.449; control2X: -31.139; control2Y: 294.11; x: -33.707; y: 296.502 }
+ , PathCubic { control1X: -35.903; control1Y: 298.549; control2X: -37.765; control2Y: 304.893; x: -38; y: 305.401 }
+ , PathCubic { control1X: -34.303; control1Y: 300.145; control2X: -32.046; control2Y: 297.399; x: -31.202; y: 296.599 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -44.776; y: 290.635 }
+ , PathCubic { control1X: -44.253; control1Y: 290.265; control2X: -44.555; control2Y: 289.774; x: -44.338; y: 289.442 }
+ , PathCubic { control1X: -43.385; control1Y: 287.984; control2X: -42.084; control2Y: 286.738; x: -42.066; y: 285 }
+ , PathCubic { control1X: -42.063; control1Y: 284.723; control2X: -42.441; control2Y: 284.414; x: -42.776; y: 284.638 }
+ , PathCubic { control1X: -43.053; control1Y: 284.822; control2X: -43.395; control2Y: 284.952; x: -43.503; y: 285.082 }
+ , PathCubic { control1X: -45.533; control1Y: 287.531; control2X: -46.933; control2Y: 290.202; x: -48.376; y: 293.014 }
+ , PathCubic { control1X: -48.559; control1Y: 293.371; control2X: -49.703; control2Y: 297.862; x: -49.39; y: 297.973 }
+ , PathCubic { control1X: -49.151; control1Y: 298.058; control2X: -47.431; control2Y: 293.877; x: -47.221; y: 293.763 }
+ , PathCubic { control1X: -45.958; control1Y: 293.077; control2X: -45.946; control2Y: 291.462; x: -44.776; y: 290.635 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -28.043; y: 310.179 }
+ , PathCubic { control1X: -27.599; control1Y: 309.31; control2X: -26.023; control2Y: 308.108; x: -26.136; y: 307.219 }
+ , PathCubic { control1X: -26.254; control1Y: 306.291; control2X: -25.786; control2Y: 304.848; x: -26.698; y: 305.536 }
+ , PathCubic { control1X: -27.955; control1Y: 306.484; control2X: -31.404; control2Y: 307.833; x: -31.674; y: 313.641 }
+ , PathCubic { control1X: -31.7; control1Y: 314.212; control2X: -28.726; control2Y: 311.519; x: -28.043; y: 310.179 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -13.6; y: 293.001 }
+ , PathCubic { control1X: -13.2; control1Y: 292.333; control2X: -12.492; control2Y: 292.806; x: -12.033; y: 292.543 }
+ , PathCubic { control1X: -11.385; control1Y: 292.171; control2X: -10.774; control2Y: 291.613; x: -10.482; y: 290.964 }
+ , PathCubic { control1X: -9.512; control1Y: 288.815; control2X: -7.743; control2Y: 286.995; x: -7.6; y: 284.601 }
+ , PathCubic { control1X: -9.091; control1Y: 283.196; control2X: -9.77; control2Y: 285.236; x: -10.4; y: 286.201 }
+ , PathCubic { control1X: -11.723; control1Y: 284.554; control2X: -12.722; control2Y: 286.428; x: -14.022; y: 286.947 }
+ , PathCubic { control1X: -14.092; control1Y: 286.975; control2X: -14.305; control2Y: 286.628; x: -14.38; y: 286.655 }
+ , PathCubic { control1X: -15.557; control1Y: 287.095; control2X: -16.237; control2Y: 288.176; x: -17.235; y: 288.957 }
+ , PathCubic { control1X: -17.406; control1Y: 289.091; control2X: -17.811; control2Y: 288.911; x: -17.958; y: 289.047 }
+ , PathCubic { control1X: -18.61; control1Y: 289.65; control2X: -19.583; control2Y: 289.975; x: -19.863; y: 290.657 }
+ , PathCubic { control1X: -20.973; control1Y: 293.364; control2X: -24.113; control2Y: 295.459; x: -26; y: 303.001 }
+ , PathCubic { control1X: -25.619; control1Y: 303.91; control2X: -21.488; control2Y: 296.359; x: -21.001; y: 295.661 }
+ , PathCubic { control1X: -20.165; control1Y: 294.465; control2X: -20.047; control2Y: 297.322; x: -18.771; y: 296.656 }
+ , PathCubic { control1X: -18.72; control1Y: 296.629; control2X: -18.534; control2Y: 296.867; x: -18.4; y: 297.001 }
+ , PathCubic { control1X: -18.206; control1Y: 296.721; control2X: -17.988; control2Y: 296.492; x: -17.6; y: 296.601 }
+ , PathCubic { control1X: -17.6; control1Y: 296.201; control2X: -17.734; control2Y: 295.645; x: -17.533; y: 295.486 }
+ , PathCubic { control1X: -16.296; control1Y: 294.509; control2X: -16.38; control2Y: 293.441; x: -15.6; y: 292.201 }
+ , PathCubic { control1X: -15.142; control1Y: 292.99; control2X: -14.081; control2Y: 292.271; x: -13.6; y: 293.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 46.2; y: 347.401 }
+ , PathCubic { control1X: 46.2; control1Y: 347.401; control2X: 53.6; control2Y: 327.001; x: 49.2; y: 315.801 }
+ , PathCubic { control1X: 49.2; control1Y: 315.801; control2X: 60.6; control2Y: 337.401; x: 56; y: 348.601 }
+ , PathCubic { control1X: 56; control1Y: 348.601; control2X: 55.6; control2Y: 338.201; x: 51.6; y: 333.201 }
+ , PathCubic { control1X: 51.6; control1Y: 333.201; control2X: 47.6; control2Y: 346.001; x: 46.2; y: 347.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 31.4; y: 344.801 }
+ , PathCubic { control1X: 31.4; control1Y: 344.801; control2X: 36.8; control2Y: 336.001; x: 28.8; y: 317.601 }
+ , PathCubic { control1X: 28.8; control1Y: 317.601; control2X: 28; control2Y: 338.001; x: 21.2; y: 349.001 }
+ , PathCubic { control1X: 21.2; control1Y: 349.001; control2X: 35.4; control2Y: 328.801; x: 31.4; y: 344.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 21.4; y: 342.801 }
+ , PathCubic { control1X: 21.4; control1Y: 342.801; control2X: 21.2; control2Y: 322.801; x: 21.6; y: 319.801 }
+ , PathCubic { control1X: 21.6; control1Y: 319.801; control2X: 17.8; control2Y: 336.401; x: 7.6; y: 346.001 }
+ , PathCubic { control1X: 7.6; control1Y: 346.001; control2X: 22; control2Y: 334.001; x: 21.4; y: 342.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 11.8; y: 310.801 }
+ , PathCubic { control1X: 11.8; control1Y: 310.801; control2X: 17.8; control2Y: 324.401; x: 7.8; y: 342.801 }
+ , PathCubic { control1X: 7.8; control1Y: 342.801; control2X: 14.2; control2Y: 330.601; x: 9.4; y: 323.601 }
+ , PathCubic { control1X: 9.4; control1Y: 323.601; control2X: 12; control2Y: 320.201; x: 11.8; y: 310.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -7.4; y: 342.401 }
+ , PathCubic { control1X: -7.4; control1Y: 342.401; control2X: -8.4; control2Y: 326.801; x: -6.6; y: 324.601 }
+ , PathCubic { control1X: -6.6; control1Y: 324.601; control2X: -6.4; control2Y: 318.201; x: -6.8; y: 317.201 }
+ , PathCubic { control1X: -6.8; control1Y: 317.201; control2X: -2.8; control2Y: 311.001; x: -2.6; y: 318.401 }
+ , PathCubic { control1X: -2.6; control1Y: 318.401; control2X: -1.2; control2Y: 326.201; x: 1.6; y: 330.801 }
+ , PathCubic { control1X: 1.6; control1Y: 330.801; control2X: 5.2; control2Y: 336.201; x: 5; y: 342.601 }
+ , PathCubic { control1X: 5; control1Y: 342.601; control2X: -5; control2Y: 312.401; x: -7.4; y: 342.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -11; y: 314.801 }
+ , PathCubic { control1X: -11; control1Y: 314.801; control2X: -17.6; control2Y: 325.601; x: -19.4; y: 344.601 }
+ , PathCubic { control1X: -19.4; control1Y: 344.601; control2X: -20.8; control2Y: 338.401; x: -17; y: 324.001 }
+ , PathCubic { control1X: -17; control1Y: 324.001; control2X: -12.8; control2Y: 308.601; x: -11; y: 314.801 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -32.8; y: 334.601 }
+ , PathCubic { control1X: -32.8; control1Y: 334.601; control2X: -27.8; control2Y: 329.201; x: -26.4; y: 324.201 }
+ , PathCubic { control1X: -26.4; control1Y: 324.201; control2X: -22.8; control2Y: 308.401; x: -29.2; y: 317.001 }
+ , PathCubic { control1X: -29.2; control1Y: 317.001; control2X: -29; control2Y: 325.001; x: -37.2; y: 332.401 }
+ , PathCubic { control1X: -37.2; control1Y: 332.401; control2X: -32.4; control2Y: 330.001; x: -32.8; y: 334.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -38.6; y: 329.601 }
+ , PathCubic { control1X: -38.6; control1Y: 329.601; control2X: -35.2; control2Y: 312.201; x: -34.4; y: 311.401 }
+ , PathCubic { control1X: -34.4; control1Y: 311.401; control2X: -32.6; control2Y: 308.001; x: -35.4; y: 311.201 }
+ , PathCubic { control1X: -35.4; control1Y: 311.201; control2X: -44.2; control2Y: 330.401; x: -48.2; y: 337.001 }
+ , PathCubic { control1X: -48.2; control1Y: 337.001; control2X: -40.2; control2Y: 327.801; x: -38.6; y: 329.601 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -44.4; y: 313.001 }
+ , PathCubic { control1X: -44.4; control1Y: 313.001; control2X: -32.8; control2Y: 290.601; x: -54.6; y: 316.401 }
+ , PathCubic { control1X: -54.6; control1Y: 316.401; control2X: -43.6; control2Y: 306.601; x: -44.4; y: 313.001 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: -59.8; y: 298.401 }
+ , PathCubic { control1X: -59.8; control1Y: 298.401; control2X: -55; control2Y: 279.601; x: -52.4; y: 279.801 }
+ , PathCubic { control1X: -52.4; control1Y: 279.801; control2X: -44.2; control2Y: 270.801; x: -50.8; y: 281.401 }
+ , PathCubic { control1X: -50.8; control1Y: 281.401; control2X: -56.8; control2Y: 291.001; x: -56.2; y: 300.801 }
+ , PathCubic { control1X: -56.2; control1Y: 300.801; control2X: -56.8; control2Y: 291.201; x: -59.8; y: 298.401 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 270.5; y: 287 }
+ , PathCubic { control1X: 270.5; control1Y: 287; control2X: 258.5; control2Y: 277; x: 256; y: 273.5 }
+ , PathCubic { control1X: 256; control1Y: 273.5; control2X: 269.5; control2Y: 292; x: 269.5; y: 299 }
+ , PathCubic { control1X: 269.5; control1Y: 299; control2X: 272; control2Y: 291.5; x: 270.5; y: 287 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 276; y: 265 }
+ , PathCubic { control1X: 276; control1Y: 265; control2X: 255; control2Y: 250; x: 251.5; y: 242.5 }
+ , PathCubic { control1X: 251.5; control1Y: 242.5; control2X: 278; control2Y: 272; x: 278; y: 276.5 }
+ , PathCubic { control1X: 278; control1Y: 276.5; control2X: 278.5; control2Y: 267.5; x: 276; y: 265 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 293; y: 111 }
+ , PathCubic { control1X: 293; control1Y: 111; control2X: 281; control2Y: 103; x: 279.5; y: 105 }
+ , PathCubic { control1X: 279.5; control1Y: 105; control2X: 290; control2Y: 111.5; x: 292.5; y: 120 }
+ , PathCubic { control1X: 292.5; control1Y: 120; control2X: 291; control2Y: 111; x: 293; y: 111 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "#cccccc"
+ strokeWidth: -1
+ delegate: [PathMove { x: 301.5; y: 191.5 }
+ , PathLine { x: 284; y: 179.5 }
+ , PathCubic { control1X: 284; control1Y: 179.5; control2X: 303; control2Y: 196.5; x: 303.5; y: 200.5 }
+ , PathLine { x: 301.5; y: 191.5 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ delegate: [PathMove { x: -89.25; y: 169 }
+ , PathLine { x: -67.25; y: 173.75 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ delegate: [PathMove { x: -39; y: 331 }
+ , PathCubic { control1X: -39; control1Y: 331; control2X: -39.5; control2Y: 327.5; x: -48.5; y: 338 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ delegate: [PathMove { x: -33.5; y: 336 }
+ , PathCubic { control1X: -33.5; control1Y: 336; control2X: -31.5; control2Y: 329.5; x: -38; y: 334 }
+ ]
+ }
+
+ ControlledShape {
+ fillColor: "transparent"
+ strokeColor: "#000000"
+ strokeWidth: 1
+ delegate: [PathMove { x: 20.5; y: 344.5 }
+ , PathCubic { control1X: 20.5; control1Y: 344.5; control2X: 22; control2Y: 333.5; x: 10.5; y: 346.5 }
+ ]
+ }
+}
diff --git a/tests/manual/painterpathquickshape/zoombox.frag b/tests/manual/painterpathquickshape/zoombox.frag
new file mode 100644
index 0000000000..e7754cb75c
--- /dev/null
+++ b/tests/manual/painterpathquickshape/zoombox.frag
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float textureWidth;
+ float textureHeight;
+};
+layout(binding = 1) uniform sampler2D src;
+
+
+void main(void)
+{
+
+ float xPixelIndex = (round((textureWidth - 1.) * qt_TexCoord0.x) + 0.5) / textureWidth;
+ float yPixelIndex = (round((textureHeight - 1.) * qt_TexCoord0.y) + 0.5) / textureHeight;
+
+ fragColor = texture(src, vec2(xPixelIndex, yPixelIndex)) * qt_Opacity;
+}
diff --git a/tests/manual/pointer/content/CheckBox.qml b/tests/manual/pointer/content/CheckBox.qml
index fd89337963..bd5661d76b 100644
--- a/tests/manual/pointer/content/CheckBox.qml
+++ b/tests/manual/pointer/content/CheckBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/pointer/content/FakeFlickable.qml b/tests/manual/pointer/content/FakeFlickable.qml
new file mode 100644
index 0000000000..413bc9979d
--- /dev/null
+++ b/tests/manual/pointer/content/FakeFlickable.qml
@@ -0,0 +1,102 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import Qt.labs.animation
+
+Item {
+ id: root
+ objectName: "viewport"
+ default property alias data: __contentItem.data
+ property alias velocity: anim.velocity
+ property alias contentX: __contentItem.x // sign is reversed compared to Flickable.contentX
+ property alias contentY: __contentItem.y // sign is reversed compared to Flickable.contentY
+ property alias contentWidth: __contentItem.width
+ property alias contentHeight: __contentItem.height
+ signal flickStarted
+ signal flickEnded
+
+ Item {
+ id: __contentItem
+ objectName: "__contentItem"
+ width: childrenRect.width
+ height: childrenRect.height
+
+ BoundaryRule on x {
+ id: xbr
+ minimum: root.width - __contentItem.width
+ maximum: 0
+ minimumOvershoot: 100
+ maximumOvershoot: 100
+ overshootFilter: BoundaryRule.Peak
+ }
+
+ BoundaryRule on y {
+ id: ybr
+ minimum: root.height - __contentItem.height
+ maximum: 0
+ minimumOvershoot: 100
+ maximumOvershoot: 100
+ overshootFilter: BoundaryRule.Peak
+ }
+
+ DragHandler {
+ id: dragHandler
+ onActiveChanged:
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ var vel = centroid.velocity
+ if (xbr.returnToBounds())
+ vel.x = 0
+ if (ybr.returnToBounds())
+ vel.y = 0
+ if (vel.x !== 0 || vel.y !== 0)
+ anim.restart(vel)
+ else
+ root.flickEnded()
+ }
+ }
+ WheelHandler {
+ rotationScale: 15
+ property: "x"
+ orientation: Qt.Horizontal
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onActiveChanged:
+ // emitting signals in both instances is redundant but hard to avoid
+ // when the touchpad is flicking along both axes
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ xbr.returnToBounds()
+ root.flickEnded()
+ }
+ }
+ WheelHandler {
+ rotationScale: 15
+ property: "y"
+ orientation: Qt.Vertical
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onActiveChanged:
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ ybr.returnToBounds()
+ root.flickEnded()
+ }
+ }
+ MomentumAnimation {
+ id: anim
+ target: __contentItem
+ onStarted: root.flickStarted()
+ onStopped: {
+ xbr.returnToBounds()
+ ybr.returnToBounds()
+ root.flickEnded()
+ }
+ }
+ }
+}
diff --git a/tests/manual/pointer/content/FlashAnimation.qml b/tests/manual/pointer/content/FlashAnimation.qml
index 09d0ab5e4e..ef19f4d427 100644
--- a/tests/manual/pointer/content/FlashAnimation.qml
+++ b/tests/manual/pointer/content/FlashAnimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/pointer/content/MomentumAnimation.qml b/tests/manual/pointer/content/MomentumAnimation.qml
index 23d2a60623..2ea6a55379 100644
--- a/tests/manual/pointer/content/MomentumAnimation.qml
+++ b/tests/manual/pointer/content/MomentumAnimation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/pointer/content/MouseAreaButton.qml b/tests/manual/pointer/content/MouseAreaButton.qml
index b495d59066..2c1ddb7295 100644
--- a/tests/manual/pointer/content/MouseAreaButton.qml
+++ b/tests/manual/pointer/content/MouseAreaButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.12
diff --git a/tests/manual/pointer/content/MouseAreaSlider.qml b/tests/manual/pointer/content/MouseAreaSlider.qml
index e19e46d621..9f0553c9a7 100644
--- a/tests/manual/pointer/content/MouseAreaSlider.qml
+++ b/tests/manual/pointer/content/MouseAreaSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/pointer/content/MptaButton.qml b/tests/manual/pointer/content/MptaButton.qml
index 19b9d907bb..444269b1ba 100644
--- a/tests/manual/pointer/content/MptaButton.qml
+++ b/tests/manual/pointer/content/MptaButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.12
diff --git a/tests/manual/pointer/content/Slider.qml b/tests/manual/pointer/content/Slider.qml
index f20b634778..bcf19b6b4b 100644
--- a/tests/manual/pointer/content/Slider.qml
+++ b/tests/manual/pointer/content/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import Qt.labs.animation 1.0
diff --git a/tests/manual/pointer/content/TapHandlerButton.qml b/tests/manual/pointer/content/TapHandlerButton.qml
index 1cfc8d8bc3..84a45b777f 100644
--- a/tests/manual/pointer/content/TapHandlerButton.qml
+++ b/tests/manual/pointer/content/TapHandlerButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.12
diff --git a/tests/manual/pointer/content/TextBox.qml b/tests/manual/pointer/content/TextBox.qml
index 9eb77a85a3..1443019dc9 100644
--- a/tests/manual/pointer/content/TextBox.qml
+++ b/tests/manual/pointer/content/TextBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/pointer/content/TouchpointFeedbackSprite.qml b/tests/manual/pointer/content/TouchpointFeedbackSprite.qml
index 36efc5195b..2bc4a805ce 100644
--- a/tests/manual/pointer/content/TouchpointFeedbackSprite.qml
+++ b/tests/manual/pointer/content/TouchpointFeedbackSprite.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/pointer/flickablesWithHandlers.qml b/tests/manual/pointer/flickablesWithHandlers.qml
index 08826ed858..abfe57bfac 100644
--- a/tests/manual/pointer/flickablesWithHandlers.qml
+++ b/tests/manual/pointer/flickablesWithHandlers.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "content"
diff --git a/tests/manual/pointer/hoverpropagation.qml b/tests/manual/pointer/hoverpropagation.qml
index f79b9e754e..42d431c4ea 100644
--- a/tests/manual/pointer/hoverpropagation.qml
+++ b/tests/manual/pointer/hoverpropagation.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
import QtQuick.Window
diff --git a/tests/manual/pointer/map2.qml b/tests/manual/pointer/map2.qml
index 0c83e58a26..fa2f1157df 100644
--- a/tests/manual/pointer/map2.qml
+++ b/tests/manual/pointer/map2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/manual/pointer/photosurface.qml b/tests/manual/pointer/photosurface.qml
index 9bf5d5875a..489c11e12d 100644
--- a/tests/manual/pointer/photosurface.qml
+++ b/tests/manual/pointer/photosurface.qml
@@ -1,9 +1,10 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-import QtQuick 2.12
-import QtQuick.Dialogs 1.0
-import Qt.labs.folderlistmodel 1.0
+import QtCore
+import QtQuick
+import QtQuick.Dialogs
+import Qt.labs.folderlistmodel
import "content"
Rectangle {
@@ -11,36 +12,37 @@ Rectangle {
visible: true
width: 1024; height: 600
color: "black"
- property int highestZ: 0
property real defaultSize: 200
property real surfaceViewportRatio: 1.5
- FileDialog {
- id: fileDialog
+ FolderDialog {
+ id: folderDialog
title: "Choose a folder with some images"
- selectFolder: true
- onAccepted: folderModel.folder = fileUrl + "/"
+ onAccepted: folderModel.folder = selectedFolder + "/"
}
Shortcut {
id: openShortcut
sequence: StandardKey.Open
- onActivated: fileDialog.open()
+ onActivated: folderDialog.open()
}
FakeFlickable {
id: flick
anchors.fill: parent
- contentWidth: width * 2
- contentHeight: height * 2
+ contentWidth: width * root.surfaceViewportRatio
+ contentHeight: height * root.surfaceViewportRatio
+ property int highestZ: 0
Repeater {
model: FolderListModel {
id: folderModel
- folder: "resources/"
objectName: "folderModel"
showDirs: false
nameFilters: ["*.png", "*.jpg", "*.gif"]
}
- Rectangle {
+ delegate: Rectangle {
+ required property string fileModified
+ required property string fileName
+ required property string fileUrl
id: photoFrame
objectName: "frame-" + fileName
width: image.width * (1 + 0.10 * image.height / image.width)
@@ -58,21 +60,34 @@ Rectangle {
y = Math.random() * root.height - height / 2
rotation = Math.random() * 13 - 6
}
+
Image {
id: image
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
- source: folderModel.folder + fileName
+ source: photoFrame.fileUrl
antialiasing: true
}
- MomentumAnimation { id: anim; target: photoFrame }
+ Text {
+ text: fileName + " â– " + Qt.formatDateTime(fileModified, Locale.LongFormat)
+ horizontalAlignment: Text.AlignHCenter
+ elide: Text.ElideRight
+ font.pixelSize: (parent.height - image.height) / 3
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ margins: font.pixelSize / 5
+ }
+ }
- DragHandler {
- id: dragHandler
- onActiveChanged: {
- if (!active)
- anim.restart(point.velocity)
+ MomentumAnimation {
+ id: anim
+ target: photoFrame
+ onFinished: {
+ flick.contentWidth = Math.max(photoFrame.x + photoFrame.width, flick.contentWidth)
+ flick.contentHeight = Math.max(photoFrame.y + photoFrame.height, flick.contentHeight)
}
}
@@ -82,7 +97,18 @@ Rectangle {
maximumRotation: 360
minimumScale: 0.1
maximumScale: 10
- property real zRestore: 0
+ grabPermissions: PointerHandler.CanTakeOverFromAnything // and never gonna give it up
+ onActiveChanged: if (active) photoFrame.z = ++flick.highestZ
+ }
+
+ DragHandler {
+ id: dragHandler
+ onActiveChanged: {
+ if (active)
+ photoFrame.z = ++flick.highestZ
+ else
+ anim.restart(centroid.velocity)
+ }
}
}
}
@@ -134,4 +160,22 @@ Rectangle {
"On a touchscreen: use two fingers to zoom and rotate, one finger to drag\n" +
"With a mouse: drag normally"
}
+
+ Shortcut { sequence: StandardKey.Quit; onActivated: Qt.quit() }
+
+ Component.onCompleted: {
+ const lastArg = Application.arguments.slice(-1)[0]
+ const standardPicturesLocations = StandardPaths.standardLocations(StandardPaths.PicturesLocation)
+ const hasValidPicturesLocation = standardPicturesLocations.length > 0
+ if (hasValidPicturesLocation)
+ folderDialog.currentFolder = standardPicturesLocations[0]
+ if (/.*hotosurface.*|--+/.test(lastArg)) {
+ if (hasValidPicturesLocation)
+ folderModel.folder = standardPicturesLocations[0]
+ else
+ folderDialog.open()
+ }
+ else
+ folderModel.folder = Qt.resolvedUrl("file:" + lastArg)
+ }
}
diff --git a/tests/manual/pointer/pinchAndWheel.qml b/tests/manual/pointer/pinchAndWheel.qml
index fa60f4a79c..bd588fabf1 100644
--- a/tests/manual/pointer/pinchAndWheel.qml
+++ b/tests/manual/pointer/pinchAndWheel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import Qt.labs.animation 1.0
@@ -25,7 +25,9 @@ Rectangle {
function getTransformationDetails(item, pinchhandler) {
return "\n\npinch.scale:" + pinchhandler.scale.toFixed(2)
+ "\npinch.rotation:" + pinchhandler.rotation.toFixed(2)
- + "°\npinch.translation:" + "(" + pinchhandler.translation.x.toFixed(2) + "," + pinchhandler.translation.y.toFixed(2) + ")"
+ + "°\npinch.activeTranslation:" + "(" + pinchhandler.activeTranslation.x.toFixed(2) + "," + pinchhandler.activeTranslation.y.toFixed(2) + ")"
+ + "\npinch.persistentTranslation:" + "(" + pinchhandler.persistentTranslation.x.toFixed(2) + "," + pinchhandler.persistentTranslation.y.toFixed(2) + ")"
+ + " item pos " + "(" + transformable.x.toFixed(2) + "," + transformable.y.toFixed(2) + ")"
+ "\nscale wheel.rotation:" + scaleWheelHandler.rotation.toFixed(2)
+ "°\nhorizontal wheel.rotation:" + horizontalRotationWheelHandler.rotation.toFixed(2)
+ "°\ncontrol-rotation wheel.rotation:" + controlRotationWheelHandler.rotation.toFixed(2)
diff --git a/tests/manual/pointer/pinchDragFlingMPTA.qml b/tests/manual/pointer/pinchDragFlingMPTA.qml
index 3c268a0d4b..45d2b56182 100644
--- a/tests/manual/pointer/pinchDragFlingMPTA.qml
+++ b/tests/manual/pointer/pinchDragFlingMPTA.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import "content"
diff --git a/tests/manual/pointer/pinchFlickable.qml b/tests/manual/pointer/pinchFlickable.qml
index 1084a45e99..7b22461507 100644
--- a/tests/manual/pointer/pinchFlickable.qml
+++ b/tests/manual/pointer/pinchFlickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/manual/pointer/pinchNullTarget.qml b/tests/manual/pointer/pinchNullTarget.qml
index 79c6047991..d7f0c7e279 100644
--- a/tests/manual/pointer/pinchNullTarget.qml
+++ b/tests/manual/pointer/pinchNullTarget.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
diff --git a/tests/manual/pointer/pointHandlerOnFlickable.qml b/tests/manual/pointer/pointHandlerOnFlickable.qml
index 852df89f6a..db6b8c7257 100644
--- a/tests/manual/pointer/pointHandlerOnFlickable.qml
+++ b/tests/manual/pointer/pointHandlerOnFlickable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import "content"
diff --git a/tests/manual/pointer/pointerDrag.qml b/tests/manual/pointer/pointerDrag.qml
index 0d603c71d0..85d91113db 100644
--- a/tests/manual/pointer/pointerDrag.qml
+++ b/tests/manual/pointer/pointerDrag.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
import "content"
diff --git a/tests/manual/pointer/rubberbandOnTable.qml b/tests/manual/pointer/rubberbandOnTable.qml
index dacd549f12..bc3506ee73 100644
--- a/tests/manual/pointer/rubberbandOnTable.qml
+++ b/tests/manual/pointer/rubberbandOnTable.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/pointer/tapWithModifiers.qml b/tests/manual/pointer/tapWithModifiers.qml
index 8455a45f86..724754502d 100644
--- a/tests/manual/pointer/tapWithModifiers.qml
+++ b/tests/manual/pointer/tapWithModifiers.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/qmldom/CMakeLists.txt b/tests/manual/qmldom/CMakeLists.txt
new file mode 100644
index 0000000000..a0bdd5b81a
--- /dev/null
+++ b/tests/manual/qmldom/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.18)
+project(qmldomloadeditwrite LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/qmldomloadeditwrite")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Qml QmlDomPrivate)
+
+add_compile_definitions(
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../../../tests/auto/qmldom/domdata"
+)
+
+qt_add_executable(qmldomloadeditwrite
+ qmldomloadeditwrite.cpp
+)
+
+target_link_libraries(qmldomloadeditwrite PUBLIC
+ Qt6::Core
+ Qt6::Qml
+ Qt6::QmlDomPrivate
+)
+
+install(TARGETS qmldomloadeditwrite
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/qmldom/qmldomloadeditwrite.cpp b/tests/manual/qmldom/qmldomloadeditwrite.cpp
new file mode 100644
index 0000000000..9ca6b3c764
--- /dev/null
+++ b/tests/manual/qmldom/qmldomloadeditwrite.cpp
@@ -0,0 +1,502 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// common declarations
+#include <QtQmlDom/private/qqmldomitem_p.h>
+// comparisons of two DomItems
+#include <QtQmlDom/private/qqmldomcompare_p.h>
+// field filters to compare only selected fields (ignore for example location changes)
+#include <QtQmlDom/private/qqmldomfieldfilter_p.h>
+// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
+#include <QtQmlDom/private/qqmldomelements_p.h>
+// cast of the top level items (DomEnvironments,...)
+#include <QtQmlDom/private/qqmldomtop_p.h>
+
+#include <QtTest/QtTest>
+#include <QCborValue>
+#include <QDebug>
+#include <QLatin1String>
+#include <QLatin1Char>
+#include <QLibraryInfo>
+#include <QDir>
+
+#include <memory>
+
+// everything is in the QQmlJS::Dom namespace
+using namespace QQmlJS::Dom;
+
+int main()
+{
+ QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
+ QStringList qmltypeDirs =
+ QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
+
+ qDebug() << "Creating an environment loading qml from the directories" << qmltypeDirs;
+ qDebug() << "single threaded, no dependencies";
+ auto envPtr =
+ DomEnvironment::create(qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ QString testFilePath = baseDir + QLatin1String("/file1.qml");
+ DomItem tFile; // place where to store the loaded file
+
+ qDebug() << "loading the file" << testFilePath;
+ envPtr->loadFile(
+#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
+ FileToLoad::fromFileSystem(envPtr, testFilePath),
+#else
+ testFilePath, QString(),
+#endif
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt; // callback called when everything is loaded that receives the loaded
+ // external file pair (path, oldValue, newValue)
+ });
+
+ // trigger the load
+ envPtr->loadPendingDependencies();
+
+ // # Read only API: DomItem is a generic pointer for read only access to Dom Itmes :)
+ {
+ // ## declarative json like API
+ DomItem qmlFile = tFile.field(Fields::currentItem);
+ DomItem imports = qmlFile.field(Fields::imports);
+ DomItem qmlObj = qmlFile.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+
+ // ### Dump
+ // any DomItem can be dumped
+ qDebug() << "writing to QDebug dumps that element:" << imports;
+ // often the dump is too verbose, and one might want it to a separate file
+ QString dumpFilePath =
+ QDir(QDir::tempPath())
+ .filePath(QFileInfo(testFilePath).baseName() + QLatin1String(".dump.json"));
+ qmlFile.dump(dumpFilePath, FieldFilter::defaultFilter());
+ qDebug() << "dumped file to" << dumpFilePath;
+
+ // ### Paths
+ qDebug() << "To identify a DomItem a canonical path can be used:"
+ << imports.canonicalPath();
+ // a path can be converted to/from strings
+ QString pString = imports.canonicalPath().toString();
+ Path importsPath = Path::fromString(pString);
+ // and loaded again using the .path(somePath) method
+ DomItem imports2 = env.path(importsPath);
+ Q_ASSERT(imports == imports2);
+ // the canonical path is absolute, but you can have relative paths
+ Path first = Path::Index(0);
+ DomItem firstImport = imports.path(first);
+ // an existing path can also be extended
+ Path firstImportPath = importsPath.index(0);
+ Q_ASSERT(firstImportPath == firstImport.canonicalPath());
+ // the normal elements of a path are index, key, field
+ // Uppercase static method creates one, lowercase appends to an existing path.
+ Path mainComponentPath = Path::Field(Fields::components).key("").index(0);
+ DomItem mainComponent = qmlFile.path(mainComponentPath);
+ // DomItems have the same methods to access their elements
+ DomItem mainComponent2 = qmlFile.field(Fields::components).key("").index(0);
+ // two other special ements are root (root element for absolute paths)
+ Path topPath = Path::Root(PathRoot::Top);
+ Q_ASSERT(topPath == importsPath[0]);
+ // the current element performs an operation (tipically a lookup or iteration) at the
+ // current path location (not handled here)
+ Path lookupPath = Path::Current(PathCurrent::Lookup);
+
+ // there are various visit methods to iterate/visit DomItems in particular visitTree
+ // which is quite flexible.
+ // They normally use callbacks that can return false to stop the iteration.
+ // Still often the DomKind specific for loop presentated later are clearer and more
+ // convenient
+ {
+ QDebug dbg = qDebug().noquote().nospace();
+ imports.visitTree(
+ Path(),
+ [&dbg](Path p, const DomItem &el, bool adopted) {
+ dbg << QStringLiteral(u" ").repeated(p.length()) << "*" << p.last() << " "
+ << domKindToString(el.domKind()) << "(" << el.internalKindStr()
+ << ")\n";
+ // returning false here stops the whole iteration
+ return true;
+ },
+ VisitOption::Default, // we want a recursive visit visiting also the top and
+ // adopted
+ [&dbg](Path p, const DomItem &, bool canonicalChild) {
+ // returning false here skips that branch
+ if (!canonicalChild) {
+ dbg << QStringLiteral(u" ").repeated(p.length()) << "+" << p.last()
+ << " (adopted, will not recurse)\n";
+ } else if (p && p.headIndex(0) % 2 == 1) {
+ dbg << QStringLiteral(u" ").repeated(p.length()) << "-" << p.last()
+ << " *recursive visit skipped*\n";
+ return false; // we skip odd entries in lists;
+ } else {
+ dbg << QStringLiteral(u" ").repeated(p.length()) << "+" << p.last()
+ << "\n";
+ }
+ return true;
+ },
+ [&dbg](Path p, const DomItem &, bool) {
+ dbg << QStringLiteral(u" ").repeated(p.length()) << "=" << p.last() << "\n";
+ return true;
+ });
+ }
+
+ // ### DomKind
+ // any DomItem belongs to one of 5 fundamental types
+
+ // 1. Object (a C++ object)
+ Q_ASSERT(qmlFile.domKind() == DomKind::Object);
+ // The underlying type of the c++ object can be found with .internalKind()
+ Q_ASSERT(qmlFile.internalKind() == DomType::QmlFile);
+ // .initernalKindStr() is a convenience string version of it
+ Q_ASSERT(qmlFile.internalKindStr() == u"QmlFile");
+ // the object attributes (fields) can be reached using .field(u"filedName")
+ // normally one should not use a string, but the Fields:: constant
+ DomItem qmlFile2 = tFile.field(Fields::currentItem);
+ // all the available fields can be listed via fields()
+ qDebug() << "The" << qmlObj.internalKindStr() << "at" << qmlObj.canonicalPath()
+ << "has the following fields:" << qmlObj.fields();
+ // we can access the underlying C++ object with as<>
+ if (const QmlFile *qmlFilePtr = qmlFile.as<QmlFile>())
+ qDebug() << "The QmlFile lives at the address" << qmlFilePtr;
+ // We can get the shared pointer of the owner type (which for the file is the QmlFile itself
+ if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
+ qDebug() << "QmlFile uses shared pointers as ownership method, the underlying address "
+ "is the same"
+ << qmlFilePtr.get();
+
+ // 2. a (Cbor-) Value, i.e a string, number,...
+ DomItem fPath = qmlFile.field(Fields::canonicalFilePath);
+ Q_ASSERT(fPath.domKind() == DomKind::Value);
+ // the Cbor representation of a value can be extracted with .value(), and in this case we
+ // can then call toString
+ qDebug() << "The filePath DomItem is " << fPath << " and it still 'knows' its path "
+ << fPath.canonicalPath() << " but can have it also as value:" << fPath.value()
+ << "or even better as string." << fPath.value().toString(QLatin1String("*none*"));
+ // a DomItem might have a valid value() even if it is not of type DomKind::Value, indeed
+ // CBor maps and lists are mapped to DomKind::Map and DomKind::List, and can be traversed
+ // thought that but also have a valid value().
+
+ // 3. a list
+ Q_ASSERT(imports.domKind() == DomKind::List);
+ // the number of elements can be sound with .indexes() and with .index(n) we access each
+ // element
+ qDebug() << "We have " << imports.indexes() << " imports, and the first is "
+ << imports.index(0);
+ // If we want to just loop on the elements .values() is the most convenient way
+ // technically values *always* works even for objects and maps, iterating on the values
+ for (DomItem import : imports.values()) {
+ if (const Import *importPtr = import.as<Import>()) {
+ if (importPtr->implicit)
+ qDebug() << importPtr->uri.toString() << importPtr->version.stringValue();
+ }
+ }
+
+ // 4. a map
+ DomItem bindings = qmlObj.field(Fields::bindings);
+ Q_ASSERT(bindings.domKind() == DomKind::Map);
+ // The keys of the map can be reached either with .keys() or .sortedKeys(), each element
+ // with .key(k)
+ qDebug() << "bindings";
+ for (QString k : bindings.sortedKeys()) {
+ for (DomItem b : bindings.key(k).values()) {
+ qDebug() << k << ":" << b;
+ }
+ }
+
+ // 5 The empty element
+ DomItem empty;
+ Q_ASSERT(empty.domKind() == DomKind::Empty);
+ // The empty element is the only DomItem that casted to bool returns false, so checking for
+ // it can be just an implicit cast to bool
+ Q_ASSERT(bindings && !empty);
+ // the empty element supports all the previus operations so that one can traverse a non
+ // existing path without checking at every element, but only check the result
+ DomItem nonExisting = qmlFile.field(u"no-existing").key(u"a").index(0);
+ Q_ASSERT(!nonExisting);
+
+ // the index operator [] can be used instead of .index/.key/.field, it might be slightly
+ // less efficient but works
+
+ // find type
+ // access type
+
+ // ### write out
+ // it is possible to write out a qmlFile (actually also parts of it), which will
+ // automatically reformat it
+ QString reformattedFilePath =
+ QDir(QDir::tempPath())
+ .filePath(QFileInfo(testFilePath).baseName() + QLatin1String(".qml"));
+ DomItem newFile = qmlFile.writeOut(reformattedFilePath);
+ qDebug() << "reformatted written at " << reformattedFilePath;
+
+ // ## Jumping around
+ // ### Generic Methods
+ // from a DomItem you do no have just deeper in the tree, you can also go up the hierarch
+ // toward the root .container() just goes up one step in the canonicalPath of the object
+ Q_ASSERT(imports == firstImport.container());
+ // .containingObject() goes up to the containing DomKind::Object, skipping over all Maps and
+ // Lists
+ Q_ASSERT(qmlFile == firstImport.containingObject());
+ // .owner() returns the shared pointer based "owner" object, qmlFile and ScriptExpression
+ // are owningItems
+ Q_ASSERT(qmlFile == bindings.owner());
+ // .top() goes to the top of the tree, i.e the environment (or the universe)
+ Q_ASSERT(env == bindings.top());
+ // environment is normally the same as top, but making sure it is a actually a
+ // DomEnvironment
+ Q_ASSERT(env = bindings.environment());
+ // the universe is a cache of loaded files which for each file keeps two versions: the
+ // latest and the latest valid it can be reached with .universe(), from the universe you
+ // cannot get back to the environment.
+ Q_ASSERT(env.universe().internalKind() == DomType::DomUniverse);
+
+ // ## QML Oriented Methods
+ // The Dom model is not for generic json-like structures, so there are methods tailored for
+ // Qml and its structure
+ // The methods can succeed if there is a clearly defined unique result.
+ // sometime there is an obivious, but not necessarily unique choice (tipically going up the
+ // hierarchy), for example given a qml file the obvious choice for a component is the root
+ // component, but the file might contain other inline components, and for an object with
+ // different version exposed (C++ property versioning) the latest version is the natural
+ // choice, but other might be available. In these case passing GoTo::MostLikely as argument
+ // makes the method to this obivious choice (or possibly even only choice if no other
+ // versions/components are actually defined), instead of refusing any potentially ambiguous
+ // situation and returning the empty element.
+
+ // .fileObject() goes to the object representing the whole file
+ // (from either the external object returned by load or from inside the file)
+ DomItem fileObject = tFile.fileObject();
+ DomItem fileObject2 = imports.fileObject();
+ Q_ASSERT(fileObject == fileObject2 && fileObject.internalKind() == DomType::QmlFile);
+ // .component() goes to the component object.
+ Q_ASSERT(qmlObj.component() == qmlFile.component(GoTo::MostLikely));
+ // .pragmas gives access to the pragmas of the current component
+ Q_ASSERT(qmlFile.pragmas() == qmlFile.field(Fields::pragmas));
+
+ // QmlObject
+ // QmlObject if the main to represent the type information (methods, bindings,
+ // properties,...) of qml. Please note that QmlObject -> component operation is potentially
+ // lossy, when multiple version are exposed, so we represent a type through its root object,
+ // not through a component.
+
+ // .qmlObject() goes to the current QmlObject
+ Q_ASSERT(qmlObj == bindings.qmlObject());
+
+ // Given the centrality of QmlObject several of its attributes have convenience methods
+ // to access them:
+
+ // .children() makes subObjects contained inside a QmlObject accessible
+ // note that it is possible to add objects also by directly binding the children or data
+ // attribute, those children are not listed here, this accesses only those listed inside
+ // the QmlObject
+ Q_ASSERT(qmlObj.children() == qmlObj.field(Fields::children));
+ DomItem subObj0 = qmlObj.children().index(0);
+ // .child(<i>) is a shortcut for .children.index(<i>)
+ Q_ASSERT(subObj0 == qmlObj.child(0));
+ // rootQmlObject goes to the root qmlObject (unless one reaches an empty element)
+ Q_ASSERT(!subObj0 || subObj0.rootQmlObject() == qmlObj);
+ // .bindings() returns the bindings defined in the current object
+ Q_ASSERT(bindings == qmlObj.bindings());
+ DomItem mCompObj = qmlObj.child(0)
+ .child(0)
+ .bindings()
+ .key(u"delegate")
+ .index(0)
+ .field(Fields::value)
+ .child(1);
+ // .methods() gives methods definitions and signals
+ DomItem methods = mCompObj.methods();
+ qDebug() << "mCompObj methods:";
+ for (QString methodName : methods.sortedKeys()) {
+ for (DomItem method : methods.key(methodName).values()) {
+ if (const MethodInfo *methodPtr = method.as<MethodInfo>()) {
+ Q_ASSERT(methodName == methodPtr->name);
+ qDebug() << " " << methodPtr->name << methodPtr->methodType;
+ }
+ }
+ }
+ qDebug() << "mCompObj propertyDefs:";
+ // .propertyDefs() returns the properties defined in the current object
+ DomItem pDefs = mCompObj.propertyDefs();
+ for (QString pDefName : pDefs.sortedKeys()) {
+ for (DomItem pDef : pDefs.key(pDefName).values()) {
+ if (const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>()) {
+ Q_ASSERT(pDefName == pDefPtr->name);
+ qDebug() << " " << pDefPtr->name << pDefPtr->typeName;
+ }
+ }
+ }
+ // binding and property definitions are about the ones defined in the current object
+ // often one is interested also to the inherited properties.
+ // Here PropertyInfo helps, it list all the definitions and bindings for a given property
+ // in the inheritance order (local definitions, parent definitions, parent parent
+ // definitions,...)
+ // .propertyInfos() gives access in the usual way (through a DomItem)
+ DomItem propertyInfos = mCompObj.propertyInfos();
+ // .propertyInfoWithName(<name>) directly accesses one
+ PropertyInfo pInfo = mCompObj.propertyInfoWithName(QStringLiteral(u"a"));
+ qDebug() << "bindings" << pInfo.bindings;
+ // .propertyInfoNames() gives the names of the properties
+ Q_ASSERT(propertyInfos.keys() == mCompObj.propertyInfoNames());
+
+ // .globalScope() goes to the globa scope object
+ Q_ASSERT(qmlObj.globalScope().internalKind() == DomType::GlobalScope);
+ // and scope to the containing scope
+ Q_ASSERT(bindings.scope() == qmlObj);
+ }
+ // mutate & edit
+ {
+ // DomItem handles read-only access, but if one wants to change something it cannot be used.
+ // MutableDomItem can be initialized with a DomItem, and provides also the methods to modify
+ // the item. It keeps the OwningItem and the path to the current item.
+ // Mutability can invalidate pointers to non owning items (and thus DomItem).
+ // For this reason one should not modify something that other code can have a DomItem
+ // pointer to, the best practice is to make shared object immutable and never change them.
+ // One should modify only a copy that is used only by a single thread, and
+ // do not shared untils all modifications are done.
+ // A MutableItem stays valid (or becomes Empty), but stays safe to use
+ //
+ // Assuming one guarantees that editing is ok, doing it in practice is just about using
+ // MutableDomItem instead of DomItem
+ // It is possible to simply initialize a mutable item with a DomItem
+ DomItem origFile = tFile.fileObject();
+ MutableDomItem myFile0(origFile);
+ // Normally it is better to have a separate environment. Is possible to avoid re-reading
+ // the files already read by sharing the Universe between two environments.
+ // But normally it is better and just as safe to work on a copy, so that one can be sure
+ // that no DomItem is kept by other code gets invalidated. The .makeCopy creates a deep
+ // copy, and by default (DomItem::CopyOption::EnvConnected) creates an environment which to
+ // takes all non local elements from the current environment (its parent environment) but
+ // replaces the file object with the copy. When finished one can replace the file object of
+ // the parent with the new one using .commitToBase().
+ MutableDomItem myFile = origFile.makeCopy();
+ Q_ASSERT(myFile.ownerAs<QmlFile>()
+ && myFile.ownerAs<QmlFile>() != myFile0.ownerAs<QmlFile>());
+ Q_ASSERT(myFile.environment().ownerAs<DomEnvironment>()
+ && myFile.environment().ownerAs<DomEnvironment>()
+ != myFile0.environment().ownerAs<DomEnvironment>());
+ // we can check that the two files are really identical (.item() give back the DomItem of
+ // a MutableDomItem
+ Q_ASSERT(domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
+ // MutableDomItem has the same methods as DomItem
+ MutableDomItem qmlObj = myFile.qmlObject(GoTo::MostLikely);
+ MutableDomItem qmlObj2 = myFile.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+ Q_ASSERT(qmlObj && qmlObj == qmlObj2);
+ qDebug() << "mutable qmlObj has canonicalPath " << qmlObj.canonicalPath();
+ // but it adds methods to add
+ // * new PropertyDefinitions
+ PropertyDefinition b;
+ b.name = QLatin1String("xx");
+ b.typeName = QLatin1String("int");
+ // if we make t true we also have to give a value...
+ MutableDomItem addedPDef = qmlObj.addPropertyDef(b);
+ qDebug() << "added property definition at:" << addedPDef.pathFromOwner();
+ // * new bindings
+ MutableDomItem addedBinding0 = qmlObj.addBinding(
+ Binding("height",
+ std::shared_ptr<ScriptExpression>(new ScriptExpression(
+ QStringLiteral(u"243"),
+ ScriptExpression::ExpressionType::BindingExpression))));
+ // by default addBinding, addPropertyDef and addMethod have the AddOption::Override
+ // to make it more difficult to create invalid documents, so that only the
+ // following binding remains (where we use the convenience constructor that constucts
+ // the ScriptExpression internally
+ MutableDomItem addedBinding = qmlObj.addBinding(Binding("height", QStringLiteral(u"242")));
+ qDebug() << "added binding at:" << addedBinding.pathFromOwner();
+ // * new methods
+ MethodInfo mInfo;
+ mInfo.name = QLatin1String("foo2");
+ MethodParameter param;
+ param.name = QLatin1String("x");
+ mInfo.parameters.append(param);
+ mInfo.setCode(QLatin1String("return 4*10+2 - x"));
+ // we can change the added binding
+ addedBinding.setCode(QLatin1String("245"));
+ MutableDomItem addedMethod = qmlObj.addMethod(mInfo);
+ qDebug() << "added method at:" << addedMethod.pathFromOwner();
+ // * new QmlObjects
+ QmlObject subObj;
+ subObj.setName(QLatin1String("Item"));
+ MutableDomItem addedSubObj = qmlObj.addChild(subObj);
+ qDebug() << "added subObject at:" << addedMethod.pathFromOwner();
+ // It is possible to modify the content of objects, using the mutableAs method
+ if (PropertyDefinition *addedPDefPtr = addedPDef.mutableAs<PropertyDefinition>()) {
+ addedPDefPtr->isRequired = true;
+ }
+ MutableDomItem firstChild = qmlObj.child(0);
+ qDebug() << "firstChild:" << firstChild;
+ // It is possible remove objects
+ if (QmlObject *qmlObjPtr = qmlObj.mutableAs<QmlObject>()) {
+ QList<QmlObject> children = qmlObjPtr->children();
+ children.removeAt(0);
+ qmlObjPtr->setChildren(children);
+ }
+ // But as MutableDomItem does not keep the identity, just the same position, the addedSubObj
+ // becomes invalid (and firstChild changes)
+ qDebug() << "after removal firstChild:" << firstChild;
+ qDebug() << "addedSubObj becomes invalid:" << addedSubObj;
+ qDebug() << "But the last object is the added one:"
+ << qmlObj.child(qmlObj.children().indexes() - 1);
+
+ // now origFile are different
+ Q_ASSERT(!domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
+ // and we can look at the places where they differ
+ qDebug().noquote().nospace()
+ << "Edits introduced the following diffs (ignoring file locations"
+ << " and thus whitespace/reformatting changes):\n"
+ << domCompareStrList(origFile, myFile, FieldFilter::noLocationFilter(),
+ DomCompareStrList::AllDiffs)
+ .join(QString());
+
+ QString reformattedFilePath =
+ QDir(QDir::tempPath())
+ .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
+ + QLatin1String(".qml"));
+ MutableDomItem reformattedEditedFile = myFile.writeOut(reformattedFilePath);
+ // the reformatted edited file might be different from the edited file
+ // but the differences are just in file location/formatting
+ Q_ASSERT(domCompareStrList(myFile, reformattedEditedFile, FieldFilter::noLocationFilter())
+ .isEmpty());
+
+ qDebug() << "The edited file was written at " << reformattedFilePath;
+ QString dumpFilePath =
+ QDir(QDir::tempPath())
+ .filePath(QStringLiteral(u"edited0") + QFileInfo(testFilePath).baseName()
+ + QLatin1String(".dump.json"));
+ myFile.dump(dumpFilePath);
+ qDebug() << "The non reformatted edited file was dumped at " << dumpFilePath;
+ QString reformattedDumpFilePath =
+ QDir(QDir::tempPath())
+ .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
+ + QLatin1String(".dump.json"));
+ reformattedEditedFile.dump(reformattedDumpFilePath);
+ qDebug() << "The edited file was dumped at " << reformattedDumpFilePath;
+ // The top environment still contains the original loaded file
+ Q_ASSERT(origFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>());
+ Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>());
+ Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>() == origFile.ownerAs<QmlFile>());
+ Q_ASSERT(tFile.fileObject().ownerAs<QmlFile>() == origFile.ownerAs<QmlFile>());
+ Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
+ != reformattedEditedFile.ownerAs<QmlFile>());
+ // we can commit the reformatted file
+ if (!reformattedEditedFile.commitToBase()) {
+ qWarning() << "No reformatted file to commit";
+ }
+ // myFile might not be the same (If and updated check is requested, not the case here)
+ if (myFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>()
+ && !myFile.commitToBase()) {
+ qWarning() << "Could not commit edited file";
+ }
+ // but refreshing it (looking up its canonical path) we always find the updated file
+ Q_ASSERT(myFile.refreshed().ownerAs<QmlFile>() == reformattedEditedFile.ownerAs<QmlFile>());
+ Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
+ == reformattedEditedFile.ownerAs<QmlFile>());
+ }
+}
diff --git a/tests/manual/qmlextensionplugins/CMakeLists.txt b/tests/manual/qmlextensionplugins/CMakeLists.txt
new file mode 100644
index 0000000000..26e454b0b4
--- /dev/null
+++ b/tests/manual/qmlextensionplugins/CMakeLists.txt
@@ -0,0 +1,62 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(qmlqtimeexample LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/qmlextensionplugins/imports/TimeExample")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+#![0]
+set(qml_files
+ imports/TimeExample/Clock.qml
+)
+
+set(images
+ imports/TimeExample/center.png
+ imports/TimeExample/clock.png
+ imports/TimeExample/hour.png
+ imports/TimeExample/minute.png
+)
+#![0]
+
+foreach(file IN LISTS qml_files images)
+ get_filename_component(filename ${file} NAME)
+ set_source_files_properties(${file} PROPERTIES QT_RESOURCE_ALIAS ${filename})
+endforeach()
+
+#![1]
+qt_add_qml_module(qmlqtimeexample
+ OUTPUT_DIRECTORY imports/TimeExample
+ URI "TimeExample"
+ SOURCES timemodel.cpp timemodel.h
+ QML_FILES ${qml_files}
+ RESOURCES ${images}
+)
+#![1]
+
+set_target_properties(qmlqtimeexample PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(qmlqtimeexample PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+)
+
+install(TARGETS qmlqtimeexample
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/qml/qmlextensionplugins/doc/images/qml-plugins-example.png b/tests/manual/qmlextensionplugins/doc/images/qml-plugins-example.png
index c2d4886ea2..c2d4886ea2 100644
--- a/examples/qml/qmlextensionplugins/doc/images/qml-plugins-example.png
+++ b/tests/manual/qmlextensionplugins/doc/images/qml-plugins-example.png
Binary files differ
diff --git a/examples/qml/qmlextensionplugins/doc/src/qmlpluginex.qdoc b/tests/manual/qmlextensionplugins/doc/src/qmlpluginex.qdoc
index 80f39e36ce..80f39e36ce 100644
--- a/examples/qml/qmlextensionplugins/doc/src/qmlpluginex.qdoc
+++ b/tests/manual/qmlextensionplugins/doc/src/qmlpluginex.qdoc
diff --git a/tests/manual/qmlextensionplugins/imports/TimeExample/Clock.qml b/tests/manual/qmlextensionplugins/imports/TimeExample/Clock.qml
new file mode 100644
index 0000000000..416b78d31c
--- /dev/null
+++ b/tests/manual/qmlextensionplugins/imports/TimeExample/Clock.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ id: clock
+ width: 200; height: 200; color: "gray"
+
+ property alias city: cityLabel.text
+ property variant hours
+ property variant minutes
+ property variant shift : 0
+
+ Image { id: background; source: "clock.png" }
+
+ Image {
+ x: 92.5; y: 27
+ source: "hour.png"
+ transform: Rotation {
+ id: hourRotation
+ origin.x: 7.5; origin.y: 73;
+ angle: (clock.hours * 30) + (clock.minutes * 0.5)
+ Behavior on angle {
+ SpringAnimation{ spring: 2; damping: 0.2; modulus: 360 }
+ }
+ }
+ }
+
+ Image {
+ x: 93.5; y: 17
+ source: "minute.png"
+ transform: Rotation {
+ id: minuteRotation
+ origin.x: 6.5; origin.y: 83;
+ angle: clock.minutes * 6
+ Behavior on angle {
+ SpringAnimation{ spring: 2; damping: 0.2; modulus: 360 }
+ }
+ }
+ }
+
+ Image {
+ anchors.centerIn: background; source: "center.png"
+ }
+
+ Text {
+ id: cityLabel; font.bold: true; font.pixelSize: 14; y:200; color: "white"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+}
diff --git a/examples/qml/qmlextensionplugins/imports/TimeExample/center.png b/tests/manual/qmlextensionplugins/imports/TimeExample/center.png
index 7fbd802a44..7fbd802a44 100644
--- a/examples/qml/qmlextensionplugins/imports/TimeExample/center.png
+++ b/tests/manual/qmlextensionplugins/imports/TimeExample/center.png
Binary files differ
diff --git a/examples/qml/qmlextensionplugins/imports/TimeExample/clock.png b/tests/manual/qmlextensionplugins/imports/TimeExample/clock.png
index 462edacc0e..462edacc0e 100644
--- a/examples/qml/qmlextensionplugins/imports/TimeExample/clock.png
+++ b/tests/manual/qmlextensionplugins/imports/TimeExample/clock.png
Binary files differ
diff --git a/examples/qml/qmlextensionplugins/imports/TimeExample/hour.png b/tests/manual/qmlextensionplugins/imports/TimeExample/hour.png
index 9f33fc5d48..9f33fc5d48 100644
--- a/examples/qml/qmlextensionplugins/imports/TimeExample/hour.png
+++ b/tests/manual/qmlextensionplugins/imports/TimeExample/hour.png
Binary files differ
diff --git a/examples/qml/qmlextensionplugins/imports/TimeExample/minute.png b/tests/manual/qmlextensionplugins/imports/TimeExample/minute.png
index e2f216c897..e2f216c897 100644
--- a/examples/qml/qmlextensionplugins/imports/TimeExample/minute.png
+++ b/tests/manual/qmlextensionplugins/imports/TimeExample/minute.png
Binary files differ
diff --git a/examples/qml/qmlextensionplugins/imports/TimeExample/qmldir b/tests/manual/qmlextensionplugins/imports/TimeExample/qmldir
index 252e662e12..252e662e12 100644
--- a/examples/qml/qmlextensionplugins/imports/TimeExample/qmldir
+++ b/tests/manual/qmlextensionplugins/imports/TimeExample/qmldir
diff --git a/tests/manual/qmlextensionplugins/plugin.cpp b/tests/manual/qmlextensionplugins/plugin.cpp
new file mode 100644
index 0000000000..afda6626ce
--- /dev/null
+++ b/tests/manual/qmlextensionplugins/plugin.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQml/QQmlEngineExtensionPlugin>
+#include <qdebug.h>
+
+//![plugin]
+class QExampleQmlPlugin : public QQmlEngineExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
+};
+//![plugin]
+
+#include "plugin.moc"
diff --git a/tests/manual/qmlextensionplugins/plugins.qml b/tests/manual/qmlextensionplugins/plugins.qml
new file mode 100644
index 0000000000..9e798db7e2
--- /dev/null
+++ b/tests/manual/qmlextensionplugins/plugins.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+//![0]
+import TimeExample // import types from the plugin
+
+Clock { // this class is defined in QML (imports/TimeExample/Clock.qml)
+
+ Time { // this class is defined in C++ (plugin.cpp)
+ id: time
+ }
+
+ hours: time.hour
+ minutes: time.minute
+
+}
+//![0]
diff --git a/examples/qml/qmlextensionplugins/plugins.qmlproject b/tests/manual/qmlextensionplugins/plugins.qmlproject
index c2de5f5703..c2de5f5703 100644
--- a/examples/qml/qmlextensionplugins/plugins.qmlproject
+++ b/tests/manual/qmlextensionplugins/plugins.qmlproject
diff --git a/examples/qml/qmlextensionplugins/qmlextensionplugins.pro b/tests/manual/qmlextensionplugins/qmlextensionplugins.pro
index c074b8d671..c074b8d671 100644
--- a/examples/qml/qmlextensionplugins/qmlextensionplugins.pro
+++ b/tests/manual/qmlextensionplugins/qmlextensionplugins.pro
diff --git a/tests/manual/qmlextensionplugins/timemodel.cpp b/tests/manual/qmlextensionplugins/timemodel.cpp
new file mode 100644
index 0000000000..1e539b430d
--- /dev/null
+++ b/tests/manual/qmlextensionplugins/timemodel.cpp
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "timemodel.h"
+
+int TimeModel::instances=0;
+MinuteTimer *TimeModel::timer=nullptr;
+
+void MinuteTimer::start()
+{
+ if (!timer.isActive()) {
+ time = QTime::currentTime();
+ timer.start(60000-time.second()*1000, this);
+ }
+}
+
+void MinuteTimer::stop()
+{
+ timer.stop();
+}
+
+void MinuteTimer::timerEvent(QTimerEvent *)
+{
+ QTime now = QTime::currentTime();
+ if (now.second() == 59 && now.minute() == time.minute() && now.hour() == time.hour()) {
+ // just missed time tick over, force it, wait extra 0.5 seconds
+ time = time.addSecs(60);
+ timer.start(60500, this);
+ } else {
+ time = now;
+ timer.start(60000-time.second()*1000, this);
+ }
+ emit timeChanged();
+}
+
+TimeModel::TimeModel(QObject *parent) : QObject(parent)
+{
+ if (++instances == 1) {
+ if (!timer)
+ timer = new MinuteTimer(QCoreApplication::instance());
+ connect(timer, &MinuteTimer::timeChanged, this, &TimeModel::timeChanged);
+ timer->start();
+ }
+}
+
+TimeModel::~TimeModel()
+{
+ if (--instances == 0) {
+ timer->stop();
+ }
+}
diff --git a/tests/manual/qmlextensionplugins/timemodel.h b/tests/manual/qmlextensionplugins/timemodel.h
new file mode 100644
index 0000000000..5ecd6d2d61
--- /dev/null
+++ b/tests/manual/qmlextensionplugins/timemodel.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TIMEMODEL_H
+#define TIMEMODEL_H
+
+#include <QtQml/qqml.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qbasictimer.h>
+#include <QtCore/qcoreapplication.h>
+
+// Implements a "TimeModel" class with hour and minute properties
+// that change on-the-minute yet efficiently sleep the rest
+// of the time.
+
+class MinuteTimer : public QObject
+{
+ Q_OBJECT
+public:
+ MinuteTimer(QObject *parent) : QObject(parent) {}
+
+ void start();
+ void stop();
+
+ int hour() const { return time.hour(); }
+ int minute() const { return time.minute(); }
+
+signals:
+ void timeChanged();
+
+protected:
+ void timerEvent(QTimerEvent *) override;
+
+private:
+ QTime time;
+ QBasicTimer timer;
+};
+
+//![0]
+class TimeModel : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int hour READ hour NOTIFY timeChanged)
+ Q_PROPERTY(int minute READ minute NOTIFY timeChanged)
+ QML_NAMED_ELEMENT(Time)
+//![0]
+
+public:
+ TimeModel(QObject *parent=nullptr);
+ ~TimeModel() override;
+
+ int minute() const { return timer->minute(); }
+ int hour() const { return timer->hour(); }
+
+signals:
+ void timeChanged();
+
+private:
+ QTime t;
+ static MinuteTimer *timer;
+ static int instances;
+};
+
+#endif // TIMEMODEL_H
diff --git a/tests/manual/qmlls-cmake-builds/CMakeLists.txt b/tests/manual/qmlls-cmake-builds/CMakeLists.txt
new file mode 100644
index 0000000000..e9bf94e034
--- /dev/null
+++ b/tests/manual/qmlls-cmake-builds/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(qmllscmakebuilds VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(QT_QML_GENERATE_QMLLS_INI ON CACHE BOOL "" FORCE)
+
+find_package(Qt6 REQUIRED COMPONENTS Quick)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(appautoGenCMake
+ main.cpp
+)
+
+qt_add_qml_module(appautoGenCMake
+ URI AutoGenCMake
+ VERSION 1.0
+ QML_FILES Main.qml
+ SOURCES helloworld.cpp helloworld.h
+)
+
+# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
+# If you are developing for iOS or macOS you should consider setting an
+# explicit, fixed bundle identifier manually though.
+set_target_properties(appautoGenCMake PROPERTIES
+# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appautoGenCMake
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+target_link_libraries(appautoGenCMake
+ PRIVATE Qt6::Quick
+)
diff --git a/tests/manual/qmlls-cmake-builds/Main.qml b/tests/manual/qmlls-cmake-builds/Main.qml
new file mode 100644
index 0000000000..bd0c21ae63
--- /dev/null
+++ b/tests/manual/qmlls-cmake-builds/Main.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import autoGenCMake
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ title: qsTr("Hello World")
+
+ HelloWorld { myP: 55; myPPP: 55 }
+}
diff --git a/tests/manual/qmlls-cmake-builds/README.md b/tests/manual/qmlls-cmake-builds/README.md
new file mode 100644
index 0000000000..1063433f1d
--- /dev/null
+++ b/tests/manual/qmlls-cmake-builds/README.md
@@ -0,0 +1,16 @@
+# Testing the automatic qmlls CMake type registration
+
+First of all, setup qmlls in your favorite editor (see https://www.qt.io/blog/whats-new-in-qml-language-server-qmlls-shipped-with-qt-6.6 for instructions).
+
+## Steps
+
+1. Open the manual test using the `CMakeLists.txt` (for QtC, for example) or the folder in which the `CMakeLists.txt` lies (for VS Code, for example) in the editor prepared for qmlls.
+2. Make sure the project is configured. It does not need to be built, just configured via CMake.
+3. In your editor, open the `Main.qml` and `helloworld.h`.
+4. Modify the `helloworld.h` file by commenting the existing `Q_PROPERTY myPPP` out and save `helloworld.h`.
+5. In the Main.qml file, write some code (add or delete a newline). It should complain about the binding to `myPPP` in
+`HelloWorld`, as the property does not exist anymore. It should also not propose myPPP as autocompletion
+in `HelloWorld` anymore.
+
+6. Repeat steps 4 + 5 with your own modifications and check that the modification in the `helloworld.h` can be seen in
+the `Main.qml` file, without having to rebuild the project yourself.
diff --git a/tests/manual/qmlls-cmake-builds/helloworld.cpp b/tests/manual/qmlls-cmake-builds/helloworld.cpp
new file mode 100644
index 0000000000..445103c228
--- /dev/null
+++ b/tests/manual/qmlls-cmake-builds/helloworld.cpp
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "helloworld.h"
+
+HelloWorld::HelloWorld(QObject *parent)
+ : QObject{parent}
+{}
diff --git a/tests/manual/qmlls-cmake-builds/helloworld.h b/tests/manual/qmlls-cmake-builds/helloworld.h
new file mode 100644
index 0000000000..0a13344476
--- /dev/null
+++ b/tests/manual/qmlls-cmake-builds/helloworld.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef HELLOWORLD_H
+#define HELLOWORLD_H
+
+#include <QObject>
+#include <QQmlEngine>
+
+class HelloWorld : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int myP READ myP WRITE setMyP NOTIFY myPChanged FINAL)
+ Q_PROPERTY(int myPPP READ myP WRITE setMyP NOTIFY myPChanged FINAL)
+
+public:
+ explicit HelloWorld(QObject *parent = nullptr);
+
+ int myP() { return m_myP; }
+ void setMyP(int p) { m_myP = p; }
+private:
+ int m_myP;
+
+signals:
+ void myPChanged();
+};
+
+#endif // HELLOWORLD_H
diff --git a/tests/manual/qmlls-cmake-builds/main.cpp b/tests/manual/qmlls-cmake-builds/main.cpp
new file mode 100644
index 0000000000..a0cb14629e
--- /dev/null
+++ b/tests/manual/qmlls-cmake-builds/main.cpp
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ const QUrl url(u"qrc:/autoGenCMake/Main.qml"_qs);
+ QObject::connect(
+ &engine,
+ &QQmlApplicationEngine::objectCreationFailed,
+ &app,
+ []() { QCoreApplication::exit(-1); },
+ Qt::QueuedConnection);
+ engine.load(url);
+
+ return app.exec();
+}
diff --git a/tests/manual/qmllsformatter/test.qml b/tests/manual/qmllsformatter/test.qml
new file mode 100644
index 0000000000..f2e0a9b462
--- /dev/null
+++ b/tests/manual/qmllsformatter/test.qml
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// Start Test
+// Open that test.qml file in a text editor that has a client for qmlls
+
+////////////////////////////////////////////////////////////
+// Test Case - 1
+// Type the following line manually
+
+import QtQml
+
+Window {
+
+
+}
+
+// Then, execute "Format Document" in the text editor
+
+// Expected Result
+import QtQml
+
+Window {
+}
+
+////////////////////////////////////////////////////////////
+// Test Case - 2
+// Type the following line manually
+
+import QtQml
+
+Window {
+ aa a: {
+ }
+}
+
+// Then, execute "Format Document" in the text editor
+
+// Expected Result
+import QtQml
+
+Window {
+ aa: {
+ }
+}
diff --git a/tests/manual/qmltest/CMakeLists.txt b/tests/manual/qmltest/CMakeLists.txt
new file mode 100644
index 0000000000..90d0d77749
--- /dev/null
+++ b/tests/manual/qmltest/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(tst_qmltestexample LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qmltest/qmltest")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui QuickTest)
+
+qt_add_executable(tst_qmltestexample
+ tst_qmltest.cpp
+)
+
+set_target_properties(tst_qmltestexample PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(tst_qmltestexample PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::QuickTest
+)
+
+if(MACOS AND QT_BUILDING_QT)
+ set_target_properties(tst_qmltestexample PROPERTIES
+ MACOSX_BUNDLE FALSE
+ )
+endif()
+
+set(tst_files tst_basic.qml tst_item.qml)
+
+install(FILES ${tst_files}
+ DESTINATION "${INSTALL_EXAMPLEDIR}")
+
+install(TARGETS tst_qmltestexample
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/qmltest/qmltest/qmltest.pro b/tests/manual/qmltest/qmltest.pro
index b5893c5a1e..b5893c5a1e 100644
--- a/examples/qmltest/qmltest/qmltest.pro
+++ b/tests/manual/qmltest/qmltest.pro
diff --git a/tests/manual/qmltest/tst_basic.qml b/tests/manual/qmltest/tst_basic.qml
new file mode 100644
index 0000000000..d258a7665e
--- /dev/null
+++ b/tests/manual/qmltest/tst_basic.qml
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtTest
+
+TestCase {
+ name: "BasicTests"
+
+ function test_pass() {
+ compare(2 + 2, 4, "2 + 2")
+ }
+
+ function test_fail() {
+ expectFail("", "this is the fail we wanted")
+ compare(2 + 2, 5, "2 + 2")
+ }
+
+ function test_skip() {
+ skip("skipping")
+ }
+
+ function test_expecting() {
+ expectFail("", "this is the fail we wanted")
+ verify(false)
+ }
+
+ function test_table_data() {
+ return [
+ {tag: "2 + 2 = 4", a: 2, b: 2, answer: 4 },
+ {tag: "2 + 6 = 8", a: 2, b: 6, answer: 8 },
+ {tag: "2 + 2 = 5", a: 2, b: 2, answer: 5 }, // fail
+ ]
+ }
+
+ function test_table(data) {
+ if (data.answer === 5)
+ expectFail("", "this is the fail we wanted")
+ compare(data.a + data.b, data.answer)
+ }
+}
diff --git a/tests/manual/qmltest/tst_item.qml b/tests/manual/qmltest/tst_item.qml
new file mode 100644
index 0000000000..b6b988d371
--- /dev/null
+++ b/tests/manual/qmltest/tst_item.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtTest
+
+Rectangle {
+ id: foo
+ width: 640; height: 480
+ color: "cyan"
+
+ TestCase {
+ name: "ItemTests"
+ id: test1
+
+ function test_color() {
+ compare(foo.color, "#00ffff")
+ }
+ }
+}
diff --git a/tests/manual/qmltest/tst_qmltest.cpp b/tests/manual/qmltest/tst_qmltest.cpp
new file mode 100644
index 0000000000..6734a67dc5
--- /dev/null
+++ b/tests/manual/qmltest/tst_qmltest.cpp
@@ -0,0 +1,5 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQuickTest/quicktest.h>
+QUICK_TEST_MAIN(qmltestexample)
diff --git a/tests/manual/qmltypememory/TestPlugin/plugin.cpp b/tests/manual/qmltypememory/TestPlugin/plugin.cpp
index 82c71025cd..6cd0fbff5f 100644
--- a/tests/manual/qmltypememory/TestPlugin/plugin.cpp
+++ b/tests/manual/qmltypememory/TestPlugin/plugin.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/QQmlExtensionPlugin>
#include <QtQml/qqml.h>
diff --git a/tests/manual/qmltypememory/TestType.qml b/tests/manual/qmltypememory/TestType.qml
index 9f98ec971f..0699ac894f 100644
--- a/tests/manual/qmltypememory/TestType.qml
+++ b/tests/manual/qmltypememory/TestType.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.0
QtObject{property int notJustAStandardQtObject: 10 }
diff --git a/tests/manual/qmltypememory/main.cpp b/tests/manual/qmltypememory/main.cpp
index 8f7bf4c4e1..83848ffc1e 100644
--- a/tests/manual/qmltypememory/main.cpp
+++ b/tests/manual/qmltypememory/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml>
#include <QGuiApplication>
diff --git a/tests/manual/qmltypememory/main.qml b/tests/manual/qmltypememory/main.qml
index 3aa815372b..df84f38710 100644
--- a/tests/manual/qmltypememory/main.qml
+++ b/tests/manual/qmltypememory/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.0
import QtQuick 2.0
diff --git a/tests/manual/qmltypememory/testdriver.cpp b/tests/manual/qmltypememory/testdriver.cpp
index a65208a0f6..eeac9bf707 100644
--- a/tests/manual/qmltypememory/testdriver.cpp
+++ b/tests/manual/qmltypememory/testdriver.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testdriver.h"
#include <QCoreApplication>
diff --git a/tests/manual/qmltypememory/testdriver.h b/tests/manual/qmltypememory/testdriver.h
index 5b5a4c839a..852a5a571c 100644
--- a/tests/manual/qmltypememory/testdriver.h
+++ b/tests/manual/qmltypememory/testdriver.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 Research In Motion.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTDRIVER_H
#define TESTDRIVER_H
diff --git a/tests/manual/quickcontrols/CMakeLists.txt b/tests/manual/quickcontrols/CMakeLists.txt
index 524352d824..e7f07e6110 100644
--- a/tests/manual/quickcontrols/CMakeLists.txt
+++ b/tests/manual/quickcontrols/CMakeLists.txt
@@ -5,12 +5,17 @@
add_subdirectory(buttons)
add_subdirectory(fonts)
-add_subdirectory(gifs)
+if(LINUX)
+ add_subdirectory(gifs)
+endif()
add_subdirectory(headerview)
+add_subdirectory(imagine/musicplayer)
add_subdirectory(qquickdialog)
add_subdirectory(screenshots)
+add_subdirectory(sidepanel)
add_subdirectory(styles)
add_subdirectory(styles-cover-flow)
+add_subdirectory(swipetoremove)
add_subdirectory(testbench)
if(QT_FEATURE_systemtrayicon)
add_subdirectory(systemtrayicon)
diff --git a/tests/manual/quickcontrols/buttons/ButtonLoader.qml b/tests/manual/quickcontrols/buttons/ButtonLoader.qml
index 24931d4078..66eb0eef29 100644
--- a/tests/manual/quickcontrols/buttons/ButtonLoader.qml
+++ b/tests/manual/quickcontrols/buttons/ButtonLoader.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/buttons/buttons.cpp b/tests/manual/quickcontrols/buttons/buttons.cpp
index 44e5971f4f..c27bed6612 100644
--- a/tests/manual/quickcontrols/buttons/buttons.cpp
+++ b/tests/manual/quickcontrols/buttons/buttons.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/quickcontrols/buttons/buttons.qml b/tests/manual/quickcontrols/buttons/buttons.qml
index 6c51c809e6..644e714a85 100644
--- a/tests/manual/quickcontrols/buttons/buttons.qml
+++ b/tests/manual/quickcontrols/buttons/buttons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
diff --git a/tests/manual/quickcontrols/fonts/main.cpp b/tests/manual/quickcontrols/fonts/main.cpp
index eeee73e053..c0b285e76d 100644
--- a/tests/manual/quickcontrols/fonts/main.cpp
+++ b/tests/manual/quickcontrols/fonts/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/quickcontrols/fonts/main.qml b/tests/manual/quickcontrols/fonts/main.qml
index 3f1b5a91ed..1610b5abb4 100644
--- a/tests/manual/quickcontrols/fonts/main.qml
+++ b/tests/manual/quickcontrols/fonts/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -27,6 +27,7 @@ ApplicationWindow {
to: 48
value: 12
stepSize: 1
+ Layout.fillWidth: false
}
Label {
text: pointSizeSlider.value + " pt " + font.family
@@ -34,6 +35,7 @@ ApplicationWindow {
Button {
text: "Font…"
palette.buttonText: systemPalette.buttonText
+ Layout.fillWidth: false
onClicked: fontDialog.open()
FontDialog { id: fontDialog }
Component.onCompleted: fontDialog.selectedFont = window.font
@@ -42,6 +44,7 @@ ApplicationWindow {
Button {
text: "Text…"
palette.buttonText: textColorDialog.selectedColor
+ Layout.fillWidth: false
onClicked: textColorDialog.open()
ColorDialog { id: textColorDialog }
Component.onCompleted: textColorDialog.selectedColor = systemPalette.text
@@ -50,6 +53,7 @@ ApplicationWindow {
}
Button {
text: "Buttons…"
+ Layout.fillWidth: false
onClicked: buttonTextColorDialog.open()
ColorDialog { id: buttonTextColorDialog }
Component.onCompleted: buttonTextColorDialog.selectedColor = systemPalette.buttonText
@@ -72,20 +76,44 @@ ApplicationWindow {
text: "Label with **Bold** *Italics* _Underline_ ~~Strikethrough~~ `Mono`"
textFormat: Label.MarkdownText
}
- Button { text: "Button" }
+ Button {
+ text: "Button"
+ Layout.fillWidth: false
+ }
GroupBox {
title: "GroupBox"
+ Layout.fillWidth: false
+ Layout.fillHeight: false
ColumnLayout {
RadioButton { text: "RadioButton" }
CheckBox { text: "CheckBox" }
}
}
- Switch { text: "Switch" }
- TabButton { text: "TabButton" }
- TextField { placeholderText: "TextField" }
- TextArea { placeholderText: "TextArea" }
- ToolButton { text: "ToolButton" }
- Tumbler { model: 3 }
+ Switch {
+ text: "Switch"
+ Layout.fillWidth: false
+ }
+ TabButton {
+ text: "TabButton"
+ Layout.fillWidth: false
+ }
+ TextField {
+ placeholderText: "TextField"
+ Layout.fillWidth: false
+ }
+ TextArea {
+ placeholderText: "TextArea"
+ Layout.fillWidth: false
+ Layout.fillHeight: false
+ }
+ ToolButton {
+ text: "ToolButton"
+ }
+ Tumbler {
+ model: 3
+ Layout.fillWidth: false
+ Layout.fillHeight: false
+ }
}
ScrollBar.vertical: ScrollBar { }
diff --git a/tests/manual/quickcontrols/gifs/CMakeLists.txt b/tests/manual/quickcontrols/gifs/CMakeLists.txt
index 148aefe80f..674806e145 100644
--- a/tests/manual/quickcontrols/gifs/CMakeLists.txt
+++ b/tests/manual/quickcontrols/gifs/CMakeLists.txt
@@ -13,13 +13,12 @@ endif()
## tst_gifs Test:
#####################################################################
-# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/data/*)
list(APPEND test_data ${test_data_glob})
-qt_internal_add_manual_test(tst_gifs
+qt_internal_add_test(tst_gifs
SOURCES
capturedevent.cpp capturedevent.h
eventcapturer.cpp eventcapturer.h
@@ -28,9 +27,21 @@ qt_internal_add_manual_test(tst_gifs
LIBRARIES
Qt::Gui
Qt::Quick
+ Qt::Test
TESTDATA ${test_data}
)
+# Make the QML files available to Creator's locator.
+target_sources(tst_gifs
+ PRIVATE
+ ${test_data}
+)
+
+set_source_files_properties(${test_data}
+ PROPERTIES
+ HEADER_FILE_ONLY ON
+)
+
#### Keys ignored in scope 1:.:.:gifs.pro:<TRUE>:
# TEMPLATE = "app"
diff --git a/tests/manual/quickcontrols/gifs/capturedevent.cpp b/tests/manual/quickcontrols/gifs/capturedevent.cpp
index b693161814..4e67e9a039 100644
--- a/tests/manual/quickcontrols/gifs/capturedevent.cpp
+++ b/tests/manual/quickcontrols/gifs/capturedevent.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "capturedevent.h"
diff --git a/tests/manual/quickcontrols/gifs/capturedevent.h b/tests/manual/quickcontrols/gifs/capturedevent.h
index 4ffa8ff72d..fe9b179962 100644
--- a/tests/manual/quickcontrols/gifs/capturedevent.h
+++ b/tests/manual/quickcontrols/gifs/capturedevent.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CAPTUREDEVENT_H
#define CAPTUREDEVENT_H
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-busyindicator.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-busyindicator.qml
index 1463a48e42..ea977dffb8 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-busyindicator.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-busyindicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-flat.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-flat.qml
index c08885ccf7..030351e65a 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-flat.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-flat.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-highlighted.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-highlighted.qml
index 7d6baef42a..6085edf63c 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-highlighted.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button-highlighted.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button.qml
index 895a143762..4d55b72aae 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox-tristate.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox-tristate.qml
index 55d9bc28e3..490eb0fe9c 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox-tristate.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox-tristate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -23,18 +23,21 @@ Window {
checkState: english.checked && norwegian.checked
? Qt.Checked : (english.checked || norwegian.checked) ? Qt.PartiallyChecked : Qt.Unchecked
tristate: true
+ Layout.fillWidth: false
}
CheckBox {
id: english
text: qsTr("English")
checked: true
leftPadding: indicator.width
+ Layout.fillWidth: false
}
CheckBox {
id: norwegian
text: qsTr("Norwegian")
checked: true
leftPadding: indicator.width
+ Layout.fillWidth: false
}
}
}
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox.qml
index 867bb6142e..7d99c7421b 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkbox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -21,15 +21,18 @@ Window {
CheckBox {
checked: true
text: qsTr("First")
+ Layout.fillWidth: false
}
CheckBox {
id: control2
text: qsTr("Second")
+ Layout.fillWidth: false
}
CheckBox {
id: control3
checked: true
text: qsTr("Third")
+ Layout.fillWidth: false
}
}
}
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate-tristate.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate-tristate.qml
index 3dfec43cf5..4294170c94 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate-tristate.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate-tristate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate.qml
index 274f7580e9..e67960f335 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-checkdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-combobox.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-combobox.qml
index 0982ff0bf0..67739ec3e6 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-combobox.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-combobox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-delaybutton.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-delaybutton.qml
index e044dc7744..18a2980b8e 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-delaybutton.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-delaybutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-no-wrap.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-no-wrap.qml
index 13641e17f4..88d9f6c449 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-no-wrap.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-no-wrap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-wrap.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-wrap.qml
index 3a47350a4f..c08b0d9d2c 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-wrap.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-dial-wrap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-drawer.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-drawer.qml
index 2cf05730af..c93f4c47a1 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-drawer.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-drawer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-itemdelegate.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-itemdelegate.qml
index b4c7f29ec8..6f8011ecc9 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-itemdelegate.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-itemdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-menu.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-menu.qml
index f39cc8f7c5..ce4e58c025 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-menu.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar-indeterminate.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar-indeterminate.qml
index 818c1a28ec..ce49f6c844 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar-indeterminate.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar-indeterminate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar.qml
index 2faca20388..3fa61db4ea 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-progressbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiobutton.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiobutton.qml
index 7e4c391673..8869cf6659 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiobutton.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiobutton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -23,14 +23,17 @@ Window {
id: control1
text: qsTr("First")
checked: true
+ Layout.fillWidth: false
}
RadioButton {
id: control2
text: qsTr("Second")
+ Layout.fillWidth: false
}
RadioButton {
id: control3
text: qsTr("Third")
+ Layout.fillWidth: false
}
}
}
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiodelegate.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiodelegate.qml
index 7569c8d7b2..004f806494 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiodelegate.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-radiodelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-rangeslider.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-rangeslider.qml
index 6c7f548f48..42c4743e82 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-rangeslider.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-rangeslider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar-snap.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar-snap.qml
index 06c8bd96d5..73f84b610b 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar-snap.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar-snap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar.qml
index 63afabbefe..7e784bd467 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollindicator.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollindicator.qml
index f2f12bbe91..fbb48d0325 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollindicator.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-scrollindicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider-snap.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider-snap.qml
index ae5d8cf6cb..bcc57a4ca1 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider-snap.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider-snap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider.qml
index 134341ccfa..570fb67109 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-pop.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-pop.qml
index 64c44063a6..ad3d026c11 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-pop.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-pop.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-push.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-push.qml
index 523c482de8..6e241c4105 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-push.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-push.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-replace.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-replace.qml
index f1ba010b93..83e83cd6b6 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-replace.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-replace.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-unwind.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-unwind.qml
index 4631dce66a..bcef44cde0 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-unwind.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-stackview-unwind.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-behind.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-behind.qml
index 9a059e8c3d..2151e60afe 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-behind.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-behind.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-leading-trailing.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-leading-trailing.qml
index 7e09e7bcc6..b047ef4b64 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-leading-trailing.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate-leading-trailing.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate.qml
index a203f6fe83..48e67907b5 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipedelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipeview.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipeview.qml
index dcdc8832c9..dbc963dd69 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipeview.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-swipeview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-switch.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-switch.qml
index 7099def2fc..98672e35d9 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-switch.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-switch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-switchdelegate.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-switchdelegate.qml
index 123c4aec00..124e3eda29 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-switchdelegate.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-switchdelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-tabbar.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-tabbar.qml
index 2a2da93f87..fd811585e0 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-tabbar.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-tabbar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-tumbler-wrap.qml b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-tumbler-wrap.qml
index 1d6832bbc2..6cbd406493 100644
--- a/tests/manual/quickcontrols/gifs/data/qtquickcontrols-tumbler-wrap.qml
+++ b/tests/manual/quickcontrols/gifs/data/qtquickcontrols-tumbler-wrap.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/gifs/eventcapturer.cpp b/tests/manual/quickcontrols/gifs/eventcapturer.cpp
index 787e62a868..329426ab95 100644
--- a/tests/manual/quickcontrols/gifs/eventcapturer.cpp
+++ b/tests/manual/quickcontrols/gifs/eventcapturer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "eventcapturer.h"
diff --git a/tests/manual/quickcontrols/gifs/eventcapturer.h b/tests/manual/quickcontrols/gifs/eventcapturer.h
index 5833298c24..e595d01b9a 100644
--- a/tests/manual/quickcontrols/gifs/eventcapturer.h
+++ b/tests/manual/quickcontrols/gifs/eventcapturer.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef EVENTCAPTURER_H
#define EVENTCAPTURER_H
diff --git a/tests/manual/quickcontrols/gifs/gifrecorder.cpp b/tests/manual/quickcontrols/gifs/gifrecorder.cpp
index 77f965c2da..902341f11d 100644
--- a/tests/manual/quickcontrols/gifs/gifrecorder.cpp
+++ b/tests/manual/quickcontrols/gifs/gifrecorder.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "gifrecorder.h"
@@ -14,7 +14,7 @@
\note The following programs must be installed if \c setHighQuality(true)
is called:
- \li \e ffmpeg (sudo apt-get install ffmpeg)
+ \li \e FFmpeg (sudo apt-get install ffmpeg)
\li \e convert (sudo apt-get install imagemagick)
\li \e gifsicle (sudo apt-get install gifsicle)
@@ -228,7 +228,7 @@ void GifRecorder::waitForFinish()
QSignalSpy spy(mWindow, SIGNAL(frameSwapped()));
QVERIFY(spy.wait());
- // Start ffmpeg and send its output to imagemagick's convert command.
+ // Start FFmpeg and send its output to imagemagick's convert command.
// Based on the example in the documentation for QProcess::setStandardOutputProcess().
QProcess ffmpegProcess;
QProcess convertProcess;
diff --git a/tests/manual/quickcontrols/gifs/gifrecorder.h b/tests/manual/quickcontrols/gifs/gifrecorder.h
index c01a6da5e3..738e46d980 100644
--- a/tests/manual/quickcontrols/gifs/gifrecorder.h
+++ b/tests/manual/quickcontrols/gifs/gifrecorder.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef GIFRECORDER_H
#define GIFRECORDER_H
diff --git a/tests/manual/quickcontrols/gifs/tst_gifs.cpp b/tests/manual/quickcontrols/gifs/tst_gifs.cpp
index 72f7039374..4ca12b1b82 100644
--- a/tests/manual/quickcontrols/gifs/tst_gifs.cpp
+++ b/tests/manual/quickcontrols/gifs/tst_gifs.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtQuick>
diff --git a/tests/manual/quickcontrols/headerview/main.cpp b/tests/manual/quickcontrols/headerview/main.cpp
index fb4e191a88..ae248d08cf 100644
--- a/tests/manual/quickcontrols/headerview/main.cpp
+++ b/tests/manual/quickcontrols/headerview/main.cpp
@@ -1,10 +1,11 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QAbstractTableModel>
#include <QDebug>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
+#include <QSet>
class TestTableModel : public QAbstractTableModel {
Q_OBJECT
diff --git a/tests/manual/quickcontrols/headerview/main.qml b/tests/manual/quickcontrols/headerview/main.qml
index 89367d91bf..182ba10069 100644
--- a/tests/manual/quickcontrols/headerview/main.qml
+++ b/tests/manual/quickcontrols/headerview/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml.Models
import QtQuick
@@ -14,7 +14,7 @@ Window {
width: 640
height: 480
title: qsTr("HeaderView Test")
- color: Qt.styleHints.appearance === Qt.Light ? palette.mid : palette.midlight
+ color: Qt.styleHints.colorScheme === Qt.Light ? palette.mid : palette.midlight
TestTableModelWithHeader {
id: tableModel
diff --git a/tests/manual/quickcontrols/imagine/musicplayer/CMakeLists.txt b/tests/manual/quickcontrols/imagine/musicplayer/CMakeLists.txt
new file mode 100644
index 0000000000..9adc56f86e
--- /dev/null
+++ b/tests/manual/quickcontrols/imagine/musicplayer/CMakeLists.txt
@@ -0,0 +1,187 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(musicplayer LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/imagine/musicplayer")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
+
+qt_add_executable(musicplayerexample WIN32 MACOSX_BUNDLE
+ musicplayer.cpp
+)
+
+qt_add_qml_module(musicplayerexample
+ URI musicplayer
+ NO_RESOURCE_TARGET_PATH
+ QML_FILES
+ "musicplayer.qml"
+ RESOURCES
+ "icons/musicplayer/32x32/bluetooth.png"
+ "icons/musicplayer/32x32/cart.png"
+ "icons/musicplayer/32x32/cloud.png"
+ "icons/musicplayer/32x32/favorite.png"
+ "icons/musicplayer/32x32/filter.png"
+ "icons/musicplayer/32x32/folder.png"
+ "icons/musicplayer/32x32/message.png"
+ "icons/musicplayer/32x32/music.png"
+ "icons/musicplayer/32x32/next.png"
+ "icons/musicplayer/32x32/pause.png"
+ "icons/musicplayer/32x32/power.png"
+ "icons/musicplayer/32x32/previous.png"
+ "icons/musicplayer/32x32/repeat.png"
+ "icons/musicplayer/32x32/save.png"
+ "icons/musicplayer/32x32/settings.png"
+ "icons/musicplayer/32x32/shuffle.png"
+ "icons/musicplayer/32x32/stop.png"
+ "icons/musicplayer/32x32@2/bluetooth.png"
+ "icons/musicplayer/32x32@2/cart.png"
+ "icons/musicplayer/32x32@2/cloud.png"
+ "icons/musicplayer/32x32@2/favorite.png"
+ "icons/musicplayer/32x32@2/filter.png"
+ "icons/musicplayer/32x32@2/folder.png"
+ "icons/musicplayer/32x32@2/grid.png"
+ "icons/musicplayer/32x32@2/message.png"
+ "icons/musicplayer/32x32@2/music.png"
+ "icons/musicplayer/32x32@2/next.png"
+ "icons/musicplayer/32x32@2/pause.png"
+ "icons/musicplayer/32x32@2/power.png"
+ "icons/musicplayer/32x32@2/previous.png"
+ "icons/musicplayer/32x32@2/repeat.png"
+ "icons/musicplayer/32x32@2/save.png"
+ "icons/musicplayer/32x32@2/settings.png"
+ "icons/musicplayer/32x32@2/shuffle.png"
+ "icons/musicplayer/32x32@2/stop.png"
+ "icons/musicplayer/index.theme"
+ "images/album-cover.jpg"
+ "imagine-assets/applicationwindow-background.png"
+ "imagine-assets/button-background-checked-hovered.9.png"
+ "imagine-assets/button-background-checked-hovered@2x.9.png"
+ "imagine-assets/button-background-checked.9.png"
+ "imagine-assets/button-background-checked@2x.9.png"
+ "imagine-assets/button-background-disabled.9.png"
+ "imagine-assets/button-background-disabled@2x.9.png"
+ "imagine-assets/button-background-hovered.9.png"
+ "imagine-assets/button-background-hovered@2x.9.png"
+ "imagine-assets/button-background-pressed.9.png"
+ "imagine-assets/button-background-pressed@2x.9.png"
+ "imagine-assets/button-background.9.png"
+ "imagine-assets/button-background@2x.9.png"
+ "imagine-assets/combobox-background-hovered.9.png"
+ "imagine-assets/combobox-background-hovered@2x.9.png"
+ "imagine-assets/combobox-background-open.9.png"
+ "imagine-assets/combobox-background-open@2x.9.png"
+ "imagine-assets/combobox-background-pressed.9.png"
+ "imagine-assets/combobox-background-pressed@2x.9.png"
+ "imagine-assets/combobox-background.9.png"
+ "imagine-assets/combobox-background@2x.9.png"
+ "imagine-assets/combobox-indicator-hovered.png"
+ "imagine-assets/combobox-indicator-hovered@2x.png"
+ "imagine-assets/combobox-indicator-open.png"
+ "imagine-assets/combobox-indicator-open@2x.png"
+ "imagine-assets/combobox-indicator-pressed.png"
+ "imagine-assets/combobox-indicator-pressed@2x.png"
+ "imagine-assets/combobox-indicator.png"
+ "imagine-assets/combobox-indicator@2x.png"
+ "imagine-assets/combobox-popup.9.png"
+ "imagine-assets/combobox-popup@2x.9.png"
+ "imagine-assets/dial-background-hovered.png"
+ "imagine-assets/dial-background-hovered@2x.png"
+ "imagine-assets/dial-background-pressed.png"
+ "imagine-assets/dial-background-pressed@2x.png"
+ "imagine-assets/dial-background.png"
+ "imagine-assets/dial-background@2x.png"
+ "imagine-assets/dial-handle-pressed.png"
+ "imagine-assets/dial-handle-pressed@2x.png"
+ "imagine-assets/dial-handle.png"
+ "imagine-assets/dial-handle@2x.png"
+ "imagine-assets/frame-background.9.png"
+ "imagine-assets/frame-background@2x.9.png"
+ "imagine-assets/itemdelegate-background-checked.9.png"
+ "imagine-assets/itemdelegate-background-checked@2x.9.png"
+ "imagine-assets/itemdelegate-background-disabled.9.png"
+ "imagine-assets/itemdelegate-background-disabled@2x.9.png"
+ "imagine-assets/itemdelegate-background-hovered.9.png"
+ "imagine-assets/itemdelegate-background-hovered@2x.9.png"
+ "imagine-assets/itemdelegate-background-pressed.9.png"
+ "imagine-assets/itemdelegate-background-pressed@2x.9.png"
+ "imagine-assets/itemdelegate-background.9.png"
+ "imagine-assets/itemdelegate-background@2x.9.png"
+ "imagine-assets/roundbutton-background-checked-hovered.png"
+ "imagine-assets/roundbutton-background-checked-hovered@2x.png"
+ "imagine-assets/roundbutton-background-checked.png"
+ "imagine-assets/roundbutton-background-checked@2x.png"
+ "imagine-assets/roundbutton-background-disabled.png"
+ "imagine-assets/roundbutton-background-disabled@2x.png"
+ "imagine-assets/roundbutton-background-hovered.png"
+ "imagine-assets/roundbutton-background-hovered@2x.png"
+ "imagine-assets/roundbutton-background-pressed.png"
+ "imagine-assets/roundbutton-background-pressed@2x.png"
+ "imagine-assets/roundbutton-background.png"
+ "imagine-assets/roundbutton-background@2x.png"
+ "imagine-assets/scrollbar-handle-interactive-hovered.png"
+ "imagine-assets/scrollbar-handle-interactive-hovered@2x.png"
+ "imagine-assets/scrollbar-handle-interactive-pressed.png"
+ "imagine-assets/scrollbar-handle-interactive-pressed@2x.png"
+ "imagine-assets/scrollbar-handle-interactive.png"
+ "imagine-assets/scrollbar-handle-interactive@2x.png"
+ "imagine-assets/slider-background-horizontal-disabled.9.png"
+ "imagine-assets/slider-background-horizontal-disabled@2x.9.png"
+ "imagine-assets/slider-background-horizontal.9.png"
+ "imagine-assets/slider-background-horizontal@2x.9.png"
+ "imagine-assets/slider-background-vertical-disabled.9.png"
+ "imagine-assets/slider-background-vertical-disabled@2x.9.png"
+ "imagine-assets/slider-background-vertical.9.png"
+ "imagine-assets/slider-background-vertical@2x.9.png"
+ "imagine-assets/slider-handle-disabled.png"
+ "imagine-assets/slider-handle-disabled@2x.png"
+ "imagine-assets/slider-handle-hovered.png"
+ "imagine-assets/slider-handle-hovered@2x.png"
+ "imagine-assets/slider-handle.png"
+ "imagine-assets/slider-handle@2x.png"
+ "imagine-assets/slider-progress-horizontal.9.png"
+ "imagine-assets/slider-progress-horizontal@2x.9.png"
+ "imagine-assets/slider-progress-vertical-disabled.9.png"
+ "imagine-assets/slider-progress-vertical-disabled@2x.9.png"
+ "imagine-assets/slider-progress-vertical.9.png"
+ "imagine-assets/slider-progress-vertical@2x.9.png"
+ "imagine-assets/textfield-background-disabled.9.png"
+ "imagine-assets/textfield-background-disabled@2x.9.png"
+ "imagine-assets/textfield-background.9.png"
+ "imagine-assets/textfield-background@2x.9.png"
+ "imagine-assets/toolbar-background.9.png"
+ "imagine-assets/toolbar-background@2x.9.png"
+ "imagine-assets/toolbutton-background-checked-hovered.9.png"
+ "imagine-assets/toolbutton-background-checked-hovered@2x.9.png"
+ "imagine-assets/toolbutton-background-checked.9.png"
+ "imagine-assets/toolbutton-background-checked@2x.9.png"
+ "imagine-assets/toolbutton-background-hovered.9.png"
+ "imagine-assets/toolbutton-background-hovered@2x.9.png"
+ "imagine-assets/toolbutton-background-pressed.9.png"
+ "imagine-assets/toolbutton-background-pressed@2x.9.png"
+ "imagine-assets/toolbutton-background.9.png"
+ "imagine-assets/toolbutton-background@2x.9.png"
+ "imagine-assets/tooltip-background.9.png"
+ "imagine-assets/tooltip-background@2x.9.png"
+ "qtquickcontrols2.conf"
+)
+
+target_link_libraries(musicplayerexample PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
+)
+
+install(TARGETS musicplayerexample
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/icons.qrc b/tests/manual/quickcontrols/imagine/musicplayer/icons/icons.qrc
index 870b4445e5..870b4445e5 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/icons.qrc
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/icons.qrc
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/bluetooth.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/bluetooth.png
index 925f564c9e..925f564c9e 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/bluetooth.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/bluetooth.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cart.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cart.png
index 4d5ad09955..4d5ad09955 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cart.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cart.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cloud.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cloud.png
index 62ef511833..62ef511833 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cloud.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/cloud.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/favorite.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/favorite.png
index 16ea84deb6..16ea84deb6 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/favorite.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/favorite.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/filter.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/filter.png
index 4472cead58..4472cead58 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/filter.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/filter.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/folder.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/folder.png
index 5dd05778fd..5dd05778fd 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/folder.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/folder.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/message.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/message.png
index 1d93b3c660..1d93b3c660 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/message.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/message.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/music.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/music.png
index 4448921389..4448921389 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/music.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/music.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/next.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/next.png
index ab9164e877..ab9164e877 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/next.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/next.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/pause.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/pause.png
index 9c9ef8fa60..9c9ef8fa60 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/pause.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/pause.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/power.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/power.png
index 8f7d0c778c..8f7d0c778c 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/power.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/power.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/previous.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/previous.png
index 3a0880fdbc..3a0880fdbc 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/previous.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/previous.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/repeat.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/repeat.png
index 7c8e8c1c2d..7c8e8c1c2d 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/repeat.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/repeat.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/save.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/save.png
index c0b2748327..c0b2748327 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/save.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/save.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/settings.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/settings.png
index 7992146a6e..7992146a6e 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/settings.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/settings.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/shuffle.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/shuffle.png
index 885782a219..885782a219 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/shuffle.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/shuffle.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/stop.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/stop.png
index 3e9c332732..3e9c332732 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/stop.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32/stop.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/bluetooth.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/bluetooth.png
index 2e70afc396..2e70afc396 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/bluetooth.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/bluetooth.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cart.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cart.png
index 5717dd6582..5717dd6582 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cart.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cart.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cloud.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cloud.png
index ce5ab9f991..ce5ab9f991 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cloud.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/cloud.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/favorite.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/favorite.png
index ddc9aa45c8..ddc9aa45c8 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/favorite.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/favorite.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/filter.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/filter.png
index 3632c556fe..3632c556fe 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/filter.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/filter.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/folder.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/folder.png
index 49d41e202e..49d41e202e 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/folder.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/folder.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/grid.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/grid.png
index 8a1624e70b..8a1624e70b 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/grid.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/grid.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/message.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/message.png
index 6650f26b6a..6650f26b6a 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/message.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/message.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/music.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/music.png
index 2cb8d49a67..2cb8d49a67 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/music.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/music.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/next.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/next.png
index d7bb4b278e..d7bb4b278e 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/next.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/next.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/pause.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/pause.png
index 8a081d02f3..8a081d02f3 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/pause.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/pause.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/power.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/power.png
index 1201e55b4f..1201e55b4f 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/power.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/power.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/previous.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/previous.png
index 4236afdbbd..4236afdbbd 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/previous.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/previous.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/repeat.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/repeat.png
index c8cb06d84d..c8cb06d84d 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/repeat.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/repeat.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/save.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/save.png
index 448b25e4be..448b25e4be 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/save.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/save.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/settings.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/settings.png
index 12a0cf6b76..12a0cf6b76 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/settings.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/settings.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/shuffle.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/shuffle.png
index 22b901cce9..22b901cce9 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/shuffle.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/shuffle.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/stop.png b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/stop.png
index 0521a943d4..0521a943d4 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/stop.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/32x32@2/stop.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/icons.svg b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/icons.svg
index 4cede992c3..4cede992c3 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/icons.svg
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/icons.svg
diff --git a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/index.theme b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/index.theme
index 2523293552..2523293552 100644
--- a/examples/quickcontrols/imagine/musicplayer/icons/musicplayer/index.theme
+++ b/tests/manual/quickcontrols/imagine/musicplayer/icons/musicplayer/index.theme
diff --git a/examples/quickcontrols/imagine/musicplayer/images/album-cover.jpg b/tests/manual/quickcontrols/imagine/musicplayer/images/album-cover.jpg
index 48bd6231ba..48bd6231ba 100644
--- a/examples/quickcontrols/imagine/musicplayer/images/album-cover.jpg
+++ b/tests/manual/quickcontrols/imagine/musicplayer/images/album-cover.jpg
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/applicationwindow-background.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/applicationwindow-background.png
index 036bf74692..036bf74692 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/applicationwindow-background.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/applicationwindow-background.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered.9.png
index c275d3242d..c275d3242d 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered@2x.9.png
index eec285f305..eec285f305 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked-hovered@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked.9.png
index 2d284a5bc8..2d284a5bc8 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked@2x.9.png
index fec385f539..fec385f539 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-checked@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled.9.png
index bcad147094..bcad147094 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled@2x.9.png
index 03aba1535c..03aba1535c 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-disabled@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered.9.png
index 962cf6778e..962cf6778e 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered@2x.9.png
index 841c0064e7..841c0064e7 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-hovered@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed.9.png
index f97f42b809..f97f42b809 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed@2x.9.png
index 970583495c..970583495c 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background-pressed@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background.9.png
index 74a8574fd3..74a8574fd3 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background@2x.9.png
index 251633853e..251633853e 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/button-background@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/button-background@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered.9.png
index f9f90df4f4..f9f90df4f4 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered@2x.9.png
index a4be50e154..a4be50e154 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-hovered@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open.9.png
index 147231ecb7..147231ecb7 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open@2x.9.png
index 0a978572e7..0a978572e7 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-open@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed.9.png
index 7726198ab1..7726198ab1 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed@2x.9.png
index d64d405239..d64d405239 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background-pressed@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background.9.png
index e919d50aa0..e919d50aa0 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background@2x.9.png
index 5effba7e8b..5effba7e8b 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-background@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered.png
index 78b0650555..78b0650555 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered@2x.png
index ec3c8734c8..ec3c8734c8 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-hovered@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open.png
index 7a831632fe..7a831632fe 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open@2x.png
index 47c664a2c8..47c664a2c8 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-open@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed.png
index 3340294f05..3340294f05 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed@2x.png
index aa35b88200..aa35b88200 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator-pressed@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator.png
index 2c7f557819..2c7f557819 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator@2x.png
index 20b9c44c34..20b9c44c34 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-indicator@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup.9.png
index cae75cd78e..cae75cd78e 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup@2x.9.png
index 392607bcdf..392607bcdf 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/combobox-popup@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered.png
index ac614678b3..ac614678b3 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered@2x.png
index b13783c23a..b13783c23a 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-hovered@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed.png
index 1bd74acac6..1bd74acac6 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed@2x.png
index ffd7d009eb..ffd7d009eb 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background-pressed@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background.png
index c81fcb700b..c81fcb700b 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background@2x.png
index be3e15476b..be3e15476b 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-background@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-background@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed.png
index d27f9355d5..d27f9355d5 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed@2x.png
index a44e141066..a44e141066 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle-pressed@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle.png
index 9992a83990..9992a83990 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle@2x.png
index 8816482a08..8816482a08 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/dial-handle@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/frame-background.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/frame-background.9.png
index 1e436f2b36..1e436f2b36 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/frame-background.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/frame-background.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/frame-background@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/frame-background@2x.9.png
index abdf3ab8e3..abdf3ab8e3 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/frame-background@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/frame-background@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/imagine-assets.qrc b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/imagine-assets.qrc
index 7bda4995d4..7bda4995d4 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/imagine-assets.qrc
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/imagine-assets.qrc
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked.9.png
index 6c7915aab9..6c7915aab9 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked@2x.9.png
index 24ddbce09a..24ddbce09a 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-checked@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled.9.png
index 65a670ebb5..65a670ebb5 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled@2x.9.png
index df81dd0a98..df81dd0a98 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-disabled@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered.9.png
index a8eb9e7a13..a8eb9e7a13 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered@2x.9.png
index 23559a28b4..23559a28b4 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-hovered@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed.9.png
index 7d7a2bb960..7d7a2bb960 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed@2x.9.png
index 911563fc0e..911563fc0e 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background-pressed@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background.9.png
index 65a670ebb5..65a670ebb5 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background@2x.9.png
index df81dd0a98..df81dd0a98 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/itemdelegate-background@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered.png
index fae98b91b7..fae98b91b7 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered@2x.png
index 1b575b0354..1b575b0354 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked-hovered@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked.png
index 859232e2e5..859232e2e5 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked@2x.png
index ef147a334a..ef147a334a 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-checked@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled.png
index 862f4a26d8..862f4a26d8 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled@2x.png
index f99e11fd08..f99e11fd08 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-disabled@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered.png
index 6239eb3214..6239eb3214 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered@2x.png
index aa2e8f46d8..aa2e8f46d8 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-hovered@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed.png
index 81d2cd7442..81d2cd7442 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed@2x.png
index fd82c0c38c..fd82c0c38c 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background-pressed@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background.png
index ab2e9ceea8..ab2e9ceea8 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background@2x.png
index 33e81bf619..33e81bf619 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/roundbutton-background@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered.png
index 2657743a81..2657743a81 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered@2x.png
index 60ade78071..60ade78071 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-hovered@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed.png
index 626ab8b6bb..626ab8b6bb 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed@2x.png
index 92789571d1..92789571d1 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive-pressed@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive.png
index a7a7347120..a7a7347120 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive@2x.png
index 837e179553..837e179553 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/scrollbar-handle-interactive@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled.9.png
index f45f588838..f45f588838 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled@2x.9.png
index 40e6d50567..40e6d50567 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal-disabled@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal.9.png
index a465f300cc..a465f300cc 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal@2x.9.png
index 7a3170f529..7a3170f529 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-horizontal@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled.9.png
index 0bee2685a0..0bee2685a0 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled@2x.9.png
index 7fed90c816..7fed90c816 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical-disabled@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical.9.png
index a5074864b0..a5074864b0 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical@2x.9.png
index a3974ebe3c..a3974ebe3c 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-background-vertical@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled.png
index 02a9df388d..02a9df388d 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled@2x.png
index b37cef3272..b37cef3272 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-disabled@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered.png
index 2048aa6b07..2048aa6b07 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered@2x.png
index 0515be8e11..0515be8e11 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle-hovered@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle.png
index 2a80f27f27..2a80f27f27 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle@2x.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle@2x.png
index 2944704118..2944704118 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle@2x.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-handle@2x.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal.9.png
index 1224522f9d..1224522f9d 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal@2x.9.png
index c304bfe5ad..c304bfe5ad 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-horizontal@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled.9.png
index 0e7b08bd0e..0e7b08bd0e 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled@2x.9.png
index d9654ceec4..d9654ceec4 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical-disabled@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical.9.png
index 928ff864f0..928ff864f0 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical@2x.9.png
index 3d640b277a..3d640b277a 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/slider-progress-vertical@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled.9.png
index de557cbc5f..de557cbc5f 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled@2x.9.png
index 23984d715d..23984d715d 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background-disabled@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background.9.png
index 0dc163a4a3..0dc163a4a3 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background@2x.9.png
index 41aae23d7f..41aae23d7f 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/textfield-background@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background.9.png
index ab2f47e243..ab2f47e243 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background@2x.9.png
index 7e389fe0f0..7e389fe0f0 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbar-background@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered.9.png
index 3acf92e12a..3acf92e12a 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered@2x.9.png
index 0f43f93337..0f43f93337 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked-hovered@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked.9.png
index ff0faa567b..ff0faa567b 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked@2x.9.png
index 694f5eab88..694f5eab88 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-checked@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered.9.png
index c60e64d10c..c60e64d10c 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered@2x.9.png
index d0103202bd..d0103202bd 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-hovered@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed.9.png
index f2ec9c6e4b..f2ec9c6e4b 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed@2x.9.png
index e3a86911ee..e3a86911ee 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background-pressed@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background.9.png
index a292ac0959..a292ac0959 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background@2x.9.png
index 6f8880c393..6f8880c393 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/toolbutton-background@2x.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background.9.png
index a74441c92a..a74441c92a 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background.9.png
Binary files differ
diff --git a/examples/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background@2x.9.png b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background@2x.9.png
index d4b20b74b3..d4b20b74b3 100644
--- a/examples/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background@2x.9.png
+++ b/tests/manual/quickcontrols/imagine/musicplayer/imagine-assets/tooltip-background@2x.9.png
Binary files differ
diff --git a/tests/manual/quickcontrols/imagine/musicplayer/musicplayer.cpp b/tests/manual/quickcontrols/imagine/musicplayer/musicplayer.cpp
new file mode 100644
index 0000000000..bb1e01b6bc
--- /dev/null
+++ b/tests/manual/quickcontrols/imagine/musicplayer/musicplayer.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QSettings>
+#include <QIcon>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication::setApplicationName("Music Player");
+ QGuiApplication::setOrganizationName("QtProject");
+
+ QGuiApplication app(argc, argv);
+
+ QIcon::setThemeName("musicplayer");
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl("qrc:/musicplayer.qml"));
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
diff --git a/examples/quickcontrols/imagine/musicplayer/musicplayer.pro b/tests/manual/quickcontrols/imagine/musicplayer/musicplayer.pro
index 67c716406e..67c716406e 100644
--- a/examples/quickcontrols/imagine/musicplayer/musicplayer.pro
+++ b/tests/manual/quickcontrols/imagine/musicplayer/musicplayer.pro
diff --git a/tests/manual/quickcontrols/imagine/musicplayer/musicplayer.qml b/tests/manual/quickcontrols/imagine/musicplayer/musicplayer.qml
new file mode 100644
index 0000000000..314e05586c
--- /dev/null
+++ b/tests/manual/quickcontrols/imagine/musicplayer/musicplayer.qml
@@ -0,0 +1,417 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Window
+
+ApplicationWindow {
+ id: window
+ width: 1280
+ height: 720
+ visible: true
+ title: "Qt Quick Controls - Imagine Style Example: Music Player"
+
+ Component.onCompleted: {
+ x = Screen.width / 2 - width / 2
+ y = Screen.height / 2 - height / 2
+ }
+
+ Shortcut {
+ sequence: "Ctrl+Q"
+ onActivated: Qt.quit()
+ }
+
+ header: ToolBar {
+ RowLayout {
+ id: headerRowLayout
+ anchors.fill: parent
+ spacing: 0
+
+ ToolButton { icon.name: "grid" }
+ ToolButton { icon.name: "settings" }
+ ToolButton { icon.name: "filter" }
+ ToolButton { icon.name: "message" }
+ ToolButton { icon.name: "music" }
+ ToolButton { icon.name: "cloud" }
+ ToolButton { icon.name: "bluetooth" }
+ ToolButton { icon.name: "cart" }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ ToolButton {
+ icon.name: "power"
+ onClicked: Qt.quit()
+ }
+ }
+ }
+
+ Label {
+ text: "Qtify"
+ font.pixelSize: Qt.application.font.pixelSize * 1.3
+ anchors.centerIn: header
+ z: header.z + 1
+ }
+
+ RowLayout {
+ spacing: 115
+ anchors.fill: parent
+ anchors.margins: 70
+
+ ColumnLayout {
+ spacing: 0
+ Layout.preferredWidth: 230
+
+ RowLayout {
+ Layout.maximumHeight: 170
+
+ ColumnLayout {
+ Label {
+ text: "12 dB"
+ Layout.fillHeight: true
+ }
+ Label {
+ text: "6 dB"
+ Layout.fillHeight: true
+ }
+ Label {
+ text: "0 dB"
+ Layout.fillHeight: true
+ }
+ Label {
+ text: "-6 dB"
+ Layout.fillHeight: true
+ }
+ Label {
+ text: "-12 dB"
+ Layout.fillHeight: true
+ }
+ }
+
+ Repeater {
+ model: 7
+
+ Slider {
+ value: Math.random()
+ orientation: Qt.Vertical
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ }
+
+ RowLayout {
+ spacing: 10
+ Layout.topMargin: 23
+
+ ComboBox {
+ currentIndex: 1
+ model: ["Blues", "Classical", "Jazz", "Metal"]
+ Layout.fillWidth: true
+ }
+
+ Button {
+ icon.name: "folder"
+ Layout.fillWidth: false
+ }
+
+ Button {
+ icon.name: "save"
+ enabled: false
+ Layout.fillWidth: false
+ }
+ }
+
+ Dial {
+ Layout.alignment: Qt.AlignHCenter
+ Layout.topMargin: 50
+ }
+
+ Label {
+ text: "Volume"
+
+ Layout.alignment: Qt.AlignHCenter
+ Layout.topMargin: 12
+ }
+ }
+
+ ColumnLayout {
+ spacing: 26
+ Layout.preferredWidth: 230
+
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ Image {
+ anchors.fill: parent
+ fillMode: Image.PreserveAspectCrop
+ source: "images/album-cover.jpg"
+ }
+ }
+
+ Item {
+ id: songLabelContainer
+ clip: true
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: songNameLabel.implicitHeight
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ PauseAnimation {
+ duration: 2000
+ }
+ ParallelAnimation {
+ XAnimator {
+ target: songNameLabel
+ from: 0
+ to: songLabelContainer.width - songNameLabel.implicitWidth
+ duration: 5000
+ }
+ OpacityAnimator {
+ target: leftGradient
+ from: 0
+ to: 1
+ }
+ }
+ OpacityAnimator {
+ target: rightGradient
+ from: 1
+ to: 0
+ }
+ PauseAnimation {
+ duration: 1000
+ }
+ OpacityAnimator {
+ target: rightGradient
+ from: 0
+ to: 1
+ }
+ ParallelAnimation {
+ XAnimator {
+ target: songNameLabel
+ from: songLabelContainer.width - songNameLabel.implicitWidth
+ to: 0
+ duration: 5000
+ }
+ OpacityAnimator {
+ target: leftGradient
+ from: 0
+ to: 1
+ }
+ }
+ OpacityAnimator {
+ target: leftGradient
+ from: 1
+ to: 0
+ }
+ }
+
+ Rectangle {
+ id: leftGradient
+ gradient: Gradient {
+ GradientStop {
+ position: 0
+ color: "#dfe4ea"
+ }
+ GradientStop {
+ position: 1
+ color: "#00dfe4ea"
+ }
+ }
+
+ width: height
+ height: parent.height
+ anchors.left: parent.left
+ z: 1
+ rotation: -90
+ opacity: 0
+ }
+
+ Label {
+ id: songNameLabel
+ text: "Edvard Grieg - In the Hall of the Mountain King"
+ font.pixelSize: Qt.application.font.pixelSize * 1.4
+ }
+
+ Rectangle {
+ id: rightGradient
+ gradient: Gradient {
+ GradientStop {
+ position: 0
+ color: "#00dfe4ea"
+ }
+ GradientStop {
+ position: 1
+ color: "#dfe4ea"
+ }
+ }
+
+ width: height
+ height: parent.height
+ anchors.right: parent.right
+ rotation: -90
+ }
+ }
+
+ RowLayout {
+ spacing: 8
+ Layout.alignment: Qt.AlignHCenter
+
+ RoundButton {
+ icon.name: "favorite"
+ icon.width: 32
+ icon.height: 32
+ Layout.fillWidth: false
+ }
+ RoundButton {
+ icon.name: "stop"
+ icon.width: 32
+ icon.height: 32
+ Layout.fillWidth: false
+ }
+ RoundButton {
+ icon.name: "previous"
+ icon.width: 32
+ icon.height: 32
+ Layout.fillWidth: false
+ }
+ RoundButton {
+ icon.name: "pause"
+ icon.width: 32
+ icon.height: 32
+ Layout.fillWidth: false
+ }
+ RoundButton {
+ icon.name: "next"
+ icon.width: 32
+ icon.height: 32
+ Layout.fillWidth: false
+ }
+ RoundButton {
+ icon.name: "repeat"
+ icon.width: 32
+ icon.height: 32
+ Layout.fillWidth: false
+ }
+ RoundButton {
+ icon.name: "shuffle"
+ icon.width: 32
+ icon.height: 32
+ Layout.fillWidth: false
+ }
+ }
+
+ Slider {
+ id: seekSlider
+ value: 113
+ to: 261
+
+ Layout.fillWidth: true
+
+ ToolTip {
+ parent: seekSlider.handle
+ visible: seekSlider.pressed
+ text: pad(Math.floor(value / 60)) + ":" + pad(Math.floor(value % 60))
+ y: parent.height
+
+ readonly property int value: seekSlider.valueAt(seekSlider.position)
+
+ function pad(number) {
+ if (number <= 9)
+ return "0" + number;
+ return number;
+ }
+ }
+ }
+ }
+
+ ColumnLayout {
+ spacing: 16
+ Layout.preferredWidth: 230
+
+ ButtonGroup {
+ buttons: libraryRowLayout.children
+ }
+
+ RowLayout {
+ id: libraryRowLayout
+ Layout.alignment: Qt.AlignHCenter
+
+ Button {
+ text: "Files"
+ checked: true
+ Layout.fillWidth: false
+ }
+ Button {
+ text: "Playlists"
+ checkable: true
+ Layout.fillWidth: false
+ }
+ Button {
+ text: "Favourites"
+ checkable: true
+ Layout.fillWidth: false
+ }
+ }
+
+ RowLayout {
+ TextField {
+ Layout.fillWidth: true
+ }
+ Button {
+ icon.name: "folder"
+ Layout.fillWidth: false
+ }
+ }
+
+ Frame {
+ id: filesFrame
+ leftPadding: 1
+ rightPadding: 1
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ ListView {
+ id: filesListView
+ clip: true
+ anchors.fill: parent
+ model: ListModel {
+ Component.onCompleted: {
+ for (var i = 0; i < 100; ++i) {
+ append({
+ author: "Author",
+ album: "Album",
+ track: "Track 0" + (i % 9 + 1),
+ });
+ }
+ }
+ }
+ delegate: ItemDelegate {
+ text: model.author + " - " + model.album + " - " + model.track
+ width: filesListView.width
+ }
+
+ ScrollBar.vertical: ScrollBar {
+ parent: filesFrame
+ policy: ScrollBar.AlwaysOn
+ anchors.top: parent.top
+ anchors.topMargin: filesFrame.topPadding
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: filesFrame.bottomPadding
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/quickcontrols/imagine/musicplayer/qtquickcontrols2.conf b/tests/manual/quickcontrols/imagine/musicplayer/qtquickcontrols2.conf
new file mode 100644
index 0000000000..df765ea7d2
--- /dev/null
+++ b/tests/manual/quickcontrols/imagine/musicplayer/qtquickcontrols2.conf
@@ -0,0 +1,8 @@
+[Controls]
+Style=Imagine
+
+[Imagine]
+Path=:/imagine-assets
+Palette\Text=#6b7b89
+Palette\ButtonText=#6b7b89
+Palette\WindowText=#6b7b89
diff --git a/tests/manual/quickcontrols/manual.pro b/tests/manual/quickcontrols/manual.pro
index 7010d902f1..874fdef2e7 100644
--- a/tests/manual/quickcontrols/manual.pro
+++ b/tests/manual/quickcontrols/manual.pro
@@ -4,6 +4,7 @@ SUBDIRS += \
fonts \
gifs \
headerview \
+ imagine/musicplayer \
qquickdialog \
screenshots \
styles \
diff --git a/tests/manual/quickcontrols/material/CMakeLists.txt b/tests/manual/quickcontrols/material/CMakeLists.txt
index 004d3e8708..8c18341bbe 100644
--- a/tests/manual/quickcontrols/material/CMakeLists.txt
+++ b/tests/manual/quickcontrols/material/CMakeLists.txt
@@ -24,6 +24,8 @@ set(qmake_immediate_resource_files
"pages/DelayButtonPage.qml"
"pages/RoundButtonPage.qml"
"pages/SwitchPage.qml"
+ "pages/TextAreaPage.qml"
+ "pages/TextFieldPage.qml"
"qmldir"
)
diff --git a/tests/manual/quickcontrols/material/Constants.qml b/tests/manual/quickcontrols/material/Constants.qml
index 51b9b6ff23..c2e46e6212 100644
--- a/tests/manual/quickcontrols/material/Constants.qml
+++ b/tests/manual/quickcontrols/material/Constants.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
pragma Singleton
import QtQml
diff --git a/tests/manual/quickcontrols/material/material.cpp b/tests/manual/quickcontrols/material/material.cpp
index 2e257b0902..aea1c937fe 100644
--- a/tests/manual/quickcontrols/material/material.cpp
+++ b/tests/manual/quickcontrols/material/material.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSettings>
diff --git a/tests/manual/quickcontrols/material/material.qml b/tests/manual/quickcontrols/material/material.qml
index 78efaf72e3..5aae5211db 100644
--- a/tests/manual/quickcontrols/material/material.qml
+++ b/tests/manual/quickcontrols/material/material.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtCore
import QtQuick
import QtQuick.Controls.Material
@@ -53,6 +53,7 @@ ApplicationWindow {
ToolButton {
action: openDrawerAction
+ Layout.fillWidth: false
}
Label {
@@ -73,11 +74,13 @@ ApplicationWindow {
id: darkThemeSwitch
text: "Dark"
checked: settings.theme === Material.Dark
+ Layout.fillWidth: false
}
Switch {
id: denseSwitch
text: "Dense"
+ Layout.fillWidth: false
checked: settings.variant === "Dense"
ToolTip.text: "Requires restart"
@@ -97,7 +100,7 @@ ApplicationWindow {
focus: true
currentIndex: settings.currentControlIndex
anchors.fill: parent
- model: ["Button", "DelayButton", "RoundButton", "Switch"]
+ model: ["Button", "DelayButton", "RoundButton", "Switch", "TextArea", "TextField"]
delegate: ItemDelegate {
width: listView.width
text: modelData
diff --git a/tests/manual/quickcontrols/material/pages/ButtonPage.qml b/tests/manual/quickcontrols/material/pages/ButtonPage.qml
index 3f861e7a70..928f7c3194 100644
--- a/tests/manual/quickcontrols/material/pages/ButtonPage.qml
+++ b/tests/manual/quickcontrols/material/pages/ButtonPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
@@ -13,13 +13,22 @@ Page {
header: RowLayout {
CheckBox {
+ id: textCheckBox
+ text: "Text"
+ checked: true
+ Layout.fillWidth: false
+ }
+
+ CheckBox {
id: iconCheckBox
text: "Icon"
+ Layout.fillWidth: false
}
CheckBox {
id: disabledCheckBox
text: "Disabled"
+ Layout.fillWidth: false
}
Item {
@@ -88,7 +97,7 @@ Page {
model: 13
Button {
- text: modelData
+ text: textCheckBox.checked ? modelData : ""
flat: flatCheckBox.checked
icon.source: iconCheckBox.checked ? "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png" : ""
diff --git a/tests/manual/quickcontrols/material/pages/DelayButtonPage.qml b/tests/manual/quickcontrols/material/pages/DelayButtonPage.qml
index ee9c12f446..cfdebf742c 100644
--- a/tests/manual/quickcontrols/material/pages/DelayButtonPage.qml
+++ b/tests/manual/quickcontrols/material/pages/DelayButtonPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
@@ -15,6 +15,7 @@ Page {
CheckBox {
id: disabledCheckBox
text: "Disabled"
+ Layout.fillWidth: false
}
Item {
diff --git a/tests/manual/quickcontrols/material/pages/RoundButtonPage.qml b/tests/manual/quickcontrols/material/pages/RoundButtonPage.qml
index 1fbcbf2e45..bc9d3336f9 100644
--- a/tests/manual/quickcontrols/material/pages/RoundButtonPage.qml
+++ b/tests/manual/quickcontrols/material/pages/RoundButtonPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
@@ -15,11 +15,13 @@ Page {
CheckBox {
id: iconCheckBox
text: "Icon"
+ Layout.fillWidth: false
}
CheckBox {
id: disabledCheckBox
text: "Disabled"
+ Layout.fillWidth: false
}
Item {
@@ -46,6 +48,7 @@ Page {
text: "Flat"
Layout.leftMargin: elevationLayout.contentLeftMargin
+ Layout.fillWidth: false
}
}
@@ -65,6 +68,7 @@ Page {
Material.elevation: modelData
Layout.leftMargin: elevationLayout.contentLeftMargin
+ Layout.fillWidth: false
}
}
diff --git a/tests/manual/quickcontrols/material/pages/SwitchPage.qml b/tests/manual/quickcontrols/material/pages/SwitchPage.qml
index 5335fa6290..12c553a1c5 100644
--- a/tests/manual/quickcontrols/material/pages/SwitchPage.qml
+++ b/tests/manual/quickcontrols/material/pages/SwitchPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Layouts
@@ -17,22 +17,26 @@ Page {
Switch {
text: "Switch"
+ Layout.fillWidth: false
}
Switch {
text: "Checked Switch"
checked: true
+ Layout.fillWidth: false
}
Switch {
text: "Disabled Switch"
enabled: false
+ Layout.fillWidth: false
}
Switch {
text: "Checked disabled Switch"
checked: true
enabled: false
+ Layout.fillWidth: false
}
Item {
@@ -46,18 +50,21 @@ Page {
Switch {
text: "Switch"
icon.source: Constants.iconSource
+ Layout.fillWidth: false
}
Switch {
text: "Checked Switch"
icon.source: Constants.iconSource
checked: true
+ Layout.fillWidth: false
}
Switch {
text: "Disabled Switch"
icon.source: Constants.iconSource
enabled: false
+ Layout.fillWidth: false
}
Switch {
@@ -65,6 +72,7 @@ Page {
icon.source: Constants.iconSource
checked: true
enabled: false
+ Layout.fillWidth: false
}
Item {
diff --git a/tests/manual/quickcontrols/material/pages/TextAreaPage.qml b/tests/manual/quickcontrols/material/pages/TextAreaPage.qml
new file mode 100644
index 0000000000..9de28d1118
--- /dev/null
+++ b/tests/manual/quickcontrols/material/pages/TextAreaPage.qml
@@ -0,0 +1,194 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Controls.Material
+import QtQuick.Layouts
+
+import ".."
+
+Page {
+ id: root
+ topPadding: Constants.pageTopPadding
+
+// Component.onCompleted: Material.background = "red"
+
+ component TextAreaFlow: Flow {
+ id: layout
+ spacing: 40
+
+ required property int containerStyle
+
+ TextArea {
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ placeholderText: "placeholderText"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "text"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "text\nmore text"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "text"
+ placeholderText: "placeholderText"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "text\nmore text"
+ placeholderText: "placeholderText"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ placeholderText: "Disabled placeholder"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "Disabled text"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "Disabled text\nMore text"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "text"
+ placeholderText: "placeholderText"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "text\nmore text"
+ placeholderText: "placeholderText"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "0 leftPadding"
+ placeholderText: "placeholderText"
+ leftPadding: 0
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "0 rightPadding"
+ placeholderText: "placeholderText"
+ rightPadding: 0
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "0 topPadding"
+ placeholderText: "placeholderText"
+ topPadding: 0
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "0 bottomPadding"
+ placeholderText: "placeholderText"
+ bottomPadding: 0
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ Flickable {
+ width: 200
+ height: 100
+
+ TextArea.flickable: TextArea {
+ placeholderText: "placeholderText"
+ text: "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn"
+
+ Material.containerStyle: layout.containerStyle
+ }
+ }
+
+ TextArea {
+ text: "AlignLeft"
+ placeholderText: "AlignLeft"
+ horizontalAlignment: TextArea.AlignLeft
+ // Make it a big bigger so it's easier to see the effects of alignment.
+ width: implicitWidth + 30
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "AlignHCenter"
+ placeholderText: "AlignMiddle"
+ horizontalAlignment: TextArea.AlignMiddle
+ width: implicitWidth + 30
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextArea {
+ text: "AlignRight"
+ placeholderText: "AlignRight"
+ horizontalAlignment: TextArea.AlignRight
+ width: implicitWidth + 30
+
+ Material.containerStyle: layout.containerStyle
+ }
+ }
+
+ ScrollView {
+ id: scrollView
+ anchors.fill: parent
+
+ ColumnLayout {
+ width: scrollView.availableWidth
+
+ Label {
+ text: "Filled"
+ }
+ TextAreaFlow {
+ containerStyle: Material.Filled
+
+ Layout.fillWidth: true
+ Layout.bottomMargin: 40
+ }
+
+ Label {
+ text: "Outlined"
+ }
+ TextAreaFlow {
+ containerStyle: Material.Outlined
+
+ Layout.fillWidth: true
+ }
+ }
+ }
+}
diff --git a/tests/manual/quickcontrols/material/pages/TextFieldPage.qml b/tests/manual/quickcontrols/material/pages/TextFieldPage.qml
new file mode 100644
index 0000000000..9aeef4b5c2
--- /dev/null
+++ b/tests/manual/quickcontrols/material/pages/TextFieldPage.qml
@@ -0,0 +1,118 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Controls.Material
+import QtQuick.Layouts
+
+import ".."
+
+Page {
+ topPadding: Constants.pageTopPadding
+
+ component TextFieldFlow: Flow {
+ id: layout
+ spacing: 40
+
+ required property int containerStyle
+
+ TextField {
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ placeholderText: "placeholderText"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "text"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "text"
+ placeholderText: "placeholderText"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ placeholderText: "Disabled placeholder"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "Disabled text"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "text"
+ placeholderText: "placeholderText"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "0 leftPadding"
+ placeholderText: "placeholderText"
+ leftPadding: 0
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "0 rightPadding"
+ placeholderText: "placeholderText"
+ rightPadding: 0
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "0 topPadding"
+ placeholderText: "placeholderText"
+ topPadding: 0
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "0 bottomPadding"
+ placeholderText: "placeholderText"
+ bottomPadding: 0
+
+ Material.containerStyle: layout.containerStyle
+ }
+ }
+
+ ColumnLayout {
+ width: parent.width
+
+ Label {
+ text: "Filled"
+ }
+ TextFieldFlow {
+ containerStyle: Material.Filled
+
+ Layout.fillWidth: true
+ Layout.bottomMargin: 40
+ }
+
+ Label {
+ text: "Outlined"
+ }
+ TextFieldFlow {
+ containerStyle: Material.Outlined
+
+ Layout.fillWidth: true
+ }
+ }
+}
diff --git a/tests/manual/quickcontrols/nativestyle/Buttons.qml b/tests/manual/quickcontrols/nativestyle/Buttons.qml
index d2fd1e2893..1332f1e198 100644
--- a/tests/manual/quickcontrols/nativestyle/Buttons.qml
+++ b/tests/manual/quickcontrols/nativestyle/Buttons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CheckBoxes.qml b/tests/manual/quickcontrols/nativestyle/CheckBoxes.qml
index 86d083c355..899cae5def 100644
--- a/tests/manual/quickcontrols/nativestyle/CheckBoxes.qml
+++ b/tests/manual/quickcontrols/nativestyle/CheckBoxes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/ComboBoxes.qml b/tests/manual/quickcontrols/nativestyle/ComboBoxes.qml
index b32fa35038..9d3fd2d0ea 100644
--- a/tests/manual/quickcontrols/nativestyle/ComboBoxes.qml
+++ b/tests/manual/quickcontrols/nativestyle/ComboBoxes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/ControlContainer.qml b/tests/manual/quickcontrols/nativestyle/ControlContainer.qml
index 11727aef09..6f85f0d901 100644
--- a/tests/manual/quickcontrols/nativestyle/ControlContainer.qml
+++ b/tests/manual/quickcontrols/nativestyle/ControlContainer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomButtons.qml b/tests/manual/quickcontrols/nativestyle/CustomButtons.qml
index 9a3ecb4928..5208a35d3b 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomButtons.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomButtons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomCheckBoxes.qml b/tests/manual/quickcontrols/nativestyle/CustomCheckBoxes.qml
index 945669a3e5..a77ce31982 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomCheckBoxes.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomCheckBoxes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomComboBoxes.qml b/tests/manual/quickcontrols/nativestyle/CustomComboBoxes.qml
index d9da3d4de8..8a8a7af627 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomComboBoxes.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomComboBoxes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomDials.qml b/tests/manual/quickcontrols/nativestyle/CustomDials.qml
index 5a96741a7b..b7812b446b 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomDials.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomDials.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomFrames.qml b/tests/manual/quickcontrols/nativestyle/CustomFrames.qml
index 09194f48c3..b72e1e385a 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomFrames.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomFrames.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomProgressBars.qml b/tests/manual/quickcontrols/nativestyle/CustomProgressBars.qml
index 5ae4ce5bb0..a9b614611b 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomProgressBars.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomProgressBars.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomRadioButtons.qml b/tests/manual/quickcontrols/nativestyle/CustomRadioButtons.qml
index 7da347f891..a508e2465f 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomRadioButtons.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomRadioButtons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomScrollBars.qml b/tests/manual/quickcontrols/nativestyle/CustomScrollBars.qml
index b10e7b7933..09d469ba74 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomScrollBars.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomScrollBars.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomSliders.qml b/tests/manual/quickcontrols/nativestyle/CustomSliders.qml
index dcc0c29d1e..c5c0c45a48 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomSliders.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomSliders.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomSpinBoxes.qml b/tests/manual/quickcontrols/nativestyle/CustomSpinBoxes.qml
index eeaee25ea5..5938f2c30f 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomSpinBoxes.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomSpinBoxes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomTextAreas.qml b/tests/manual/quickcontrols/nativestyle/CustomTextAreas.qml
index ae8254f197..7a7f2a635e 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomTextAreas.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomTextAreas.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/CustomTextFields.qml b/tests/manual/quickcontrols/nativestyle/CustomTextFields.qml
index 2a2c659e0e..609263c630 100644
--- a/tests/manual/quickcontrols/nativestyle/CustomTextFields.qml
+++ b/tests/manual/quickcontrols/nativestyle/CustomTextFields.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/Dials.qml b/tests/manual/quickcontrols/nativestyle/Dials.qml
index b83e6aac66..5c7e0d1820 100644
--- a/tests/manual/quickcontrols/nativestyle/Dials.qml
+++ b/tests/manual/quickcontrols/nativestyle/Dials.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/Frames.qml b/tests/manual/quickcontrols/nativestyle/Frames.qml
index a7a8684c99..499a9a1bd7 100644
--- a/tests/manual/quickcontrols/nativestyle/Frames.qml
+++ b/tests/manual/quickcontrols/nativestyle/Frames.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/ProgressBars.qml b/tests/manual/quickcontrols/nativestyle/ProgressBars.qml
index b22a4fef3f..aa94c60a6c 100644
--- a/tests/manual/quickcontrols/nativestyle/ProgressBars.qml
+++ b/tests/manual/quickcontrols/nativestyle/ProgressBars.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/RadioButtons.qml b/tests/manual/quickcontrols/nativestyle/RadioButtons.qml
index b67e53c8e7..6290f5a1bc 100644
--- a/tests/manual/quickcontrols/nativestyle/RadioButtons.qml
+++ b/tests/manual/quickcontrols/nativestyle/RadioButtons.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/ScrollBars.qml b/tests/manual/quickcontrols/nativestyle/ScrollBars.qml
index 1517907a06..8b95af8250 100644
--- a/tests/manual/quickcontrols/nativestyle/ScrollBars.qml
+++ b/tests/manual/quickcontrols/nativestyle/ScrollBars.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/Sliders.qml b/tests/manual/quickcontrols/nativestyle/Sliders.qml
index 856df57162..004027ec2b 100644
--- a/tests/manual/quickcontrols/nativestyle/Sliders.qml
+++ b/tests/manual/quickcontrols/nativestyle/Sliders.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/SlidersMini.qml b/tests/manual/quickcontrols/nativestyle/SlidersMini.qml
index 2f36845cee..c3017f8fd8 100644
--- a/tests/manual/quickcontrols/nativestyle/SlidersMini.qml
+++ b/tests/manual/quickcontrols/nativestyle/SlidersMini.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/SlidersSmall.qml b/tests/manual/quickcontrols/nativestyle/SlidersSmall.qml
index 1339b42a9e..65010abdb3 100644
--- a/tests/manual/quickcontrols/nativestyle/SlidersSmall.qml
+++ b/tests/manual/quickcontrols/nativestyle/SlidersSmall.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/SpinBoxes.qml b/tests/manual/quickcontrols/nativestyle/SpinBoxes.qml
index 8dd4f8762b..b1c18dc690 100644
--- a/tests/manual/quickcontrols/nativestyle/SpinBoxes.qml
+++ b/tests/manual/quickcontrols/nativestyle/SpinBoxes.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/TextAreas.qml b/tests/manual/quickcontrols/nativestyle/TextAreas.qml
index 9eb2fb6a6b..b9393f96aa 100644
--- a/tests/manual/quickcontrols/nativestyle/TextAreas.qml
+++ b/tests/manual/quickcontrols/nativestyle/TextAreas.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/TextFields.qml b/tests/manual/quickcontrols/nativestyle/TextFields.qml
index 4448edbfff..bf8130d8b3 100644
--- a/tests/manual/quickcontrols/nativestyle/TextFields.qml
+++ b/tests/manual/quickcontrols/nativestyle/TextFields.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/nativestyle/main.cpp b/tests/manual/quickcontrols/nativestyle/main.cpp
index 6ecd80f5e2..badc25887b 100644
--- a/tests/manual/quickcontrols/nativestyle/main.cpp
+++ b/tests/manual/quickcontrols/nativestyle/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/quickcontrols/nativestyle/main.qml b/tests/manual/quickcontrols/nativestyle/main.qml
index 85a476e280..e2dc856292 100644
--- a/tests/manual/quickcontrols/nativestyle/main.qml
+++ b/tests/manual/quickcontrols/nativestyle/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/qquickdialog/CustomDialog.qml b/tests/manual/quickcontrols/qquickdialog/CustomDialog.qml
index c54b58603b..3aa150b2fc 100644
--- a/tests/manual/quickcontrols/qquickdialog/CustomDialog.qml
+++ b/tests/manual/quickcontrols/qquickdialog/CustomDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/qquickdialog/DialogLabel.qml b/tests/manual/quickcontrols/qquickdialog/DialogLabel.qml
index 273eaf162d..7c2d52953f 100644
--- a/tests/manual/quickcontrols/qquickdialog/DialogLabel.qml
+++ b/tests/manual/quickcontrols/qquickdialog/DialogLabel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/qquickdialog/Marker.qml b/tests/manual/quickcontrols/qquickdialog/Marker.qml
index 42f9c2d93b..9e8e5188a3 100644
--- a/tests/manual/quickcontrols/qquickdialog/Marker.qml
+++ b/tests/manual/quickcontrols/qquickdialog/Marker.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/manual/quickcontrols/qquickdialog/qquickdialog.cpp b/tests/manual/quickcontrols/qquickdialog/qquickdialog.cpp
index 92c060e6c4..16e4986c01 100644
--- a/tests/manual/quickcontrols/qquickdialog/qquickdialog.cpp
+++ b/tests/manual/quickcontrols/qquickdialog/qquickdialog.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/quickcontrols/qquickdialog/qquickdialog.qml b/tests/manual/quickcontrols/qquickdialog/qquickdialog.qml
index 2afa0898e8..a29869820e 100644
--- a/tests/manual/quickcontrols/qquickdialog/qquickdialog.qml
+++ b/tests/manual/quickcontrols/qquickdialog/qquickdialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/screenshots/screenshots.cpp b/tests/manual/quickcontrols/screenshots/screenshots.cpp
index f85ee9d258..c28ac5984c 100644
--- a/tests/manual/quickcontrols/screenshots/screenshots.cpp
+++ b/tests/manual/quickcontrols/screenshots/screenshots.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/quickcontrols/screenshots/screenshots.qml b/tests/manual/quickcontrols/screenshots/screenshots.qml
index cd8d52d658..9c4771920d 100644
--- a/tests/manual/quickcontrols/screenshots/screenshots.qml
+++ b/tests/manual/quickcontrols/screenshots/screenshots.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Layouts
diff --git a/tests/manual/quickcontrols/shared/FontAwesome.qml b/tests/manual/quickcontrols/shared/FontAwesome.qml
index d86620d149..3b654f9c11 100644
--- a/tests/manual/quickcontrols/shared/FontAwesome.qml
+++ b/tests/manual/quickcontrols/shared/FontAwesome.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
pragma Singleton
diff --git a/tests/manual/quickcontrols/shared/FontAwesomeIcon.qml b/tests/manual/quickcontrols/shared/FontAwesomeIcon.qml
index da98f26c73..273baad2b4 100644
--- a/tests/manual/quickcontrols/shared/FontAwesomeIcon.qml
+++ b/tests/manual/quickcontrols/shared/FontAwesomeIcon.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "qrc:/shared"
diff --git a/examples/quickcontrols/sidepanel/CMakeLists.txt b/tests/manual/quickcontrols/sidepanel/CMakeLists.txt
index fa65ea1f11..fa65ea1f11 100644
--- a/examples/quickcontrols/sidepanel/CMakeLists.txt
+++ b/tests/manual/quickcontrols/sidepanel/CMakeLists.txt
diff --git a/examples/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-landscape.png b/tests/manual/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-landscape.png
index 86907591b2..86907591b2 100644
--- a/examples/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-landscape.png
+++ b/tests/manual/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-landscape.png
Binary files differ
diff --git a/examples/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-portrait.png b/tests/manual/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-portrait.png
index cf1ed2efd0..cf1ed2efd0 100644
--- a/examples/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-portrait.png
+++ b/tests/manual/quickcontrols/sidepanel/doc/images/qtquickcontrols-sidepanel-portrait.png
Binary files differ
diff --git a/examples/quickcontrols/sidepanel/doc/src/qtquickcontrols-sidepanel.qdoc b/tests/manual/quickcontrols/sidepanel/doc/src/qtquickcontrols-sidepanel.qdoc
index 7029099d0f..7029099d0f 100644
--- a/examples/quickcontrols/sidepanel/doc/src/qtquickcontrols-sidepanel.qdoc
+++ b/tests/manual/quickcontrols/sidepanel/doc/src/qtquickcontrols-sidepanel.qdoc
diff --git a/examples/quickcontrols/sidepanel/images/qt-logo.png b/tests/manual/quickcontrols/sidepanel/images/qt-logo.png
index dff7729515..dff7729515 100644
--- a/examples/quickcontrols/sidepanel/images/qt-logo.png
+++ b/tests/manual/quickcontrols/sidepanel/images/qt-logo.png
Binary files differ
diff --git a/examples/quickcontrols/sidepanel/images/qt-logo@2x.png b/tests/manual/quickcontrols/sidepanel/images/qt-logo@2x.png
index dbd73aab77..dbd73aab77 100644
--- a/examples/quickcontrols/sidepanel/images/qt-logo@2x.png
+++ b/tests/manual/quickcontrols/sidepanel/images/qt-logo@2x.png
Binary files differ
diff --git a/examples/quickcontrols/sidepanel/images/qt-logo@3x.png b/tests/manual/quickcontrols/sidepanel/images/qt-logo@3x.png
index 68e763b597..68e763b597 100644
--- a/examples/quickcontrols/sidepanel/images/qt-logo@3x.png
+++ b/tests/manual/quickcontrols/sidepanel/images/qt-logo@3x.png
Binary files differ
diff --git a/examples/quickcontrols/sidepanel/images/qt-logo@4x.png b/tests/manual/quickcontrols/sidepanel/images/qt-logo@4x.png
index 08fd882572..08fd882572 100644
--- a/examples/quickcontrols/sidepanel/images/qt-logo@4x.png
+++ b/tests/manual/quickcontrols/sidepanel/images/qt-logo@4x.png
Binary files differ
diff --git a/tests/manual/quickcontrols/sidepanel/qtquickcontrols2.conf b/tests/manual/quickcontrols/sidepanel/qtquickcontrols2.conf
new file mode 100644
index 0000000000..b6c7c87ea1
--- /dev/null
+++ b/tests/manual/quickcontrols/sidepanel/qtquickcontrols2.conf
@@ -0,0 +1,2 @@
+[Controls]
+Style=Material
diff --git a/tests/manual/quickcontrols/sidepanel/sidepanel.cpp b/tests/manual/quickcontrols/sidepanel/sidepanel.cpp
new file mode 100644
index 0000000000..5a3e6b32ee
--- /dev/null
+++ b/tests/manual/quickcontrols/sidepanel/sidepanel.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl("qrc:/sidepanel.qml"));
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
diff --git a/examples/quickcontrols/sidepanel/sidepanel.pro b/tests/manual/quickcontrols/sidepanel/sidepanel.pro
index bbf118b361..bbf118b361 100644
--- a/examples/quickcontrols/sidepanel/sidepanel.pro
+++ b/tests/manual/quickcontrols/sidepanel/sidepanel.pro
diff --git a/tests/manual/quickcontrols/sidepanel/sidepanel.qml b/tests/manual/quickcontrols/sidepanel/sidepanel.qml
new file mode 100644
index 0000000000..e1b2f64678
--- /dev/null
+++ b/tests/manual/quickcontrols/sidepanel/sidepanel.qml
@@ -0,0 +1,132 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 360
+ height: 520
+ visible: true
+ title: qsTr("Side Panel")
+
+ //! [orientation]
+ readonly property bool inPortrait: window.width < window.height
+ //! [orientation]
+
+ ToolBar {
+ id: overlayHeader
+
+ z: 1
+ width: parent.width
+ parent: Overlay.overlay
+
+ Label {
+ id: label
+ anchors.centerIn: parent
+ text: "Qt Quick Controls"
+ }
+ }
+
+ Drawer {
+ id: drawer
+
+ y: overlayHeader.height
+ width: window.width / 2
+ height: window.height - overlayHeader.height
+
+ modal: inPortrait
+ interactive: inPortrait
+ position: inPortrait ? 0 : 1
+ visible: !inPortrait
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+
+ headerPositioning: ListView.OverlayHeader
+ header: Pane {
+ id: header
+ z: 2
+ width: parent.width
+
+ contentHeight: logo.height
+
+ Image {
+ id: logo
+ width: parent.width
+ source: "images/qt-logo.png"
+ fillMode: implicitWidth > width ? Image.PreserveAspectFit : Image.Pad
+ }
+
+ MenuSeparator {
+ parent: header
+ width: parent.width
+ anchors.verticalCenter: parent.bottom
+ visible: !listView.atYBeginning
+ }
+ }
+
+ footer: ItemDelegate {
+ id: footer
+ text: qsTr("Footer")
+ width: parent.width
+
+ MenuSeparator {
+ parent: footer
+ width: parent.width
+ anchors.verticalCenter: parent.top
+ }
+ }
+
+ model: 10
+
+ delegate: ItemDelegate {
+ text: qsTr("Title %1").arg(index + 1)
+ width: listView.width
+ }
+
+ ScrollIndicator.vertical: ScrollIndicator { }
+ }
+ }
+
+ Flickable {
+ id: flickable
+
+ anchors.fill: parent
+ anchors.topMargin: overlayHeader.height
+ anchors.leftMargin: !inPortrait ? drawer.width : undefined
+
+ topMargin: 20
+ bottomMargin: 20
+ contentHeight: column.height
+
+ Column {
+ id: column
+ spacing: 20
+ anchors.margins: 20
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ Label {
+ font.pixelSize: 22
+ width: parent.width
+ elide: Label.ElideRight
+ horizontalAlignment: Qt.AlignHCenter
+ text: qsTr("Side Panel Example")
+ }
+
+ Label {
+ width: parent.width
+ wrapMode: Label.WordWrap
+ text: qsTr("This example demonstrates how Drawer can be used as a non-closable persistent side panel.\n\n" +
+ "When the application is in portrait mode, the drawer is an interactive side panel that can " +
+ "be swiped open from the left edge. When the application is in landscape mode, the drawer " +
+ "and the content are laid out side by side.\n\nThe application is currently in %1 mode.").arg(inPortrait ? qsTr("portrait") : qsTr("landscape"))
+ }
+ }
+
+ ScrollIndicator.vertical: ScrollIndicator { }
+ }
+}
diff --git a/tests/manual/quickcontrols/styles-cover-flow/CoverFlowDelegate.qml b/tests/manual/quickcontrols/styles-cover-flow/CoverFlowDelegate.qml
index 7383b7fae8..9fabbd60de 100644
--- a/tests/manual/quickcontrols/styles-cover-flow/CoverFlowDelegate.qml
+++ b/tests/manual/quickcontrols/styles-cover-flow/CoverFlowDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtGraphicalEffects
import QtQuick
diff --git a/tests/manual/quickcontrols/styles-cover-flow/CoverFlowPath.qml b/tests/manual/quickcontrols/styles-cover-flow/CoverFlowPath.qml
index 408a3693f7..15869772d8 100644
--- a/tests/manual/quickcontrols/styles-cover-flow/CoverFlowPath.qml
+++ b/tests/manual/quickcontrols/styles-cover-flow/CoverFlowPath.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.cpp b/tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.cpp
index 80d5ff7235..d2625408f6 100644
--- a/tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.cpp
+++ b/tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.qml b/tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.qml
index 051732ec6b..b922a9e6c3 100644
--- a/tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.qml
+++ b/tests/manual/quickcontrols/styles-cover-flow/styles-cover-flow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/quickcontrols/styles/styles.cpp b/tests/manual/quickcontrols/styles/styles.cpp
index c1355c43cf..f3c1937dc1 100644
--- a/tests/manual/quickcontrols/styles/styles.cpp
+++ b/tests/manual/quickcontrols/styles/styles.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/quickcontrols/styles/styles.qml b/tests/manual/quickcontrols/styles/styles.qml
index 62a39e4071..d8e96db62e 100644
--- a/tests/manual/quickcontrols/styles/styles.qml
+++ b/tests/manual/quickcontrols/styles/styles.qml
@@ -1,5 +1,86 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*
+ This manual test is used to take screenshots of each style for use in the
+ documentation:
+
+ Step 1
+
+ Take screenshots of the app in the relevant styles in both light and dark
+ themes (where applicable) using the commented-out window hints.
+
+ The screenshots are usually taken on a MacBook with a DPI of 2.
+ On macOS, Cmd+Shift+4 starts the screenshot process, Space allows selecting
+ a window, holding Option before left-clicking takes a screenshot of the
+ window without the default drop shadow border.
+
+ If updating all styles, it can be helpful to have a script to open an
+ instance of the app for each style:
+
+ #! /bin/bash
+
+ app=$1
+
+ set -e
+
+ usageExample="Usage example:\run-with-all-controls-styles.sh ./tst_manual_qqc_styles"
+
+ if [ ! -f "$app" ]; then
+ echo "app \"$app\" doesn't exist in \"$PWD\"; aborting"
+ echo $usageExample
+ exit
+ fi
+
+ # For dark mode.
+ export QT_QUICK_CONTROLS_MATERIAL_THEME=Dark
+ export QT_QUICK_CONTROLS_UNIVERSAL_THEME=Dark
+
+ QT_QUICK_CONTROLS_STYLE=Basic $app 0 0 &
+ QT_QUICK_CONTROLS_STYLE=Fusion $app 400 0 &
+ QT_QUICK_CONTROLS_STYLE=macOS $app 800 0 &
+ QT_QUICK_CONTROLS_STYLE=Material $app 1200 0 &
+ QT_QUICK_CONTROLS_STYLE=Imagine $app 0 400 &
+ QT_QUICK_CONTROLS_STYLE=iOS $app 400 0 &
+ QT_QUICK_CONTROLS_STYLE=Universal $app 800 400 &
+ #QT_QUICK_CONTROLS_STYLE=Windows $app 1200 400 &
+
+ Step 2
+
+ Rename images accordingly.
+
+ Step 3
+
+ Until QTBUG-63366 is solved, and if taken on a display with a DPR > 1,
+ reduce the size of the images so that they are their natural (1 DPI) size.
+ For example, if taken on a display with a DPR of 2, halve them:
+
+ mogrify -resize 50% qtquickcontrols-basic.png
+ mogrify -resize 50% qtquickcontrols-fusion-light.png
+ mogrify -resize 50% qtquickcontrols-fusion-dark.png
+ mogrify -resize 50% qtquickcontrols-imagine.png
+ mogrify -resize 50% qtquickcontrols-ios-light.png
+ mogrify -resize 50% qtquickcontrols-ios-dark.png
+ mogrify -resize 50% qtquickcontrols-macos-light.png
+ mogrify -resize 50% qtquickcontrols-macos-dark.png
+ mogrify -resize 50% qtquickcontrols-material-light.png
+ mogrify -resize 50% qtquickcontrols-material-dark.png
+ mogrify -resize 50% qtquickcontrols-universal-light.png
+ mogrify -resize 50% qtquickcontrols-universal-dark.png
+ mogrify -resize 50% qtquickcontrols-windows.png
+
+ Step 4
+
+ Run "optipng -o 7 -strip all" on each image to reduce their file size.
+
+ Step 5
+
+ There may be extra screenshot images that need to be updated, depending
+ on which style is being updated. For example, the Material style has
+ screenshots that can be generated using tst_snippets:
+
+ SCREENSHOTS=1 QT_QUICK_CONTROLS_STYLE=Material ./tst_snippets verify:qtquickcontrols-material-accent verify:qtquickcontrols-material-attributes verify:qtquickcontrols-material-background verify:qtquickcontrols-material-elevation verify:qtquickcontrols-material-foreground verify:qtquickcontrols-material-theme verify:qtquickcontrols-material-variant
+*/
import QtQuick
import QtQuick.Layouts
@@ -9,16 +90,16 @@ ApplicationWindow {
id: window
visible: true
- minimumWidth: column.implicitWidth + 80
- minimumHeight: column.implicitHeight + 80
- title: "Qt Quick Controls 2 - Styles"
+ // Add some extra width and height to give the content a little more room.
+ minimumWidth: column.implicitWidth + column.anchors.margins + 120
+ minimumHeight: column.implicitHeight + column.anchors.margins + 20
+ title: "Qt Quick Controls - Styles"
// for taking frameless screenshots:
- // flags: Qt.Window | Qt.FramelessWindowHint
+// flags: Qt.Window | Qt.FramelessWindowHint
ColumnLayout {
id: column
-
spacing: 20
anchors.fill: parent
anchors.margins: 40
@@ -26,41 +107,81 @@ ApplicationWindow {
GroupBox {
title: "Font Size"
topPadding: 30
+ contentWidth: fontColumnLayout.implicitWidth + fontColumnLayout.anchors.leftMargin
background.visible: false
+ Layout.fillWidth: false
+ Layout.fillHeight: false
ColumnLayout {
+ id: fontColumnLayout
anchors.fill: parent
anchors.leftMargin: 20
- RadioButton { leftPadding: 0; text: "Small" }
- RadioButton { leftPadding: 0; text: "Medium"; checked: true }
- RadioButton { leftPadding: 0; text: "Large" }
+ RadioButton {
+ leftPadding: 0
+ text: "Small"
+ Layout.fillWidth: false
+ }
+ RadioButton {
+ leftPadding: 0
+ text: "Medium"
+ checked: true
+ Layout.fillWidth: false
+ }
+ RadioButton {
+ leftPadding: 0
+ text: "Large"
+ Layout.fillWidth: false
+ }
}
}
GroupBox {
title: "Audio"
topPadding: 30
+ contentWidth: audioGridLayout.implicitWidth + audioGridLayout.anchors.leftMargin
background.visible: false
+ Layout.fillWidth: true
+ Layout.fillHeight: false
+
GridLayout {
+ id: audioGridLayout
columns: 2
columnSpacing: 30
anchors.fill: parent
anchors.leftMargin: 20
- Label { text: "Volume" }
- Slider { value: 1.0 }
- Label { text: "Bass" }
- Slider { value: 0.75 }
- Label { text: "Treble" }
- Slider { value: 0.5 }
+ Label {
+ text: "Volume"
+ }
+ Slider {
+ value: 1.0
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: "Bass"
+ }
+ Slider {
+ value: 0.75
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: "Treble"
+ }
+ Slider {
+ value: 0.5
+ Layout.fillWidth: true
+ }
}
}
Button {
text: "Save"
Layout.alignment: Qt.AlignRight
+ Layout.fillWidth: false
}
Item { Layout.fillHeight: true }
diff --git a/tests/manual/quickcontrols/swipedelegate/CloseOnCompletedWorks.qml b/tests/manual/quickcontrols/swipedelegate/CloseOnCompletedWorks.qml
index fc29a94699..64f8971828 100644
--- a/tests/manual/quickcontrols/swipedelegate/CloseOnCompletedWorks.qml
+++ b/tests/manual/quickcontrols/swipedelegate/CloseOnCompletedWorks.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2
import QtQuick.Controls 2
diff --git a/examples/quickcontrols/swipetoremove/CMakeLists.txt b/tests/manual/quickcontrols/swipetoremove/CMakeLists.txt
index 4500695355..4500695355 100644
--- a/examples/quickcontrols/swipetoremove/CMakeLists.txt
+++ b/tests/manual/quickcontrols/swipetoremove/CMakeLists.txt
diff --git a/examples/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.gif b/tests/manual/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.gif
index 6142831356..6142831356 100644
--- a/examples/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.gif
+++ b/tests/manual/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.gif
Binary files differ
diff --git a/examples/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.png b/tests/manual/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.png
index 07a200fe92..07a200fe92 100644
--- a/examples/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.png
+++ b/tests/manual/quickcontrols/swipetoremove/doc/images/qtquickcontrols-swipetoremove.png
Binary files differ
diff --git a/examples/quickcontrols/swipetoremove/doc/src/qtquickcontrols-swipetoremove.qdoc b/tests/manual/quickcontrols/swipetoremove/doc/src/qtquickcontrols-swipetoremove.qdoc
index 29f4e652b1..29f4e652b1 100644
--- a/examples/quickcontrols/swipetoremove/doc/src/qtquickcontrols-swipetoremove.qdoc
+++ b/tests/manual/quickcontrols/swipetoremove/doc/src/qtquickcontrols-swipetoremove.qdoc
diff --git a/examples/quickcontrols/swipetoremove/fonts/LICENSE.txt b/tests/manual/quickcontrols/swipetoremove/fonts/LICENSE.txt
index 7394a58ddf..7394a58ddf 100644
--- a/examples/quickcontrols/swipetoremove/fonts/LICENSE.txt
+++ b/tests/manual/quickcontrols/swipetoremove/fonts/LICENSE.txt
diff --git a/examples/quickcontrols/swipetoremove/fonts/fontello.ttf b/tests/manual/quickcontrols/swipetoremove/fonts/fontello.ttf
index 553fd2efca..553fd2efca 100644
--- a/examples/quickcontrols/swipetoremove/fonts/fontello.ttf
+++ b/tests/manual/quickcontrols/swipetoremove/fonts/fontello.ttf
Binary files differ
diff --git a/tests/manual/quickcontrols/swipetoremove/swipetoremove.cpp b/tests/manual/quickcontrols/swipetoremove/swipetoremove.cpp
new file mode 100644
index 0000000000..c46926e2b2
--- /dev/null
+++ b/tests/manual/quickcontrols/swipetoremove/swipetoremove.cpp
@@ -0,0 +1,20 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QFontDatabase>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QFontDatabase::addApplicationFont(":/fonts/fontello.ttf");
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl("qrc:/swipetoremove.qml"));
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
diff --git a/examples/quickcontrols/swipetoremove/swipetoremove.pro b/tests/manual/quickcontrols/swipetoremove/swipetoremove.pro
index e16ff8145f..e16ff8145f 100644
--- a/examples/quickcontrols/swipetoremove/swipetoremove.pro
+++ b/tests/manual/quickcontrols/swipetoremove/swipetoremove.pro
diff --git a/tests/manual/quickcontrols/swipetoremove/swipetoremove.qml b/tests/manual/quickcontrols/swipetoremove/swipetoremove.qml
new file mode 100644
index 0000000000..dd2d9d3ecf
--- /dev/null
+++ b/tests/manual/quickcontrols/swipetoremove/swipetoremove.qml
@@ -0,0 +1,130 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ApplicationWindow {
+ id: window
+ width: 300
+ height: 400
+ visible: true
+ title: qsTr("Swipe to Remove")
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+
+ delegate: SwipeDelegate {
+ id: delegate
+
+ text: modelData
+ width: listView.width
+
+ //! [delegate]
+ swipe.right: Rectangle {
+ width: parent.width
+ height: parent.height
+
+ clip: true
+ color: SwipeDelegate.pressed ? "#555" : "#666"
+
+ Label {
+ font.family: "Fontello"
+ text: delegate.swipe.complete ? "\ue805" // icon-cw-circled
+ : "\ue801" // icon-cancel-circled-1
+
+ padding: 20
+ anchors.fill: parent
+ horizontalAlignment: Qt.AlignRight
+ verticalAlignment: Qt.AlignVCenter
+
+ opacity: 2 * -delegate.swipe.position
+
+ color: Material.color(delegate.swipe.complete ? Material.Green : Material.Red, Material.Shade200)
+ Behavior on color { ColorAnimation { } }
+ }
+
+ Label {
+ text: qsTr("Removed")
+ color: "white"
+
+ padding: 20
+ anchors.fill: parent
+ horizontalAlignment: Qt.AlignLeft
+ verticalAlignment: Qt.AlignVCenter
+
+ opacity: delegate.swipe.complete ? 1 : 0
+ Behavior on opacity { NumberAnimation { } }
+ }
+
+ SwipeDelegate.onClicked: delegate.swipe.close()
+ SwipeDelegate.onPressedChanged: undoTimer.stop()
+ }
+ //! [delegate]
+
+ //! [removal]
+ Timer {
+ id: undoTimer
+ interval: 3600
+ onTriggered: listModel.remove(index)
+ }
+
+ swipe.onCompleted: undoTimer.start()
+ //! [removal]
+ }
+
+ model: ListModel {
+ id: listModel
+ ListElement { text: "Lorem ipsum dolor sit amet" }
+ ListElement { text: "Curabitur sit amet risus" }
+ ListElement { text: "Suspendisse vehicula nisi" }
+ ListElement { text: "Mauris imperdiet libero" }
+ ListElement { text: "Sed vitae dui aliquet augue" }
+ ListElement { text: "Praesent in elit eu nulla" }
+ ListElement { text: "Etiam vitae magna" }
+ ListElement { text: "Pellentesque eget elit euismod" }
+ ListElement { text: "Nulla at enim porta" }
+ ListElement { text: "Fusce tincidunt odio" }
+ ListElement { text: "Ut non ex a ligula molestie" }
+ ListElement { text: "Nam vitae justo scelerisque" }
+ ListElement { text: "Vestibulum pulvinar tellus" }
+ ListElement { text: "Quisque dignissim leo sed gravida" }
+ }
+
+ //! [transitions]
+ remove: Transition {
+ SequentialAnimation {
+ PauseAnimation { duration: 125 }
+ NumberAnimation { property: "height"; to: 0; easing.type: Easing.InOutQuad }
+ }
+ }
+
+ displaced: Transition {
+ SequentialAnimation {
+ PauseAnimation { duration: 125 }
+ NumberAnimation { property: "y"; easing.type: Easing.InOutQuad }
+ }
+ }
+ //! [transitions]
+
+ ScrollIndicator.vertical: ScrollIndicator { }
+ }
+
+ Label {
+ id: placeholder
+ text: qsTr("Swipe no more")
+
+ anchors.margins: 60
+ anchors.fill: parent
+
+ opacity: 0.5
+ visible: listView.count === 0
+
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+ wrapMode: Label.WordWrap
+ font.pixelSize: 18
+ }
+}
diff --git a/tests/manual/quickcontrols/systemtrayicon/systemtrayicon.cpp b/tests/manual/quickcontrols/systemtrayicon/systemtrayicon.cpp
index 32b1b4c1ce..0108367b9f 100644
--- a/tests/manual/quickcontrols/systemtrayicon/systemtrayicon.cpp
+++ b/tests/manual/quickcontrols/systemtrayicon/systemtrayicon.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/quickcontrols/systemtrayicon/systemtrayicon.qml b/tests/manual/quickcontrols/systemtrayicon/systemtrayicon.qml
index d3a9454074..aeb0040ace 100644
--- a/tests/manual/quickcontrols/systemtrayicon/systemtrayicon.qml
+++ b/tests/manual/quickcontrols/systemtrayicon/systemtrayicon.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/+Imagine/ApplicationWindow.qml b/tests/manual/quickcontrols/testbench/+Imagine/ApplicationWindow.qml
index 8621f52da3..45c3e39a23 100644
--- a/tests/manual/quickcontrols/testbench/+Imagine/ApplicationWindow.qml
+++ b/tests/manual/quickcontrols/testbench/+Imagine/ApplicationWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls.Imagine
diff --git a/tests/manual/quickcontrols/testbench/+Imagine/ContentPane.qml b/tests/manual/quickcontrols/testbench/+Imagine/ContentPane.qml
index e0c66c7c1d..bb0a8a2648 100644
--- a/tests/manual/quickcontrols/testbench/+Imagine/ContentPane.qml
+++ b/tests/manual/quickcontrols/testbench/+Imagine/ContentPane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls.Imagine
diff --git a/tests/manual/quickcontrols/testbench/+Imagine/Dialog.qml b/tests/manual/quickcontrols/testbench/+Imagine/Dialog.qml
index 3cb16399cf..3f8a131c12 100644
--- a/tests/manual/quickcontrols/testbench/+Imagine/Dialog.qml
+++ b/tests/manual/quickcontrols/testbench/+Imagine/Dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls.Imagine
diff --git a/tests/manual/quickcontrols/testbench/+Imagine/Menu.qml b/tests/manual/quickcontrols/testbench/+Imagine/Menu.qml
index 548f4a9c47..6614977ce6 100644
--- a/tests/manual/quickcontrols/testbench/+Imagine/Menu.qml
+++ b/tests/manual/quickcontrols/testbench/+Imagine/Menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls.Imagine
diff --git a/tests/manual/quickcontrols/testbench/+Imagine/ToolBar.qml b/tests/manual/quickcontrols/testbench/+Imagine/ToolBar.qml
index 0b2653835f..77a39f063d 100644
--- a/tests/manual/quickcontrols/testbench/+Imagine/ToolBar.qml
+++ b/tests/manual/quickcontrols/testbench/+Imagine/ToolBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls.Imagine
diff --git a/tests/manual/quickcontrols/testbench/ApplicationWindow.qml b/tests/manual/quickcontrols/testbench/ApplicationWindow.qml
index dca67c4750..57baed7127 100644
--- a/tests/manual/quickcontrols/testbench/ApplicationWindow.qml
+++ b/tests/manual/quickcontrols/testbench/ApplicationWindow.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/ColorEditor.qml b/tests/manual/quickcontrols/testbench/ColorEditor.qml
index dd5d7f3aaa..4d2ab5ad86 100644
--- a/tests/manual/quickcontrols/testbench/ColorEditor.qml
+++ b/tests/manual/quickcontrols/testbench/ColorEditor.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -29,5 +29,6 @@ RowLayout {
}
Layout.preferredWidth: 200
+ Layout.fillWidth: false
}
}
diff --git a/tests/manual/quickcontrols/testbench/ContentPane.qml b/tests/manual/quickcontrols/testbench/ContentPane.qml
index 5bff3f5d23..a42edbdb81 100644
--- a/tests/manual/quickcontrols/testbench/ContentPane.qml
+++ b/tests/manual/quickcontrols/testbench/ContentPane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/ControlContainer.qml b/tests/manual/quickcontrols/testbench/ControlContainer.qml
index 34688fb7dd..57ba0c1a85 100644
--- a/tests/manual/quickcontrols/testbench/ControlContainer.qml
+++ b/tests/manual/quickcontrols/testbench/ControlContainer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/tests/manual/quickcontrols/testbench/Dialog.qml b/tests/manual/quickcontrols/testbench/Dialog.qml
index a1a4e5d624..f909386c86 100644
--- a/tests/manual/quickcontrols/testbench/Dialog.qml
+++ b/tests/manual/quickcontrols/testbench/Dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/ExampleContainer.qml b/tests/manual/quickcontrols/testbench/ExampleContainer.qml
index 19dbb9a41c..478012c884 100644
--- a/tests/manual/quickcontrols/testbench/ExampleContainer.qml
+++ b/tests/manual/quickcontrols/testbench/ExampleContainer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/Menu.qml b/tests/manual/quickcontrols/testbench/Menu.qml
index 15786af830..8f39739aaa 100644
--- a/tests/manual/quickcontrols/testbench/Menu.qml
+++ b/tests/manual/quickcontrols/testbench/Menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/SettingsDialog.qml b/tests/manual/quickcontrols/testbench/SettingsDialog.qml
index f879e5074f..545cf6fa83 100644
--- a/tests/manual/quickcontrols/testbench/SettingsDialog.qml
+++ b/tests/manual/quickcontrols/testbench/SettingsDialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -120,6 +120,7 @@ Ui.Dialog {
GroupBox {
title: qsTr("General")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -161,6 +162,7 @@ Ui.Dialog {
title: qsTr("Imagine Style")
visible: usingImagineStyle
Layout.fillWidth: true
+ Layout.fillHeight: false
Layout.columnSpan: 2
GridLayout {
@@ -173,6 +175,7 @@ Ui.Dialog {
enabled: usingImagineStyle
Layout.columnSpan: 2
+ Layout.fillWidth: false
}
Label {
@@ -217,6 +220,7 @@ Ui.Dialog {
enabled: usingImagineStyle && customImagineStyleCheckBox.checked
Layout.columnSpan: 2
+ Layout.fillWidth: false
ToolTip {
text: "If set, custom Imagine style assets will be modified to be compliant whenever they have changed on disk"
@@ -231,6 +235,7 @@ Ui.Dialog {
GroupBox {
title: qsTr("Palette")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -239,6 +244,7 @@ Ui.Dialog {
CheckBox {
id: useCustomPaletteCheckBox
text: qsTr("Use Custom Palette")
+ Layout.fillWidth: false
}
ColorEditor {
@@ -376,6 +382,7 @@ Ui.Dialog {
Layout.columnSpan: 2
Layout.alignment: Qt.AlignRight
+ Layout.fillWidth: false
}
}
}
diff --git a/tests/manual/quickcontrols/testbench/ToolBar.qml b/tests/manual/quickcontrols/testbench/ToolBar.qml
index ffbf422a76..1d7c503f56 100644
--- a/tests/manual/quickcontrols/testbench/ToolBar.qml
+++ b/tests/manual/quickcontrols/testbench/ToolBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/assetfixer.cpp b/tests/manual/quickcontrols/testbench/assetfixer.cpp
index 57936b737b..c2582a561a 100644
--- a/tests/manual/quickcontrols/testbench/assetfixer.cpp
+++ b/tests/manual/quickcontrols/testbench/assetfixer.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "assetfixer.h"
diff --git a/tests/manual/quickcontrols/testbench/assetfixer.h b/tests/manual/quickcontrols/testbench/assetfixer.h
index 88594a87d0..1651ffad26 100644
--- a/tests/manual/quickcontrols/testbench/assetfixer.h
+++ b/tests/manual/quickcontrols/testbench/assetfixer.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef ASSETFIXER_H
#define ASSETFIXER_H
diff --git a/tests/manual/quickcontrols/testbench/clipboard.cpp b/tests/manual/quickcontrols/testbench/clipboard.cpp
index e8017e0e23..93ee93cacf 100644
--- a/tests/manual/quickcontrols/testbench/clipboard.cpp
+++ b/tests/manual/quickcontrols/testbench/clipboard.cpp
@@ -1,11 +1,12 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "clipboard.h"
#include <QClipboard>
#include <QGuiApplication>
#include <QSettings>
+#include <QMap>
const QStringList keys = {
"Palette/window",
diff --git a/tests/manual/quickcontrols/testbench/clipboard.h b/tests/manual/quickcontrols/testbench/clipboard.h
index f262638391..c56103b84c 100644
--- a/tests/manual/quickcontrols/testbench/clipboard.h
+++ b/tests/manual/quickcontrols/testbench/clipboard.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CLIPBOARD_H
#define CLIPBOARD_H
diff --git a/tests/manual/quickcontrols/testbench/controls/BusyIndicator.qml b/tests/manual/quickcontrols/testbench/controls/BusyIndicator.qml
index c61c100668..1c14c13a00 100644
--- a/tests/manual/quickcontrols/testbench/controls/BusyIndicator.qml
+++ b/tests/manual/quickcontrols/testbench/controls/BusyIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Button.qml b/tests/manual/quickcontrols/testbench/controls/Button.qml
index 1dcf05da36..12f398ccab 100644
--- a/tests/manual/quickcontrols/testbench/controls/Button.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/CheckBox.qml b/tests/manual/quickcontrols/testbench/controls/CheckBox.qml
index 4973033a4d..b595e592b2 100644
--- a/tests/manual/quickcontrols/testbench/controls/CheckBox.qml
+++ b/tests/manual/quickcontrols/testbench/controls/CheckBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml b/tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml
index d64339501f..085b5acc82 100644
--- a/tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/CheckDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -15,6 +15,9 @@ QtObject {
["partially-checked"],
["partially-checked", "disabled"],
["partially-checked", "pressed"],
+ ["highlighted"],
+ ["highlighted", "pressed"],
+ ["mirrored"]
]
property Component component: CheckDelegate {
@@ -23,7 +26,10 @@ QtObject {
checkState: is("checked") ? Qt.Checked : is("partially-checked") ? Qt.PartiallyChecked : Qt.Unchecked
// Only set it if it's pressed, or the non-pressed examples will have no press effects
down: is("pressed") ? true : undefined
+ highlighted: is("highlighted")
focusPolicy: Qt.StrongFocus
+
+ LayoutMirroring.enabled: is("mirrored")
}
property Component exampleComponent: ListView {
diff --git a/tests/manual/quickcontrols/testbench/controls/ComboBox.qml b/tests/manual/quickcontrols/testbench/controls/ComboBox.qml
index 39b8579630..a1c57db693 100644
--- a/tests/manual/quickcontrols/testbench/controls/ComboBox.qml
+++ b/tests/manual/quickcontrols/testbench/controls/ComboBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/DelayButton.qml b/tests/manual/quickcontrols/testbench/controls/DelayButton.qml
index 1c36a4f7ca..8509932a0d 100644
--- a/tests/manual/quickcontrols/testbench/controls/DelayButton.qml
+++ b/tests/manual/quickcontrols/testbench/controls/DelayButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Dial.qml b/tests/manual/quickcontrols/testbench/controls/Dial.qml
index c4847ac886..ec7f5411e3 100644
--- a/tests/manual/quickcontrols/testbench/controls/Dial.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Dial.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Dialog.qml b/tests/manual/quickcontrols/testbench/controls/Dialog.qml
index 1a25fbdfb9..259e1742b4 100644
--- a/tests/manual/quickcontrols/testbench/controls/Dialog.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Dialog.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Frame.qml b/tests/manual/quickcontrols/testbench/controls/Frame.qml
index a57cb76f18..76f90d7d2f 100644
--- a/tests/manual/quickcontrols/testbench/controls/Frame.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Frame.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/GroupBox.qml b/tests/manual/quickcontrols/testbench/controls/GroupBox.qml
index 23c938ff21..44c122a230 100644
--- a/tests/manual/quickcontrols/testbench/controls/GroupBox.qml
+++ b/tests/manual/quickcontrols/testbench/controls/GroupBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml b/tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml
index e8171c36bc..709a8d5b5f 100644
--- a/tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/ItemDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Label.qml b/tests/manual/quickcontrols/testbench/controls/Label.qml
index a5074e8faf..6632643803 100644
--- a/tests/manual/quickcontrols/testbench/controls/Label.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Label.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Menu.qml b/tests/manual/quickcontrols/testbench/controls/Menu.qml
index aeadee4f86..df5bc55d20 100644
--- a/tests/manual/quickcontrols/testbench/controls/Menu.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Menu.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/MenuBar.qml b/tests/manual/quickcontrols/testbench/controls/MenuBar.qml
index bed6fbd909..3ea2e2be83 100644
--- a/tests/manual/quickcontrols/testbench/controls/MenuBar.qml
+++ b/tests/manual/quickcontrols/testbench/controls/MenuBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Page.qml b/tests/manual/quickcontrols/testbench/controls/Page.qml
index d8c29a7fe4..ef3a0dca7d 100644
--- a/tests/manual/quickcontrols/testbench/controls/Page.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Page.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/PageIndicator.qml b/tests/manual/quickcontrols/testbench/controls/PageIndicator.qml
index 34a242a6af..4b7c0d5a0d 100644
--- a/tests/manual/quickcontrols/testbench/controls/PageIndicator.qml
+++ b/tests/manual/quickcontrols/testbench/controls/PageIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Pane.qml b/tests/manual/quickcontrols/testbench/controls/Pane.qml
index 9f60bec588..7af1ae731a 100644
--- a/tests/manual/quickcontrols/testbench/controls/Pane.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Pane.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/ProgressBar.qml b/tests/manual/quickcontrols/testbench/controls/ProgressBar.qml
index bd4ab8e379..3632effa3e 100644
--- a/tests/manual/quickcontrols/testbench/controls/ProgressBar.qml
+++ b/tests/manual/quickcontrols/testbench/controls/ProgressBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/RadioButton.qml b/tests/manual/quickcontrols/testbench/controls/RadioButton.qml
index 00beb1bac9..feb8d8f195 100644
--- a/tests/manual/quickcontrols/testbench/controls/RadioButton.qml
+++ b/tests/manual/quickcontrols/testbench/controls/RadioButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml b/tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml
index 4dab387fcf..e182cae8ad 100644
--- a/tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/RadioDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -11,7 +11,10 @@ QtObject {
["pressed"],
["checked"],
["checked", "disabled"],
- ["checked", "pressed"]
+ ["checked", "pressed"],
+ ["highlighted"],
+ ["highlighted", "pressed"],
+ ["mirrored"]
]
property Component component: Component {
@@ -21,7 +24,10 @@ QtObject {
checked: is("checked")
// Only set it if it's pressed, or the non-pressed examples will have no press effects
down: is("pressed") ? true : undefined
+ highlighted: is("highlighted")
focusPolicy: Qt.StrongFocus
+
+ LayoutMirroring.enabled: is("mirrored")
}
}
diff --git a/tests/manual/quickcontrols/testbench/controls/RangeSlider.qml b/tests/manual/quickcontrols/testbench/controls/RangeSlider.qml
index 18a2418451..75bfbd97a4 100644
--- a/tests/manual/quickcontrols/testbench/controls/RangeSlider.qml
+++ b/tests/manual/quickcontrols/testbench/controls/RangeSlider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/RoundButton.qml b/tests/manual/quickcontrols/testbench/controls/RoundButton.qml
index 530aaee338..9e25c8210a 100644
--- a/tests/manual/quickcontrols/testbench/controls/RoundButton.qml
+++ b/tests/manual/quickcontrols/testbench/controls/RoundButton.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/ScrollBar.qml b/tests/manual/quickcontrols/testbench/controls/ScrollBar.qml
index 5fa00fec0f..d2124a53cb 100644
--- a/tests/manual/quickcontrols/testbench/controls/ScrollBar.qml
+++ b/tests/manual/quickcontrols/testbench/controls/ScrollBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/ScrollIndicator.qml b/tests/manual/quickcontrols/testbench/controls/ScrollIndicator.qml
index 5c0c0d4edc..f43027ae58 100644
--- a/tests/manual/quickcontrols/testbench/controls/ScrollIndicator.qml
+++ b/tests/manual/quickcontrols/testbench/controls/ScrollIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Slider.qml b/tests/manual/quickcontrols/testbench/controls/Slider.qml
index d35e8fe0fa..098621fc73 100644
--- a/tests/manual/quickcontrols/testbench/controls/Slider.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Slider.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/SpinBox.qml b/tests/manual/quickcontrols/testbench/controls/SpinBox.qml
index 06c6ca43ef..b7df6a080e 100644
--- a/tests/manual/quickcontrols/testbench/controls/SpinBox.qml
+++ b/tests/manual/quickcontrols/testbench/controls/SpinBox.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/SplitView.qml b/tests/manual/quickcontrols/testbench/controls/SplitView.qml
index c51ac846f2..7d7a975561 100644
--- a/tests/manual/quickcontrols/testbench/controls/SplitView.qml
+++ b/tests/manual/quickcontrols/testbench/controls/SplitView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml b/tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml
index e04f0bb630..36d683a8ac 100644
--- a/tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/SwipeDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Switch.qml b/tests/manual/quickcontrols/testbench/controls/Switch.qml
index c7e5103183..db44ed1900 100644
--- a/tests/manual/quickcontrols/testbench/controls/Switch.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Switch.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml b/tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml
index 9508b2ce80..6fe3ecefce 100644
--- a/tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml
+++ b/tests/manual/quickcontrols/testbench/controls/SwitchDelegate.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/TabBar.qml b/tests/manual/quickcontrols/testbench/controls/TabBar.qml
index e0286146f9..96ee0896f4 100644
--- a/tests/manual/quickcontrols/testbench/controls/TabBar.qml
+++ b/tests/manual/quickcontrols/testbench/controls/TabBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/TextArea.qml b/tests/manual/quickcontrols/testbench/controls/TextArea.qml
index 3b76ef2b0a..b9c08846e4 100644
--- a/tests/manual/quickcontrols/testbench/controls/TextArea.qml
+++ b/tests/manual/quickcontrols/testbench/controls/TextArea.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/TextField.qml b/tests/manual/quickcontrols/testbench/controls/TextField.qml
index 205418f08c..74719dacad 100644
--- a/tests/manual/quickcontrols/testbench/controls/TextField.qml
+++ b/tests/manual/quickcontrols/testbench/controls/TextField.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/ToolBar.qml b/tests/manual/quickcontrols/testbench/controls/ToolBar.qml
index 1fccb74e65..2596f4309e 100644
--- a/tests/manual/quickcontrols/testbench/controls/ToolBar.qml
+++ b/tests/manual/quickcontrols/testbench/controls/ToolBar.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/ToolTip.qml b/tests/manual/quickcontrols/testbench/controls/ToolTip.qml
index de8f117429..f6ec986047 100644
--- a/tests/manual/quickcontrols/testbench/controls/ToolTip.qml
+++ b/tests/manual/quickcontrols/testbench/controls/ToolTip.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/controls/Tumbler.qml b/tests/manual/quickcontrols/testbench/controls/Tumbler.qml
index 1c571d22b1..067d3664b6 100644
--- a/tests/manual/quickcontrols/testbench/controls/Tumbler.qml
+++ b/tests/manual/quickcontrols/testbench/controls/Tumbler.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickcontrols/testbench/directoryvalidator.cpp b/tests/manual/quickcontrols/testbench/directoryvalidator.cpp
index c1c083a246..f174832979 100644
--- a/tests/manual/quickcontrols/testbench/directoryvalidator.cpp
+++ b/tests/manual/quickcontrols/testbench/directoryvalidator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "directoryvalidator.h"
diff --git a/tests/manual/quickcontrols/testbench/directoryvalidator.h b/tests/manual/quickcontrols/testbench/directoryvalidator.h
index 89bb03bd29..b411662281 100644
--- a/tests/manual/quickcontrols/testbench/directoryvalidator.h
+++ b/tests/manual/quickcontrols/testbench/directoryvalidator.h
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DIRECTORYVALIDATOR_H
#define DIRECTORYVALIDATOR_H
diff --git a/tests/manual/quickcontrols/testbench/fonts.qrc b/tests/manual/quickcontrols/testbench/fonts.qrc
index fc86e03183..4203c83326 100644
--- a/tests/manual/quickcontrols/testbench/fonts.qrc
+++ b/tests/manual/quickcontrols/testbench/fonts.qrc
@@ -1,5 +1,5 @@
<RCC>
<qresource prefix="/">
- <file>fonts/fontawesome.ttf</file>
+ <file>fonts/fontello.ttf</file>
</qresource>
</RCC>
diff --git a/tests/manual/quickcontrols/testbench/main.cpp b/tests/manual/quickcontrols/testbench/main.cpp
index 4a629523e6..14da6e7b2c 100644
--- a/tests/manual/quickcontrols/testbench/main.cpp
+++ b/tests/manual/quickcontrols/testbench/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QDebug>
#include <QFontDatabase>
diff --git a/tests/manual/quickcontrols/testbench/testbench.qml b/tests/manual/quickcontrols/testbench/testbench.qml
index 9df1ed873f..9c7f4a70aa 100644
--- a/tests/manual/quickcontrols/testbench/testbench.qml
+++ b/tests/manual/quickcontrols/testbench/testbench.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtCore
import QtQuick
diff --git a/tests/manual/quickcontrols/viewinqwidget/main.cpp b/tests/manual/quickcontrols/viewinqwidget/main.cpp
index 7ce08aaa78..21e78868c4 100644
--- a/tests/manual/quickcontrols/viewinqwidget/main.cpp
+++ b/tests/manual/quickcontrols/viewinqwidget/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QHBoxLayout>
diff --git a/tests/manual/quickcontrols/viewinqwidget/main.qml b/tests/manual/quickcontrols/viewinqwidget/main.qml
index 8e347bdb81..10d70f18d1 100644
--- a/tests/manual/quickcontrols/viewinqwidget/main.qml
+++ b/tests/manual/quickcontrols/viewinqwidget/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickdialogs/dialogs/ColorDialogPage.qml b/tests/manual/quickdialogs/dialogs/ColorDialogPage.qml
index 678f978146..ff17bd73a0 100644
--- a/tests/manual/quickdialogs/dialogs/ColorDialogPage.qml
+++ b/tests/manual/quickdialogs/dialogs/ColorDialogPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -20,6 +20,7 @@ ColumnLayout {
title: qsTr("Dialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -41,17 +42,20 @@ ColumnLayout {
RadioButton {
text: qsTr("Qt.NonModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.NonModal
}
RadioButton {
text: qsTr("Qt.WindowModal")
checked: true
+ Layout.fillWidth: false
readonly property int modality: Qt.WindowModal
}
RadioButton {
text: qsTr("Qt.ApplicationModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.ApplicationModal
}
@@ -65,6 +69,7 @@ ColumnLayout {
text: colorDialog.result === 1 ? qsTr("Accepted") : qsTr("Rejected")
readOnly: true
enabled: false
+ Layout.fillWidth: false
}
Label {
@@ -73,6 +78,7 @@ ColumnLayout {
TextField {
id: titleTextField
text: qsTr("A Color Dialog")
+ Layout.fillWidth: false
}
}
}
@@ -81,6 +87,7 @@ ColumnLayout {
title: qsTr("ColorDialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -122,15 +129,25 @@ ColumnLayout {
CheckBox {
id: showAlphaChannel
text: qsTr("ShowAlphaChannel")
+ Layout.fillWidth: false
readonly property int colorOption: checked ? ColorDialog.ShowAlphaChannel : 0
}
CheckBox {
id: noButtons
text: qsTr("NoButtons")
+ Layout.fillWidth: false
readonly property int colorOption: checked ? ColorDialog.NoButtons : 0
}
+
+ CheckBox {
+ id: noEyeDropperButton
+ text: qsTr("NoEyeDropperButton")
+ Layout.fillWidth: false
+
+ readonly property int colorOption: checked ? ColorDialog.NoEyeDropperButton : 0
+ }
}
}
}
@@ -139,7 +156,7 @@ ColumnLayout {
id: colorDialog
modality: modalityButtonGroup.checkedButton.modality
title: titleTextField.text
- options: showAlphaChannel.colorOption | noButtons.colorOption
+ options: showAlphaChannel.colorOption | noButtons.colorOption | noEyeDropperButton.colorOption
}
}
}
diff --git a/tests/manual/quickdialogs/dialogs/FileDialogPage.qml b/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
index a7fb46b232..c4f8ddc01c 100644
--- a/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
+++ b/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -22,6 +22,7 @@ ColumnLayout {
title: qsTr("Dialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -43,17 +44,20 @@ ColumnLayout {
RadioButton {
text: qsTr("Qt.NonModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.NonModal
}
RadioButton {
text: qsTr("Qt.WindowModal")
checked: true
+ Layout.fillWidth: false
readonly property int modality: Qt.WindowModal
}
RadioButton {
text: qsTr("Qt.ApplicationModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.ApplicationModal
}
@@ -67,6 +71,7 @@ ColumnLayout {
text: fileDialog.result === 1 ? qsTr("Accepted") : qsTr("Rejected")
readOnly: true
enabled: false
+ Layout.fillWidth: false
}
Label {
@@ -75,6 +80,7 @@ ColumnLayout {
TextField {
id: titleTextField
text: qsTr("Choose a file")
+ Layout.fillWidth: false
}
}
}
@@ -83,6 +89,7 @@ ColumnLayout {
title: qsTr("FileDialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -97,6 +104,7 @@ ColumnLayout {
TextField {
id: acceptLabelTextField
text: qsTr("OK")
+ Layout.fillWidth: false
}
Label {
@@ -145,24 +153,28 @@ ColumnLayout {
CheckBox {
id: dontResolveSymlinksCheckBox
text: qsTr("DontResolveSymlinks")
+ Layout.fillWidth: false
readonly property int fileOption: checked ? FileDialog.DontResolveSymlinks : 0
}
CheckBox {
id: dontConfirmOverwriteCheckBox
text: qsTr("DontConfirmOverwrite")
+ Layout.fillWidth: false
readonly property int fileOption: checked ? FileDialog.DontConfirmOverwrite : 0
}
CheckBox {
id: readOnlyCheckBox
text: qsTr("ReadOnly")
+ Layout.fillWidth: false
readonly property int fileOption: checked ? FileDialog.ReadOnly : 0
}
CheckBox {
id: hideNameFilterDetailsCheckBox
text: qsTr("HideNameFilterDetails")
+ Layout.fillWidth: false
readonly property int fileOption: checked ? FileDialog.HideNameFilterDetails : 0
}
@@ -182,17 +194,20 @@ ColumnLayout {
RadioButton {
text: qsTr("OpenFile")
+ Layout.fillWidth: false
readonly property int fileMode: FileDialog.OpenFile
}
RadioButton {
text: qsTr("OpenFiles")
checked: true
+ Layout.fillWidth: false
readonly property int fileMode: FileDialog.OpenFiles
}
RadioButton {
text: qsTr("SaveFile")
+ Layout.fillWidth: false
readonly property int fileMode: FileDialog.SaveFile
}
@@ -218,6 +233,7 @@ ColumnLayout {
TextField {
id: rejectLabelTextField
text: qsTr("Cancel")
+ Layout.fillWidth: false
}
Label {
diff --git a/tests/manual/quickdialogs/dialogs/FolderDialogPage.qml b/tests/manual/quickdialogs/dialogs/FolderDialogPage.qml
index 765b886cff..67db8b8dbc 100644
--- a/tests/manual/quickdialogs/dialogs/FolderDialogPage.qml
+++ b/tests/manual/quickdialogs/dialogs/FolderDialogPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -22,6 +22,7 @@ ColumnLayout {
title: qsTr("Dialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -43,17 +44,20 @@ ColumnLayout {
RadioButton {
text: qsTr("Qt.NonModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.NonModal
}
RadioButton {
text: qsTr("Qt.WindowModal")
checked: true
+ Layout.fillWidth: false
readonly property int modality: Qt.WindowModal
}
RadioButton {
text: qsTr("Qt.ApplicationModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.ApplicationModal
}
@@ -67,6 +71,7 @@ ColumnLayout {
text: folderDialog.result === 1 ? qsTr("Accepted") : qsTr("Rejected")
readOnly: true
enabled: false
+ Layout.fillWidth: false
}
Label {
@@ -75,6 +80,7 @@ ColumnLayout {
TextField {
id: titleTextField
text: qsTr("Choose a folder")
+ Layout.fillWidth: false
}
}
}
@@ -83,6 +89,7 @@ ColumnLayout {
title: qsTr("FolderDialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -97,6 +104,7 @@ ColumnLayout {
TextField {
id: acceptLabelTextField
text: qsTr("OK")
+ Layout.fillWidth: false
}
Label {
@@ -105,6 +113,7 @@ ColumnLayout {
TextField {
id: rejectLabelTextField
text: qsTr("Cancel")
+ Layout.fillWidth: false
}
Label {
@@ -130,12 +139,14 @@ ColumnLayout {
CheckBox {
id: dontResolveSymlinksCheckBox
text: qsTr("DontResolveSymlinks")
+ Layout.fillWidth: false
readonly property int folderOption: checked ? FolderDialog.DontResolveSymlinks : 0
}
CheckBox {
id: readOnlyCheckBox
text: qsTr("ReadOnly")
+ Layout.fillWidth: false
readonly property int folderOption: checked ? FolderDialog.ReadOnly : 0
}
diff --git a/tests/manual/quickdialogs/dialogs/FontDialogPage.qml b/tests/manual/quickdialogs/dialogs/FontDialogPage.qml
index 285d5f764d..f80ed09d72 100644
--- a/tests/manual/quickdialogs/dialogs/FontDialogPage.qml
+++ b/tests/manual/quickdialogs/dialogs/FontDialogPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -22,6 +22,7 @@ ColumnLayout {
title: qsTr("Dialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -43,17 +44,20 @@ ColumnLayout {
RadioButton {
text: qsTr("Qt.NonModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.NonModal
}
RadioButton {
text: qsTr("Qt.WindowModal")
checked: true
+ Layout.fillWidth: false
readonly property int modality: Qt.WindowModal
}
RadioButton {
text: qsTr("Qt.ApplicationModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.ApplicationModal
}
@@ -67,6 +71,7 @@ ColumnLayout {
text: fontDialog.result === 1 ? qsTr("Accepted") : qsTr("Rejected")
readOnly: true
enabled: false
+ Layout.fillWidth: false
}
Label {
@@ -75,6 +80,7 @@ ColumnLayout {
TextField {
id: titleTextField
text: qsTr("Pick a font")
+ Layout.fillWidth: false
}
}
}
@@ -83,6 +89,7 @@ ColumnLayout {
title: qsTr("FontDialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -127,30 +134,35 @@ ColumnLayout {
CheckBox {
id: noButtons
text: qsTr("NoButtons")
+ Layout.fillWidth: false
readonly property int fontOption: checked ? FontDialog.NoButtons : 0
}
CheckBox {
id: scalableFonts
text: qsTr("ScalableFonts")
+ Layout.fillWidth: false
readonly property int fontOption: checked ? FontDialog.ScalableFonts : 0
}
CheckBox {
id: nonScalableFonts
text: qsTr("NonScalableFonts")
+ Layout.fillWidth: false
readonly property int fontOption: checked ? FontDialog.NonScalableFonts : 0
}
CheckBox {
id: monospacedFonts
text: qsTr("MonospacedFonts")
+ Layout.fillWidth: false
readonly property int fontOption: checked ? FontDialog.MonospacedFonts : 0
}
CheckBox {
id: proportionalFonts
text: qsTr("ProportionalFonts")
+ Layout.fillWidth: false
readonly property int fontOption: checked ? FontDialog.ProportionalFonts : 0
}
diff --git a/tests/manual/quickdialogs/dialogs/MessageDialogPage.qml b/tests/manual/quickdialogs/dialogs/MessageDialogPage.qml
index 1fa2deef18..8ed211183b 100644
--- a/tests/manual/quickdialogs/dialogs/MessageDialogPage.qml
+++ b/tests/manual/quickdialogs/dialogs/MessageDialogPage.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -20,6 +20,7 @@ ColumnLayout {
title: qsTr("Dialog properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
GridLayout {
columns: 2
@@ -41,17 +42,20 @@ ColumnLayout {
RadioButton {
text: qsTr("Qt.NonModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.NonModal
}
RadioButton {
text: qsTr("Qt.WindowModal")
checked: true
+ Layout.fillWidth: false
readonly property int modality: Qt.WindowModal
}
RadioButton {
text: qsTr("Qt.ApplicationModal")
+ Layout.fillWidth: false
readonly property int modality: Qt.ApplicationModal
}
@@ -65,6 +69,7 @@ ColumnLayout {
text: messageBox.result === 1 ? qsTr("Accepted") : qsTr("Rejected")
readOnly: true
enabled: false
+ Layout.fillWidth: false
}
Label {
@@ -73,6 +78,7 @@ ColumnLayout {
TextField {
id: titleTextField
text: qsTr("Hello World")
+ Layout.fillWidth: false
}
}
}
@@ -81,6 +87,7 @@ ColumnLayout {
title: qsTr("MessageBox properties")
Layout.fillWidth: true
+ Layout.fillHeight: false
ColumnLayout {
RowLayout {
@@ -90,6 +97,7 @@ ColumnLayout {
TextField {
id: textProperty
text: qsTr("default text")
+ Layout.fillWidth: false
}
}
RowLayout {
@@ -99,6 +107,7 @@ ColumnLayout {
TextField {
id: informativeTextProperty
text: qsTr("default informative text")
+ Layout.fillWidth: false
}
}
@@ -116,6 +125,8 @@ ColumnLayout {
border.width: 1
}
Layout.maximumWidth: ApplicationWindow.window.width * 0.5
+ Layout.fillWidth: false
+ Layout.fillHeight: false
wrapMode: TextEdit.WordWrap
text: qsTr("This text will be displayed in the 'detailed text' textArea, which the user must press a button to see.\n"
+ "If this is an empty string, the button will not be visible.")
@@ -125,102 +136,119 @@ ColumnLayout {
CheckBox {
id: okCheckbox
text: qsTr("Ok")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Ok : MessageDialog.NoButton
}
CheckBox {
id: saveCheckbox
text: qsTr("Save")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Save : MessageDialog.NoButton
}
CheckBox {
id: saveAllCheckbox
text: qsTr("Save All")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.SaveAll : MessageDialog.NoButton
}
CheckBox {
id: openCheckbox
text: qsTr("Open")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Open : MessageDialog.NoButton
}
CheckBox {
id: yesCheckbox
text: qsTr("Yes")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Yes : MessageDialog.NoButton
}
CheckBox {
id: yesToAllCheckbox
text: qsTr("Yes to all")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.YesToAll : MessageDialog.NoButton
}
CheckBox {
id: noCheckbox
text: qsTr("No")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.No : MessageDialog.NoButton
}
CheckBox {
id: noToAllCheckbox
text: qsTr("No to all")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.NoToAll : MessageDialog.NoButton
}
CheckBox {
id: abortCheckbox
text: qsTr("Abort")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Abort : MessageDialog.NoButton
}
CheckBox {
id: retryCheckbox
text: qsTr("Retry")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Retry : MessageDialog.NoButton
}
CheckBox {
id: ignoreCheckbox
text: qsTr("Ignore")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Ignore : MessageDialog.NoButton
}
CheckBox {
id: closeCheckbox
text: qsTr("Close")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Close : MessageDialog.NoButton
}
CheckBox {
id: cancelCheckbox
text: qsTr("Cancel")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Cancel : MessageDialog.NoButton
}
CheckBox {
id: discardCheckbox
text: qsTr("Discard")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Discard : MessageDialog.NoButton
}
CheckBox {
id: helpCheckbox
text: qsTr("Help")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Help : MessageDialog.NoButton
}
CheckBox {
id: applyCheckbox
text: qsTr("Apply")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Apply : MessageDialog.NoButton
}
CheckBox {
id: resetCheckbox
text: qsTr("Reset")
+ Layout.fillWidth: false
readonly property int value: checked ? MessageDialog.Reset : MessageDialog.NoButton
}
diff --git a/tests/manual/quickdialogs/dialogs/StringListView.qml b/tests/manual/quickdialogs/dialogs/StringListView.qml
index a93b925142..72dfbe5d4f 100644
--- a/tests/manual/quickdialogs/dialogs/StringListView.qml
+++ b/tests/manual/quickdialogs/dialogs/StringListView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
diff --git a/tests/manual/quickdialogs/dialogs/Theme.qml b/tests/manual/quickdialogs/dialogs/Theme.qml
index 4b7f49fe27..29c2262a47 100644
--- a/tests/manual/quickdialogs/dialogs/Theme.qml
+++ b/tests/manual/quickdialogs/dialogs/Theme.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
pragma Singleton
diff --git a/tests/manual/quickdialogs/dialogs/dialogs.cpp b/tests/manual/quickdialogs/dialogs/dialogs.cpp
index 25763c4279..2e17c3994c 100644
--- a/tests/manual/quickdialogs/dialogs/dialogs.cpp
+++ b/tests/manual/quickdialogs/dialogs/dialogs.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/qsettings.h>
#include <QtGui/qguiapplication.h>
diff --git a/tests/manual/quickdialogs/dialogs/dialogs.qml b/tests/manual/quickdialogs/dialogs/dialogs.qml
index cd0c2e7e7f..0356755b6a 100644
--- a/tests/manual/quickdialogs/dialogs/dialogs.qml
+++ b/tests/manual/quickdialogs/dialogs/dialogs.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtCore
import QtQuick
@@ -80,6 +80,7 @@ ApplicationWindow {
id: useNativeDialogsCheckBox
text: qsTr("Use Native Dialogs (requires restart)")
checked: settings.useNativeDialogs
+ Layout.fillWidth: false
}
Item {
@@ -88,6 +89,7 @@ ApplicationWindow {
Button {
text: qsTr("Open")
+ Layout.fillWidth: false
onClicked: stackLayout.children[stackLayout.currentIndex].dialog.open()
}
diff --git a/tests/manual/quicklayouts/gridlayout/uniformColumnTest.qml b/tests/manual/quicklayouts/gridlayout/uniformColumnTest.qml
new file mode 100644
index 0000000000..61e07ef0a7
--- /dev/null
+++ b/tests/manual/quicklayouts/gridlayout/uniformColumnTest.qml
@@ -0,0 +1,111 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Layouts
+import QtQuick.Controls
+
+Window {
+ id: window
+ visible: true
+
+ width: grid.implicitWidth
+ height: grid.implicitHeight
+
+ title: "Window (" + grid.width + "x" + grid.height + ")"
+
+ GridLayout {
+ id: grid
+ columns: 3
+ rows: 3
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ uniformCellWidths: true
+
+ Repeater {
+ model: 3
+ Rectangle {
+ color: "#243a5e"
+ implicitWidth: 300
+ implicitHeight: 300
+ opacity: implicitWidth/600/2 + implicitHeight/600/2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ Layout.maximumWidth: 1000
+ Layout.maximumHeight: 1000
+
+ Layout.minimumWidth: 20
+ Layout.minimumHeight: 20
+
+ Layout.alignment: Qt.AlignCenter
+ Text {
+ id: sizeText
+ anchors.centerIn: parent
+ text: "min :" + parent.Layout.minimumWidth + "x" + parent.Layout.minimumHeight
+ }
+ Text {
+ id: sizeText2
+ anchors.top: sizeText.bottom
+ anchors.horizontalCenter: sizeText.horizontalCenter
+ text: "want:" + parent.implicitWidth + "x" + parent.implicitHeight
+ }
+ Text {
+ id: sizeText3
+ anchors.top: sizeText2.bottom
+ anchors.horizontalCenter: sizeText2.horizontalCenter
+ text: "size :" + parent.width + "x" + parent.height
+ }
+ Text {
+ anchors.top: sizeText3.bottom
+ anchors.horizontalCenter: sizeText3.horizontalCenter
+ text: "max :" + parent.Layout.maximumWidth + "x" + parent.Layout.maximumHeight
+ }
+ Text {
+ anchors.bottom: sizeText.top
+ anchors.horizontalCenter: sizeText.horizontalCenter
+ text: index
+ font.pointSize: 14
+ }
+ WheelHandler {
+ acceptedModifiers: Qt.NoModifier
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onWheel: (event)=> {
+ if (event.angleDelta.y > 0)
+ implicitWidth += 5
+ else if (implicitWidth > 50)
+ implicitWidth -= 5
+ }
+ }
+ WheelHandler {
+ acceptedModifiers: Qt.ShiftModifier
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onWheel: (event)=> {
+ if (event.angleDelta.y > 0)
+ parent.Layout.minimumWidth = Math.min(parent.Layout.minimumWidth + 5, parent.Layout.maximumWidth)
+ else
+ parent.Layout.minimumWidth = Math.max(parent.Layout.minimumWidth - 5, 0)
+ }
+ }
+ WheelHandler {
+ acceptedModifiers: Qt.ControlModifier
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onWheel: (event)=> {
+ if (event.angleDelta.y > 0)
+ parent.Layout.maximumWidth = Math.min(parent.Layout.maximumWidth + 5, 2500)
+ else
+ parent.Layout.maximumWidth = Math.max(parent.Layout.maximumWidth - 5, parent.Layout.minimumWidth)
+ }
+ }
+ }
+ }
+ }
+
+ onWidthChanged: {
+ console.log("Preferred Size:", grid.implicitWidth, "x", grid.implicitHeight)
+ console.log(" Minimum Size:", grid.Layout.minimumWidth, "x", grid.Layout.minimumHeight)
+ console.log(" Maximum Size:", grid.Layout.maximumWidth, "x", grid.Layout.maximumHeight)
+ }
+}
diff --git a/tests/manual/quicklayouts/layoutItemProxy/LayoutChooser.qml b/tests/manual/quicklayouts/layoutItemProxy/LayoutChooser.qml
new file mode 100644
index 0000000000..7667aa950d
--- /dev/null
+++ b/tests/manual/quicklayouts/layoutItemProxy/LayoutChooser.qml
@@ -0,0 +1,52 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+import QtQuick.Controls
+
+Item {
+ id: __layoutChooser
+ property var chosenLayout: undefined
+ property list<bool> criteria: []
+ property list<var> layoutChoices: []
+
+ implicitWidth: chosenLayout.implicitWidth
+ implicitHeight: chosenLayout.implicitHeight
+
+ onCriteriaChanged: {
+ console.log("Criterias:", criteria)
+ showAndHide()
+ }
+
+ onLayoutChoicesChanged: {
+ console.log("Layouts:", layoutChoices)
+ showAndHide()
+ }
+
+ function showAndHide() {
+ const oldLayout = chosenLayout
+
+ let i = 0
+ for (; i < criteria.length; i++) {
+ if (criteria[i])
+ break
+ }
+
+ console.log("Choosing layout", i)
+
+ if (i < layoutChoices.length)
+ chosenLayout = layoutChoices[i]
+ else if (layoutChoices.length > 0)
+ chosenLayout = layoutChoices[0]
+ else
+ return
+
+ for (i = 0; i < layoutChoices.length; i++) {
+ layoutChoices[i].visible = false
+ }
+ chosenLayout.visible = true
+ chosenLayout.ensurePolished()
+ }
+}
diff --git a/tests/manual/quicklayouts/layoutItemProxy/ms-rearchitect.qml b/tests/manual/quicklayouts/layoutItemProxy/ms-rearchitect.qml
new file mode 100644
index 0000000000..6a884075e7
--- /dev/null
+++ b/tests/manual/quicklayouts/layoutItemProxy/ms-rearchitect.qml
@@ -0,0 +1,235 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// This example was created for the blog post about responsive layouts:
+// https://www.qt.io/blog/responsive-layouts-in-qt
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+import QtQuick.Controls
+
+Window {
+ id: window
+
+ width: 700
+ height: 800
+
+ minimumHeight: 500
+ minimumWidth: 200
+
+ title: "Window: (" + width + "x" + height + ")"
+ visible: true
+
+ Component {
+ id: delegate
+ Rectangle {
+ width: listView.width
+ height: 70
+ Rectangle {
+ id: circ
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 10
+ height: 50
+ width: 50
+ radius: 25
+ color: "#c8c8c8"
+ }
+ Rectangle {
+ anchors.left: circ.right
+ anchors.right: parent.right
+ anchors.bottom: circ.verticalCenter
+ anchors.leftMargin: 10
+ anchors.rightMargin: 90
+ anchors.bottomMargin: 5
+ height: 11
+ color: "#c8c8c8"
+ }
+ Rectangle {
+ anchors.left: circ.right
+ anchors.right: parent.right
+ anchors.top: circ.verticalCenter
+ anchors.leftMargin: 10
+ anchors.rightMargin: 90
+ anchors.topMargin: 5
+ height: 11
+ color: "#c8c8c8"
+ }
+ Rectangle {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ height: 1
+ color: "#eaeaea"
+ }
+ Rectangle {
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ width: 1
+ color: "#eaeaea"
+ }
+ }
+ }
+
+ ListView {
+ id: listView
+ z: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.minimumWidth: 300
+ Layout.maximumWidth: 550
+
+ model: 20
+ delegate: delegate
+
+ TapHandler {
+ onTapped: swipeView.currentIndex = 1
+ }
+ }
+
+ GridLayout {
+ z: 2
+ id: detailView
+
+ Layout.minimumWidth: 250
+ Layout.fillHeight: false
+ Layout.fillWidth: true
+ Layout.leftMargin: 15
+ Layout.rightMargin: 15
+ Layout.alignment: Qt.AlignTop
+
+ columns: Math.max(width,100)/10
+ columnSpacing: 0
+ rowSpacing: 10
+ Rectangle {
+ Layout.columnSpan: detailView.columns
+ Layout.fillWidth: true
+ height: 300
+ color: "#c8c8c8"
+ }
+ Rectangle {
+ Layout.columnSpan: detailView.columns
+ Layout.fillWidth: true
+ Layout.fillHeight: false
+ height: 10
+ }
+ Repeater {
+ model: 1000
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.fillHeight: false
+ height: 10
+ color: "#c8c8c8"
+ }
+ }
+ }
+
+ LayoutChooser {
+ id: layoutChooser
+ width: parent.width
+ height: parent.height
+
+ layoutChoices: [
+ smallLayout,
+ largeLayout
+ ]
+
+ criteria: [
+ window.width < listView.Layout.minimumWidth + detailView.Layout.minimumWidth + 20,
+ true
+ ]
+
+ property Item smallLayout: ColumnLayout {
+ parent: layoutChooser
+ height: parent.height
+ width: parent.width
+
+ SwipeView {
+ id: swipeView
+
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Item {
+ ColumnLayout {
+ height: parent.height
+ width: parent.width
+
+ spacing: 0
+ Rectangle {
+ height: 50
+ Layout.fillWidth: true
+ }
+ Rectangle {
+ height: 1
+ color: "#eaeaea"
+ Layout.fillWidth: true
+ }
+ LayoutItemProxy { target: listView }
+ }
+ }
+ Item {
+ ColumnLayout {
+ height: parent.height
+ width: parent.width
+ Rectangle {
+ height: 50
+ Layout.fillWidth: true
+
+ Text {
+ id: im
+ FontLoader {
+ id: materialFont
+ source: "https://github.com/google/material-design-icons/blob/master/font/MaterialIcons-Regular.ttf?raw=true"
+ }
+ font.family: materialFont.font.family
+ font.weight: materialFont.font.weight
+ font.pixelSize: 32
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 10
+ text: String.fromCodePoint(0xe5c4)
+ color: "#010101"
+ TapHandler {
+ onTapped: { swipeView.currentIndex = 0; }
+ }
+ }
+ }
+
+ Rectangle {
+ height: 1
+ color: "#eaeaea"
+ Layout.fillWidth: true
+ }
+
+ LayoutItemProxy { target: detailView }
+ }
+ }
+ }
+ }
+
+ property Item largeLayout: ColumnLayout {
+ parent: layoutChooser
+ height: parent.height
+ width: parent.width
+
+ spacing: 0
+ Rectangle {
+ height: 50
+ Layout.fillWidth: true
+ }
+ Rectangle {
+ height: 1
+ color: "#eaeaea"
+ Layout.fillWidth: true
+ }
+
+ RowLayout {
+ spacing: 0
+ LayoutItemProxy { target: listView }
+ LayoutItemProxy { target: detailView }
+ }
+ }
+ }
+
+}
diff --git a/tests/manual/quicklayouts/layoutItemProxy/ms-replace.qml b/tests/manual/quicklayouts/layoutItemProxy/ms-replace.qml
new file mode 100644
index 0000000000..a1b9ba476a
--- /dev/null
+++ b/tests/manual/quicklayouts/layoutItemProxy/ms-replace.qml
@@ -0,0 +1,137 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// This example was created for the blog post about responsive layouts:
+// https://www.qt.io/blog/responsive-layouts-in-qt
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+import QtQuick.Controls
+
+Window {
+ id: window
+
+ width: 600
+ height: 800
+
+ minimumHeight: 500
+ minimumWidth: 160
+
+ title: "Window: (" + width + "x" + height + ")"
+ visible: true
+
+
+ component MyButton : Rectangle {
+ implicitWidth: 56
+ implicitHeight: 56
+ Layout.minimumWidth: 56
+ Layout.minimumHeight: 56
+ Layout.fillWidth: true
+ Layout.fillHeight: false
+ Layout.alignment: Qt.AlignHCenter
+ property string label: ""
+ color: "#eaeaea"
+ Rectangle {
+ implicitWidth: 56
+ implicitHeight: 56
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ color: "#eaeaea"
+ Text {
+ font.pixelSize: 32
+ font.bold: true
+ anchors.centerIn: parent
+ text: label
+ color: "#243a5e"
+ }
+ }
+ }
+
+ LayoutChooser {
+ id: layoutChooser
+ width: parent.width
+ height: parent.height
+
+ layoutChoices: [
+ smallLayout,
+ largeLayout
+ ]
+
+ criteria: [
+ window.width < rl2.Layout.minimumWidth + rl2.anchors.margins * 2,
+ true
+ ]
+
+ MyButton { id: aButton; label: "A"; z: 2 }
+ MyButton { id: bButton; label: "B"; z: 2 }
+ MyButton { id: cButton; label: "C"; z: 2 }
+ MyButton { id: dButton; label: "D"; z: 2 }
+ MyButton { id: eButton; label: "E"; z: 2 }
+ MyButton { id: fButton; label: "F"; z: 2 }
+ MyButton { id: gButton; label: "G"; z: 2 }
+
+ Rectangle {
+ id: rect
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ color: "#fff"
+ }
+
+ property Item smallLayout: RowLayout {
+ parent: layoutChooser
+ height: parent.height
+ width: parent.width
+ spacing: 0
+ Rectangle {
+ id: buttonRect1
+ width: 200
+ Layout.fillHeight: true
+ color: "#c8c8c8"
+ ColumnLayout {
+ anchors.margins: 10
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ spacing: 8
+ LayoutItemProxy { target: aButton }
+ LayoutItemProxy { target: bButton }
+ LayoutItemProxy { target: cButton }
+ LayoutItemProxy { target: dButton }
+ LayoutItemProxy { target: eButton }
+ LayoutItemProxy { target: fButton }
+ LayoutItemProxy { target: gButton }
+ }
+ }
+ LayoutItemProxy { target: rect }
+ }
+
+ property Item largeLayout: ColumnLayout {
+ parent: layoutChooser
+ height: parent.height
+ width: parent.width
+ spacing: 0
+ Rectangle {
+ id: buttonRect2
+ Layout.fillWidth: true
+ height: rl2.height + rl2.anchors.margins * 2
+ implicitWidth: rl2.width + rl2.anchors.margins * 2
+ color: "#c8c8c8"
+ RowLayout {
+ id: rl2
+ anchors.margins: 10
+ anchors.top: parent.top
+ anchors.left: parent.left
+ spacing: 8
+ LayoutItemProxy { target: aButton }
+ LayoutItemProxy { target: bButton }
+ LayoutItemProxy { target: cButton }
+ LayoutItemProxy { target: dButton }
+ LayoutItemProxy { target: eButton }
+ LayoutItemProxy { target: fButton }
+ LayoutItemProxy { target: gButton }
+ }
+ }
+ LayoutItemProxy { target: rect }
+ }
+ }
+}
diff --git a/tests/manual/quicklayouts/layoutItemProxy/ms-showhide.qml b/tests/manual/quicklayouts/layoutItemProxy/ms-showhide.qml
new file mode 100644
index 0000000000..2e4ca140a1
--- /dev/null
+++ b/tests/manual/quicklayouts/layoutItemProxy/ms-showhide.qml
@@ -0,0 +1,155 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// This example was created for the blog post about responsive layouts:
+// https://www.qt.io/blog/responsive-layouts-in-qt
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+
+Window {
+ id: window
+
+ width: 600
+ height: 800
+
+ minimumHeight: 500
+ minimumWidth: 160
+
+ title: "Window: (" + width + "x" + height + ")"
+ visible: true
+
+
+ component MyButton : Rectangle {
+ implicitWidth: 56
+ implicitHeight: 56
+ Layout.minimumWidth: 56
+ Layout.minimumHeight: 56
+ Layout.fillWidth: false
+ Layout.fillHeight: false
+ Layout.alignment: Qt.AlignHCenter
+ property string label: ""
+ color: "#eaeaea"
+ Text {
+ font.pixelSize: 32
+ font.bold: true
+ anchors.centerIn: parent
+ text: label
+ color: "#243a5e"
+ }
+ }
+
+ component ButtonBox : Rectangle {
+
+ id: bb
+ property Item elideButton: Item {}
+ property Item buttonLayout: Item {}
+
+ height: buttonLayout.height + buttonLayout.anchors.margins * 2
+ implicitWidth: buttonLayout.width + buttonLayout.anchors.margins * 2
+
+ color: "#c8c8c8"
+
+ function updateItems() {
+
+ if (width == 0)
+ return;
+
+ let butts = buttonLayout.children
+
+ let lastBut = undefined
+ let xmax = buttonLayout.anchors.margins
+ for (let i = butts.length - 1; i>= 0; --i) {
+ if (butts[i] !== elideButton && butts[i].visible) {
+ if (butts[i].x + butts[i].width > xmax) {
+ lastBut = butts[i]
+ xmax = butts[i].x + butts[i].width
+ }
+ }
+ }
+
+ let buttonsRightX = xmax
+ + buttonLayout.spacing * 2
+ let hiddenItems = 0
+ for (let i = 0; i < butts.length; i++) {
+ if (butts[i] === elideButton)
+ continue;
+ if (butts[i].visible === false) {
+ hiddenItems++;
+ let buttonAddedWidth = butts[i].width + buttonLayout.spacing
+ if (buttonsRightX + buttonAddedWidth < width - elideButton.implicitWidth - buttonLayout.anchors.margins - buttonLayout.spacing) {
+ buttonsRightX += buttonAddedWidth
+ butts[i].visible = true
+ hiddenItems--;
+ }
+ }
+ }
+
+ for (let i = butts.length - 1; i>= 0; --i) {
+ if ( butts[i] === elideButton)
+ continue;
+ if (butts[i].visible === true) {
+ let buttonRemovedWidth = butts[i].width + buttonLayout.spacing
+ if (buttonsRightX > width - elideButton.implicitWidth - buttonLayout.anchors.margins - buttonLayout.spacing) {
+ buttonsRightX -= buttonRemovedWidth
+ butts[i].visible = false
+ hiddenItems++;
+ }
+ }
+ }
+
+ if (hiddenItems == 0)
+ elideButton.visible = false
+ else if (hiddenItems == 1) {
+ for (let i = butts.length - 1; i>= 0; --i)
+ butts[i].visible = true
+ elideButton.visible = false
+ } else
+ elideButton.visible = true
+ }
+
+ onWidthChanged: {
+ updateItems()
+ }
+
+ onChildrenChanged: {
+ updateItems()
+ }
+ }
+
+ ColumnLayout {
+ height: parent.height
+ width: parent.width
+ spacing: 0
+ ButtonBox {
+ Layout.fillHeight: false
+ Layout.fillWidth: true
+
+ RowLayout {
+ id: buttonLayout
+ anchors.margins: 10
+ anchors.top: parent.top
+ anchors.left: parent.left
+ spacing: 8
+ MyButton { id: aButton; label: "A"; z: 2 }
+ MyButton { id: bButton; label: "B"; z: 2 }
+ MyButton { id: cButton; label: "C"; z: 2 }
+ MyButton { id: dButton; label: "D"; z: 2 }
+ MyButton { id: eButton; label: "E"; z: 2 }
+ MyButton { id: fButton; label: "F"; z: 2 }
+ MyButton { id: gButton; label: "G"; z: 2 }
+ MyButton { id: elideButton; label: "..."; z: 2 }
+ }
+ elideButton: elideButton
+ buttonLayout: buttonLayout
+ }
+
+ Rectangle {
+ id: rect
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ color: "#fff"
+ }
+ }
+}
+
diff --git a/tests/manual/quicklayouts/layoutItemProxy/scaffold.qml b/tests/manual/quicklayouts/layoutItemProxy/scaffold.qml
new file mode 100644
index 0000000000..4b45cfb96e
--- /dev/null
+++ b/tests/manual/quicklayouts/layoutItemProxy/scaffold.qml
@@ -0,0 +1,202 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// This example was created for the blog post about responsive layouts:
+// https://www.qt.io/blog/responsive-layouts-in-qt
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+import QtQuick.Controls
+
+
+Window {
+ id: window
+
+ width: 600
+ height: 800
+
+ minimumHeight: 500
+ minimumWidth: 160
+
+ title: "Window: (" + width + "x" + height + ")"
+ visible: true
+
+ FontLoader { id: materialFont; source: "https://github.com/google/material-design-icons/blob/master/font/MaterialIcons-Regular.ttf?raw=true" }
+
+ component MyButton : Rectangle {
+ implicitWidth: 32
+ implicitHeight: label == "" ? 32 : 32+22
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.alignment: Qt.AlignHCenter
+ property int iconId: 0
+ property string label: ""
+
+ Text {
+ id: im
+ height: 32
+ width: 32
+ font.family: materialFont.font.family
+ font.weight: materialFont.font.weight
+ font.pixelSize: 32
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ text: String.fromCodePoint(iconId)
+ color: "#555"
+ }
+
+ Text {
+ text: parent.label
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: im.bottom
+ font.pixelSize: 12
+ color: "#555"
+ }
+ }
+
+ LayoutChooser {
+ id: layoutChooser
+ width: parent.width
+ height: parent.height
+
+ layoutChoices: [
+ smallLayout,
+ mediumLayout,
+ largeLayout
+ ]
+
+ criteria: [
+ width < 700,
+ width < 1000,
+ true
+ ]
+
+ MyButton {
+ id: inboxButton
+ objectName: "inboxButton"
+ iconId: 0xe156 // see https://fonts.google.com/icons
+ label: layoutChooser.width <700 ? "Inbox" : ""
+ }
+ MyButton {
+ id: articlesButton
+ objectName: "articlesButton"
+ iconId: 0xef42 // see https://fonts.google.com/icons
+ }
+ MyButton {
+ id: chatButton
+ objectName: "chatButton"
+ iconId: 0xe0b7 // see https://fonts.google.com/icons
+ }
+ MyButton {
+ id: videoButton
+ objectName: "videoButton"
+ iconId: 0xe070 // see https://fonts.google.com/icons
+ }
+
+ Rectangle {
+ id: bigbox
+ color: '#e99ec0'
+ implicitHeight: 512
+ implicitWidth: 512
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+
+ Flickable {
+ id: flick
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ implicitWidth: 512
+ contentWidth: width
+ contentHeight: gl.height
+ GridLayout {
+ id: gl
+ columns: 2
+ width: parent.width
+ height: implicitHeight
+ columnSpacing: 10
+ rowSpacing: 10
+ Repeater {
+ model: 12
+ LayoutItemProxy { target: rep.itemAt(index) }
+ }
+ }
+ ScrollIndicator.vertical: ScrollIndicator { }
+ }
+
+ Repeater {
+ id: rep
+ model: 12
+ Rectangle {
+ objectName: "Rectangle" + index
+ color: '#ffc9c5'
+ implicitHeight: width
+ implicitWidth: 256
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Text {
+ anchors.centerIn: parent
+ color: '#e99ec0'
+ text: index
+ font.pixelSize: 64
+ }
+ }
+ }
+
+ property Item smallLayout: ColumnLayout {
+ parent: layoutChooser
+ height: parent.height
+ width: parent.width
+ Repeater {
+ model: 2
+ LayoutItemProxy { target: rep.itemAt(index) }
+ }
+ RowLayout {
+ Layout.fillHeight: false
+ Layout.fillWidth: true
+ LayoutItemProxy { target: inboxButton }
+ LayoutItemProxy { target: articlesButton }
+ LayoutItemProxy { target: chatButton }
+ LayoutItemProxy { target: videoButton }
+ }
+ }
+
+ property Item mediumLayout: RowLayout {
+ parent: layoutChooser
+ height: parent.height
+ width: parent.width
+ ColumnLayout {
+ Layout.fillHeight: false
+ Layout.alignment: Qt.AlignTop
+ LayoutItemProxy { target: inboxButton }
+ LayoutItemProxy { target: articlesButton }
+ LayoutItemProxy { target: chatButton }
+ LayoutItemProxy { target: videoButton }
+ }
+ LayoutItemProxy { target: flick }
+ LayoutItemProxy { target: bigbox }
+ }
+
+ property Item largeLayout: RowLayout {
+ parent: layoutChooser
+ height: parent.height
+ width: parent.width
+ GridLayout {
+ columns: 2
+ Layout.fillHeight: false
+ Layout.fillWidth: false
+ Layout.alignment: Qt.AlignTop
+ LayoutItemProxy { target: inboxButton }
+ Text { text: "Inbox"; color: "#555"; font.pixelSize: 20 }
+ LayoutItemProxy { target: articlesButton }
+ Text { text: "Articles"; color: "#555"; font.pixelSize: 20 }
+ LayoutItemProxy { target: chatButton }
+ Text { text: "Chat"; color: "#555"; font.pixelSize: 20 }
+ LayoutItemProxy { target: videoButton }
+ Text { text: "Video"; color: "#555"; font.pixelSize: 20 }
+ }
+ LayoutItemProxy { target: flick }
+ LayoutItemProxy { target: bigbox }
+ }
+ }
+}
diff --git a/tests/manual/quickvectorimage/CMakeLists.txt b/tests/manual/quickvectorimage/CMakeLists.txt
new file mode 100644
index 0000000000..10b17bb143
--- /dev/null
+++ b/tests/manual/quickvectorimage/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(vectorimage)
diff --git a/tests/manual/quickvectorimage/vectorimage/CMakeLists.txt b/tests/manual/quickvectorimage/vectorimage/CMakeLists.txt
new file mode 100644
index 0000000000..631009594f
--- /dev/null
+++ b/tests/manual/quickvectorimage/vectorimage/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(svgitem LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(tst_manual_vectorimage
+ GUI
+ SOURCES
+ vectorimage.cpp
+ LIBRARIES
+ Qt::Quick
+ Qt::QuickVectorImage
+)
+
+# Resources:
+set(res
+ "res/spheres.svg"
+ "Main.qml"
+)
+
+qt_internal_add_resource(tst_manual_vectorimage "vectorimage_res"
+ PREFIX
+ "/"
+ FILES
+ ${res}
+)
diff --git a/tests/manual/quickvectorimage/vectorimage/Main.qml b/tests/manual/quickvectorimage/vectorimage/Main.qml
new file mode 100644
index 0000000000..4ce5f33262
--- /dev/null
+++ b/tests/manual/quickvectorimage/vectorimage/Main.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtCore
+import QtQuick
+import QtQuick.VectorImage
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+Window {
+ width: 1920
+ height: 1080
+ title: qsTr("Vector Image")
+ visible: true
+
+ Column {
+ spacing: 20
+
+ Button {
+ width: 100
+ height: 50
+ text: qsTr("Select Svg file")
+ onClicked: fileDialog.open()
+ }
+
+ VectorImage {
+ id: vectorImage
+ source: "qrc:/res/spheres.svg"
+ }
+ }
+
+ FileDialog {
+ id: fileDialog
+ currentFolder: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
+ onAccepted: vectorImage.source = selectedFile
+ }
+}
diff --git a/tests/manual/quickvectorimage/vectorimage/res/spheres.svg b/tests/manual/quickvectorimage/vectorimage/res/spheres.svg
new file mode 100644
index 0000000000..b23164bce3
--- /dev/null
+++ b/tests/manual/quickvectorimage/vectorimage/res/spheres.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" standalone="no"?>
+<svg width="8cm" height="8cm" viewBox="0 0 400 400"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink/"
+ baseProfile="tiny" version="1.2">
+ <title>Spheres</title>
+ <desc>Gradient filled spheres with different colors.</desc>
+ <defs>
+ <!-- Create radial gradients for each circle to make them look like
+ spheres. -->
+ <radialGradient id="blueSphere" gradientUnits="userSpaceOnUse"
+ cx="0" cy="0" r="100" fx="-50" fy="-50">
+ <stop offset="0%" stop-color="white" />
+ <stop offset="75%" stop-color="blue" />
+ <stop offset="100%" stop-color="#222244" />
+ </radialGradient>
+ <radialGradient id="redSphere" gradientUnits="userSpaceOnUse"
+ cx="0" cy="0" r="100" fx="-50" fy="-50">
+ <stop offset="0%" stop-color="white" />
+ <stop offset="75%" stop-color="red" />
+ <stop offset="100%" stop-color="#442222" />
+ </radialGradient>
+ <radialGradient id="greenSphere" gradientUnits="userSpaceOnUse"
+ cx="0" cy="0" r="100" fx="-50" fy="-50">
+ <stop offset="0%" stop-color="white" />
+ <stop offset="75%" stop-color="green" />
+ <stop offset="100%" stop-color="#113311" />
+ </radialGradient>
+ <radialGradient id="yellowSphere" gradientUnits="userSpaceOnUse"
+ cx="0" cy="0" r="100" fx="-50" fy="-50">
+ <stop offset="0%" stop-color="white" />
+ <stop offset="75%" stop-color="yellow" />
+ <stop offset="100%" stop-color="#444422" />
+ </radialGradient>
+ <radialGradient id="shadowGrad" gradientUnits="userSpaceOnUse"
+ cx="0" cy="0" r="100" fx="-50" fy="50">
+ <stop offset="0%" stop-color="black" stop-opacity="1.0" />
+ <stop offset="100%" stop-color="white" stop-opacity="0.0" />
+ </radialGradient>
+
+ <!-- Define a shadow for each sphere. -->
+ <circle id="shadow" fill="url(#shadowGrad)" cx="0" cy="0" r="100" />
+ </defs>
+ <g fill="#ffee99" stroke="none" >
+ <rect x="0" y="0" width="400" height="400" />
+ </g>
+ <g fill="white" stroke="none" >
+ <rect x="0" y="175" width="400" height="225" />
+ </g>
+ <g transform="translate(200,175)">
+ <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" />
+ <circle fill="url(#blueSphere)" cx="0" cy="0" r="100" />
+ </g>
+ <g transform="translate(315,240)">
+ <g transform="scale(0.5,0.5)">
+ <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" />
+ <circle fill="url(#redSphere)" cx="0" cy="0" r="100" />
+ </g>
+ </g>
+ <g transform="translate(80,275)">
+ <g transform="scale(0.65,0.65)">
+ <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" />
+ <circle fill="url(#greenSphere)" cx="0" cy="0" r="100" />
+ </g>
+ </g>
+ <g transform="translate(255,325)">
+ <g transform="scale(0.3,0.3)">
+ <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" />
+ <circle fill="url(#yellowSphere)" cx="0" cy="0" r="100" />
+ </g>
+ </g>
+</svg>
diff --git a/tests/manual/quickvectorimage/vectorimage/vectorimage.cpp b/tests/manual/quickvectorimage/vectorimage/vectorimage.cpp
new file mode 100644
index 0000000000..d6795d7c64
--- /dev/null
+++ b/tests/manual/quickvectorimage/vectorimage/vectorimage.cpp
@@ -0,0 +1,22 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ const QString filePath(QLatin1String(":/Main.qml"));
+ QObject::connect(
+ &engine,
+ &QQmlApplicationEngine::objectCreationFailed,
+ &app,
+ []() { QCoreApplication::exit(-1); },
+ Qt::QueuedConnection);
+ engine.load(filePath);
+
+ return app.exec();
+}
diff --git a/tests/manual/quickwidgetviewer/main.cpp b/tests/manual/quickwidgetviewer/main.cpp
index 547f4b98e7..d78d35a3ef 100644
--- a/tests/manual/quickwidgetviewer/main.cpp
+++ b/tests/manual/quickwidgetviewer/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QApplication>
#include <QCommandLineOption>
diff --git a/tests/manual/rectangle/corners.qml b/tests/manual/rectangle/corners.qml
new file mode 100644
index 0000000000..87dbbefcaf
--- /dev/null
+++ b/tests/manual/rectangle/corners.qml
@@ -0,0 +1,221 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+import QtQuick.Layouts
+
+Item {
+ id: root
+ width: 600
+ height: 400
+
+ component ResettablePropertySlider: RowLayout {
+ property alias setCheckBox: checkBox
+ property alias valueSlider: slider
+
+ CheckBox {
+ id: checkBox
+ text: "Set"
+ }
+ Slider {
+ id: slider
+ from: 0
+ value: 25
+ to: 200
+ enabled: checkBox.checked
+
+ Layout.fillWidth: true
+ }
+ }
+
+ RowLayout {
+ anchors.fill: parent
+
+ GridLayout {
+ columns: 2
+ rowSpacing: 10
+
+ Label {
+ text: "radius"
+ }
+ Slider {
+ id: radiusSlider
+ from: 0
+ value: 25
+ to: 200
+ }
+
+ Label {
+ text: "topLeftRadius"
+ }
+ ResettablePropertySlider {
+ id: topLeftRadiusSlider
+ }
+
+ Label {
+ text: "topRightRadius"
+ }
+ ResettablePropertySlider {
+ id: topRightRadiusSlider
+ }
+
+ Label {
+ text: "bottomLeftRadius"
+ }
+ ResettablePropertySlider {
+ id: bottomLeftRadiusSlider
+ }
+
+ Label {
+ text: "bottomRightRadius"
+ }
+ ResettablePropertySlider {
+ id: bottomRightRadiusSlider
+ }
+
+ Label {
+ text: "border.width"
+ }
+ Slider {
+ id: borderWidthSlider
+ from: 0
+ value: 15
+ to: 100
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: "border alpha"
+ }
+ Slider {
+ id: borderAlphaSlider
+ from: 0
+ value: 1
+ to: 1
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: "opacity"
+ }
+ Slider {
+ id: opacitySlider
+ from: 0
+ value: 1
+ to: 1
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: "gradient"
+ }
+ ComboBox {
+ id: gradientComboBox
+ model: ListModel {
+ id: model
+ ListElement { text: "NoGradient" }
+ ListElement { text: "LowFreq" }
+ ListElement { text: "HighFreq" }
+ }
+
+ Layout.fillWidth: true
+
+ readonly property var gradients: [undefined, lowFGrad, highFGrad]
+
+ property var lowFGrad: Gradient {
+ orientation: Qt.Horizontal
+ GradientStop { position: 0.0; color: "green" }
+ GradientStop { position: 0.33; color: "blue" }
+ GradientStop { position: 0.66; color: "red" }
+ GradientStop { position: 1.0; color: "yellow" }
+ }
+
+ property var highFGrad: Gradient {
+ orientation: Qt.Horizontal
+ GradientStop { position: 0.00; color: "yellow" }
+ GradientStop { position: 0.05; color: "red" }
+ GradientStop { position: 0.10; color: "yellow" }
+ GradientStop { position: 0.15; color: "red" }
+ GradientStop { position: 0.20; color: "yellow" }
+ GradientStop { position: 0.25; color: "red" }
+ GradientStop { position: 0.30; color: "yellow" }
+ GradientStop { position: 0.35; color: "red" }
+ GradientStop { position: 0.40; color: "yellow" }
+ GradientStop { position: 0.45; color: "red" }
+ GradientStop { position: 0.50; color: "yellow" }
+ GradientStop { position: 0.55; color: "red" }
+ GradientStop { position: 0.60; color: "yellow" }
+ GradientStop { position: 0.65; color: "red" }
+ GradientStop { position: 0.70; color: "yellow" }
+ GradientStop { position: 0.75; color: "red" }
+ GradientStop { position: 0.80; color: "yellow" }
+ GradientStop { position: 0.85; color: "red" }
+ GradientStop { position: 0.90; color: "yellow" }
+ GradientStop { position: 0.95; color: "red" }
+ GradientStop { position: 1.00; color: "yellow" }
+ }
+ }
+
+ Label {
+ text: "rotation"
+ }
+ Slider {
+ id: rotationSlider
+ from: 0
+ value: 0
+ to: 10
+
+ Layout.fillWidth: true
+ }
+ }
+
+ Rectangle {
+ id: rect1
+ color: colorDialog.selectedColor
+ border.color: borderColorDialog.alphaAdjusted
+ border.width: borderWidthSlider.value
+ radius: radiusSlider.value
+ topLeftRadius: topLeftRadiusSlider.setCheckBox.checked ? topLeftRadiusSlider.valueSlider.value : undefined
+ topRightRadius: topRightRadiusSlider.setCheckBox.checked ? topRightRadiusSlider.valueSlider.value : undefined
+ bottomLeftRadius: bottomLeftRadiusSlider.setCheckBox.checked ? bottomLeftRadiusSlider.valueSlider.value : undefined
+ bottomRightRadius: bottomRightRadiusSlider.setCheckBox.checked ? bottomRightRadiusSlider.valueSlider.value : undefined
+ antialiasing: true
+ opacity: opacitySlider.value
+ rotation: rotationSlider.value
+ gradient: gradientComboBox.gradients[gradientComboBox.currentIndex]
+
+ Layout.preferredWidth: root.width / 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 32
+
+ TapHandler {
+ onTapped: colorDialog.open()
+ }
+
+ TapHandler {
+ acceptedButtons: Qt.RightButton
+ onTapped: borderColorDialog.open()
+ }
+ }
+
+ ColorDialog {
+ id: colorDialog
+ selectedColor: "khaki"
+ }
+
+ ColorDialog {
+ id: borderColorDialog
+ selectedColor: "sienna"
+ property color alphaAdjusted: Qt.rgba(selectedColor.r,
+ selectedColor.g,
+ selectedColor.b,
+ borderAlphaSlider.value)
+ }
+ }
+}
diff --git a/tests/manual/righttoleft/CMakeLists.txt b/tests/manual/righttoleft/CMakeLists.txt
new file mode 100644
index 0000000000..72a13c05f3
--- /dev/null
+++ b/tests/manual/righttoleft/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(layoutdirection)
+add_subdirectory(layoutmirroring)
+add_subdirectory(textalignment)
diff --git a/tests/manual/righttoleft/layoutdirection/CMakeLists.txt b/tests/manual/righttoleft/layoutdirection/CMakeLists.txt
new file mode 100644
index 0000000000..7c1e38e79a
--- /dev/null
+++ b/tests/manual/righttoleft/layoutdirection/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(layoutdirection LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(tst_manual_layoutdirection
+ GUI
+ SOURCES
+ layoutdirection.cpp
+ LIBRARIES
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+set(resource_files
+ "layoutdirection.qml"
+)
+
+
+qt_internal_add_resource(tst_manual_layoutdirection "qmake_immediate"
+ PREFIX
+ "/qt/qml/layoutdirection"
+ FILES
+ ${resource_files}
+)
diff --git a/tests/manual/righttoleft/layoutdirection/layoutdirection.cpp b/tests/manual/righttoleft/layoutdirection/layoutdirection.cpp
new file mode 100644
index 0000000000..f0296a5ba2
--- /dev/null
+++ b/tests/manual/righttoleft/layoutdirection/layoutdirection.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui/qguiapplication.h>
+#include <QtQuick/QQuickView>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("layoutdirection-manual-test");
+ QCoreApplication::setOrganizationName("QtProject");
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/qt/qml/layoutdirection/layoutdirection.qml")));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/righttoleft/layoutdirection/layoutdirection.pro b/tests/manual/righttoleft/layoutdirection/layoutdirection.pro
new file mode 100644
index 0000000000..bd50b5ad14
--- /dev/null
+++ b/tests/manual/righttoleft/layoutdirection/layoutdirection.pro
@@ -0,0 +1,5 @@
+TEMPLATE = app
+TARGET = layoutdirection
+QT += qml quick
+SOURCES += layoutdirection.cpp
+RESOURCES += layoutdirection.qrc
diff --git a/tests/manual/righttoleft/layoutdirection/layoutdirection.qml b/tests/manual/righttoleft/layoutdirection/layoutdirection.qml
new file mode 100644
index 0000000000..5f9bc6ca9c
--- /dev/null
+++ b/tests/manual/righttoleft/layoutdirection/layoutdirection.qml
@@ -0,0 +1,220 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+pragma ComponentBehavior: Bound
+
+Rectangle {
+ id: root
+ property bool mirror
+ property int direction: (Qt.application as Application).layoutDirection
+ LayoutMirroring.enabled: mirror
+ LayoutMirroring.childrenInherit: true
+ width: 320
+ height: 480
+ Column {
+ id: columnA
+ spacing: 10
+ x: 10
+ y: 10
+ width: 140
+
+ Item {
+ id: rowCell
+ }
+ Text {
+ text: qsTr("Row")
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ Row {
+ layoutDirection: root.direction
+ spacing: 10
+ move: Transition {
+ NumberAnimation {
+ properties: "x"
+ }
+ }
+ Repeater {
+ model: 3
+ delegate: PositionerDelegate {
+ }
+ }
+ }
+
+ Text {
+ text: qsTr("Grid")
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ Grid {
+ layoutDirection: root.direction
+ spacing: 10
+ columns: 3
+ move: Transition {
+ NumberAnimation {
+ properties: "x"
+ }
+ }
+ Repeater {
+ model: 8
+ delegate: PositionerDelegate {
+ }
+ }
+ }
+
+ Text {
+ text: qsTr("Flow")
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ Flow {
+ layoutDirection: root.direction
+ spacing: 10; width: parent.width
+ move: Transition {
+ NumberAnimation {
+ properties: "x"
+ }
+ }
+ Repeater {
+ model: 8
+ delegate: PositionerDelegate {
+
+ }
+ }
+ }
+ }
+ Column {
+ id: columnB
+ spacing: 10
+ x: 160
+ y: 10
+
+ Text {
+ text: qsTr("ListView")
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ ListView {
+ id: listView
+ clip: true
+ width: parent.width; height: 40
+ layoutDirection: root.direction
+ orientation: Qt.Horizontal
+ model: 48
+ delegate: ViewDelegate {
+ }
+ }
+
+ Text {
+ text: "GridView"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ GridView {
+ clip: true
+ width: 150; height: 160
+ cellWidth: 50; cellHeight: 50
+ layoutDirection: root.direction
+ model: 48
+ delegate: ViewDelegate {
+ }
+ }
+
+ Rectangle {
+ height: 50; width: parent.width
+ color: mouseArea.pressed ? "black" : "gray"
+ Column {
+ anchors.centerIn: parent
+ Text {
+ text: root.direction ? qsTr("Right to left") : qsTr("Left to right")
+ color: "white"
+ font.pixelSize: 16
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ Text {
+ text: qsTr("(click here to toggle)")
+ color: "white"
+ font.pixelSize: 10
+ font.italic: true
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onClicked: {
+ if (root.direction === Qt.LeftToRight) {
+ root.direction = Qt.RightToLeft;
+ } else {
+ root.direction = Qt.LeftToRight;
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ height: 50; width: parent.width
+ color: mouseArea2.pressed ? "black" : "gray"
+ Column {
+ anchors.centerIn: parent
+ Text {
+ text: root.mirror ? qsTr("Mirrored") : qsTr("Not mirrored")
+ color: "white"
+ font.pixelSize: 16
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ Text {
+ text: qsTr("(click here to toggle)")
+ color: "white"
+ font.pixelSize: 10
+ font.italic: true
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ MouseArea {
+ id: mouseArea2
+ anchors.fill: parent
+ onClicked: {
+ root.mirror = !root.mirror;
+ }
+ }
+ }
+ }
+
+ component PositionerDelegate : Rectangle {
+ width: 40
+ height: 40
+
+ required property int index
+ property int lightness: index + 1
+ color: Qt.rgba(0.8 / lightness, 0.8 / lightness, 0.8 / lightness, 1.0)
+ Text {
+ text: parent.lightness
+ color: "white"
+ font.pixelSize: 18
+ anchors.centerIn: parent
+ }
+ }
+
+ component ViewDelegate : Item {
+ id: delegateItem
+ required property int index
+ width: (listView.effectiveLayoutDirection === Qt.LeftToRight ? (index === 48 - 1) : (index === 0)) ? 40 : 50
+ Rectangle {
+ width: 40; height: 40
+ color: Qt.rgba(0.5 + (48 - delegateItem.index) * Math.random() / 48,
+ 0.3 + delegateItem.index * Math.random() / 48,
+ 0.3 * Math.random(),
+ 1.0)
+ Text {
+ text: delegateItem.index + 1
+ color: "white"
+ font.pixelSize: 18
+ anchors.centerIn: parent
+ }
+ }
+ }
+}
+
diff --git a/tests/manual/righttoleft/layoutdirection/layoutdirection.qrc b/tests/manual/righttoleft/layoutdirection/layoutdirection.qrc
new file mode 100644
index 0000000000..cdf2c187f4
--- /dev/null
+++ b/tests/manual/righttoleft/layoutdirection/layoutdirection.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/qt/qml/layoutdirection">
+ <file>layoutdirection.qml</file>
+ </qresource>
+</RCC>
+
diff --git a/tests/manual/righttoleft/layoutmirroring/CMakeLists.txt b/tests/manual/righttoleft/layoutmirroring/CMakeLists.txt
new file mode 100644
index 0000000000..69fb0a05a8
--- /dev/null
+++ b/tests/manual/righttoleft/layoutmirroring/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(layoutmirroring LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(tst_manual_layoutmirroring
+ GUI
+ SOURCES
+ layoutmirroring.cpp
+ LIBRARIES
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+set(resource_files
+ "layoutmirroring.qml"
+)
+
+
+qt_internal_add_resource(tst_manual_layoutmirroring "qmake_immediate"
+ PREFIX
+ "/qt/qml/layoutmirroring"
+ FILES
+ ${resource_files}
+)
diff --git a/tests/manual/righttoleft/layoutmirroring/layoutmirroring.cpp b/tests/manual/righttoleft/layoutmirroring/layoutmirroring.cpp
new file mode 100644
index 0000000000..7116ea42c9
--- /dev/null
+++ b/tests/manual/righttoleft/layoutmirroring/layoutmirroring.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui/qguiapplication.h>
+#include <QtQuick/QQuickView>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("layoutmirroring-manual-test");
+ QCoreApplication::setOrganizationName("QtProject");
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/qt/qml/layoutmirroring/layoutmirroring.qml")));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/righttoleft/layoutmirroring/layoutmirroring.pro b/tests/manual/righttoleft/layoutmirroring/layoutmirroring.pro
new file mode 100644
index 0000000000..4e52fb4162
--- /dev/null
+++ b/tests/manual/righttoleft/layoutmirroring/layoutmirroring.pro
@@ -0,0 +1,5 @@
+TEMPLATE = app
+TARGET = layoutmirroring
+QT += qml quick
+SOURCES += layoutmirroring.cpp
+RESOURCES += layoutmirroring.qrc
diff --git a/tests/manual/righttoleft/layoutmirroring/layoutmirroring.qml b/tests/manual/righttoleft/layoutmirroring/layoutmirroring.qml
new file mode 100644
index 0000000000..9927544e9c
--- /dev/null
+++ b/tests/manual/righttoleft/layoutmirroring/layoutmirroring.qml
@@ -0,0 +1,197 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ id: root
+ property bool mirror: (Qt.application as Application).layoutDirection == Qt.RightToLeft
+ LayoutMirroring.enabled: mirror
+ LayoutMirroring.childrenInherit: true
+ width: 320
+ height: 480
+ color: "lightsteelblue"
+
+ Flickable {
+ anchors {
+ fill: parent
+ margins: 10
+ }
+ contentWidth: parent.width
+ contentHeight: content.height
+ flickableDirection: Flickable.VerticalFlick
+
+ Column {
+ id: content
+ spacing: 10
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+
+ Text {
+ text: qsTr("Text alignment")
+ anchors.left: parent.left
+ }
+
+ Rectangle {
+ width: textStrings.width + 10
+ height: textStrings.height + 10
+ color: "white"
+ anchors.left: parent.left
+ Column {
+ anchors.centerIn: parent
+ id: textStrings
+ spacing: 5
+ width: 148
+ Text {
+ id: englishText
+ width: parent.width
+ text: qsTr("English text")
+ }
+ Text {
+ id: arabicText
+ width: parent.width
+ text: qsTr("النص العربي")
+ }
+ Text {
+ id: leftAlignedText
+ width: parent.width
+ text: qsTr("Text aligned to left")
+ horizontalAlignment: Text.AlignLeft
+ }
+ Text {
+ id: rightAlignedText
+ width: parent.width
+ text: qsTr("Text aligned to right")
+ horizontalAlignment: Text.AlignRight
+ }
+ }
+ }
+
+ Text {
+ text: qsTr("Item x")
+ anchors.left: parent.left
+ }
+ Rectangle {
+ id: items
+ color: Qt.rgba(0.2, 0.2, 0.2, 0.6)
+ width: 275
+ height: 95
+ anchors.left: parent.left
+ Rectangle {
+ y: 5
+ x: 5
+ width: 130
+ height: 40
+ Text {
+ text: qsTr("Item with x: 5\n(not mirrored)")
+ anchors.centerIn: parent
+ }
+ }
+ Rectangle {
+ color: Qt.rgba(0.7, 0.7, 0.7)
+ x: mirror(5)
+ y: 50
+ width: 130
+ height: 40
+ function mirror(value) {
+ return LayoutMirroring.enabled ? (parent.width - width - value) : value;
+ }
+ Text {
+ text: qsTr("Item with x: %1 \n(manually mirrored)").arg(parent.x)
+ anchors.centerIn: parent
+ }
+ }
+ }
+ Text {
+ text: qsTr("Item anchors")
+ anchors.left: parent.left
+ }
+
+ Rectangle {
+ id: anchoredItems
+ color: Qt.rgba(0.2, 0.2, 0.2, 0.6)
+ width: 270
+ height: 170
+ anchors.left: parent.left
+ Rectangle {
+ id: blackRectangle
+ color: "black"
+ width: 180
+ height: 90
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ horizontalCenterOffset: 30
+ }
+ Text {
+ text: qsTr("Horizontal center anchored\nwith offset 30\nto the horizontal center\nof the parent.")
+ color: "white"
+ anchors.centerIn: parent
+ }
+ }
+ Rectangle {
+ id: whiteRectangle
+ color: "white"
+ width: 120
+ height: 70
+ anchors {
+ left: parent.left
+ bottom: parent.bottom
+ }
+ Text {
+ text: qsTr("Left side anchored\nto the left side\nof the parent.")
+ color: "black"
+ anchors.centerIn: parent
+ }
+ }
+ Rectangle {
+ id: grayRectangle
+ color: Qt.rgba(0.7, 0.7, 0.7)
+ width: 140
+ height: 90
+ anchors {
+ right: parent.right
+ bottom: parent.bottom
+ }
+ Text {
+ text: qsTr("Right side anchored\nto the right side\nof the parent.")
+ anchors.centerIn: parent
+ }
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: mirrorButton
+ color: mouseArea2.pressed ? "black" : "gray"
+ height: 50
+ width: 160
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.margins: 10
+ Column {
+ anchors.centerIn: parent
+ Text {
+ text: root.mirror ? qsTr("Mirrored") : qsTr("Not mirrored")
+ color: "white"
+ font.pixelSize: 16
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ Text {
+ text: qsTr("(click here to toggle)")
+ color: "white"
+ font.pixelSize: 10
+ font.italic: true
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ MouseArea {
+ id: mouseArea2
+ anchors.fill: parent
+ onClicked: root.mirror = !root.mirror;
+ }
+ }
+}
+
diff --git a/tests/manual/righttoleft/layoutmirroring/layoutmirroring.qrc b/tests/manual/righttoleft/layoutmirroring/layoutmirroring.qrc
new file mode 100644
index 0000000000..44500b40d6
--- /dev/null
+++ b/tests/manual/righttoleft/layoutmirroring/layoutmirroring.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/qt/qml/layoutmirroring">
+ <file>layoutmirroring.qml</file>
+ </qresource>
+</RCC>
+
diff --git a/tests/manual/righttoleft/textalignment/CMakeLists.txt b/tests/manual/righttoleft/textalignment/CMakeLists.txt
new file mode 100644
index 0000000000..fa0c90f79c
--- /dev/null
+++ b/tests/manual/righttoleft/textalignment/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(textalignment LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(tst_manual_textalignment
+ GUI
+ SOURCES
+ textalignment.cpp
+ LIBRARIES
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+)
+
+set(resource_files
+ "textalignment.qml"
+)
+
+qt_internal_add_resource(tst_manual_textalignment "qmake_immediate"
+ PREFIX
+ "/qt/qml/textalignment"
+ FILES
+ ${resource_files}
+)
diff --git a/tests/manual/righttoleft/textalignment/textalignment.cpp b/tests/manual/righttoleft/textalignment/textalignment.cpp
new file mode 100644
index 0000000000..a3510befdf
--- /dev/null
+++ b/tests/manual/righttoleft/textalignment/textalignment.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui/qguiapplication.h>
+#include <QtQuick/QQuickView>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("textalignment-manual-test");
+ QCoreApplication::setOrganizationName("QtProject");
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/qt/qml/textalignment/textalignment.qml")));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/righttoleft/textalignment/textalignment.pro b/tests/manual/righttoleft/textalignment/textalignment.pro
new file mode 100644
index 0000000000..5cf4e2c0d0
--- /dev/null
+++ b/tests/manual/righttoleft/textalignment/textalignment.pro
@@ -0,0 +1,5 @@
+TEMPLATE = app
+TARGET = textalignment
+QT += qml quick
+SOURCES += textalignment.cpp
+RESOURCES += textalignment.qrc
diff --git a/tests/manual/righttoleft/textalignment/textalignment.qml b/tests/manual/righttoleft/textalignment/textalignment.qml
new file mode 100644
index 0000000000..152860cf13
--- /dev/null
+++ b/tests/manual/righttoleft/textalignment/textalignment.qml
@@ -0,0 +1,472 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+pragma ComponentBehavior: Bound
+
+Rectangle {
+ id: root
+ color: "white"
+ width: 320
+ height: 480
+
+ property bool mirror: false
+ property int pxSz: 18
+ property variant horizontalAlignment: undefined
+
+ readonly property variant editorType: [qsTr("Plain Text"), qsTr("Styled Text"), qsTr("Plain Rich Text"), qsTr("Italic Rich Text"), qsTr("Plain TextEdit"), qsTr("Italic TextEdit"), qsTr("TextInput")]
+ readonly property variant text: ["", " ", "Hello world!", "مرحبا العالم!", "Hello world! Hello!\nHello world! Hello!", "مرحبا العالم! مرحبا! مرحبا العالم! مرحبا!" ,"مرحبا العالم! مرحبا! مرحبا Hello world!\nالعالم! مرحبا!"]
+ readonly property variant description: [qsTr("empty text"), qsTr("white-space-only text"), qsTr("left-to-right text"), qsTr("right-to-left text"), qsTr("multi-line left-to-right text"), qsTr("multi-line right-to-left text"), qsTr("multi-line bidi text")]
+ readonly property variant textComponents: [plainTextComponent, styledTextComponent, richTextComponent, italicRichTextComponent, plainTextEdit, italicTextEdit, textInput]
+
+ function shortText(horizontalAlignment) {
+
+ // all the different QML editors have
+ // the same alignment values
+ switch (horizontalAlignment) {
+ case Text.AlignLeft:
+ return qsTr("L");
+ case Text.AlignRight:
+ return qsTr("R");
+ case Text.AlignHCenter:
+ return qsTr("C");
+ case Text.AlignJustify:
+ return qsTr("J");
+ default:
+ return qsTr("Error");
+ }
+ }
+
+ ListView {
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ bottom: buttons.top
+ topMargin: 5
+ }
+ model: root.editorType.length
+ orientation: ListView.Horizontal
+ cacheBuffer: 1000 //Load the really expensive ones async if possible
+ delegate: Item {
+ id: delegate
+
+ required property int index
+
+ width: ListView.view.width
+ height: ListView.view.height
+
+ Column {
+ anchors.horizontalCenter: parent.horizontalCenter
+ spacing: 5
+ width: textColumn.width + 10
+ Text {
+ text: root.editorType[delegate.index]
+ font.pixelSize: 16
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ Column {
+ id: textColumn
+ spacing: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ Repeater {
+ model: root.textComponents.length
+ delegate: root.textComponents[delegate.index]
+ }
+ }
+ }
+ }
+ }
+
+ Column {
+ id: buttons
+ spacing: 2
+ width: parent.width
+ anchors.bottom: parent.bottom
+ Rectangle {
+ // button
+ height: 50
+ width: parent.width
+ color: mouseArea.pressed ? "black" : "lightgray"
+ Column {
+ anchors.centerIn: parent
+ Text {
+ text: root.mirror ? qsTr("Mirrored") : qsTr("Not mirrored")
+ color: "white"
+ font.pixelSize: 16
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ Text {
+ text: qsTr("(click here to toggle)")
+ color: "white"
+ font.pixelSize: 10
+ font.italic: true
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ MouseArea {
+ id: mouseArea
+
+ property int index: 0
+
+ anchors.fill: parent
+ onClicked: root.mirror = !root.mirror
+ }
+ }
+ Rectangle {
+ // button
+ height: 50
+ width: parent.width
+ color: mouseArea2.pressed ? "black" : "gray"
+ Column {
+ anchors.centerIn: parent
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "white"
+ font.pixelSize: 16
+ text: {
+ if (root.horizontalAlignment == undefined)
+ return qsTr("Implict alignment");
+ switch (root.horizontalAlignment) {
+ case Text.AlignLeft:
+ return qsTr("Left alignment");
+ case Text.AlignRight:
+ return qsTr("Right alignment");
+ case Text.AlignHCenter:
+ return qsTr("Center alignment");
+ case Text.AlignJustify:
+ return qsTr("Justify alignment");
+ }
+ }
+ }
+ Text {
+ text: qsTr("(click here to toggle)")
+ color: "white"
+ font.pixelSize: 10
+ font.italic: true
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ MouseArea {
+ id: mouseArea2
+
+ property int index: 0
+
+ anchors.fill: parent
+ onClicked: {
+ root.horizontalAlignment = index < 0 ? undefined : Math.pow(2, index)
+ index = (index + 2) % 5 - 1;
+ }
+ }
+ }
+ }
+
+ Component {
+ id: plainTextComponent
+ Text {
+ required property int index
+
+ width: 180
+ text: root.text[index]
+ font.pixelSize: root.pxSz
+ wrapMode: Text.WordWrap
+ horizontalAlignment: root.horizontalAlignment
+ LayoutMirroring.enabled: root.mirror
+ textFormat: Text.RichText
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.8, 0.2, 0.2, 0.3)
+ anchors.fill: parent
+ }
+ Text {
+ text: root.description[parent.index]
+ color: Qt.rgba(1,1,1,1.0)
+ anchors.centerIn: parent
+ font.pixelSize: root.pxSz - 2
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.3, 0, 0, 0.3)
+ anchors {
+ fill: parent
+ margins: -3
+ }
+ }
+ }
+ Text {
+ color: "white"
+ text: root.shortText(parent.horizontalAlignment)
+ anchors {
+ top: parent.top
+ right: parent.right
+ margins: 2
+ }
+ }
+ }
+ }
+
+ Component {
+ id: styledTextComponent
+ Text {
+ required property int index
+
+ width: 180
+ text: root.text[index]
+ font.pixelSize: root.pxSz
+ wrapMode: Text.WordWrap
+ horizontalAlignment: root.horizontalAlignment
+ LayoutMirroring.enabled: root.mirror
+ textFormat: Text.RichText
+ style: Text.Sunken
+ styleColor: "white"
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.8, 0.2, 0.2, 0.3)
+ anchors.fill: parent
+ }
+ Text {
+ text: root.description[parent.index]
+ color: Qt.rgba(1,1,1,1.0)
+ anchors.centerIn: parent
+ font.pixelSize: root.pxSz - 2
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.3, 0, 0, 0.3)
+ anchors {
+ fill: parent
+ margins: -3
+ }
+ }
+ }
+ Text {
+ color: "white"
+ text: root.shortText(parent.horizontalAlignment)
+ anchors {
+ top: parent.top
+ right: parent.right
+ margins: 2
+ }
+ }
+ }
+ }
+
+ Component {
+ id: richTextComponent
+ Text {
+ required property int index
+
+ width: 180
+ text: root.text[index]
+ font.pixelSize: root.pxSz
+ wrapMode: Text.WordWrap
+ horizontalAlignment: root.horizontalAlignment
+ LayoutMirroring.enabled: root.mirror
+ textFormat: Text.RichText
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.8, 0.2, 0.2, 0.3)
+ anchors.fill: parent
+ }
+ Text {
+ text: root.description[parent.index]
+ color: Qt.rgba(1,1,1,1.0)
+ anchors.centerIn: parent
+ font.pixelSize: root.pxSz - 2
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.3, 0, 0, 0.3)
+ anchors {
+ fill: parent
+ margins: -3
+ }
+ }
+ }
+ Text {
+ color: "white"
+ text: root.shortText(parent.horizontalAlignment)
+ anchors {
+ top: parent.top
+ right: parent.right
+ margins: 2
+ }
+ }
+ }
+ }
+
+ Component {
+ id: italicRichTextComponent
+ Text {
+ required property int index
+
+ width: 180
+ text: "<i>" + root.text[index] + "</i>"
+ font.pixelSize: root.pxSz
+ wrapMode: Text.WordWrap
+ horizontalAlignment: root.horizontalAlignment
+ LayoutMirroring.enabled: root.mirror
+ textFormat: Text.RichText
+ property variant backgroundColor: Qt.rgba(0.8, 0.2, 0.2, 0.3)
+ Rectangle {
+ z: -1
+ color: parent.backgroundColor
+ anchors.fill: parent
+ }
+ Text {
+ text: root.description[parent.index]
+ color: Qt.rgba(1,1,1,1.0)
+ anchors.centerIn: parent
+ font.pixelSize: root.pxSz - 2
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.3, 0, 0, 0.3)
+ anchors {
+ fill: parent
+ margins: -3
+ }
+ }
+ }
+ Text {
+ color: "white"
+ text: root.shortText(parent.horizontalAlignment)
+ anchors {
+ top: parent.top
+ right: parent.right
+ margins: 2
+ }
+ }
+ }
+ }
+
+ Component {
+ id: plainTextEdit
+ TextEdit {
+ required property int index
+
+ width: 180
+ text: root.text[index]
+ font.pixelSize: root.pxSz
+ cursorVisible: true
+ wrapMode: TextEdit.WordWrap
+ horizontalAlignment: root.horizontalAlignment
+ LayoutMirroring.enabled: root.mirror
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.5, 0.5, 0.2, 0.3)
+ anchors.fill: parent
+ }
+ Text {
+ text: root.description[parent.index]
+ color: Qt.rgba(1,1,1,1.0)
+ anchors.centerIn: parent
+ font.pixelSize: root.pxSz - 2
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.3, 0, 0, 0.3)
+ anchors {
+ fill: parent
+ margins: -3
+ }
+ }
+ }
+ Text {
+ color: "white"
+ text: root.shortText(parent.horizontalAlignment)
+ anchors {
+ top: parent.top
+ right: parent.right
+ margins: 2
+ }
+ }
+ }
+ }
+
+ Component {
+ id: italicTextEdit
+ TextEdit {
+ required property int index
+
+ width: 180
+ text: "<i>" + root.text[index] + "<i>"
+ font.pixelSize: root.pxSz
+ cursorVisible: true
+ wrapMode: TextEdit.WordWrap
+ textFormat: TextEdit.RichText
+ horizontalAlignment: root.horizontalAlignment
+ LayoutMirroring.enabled: root.mirror
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.5, 0.5, 0.2, 0.3)
+ anchors.fill: parent
+ }
+ Text {
+ text: root.description[parent.index]
+ color: Qt.rgba(1,1,1,1.0)
+ anchors.centerIn: parent
+ font.pixelSize: root.pxSz - 2
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.3, 0, 0, 0.3)
+ anchors {
+ fill: parent
+ margins: -3
+ }
+ }
+ }
+ Text {
+ color: "white"
+ text: root.shortText(parent.horizontalAlignment)
+ anchors {
+ top: parent.top
+ right: parent.right
+ margins: 2
+ }
+ }
+ }
+ }
+
+ Component {
+ id: textInput
+ TextInput {
+ id: textDelegate
+
+ required property int index
+
+ width: 180
+ text: root.text[textDelegate.index]
+ font.pixelSize: root.pxSz
+ cursorVisible: true
+ wrapMode: Text.Wrap
+ horizontalAlignment: root.horizontalAlignment
+ LayoutMirroring.enabled: root.mirror
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.6, 0.4, 0.2, 0.3)
+ anchors.fill: parent
+ }
+ Text {
+ text: root.description[textDelegate.index]
+ color: Qt.rgba(1,1,1,1.0)
+ anchors.centerIn: parent
+ font.pixelSize: root.pxSz - 2
+ Rectangle {
+ z: -1
+ color: Qt.rgba(0.3, 0, 0, 0.3)
+ anchors {
+ fill: parent
+ margins: -3
+ }
+ }
+ }
+ Text {
+ color: "white"
+ text: root.shortText(parent.horizontalAlignment)
+ anchors {
+ top: parent.top
+ right: parent.right
+ margins: 2
+ }
+ }
+ }
+ }
+}
+
diff --git a/tests/manual/righttoleft/textalignment/textalignment.qrc b/tests/manual/righttoleft/textalignment/textalignment.qrc
new file mode 100644
index 0000000000..7f58328966
--- /dev/null
+++ b/tests/manual/righttoleft/textalignment/textalignment.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/qt/qml/textalignment">
+ <file>textalignment.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/scalablepath/ShapeTestScale.qml b/tests/manual/scalablepath/ShapeTestScale.qml
index a317ea8dd1..fbc781c1cc 100644
--- a/tests/manual/scalablepath/ShapeTestScale.qml
+++ b/tests/manual/scalablepath/ShapeTestScale.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
diff --git a/tests/manual/scalablepath/main.cpp b/tests/manual/scalablepath/main.cpp
index 543737539a..dd5727a7d3 100644
--- a/tests/manual/scalablepath/main.cpp
+++ b/tests/manual/scalablepath/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/scalablepath/main.qml b/tests/manual/scalablepath/main.qml
index 4f4a1eb34e..a05aa028c2 100644
--- a/tests/manual/scalablepath/main.qml
+++ b/tests/manual/scalablepath/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Window 2.14
diff --git a/tests/manual/shapestest/main.cpp b/tests/manual/shapestest/main.cpp
index a16f1ce8a2..b5161cdba6 100644
--- a/tests/manual/shapestest/main.cpp
+++ b/tests/manual/shapestest/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QSurfaceFormat>
diff --git a/tests/manual/shapestest/shapestest.qml b/tests/manual/shapestest/shapestest.qml
index ad3cbd2239..ce6df8f330 100644
--- a/tests/manual/shapestest/shapestest.qml
+++ b/tests/manual/shapestest/shapestest.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
import QtQuick.Shapes 1.0
diff --git a/tests/manual/shared/Button.qml b/tests/manual/shared/Button.qml
new file mode 100644
index 0000000000..319cb929fa
--- /dev/null
+++ b/tests/manual/shared/Button.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+
+Item {
+ id: container
+
+ property alias text: buttonLabel.text
+ property alias label: buttonLabel
+ signal clicked
+ property alias containsMouse: hoverHandler.hovered
+ property alias pressed: tapHandler.pressed
+ implicitHeight: Math.max(Screen.pixelDensity * 7, buttonLabel.implicitHeight * 1.2)
+ implicitWidth: Math.max(Screen.pixelDensity * 11, buttonLabel.implicitWidth * 1.3)
+ height: implicitHeight
+ width: implicitWidth
+
+ SystemPalette { id: palette }
+
+ Rectangle {
+ id: frame
+ anchors.fill: parent
+ color: palette.button
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? Qt.darker(palette.button, 1.3) : palette.button }
+ GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) }
+ }
+ antialiasing: true
+ radius: height / 6
+ border.color: Qt.darker(palette.button, 1.5)
+ border.width: 1
+ }
+
+ TapHandler {
+ id: tapHandler
+ onTapped: container.clicked();
+ }
+ HoverHandler {
+ id: hoverHandler
+ }
+
+ Text {
+ id: buttonLabel
+ text: container.text
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+}
diff --git a/tests/manual/shared/CMakeLists.txt b/tests/manual/shared/CMakeLists.txt
new file mode 100644
index 0000000000..bef105cb9c
--- /dev/null
+++ b/tests/manual/shared/CMakeLists.txt
@@ -0,0 +1,62 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+set_source_files_properties(CheckBox.qml TabSet.qml TextField.qml
+ PROPERTIES
+ QT_QML_SOURCE_VERSIONS 2.1
+)
+
+set_source_files_properties(Images.qml
+ PROPERTIES
+ QT_QML_SINGLETON_TYPE true
+ QT_QML_SOURCE_VERSIONS 2.2
+)
+
+qt_add_qml_module(${PROJECT_NAME}_shared
+ URI shared
+ VERSION 2.2
+ PLUGIN_TARGET ${PROJECT_NAME}_shared
+ SOURCES
+ "shared.h"
+ QML_FILES
+ "Button.qml"
+ "CheckBox.qml"
+ "FlickrRssModel.qml"
+ "Label.qml"
+ "LauncherList.qml"
+ "SimpleLauncherDelegate.qml"
+ "Slider.qml"
+ "TabSet.qml"
+ "TextField.qml"
+ "Images.qml"
+ RESOURCES
+ "images/back.png"
+ "images/checkmark.png"
+ "images/next.png"
+ "images/qt-logo.png"
+ "images/slider_handle.png"
+ "images/tab.png"
+)
+
+qt_autogen_tools(${PROJECT_NAME}_shared ENABLE_AUTOGEN_TOOLS "moc" "rcc")
+if(TARGET Qt::Widgets)
+ qt_autogen_tools(${PROJECT_NAME}_shared ENABLE_AUTOGEN_TOOLS "uic")
+endif()
+
+include(QtBundleQmlModuleForMacOS.cmake)
+# Puts the shared qml module plugin and qmldir into the macOS app bundle directory.
+# Only call this function if your main project has the MACOSX_BUNDLE option set.
+function(bundle_shared app_target)
+ set(qml_module_target "${PROJECT_NAME}_shared")
+ add_qml_module_to_macos_app_bundle("${app_target}" "${qml_module_target}")
+endfunction()
+
+set(INSTALL_SHAREDDIR "${INSTALL_EXAMPLESDIR}/quick/${PROJECT_NAME}/shared")
+install(TARGETS ${PROJECT_NAME}_shared
+ RUNTIME DESTINATION "${INSTALL_SHAREDDIR}"
+ LIBRARY DESTINATION "${INSTALL_SHAREDDIR}"
+)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
+ DESTINATION "${INSTALL_SHAREDDIR}"
+)
diff --git a/tests/manual/shared/CheckBox.qml b/tests/manual/shared/CheckBox.qml
new file mode 100644
index 0000000000..0ba11a075f
--- /dev/null
+++ b/tests/manual/shared/CheckBox.qml
@@ -0,0 +1,59 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: root
+ implicitHeight: frame.height
+ implicitWidth: row.implicitWidth
+ width: implicitWidth
+ height: implicitHeight
+ property alias text: label.text
+ property bool checked
+ property alias pressed: tapHandler.pressed
+ property alias row: row
+ signal clicked
+
+ SystemPalette { id: palette }
+
+ Row {
+ id: row
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: 6
+ Rectangle {
+ id: frame
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? Qt.darker(palette.button, 1.3) : palette.button }
+ GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) }
+ }
+ height: label.implicitHeight * 1.5
+ width: height
+ anchors.margins: 1
+ radius: 3
+ antialiasing: true
+ border.color: Qt.darker(palette.button, 1.5)
+ Image {
+ id: theX
+ source: "images/checkmark.png"
+ anchors.fill: frame
+ anchors.margins: frame.width / 5
+ fillMode: Image.PreserveAspectFit
+ smooth: true
+ visible: root.checked
+ }
+ }
+ Text {
+ id: label
+ color: palette.text
+ anchors.verticalCenter: frame.verticalCenter
+ }
+ }
+ TapHandler {
+ id: tapHandler
+ onTapped: {
+ parent.checked = !parent.checked
+ parent.clicked()
+ }
+ }
+}
diff --git a/tests/manual/shared/FlickrRssModel.qml b/tests/manual/shared/FlickrRssModel.qml
new file mode 100644
index 0000000000..cc9d574399
--- /dev/null
+++ b/tests/manual/shared/FlickrRssModel.qml
@@ -0,0 +1,45 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+ListModel {
+ id: flickrImages
+ property string tags : ""
+ readonly property string queryUrl : "http://api.flickr.com/services/feeds/photos_public.gne?"
+
+ function encodeParams(x) {
+ return encodeURIComponent(x.replace(" ",","));
+ }
+ function fetchImages(format) {
+ var requestURL = queryUrl + (tags ? "tags="+encodeParams(tags)+"&" : "") + "format=" + format + "&nojsoncallback=1";
+ var xhr = new XMLHttpRequest;
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === XMLHttpRequest.DONE) {
+
+ if (xhr.status !== 200) {
+ console.log("Failed to get images from flickr. status code: " + xhr.status);
+ return;
+ }
+
+ var jsonText = xhr.responseText;
+ var objArray = JSON.parse(jsonText.replace(/\'/g,"'"))
+ if (objArray.errors !== undefined)
+ console.log("Error fetching tweets: " + objArray.errors[0].message)
+ else {
+ for (var key in objArray.items) {
+ var rssItem = objArray.items[key];
+ var jsonObject = "{ \"title\": \"" + rssItem.title +"\",\"media\": \"" + rssItem.media.m + "\", \"thumbnail\": \"" + rssItem.media.m.replace(/\_m\.jpg/,"_s.jpg") +"\"}"
+ flickrImages.append(JSON.parse(jsonObject));
+ }
+ }
+ }
+ }
+ xhr.open("GET", requestURL, true);
+ xhr.send();
+ }
+ Component.onCompleted: {
+ fetchImages("json");
+ }
+}
+
diff --git a/tests/manual/shared/Images.qml b/tests/manual/shared/Images.qml
new file mode 100644
index 0000000000..489d81c9e7
--- /dev/null
+++ b/tests/manual/shared/Images.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Singleton
+import QtQml
+
+QtObject {
+ readonly property url back: Qt.resolvedUrl("images/back.png")
+ readonly property url checkmark: Qt.resolvedUrl("images/checkmark.png")
+ readonly property url next: Qt.resolvedUrl("images/next.png")
+ readonly property url qtLogo: Qt.resolvedUrl("images/qt-logo.png")
+ readonly property url sliderHandle: Qt.resolvedUrl("images/slider_handle.png")
+ readonly property url tab: Qt.resolvedUrl("images/tab.png")
+}
diff --git a/tests/manual/shared/Label.qml b/tests/manual/shared/Label.qml
new file mode 100644
index 0000000000..6186d64135
--- /dev/null
+++ b/tests/manual/shared/Label.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ SystemPalette { id: palette }
+ color: palette.text
+}
diff --git a/tests/manual/shared/QtBundleQmlModuleForMacOS.cmake b/tests/manual/shared/QtBundleQmlModuleForMacOS.cmake
new file mode 100644
index 0000000000..767b64bbfc
--- /dev/null
+++ b/tests/manual/shared/QtBundleQmlModuleForMacOS.cmake
@@ -0,0 +1,35 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+function(add_qml_module_to_macos_app_bundle app_target qml_module)
+ if(QT6_IS_SHARED_LIBS_BUILD AND APPLE)
+ # The application's main.cpp adds an explicit QML import path to look for qml module plugins
+ # under a PlugIns subdirectory of a macOS app bundle.
+ # Copy the qmldir and shared library qml plugin.
+
+ qt6_query_qml_module(${qml_module}
+ QMLDIR qmldir_file
+ PLUGIN_TARGET qml_plugin_target
+ URI qml_module_uri
+ )
+
+ # Ensure the executable depends on the plugin so the plugin is copied
+ # only after it was built.
+ add_dependencies(${app_target} ${qml_plugin_target})
+
+ set(app_dir "$<TARGET_FILE_DIR:${app_target}>")
+
+ string(REGEX REPLACE "[^A-Za-z0-9]" "_" escaped_uri "${qml_module_uri}")
+
+ set(dest_module_dir_in_app_bundle "${app_dir}/../PlugIns/${escaped_uri}")
+
+ add_custom_command(TARGET ${app_target} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${dest_module_dir_in_app_bundle}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ $<TARGET_FILE:${qml_plugin_target}> ${dest_module_dir_in_app_bundle}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ ${qmldir_file} ${dest_module_dir_in_app_bundle}
+ )
+ endif()
+endfunction()
+
diff --git a/tests/manual/shared/Slider.qml b/tests/manual/shared/Slider.qml
new file mode 100644
index 0000000000..e8beeed632
--- /dev/null
+++ b/tests/manual/shared/Slider.qml
@@ -0,0 +1,82 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: slider
+ height: 26
+ // default drag range is 180: divisible by 2, 3, 4, 5, 6, 9, 10, ...
+ width: sliderName.width + 223 + handle.width / 2
+
+ property real min: 0
+ property real max: 1
+ property real value: min + (max - min) * dragHandler.value
+ property real init: min+(max-min)/2
+ property string name: "Slider"
+ property color color: "#0066cc"
+ property real minLabelWidth: 44
+
+ DragHandler {
+ id: dragHandler
+ target: handle
+ xAxis.minimum: Math.round(-handle.width / 2 + 3)
+ xAxis.maximum: Math.round(groove.width - handle.width / 2 - 3)
+ property real value: (handle.x - xAxis.minimum) / (xAxis.maximum - xAxis.minimum)
+ }
+
+ Component.onCompleted: setValue(init)
+ function setValue(v) {
+ if (min < max) {
+ handle.x = Math.round( v / (max - min) *
+ (dragHandler.xAxis.maximum - dragHandler.xAxis.minimum)
+ + dragHandler.xAxis.minimum);
+// console.log(name, v, "-> handle.x", handle.x, "from fraction", (v / (max - min)),
+// "of drag range", (dragHandler.xAxis.maximum - dragHandler.xAxis.minimum), "px", min, ":", max)
+ }
+ }
+ Rectangle {
+ id:sliderName
+ anchors.left: parent.left
+ anchors.leftMargin: 16
+ height: childrenRect.height
+ width: Math.max(slider.minLabelWidth, childrenRect.width)
+ anchors.verticalCenter: parent.verticalCenter
+ Text {
+ text: slider.name + ":"
+ font.pointSize: 12
+ color: "#333"
+ }
+ }
+
+ Rectangle {
+ id: groove
+ width: parent.width - 8 - sliderName.width
+ color: "#eee"
+ height: 7
+ radius: 3
+ antialiasing: true
+ border.color: Qt.darker(color, 1.2)
+ anchors.left: sliderName.right
+ anchors.right: parent.right
+ anchors.leftMargin: 10
+ anchors.rightMargin: 24
+ anchors.verticalCenter: parent.verticalCenter
+
+ Rectangle {
+ height: parent.height
+ anchors.left: parent.left
+ anchors.right: handle.horizontalCenter
+ color: slider.color
+ radius: 3
+ border.width: 1
+ border.color: Qt.darker(color, 1.3)
+ opacity: 0.8
+ }
+ Image {
+ id: handle
+ source: "images/slider_handle.png"
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+}
diff --git a/tests/manual/shared/TabSet.qml b/tests/manual/shared/TabSet.qml
new file mode 100644
index 0000000000..b5cf14639c
--- /dev/null
+++ b/tests/manual/shared/TabSet.qml
@@ -0,0 +1,69 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+
+Item {
+ id: tabWidget
+
+ // Setting the default property to stack.children means any child items
+ // of the TabWidget are actually added to the 'stack' item's children.
+ // See the "Property Binding"
+ // documentation for details on default properties.
+ default property alias content: stack.children
+
+ property int current: 0
+
+ onCurrentChanged: setZOrders()
+ Component.onCompleted: setZOrders()
+
+ function setZOrders() {
+ for (var i = 0; i < stack.children.length; ++i) {
+ stack.children[i].z = (i == current ? 1 : 0)
+ stack.children[i].enabled = (i == current)
+ }
+ }
+
+ Row {
+ id: header
+
+ Repeater {
+ model: stack.children.length
+ delegate: Rectangle {
+ required property int index
+ width: tabWidget.width / stack.children.length
+ height: Math.max(Screen.pixelDensity * 7, label.implicitHeight * 1.2)
+
+ Rectangle {
+ width: parent.width; height: 1
+ anchors { bottom: parent.bottom; bottomMargin: 1 }
+ color: "#acb2c2"
+ }
+ BorderImage {
+ anchors { fill: parent; leftMargin: 2; topMargin: 5; rightMargin: 1 }
+ border { left: 7; right: 7 }
+ source: "images/tab.png"
+ visible: tabWidget.current == parent.index
+ }
+ Text {
+ id: label
+ horizontalAlignment: Qt.AlignHCenter; verticalAlignment: Qt.AlignVCenter
+ anchors.fill: parent
+ text: stack.children[parent.index].title
+ elide: Text.ElideRight
+ font.bold: tabWidget.current == parent.index
+ }
+ TapHandler {
+ onTapped: tabWidget.current = parent.index
+ }
+ }
+ }
+ }
+
+ Item {
+ id: stack
+ width: tabWidget.width
+ anchors.top: header.bottom; anchors.bottom: tabWidget.bottom
+ }
+}
diff --git a/tests/manual/shared/TextField.qml b/tests/manual/shared/TextField.qml
new file mode 100644
index 0000000000..74b59bc088
--- /dev/null
+++ b/tests/manual/shared/TextField.qml
@@ -0,0 +1,43 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: root
+
+ property alias textInput: textInput
+ property alias text: textInput.text
+ signal accepted
+ signal downPressed
+ implicitWidth: textInput.implicitWidth + rect.radius * 2
+ implicitHeight: textInput.implicitHeight
+
+ function copyAll() {
+ textInput.selectAll()
+ textInput.copy()
+ }
+
+ SystemPalette { id: palette }
+ height: textInput.implicitHeight + 8
+ clip: true
+
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ radius: height / 4
+ color: palette.button
+ border.color: Qt.darker(palette.button, 1.5)
+ }
+
+ TextInput {
+ id: textInput
+ color: palette.text
+ anchors.fill: parent
+ anchors.leftMargin: rect.radius
+ anchors.rightMargin: rect.radius
+ verticalAlignment: Text.AlignVCenter
+ onAccepted: root.accepted()
+ Keys.onDownPressed: root.downPressed()
+ }
+}
diff --git a/tests/manual/shared/images/back.png b/tests/manual/shared/images/back.png
new file mode 100644
index 0000000000..53402096b2
--- /dev/null
+++ b/tests/manual/shared/images/back.png
Binary files differ
diff --git a/tests/manual/shared/images/checkmark.png b/tests/manual/shared/images/checkmark.png
new file mode 100644
index 0000000000..821aafccdd
--- /dev/null
+++ b/tests/manual/shared/images/checkmark.png
Binary files differ
diff --git a/tests/manual/shared/images/next.png b/tests/manual/shared/images/next.png
new file mode 100644
index 0000000000..cdef8db6e8
--- /dev/null
+++ b/tests/manual/shared/images/next.png
Binary files differ
diff --git a/tests/manual/shared/images/qt-logo.png b/tests/manual/shared/images/qt-logo.png
new file mode 100644
index 0000000000..ecbff0ca36
--- /dev/null
+++ b/tests/manual/shared/images/qt-logo.png
Binary files differ
diff --git a/tests/manual/shared/images/slider_handle.png b/tests/manual/shared/images/slider_handle.png
new file mode 100644
index 0000000000..63c518be7d
--- /dev/null
+++ b/tests/manual/shared/images/slider_handle.png
Binary files differ
diff --git a/tests/manual/shared/images/tab.png b/tests/manual/shared/images/tab.png
new file mode 100644
index 0000000000..2ea989b68d
--- /dev/null
+++ b/tests/manual/shared/images/tab.png
Binary files differ
diff --git a/tests/manual/shared/qmldir b/tests/manual/shared/qmldir
new file mode 100644
index 0000000000..5ccdf60219
--- /dev/null
+++ b/tests/manual/shared/qmldir
@@ -0,0 +1,11 @@
+module shared
+Button 2.0 Button.qml
+CheckBox 2.1 CheckBox.qml
+FlickrRssModel 2.0 FlickrRssModel.qml
+Label 2.0 Label.qml
+LauncherList 2.0 LauncherList.qml
+SimpleLauncherDelegate 2.0 SimpleLauncherDelegate.qml
+Slider 2.0 Slider.qml
+TabSet 2.1 TabSet.qml
+TextField 2.1 TextField.qml
+singleton Images 2.2 Images.qml
diff --git a/tests/manual/shared/quick_shared.qrc b/tests/manual/shared/quick_shared.qrc
new file mode 100644
index 0000000000..21f393a64d
--- /dev/null
+++ b/tests/manual/shared/quick_shared.qrc
@@ -0,0 +1,15 @@
+<RCC>
+ <qresource prefix="/quick/shared">
+ <file>LauncherList.qml</file>
+ <file>SimpleLauncherDelegate.qml</file>
+ <file>Button.qml</file>
+ <file>CheckBox.qml</file>
+ <file>Label.qml</file>
+ <file>TextField.qml</file>
+ <file>images/back.png</file>
+ <file>images/next.png</file>
+ <file>images/checkmark.png</file>
+ <file>Slider.qml</file>
+ <file>images/slider_handle.png</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/shared/shared.h b/tests/manual/shared/shared.h
new file mode 100644
index 0000000000..b033ed5e66
--- /dev/null
+++ b/tests/manual/shared/shared.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QDir>
+#include <QGuiApplication>
+#include <QQmlEngine>
+#include <QQmlFileSelector>
+#include <QQuickView> //Not using QQmlApplicationEngine because many examples don't have a Window{}
+
+#ifdef Q_OS_MACOS
+#define ADD_MACOS_BUNDLE_IMPORT_PATH \
+ view.engine()->addImportPath(app.applicationDirPath() + QStringLiteral("/../PlugIns"));
+#else
+#define ADD_MACOS_BUNDLE_IMPORT_PATH
+#endif
+
+#define DECLARATIVE_EXAMPLE_MAIN(NAME) int main(int argc, char* argv[]) \
+{\
+ QGuiApplication app(argc,argv);\
+ app.setOrganizationName("QtProject");\
+ app.setOrganizationDomain("qt-project.org");\
+ app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName());\
+ QQuickView view;\
+ ADD_MACOS_BUNDLE_IMPORT_PATH\
+ if (qEnvironmentVariableIntValue("QT_QUICK_CORE_PROFILE")) {\
+ QSurfaceFormat f = view.format();\
+ f.setProfile(QSurfaceFormat::CoreProfile);\
+ f.setVersion(4, 4);\
+ view.setFormat(f);\
+ }\
+ if (qEnvironmentVariableIntValue("QT_QUICK_MULTISAMPLE")) {\
+ QSurfaceFormat f = view.format();\
+ f.setSamples(4);\
+ view.setFormat(f);\
+ }\
+ view.connect(view.engine(), &QQmlEngine::quit, &app, &QCoreApplication::quit);\
+ new QQmlFileSelector(view.engine(), &view);\
+ view.setSource(QUrl("qrc:/qt/qml/" #NAME ".qml")); \
+ if (view.status() == QQuickView::Error)\
+ return -1;\
+ view.setResizeMode(QQuickView::SizeRootObjectToView);\
+ view.show();\
+ return app.exec();\
+}
diff --git a/tests/manual/shared/shared.qrc b/tests/manual/shared/shared.qrc
new file mode 100644
index 0000000000..67f69c9c8d
--- /dev/null
+++ b/tests/manual/shared/shared.qrc
@@ -0,0 +1,21 @@
+<RCC>
+ <qresource prefix="/qt/qml/shared">
+ <file>Button.qml</file>
+ <file>CheckBox.qml</file>
+ <file>FlickrRssModel.qml</file>
+ <file>Images.qml</file>
+ <file>Label.qml</file>
+ <file>LauncherList.qml</file>
+ <file>SimpleLauncherDelegate.qml</file>
+ <file>Slider.qml</file>
+ <file>TabSet.qml</file>
+ <file>TextField.qml</file>
+ <file>images/back.png</file>
+ <file>images/checkmark.png</file>
+ <file>images/next.png</file>
+ <file>images/qt-logo.png</file>
+ <file>images/slider_handle.png</file>
+ <file>images/tab.png</file>
+ <file>qmldir</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/shortcuts/shortcuts.qml b/tests/manual/shortcuts/shortcuts.qml
index 7687700303..9e9bfeddc6 100644
--- a/tests/manual/shortcuts/shortcuts.qml
+++ b/tests/manual/shortcuts/shortcuts.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.5
import QtQuick.Layouts 1.1
@@ -77,6 +77,7 @@ ApplicationWindow {
ComboBox {
id: combobox
enabled: checkbox.checked
+ Layout.fillWidth: false
model: ["WindowShortcut", "ApplicationShortcut"]
}
diff --git a/tests/manual/svg/CMakeLists.txt b/tests/manual/svg/CMakeLists.txt
new file mode 100644
index 0000000000..1576c0d741
--- /dev/null
+++ b/tests/manual/svg/CMakeLists.txt
@@ -0,0 +1,74 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.5)
+
+project(svg VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Quick Qml Svg SvgWidgets QuickWidgets QuickVectorImageGeneratorPrivate)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Quick Qml Svg SvgWidgets QuickWidgets QuickVectorImageGeneratorPrivate)
+
+set(PROJECT_SOURCES
+ main.cpp
+ mainwindow.h mainwindow.cpp
+ mainwindow.ui
+ svgpainter.h svgpainter.cpp
+ svgmanager.h svgmanager.cpp
+)
+
+if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
+ qt_add_executable(svg
+ MANUAL_FINALIZATION
+ ${PROJECT_SOURCES}
+ )
+# Define target properties for Android with Qt 6 as:
+# set_property(TARGET svg APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
+# ${CMAKE_CURRENT_SOURCE_DIR}/android)
+# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
+else()
+ if(ANDROID)
+ add_library(svg SHARED
+ ${PROJECT_SOURCES}
+ )
+# Define properties for Android with Qt 5 after find_package() calls as:
+# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
+ else()
+ add_executable(svg
+ ${PROJECT_SOURCES}
+ )
+ endif()
+endif()
+
+target_link_libraries(svg PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Qml Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::QuickWidgets Qt${QT_VERSION_MAJOR}::QuickVectorImageGeneratorPrivate Qt${QT_VERSION_MAJOR}::SvgWidgets)
+
+set_target_properties(svg PROPERTIES
+ MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+install(TARGETS svg
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+
+qt_add_qml_module(svg
+ VERSION 1.0
+ URI SvgImageTest
+ QML_FILES SvgImage.qml QmlGenerator.qml
+)
+
+
+if(QT_VERSION_MAJOR EQUAL 6)
+ qt_finalize_executable(svg)
+endif()
diff --git a/tests/manual/svg/QmlGenerator.qml b/tests/manual/svg/QmlGenerator.qml
new file mode 100644
index 0000000000..96435ba4f0
--- /dev/null
+++ b/tests/manual/svg/QmlGenerator.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import SvgImageTest
+
+Item {
+ id: item
+ width: childrenRect.width * (SvgManager.scale / 10.0)
+ height: childrenRect.height * (SvgManager.scale / 10.0)
+ scale: SvgManager.scale / 10
+ transformOrigin: Item.TopLeft
+
+ property var dynamicObject: null
+ Connections {
+ target: SvgManager
+ function onCurrentSourceChanged() {
+ if (dynamicObject)
+ dynamicObject.destroy()
+
+ var s = SvgManager.qmlSource
+
+ dynamicObject = Qt.createQmlObject(s, item, "dummy.qml")
+ }
+ }
+}
diff --git a/tests/manual/svg/SvgImage.qml b/tests/manual/svg/SvgImage.qml
new file mode 100644
index 0000000000..ce0bb4ab12
--- /dev/null
+++ b/tests/manual/svg/SvgImage.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.VectorImage
+import SvgImageTest
+
+Item {
+ width: vectorImage.implicitWidth * (SvgManager.scale / 10.0)
+ height: vectorImage.implicitHeight * (SvgManager.scale / 10.0)
+ scale: SvgManager.scale / 10.0
+ transformOrigin: Item.TopLeft
+ VectorImage {
+ id: vectorImage
+ source: SvgManager.currentSource
+ }
+}
diff --git a/tests/manual/svg/data/image/1.svg b/tests/manual/svg/data/image/1.svg
new file mode 100644
index 0000000000..d5f27450c2
--- /dev/null
+++ b/tests/manual/svg/data/image/1.svg
@@ -0,0 +1,3 @@
+<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
+ <image xlink:href="data.png" height="200" width="200" />
+</svg>
diff --git a/tests/manual/svg/data/image/2.svg b/tests/manual/svg/data/image/2.svg
new file mode 100644
index 0000000000..3f71e5ca23
--- /dev/null
+++ b/tests/manual/svg/data/image/2.svg
@@ -0,0 +1,3 @@
+<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
+ <image xlink:href="qtlogo.png" height="200" width="200" />
+</svg>
diff --git a/tests/manual/svg/data/image/3.svg b/tests/manual/svg/data/image/3.svg
new file mode 100644
index 0000000000..85751587d9
--- /dev/null
+++ b/tests/manual/svg/data/image/3.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg viewBox="0 0 210 297">
+<image width="200" height="200" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFQAAAA8CAYAAAGPy61gAAAAAXNSR0IArs4c6QAAAJZlWElmTU0A&#10;KgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgExAAIAAAARAAAAWodp&#10;AAQAAAABAAAAbAAAAAAAAABIAAAAAQAAAEgAAAABQWRvYmUgSW1hZ2VSZWFkeQAAAAOgAQADAAAA&#10;AQABAACgAgAEAAAAAQAAAFSgAwAEAAAAAQAAADwAAAAAm/SbDAAAAAlwSFlzAAALEwAACxMBAJqc&#10;GAAAAi1pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6&#10;bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRm&#10;PSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJk&#10;ZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8v&#10;bnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMu&#10;YWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+QWRvYmUgSW1h&#10;Z2VSZWFkeTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43Mjwv&#10;dGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmll&#10;bnRhdGlvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+&#10;CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpg60/Z&#10;AAAXv0lEQVR4Ae1caZBdxXXuvve+bXaNFhAIJI0EWmZGCEaIJYVLBlxFlkpBbBGXKwRXYkOFwmAW&#10;B20uPYFGQrahAnHiiMTlCrHLCYqdhB/g2NiocDDgMEbSLEhCmhkJAVJmkWZ9672d7+v77tvfmzej&#10;wdYPGt7cvr2cPn369OlzTp8rKZBaetsvwuM08+WS0Xb8yXo08BrGjJCP7VWxTkZM2udSFZGupq1B&#10;J5KIzlua8LMsMDxPP72OhpcBrIE1p79JiMGhfv8W6TNFR9s9SemIP/DaZBrLzNCO45gqYau2jucs&#10;ZYiXChsLseDQxV+TMmCJ4NmTO9FAash495IEJbInE7Hm1YTMo0F/rHEwjkZxc06V3z47qdtnurnd&#10;Q8nBcWHPn4wLWxekG0bt00HJojzobre8v0ZL7y7HK7Pm1girsdp7zXmCCkpDBW1lcnhcGFV+oVRi&#10;MUkGPNONM+RiEaYXP3VWBJsWfiQd+9bk4ORtQmo4In9CaQjKMF+x5gWEN/MciByWv+jJoSs4tFEd&#10;SHdMN+TMySxyIrHCDPi6HVBJRUhSN+UMnRyawNqIIwKNmPQ7nmZ9CMVIrSd2z1GOM4jJpEdg+UxT&#10;/rJrOGZdUMzpHPPt37DdXU+WSqkwFa6P5FyFoxqN2uCwfc5lCd0x9acAqLWgVhyoHjZb+v02Fspt&#10;hvkYIX/NoYsenWz98BtO56WPuWuB2kW/eijUsHD+ZLotynLoRAjJ/xt7TSzdrqS1R4D9WaTX2JlM&#10;7N0o9t39btwWre8/qfxn5vjt5ZF4chi09QZ3WxfSEJh+SuzYIZWd5kROWETt+H37xEYHANIVKg9Y&#10;CmZRTMVVD9bbB+c8JJe/92wgEB/2dTeHx9mhpdeXwMNQSVvUjn2kTg02BoOmaBCGPA0aezBFAU3T&#10;NbmZeK0v1DASHV9pmOZvcqty37hPNMGX9IWDdaHqCBlzNpLZrML+KlEfdcaiswFPw7Cs4bqYPVLI&#10;a0YN9hzoJP2WAC+SDcCclSXDHonkt1SUKYcWPGoc+ocJ8+CzI2bX85DbSnlS1WG9mVCLXOLldy/C&#10;p1LKV9kMu2cAj2pu3UQdhMqJ47fHFyyNq6TLUZZljdsqs+GyQRfs9c6lW25JNZiLZxDY+IOmf7S2&#10;7RKlkuogsNRk6Fi2aYQYK6keywbIfD7QWH4DvoOmkf0ynJSG2tzau/NTLNvwathqPvbEzZDCy/ie&#10;nXK3qXQPhOXvheuyGyHvcraSfZDk3Xg3xy4PVkvD+Xn+FmW/XEwdd4WPXREeZWVW0vzsCLkkBT5V&#10;ldlFWW3zgEqRkd1ZrcyGqtAGFbYMoXZlFZfM5mKKZi3H23+Rav0az18eBQfnjJj9/ZAThrzag8SF&#10;olyVGCo/FZZI8WnSVEUSn5GmUeNTkzgpt6sa4RtMSS6juW/X7ja11xcfl3Uqoe7LB1pcoEDU6UO0&#10;NiiM2oAYPnE6dO2S1Yl3+95LegD0qYx2lPw5YhINCjFlL8hJFU/iMBsX8f4hUaN8j+yTd9okh5co&#10;nFmfB1DFg4H5xTH1enpPKSCc9bqX2f9SWTLeeGBp+Fz6rKFAtkLjNcGaqkFgQV5J13mwf5dP2dwd&#10;9geDwVCywX8OAmsGCEr0wTbjtIpvhVmZn2U0hGKxmK2EK1XLUFEqsz4IHTmyu2vZ1i3FRm/p33mj&#10;NMzXVcJJAvEMo6QaSz9WOTUZtHEnWAxQkTJLxTQzl0YQNSZ2wMF5j2Dfhw0xN6xFdMsHOy9TUfkF&#10;qEJ1ylG/7F7+9Z90LU6+KeQ2DQsSnucJ1XtuBAc7yehctEmvoK9KVKk59We1vlThKhTMOn8y0mcJ&#10;NTjZRCQ3YNDBvvavgCrtIiaqXc0Wq44MEIuLPmF1pQ5HnAihNWe+pZyJmMIBahipnRhcvVDZ/R9p&#10;dVNgIblzHexcFQXBSpNrSuXEFqZhdi3enAZx1dDTqsjh681PSUP+Uefi+E+WHxvyBa2LN2Oi2yE2&#10;qoaDCevI/MfGdEMc+isG99RYKqLMUVDX8N2rDPl4ttbkAfSexeWTVwv7QDrqLRe4kje8/3QIllum&#10;tjAXARvcIWTYWXRqLjRb8WPKuXGZmAwKSx9ObW/v9a3tD9dX1daMmnbVmKqqOiMsoyySHGbKpS92&#10;TBbil13imkw5JfGk4yiYjKlkOkGVhGniRDHpCnk03dkDkvf0YUmu02XYNW9c9nDEqNKGc16z9GvQ&#10;EMaL5OdTixpN8O7tqCmuE6W7VJaZiqIWz73m409e3t0UPbVhvzCGRiL3AvTf4keM07yLPPRaGTjU&#10;tOVF5MUxIWLYTDvURNzdMamWtW0fqbEOkZBzg2xWcZqKovosNWusE8uPNfpOLRoyO5dufQ47Ooif&#10;YTiiFafgdaomUctdPu9EHNqri3zrqT16x2Nt6T0wKD2Y9ovtdkebiI4PGKC+ugG1UEvUt2j3lEuV&#10;ncsuBAVFSsKIfl0Yzjb4Ct7sXxrW1gF1yf2fDqe1C4gqbhza8DnnOJUFyO32RNzac2SlKwFW9bcv&#10;NBz5l9IQT5Tb9dNBNHfCJIDeCDhClbJx6lidl23SZKFuFesf/FfUk0cLVy3dF7XZ+dwRvDcH9pgx&#10;c0Q9MFlPa36tSAyMbzQsuQ/yF9TTEl0jn9Ws0qxeweTEyNxYtHFiqs1UKVDdLjkwRgLtg90Cq16f&#10;tDNA0tUprHPxOdGPRiIwr7U1WwAI+vj/YrQ1GJnn9AWTcija3LuTtvtq/C4oJEmtNKItvTsPgLNX&#10;gJoBd5Ow+sJJWnzApXgIKK3Er6h9V4BummGoNGNq6feClrNWYLb27YJmplYAYiXLjR0CjXguNDz4&#10;PCCj4df1CbMuBBCQUgknjuoc2TlbmFrweK0CsEJZVzgClEYck6a5/kD9V9/Nr6ZJI0PWP8Fhepcz&#10;keCxmUNn2LQYBUX4X7v2tAzOh1L6nQhWgqQN+I/DBKntWvzXh0E9HIthCEo8w+6TYkRFkl86dNHX&#10;pFkbIJJpVIikYzt3W1FnQeelmwwQ5x3UT8vnVQmSUQx5oLNp6xN6vvBNYsFVc691R3Nv+67mu3wP&#10;tp58soF1RLbt7XCVMx5bBWWGyKYTusSM6vgEKIrZSZ4E00rpXV+0F4bC+R6Ef3OdpiAatZwwV1iP&#10;PtuTPDNGftRSHc+nW4/v/EXnsm23dKwL05l3uKWvHW5Zfd67CMNT5bfr0vihMJ0vOnZeYXlEHZGA&#10;EuJ6EaG1s681+kwPry+AXHrzQavHjIybW/t33eAf7H97pL7FkM7ovUrKfyFfGnCQA066PcDUQjcI&#10;GdiIrK/ETC+PqBS0O75NBMULG80116xdlRykcyJ3o6S0HjoF/qZj3XOuoi3ED+BQ/z6Mtk6sCE+6&#10;dIJKSFGYTrxvgt+XhCjJilMgKuFGdl7isreJhUasb+jL1JRAzcJ+EFiwNNenR+eoULrtSMJccXhP&#10;7cIVEe1qZH3bh+Gq6FlhmHOrFa6uJiphgpIzADL6ngYW+fGNYrXsEPdAPKnfg5Xp8lw2RswrZeJu&#10;wy2lJGARbxikCJm+5Prh46LR4/NYxFqNO6A2J5K8FrrrGlzOsHlxuKxBKo0oKunZgA9fW4/c6Si6&#10;GGK9OECWejXbkUdy6NyQcqlVG3jFlr5bbzhVlzr55PcgovYbAfNVOCEPuupgurfbOe9v4RJmNaBj&#10;YXR0JNs4S5saWc0yWU6FaV+3izKwwZtUsDYlWSaT6ImOwgr1DCdvipkWebmyFOU4dXV1mSPREBRJ&#10;Hjp5oPDqDbex2W3DmTLxzdArol8JozQQt0n+3/KI4sZqLBFxXeU8hRxxGsK6+Bgs9Wp2uMMYwbIL&#10;lo9L2ffSiGJQXhVJ227aJ3pUm3gOWoj4FWSmh04+YAd+Jrcs7MpcKi4lUykoJTqURpQdlJp0DAP3&#10;vGGnY98rjmPbe7GEGVbIBRqDQ51neDoprEipBKaYFqplpowhlPCBzR5Abru4c5/dg12yduwZmRwY&#10;LxDOMIVDMp64bwPuJfpxjVDrWJ91InQ+uJwrDSPtYgHj8nDwOLrUXHLKyyNqCB/Oeq1weDJQ9Dtz&#10;IAPPcjsoqhYYTmtHE/G9OOvfTEFPQhn/nse0+ohVaomnqaLXAfS5Cc4xAbksIE9RVELspQBOsfRw&#10;HJ2djMHg6+byCxFW5tC7EwyEiJ9LzrMT9vUibq/i+7yTifsJk3ophPifYQJZLh9943vbG98d1UyM&#10;I/QB+KTXwxlxle+DyWojZGGPYquWSZXa9RhAvtDVtOXPQUnZ3LPDF5wjLO8YXHHR4iivTUj1lj7f&#10;54yg79+0py4zsGNU+w3qqrzTXdIvkgMLcDWIxNtmun/gnixgp0x3jI7Zg+wVpShOkSCUjM24s/lB&#10;1/Jt7+f0wrG55G7hr074VhpV1jv0i+Yl7iwwi7m+u2nTQV3HiX1oXSoSxkkQgNc5JVd4OogSNp2z&#10;8GBgrRi7QD5NOMIejZzFcjbqwfEHk+/Bg3ZY7sAKKFX7fYz1YF8yOCkPRZvZcik+XURdYATqrQM3&#10;kwF8HPt2ZzL5sufZWDv6jCqqEmb3JbT8d3cE96+ukzFMqjN3xtmNyuU9JNkGebCCMufW/ieRpHhi&#10;MZRrmODZDVmKlF+U/+62cv86IoariB7YatfOjKLZwDJ5Xte8hBVtsOqCt9hcTr28mQbTyinqFerd&#10;rqZta9mvvBydFmTB8J/PYrWUPRYlnbhwM01Qt+QR8L1GkkBmE1FvWc8HQeLk4DQ8iiiHHPOlKFBe&#10;4B674oHYlR+G5/kjvv82G6uugeCn+y/bQCPQT1IeBXI2/fL3vqIthUBwuBqi8J1QqH4AjLYaxERM&#10;xSfEzKNd0VdNUKoRrK2x5oZAyINWdf0QXleBkLyK4YkMLxNbfJKmooDe8gy+jYrk61ZDVTN8K9za&#10;WfrZVCCmqOcI0AfwHyMNPH2fC8mfK3JYC5mEN1fPU5CZxTwcaHShJx6fBxFPugbhF5gwbaHyWnbF&#10;E4KWhOglWAchkUSgsHScd6DGvgUfBhRw45RCxJCwJewUqPeWDBiOPRfbYQngU8hfD+1qOW/57LOw&#10;WEq5kNCwROICFe4pqpBFT40SUGZQzNOzVQczaI4oxGGaMPVEsECMTvwPK67uf6f+qwwcdBMFyI6w&#10;hJXoph2pp/eOKDAsKgvTiDT3ta9FyT8jrHeNPRolB7OBbsSGJRI8UAr6kVY90QQ3y3CLoVcD4PuQ&#10;J/ypYJQAXb54NhW8KO5Jgoiifgr6zqN6WHhTaWDrPH0sroutPEaszWpL68q7v8ZuegshNOthC4Cw&#10;pHzObnJg0yKiAXLDVp/rXrL5RwwI6Wi7l6JGwftajWjlV6GxXGsPT0RQFsos29QoVdri/PU7yj3o&#10;35hoMD44cWvPsq0/11ck9OeQmLw2gY199fvfWJjobd8NxP4UHByQVsrTQj4Bw9CdYo9HhwDt29Fj&#10;D+xm6MRG9YIJs1Zfm9Dr23HJ1utw//mUVRt62B6D1o+e+BVwGsSLBu6/aMJq69grOsS9Cd+EbcUQ&#10;66rHclcDXWc/5ahNMwIPWQnOtOyJ+Bc1MQlkO7YutzfSBuw3cNZRp8b/AV5vx89HEcMQSX7woJ/4&#10;VgDWAMlTi14P1V6+Moo+/6h9BOhAwnZcEp6kWtfdtO0RtP2RUePXS4FqJHgWIZTpsWbYZRlvpqMj&#10;rF0rmuJDm+6pp3sgEtx5pJkT1OWLSQSlBrDNf4Yt9rzGg9ucWzvlpBvs83XAYXIFiEjk6WLNjOnx&#10;WHoCkG9S1CU+PDeJr2C+hCurPazynC9XL79JOzMgDh/Gto/oLc6dL9UkeI/urjdBsF87hkm1TwxE&#10;LYeh/cxHo1GMLzuT5ya7Qfxf4/em/kmdfw2NTlJcuKmE6zdVW+5xPjKUXJHEKe5Ljsa+0L108w89&#10;bCC7rI519yZa+tv/wqwJfRfEpBybjniJ44T3w3U2pkx5fffiLT1e/A91ZnoN4K77ISKPP8/7FtwM&#10;dHYt25ZjApabdHZdGl7vrkcRcvZN7SlztCsvs/DZHabIz6hTCmZCBi1u31HHSb7NMm5NPJTHFZCH&#10;f0KZBWJ6S5/qOtVD+kBMfptVi/P5JrbWwVQpMcJ3gPwpn/pskpLcn5sou73EQ65E8vxOynCg25Vo&#10;NI3i6XBNBqweWNq8aLWjyZOJhvozrBzY38Ma6U4eE+oVKxi4yjL+qTjpw0Mk4SWkBbcy0y8s56/G&#10;MjGZ4jCI7t4ICnEVZC4IIvmRiAG639ktt/z7IoSOXnTmcLJDygSNF3wQ+DKCHW7ARcgIICBUA3vM&#10;XSQcq8J0JnS0EqFnFoNv00gz7qg5j58dSHEu0DisT+L5G1YDQS2yxIojIXxdoOpSl83TI6g3AYAC&#10;wLSrlZeA8wXGQKJhgO0OcqRAU2l3w1KlmVIgXDBtHjQ+vb2CTrojO7PMfWa3nGF+ZhyaPZhLv+yS&#10;TD4110zBDHJltmsWNJLEh0MKcQvQ4xK2ZpTL0CDNc25j1zeuTWvlXiDNBo5ZiMyYQ0FHWOZav66P&#10;HXOvNPSWT3HMkRURBJHIUYY5IpUjexY6eVk9WeNsuhS3vwNCixUhE2aDjqhJ7Yh0mwoyGvdZJqQ3&#10;7MwIqjGCPccob6EuD8jAAgLUWx4FPJH1hZlSRxmegTQ9HQ8RPOhjMpQTW/4oAbgprAZ6XPnmWOJK&#10;qGMsnh7sFKSP6zEzgrrY8CROwOqpl6atBRX0RT25sdqF7vpL48dat5suf+KLPkSFBPB1UgSAfsnh&#10;3EXKcDpI/hkt+WbK/R8TRWdOUFeQ06NEO+WLKfwUo6Gog/IdN6XUQQ/AqkFwONx3lSZEV5lz+CGm&#10;/A78Aridgtq0QThUy6jmrHlvzyLQ8Q73VEZ0eQVpxNH3MvoQwyJNd4krGMFtUhEyRaG5KFXBSoqZ&#10;jTW34TL+Lt1O2+8ZvQ8EuRoT/x9rfg0OQL2VEfam9Ubqjtk/crfmcN/F9VXJM6NPdTdteYQwtX4L&#10;f0CP6NHyQ5nJv4PvoApmJtpnxtJOJBxK0pFz2I/J04mPLQ+PQQsYSIl4XuByLESWpXDxfLG618z/&#10;zJygmTH9yeHJBLjw+ea+3bexeMP+HXriOg95CqLedKDuQYqBL+P3CiZOu56BiORacjO+fRR92ML/&#10;xX/v4Z2q+yU9Vm1v3+OexGjAfLeEhdS76wVZE/hjmJ5YUk9nQgMekvwSmjLbcLQI+ig5qqgT82MF&#10;1MLUd7YlhyY70RQqHWJV8WU7iMwIzGH86IE673Q+pmdmcM5LqQgsmxCuTb4PYmhu5aUyD5Hu1WEQ&#10;LSwZ3fbGIkRm6AiPTHcvx4n7T01Ybyx6mJOlTivxjwMYtOXX9O66CUfUT0GwIPRPclcxZrBl0GfC&#10;FIXz2lnXdcXXj8O0rIGYQMioK4e10eENmPVs6dv1Vzjk/t6JAdXzMD1nh6AZxCgI6H3yI3zgkDDl&#10;jloz+DI/zco0mSKXcvdptx+bgviw21+35lXfCO5imAwDJcrxEj6lsUwEhUQg3T/vfUk1xaii9fju&#10;W6HJ/oziFV/Nl1qwqcBMK8plSmB5DbQTl59IM8AJnEsHyQngewqq4wCOklFIMerdsHEULgLlKtx0&#10;P9+1bMt3XGJu5wGivCvt5uM7dyOwf5Oj/aBagJcjKsDKhPRBaUN8MuT8KMbu0mMLcU7LcOXUwAqY&#10;D0gLIdoXoW6uiz/lfNkFy5tm7utsc2gudL654gDLnr6kIzVIDO/HVuSIGDi7HpNPiwzKTcRGJ1zn&#10;cniytb/993Gr8ZKOVKvkGoMjUKfFvzCD0XgAUpB490rEgz8mtqTVmJb9LJxGIpwE7sH8Hz9BK8IK&#10;k6YahiBKyOEqfLr8m4Bj3sx/qiBNVDg3+E5wzcfbjyAE9Urowd7iVDTKrDdybyvi2IVBXER2AP8/&#10;LCbYZ33cqQFii9GElKIKoiEGL9Y1McP+oPX4463kUPaPK/tGeJRexI+67++amFzIGOI9Sb/D0fHJ&#10;eV1Lt6zDB84jFwiHkmQ5yeU86pTwBTDOWCXgqovjdKf+6H4YxW36W05avsKLEPLj2v0QcNvQuXjz&#10;WU/OE5kLhEML6OISC8HdVJFgGCCGlCJQyz/Kut8yMSmHcTuBfz4CYx+1zsYboBpe5Vweow4rGAfG&#10;J9OFSlAXO5dwwHHmp64H6DyeSgbwnZgUPfhuloRsxf2UVgNpaOTD/X+TqEOdRtk7pQAAAABJRU5E&#10;rkJggg==&#10;" id="image1" x="10" y="10" />
+</svg>
diff --git a/tests/manual/svg/data/image/data.png b/tests/manual/svg/data/image/data.png
new file mode 100644
index 0000000000..7b660be3de
--- /dev/null
+++ b/tests/manual/svg/data/image/data.png
Binary files differ
diff --git a/tests/manual/svg/data/image/qtlogo.png b/tests/manual/svg/data/image/qtlogo.png
new file mode 100644
index 0000000000..7b660be3de
--- /dev/null
+++ b/tests/manual/svg/data/image/qtlogo.png
Binary files differ
diff --git a/tests/manual/svg/data/painting/fill_color.svg b/tests/manual/svg/data/painting/fill_color.svg
new file mode 100644
index 0000000000..7772839369
--- /dev/null
+++ b/tests/manual/svg/data/painting/fill_color.svg
@@ -0,0 +1,7 @@
+<svg viewBox="0 0 400 1000" xmlns="http://www.w3.org/2000/svg">
+ <g fill-opacity="0.3">
+ <rect width="300" height="300" fill="red"/>
+ </g>
+ <rect transform="translate(0, 350)" width="300" height="300" fill="red"/>
+ <rect transform="translate(0, 700)" width="300" height="300" fill="transparent" stroke="yellow" stroke-width="5"/>
+</svg>
diff --git a/tests/manual/svg/data/painting/fill_gradient.svg b/tests/manual/svg/data/painting/fill_gradient.svg
new file mode 100644
index 0000000000..5fefb40442
--- /dev/null
+++ b/tests/manual/svg/data/painting/fill_gradient.svg
@@ -0,0 +1,15 @@
+<svg viewBox="0 0 200 400" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <linearGradient id="grad1" x1="0%" x2="100%" y1="0%" y2="0%">
+ <stop offset="0%" stop-color="green" stop-opacity="0.8"/>
+ <stop offset="100%" stop-color="red" stop-opacity="0.8"/>
+ </linearGradient>
+ <linearGradient id="grad2" x1="0%" x2="100%" y1="0%" y2="0%">
+ <stop offset="0%" stop-color="green"/>
+ <stop offset="100%" stop-color="red"/>
+ </linearGradient>
+ </defs>
+ <ellipse cx="100" cy="70" rx="85" ry="55" fill="url(#grad1)"/>
+ <ellipse cx="100" cy="200" rx="85" ry="55" fill="url(#grad2)" fill-opacity="0.8"/>
+ <ellipse cx="100" cy="330" rx="85" ry="55" fill="url(#grad1)" fill-opacity="0.5"/>
+</svg>
diff --git a/tests/manual/svg/data/painting/fill_stroke.svg b/tests/manual/svg/data/painting/fill_stroke.svg
new file mode 100644
index 0000000000..8069faf516
--- /dev/null
+++ b/tests/manual/svg/data/painting/fill_stroke.svg
@@ -0,0 +1,3 @@
+<svg viewBox="0 0 800 800" xmlns="http://www.w3.org/2000/svg">
+ <rect transform="translate(50, 50)" width="400" height="400" fill="green" stroke="black" fill-opacity="0.5" stroke-opacity="0.2" stroke-width="20"/>
+</svg> \ No newline at end of file
diff --git a/tests/manual/svg/data/painting/stroke_color.svg b/tests/manual/svg/data/painting/stroke_color.svg
new file mode 100644
index 0000000000..d77755e1fd
--- /dev/null
+++ b/tests/manual/svg/data/painting/stroke_color.svg
@@ -0,0 +1,7 @@
+<svg viewBox="0 0 400 1000" xmlns="http://www.w3.org/2000/svg">
+ <g stroke-opacity="0.3">
+ <rect transform="translate(25, 25)" width="300" height="300" stroke="red" stroke-width="10"/>
+ </g>
+ <rect transform="translate(25, 350)" width="300" height="300" stroke="red" stroke-width="10"/>
+ <rect transform="translate(25, 700)" width="300" height="300" fill="yellow" stroke="transparent"/>
+</svg>
diff --git a/tests/manual/svg/data/styling/stroking_capStyle_shapes_1.svg b/tests/manual/svg/data/styling/stroking_capStyle_shapes_1.svg
new file mode 100644
index 0000000000..9448fcb527
--- /dev/null
+++ b/tests/manual/svg/data/styling/stroking_capStyle_shapes_1.svg
@@ -0,0 +1,11 @@
+<svg width="900" height="600" viewBox="0 0 90 60" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <g id="capTest">
+ <polyline points="10,10 80,10" fill="none" stroke-width="8" stroke="black"/>
+ <polyline points="10,10 80,10" fill="none" stroke-width="0.5" stroke-linecap="butt" stroke="yellow"/>
+ </g>
+ </defs>
+ <use href="#capTest" stroke-linecap="butt"/>
+ <use href="#capTest" y="20" stroke-linecap="round"/>
+ <use href="#capTest" y=" 40" stroke-linecap="square"/>
+</svg>
diff --git a/tests/manual/svg/data/styling/stroking_capStyle_shapes_2.svg b/tests/manual/svg/data/styling/stroking_capStyle_shapes_2.svg
new file mode 100644
index 0000000000..1d52d2d247
--- /dev/null
+++ b/tests/manual/svg/data/styling/stroking_capStyle_shapes_2.svg
@@ -0,0 +1,12 @@
+<svg width="900" height="300" viewBox="0 0 130 40" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <polyline id="letterz" points="10,10 40,10 10,30 40,30" fill="none" stroke-width="2.5" stroke="black"/>
+ <polyline id="highlight" points="10,10 40,10 10,30 40,30" fill="none" stroke-width="0.1" stroke="yellow"/>
+ </defs>
+ <use href="#letterz" stroke-linecap="butt"/>
+ <use href="#highlight"/>
+ <use href="#letterz" x="40" stroke-linecap="round"/>
+ <use href="#highlight" x="40"/>
+ <use href="#letterz" x=" 80" stroke-linecap="square"/>
+ <use href="#highlight" x="80"/>
+</svg>
diff --git a/tests/manual/svg/data/styling/stroking_dash.svg b/tests/manual/svg/data/styling/stroking_dash.svg
new file mode 100644
index 0000000000..5fdefa4cce
--- /dev/null
+++ b/tests/manual/svg/data/styling/stroking_dash.svg
@@ -0,0 +1,10 @@
+<svg width="460" height="200" viewBox="0 0 30 13" xmlns="http://www.w3.org/2000/svg">
+ <line x1="0" y1="1" x2="30" y2="1" stroke="black" stroke-dashoffset="2"/>
+ <line x1="0" y1="3" x2="30" y2="3" stroke="black" stroke-dasharray="4 2"/>
+<!--positive dashoffset pulls the dashes-->
+ <line x1="0" y1="5" x2="30" y2="5" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="2"/>
+ <line x1="0" y1="7" x2="30" y2="7" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="4"/>
+ <!--negative dashoffset pushs the dashes-->
+ <line x1="0" y1="9" x2="30" y2="9" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="-2"/>
+ <line x1="0" y1="11" x2="30" y2="11" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="-4"/>
+</svg>
diff --git a/tests/manual/svg/data/styling/stroking_joinStyle_shapes_1.svg b/tests/manual/svg/data/styling/stroking_joinStyle_shapes_1.svg
new file mode 100644
index 0000000000..1da775a97e
--- /dev/null
+++ b/tests/manual/svg/data/styling/stroking_joinStyle_shapes_1.svg
@@ -0,0 +1,12 @@
+<svg width="600" height="200" viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <g id="capTest">
+ <polyline points="1,8 1,2 7,2" fill="none" stroke-width="1" stroke="black"/>
+ <polyline points="1,8 1,2 7,2" fill="none" stroke-width="0.03" stroke="yellow"/>
+ </g>
+ </defs>
+
+ <use href="#capTest" x="1" stroke-linejoin="bevel"/>
+ <use href="#capTest" x="11" stroke-linejoin="round"/>
+ <use href="#capTest" x="21" stroke-linejoin="miter"/>
+</svg>
diff --git a/tests/manual/svg/data/text/1.svg b/tests/manual/svg/data/text/1.svg
new file mode 100644
index 0000000000..d6fd4905f0
--- /dev/null
+++ b/tests/manual/svg/data/text/1.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="210mm"
+ height="297mm"
+ viewBox="0 0 210 297"
+ version="1.1"
+ id="svg1"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1">
+ <linearGradient
+ id="linearGradient1">
+ <stop
+ style="stop-color:#ff0000;stop-opacity:1;"
+ offset="0"
+ id="stop1" />
+ <stop
+ style="stop-color:#00ff00;stop-opacity:1;"
+ offset="1"
+ id="stop2" />
+ </linearGradient>
+ <rect
+ x="150.02508"
+ y="181.45326"
+ width="412.12424"
+ height="148.24613"
+ id="rect1" />
+ <linearGradient
+ xlink:href="#linearGradient1"
+ id="linearGradient2"
+ x1="159.13997"
+ y1="232.67492"
+ x2="543.63868"
+ y2="232.67492"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <g id="layer1">
+ <text xml:space="preserve" transform="scale(0.26458333)" id="text1" x="150.02539" y="266.60392" style="font-size:93.3333px;fill:url(#linearGradient2)"><tspan id="tspan3">FOO</tspan><tspan style="font-weight:bold;font-family:Dubai;-inkscape-font-specification:'Dubai Bold'">BAR</tspan></text>
+ </g>
+</svg>
diff --git a/tests/manual/svg/data/text/2.svg b/tests/manual/svg/data/text/2.svg
new file mode 100644
index 0000000000..bf438e7aa0
--- /dev/null
+++ b/tests/manual/svg/data/text/2.svg
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="210mm"
+ height="297mm"
+ viewBox="0 0 210 297"
+ version="1.1"
+ id="svg1"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1">
+ <linearGradient
+ id="linearGradient1">
+ <stop
+ style="stop-color:#ff0000;stop-opacity:1;"
+ offset="0"
+ id="stop1" />
+ <stop
+ style="stop-color:#00ff00;stop-opacity:1;"
+ offset="1"
+ id="stop2" />
+ </linearGradient>
+ <rect
+ x="150.02508"
+ y="181.45326"
+ width="412.12424"
+ height="148.24613"
+ id="rect1" />
+ <linearGradient
+ xlink:href="#linearGradient1"
+ id="linearGradient2"
+ x1="159.13997"
+ y1="232.67492"
+ x2="543.63868"
+ y2="232.67492"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <g id="layer1">
+ <text xml:space="preserve" transform="scale(0.26458333)" id="text1" x="150.02539" y="266.60392" style="font-size:93.3333px;font-family:Cali;white-space:pre;shape-inside:url(#rect1);display:inline;opacity:0.557377;fill:url(#linearGradient2);stroke-width:1.00157">
+ <tspan>FOO</tspan><tspan style="font-weight:bold;font-family:Dubai;-inkscape-font-specification:'Dubai Bold'">BAR</tspan></text>
+ </g>
+</svg>
diff --git a/tests/manual/svg/data/text/3.svg b/tests/manual/svg/data/text/3.svg
new file mode 100644
index 0000000000..322cfff821
--- /dev/null
+++ b/tests/manual/svg/data/text/3.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="210mm"
+ height="297mm"
+ viewBox="0 0 210 297"
+ version="1.1"
+ id="svg1"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1">
+ </defs>
+ <g id="layer1">
+ <text xml:space="preserve" transform="scale(0.26458333)" id="text1" x="0" y="100" style="font-size:93.3333px;white-space:pre;shape-inside:url(#rect1);display:inline;opacity:0.557377;fill:#ff0000ff;stroke-width:1.00157">
+ <tspan>FOO</tspan><tspan style="font-weight:bold;font-family:Dubai">BAR</tspan></text>
+ </g>
+</svg>
diff --git a/tests/manual/svg/data/text/4.svg b/tests/manual/svg/data/text/4.svg
new file mode 100644
index 0000000000..d7146dedab
--- /dev/null
+++ b/tests/manual/svg/data/text/4.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="210mm"
+ height="297mm"
+ viewBox="0 0 210 297"
+ version="1.1"
+ id="svg1"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1">
+ </defs>
+ <g id="layer1">
+ <text xml:space="preserve" transform="scale(0.26458333)" id="text1" x="0" y="100" style="font-size:93.3333px">
+ <tspan style="fill: red">FO</tspan><tspan style="stroke: red">O</tspan><tspan style="font-weight:bold;font-family:Dubai">BAR</tspan></text>
+ </g>
+</svg>
diff --git a/tests/manual/svg/data/text/5.svg b/tests/manual/svg/data/text/5.svg
new file mode 100644
index 0000000000..a63d53e526
--- /dev/null
+++ b/tests/manual/svg/data/text/5.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="210mm"
+ height="297mm"
+ viewBox="0 0 210 297"
+ version="1.1"
+ id="svg1"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1">
+ <linearGradient
+ id="linearGradient1">
+ <stop
+ style="stop-color:#ff0000;stop-opacity:1;"
+ offset="0"
+ id="stop1" />
+ <stop
+ style="stop-color:#00ff00;stop-opacity:1;"
+ offset="1"
+ id="stop2" />
+ </linearGradient>
+ <rect
+ x="150.02508"
+ y="181.45326"
+ width="412.12424"
+ height="148.24613"
+ id="rect1" />
+ <linearGradient
+ xlink:href="#linearGradient1"
+ id="linearGradient2"
+ x1="159.13997"
+ y1="232.67492"
+ x2="543.63868"
+ y2="232.67492"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <g id="layer1">
+ <text xml:space="preserve" transform="scale(0.26458333)" id="text1" x="150.02539" y="266.60392" style="font-size:93.3333px"><tspan id="tspan3" style="stroke:red">FOO</tspan><tspan style="fill:url(#linearGradient2);font-weight:bold;font-family:Dubai;">BAR</tspan></text>
+ </g>
+</svg>
diff --git a/tests/manual/svg/main.cpp b/tests/manual/svg/main.cpp
new file mode 100644
index 0000000000..d86d7a3793
--- /dev/null
+++ b/tests/manual/svg/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+ return a.exec();
+}
diff --git a/tests/manual/svg/mainwindow.cpp b/tests/manual/svg/mainwindow.cpp
new file mode 100644
index 0000000000..403202514b
--- /dev/null
+++ b/tests/manual/svg/mainwindow.cpp
@@ -0,0 +1,142 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "mainwindow.h"
+#include "./ui_mainwindow.h"
+
+#include "svgmanager.h"
+#include "svgpainter.h"
+
+#include <QFileDialog>
+#include <QSettings>
+#include <QQuickWidget>
+#include <QQmlEngine>
+#include <QSlider>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+ , ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+
+ connect(ui->tbSelectDir, &QToolButton::clicked, this, &MainWindow::selectDirectory);
+ connect(ui->tbNext, &QToolButton::clicked, this, &MainWindow::next);
+ connect(ui->tbPrev, &QToolButton::clicked, this, &MainWindow::previous);
+ m_manager = new SvgManager(this);
+
+ m_imageLabel = new QLabel;
+ m_imageLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ ui->saImage->setWidget(m_imageLabel);
+ ui->saImage->setBackgroundRole(QPalette::Base);
+
+ m_svgPainter = new SvgPainter;
+ ui->saSvgPainter->setWidget(m_svgPainter);
+ ui->saSvgPainter->setBackgroundRole(QPalette::Base);
+
+ m_svgImageWidget = new QQuickWidget;
+ m_svgImageWidget->setSource(QUrl(QStringLiteral("qrc:/SvgImageTest/SvgImage.qml")));
+ m_svgImageWidget->setResizeMode(QQuickWidget::SizeViewToRootObject);
+ m_svgImageWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+ ui->saSvgImage->setWidget(m_svgImageWidget);
+
+ m_qmlGeneratorWidget = new QQuickWidget;
+ m_qmlGeneratorWidget->setSource(QUrl(QStringLiteral("qrc:/SvgImageTest/QmlGenerator.qml")));
+ m_qmlGeneratorWidget->setResizeMode(QQuickWidget::SizeViewToRootObject);
+ m_qmlGeneratorWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+ ui->saQmlGenerator->setWidget(m_qmlGeneratorWidget);
+
+ connect(m_manager, &SvgManager::currentSourceChanged, this, &MainWindow::updateSources);
+
+ m_settings = new QSettings(QStringLiteral("org.qtproject"), QStringLiteral("svg-test"), this);
+
+ connect(ui->cbCurrentDir, &QComboBox::currentTextChanged, this, &MainWindow::loadDirectory);
+ QStringList list = m_settings->value(QStringLiteral("directories")).toString().split(QLatin1Char(','));
+ setDirList(list);
+
+ connect(ui->hsScale, &QAbstractSlider::valueChanged, m_manager, &SvgManager::setScale);
+ connect(ui->hsScale, &QAbstractSlider::valueChanged, m_svgPainter, &SvgPainter::setScale);
+ connect(ui->hsScale, &QAbstractSlider::valueChanged, this, &MainWindow::setScale);
+ int scale = m_settings->value(QStringLiteral("scale"), 10).toInt();
+ ui->hsScale->setValue(scale);
+}
+
+void MainWindow::loadDirectory(const QString &newDir)
+{
+ m_manager->setCurrentDirectory(newDir);
+}
+
+void MainWindow::setDirList(const QStringList &list)
+{
+ ui->cbCurrentDir->clear();
+ for (QString dirName : list)
+ ui->cbCurrentDir->addItem(dirName);
+
+ m_settings->setValue(QStringLiteral("directories"), list.join(QLatin1Char(',')));
+}
+
+void MainWindow::setScale(const int scale)
+{
+ m_settings->setValue(QStringLiteral("scale"), scale);
+}
+
+void MainWindow::updateCurrentDir(const QString &s)
+{
+ QStringList list;
+ for (int i = 0; i < ui->cbCurrentDir->count(); ++i) {
+ if (ui->cbCurrentDir->itemText(i) != s)
+ list.append(ui->cbCurrentDir->itemText(i));
+ }
+
+ list.prepend(s);
+
+ while (list.size() > 10)
+ list.removeLast();
+
+ setDirList(list);
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+void MainWindow::updateSources()
+{
+ m_svgPainter->setSource(m_manager->currentSource());
+
+ QFileInfo info(m_manager->currentSource().toLocalFile());
+ ui->lCurrentFileName->setText(info.baseName());
+
+ QFileInfo pngInfo(info.absolutePath() + QLatin1Char('/') + info.baseName() + QStringLiteral(".png"));
+ if (pngInfo.exists()) {
+ ui->lRefPng->setVisible(true);
+ ui->saImage->setVisible(true);
+ QPixmap pixmap = QPixmap::fromImage(QImage(pngInfo.absoluteFilePath()));
+ m_imageLabel->setPixmap(pixmap);
+ } else {
+ ui->lRefPng->setVisible(false);
+ ui->saImage->setVisible(false);
+ m_imageLabel->clear();
+ }
+}
+
+void MainWindow::selectDirectory()
+{
+ QString s = QFileDialog::getExistingDirectory(this, tr("Select directory"), m_manager->currentDirectory());
+ if (!s.isEmpty()) {
+ updateCurrentDir(s);
+ }
+}
+
+void MainWindow::next()
+{
+ if (m_manager->currentIndex() + 1 < m_manager->sourceCount())
+ m_manager->setCurrentIndex(m_manager->currentIndex() + 1);
+}
+
+void MainWindow::previous()
+{
+ if (m_manager->currentIndex() > 0)
+ m_manager->setCurrentIndex(m_manager->currentIndex() - 1);
+
+}
diff --git a/tests/manual/svg/mainwindow.h b/tests/manual/svg/mainwindow.h
new file mode 100644
index 0000000000..81e4bea113
--- /dev/null
+++ b/tests/manual/svg/mainwindow.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+QT_END_NAMESPACE
+
+class SvgPainter;
+class SvgManager;
+class QSettings;
+class QLabel;
+class QQuickWidget;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+private slots:
+ void updateSources();
+ void selectDirectory();
+ void next();
+ void previous();
+ void loadDirectory(const QString &newDir);
+
+private:
+ void updateCurrentDir(const QString &newDir);
+ void setDirList(const QStringList &list);
+ void setScale(const int scale);
+
+ Ui::MainWindow *ui;
+ SvgManager *m_manager = nullptr;
+ QSettings *m_settings = nullptr;
+ QLabel *m_imageLabel = nullptr;
+ SvgPainter *m_svgPainter = nullptr;
+ QQuickWidget *m_svgImageWidget = nullptr;
+ QQuickWidget *m_qmlGeneratorWidget = nullptr;
+};
+#endif // MAINWINDOW_H
diff --git a/tests/manual/svg/mainwindow.ui b/tests/manual/svg/mainwindow.ui
new file mode 100644
index 0000000000..ee75f1f49b
--- /dev/null
+++ b/tests/manual/svg/mainwindow.ui
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1107</width>
+ <height>1003</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="vaRefPng">
+ <item>
+ <widget class="QLabel" name="lRefPng">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Reference PNG</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollArea" name="saImage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Reference png</string>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>263</width>
+ <height>789</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>QSvgRenderer</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollArea" name="saSvgPainter">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Reference QPainter</string>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents_4">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>264</width>
+ <height>789</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>VectorImage</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollArea" name="saSvgImage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>QML generator</string>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents_2">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>263</width>
+ <height>789</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Qml Generator</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollArea" name="saQmlGenerator">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>VectorImage</string>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents_3">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>263</width>
+ <height>789</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Directory</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QComboBox" name="cbCurrentDir">
+ <property name="maxCount">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tbSelectDir">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QToolButton" name="tbPrev">
+ <property name="text">
+ <string>⬅ï¸</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lCurrentFileName">
+ <property name="text">
+ <string>Filename</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tbNext">
+ <property name="font">
+ <font>
+ <family>Segoe UI Emoji</family>
+ </font>
+ </property>
+ <property name="text">
+ <string>âž¡ï¸</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSlider" name="hsScale">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1107</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/svg/svgmanager.cpp b/tests/manual/svg/svgmanager.cpp
new file mode 100644
index 0000000000..b8d49413a5
--- /dev/null
+++ b/tests/manual/svg/svgmanager.cpp
@@ -0,0 +1,96 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "svgmanager.h"
+
+#include <QDir>
+#include <QtQuickVectorImageGenerator/private/qquickqmlgenerator_p.h>
+#include <QTemporaryFile>
+
+SvgManager *SvgManager::g_manager = nullptr;
+
+SvgManager::SvgManager(QObject *parent)
+ : QObject(parent)
+{
+ Q_ASSERT(g_manager == nullptr);
+ g_manager = this;
+ connect(this, &SvgManager::currentIndexChanged, this, &SvgManager::currentSourceChanged, Qt::QueuedConnection);
+}
+
+SvgManager::~SvgManager()
+{
+ Q_ASSERT(g_manager == this);
+ g_manager = nullptr;
+}
+
+void SvgManager::setCurrentIndex(int newCurrentIndex)
+{
+ if (m_currentIndex == newCurrentIndex)
+ return;
+ m_currentIndex = newCurrentIndex;
+ emit currentIndexChanged();
+}
+
+QList<QUrl> SvgManager::sources() const
+{
+ return m_sources;
+}
+
+QString SvgManager::currentDirectory() const
+{
+ return m_currentDirectory;
+}
+
+void SvgManager::setCurrentDirectory(const QString &newCurrentDirectory)
+{
+ if (m_currentDirectory == newCurrentDirectory)
+ return;
+ m_currentDirectory = newCurrentDirectory;
+ emit currentDirectoryChanged();
+
+ m_sources.clear();
+ if (!m_currentDirectory.isEmpty()) {
+ QDir dir(m_currentDirectory);
+ QList<QFileInfo> infos = dir.entryInfoList(QStringList() << QStringLiteral("*.svg"));
+
+ for (const QFileInfo &info : infos)
+ m_sources.append(QUrl::fromLocalFile(info.absoluteFilePath()));
+ }
+ m_currentIndex = m_sources.isEmpty() ? -1 : 0;
+ emit sourcesChanged();
+ emit currentIndexChanged();
+}
+
+QString SvgManager::qmlSource() const
+{
+ QTemporaryFile tempFile;
+ if (tempFile.open()) {
+ QString name = tempFile.fileName();
+ {
+ QQuickQmlGenerator generator(currentSource().toLocalFile(), QQuickVectorImageGenerator::CurveRenderer, tempFile.fileName());
+ generator.setCommentString(QStringLiteral("Generated"));
+ generator.generate();
+ }
+ tempFile.close();
+
+ QFile file(name);
+ if (file.open(QIODevice::ReadOnly))
+ return QString::fromUtf8(file.readAll());
+
+ }
+
+ return QStringLiteral("import QtQuick\nRectangle { width: 100; height: 100; color: \"red\" }");;
+}
+
+qreal SvgManager::scale() const
+{
+ return m_scale;
+}
+
+void SvgManager::setScale(int newScale)
+{
+ if (qFuzzyCompare(m_scale, newScale))
+ return;
+ m_scale = newScale;
+ emit scaleChanged();
+}
diff --git a/tests/manual/svg/svgmanager.h b/tests/manual/svg/svgmanager.h
new file mode 100644
index 0000000000..bdfb20242d
--- /dev/null
+++ b/tests/manual/svg/svgmanager.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SVGMANAGER_H
+#define SVGMANAGER_H
+
+#include <QObject>
+#include <QUrl>
+#include <QQmlEngine>
+
+class SvgManager : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+ Q_PROPERTY(QUrl currentSource READ currentSource NOTIFY currentSourceChanged)
+ Q_PROPERTY(QString qmlSource READ qmlSource NOTIFY currentSourceChanged)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+ Q_PROPERTY(int sourceCount READ sourceCount NOTIFY sourcesChanged)
+ Q_PROPERTY(QString currentDirectory READ currentDirectory WRITE setCurrentDirectory NOTIFY currentDirectoryChanged)
+ Q_PROPERTY(QList<QUrl> sources READ sources NOTIFY sourcesChanged)
+ Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged)
+public:
+ SvgManager(QObject *parent);
+ ~SvgManager() override;
+
+ static SvgManager *create(QQmlEngine *, QJSEngine *)
+ {
+ return g_manager;
+ }
+
+ QUrl currentSource() const
+ {
+ if (m_currentIndex < 0)
+ return QUrl{};
+
+ return m_sources.at(m_currentIndex);
+ }
+
+ int currentIndex() const
+ {
+ return m_currentIndex;
+ }
+ void setCurrentIndex(int newCurrentIndex);
+
+ int sourceCount() const
+ {
+ return m_sources.size();
+ }
+
+ QList<QUrl> sources() const;
+
+ QString currentDirectory() const;
+ void setCurrentDirectory(const QString &newCurrentDirectory);
+
+ QString qmlSource() const;
+
+ qreal scale() const;
+
+public slots:
+ void setScale(int newScale);
+
+signals:
+ void currentSourceChanged();
+ void currentIndexChanged();
+
+ void sourcesChanged();
+
+ void currentDirectoryChanged();
+
+ void scaleChanged();
+
+private:
+ static SvgManager *g_manager;
+ int m_currentIndex = -1;
+ QList<QUrl> m_sources;
+ QString m_currentDirectory;
+ QString m_qmlSource;
+ qreal m_scale = 10.0;
+};
+
+#endif // SVGMANAGER_H
diff --git a/tests/manual/svg/svgpainter.cpp b/tests/manual/svg/svgpainter.cpp
new file mode 100644
index 0000000000..e040c1d087
--- /dev/null
+++ b/tests/manual/svg/svgpainter.cpp
@@ -0,0 +1,81 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "svgpainter.h"
+#include "svgmanager.h"
+
+#include <QtSvg>
+
+SvgPainter::SvgPainter(QWidget *parent)
+#ifdef SVGWIDGET
+ : QSvgWidget{parent}
+#else
+ : QWidget{parent}
+#endif
+ , m_scale(10)
+{
+#ifndef SVGWIDGET
+ connect(this, SIGNAL(sourceChanged()), this, SLOT(update()));
+#endif
+
+ connect(this, SIGNAL(scaleChanged()), this, SLOT(update()));
+}
+
+QUrl SvgPainter::source() const
+{
+ return m_source;
+}
+
+void SvgPainter::setSource(const QUrl &newSource)
+{
+ if (m_source == newSource)
+ return;
+ m_source = newSource;
+#ifdef SVGWIDGET
+ load(m_source.toLocalFile());
+#endif
+ emit sourceChanged();
+}
+
+qreal SvgPainter::scale() const
+{
+ return m_scale;
+}
+
+void SvgPainter::setScale(const qreal scale)
+{
+ if (m_scale == scale)
+ return;
+ m_scale = scale;
+ emit scaleChanged();
+}
+
+#ifndef SVGWIDGET
+QSize SvgPainter::sizeHint() const
+{
+ return !m_source.isEmpty() ? m_size * m_scale / 10.0 : QSize(1, 1);
+}
+#endif
+
+void SvgPainter::paintEvent(QPaintEvent *event)
+{
+#ifndef SVGWIDGET
+ Q_UNUSED(event)
+ if (!m_source.isEmpty()) {
+ QPainter p(this);
+ p.fillRect(rect(), Qt::white);
+ QSvgRenderer renderer(m_source.toLocalFile());
+
+ renderer.setAspectRatioMode(Qt::KeepAspectRatio);
+ renderer.render(&p);
+ m_size = renderer.defaultSize();
+ setFixedSize(m_size * m_scale / 10.0);
+ }
+#else
+ m_size = renderer()->defaultSize();
+ setFixedSize(m_size * m_scale / 10.0);
+ QSvgWidget::paintEvent(event);
+#endif
+
+}
+
diff --git a/tests/manual/svg/svgpainter.h b/tests/manual/svg/svgpainter.h
new file mode 100644
index 0000000000..5e8a641bd7
--- /dev/null
+++ b/tests/manual/svg/svgpainter.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SVGPAINTER_H
+#define SVGPAINTER_H
+
+#include <QWidget>
+#include <QUrl>
+#include <QtSvgWidgets/QSvgWidget>
+
+// #define SVGWIDGET
+
+#ifdef SVGWIDGET
+class SvgPainter : public QSvgWidget
+#else
+class SvgPainter : public QWidget
+#endif
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged)
+public:
+ explicit SvgPainter(QWidget *parent = nullptr);
+
+ QUrl source() const;
+ void setSource(const QUrl &newSource);
+
+ qreal scale() const;
+ void setScale(const qreal scale);
+
+signals:
+ void sourceChanged();
+ void scaleChanged();
+
+
+protected:
+#ifndef SVGWIDGET
+ QSize sizeHint() const override;
+#endif
+ void paintEvent(QPaintEvent *) override;
+
+
+private:
+ QUrl m_source;
+ QSize m_size;
+ qreal m_scale;
+};
+
+#endif // SVGPAINTER_H
diff --git a/tests/manual/syntaxhighlighter/app.qml b/tests/manual/syntaxhighlighter/app.qml
index c989d31813..e5d8274e82 100644
--- a/tests/manual/syntaxhighlighter/app.qml
+++ b/tests/manual/syntaxhighlighter/app.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -9,7 +9,12 @@ TextEdit {
id: textEdit
width: 420; height: 200
text: '<p>Try the different <span style="text-decoration: underline overline; text-decoration-color: green;">highlight</span> styles.' +
- "The keyword is 'char'.</p><code>char * test;</code>"
+ "The keyword is 'char'.</p><code>char * test;</code>" + '<ol>
+<li style="color:#f32727">Red bullet</li>
+<li style="color:#f327ca">Pink bullet</li>
+<li>black bullet</li>
+<li style="color:#f32727">Red bullet</li></ol><p>
+Some non-red text<p>'
textFormat: TextEdit.RichText
leftPadding: 6; topPadding: 6
diff --git a/tests/manual/syntaxhighlighter/documenthighlighter.cpp b/tests/manual/syntaxhighlighter/documenthighlighter.cpp
index 115b02e3da..934f1f3084 100644
--- a/tests/manual/syntaxhighlighter/documenthighlighter.cpp
+++ b/tests/manual/syntaxhighlighter/documenthighlighter.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "documenthighlighter.h"
#include <QtQuick/QQuickTextDocument>
diff --git a/tests/manual/syntaxhighlighter/documenthighlighter.h b/tests/manual/syntaxhighlighter/documenthighlighter.h
index 258cfe7486..20c6000801 100644
--- a/tests/manual/syntaxhighlighter/documenthighlighter.h
+++ b/tests/manual/syntaxhighlighter/documenthighlighter.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef DOCUMENTHIGHLIGHTER_H
#define DOCUMENTHIGHLIGHTER_H
diff --git a/tests/manual/syntaxhighlighter/main.cpp b/tests/manual/syntaxhighlighter/main.cpp
index a3bfcf7a75..61bd768346 100644
--- a/tests/manual/syntaxhighlighter/main.cpp
+++ b/tests/manual/syntaxhighlighter/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuick/QQuickView>
#include <QGuiApplication>
diff --git a/tests/manual/tableview/abstracttablemodel/Button.qml b/tests/manual/tableview/abstracttablemodel/Button.qml
index b3ca5dd260..d49f2102a2 100644
--- a/tests/manual/tableview/abstracttablemodel/Button.qml
+++ b/tests/manual/tableview/abstracttablemodel/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
diff --git a/tests/manual/tableview/abstracttablemodel/main.cpp b/tests/manual/tableview/abstracttablemodel/main.cpp
index a765f66272..6b7d38c9ec 100644
--- a/tests/manual/tableview/abstracttablemodel/main.cpp
+++ b/tests/manual/tableview/abstracttablemodel/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/tableview/abstracttablemodel/main.qml b/tests/manual/tableview/abstracttablemodel/main.qml
index 316d04131a..5578e86dba 100644
--- a/tests/manual/tableview/abstracttablemodel/main.qml
+++ b/tests/manual/tableview/abstracttablemodel/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -12,7 +12,7 @@ import Qt.labs.qmlmodels
ApplicationWindow {
id: window
width: 1000
- height: 800
+ height: 600
visible: true
readonly property var currentIndex: tableView.selectionModel.currentIndex
@@ -33,11 +33,14 @@ ApplicationWindow {
Layout.rightMargin: menu.menuMargin
Layout.leftMargin: menu.menuMargin
Layout.topMargin: menu.menuMargin
+ Layout.fillWidth: false
+ Layout.fillHeight: false
ColumnLayout {
CheckBox {
text: "Use Syncview"
checkable: true
checked: true
+ Layout.fillWidth: false
onCheckedChanged: {
if (checked) {
leftHeader.syncView = tableView
@@ -52,7 +55,8 @@ ApplicationWindow {
CheckBox {
id: flickingMode
checkable: true
- checked: true
+ checked: false
+ Layout.fillWidth: false
text: "Interactive"
}
@@ -60,6 +64,7 @@ ApplicationWindow {
id: indexNavigation
checkable: true
checked: true
+ Layout.fillWidth: false
text: "Enable navigation"
}
@@ -67,6 +72,7 @@ ApplicationWindow {
id: resizableRowsEnabled
checkable: true
checked: false
+ Layout.fillWidth: false
text: "Resizable rows"
}
@@ -74,6 +80,7 @@ ApplicationWindow {
id: resizableColumnsEnabled
checkable: true
checked: false
+ Layout.fillWidth: false
text: "Resizable columns"
}
@@ -81,6 +88,7 @@ ApplicationWindow {
id: enableAnimation
checkable: true
checked: true
+ Layout.fillWidth: false
text: "Enable animation"
}
@@ -88,6 +96,7 @@ ApplicationWindow {
id: drawText
checkable: true
checked: true
+ Layout.fillWidth: false
text: "Draw text"
}
@@ -95,6 +104,7 @@ ApplicationWindow {
id: useRandomColor
checkable: true
checked: false
+ Layout.fillWidth: false
text: "Use colors"
}
@@ -103,6 +113,7 @@ ApplicationWindow {
checkable: true
checked: false
text: "Use large cells"
+ Layout.fillWidth: false
onCheckedChanged: Qt.callLater(tableView.forceLayout)
}
@@ -110,6 +121,7 @@ ApplicationWindow {
id: useSubRect
checkable: true
checked: false
+ Layout.fillWidth: false
text: "Use subRect"
}
@@ -117,6 +129,7 @@ ApplicationWindow {
id: useOffset
checkable: true
checked: false
+ Layout.fillWidth: false
text: "Use offset"
}
@@ -124,6 +137,7 @@ ApplicationWindow {
id: highlightCurrentRow
checkable: true
checked: false
+ Layout.fillWidth: false
text: "Highlight row/col"
}
@@ -132,17 +146,35 @@ ApplicationWindow {
checkable: true
checked: tableView.interactive
text: "Show selection handles"
+ Layout.fillWidth: false
}
ComboBox {
id: selectionCombo
+ currentIndex: 1
model: [
"SelectionDisabled",
"SelectCells",
"SelectRows",
"SelectColumns",
]
+ Layout.fillWidth: false
}
+
+ ComboBox {
+ id: selectionModeTv
+ textRole: "text"
+ valueRole: "value"
+ implicitContentWidthPolicy: ComboBox.WidestText
+ currentIndex: 2
+ model: [
+ { text: "SingleSelection", value: TableView.SingleSelection },
+ { text: "ContiguousSelection", value: TableView.ContiguousSelection },
+ { text: "ExtendedSelection", value: TableView.ExtendedSelection }
+ ]
+ Layout.fillWidth: false
+ }
+
ComboBox {
id: selectionModeCombo
enabled: selectionCombo.currentText != "SelectionDisabled"
@@ -151,6 +183,7 @@ ApplicationWindow {
"Drag",
"PressAndHold",
]
+ Layout.fillWidth: false
}
ComboBox {
id: editCombo
@@ -163,6 +196,11 @@ ApplicationWindow {
"EditKeyPressed",
"AnyKeyPressed",
]
+ Layout.fillWidth: false
+ }
+ Button {
+ text: "Clear selection"
+ onClicked: tableView.selectionModel.clearSelection()
}
}
}
@@ -171,6 +209,8 @@ ApplicationWindow {
Layout.minimumWidth: menu.availableWidth - (menu.menuMargin * 2)
Layout.rightMargin: menu.menuMargin
Layout.leftMargin: menu.menuMargin
+ Layout.fillWidth: false
+ Layout.fillHeight: false
GridLayout {
columns: 2
Label { text: "Model size:" }
@@ -180,6 +220,7 @@ ApplicationWindow {
to: 1000
value: 200
editable: true
+ Layout.fillWidth: false
}
Label { text: "Spacing:" }
SpinBox {
@@ -188,6 +229,7 @@ ApplicationWindow {
to: 100
value: 1
editable: true
+ Layout.fillWidth: false
}
Label { text: "Margins:" }
SpinBox {
@@ -196,6 +238,7 @@ ApplicationWindow {
to: 100
value: 0
editable: true
+ Layout.fillWidth: false
}
}
}
@@ -204,10 +247,13 @@ ApplicationWindow {
Layout.minimumWidth: menu.availableWidth - (menu.menuMargin * 2)
Layout.rightMargin: menu.menuMargin
Layout.leftMargin: menu.menuMargin
+ Layout.fillWidth: false
+ Layout.fillHeight: false
RowLayout {
id: positionRow
Button {
text: "<<"
+ Layout.fillWidth: false
onClicked: {
tableView.positionViewAtRow(0, Qt.AlignTop, -tableView.topMargin)
tableView.positionViewAtColumn(0, Qt.AlignLeft, -tableView.leftMargin)
@@ -216,6 +262,7 @@ ApplicationWindow {
Button {
text: ">>"
+ Layout.fillWidth: false
onClicked: {
tableView.positionViewAtRow(tableView.rows - 1, Qt.AlignBottom, tableView.bottomMargin)
tableView.positionViewAtColumn(tableView.columns - 1, Qt.AlignRight, tableView.rightMargin)
@@ -228,34 +275,41 @@ ApplicationWindow {
Layout.minimumWidth: menu.availableWidth - (menu.menuMargin * 2)
Layout.rightMargin: menu.menuMargin
Layout.leftMargin: menu.menuMargin
+ Layout.fillWidth: false
+ Layout.fillHeight: false
ColumnLayout {
Button {
text: "Add row"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: tableView.model.insertRows(currentIndex.row, 1)
}
Button {
text: "Remove row"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: tableView.model.removeRows(currentIndex.row, 1)
}
Button {
text: "Add column"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: tableView.model.insertColumns(currentIndex.column, 1)
}
Button {
text: "Remove column"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: tableView.model.removeColumns(currentIndex.column, 1)
}
Button {
text: "Hide column"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: {
hiddenColumn = currentIndex.column
tableView.forceLayout()
@@ -268,10 +322,13 @@ ApplicationWindow {
Layout.minimumWidth: menu.availableWidth - (menu.menuMargin * 2)
Layout.rightMargin: menu.menuMargin
Layout.leftMargin: menu.menuMargin
+ Layout.fillWidth: false
+ Layout.fillHeight: false
ColumnLayout {
Button {
text: "Current to top-left"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: {
let cell = Qt.point(currentIndex.column, currentIndex.row)
tableView.positionViewAtCell(cell, Qt.AlignTop | Qt.AlignLeft, positionOffset, positionSubRect)
@@ -281,6 +338,7 @@ ApplicationWindow {
Button {
text: "Current to center"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: {
let cell = Qt.point(currentIndex.column, currentIndex.row)
tableView.positionViewAtCell(cell, Qt.AlignCenter, positionOffset, positionSubRect)
@@ -290,6 +348,7 @@ ApplicationWindow {
Button {
text: "Current to bottom-right"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: {
let cell = Qt.point(currentIndex.column, currentIndex.row)
tableView.positionViewAtCell(cell, Qt.AlignBottom | Qt.AlignRight, positionOffset, positionSubRect)
@@ -299,6 +358,7 @@ ApplicationWindow {
Button {
text: "Current to Visible"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: {
let cell = Qt.point(currentIndex.column, currentIndex.row)
tableView.positionViewAtCell(cell, TableView.Visible, positionOffset, positionSubRect)
@@ -308,6 +368,7 @@ ApplicationWindow {
Button {
text: "Current to Contain"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: {
let cell = Qt.point(currentIndex.column, currentIndex.row)
tableView.positionViewAtCell(cell, TableView.Contain, positionOffset, positionSubRect)
@@ -320,10 +381,13 @@ ApplicationWindow {
Layout.minimumWidth: menu.availableWidth - (menu.menuMargin * 2)
Layout.rightMargin: menu.menuMargin
Layout.leftMargin: menu.menuMargin
+ Layout.fillWidth: false
+ Layout.fillHeight: false
ColumnLayout {
Button {
text: "Open editor"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: {
tableView.edit(currentIndex, true)
}
@@ -331,12 +395,14 @@ ApplicationWindow {
Button {
text: "Close editor"
enabled: currentIndex.valid
+ Layout.fillWidth: false
onClicked: {
tableView.closeEditor()
}
}
Button {
text: "Set current index"
+ Layout.fillWidth: false
onClicked: {
let index = tableView.modelIndex(1, 1);
tableView.selectionModel.setCurrentIndex(index, ItemSelectionModel.NoUpdate)
@@ -350,9 +416,12 @@ ApplicationWindow {
Layout.rightMargin: menu.menuMargin
Layout.leftMargin: menu.menuMargin
Layout.bottomMargin: menu.menuMargin
+ Layout.fillWidth: false
+ Layout.fillHeight: false
ColumnLayout {
Button {
text: "Fast-flick table"
+ Layout.fillWidth: false
onClicked: {
tableView.contentX += tableView.width * 1.2
}
@@ -360,6 +429,7 @@ ApplicationWindow {
Button {
text: "Fast-flick headers"
+ Layout.fillWidth: false
onClicked: {
topHeader.contentX += tableView.width * 1.2
leftHeader.contentY += tableView.height * 1.2
@@ -368,6 +438,7 @@ ApplicationWindow {
Button {
text: "ForceLayout()"
+ Layout.fillWidth: false
onClicked: tableView.forceLayout()
}
}
@@ -480,6 +551,7 @@ ApplicationWindow {
}
return TableView.SelectionDisabled
}
+ selectionMode: selectionModeTv.currentValue
editTriggers: {
switch (editCombo.currentText) {
case "NoEditTriggers": return TableView.NoEditTriggers
@@ -512,6 +584,7 @@ ApplicationWindow {
SelectionRectangle {
id: selectionRectangle
target: tableView
+ enabled: selectionCombo.currentText != "SelectionDisabled"
topLeftHandle: Rectangle {
width: 20
height: 20
diff --git a/tests/manual/tableview/imagetiling/imageTiling.qml b/tests/manual/tableview/imagetiling/imageTiling.qml
index 0784bfc0fe..834c356c1f 100644
--- a/tests/manual/tableview/imagetiling/imageTiling.qml
+++ b/tests/manual/tableview/imagetiling/imageTiling.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.15
import Qt.labs.qmlmodels 1.0
diff --git a/tests/manual/tableview/listmodel/main.cpp b/tests/manual/tableview/listmodel/main.cpp
index e50521c3b2..d65c3ea7e1 100644
--- a/tests/manual/tableview/listmodel/main.cpp
+++ b/tests/manual/tableview/listmodel/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/tableview/listmodel/main.qml b/tests/manual/tableview/listmodel/main.qml
index 56c8e0678e..f4b7f337a5 100644
--- a/tests/manual/tableview/listmodel/main.qml
+++ b/tests/manual/tableview/listmodel/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Window 2.3
diff --git a/tests/manual/tableview/storagemodel/main.cpp b/tests/manual/tableview/storagemodel/main.cpp
index b315e92b43..d6d336f145 100644
--- a/tests/manual/tableview/storagemodel/main.cpp
+++ b/tests/manual/tableview/storagemodel/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/tableview/storagemodel/main.qml b/tests/manual/tableview/storagemodel/main.qml
index f188c66fb9..0888497ea7 100644
--- a/tests/manual/tableview/storagemodel/main.qml
+++ b/tests/manual/tableview/storagemodel/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
diff --git a/tests/manual/tableview/storagemodel/storagemodel.cpp b/tests/manual/tableview/storagemodel/storagemodel.cpp
index 796e27570d..65ac8509e5 100644
--- a/tests/manual/tableview/storagemodel/storagemodel.cpp
+++ b/tests/manual/tableview/storagemodel/storagemodel.cpp
@@ -1,6 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Ivan Komissarov
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "storagemodel.h"
diff --git a/tests/manual/tableview/storagemodel/storagemodel.h b/tests/manual/tableview/storagemodel/storagemodel.h
index 3525d686b4..34ec29f575 100644
--- a/tests/manual/tableview/storagemodel/storagemodel.h
+++ b/tests/manual/tableview/storagemodel/storagemodel.h
@@ -1,6 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Ivan Komissarov
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef STORAGEMODEL_H
#define STORAGEMODEL_H
diff --git a/tests/manual/tableview/tablemodel/form/RowForm.qml b/tests/manual/tableview/tablemodel/form/RowForm.qml
index 0b6d5761be..33f11b18b2 100644
--- a/tests/manual/tableview/tablemodel/form/RowForm.qml
+++ b/tests/manual/tableview/tablemodel/form/RowForm.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Controls 2.3
diff --git a/tests/manual/tableview/tablemodel/form/main.cpp b/tests/manual/tableview/tablemodel/form/main.cpp
index e50521c3b2..d65c3ea7e1 100644
--- a/tests/manual/tableview/tablemodel/form/main.cpp
+++ b/tests/manual/tableview/tablemodel/form/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/tableview/tablemodel/form/main.qml b/tests/manual/tableview/tablemodel/form/main.qml
index 6e6d61f3f4..319c06dbdb 100644
--- a/tests/manual/tableview/tablemodel/form/main.qml
+++ b/tests/manual/tableview/tablemodel/form/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Controls 2.5
diff --git a/tests/manual/tableview/tablemodel/json/main.cpp b/tests/manual/tableview/tablemodel/json/main.cpp
index daf43cb257..b174cfd1e3 100644
--- a/tests/manual/tableview/tablemodel/json/main.cpp
+++ b/tests/manual/tableview/tablemodel/json/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/tableview/tablemodel/json/main.qml b/tests/manual/tableview/tablemodel/json/main.qml
index cea6d9724e..b9d1ea924e 100644
--- a/tests/manual/tableview/tablemodel/json/main.qml
+++ b/tests/manual/tableview/tablemodel/json/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.12
import QtQuick.Controls 2.5
diff --git a/tests/manual/text/SignalIndicator.qml b/tests/manual/text/SignalIndicator.qml
index f18f4ec1cd..e858d3dda4 100644
--- a/tests/manual/text/SignalIndicator.qml
+++ b/tests/manual/text/SignalIndicator.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
diff --git a/tests/manual/text/main.cpp b/tests/manual/text/main.cpp
index bf8eea7618..4862df0b8e 100644
--- a/tests/manual/text/main.cpp
+++ b/tests/manual/text/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QtQuick/QQuickView>
diff --git a/tests/manual/text/textInputPropertiesAndSignals.qml b/tests/manual/text/textInputPropertiesAndSignals.qml
index c06b532693..ae405b20b8 100644
--- a/tests/manual/text/textInputPropertiesAndSignals.qml
+++ b/tests/manual/text/textInputPropertiesAndSignals.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
@@ -17,6 +17,7 @@ Item {
id: enabledCB
text: "enabled"
checked: true
+ Layout.fillWidth: false
}
// ====================================================
@@ -31,6 +32,7 @@ Item {
id: textInputMouseSelCB
text: "mouse select"
onCheckedChanged: textInput.selectByMouse = checked
+ Layout.fillWidth: false
}
Rectangle {
@@ -110,6 +112,7 @@ Item {
id: textFieldMouseSelCB
text: "mouse select"
onCheckedChanged: textField.selectByMouse = checked
+ Layout.fillWidth: false
}
TextField {
@@ -190,6 +193,7 @@ Item {
text: "setText"
Layout.column: 2
Layout.row: 14
+ Layout.fillWidth: false
onClicked: {
Qt.inputMethod.reset()
textInput.text = copyFrom.text
@@ -211,14 +215,17 @@ Item {
CheckBox {
id: textEditReadOnly
text: "read-only"
+ Layout.fillWidth: false
}
CheckBox {
id: textEditMouseSelCB
text: "mouse select"
+ Layout.fillWidth: false
onCheckedChanged: textEdit.selectByMouse = checked
}
CheckBox {
id: textEditPressDelayCB
+ Layout.fillWidth: false
text: "press delay: " + flick.pressDelay
}
}
@@ -350,10 +357,12 @@ Item {
CheckBox {
id: textAreaReadOnly
text: "read-only"
+ Layout.fillWidth: false
}
CheckBox {
id: textAreaMouseSelCB
text: "mouse select"
+ Layout.fillWidth: false
onCheckedChanged: textArea.selectByMouse = checked
}
}
diff --git a/tests/manual/textrendering/CMakeLists.txt b/tests/manual/textrendering/CMakeLists.txt
new file mode 100644
index 0000000000..9987010192
--- /dev/null
+++ b/tests/manual/textrendering/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ project(textrendering LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+find_package(Qt6 COMPONENTS ShaderTools)
+
+qt_internal_add_manual_test(textrendering
+ GUI
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+ Qt::QuickPrivate
+)
+
+
+set(qml_resource_files
+ "main.qml"
+)
+
+qt_internal_add_resource(textrendering "qml"
+ PREFIX
+ "/"
+ FILES
+ ${qml_resource_files}
+)
+
+qt_add_qml_module(textrendering
+ VERSION 1.0
+ URI TextRendering
+ RESOURCE_PREFIX /
+)
diff --git a/tests/manual/textrendering/main.cpp b/tests/manual/textrendering/main.cpp
new file mode 100644
index 0000000000..3a0927e243
--- /dev/null
+++ b/tests/manual/textrendering/main.cpp
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QFontDatabase>
+
+int main(int argc, char *argv[])
+{
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+#endif
+ QGuiApplication app(argc, argv);
+ app.setOrganizationName("QtProject");
+
+ QQmlApplicationEngine engine;
+ const QUrl url(QStringLiteral("qrc:/main.qml"));
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ &app, [url](QObject *obj, const QUrl &objUrl) {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ }, Qt::QueuedConnection);
+ engine.load(url);
+
+ return app.exec();
+}
diff --git a/tests/manual/textrendering/main.qml b/tests/manual/textrendering/main.qml
new file mode 100644
index 0000000000..15a69f7c51
--- /dev/null
+++ b/tests/manual/textrendering/main.qml
@@ -0,0 +1,108 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Shapes
+
+Window {
+ id: theWindow
+ width: 1024
+ height: 768
+ visible: true
+ title: qsTr("Text Rendering")
+ color: "white"
+
+ Text {
+ id: dummyTextRendering
+ scale: scaleSlider.value
+ anchors.centerIn: parent
+ text: dummyText.text
+ font.pixelSize: fontSize.value
+ renderType: renderTypeCombo.currentIndex
+ style: styleCombo.currentIndex
+ styleColor: "indianred"
+ color: "black"
+ visible: renderTypeCombo.currentIndex <= 2
+ }
+
+ Shape {
+ id: dummyShapeRendering
+ anchors.centerIn: parent
+ scale: scaleSlider.value
+ visible: !dummyTextRendering.visible
+ width: boundingRect.width
+ height: boundingRect.height
+ preferredRendererType: shapesRendererCombo.currentIndex === 0 ? Shape.GeometryRenderer : Shape.CurveRenderer
+
+ ShapePath {
+ id: shapePath
+ fillColor: "black"
+ strokeColor: styleCombo.currentIndex === 1 ? "indianred" : "transparent"
+ strokeStyle: ShapePath.SolidLine
+ strokeWidth: 1
+ fillRule: ShapePath.WindingFill
+ PathText {
+ text: dummyText.text
+ font.pixelSize: fontSize.value
+ }
+ }
+ }
+
+ RowLayout {
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ TextField {
+ id: dummyText
+ text: "Foobar"
+ Layout.fillWidth: false
+ }
+
+ Label {
+ text: "Scale:"
+ }
+
+ Slider {
+ id: scaleSlider
+ from: 0.5
+ to: 10
+ value: 1
+ Layout.fillWidth: false
+ }
+
+ Label {
+ text: "Font size:"
+ }
+
+ Slider {
+ id: fontSize
+ from: 1
+ to: 1000
+ value: 100
+ Layout.fillWidth: true
+ }
+
+ ComboBox {
+ id: styleCombo
+ Layout.fillWidth: false
+ model: [ "Normal", "Outline", "Raised", "Sunken" ]
+ }
+
+ ComboBox {
+ id: renderTypeCombo
+ Layout.fillWidth: false
+ model: [ "QtRendering", "NativeRendering", "CurveRendering", "Qt Quick Shapes" ]
+ }
+
+ ComboBox {
+ id: shapesRendererCombo
+ Layout.fillWidth: false
+ model: [ "GeometryRenderer", "CurveRenderer" ]
+ visible: renderTypeCombo.currentIndex > 2
+ }
+ }
+}
diff --git a/examples/quick/text/textselection/pics/endHandle.png b/tests/manual/textselection/pics/endHandle.png
index 598cf98857..598cf98857 100644
--- a/examples/quick/text/textselection/pics/endHandle.png
+++ b/tests/manual/textselection/pics/endHandle.png
Binary files differ
diff --git a/examples/quick/text/textselection/pics/endHandle.sci b/tests/manual/textselection/pics/endHandle.sci
index 4f51f24278..4f51f24278 100644
--- a/examples/quick/text/textselection/pics/endHandle.sci
+++ b/tests/manual/textselection/pics/endHandle.sci
diff --git a/examples/quick/text/textselection/pics/startHandle.png b/tests/manual/textselection/pics/startHandle.png
index 51def03054..51def03054 100644
--- a/examples/quick/text/textselection/pics/startHandle.png
+++ b/tests/manual/textselection/pics/startHandle.png
Binary files differ
diff --git a/examples/quick/text/textselection/pics/startHandle.sci b/tests/manual/textselection/pics/startHandle.sci
index f9eae204c1..f9eae204c1 100644
--- a/examples/quick/text/textselection/pics/startHandle.sci
+++ b/tests/manual/textselection/pics/startHandle.sci
diff --git a/tests/manual/textselection/textselection.qml b/tests/manual/textselection/textselection.qml
new file mode 100644
index 0000000000..9375386704
--- /dev/null
+++ b/tests/manual/textselection/textselection.qml
@@ -0,0 +1,201 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+
+Rectangle {
+ id: editor
+ color: "lightGrey"
+ width: 640
+ height: 480
+
+ Rectangle {
+ color: "white"
+ anchors.fill: parent
+ anchors.margins: 20
+
+ BorderImage {
+ id: startHandle
+ source: "pics/startHandle.sci"
+ opacity: 0.0
+ width: 10
+ x: edit.positionToRectangle(edit.selectionStart).x - flick.contentX-width
+ y: edit.positionToRectangle(edit.selectionStart).y - flick.contentY
+ height: edit.positionToRectangle(edit.selectionStart).height
+ }
+
+ BorderImage {
+ id: endHandle
+ source: "pics/endHandle.sci"
+ opacity: 0.0
+ width: 10
+ x: edit.positionToRectangle(edit.selectionEnd).x - flick.contentX
+ y: edit.positionToRectangle(edit.selectionEnd).y - flick.contentY
+ height: edit.positionToRectangle(edit.selectionEnd).height
+ }
+
+ Flickable {
+ id: flick
+
+ anchors.fill: parent
+ contentWidth: edit.contentWidth
+ contentHeight: edit.contentHeight
+ interactive: true
+ clip: true
+
+ function ensureVisible(r) {
+ if (contentX >= r.x)
+ contentX = r.x;
+ else if (contentX+width <= r.x+r.width)
+ contentX = r.x+r.width-width;
+ if (contentY >= r.y)
+ contentY = r.y;
+ else if (contentY+height <= r.y+r.height)
+ contentY = r.y+r.height-height;
+ }
+
+ TextEdit {
+ id: edit
+ width: flick.width
+ height: flick.height
+ focus: true
+ wrapMode: TextEdit.Wrap
+ textFormat: TextEdit.RichText
+
+ onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)
+
+ text: "<h1>Text Selection</h1>"
+ +"<p>This example is a whacky text selection mechanisms, showing how these can be implemented in the TextEdit element, to cater for whatever style is appropriate for the target platform."
+ +"<p><b>Press-and-hold</b> to select a word, then drag the selection handles."
+ +"<p><b>Drag outside the selection</b> to scroll the text."
+ +"<p><b>Click inside the selection</b> to cut/copy/paste/cancel selection."
+ +"<p>It's too whacky to let you paste if there is no current selection."
+
+ }
+ }
+
+ Item {
+ id: menu
+ opacity: 0.0
+ width: 100
+ height: 120
+ anchors.centerIn: parent
+
+ Rectangle {
+ border.width: 1
+ border.color: "darkBlue"
+ radius: 15
+ color: "#806080FF"
+ anchors.fill: parent
+ }
+
+ Column {
+ anchors.centerIn: parent
+ spacing: 8
+
+ Rectangle {
+ border.width: 1
+ border.color: "darkBlue"
+ color: "#ff7090FF"
+ width: 60
+ height: 16
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("Cut")
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: function() {
+ edit.cut()
+ editor.state = ""
+ }
+ }
+ }
+
+ Rectangle {
+ border.width: 1
+ border.color: "darkBlue"
+ color: "#ff7090FF"
+ width: 60
+ height: 16
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("Copy")
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: function() {
+ edit.copy()
+ editor.state = "selection"
+ }
+ }
+ }
+
+ Rectangle {
+ border.width: 1
+ border.color: "darkBlue"
+ color: "#ff7090FF"
+ width: 60
+ height: 16
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("Paste")
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: function() {
+ edit.paste()
+ edit.cursorPosition = edit.selectionEnd
+ editor.state = ""
+ }
+ }
+ }
+
+ Rectangle {
+ border.width: 1
+ border.color: "darkBlue"
+ color: "#ff7090FF"
+ width: 60
+ height: 16
+
+ Text {
+ anchors.centerIn: parent
+ text: qsTr("Deselect")
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ edit.cursorPosition = edit.selectionEnd;
+ edit.deselect();
+ editor.state = ""
+ }
+ }
+ }
+ }
+ }
+ }
+
+ states: [
+ State {
+ name: "selection"
+ PropertyChanges {
+ startHandle.opacity: 1.0
+ endHandle.opacity: 1.0
+ }
+ },
+ State {
+ name: "menu"
+ PropertyChanges {
+ startHandle.opacity: 0.5
+ endHandle.opacity: 0.5
+ menu.opacity: 1.0
+ }
+ }
+ ]
+}
diff --git a/tests/manual/threading/CMakeLists.txt b/tests/manual/threading/CMakeLists.txt
new file mode 100644
index 0000000000..99ed5d9d6e
--- /dev/null
+++ b/tests/manual/threading/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(threadedlistmodel)
+add_subdirectory(workerscript)
diff --git a/tests/manual/threading/threadedlistmodel/CMakeLists.txt b/tests/manual/threading/threadedlistmodel/CMakeLists.txt
new file mode 100644
index 0000000000..68fb5c511c
--- /dev/null
+++ b/tests/manual/threading/threadedlistmodel/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(threadedlistmodel LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(tst_manual_threadedlistmodel
+ GUI
+ SOURCES
+ threadedlistmodel.cpp
+ DEFINES
+ QT_DEPRECATED_WARNINGS
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+)
+
+# Resources:
+set(qmake_immediate_resource_files
+ timedisplay.qml
+ dataloader.mjs
+)
+
+qt_internal_add_resource(tst_manual_threadedlistmodel "qmake_immediate"
+ PREFIX
+ "/qt/qml/threadedlistmodel"
+ FILES
+ ${qmake_immediate_resource_files}
+)
diff --git a/tests/manual/threading/threadedlistmodel/dataloader.mjs b/tests/manual/threading/threadedlistmodel/dataloader.mjs
new file mode 100644
index 0000000000..2c436d34dc
--- /dev/null
+++ b/tests/manual/threading/threadedlistmodel/dataloader.mjs
@@ -0,0 +1,12 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// ![0]
+WorkerScript.onMessage = function(msg) {
+ if (msg.action == 'appendCurrentTime') {
+ var data = {'time': new Date().toTimeString()};
+ msg.model.append(data);
+ msg.model.sync(); // updates the changes to the list
+ }
+}
+// ![0]
diff --git a/tests/manual/threading/threadedlistmodel/threadedlistmodel.cpp b/tests/manual/threading/threadedlistmodel/threadedlistmodel.cpp
new file mode 100644
index 0000000000..569e1d8418
--- /dev/null
+++ b/tests/manual/threading/threadedlistmodel/threadedlistmodel.cpp
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui/qguiapplication.h>
+#include <QtQuick/QQuickView>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("threadedlistmodel-manual-test");
+ QCoreApplication::setOrganizationName("QtProject");
+
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/qt/qml/threadedlistmodel/timedisplay.qml")));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/threading/threadedlistmodel/threadedlistmodel.pro b/tests/manual/threading/threadedlistmodel/threadedlistmodel.pro
new file mode 100644
index 0000000000..b3849b3e1a
--- /dev/null
+++ b/tests/manual/threading/threadedlistmodel/threadedlistmodel.pro
@@ -0,0 +1,6 @@
+TEMPLATE = app
+TARGET = threadedlistmodel
+QT += qml quick
+
+SOURCES += threadedlistmodel.cpp
+RESOURCES += threadedlistmodel.qrc
diff --git a/tests/manual/threading/threadedlistmodel/threadedlistmodel.qrc b/tests/manual/threading/threadedlistmodel/threadedlistmodel.qrc
new file mode 100644
index 0000000000..dbc5bc312d
--- /dev/null
+++ b/tests/manual/threading/threadedlistmodel/threadedlistmodel.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/qt/qml/threadedlistmodel">
+ <file>timedisplay.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/threading/threadedlistmodel/timedisplay.qml b/tests/manual/threading/threadedlistmodel/timedisplay.qml
new file mode 100644
index 0000000000..19bd694137
--- /dev/null
+++ b/tests/manual/threading/threadedlistmodel/timedisplay.qml
@@ -0,0 +1,42 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ color: "white"
+ width: 200
+ height: 300
+
+ ListView {
+ anchors.fill: parent
+ model: listModel
+ delegate: Component {
+ Text {
+ required property string time
+ text: time
+ }
+ }
+
+ ListModel { id: listModel }
+
+ WorkerScript {
+ id: worker
+ source: "dataloader.mjs"
+ }
+
+// ![0]
+ Timer {
+ id: timer
+ interval: 2000; repeat: true
+ running: true
+ triggeredOnStart: true
+
+ onTriggered: {
+ var msg = {'action': 'appendCurrentTime', 'model': listModel};
+ worker.sendMessage(msg);
+ }
+ }
+// ![0]
+ }
+}
diff --git a/tests/manual/threading/workerscript/CMakeLists.txt b/tests/manual/threading/workerscript/CMakeLists.txt
new file mode 100644
index 0000000000..3f73985b33
--- /dev/null
+++ b/tests/manual/threading/workerscript/CMakeLists.txt
@@ -0,0 +1,34 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(workerscript LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(tst_manual_workerscript
+ GUI
+ SOURCES
+ workerscript.cpp
+ DEFINES
+ QT_DEPRECATED_WARNINGS
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+)
+
+# Resources:
+set(qmake_immediate_resource_files
+ "Spinner.qml"
+ "workerscript.qml"
+ "workerscript.mjs"
+)
+
+qt_internal_add_resource(tst_manual_workerscript "qmake_immediate"
+ PREFIX
+ "/qt/qml/workerscript"
+ FILES
+ ${qmake_immediate_resource_files}
+)
diff --git a/tests/manual/threading/workerscript/Spinner.qml b/tests/manual/threading/workerscript/Spinner.qml
new file mode 100644
index 0000000000..0c894a2567
--- /dev/null
+++ b/tests/manual/threading/workerscript/Spinner.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ width: 64
+ height: 64
+ property alias value: list.currentIndex
+ property alias label: caption.text
+
+ Text {
+ id: caption
+ text: "Spinner"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ Rectangle {
+ anchors.top: caption.bottom
+ anchors.topMargin: 4
+ anchors.horizontalCenter: parent.horizontalCenter
+ height: 48
+ width: 32
+ color: "black"
+ ListView {
+ id: list
+ anchors.fill: parent
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ preferredHighlightBegin: height/3
+ preferredHighlightEnd: height/3
+ clip: true
+ model: 64
+ delegate: Text {
+ required property int index
+ font.pixelSize: 18;
+ color: "white";
+ text: index;
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ Rectangle {
+ anchors.fill: parent
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#FF000000" }
+ GradientStop { position: 0.2; color: "#00000000" }
+ GradientStop { position: 0.8; color: "#00000000" }
+ GradientStop { position: 1.0; color: "#FF000000" }
+ }
+ }
+ }
+}
diff --git a/tests/manual/threading/workerscript/workerscript.cpp b/tests/manual/threading/workerscript/workerscript.cpp
new file mode 100644
index 0000000000..9b28b7c350
--- /dev/null
+++ b/tests/manual/threading/workerscript/workerscript.cpp
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui/qguiapplication.h>
+#include <QtQuick/QQuickView>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("workerscript-manual-test");
+ QCoreApplication::setOrganizationName("QtProject");
+
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/qt/qml/workerscript/workerscript.qml")));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/threading/workerscript/workerscript.mjs b/tests/manual/threading/workerscript/workerscript.mjs
new file mode 100644
index 0000000000..cecce85896
--- /dev/null
+++ b/tests/manual/threading/workerscript/workerscript.mjs
@@ -0,0 +1,27 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+//Will be initialized when WorkerScript{} is instantiated
+var cache = new Array(64);
+for (var i = 0; i < 64; i++)
+ cache[i] = new Array(64);
+
+function triangle(row, column) {
+ if (cache[row][column])
+ return cache[row][column]
+ if (column < 0 || column > row)
+ return -1;
+ if (column == 0 || column == row)
+ return 1;
+ return triangle(row-1, column-1) + triangle(row-1, column);
+}
+//! [0]
+WorkerScript.onMessage = function(message) {
+ //Calculate result (may take a while, using a naive algorithm)
+ var calculatedResult = triangle(message.row, message.column);
+ //Send result back to main thread
+ WorkerScript.sendMessage( { row: message.row,
+ column: message.column,
+ result: calculatedResult} );
+}
+//! [0]
diff --git a/tests/manual/threading/workerscript/workerscript.pro b/tests/manual/threading/workerscript/workerscript.pro
new file mode 100644
index 0000000000..33ec5bda14
--- /dev/null
+++ b/tests/manual/threading/workerscript/workerscript.pro
@@ -0,0 +1,6 @@
+TEMPLATE = app
+TARGET = workerscript
+QT += qml quick
+
+SOURCES += workerscript.cpp
+RESOURCES += workerscript.qrc
diff --git a/tests/manual/threading/workerscript/workerscript.qml b/tests/manual/threading/workerscript/workerscript.qml
new file mode 100644
index 0000000000..378b45bc82
--- /dev/null
+++ b/tests/manual/threading/workerscript/workerscript.qml
@@ -0,0 +1,60 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ width: 320; height: 480
+
+ WorkerScript {
+ id: myWorker
+ source: "workerscript.mjs"
+
+ onMessage: (messageObject) => {
+ if (messageObject.row == rowSpinner.value && messageObject.column == columnSpinner.value){ //Not an old result
+ if (messageObject.result == -1)
+ resultText.text = "Column must be <= Row";
+ else
+ resultText.text = messageObject.result;
+ }
+ }
+ }
+
+ Row {
+ y: 24
+ spacing: 24
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ Spinner {
+ id: rowSpinner
+ label: "Row"
+ onValueChanged: {
+ resultText.text = "Loading...";
+ myWorker.sendMessage( { row: rowSpinner.value, column: columnSpinner.value } );
+ }
+ }
+
+ Spinner {
+ id: columnSpinner
+ label: "Column"
+ onValueChanged: {
+ resultText.text = "Loading...";
+ myWorker.sendMessage( { row: rowSpinner.value, column: columnSpinner.value } );
+ }
+ }
+ }
+
+ Text {
+ id: resultText
+ y: 180
+ width: parent.width
+ horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.WordWrap
+ font.pixelSize: 32
+ }
+
+ Text {
+ text: "Pascal's Triangle Calculator"
+ anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; bottomMargin: 50 }
+ }
+}
diff --git a/tests/manual/threading/workerscript/workerscript.qrc b/tests/manual/threading/workerscript/workerscript.qrc
new file mode 100644
index 0000000000..ca3833ff1c
--- /dev/null
+++ b/tests/manual/threading/workerscript/workerscript.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/qt/qml/workerscript">
+ <file>Spinner.qml</file>
+ <file>workerscript.mjs</file>
+ <file>workerscript.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/touch/CMakeLists.txt b/tests/manual/touch/CMakeLists.txt
index 835a593732..a843eb2248 100644
--- a/tests/manual/touch/CMakeLists.txt
+++ b/tests/manual/touch/CMakeLists.txt
@@ -19,9 +19,24 @@ qt_internal_add_manual_test(tst_manual_touch
# Resources:
set(qml_resource_files
+ "basic-flickable.qml"
+ "flickresize.qml"
"flicktext.qml"
"main.qml"
"mpta-crosshairs.qml"
+ "qt-logo.jpg"
+ "bearwhack/heart-blur.png"
+ "bearwhack/Bear0.png"
+ "bearwhack/BearWhackParticleSystem.qml"
+ "bearwhack/blur-circle.png"
+ "bearwhack/Bear1.png"
+ "bearwhack/bearwhack.qml"
+ "bearwhack/Bear2.png"
+ "bearwhack/title.png"
+ "bearwhack/blur-circle3.png"
+ "bearwhack/Bear3.png"
+ "bearwhack/AugmentedTouchPoint.qml"
+ "bearwhack/BearB.png"
)
qt_internal_add_resource(tst_manual_touch "qml"
@@ -53,7 +68,3 @@ qt_internal_add_resource(tst_manual_touch "quick_shared"
FILES
${quick_shared_resource_files}
)
-
-
-#### Keys ignored in scope 1:.:.:touch.pro:<TRUE>:
-# TEMPLATE = "app"
diff --git a/tests/manual/touch/basic-flickable.qml b/tests/manual/touch/basic-flickable.qml
new file mode 100644
index 0000000000..c3ccfeb46c
--- /dev/null
+++ b/tests/manual/touch/basic-flickable.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+ //![0]
+Rectangle {
+ width: 320
+ height: 480
+ Flickable {
+ anchors.fill: parent
+ contentWidth: 1200
+ contentHeight: 1200
+ Rectangle {
+ width: 1000
+ height: 1000
+ //![0]
+ x: 100
+ y: 100
+ radius: 128
+ border.width: 4
+ border.color: "black"
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#000000" }
+ GradientStop { position: 0.2; color: "#888888" }
+ GradientStop { position: 0.4; color: "#FFFFFF" }
+ GradientStop { position: 0.6; color: "#FFFFFF" }
+ GradientStop { position: 0.8; color: "#888888" }
+ GradientStop { position: 1.0; color: "#000000" }
+ }
+ }
+ }
+}
diff --git a/tests/manual/touch/bearwhack/AugmentedTouchPoint.qml b/tests/manual/touch/bearwhack/AugmentedTouchPoint.qml
new file mode 100644
index 0000000000..6c7a4127fa
--- /dev/null
+++ b/tests/manual/touch/bearwhack/AugmentedTouchPoint.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Particles
+
+//! [0]
+TouchPoint {
+ id: container
+ property ParticleSystem system
+ onPressedChanged: {
+ if (pressed) {
+ timer.restart();
+ child.enabled = true;
+ system.explode(x,y);
+ }
+ }
+ property QtObject obj: Timer {
+ id: timer
+ interval: 100
+ running: false
+ repeat: false
+ onTriggered: container.child.enabled = false
+ }
+ property Item child: SpriteGoal {
+ enabled: false
+ x: container.area.x - 16
+ y: container.area.y - 16
+ width: container.area.width + 32
+ height: container.area.height + 32 //+32 so it doesn't have to hit the exact center
+ system: container.system
+ parent: container.system
+ goalState: "falling"
+ }
+}
+//! [0]
diff --git a/examples/quick/touchinteraction/multipointtouch/Bear0.png b/tests/manual/touch/bearwhack/Bear0.png
index f520cc1f1b..f520cc1f1b 100644
--- a/examples/quick/touchinteraction/multipointtouch/Bear0.png
+++ b/tests/manual/touch/bearwhack/Bear0.png
Binary files differ
diff --git a/examples/quick/touchinteraction/multipointtouch/Bear1.png b/tests/manual/touch/bearwhack/Bear1.png
index 3b88f3b32e..3b88f3b32e 100644
--- a/examples/quick/touchinteraction/multipointtouch/Bear1.png
+++ b/tests/manual/touch/bearwhack/Bear1.png
Binary files differ
diff --git a/examples/quick/touchinteraction/multipointtouch/Bear2.png b/tests/manual/touch/bearwhack/Bear2.png
index 337b636070..337b636070 100644
--- a/examples/quick/touchinteraction/multipointtouch/Bear2.png
+++ b/tests/manual/touch/bearwhack/Bear2.png
Binary files differ
diff --git a/examples/quick/touchinteraction/multipointtouch/Bear3.png b/tests/manual/touch/bearwhack/Bear3.png
index f0e68590f4..f0e68590f4 100644
--- a/examples/quick/touchinteraction/multipointtouch/Bear3.png
+++ b/tests/manual/touch/bearwhack/Bear3.png
Binary files differ
diff --git a/examples/quick/touchinteraction/multipointtouch/BearB.png b/tests/manual/touch/bearwhack/BearB.png
index 60be2b32c6..60be2b32c6 100644
--- a/examples/quick/touchinteraction/multipointtouch/BearB.png
+++ b/tests/manual/touch/bearwhack/BearB.png
Binary files differ
diff --git a/tests/manual/touch/bearwhack/BearWhackParticleSystem.qml b/tests/manual/touch/bearwhack/BearWhackParticleSystem.qml
new file mode 100644
index 0000000000..611d6d540f
--- /dev/null
+++ b/tests/manual/touch/bearwhack/BearWhackParticleSystem.qml
@@ -0,0 +1,146 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Particles
+
+ParticleSystem {
+ id: particleSystem
+
+ property int score
+
+ function explode(x,y) {
+ fireEmitter.burst(100,x,y);
+ }
+
+ Emitter {
+ id: emitter
+ group: "bears"
+ width: parent.width
+ emitRate: 1
+ NumberAnimation on emitRate {
+ id: goFaster
+ from: 1
+ to: 16
+ running: particleSystem.running
+ loops: 1
+ duration: 60000 * 5
+ easing.type: Easing.Linear
+ }
+ lifeSpan: 4000 + 800*(16-emitRate)
+ maximumEmitted: 128
+ size: 64
+ velocity: PointDirection{ y: 40 + 10 * emitter.emitRate }
+ }
+
+ Emitter {
+ id: fireEmitter
+ enabled: false
+ maximumEmitted: 6000
+ group: "flame"
+ emitRate: 1000
+ size: 16
+ endSize: 8
+ velocity: CumulativeDirection { AngleDirection {angleVariation: 180; magnitudeVariation: 120;} PointDirection { y: -60 }}
+ lifeSpan: 400
+ }
+ Emitter {
+ id: heartEmitter
+ enabled: false
+ maximumEmitted: 6000
+ group: "hearts"
+ emitRate: 1000
+ size: 16
+ endSize: 8
+ velocity: AngleDirection {angleVariation: 180; magnitudeVariation: 180;}
+ lifeSpan: 600
+ }
+ Emitter {
+ id: bloodEmitter
+ enabled: false
+ maximumEmitted: 6000
+ group: "blood"
+ emitRate: 1000
+ size: 16
+ endSize: 8
+ velocity: CumulativeDirection { AngleDirection {angleVariation: 180; magnitudeVariation: 80;} PointDirection { y: 40 }}
+ lifeSpan: 600
+ }
+
+ Affector {
+ width: parent.width
+ height: 64
+ once: true
+ y: parent.height - 32
+ groups: "bears"
+ onAffectParticles: function(particles) {
+ for (var i=0;i<particles.length; i++) {
+ if (particles[i].animationIndex != 0) {
+ particleSystem.score++;
+ bloodEmitter.burst(100, particles[i].x, particles[i].y);
+ } else {
+ particleSystem.score--;
+ heartEmitter.burst(100, particles[i].x, particles[i].y);
+ }
+ particles[i].update = 1.0;
+ particles[i].t -= 1000.0;
+ }
+ }
+ }
+ ImageParticle {
+ groups: ["flame"]
+ source: "blur-circle.png"
+ z: 4
+ colorVariation: 0.1
+ color: "#ffa24d"
+ alpha: 0.4
+ }
+ ImageParticle {
+ groups: ["blood"]
+ color: "red"
+ z: 2
+ source: "blur-circle3.png"
+ alpha: 0.2
+ }
+ ImageParticle {
+ groups: ["hearts"]
+ color: "#ff66AA"
+ z: 3
+ source: "heart-blur.png"
+ alpha: 0.4
+ autoRotation: true
+ }
+ ImageParticle {
+ groups: ["bears"]
+ z: 1
+ spritesInterpolate: false
+ sprites:[
+ Sprite{
+ name: "floating"
+ source: "Bear1.png"
+ frameCount: 9
+ frameWidth: 256
+ frameHeight: 256
+ frameDuration: 80
+ to: {"still":0, "flailing":0}
+ },
+ Sprite{
+ name: "flailing"
+ source: "Bear2.png"
+ frameCount: 8
+ frameWidth: 256
+ frameHeight: 256
+ frameDuration: 80
+ to: {"falling":1}
+ },
+ Sprite{
+ name: "falling"
+ source: "Bear3.png"
+ frameCount: 5
+ frameWidth: 256
+ frameHeight: 256
+ frameDuration: 80
+ to: {"falling":1}
+ }
+ ]
+ }
+}
diff --git a/tests/manual/touch/bearwhack/bearwhack.qml b/tests/manual/touch/bearwhack/bearwhack.qml
new file mode 100644
index 0000000000..35121d2515
--- /dev/null
+++ b/tests/manual/touch/bearwhack/bearwhack.qml
@@ -0,0 +1,97 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Particles
+
+Item {
+ id: root
+ height: 480
+ width: 320
+ Item {
+ id: startScreen
+ anchors.fill: parent
+ z: 1000
+ Image {
+ source: "title.png"
+ anchors.centerIn: parent
+ }
+ MouseArea{
+ anchors.fill: parent
+ onClicked: {//Game Start
+ parent.visible = false;
+ }
+ }
+ }
+ Rectangle {
+ id: bg
+ anchors.fill: parent
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "DarkBlue" }
+ GradientStop { position: 0.8; color: "SkyBlue" }
+ GradientStop { position: 0.81; color: "ForestGreen" }
+ GradientStop { position: 1.0; color: "DarkGreen" }
+ }
+ }
+
+ BearWhackParticleSystem {
+ id: particleSystem
+ anchors.fill: parent
+ running: !startScreen.visible
+ }
+
+ property int score: particleSystem.score
+
+ Text {
+ anchors.right: parent.right
+ anchors.margins: 4
+ anchors.top: parent.top
+ color: "white"
+ function padded(num) {
+ var ret = num.toString();
+
+ if (ret >= 0)
+ return ret.padStart(6, "0");
+ else
+ return "-" + ret.substr(1).padStart(6, "0");
+ }
+ text: "Score: " + padded(root.score)
+ }
+ MultiPointTouchArea {
+ anchors.fill: parent
+ touchPoints: [//Support up to 4 touches at once?
+ AugmentedTouchPoint{ system: particleSystem },
+ AugmentedTouchPoint{ system: particleSystem },
+ AugmentedTouchPoint{ system: particleSystem },
+ AugmentedTouchPoint{ system: particleSystem }
+ ]
+ }
+ MouseArea{
+ anchors.fill: parent
+ id: ma
+ onPressedChanged: {
+ if (pressed) {
+ timer.restart();
+ sgoal.enabled = true;
+ particleSystem.explode(mouseX,mouseY);
+ }
+ }
+ Timer {
+ id: timer
+ interval: 100
+ running: false
+ repeat: false
+ onTriggered: sgoal.enabled = false
+ }
+ SpriteGoal {
+ id: sgoal
+ x: ma.mouseX - 16
+ y: ma.mouseY - 16
+ width: 32
+ height: 32
+ system: particleSystem
+ parent: particleSystem
+ goalState: "falling"
+ enabled: false
+ }
+ }
+}
diff --git a/tests/manual/touch/bearwhack/blur-circle.png b/tests/manual/touch/bearwhack/blur-circle.png
new file mode 100644
index 0000000000..1b03cd0b2c
--- /dev/null
+++ b/tests/manual/touch/bearwhack/blur-circle.png
Binary files differ
diff --git a/examples/quick/touchinteraction/multipointtouch/blur-circle3.png b/tests/manual/touch/bearwhack/blur-circle3.png
index dbc39cb16e..dbc39cb16e 100644
--- a/examples/quick/touchinteraction/multipointtouch/blur-circle3.png
+++ b/tests/manual/touch/bearwhack/blur-circle3.png
Binary files differ
diff --git a/examples/quick/touchinteraction/multipointtouch/heart-blur.png b/tests/manual/touch/bearwhack/heart-blur.png
index dbc38804ee..dbc38804ee 100644
--- a/examples/quick/touchinteraction/multipointtouch/heart-blur.png
+++ b/tests/manual/touch/bearwhack/heart-blur.png
Binary files differ
diff --git a/examples/quick/touchinteraction/multipointtouch/title.png b/tests/manual/touch/bearwhack/title.png
index 01893a247d..01893a247d 100644
--- a/examples/quick/touchinteraction/multipointtouch/title.png
+++ b/tests/manual/touch/bearwhack/title.png
Binary files differ
diff --git a/tests/manual/touch/flickresize.qml b/tests/manual/touch/flickresize.qml
new file mode 100644
index 0000000000..54649e59c4
--- /dev/null
+++ b/tests/manual/touch/flickresize.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ width: 640
+ height: 360
+ color: "gray"
+
+ Flickable {
+ id: flick
+ anchors.fill: parent
+ contentWidth: 500
+ contentHeight: 500
+
+ PinchArea {
+ width: Math.max(flick.contentWidth, flick.width)
+ height: Math.max(flick.contentHeight, flick.height)
+
+ property real initialWidth
+ property real initialHeight
+ //![0]
+ onPinchStarted: {
+ initialWidth = flick.contentWidth
+ initialHeight = flick.contentHeight
+ }
+
+ onPinchUpdated: (pinch)=> {
+ // adjust content pos due to drag
+ flick.contentX += pinch.previousCenter.x - pinch.center.x
+ flick.contentY += pinch.previousCenter.y - pinch.center.y
+
+ // resize content
+ flick.resizeContent(initialWidth * pinch.scale, initialHeight * pinch.scale, pinch.center)
+ }
+
+ onPinchFinished: {
+ // Move its content within bounds.
+ flick.returnToBounds()
+ }
+ //![0]
+
+ Rectangle {
+ width: flick.contentWidth
+ height: flick.contentHeight
+ color: "white"
+ Image {
+ anchors.fill: parent
+ source: "qt-logo.jpg"
+ MouseArea {
+ anchors.fill: parent
+ onDoubleClicked: {
+ flick.contentWidth = 500
+ flick.contentHeight = 500
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/touch/flicktext.qml b/tests/manual/touch/flicktext.qml
index 56b65e8419..8c93f313c5 100644
--- a/tests/manual/touch/flicktext.qml
+++ b/tests/manual/touch/flicktext.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import "qrc:/quick/shared/" as Examples
diff --git a/tests/manual/touch/main.cpp b/tests/manual/touch/main.cpp
index 1c79ce0ea9..17f6617e97 100644
--- a/tests/manual/touch/main.cpp
+++ b/tests/manual/touch/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QQmlApplicationEngine>
diff --git a/tests/manual/touch/main.qml b/tests/manual/touch/main.qml
index ca40140271..8a72c690be 100644
--- a/tests/manual/touch/main.qml
+++ b/tests/manual/touch/main.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.4
import QtQuick.Window 2.2
@@ -14,6 +14,8 @@ Window {
anchors.fill: parent
Component.onCompleted: {
addExample("crosshairs", "crosshairs and velocity vectors at finger positions", Qt.resolvedUrl("mpta-crosshairs.qml"))
+ addExample("flickable", "flick a bigger Rectangle", Qt.resolvedUrl("basic-flickable.qml"))
+ addExample("flick and resize", "flick and resize an image (old-school PinchArea)", Qt.resolvedUrl("flickresize.qml"))
addExample("flick text", "flick a text document", Qt.resolvedUrl("flicktext.qml"))
}
}
diff --git a/tests/manual/touch/mpta-crosshairs.qml b/tests/manual/touch/mpta-crosshairs.qml
index 15667a2364..a703d78ddf 100644
--- a/tests/manual/touch/mpta-crosshairs.qml
+++ b/tests/manual/touch/mpta-crosshairs.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.9
import QtQuick.Window 2.2
diff --git a/examples/quick/touchinteraction/pincharea/qt-logo.jpg b/tests/manual/touch/qt-logo.jpg
index 4014b4659c..4014b4659c 100644
--- a/examples/quick/touchinteraction/pincharea/qt-logo.jpg
+++ b/tests/manual/touch/qt-logo.jpg
Binary files differ
diff --git a/tests/manual/treeview/sidebyside/CMakeLists.txt b/tests/manual/treeview/sidebyside/CMakeLists.txt
index 818cb73bab..046ab4e3bd 100644
--- a/tests/manual/treeview/sidebyside/CMakeLists.txt
+++ b/tests/manual/treeview/sidebyside/CMakeLists.txt
@@ -35,6 +35,8 @@ qt_internal_extend_target(treeview_sidebyside CONDITION TARGET Qt::Widgets
qt6_add_qml_module(treeview_sidebyside
URI TestModel
VERSION 1.0
+ QML_FILES
+ data/treeview.qml
)
# Resources:
diff --git a/tests/manual/treeview/sidebyside/data/treeview.qml b/tests/manual/treeview/sidebyside/data/treeview.qml
index 7d265c44e1..6cd3af662f 100644
--- a/tests/manual/treeview/sidebyside/data/treeview.qml
+++ b/tests/manual/treeview/sidebyside/data/treeview.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
@@ -12,7 +12,6 @@ ApplicationWindow {
visible: true
property alias treeView: treeView
- property var selectedIndex: undefined
UICallback { id: callback }
@@ -37,6 +36,11 @@ ApplicationWindow {
text: "Use file system model"
}
+ CheckBox {
+ id: interactiveMode
+ text: "Interactive"
+ }
+
Button {
text: "Show QTreeView"
onClicked: callback.showQTreeView(treeView.model)
@@ -45,7 +49,7 @@ ApplicationWindow {
Button {
text: "Insert row"
onClicked: {
- let index = treeView.modelIndex(1, 0)
+ let index = treeView.index(1, 0)
treeView.model.insertRows(index.row, 1, index.parent);
}
}
@@ -53,20 +57,31 @@ ApplicationWindow {
Button {
text: "Remove row"
onClicked: {
- let index = treeView.modelIndex(1, 0)
+ let index = treeView.index(1, 0)
treeView.model.removeRows(index.row, 1, index.parent);
}
}
Button {
text: "Expand to"
- enabled: selectedIndex != undefined
onClicked: {
- treeView.expandToIndex(selectedIndex);
+ treeView.expandToIndex(treeView.selectionModel.currentIndex);
treeView.forceLayout()
- let row = treeView.rowAtIndex(selectedIndex)
+ let row = treeView.rowAtIndex(treeView.selectionModel.currentIndex)
treeView.positionViewAtRow(row, Qt.AlignVCenter)
}
}
+ Button {
+ text: "Set root"
+ onClicked: {
+ treeView.rootIndex = treeView.selectionModel.currentIndex
+ }
+ }
+ Button {
+ text: "Reset root"
+ onClicked: {
+ treeView.rootIndex = undefined
+ }
+ }
}
TreeView {
@@ -78,6 +93,7 @@ ApplicationWindow {
anchors.bottom: parent.bottom
anchors.leftMargin: 100
anchors.topMargin: 100
+ interactive: interactiveMode.checked
clip: true
selectionModel: ItemSelectionModel {}
@@ -85,6 +101,10 @@ ApplicationWindow {
model: useFileSystemModel.checked ? fileSystemModel : testModel
delegate: useCustomDelegate.checked ? customDelegate : treeViewDelegate
}
+
+ SelectionRectangle {
+ target: treeView
+ }
}
TestModel {
@@ -103,27 +123,19 @@ ApplicationWindow {
treeView.expandRecursively(row)
}
}
- TapHandler {
- acceptedModifiers: Qt.ShiftModifier
- onTapped: selectedIndex = treeView.modelIndex(row, 0)
- }
- Rectangle {
- anchors.fill: parent
- border.color: "red"
- border.width: 1
- visible: treeView.modelIndex(row, column) === selectedIndex
- }
}
}
Component {
id: customDelegate
- Item {
+ Rectangle {
id: root
implicitWidth: padding + label.x + label.implicitWidth + padding
implicitHeight: label.implicitHeight * 1.5
+ required property bool selected
+
readonly property real indentation: 20
readonly property real padding: 5
@@ -134,6 +146,8 @@ ApplicationWindow {
required property int hasChildren
required property int depth
+ color: selected ? "lightblue" : "transparent"
+
Text {
id: indicator
visible: root.isTreeNode && root.hasChildren
diff --git a/tests/manual/treeview/sidebyside/main.cpp b/tests/manual/treeview/sidebyside/main.cpp
index 25b598b703..c3661525a2 100644
--- a/tests/manual/treeview/sidebyside/main.cpp
+++ b/tests/manual/treeview/sidebyside/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickwindow.h>
@@ -26,6 +26,7 @@ int main(int c, char **args) {
#endif
QFileSystemModel model;
+ model.setIconProvider(nullptr); // save time: we don't need icons
model.setRootPath("/");
QQmlApplicationEngine engine("qrc:data/treeview.qml");
diff --git a/tests/manual/treeview/sidebyside/testmodel.cpp b/tests/manual/treeview/sidebyside/testmodel.cpp
index 3b70ea7ab5..82d6e1efcf 100644
--- a/tests/manual/treeview/sidebyside/testmodel.cpp
+++ b/tests/manual/treeview/sidebyside/testmodel.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testmodel.h"
diff --git a/tests/manual/treeview/sidebyside/testmodel.h b/tests/manual/treeview/sidebyside/testmodel.h
index 86007f0d7f..27b528f664 100644
--- a/tests/manual/treeview/sidebyside/testmodel.h
+++ b/tests/manual/treeview/sidebyside/testmodel.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTMODEL_H
#define TESTMODEL_H
diff --git a/tests/manual/v4/auto/executableallocator/tst_executableallocator.cpp b/tests/manual/v4/auto/executableallocator/tst_executableallocator.cpp
index 065ae0b338..96d99a3ca6 100644
--- a/tests/manual/v4/auto/executableallocator/tst_executableallocator.cpp
+++ b/tests/manual/v4/auto/executableallocator/tst_executableallocator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtCore>
diff --git a/tests/manual/wasm/CMakeLists.txt b/tests/manual/wasm/CMakeLists.txt
new file mode 100644
index 0000000000..880bab9cd1
--- /dev/null
+++ b/tests/manual/wasm/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(a11y)
diff --git a/tests/manual/wasm/a11y/CMakeLists.txt b/tests/manual/wasm/a11y/CMakeLists.txt
new file mode 100644
index 0000000000..f769bfb83f
--- /dev/null
+++ b/tests/manual/wasm/a11y/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(qml_basic_item)
diff --git a/tests/manual/wasm/a11y/qml_basic_item/AboutDialog.qml b/tests/manual/wasm/a11y/qml_basic_item/AboutDialog.qml
new file mode 100644
index 0000000000..f74677abf0
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/AboutDialog.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+Dialog {
+ id: aboutDialog
+ width: 500
+ height: 150
+ title: "WebAssembly Dialog box"
+ modal: true
+
+ background: Rectangle {
+ width: parent.width * 0.8
+ height: parent.height * 0.4
+ color: "lightgray"
+ anchors.fill: parent
+ Accessible.role: Accessible.Dialog
+ Accessible.name: parent.title
+ Accessible.description: "The About Dialog box."
+ }
+ ColumnLayout {
+ spacing: 2
+ anchors.fill: parent
+
+ Label {
+ id: labelInfo
+ Layout.alignment: Qt.AlignCenter
+ text: "Accessibility Demo sample application developed in QML."
+ Accessible.role: Accessible.StaticText
+ horizontalAlignment: Text.AlignHCenter
+ Accessible.name: text
+ Accessible.description: "Purpose of this application."
+ wrapMode: Text.WordWrap
+ }
+
+ Button {
+ id: closeButton
+ text: "Close"
+ Layout.alignment: Qt.AlignCenter
+ Accessible.role: Accessible.Button
+ Accessible.name: text
+ Accessible.description: "To close the About Dialog box."
+ onClicked: {
+ aboutDialog.close()
+ }
+ }
+ }
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/CMakeLists.txt b/tests/manual/wasm/a11y/qml_basic_item/CMakeLists.txt
new file mode 100644
index 0000000000..2214747fe5
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/CMakeLists.txt
@@ -0,0 +1,51 @@
+
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(textManual LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(qml_basic_item
+ GUI
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::QuickControls2
+)
+
+# Resources:
+set(qmake_immediate_resource_files
+ "main.qml"
+ "WasmMenu.qml"
+ "WasmToolBar.qml"
+ "MeetingTabs.qml"
+ "MeetingInviteesPage.qml"
+ "MeetingSchedulerPage.qml"
+ "MeetingSummary.qml"
+ "WasmListView.qml"
+ "MeetingInviteesModel.qml"
+ "AboutDialog.qml"
+)
+
+add_custom_command(
+ TARGET qml_basic_item PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/basic_items.html
+ ${CMAKE_CURRENT_BINARY_DIR}/basic_items.html
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/basic_items.html
+)
+
+set(CMAKE_CXX_FLAGS_DEBUG "-g0 -O3")
+qt6_import_qml_plugins(qml_basic_item INCLUDE Qt::QtQuick2Plugin)
+qt_internal_add_resource (qml_basic_item "qmake_immediate"
+ PREFIX
+ "/"
+ FILES
+ ${qmake_immediate_resource_files}
+)
+
diff --git a/tests/manual/wasm/a11y/qml_basic_item/MeetingInviteesModel.qml b/tests/manual/wasm/a11y/qml_basic_item/MeetingInviteesModel.qml
new file mode 100644
index 0000000000..e22160c6e0
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/MeetingInviteesModel.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+
+ListModel {
+ id: contactModel
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/MeetingInviteesPage.qml b/tests/manual/wasm/a11y/qml_basic_item/MeetingInviteesPage.qml
new file mode 100644
index 0000000000..6ed998738a
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/MeetingInviteesPage.qml
@@ -0,0 +1,139 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+GroupBox {
+ id: grpBox
+ title: "Add Invitees"
+ height: parent.height - 10
+ property alias nextButton: nextButton
+ property alias dateAndTime: dateAndTime
+ property string inviteesNameEmail
+ ColumnLayout {
+ id: columnLayout
+ anchors.fill: parent
+ anchors.rightMargin: 10
+ anchors.leftMargin: 10
+ spacing: 10
+ Text {
+ id: dateAndTime
+ width: 500
+ height: 50
+ text: "Select Date & Time from Chrono Menu"
+ font.pixelSize: 14
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "This is time and date label"
+ }
+
+ RowLayout {
+ id: rLayout
+ spacing: 6
+
+ Text {
+
+ text: "Name:"
+ font.pixelSize: 14
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "Provide invitee's name"
+ }
+ Rectangle {
+ width: 250
+ height: 22
+ color: "grey"
+ border.width: 1
+ border.color: "black"
+ Layout.fillWidth: true
+ Layout.minimumWidth: 50
+ Layout.preferredWidth: 200
+ Layout.maximumWidth: 300
+ TextEdit {
+ id: textEdit
+ clip: true
+ anchors.fill: parent
+ anchors.leftMargin: 6
+ anchors.centerIn: parent
+ Accessible.role: Accessible.EditableText
+ Accessible.name: text
+ Accessible.description: "Write invitee's name"
+ }
+ }
+ Text {
+ text: "Email:"
+ font.pixelSize: 14
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "Provide invitee's Email"
+ }
+ Rectangle {
+ width: 250
+ height: 22
+ color: "grey"
+ border.width: 1
+ border.color: "black"
+ Layout.fillWidth: true
+ Layout.minimumWidth: 50
+ Layout.preferredWidth: 200
+ Layout.maximumWidth: 300
+ TextEdit {
+ id: textEmail
+ clip: true
+ width: 250
+ height: 20
+ anchors.leftMargin: 6
+ anchors.centerIn: parent
+ anchors.fill: parent
+ Accessible.role: Accessible.EditableText
+ Accessible.name: text
+ Accessible.description: "Write invitee's Email"
+ }
+ }
+ }
+ Button {
+ id: addButton
+ text: "Add"
+ Layout.alignment: Qt.AlignRight
+ anchors.rightMargin: 10
+ Accessible.role: Accessible.Button
+ Accessible.name: text
+ Accessible.description: "Press Button to add invitee's in the list"
+ onClicked: {
+ var name = textEdit.text
+ var email = textEmail.text
+ if (inviteesNameEmail == "") {
+ inviteesNameEmail += name + "&lt;" + email + "&gt;"
+ } else {
+ inviteesNameEmail += ", " + name + "&lt;" + email + "&gt;"
+ }
+
+ meetingInviteesModel.append({
+ "name": name,
+ "email": email
+ })
+ textEdit.text = ""
+ textEmail.text = ""
+ }
+ }
+ MeetingInviteesModel {
+ id: meetingInviteesModel
+ }
+
+ WasmListView {
+ Layout.fillWidth: true
+ height: 200
+ listModel: meetingInviteesModel
+ }
+
+ Button {
+ id: nextButton
+ text: "Next"
+ Layout.alignment: Qt.AlignRight
+ Accessible.role: Accessible.Button
+ Accessible.name: text
+ Accessible.description: "Press Button to go to next meeting Scheduler"
+ }
+ }
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/MeetingSchedulerPage.qml b/tests/manual/wasm/a11y/qml_basic_item/MeetingSchedulerPage.qml
new file mode 100644
index 0000000000..2907c07886
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/MeetingSchedulerPage.qml
@@ -0,0 +1,163 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+GroupBox {
+ title: "Meeting Scheduler"
+ property alias nextButton: nextButton
+ property string meetingOccurrence: radioOnce.checked ? "Once" : "Weekly"
+ property string onlineOfflineStatus: onlineMeeting.checked ? "Online" : "Offline"
+ property int roomNumber: room.value
+ property int calendarWeek: calendar.value
+ property string meetingDescription: description.text
+ property alias description: description
+ height: parent.height-10
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 10
+ Row {
+ spacing: 10
+ RadioButton {
+ id: radioOnce
+ text: qsTr("Once")
+ checked: true
+ Accessible.role: Accessible.RadioButton
+ Accessible.name: text
+ Accessible.description: "Select this option if you want meeting once a week"
+ Accessible.checkable: true
+
+ Accessible.onToggleAction: {
+ toggle()
+ }
+ }
+ RadioButton {
+ id: radioWeekly
+ text: qsTr("Weekly")
+ Accessible.role: Accessible.RadioButton
+ Accessible.name: text
+ Accessible.description: "Select this option if you want meeting weekly"
+ Accessible.checkable: true
+
+ Accessible.onToggleAction: {
+ toggle()
+ }
+ }
+ }
+
+ CheckBox {
+ id: onlineMeeting
+ text: "Select if meeting will be online"
+ Accessible.role: Accessible.CheckBox
+ Accessible.name: text
+ Accessible.description: "Select this option if you want an online meeting"
+ Accessible.checkable: true
+
+ Accessible.onToggleAction: {
+ toggle()
+ }
+ }
+
+ Row {
+ spacing: 10
+ enabled: onlineMeeting.checkState === Qt.Unchecked
+
+ Label {
+ text: "Select Meeting room"
+ height: 50
+ verticalAlignment: Text.AlignVCenter
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "Select a meeting room"
+ }
+
+ SpinBox {
+ id: room
+ from: 0
+ to: 10
+ value: 0
+ Accessible.role: Accessible.SpinBox
+ Accessible.name: "Room number"
+ Accessible.description: "Select a room for the meeting"
+ Accessible.editable: true
+
+ Accessible.onDecreaseAction: {
+ decrease()
+ }
+ Accessible.onIncreaseAction: {
+ increase()
+ }
+ }
+ }
+
+ Row {
+ spacing: 10
+
+ Label {
+ text: "Calendar Week"
+ height: 50
+ verticalAlignment: Text.AlignVCenter
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "Select the calendar week"
+ }
+
+ Slider {
+ id: calendar
+ from: 1
+ to: 52
+ Accessible.role: Accessible.Slider
+ Accessible.name: "Calendar Week"
+ Accessible.description: "Select the week"
+ Accessible.onDecreaseAction: {
+ decrease()
+ }
+ Accessible.onIncreaseAction: {
+ increase()
+ }
+ }
+ }
+
+ Label {
+ text: "Meeting Description"
+ height: 50
+ verticalAlignment: Text.AlignVCenter
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "Meeting Description"
+ }
+
+ Rectangle {
+ Layout.preferredHeight: 100
+ Layout.preferredWidth: 310
+ border.color: "black"
+ border.width: 1
+ ScrollView {
+ id: view
+ anchors.fill: parent
+ clip: true
+
+ TextArea {
+ id: description
+ wrapMode: TextEdit.Wrap
+ readOnly: false
+ font.pixelSize: 16
+ Accessible.role: Accessible.EditableText
+ Accessible.editable: true
+ Accessible.name: "Enter description"
+ Accessible.description: "Describe in short "
+ Accessible.multiLine: true
+ }
+ }
+ }
+ Button {
+ id: nextButton
+ text: "Next"
+ Layout.alignment: Qt.AlignRight
+ Accessible.role: Accessible.Button
+ Accessible.name: text
+ Accessible.description: "Press Button to go to next meeting Summary"
+ }
+ }
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/MeetingSummary.qml b/tests/manual/wasm/a11y/qml_basic_item/MeetingSummary.qml
new file mode 100644
index 0000000000..142b05fc99
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/MeetingSummary.qml
@@ -0,0 +1,117 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+GroupBox {
+ id: root
+ title: "Summary"
+ height: parent.height-10
+ property string meetingOccurrence: "Once"
+ property string onlineOfflineStatus: "offline"
+ property int roomNumber: 0
+ property int calendarWeek: 1
+ property string meetingDescription: "No Description"
+
+ property string inviteesNameEmail: ""
+ property bool addedReadRequest: false
+ clip: true
+
+ Flickable {
+ id: flickable
+ width: parent.width - 10
+ height: parent.height - 10
+ contentHeight: meetingHeader.contentHeight + textSummary.contentHeight + 50
+ contentWidth: parent.width - 10
+ clip: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {
+ policy: ScrollBar.AlwaysOn
+
+ Accessible.role: Accessible.ScrollBar
+ Accessible.name: "Vertical ScrollBar"
+ Accessible.description: "Use this to scroll summary page"
+ Accessible.onDecreaseAction: {
+ decrease()
+ }
+ Accessible.onIncreaseAction: {
+ increase()
+ }
+ }
+
+ Label {
+ id: meetingHeader
+ height: 30
+ text: "Meeting Details"
+ font.bold: true
+ font.pixelSize: 24
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "Title"
+ }
+
+ Connections {
+ target: wasmToolbar
+ function onCancelThisMeeting() {
+ textSummary.text = ""
+ addedReadRequest = false
+ }
+ function onRequestReadReceipt() {
+ if (addedReadRequest == false) {
+ var temp = textSummary.text
+ textSummary.text = "<b>Read receipt requested</b><br>" + temp
+ addedReadRequest = true
+ }
+ }
+ }
+ TextEdit {
+ id: textSummary
+ anchors {
+ left: parent.left
+ top: meetingHeader.bottom
+ topMargin: 10
+ leftMargin: 10
+ }
+ width: parent.width - 50
+ font.pixelSize: 16
+ textFormat: TextEdit.RichText
+ text: qsTr((" Occurrence:<b> %1 </b> <br>
+ Meeting to be held: <b>%2</b> <br>
+ Invitees:<b> %3 </b> <br>
+ Meeting Room: <b> %4 </b> <br>
+ Starts in Calendar Week:<b> %5</b> <br>
+ Meeting Description:<b> %6</b>")
+ .arg(meetingOccurrence)
+ .arg(onlineOfflineStatus)
+ .arg(inviteesNameEmail)
+ .arg(roomNumber)
+ .arg(calendarWeek)
+ .arg(meetingDescription))
+
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ readOnly: true
+ clip: true
+ Accessible.readOnly: readOnly
+ Accessible.role: Accessible.StaticText
+ Accessible.name: textSummary.getText(0, textSummary.length)
+ Accessible.description: "A short summary of the meeting details"
+ onVisibleChanged: {
+ textSummary.text = qsTr((" Occurrence:<b> %1 </b> <br>
+ Meeting to be held: <b>%2</b> <br>
+ Invitees:<b> %3 </b> <br>
+ Meeting Room: <b> %4 </b> <br>
+ Starts in Calendar Week:<b> %5</b> <br>
+ Meeting Description:<b> %6</b>").arg(meetingOccurrence).arg
+ (onlineOfflineStatus).arg
+ (inviteesNameEmail).arg(roomNumber).arg
+ (calendarWeek).arg(meetingDescription))
+ if (addedReadRequest == true) {
+ var temp = textSummary.text
+ textSummary.text = "<b>Read receipt requested<b><br>" + temp
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/MeetingTabs.qml b/tests/manual/wasm/a11y/qml_basic_item/MeetingTabs.qml
new file mode 100644
index 0000000000..10507011f7
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/MeetingTabs.qml
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+Item {
+ id: root
+ width: parent.width -10
+ height:parent.height -10
+ property color itemColor: "lightgrey"
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ color: itemColor
+ }
+ enum Types {
+ Invitees,
+ Scheduler,
+ Summary
+ }
+ property alias setTime: invitees.dateAndTime
+ property alias textSummary: scheduler.description
+ property alias currentIndex: meetingTabs.currentIndex
+ TabBar {
+ id: meetingTabs
+ width: parent.width
+ TabButton {
+ text: qsTr("Meeting Invitees")
+ width:meetingTabs.width/3
+ Accessible.role: Accessible.PageTab
+ Accessible.name: text
+ Accessible.description: "Tab to add meeting invitees"
+ }
+ TabButton {
+ text: qsTr("Meeting Scheduler")
+ width:meetingTabs.width/3
+ Accessible.role: Accessible.PageTab
+ Accessible.name: text
+ Accessible.description: "Tab to add a schedule"
+ }
+ TabButton {
+ text: qsTr("Summary")
+ width:meetingTabs.width/3
+ Accessible.role: Accessible.PageTab
+ Accessible.name: text
+ Accessible.description: "Tab to add meeting summary"
+ }
+
+ Accessible.role: Accessible.PageTabList
+ Accessible.name: "Meetings Tab Bar"
+ Accessible.description: "A Tab list of tabs to setup a meeting"
+ }
+
+ StackLayout {
+ width: parent.width - 20
+ currentIndex: meetingTabs.currentIndex
+ anchors {
+ left: parent.left
+ leftMargin: 10
+ top: meetingTabs.bottom
+ topMargin: 20
+ }
+
+ MeetingInviteesPage {
+ id: invitees
+ nextButton.Accessible.onPressAction: {
+ meetingTabs.currentIndex = MeetingTabs.Types.Scheduler
+ }
+ nextButton.onReleased: {
+ meetingTabs.currentIndex = MeetingTabs.Types.Scheduler
+ }
+ }
+
+ MeetingSchedulerPage {
+ id: scheduler
+ nextButton.Accessible.onPressAction: {
+ meetingTabs.currentIndex = MeetingTabs.Types.Summary
+ }
+ nextButton.onReleased: {
+ meetingTabs.currentIndex = MeetingTabs.Types.Summary
+ }
+ }
+
+ MeetingSummary {
+ id: activityTab
+ meetingOccurrence: scheduler.meetingOccurrence
+ onlineOfflineStatus: scheduler.onlineOfflineStatus
+ roomNumber: scheduler.roomNumber
+ calendarWeek: scheduler.calendarWeek
+ meetingDescription: scheduler.meetingDescription
+ inviteesNameEmail: invitees.inviteesNameEmail
+ }
+ }
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/WasmListView.qml b/tests/manual/wasm/a11y/qml_basic_item/WasmListView.qml
new file mode 100644
index 0000000000..a8d3ca459c
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/WasmListView.qml
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Rectangle {
+ id: listRect
+ width: parent.width
+ height: parent.height / 2
+ color: "bisque"
+ property alias listModel: wasmList.model
+
+ ListView {
+ id: wasmList
+ anchors.fill: listRect
+ clip: true
+ Accessible.role: Accessible.List
+ Accessible.name: "ListView"
+ Accessible.description: "List view to add the names and emails of the invitees"
+ headerPositioning: ListView.OverlayHeader
+ focus: true
+ header: Component {
+ Row {
+ z: 2
+ spacing: 2
+ Rectangle {
+ color: "#808080"
+ Text {
+ text: "Name"
+ width: listRect.width / 2
+ font.bold: true
+ horizontalAlignment: Text.AlignLeft
+ Layout.fillWidth: true
+ color: "black"
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "Invitee's name"
+ }
+ width: childrenRect.width
+ height: childrenRect.height
+ }
+ Rectangle {
+ color: "#808080"
+ Text {
+ text: "Email"
+ width: listRect.width / 2
+ color: 'black'
+ font.bold: true
+ horizontalAlignment: Text.AlignLeft
+ Layout.fillWidth: true
+ Accessible.role: Accessible.StaticText
+ Accessible.name: text
+ Accessible.description: "Invitee's email address"
+ }
+ width: childrenRect.width
+ height: childrenRect.height
+ }
+ }
+ }
+ delegate: Rectangle {
+ width: wasmList.width
+ height: 50
+ color: index % 2 === 0 ? "#F5F5F5" : "#FFFFFF"
+
+ Text {
+ id: inviteeName
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ leftPadding: 10
+ width: listRect.width / 2
+ text: model.name
+ font.bold: true
+ Accessible.role: Accessible.ListItem
+ Accessible.name: text
+ Accessible.description: text
+ color: "black"
+ }
+
+ Text {
+ id: inviteeEmail
+ width: listRect.width / 2
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ rightPadding: 10
+ text: model.email
+ font.bold: true
+ color: "black"
+ Accessible.role: Accessible.ListItem
+ Accessible.name: text
+ Accessible.description: text
+ }
+ }
+ }
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/WasmMenu.qml b/tests/manual/wasm/a11y/qml_basic_item/WasmMenu.qml
new file mode 100644
index 0000000000..94e25a63d0
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/WasmMenu.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+MenuBar {
+ Accessible.role: Accessible.MenuBar
+ signal showTime
+ signal showDate
+ signal showAboutDialog
+ Menu {
+ title: "Chrono"
+
+ MenuItem {
+ text: "Date"
+ Accessible.role: Accessible.ButtonMenu
+ Accessible.name: text
+ Accessible.description: text
+ onTriggered: {
+ showDate()
+ }
+ }
+ MenuItem {
+ text: "Time"
+ Accessible.role: Accessible.ButtonMenu
+ Accessible.name: text
+ Accessible.description: text
+ onTriggered: {
+ showTime()
+ }
+ }
+ }
+ Menu {
+ title: "Help"
+
+ MenuItem {
+ text: "About"
+ Accessible.role: Accessible.ButtonMenu
+ Accessible.name: text
+ Accessible.description: text
+ onTriggered: {
+ showAboutDialog()
+ }
+ }
+ }
+ delegate: MenuBarItem {
+ id: menuBarItem
+ Accessible.role: Accessible.ButtonMenu
+ Accessible.name: menuBarItem.text
+ Accessible.description: menuBarItem.text
+
+ }
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/WasmToolBar.qml b/tests/manual/wasm/a11y/qml_basic_item/WasmToolBar.qml
new file mode 100644
index 0000000000..1a12275582
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/WasmToolBar.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ToolBar {
+ id: mainTooBar
+ signal cancelThisMeeting
+ signal requestReadReceipt
+ property bool cancelMeeting: false
+ RowLayout {
+
+ spacing: 10
+ Accessible.role: Accessible.ToolBar
+ ToolButton {
+ id: cancelButton
+ text: qsTr("Clear")
+ Accessible.role: Accessible.Button
+ Accessible.name: text
+ Accessible.description: text
+ onClicked: {
+ cancelThisMeeting()
+ }
+ }
+ ToolButton {
+ id: readRequestButton
+ text: qsTr("Request receipt")
+ Accessible.role: Accessible.Button
+ Accessible.name: text
+ Accessible.description: text
+ onClicked: {
+ requestReadReceipt()
+ }
+ }
+ }
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/basic_items.html b/tests/manual/wasm/a11y/qml_basic_item/basic_items.html
new file mode 100644
index 0000000000..8ef702b5ed
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/basic_items.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+<script src="qml_basic_item.js" async></script>
+
+<script>
+ window.onload = async () => {
+ let qt_instance = await qml_basic_item_entry({
+ qtContainerElements: [document.getElementById("qt_container")],
+ });
+ }
+</script>
+
+
+</head>
+
+<body>
+ <h1>Qt Accessibility Tester</H1>
+ <div id="qt_container" style="width:640px; height:640px"></div>
+</body>
+
+</html>
diff --git a/tests/manual/wasm/a11y/qml_basic_item/main.cpp b/tests/manual/wasm/a11y/qml_basic_item/main.cpp
new file mode 100644
index 0000000000..316fb6f3dd
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QtQuick/QQuickView>
+#include <QtQml/qqmlapplicationengine.h>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+ return app.exec();
+}
diff --git a/tests/manual/wasm/a11y/qml_basic_item/main.qml b/tests/manual/wasm/a11y/qml_basic_item/main.qml
new file mode 100644
index 0000000000..c710740f90
--- /dev/null
+++ b/tests/manual/wasm/a11y/qml_basic_item/main.qml
@@ -0,0 +1,97 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+
+ApplicationWindow {
+ visible: true
+ width: 640
+ height: 600
+ AboutDialog {
+ id: aboutDialog
+ anchors.centerIn: parent
+ }
+ WasmMenu {
+ id: wasmMenu
+ Accessible.focusable: true
+ focusPolicy: Qt.StrongFocus
+ focus: true
+ property string timeCaption: "Initiated at :"
+ anchors {
+ left: parent.left
+ leftMargin: 20
+ top: parent.top
+ }
+ function getCurrentDate() {
+ var currentDate = new Date()
+ var year = currentDate.getFullYear()
+ var month = currentDate.getMonth() + 1
+ var day = currentDate.getDate()
+ return day + "/" + month + "/" + year
+ }
+ function getCurrentTime() {
+ var currentDate = new Date()
+ var hours = currentDate.getHours()
+ var minutes = currentDate.getMinutes()
+ var seconds = currentDate.getSeconds()
+ return hours + ":" + minutes + ":" + seconds
+ }
+ function removeTextAfterIndex(rmText, currdateTime) {
+ var index = currdateTime.indexOf(rmText)
+ if (index !== -1) {
+ currdateTime = currdateTime.substring(0, index)
+ }
+ return currdateTime
+ }
+ onShowTime: {
+ timeCaption = removeTextAfterIndex(", time:", timeCaption)
+ timeCaption += ", time: " + getCurrentTime()
+ meetingTabs.setTime.text = timeCaption
+ }
+ onShowDate: {
+ timeCaption = removeTextAfterIndex(" date:", timeCaption)
+ timeCaption += " date: " + getCurrentDate()
+ meetingTabs.setTime.text = timeCaption
+ }
+ onShowAboutDialog: {
+ aboutDialog.open()
+ }
+ }
+
+ WasmToolBar {
+ id: wasmToolbar
+ anchors {
+ left: parent.left
+ leftMargin: 20
+ top: wasmMenu.bottom
+ topMargin: 3
+ }
+ enabled: meetingTabs.currentIndex === MeetingTabs.Types.Summary ? true : false
+ }
+
+ Rectangle {
+ width: parent.width - 30
+ height: parent.height - wasmToolbar.height - wasmMenu.height - 30
+ border.color: "black"
+ border.width: 1
+ id:outerRect
+ anchors {
+ left: parent.left
+ leftMargin: 20
+ top: wasmToolbar.bottom
+ topMargin: 10
+ bottomMargin: 10
+ }
+
+ MeetingTabs {
+ id: meetingTabs
+ parent:outerRect
+ anchors {
+ centerIn: parent
+ }
+ height: parent.height - 20
+ width: parent.width - 20
+ }
+ }
+}
diff --git a/tests/manual/window/CMakeLists.txt b/tests/manual/window/CMakeLists.txt
new file mode 100644
index 0000000000..702c7aaa4f
--- /dev/null
+++ b/tests/manual/window/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(rotation)
diff --git a/tests/manual/window/rotation/CMakeLists.txt b/tests/manual/window/rotation/CMakeLists.txt
new file mode 100644
index 0000000000..9a1c541f73
--- /dev/null
+++ b/tests/manual/window/rotation/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(windowrotation VERSION 0.1 LANGUAGES CXX)
+
+find_package(Qt6 COMPONENTS Core Gui Quick QuickControls2 REQUIRED)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(windowrotation
+ MANUAL_FINALIZATION
+ main.cpp
+)
+
+target_link_libraries(windowrotation PRIVATE
+ Qt6::Quick Qt6::QuickControls2
+)
+
+qt_finalize_executable(windowrotation)
+
+qt_add_qml_module(windowrotation
+ URI Main
+ VERSION 1.0
+ QML_FILES "Main.qml"
+)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/tests/manual/window/rotation/Main.qml b/tests/manual/window/rotation/Main.qml
new file mode 100644
index 0000000000..5164334c7e
--- /dev/null
+++ b/tests/manual/window/rotation/Main.qml
@@ -0,0 +1,124 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+
+Window {
+ id: window
+ width: 640
+ height: 480
+ visible: true
+
+ Rectangle {
+ anchors.fill: parent
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "white" }
+ GradientStop { position: 1.0; color: "black" }
+ }
+ Text {
+ anchors.centerIn: parent
+ text: "Screen orientation: " + orientationName(Screen.orientation) + "\n" +
+ "Content orientation: " + orientationName(window.contentOrientation)
+ }
+ }
+
+ Row {
+ Button {
+ text: "Toggle"
+ onClicked: popup.visible ? popup.close() : popup.open()
+ focus: true
+ }
+ ComboBox {
+ id: orientationSelection
+ model: ListModel {
+ ListElement {
+ name: "Primary"
+ value: Qt.PrimaryOrientation
+ }
+ ListElement {
+ name: "Portrait"
+ value: Qt.PortraitOrientation
+ }
+ ListElement {
+ name: "Landscape"
+ value: Qt.LandscapeOrientation
+ }
+ ListElement {
+ name: "Inverted Portrait"
+ value: Qt.InvertedPortraitOrientation
+ }
+ ListElement {
+ name: "Inverted Landscape"
+ value: Qt.InvertedLandscapeOrientation
+ }
+ }
+ textRole: "name"
+ valueRole: "value"
+
+ onActivated: updateOrientation()
+ }
+ Keys.onLeftPressed: (event) => {
+ window.contentItem.rotation -= event.modifiers & Qt.ShiftModifier ? 10 : 1;
+ }
+ Keys.onRightPressed: (event) => {
+ window.contentItem.rotation += event.modifiers & Qt.ShiftModifier ? 10 : 1;
+ }
+ }
+
+ Popup {
+ id: popup
+ anchors.centerIn: parent
+ width: 320
+ height: 240
+ modal: false
+ dim: true
+ Text {
+ text: "Hello Popup"
+ anchors.fill: parent
+ }
+
+ Overlay.modeless: Rectangle {
+ opacity: 0.5
+ color: "blue"
+ }
+ }
+
+ Drawer {
+ Text {
+ anchors.centerIn: parent
+ text: "Hello Left Drawer"
+ }
+ edge: Qt.LeftEdge
+ height: parent.height
+ }
+ Drawer {
+ Text {
+ anchors.centerIn: parent
+ text: "Hello Right Drawer"
+ }
+ edge: Qt.RightEdge
+ height: parent.height
+ }
+
+ function updateOrientation() {
+ window.contentOrientation = orientationSelection.currentValue;
+ let angle = Screen.angleBetween(Screen.orientation, window.contentOrientation);
+ console.log("Rotation between " + Screen.orientation + " and " + window.contentOrientation + " should be " + angle);
+ window.contentItem.rotation = angle;
+ }
+
+ function orientationName(orientation) {
+ for (let i = 0; i < orientationSelection.model.count; i++) {
+ let entry = orientationSelection.model.get(i);
+ if (entry.value === orientation)
+ return entry.name
+ }
+ }
+
+ Component.onCompleted: {
+ Screen.orientationChanged.connect(updateOrientation);
+ updateOrientation();
+ }
+}
diff --git a/tests/manual/window/rotation/main.cpp b/tests/manual/window/rotation/main.cpp
new file mode 100644
index 0000000000..982bbcf178
--- /dev/null
+++ b/tests/manual/window/rotation/main.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
+ &app, []() { QCoreApplication::exit(-1); },
+ Qt::QueuedConnection);
+ engine.loadFromModule("Main", "Main");
+
+ return app.exec();
+}
diff --git a/tests/manual/windowembedding/CMakeLists.txt b/tests/manual/windowembedding/CMakeLists.txt
new file mode 100644
index 0000000000..6ad5167186
--- /dev/null
+++ b/tests/manual/windowembedding/CMakeLists.txt
@@ -0,0 +1,86 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(windowembedding LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick Widgets)
+find_package(Qt6 QUIET OPTIONAL_COMPONENTS Multimedia)
+
+qt_standard_project_setup(REQUIRES 6.7)
+
+add_subdirectory("../../../examples/quick/shared" "shared")
+
+qt_add_executable(windowembedding
+ WIN32
+ MACOSX_BUNDLE
+ main.cpp
+)
+
+target_link_libraries(windowembedding PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
+ Qt6::Widgets
+)
+
+add_dependencies(windowembedding windowembedding_shared)
+
+qt_add_qml_module(windowembedding
+ URI windowembeddingexample
+ QML_FILES
+ "windowembedding.qml"
+ "QtLogo.qml"
+ "examples/clipping.qml"
+ "examples/map.qml"
+ "examples/stacking.qml"
+ "examples/transform.qml"
+ "examples/video.qml"
+ "examples/webview.qml"
+ "examples/widget.qml"
+ RESOURCES
+ "qtlogo-white.png"
+ "qtlogo-green.png"
+ NO_LINT
+)
+
+if(TARGET Qt::Multimedia)
+ target_link_libraries(windowembedding PRIVATE Qt6::Multimedia)
+ qt_target_qml_sources(windowembedding
+ QML_FILES "examples/hdr.qml"
+ RESOURCES "examples/hdrtest.mp4"
+ )
+endif()
+
+if(APPLE)
+ enable_language(OBJCXX)
+ set_source_files_properties(main.cpp PROPERTIES LANGUAGE OBJCXX)
+ target_link_libraries(windowembedding PRIVATE
+ "-framework WebKit"
+ "-framework MapKit"
+ "-framework AVFoundation"
+ "-framework AVKit"
+ )
+ if(NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
+ # Xcode fails to sign Contents/PlugIns/shared/qmldir
+ set_target_properties(windowembedding PROPERTIES
+ XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
+ )
+ endif()
+endif()
+
+install(TARGETS windowembedding
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET windowembedding
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/tests/manual/windowembedding/QtLogo.qml b/tests/manual/windowembedding/QtLogo.qml
new file mode 100644
index 0000000000..5957a37b84
--- /dev/null
+++ b/tests/manual/windowembedding/QtLogo.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+WindowContainer {
+ id: logoContainer
+ width: 42; height: 30
+ window: Window {
+ id: videoLogo
+ visible: true
+ color: "transparent"
+
+ Image {
+ source: {
+ var logo = "qtlogo-"
+ logo += mouseArea.pressed ? "green" : "white"
+ logo += ".png"
+ return logo
+ }
+ anchors.fill: parent
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ }
+ }
+ }
+}
diff --git a/tests/manual/windowembedding/examples/clipping.qml b/tests/manual/windowembedding/examples/clipping.qml
new file mode 100644
index 0000000000..fa3d27db72
--- /dev/null
+++ b/tests/manual/windowembedding/examples/clipping.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ id: rootItem
+ gradient: Gradient.TemptingAzure
+ width: 500; height: 500
+
+ Flickable {
+ id: flickable
+ anchors.fill: parent
+ anchors.margins: 20
+ contentWidth: 2000; contentHeight: 2000
+
+ clip: true
+
+ Window {
+ id: redWindow
+ flags: Qt.WindowTransparentForInput
+ color: "lightgray"
+ visible: true
+ width: 200; height: 200
+ parent: flickable.contentItem
+
+ Image {
+ source: "https://placedog.net/500/500?random"
+ anchors.fill: parent
+ }
+ }
+ }
+}
diff --git a/tests/manual/windowembedding/examples/hdr.qml b/tests/manual/windowembedding/examples/hdr.qml
new file mode 100644
index 0000000000..31efe8a51c
--- /dev/null
+++ b/tests/manual/windowembedding/examples/hdr.qml
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtMultimedia
+
+Rectangle {
+ id: rootItem
+ width: 500; height: 500
+
+ // Show the difference in color space for the two windows
+ color: "#e0a32e"
+
+ WindowContainer {
+ window: Window {
+ color: rootItem.color
+
+ VideoOutput {
+ id: videoOutput
+ anchors.fill: parent
+ }
+
+ // Trigger Qt Quick to use a HDR-enabled swap chain
+ property var _qt_sg_hdr_format: "scrgb"
+ }
+
+ anchors {
+ fill: parent
+ topMargin: 20
+ bottomMargin: 20
+ }
+ }
+
+ MediaPlayer {
+ id: mediaPlayer
+ videoOutput: videoOutput
+ source: "hdrtest.mp4"
+ loops: MediaPlayer.Infinite
+ autoPlay: true
+ }
+}
diff --git a/tests/manual/windowembedding/examples/hdrtest.mp4 b/tests/manual/windowembedding/examples/hdrtest.mp4
new file mode 100644
index 0000000000..a0407c33b9
--- /dev/null
+++ b/tests/manual/windowembedding/examples/hdrtest.mp4
Binary files differ
diff --git a/tests/manual/windowembedding/examples/map.qml b/tests/manual/windowembedding/examples/map.qml
new file mode 100644
index 0000000000..4f581cbe68
--- /dev/null
+++ b/tests/manual/windowembedding/examples/map.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import ".."
+
+Rectangle {
+ id: rootItem
+ gradient: Gradient.TemptingAzure
+ width: 500; height: 500
+
+ WindowContainer {
+ id: foreignWindowContainer
+ window: mapWindow
+ anchors.fill: parent
+ anchors.margins: 20
+
+ QtLogo {
+ anchors {
+ top: parent.top
+ left: parent.left
+ margins: 10
+ }
+ }
+ }
+}
diff --git a/tests/manual/windowembedding/examples/stacking.qml b/tests/manual/windowembedding/examples/stacking.qml
new file mode 100644
index 0000000000..7634bb0bff
--- /dev/null
+++ b/tests/manual/windowembedding/examples/stacking.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ id: rootItem
+ gradient: Gradient.TemptingAzure
+ width: 500; height: 500
+
+ property int windowZ: 0
+
+ Window {
+ id: redWindow
+ color: "red"
+ visible: true
+ parent: rootItem
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: redWindow.z = ++rootItem.windowZ
+ }
+ }
+
+ Window {
+ id: greenWindow
+ color: "green"
+ visible: true
+ parent: rootItem
+ x: 100; y: 100
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: greenWindow.z = ++rootItem.windowZ
+ }
+ }
+
+ Window {
+ id: blueWindow
+ color: "blue"
+ visible: true
+ parent: rootItem
+ x: 200; y: 200
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: blueWindow.z = ++rootItem.windowZ
+ }
+ }
+}
diff --git a/tests/manual/windowembedding/examples/transform.qml b/tests/manual/windowembedding/examples/transform.qml
new file mode 100644
index 0000000000..c07dae5850
--- /dev/null
+++ b/tests/manual/windowembedding/examples/transform.qml
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ id: rootItem
+ gradient: Gradient.TemptingAzure
+ width: 500; height: 500
+
+ Rectangle {
+ id: rectangle
+ width: 200; height: 200
+ color: "lightgray"
+
+ transformOrigin: Item.TopLeft
+ transform: [
+ Translate { id: translation; x: 50; y: 50 }
+ ]
+ //scale: 3 // FIXME: Doesn't work when assigned like this
+
+ Window {
+ id: childWindow
+ objectName: "childWindow"
+ visible: true
+ parent: rectangle
+ width: 200; height: 200
+
+ Image {
+ source: "https://placedog.net/500/500?random"
+ anchors.fill: parent
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ rectangle.scale += 0.1
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/windowembedding/examples/video.qml b/tests/manual/windowembedding/examples/video.qml
new file mode 100644
index 0000000000..c9a8d289a3
--- /dev/null
+++ b/tests/manual/windowembedding/examples/video.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import ".."
+
+Rectangle {
+ id: rootItem
+ gradient: Gradient.TemptingAzure
+ width: 500; height: 500
+ visible: true
+
+ WindowContainer {
+ id: foreignWindowContainer
+ window: videoWindow
+ anchors.fill: parent
+ anchors.margins: 20
+
+ QtLogo {
+ anchors {
+ top: parent.top
+ left: parent.left
+ margins: 10
+ }
+ }
+ }
+}
diff --git a/tests/manual/windowembedding/examples/webview.qml b/tests/manual/windowembedding/examples/webview.qml
new file mode 100644
index 0000000000..50f03ac059
--- /dev/null
+++ b/tests/manual/windowembedding/examples/webview.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Rectangle {
+ id: rootItem
+ gradient: Gradient.TemptingAzure
+ width: 500; height: 500
+ visible: true
+
+ WindowContainer {
+ id: foreignWindowContainer
+ window: webViewWindow
+ anchors.fill: parent
+ anchors.margins: 20
+ }
+}
diff --git a/tests/manual/windowembedding/examples/widget.qml b/tests/manual/windowembedding/examples/widget.qml
new file mode 100644
index 0000000000..5c2b612c3f
--- /dev/null
+++ b/tests/manual/windowembedding/examples/widget.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import ".."
+
+Rectangle {
+ id: rootItem
+ gradient: Gradient.TemptingAzure
+ width: 500; height: 500
+ visible: true
+
+ WindowContainer {
+ id: foreignWindowContainer
+ window: widgetWindow
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ margins: 20
+ }
+ // Anchoring to bottom will result in the widget resizing during
+ // the transition, which slows down things quite a bit, so as a
+ // workaround, base the height of the widget on the outer window.
+ height: root.height - 80
+ }
+}
diff --git a/tests/manual/windowembedding/main.cpp b/tests/manual/windowembedding/main.cpp
new file mode 100644
index 0000000000..f94affbb5c
--- /dev/null
+++ b/tests/manual/windowembedding/main.cpp
@@ -0,0 +1,111 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtGui>
+#include <QtWidgets>
+#include <QQmlEngine>
+#include <QQuickView>
+#include <QQmlContext>
+
+class TestWindow : public QRasterWindow
+{
+public:
+ using QRasterWindow::QRasterWindow;
+ TestWindow(const QBrush &brush) : m_brush(brush) {}
+
+protected:
+ void mousePressEvent(QMouseEvent *) override
+ {
+ m_pressed = true;
+ update();
+ }
+
+ void mouseReleaseEvent(QMouseEvent *) override
+ {
+ m_pressed = false;
+ update();
+ }
+
+ void paintEvent(QPaintEvent *event) override
+ {
+ QPainter painter(this);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(event->rect(), m_pressed ? QGradient(QGradient::JuicyPeach) : m_brush);
+ }
+
+private:
+ QBrush m_brush = QGradient(QGradient::DustyGrass);
+ bool m_pressed = false;
+};
+
+#ifdef Q_OS_APPLE
+#include <WebKit/WebKit.h>
+#include <MapKit/MapKit.h>
+#include <AVKit/AVKit.h>
+#endif
+
+int main(int argc, char* argv[])
+{
+ QApplication app(argc,argv);
+ app.setOrganizationName("QtProject");
+ app.setOrganizationDomain("qt-project.org");
+ app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName());
+
+ QQuickView view;
+ view.connect(view.engine(), &QQmlEngine::quit, &app, &QCoreApplication::quit);
+
+#ifdef Q_OS_MACOS
+ view.engine()->addImportPath(app.applicationDirPath() + QStringLiteral("/../PlugIns"));
+#endif
+
+ TestWindow testWindow;
+
+ QWindow *mapWindow = nullptr;
+ QWindow *webViewWindow = nullptr;
+ QWindow *videoWindow = nullptr;
+
+#ifdef Q_OS_APPLE
+ auto *webView = [WKWebView new];
+ [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.qt.io"]]];
+ webViewWindow = QWindow::fromWinId(WId(webView));
+
+ auto *mapView = [MKMapView new];
+ mapWindow = QWindow::fromWinId(WId(mapView));
+
+# ifndef Q_OS_IOS
+ auto *videoView = [AVPlayerView new];
+ videoView.player = [AVPlayer playerWithURL:[NSURL URLWithString:
+ @"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"]];
+ videoWindow = QWindow::fromWinId(WId(videoView));
+# endif
+
+#else
+ mapWindow = &testWindow;
+ webViewWindow = &testWindow;
+ videoWindow = &testWindow;
+#endif
+
+ auto *calendarWidget = new QCalendarWidget;
+ calendarWidget->setAttribute(Qt::WA_NativeWindow);
+ QWindow *widgetWindow = calendarWidget->windowHandle();
+
+ auto *context = view.engine()->rootContext();
+ context->setContextProperty("testWindow", &testWindow);
+ context->setContextProperty("widgetWindow", widgetWindow);
+ context->setContextProperty("mapWindow", mapWindow);
+ context->setContextProperty("webViewWindow", webViewWindow);
+ context->setContextProperty("videoWindow", videoWindow);
+
+#if defined(QT_MULTIMEDIA_LIB)
+ context->setContextProperty("haveQtMultimedia", true);
+#endif
+
+ view.setSource(QUrl("qrc:/qt/qml/windowembeddingexample/windowembedding.qml"));
+ if (view.status() == QQuickView::Error)
+ return -1;
+
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/windowembedding/qtlogo-green.png b/tests/manual/windowembedding/qtlogo-green.png
new file mode 100644
index 0000000000..7b660be3de
--- /dev/null
+++ b/tests/manual/windowembedding/qtlogo-green.png
Binary files differ
diff --git a/tests/manual/windowembedding/qtlogo-white.png b/tests/manual/windowembedding/qtlogo-white.png
new file mode 100644
index 0000000000..5b96875d78
--- /dev/null
+++ b/tests/manual/windowembedding/qtlogo-white.png
Binary files differ
diff --git a/tests/manual/windowembedding/windowembedding.qml b/tests/manual/windowembedding/windowembedding.qml
new file mode 100644
index 0000000000..e8f75acecb
--- /dev/null
+++ b/tests/manual/windowembedding/windowembedding.qml
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import shared as Examples
+
+Item {
+ width: 500; height: 500
+ Examples.LauncherList {
+ id: launcherList
+ anchors.fill: parent
+ Component.onCompleted: {
+ addExample("📚 Stacking", "Window stacking order", Qt.resolvedUrl("examples/stacking.qml"));
+ addExample("âœ‚ï¸ Clipping", "Ancestor clipping", Qt.resolvedUrl("examples/clipping.qml"));
+ addExample("â†˜ï¸ Transform", "Transforms", Qt.resolvedUrl("examples/transform.qml"));
+ addExample("ðŸ—“ï¸ Widget", "A calendar widget", Qt.resolvedUrl("examples/widget.qml"));
+ addExample("ðŸ•¸ï¸ Web", "A native web view", Qt.resolvedUrl("examples/webview.qml"));
+ addExample("ðŸ—ºï¸ Map", "A native map view", Qt.resolvedUrl("examples/map.qml"));
+ addExample("ðŸ“½ï¸ Video", "A native video player", Qt.resolvedUrl("examples/video.qml"));
+ if (haveQtMultimedia)
+ addExample("💡 HDR", "A HDR enabled video player", Qt.resolvedUrl("examples/hdr.qml"));
+ }
+ }
+}
diff --git a/tests/manual/windowembedding/windowembedding.qmlproject b/tests/manual/windowembedding/windowembedding.qmlproject
new file mode 100644
index 0000000000..c5bf133574
--- /dev/null
+++ b/tests/manual/windowembedding/windowembedding.qmlproject
@@ -0,0 +1,17 @@
+import QmlProject 1.1
+
+Project {
+ mainFile: "windowembedding.qml"
+ importPaths: ["../"]
+
+ /* Include .qml, .js, and image files from current directory and subdirectories */
+ QmlFiles {
+ directory: "."
+ }
+ JavaScriptFiles {
+ directory: "."
+ }
+ ImageFiles {
+ directory: "."
+ }
+}
diff --git a/tests/system/sys_animatedsprite.qtt b/tests/system/sys_animatedsprite.qtt
index 2ea278bb5a..cd5a1f4f98 100644
--- a/tests/system/sys_animatedsprite.qtt
+++ b/tests/system/sys_animatedsprite.qtt
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//TESTED_COMPONENT=qtdeclarative
diff --git a/tests/system/sys_elements.qtt b/tests/system/sys_elements.qtt
index 954ce20de3..79654d4705 100644
--- a/tests/system/sys_elements.qtt
+++ b/tests/system/sys_elements.qtt
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//TESTED_COMPONENT=qtdeclarative
diff --git a/tests/system/sys_listview.qtt b/tests/system/sys_listview.qtt
index a51114e68c..ed2116429c 100644
--- a/tests/system/sys_listview.qtt
+++ b/tests/system/sys_listview.qtt
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//TESTED_COMPONENT=qtdeclarative
diff --git a/tests/system/sys_text.qtt b/tests/system/sys_text.qtt
index 0324c601bc..4a163a6ee6 100644
--- a/tests/system/sys_text.qtt
+++ b/tests/system/sys_text.qtt
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//TESTED_COMPONENT=qtdeclarative
diff --git a/tests/system/sys_textedit.qtt b/tests/system/sys_textedit.qtt
index 4919940c9b..8565f8e430 100644
--- a/tests/system/sys_textedit.qtt
+++ b/tests/system/sys_textedit.qtt
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//TESTED_COMPONENT=qtdeclarative
diff --git a/tests/system/sys_textinput.qtt b/tests/system/sys_textinput.qtt
index 262b8f99f2..bcb1ca116b 100644
--- a/tests/system/sys_textinput.qtt
+++ b/tests/system/sys_textinput.qtt
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//TESTED_COMPONENT=qtdeclarative
diff --git a/tests/testapplications/animatedsprite/animatedsprite.qml b/tests/testapplications/animatedsprite/animatedsprite.qml
index 68adbdb402..dd73061676 100644
--- a/tests/testapplications/animatedsprite/animatedsprite.qml
+++ b/tests/testapplications/animatedsprite/animatedsprite.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/animatedsprite/animatedspriteadvance.qml b/tests/testapplications/animatedsprite/animatedspriteadvance.qml
index 2cef06b5b7..9ff9f21d64 100644
--- a/tests/testapplications/animatedsprite/animatedspriteadvance.qml
+++ b/tests/testapplications/animatedsprite/animatedspriteadvance.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/AffectorElement.qml b/tests/testapplications/elements/content/AffectorElement.qml
index 70156166c9..3f4caa9483 100644
--- a/tests/testapplications/elements/content/AffectorElement.qml
+++ b/tests/testapplications/elements/content/AffectorElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/elements/content/AnimatedImageElement.qml b/tests/testapplications/elements/content/AnimatedImageElement.qml
index 170e14dd82..e1a65f62ef 100644
--- a/tests/testapplications/elements/content/AnimatedImageElement.qml
+++ b/tests/testapplications/elements/content/AnimatedImageElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/AppContainer.qml b/tests/testapplications/elements/content/AppContainer.qml
index b95ba51aa9..abfc91885d 100644
--- a/tests/testapplications/elements/content/AppContainer.qml
+++ b/tests/testapplications/elements/content/AppContainer.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/BorderImageElement.qml b/tests/testapplications/elements/content/BorderImageElement.qml
index 84b8b8b26a..343c600957 100644
--- a/tests/testapplications/elements/content/BorderImageElement.qml
+++ b/tests/testapplications/elements/content/BorderImageElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/BugPanel.qml b/tests/testapplications/elements/content/BugPanel.qml
index efd90c1abf..be5b43e4f1 100644
--- a/tests/testapplications/elements/content/BugPanel.qml
+++ b/tests/testapplications/elements/content/BugPanel.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/ColumnElement.qml b/tests/testapplications/elements/content/ColumnElement.qml
index 3344559039..eb63f354a9 100644
--- a/tests/testapplications/elements/content/ColumnElement.qml
+++ b/tests/testapplications/elements/content/ColumnElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/DirectionElement.qml b/tests/testapplications/elements/content/DirectionElement.qml
index 8678bff5c9..3d8a6e8555 100644
--- a/tests/testapplications/elements/content/DirectionElement.qml
+++ b/tests/testapplications/elements/content/DirectionElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/elements/content/DoubleValidatorElement.qml b/tests/testapplications/elements/content/DoubleValidatorElement.qml
index 81b4b97c0b..6730e48d5c 100644
--- a/tests/testapplications/elements/content/DoubleValidatorElement.qml
+++ b/tests/testapplications/elements/content/DoubleValidatorElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/EmitterElement.qml b/tests/testapplications/elements/content/EmitterElement.qml
index 35cf5db637..3232fe7cf4 100644
--- a/tests/testapplications/elements/content/EmitterElement.qml
+++ b/tests/testapplications/elements/content/EmitterElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/elements/content/FlickableElement.qml b/tests/testapplications/elements/content/FlickableElement.qml
index 084af5b356..adbe2d73c5 100644
--- a/tests/testapplications/elements/content/FlickableElement.qml
+++ b/tests/testapplications/elements/content/FlickableElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/FlipableElement.qml b/tests/testapplications/elements/content/FlipableElement.qml
index b59b13a84e..5c418f4d74 100644
--- a/tests/testapplications/elements/content/FlipableElement.qml
+++ b/tests/testapplications/elements/content/FlipableElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/FlowElement.qml b/tests/testapplications/elements/content/FlowElement.qml
index 978fa64c5d..8026668e49 100644
--- a/tests/testapplications/elements/content/FlowElement.qml
+++ b/tests/testapplications/elements/content/FlowElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/FocusScopeElement.qml b/tests/testapplications/elements/content/FocusScopeElement.qml
index 951a77e784..e0e0a68b16 100644
--- a/tests/testapplications/elements/content/FocusScopeElement.qml
+++ b/tests/testapplications/elements/content/FocusScopeElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/FontLoaderElement.qml b/tests/testapplications/elements/content/FontLoaderElement.qml
index a9ac7d00bb..70c201b17f 100644
--- a/tests/testapplications/elements/content/FontLoaderElement.qml
+++ b/tests/testapplications/elements/content/FontLoaderElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import "font"
diff --git a/tests/testapplications/elements/content/GradientElement.qml b/tests/testapplications/elements/content/GradientElement.qml
index e620076491..b3f73d5b39 100644
--- a/tests/testapplications/elements/content/GradientElement.qml
+++ b/tests/testapplications/elements/content/GradientElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/GridElement.qml b/tests/testapplications/elements/content/GridElement.qml
index ab1be2692d..df8ec0f926 100644
--- a/tests/testapplications/elements/content/GridElement.qml
+++ b/tests/testapplications/elements/content/GridElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/GridViewElement.qml b/tests/testapplications/elements/content/GridViewElement.qml
index 64804ba1f5..657e0c873b 100644
--- a/tests/testapplications/elements/content/GridViewElement.qml
+++ b/tests/testapplications/elements/content/GridViewElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/Help.qml b/tests/testapplications/elements/content/Help.qml
index f1377ba062..f357aabceb 100644
--- a/tests/testapplications/elements/content/Help.qml
+++ b/tests/testapplications/elements/content/Help.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/HelpDesk.qml b/tests/testapplications/elements/content/HelpDesk.qml
index 978cbe6a78..e7d34554b3 100644
--- a/tests/testapplications/elements/content/HelpDesk.qml
+++ b/tests/testapplications/elements/content/HelpDesk.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/ImageElement.qml b/tests/testapplications/elements/content/ImageElement.qml
index 275e81625e..c99e1d44a6 100644
--- a/tests/testapplications/elements/content/ImageElement.qml
+++ b/tests/testapplications/elements/content/ImageElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/ImageParticleElement.qml b/tests/testapplications/elements/content/ImageParticleElement.qml
index c9ac8e6ba0..8b4890036b 100644
--- a/tests/testapplications/elements/content/ImageParticleElement.qml
+++ b/tests/testapplications/elements/content/ImageParticleElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/elements/content/IntValidatorElement.qml b/tests/testapplications/elements/content/IntValidatorElement.qml
index 83a676b4bf..d715d1d00c 100644
--- a/tests/testapplications/elements/content/IntValidatorElement.qml
+++ b/tests/testapplications/elements/content/IntValidatorElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/KeysElement.qml b/tests/testapplications/elements/content/KeysElement.qml
index 00fd3ad538..77be18b4de 100644
--- a/tests/testapplications/elements/content/KeysElement.qml
+++ b/tests/testapplications/elements/content/KeysElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/ListViewElement.qml b/tests/testapplications/elements/content/ListViewElement.qml
index fd4b37d803..36ccff6ff2 100644
--- a/tests/testapplications/elements/content/ListViewElement.qml
+++ b/tests/testapplications/elements/content/ListViewElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/MouseAreaElement.qml b/tests/testapplications/elements/content/MouseAreaElement.qml
index 87e5a7bb1e..7ad5ed2688 100644
--- a/tests/testapplications/elements/content/MouseAreaElement.qml
+++ b/tests/testapplications/elements/content/MouseAreaElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/ParallelAnimationElement.qml b/tests/testapplications/elements/content/ParallelAnimationElement.qml
index 5950e0893c..e14066ae5f 100644
--- a/tests/testapplications/elements/content/ParallelAnimationElement.qml
+++ b/tests/testapplications/elements/content/ParallelAnimationElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/ParticleSystemElement.qml b/tests/testapplications/elements/content/ParticleSystemElement.qml
index 3be9b5f8e1..f537263e10 100644
--- a/tests/testapplications/elements/content/ParticleSystemElement.qml
+++ b/tests/testapplications/elements/content/ParticleSystemElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/elements/content/RectangleElement.qml b/tests/testapplications/elements/content/RectangleElement.qml
index 1f1b1fe9c2..1b690e4883 100644
--- a/tests/testapplications/elements/content/RectangleElement.qml
+++ b/tests/testapplications/elements/content/RectangleElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/RegExpValidatorElement.qml b/tests/testapplications/elements/content/RegExpValidatorElement.qml
index 573446fd76..42f2560ceb 100644
--- a/tests/testapplications/elements/content/RegExpValidatorElement.qml
+++ b/tests/testapplications/elements/content/RegExpValidatorElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
diff --git a/tests/testapplications/elements/content/RepeaterElement.qml b/tests/testapplications/elements/content/RepeaterElement.qml
index 34652aae02..ff5d1c7be6 100644
--- a/tests/testapplications/elements/content/RepeaterElement.qml
+++ b/tests/testapplications/elements/content/RepeaterElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQml.Models 2.12
diff --git a/tests/testapplications/elements/content/RowElement.qml b/tests/testapplications/elements/content/RowElement.qml
index f56be72b4b..5d9ee148f9 100644
--- a/tests/testapplications/elements/content/RowElement.qml
+++ b/tests/testapplications/elements/content/RowElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/ScaleElement.qml b/tests/testapplications/elements/content/ScaleElement.qml
index a325646c97..aa1b7e5719 100644
--- a/tests/testapplications/elements/content/ScaleElement.qml
+++ b/tests/testapplications/elements/content/ScaleElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/SequentialAnimationElement.qml b/tests/testapplications/elements/content/SequentialAnimationElement.qml
index 3c82d87e84..4e1bd54c04 100644
--- a/tests/testapplications/elements/content/SequentialAnimationElement.qml
+++ b/tests/testapplications/elements/content/SequentialAnimationElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/ShapeElement.qml b/tests/testapplications/elements/content/ShapeElement.qml
index a998e12484..35a17e6b05 100644
--- a/tests/testapplications/elements/content/ShapeElement.qml
+++ b/tests/testapplications/elements/content/ShapeElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/elements/content/SpriteSequenceElement.qml b/tests/testapplications/elements/content/SpriteSequenceElement.qml
index cbd799f75e..bb951235d5 100644
--- a/tests/testapplications/elements/content/SpriteSequenceElement.qml
+++ b/tests/testapplications/elements/content/SpriteSequenceElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/SystemPaletteElement.qml b/tests/testapplications/elements/content/SystemPaletteElement.qml
index 0209bc37fa..f9011e9c54 100644
--- a/tests/testapplications/elements/content/SystemPaletteElement.qml
+++ b/tests/testapplications/elements/content/SystemPaletteElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/SystemTestHelp.qml b/tests/testapplications/elements/content/SystemTestHelp.qml
index e5cf91cdf3..99548e355e 100644
--- a/tests/testapplications/elements/content/SystemTestHelp.qml
+++ b/tests/testapplications/elements/content/SystemTestHelp.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/TextEditElement.qml b/tests/testapplications/elements/content/TextEditElement.qml
index a1bb32ba80..bd6525f195 100644
--- a/tests/testapplications/elements/content/TextEditElement.qml
+++ b/tests/testapplications/elements/content/TextEditElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/TextElement.qml b/tests/testapplications/elements/content/TextElement.qml
index 032e0db26a..663d39dd8e 100644
--- a/tests/testapplications/elements/content/TextElement.qml
+++ b/tests/testapplications/elements/content/TextElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/TextInputElement.qml b/tests/testapplications/elements/content/TextInputElement.qml
index f62d5bc815..8371444162 100644
--- a/tests/testapplications/elements/content/TextInputElement.qml
+++ b/tests/testapplications/elements/content/TextInputElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/elements/content/TrailEmitterElement.qml b/tests/testapplications/elements/content/TrailEmitterElement.qml
index 61ed80652c..2193a82d95 100644
--- a/tests/testapplications/elements/content/TrailEmitterElement.qml
+++ b/tests/testapplications/elements/content/TrailEmitterElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/elements/content/XmlListModelElement.qml b/tests/testapplications/elements/content/XmlListModelElement.qml
index 821a7cd7d7..d69a4f093a 100644
--- a/tests/testapplications/elements/content/XmlListModelElement.qml
+++ b/tests/testapplications/elements/content/XmlListModelElement.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQml.XmlListModel
diff --git a/tests/testapplications/elements/content/elements.js b/tests/testapplications/elements/content/elements.js
index c4108126cb..711fbedf46 100644
--- a/tests/testapplications/elements/content/elements.js
+++ b/tests/testapplications/elements/content/elements.js
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
var newcomponent = Qt.createComponent("AppContainer.qml");
var appfile;
diff --git a/tests/testapplications/elements/elements.qml b/tests/testapplications/elements/elements.qml
index b23de7180d..f8960bd2c4 100644
--- a/tests/testapplications/elements/elements.qml
+++ b/tests/testapplications/elements/elements.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import "content/elements.js" as Elements
diff --git a/tests/testapplications/listview/alteredViews.qml b/tests/testapplications/listview/alteredViews.qml
index 73013b24d5..6d41f2a49d 100644
--- a/tests/testapplications/listview/alteredViews.qml
+++ b/tests/testapplications/listview/alteredViews.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/listview/onRemove.qml b/tests/testapplications/listview/onRemove.qml
index b9edd68614..f3ab4a1f72 100644
--- a/tests/testapplications/listview/onRemove.qml
+++ b/tests/testapplications/listview/onRemove.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/listview/sections.qml b/tests/testapplications/listview/sections.qml
index 56f0e02668..961fb7b5ad 100644
--- a/tests/testapplications/listview/sections.qml
+++ b/tests/testapplications/listview/sections.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/listview/sticky.qml b/tests/testapplications/listview/sticky.qml
index e617b3cbfe..81f86cdace 100644
--- a/tests/testapplications/listview/sticky.qml
+++ b/tests/testapplications/listview/sticky.qml
@@ -1,16 +1,15 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtCore
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-import QtQuick.Controls.Styles
ApplicationWindow {
id: window
- width: 640
+ width: 800
height: 480
visible: true
@@ -37,7 +36,7 @@ ApplicationWindow {
property alias stickyNextSection: snsItem.checked
}
- toolBar: GridLayout {
+ header: GridLayout {
rows: 3
flow: GridLayout.TopToBottom
@@ -45,46 +44,141 @@ ApplicationWindow {
anchors.left: parent.left
anchors.right: parent.right
- ComboBox { id: oriItem; currentIndex: 1; model: ["Horizontal", "Vertical"] }
- ComboBox { id: ldItem; model: ["LeftToRight", "RightToLeft"] }
- ComboBox { id: vldItem; model: ["TopToBottom", "BottomToTop"] }
+ ComboBox {
+ id: oriItem
+ currentIndex: 1
+ model: ["Horizontal", "Vertical"]
+ Layout.fillWidth: false
+ }
+ ComboBox {
+ id: ldItem
+ model: ["LeftToRight", "RightToLeft"]
+ Layout.fillWidth: false
+ }
+ ComboBox {
+ id: vldItem
+ model: ["TopToBottom", "BottomToTop"]
+ Layout.fillWidth: false
+ }
- CheckBox { id: clipItem; text: "Clip"; checked: true }
- CheckBox { id: opacityItem; text: "Opaque"; checked: true }
- CheckBox { id: filterItem; text: "Filter" }
+ CheckBox {
+ id: clipItem
+ text: "Clip"
+ checked: true
+ Layout.fillWidth: false
+ }
+ CheckBox {
+ id: opacityItem
+ text: "Opaque"
+ checked: true
+ Layout.fillWidth: false
+ }
+ CheckBox {
+ id: filterItem
+ text: "Filter"
+ Layout.fillWidth: false
+ }
- CheckBox { id: hItem; text: "Header:" }
- CheckBox { id: fItem; text: "Footer:" }
- Item { width: 1; height: 1 }
+ CheckBox {
+ id: hItem
+ text: "Header:"
+ Layout.fillWidth: false
+ }
+ CheckBox {
+ id: fItem
+ text: "Footer:"
+ Layout.fillWidth: false
+ }
+ Item {
+ width: 1
+ height: 1
+ }
- ComboBox { id: shItem; model: shModel; textRole: "name"; enabled: hItem.checked }
- ComboBox { id: sfItem; model: sfModel; textRole: "name"; enabled: fItem.checked }
- Item { width: 1; height: 1 }
+ ComboBox {
+ id: shItem
+ model: shModel
+ textRole: "name"
+ enabled: hItem.checked
+ Layout.fillWidth: false
+ }
+ ComboBox {
+ id: sfItem
+ model: sfModel
+ textRole: "name"
+ enabled: fItem.checked
+ Layout.fillWidth: false
+ }
+ Item {
+ width: 1
+ height: 1
+ }
- CheckBox { id: scsItem; text: "Sticky current section" }
- CheckBox { id: snsItem; text: "Sticky next section" }
- CheckBox { id: isItem; text: "Inline sections" }
+ CheckBox {
+ id: scsItem
+ text: "Sticky current section"
+ Layout.fillWidth: false
+ }
+ CheckBox {
+ id: snsItem
+ text: "Sticky next section"
+ Layout.fillWidth: false
+ }
+ CheckBox {
+ id: isItem
+ text: "Inline sections"
+ Layout.fillWidth: false
+ }
- Button { text: "Beginning"; onClicked: listview.positionViewAtBeginning() }
- Button { text: "Middle"; onClicked: listview.positionViewAtIndex(50, ListView.Center) }
- Button { text: "End"; onClicked: listview.positionViewAtEnd() }
+ Button {
+ text: "Beginning"
+ Layout.fillWidth: false
+ onClicked: listview.positionViewAtBeginning()
+ }
+ Button {
+ text: "Middle"
+ Layout.fillWidth: false
+ onClicked: listview.positionViewAtIndex(50, ListView.Center)
+ }
+ Button {
+ text: "End"
+ Layout.fillWidth: false
+ onClicked: listview.positionViewAtEnd()
+ }
}
ListModel {
id: shModel
- ListElement { name: "Inline"; value: ListView.InlineHeader }
- ListElement { name: "Overlay"; value: ListView.OverlayHeader }
- ListElement { name: "PullBack"; value: ListView.PullBackHeader }
+ ListElement {
+ name: "Inline"
+ value: ListView.InlineHeader
+ }
+ ListElement {
+ name: "Overlay"
+ value: ListView.OverlayHeader
+ }
+ ListElement {
+ name: "PullBack"
+ value: ListView.PullBackHeader
+ }
}
ListModel {
id: sfModel
- ListElement { name: "Inline"; value: ListView.InlineFooter }
- ListElement { name: "Overlay"; value: ListView.OverlayFooter }
- ListElement { name: "PullBack"; value: ListView.PullBackFooter }
+ ListElement {
+ name: "Inline"
+ value: ListView.InlineFooter
+ }
+ ListElement {
+ name: "Overlay"
+ value: ListView.OverlayFooter
+ }
+ ListElement {
+ name: "PullBack"
+ value: ListView.PullBackFooter
+ }
}
- statusBar: RowLayout {
+ footer: RowLayout {
anchors.margins: window.hspacing
anchors.left: parent.left
anchors.right: parent.right
diff --git a/tests/testapplications/listview/viewTransitions.qml b/tests/testapplications/listview/viewTransitions.qml
index b92ac19dce..8fdeea5b89 100644
--- a/tests/testapplications/listview/viewTransitions.qml
+++ b/tests/testapplications/listview/viewTransitions.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQuick.Particles 2.0
diff --git a/tests/testapplications/qsgimage/ImageNG.qml b/tests/testapplications/qsgimage/ImageNG.qml
index aa58256961..5277ef552d 100644
--- a/tests/testapplications/qsgimage/ImageNG.qml
+++ b/tests/testapplications/qsgimage/ImageNG.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/qsgimage/img-align.qml b/tests/testapplications/qsgimage/img-align.qml
index 71b337ddc2..cb2478dd03 100644
--- a/tests/testapplications/qsgimage/img-align.qml
+++ b/tests/testapplications/qsgimage/img-align.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/text/Button.qml b/tests/testapplications/text/Button.qml
index c99e6d2a27..da14a0cde7 100644
--- a/tests/testapplications/text/Button.qml
+++ b/tests/testapplications/text/Button.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/text/ControlView.qml b/tests/testapplications/text/ControlView.qml
index bc9f937df3..68fffdedbd 100644
--- a/tests/testapplications/text/ControlView.qml
+++ b/tests/testapplications/text/ControlView.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tests/testapplications/text/text.qml b/tests/testapplications/text/text.qml
index 8d5c89bd33..38bec67916 100644
--- a/tests/testapplications/text/text.qml
+++ b/tests/testapplications/text/text.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import QtQml.Models 2.12
diff --git a/tests/testapplications/text/textedit.qml b/tests/testapplications/text/textedit.qml
index 4bef471c6e..a4a1b17670 100644
--- a/tests/testapplications/text/textedit.qml
+++ b/tests/testapplications/text/textedit.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.1
import QtQml.Models 2.12
diff --git a/tests/testapplications/text/textinput.qml b/tests/testapplications/text/textinput.qml
index 6b88902fab..75427b7650 100644
--- a/tests/testapplications/text/textinput.qml
+++ b/tests/testapplications/text/textinput.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQml.Models 2.12
diff --git a/tests/testapplications/textlayout/styledtext-layout.qml b/tests/testapplications/textlayout/styledtext-layout.qml
index 2ec6379391..fdca55f46a 100644
--- a/tests/testapplications/textlayout/styledtext-layout.qml
+++ b/tests/testapplications/textlayout/styledtext-layout.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index a99cd33c24..0b89bea46e 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -3,79 +3,70 @@
# Generated from tools.pro.
-if(QT_FEATURE_qml_devtools)
- add_subdirectory(qmldom)
- if(QT_FEATURE_commandlineparser)
- add_subdirectory(qmllint)
- add_subdirectory(qmltc)
- add_subdirectory(qmltyperegistrar)
- endif()
- add_subdirectory(qmlimportscanner)
- add_subdirectory(qmlformat)
- if (TARGET Qt::LanguageServerPrivate
- AND NOT WASM AND NOT IOS AND NOT ANDROID AND NOT QNX AND NOT INTEGRITY AND NOT WEBOS)
+add_subdirectory(qmldom)
+if(QT_FEATURE_commandlineparser)
+ add_subdirectory(qmllint)
+ add_subdirectory(qmltc)
+ add_subdirectory(qmltyperegistrar)
+ add_subdirectory(qmljsrootgen)
+endif()
+add_subdirectory(qmlimportscanner)
+add_subdirectory(qmlformat)
+if(TARGET Qt::LanguageServerPrivate AND QT_FEATURE_commandlineparser AND QT_FEATURE_filesystemwatcher)
+ if (NOT CMAKE_CROSSCOMPILING OR QT_FORCE_BUILD_TOOLS)
add_subdirectory(qmlls)
endif()
endif()
-if(QT_FEATURE_qml_devtools AND QT_FEATURE_xmlstreamwriter)
+if(QT_FEATURE_xmlstreamwriter)
# special case begin
# Do not build qmlcachegen here but build it at src/
# time, so that we can use it for our own .qml files in src/imports.
# add_subdirectory(qmlcachegen)
# special case end
endif()
-if(QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT IOS AND NOT rtems)
- add_subdirectory(qml)
-endif()
-if(QT_FEATURE_qml_profiler
- AND QT_FEATURE_thread
- AND NOT ANDROID
- AND NOT IOS
- AND NOT WASM
- AND NOT rtems)
- add_subdirectory(qmlprofiler)
-endif()
-if(QT_FEATURE_qml_preview AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT IOS AND NOT rtems)
- add_subdirectory(qmlpreview)
-endif()
-if(QT_BUILD_SHARED_LIBS AND QT_FEATURE_thread AND TARGET Qt::Quick AND NOT ANDROID AND NOT WASM AND NOT IOS AND NOT rtems)
- add_subdirectory(qmlscene)
- add_subdirectory(qmltime)
-endif()
-if(QT_BUILD_SHARED_LIBS
- AND QT_FEATURE_process
- AND QT_FEATURE_regularexpression
- AND QT_FEATURE_thread
- AND TARGET Qt::Quick
- AND NOT ANDROID
- AND NOT IOS
- AND NOT WASM
- AND NOT rtems)
- add_subdirectory(qmlplugindump)
-endif()
-if(TARGET Qt::Quick
- AND TARGET Qt::Widgets
- AND QT_FEATURE_checkbox
- AND QT_FEATURE_combobox
- AND QT_FEATURE_dialogbuttonbox
- AND QT_FEATURE_formlayout
- AND QT_FEATURE_groupbox
- AND QT_FEATURE_lineedit
- AND QT_FEATURE_mainwindow
- AND QT_FEATURE_spinbox
- AND QT_FEATURE_textedit
- AND NOT ANDROID
- AND NOT IOS
- AND NOT WASM
- AND NOT rtems)
- add_subdirectory(qmleasing)
-endif()
-if(QT_FEATURE_thread AND TARGET Qt::QuickTest AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmltestrunner)
-endif()
-if(QT_FEATURE_private_tests AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT IOS AND NOT rtems)
- add_subdirectory(qmljs)
-endif()
-if (QT_FEATURE_private_tests AND NOT CMAKE_CROSSCOMPILING)
- add_subdirectory(qmljsrootgen)
-endif()
+
+if(NOT (ANDROID OR WASM OR IOS OR VISIONOS OR rtems))
+ if(QT_FEATURE_thread)
+ add_subdirectory(qml)
+ if(QT_FEATURE_qml_profiler)
+ add_subdirectory(qmlprofiler)
+ endif()
+ if(QT_FEATURE_qml_preview)
+ add_subdirectory(qmlpreview)
+ endif()
+ if(QT_BUILD_SHARED_LIBS AND TARGET Qt::Quick)
+ add_subdirectory(qmlscene)
+ add_subdirectory(qmltime)
+ endif()
+ if(QT_BUILD_SHARED_LIBS
+ AND QT_FEATURE_process
+ AND QT_FEATURE_regularexpression
+ AND TARGET Qt::Quick)
+ add_subdirectory(qmlplugindump)
+ endif()
+ if(TARGET Qt::QuickTest)
+ add_subdirectory(qmltestrunner)
+ endif()
+ if(QT_FEATURE_private_tests)
+ add_subdirectory(qmljs)
+ endif()
+ endif() # QT_FEATURE_thread
+
+ if(TARGET Qt::Quick
+ AND TARGET Qt::Widgets
+ AND QT_FEATURE_checkbox
+ AND QT_FEATURE_combobox
+ AND QT_FEATURE_dialogbuttonbox
+ AND QT_FEATURE_formlayout
+ AND QT_FEATURE_groupbox
+ AND QT_FEATURE_lineedit
+ AND QT_FEATURE_mainwindow
+ AND QT_FEATURE_spinbox
+ AND QT_FEATURE_textedit)
+ add_subdirectory(qmleasing)
+ endif()
+
+ if(TARGET Qt::Quick AND TARGET Qt::Svg)
+ add_subdirectory(svgtoqml)
+ endif()
+endif() # NOT (ANDROID OR WASM OR IOS OR rtems)
diff --git a/tools/qml/CMakeLists.txt b/tools/qml/CMakeLists.txt
index 0bbde6d288..b209c730d3 100644
--- a/tools/qml/CMakeLists.txt
+++ b/tools/qml/CMakeLists.txt
@@ -19,35 +19,18 @@ qt_internal_add_app(qml
set_target_properties(qml PROPERTIES WIN32_EXECUTABLE FALSE)
-set_source_files_properties(
- conf/default.qml
- conf/resizeToItem.qml
- conf/content/resizeItemToWindow.qml
- conf/content/resizeWindowToItem.qml
- PROPERTIES QT_SKIP_QUICKCOMPILER 1
-)
-
-# Resources:
-set(qml_resource_files
- "conf/content/resizeItemToWindow.qml"
- "conf/content/resizeWindowToItem.qml"
- "conf/default.qml"
- "conf/resizeToItem.qml"
- "resources/qml-64.png"
-)
-
-qt_internal_add_resource(qml qml
- PREFIX
- "/qt-project.org/QmlRuntime"
- FILES
- ${qml_resource_files}
-)
-
# Turn the tool into its own self-contained qml module
qt6_add_qml_module(qml
- RESOURCE_PREFIX "/qt-project.org"
+ RESOURCE_PREFIX "/qt-project.org/imports"
URI QmlRuntime.Config
VERSION 1.0
+ QML_FILES
+ default.qml
+ resizeToItem.qml
+ ResizeItemToWindow.qml
+ ResizeWindowToItem.qml
+ RESOURCES
+ resources/qml-64.png
)
qt_internal_extend_target(qml CONDITION TARGET Qt::Gui
@@ -60,14 +43,6 @@ qt_internal_extend_target(qml CONDITION TARGET Qt::Widgets
Qt::Widgets
)
-#### Keys ignored in scope 4:.:.:qml.pro:WIN32:
-# RC_ICONS = "resources/qml.ico"
-
-#### Keys ignored in scope 5:.:.:qml.pro:APPLE:
-# ICON = "resources/qml.icns"
-# OTHER_FILES = "resources/Info.plist"
-# QMAKE_INFO_PLIST = "resources/Info.plist"
-
qt_internal_extend_target(qml CONDITION QT_FEATURE_qml_debug
DEFINES
QT_QML_DEBUG_NO_WARNING
diff --git a/tools/qml/ResizeItemToWindow.qml b/tools/qml/ResizeItemToWindow.qml
new file mode 100644
index 0000000000..a4d8bfec40
--- /dev/null
+++ b/tools/qml/ResizeItemToWindow.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+import QtQuick.Window 2.0
+import QtQuick 2.0
+
+Window {
+ property Item containedObject: null
+ onContainedObjectChanged: {
+ if (containedObject == undefined || containedObject == null) {
+ visible = false;
+ return;
+ }
+ width = containedObject.width;
+ height = containedObject.height;
+ containedObject.parent = contentItem;
+ visible = true;
+ }
+ onWidthChanged: if (containedObject) containedObject.width = width
+ onHeightChanged: if (containedObject) containedObject.height = height
+}
diff --git a/tools/qml/ResizeWindowToItem.qml b/tools/qml/ResizeWindowToItem.qml
new file mode 100644
index 0000000000..b969971bc2
--- /dev/null
+++ b/tools/qml/ResizeWindowToItem.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+import QtQuick.Window 2.0
+import QtQuick 2.0
+
+Window {
+ property Item containedObject: null
+ onContainedObjectChanged: {
+ if (containedObject == undefined || containedObject == null) {
+ visible = false;
+ return;
+ }
+ width = Qt.binding(function() { return containedObject.width });
+ height = Qt.binding(function() { return containedObject.height });
+ containedObject.parent = contentItem;
+ visible = true;
+ }
+}
diff --git a/tools/qml/conf/content/resizeItemToWindow.qml b/tools/qml/conf/content/resizeItemToWindow.qml
deleted file mode 100644
index 4a6d06591c..0000000000
--- a/tools/qml/conf/content/resizeItemToWindow.qml
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick.Window 2.0
-import QtQuick 2.0
-
-Window {
- property Item containedObject: null
- property bool __resizeGuard: false
- onContainedObjectChanged: {
- if (containedObject == undefined || containedObject == null) {
- visible = false;
- return;
- }
- __resizeGuard = true
- width = containedObject.width;
- height = containedObject.height;
- containedObject.parent = contentItem;
- visible = true;
- __resizeGuard = false
- }
- onWidthChanged: if (!__resizeGuard && containedObject) containedObject.width = width
- onHeightChanged: if (!__resizeGuard && containedObject) containedObject.height = height
-}
diff --git a/tools/qml/conf/content/resizeWindowToItem.qml b/tools/qml/conf/content/resizeWindowToItem.qml
deleted file mode 100644
index 942e739d13..0000000000
--- a/tools/qml/conf/content/resizeWindowToItem.qml
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick.Window 2.0
-import QtQuick 2.0
-
-Window {
- property Item containedObject: null
- onContainedObjectChanged: {
- if (containedObject == undefined || containedObject == null) {
- visible = false;
- return;
- }
- width = Qt.binding(function() { return containedObject.width });
- height = Qt.binding(function() { return containedObject.height });
- containedObject.parent = contentItem;
- visible = true;
- }
-}
diff --git a/tools/qml/conf/default.qml b/tools/qml/conf/default.qml
deleted file mode 100644
index c9bae6dcd0..0000000000
--- a/tools/qml/conf/default.qml
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QmlRuntime.Config 1.0
-
-Configuration {
- PartialScene {
- itemType: "QQuickItem"
- container: "qrc:/qt-project.org/QmlRuntime/conf/content/resizeItemToWindow.qml"
- }
-}
diff --git a/tools/qml/conf/resizeToItem.qml b/tools/qml/conf/resizeToItem.qml
deleted file mode 100644
index 7c1389a7a7..0000000000
--- a/tools/qml/conf/resizeToItem.qml
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QmlRuntime.Config 1.0
-
-Configuration {
- PartialScene {
- itemType: "QQuickItem"
- container: "qrc:/qt-project.org/QmlRuntime/conf/content/resizeWindowToItem.qml"
- }
-}
diff --git a/tools/qml/default.qml b/tools/qml/default.qml
new file mode 100644
index 0000000000..54a521193c
--- /dev/null
+++ b/tools/qml/default.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+import QmlRuntime.Config
+
+Configuration {
+ PartialScene {
+ itemType: "QQuickItem"
+ container: "qrc:/qt-project.org/imports/QmlRuntime/Config/ResizeItemToWindow.qml"
+ }
+}
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index ae5596e000..da544c5563 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -73,12 +73,18 @@ static QQmlApplicationEngine *qae = nullptr;
#if defined(Q_OS_DARWIN) || defined(QT_GUI_LIB)
static int exitTimerId = -1;
#endif
-static const QString iconResourcePath(QStringLiteral(":/qt-project.org/QmlRuntime/resources/qml-64.png"));
-static const QString confResourcePath(QStringLiteral(":/qt-project.org/QmlRuntime/conf/"));
+static const QString iconResourcePath(QStringLiteral(":/qt-project.org/imports/QmlRuntime/Config/resources/qml-64.png"));
+static const QString confResourcePath(QStringLiteral(":/qt-project.org/imports/QmlRuntime/Config/"));
static const QString customConfFileName(QStringLiteral("configuration.qml"));
static bool verboseMode = false;
static bool quietMode = false;
static bool glShareContexts = true;
+static bool disableShaderCache = true;
+#if defined(QT_GUI_LIB)
+static bool requestAlphaChannel = false;
+static bool requestMSAA = false;
+static bool requestCoreProfile = false;
+#endif
static void loadConf(const QString &override, bool quiet) // Terminates app on failure
{
@@ -152,8 +158,16 @@ static void listConfFiles()
{
const QDir confResourceDir(confResourcePath);
printf("%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:")));
- for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files))
- printf(" %s\n", qPrintable(fi.baseName()));
+ for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files)) {
+ if (fi.completeSuffix() != QLatin1String("qml"))
+ continue;
+
+ const QString baseName = fi.baseName();
+ if (baseName.isEmpty() || baseName[0].isUpper())
+ continue;
+
+ printf(" %s\n", qPrintable(baseName));
+ }
printf("%s\n", qPrintable(QCoreApplication::translate("main", "Other configurations:")));
bool foundOther = false;
const QStringList otherLocations = QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation);
@@ -324,6 +338,14 @@ static void getAppFlags(int argc, char **argv)
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
} else if (!strcmp(argv[i], "-disable-context-sharing") || !strcmp(argv[i], "--disable-context-sharing")) {
glShareContexts = false;
+ } else if (!strcmp(argv[i], "-enable-shader-cache") || !strcmp(argv[i], "--enable-shader-cache")) {
+ disableShaderCache = false;
+ } else if (!strcmp(argv[i], "-transparent") || !strcmp(argv[i], "--transparent")) {
+ requestAlphaChannel = true;
+ } else if (!strcmp(argv[i], "-multisample") || !strcmp(argv[i], "--multisample")) {
+ requestMSAA = true;
+ } else if (!strcmp(argv[i], "-core-profile") || !strcmp(argv[i], "--core-profile")) {
+ requestCoreProfile = true;
}
}
#else
@@ -362,8 +384,31 @@ int main(int argc, char *argv[])
{
getAppFlags(argc, argv);
+ // Must set the default QSurfaceFormat before creating the app object if
+ // AA_ShareOpenGLContexts is going to be set.
+#if defined(QT_GUI_LIB)
+ QSurfaceFormat surfaceFormat;
+ surfaceFormat.setDepthBufferSize(24);
+ surfaceFormat.setStencilBufferSize(8);
+ if (requestMSAA)
+ surfaceFormat.setSamples(4);
+ if (requestAlphaChannel)
+ surfaceFormat.setAlphaBufferSize(8);
+ if (qEnvironmentVariableIsSet("QSG_CORE_PROFILE")
+ || qEnvironmentVariableIsSet("QML_CORE_PROFILE")
+ || requestCoreProfile)
+ {
+ // intentionally requesting 4.1 core to play nice with macOS
+ surfaceFormat.setVersion(4, 1);
+ surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
+ }
+ QSurfaceFormat::setDefaultFormat(surfaceFormat);
+#endif
+
if (glShareContexts)
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
+ if (disableShaderCache)
+ QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache);
std::unique_ptr<QCoreApplication> app;
switch (applicationType) {
@@ -390,7 +435,6 @@ int main(int argc, char *argv[])
app->setOrganizationDomain("qt-project.org");
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
- QQmlApplicationEngine e;
QStringList files;
QString confFile;
QString translationFile;
@@ -399,8 +443,8 @@ int main(int argc, char *argv[])
QCommandLineParser parser;
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
- const QCommandLineOption helpOption = parser.addHelpOption();
- const QCommandLineOption versionOption = parser.addVersionOption();
+ parser.addHelpOption();
+ parser.addVersionOption();
#ifdef QT_GUI_LIB
QCommandLineOption apptypeOption(QStringList() << QStringLiteral("a") << QStringLiteral("apptype"),
QCoreApplication::translate("main", "Select which application class to use. Default is gui."),
@@ -444,11 +488,22 @@ int main(int argc, char *argv[])
parser.addOption(glSoftwareOption); // Just for the help text... we've already handled this argument above
QCommandLineOption glCoreProfile(QStringLiteral("core-profile"),
QCoreApplication::translate("main", "Force use of OpenGL Core Profile."));
- parser.addOption(glCoreProfile);
+ parser.addOption(glCoreProfile); // Just for the help text... we've already handled this argument above
QCommandLineOption glContextSharing(QStringLiteral("disable-context-sharing"),
QCoreApplication::translate("main", "Disable the use of a shared GL context for QtQuick Windows"));
parser.addOption(glContextSharing); // Just for the help text... we've already handled this argument above
+ // Options relevant for other 3D APIs as well
+ QCommandLineOption shaderCaching(QStringLiteral("enable-shader-cache"),
+ QCoreApplication::translate("main", "Enable persistent caching of generated shaders"));
+ parser.addOption(shaderCaching); // Just for the help text... we've already handled this argument above
+ QCommandLineOption transparentOption(QStringLiteral("transparent"),
+ QCoreApplication::translate("main", "Requests an alpha channel in order to enable semi-transparent windows."));
+ parser.addOption(transparentOption); // Just for the help text... we've already handled this argument above
+ QCommandLineOption multisampleOption(QStringLiteral("multisample"),
+ QCoreApplication::translate("main", "Requests 4x multisample antialiasing."));
+ parser.addOption(multisampleOption); // Just for the help text... we've already handled this argument above
#endif // QT_GUI_LIB
+
// Debugging and verbosity options
QCommandLineOption quietOption(QStringLiteral("quiet"),
QCoreApplication::translate("main", "Suppress all output."));
@@ -464,7 +519,7 @@ int main(int argc, char *argv[])
parser.addOption(fixedAnimationsOption);
QCommandLineOption rhiOption(QStringList() << QStringLiteral("r") << QStringLiteral("rhi"),
QCoreApplication::translate("main", "Set the backend for the Qt graphics abstraction (RHI). "
- "Backend is one of: default, vulkan, metal, d3d11, gl"),
+ "Backend is one of: default, vulkan, metal, d3d11, d3d12, opengl"),
QStringLiteral("backend"));
parser.addOption(rhiOption);
QCommandLineOption selectorOption(QStringLiteral("S"), QCoreApplication::translate("main",
@@ -477,14 +532,7 @@ int main(int argc, char *argv[])
parser.addPositionalArgument("args",
QCoreApplication::translate("main", "Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."), "[-- args...]");
- if (!parser.parse(QCoreApplication::arguments())) {
- qWarning() << parser.errorText();
- exit(1);
- }
- if (parser.isSet(versionOption))
- parser.showVersion();
- if (parser.isSet(helpOption))
- parser.showHelp();
+ parser.process(*app);
if (parser.isSet(verboseOption))
verboseMode = true;
if (parser.isSet(quietOption)) {
@@ -507,6 +555,9 @@ int main(int argc, char *argv[])
if (parser.isSet(fixedAnimationsOption))
QUnifiedTimer::instance()->setConsistentTiming(true);
#endif
+
+ QQmlApplicationEngine e;
+
for (const QString &importPath : parser.values(importOption))
e.addImportPath(importPath);
@@ -517,17 +568,6 @@ int main(int argc, char *argv[])
if (!customSelectors.isEmpty())
e.setExtraFileSelectors(customSelectors);
-#if defined(QT_GUI_LIB)
- if (qEnvironmentVariableIsSet("QSG_CORE_PROFILE") || qEnvironmentVariableIsSet("QML_CORE_PROFILE") || parser.isSet(glCoreProfile)) {
- QSurfaceFormat surfaceFormat;
- surfaceFormat.setStencilBufferSize(8);
- surfaceFormat.setDepthBufferSize(24);
- surfaceFormat.setVersion(4, 1);
- surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
- QSurfaceFormat::setDefaultFormat(surfaceFormat);
- }
-#endif
-
files << parser.values(qmlFileOption);
if (parser.isSet(configOption))
confFile = parser.value(configOption);
@@ -550,9 +590,8 @@ int main(int argc, char *argv[])
#if QT_CONFIG(translation)
// Need to be installed before QQmlApplicationEngine's automatic translation loading
// (qt_ translations are loaded there)
+ QTranslator translator;
if (!translationFile.isEmpty()) {
- QTranslator translator;
-
if (translator.load(translationFile)) {
app->installTranslator(&translator);
if (verboseMode)
diff --git a/tools/qml/resizeToItem.qml b/tools/qml/resizeToItem.qml
new file mode 100644
index 0000000000..5bddd8ebaa
--- /dev/null
+++ b/tools/qml/resizeToItem.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+import QmlRuntime.Config
+
+Configuration {
+ PartialScene {
+ itemType: "QQuickItem"
+ container: "qrc:/qt-project.org/imports/QmlRuntime/Config/ResizeWindowToItem.qml"
+ }
+}
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index 93c534d429..1180931d9f 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -24,12 +24,14 @@
#include <algorithm>
+using namespace Qt::Literals::StringLiterals;
+
static bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QStringList &arguments)
{
allArguments.reserve(arguments.size());
for (const QString &argument : arguments) {
// "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
- if (argument.startsWith(QLatin1Char('@'))) {
+ if (argument.startsWith(u'@')) {
QString optionsFile = argument;
optionsFile.remove(0, 1);
if (optionsFile.isEmpty()) {
@@ -59,51 +61,49 @@ int main(int argc, char **argv)
QHashSeed::setDeterministicGlobalSeed();
QCoreApplication app(argc, argv);
- QCoreApplication::setApplicationName(QStringLiteral("qmlcachegen"));
+ QCoreApplication::setApplicationName("qmlcachegen"_L1);
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
QCommandLineParser parser;
parser.addHelpOption();
parser.addVersionOption();
- QCommandLineOption bareOption(QStringLiteral("bare"), QCoreApplication::translate("main", "Do not include default import directories. This may be used to run qmlcachegen on a project using a different Qt version."));
+ QCommandLineOption bareOption("bare"_L1, QCoreApplication::translate("main", "Do not include default import directories. This may be used to run qmlcachegen on a project using a different Qt version."));
parser.addOption(bareOption);
- QCommandLineOption filterResourceFileOption(QStringLiteral("filter-resource-file"), QCoreApplication::translate("main", "Filter out QML/JS files from a resource file that can be cached ahead of time instead"));
+ QCommandLineOption filterResourceFileOption("filter-resource-file"_L1, QCoreApplication::translate("main", "Filter out QML/JS files from a resource file that can be cached ahead of time instead"));
parser.addOption(filterResourceFileOption);
- QCommandLineOption resourceFileMappingOption(QStringLiteral("resource-file-mapping"), QCoreApplication::translate("main", "Path from original resource file to new one"), QCoreApplication::translate("main", "old-name=new-name"));
+ QCommandLineOption resourceFileMappingOption("resource-file-mapping"_L1, QCoreApplication::translate("main", "Path from original resource file to new one"), QCoreApplication::translate("main", "old-name=new-name"));
parser.addOption(resourceFileMappingOption);
- QCommandLineOption resourceOption(QStringLiteral("resource"), QCoreApplication::translate("main", "Qt resource file that might later contain one of the compiled files"), QCoreApplication::translate("main", "resource-file-name"));
+ QCommandLineOption resourceOption("resource"_L1, QCoreApplication::translate("main", "Qt resource file that might later contain one of the compiled files"), QCoreApplication::translate("main", "resource-file-name"));
parser.addOption(resourceOption);
- QCommandLineOption resourcePathOption(QStringLiteral("resource-path"), QCoreApplication::translate("main", "Qt resource file path corresponding to the file being compiled"), QCoreApplication::translate("main", "resource-path"));
+ QCommandLineOption resourcePathOption("resource-path"_L1, QCoreApplication::translate("main", "Qt resource file path corresponding to the file being compiled"), QCoreApplication::translate("main", "resource-path"));
parser.addOption(resourcePathOption);
- QCommandLineOption resourceNameOption(QStringLiteral("resource-name"),
- QCoreApplication::translate("main", "Required to generate qmlcache_loader without qrc files. This is the name of the Qt resource the input files belong to."),
- QCoreApplication::translate("main", "compiled-file-list"));
+ QCommandLineOption resourceNameOption("resource-name"_L1, QCoreApplication::translate("main", "Required to generate qmlcache_loader without qrc files. This is the name of the Qt resource the input files belong to."), QCoreApplication::translate("main", "compiled-file-list"));
parser.addOption(resourceNameOption);
- QCommandLineOption directCallsOption(QStringLiteral("direct-calls"), QCoreApplication::translate("main", "This option is ignored."));
+ QCommandLineOption directCallsOption("direct-calls"_L1, QCoreApplication::translate("main", "This option is ignored."));
directCallsOption.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(directCallsOption);
- QCommandLineOption importsOption(
- QStringLiteral("i"),
- QCoreApplication::translate("main", "Import extra qmldir"),
- QCoreApplication::translate("main", "qmldir file"));
+ QCommandLineOption staticOption("static"_L1, QCoreApplication::translate("main", "This option is ignored."));
+ staticOption.setFlags(QCommandLineOption::HiddenFromHelp);
+ parser.addOption(staticOption);
+ QCommandLineOption importsOption("i"_L1, QCoreApplication::translate("main", "Import extra qmldir"), QCoreApplication::translate("main", "qmldir file"));
parser.addOption(importsOption);
- QCommandLineOption importPathOption(
- QStringLiteral("I"),
- QCoreApplication::translate("main", "Look for QML modules in specified directory"),
- QCoreApplication::translate("main", "import directory"));
+ QCommandLineOption importPathOption("I"_L1, QCoreApplication::translate("main", "Look for QML modules in specified directory"), QCoreApplication::translate("main", "import directory"));
parser.addOption(importPathOption);
- QCommandLineOption onlyBytecode(
- QStringLiteral("only-bytecode"),
- QCoreApplication::translate(
- "main", "Generate only byte code for bindings and functions, no C++ code"));
+ QCommandLineOption onlyBytecode("only-bytecode"_L1, QCoreApplication::translate("main", "Generate only byte code for bindings and functions, no C++ code"));
parser.addOption(onlyBytecode);
+ QCommandLineOption verboseOption("verbose"_L1, QCoreApplication::translate("main", "Output compile warnings"));
+ parser.addOption(verboseOption);
+ QCommandLineOption warningsAreErrorsOption("warnings-are-errors"_L1, QCoreApplication::translate("main", "Treat warnings as errors"));
+ parser.addOption(warningsAreErrorsOption);
+
+ QCommandLineOption validateBasicBlocksOption("validate-basic-blocks"_L1, QCoreApplication::translate("main", "Performs checks on the basic blocks of a function compiled ahead of time to validate its structure and coherence"));
+ parser.addOption(validateBasicBlocksOption);
- QCommandLineOption outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name"));
+ QCommandLineOption outputFileOption("o"_L1, QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name"));
parser.addOption(outputFileOption);
- parser.addPositionalArgument(QStringLiteral("[qml file]"),
- QStringLiteral("QML source file to generate cache for."));
+ parser.addPositionalArgument("[qml file]"_L1, "QML source file to generate cache for."_L1);
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
@@ -125,9 +125,9 @@ int main(int argc, char **argv)
if (parser.isSet(outputFileOption))
outputFileName = parser.value(outputFileOption);
- if (outputFileName.endsWith(QLatin1String(".cpp"))) {
+ if (outputFileName.endsWith(".cpp"_L1)) {
target = GenerateCpp;
- if (outputFileName.endsWith(QLatin1String("qmlcache_loader.cpp")))
+ if (outputFileName.endsWith("qmlcache_loader.cpp"_L1))
target = GenerateLoader;
}
@@ -138,13 +138,13 @@ int main(int argc, char **argv)
if (sources.isEmpty()){
parser.showHelp();
} else if (sources.size() > 1 && (target != GenerateLoader && target != GenerateLoaderStandAlone)) {
- fprintf(stderr, "%s\n", qPrintable(QStringLiteral("Too many input files specified: '") + sources.join(QStringLiteral("' '")) + QLatin1Char('\'')));
+ fprintf(stderr, "%s\n", qPrintable("Too many input files specified: '"_L1 + sources.join("' '"_L1) + u'\''));
return EXIT_FAILURE;
}
const QString inputFile = !sources.isEmpty() ? sources.first() : QString();
if (outputFileName.isEmpty())
- outputFileName = inputFile + QLatin1Char('c');
+ outputFileName = inputFile + u'c';
if (parser.isSet(filterResourceFileOption))
return qRelocateResourceFile(inputFile, outputFileName);
@@ -156,7 +156,7 @@ int main(int argc, char **argv)
if (!qQmlJSGenerateLoader(
mapper.resourcePaths(QQmlJSResourceFileMapper::allQmlJSFilter()),
outputFileName, parser.values(resourceFileMappingOption), &error.message)) {
- error.augment(QLatin1String("Error generating loader stub: ")).print();
+ error.augment("Error generating loader stub: "_L1).print();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
@@ -166,7 +166,7 @@ int main(int argc, char **argv)
QQmlJSCompileError error;
if (!qQmlJSGenerateLoader(sources, outputFileName,
parser.values(resourceNameOption), &error.message)) {
- error.augment(QLatin1String("Error generating loader stub: ")).print();
+ error.augment("Error generating loader stub: "_L1).print();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
@@ -201,13 +201,12 @@ int main(int argc, char **argv)
}
if (target == GenerateCpp) {
- inputFileUrl = QStringLiteral("qrc://") + inputResourcePath;
+ inputFileUrl = "qrc://"_L1 + inputResourcePath;
saveFunction = [inputResourcePath, outputFileName](
const QV4::CompiledData::SaveableUnitPointer &unit,
const QQmlJSAotFunctionMap &aotFunctions,
QString *errorString) {
- return qSaveQmlJSUnitAsCpp(inputResourcePath, outputFileName, unit, aotFunctions,
- errorString);
+ return qSaveQmlJSUnitAsCpp(inputResourcePath, outputFileName, unit, aotFunctions, errorString);
};
} else {
@@ -223,20 +222,20 @@ int main(int argc, char **argv)
};
}
- if (inputFile.endsWith(QLatin1String(".qml"))) {
+ if (inputFile.endsWith(".qml"_L1)) {
QQmlJSCompileError error;
if (target != GenerateCpp || inputResourcePath.isEmpty() || parser.isSet(onlyBytecode)) {
if (!qCompileQmlFile(inputFile, saveFunction, nullptr, &error,
/* storeSourceLocation */ false)) {
- error.augment(QStringLiteral("Error compiling qml file: ")).print();
+ error.augment("Error compiling qml file: "_L1).print();
return EXIT_FAILURE;
}
} else {
QStringList importPaths;
if (parser.isSet(resourceOption)) {
- importPaths.append(QLatin1String(":/qt-project.org/imports"));
- importPaths.append(QLatin1String(":/qt/qml"));
+ importPaths.append("qt-project.org/imports"_L1);
+ importPaths.append("qt/qml"_L1);
};
if (parser.isSet(importPathOption))
@@ -254,39 +253,41 @@ int main(int argc, char **argv)
logger.setCategoryIgnored(qmlCompiler, false);
logger.setCategoryFatal(qmlCompiler, true);
- // By default, we're completely silent,
- // as the lcAotCompiler category default is QtFatalMsg
- const bool loggingEnabled = lcAotCompiler().isDebugEnabled()
- || lcAotCompiler().isInfoEnabled() || lcAotCompiler().isWarningEnabled()
- || lcAotCompiler().isCriticalEnabled();
- if (!loggingEnabled)
+ if (!parser.isSet(verboseOption) && !parser.isSet(warningsAreErrorsOption))
logger.setSilent(true);
QQmlJSAotCompiler cppCodeGen(
&importer, u':' + inputResourcePath, parser.values(importsOption), &logger);
+ if (parser.isSet(validateBasicBlocksOption))
+ cppCodeGen.m_flags.setFlag(QQmlJSAotCompiler::ValidateBasicBlocks);
+
if (!qCompileQmlFile(inputFile, saveFunction, &cppCodeGen, &error,
/* storeSourceLocation */ true)) {
- error.augment(QStringLiteral("Error compiling qml file: ")).print();
+ error.augment("Error compiling qml file: "_L1).print();
return EXIT_FAILURE;
}
QList<QQmlJS::DiagnosticMessage> warnings = importer.takeGlobalWarnings();
if (!warnings.isEmpty()) {
- logger.log(QStringLiteral("Type warnings occurred while compiling file:"),
+ logger.log("Type warnings occurred while compiling file:"_L1,
qmlImport, QQmlJS::SourceLocation());
logger.processMessages(warnings, qmlImport);
+ if (parser.isSet(warningsAreErrorsOption))
+ return EXIT_FAILURE;
}
}
- } else if (inputFile.endsWith(QLatin1String(".js")) || inputFile.endsWith(QLatin1String(".mjs"))) {
+ } else if (inputFile.endsWith(".js"_L1) || inputFile.endsWith(".mjs"_L1)) {
QQmlJSCompileError error;
if (!qCompileJSFile(inputFile, inputFileUrl, saveFunction, &error)) {
- error.augment(QLatin1String("Error compiling js file: ")).print();
+ error.augment("Error compiling js file: "_L1).print();
return EXIT_FAILURE;
}
} else {
fprintf(stderr, "Ignoring %s input file as it is not QML source code - maybe remove from QML_FILES?\n", qPrintable(inputFile));
+ if (parser.isSet(warningsAreErrorsOption))
+ return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/tools/qmldom/qmldomtool.cpp b/tools/qmldom/qmldomtool.cpp
index 30eeda3792..99c81e0a95 100644
--- a/tools/qmldom/qmldomtool.cpp
+++ b/tools/qmldom/qmldomtool.cpp
@@ -208,18 +208,18 @@ int main(int argc, char *argv[])
DomItem env(envPtr);
qDebug() << "will load\n";
if (dep != Dependencies::None)
- env.loadBuiltins();
+ envPtr->loadBuiltins();
QList<DomItem> loadedFiles(positionalArguments.size());
qsizetype iPos = 0;
for (const QString &s : std::as_const(positionalArguments)) {
- env.loadFile(
- s, QString(),
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, s),
[&loadedFiles, iPos](Path, const DomItem &, const DomItem &newIt) {
loadedFiles[iPos] = newIt;
},
- LoadOption::DefaultLoad, fileType);
+ fileType);
}
- envPtr->loadPendingDependencies(env);
+ envPtr->loadPendingDependencies();
bool hadFailures = false;
const qsizetype largestFileSizeToCheck = 32000;
@@ -245,7 +245,7 @@ int main(int argc, char *argv[])
QDir d(rDir);
target = d.filePath(f.fileName());
}
- MutableDomItem res = qmlFile.writeOut(target, nBackups, lwOptions, &fw, checks);
+ auto res = qmlFile.writeOut(target, nBackups, lwOptions, &fw, checks);
switch (fw.status) {
case FileWriter::Status::ShouldWrite:
case FileWriter::Status::SkippedDueToFailure:
@@ -257,7 +257,7 @@ int main(int argc, char *argv[])
case FileWriter::Status::SkippedEqual:
qDebug() << "no change";
}
- hadFailures = hadFailures || !bool(res);
+ hadFailures = hadFailures || !res;
}
} else if (parser.isSet(dumpAstOption)) {
if (pathsToDump.size() > 1) {
@@ -285,7 +285,7 @@ int main(int argc, char *argv[])
};
qsizetype iPathToDump = 0;
bool globalPaths = false;
- for (auto p : pathsToDump)
+ for (const auto &p : pathsToDump)
if (p.headKind() == Path::Kind::Root)
globalPaths = true;
if (globalPaths)
diff --git a/tools/qmlformat/CMakeLists.txt b/tools/qmlformat/CMakeLists.txt
index 7675363ce4..908901b9f5 100644
--- a/tools/qmlformat/CMakeLists.txt
+++ b/tools/qmlformat/CMakeLists.txt
@@ -13,11 +13,10 @@ qt_internal_add_tool(${target_name}
TOOLS_TARGET Qml # special case
SOURCES
qmlformat.cpp
- ../shared/qqmltoolingsettings.h
- ../shared/qqmltoolingsettings.cpp
LIBRARIES
Qt::Core
Qt::QmlDomPrivate
+ Qt::QmlToolingSettingsPrivate
)
qt_internal_return_unless_building_tools()
diff --git a/tools/qmlformat/qmlformat.cpp b/tools/qmlformat/qmlformat.cpp
index b1e0bb75de..e26a6412c9 100644
--- a/tools/qmlformat/qmlformat.cpp
+++ b/tools/qmlformat/qmlformat.cpp
@@ -19,7 +19,8 @@
# include <QCommandLineParser>
#endif
-#include "../shared/qqmltoolingsettings.h"
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+
using namespace QQmlJS::Dom;
@@ -45,45 +46,16 @@ struct Options
QStringList errors;
};
-bool parseFile(const QString &filename, const Options &options)
+// TODO refactor
+// Move out to the LineWriterOptions class / helper
+static LineWriterOptions composeLwOptions(const Options &options, QStringView code)
{
- DomItem env =
- DomEnvironment::create(QStringList(),
- QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
- | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
- DomItem tFile; // place where to store the loaded file
- env.loadFile(
- filename, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) {
- tFile = newIt; // callback called when everything is loaded that receives the loaded
- // external file pair (path, oldValue, newValue)
- },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
- DomItem qmlFile = tFile.fileObject();
- std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>();
- if (!qmlFilePtr || !qmlFilePtr->isValid()) {
- qmlFile.iterateErrors(
- [](DomItem, ErrorMessage msg) {
- errorToQDebug(msg);
- return true;
- },
- true);
- qWarning().noquote() << "Failed to parse" << filename;
- return false;
- }
-
- // Turn AST back into source code
- if (options.verbose)
- qWarning().noquote() << "Dumping" << filename;
-
LineWriterOptions lwOptions;
lwOptions.formatOptions.indentSize = options.indentWidth;
lwOptions.formatOptions.useTabs = options.tabs;
lwOptions.updateOptions = LineWriterOptions::Update::None;
if (options.newline == "native") {
// find out current line endings...
- QStringView code = qmlFilePtr->code();
int newlineIndex = code.indexOf(QChar(u'\n'));
int crIndex = code.indexOf(QChar(u'\r'));
if (newlineIndex >= 0) {
@@ -108,37 +80,111 @@ bool parseFile(const QString &filename, const Options &options)
} else if (options.newline == "unix") {
lwOptions.lineEndings = LineWriterOptions::LineEndings::Unix;
} else {
- qWarning().noquote() << "Unknown line ending type" << options.newline;
- return false;
+ qWarning().noquote() << "Unknown line ending type" << options.newline << ", using default";
}
if (options.normalize)
lwOptions.attributesSequence = LineWriterOptions::AttributesSequence::Normalize;
else
lwOptions.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
- WriteOutChecks checks = WriteOutCheck::Default;
- if (options.force || qmlFilePtr->code().size() > 32000)
- checks = WriteOutCheck::None;
lwOptions.objectsSpacing = options.objectsSpacing;
lwOptions.functionsSpacing = options.functionsSpacing;
+ return lwOptions;
+}
- MutableDomItem res;
+static void logParsingErrors(const DomItem &fileItem, const QString &filename)
+{
+ fileItem.iterateErrors(
+ [](const DomItem &, const ErrorMessage &msg) {
+ errorToQDebug(msg);
+ return true;
+ },
+ true);
+ qWarning().noquote() << "Failed to parse" << filename;
+}
+
+// TODO
+// refactor this workaround. ExternalOWningItem is not recognized as an owning type
+// in ownerAs.
+static std::shared_ptr<ExternalOwningItem> getFileItemOwner(const DomItem &fileItem)
+{
+ std::shared_ptr<ExternalOwningItem> filePtr = nullptr;
+ switch (fileItem.internalKind()) {
+ case DomType::JsFile:
+ filePtr = fileItem.ownerAs<JsFile>();
+ break;
+ default:
+ filePtr = fileItem.ownerAs<QmlFile>();
+ break;
+ }
+ return filePtr;
+}
+
+// TODO refactor
+// Introduce better encapsulation and separation of concerns and move to DOM API
+// returns a DomItem corresponding to the loaded file and bool indicating the validity of the file
+static std::pair<DomItem, bool> parse(const QString &filename)
+{
+ auto envPtr =
+ DomEnvironment::create(QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+ // placeholder for a node
+ // containing metadata (ExternalItemInfo) about the loaded file
+ DomItem fMetadataItem;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filename),
+ // callback called when everything is loaded that receives the
+ // loaded external file pair (path, oldValue, newValue)
+ [&fMetadataItem](Path, const DomItem &, const DomItem &extItemInfo) {
+ fMetadataItem = extItemInfo;
+ });
+ auto fItem = fMetadataItem.fileObject();
+ auto filePtr = getFileItemOwner(fItem);
+ return { fItem, filePtr && filePtr->isValid() };
+}
+
+static bool parseFile(const QString &filename, const Options &options)
+{
+ const auto [fileItem, validFile] = parse(filename);
+ if (!validFile) {
+ logParsingErrors(fileItem, filename);
+ return false;
+ }
+
+ // Turn AST back into source code
+ if (options.verbose)
+ qWarning().noquote() << "Dumping" << filename;
+
+ const auto &code = getFileItemOwner(fileItem)->code();
+ auto lwOptions = composeLwOptions(options, code);
+ WriteOutChecks checks = WriteOutCheck::Default;
+ //Disable writeOutChecks for some usecases
+ if (options.force ||
+ code.size() > 32000 ||
+ fileItem.internalKind() == DomType::JsFile) {
+ checks = WriteOutCheck::None;
+ }
+
+ bool res = false;
if (options.inplace) {
if (options.verbose)
qWarning().noquote() << "Writing to file" << filename;
FileWriter fw;
const unsigned numberOfBackupFiles = 0;
- res = qmlFile.writeOut(filename, numberOfBackupFiles, lwOptions, &fw, checks);
+ res = fileItem.writeOut(filename, numberOfBackupFiles, lwOptions, &fw, checks);
} else {
QFile out;
- out.open(stdout, QIODevice::WriteOnly);
- LineWriter lw([&out](QStringView s) { out.write(s.toUtf8()); }, filename, lwOptions);
- OutWriter ow(lw);
- res = qmlFile.writeOutForFile(ow, checks);
- ow.flush();
+ if (out.open(stdout, QIODevice::WriteOnly)) {
+ LineWriter lw([&out](QStringView s) { out.write(s.toUtf8()); }, filename, lwOptions);
+ OutWriter ow(lw);
+ res = fileItem.writeOutForFile(ow, checks);
+ ow.flush();
+ } else {
+ res = false;
+ }
}
- return bool(res);
+ return res;
}
Options buildCommandLineOptions(const QCoreApplication &app)
@@ -216,8 +262,7 @@ Options buildCommandLineOptions(const QCoreApplication &app)
QStringList files;
if (!parser.value("files").isEmpty()) {
QFile file(parser.value("files"));
- file.open(QIODevice::Text | QIODevice::ReadOnly);
- if (file.isOpen()) {
+ if (file.open(QIODevice::Text | QIODevice::ReadOnly)) {
QTextStream in(&file);
while (!in.atEnd()) {
QString file = in.readLine();
@@ -291,6 +336,10 @@ int main(int argc, char *argv[])
return settings.writeDefaults() ? 0 : -1;
auto getSettings = [&](const QString &file, Options options) {
+ // Perform formatting inplace if --files option is set.
+ if (!options.files.isEmpty())
+ options.inplace = true;
+
if (options.ignoreSettings || !settings.search(file))
return options;
diff --git a/tools/qmljs/CMakeLists.txt b/tools/qmljs/CMakeLists.txt
index 4a1f8488b2..40faa5d013 100644
--- a/tools/qmljs/CMakeLists.txt
+++ b/tools/qmljs/CMakeLists.txt
@@ -49,11 +49,6 @@ set_target_properties(qmljs PROPERTIES WIN32_EXECUTABLE FALSE)
## Scopes:
#####################################################################
-qt_internal_extend_target(qmljs CONDITION WIN32
- DEFINES
- NOMINMAX
-)
-
qt_internal_extend_target(qmljs CONDITION disassembler AND ((TEST_architecture_arch STREQUAL "i386") OR (TEST_architecture_arch STREQUAL "x86_64"))
DEFINES
WTF_USE_UDIS86=1
diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index ae01e7560f..8a1c4d0ded 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -19,6 +19,7 @@
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QDateTime>
+#include <QtCore/qcommandlineparser.h>
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
@@ -49,43 +50,48 @@ int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
- QStringList args = app.arguments();
- args.removeFirst();
- bool runAsQml = false;
- bool runAsModule = false;
- bool cache = false;
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.setApplicationDescription("Utility to execute scripts in QML's V4 engine");
+ parser.addVersionOption();
+ parser.addPositionalArgument("files", "Files to execute.", "[files...]");
- if (!args.isEmpty()) {
- if (args.constFirst() == QLatin1String("--jit")) {
- qputenv("QV4_JIT_CALL_THRESHOLD", QByteArray("0"));
- args.removeFirst();
- }
- if (args.constFirst() == QLatin1String("--interpret")) {
- qputenv("QV4_FORCE_INTERPRETER", QByteArray("1"));
- args.removeFirst();
- }
- if (args.constFirst() == QLatin1String("--qml")) {
- runAsQml = true;
- args.removeFirst();
- }
+ QCommandLineOption forceJit("jit", "Force JIT.");
+ parser.addOption(forceJit);
- if (args.constFirst() == QLatin1String("--module")) {
- runAsModule = true;
- args.removeFirst();
- }
+ QCommandLineOption forceInterpreter("interpret", "Force interpreter.");
+ parser.addOption(forceInterpreter);
- if (args.constFirst() == QLatin1String("--cache")) {
- cache = true;
- args.removeFirst();
- }
+ QCommandLineOption qml("qml", "Run as QML.");
+ parser.addOption(qml);
+
+ QCommandLineOption module("module", "Run as Module.");
+ parser.addOption(module);
- if (args.constFirst() == QLatin1String("--help")) {
- std::cerr << "Usage: qmljs [|--jit|--interpret|--qml] file..." << std::endl;
- return EXIT_SUCCESS;
+ QCommandLineOption cache("cache", "Use cache.");
+ parser.addOption(cache);
+
+ parser.process(app);
+
+ bool jitEnabled = false;
+
+ if (parser.isSet(forceJit)) {
+ qputenv("QV4_JIT_CALL_THRESHOLD", QByteArray("0"));
+ jitEnabled = true;
+ }
+ if (parser.isSet(forceInterpreter)) {
+ qputenv("QV4_FORCE_INTERPRETER", QByteArray("1"));
+ if (jitEnabled) {
+ std::cerr << "You cannot use 'Force JIT' and 'Force Interpreter' at the same time.";
+ return EXIT_FAILURE;
}
}
+ const bool runAsQml = parser.isSet(qml);
+ const bool runAsModule = parser.isSet(module);
+ const bool useCache = parser.isSet(cache);
+ const QStringList args = parser.positionalArguments();
QV4::ExecutionEngine vm;
@@ -94,12 +100,12 @@ int main(int argc, char *argv[])
QV4::GlobalExtensions::init(vm.globalObject, QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension);
- for (const QString &fn : std::as_const(args)) {
+ for (const QString &fn : args) {
QV4::ScopedValue result(scope);
if (runAsModule) {
auto module = vm.loadModule(QUrl::fromLocalFile(QFileInfo(fn).absoluteFilePath()));
if (module.compiled) {
- if (module.compiled->instantiate(&vm))
+ if (module.compiled->instantiate())
module.compiled->evaluate();
} else if (module.native) {
// Nothing to do. Native modules have no global code.
@@ -113,12 +119,12 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
QScopedPointer<QV4::Script> script;
- if (cache && QFile::exists(fn + QLatin1Char('c'))) {
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
- = QV4::ExecutableCompilationUnit::create();
+ if (useCache && QFile::exists(fn + QLatin1Char('c'))) {
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
QString error;
if (unit->loadFromDisk(QUrl::fromLocalFile(fn), QFileInfo(fn).lastModified(), &error)) {
- script.reset(new QV4::Script(&vm, nullptr, unit));
+ script.reset(new QV4::Script(
+ &vm, nullptr, vm.insertCompilationUnit(std::move(unit))));
} else {
std::cout << "Error loading" << qPrintable(fn) << "from disk cache:" << qPrintable(error) << std::endl;
}
@@ -134,12 +140,13 @@ int main(int argc, char *argv[])
}
if (!scope.hasException()) {
const auto unit = script->compilationUnit;
- if (cache && unit && !(unit->unitData()->flags & QV4::CompiledData::Unit::StaticData)) {
+ if (useCache && unit && !(unit->unitData()->flags & QV4::CompiledData::Unit::StaticData)) {
if (unit->unitData()->sourceTimeStamp == 0) {
const_cast<QV4::CompiledData::Unit*>(unit->unitData())->sourceTimeStamp = QFileInfo(fn).lastModified().toMSecsSinceEpoch();
}
QString saveError;
- if (!unit->saveToDisk(QUrl::fromLocalFile(fn), &saveError)) {
+ if (!unit->baseCompilationUnit()->saveToDisk(
+ QUrl::fromLocalFile(fn), &saveError)) {
std::cout << "Error saving JS cache file: " << qPrintable(saveError) << std::endl;
}
}
diff --git a/tools/qmljsrootgen/main.cpp b/tools/qmljsrootgen/main.cpp
index 2bd54692d1..acd375144e 100644
--- a/tools/qmljsrootgen/main.cpp
+++ b/tools/qmljsrootgen/main.cpp
@@ -10,6 +10,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfile.h>
+#include <QtCore/qcommandlineparser.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonarray.h>
@@ -141,8 +142,8 @@ static QString buildClass(const QJSManagedValue &value, QJsonArray *classes,
protoName = name.at(0).toUpper() + name.mid(1) + QStringLiteral("Prototype");
}
- auto it = seen->prototypes.find(protoName);
- if (it == seen->prototypes.end()) {
+ auto it = seen->prototypes.constFind(protoName);
+ if (it == seen->prototypes.cend()) {
seen->prototypes.insert(protoName, prototype.toJSValue());
buildClass(prototype, classes, seen, protoName);
} else if (!it->strictlyEquals(prototype.toJSValue())) {
@@ -311,8 +312,8 @@ static QString buildConstructor(const QJSManagedValue &constructor, QJsonArray *
Q_UNREACHABLE();
}
- auto it = seen->constructors.find(name);
- if (it == seen->constructors.end()) {
+ auto it = seen->constructors.constFind(name);
+ if (it == seen->constructors.cend()) {
seen->constructors.insert(name, constructor.toJSValue());
return buildClass(*constructed, classes, seen, name);
} else if (!constructor.strictlyEquals(QJSManagedValue(*it, constructor.engine()))) {
@@ -326,14 +327,22 @@ int main(int argc, char *argv[])
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
- QStringList args = app.arguments();
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.setApplicationDescription("Internal development tool.");
+ parser.addPositionalArgument("path", "Output json path.", "path");
- if (args.size() != 2) {
- qWarning().noquote() << app.applicationName() << "[output json path]";
- return 1;
+ parser.process(app);
+
+ const QStringList args = parser.positionalArguments();
+ if (auto size = args.size(); size == 0) {
+ qWarning().noquote().nospace() << app.applicationName() << ": Output path missing.";
+ return EXIT_FAILURE;
+ } else if (size >= 2) {
+ qWarning().noquote().nospace() << app.applicationName() << ": Too many output paths given. Only one allowed.";
}
- QString fileName = args.at(1);
+ const QString fileName = args.at(0);
QJSEngine engine;
engine.installExtensions(QJSEngine::AllExtensions);
diff --git a/tools/qmllint/CMakeLists.txt b/tools/qmllint/CMakeLists.txt
index 2802d6d3af..ae3de5901d 100644
--- a/tools/qmllint/CMakeLists.txt
+++ b/tools/qmllint/CMakeLists.txt
@@ -13,11 +13,10 @@ qt_internal_add_tool(${target_name}
TOOLS_TARGET Qml # special case
SOURCES
main.cpp
- ../shared/qqmltoolingsettings.h
- ../shared/qqmltoolingsettings.cpp
LIBRARIES
Qt::CorePrivate
Qt::QmlCompilerPrivate
+ Qt::QmlToolingSettingsPrivate
)
qt_internal_return_unless_building_tools()
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 5c35c19928..05dc667232 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -1,11 +1,13 @@
// Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "../shared/qqmltoolingsettings.h"
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+#include <QtQmlToolingSettings/private/qqmltoolingutils_p.h>
#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
#include <QtQmlCompiler/private/qqmljscompiler_p.h>
#include <QtQmlCompiler/private/qqmljslinter_p.h>
+#include <QtQmlCompiler/private/qqmljsloggingutils_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
@@ -29,10 +31,39 @@ using namespace Qt::StringLiterals;
constexpr int JSON_LOGGING_FORMAT_REVISION = 3;
+bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QStringList &arguments)
+{
+ allArguments.reserve(arguments.size());
+ for (const QString &argument : arguments) {
+ // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
+ if (argument.startsWith(u'@')) {
+ QString optionsFile = argument;
+ optionsFile.remove(0, 1);
+ if (optionsFile.isEmpty()) {
+ qWarning().nospace() << "The @ option requires an input file";
+ return false;
+ }
+ QFile f(optionsFile);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning().nospace() << "Cannot open options file specified with @";
+ return false;
+ }
+ while (!f.atEnd()) {
+ QString line = QString::fromLocal8Bit(f.readLine().trimmed());
+ if (!line.isEmpty())
+ allArguments << line;
+ }
+ } else {
+ allArguments << argument;
+ }
+ }
+ return true;
+}
+
int main(int argv, char *argc[])
{
QHashSeed::setDeterministicGlobalSeed();
- QList<QQmlJSLogger::Category> categories;
+ QList<QQmlJS::LoggerCategory> categories;
QCoreApplication app(argv, argc);
QCoreApplication::setApplicationName("qmllint");
@@ -92,6 +123,11 @@ All warnings can be set to three levels:
const QString qmlImportPathsSetting = QLatin1String("AdditionalQmlImportPaths");
settings.addOption(qmlImportPathsSetting);
+ QCommandLineOption environmentOption(
+ QStringList() << "E",
+ QLatin1String("Use the QML_IMPORT_PATH environment variable to look for QML Modules"));
+ parser.addOption(environmentOption);
+
QCommandLineOption qmlImportNoDefault(
QStringList() << "bare",
QLatin1String("Do not include default import directories or the current directory. "
@@ -150,20 +186,36 @@ All warnings can be set to three levels:
QLatin1String("directory"));
parser.addOption(pluginPathsOption);
- auto addCategory = [&](const QQmlJSLogger::Category &category) {
+ auto levelToString = [](const QQmlJS::LoggerCategory &category) -> QString {
+ Q_ASSERT(category.isIgnored() || category.level() != QtCriticalMsg);
+ if (category.isIgnored())
+ return QStringLiteral("disable");
+
+ switch (category.level()) {
+ case QtInfoMsg:
+ return QStringLiteral("info");
+ case QtWarningMsg:
+ return QStringLiteral("warning");
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ };
+
+ auto addCategory = [&](const QQmlJS::LoggerCategory &category) {
categories.push_back(category);
- if (category.isDefault)
+ if (category.isDefault())
return;
QCommandLineOption option(
category.id().name().toString(),
- category.description
- + QStringLiteral(" (default: %1)").arg(category.levelToString()),
- QStringLiteral("level"), category.levelToString());
- if (category.ignored)
+ category.description()
+ + QStringLiteral(" (default: %1)").arg(levelToString(category)),
+ QStringLiteral("level"), levelToString(category));
+ if (category.isIgnored())
option.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(option);
- settings.addOption(QStringLiteral("Warnings/") + category.settingsName,
- category.levelToString());
+ settings.addOption(QStringLiteral("Warnings/") + category.settingsName(),
+ levelToString(category));
};
for (const auto &category : QQmlJSLogger::defaultCategories()) {
@@ -172,11 +224,16 @@ All warnings can be set to three levels:
parser.addPositionalArgument(QLatin1String("files"),
QLatin1String("list of qml or js files to verify"));
- if (!parser.parse(app.arguments())) {
- if (parser.unknownOptionNames().isEmpty()) {
- qWarning().noquote() << parser.errorText();
- return 1;
- }
+
+ QStringList arguments;
+ if (!argumentsFromCommandLineAndFile(arguments, app.arguments())) {
+ // argumentsFromCommandLine already printed any necessary warnings.
+ return 1;
+ }
+
+ if (!parser.parse(arguments)) {
+ qWarning().noquote() << parser.errorText();
+ return 1;
}
// Since we can't use QCommandLineParser::process(), we need to handle version and help manually
@@ -192,21 +249,30 @@ All warnings can be set to three levels:
auto updateLogLevels = [&]() {
for (auto &category : categories) {
- if (category.isDefault)
+ if (category.isDefault())
continue;
const QString &key = category.id().name().toString();
- const QString &settingsName = QStringLiteral("Warnings/") + category.settingsName;
+ const QString &settingsName = QStringLiteral("Warnings/") + category.settingsName();
if (parser.isSet(key) || settings.isSet(settingsName)) {
const QString value = parser.isSet(key) ? parser.value(key)
: settings.value(settingsName).toString();
// Do not try to set the levels if it's due to a default config option.
// This way we can tell which options have actually been overwritten by the user.
- if (category.levelToString() == value && !parser.isSet(key))
+ if (levelToString(category) == value && !parser.isSet(key))
continue;
- if (!category.setLevel(value)) {
+ if (value == "disable"_L1) {
+ category.setLevel(QtCriticalMsg);
+ category.setIgnored(true);
+ } else if (value == "info"_L1) {
+ category.setLevel(QtInfoMsg);
+ category.setIgnored(false);
+ } else if (value == "warning"_L1) {
+ category.setLevel(QtWarningMsg);
+ category.setIgnored(false);
+ } else {
qWarning() << "Invalid logging level" << value << "provided for"
<< category.id().name().toString()
<< "(allowed are: disable, info, warning)";
@@ -236,7 +302,7 @@ All warnings can be set to three levels:
QStringList defaultQmldirFiles;
if (parser.isSet(qmldirFilesOption)) {
defaultQmldirFiles = parser.values(qmldirFilesOption);
- } else {
+ } else if (!parser.isSet(qmlImportNoDefault)){
// If nothing given explicitly, use the qmldir file from the current directory.
QFileInfo qmldirFile(QStringLiteral("qmldir"));
if (qmldirFile.isFile()) {
@@ -267,7 +333,7 @@ All warnings can be set to three levels:
QQmlJSLinter linter(qmlImportPaths, pluginPaths, useAbsolutePath);
for (const QQmlJSLinter::Plugin &plugin : linter.plugins()) {
- for (const QQmlJSLogger::Category &category : plugin.categories())
+ for (const QQmlJS::LoggerCategory &category : plugin.categories())
addCategory(category);
}
@@ -300,70 +366,97 @@ All warnings can be set to three levels:
QJsonArray jsonFiles;
for (const QString &filename : positionalArguments) {
- if (!parser.isSet(ignoreSettings)) {
+ if (!parser.isSet(ignoreSettings))
settings.search(filename);
- updateLogLevels();
+ updateLogLevels();
- const QDir fileDir = QFileInfo(filename).absoluteDir();
- auto addAbsolutePaths = [&](QStringList &list, const QStringList &entries) {
- for (const QString &file : entries)
- list << (QFileInfo(file).isAbsolute() ? file : fileDir.filePath(file));
- };
+ const QDir fileDir = QFileInfo(filename).absoluteDir();
+ auto addAbsolutePaths = [&](QStringList &list, const QStringList &entries) {
+ for (const QString &file : entries)
+ list << (QFileInfo(file).isAbsolute() ? file : fileDir.filePath(file));
+ };
- resourceFiles = defaultResourceFiles;
+ resourceFiles = defaultResourceFiles;
- addAbsolutePaths(resourceFiles, settings.value(resourceSetting).toStringList());
+ addAbsolutePaths(resourceFiles, settings.value(resourceSetting).toStringList());
- qmldirFiles = defaultQmldirFiles;
- if (settings.isSet(qmldirFilesSetting)
- && !settings.value(qmldirFilesSetting).toStringList().isEmpty()) {
- qmldirFiles = {};
- addAbsolutePaths(qmldirFiles,
- settings.value(qmldirFilesSetting).toStringList());
- }
+ qmldirFiles = defaultQmldirFiles;
+ if (settings.isSet(qmldirFilesSetting)
+ && !settings.value(qmldirFilesSetting).toStringList().isEmpty()) {
+ qmldirFiles = {};
+ addAbsolutePaths(qmldirFiles, settings.value(qmldirFilesSetting).toStringList());
+ }
+
+ if (parser.isSet(qmlImportNoDefault)
+ || (settings.isSet(qmlImportNoDefaultSetting)
+ && settings.value(qmlImportNoDefaultSetting).toBool())) {
+ qmlImportPaths = {};
+ } else {
+ qmlImportPaths = defaultImportPaths;
+ }
- if (parser.isSet(qmlImportNoDefault)
- || (settings.isSet(qmlImportNoDefaultSetting)
- && settings.value(qmlImportNoDefaultSetting).toBool())) {
- qmlImportPaths = {};
+ if (parser.isSet(qmlImportPathsOption))
+ qmlImportPaths << parser.values(qmlImportPathsOption);
+ if (parser.isSet(environmentOption)) {
+ if (silent) {
+ qmlImportPaths << qEnvironmentVariable("QML_IMPORT_PATH")
+ .split(QDir::separator(), Qt::SkipEmptyParts)
+ << qEnvironmentVariable("QML2_IMPORT_PATH")
+ .split(QDir::separator(), Qt::SkipEmptyParts);
} else {
- qmlImportPaths = defaultImportPaths;
+ if (const QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML_IMPORT_PATH"_s);
+ !dirsFromEnv.isEmpty()) {
+ qInfo().nospace().noquote()
+ << "Using import directories passed from environment variable "
+ "\"QML_IMPORT_PATH\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+ qmlImportPaths << dirsFromEnv;
+ }
+ if (const QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(
+ u"QML2_IMPORT_PATH"_s);
+ !dirsFromEnv.isEmpty()) {
+ qInfo().nospace().noquote() << "Using import directories passed from the "
+ "deprecated environment variable "
+ "\"QML2_IMPORT_PATH\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+ qmlImportPaths << dirsFromEnv;
+ }
}
+ }
- if (parser.isSet(qmlImportPathsOption))
- qmlImportPaths << parser.values(qmlImportPathsOption);
-
- addAbsolutePaths(qmlImportPaths, settings.value(qmlImportPathsSetting).toStringList());
+ addAbsolutePaths(qmlImportPaths, settings.value(qmlImportPathsSetting).toStringList());
- QSet<QString> disabledPlugins;
+ QSet<QString> disabledPlugins;
- if (parser.isSet(pluginsDisable)) {
- for (const QString &plugin : parser.values(pluginsDisable))
- disabledPlugins << plugin.toLower();
- }
+ if (parser.isSet(pluginsDisable)) {
+ for (const QString &plugin : parser.values(pluginsDisable))
+ disabledPlugins << plugin.toLower();
+ }
- if (settings.isSet(pluginsDisableSetting)) {
- for (const QString &plugin : settings.value(pluginsDisableSetting).toStringList())
- disabledPlugins << plugin.toLower();
- }
+ if (settings.isSet(pluginsDisableSetting)) {
+ for (const QString &plugin : settings.value(pluginsDisableSetting).toStringList())
+ disabledPlugins << plugin.toLower();
+ }
- linter.setPluginsEnabled(!disabledPlugins.contains("all"));
+ linter.setPluginsEnabled(!disabledPlugins.contains("all"));
- if (!linter.pluginsEnabled())
- continue;
+ if (!linter.pluginsEnabled())
+ continue;
- auto &plugins = linter.plugins();
+ auto &plugins = linter.plugins();
- for (auto &plugin : plugins)
- plugin.setEnabled(!disabledPlugins.contains(plugin.name().toLower()));
- }
+ for (auto &plugin : plugins)
+ plugin.setEnabled(!disabledPlugins.contains(plugin.name().toLower()));
const bool isFixing = parser.isSet(fixFile);
QQmlJSLinter::LintResult lintResult;
if (parser.isSet(moduleOption)) {
- lintResult = linter.lintModule(filename, silent, useJson ? &jsonFiles : nullptr);
+ lintResult = linter.lintModule(filename, silent, useJson ? &jsonFiles : nullptr,
+ qmlImportPaths, resourceFiles);
} else {
lintResult = linter.lintFile(filename, nullptr, silent || isFixing,
useJson ? &jsonFiles : nullptr, qmlImportPaths,
@@ -451,8 +544,10 @@ All warnings can be set to three levels:
QTextStream(stdout) << QString::fromUtf8(json);
} else {
QFile file(fileName);
- file.open(QFile::WriteOnly);
- file.write(json);
+ if (file.open(QFile::WriteOnly))
+ file.write(json);
+ else
+ success = false;
}
}
diff --git a/tools/qmlls/CMakeLists.txt b/tools/qmlls/CMakeLists.txt
index dcc703141e..d61a18582e 100644
--- a/tools/qmlls/CMakeLists.txt
+++ b/tools/qmlls/CMakeLists.txt
@@ -8,28 +8,16 @@
qt_internal_add_app(qmlls
TARGET_DESCRIPTION "QML Language Server"
SOURCES
- lspcustomtypes.h
- qlanguageserver.h qlanguageserver_p.h qlanguageserver.cpp
- qqmllanguageserver.h qqmllanguageserver.cpp
qmllanguageservertool.cpp
- workspace.cpp workspace.h
- textblock.h textblock.cpp
- textcursor.h textcursor.cpp
- textcursor.cpp textcursor.h
- textdocument.cpp textdocument.h
- qmllintsuggestions.h qmllintsuggestions.cpp
- textsynchronization.cpp textsynchronization.h
- qmlcompletionsupport.h qmlcompletionsupport.cpp
- qqmlcodemodel.h qqmlcodemodel.cpp
- ../shared/qqmltoolingsettings.h
- ../shared/qqmltoolingsettings.cpp
DEFINES
QT_USE_QSTRINGBUILDER
LIBRARIES
- Qt::QmlPrivate
- Qt::CorePrivate
- Qt::QmlDomPrivate
- Qt::LanguageServerPrivate
- Qt::QmlCompilerPrivate
+ Qt::QmlLSPrivate
+ Qt::QmlToolingSettingsPrivate
)
set_target_properties(qmlls PROPERTIES WIN32_EXECUTABLE FALSE)
+
+if(NOT QT6_IS_SHARED_LIBS_BUILD)
+ qt_import_plugins(qmlls INCLUDE Qt::QmlLSQuickPlugin)
+ target_link_libraries(qmlls PRIVATE Qt::QmlLSQuickPlugin)
+endif()
diff --git a/tools/qmlls/lspcustomtypes.h b/tools/qmlls/lspcustomtypes.h
deleted file mode 100644
index f46b36e578..0000000000
--- a/tools/qmlls/lspcustomtypes.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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 LSPCUSTOMTYPES_H
-#define LSPCUSTOMTYPES_H
-#include <QtLanguageServer/private/qlanguageserverspec_p.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QLspSpecification {
-
-class UriToBuildDirs
-{
-public:
- QByteArray baseUri = {};
- QList<QByteArray> buildDirs = {};
-
- template<typename W>
- void walk(W &w)
- {
- field(w, "baseUri", baseUri);
- field(w, "buildDirs", buildDirs);
- }
-};
-
-namespace Notifications {
-constexpr auto AddBuildDirsMethod = "$/addBuildDirs";
-
-class AddBuildDirsParams
-{
-public:
- QList<UriToBuildDirs> buildDirsToSet = {};
-
- template<typename W>
- void walk(W &w)
- {
- field(w, "buildDirsToSet", buildDirsToSet);
- }
-};
-} // namespace Notifications
-} // namespace QLspSpecification
-
-QT_END_NAMESPACE
-
-#endif // LSPCUSTOMTYPES_H
diff --git a/tools/qmlls/qlanguageserver.cpp b/tools/qmlls/qlanguageserver.cpp
deleted file mode 100644
index 4f9c9fe77d..0000000000
--- a/tools/qmlls/qlanguageserver.cpp
+++ /dev/null
@@ -1,376 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "qlanguageserver_p.h"
-#include <QtLanguageServer/private/qlspnotifysignals_p.h>
-#include <QtJsonRpc/private/qjsonrpcprotocol_p_p.h>
-
-QT_BEGIN_NAMESPACE
-
-using namespace QLspSpecification;
-using namespace Qt::StringLiterals;
-
-Q_LOGGING_CATEGORY(lspServerLog, "qt.languageserver.server")
-
-QLanguageServerPrivate::QLanguageServerPrivate(const QJsonRpcTransport::DataHandler &h)
- : protocol(h)
-{
-}
-
-/*!
-\internal
-\class QLanguageServer
-\brief Implements a server for the language server protocol
-
-QLanguageServer is a class that uses the QLanguageServerProtocol to
-provide a server implementation.
-It handles the lifecycle management, and can be extended via
-QLanguageServerModule subclasses.
-
-The language server keeps a strictly monotonically increasing runState that can be queried
-from any thread (and is thus mutex gated), the normal run state is DidInitialize.
-
-The language server also keeps track of the task canceled by the client (or implicitly when
-shutting down, and isRequestCanceled can be called from any thread.
-*/
-
-QLanguageServer::QLanguageServer(const QJsonRpcTransport::DataHandler &h, QObject *parent)
- : QObject(*new QLanguageServerPrivate(h), parent)
-{
- Q_D(QLanguageServer);
- registerMethods(*d->protocol.typedRpc());
- d->notifySignals.registerHandlers(&d->protocol);
-}
-
-QLanguageServerProtocol *QLanguageServer::protocol()
-{
- Q_D(QLanguageServer);
- return &d->protocol;
-}
-
-QLanguageServer::RunStatus QLanguageServer::runStatus() const
-{
- const Q_D(QLanguageServer);
- QMutexLocker l(&d->mutex);
- return d->runStatus;
-}
-
-void QLanguageServer::finishSetup()
-{
- Q_D(QLanguageServer);
- RunStatus rStatus;
- {
- QMutexLocker l(&d->mutex);
- rStatus = d->runStatus;
- if (rStatus == RunStatus::NotSetup)
- d->runStatus = RunStatus::SettingUp;
- }
- if (rStatus != RunStatus::NotSetup) {
- emit lifecycleError();
- return;
- }
- emit runStatusChanged(RunStatus::SettingUp);
-
- registerHandlers(&d->protocol);
- for (auto module : d->modules)
- module->registerHandlers(this, &d->protocol);
-
- {
- QMutexLocker l(&d->mutex);
- rStatus = d->runStatus;
- if (rStatus == RunStatus::SettingUp)
- d->runStatus = RunStatus::DidSetup;
- }
- if (rStatus != RunStatus::SettingUp) {
- emit lifecycleError();
- return;
- }
- emit runStatusChanged(RunStatus::DidSetup);
-}
-
-void QLanguageServer::addServerModule(QLanguageServerModule *serverModule)
-{
- Q_D(QLanguageServer);
- Q_ASSERT(serverModule);
- RunStatus rStatus;
- {
- QMutexLocker l(&d->mutex);
- rStatus = d->runStatus;
- if (rStatus == RunStatus::NotSetup) {
- if (d->modules.contains(serverModule->name())) {
- d->modules.insert(serverModule->name(), serverModule);
- qCWarning(lspServerLog) << "Duplicate add of QLanguageServerModule named"
- << serverModule->name() << ", overwriting.";
- } else {
- d->modules.insert(serverModule->name(), serverModule);
- }
- }
- }
- if (rStatus != RunStatus::NotSetup) {
- qCWarning(lspServerLog) << "Called QLanguageServer::addServerModule after setup";
- emit lifecycleError();
- return;
- }
-}
-
-QLanguageServerModule *QLanguageServer::moduleByName(const QString &n) const
-{
- const Q_D(QLanguageServer);
- QMutexLocker l(&d->mutex);
- return d->modules.value(n);
-}
-
-QLspNotifySignals *QLanguageServer::notifySignals()
-{
- Q_D(QLanguageServer);
- return &d->notifySignals;
-}
-
-void QLanguageServer::registerMethods(QJsonRpc::TypedRpc &typedRpc)
-{
- typedRpc.installMessagePreprocessor(
- [this](const QJsonDocument &doc, const QJsonParseError &err,
- const QJsonRpcProtocol::Handler<QJsonRpcProtocol::Response> &responder) {
- Q_D(QLanguageServer);
- if (!doc.isObject()) {
- qCWarning(lspServerLog)
- << "non object jsonrpc message" << doc << err.errorString();
- return QJsonRpcProtocol::Processing::Stop;
- }
- bool sendErrorResponse = false;
- RunStatus rState;
- QJsonValue id = doc.object()[u"id"];
- {
- QMutexLocker l(&d->mutex);
- // the normal case is d->runStatus == RunStatus::DidInitialize
- if (d->runStatus != RunStatus::DidInitialize) {
- if (d->runStatus == RunStatus::DidSetup && !doc.isNull()
- && doc.object()[u"method"].toString()
- == QString::fromUtf8(
- QLspSpecification::Requests::InitializeMethod)) {
- return QJsonRpcProtocol::Processing::Continue;
- } else if (!doc.isNull()
- && doc.object()[u"method"].toString()
- == QString::fromUtf8(
- QLspSpecification::Notifications::ExitMethod)) {
- return QJsonRpcProtocol::Processing::Continue;
- }
- if (id.isString() || id.isDouble()) {
- sendErrorResponse = true;
- rState = d->runStatus;
- } else {
- return QJsonRpcProtocol::Processing::Stop;
- }
- }
- }
- if (!sendErrorResponse) {
- if (id.isString() || id.isDouble()) {
- QMutexLocker l(&d->mutex);
- d->requestsInProgress.insert(id, QRequestInProgress {});
- }
- return QJsonRpcProtocol::Processing::Continue;
- }
- if (rState == RunStatus::NotSetup || rState == RunStatus::DidSetup)
- responder(QJsonRpcProtocol::MessageHandler::error(
- int(QLspSpecification::ErrorCodes::ServerNotInitialized),
- u"Request on non initialized Language Server (runStatus %1): %2"_s
- .arg(int(rState))
- .arg(QString::fromUtf8(doc.toJson()))));
- else
- responder(QJsonRpcProtocol::MessageHandler::error(
- int(QLspSpecification::ErrorCodes::InvalidRequest),
- u"Method called on stopping Language Server (runStatus %1)"_s.arg(
- int(rState))));
- return QJsonRpcProtocol::Processing::Stop;
- });
- typedRpc.installOnCloseAction([this](QJsonRpc::TypedResponse::Status,
- const QJsonRpc::IdType &id, QJsonRpc::TypedRpc &) {
- Q_D(QLanguageServer);
- QJsonValue idValue = QTypedJson::toJsonValue(id);
- bool lastReq;
- {
- QMutexLocker l(&d->mutex);
- d->requestsInProgress.remove(idValue);
- lastReq = d->runStatus == RunStatus::WaitPending && d->requestsInProgress.size() <= 1;
- if (lastReq)
- d->runStatus = RunStatus::Stopping;
- }
- if (lastReq)
- executeShutdown();
- });
-}
-
-void QLanguageServer::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &serverInfo)
-{
- Q_D(QLanguageServer);
- for (auto module : std::as_const(d->modules))
- module->setupCapabilities(clientInfo, serverInfo);
-}
-
-const QLspSpecification::InitializeParams &QLanguageServer::clientInfo() const
-{
- const Q_D(QLanguageServer);
-
- if (int(runStatus()) < int(RunStatus::DidInitialize))
- qCWarning(lspServerLog) << "asked for Language Server clientInfo before initialization";
- return d->clientInfo;
-}
-
-const QLspSpecification::InitializeResult &QLanguageServer::serverInfo() const
-{
- const Q_D(QLanguageServer);
- if (int(runStatus()) < int(RunStatus::DidInitialize))
- qCWarning(lspServerLog) << "asked for Language Server serverInfo before initialization";
- return d->serverInfo;
-}
-
-void QLanguageServer::receiveData(const QByteArray &d)
-{
- protocol()->receiveData(d);
-}
-
-void QLanguageServer::registerHandlers(QLanguageServerProtocol *protocol)
-{
- QObject::connect(notifySignals(), &QLspNotifySignals::receivedCancelNotification, this,
- [this](const QLspSpecification::Notifications::CancelParamsType &params) {
- Q_D(QLanguageServer);
- QJsonValue id = QTypedJson::toJsonValue(params.id);
- QMutexLocker l(&d->mutex);
- if (d->requestsInProgress.contains(id))
- d->requestsInProgress[id].canceled = true;
- else
- qCWarning(lspServerLog)
- << "Ignoring cancellation of non in progress request" << id;
- });
-
- protocol->registerInitializeRequestHandler(
- [this](const QByteArray &,
- const QLspSpecification::Requests::InitializeParamsType &params,
- QLspSpecification::Responses::InitializeResponseType &&response) {
- qCDebug(lspServerLog) << "init";
- Q_D(QLanguageServer);
- RunStatus rStatus;
- {
- QMutexLocker l(&d->mutex);
- rStatus = d->runStatus;
- if (rStatus == RunStatus::DidSetup)
- d->runStatus = RunStatus::Initializing;
- }
- if (rStatus != RunStatus::DidSetup) {
- if (rStatus == RunStatus::NotSetup || rStatus == RunStatus::SettingUp)
- response.sendErrorResponse(
- int(QLspSpecification::ErrorCodes::InvalidRequest),
- u"Initialization request received on non setup language server"_s
- .toUtf8());
- else
- response.sendErrorResponse(
- int(QLspSpecification::ErrorCodes::InvalidRequest),
- u"Received multiple initialization requests"_s.toUtf8());
- emit lifecycleError();
- return;
- }
- emit runStatusChanged(RunStatus::Initializing);
- d->clientInfo = params;
- setupCapabilities(d->clientInfo, d->serverInfo);
- {
- QMutexLocker l(&d->mutex);
- d->runStatus = RunStatus::DidInitialize;
- }
- emit runStatusChanged(RunStatus::DidInitialize);
- response.sendResponse(d->serverInfo);
- });
-
- QObject::connect(notifySignals(), &QLspNotifySignals::receivedInitializedNotification, this,
- [this](const QLspSpecification::Notifications::InitializedParamsType &) {
- Q_D(QLanguageServer);
- {
- QMutexLocker l(&d->mutex);
- d->clientInitialized = true;
- }
- emit clientInitialized(this);
- });
-
- protocol->registerShutdownRequestHandler(
- [this](const QByteArray &, const QLspSpecification::Requests::ShutdownParamsType &,
- QLspSpecification::Responses::ShutdownResponseType &&response) {
- Q_D(QLanguageServer);
- RunStatus rStatus;
- bool shouldExecuteShutdown = false;
- {
- QMutexLocker l(&d->mutex);
- rStatus = d->runStatus;
- if (rStatus == RunStatus::DidInitialize) {
- d->shutdownResponse = std::move(response);
- if (d->requestsInProgress.size() <= 1) {
- d->runStatus = RunStatus::Stopping;
- shouldExecuteShutdown = true;
- } else {
- d->runStatus = RunStatus::WaitPending;
- }
- }
- }
- if (rStatus != RunStatus::DidInitialize)
- emit lifecycleError();
- else if (shouldExecuteShutdown)
- executeShutdown();
- });
-
- QObject::connect(notifySignals(), &QLspNotifySignals::receivedExitNotification, this,
- [this](const QLspSpecification::Notifications::ExitParamsType &) {
- if (runStatus() != RunStatus::Stopped)
- emit lifecycleError();
- else
- emit exit();
- });
-}
-
-void QLanguageServer::executeShutdown()
-{
- RunStatus rStatus = runStatus();
- if (rStatus != RunStatus::Stopping) {
- emit lifecycleError();
- return;
- }
- emit shutdown();
- QLspSpecification::Responses::ShutdownResponseType shutdownResponse;
- {
- Q_D(QLanguageServer);
- QMutexLocker l(&d->mutex);
- rStatus = d->runStatus;
- if (rStatus == RunStatus::Stopping) {
- shutdownResponse = std::move(d->shutdownResponse);
- d->runStatus = RunStatus::Stopped;
- }
- }
- if (rStatus != RunStatus::Stopping)
- emit lifecycleError();
- else
- shutdownResponse.sendResponse(nullptr);
-}
-
-bool QLanguageServer::isRequestCanceled(const QJsonRpc::IdType &id) const
-{
- const Q_D(QLanguageServer);
- QJsonValue idVal = QTypedJson::toJsonValue(id);
- QMutexLocker l(&d->mutex);
- return d->requestsInProgress.value(idVal).canceled || d->runStatus != RunStatus::DidInitialize;
-}
-
-bool QLanguageServer::isInitialized() const
-{
- switch (runStatus()) {
- case RunStatus::NotSetup:
- case RunStatus::SettingUp:
- case RunStatus::DidSetup:
- case RunStatus::Initializing:
- return false;
- case RunStatus::DidInitialize:
- case RunStatus::WaitPending:
- case RunStatus::Stopping:
- case RunStatus::Stopped:
- break;
- }
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/tools/qmlls/qlanguageserver.h b/tools/qmlls/qlanguageserver.h
deleted file mode 100644
index d4172b192c..0000000000
--- a/tools/qmlls/qlanguageserver.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef QLANGUAGESERVER_P_H
-#define QLANGUAGESERVER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtLanguageServer/private/qlanguageserverspec_p.h>
-#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
-#include <QtLanguageServer/private/qlspnotifysignals_p.h>
-#include <QtCore/qloggingcategory.h>
-
-QT_BEGIN_NAMESPACE
-
-class QLanguageServer;
-class QLanguageServerPrivate;
-Q_DECLARE_LOGGING_CATEGORY(lspServerLog)
-
-class QLanguageServerModule : public QObject
-{
- Q_OBJECT
-public:
- QLanguageServerModule(QObject *parent = nullptr) : QObject(parent) { }
- virtual QString name() const = 0;
- virtual void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) = 0;
- virtual void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &) = 0;
-};
-
-class QLanguageServer : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(RunStatus runStatus READ runStatus NOTIFY runStatusChanged)
- Q_PROPERTY(bool isInitialized READ isInitialized)
-public:
- QLanguageServer(const QJsonRpcTransport::DataHandler &h, QObject *parent = nullptr);
- enum class RunStatus {
- NotSetup,
- SettingUp,
- DidSetup,
- Initializing,
- DidInitialize, // normal state of execution
- WaitPending,
- Stopping,
- Stopped
- };
- Q_ENUM(RunStatus)
-
- QLanguageServerProtocol *protocol();
- void finishSetup();
- void registerHandlers(QLanguageServerProtocol *protocol);
- void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &serverInfo);
- void addServerModule(QLanguageServerModule *serverModule);
- QLanguageServerModule *moduleByName(const QString &n) const;
- QLspNotifySignals *notifySignals();
-
- // API
- RunStatus runStatus() const;
- bool isInitialized() const;
- bool isRequestCanceled(const QJsonRpc::IdType &id) const;
- const QLspSpecification::InitializeParams &clientInfo() const;
- const QLspSpecification::InitializeResult &serverInfo() const;
-
-public Q_SLOTS:
- void receiveData(const QByteArray &d);
-Q_SIGNALS:
- void runStatusChanged(RunStatus);
- void clientInitialized(QLanguageServer *server);
- void shutdown();
- void exit();
- void lifecycleError();
-
-private:
- void registerMethods(QJsonRpc::TypedRpc &typedRpc);
- void executeShutdown();
- Q_DECLARE_PRIVATE(QLanguageServer)
-};
-
-QT_END_NAMESPACE
-
-#endif // QLANGUAGESERVER_P_H
diff --git a/tools/qmlls/qlanguageserver_p.h b/tools/qmlls/qlanguageserver_p.h
deleted file mode 100644
index f051309f90..0000000000
--- a/tools/qmlls/qlanguageserver_p.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef QLANGUAGESERVER_P_P_H
-#define QLANGUAGESERVER_P_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qlanguageserver.h"
-#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
-#include <QtCore/QMutex>
-#include <QtCore/QHash>
-#include <QtCore/private/qobject_p.h>
-#include <QtLanguageServer/private/qlspnotifysignals_p.h>
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-
-class QRequestInProgress
-{
-public:
- bool canceled = false;
-};
-
-class QLanguageServerPrivate : public QObjectPrivate
-{
-public:
- QLanguageServerPrivate(const QJsonRpcTransport::DataHandler &h);
- mutable QMutex mutex;
- // mutex gated, monotonically increasing
- QLanguageServer::RunStatus runStatus = QLanguageServer::RunStatus::NotSetup;
- QHash<QJsonValue, QRequestInProgress> requestsInProgress; // mutex gated
- bool clientInitialized = false; // mutex gated
- QLspSpecification::InitializeParams clientInfo; // immutable after runStatus > DidInitialize
- QLspSpecification::InitializeResult serverInfo; // immutable after runStatus > DidInitialize
- QLspSpecification::Responses::ShutdownResponseType shutdownResponse;
- QHash<QString, QLanguageServerModule *> modules;
- QLanguageServerProtocol protocol;
- QLspNotifySignals notifySignals;
-};
-
-QT_END_NAMESPACE
-#endif // QLANGUAGESERVER_P_P_H
diff --git a/tools/qmlls/qmlcompletionsupport.cpp b/tools/qmlls/qmlcompletionsupport.cpp
deleted file mode 100644
index a148bc3935..0000000000
--- a/tools/qmlls/qmlcompletionsupport.cpp
+++ /dev/null
@@ -1,665 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qmlcompletionsupport.h"
-#include "qqmllanguageserver.h"
-#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
-#include <QtCore/qthreadpool.h>
-#include <QtCore/private/qduplicatetracker_p.h>
-#include <QtCore/QRegularExpression>
-#include <QtQmlDom/private/qqmldomexternalitems_p.h>
-#include <QtQmlDom/private/qqmldomtop_p.h>
-
-QT_USE_NAMESPACE
-using namespace QLspSpecification;
-using namespace QQmlJS::Dom;
-using namespace Qt::StringLiterals;
-
-Q_LOGGING_CATEGORY(complLog, "qt.languageserver.completions")
-
-void QmlCompletionSupport::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
-{
- protocol->registerCompletionRequestHandler(
- [this](const QByteArray &, const CompletionParams &cParams,
- LSPPartialResponse<
- std::variant<QList<CompletionItem>, CompletionList, std::nullptr_t>,
- std::variant<CompletionList, QList<CompletionItem>>> &&response) {
- QmlLsp::OpenDocument doc = m_codeModel->openDocumentByUrl(
- QmlLsp::lspUriToQmlUrl(cParams.textDocument.uri));
- if (!doc.textDocument) {
- response.sendResponse(QList<CompletionItem>());
- return;
- }
- CompletionRequest *req = new CompletionRequest;
- std::optional<int> targetVersion;
- {
- QMutexLocker l(doc.textDocument->mutex());
- targetVersion = doc.textDocument->version();
- if (!targetVersion) {
- qCWarning(complLog) << "no target version for completions in "
- << QString::fromUtf8(cParams.textDocument.uri);
- }
- req->code = doc.textDocument->toPlainText();
- }
- req->minVersion = (targetVersion ? *targetVersion : 0);
- req->response = std::move(response);
- req->completionParams = cParams;
- {
- QMutexLocker l(&m_mutex);
- m_completions.insert(req->completionParams.textDocument.uri, req);
- }
- if (doc.snapshot.docVersion && *doc.snapshot.docVersion >= req->minVersion)
- updatedSnapshot(QmlLsp::lspUriToQmlUrl(req->completionParams.textDocument.uri));
- });
- protocol->registerCompletionItemResolveRequestHandler(
- [](const QByteArray &, const CompletionItem &cParams,
- LSPResponse<CompletionItem> &&response) { response.sendResponse(cParams); });
-}
-
-QmlCompletionSupport::QmlCompletionSupport(QmlLsp::QQmlCodeModel *codeModel)
- : m_codeModel(codeModel)
-{
- QObject::connect(m_codeModel, &QmlLsp::QQmlCodeModel::updatedSnapshot, this,
- &QmlCompletionSupport::updatedSnapshot);
-}
-
-QmlCompletionSupport::~QmlCompletionSupport()
-{
- QMutexLocker l(&m_mutex);
- qDeleteAll(m_completions);
- m_completions.clear();
-}
-
-QString QmlCompletionSupport::name() const
-{
- return u"QmlCompletionSupport"_s;
-}
-
-void QmlCompletionSupport::setupCapabilities(
- const QLspSpecification::InitializeParams &,
- QLspSpecification::InitializeResult &serverCapabilities)
-{
- QLspSpecification::CompletionOptions cOptions;
- if (serverCapabilities.capabilities.completionProvider)
- cOptions = *serverCapabilities.capabilities.completionProvider;
- cOptions.resolveProvider = false;
- cOptions.triggerCharacters = QList<QByteArray>({ QByteArray(".") });
- serverCapabilities.capabilities.completionProvider = cOptions;
-}
-
-void QmlCompletionSupport::updatedSnapshot(const QByteArray &url)
-{
- QmlLsp::OpenDocumentSnapshot doc = m_codeModel->snapshotByUrl(url);
- QList<CompletionRequest *> toCompl;
- {
- QMutexLocker l(&m_mutex);
- for (auto [it, end] = m_completions.equal_range(url); it != end; ++it) {
- if (doc.docVersion && it.value()->minVersion <= *doc.docVersion)
- toCompl.append(it.value());
- }
- if (!m_completions.isEmpty())
- qCDebug(complLog) << "updated " << QString::fromUtf8(url) << " v"
- << (doc.docVersion ? (*doc.docVersion) : -1) << ", completing"
- << m_completions.size() << "/" << m_completions.size();
- for (auto req : toCompl)
- m_completions.remove(url, req);
- }
- for (auto it = toCompl.rbegin(), end = toCompl.rend(); it != end; ++it) {
- CompletionRequest *req = *it;
- QThreadPool::globalInstance()->start([req, doc]() mutable {
- req->sendCompletions(doc);
- delete req;
- });
- }
-}
-
-struct ItemLocation
-{
- int depth = -1;
- DomItem domItem;
- FileLocations::Tree fileLocation;
-};
-
-QString CompletionRequest::urlAndPos() const
-{
- return QString::fromUtf8(completionParams.textDocument.uri) + u":"
- + QString::number(completionParams.position.line) + u":"
- + QString::number(completionParams.position.character);
-}
-
-// return the position of 0 based line and char offsets, never goes to the "next" line, but might
-// return the position of the \n or \r that starts the next line (never the one that starts line)
-static qsizetype posAfterLineChar(QString code, int line, int character)
-{
- int targetLine = line;
- qsizetype i = 0;
- while (i != code.size() && targetLine != 0) {
- QChar c = code.at(i++);
- if (c == u'\n') {
- --targetLine;
- }
- if (c == u'\r') {
- if (i != code.size() && code.at(i) == u'\n')
- ++i;
- --targetLine;
- }
- }
- qsizetype leftChars = character;
- while (i != code.size() && leftChars) {
- QChar c = code.at(i);
- if (c == u'\n' || c == u'\r')
- break;
- ++i;
- if (!c.isLowSurrogate())
- --leftChars;
- }
- return i;
-}
-
-static QList<ItemLocation> findLastItemsContaining(DomItem file, int line, int character)
-{
- QList<ItemLocation> itemsFound;
- std::shared_ptr<QmlFile> filePtr = file.ownerAs<QmlFile>();
- if (!filePtr)
- return itemsFound;
- FileLocations::Tree t = filePtr->fileLocationsTree();
- Q_ASSERT(t);
- QString code = filePtr->code(); // do something more advanced wrt to changes wrt to this->code?
- if (code.isEmpty())
- qCWarning(complLog) << "no code";
- QList<ItemLocation> toDo;
- qsizetype targetPos = posAfterLineChar(code, line, character);
- Q_ASSERT(targetPos >= 0);
- auto containsTarget = [targetPos](QQmlJS::SourceLocation l) {
- if constexpr (sizeof(qsizetype) <= sizeof(quint32)) {
- return l.begin() <= quint32(targetPos) && quint32(targetPos) < l.end();
- } else {
- return l.begin() <= targetPos && targetPos < l.end();
- }
- };
- if (containsTarget(t->info().fullRegion)) {
- ItemLocation loc;
- loc.depth = 0;
- loc.domItem = file;
- loc.fileLocation = t;
- toDo.append(loc);
- }
- while (!toDo.isEmpty()) {
- ItemLocation iLoc = toDo.last();
- toDo.removeLast();
- if (itemsFound.isEmpty() || itemsFound.constFirst().depth <= iLoc.depth) {
- if (!itemsFound.isEmpty() && itemsFound.constFirst().depth < iLoc.depth)
- itemsFound.clear();
- itemsFound.append(iLoc);
- }
- auto subEls = iLoc.fileLocation->subItems();
- for (auto it = subEls.begin(); it != subEls.end(); ++it) {
- auto subLoc = std::static_pointer_cast<AttachedInfoT<FileLocations>>(it.value());
- Q_ASSERT(subLoc);
- if (containsTarget(subLoc->info().fullRegion)) {
- ItemLocation subItem;
- subItem.depth = iLoc.depth + 1;
- subItem.domItem = iLoc.domItem.path(it.key());
- subItem.fileLocation = subLoc;
- toDo.append(subItem);
- }
- }
- }
- return itemsFound;
-}
-
-// finds the filter string, the base (for fully qualified accesses) and the whole string
-// just before pos in code
-struct CompletionContextStrings
-{
- CompletionContextStrings(QString code, qsizetype pos);
-
-public:
- // line up until pos
- QStringView preLine() const
- {
- return QStringView(m_code).mid(m_lineStart, m_pos - m_lineStart);
- }
- // the part used to filter the completion (normally actual filtering is left to the client)
- QStringView filterChars() const
- {
- return QStringView(m_code).mid(m_filterStart, m_pos - m_filterStart);
- }
- // the base part (qualified access)
- QStringView base() const
- {
- return QStringView(m_code).mid(m_baseStart, m_filterStart - m_baseStart);
- }
- // if we are at line start
- bool atLineStart() const { return m_atLineStart; }
-
-private:
- QString m_code; // the current code
- qsizetype m_pos = {}; // current position of the cursor
- qsizetype m_filterStart = {}; // start of the characters that are used to filter the suggestions
- qsizetype m_lineStart = {}; // start of the current line
- qsizetype m_baseStart = {}; // start of the dotted expression that ends at the cursor position
- bool m_atLineStart = {}; // if there are only spaces before base
-};
-
-CompletionContextStrings::CompletionContextStrings(QString code, qsizetype pos)
- : m_code(code), m_pos(pos)
-{
- // computes the context just before pos in code.
- // After this code all the values of all the attributes should be correct (see above)
- // handle also letter or numbers represented a surrogate pairs?
- m_filterStart = m_pos;
- while (m_filterStart != 0) {
- QChar c = code.at(m_filterStart - 1);
- if (!c.isLetterOrNumber() && c != u'_')
- break;
- else
- --m_filterStart;
- }
- // handle spaces?
- m_baseStart = m_filterStart;
- while (m_baseStart != 0) {
- QChar c = code.at(m_baseStart - 1);
- if (c != u'.' || m_baseStart == 1)
- break;
- c = code.at(m_baseStart - 2);
- if (!c.isLetterOrNumber() && c != u'_')
- break;
- qsizetype baseEnd = --m_baseStart;
- while (m_baseStart != 0) {
- QChar c = code.at(m_baseStart - 1);
- if (!c.isLetterOrNumber() && c != u'_')
- break;
- else
- --m_baseStart;
- }
- if (m_baseStart == baseEnd)
- break;
- }
- m_atLineStart = true;
- m_lineStart = m_baseStart;
- while (m_lineStart != 0) {
- QChar c = code.at(m_lineStart - 1);
- if (c == u'\n' || c == '\r')
- break;
- if (!c.isSpace())
- m_atLineStart = false;
- --m_lineStart;
- }
-}
-
-enum class TypeCompletionsType { None, Types, TypesAndAttributes };
-
-enum class FunctionCompletion { None, Declaration };
-
-enum class ImportCompletionType { None, Module, Version };
-
-void CompletionRequest::sendCompletions(QmlLsp::OpenDocumentSnapshot &doc)
-{
- QList<CompletionItem> res = completions(doc);
- response.sendResponse(res);
-}
-
-static QList<CompletionItem> importCompletions(DomItem &file, const CompletionContextStrings &ctx)
-{
- // returns completions for import statements, ctx is supposed to be in an import statement
- QList<CompletionItem> res;
- ImportCompletionType importCompletionType = ImportCompletionType::None;
- QRegularExpression spaceRe(uR"(\W+)"_s);
- QList<QStringView> linePieces = ctx.preLine().split(spaceRe, Qt::SkipEmptyParts);
- qsizetype effectiveLength = linePieces.size()
- + ((!ctx.preLine().isEmpty() && ctx.preLine().last().isSpace()) ? 1 : 0);
- if (effectiveLength < 2) {
- CompletionItem comp;
- comp.label = "import";
- comp.kind = int(CompletionItemKind::Keyword);
- res.append(comp);
- }
- if (linePieces.isEmpty() || linePieces.first() != u"import")
- return res;
- if (effectiveLength == 2) {
- // the cursor is after the import, possibly in a partial module name
- importCompletionType = ImportCompletionType::Module;
- } else if (effectiveLength == 3) {
- if (linePieces.last() != u"as") {
- // the cursor is after the module, possibly in a partial version token (or partial as)
- CompletionItem comp;
- comp.label = "as";
- comp.kind = int(CompletionItemKind::Keyword);
- res.append(comp);
- importCompletionType = ImportCompletionType::Version;
- }
- }
- DomItem env = file.environment();
- if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
- switch (importCompletionType) {
- case ImportCompletionType::None:
- break;
- case ImportCompletionType::Module: {
- QDuplicateTracker<QString> modulesSeen;
- for (const QString &uri : envPtr->moduleIndexUris(env)) {
- QStringView base = ctx.base(); // if we allow spaces we should get rid of them
- if (uri.startsWith(base)) {
- QStringList rest = uri.mid(base.size()).split(u'.');
- if (rest.isEmpty())
- continue;
-
- const QString label = rest.first();
- if (!modulesSeen.hasSeen(label)) {
- CompletionItem comp;
- comp.label = label.toUtf8();
- comp.kind = int(CompletionItemKind::Module);
- res.append(comp);
- }
- }
- }
- break;
- }
- case ImportCompletionType::Version:
- if (ctx.base().isEmpty()) {
- for (int majorV :
- envPtr->moduleIndexMajorVersions(env, linePieces.at(1).toString())) {
- CompletionItem comp;
- comp.label = QString::number(majorV).toUtf8();
- comp.kind = int(CompletionItemKind::Constant);
- res.append(comp);
- }
- } else {
- bool hasMajorVersion = ctx.base().endsWith(u'.');
- int majorV = -1;
- if (hasMajorVersion)
- majorV = ctx.base().mid(0, ctx.base().size() - 1).toInt(&hasMajorVersion);
- if (!hasMajorVersion)
- break;
- if (std::shared_ptr<ModuleIndex> mIndex =
- envPtr->moduleIndexWithUri(env, linePieces.at(1).toString(), majorV)) {
- for (int minorV : mIndex->minorVersions()) {
- CompletionItem comp;
- comp.label = QString::number(minorV).toUtf8();
- comp.kind = int(CompletionItemKind::Constant);
- res.append(comp);
- }
- }
- }
- break;
- }
- }
- return res;
-}
-
-static QList<CompletionItem> idsCompletions(DomItem component)
-{
- qCDebug(complLog) << "adding ids completions";
- QList<CompletionItem> res;
- for (const QString &k : component.field(Fields::ids).keys()) {
- CompletionItem comp;
- comp.label = k.toUtf8();
- comp.kind = int(CompletionItemKind::Value);
- res.append(comp);
- }
- return res;
-}
-
-static QList<CompletionItem> bindingsCompletions(DomItem &containingObject)
-{
- // returns valid bindings completions (i.e. reachable properties and signal handlers)
- QList<CompletionItem> res;
- qCDebug(complLog) << "binding completions";
- containingObject.visitPrototypeChain(
- [&res](DomItem &it) {
- qCDebug(complLog) << "prototypeChain" << it.internalKindStr() << it.canonicalPath();
- if (const QmlObject *itPtr = it.as<QmlObject>()) {
- // signal handlers
- auto methods = itPtr->methods();
- auto it = methods.cbegin();
- while (it != methods.cend()) {
- if (it.value().methodType == MethodInfo::MethodType::Signal) {
- CompletionItem comp;
- QString signal = it.key();
- comp.label =
- (u"on"_s + signal.at(0).toUpper() + signal.mid(1)).toUtf8();
- res.append(comp);
- }
- ++it;
- }
- // properties that can be bound
- auto pDefs = itPtr->propertyDefs();
- for (auto it2 = pDefs.keyBegin(); it2 != pDefs.keyEnd(); ++it2) {
- qCDebug(complLog) << "adding property" << *it2;
- CompletionItem comp;
- comp.label = it2->toUtf8();
- comp.insertText = (*it2 + u": "_s).toUtf8();
- comp.kind = int(CompletionItemKind::Property);
- res.append(comp);
- }
- }
- return true;
- },
- VisitPrototypesOption::Normal);
- return res;
-}
-
-static QList<CompletionItem> reachableSymbols(DomItem &context, const CompletionContextStrings &ctx,
- TypeCompletionsType typeCompletionType,
- FunctionCompletion completeMethodCalls)
-{
- // returns completions for the reachable types or attributes from context
- QList<CompletionItem> res;
- QMap<CompletionItemKind, QSet<QString>> symbols;
- QSet<quintptr> visited;
- QList<Path> visitedRefs;
- auto addLocalSymbols = [&res, typeCompletionType, completeMethodCalls, &symbols](DomItem &el) {
- switch (typeCompletionType) {
- case TypeCompletionsType::None:
- return false;
- case TypeCompletionsType::Types:
- switch (el.internalKind()) {
- case DomType::ImportScope: {
- const QSet<QString> localSymbols = el.localSymbolNames(
- LocalSymbolsType::QmlTypes | LocalSymbolsType::Namespaces);
- qCDebug(complLog) << "adding local symbols of:" << el.internalKindStr()
- << el.canonicalPath() << localSymbols;
- symbols[CompletionItemKind::Class] += localSymbols;
- break;
- }
- default: {
- qCDebug(complLog) << "skipping local symbols for non type" << el.internalKindStr()
- << el.canonicalPath();
- break;
- }
- }
- break;
- case TypeCompletionsType::TypesAndAttributes:
- auto localSymbols = el.localSymbolNames(LocalSymbolsType::All);
- if (const QmlObject *elPtr = el.as<QmlObject>()) {
- auto methods = elPtr->methods();
- auto it = methods.cbegin();
- while (it != methods.cend()) {
- localSymbols.remove(it.key());
- if (completeMethodCalls == FunctionCompletion::Declaration) {
- QStringList parameters;
- for (const MethodParameter &pInfo : std::as_const(it->parameters)) {
- QStringList param;
- if (!pInfo.typeName.isEmpty())
- param << pInfo.typeName;
- if (!pInfo.name.isEmpty())
- param << pInfo.name;
- if (pInfo.defaultValue) {
- param << u"= " + pInfo.defaultValue->code();
- }
- parameters.append(param.join(' '));
- }
-
- QString commentsStr;
-
- if (!it->comments.regionComments.isEmpty()) {
- for (const Comment &c : it->comments.regionComments[0].preComments) {
- commentsStr += c.rawComment().toString().trimmed() + '\n';
- }
- }
-
- CompletionItem comp;
- comp.documentation =
- u"%1%2(%3)"_s.arg(commentsStr, it.key(), parameters.join(u", "))
- .toUtf8();
- comp.label = (it.key() + u"()").toUtf8();
- comp.kind = int(CompletionItemKind::Function);
-
- if (it->typeName.isEmpty())
- comp.detail = "returns void";
- else
- comp.detail = (u"returns "_s + it->typeName).toUtf8();
-
- // Only append full bracket if there are no parameters
- if (it->parameters.isEmpty())
- comp.insertText = comp.label;
- else
- // add snippet support?
- comp.insertText = (it.key() + u"(").toUtf8();
-
- res.append(comp);
- }
- ++it;
- }
- }
- qCDebug(complLog) << "adding local symbols of:" << el.internalKindStr()
- << el.canonicalPath() << localSymbols;
- symbols[CompletionItemKind::Field] += localSymbols;
- break;
- }
- return true;
- };
- if (ctx.base().isEmpty()) {
- if (typeCompletionType != TypeCompletionsType::None) {
- qCDebug(complLog) << "adding symbols reachable from:" << context.internalKindStr()
- << context.canonicalPath();
- DomItem it = context.proceedToScope();
- it.visitScopeChain(addLocalSymbols, LookupOption::Normal, &defaultErrorHandler,
- &visited, &visitedRefs);
- }
- } else {
- QList<QStringView> baseItems = ctx.base().split(u'.', Qt::SkipEmptyParts);
- Q_ASSERT(!baseItems.isEmpty());
- auto addReachableSymbols = [&visited, &visitedRefs, &addLocalSymbols](Path,
- DomItem &it) -> bool {
- qCDebug(complLog) << "adding directly accessible symbols of" << it.internalKindStr()
- << it.canonicalPath();
- it.visitDirectAccessibleScopes(addLocalSymbols, VisitPrototypesOption::Normal,
- &defaultErrorHandler, &visited, &visitedRefs);
- return true;
- };
- Path toSearch = Paths::lookupSymbolPath(ctx.base().toString().chopped(1));
- context.resolve(toSearch, addReachableSymbols, &defaultErrorHandler);
- // add attached types? technically we should...
- }
- for (auto symbolKinds = symbols.constBegin(); symbolKinds != symbols.constEnd();
- ++symbolKinds) {
- for (auto symbol = symbolKinds.value().constBegin();
- symbol != symbolKinds.value().constEnd(); ++symbol) {
- CompletionItem comp;
- comp.label = symbol->toUtf8();
- comp.kind = int(symbolKinds.key());
- res.append(comp);
- }
- }
- return res;
-}
-
-QList<CompletionItem> CompletionRequest::completions(QmlLsp::OpenDocumentSnapshot &doc) const
-{
- QList<CompletionItem> res;
- if (!doc.validDoc) {
- qCWarning(complLog) << "No valid document for completions for "
- << QString::fromUtf8(completionParams.textDocument.uri);
- // try to add some import and global completions?
- return res;
- }
- if (!doc.docVersion || *doc.docVersion < minVersion) {
- qCWarning(complLog) << "sendCompletions on older doc version";
- } else if (!doc.validDocVersion || *doc.validDocVersion < minVersion) {
- qCWarning(complLog) << "using outdated valid doc, position might be incorrect";
- }
- DomItem file = doc.validDoc.fileObject(QQmlJS::Dom::GoTo::MostLikely);
- // clear reference cache to resolve latest versions (use a local env instead?)
- if (std::shared_ptr<DomEnvironment> envPtr = file.environment().ownerAs<DomEnvironment>())
- envPtr->clearReferenceCache();
- qsizetype pos = posAfterLineChar(code, completionParams.position.line,
- completionParams.position.character);
- CompletionContextStrings ctx(code, pos);
- QList<ItemLocation> itemsFound =
- findLastItemsContaining(file, completionParams.position.line,
- completionParams.position.character - ctx.filterChars().size());
- if (itemsFound.size() > 1) {
- QStringList paths;
- for (auto &it : itemsFound)
- paths.append(it.domItem.canonicalPath().toString());
- qCWarning(complLog) << "Multiple elements of " << urlAndPos()
- << " at the same depth:" << paths << "(using first)";
- }
- DomItem currentItem;
- if (!itemsFound.isEmpty())
- currentItem = itemsFound.first().domItem;
- qCDebug(complLog) << "Completion at " << urlAndPos() << " " << completionParams.position.line
- << ":" << completionParams.position.character << "offset:" << pos
- << "base:" << ctx.base() << "filter:" << ctx.filterChars()
- << "lastVersion:" << (doc.docVersion ? (*doc.docVersion) : -1)
- << "validVersion:" << (doc.validDocVersion ? (*doc.validDocVersion) : -1)
- << "in" << currentItem.internalKindStr() << currentItem.canonicalPath();
- DomItem containingObject = currentItem.qmlObject();
- TypeCompletionsType typeCompletionType = TypeCompletionsType::None;
- FunctionCompletion methodCompletion = FunctionCompletion::Declaration;
-
- if (!containingObject) {
- methodCompletion = FunctionCompletion::None;
- // global completions
- if (ctx.atLineStart()) {
- if (ctx.base().isEmpty()) {
- {
- CompletionItem comp;
- comp.label = "pragma";
- comp.kind = int(CompletionItemKind::Keyword);
- res.append(comp);
- }
- }
- typeCompletionType = TypeCompletionsType::Types;
- }
- // Import completion
- res += importCompletions(file, ctx);
- } else {
- methodCompletion = FunctionCompletion::Declaration;
- bool addIds = false;
-
- if (ctx.atLineStart() && currentItem.internalKind() != DomType::ScriptExpression
- && currentItem.internalKind() != DomType::List) {
- // add bindings
- methodCompletion = FunctionCompletion::None;
- if (ctx.base().isEmpty()) {
- for (const QStringView &s : std::array<QStringView, 5>(
- { u"property", u"readonly", u"default", u"signal", u"function" })) {
- CompletionItem comp;
- comp.label = s.toUtf8();
- comp.kind = int(CompletionItemKind::Keyword);
- res.append(comp);
- }
- res += bindingsCompletions(containingObject);
- typeCompletionType = TypeCompletionsType::Types;
- } else {
- // handle value types later with type expansion
- typeCompletionType = TypeCompletionsType::TypesAndAttributes;
- }
- } else {
- addIds = true;
- typeCompletionType = TypeCompletionsType::TypesAndAttributes;
- }
- if (addIds) {
- res += idsCompletions(containingObject.component());
- }
- }
-
- DomItem context = containingObject;
- if (!context)
- context = file;
- // adds types and attributes
- res += reachableSymbols(context, ctx, typeCompletionType, methodCompletion);
- return res;
-}
diff --git a/tools/qmlls/qmlcompletionsupport.h b/tools/qmlls/qmlcompletionsupport.h
deleted file mode 100644
index ce149667bd..0000000000
--- a/tools/qmlls/qmlcompletionsupport.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef QMLCOMPLETIONSUPPORT_H
-#define QMLCOMPLETIONSUPPORT_H
-
-#include "qlanguageserver.h"
-#include "qqmlcodemodel.h"
-#include <QtCore/qmutex.h>
-#include <QtCore/qhash.h>
-
-struct CompletionRequest
-{
- int minVersion;
- QString code;
- QLspSpecification::CompletionParams completionParams;
- QLspSpecification::LSPPartialResponse<
- std::variant<QList<QLspSpecification::CompletionItem>,
- QLspSpecification::CompletionList, std::nullptr_t>,
- std::variant<QLspSpecification::CompletionList,
- QList<QLspSpecification::CompletionItem>>>
- response;
- void sendCompletions(QmlLsp::OpenDocumentSnapshot &);
- QString urlAndPos() const;
- QList<QLspSpecification::CompletionItem> completions(QmlLsp::OpenDocumentSnapshot &doc) const;
-};
-
-class QmlCompletionSupport : public QLanguageServerModule
-{
- Q_OBJECT
-public:
- QmlCompletionSupport(QmlLsp::QQmlCodeModel *codeModel);
- ~QmlCompletionSupport();
- QString name() const override;
- void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
- void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &) override;
-public Q_SLOTS:
- void updatedSnapshot(const QByteArray &uri);
-
-private:
- QmlLsp::QQmlCodeModel *m_codeModel;
- QMutex m_mutex;
- QMultiHash<QString, CompletionRequest *> m_completions;
-};
-
-#endif // QMLCOMPLETIONSUPPORT_H
diff --git a/tools/qmlls/qmllanguageservertool.cpp b/tools/qmlls/qmllanguageservertool.cpp
index 896e0c049c..34138638b7 100644
--- a/tools/qmlls/qmllanguageservertool.cpp
+++ b/tools/qmlls/qmllanguageservertool.cpp
@@ -1,12 +1,14 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "qqmllanguageserver.h"
+
+#include <QtQmlLS/private/qqmllanguageserver_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qcoreapplication.h>
-#include "../shared/qqmltoolingsettings.h"
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+#include <QtQmlToolingSettings/private/qqmltoolingutils_p.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonarray.h>
@@ -131,7 +133,7 @@ int main(int argv, char *argc[])
QCoreApplication app(argv, argc);
QCoreApplication::setApplicationName("qmlls");
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
-#if QT_CONFIG(commandlineparser)
+
QCommandLineParser parser;
QQmlToolingSettings settings(QLatin1String("qmlls"));
parser.setApplicationDescription(QLatin1String(R"(QML languageserver)"));
@@ -163,6 +165,17 @@ int main(int argv, char *argc[])
parser.addOption(buildDirOption);
settings.addOption(buildDir);
+ QString qmlImportPath = QStringLiteral(u"qml-import-path");
+ QCommandLineOption qmlImportPathOption(
+ QStringList() << "I", QLatin1String("Look for QML modules in the specified directory"),
+ qmlImportPath);
+ parser.addOption(qmlImportPathOption);
+
+ QCommandLineOption environmentOption(
+ QStringList() << "E",
+ QLatin1String("Use the QML_IMPORT_PATH environment variable to look for QML Modules"));
+ parser.addOption(environmentOption);
+
QCommandLineOption writeDefaultsOption(
QStringList() << "write-defaults",
QLatin1String("Writes defaults settings to .qmlls.ini and exits (Warning: This "
@@ -174,6 +187,12 @@ int main(int argv, char *argc[])
"command line options into consideration"));
parser.addOption(ignoreSettings);
+ QCommandLineOption noCMakeCallsOption(
+ QStringList() << "no-cmake-calls",
+ QLatin1String("Disables automatic CMake rebuilds and C++ file watching."));
+ parser.addOption(noCMakeCallsOption);
+ settings.addOption("no-cmake-calls", "false");
+
parser.process(app);
if (parser.isSet(writeDefaultsOption)) {
@@ -203,7 +222,6 @@ int main(int argv, char *argc[])
QThread::sleep(waitSeconds);
qDebug() << "starting";
}
-#endif
QMutex writeMutex;
QQmlLanguageServer qmlServer(
[&writeMutex](const QByteArray &data) {
@@ -212,8 +230,74 @@ int main(int argv, char *argc[])
std::cout.flush();
},
(parser.isSet(ignoreSettings) ? nullptr : &settings));
- if (parser.isSet(buildDirOption))
- qmlServer.codeModel()->setBuildPathsForRootUrl(QByteArray(), parser.values(buildDirOption));
+
+ const bool disableCMakeCallsViaEnvironment =
+ qmlGetConfigOption<bool, qmlConvertBoolConfigOption>("QMLLS_NO_CMAKE_CALLS");
+
+ if (disableCMakeCallsViaEnvironment || parser.isSet(noCMakeCallsOption)) {
+ if (disableCMakeCallsViaEnvironment) {
+ qWarning() << "Disabling CMake calls via QMLLS_NO_CMAKE_CALLS environment variable.";
+ } else {
+ qWarning() << "Disabling CMake calls via command line switch.";
+ }
+
+ qmlServer.codeModel()->disableCMakeCalls();
+ }
+
+ if (parser.isSet(buildDirOption)) {
+ const QStringList dirs =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromOption(parser, buildDirOption);
+
+ qInfo().nospace().noquote()
+ << "Using build directories passed by -b: \"" << dirs.join(u"\", \""_s) << "\".";
+
+ qmlServer.codeModel()->setBuildPathsForRootUrl(QByteArray(), dirs);
+ } else if (QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv("QMLLS_BUILD_DIRS");
+ !dirsFromEnv.isEmpty()) {
+
+ // warn now at qmlls startup that those directories will be used later in qqmlcodemodel when
+ // searching for build folders.
+ qInfo().nospace().noquote() << "Using build directories passed from environment variable "
+ "\"QMLLS_BUILD_DIRS\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+
+ } else {
+ qInfo() << "Using the build directories found in the .qmlls.ini file. Your build folder "
+ "might not be found if no .qmlls.ini files are present in the root source "
+ "folder.";
+ }
+ QStringList importPaths{ QLibraryInfo::path(QLibraryInfo::QmlImportsPath) };
+ if (parser.isSet(qmlImportPathOption)) {
+ const QStringList pathsFromOption =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromOption(parser, qmlImportPathOption);
+ qInfo().nospace().noquote() << "Using import directories passed by -I: \""
+ << pathsFromOption.join(u"\", \""_s) << "\".";
+ importPaths << pathsFromOption;
+ }
+ if (parser.isSet(environmentOption)) {
+ if (const QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML_IMPORT_PATH"_s);
+ !dirsFromEnv.isEmpty()) {
+ qInfo().nospace().noquote()
+ << "Using import directories passed from environment variable "
+ "\"QML_IMPORT_PATH\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+ importPaths << dirsFromEnv;
+ }
+
+ if (const QStringList dirsFromEnv2 =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML2_IMPORT_PATH"_s);
+ !dirsFromEnv2.isEmpty()) {
+ qInfo().nospace().noquote()
+ << "Using import directories passed from the deprecated environment variable "
+ "\"QML2_IMPORT_PATH\": \""
+ << dirsFromEnv2.join(u"\", \""_s) << "\".";
+ importPaths << dirsFromEnv2;
+ }
+ }
+ qmlServer.codeModel()->setImportPaths(importPaths);
+
StdinReader r;
QObject::connect(&r, &StdinReader::receivedData,
qmlServer.server(), &QLanguageServer::receiveData);
diff --git a/tools/qmlls/qmllintsuggestions.cpp b/tools/qmlls/qmllintsuggestions.cpp
deleted file mode 100644
index d08209a054..0000000000
--- a/tools/qmlls/qmllintsuggestions.cpp
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "qmllintsuggestions.h"
-#include <QtLanguageServer/private/qlanguageserverspec_p.h>
-#include <QtQmlCompiler/private/qqmljslinter_p.h>
-#include <QtQmlCompiler/private/qqmljslogger_p.h>
-#include <QtCore/qlibraryinfo.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qdebug.h>
-
-using namespace QLspSpecification;
-using namespace QQmlJS::Dom;
-using namespace Qt::StringLiterals;
-
-Q_LOGGING_CATEGORY(lintLog, "qt.languageserver.lint")
-
-QT_BEGIN_NAMESPACE
-namespace QmlLsp {
-
-static DiagnosticSeverity severityFromMsgType(QtMsgType t)
-{
- switch (t) {
- case QtDebugMsg:
- return DiagnosticSeverity::Hint;
- case QtInfoMsg:
- return DiagnosticSeverity::Information;
- case QtWarningMsg:
- return DiagnosticSeverity::Warning;
- case QtCriticalMsg:
- case QtFatalMsg:
- break;
- }
- return DiagnosticSeverity::Error;
-}
-
-static void codeActionHandler(
- const QByteArray &, const CodeActionParams &params,
- LSPPartialResponse<std::variant<QList<std::variant<Command, CodeAction>>, std::nullptr_t>,
- QList<std::variant<Command, CodeAction>>> &&response)
-{
- QList<std::variant<Command, CodeAction>> responseData;
-
- for (const Diagnostic &diagnostic : params.context.diagnostics) {
- if (!diagnostic.data.has_value())
- continue;
-
- const auto &data = diagnostic.data.value();
-
- int version = data[u"version"].toInt();
- QJsonArray suggestions = data[u"suggestions"].toArray();
-
- QList<TextDocumentEdit> edits;
- QString message;
- for (const QJsonValue &suggestion : suggestions) {
- QString replacement = suggestion[u"replacement"].toString();
- message += suggestion[u"message"].toString() + u"\n";
-
- TextEdit textEdit;
- textEdit.range = { Position { suggestion[u"lspBeginLine"].toInt(),
- suggestion[u"lspBeginCharacter"].toInt() },
- Position { suggestion[u"lspEndLine"].toInt(),
- suggestion[u"lspEndCharacter"].toInt() } };
- textEdit.newText = replacement.toUtf8();
-
- TextDocumentEdit textDocEdit;
- textDocEdit.textDocument = { params.textDocument, version };
- textDocEdit.edits.append(textEdit);
-
- edits.append(textDocEdit);
- }
- message.chop(1);
- WorkspaceEdit edit;
- edit.documentChanges = edits;
-
- CodeAction action;
- action.kind = u"refactor.rewrite"_s.toUtf8();
- action.edit = edit;
- action.title = message.toUtf8();
-
- responseData.append(action);
- }
-
- response.sendResponse(responseData);
-}
-
-void QmlLintSuggestions::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
-{
- protocol->registerCodeActionRequestHandler(&codeActionHandler);
-}
-
-void QmlLintSuggestions::setupCapabilities(const QLspSpecification::InitializeParams &,
- QLspSpecification::InitializeResult &serverInfo)
-{
- serverInfo.capabilities.codeActionProvider = true;
-}
-
-QmlLintSuggestions::QmlLintSuggestions(QLanguageServer *server, QmlLsp::QQmlCodeModel *codeModel)
- : m_server(server), m_codeModel(codeModel)
-{
- QObject::connect(m_codeModel, &QmlLsp::QQmlCodeModel::updatedSnapshot, this,
- &QmlLintSuggestions::diagnose, Qt::DirectConnection);
-}
-
-void QmlLintSuggestions::diagnose(const QByteArray &url)
-{
- const int maxInvalidMsec = 4000;
- qCDebug(lintLog) << "diagnose start";
- QmlLsp::OpenDocumentSnapshot snapshot = m_codeModel->snapshotByUrl(url);
- QList<Diagnostic> diagnostics;
- std::optional<int> version;
- DomItem doc;
- {
- QMutexLocker l(&m_mutex);
- LastLintUpdate &lastUpdate = m_lastUpdate[url];
- if (lastUpdate.version && *lastUpdate.version == version) {
- qCDebug(lspServerLog) << "skipped update of " << url << "unchanged valid doc";
- return;
- }
- if (snapshot.validDocVersion
- && (!lastUpdate.version || *snapshot.validDocVersion > *lastUpdate.version)) {
- doc = snapshot.validDoc;
- version = snapshot.validDocVersion;
- } else if (snapshot.docVersion
- && (!lastUpdate.version || *snapshot.docVersion > *lastUpdate.version)) {
- if (!lastUpdate.version || !snapshot.validDocVersion
- || (lastUpdate.invalidUpdatesSince
- && lastUpdate.invalidUpdatesSince->msecsTo(QDateTime::currentDateTime())
- > maxInvalidMsec)) {
- doc = snapshot.doc;
- version = snapshot.docVersion;
- } else if (!lastUpdate.invalidUpdatesSince) {
- lastUpdate.invalidUpdatesSince = QDateTime::currentDateTime();
- QTimer::singleShot(maxInvalidMsec, Qt::VeryCoarseTimer, this,
- [this, url]() { diagnose(url); });
- }
- }
- if (doc) {
- // update immediately, and do not keep track of sent version, thus in extreme cases sent
- // updates could be out of sync
- lastUpdate.version = version;
- lastUpdate.invalidUpdatesSince.reset();
- }
- }
- QString fileContents;
- if (doc) {
- qCDebug(lintLog) << "has doc, do real lint";
- QStringList imports = m_codeModel->buildPathsForFileUrl(url);
- imports.append(QLibraryInfo::path(QLibraryInfo::QmlImportsPath));
- // add m_server->clientInfo().rootUri & co?
- bool silent = true;
- QString filename = doc.canonicalFilePath();
- fileContents = doc.field(Fields::code).value().toString();
- QStringList qmltypesFiles;
- QStringList resourceFiles;
- QList<QQmlJSLogger::Category> categories;
-
- QQmlJSLinter linter(imports);
-
- linter.lintFile(filename, &fileContents, silent, nullptr, imports, qmltypesFiles,
- resourceFiles, categories);
- auto addLength = [&fileContents](Position &position, int startOffset, int length) {
- int i = startOffset;
- int iEnd = i + length;
- if (iEnd > int(fileContents.size()))
- iEnd = fileContents.size();
- while (i < iEnd) {
- if (fileContents.at(i) == u'\n') {
- ++position.line;
- position.character = 0;
- if (i + 1 < iEnd && fileContents.at(i) == u'\r')
- ++i;
- } else {
- ++position.character;
- }
- ++i;
- }
- };
-
- auto messageToDiagnostic = [&addLength, &version](const Message &message) {
- Diagnostic diagnostic;
- diagnostic.severity = severityFromMsgType(message.type);
- Range &range = diagnostic.range;
- Position &position = range.start;
-
- QQmlJS::SourceLocation srcLoc = message.loc;
-
- position.line = srcLoc.isValid() ? srcLoc.startLine - 1 : 0;
- position.character = srcLoc.isValid() ? srcLoc.startColumn - 1 : 0;
- range.end = position;
- addLength(range.end, srcLoc.isValid() ? message.loc.offset : 0, srcLoc.isValid() ? message.loc.length : 0);
- diagnostic.message = message.message.toUtf8();
- diagnostic.source = QByteArray("qmllint");
-
- auto suggestion = message.fixSuggestion;
- if (suggestion.has_value()) {
- // We need to interject the information about where the fix suggestions end
- // here since we don't have access to the textDocument to calculate it later.
- QJsonArray fixedSuggestions;
- for (const FixSuggestion::Fix &fix : suggestion->fixes) {
- QQmlJS::SourceLocation cut = fix.cutLocation;
-
- int line = cut.isValid() ? cut.startLine - 1 : 0;
- int column = cut.isValid() ? cut.startColumn - 1 : 0;
-
- QJsonObject object;
- object[u"lspBeginLine"] = line;
- object[u"lspBeginCharacter"] = column;
-
- Position end = { line, column };
-
- addLength(end, srcLoc.isValid() ? cut.offset : 0,
- srcLoc.isValid() ? cut.length : 0);
- object[u"lspEndLine"] = end.line;
- object[u"lspEndCharacter"] = end.character;
-
- object[u"message"] = fix.message;
- object[u"replacement"] = fix.replacementString;
-
- fixedSuggestions << object;
- }
- QJsonObject data;
- data[u"suggestions"] = fixedSuggestions;
-
- Q_ASSERT(version.has_value());
- data[u"version"] = version.value();
-
- diagnostic.data = data;
- }
- return diagnostic;
- };
- doc.iterateErrors(
- [&diagnostics, &addLength](DomItem, ErrorMessage msg) {
- Diagnostic diagnostic;
- diagnostic.severity = severityFromMsgType(QtMsgType(int(msg.level)));
- // do something with msg.errorGroups ?
- auto &location = msg.location;
- Range &range = diagnostic.range;
- range.start.line = location.startLine - 1;
- range.start.character = location.startColumn - 1;
- range.end = range.start;
- addLength(range.end, location.offset, location.length);
- diagnostic.code = QByteArray(msg.errorId.data(), msg.errorId.size());
- diagnostic.source = "domParsing";
- diagnostic.message = msg.message.toUtf8();
- diagnostics.append(diagnostic);
- return true;
- },
- true);
-
- if (const QQmlJSLogger *logger = linter.logger()) {
- qsizetype nDiagnostics = diagnostics.size();
- for (const auto &messages : { logger->infos(), logger->warnings(), logger->errors() }) {
- for (const Message &message : messages) {
- diagnostics.append(messageToDiagnostic(message));
- }
- }
- if (diagnostics.size() != nDiagnostics && imports.size() == 1) {
- Diagnostic diagnostic;
- diagnostic.severity = DiagnosticSeverity::Warning;
- Range &range = diagnostic.range;
- Position &position = range.start;
- position.line = 0;
- position.character = 0;
- Position &positionEnd = range.end;
- positionEnd.line = 1;
- diagnostic.message =
- "qmlls could not find a build directory, without a build directory "
- "containing a current build there could be spurious warnings, you might "
- "want to pass the --build-dir <buildDir> option to qmlls, or set the "
- "environment variable QMLLS_BUILD_DIRS.";
- diagnostic.source = QByteArray("qmllint");
- diagnostics.append(diagnostic);
- }
- }
- }
- PublishDiagnosticsParams diagnosticParams;
- diagnosticParams.uri = url;
- diagnosticParams.diagnostics = diagnostics;
- diagnosticParams.version = version;
-
- m_server->protocol()->notifyPublishDiagnostics(diagnosticParams);
- qCDebug(lintLog) << "lint" << QString::fromUtf8(url) << "found"
- << diagnosticParams.diagnostics.size() << "issues"
- << QTypedJson::toJsonValue(diagnosticParams);
-}
-
-} // namespace QmlLsp
-QT_END_NAMESPACE
diff --git a/tools/qmlls/qmllintsuggestions.h b/tools/qmlls/qmllintsuggestions.h
deleted file mode 100644
index 9d738a269f..0000000000
--- a/tools/qmlls/qmllintsuggestions.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef QMLLINTSUGGESTIONS_H
-#define QMLLINTSUGGESTIONS_H
-
-#include "qlanguageserver.h"
-#include "qqmlcodemodel.h"
-
-#include <optional>
-
-QT_BEGIN_NAMESPACE
-namespace QmlLsp {
-struct LastLintUpdate
-{
- std::optional<int> version;
- std::optional<QDateTime> invalidUpdatesSince;
-};
-
-class QmlLintSuggestions : public QLanguageServerModule
-{
- Q_OBJECT
-public:
- QmlLintSuggestions(QLanguageServer *server, QmlLsp::QQmlCodeModel *codeModel);
-
- QString name() const override { return QLatin1StringView("QmlLint Suggestions"); }
-public Q_SLOTS:
- void diagnose(const QByteArray &uri);
- void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
- void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &) override;
-
-private:
- QMutex m_mutex;
- QHash<QByteArray, LastLintUpdate> m_lastUpdate;
- QLanguageServer *m_server;
- QmlLsp::QQmlCodeModel *m_codeModel;
-};
-} // namespace QmlLsp
-QT_END_NAMESPACE
-#endif // QMLLINTSUGGESTIONS_H
diff --git a/tools/qmlls/qqmlcodemodel.cpp b/tools/qmlls/qqmlcodemodel.cpp
deleted file mode 100644
index 9c3521aa57..0000000000
--- a/tools/qmlls/qqmlcodemodel.cpp
+++ /dev/null
@@ -1,700 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "qqmllanguageserver.h"
-#include "qqmlcodemodel.h"
-#include <QtCore/qfileinfo.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qthreadpool.h>
-#include <QtCore/qlibraryinfo.h>
-#include <QtQmlDom/private/qqmldomtop_p.h>
-#include "textdocument.h"
-
-#include <memory>
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-namespace QmlLsp {
-
-Q_LOGGING_CATEGORY(codeModelLog, "qt.languageserver.codemodel")
-
-using namespace QQmlJS::Dom;
-using namespace Qt::StringLiterals;
-
-/*!
-\internal
-\class QQmlCodeModel
-
-The code model offers a view of the current state of the current files, and traks open files.
-All methods are threadsafe, and generally return immutable or threadsafe objects that can be
-worked on from any thread (unless otherwise noted).
-The idea is the let all other operations be as lock free as possible, concentrating all tricky
-synchronization here.
-
-\section2 Global views
-\list
-\li currentEnv() offers a view that contains the latest version of all the loaded files
-\li validEnv() is just like current env but stores only the valid (meaning correctly parsed,
- not necessarily without errors) version of a file, it is normally a better choice to load the
- dependencies/symbol information from
-\endlist
-
-\section2 OpenFiles
-\list
-\li snapshotByUrl() returns an OpenDocumentSnapshot of an open document. From it you can get the
- document, its latest valid version, scope, all connected to a specific version of the document
- and immutable. The signal updatedSnapshot() is called every time a snapshot changes (also for
- every partial change: document change, validDocument change, scope change).
-\li openDocumentByUrl() is a lower level and more intrusive access to OpenDocument objects. These
- contains the current snapshot, and shared pointer to a Utils::TextDocument. This is *always* the
- current version of the document, and has line by line support.
- Working on it is more delicate and intrusive, because you have to explicitly acquire its mutex()
- before *any* read or write/modification to it.
- It has a version nuber which is supposed to always change and increase.
- It is mainly used for highlighting/indenting, and is immediately updated when the user edits a
- document. Its use should be avoided if possible, preferring the snapshots.
-\endlist
-
-\section2 Parallelism/Theading
-Most operations are not parallel and usually take place in the main thread (but are still thread
-safe).
-There are two main task that are executed in parallel: Indexing, and OpenDocumentUpdate.
-Indexing is meant to keep the global view up to date.
-OpenDocumentUpdate keeps the snapshots of the open documents up to date.
-
-There is always a tension between being responsive, using all threads available, and avoid to hog
-too many resources. One can choose different parallelization strategies, we went with a flexiable
-approach.
-We have (private) functions that execute part of the work: indexSome() and openUpdateSome(). These
-do all locking needed, get some work, do it without locks, and at the end update the state of the
-code model. If there is more work, then they return true. Thus while (xxxSome()); works until there
-is no work left.
-
-addDirectoriesToIndex(), the internal addDirectory() and addOpenToUpdate() add more work to do.
-
-indexNeedsUpdate() and openNeedUpdate(), check if there is work to do, and if yes ensure that a
-worker thread (or more) that work on it exist.
-*/
-
-QQmlCodeModel::QQmlCodeModel(QObject *parent, QQmlToolingSettings *settings)
- : QObject { parent },
- m_currentEnv(std::make_shared<DomEnvironment>(
- QStringList(QLibraryInfo::path(QLibraryInfo::QmlImportsPath)),
- DomEnvironment::Option::SingleThreaded)),
- m_validEnv(std::make_shared<DomEnvironment>(
- QStringList(QLibraryInfo::path(QLibraryInfo::QmlImportsPath)),
- DomEnvironment::Option::SingleThreaded)),
- m_settings(settings)
-{
-}
-
-QQmlCodeModel::~QQmlCodeModel()
-{
- while (true) {
- bool shouldWait;
- {
- QMutexLocker l(&m_mutex);
- m_state = State::Stopping;
- m_openDocumentsToUpdate.clear();
- shouldWait = m_nIndexInProgress != 0 || m_nUpdateInProgress != 0;
- }
- if (!shouldWait)
- break;
- QThread::yieldCurrentThread();
- }
-}
-
-OpenDocumentSnapshot QQmlCodeModel::snapshotByUrl(const QByteArray &url)
-{
- return openDocumentByUrl(url).snapshot;
-}
-
-int QQmlCodeModel::indexEvalProgress() const
-{
- Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
- const int dirCost = 10;
- int costToDo = 1;
- for (const ToIndex &el : std::as_const(m_toIndex))
- costToDo += dirCost * el.leftDepth;
- costToDo += m_indexInProgressCost;
- return m_indexDoneCost * 100 / (costToDo + m_indexDoneCost);
-}
-
-void QQmlCodeModel::indexStart()
-{
- Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
- qCDebug(codeModelLog) << "indexStart";
-}
-
-void QQmlCodeModel::indexEnd()
-{
- Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
- qCDebug(codeModelLog) << "indexEnd";
- m_lastIndexProgress = 0;
- m_nIndexInProgress = 0;
- m_toIndex.clear();
- m_indexInProgressCost = 0;
- m_indexDoneCost = 0;
-}
-
-void QQmlCodeModel::indexSendProgress(int progress)
-{
- if (progress <= m_lastIndexProgress)
- return;
- m_lastIndexProgress = progress;
- // ### actually send progress
-}
-
-bool QQmlCodeModel::indexCancelled()
-{
- QMutexLocker l(&m_mutex);
- if (m_state == State::Stopping)
- return true;
- return false;
-}
-
-void QQmlCodeModel::indexDirectory(const QString &path, int depthLeft)
-{
- if (indexCancelled())
- return;
- QDir dir(path);
- if (depthLeft > 1) {
- const QStringList dirs =
- dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
- for (const QString &child : dirs)
- addDirectory(dir.filePath(child), --depthLeft);
- }
- const QStringList qmljs = dir.entryList(QStringList({ "*.qml", "*.js", "*.mjs" }), QDir::Files);
- int progress = 0;
- {
- QMutexLocker l(&m_mutex);
- m_indexInProgressCost += qmljs.size();
- progress = indexEvalProgress();
- }
- indexSendProgress(progress);
- if (qmljs.isEmpty())
- return;
- DomItem newCurrent = m_currentEnv.makeCopy(DomItem::CopyOption::EnvConnected).item();
- for (const QString &file : qmljs) {
- if (indexCancelled())
- return;
- QString fPath = dir.filePath(file);
- QFileInfo fInfo(fPath);
- QString cPath = fInfo.canonicalFilePath();
- if (!cPath.isEmpty()) {
- newCurrent.loadBuiltins();
- newCurrent.loadFile(cPath, fPath, [](Path, DomItem &, DomItem &) {}, {});
- newCurrent.loadPendingDependencies();
- newCurrent.commitToBase(m_validEnv.ownerAs<DomEnvironment>());
- }
- {
- QMutexLocker l(&m_mutex);
- ++m_indexDoneCost;
- --m_indexInProgressCost;
- progress = indexEvalProgress();
- }
- indexSendProgress(progress);
- }
-}
-
-void QQmlCodeModel::addDirectoriesToIndex(const QStringList &paths, QLanguageServer *server)
-{
- Q_UNUSED(server);
- // ### create progress, &scan in a separate instance
- const int maxDepth = 5;
- for (const auto &path : paths)
- addDirectory(path, maxDepth);
- indexNeedsUpdate();
-}
-
-void QQmlCodeModel::addDirectory(const QString &path, int depthLeft)
-{
- if (depthLeft < 1)
- return;
- {
- QMutexLocker l(&m_mutex);
- for (auto it = m_toIndex.begin(); it != m_toIndex.end();) {
- if (it->path.startsWith(path)) {
- if (it->path.size() == path.size())
- return;
- if (it->path.at(path.size()) == u'/') {
- it = m_toIndex.erase(it);
- continue;
- }
- } else if (path.startsWith(it->path) && path.at(it->path.size()) == u'/')
- return;
- ++it;
- }
- m_toIndex.append({ path, depthLeft });
- }
-}
-
-void QQmlCodeModel::removeDirectory(const QString &path)
-{
- {
- QMutexLocker l(&m_mutex);
- auto toRemove = [path](const QString &p) {
- return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
- };
- auto it = m_toIndex.begin();
- auto end = m_toIndex.end();
- while (it != end) {
- if (toRemove(it->path))
- it = m_toIndex.erase(it);
- else
- ++it;
- }
- }
- if (auto validEnvPtr = m_validEnv.ownerAs<DomEnvironment>())
- validEnvPtr->removePath(path);
- if (auto currentEnvPtr = m_currentEnv.ownerAs<DomEnvironment>())
- currentEnvPtr->removePath(path);
-}
-
-QString QQmlCodeModel::url2Path(const QByteArray &url, UrlLookup options)
-{
- QString res;
- {
- QMutexLocker l(&m_mutex);
- res = m_url2path.value(url);
- }
- if (!res.isEmpty() && options == UrlLookup::Caching)
- return res;
- QUrl qurl(QString::fromUtf8(url));
- QFileInfo f(qurl.toLocalFile());
- QString cPath = f.canonicalFilePath();
- if (cPath.isEmpty())
- cPath = f.filePath();
- {
- QMutexLocker l(&m_mutex);
- if (!res.isEmpty() && res != cPath)
- m_path2url.remove(res);
- m_url2path.insert(url, cPath);
- m_path2url.insert(cPath, url);
- }
- return cPath;
-}
-
-void QQmlCodeModel::newOpenFile(const QByteArray &url, int version, const QString &docText)
-{
- {
- QMutexLocker l(&m_mutex);
- auto &openDoc = m_openDocuments[url];
- if (!openDoc.textDocument)
- openDoc.textDocument = std::make_shared<Utils::TextDocument>();
- QMutexLocker l2(openDoc.textDocument->mutex());
- openDoc.textDocument->setVersion(version);
- openDoc.textDocument->setPlainText(docText);
- }
- addOpenToUpdate(url);
- openNeedUpdate();
-}
-
-OpenDocument QQmlCodeModel::openDocumentByUrl(const QByteArray &url)
-{
- QMutexLocker l(&m_mutex);
- return m_openDocuments.value(url);
-}
-
-void QQmlCodeModel::indexNeedsUpdate()
-{
- const int maxIndexThreads = 1;
- {
- QMutexLocker l(&m_mutex);
- if (m_toIndex.isEmpty() || m_nIndexInProgress >= maxIndexThreads)
- return;
- if (++m_nIndexInProgress == 1)
- indexStart();
- }
- QThreadPool::globalInstance()->start([this]() {
- while (indexSome()) { }
- });
-}
-
-bool QQmlCodeModel::indexSome()
-{
- qCDebug(codeModelLog) << "indexSome";
- ToIndex toIndex;
- {
- QMutexLocker l(&m_mutex);
- if (m_toIndex.isEmpty()) {
- if (--m_nIndexInProgress == 0)
- indexEnd();
- return false;
- }
- toIndex = m_toIndex.last();
- m_toIndex.removeLast();
- }
- bool hasMore = false;
- {
- auto guard = qScopeGuard([this, &hasMore]() {
- QMutexLocker l(&m_mutex);
- if (m_toIndex.isEmpty()) {
- if (--m_nIndexInProgress == 0)
- indexEnd();
- hasMore = false;
- } else {
- hasMore = true;
- }
- });
- indexDirectory(toIndex.path, toIndex.leftDepth);
- }
- return hasMore;
-}
-
-void QQmlCodeModel::openNeedUpdate()
-{
- qCDebug(codeModelLog) << "openNeedUpdate";
- const int maxIndexThreads = 1;
- {
- QMutexLocker l(&m_mutex);
- if (m_openDocumentsToUpdate.isEmpty() || m_nUpdateInProgress >= maxIndexThreads)
- return;
- if (++m_nUpdateInProgress == 1)
- openUpdateStart();
- }
- QThreadPool::globalInstance()->start([this]() {
- while (openUpdateSome()) { }
- });
-}
-
-bool QQmlCodeModel::openUpdateSome()
-{
- qCDebug(codeModelLog) << "openUpdateSome start";
- QByteArray toUpdate;
- {
- QMutexLocker l(&m_mutex);
- if (m_openDocumentsToUpdate.isEmpty()) {
- if (--m_nUpdateInProgress == 0)
- openUpdateEnd();
- return false;
- }
- auto it = m_openDocumentsToUpdate.find(m_lastOpenDocumentUpdated);
- auto end = m_openDocumentsToUpdate.end();
- if (it == end)
- it = m_openDocumentsToUpdate.begin();
- else if (++it == end)
- it = m_openDocumentsToUpdate.begin();
- toUpdate = *it;
- m_openDocumentsToUpdate.erase(it);
- }
- bool hasMore = false;
- {
- auto guard = qScopeGuard([this, &hasMore]() {
- QMutexLocker l(&m_mutex);
- if (m_openDocumentsToUpdate.isEmpty()) {
- if (--m_nUpdateInProgress == 0)
- openUpdateEnd();
- hasMore = false;
- } else {
- hasMore = true;
- }
- });
- openUpdate(toUpdate);
- }
- return hasMore;
-}
-
-void QQmlCodeModel::openUpdateStart()
-{
- qCDebug(codeModelLog) << "openUpdateStart";
-}
-
-void QQmlCodeModel::openUpdateEnd()
-{
- qCDebug(codeModelLog) << "openUpdateEnd";
-}
-
-void QQmlCodeModel::newDocForOpenFile(const QByteArray &url, int version, const QString &docText)
-{
- qCDebug(codeModelLog) << "updating doc" << url << "to version" << version << "("
- << docText.size() << "chars)";
- DomItem newCurrent = m_currentEnv.makeCopy(DomItem::CopyOption::EnvConnected).item();
- QStringList loadPaths = buildPathsForFileUrl(url);
- loadPaths.append(QLibraryInfo::path(QLibraryInfo::QmlImportsPath));
- if (std::shared_ptr<DomEnvironment> newCurrentPtr = newCurrent.ownerAs<DomEnvironment>()) {
- newCurrentPtr->setLoadPaths(loadPaths);
- }
- QString fPath = url2Path(url, UrlLookup::ForceLookup);
- Path p;
- newCurrent.loadFile(
- fPath, fPath, docText, QDateTime::currentDateTimeUtc(),
- [&p](Path, DomItem &, DomItem &newValue) { p = newValue.fileObject().canonicalPath(); },
- {});
- newCurrent.loadPendingDependencies();
- if (p) {
- newCurrent.commitToBase(m_validEnv.ownerAs<DomEnvironment>());
- DomItem item = m_currentEnv.path(p);
- {
- QMutexLocker l(&m_mutex);
- OpenDocument &doc = m_openDocuments[url];
- if (!doc.textDocument) {
- qCWarning(lspServerLog)
- << "ignoring update to closed document" << QString::fromUtf8(url);
- return;
- } else {
- QMutexLocker l(doc.textDocument->mutex());
- if (doc.textDocument->version() && *doc.textDocument->version() > version) {
- qCWarning(lspServerLog)
- << "docUpdate: version" << version << "of document"
- << QString::fromUtf8(url) << "is not the latest anymore";
- return;
- }
- }
- if (!doc.snapshot.docVersion || *doc.snapshot.docVersion < version) {
- doc.snapshot.docVersion = version;
- doc.snapshot.doc = item;
- } else {
- qCWarning(lspServerLog) << "skipping update of current doc to obsolete version"
- << version << "of document" << QString::fromUtf8(url);
- }
- if (item.field(Fields::isValid).value().toBool(false)) {
- if (!doc.snapshot.validDocVersion || *doc.snapshot.validDocVersion < version) {
- DomItem vDoc = m_validEnv.path(p);
- doc.snapshot.validDocVersion = version;
- doc.snapshot.validDoc = vDoc;
- } else {
- qCWarning(lspServerLog) << "skippig update of valid doc to obsolete version"
- << version << "of document" << QString::fromUtf8(url);
- }
- } else {
- qCWarning(lspServerLog)
- << "avoid update of validDoc to " << version << "of document"
- << QString::fromUtf8(url) << "as it is invalid";
- }
- }
- }
- if (codeModelLog().isDebugEnabled()) {
- qCDebug(codeModelLog) << "finished update doc of " << url << "to version" << version;
- snapshotByUrl(url).dump(qDebug() << "postSnapshot",
- OpenDocumentSnapshot::DumpOption::AllCode);
- }
- // we should update the scope in the future thus call addOpen(url)
- emit updatedSnapshot(url);
-}
-
-void QQmlCodeModel::closeOpenFile(const QByteArray &url)
-{
- QMutexLocker l(&m_mutex);
- m_openDocuments.remove(url);
-}
-
-void QQmlCodeModel::setRootUrls(const QList<QByteArray> &urls)
-{
- QMutexLocker l(&m_mutex);
- m_rootUrls = urls;
-}
-
-void QQmlCodeModel::addRootUrls(const QList<QByteArray> &urls)
-{
- QMutexLocker l(&m_mutex);
- for (const QByteArray &url : urls) {
- if (!m_rootUrls.contains(url))
- m_rootUrls.append(url);
- }
-}
-
-void QQmlCodeModel::removeRootUrls(const QList<QByteArray> &urls)
-{
- QMutexLocker l(&m_mutex);
- for (const QByteArray &url : urls)
- m_rootUrls.removeOne(url);
-}
-
-QList<QByteArray> QQmlCodeModel::rootUrls() const
-{
- QMutexLocker l(&m_mutex);
- return m_rootUrls;
-}
-
-QStringList QQmlCodeModel::buildPathsForRootUrl(const QByteArray &url)
-{
- QMutexLocker l(&m_mutex);
- return m_buildPathsForRootUrl.value(url);
-}
-
-static bool isNotSeparator(char c)
-{
- return c != '/';
-}
-
-QStringList QQmlCodeModel::buildPathsForFileUrl(const QByteArray &url)
-{
- QList<QByteArray> roots;
- {
- QMutexLocker l(&m_mutex);
- roots = m_buildPathsForRootUrl.keys();
- }
- // we want to longest match to be first, as it should override shorter matches
- std::sort(roots.begin(), roots.end(), [](const QByteArray &el1, const QByteArray &el2) {
- if (el1.size() > el2.size())
- return true;
- if (el1.size() < el2.size())
- return false;
- return el1 < el2;
- });
- QStringList buildPaths;
- QStringList defaultValues;
- if (!roots.isEmpty() && roots.last().isEmpty())
- roots.removeLast();
- QByteArray urlSlash(url);
- if (!urlSlash.isEmpty() && isNotSeparator(urlSlash.at(urlSlash.size() - 1)))
- urlSlash.append('/');
- // look if the file has a know prefix path
- for (const QByteArray &root : roots) {
- if (urlSlash.startsWith(root)) {
- buildPaths += buildPathsForRootUrl(root);
- break;
- }
- }
- QString path = url2Path(url);
- if (buildPaths.isEmpty() && m_settings) {
- // look in the settings
- m_settings->search(path);
- QString buildDir = QStringLiteral(u"buildDir");
- if (m_settings->isSet(buildDir))
- buildPaths += m_settings->value(buildDir).toString().split(',', Qt::SkipEmptyParts);
- }
- if (buildPaths.isEmpty()) {
- // default values
- buildPaths += buildPathsForRootUrl(QByteArray());
- }
- // env variable
- QStringList envPaths = qEnvironmentVariable("QMLLS_BUILD_DIRS").split(',', Qt::SkipEmptyParts);
- buildPaths += envPaths;
- if (buildPaths.isEmpty()) {
- // heuristic to find build dir
- QDir d(path);
- d.setNameFilters(QStringList({ u"build*"_s }));
- const int maxDirDepth = 8;
- int iDir = maxDirDepth;
- QString dirName = d.dirName();
- QDateTime lastModified;
- while (d.cdUp() && --iDir > 0) {
- for (const QFileInfo &fInfo : d.entryInfoList(QDir::Dirs)) {
- if (fInfo.completeBaseName() == u"build"
- || fInfo.completeBaseName().startsWith(u"build-%1"_s.arg(dirName))) {
- if (iDir > 1)
- iDir = 1;
- if (!lastModified.isValid() || lastModified < fInfo.lastModified()) {
- buildPaths.clear();
- buildPaths.append(fInfo.absoluteFilePath());
- }
- }
- }
- }
- }
- // add dependent build directories
- QStringList res;
- std::reverse(buildPaths.begin(), buildPaths.end());
- const int maxDeps = 4;
- while (!buildPaths.isEmpty()) {
- QString bPath = buildPaths.last();
- buildPaths.removeLast();
- res += bPath;
- if (QFile::exists(bPath + u"/_deps") && bPath.split(u"/_deps/"_s).size() < maxDeps) {
- QDir d(bPath + u"/_deps");
- for (const QFileInfo &fInfo : d.entryInfoList(QDir::Dirs))
- buildPaths.append(fInfo.absoluteFilePath());
- }
- }
- return res;
-}
-
-void QQmlCodeModel::setBuildPathsForRootUrl(QByteArray url, const QStringList &paths)
-{
- QMutexLocker l(&m_mutex);
- if (!url.isEmpty() && isNotSeparator(url.at(url.size() - 1)))
- url.append('/');
- if (paths.isEmpty())
- m_buildPathsForRootUrl.remove(url);
- else
- m_buildPathsForRootUrl.insert(url, paths);
-}
-
-void QQmlCodeModel::openUpdate(const QByteArray &url)
-{
- bool updateDoc = false;
- bool updateScope = false;
- std::optional<int> rNow = 0;
- QString docText;
- DomItem validDoc;
- std::shared_ptr<Utils::TextDocument> document;
- {
- QMutexLocker l(&m_mutex);
- OpenDocument &doc = m_openDocuments[url];
- document = doc.textDocument;
- if (!document)
- return;
- {
- QMutexLocker l2(document->mutex());
- rNow = document->version();
- }
- if (rNow && (!doc.snapshot.docVersion || *doc.snapshot.docVersion != *rNow))
- updateDoc = true;
- else if (doc.snapshot.validDocVersion
- && (!doc.snapshot.scopeVersion
- || *doc.snapshot.scopeVersion != *doc.snapshot.validDocVersion))
- updateScope = true;
- else
- return;
- if (updateDoc) {
- QMutexLocker l2(doc.textDocument->mutex());
- rNow = doc.textDocument->version();
- docText = doc.textDocument->toPlainText();
- } else {
- validDoc = doc.snapshot.validDoc;
- rNow = doc.snapshot.validDocVersion;
- }
- }
- if (updateDoc) {
- newDocForOpenFile(url, *rNow, docText);
- }
- if (updateScope) {
- // to do
- }
-}
-
-void QQmlCodeModel::addOpenToUpdate(const QByteArray &url)
-{
- QMutexLocker l(&m_mutex);
- m_openDocumentsToUpdate.insert(url);
-}
-
-QDebug OpenDocumentSnapshot::dump(QDebug dbg, DumpOptions options)
-{
- dbg.noquote().nospace() << "{";
- dbg << " url:" << QString::fromUtf8(url) << "\n";
- dbg << " docVersion:" << (docVersion ? QString::number(*docVersion) : u"*none*"_s) << "\n";
- if (options & DumpOption::LatestCode) {
- dbg << " doc: ------------\n"
- << doc.field(Fields::code).value().toString() << "\n==========\n";
- } else {
- dbg << u" doc:"
- << (doc ? u"%1chars"_s.arg(doc.field(Fields::code).value().toString().size())
- : u"*none*"_s)
- << "\n";
- }
- dbg << " validDocVersion:"
- << (validDocVersion ? QString::number(*validDocVersion) : u"*none*"_s) << "\n";
- if (options & DumpOption::ValidCode) {
- dbg << " validDoc: ------------\n"
- << validDoc.field(Fields::code).value().toString() << "\n==========\n";
- } else {
- dbg << u" validDoc:"
- << (validDoc ? u"%1chars"_s.arg(
- validDoc.field(Fields::code).value().toString().size())
- : u"*none*"_s)
- << "\n";
- }
- dbg << " scopeVersion:" << (scopeVersion ? QString::number(*scopeVersion) : u"*none*"_s)
- << "\n";
- dbg << " scopeDependenciesLoadTime:" << scopeDependenciesLoadTime << "\n";
- dbg << " scopeDependenciesChanged" << scopeDependenciesChanged << "\n";
- dbg << "}";
- return dbg;
-}
-
-} // namespace QmlLsp
-
-QT_END_NAMESPACE
diff --git a/tools/qmlls/qqmlcodemodel.h b/tools/qmlls/qqmlcodemodel.h
deleted file mode 100644
index 549f7dd1a1..0000000000
--- a/tools/qmlls/qqmlcodemodel.h
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef QQMLCODEMODEL_H
-#define QQMLCODEMODEL_H
-
-#include <QObject>
-#include <QHash>
-#include <QtQmlDom/private/qqmldomitem_p.h>
-#include <QtQmlCompiler/private/qqmljsscope_p.h>
-#include "qlanguageserver_p.h"
-#include "textdocument.h"
-#include "../shared/qqmltoolingsettings.h"
-
-#include <functional>
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-class TextSynchronization;
-namespace QmlLsp {
-
-class OpenDocumentSnapshot
-{
-public:
- enum class DumpOption {
- NoCode = 0,
- LatestCode = 0x1,
- ValidCode = 0x2,
- AllCode = LatestCode | ValidCode
- };
- Q_DECLARE_FLAGS(DumpOptions, DumpOption)
- QStringList searchPath;
- QByteArray url;
- std::optional<int> docVersion;
- QQmlJS::Dom::DomItem doc;
- std::optional<int> validDocVersion;
- QQmlJS::Dom::DomItem validDoc;
- std::optional<int> scopeVersion;
- QDateTime scopeDependenciesLoadTime;
- bool scopeDependenciesChanged = false;
- QQmlJSScope::Ptr scope = {};
- QDebug dump(QDebug dbg, DumpOptions dump = DumpOption::NoCode);
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(OpenDocumentSnapshot::DumpOptions)
-
-class OpenDocument
-{
-public:
- OpenDocumentSnapshot snapshot;
- std::shared_ptr<Utils::TextDocument> textDocument;
-};
-
-struct ToIndex
-{
- QString path;
- int leftDepth;
-};
-
-class QQmlCodeModel : public QObject
-{
- Q_OBJECT
-public:
- enum class UrlLookup { Caching, ForceLookup };
- enum class State { Running, Stopping };
-
- explicit QQmlCodeModel(QObject *parent = nullptr, QQmlToolingSettings *settings = nullptr);
- ~QQmlCodeModel();
- QQmlJS::Dom::DomItem currentEnv();
- QQmlJS::Dom::DomItem validEnv();
- OpenDocumentSnapshot snapshotByUrl(const QByteArray &url);
- OpenDocument openDocumentByUrl(const QByteArray &url);
-
- void openNeedUpdate();
- void indexNeedsUpdate();
- void addDirectoriesToIndex(const QStringList &paths, QLanguageServer *server);
- void addOpenToUpdate(const QByteArray &);
- void removeDirectory(const QString &path);
- // void updateDocument(const OpenDocument &doc);
- QString url2Path(const QByteArray &url, UrlLookup options = UrlLookup::Caching);
- void newOpenFile(const QByteArray &url, int version, const QString &docText);
- void newDocForOpenFile(const QByteArray &url, int version, const QString &docText);
- void closeOpenFile(const QByteArray &url);
- void setRootUrls(const QList<QByteArray> &urls);
- QList<QByteArray> rootUrls() const;
- void addRootUrls(const QList<QByteArray> &urls);
- QStringList buildPathsForRootUrl(const QByteArray &url);
- QStringList buildPathsForFileUrl(const QByteArray &url);
- void setBuildPathsForRootUrl(QByteArray url, const QStringList &paths);
- void removeRootUrls(const QList<QByteArray> &urls);
- QQmlToolingSettings *settings();
-Q_SIGNALS:
- void updatedSnapshot(const QByteArray &url);
-private:
- void indexDirectory(const QString &path, int depthLeft);
- int indexEvalProgress() const; // to be called in the mutex
- void indexStart(); // to be called in the mutex
- void indexEnd(); // to be called in the mutex
- void indexSendProgress(int progress);
- bool indexCancelled();
- bool indexSome();
- void addDirectory(const QString &path, int leftDepth);
- bool openUpdateSome();
- void openUpdateStart();
- void openUpdateEnd();
- void openUpdate(const QByteArray &);
- mutable QMutex m_mutex;
- State m_state = State::Running;
- int m_lastIndexProgress = 0;
- int m_nIndexInProgress = 0;
- QList<ToIndex> m_toIndex;
- int m_indexInProgressCost = 0;
- int m_indexDoneCost = 0;
- int m_nUpdateInProgress = 0;
- QQmlJS::Dom::DomItem m_currentEnv;
- QQmlJS::Dom::DomItem m_validEnv;
- QByteArray m_lastOpenDocumentUpdated;
- QSet<QByteArray> m_openDocumentsToUpdate;
- QHash<QByteArray, QStringList> m_buildPathsForRootUrl;
- QList<QByteArray> m_rootUrls;
- QHash<QByteArray, QString> m_url2path;
- QHash<QString, QByteArray> m_path2url;
- QHash<QByteArray, OpenDocument> m_openDocuments;
- QQmlToolingSettings *m_settings;
-};
-
-} // namespace QmlLsp
-QT_END_NAMESPACE
-#endif // QQMLCODEMODEL_H
diff --git a/tools/qmlls/qqmllanguageserver.cpp b/tools/qmlls/qqmllanguageserver.cpp
deleted file mode 100644
index 48639f37d2..0000000000
--- a/tools/qmlls/qqmllanguageserver.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "qqmllanguageserver.h"
-
-#include "textsynchronization.h"
-
-#include "qlanguageserver.h"
-#include "lspcustomtypes.h"
-#include <QtCore/qdir.h>
-
-#include <iostream>
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-namespace QmlLsp {
-
-using namespace QLspSpecification;
-using namespace Qt::StringLiterals;
-/*!
-\internal
-\class QmlLsp::QQmlLanguageServer
-\brief Class that sets up a QmlLanguageServer
-
-This class sets up a QML language server.
-It needs a function
-\code
-std::function<void(const QByteArray &)> sendData
-\endcode
-to send out its replies, and one should feed the data it receives to the server()->receive() method.
-It is expected to call this method only from a single thread, and not to block, the simplest way to
-achieve this is to avoid direct calls, and connect it as slot, while reading from another thread.
-
-The Server is build with separate QLanguageServerModule that implement a given functionality, and
-all of them are constructed and registered with the QLanguageServer in the constructor o this class.
-
-Generally all operations are expected to be done in the object thread, and handlers are always
-called from it, but they are free to delegate the response to another thread, the response handler
-is thread safe. All the methods of the server() obect are also threadsafe.
-
-The code model starts other threads to update its state, see its documentation for more information.
-*/
-QQmlLanguageServer::QQmlLanguageServer(std::function<void(const QByteArray &)> sendData,
- QQmlToolingSettings *settings)
- : m_codeModel(nullptr, settings),
- m_server(sendData),
- m_textSynchronization(&m_codeModel),
- m_lint(&m_server, &m_codeModel),
- m_workspace(&m_codeModel),
- m_completionSupport(&m_codeModel)
-{
- m_server.addServerModule(this);
- m_server.addServerModule(&m_textSynchronization);
- m_server.addServerModule(&m_lint);
- m_server.addServerModule(&m_workspace);
- m_server.addServerModule(&m_completionSupport);
- m_server.finishSetup();
- qCWarning(lspServerLog) << "Did Setup";
-}
-
-void QQmlLanguageServer::registerHandlers(QLanguageServer *server,
- QLanguageServerProtocol *protocol)
-{
- Q_UNUSED(protocol);
- QObject::connect(server, &QLanguageServer::lifecycleError, this,
- &QQmlLanguageServer::errorExit);
- QObject::connect(server, &QLanguageServer::exit, this, &QQmlLanguageServer::exit);
- QObject::connect(server, &QLanguageServer::runStatusChanged, [](QLanguageServer::RunStatus r) {
- qCDebug(lspServerLog) << "runStatus" << int(r);
- });
- protocol->typedRpc()->registerNotificationHandler<Notifications::AddBuildDirsParams>(
- QByteArray(Notifications::AddBuildDirsMethod),
- [this](const QByteArray &, const Notifications::AddBuildDirsParams &params) {
- for (const auto &buildDirs : params.buildDirsToSet) {
- QStringList dirPaths;
- dirPaths.resize(buildDirs.buildDirs.size());
- std::transform(buildDirs.buildDirs.begin(), buildDirs.buildDirs.end(),
- dirPaths.begin(), [](const QByteArray &utf8Str) {
- return QString::fromUtf8(utf8Str);
- });
- m_codeModel.setBuildPathsForRootUrl(buildDirs.baseUri, dirPaths);
- }
- });
-}
-
-void QQmlLanguageServer::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &serverInfo)
-{
- Q_UNUSED(clientInfo);
- QJsonObject expCap;
- if (serverInfo.capabilities.experimental.has_value() && serverInfo.capabilities.experimental->isObject())
- expCap = serverInfo.capabilities.experimental->toObject();
- expCap.insert(u"addBuildDirs"_s, QJsonObject({ { u"supported"_s, true } }));
- serverInfo.capabilities.experimental = expCap;
-}
-
-QString QQmlLanguageServer::name() const
-{
- return u"QQmlLanguageServer"_s;
-}
-
-void QQmlLanguageServer::errorExit()
-{
- qCWarning(lspServerLog) << "Error exit";
- fclose(stdin);
-}
-
-void QQmlLanguageServer::exit()
-{
- m_returnValue = 0;
- fclose(stdin);
-}
-
-int QQmlLanguageServer::returnValue() const
-{
- return m_returnValue;
-}
-
-QQmlCodeModel *QQmlLanguageServer::codeModel()
-{
- return &m_codeModel;
-}
-
-QLanguageServer *QQmlLanguageServer::server()
-{
- return &m_server;
-}
-
-TextSynchronization *QQmlLanguageServer::textSynchronization()
-{
- return &m_textSynchronization;
-}
-
-QmlLintSuggestions *QQmlLanguageServer::lint()
-{
- return &m_lint;
-}
-
-WorkspaceHandlers *QQmlLanguageServer::worspace()
-{
- return &m_workspace;
-}
-
-} // namespace QmlLsp
-
-QT_END_NAMESPACE
diff --git a/tools/qmlls/qqmllanguageserver.h b/tools/qmlls/qqmllanguageserver.h
deleted file mode 100644
index 7efe93990a..0000000000
--- a/tools/qmlls/qqmllanguageserver.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef QQMLLANGUAGESERVER_H
-#define QQMLLANGUAGESERVER_H
-
-#include "qlanguageserver_p.h"
-#include "qqmlcodemodel.h"
-#include "textsynchronization.h"
-#include "qmllintsuggestions.h"
-#include "workspace.h"
-#include "qmlcompletionsupport.h"
-#include "../shared/qqmltoolingsettings.h"
-
-QT_BEGIN_NAMESPACE
-namespace QmlLsp {
-
-/*
- * The language server protocol calls "URI" what QML calls "URL".
- * According to RFC 3986, a URL is a special case of URI that not only
- * identifies a resource but also shows how to access it.
- * In QML, however, URIs are distinct from URLs. URIs are the
- * identifiers of modules, for example "QtQuick.Controls".
- * In order to not confuse the terms we interpret language server URIs
- * as URLs in the QML code model.
- * This method marks a point of translation between the terms, but does
- * not have to change the actual URI/URL.
- */
-inline QByteArray lspUriToQmlUrl(const QByteArray &uri) { return uri; }
-
-class QQmlLanguageServer : public QLanguageServerModule
-{
- Q_OBJECT
-public:
- QQmlLanguageServer(std::function<void(const QByteArray &)> sendData,
- QQmlToolingSettings *settings = nullptr);
-
- QString name() const final;
- void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) final;
- void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &serverInfo) final;
-
- int returnValue() const;
-
- QQmlCodeModel *codeModel();
- QLanguageServer *server();
- TextSynchronization *textSynchronization();
- QmlLintSuggestions *lint();
- WorkspaceHandlers *worspace();
-
-public Q_SLOTS:
- void exit();
- void errorExit();
-
-private:
- QQmlCodeModel m_codeModel;
- QLanguageServer m_server;
- TextSynchronization m_textSynchronization;
- QmlLintSuggestions m_lint;
- WorkspaceHandlers m_workspace;
- QmlCompletionSupport m_completionSupport;
- int m_returnValue = 1;
-};
-
-} // namespace QmlLsp
-QT_END_NAMESPACE
-#endif // QQMLLANGUAGESERVER_H
diff --git a/tools/qmlls/textblock.cpp b/tools/qmlls/textblock.cpp
deleted file mode 100644
index 04a228922e..0000000000
--- a/tools/qmlls/textblock.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "textblock.h"
-#include "textdocument.h"
-
-#include <QtCore/qstring.h>
-
-namespace Utils {
-
-bool TextBlock::isValid() const
-{
- return m_document;
-}
-
-void TextBlock::setBlockNumber(int blockNumber)
-{
- m_blockNumber = blockNumber;
-}
-
-int TextBlock::blockNumber() const
-{
- return m_blockNumber;
-}
-
-void TextBlock::setPosition(int position)
-{
- m_position = position;
-}
-
-int TextBlock::position() const
-{
- return m_position;
-}
-
-void TextBlock::setLength(int length)
-{
- m_length = length;
-}
-
-int TextBlock::length() const
-{
- return m_length;
-}
-
-TextBlock TextBlock::next() const
-{
- return m_document->findBlockByNumber(m_blockNumber + 1);
-}
-
-TextBlock TextBlock::previous() const
-{
- return m_document->findBlockByNumber(m_blockNumber - 1);
-}
-
-int TextBlock::userState() const
-{
- return m_document->userState(m_blockNumber);
-}
-
-void TextBlock::setUserState(int state)
-{
- m_document->setUserState(m_blockNumber, state);
-}
-
-void TextBlock::setDocument(TextDocument *document)
-{
- m_document = document;
-}
-
-TextDocument *TextBlock::document() const
-{
- return m_document;
-}
-
-QString TextBlock::text() const
-{
- return document()->toPlainText().mid(position(), length());
-}
-
-int TextBlock::revision() const
-{
- return m_revision;
-}
-
-void TextBlock::setRevision(int rev)
-{
- m_revision = rev;
-}
-
-bool operator==(const TextBlock &t1, const TextBlock &t2)
-{
- return t1.document() == t2.document() && t1.blockNumber() == t2.blockNumber();
-}
-
-bool operator!=(const TextBlock &t1, const TextBlock &t2)
-{
- return !(t1 == t2);
-}
-
-} // namespace Utils
diff --git a/tools/qmlls/textblock.h b/tools/qmlls/textblock.h
deleted file mode 100644
index 593aa88daf..0000000000
--- a/tools/qmlls/textblock.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef TEXTBLOCK_H
-#define TEXTBLOCK_H
-
-#include <QtCore/qstring.h>
-
-namespace Utils {
-
-class TextDocument;
-class TextBlockUserData;
-
-class TextBlock
-{
-public:
- bool isValid() const;
-
- void setBlockNumber(int blockNumber);
- int blockNumber() const;
-
- void setPosition(int position);
- int position() const;
-
- void setLength(int length);
- int length() const;
-
- TextBlock next() const;
- TextBlock previous() const;
-
- int userState() const;
- void setUserState(int state);
-
- bool isVisible() const;
- void setVisible(bool visible);
-
- void setLineCount(int count);
- int lineCount() const;
-
- void setDocument(TextDocument *document);
- TextDocument *document() const;
-
- QString text() const;
-
- int revision() const;
- void setRevision(int rev);
-
- friend bool operator==(const TextBlock &t1, const TextBlock &t2);
- friend bool operator!=(const TextBlock &t1, const TextBlock &t2);
-
-private:
- TextDocument *m_document = nullptr;
- int m_revision = 0;
-
- int m_position = 0;
- int m_length = 0;
- int m_blockNumber = -1;
-};
-
-} // namespace Utils
-
-#endif // TEXTBLOCK_H
diff --git a/tools/qmlls/textcursor.cpp b/tools/qmlls/textcursor.cpp
deleted file mode 100644
index 3a6e7ca080..0000000000
--- a/tools/qmlls/textcursor.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "textcursor.h"
-#include "textdocument.h"
-#include "textblock.h"
-
-namespace Utils {
-
-class TextFrame;
-class TextTable;
-class TextTableCell;
-
-TextCursor::TextCursor(TextDocument *document) : m_document(document) { }
-
-bool TextCursor::movePosition(TextCursor::MoveOperation op, TextCursor::MoveMode mode, int n)
-{
- Q_UNUSED(n);
- switch (op) {
- case NoMove:
- return true;
- case Start:
- m_position = 0;
- break;
- case PreviousCharacter:
- while (--n >= 0) {
- if (m_position == 0)
- return false;
- --m_position;
- }
- break;
- case End:
- m_position = m_document->characterCount();
- break;
- case NextCharacter:
- while (--n >= 0) {
- if (m_position == m_document->characterCount())
- return false;
- ++m_position;
- }
- break;
- }
-
- if (mode == MoveAnchor)
- m_anchor = m_position;
-
- return false;
-}
-
-int TextCursor::position() const
-{
- return m_position;
-}
-
-void TextCursor::setPosition(int pos, Utils::TextCursor::MoveMode mode)
-{
- m_position = pos;
- if (mode == MoveAnchor)
- m_anchor = pos;
-}
-
-QString TextCursor::selectedText() const
-{
- return m_document->toPlainText().mid(qMin(m_position, m_anchor), qAbs(m_position - m_anchor));
-}
-
-void TextCursor::clearSelection()
-{
- m_anchor = m_position;
-}
-
-TextDocument *TextCursor::document() const
-{
- return m_document;
-}
-
-void TextCursor::insertText(const QString &text)
-{
- const QString orig = m_document->toPlainText();
- const QString left = orig.left(qMin(m_position, m_anchor));
- const QString right = orig.mid(qMax(m_position, m_anchor));
- m_document->setPlainText(left + text + right);
-}
-
-TextBlock TextCursor::block() const
-{
- TextBlock current = m_document->firstBlock();
- while (current.isValid()) {
- if (current.position() <= position()
- && current.position() + current.length() > current.position())
- break;
- current = current.next();
- }
- return current;
-}
-
-int TextCursor::positionInBlock() const
-{
- return m_position - block().position();
-}
-
-int TextCursor::blockNumber() const
-{
- return block().blockNumber();
-}
-
-void TextCursor::removeSelectedText()
-{
- insertText(QString());
-}
-
-int TextCursor::selectionEnd() const
-{
- return qMax(m_position, m_anchor);
-}
-
-bool TextCursor::isNull() const
-{
- return m_document == nullptr;
-}
-
-}
diff --git a/tools/qmlls/textcursor.h b/tools/qmlls/textcursor.h
deleted file mode 100644
index 618cd739c3..0000000000
--- a/tools/qmlls/textcursor.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef TEXTCURSOR_H
-#define TEXTCURSOR_H
-
-#include <QtCore/qstring.h>
-
-namespace Utils {
-
-class TextDocument;
-class TextBlock;
-
-class TextCursor
-{
-public:
- enum MoveOperation {
- NoMove,
- Start,
- PreviousCharacter,
- End,
- NextCharacter,
- };
-
- enum MoveMode { MoveAnchor, KeepAnchor };
-
- enum SelectionType { Document };
-
- TextCursor();
- TextCursor(const TextBlock &block);
- TextCursor(TextDocument *document);
-
- bool movePosition(MoveOperation op, MoveMode = MoveAnchor, int n = 1);
- int position() const;
- void setPosition(int pos, MoveMode mode = MoveAnchor);
- QString selectedText() const;
- void clearSelection();
- int anchor() const;
- TextDocument *document() const;
- void insertText(const QString &text);
- TextBlock block() const;
- int positionInBlock() const;
- int blockNumber() const;
-
- void select(SelectionType selection);
-
- bool hasSelection() const;
-
- void removeSelectedText();
- int selectionEnd() const;
-
- bool isNull() const;
-
-private:
- TextDocument *m_document = nullptr;
- int m_position = 0;
- int m_anchor = 0;
-};
-} // namespace Utils
-
-#endif // TEXTCURSOR_H
diff --git a/tools/qmlls/textdocument.cpp b/tools/qmlls/textdocument.cpp
deleted file mode 100644
index b5d4cc6f68..0000000000
--- a/tools/qmlls/textdocument.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "textdocument.h"
-#include "textblock.h"
-
-namespace Utils {
-
-TextDocument::TextDocument(const QString &text)
-{
- setPlainText(text);
-}
-
-TextBlock TextDocument::findBlockByNumber(int blockNumber) const
-{
- return (blockNumber >= 0 && blockNumber < m_blocks.size())
- ? m_blocks.at(blockNumber).textBlock
- : TextBlock();
-}
-
-TextBlock TextDocument::findBlockByLineNumber(int lineNumber) const
-{
- return findBlockByNumber(lineNumber);
-}
-
-QChar TextDocument::characterAt(int pos) const
-{
- return m_content.at(pos);
-}
-
-int TextDocument::characterCount() const
-{
- return m_content.size();
-}
-
-TextBlock TextDocument::begin() const
-{
- return m_blocks.isEmpty() ? TextBlock() : m_blocks.at(0).textBlock;
-}
-
-TextBlock TextDocument::firstBlock() const
-{
- return begin();
-}
-
-TextBlock TextDocument::lastBlock() const
-{
- return m_blocks.isEmpty() ? TextBlock() : m_blocks.last().textBlock;
-}
-
-std::optional<int> TextDocument::version() const
-{
- return m_version;
-}
-
-void TextDocument::setVersion(std::optional<int> v)
-{
- m_version = v;
-}
-
-QString TextDocument::toPlainText() const
-{
- return m_content;
-}
-
-void TextDocument::setPlainText(const QString &text)
-{
- m_content = text;
- m_blocks.clear();
-
- int blockStart = 0;
- int blockNumber = 0;
- while (blockStart < text.size()) {
- Block block;
- block.textBlock.setBlockNumber(blockNumber++);
- block.textBlock.setPosition(blockStart);
- block.textBlock.setDocument(this);
-
- int blockEnd = text.indexOf('\n', blockStart) + 1;
- if (blockEnd == 0)
- blockEnd = text.size();
-
- block.textBlock.setLength(blockEnd - blockStart);
- m_blocks.append(block);
- blockStart = blockEnd;
- }
-}
-
-bool TextDocument::isModified() const
-{
- return m_modified;
-}
-
-void TextDocument::setModified(bool modified)
-{
- m_modified = modified;
-}
-
-void TextDocument::setUserState(int blockNumber, int state)
-{
- if (blockNumber >= 0 && blockNumber < m_blocks.size())
- m_blocks[blockNumber].userState = state;
-}
-
-int TextDocument::userState(int blockNumber) const
-{
- return (blockNumber >= 0 && blockNumber < m_blocks.size()) ? m_blocks[blockNumber].userState
- : -1;
-}
-
-QMutex *TextDocument::mutex() const
-{
- return &m_mutex;
-}
-
-} // namespace Utils
diff --git a/tools/qmlls/textdocument.h b/tools/qmlls/textdocument.h
deleted file mode 100644
index e0db66bfb3..0000000000
--- a/tools/qmlls/textdocument.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef TEXTDOCUMENT_H
-#define TEXTDOCUMENT_H
-
-#include "textblock.h"
-
-#include <QtCore/qchar.h>
-#include <QtCore/qvector.h>
-#include <QtCore/qscopedpointer.h>
-#include <QtCore/qmutex.h>
-
-#include <optional>
-
-namespace Utils {
-
-class TextBlockUserData;
-
-class TextDocument
-{
-public:
- TextDocument() = default;
- explicit TextDocument(const QString &text);
-
- TextBlock findBlockByNumber(int blockNumber) const;
- TextBlock findBlockByLineNumber(int lineNumber) const;
- QChar characterAt(int pos) const;
- int characterCount() const;
- TextBlock begin() const;
- TextBlock firstBlock() const;
- TextBlock lastBlock() const;
-
- std::optional<int> version() const;
- void setVersion(std::optional<int>);
-
- QString toPlainText() const;
- void setPlainText(const QString &text);
-
- bool isModified() const;
- void setModified(bool modified);
-
- void setUndoRedoEnabled(bool enable);
-
- void clear();
-
- void setUserState(int blockNumber, int state);
- int userState(int blockNumber) const;
- QMutex *mutex() const;
-
-private:
- struct Block
- {
- TextBlock textBlock;
- int userState = -1;
- };
-
- QVector<Block> m_blocks;
-
- QString m_content;
- bool m_modified = false;
- std::optional<int> m_version;
- mutable QMutex m_mutex;
-};
-} // namespace Utils
-
-#endif // TEXTDOCUMENT_H
diff --git a/tools/qmlls/textsynchronization.cpp b/tools/qmlls/textsynchronization.cpp
deleted file mode 100644
index dcef4606f7..0000000000
--- a/tools/qmlls/textsynchronization.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "textsynchronization.h"
-#include "qqmllanguageserver.h"
-
-#include "textdocument.h"
-
-using namespace QLspSpecification;
-using namespace Qt::StringLiterals;
-
-QT_BEGIN_NAMESPACE
-
-TextSynchronization::TextSynchronization(QmlLsp::QQmlCodeModel *codeModel, QObject *parent)
- : QLanguageServerModule(parent), m_codeModel(codeModel)
-{
-}
-
-void TextSynchronization::didCloseTextDocument(const DidCloseTextDocumentParams &params)
-{
- m_codeModel->closeOpenFile(QmlLsp::lspUriToQmlUrl(params.textDocument.uri));
-}
-
-void TextSynchronization::didOpenTextDocument(const DidOpenTextDocumentParams &params)
-{
- const TextDocumentItem &item = params.textDocument;
- const QString fileName = m_codeModel->url2Path(QmlLsp::lspUriToQmlUrl(item.uri));
- m_codeModel->newOpenFile(QmlLsp::lspUriToQmlUrl(item.uri), item.version,
- QString::fromUtf8(item.text));
-}
-
-void TextSynchronization::didDidChangeTextDocument(const DidChangeTextDocumentParams &params)
-{
- QByteArray url = QmlLsp::lspUriToQmlUrl(params.textDocument.uri);
- const QString fileName = m_codeModel->url2Path(url);
- auto openDoc = m_codeModel->openDocumentByUrl(url);
- std::shared_ptr<Utils::TextDocument> document = openDoc.textDocument;
- if (!document) {
- qCWarning(lspServerLog) << "Ingnoring changes to non open or closed document"
- << QString::fromUtf8(url);
- return;
- }
- const auto &changes = params.contentChanges;
- {
- QMutexLocker l(document->mutex());
- for (const auto &change : changes) {
- if (!change.range) {
- document->setPlainText(QString::fromUtf8(change.text));
- continue;
- }
-
- const auto &range = *change.range;
- const auto &rangeStart = range.start;
- const int start =
- document->findBlockByNumber(rangeStart.line).position() + rangeStart.character;
- const auto &rangeEnd = range.end;
- const int end =
- document->findBlockByNumber(rangeEnd.line).position() + rangeEnd.character;
-
- document->setPlainText(document->toPlainText().replace(start, end - start,
- QString::fromUtf8(change.text)));
- }
- document->setVersion(params.textDocument.version);
- qCDebug(lspServerLog).noquote()
- << "text is\n:----------" << document->toPlainText() << "\n_________";
- }
- m_codeModel->addOpenToUpdate(url);
- m_codeModel->openNeedUpdate();
-}
-
-void TextSynchronization::registerHandlers(QLanguageServer *server, QLanguageServerProtocol *)
-{
- QObject::connect(server->notifySignals(),
- &QLspNotifySignals::receivedDidOpenTextDocumentNotification, this,
- &TextSynchronization::didOpenTextDocument);
-
- QObject::connect(server->notifySignals(),
- &QLspNotifySignals::receivedDidChangeTextDocumentNotification, this,
- &TextSynchronization::didDidChangeTextDocument);
-
- QObject::connect(server->notifySignals(),
- &QLspNotifySignals::receivedDidCloseTextDocumentNotification, this,
- &TextSynchronization::didCloseTextDocument);
-}
-
-QString TextSynchronization::name() const
-{
- return u"TextSynchonization"_s;
-}
-
-void TextSynchronization::setupCapabilities(const QLspSpecification::InitializeParams &,
- QLspSpecification::InitializeResult &serverInfo)
-{
- TextDocumentSyncOptions syncOptions;
- syncOptions.openClose = true;
- syncOptions.change = TextDocumentSyncKind::Incremental;
- serverInfo.capabilities.textDocumentSync = syncOptions;
-}
-
-QT_END_NAMESPACE
diff --git a/tools/qmlls/textsynchronization.h b/tools/qmlls/textsynchronization.h
deleted file mode 100644
index 69076aa81b..0000000000
--- a/tools/qmlls/textsynchronization.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#ifndef TEXTSYNCH_H
-#define TEXTSYNCH_H
-
-#include "qqmlcodemodel.h"
-
-#include "qlanguageserver_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class TextSynchronization : public QLanguageServerModule
-{
- Q_OBJECT
-public:
- TextSynchronization(QmlLsp::QQmlCodeModel *codeModel, QObject *parent = nullptr);
- QString name() const override;
- void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
- void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &) override;
-
-public Q_SLOTS:
- void didOpenTextDocument(const QLspSpecification::DidOpenTextDocumentParams &params);
- void didDidChangeTextDocument(const QLspSpecification::DidChangeTextDocumentParams &params);
- void didCloseTextDocument(const QLspSpecification::DidCloseTextDocumentParams &params);
-
-private:
- QmlLsp::QQmlCodeModel *m_codeModel;
-};
-
-QT_END_NAMESPACE
-#endif // TEXTSYNCH_H
diff --git a/tools/qmlls/workspace.cpp b/tools/qmlls/workspace.cpp
deleted file mode 100644
index a18d5f6f8d..0000000000
--- a/tools/qmlls/workspace.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "workspace.h"
-#include "qqmllanguageserver.h"
-#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
-#include <QtLanguageServer/private/qlspnotifysignals_p.h>
-
-#include <QtCore/qfile.h>
-#include <variant>
-
-QT_BEGIN_NAMESPACE
-using namespace Qt::StringLiterals;
-using namespace QLspSpecification;
-
-void WorkspaceHandlers::registerHandlers(QLanguageServer *server, QLanguageServerProtocol *)
-{
- QObject::connect(server->notifySignals(),
- &QLspNotifySignals::receivedDidChangeWorkspaceFoldersNotification, this,
- [server, this](const DidChangeWorkspaceFoldersParams &params) {
- const WorkspaceFoldersChangeEvent &event = params.event;
-
- const QList<WorkspaceFolder> &removed = event.removed;
- QList<QByteArray> toRemove;
- for (const WorkspaceFolder &folder : removed) {
- toRemove.append(QmlLsp::lspUriToQmlUrl(folder.uri));
- m_codeModel->removeDirectory(
- m_codeModel->url2Path(QmlLsp::lspUriToQmlUrl(folder.uri)));
- }
- m_codeModel->removeRootUrls(toRemove);
- const QList<WorkspaceFolder> &added = event.added;
- QList<QByteArray> toAdd;
- QStringList pathsToAdd;
- for (const WorkspaceFolder &folder : added) {
- toAdd.append(QmlLsp::lspUriToQmlUrl(folder.uri));
- pathsToAdd.append(
- m_codeModel->url2Path(QmlLsp::lspUriToQmlUrl(folder.uri)));
- }
- m_codeModel->addRootUrls(toAdd);
- m_codeModel->addDirectoriesToIndex(pathsToAdd, server);
- });
-
- QObject::connect(server->notifySignals(),
- &QLspNotifySignals::receivedDidChangeWatchedFilesNotification, this,
- [this](const DidChangeWatchedFilesParams &params) {
- const QList<FileEvent> &changes = params.changes;
- for (const FileEvent &change : changes) {
- const QString filename =
- m_codeModel->url2Path(QmlLsp::lspUriToQmlUrl(change.uri));
- switch (FileChangeType(change.type)) {
- case FileChangeType::Created:
- // m_codeModel->addFile(filename);
- break;
- case FileChangeType::Changed: {
- QFile file(filename);
- if (file.open(QIODevice::ReadOnly))
- // m_modelManager->setFileContents(filename, file.readAll());
- break;
- }
- case FileChangeType::Deleted:
- // m_modelManager->removeFile(filename);
- break;
- }
- }
- // update due to dep changes...
- });
-
- QObject::connect(server, &QLanguageServer::clientInitialized, this,
- &WorkspaceHandlers::clientInitialized);
-}
-
-QString WorkspaceHandlers::name() const
-{
- return u"Workspace"_s;
-}
-
-void WorkspaceHandlers::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &serverInfo)
-{
- if (!clientInfo.capabilities.workspace
- || !clientInfo.capabilities.workspace->value("workspaceFolders").toBool(false))
- return;
- WorkspaceFoldersServerCapabilities folders;
- folders.supported = true;
- folders.changeNotifications = true;
- if (!serverInfo.capabilities.workspace)
- serverInfo.capabilities.workspace = QJsonObject();
- serverInfo.capabilities.workspace->insert("workspaceFolders", QTypedJson::toJsonValue(folders));
-}
-
-void WorkspaceHandlers::clientInitialized(QLanguageServer *server)
-{
- QLanguageServerProtocol *protocol = server->protocol();
- const auto clientInfo = server->clientInfo();
- QList<Registration> registrations;
- if (clientInfo.capabilities.workspace
- && clientInfo.capabilities.workspace->value("didChangeWatchedFiles")["dynamicRegistration"]
- .toBool(false)) {
- const int watchAll =
- int(WatchKind::Create) | int(WatchKind::Change) | int(WatchKind::Delete);
- DidChangeWatchedFilesRegistrationOptions watchedFilesParams;
- FileSystemWatcher qmlWatcher;
- qmlWatcher.globPattern = QByteArray("*.{qml,js,mjs}");
- qmlWatcher.kind = watchAll;
- FileSystemWatcher qmldirWatcher;
- qmldirWatcher.globPattern = "qmldir";
- qmldirWatcher.kind = watchAll;
- FileSystemWatcher qmltypesWatcher;
- qmltypesWatcher.globPattern = QByteArray("*.qmltypes");
- qmltypesWatcher.kind = watchAll;
- watchedFilesParams.watchers =
- QList<FileSystemWatcher>({ qmlWatcher, qmldirWatcher, qmltypesWatcher });
- registrations.append(Registration {
- // use ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles as id too
- ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles,
- ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles,
- QTypedJson::toJsonValue(watchedFilesParams) });
- }
-
- if (!registrations.isEmpty()) {
- RegistrationParams params;
- params.registrations = registrations;
- protocol->requestRegistration(
- params,
- []() {
- // successful registration
- },
- [protocol](const ResponseError &err) {
- LogMessageParams msg;
- msg.message = QByteArray("registration of file udates failed, will miss file "
- "changes done outside the editor due to error ");
- msg.message.append(QString::number(err.code).toUtf8());
- if (!err.message.isEmpty())
- msg.message.append(" ");
- msg.message.append(err.message);
- msg.type = MessageType::Warning;
- qCWarning(lspServerLog) << QString::fromUtf8(msg.message);
- protocol->notifyLogMessage(msg);
- });
- }
-
- QSet<QString> rootPaths;
- if (std::holds_alternative<QByteArray>(clientInfo.rootUri)) {
- QString path = m_codeModel->url2Path(
- QmlLsp::lspUriToQmlUrl(std::get<QByteArray>(clientInfo.rootUri)));
- rootPaths.insert(path);
- } else if (clientInfo.rootPath && std::holds_alternative<QByteArray>(*clientInfo.rootPath)) {
- QString path = QString::fromUtf8(std::get<QByteArray>(*clientInfo.rootPath));
- rootPaths.insert(path);
- }
-
- if (clientInfo.workspaceFolders
- && std::holds_alternative<QList<WorkspaceFolder>>(*clientInfo.workspaceFolders)) {
- for (const WorkspaceFolder &workspace :
- std::as_const(std::get<QList<WorkspaceFolder>>(*clientInfo.workspaceFolders))) {
- const QUrl workspaceUrl(QString::fromUtf8(QmlLsp::lspUriToQmlUrl(workspace.uri)));
- rootPaths.insert(workspaceUrl.toLocalFile());
- }
- }
- if (m_status == Status::Indexing)
- m_codeModel->addDirectoriesToIndex(QStringList(rootPaths.begin(), rootPaths.end()), server);
-}
-
-QT_END_NAMESPACE
diff --git a/tools/qmlls/workspace.h b/tools/qmlls/workspace.h
deleted file mode 100644
index 03dcf291d1..0000000000
--- a/tools/qmlls/workspace.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef WORKSPACE_H
-#define WORKSPACE_H
-
-#include "qqmlcodemodel.h"
-#include "qlanguageserver.h"
-
-QT_BEGIN_NAMESPACE
-
-class WorkspaceHandlers : public QLanguageServerModule
-{
- Q_OBJECT
-public:
- enum class Status { NoIndex, Indexing };
- WorkspaceHandlers(QmlLsp::QQmlCodeModel *codeModel) : m_codeModel(codeModel) { }
- QString name() const override;
- void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override;
- void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
- QLspSpecification::InitializeResult &) override;
-public Q_SLOTS:
- void clientInitialized(QLanguageServer *);
-
-private:
- QmlLsp::QQmlCodeModel *m_codeModel = nullptr;
- Status m_status = Status::NoIndex;
-};
-
-QT_END_NAMESPACE
-
-#endif // WORKSPACE_H
diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp
index eeb8c6cde3..ead1e729b9 100644
--- a/tools/qmlplugindump/main.cpp
+++ b/tools/qmlplugindump/main.cpp
@@ -30,6 +30,7 @@
#include <QtCore/private/qobject_p.h>
#include <QtCore/private/qmetaobject_p.h>
#include <QtQmlTypeRegistrar/private/qqmljsstreamwriter_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <QRegularExpression>
#include <iostream>
@@ -68,13 +69,6 @@ QString inObjectInstantiation;
}
-static QString enquote(const QString &string)
-{
- QString s = string;
- return QString("\"%1\"").arg(s.replace(QLatin1Char('\\'), QLatin1String("\\\\"))
- .replace(QLatin1Char('"'),QLatin1String("\\\"")));
-}
-
struct QmlVersionInfo
{
QString pluginImportUri;
@@ -268,7 +262,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
QObject *object = nullptr;
if (ty.isSingleton()) {
- QQmlType::SingletonInstanceInfo *siinfo = ty.singletonInstanceInfo();
+ QQmlType::SingletonInstanceInfo::ConstPtr siinfo = ty.singletonInstanceInfo();
if (!siinfo) {
std::cerr << "Internal error, " << qPrintable(tyName)
<< "(" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")"
@@ -278,7 +272,8 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
if (ty.isQObjectSingleton()) {
if (verbose)
std::cerr << "Trying to get singleton for " << qPrintable(tyName)
- << " (" << qPrintable( siinfo->typeName ) << ")" << std::endl;
+ << " (" << qPrintable( QString::fromUtf8(siinfo->typeName) )
+ << ")" << std::endl;
collectReachableMetaObjects(object, &metas, info);
object = QQmlEnginePrivate::get(engine)->singletonInstance<QObject*>(ty);
} else {
@@ -348,7 +343,7 @@ public:
relocatableModuleUri = uri;
}
- QString getExportString(const QQmlType &type, const QmlVersionInfo &versionInfo)
+ QByteArray getExportString(const QQmlType &type, const QmlVersionInfo &versionInfo)
{
const QString module = type.module().isEmpty() ? versionInfo.pluginImportUri
: type.module();
@@ -358,12 +353,14 @@ public:
type.version().hasMinorVersion() ? type.version().minorVersion()
: versionInfo.version.minorVersion());
- const QString versionedElement = type.elementName()
- + QString::fromLatin1(" %1.%2").arg(version.majorVersion()).arg(version.minorVersion());
+ const QByteArray versionedElement
+ = (type.elementName() + QString::fromLatin1(" %1.%2")
+ .arg(version.majorVersion())
+ .arg(version.minorVersion())).toUtf8();
- return enquote((module == relocatableModuleUri)
+ return (module == relocatableModuleUri)
? versionedElement
- : module + QLatin1Char('/') + versionedElement);
+ : module.toUtf8() + '/' + versionedElement;
}
void writeMetaContent(const QMetaObject *meta, KnownAttributes *knownAttributes = nullptr)
@@ -375,30 +372,31 @@ public:
for (int index = meta->methodOffset(); index < meta->methodCount(); ++index) {
QMetaMethod method = meta->method(index);
QByteArray signature = method.methodSignature();
- if (signature == QByteArrayLiteral("destroyed(QObject*)")
- || signature == QByteArrayLiteral("destroyed()")
- || signature == QByteArrayLiteral("deleteLater()"))
+ if (signature == "destroyed(QObject*)"
+ || signature == "destroyed()"
+ || signature == "deleteLater()") {
continue;
+ }
dump(method, implicitSignals, knownAttributes);
}
// and add toString(), destroy() and destroy(int)
- if (!knownAttributes || !knownAttributes->knownMethod(QByteArray("toString"), 0, QTypeRevision::zero())) {
- qml->writeStartObject(QLatin1String("Method"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("toString")));
+ if (!knownAttributes || !knownAttributes->knownMethod("toString", 0, QTypeRevision::zero())) {
+ qml->writeStartObject("Method");
+ qml->writeStringBinding("name", QLatin1String("toString"));
qml->writeEndObject();
}
- if (!knownAttributes || !knownAttributes->knownMethod(QByteArray("destroy"), 0, QTypeRevision::zero())) {
- qml->writeStartObject(QLatin1String("Method"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy")));
+ if (!knownAttributes || !knownAttributes->knownMethod("destroy", 0, QTypeRevision::zero())) {
+ qml->writeStartObject("Method");
+ qml->writeStringBinding("name", QLatin1String("destroy"));
qml->writeEndObject();
}
- if (!knownAttributes || !knownAttributes->knownMethod(QByteArray("destroy"), 1, QTypeRevision::zero())) {
- qml->writeStartObject(QLatin1String("Method"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy")));
- qml->writeStartObject(QLatin1String("Parameter"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("delay")));
- qml->writeScriptBinding(QLatin1String("type"), enquote(QLatin1String("int")));
+ if (!knownAttributes || !knownAttributes->knownMethod("destroy", 1, QTypeRevision::zero())) {
+ qml->writeStartObject("Method");
+ qml->writeStringBinding("name", QLatin1String("destroy"));
+ qml->writeStartObject("Parameter");
+ qml->writeStringBinding("name", QLatin1String("delay"));
+ qml->writeStringBinding("type", QLatin1String("int"));
qml->writeEndObject();
qml->writeEndObject();
}
@@ -408,12 +406,12 @@ public:
}
}
- QString getPrototypeNameForCompositeType(
+ QByteArray getPrototypeNameForCompositeType(
const QMetaObject *metaObject, QList<const QMetaObject *> *objectsToMerge,
const QmlVersionInfo &versionInfo)
{
auto ty = QQmlMetaType::qmlType(metaObject);
- QString prototypeName;
+ QByteArray prototypeName;
if (matchingImportUri(ty, versionInfo)) {
// dynamic meta objects can break things badly
// but extended types are usually fine
@@ -464,35 +462,36 @@ public:
QList<const QMetaObject *> objectsToMerge;
KnownAttributes knownAttributes;
// Get C++ base class name for the composite type
- QString prototypeName = getPrototypeNameForCompositeType(mainMeta, &objectsToMerge,
- versionInfo);
- qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName));
+ QByteArray prototypeName = getPrototypeNameForCompositeType(
+ mainMeta, &objectsToMerge, versionInfo);
+ qml->writeStringBinding("prototype", QUtf8StringView(prototypeName));
- QString qmlTyName = compositeType.qmlTypeName();
- const QString exportString = getExportString(compositeType, versionInfo);
+ const QByteArray exportString = getExportString(compositeType, versionInfo);
// TODO: why don't we simply output the compositeType.elementName() here?
// That would make more sense, but it would change the format quite a bit.
- qml->writeScriptBinding(QLatin1String("name"), exportString);
+ qml->writeStringBinding("name", QUtf8StringView(exportString));
- qml->writeArrayBinding(QLatin1String("exports"), QStringList() << exportString);
+ qml->writeStringListBinding(
+ "exports", QList<QAnyStringView> { QUtf8StringView(exportString) });
// TODO: shouldn't this be metaObjectRevision().value<quint16>()
// rather than version().minorVersion()
- qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), QStringList()
- << QString::number(compositeType.version().minorVersion()));
+ qml->writeArrayBinding(
+ "exportMetaObjectRevisions",
+ QByteArrayList() << QByteArray::number(compositeType.version().minorVersion()));
- qml->writeBooleanBinding(QLatin1String("isComposite"), true);
+ qml->writeBooleanBinding("isComposite", true);
if (compositeType.isSingleton()) {
- qml->writeBooleanBinding(QLatin1String("isCreatable"), false);
- qml->writeBooleanBinding(QLatin1String("isSingleton"), true);
+ qml->writeBooleanBinding("isCreatable", false);
+ qml->writeBooleanBinding("isSingleton", true);
}
for (int index = mainMeta->classInfoCount() - 1 ; index >= 0 ; --index) {
QMetaClassInfo classInfo = mainMeta->classInfo(index);
- if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
- qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value())));
+ if (QUtf8StringView(classInfo.name()) == QUtf8StringView("DefaultProperty")) {
+ qml->writeStringBinding("defaultProperty", QUtf8StringView(classInfo.value()));
break;
}
}
@@ -507,22 +506,29 @@ public:
qml->writeEndObject();
}
- QString getDefaultProperty(const QMetaObject *meta)
+ QByteArray getDefaultProperty(const QMetaObject *meta)
{
for (int index = meta->classInfoCount() - 1; index >= 0; --index) {
QMetaClassInfo classInfo = meta->classInfo(index);
if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
- return QLatin1String(classInfo.value());
+ return QByteArray(classInfo.value());
}
}
- return QString();
+ return QByteArray();
}
struct QmlTypeInfo {
QmlTypeInfo() {}
- QmlTypeInfo(const QString &exportString, QTypeRevision revision, const QMetaObject *extendedObject, QByteArray attachedTypeId)
- : exportString(exportString), revision(revision), extendedObject(extendedObject), attachedTypeId(attachedTypeId) {}
- QString exportString;
+ QmlTypeInfo(
+ const QByteArray &exportString, QTypeRevision revision,
+ const QMetaObject *extendedObject, QByteArray attachedTypeId)
+ : exportString(exportString)
+ , revision(revision)
+ , extendedObject(extendedObject)
+ , attachedTypeId(attachedTypeId)
+ {}
+
+ QByteArray exportString;
QTypeRevision revision = QTypeRevision::zero();
const QMetaObject *extendedObject = nullptr;
QByteArray attachedTypeId;
@@ -533,11 +539,12 @@ public:
qml->writeStartObject("Component");
QByteArray id = convertToId(meta);
- qml->writeScriptBinding(QLatin1String("name"), enquote(id));
+ qml->writeStringBinding("name", QUtf8StringView(id));
// collect type information
QVector<QmlTypeInfo> typeInfo;
- for (QQmlType type : qmlTypesByCppName.value(meta->className())) {
+ const auto types = qmlTypesByCppName.value(meta->className());
+ for (const QQmlType &type : types) {
const QMetaObject *extendedObject = type.extensionFunction() ? type.metaObject() : nullptr;
QByteArray attachedTypeId;
if (const QMetaObject *attachedType = type.attachedPropertiesType(engine)) {
@@ -546,7 +553,8 @@ public:
if (attachedType != meta)
attachedTypeId = convertToId(attachedType);
}
- const QString exportString = getExportString(type, { QString(), QTypeRevision(), false });
+ const QByteArray exportString = getExportString(
+ type, { QString(), QTypeRevision(), false });
QTypeRevision metaObjectRevision = type.metaObjectRevision();
if (extendedObject) {
// emulate custom metaobjectrevision out of import
@@ -564,7 +572,7 @@ public:
// determine default property
// TODO: support revisioning of default property
- QString defaultProperty = getDefaultProperty(meta);
+ QByteArray defaultProperty = getDefaultProperty(meta);
if (defaultProperty.isEmpty()) {
for (const QmlTypeInfo &iter : typeInfo) {
if (iter.extendedObject) {
@@ -575,31 +583,33 @@ public:
}
}
if (!defaultProperty.isEmpty())
- qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(defaultProperty));
+ qml->writeStringBinding("defaultProperty", defaultProperty);
if (meta->superClass())
- qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
+ qml->writeStringBinding("prototype", convertToId(meta->superClass()));
if (!typeInfo.isEmpty()) {
- QMap<QString, QString> exports; // sort exports
- for (const QmlTypeInfo &iter : typeInfo)
- exports.insert(iter.exportString, QString::number(iter.revision.toEncodedVersion<quint16>()));
+ QMap<QAnyStringView, QByteArray> exports; // sort exports
+ for (const QmlTypeInfo &iter : typeInfo) {
+ exports.insert(
+ QUtf8StringView(iter.exportString),
+ QByteArray::number(iter.revision.toEncodedVersion<quint16>()));
+ }
- QStringList exportStrings = exports.keys();
- QStringList metaObjectRevisions = exports.values();
- qml->writeArrayBinding(QLatin1String("exports"), exportStrings);
+ QByteArrayList metaObjectRevisions = exports.values();
+ qml->writeStringListBinding("exports", exports.keys());
if (isUncreatable)
- qml->writeBooleanBinding(QLatin1String("isCreatable"), false);
+ qml->writeBooleanBinding("isCreatable", false);
if (isSingleton)
- qml->writeBooleanBinding(QLatin1String("isSingleton"), true);
+ qml->writeBooleanBinding("isSingleton", true);
- qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions);
+ qml->writeArrayBinding("exportMetaObjectRevisions", metaObjectRevisions);
for (const QmlTypeInfo &iter : typeInfo) {
if (!iter.attachedTypeId.isEmpty()) {
- qml->writeScriptBinding(QLatin1String("attachedType"), enquote(iter.attachedTypeId));
+ qml->writeStringBinding("attachedType", iter.attachedTypeId);
break;
}
}
@@ -611,7 +621,7 @@ public:
writeMetaContent(meta);
// dump properties from extended metaobjects last
- for (auto iter : typeInfo) {
+ for (const auto &iter : typeInfo) {
if (iter.extendedObject)
dumpMetaProperties(iter.extendedObject, iter.revision);
}
@@ -647,13 +657,13 @@ private:
bool isList = false, isPointer = false;
removePointerAndList(&typeName, &isList, &isPointer);
- qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
+ qml->writeStringBinding("type", QUtf8StringView(typeName));
if (isList)
- qml->writeScriptBinding(QLatin1String("isList"), QLatin1String("true"));
+ qml->writeBooleanBinding("isList", true);
if (!isWritable)
- qml->writeScriptBinding(QLatin1String("isReadonly"), QLatin1String("true"));
+ qml->writeBooleanBinding("isReadonly", true);
if (isPointer)
- qml->writeScriptBinding(QLatin1String("isPointer"), QLatin1String("true"));
+ qml->writeBooleanBinding("isPointer", true);
}
void dump(const QMetaProperty &prop, QTypeRevision metaRevision = QTypeRevision(),
@@ -667,9 +677,9 @@ private:
if (knownAttributes && knownAttributes->knownProperty(propName, revision))
return;
qml->writeStartObject("Property");
- qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(prop.name())));
+ qml->writeStringBinding("name", QUtf8StringView(prop.name()));
if (revision != QTypeRevision::zero())
- qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision.toEncodedVersion<quint16>()));
+ qml->writeNumberBinding("revision", revision.toEncodedVersion<quint16>());
writeTypeProperties(prop.typeName(), prop.isWritable());
qml->writeEndObject();
@@ -682,10 +692,13 @@ private:
for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) {
const QMetaProperty &property = meta->property(index);
dump(property, metaRevision, knownAttributes);
+ const QByteArray changedSignalName =
+ QQmlSignalNames::propertyNameToChangedSignalName(property.name());
if (knownAttributes)
- knownAttributes->knownMethod(QByteArray(property.name()).append("Changed"),
- 0, QTypeRevision::fromEncodedVersion(property.revision()));
- implicitSignals.insert(QString("%1Changed").arg(QString::fromUtf8(property.name())));
+ knownAttributes->knownMethod(
+ changedSignalName, 0,
+ QTypeRevision::fromEncodedVersion(property.revision()));
+ implicitSignals.insert(changedSignalName);
}
return implicitSignals;
}
@@ -701,13 +714,13 @@ private:
}
QByteArray name = meth.name();
- const QString typeName = convertToId(meth.typeName());
+ const QByteArray typeName = convertToId(meth.typeName());
if (implicitSignals.contains(name)
&& !meth.revision()
&& meth.methodType() == QMetaMethod::Signal
&& meth.parameterNames().isEmpty()
- && typeName == QLatin1String("void")) {
+ && typeName == "void") {
// don't mention implicit signals
return;
}
@@ -716,24 +729,24 @@ private:
if (knownAttributes && knownAttributes->knownMethod(name, meth.parameterNames().size(), revision))
return;
if (meth.methodType() == QMetaMethod::Signal)
- qml->writeStartObject(QLatin1String("Signal"));
+ qml->writeStartObject("Signal");
else
- qml->writeStartObject(QLatin1String("Method"));
+ qml->writeStartObject("Method");
- qml->writeScriptBinding(QLatin1String("name"), enquote(name));
+ qml->writeStringBinding("name", QUtf8StringView(name));
if (revision != QTypeRevision::zero())
- qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision.toEncodedVersion<quint16>()));
+ qml->writeNumberBinding("revision", revision.toEncodedVersion<quint16>());
- if (typeName != QLatin1String("void"))
- qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
+ if (typeName != "void")
+ qml->writeStringBinding("type", QUtf8StringView(typeName));
for (int i = 0; i < meth.parameterTypes().size(); ++i) {
QByteArray argName = meth.parameterNames().at(i);
- qml->writeStartObject(QLatin1String("Parameter"));
- if (! argName.isEmpty())
- qml->writeScriptBinding(QLatin1String("name"), enquote(argName));
+ qml->writeStartObject("Parameter");
+ if (!argName.isEmpty())
+ qml->writeStringBinding("name", QUtf8StringView(argName));
writeTypeProperties(meth.parameterTypes().at(i), true);
qml->writeEndObject();
}
@@ -743,17 +756,16 @@ private:
void dump(const QMetaEnum &e)
{
- qml->writeStartObject(QLatin1String("Enum"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(e.name())));
+ qml->writeStartObject("Enum");
+ qml->writeStringBinding("name", QUtf8StringView(e.name()));
- QList<QPair<QString, QString> > namesValues;
+ QList<QPair<QAnyStringView, int>> namesValues;
const int keyCount = e.keyCount();
namesValues.reserve(keyCount);
- for (int index = 0; index < keyCount; ++index) {
- namesValues.append(qMakePair(enquote(QString::fromUtf8(e.key(index))), QString::number(e.value(index))));
- }
+ for (int index = 0; index < keyCount; ++index)
+ namesValues.append(qMakePair(QUtf8StringView(e.key(index)), e.value(index)));
- qml->writeScriptObjectLiteralBinding(QLatin1String("values"), namesValues);
+ qml->writeEnumObjectLiteralBinding("values", namesValues);
qml->writeEndObject();
}
};
@@ -1167,17 +1179,17 @@ int main(int argc, char *argv[])
// Merge file.
QStringList mergeDependencies;
- QString mergeComponents;
+ QByteArray mergeComponents;
if (!mergeFile.isEmpty()) {
const QStringList merge = readQmlTypes(mergeFile);
if (!merge.isEmpty()) {
- QRegularExpression re("(\\w+\\.*\\w*\\s*\\d+\\.\\d+)");
+ static const QRegularExpression re("(\\w+\\.*\\w*\\s*\\d+\\.\\d+)");
QRegularExpressionMatchIterator i = re.globalMatch(merge[1]);
while (i.hasNext()) {
QRegularExpressionMatch m = i.next();
mergeDependencies << m.captured(1);
}
- mergeComponents = merge [2];
+ mergeComponents = merge[2].toUtf8();
}
}
@@ -1317,16 +1329,20 @@ int main(int argc, char *argv[])
QQmlJSStreamWriter qml(&bytes);
qml.writeStartDocument();
- qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 2);
- qml.write(QString("\n"
+ qml.writeLibraryImport("QtQuick.tooling", 1, 2);
+ qml.write("\n"
"// This file describes the plugin-supplied types contained in the library.\n"
"// It is used for QML tooling purposes only.\n"
"//\n"
"// This file was auto-generated by:\n"
- "// '%1 %2'\n"
+ "// '");
+ qml.write(QFileInfo(args.at(0)).baseName().toUtf8());
+ qml.write(" ");
+ qml.write(args.mid(1).join(QLatin1Char(' ')).toUtf8());
+ qml.write("'\n"
"//\n"
"// qmlplugindump is deprecated! You should use qmltyperegistrar instead.\n"
- "\n").arg(QFileInfo(args.at(0)).baseName(), args.mid(1).join(QLatin1Char(' '))));
+ "\n");
qml.writeStartObject("Module");
// put the metaobjects into a map so they are always dumped in the same order
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
index 8f75226fd8..c9e917cc00 100644
--- a/tools/qmlprofiler/qmlprofilerdata.cpp
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -3,14 +3,12 @@
#include "qmlprofilerdata.h"
-#include <QStringList>
-#include <QUrl>
-#include <QHash>
-#include <QFile>
-#include <QXmlStreamReader>
-#include <QRegularExpression>
-#include <QQueue>
-#include <QStack>
+#include <QtCore/qfile.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qxmlstream.h>
+#include <QtCore/qxpfunctional.h>
#include <limits>
@@ -375,6 +373,132 @@ private:
QXmlStreamWriter stream;
};
+struct DataIterator
+{
+ DataIterator(
+ const QmlProfilerDataPrivate *d,
+ qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> &&sendEvent)
+ : d(d)
+ , sendEvent(std::move(sendEvent))
+ {}
+
+ void run();
+
+private:
+ void handleRangeEvent(const QQmlProfilerEvent &event, const QQmlProfilerEventType &type);
+ void sendPending();
+ void endLevel0();
+
+ const QmlProfilerDataPrivate *d = nullptr;
+ const qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> sendEvent;
+
+ QQueue<QQmlProfilerEvent> pointEvents;
+ QList<QQmlProfilerEvent> rangeStarts[MaximumRangeType];
+ QList<qint64> rangeEnds[MaximumRangeType];
+
+ int level = 0;
+};
+
+void DataIterator::handleRangeEvent(
+ const QQmlProfilerEvent &event, const QQmlProfilerEventType &type)
+{
+ QList<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
+ switch (event.rangeStage()) {
+ case RangeStart: {
+ ++level;
+ starts.append(event);
+ break;
+ }
+ case RangeEnd: {
+ const qint64 invalidTimestamp = -1;
+ QList<qint64> &ends = rangeEnds[type.rangeType()];
+
+ // -1 because all valid timestamps are >= 0.
+ ends.resize(starts.size(), invalidTimestamp);
+
+ qsizetype i = starts.size();
+ while (ends[--i] != invalidTimestamp) {}
+
+ Q_ASSERT(i >= 0);
+ Q_ASSERT(starts[i].timestamp() <= event.timestamp());
+
+ ends[i] = event.timestamp();
+ if (--level == 0)
+ endLevel0();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void DataIterator::sendPending()
+{
+ // Send all pending events in the order of their start times.
+
+ qsizetype index[MaximumRangeType] = { 0, 0, 0, 0, 0, 0 };
+ while (true) {
+
+ // Find the range type with the minimum start time.
+ qsizetype minimum = MaximumRangeType;
+ qint64 minimumTime = std::numeric_limits<qint64>::max();
+ for (qsizetype i = 0; i < MaximumRangeType; ++i) {
+ const QList<QQmlProfilerEvent> &starts = rangeStarts[i];
+ if (starts.size() == index[i])
+ continue;
+ const qint64 timestamp = starts[index[i]].timestamp();
+ if (timestamp < minimumTime) {
+ minimumTime = timestamp;
+ minimum = i;
+ }
+ }
+ if (minimum == MaximumRangeType)
+ break;
+
+ // Send all point events that happened before the range we've found.
+ while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
+ sendEvent(pointEvents.dequeue(), 0);
+
+ // Send the range itself
+ sendEvent(rangeStarts[minimum][index[minimum]],
+ rangeEnds[minimum][index[minimum]] - minimumTime);
+
+ // Bump the index so that we don't send the same range again
+ ++index[minimum];
+ }
+}
+
+void DataIterator::endLevel0()
+{
+ sendPending();
+ for (qsizetype i = 0; i < MaximumRangeType; ++i) {
+ rangeStarts[i].clear();
+ rangeEnds[i].clear();
+ }
+}
+
+void DataIterator::run()
+{
+ for (const QQmlProfilerEvent &event : std::as_const(d->events)) {
+ const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
+ if (type.rangeType() != MaximumRangeType)
+ handleRangeEvent(event, type);
+ else if (level == 0)
+ sendEvent(event, 0);
+ else
+ pointEvents.enqueue(event);
+ }
+
+ for (qsizetype i = 0; i < MaximumRangeType; ++i) {
+ while (rangeEnds[i].size() < rangeStarts[i].size()) {
+ rangeEnds[i].append(d->traceEndTime);
+ --level;
+ }
+ }
+
+ sendPending();
+}
+
bool QmlProfilerData::save(const QString &filename)
{
if (isEmpty()) {
@@ -442,6 +566,7 @@ bool QmlProfilerData::save(const QString &filename)
stream.writeStartElement("profilerDataModel");
auto sendEvent = [&](const QQmlProfilerEvent &event, qint64 duration = 0) {
+ Q_ASSERT(duration >= 0);
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
stream.writeStartElement("range");
stream.writeAttribute("startTime", event.timestamp());
@@ -481,74 +606,7 @@ bool QmlProfilerData::save(const QString &filename)
stream.writeEndElement();
};
- QQueue<QQmlProfilerEvent> pointEvents;
- QQueue<QQmlProfilerEvent> rangeStarts[MaximumRangeType];
- QStack<qint64> rangeEnds[MaximumRangeType];
- int level = 0;
-
- auto sendPending = [&]() {
- forever {
- int minimum = MaximumRangeType;
- qint64 minimumTime = std::numeric_limits<qint64>::max();
- for (int i = 0; i < MaximumRangeType; ++i) {
- const QQueue<QQmlProfilerEvent> &starts = rangeStarts[i];
- if (starts.isEmpty())
- continue;
- if (starts.head().timestamp() < minimumTime) {
- minimumTime = starts.head().timestamp();
- minimum = i;
- }
- }
- if (minimum == MaximumRangeType)
- break;
-
- while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
- sendEvent(pointEvents.dequeue());
-
- sendEvent(rangeStarts[minimum].dequeue(),
- rangeEnds[minimum].pop() - minimumTime);
- }
- };
-
- for (const QQmlProfilerEvent &event : std::as_const(d->events)) {
- const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
-
- if (type.rangeType() != MaximumRangeType) {
- QQueue<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
- switch (event.rangeStage()) {
- case RangeStart: {
- ++level;
- starts.enqueue(event);
- break;
- }
- case RangeEnd: {
- QStack<qint64> &ends = rangeEnds[type.rangeType()];
- if (starts.size() > ends.size()) {
- ends.push(event.timestamp());
- if (--level == 0)
- sendPending();
- }
- break;
- }
- default:
- break;
- }
- } else {
- if (level == 0)
- sendEvent(event);
- else
- pointEvents.enqueue(event);
- }
- }
-
- for (int i = 0; i < MaximumRangeType; ++i) {
- while (rangeEnds[i].size() < rangeStarts[i].size()) {
- rangeEnds[i].push(d->traceEndTime);
- --level;
- }
- }
-
- sendPending();
+ DataIterator(d, std::move(sendEvent)).run();
stream.writeEndElement(); // profilerDataModel
diff --git a/tools/qmltc/CMakeLists.txt b/tools/qmltc/CMakeLists.txt
index 8e7ce0a586..42e47e24da 100644
--- a/tools/qmltc/CMakeLists.txt
+++ b/tools/qmltc/CMakeLists.txt
@@ -3,7 +3,7 @@
qt_get_tool_target_name(target_name qmltc)
qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Type Compiler"
+ TARGET_DESCRIPTION "QML type compiler"
TOOLS_TARGET Qml
SOURCES
main.cpp
diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp
index 6ef0afe105..a310c3d3c6 100644
--- a/tools/qmltc/main.cpp
+++ b/tools/qmltc/main.cpp
@@ -24,6 +24,8 @@
#include <QtQml/private/qqmljsastvisitor_p.h>
#include <QtQml/private/qqmljsast_p.h>
#include <QtQml/private/qqmljsdiagnosticmessage_p.h>
+#include <QtQmlCompiler/qqmlsa.h>
+#include <QtQmlCompiler/private/qqmljsliteralbindingcheck_p.h>
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
@@ -31,8 +33,8 @@ using namespace Qt::StringLiterals;
void setupLogger(QQmlJSLogger &logger) // prepare logger to work with compiler
{
- for (const QQmlJSLogger::Category &category : logger.categories()) {
- if (category == qmlUnusedImports)
+ for (const QQmlJS::LoggerCategory &category : logger.categories()) {
+ if (category.id() == qmlUnusedImports)
continue;
logger.setCategoryLevel(category.id(), QtCriticalMsg);
logger.setCategoryIgnored(category.id(), false);
@@ -99,6 +101,25 @@ int main(int argc, char **argv)
QCoreApplication::translate("main", "namespace")
};
parser.addOption(namespaceOption);
+ QCommandLineOption moduleOption{
+ u"module"_s,
+ QCoreApplication::translate("main",
+ "Name of the QML module that this QML code belongs to."),
+ QCoreApplication::translate("main", "module")
+ };
+ parser.addOption(moduleOption);
+ QCommandLineOption exportOption{ u"export"_s,
+ QCoreApplication::translate(
+ "main", "Export macro used in the generated C++ code"),
+ QCoreApplication::translate("main", "export") };
+ parser.addOption(exportOption);
+ QCommandLineOption exportIncludeOption{
+ u"exportInclude"_s,
+ QCoreApplication::translate(
+ "main", "Header defining the export macro to be used in the generated C++ code"),
+ QCoreApplication::translate("main", "exportInclude")
+ };
+ parser.addOption(exportIncludeOption);
parser.process(app);
@@ -224,6 +245,8 @@ int main(int argc, char **argv)
info.outputHFile = parser.value(outputHOption);
info.resourcePath = firstQml(paths);
info.outputNamespace = parser.value(namespaceOption);
+ info.exportMacro = parser.value(exportOption);
+ info.exportInclude = parser.value(exportIncludeOption);
if (info.outputCppFile.isEmpty()) {
fprintf(stderr, "An output C++ file is required. Pass one using --impl");
@@ -236,24 +259,37 @@ int main(int argc, char **argv)
QQmlJSImporter importer { importPaths, &mapper };
importer.setMetaDataMapper(&metaDataMapper);
- auto createQmltcVisitor = [](const QQmlJSScope::Ptr &root, QQmlJSImporter *importer,
- QQmlJSLogger *logger, const QString &implicitImportDirectory,
- const QStringList &qmldirFiles) -> QQmlJSImportVisitor * {
- return new QmltcVisitor(root, importer, logger, implicitImportDirectory, qmldirFiles);
+ auto qmltcVisitor = [](QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
+ const QQmlJSImporter::ImportVisitorPrerequisites &p) {
+ QmltcVisitor v(p.m_target, self, p.m_logger, p.m_implicitImportDirectory, p.m_qmldirFiles);
+ QQmlJS::AST::Node::accept(rootNode, &v);
};
- importer.setImportVisitorCreator(createQmltcVisitor);
+ importer.setImportVisitor(qmltcVisitor);
QQmlJSLogger logger;
logger.setFileName(url);
logger.setCode(sourceCode);
setupLogger(logger);
- QmltcVisitor visitor(QQmlJSScope::create(), &importer, &logger,
+ auto currentScope = QQmlJSScope::create();
+ if (parser.isSet(moduleOption))
+ currentScope->setOwnModuleName(parser.value(moduleOption));
+
+ QmltcVisitor visitor(currentScope, &importer, &logger,
QQmlJSImportVisitor::implicitImportDirectory(url, &mapper), qmldirFiles);
visitor.setMode(QmltcVisitor::Compile);
QmltcTypeResolver typeResolver { &importer };
typeResolver.init(&visitor, qmlParser.rootNode());
+ using PassManagerPtr =
+ std::unique_ptr<QQmlSA::PassManager,
+ decltype(&QQmlSA::PassManagerPrivate::deletePassManager)>;
+ PassManagerPtr passMan(QQmlSA::PassManagerPrivate::createPassManager(&visitor, &typeResolver),
+ &QQmlSA::PassManagerPrivate::deletePassManager);
+ passMan->registerPropertyPass(std::make_unique<QQmlJSLiteralBindingCheck>(passMan.get()),
+ QString(), QString(), QString());
+ passMan->analyze(QQmlJSScope::createQQmlSAElement(visitor.result()));
+
if (logger.hasErrors())
return EXIT_FAILURE;
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp
index 8010f1066c..134de0f98e 100644
--- a/tools/qmltc/qmltccodewriter.cpp
+++ b/tools/qmltc/qmltccodewriter.cpp
@@ -42,14 +42,14 @@ static QString getFunctionCategory(const QmltcMethod &method)
{
QString category = getFunctionCategory(static_cast<const QmltcMethodBase &>(method));
switch (method.type) {
- case QQmlJSMetaMethod::Signal:
+ case QQmlJSMetaMethodType::Signal:
category = u"Q_SIGNALS"_s;
break;
- case QQmlJSMetaMethod::Slot:
+ case QQmlJSMetaMethodType::Slot:
category += u" Q_SLOTS"_s;
break;
- case QQmlJSMetaMethod::Method:
- case QQmlJSMetaMethod::StaticMethod:
+ case QQmlJSMetaMethodType::Method:
+ case QQmlJSMetaMethodType::StaticMethod:
break;
}
return category;
@@ -118,6 +118,7 @@ void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString
code.rawAppendToHeader(u"#include <QtCore/qproperty.h>");
code.rawAppendToHeader(u"#include <QtCore/qobject.h>");
code.rawAppendToHeader(u"#include <QtCore/qcoreapplication.h>");
+ code.rawAppendToHeader(u"#include <QtCore/qxpfunctional.h>");
code.rawAppendToHeader(u"#include <QtQml/qqmlengine.h>");
code.rawAppendToHeader(u"#include <QtCore/qurl.h>"); // used in engine execution
code.rawAppendToHeader(u"#include <QtQml/qqml.h>"); // used for attached properties
@@ -160,6 +161,64 @@ void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString
}
}
+void QmltcCodeWriter::write(QmltcOutputWrapper &code,
+ const QmltcPropertyInitializer &propertyInitializer,
+ const QmltcType &wrappedType)
+{
+ code.rawAppendToHeader(u"class " + propertyInitializer.name + u" {");
+
+ {
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ code.rawAppendToHeader(u"friend class " + wrappedType.cppType + u";");
+ }
+
+ code.rawAppendToHeader(u"public:"_s);
+
+ [[maybe_unused]] QmltcOutputWrapper::MemberNameScope typeScope(&code, propertyInitializer.name);
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ write(code, propertyInitializer.constructor);
+ code.rawAppendToHeader(u""); // blank line
+
+ for (const auto &propertySetter : propertyInitializer.propertySetters) {
+ write(code, propertySetter);
+ }
+ }
+
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"private:"_s);
+
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ write(code, propertyInitializer.component);
+ write(code, propertyInitializer.initializedCache);
+ }
+ }
+
+ code.rawAppendToHeader(u"};"_s);
+ code.rawAppendToHeader(u""); // blank line
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle)
+{
+ code.rawAppendToHeader(u"struct " + requiredPropertiesBundle.name + u" {");
+
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ for (const auto &member : requiredPropertiesBundle.members) {
+ write(code, member);
+ }
+ }
+
+ code.rawAppendToHeader(u"};"_s);
+ code.rawAppendToHeader(u""); // blank line
+}
+
void QmltcCodeWriter::writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
const QString &outNamespace)
{
@@ -187,12 +246,14 @@ static void writeToFile(const QString &path, const QByteArray &data)
QFileInfo fi(path);
if (fi.exists() && fi.size() == data.size()) {
QFile oldFile(path);
- oldFile.open(QIODevice::ReadOnly);
- if (oldFile.readAll() == data)
- return;
+ if (oldFile.open(QIODevice::ReadOnly)) {
+ if (oldFile.readAll() == data)
+ return;
+ }
}
QFile file(path);
- file.open(QIODevice::WriteOnly);
+ if (!file.open(QIODevice::WriteOnly))
+ qFatal("Could not open file %s", qPrintable(path));
file.write(data);
}
@@ -209,7 +270,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &progra
code.rawAppendToHeader(u"class " + type.cppType + u";");
// write all the types and their content
for (const QmltcType &type : std::as_const(program.compiledTypes))
- write(code, type);
+ write(code, type, program.exportMacro);
// add typeCount definitions. after all types have been written down (so
// they are now complete types as per C++). practically, this only concerns
@@ -251,10 +312,14 @@ static void dumpFunctions(QmltcOutputWrapper &code, const QList<QmltcMethod> &fu
}
}
-void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type,
+ const QString &exportMacro)
{
const auto constructClassString = [&]() {
- QString str = u"class " + type.cppType;
+ QString str = u"class "_s;
+ if (!exportMacro.isEmpty())
+ str.append(exportMacro).append(u" "_s);
+ str.append(type.cppType);
QStringList nonEmptyBaseClasses;
nonEmptyBaseClasses.reserve(type.baseClasses.size());
std::copy_if(type.baseClasses.cbegin(), type.baseClasses.cend(),
@@ -287,6 +352,12 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
code.rawAppendToHeader(u"/* External C++ API */");
code.rawAppendToHeader(u"public:", -1);
+ if (!type.propertyInitializer.name.isEmpty())
+ write(code, type.propertyInitializer, type);
+
+ if (type.requiredPropertiesBundle)
+ write(code, *type.requiredPropertiesBundle);
+
// NB: when non-document root, the externalCtor won't be public - but we
// really don't care about the output format of such types
if (!type.ignoreInit && type.externalCtor.access == QQmlJSMetaMethod::Public) {
@@ -340,7 +411,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
// children
for (const auto &child : std::as_const(type.children))
- QmltcCodeWriter::write(code, child);
+ QmltcCodeWriter::write(code, child, exportMacro);
// (non-visible) functions
dumpFunctions(code, type.functions, std::not_fn(isUserVisibleFunction));
@@ -392,14 +463,14 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
{
const auto [hSignature, cppSignature] = functionSignatures(method);
// Note: augment return type with preambles in declaration
- code.rawAppendToHeader((method.type == QQmlJSMetaMethod::StaticMethod
+ code.rawAppendToHeader((method.type == QQmlJSMetaMethodType::StaticMethod
? u"static " + functionReturnType(method)
: functionReturnType(method))
+ u" " + hSignature + u";");
// do not generate method implementation if it is a signal
const auto methodType = method.type;
- if (methodType != QQmlJSMetaMethod::Signal) {
+ if (methodType != QQmlJSMetaMethodType::Signal) {
code.rawAppendToCpp(u""_s); // blank line
if (method.comments.size() > 0) {
code.rawAppendToCpp(u"/*! \\internal"_s);
diff --git a/tools/qmltc/qmltccodewriter.h b/tools/qmltc/qmltccodewriter.h
index e95777998e..20b0262737 100644
--- a/tools/qmltc/qmltccodewriter.h
+++ b/tools/qmltc/qmltccodewriter.h
@@ -20,13 +20,15 @@ struct QmltcCodeWriter
static void writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
const QString &outNamespace);
static void write(QmltcOutputWrapper &code, const QmltcProgram &program);
- static void write(QmltcOutputWrapper &code, const QmltcType &type);
+ static void write(QmltcOutputWrapper &code, const QmltcType &type, const QString &exportMacro);
static void write(QmltcOutputWrapper &code, const QmltcEnum &enumeration);
static void write(QmltcOutputWrapper &code, const QmltcMethod &method);
static void write(QmltcOutputWrapper &code, const QmltcCtor &ctor);
static void write(QmltcOutputWrapper &code, const QmltcDtor &dtor);
static void write(QmltcOutputWrapper &code, const QmltcVariable &var);
static void write(QmltcOutputWrapper &code, const QmltcProperty &prop);
+ static void write(QmltcOutputWrapper &code, const QmltcPropertyInitializer &propertyInitializer, const QmltcType& wrappedType);
+ static void write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle);
private:
static void writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod); // special
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index 4050136ef2..75bd580e07 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -8,6 +8,7 @@
#include "qmltccompilerpieces.h"
#include <QtCore/qloggingcategory.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <private/qqmljsutils_p.h>
#include <algorithm>
@@ -22,6 +23,137 @@ bool qIsReferenceTypeList(const QQmlJSMetaProperty &p)
return false;
}
+static QList<QQmlJSMetaProperty> unboundRequiredProperties(
+ const QQmlJSScope::ConstPtr &type,
+ QmltcTypeResolver *resolver
+) {
+ QList<QQmlJSMetaProperty> requiredProperties{};
+
+ auto isPropertyRequired = [&type, &resolver](const auto &property) {
+ if (!type->isPropertyRequired(property.propertyName()))
+ return false;
+
+ if (type->hasPropertyBindings(property.propertyName()))
+ return false;
+
+ if (property.isAlias()) {
+ QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
+
+ QQmlJSUtils::ResolvedAlias result =
+ QQmlJSUtils::resolveAlias(resolver, property, type, aliasVisitor);
+
+ if (result.kind != QQmlJSUtils::AliasTarget_Property)
+ return false;
+
+ // If the top level alias targets a property that is in
+ // the top level scope and that property is required, then
+ // we will already pick up the property during one of the
+ // iterations.
+ // Setting the property or the alias is the same so we
+ // discard one of the two, as otherwise we would require
+ // the user to pass two values for the same property ,in
+ // this case the alias.
+ //
+ // For example in:
+ //
+ // ```
+ // Item {
+ // id: self
+ // required property int foo
+ // property alias bar: self.foo
+ // }
+ // ```
+ //
+ // Both foo and bar are required but setting one or the
+ // other is the same operation so that we should choose
+ // only one.
+ if (result.owner == type &&
+ type->isPropertyRequired(result.property.propertyName()))
+ return false;
+
+ if (result.owner->hasPropertyBindings(result.property.propertyName()))
+ return false;
+ }
+
+ return true;
+ };
+
+ const auto properties = type->properties();
+ std::copy_if(properties.cbegin(), properties.cend(),
+ std::back_inserter(requiredProperties), isPropertyRequired);
+ std::sort(requiredProperties.begin(), requiredProperties.end(),
+ [](const auto &left, const auto &right) {
+ return left.propertyName() < right.propertyName();
+ });
+
+ return requiredProperties;
+}
+
+
+// Populates the internal representation for a
+// RequiredPropertiesBundle, a class that acts as a bundle of initial
+// values that should be set for the required properties of a type.
+static void compileRequiredPropertiesBundle(
+ QmltcType &current,
+ const QQmlJSScope::ConstPtr &type,
+ QmltcTypeResolver *resolver
+) {
+
+ QList<QQmlJSMetaProperty> requiredProperties = unboundRequiredProperties(type, resolver);
+
+ if (requiredProperties.isEmpty())
+ return;
+
+ current.requiredPropertiesBundle.emplace();
+ current.requiredPropertiesBundle->name = u"RequiredPropertiesBundle"_s;
+
+ current.requiredPropertiesBundle->members.reserve(requiredProperties.size());
+ std::transform(requiredProperties.cbegin(), requiredProperties.cend(),
+ std::back_inserter(current.requiredPropertiesBundle->members),
+ [](const QQmlJSMetaProperty &property) {
+ QString type = qIsReferenceTypeList(property)
+ ? u"const QList<%1*>&"_s.arg(
+ property.type()->valueType()->internalName())
+ : u"passByConstRefOrValue<%1>"_s.arg(
+ property.type()->augmentedInternalName());
+ return QmltcVariable{ type, property.propertyName() };
+ });
+}
+
+static void compileRootExternalConstructorBody(
+ QmltcType& current,
+ const QQmlJSScope::ConstPtr &type
+) {
+ current.externalCtor.body << u"// document root:"_s;
+ // if it's document root, we want to create our QQmltcObjectCreationBase
+ // that would store all the created objects
+ current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
+ type->internalName());
+ current.externalCtor.body
+ << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
+ current.externalCtor.body << u"creator.set(0, this);"_s; // special case
+
+ QString initializerName = u"initializer"_s;
+ if (current.requiredPropertiesBundle) {
+ // Compose new initializer based on the initial values for required properties.
+ current.externalCtor.body << u"auto newInitializer = [&](auto& propertyInitializer) {"_s;
+ for (const auto& member : current.requiredPropertiesBundle->members) {
+ current.externalCtor.body << u" propertyInitializer.%1(requiredPropertiesBundle.%2);"_s.arg(
+ QmltcPropertyData(member.name).write, member.name
+ );
+ }
+ current.externalCtor.body << u" initializer(propertyInitializer);"_s;
+ current.externalCtor.body << u"};"_s;
+
+ initializerName = u"newInitializer"_s;
+ }
+
+ // now call init
+ current.externalCtor.body << current.init.name
+ + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
+ u"endInit */ true, %1);"_s.arg(initializerName);
+};
+
Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
const QString QmltcCodeGenerator::privateEngineName = u"ePriv"_s;
@@ -87,6 +219,9 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
const auto *inlineComponentAName = std::get_if<InlineComponentNameType>(&a);
const auto *inlineComponentBName = std::get_if<InlineComponentNameType>(&b);
+ if (inlineComponentAName == inlineComponentBName)
+ return false;
+
// the root comes at last, so (a < b) == true when b is the root and a is not
if (inlineComponentAName && !inlineComponentBName)
return true;
@@ -127,7 +262,7 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
};
for (const auto &type : pureTypes) {
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
compiledTypes.emplaceBack(); // create empty type
compileType(compiledTypes.back(), type, compile);
}
@@ -142,8 +277,11 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
program.cppPath = m_info.outputCppFile;
program.hPath = m_info.outputHFile;
program.outNamespace = m_info.outputNamespace;
+ program.exportMacro = m_info.exportMacro;
program.compiledTypes = compiledTypes;
program.includes = m_visitor->cppIncludeFiles();
+ if (!m_info.exportMacro.isEmpty() && !m_info.exportInclude.isEmpty())
+ program.includes += (m_info.exportInclude);
program.urlMethod = urlMethod;
QmltcOutput out;
@@ -186,7 +324,8 @@ void QmltcCompiler::compileType(
// make document root a friend to allow it to access init and endInit
const QString rootInternalName =
m_visitor->inlineComponent(type->enclosingInlineComponentName())->internalName();
- current.otherCode << u"friend class %1;"_s.arg(rootInternalName);
+ if (rootInternalName != current.cppType) // avoid GCC13 warning on self-befriending
+ current.otherCode << "friend class %1;"_L1.arg(rootInternalName);
}
if (documentRoot || inlineComponent) {
auto name = type->inlineComponentName()
@@ -210,8 +349,11 @@ void QmltcCompiler::compileType(
return scope->parentScope();
return scope;
};
- current.otherCode << u"friend class %1;"_s.arg(
- realQmlScope(type->parentScope())->internalName());
+
+ const auto& realScope = realQmlScope(type->parentScope());
+ if (realScope != rootType) {
+ current.otherCode << u"friend class %1;"_s.arg(realScope->internalName());
+ }
}
// make QQmltcObjectCreationHelper a friend of every type since it provides
@@ -240,6 +382,17 @@ void QmltcCompiler::compileType(
current.finalizeComponent.access = QQmlJSMetaMethod::Protected;
current.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
+ current.propertyInitializer.name = u"PropertyInitializer"_s;
+ current.propertyInitializer.constructor.access = QQmlJSMetaMethod::Public;
+ current.propertyInitializer.constructor.name = current.propertyInitializer.name;
+ current.propertyInitializer.constructor.parameterList = {
+ QmltcVariable(u"%1&"_s.arg(current.cppType), u"component"_s)
+ };
+ current.propertyInitializer.component.cppType = current.cppType + u"&";
+ current.propertyInitializer.component.name = u"component"_s;
+ current.propertyInitializer.initializedCache.cppType = u"QSet<QString>"_s;
+ current.propertyInitializer.initializedCache.name = u"initializedCache"_s;
+
current.baselineCtor.name = current.cppType;
current.externalCtor.name = current.cppType;
current.init.name = u"QML_init"_s;
@@ -259,19 +412,40 @@ void QmltcCompiler::compileType(
QmltcVariable creator(u"QQmltcObjectCreationHelper*"_s, u"creator"_s);
QmltcVariable engine(u"QQmlEngine*"_s, u"engine"_s);
QmltcVariable parent(u"QObject*"_s, u"parent"_s, u"nullptr"_s);
+ QmltcVariable initializedCache(
+ u"[[maybe_unused]] const QSet<QString>&"_s,
+ u"initializedCache"_s,
+ u"{}"_s
+ );
QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_s, u"parentContext"_s);
QmltcVariable finalizeFlag(u"bool"_s, u"canFinalize"_s);
current.baselineCtor.parameterList = { parent };
current.endInit.parameterList = { creator, engine };
- current.setComplexBindings.parameterList = { creator, engine };
+ current.setComplexBindings.parameterList = { creator, engine, initializedCache };
current.handleOnCompleted.parameterList = { creator };
if (documentRoot || inlineComponent) {
- current.externalCtor.parameterList = { engine, parent };
- current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
+ const QmltcVariable initializer(
+ u"[[maybe_unused]] qxp::function_ref<void(%1&)>"_s.arg(current.propertyInitializer.name),
+ u"initializer"_s,
+ u"[](%1&){}"_s.arg(current.propertyInitializer.name));
+
+ current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag, initializer };
current.beginClass.parameterList = { creator, finalizeFlag };
current.completeComponent.parameterList = { creator, finalizeFlag };
current.finalizeComponent.parameterList = { creator, finalizeFlag };
+
+ compileRequiredPropertiesBundle(current, type, m_typeResolver);
+
+ if (current.requiredPropertiesBundle) {
+ QmltcVariable bundle{
+ u"const %1&"_s.arg(current.requiredPropertiesBundle->name),
+ u"requiredPropertiesBundle"_s,
+ };
+ current.externalCtor.parameterList = { engine, bundle, parent, initializer };
+ } else {
+ current.externalCtor.parameterList = { engine, parent, initializer };
+ }
} else {
current.externalCtor.parameterList = { creator, engine, parent };
current.init.parameterList = { creator, engine, ctxtdata };
@@ -295,18 +469,7 @@ void QmltcCompiler::compileType(
// compilation stub:
current.externalCtor.body << u"Q_UNUSED(engine)"_s;
if (documentRoot || inlineComponent) {
- current.externalCtor.body << u"// document root:"_s;
- // if it's document root, we want to create our QQmltcObjectCreationBase
- // that would store all the created objects
- current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
- type->internalName());
- current.externalCtor.body
- << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
- current.externalCtor.body << u"creator.set(0, this);"_s; // special case
- // now call init
- current.externalCtor.body << current.init.name
- + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
- u"endInit */ true);";
+ compileRootExternalConstructorBody(current, type);
} else {
current.externalCtor.body << u"// not document root:"_s;
// just call init, we don't do any setup here otherwise
@@ -321,7 +484,7 @@ void QmltcCompiler::compileType(
staticCreate.comments
<< u"Used by the engine for singleton creation."_s
<< u"See also \\l {https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON}."_s;
- staticCreate.type = QQmlJSMetaMethod::StaticMethod;
+ staticCreate.type = QQmlJSMetaMethodType::StaticMethod;
staticCreate.access = QQmlJSMetaMethod::Public;
staticCreate.name = u"create"_s;
staticCreate.returnType = u"%1 *"_s.arg(current.cppType);
@@ -354,6 +517,102 @@ static Iterator partitionBindings(Iterator first, Iterator last)
});
}
+// Populates the propertyInitializer of the current type based on the
+// available properties.
+//
+// A propertyInitializer is a generated class that provides a
+// restricted interface that only allows setting property values and
+// internally keep tracks of which properties where actually set,
+// intended to be used to allow the user to set up the initial values
+// when creating an instance of a component.
+//
+// For each property of the current type that is known, is not private
+// and is writable, a setter method is generated.
+// Each setter method knows how to set a specific property, so as to
+// provide a strongly typed interface to property setting, as if the
+// relevant C++ type was used directly.
+//
+// Each setter uses the write method for the proprerty when available
+// and otherwise falls back to a the more generic
+// `QObject::setProperty` for properties where a WRITE method is not
+// available or in scope.
+static void compilePropertyInitializer(QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ static auto isFromExtension = [](const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &scope) {
+ return scope->ownerOfProperty(scope, property.propertyName()).extensionSpecifier != QQmlJSScope::NotExtension;
+ };
+
+ current.propertyInitializer.constructor.initializerList << u"component{component}"_s;
+
+ auto properties = type->properties().values();
+ for (auto& property: properties) {
+ if (property.index() == -1) continue;
+ if (property.isPrivate()) continue;
+ if (!property.isWritable() && !qIsReferenceTypeList(property)) continue;
+
+ const QString name = property.propertyName();
+
+ current.propertyInitializer.propertySetters.emplace_back();
+ auto& compiledSetter = current.propertyInitializer.propertySetters.back();
+
+ compiledSetter.userVisible = true;
+ compiledSetter.returnType = u"void"_s;
+ compiledSetter.name = QmltcPropertyData(property).write;
+
+ if (qIsReferenceTypeList(property)) {
+ compiledSetter.parameterList.emplaceBack(
+ QQmlJSUtils::constRefify(u"QList<%1*>"_s.arg(property.type()->valueType()->internalName())),
+ name + u"_", QString()
+ );
+ } else {
+ compiledSetter.parameterList.emplaceBack(
+ QQmlJSUtils::constRefify(getUnderlyingType(property)), name + u"_", QString()
+ );
+ }
+
+ if (qIsReferenceTypeList(property)) {
+ compiledSetter.body << u"QQmlListReference list_ref_(&%1, \"%2\");"_s.arg(
+ current.propertyInitializer.component.name, name
+ );
+ compiledSetter.body << u"list_ref_.clear();"_s;
+ compiledSetter.body << u"for (const auto& list_item_ : %1_)"_s.arg(name);
+ compiledSetter.body << u" list_ref_.append(list_item_);"_s;
+ } else if (
+ QQmlJSUtils::bindablePropertyHasDefaultAccessor(property, QQmlJSUtils::PropertyAccessor_Write)
+ ) {
+ compiledSetter.body << u"%1.%2().setValue(%3_);"_s.arg(
+ current.propertyInitializer.component.name, property.bindable(), name);
+ } else if (type->hasOwnProperty(name)) {
+ compiledSetter.body << u"%1.%2(%3_);"_s.arg(
+ current.propertyInitializer.component.name, QmltcPropertyData(property).write, name);
+ } else if (property.write().isEmpty() || isFromExtension(property, type)) {
+ // We can end here if a WRITE method is not available or
+ // if the method is available but not in this scope, so
+ // that we fallback to the string-based setters..
+ //
+ // For example, types that makes use of QML_EXTENDED
+ // types, will have the extension types properties
+ // available and with a WRITE method, but the WRITE method
+ // will not be available to the extended type, from C++,
+ // as the type does not directly inherit from the
+ // extension type.
+ //
+ // We specifically scope `setProperty` to `QObject` as
+ // certain types might have shadowed the method.
+ // For example, in QtQuick, some types have a property
+ // called `property` with a `setProperty` WRITE method
+ // that will produce the shadowing.
+ compiledSetter.body << u"%1.QObject::setProperty(\"%2\", QVariant::fromValue(%2_));"_s.arg(
+ current.propertyInitializer.component.name, name);
+ } else {
+ compiledSetter.body << u"%1.%2(%3_);"_s.arg(
+ current.propertyInitializer.component.name, property.write(), name);
+ }
+
+ compiledSetter.body << u"%1.insert(\"%2\");"_s.arg(
+ current.propertyInitializer.initializedCache.name, name);
+ }
+}
+
void QmltcCompiler::compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type)
{
// compile components of a type:
@@ -396,6 +655,7 @@ void QmltcCompiler::compileTypeElements(QmltcType &current, const QQmlJSScope::C
auto bindings = type->ownPropertyBindingsInQmlIROrder();
partitionBindings(bindings.begin(), bindings.end());
+ compilePropertyInitializer(current, type);
compileBinding(current, bindings.begin(), bindings.end(), type, { type });
}
@@ -450,7 +710,7 @@ compileMethodParameters(const QList<QQmlJSMetaParameter> &parameterInfos, bool a
static QString figureReturnType(const QQmlJSMetaMethod &m)
{
const bool isVoidMethod =
- m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethod::Signal;
+ m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethodType::Signal;
Q_ASSERT(isVoidMethod || m.returnType());
QString type;
if (isVoidMethod) {
@@ -467,10 +727,10 @@ void QmltcCompiler::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
const auto returnType = figureReturnType(m);
const QList<QmltcVariable> compiledParams = compileMethodParameters(m.parameters());
- const auto methodType = QQmlJSMetaMethod::Type(m.methodType());
+ const auto methodType = m.methodType();
QStringList code;
- if (methodType != QQmlJSMetaMethod::Signal) {
+ if (methodType != QQmlJSMetaMethodType::Signal) {
QmltcCodeGenerator urlGenerator { m_url, m_visitor };
QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
&code, urlGenerator.urlMethodName() + u"()",
@@ -485,7 +745,7 @@ void QmltcCompiler::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
compiled.body = std::move(code);
compiled.type = methodType;
compiled.access = m.access();
- if (methodType != QQmlJSMetaMethod::Signal) {
+ if (methodType != QQmlJSMetaMethodType::Signal) {
compiled.declarationPrefixes << u"Q_INVOKABLE"_s;
compiled.userVisible = m.access() == QQmlJSMetaMethod::Public;
} else {
@@ -1057,7 +1317,7 @@ void QmltcCompiler::compileObjectBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::Object);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Object);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1115,7 +1375,7 @@ void QmltcCompiler::compileObjectBinding(QmltcType &current,
Q_ASSERT(object->baseType()->internalName() == u"QQmlComponent"_s);
if (int id = m_visitor->runtimeId(object); id >= 0) {
- QString idString = m_visitor->addressableScopes().id(object);
+ QString idString = m_visitor->addressableScopes().id(object, object);
if (idString.isEmpty())
idString = u"<unknown>"_s;
QmltcCodeGenerator::generate_setIdValue(block, u"thisContext"_s, id, objectName,
@@ -1154,8 +1414,8 @@ void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::ValueSource
- || binding.bindingType() == QQmlJSMetaPropertyBinding::Interceptor);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::ValueSource
+ || binding.bindingType() == QQmlSA::BindingType::Interceptor);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1163,7 +1423,7 @@ void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType &current,
// NB: object is compiled with compileType(), here just need to use it
QSharedPointer<const QQmlJSScope> object;
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Interceptor)
+ if (binding.bindingType() == QQmlSA::BindingType::Interceptor)
object = binding.interceptorType();
else
object = binding.valueSourceType();
@@ -1212,7 +1472,7 @@ void QmltcCompiler::compileAttachedPropertyBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::AttachedProperty);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::AttachedProperty);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1276,7 +1536,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::GroupProperty);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1333,7 +1593,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
auto it = subbindings.begin();
Q_ASSERT(std::all_of(it, firstScript, [](const auto &x) {
- return x.bindingType() != QQmlJSMetaPropertyBinding::Script;
+ return x.bindingType() != QQmlSA::BindingType::Script;
}));
compile(it, firstScript);
it = firstScript;
@@ -1354,7 +1614,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
// once the value is written back, process the script bindings
Q_ASSERT(std::all_of(it, subbindings.end(), [](const auto &x) {
- return x.bindingType() == QQmlJSMetaPropertyBinding::Script;
+ return x.bindingType() == QQmlSA::BindingType::Script;
}));
compile(it, subbindings.end());
}
@@ -1368,8 +1628,8 @@ void QmltcCompiler::compileTranslationBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::Translation
- || binding.bindingType() == QQmlJSMetaPropertyBinding::TranslationById);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Translation
+ || binding.bindingType() == QQmlSA::BindingType::TranslationById);
const QString &propertyName = binding.propertyName();
@@ -1446,7 +1706,7 @@ void QmltcCompiler::compileBinding(QmltcType &current,
const auto location = binding.sourceLocation();
// make sure group property is not generalized by checking if type really has a property
// called propertyName. If not, it is probably an id.
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty
+ if (binding.bindingType() == QQmlSA::BindingType::GroupProperty
&& type->hasProperty(propertyName)) {
qCWarning(lcQmltcCompiler)
<< QStringLiteral("Binding at line %1 column %2 is not deferred as it is a "
@@ -1493,26 +1753,32 @@ void QmltcCompiler::compileBindingByType(QmltcType &current,
accessor.name, constructFromQObject);
};
switch (binding.bindingType()) {
- case QQmlJSMetaPropertyBinding::BoolLiteral: {
+ case QQmlSA::BindingType::BoolLiteral: {
const bool value = binding.boolValue();
assignToProperty(metaProperty, value ? u"true"_s : u"false"_s);
break;
}
- case QQmlJSMetaPropertyBinding::NumberLiteral: {
+ case QQmlSA::BindingType::NumberLiteral: {
assignToProperty(metaProperty, QString::number(binding.numberValue()));
break;
}
- case QQmlJSMetaPropertyBinding::StringLiteral: {
- assignToProperty(metaProperty, QQmlJSUtils::toLiteral(binding.stringValue()));
+ case QQmlSA::BindingType::StringLiteral: {
+ QString value = QQmlJSUtils::toLiteral(binding.stringValue());
+ if (auto type = metaProperty.type()) {
+ if (type->internalName() == u"QUrl"_s) {
+ value = u"QUrl(%1)"_s.arg(value);
+ }
+ }
+ assignToProperty(metaProperty, value);
break;
}
- case QQmlJSMetaPropertyBinding::RegExpLiteral: {
+ case QQmlSA::BindingType::RegExpLiteral: {
const QString value =
u"QRegularExpression(%1)"_s.arg(QQmlJSUtils::toLiteral(binding.regExpValue()));
assignToProperty(metaProperty, value);
break;
}
- case QQmlJSMetaPropertyBinding::Null: {
+ case QQmlSA::BindingType::Null: {
// poor check: null bindings are only supported for var and objects
Q_ASSERT(propertyType->isSameType(m_typeResolver->varType())
|| propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
@@ -1522,38 +1788,38 @@ void QmltcCompiler::compileBindingByType(QmltcType &current,
assignToProperty(metaProperty, u"QVariant::fromValue(nullptr)"_s);
break;
}
- case QQmlJSMetaPropertyBinding::Script: {
+ case QQmlSA::BindingType::Script: {
QString bindingSymbolName = type->internalName() + u'_' + propertyName + u"_binding";
bindingSymbolName.replace(u'.', u'_'); // can happen with group properties
compileScriptBinding(current, binding, bindingSymbolName, type, propertyName, propertyType,
accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Object: {
+ case QQmlSA::BindingType::Object: {
compileObjectBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Interceptor:
+ case QQmlSA::BindingType::Interceptor:
Q_FALLTHROUGH();
- case QQmlJSMetaPropertyBinding::ValueSource: {
+ case QQmlSA::BindingType::ValueSource: {
compileValueSourceOrInterceptorBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::AttachedProperty: {
+ case QQmlSA::BindingType::AttachedProperty: {
compileAttachedPropertyBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::GroupProperty: {
+ case QQmlSA::BindingType::GroupProperty: {
compileGroupPropertyBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::TranslationById:
- case QQmlJSMetaPropertyBinding::Translation: {
+ case QQmlSA::BindingType::TranslationById:
+ case QQmlSA::BindingType::Translation: {
compileTranslationBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Invalid: {
+ case QQmlSA::BindingType::Invalid: {
recordError(binding.sourceLocation(), u"This binding is invalid"_s);
break;
}
@@ -1617,8 +1883,11 @@ static std::pair<QQmlJSMetaProperty, int> getMetaPropertyIndex(const QQmlJSScope
// index is already added as p.index())
if (type->isSameType(owner))
return;
- if (m == QQmlJSScope::ExtensionNamespace) // extension namespace properties are ignored
+
+ // extension namespace and JavaScript properties are ignored
+ if (m == QQmlJSScope::ExtensionNamespace || m == QQmlJSScope::ExtensionJavaScript)
return;
+
index += int(type->ownProperties().size());
};
QQmlJSUtils::traverseFollowingMetaObjectHierarchy(scope, owner, increment);
@@ -1649,10 +1918,10 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
Q_ASSERT(!objectClassName_signal.isEmpty());
Q_ASSERT(!objectClassName_slot.isEmpty());
- const auto signalMethods = objectType->methods(name, QQmlJSMetaMethod::Signal);
+ const auto signalMethods = objectType->methods(name, QQmlJSMetaMethodType::Signal);
Q_ASSERT(!signalMethods.isEmpty()); // an error somewhere else
QQmlJSMetaMethod signal = signalMethods.at(0);
- Q_ASSERT(signal.methodType() == QQmlJSMetaMethod::Signal);
+ Q_ASSERT(signal.methodType() == QQmlJSMetaMethodType::Signal);
const QString signalName = signal.methodName();
const QString slotName = newSymbol(signalName + u"_slot");
@@ -1672,7 +1941,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
objectType->ownRuntimeFunctionIndex(binding.scriptIndex()),
u"this"_s, // Note: because script bindings always use current QML object scope
signalReturnType, slotParameters);
- slotMethod.type = QQmlJSMetaMethod::Slot;
+ slotMethod.type = QQmlJSMetaMethodType::Slot;
current.functions << std::move(slotMethod);
current.setComplexBindings.body << u"QObject::connect(" + This_signal + u", " + u"&"
@@ -1681,7 +1950,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
};
switch (binding.scriptKind()) {
- case QQmlJSMetaPropertyBinding::Script_PropertyBinding: {
+ case QQmlSA::ScriptBindingKind::PropertyBinding: {
if (!propertyType) {
recordError(binding.sourceLocation(),
u"Binding on property '" + propertyName + u"' of unknown type");
@@ -1723,19 +1992,20 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
property, valueTypeIndex, accessor.name);
break;
}
- case QQmlJSMetaPropertyBinding::Script_SignalHandler: {
- const auto name = QQmlJSUtils::signalName(propertyName);
+ case QQmlSA::ScriptBindingKind::SignalHandler: {
+ const auto name = QQmlSignalNames::handlerNameToSignalName(propertyName);
Q_ASSERT(name.has_value());
compileScriptSignal(*name);
break;
}
- case QQmlJSMetaPropertyBinding::Script_ChangeHandler: {
+ case QQmlSA ::ScriptBindingKind::ChangeHandler: {
const QString objectClassName = objectType->internalName();
const QString bindingFunctorName = newSymbol(bindingSymbolName + u"Functor");
- const auto signalName = QQmlJSUtils::signalName(propertyName);
+ const auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
Q_ASSERT(signalName.has_value()); // an error somewhere else
- const auto actualProperty = QQmlJSUtils::changeHandlerProperty(objectType, *signalName);
+ const auto actualProperty =
+ QQmlJSUtils::propertyFromChangedHandler(objectType, propertyName);
Q_ASSERT(actualProperty.has_value()); // an error somewhere else
const auto actualPropertyType = actualProperty->type();
if (!actualPropertyType) {
@@ -1759,9 +2029,12 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
current.children << compileScriptBindingPropertyChangeHandler(
binding, objectType, m_urlMethodName, bindingFunctorName, objectClassName);
+ current.setComplexBindings.body << u"if (!%1.contains(\"%2\"))"_s.arg(
+ current.propertyInitializer.initializedCache.name, propertyName);
+
// TODO: this could be dropped if QQmlEngine::setContextForObject() is
// done before currently generated C++ object is constructed
- current.setComplexBindings.body << bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ current.setComplexBindings.body << u" "_s + bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ bindingFunctorName + u">("
+ QmltcCodeGenerator::wrap_privateClass(accessor.name, *actualProperty)
+ u"->" + bindableString + u"().onValueChanged(" + bindingFunctorName + u"("
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index d5f07aa3b0..3deab6d44e 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -25,6 +25,8 @@ struct QmltcCompilerInfo
QString outputHFile;
QString outputNamespace;
QString resourcePath;
+ QString exportMacro;
+ QString exportInclude;
};
class QmltcCompiler
@@ -48,7 +50,7 @@ public:
static bool isComplexBinding(const QQmlJSMetaPropertyBinding &binding)
{
// TODO: translation bindings (once supported) are also complex?
- return binding.bindingType() == QQmlJSMetaPropertyBinding::Script;
+ return binding.bindingType() == QQmlSA::BindingType::Script;
}
private:
@@ -185,14 +187,14 @@ private:
bool hasErrors() const { return m_logger->hasErrors(); }
void recordError(const QQmlJS::SourceLocation &location, const QString &message,
- LoggerWarningId id = qmlCompiler)
+ QQmlJS::LoggerWarningId id = qmlCompiler)
{
// pretty much any compiler error is a critical error (we cannot
// generate code - compilation fails)
m_logger->log(message, id, location);
}
void recordError(const QV4::CompiledData::Location &location, const QString &message,
- LoggerWarningId id = qmlCompiler)
+ QQmlJS::LoggerWarningId id = qmlCompiler)
{
recordError(QQmlJS::SourceLocation { 0, 0, location.line(), location.column() }, message,
id);
diff --git a/tools/qmltc/qmltccompilerpieces.cpp b/tools/qmltc/qmltccompilerpieces.cpp
index 601cf1bbed..cd1735bc07 100644
--- a/tools/qmltc/qmltccompilerpieces.cpp
+++ b/tools/qmltc/qmltccompilerpieces.cpp
@@ -15,8 +15,8 @@ static QString scopeName(const QQmlJSScope::ConstPtr &scope)
{
Q_ASSERT(scope->isFullyResolved());
const auto scopeType = scope->scopeType();
- if (scopeType == QQmlJSScope::GroupedPropertyScope
- || scopeType == QQmlJSScope::AttachedPropertyScope) {
+ if (scopeType == QQmlSA::ScopeType::GroupedPropertyScope
+ || scopeType == QQmlSA::ScopeType::AttachedPropertyScope) {
return scope->baseType()->internalName();
}
return scope->internalName();
@@ -237,15 +237,18 @@ void QmltcCodeGenerator::generate_createBindingOnProperty(
}
*block += prologue;
- *block << value + u"->" + bindable + u"().setBinding(" + createBindingForBindable + u");";
+ *block << u"if (!initializedCache.contains(\"%1\"))"_s.arg(p.propertyName());
+ *block << u" "_s + value + u"->" + bindable + u"().setBinding(" + createBindingForBindable + u");";
*block += epilogue;
} else {
QString createBindingForNonBindable =
- u"QT_PREPEND_NAMESPACE(QQmlCppBinding)::createBindingForNonBindable(" + unitVarName
+ u" "_s
+ + u"QT_PREPEND_NAMESPACE(QQmlCppBinding)::createBindingForNonBindable(" + unitVarName
+ u", " + scope + u", " + QString::number(functionIndex) + u", " + target + u", "
+ QString::number(propertyIndex) + u", " + QString::number(valueTypeIndex) + u", "
+ propName + u")";
// Note: in this version, the binding is set implicitly
+ *block << u"if (!initializedCache.contains(\"%1\"))"_s.arg(p.propertyName());
*block << createBindingForNonBindable + u";";
}
}
@@ -293,8 +296,8 @@ void QmltcCodeGenerator::generate_createTranslationBindingOnProperty(
QmltcCodeGenerator::PreparedValue
QmltcCodeGenerator::wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p, QString value)
{
- auto isDerivedFromBuiltin = [](QQmlJSScope::ConstPtr t, const QString &builtin) {
- for (; t; t = t->baseType()) {
+ auto isDerivedFromBuiltin = [](const QQmlJSScope::ConstPtr &derived, const QString &builtin) {
+ for (QQmlJSScope::ConstPtr t = derived; t; t = t->baseType()) {
if (t->internalName() == builtin)
return true;
}
@@ -302,7 +305,7 @@ QmltcCodeGenerator::wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p,
};
QStringList prologue;
QStringList epilogue;
- auto propType = p.type();
+ const QQmlJSScope::ConstPtr propType = p.type();
if (isDerivedFromBuiltin(propType, u"QVariant"_s)) {
const QString variantName = u"var_" + p.propertyName();
prologue << u"{ // accepts QVariant"_s;
diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h
index bfd22e1b79..3252f19e86 100644
--- a/tools/qmltc/qmltccompilerpieces.h
+++ b/tools/qmltc/qmltccompilerpieces.h
@@ -152,14 +152,17 @@ struct QmltcCodeGenerator
/*!
\internal
- Generates \a{current.init}'s code. The init method sets up a QQmlContext for
- the object and (in case \a type is a document root) calls other object
- creation methods in a well-defined order:
+ Generates \a{current.init}'s code. The init method sets up a
+ QQmlContext for the object and (in case \a type is a document
+ root) calls other object creation methods, and a user-provided
+ initialization callback, in a well-defined order:
1. current.beginClass
2. current.endInit
- 3. current.completeComponent
- 4. current.finalizeComponent
- 5. current.handleOnCompleted
+ 3. user-provided initialization function
+ 4. current.setComplexBindings
+ 5. current.completeComponent
+ 6. current.finalizeComponent
+ 7. current.handleOnCompleted
This function returns a QScopeGuard with the final instructions that have to
be generated at a later point, once everything else is compiled.
@@ -260,7 +263,7 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
if (int id = visitor->runtimeId(type); id >= 0) {
current.init.body << u"// 3. set id since it is provided"_s;
- QString idString = visitor->addressableScopes().id(type);
+ QString idString = visitor->addressableScopes().id(type, type);
if (idString.isEmpty())
idString = u"<unknown>"_s;
QmltcCodeGenerator::generate_setIdValue(&current.init.body, u"context"_s, id, u"this"_s,
@@ -294,8 +297,14 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
.arg(current.beginClass.name);
current.init.body << QStringLiteral(" %1(creator, engine);")
.arg(current.endInit.name);
- current.init.body << QStringLiteral(" %1(creator, engine);")
- .arg(current.setComplexBindings.name);
+
+ current.init.body << QStringLiteral(" {");
+ current.init.body << QStringLiteral(" PropertyInitializer propertyInitializer(*this);");
+ current.init.body << QStringLiteral(" initializer(propertyInitializer);");
+ current.init.body << QStringLiteral(" %1(creator, engine, propertyInitializer.initializedCache);").arg(current.setComplexBindings.name);
+ current.init.body << QStringLiteral(" }");
+
+
current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
.arg(current.completeComponent.name);
current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h
index 8884ddc3a3..de531f718d 100644
--- a/tools/qmltc/qmltcoutputir.h
+++ b/tools/qmltc/qmltcoutputir.h
@@ -75,7 +75,7 @@ struct QmltcMethodBase
struct QmltcMethod : QmltcMethodBase
{
QString returnType; // C++ return type
- QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::Method; // Qt function type
+ QQmlJSMetaMethodType type = QQmlJSMetaMethodType::Method; // Qt function type
// TODO: should be a better way to handle this
bool userVisible = false; // tells if a function is prioritized during the output generation
@@ -92,6 +92,42 @@ struct QmltcDtor : QmltcMethodBase
{
};
+// Represents a generated class that knows how to set the public,
+// writable properties of a compiled QML -> C++ type.
+// This is generally intended to be available for the root of the
+// document to allow the user to set the initial values for
+// properties, when creating a component, with support for strong
+// typing.
+struct QmltcPropertyInitializer {
+ QString name;
+
+ QmltcCtor constructor;
+
+ // A member containing a reference to the object for which the
+ // properties should be set.
+ QmltcVariable component;
+
+ // A member containing a cache of properties that were actually
+ // set that can be referenced later..
+ QmltcVariable initializedCache;
+
+ // Setter methods for each property.
+ QList<QmltcMethod> propertySetters;
+};
+
+// Represents a generated class that contains a bundle of values to
+// initialize the required properties of a type.
+//
+// This is generally intended to be available for the root component
+// of the document, where it will be used as a constructor argument to
+// force the user to provide initial values for the required
+// properties of the constructed type.
+struct QmltcRequiredPropertiesBundle {
+ QString name;
+
+ QList<QmltcVariable> members;
+};
+
// Represents QML -> C++ compiled type
struct QmltcType
{
@@ -131,6 +167,12 @@ struct QmltcType
// needed for singletons
std::optional<QmltcMethod> staticCreate{};
+
+ // A proxy class that provides a restricted interface that only
+ // allows setting the properties of the type.
+ QmltcPropertyInitializer propertyInitializer{};
+
+ std::optional<QmltcRequiredPropertiesBundle> requiredPropertiesBundle{};
};
// Represents whole QML program, compiled to C++
@@ -140,6 +182,8 @@ struct QmltcProgram
QString cppPath; // C++ output .cpp path
QString hPath; // C++ output .h path
QString outNamespace;
+ QString exportMacro; // if not empty, the macro that should be used to export the generated
+ // classes
QSet<QString> includes; // non-default C++ include files
QmltcMethod urlMethod; // returns QUrl of the QML document
diff --git a/tools/qmltc/qmltcpropertyutils.h b/tools/qmltc/qmltcpropertyutils.h
index db26c498dd..8a69e5ef09 100644
--- a/tools/qmltc/qmltcpropertyutils.h
+++ b/tools/qmltc/qmltcpropertyutils.h
@@ -6,6 +6,7 @@
#include <private/qqmljsmetatypes_p.h>
#include <private/qqmljsscope_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
QT_BEGIN_NAMESPACE
@@ -35,13 +36,11 @@ struct QmltcPropertyData
QmltcPropertyData(const QString &propertyName)
{
- const QString nameWithUppercase = propertyName[0].toUpper() + propertyName.sliced(1);
-
read = propertyName;
- write = u"set" + nameWithUppercase;
- bindable = u"bindable" + nameWithUppercase;
- notify = propertyName + u"Changed";
- reset = u"reset" + nameWithUppercase;
+ write = QQmlSignalNames::addPrefixToPropertyName(u"set", propertyName);
+ bindable = QQmlSignalNames::addPrefixToPropertyName(u"bindable", propertyName);
+ notify = QQmlSignalNames::propertyNameToChangedSignalName(propertyName);
+ reset = QQmlSignalNames::addPrefixToPropertyName(u"reset", propertyName);
}
QString read;
diff --git a/tools/qmltc/qmltctyperesolver.cpp b/tools/qmltc/qmltctyperesolver.cpp
index 045f3af088..a7bf9debac 100644
--- a/tools/qmltc/qmltctyperesolver.cpp
+++ b/tools/qmltc/qmltctyperesolver.cpp
@@ -18,9 +18,6 @@ void QmltcTypeResolver::init(QmltcVisitor *visitor, QQmlJS::AST::Node *program)
{
QQmlJSTypeResolver::init(visitor, program);
- QQmlJSLiteralBindingCheck literalCheck;
- literalCheck.run(visitor, this);
-
m_root = visitor->result();
QQueue<QQmlJSScope::Ptr> objects;
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
index 236ad76467..119308ef65 100644
--- a/tools/qmltc/qmltcvisitor.cpp
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -8,6 +8,7 @@
#include <QtCore/qstack.h>
#include <QtCore/qdir.h>
#include <QtCore/qloggingcategory.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <private/qqmljsutils_p.h>
@@ -101,6 +102,9 @@ void QmltcVisitor::findCppIncludes()
// look in type
addCppInclude(type);
+ if (type->isListProperty())
+ addCppInclude(type->valueType());
+
// look in type's base type
auto base = type->baseType();
if (!base && type->isComposite())
@@ -133,8 +137,9 @@ void QmltcVisitor::findCppIncludes()
Q_ASSERT(type);
const auto scopeType = type->scopeType();
- if (scopeType != QQmlJSScope::QMLScope && scopeType != QQmlJSScope::GroupedPropertyScope
- && scopeType != QQmlJSScope::AttachedPropertyScope) {
+ if (scopeType != QQmlSA::ScopeType::QMLScope
+ && scopeType != QQmlSA::ScopeType::GroupedPropertyScope
+ && scopeType != QQmlSA::ScopeType::AttachedPropertyScope) {
continue;
}
@@ -174,7 +179,7 @@ void QmltcVisitor::findCppIncludes()
static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr &scope)
{
- Q_ASSERT(scope->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(scope->scopeType() == QQmlSA::ScopeType::QMLScope);
Q_ASSERT(!scope->isArrayScope());
Q_ASSERT(!scope->baseTypeName().isEmpty());
// the scope is guaranteed to be a new QML type, so any prefixes (separated
@@ -197,7 +202,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
}
// we're not interested in non-QML scopes
- if (m_currentScope->scopeType() != QQmlJSScope::QMLScope)
+ if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope)
return true;
if (m_currentScope->isInlineComponent()) {
@@ -216,7 +221,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
void QmltcVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *object)
{
- if (m_currentScope->scopeType() == QQmlJSScope::QMLScope)
+ if (m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope)
m_qmlTypeNames.removeLast();
QQmlJSImportVisitor::endVisit(object);
}
@@ -268,7 +273,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember)
owner->addOwnProperty(property);
}
- const QString notifyName = name + u"Changed"_s;
+ const QString notifyName = QQmlSignalNames::propertyNameToChangedSignalName(name);
// also check that notify is already a method of the scope
{
auto owningScope = m_savedBindingOuterScope ? m_savedBindingOuterScope : m_currentScope;
@@ -280,7 +285,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember)
u"internal error: %1 found for property '%2'"_s.arg(errorString, name),
qmlCompiler, publicMember->identifierToken);
return false;
- } else if (methods[0].methodType() != QQmlJSMetaMethod::Signal) {
+ } else if (methods[0].methodType() != QQmlJSMetaMethodType::Signal) {
m_logger->log(u"internal error: method %1 of property %2 must be a signal"_s.arg(
notifyName, name),
qmlCompiler, publicMember->identifierToken);
@@ -336,17 +341,17 @@ void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program)
for (const QList<QQmlJSScope::ConstPtr> &qmlTypes : m_pureQmlTypes)
for (const QQmlJSScope::ConstPtr &type : qmlTypes)
- checkForNamingCollisionsWithCpp(type);
+ checkNamesAndTypes(type);
}
QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
{
switch (binding.bindingType()) {
- case QQmlJSMetaPropertyBinding::Object:
+ case QQmlSA::BindingType::Object:
return binding.objectType();
- case QQmlJSMetaPropertyBinding::Interceptor:
+ case QQmlSA::BindingType::Interceptor:
return binding.interceptorType();
- case QQmlJSMetaPropertyBinding::ValueSource:
+ case QQmlSA::BindingType::ValueSource:
return binding.valueSourceType();
// TODO: AttachedProperty and GroupProperty are not supported yet,
// but have to also be acknowledged here
@@ -435,7 +440,7 @@ void QmltcVisitor::postVisitResolve(
// match scopes to indices of QmlIR::Object from QmlIR::Document
qsizetype count = 0;
const auto setIndex = [&](const QQmlJSScope::Ptr &current) {
- if (current->scopeType() != QQmlJSScope::QMLScope || current->isArrayScope())
+ if (current->scopeType() != QQmlSA::ScopeType::QMLScope || current->isArrayScope())
return;
Q_ASSERT(!m_qmlIrObjectIndices.contains(current));
m_qmlIrObjectIndices[current] = count;
@@ -547,9 +552,9 @@ void QmltcVisitor::postVisitResolve(
if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope
return;
switch (scope->scopeType()) {
- case QQmlJSScope::QMLScope:
- case QQmlJSScope::GroupedPropertyScope:
- case QQmlJSScope::AttachedPropertyScope: {
+ case QQmlSA::ScopeType::QMLScope:
+ case QQmlSA::ScopeType::GroupedPropertyScope:
+ case QQmlSA::ScopeType::AttachedPropertyScope: {
++qmlScopeCount[scope->enclosingInlineComponentName()];
break;
}
@@ -653,7 +658,7 @@ void QmltcVisitor::setupAliases()
}
}
-void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &type)
+void QmltcVisitor::checkNamesAndTypes(const QQmlJSScope::ConstPtr &type)
{
static const QString cppKeywords[] = {
u"alignas"_s,
@@ -672,20 +677,20 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &
u"case"_s,
u"catch"_s,
u"char"_s,
- u"char8_t"_s,
u"char16_t"_s,
u"char32_t"_s,
+ u"char8_t"_s,
u"class"_s,
+ u"co_await"_s,
+ u"co_return"_s,
+ u"co_yield"_s,
u"compl"_s,
u"concept"_s,
u"const"_s,
+ u"const_cast"_s,
u"consteval"_s,
u"constexpr"_s,
- u"const_cast"_s,
u"continue"_s,
- u"co_await"_s,
- u"co_return"_s,
- u"co_yield"_s,
u"decltype"_s,
u"default"_s,
u"delete"_s,
@@ -753,6 +758,7 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &
u"xor"_s,
u"xor_eq"_s,
};
+ Q_ASSERT(std::is_sorted(std::begin(cppKeywords), std::end(cppKeywords)));
const auto isReserved = [&](QStringView word) {
if (word.startsWith(QChar(u'_')) && word.size() >= 2
@@ -769,6 +775,23 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &
qmlCompiler, type->sourceLocation());
};
+ const auto validateType = [&type, this](const QQmlJSScope::ConstPtr &typeToCheck,
+ QStringView name, QStringView errorPrefix) {
+ if (type->moduleName().isEmpty() || typeToCheck.isNull())
+ return;
+
+ if (typeToCheck->isComposite() && typeToCheck->moduleName() != type->moduleName()) {
+ m_logger->log(
+ QStringLiteral(
+ "Can't compile the %1 type \"%2\" to C++ because it "
+ "lives in \"%3\" instead of the current file's \"%4\" QML module.")
+ .arg(errorPrefix, name, typeToCheck->moduleName(), type->moduleName()),
+ qmlCompiler, type->sourceLocation());
+ }
+ };
+
+ validateType(type->baseType(), type->baseTypeName(), u"QML base");
+
const auto enums = type->ownEnumerations();
for (auto it = enums.cbegin(); it != enums.cend(); ++it) {
const QQmlJSMetaEnum e = it.value();
@@ -783,16 +806,23 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &
for (auto it = properties.cbegin(); it != properties.cend(); ++it) {
const QQmlJSMetaProperty &p = it.value();
validate(p.propertyName(), u"Property");
+
+ if (!p.isAlias() && !p.typeName().isEmpty())
+ validateType(p.type(), p.typeName(), u"QML property");
}
const auto methods = type->ownMethods();
for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
const QQmlJSMetaMethod &m = it.value();
validate(m.methodName(), u"Method");
+ if (!m.returnTypeName().isEmpty())
+ validateType(m.returnType(), m.returnTypeName(), u"QML method return");
- const auto parameterNames = m.parameterNames();
- for (const auto &name : parameterNames)
- validate(name, u"Method '%1' parameter"_s.arg(m.methodName()));
+ for (const auto &parameter : m.parameters()) {
+ validate(parameter.name(), u"Method '%1' parameter"_s.arg(m.methodName()));
+ if (!parameter.typeName().isEmpty())
+ validateType(parameter.type(), parameter.typeName(), u"QML parameter");
+ }
}
// TODO: one could also test signal handlers' parameters but we do not store
diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h
index 0ec9349527..111df0e885 100644
--- a/tools/qmltc/qmltcvisitor.h
+++ b/tools/qmltc/qmltcvisitor.h
@@ -22,7 +22,7 @@ class QmltcVisitor : public QQmlJSImportVisitor
void postVisitResolve(const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>>
&qmlIrOrderedBindings);
void setupAliases();
- void checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &type);
+ void checkNamesAndTypes(const QQmlJSScope::ConstPtr &type);
void setRootFilePath();
QString sourceDirectoryPath(const QString &path);
@@ -57,7 +57,7 @@ public:
qsizetype creationIndex(const QQmlJSScope::ConstPtr &type) const
{
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
return m_creationIndices.value(type, -1);
}
@@ -68,13 +68,13 @@ public:
qsizetype qmlComponentIndex(const QQmlJSScope::ConstPtr &type) const
{
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
return m_syntheticTypeIndices.value(type, -1);
}
qsizetype qmlIrObjectIndex(const QQmlJSScope::ConstPtr &type) const
{
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
Q_ASSERT(m_qmlIrObjectIndices.contains(type));
return m_qmlIrObjectIndices.value(type, -1);
}
diff --git a/tools/qmltime/qmltime.h b/tools/qmltime/qmltime.h
index 7812600456..fa0f44a5e4 100644
--- a/tools/qmltime/qmltime.h
+++ b/tools/qmltime/qmltime.h
@@ -38,6 +38,5 @@ private:
QQuickView m_view;
QQuickItem *m_item;
};
-QML_DECLARE_TYPE(Timer);
#endif // QMLTIME_H
diff --git a/tools/qmltyperegistrar/qmltyperegistrar.cpp b/tools/qmltyperegistrar/qmltyperegistrar.cpp
index 5d895bbed7..248926fb33 100644
--- a/tools/qmltyperegistrar/qmltyperegistrar.cpp
+++ b/tools/qmltyperegistrar/qmltyperegistrar.cpp
@@ -3,12 +3,14 @@
#include <QCoreApplication>
#include <QCommandLineParser>
+#include <QDir>
#include <QFile>
#include <QScopedPointer>
#include <cstdlib>
#include <QtQmlTypeRegistrar/private/qqmltyperegistrar_p.h>
+#include <QtQmlTypeRegistrar/private/qqmltyperegistrarutils_p.h>
using namespace Qt::Literals;
@@ -17,6 +19,11 @@ int main(int argc, char **argv)
// Produce reliably the same output for the same input by disabling QHash's random seeding.
QHashSeed::setDeterministicGlobalSeed();
+ // No, you are not supposed to mess with the message pattern.
+ // Qt Creator wants to read those messages as-is and we want the convenience
+ // of QDebug to print them.
+ qputenv("QT_MESSAGE_PATTERN", "%{if-category}%{category}: %{endif}%{message}");
+
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationName(QStringLiteral("qmltyperegistrar"));
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
@@ -88,6 +95,13 @@ int main(int argc, char **argv)
"want to follow Qt's versioning scheme."));
parser.addOption(followForeignVersioningOption);
+ QCommandLineOption jsroot(QStringLiteral("jsroot"));
+ jsroot.setDescription(
+ QStringLiteral("Use the JavaScript root object's meta types as sole input and do not "
+ "generate any C++ output. Only useful in combination with "
+ "--generate-qmltypes"));
+ parser.addOption(jsroot);
+
QCommandLineOption extract(u"extract"_s);
extract.setDescription(
u"Extract QML types from a module and use QML_FOREIGN to register them"_s);
@@ -104,20 +118,43 @@ int main(int argc, char **argv)
const QString module = parser.value(importNameOption);
+ const QLatin1String jsrootMetaTypes
+ = QLatin1String(":/qt-project.org/meta_types/jsroot_metatypes.json");
+ QStringList files = parser.positionalArguments();
+ if (parser.isSet(jsroot)) {
+ if (parser.isSet(extract)) {
+ error(module) << "If --jsroot is passed, no type registrations can be extracted.";
+ return EXIT_FAILURE;
+ }
+ if (parser.isSet(outputOption)) {
+ error(module) << "If --jsroot is passed, no C++ output can be generated.";
+ return EXIT_FAILURE;
+ }
+ if (!files.isEmpty() || parser.isSet(foreignTypesOption)) {
+ error(module) << "If --jsroot is passed, no further metatypes can be processed.";
+ return EXIT_FAILURE;
+ }
+
+ files.append(jsrootMetaTypes);
+ }
+
MetaTypesJsonProcessor processor(parser.isSet(privateIncludesOption));
- if (!processor.processTypes(parser.positionalArguments()))
+ if (!processor.processTypes(files))
return EXIT_FAILURE;
processor.postProcessTypes();
- if (parser.isSet(foreignTypesOption))
- processor.processForeignTypes(parser.value(foreignTypesOption).split(QLatin1Char(',')));
+ if (!parser.isSet(jsroot)) {
+ processor.processForeignTypes(jsrootMetaTypes);
+ if (parser.isSet(foreignTypesOption))
+ processor.processForeignTypes(parser.value(foreignTypesOption).split(QLatin1Char(',')));
+ }
processor.postProcessForeignTypes();
if (parser.isSet(extract)) {
if (!parser.isSet(outputOption)) {
- fprintf(stderr, "Error: The output file name must be provided\n");
+ error(module) << "The output file name must be provided";
return EXIT_FAILURE;
}
QString baseName = parser.value(outputOption);
@@ -137,25 +174,34 @@ int main(int argc, char **argv)
parser.isSet(followForeignVersioningOption));
typeRegistrar.setTypes(processor.types(), processor.foreignTypes());
- if (parser.isSet(outputOption)) {
- // extract does its own file handling
- QString outputName = parser.value(outputOption);
- QFile file(outputName);
- if (!file.open(QIODeviceBase::WriteOnly)) {
- fprintf(stderr, "Error: Cannot open %s for writing\n", qPrintable(outputName));
- return EXIT_FAILURE;
+ if (!parser.isSet(jsroot)) {
+ if (module.isEmpty()) {
+ warning(module) << "The module name is empty. Cannot generate C++ code";
+ } else if (parser.isSet(outputOption)) {
+ // extract does its own file handling
+ QString outputName = parser.value(outputOption);
+ QFile file(outputName);
+ if (!file.open(QIODeviceBase::WriteOnly)) {
+ error(QDir::toNativeSeparators(outputName))
+ << "Cannot open file for writing:" << file.errorString();
+ return EXIT_FAILURE;
+ }
+ QTextStream output(&file);
+ typeRegistrar.write(output, outputName);
+ } else {
+ QTextStream output(stdout);
+ typeRegistrar.write(output, "stdout");
}
- QTextStream output(&file);
- typeRegistrar.write(output);
- } else {
- QTextStream output(stdout);
- typeRegistrar.write(output);
}
if (!parser.isSet(pluginTypesOption))
return EXIT_SUCCESS;
typeRegistrar.setReferencedTypes(processor.referencedTypes());
- typeRegistrar.generatePluginTypes(parser.value(pluginTypesOption));
+ const QString qmltypes = parser.value(pluginTypesOption);
+ if (!typeRegistrar.generatePluginTypes(qmltypes)) {
+ error(qmltypes) << "Cannot generate qmltypes file";
+ return EXIT_FAILURE;
+ }
return EXIT_SUCCESS;
}
diff --git a/tools/shared/qqmltoolingsettings.cpp b/tools/shared/qqmltoolingsettings.cpp
deleted file mode 100644
index dffd59fbc7..0000000000
--- a/tools/shared/qqmltoolingsettings.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qqmltoolingsettings.h"
-
-#include <algorithm>
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/qset.h>
-#include <QtCore/qsettings.h>
-#include <QtCore/qstandardpaths.h>
-
-using namespace Qt::StringLiterals;
-
-void QQmlToolingSettings::addOption(const QString &name, QVariant defaultValue)
-{
- m_values[name] = defaultValue;
-}
-
-bool QQmlToolingSettings::read(const QString &settingsFilePath)
-{
- if (!QFileInfo::exists(settingsFilePath))
- return false;
-
- if (m_currentSettingsPath == settingsFilePath)
- return true;
-
- QSettings settings(settingsFilePath, QSettings::IniFormat);
-
- for (const QString &key : settings.allKeys())
- m_values[key] = settings.value(key).toString();
-
- m_currentSettingsPath = settingsFilePath;
-
- return true;
-}
-
-bool QQmlToolingSettings::writeDefaults() const
-{
- const QString path = QFileInfo(u".%1.ini"_s.arg(m_toolName)).absoluteFilePath();
-
- QSettings settings(path, QSettings::IniFormat);
- for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it) {
- settings.setValue(it.key(), it.value().isNull() ? QString() : it.value());
- }
-
- settings.sync();
-
- if (settings.status() != QSettings::NoError) {
- qWarning() << "Failed to write default settings to" << path
- << "Error:" << settings.status();
- return false;
- }
-
- qInfo() << "Wrote default settings to" << path;
- return true;
-}
-
-bool QQmlToolingSettings::search(const QString &path)
-{
- QFileInfo fileInfo(path);
- QDir dir(fileInfo.isDir() ? path : fileInfo.dir());
-
- QSet<QString> dirs;
-
- const QString settingsFileName = u".%1.ini"_s.arg(m_toolName);
-
- while (dir.exists() && dir.isReadable()) {
- const QString dirPath = dir.absolutePath();
-
- if (m_seenDirectories.contains(dirPath)) {
- const QString cachedIniPath = m_seenDirectories[dirPath];
- if (cachedIniPath.isEmpty())
- return false;
-
- return read(cachedIniPath);
- }
-
- dirs << dirPath;
-
- const QString iniFile = dir.absoluteFilePath(settingsFileName);
-
- if (read(iniFile)) {
- for (const QString &dir : std::as_const(dirs))
- m_seenDirectories[dir] = iniFile;
- return true;
- }
-
- if (!dir.cdUp())
- break;
- }
-
- if (const QString iniFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, u"%1.ini"_s.arg(m_toolName)); !iniFile.isEmpty()) {
- if (read(iniFile)) {
- for (const QString &dir : std::as_const(dirs))
- m_seenDirectories[dir] = iniFile;
- return true;
- }
- }
-
- // No INI file found anywhere, record the failure so we won't have to traverse the entire
- // filesystem again
- for (const QString &dir : std::as_const(dirs))
- m_seenDirectories[dir] = QString();
-
- return false;
-}
-
-QVariant QQmlToolingSettings::value(QString name) const
-{
- return m_values.value(name);
-}
-
-bool QQmlToolingSettings::isSet(QString name) const
-{
- if (!m_values.contains(name))
- return false;
-
- QVariant variant = m_values[name];
-
- // Unset is encoded as an empty string
- return !(variant.canConvert(QMetaType(QMetaType::QString)) && variant.toString().isEmpty());
-}
diff --git a/tools/shared/qqmltoolingsettings.h b/tools/shared/qqmltoolingsettings.h
deleted file mode 100644
index 483147b3e9..0000000000
--- a/tools/shared/qqmltoolingsettings.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef QQMLTOOLINGSETTINGS_H
-#define QQMLTOOLINGSETTINGS_H
-
-#include <QtCore/qstring.h>
-#include <QtCore/qhash.h>
-#include <QtCore/qvariant.h>
-
-class QQmlToolingSettings
-{
-public:
- QQmlToolingSettings(const QString &toolName) : m_toolName(toolName) { }
-
- void addOption(const QString &name, const QVariant defaultValue = QVariant());
-
- bool writeDefaults() const;
- bool search(const QString &path);
-
- QVariant value(QString name) const;
- bool isSet(QString name) const;
-
-private:
- QString m_toolName;
- QString m_currentSettingsPath;
- QHash<QString, QString> m_seenDirectories;
- QVariantHash m_values;
-
- bool read(const QString &settingsFilePath);
-};
-
-#endif
diff --git a/tools/svgtoqml/CMakeLists.txt b/tools/svgtoqml/CMakeLists.txt
new file mode 100644
index 0000000000..fd6c91e7ea
--- /dev/null
+++ b/tools/svgtoqml/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## svgtoqml Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name svgtoqml)
+qt_internal_add_tool(${target_name}
+ TARGET_DESCRIPTION "SVG to QML Converter"
+ TOOLS_TARGET Quick
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+ Qt::QuickVectorImageGeneratorPrivate
+)
+qt_internal_return_unless_building_tools()
+
+set(resource_files
+ "main.qml"
+)
+
+qt_internal_add_resource(${target_name} "qml"
+ PREFIX
+ "/"
+ FILES
+ ${resource_files}
+)
diff --git a/tools/svgtoqml/main.cpp b/tools/svgtoqml/main.cpp
new file mode 100644
index 0000000000..64474030dc
--- /dev/null
+++ b/tools/svgtoqml/main.cpp
@@ -0,0 +1,141 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QCommandLineParser>
+#include <QFile>
+#include <QQuickWindow>
+#include <QQuickItem>
+#include <QtQuickVectorImageGenerator/private/qquickitemgenerator_p.h>
+#include <QtQuickVectorImageGenerator/private/qquickqmlgenerator_p.h>
+#include <QtQuickVectorImageGenerator/private/qquickvectorimageglobal_p.h>
+
+#define ENABLE_GUI
+
+int main(int argc, char *argv[])
+{
+#ifdef ENABLE_GUI
+ QGuiApplication app(argc, argv);
+#else
+ QCoreApplication app(argc, argv);
+#endif
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("SVG to QML converter [tech preview]");
+ parser.addHelpOption();
+ parser.addPositionalArgument("input", QCoreApplication::translate("main", "SVG file to read."));
+ parser.addPositionalArgument("output", QCoreApplication::translate("main", "QML file to write."));
+
+ QCommandLineOption optimizeOption("optimize-paths",
+ QCoreApplication::translate("main", "Optimize paths for the curve renderer."));
+ parser.addOption(optimizeOption);
+
+ QCommandLineOption curveRendererOption("curve-renderer",
+ QCoreApplication::translate("main", "Use the curve renderer in generated QML."));
+ parser.addOption(curveRendererOption);
+
+ QCommandLineOption typeNameOption(QStringList() << "t" << "type-name",
+ QCoreApplication::translate("main", "Use <typename> for Shape."),
+ QCoreApplication::translate("main", "typename"));
+ parser.addOption(typeNameOption);
+
+ QCommandLineOption copyrightOption("copyright-statement",
+ QCoreApplication::translate("main", "Add <string> as a comment at the start of the generated file."),
+ QCoreApplication::translate("main", "string"));
+ parser.addOption(copyrightOption);
+
+ QCommandLineOption outlineModeOption("outline-stroke-mode",
+ QCoreApplication::translate("main", "Stroke the outline (contour) of the filled shape instead of "
+ "the original path. Also sets optimize-paths."));
+ parser.addOption(outlineModeOption);
+
+ QCommandLineOption keepPathsOption("keep-external-paths",
+ QCoreApplication::translate("main", "Any paths to external files will be retained in the QML output. "
+ "The paths will be reformatted as relative to the output file. If "
+ "this is not enabled, copies of the file will be saved to the asset output "
+ "directory. Embedded data will still be saved to files, even if "
+ "this option is set."));
+ parser.addOption(keepPathsOption);
+
+ QCommandLineOption assetOutputDirectoryOption("asset-output-directory",
+ QCoreApplication::translate("main", "If the SVG refers to external or embedded files, such as images, these "
+ "will be copied into the same directory as the output QML file by default. "
+ "Set the asset output directory to override the location."),
+ QCoreApplication::translate("main", "directory"));
+ parser.addOption(assetOutputDirectoryOption);
+
+ QCommandLineOption assetOutputPrefixOption("asset-output-prefix",
+ QCoreApplication::translate("main", "If the SVG refers to external or embedded files, such as images, these "
+ "will be copied to files with unique identifiers. By default, the files will be prefixed "
+ "with \"svg_asset_\". Set the asset output prefix to override the prefix."),
+ QCoreApplication::translate("main", "prefix"));
+ parser.addOption(assetOutputPrefixOption);
+
+#ifdef ENABLE_GUI
+ QCommandLineOption guiOption(QStringList() << "v" << "view",
+ QCoreApplication::translate("main", "Display the SVG in a window."));
+ parser.addOption(guiOption);
+#endif
+ parser.process(app);
+ const QStringList args = parser.positionalArguments();
+ if (args.size() < 1) {
+ parser.showHelp(1);
+ }
+
+ const QString inFileName = args.at(0);
+
+ QString commentString = QLatin1String("Generated from SVG file %1").arg(inFileName);
+
+ const auto outFileName = args.size() > 1 ? args.at(1) : QString{};
+ const auto typeName = parser.value(typeNameOption);
+ const auto assetOutputDirectory = parser.value(assetOutputDirectoryOption);
+ const auto assetOutputPrefix = parser.value(assetOutputPrefixOption);
+ const bool keepPaths = parser.isSet(keepPathsOption);
+ auto copyrightString = parser.value(copyrightOption);
+
+ if (!copyrightString.isEmpty()) {
+ copyrightString = copyrightString.replace("\\n", "\n");
+ commentString = copyrightString + u"\n" + commentString;
+ }
+
+ QQuickVectorImageGenerator::GeneratorFlags flags;
+ if (parser.isSet(curveRendererOption))
+ flags |= QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer;
+ if (parser.isSet(optimizeOption))
+ flags |= QQuickVectorImageGenerator::GeneratorFlag::OptimizePaths;
+ if (parser.isSet(outlineModeOption))
+ flags |= (QQuickVectorImageGenerator::GeneratorFlag::OutlineStrokeMode
+ | QQuickVectorImageGenerator::GeneratorFlag::OptimizePaths);
+
+ QQuickQmlGenerator generator(inFileName, flags, outFileName);
+ generator.setShapeTypeName(typeName);
+ generator.setCommentString(commentString);
+ generator.setAssetFileDirectory(assetOutputDirectory);
+ generator.setAssetFilePrefix(assetOutputPrefix);
+ generator.setRetainFilePaths(keepPaths);
+ generator.generate();
+
+#ifdef ENABLE_GUI
+ if (parser.isSet(guiOption)) {
+ app.setOrganizationName("QtProject");
+ const QUrl url(QStringLiteral("qrc:/main.qml"));
+ QQmlApplicationEngine engine;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ &app, [&](QObject *obj, const QUrl &objUrl){
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ if (obj) {
+ auto *containerItem = obj->findChild<QQuickItem*>(QStringLiteral("svg_item"));
+ QQuickItemGenerator generator(inFileName, flags, containerItem);
+ generator.generate();
+ }
+ });
+ engine.load(url);
+ return app.exec();
+ }
+#else
+ return 0;
+#endif
+
+}
diff --git a/tools/svgtoqml/main.qml b/tools/svgtoqml/main.qml
new file mode 100644
index 0000000000..9a8ceefe5d
--- /dev/null
+++ b/tools/svgtoqml/main.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Window {
+ id: mainWindow
+ width: 1280
+ height: 960
+ visible: true
+ color: "white"
+
+ Item {
+ id: svg
+ objectName: "svg_item"
+ anchors.centerIn: parent
+ }
+}